comparison mcabber/mcabber/xmpp.c @ 1668:41c26b7d2890

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