* Renamed the 'jabber' module to 'xmpp' module. Also renamed functions starting...
authorArt Cancro <ajc@citadel.org>
Mon, 22 Feb 2010 18:30:51 +0000 (18:30 +0000)
committerArt Cancro <ajc@citadel.org>
Mon, 22 Feb 2010 18:30:51 +0000 (18:30 +0000)
14 files changed:
citadel/modules/jabber/serv_xmpp.c [deleted file]
citadel/modules/jabber/serv_xmpp.h [deleted file]
citadel/modules/jabber/xmpp_messages.c [deleted file]
citadel/modules/jabber/xmpp_presence.c [deleted file]
citadel/modules/jabber/xmpp_query_namespace.c [deleted file]
citadel/modules/jabber/xmpp_queue.c [deleted file]
citadel/modules/jabber/xmpp_sasl_service.c [deleted file]
citadel/modules/xmpp/serv_xmpp.c [new file with mode: 0644]
citadel/modules/xmpp/serv_xmpp.h [new file with mode: 0644]
citadel/modules/xmpp/xmpp_messages.c [new file with mode: 0644]
citadel/modules/xmpp/xmpp_presence.c [new file with mode: 0644]
citadel/modules/xmpp/xmpp_query_namespace.c [new file with mode: 0644]
citadel/modules/xmpp/xmpp_queue.c [new file with mode: 0644]
citadel/modules/xmpp/xmpp_sasl_service.c [new file with mode: 0644]

