source: protocols/jabber/jabber_util.c

Last change on this file was 5d9257cf, checked in by GitHub <noreply@…>, at 2023-04-07T20:43:51Z

jabber: fix regression with gchecksum in jabber_cache_add (#186)

This crashed with "The checksum '%s' has been closed and cannot be
updated anymore." (which is also a user after free)

Issue introduced in 1bdc6696aefb617873b56001f81d649d6ca3aa8e which is
not in any released version.

There was this md5_digest_keep() function that freed a copy of the
checksum instead of the one you pass. The code that it was replaced with
ignored this distinction, and since jabber_cache_add() needs to reuse the
GChecksum this led to this issue.

No other place in the bitlbee code used this function.

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