#include "XMPPThread/internal.h" #include #include #include #include #include #include static XMLElement * CreateVCardRequest(char *from, char *to) { XMLElement *vcard_request, *vcard; char *id; vcard_request = XMLCreateTag("iq"); XMLAddAttr(vcard_request, "from", from); XMLAddAttr(vcard_request, "to", to); XMLAddAttr(vcard_request, "id", (id = StrRandom(16))); XMLAddAttr(vcard_request, "type", "get"); vcard = XMLCreateTag("vCard"); XMLAddAttr(vcard, "xmlns", "vcard-temp"); XMLAddChild(vcard_request, vcard); Free(id); return vcard_request; } static UserStatus GuessStatus(XMLElement *stanza) { /* C.F RFC3921: XMPP IM */ XMLElement *show = XMLookForUnique(stanza, "show"); XMLElement *data = show ? ArrayGet(show->children, 0) : NULL; if (!show || !data) { return USER_STATUS_ONLINE; } if (StrEquals(data->data, "away") || StrEquals(data->data, "xa")) { return USER_STATUS_OFFLINE; } if (StrEquals(data->data, "chat")) { return USER_STATUS_ONLINE; } if (StrEquals(data->data, "dnd")) { return USER_STATUS_UNAVAILABLE; } return USER_STATUS_ONLINE; } void PresenceStanza(ParseeData *args, XMLElement *stanza) { #define MUC_USER_NS "http://jabber.org/protocol/muc#user" XMLElement *user_info; XMLElement *vc = XMLookForTKV(stanza, "x", "xmlns", "vcard-temp:x:update"); XMLElement *status = XMLookForUnique(stanza, "status"); char *oid = HashMapGet(stanza->attrs, "from"); if ((user_info = XMLookForTKV(stanza, "x", "xmlns", MUC_USER_NS))) { XMLElement *item = XMLookForUnique(user_info, "item"); XMLElement *status = XMLookForUnique(user_info, "status"); #define IsStatus(code) (status && \ StrEquals(HashMapGet(status->attrs, "code"), #code)) char *jid = item ? HashMapGet(item->attrs, "jid") : NULL; char *trim = ParseeTrimJID(jid); char *from = NULL; char *type = HashMapGet(stanza->attrs, "type"); char *room = ParseeGetBridgedRoom(args, stanza); char *decode_from = ParseeLookupJID(oid); char *real_matrix = ParseeDecodeMXID(decode_from); char *matrix_user_pl = ParseeEncodeJID(args->config, trim, false); char *affiliation = HashMapGet(item->attrs, "affiliation"); int power_level = 0; char *parsee = ParseeMXID(args); Free(trim); if (!real_matrix || *real_matrix != '@') { Free(real_matrix); real_matrix = ParseeEncodeJID(args->config, decode_from, false); } if (StrEquals(affiliation, "owner")) { power_level = 98; } else if (StrEquals(affiliation, "admin")) { power_level = 50; } else if (StrEquals(affiliation, "member")) { /* TODO: Reconsider setting the PL if a member, as there's no * clear reason to do this in some cases. */ power_level = 0; } else if (StrEquals(affiliation, "outcast")) { power_level = -1; } /* Set the user's PL * TODO: Do NOT change the PL of *real* people nilly-willy. * In some scenarios, this is really bad behaviour. */ if (room) { HashMap *powers = ASGetPL(args->config, room); HashMap *users = GrabObject(powers, 1, "users"); int64_t level = GrabInteger(powers, 2, "users", matrix_user_pl); /* I may or may not have fucked up the state hard with this * lacking any checking. (--gen) */ if (StrEquals(parsee, matrix_user_pl)) { /* TODO: Give the user an achievement, this is goofy. */ Log(LOG_ERR, "BAD PL DOWNGRADE (%d->%d)", level, power_level); } if (users && level != power_level && !StrEquals(parsee, matrix_user_pl) && matrix_user_pl) { JsonValue *val = JsonValueInteger(power_level); JsonValueFree(JsonSet(users, val, 1, matrix_user_pl)); ASSetPL(args->config, room, powers); } else { JsonFree(powers); } } if (StrEquals(type, "unavailable") && !StrEquals(parsee, real_matrix)) { /* If not an MXID, use the Parsee user */ if (IsStatus(301)) { ASBan(args->config, room, real_matrix); } else if (IsStatus(307)) { ASKick(args->config, room, real_matrix); } else if (IsStatus(303)) { char *nick = HashMapGet(item->attrs, "nick"); ASSetName(args->config, real_matrix, nick); } else { ASLeave(args->config, room, real_matrix); } } if (jid) { ParseePushJIDTable(oid, jid); } Free(from); Free(decode_from); Free(real_matrix); Free(matrix_user_pl); Free(parsee); Free(room); } if (status) { XMLElement *status_data = ArrayGet(status->children, 0); char *decode_from = ParseeLookupJID(oid); char *trimmed = ParseeTrimJID(decode_from); char *from_matrix = ParseeEncodeJID(args->config, trimmed, false); char *status_str = NULL; if (status_data) { status_str = status_data->data; } /* TODO: "The server will automatically set a user's presence to * unavailable if their last active time was over a threshold value * (e.g. 5 minutes)." * We _will_ need to manage those cases properly(cronjob?) if we want * XMPP presences to sync properly */ ASSetStatus( args->config, from_matrix, GuessStatus(stanza), status_str ); Free(decode_from); Free(from_matrix); Free(trimmed); } if (vc) { XMLElement *photo = XMLookForUnique(vc, "photo"); XMLElement *p_dat = photo ? ArrayGet(photo->children, 0) : NULL; XMLElement *vcard_request; XMPPComponent *jabber = args->jabber; char *from; DbRef *avatars; HashMap *json; char *avatar_id; if (!p_dat) { return; } avatars = DbLock(args->db, 1, "avatars"); if (!avatars) { avatars = DbCreate(args->db, 1, "avatars"); } json = DbJson(avatars); avatar_id = GrabString(json, 1, oid); if (avatar_id && StrEquals(avatar_id, p_dat->data)) { DbUnlock(args->db, avatars); return; } JsonValueFree(JsonSet( json, JsonValueString(p_dat->data), 1, oid) ); DbUnlock(args->db, avatars); from = ParseeJID(args); vcard_request = CreateVCardRequest( from, HashMapGet(stanza->attrs, "from") ); pthread_mutex_lock(&jabber->write_lock); XMLEncode(jabber->stream, vcard_request); StreamFlush(jabber->stream); pthread_mutex_unlock(&jabber->write_lock); XMLFreeElement(vcard_request); Free(from); } /* TODO: Sending VCard on presence is slow and stalls the thread */ #undef MUC_USER_NS }