source: protocols/jabber/jabber_util.c @ e199620

Last change on this file since e199620 was 2dc394c, checked in by dequis <dx@…>, at 2016-08-24T00:02:20Z

jabber: handle nulls in jabber_compare_jid

As far as I can see this isn't remotely exploitable (and if it were it
would be just DoS of the child process), but i'm still looking into it

  • Property mode set to 100644
File size: 21.7 KB
RevLine 
[f06894d]1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
[21167d2]4*  Jabber module - Misc. stuff                                              *
[f06894d]5*                                                                           *
[5ebff60]6*  Copyright 2006-2010 Wilmer van der Gaast <wilmer@gaast.net>
[f06894d]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"
[89d736a]25#include "md5.h"
26#include "base64.h"
[f06894d]27
[dfa41a4]28static unsigned int next_id = 1;
[21167d2]29
[5ebff60]30char *set_eval_priority(set_t *set, char *value)
[f06894d]31{
32        account_t *acc = set->data;
[788a1af]33        int i;
[5ebff60]34
35        if (sscanf(value, "%d", &i) == 1) {
[788a1af]36                /* Priority is a signed 8-bit integer, according to RFC 3921. */
[5ebff60]37                if (i < -128 || i > 127) {
[7125cb3]38                        return SET_INVALID;
[5ebff60]39                }
40        } else {
[7125cb3]41                return SET_INVALID;
[5ebff60]42        }
43
[172a73f1]44        /* Only run this stuff if the account is online ATM,
45           and if the setting seems to be acceptable. */
[5ebff60]46        if (acc->ic) {
[ebe7b36]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
[e35d1a1]51                   after the (this) eval returns a non-NULL value.
[5ebff60]52
[ebe7b36]53                   So now I can choose between implementing post-set
54                   functions next to evals, or just do this little hack: */
[5ebff60]55
56                g_free(set->value);
57                set->value = g_strdup(value);
58
[ebe7b36]59                /* (Yes, sorry, I prefer the hack. :-P) */
[5ebff60]60
61                presence_send_update(acc->ic);
[f06894d]62        }
[5ebff60]63
[788a1af]64        return value;
[f06894d]65}
66
[5ebff60]67char *set_eval_tls(set_t *set, char *value)
[f06894d]68{
[5ebff60]69        if (g_strcasecmp(value, "try") == 0) {
[f06894d]70                return value;
[5ebff60]71        } else {
72                return set_eval_bool(set, value);
73        }
[f06894d]74}
[21167d2]75
[5ebff60]76struct xt_node *jabber_make_packet(char *name, char *type, char *to, struct xt_node *children)
[21167d2]77{
78        struct xt_node *node;
[5ebff60]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
[dfa41a4]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. */
[5ebff60]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);
[dfa41a4]97        }
[5ebff60]98
[fe7a554]99        return node;
100}
101
[5ebff60]102struct xt_node *jabber_make_error_packet(struct xt_node *orig, char *err_cond, char *err_type, char *err_code)
[259edd4]103{
104        struct xt_node *node, *c;
105        char *to;
[5ebff60]106
[259edd4]107        /* Create the "defined-condition" tag. */
[5ebff60]108        c = xt_new_node(err_cond, NULL, NULL);
109        xt_add_attr(c, "xmlns", XMLNS_STANZA_ERROR);
110
[259edd4]111        /* Put it in an <error> tag. */
[5ebff60]112        c = xt_new_node("error", NULL, c);
113        xt_add_attr(c, "type", err_type);
114
[2c2df7d]115        /* Add the error code, if present */
[5ebff60]116        if (err_code) {
117                xt_add_attr(c, "code", err_code);
118        }
119
[259edd4]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. */
[5ebff60]123        node = xt_dup(orig);
124        xt_add_child(node, c);
125        xt_add_attr(node, "type", "error");
126
[259edd4]127        /* Return to sender. */
[5ebff60]128        if ((to = xt_find_attr(node, "from"))) {
129                xt_add_attr(node, "to", to);
130                xt_remove_attr(node, "from");
[259edd4]131        }
[5ebff60]132
[259edd4]133        return node;
134}
135
[dfa41a4]136/* Cache a node/packet for later use. Mainly useful for IQ packets if you need
[fe7a554]137   them when you receive the response. Use this BEFORE sending the packet so
[e35d1a1]138   it'll get a new id= tag, and do NOT free() the packet after sending it! */
[5ebff60]139void jabber_cache_add(struct im_connection *ic, struct xt_node *node, jabber_cache_event func)
[fe7a554]140{
[0da65d5]141        struct jabber_data *jd = ic->proto_data;
[5ebff60]142        struct jabber_cache_entry *entry = g_new0(struct jabber_cache_entry, 1);
[89d736a]143        md5_state_t id_hash;
144        md5_byte_t id_sum[16];
145        char *id, *asc_hash;
[5ebff60]146
147        next_id++;
148
[89d736a]149        id_hash = jd->cached_id_prefix;
[5ebff60]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
[038d17f]159        entry->node = node;
[861c199]160        entry->func = func;
[5ebff60]161        entry->saved_at = time(NULL);
162        g_hash_table_insert(jd->node_cache, xt_find_attr(node, "id"), entry);
[fe7a554]163}
164
[5ebff60]165void jabber_cache_entry_free(gpointer data)
[038d17f]166{
167        struct jabber_cache_entry *entry = data;
[5ebff60]168
169        xt_free_node(entry->node);
170        g_free(entry);
[038d17f]171}
172
[5ebff60]173gboolean jabber_cache_clean_entry(gpointer key, gpointer entry, gpointer nullpointer);
[038d17f]174
[861c199]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). */
[5ebff60]180void jabber_cache_clean(struct im_connection *ic)
[038d17f]181{
[0da65d5]182        struct jabber_data *jd = ic->proto_data;
[5ebff60]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);
[038d17f]186}
187
[5ebff60]188gboolean jabber_cache_clean_entry(gpointer key, gpointer entry_, gpointer threshold_)
[038d17f]189{
190        struct jabber_cache_entry *entry = entry_;
[979cfb4]191        time_t *threshold = threshold_;
[5ebff60]192
[979cfb4]193        return entry->saved_at < *threshold;
[21167d2]194}
[5e202b0]195
[5ebff60]196xt_status jabber_cache_handle_packet(struct im_connection *ic, struct xt_node *node)
[4306d8b]197{
198        struct jabber_data *jd = ic->proto_data;
199        struct jabber_cache_entry *entry;
200        char *s;
[5ebff60]201
202        if ((s = xt_find_attr(node, "id")) == NULL ||
203            strncmp(s, JABBER_CACHED_ID, strlen(JABBER_CACHED_ID)) != 0) {
[4306d8b]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        }
[5ebff60]209
210        entry = g_hash_table_lookup(jd->node_cache, s);
211
212        if (entry == NULL) {
[89d736a]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.
[5ebff60]217
[43462708]218                imcb_log( ic, "Warning: Received %s-%s packet with unknown/expired ID %s!",
[4306d8b]219                              node->name, xt_find_attr( node, "type" ) ? : "(no type)", s );
[89d736a]220                */
[5ebff60]221        } else if (entry->func) {
222                return entry->func(ic, node, entry->node);
[4306d8b]223        }
[5ebff60]224
[4306d8b]225        return XT_HANDLED;
226}
227
[5e202b0]228const struct jabber_away_state jabber_away_state_list[] =
229{
230        { "away",  "Away" },
[840bba8]231        { "chat",  "Free for Chat" },   /* WTF actually uses this? */
[5e202b0]232        { "dnd",   "Do not Disturb" },
233        { "xa",    "Extended Away" },
234        { "",      NULL }
235};
236
[5ebff60]237const struct jabber_away_state *jabber_away_state_by_code(char *code)
[5e202b0]238{
239        int i;
[5ebff60]240
241        if (code == NULL) {
[840bba8]242                return NULL;
[5ebff60]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) {
[5e202b0]247                        return jabber_away_state_list + i;
[5ebff60]248                }
249        }
250
[5e202b0]251        return NULL;
252}
253
[5ebff60]254const struct jabber_away_state *jabber_away_state_by_name(char *name)
[5e202b0]255{
256        int i;
[5ebff60]257
258        if (name == NULL) {
[840bba8]259                return NULL;
[5ebff60]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) {
[5e202b0]264                        return jabber_away_state_list + i;
[5ebff60]265                }
266        }
267
[5e202b0]268        return NULL;
269}
[8e5e2e9]270
[5ebff60]271struct jabber_buddy_ask_data {
[0da65d5]272        struct im_connection *ic;
[8e5e2e9]273        char *handle;
274        char *realname;
275};
276
[5ebff60]277static void jabber_buddy_ask_yes(void *data)
[8e5e2e9]278{
[9143aeb]279        struct jabber_buddy_ask_data *bla = data;
[5ebff60]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);
[8e5e2e9]287}
288
[5ebff60]289static void jabber_buddy_ask_no(void *data)
[8e5e2e9]290{
[9143aeb]291        struct jabber_buddy_ask_data *bla = data;
[5ebff60]292
293        presence_send_request(bla->ic, bla->handle, "unsubscribed");
294
295        g_free(bla->handle);
296        g_free(bla);
[8e5e2e9]297}
298
[5ebff60]299void jabber_buddy_ask(struct im_connection *ic, char *handle)
[8e5e2e9]300{
[5ebff60]301        struct jabber_buddy_ask_data *bla = g_new0(struct jabber_buddy_ask_data, 1);
[8e5e2e9]302        char *buf;
[5ebff60]303
[0da65d5]304        bla->ic = ic;
[5ebff60]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);
[8e5e2e9]310}
[6a1128d]311
[72721cd]312/* Compares just the bare portions of two Jabber IDs. */
[5ebff60]313int jabber_compare_jid(const char *jid1, const char *jid2)
[757515a]314{
315        int i;
[5ebff60]316
[2dc394c]317        if (!jid1 || !jid2) {
318                return FALSE;
319        }
320
[5ebff60]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] == '/')) {
[757515a]324                                break;
[5ebff60]325                        }
[757515a]326                        return FALSE;
327                }
[5ebff60]328                if (g_ascii_tolower(jid1[i]) != g_ascii_tolower(jid2[i])) {
[757515a]329                        return FALSE;
330                }
331        }
[5ebff60]332
[757515a]333        return TRUE;
334}
335
[4cff28f]336/* The /resource part is case sensitive. This stops once we see a slash.
337   Returns a new string. Don't leak it! */
[5ebff60]338char *jabber_normalize(const char *orig)
[0d3f30f]339{
[4cff28f]340        char *lower, *new, *s;
341
[5ebff60]342        if (!(s = strchr(orig, '/'))) {
343                return g_utf8_strdown(orig, -1);
344        }
[4cff28f]345
[5ebff60]346        lower = g_utf8_strdown(orig, (s - orig));    /* stop in s */
347        new = g_strconcat(lower, s, NULL);
348        g_free(lower);
[4cff28f]349        return new;
350}
351
352/* Similar to jabber_normalize, but works with addresses in the form
353 * resource=chatroom@example.com */
[5ebff60]354char *jabber_normalize_ext(const char *orig)
[4cff28f]355{
356        char *lower, *new, *s;
357
[5ebff60]358        if (!(s = strchr(orig, '='))) {
359                return g_utf8_strdown(orig, -1);
360        }
[4cff28f]361
[5ebff60]362        lower = g_utf8_strdown(s, -1);   /* start in s */
[4cff28f]363
364        *s = 0;
[5ebff60]365        new = g_strconcat(orig, lower, NULL);
[4cff28f]366        *s = '=';
367
[5ebff60]368        g_free(lower);
[0d3f30f]369        return new;
370}
371
[6a1128d]372/* Adds a buddy/resource to our list. Returns NULL if full_jid is not really a
[0d3f30f]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... */
[5ebff60]377struct jabber_buddy *jabber_buddy_add(struct im_connection *ic, char *full_jid_)
[6a1128d]378{
[0da65d5]379        struct jabber_data *jd = ic->proto_data;
[6a1128d]380        struct jabber_buddy *bud, *new, *bi;
[0d3f30f]381        char *s, *full_jid;
[5ebff60]382
383        full_jid = jabber_normalize(full_jid_);
384
385        if ((s = strchr(full_jid, '/'))) {
[0d3f30f]386                *s = 0;
[5ebff60]387        }
388
389        new = g_new0(struct jabber_buddy, 1);
390
391        if ((bud = g_hash_table_lookup(jd->buddies, full_jid))) {
[76c85b4c]392                /* The first entry is always a bare JID. If there are more, we
393                   should ignore the first one here. */
[5ebff60]394                if (bud->next) {
[76c85b4c]395                        bud = bud->next;
[5ebff60]396                }
397
[0d3f30f]398                /* If this is a transport buddy or whatever, it can't have more
399                   than one instance, so this is always wrong: */
[5ebff60]400                if (s == NULL || bud->resource == NULL) {
401                        if (s) {
402                                *s = '/';
403                        }
404                        g_free(new);
405                        g_free(full_jid);
[0d3f30f]406                        return NULL;
407                }
[5ebff60]408
[0d3f30f]409                new->bare_jid = bud->bare_jid;
[5ebff60]410
[6a1128d]411                /* We already have another resource for this buddy, add the
412                   new one to the list. */
[5ebff60]413                for (bi = bud; bi; bi = bi->next) {
[0d3f30f]414                        /* Check for dupes. */
[5ebff60]415                        if (strcmp(bi->resource, s + 1) == 0) {
[6a1128d]416                                *s = '/';
[5ebff60]417                                g_free(new);
418                                g_free(full_jid);
[6a1128d]419                                return NULL;
420                        }
421                        /* Append the new item to the list. */
[5ebff60]422                        else if (bi->next == NULL) {
[6a1128d]423                                bi->next = new;
424                                break;
425                        }
426                }
[5ebff60]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);
[76c85b4c]433                        new->next->bare_jid = new->bare_jid;
434                        new = new->next;
435                }
[6a1128d]436        }
[5ebff60]437
438        if (s) {
[0d3f30f]439                *s = '/';
440                new->full_jid = full_jid;
[5ebff60]441                new->resource = strchr(new->full_jid, '/') + 1;
442        } else {
[0d3f30f]443                /* Let's waste some more bytes of RAM instead of to make
[b9f8b87]444                   memory management a total disaster here. And it saves
445                   me one g_free() call in this function. :-P */
[0d3f30f]446                new->full_jid = full_jid;
447        }
[5ebff60]448
[6a1128d]449        return new;
450}
451
[788a1af]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. */
[5ebff60]455struct jabber_buddy *jabber_buddy_by_jid(struct im_connection *ic, char *jid_, get_buddy_flags_t flags)
[6a1128d]456{
[0da65d5]457        struct jabber_data *jd = ic->proto_data;
[76c85b4c]458        struct jabber_buddy *bud, *head;
[0d3f30f]459        char *s, *jid;
[5ebff60]460
461        jid = jabber_normalize(jid_);
462
463        if ((s = strchr(jid, '/'))) {
[08e5bb2]464                int bare_exists = 0;
[5ebff60]465
[6a1128d]466                *s = 0;
[5ebff60]467                if ((bud = g_hash_table_lookup(jd->buddies, jid))) {
[08e5bb2]468                        bare_exists = 1;
[5ebff60]469
470                        if (bud->next) {
[76c85b4c]471                                bud = bud->next;
[5ebff60]472                        }
473
[0adce21]474                        /* Just return the first one for this bare JID. */
[5ebff60]475                        if (flags & GET_BUDDY_FIRST) {
[0adce21]476                                *s = '/';
[5ebff60]477                                g_free(jid);
[0adce21]478                                return bud;
479                        }
[5ebff60]480
[0d3f30f]481                        /* Is this one of those no-resource buddies? */
[5ebff60]482                        if (bud->resource == NULL) {
[0adce21]483                                *s = '/';
[5ebff60]484                                g_free(jid);
[16b5f86]485                                return NULL;
[0d3f30f]486                        }
[5ebff60]487
[0adce21]488                        /* See if there's an exact match. */
[5ebff60]489                        for (; bud; bud = bud->next) {
490                                if (strcmp(bud->resource, s + 1) == 0) {
[0adce21]491                                        break;
[5ebff60]492                                }
493                        }
[0d3f30f]494                }
[5ebff60]495
496                if (bud == NULL && (flags & GET_BUDDY_CREAT) &&
497                    (bare_exists || bee_user_by_handle(ic->bee, ic, jid))) {
[16b5f86]498                        *s = '/';
[5ebff60]499                        bud = jabber_buddy_add(ic, jid);
[16b5f86]500                }
[5ebff60]501
502                g_free(jid);
[0d3f30f]503                return bud;
[5ebff60]504        } else {
[a21a8ac]505                struct jabber_buddy *best_prio, *best_time;
506                char *set;
[5ebff60]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) {
[16b5f86]514                        /* No match. Create it now? */
[5ebff60]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)) {
[16b5f86]519                        /* We want an exact match, so in thise case there shouldn't be a /resource. */
[e4145d9]520                        if (head != bud && head->resource == NULL) {
521                                return head;
522                        } else {
523                                return NULL;
524                        }
[5ebff60]525                } else if (bud->resource == NULL || bud->next == NULL) {
[16b5f86]526                        /* No need for selection if there's only one option. */
[0d3f30f]527                        return bud;
[5ebff60]528                } else if (flags & GET_BUDDY_FIRST) {
[0adce21]529                        /* Looks like the caller doesn't care about details. */
530                        return bud;
[5ebff60]531                } else if (flags & GET_BUDDY_BARE) {
[76c85b4c]532                        return head;
[5ebff60]533                }
534
[0d3f30f]535                best_prio = best_time = bud;
[5ebff60]536                for (; bud; bud = bud->next) {
537                        if (bud->priority > best_prio->priority) {
[a21a8ac]538                                best_prio = bud;
[5ebff60]539                        }
540                        if (bud->last_msg > best_time->last_msg) {
[a21a8ac]541                                best_time = bud;
[5ebff60]542                        }
[a21a8ac]543                }
[5ebff60]544
545                if ((set = set_getstr(&ic->acc->set, "resource_select")) == NULL) {
[a21a8ac]546                        return NULL;
[5ebff60]547                } else if (strcmp(set, "priority") == 0) {
[a21a8ac]548                        return best_prio;
[5ebff60]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)) {
[76c85b4c]551                                return best_time;
[5ebff60]552                        } else {
[76c85b4c]553                                return head;
[5ebff60]554                        }
555                } else {
[76c85b4c]556                        return best_time;
[5ebff60]557                }
[6a1128d]558        }
559}
560
[b9f8b87]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...). */
[5ebff60]568struct jabber_buddy *jabber_buddy_by_ext_jid(struct im_connection *ic, char *jid_, get_buddy_flags_t flags)
[b9f8b87]569{
570        struct jabber_buddy *bud;
571        char *s, *jid;
[5ebff60]572
573        jid = jabber_normalize_ext(jid_);
574
575        if ((s = strchr(jid, '=')) == NULL) {
[098a75b]576                g_free(jid);
[b9f8b87]577                return NULL;
[5ebff60]578        }
579
580        for (bud = jabber_buddy_by_jid(ic, s + 1, GET_BUDDY_FIRST); bud; bud = bud->next) {
[b9f8b87]581                /* Hmmm, could happen if not all people in the chat are anonymized? */
[5ebff60]582                if (bud->ext_jid == NULL) {
[b9f8b87]583                        continue;
[5ebff60]584                }
585
586                if (strcmp(bud->ext_jid, jid) == 0) {
[b9f8b87]587                        break;
[5ebff60]588                }
[b9f8b87]589        }
[5ebff60]590
591        g_free(jid);
592
[b9f8b87]593        return bud;
594}
595
[788a1af]596/* Remove one specific full JID from our list. Use this when a buddy goes
[0d3f30f]597   off-line (because (s)he can still be online from a different location.
598   XXX: See above, we should accept bare JIDs too... */
[5ebff60]599int jabber_buddy_remove(struct im_connection *ic, char *full_jid_)
[6a1128d]600{
[0da65d5]601        struct jabber_data *jd = ic->proto_data;
[842cd8d]602        struct jabber_buddy *bud, *prev = NULL, *bi;
[0d3f30f]603        char *s, *full_jid;
[5ebff60]604
605        full_jid = jabber_normalize(full_jid_);
606
607        if ((s = strchr(full_jid, '/'))) {
[0d3f30f]608                *s = 0;
[5ebff60]609        }
610
611        if ((bud = g_hash_table_lookup(jd->buddies, full_jid))) {
612                if (bud->next) {
613                        bud = (prev = bud)->next;
614                }
615
[6a1128d]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!) */
[5ebff60]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);
[03e6c52]624                        return st;
[5ebff60]625                } else if (s == NULL || bud->resource == NULL) {
[0d3f30f]626                        /* Tried to remove a bare JID while this JID does seem
627                           to have resources... (Or the opposite.) *sigh* */
[5ebff60]628                        g_free(full_jid);
[0d3f30f]629                        return 0;
[5ebff60]630                } else {
631                        for (bi = bud; bi; bi = (prev = bi)->next) {
632                                if (strcmp(bi->resource, s + 1) == 0) {
[6a1128d]633                                        break;
[5ebff60]634                                }
635                        }
636
637                        g_free(full_jid);
638
639                        if (bi) {
640                                if (prev) {
[6a1128d]641                                        prev->next = bi->next;
[5ebff60]642                                } else {
[842cd8d]643                                        /* Don't think this should ever happen anymore. */
[5ebff60]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
[0d3f30f]652                                return 1;
[5ebff60]653                        } else {
[6a1128d]654                                return 0;
655                        }
656                }
[5ebff60]657        } else {
658                g_free(full_jid);
[6a1128d]659                return 0;
660        }
661}
[788a1af]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. */
[5ebff60]666int jabber_buddy_remove_bare(struct im_connection *ic, char *bare_jid)
[788a1af]667{
[0da65d5]668        struct jabber_data *jd = ic->proto_data;
[788a1af]669        struct jabber_buddy *bud, *next;
[5ebff60]670
671        if (strchr(bare_jid, '/')) {
[788a1af]672                return 0;
[5ebff60]673        }
674
675        if ((bud = jabber_buddy_by_jid(ic, bare_jid, GET_BUDDY_FIRST))) {
[788a1af]676                /* Most important: Remove the hash reference. We don't know
677                   this buddy anymore. */
[5ebff60]678                g_hash_table_remove(jd->buddies, bud->bare_jid);
679                g_free(bud->bare_jid);
680
[788a1af]681                /* Deallocate the linked list of resources. */
[5ebff60]682                while (bud) {
[9da0bbf]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. */
[5ebff60]687                        if (bud->ext_jid && bud->flags & JBFLAG_IS_ANONYMOUS) {
688                                imcb_remove_buddy(ic, bud->ext_jid, NULL);
689                        }
690
[788a1af]691                        next = bud->next;
[5ebff60]692                        g_free(bud->ext_jid);
693                        g_free(bud->full_jid);
694                        g_free(bud->away_message);
695                        g_free(bud);
[788a1af]696                        bud = next;
697                }
[5ebff60]698
[788a1af]699                return 1;
[5ebff60]700        } else {
[788a1af]701                return 0;
702        }
703}
[e35d1a1]704
[5ebff60]705static gboolean jabber_buddy_remove_all_cb(gpointer key, gpointer value, gpointer data)
[04a927c]706{
707        struct jabber_buddy *bud, *next;
[5ebff60]708
[04a927c]709        bud = value;
[5ebff60]710        if (bud->bare_jid != bud->full_jid) {
711                g_free(bud->bare_jid);
712        }
713        while (bud) {
[04a927c]714                next = bud->next;
[5ebff60]715                g_free(bud->ext_jid);
716                g_free(bud->full_jid);
717                g_free(bud->away_message);
718                g_free(bud);
[04a927c]719                bud = next;
720        }
[5ebff60]721
[04a927c]722        return TRUE;
723}
724
[5ebff60]725void jabber_buddy_remove_all(struct im_connection *ic)
[04a927c]726{
727        struct jabber_data *jd = ic->proto_data;
[5ebff60]728
729        g_hash_table_foreach_remove(jd->buddies, jabber_buddy_remove_all_cb, NULL);
730        g_hash_table_destroy(jd->buddies);
[04a927c]731}
732
[5ebff60]733time_t jabber_get_timestamp(struct xt_node *xt)
[43671b9]734{
735        struct xt_node *c;
736        char *s = NULL;
[2e3a857]737        struct tm tp;
[7b40f17]738        gboolean is_old = TRUE;
739        const char *format;
740
741        /* XEP-0091 has <x> */
[5ebff60]742        c = xt_find_node_by_attr(xt->children, "x", "xmlns", XMLNS_DELAY_OLD);
[7b40f17]743
[5ebff60]744        if (!c || !(s = xt_find_attr(c, "stamp"))) {
[7b40f17]745                is_old = FALSE;
746
747                /* XEP-0203 has <delay> */
[5ebff60]748                c = xt_find_node_by_attr(xt->children, "delay", "xmlns", XMLNS_DELAY);
749                if (!c || !(s = xt_find_attr(c, "stamp"))) {
[7b40f17]750                        return 0;
751                }
[43671b9]752        }
[5ebff60]753
754        memset(&tp, 0, sizeof(tp));
[7b40f17]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
[5ebff60]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) {
[43671b9]761                return 0;
[5ebff60]762        }
763
[43671b9]764        tp.tm_year -= 1900;
[5ebff60]765        tp.tm_mon--;
766
767        return mktime_utc(&tp);
[43671b9]768}
[1baaef8]769
[5ebff60]770struct jabber_error *jabber_error_parse(struct xt_node *node, char *xmlns)
[1baaef8]771{
[5bd21df]772        struct jabber_error *err;
[1baaef8]773        struct xt_node *c;
774        char *s;
[5ebff60]775
776        if (node == NULL) {
[5bd21df]777                return NULL;
[5ebff60]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) {
[1baaef8]786                        continue;
[5ebff60]787                }
788
789                if (strcmp(c->name, "text") != 0) {
[1baaef8]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. */
[5ebff60]794                else if (!(s = xt_find_attr(c, "xml:lang")) ||
795                         !*s || strncmp(s, "en", 2) == 0) {
[1baaef8]796                        err->text = c->text;
797                }
798        }
[5ebff60]799
[1baaef8]800        return err;
801}
802
[5ebff60]803void jabber_error_free(struct jabber_error *err)
[1baaef8]804{
[5ebff60]805        g_free(err);
[1baaef8]806}
[68286eb]807
[5ebff60]808gboolean jabber_set_me(struct im_connection *ic, const char *me)
[68286eb]809{
810        struct jabber_data *jd = ic->proto_data;
[5ebff60]811
812        if (strchr(me, '@') == NULL) {
[68286eb]813                return FALSE;
[5ebff60]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++;
[be1efa3]823
824        /* Set the "internal" account username, for groupchats */
[5ebff60]825        g_free(jd->internal_jid);
826        jd->internal_jid = g_strdup(jd->me);
827
[68286eb]828        return TRUE;
829}
[3d31618]830
831/* Returns new reference! g_free() afterwards. */
832char *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}
Note: See TracBrowser for help on using the repository browser.