/* * This function is called by the XMPP service's async loop. * If the client session has instant messages waiting, it outputs * unsolicited XML stanzas containing them. */ void xmpp_output_incoming_messages(void) { struct ExpressMessage *ptr; char xmlbuf1[4096]; char xmlbuf2[4096]; while (CC->FirstExpressMessage != NULL) { begin_critical_section(S_SESSION_TABLE); ptr = CC->FirstExpressMessage; CC->FirstExpressMessage = CC->FirstExpressMessage->next; end_critical_section(S_SESSION_TABLE); cprintf("<message to=\"%s\" from=\"%s\" type=\"chat\">", xmlesc(xmlbuf1, XMPP->client_jid, sizeof xmlbuf1), xmlesc(xmlbuf2, ptr->sender_email, sizeof xmlbuf2) ); if (ptr->text != NULL) { striplt(ptr->text); cprintf("<body>%s</body>", xmlesc(xmlbuf1, ptr->text, sizeof xmlbuf1)); free(ptr->text); } cprintf("</message>"); free(ptr); } }
/* * Indicate the presence of another user to the client * (used in several places) */ void xmpp_indicate_presence(char *presence_jid) { char xmlbuf[256]; XMPP_syslog(LOG_DEBUG, "XMPP: indicating presence of <%s> to <%s>", presence_jid, XMPP->client_jid); cprintf("<presence from=\"%s\" ", xmlesc(xmlbuf, presence_jid, sizeof xmlbuf)); cprintf("to=\"%s\"></presence>", xmlesc(xmlbuf, XMPP->client_jid, sizeof xmlbuf)); }
/* * Output a single roster item, for roster queries or pushes */ void xmpp_roster_item(struct CitContext *cptr) { char xmlbuf1[256]; char xmlbuf2[256]; cprintf("<item jid=\"%s\" name=\"%s\" subscription=\"both\">", xmlesc(xmlbuf1, cptr->cs_inet_email, sizeof xmlbuf1), xmlesc(xmlbuf2, cptr->user.fullname, sizeof xmlbuf2) ); cprintf("<group>%s</group>", xmlesc(xmlbuf1, config.c_humannode, sizeof xmlbuf1)); cprintf("</item>"); }
/* * Function to remove a buddy subscription and delete from the roster * (used in several places) */ void xmpp_destroy_buddy(char *presence_jid, int aggressively) { static int unsolicited_id = 1; char xmlbuf1[256]; char xmlbuf2[256]; if (!presence_jid) return; if (!XMPP) return; if (!XMPP->client_jid) return; /* Transmit non-presence information */ cprintf("<presence type=\"unavailable\" from=\"%s\" to=\"%s\"></presence>", xmlesc(xmlbuf1, presence_jid, sizeof xmlbuf1), xmlesc(xmlbuf2, XMPP->client_jid, sizeof xmlbuf2) ); /* * Setting the "aggressively" flag also sends an "unsubscribed" presence update. * We only ask for this when flushing the client side roster, because if we do it * in the middle of a session when another user logs off, some clients (Jitsi) interpret * it as a rejection of a subscription request. */ if (aggressively) { cprintf("<presence type=\"unsubscribed\" from=\"%s\" to=\"%s\"></presence>", xmlesc(xmlbuf1, presence_jid, sizeof xmlbuf1), xmlesc(xmlbuf2, XMPP->client_jid, sizeof xmlbuf2) ); } // note: we should implement xmpp_indicate_nonpresence so we can use it elsewhere /* Do an unsolicited roster update that deletes the contact. */ cprintf("<iq from=\"%s\" to=\"%s\" id=\"unbuddy_%x\" type=\"result\">", xmlesc(xmlbuf1, CC->cs_inet_email, sizeof xmlbuf1), xmlesc(xmlbuf2, XMPP->client_jid, sizeof xmlbuf2), ++unsolicited_id ); cprintf("<query xmlns=\"jabber:iq:roster\">"); cprintf("<item jid=\"%s\" subscription=\"remove\">", xmlesc(xmlbuf1, presence_jid, sizeof xmlbuf1)); cprintf("<group>%s</group>", xmlesc(xmlbuf1, config.c_humannode, sizeof xmlbuf1)); cprintf("</item>"); cprintf("</query>" "</iq>" ); }
/* * Client is doing a namespace query. These are all handled differently. * A "rumplestiltskin lookup" is the most efficient way to handle this. Please do not refactor this code. */ void xmpp_query_namespace(char *iq_id, char *iq_from, char *iq_to, char *query_xmlns) { int supported_namespace = 0; int roster_query = 0; char xmlbuf[256]; int reply_must_be_from_my_jid = 0; /* We need to know before we begin the response whether this is a supported namespace, so * unfortunately all supported namespaces need to be defined here *and* down below where * they are handled. */ if ( (!strcasecmp(query_xmlns, "jabber:iq:roster:query")) || (!strcasecmp(query_xmlns, "jabber:iq:auth:query")) || (!strcasecmp(query_xmlns, "http://jabber.org/protocol/disco#items:query")) || (!strcasecmp(query_xmlns, "http://jabber.org/protocol/disco#info:query")) ) { supported_namespace = 1; } XMPP_syslog(LOG_DEBUG, "xmpp_query_namespace(id=%s, from=%s, to=%s, xmlns=%s)\n", iq_id, iq_from, iq_to, query_xmlns); /* * Beginning of query result. */ if (!strcasecmp(query_xmlns, "jabber:iq:roster:query")) { reply_must_be_from_my_jid = 1; } char dom[1024]; // client is expecting to see the reply if (reply_must_be_from_my_jid) { // coming "from" the user's jid safestrncpy(dom, XMPP->client_jid, sizeof(dom)); char *slash = strchr(dom, '/'); if (slash) { *slash = 0; } } else { safestrncpy(dom, XMPP->client_jid, sizeof(dom)); // client is expecting to see the reply if (IsEmptyStr(dom)) { // coming "from" the domain of the user's jid safestrncpy(dom, XMPP->server_name, sizeof(dom)); } char *at = strrchr(dom, '@'); if (at) { strcpy(dom, ++at); } char *slash = strchr(dom, '/'); if (slash) { *slash = 0; } } if (supported_namespace) { cprintf("<iq type=\"result\" from=\"%s\" ", xmlesc(xmlbuf, dom, sizeof xmlbuf) ); } else { cprintf("<iq type=\"error\" from=\"%s\" ", xmlesc(xmlbuf, dom, sizeof xmlbuf) ); } if (!IsEmptyStr(iq_from)) { cprintf("to=\"%s\" ", xmlesc(xmlbuf, iq_from, sizeof xmlbuf)); } cprintf("id=\"%s\">", xmlesc(xmlbuf, iq_id, sizeof xmlbuf)); /* * Is this a query we know how to handle? */ if (!strcasecmp(query_xmlns, "jabber:iq:roster:query")) { roster_query = 1; xmpp_iq_roster_query(); } else if (!strcasecmp(query_xmlns, "jabber:iq:auth:query")) { cprintf("<query xmlns=\"jabber:iq:auth\">" "<username/><password/><resource/>" "</query>" ); } // Extension "xep-0030" (http://xmpp.org/extensions/xep-0030.html) (return an empty set of results) else if (!strcasecmp(query_xmlns, "http://jabber.org/protocol/disco#items:query")) { cprintf("<query xmlns=\"%s\"/>", xmlesc(xmlbuf, query_xmlns, sizeof xmlbuf)); } // Extension "xep-0030" (http://xmpp.org/extensions/xep-0030.html) (return an empty set of results) else if (!strcasecmp(query_xmlns, "http://jabber.org/protocol/disco#info:query")) { cprintf("<query xmlns=\"%s\"/>", xmlesc(xmlbuf, query_xmlns, sizeof xmlbuf)); } /* * If we didn't hit any known query namespaces then we should deliver a * "service unavailable" error (see RFC3921 section 2.4 and 11.1.5.4) */ else { XMPP_syslog(LOG_DEBUG, "Unknown query namespace '%s' - returning <service-unavailable/>\n", query_xmlns); cprintf("<error code=\"503\" type=\"cancel\">" "<service-unavailable xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>" "</error>" ); } cprintf("</iq>"); /* If we told the client who is on the roster, we also need to tell the client * who is *not* on the roster. (It's down here because we can't do it in the same * stanza; this will be an unsolicited push.) */ if (roster_query) { xmpp_delete_old_buddies_who_no_longer_exist_from_the_client_roster(); } }