diff --git a/citadel/modules/jabber/serv_xmpp.c b/citadel/modules/jabber/serv_xmpp.c
deleted file mode 100644 (file)
index 7f37e5a..0000000
+++ /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 <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <pwd.h>
-#include <errno.h>
-#include <sys/types.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#include <sys/wait.h>
-#include <string.h>
-#include <limits.h>
-#include <ctype.h>
-#include <libcitadel.h>
-#include <expat.h>
-#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 <stream> 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("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
-
-       cprintf("<stream:stream ");
-       cprintf("from=\"%s\" ", XMPP->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("<stream:features>");
-
-#ifdef HAVE_OPENSSL_XXXX_COMMENTED_OUT
-       /* TLS encryption (but only if it isn't already active) */
-       if (!CC->redirect_ssl) {
-               cprintf("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'></starttls>");
-       }
-#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("<auth xmlns=\"http://jabber.org/features/iq-auth\"/>");
-       }
-
-       /* Offer binding and sessions as part of our feature set */
-       cprintf("<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/>");
-       cprintf("<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>");
-
-       cprintf("</stream:features>");
-
-       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 type=\"result\" ");
-                               if (!IsEmptyStr(XMPP->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; <service-unavailable/>\n");
-                               cprintf("<iq type=\"error\" id=\"%s\">", XMPP->iq_id);
-                               cprintf("<error code=\"503\" type=\"cancel\">"
-                                       "<service-unavailable xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>"
-                                       "</error>"
-                               );
-                               cprintf("</iq>");
-                       }
-               }
-
-               /*
-                * 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 <iq> 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("<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_id);
-                       cprintf("<error></error>");
-                       cprintf("</iq>");
-               }
-
-               /* 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 <presence> 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("<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
-       CtdlModuleStartCryptoMsgs(NULL, NULL, NULL);
-       if (!CC->redirect_ssl) CC->kill_me = 1;
-#else
-       cprintf("<failure xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
-       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 (file)
index f7d9e14..0000000
+++ /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 <iq> 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 <query> */
-
-       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;             /* <html> 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 (file)
index 405856e..0000000
+++ /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 <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <pwd.h>
-#include <errno.h>
-#include <sys/types.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#include <sys/wait.h>
-#include <string.h>
-#include <limits.h>
-#include <ctype.h>
-#include <expat.h>
-#include <libcitadel.h>
-#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("<message to=\"%s\" from=\"%s\" type=\"chat\">",
-                       XMPP->client_jid,
-                       ptr->sender_email);
-               if (ptr->text != NULL) {
-                       striplt(ptr->text);
-                       cprintf("<body>%s</body>", ptr->text);
-                       free(ptr->text);
-               }
-               cprintf("</message>");
-               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 (file)
index 96b4504..0000000
+++ /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 <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <pwd.h>
-#include <errno.h>
-#include <sys/types.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#include <sys/wait.h>
-#include <string.h>
-#include <limits.h>
-#include <ctype.h>
-#include <expat.h>
-#include <libcitadel.h>
-#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; i<nContexts; i++) {
-               if (cptr[i].logged_in) {
-                       if (
-                               (((cptr[i].cs_flags&CS_STEALTH)==0) || (aide))  /* aides see everyone */
-                               && (cptr[i].user.usernum != CC->user.usernum)   /* don't show myself */
-                               && (cptr[i].can_receive_im)                     /* IM-capable session */
-                          ) {
-                               cprintf("<presence type=\"available\" from=\"%s\"></presence>",
-                                       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<nContexts; i++) {
-               if (cptr[i].logged_in) {
-                       if (
-                               (!strcasecmp(cptr[i].cs_inet_email, presence_jid)) 
-                               && (((cptr[i].cs_flags&CS_STEALTH)==0) || (aide))
-                               && (cptr[i].can_receive_im)
-                       ) {
-                               ++visible_sessions;
-                       }
-               }
-       }
-
-       CtdlLogPrintf(CTDL_DEBUG, "%d sessions for <%s> 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<nContexts; i++) {
-                       if (cptr[i].logged_in) {
-                               if (!strcasecmp(cptr[i].cs_inet_email, presence_jid)) {
-                                       cprintf("<iq id=\"unsolicited_%x\" type=\"result\">",
-                                               ++unsolicited_id);
-                                       cprintf("<query xmlns=\"jabber:iq:roster\">");
-                                       jabber_roster_item(&cptr[i]);
-                                       cprintf("</query>"
-                                               "</iq>");
-                               }
-                       }
-               }
-
-               /* Transmit presence information */
-               cprintf("<presence type=\"available\" from=\"%s\"></presence>", 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 type=\"unavailable\" from=\"%s\"></presence>", presence_jid);
-               cprintf("<presence type=\"unsubscribed\" from=\"%s\"></presence>", presence_jid);
-
-               /* Do an unsolicited roster update that deletes the contact. */
-               cprintf("<iq id=\"unsolicited_%x\" type=\"result\">", ++unsolicited_id);
-               cprintf("<query xmlns=\"jabber:iq:roster\">");
-               cprintf("<item jid=\"%s\" subscription=\"remove\">", presence_jid);
-               cprintf("<group>%s</group>", config.c_humannode);
-               cprintf("</item>");
-               cprintf("</query>"
-                       "</iq>");
-       }
-       free(cptr);
-}
diff --git a/citadel/modules/jabber/xmpp_query_namespace.c b/citadel/modules/jabber/xmpp_query_namespace.c
deleted file mode 100644 (file)
index 64aba16..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * $Id$ 
- *
- * Handle <iq> <get> <query> 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 <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <pwd.h>
-#include <errno.h>
-#include <sys/types.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#include <sys/wait.h>
-#include <string.h>
-#include <limits.h>
-#include <ctype.h>
-#include <expat.h>
-#include <libcitadel.h>
-#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("<item jid=\"%s\" name=\"%s\" subscription=\"both\">",
-               cptr->cs_inet_email,
-               cptr->user.fullname
-       );
-       cprintf("<group>%s</group>", config.c_humannode);
-       cprintf("</item>");
-}
-
-/* 
- * 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("<query xmlns=\"jabber:iq:roster\">");
-
-       cptr = CtdlGetContextArray(&nContexts);
-       if (cptr) {
-               for (i=0; i<nContexts; i++) {
-                       if (cptr[i].logged_in) {
-                               if (
-                                       (((cptr[i].cs_flags&CS_STEALTH)==0) || (aide))
-                                       && (cptr[i].user.usernum != CC->user.usernum)
-                               ) {
-                                       jabber_roster_item(&cptr[i]);
-                               }
-                       }
-               }
-               free (cptr);
-       }
-       cprintf("</query>");
-}
-
-
-/*
- * 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 type=\"result\" ");
-       }
-       else {
-               cprintf("<iq type=\"error\" ");
-       }
-       if (!IsEmptyStr(iq_from)) {
-               cprintf("to=\"%s\" ", iq_from);
-       }
-       cprintf("id=\"%s\">", 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("<query xmlns=\"jabber:iq:auth\">"
-                       "<username/><password/><resource/>"
-                       "</query>"
-               );
-       }
-
-       else {
-               CtdlLogPrintf(CTDL_DEBUG, "Unknown namespace; returning <service-unavailable/>\n");
-               cprintf("<error code=\"503\" type=\"cancel\">"
-                       "<service-unavailable xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>"
-                       "</error>"
-               );
-       }
-
-       /*
-        * 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("</iq>");
-}
diff --git a/citadel/modules/jabber/xmpp_queue.c b/citadel/modules/jabber/xmpp_queue.c
deleted file mode 100644 (file)
index 3bd8c95..0000000
+++ /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 <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <pwd.h>
-#include <errno.h>
-#include <sys/types.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#include <sys/wait.h>
-#include <string.h>
-#include <limits.h>
-#include <ctype.h>
-#include <expat.h>
-#include <libcitadel.h>
-#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 (file)
index 1391512..0000000
+++ /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 <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <pwd.h>
-#include <errno.h>
-#include <sys/types.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#include <sys/wait.h>
-#include <string.h>
-#include <limits.h>
-#include <ctype.h>
-#include <expat.h>
-#include <libcitadel.h>
-#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("<mechanisms xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
-       cprintf("<mechanism>PLAIN</mechanism>");
-       cprintf("</mechanisms>");
-}
-
-/*
- * 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("<failure xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
-               cprintf("<invalid-mechanism/>");
-               cprintf("</failure>");
-               return;
-       }
-
-        if (CC->logged_in) CtdlUserLogout();  /* Client may try to log in twice.  Handle this. */
-
-       if (CC->nologin) {
-               cprintf("<failure xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
-               cprintf("<system-shutdown/>");
-               cprintf("</failure>");
-       }
-
-       else if (xmpp_auth_plain(authstring) == 0) {
-               cprintf("<success xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\"/>");
-       }
-
-       else {
-               cprintf("<failure xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
-               cprintf("<not-authorized/>");
-               cprintf("</failure>");
-       }
-}
-
-
-
-/*
- * 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 type=\"result\" id=\"%s\"></iq>", iq_id);  /* success */
-                       return;
-               }
-       }
-
-       /* failure */
-       cprintf("<iq type=\"error\" id=\"%s\">", iq_id);
-       cprintf("<error code=\"401\" type=\"auth\">"
-               "<not-authorized xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>"
-               "</error>"
-               "</iq>"
-       );
-}
diff --git a/citadel/modules/xmpp/serv_xmpp.c b/citadel/modules/xmpp/serv_xmpp.c
new file mode 100644 (file)
index 0000000..0b7da46
--- /dev/null
@@ -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 <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <pwd.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <sys/wait.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+#include <libcitadel.h>
+#include <expat.h>
+#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 <stream> 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("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+
+       cprintf("<stream:stream ");
+       cprintf("from=\"%s\" ", XMPP->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("<stream:features>");
+
+#ifdef HAVE_OPENSSL_XXXX_COMMENTED_OUT
+       /* TLS encryption (but only if it isn't already active) */
+       if (!CC->redirect_ssl) {
+               cprintf("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'></starttls>");
+       }
+#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("<auth xmlns=\"http://jabber.org/features/iq-auth\"/>");
+       }
+
+       /* Offer binding and sessions as part of our feature set */
+       cprintf("<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/>");
+       cprintf("<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>");
+
+       cprintf("</stream:features>");
+
+       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 type=\"result\" ");
+                               if (!IsEmptyStr(XMPP->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; <service-unavailable/>\n");
+                               cprintf("<iq type=\"error\" id=\"%s\">", XMPP->iq_id);
+                               cprintf("<error code=\"503\" type=\"cancel\">"
+                                       "<service-unavailable xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>"
+                                       "</error>"
+                               );
+                               cprintf("</iq>");
+                       }
+               }
+
+               /*
+                * 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 <iq> 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("<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_id);
+                       cprintf("<error></error>");
+                       cprintf("</iq>");
+               }
+
+               /* 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 <presence> 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("<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
+       CtdlModuleStartCryptoMsgs(NULL, NULL, NULL);
+       if (!CC->redirect_ssl) CC->kill_me = 1;
+#else
+       cprintf("<failure xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
+       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 (file)
index 0000000..dedc599
--- /dev/null
@@ -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 <iq> 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 <query> */
+
+       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;             /* <html> 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 (file)
index 0000000..5ea435d
--- /dev/null
@@ -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 <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <pwd.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <sys/wait.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+#include <expat.h>
+#include <libcitadel.h>
+#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("<message to=\"%s\" from=\"%s\" type=\"chat\">",
+                       XMPP->client_jid,
+                       ptr->sender_email);
+               if (ptr->text != NULL) {
+                       striplt(ptr->text);
+                       cprintf("<body>%s</body>", ptr->text);
+                       free(ptr->text);
+               }
+               cprintf("</message>");
+               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 (file)
index 0000000..a60580f
--- /dev/null
@@ -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 <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <pwd.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <sys/wait.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+#include <expat.h>
+#include <libcitadel.h>
+#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; i<nContexts; i++) {
+               if (cptr[i].logged_in) {
+                       if (
+                               (((cptr[i].cs_flags&CS_STEALTH)==0) || (aide))  /* aides see everyone */
+                               && (cptr[i].user.usernum != CC->user.usernum)   /* don't show myself */
+                               && (cptr[i].can_receive_im)                     /* IM-capable session */
+                          ) {
+                               cprintf("<presence type=\"available\" from=\"%s\"></presence>",
+                                       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<nContexts; i++) {
+               if (cptr[i].logged_in) {
+                       if (
+                               (!strcasecmp(cptr[i].cs_inet_email, presence_jid)) 
+                               && (((cptr[i].cs_flags&CS_STEALTH)==0) || (aide))
+                               && (cptr[i].can_receive_im)
+                       ) {
+                               ++visible_sessions;
+                       }
+               }
+       }
+
+       CtdlLogPrintf(CTDL_DEBUG, "%d sessions for <%s> 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<nContexts; i++) {
+                       if (cptr[i].logged_in) {
+                               if (!strcasecmp(cptr[i].cs_inet_email, presence_jid)) {
+                                       cprintf("<iq id=\"unsolicited_%x\" type=\"result\">",
+                                               ++unsolicited_id);
+                                       cprintf("<query xmlns=\"jabber:iq:roster\">");
+                                       xmpp_roster_item(&cptr[i]);
+                                       cprintf("</query>"
+                                               "</iq>");
+                               }
+                       }
+               }
+
+               /* Transmit presence information */
+               cprintf("<presence type=\"available\" from=\"%s\"></presence>", 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 type=\"unavailable\" from=\"%s\"></presence>", presence_jid);
+               cprintf("<presence type=\"unsubscribed\" from=\"%s\"></presence>", presence_jid);
+
+               /* Do an unsolicited roster update that deletes the contact. */
+               cprintf("<iq id=\"unsolicited_%x\" type=\"result\">", ++unsolicited_id);
+               cprintf("<query xmlns=\"jabber:iq:roster\">");
+               cprintf("<item jid=\"%s\" subscription=\"remove\">", presence_jid);
+               cprintf("<group>%s</group>", config.c_humannode);
+               cprintf("</item>");
+               cprintf("</query>"
+                       "</iq>");
+       }
+       free(cptr);
+}
diff --git a/citadel/modules/xmpp/xmpp_query_namespace.c b/citadel/modules/xmpp/xmpp_query_namespace.c
new file mode 100644 (file)
index 0000000..5bfe7ae
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * $Id$ 
+ *
+ * Handle <iq> <get> <query> 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 <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <pwd.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <sys/wait.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+#include <expat.h>
+#include <libcitadel.h>
+#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("<item jid=\"%s\" name=\"%s\" subscription=\"both\">",
+               cptr->cs_inet_email,
+               cptr->user.fullname
+       );
+       cprintf("<group>%s</group>", config.c_humannode);
+       cprintf("</item>");
+}
+
+/* 
+ * 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("<query xmlns=\"jabber:iq:roster\">");
+
+       cptr = CtdlGetContextArray(&nContexts);
+       if (cptr) {
+               for (i=0; i<nContexts; i++) {
+                       if (cptr[i].logged_in) {
+                               if (
+                                       (((cptr[i].cs_flags&CS_STEALTH)==0) || (aide))
+                                       && (cptr[i].user.usernum != CC->user.usernum)
+                               ) {
+                                       xmpp_roster_item(&cptr[i]);
+                               }
+                       }
+               }
+               free (cptr);
+       }
+       cprintf("</query>");
+}
+
+
+/*
+ * 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 type=\"result\" ");
+       }
+       else {
+               cprintf("<iq type=\"error\" ");
+       }
+       if (!IsEmptyStr(iq_from)) {
+               cprintf("to=\"%s\" ", iq_from);
+       }
+       cprintf("id=\"%s\">", 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("<query xmlns=\"jabber:iq:auth\">"
+                       "<username/><password/><resource/>"
+                       "</query>"
+               );
+       }
+
+       else {
+               CtdlLogPrintf(CTDL_DEBUG, "Unknown namespace; returning <service-unavailable/>\n");
+               cprintf("<error code=\"503\" type=\"cancel\">"
+                       "<service-unavailable xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>"
+                       "</error>"
+               );
+       }
+
+       /*
+        * 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("</iq>");
+}
diff --git a/citadel/modules/xmpp/xmpp_queue.c b/citadel/modules/xmpp/xmpp_queue.c
new file mode 100644 (file)
index 0000000..3bd8c95
--- /dev/null
@@ -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 <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <pwd.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <sys/wait.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+#include <expat.h>
+#include <libcitadel.h>
+#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 (file)
index 0000000..8e952d2
--- /dev/null
@@ -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 <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <pwd.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <sys/wait.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+#include <expat.h>
+#include <libcitadel.h>
+#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("<mechanisms xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
+       cprintf("<mechanism>PLAIN</mechanism>");
+       cprintf("</mechanisms>");
+}
+
+/*
+ * 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("<failure xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
+               cprintf("<invalid-mechanism/>");
+               cprintf("</failure>");
+               return;
+       }
+
+        if (CC->logged_in) CtdlUserLogout();  /* Client may try to log in twice.  Handle this. */
+
+       if (CC->nologin) {
+               cprintf("<failure xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
+               cprintf("<system-shutdown/>");
+               cprintf("</failure>");
+       }
+
+       else if (xmpp_auth_plain(authstring) == 0) {
+               cprintf("<success xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\"/>");
+       }
+
+       else {
+               cprintf("<failure xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
+               cprintf("<not-authorized/>");
+               cprintf("</failure>");
+       }
+}
+
+
+
+/*
+ * 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 type=\"result\" id=\"%s\"></iq>", iq_id);  /* success */
+                       return;
+               }
+       }
+
+       /* failure */
+       cprintf("<iq type=\"error\" id=\"%s\">", iq_id);
+       cprintf("<error code=\"401\" type=\"auth\">"
+               "<not-authorized xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>"
+               "</error>"
+               "</iq>"
+       );
+}