source: protocols/jabber/jabber_util.c @ bc73e2ba

Last change on this file since bc73e2ba was 5ebff60, checked in by dequis <dx@…>, at 2015-02-20T22:50:54Z

Reindent everything to K&R style with tabs

Used uncrustify, with the configuration file in ./doc/uncrustify.cfg

Commit author set to "Indent <please@…>" so that it's easier to
skip while doing git blame.

  • Property mode set to 100644
File size: 21.3 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) {
[b9f8b87]568                return NULL;
[5ebff60]569        }
570
571        for (bud = jabber_buddy_by_jid(ic, s + 1, GET_BUDDY_FIRST); bud; bud = bud->next) {
[b9f8b87]572                /* Hmmm, could happen if not all people in the chat are anonymized? */
[5ebff60]573                if (bud->ext_jid == NULL) {
[b9f8b87]574                        continue;
[5ebff60]575                }
576
577                if (strcmp(bud->ext_jid, jid) == 0) {
[b9f8b87]578                        break;
[5ebff60]579                }
[b9f8b87]580        }
[5ebff60]581
582        g_free(jid);
583
[b9f8b87]584        return bud;
585}
586
[788a1af]587/* Remove one specific full JID from our list. Use this when a buddy goes
[0d3f30f]588   off-line (because (s)he can still be online from a different location.
589   XXX: See above, we should accept bare JIDs too... */
[5ebff60]590int jabber_buddy_remove(struct im_connection *ic, char *full_jid_)
[6a1128d]591{
[0da65d5]592        struct jabber_data *jd = ic->proto_data;
[842cd8d]593        struct jabber_buddy *bud, *prev = NULL, *bi;
[0d3f30f]594        char *s, *full_jid;
[5ebff60]595
596        full_jid = jabber_normalize(full_jid_);
597
598        if ((s = strchr(full_jid, '/'))) {
[0d3f30f]599                *s = 0;
[5ebff60]600        }
601
602        if ((bud = g_hash_table_lookup(jd->buddies, full_jid))) {
603                if (bud->next) {
604                        bud = (prev = bud)->next;
605                }
606
[6a1128d]607                /* If there's only one item in the list (and if the resource
608                   matches), removing it is simple. (And the hash reference
609                   should be removed too!) */
[5ebff60]610                if (bud->next == NULL &&
611                    ((s == NULL && bud->resource == NULL) ||
612                     (bud->resource && s && strcmp(bud->resource, s + 1) == 0))) {
613                        int st = jabber_buddy_remove_bare(ic, full_jid);
614                        g_free(full_jid);
[03e6c52]615                        return st;
[5ebff60]616                } else if (s == NULL || bud->resource == NULL) {
[0d3f30f]617                        /* Tried to remove a bare JID while this JID does seem
618                           to have resources... (Or the opposite.) *sigh* */
[5ebff60]619                        g_free(full_jid);
[0d3f30f]620                        return 0;
[5ebff60]621                } else {
622                        for (bi = bud; bi; bi = (prev = bi)->next) {
623                                if (strcmp(bi->resource, s + 1) == 0) {
[6a1128d]624                                        break;
[5ebff60]625                                }
626                        }
627
628                        g_free(full_jid);
629
630                        if (bi) {
631                                if (prev) {
[6a1128d]632                                        prev->next = bi->next;
[5ebff60]633                                } else {
[842cd8d]634                                        /* Don't think this should ever happen anymore. */
[5ebff60]635                                        g_hash_table_replace(jd->buddies, bi->bare_jid, bi->next);
636                                }
637
638                                g_free(bi->ext_jid);
639                                g_free(bi->full_jid);
640                                g_free(bi->away_message);
641                                g_free(bi);
642
[0d3f30f]643                                return 1;
[5ebff60]644                        } else {
[6a1128d]645                                return 0;
646                        }
647                }
[5ebff60]648        } else {
649                g_free(full_jid);
[6a1128d]650                return 0;
651        }
652}
[788a1af]653
654/* Remove a buddy completely; removes all resources that belong to the
655   specified bare JID. Use this when removing someone from the contact
656   list, for example. */
[5ebff60]657int jabber_buddy_remove_bare(struct im_connection *ic, char *bare_jid)
[788a1af]658{
[0da65d5]659        struct jabber_data *jd = ic->proto_data;
[788a1af]660        struct jabber_buddy *bud, *next;
[5ebff60]661
662        if (strchr(bare_jid, '/')) {
[788a1af]663                return 0;
[5ebff60]664        }
665
666        if ((bud = jabber_buddy_by_jid(ic, bare_jid, GET_BUDDY_FIRST))) {
[788a1af]667                /* Most important: Remove the hash reference. We don't know
668                   this buddy anymore. */
[5ebff60]669                g_hash_table_remove(jd->buddies, bud->bare_jid);
670                g_free(bud->bare_jid);
671
[788a1af]672                /* Deallocate the linked list of resources. */
[5ebff60]673                while (bud) {
[9da0bbf]674                        /* ext_jid && anonymous means that this buddy is
675                           specific to one groupchat (the one we're
676                           currently cleaning up) so it can be deleted
677                           completely. */
[5ebff60]678                        if (bud->ext_jid && bud->flags & JBFLAG_IS_ANONYMOUS) {
679                                imcb_remove_buddy(ic, bud->ext_jid, NULL);
680                        }
681
[788a1af]682                        next = bud->next;
[5ebff60]683                        g_free(bud->ext_jid);
684                        g_free(bud->full_jid);
685                        g_free(bud->away_message);
686                        g_free(bud);
[788a1af]687                        bud = next;
688                }
[5ebff60]689
[788a1af]690                return 1;
[5ebff60]691        } else {
[788a1af]692                return 0;
693        }
694}
[e35d1a1]695
[5ebff60]696static gboolean jabber_buddy_remove_all_cb(gpointer key, gpointer value, gpointer data)
[04a927c]697{
698        struct jabber_buddy *bud, *next;
[5ebff60]699
[04a927c]700        bud = value;
[5ebff60]701        if (bud->bare_jid != bud->full_jid) {
702                g_free(bud->bare_jid);
703        }
704        while (bud) {
[04a927c]705                next = bud->next;
[5ebff60]706                g_free(bud->ext_jid);
707                g_free(bud->full_jid);
708                g_free(bud->away_message);
709                g_free(bud);
[04a927c]710                bud = next;
711        }
[5ebff60]712
[04a927c]713        return TRUE;
714}
715
[5ebff60]716void jabber_buddy_remove_all(struct im_connection *ic)
[04a927c]717{
718        struct jabber_data *jd = ic->proto_data;
[5ebff60]719
720        g_hash_table_foreach_remove(jd->buddies, jabber_buddy_remove_all_cb, NULL);
721        g_hash_table_destroy(jd->buddies);
[04a927c]722}
723
[5ebff60]724time_t jabber_get_timestamp(struct xt_node *xt)
[43671b9]725{
726        struct xt_node *c;
727        char *s = NULL;
[2e3a857]728        struct tm tp;
[7b40f17]729        gboolean is_old = TRUE;
730        const char *format;
731
732        /* XEP-0091 has <x> */
[5ebff60]733        c = xt_find_node_by_attr(xt->children, "x", "xmlns", XMLNS_DELAY_OLD);
[7b40f17]734
[5ebff60]735        if (!c || !(s = xt_find_attr(c, "stamp"))) {
[7b40f17]736                is_old = FALSE;
737
738                /* XEP-0203 has <delay> */
[5ebff60]739                c = xt_find_node_by_attr(xt->children, "delay", "xmlns", XMLNS_DELAY);
740                if (!c || !(s = xt_find_attr(c, "stamp"))) {
[7b40f17]741                        return 0;
742                }
[43671b9]743        }
[5ebff60]744
745        memset(&tp, 0, sizeof(tp));
[7b40f17]746
747        /* The other main difference between XEPs is the timestamp format */
748        format = (is_old) ? "%4d%2d%2dT%2d:%2d:%2d" : "%4d-%2d-%2dT%2d:%2d:%2dZ";
749
[5ebff60]750        if (sscanf(s, format, &tp.tm_year, &tp.tm_mon, &tp.tm_mday,
751                   &tp.tm_hour, &tp.tm_min, &tp.tm_sec) != 6) {
[43671b9]752                return 0;
[5ebff60]753        }
754
[43671b9]755        tp.tm_year -= 1900;
[5ebff60]756        tp.tm_mon--;
757
758        return mktime_utc(&tp);
[43671b9]759}
[1baaef8]760
[5ebff60]761struct jabber_error *jabber_error_parse(struct xt_node *node, char *xmlns)
[1baaef8]762{
[5bd21df]763        struct jabber_error *err;
[1baaef8]764        struct xt_node *c;
765        char *s;
[5ebff60]766
767        if (node == NULL) {
[5bd21df]768                return NULL;
[5ebff60]769        }
770
771        err = g_new0(struct jabber_error, 1);
772        err->type = xt_find_attr(node, "type");
773
774        for (c = node->children; c; c = c->next) {
775                if (!(s = xt_find_attr(c, "xmlns")) ||
776                    strcmp(s, xmlns) != 0) {
[1baaef8]777                        continue;
[5ebff60]778                }
779
780                if (strcmp(c->name, "text") != 0) {
[1baaef8]781                        err->code = c->name;
782                }
783                /* Only use the text if it doesn't have an xml:lang attribute,
784                   if it's empty or if it's set to something English. */
[5ebff60]785                else if (!(s = xt_find_attr(c, "xml:lang")) ||
786                         !*s || strncmp(s, "en", 2) == 0) {
[1baaef8]787                        err->text = c->text;
788                }
789        }
[5ebff60]790
[1baaef8]791        return err;
792}
793
[5ebff60]794void jabber_error_free(struct jabber_error *err)
[1baaef8]795{
[5ebff60]796        g_free(err);
[1baaef8]797}
[68286eb]798
[5ebff60]799gboolean jabber_set_me(struct im_connection *ic, const char *me)
[68286eb]800{
801        struct jabber_data *jd = ic->proto_data;
[5ebff60]802
803        if (strchr(me, '@') == NULL) {
[68286eb]804                return FALSE;
[5ebff60]805        }
806
807        g_free(jd->username);
808        g_free(jd->me);
809
810        jd->me = jabber_normalize(me);
811        jd->server = strchr(jd->me, '@');
812        jd->username = g_strndup(jd->me, jd->server - jd->me);
813        jd->server++;
[be1efa3]814
815        /* Set the "internal" account username, for groupchats */
[5ebff60]816        g_free(jd->internal_jid);
817        jd->internal_jid = g_strdup(jd->me);
818
[68286eb]819        return TRUE;
820}
Note: See TracBrowser for help on using the repository browser.