Merge branch 'master' of ssh://git.citadel.org/appl/gitroot/citadel
[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 // uncomment for more verbosity
21 #define XMPP_DEBUG 1
22
23 #include "sysdep.h"
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <stdio.h>
27 #include <fcntl.h>
28 #include <signal.h>
29 #include <pwd.h>
30 #include <errno.h>
31 #include <sys/types.h>
32
33 #if TIME_WITH_SYS_TIME
34 # include <sys/time.h>
35 # include <time.h>
36 #else
37 # if HAVE_SYS_TIME_H
38 #  include <sys/time.h>
39 # else
40 #  include <time.h>
41 # endif
42 #endif
43
44 #include <sys/wait.h>
45 #include <string.h>
46 #include <limits.h>
47 #include <ctype.h>
48 #include <libcitadel.h>
49 #include <expat.h>
50 #include "citadel.h"
51 #include "server.h"
52 #include "citserver.h"
53 #include "support.h"
54 #include "config.h"
55 #include "user_ops.h"
56 #include "database.h"
57 #include "msgbase.h"
58 #include "internet_addressing.h"
59 #include "md5.h"
60 #include "ctdl_module.h"
61 #include "serv_xmpp.h"
62
63 /* XML_StopParser is present in expat 2.x */
64 #if XML_MAJOR_VERSION > 1
65 #define HAVE_XML_STOPPARSER
66 #endif
67
68 struct xmpp_event *xmpp_queue = NULL;
69
70 int XMPPSrvDebugEnable = 0;
71
72
73
74 #ifdef HAVE_XML_STOPPARSER
75 /* Stop the parser if an entity declaration is hit. */
76 static void xmpp_entity_declaration(void *userData, const XML_Char *entityName,
77                                 int is_parameter_entity, const XML_Char *value,
78                                 int value_length, const XML_Char *base,
79                                 const XML_Char *systemId, const XML_Char *publicId,
80                                 const XML_Char *notationName
81 ) {
82 <<<<<<< HEAD
83         syslog(LOG_WARNING, "Illegal entity declaration encountered; stopping parser.");
84 =======
85         XMPPM_syslog(LOG_WARNING, "Illegal entity declaration encountered; stopping parser.");
86 >>>>>>> 1c0b8162b0a90f2e97028a531005c11b09441498
87         XML_StopParser(XMPP->xp, XML_FALSE);
88 }
89 #endif
90
91 static inline int XMPP_GetUtf8SequenceLength(const char *CharS, const char *CharE)
92 {
93         /* if this is is migrated to strbuf, remove this copy. */
94         int n = 0;
95         unsigned char test = (1<<7);
96
97         if ((*CharS & 0xC0) != 0xC0) 
98                 return 1;
99
100         while ((n < 8) && 
101                ((test & ((unsigned char)*CharS)) != 0)) 
102         {
103                 test = test >> 1;
104                 n ++;
105         }
106         if ((n > 6) || ((CharE - CharS) < n))
107                 n = 0;
108         return n;
109 }
110
111
112 /*
113  * Given a source string and a target buffer, returns the string
114  * properly escaped for insertion into an XML stream.  Returns a
115  * pointer to the target buffer for convenience.
116  *
117  * BUG: this does not properly handle UTF-8
118  */
119 char *xmlesc(char *buf, char *str, int bufsiz)
120 {
121         char *ptr;
122         char *eiptr;
123         unsigned char ch;
124         int inlen;
125         int len = 0;
126         int IsUtf8Sequence;
127
128         if (!buf) return(NULL);
129         buf[0] = 0;
130         len = 0;
131         if (!str) {
132                 return(buf);
133         }
134
135         inlen = strlen(str);
136         eiptr = str + inlen;
137
138         for (ptr=str; *ptr; ptr++) {
139                 ch = *ptr;
140                 if (ch == '<') {
141                         strcpy(&buf[len], "&lt;");
142                         len += 4;
143                 }
144                 else if (ch == '>') {
145                         strcpy(&buf[len], "&gt;");
146                         len += 4;
147                 }
148                 else if (ch == '&') {
149                         strcpy(&buf[len], "&amp;");
150                         len += 5;
151                 }
152                 else if ((ch >= 0x20) && (ch <= 0x7F)) {
153                         buf[len++] = ch;
154                         buf[len] = 0;
155                 }
156                 else if (ch < 0x20) {
157                         /* we probably shouldn't be doing this */
158                         buf[len++] = '_';
159                         buf[len] = 0;
160                 }
161                 else {
162                         char oct[32];
163
164                         IsUtf8Sequence =  XMPP_GetUtf8SequenceLength(&buf[len], eiptr);
165                         if (IsUtf8Sequence)
166                         {
167                                 while (IsUtf8Sequence > 0){
168                                         buf[len] = *ptr;
169                                         len ++;
170                                         if (--IsUtf8Sequence)
171                                                 ptr++;
172                                 }
173                                 buf[len] = '\0';
174                         }
175                         else
176                         {
177                                 sprintf(oct, "&#%o;", ch);
178                                 strcpy(&buf[len], oct);
179                                 len += strlen(oct);
180                         }
181                 }
182                 if ((len + 6) > bufsiz) {
183                         return(buf);
184                 }
185         }
186         return(buf);
187 }
188
189
190 /*
191  * We have just received a <stream> tag from the client, so send them ours
192  */
193 void xmpp_stream_start(void *data, const char *supplied_el, const char **attr)
194 {
195         char xmlbuf[256];
196
197         while (*attr) {
198                 if (!strcasecmp(attr[0], "to")) {
199                         safestrncpy(XMPP->server_name, attr[1], sizeof XMPP->server_name);
200                 }
201                 attr += 2;
202         }
203
204         cprintf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
205
206         cprintf("<stream:stream ");
207         cprintf("from=\"%s\" ", xmlesc(xmlbuf, XMPP->server_name, sizeof xmlbuf));
208         cprintf("id=\"%08x\" ", CC->cs_pid);
209         cprintf("version=\"1.0\" ");
210         cprintf("xmlns:stream=\"http://etherx.jabber.org/streams\" ");
211         cprintf("xmlns=\"jabber:client\">");
212
213         /* The features of this stream are... */
214         cprintf("<stream:features>");
215
216         /*
217          * TLS encryption (but only if it isn't already active)
218 <<<<<<< HEAD
219          */ 
220 =======
221          * / 
222 >>>>>>> 1c0b8162b0a90f2e97028a531005c11b09441498
223 #ifdef HAVE_OPENSSL
224         if (!CC->redirect_ssl) {
225                 cprintf("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'></starttls>");
226         }
227 #endif
228 <<<<<<< HEAD
229
230 =======
231         */
232 >>>>>>> 1c0b8162b0a90f2e97028a531005c11b09441498
233         if (!CC->logged_in) {
234                 /* If we're not logged in yet, offer SASL as our feature set */
235                 xmpp_output_auth_mechs();
236
237                 /* Also offer non-SASL authentication */
238                 cprintf("<auth xmlns=\"http://jabber.org/features/iq-auth\"/>");
239         }
240
241         /* Offer binding and sessions as part of our feature set */
242         cprintf("<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/>");
243         cprintf("<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>");
244 <<<<<<< HEAD
245
246         cprintf("</stream:features>");
247 =======
248 >>>>>>> 1c0b8162b0a90f2e97028a531005c11b09441498
249
250         cprintf("</stream:features>");
251
252 <<<<<<< HEAD
253
254 =======
255         CC->is_async = 1;               /* XMPP sessions are inherently async-capable */
256 }
257
258
259 >>>>>>> 1c0b8162b0a90f2e97028a531005c11b09441498
260 void xmpp_xml_start(void *data, const char *supplied_el, const char **attr) {
261         char el[256];
262         char *sep = NULL;
263         int i;
264
265         /* Axe the namespace, we don't care about it */
266         safestrncpy(el, supplied_el, sizeof el);
267         while (sep = strchr(el, ':'), sep) {
268                 strcpy(el, ++sep);
269         }
270
271 <<<<<<< HEAD
272 #ifdef XMPP_DEBUG
273         syslog(LOG_DEBUG, "XMPP ELEMENT START: <%s>\n", el);
274         for (i=0; attr[i] != NULL; i+=2) {
275                 syslog(LOG_DEBUG, "                    Attribute '%s' = '%s'\n", attr[i], attr[i+1]);
276         }
277 #endif /* XMPP_DEBUG */
278 =======
279         /*
280         XMPP_syslog(LOG_DEBUG, "XMPP ELEMENT START: <%s>\n", el);
281         for (i=0; attr[i] != NULL; i+=2) {
282                 XMPP_syslog(LOG_DEBUG, "                    Attribute '%s' = '%s'\n", attr[i], attr[i+1]);
283         }
284         uncomment for more verbosity */
285 >>>>>>> 1c0b8162b0a90f2e97028a531005c11b09441498
286
287         if (!strcasecmp(el, "stream")) {
288                 xmpp_stream_start(data, supplied_el, attr);
289         }
290
291         else if (!strcasecmp(el, "query")) {
292                 XMPP->iq_query_xmlns[0] = 0;
293                 safestrncpy(XMPP->iq_query_xmlns, supplied_el, sizeof XMPP->iq_query_xmlns);
294 <<<<<<< HEAD
295         }
296
297         else if (!strcasecmp(el, "bind")) {
298                 XMPP->bind_requested = 1;
299         }
300
301         else if (!strcasecmp(el, "iq")) {
302                 for (i=0; attr[i] != NULL; i+=2) {
303                         if (!strcasecmp(attr[i], "type")) {
304                                 safestrncpy(XMPP->iq_type, attr[i+1], sizeof XMPP->iq_type);
305                         }
306                         else if (!strcasecmp(attr[i], "id")) {
307                                 safestrncpy(XMPP->iq_id, attr[i+1], sizeof XMPP->iq_id);
308                         }
309                         else if (!strcasecmp(attr[i], "from")) {
310                                 safestrncpy(XMPP->iq_from, attr[i+1], sizeof XMPP->iq_from);
311                         }
312                         else if (!strcasecmp(attr[i], "to")) {
313                                 safestrncpy(XMPP->iq_to, attr[i+1], sizeof XMPP->iq_to);
314                         }
315                 }
316         }
317
318         else if (!strcasecmp(el, "auth")) {
319                 XMPP->sasl_auth_mech[0] = 0;
320                 for (i=0; attr[i] != NULL; i+=2) {
321                         if (!strcasecmp(attr[i], "mechanism")) {
322                                 safestrncpy(XMPP->sasl_auth_mech, attr[i+1], sizeof XMPP->sasl_auth_mech);
323                         }
324                 }
325         }
326
327         else if (!strcasecmp(el, "message")) {
328                 for (i=0; attr[i] != NULL; i+=2) {
329                         if (!strcasecmp(attr[i], "to")) {
330                                 safestrncpy(XMPP->message_to, attr[i+1], sizeof XMPP->message_to);
331                         }
332                 }
333 =======
334         }
335
336         else if (!strcasecmp(el, "bind")) {
337                 XMPP->bind_requested = 1;
338         }
339
340         else if (!strcasecmp(el, "iq")) {
341                 for (i=0; attr[i] != NULL; i+=2) {
342                         if (!strcasecmp(attr[i], "type")) {
343                                 safestrncpy(XMPP->iq_type, attr[i+1], sizeof XMPP->iq_type);
344                         }
345                         else if (!strcasecmp(attr[i], "id")) {
346                                 safestrncpy(XMPP->iq_id, attr[i+1], sizeof XMPP->iq_id);
347                         }
348                         else if (!strcasecmp(attr[i], "from")) {
349                                 safestrncpy(XMPP->iq_from, attr[i+1], sizeof XMPP->iq_from);
350                         }
351                         else if (!strcasecmp(attr[i], "to")) {
352                                 safestrncpy(XMPP->iq_to, attr[i+1], sizeof XMPP->iq_to);
353                         }
354                 }
355         }
356
357         else if (!strcasecmp(el, "auth")) {
358                 XMPP->sasl_auth_mech[0] = 0;
359                 for (i=0; attr[i] != NULL; i+=2) {
360                         if (!strcasecmp(attr[i], "mechanism")) {
361                                 safestrncpy(XMPP->sasl_auth_mech, attr[i+1], sizeof XMPP->sasl_auth_mech);
362                         }
363                 }
364         }
365
366         else if (!strcasecmp(el, "message")) {
367                 for (i=0; attr[i] != NULL; i+=2) {
368                         if (!strcasecmp(attr[i], "to")) {
369                                 safestrncpy(XMPP->message_to, attr[i+1], sizeof XMPP->message_to);
370                         }
371                 }
372         }
373
374         else if (!strcasecmp(el, "html")) {
375                 ++XMPP->html_tag_level;
376         }
377 }
378
379
380
381 void xmpp_xml_end(void *data, const char *supplied_el) {
382         char el[256];
383         char *sep = NULL;
384         char xmlbuf[256];
385
386         /* Axe the namespace, we don't care about it */
387         safestrncpy(el, supplied_el, sizeof el);
388         while (sep = strchr(el, ':'), sep) {
389                 strcpy(el, ++sep);
390         }
391
392         /*
393         XMPP_syslog(LOG_DEBUG, "XMPP ELEMENT END  : <%s>\n", el);
394         if (XMPP->chardata_len > 0) {
395                 XMPP_syslog(LOG_DEBUG, "          chardata: %s\n", XMPP->chardata);
396 >>>>>>> 1c0b8162b0a90f2e97028a531005c11b09441498
397         }
398
399 <<<<<<< HEAD
400         else if (!strcasecmp(el, "html")) {
401                 ++XMPP->html_tag_level;
402         }
403 }
404
405
406
407 void xmpp_xml_end(void *data, const char *supplied_el) {
408         char el[256];
409         char *sep = NULL;
410         char xmlbuf[256];
411
412         /* Axe the namespace, we don't care about it */
413         safestrncpy(el, supplied_el, sizeof el);
414         while (sep = strchr(el, ':'), sep) {
415                 strcpy(el, ++sep);
416         }
417
418 #ifdef XMPP_DEBUG
419         syslog(LOG_DEBUG, "XMPP ELEMENT END  : <%s>\n", el);
420         if (XMPP->chardata_len > 0) {
421                 syslog(LOG_DEBUG, "          chardata: %s\n", XMPP->chardata);
422         }
423 #endif /* XMPP_DEBUG */
424
425         if (!strcasecmp(el, "resource")) {
426                 if (XMPP->chardata_len > 0) {
427                         safestrncpy(XMPP->iq_client_resource, XMPP->chardata,
428                                 sizeof XMPP->iq_client_resource);
429                         striplt(XMPP->iq_client_resource);
430                 }
431         }
432
433         else if (!strcasecmp(el, "username")) {         /* NON SASL ONLY */
434                 if (XMPP->chardata_len > 0) {
435                         safestrncpy(XMPP->iq_client_username, XMPP->chardata,
436                                 sizeof XMPP->iq_client_username);
437                         striplt(XMPP->iq_client_username);
438                 }
439         }
440
441         else if (!strcasecmp(el, "password")) {         /* NON SASL ONLY */
442                 if (XMPP->chardata_len > 0) {
443                         safestrncpy(XMPP->iq_client_password, XMPP->chardata,
444                                 sizeof XMPP->iq_client_password);
445                         striplt(XMPP->iq_client_password);
446                 }
447         }
448
449         else if (!strcasecmp(el, "iq")) {
450
451 =======
452         if (!strcasecmp(el, "resource")) {
453                 if (XMPP->chardata_len > 0) {
454                         safestrncpy(XMPP->iq_client_resource, XMPP->chardata,
455                                 sizeof XMPP->iq_client_resource);
456                         striplt(XMPP->iq_client_resource);
457                 }
458         }
459
460         else if (!strcasecmp(el, "username")) {         /* NON SASL ONLY */
461                 if (XMPP->chardata_len > 0) {
462                         safestrncpy(XMPP->iq_client_username, XMPP->chardata,
463                                 sizeof XMPP->iq_client_username);
464                         striplt(XMPP->iq_client_username);
465                 }
466         }
467
468         else if (!strcasecmp(el, "password")) {         /* NON SASL ONLY */
469                 if (XMPP->chardata_len > 0) {
470                         safestrncpy(XMPP->iq_client_password, XMPP->chardata,
471                                 sizeof XMPP->iq_client_password);
472                         striplt(XMPP->iq_client_password);
473                 }
474         }
475
476         else if (!strcasecmp(el, "iq")) {
477
478 >>>>>>> 1c0b8162b0a90f2e97028a531005c11b09441498
479                 /*
480                  * iq type="get" (handle queries)
481                  */
482                 if (!strcasecmp(XMPP->iq_type, "get")) {
483
484                         /*
485                          * Query on a namespace
486                          */
487                         if (!IsEmptyStr(XMPP->iq_query_xmlns)) {
488                                 xmpp_query_namespace(XMPP->iq_id, XMPP->iq_from,
489                                                 XMPP->iq_to, XMPP->iq_query_xmlns);
490                         }
491
492                         /*
493                          * ping ( http://xmpp.org/extensions/xep-0199.html )
494                          */
495                         else if (XMPP->ping_requested) {
496                                 cprintf("<iq type=\"result\" ");
497                                 if (!IsEmptyStr(XMPP->iq_from)) {
498                                         cprintf("to=\"%s\" ", xmlesc(xmlbuf, XMPP->iq_from, sizeof xmlbuf));
499                                 }
500                                 if (!IsEmptyStr(XMPP->iq_to)) {
501                                         cprintf("from=\"%s\" ", xmlesc(xmlbuf, XMPP->iq_to, sizeof xmlbuf));
502                                 }
503                                 cprintf("id=\"%s\"/>", xmlesc(xmlbuf, XMPP->iq_id, sizeof xmlbuf));
504                         }
505
506                         /*
507                          * Unknown query ... return the XML equivalent of a blank stare
508                          */
509                         else {
510 <<<<<<< HEAD
511                                 syslog(LOG_DEBUG,
512 =======
513                                 XMPP_syslog(LOG_DEBUG,
514 >>>>>>> 1c0b8162b0a90f2e97028a531005c11b09441498
515                                             "Unknown query <%s> - returning <service-unavailable/>\n",
516                                             el
517                                 );
518                                 cprintf("<iq type=\"error\" id=\"%s\">", xmlesc(xmlbuf, XMPP->iq_id, sizeof xmlbuf));
519                                 cprintf("<error code=\"503\" type=\"cancel\">"
520                                         "<service-unavailable xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>"
521                                         "</error>"
522                                 );
523                                 cprintf("</iq>");
524                         }
525                 }
526
527                 /*
528                  * Non SASL authentication
529                  */
530                 else if (
531                         (!strcasecmp(XMPP->iq_type, "set"))
532                         && (!strcasecmp(XMPP->iq_query_xmlns, "jabber:iq:auth:query"))
533                         ) {
534
535                         xmpp_non_sasl_authenticate(
536                                 XMPP->iq_id,
537                                 XMPP->iq_client_username,
538                                 XMPP->iq_client_password,
539                                 XMPP->iq_client_resource
540                         );
541                 }       
542
543                 /*
544                  * If this <iq> stanza was a "bind" attempt, process it ...
545                  */
546                 else if (
547                         (XMPP->bind_requested)
548                         && (!IsEmptyStr(XMPP->iq_id))
549                         && (!IsEmptyStr(XMPP->iq_client_resource))
550                         && (CC->logged_in)
551                         ) {
552
553                         /* Generate the "full JID" of the client resource */
554
555                         snprintf(XMPP->client_jid, sizeof XMPP->client_jid,
556                                 "%s/%s",
557                                 CC->cs_inet_email,
558                                 XMPP->iq_client_resource
559                         );
560
561                         /* Tell the client what its JID is */
562
563                         cprintf("<iq type=\"result\" id=\"%s\">", xmlesc(xmlbuf, XMPP->iq_id, sizeof xmlbuf));
564                         cprintf("<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\">");
565                         cprintf("<jid>%s</jid>", xmlesc(xmlbuf, XMPP->client_jid, sizeof xmlbuf));
566                         cprintf("</bind>");
567                         cprintf("</iq>");
568                 }
569
570                 else if (XMPP->iq_session) {
571                         cprintf("<iq type=\"result\" id=\"%s\">", xmlesc(xmlbuf, XMPP->iq_id, sizeof xmlbuf));
572                         cprintf("</iq>");
573                 }
574
575                 else {
576                         cprintf("<iq type=\"error\" id=\"%s\">", xmlesc(xmlbuf, XMPP->iq_id, sizeof xmlbuf));
577                         cprintf("<error>Don't know howto do '%s'!</error>", xmlesc(xmlbuf, XMPP->iq_type, sizeof xmlbuf));
578                         cprintf("</iq>");
579                 }
580
581                 /* Now clear these fields out so they don't get used by a future stanza */
582                 XMPP->iq_id[0] = 0;
583                 XMPP->iq_from[0] = 0;
584                 XMPP->iq_to[0] = 0;
585                 XMPP->iq_type[0] = 0;
586                 XMPP->iq_client_resource[0] = 0;
587                 XMPP->iq_session = 0;
588                 XMPP->iq_query_xmlns[0] = 0;
589                 XMPP->bind_requested = 0;
590                 XMPP->ping_requested = 0;
591         }
592
593         else if (!strcasecmp(el, "auth")) {
594
595                 /* Try to authenticate (this function is responsible for the output stanza) */
596                 xmpp_sasl_auth(XMPP->sasl_auth_mech, (XMPP->chardata != NULL ? XMPP->chardata : "") );
597
598                 /* Now clear these fields out so they don't get used by a future stanza */
599                 XMPP->sasl_auth_mech[0] = 0;
600         }
601
602         else if (!strcasecmp(el, "session")) {
603                 XMPP->iq_session = 1;
604         }
605
606         else if (!strcasecmp(el, "presence")) {
607
608                 /* Respond to a <presence> update by firing back with presence information
609                  * on the entire wholist.  Check this assumption, it's probably wrong.
610                  */
611                 xmpp_wholist_presence_dump();
612         }
613
614         else if ( (!strcasecmp(el, "body")) && (XMPP->html_tag_level == 0) ) {
615                 if (XMPP->message_body != NULL) {
616                         free(XMPP->message_body);
617                         XMPP->message_body = NULL;
618                 }
619                 if (XMPP->chardata_len > 0) {
620                         XMPP->message_body = strdup(XMPP->chardata);
621                 }
622         }
623
624         else if (!strcasecmp(el, "message")) {
625                 xmpp_send_message(XMPP->message_to, XMPP->message_body);
626                 XMPP->html_tag_level = 0;
627         }
628
629         else if (!strcasecmp(el, "html")) {
630                 --XMPP->html_tag_level;
631         }
632
633         else if (!strcasecmp(el, "starttls")) {
634 #ifdef HAVE_OPENSSL
635                 cprintf("<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
636                 CtdlModuleStartCryptoMsgs(NULL, NULL, NULL);
637                 if (!CC->redirect_ssl) CC->kill_me = KILLME_NO_CRYPTO;
638 #else
639                 cprintf("<failure xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
640                 CC->kill_me = KILLME_NO_CRYPTO;
641 #endif
642         }
643
644         else if (!strcasecmp(el, "ping")) {
645                 XMPP->ping_requested = 1;
646 <<<<<<< HEAD
647         }
648
649         else if (!strcasecmp(el, "stream")) {
650                 syslog(LOG_DEBUG, "XMPP client shut down their stream\n");
651                 xmpp_massacre_roster();
652                 cprintf("</stream>\n");
653                 CC->kill_me = KILLME_CLIENT_LOGGED_OUT;
654         }
655
656         else if (!strcasecmp(el, "query")) {
657                 // no action required here, we picked up the xmlns= parameter during xmpp_xml_start()
658         }
659
660         else {
661                 syslog(LOG_DEBUG, "Ignoring unknown tag <%s>\n", el);
662 =======
663         }
664
665         else if (!strcasecmp(el, "stream")) {
666                 XMPPM_syslog(LOG_DEBUG, "XMPP client shut down their stream\n");
667                 xmpp_massacre_roster();
668                 cprintf("</stream>\n");
669                 CC->kill_me = KILLME_CLIENT_LOGGED_OUT;
670         }
671
672         else {
673                 XMPP_syslog(LOG_DEBUG, "Ignoring unknown tag <%s>\n", el);
674 >>>>>>> 1c0b8162b0a90f2e97028a531005c11b09441498
675         }
676
677         XMPP->chardata_len = 0;
678         if (XMPP->chardata_alloc > 0) {
679                 XMPP->chardata[0] = 0;
680         }
681 }
682
683
684 void xmpp_xml_chardata(void *data, const XML_Char *s, int len)
685 {
686         citxmpp *X = XMPP;
687
688         if (X->chardata_alloc == 0) {
689                 X->chardata_alloc = SIZ;
690                 X->chardata = malloc(X->chardata_alloc);
691         }
692         if ((X->chardata_len + len + 1) > X->chardata_alloc) {
693                 X->chardata_alloc = X->chardata_len + len + 1024;
694                 X->chardata = realloc(X->chardata, X->chardata_alloc);
695         }
696         memcpy(&X->chardata[X->chardata_len], s, len);
697         X->chardata_len += len;
698         X->chardata[X->chardata_len] = 0;
699 }
700
701
702 /*
703  * This cleanup function blows away the temporary memory and files used by the XMPP service.
704  */
705 void xmpp_cleanup_function(void) {
706
707         /* Don't do this stuff if this is not a XMPP session! */
708         if (CC->h_command_function != xmpp_command_loop) return;
709
710         if (XMPP->chardata != NULL) {
711                 free(XMPP->chardata);
712                 XMPP->chardata = NULL;
713                 XMPP->chardata_len = 0;
714                 XMPP->chardata_alloc = 0;
715                 if (XMPP->message_body != NULL) {
716                         free(XMPP->message_body);
717                 }
718         }
719         XML_ParserFree(XMPP->xp);
720         free(XMPP);
721 }
722
723
724
725 /*
726  * Here's where our XMPP session begins its happy day.
727  */
728 void xmpp_greeting(void) {
729         client_set_inbound_buf(4);
730         strcpy(CC->cs_clientname, "XMPP session");
731         CC->session_specific_data = malloc(sizeof(citxmpp));
732         memset(XMPP, 0, sizeof(citxmpp));
733         XMPP->last_event_processed = queue_event_seq;
734
735         /* XMPP does not use a greeting, but we still have to initialize some things. */
736
737         XMPP->xp = XML_ParserCreateNS("UTF-8", ':');
738         if (XMPP->xp == NULL) {
739                 syslog(LOG_ALERT, "Cannot create XML parser!\n");
740                 CC->kill_me = KILLME_XML_PARSER;
741                 return;
742         }
743
744         XML_SetElementHandler(XMPP->xp, xmpp_xml_start, xmpp_xml_end);
745         XML_SetCharacterDataHandler(XMPP->xp, xmpp_xml_chardata);
746         // XML_SetUserData(XMPP->xp, something...);
747
748         /* Prevent the "billion laughs" attack against expat by disabling
749          * internal entity expansion.  With 2.x, forcibly stop the parser
750          * if an entity is declared - this is safer and a more obvious
751          * failure mode.  With older versions, simply prevent expansion
752          * of such entities. */
753 #ifdef HAVE_XML_STOPPARSER
754         XML_SetEntityDeclHandler(XMPP->xp, xmpp_entity_declaration);
755 #else
756         XML_SetDefaultHandler(XMPP->xp, NULL);
757 #endif
758
759         CC->can_receive_im = 1;         /* This protocol is capable of receiving instant messages */
760 }
761
762
763 /* 
764  * Main command loop for XMPP sessions.
765  */
766 void xmpp_command_loop(void) {
767         int rc;
768         StrBuf *stream_input = NewStrBuf();
769
770         time(&CC->lastcmd);
771         rc = client_read_random_blob(stream_input, 30);
772         if (rc > 0) {
773                 syslog(LOG_DEBUG, "\033[32m%s\033[0m", ChrPtr(stream_input));
774                 XML_Parse(XMPP->xp, ChrPtr(stream_input), rc, 0);
775         }
776         else {
777                 syslog(LOG_ERR, "client disconnected: ending session.\n");
778                 CC->kill_me = KILLME_CLIENT_DISCONNECTED;
779         }
780         FreeStrBuf(&stream_input);
781 }
782
783
784 /*
785  * Async loop for XMPP sessions (handles the transmission of unsolicited stanzas)
786  */
787 void xmpp_async_loop(void) {
788         xmpp_process_events();
789         xmpp_output_incoming_messages();
790 }
791
792
793 /*
794  * Login hook for XMPP sessions
795  */
796 void xmpp_login_hook(void) {
797         xmpp_queue_event(XMPP_EVT_LOGIN, CC->cs_inet_email);
798 }
799
800
801 /*
802  * Logout hook for XMPP sessions
803  */
804 void xmpp_logout_hook(void) {
805         xmpp_queue_event(XMPP_EVT_LOGOUT, CC->cs_inet_email);
806 }
807
808
809 void LogXMPPSrvDebugEnable(const int n)
810 {
811         XMPPSrvDebugEnable = n;
812 }
813 const char *CitadelServiceXMPP="XMPP";
814 extern void xmpp_cleanup_events(void);
815 CTDL_MODULE_INIT(xmpp)
816 {
817         if (!threading) {
818                 CtdlRegisterServiceHook(config.c_xmpp_c2s_port,
819                                         NULL,
820                                         xmpp_greeting,
821                                         xmpp_command_loop,
822                                         xmpp_async_loop,
823                                         CitadelServiceXMPP
824                 );
825                 CtdlRegisterDebugFlagHook(HKEY("serv_xmpp"), LogXMPPSrvDebugEnable, &XMPPSrvDebugEnable);
826                 CtdlRegisterSessionHook(xmpp_cleanup_function, EVT_STOP, PRIO_STOP + 70);
827                 CtdlRegisterSessionHook(xmpp_login_hook, EVT_LOGIN, PRIO_LOGIN + 90);
828                 CtdlRegisterSessionHook(xmpp_logout_hook, EVT_LOGOUT, PRIO_LOGOUT + 90);
829                 CtdlRegisterSessionHook(xmpp_login_hook, EVT_UNSTEALTH, PRIO_UNSTEALTH + 1);
830                 CtdlRegisterSessionHook(xmpp_logout_hook, EVT_STEALTH, PRIO_STEALTH + 1);
831                 CtdlRegisterCleanupHook(xmpp_cleanup_events);
832
833         }
834
835         /* return our module name for the log */
836         return "xmpp";
837 }