root/climm/src/xmpp_base.c

Revision 2833, 54.6 kB (checked in by kuhlmann, 10 months ago)

* change xmpp logging (all xml on one line now) * react to incoming roster set iq to better sync contact list

Line 
1 /*
2  * Implements the XMPP protocol using libiksemel >= 1.2.
3  *
4  * climm Copyright (C) © 2001-2009 RÃŒdiger Kuhlmann
5  *
6  * climm is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 dated June, 1991.
9  *
10  * climm is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
13  * License for more details.
14  *
15  * In addition, as a special exception permission is granted to link the
16  * code of this release of climm with the OpenSSL project's "OpenSSL"
17  * library, and distribute the linked executables.  You must obey the GNU
18  * General Public License in all respects for all of the code used other
19  * than "OpenSSL".  If you modify this file, you may extend this exception
20  * to your version of the file, but you are not obligated to do so.  If you
21  * do not wish to do so, delete this exception statement from your version
22  * of this file.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this package; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27  * 02111-1307, USA.
28  *
29  * $Id: oscar_base.c 2670 2009-03-01 16:48:03Z kuhlmann $
30  */
31
32 //#define IKS_TRANS_USER_DATA Server
33 //#define IKS_FILTER_USER_DATA Server
34 //#define IKS_SOCK_USER_DATA Connection
35 #define IKS_TRANS_USER_DATA void
36 #define IKS_FILTER_USER_DATA void
37 #define IKS_SOCK_USER_DATA void
38
39 #include "climm.h"
40 #include <sys/types.h>
41 #include <errno.h>
42 #if HAVE_SYS_STAT_H
43 #include <sys/stat.h>
44 #endif
45 #if HAVE_WINSOCK2_H
46 #include <winsock2.h>
47 #endif
48 #include <fcntl.h>
49 #include <assert.h>
50
51 #if ENABLE_XMPP
52
53 #include <iksemel.h>
54
55 #include "xmpp_base.h"
56 #include "connection.h"
57 #include "contact.h"
58 #include "conv.h"
59 #include "oscar_dc.h"
60 #include "util_io.h"
61 #include "im_response.h"
62 #include "im_request.h"
63 #include "util_ui.h"
64 #include "util_rl.h"
65 #include "buildmark.h"
66 #include "preferences.h"
67 #include "oscar_dc_file.h"
68 #include "io/io_dns.h"
69 #include "os.h"
70
71 static jump_conn_f XMPPCallbackDispatch;
72
73 static int  iks_climm_TConnect (iksparser *prs, IKS_SOCK_USER_DATA **socketptr, const char *server, int port)
74 {
75     Server *serv = iks_stream_user_data (prs);
76     Connection *conn = serv->conn;
77     *((Connection **)socketptr) = conn;
78     return IKS_OK;
79 }
80
81 static void iks_climm_TClose (IKS_SOCK_USER_DATA *conn)
82 {
83     UtilIOClose ((Connection *)conn);
84 }
85
86 static int iks_climm_TSend (IKS_SOCK_USER_DATA *conn, const char *data, size_t len)
87 {
88     io_err_t rc = UtilIOWrite ((Connection *)conn, data, len);
89     if (rc != IO_OK)
90         return IKS_NET_RWERR;
91     return IKS_OK;
92 }
93
94 static int iks_climm_TRecv (IKS_SOCK_USER_DATA *conn, char *data, size_t len, int timeout)
95 {
96     int rc = UtilIORead ((Connection *)conn, data, len);
97     if (rc > 0)
98         return rc;
99     if (rc == IO_OK || rc == IO_CONNECTED)
100         return 0;
101     return -1;
102 }
103
104 static const ikstransport iks_climm_transport = {
105     IKS_TRANSPORT_V1,
106     iks_climm_TConnect,
107     iks_climm_TSend,
108     iks_climm_TRecv,
109     iks_climm_TClose,
110     NULL,
111 };
112
113 static void XMPPCallBackTimeout (Event *event)
114 {
115     Connection *conn = event->conn;
116
117     if (!conn)
118     {
119         EventD (event);
120         return;
121     }
122     assert (conn->serv);
123     assert (conn->serv->type == TYPE_XMPP_SERVER);
124     if (~conn->connect & CONNECT_OK)
125         rl_print ("# XMPP timeout\n");
126     EventD (event);
127 }
128
129 void XmppStreamError (Server *serv, const char *text)
130 {
131     rl_printf ("Stream level error occurred: %s [%s]\n", text, UtilIOErr (serv->conn) ? UtilIOErr (serv->conn) : "");
132     XMPPLogout (serv);
133 }
134
135 Event *XMPPLogin (Server *serv)
136 {
137     const char *scrserv, *sp;
138     char *semi;
139     Event *event;
140
141     assert (serv->type == TYPE_XMPP_SERVER);
142
143     if (!serv->screen || !serv->passwd)
144         return NULL;
145     if (!strchr (serv->screen, '@'))
146     {
147         const char *jid = s_sprintf ("%s@jabber.com", serv->screen);
148         s_repl (&serv->screen, jid);
149     }
150     scrserv = strchr (serv->screen, '@') + 1;
151     if (!serv->conn->server || !strcmp (scrserv, serv->conn->server))
152     {
153         const char *rrdns = io_dns_resolve (s_sprintf ("_xmpp-client._tcp.%s", scrserv));
154         s_repl (&serv->conn->server, rrdns ? rrdns : scrserv);
155     }
156
157     XMPPLogout (serv);
158
159     s_repl (&serv->xmpp_stamp, "YYYYmmddHHMMSS");
160     time_t now = time (NULL);
161     strftime (serv->xmpp_stamp, 15, "%Y%m%d%H%M%S", gmtime (&now));
162
163     semi = strchr (serv->conn->server, ';');
164     if (semi)
165         *semi = 0;
166     sp = s_sprintf ("%s", s_wordquote (strchr (serv->conn->server, ':') ? serv->conn->server : s_sprintf ("%s:%lu", serv->conn->server, serv->conn->port)));
167     rl_printf (i18n (2620, "Opening XMPP connection for %s at %s...\n"),
168         s_wordquote (serv->screen), sp);
169     if (semi)
170         *semi = ';';
171
172     if (!serv->conn->port)
173         serv->conn->port = ~0;
174
175     serv->conn->dispatch = &XMPPCallbackDispatch;
176     serv->conn->connect = 0;
177
178     if ((event = QueueDequeue2 (serv->conn, QUEUE_DEP_WAITLOGIN, 0, NULL)))
179     {
180         event->attempts++;
181         event->due = time (NULL) + 10 * event->attempts + 10;
182         event->callback = &XMPPCallBackTimeout;
183         QueueEnqueue (event);
184     }
185     else
186         event = QueueEnqueueData (serv->conn, QUEUE_DEP_WAITLOGIN, 0, time (NULL) + 5,
187                                   NULL, serv->conn->cont, NULL, &XMPPCallBackTimeout);
188
189     UtilIOConnectTCP (serv->conn);
190     return event;
191 }
192
193 static void XmppSaveLog (IKS_TRANS_USER_DATA *userv, const char *text, size_t size, int in)
194 {
195     Server *serv = (Server *)userv;
196     const char *data;
197     size_t rc;
198
199     data = s_sprintf ("%s%s %s%s ",
200         iks_is_secure (serv->xmpp_parser) ? "SSL """, s_now,
201         in & 1 ? "<<<" : ">>>", in & 2 ? " residual:" : "");
202
203     if (in)
204         DebugH (DEB_XMPPIN, "%s", data);
205     else
206         DebugH (DEB_XMPPOUT, "%s", data);
207
208     if (!ConnectionPrefVal (serv, CO_LOGSTREAM))
209         return;
210
211     if (serv->logfd < 0)
212     {
213         const char *dir, *file;
214         dir = s_sprintf ("%sdebug", PrefUserDir (prG));
215         mkdir (dir, 0700);
216         dir = s_sprintf ("%sdebug" _OS_PATHSEPSTR "packets.xmpp.%s", PrefUserDir (prG), serv->screen);
217         mkdir (dir, 0700);
218         file = s_sprintf ("%sdebug" _OS_PATHSEPSTR "packets.xmpp.%s/%lu", PrefUserDir (prG), serv->screen, time (NULL));
219         serv->logfd = open (file, O_WRONLY | O_CREAT | O_APPEND | O_SYNC, 0600);
220     }
221     if (serv->logfd < 0)
222         return;
223
224     rc = write (serv->logfd, data, strlen (data));
225
226     char *textnonl = strdup (text);
227     int i;
228     for (i = 0; textnonl[i]; i++)
229         if (textnonl[i] == '\n' || textnonl[i] == '\r')
230             textnonl[i] = ' ';
231     rc = write (serv->logfd, textnonl, strlen (textnonl));
232     rc = write (serv->logfd, "\n", 1);
233     free (textnonl);
234 }
235
236 static void GetBothContacts (iksid *j, Server *conn, Contact **b, Contact **f, char crea)
237 {
238     Contact *bb, *ff, **t;
239     char *jb = j->partial;
240     char *jr = j->resource;
241
242     if ((bb = ContactFindScreen (conn->contacts, jb)))
243     {
244         if (!(ff = ContactFindScreenP (conn->contacts, bb, jr ? jr : "")))
245         {
246             ff = ContactScreenP (conn, bb, jr ? jr : "");
247         }
248     }
249     else
250     {
251         bb = ContactScreen (conn, jb);
252         if (crea)
253             ContactCreate (conn, bb);
254         ff = ContactScreenP (conn, bb, jr ? jr : "");
255     }
256     assert (bb);
257     if (ff)
258     {
259         /* make ff the firstchild or the firstchild->next of bb */
260         if (!bb->firstchild)
261             bb->firstchild = ff;
262         else if (bb->firstchild != ff && bb->firstchild->next != ff)
263         {
264             for (t = &bb->firstchild; *t; t = &((*t)->next))
265                 if (*t == ff)
266                 {
267                     *t = ff->next;
268                     ff->next = bb->firstchild->next;
269                     bb->firstchild->next = ff;
270                     t = NULL;
271                     break;
272                 }
273             if (t)
274             {
275                 ff->next = bb->firstchild->next;
276                 bb->firstchild->next = ff;
277             }
278         }
279     }
280     else
281         ff = bb;
282     *b = bb;
283     *f = ff;
284 }
285
286 static iks *find_with_ns_attrib (iks *tag, const char *childname, const char *childnamespace)
287 {
288     iks *p, *a;
289     for (p = iks_first_tag (tag); p; p = iks_next_tag(p))
290     {
291         for (a = iks_attrib (p); a; a  = iks_next (a))
292         {
293             if (iks_type (a) != IKS_ATTRIBUTE)
294                 continue;
295             if (strncmp (iks_name (a), "xmlns", 5))
296                 continue;
297             if (strcmp (iks_cdata  (a), childnamespace))
298                 continue;
299             if (!iks_name (a)[5] || iks_name (a)[5] == ':')
300                 return p;
301         }
302     }
303     return NULL;
304 }
305
306 #define foreach_subtag(iter,parent,name) \
307     iks *iter; \
308     for (iter = iks_first_tag (parent); iter; iter = iks_next_tag (iter)) \
309         if (iks_type (iter) == IKS_TAG && !strcmp (iks_name (iter), name))
310
311
312 static enum ikshowtype StatusToIksstatus (status_t *status)
313 {
314     switch (*status)
315     {
316         case ims_online:   return IKS_SHOW_AVAILABLE;   break;
317         case ims_ffc:      return IKS_SHOW_CHAT;        break;
318         case ims_away:     return IKS_SHOW_AWAY;        break;
319         case ims_occ:      *status = ims_dnd;
320         case ims_dnd:      return IKS_SHOW_DND;         break;
321         case ims_na:       return IKS_SHOW_XA;          break;
322         case ims_offline:  *status = ims_inv;
323         default:           return IKS_SHOW_UNAVAILABLE; break;
324     }
325 }
326
327 /****************** send stuff ******************/
328
329 static void XmppSendIqGmail (Server *serv, int64_t newer, const char *newertid, const char *query)
330 {
331     iks *x = iks_new ("iq");
332     iks_insert_attrib (x, "type", "get");
333     iks_insert_attrib (x, "to", serv->xmpp_id->partial);
334     iks_insert_attrib (x, "id", s_sprintf ("%s-%s-%x", query ? "mailq" : "mail", serv->xmpp_stamp, serv->xmpp_sequence++));
335
336     iks *q = iks_insert (x, "query");
337     iks_insert_attrib (q, "xmlns", "google:mail:notify");
338     if (newer != 0)
339         iks_insert_attrib (q, "newer-than-time", s_sprintf ("%llu", newer));
340     if (newertid && *newertid)
341         iks_insert_attrib (q, "newer-than-tid", newertid);
342     if (query && *query)
343         iks_insert_attrib (q, "q", query);
344     iks_send (serv->xmpp_parser, x);
345     iks_delete (x);
346 }
347
348 static void XmppSendIqTime (Server *serv)
349 {
350     iks *x = iks_new ("iq");
351     iks_insert_attrib (x, "type", "get");
352     iks_insert_attrib (iks_insert (x, "time"), "xmlns", "urn:xmpp:time");
353     iks_insert_attrib (x, "id", s_sprintf ("time-%s-%x", serv->xmpp_stamp, serv->xmpp_sequence++));
354     iks_insert_attrib (x, "to", serv->xmpp_id->server);
355     iks_send (serv->xmpp_parser, x);
356     iks_delete (x);
357 }
358
359 static void sendIqGmailReqs (Event *event)
360 {
361     Server *serv;
362     if (!event->conn)
363     {
364         EventD (event);
365         return;
366     }
367     serv = event->conn->serv;
368     XmppSendIqGmail (serv, serv->xmpp_gmail_new_newer, serv->xmpp_gmail_new_newertid, NULL);
369     event->due += 300;
370     QueueEnqueue (event);
371 }
372
373 static void sendIqTimeReqs (Event *event)
374 {
375     if (!event->conn)
376     {
377         EventD (event);
378         return;
379     }
380     XmppSendIqTime (event->conn->serv);
381     event->due += 300;
382     QueueEnqueue (event);
383 }
384
385 /****************** IqHandler **********/
386
387 static int XmppHandleIqGmail (IKS_FILTER_USER_DATA *fserv, ikspak *pak)
388 {
389     Server *serv = (Server *)fserv;
390     iks *mb = find_with_ns_attrib (pak->x, "mailbox", "google:mail:notify");
391
392     if (!mb)
393         return IKS_FILTER_PASS;
394
395     int n = 0;
396     int ismail = 0;
397     char *n_s = iks_find_attrib (mb, "total-matched");
398     if (n_s)
399         n = atoi (n_s);
400
401     if (pak->id && !strncmp (pak->id, "mail-", 5))
402     {
403         ismail = 1;
404         if (n || !strcmp (pak->id + strlen (pak->id) - 2, "-0"))
405             rl_printf (i18n (2738, "Found %d new mails for %s.\n"), n, serv->xmpp_id->partial);
406     }
407     else
408         rl_printf (i18n (2739, "Found %d mails for %s.\n"), n, serv->xmpp_id->partial);
409     if (!n)
410         return IKS_FILTER_EAT;
411
412     const char *ntid = "";
413     Contact *cont = serv->conn->cont;
414     foreach_subtag (mb_c, mb, "mail-thread-info")
415     {
416         char *sub = iks_find_cdata (mb_c, "subject");
417         char *snip = iks_find_cdata (mb_c, "snippet");
418         char *dato = iks_find_attrib (mb_c, "date");
419         ntid = iks_find_attrib (mb_c, "tid");
420         time_t t = atoll (dato) / 1000ULL;
421         rl_printf ("%s ", s_time (&t));
422         rl_printf ("%s%s %s%s%s", COLMESSAGE, sub ? sub : "", COLQUOTE, COLSINGLE, snip ? snip : "");
423         rl_print ("\n");
424         foreach_subtag (mb_s_c, iks_find (mb_c, "senders"), "sender")
425         {
426             if (!ismail || (iks_find_attrib (mb_s_c, "unread") && !strcmp (iks_find_attrib (mb_s_c, "unread"), "1")))
427             {
428                 char *email = iks_find_attrib (mb_s_c, "address");
429                 char *name = iks_find_attrib (mb_s_c, "name");
430                 rl_printf ("            %s%s%s <%s%s%s>\n", COLQUOTE, name ? name : "", COLNONE, COLCONTACT, email ? email : "", COLNONE);
431             }
432         }
433     }
434     char *foundnewer = iks_find_attrib (mb, "result-time");
435     if (ismail)
436     {
437         serv->xmpp_gmail_new_newer = atoll (foundnewer);
438         s_repl (&serv->xmpp_gmail_new_newertid, ntid);
439     }
440     else
441     {
442         serv->xmpp_gmail_newer = atoll (foundnewer);
443         s_repl (&serv->xmpp_gmail_newertid, ntid);
444     }
445     return IKS_FILTER_EAT;
446 }
447
448 static int XmppHandleIqDisco (IKS_FILTER_USER_DATA *fserv, ikspak *pak)
449 {
450     Server *serv = (Server *)fserv;
451     if (pak->subtype == IKS_TYPE_RESULT)
452     {
453         /*  what did we ask for? */
454         if (!pak->from || pak->from->resource || pak->from->user || strcmp (pak->from->server, serv->xmpp_id->server))
455            return IKS_FILTER_PASS;
456
457         /* disco is from server */
458         if (iks_find_with_attrib (iks_find (pak->x, "query"), "feature", "var", "google:mail:notify"))
459         {
460             /* have Gmail, so start requesting */
461             XmppSendIqGmail (serv, 0, NULL, NULL);
462             QueueEnqueueData2 (serv->conn, QUEUE_XMPP_GMAIL, 0, 300, NULL, &sendIqGmailReqs, NULL);
463             iks_filter_add_rule (serv->xmpp_filter, XmppHandleIqGmail , serv, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_NS, "google:mail:notify", IKS_RULE_DONE);
464             return IKS_FILTER_EAT;
465         }
466     }
467     else if (pak->subtype == IKS_TYPE_GET)
468     {
469         iks *x = iks_new ("iq");
470         iks_insert_attrib (x, "type", "result");
471         iks_insert_attrib (x, "id", pak->id);
472         iks_insert_attrib (x, "to", pak->from->full);
473         iks *q = iks_insert (x, "query");
474         iks_insert_attrib (q, "xmlns", "http://jabber.org/protocol/disco#info");
475         iks_insert_attrib (q, "node", iks_find_attrib (pak->query, "node"));
476         iks *f = iks_insert (q, "feature");
477         iks_insert_attrib (f, "var", "http://jabber.org/protocol/disco#info");
478              f = iks_insert (q, "feature");
479         iks_insert_attrib (f, "var", "http://jabber.org/protocol/chatstates");
480              f = iks_insert (q, "feature");
481         iks_insert_attrib (f, "var", "jabber:iq:version");
482              f = iks_insert (q, "feature");
483         iks_insert_attrib (f, "var", "jabber:iq:last");
484         iks *i =  iks_insert (q, "identity");
485         iks_insert_attrib (i, "category", "client");
486         iks_insert_attrib (i, "type", "console");
487         iks_insert_attrib (i, "name", "climm");
488         iks_send (serv->xmpp_parser, x);
489         iks_delete (x);
490         return IKS_FILTER_EAT;
491     }
492
493     return IKS_FILTER_PASS;
494 }
495
496 static int XmppHandleIqXEP12 (IKS_FILTER_USER_DATA *fserv, ikspak *pak)
497 {
498     Server *serv = (Server *)fserv;
499     if (pak->subtype == IKS_TYPE_GET)
500     {
501         iks *x = iks_new ("iq");
502         iks_insert_attrib (x, "type", "result");
503         iks_insert_attrib (x, "id", pak->id);
504         iks_insert_attrib (x, "to", pak->from->full);
505         iks *q = iks_insert (x, "query");
506         iks_insert_attrib (q, "xmlns", "jabber:iq:last");
507         iks_insert_attrib (q, "seconds", s_sprintf ("%ld", uiG.idle_val ? os_DetermineIdleTime (time (NULL), uiG.idle_val) : 0));
508         iks_send (serv->xmpp_parser, x);
509         iks_delete (x);
510         return IKS_FILTER_EAT;
511     }
512     return IKS_FILTER_PASS;
513 }
514
515 static int XmppHandleIqXEP92 (IKS_FILTER_USER_DATA *fserv, ikspak *pak)
516 {
517     Server *serv = (Server *)fserv;
518     if (pak->subtype == IKS_TYPE_GET)
519     {
520         iks *x = iks_new ("iq");
521         iks_insert_attrib (x, "type", "result");
522         iks_insert_attrib (x, "id", pak->id);
523         iks_insert_attrib (x, "to", pak->from->full);
524         iks *q = iks_insert (x, "query");
525         iks_insert_attrib (q, "xmlns", "jabber:iq:version");
526         iks_insert_cdata (iks_insert (q, "name"), "climm", 0);
527         iks_insert_cdata (iks_insert (q, "version"), BuildVersionStr, 0);
528         iks_insert_cdata (iks_insert (q, "os"), BuildPlatformStr, 0);
529         iks_send (serv->xmpp_parser, x);
530         iks_delete (x);
531         return IKS_FILTER_EAT;
532     }
533     return IKS_FILTER_PASS;
534 }
535
536 static int XmppHandleIqRoster (IKS_FILTER_USER_DATA *fserv, ikspak *pak)
537 {
538     Server *serv = (Server *)fserv;
539     ContactGroup *cg;
540     Contact *contr, *contb;
541     int i;
542    
543     if (pak->subtype == IKS_TYPE_RESULT)
544     {
545         Contact *cont;
546         for (i = 0, cg = serv->contacts; (cont = ContactIndex (cg, i)); i++)
547         {
548             OptSetVal(&cont->copts, CO_ISSBL, 0);
549             OptSetVal(&cont->copts, CO_TO_SBL, 0);
550             OptSetVal(&cont->copts, CO_FROM_SBL, 0);
551             OptSetVal(&cont->copts, CO_ASK_SBL, 0);
552         }
553     }
554     foreach_subtag(item_c, pak->query, "item")
555     {
556         char *jid = iks_find_attrib (item_c, "jid");
557         char *subs = iks_find_attrib (item_c, "subscription");
558         char *name = iks_find_attrib (item_c, "name");
559         char *ask = iks_find_attrib (item_c, "ask");
560        
561         if (!jid || !subs)
562             continue;
563        
564         iksid *jjid = iks_id_new (iks_stack (pak->x), jid);
565         GetBothContacts (jjid, serv, &contb, &contr, 1);
566            
567         OptSetVal(&contb->copts, CO_ISSBL, 1);
568         OptSetVal(&contb->copts, CO_TO_SBL, !strcmp (subs, "both") || !strcmp (subs, "to"));
569         OptSetVal(&contb->copts, CO_FROM_SBL, !strcmp (subs, "both") || !strcmp (subs, "from"));
570         OptSetVal(&contb->copts, CO_ASK_SBL, ask && !strcmp (ask, "subscribe"));
571        
572         if (name && !ContactFindScreen (serv->contacts, name))
573             ContactAddAlias (contb, name);
574        
575         foreach_subtag(group, item_c, "group")
576         {
577             char *g = iks_cdata (group);
578             if (g && *g)
579             {
580                 cg = ContactGroupFind (serv, 0, g);
581                 if (!cg)
582                     cg = ContactGroupC (serv, 0, g);
583                 if (!ContactHas (cg, contb))
584                     ContactAdd (cg, contb);
585             }
586         }
587     }
588     return IKS_FILTER_EAT;
589 }
590
591 static int XmppHandleIqTime (IKS_FILTER_USER_DATA *fserv, ikspak *pak)
592 {
593     return IKS_FILTER_EAT;
594 }
595
596 static int XmppHandleIqDefault (IKS_FILTER_USER_DATA *fserv, ikspak *pak)
597 {
598     Server *serv = (Server *)fserv;
599     if (pak->subtype == IKS_TYPE_ERROR || pak->subtype == IKS_TYPE_RESULT)
600         return IKS_FILTER_PASS;
601     iks *x = iks_new ("iq");
602     iks_insert_attrib (x, "type", "error");
603     iks_insert_attrib (x, "id", pak->id);
604     iks *e = iks_insert (x, "error");
605     iks_insert_attrib  (e, "type", "cancel");
606     iks *t = iks_insert (e, "feature-not-implemented");
607     iks_insert_attrib  (t, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
608     iks *c = iks_tree (iks_string (iks_stack (x), iks_first_tag (x)), 0, NULL);
609     iks_insert_node (x, c);
610     iks_send (serv->xmpp_parser, x);
611     iks_delete (c);
612     iks_delete (x);
613     return IKS_FILTER_PASS;
614 }
615
616 /*********** other handlers *************/
617
618 static int __SkipChar (const char **s, char c)
619 {
620     if (**s == c && **s)
621         (*s)++;
622     if (**s)
623         return *((*s)++) - '0';
624     return 0;
625 }
626
627 static time_t ParseUTCDate (const char *s)
628 {
629    struct tm tm;
630    tm.tm_year =  __SkipChar (&s, 0) * 1000;
631    tm.tm_year += __SkipChar (&s, 0) * 100;
632    tm.tm_year += __SkipChar (&s, 0) * 10;
633    tm.tm_year += __SkipChar (&s, 0) - 1900;
634    tm.tm_mon  =  __SkipChar (&s, '-') * 10;
635    tm.tm_mon  += __SkipChar (&s, 0) - 1;
636    tm.tm_mday =  __SkipChar (&s, '-') * 10;
637    tm.tm_mday += __SkipChar (&s, 0);
638    tm.tm_hour =  __SkipChar (&s, 'T') * 10;
639    tm.tm_hour += __SkipChar (&s, 0);
640    tm.tm_min  =  __SkipChar (&s, ':') * 10;
641    tm.tm_min  += __SkipChar (&s, 0);
642    tm.tm_sec  =  __SkipChar (&s, ':') * 10;
643    if (!*s)
644        return -1;
645    tm.tm_sec  += __SkipChar (&s, 0);
646    if (*s)
647        return -1;
648     return timegm (&tm);
649 }
650
651 static time_t XmppHandleXEP91 (iks *x)
652 {
653     time_t date = NOW;
654     iks *delay;
655     if ((delay = find_with_ns_attrib (x, "x", "jabber:x:delay")))
656     {
657         struct tm;
658         // char *dfrom =
659         iks_find_attrib (delay, "from");
660         char *stamp = iks_find_attrib (delay, "stamp");
661         date = ParseUTCDate (stamp);
662 //        if (date != NOW)
663 //            DropAttrib (delay, "stamp");
664 //        DropAttrib (delay, "from");
665 //        CheckInvalid (delay);
666     }
667     return date;
668 }
669
670 static void XmppHandleXEP115 (iks *x, Contact *contr)
671 {
672     iks *caps;
673     if ((caps = find_with_ns_attrib (x, "c", "http://jabber.org/protocol/caps")))
674     {
675         char *node = iks_find_attrib (caps, "node");
676         char *ver = iks_find_attrib (caps, "ver");
677         char *ext = iks_find_attrib (caps, "ext");
678         if (!strcmp (node, "http://www.climm.org/xmpp/caps"))
679             node = "climm";
680         else if (!strcmp (node, "http://mail.google.com/xmpp/client/caps"))
681             node = "GoogleMail";
682         else if (!strcmp (node, "http://www.google.com/xmpp/client/caps"))
683             node = "GoogleTalk";
684         else if (!strcmp (node, "http://www.android.com/gtalk/client/caps"))
685             node = "Android";
686         else if (!strcmp (node, "http://pidgin.im/caps"))
687             node = "Pidgin";
688         else if (!strcmp (node, "http://gaim.sf.net/caps"))
689             node = "Gaim";
690         else if (!strcmp (node, "http://kopete.kde.org/jabber/caps"))
691             node = "Kopete";
692         else if (!strcmp (node, "http://psi-im.org/caps"))
693             node = "Psi";
694         else if (!strcmp (node, "http://miranda-im.org/caps"))
695             node = "Miranda";
696         else if (!strcmp (node, "apple:ichat:caps") || !strcmp (node, "http://www.apple.com/ichat/caps"))
697             node = "iChat";
698         else if (!strcmp (node, "http://telepathy.freedesktop.org/caps"))
699             node = "Telepathy";
700         else if (!strcmp (node, "http://talkgadget.google.com/client/caps"))
701             node = "TalkGadget";
702         else if (!strcmp (node, "http://trillian.im/caps"))
703             node = "Trillian";
704         s_repl (&contr->cap_string, ext);
705         s_repl (&contr->version, s_sprintf ("%s %s", node, ver));
706 //        DropAttrib (caps, "ver");
707 //        DropAttrib (caps, "ext");
708 //        DropAttrib (caps, "node");
709 //        CheckInvalid (caps);
710     }
711 }
712
713 static void XmppHandleXEP22a (Server *serv, iks *x, Contact *cfrom)
714 {
715     char *refid;
716     int ref = -1;
717     int_msg_t type;
718     iks *ch;
719
720     if ((refid = iks_find_cdata (x, "id")))
721     {
722 //        DropCData (tid);
723 //        CheckInvalid (tid);
724     }
725     if (refid && !strncmp (refid, "xmpp-", 5) && !strncmp (refid + 5, serv->xmpp_stamp, 14)
726         && strlen (refid) > 19 && refid[19] == '-')
727         sscanf (refid + 20, "%x", &ref);
728
729     if ((ch =  iks_find (x, "offline")))
730     {
731         type = INT_MSGOFF;
732 //        CheckInvalid (dotag);
733     }
734     else if ((ch = iks_find (x, "paused")))
735     {
736 //        CheckInvalid (dotag);
737         IMIntMsg (cfrom, NOW, ims_offline, INT_MSGNOCOMP, "");
738         ref = -1;
739     }
740     else if ((ch = iks_find (x, "delivered")))
741     {
742         type = INT_MSGACK_TYPE2;
743 //        CheckInvalid (dotag);
744     }
745     else if ((ch = iks_find (x, "displayed")))
746     {
747         type = INT_MSGDISPL;
748 //        CheckInvalid (dotag);
749     }
750     else if ((ch = iks_find (x, "composing")))
751     {
752 //        CheckInvalid (dotag);
753         IMIntMsg (cfrom, NOW, ims_offline, INT_MSGCOMP, "");
754         ref = -1;
755     }
756     else
757         ref = -1;
758
759     if (ref != -1)
760     {
761         Event *event = QueueDequeue (serv->conn, QUEUE_XMPP_RESEND_ACK, ref);
762         if (event)
763         {
764             Message *msg = (Message *)event->data;
765             assert (msg);
766             if (msg->send_message && !msg->otrinjected)
767             {
768                 msg->type = type;
769                 IMIntMsgMsg (msg, NOW, ims_offline);
770             }
771             event->attempts += 5;
772             QueueEnqueue (event);
773         }
774     }
775 //    CheckInvalid (XEP22);
776 }
777
778 static void XmppHandleXEP22c (Server *serv, iksid *from, char *tof, char *id, char *type)
779 {
780     iks *msg  = iks_make_msg (IKS_TYPE_NONE, from->full, NULL);
781     iks_insert_attrib (msg, "id", s_sprintf ("ack-%s-%x", serv->xmpp_stamp, ++serv->xmpp_sequence));
782     iks *x = iks_insert (msg, "x");
783     iks_insert_attrib (x, "xmlns", "jabber:x:event");
784     iks_insert (x, type);
785     iks_insert_cdata (iks_insert (x, "id"), id, 0);
786     iks_send (serv->xmpp_parser, msg);
787     iks_delete (msg);
788 }
789
790 static void XmppHandleXEP22b (Server *serv, iks *x, iksid *from, char *tof, char *id)
791 {
792     iks *ch;
793     if ((ch = iks_find (x, "delivered")))
794     {
795         XmppHandleXEP22c (serv, from, tof, id, "delivered");
796 //        CheckInvalid (dotag);
797     }
798     if ((ch = iks_find (x, "displayed")))
799     {
800         XmppHandleXEP22c (serv, from, tof, id, "displayed");
801 //        CheckInvalid (dotag);
802     }
803     if ((ch = iks_find (x, "composing")))
804     {
805 //        CheckInvalid (dotag);
806     }
807     if ((ch = iks_find (x, "offline")))
808     {
809 //        CheckInvalid (dotag);
810     }
811 }
812
813 static char XmppHandleXEP22 (Server *serv, iks *t, Contact *cfrom, iksid *from, char *tof, char *id)
814 {
815     char ret = 0;
816     iks *ch;
817     if ((ch = find_with_ns_attrib (t, "x", "jabber:x:event")))
818     {
819         ret = 1;
820         if (!iks_find (t, "body"))
821         {
822             XmppHandleXEP22a (serv, ch, cfrom);
823             return 1;
824         }
825         else
826             XmppHandleXEP22b (serv, ch, from, tof, id);
827     }
828     return 0;
829 }
830
831 static char XmppHandleXEP85 (Server *serv, iks *t, Contact *cfrom, iksid *from, char *tof, char *id)
832 {
833     char ret = 0;
834     iks *ch;
835     if ((ch = find_with_ns_attrib (t, "addresses", "http://jabber.org/protocol/address")))
836     {
837 //        DropAllChildsTree (address, "address");
838 //        CheckInvalid (address);
839         ret = 1;
840     }
841
842     if ((ch = find_with_ns_attrib (t, "active", "http://jabber.org/protocol/chatstates")))
843     {
844 //        CheckInvalid (active);
845         ret = 1;
846     }
847     if ((ch = find_with_ns_attrib (t, "composing", "http://jabber.org/protocol/chatstates")))
848     {
849 //        CheckInvalid (composing);
850         IMIntMsg (cfrom, NOW, ims_offline, INT_MSGCOMP, "");
851         ret = 1;
852     }
853     if ((ch = find_with_ns_attrib (t, "paused", "http://jabber.org/protocol/chatstates")))
854     {
855 //        CheckInvalid (paused);
856         IMIntMsg (cfrom, NOW, ims_offline, INT_MSGNOCOMP, "");
857         ret = 1;
858     }
859     if ((ch = find_with_ns_attrib (t, "inactive", "http://jabber.org/protocol/chatstates")))
860     {
861 //        CheckInvalid (inactive);
862         ret = 1;
863     }
864     if ((ch = find_with_ns_attrib (t, "gone", "http://jabber.org/protocol/chatstates")))
865     {
866 //        CheckInvalid (gone);
867         ret = 1;
868     }
869     if (ret && !iks_find (t, "body"))
870         return 1;
871     return 0;
872 }
873
874 static int XmppHandleMessage (IKS_FILTER_USER_DATA *fserv, ikspak *pak)
875 {
876     Server *serv = (Server *)fserv;
877 //    char *toX = iks_find_attrib (pak->x, "to");
878 //    iksid *to = iks_id_new (iks_stack (pak->x), toX);
879
880 //    Contact *cto = ContactScreen (serv, pak->tof);
881 //    std::string subtypeval = t->findAttribute ("type");
882 //    std::string body = t->body();
883 //    std::string subject = t->subject();
884 //    std::string html;
885 //    gloox::Tag *htmltag = find_with_ns_attrib (t, "html", "http://jabber.org/protocol/xhtml-im");
886 //    if (htmltag)
887 //        htmltag = find_with_ns_attrib (htmltag, "body", "http://www.w3.org/1999/xhtml");
888 //    if (htmltag)
889 //        html = htmltag->cdata();
890 //    DropAttrib (t, "type");
891     time_t delay = -1;
892     Contact *contb, *contr;
893
894     GetBothContacts (pak->from, serv, &contb, &contr, 0);
895
896 //    DropAllChilds (t, "subject");
897 //    DropAllChilds (t, "thread");
898 //    handleXEP71 (t);
899 //    handleGoogleNosave (t);
900 //    handleGoogleSig (t);
901 //    handleGoogleChatstate (t);
902 //    handleXEP136 (t);
903     delay = XmppHandleXEP91 (pak->x);
904     XmppHandleXEP115 (pak->x, contr); // entity capabilities (used also for client version)
905     if (XmppHandleXEP22 (serv, pak->x, contr, pak->from, iks_find_attrib (pak->x, "to"), pak->id))
906         return IKS_FILTER_EAT;
907     if (XmppHandleXEP85 (serv, pak->x, contr, pak->from, iks_find_attrib (pak->x, "to"), pak->id))
908         return IKS_FILTER_EAT;
909 //    DropAllChilds (t, "body");
910
911     Opt *opt = OptSetVals (NULL, CO_ORIGIN, CV_ORIGIN_v8, CO_MSGTYPE, MSG_NORM, CO_MSGTEXT, iks_find_cdata (pak->x, "body"), 0);
912
913 //    if (!subject.empty())
914 //        opt = OptSetVals (opt, CO_MSGTYPE, MSG_NORM_SUBJ, CO_SUBJECT, subject.c_str(), 0);
915 //    if (!strcmp (html.c_str(), body.c_str()))
916 //        opt = OptSetVals (opt, CO_SAMEHTML, 1);
917     IMSrvMsgFat (contr, delay, opt);
918
919 //    if (gloox::Tag *x = t->findChild ("x"))
920 //        CheckInvalid (x);
921     return IKS_FILTER_EAT;
922 }
923
924
925 static int XmppHandlePresenceErr (IKS_FILTER_USER_DATA *fserv, ikspak *pak)
926 {
927     Contact *contb, *contr;
928
929     Server *serv = (Server *)fserv;
930     GetBothContacts (pak->from, serv, &contb, &contr, 1);
931     IMOffline (contr);
932     return IKS_FILTER_EAT;
933 }
934
935 /*
936  * React to subscription requests.
937  */
938 static int XmppHandleSubscription (IKS_FILTER_USER_DATA *fserv, ikspak *pak)
939 {
940     Server *serv = (Server *)fserv;
941     Contact *contb, *contr;
942     GetBothContacts (pak->from, serv, &contb, &contr, 1);
943
944     if (pak->subtype == IKS_TYPE_SUBSCRIBE)
945         IMSrvMsg (contr, NOW, CV_ORIGIN_v8, MSG_AUTH_REQ, NULL);
946     else if (pak->subtype == IKS_TYPE_SUBSCRIBED)
947         IMSrvMsg (contr, NOW, CV_ORIGIN_v8, MSG_AUTH_GRANT, NULL);
948     else if (pak->subtype == IKS_TYPE_UNSUBSCRIBE)
949         IMSrvMsg (contr, NOW, CV_ORIGIN_v8, MSG_AUTH_DENY, NULL);
950     else if (pak->subtype == IKS_TYPE_UNSUBSCRIBED)
951         IMSrvMsg (contr, NOW, CV_ORIGIN_v8, MSG_AUTH_DONE, NULL);
952     else
953         return IKS_FILTER_PASS;
954     return IKS_FILTER_EAT;
955 }
956
957 static int XmppHandlePresence (IKS_FILTER_USER_DATA *fserv, ikspak *pak)
958 {
959     Server *serv = (Server *)fserv;
960 //    ContactGroup *tcg;
961     Contact *contb, *contr; // , *c;
962     status_t status;
963 //    std::string pri;
964     time_t delay;
965
966     GetBothContacts (pak->from, serv, &contb, &contr, 1);
967
968 //    if (gloox::Tag *priority = s->findChild ("priority"))
969 //    {
970 //        pri = priority->cdata();
971 //        DropCData (priority);
972 //        CheckInvalid (priority);
973 //    }
974
975     delay = XmppHandleXEP91 (pak->x);
976     // FIXME: do something with it!
977
978     XmppHandleXEP115 (pak->x, contr); // entity capabilities (used also for client version)
979 //    handleXEP153 (s, contb); // vcard-based avatar, nickname
980 //    handleXEP27 (s);         // OpenPGP signature (obsolete)
981 //    handleXEP8 (s);          // iq-based avatar (obsolete)
982
983     if (pak->subtype == IKS_TYPE_UNAVAILABLE)
984     {
985         status = ims_offline;
986 //        DropAttrib (s, "type");
987         IMOffline (contr);
988         return IKS_FILTER_EAT;
989     }
990     else if (pak->show == IKS_SHOW_CHAT)      status = ims_ffc;
991     else if (pak->show == IKS_SHOW_AWAY)      status = ims_away;
992     else if (pak->show == IKS_SHOW_DND)       status = ims_dnd;
993     else if (pak->show == IKS_SHOW_XA)        status = ims_na;
994     else if (pak->show == IKS_SHOW_AVAILABLE) status = ims_online;
995     else assert (0);
996
997     if (!ContactPrefVal (contb, CO_TO_SBL))
998         XMPPAuthorize (serv, contb, auth_req, "auto re-sync");
999
1000     IMOnline (contr, status, imf_none, pak->show, iks_find_cdata (pak->x, "status"));
1001     return IKS_FILTER_EAT;
1002 }
1003
1004
1005 static int XmppUnknown (IKS_FILTER_USER_DATA *fserv, ikspak *pak)
1006 {
1007     Server *serv = (Server *)fserv;
1008     XmppSaveLog (serv, iks_string (iks_stack (pak->x), pak->x), 0, 2);
1009     return IKS_FILTER_EAT;
1010 }
1011
1012 /***************** end handlers *******************/
1013
1014
1015 static void XmppLoggedIn (Server *serv)
1016 {
1017     if (serv->xmpp_filter)
1018         iks_filter_delete (serv->xmpp_filter);
1019     serv->xmpp_filter = iks_filter_new ();
1020     iks_filter_add_rule (serv->xmpp_filter, XmppUnknown, serv, IKS_RULE_DONE);
1021     iks_filter_add_rule (serv->xmpp_filter, XmppHandlePresence, serv, IKS_RULE_TYPE, IKS_PAK_PRESENCE, IKS_RULE_DONE);
1022     iks_filter_add_rule (serv->xmpp_filter, XmppHandleSubscription, serv, IKS_RULE_TYPE, IKS_PAK_S10N, IKS_RULE_DONE);
1023     iks_filter_add_rule (serv->xmpp_filter, XmppHandlePresenceErr, serv, IKS_RULE_TYPE, IKS_PAK_S10N, IKS_RULE_SUBTYPE, IKS_TYPE_ERROR, IKS_RULE_DONE);
1024     iks_filter_add_rule (serv->xmpp_filter, XmppHandleMessage, serv, IKS_RULE_TYPE, IKS_PAK_MESSAGE, IKS_RULE_DONE);
1025
1026     serv->conn->connect = CONNECT_OK | CONNECT_SELECT_R;
1027
1028     XMPPSetstatus (serv, NULL, serv->status, serv->conn->cont->status_message);
1029
1030     iks *x = iks_make_iq (IKS_TYPE_GET, IKS_NS_ROSTER);
1031     iks_insert_attrib (x, "id", s_sprintf ("roster-%s-%x", serv->xmpp_stamp, serv->xmpp_sequence++));
1032     iks_send (serv->xmpp_parser, x);
1033     iks_delete (x);
1034     iks_filter_add_rule (serv->xmpp_filter, XmppHandleIqRoster, serv, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_NS, IKS_NS_ROSTER, IKS_RULE_DONE);
1035     iks_filter_add_rule (serv->xmpp_filter, XmppHandleIqRoster, serv, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_SET, IKS_RULE_NS, IKS_NS_ROSTER, IKS_RULE_DONE);
1036
1037     QueueEnqueueData2 (serv->conn, QUEUE_SRV_KEEPALIVE, 0, 300, NULL, &sendIqTimeReqs, NULL);
1038     iks_filter_add_rule (serv->xmpp_filter, XmppHandleIqTime, serv, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, "urn:xmpp:time", IKS_RULE_DONE);
1039
1040     x = iks_make_iq (IKS_TYPE_GET, "http://jabber.org/protocol/disco#info");
1041     iks_insert_attrib (x, "id", s_sprintf ("disco-%s-%x", serv->xmpp_stamp, serv->xmpp_sequence++));
1042     iks_insert_attrib (x, "to", serv->xmpp_id->server);
1043     iks_send (serv->xmpp_parser, x);
1044     iks_delete (x);
1045     iks_filter_add_rule (serv->xmpp_filter, XmppHandleIqDisco, serv, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE);
1046
1047     iks_filter_add_rule (serv->xmpp_filter, XmppHandleIqXEP92, serv, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, "jabber:iq:version", IKS_RULE_DONE);
1048     iks_filter_add_rule (serv->xmpp_filter, XmppHandleIqXEP12, serv, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, "jabber:iq:last", IKS_RULE_DONE);
1049
1050     iks_filter_add_rule (serv->xmpp_filter, XmppHandleIqDefault, serv, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_DONE);
1051 }
1052
1053 static int XmppSessionResult (IKS_FILTER_USER_DATA *fserv, ikspak *pak)
1054 {
1055     Server *serv = (Server *)fserv;
1056     if (pak->subtype != IKS_TYPE_RESULT)
1057         XmppStreamError (serv, s_sprintf ("Couldn't get session %s.", iks_string (iks_stack (pak->x), pak->x)));
1058     else
1059         XmppLoggedIn (serv);
1060     return IKS_FILTER_EAT;
1061 }
1062
1063 static int XmppBindResult (IKS_FILTER_USER_DATA *fserv, ikspak *pak)
1064 {
1065     Server *serv = (Server *)fserv;
1066     if (pak->subtype == IKS_TYPE_RESULT)
1067     {
1068         iks *bind = iks_find_with_attrib (pak->x, "bind", "xmlns", "urn:ietf:params:xml:ns:xmpp-bind");
1069         char *newjid;
1070         if  (bind && (newjid = iks_find_cdata (bind, "jid")))
1071         {
1072             iksid *newid = iks_id_new (iks_parser_stack (serv->xmpp_parser), newjid);
1073             if (strchr (serv->screen,  '/'))
1074                 s_repl (&serv->screen, s_sprintf ("%s/%s", newid->partial, strchr (serv->screen,  '/')  + 1));
1075             else
1076                 s_repl (&serv->screen, newid->partial);
1077             if (strcmp (serv->xmpp_id->partial, newid->partial))
1078                 rl_printf ("Server changed JID from %s to %s.\n", serv->xmpp_id->partial, newid->partial);
1079             serv->xmpp_id = newid;
1080         }
1081     } else
1082         XmppStreamError (serv, s_sprintf ("Couldn't bind. %s", iks_string (iks_stack (pak->x), pak->x)));
1083     return IKS_FILTER_EAT;
1084 }
1085
1086 #if LIBIKS_VERSION < 0x0103 || defined(ENABLE_AUTOPACKAGE)
1087 static char *base64_encode (const char *data, int count)
1088 {
1089     char *base64 = iks_base64_encode (data, count);
1090     char *b64 = strdup (base64);
1091     int len = strlen (b64);
1092     iks_free (base64);
1093     while ((len % 3) && b64[len - 1] == '=')
1094       len--;
1095     b64[len] = 0;
1096     return b64;
1097 }
1098
1099 void replace_iks_start_sasl (iksparser *prs, char *user, char *password)
1100 {
1101     char *base64 = base64_encode (s_sprintf ("%c%s%c%s", 0, user, 0, password), strlen (user) + strlen (password) + 2);
1102     iks_send_raw (prs, s_sprintf ("<auth xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" mechanism=\"PLAIN\">%s</auth>", base64));
1103     free (base64);
1104 }
1105 #endif
1106
1107 static int XmppStreamHook (IKS_TRANS_USER_DATA *userv, int type, iks *node)
1108 {
1109     Server *serv = (Server *)userv;
1110     iksparser *prs = serv->xmpp_parser;
1111     switch (type)
1112     {
1113         case IKS_NODE_STOP:
1114             XmppStreamError (serv, s_sprintf ("server disconnect [%s]", iks_name (iks_first_tag (node))));
1115             if (node) iks_delete (node);
1116             return IKS_NET_DROPPED;
1117         case IKS_NODE_ERROR:
1118             XmppStreamError (serv, s_sprintf ("stream error [%s]", iks_name (iks_first_tag (node))));
1119             if (node) iks_delete (node);
1120             return IKS_NET_DROPPED;
1121         case IKS_NODE_START:
1122             // nothing to do with it
1123             break;
1124         case IKS_NODE_NORMAL:
1125             if (!strcmp ("stream:features", iks_name (node)))
1126             {
1127                 int feat = iks_stream_features (node);
1128                 if (feat & IKS_STREAM_STARTTLS && UtilIOSSLSupported() == IO_SSL_OK)
1129                     iks_send_raw (prs, "<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>");
1130                 else if (feat & IKS_STREAM_SASL_MD5)
1131                     iks_start_sasl (prs, IKS_SASL_DIGEST_MD5, serv->xmpp_id->user, serv->passwd);
1132                 else if (feat & IKS_STREAM_SASL_PLAIN)
1133 #if LIBIKS_VERSION < 0x0103 || defined(USE_AUTOPACKAGE)
1134                     replace_iks_start_sasl (prs, serv->xmpp_id->user, serv->passwd);
1135 #else
1136                     iks_start_sasl (prs, IKS_SASL_PLAIN, serv->xmpp_id->user, serv->passwd);
1137 #endif
1138                 else
1139                 {
1140                     if (feat & (IKS_STREAM_BIND | IKS_STREAM_SESSION))
1141                     {
1142                         if (serv->xmpp_filter)
1143                             iks_filter_delete (serv->xmpp_filter);
1144                         serv->xmpp_filter = iks_filter_new ();
1145                         iks_filter_add_rule (serv->xmpp_filter, XmppUnknown, serv, IKS_RULE_DONE);
1146                         if (feat & IKS_STREAM_BIND)
1147                         {
1148                             iks *t = iks_make_resource_bind (serv->xmpp_id);
1149                             iks_insert_attrib (t, "id", "bind");
1150                             iks_filter_add_rule (serv->xmpp_filter, XmppBindResult, serv,
1151                                 IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_ID, "bind", IKS_RULE_DONE);
1152                             iks_send (prs, t);
1153                             iks_delete (t);
1154                         }
1155                         if (feat & IKS_STREAM_SESSION)
1156                         {
1157                             iks *t = iks_make_session ();
1158                             iks_insert_attrib (t, "id", "auth");
1159                             iks_filter_add_rule (serv->xmpp_filter, XmppSessionResult, serv,
1160                                 IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_ID, "auth", IKS_RULE_DONE);
1161                             iks_send (prs, t);
1162                             iks_delete (t);
1163                         }
1164                     }
1165                     else
1166                         XmppLoggedIn (serv);
1167                 }
1168             }
1169             else if (strcmp ("failure", iks_name (node)) == 0)
1170                 XmppStreamError (serv, "sasl authentication failed");
1171             else if (strcmp ("success", iks_name (node)) == 0)
1172                 iks_send_header (prs, serv->xmpp_id->server);
1173             else if (strcmp ("proceed", iks_name (node)) == 0)
1174             {
1175                 io_ssl_err_t rce = UtilIOSSLOpen (serv->conn, 2);
1176                 if (rce != IO_SSL_OK)
1177                 {
1178                     XmppStreamError (serv, s_sprintf ("ssl error %d %s", rce, UtilIOErr (serv->conn)));
1179                     return IKS_NET_RWERR;
1180                 }
1181                 serv->conn->connect &= ~CONNECT_OK & ~4;
1182                 return IKS_OK;
1183             }
1184             else
1185             {
1186                 ikspak *pak = iks_packet (node);
1187                 iks_filter_packet (serv->xmpp_filter, pak);
1188             }
1189     }
1190     if (node) iks_delete (node);
1191     return IKS_OK;
1192 }
1193
1194 static void XMPPCallbackDispatch (Connection *conn)
1195 {
1196     iksparser *prs = conn->serv->xmpp_parser;
1197     io_err_t rce;
1198     char *semi;
1199     const char *sp;
1200     int rc;
1201
1202     if (!(conn->connect & (CONNECT_OK | 4)))
1203     {
1204         rc = UtilIORead (conn, NULL, 0);
1205         rce = UtilIOShowError (conn, rc);
1206         switch (rce) {
1207             case IO_RW:
1208                 semi = strchr (conn->server, ';');
1209                 if (!semi)
1210                     return;
1211                 memmove (conn->server, semi + 1, strlen (semi));
1212                 semi = strchr (conn->server, ';');
1213                 if (semi)
1214                     *semi = 0;
1215                 sp = s_sprintf ("%s", s_wordquote (strchr (conn->server, ':') ? conn->server : s_sprintf ("%s:%lu", conn->server, conn->port)));
1216                 rl_printf (i18n (2620, "Opening XMPP connection for %s at %s...\n"),
1217                     s_wordquote (conn->serv->screen), sp);
1218                 if (semi)
1219                     *semi = ';';
1220                 UtilIOConnectTCP (conn);
1221                 return;
1222             case IO_OK:
1223                 return;
1224             case IO_CONNECTED:
1225                 rl_print ("");
1226                 if (prG->verbose || (conn->serv && conn == conn->serv->conn))
1227                     if (rl_pos () > 0)
1228                          rl_print (i18n (1634, "ok.\n"));
1229
1230                 if (conn->port == 443 || conn->port == 5223)
1231                 {
1232                     io_ssl_err_t rce = UtilIOSSLOpen (conn, 2);
1233                     if (rce != IO_SSL_OK)
1234                     {
1235                         XmppStreamError (conn->serv, s_sprintf ("ssl error %d %s", rce, UtilIOErr (conn)));
1236                         EventD (QueueDequeue2 (conn, QUEUE_DEP_WAITLOGIN, 0, NULL));
1237                         return;
1238                     }
1239                 }
1240
1241                 if (!conn->serv->xmpp_parser)
1242                 {
1243                     prs = iks_stream_new (IKS_NS_CLIENT, conn->serv, &XmppStreamHook);
1244                     conn->serv->xmpp_id = iks_id_new (iks_parser_stack (prs), conn->serv->screen);
1245                     if (!conn->serv->xmpp_id->resource)
1246                         conn->serv->xmpp_id = iks_id_new (iks_parser_stack (prs), s_sprintf ("%s@%s/%s", conn->serv->xmpp_id->user, conn->serv->xmpp_id->server, "iks"));
1247                     iks_set_log_hook (prs, XmppSaveLog);
1248                 }
1249                 conn->serv->xmpp_parser = prs;
1250                 rc = iks_connect_with (prs, conn->server, conn->port, conn->serv->xmpp_id->server, &iks_climm_transport);
1251                 if (rc != IKS_OK)
1252                 {
1253                     XmppStreamError (conn->serv, "could not send stream header");
1254                     return;
1255                 }
1256                 conn->connect |= 4;
1257                 return;
1258             default:
1259                 assert (0);
1260         }
1261     }
1262     rc = iks_recv (prs, 0);
1263     if (rc != IKS_OK && conn->dispatcher)
1264         XmppStreamError (conn->serv, s_sprintf ("failing with error code %d", rc));
1265     if (!conn->dispatcher)
1266         IMCallBackReconn (conn);
1267 }
1268
1269 void XMPPLogout (Server *serv)
1270 {
1271     if (serv->xmpp_parser)
1272     {
1273         iksparser *prs = serv->xmpp_parser;
1274         serv->xmpp_parser = NULL;
1275         iks_disconnect (prs);
1276         iks_parser_delete (prs);
1277         serv->xmpp_id = NULL;
1278         serv->xmpp_filter = NULL;
1279     }
1280     QueueCancel (serv->conn);
1281     UtilIOClose (serv->conn);
1282 }
1283
1284 /* **************** */
1285
1286 static void SnacCallbackXmpp (Event *event)
1287 {
1288     Message *msg = (Message *)event->data;
1289
1290     assert (event);
1291     assert (msg);
1292     assert (event->cont);
1293
1294     if (event->attempts < 5)
1295     {
1296         if (msg->send_message && !msg->otrinjected)
1297         {
1298             msg->type = INT_MSGACK_V8;
1299 //            IMIntMsgMsg (msg, NOW, ims_offline);
1300         }
1301         event->attempts = 20;
1302         event->due = time (NULL) + 600;
1303         QueueEnqueue (event);
1304     }
1305     else
1306     {
1307         MsgD (msg);
1308         event->data = NULL;
1309         EventD (event);
1310     }
1311 }
1312
1313 static void SnacCallbackXmppCancel (Event *event)
1314 {
1315     Message *msg = (Message *)event->data;
1316     if (msg->send_message && !msg->otrinjected)
1317         rl_printf (i18n (2234, "Message %s discarded - lost session.\n"),
1318     msg->plain_message ? msg->plain_message : msg->send_message);
1319     MsgD (msg);
1320     event->data = NULL;
1321     EventD (event);
1322 }
1323
1324 UBYTE XMPPSendmsg (Server *serv, Contact *cont, Message *msg)
1325 {
1326     assert (cont);
1327     assert (msg->send_message);
1328
1329     if (~serv->conn->connect & CONNECT_OK)
1330         return RET_DEFER;
1331     if (msg->type != MSG_NORM)
1332         return RET_DEFER;
1333
1334     iks *x = iks_make_msg (IKS_TYPE_CHAT, cont->screen, msg->send_message);
1335     iks_insert_attrib (x, "id", s_sprintf ("xmpp-%s-%x", serv->xmpp_stamp, ++serv->xmpp_sequence));
1336     iks_insert_attrib (x, "from", serv->xmpp_id->full);
1337     iks *xx = iks_insert (x, "x");
1338     iks_insert_attrib (xx, "xmlns", "jabber:x:event");
1339     iks_insert (xx, "offline");
1340     iks_insert (xx, "delivered");
1341     iks_insert (xx, "displayed");
1342     iks_insert (xx, "composing");
1343     iks_send (serv->xmpp_parser, x);
1344     iks_delete (x);
1345
1346     Event *event = QueueEnqueueData2 (serv->conn, QUEUE_XMPP_RESEND_ACK, serv->xmpp_sequence, 120, msg, &SnacCallbackXmpp, &SnacCallbackXmppCancel);
1347     event->cont = cont;
1348
1349     return RET_OK;
1350 }
1351
1352 void XMPPSetstatus (Server *serv, Contact *cont, status_t status, const char *msg)
1353 {
1354     enum ikshowtype p = StatusToIksstatus (&status);
1355     iks *x = iks_make_pres (p, msg);
1356     if (p != IKS_SHOW_UNAVAILABLE)
1357     {
1358         iks *caps = iks_insert (x, "c");
1359         iks_insert_attrib (caps, "xmlns", "http://jabber.org/protocol/caps");
1360         iks_insert_attrib (caps, "node", "http://www.climm.org/xmpp/caps");
1361         iks_insert_attrib (caps, "ver", BuildVersionStr);
1362         iks_insert_cdata (iks_insert (x, "priority"), "5", 0);
1363     }
1364     if (cont)
1365         iks_insert_attrib (x, "to", cont->screen);
1366     else
1367     {
1368         s_repl (&serv->conn->cont->status_message, msg);
1369         serv->status = status;
1370         serv->nativestatus = p;
1371     }
1372     iks_send (serv->xmpp_parser, x);
1373     iks_delete (x);
1374 }
1375
1376 UBYTE XMPPSendIq (Server *serv, Contact *cont, int8_t which, const char *msg)
1377 {
1378     int err;
1379     iks *child = iks_tree (msg, 0, &err);
1380     if (!child)
1381         return RET_FAIL;
1382     iks *x = iks_new_within ("iq", iks_stack (child));
1383     iks_insert_attrib (x, "type", which ? "set" : "get");
1384     iks_insert_attrib (x, "id", s_sprintf ("user-%s-%x", serv->xmpp_stamp, ++serv->xmpp_sequence));
1385     iks_insert_attrib (x, "from", serv->xmpp_id->full);
1386     iks_insert_attrib (x, "to", cont->screen);
1387     iks_insert_node (x, child);
1388     iks_send (serv->xmpp_parser, x);
1389     iks_delete (x);
1390     return RET_OK;
1391 }
1392
1393 void XMPPAuthorize (Server *serv, Contact *cont, auth_t how, const char *msg)
1394 {
1395     while (cont->parent && cont->parent->serv == cont->serv)
1396         cont = cont->parent;
1397
1398     iks *x = iks_make_s10n (how == auth_grant ? IKS_TYPE_SUBSCRIBED
1399                           : how == auth_deny  ? IKS_TYPE_UNSUBSCRIBED
1400                           : how == auth_req   ? IKS_TYPE_SUBSCRIBE
1401                                               : IKS_TYPE_UNSUBSCRIBE,
1402                             cont->screen, msg);
1403     iks_send (serv->xmpp_parser, x);
1404     iks_delete (x);
1405 }
1406
1407 void XMPPGoogleMail (Server *serv, time_t since, const char *query)
1408 {
1409     assert (query);
1410     if (since == 1)
1411         XmppSendIqGmail (serv, serv->xmpp_gmail_newer, serv->xmpp_gmail_newertid, serv->xmpp_gmail_query);
1412     else
1413         XmppSendIqGmail (serv, since * 1000ULL, NULL, query);
1414     s_repl (&serv->xmpp_gmail_query, query);
1415 }
1416
1417 #if 0
1418 class CLIMMXMPP: public gloox::ConnectionListener, public gloox::MessageHandler,
1419     private :
1420         void handleXEP8 (gloox::Tag *t);
1421         void handleXEP27 (gloox::Tag *t);
1422         void handleXEP71 (gloox::Tag *t);
1423         void handleXEP153 (gloox::Tag *t, Contact *contr);
1424         void handleGoogleNosave (gloox::Tag *t);
1425         void handleGoogleSig (gloox::Tag *t);
1426         void handleGoogleChatstate (gloox::Tag *t);
1427         void handleXEP136 (gloox::Tag *t);
1428         virtual void handleSubscription (gloox::Stanza *stanza);
1429 };
1430
1431 static bool DropChild (gloox::Tag *s, gloox::Tag *c)
1432 {
1433     if (s->children().size())
1434         s->setCData ("");
1435     s->children().remove (c);
1436     delete c;
1437 }
1438
1439 static bool DropAttrib (gloox::Tag *s, const std::string &a)
1440 {
1441     if (s->children().size())
1442         s->setCData ("");
1443 #if defined(LIBGLOOX_VERSION) && LIBGLOOX_VERSION >= 0x000900
1444     s->attributes().remove (gloox::Tag::Attribute (a, s->findAttribute (a)));
1445 #else
1446     s->attributes().erase (a);
1447 #endif
1448 }
1449
1450 static bool DropCData (gloox::Tag *s)
1451 {
1452     s->setCData ("");
1453 }
1454
1455 static bool CheckInvalid (gloox::Tag *s)
1456 {
1457     if (!s->attributes().size() && !s->children().size() && s->cdata().empty())
1458     {
1459         if (s->parent())
1460             DropChild (s->parent(), s);
1461         return true;
1462     }
1463     return false;
1464 }
1465
1466 static void DropAllChilds (gloox::Tag *s, const std::string &a)
1467 {
1468     gloox::Tag::TagList::const_iterator it;
1469     for (it = s->children().begin(); it != s->children().end(); ++it)
1470     {
1471         if ((*it)->name() == a)
1472         {
1473             gloox::Tag *b = *it;
1474             DropCData (b);
1475             DropAttrib (b, "xml:lang");
1476             if (CheckInvalid (b))
1477                 return (DropAllChilds (s, a));
1478         }
1479     }
1480 }
1481
1482 static void DropAllChildsTree (gloox::Tag *s, const std::string &a)
1483 {
1484     gloox::Tag::TagList::const_iterator it;
1485     if (a.empty ())
1486     {
1487         s->attributes().clear();
1488         for (it = s->children().begin(); it != s->children().end(); it = s->children().begin())
1489         {
1490             gloox::Tag *b = *it;
1491             DropCData (b);
1492             DropAllChildsTree (b, "");
1493             CheckInvalid (b);
1494         }
1495     }
1496     else
1497     {
1498         for (it = s->children().begin(); it != s->children().end(); ++it)
1499         {
1500             while ((*it)->name() == a)
1501             {
1502                 gloox::Tag *b = *it;
1503                 DropCData (b);
1504                 DropAllChildsTree (b, "");
1505                 CheckInvalid (b);
1506                 it = s->children().begin();
1507             }
1508         }
1509     }
1510 }
1511
1512 void CLIMMXMPP::handleXEP8 (gloox::Tag *t)
1513 {
1514     if (gloox::Tag *avatar = find_with_ns_attrib (t, "x", "jabber:x:avatar"))
1515     {
1516         if (gloox::Tag *hash = avatar->findChild ("hash"))
1517         {
1518             DropCData (hash);
1519             CheckInvalid (hash);
1520         }
1521         CheckInvalid (avatar);
1522     }
1523 }
1524
1525 void CLIMMXMPP::handleXEP27 (gloox::Tag *t)
1526 {
1527     if (gloox::Tag *sig = find_with_ns_attrib (t, "x", "jabber:x:signed"))
1528     {
1529         DropCData (sig);
1530         CheckInvalid (sig);
1531     }
1532 }
1533
1534 void CLIMMXMPP::handleXEP71 (gloox::Tag *t)
1535 {
1536     if (gloox::Tag *xhtmlim = find_with_ns_attrib (t, "html", "http://jabber.org/protocol/xhtml-im"))
1537     {
1538         DropAllChildsTree (xhtmlim, "body");
1539         CheckInvalid (xhtmlim);
1540     }
1541 }
1542
1543 void CLIMMXMPP::handleXEP136 (gloox::Tag *t)
1544 {
1545     if (gloox::Tag *arc = find_with_ns_attrib (t, "record", "http://jabber.org/protocol/archive"))
1546     {
1547         DropAttrib (arc, "otr");
1548         CheckInvalid (arc);
1549     }
1550 }
1551
1552 void CLIMMXMPP::handleXEP153 (gloox::Tag *t, Contact *contr)
1553 {
1554     if (gloox::Tag *vcard = find_with_ns_attrib (t, "x", "vcard-temp:x:update"))
1555     {
1556         if (gloox::Tag *photo = vcard->findChild ("photo"))
1557         {
1558             DropCData (photo);
1559             CheckInvalid (photo);
1560         }
1561         if (gloox::Tag *nick = vcard->findChild ("nickname"))
1562         {
1563             std::string nickname = nick->cdata();
1564             ContactAddAlias (contr, nickname.c_str());
1565             DropCData (nick);
1566             CheckInvalid (nick);
1567         }
1568         CheckInvalid (vcard);
1569     }
1570 }
1571
1572 void CLIMMXMPP::handleGoogleNosave (gloox::Tag *t)
1573 {
1574     if (gloox::Tag *nosave = find_with_ns_attrib (t, "x", "google:nosave"))
1575     {
1576         DropAttrib (nosave, "value");
1577         CheckInvalid (nosave);
1578     }
1579 }
1580
1581 void CLIMMXMPP::handleGoogleSig (gloox::Tag *t)
1582 {
1583     if (gloox::Tag *sig = find_with_ns_attrib (t, "google-mail-signature", "google:metadata"))
1584     {
1585         DropCData (sig);
1586         CheckInvalid (sig);
1587     }
1588 }
1589
1590 void CLIMMXMPP::handleGoogleChatstate(gloox::Tag *t)
1591 {
1592     if (gloox::Tag *chat = find_with_ns_attrib (t, "active", "http://jabber.org/protocol/chatstates"))
1593         CheckInvalid (chat);
1594 }
1595
1596
1597 #if defined(LIBGLOOX_VERSION) && LIBGLOOX_VERSION >= 0x000900
1598 void CLIMMXMPP::handleMessage (gloox::Stanza *s, gloox::MessageSession *session)
1599 #else
1600 void CLIMMXMPP::handleMessage (gloox::Stanza *s)
1601 #endif
1602 {
1603     assert (s);
1604     assert (s->type() == gloox::StanzaMessage);
1605
1606 #if defined(LIBGLOOX_VERSION) && LIBGLOOX_VERSION >= 0x000900
1607     gloox::Stanza *t = new gloox::Stanza (s);
1608 #else
1609     gloox::Stanza *t = s->clone();
1610 #endif
1611
1612     if (t->subtype() == gloox::StanzaMessageError)
1613         DropAllChildsTree (t, "");
1614     else
1615         handleMessage2 (t, s->from(), s->to().full(), s->id(), s->subtype ());
1616
1617     DropAttrib (t, "from");
1618     DropAttrib (t, "to");
1619     DropAttrib (t, "id");
1620     DropAttrib (t, "xml:lang");
1621     if (t->hasAttribute ("xmlns", "jabber:client"))
1622         DropAttrib (t, "xmlns");
1623     if (!CheckInvalid (t))
1624     {
1625         std::string txml = t->xml();
1626         CLIMMXMPPSave (m_serv, txml.c_str(), 3);
1627     }
1628     delete t;
1629 }
1630
1631 void CLIMMXMPP::handlePresence (gloox::Stanza *s)
1632 {
1633     assert (s);
1634     assert (s->type() == gloox::StanzaPresence);
1635
1636 #if defined(LIBGLOOX_VERSION) && LIBGLOOX_VERSION >= 0x000900
1637     gloox::Stanza *t = new gloox::Stanza(s);
1638 #else
1639     gloox::Stanza *t = s->clone();
1640 #endif
1641
1642     if (t->subtype() == gloox::StanzaPresenceError)
1643         DropAllChildsTree (t, "");
1644     else
1645         handlePresence2 (t, s->from(), s->to(), s->status());
1646
1647     DropAttrib (t, "from");
1648     DropAttrib (t, "to");
1649     DropAttrib (t, "id");
1650     DropAttrib (t, "xml:lang");
1651     if (t->hasAttribute ("xmlns", "jabber:client"))
1652         DropAttrib (t, "xmlns");
1653     DropAllChilds (t, "status");
1654
1655     if (!CheckInvalid (t))
1656     {
1657         std::string txml = t->xml();
1658         CLIMMXMPPSave (m_serv, txml.c_str(), 3);
1659     }
1660     delete t;
1661 }
1662
1663 void CLIMMXMPP::handleSubscription (gloox::Stanza *s)
1664 {
1665     assert (s);
1666     assert (s->type() == gloox::StanzaS10n);
1667     rl_printf ("handleSubscription from:<%s> to:<%s> type:<%d> id:<%s> status:<%s> prio:<%d> body:<%s> subj:<%s> thread:<%s> pres:<%d> xml:<%s>\n",
1668                s->from().full().c_str(), s->to().full().c_str(), s->subtype(),
1669                s->id().c_str(), s->status().c_str(), s->priority(),
1670                s->body().c_str(), s->subject().c_str(),
1671 #if defined(LIBGLOOX_VERSION) && LIBGLOOX_VERSION >= 0x000900
1672                s->thread().c_str(), s->presence(),
1673 #else
1674                s->thread().c_str(), s->show(),
1675 #endif
1676                s->xml().c_str());
1677 }
1678 #endif
1679
1680 #endif
Note: See TracBrowser for help on using the browser.