From 001b4821fe2706bc6589e4c15670df7c1627b71a Mon Sep 17 00:00:00 2001 From: lda Date: Thu, 2 May 2024 20:02:30 +0200 Subject: [PATCH] [ADD/WIP/UNTESTED] Start creating PDU for client events --- .ycm_extra_conf.py | 14 +++ src/CanonicalJson.c | 53 ++++++++ src/Room.c | 235 ++++++++++++++++++++++++++++++++++++ src/include/CanonicalJson.h | 10 ++ 4 files changed, 312 insertions(+) create mode 100644 .ycm_extra_conf.py diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py new file mode 100644 index 0000000..8a704da --- /dev/null +++ b/.ycm_extra_conf.py @@ -0,0 +1,14 @@ +import os +import ycm_core + +def Settings( **kwargs ): + return { + 'flags': [ + "-xc", + "-I", "src/include", + "-I", "Cytoplasm/include", + "-I", "build", + + "-std=c99" + ] + } diff --git a/src/CanonicalJson.c b/src/CanonicalJson.c index a865df3..000d952 100644 --- a/src/CanonicalJson.c +++ b/src/CanonicalJson.c @@ -22,11 +22,16 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#include "Cytoplasm/Io.h" +#include "Cytoplasm/Stream.h" #include #include +#include #include #include +#include +#include #include #include @@ -159,3 +164,51 @@ CanonicalJsonEncode(HashMap * object, Stream * out) ArrayFree(keys); return length; } + +static ssize_t +StringIoRead(void *cookie, void *buf, size_t count) +{ + return -1; /* TODO: Consider reading properly */ +} +static ssize_t +StringIoWrite(void *c, void *buf, size_t count) +{ + char **str = c; + char *stringised = Malloc(count + 1); + char *new; + memcpy(stringised, buf, count); + new = StrConcat(2, *str, stringised); + + Free(stringised); + if (*str) + { + Free(*str); + } + *str = new; + return count; +} +static const IoFunctions Functions = { + .read = StringIoRead, + .write = StringIoWrite, + .close = NULL, + .seek = NULL +}; +char * +CanonicalJsonHash(HashMap *json) +{ + char *string = NULL; + unsigned char *sha; + char *shastr; + Io *string_writer = IoCreate(&string, Functions); + Stream *io_stream = StreamIo(string_writer); + CanonicalJsonEncode(json, io_stream); + + sha = Sha256(string); + shastr = ShaToHex(sha); + + Free(string); + Free(sha); + StreamClose(io_stream); + + return shastr; +} diff --git a/src/Room.c b/src/Room.c index 1d327f2..6369d4a 100644 --- a/src/Room.c +++ b/src/Room.c @@ -23,13 +23,23 @@ * SOFTWARE. */ +#include "Cytoplasm/Json.h" +#include "Cytoplasm/Stream.h" +#include "Parser.h" #include #include +#include #include #include #include +#include +#include + +#include +#include +#include #include @@ -155,3 +165,228 @@ RoomStateGet(Room * room) return NULL; } + +static bool +PopulateEventV1(Room * room, HashMap * event, PduV1 * pdu) +{ + char *unused; + if (PduV1FromJson(event, pdu, &unused)) + { + return true; + } + + /* TODO: Create a PDU of our own, signed and everything. + * https://telodendria.io/blog/matrix-protocol-overview + * has some ideas on how this could be done(up until stage 5). */ + pdu->sender = + StrDuplicate(JsonValueAsString(JsonGet(event, 1, "sender"))); + pdu->auth_events = ArrayCreate(); + pdu->origin_server_ts = UtilTsMillis(); + pdu->content = JsonDuplicate(event); /* Copy the original JSON data */ + + /* TODO: Signature and alldat. */ + return false; +} +static bool +PopulateEventV3(Room * room, HashMap * event, PduV3 *pdu) +{ + char *unused; + if (PduV3FromJson(event, pdu, &unused)) + { + return true; + } + + /* TODO: Create a PDU of our own, signed and everything. + * https://telodendria.io/blog/matrix-protocol-overview + * has some ideas on how this could be done(up until stage 5). */ + return false; +} +static AuthoriseCreateV1(PduV1 pdu) +{ + bool ret = true; + CommonID sender = { 0 }, room_id = { 0 }; + char *sender_serv = NULL, *id_serv = NULL; + + if (ArraySize(pdu.auth_events) > 0) + { + ret = false; + goto finish; + } + if (!ParseCommonID(pdu.room_id, &room_id) || + !ParseCommonID(pdu.sender, &sender)) + { + ret = false; + goto finish; + } + sender_serv = ParserRecomposeServerPart(sender.server); + id_serv = ParserRecomposeServerPart(room_id.server); + if (!StrEquals(sender_serv, id_serv)) + { + ret = false; + goto finish; + } + /* TODO: Check room_version as in step 1.3 */ + if (!HashMapGet(pdu.content, "creator")) + { + ret = false; + goto finish; + } +finish: + if (sender_serv) + { + Free(sender_serv); + } + if (id_serv) + { + Free(id_serv); + } + CommonIDFree(sender); + CommonIDFree(room_id); + return ret; +} +static bool +AuthoriseEventV1(PduV1 pdu) +{ + size_t i; + if (StrEquals(pdu.type, "m.room.create")) + { + return AuthoriseCreateV1(pdu); + } + + /* TODO: Consider, everything else! */ + for (i = 0; i < ArraySize(pdu.auth_events); i++) + { + char *event_id = ArrayGet(pdu.auth_events, i); + /* TODO: Fetch event there */ + } + + /* TODO */ + return true; +} +static char * +RoomHashEvent(HashMap * pdu_json) +{ + HashMap * copy = JsonDuplicate(pdu_json); + char *hash; + JsonValueFree(HashMapDelete(copy, "unsigned")); + JsonValueFree(HashMapDelete(copy, "signatures")); + JsonValueFree(HashMapDelete(copy, "hashes")); + + hash = CanonicalJsonHash(copy); + JsonFree(copy); + return hash; +} +static char * +RoomHashEventV1(PduV1 pdu) +{ + HashMap *json = PduV1ToJson(&pdu); + char *sha = RoomHashEvent(json); + + JsonFree(json); + return sha; +} +static HashMap * +RoomEventSendV1(Room * room, HashMap * event) +{ + PduV1 pdu = { 0 }; + HashMap *pdu_object = NULL; + bool client_event; + HashMap *state = NULL; + + client_event = PopulateEventV1(room, event, &pdu); + pdu_object = PduV1ToJson(&pdu); + + state = StateResolve(room, pdu_object); /* Compute the state + * at that point for later. */ + if (client_event) + { + char *event_id; +#define AddState(type, key) do \ + { \ + event_id = StateGet(state, type, key); \ + if (event_id) \ + { \ + char *dup = StrDuplicate(event_id); \ + ArrayAdd(pdu.auth_events, dup); \ + } \ + } \ + while (0) + + /* + * Implemented from + * https://spec.matrix.org/v1.7/server-server-api/ + * #auth-events-selection */ + AddState("m.room.create", ""); + AddState("m.room.power_levels", ""); + AddState("m.room.member", pdu.sender); + if (StrEquals(pdu.type, "m.room.member")) + { + char *target = pdu.state_key; + char *membership = + JsonValueAsString(JsonGet(pdu.content, 1, "membership")); + char *auth_via = + JsonValueAsString( + JsonGet( + pdu.content, 1, "join_authorised_via_users_server") + ); + HashMap *inv = + JsonValueAsObject( + JsonGet(pdu.content, 1, "third_party_invite")); + + if (target && !StrEquals(target, pdu.sender)) + { + AddState("m.room.member", target); + } + if (StrEquals(membership, "join") || + StrEquals(membership, "invite")) + { + AddState("m.room.join_rules", ""); + } + if (StrEquals(membership, "invite") && inv) + { + char *token = + JsonValueAsString(JsonGet(inv, 2, "signed", "token")); + AddState("m.room.third_party_invite", token); + } + if (auth_via && room->version >= 8) + { + AddState("m.room.member", auth_via); + } + } + pdu.hashes.sha256 = RoomHashEventV1(pdu); +#undef AddState + } + /* TODO: Do a size check! */ +finish: + if (state) + { + JsonFree(state); + } + if (pdu_object) + { + JsonFree(pdu_object); + } + PduV1Free(&pdu); + return NULL; +} +static HashMap * +RoomEventSendV3(Room * room, HashMap * event) +{ + /* TODO */ + return NULL; +} +HashMap * +RoomEventSend(Room * room, HashMap * event) +{ + if (!room || !event) + { + return NULL; + } + if (room->version == 1) + { + /* Manage with PDUv1 */ + return RoomEventSendV1(room, event); + } + /* Manage with PDUv3 otherwise */ + return RoomEventSendV3(room, event); +} diff --git a/src/include/CanonicalJson.h b/src/include/CanonicalJson.h index aab3d08..978de40 100644 --- a/src/include/CanonicalJson.h +++ b/src/include/CanonicalJson.h @@ -79,6 +79,16 @@ */ extern int CanonicalJsonEncode(HashMap *, Stream *); +/** + * Computes a JSON object encoded as Canonical JSON's SHA-256 + * hash. + * + * This function returns a SHA-256 string stored on the heap, + * which will need to be freed with + * .Fn Free . + */ +extern char * CanonicalJsonHash(HashMap *); + /** * Encode a JSON value following the rules of Canonical JSON. * See the documentation for