From 4b427a4c82a82d98940d857b8625028e82286520 Mon Sep 17 00:00:00 2001 From: LDA Date: Mon, 2 Dec 2024 11:50:38 +0100 Subject: [PATCH] [ADD/WIP] Dumb key management This is just enough to fool FluffyChat and nheko, and still not enough to actually do proper E2EE. Also, don't use Cinny with it, it seems to repeteadly upload keys. Terrible. --- Schema/KeyUpload.json | 62 ++++++++++++++ Schema/PduV1.json | 4 + Schema/SyncResponse.json | 4 + src/Room.c | 16 ++-- src/Room/Populate.c | 16 ++-- src/Room/V1/Populate.c | 2 + src/Room/V1/Send.c | 5 ++ src/Routes/RouteActRoom.c | 2 +- src/Routes/RouteKeyManagement.c | 138 +++++++++++++++++++++++++++++++- src/Routes/RouteSendEvent.c | 4 +- src/Routes/RouteSync.c | 29 +++++-- src/Routes/RouteUserProfile.c | 2 +- src/User.c | 123 ++++++++++++++++++++++++++++ src/include/Room.h | 2 +- src/include/User.h | 24 ++++++ 15 files changed, 405 insertions(+), 28 deletions(-) create mode 100644 Schema/KeyUpload.json diff --git a/Schema/KeyUpload.json b/Schema/KeyUpload.json new file mode 100644 index 0000000..a0bafbd --- /dev/null +++ b/Schema/KeyUpload.json @@ -0,0 +1,62 @@ +{ + "guard": "TELODENDRIA_KEY_UPLOAD_H", + "header": "Schema/KeyUpload.h", + "types": { + "DeviceKeys": { + "type": "struct", + "fields": { + "device_id": { + "type": "string", + "required": true + }, + "user_id": { + "type": "string", + "required": true + }, + "algorithms": { + "type": "[string]", + "required": true + }, + "keys": { + "type": "{string}", + "required": true + }, + "signatures": { + "//": "TODO: More complex j2s types.", + "//": "This is meant to be a map from user ID to ", + "//": "algo+device ID to a signature(string).", + "type": "object", + "required": true + } + } + }, + "KeyResponse": { + "type": "struct", + "fields": { + "one_time_key_counts": { + "type": "{integer}", + "required": true + } + } + }, + "KeyUploadRequest": { + "type": "struct", + "fields": { + "device_keys": { + "type": "DeviceKeys", + "required": false + }, + "fallback_keys": { + "//": "This is a one-time key.", + "type": "object", + "required": false + }, + "one_time_keys": { + "//": "This is a one-time key.", + "type": "object", + "required": false + } + } + } + } +} diff --git a/Schema/PduV1.json b/Schema/PduV1.json index d63f2df..0a45184 100644 --- a/Schema/PduV1.json +++ b/Schema/PduV1.json @@ -38,6 +38,10 @@ "redacted_because": { "type": "object", "required": false + }, + "transaction_id": { + "type": "string", + "required": false } } }, diff --git a/Schema/SyncResponse.json b/Schema/SyncResponse.json index c074f00..f9266fa 100644 --- a/Schema/SyncResponse.json +++ b/Schema/SyncResponse.json @@ -68,6 +68,7 @@ "sender": { "type": "string", "required": true }, "state_key": { "type": "string" }, "redacts": { "type": "string" }, + "_unsigned": { "type": "object" }, "type": { "type": "string", "required": true } }, "type": "struct" @@ -105,6 +106,9 @@ }, "rooms": { "type": "Rooms" + }, + "device_one_time_keys_count": { + "type": "{integer}" } }, "type": "struct" diff --git a/src/Room.c b/src/Room.c index e8670ca..fe62944 100644 --- a/src/Room.c +++ b/src/Room.c @@ -348,6 +348,11 @@ RoomEventFetch(Room *room, char *id, bool prev) "pdu_status", JsonValueDuplicate(HashMapGet(DbJson(event_ref), "status")) )); + JsonValueFree(HashMapSet( + unsign, + "transaction_id", + JsonValueDuplicate(HashMapGet(DbJson(event_ref), "transaction")) + )); ts = JsonValueAsInteger(HashMapGet(ret, "origin_server_ts")); JsonValueFree(HashMapSet( unsign, @@ -391,7 +396,7 @@ finish: } HashMap * -RoomEventCreate(char *sender, char *type, char *key, HashMap *c) +RoomEventCreate(char *sender, char *type, char *key, HashMap *c, char *txn) { HashMap *event; if (!sender || !type || !c) @@ -403,6 +408,7 @@ RoomEventCreate(char *sender, char *type, char *key, HashMap *c) JsonSet(event, JsonValueObject(c), 1, "content"); JsonSet(event, JsonValueString(sender), 1, "sender"); JsonSet(event, JsonValueString(type), 1, "type"); + JsonSet(event, JsonValueString(txn), 1, "transaction"); if (key) { JsonSet(event, JsonValueString(key), 1, "state_key"); @@ -557,7 +563,7 @@ RoomSendInvite(User *sender, bool direct, char *user, Room *room) content = HashMapCreate(); JsonSet(content, JsonValueBoolean(direct), 1, "is_direct"); JsonSet(content, JsonValueString("invite"), 1, "membership"); - event = RoomEventCreate(senderStr, "m.room.member", user, content); + event = RoomEventCreate(senderStr, "m.room.member", user, content, NULL); JsonFree(RoomEventSend(room, event, NULL)); JsonFree(event); @@ -692,7 +698,7 @@ RoomLeave(Room *room, User *user, char **errp) content = HashMapCreate(); JsonSet(content, JsonValueString("leave"), 1, "membership"); - event = RoomEventCreate(userString, "m.room.member", userString, content); + event = RoomEventCreate(userString, "m.room.member", userString, content, NULL); pdu = RoomEventSend(room, event, errp); /* TODO: One ought to be extremely careful with managing users in those @@ -756,7 +762,7 @@ RoomRedact(Room *room, User *user, char *eventID, char *reason, char **errp) HashMapSet(content, "reason", JsonValueString(reason)); event = RoomEventCreate(userString, "m.room.redaction", NULL, - content + content, NULL ); HashMapSet(event, "redacts", JsonValueString(eventID)); pdu = RoomEventSend(room, event, errp); @@ -811,7 +817,7 @@ RoomJoin(Room *room, User *user, char **errp) content = HashMapCreate(); JsonSet(content, JsonValueString("join"), 1, "membership"); - event = RoomEventCreate(userString, "m.room.member", userString, content); + event = RoomEventCreate(userString, "m.room.member", userString, content, NULL); pdu = RoomEventSend(room, event, errp); /* TODO: One ought to be extremely careful with managing users in those diff --git a/src/Room/Populate.c b/src/Room/Populate.c index 4d646fb..527aa32 100644 --- a/src/Room/Populate.c +++ b/src/Room/Populate.c @@ -97,7 +97,7 @@ RoomPopulate(Room *room, User *user, RoomCreateRequest *req, ServerPart s) } JsonValueFree(HashMapSet(content, key, JsonValueDuplicate(val))); } - event = RoomEventCreate(sender_str, "m.room.create", "", content); + event = RoomEventCreate(sender_str, "m.room.create", "", content, NULL); JsonFree(RoomEventSend(room, event, NULL)); JsonFree(event); UserAddJoin(user, room->id); @@ -105,7 +105,7 @@ RoomPopulate(Room *room, User *user, RoomCreateRequest *req, ServerPart s) /* m.room.member */ content = HashMapCreate(); JsonSet(content, JsonValueString("join"), 1, "membership"); - event = RoomEventCreate(sender_str, "m.room.member", sender_str, content); + event = RoomEventCreate(sender_str, "m.room.member", sender_str, content, NULL); JsonFree(RoomEventSend(room, event, NULL)); JsonFree(event); @@ -126,7 +126,7 @@ RoomPopulate(Room *room, User *user, RoomCreateRequest *req, ServerPart s) } HashMapSet(content, key, JsonValueDuplicate(val)); } - event = RoomEventCreate(sender_str, "m.room.power_levels", "", content); + event = RoomEventCreate(sender_str, "m.room.power_levels", "", content, NULL); JsonFree(RoomEventSend(room, event, NULL)); JsonFree(event); @@ -174,7 +174,7 @@ RoomPopulate(Room *room, User *user, RoomCreateRequest *req, ServerPart s) , 1, a); \ event = RoomEventCreate( \ sender_str, \ - "m.room." #p, "", content); \ + "m.room." #p, "", content, NULL); \ JsonFree(RoomEventSend(room, event, NULL)); \ JsonFree(event); \ } \ @@ -208,7 +208,7 @@ RoomPopulate(Room *room, User *user, RoomCreateRequest *req, ServerPart s) { content = HashMapCreate(); JsonSet(content, JsonValueString(req->name), 1, "name"); - event = RoomEventCreate(sender_str, "m.room.name", "", content); + event = RoomEventCreate(sender_str, "m.room.name", "", content, NULL); JsonFree(RoomEventSend(room, event, NULL)); JsonFree(event); } @@ -216,7 +216,7 @@ RoomPopulate(Room *room, User *user, RoomCreateRequest *req, ServerPart s) { content = HashMapCreate(); JsonSet(content, JsonValueString(req->topic), 1, "topic"); - event = RoomEventCreate(sender_str, "m.room.topic", "", content); + event = RoomEventCreate(sender_str, "m.room.topic", "", content, NULL); JsonFree(RoomEventSend(room, event, NULL)); JsonFree(event); } @@ -240,7 +240,7 @@ RoomPopulate(Room *room, User *user, RoomCreateRequest *req, ServerPart s) JsonSet(content, JsonValueString(fullStr), 1, "alias"); event = RoomEventCreate( sender_str, - "m.room.canonical_alias", "", content); + "m.room.canonical_alias", "", content, NULL); JsonFree(RoomEventSend(room, event, NULL)); JsonFree(event); @@ -270,7 +270,7 @@ RoomPopulate(Room *room, User *user, RoomCreateRequest *req, ServerPart s) } - event = RoomEventCreate(sender_str, "m.room.power_levels", "", pl_content); + event = RoomEventCreate(sender_str, "m.room.power_levels", "", pl_content, NULL); JsonFree(RoomEventSend(room, event, NULL)); JsonFree(event); diff --git a/src/Room/V1/Populate.c b/src/Room/V1/Populate.c index 54ea282..87d0afb 100644 --- a/src/Room/V1/Populate.c +++ b/src/Room/V1/Populate.c @@ -29,6 +29,8 @@ PopulateEventV1(Room * room, HashMap * event, PduV1 * pdu, ServerPart serv) StrDuplicate(JsonValueAsString(JsonGet(event, 1, "type"))); pdu->redacts = StrDuplicate(JsonValueAsString(JsonGet(event, 1, "redacts"))); + pdu->_unsigned.transaction_id = + StrDuplicate(JsonValueAsString(JsonGet(event, 1, "transaction"))); if (JsonGet(event, 1, "state_key")) { pdu->state_key = diff --git a/src/Room/V1/Send.c b/src/Room/V1/Send.c index 2bf6f4b..b5b4dca 100644 --- a/src/Room/V1/Send.c +++ b/src/Room/V1/Send.c @@ -242,6 +242,11 @@ RoomAddEventV1(Room *room, PduV1 pdu, PduV1Status status) JsonValueArray(ArrayCreate()), 1, "next_events" ); + JsonSet( + DbJson(event_ref), + JsonValueString(pdu._unsigned.transaction_id), + 1, "transaction" + ); DbUnlock(room->db, event_ref); Free(safe_id); diff --git a/src/Routes/RouteActRoom.c b/src/Routes/RouteActRoom.c index 173dcd1..4d292a9 100644 --- a/src/Routes/RouteActRoom.c +++ b/src/Routes/RouteActRoom.c @@ -298,7 +298,7 @@ ROUTE_IMPL(RouteKickRoom, path, argp) membership = RoomEventCreate( sender, "m.room.member", kicked, - content + content, NULL ); HashMapSet(content, "membership", JsonValueString(membershipState)); diff --git a/src/Routes/RouteKeyManagement.c b/src/Routes/RouteKeyManagement.c index c095204..fdc0302 100644 --- a/src/Routes/RouteKeyManagement.c +++ b/src/Routes/RouteKeyManagement.c @@ -34,8 +34,105 @@ #include #include -#include +#include +HashMap * +UploadKey(RouteArgs *args, User *user, KeyUploadRequest *req, char *sender) +{ + char *deviceId = UserGetDeviceId(user); + KeyResponse response = { 0 }; + HashMap *json; + char *fbKey; + JsonValue *fbValue; + size_t i; + if (!user || !req || !sender) + { + return NULL; + } + Log(LOG_ERR, "did=%s", deviceId); + if (req->device_keys.user_id) + { + HashMap *publicKeys; + char *pkTag, *pk; + /* We have device key information */ + if (!StrEquals(req->device_keys.user_id, sender)) + { + HttpResponseStatus(args->context, HTTP_BAD_REQUEST); + return MatrixErrorCreate( + M_UNAUTHORIZED, "Device key update has an invalid user ID" + ); + } + if (!StrEquals(req->device_keys.device_id, deviceId)) + { + HttpResponseStatus(args->context, HTTP_BAD_REQUEST); + return MatrixErrorCreate( + M_UNAUTHORIZED, "Device key update has an invalid device ID" + ); + } + + /* Check the public key list */ + publicKeys = req->device_keys.keys; + i = 0; + while (HashMapIterateReentrant(publicKeys, &pkTag, (void **) &pk, &i)) + { + char *pktDID = strchr(pkTag, ':'); + + /* Maybe C does need NULL saturation */ + pktDID = pktDID ? pktDID + 1 : NULL; + if (!StrEquals(pktDID, deviceId)) + { + /* As far as I know, we're not meant to handle other devices' + * public keys */ + HttpResponseStatus(args->context, HTTP_BAD_REQUEST); + Log(LOG_ERR, "%s!=%s 1", pktDID, deviceId); + return MatrixErrorCreate( + M_UNAUTHORIZED, "Device key update has an invalid device ID" + ); + } + } + + UserSetDeviceKeys(user, &req->device_keys); + } + + UserClearFallbackKeys(user); + i = 0; + while (HashMapIterateReentrant(req->fallback_keys, &fbKey, (void **) &fbValue, &i)) + { + char *fbKID = strchr(fbKey, ':'); + size_t len = fbKID ? fbKID - fbKey : 0; + char algo[len + 1]; + + memcpy(algo, fbKey, len); + algo[len] = '\0'; + + /* Maybe C does need NULL saturation */ + fbKID = fbKID ? fbKID + 1 : NULL; + + UserAddKey(user, fbKey, fbValue, true); + (void) fbKID; + } + i = 0; + while (HashMapIterateReentrant(req->one_time_keys, &fbKey, (void **) &fbValue, &i)) + { + char *fbKID = strchr(fbKey, ':'); + size_t len = fbKID ? fbKID - fbKey : 0; + char algo[len + 1]; + + memcpy(algo, fbKey, len); + algo[len] = '\0'; + + /* Maybe C does need NULL saturation */ + fbKID = fbKID ? fbKID + 1 : NULL; + + UserAddKey(user, fbKey, fbValue, false); + (void) fbKID; + } + response.one_time_key_counts = UserGetOnetimeCounts(user); + UserNotifyUser(UserGetName(user)); + json = KeyResponseToJson(&response); + KeyResponseFree(&response); + return json; +} ROUTE_IMPL(RouteKeyQuery, path, argp) { @@ -45,9 +142,14 @@ ROUTE_IMPL(RouteKeyQuery, path, argp) HashMap *request = NULL; HashMap *response = NULL; - User *user = NULL; + CommonID *id = NULL; char *token = NULL; + User *user = NULL; + char *serverName = NULL; + char *sender = NULL; + + char *method = ArrayGet(path, 0); char *err; if (HttpRequestMethodGet(args->context) != HTTP_POST) @@ -71,6 +173,11 @@ ROUTE_IMPL(RouteKeyQuery, path, argp) response = MatrixErrorCreate(M_UNKNOWN_TOKEN, NULL); goto finish; } + serverName = ConfigGetServerName(db); + id = UserIdParse(UserGetName(user), serverName); + id->sigil = '@'; + sender = ParserRecomposeCommonID(*id); + request = JsonDecode(HttpServerStream(args->context)); if (!request) { @@ -79,10 +186,37 @@ ROUTE_IMPL(RouteKeyQuery, path, argp) goto finish; } + if (StrEquals(method, "upload")) + { + KeyUploadRequest upload = { 0 }; + + if (!KeyUploadRequestFromJson(request, &upload, &err)) + { + HttpResponseStatus(args->context, HTTP_BAD_REQUEST); + response = MatrixErrorCreate(M_BAD_JSON, err); + goto finish; + } + + if ((response = UploadKey(args, user, &upload, sender))) + { + KeyUploadRequestFree(&upload); + goto finish; + } + KeyUploadRequestFree(&upload); + } + else if (StrEquals(method, "query")) + { + /* TODO: Fetch a user's key information */ + } + response = HashMapCreate(); (void) path; finish: JsonFree(request); UserUnlock(user); + + Free(serverName); + UserIdFree(id); + Free(sender); return response; } diff --git a/src/Routes/RouteSendEvent.c b/src/Routes/RouteSendEvent.c index e515d21..efadb08 100644 --- a/src/Routes/RouteSendEvent.c +++ b/src/Routes/RouteSendEvent.c @@ -122,7 +122,7 @@ ROUTE_IMPL(RouteSendEvent, path, argp) goto finish; } - event = RoomEventCreate(sender, eventType, NULL, JsonDuplicate(request)); + event = RoomEventCreate(sender, eventType, NULL, JsonDuplicate(request), transId); filled = RoomEventSend(room, event, &err); JsonFree(event); @@ -266,7 +266,7 @@ ROUTE_IMPL(RouteSendState, path, argp) event = RoomEventCreate( sender, eventType, stateKey ? stateKey : "", - JsonDuplicate(request) + JsonDuplicate(request), NULL ); filled = RoomEventSend(room, event, &err); JsonFree(event); diff --git a/src/Routes/RouteSync.c b/src/Routes/RouteSync.c index 3343078..1ca54df 100644 --- a/src/Routes/RouteSync.c +++ b/src/Routes/RouteSync.c @@ -139,12 +139,17 @@ ROUTE_IMPL(RouteSync, path, argp) /* TODO: I only am manually parsing this because j2s does not support * a hashmap of unknown keys pointing to a known type. */ - sync.rooms.invite = HashMapCreate(); - sync.rooms.join = HashMapCreate(); - sync.account_data.events = ArrayCreate(); + sync.rooms.invite = NULL; + sync.rooms.join = NULL; + sync.account_data.events = NULL; + sync.device_one_time_keys_count = UserGetOnetimeCounts(user); /* account data */ accountData = UserGetAccountDataSync(user, currBatch); + if (ArraySize(accountData) > 0) + { + sync.account_data.events = ArrayCreate(); + } for (i = 0; i < ArraySize(accountData); i++) { char *key = ArrayGet(accountData, i); @@ -158,6 +163,10 @@ ROUTE_IMPL(RouteSync, path, argp) /* invites */ invites = UserGetInvites(user, currBatch); + if (ArraySize(invites) > 0) + { + sync.rooms.invite = HashMapCreate(); + } for (i = 0; i < ArraySize(invites); i++) { char *roomId = ArrayGet(invites, i); @@ -171,7 +180,7 @@ ROUTE_IMPL(RouteSync, path, argp) invited = Malloc(sizeof(*invited)); memset(invited, 0, sizeof(*invited)); - /* TODO: Populate the invitestate */ + // TODO: Populate the invitestate invited->invite_state.events = ArrayCreate(); HashMapSet(sync.rooms.invite, roomId, invited); } @@ -179,9 +188,12 @@ ROUTE_IMPL(RouteSync, path, argp) /* Joins */ joins = UserGetJoins(user, currBatch); + if (ArraySize(joins) > 0) + { + sync.rooms.join = HashMapCreate(); + } for (i = 0; i < ArraySize(joins); i++) { - /* TODO: Rename these variables */ char *roomId = ArrayGet(joins, i); JoinedRooms *joined; char *firstEvent = NULL; @@ -230,8 +242,8 @@ ROUTE_IMPL(RouteSync, path, argp) joined->timeline.prev_batch = UserNewMessageToken( user, roomId, firstEvent ); - /* TODO: Don't shove the entire state. - * That's a recipe for disaster, especially on large rooms. */ + // TODO: Don't shove the entire state. + // That's a recipe for disaster, especially on large rooms. joined->state.events = ArrayCreate(); while (StateIterate(state, &type, &key, (void **) &id)) { @@ -253,12 +265,13 @@ ROUTE_IMPL(RouteSync, path, argp) if (prevBatch) { /* TODO: Should we be dropping syncs? */ - UserDropSync(user, prevBatch); + //UserDropSync(user, prevBatch); nextBatch = UserInitSyncDiff(user); } sync.next_batch = nextBatch; response = SyncResponseToJson(&sync); SyncResponseFree(&sync); + (void) i; finish: FilterDestroy(filterData); UserUnlock(user); diff --git a/src/Routes/RouteUserProfile.c b/src/Routes/RouteUserProfile.c index 9e144da..5c1a030 100644 --- a/src/Routes/RouteUserProfile.c +++ b/src/Routes/RouteUserProfile.c @@ -56,7 +56,7 @@ SendMembership(Db *db, User *user) HashMap *content = HashMapCreate(); HashMap *membership = RoomEventCreate( sender, "m.room.member", sender, - content + content, NULL ); HashMapSet(content, "membership", JsonValueString("join")); diff --git a/src/User.c b/src/User.c index ffef0c9..6248160 100644 --- a/src/User.c +++ b/src/User.c @@ -1533,6 +1533,7 @@ UserGetAccountDataSync(User *user, char *batch) syncRef = DbLock(db, 4, "users", user->name, "sync", batch); if (!syncRef) { + Log(LOG_ERR, "Tried to get batch=%s (user=%s), but it's gone?", batch, user->deviceId); return NULL; } @@ -2153,3 +2154,125 @@ UserSetAccountData(User *user, char *key, HashMap *obj) UserPushAccountData(user, key); } + +void +UserSetDeviceKeys(User *user, DeviceKeys *keys) +{ + char *device; + HashMap *deviceObj; + if (!user || !keys) + { + return; + } + + device = UserGetDeviceId(user); + deviceObj = JsonValueAsObject(HashMapGet( + UserGetDevices(user), device + )); + if (!deviceObj) + { + return; + } + + JsonValueFree(HashMapSet(deviceObj, "deviceKeys", JsonValueObject(DeviceKeysToJson(keys)))); +} + + +void +UserClearFallbackKeys(User *user) +{ + char *device; + HashMap *deviceObj; + if (!user) + { + return; + } + + device = UserGetDeviceId(user); + deviceObj = JsonValueAsObject(HashMapGet( + UserGetDevices(user), device + )); + if (!deviceObj) + { + return; + } + + if (!HashMapGet(deviceObj, "oneTimeKeys")) + { + JsonValueFree(HashMapSet(deviceObj, + "oneTimeKeys", JsonValueObject(HashMapCreate()) + )); + } + JsonValueFree(HashMapSet(deviceObj, + "fallbackKeys", JsonValueObject(HashMapCreate()) + )); +} +void +UserAddKey(User *user, char *algo, JsonValue *key, bool fb) +{ + char *device; + HashMap *deviceObj, *method; + if (!user || !algo || !key) + { + return; + } + + device = UserGetDeviceId(user); + deviceObj = JsonValueAsObject(HashMapGet( + UserGetDevices(user), device + )); + if (!deviceObj) + { + return; + } + + method = JsonValueAsObject(HashMapGet( + deviceObj, fb ? "fallbackKeys" : "oneTimeKeys" + )); + JsonValueFree(HashMapSet(method, algo, JsonValueDuplicate(key))); +} +HashMap * +UserGetOnetimeCounts(User *user) +{ + char *device, *algoKey; + HashMap *deviceObj, *otk, *ret; + void *ignore; + if (!user) + { + return NULL; + } + + device = UserGetDeviceId(user); + deviceObj = JsonValueAsObject(HashMapGet( + UserGetDevices(user), device + )); + if (!deviceObj) + { + return NULL; + } + + otk = JsonValueAsObject(HashMapGet( + deviceObj, "oneTimeKeys" + )); + ret = HashMapCreate(); + while (HashMapIterate(otk, &algoKey, &ignore)) + { + char *algo = StrDuplicate(algoKey); + char *end = strchr(algo, ':'); + int64_t *ptr; + if (end) + { + *end = '\0'; + } + + if (!(ptr = HashMapGet(ret, algo))) + { + ptr = Malloc(sizeof(*ptr)); + *ptr = 0; + HashMapSet(ret, algo, ptr); + } + (*ptr)++; + Free(algo); + } + return ret; +} diff --git a/src/include/Room.h b/src/include/Room.h index 7d7e53b..d7151a1 100644 --- a/src/include/Room.h +++ b/src/include/Room.h @@ -195,7 +195,7 @@ extern bool RoomAddEventV1(Room *, PduV1, PduV1Status); * Creates a barebones JSON object to be sent to * .Fn RoomEventFetch . */ -extern HashMap * RoomEventCreate(char *, char *, char *, HashMap *); +extern HashMap * RoomEventCreate(char *, char *, char *, HashMap *, char *); /** * Computes an approximation of the PDU depth by looking at diff --git a/src/include/User.h b/src/include/User.h index 27007f7..07430fa 100644 --- a/src/include/User.h +++ b/src/include/User.h @@ -44,6 +44,8 @@ #include +#include + #include /** @@ -522,4 +524,26 @@ extern HashMap * UserGetAccountData(User *, char *); * Replaces an account data entry. */ extern void UserSetAccountData(User *, char *, HashMap *); + +/** + * Sets the device key list. + */ +extern void UserSetDeviceKeys(User *, DeviceKeys *); + +/** + * Clears the fallback/one-time key list. + */ +extern void UserClearFallbackKeys(User *); + +/** + * Adds a one-time/fallback key. + */ +extern void UserAddKey(User *, char *, JsonValue *, bool); + +/** + * Generates a hashmap from algorithm to one-time key count as + * a pointer to the uint64_t. + * This is intended for /keys/upload. Please do not use this + * elsewhere */ +extern HashMap * UserGetOnetimeCounts(User *); #endif /* TELODENDRIA_USER_H */