X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fmodules%2Fjabber%2Fserv_xmpp.c;h=fe7178db32fe32f813a61f5a15d9eba0da5f2ae5;hb=8c47559cb5ae97ec0fa35660ee16fd61a9451c72;hp=66a32097ca740fd982df1bccda496ef3fda9aa52;hpb=951bd53fc8ee8b3c7660a97de02760c40becd672;p=citadel.git diff --git a/citadel/modules/jabber/serv_xmpp.c b/citadel/modules/jabber/serv_xmpp.c index 66a32097c..fe7178db3 100644 --- a/citadel/modules/jabber/serv_xmpp.c +++ b/citadel/modules/jabber/serv_xmpp.c @@ -2,9 +2,21 @@ * $Id$ * * XMPP (Jabber) service for the Citadel system - * Copyright (c) 2007 by Art Cancro - * This code is released under the terms of the GNU General Public License. + * Copyright (c) 2007-2009 by Art Cancro * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "sysdep.h" @@ -33,6 +45,7 @@ #include #include #include +#include #include "citadel.h" #include "server.h" #include "citserver.h" @@ -46,9 +59,6 @@ #include "internet_addressing.h" #include "md5.h" #include "ctdl_module.h" - -#ifdef HAVE_EXPAT -#include #include "serv_xmpp.h" struct xmpp_event *xmpp_queue = NULL; @@ -57,9 +67,6 @@ struct xmpp_event *xmpp_queue = NULL; void xmpp_stream_start(void *data, const char *supplied_el, const char **attr) { - - lprintf(CTDL_DEBUG, "New stream detected.\n"); - while (*attr) { if (!strcasecmp(attr[0], "to")) { safestrncpy(XMPP->server_name, attr[1], sizeof XMPP->server_name); @@ -79,16 +86,25 @@ void xmpp_stream_start(void *data, const char *supplied_el, const char **attr) /* The features of this stream are... */ cprintf(""); +#ifdef HAVE_OPENSSL_XXXX_COMMENTED_OUT + /* TLS encryption (but only if it isn't already active) */ + if (!CC->redirect_ssl) { + cprintf(""); + } +#endif + 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(""); - cprintf(""); + + /* Also offer non-SASL authentication */ + cprintf(""); } + /* Offer binding and sessions as part of our feature set */ + cprintf(""); + cprintf(""); + cprintf(""); CC->is_async = 1; /* XMPP sessions are inherently async-capable */ @@ -106,11 +122,12 @@ void xmpp_xml_start(void *data, const char *supplied_el, const char **attr) { strcpy(el, ++sep); } - lprintf(CTDL_DEBUG, "XMPP ELEMENT START: <%s>\n", el); - + /* + CtdlLogPrintf(CTDL_DEBUG, "XMPP ELEMENT START: <%s>\n", el); for (i=0; attr[i] != NULL; i+=2) { - lprintf(CTDL_DEBUG, " Attribute '%s' = '%s'\n", attr[i], attr[i+1]); + CtdlLogPrintf(CTDL_DEBUG, " Attribute '%s' = '%s'\n", attr[i], attr[i+1]); } + uncomment for more verbosity */ if (!strcasecmp(el, "stream")) { xmpp_stream_start(data, supplied_el, attr); @@ -121,6 +138,10 @@ void xmpp_xml_start(void *data, const char *supplied_el, const char **attr) { safestrncpy(XMPP->iq_query_xmlns, supplied_el, sizeof XMPP->iq_query_xmlns); } + else if (!strcasecmp(el, "bind")) { + XMPP->bind_requested = 1; + } + else if (!strcasecmp(el, "iq")) { for (i=0; attr[i] != NULL; i+=2) { if (!strcasecmp(attr[i], "type")) { @@ -146,6 +167,18 @@ void xmpp_xml_start(void *data, const char *supplied_el, const char **attr) { } } } + + else if (!strcasecmp(el, "message")) { + for (i=0; attr[i] != NULL; i+=2) { + if (!strcasecmp(attr[i], "to")) { + safestrncpy(XMPP->message_to, attr[i+1], sizeof XMPP->message_to); + } + } + } + + else if (!strcasecmp(el, "html")) { + ++XMPP->html_tag_level; + } } @@ -160,15 +193,34 @@ void xmpp_xml_end(void *data, const char *supplied_el) { strcpy(el, ++sep); } - lprintf(CTDL_DEBUG, "XMPP ELEMENT END : <%s>\n", el); + /* + CtdlLogPrintf(CTDL_DEBUG, "XMPP ELEMENT END : <%s>\n", el); if (XMPP->chardata_len > 0) { - lprintf(CTDL_DEBUG, " chardata: %s\n", XMPP->chardata); + CtdlLogPrintf(CTDL_DEBUG, " chardata: %s\n", XMPP->chardata); } + uncomment for more verbosity */ if (!strcasecmp(el, "resource")) { if (XMPP->chardata_len > 0) { safestrncpy(XMPP->iq_client_resource, XMPP->chardata, sizeof XMPP->iq_client_resource); + striplt(XMPP->iq_client_resource); + } + } + + if (!strcasecmp(el, "username")) { /* NON SASL ONLY */ + if (XMPP->chardata_len > 0) { + safestrncpy(XMPP->iq_client_username, XMPP->chardata, + sizeof XMPP->iq_client_username); + striplt(XMPP->iq_client_username); + } + } + + if (!strcasecmp(el, "password")) { /* NON SASL ONLY */ + if (XMPP->chardata_len > 0) { + safestrncpy(XMPP->iq_client_password, XMPP->chardata, + sizeof XMPP->iq_client_password); + striplt(XMPP->iq_client_password); } } @@ -196,20 +248,34 @@ void xmpp_xml_end(void *data, const char *supplied_el) { } } + /* + * Non SASL authentication + */ + else if ( + (!strcasecmp(XMPP->iq_type, "set")) + && (!strcasecmp(XMPP->iq_query_xmlns, "jabber:iq:auth:query")) + ) { + + jabber_non_sasl_authenticate( + XMPP->iq_id, + XMPP->iq_client_username, + XMPP->iq_client_password, + XMPP->iq_client_resource + ); + } + /* * If this stanza was a "bind" attempt, process it ... */ - else if ( (!IsEmptyStr(XMPP->iq_id)) && (!IsEmptyStr(XMPP->iq_client_resource)) ) { + else if ( + (XMPP->bind_requested) + && (!IsEmptyStr(XMPP->iq_id)) + && (!IsEmptyStr(XMPP->iq_client_resource)) + && (CC->logged_in) + ) { /* 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, "%s/%s", CC->cs_inet_email, @@ -244,6 +310,7 @@ void xmpp_xml_end(void *data, const char *supplied_el) { XMPP->iq_client_resource[0] = 0; XMPP->iq_session = 0; XMPP->iq_query_xmlns[0] = 0; + XMPP->bind_requested = 0; } else if (!strcasecmp(el, "auth")) { @@ -267,6 +334,36 @@ void xmpp_xml_end(void *data, const char *supplied_el) { jabber_wholist_presence_dump(); } + else if ( (!strcasecmp(el, "body")) && (XMPP->html_tag_level == 0) ) { + if (XMPP->message_body != NULL) { + free(XMPP->message_body); + XMPP->message_body = NULL; + } + if (XMPP->chardata_len > 0) { + XMPP->message_body = strdup(XMPP->chardata); + } + } + + else if (!strcasecmp(el, "message")) { + jabber_send_message(XMPP->message_to, XMPP->message_body); + XMPP->html_tag_level = 0; + } + + else if (!strcasecmp(el, "html")) { + --XMPP->html_tag_level; + } + + else if (!strcasecmp(el, "starttls")) { +#ifdef HAVE_OPENSSL + cprintf(""); + CtdlModuleStartCryptoMsgs(NULL, NULL, NULL); + if (!CC->redirect_ssl) CC->kill_me = 1; +#else + cprintf(""); + CC->kill_me = 1; +#endif + } + XMPP->chardata_len = 0; if (XMPP->chardata_alloc > 0) { XMPP->chardata[0] = 0; @@ -300,12 +397,14 @@ void xmpp_cleanup_function(void) { /* Don't do this stuff if this is not a XMPP session! */ if (CC->h_command_function != xmpp_command_loop) return; - lprintf(CTDL_DEBUG, "Performing XMPP cleanup hook\n"); if (XMPP->chardata != NULL) { free(XMPP->chardata); XMPP->chardata = NULL; XMPP->chardata_len = 0; XMPP->chardata_alloc = 0; + if (XMPP->message_body != NULL) { + free(XMPP->message_body); + } } XML_ParserFree(XMPP->xp); free(XMPP); @@ -320,12 +419,13 @@ void xmpp_greeting(void) { strcpy(CC->cs_clientname, "Jabber session"); CC->session_specific_data = malloc(sizeof(struct citxmpp)); memset(XMPP, 0, sizeof(struct citxmpp)); + XMPP->last_event_processed = queue_event_seq; /* XMPP does not use a greeting, but we still have to initialize some things. */ XMPP->xp = XML_ParserCreateNS("UTF-8", ':'); if (XMPP->xp == NULL) { - lprintf(CTDL_ALERT, "Cannot create XML parser!\n"); + CtdlLogPrintf(CTDL_ALERT, "Cannot create XML parser!\n"); CC->kill_me = 1; return; } @@ -333,6 +433,8 @@ void xmpp_greeting(void) { XML_SetElementHandler(XMPP->xp, xmpp_xml_start, xmpp_xml_end); XML_SetCharacterDataHandler(XMPP->xp, xmpp_xml_chardata); // XML_SetUserData(XMPP->xp, something...); + + CC->can_receive_im = 1; /* This protocol is capable of receiving instant messages */ } @@ -347,7 +449,7 @@ void xmpp_command_loop(void) { memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */ retval = client_read(cmdbuf, 1); if (retval != 1) { - lprintf(CTDL_ERR, "Client disconnected: ending session.\r\n"); + CtdlLogPrintf(CTDL_ERR, "Client disconnected: ending session.\r\n"); CC->kill_me = 1; return; } @@ -362,6 +464,7 @@ void xmpp_command_loop(void) { * Async loop for XMPP sessions (handles the transmission of unsolicited stanzas) */ void xmpp_async_loop(void) { + xmpp_process_events(); jabber_output_incoming_messages(); } @@ -370,24 +473,24 @@ void xmpp_async_loop(void) { * Login hook for XMPP sessions */ void xmpp_login_hook(void) { + xmpp_queue_event(XMPP_EVT_LOGIN, CC->cs_inet_email); +} - // 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"); +/* + * Logout hook for XMPP sessions + */ +void xmpp_logout_hook(void) { + xmpp_queue_event(XMPP_EVT_LOGOUT, CC->cs_inet_email); } const char *CitadelServiceXMPP="XMPP"; -#endif /* HAVE_EXPAT */ - CTDL_MODULE_INIT(jabber) { -#ifdef HAVE_EXPAT if (!threading) { - CtdlRegisterServiceHook(5222, /* FIXME change to config.c_xmpp_port */ + CtdlRegisterServiceHook(config.c_xmpp_c2s_port, NULL, xmpp_greeting, xmpp_command_loop, @@ -395,10 +498,9 @@ CTDL_MODULE_INIT(jabber) 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 + CtdlRegisterSessionHook(xmpp_logout_hook, EVT_LOGOUT); + CtdlRegisterSessionHook(xmpp_login_hook, EVT_UNSTEALTH); + CtdlRegisterSessionHook(xmpp_logout_hook, EVT_STEALTH); } /* return our Subversion id for the Log */