From: Art Cancro Date: Mon, 22 Feb 2010 18:30:51 +0000 (+0000) Subject: * Renamed the 'jabber' module to 'xmpp' module. Also renamed functions starting... X-Git-Tag: v7.86~391 X-Git-Url: https://code.citadel.org/?p=citadel.git;a=commitdiff_plain;h=131b59b3ca3c8368c6ad179ef7fb9cc02e99ede4 * Renamed the 'jabber' module to 'xmpp' module. Also renamed functions starting with jabber_ to xmpp_ . Obviously I did *not* rename any of the XML namespaces because that would break the protocol. --- diff --git a/citadel/modules/jabber/serv_xmpp.c b/citadel/modules/jabber/serv_xmpp.c deleted file mode 100644 index 7f37e5acd..000000000 --- a/citadel/modules/jabber/serv_xmpp.c +++ /dev/null @@ -1,531 +0,0 @@ -/* - * $Id$ - * - * XMPP (Jabber) service for the Citadel system - * Copyright (c) 2007-2010 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" -#include -#include -#include -#include -#include -#include -#include -#include - -#if TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif - -#include -#include -#include -#include -#include -#include -#include "citadel.h" -#include "server.h" -#include "citserver.h" -#include "support.h" -#include "config.h" -#include "user_ops.h" -#include "policy.h" -#include "database.h" -#include "msgbase.h" -#include "internet_addressing.h" -#include "md5.h" -#include "ctdl_module.h" -#include "serv_xmpp.h" - -struct xmpp_event *xmpp_queue = NULL; - -/* We have just received a tag from the client, so send them ours */ - -void xmpp_stream_start(void *data, const char *supplied_el, const char **attr) -{ - while (*attr) { - if (!strcasecmp(attr[0], "to")) { - safestrncpy(XMPP->server_name, attr[1], sizeof XMPP->server_name); - } - attr += 2; - } - - cprintf(""); - - cprintf("server_name); - cprintf("id=\"%08x\" ", CC->cs_pid); - cprintf("version=\"1.0\" "); - cprintf("xmlns:stream=\"http://etherx.jabber.org/streams\" "); - cprintf("xmlns=\"jabber:client\">"); - - /* 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(); - - /* 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 */ -} - - -void xmpp_xml_start(void *data, const char *supplied_el, const char **attr) { - char el[256]; - char *sep = NULL; - int i; - - /* Axe the namespace, we don't care about it */ - safestrncpy(el, supplied_el, sizeof el); - while (sep = strchr(el, ':'), sep) { - strcpy(el, ++sep); - } - - /* - CtdlLogPrintf(CTDL_DEBUG, "XMPP ELEMENT START: <%s>\n", el); - for (i=0; attr[i] != NULL; i+=2) { - 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); - } - - 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, "bind")) { - XMPP->bind_requested = 1; - } - - else if (!strcasecmp(el, "iq")) { - for (i=0; attr[i] != NULL; i+=2) { - 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, "auth")) { - XMPP->sasl_auth_mech[0] = 0; - for (i=0; attr[i] != NULL; i+=2) { - if (!strcasecmp(attr[i], "mechanism")) { - safestrncpy(XMPP->sasl_auth_mech, attr[i+1], sizeof XMPP->sasl_auth_mech); - } - } - } - - 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; - } -} - - - -void xmpp_xml_end(void *data, const char *supplied_el) { - char el[256]; - char *sep = NULL; - - /* Axe the namespace, we don't care about it */ - safestrncpy(el, supplied_el, sizeof el); - while (sep = strchr(el, ':'), sep) { - strcpy(el, ++sep); - } - - /* - CtdlLogPrintf(CTDL_DEBUG, "XMPP ELEMENT END : <%s>\n", el); - if (XMPP->chardata_len > 0) { - 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); - } - } - - 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); - } - - /* - * ping ( http://xmpp.org/extensions/xep-0199.html ) - */ - else if (XMPP->ping_requested) { - cprintf("iq_from)) { - cprintf("to=\"%s\" ", XMPP->iq_from); - } - if (!IsEmptyStr(XMPP->iq_to)) { - cprintf("from=\"%s\" ", XMPP->iq_to); - } - cprintf("id=\"%s\"/>", XMPP->iq_id); - } - - /* - * Unknown queries ... return the XML equivalent of a blank stare - */ - else { - CtdlLogPrintf(CTDL_DEBUG, "Unknown query; \n"); - cprintf("", XMPP->iq_id); - cprintf("" - "" - "" - ); - cprintf(""); - } - } - - /* - * 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 ( - (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, - "%s/%s", - CC->cs_inet_email, - XMPP->iq_client_resource - ); - - /* Tell the client what its JID is */ - - cprintf("", XMPP->iq_id); - cprintf(""); - cprintf("%s", XMPP->client_jid); - cprintf(""); - cprintf(""); - } - - else if (XMPP->iq_session) { - cprintf("", XMPP->iq_id); - cprintf(""); - } - - else { - cprintf("", XMPP->iq_id); - cprintf(""); - cprintf(""); - } - - /* Now clear these fields out so they don't get used by a future stanza */ - 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; - XMPP->bind_requested = 0; - XMPP->ping_requested = 0; - } - - else if (!strcasecmp(el, "auth")) { - - /* Try to authenticate (this function is responsible for the output stanza) */ - xmpp_sasl_auth(XMPP->sasl_auth_mech, (XMPP->chardata != NULL ? XMPP->chardata : "") ); - - /* Now clear these fields out so they don't get used by a future stanza */ - XMPP->sasl_auth_mech[0] = 0; - } - - else if (!strcasecmp(el, "session")) { - XMPP->iq_session = 1; - } - - else if (!strcasecmp(el, "presence")) { - - /* Respond to a update by firing back with presence information - * on the entire wholist. Check this assumption, it's probably wrong. - */ - 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 - } - - else if (!strcasecmp(el, "ping")) { - XMPP->ping_requested = 1; - } - - XMPP->chardata_len = 0; - if (XMPP->chardata_alloc > 0) { - XMPP->chardata[0] = 0; - } -} - - -void xmpp_xml_chardata(void *data, const XML_Char *s, int len) -{ - struct citxmpp *X = XMPP; - - if (X->chardata_alloc == 0) { - X->chardata_alloc = SIZ; - X->chardata = malloc(X->chardata_alloc); - } - if ((X->chardata_len + len + 1) > X->chardata_alloc) { - X->chardata_alloc = X->chardata_len + len + 1024; - X->chardata = realloc(X->chardata, X->chardata_alloc); - } - memcpy(&X->chardata[X->chardata_len], s, len); - X->chardata_len += len; - X->chardata[X->chardata_len] = 0; -} - - -/* - * This cleanup function blows away the temporary memory and files used by the XMPP service. - */ -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; - - 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); -} - - - -/* - * Here's where our XMPP session begins its happy day. - */ -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) { - CtdlLogPrintf(CTDL_ALERT, "Cannot create XML parser!\n"); - CC->kill_me = 1; - return; - } - - 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 */ -} - - -/* - * Main command loop for XMPP sessions. - */ -void xmpp_command_loop(void) { - char cmdbuf[16]; - int retval; - - time(&CC->lastcmd); - memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */ - retval = client_read(cmdbuf, 1); - if (retval != 1) { - CtdlLogPrintf(CTDL_ERR, "Client disconnected: ending session.\r\n"); - CC->kill_me = 1; - return; - } - - /* FIXME ... this is woefully inefficient. */ - - XML_Parse(XMPP->xp, cmdbuf, 1, 0); -} - - -/* - * Async loop for XMPP sessions (handles the transmission of unsolicited stanzas) - */ -void xmpp_async_loop(void) { - xmpp_process_events(); - jabber_output_incoming_messages(); -} - - -/* - * Login hook for XMPP sessions - */ -void xmpp_login_hook(void) { - xmpp_queue_event(XMPP_EVT_LOGIN, CC->cs_inet_email); -} - - -/* - * Logout hook for XMPP sessions - */ -void xmpp_logout_hook(void) { - xmpp_queue_event(XMPP_EVT_LOGOUT, CC->cs_inet_email); -} - - -const char *CitadelServiceXMPP="XMPP"; - -CTDL_MODULE_INIT(jabber) -{ - if (!threading) { - CtdlRegisterServiceHook(config.c_xmpp_c2s_port, - NULL, - xmpp_greeting, - xmpp_command_loop, - xmpp_async_loop, - CitadelServiceXMPP); - CtdlRegisterSessionHook(xmpp_cleanup_function, EVT_STOP); - CtdlRegisterSessionHook(xmpp_login_hook, EVT_LOGIN); - 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 */ - return "$Id$"; -} diff --git a/citadel/modules/jabber/serv_xmpp.h b/citadel/modules/jabber/serv_xmpp.h deleted file mode 100644 index f7d9e1452..000000000 --- a/citadel/modules/jabber/serv_xmpp.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * $Id$ - * - * 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 - */ - -struct citxmpp { /* Information about the current session */ - XML_Parser xp; /* XML parser instance for incoming client stream */ - char server_name[256]; /* who they think we are */ - char *chardata; - int chardata_len; - int chardata_alloc; - char client_jid[256]; /* "full JID" of the client */ - int last_event_processed; - - char iq_type[256]; /* for stanzas */ - char iq_id[256]; - char iq_from[256]; - char iq_to[256]; - char iq_client_username[256]; /* username requested by the client (NON SASL ONLY) */ - char iq_client_password[256]; /* password requested by the client (NON SASL ONLY) */ - char iq_client_resource[256]; /* resource name requested by the client */ - int iq_session; /* nonzero == client is requesting a session */ - char iq_query_xmlns[256]; /* Namespace of */ - - char sasl_auth_mech[32]; /* SASL auth mechanism requested by the client */ - - char message_to[256]; - char *message_body; /* Message body in transit */ - int html_tag_level; /* tag nesting level */ - - int bind_requested; /* In this stanza, client is asking server to bind a resource. */ - int ping_requested; /* In this stanza, client is pinging the server. */ -}; - -#define XMPP ((struct citxmpp *)CC->session_specific_data) - -struct xmpp_event { - struct xmpp_event *next; - int event_seq; - time_t event_time; - int event_type; - char event_jid[256]; - int session_which_generated_this_event; -}; - -extern struct xmpp_event *xmpp_queue; -extern int queue_event_seq; - -enum { - XMPP_EVT_LOGIN, - XMPP_EVT_LOGOUT -}; - -void xmpp_cleanup_function(void); -void xmpp_greeting(void); -void xmpp_command_loop(void); -void xmpp_async_loop(void); -void xmpp_sasl_auth(char *, char *); -void xmpp_output_auth_mechs(void); -void xmpp_query_namespace(char *, char *, char *, char *); -void jabber_wholist_presence_dump(void); -void jabber_output_incoming_messages(void); -void xmpp_queue_event(int, char *); -void xmpp_process_events(void); -void xmpp_presence_notify(char *, int); -void jabber_roster_item(struct CitContext *); -void jabber_send_message(char *, char *); -void jabber_non_sasl_authenticate(char *, char *, char *, char *); diff --git a/citadel/modules/jabber/xmpp_messages.c b/citadel/modules/jabber/xmpp_messages.c deleted file mode 100644 index 405856e92..000000000 --- a/citadel/modules/jabber/xmpp_messages.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * $Id$ - * - * Handle messages sent and received using XMPP (Jabber) protocol - * - * 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" -#include -#include -#include -#include -#include -#include -#include -#include - -#if TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif - -#include -#include -#include -#include -#include -#include -#include "citadel.h" -#include "server.h" -#include "citserver.h" -#include "support.h" -#include "config.h" -#include "internet_addressing.h" -#include "md5.h" -#include "ctdl_module.h" -#include "serv_xmpp.h" - - -/* - * 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 jabber_output_incoming_messages(void) { - - struct ExpressMessage *ptr; - - 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("", - XMPP->client_jid, - ptr->sender_email); - if (ptr->text != NULL) { - striplt(ptr->text); - cprintf("%s", ptr->text); - free(ptr->text); - } - cprintf(""); - free(ptr); - } -} - -/* - * Client is sending a message. - */ -void jabber_send_message(char *message_to, char *message_body) { - char *recp = NULL; - int message_sent = 0; - struct CitContext *cptr; - - if (message_body == NULL) return; - if (message_to == NULL) return; - if (IsEmptyStr(message_to)) return; - if (!CC->logged_in) return; - - for (cptr = ContextList; cptr != NULL; cptr = cptr->next) { - if ( (cptr->logged_in) - && (cptr->can_receive_im) - && (!strcasecmp(cptr->cs_inet_email, message_to)) - ) { - recp = cptr->user.fullname; - } - } - - if (recp) { - message_sent = PerformXmsgHooks(CC->user.fullname, CC->cs_inet_email, recp, message_body); - } - - free(XMPP->message_body); - XMPP->message_body = NULL; - XMPP->message_to[0] = 0; - time(&CC->lastidle); -} - diff --git a/citadel/modules/jabber/xmpp_presence.c b/citadel/modules/jabber/xmpp_presence.c deleted file mode 100644 index 96b4504b8..000000000 --- a/citadel/modules/jabber/xmpp_presence.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * $Id$ - * - * Handle XMPP presence exchanges - * - * 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" -#include -#include -#include -#include -#include -#include -#include -#include - -#if TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif - -#include -#include -#include -#include -#include -#include -#include "citadel.h" -#include "server.h" -#include "citserver.h" -#include "support.h" -#include "config.h" -#include "internet_addressing.h" -#include "md5.h" -#include "ctdl_module.h" -#include "serv_xmpp.h" - - -/* - * Initial dump of the entire wholist - */ -void jabber_wholist_presence_dump(void) -{ - struct CitContext *cptr = NULL; - int nContexts, i; - - int aide = (CC->user.axlevel >= 6); - - cptr = CtdlGetContextArray(&nContexts); - if (!cptr) - return ; /** FIXME: Does jabber need to send something to maintain the protocol? */ - - for (i=0; iuser.usernum) /* don't show myself */ - && (cptr[i].can_receive_im) /* IM-capable session */ - ) { - cprintf("", - cptr[i].cs_inet_email); - } - } - } - free(cptr); -} - - - -/* - * When a user logs in or out of the local Citadel system, notify all Jabber sessions - * about it. - */ -void xmpp_presence_notify(char *presence_jid, int event_type) { - struct CitContext *cptr; - static int unsolicited_id; - int visible_sessions = 0; - int nContexts, i; - int aide = (CC->user.axlevel >= 6); - - if (IsEmptyStr(presence_jid)) return; - - cptr = CtdlGetContextArray(&nContexts); - if (!cptr) - return ; /** FIXME: Does jabber need to send something to maintain the protocol? */ - - /* Count the visible sessions for this user */ - for (i=0; i are now visible to session %d\n", - visible_sessions, presence_jid, CC->cs_pid); - - if ( (event_type == XMPP_EVT_LOGIN) && (visible_sessions == 1) ) { - - CtdlLogPrintf(CTDL_DEBUG, "Telling session %d that <%s> logged in\n", CC->cs_pid, presence_jid); - - /* Do an unsolicited roster update that adds a new contact. */ - for (i=0; i", - ++unsolicited_id); - cprintf(""); - jabber_roster_item(&cptr[i]); - cprintf("" - ""); - } - } - } - - /* Transmit presence information */ - cprintf("", presence_jid); - } - - if (visible_sessions == 0) { - CtdlLogPrintf(CTDL_DEBUG, "Telling session %d that <%s> logged out\n", CC->cs_pid, presence_jid); - - /* Transmit non-presence information */ - cprintf("", presence_jid); - cprintf("", presence_jid); - - /* Do an unsolicited roster update that deletes the contact. */ - cprintf("", ++unsolicited_id); - cprintf(""); - cprintf("", presence_jid); - cprintf("%s", config.c_humannode); - cprintf(""); - cprintf("" - ""); - } - free(cptr); -} diff --git a/citadel/modules/jabber/xmpp_query_namespace.c b/citadel/modules/jabber/xmpp_query_namespace.c deleted file mode 100644 index 64aba16c5..000000000 --- a/citadel/modules/jabber/xmpp_query_namespace.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * $Id$ - * - * Handle type situations (namespace queries) - * - * 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" -#include -#include -#include -#include -#include -#include -#include -#include - -#if TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif - -#include -#include -#include -#include -#include -#include -#include "citadel.h" -#include "server.h" -#include "citserver.h" -#include "support.h" -#include "config.h" -#include "internet_addressing.h" -#include "md5.h" -#include "ctdl_module.h" -#include "serv_xmpp.h" - - -/* - * Output a single roster item, for roster queries or pushes - */ -void jabber_roster_item(struct CitContext *cptr) { - cprintf("", - cptr->cs_inet_email, - cptr->user.fullname - ); - cprintf("%s", config.c_humannode); - cprintf(""); -} - -/* - * Return the results for a "jabber:iq:roster:query" - * - * Since we are not yet managing a roster, we simply return the entire wholist - * (minus any entries for this user -- don't tell me about myself) - * - */ -void jabber_iq_roster_query(void) -{ - struct CitContext *cptr; - int nContexts, i; - int aide = (CC->user.axlevel >= 6); - - cprintf(""); - - cptr = CtdlGetContextArray(&nContexts); - if (cptr) { - for (i=0; iuser.usernum) - ) { - jabber_roster_item(&cptr[i]); - } - } - } - free (cptr); - } - cprintf(""); -} - - -/* - * TODO: handle queries on some or all of these namespaces - * -xmpp_query_namespace(purple5b5c1e58, splorph.xand.com, http://jabber.org/protocol/disco#items:query) -xmpp_query_namespace(purple5b5c1e59, splorph.xand.com, http://jabber.org/protocol/disco#info:query) -xmpp_query_namespace(purple5b5c1e5a, , vcard-temp:query) - * - */ - -void xmpp_query_namespace(char *iq_id, char *iq_from, char *iq_to, char *query_xmlns) -{ - int supported_namespace = 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")) - ) { - supported_namespace = 1; - } - - CtdlLogPrintf(CTDL_DEBUG, "xmpp_query_namespace(%s, %s, %s, %s)\n", iq_id, iq_from, iq_to, query_xmlns); - - /* - * Beginning of query result. - */ - if (supported_namespace) { - cprintf("", iq_id); - - /* - * Is this a query we know how to handle? - */ - - if (!strcasecmp(query_xmlns, "jabber:iq:roster:query")) { - jabber_iq_roster_query(); - } - - else if (!strcasecmp(query_xmlns, "jabber:iq:auth:query")) { - cprintf("" - "" - "" - ); - } - - else { - CtdlLogPrintf(CTDL_DEBUG, "Unknown namespace; returning \n"); - cprintf("" - "" - "" - ); - } - - /* - * End of query result. If we didn't hit any known namespaces then we should - * deliver a "service unavailable" error (see RFC3921 section 2.4 and 11.1.5.4) - */ - cprintf(""); -} diff --git a/citadel/modules/jabber/xmpp_queue.c b/citadel/modules/jabber/xmpp_queue.c deleted file mode 100644 index 3bd8c95d7..000000000 --- a/citadel/modules/jabber/xmpp_queue.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - * $Id$ - * - * XMPP event queue - * - * 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" -#include -#include -#include -#include -#include -#include -#include -#include - -#if TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif - -#include -#include -#include -#include -#include -#include -#include "citadel.h" -#include "server.h" -#include "citserver.h" -#include "support.h" -#include "config.h" -#include "internet_addressing.h" -#include "md5.h" -#include "ctdl_module.h" -#include "serv_xmpp.h" - -int queue_event_seq = 0; - -void xmpp_queue_event(int event_type, char *email_addr) { - - struct xmpp_event *xptr = NULL; - struct xmpp_event *new_event = NULL; - struct xmpp_event *last = NULL; - int purged_something = 0; - struct CitContext *cptr; - - CtdlLogPrintf(CTDL_DEBUG, "xmpp_queue_event(%d, %s)\n", event_type, email_addr); - - /* Purge events more than a minute old */ - begin_critical_section(S_XMPP_QUEUE); - do { - purged_something = 0; - if (xmpp_queue != NULL) { - if ((time(NULL) - xmpp_queue->event_time) > 60) { - xptr = xmpp_queue->next; - free(xmpp_queue); - xmpp_queue = xptr; - purged_something = 1; - } - } - } while(purged_something); - end_critical_section(S_XMPP_QUEUE); - - /* Create a new event */ - new_event = (struct xmpp_event *) malloc(sizeof(struct xmpp_event)); - new_event->next = NULL; - new_event->event_time = time(NULL); - new_event->event_seq = ++queue_event_seq; - new_event->event_type = event_type; - new_event->session_which_generated_this_event = CC->cs_pid; - safestrncpy(new_event->event_jid, email_addr, sizeof new_event->event_jid); - - /* Add it to the list */ - begin_critical_section(S_XMPP_QUEUE); - if (xmpp_queue == NULL) { - xmpp_queue = new_event; - } - else { - for (xptr = xmpp_queue; xptr != NULL; xptr = xptr->next) { - if (xptr->next == NULL) { - last = xptr; - } - } - last->next = new_event; - } - end_critical_section(S_XMPP_QUEUE); - - /* Tell the sessions that something is happening */ - begin_critical_section(S_SESSION_TABLE); - for (cptr = ContextList; cptr != NULL; cptr = cptr->next) { - if ((cptr->logged_in) && (cptr->h_async_function == xmpp_async_loop)) { - cptr->async_waiting = 1; - } - } - end_critical_section(S_SESSION_TABLE); -} - - -/* - * Are we interested in anything from the queue? (Called in async loop) - */ -void xmpp_process_events(void) { - struct xmpp_event *xptr = NULL; - int highest_event = 0; - - for (xptr=xmpp_queue; xptr!=NULL; xptr=xptr->next) { - if (xptr->event_seq > XMPP->last_event_processed) { - - switch(xptr->event_type) { - - case XMPP_EVT_LOGIN: - case XMPP_EVT_LOGOUT: - if (xptr->session_which_generated_this_event != CC->cs_pid) { - xmpp_presence_notify(xptr->event_jid, xptr->event_type); - } - break; - } - - if (xptr->event_seq > highest_event) { - highest_event = xptr->event_seq; - } - } - } - - XMPP->last_event_processed = highest_event; -} diff --git a/citadel/modules/jabber/xmpp_sasl_service.c b/citadel/modules/jabber/xmpp_sasl_service.c deleted file mode 100644 index 1391512c4..000000000 --- a/citadel/modules/jabber/xmpp_sasl_service.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * $Id$ - * - * Barebones SASL authentication service for XMPP (Jabber) clients. - * - * Note: RFC3920 says we "must" support DIGEST-MD5 but we only support PLAIN. - * - * 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" -#include -#include -#include -#include -#include -#include -#include -#include - -#if TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif - -#include -#include -#include -#include -#include -#include -#include "citadel.h" -#include "server.h" -#include "citserver.h" -#include "support.h" -#include "config.h" -#include "user_ops.h" -#include "internet_addressing.h" -#include "md5.h" -#include "ctdl_module.h" -#include "serv_xmpp.h" - - -/* - * PLAIN authentication. Returns zero on success, nonzero on failure. - */ -int xmpp_auth_plain(char *authstring) -{ - char decoded_authstring[1024]; - char ident[256]; - char user[256]; - char pass[256]; - int result; - - - /* Take apart the authentication string */ - memset(pass, 0, sizeof(pass)); - - CtdlDecodeBase64(decoded_authstring, authstring, strlen(authstring)); - safestrncpy(ident, decoded_authstring, sizeof ident); - safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user); - safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass); - - - /* If there are underscores in either string, change them to spaces. Some clients - * do not allow spaces so we can tell the user to substitute underscores if their - * login name contains spaces. - */ - convert_spaces_to_underscores(ident); - convert_spaces_to_underscores(user); - - /* Now attempt authentication */ - - if (!IsEmptyStr(ident)) { - result = CtdlLoginExistingUser(user, ident); - } - else { - result = CtdlLoginExistingUser(NULL, user); - } - - if (result == login_ok) { - if (CtdlTryPassword(pass) == pass_ok) { - return(0); /* success */ - } - } - - return(1); /* failure */ -} - - -/* - * Output the list of SASL mechanisms offered by this stream. - */ -void xmpp_output_auth_mechs(void) { - cprintf(""); - cprintf("PLAIN"); - cprintf(""); -} - -/* - * Here we go ... client is trying to authenticate. - */ -void xmpp_sasl_auth(char *sasl_auth_mech, char *authstring) { - - if (strcasecmp(sasl_auth_mech, "PLAIN")) { - cprintf(""); - cprintf(""); - cprintf(""); - return; - } - - if (CC->logged_in) CtdlUserLogout(); /* Client may try to log in twice. Handle this. */ - - if (CC->nologin) { - cprintf(""); - cprintf(""); - cprintf(""); - } - - else if (xmpp_auth_plain(authstring) == 0) { - cprintf(""); - } - - else { - cprintf(""); - cprintf(""); - cprintf(""); - } -} - - - -/* - * Non-SASL authentication - */ -void jabber_non_sasl_authenticate(char *iq_id, char *username, char *password, char *resource) { - int result; - - if (CC->logged_in) CtdlUserLogout(); /* Client may try to log in twice. Handle this. */ - - result = CtdlLoginExistingUser(NULL, username); - if (result == login_ok) { - result = CtdlTryPassword(password); - if (result == pass_ok) { - cprintf("", iq_id); /* success */ - return; - } - } - - /* failure */ - cprintf("", iq_id); - cprintf("" - "" - "" - "" - ); -} diff --git a/citadel/modules/xmpp/serv_xmpp.c b/citadel/modules/xmpp/serv_xmpp.c new file mode 100644 index 000000000..0b7da46e9 --- /dev/null +++ b/citadel/modules/xmpp/serv_xmpp.c @@ -0,0 +1,531 @@ +/* + * $Id$ + * + * XMPP (Jabber) service for the Citadel system + * Copyright (c) 2007-2010 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" +#include +#include +#include +#include +#include +#include +#include +#include + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#include +#include +#include +#include +#include +#include +#include "citadel.h" +#include "server.h" +#include "citserver.h" +#include "support.h" +#include "config.h" +#include "user_ops.h" +#include "policy.h" +#include "database.h" +#include "msgbase.h" +#include "internet_addressing.h" +#include "md5.h" +#include "ctdl_module.h" +#include "serv_xmpp.h" + +struct xmpp_event *xmpp_queue = NULL; + +/* We have just received a tag from the client, so send them ours */ + +void xmpp_stream_start(void *data, const char *supplied_el, const char **attr) +{ + while (*attr) { + if (!strcasecmp(attr[0], "to")) { + safestrncpy(XMPP->server_name, attr[1], sizeof XMPP->server_name); + } + attr += 2; + } + + cprintf(""); + + cprintf("server_name); + cprintf("id=\"%08x\" ", CC->cs_pid); + cprintf("version=\"1.0\" "); + cprintf("xmlns:stream=\"http://etherx.jabber.org/streams\" "); + cprintf("xmlns=\"jabber:client\">"); + + /* 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(); + + /* 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 */ +} + + +void xmpp_xml_start(void *data, const char *supplied_el, const char **attr) { + char el[256]; + char *sep = NULL; + int i; + + /* Axe the namespace, we don't care about it */ + safestrncpy(el, supplied_el, sizeof el); + while (sep = strchr(el, ':'), sep) { + strcpy(el, ++sep); + } + + /* + CtdlLogPrintf(CTDL_DEBUG, "XMPP ELEMENT START: <%s>\n", el); + for (i=0; attr[i] != NULL; i+=2) { + 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); + } + + 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, "bind")) { + XMPP->bind_requested = 1; + } + + else if (!strcasecmp(el, "iq")) { + for (i=0; attr[i] != NULL; i+=2) { + 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, "auth")) { + XMPP->sasl_auth_mech[0] = 0; + for (i=0; attr[i] != NULL; i+=2) { + if (!strcasecmp(attr[i], "mechanism")) { + safestrncpy(XMPP->sasl_auth_mech, attr[i+1], sizeof XMPP->sasl_auth_mech); + } + } + } + + 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; + } +} + + + +void xmpp_xml_end(void *data, const char *supplied_el) { + char el[256]; + char *sep = NULL; + + /* Axe the namespace, we don't care about it */ + safestrncpy(el, supplied_el, sizeof el); + while (sep = strchr(el, ':'), sep) { + strcpy(el, ++sep); + } + + /* + CtdlLogPrintf(CTDL_DEBUG, "XMPP ELEMENT END : <%s>\n", el); + if (XMPP->chardata_len > 0) { + 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); + } + } + + 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); + } + + /* + * ping ( http://xmpp.org/extensions/xep-0199.html ) + */ + else if (XMPP->ping_requested) { + cprintf("iq_from)) { + cprintf("to=\"%s\" ", XMPP->iq_from); + } + if (!IsEmptyStr(XMPP->iq_to)) { + cprintf("from=\"%s\" ", XMPP->iq_to); + } + cprintf("id=\"%s\"/>", XMPP->iq_id); + } + + /* + * Unknown queries ... return the XML equivalent of a blank stare + */ + else { + CtdlLogPrintf(CTDL_DEBUG, "Unknown query; \n"); + cprintf("", XMPP->iq_id); + cprintf("" + "" + "" + ); + cprintf(""); + } + } + + /* + * Non SASL authentication + */ + else if ( + (!strcasecmp(XMPP->iq_type, "set")) + && (!strcasecmp(XMPP->iq_query_xmlns, "jabber:iq:auth:query")) + ) { + + xmpp_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 ( + (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, + "%s/%s", + CC->cs_inet_email, + XMPP->iq_client_resource + ); + + /* Tell the client what its JID is */ + + cprintf("", XMPP->iq_id); + cprintf(""); + cprintf("%s", XMPP->client_jid); + cprintf(""); + cprintf(""); + } + + else if (XMPP->iq_session) { + cprintf("", XMPP->iq_id); + cprintf(""); + } + + else { + cprintf("", XMPP->iq_id); + cprintf(""); + cprintf(""); + } + + /* Now clear these fields out so they don't get used by a future stanza */ + 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; + XMPP->bind_requested = 0; + XMPP->ping_requested = 0; + } + + else if (!strcasecmp(el, "auth")) { + + /* Try to authenticate (this function is responsible for the output stanza) */ + xmpp_sasl_auth(XMPP->sasl_auth_mech, (XMPP->chardata != NULL ? XMPP->chardata : "") ); + + /* Now clear these fields out so they don't get used by a future stanza */ + XMPP->sasl_auth_mech[0] = 0; + } + + else if (!strcasecmp(el, "session")) { + XMPP->iq_session = 1; + } + + else if (!strcasecmp(el, "presence")) { + + /* Respond to a update by firing back with presence information + * on the entire wholist. Check this assumption, it's probably wrong. + */ + xmpp_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")) { + xmpp_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 + } + + else if (!strcasecmp(el, "ping")) { + XMPP->ping_requested = 1; + } + + XMPP->chardata_len = 0; + if (XMPP->chardata_alloc > 0) { + XMPP->chardata[0] = 0; + } +} + + +void xmpp_xml_chardata(void *data, const XML_Char *s, int len) +{ + struct citxmpp *X = XMPP; + + if (X->chardata_alloc == 0) { + X->chardata_alloc = SIZ; + X->chardata = malloc(X->chardata_alloc); + } + if ((X->chardata_len + len + 1) > X->chardata_alloc) { + X->chardata_alloc = X->chardata_len + len + 1024; + X->chardata = realloc(X->chardata, X->chardata_alloc); + } + memcpy(&X->chardata[X->chardata_len], s, len); + X->chardata_len += len; + X->chardata[X->chardata_len] = 0; +} + + +/* + * This cleanup function blows away the temporary memory and files used by the XMPP service. + */ +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; + + 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); +} + + + +/* + * Here's where our XMPP session begins its happy day. + */ +void xmpp_greeting(void) { + strcpy(CC->cs_clientname, "XMPP 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) { + CtdlLogPrintf(CTDL_ALERT, "Cannot create XML parser!\n"); + CC->kill_me = 1; + return; + } + + 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 */ +} + + +/* + * Main command loop for XMPP sessions. + */ +void xmpp_command_loop(void) { + char cmdbuf[16]; + int retval; + + time(&CC->lastcmd); + memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */ + retval = client_read(cmdbuf, 1); + if (retval != 1) { + CtdlLogPrintf(CTDL_ERR, "Client disconnected: ending session.\r\n"); + CC->kill_me = 1; + return; + } + + /* FIXME ... this is woefully inefficient. */ + + XML_Parse(XMPP->xp, cmdbuf, 1, 0); +} + + +/* + * Async loop for XMPP sessions (handles the transmission of unsolicited stanzas) + */ +void xmpp_async_loop(void) { + xmpp_process_events(); + xmpp_output_incoming_messages(); +} + + +/* + * Login hook for XMPP sessions + */ +void xmpp_login_hook(void) { + xmpp_queue_event(XMPP_EVT_LOGIN, CC->cs_inet_email); +} + + +/* + * Logout hook for XMPP sessions + */ +void xmpp_logout_hook(void) { + xmpp_queue_event(XMPP_EVT_LOGOUT, CC->cs_inet_email); +} + + +const char *CitadelServiceXMPP="XMPP"; + +CTDL_MODULE_INIT(xmpp) +{ + if (!threading) { + CtdlRegisterServiceHook(config.c_xmpp_c2s_port, + NULL, + xmpp_greeting, + xmpp_command_loop, + xmpp_async_loop, + CitadelServiceXMPP); + CtdlRegisterSessionHook(xmpp_cleanup_function, EVT_STOP); + CtdlRegisterSessionHook(xmpp_login_hook, EVT_LOGIN); + 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 */ + return "$Id$"; +} diff --git a/citadel/modules/xmpp/serv_xmpp.h b/citadel/modules/xmpp/serv_xmpp.h new file mode 100644 index 000000000..dedc599ef --- /dev/null +++ b/citadel/modules/xmpp/serv_xmpp.h @@ -0,0 +1,83 @@ +/* + * $Id$ + * + * 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 + */ + +struct citxmpp { /* Information about the current session */ + XML_Parser xp; /* XML parser instance for incoming client stream */ + char server_name[256]; /* who they think we are */ + char *chardata; + int chardata_len; + int chardata_alloc; + char client_jid[256]; /* "full JID" of the client */ + int last_event_processed; + + char iq_type[256]; /* for stanzas */ + char iq_id[256]; + char iq_from[256]; + char iq_to[256]; + char iq_client_username[256]; /* username requested by the client (NON SASL ONLY) */ + char iq_client_password[256]; /* password requested by the client (NON SASL ONLY) */ + char iq_client_resource[256]; /* resource name requested by the client */ + int iq_session; /* nonzero == client is requesting a session */ + char iq_query_xmlns[256]; /* Namespace of */ + + char sasl_auth_mech[32]; /* SASL auth mechanism requested by the client */ + + char message_to[256]; + char *message_body; /* Message body in transit */ + int html_tag_level; /* tag nesting level */ + + int bind_requested; /* In this stanza, client is asking server to bind a resource. */ + int ping_requested; /* In this stanza, client is pinging the server. */ +}; + +#define XMPP ((struct citxmpp *)CC->session_specific_data) + +struct xmpp_event { + struct xmpp_event *next; + int event_seq; + time_t event_time; + int event_type; + char event_jid[256]; + int session_which_generated_this_event; +}; + +extern struct xmpp_event *xmpp_queue; +extern int queue_event_seq; + +enum { + XMPP_EVT_LOGIN, + XMPP_EVT_LOGOUT +}; + +void xmpp_cleanup_function(void); +void xmpp_greeting(void); +void xmpp_command_loop(void); +void xmpp_async_loop(void); +void xmpp_sasl_auth(char *, char *); +void xmpp_output_auth_mechs(void); +void xmpp_query_namespace(char *, char *, char *, char *); +void xmpp_wholist_presence_dump(void); +void xmpp_output_incoming_messages(void); +void xmpp_queue_event(int, char *); +void xmpp_process_events(void); +void xmpp_presence_notify(char *, int); +void xmpp_roster_item(struct CitContext *); +void xmpp_send_message(char *, char *); +void xmpp_non_sasl_authenticate(char *, char *, char *, char *); diff --git a/citadel/modules/xmpp/xmpp_messages.c b/citadel/modules/xmpp/xmpp_messages.c new file mode 100644 index 000000000..5ea435d4a --- /dev/null +++ b/citadel/modules/xmpp/xmpp_messages.c @@ -0,0 +1,122 @@ +/* + * $Id$ + * + * Handle messages sent and received using XMPP (Jabber) protocol + * + * 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" +#include +#include +#include +#include +#include +#include +#include +#include + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#include +#include +#include +#include +#include +#include +#include "citadel.h" +#include "server.h" +#include "citserver.h" +#include "support.h" +#include "config.h" +#include "internet_addressing.h" +#include "md5.h" +#include "ctdl_module.h" +#include "serv_xmpp.h" + + +/* + * 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; + + 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("", + XMPP->client_jid, + ptr->sender_email); + if (ptr->text != NULL) { + striplt(ptr->text); + cprintf("%s", ptr->text); + free(ptr->text); + } + cprintf(""); + free(ptr); + } +} + +/* + * Client is sending a message. + */ +void xmpp_send_message(char *message_to, char *message_body) { + char *recp = NULL; + int message_sent = 0; + struct CitContext *cptr; + + if (message_body == NULL) return; + if (message_to == NULL) return; + if (IsEmptyStr(message_to)) return; + if (!CC->logged_in) return; + + for (cptr = ContextList; cptr != NULL; cptr = cptr->next) { + if ( (cptr->logged_in) + && (cptr->can_receive_im) + && (!strcasecmp(cptr->cs_inet_email, message_to)) + ) { + recp = cptr->user.fullname; + } + } + + if (recp) { + message_sent = PerformXmsgHooks(CC->user.fullname, CC->cs_inet_email, recp, message_body); + } + + free(XMPP->message_body); + XMPP->message_body = NULL; + XMPP->message_to[0] = 0; + time(&CC->lastidle); +} + diff --git a/citadel/modules/xmpp/xmpp_presence.c b/citadel/modules/xmpp/xmpp_presence.c new file mode 100644 index 000000000..a60580fae --- /dev/null +++ b/citadel/modules/xmpp/xmpp_presence.c @@ -0,0 +1,164 @@ +/* + * $Id$ + * + * Handle XMPP presence exchanges + * + * 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" +#include +#include +#include +#include +#include +#include +#include +#include + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#include +#include +#include +#include +#include +#include +#include "citadel.h" +#include "server.h" +#include "citserver.h" +#include "support.h" +#include "config.h" +#include "internet_addressing.h" +#include "md5.h" +#include "ctdl_module.h" +#include "serv_xmpp.h" + + +/* + * Initial dump of the entire wholist + */ +void xmpp_wholist_presence_dump(void) +{ + struct CitContext *cptr = NULL; + int nContexts, i; + + int aide = (CC->user.axlevel >= 6); + + cptr = CtdlGetContextArray(&nContexts); + if (!cptr) + return ; /** FIXME: Does xmpp need to send something to maintain the protocol? */ + + for (i=0; iuser.usernum) /* don't show myself */ + && (cptr[i].can_receive_im) /* IM-capable session */ + ) { + cprintf("", + cptr[i].cs_inet_email); + } + } + } + free(cptr); +} + + + +/* + * When a user logs in or out of the local Citadel system, notify all XMPP sessions about it. + */ +void xmpp_presence_notify(char *presence_jid, int event_type) { + struct CitContext *cptr; + static int unsolicited_id; + int visible_sessions = 0; + int nContexts, i; + int aide = (CC->user.axlevel >= 6); + + if (IsEmptyStr(presence_jid)) return; + + cptr = CtdlGetContextArray(&nContexts); + if (!cptr) + return ; /** FIXME: Does XMPP need to send something to maintain the protocol? */ + + /* Count the visible sessions for this user */ + for (i=0; i are now visible to session %d\n", + visible_sessions, presence_jid, CC->cs_pid); + + if ( (event_type == XMPP_EVT_LOGIN) && (visible_sessions == 1) ) { + + CtdlLogPrintf(CTDL_DEBUG, "Telling session %d that <%s> logged in\n", CC->cs_pid, presence_jid); + + /* Do an unsolicited roster update that adds a new contact. */ + for (i=0; i", + ++unsolicited_id); + cprintf(""); + xmpp_roster_item(&cptr[i]); + cprintf("" + ""); + } + } + } + + /* Transmit presence information */ + cprintf("", presence_jid); + } + + if (visible_sessions == 0) { + CtdlLogPrintf(CTDL_DEBUG, "Telling session %d that <%s> logged out\n", CC->cs_pid, presence_jid); + + /* Transmit non-presence information */ + cprintf("", presence_jid); + cprintf("", presence_jid); + + /* Do an unsolicited roster update that deletes the contact. */ + cprintf("", ++unsolicited_id); + cprintf(""); + cprintf("", presence_jid); + cprintf("%s", config.c_humannode); + cprintf(""); + cprintf("" + ""); + } + free(cptr); +} diff --git a/citadel/modules/xmpp/xmpp_query_namespace.c b/citadel/modules/xmpp/xmpp_query_namespace.c new file mode 100644 index 000000000..5bfe7ae11 --- /dev/null +++ b/citadel/modules/xmpp/xmpp_query_namespace.c @@ -0,0 +1,174 @@ +/* + * $Id$ + * + * Handle type situations (namespace queries) + * + * 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" +#include +#include +#include +#include +#include +#include +#include +#include + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#include +#include +#include +#include +#include +#include +#include "citadel.h" +#include "server.h" +#include "citserver.h" +#include "support.h" +#include "config.h" +#include "internet_addressing.h" +#include "md5.h" +#include "ctdl_module.h" +#include "serv_xmpp.h" + + +/* + * Output a single roster item, for roster queries or pushes + */ +void xmpp_roster_item(struct CitContext *cptr) { + cprintf("", + cptr->cs_inet_email, + cptr->user.fullname + ); + cprintf("%s", config.c_humannode); + cprintf(""); +} + +/* + * Return the results for a "jabber:iq:roster:query" + * + * Since we are not yet managing a roster, we simply return the entire wholist + * (minus any entries for this user -- don't tell me about myself) + * + */ +void xmpp_iq_roster_query(void) +{ + struct CitContext *cptr; + int nContexts, i; + int aide = (CC->user.axlevel >= 6); + + cprintf(""); + + cptr = CtdlGetContextArray(&nContexts); + if (cptr) { + for (i=0; iuser.usernum) + ) { + xmpp_roster_item(&cptr[i]); + } + } + } + free (cptr); + } + cprintf(""); +} + + +/* + * TODO: handle queries on some or all of these namespaces + * +xmpp_query_namespace(purple5b5c1e58, splorph.xand.com, http://jabber.org/protocol/disco#items:query) +xmpp_query_namespace(purple5b5c1e59, splorph.xand.com, http://jabber.org/protocol/disco#info:query) +xmpp_query_namespace(purple5b5c1e5a, , vcard-temp:query) + * + */ + +void xmpp_query_namespace(char *iq_id, char *iq_from, char *iq_to, char *query_xmlns) +{ + int supported_namespace = 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")) + ) { + supported_namespace = 1; + } + + CtdlLogPrintf(CTDL_DEBUG, "xmpp_query_namespace(%s, %s, %s, %s)\n", iq_id, iq_from, iq_to, query_xmlns); + + /* + * Beginning of query result. + */ + if (supported_namespace) { + cprintf("", iq_id); + + /* + * Is this a query we know how to handle? + */ + + if (!strcasecmp(query_xmlns, "jabber:iq:roster:query")) { + xmpp_iq_roster_query(); + } + + else if (!strcasecmp(query_xmlns, "jabber:iq:auth:query")) { + cprintf("" + "" + "" + ); + } + + else { + CtdlLogPrintf(CTDL_DEBUG, "Unknown namespace; returning \n"); + cprintf("" + "" + "" + ); + } + + /* + * End of query result. If we didn't hit any known namespaces then we should + * deliver a "service unavailable" error (see RFC3921 section 2.4 and 11.1.5.4) + */ + cprintf(""); +} diff --git a/citadel/modules/xmpp/xmpp_queue.c b/citadel/modules/xmpp/xmpp_queue.c new file mode 100644 index 000000000..3bd8c95d7 --- /dev/null +++ b/citadel/modules/xmpp/xmpp_queue.c @@ -0,0 +1,150 @@ +/* + * $Id$ + * + * XMPP event queue + * + * 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" +#include +#include +#include +#include +#include +#include +#include +#include + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#include +#include +#include +#include +#include +#include +#include "citadel.h" +#include "server.h" +#include "citserver.h" +#include "support.h" +#include "config.h" +#include "internet_addressing.h" +#include "md5.h" +#include "ctdl_module.h" +#include "serv_xmpp.h" + +int queue_event_seq = 0; + +void xmpp_queue_event(int event_type, char *email_addr) { + + struct xmpp_event *xptr = NULL; + struct xmpp_event *new_event = NULL; + struct xmpp_event *last = NULL; + int purged_something = 0; + struct CitContext *cptr; + + CtdlLogPrintf(CTDL_DEBUG, "xmpp_queue_event(%d, %s)\n", event_type, email_addr); + + /* Purge events more than a minute old */ + begin_critical_section(S_XMPP_QUEUE); + do { + purged_something = 0; + if (xmpp_queue != NULL) { + if ((time(NULL) - xmpp_queue->event_time) > 60) { + xptr = xmpp_queue->next; + free(xmpp_queue); + xmpp_queue = xptr; + purged_something = 1; + } + } + } while(purged_something); + end_critical_section(S_XMPP_QUEUE); + + /* Create a new event */ + new_event = (struct xmpp_event *) malloc(sizeof(struct xmpp_event)); + new_event->next = NULL; + new_event->event_time = time(NULL); + new_event->event_seq = ++queue_event_seq; + new_event->event_type = event_type; + new_event->session_which_generated_this_event = CC->cs_pid; + safestrncpy(new_event->event_jid, email_addr, sizeof new_event->event_jid); + + /* Add it to the list */ + begin_critical_section(S_XMPP_QUEUE); + if (xmpp_queue == NULL) { + xmpp_queue = new_event; + } + else { + for (xptr = xmpp_queue; xptr != NULL; xptr = xptr->next) { + if (xptr->next == NULL) { + last = xptr; + } + } + last->next = new_event; + } + end_critical_section(S_XMPP_QUEUE); + + /* Tell the sessions that something is happening */ + begin_critical_section(S_SESSION_TABLE); + for (cptr = ContextList; cptr != NULL; cptr = cptr->next) { + if ((cptr->logged_in) && (cptr->h_async_function == xmpp_async_loop)) { + cptr->async_waiting = 1; + } + } + end_critical_section(S_SESSION_TABLE); +} + + +/* + * Are we interested in anything from the queue? (Called in async loop) + */ +void xmpp_process_events(void) { + struct xmpp_event *xptr = NULL; + int highest_event = 0; + + for (xptr=xmpp_queue; xptr!=NULL; xptr=xptr->next) { + if (xptr->event_seq > XMPP->last_event_processed) { + + switch(xptr->event_type) { + + case XMPP_EVT_LOGIN: + case XMPP_EVT_LOGOUT: + if (xptr->session_which_generated_this_event != CC->cs_pid) { + xmpp_presence_notify(xptr->event_jid, xptr->event_type); + } + break; + } + + if (xptr->event_seq > highest_event) { + highest_event = xptr->event_seq; + } + } + } + + XMPP->last_event_processed = highest_event; +} diff --git a/citadel/modules/xmpp/xmpp_sasl_service.c b/citadel/modules/xmpp/xmpp_sasl_service.c new file mode 100644 index 000000000..8e952d2c8 --- /dev/null +++ b/citadel/modules/xmpp/xmpp_sasl_service.c @@ -0,0 +1,178 @@ +/* + * $Id$ + * + * Barebones SASL authentication service for XMPP (Jabber) clients. + * + * Note: RFC3920 says we "must" support DIGEST-MD5 but we only support PLAIN. + * + * 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" +#include +#include +#include +#include +#include +#include +#include +#include + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#include +#include +#include +#include +#include +#include +#include "citadel.h" +#include "server.h" +#include "citserver.h" +#include "support.h" +#include "config.h" +#include "user_ops.h" +#include "internet_addressing.h" +#include "md5.h" +#include "ctdl_module.h" +#include "serv_xmpp.h" + + +/* + * PLAIN authentication. Returns zero on success, nonzero on failure. + */ +int xmpp_auth_plain(char *authstring) +{ + char decoded_authstring[1024]; + char ident[256]; + char user[256]; + char pass[256]; + int result; + + + /* Take apart the authentication string */ + memset(pass, 0, sizeof(pass)); + + CtdlDecodeBase64(decoded_authstring, authstring, strlen(authstring)); + safestrncpy(ident, decoded_authstring, sizeof ident); + safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user); + safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass); + + + /* If there are underscores in either string, change them to spaces. Some clients + * do not allow spaces so we can tell the user to substitute underscores if their + * login name contains spaces. + */ + convert_spaces_to_underscores(ident); + convert_spaces_to_underscores(user); + + /* Now attempt authentication */ + + if (!IsEmptyStr(ident)) { + result = CtdlLoginExistingUser(user, ident); + } + else { + result = CtdlLoginExistingUser(NULL, user); + } + + if (result == login_ok) { + if (CtdlTryPassword(pass) == pass_ok) { + return(0); /* success */ + } + } + + return(1); /* failure */ +} + + +/* + * Output the list of SASL mechanisms offered by this stream. + */ +void xmpp_output_auth_mechs(void) { + cprintf(""); + cprintf("PLAIN"); + cprintf(""); +} + +/* + * Here we go ... client is trying to authenticate. + */ +void xmpp_sasl_auth(char *sasl_auth_mech, char *authstring) { + + if (strcasecmp(sasl_auth_mech, "PLAIN")) { + cprintf(""); + cprintf(""); + cprintf(""); + return; + } + + if (CC->logged_in) CtdlUserLogout(); /* Client may try to log in twice. Handle this. */ + + if (CC->nologin) { + cprintf(""); + cprintf(""); + cprintf(""); + } + + else if (xmpp_auth_plain(authstring) == 0) { + cprintf(""); + } + + else { + cprintf(""); + cprintf(""); + cprintf(""); + } +} + + + +/* + * Non-SASL authentication + */ +void xmpp_non_sasl_authenticate(char *iq_id, char *username, char *password, char *resource) { + int result; + + if (CC->logged_in) CtdlUserLogout(); /* Client may try to log in twice. Handle this. */ + + result = CtdlLoginExistingUser(NULL, username); + if (result == login_ok) { + result = CtdlTryPassword(password); + if (result == pass_ok) { + cprintf("", iq_id); /* success */ + return; + } + } + + /* failure */ + cprintf("", iq_id); + cprintf("" + "" + "" + "" + ); +}