+ const char * server)\r
+{\r
+ memset(client, 0, sizeof(MatrixClient));\r
+\r
+ strcpy(client->server, server);\r
+\r
+ // init olm account\r
+ MatrixOlmAccountInit(&client->olmAccount);\r
+\r
+ // set device key\r
+ static char deviceKeysJson[OLM_IDENTITY_KEYS_JSON_SIZE];\r
+ size_t res =\r
+ olm_account_identity_keys(\r
+ client->olmAccount.account,\r
+ deviceKeysJson,\r
+ OLM_IDENTITY_KEYS_JSON_SIZE);\r
+\r
+ mjson_get_string(deviceKeysJson, res,\r
+ "$.curve25519",\r
+ client->deviceKey, DEVICE_KEY_SIZE);\r
+ mjson_get_string(deviceKeysJson, res,\r
+ "$.ed25519",\r
+ client->signingKey, SIGNING_KEY_SIZE);\r
+\r
+ return true;\r
+}\r
+\r
+bool\r
+MatrixClientSave(\r
+ MatrixClient * client,\r
+ const char * filename)\r
+{\r
+ FILE * f = fopen(filename, "w");\r
+ \r
+ fwrite(client->deviceKey, 1, DEVICE_KEY_SIZE, f);\r
+ fwrite(client->signingKey, 1, DEVICE_KEY_SIZE, f);\r
+ fwrite(client->userId, 1, USER_ID_SIZE, f);\r
+ fwrite(client->server, 1, SERVER_SIZE, f);\r
+ fwrite(client->accessToken, 1, ACCESS_TOKEN_SIZE, f);\r
+ fwrite(client->deviceId, 1, DEVICE_ID_SIZE, f);\r
+ fwrite(client->expireMs, 1, EXPIRE_MS_SIZE, f);\r
+ fwrite(client->refreshToken, 1, REFRESH_TOKEN_SIZE, f);\r
+\r
+ fwrite(&client->numDevices, sizeof(int), 1, f);\r
+ for (int i = 0; i < client->numDevices; i++) {\r
+ fwrite(client->devices[i].deviceId, 1, DEVICE_ID_SIZE, f);\r
+ fwrite(client->devices[i].deviceKey, 1, DEVICE_KEY_SIZE, f);\r
+ }\r
+\r
+ fclose(f);\r
+ return true;\r
+}\r
+\r
+bool\r
+MatrixClientLoad(\r
+ MatrixClient * client,\r
+ const char * filename)\r
+{\r
+ FILE * f = fopen(filename, "r");\r
+ \r
+ fread(client->deviceKey, 1, DEVICE_KEY_SIZE, f);\r
+ fread(client->signingKey, 1, DEVICE_KEY_SIZE, f);\r
+ fread(client->userId, 1, USER_ID_SIZE, f);\r
+ fread(client->server, 1, SERVER_SIZE, f);\r
+ fread(client->accessToken, 1, ACCESS_TOKEN_SIZE, f);\r
+ fread(client->deviceId, 1, DEVICE_ID_SIZE, f);\r
+ fread(client->expireMs, 1, EXPIRE_MS_SIZE, f);\r
+ fread(client->refreshToken, 1, REFRESH_TOKEN_SIZE, f);\r
+\r
+ fread(&client->numDevices, sizeof(int), 1, f);\r
+ for (int i = 0; i < client->numDevices; i++) {\r
+ fread(client->devices[i].deviceId, 1, DEVICE_ID_SIZE, f);\r
+ fread(client->devices[i].deviceKey, 1, DEVICE_KEY_SIZE, f);\r
+ }\r
+\r
+ fclose(f);\r
+ return true;\r
+}\r
+\r
+bool\r
+MatrixClientSetAccessToken(\r
+ MatrixClient * client,\r
+ const char * accessToken)\r
+{\r
+ int accessTokenLen = strlen(accessToken);\r
+\r
+ if (accessTokenLen > ACCESS_TOKEN_SIZE - 1)\r
+ return false;\r
+\r
+ for (int i = 0; i < accessTokenLen; i++)\r
+ client->accessToken[i] = accessToken[i];\r
+ client->accessToken[accessTokenLen] = '\0';\r
+\r
+ return true;\r
+}\r
+\r
+bool\r
+MatrixClientSetDeviceId(\r
+ MatrixClient * client,\r
+ const char * deviceId)\r
+{\r
+ int deviceIdLen = strlen(deviceId);\r
+\r
+ if (deviceIdLen > DEVICE_ID_SIZE - 1)\r
+ return false;\r
+\r
+ for (int i = 0; i < deviceIdLen; i++)\r
+ client->deviceId[i] = deviceId[i];\r
+ client->deviceId[deviceIdLen] = '\0';\r
+\r
+ return true;\r
+}\r
+\r
+bool\r
+MatrixClientSetUserId(\r
+ MatrixClient * client,\r
+ const char * userId)\r
+{\r
+ int userIdLen = strlen(userId);\r
+\r
+ if (userIdLen > USER_ID_SIZE - 1)\r
+ return false;\r
+\r
+ for (int i = 0; i < userIdLen; i++)\r
+ client->userId[i] = userId[i];\r
+ client->userId[userIdLen] = '\0';\r
+\r
+ return true;\r
+}\r
+\r
+bool\r
+MatrixClientGenerateOnetimeKeys(\r
+ MatrixClient * client,\r
+ int numberOfKeys)\r
+{\r
+ static uint8_t random[OLM_ONETIME_KEYS_RANDOM_SIZE];\r
+ Randomize(random, OLM_ONETIME_KEYS_RANDOM_SIZE);\r
+\r
+ size_t res =\r
+ olm_account_generate_one_time_keys(client->olmAccount.account,\r
+ numberOfKeys, random, OLM_ONETIME_KEYS_RANDOM_SIZE);\r
+\r
+ return res != olm_error();\r
+}\r
+\r
+// https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv3keysupload\r
+bool\r
+MatrixClientUploadOnetimeKeys(\r
+ MatrixClient * client)\r
+{\r
+ static char requestBuffer[KEYS_UPLOAD_REQUEST_SIZE];\r
+\r
+ mjson_snprintf(requestBuffer, KEYS_UPLOAD_REQUEST_SIZE,\r
+ "{\"one_time_keys\":{");\r
+\r
+ static char onetimeKeysBuffer[1024];\r
+ olm_account_one_time_keys(client->olmAccount.account,\r
+ onetimeKeysBuffer, 1024);\r
+\r
+ const char *keys;\r
+ int keysLen;\r
+ mjson_find(onetimeKeysBuffer, strlen(onetimeKeysBuffer), "$.curve25519", &keys, &keysLen);\r
+\r
+ int koff, klen, voff, vlen, vtype, off = 0;\r
+ while ((off = mjson_next(keys, keysLen, off, &koff, &klen, &voff, &vlen, &vtype)) != 0) {\r
+ static char keyJson[JSON_ONETIME_KEY_SIZE];\r
+ \r
+ snprintf(keyJson, JSON_ONETIME_KEY_SIZE,\r
+ "{\"key\":\"%.*s\"}",\r
+ vlen-2, keys + voff+1);\r
+\r
+ static char keyJsonSigned[JSON_ONETIME_KEY_SIGNED_SIZE];\r
+\r
+ JsonSign(client,\r
+ keyJson, JSON_ONETIME_KEY_SIZE,\r
+ keyJsonSigned, JSON_ONETIME_KEY_SIGNED_SIZE);\r
+ \r
+ mjson_snprintf(requestBuffer+strlen(requestBuffer), KEYS_UPLOAD_REQUEST_SIZE-strlen(requestBuffer),\r
+ "\"signed_curve25519:%.*s\":%s,",\r
+ klen-2, keys + koff+1,\r
+ keyJsonSigned);\r
+ }\r
+\r
+ mjson_snprintf(requestBuffer+strlen(requestBuffer)-1, KEYS_UPLOAD_REQUEST_SIZE-strlen(requestBuffer),\r
+ "}}");\r
+\r
+ static char responseBuffer[KEYS_UPLOAD_RESPONSE_SIZE];\r
+ MatrixHttpPost(client,\r
+ KEYS_UPLOAD_URL,\r
+ requestBuffer,\r
+ responseBuffer, KEYS_UPLOAD_RESPONSE_SIZE,\r
+ true);\r
+\r
+ return true;\r
+}\r
+\r
+// https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv3keysupload\r
+bool\r
+MatrixClientUploadDeviceKeys(\r
+ MatrixClient * client)\r
+{\r
+ static char deviceKeysBuffer[KEYS_UPLOAD_REQUEST_SIZE];\r
+\r
+ mjson_snprintf(deviceKeysBuffer, KEYS_UPLOAD_REQUEST_SIZE,\r
+ "{\"device_keys\":{"\r
+ "\"algorithms\":[\"m.olm.v1.curve25519-aes-sha2\",\"m.megolm.v1.aes-sha2\"],"\r
+ "\"device_id\":\"%s\","\r
+ "\"keys\":{"\r
+ "\"curve25519:%s\":\"%s\","\r
+ "\"ed25519:%s\":\"%s\""\r
+ "},"\r
+ "\"user_id\":\"%s\""\r
+ "}}",\r
+ client->deviceId,\r
+ client->deviceId, client->deviceKey,\r
+ client->deviceId, client->signingKey,\r
+ client->userId);\r
+\r
+ static char deviceKeysSignedBuffer[KEYS_UPLOAD_REQUEST_SIGNED_SIZE];\r
+ JsonSign(client,\r
+ deviceKeysBuffer, KEYS_UPLOAD_REQUEST_SIZE,\r
+ deviceKeysSignedBuffer, KEYS_UPLOAD_REQUEST_SIZE);\r
+\r
+\r
+ static char responseBuffer[KEYS_UPLOAD_RESPONSE_SIZE];\r
+ MatrixHttpPost(client,\r
+ KEYS_UPLOAD_URL,\r
+ deviceKeysSignedBuffer,\r
+ responseBuffer, KEYS_UPLOAD_RESPONSE_SIZE,\r
+ true);\r
+\r
+ return true;\r
+}\r
+\r
+// https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv3keysclaim\r
+bool\r
+MatrixClientClaimOnetimeKey(\r
+ MatrixClient * client,\r
+ const char * userId,\r
+ const char * deviceId,\r
+ char * outOnetimeKey, int outOnetimeKeyCap)\r
+{\r
+ static char requestBuffer[KEYS_CLAIM_REQUEST_SIZE];\r
+ mjson_snprintf(requestBuffer, KEYS_CLAIM_REQUEST_SIZE,\r
+ "{"\r
+ "\"one_time_keys\": {"\r
+ "\"%s\": {"\r
+ "\"%s\": \"signed_curve25519\""\r
+ "}"\r
+ "},"\r
+ "\"timeout\": 10000"\r
+ "}",\r
+ userId,\r
+ deviceId);\r