1 /*
2  * xmpp.c       -- Jabber protocol handling
3  *
4  * Copyright (C) 2008-2014 Frank Zschockelt <mcabber@freakysoft.de>
5  * Copyright (C) 2005-2015 Mikael Berthe <mikael@lilotux.net>
6  * Parts come from the centericq project:
7  * Copyright (C) 2002-2005 by Konstantin Klyagin <konst@konst.org.ua>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or (at
12  * your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, see <http://www.gnu.org/licenses/>.
21  */
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include "xmpp.h"
26 #include "xmpp_helper.h"
27 #include "xmpp_iq.h"
28 #include "xmpp_iqrequest.h"
29 #include "xmpp_muc.h"
30 #include "xmpp_s10n.h"
31 #include "caps.h"
32 #include "events.h"
33 #include "histolog.h"
34 #include "hooks.h"
35 #include "otr.h"
36 #include "roster.h"
37 #include "screen.h"
38 #include "settings.h"
39 #include "utils.h"
40 #include "main.h"
41 #include "carbons.h"
42 
43 #define RECONNECTION_TIMEOUT    60L
44 
45 #ifndef LOUDMOUTH_USES_SHA256
46 #define FINGERPRINT_LENGTH      16  // old loudmouth still uses MD5 :(
47 #endif
48 
49 LmConnection* lconnection = NULL;
50 static guint AutoConnection;
51 
52 inline void update_last_use(void);
53 inline gboolean xmpp_reconnect();
54 
55 enum imstatus mystatus = offline;
56 static enum imstatus mywantedstatus = available;
57 gchar *mystatusmsg;
58 
59 char imstatus2char[imstatus_size+1] = {
60     '_', 'o', 'f', 'd', 'n', 'a', 'i', '\0'
61 };
62 
63 char *imstatus_showmap[] = {
64   "",
65   "",
66   "chat",
67   "dnd",
68   "xa",
69   "away",
70   ""
71 };
72 
73 LmMessageNode *bookmarks = NULL;
74 LmMessageNode *rosternotes = NULL;
75 
76 static struct IqHandlers
77 {
78   const gchar *xmlns;
79   LmHandleMessageFunction handler;
80 } iq_handlers[] = {
81   {NS_PING,       &handle_iq_ping},
82   {NS_VERSION,    &handle_iq_version},
83   {NS_TIME,       &handle_iq_time},
84   {NS_ROSTER,     &handle_iq_roster},
85   {NS_XMPP_TIME,  &handle_iq_time202},
86   {NS_LAST,       &handle_iq_last},
87   {NS_DISCO_INFO, &handle_iq_disco_info},
88   {NS_DISCO_ITEMS,&handle_iq_disco_items},
89   {NS_COMMANDS,   &handle_iq_commands},
90   {NS_VCARD,      &handle_iq_vcard},
91   {NULL, NULL}
92 };
93 
update_last_use(void)94 void update_last_use(void)
95 {
96   iqlast = time(NULL);
97 }
98 
xmpp_is_online(void)99 gboolean xmpp_is_online(void)
100 {
101   if (lconnection && lm_connection_is_authenticated(lconnection))
102     return TRUE;
103   else
104     return FALSE;
105 }
106 
107 // Note: the caller should check the jid is correct
xmpp_addbuddy(const char * bjid,const char * name,const char * group)108 void xmpp_addbuddy(const char *bjid, const char *name, const char *group)
109 {
110   LmMessageNode *query, *y;
111   LmMessage *iq;
112   LmMessageHandler *handler;
113   char *cleanjid;
114 
115   if (!xmpp_is_online())
116     return;
117 
118   cleanjid = jidtodisp(bjid); // Stripping resource, just in case...
119 
120   // We don't check if the jabber user already exists in the roster,
121   // because it allows to re-ask for notification.
122 
123   iq = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_IQ,
124                                     LM_MESSAGE_SUB_TYPE_SET);
125   query = lm_message_node_add_child(iq->node, "query", NULL);
126   lm_message_node_set_attribute(query, "xmlns", NS_ROSTER);
127   y = lm_message_node_add_child(query, "item", NULL);
128   lm_message_node_set_attribute(y, "jid", cleanjid);
129 
130   if (name)
131     lm_message_node_set_attribute(y, "name", name);
132 
133   if (group)
134     lm_message_node_add_child(y, "group", group);
135 
136   handler = lm_message_handler_new(handle_iq_dummy, NULL, FALSE);
137   lm_connection_send_with_reply(lconnection, iq, handler, NULL);
138   lm_message_handler_unref(handler);
139   lm_message_unref(iq);
140 
141   xmpp_send_s10n(cleanjid, LM_MESSAGE_SUB_TYPE_SUBSCRIBE);
142 
143   roster_add_user(cleanjid, name, group, ROSTER_TYPE_USER, sub_pending, -1);
144   g_free(cleanjid);
145   buddylist_defer_build();
146   scr_update_roster();
147 }
148 
xmpp_updatebuddy(const char * bjid,const char * name,const char * group)149 void xmpp_updatebuddy(const char *bjid, const char *name, const char *group)
150 {
151   LmMessage *iq;
152   LmMessageHandler *handler;
153   LmMessageNode *x;
154   char *cleanjid;
155 
156   if (!xmpp_is_online())
157     return;
158 
159   // XXX We should check name's and group's correctness
160 
161   cleanjid = jidtodisp(bjid); // Stripping resource, just in case...
162 
163   iq = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_IQ,
164                                     LM_MESSAGE_SUB_TYPE_SET);
165   x = lm_message_node_add_child(iq->node, "query", NULL);
166   lm_message_node_set_attribute(x, "xmlns", NS_ROSTER);
167   x = lm_message_node_add_child(x, "item", NULL);
168   lm_message_node_set_attribute(x, "jid", cleanjid);
169   if (name)
170     lm_message_node_set_attribute(x, "name", name);
171 
172   if (group)
173     lm_message_node_add_child(x, "group", group);
174 
175   handler = lm_message_handler_new(handle_iq_dummy, NULL, FALSE);
176   lm_connection_send_with_reply(lconnection, iq, handler, NULL);
177   lm_message_handler_unref(handler);
178   lm_message_unref(iq);
179   g_free(cleanjid);
180 }
181 
xmpp_delbuddy(const char * bjid)182 void xmpp_delbuddy(const char *bjid)
183 {
184   LmMessageNode *y, *z;
185   LmMessage *iq;
186   LmMessageHandler *handler;
187   char *cleanjid;
188 
189   if (!xmpp_is_online())
190     return;
191 
192   cleanjid = jidtodisp(bjid); // Stripping resource, just in case...
193 
194   // If the current buddy is an agent, unsubscribe from it
195   if (roster_gettype(cleanjid) == ROSTER_TYPE_AGENT) {
196     scr_LogPrint(LPRINT_LOGNORM, "Unregistering from the %s agent", cleanjid);
197 
198     iq = lm_message_new_with_sub_type(cleanjid, LM_MESSAGE_TYPE_IQ,
199                                       LM_MESSAGE_SUB_TYPE_SET);
200     y = lm_message_node_add_child(iq->node, "query", NULL);
201     lm_message_node_set_attribute(y, "xmlns", NS_REGISTER);
202     lm_message_node_add_child(y, "remove", NULL);
203     handler = lm_message_handler_new(handle_iq_dummy, NULL, FALSE);
204     lm_connection_send_with_reply(lconnection, iq, handler, NULL);
205     lm_message_handler_unref(handler);
206     lm_message_unref(iq);
207   }
208 
209   // Cancel the subscriptions
210   xmpp_send_s10n(cleanjid, LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED); // cancel "from"
211   xmpp_send_s10n(cleanjid, LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE);  // cancel "to"
212 
213   // Ask for removal from roster
214   iq = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_IQ,
215                                     LM_MESSAGE_SUB_TYPE_SET);
216 
217   y = lm_message_node_add_child(iq->node, "query", NULL);
218   lm_message_node_set_attribute(y, "xmlns", NS_ROSTER);
219   z = lm_message_node_add_child(y, "item", NULL);
220   lm_message_node_set_attributes(z,
221                                  "jid", cleanjid,
222                                  "subscription", "remove",
223                                  NULL);
224   handler = lm_message_handler_new(handle_iq_dummy, NULL, FALSE);
225   lm_connection_send_with_reply(lconnection, iq, handler, NULL);
226   lm_message_handler_unref(handler);
227   lm_message_unref(iq);
228 
229   roster_del_user(cleanjid);
230   g_free(cleanjid);
231 
232   scr_update_roster();
233 }
234 
xmpp_request(const char * fjid,enum iqreq_type reqtype)235 void xmpp_request(const char *fjid, enum iqreq_type reqtype)
236 {
237   GSList *resources, *p_res;
238   GSList *roster_elt;
239   const char *strreqtype, *xmlns;
240   gboolean vcard2user;
241 
242   if (reqtype == iqreq_version) {
243     xmlns = NS_VERSION;
244     strreqtype = "version";
245   } else if (reqtype == iqreq_time) {
246     xmlns = NS_XMPP_TIME;
247     strreqtype = "time";
248   } else if (reqtype == iqreq_last) {
249     xmlns = NS_LAST;
250     strreqtype = "last";
251   } else if (reqtype == iqreq_ping) {
252     xmlns = NS_PING;
253     strreqtype = "ping";
254   } else if (reqtype == iqreq_vcard) {
255     xmlns = NS_VCARD;
256     strreqtype = "vCard";
257   } else
258     return;
259 
260   // Is it a vCard request to a regular user?
261   // (vCard requests are sent to bare JIDs, except in MUC rooms...)
262   vcard2user = (reqtype == iqreq_vcard &&
263                 !roster_find(fjid, jidsearch, ROSTER_TYPE_ROOM));
264 
265   if (g_utf8_strchr(fjid, -1, JID_RESOURCE_SEPARATOR) || vcard2user) {
266     // This is a full JID or a vCard request to a contact
267     xmpp_iq_request(fjid, xmlns);
268     scr_LogPrint(LPRINT_NORMAL, "Sent %s request to <%s>", strreqtype, fjid);
269     return;
270   }
271 
272   // The resource has not been specified
273   roster_elt = roster_find(fjid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_ROOM);
274   if (!roster_elt) {
275     scr_LogPrint(LPRINT_NORMAL, "No known resource for <%s>...", fjid);
276     xmpp_iq_request(fjid, xmlns); // Let's send a request anyway...
277     scr_LogPrint(LPRINT_NORMAL, "Sent %s request to <%s>", strreqtype, fjid);
278     return;
279   }
280 
281   // Send a request to each resource
282   resources = buddy_getresources(roster_elt->data);
283   if (!resources) {
284     scr_LogPrint(LPRINT_NORMAL, "No known resource for <%s>...", fjid);
285     xmpp_iq_request(fjid, xmlns); // Let's send a request anyway...
286     scr_LogPrint(LPRINT_NORMAL, "Sent %s request to <%s>", strreqtype, fjid);
287   }
288   for (p_res = resources ; p_res ; p_res = g_slist_next(p_res)) {
289     gchar *fulljid;
290     fulljid = g_strdup_printf("%s/%s", fjid, (char*)p_res->data);
291     xmpp_iq_request(fulljid, xmlns);
292     scr_LogPrint(LPRINT_NORMAL, "Sent %s request to <%s>", strreqtype, fulljid);
293     g_free(fulljid);
294     g_free(p_res->data);
295   }
296   g_slist_free(resources);
297 }
298 
299 //  xmpp_send_msg(jid, text, type, subject,
300 //                otrinject, *encrypted, type_overwrite, *xep184)
301 // When encrypted is not NULL, the function set *encrypted to 1 if the
302 // message has been PGP (or OTR) -encrypted.  If encryption enforcement is set
303 // and encryption fails, *encrypted is set to -1.
304 // otrinject should be set to FALSE (unless the message already has an OTR
305 // payload, i.e. if the function is called from an otr.c routine).
xmpp_send_msg(const char * fjid,const char * text,int type,const char * subject,gboolean otrinject,gint * encrypted,LmMessageSubType type_overwrite,gpointer * xep184)306 void xmpp_send_msg(const char *fjid, const char *text, int type,
307                    const char *subject, gboolean otrinject, gint *encrypted,
308                    LmMessageSubType type_overwrite, gpointer *xep184)
309 {
310   LmMessage *x;
311   LmMessageSubType subtype;
312 #ifdef HAVE_LIBOTR
313   int otr_msg = 0;
314   char *otr_msg_string = NULL;
315 #endif
316   char *barejid;
317 #if defined HAVE_GPGME || defined XEP0085
318   const char *rname;
319   GSList *sl_buddy;
320 #endif
321 #ifdef XEP0085
322   LmMessageNode *event;
323   struct xep0085 *xep85 = NULL;
324 #endif
325   gchar *enc = NULL;
326 
327   if (encrypted)
328     *encrypted = 0;
329 
330   if (!xmpp_is_online())
331     return;
332 
333   if (!text && type == ROSTER_TYPE_USER)
334     return;
335 
336   if (type_overwrite != LM_MESSAGE_SUB_TYPE_NOT_SET)
337     subtype = type_overwrite;
338   else {
339     if (type == ROSTER_TYPE_ROOM)
340       subtype = LM_MESSAGE_SUB_TYPE_GROUPCHAT;
341     else
342       subtype = LM_MESSAGE_SUB_TYPE_CHAT;
343   }
344 
345   barejid = jidtodisp(fjid);
346 #if defined HAVE_GPGME || defined HAVE_LIBOTR || defined XEP0085
347   rname = jid_get_resource_name(fjid);
348   sl_buddy = roster_find(barejid, jidsearch, ROSTER_TYPE_USER);
349 
350 #ifdef HAVE_LIBOTR
351   if (otr_enabled() && !otrinject) {
352     if (type == ROSTER_TYPE_USER) {
353       otr_msg_string = otr_send(text, barejid, &otr_msg);
354       if (!otr_msg_string) {
355         if (encrypted)
356           *encrypted = -1;
357         goto xmpp_send_msg_return;
358       }
359       text = otr_msg_string;
360     }
361     if (otr_msg && encrypted)
362       *encrypted = ENCRYPTED_OTR;
363   }
364 #endif
365 
366 #ifdef HAVE_GPGME
367   if (type == ROSTER_TYPE_USER && sl_buddy && gpg_enabled()) {
368     if (!settings_pgp_getdisabled(barejid)) { // not disabled for this contact?
369       guint force;
370       struct pgp_data *res_pgpdata;
371       force = settings_pgp_getforce(barejid);
372       res_pgpdata = buddy_resource_pgp(sl_buddy->data, rname);
373       if (force || (res_pgpdata && res_pgpdata->sign_keyid)) {
374         /* Remote client has PGP support (we have a signature)
375          * OR encryption is enforced (force = TRUE).
376          * If the contact has a specific KeyId, we'll use it;
377          * if not, we'll use the key used for the signature.
378          * Both keys should match, in theory (cf. XEP-0027). */
379         const char *key;
380         key = settings_pgp_getkeyid(barejid);
381         if (!key && res_pgpdata)
382           key = res_pgpdata->sign_keyid;
383         if (key) {
384           int nkeys = 1;
385           const char *keys[] = { key, 0 };
386           if (carbons_enabled())
387             keys[nkeys++] = gpg_get_private_key_id();
388           enc = gpg_encrypt(text, keys, nkeys);
389         }
390         if (!enc && force) {
391           if (encrypted)
392             *encrypted = -1;
393           goto xmpp_send_msg_return;
394         }
395       }
396     }
397   }
398 #endif // HAVE_GPGME
399 
400 #endif // HAVE_GPGME || defined XEP0085
401 
402   x = lm_message_new_with_sub_type(fjid, LM_MESSAGE_TYPE_MESSAGE, subtype);
403   lm_message_node_add_child(x->node, "body",
404                             enc ? "This message is PGP-encrypted." : text);
405 
406   if (subject)
407     lm_message_node_add_child(x->node, "subject", subject);
408 
409   if (enc) {
410     LmMessageNode *y;
411     y = lm_message_node_add_child(x->node, "x", enc);
412     lm_message_node_set_attribute(y, "xmlns", NS_ENCRYPTED);
413     if (encrypted)
414       *encrypted = ENCRYPTED_PGP;
415     g_free(enc);
416   }
417 
418 #ifdef HAVE_LIBOTR
419   // We probably don't want Carbons for encrypted messages, since the other
420   // resources won't be able to decrypt them.
421   if (otr_msg && carbons_enabled())
422     lm_message_node_add_child(x->node, "private", NS_CARBONS_2);
423 #endif
424 
425   // XEP-0184: Message Receipts
426   if (sl_buddy && xep184 &&
427       caps_has_feature(buddy_resource_getcaps(sl_buddy->data, rname),
428                        NS_RECEIPTS, barejid)) {
429     lm_message_node_set_attribute(lm_message_node_add_child(x->node, "request",
430                                                             NULL),
431                                   "xmlns", NS_RECEIPTS);
432     *xep184 = g_strdup(lm_message_get_id(x));
433   }
434 
435 #ifdef XEP0085
436   // If typing notifications are disabled, we can skip all this stuff...
437   if (chatstates_disabled || type == ROSTER_TYPE_ROOM)
438     goto xmpp_send_msg_no_chatstates;
439 
440   if (sl_buddy)
441     xep85 = buddy_resource_xep85(sl_buddy->data, rname);
442 
443   /* XEP-0085 5.1
444    * "Until receiving a reply to the initial content message (or a standalone
445    * notification) from the Contact, the User MUST NOT send subsequent chat
446    * state notifications to the Contact."
447    * In our implementation support is initially "unknown", then it's "probed"
448    * and can become "ok".
449    */
450   if (xep85 && (xep85->support == CHATSTATES_SUPPORT_OK ||
451                 xep85->support == CHATSTATES_SUPPORT_UNKNOWN)) {
452     event = lm_message_node_add_child(x->node, "active", NULL);
453     lm_message_node_set_attribute(event, "xmlns", NS_CHATSTATES);
454     if (xep85->support == CHATSTATES_SUPPORT_UNKNOWN)
455       xep85->support = CHATSTATES_SUPPORT_PROBED;
456     xep85->last_state_sent = ROSTER_EVENT_ACTIVE;
457   }
458 #endif
459 
460 xmpp_send_msg_no_chatstates:
461 #ifdef WITH_DEPRECATED_STATUS_INVISIBLE
462   if (mystatus != invisible)
463 #endif
464     update_last_use();
465   lm_connection_send(lconnection, x, NULL);
466   lm_message_unref(x);
467 
468 xmpp_send_msg_return:
469 #ifdef HAVE_LIBOTR
470   g_free(otr_msg_string);
471 #endif
472   g_free(barejid);
473 }
474 
475 #ifdef XEP0085
476 //  xmpp_send_xep85_chatstate()
477 // Send a XEP-85 chatstate.
xmpp_send_xep85_chatstate(const char * bjid,const char * resname,guint state)478 static void xmpp_send_xep85_chatstate(const char *bjid, const char *resname,
479                                       guint state)
480 {
481   LmMessage *m;
482   LmMessageNode *event;
483   GSList *sl_buddy;
484   const char *chattag;
485   char *rjid, *fjid = NULL;
486   struct xep0085 *xep85 = NULL;
487 
488   if (!xmpp_is_online())
489     return;
490 
491   sl_buddy = roster_find(bjid, jidsearch, ROSTER_TYPE_USER);
492 
493   // If we have a resource name, we use it.  Else we use NULL,
494   // which hopefully will give us the most likely resource.
495   if (sl_buddy)
496     xep85 = buddy_resource_xep85(sl_buddy->data, resname);
497 
498   if (!xep85 || (xep85->support != CHATSTATES_SUPPORT_OK))
499     return;
500 
501   if (state == xep85->last_state_sent)
502     return;
503 
504   if (state == ROSTER_EVENT_ACTIVE)
505     chattag = "active";
506   else if (state == ROSTER_EVENT_COMPOSING)
507     chattag = "composing";
508   else if (state == ROSTER_EVENT_PAUSED)
509     chattag = "paused";
510   else {
511     scr_LogPrint(LPRINT_LOGNORM, "Error: unsupported XEP-85 state (%d)", state);
512     return;
513   }
514 
515   xep85->last_state_sent = state;
516 
517   if (resname)
518     fjid = g_strdup_printf("%s/%s", bjid, resname);
519 
520   rjid = resname ? fjid : (char*)bjid;
521   m = lm_message_new_with_sub_type(rjid, LM_MESSAGE_TYPE_MESSAGE,
522                                    LM_MESSAGE_SUB_TYPE_CHAT);
523 
524   event = lm_message_node_add_child(m->node, chattag, NULL);
525   lm_message_node_set_attribute(event, "xmlns", NS_CHATSTATES);
526 
527   lm_connection_send(lconnection, m, NULL);
528   lm_message_unref(m);
529 
530   g_free(fjid);
531 }
532 #endif
533 
534 //  xmpp_send_chatstate(buddy, state)
535 // Send a chatstate or event (XEP-22/85) according to the buddy's capabilities.
536 // The message is sent to one of the resources with the highest priority.
537 #if defined XEP0085
xmpp_send_chatstate(gpointer buddy,guint chatstate)538 void xmpp_send_chatstate(gpointer buddy, guint chatstate)
539 {
540   const char *bjid;
541   const char *activeres;
542   GSList *resources, *p_res, *p_next;
543   struct xep0085 *xep85 = NULL;
544 
545   bjid = buddy_getjid(buddy);
546   if (!bjid) return;
547   activeres = buddy_getactiveresource(buddy);
548 
549   /* Send the chatstate to the last resource (which should have the highest
550      priority).
551      If chatstate is "active", send an "active" state to all resources
552      which do not curently have this state.
553    */
554   resources = buddy_getresources(buddy);
555   for (p_res = resources ; p_res ; p_res = p_next) {
556     p_next = g_slist_next(p_res);
557     xep85 = buddy_resource_xep85(buddy, p_res->data);
558     if (xep85 && xep85->support == CHATSTATES_SUPPORT_OK) {
559       // If p_next is NULL, this is the highest (prio) resource, i.e.
560       // the one we are probably writing to - unless there is defined an
561       // active resource
562       if (!g_strcmp0(p_res->data, activeres) || (!p_next && !activeres) ||
563              (xep85->last_state_sent != ROSTER_EVENT_ACTIVE &&
564               chatstate == ROSTER_EVENT_ACTIVE))
565         xmpp_send_xep85_chatstate(bjid, p_res->data, chatstate);
566     }
567     g_free(p_res->data);
568   }
569   g_slist_free(resources);
570   // If the last resource had chatstates support when can return now,
571   // we don't want to send a XEP22 event.
572   if (xep85 && xep85->support == CHATSTATES_SUPPORT_OK)
573     return;
574 }
575 #endif
576 
577 
578 //  chatstates_reset_probed(fulljid)
579 // If the XEP has been probed for this contact, set it back to unknown so
580 // that we probe it again.  The parameter must be a full jid (w/ resource).
581 #if defined XEP0085
chatstates_reset_probed(const char * bare_jid,const char * resource_name)582 static void chatstates_reset_probed(const char *bare_jid,
583                                     const char *resource_name)
584 {
585   GSList *sl_buddy;
586   struct xep0085 *xep85;
587 
588   sl_buddy = roster_find(bare_jid, jidsearch, ROSTER_TYPE_USER);
589 
590   // only reset if we found the buddy and it has a resource
591   if (!sl_buddy || !resource_name)
592     return;
593 
594   xep85 = buddy_resource_xep85(sl_buddy->data, resource_name);
595 
596   if (xep85 && xep85->support == CHATSTATES_SUPPORT_PROBED)
597     xep85->support = CHATSTATES_SUPPORT_UNKNOWN;
598 }
599 #endif
600 
601 #ifdef HAVE_GPGME
602 //  keys_mismatch(key, expectedkey)
603 // Return TRUE if both keys are non-null and "expectedkey" doesn't match
604 // the end of "key".
605 // If one of the keys is null, return FALSE.
606 // If expectedkey is less than 8 bytes long, return TRUE.
607 //
608 // Example: keys_mismatch("C9940A9BB0B92210", "B0B92210") will return FALSE.
keys_mismatch(const char * key,const char * expectedkey)609 static bool keys_mismatch(const char *key, const char *expectedkey)
610 {
611   int lk, lek;
612 
613   if (!expectedkey || !key)
614     return FALSE;
615 
616   lk = strlen(key);
617   lek = strlen(expectedkey);
618 
619   // If the expectedkey is less than 8 bytes long, this is probably a
620   // user mistake so we consider it's a mismatch.
621   if (lek < 8)
622     return TRUE;
623 
624   if (lek < lk)
625     key += lk - lek;
626 
627   return strcasecmp(key, expectedkey);
628 }
629 #endif
630 
631 //  check_signature(barejid, resourcename, xmldata, text)
632 // Verify the signature (in xmldata) of "text" for the contact
633 // barejid/resourcename.
634 // xmldata is the 'jabber:x:signed' stanza.
635 // If the key id is found, the contact's PGP data are updated.
check_signature(const char * barejid,const char * rname,LmMessageNode * node,const char * text)636 static void check_signature(const char *barejid, const char *rname,
637                             LmMessageNode *node, const char *text)
638 {
639 #ifdef HAVE_GPGME
640   const char *p, *key;
641   GSList *sl_buddy;
642   struct pgp_data *res_pgpdata;
643   gpgme_sigsum_t sigsum;
644 
645   // All parameters must be valid
646   if (!(node && barejid && rname && text))
647     return;
648 
649   if (!gpg_enabled())
650     return;
651 
652   // Get the resource PGP data structure
653   sl_buddy = roster_find(barejid, jidsearch, ROSTER_TYPE_USER);
654   if (!sl_buddy)
655     return;
656   res_pgpdata = buddy_resource_pgp(sl_buddy->data, rname);
657   if (!res_pgpdata)
658     return;
659 
660   if (!node->name || strcmp(node->name, "x")) // XXX: probably useless
661     return; // We expect "<x xmlns='jabber:x:signed'>"
662 
663   // Get signature
664   p = lm_message_node_get_value(node);
665   if (!p)
666     return;
667 
668   key = gpg_verify(p, text, &sigsum);
669   if (key) {
670     const char *expectedkey;
671     char *buf;
672     g_free(res_pgpdata->sign_keyid);
673     res_pgpdata->sign_keyid = (char *)key;
674     res_pgpdata->last_sigsum = sigsum;
675     if (sigsum & GPGME_SIGSUM_RED) {
676       buf = g_strdup_printf("Bad signature from <%s/%s>", barejid, rname);
677       scr_WriteIncomingMessage(barejid, buf, 0, HBB_PREFIX_INFO, 0);
678       scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
679       g_free(buf);
680     }
681     // Verify that the key id is the one we expect.
682     expectedkey = settings_pgp_getkeyid(barejid);
683     if (keys_mismatch(key, expectedkey)) {
684       buf = g_strdup_printf("Warning: The KeyId from <%s/%s> doesn't match "
685                             "the key you set up", barejid, rname);
686       scr_WriteIncomingMessage(barejid, buf, 0, HBB_PREFIX_INFO, 0);
687       scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
688       g_free(buf);
689     }
690   }
691 #endif
692 }
693 
ssl_cb(LmSSL * ssl,LmSSLStatus status,gpointer ud)694 static LmSSLResponse ssl_cb(LmSSL *ssl, LmSSLStatus status, gpointer ud)
695 {
696   switch (status) {
697   case LM_SSL_STATUS_NO_CERT_FOUND:
698     scr_LogPrint(LPRINT_LOGNORM, "No certificate found!");
699     break;
700   case LM_SSL_STATUS_UNTRUSTED_CERT:
701     scr_LogPrint(LPRINT_LOGNORM, "Certificate is not trusted!");
702     // The user specified a fingerprint, let's wait for lm to check that
703     if (settings_opt_get("ssl_fingerprint"))
704       return LM_SSL_RESPONSE_CONTINUE;
705     break;
706   case LM_SSL_STATUS_CERT_EXPIRED:
707     scr_LogPrint(LPRINT_LOGNORM, "Certificate has expired!");
708     break;
709   case LM_SSL_STATUS_CERT_NOT_ACTIVATED:
710     scr_LogPrint(LPRINT_LOGNORM, "Certificate has not been activated!");
711     break;
712   case LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH:
713     scr_LogPrint(LPRINT_LOGNORM,
714                  "Certificate hostname does not match expected hostname!");
715     break;
716   case LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH: {
717 #ifndef LOUDMOUTH_USES_SHA256
718       char fpr[3*FINGERPRINT_LENGTH] = {0};
719       fingerprint_to_hex(lm_ssl_get_fingerprint(ssl), fpr, FINGERPRINT_LENGTH);
720 #endif
721       scr_LogPrint(LPRINT_LOGNORM,
722                 "Certificate fingerprint does not match expected fingerprint!");
723 #ifndef LOUDMOUTH_USES_SHA256
724       scr_LogPrint(LPRINT_LOGNORM, "Remote fingerprint: %s", fpr);
725 #else
726       scr_LogPrint(LPRINT_LOGNORM, "Remote fingerprint: %s", lm_ssl_get_fingerprint(ssl));
727 #endif
728 
729       scr_LogPrint(LPRINT_LOGNORM, "Expect fingerprint: %s",
730                    settings_opt_get("ssl_fingerprint"));
731 
732       return LM_SSL_RESPONSE_STOP;
733     }
734     break;
735   case LM_SSL_STATUS_GENERIC_ERROR:
736     scr_LogPrint(LPRINT_LOGNORM, "Generic SSL error!");
737     break;
738   default:
739     scr_LogPrint(LPRINT_LOGNORM, "SSL error:%d", status);
740   }
741 
742   if (settings_opt_get_int("ssl_ignore_checks"))
743     return LM_SSL_RESPONSE_CONTINUE;
744   return LM_SSL_RESPONSE_STOP;
745 }
746 
connection_auth_cb(LmConnection * connection,gboolean success,gpointer user_data)747 static void connection_auth_cb(LmConnection *connection, gboolean success,
748                                gpointer user_data)
749 {
750   LmSSL *lssl;
751   if ((lssl = lm_connection_get_ssl(connection)) != NULL) {
752 #ifndef LOUDMOUTH_USES_SHA256
753     char fpr[3*FINGERPRINT_LENGTH] = {0};
754     fingerprint_to_hex(lm_ssl_get_fingerprint(lssl), fpr, FINGERPRINT_LENGTH);
755     scr_LogPrint(LPRINT_LOGNORM, "Connection established.\n"
756                  "Remote fingerprint: %s", fpr);
757 #else
758     scr_LogPrint(LPRINT_LOGNORM, "Connection established.\n"
759                  "Remote fingerprint: %s", lm_ssl_get_fingerprint(lssl));
760 #endif
761   }
762 
763   if (success) {
764     xmpp_iq_request(NULL, NS_ROSTER);
765     xmpp_iq_request(NULL, NS_DISCO_INFO);
766     xmpp_request_storage("storage:bookmarks");
767     xmpp_request_storage("storage:rosternotes");
768 
769     /* XXX Not needed before xmpp_setprevstatus()
770     LmMessage *m;
771     m = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_PRESENCE,
772                                      LM_MESSAGE_SUB_TYPE_AVAILABLE);
773     lm_connection_send(connection, m, NULL);
774     lm_message_unref(m);
775     */
776 
777     xmpp_setprevstatus();
778 
779     AutoConnection = TRUE;
780   } else
781     scr_LogPrint(LPRINT_LOGNORM, "Authentication failed");
782 }
783 
xmpp_reconnect()784 gboolean xmpp_reconnect()
785 {
786   if (!lconnection)
787     xmpp_connect();
788   return FALSE;
789 }
790 
_try_to_reconnect(void)791 static void _try_to_reconnect(void)
792 {
793   xmpp_disconnect();
794   if (AutoConnection)
795     g_timeout_add_seconds(RECONNECTION_TIMEOUT + (random() % 90L),
796                           xmpp_reconnect, NULL);
797 }
798 
connection_open_cb(LmConnection * connection,gboolean success,gpointer user_data)799 static void connection_open_cb(LmConnection *connection, gboolean success,
800                                gpointer user_data)
801 {
802   GError *error = NULL;
803 
804   if (success) {
805     const char *password, *resource;
806     char *username;
807     username   = jid_get_username(settings_opt_get("jid"));
808     password   = settings_opt_get("password");
809     resource   = jid_get_resource_name(lm_connection_get_jid(connection));
810 
811     if (!lm_connection_authenticate(lconnection, username, password, resource,
812                                     connection_auth_cb, NULL, FALSE, &error)) {
813       scr_LogPrint(LPRINT_LOGNORM, "Failed to authenticate: %s",
814                    error->message);
815       g_error_free (error);
816       _try_to_reconnect();
817     }
818     g_free(username);
819   } else {
820     scr_LogPrint(LPRINT_LOGNORM, "There was an error while connecting.");
821     _try_to_reconnect();
822   }
823 }
824 
connection_close_cb(LmConnection * connection,LmDisconnectReason reason,gpointer user_data)825 static void connection_close_cb(LmConnection *connection,
826                                 LmDisconnectReason reason,
827                                 gpointer user_data)
828 {
829   const char *str;
830 
831   switch (reason) {
832   case LM_DISCONNECT_REASON_OK:
833           str = "LM_DISCONNECT_REASON_OK";
834           break;
835   case LM_DISCONNECT_REASON_PING_TIME_OUT:
836           str = "LM_DISCONNECT_REASON_PING_TIME_OUT";
837           break;
838   case LM_DISCONNECT_REASON_HUP:
839           str = "LM_DISCONNECT_REASON_HUP";
840           break;
841   case LM_DISCONNECT_REASON_ERROR:
842           str = "LM_DISCONNECT_REASON_ERROR";
843           break;
844   case LM_DISCONNECT_REASON_UNKNOWN:
845   default:
846           str = "LM_DISCONNECT_REASON_UNKNOWN";
847           break;
848   }
849 
850   if (reason != LM_DISCONNECT_REASON_OK)
851     _try_to_reconnect();
852 
853   // Free bookmarks
854   if (bookmarks)
855     lm_message_node_unref(bookmarks);
856   bookmarks = NULL;
857   // Free roster
858   roster_free();
859   if (rosternotes)
860     lm_message_node_unref(rosternotes);
861   rosternotes = NULL;
862   // Reset carbons
863   carbons_reset();
864   // Update display
865   scr_update_roster();
866   scr_update_buddy_window();
867 
868   if (!reason)
869     scr_LogPrint(LPRINT_LOGNORM, "Disconnected.");
870   else
871     scr_LogPrint(LPRINT_NORMAL, "Disconnected, reason: %d->'%s'", reason, str);
872 
873   xmpp_setstatus(offline, NULL, mystatusmsg, TRUE);
874 }
875 
handle_state_events(const char * bjid,const char * resource,LmMessageNode * node)876 static void handle_state_events(const char *bjid,
877                                 const char *resource,
878                                 LmMessageNode *node)
879 {
880 #if defined XEP0085
881   LmMessageNode *state_ns = NULL;
882   GSList *sl_buddy;
883   struct xep0085 *xep85 = NULL;
884 
885   sl_buddy = roster_find(bjid, jidsearch, ROSTER_TYPE_USER);
886 
887   if (!sl_buddy) {
888     return;
889   }
890 
891   /* Let's see whether the contact supports XEP0085 */
892   xep85 = buddy_resource_xep85(sl_buddy->data, resource);
893   if (xep85)
894     state_ns = lm_message_node_find_xmlns(node, NS_CHATSTATES);
895 
896   if (!state_ns) { /* Sender does not use chat states */
897     return;
898   }
899 
900   xep85->support = CHATSTATES_SUPPORT_OK;
901 
902   if (!strcmp(state_ns->name, "composing")) {
903     xep85->last_state_rcvd = ROSTER_EVENT_COMPOSING;
904   } else if (!strcmp(state_ns->name, "active")) {
905     xep85->last_state_rcvd = ROSTER_EVENT_ACTIVE;
906   } else if (!strcmp(state_ns->name, "paused")) {
907     xep85->last_state_rcvd = ROSTER_EVENT_PAUSED;
908   } else if (!strcmp(state_ns->name, "inactive")) {
909     xep85->last_state_rcvd = ROSTER_EVENT_INACTIVE;
910   } else if (!strcmp(state_ns->name, "gone")) {
911     xep85->last_state_rcvd = ROSTER_EVENT_GONE;
912   }
913 
914   buddy_resource_setevents(sl_buddy->data, resource, xep85->last_state_rcvd);
915   scr_update_roster();
916 #endif
917 }
918 
gotmessage(LmMessageSubType type,const char * from,const char * body,const char * enc,const char * subject,time_t timestamp,LmMessageNode * node_signed,gboolean carbon)919 static void gotmessage(LmMessageSubType type, const char *from,
920                        const char *body, const char *enc,
921                        const char *subject, time_t timestamp,
922                        LmMessageNode *node_signed, gboolean carbon)
923 {
924   char *bjid;
925   const char *rname;
926   char *decrypted_pgp = NULL;
927   char *decrypted_otr = NULL;
928   int otr_msg = 0, free_msg = 0;
929 
930   bjid = jidtodisp(from);
931   rname = jid_get_resource_name(from);
932 
933 #ifdef HAVE_GPGME
934   if (gpg_enabled()) {
935     if (enc) {
936       decrypted_pgp = gpg_decrypt(enc);
937       if (decrypted_pgp)
938         body = decrypted_pgp;
939     }
940     // Check signature of the unencrypted/decrypted message
941     if (node_signed)
942       check_signature(bjid, rname, node_signed, body);
943   }
944 #endif
945 
946   // Check for unexpected groupchat messages
947   // If we receive a groupchat message from a room we're not a member of,
948   // this is probably a server issue and the best we can do is to send
949   // a type unavailable.
950   if (type == LM_MESSAGE_SUB_TYPE_GROUPCHAT && !roster_getnickname(bjid)) {
951     // It shouldn't happen, probably a server issue
952     GSList *room_elt;
953     char *mbuf;
954 
955     mbuf = g_strdup_printf("Unexpected groupchat packet!");
956     scr_LogPrint(LPRINT_LOGNORM, "%s", mbuf);
957     scr_WriteIncomingMessage(bjid, mbuf, 0, HBB_PREFIX_INFO, 0);
958     g_free(mbuf);
959 
960     // Send back an unavailable packet
961     xmpp_setstatus(offline, bjid, "", TRUE);
962 
963     // MUC
964     // Make sure this is a room (it can be a conversion user->room)
965     room_elt = roster_find(bjid, jidsearch, 0);
966     if (!room_elt) {
967       roster_add_user(bjid, NULL, NULL, ROSTER_TYPE_ROOM, sub_none, -1);
968     } else {
969       buddy_settype(room_elt->data, ROSTER_TYPE_ROOM);
970     }
971 
972     buddylist_defer_build();
973     scr_update_roster();
974     goto gotmessage_return;
975   }
976 
977   if (subject && LM_MESSAGE_SUB_TYPE_GROUPCHAT == type) {
978     // Room topic
979     GSList *roombuddy;
980     gchar *mbuf;
981     // Set the new topic
982     roombuddy = roster_find(bjid, jidsearch, 0);
983     if (roombuddy)
984       buddy_settopic(roombuddy->data, subject);
985     // Display inside the room window
986     if (!rname) {
987       // No specific resource (this is certainly history)
988       if (*subject)
989         mbuf = g_strdup_printf("The topic has been set to: %s", subject);
990       else
991         mbuf = g_strdup_printf("The topic has been cleared");
992     } else {
993       if (*subject)
994         mbuf = g_strdup_printf("%s has set the topic to: %s", rname, subject);
995       else
996         mbuf = g_strdup_printf("%s has cleared the topic", rname);
997     }
998     scr_WriteIncomingMessage(bjid, mbuf, timestamp,
999                              HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0);
1000     if (settings_opt_get_int("log_muc_conf"))
1001       hlog_write_message(bjid, 0, -1, mbuf);
1002     g_free(mbuf);
1003     // The topic is displayed in the chat status line, so refresh now.
1004     scr_update_chat_status(TRUE);
1005   }
1006 
1007   // We don't call the message_in hook if 'block_unsubscribed' is true and
1008   // this is a regular message from an unsubscribed user.
1009   // System messages (from our server) are allowed.
1010   if (settings_opt_get_int("block_unsubscribed") &&
1011       (roster_gettype(bjid) != ROSTER_TYPE_ROOM) &&
1012       !(roster_getsubscription(bjid) & sub_from) &&
1013       (type != LM_MESSAGE_SUB_TYPE_GROUPCHAT)) {
1014     char *sbjid = jidtodisp(lm_connection_get_jid(lconnection));
1015     const char *server = strchr(sbjid, JID_DOMAIN_SEPARATOR);
1016     if (server && g_strcmp0(server+1, bjid)) {
1017       scr_LogPrint(LPRINT_LOGNORM, "Blocked a message from <%s>", bjid);
1018       g_free(sbjid);
1019       goto gotmessage_return;
1020     }
1021     g_free(sbjid);
1022   }
1023 
1024 #ifdef HAVE_LIBOTR
1025   if (otr_enabled()) {
1026     decrypted_otr = (char*)body;
1027     otr_msg = otr_receive(&decrypted_otr, bjid, &free_msg);
1028     if (!decrypted_otr) {
1029       goto gotmessage_return;
1030     }
1031     body = decrypted_otr;
1032   }
1033 #endif
1034 
1035   { // format and pass message for further processing
1036     gchar *fullbody = NULL;
1037     guint encrypted;
1038 
1039     if (decrypted_pgp)
1040       encrypted = ENCRYPTED_PGP;
1041     else if (otr_msg)
1042       encrypted = ENCRYPTED_OTR;
1043     else
1044       encrypted = 0;
1045 
1046     if (subject) {
1047       if (body)
1048         fullbody = g_strdup_printf("[%s]\n%s", subject, body);
1049       else
1050         fullbody = g_strdup_printf("[%s]\n", subject);
1051       body = fullbody;
1052     }
1053     hk_message_in(bjid, rname, timestamp, body, type, encrypted, carbon);
1054     g_free(fullbody);
1055   }
1056 
1057 gotmessage_return:
1058   // Clean up and exit
1059   g_free(bjid);
1060   g_free(decrypted_pgp);
1061   if (free_msg)
1062     g_free(decrypted_otr);
1063 }
1064 
1065 
handle_messages(LmMessageHandler * handler,LmConnection * connection,LmMessage * m,gpointer user_data)1066 static LmHandlerResult handle_messages(LmMessageHandler *handler,
1067                                        LmConnection *connection,
1068                                        LmMessage *m, gpointer user_data)
1069 {
1070   const char *from = lm_message_get_from(m);
1071   char *bjid;
1072   const char *res;
1073   LmMessageNode *x;
1074   LmMessageNode *message_node = m->node;
1075   const char *body = NULL;
1076   const char *enc = NULL;
1077   const char *subject = NULL;
1078   time_t timestamp = 0L;
1079   LmMessageSubType mstype;
1080   LmMessageNode *ns_signed = NULL;
1081 
1082   if (!from) {
1083     scr_LogPrint(LPRINT_DEBUG, "handle_messages: message with missing from attribute");
1084     return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
1085   }
1086 
1087   // Get the bare-JID/room (bjid) and the resource/nickname (res)
1088   bjid = jidtodisp(from);
1089   res = jid_get_resource_name(from);
1090 
1091   mstype = lm_message_get_sub_type(m);
1092   // Timestamp?
1093   timestamp = lm_message_node_get_timestamp(message_node);
1094 
1095   if (mstype == LM_MESSAGE_SUB_TYPE_ERROR) {
1096     x = lm_message_node_get_child(message_node, "error");
1097     display_server_error(x, from);
1098 #ifdef XEP0085
1099     // If the XEP85/22 support is probed, set it back to unknown so that
1100     // we probe it again.
1101     chatstates_reset_probed(bjid, res);
1102 #endif
1103   } else {
1104     handle_state_events(bjid, res, message_node);
1105   }
1106 
1107   // Check for carbons!
1108   x = lm_message_node_find_xmlns(message_node, NS_CARBONS_2);
1109   gboolean carbons = FALSE;
1110   if (x && (!g_strcmp0(x->name, "received") || !g_strcmp0(x->name, "sent"))) {
1111     LmMessageNode *xenc;
1112     const char *carbon_name = x->name;
1113     carbons = TRUE;
1114 
1115     // Check envelope JID for carbon messages
1116     if (!jid_equal(lm_connection_get_jid(lconnection), bjid)) {
1117       scr_LogPrint(LPRINT_LOGNORM, "Received invalid carbon copy from %s.", bjid);
1118       goto handle_messages_return;
1119     }
1120 
1121     // Go 1 level deeper to the forwarded message
1122     x = lm_message_node_find_xmlns(x, NS_FORWARD);
1123     if (x)
1124       x = lm_message_node_get_child(x, "message");
1125 
1126     if (!x) {
1127       scr_LogPrint(LPRINT_LOGNORM,
1128                    "Could not read carbon message!  Please file a bug.");
1129       goto handle_messages_return;
1130     }
1131 
1132     // We should now consider the forwarded node...
1133     message_node = x;
1134 
1135     xenc = lm_message_node_find_xmlns(message_node, NS_ENCRYPTED);
1136     if (xenc)
1137       enc = lm_message_node_get_value(xenc);
1138 
1139     body = lm_message_node_get_child_value(message_node, "body");
1140     subject = lm_message_node_get_child_value(message_node, "subject");
1141     ns_signed = lm_message_node_find_xmlns(message_node, NS_SIGNED);
1142     const char *to = lm_message_node_get_attribute(message_node, "to");
1143     from = lm_message_node_get_attribute(message_node, "from");
1144     if (!from) {
1145       scr_LogPrint(LPRINT_LOGNORM, "Malformed carbon copy (missing 'from' attribute).");
1146       goto handle_messages_return;
1147     }
1148     if (!to) {
1149       scr_LogPrint(LPRINT_LOGNORM, "Malformed carbon copy (missing 'to' attribute).");
1150       goto handle_messages_return;
1151     }
1152 
1153     // Parse a message that is sent to one of our other resources
1154     if (!g_strcmp0(carbon_name, "received")) {
1155       g_free(bjid);
1156       bjid = jidtodisp(from);
1157       res = jid_get_resource_name(from);
1158 
1159       // Try to handle forwarded chat state messages
1160       handle_state_events(from, res, message_node);
1161 
1162       scr_LogPrint(LPRINT_DEBUG, "Received incoming carbon from <%s>", from);
1163 
1164     } else if (!g_strcmp0(carbon_name, "sent")) {
1165 #ifdef HAVE_GPGME
1166       char *decrypted_pgp = NULL;
1167 #endif
1168       guint encrypted = 0;
1169 
1170       g_free(bjid);
1171       bjid = jidtodisp(to);
1172 
1173 #ifdef HAVE_GPGME
1174       if (gpg_enabled()) {
1175         if (enc) {
1176           decrypted_pgp = gpg_decrypt(enc);
1177           if (decrypted_pgp) {
1178             body = decrypted_pgp;
1179             encrypted = ENCRYPTED_PGP;
1180           }
1181         }
1182         /*
1183         // Check messsage signature
1184         // This won't work here, since check_signature wasn't intended
1185         // to be used to check our own messages.
1186         if (ns_signed)
1187           check_signature(ME, NULL, ns_signed, body);
1188         */
1189       }
1190 #endif
1191 
1192       if (body && *body)
1193         hk_message_out(bjid, NULL, timestamp, body, encrypted, TRUE, NULL);
1194 
1195       scr_LogPrint(LPRINT_DEBUG, "Received outgoing carbon for <%s>", to);
1196 #ifdef HAVE_GPGME
1197       g_free(decrypted_pgp);
1198 #endif
1199       goto handle_messages_return;
1200     }
1201   } else { // Not a Carbon
1202     subject = lm_message_node_get_child_value(message_node, "subject");
1203     body = lm_message_node_get_child_value(message_node, "body");
1204     x = lm_message_node_find_xmlns(message_node, NS_ENCRYPTED);
1205     if (x)
1206       enc = lm_message_node_get_value(x);
1207     ns_signed = lm_message_node_find_xmlns(message_node, NS_SIGNED);
1208   }
1209 
1210   // Only process messages that have a body or a subject
1211   if (body || subject) {
1212     gotmessage(mstype, from, body, enc, subject, timestamp,
1213                ns_signed, carbons);
1214   }
1215 
1216   // Handle XEP 184
1217   {
1218     LmMessageNode *xep184 = lm_message_node_find_xmlns(message_node, NS_RECEIPTS);
1219     if (xep184) {
1220       if(!g_strcmp0("request", xep184->name)
1221          && !jid_equal(lm_connection_get_jid(lconnection), from)
1222          && (roster_getsubscription(bjid) & sub_from)) {
1223         // Report received message if message delivery receipt was requested
1224         const gchar *mid;
1225         LmMessageNode *y;
1226         LmMessage *rcvd = lm_message_new(from, LM_MESSAGE_TYPE_MESSAGE);
1227         mid = lm_message_node_get_attribute(message_node, "id");
1228         // For backward compatibility (XEP184 < v.1.1):
1229         lm_message_node_set_attribute(rcvd->node, "id", mid);
1230         y = lm_message_node_add_child(rcvd->node, "received", NULL);
1231         lm_message_node_set_attribute(y, "xmlns", NS_RECEIPTS);
1232         lm_message_node_set_attribute(y, "id", mid);
1233         lm_connection_send(connection, rcvd, NULL);
1234         lm_message_unref(rcvd);
1235       } else if(!g_strcmp0("received", xep184->name)) {
1236         // receipt acknowledged
1237         const char *id  = lm_message_node_get_attribute(xep184, "id");
1238         // This is for backward compatibility; if the remote client didn't add
1239         // the id as an attribute of the 'received' tag, we use the message id:
1240         if (!id)
1241           id = lm_message_node_get_attribute(message_node, "id");
1242         scr_remove_receipt_flag(bjid, id);
1243 
1244 #ifdef MODULES_ENABLE
1245         {
1246           hk_arg_t args[] = {
1247             { "jid", from },
1248             { NULL, NULL },
1249           };
1250           hk_run_handlers("hook-mdr-received", args);
1251         }
1252 #endif
1253       }
1254     }
1255   }
1256 
1257   {
1258     LmMessageNode *muc_message = lm_message_node_find_xmlns(message_node, NS_MUC_USER);
1259     if (muc_message && !strcmp(muc_message->name, "x"))
1260       got_muc_message(from, muc_message, timestamp);
1261   }
1262 
1263   {
1264     LmMessageNode *muc_invite = lm_message_node_find_xmlns(message_node, NS_X_CONFERENCE);
1265 
1266     if (muc_invite && !strcmp(muc_invite->name, "x")) {
1267       const char *jid = lm_message_node_get_attribute(muc_invite, "jid");
1268       if (jid) {
1269         const char *reason = lm_message_node_get_attribute(muc_invite, "reason");
1270         const char *password = lm_message_node_get_attribute(muc_invite, "password");
1271         // We won't send decline stanzas as it is a Direct Invitation
1272         got_invite(from, jid, reason, password, FALSE);
1273       }
1274     }
1275   }
1276 
1277 handle_messages_return:
1278   g_free(bjid);
1279   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
1280 }
1281 
cb_caps(LmMessageHandler * h,LmConnection * c,LmMessage * m,gpointer user_data)1282 static LmHandlerResult cb_caps(LmMessageHandler *h, LmConnection *c,
1283                                LmMessage *m, gpointer user_data)
1284 {
1285   char *ver = user_data;
1286   char *hash;
1287   const char *from = lm_message_get_from(m);
1288   char *bjid = jidtodisp(from);
1289   LmMessageSubType mstype = lm_message_get_sub_type(m);
1290 
1291   hash = strchr(ver, ',');
1292   if (hash)
1293     *hash++ = '\0';
1294 
1295   if (mstype == LM_MESSAGE_SUB_TYPE_RESULT) {
1296     LmMessageNode *info;
1297     LmMessageNode *query = lm_message_node_get_child(m->node, "query");
1298 
1299     if (caps_has_hash(ver, bjid) || !query)
1300       goto caps_callback_return;
1301 
1302     caps_add(ver);
1303 
1304     info = lm_message_node_get_child(query, "identity");
1305     while (info) {
1306       if (!g_strcmp0(info->name, "identity"))
1307         caps_add_identity(ver, lm_message_node_get_attribute(info, "category"),
1308                           lm_message_node_get_attribute(info, "name"),
1309                           lm_message_node_get_attribute(info, "type"),
1310                           lm_message_node_get_attribute(info, "xml:lang"));
1311       info = info->next;
1312     }
1313 
1314     info = lm_message_node_get_child(query, "feature");
1315     while (info) {
1316       if (!g_strcmp0(info->name, "feature"))
1317         caps_add_feature(ver, lm_message_node_get_attribute(info, "var"));
1318       info = info->next;
1319     }
1320 
1321     info = lm_message_node_get_child(query, "x");
1322     {
1323       LmMessageNode *field;
1324       LmMessageNode *value;
1325       const char *formtype, *var;
1326       while (info) {
1327         if (!g_strcmp0(info->name, "x")
1328             && !g_strcmp0(lm_message_node_get_attribute(info, "type"),
1329                           "result")
1330             && !g_strcmp0(lm_message_node_get_attribute(info, "xmlns"),
1331                           "jabber:x:data")) {
1332           field = lm_message_node_get_child(info, "field");
1333           formtype = NULL;
1334           while (field) {
1335             if (!g_strcmp0(field->name, "field")
1336                 && !g_strcmp0(lm_message_node_get_attribute(field, "var"),
1337                               "FORM_TYPE")
1338                 && !g_strcmp0(lm_message_node_get_attribute(field, "type"),
1339                               "hidden")) {
1340               value = lm_message_node_get_child(field, "value");
1341               if (value)
1342                 formtype = lm_message_node_get_value(value);
1343             }
1344             field = field->next;
1345           }
1346           if (formtype) {
1347             caps_add_dataform(ver, formtype);
1348             field = lm_message_node_get_child(info, "field");
1349             while (field) {
1350               var = lm_message_node_get_attribute(field, "var");
1351               if (!g_strcmp0(field->name, "field")
1352                   && (g_strcmp0(var, "FORM_TYPE")
1353                   || g_strcmp0(lm_message_node_get_attribute(field, "type"),
1354                                "hidden"))) {
1355                 value = lm_message_node_get_child(field, "value");
1356                 while (value) {
1357                   if (!g_strcmp0(value->name, "value"))
1358                     caps_add_dataform_field(ver, formtype, var,
1359                       lm_message_node_get_value(value));
1360                   value = value->next;
1361                 }
1362               }
1363               field = field->next;
1364             }
1365           }
1366         }
1367         info = info->next;
1368       }
1369     }
1370 
1371     if (caps_verify(ver, hash))
1372       caps_copy_to_persistent(ver, lm_message_node_to_string(query));
1373     else
1374       caps_move_to_local(ver, bjid);
1375   }
1376 
1377 caps_callback_return:
1378   g_free(bjid);
1379   g_free(ver);
1380   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
1381 }
1382 
handle_presence(LmMessageHandler * handler,LmConnection * connection,LmMessage * m,gpointer user_data)1383 static LmHandlerResult handle_presence(LmMessageHandler *handler,
1384                                        LmConnection *connection,
1385                                        LmMessage *m, gpointer user_data)
1386 {
1387   char *bjid;
1388   const char *from, *rname, *p=NULL, *ustmsg=NULL;
1389   enum imstatus ust;
1390   char bpprio;
1391   time_t timestamp = 0L;
1392   LmMessageNode *muc_packet, *caps;
1393   LmMessageSubType mstype = lm_message_get_sub_type(m);
1394 
1395   from = lm_message_get_from(m);
1396   if (!from) {
1397     scr_LogPrint(LPRINT_LOGNORM, "Unexpected presence packet!");
1398 
1399     if (mstype == LM_MESSAGE_SUB_TYPE_ERROR) {
1400       display_server_error(lm_message_node_get_child(m->node, "error"),
1401                            lm_message_get_from(m));
1402       return LM_HANDLER_RESULT_REMOVE_MESSAGE;
1403     }
1404     return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
1405   }
1406 
1407   if (settings_opt_get_int("ignore_self_presence")) {
1408     const char *self_fjid = lm_connection_get_jid(connection);
1409     if (self_fjid && !strcasecmp(self_fjid, from)) {
1410       return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; // Ignoring self presence
1411     }
1412   }
1413 
1414   bjid = jidtodisp(from);
1415 
1416   if (mstype == LM_MESSAGE_SUB_TYPE_ERROR) {
1417     LmMessageNode *x;
1418     scr_LogPrint(LPRINT_LOGNORM, "Error presence packet from <%s>", bjid);
1419     x = lm_message_node_get_child(m->node, "error");
1420     display_server_error(x, from);
1421     // Let's check it isn't a nickname conflict.
1422     // XXX Note: We should handle the <conflict/> string condition.
1423     if ((p = lm_message_node_get_attribute(x, "code")) != NULL) {
1424       if (atoi(p) == 409) {
1425         // 409 = conflict (nickname is in use or registered by another user)
1426         // If we are not inside this room, we should reset the nickname
1427         GSList *room_elt = roster_find(bjid, jidsearch, 0);
1428         if (room_elt && !buddy_getinsideroom(room_elt->data))
1429           buddy_setnickname(room_elt->data, NULL);
1430       }
1431     }
1432 
1433     g_free(bjid);
1434     return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
1435   }
1436 
1437   p = lm_message_node_get_child_value(m->node, "priority");
1438   if (p && *p) {
1439     int rawprio = atoi(p);
1440     if (rawprio > 127)
1441       bpprio = 127;
1442     else if (rawprio < -128)
1443       bpprio = -128;
1444     else
1445       bpprio = rawprio;
1446   } else {
1447     bpprio = 0;
1448   }
1449 
1450   ust = available;
1451 
1452   p = lm_message_node_get_child_value(m->node, "show");
1453   if (p) {
1454     if (!strcmp(p, "away"))      ust = away;
1455     else if (!strcmp(p, "dnd"))  ust = dontdisturb;
1456     else if (!strcmp(p, "xa"))   ust = notavail;
1457     else if (!strcmp(p, "chat")) ust = freeforchat;
1458   }
1459 
1460   if (mstype == LM_MESSAGE_SUB_TYPE_UNAVAILABLE)
1461     ust = offline;
1462 
1463   ustmsg = lm_message_node_get_child_value(m->node, "status");
1464   if (ustmsg && !*ustmsg)
1465     ustmsg = NULL;
1466 
1467   // Timestamp?
1468   timestamp = lm_message_node_get_timestamp(m->node);
1469   // Check for MUC presence packet
1470   muc_packet = lm_message_node_find_xmlns(m->node, NS_MUC_USER);
1471   rname = jid_get_resource_name(from);
1472 
1473   if (muc_packet) {
1474     // This is a MUC presence message
1475     handle_muc_presence(from, muc_packet, bjid, rname,
1476                         ust, ustmsg, timestamp, bpprio);
1477   } else {
1478     // Not a MUC message, so this is a regular buddy...
1479     // Call hk_statuschange() if status has changed or if the
1480     // status message is different
1481     const char *msg;
1482     msg = roster_getstatusmsg(bjid, rname);
1483     if ((ust != roster_getstatus(bjid, rname)) ||
1484         (!ustmsg && msg && msg[0]) ||
1485         (ustmsg && (!msg || strcmp(ustmsg, msg))) ||
1486         (bpprio != roster_getprio(bjid, rname)))
1487       hk_statuschange(bjid, rname, bpprio, timestamp, ust, ustmsg);
1488     // Presence signature processing
1489     if (!ustmsg)
1490       ustmsg = ""; // Some clients omit the <status/> element :-(
1491     check_signature(bjid, rname,
1492                     lm_message_node_find_xmlns(m->node, NS_SIGNED), ustmsg);
1493   }
1494 
1495   // XEP-0115 Entity Capabilities
1496   caps = lm_message_node_find_xmlns(m->node, NS_CAPS);
1497   if (caps && ust != offline) {
1498     const char *ver = lm_message_node_get_attribute(caps, "ver");
1499     const char *hash = lm_message_node_get_attribute(caps, "hash");
1500     GSList *sl_buddy = NULL;
1501 
1502     if (!hash) {
1503       // No support for legacy format
1504       goto handle_presence_return;
1505     }
1506     if (!ver || !g_strcmp0(ver, "") || !g_strcmp0(hash, ""))
1507       goto handle_presence_return;
1508 
1509     if (rname)
1510       sl_buddy = roster_find(bjid, jidsearch, ROSTER_TYPE_USER);
1511     // Only cache the caps if the user is on the roster
1512     if (sl_buddy && buddy_getonserverflag(sl_buddy->data)) {
1513       buddy_resource_setcaps(sl_buddy->data, rname, ver);
1514 
1515       if (!caps_has_hash(ver, bjid) && !caps_restore_from_persistent(ver)) {
1516         char *node;
1517         LmMessageHandler *handler;
1518         LmMessage *iq = lm_message_new_with_sub_type(from, LM_MESSAGE_TYPE_IQ,
1519                                                      LM_MESSAGE_SUB_TYPE_GET);
1520         node = g_strdup_printf("%s#%s",
1521                                lm_message_node_get_attribute(caps, "node"),
1522                                ver);
1523         lm_message_node_set_attributes
1524                 (lm_message_node_add_child(iq->node, "query", NULL),
1525                  "xmlns", NS_DISCO_INFO,
1526                  "node", node,
1527                  NULL);
1528         g_free(node);
1529         handler = lm_message_handler_new(cb_caps,
1530                                          g_strdup_printf("%s,%s",ver,hash),
1531                                          NULL);
1532         lm_connection_send_with_reply(connection, iq, handler, NULL);
1533         lm_message_unref(iq);
1534         lm_message_handler_unref(handler);
1535       }
1536     }
1537   }
1538 
1539 handle_presence_return:
1540   g_free(bjid);
1541   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
1542 }
1543 
1544 
handle_iq(LmMessageHandler * handler,LmConnection * connection,LmMessage * m,gpointer user_data)1545 static LmHandlerResult handle_iq(LmMessageHandler *handler,
1546                                  LmConnection *connection,
1547                                  LmMessage *m, gpointer user_data)
1548 {
1549   int i;
1550   const char *xmlns = NULL;
1551   char *nodestr;
1552   LmMessageNode *x;
1553   LmMessageSubType mstype = lm_message_get_sub_type(m);
1554 
1555   if (mstype == LM_MESSAGE_SUB_TYPE_ERROR) {
1556     display_server_error(lm_message_node_get_child(m->node, "error"),
1557                          lm_message_get_from(m));
1558     return LM_HANDLER_RESULT_REMOVE_MESSAGE;
1559   }
1560 
1561   if (mstype == LM_MESSAGE_SUB_TYPE_RESULT) {
1562     nodestr = lm_message_node_to_string(m->node);
1563     scr_LogPrint(LPRINT_DEBUG, "Unhandled IQ result? %s", nodestr);
1564     g_free(nodestr);
1565     return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
1566   }
1567 
1568   for (x = m->node->children; x; x=x->next) {
1569     xmlns = lm_message_node_get_attribute(x, "xmlns");
1570     if (xmlns)
1571       for (i=0; iq_handlers[i].xmlns; ++i)
1572         if (!strcmp(iq_handlers[i].xmlns, xmlns))
1573           return iq_handlers[i].handler(NULL, connection, m, user_data);
1574     xmlns = NULL;
1575   }
1576 
1577   if ((mstype == LM_MESSAGE_SUB_TYPE_SET) ||
1578       (mstype == LM_MESSAGE_SUB_TYPE_GET))
1579     send_iq_error(connection, m, XMPP_ERROR_NOT_IMPLEMENTED);
1580 
1581   nodestr = lm_message_node_to_string(m->node);
1582   scr_LogPrint(LPRINT_DEBUG, "Unhandled IQ: %s", nodestr);
1583   g_free(nodestr);
1584 
1585   scr_LogPrint(LPRINT_NORMAL, "Received unhandled IQ request from <%s>.",
1586                lm_message_get_from(m));
1587 
1588   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
1589 }
1590 
handle_s10n(LmMessageHandler * handler,LmConnection * connection,LmMessage * m,gpointer user_data)1591 static LmHandlerResult handle_s10n(LmMessageHandler *handler,
1592                                    LmConnection *connection,
1593                                    LmMessage *m, gpointer user_data)
1594 {
1595   char *r;
1596   char *buf;
1597   int newbuddy;
1598   guint hook_result;
1599   LmMessageSubType mstype = lm_message_get_sub_type(m);
1600   const char *from = lm_message_get_from(m);
1601   const char *msg = lm_message_node_get_child_value(m->node, "status");
1602 
1603   if (mstype == LM_MESSAGE_SUB_TYPE_ERROR) {
1604     display_server_error(lm_message_node_get_child(m->node, "error"), from);
1605     return LM_HANDLER_RESULT_REMOVE_MESSAGE;
1606   }
1607 
1608   if (!from) {
1609     scr_LogPrint(LPRINT_DEBUG, "handle_s10n: Unexpected presence packet!");
1610     return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
1611   }
1612   r = jidtodisp(from);
1613 
1614   newbuddy = !roster_find(r, jidsearch, 0);
1615 
1616   hook_result = hk_subscription(mstype, r, msg);
1617 
1618   if (mstype == LM_MESSAGE_SUB_TYPE_SUBSCRIBE) {
1619     /* The sender wishes to subscribe to our presence */
1620 
1621     if (hook_result) {
1622       g_free(r);
1623       return LM_HANDLER_RESULT_REMOVE_MESSAGE;
1624     }
1625 
1626     buf = g_strdup_printf("<%s> wants to subscribe to your presence updates",
1627                           from);
1628     scr_WriteIncomingMessage(r, buf, 0, HBB_PREFIX_INFO, 0);
1629     scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
1630     g_free(buf);
1631 
1632     if (msg) {
1633       buf = g_strdup_printf("<%s> said: %s", from, msg);
1634       scr_WriteIncomingMessage(r, buf, 0, HBB_PREFIX_INFO, 0);
1635       replace_nl_with_dots(buf);
1636       scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
1637       g_free(buf);
1638     }
1639 
1640     // Create a new event item
1641     {
1642       const char *id;
1643       char *desc = g_strdup_printf("<%s> wants to subscribe to your "
1644                                    "presence updates", r);
1645 
1646       id = evs_new(desc, NULL, 0, evscallback_subscription, g_strdup(r),
1647                    (GDestroyNotify)g_free);
1648       g_free(desc);
1649       if (id)
1650         buf = g_strdup_printf("Please use /event %s accept|reject", id);
1651       else
1652         buf = g_strdup_printf("Unable to create a new event!");
1653     }
1654     scr_WriteIncomingMessage(r, buf, 0, HBB_PREFIX_INFO, 0);
1655     scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
1656     g_free(buf);
1657   } else if (mstype == LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE) {
1658     /* The sender is unsubscribing from our presence */
1659     xmpp_send_s10n(from, LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED);
1660     buf = g_strdup_printf("<%s> is unsubscribing from your "
1661                           "presence updates", from);
1662     scr_WriteIncomingMessage(r, buf, 0, HBB_PREFIX_INFO, 0);
1663     scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
1664     g_free(buf);
1665   } else if (mstype == LM_MESSAGE_SUB_TYPE_SUBSCRIBED) {
1666     /* The sender has allowed us to receive their presence */
1667     buf = g_strdup_printf("<%s> has allowed you to receive their "
1668                           "presence updates", from);
1669     scr_WriteIncomingMessage(r, buf, 0, HBB_PREFIX_INFO, 0);
1670     scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
1671     g_free(buf);
1672   } else if (mstype == LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED) {
1673     /* The subscription request has been denied or a previously-granted
1674        subscription has been cancelled */
1675     roster_unsubscribed(from);
1676     scr_update_roster();
1677     buf = g_strdup_printf("<%s> has cancelled your subscription to "
1678                           "their presence updates", from);
1679     scr_WriteIncomingMessage(r, buf, 0, HBB_PREFIX_INFO, 0);
1680     scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
1681     g_free(buf);
1682   } else {
1683     g_free(r);
1684     return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
1685   }
1686 
1687   if (newbuddy)
1688     scr_update_roster();
1689   g_free(r);
1690   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
1691 }
1692 
1693 // TODO: Use the enum of loudmouth, when it's included in the header...
1694 typedef enum {
1695   LM_LOG_LEVEL_VERBOSE = 1 << (G_LOG_LEVEL_USER_SHIFT),
1696   LM_LOG_LEVEL_NET     = 1 << (G_LOG_LEVEL_USER_SHIFT + 1),
1697   LM_LOG_LEVEL_PARSER  = 1 << (G_LOG_LEVEL_USER_SHIFT + 2),
1698   LM_LOG_LEVEL_SSL     = 1 << (G_LOG_LEVEL_USER_SHIFT + 3),
1699   LM_LOG_LEVEL_SASL    = 1 << (G_LOG_LEVEL_USER_SHIFT + 4),
1700   LM_LOG_LEVEL_ALL     = (LM_LOG_LEVEL_NET |
1701         LM_LOG_LEVEL_VERBOSE |
1702         LM_LOG_LEVEL_PARSER |
1703         LM_LOG_LEVEL_SSL |
1704         LM_LOG_LEVEL_SASL)
1705 } LmLogLevelFlags;
1706 
lm_debug_handler(const gchar * log_domain,GLogLevelFlags log_level,const gchar * message,gpointer user_data)1707 static void lm_debug_handler (const gchar    *log_domain,
1708                               GLogLevelFlags  log_level,
1709                               const gchar    *message,
1710                               gpointer        user_data)
1711 {
1712   if (message && *message) {
1713     char *msg;
1714     int mcabber_loglevel = settings_opt_get_int("tracelog_level");
1715 
1716     if (mcabber_loglevel < 2)
1717       return;
1718 
1719     if (message[0] == '\n')
1720       msg = g_strdup(&message[1]);
1721     else
1722       msg = g_strdup(message);
1723 
1724     if (msg[strlen(msg)-1] == '\n')
1725       msg[strlen(msg)-1] = '\0';
1726 
1727     if (log_level & LM_LOG_LEVEL_VERBOSE) {
1728       scr_LogPrint(LPRINT_DEBUG, "LM-VERBOSE: %s", msg);
1729     }
1730     if (log_level & LM_LOG_LEVEL_NET) {
1731       if (mcabber_loglevel > 2)
1732         scr_LogPrint(LPRINT_DEBUG, "LM-NET: %s", msg);
1733     } else if (log_level & LM_LOG_LEVEL_PARSER) {
1734       if (mcabber_loglevel > 3)
1735         scr_LogPrint(LPRINT_DEBUG, "LM-PARSER: %s", msg);
1736     } else if (log_level & LM_LOG_LEVEL_SASL) {
1737       scr_LogPrint(LPRINT_DEBUG, "LM-SASL: %s", msg);
1738     } else if (log_level & LM_LOG_LEVEL_SSL) {
1739       scr_LogPrint(LPRINT_DEBUG, "LM-SSL: %s", msg);
1740     }
1741     g_free(msg);
1742   }
1743 }
1744 
1745 
1746 //  xmpp_connect()
1747 // Return a non-zero value if there's an obvious problem
1748 // (no JID, no password, etc.)
xmpp_connect(void)1749 gint xmpp_connect(void)
1750 {
1751   const char *userjid, *password, *resource, *servername, *ssl_fpr;
1752   char *dynresource = NULL;
1753 #ifndef LOUDMOUTH_USES_SHA256
1754   char fpr[FINGERPRINT_LENGTH] = {0};
1755 #endif
1756   const char *proxy_host;
1757   const char *resource_prefix = PACKAGE_NAME;
1758   char *fjid;
1759   int ssl, tls;
1760   LmSSL *lssl;
1761   unsigned int port;
1762   unsigned int ping = 40;
1763   LmMessageHandler *handler;
1764   GError *error = NULL;
1765 
1766   xmpp_disconnect();
1767 
1768   servername  = settings_opt_get("server");
1769   userjid     = settings_opt_get("jid");
1770   password    = settings_opt_get("password");
1771   resource    = settings_opt_get("resource");
1772   proxy_host  = settings_opt_get("proxy_host");
1773   ssl_fpr     = settings_opt_get("ssl_fingerprint");
1774 
1775   if (!userjid) {
1776     scr_LogPrint(LPRINT_LOGNORM, "Your JID has not been specified!");
1777     return -1;
1778   }
1779   if (!password) {
1780     scr_LogPrint(LPRINT_LOGNORM, "Your password has not been specified!");
1781     return -1;
1782   }
1783 
1784   lconnection = lm_connection_new_with_context(NULL, main_context);
1785 
1786   g_log_set_handler("LM", LM_LOG_LEVEL_ALL, lm_debug_handler, NULL);
1787 
1788   if (settings_opt_get("pinginterval"))
1789     ping = (unsigned int) settings_opt_get_int("pinginterval");
1790   lm_connection_set_keep_alive_rate(lconnection, ping);
1791   scr_LogPrint(LPRINT_DEBUG, "Ping interval established: %d secs", ping);
1792 
1793   lm_connection_set_disconnect_function(lconnection, connection_close_cb,
1794                                         NULL, NULL);
1795 
1796   handler = lm_message_handler_new(handle_messages, NULL, NULL);
1797   lm_connection_register_message_handler(lconnection, handler,
1798                                          LM_MESSAGE_TYPE_MESSAGE,
1799                                          LM_HANDLER_PRIORITY_NORMAL);
1800   lm_message_handler_unref(handler);
1801 
1802   handler = lm_message_handler_new(handle_iq, NULL, NULL);
1803   lm_connection_register_message_handler(lconnection, handler,
1804                                          LM_MESSAGE_TYPE_IQ,
1805                                          LM_HANDLER_PRIORITY_NORMAL);
1806   lm_message_handler_unref(handler);
1807 
1808   handler = lm_message_handler_new(handle_presence, NULL, NULL);
1809   lm_connection_register_message_handler(lconnection, handler,
1810                                          LM_MESSAGE_TYPE_PRESENCE,
1811                                          LM_HANDLER_PRIORITY_LAST);
1812   lm_message_handler_unref(handler);
1813 
1814   handler = lm_message_handler_new(handle_s10n, NULL, NULL);
1815   lm_connection_register_message_handler(lconnection, handler,
1816                                          LM_MESSAGE_TYPE_PRESENCE,
1817                                          LM_HANDLER_PRIORITY_NORMAL);
1818   lm_message_handler_unref(handler);
1819 
1820   /* Connect to server */
1821   scr_LogPrint(LPRINT_NORMAL|LPRINT_DEBUG, "Connecting to server%s%s",
1822                servername ? ": " : "",
1823                servername ? servername : "");
1824   if (!resource)
1825     resource = resource_prefix;
1826 
1827   // Initialize pseudo-random seed
1828   srandom(time(NULL));
1829 
1830   if (!settings_opt_get_int("disable_random_resource")) {
1831 #if HAVE_ARC4RANDOM
1832     dynresource = g_strdup_printf("%s.%08x", resource, arc4random());
1833 #else
1834     unsigned int tab[2];
1835     tab[0] = (unsigned int) (0xffff * (random() / (RAND_MAX + 1.0)));
1836     tab[1] = (unsigned int) (0xffff * (random() / (RAND_MAX + 1.0)));
1837     dynresource = g_strdup_printf("%s.%04x%04x", resource, tab[0], tab[1]);
1838 #endif
1839     resource = dynresource;
1840   }
1841 
1842   port = (unsigned int) settings_opt_get_int("port");
1843 
1844   if (port)
1845     scr_LogPrint(LPRINT_NORMAL|LPRINT_DEBUG, " using port %d", port);
1846   scr_LogPrint(LPRINT_NORMAL|LPRINT_DEBUG, " with resource %s", resource);
1847 
1848   if (proxy_host) {
1849     int proxy_port = settings_opt_get_int("proxy_port");
1850     if (proxy_port <= 0 || proxy_port > 65535) {
1851       scr_LogPrint(LPRINT_NORMAL|LPRINT_DEBUG, "Invalid proxy port: %d",
1852                    proxy_port);
1853     } else {
1854       const char *proxy_user, *proxy_pass;
1855       LmProxy *lproxy;
1856       proxy_user = settings_opt_get("proxy_user");
1857       proxy_pass = settings_opt_get("proxy_pass");
1858       // Proxy initialization
1859       lproxy = lm_proxy_new_with_server(LM_PROXY_TYPE_HTTP,
1860                                         proxy_host, proxy_port);
1861       lm_proxy_set_username(lproxy, proxy_user);
1862       lm_proxy_set_password(lproxy, proxy_pass);
1863       lm_connection_set_proxy(lconnection, lproxy);
1864       lm_proxy_unref(lproxy);
1865       scr_LogPrint(LPRINT_NORMAL|LPRINT_DEBUG, " using proxy %s:%d",
1866                    proxy_host, proxy_port);
1867     }
1868   }
1869 
1870   fjid = compose_jid(userjid, servername, resource);
1871   lm_connection_set_jid(lconnection, fjid);
1872   if (servername)
1873     lm_connection_set_server(lconnection, servername);
1874 #if defined(HAVE_LIBOTR)
1875   otr_init(fjid);
1876 #endif
1877   g_free(fjid);
1878   g_free(dynresource);
1879 
1880   ssl = settings_opt_get_int("ssl");
1881   tls = settings_opt_get_int("tls");
1882 
1883   if (!lm_ssl_is_supported()) {
1884     if (ssl || tls) {
1885       scr_LogPrint(LPRINT_LOGNORM, "** Error: SSL is NOT available, "
1886                    "please recompile loudmouth with SSL enabled.");
1887       return -1;
1888     }
1889   }
1890 
1891   if (ssl && tls) {
1892     scr_LogPrint(LPRINT_LOGNORM, "You can only set ssl or tls, not both.");
1893     return -1;
1894   }
1895 
1896   if (!port)
1897     port = (ssl ? LM_CONNECTION_DEFAULT_PORT_SSL : LM_CONNECTION_DEFAULT_PORT);
1898   lm_connection_set_port(lconnection, port);
1899 
1900 #ifndef LOUDMOUTH_USES_SHA256
1901   if (ssl_fpr && (!hex_to_fingerprint(ssl_fpr, fpr, FINGERPRINT_LENGTH))) {
1902     scr_LogPrint(LPRINT_LOGNORM, "** Please set the fingerprint in the format "
1903                  "97:5C:00:3F:1D:77:45:25:E2:C5:70:EC:83:C8:87:EE");
1904     return -1;
1905   }
1906 
1907   lssl = lm_ssl_new((ssl_fpr ? fpr : NULL), ssl_cb, NULL, NULL);
1908 #else
1909   lssl = lm_ssl_new(ssl_fpr, ssl_cb, NULL, NULL);
1910 #endif
1911   if (lssl) {
1912 #ifdef HAVE_LM_SSL_CIPHER_LIST
1913     const char *ssl_ciphers = settings_opt_get("ssl_ciphers");
1914     lm_ssl_set_cipher_list(lssl, ssl_ciphers);
1915 #endif
1916 #ifdef HAVE_LM_SSL_CA
1917     const char *ssl_ca = settings_opt_get("ssl_ca");
1918     char *ssl_ca_xp;
1919     ssl_ca_xp = expand_filename(ssl_ca);
1920     lm_ssl_set_ca(lssl, ssl_ca_xp);
1921     g_free(ssl_ca_xp);
1922 #endif
1923     lm_ssl_use_starttls(lssl, !ssl, tls);
1924     lm_connection_set_ssl(lconnection, lssl);
1925     lm_ssl_unref(lssl);
1926   } else if (ssl || tls) {
1927     scr_LogPrint(LPRINT_LOGNORM, "** Error: Couldn't create SSL struct.");
1928     return -1;
1929   }
1930 
1931   if (!lm_connection_open(lconnection, connection_open_cb,
1932                           NULL, FALSE, &error)) {
1933     _try_to_reconnect();
1934     scr_LogPrint(LPRINT_LOGNORM, "Failed to open: %s", error->message);
1935     g_error_free(error);
1936   }
1937   return 0;
1938 }
1939 
1940 //  xmpp_insert_entity_capabilities(presence_stanza)
1941 // Entity Capabilities (XEP-0115)
xmpp_insert_entity_capabilities(LmMessageNode * x,enum imstatus status)1942 void xmpp_insert_entity_capabilities(LmMessageNode *x, enum imstatus status)
1943 {
1944   LmMessageNode *y;
1945   const char *ver = entity_version(status);
1946   if (!ver)
1947     return;
1948 
1949   y = lm_message_node_add_child(x, "c", NULL);
1950   lm_message_node_set_attribute(y, "xmlns", NS_CAPS);
1951   lm_message_node_set_attribute(y, "hash", "sha-1");
1952   lm_message_node_set_attribute(y, "node", MCABBER_CAPS_NODE);
1953   lm_message_node_set_attribute(y, "ver", ver);
1954 }
1955 
xmpp_disconnect(void)1956 void xmpp_disconnect(void)
1957 {
1958   if (!lconnection)
1959     return;
1960   if (lm_connection_is_authenticated(lconnection)) {
1961     // Launch pre-disconnect internal hook
1962     hk_predisconnect();
1963     // Announce it to  everyone else
1964     xmpp_setstatus(offline, NULL, "", FALSE);
1965   }
1966   if (lm_connection_is_open(lconnection))
1967     lm_connection_close(lconnection, NULL);
1968   lm_connection_unref(lconnection);
1969   lconnection = NULL;
1970 }
1971 
xmpp_setstatus(enum imstatus st,const char * recipient,const char * msg,int do_not_sign)1972 void xmpp_setstatus(enum imstatus st, const char *recipient, const char *msg,
1973                   int do_not_sign)
1974 {
1975   LmMessage *m;
1976   gboolean isonline;
1977 
1978   if (msg) {
1979     // The status message has been specified.  We'll use it, unless it is
1980     // "-" which is a special case (option meaning "no status message").
1981     if (!strcmp(msg, "-"))
1982       msg = "";
1983   } else {
1984     // No status message specified; we'll use:
1985     // a) the default status message (if provided by the user);
1986     // b) the current status message;
1987     // c) no status message (i.e. an empty one).
1988     msg = settings_get_status_msg(st);
1989     if (!msg) {
1990       if (mystatusmsg)
1991         msg = mystatusmsg;
1992       else
1993         msg = "";
1994     }
1995   }
1996 
1997   isonline = xmpp_is_online();
1998 
1999   // Only send the packet if we're online.
2000   // (But we want to update internal status even when disconnected,
2001   // in order to avoid some problems during network failures)
2002   if (isonline) {
2003 #ifdef WITH_DEPRECATED_STATUS_INVISIBLE
2004     const char *s_msg = (st != invisible ? msg : NULL);
2005 #else
2006     // XXX Could be removed if/when we get rid of status invisible
2007     // completely.
2008     const char *s_msg = msg;
2009 #endif
2010 
2011     if (!recipient) {
2012       // This is a global status, send presence to chatrooms
2013 #ifdef WITH_DEPRECATED_STATUS_INVISIBLE
2014       if (st != invisible)
2015 #endif
2016       {
2017         struct T_presence room_presence;
2018         room_presence.st = st;
2019         room_presence.msg = msg;
2020         foreach_buddy(ROSTER_TYPE_ROOM, &roompresence, &room_presence);
2021       }
2022     }
2023 
2024     m = lm_message_new_presence(st, recipient, s_msg);
2025     xmpp_insert_entity_capabilities(m->node, st); // Entity Caps (XEP-0115)
2026 #ifdef HAVE_GPGME
2027     if (!do_not_sign && gpg_enabled()) {
2028       char *signature;
2029       signature = gpg_sign(s_msg ? s_msg : "");
2030       if (signature) {
2031         LmMessageNode *y;
2032         y = lm_message_node_add_child(m->node, "x", signature);
2033         lm_message_node_set_attribute(y, "xmlns", NS_SIGNED);
2034         g_free(signature);
2035       }
2036     }
2037 #endif
2038     lm_connection_send(lconnection, m, NULL);
2039     lm_message_unref(m);
2040   }
2041 
2042   // If we didn't change our _global_ status, we are done
2043   if (recipient) return;
2044 
2045   if (isonline || !st) {
2046     // We'll have to update the roster if we switch to/from offline because
2047     // we don't know the presences of buddies when offline...
2048     if (mystatus == offline || st == offline)
2049       scr_update_roster();
2050 
2051     if (isonline || mystatus || st)
2052 #ifdef WITH_DEPRECATED_STATUS_INVISIBLE
2053       hk_mystatuschange(0, mystatus, st, (st != invisible ? msg : ""));
2054 #else
2055       hk_mystatuschange(0, mystatus, st, msg);
2056 #endif
2057     mystatus = st;
2058   }
2059 
2060   if (st)
2061     mywantedstatus = st;
2062 
2063   if (msg != mystatusmsg) {
2064     g_free(mystatusmsg);
2065     if (*msg)
2066       mystatusmsg = g_strdup(msg);
2067     else
2068       mystatusmsg = NULL;
2069   }
2070 
2071   if (!scr_curses_status())
2072     return;  // Called from config. file
2073 
2074   if (!Autoaway)
2075     update_last_use();
2076 
2077   // Update status line
2078   scr_update_main_status(TRUE);
2079 }
2080 
2081 
xmpp_getstatus(void)2082 enum imstatus xmpp_getstatus(void)
2083 {
2084   return mystatus;
2085 }
2086 
xmpp_getstatusmsg(void)2087 const char *xmpp_getstatusmsg(void)
2088 {
2089   return mystatusmsg;
2090 }
2091 
2092 //  xmpp_setprevstatus()
2093 // Set previous status.  This wrapper function is used after a disconnection.
xmpp_setprevstatus(void)2094 void xmpp_setprevstatus(void)
2095 {
2096   xmpp_setstatus(mywantedstatus, NULL, mystatusmsg, FALSE);
2097 }
2098 
2099 //  send_storage(store)
2100 // Send the node "store" to update the server.
2101 // Note: the caller should check we're online.
send_storage(LmMessageNode * store)2102 void send_storage(LmMessageNode *store)
2103 {
2104   LmMessage *iq;
2105   LmMessageHandler *handler;
2106   LmMessageNode *query;
2107 
2108   if (!rosternotes) return;
2109 
2110   iq = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_IQ,
2111                                     LM_MESSAGE_SUB_TYPE_SET);
2112   query = lm_message_node_add_child(iq->node, "query", NULL);
2113   lm_message_node_set_attribute(query, "xmlns", NS_PRIVATE);
2114   lm_message_node_insert_childnode(query, store);
2115 
2116   handler = lm_message_handler_new(handle_iq_dummy, NULL, FALSE);
2117   lm_connection_send_with_reply(lconnection, iq, handler, NULL);
2118   lm_message_handler_unref(handler);
2119   lm_message_unref(iq);
2120 }
2121 
2122 
2123 //  xmpp_is_bookmarked(roomjid)
2124 // Return TRUE if there's a bookmark for the given jid.
xmpp_is_bookmarked(const char * bjid)2125 guint xmpp_is_bookmarked(const char *bjid)
2126 {
2127   LmMessageNode *x;
2128 
2129   if (!bookmarks)
2130     return FALSE;
2131 
2132   // Walk through the storage bookmark tags
2133   for (x = bookmarks->children ; x; x = x->next) {
2134     // If the node is a conference item, check the jid.
2135     if (x->name && !strcmp(x->name, "conference")) {
2136       const char *fjid = lm_message_node_get_attribute(x, "jid");
2137       if (fjid && !strcasecmp(bjid, fjid))
2138         return TRUE;
2139     }
2140   }
2141   return FALSE;
2142 }
2143 
2144 //  xmpp_get_bookmark_nick(roomjid)
2145 // Return the room nickname if it is present in a bookmark.
xmpp_get_bookmark_nick(const char * bjid)2146 const char *xmpp_get_bookmark_nick(const char *bjid)
2147 {
2148   LmMessageNode *x;
2149 
2150   if (!bookmarks || !bjid)
2151     return NULL;
2152 
2153   // Walk through the storage bookmark tags
2154   for (x = bookmarks->children ; x; x = x->next) {
2155     // If the node is a conference item, check the jid.
2156     if (x->name && !strcmp(x->name, "conference")) {
2157       const char *fjid = lm_message_node_get_attribute(x, "jid");
2158       if (fjid && !strcasecmp(bjid, fjid))
2159         return lm_message_node_get_child_value(x, "nick");
2160     }
2161   }
2162   return NULL;
2163 }
2164 
2165 //  xmpp_get_bookmark_password(roomjid)
2166 // Return the room password if it is present in a bookmark.
xmpp_get_bookmark_password(const char * bjid)2167 const char *xmpp_get_bookmark_password(const char *bjid)
2168 {
2169   LmMessageNode *x;
2170 
2171   if (!bookmarks || !bjid)
2172     return NULL;
2173 
2174   // Walk through the storage bookmark tags
2175   for (x = bookmarks->children ; x; x = x->next) {
2176     // If the node is a conference item, check the jid.
2177     if (x->name && !strcmp(x->name, "conference")) {
2178       const char *fjid = lm_message_node_get_attribute(x, "jid");
2179       if (fjid && !strcasecmp(bjid, fjid))
2180         return lm_message_node_get_child_value(x, "password");
2181     }
2182   }
2183   return NULL;
2184 }
2185 
xmpp_get_bookmark_autojoin(const char * bjid)2186 int xmpp_get_bookmark_autojoin(const char *bjid)
2187 {
2188   LmMessageNode *x;
2189 
2190   if (!bookmarks || !bjid)
2191     return 0;
2192 
2193   // Walk through the storage bookmark tags
2194   for (x = bookmarks->children ; x; x = x->next) {
2195     // If the node is a conference item, check the jid.
2196     if (x->name && !strcmp(x->name, "conference")) {
2197       const char *fjid = lm_message_node_get_attribute(x, "jid");
2198       if (fjid && !strcasecmp(bjid, fjid)) {
2199         const char *autojoin;
2200         autojoin = lm_message_node_get_attribute(x, "autojoin");
2201         if (autojoin && (!strcmp(autojoin, "1") || !strcmp(autojoin, "true")))
2202           return 1;
2203         return 0;
2204       }
2205     }
2206   }
2207   return 0;
2208 }
2209 
2210 //  xmpp_get_all_storage_bookmarks()
2211 // Return a GSList with all storage bookmarks.
2212 // The caller should g_free the list (not the MUC jids).
xmpp_get_all_storage_bookmarks(void)2213 GSList *xmpp_get_all_storage_bookmarks(void)
2214 {
2215   LmMessageNode *x;
2216   GSList *sl_bookmarks = NULL;
2217 
2218   // If we have no bookmarks, probably the server doesn't support them.
2219   if (!bookmarks)
2220     return NULL;
2221 
2222   // Walk through the storage bookmark tags
2223   for (x = bookmarks->children ; x; x = x->next) {
2224     // If the node is a conference item, let's add the note to our list.
2225     if (x->name && !strcmp(x->name, "conference")) {
2226       struct bookmark *bm_elt;
2227       const char *autojoin, *name, *nick, *passwd;
2228       const char *fjid = lm_message_node_get_attribute(x, "jid");
2229       if (!fjid)
2230         continue;
2231       bm_elt = g_new0(struct bookmark, 1);
2232       bm_elt->roomjid = g_strdup(fjid);
2233       autojoin = lm_message_node_get_attribute(x, "autojoin");
2234       nick = lm_message_node_get_child_value(x, "nick");
2235       name = lm_message_node_get_attribute(x, "name");
2236       passwd = lm_message_node_get_child_value(x, "password");
2237       if (autojoin && (!strcmp(autojoin, "1") || !strcmp(autojoin, "true")))
2238         bm_elt->autojoin = 1;
2239       if (nick)
2240         bm_elt->nick = g_strdup(nick);
2241       if (name)
2242         bm_elt->name = g_strdup(name);
2243       if (passwd)
2244         bm_elt->password = g_strdup(passwd);
2245       sl_bookmarks = g_slist_append(sl_bookmarks, bm_elt);
2246     }
2247   }
2248   return sl_bookmarks;
2249 }
2250 
2251 //  xmpp_set_storage_bookmark(roomid, name, nick, passwd, autojoin,
2252 //                          printstatus, autowhois, flagjoins, group)
2253 // Update the private storage bookmarks: add a conference room.
2254 // If name is nil, we remove the bookmark.
xmpp_set_storage_bookmark(const char * roomid,const char * name,const char * nick,const char * passwd,int autojoin,enum room_printstatus pstatus,enum room_autowhois awhois,enum room_flagjoins fjoins,const char * group)2255 void xmpp_set_storage_bookmark(const char *roomid, const char *name,
2256                                const char *nick, const char *passwd,
2257                                int autojoin, enum room_printstatus pstatus,
2258                                enum room_autowhois awhois,
2259                                enum room_flagjoins fjoins, const char *group)
2260 {
2261   LmMessageNode *x;
2262   bool changed = FALSE;
2263 
2264   if (!roomid)
2265     return;
2266 
2267   // If we have no bookmarks, probably the server doesn't support them.
2268   if (!bookmarks) {
2269     scr_LogPrint(LPRINT_NORMAL,
2270                  "Sorry, your server doesn't seem to support private storage.");
2271     return;
2272   }
2273 
2274   // Walk through the storage tags
2275   for (x = bookmarks->children ; x; x = x->next) {
2276     // If the current node is a conference item, see if we have to replace it.
2277     if (x->name && !strcmp(x->name, "conference")) {
2278       const char *fjid = lm_message_node_get_attribute(x, "jid");
2279       if (!fjid)
2280         continue;
2281       if (!strcmp(fjid, roomid)) {
2282         // We've found a bookmark for this room.  Let's hide it and we'll
2283         // create a new one.
2284         lm_message_node_hide(x);
2285         changed = TRUE;
2286         if (!name)
2287           scr_LogPrint(LPRINT_LOGNORM, "Deleting bookmark...");
2288       }
2289     }
2290   }
2291 
2292   // Let's create a node/bookmark for this roomid, if the name is not NULL.
2293   if (name) {
2294     x = lm_message_node_add_child(bookmarks, "conference", NULL);
2295     lm_message_node_set_attributes(x,
2296                                    "jid", roomid,
2297                                    "name", name,
2298                                    "autojoin", autojoin ? "1" : "0",
2299                                    NULL);
2300     if (nick)
2301       lm_message_node_add_child(x, "nick", nick);
2302     if (passwd)
2303       lm_message_node_add_child(x, "password", passwd);
2304     if (pstatus)
2305       lm_message_node_add_child(x, "print_status", strprintstatus[pstatus]);
2306     if (awhois)
2307       lm_message_node_set_attributes(x, "autowhois",
2308                                      (awhois == autowhois_on) ? "1" : "0",
2309                                      NULL);
2310     if (fjoins)
2311       lm_message_node_add_child(x, "flag_joins", strflagjoins[fjoins]);
2312     if (group && *group)
2313       lm_message_node_add_child(x, "group", group);
2314     changed = TRUE;
2315   }
2316 
2317   if (!changed)
2318     return;
2319 
2320   if (xmpp_is_online()) {
2321     send_storage(bookmarks);
2322     scr_LogPrint(LPRINT_LOGNORM, "Bookmarks updated.");
2323   } else {
2324     scr_LogPrint(LPRINT_LOGNORM,
2325                  "Warning: you're not connected to the server.");
2326   }
2327 }
2328 
parse_storage_rosternote(LmMessageNode * notenode)2329 static struct annotation *parse_storage_rosternote(LmMessageNode *notenode)
2330 {
2331   const char *p;
2332   struct annotation *note = g_new0(struct annotation, 1);
2333   p = lm_message_node_get_attribute(notenode, "cdate");
2334   if (p)
2335     note->cdate = from_iso8601(p, 1);
2336   p = lm_message_node_get_attribute(notenode, "mdate");
2337   if (p)
2338     note->mdate = from_iso8601(p, 1);
2339   note->text = g_strdup(lm_message_node_get_value(notenode));
2340   note->jid = g_strdup(lm_message_node_get_attribute(notenode, "jid"));
2341   return note;
2342 }
2343 
2344 //  xmpp_get_all_storage_rosternotes()
2345 // Return a GSList with all storage annotations.
2346 // The caller should g_free the list and its contents.
xmpp_get_all_storage_rosternotes(void)2347 GSList *xmpp_get_all_storage_rosternotes(void)
2348 {
2349   LmMessageNode *x;
2350   GSList *sl_notes = NULL;
2351 
2352   // If we have no rosternotes, probably the server doesn't support them.
2353   if (!rosternotes)
2354     return NULL;
2355 
2356   // Walk through the storage rosternotes tags
2357   for (x = rosternotes->children ; x; x = x->next) {
2358     struct annotation *note;
2359 
2360     // We want a note item
2361     if (!x->name || strcmp(x->name, "note"))
2362       continue;
2363     // Just in case, check the jid...
2364     if (!lm_message_node_get_attribute(x, "jid"))
2365       continue;
2366     // Ok, let's add the note to our list
2367     note = parse_storage_rosternote(x);
2368     sl_notes = g_slist_append(sl_notes, note);
2369   }
2370   return sl_notes;
2371 }
2372 
2373 //  xmpp_get_storage_rosternotes(barejid, silent)
2374 // Return the annotation associated with this jid.
2375 // If silent is TRUE, no warning is displayed when rosternotes is disabled
2376 // The caller should g_free the string and structure after use.
xmpp_get_storage_rosternotes(const char * barejid,int silent)2377 struct annotation *xmpp_get_storage_rosternotes(const char *barejid, int silent)
2378 {
2379   LmMessageNode *x;
2380 
2381   if (!barejid)
2382     return NULL;
2383 
2384   // If we have no rosternotes, probably the server doesn't support them.
2385   if (!rosternotes) {
2386     if (!silent)
2387       scr_LogPrint(LPRINT_NORMAL, "Sorry, "
2388                    "your server doesn't seem to support private storage.");
2389     return NULL;
2390   }
2391 
2392   // Walk through the storage rosternotes tags
2393   for (x = rosternotes->children ; x; x = x->next) {
2394     const char *fjid;
2395     // We want a note item
2396     if (!x->name || strcmp(x->name, "note"))
2397       continue;
2398     // Just in case, check the jid...
2399     fjid = lm_message_node_get_attribute(x, "jid");
2400     if (fjid && !strcmp(fjid, barejid)) // We've found a note for this contact.
2401       return parse_storage_rosternote(x);
2402   }
2403   return NULL;  // No note found
2404 }
2405 
2406 //  xmpp_set_storage_rosternotes(barejid, note)
2407 // Update the private storage rosternotes: add/delete a note.
2408 // If note is nil, we remove the existing note.
xmpp_set_storage_rosternotes(const char * barejid,const char * note)2409 void xmpp_set_storage_rosternotes(const char *barejid, const char *note)
2410 {
2411   LmMessageNode *x;
2412   bool changed = FALSE;
2413   const char *cdate = NULL;
2414 
2415   if (!barejid)
2416     return;
2417 
2418   // If we have no rosternotes, probably the server doesn't support them.
2419   if (!rosternotes) {
2420     scr_LogPrint(LPRINT_NORMAL,
2421                  "Sorry, your server doesn't seem to support private storage.");
2422     return;
2423   }
2424 
2425   // Walk through the storage tags
2426   for (x = rosternotes->children ; x; x = x->next) {
2427     // If the current node is a conference item, see if we have to replace it.
2428     if (x->name && !strcmp(x->name, "note")) {
2429       const char *fjid = lm_message_node_get_attribute(x, "jid");
2430       if (!fjid)
2431         continue;
2432       if (!strcmp(fjid, barejid)) {
2433         // We've found a note for this jid.  Let's hide it and we'll
2434         // create a new one.
2435         cdate = lm_message_node_get_attribute(x, "cdate");
2436         lm_message_node_hide(x);
2437         changed = TRUE;
2438         break;
2439       }
2440     }
2441   }
2442 
2443   // Let's create a node for this jid, if the note is not NULL.
2444   if (note) {
2445     char mdate[20];
2446     time_t now;
2447     time(&now);
2448     to_iso8601(mdate, now);
2449     if (!cdate)
2450       cdate = mdate;
2451     x = lm_message_node_add_child(rosternotes, "note", note);
2452     lm_message_node_set_attributes(x,
2453                                    "jid", barejid,
2454                                    "cdate", cdate,
2455                                    "mdate", mdate,
2456                                    NULL);
2457     changed = TRUE;
2458   }
2459 
2460   if (!changed)
2461     return;
2462 
2463   if (xmpp_is_online())
2464     send_storage(rosternotes);
2465   else
2466     scr_LogPrint(LPRINT_LOGNORM,
2467                  "Warning: you're not connected to the server.");
2468 }
2469 
2470 /* vim: set et cindent cinoptions=>2\:2(0 ts=2 sw=2:  For Vim users... */
2471