]> gitweb.ps.run Git - matrix_esp_thesis/blob - src/matrix.c
06409b82ea3f3029d07c8f28dc267b98c900c0bb
[matrix_esp_thesis] / src / matrix.c
1 #include "matrix.h"\r
2 \r
3 #include <time.h>\r
4 #include <stdio.h>\r
5 #include <mjson.h>\r
6 \r
7 \r
8 #define LOGIN_REQUEST_SIZE 1024\r
9 #define LOGIN_RESPONSE_SIZE 1024\r
10 #define LOGIN_URL "/_matrix/client/v3/login"\r
11 \r
12 #define ENCRYPTED_REQUEST_SIZE (1024*5)\r
13 #define ENCRYPTED_EVENT_SIZE (1024*10)\r
14 #define ROOMEVENT_REQUEST_SIZE 256\r
15 #define ROOMEVENT_RESPONSE_SIZE 1024\r
16 #define ROOMEVENT_URL "/_matrix/client/v3/rooms/%s/send/%s/%d"\r
17 \r
18 #define TODEVICE_EVENT_SIZE (1024*5)\r
19 #define TODEVICE_URL "/_matrix/client/v3/sendToDevice/%s/%d"\r
20 \r
21 #define KEYS_QUERY_URL "/_matrix/client/v3/keys/query"\r
22 #define KEYS_QUERY_REQUEST_SIZE 256\r
23 #define KEYS_QUERY_RESPONSE_SIZE (1024*10)\r
24 \r
25 #define KEYS_UPLOAD_URL "/_matrix/client/v3/keys/upload"\r
26 #define KEYS_UPLOAD_REQUEST_SIZE 1024*4\r
27 #define KEYS_UPLOAD_REQUEST_SIGNED_SIZE 2048*4\r
28 #define KEYS_UPLOAD_RESPONSE_SIZE 2048\r
29 \r
30 #define KEYS_CLAIM_URL "/_matrix/client/v3/keys/claim"\r
31 #define KEYS_CLAIM_REQUEST_SIZE 1024\r
32 #define KEYS_CLAIM_RESPONSE_SIZE 1024\r
33 \r
34 #define JSON_QUERY_SIZE 128\r
35 \r
36 \r
37 \r
38 void\r
39 Randomize(\r
40     uint8_t * random,\r
41     int randomLen)\r
42 {\r
43     static bool first = true;\r
44     if (first) { srand(time(0)); first = false; }\r
45 \r
46     for (int i = 0; i < randomLen; i++)\r
47     {\r
48         random[i] = rand() % 256;\r
49     }\r
50 }\r
51 \r
52 bool\r
53 JsonEscape(\r
54     const char * sIn, int sInLen,\r
55     char * sOut, int sOutCap)\r
56 {\r
57     int sOutIndex = 0;\r
58 \r
59     for (int i = 0; i < sInLen; i++)\r
60     {\r
61         if (i >= sOutCap)\r
62             return false;\r
63         \r
64         if (sIn[i] == '.' ||\r
65             sIn[i] == '[' ||\r
66             sIn[i] == ']'\r
67         ) {\r
68             sOut[sOutIndex++] = '\\';\r
69         }\r
70         sOut[sOutIndex++] = sIn[i];\r
71     }\r
72 \r
73     if (sOutIndex < sOutCap)\r
74         sOut[sOutIndex] = '\0';\r
75 \r
76     return true;\r
77 }\r
78 \r
79 bool JsonSign(\r
80     MatrixClient * client,\r
81     const char * sIn, int sInLen,\r
82     char * sOut, int sOutCap)\r
83 {\r
84     static char signature[OLM_SIGNATURE_SIZE];\r
85     size_t res =\r
86         olm_account_sign(client->olmAccount.account,\r
87             sIn, sInLen,\r
88             signature, OLM_SIGNATURE_SIZE);\r
89     \r
90     int signatureLen = res;\r
91     \r
92     char thisSigningKey[DEVICE_KEY_SIZE];\r
93     MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, DEVICE_KEY_SIZE);\r
94 \r
95     static char signatureJson[JSON_SIGNATURE_SIZE];\r
96     int signatureJsonLen =\r
97         mjson_snprintf(signatureJson, JSON_SIGNATURE_SIZE,\r
98             "{"\r
99                 "\"signatures\":{"\r
100                     "\"%s\":{"\r
101                         "\"ed25519:%s\":\"%.*s\""\r
102                     "}"\r
103                 "}"\r
104             "}",\r
105             client->userId,\r
106             "1",\r
107             signatureLen, signature);\r
108 \r
109     struct mjson_fixedbuf result = { sOut, sOutCap, 0 };\r
110     mjson_merge(\r
111         sIn, sInLen,\r
112         signatureJson, signatureJsonLen,\r
113         mjson_print_fixed_buf,\r
114         &result);\r
115 \r
116     return true;\r
117 }\r
118 \r
119 \r
120 bool\r
121 MatrixOlmAccountInit(\r
122     MatrixOlmAccount * account)\r
123 {\r
124     account->account = olm_account(account->memory);\r
125 \r
126     static uint8_t random[OLM_ACCOUNT_RANDOM_SIZE];\r
127     Randomize(random, OLM_ACCOUNT_RANDOM_SIZE);\r
128 \r
129     size_t res = olm_create_account(\r
130         account->account,\r
131         random,\r
132         OLM_ACCOUNT_RANDOM_SIZE);\r
133 \r
134     return res != olm_error();\r
135 }\r
136 \r
137 bool\r
138 MatrixOlmAccountUnpickle(\r
139     MatrixOlmAccount * account,\r
140     void * pickled, int pickledLen,\r
141     const void * key, int keyLen)\r
142 {\r
143     size_t res;\r
144     res = olm_unpickle_account(account->account,\r
145         key, keyLen,\r
146         pickled, pickledLen);\r
147     if (res == olm_error()) {\r
148         printf("error unpickling olm account:%s\n",\r
149             olm_account_last_error(account->account));\r
150     }\r
151     return res != olm_error();\r
152 }\r
153 \r
154 bool\r
155 MatrixOlmAccountGetDeviceKey(\r
156     MatrixOlmAccount * account,\r
157     char * key, int keyCap)\r
158 {\r
159     static char deviceKeysJson[OLM_IDENTITY_KEYS_JSON_SIZE];\r
160     size_t res =\r
161         olm_account_identity_keys(account->account,\r
162             deviceKeysJson, OLM_IDENTITY_KEYS_JSON_SIZE);\r
163     mjson_get_string(deviceKeysJson, res,\r
164         "$.curve25519",\r
165         key, keyCap);\r
166     return true;\r
167 }\r
168 \r
169 bool\r
170 MatrixOlmAccountGetSigningKey(\r
171     MatrixOlmAccount * account,\r
172     char * key, int keyCap)\r
173 {\r
174     static char deviceKeysJson[OLM_IDENTITY_KEYS_JSON_SIZE];\r
175     size_t res =\r
176         olm_account_identity_keys(account->account,\r
177             deviceKeysJson, OLM_IDENTITY_KEYS_JSON_SIZE);\r
178     mjson_get_string(deviceKeysJson, res,\r
179         "$.ed25519",\r
180         key, keyCap);\r
181     return true;\r
182 }\r
183 \r
184 // TODO:in/outbound sessions\r
185 bool\r
186 MatrixOlmSessionTo(\r
187     MatrixOlmSession * session,\r
188     OlmAccount * olmAccount,\r
189     const char * deviceId,\r
190     const char * deviceKey,\r
191     const char * deviceOnetimeKey)\r
192 {\r
193     memset(session, 0, sizeof(MatrixOlmSession));\r
194 \r
195     session->deviceId = deviceId;\r
196 \r
197     session->session =\r
198         olm_session(session->memory);\r
199 \r
200     static uint8_t random[OLM_OUTBOUND_SESSION_RANDOM_SIZE];\r
201     Randomize(random, OLM_OUTBOUND_SESSION_RANDOM_SIZE);\r
202 \r
203     size_t res =\r
204         olm_create_outbound_session(session->session,\r
205             olmAccount,\r
206             deviceKey, strlen(deviceKey),\r
207             deviceOnetimeKey, strlen(deviceOnetimeKey),\r
208             random, OLM_OUTBOUND_SESSION_RANDOM_SIZE);\r
209     \r
210     if (res == olm_error()) {\r
211         printf("error olm:%s\n", olm_session_last_error(session->session));\r
212     }\r
213 \r
214     return session->session != NULL;\r
215 }\r
216 \r
217 bool\r
218 MatrixOlmSessionUnpickle(\r
219     MatrixOlmSession * session,\r
220     const char * deviceId,\r
221     void * pickled, int pickledLen,\r
222     const void * key, int keyLen)\r
223 {\r
224     memset(session, 0, sizeof(MatrixOlmSession));\r
225 \r
226     session->deviceId = deviceId;\r
227 \r
228     session->session =\r
229         olm_session(session->memory);\r
230     \r
231     size_t res;\r
232     res = olm_unpickle_session(session->session,\r
233         key, keyLen,\r
234         pickled, pickledLen);\r
235     \r
236     if (res == olm_error()) {\r
237         printf("error unpickling olm session:%s\n", olm_session_last_error(session->session));\r
238     }\r
239 \r
240     return res != olm_error();\r
241 }\r
242 \r
243 bool\r
244 MatrixOlmSessionEncrypt(\r
245     MatrixOlmSession * session,\r
246     const char * plaintext,\r
247     char * outBuffer, int outBufferCap)\r
248 {\r
249     static uint8_t random[OLM_ENCRYPT_RANDOM_SIZE];\r
250     Randomize(random, OLM_ENCRYPT_RANDOM_SIZE);\r
251 \r
252     size_t res = olm_encrypt(session->session,\r
253         plaintext, strlen(plaintext),\r
254         random, OLM_ENCRYPT_RANDOM_SIZE,\r
255         outBuffer, outBufferCap);\r
256 \r
257     return res != olm_error();\r
258 }\r
259 \r
260 // https://matrix.org/docs/guides/end-to-end-encryption-implementation-guide#starting-a-megolm-session\r
261 bool\r
262 MatrixMegolmOutSessionInit(\r
263     MatrixMegolmOutSession * session,\r
264     const char * roomId)\r
265 {\r
266     memset(session, 0, sizeof(MatrixMegolmOutSession));\r
267 \r
268     static uint8_t random[MEGOLM_INIT_RANDOM_SIZE];\r
269     Randomize(random, MEGOLM_INIT_RANDOM_SIZE);\r
270 \r
271     strncpy(session->roomId, roomId, ROOM_ID_SIZE);\r
272 \r
273     session->session =\r
274         olm_outbound_group_session(session->memory);\r
275 \r
276     olm_init_outbound_group_session(\r
277         session->session,\r
278         random,\r
279         MEGOLM_INIT_RANDOM_SIZE);\r
280 \r
281     olm_outbound_group_session_id(session->session,\r
282         (uint8_t *)session->id,\r
283         MEGOLM_SESSION_ID_SIZE);\r
284         \r
285     olm_outbound_group_session_key(session->session,\r
286         (uint8_t *)session->key,\r
287         MEGOLM_SESSION_KEY_SIZE);\r
288     \r
289     return true;\r
290 }\r
291 \r
292 bool\r
293 MatrixMegolmOutSessionEncrypt(\r
294     MatrixMegolmOutSession * session,\r
295     const char * plaintext,\r
296     char * outBuffer, int outBufferCap)\r
297 {\r
298     size_t res = olm_group_encrypt(session->session,\r
299         (uint8_t *)plaintext, strlen(plaintext),\r
300         (uint8_t *)outBuffer, outBufferCap);\r
301 \r
302     return res != olm_error();\r
303 }\r
304 \r
305 bool\r
306 MatrixMegolmOutSessionSave(\r
307     MatrixMegolmOutSession * session,\r
308     const char * filename,\r
309     const char * key)\r
310 {\r
311     FILE * f = fopen(filename, "w");\r
312 \r
313     size_t roomIdLen = strlen(session->roomId);\r
314     fwrite(&roomIdLen, sizeof(size_t), 1, f);\r
315     fwrite(session->roomId, 1, roomIdLen, f);\r
316 \r
317     size_t pickleBufferLen =\r
318         olm_pickle_outbound_group_session_length(\r
319             session->session);\r
320     void * pickleBuffer = malloc(pickleBufferLen);\r
321 \r
322     olm_pickle_outbound_group_session(\r
323         session->session,\r
324         key, strlen(key),\r
325         pickleBuffer, pickleBufferLen);\r
326     \r
327     fwrite(&pickleBufferLen, sizeof(size_t), 1, f);\r
328     fwrite(pickleBuffer, 1, pickleBufferLen, f);\r
329     free(pickleBuffer);\r
330 \r
331     fclose(f);\r
332 \r
333     return true;\r
334 }\r
335 \r
336 bool\r
337 MatrixMegolmOutSessionLoad(\r
338     MatrixMegolmOutSession * session,\r
339     const char * filename,\r
340     const char * key)\r
341 {\r
342     FILE * f = fopen(filename, "r");\r
343 \r
344     size_t roomIdLen;\r
345     fread(&roomIdLen, sizeof(size_t), 1, f);\r
346     fread(session->roomId, 1, roomIdLen, f);\r
347     for (int i = roomIdLen; i < ROOM_ID_SIZE; i++)\r
348         session->roomId[i] = '\0';\r
349 \r
350     size_t pickleBufferLen;\r
351     fread(&pickleBufferLen, sizeof(size_t), 1, f);\r
352 \r
353     void * pickleBuffer = malloc(pickleBufferLen);\r
354     fread(pickleBuffer, 1, pickleBufferLen, f);\r
355 \r
356     olm_unpickle_outbound_group_session(\r
357         session->session,\r
358         key, strlen(key),\r
359         pickleBuffer, pickleBufferLen);\r
360     \r
361     free(pickleBuffer);\r
362 \r
363     olm_outbound_group_session_id(session->session, (uint8_t *)session->id, MEGOLM_SESSION_ID_SIZE);\r
364     olm_outbound_group_session_key(session->session, (uint8_t *)session->key, MEGOLM_SESSION_KEY_SIZE);\r
365 \r
366     fclose(f);\r
367 \r
368     return true;\r
369 }\r
370 \r
371 \r
372 \r
373 bool\r
374 MatrixClientInit(\r
375     MatrixClient * client,\r
376     const char * server)\r
377 {\r
378     memset(client, 0, sizeof(MatrixClient));\r
379 \r
380     strcpy(client->server, server);\r
381 \r
382     // init olm account\r
383     MatrixOlmAccountInit(&client->olmAccount);\r
384 \r
385     return true;\r
386 }\r
387 \r
388 bool\r
389 MatrixClientSave(\r
390     MatrixClient * client,\r
391     const char * filename)\r
392 {\r
393     FILE * f = fopen(filename, "w");\r
394     \r
395     \r
396     char thisDeviceKey[DEVICE_KEY_SIZE];\r
397     MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);\r
398     char thisSigningKey[DEVICE_KEY_SIZE];\r
399     MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, DEVICE_KEY_SIZE);\r
400 \r
401 \r
402     fwrite(thisDeviceKey, 1, DEVICE_KEY_SIZE, f);\r
403     fwrite(thisSigningKey, 1, DEVICE_KEY_SIZE, f);\r
404     fwrite(client->userId, 1, USER_ID_SIZE, f);\r
405     fwrite(client->server, 1, SERVER_SIZE, f);\r
406     fwrite(client->accessToken, 1, ACCESS_TOKEN_SIZE, f);\r
407     fwrite(client->deviceId, 1, DEVICE_ID_SIZE, f);\r
408     fwrite(client->expireMs, 1, EXPIRE_MS_SIZE, f);\r
409     fwrite(client->refreshToken, 1, REFRESH_TOKEN_SIZE, f);\r
410 \r
411     fwrite(&client->numDevices, sizeof(int), 1, f);\r
412     for (int i = 0; i < client->numDevices; i++) {\r
413         fwrite(client->devices[i].deviceId, 1, DEVICE_ID_SIZE, f);\r
414         fwrite(client->devices[i].deviceKey, 1, DEVICE_KEY_SIZE, f);\r
415     }\r
416 \r
417     fclose(f);\r
418     return true;\r
419 }\r
420 \r
421 bool\r
422 MatrixClientLoad(\r
423     MatrixClient * client,\r
424     const char * filename)\r
425 {\r
426     FILE * f = fopen(filename, "r");\r
427     \r
428     \r
429     char thisDeviceKey[DEVICE_KEY_SIZE];\r
430     MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);\r
431     char thisSigningKey[DEVICE_KEY_SIZE];\r
432     MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, DEVICE_KEY_SIZE);\r
433 \r
434 \r
435     fread(thisDeviceKey, 1, DEVICE_KEY_SIZE, f);\r
436     fread(thisSigningKey, 1, DEVICE_KEY_SIZE, f);\r
437     fread(client->userId, 1, USER_ID_SIZE, f);\r
438     fread(client->server, 1, SERVER_SIZE, f);\r
439     fread(client->accessToken, 1, ACCESS_TOKEN_SIZE, f);\r
440     fread(client->deviceId, 1, DEVICE_ID_SIZE, f);\r
441     fread(client->expireMs, 1, EXPIRE_MS_SIZE, f);\r
442     fread(client->refreshToken, 1, REFRESH_TOKEN_SIZE, f);\r
443 \r
444     fread(&client->numDevices, sizeof(int), 1, f);\r
445     for (int i = 0; i < client->numDevices; i++) {\r
446         fread(client->devices[i].deviceId, 1, DEVICE_ID_SIZE, f);\r
447         fread(client->devices[i].deviceKey, 1, DEVICE_KEY_SIZE, f);\r
448     }\r
449 \r
450     fclose(f);\r
451     return true;\r
452 }\r
453 \r
454 bool\r
455 MatrixClientSetAccessToken(\r
456     MatrixClient * client,\r
457     const char * accessToken)\r
458 {\r
459     for (int i = 0; i < ACCESS_TOKEN_SIZE-1; i++)\r
460         client->accessToken[i] = accessToken[i];\r
461     client->accessToken[ACCESS_TOKEN_SIZE-1] = '\0';\r
462 \r
463     return true;\r
464 }\r
465 \r
466 bool\r
467 MatrixClientSetDeviceId(\r
468     MatrixClient * client,\r
469     const char * deviceId)\r
470 {\r
471     for (int i = 0; i < DEVICE_ID_SIZE-1; i++)\r
472         client->deviceId[i] = deviceId[i];\r
473     client->deviceId[DEVICE_ID_SIZE-1] = '\0';\r
474 \r
475     return true;\r
476 }\r
477 \r
478 bool\r
479 MatrixClientSetUserId(\r
480     MatrixClient * client,\r
481     const char * userId)\r
482 {\r
483     for (int i = 0; i < USER_ID_SIZE-1; i++)\r
484         client->userId[i] = userId[i];\r
485     client->userId[USER_ID_SIZE-1] = '\0';\r
486 \r
487     return true;\r
488 }\r
489 \r
490 bool\r
491 MatrixClientGenerateOnetimeKeys(\r
492     MatrixClient * client,\r
493     int numberOfKeys)\r
494 {\r
495     static uint8_t random[OLM_ONETIME_KEYS_RANDOM_SIZE];\r
496     Randomize(random, OLM_ONETIME_KEYS_RANDOM_SIZE);\r
497 \r
498     size_t res =\r
499         olm_account_generate_one_time_keys(client->olmAccount.account,\r
500             numberOfKeys, random, OLM_ONETIME_KEYS_RANDOM_SIZE);\r
501 \r
502     return res != olm_error();\r
503 }\r
504 \r
505 // https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv3keysupload\r
506 bool\r
507 MatrixClientUploadOnetimeKeys(\r
508     MatrixClient * client)\r
509 {\r
510     static char requestBuffer[KEYS_UPLOAD_REQUEST_SIZE];\r
511 \r
512     mjson_snprintf(requestBuffer, KEYS_UPLOAD_REQUEST_SIZE,\r
513         "{\"one_time_keys\":{");\r
514 \r
515     static char onetimeKeysBuffer[1024];\r
516     olm_account_one_time_keys(client->olmAccount.account,\r
517         onetimeKeysBuffer, 1024);\r
518 \r
519     const char *keys;\r
520     int keysLen;\r
521     mjson_find(onetimeKeysBuffer, strlen(onetimeKeysBuffer), "$.curve25519", &keys, &keysLen);\r
522 \r
523     int koff, klen, voff, vlen, vtype, off = 0;\r
524     while ((off = mjson_next(keys, keysLen, off, &koff, &klen, &voff, &vlen, &vtype)) != 0) {\r
525         static char keyJson[JSON_ONETIME_KEY_SIZE];\r
526         \r
527         snprintf(keyJson, JSON_ONETIME_KEY_SIZE,\r
528             "{\"key\":\"%.*s\"}",\r
529             vlen-2, keys + voff+1);\r
530 \r
531         static char keyJsonSigned[JSON_ONETIME_KEY_SIGNED_SIZE];\r
532 \r
533         JsonSign(client,\r
534             keyJson, JSON_ONETIME_KEY_SIZE,\r
535             keyJsonSigned, JSON_ONETIME_KEY_SIGNED_SIZE);\r
536         \r
537         mjson_snprintf(requestBuffer+strlen(requestBuffer), KEYS_UPLOAD_REQUEST_SIZE-strlen(requestBuffer),\r
538             "\"signed_curve25519:%.*s\":%s,",\r
539             klen-2, keys + koff+1,\r
540             keyJsonSigned);\r
541     }\r
542 \r
543     mjson_snprintf(requestBuffer+strlen(requestBuffer)-1, KEYS_UPLOAD_REQUEST_SIZE-strlen(requestBuffer),\r
544         "}}");\r
545 \r
546     static char responseBuffer[KEYS_UPLOAD_RESPONSE_SIZE];\r
547     MatrixHttpPost(client,\r
548         KEYS_UPLOAD_URL,\r
549         requestBuffer,\r
550         responseBuffer, KEYS_UPLOAD_RESPONSE_SIZE,\r
551         true);\r
552 \r
553     return true;\r
554 }\r
555 \r
556 // https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv3keysupload\r
557 bool\r
558 MatrixClientUploadDeviceKey(\r
559     MatrixClient * client)\r
560 {\r
561     char thisDeviceKey[DEVICE_KEY_SIZE];\r
562     MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);\r
563     char thisSigningKey[DEVICE_KEY_SIZE];\r
564     MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, DEVICE_KEY_SIZE);\r
565 \r
566     static char deviceKeysBuffer[KEYS_UPLOAD_REQUEST_SIZE];\r
567 \r
568     mjson_snprintf(deviceKeysBuffer, KEYS_UPLOAD_REQUEST_SIZE,\r
569         "{\"device_keys\":{"\r
570             "\"algorithms\":[\"m.olm.v1.curve25519-aes-sha2\",\"m.megolm.v1.aes-sha2\"],"\r
571             "\"device_id\":\"%s\","\r
572             "\"keys\":{"\r
573                 "\"curve25519:%s\":\"%s\","\r
574                 "\"ed25519:%s\":\"%s\""\r
575             "},"\r
576             "\"user_id\":\"%s\""\r
577         "}}",\r
578         client->deviceId,\r
579         client->deviceId, thisDeviceKey,\r
580         client->deviceId, thisSigningKey,\r
581         client->userId);\r
582 \r
583     static char deviceKeysSignedBuffer[KEYS_UPLOAD_REQUEST_SIGNED_SIZE];\r
584     JsonSign(client,\r
585         deviceKeysBuffer, KEYS_UPLOAD_REQUEST_SIZE,\r
586         deviceKeysSignedBuffer, KEYS_UPLOAD_REQUEST_SIZE);\r
587 \r
588 \r
589     static char responseBuffer[KEYS_UPLOAD_RESPONSE_SIZE];\r
590     MatrixHttpPost(client,\r
591         KEYS_UPLOAD_URL,\r
592         deviceKeysSignedBuffer,\r
593         responseBuffer, KEYS_UPLOAD_RESPONSE_SIZE,\r
594         true);\r
595 \r
596     return true;\r
597 }\r
598 \r
599 // https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv3keysclaim\r
600 bool\r
601 MatrixClientClaimOnetimeKey(\r
602     MatrixClient * client,\r
603     const char * userId,\r
604     const char * deviceId,\r
605     char * outOnetimeKey, int outOnetimeKeyCap)\r
606 {\r
607     static char requestBuffer[KEYS_CLAIM_REQUEST_SIZE];\r
608     mjson_snprintf(requestBuffer, KEYS_CLAIM_REQUEST_SIZE,\r
609     "{"\r
610       "\"one_time_keys\":{"\r
611         "\"%s\":{"\r
612           "\"%s\":\"signed_curve25519\""\r
613         "}"\r
614       "},"\r
615       "\"timeout\":10000"\r
616     "}",\r
617     userId,\r
618     deviceId);\r
619 \r
620     static char responseBuffer[KEYS_CLAIM_RESPONSE_SIZE];\r
621     MatrixHttpPost(client,\r
622         KEYS_CLAIM_URL,\r
623         requestBuffer,\r
624         responseBuffer, KEYS_CLAIM_RESPONSE_SIZE,\r
625         true);\r
626     \r
627     char userIdEscaped[USER_ID_SIZE];\r
628     JsonEscape(userId, strlen(userId),\r
629         userIdEscaped, USER_ID_SIZE);\r
630     \r
631     static char query[JSON_QUERY_SIZE];\r
632     snprintf(query, JSON_QUERY_SIZE,\r
633         "$.one_time_keys.%s.%s",\r
634         userIdEscaped,\r
635         deviceId);\r
636     \r
637     const char * keyObject;\r
638     int keyObjectSize;\r
639     mjson_find(responseBuffer, strlen(responseBuffer),\r
640         query,\r
641         &keyObject, &keyObjectSize);\r
642     \r
643     int koff, klen, voff, vlen, vtype;\r
644     mjson_next(keyObject, keyObjectSize, 0,\r
645         &koff, &klen, &voff, &vlen, &vtype);\r
646     \r
647     mjson_get_string(keyObject + voff, vlen,\r
648         "$.key", outOnetimeKey, outOnetimeKeyCap);\r
649     \r
650     // TODO:verify signature\r
651     \r
652     return true;\r
653 }\r
654 \r
655 // https://spec.matrix.org/v1.6/client-server-api/#post_matrixclientv3login\r
656 bool\r
657 MatrixClientLoginPassword(\r
658     MatrixClient * client,\r
659     const char * username,\r
660     const char * password,\r
661     const char * displayName)\r
662 {\r
663     static char requestBuffer[LOGIN_REQUEST_SIZE];\r
664 \r
665     mjson_snprintf(requestBuffer, LOGIN_REQUEST_SIZE,\r
666         "{"\r
667             "\"type\":\"m.login.password\","\r
668             "\"identifier\":{"\r
669                 "\"type\":\"m.id.user\","\r
670                 "\"user\":\"%s\""\r
671             "},"\r
672             "\"password\":\"%s\","\r
673             "\"initial_device_display_name\":\"%s\""\r
674         "}",\r
675         username,\r
676         password,\r
677         displayName);\r
678     \r
679     static char responseBuffer[LOGIN_RESPONSE_SIZE];\r
680     bool result =\r
681         MatrixHttpPost(client,\r
682             LOGIN_URL,\r
683             requestBuffer,\r
684             responseBuffer, LOGIN_RESPONSE_SIZE,\r
685             false);\r
686     \r
687     if (!result)\r
688         return false;\r
689     \r
690     int responseLen = strlen(responseBuffer);\r
691 \r
692     mjson_get_string(responseBuffer, responseLen,\r
693         "$.access_token",\r
694         client->accessToken, ACCESS_TOKEN_SIZE);\r
695     mjson_get_string(responseBuffer, responseLen,\r
696         "$.device_id",\r
697         client->deviceId, DEVICE_ID_SIZE);\r
698     mjson_get_string(responseBuffer, responseLen,\r
699         "$.expires_in_ms",\r
700         client->expireMs, EXPIRE_MS_SIZE);\r
701     mjson_get_string(responseBuffer, responseLen,\r
702         "$.refresh_token",\r
703         client->refreshToken, REFRESH_TOKEN_SIZE);\r
704 \r
705     return true;\r
706 }\r
707 \r
708 // https://spec.matrix.org/v1.6/client-server-api/#put_matrixclientv3roomsroomidsendeventtypetxnid\r
709 bool\r
710 MatrixClientSendEvent(\r
711     MatrixClient * client,\r
712     const char * roomId,\r
713     const char * msgType,\r
714     const char * msgBody)\r
715 {    \r
716     static char requestUrl[MAX_URL_LEN];\r
717     sprintf(requestUrl,\r
718         ROOMEVENT_URL, roomId, msgType, (int)time(NULL));\r
719 \r
720     static char responseBuffer[ROOMEVENT_RESPONSE_SIZE];\r
721     bool result =\r
722         MatrixHttpPut(client,\r
723             requestUrl,\r
724             msgBody,\r
725             responseBuffer, ROOMEVENT_RESPONSE_SIZE,\r
726             true);\r
727     \r
728     return result;\r
729 }\r
730 \r
731 // https://spec.matrix.org/v1.6/client-server-api/#mroomencrypted\r
732 // https://matrix.org/docs/guides/end-to-end-encryption-implementation-guide#sending-an-encrypted-message-event\r
733 bool\r
734 MatrixClientSendEventEncrypted(\r
735     MatrixClient * client,\r
736     const char * roomId,\r
737     const char * msgType,\r
738     const char * msgBody)\r
739 {\r
740     // event json\r
741     static char requestBuffer[ROOMEVENT_REQUEST_SIZE];\r
742     sprintf(requestBuffer,\r
743         "{"\r
744         "\"type\":\"%s\","\r
745         "\"content\":%s,"\r
746         "\"room_id\":\"%s\""\r
747         "}",\r
748         msgType,\r
749         msgBody,\r
750         roomId);\r
751 \r
752     // get megolm session\r
753     MatrixMegolmOutSession * outSession;\r
754     MatrixClientGetMegolmOutSession(client, roomId, &outSession);\r
755         \r
756     // encrypt\r
757     static char encryptedBuffer[ENCRYPTED_REQUEST_SIZE];\r
758     MatrixMegolmOutSessionEncrypt(outSession,\r
759         requestBuffer,\r
760         encryptedBuffer, ENCRYPTED_REQUEST_SIZE);\r
761 \r
762     char thisDeviceKey[DEVICE_KEY_SIZE];\r
763     MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);\r
764     \r
765 \r
766     // encrypted event json\r
767     const char * senderKey = thisDeviceKey;\r
768     const char * sessionId = outSession->id;\r
769     const char * deviceId = client->deviceId;\r
770 \r
771     static char encryptedEventBuffer[ENCRYPTED_EVENT_SIZE];\r
772     sprintf(encryptedEventBuffer,\r
773         "{"\r
774         "\"algorithm\":\"m.megolm.v1.aes-sha2\","\r
775         "\"ciphertext\":\"%s\","\r
776         "\"device_id\":\"%s\","\r
777         "\"sender_key\":\"%s\","\r
778         "\"session_id\":\"%s\""\r
779         "}",\r
780         encryptedBuffer,\r
781         deviceId,\r
782         senderKey,\r
783         sessionId);\r
784 \r
785     // send\r
786     return MatrixClientSendEvent(client,\r
787         roomId,\r
788         "m.room.encrypted",\r
789         encryptedEventBuffer);\r
790 }\r
791 \r
792 // https://spec.matrix.org/v1.6/client-server-api/#get_matrixclientv3sync\r
793 bool\r
794 MatrixClientSync(\r
795     MatrixClient * client,\r
796     char * outSyncBuffer, int outSyncCap)\r
797 {\r
798     return\r
799         MatrixHttpGet(client,\r
800             "/_matrix/client/v3/sync",\r
801             outSyncBuffer, outSyncCap,\r
802             true);\r
803 }\r
804 \r
805 bool\r
806 MatrixClientShareMegolmOutSession(\r
807     MatrixClient * client,\r
808     const char * userId,\r
809     const char * deviceId,\r
810     MatrixMegolmOutSession * session)\r
811 {\r
812     // generate room key event\r
813     static char eventBuffer[KEY_SHARE_EVENT_LEN];\r
814     sprintf(eventBuffer,\r
815         "{"\r
816             "\"algorithm\":\"m.megolm.v1.aes-sha2\","\r
817             "\"room_id\":\"%s\","\r
818             "\"session_id\":\"%s\","\r
819             "\"session_key\":\"%s\""\r
820         "}",\r
821         session->roomId,\r
822         session->id,\r
823         session->key\r
824     );\r
825 \r
826     // // get olm session\r
827     // MatrixOlmSession * olmSession;\r
828     // MatrixClientGetOlmSession(client, userId, deviceId, &olmSession);\r
829 \r
830     // // encrypt\r
831     // char encryptedBuffer[KEY_SHARE_EVENT_LEN];\r
832     // MatrixOlmSessionEncrypt(olmSession,\r
833     //     eventBuffer,\r
834     //     encryptedBuffer, KEY_SHARE_EVENT_LEN);\r
835 \r
836     // send\r
837     MatrixClientSendToDeviceEncrypted(client,\r
838         userId,\r
839         deviceId,\r
840         eventBuffer,\r
841         "m.room_key");\r
842 \r
843     return true;\r
844 }\r
845 \r
846 bool\r
847 MatrixClientShareMegolmOutSessionTest(\r
848     MatrixClient * client,\r
849     const char * userId,\r
850     const char * deviceId,\r
851     MatrixMegolmOutSession * session)\r
852 {\r
853     // generate room key event\r
854     char eventBuffer[KEY_SHARE_EVENT_LEN];\r
855     sprintf(eventBuffer,\r
856         "{"\r
857             "\"algorithm\":\"m.megolm.v1.aes-sha2\","\r
858             "\"room_id\":\"%s\","\r
859             "\"session_id\":\"%s\","\r
860             "\"session_key\":\"%s\""\r
861         "}",\r
862         session->roomId,\r
863         session->id,\r
864         session->key\r
865     );\r
866 \r
867     // send\r
868     MatrixClientSendToDevice(client,\r
869         userId,\r
870         deviceId,\r
871         eventBuffer,\r
872         "m.room_key");\r
873 \r
874     return true;\r
875 }\r
876 \r
877 // bool\r
878 // MatrixClientSetMegolmOutSession(\r
879 //     MatrixClient * client,\r
880 //     const char * roomId,\r
881 //     MatrixMegolmOutSession session)\r
882 // {\r
883 //     if (client->numMegolmOutSessions < 10)\r
884 //     {\r
885 //         session.roomId = roomId;\r
886 //         client->megolmOutSessions[client->numMegolmOutSessions] = session;\r
887 //         client->numMegolmOutSessions++;\r
888 \r
889 //         return true;\r
890 //     }\r
891 //     return false;\r
892 // }\r
893 \r
894 bool\r
895 MatrixClientGetMegolmOutSession(\r
896     MatrixClient * client,\r
897     const char * roomId,\r
898     MatrixMegolmOutSession ** outSession)\r
899 {\r
900     for (int i = 0; i < client->numMegolmOutSessions; i++)\r
901     {\r
902         if (strcmp(client->megolmOutSessions[i].roomId, roomId) == 0)\r
903         {\r
904             *outSession = &client->megolmOutSessions[i];\r
905             return true;\r
906         }\r
907     }\r
908 \r
909     if (MatrixClientInitMegolmOutSession(client, roomId)) {\r
910         *outSession = &client->megolmOutSessions[client->numMegolmOutSessions-1];\r
911         return true;\r
912     }\r
913 \r
914     return false;\r
915 }\r
916 \r
917 bool\r
918 MatrixClientInitMegolmOutSession(\r
919     MatrixClient * client,\r
920     const char * roomId)\r
921 {\r
922     if (client->numMegolmOutSessions < NUM_MEGOLM_SESSIONS)\r
923     {\r
924         MatrixMegolmOutSessionInit(\r
925             &client->megolmOutSessions[client->numMegolmOutSessions],\r
926             roomId);\r
927         \r
928         client->numMegolmOutSessions++;\r
929 \r
930         return true;\r
931     }\r
932     return false;\r
933 }\r
934 \r
935 bool\r
936 MatrixClientGetOlmSession(\r
937     MatrixClient * client,\r
938     const char * userId,\r
939     const char * deviceId,\r
940     MatrixOlmSession ** outSession)\r
941 {\r
942     for (int i = 0; i < client->numOlmSessions; i++)\r
943     {\r
944         if (strcmp(client->olmSessions[i].deviceId, deviceId) == 0)\r
945         {\r
946             *outSession = &client->olmSessions[i];\r
947             return true;\r
948         }\r
949     }\r
950 \r
951     if (client->numOlmSessions < NUM_OLM_SESSIONS)\r
952     {\r
953         static char deviceKey[DEVICE_KEY_SIZE];\r
954         MatrixClientRequestDeviceKey(client,\r
955             deviceId,\r
956             deviceKey, DEVICE_KEY_SIZE);\r
957 \r
958         char onetimeKey[ONETIME_KEY_SIZE];\r
959         MatrixClientClaimOnetimeKey(client,\r
960             userId,\r
961             deviceId,\r
962             onetimeKey, ONETIME_KEY_SIZE);\r
963 \r
964         MatrixOlmSessionTo(\r
965             &client->olmSessions[client->numOlmSessions],\r
966             client->olmAccount.account,\r
967             deviceId,\r
968             deviceKey,\r
969             onetimeKey);\r
970 \r
971         *outSession = &client->olmSessions[client->numOlmSessions];\r
972         \r
973         client->numOlmSessions++;\r
974 \r
975         return true;\r
976     }\r
977 \r
978     return false;\r
979 }\r
980 \r
981 // https://spec.matrix.org/v1.6/client-server-api/#put_matrixclientv3sendtodeviceeventtypetxnid\r
982 bool\r
983 MatrixClientSendToDevice(\r
984     MatrixClient * client,\r
985     const char * userId,\r
986     const char * deviceId,\r
987     const char * message,\r
988     const char * msgType)\r
989 {\r
990     static char requestUrl[MAX_URL_LEN];\r
991     sprintf(requestUrl,\r
992         TODEVICE_URL, msgType, (int)time(NULL));\r
993 \r
994     static char eventBuffer[TODEVICE_EVENT_SIZE];\r
995     snprintf(eventBuffer, TODEVICE_EVENT_SIZE,\r
996         "{"\r
997             "\"messages\":{"\r
998                 "\"%s\":{"\r
999                     "\"%s\":%s"\r
1000                 "}"\r
1001             "}"\r
1002         "}",\r
1003         userId,\r
1004         deviceId,\r
1005         message);\r
1006 \r
1007     static char responseBuffer[ROOMEVENT_RESPONSE_SIZE];\r
1008     bool result =\r
1009         MatrixHttpPut(client,\r
1010             requestUrl,\r
1011             eventBuffer,\r
1012             responseBuffer, ROOMEVENT_RESPONSE_SIZE,\r
1013             true);\r
1014     \r
1015     return result;\r
1016 }\r
1017 \r
1018 bool\r
1019 MatrixClientSendToDeviceEncrypted(\r
1020     MatrixClient * client,\r
1021     const char * userId,\r
1022     const char * deviceId,\r
1023     const char * message,\r
1024     const char * msgType)\r
1025 {\r
1026     // get olm session\r
1027     MatrixOlmSession * olmSession;\r
1028     MatrixClientGetOlmSession(client, userId, deviceId, &olmSession);\r
1029 \r
1030     // create event json\r
1031     char targetDeviceKey[DEVICE_KEY_SIZE];\r
1032     MatrixClientRequestDeviceKey(client, deviceId, targetDeviceKey, DEVICE_KEY_SIZE);\r
1033     char targetSigningKey[SIGNING_KEY_SIZE];\r
1034     MatrixClientRequestSigningKey(client, deviceId, targetSigningKey, SIGNING_KEY_SIZE);\r
1035     \r
1036     char thisSigningKey[DEVICE_KEY_SIZE];\r
1037     MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, DEVICE_KEY_SIZE);\r
1038 \r
1039     static char eventBuffer[TODEVICE_EVENT_SIZE];\r
1040     sprintf(eventBuffer,\r
1041         "{"\r
1042         "\"type\":\"%s\","\r
1043         "\"content\":%s,"\r
1044         "\"sender\":\"%s\","\r
1045         "\"recipient\":\"%s\","\r
1046         "\"recipient_keys\":{"\r
1047           "\"ed25519\":\"%s\""\r
1048         "},"\r
1049         "\"keys\":{"\r
1050           "\"ed25519\":\"%s\""\r
1051         "}"\r
1052         "}",\r
1053         msgType,\r
1054         message,\r
1055         client->userId,\r
1056         userId, // recipient user id\r
1057         targetSigningKey, // recipient device key\r
1058         thisSigningKey);\r
1059     \r
1060     printf("%s\n", eventBuffer);\r
1061 \r
1062     // encrypt\r
1063     static char encryptedBuffer[ENCRYPTED_REQUEST_SIZE];\r
1064     MatrixOlmSessionEncrypt(olmSession,\r
1065         eventBuffer,\r
1066         encryptedBuffer, ENCRYPTED_REQUEST_SIZE);\r
1067 \r
1068     char thisDeviceKey[DEVICE_KEY_SIZE];\r
1069     MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);\r
1070 \r
1071 \r
1072     static char encryptedEventBuffer[ENCRYPTED_EVENT_SIZE];\r
1073     sprintf(encryptedEventBuffer,\r
1074         "{"\r
1075         "\"algorithm\":\"m.olm.v1.curve25519-aes-sha2\","\r
1076         "\"ciphertext\":{"\r
1077           "\"%s\":{"\r
1078             "\"body\":\"%s\","\r
1079             "\"type\":%d"\r
1080           "}"\r
1081         "},"\r
1082         "\"device_id\":\"%s\","\r
1083         "\"sender_key\":\"%s\""\r
1084         "}",\r
1085         targetDeviceKey,\r
1086         encryptedBuffer,\r
1087         olm_session_has_received_message(olmSession->session),\r
1088         client->deviceId,\r
1089         thisDeviceKey);\r
1090 \r
1091     // send\r
1092     return MatrixClientSendToDevice(\r
1093         client,\r
1094         userId,\r
1095         deviceId,\r
1096         encryptedEventBuffer,\r
1097         "m.room.encrypted");\r
1098 }\r
1099 \r
1100 bool\r
1101 MatrixClientFindDevice(\r
1102     MatrixClient * client,\r
1103     const char * deviceId,\r
1104     MatrixDevice ** outDevice)\r
1105 {\r
1106     MatrixClientRequestDeviceKeys(client);\r
1107 \r
1108     for (int i = 0; i < client->numDevices; i++)\r
1109     {\r
1110         if (strcmp(client->devices[i].deviceId, deviceId) == 0)\r
1111         {\r
1112             *outDevice = &client->devices[i];\r
1113             return true;\r
1114         }\r
1115     }\r
1116 \r
1117     *outDevice = NULL;\r
1118     return false;\r
1119 }\r
1120 \r
1121 bool\r
1122 MatrixClientRequestDeviceKey(\r
1123     MatrixClient * client,\r
1124     const char * deviceId,\r
1125     char * outDeviceKey, int outDeviceKeyCap)\r
1126 {\r
1127     MatrixDevice * device;\r
1128     \r
1129     if (MatrixClientFindDevice(client, deviceId, &device))\r
1130     {\r
1131         strncpy(outDeviceKey, device->deviceKey, outDeviceKeyCap);\r
1132         return true;\r
1133     }\r
1134 \r
1135     MatrixClientRequestDeviceKeys(client);\r
1136     \r
1137     if (MatrixClientFindDevice(client, deviceId, &device))\r
1138     {\r
1139         strncpy(outDeviceKey, device->deviceKey, outDeviceKeyCap);\r
1140         return true;\r
1141     }\r
1142 \r
1143     return false;\r
1144 }\r
1145 \r
1146 bool\r
1147 MatrixClientRequestSigningKey(\r
1148     MatrixClient * client,\r
1149     const char * deviceId,\r
1150     char * outSigningKey, int outSigningKeyCap)\r
1151 {\r
1152     MatrixDevice * device;\r
1153     \r
1154     if (MatrixClientFindDevice(client, deviceId, &device))\r
1155     {\r
1156         strncpy(outSigningKey, device->signingKey, outSigningKeyCap);\r
1157         return true;\r
1158     }\r
1159 \r
1160     MatrixClientRequestDeviceKeys(client);\r
1161     \r
1162     if (MatrixClientFindDevice(client, deviceId, &device))\r
1163     {\r
1164         strncpy(outSigningKey, device->signingKey, outSigningKeyCap);\r
1165         return true;\r
1166     }\r
1167 \r
1168     return false;\r
1169 }\r
1170 \r
1171 // https://spec.matrix.org/v1.6/client-server-api/#post_matrixclientv3keysquery\r
1172 bool\r
1173 MatrixClientRequestDeviceKeys(\r
1174     MatrixClient * client)\r
1175 {\r
1176     static char userIdEscaped[USER_ID_SIZE];\r
1177     JsonEscape(client->userId, strlen(client->userId),\r
1178         userIdEscaped, USER_ID_SIZE);\r
1179 \r
1180     static char request[KEYS_QUERY_REQUEST_SIZE];\r
1181     snprintf(request, KEYS_QUERY_REQUEST_SIZE,\r
1182         "{\"device_keys\":{\"%s\":[]}}", client->userId);\r
1183 \r
1184     static char responseBuffer[KEYS_QUERY_RESPONSE_SIZE];\r
1185     bool requestResult = MatrixHttpPost(client,\r
1186         KEYS_QUERY_URL,\r
1187         request,\r
1188         responseBuffer, KEYS_QUERY_RESPONSE_SIZE,\r
1189         true);\r
1190 \r
1191     if (! requestResult)\r
1192         return false;\r
1193 \r
1194     // query for retrieving device keys for user id\r
1195     static char query[JSON_QUERY_SIZE];\r
1196     snprintf(query, JSON_QUERY_SIZE,\r
1197         "$.device_keys.%s", userIdEscaped);\r
1198     \r
1199     const char * s;\r
1200     int slen;\r
1201     mjson_find(responseBuffer, strlen(responseBuffer),\r
1202         query, &s, &slen);\r
1203     \r
1204     // loop over keys\r
1205     \r
1206     int koff, klen, voff, vlen, vtype, off = 0;\r
1207     for (off = 0; (off = mjson_next(s, slen, off, &koff, &klen,\r
1208                                     &voff, &vlen, &vtype)) != 0; ) {\r
1209         const char * key = s + koff;\r
1210         const char * val = s + voff;\r
1211 \r
1212         // set device id, "key" is the JSON key\r
1213         MatrixDevice d;\r
1214         snprintf(d.deviceId, DEVICE_ID_SIZE,\r
1215             "%.*s", klen-2, key+1);\r
1216 \r
1217         // look for device key in value\r
1218         static char deviceKeyQuery[JSON_QUERY_SIZE];\r
1219         snprintf(deviceKeyQuery, JSON_QUERY_SIZE,\r
1220             "$.keys.curve25519:%s", d.deviceId);\r
1221         mjson_get_string(val, vlen,\r
1222             deviceKeyQuery, d.deviceKey, DEVICE_KEY_SIZE);\r
1223 \r
1224         // look for signing key in value\r
1225         static char signingKeyQuery[JSON_QUERY_SIZE];\r
1226         snprintf(signingKeyQuery, JSON_QUERY_SIZE,\r
1227             "$.keys.ed25519:%s", d.deviceId);\r
1228         mjson_get_string(val, vlen,\r
1229             signingKeyQuery, d.signingKey, SIGNING_KEY_SIZE);\r
1230 \r
1231         // add device\r
1232         if (client->numDevices < NUM_DEVICES)\r
1233         {\r
1234             bool foundDevice = false;\r
1235             for (int i = 0; i < client->numDevices; i++)\r
1236                 if (strcmp(client->devices[i].deviceId, d.deviceId) == 0)\r
1237                     foundDevice = true;\r
1238 \r
1239             if (! foundDevice) {\r
1240                 printf("new device: %s %s %s\n", d.deviceId, d.deviceKey, d.signingKey);\r
1241                 client->devices[client->numDevices] = d;\r
1242                 client->numDevices++;\r
1243             }\r
1244         }\r
1245         else\r
1246         {\r
1247             return false;\r
1248         }\r
1249     }\r
1250 \r
1251     return true;\r
1252 }\r
1253 \r
1254 bool\r
1255 MatrixClientDeleteDevice(\r
1256     MatrixClient * client)\r
1257 {\r
1258     static char deleteRequest[1024];\r
1259     snprintf(deleteRequest, 1024,\r
1260         "{\"devices\":[\"%s\"]}",\r
1261         client->deviceId);\r
1262     static char deleteResponse[1024];\r
1263     bool res = MatrixHttpPost(client, "/_matrix/client/v3/delete_devices",\r
1264         deleteRequest, deleteResponse, 1024, true);\r
1265     return res;\r
1266 }