source: protocols/jabber/jabber_util.c @ 7e68015

Last change on this file since 7e68015 was e4145d9, checked in by dequis <dx@…>, at 2015-10-21T06:05:01Z

jabber: Fix incoming google talk typing notifications

jabber_buddy_by_jid() does the following:

head = g_hash_table_lookup(jd->buddies, jid);
bud = (head && head->next) ? head->next : head;

'head' has the one without resource, 'bud' has the first resource.

So if a resource is available, it uses it and ignores the head.
When asked for a bare JID (with no resource) and GET_BUDDY_EXACT, it
shouldn't do this, but it should return the head.

In other words, the problem was a message in this format:

<message from="username@…" ...>

Instead of

<message from="username@…/resource" ...>

This only deals with incoming typing notifications. See next commit.

  • Property mode set to 100644
File size: 21.7 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 "md5.h"
26#include "base64.h"
27
28static unsigned int next_id = 1;
29
30char *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
67char *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
76struct 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
102struct 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! */
139void 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
165void 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
173gboolean 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). */
180void 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
188gboolean 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
196xt_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
228const 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
237const 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
254const 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
271struct jabber_buddy_ask_data {
272        struct im_connection *ic;
273        char *handle;
274        char *realname;
275};
276
277static 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
289static 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
299void 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. */
313int jabber_compare_jid(const char *jid1, const char *jid2)
314{
315        int i;
316
317        for (i = 0;; i++) {
318                if (jid1[i] == '\0' || jid1[i] == '/' || jid2[i] == '\0' || jid2[i] == '/') {
319                        if ((jid1[i] == '\0' || jid1[i] == '/') && (jid2[i] == '\0' || jid2[i] == '/')) {
320                                break;
321                        }
322                        return FALSE;
323                }
324                if (g_ascii_tolower(jid1[i]) != g_ascii_tolower(jid2[i])) {
325                        return FALSE;
326                }
327        }
328
329        return TRUE;
330}
331
332/* The /resource part is case sensitive. This stops once we see a slash.
333   Returns a new string. Don't leak it! */
334char *jabber_normalize(const char *orig)
335{
336        char *lower, *new, *s;
337
338        if (!(s = strchr(orig, '/'))) {
339                return g_utf8_strdown(orig, -1);
340        }
341
342        lower = g_utf8_strdown(orig, (s - orig));    /* stop in s */
343        new = g_strconcat(lower, s, NULL);
344        g_free(lower);
345        return new;
346}
347
348/* Similar to jabber_normalize, but works with addresses in the form
349 * resource=chatroom@example.com */
350char *jabber_normalize_ext(const char *orig)
351{
352        char *lower, *new, *s;
353
354        if (!(s = strchr(orig, '='))) {
355                return g_utf8_strdown(orig, -1);
356        }
357
358        lower = g_utf8_strdown(s, -1);   /* start in s */
359
360        *s = 0;
361        new = g_strconcat(orig, lower, NULL);
362        *s = '=';
363
364        g_free(lower);
365        return new;
366}
367
368/* Adds a buddy/resource to our list. Returns NULL if full_jid is not really a
369   FULL jid or if we already have this buddy/resource. XXX: No, great, actually
370   buddies from transports don't (usually) have resources. So we'll really have
371   to deal with that properly. Set their ->resource property to NULL. Do *NOT*
372   allow to mix this stuff, though... */
373struct jabber_buddy *jabber_buddy_add(struct im_connection *ic, char *full_jid_)
374{
375        struct jabber_data *jd = ic->proto_data;
376        struct jabber_buddy *bud, *new, *bi;
377        char *s, *full_jid;
378
379        full_jid = jabber_normalize(full_jid_);
380
381        if ((s = strchr(full_jid, '/'))) {
382                *s = 0;
383        }
384
385        new = g_new0(struct jabber_buddy, 1);
386
387        if ((bud = g_hash_table_lookup(jd->buddies, full_jid))) {
388                /* The first entry is always a bare JID. If there are more, we
389                   should ignore the first one here. */
390                if (bud->next) {
391                        bud = bud->next;
392                }
393
394                /* If this is a transport buddy or whatever, it can't have more
395                   than one instance, so this is always wrong: */
396                if (s == NULL || bud->resource == NULL) {
397                        if (s) {
398                                *s = '/';
399                        }
400                        g_free(new);
401                        g_free(full_jid);
402                        return NULL;
403                }
404
405                new->bare_jid = bud->bare_jid;
406
407                /* We already have another resource for this buddy, add the
408                   new one to the list. */
409                for (bi = bud; bi; bi = bi->next) {
410                        /* Check for dupes. */
411                        if (strcmp(bi->resource, s + 1) == 0) {
412                                *s = '/';
413                                g_free(new);
414                                g_free(full_jid);
415                                return NULL;
416                        }
417                        /* Append the new item to the list. */
418                        else if (bi->next == NULL) {
419                                bi->next = new;
420                                break;
421                        }
422                }
423        } else {
424                new->full_jid = new->bare_jid = g_strdup(full_jid);
425                g_hash_table_insert(jd->buddies, new->bare_jid, new);
426
427                if (s) {
428                        new->next = g_new0(struct jabber_buddy, 1);
429                        new->next->bare_jid = new->bare_jid;
430                        new = new->next;
431                }
432        }
433
434        if (s) {
435                *s = '/';
436                new->full_jid = full_jid;
437                new->resource = strchr(new->full_jid, '/') + 1;
438        } else {
439                /* Let's waste some more bytes of RAM instead of to make
440                   memory management a total disaster here. And it saves
441                   me one g_free() call in this function. :-P */
442                new->full_jid = full_jid;
443        }
444
445        return new;
446}
447
448/* Finds a buddy from our structures. Can find both full- and bare JIDs. When
449   asked for a bare JID, it uses the "resource_select" setting to see which
450   resource to pick. */
451struct jabber_buddy *jabber_buddy_by_jid(struct im_connection *ic, char *jid_, get_buddy_flags_t flags)
452{
453        struct jabber_data *jd = ic->proto_data;
454        struct jabber_buddy *bud, *head;
455        char *s, *jid;
456
457        jid = jabber_normalize(jid_);
458
459        if ((s = strchr(jid, '/'))) {
460                int bare_exists = 0;
461
462                *s = 0;
463                if ((bud = g_hash_table_lookup(jd->buddies, jid))) {
464                        bare_exists = 1;
465
466                        if (bud->next) {
467                                bud = bud->next;
468                        }
469
470                        /* Just return the first one for this bare JID. */
471                        if (flags & GET_BUDDY_FIRST) {
472                                *s = '/';
473                                g_free(jid);
474                                return bud;
475                        }
476
477                        /* Is this one of those no-resource buddies? */
478                        if (bud->resource == NULL) {
479                                *s = '/';
480                                g_free(jid);
481                                return NULL;
482                        }
483
484                        /* See if there's an exact match. */
485                        for (; bud; bud = bud->next) {
486                                if (strcmp(bud->resource, s + 1) == 0) {
487                                        break;
488                                }
489                        }
490                }
491
492                if (bud == NULL && (flags & GET_BUDDY_CREAT) &&
493                    (bare_exists || bee_user_by_handle(ic->bee, ic, jid))) {
494                        *s = '/';
495                        bud = jabber_buddy_add(ic, jid);
496                }
497
498                g_free(jid);
499                return bud;
500        } else {
501                struct jabber_buddy *best_prio, *best_time;
502                char *set;
503
504                head = g_hash_table_lookup(jd->buddies, jid);
505                bud = (head && head->next) ? head->next : head;
506
507                g_free(jid);
508
509                if (bud == NULL) {
510                        /* No match. Create it now? */
511                        return ((flags & GET_BUDDY_CREAT) &&
512                                bee_user_by_handle(ic->bee, ic, jid_)) ?
513                               jabber_buddy_add(ic, jid_) : NULL;
514                } else if (bud->resource && (flags & GET_BUDDY_EXACT)) {
515                        /* We want an exact match, so in thise case there shouldn't be a /resource. */
516                        if (head != bud && head->resource == NULL) {
517                                return head;
518                        } else {
519                                return NULL;
520                        }
521                } else if (bud->resource == NULL || bud->next == NULL) {
522                        /* No need for selection if there's only one option. */
523                        return bud;
524                } else if (flags & GET_BUDDY_FIRST) {
525                        /* Looks like the caller doesn't care about details. */
526                        return bud;
527                } else if (flags & GET_BUDDY_BARE) {
528                        return head;
529                }
530
531                best_prio = best_time = bud;
532                for (; bud; bud = bud->next) {
533                        if (bud->priority > best_prio->priority) {
534                                best_prio = bud;
535                        }
536                        if (bud->last_msg > best_time->last_msg) {
537                                best_time = bud;
538                        }
539                }
540
541                if ((set = set_getstr(&ic->acc->set, "resource_select")) == NULL) {
542                        return NULL;
543                } else if (strcmp(set, "priority") == 0) {
544                        return best_prio;
545                } else if (flags & GET_BUDDY_BARE_OK) { /* && strcmp( set, "activity" ) == 0 */
546                        if (best_time->last_msg + set_getint(&ic->acc->set, "activity_timeout") >= time(NULL)) {
547                                return best_time;
548                        } else {
549                                return head;
550                        }
551                } else {
552                        return best_time;
553                }
554        }
555}
556
557/* I'm keeping a separate ext_jid attribute to save a JID that makes sense
558   to export to BitlBee. This is mainly for groupchats right now. It's
559   a bit of a hack, but I just think having the user nickname in the hostname
560   part of the hostmask doesn't look nice on IRC. Normally you can convert
561   a normal JID to ext_jid by swapping the part before and after the / and
562   replacing the / with a =. But there should be some stripping (@s are
563   allowed in Jabber nicks...). */
564struct jabber_buddy *jabber_buddy_by_ext_jid(struct im_connection *ic, char *jid_, get_buddy_flags_t flags)
565{
566        struct jabber_buddy *bud;
567        char *s, *jid;
568
569        jid = jabber_normalize_ext(jid_);
570
571        if ((s = strchr(jid, '=')) == NULL) {
572                g_free(jid);
573                return NULL;
574        }
575
576        for (bud = jabber_buddy_by_jid(ic, s + 1, GET_BUDDY_FIRST); bud; bud = bud->next) {
577                /* Hmmm, could happen if not all people in the chat are anonymized? */
578                if (bud->ext_jid == NULL) {
579                        continue;
580                }
581
582                if (strcmp(bud->ext_jid, jid) == 0) {
583                        break;
584                }
585        }
586
587        g_free(jid);
588
589        return bud;
590}
591
592/* Remove one specific full JID from our list. Use this when a buddy goes
593   off-line (because (s)he can still be online from a different location.
594   XXX: See above, we should accept bare JIDs too... */
595int jabber_buddy_remove(struct im_connection *ic, char *full_jid_)
596{
597        struct jabber_data *jd = ic->proto_data;
598        struct jabber_buddy *bud, *prev = NULL, *bi;
599        char *s, *full_jid;
600
601        full_jid = jabber_normalize(full_jid_);
602
603        if ((s = strchr(full_jid, '/'))) {
604                *s = 0;
605        }
606
607        if ((bud = g_hash_table_lookup(jd->buddies, full_jid))) {
608                if (bud->next) {
609                        bud = (prev = bud)->next;
610                }
611
612                /* If there's only one item in the list (and if the resource
613                   matches), removing it is simple. (And the hash reference
614                   should be removed too!) */
615                if (bud->next == NULL &&
616                    ((s == NULL && bud->resource == NULL) ||
617                     (bud->resource && s && strcmp(bud->resource, s + 1) == 0))) {
618                        int st = jabber_buddy_remove_bare(ic, full_jid);
619                        g_free(full_jid);
620                        return st;
621                } else if (s == NULL || bud->resource == NULL) {
622                        /* Tried to remove a bare JID while this JID does seem
623                           to have resources... (Or the opposite.) *sigh* */
624                        g_free(full_jid);
625                        return 0;
626                } else {
627                        for (bi = bud; bi; bi = (prev = bi)->next) {
628                                if (strcmp(bi->resource, s + 1) == 0) {
629                                        break;
630                                }
631                        }
632
633                        g_free(full_jid);
634
635                        if (bi) {
636                                if (prev) {
637                                        prev->next = bi->next;
638                                } else {
639                                        /* Don't think this should ever happen anymore. */
640                                        g_hash_table_replace(jd->buddies, bi->bare_jid, bi->next);
641                                }
642
643                                g_free(bi->ext_jid);
644                                g_free(bi->full_jid);
645                                g_free(bi->away_message);
646                                g_free(bi);
647
648                                return 1;
649                        } else {
650                                return 0;
651                        }
652                }
653        } else {
654                g_free(full_jid);
655                return 0;
656        }
657}
658
659/* Remove a buddy completely; removes all resources that belong to the
660   specified bare JID. Use this when removing someone from the contact
661   list, for example. */
662int jabber_buddy_remove_bare(struct im_connection *ic, char *bare_jid)
663{
664        struct jabber_data *jd = ic->proto_data;
665        struct jabber_buddy *bud, *next;
666
667        if (strchr(bare_jid, '/')) {
668                return 0;
669        }
670
671        if ((bud = jabber_buddy_by_jid(ic, bare_jid, GET_BUDDY_FIRST))) {
672                /* Most important: Remove the hash reference. We don't know
673                   this buddy anymore. */
674                g_hash_table_remove(jd->buddies, bud->bare_jid);
675                g_free(bud->bare_jid);
676
677                /* Deallocate the linked list of resources. */
678                while (bud) {
679                        /* ext_jid && anonymous means that this buddy is
680                           specific to one groupchat (the one we're
681                           currently cleaning up) so it can be deleted
682                           completely. */
683                        if (bud->ext_jid && bud->flags & JBFLAG_IS_ANONYMOUS) {
684                                imcb_remove_buddy(ic, bud->ext_jid, NULL);
685                        }
686
687                        next = bud->next;
688                        g_free(bud->ext_jid);
689                        g_free(bud->full_jid);
690                        g_free(bud->away_message);
691                        g_free(bud);
692                        bud = next;
693                }
694
695                return 1;
696        } else {
697                return 0;
698        }
699}
700
701static gboolean jabber_buddy_remove_all_cb(gpointer key, gpointer value, gpointer data)
702{
703        struct jabber_buddy *bud, *next;
704
705        bud = value;
706        if (bud->bare_jid != bud->full_jid) {
707                g_free(bud->bare_jid);
708        }
709        while (bud) {
710                next = bud->next;
711                g_free(bud->ext_jid);
712                g_free(bud->full_jid);
713                g_free(bud->away_message);
714                g_free(bud);
715                bud = next;
716        }
717
718        return TRUE;
719}
720
721void jabber_buddy_remove_all(struct im_connection *ic)
722{
723        struct jabber_data *jd = ic->proto_data;
724
725        g_hash_table_foreach_remove(jd->buddies, jabber_buddy_remove_all_cb, NULL);
726        g_hash_table_destroy(jd->buddies);
727}
728
729time_t jabber_get_timestamp(struct xt_node *xt)
730{
731        struct xt_node *c;
732        char *s = NULL;
733        struct tm tp;
734        gboolean is_old = TRUE;
735        const char *format;
736
737        /* XEP-0091 has <x> */
738        c = xt_find_node_by_attr(xt->children, "x", "xmlns", XMLNS_DELAY_OLD);
739
740        if (!c || !(s = xt_find_attr(c, "stamp"))) {
741                is_old = FALSE;
742
743                /* XEP-0203 has <delay> */
744                c = xt_find_node_by_attr(xt->children, "delay", "xmlns", XMLNS_DELAY);
745                if (!c || !(s = xt_find_attr(c, "stamp"))) {
746                        return 0;
747                }
748        }
749
750        memset(&tp, 0, sizeof(tp));
751
752        /* The other main difference between XEPs is the timestamp format */
753        format = (is_old) ? "%4d%2d%2dT%2d:%2d:%2d" : "%4d-%2d-%2dT%2d:%2d:%2dZ";
754
755        if (sscanf(s, format, &tp.tm_year, &tp.tm_mon, &tp.tm_mday,
756                   &tp.tm_hour, &tp.tm_min, &tp.tm_sec) != 6) {
757                return 0;
758        }
759
760        tp.tm_year -= 1900;
761        tp.tm_mon--;
762
763        return mktime_utc(&tp);
764}
765
766struct jabber_error *jabber_error_parse(struct xt_node *node, char *xmlns)
767{
768        struct jabber_error *err;
769        struct xt_node *c;
770        char *s;
771
772        if (node == NULL) {
773                return NULL;
774        }
775
776        err = g_new0(struct jabber_error, 1);
777        err->type = xt_find_attr(node, "type");
778
779        for (c = node->children; c; c = c->next) {
780                if (!(s = xt_find_attr(c, "xmlns")) ||
781                    strcmp(s, xmlns) != 0) {
782                        continue;
783                }
784
785                if (strcmp(c->name, "text") != 0) {
786                        err->code = c->name;
787                }
788                /* Only use the text if it doesn't have an xml:lang attribute,
789                   if it's empty or if it's set to something English. */
790                else if (!(s = xt_find_attr(c, "xml:lang")) ||
791                         !*s || strncmp(s, "en", 2) == 0) {
792                        err->text = c->text;
793                }
794        }
795
796        return err;
797}
798
799void jabber_error_free(struct jabber_error *err)
800{
801        g_free(err);
802}
803
804gboolean jabber_set_me(struct im_connection *ic, const char *me)
805{
806        struct jabber_data *jd = ic->proto_data;
807
808        if (strchr(me, '@') == NULL) {
809                return FALSE;
810        }
811
812        g_free(jd->username);
813        g_free(jd->me);
814
815        jd->me = jabber_normalize(me);
816        jd->server = strchr(jd->me, '@');
817        jd->username = g_strndup(jd->me, jd->server - jd->me);
818        jd->server++;
819
820        /* Set the "internal" account username, for groupchats */
821        g_free(jd->internal_jid);
822        jd->internal_jid = g_strdup(jd->me);
823
824        return TRUE;
825}
826
827/* Returns new reference! g_free() afterwards. */
828char *jabber_get_bare_jid(char *jid)
829{
830        char *s = NULL;
831
832        if (jid == NULL) {
833                return NULL;
834        }
835
836        if ((s = strchr(jid, '/'))) {
837                return g_strndup(jid, s - jid);
838        } else {
839                return g_strdup(jid);
840        }
841}
Note: See TracBrowser for help on using the repository browser.