diff --git a/src/Events.c b/src/Events.c index c07779a..6a3137d 100644 --- a/src/Events.c +++ b/src/Events.c @@ -129,3 +129,31 @@ MatrixGetReply(HashMap *event) "event_id") )); } +HashMap * +MatrixCreateReplace(char *event, char *body) +{ + HashMap *map; + HashMap *new; + HashMap *rel; + if (!body || !event) + { + return NULL; + } + + map = HashMapCreate(); + HashMapSet(map, "msgtype", JsonValueString("m.text")); + HashMapSet(map, "body", JsonValueString(body)); + + new = HashMapCreate(); + HashMapSet(new, "msgtype", JsonValueString("m.text")); + HashMapSet(new, "body", JsonValueString(body)); + + rel = HashMapCreate(); + HashMapSet(rel, "rel_type", JsonValueString("m.replace")); + HashMapSet(rel, "event_id", JsonValueString(event)); + + HashMapSet(map, "m.new_content", JsonValueObject(new)); + HashMapSet(map, "m.relates_to", JsonValueObject(rel)); + + return map; +} diff --git a/src/MatrixEventHandler.c b/src/MatrixEventHandler.c index 7cc0e8e..4c0045f 100644 --- a/src/MatrixEventHandler.c +++ b/src/MatrixEventHandler.c @@ -18,7 +18,6 @@ ParseeMemberHandler(ParseeData *data, HashMap *event) char *sender = GrabString(event, 1, "sender"); char *chat_id; const ParseeConfig *conf = data->config; - Log(LOG_INFO, "Membership '%s'->'%s'", state_key, membership); if (StrEquals(membership, "invite") && ParseeIsPuppet(conf, state_key)) { @@ -84,11 +83,6 @@ ParseeMessageHandler(ParseeData *data, HashMap *event) return; } - if (reply_id) - { - Log(LOG_INFO, "reply=%s", reply_id); - } - ref = DbLock(data->db, 3, "rooms", id, "data"); json = DbJson(ref); direct = JsonValueAsBoolean(HashMapGet(json, "is_direct")); @@ -125,8 +119,6 @@ ParseeMessageHandler(ParseeData *data, HashMap *event) if (reply_id) { ParseeGetStanzaInfo(data, chat_id, reply_id, &stanza, &sender); - - Log(LOG_INFO, "Replying to %s by %s", stanza, sender); } XMPPJoinMUC(jabber, jid, rev); XMPPSendPlain( diff --git a/src/Parsee/Data.c b/src/Parsee/Data.c index d66545d..a9d9b81 100644 --- a/src/Parsee/Data.c +++ b/src/Parsee/Data.c @@ -285,3 +285,137 @@ ParseeXMPPify(HashMap *event) return xepd; } +void +ParseePushStanza(ParseeData *data, char *chat_id, char *stanza_id, char *id, char *ev, char *sender) +{ + DbRef *ref; + HashMap *j; + HashMap *stanzas, *obj, *events, *ids; + bool new_stanzas = false, new_events = false; + bool new_ids = false; + uint64_t age = UtilTsMillis(); + if (!data || !chat_id || !stanza_id || !ev || !sender) + { + return; + } + + ref = DbLock(data->db, 2, "chats", chat_id); + j = DbJson(ref); + if (!ref) + { + return; + } + + stanzas = JsonValueAsObject(HashMapGet(j, "stanzas")); + if (!stanzas) + { + stanzas = HashMapCreate(); + new_stanzas = true; + } + + obj = HashMapCreate(); + HashMapSet(obj, "age", JsonValueInteger(age)); + JsonValueFree(HashMapSet(stanzas, stanza_id, JsonValueObject(obj))); + + if (new_stanzas) + { + HashMapSet(j, "stanzas", JsonValueObject(stanzas)); + } + + events = JsonValueAsObject(HashMapGet(j, "events")); + if (!events) + { + events = HashMapCreate(); + new_events = true; + } + obj = HashMapCreate(); + HashMapSet(obj, "stanza", JsonValueString(stanza_id)); + HashMapSet(obj, "origin", JsonValueString(id)); + HashMapSet(obj, "sender", JsonValueString(sender)); + JsonValueFree(HashMapSet(events, ev, JsonValueObject(obj))); + if (new_events) + { + HashMapSet(j, "events", JsonValueObject(events)); + } + + if (id) + { + ids = JsonValueAsObject(HashMapGet(j, "ids")); + if (!ids) + { + ids = HashMapCreate(); + new_ids = true; + } + obj = HashMapCreate(); + HashMapSet(obj, "stanza", JsonValueString(stanza_id)); + HashMapSet(obj, "event", JsonValueString(ev)); + JsonValueFree(HashMapSet(ids, id, JsonValueObject(obj))); + if (new_ids) + { + HashMapSet(j, "ids", JsonValueObject(ids)); + } + } + DbUnlock(data->db, ref); +} + +bool +ParseeVerifyStanza(ParseeData *data, char *chat_id, char *stanza_id) +{ + DbRef *ref = NULL; + HashMap *j = NULL; + HashMap *stanzas = NULL; + bool ret = true; + if (!data || !chat_id || !stanza_id) + { + return true; + } + + ref = DbLock(data->db, 2, "chats", chat_id); + j = DbJson(ref); + if (!ref) + { + goto end; + } + + stanzas = JsonValueAsObject(HashMapGet(j, "stanzas")); + if (!stanzas) + { + goto end; + } + + ret = !HashMapGet(stanzas, stanza_id); +end: + DbUnlock(data->db, ref); + return ret; +} +char * +ParseeEventFromID(ParseeData *data, char *chat_id, char *id) +{ + DbRef *ref = NULL; + HashMap *j = NULL; + HashMap *ids = NULL; + char *ret = NULL; + if (!data || !chat_id || !id) + { + return NULL; + } + + ref = DbLock(data->db, 2, "chats", chat_id); + j = DbJson(ref); + if (!ref) + { + goto end; + } + + ids = JsonValueAsObject(HashMapGet(j, "ids")); + if (!ids) + { + goto end; + } + + ret = JsonValueAsString(JsonGet(ids, 2, id, "event")); + ret = StrDuplicate(ret); +end: + DbUnlock(data->db, ref); + return ret; +} diff --git a/src/Parsee/User.c b/src/Parsee/User.c index ecfd1ff..d91d8b1 100644 --- a/src/Parsee/User.c +++ b/src/Parsee/User.c @@ -540,89 +540,6 @@ ParseeGetMUCID(ParseeData *data, char *chat_id) return ret; } -void -ParseePushStanza(ParseeData *data, char *chat_id, char *stanza_id, char *ev, char *sender) -{ - DbRef *ref; - HashMap *j; - HashMap *stanzas, *obj, *events; - bool new_stanzas = false, new_events = false; - uint64_t age = UtilTsMillis(); - if (!data || !chat_id || !stanza_id || !ev || !sender) - { - return; - } - - ref = DbLock(data->db, 2, "chats", chat_id); - j = DbJson(ref); - if (!ref) - { - return; - } - - stanzas = JsonValueAsObject(HashMapGet(j, "stanzas")); - if (!stanzas) - { - stanzas = HashMapCreate(); - new_stanzas = true; - } - - obj = HashMapCreate(); - HashMapSet(obj, "age", JsonValueInteger(age)); - JsonValueFree(HashMapSet(stanzas, stanza_id, JsonValueObject(obj))); - - if (new_stanzas) - { - HashMapSet(j, "stanzas", JsonValueObject(stanzas)); - } - - events = JsonValueAsObject(HashMapGet(j, "events")); - if (!events) - { - events = HashMapCreate(); - new_events = true; - } - obj = HashMapCreate(); - HashMapSet(obj, "stanza", JsonValueString(stanza_id)); - HashMapSet(obj, "sender", JsonValueString(sender)); - JsonValueFree(HashMapSet(events, ev, JsonValueObject(obj))); - if (new_events) - { - HashMapSet(j, "events", JsonValueObject(events)); - } - DbUnlock(data->db, ref); -} - -bool -ParseeVerifyStanza(ParseeData *data, char *chat_id, char *stanza_id) -{ - DbRef *ref = NULL; - HashMap *j = NULL; - HashMap *stanzas = NULL; - bool ret = true; - if (!data || !chat_id || !stanza_id) - { - return true; - } - - ref = DbLock(data->db, 2, "chats", chat_id); - j = DbJson(ref); - if (!ref) - { - goto end; - } - - stanzas = JsonValueAsObject(HashMapGet(j, "stanzas")); - if (!stanzas) - { - goto end; - } - - ret = !HashMapGet(stanzas, stanza_id); -end: - DbUnlock(data->db, ref); - return ret; -} void ParseeSendPresence(ParseeData *data) diff --git a/src/Routes/Transactions.c b/src/Routes/Transactions.c index fd178f3..7987f50 100644 --- a/src/Routes/Transactions.c +++ b/src/Routes/Transactions.c @@ -35,9 +35,7 @@ RouteHead(RouteTxns, arr, argp) for (i = 0; i < ArraySize(events); i++) { HashMap *event = JsonValueAsObject(ArrayGet(events, i)); - event = JsonDuplicate(event); ParseeEventHandler(args->data, event); - JsonFree(event); } /* TODO: Store TXN ID somewhere so that we can commit diff --git a/src/Signal.c b/src/Signal.c index 6a706a6..8289d31 100644 --- a/src/Signal.c +++ b/src/Signal.c @@ -34,6 +34,7 @@ SignalHandler(int signal) Log(LOG_INFO, "Killing thread..."); XMPPKillThread(jabber, "killer"); pthread_join(xmpp_thr, NULL); + Log(LOG_INFO, "Stopping server..."); HttpServerStop(server); break; } diff --git a/src/XML/Elements.c b/src/XML/Elements.c index f6c3c6d..714ef5e 100644 --- a/src/XML/Elements.c +++ b/src/XML/Elements.c @@ -137,7 +137,7 @@ XMLookForTKV(XMLElement *parent, char *tag, char *k, char *v) { size_t i; - if (!parent || !tag || !k || !v) + if (!parent || !k || !v) { return NULL; } @@ -147,9 +147,12 @@ XMLookForTKV(XMLElement *parent, char *tag, char *k, char *v) XMLElement *child = ArrayGet(parent->children, i); HashMap *attrs = child->attrs; char *value = HashMapGet(attrs, k); - if (StrEquals(child->name, tag) && StrEquals(value, v)) + if (StrEquals(child->name, tag) || !tag) { - return child; + if (StrEquals(value, v)) + { + return child; + } } } diff --git a/src/XML/SAX.c b/src/XML/SAX.c index f60c22a..eb51c27 100644 --- a/src/XML/SAX.c +++ b/src/XML/SAX.c @@ -730,12 +730,14 @@ XMLParseAttQuote(XMLexer *lexer) char *code = NULL; int c2; int p2 = XMLInitialiseBuffer(lexer); - while ((c2 = XMLGetc(lexer)) && c2 != EOF) + int j = 0; + while ((c2 = XMLGetc(lexer)) && c2 != EOF && j < 8) { if (c2 == ';') { break; } + j++; } if (c2 != ';') { @@ -775,12 +777,14 @@ XMLParseAttDouble(XMLexer *lexer) char *code = NULL; int c2; int p2 = XMLInitialiseBuffer(lexer); - while ((c2 = XMLGetc(lexer)) && c2 != EOF) + int j = 0; + while ((c2 = XMLGetc(lexer)) && c2 != EOF && j < 8) { if (c2 == ';') { break; } + j++; } if (c2 != ';') { diff --git a/src/XMPP/Stanza.c b/src/XMPP/Stanza.c index 5f6bc89..4e4aefd 100644 --- a/src/XMPP/Stanza.c +++ b/src/XMPP/Stanza.c @@ -199,3 +199,38 @@ XMPPIsParseeStanza(XMLElement *stanza) return !!XMLookForUnique(stanza, "x-parsee"); } + + +char * +XMPPGetStanzaID(XMLElement *stanza) +{ + XMLElement *stanza_id; + if (!stanza) + { + return NULL; + } + + stanza_id = XMLookForTKV(stanza, "stanza-id", "xmlns", "urn:xmpp:sid:0"); + return stanza_id ? HashMapGet(stanza_id->attrs, "id") : NULL; +} +char * +XMPPGetOriginID(XMLElement *stanza) +{ + XMLElement *origin_id; + if (!stanza) + { + return NULL; + } + + origin_id = XMLookForTKV(stanza, "origin-id", "xmlns", "urn:xmpp:sid:0"); + return origin_id ? HashMapGet(origin_id->attrs, "id") : NULL; +} +char * +XMPPGetReplacedID(XMLElement *stanza) +{ + XMLElement *replace = XMLookForTKV( + stanza, "replace", + "xmlns", "urn:xmpp:message-correct:0" + ); + return replace ? HashMapGet(replace->attrs, "id") : NULL; +} diff --git a/src/XMPPThread.c b/src/XMPPThread.c index a00541b..37c7375 100644 --- a/src/XMPPThread.c +++ b/src/XMPPThread.c @@ -40,7 +40,6 @@ MessageStanza(ParseeData *args, XMLElement *stanza) XMLElement *body = NULL; XMLElement *data = NULL; - XMLElement *stanza_id = NULL; char *to, *room, *from, *from_matrix; char *chat_id, *mroom_id; @@ -51,7 +50,6 @@ MessageStanza(ParseeData *args, XMLElement *stanza) XMLFreeElement(stanza); return false; } - stanza_id = XMLookForUnique(stanza, "stanza-id"); to = ParseeDecodeMXID(HashMapGet(stanza->attrs, "to")); from = HashMapGet(stanza->attrs, "from"); @@ -69,12 +67,15 @@ MessageStanza(ParseeData *args, XMLElement *stanza) if (mroom_id && !XMPPIsParseeStanza(stanza)) { char *res = ParseeGetResource(from); - char *encoded = ParseeEncodeJID(args->config, from, false); - char *s_id_str = HashMapGet(stanza_id->attrs, "id"); + char *encoded = ParseeEncodeJID(args->config, from, false); + char *s_id_str = XMPPGetStanzaID(stanza); + char *o_id_str = XMPPGetOriginID(stanza); + char *id_str = HashMapGet(stanza->attrs, "id"); char *event_id; + char *replaced = XMPPGetReplacedID(stanza); /* TODO: Create smarter puppet names */ - if (ParseeVerifyStanza(args, chat_id, s_id_str)) + if (ParseeVerifyStanza(args, chat_id, s_id_str) && !replaced) { XMLElement *oob, *oob_data; @@ -108,7 +109,18 @@ MessageStanza(ParseeData *args, XMLElement *stanza) "m.room.message", MatrixCreateMessage(data->data) ); } - ParseePushStanza(args, chat_id, s_id_str, event_id, from); + ParseePushStanza(args, chat_id, s_id_str, id_str, event_id, from); + Free(event_id); + } + else if (replaced) + { + event_id = ParseeEventFromID(args, chat_id, replaced); + Log(LOG_WARNING, "RETRACTION REQUEST TO %s->%s", replaced, event_id); + Free(ASSend( + args->config, mroom_id, encoded, + "m.room.message", MatrixCreateReplace(event_id, data->data) + )); + Free(event_id); } @@ -154,8 +166,10 @@ IQDiscoGet(ParseeData *args, XMPPComponent *jabber, XMLElement *stanza) } \ while (0) - AdvertiseSimple("urn:xmpp:reply:0"); + AdvertiseSimple("urn:xmpp:message-correct:0"); AdvertiseSimple("urn:xmpp:styling:0"); + AdvertiseSimple("urn:xmpp:reply:0"); + AdvertiseSimple("urn:xmpp:sid:0"); AdvertiseSimple("urn:parsee:x-parsee:0"); AdvertiseSimple("urn:parsee:jealousy:0"); @@ -234,6 +248,7 @@ ParseeXMPPThread(void *argp) from = HashMapGet(stanza->attrs, "from"); if (!strncmp(from, killer, strlen(killer))) { + Log(LOG_INFO, "Killer detected."); XMLFreeElement(stanza); break; } diff --git a/src/include/Matrix.h b/src/include/Matrix.h index 974f20d..bfb2685 100644 --- a/src/include/Matrix.h +++ b/src/include/Matrix.h @@ -12,6 +12,9 @@ extern HashMap * MatrixCreateNotice(char *body); /* Creates the content for a normal message. */ extern HashMap * MatrixCreateMessage(char *body); +/* Creates the content for a replace message. */ +extern HashMap * MatrixCreateReplace(char *event, char *body); + /* Creates the content for a media file. */ extern HashMap * MatrixCreateMedia(char *mxc, char *body, char *mime); diff --git a/src/include/Parsee.h b/src/include/Parsee.h index 38b8fd5..b61ec2e 100644 --- a/src/include/Parsee.h +++ b/src/include/Parsee.h @@ -145,7 +145,7 @@ extern char * ParseeGetRoomID(ParseeData *, char *chat_id); extern char * ParseeGetMUCID(ParseeData *, char *chat_id); /* Pushes a stanza ID to a chat ID */ -extern void ParseePushStanza(ParseeData *, char *chat_id, char *stanza_id, char *event, char *sender); +extern void ParseePushStanza(ParseeData *, char *chat_id, char *stanza_id, char *origin_id, char *event, char *sender); /* Checks if a stanza is not duplicated in a chat ID */ extern bool ParseeVerifyStanza(ParseeData *, char *chat_id, char *stanza_id); @@ -167,4 +167,7 @@ extern int ParseeFindDatastart(char *data); /* XMPP-ifies a message event to XEP-0393 if possible. */ extern char * ParseeXMPPify(HashMap *event); + +/* Finds an event ID from an ID in the stanza's attributes */ +extern char * ParseeEventFromID(ParseeData *d, char *c_id, char *ori_id); #endif diff --git a/src/include/XMPP.h b/src/include/XMPP.h index 9c3710b..8020d8b 100644 --- a/src/include/XMPP.h +++ b/src/include/XMPP.h @@ -61,4 +61,14 @@ extern void XMPPFreeMUCInfo(MUCInfo info); /* Checks if a stanza has an x-parsee element */ extern bool XMPPIsParseeStanza(XMLElement *); +/* Returns the stanza ID of a stanza, if existent */ +extern char * XMPPGetStanzaID(XMLElement *); + +/* Returns the origin ID of a stanza, if existent */ +extern char * XMPPGetOriginID(XMLElement *); + +/* Returns the origin ID of the replaced stanza, if the current one + * is a replacement notice */ +extern char * XMPPGetReplacedID(XMLElement *); + #endif