/*
- * $Id: $
+ * $Id$
*
* XMPP (Jabber) service for the Citadel system
* Copyright (c) 2007 by Art Cancro
#include <expat.h>
#include "serv_xmpp.h"
+struct xmpp_event *xmpp_queue = NULL;
/* We have just received a <stream> tag from the client, so send them ours */
/* The features of this stream are... */
cprintf("<stream:features>");
- /* Binding... */
- cprintf("<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/>");
-
- /* Sessions... */
- cprintf("<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>");
-
- /* A really bad SASL implementation... */
- xmpp_output_auth_mechs();
+ if (!CC->logged_in) {
+ /* If we're not logged in yet, offer SASL as our feature set */
+ xmpp_output_auth_mechs();
+ }
+ else {
+ /* If we've logged in, now offer binding and sessions as our feature set */
+ cprintf("<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/>");
+ cprintf("<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>");
+ }
- /* ...and the ability to close XML tags using angle brackets. We should patent this. */
cprintf("</stream:features>");
+
+ CC->is_async = 1; /* XMPP sessions are inherently async-capable */
}
xmpp_stream_start(data, supplied_el, attr);
}
+ else if (!strcasecmp(el, "query")) {
+ XMPP->iq_query_xmlns[0] = 0;
+ safestrncpy(XMPP->iq_query_xmlns, supplied_el, sizeof XMPP->iq_query_xmlns);
+ }
+
else if (!strcasecmp(el, "iq")) {
- const char *iqtype = NULL;
- const char *iqid = NULL;
for (i=0; attr[i] != NULL; i+=2) {
- if (!strcasecmp(attr[i], "type")) iqtype = attr[i+1];
- if (!strcasecmp(attr[i], "id")) iqid = attr[i+1];
- }
- if ((iqtype != NULL) && (iqid != NULL)) {
- if (!strcasecmp(iqtype, "set")) {
- safestrncpy(XMPP->iq_bind_id, iqid, sizeof XMPP->iq_bind_id);
+ if (!strcasecmp(attr[i], "type")) {
+ safestrncpy(XMPP->iq_type, attr[i+1], sizeof XMPP->iq_type);
+ }
+ else if (!strcasecmp(attr[i], "id")) {
+ safestrncpy(XMPP->iq_id, attr[i+1], sizeof XMPP->iq_id);
+ }
+ else if (!strcasecmp(attr[i], "from")) {
+ safestrncpy(XMPP->iq_from, attr[i+1], sizeof XMPP->iq_from);
+ }
+ else if (!strcasecmp(attr[i], "to")) {
+ safestrncpy(XMPP->iq_to, attr[i+1], sizeof XMPP->iq_to);
}
}
}
else if (!strcasecmp(el, "iq")) {
+ /*
+ * iq type="get" (handle queries)
+ */
+ if (!strcasecmp(XMPP->iq_type, "get")) {
+
+ /*
+ * Query on a namespace
+ */
+ if (!IsEmptyStr(XMPP->iq_query_xmlns)) {
+ xmpp_query_namespace(XMPP->iq_id, XMPP->iq_from,
+ XMPP->iq_to, XMPP->iq_query_xmlns);
+ }
- /* If this <iq> stanza was a "bind" attempt, process it ... */
+ /*
+ * Unknown queries ... return the XML equivalent of a blank stare
+ */
+ else {
+ cprintf("<iq type=\"result\" id=\"%s\">", XMPP->iq_id);
+ cprintf("</iq>");
+ }
+ }
- if ( (!IsEmptyStr(XMPP->iq_bind_id)) && (!IsEmptyStr(XMPP->iq_client_resource)) ) {
+ /*
+ * If this <iq> stanza was a "bind" attempt, process it ...
+ */
+ else if ( (!IsEmptyStr(XMPP->iq_id)) && (!IsEmptyStr(XMPP->iq_client_resource)) ) {
/* Generate the "full JID" of the client resource */
+ // snprintf(XMPP->client_jid, sizeof XMPP->client_jid,
+ // "%d@%s/%s",
+ // CC->cs_pid,
+ // config.c_fqdn,
+ // XMPP->iq_client_resource
+ //);
+
snprintf(XMPP->client_jid, sizeof XMPP->client_jid,
- "%d@%s/%s",
- CC->cs_pid,
- config.c_fqdn,
+ "%s/%s",
+ CC->cs_inet_email,
XMPP->iq_client_resource
);
/* Tell the client what its JID is */
- cprintf("<iq type=\"result\" id=\"%s\">", XMPP->iq_bind_id);
+ cprintf("<iq type=\"result\" id=\"%s\">", XMPP->iq_id);
cprintf("<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\">");
cprintf("<jid>%s</jid>", XMPP->client_jid);
cprintf("</bind>");
cprintf("</iq>");
}
+ else if (XMPP->iq_session) {
+ cprintf("<iq type=\"result\" id=\"%s\">", XMPP->iq_id);
+ cprintf("</iq>");
+ }
+
else {
- cprintf("<iq type=\"error\" id=\"%s\">", XMPP->iq_bind_id);
+ cprintf("<iq type=\"error\" id=\"%s\">", XMPP->iq_id);
cprintf("<error></error>");
cprintf("</iq>");
}
/* Now clear these fields out so they don't get used by a future stanza */
- XMPP->iq_bind_id[0] = 0;
+ XMPP->iq_id[0] = 0;
+ XMPP->iq_from[0] = 0;
+ XMPP->iq_to[0] = 0;
+ XMPP->iq_type[0] = 0;
XMPP->iq_client_resource[0] = 0;
+ XMPP->iq_session = 0;
+ XMPP->iq_query_xmlns[0] = 0;
}
else if (!strcasecmp(el, "auth")) {
XMPP->sasl_auth_mech[0] = 0;
}
+ else if (!strcasecmp(el, "session")) {
+ XMPP->iq_session = 1;
+ }
+
+ else if (!strcasecmp(el, "presence")) {
+
+ /* Respond to a <presence> update by firing back with presence information
+ * on the entire wholist. Check this assumption, it's probably wrong.
+ */
+ jabber_wholist_presence_dump();
+ }
+
XMPP->chardata_len = 0;
if (XMPP->chardata_alloc > 0) {
XMPP->chardata[0] = 0;
XML_Parse(XMPP->xp, cmdbuf, 1, 0);
}
+
+/*
+ * Async loop for XMPP sessions (handles the transmission of unsolicited stanzas)
+ */
+void xmpp_async_loop(void) {
+ jabber_output_incoming_messages();
+}
+
+
+/*
+ * Login hook for XMPP sessions
+ */
+void xmpp_login_hook(void) {
+
+ // we need to somehow alert all xmpp sessions that we are here
+ // and do a roster push followed by a presence push
+
+ lprintf(CTDL_DEBUG, "LOGIN HOOOOOOOOOOOOOKK!!!\n");
+
+}
+
+
const char *CitadelServiceXMPP="XMPP";
#endif /* HAVE_EXPAT */
{
#ifdef HAVE_EXPAT
if (!threading) {
- /* CtdlRegisterServiceHook(config.c_xmpp_port, FIXME */
- CtdlRegisterServiceHook(5222,
+ CtdlRegisterServiceHook(5222, /* FIXME change to config.c_xmpp_port */
NULL,
xmpp_greeting,
xmpp_command_loop,
- NULL,
+ xmpp_async_loop,
CitadelServiceXMPP);
CtdlRegisterSessionHook(xmpp_cleanup_function, EVT_STOP);
+ CtdlRegisterSessionHook(xmpp_login_hook, EVT_LOGIN);
+
#else
lprintf(CTDL_INFO, "This server is missing the Expat XML parser. Jabber service will be disabled.\n");
#endif
}
/* return our Subversion id for the Log */
- return "$Id: $";
+ return "$Id$";
}