Back to index

citadel  8.12
Functions | Variables
serv_xmpp.c File Reference
#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>
#include <time.h>
#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 "database.h"
#include "msgbase.h"
#include "internet_addressing.h"
#include "md5.h"
#include "ctdl_module.h"
#include "serv_xmpp.h"

Go to the source code of this file.

Functions

char * xmlesc (char *buf, char *str, int bufsiz)
void xmpp_stream_start (void *data, const char *supplied_el, const char **attr)
void xmpp_xml_start (void *data, const char *supplied_el, const char **attr)
void xmpp_xml_end (void *data, const char *supplied_el)
void xmpp_xml_chardata (void *data, const XML_Char *s, int len)
void xmpp_cleanup_function (void)
void xmpp_greeting (void)
void xmpp_command_loop (void)
void xmpp_async_loop (void)
void xmpp_login_hook (void)
void xmpp_logout_hook (void)
void LogXMPPSrvDebugEnable (const int n)
void xmpp_cleanup_events (void)
 CTDL_MODULE_INIT (xmpp)

Variables

struct xmpp_eventxmpp_queue = NULL
int XMPPSrvDebugEnable = 0
const char * CitadelServiceXMPP = "XMPP"

Function Documentation

CTDL_MODULE_INIT ( xmpp  )
void LogXMPPSrvDebugEnable ( const int  n)

Definition at line 619 of file serv_xmpp.c.

Here is the caller graph for this function:

char* xmlesc ( char *  buf,
char *  str,
int  bufsiz 
)

Definition at line 93 of file serv_xmpp.c.

{
       char *ptr;
       unsigned char ch;
       int len = 0;

       if (!buf) return(NULL);
       buf[0] = 0;
       len = 0;
       if (!str) {
              return(buf);
       }

       for (ptr=str; *ptr; ptr++) {
              ch = *ptr;
              if (ch == '<') {
                     strcpy(&buf[len], "&lt;");
                     len += 4;
              }
              else if (ch == '>') {
                     strcpy(&buf[len], "&gt;");
                     len += 4;
              }
              else if (ch == '&') {
                     strcpy(&buf[len], "&amp;");
                     len += 5;
              }
              else if ((ch >= 0x20) && (ch <= 0x7F)) {
                     buf[len++] = ch;
                     buf[len] = 0;
              }
              else if (ch < 0x20) {
                     /* we probably shouldn't be doing this */
                     buf[len++] = '_';
                     buf[len] = 0;
              }
              else {
                     char oct[10];
                     sprintf(oct, "&#%o;", ch);
                     strcpy(&buf[len], oct);
                     len += strlen(oct);
              }
              if ((len + 6) > bufsiz) {
                     return(buf);
              }
       }
       return(buf);
}

Here is the caller graph for this function:

void xmpp_async_loop ( void  )

Definition at line 597 of file serv_xmpp.c.

Here is the call graph for this function:

Here is the caller graph for this function:

void xmpp_cleanup_events ( void  )

Definition at line 151 of file xmpp_queue.c.

{
        struct xmpp_event *ptr, *ptr2;
        begin_critical_section(S_XMPP_QUEUE);
       ptr = xmpp_queue;
       xmpp_queue = NULL;
       while (ptr != NULL) {
              ptr2 = ptr->next;
              free(ptr);
              ptr = ptr2;
       }
        end_critical_section(S_XMPP_QUEUE);

}

Here is the call graph for this function:

Here is the caller graph for this function:

void xmpp_cleanup_function ( void  )

Definition at line 516 of file serv_xmpp.c.

                                 {

       /* 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 is the call graph for this function:

Here is the caller graph for this function:

void xmpp_command_loop ( void  )

Definition at line 577 of file serv_xmpp.c.

                             {
       int rc;
       StrBuf *stream_input = NewStrBuf();

       time(&CC->lastcmd);
       rc = client_read_random_blob(stream_input, 30);
       if (rc > 0) {
              XML_Parse(XMPP->xp, ChrPtr(stream_input), rc, 0);
       }
       else {
              XMPPM_syslog(LOG_ERR, "client disconnected: ending session.\n");
              CC->kill_me = KILLME_CLIENT_DISCONNECTED;
       }
       FreeStrBuf(&stream_input);
}

Here is the call graph for this function:

Here is the caller graph for this function:

void xmpp_greeting ( void  )

Definition at line 539 of file serv_xmpp.c.

                         {
       client_set_inbound_buf(4);
       strcpy(CC->cs_clientname, "XMPP session");
       CC->session_specific_data = malloc(sizeof(citxmpp));
       memset(XMPP, 0, sizeof(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) {
              XMPPM_syslog(LOG_ALERT, "Cannot create XML parser!\n");
              CC->kill_me = KILLME_XML_PARSER;
              return;
       }

       XML_SetElementHandler(XMPP->xp, xmpp_xml_start, xmpp_xml_end);
       XML_SetCharacterDataHandler(XMPP->xp, xmpp_xml_chardata);
       // XML_SetUserData(XMPP->xp, something...);

       /* Prevent the "billion laughs" attack against expat by disabling
        * internal entity expansion.  With 2.x, forcibly stop the parser
        * if an entity is declared - this is safer and a more obvious
        * failure mode.  With older versions, simply prevent expansion
        * of such entities. */
#ifdef HAVE_XML_STOPPARSER
       XML_SetEntityDeclHandler(XMPP->xp, xmpp_entity_declaration);
#else
       XML_SetDefaultHandler(XMPP->xp, NULL);
#endif

       CC->can_receive_im = 1;            /* This protocol is capable of receiving instant messages */
}

