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