예제 #1
0
/*
 * 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);
	}
}
예제 #2
0
/* 
 * 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));
}
예제 #3
0
/*
 * 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>");
}
예제 #4
0
/*
 * 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>"
	);
}
예제 #5
0
/*
 * 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();
	}
}