Here is the call graph for this function:

Here is the caller graph for this function:

void xmpp_login_hook ( void  )

Definition at line 606 of file serv_xmpp.c.

                           {
       xmpp_queue_event(XMPP_EVT_LOGIN, CC->cs_inet_email);
}

Here is the call graph for this function:

Here is the caller graph for this function:

void xmpp_logout_hook ( void  )

Definition at line 614 of file serv_xmpp.c.

                            {
       xmpp_queue_event(XMPP_EVT_LOGOUT, CC->cs_inet_email);
}

Here is the call graph for this function:

Here is the caller graph for this function:

void xmpp_stream_start ( void *  data,
const char *  supplied_el,
const char **  attr 
)

Definition at line 146 of file serv_xmpp.c.

{
       char xmlbuf[256];

       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\" ", xmlesc(xmlbuf, XMPP->server_name, sizeof xmlbuf));
       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>");

       /*
        * TLS encryption (but only if it isn't already active)
        */ 
#ifdef HAVE_OPENSSL
       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 */
}

Here is the call graph for this function:

Here is the caller graph for this function:

void xmpp_xml_chardata ( void *  data,
const XML_Char *  s,
int  len 
)

Definition at line 495 of file serv_xmpp.c.

{
       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;
}

Here is the caller graph for this function:

void xmpp_xml_end ( void *  data,
const char *  supplied_el 
)

Definition at line 268 of file serv_xmpp.c.

                                                       {
       char el[256];
       char *sep = NULL;
       char xmlbuf[256];

       /* Axe the namespace, we don't care about it */
       safestrncpy(el, supplied_el, sizeof el);
       while (sep = strchr(el, ':'), sep) {
              strcpy(el, ++sep);
       }

       /*
       XMPP_syslog(LOG_DEBUG, "XMPP ELEMENT END  : <%s>\n", el);
       if (XMPP->chardata_len > 0) {
              XMPP_syslog(LOG_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);
              }
       }

       else 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);
              }
       }

       else 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\" ", xmlesc(xmlbuf, XMPP->iq_from, sizeof xmlbuf));
                            }
                            if (!IsEmptyStr(XMPP->iq_to)) {
                                   cprintf("from=\"%s\" ", xmlesc(xmlbuf, XMPP->iq_to, sizeof xmlbuf));
                            }
                            cprintf("id=\"%s\"/>", xmlesc(xmlbuf, XMPP->iq_id, sizeof xmlbuf));
                     }

                     /*
                      * Unknown query ... return the XML equivalent of a blank stare
                      */
                     else {
                            XMPP_syslog(LOG_DEBUG,
                                       "Unknown query <%s> - returning <service-unavailable/>\n",
                                       el
                            );
                            cprintf("<iq type=\"error\" id=\"%s\">", xmlesc(xmlbuf, XMPP->iq_id, sizeof xmlbuf));
                            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\">", xmlesc(xmlbuf, XMPP->iq_id, sizeof xmlbuf));
                     cprintf("<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\">");
                     cprintf("<jid>%s</jid>", xmlesc(xmlbuf, XMPP->client_jid, sizeof xmlbuf));
                     cprintf("</bind>");
                     cprintf("</iq>");
              }

              else if (XMPP->iq_session) {
                     cprintf("<iq type=\"result\" id=\"%s\">", xmlesc(xmlbuf, XMPP->iq_id, sizeof xmlbuf));
                     cprintf("</iq>");
              }

              else {
                     cprintf("<iq type=\"error\" id=\"%s\">", xmlesc(xmlbuf, XMPP->iq_id, sizeof xmlbuf));
                     cprintf("<error>Don't know howto do '%s'!</error>", xmlesc(xmlbuf, XMPP->iq_type, sizeof xmlbuf));
                     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 = KILLME_NO_CRYPTO;
#else
              cprintf("<failure xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
              CC->kill_me = KILLME_NO_CRYPTO;
#endif
       }

       else if (!strcasecmp(el, "ping")) {
              XMPP->ping_requested = 1;
       }

       else if (!strcasecmp(el, "stream")) {
              XMPPM_syslog(LOG_DEBUG, "XMPP client shut down their stream\n");
              xmpp_massacre_roster();
              cprintf("</stream>\n");
              CC->kill_me = KILLME_CLIENT_LOGGED_OUT;
       }

       else {
              XMPP_syslog(LOG_DEBUG, "Ignoring unknown tag <%s>\n", el);
       }

       XMPP->chardata_len = 0;
       if (XMPP->chardata_alloc > 0) {
              XMPP->chardata[0] = 0;
       }
}

Here is the call graph for this function:

Here is the caller graph for this function:

void xmpp_xml_start ( void *  data,
const char *  supplied_el,
const char **  attr 
)

Definition at line 196 of file serv_xmpp.c.

                                                                            {
       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);
       }

       /*
       XMPP_syslog(LOG_DEBUG, "XMPP ELEMENT START: <%s>\n", el);
       for (i=0; attr[i] != NULL; i+=2) {
              XMPP_syslog(LOG_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;
       }
}

Here is the call graph for this function:

Here is the caller graph for this function:


Variable Documentation

const char* CitadelServiceXMPP = "XMPP"

Definition at line 623 of file serv_xmpp.c.

struct xmpp_event* xmpp_queue = NULL

Definition at line 65 of file serv_xmpp.c.

Definition at line 67 of file serv_xmpp.c.