8 #define LOGIN_REQUEST_SIZE 1024
\r
9 #define LOGIN_RESPONSE_SIZE 1024
\r
10 #define LOGIN_URL "/_matrix/client/v3/login"
\r
12 #define ENCRYPTED_REQUEST_SIZE (1024*5)
\r
13 #define ENCRYPTED_EVENT_SIZE (1024*10)
\r
14 #define ROOM_SEND_REQUEST_SIZE 256
\r
15 #define ROOM_SEND_RESPONSE_SIZE 1024
\r
16 #define ROOM_SEND_URL "/_matrix/client/v3/rooms/%s/send/%s/%d"
\r
18 #define ROOMKEY_REQUEST_SIZE (1024*4)
\r
20 #define TODEVICE_EVENT_SIZE (1024*5)
\r
21 #define TODEVICE_URL "/_matrix/client/v3/sendToDevice/%s/%d"
\r
23 #define KEYS_QUERY_URL "/_matrix/client/v3/keys/query"
\r
24 #define KEYS_QUERY_REQUEST_SIZE 256
\r
25 #define KEYS_QUERY_RESPONSE_SIZE (1024*10)
\r
27 #define KEYS_UPLOAD_URL "/_matrix/client/v3/keys/upload"
\r
28 #define KEYS_UPLOAD_REQUEST_SIZE 1024*4
\r
29 #define KEYS_UPLOAD_REQUEST_SIGNED_SIZE 2048*4
\r
30 #define KEYS_UPLOAD_RESPONSE_SIZE 2048
\r
32 #define KEYS_CLAIM_URL "/_matrix/client/v3/keys/claim"
\r
33 #define KEYS_CLAIM_REQUEST_SIZE 1024
\r
34 #define KEYS_CLAIM_RESPONSE_SIZE 1024
\r
36 #define SYNC_TIMEOUT 5000
\r
38 #define JSON_QUERY_SIZE 128
\r
39 #define JSON_MAX_INDICES 100
\r
40 #define JSON_MAX_ENTRY_SIZE 1024
\r
42 #define MAX(a,b) ((a) > (b) ? (a) : (b))
\r
43 #define MIN(a,b) ((a) < (b) ? (a) : (b))
\r
50 static bool first = true;
\r
51 if (first) { srand(time(0)); first = false; }
\r
53 for (int i = 0; i < randomLen; i++)
\r
55 random[i] = rand() % 256;
\r
61 const char * sIn, int sInLen,
\r
62 char * sOut, int sOutCap)
\r
66 for (int i = 0; i < sInLen; i++)
\r
71 if (sIn[i] == '.' ||
\r
75 sOut[sOutIndex++] = '\\';
\r
77 sOut[sOutIndex++] = sIn[i];
\r
80 if (sOutIndex < sOutCap)
\r
81 sOut[sOutIndex] = '\0';
\r
88 const char * sIn, int sInLen,
\r
89 char * sOut, int sOutCap)
\r
91 snprintf(sOut, sOutCap, "{}");
\r
93 int koff, klen, voff, vlen, vtype, off;
\r
100 struct Key keys[JSON_MAX_INDICES];
\r
103 for (off = 0; (off = mjson_next(sIn, sInLen, off, &koff, &klen, &voff, &vlen, &vtype)) != 0; ) {
\r
104 keys[numKeys].ptr = sIn + koff;
\r
105 keys[numKeys].len = klen;
\r
109 for (int i = 0; i < numKeys; i++) {
\r
110 for (int j = i; j < numKeys; j++) {
\r
115 MIN(keys[i].len, keys[j].len)
\r
118 struct Key k = keys[i];
\r
125 for (int i = 0; i < numKeys; i++) {
\r
126 char jp[JSON_QUERY_SIZE];
\r
127 snprintf(jp, JSON_QUERY_SIZE, "$.%.*s", keys[i].len-2, keys[i].ptr+1);
\r
129 const char * valPtr;
\r
131 mjson_find(sIn, sInLen, jp, &valPtr, &valLen);
\r
133 static char newEntry[JSON_MAX_ENTRY_SIZE];
\r
134 snprintf(newEntry, JSON_MAX_ENTRY_SIZE, "{%.*s:%.*s}", keys[i].len, keys[i].ptr, valLen, valPtr);
\r
136 char * buffer = strdup(sOut);
\r
138 struct mjson_fixedbuf fb = { sOut, sOutCap, 0 };
\r
139 mjson_merge(buffer, strlen(buffer), newEntry, strlen(newEntry), mjson_print_fixed_buf, &fb);
\r
144 // TODO: recursively sort entries
\r
150 MatrixClient * client,
\r
151 const char * sIn, int sInLen,
\r
152 char * sOut, int sOutCap)
\r
154 static char signature[OLM_SIGNATURE_SIZE];
\r
156 olm_account_sign(client->olmAccount.account,
\r
158 signature, OLM_SIGNATURE_SIZE);
\r
160 int signatureLen = res;
\r
162 static char thisSigningKey[SIGNING_KEY_SIZE];
\r
163 MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, SIGNING_KEY_SIZE);
\r
165 static char signatureJson[JSON_SIGNATURE_SIZE];
\r
166 int signatureJsonLen =
\r
167 mjson_snprintf(signatureJson, JSON_SIGNATURE_SIZE,
\r
171 "\"ed25519:%s\":\"%.*s\""
\r
178 signatureLen, signature);
\r
180 struct mjson_fixedbuf result = { sOut, sOutCap, 0 };
\r
183 signatureJson, signatureJsonLen,
\r
184 mjson_print_fixed_buf,
\r
192 MatrixOlmAccountInit(
\r
193 MatrixOlmAccount * account)
\r
195 account->account = olm_account(account->memory);
\r
197 static uint8_t random[OLM_ACCOUNT_RANDOM_SIZE];
\r
198 Randomize(random, OLM_ACCOUNT_RANDOM_SIZE);
\r
200 size_t res = olm_create_account(
\r
203 OLM_ACCOUNT_RANDOM_SIZE);
\r
205 return res != olm_error();
\r
209 MatrixOlmAccountUnpickle(
\r
210 MatrixOlmAccount * account,
\r
211 void * pickled, int pickledLen,
\r
212 const void * key, int keyLen)
\r
215 res = olm_unpickle_account(account->account,
\r
217 pickled, pickledLen);
\r
218 if (res == olm_error()) {
\r
219 printf("error unpickling olm account:%s\n",
\r
220 olm_account_last_error(account->account));
\r
222 return res != olm_error();
\r
226 MatrixOlmAccountGetDeviceKey(
\r
227 MatrixOlmAccount * account,
\r
228 char * key, int keyCap)
\r
230 static char deviceKeysJson[OLM_IDENTITY_KEYS_JSON_SIZE];
\r
232 olm_account_identity_keys(account->account,
\r
233 deviceKeysJson, OLM_IDENTITY_KEYS_JSON_SIZE);
\r
234 mjson_get_string(deviceKeysJson, res,
\r
241 MatrixOlmAccountGetSigningKey(
\r
242 MatrixOlmAccount * account,
\r
243 char * key, int keyCap)
\r
245 static char deviceKeysJson[OLM_IDENTITY_KEYS_JSON_SIZE];
\r
247 olm_account_identity_keys(account->account,
\r
248 deviceKeysJson, OLM_IDENTITY_KEYS_JSON_SIZE);
\r
249 mjson_get_string(deviceKeysJson, res,
\r
256 MatrixOlmSessionFrom(
\r
257 MatrixOlmSession * session,
\r
258 OlmAccount * olmAccount,
\r
259 const char * deviceId,
\r
260 const char * deviceKey,
\r
261 const char * encrypted)
\r
263 memset(session, 0, sizeof(MatrixOlmSession));
\r
265 session->deviceId = deviceId;
\r
268 olm_session(session->memory);
\r
270 char * encryptedCopy = strdup(encrypted);
\r
273 olm_create_inbound_session_from(session->session, olmAccount,
\r
274 deviceKey, strlen(deviceKey),
\r
275 encryptedCopy, strlen(encryptedCopy));
\r
277 if (res == olm_error()) {
\r
278 printf("error olm:%s\n", olm_session_last_error(session->session));
\r
281 return res != olm_error();
\r
285 MatrixOlmSessionTo(
\r
286 MatrixOlmSession * session,
\r
287 OlmAccount * olmAccount,
\r
288 const char * deviceId,
\r
289 const char * deviceKey,
\r
290 const char * deviceOnetimeKey)
\r
292 memset(session, 0, sizeof(MatrixOlmSession));
\r
294 session->deviceId = deviceId;
\r
297 olm_session(session->memory);
\r
299 static uint8_t random[OLM_OUTBOUND_SESSION_RANDOM_SIZE];
\r
300 Randomize(random, OLM_OUTBOUND_SESSION_RANDOM_SIZE);
\r
303 olm_create_outbound_session(session->session,
\r
305 deviceKey, strlen(deviceKey),
\r
306 deviceOnetimeKey, strlen(deviceOnetimeKey),
\r
307 random, OLM_OUTBOUND_SESSION_RANDOM_SIZE);
\r
309 if (res == olm_error()) {
\r
310 printf("error olm:%s\n", olm_session_last_error(session->session));
\r
313 return res != olm_error();
\r
317 MatrixOlmSessionUnpickle(
\r
318 MatrixOlmSession * session,
\r
319 const char * deviceId,
\r
320 void * pickled, int pickledLen,
\r
321 const void * key, int keyLen)
\r
323 memset(session, 0, sizeof(MatrixOlmSession));
\r
325 session->deviceId = deviceId;
\r
328 olm_session(session->memory);
\r
331 res = olm_unpickle_session(session->session,
\r
333 pickled, pickledLen);
\r
335 if (res == olm_error()) {
\r
336 printf("error unpickling olm session:%s\n", olm_session_last_error(session->session));
\r
339 return res != olm_error();
\r
343 MatrixOlmSessionEncrypt(
\r
344 MatrixOlmSession * session,
\r
345 const char * plaintext,
\r
346 char * outBuffer, int outBufferCap)
\r
348 static uint8_t random[OLM_ENCRYPT_RANDOM_SIZE];
\r
349 Randomize(random, OLM_ENCRYPT_RANDOM_SIZE);
\r
351 size_t res = olm_encrypt(session->session,
\r
352 plaintext, strlen(plaintext),
\r
353 random, OLM_ENCRYPT_RANDOM_SIZE,
\r
354 outBuffer, outBufferCap);
\r
356 return res != olm_error();
\r
360 MatrixOlmSessionDecrypt(
\r
361 MatrixOlmSession * session,
\r
362 size_t messageType,
\r
364 char * outBuffer, int outBufferCap)
\r
366 static uint8_t random[OLM_ENCRYPT_RANDOM_SIZE];
\r
367 Randomize(random, OLM_ENCRYPT_RANDOM_SIZE);
\r
370 olm_decrypt(session->session,
\r
372 encrypted, strlen(encrypted),
\r
373 outBuffer, outBufferCap);
\r
375 if (res != olm_error() && res < outBufferCap)
\r
376 outBuffer[res] = '\0';
\r
378 return res != olm_error();
\r
382 MatrixMegolmInSessionInit(
\r
383 MatrixMegolmInSession * session,
\r
384 const char * roomId,
\r
385 const char * sessionId,
\r
386 const char * sessionKey, int sessionKeyLen)
\r
388 memset(session, 0, sizeof(MatrixMegolmInSession));
\r
390 strncpy(session->roomId, roomId, sizeof(session->roomId));
\r
391 strncpy(session->id, sessionId, sizeof(session->id));
\r
392 strncpy(session->key, sessionKey, sizeof(session->key));
\r
395 olm_inbound_group_session(session->memory);
\r
398 olm_init_inbound_group_session(
\r
399 // olm_import_inbound_group_session(
\r
401 (const uint8_t *)sessionKey, sessionKeyLen);
\r
402 if (res == olm_error()) {
\r
403 printf("Error initializing Megolm session: %s\n", olm_inbound_group_session_last_error(session->session));
\r
406 return res != olm_error();
\r
410 MatrixMegolmInSessionDecrypt(
\r
411 MatrixMegolmInSession * session,
\r
412 const char * encrypted, int encryptedLen,
\r
413 char * outDecrypted, int outDecryptedCap)
\r
415 // uint8_t buffer[1024];
\r
416 // memcpy(buffer, encrypted, encryptedLen);
\r
418 uint32_t megolmInMessageIndex;
\r
421 olm_group_decrypt(session->session,
\r
422 (uint8_t *)encrypted, encryptedLen,
\r
423 (uint8_t *)outDecrypted, outDecryptedCap,
\r
424 &megolmInMessageIndex);
\r
426 printf("message index: %d\n", megolmInMessageIndex);
\r
428 if (res == olm_error()) {
\r
429 printf("error decrypting megolm message: %s\n", olm_inbound_group_session_last_error(session->session));
\r
432 printf("decrypted len: %d\n", res);
\r
438 // https://matrix.org/docs/guides/end-to-end-encryption-implementation-guide#starting-a-megolm-session
\r
440 MatrixMegolmOutSessionInit(
\r
441 MatrixMegolmOutSession * session,
\r
442 const char * roomId)
\r
444 memset(session, 0, sizeof(MatrixMegolmOutSession));
\r
446 static uint8_t random[MEGOLM_INIT_RANDOM_SIZE];
\r
447 Randomize(random, MEGOLM_INIT_RANDOM_SIZE);
\r
449 strncpy(session->roomId, roomId, ROOM_ID_SIZE);
\r
452 olm_outbound_group_session(session->memory);
\r
454 olm_init_outbound_group_session(
\r
457 MEGOLM_INIT_RANDOM_SIZE);
\r
459 olm_outbound_group_session_id(session->session,
\r
460 (uint8_t *)session->id,
\r
461 MEGOLM_SESSION_ID_SIZE);
\r
463 olm_outbound_group_session_key(session->session,
\r
464 (uint8_t *)session->key,
\r
465 MEGOLM_SESSION_KEY_SIZE);
\r
471 MatrixMegolmOutSessionEncrypt(
\r
472 MatrixMegolmOutSession * session,
\r
473 const char * plaintext,
\r
474 char * outBuffer, int outBufferCap)
\r
476 size_t res = olm_group_encrypt(session->session,
\r
477 (uint8_t *)plaintext, strlen(plaintext),
\r
478 (uint8_t *)outBuffer, outBufferCap);
\r
480 return res != olm_error();
\r
484 MatrixMegolmOutSessionSave(
\r
485 MatrixMegolmOutSession * session,
\r
486 const char * filename,
\r
489 FILE * f = fopen(filename, "w");
\r
491 size_t roomIdLen = strlen(session->roomId);
\r
492 fwrite(&roomIdLen, sizeof(size_t), 1, f);
\r
493 fwrite(session->roomId, 1, roomIdLen, f);
\r
495 size_t pickleBufferLen =
\r
496 olm_pickle_outbound_group_session_length(
\r
498 void * pickleBuffer = malloc(pickleBufferLen);
\r
500 olm_pickle_outbound_group_session(
\r
503 pickleBuffer, pickleBufferLen);
\r
505 fwrite(&pickleBufferLen, sizeof(size_t), 1, f);
\r
506 fwrite(pickleBuffer, 1, pickleBufferLen, f);
\r
507 free(pickleBuffer);
\r
515 MatrixMegolmOutSessionLoad(
\r
516 MatrixMegolmOutSession * session,
\r
517 const char * filename,
\r
520 FILE * f = fopen(filename, "r");
\r
523 fread(&roomIdLen, sizeof(size_t), 1, f);
\r
524 fread(session->roomId, 1, roomIdLen, f);
\r
525 for (int i = roomIdLen; i < ROOM_ID_SIZE; i++)
\r
526 session->roomId[i] = '\0';
\r
528 size_t pickleBufferLen;
\r
529 fread(&pickleBufferLen, sizeof(size_t), 1, f);
\r
531 void * pickleBuffer = malloc(pickleBufferLen);
\r
532 fread(pickleBuffer, 1, pickleBufferLen, f);
\r
534 olm_unpickle_outbound_group_session(
\r
537 pickleBuffer, pickleBufferLen);
\r
539 free(pickleBuffer);
\r
541 olm_outbound_group_session_id(session->session, (uint8_t *)session->id, MEGOLM_SESSION_ID_SIZE);
\r
542 olm_outbound_group_session_key(session->session, (uint8_t *)session->key, MEGOLM_SESSION_KEY_SIZE);
\r
553 MatrixClient * client,
\r
554 const char * server)
\r
556 memset(client, 0, sizeof(MatrixClient));
\r
558 strcpy(client->server, server);
\r
560 // init olm account
\r
561 MatrixOlmAccountInit(&client->olmAccount);
\r
568 MatrixClient * client,
\r
569 const char * filename)
\r
571 FILE * f = fopen(filename, "w");
\r
574 char thisDeviceKey[DEVICE_KEY_SIZE];
\r
575 MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);
\r
576 char thisSigningKey[DEVICE_KEY_SIZE];
\r
577 MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, DEVICE_KEY_SIZE);
\r
580 fwrite(thisDeviceKey, 1, DEVICE_KEY_SIZE, f);
\r
581 fwrite(thisSigningKey, 1, DEVICE_KEY_SIZE, f);
\r
582 fwrite(client->userId, 1, USER_ID_SIZE, f);
\r
583 fwrite(client->server, 1, SERVER_SIZE, f);
\r
584 fwrite(client->accessToken, 1, ACCESS_TOKEN_SIZE, f);
\r
585 fwrite(client->deviceId, 1, DEVICE_ID_SIZE, f);
\r
586 fwrite(client->expireMs, 1, EXPIRE_MS_SIZE, f);
\r
587 fwrite(client->refreshToken, 1, REFRESH_TOKEN_SIZE, f);
\r
589 fwrite(&client->numDevices, sizeof(int), 1, f);
\r
590 for (int i = 0; i < client->numDevices; i++) {
\r
591 fwrite(client->devices[i].deviceId, 1, DEVICE_ID_SIZE, f);
\r
592 fwrite(client->devices[i].deviceKey, 1, DEVICE_KEY_SIZE, f);
\r
601 MatrixClient * client,
\r
602 const char * filename)
\r
604 FILE * f = fopen(filename, "r");
\r
607 char thisDeviceKey[DEVICE_KEY_SIZE];
\r
608 MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);
\r
609 char thisSigningKey[DEVICE_KEY_SIZE];
\r
610 MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, DEVICE_KEY_SIZE);
\r
613 fread(thisDeviceKey, 1, DEVICE_KEY_SIZE, f);
\r
614 fread(thisSigningKey, 1, DEVICE_KEY_SIZE, f);
\r
615 fread(client->userId, 1, USER_ID_SIZE, f);
\r
616 fread(client->server, 1, SERVER_SIZE, f);
\r
617 fread(client->accessToken, 1, ACCESS_TOKEN_SIZE, f);
\r
618 fread(client->deviceId, 1, DEVICE_ID_SIZE, f);
\r
619 fread(client->expireMs, 1, EXPIRE_MS_SIZE, f);
\r
620 fread(client->refreshToken, 1, REFRESH_TOKEN_SIZE, f);
\r
622 fread(&client->numDevices, sizeof(int), 1, f);
\r
623 for (int i = 0; i < client->numDevices; i++) {
\r
624 fread(client->devices[i].deviceId, 1, DEVICE_ID_SIZE, f);
\r
625 fread(client->devices[i].deviceKey, 1, DEVICE_KEY_SIZE, f);
\r
633 MatrixClientSetAccessToken(
\r
634 MatrixClient * client,
\r
635 const char * accessToken)
\r
637 for (int i = 0; i < ACCESS_TOKEN_SIZE-1; i++)
\r
638 client->accessToken[i] = accessToken[i];
\r
639 client->accessToken[ACCESS_TOKEN_SIZE-1] = '\0';
\r
645 MatrixClientSetDeviceId(
\r
646 MatrixClient * client,
\r
647 const char * deviceId)
\r
649 for (int i = 0; i < DEVICE_ID_SIZE-1; i++)
\r
650 client->deviceId[i] = deviceId[i];
\r
651 client->deviceId[DEVICE_ID_SIZE-1] = '\0';
\r
657 MatrixClientSetUserId(
\r
658 MatrixClient * client,
\r
659 const char * userId)
\r
661 for (int i = 0; i < USER_ID_SIZE-1; i++)
\r
662 client->userId[i] = userId[i];
\r
663 client->userId[USER_ID_SIZE-1] = '\0';
\r
669 MatrixClientGenerateOnetimeKeys(
\r
670 MatrixClient * client,
\r
673 static uint8_t random[OLM_ONETIME_KEYS_RANDOM_SIZE];
\r
674 Randomize(random, OLM_ONETIME_KEYS_RANDOM_SIZE);
\r
677 olm_account_generate_one_time_keys(client->olmAccount.account,
\r
678 numberOfKeys, random, OLM_ONETIME_KEYS_RANDOM_SIZE);
\r
680 return res != olm_error();
\r
683 // https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv3keysupload
\r
685 MatrixClientUploadOnetimeKeys(
\r
686 MatrixClient * client)
\r
688 static char requestBuffer[KEYS_UPLOAD_REQUEST_SIZE];
\r
690 mjson_snprintf(requestBuffer, KEYS_UPLOAD_REQUEST_SIZE,
\r
693 static char onetimeKeysBuffer[1024];
\r
694 olm_account_one_time_keys(client->olmAccount.account,
\r
695 onetimeKeysBuffer, 1024);
\r
699 mjson_find(onetimeKeysBuffer, strlen(onetimeKeysBuffer), "$.curve25519", &keys, &keysLen);
\r
701 int koff, klen, voff, vlen, vtype, off = 0;
\r
702 while ((off = mjson_next(keys, keysLen, off, &koff, &klen, &voff, &vlen, &vtype)) != 0) {
\r
703 static char keyJson[JSON_ONETIME_KEY_SIZE];
\r
706 snprintf(keyJson, JSON_ONETIME_KEY_SIZE,
\r
707 "{\"key\":\"%.*s\"}",
\r
708 vlen-2, keys + voff+1);
\r
710 static char keyJsonSigned[JSON_ONETIME_KEY_SIGNED_SIZE];
\r
713 keyJson, keyJsonLen,
\r
714 keyJsonSigned, JSON_ONETIME_KEY_SIGNED_SIZE);
\r
716 mjson_snprintf(requestBuffer+strlen(requestBuffer), KEYS_UPLOAD_REQUEST_SIZE-strlen(requestBuffer),
\r
717 "\"signed_curve25519:%.*s\":%s,",
\r
718 klen-2, keys + koff+1,
\r
722 if (requestBuffer[strlen(requestBuffer)-1] == ',')
\r
723 requestBuffer[strlen(requestBuffer)-1] = '\0';
\r
725 mjson_snprintf(requestBuffer+strlen(requestBuffer), KEYS_UPLOAD_REQUEST_SIZE-strlen(requestBuffer),
\r
728 // static char onetimeKeysSignedBuffer[KEYS_UPLOAD_REQUEST_SIGNED_SIZE];
\r
729 // JsonSign(client,
\r
730 // requestBuffer, strlen(requestBuffer),
\r
731 // onetimeKeysSignedBuffer, KEYS_UPLOAD_REQUEST_SIZE);
\r
733 // static char finalEvent[KEYS_UPLOAD_REQUEST_SIGNED_SIZE];
\r
734 // snprintf(finalEvent, KEYS_UPLOAD_REQUEST_SIGNED_SIZE,
\r
735 // "{\"one_time_keys\":%s}", onetimeKeysSignedBuffer);
\r
736 static char finalEvent[KEYS_UPLOAD_REQUEST_SIGNED_SIZE];
\r
737 snprintf(finalEvent, KEYS_UPLOAD_REQUEST_SIGNED_SIZE,
\r
738 "{\"one_time_keys\":%s}", requestBuffer);
\r
740 static char responseBuffer[KEYS_UPLOAD_RESPONSE_SIZE];
\r
741 MatrixHttpPost(client,
\r
744 responseBuffer, KEYS_UPLOAD_RESPONSE_SIZE,
\r
750 // https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv3keysupload
\r
752 MatrixClientUploadDeviceKey(
\r
753 MatrixClient * client)
\r
755 char thisDeviceKey[DEVICE_KEY_SIZE];
\r
756 MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);
\r
757 char thisSigningKey[DEVICE_KEY_SIZE];
\r
758 MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, DEVICE_KEY_SIZE);
\r
760 static char deviceKeysBuffer[KEYS_UPLOAD_REQUEST_SIZE];
\r
762 int deviceKeysBufferLen =
\r
763 mjson_snprintf(deviceKeysBuffer, KEYS_UPLOAD_REQUEST_SIZE,
\r
765 "\"algorithms\":[\"m.olm.v1.curve25519-aes-sha2\",\"m.megolm.v1.aes-sha2\"],"
\r
766 "\"device_id\":\"%s\","
\r
768 "\"curve25519:%s\":\"%s\","
\r
769 "\"ed25519:%s\":\"%s\""
\r
771 "\"user_id\":\"%s\""
\r
774 client->deviceId, thisDeviceKey,
\r
775 client->deviceId, thisSigningKey,
\r
778 static char deviceKeysSignedBuffer[KEYS_UPLOAD_REQUEST_SIGNED_SIZE];
\r
780 deviceKeysBuffer, deviceKeysBufferLen,
\r
781 deviceKeysSignedBuffer, KEYS_UPLOAD_REQUEST_SIZE);
\r
783 static char finalEvent[KEYS_UPLOAD_REQUEST_SIGNED_SIZE];
\r
784 snprintf(finalEvent, KEYS_UPLOAD_REQUEST_SIGNED_SIZE,
\r
785 "{\"device_keys\":%s}", deviceKeysSignedBuffer);
\r
787 static char responseBuffer[KEYS_UPLOAD_RESPONSE_SIZE];
\r
788 MatrixHttpPost(client,
\r
791 responseBuffer, KEYS_UPLOAD_RESPONSE_SIZE,
\r
797 // https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv3keysclaim
\r
799 MatrixClientClaimOnetimeKey(
\r
800 MatrixClient * client,
\r
801 const char * userId,
\r
802 const char * deviceId,
\r
803 char * outOnetimeKey, int outOnetimeKeyCap)
\r
805 static char requestBuffer[KEYS_CLAIM_REQUEST_SIZE];
\r
806 mjson_snprintf(requestBuffer, KEYS_CLAIM_REQUEST_SIZE,
\r
808 "\"one_time_keys\":{"
\r
810 "\"%s\":\"signed_curve25519\""
\r
813 "\"timeout\":10000"
\r
818 static char responseBuffer[KEYS_CLAIM_RESPONSE_SIZE];
\r
819 MatrixHttpPost(client,
\r
822 responseBuffer, KEYS_CLAIM_RESPONSE_SIZE,
\r
825 char userIdEscaped[USER_ID_SIZE];
\r
826 JsonEscape(userId, strlen(userId),
\r
827 userIdEscaped, USER_ID_SIZE);
\r
829 static char query[JSON_QUERY_SIZE];
\r
830 snprintf(query, JSON_QUERY_SIZE,
\r
831 "$.one_time_keys.%s.%s",
\r
835 const char * keyObject;
\r
837 mjson_find(responseBuffer, strlen(responseBuffer),
\r
839 &keyObject, &keyObjectSize);
\r
841 int koff, klen, voff, vlen, vtype;
\r
842 mjson_next(keyObject, keyObjectSize, 0,
\r
843 &koff, &klen, &voff, &vlen, &vtype);
\r
845 mjson_get_string(keyObject + voff, vlen,
\r
846 "$.key", outOnetimeKey, outOnetimeKeyCap);
\r
848 // TODO:verify signature
\r
853 // https://spec.matrix.org/v1.6/client-server-api/#post_matrixclientv3login
\r
855 MatrixClientLoginPassword(
\r
856 MatrixClient * client,
\r
857 const char * username,
\r
858 const char * password,
\r
859 const char * displayName)
\r
861 static char requestBuffer[LOGIN_REQUEST_SIZE];
\r
863 mjson_snprintf(requestBuffer, LOGIN_REQUEST_SIZE,
\r
865 "\"type\":\"m.login.password\","
\r
867 "\"type\":\"m.id.user\","
\r
870 "\"password\":\"%s\","
\r
871 "\"initial_device_display_name\":\"%s\""
\r
877 static char responseBuffer[LOGIN_RESPONSE_SIZE];
\r
879 MatrixHttpPost(client,
\r
882 responseBuffer, LOGIN_RESPONSE_SIZE,
\r
888 int responseLen = strlen(responseBuffer);
\r
890 mjson_get_string(responseBuffer, responseLen,
\r
892 client->accessToken, ACCESS_TOKEN_SIZE);
\r
893 mjson_get_string(responseBuffer, responseLen,
\r
895 client->deviceId, DEVICE_ID_SIZE);
\r
896 mjson_get_string(responseBuffer, responseLen,
\r
898 client->expireMs, EXPIRE_MS_SIZE);
\r
899 mjson_get_string(responseBuffer, responseLen,
\r
901 client->refreshToken, REFRESH_TOKEN_SIZE);
\r
906 // https://spec.matrix.org/v1.6/client-server-api/#put_matrixclientv3roomsroomidsendeventtypetxnid
\r
908 MatrixClientSendEvent(
\r
909 MatrixClient * client,
\r
910 const char * roomId,
\r
911 const char * msgType,
\r
912 const char * msgBody)
\r
914 static char requestUrl[MAX_URL_LEN];
\r
915 sprintf(requestUrl,
\r
916 ROOM_SEND_URL, roomId, msgType, (int)time(NULL));
\r
918 static char responseBuffer[ROOM_SEND_RESPONSE_SIZE];
\r
920 MatrixHttpPut(client,
\r
923 responseBuffer, ROOM_SEND_RESPONSE_SIZE,
\r
929 // https://spec.matrix.org/v1.6/client-server-api/#mroomencrypted
\r
930 // https://matrix.org/docs/guides/end-to-end-encryption-implementation-guide#sending-an-encrypted-message-event
\r
932 MatrixClientSendEventEncrypted(
\r
933 MatrixClient * client,
\r
934 const char * roomId,
\r
935 const char * msgType,
\r
936 const char * msgBody)
\r
939 static char requestBuffer[ROOM_SEND_REQUEST_SIZE];
\r
940 sprintf(requestBuffer,
\r
944 "\"room_id\":\"%s\""
\r
950 // get megolm session
\r
951 MatrixMegolmOutSession * outSession;
\r
952 if (! MatrixClientGetMegolmOutSession(client, roomId, &outSession))
\r
953 MatrixClientNewMegolmOutSession(client, roomId, &outSession);
\r
956 static char encryptedBuffer[ENCRYPTED_REQUEST_SIZE];
\r
957 MatrixMegolmOutSessionEncrypt(outSession,
\r
959 encryptedBuffer, ENCRYPTED_REQUEST_SIZE);
\r
961 char thisDeviceKey[DEVICE_KEY_SIZE];
\r
962 MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);
\r
965 // encrypted event json
\r
966 const char * senderKey = thisDeviceKey;
\r
967 const char * sessionId = outSession->id;
\r
968 const char * deviceId = client->deviceId;
\r
970 static char encryptedEventBuffer[ENCRYPTED_EVENT_SIZE];
\r
971 sprintf(encryptedEventBuffer,
\r
973 "\"algorithm\":\"m.megolm.v1.aes-sha2\","
\r
974 "\"ciphertext\":\"%s\","
\r
975 "\"device_id\":\"%s\","
\r
976 "\"sender_key\":\"%s\","
\r
977 "\"session_id\":\"%s\""
\r
985 return MatrixClientSendEvent(client,
\r
987 "m.room.encrypted",
\r
988 encryptedEventBuffer);
\r
991 // https://spec.matrix.org/v1.6/client-server-api/#get_matrixclientv3sync
\r
994 MatrixClient * client,
\r
995 char * outSyncBuffer, int outSyncCap,
\r
996 const char * nextBatch)
\r
998 // filter={\"event_fields\":[\"to_device\"]}
\r
999 static char url[MAX_URL_LEN];
\r
1000 snprintf(url, MAX_URL_LEN,
\r
1001 "/_matrix/client/v3/sync?timeout=%d%s",
\r
1003 strlen(nextBatch) > 0 ? "&since=" : "");
\r
1005 int index = strlen(url);
\r
1007 for (size_t i = 0; i < strlen(nextBatch); i++) {
\r
1008 char c = nextBatch[i];
\r
1011 url[index++] = '%';
\r
1012 url[index++] = '7';
\r
1013 url[index++] = 'E';
\r
1019 url[index] = '\0';
\r
1022 MatrixHttpGet(client,
\r
1024 outSyncBuffer, outSyncCap,
\r
1028 // https://spec.matrix.org/v1.7/client-server-api/#get_matrixclientv3roomsroomideventeventid
\r
1030 MatrixClientGetRoomEvent(
\r
1031 MatrixClient * client,
\r
1032 const char * roomId,
\r
1033 const char * eventId,
\r
1034 char * outEvent, int outEventCap)
\r
1036 static char url[MAX_URL_LEN];
\r
1037 snprintf(url, MAX_URL_LEN,
\r
1038 "/_matrix/client/v3/rooms/%s/event/%s",
\r
1043 MatrixHttpGet(client,
\r
1045 outEvent, outEventCap,
\r
1050 MatrixClientShareMegolmOutSession(
\r
1051 MatrixClient * client,
\r
1052 const char * userId,
\r
1053 const char * deviceId,
\r
1054 MatrixMegolmOutSession * session)
\r
1056 // generate room key event
\r
1057 static char eventBuffer[KEY_SHARE_EVENT_LEN];
\r
1058 sprintf(eventBuffer,
\r
1060 "\"algorithm\":\"m.megolm.v1.aes-sha2\","
\r
1061 "\"room_id\":\"%s\","
\r
1062 "\"session_id\":\"%s\","
\r
1063 "\"session_key\":\"%s\""
\r
1071 MatrixClientSendToDeviceEncrypted(client,
\r
1081 MatrixClientShareMegolmOutSessionTest(
\r
1082 MatrixClient * client,
\r
1083 const char * userId,
\r
1084 const char * deviceId,
\r
1085 MatrixMegolmOutSession * session)
\r
1087 // generate room key event
\r
1088 char eventBuffer[KEY_SHARE_EVENT_LEN];
\r
1089 sprintf(eventBuffer,
\r
1091 "\"algorithm\":\"m.megolm.v1.aes-sha2\","
\r
1092 "\"room_id\":\"%s\","
\r
1093 "\"session_id\":\"%s\","
\r
1094 "\"session_key\":\"%s\""
\r
1102 MatrixClientSendToDevice(client,
\r
1112 MatrixClientGetMegolmOutSession(
\r
1113 MatrixClient * client,
\r
1114 const char * roomId,
\r
1115 MatrixMegolmOutSession ** outSession)
\r
1117 for (int i = 0; i < client->numMegolmOutSessions; i++)
\r
1119 if (strcmp(client->megolmOutSessions[i].roomId, roomId) == 0)
\r
1121 *outSession = &client->megolmOutSessions[i];
\r
1130 MatrixClientNewMegolmOutSession(
\r
1131 MatrixClient * client,
\r
1132 const char * roomId,
\r
1133 MatrixMegolmOutSession ** outSession)
\r
1135 if (client->numMegolmOutSessions < NUM_MEGOLM_SESSIONS)
\r
1137 MatrixMegolmOutSession * result =
\r
1138 &client->megolmOutSessions[client->numMegolmOutSessions];
\r
1140 MatrixMegolmOutSessionInit(result,
\r
1143 *outSession = result;
\r
1145 client->numMegolmOutSessions++;
\r
1154 MatrixClientGetMegolmInSession(
\r
1155 MatrixClient * client,
\r
1156 const char * roomId, int roomIdLen,
\r
1157 const char * sessionId, int sessionIdLen,
\r
1158 MatrixMegolmInSession ** outSession)
\r
1160 for (int i = 0; i < client->numMegolmInSessions; i++)
\r
1162 if (strncmp(client->megolmInSessions[i].roomId, roomId, roomIdLen) == 0 &&
\r
1163 strncmp(client->megolmInSessions[i].id, sessionId, sessionIdLen) == 0)
\r
1165 *outSession = &client->megolmInSessions[i];
\r
1174 MatrixClientNewMegolmInSession(
\r
1175 MatrixClient * client,
\r
1176 const char * roomId,
\r
1177 const char * sessionId,
\r
1178 const char * sessionKey,
\r
1179 MatrixMegolmInSession ** outSession)
\r
1181 if (client->numMegolmInSessions < NUM_MEGOLM_SESSIONS)
\r
1183 MatrixMegolmInSession * result =
\r
1184 &client->megolmInSessions[client->numMegolmInSessions];
\r
1186 MatrixMegolmInSessionInit(result,
\r
1189 sessionKey, strlen(sessionKey));
\r
1191 *outSession = result;
\r
1193 client->numMegolmInSessions++;
\r
1202 MatrixClientRequestMegolmInSession(
\r
1203 MatrixClient * client,
\r
1204 const char * roomId,
\r
1205 const char * sessionId,
\r
1206 const char * senderKey,
\r
1207 const char * userId,
\r
1208 const char * deviceId)
\r
1210 // TODO: cancel requests
\r
1211 MatrixClientSendDummy(client, userId, deviceId);
\r
1213 static char event[ROOMKEY_REQUEST_SIZE];
\r
1214 snprintf(event, ROOMKEY_REQUEST_SIZE,
\r
1216 "\"action\":\"request\","
\r
1218 "\"algorithm\":\"m.megolm.v1.aes-sha2\","
\r
1219 "\"room_id\":\"%s\","
\r
1220 "\"sender_key\":\"%s\","
\r
1221 "\"session_id\":\"%s\""
\r
1223 "\"request_id\":\"%d\","
\r
1224 "\"requesting_device_id\":\"%s\""
\r
1230 client->deviceId);
\r
1233 MatrixClientSendToDevice(client,
\r
1237 "m.room_key_request");
\r
1243 MatrixClientGetOlmSessionIn(
\r
1244 MatrixClient * client,
\r
1245 const char * userId,
\r
1246 const char * deviceId,
\r
1247 const char * encrypted,
\r
1248 MatrixOlmSession ** outSession)
\r
1250 for (int i = 0; i < client->numOlmSessions; i++)
\r
1252 if (strcmp(client->olmSessions[i].deviceId, deviceId) == 0)
\r
1254 *outSession = &client->olmSessions[i];
\r
1259 if (client->numOlmSessions < NUM_OLM_SESSIONS)
\r
1261 static char deviceKey[DEVICE_KEY_SIZE];
\r
1262 MatrixClientRequestDeviceKey(client,
\r
1264 deviceKey, DEVICE_KEY_SIZE);
\r
1266 MatrixOlmSessionFrom(
\r
1267 &client->olmSessions[client->numOlmSessions],
\r
1268 client->olmAccount.account,
\r
1273 *outSession = &client->olmSessions[client->numOlmSessions];
\r
1275 client->numOlmSessions++;
\r
1284 MatrixClientGetOlmSessionOut(
\r
1285 MatrixClient * client,
\r
1286 const char * userId,
\r
1287 const char * deviceId,
\r
1288 MatrixOlmSession ** outSession)
\r
1290 for (int i = 0; i < client->numOlmSessions; i++)
\r
1292 if (strcmp(client->olmSessions[i].deviceId, deviceId) == 0)
\r
1294 *outSession = &client->olmSessions[i];
\r
1299 if (client->numOlmSessions < NUM_OLM_SESSIONS)
\r
1301 static char deviceKey[DEVICE_KEY_SIZE];
\r
1302 MatrixClientRequestDeviceKey(client,
\r
1304 deviceKey, DEVICE_KEY_SIZE);
\r
1306 char onetimeKey[ONETIME_KEY_SIZE];
\r
1307 MatrixClientClaimOnetimeKey(client,
\r
1310 onetimeKey, ONETIME_KEY_SIZE);
\r
1312 MatrixOlmSessionTo(
\r
1313 &client->olmSessions[client->numOlmSessions],
\r
1314 client->olmAccount.account,
\r
1319 *outSession = &client->olmSessions[client->numOlmSessions];
\r
1321 client->numOlmSessions++;
\r
1329 // https://spec.matrix.org/v1.6/client-server-api/#put_matrixclientv3sendtodeviceeventtypetxnid
\r
1331 MatrixClientSendToDevice(
\r
1332 MatrixClient * client,
\r
1333 const char * userId,
\r
1334 const char * deviceId,
\r
1335 const char * message,
\r
1336 const char * msgType)
\r
1338 static char requestUrl[MAX_URL_LEN];
\r
1339 sprintf(requestUrl,
\r
1340 TODEVICE_URL, msgType, (int)time(NULL));
\r
1342 static char eventBuffer[TODEVICE_EVENT_SIZE];
\r
1343 snprintf(eventBuffer, TODEVICE_EVENT_SIZE,
\r
1355 static char responseBuffer[ROOM_SEND_RESPONSE_SIZE];
\r
1357 MatrixHttpPut(client,
\r
1360 responseBuffer, ROOM_SEND_RESPONSE_SIZE,
\r
1363 printf("%s\n", responseBuffer);
\r
1369 MatrixClientSendToDeviceEncrypted(
\r
1370 MatrixClient * client,
\r
1371 const char * userId,
\r
1372 const char * deviceId,
\r
1373 const char * message,
\r
1374 const char * msgType)
\r
1376 // get olm session
\r
1377 MatrixOlmSession * olmSession;
\r
1378 MatrixClientGetOlmSessionOut(client, userId, deviceId, &olmSession);
\r
1380 // create event json
\r
1381 char targetDeviceKey[DEVICE_KEY_SIZE];
\r
1382 MatrixClientRequestDeviceKey(client, deviceId, targetDeviceKey, DEVICE_KEY_SIZE);
\r
1383 char targetSigningKey[SIGNING_KEY_SIZE];
\r
1384 MatrixClientRequestSigningKey(client, deviceId, targetSigningKey, SIGNING_KEY_SIZE);
\r
1386 char thisSigningKey[DEVICE_KEY_SIZE];
\r
1387 MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, DEVICE_KEY_SIZE);
\r
1389 static char eventBuffer[TODEVICE_EVENT_SIZE];
\r
1390 sprintf(eventBuffer,
\r
1392 "\"type\":\"%s\","
\r
1394 "\"sender\":\"%s\","
\r
1395 "\"recipient\":\"%s\","
\r
1396 "\"recipient_keys\":{"
\r
1397 "\"ed25519\":\"%s\""
\r
1400 "\"ed25519\":\"%s\""
\r
1406 userId, // recipient user id
\r
1407 targetSigningKey, // recipient device key
\r
1411 static char encryptedBuffer[ENCRYPTED_REQUEST_SIZE];
\r
1412 MatrixOlmSessionEncrypt(olmSession,
\r
1414 encryptedBuffer, ENCRYPTED_REQUEST_SIZE);
\r
1416 char thisDeviceKey[DEVICE_KEY_SIZE];
\r
1417 MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);
\r
1420 static char encryptedEventBuffer[ENCRYPTED_EVENT_SIZE];
\r
1421 sprintf(encryptedEventBuffer,
\r
1423 "\"algorithm\":\"m.olm.v1.curve25519-aes-sha2\","
\r
1424 "\"ciphertext\":{"
\r
1426 "\"body\":\"%s\","
\r
1430 "\"device_id\":\"%s\","
\r
1431 "\"sender_key\":\"%s\""
\r
1435 olm_session_has_received_message(olmSession->session),
\r
1440 return MatrixClientSendToDevice(
\r
1444 encryptedEventBuffer,
\r
1445 "m.room.encrypted");
\r
1449 MatrixClientSendDummy(
\r
1450 MatrixClient * client,
\r
1451 const char * userId,
\r
1452 const char * deviceId)
\r
1454 return MatrixClientSendToDeviceEncrypted(
\r
1463 MatrixClientFindDevice(
\r
1464 MatrixClient * client,
\r
1465 const char * deviceId,
\r
1466 MatrixDevice ** outDevice)
\r
1468 MatrixClientRequestDeviceKeys(client);
\r
1470 for (int i = 0; i < client->numDevices; i++)
\r
1472 if (strcmp(client->devices[i].deviceId, deviceId) == 0)
\r
1474 *outDevice = &client->devices[i];
\r
1479 *outDevice = NULL;
\r
1484 MatrixClientRequestDeviceKey(
\r
1485 MatrixClient * client,
\r
1486 const char * deviceId,
\r
1487 char * outDeviceKey, int outDeviceKeyCap)
\r
1489 MatrixDevice * device;
\r
1491 if (MatrixClientFindDevice(client, deviceId, &device))
\r
1493 strncpy(outDeviceKey, device->deviceKey, outDeviceKeyCap);
\r
1497 MatrixClientRequestDeviceKeys(client);
\r
1499 if (MatrixClientFindDevice(client, deviceId, &device))
\r
1501 strncpy(outDeviceKey, device->deviceKey, outDeviceKeyCap);
\r
1509 MatrixClientRequestSigningKey(
\r
1510 MatrixClient * client,
\r
1511 const char * deviceId,
\r
1512 char * outSigningKey, int outSigningKeyCap)
\r
1514 MatrixDevice * device;
\r
1516 if (MatrixClientFindDevice(client, deviceId, &device))
\r
1518 strncpy(outSigningKey, device->signingKey, outSigningKeyCap);
\r
1522 MatrixClientRequestDeviceKeys(client);
\r
1524 if (MatrixClientFindDevice(client, deviceId, &device))
\r
1526 strncpy(outSigningKey, device->signingKey, outSigningKeyCap);
\r
1533 // https://spec.matrix.org/v1.6/client-server-api/#post_matrixclientv3keysquery
\r
1535 MatrixClientRequestDeviceKeys(
\r
1536 MatrixClient * client)
\r
1538 static char userIdEscaped[USER_ID_SIZE];
\r
1539 JsonEscape(client->userId, strlen(client->userId),
\r
1540 userIdEscaped, USER_ID_SIZE);
\r
1542 static char request[KEYS_QUERY_REQUEST_SIZE];
\r
1543 snprintf(request, KEYS_QUERY_REQUEST_SIZE,
\r
1544 "{\"device_keys\":{\"%s\":[]}}", client->userId);
\r
1546 static char responseBuffer[KEYS_QUERY_RESPONSE_SIZE];
\r
1547 bool requestResult = MatrixHttpPost(client,
\r
1550 responseBuffer, KEYS_QUERY_RESPONSE_SIZE,
\r
1553 if (! requestResult)
\r
1556 // query for retrieving device keys for user id
\r
1557 static char query[JSON_QUERY_SIZE];
\r
1558 snprintf(query, JSON_QUERY_SIZE,
\r
1559 "$.device_keys.%s", userIdEscaped);
\r
1563 mjson_find(responseBuffer, strlen(responseBuffer),
\r
1564 query, &s, &slen);
\r
1568 int koff, klen, voff, vlen, vtype, off = 0;
\r
1569 for (off = 0; (off = mjson_next(s, slen, off, &koff, &klen,
\r
1570 &voff, &vlen, &vtype)) != 0; ) {
\r
1571 const char * key = s + koff;
\r
1572 const char * val = s + voff;
\r
1574 // set device id, "key" is the JSON key
\r
1576 snprintf(d.deviceId, DEVICE_ID_SIZE,
\r
1577 "%.*s", klen-2, key+1);
\r
1579 // look for device key in value
\r
1580 static char deviceKeyQuery[JSON_QUERY_SIZE];
\r
1581 snprintf(deviceKeyQuery, JSON_QUERY_SIZE,
\r
1582 "$.keys.curve25519:%s", d.deviceId);
\r
1583 mjson_get_string(val, vlen,
\r
1584 deviceKeyQuery, d.deviceKey, DEVICE_KEY_SIZE);
\r
1586 // look for signing key in value
\r
1587 static char signingKeyQuery[JSON_QUERY_SIZE];
\r
1588 snprintf(signingKeyQuery, JSON_QUERY_SIZE,
\r
1589 "$.keys.ed25519:%s", d.deviceId);
\r
1590 mjson_get_string(val, vlen,
\r
1591 signingKeyQuery, d.signingKey, SIGNING_KEY_SIZE);
\r
1594 if (client->numDevices < NUM_DEVICES)
\r
1596 bool foundDevice = false;
\r
1597 for (int i = 0; i < client->numDevices; i++)
\r
1598 if (strcmp(client->devices[i].deviceId, d.deviceId) == 0)
\r
1599 foundDevice = true;
\r
1601 if (! foundDevice) {
\r
1602 printf("new device: %s %s %s\n", d.deviceId, d.deviceKey, d.signingKey);
\r
1603 client->devices[client->numDevices] = d;
\r
1604 client->numDevices++;
\r
1617 MatrixClientDeleteDevice(
\r
1618 MatrixClient * client)
\r
1620 static char deleteRequest[1024];
\r
1621 snprintf(deleteRequest, 1024,
\r
1622 "{\"devices\":[\"%s\"]}",
\r
1623 client->deviceId);
\r
1624 static char deleteResponse[1024];
\r
1625 bool res = MatrixHttpPost(client, "/_matrix/client/v3/delete_devices",
\r
1626 deleteRequest, deleteResponse, 1024, true);
\r