1 /***************************************************************************\
2 * *
3 * BitlBee - An IRC to IM gateway *
4 * Jabber module - Misc. stuff *
5 * *
6 * Copyright 2006-2010 Wilmer van der Gaast <wilmer@gaast.net>
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License along *
19 * with this program; if not, write to the Free Software Foundation, Inc., *
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
21 * *
22 \***************************************************************************/
23
24 #include "jabber.h"
25 #include "md5.h"
26 #include "base64.h"
27
28 static unsigned int next_id = 1;
29
set_eval_priority(set_t * set,char * value)30 char *set_eval_priority(set_t *set, char *value)
31 {
32 account_t *acc = set->data;
33 int i;
34
35 if (sscanf(value, "%d", &i) == 1) {
36 /* Priority is a signed 8-bit integer, according to RFC 3921. */
37 if (i < -128 || i > 127) {
38 return SET_INVALID;
39 }
40 } else {
41 return SET_INVALID;
42 }
43
44 /* Only run this stuff if the account is online ATM,
45 and if the setting seems to be acceptable. */
46 if (acc->ic) {
47 /* Although set_eval functions usually are very nice and
48 convenient, they have one disadvantage: If I would just
49 call p_s_u() now to send the new prio setting, it would
50 send the old setting because the set->value gets changed
51 after the (this) eval returns a non-NULL value.
52
53 So now I can choose between implementing post-set
54 functions next to evals, or just do this little hack: */
55
56 g_free(set->value);
57 set->value = g_strdup(value);
58
59 /* (Yes, sorry, I prefer the hack. :-P) */
60
61 presence_send_update(acc->ic);
62 }
63
64 return value;
65 }
66
set_eval_tls(set_t * set,char * value)67 char *set_eval_tls(set_t *set, char *value)
68 {
69 if (g_strcasecmp(value, "try") == 0) {
70 return value;
71 } else {
72 return set_eval_bool(set, value);
73 }
74 }
75
jabber_make_packet(char * name,char * type,char * to,struct xt_node * children)76 struct xt_node *jabber_make_packet(char *name, char *type, char *to, struct xt_node *children)
77 {
78 struct xt_node *node;
79
80 node = xt_new_node(name, NULL, children);
81
82 if (type) {
83 xt_add_attr(node, "type", type);
84 }
85 if (to) {
86 xt_add_attr(node, "to", to);
87 }
88
89 /* IQ packets should always have an ID, so let's generate one. It
90 might get overwritten by jabber_cache_add() if this packet has
91 to be saved until we receive a response. Cached packets get
92 slightly different IDs so we can recognize them. */
93 if (strcmp(name, "iq") == 0) {
94 char *id = g_strdup_printf("%s%05x", JABBER_PACKET_ID, (next_id++) & 0xfffff);
95 xt_add_attr(node, "id", id);
96 g_free(id);
97 }
98
99 return node;
100 }
101
jabber_make_error_packet(struct xt_node * orig,char * err_cond,char * err_type,char * err_code)102 struct xt_node *jabber_make_error_packet(struct xt_node *orig, char *err_cond, char *err_type, char *err_code)
103 {
104 struct xt_node *node, *c;
105 char *to;
106
107 /* Create the "defined-condition" tag. */
108 c = xt_new_node(err_cond, NULL, NULL);
109 xt_add_attr(c, "xmlns", XMLNS_STANZA_ERROR);
110
111 /* Put it in an <error> tag. */
112 c = xt_new_node("error", NULL, c);
113 xt_add_attr(c, "type", err_type);
114
115 /* Add the error code, if present */
116 if (err_code) {
117 xt_add_attr(c, "code", err_code);
118 }
119
120 /* To make the actual error packet, we copy the original packet and
121 add our <error>/type="error" tag. Including the original packet
122 is recommended, so let's just do it. */
123 node = xt_dup(orig);
124 xt_add_child(node, c);
125 xt_add_attr(node, "type", "error");
126
127 /* Return to sender. */
128 if ((to = xt_find_attr(node, "from"))) {
129 xt_add_attr(node, "to", to);
130 xt_remove_attr(node, "from");
131 }
132
133 return node;
134 }
135
136 /* Cache a node/packet for later use. Mainly useful for IQ packets if you need
137 them when you receive the response. Use this BEFORE sending the packet so
138 it'll get a new id= tag, and do NOT free() the packet after sending it! */
jabber_cache_add(struct im_connection * ic,struct xt_node * node,jabber_cache_event func)139 void jabber_cache_add(struct im_connection *ic, struct xt_node *node, jabber_cache_event func)
140 {
141 struct jabber_data *jd = ic->proto_data;
142 struct jabber_cache_entry *entry = g_new0(struct jabber_cache_entry, 1);
143 md5_state_t id_hash;
144 md5_byte_t id_sum[16];
145 char *id, *asc_hash;
146
147 next_id++;
148
149 id_hash = jd->cached_id_prefix;
150 md5_append(&id_hash, (md5_byte_t *) &next_id, sizeof(next_id));
151 md5_digest_keep(&id_hash, id_sum);
152 asc_hash = base64_encode(id_sum, 12);
153
154 id = g_strdup_printf("%s%s", JABBER_CACHED_ID, asc_hash);
155 xt_add_attr(node, "id", id);
156 g_free(id);
157 g_free(asc_hash);
158
159 entry->node = node;
160 entry->func = func;
161 entry->saved_at = time(NULL);
162 g_hash_table_insert(jd->node_cache, xt_find_attr(node, "id"), entry);
163 }
164
jabber_cache_entry_free(gpointer data)165 void jabber_cache_entry_free(gpointer data)
166 {
167 struct jabber_cache_entry *entry = data;
168
169 xt_free_node(entry->node);
170 g_free(entry);
171 }
172
173 gboolean jabber_cache_clean_entry(gpointer key, gpointer entry, gpointer nullpointer);
174
175 /* This one should be called from time to time (from keepalive, in this case)
176 to make sure things don't stay in the node cache forever. By marking nodes
177 during the first run and deleting marked nodes during a next run, every
178 node should be available in the cache for at least a minute (assuming the
179 function is indeed called every minute). */
jabber_cache_clean(struct im_connection * ic)180 void jabber_cache_clean(struct im_connection *ic)
181 {
182 struct jabber_data *jd = ic->proto_data;
183 time_t threshold = time(NULL) - JABBER_CACHE_MAX_AGE;
184
185 g_hash_table_foreach_remove(jd->node_cache, jabber_cache_clean_entry, &threshold);
186 }
187
jabber_cache_clean_entry(gpointer key,gpointer entry_,gpointer threshold_)188 gboolean jabber_cache_clean_entry(gpointer key, gpointer entry_, gpointer threshold_)
189 {
190 struct jabber_cache_entry *entry = entry_;
191 time_t *threshold = threshold_;
192
193 return entry->saved_at < *threshold;
194 }
195
jabber_cache_handle_packet(struct im_connection * ic,struct xt_node * node)196 xt_status jabber_cache_handle_packet(struct im_connection *ic, struct xt_node *node)
197 {
198 struct jabber_data *jd = ic->proto_data;
199 struct jabber_cache_entry *entry;
200 char *s;
201
202 if ((s = xt_find_attr(node, "id")) == NULL ||
203 strncmp(s, JABBER_CACHED_ID, strlen(JABBER_CACHED_ID)) != 0) {
204 /* Silently ignore it, without an ID (or a non-cache
205 ID) we don't know how to handle the packet and we
206 probably don't have to. */
207 return XT_HANDLED;
208 }
209
210 entry = g_hash_table_lookup(jd->node_cache, s);
211
212 if (entry == NULL) {
213 /*
214 There's no longer an easy way to see if we generated this
215 one or someone else, and there's a ten-minute timeout anyway,
216 so meh.
217
218 imcb_log( ic, "Warning: Received %s-%s packet with unknown/expired ID %s!",
219 node->name, xt_find_attr( node, "type" ) ? : "(no type)", s );
220 */
221 } else if (entry->func) {
222 return entry->func(ic, node, entry->node);
223 }
224
225 return XT_HANDLED;
226 }
227
228 const struct jabber_away_state jabber_away_state_list[] =
229 {
230 { "away", "Away" },
231 { "chat", "Free for Chat" }, /* WTF actually uses this? */
232 { "dnd", "Do not Disturb" },
233 { "xa", "Extended Away" },
234 { "", NULL }
235 };
236
jabber_away_state_by_code(char * code)237 const struct jabber_away_state *jabber_away_state_by_code(char *code)
238 {
239 int i;
240
241 if (code == NULL) {
242 return NULL;
243 }
244
245 for (i = 0; jabber_away_state_list[i].full_name; i++) {
246 if (g_strcasecmp(jabber_away_state_list[i].code, code) == 0) {
247 return jabber_away_state_list + i;
248 }
249 }
250
251 return NULL;
252 }
253
jabber_away_state_by_name(char * name)254 const struct jabber_away_state *jabber_away_state_by_name(char *name)
255 {
256 int i;
257
258 if (name == NULL) {
259 return NULL;
260 }
261
262 for (i = 0; jabber_away_state_list[i].full_name; i++) {
263 if (g_strcasecmp(jabber_away_state_list[i].full_name, name) == 0) {
264 return jabber_away_state_list + i;
265 }
266 }
267
268 return NULL;
269 }
270
271 struct jabber_buddy_ask_data {
272 struct im_connection *ic;
273 char *handle;
274 char *realname;
275 };
276
jabber_buddy_ask_yes(void * data)277 static void jabber_buddy_ask_yes(void *data)
278 {
279 struct jabber_buddy_ask_data *bla = data;
280
281 presence_send_request(bla->ic, bla->handle, "subscribed");
282
283 imcb_ask_add(bla->ic, bla->handle, NULL);
284
285 g_free(bla->handle);
286 g_free(bla);
287 }
288
jabber_buddy_ask_no(void * data)289 static void jabber_buddy_ask_no(void *data)
290 {
291 struct jabber_buddy_ask_data *bla = data;
292
293 presence_send_request(bla->ic, bla->handle, "unsubscribed");
294
295 g_free(bla->handle);
296 g_free(bla);
297 }
298
jabber_buddy_ask(struct im_connection * ic,char * handle)299 void jabber_buddy_ask(struct im_connection *ic, char *handle)
300 {
301 struct jabber_buddy_ask_data *bla = g_new0(struct jabber_buddy_ask_data, 1);
302 char *buf;
303
304 bla->ic = ic;
305 bla->handle = g_strdup(handle);
306
307 buf = g_strdup_printf("The user %s wants to add you to his/her buddy list.", handle);
308 imcb_ask(ic, buf, bla, jabber_buddy_ask_yes, jabber_buddy_ask_no);
309 g_free(buf);
310 }
311
312 /* Compares just the bare portions of two Jabber IDs. */
jabber_compare_jid(const char * jid1,const char * jid2)313 int jabber_compare_jid(const char *jid1, const char *jid2)
314 {
315 int i;
316
317 if (!jid1 || !jid2) {
318 return FALSE;
319 }
320
321 for (i = 0;; i++) {
322 if (jid1[i] == '\0' || jid1[i] == '/' || jid2[i] == '\0' || jid2[i] == '/') {
323 if ((jid1[i] == '\0' || jid1[i] == '/') && (jid2[i] == '\0' || jid2[i] == '/')) {
324 break;
325 }
326 return FALSE;
327 }
328 if (g_ascii_tolower(jid1[i]) != g_ascii_tolower(jid2[i])) {
329 return FALSE;
330 }
331 }
332
333 return TRUE;
334 }
335
336 /* The /resource part is case sensitive. This stops once we see a slash.
337 Returns a new string. Don't leak it! */
jabber_normalize(const char * orig)338 char *jabber_normalize(const char *orig)
339 {
340 char *lower, *new, *s;
341
342 if (!(s = strchr(orig, '/'))) {
343 return g_utf8_strdown(orig, -1);
344 }
345
346 lower = g_utf8_strdown(orig, (s - orig)); /* stop in s */
347 new = g_strconcat(lower, s, NULL);
348 g_free(lower);
349 return new;
350 }
351
352 /* Similar to jabber_normalize, but works with addresses in the form
353 * resource=chatroom@example.com */
jabber_normalize_ext(const char * orig)354 char *jabber_normalize_ext(const char *orig)
355 {
356 char *lower, *new, *s;
357
358 if (!(s = strchr(orig, '='))) {
359 return g_utf8_strdown(orig, -1);
360 }
361
362 lower = g_utf8_strdown(s, -1); /* start in s */
363
364 *s = 0;
365 new = g_strconcat(orig, lower, NULL);
366 *s = '=';
367
368 g_free(lower);
369 return new;
370 }
371
372 /* Adds a buddy/resource to our list. Returns NULL if full_jid is not really a
373 FULL jid or if we already have this buddy/resource. XXX: No, great, actually
374 buddies from transports don't (usually) have resources. So we'll really have
375 to deal with that properly. Set their ->resource property to NULL. Do *NOT*
376 allow to mix this stuff, though... */
jabber_buddy_add(struct im_connection * ic,char * full_jid_)377 struct jabber_buddy *jabber_buddy_add(struct im_connection *ic, char *full_jid_)
378 {
379 struct jabber_data *jd = ic->proto_data;
380 struct jabber_buddy *bud, *new, *bi;
381 char *s, *full_jid;
382
383 full_jid = jabber_normalize(full_jid_);
384
385 if ((s = strchr(full_jid, '/'))) {
386 *s = 0;
387 }
388
389 new = g_new0(struct jabber_buddy, 1);
390
391 if ((bud = g_hash_table_lookup(jd->buddies, full_jid))) {
392 /* The first entry is always a bare JID. If there are more, we
393 should ignore the first one here. */
394 if (bud->next) {
395 bud = bud->next;
396 }
397
398 /* If this is a transport buddy or whatever, it can't have more
399 than one instance, so this is always wrong: */
400 if (s == NULL || bud->resource == NULL) {
401 if (s) {
402 *s = '/';
403 }
404 g_free(new);
405 g_free(full_jid);
406 return NULL;
407 }
408
409 new->bare_jid = bud->bare_jid;
410
411 /* We already have another resource for this buddy, add the
412 new one to the list. */
413 for (bi = bud; bi; bi = bi->next) {
414 /* Check for dupes. */
415 if (strcmp(bi->resource, s + 1) == 0) {
416 *s = '/';
417 g_free(new);
418 g_free(full_jid);
419 return NULL;
420 }
421 /* Append the new item to the list. */
422 else if (bi->next == NULL) {
423 bi->next = new;
424 break;
425 }
426 }
427 } else {
428 new->full_jid = new->bare_jid = g_strdup(full_jid);
429 g_hash_table_insert(jd->buddies, new->bare_jid, new);
430
431 if (s) {
432 new->next = g_new0(struct jabber_buddy, 1);
433 new->next->bare_jid = new->bare_jid;
434 new = new->next;
435 }
436 }
437
438 if (s) {
439 *s = '/';
440 new->full_jid = full_jid;
441 new->resource = strchr(new->full_jid, '/') + 1;
442 } else {
443 /* Let's waste some more bytes of RAM instead of to make
444 memory management a total disaster here. And it saves
445 me one g_free() call in this function. :-P */
446 new->full_jid = full_jid;
447 }
448
449 return new;
450 }
451
452 /* Finds a buddy from our structures. Can find both full- and bare JIDs. When
453 asked for a bare JID, it uses the "resource_select" setting to see which
454 resource to pick. */
jabber_buddy_by_jid(struct im_connection * ic,char * jid_,get_buddy_flags_t flags)455 struct jabber_buddy *jabber_buddy_by_jid(struct im_connection *ic, char *jid_, get_buddy_flags_t flags)
456 {
457 struct jabber_data *jd = ic->proto_data;
458 struct jabber_buddy *bud, *head;
459 char *s, *jid;
460
461 jid = jabber_normalize(jid_);
462
463 if ((s = strchr(jid, '/'))) {
464 int bare_exists = 0;
465
466 *s = 0;
467 if ((bud = g_hash_table_lookup(jd->buddies, jid))) {
468 bare_exists = 1;
469
470 if (bud->next) {
471 bud = bud->next;
472 }
473
474 /* Just return the first one for this bare JID. */
475 if (flags & GET_BUDDY_FIRST) {
476 *s = '/';
477 g_free(jid);
478 return bud;
479 }
480
481 /* Is this one of those no-resource buddies? */
482 if (bud->resource == NULL) {
483 *s = '/';
484 g_free(jid);
485 return NULL;
486 }
487
488 /* See if there's an exact match. */
489 for (; bud; bud = bud->next) {
490 if (strcmp(bud->resource, s + 1) == 0) {
491 break;
492 }
493 }
494 }
495
496 if (bud == NULL && (flags & GET_BUDDY_CREAT) &&
497 (bare_exists || bee_user_by_handle(ic->bee, ic, jid))) {
498 *s = '/';
499 bud = jabber_buddy_add(ic, jid);
500 }
501
502 g_free(jid);
503 return bud;
504 } else {
505 struct jabber_buddy *best_prio, *best_time;
506 char *set;
507
508 head = g_hash_table_lookup(jd->buddies, jid);
509 bud = (head && head->next) ? head->next : head;
510
511 g_free(jid);
512
513 if (bud == NULL) {
514 /* No match. Create it now? */
515 return ((flags & GET_BUDDY_CREAT) &&
516 bee_user_by_handle(ic->bee, ic, jid_)) ?
517 jabber_buddy_add(ic, jid_) : NULL;
518 } else if (bud->resource && (flags & GET_BUDDY_EXACT)) {
519 /* We want an exact match, so in thise case there shouldn't be a /resource. */
520 if (head != bud && head->resource == NULL) {
521 return head;
522 } else {
523 return NULL;
524 }
525 } else if (bud->resource == NULL || bud->next == NULL) {
526 /* No need for selection if there's only one option. */
527 return bud;
528 } else if (flags & GET_BUDDY_FIRST) {
529 /* Looks like the caller doesn't care about details. */
530 return bud;
531 } else if (flags & GET_BUDDY_BARE) {
532 return head;
533 }
534
535 best_prio = best_time = bud;
536 for (; bud; bud = bud->next) {
537 if (bud->priority > best_prio->priority) {
538 best_prio = bud;
539 }
540 if (bud->last_msg > best_time->last_msg) {
541 best_time = bud;
542 }
543 }
544
545 if ((set = set_getstr(&ic->acc->set, "resource_select")) == NULL) {
546 return NULL;
547 } else if (strcmp(set, "priority") == 0) {
548 return best_prio;
549 } else if (flags & GET_BUDDY_BARE_OK) { /* && strcmp( set, "activity" ) == 0 */
550 if (best_time->last_msg + set_getint(&ic->acc->set, "activity_timeout") >= time(NULL)) {
551 return best_time;
552 } else {
553 return head;
554 }
555 } else {
556 return best_time;
557 }
558 }
559 }
560
561 /* I'm keeping a separate ext_jid attribute to save a JID that makes sense
562 to export to BitlBee. This is mainly for groupchats right now. It's
563 a bit of a hack, but I just think having the user nickname in the hostname
564 part of the hostmask doesn't look nice on IRC. Normally you can convert
565 a normal JID to ext_jid by swapping the part before and after the / and
566 replacing the / with a =. But there should be some stripping (@s are
567 allowed in Jabber nicks...). */
jabber_buddy_by_ext_jid(struct im_connection * ic,char * jid_,get_buddy_flags_t flags)568 struct jabber_buddy *jabber_buddy_by_ext_jid(struct im_connection *ic, char *jid_, get_buddy_flags_t flags)
569 {
570 struct jabber_buddy *bud;
571 char *s, *jid;
572
573 jid = jabber_normalize_ext(jid_);
574
575 if ((s = strchr(jid, '=')) == NULL) {
576 g_free(jid);
577 return NULL;
578 }
579
580 for (bud = jabber_buddy_by_jid(ic, s + 1, GET_BUDDY_FIRST); bud; bud = bud->next) {
581 /* Hmmm, could happen if not all people in the chat are anonymized? */
582 if (bud->ext_jid == NULL) {
583 continue;
584 }
585
586 if (strcmp(bud->ext_jid, jid) == 0) {
587 break;
588 }
589 }
590
591 g_free(jid);
592
593 return bud;
594 }
595
596 /* Remove one specific full JID from our list. Use this when a buddy goes
597 off-line (because (s)he can still be online from a different location.
598 XXX: See above, we should accept bare JIDs too... */
jabber_buddy_remove(struct im_connection * ic,char * full_jid_)599 int jabber_buddy_remove(struct im_connection *ic, char *full_jid_)
600 {
601 struct jabber_data *jd = ic->proto_data;
602 struct jabber_buddy *bud, *prev = NULL, *bi;
603 char *s, *full_jid;
604
605 full_jid = jabber_normalize(full_jid_);
606
607 if ((s = strchr(full_jid, '/'))) {
608 *s = 0;
609 }
610
611 if ((bud = g_hash_table_lookup(jd->buddies, full_jid))) {
612 if (bud->next) {
613 bud = (prev = bud)->next;
614 }
615
616 /* If there's only one item in the list (and if the resource
617 matches), removing it is simple. (And the hash reference
618 should be removed too!) */
619 if (bud->next == NULL &&
620 ((s == NULL && bud->resource == NULL) ||
621 (bud->resource && s && strcmp(bud->resource, s + 1) == 0))) {
622 int st = jabber_buddy_remove_bare(ic, full_jid);
623 g_free(full_jid);
624 return st;
625 } else if (s == NULL || bud->resource == NULL) {
626 /* Tried to remove a bare JID while this JID does seem
627 to have resources... (Or the opposite.) *sigh* */
628 g_free(full_jid);
629 return 0;
630 } else {
631 for (bi = bud; bi; bi = (prev = bi)->next) {
632 if (strcmp(bi->resource, s + 1) == 0) {
633 break;
634 }
635 }
636
637 g_free(full_jid);
638
639 if (bi) {
640 if (prev) {
641 prev->next = bi->next;
642 } else {
643 /* Don't think this should ever happen anymore. */
644 g_hash_table_replace(jd->buddies, bi->bare_jid, bi->next);
645 }
646
647 g_free(bi->ext_jid);
648 g_free(bi->full_jid);
649 g_free(bi->away_message);
650 g_free(bi);
651
652 return 1;
653 } else {
654 return 0;
655 }
656 }
657 } else {
658 g_free(full_jid);
659 return 0;
660 }
661 }
662
663 /* Remove a buddy completely; removes all resources that belong to the
664 specified bare JID. Use this when removing someone from the contact
665 list, for example. */
jabber_buddy_remove_bare(struct im_connection * ic,char * bare_jid)666 int jabber_buddy_remove_bare(struct im_connection *ic, char *bare_jid)
667 {
668 struct jabber_data *jd = ic->proto_data;
669 struct jabber_buddy *bud, *next;
670
671 if (strchr(bare_jid, '/')) {
672 return 0;
673 }
674
675 if ((bud = jabber_buddy_by_jid(ic, bare_jid, GET_BUDDY_FIRST))) {
676 /* Most important: Remove the hash reference. We don't know
677 this buddy anymore. */
678 g_hash_table_remove(jd->buddies, bud->bare_jid);
679 g_free(bud->bare_jid);
680
681 /* Deallocate the linked list of resources. */
682 while (bud) {
683 /* ext_jid && anonymous means that this buddy is
684 specific to one groupchat (the one we're
685 currently cleaning up) so it can be deleted
686 completely. */
687 if (bud->ext_jid && bud->flags & JBFLAG_IS_ANONYMOUS) {
688 imcb_remove_buddy(ic, bud->ext_jid, NULL);
689 }
690
691 next = bud->next;
692 g_free(bud->ext_jid);
693 g_free(bud->full_jid);
694 g_free(bud->away_message);
695 g_free(bud);
696 bud = next;
697 }
698
699 return 1;
700 } else {
701 return 0;
702 }
703 }
704
jabber_buddy_remove_all_cb(gpointer key,gpointer value,gpointer data)705 static gboolean jabber_buddy_remove_all_cb(gpointer key, gpointer value, gpointer data)
706 {
707 struct jabber_buddy *bud, *next;
708
709 bud = value;
710 if (bud->bare_jid != bud->full_jid) {
711 g_free(bud->bare_jid);
712 }
713 while (bud) {
714 next = bud->next;
715 g_free(bud->ext_jid);
716 g_free(bud->full_jid);
717 g_free(bud->away_message);
718 g_free(bud);
719 bud = next;
720 }
721
722 return TRUE;
723 }
724
jabber_buddy_remove_all(struct im_connection * ic)725 void jabber_buddy_remove_all(struct im_connection *ic)
726 {
727 struct jabber_data *jd = ic->proto_data;
728
729 g_hash_table_foreach_remove(jd->buddies, jabber_buddy_remove_all_cb, NULL);
730 g_hash_table_destroy(jd->buddies);
731 }
732
jabber_get_timestamp(struct xt_node * xt)733 time_t jabber_get_timestamp(struct xt_node *xt)
734 {
735 struct xt_node *c;
736 char *s = NULL;
737 struct tm tp;
738 gboolean is_old = TRUE;
739 const char *format;
740
741 /* XEP-0091 has <x> */
742 c = xt_find_node_by_attr(xt->children, "x", "xmlns", XMLNS_DELAY_OLD);
743
744 if (!c || !(s = xt_find_attr(c, "stamp"))) {
745 is_old = FALSE;
746
747 /* XEP-0203 has <delay> */
748 c = xt_find_node_by_attr(xt->children, "delay", "xmlns", XMLNS_DELAY);
749 if (!c || !(s = xt_find_attr(c, "stamp"))) {
750 return 0;
751 }
752 }
753
754 memset(&tp, 0, sizeof(tp));
755
756 /* The other main difference between XEPs is the timestamp format */
757 format = (is_old) ? "%4d%2d%2dT%2d:%2d:%2d" : "%4d-%2d-%2dT%2d:%2d:%2dZ";
758
759 if (sscanf(s, format, &tp.tm_year, &tp.tm_mon, &tp.tm_mday,
760 &tp.tm_hour, &tp.tm_min, &tp.tm_sec) != 6) {
761 return 0;
762 }
763
764 tp.tm_year -= 1900;
765 tp.tm_mon--;
766
767 return mktime_utc(&tp);
768 }
769
jabber_error_parse(struct xt_node * node,char * xmlns)770 struct jabber_error *jabber_error_parse(struct xt_node *node, char *xmlns)
771 {
772 struct jabber_error *err;
773 struct xt_node *c;
774 char *s;
775
776 if (node == NULL) {
777 return NULL;
778 }
779
780 err = g_new0(struct jabber_error, 1);
781 err->type = xt_find_attr(node, "type");
782
783 for (c = node->children; c; c = c->next) {
784 if (!(s = xt_find_attr(c, "xmlns")) ||
785 strcmp(s, xmlns) != 0) {
786 continue;
787 }
788
789 if (strcmp(c->name, "text") != 0) {
790 err->code = c->name;
791 }
792 /* Only use the text if it doesn't have an xml:lang attribute,
793 if it's empty or if it's set to something English. */
794 else if (!(s = xt_find_attr(c, "xml:lang")) ||
795 !*s || strncmp(s, "en", 2) == 0) {
796 err->text = c->text;
797 }
798 }
799
800 return err;
801 }
802
jabber_error_free(struct jabber_error * err)803 void jabber_error_free(struct jabber_error *err)
804 {
805 g_free(err);
806 }
807
jabber_set_me(struct im_connection * ic,const char * me)808 gboolean jabber_set_me(struct im_connection *ic, const char *me)
809 {
810 struct jabber_data *jd = ic->proto_data;
811
812 if (strchr(me, '@') == NULL) {
813 return FALSE;
814 }
815
816 g_free(jd->username);
817 g_free(jd->me);
818
819 jd->me = jabber_normalize(me);
820 jd->server = strchr(jd->me, '@');
821 jd->username = g_strndup(jd->me, jd->server - jd->me);
822 jd->server++;
823
824 /* Set the "internal" account username, for groupchats */
825 g_free(jd->internal_jid);
826 jd->internal_jid = g_strdup(jd->me);
827
828 return TRUE;
829 }
830
831 /* Returns new reference! g_free() afterwards. */
jabber_get_bare_jid(char * jid)832 char *jabber_get_bare_jid(char *jid)
833 {
834 char *s = NULL;
835
836 if (jid == NULL) {
837 return NULL;
838 }
839
840 if ((s = strchr(jid, '/'))) {
841 return g_strndup(jid, s - jid);
842 } else {
843 return g_strdup(jid);
844 }
845 }
846