comparison mcabber/src/xmpp.c @ 1598:a087125d8fc8

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