source: protocols/jabber/jabber_util.c @ bef322e

Last change on this file since bef322e was 3d31618, checked in by dequis <dx@…>, at 2015-05-28T04:10:55Z

jabber: Refactor conference message handling

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