1e0f704b4a7a75f9bf2a30a6ee67172c709aa689
[citadel.git] / citadel / modules / xmpp / serv_xmpp.c
1 /*
2  * XMPP (Jabber) service for the Citadel system
3  * Copyright (c) 2007-2011 by Art Cancro
4  *
5  * This program is open source software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #include "sysdep.h"
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <stdio.h>
24 #include <stdarg.h>
25 #include <fcntl.h>
26 #include <signal.h>
27 #include <pwd.h>
28 #include <errno.h>
29 #include <sys/types.h>
30
31 #if TIME_WITH_SYS_TIME
32 # include <sys/time.h>
33 # include <time.h>
34 #else
35 # if HAVE_SYS_TIME_H
36 #  include <sys/time.h>
37 # else
38 #  include <time.h>
39 # endif
40 #endif
41
42 #include <sys/wait.h>
43 #include <string.h>
44 #include <limits.h>
45 #include <ctype.h>
46 #define SHOW_ME_VAPPEND_PRINTF
47 #include <libcitadel.h>
48 #include <expat.h>
49 #include "citadel.h"
50 #include "server.h"
51 #include "citserver.h"
52 #include "support.h"
53 #include "config.h"
54 #include "user_ops.h"
55 #include "database.h"
56 #include "msgbase.h"
57 #include "internet_addressing.h"
58 #include "md5.h"
59 #include "ctdl_module.h"
60 #include "serv_xmpp.h"
61
62 /* XML_StopParser is present in expat 2.x */
63 #if XML_MAJOR_VERSION > 1
64 #define HAVE_XML_STOPPARSER
65 #endif
66
67 struct xmpp_event *xmpp_queue = NULL;
68 HashList *XMPP_StartHandlers = NULL;
69 HashList *XMPP_EndHandlers = NULL;
70
71 int XMPPSrvDebugEnable = 0;
72
73 void XUnbuffer(void)
74 {
75         citxmpp *Xmpp = XMPP;
76
77         cputbuf(Xmpp->OutBuf);
78         FlushStrBuf(Xmpp->OutBuf);
79 }
80
81 void XPutBody(const char *Str, long Len)
82 {
83         StrBufXMLEscAppend(XMPP->OutBuf, NULL, Str, Len, 0);
84 }
85
86 void XPutProp(const char *Str, long Len)
87 {
88         StrEscAppend(XMPP->OutBuf, NULL, Str, 0, 1);
89 }
90
91 void XPut(const char *Str, long Len)
92 {
93         StrBufAppendBufPlain(XMPP->OutBuf, Str, Len, 0);
94 }
95 #define XPUT(CONSTSTR) XPut(CONSTSTR, sizeof(CONSTSTR) -1)
96
97 void XPrintf(const char *Format, ...)
98 {
99         va_list arg_ptr;
100         va_start(arg_ptr, Format);
101         StrBufVAppendPrintf(XMPP->OutBuf, Format, arg_ptr);
102         va_end(arg_ptr);
103 }
104
105
106 #ifdef HAVE_XML_STOPPARSER
107 /* Stop the parser if an entity declaration is hit. */
108 static void xmpp_entity_declaration(void *userData, const XML_Char *entityName,
109                                 int is_parameter_entity, const XML_Char *value,
110                                 int value_length, const XML_Char *base,
111                                 const XML_Char *systemId, const XML_Char *publicId,
112                                 const XML_Char *notationName
113 ) {
114         XMPPM_syslog(LOG_WARNING, "Illegal entity declaration encountered; stopping parser.");
115         XML_StopParser(XMPP->xp, XML_FALSE);
116 }
117 #endif
118
119 /*
120  * We have just received a <stream> tag from the client, so send them ours
121  */
122 void xmpp_stream_start(void *data, const char *supplied_el, const char **attr)
123 {
124         citxmpp *Xmpp = XMPP;
125
126         while (*attr) {
127                 if (!strcasecmp(attr[0], "to")) {
128                         safestrncpy(Xmpp->server_name, attr[1], sizeof Xmpp->server_name);
129                 }
130                 attr += 2;
131         }
132
133         XPUT("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
134
135         XPUT("<stream:stream ");
136         XPUT("from=\"");
137         XPutProp(Xmpp->server_name, strlen(Xmpp->server_name));
138         XPUT("\" id=\"");
139         XPrintf("%08x\" ", CC->cs_pid);
140         XPUT("version=\"1.0\" "
141                   "xmlns:stream=\"http://etherx.jabber.org/streams\" "
142                   "xmlns=\"jabber:client\">"
143         /* The features of this stream are... */
144              "<stream:features>");
145
146         /*
147          * TLS encryption (but only if it isn't already active)
148          */ 
149 #ifdef HAVE_OPENSSL
150         if (!CC->redirect_ssl) {
151                 XPUT("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'></starttls>");
152         }
153 #endif
154
155         if (!CC->logged_in) {
156                 /* If we're not logged in yet, offer SASL as our feature set */
157                 xmpp_output_auth_mechs();
158
159                 /* Also offer non-SASL authentication */
160                 XPUT("<auth xmlns=\"http://jabber.org/features/iq-auth\"/>");
161         }
162
163         /* Offer binding and sessions as part of our feature set */
164         XPUT("<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/>"
165                   "<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>"
166                   "</stream:features>");
167
168         CC->is_async = 1;               /* XMPP sessions are inherently async-capable */
169 }
170
171 void xmpp_start_query(void *data, const char *supplied_el, const char **attr)
172 {
173         XMPP->iq_query_xmlns[0] = 0;
174         safestrncpy(XMPP->iq_query_xmlns, supplied_el, sizeof XMPP->iq_query_xmlns);
175 }
176
177 void xmpp_start_bind(void *data, const char *supplied_el, const char **attr)
178 {
179         XMPP->bind_requested = 1;
180 }
181
182 void xmpp_start_iq(void *data, const char *supplied_el, const char **attr)
183 {
184         int i;
185         for (i=0; attr[i] != NULL; i+=2) {
186                 if (!strcasecmp(attr[i], "type")) {
187                         safestrncpy(XMPP->iq_type, attr[i+1], sizeof XMPP->iq_type);
188                 }
189                 else if (!strcasecmp(attr[i], "id")) {
190                         safestrncpy(XMPP->iq_id, attr[i+1], sizeof XMPP->iq_id);
191                 }
192                 else if (!strcasecmp(attr[i], "from")) {
193                         safestrncpy(XMPP->iq_from, attr[i+1], sizeof XMPP->iq_from);
194                 }
195                 else if (!strcasecmp(attr[i], "to")) {
196                         safestrncpy(XMPP->iq_to, attr[i+1], sizeof XMPP->iq_to);
197                 }
198         }
199 }
200
201 void xmpp_start_auth(void *data, const char *supplied_el, const char **attr)
202 {
203         int i;
204
205         XMPP->sasl_auth_mech[0] = 0;
206         for (i=0; attr[i] != NULL; i+=2) {
207                 if (!strcasecmp(attr[i], "mechanism")) {
208                         safestrncpy(XMPP->sasl_auth_mech, attr[i+1], sizeof XMPP->sasl_auth_mech);
209                 }
210         }
211 }
212
213 void xmpp_start_message(void *data, const char *supplied_el, const char **attr)
214 {
215         int i;
216
217         for (i=0; attr[i] != NULL; i+=2) {
218                 if (!strcasecmp(attr[i], "to")) {
219                         safestrncpy(XMPP->message_to, attr[i+1], sizeof XMPP->message_to);
220                 }
221         }
222 }
223
224 void xmpp_start_html(void *data, const char *supplied_el, const char **attr)
225 {
226         ++XMPP->html_tag_level;
227 }
228
229 void xmpp_xml_start(void *data, const char *supplied_el, const char **attr)
230 {
231         char el[256];
232         long newlen;
233         long len;
234         char *sep = NULL;
235         void *pv;
236         
237         /* Axe the namespace, we don't care about it */
238         newlen = len = safestrncpy(el, supplied_el, sizeof el);
239         while (sep = strchr(el, ':'), sep)
240         {
241                 newlen -= ++sep - el;
242                 memmove(el, sep, newlen + 1);
243                 len = newlen;
244         }
245
246         /*
247         XMPP_syslog(LOG_DEBUG, "XMPP ELEMENT START: <%s>\n", el);
248         for (i=0; attr[i] != NULL; i+=2) {
249                 XMPP_syslog(LOG_DEBUG, "                    Attribute '%s' = '%s'\n", attr[i], attr[i+1]);
250         }
251         uncomment for more verbosity */
252
253         if (GetHash(XMPP_StartHandlers, el, len, &pv))
254         {
255                 xmpp_handler *h;
256                 h = (xmpp_handler*) pv;
257                 h->Handler(data, supplied_el, attr);
258         }
259 }
260
261 void xmpp_end_resource(void *data, const char *supplied_el, const char **attr)
262 {
263         if (XMPP->chardata_len > 0) {
264                 safestrncpy(XMPP->iq_client_resource, XMPP->chardata,
265                             sizeof XMPP->iq_client_resource);
266                 striplt(XMPP->iq_client_resource);
267         }
268 }
269
270 void xmpp_end_username(void *data, const char *supplied_el, const char **attr)
271 {
272         /* NON SASL ONLY */
273         if (XMPP->chardata_len > 0) {
274                 safestrncpy(XMPP->iq_client_username, XMPP->chardata,
275                             sizeof XMPP->iq_client_username);
276                 striplt(XMPP->iq_client_username);
277         }
278 }
279
280 void xmpp_end_password(void *data, const char *supplied_el, const char **attr)
281 {               /* NON SASL ONLY */
282         if (XMPP->chardata_len > 0) {
283                 safestrncpy(XMPP->iq_client_password, XMPP->chardata,
284                             sizeof XMPP->iq_client_password);
285                 striplt(XMPP->iq_client_password);
286         }
287 }
288
289 void xmpp_end_iq(void *data, const char *supplied_el, const char **attr)
290 {
291         citxmpp *Xmpp = XMPP;
292
293         /*
294          * iq type="get" (handle queries)
295          */
296         if (!strcasecmp(Xmpp->iq_type, "get"))
297         {
298                 /*
299                  * Query on a namespace
300                  */
301                 if (!IsEmptyStr(Xmpp->iq_query_xmlns)) {
302                         xmpp_query_namespace(Xmpp->iq_id, Xmpp->iq_from,
303                                              Xmpp->iq_to, Xmpp->iq_query_xmlns);
304                 }
305                 
306                 /*
307                  * ping ( http://xmpp.org/extensions/xep-0199.html )
308                  */
309                 else if (Xmpp->ping_requested) {
310                         XPUT("<iq type=\"result\" ");
311                         if (!IsEmptyStr(Xmpp->iq_from)) {
312                                 XPUT("to=\"");
313                                 XPutProp(Xmpp->iq_from, strlen(Xmpp->iq_from));
314                                 XPUT("\" ");
315                         }
316                         if (!IsEmptyStr(Xmpp->iq_to)) {
317                                 XPUT("from=\"");
318                                 XPutProp(Xmpp->iq_to, strlen(Xmpp->iq_to));
319                                 XPUT("\" ");
320                         }
321                         XPUT("id=\"");
322                         XPutProp(Xmpp->iq_id, strlen(Xmpp->iq_id));
323                         XPUT("\"/>");
324                 }
325
326                 /*
327                  * Unknown query ... return the XML equivalent of a blank stare
328                  */
329                 else {
330 /*
331                         Xmpp_syslog(LOG_DEBUG,
332                                     "Unknown query <%s> - returning <service-unavailable/>\n",
333                                     el
334                                 );
335 */
336                         XPUT("<iq type=\"error\" id=\"");
337                         XPutProp(Xmpp->iq_id, strlen(Xmpp->iq_id));
338                         XPUT("\">"
339                              "<error code=\"503\" type=\"cancel\">"
340                              "<service-unavailable xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>"
341                              "</error>"
342                              "</iq>");
343                 }
344         }
345
346         /*
347          * Non SASL authentication
348          */
349         else if (
350                 (!strcasecmp(Xmpp->iq_type, "set"))
351                 && (!strcasecmp(Xmpp->iq_query_xmlns, "jabber:iq:auth:query"))
352                 ) {
353                 
354                 xmpp_non_sasl_authenticate(
355                         Xmpp->iq_id,
356                         Xmpp->iq_client_username,
357                         Xmpp->iq_client_password,
358                         Xmpp->iq_client_resource
359                         );
360         }
361
362         /*
363          * If this <iq> stanza was a "bind" attempt, process it ...
364          */
365         else if (
366                 (Xmpp->bind_requested)
367                 && (!IsEmptyStr(Xmpp->iq_id))
368                 && (!IsEmptyStr(Xmpp->iq_client_resource))
369                 && (CC->logged_in)
370                 ) {
371
372                 /* Generate the "full JID" of the client resource */
373
374                 snprintf(Xmpp->client_jid, sizeof Xmpp->client_jid,
375                          "%s/%s",
376                          CC->cs_inet_email,
377                          Xmpp->iq_client_resource
378                         );
379
380                 /* Tell the client what its JID is */
381
382                 XPUT("<iq type=\"result\" id=\"");
383                 XPutProp(Xmpp->iq_id, strlen(Xmpp->iq_id));
384                 XPUT("\">"
385                      "<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\">");
386                 XPUT("<jid>");
387                 XPutBody(Xmpp->client_jid, strlen(Xmpp->client_jid));
388                 XPUT("</jid>"
389                      "</bind>"
390                      "</iq>");
391         }
392
393         else if (Xmpp->iq_session) {
394                 XPUT("<iq type=\"result\" id=\"");
395                 XPutProp(Xmpp->iq_id, strlen(Xmpp->iq_id));
396                 XPUT("\">"
397                      "</iq>");
398         }
399
400         else {
401                 XPUT("<iq type=\"error\" id=\"");
402                 XPutProp(Xmpp->iq_id, strlen(Xmpp->iq_id));
403                 XPUT("\">");
404                 XPUT("<error>Don't know howto do '");
405                 XPutBody(Xmpp->iq_type, strlen(Xmpp->iq_type));
406                 XPUT("'!</error>"
407                      "</iq>");
408         }
409
410         /* Now clear these fields out so they don't get used by a future stanza */
411         Xmpp->iq_id[0] = 0;
412         Xmpp->iq_from[0] = 0;
413         Xmpp->iq_to[0] = 0;
414         Xmpp->iq_type[0] = 0;
415         Xmpp->iq_client_resource[0] = 0;
416         Xmpp->iq_session = 0;
417         Xmpp->iq_query_xmlns[0] = 0;
418         Xmpp->bind_requested = 0;
419         Xmpp->ping_requested = 0;
420 }
421
422
423 void xmpp_end_auth(void *data, const char *supplied_el, const char **attr)
424 {
425         /* Try to authenticate (this function is responsible for the output stanza) */
426         xmpp_sasl_auth(XMPP->sasl_auth_mech, (XMPP->chardata != NULL ? XMPP->chardata : "") );
427         
428         /* Now clear these fields out so they don't get used by a future stanza */
429         XMPP->sasl_auth_mech[0] = 0;
430 }
431
432 void xmpp_end_session(void *data, const char *supplied_el, const char **attr)
433 {
434         XMPP->iq_session = 1;
435 }
436
437 void xmpp_end_body(void *data, const char *supplied_el, const char **attr)
438 {
439         if (XMPP->html_tag_level == 0)
440         {
441                 if (XMPP->message_body != NULL)
442                 {
443                         free(XMPP->message_body);
444                         XMPP->message_body = NULL;
445                 }
446                 if (XMPP->chardata_len > 0) {
447                         XMPP->message_body = strdup(XMPP->chardata);
448                 }
449         }
450 }
451
452 void xmpp_end_html(void *data, const char *supplied_el, const char **attr)
453 {
454         --XMPP->html_tag_level;
455 }
456
457 void xmpp_end_starttls(void *data, const char *supplied_el, const char **attr)
458 {
459 #ifdef HAVE_OPENSSL
460         XPUT("<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
461         XUnbuffer();
462         CtdlModuleStartCryptoMsgs(NULL, NULL, NULL);
463         if (!CC->redirect_ssl) CC->kill_me = KILLME_NO_CRYPTO;
464 #else
465         XPUT("<failure xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
466         CC->kill_me = KILLME_NO_CRYPTO;
467 #endif
468 }
469
470 void xmpp_end_ping(void *data, const char *supplied_el, const char **attr)
471 {
472         XMPP->ping_requested = 1;
473 }
474
475 void xmpp_end_stream(void *data, const char *supplied_el, const char **attr)
476 {
477         XMPPM_syslog(LOG_DEBUG, "XMPP client shut down their stream\n");
478         xmpp_massacre_roster();
479         XPUT("</stream>\n");
480         CC->kill_me = KILLME_CLIENT_LOGGED_OUT;
481 }
482
483 void xmpp_xml_end(void *data, const char *supplied_el)
484 {
485         char el[256];
486         long len;
487         long newlen;
488         char *sep = NULL;
489         void *pv;
490
491         /* Axe the namespace, we don't care about it */
492         newlen = len = safestrncpy(el, supplied_el, sizeof el);
493         while (sep = strchr(el, ':'), sep) {
494
495                 newlen -= ++sep - el;
496                 memmove(el, sep, newlen + 1);
497                 len = newlen;
498         }
499
500         /*
501         XMPP_syslog(LOG_DEBUG, "XMPP ELEMENT END  : <%s>\n", el);
502         if (XMPP->chardata_len > 0) {
503                 XMPP_syslog(LOG_DEBUG, "          chardata: %s\n", XMPP->chardata);
504         }
505         uncomment for more verbosity */
506
507         if (GetHash(XMPP_EndHandlers, el, len, &pv))
508         {
509                 xmpp_handler *h;
510                 h = (xmpp_handler*) pv;
511                 h->Handler(data, supplied_el, NULL);
512         }
513         else
514         {
515                 XMPP_syslog(LOG_DEBUG, "Ignoring unknown tag <%s>\n", el);
516         }
517
518         XMPP->chardata_len = 0;
519         if (XMPP->chardata_alloc > 0) {
520                 XMPP->chardata[0] = 0;
521         }
522 }
523
524
525 void xmpp_xml_chardata(void *data, const XML_Char *s, int len)
526 {
527         citxmpp *X = XMPP;
528
529         if (X->chardata_alloc == 0) {
530                 X->chardata_alloc = SIZ;
531                 X->chardata = malloc(X->chardata_alloc);
532         }
533         if ((X->chardata_len + len + 1) > X->chardata_alloc) {
534                 X->chardata_alloc = X->chardata_len + len + 1024;
535                 X->chardata = realloc(X->chardata, X->chardata_alloc);
536         }
537         memcpy(&X->chardata[X->chardata_len], s, len);
538         X->chardata_len += len;
539         X->chardata[X->chardata_len] = 0;
540 }
541
542
543 /*
544  * This cleanup function blows away the temporary memory and files used by the XMPP service.
545  */
546 void xmpp_cleanup_function(void) {
547
548         /* Don't do this stuff if this is not a XMPP session! */
549         if (CC->h_command_function != xmpp_command_loop) return;
550
551         if (XMPP->chardata != NULL) {
552                 free(XMPP->chardata);
553                 XMPP->chardata = NULL;
554                 XMPP->chardata_len = 0;
555                 XMPP->chardata_alloc = 0;
556                 if (XMPP->message_body != NULL) {
557                         free(XMPP->message_body);
558                 }
559         }
560         XML_ParserFree(XMPP->xp);
561         FreeStrBuf(&XMPP->OutBuf);
562         free(XMPP);
563 }
564
565
566
567 /*
568  * Here's where our XMPP session begins its happy day.
569  */
570 void xmpp_greeting(void) {
571         client_set_inbound_buf(4);
572         strcpy(CC->cs_clientname, "XMPP session");
573         CC->session_specific_data = malloc(sizeof(citxmpp));
574         memset(XMPP, 0, sizeof(citxmpp));
575         XMPP->last_event_processed = queue_event_seq;
576         XMPP->OutBuf = NewStrBufPlain(NULL, SIZ);
577         /* XMPP does not use a greeting, but we still have to initialize some things. */
578
579         XMPP->xp = XML_ParserCreateNS("UTF-8", ':');
580         if (XMPP->xp == NULL) {
581                 XMPPM_syslog(LOG_ALERT, "Cannot create XML parser!\n");
582                 CC->kill_me = KILLME_XML_PARSER;
583                 return;
584         }
585
586         XML_SetElementHandler(XMPP->xp, xmpp_xml_start, xmpp_xml_end);
587         XML_SetCharacterDataHandler(XMPP->xp, xmpp_xml_chardata);
588         // XML_SetUserData(XMPP->xp, something...);
589
590         /* Prevent the "billion laughs" attack against expat by disabling
591          * internal entity expansion.  With 2.x, forcibly stop the parser
592          * if an entity is declared - this is safer and a more obvious
593          * failure mode.  With older versions, simply prevent expansion
594          * of such entities. */
595 #ifdef HAVE_XML_STOPPARSER
596         XML_SetEntityDeclHandler(XMPP->xp, xmpp_entity_declaration);
597 #else
598         XML_SetDefaultHandler(XMPP->xp, NULL);
599 #endif
600
601         CC->can_receive_im = 1;         /* This protocol is capable of receiving instant messages */
602         XUnbuffer();
603 }
604
605
606 /* 
607  * Main command loop for XMPP sessions.
608  */
609 void xmpp_command_loop(void) {
610         int rc;
611         StrBuf *stream_input = NewStrBuf();
612
613         time(&CC->lastcmd);
614         rc = client_read_random_blob(stream_input, 30);
615         if (rc > 0) {
616                 XML_Parse(XMPP->xp, ChrPtr(stream_input), rc, 0);
617         }
618         else {
619                 XMPPM_syslog(LOG_ERR, "client disconnected: ending session.\n");
620                 CC->kill_me = KILLME_CLIENT_DISCONNECTED;
621         }
622         FreeStrBuf(&stream_input);
623         XUnbuffer();
624 }
625
626
627 /*
628  * Async loop for XMPP sessions (handles the transmission of unsolicited stanzas)
629  */
630 void xmpp_async_loop(void) {
631         xmpp_process_events();
632         xmpp_output_incoming_messages();
633 }
634
635
636 /*
637  * Login hook for XMPP sessions
638  */
639 void xmpp_login_hook(void) {
640         xmpp_queue_event(XMPP_EVT_LOGIN, CC->cs_inet_email);
641 }
642
643
644 /*
645  * Logout hook for XMPP sessions
646  */
647 void xmpp_logout_hook(void) {
648         xmpp_queue_event(XMPP_EVT_LOGOUT, CC->cs_inet_email);
649 }
650
651
652 void LogXMPPSrvDebugEnable(const int n)
653 {
654         XMPPSrvDebugEnable = n;
655 }
656 const char *CitadelServiceXMPP="XMPP";
657 extern void xmpp_cleanup_events(void);
658
659
660
661 /******************************************************************************
662  *                    XMPP handler registering logic                           *
663  ******************************************************************************/
664
665 void AddXMPPStartHandler(const char *key,
666                          long len,
667                          xmpp_handler_func Handler,
668                          int Flags)
669 {
670         xmpp_handler *h;
671         h = (xmpp_handler*) malloc(sizeof (xmpp_handler));
672         h->Flags = Flags;
673         h->Handler = Handler;
674         Put(XMPP_StartHandlers, key, len, h, NULL);
675 }
676
677 void AddXMPPEndHandler(const char *key,
678                        long len,
679                        xmpp_handler_func Handler,
680                        int Flags)
681 {
682         xmpp_handler *h;
683         h = (xmpp_handler*) malloc(sizeof (xmpp_handler));
684         h->Flags = Flags;
685         h->Handler = Handler;
686         Put(XMPP_EndHandlers, key, len, h, NULL);
687 }
688
689 CTDL_MODULE_INIT(xmpp)
690 {
691         if (!threading) {
692                 CtdlRegisterServiceHook(config.c_xmpp_c2s_port,
693                                         NULL,
694                                         xmpp_greeting,
695                                         xmpp_command_loop,
696                                         xmpp_async_loop,
697                                         CitadelServiceXMPP);
698
699
700                 XMPP_StartHandlers = NewHash(1, NULL);
701                 XMPP_EndHandlers = NewHash(1, NULL);
702
703                 AddXMPPEndHandler(HKEY("resource"),      xmpp_end_resource, 0);
704                 AddXMPPEndHandler(HKEY("username"),      xmpp_end_username, 0);
705                 AddXMPPEndHandler(HKEY("password"),      xmpp_end_password, 0);
706                 AddXMPPEndHandler(HKEY("iq"),            xmpp_end_iq, 0);
707                 AddXMPPEndHandler(HKEY("auth"),          xmpp_end_auth, 0);
708                 AddXMPPEndHandler(HKEY("session"),       xmpp_end_session, 0);
709                 AddXMPPEndHandler(HKEY("body"),          xmpp_end_body, 0);
710                 AddXMPPEndHandler(HKEY("html"),          xmpp_end_html, 0);
711                 AddXMPPEndHandler(HKEY("starttls"),      xmpp_end_starttls, 0);
712                 AddXMPPEndHandler(HKEY("ping"),          xmpp_end_ping, 0);
713                 AddXMPPEndHandler(HKEY("stream"),        xmpp_end_stream, 0);
714
715                 AddXMPPStartHandler(HKEY("stream"),     xmpp_stream_start, 0);
716                 AddXMPPStartHandler(HKEY("query"),      xmpp_start_query, 0);
717                 AddXMPPStartHandler(HKEY("bind"),       xmpp_start_bind, 0);
718                 AddXMPPStartHandler(HKEY("iq"),         xmpp_start_iq, 0);
719                 AddXMPPStartHandler(HKEY("auth"),       xmpp_start_auth, 0);
720                 AddXMPPStartHandler(HKEY("message"),    xmpp_start_message, 0);
721                 AddXMPPStartHandler(HKEY("html"),       xmpp_start_html, 0);
722
723
724                 CtdlRegisterDebugFlagHook(HKEY("serv_xmpp"), LogXMPPSrvDebugEnable, &XMPPSrvDebugEnable);
725                 CtdlRegisterSessionHook(xmpp_cleanup_function, EVT_STOP, PRIO_STOP + 70);
726                 CtdlRegisterSessionHook(xmpp_login_hook, EVT_LOGIN, PRIO_LOGIN + 90);
727                 CtdlRegisterSessionHook(xmpp_logout_hook, EVT_LOGOUT, PRIO_LOGOUT + 90);
728                 CtdlRegisterSessionHook(xmpp_login_hook, EVT_UNSTEALTH, PRIO_UNSTEALTH + 1);
729                 CtdlRegisterSessionHook(xmpp_logout_hook, EVT_STEALTH, PRIO_STEALTH + 1);
730                 CtdlRegisterCleanupHook(xmpp_cleanup_events);
731
732         }
733
734         /* return our module name for the log */
735         return "xmpp";
736 }