source: protocols/jabber/jabber.c @ fa8f57b

Last change on this file since fa8f57b was fa8f57b, checked in by dequis <dx@…>, at 2015-11-21T03:01:05Z

jabber: Implement carbons (XEP-0280)

"Message carbons" (XEP-0280) is a server feature to get copies of
outgoing messages sent from other clients connected to the same account.
It's not widely supported by most public XMPP servers (easier if you
host your own), but this will probably change in the next few years.

This is enabled by default if the server supports it. It can also be
disabled with the "carbons" account setting.

Loosely based on a patch by kormat from trac ticket 1021. (Thanks!)
I moved stuff around, simplified things, fixed a few bugs, and used the
new self-messages feature.

  • Property mode set to 100644
File size: 20.5 KB
Line 
1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  Jabber module - Main file                                                *
5*                                                                           *
6*  Copyright 2006-2013 Wilmer van der Gaast <wilmer@gaast.net>              *
7*                                                                           *
8*  This program is free software; you can redistribute it and/or modify     *
9*  it under the terms of the GNU General Public License as published by     *
10*  the Free Software Foundation; either version 2 of the License, or        *
11*  (at your option) any later version.                                      *
12*                                                                           *
13*  This program is distributed in the hope that it will be useful,          *
14*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
15*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
16*  GNU General Public License for more details.                             *
17*                                                                           *
18*  You should have received a copy of the GNU General Public License along  *
19*  with this program; if not, write to the Free Software Foundation, Inc.,  *
20*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
21*                                                                           *
22\***************************************************************************/
23
24#include <glib.h>
25#include <string.h>
26#include <unistd.h>
27#include <ctype.h>
28#include <stdio.h>
29
30#include "ssl_client.h"
31#include "xmltree.h"
32#include "bitlbee.h"
33#include "jabber.h"
34#include "oauth.h"
35#include "md5.h"
36
37GSList *jabber_connections;
38
39/* First enty is the default */
40static const int jabber_port_list[] = {
41        5222,
42        5223,
43        5220,
44        5221,
45        5224,
46        5225,
47        5226,
48        5227,
49        5228,
50        5229,
51        80,
52        443,
53        0
54};
55
56static void jabber_init(account_t *acc)
57{
58        set_t *s;
59        char str[16];
60
61        s = set_add(&acc->set, "activity_timeout", "600", set_eval_int, acc);
62
63        s = set_add(&acc->set, "display_name", NULL, NULL, acc);
64
65        g_snprintf(str, sizeof(str), "%d", jabber_port_list[0]);
66        s = set_add(&acc->set, "port", str, set_eval_int, acc);
67        s->flags |= ACC_SET_OFFLINE_ONLY;
68
69        s = set_add(&acc->set, "priority", "0", set_eval_priority, acc);
70
71        s = set_add(&acc->set, "proxy", "<local>;<auto>", NULL, acc);
72
73        s = set_add(&acc->set, "resource", "BitlBee", NULL, acc);
74        s->flags |= ACC_SET_OFFLINE_ONLY;
75
76        s = set_add(&acc->set, "resource_select", "activity", NULL, acc);
77
78        s = set_add(&acc->set, "sasl", "true", set_eval_bool, acc);
79        s->flags |= ACC_SET_OFFLINE_ONLY | SET_HIDDEN_DEFAULT;
80
81        s = set_add(&acc->set, "server", NULL, set_eval_account, acc);
82        s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY | SET_NULL_OK;
83
84        if (strcmp(acc->prpl->name, "hipchat") == 0) {
85                set_setstr(&acc->set, "server", "chat.hipchat.com");
86        } else {
87                set_add(&acc->set, "oauth", "false", set_eval_oauth, acc);
88
89                /* this reuses set_eval_oauth, which clears the password */
90                set_add(&acc->set, "anonymous", "false", set_eval_oauth, acc);
91        }
92
93        s = set_add(&acc->set, "ssl", "false", set_eval_bool, acc);
94        s->flags |= ACC_SET_OFFLINE_ONLY;
95
96        s = set_add(&acc->set, "tls", "true", set_eval_tls, acc);
97        s->flags |= ACC_SET_OFFLINE_ONLY;
98
99        s = set_add(&acc->set, "tls_verify", "true", set_eval_bool, acc);
100        s->flags |= ACC_SET_OFFLINE_ONLY;
101
102        s = set_add(&acc->set, "user_agent", "BitlBee", NULL, acc);
103
104        s = set_add(&acc->set, "xmlconsole", "false", set_eval_bool, acc);
105
106        s = set_add(&acc->set, "mail_notifications", "false", set_eval_bool, acc);
107        s->flags |= ACC_SET_OFFLINE_ONLY;
108
109        /* changing this is rarely needed so keeping it secret */
110        s = set_add(&acc->set, "mail_notifications_limit", "5", set_eval_int, acc);
111        s->flags |= SET_HIDDEN_DEFAULT;
112
113        s = set_add(&acc->set, "mail_notifications_handle", NULL, NULL, acc);
114        s->flags |= ACC_SET_OFFLINE_ONLY | SET_NULL_OK;
115
116        s = set_add(&acc->set, "carbons", "true", set_eval_bool, acc);
117        s->flags |= ACC_SET_OFFLINE_ONLY;
118
119        acc->flags |= ACC_FLAG_AWAY_MESSAGE | ACC_FLAG_STATUS_MESSAGE |
120                      ACC_FLAG_HANDLE_DOMAINS;
121}
122
123static void jabber_generate_id_hash(struct jabber_data *jd);
124
125static void jabber_login(account_t *acc)
126{
127        struct im_connection *ic = imcb_new(acc);
128        struct jabber_data *jd = g_new0(struct jabber_data, 1);
129        char *s;
130
131        /* For now this is needed in the _connected() handlers if using
132           GLib event handling, to make sure we're not handling events
133           on dead connections. */
134        jabber_connections = g_slist_prepend(jabber_connections, ic);
135
136        jd->ic = ic;
137        ic->proto_data = jd;
138
139        jabber_set_me(ic, acc->user);
140
141        jd->fd = jd->r_inpa = jd->w_inpa = -1;
142
143        if (strcmp(acc->prpl->name, "hipchat") == 0) {
144                jd->flags |= JFLAG_HIPCHAT;
145        }
146
147        if (jd->server == NULL) {
148                imcb_error(ic, "Incomplete account name (format it like <username@jabberserver.name>)");
149                imc_logout(ic, FALSE);
150                return;
151        }
152
153        if ((s = strchr(jd->server, '/'))) {
154                *s = 0;
155                set_setstr(&acc->set, "resource", s + 1);
156
157                /* Also remove the /resource from the original variable so we
158                   won't have to do this again every time. */
159                s = strchr(acc->user, '/');
160                *s = 0;
161        }
162
163        jd->node_cache = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, jabber_cache_entry_free);
164        jd->buddies = g_hash_table_new(g_str_hash, g_str_equal);
165
166        if (set_getbool(&acc->set, "oauth")) {
167                GSList *p_in = NULL;
168                const char *tok;
169
170                jd->fd = jd->r_inpa = jd->w_inpa = -1;
171
172                if (strstr(jd->server, ".facebook.com")) {
173                        jd->oauth2_service = &oauth2_service_facebook;
174                } else {
175                        jd->oauth2_service = &oauth2_service_google;
176                }
177
178                oauth_params_parse(&p_in, ic->acc->pass);
179
180                /* First see if we have a refresh token, in which case any
181                   access token we *might* have has probably expired already
182                   anyway. */
183                if ((tok = oauth_params_get(&p_in, "refresh_token"))) {
184                        sasl_oauth2_refresh(ic, tok);
185                }
186                /* If we don't have a refresh token, let's hope the access
187                   token is still usable. */
188                else if ((tok = oauth_params_get(&p_in, "access_token"))) {
189                        jd->oauth2_access_token = g_strdup(tok);
190                        jabber_connect(ic);
191                }
192                /* If we don't have any, start the OAuth process now. Don't
193                   even open an XMPP connection yet. */
194                else {
195                        sasl_oauth2_init(ic);
196                        ic->flags |= OPT_SLOW_LOGIN;
197                }
198
199                oauth_params_free(&p_in);
200        } else {
201                jabber_connect(ic);
202        }
203}
204
205static void jabber_xmlconsole_enable(struct im_connection *ic)
206{
207        struct jabber_data *jd = ic->proto_data;
208        const char *handle = JABBER_XMLCONSOLE_HANDLE;
209        bee_user_t *bu;
210       
211        jd->flags |= JFLAG_XMLCONSOLE;
212
213        if (!(bu = bee_user_by_handle(ic->bee, ic, handle))) {
214                bu = bee_user_new(ic->bee, ic, handle, 0);
215                bu->flags |= BEE_USER_NOOTR;
216        }
217}
218
219/* Separate this from jabber_login() so we can do OAuth first if necessary.
220   Putting this in io.c would probably be more correct. */
221void jabber_connect(struct im_connection *ic)
222{
223        account_t *acc = ic->acc;
224        struct jabber_data *jd = ic->proto_data;
225        int i;
226        char *connect_to;
227        struct ns_srv_reply **srvl = NULL, *srv = NULL;
228
229        /* Figure out the hostname to connect to. */
230        if (acc->server && *acc->server) {
231                connect_to = acc->server;
232        } else if ((srvl = srv_lookup("xmpp-client", "tcp", jd->server)) ||
233                   (srvl = srv_lookup("jabber-client", "tcp", jd->server))) {
234                /* Find the lowest-priority one. These usually come
235                   back in random/shuffled order. Not looking at
236                   weights etc for now. */
237                srv = *srvl;
238                for (i = 1; srvl[i]; i++) {
239                        if (srvl[i]->prio < srv->prio) {
240                                srv = srvl[i];
241                        }
242                }
243
244                connect_to = srv->name;
245        } else {
246                connect_to = jd->server;
247        }
248
249        imcb_log(ic, "Connecting");
250
251        for (i = 0; jabber_port_list[i] > 0; i++) {
252                if (set_getint(&acc->set, "port") == jabber_port_list[i]) {
253                        break;
254                }
255        }
256
257        if (jabber_port_list[i] == 0) {
258                imcb_log(ic, "Illegal port number");
259                imc_logout(ic, FALSE);
260                return;
261        }
262
263        /* For non-SSL connections we can try to use the port # from the SRV
264           reply, but let's not do that when using SSL, SSL usually runs on
265           non-standard ports... */
266        if (set_getbool(&acc->set, "ssl")) {
267                jd->ssl = ssl_connect(connect_to, set_getint(&acc->set, "port"), set_getbool(&acc->set,
268                                                                                             "tls_verify"), jabber_connected_ssl,
269                                      ic);
270                jd->fd = jd->ssl ? ssl_getfd(jd->ssl) : -1;
271        } else {
272                jd->fd = proxy_connect(connect_to, srv ? srv->port : set_getint(&acc->set,
273                                                                                "port"), jabber_connected_plain, ic);
274        }
275        srv_free(srvl);
276
277        if (jd->fd == -1) {
278                imcb_error(ic, "Could not connect to server");
279                imc_logout(ic, TRUE);
280
281                return;
282        }
283
284        if (set_getbool(&acc->set, "xmlconsole")) {
285                jabber_xmlconsole_enable(ic);
286        }
287
288        if (set_getbool(&acc->set, "mail_notifications")) {
289                /* It's gmail specific, but it checks for server support before enabling it */
290                jd->flags |= JFLAG_GMAILNOTIFY;
291                if (set_getstr(&acc->set, "mail_notifications_handle")) {
292                        imcb_add_buddy(ic, set_getstr(&acc->set, "mail_notifications_handle"), NULL);
293                }
294        }
295
296        jabber_generate_id_hash(jd);
297}
298
299/* This generates an unfinished md5_state_t variable. Every time we generate
300   an ID, we finish the state by adding a sequence number and take the hash. */
301static void jabber_generate_id_hash(struct jabber_data *jd)
302{
303        md5_byte_t binbuf[4];
304        char *s;
305
306        md5_init(&jd->cached_id_prefix);
307        md5_append(&jd->cached_id_prefix, (unsigned char *) jd->username, strlen(jd->username));
308        md5_append(&jd->cached_id_prefix, (unsigned char *) jd->server, strlen(jd->server));
309        s = set_getstr(&jd->ic->acc->set, "resource");
310        md5_append(&jd->cached_id_prefix, (unsigned char *) s, strlen(s));
311        random_bytes(binbuf, 4);
312        md5_append(&jd->cached_id_prefix, binbuf, 4);
313}
314
315static void jabber_logout(struct im_connection *ic)
316{
317        struct jabber_data *jd = ic->proto_data;
318
319        while (jd->filetransfers) {
320                imcb_file_canceled(ic, (( struct jabber_transfer *) jd->filetransfers->data)->ft, "Logging out");
321        }
322
323        while (jd->streamhosts) {
324                jabber_streamhost_t *sh = jd->streamhosts->data;
325                jd->streamhosts = g_slist_remove(jd->streamhosts, sh);
326                g_free(sh->jid);
327                g_free(sh->host);
328                g_free(sh);
329        }
330
331        if (jd->fd >= 0) {
332                jabber_end_stream(ic);
333        }
334
335        while (ic->groupchats) {
336                jabber_chat_free(ic->groupchats->data);
337        }
338
339        if (jd->r_inpa >= 0) {
340                b_event_remove(jd->r_inpa);
341        }
342        if (jd->w_inpa >= 0) {
343                b_event_remove(jd->w_inpa);
344        }
345
346        if (jd->ssl) {
347                ssl_disconnect(jd->ssl);
348        }
349        if (jd->fd >= 0) {
350                proxy_disconnect(jd->fd);
351        }
352
353        if (jd->tx_len) {
354                g_free(jd->txq);
355        }
356
357        if (jd->node_cache) {
358                g_hash_table_destroy(jd->node_cache);
359        }
360
361        if (jd->buddies) {
362                jabber_buddy_remove_all(ic);
363        }
364
365        xt_free(jd->xt);
366
367        md5_free(&jd->cached_id_prefix);
368
369        g_free(jd->oauth2_access_token);
370        g_free(jd->away_message);
371        g_free(jd->internal_jid);
372        g_free(jd->gmail_tid);
373        g_free(jd->username);
374        g_free(jd->me);
375        g_free(jd);
376
377        jabber_connections = g_slist_remove(jabber_connections, ic);
378}
379
380static int jabber_buddy_msg(struct im_connection *ic, char *who, char *message, int flags)
381{
382        struct jabber_data *jd = ic->proto_data;
383        struct jabber_buddy *bud;
384        struct xt_node *node;
385        char *s;
386        int st;
387
388        if (g_strcasecmp(who, JABBER_XMLCONSOLE_HANDLE) == 0) {
389                return jabber_write(ic, message, strlen(message));
390        }
391
392        if (g_strcasecmp(who, JABBER_OAUTH_HANDLE) == 0 &&
393            !(jd->flags & OPT_LOGGED_IN) && jd->fd == -1) {
394                if (sasl_oauth2_get_refresh_token(ic, message)) {
395                        return 1;
396                } else {
397                        imcb_error(ic, "OAuth failure");
398                        imc_logout(ic, TRUE);
399                        return 0;
400                }
401        }
402
403        if ((s = strchr(who, '=')) && jabber_chat_by_jid(ic, s + 1)) {
404                bud = jabber_buddy_by_ext_jid(ic, who, 0);
405        } else {
406                bud = jabber_buddy_by_jid(ic, who, GET_BUDDY_BARE_OK);
407        }
408
409        node = xt_new_node("body", message, NULL);
410        node = jabber_make_packet("message", "chat", bud ? bud->full_jid : who, node);
411
412        if (bud && (jd->flags & JFLAG_WANT_TYPING) &&
413            ((bud->flags & JBFLAG_DOES_XEP85) ||
414             !(bud->flags & JBFLAG_PROBED_XEP85))) {
415                struct xt_node *act;
416
417                /* If the user likes typing notification and if we don't know
418                   (and didn't probe before) if this resource supports XEP85,
419                   include a probe in this packet now. Also, if we know this
420                   buddy does support XEP85, we have to send this <active/>
421                   tag to tell that the user stopped typing (well, that's what
422                   we guess when s/he pressed Enter...). */
423                act = xt_new_node("active", NULL, NULL);
424                xt_add_attr(act, "xmlns", XMLNS_CHATSTATES);
425                xt_add_child(node, act);
426
427                /* Just make sure we do this only once. */
428                bud->flags |= JBFLAG_PROBED_XEP85;
429        }
430
431        st = jabber_write_packet(ic, node);
432        xt_free_node(node);
433
434        return st;
435}
436
437static GList *jabber_away_states(struct im_connection *ic)
438{
439        static GList *l = NULL;
440        int i;
441
442        if (l == NULL) {
443                for (i = 0; jabber_away_state_list[i].full_name; i++) {
444                        l = g_list_append(l, (void *) jabber_away_state_list[i].full_name);
445                }
446        }
447
448        return l;
449}
450
451static void jabber_get_info(struct im_connection *ic, char *who)
452{
453        struct jabber_buddy *bud;
454
455        bud = jabber_buddy_by_jid(ic, who, GET_BUDDY_FIRST);
456
457        while (bud) {
458                imcb_log(ic, "Buddy %s (%d) information:", bud->full_jid, bud->priority);
459                if (bud->away_state) {
460                        imcb_log(ic, "Away state: %s", bud->away_state->full_name);
461                }
462                imcb_log(ic, "Status message: %s", bud->away_message ? bud->away_message : "(none)");
463
464                bud = bud->next;
465        }
466
467        jabber_get_vcard(ic, who);
468}
469
470static void jabber_set_away(struct im_connection *ic, char *state_txt, char *message)
471{
472        struct jabber_data *jd = ic->proto_data;
473
474        /* state_txt == NULL -> Not away.
475           Unknown state -> fall back to the first defined away state. */
476        if (state_txt == NULL) {
477                jd->away_state = NULL;
478        } else if ((jd->away_state = jabber_away_state_by_name(state_txt)) == NULL) {
479                jd->away_state = jabber_away_state_list;
480        }
481
482        g_free(jd->away_message);
483        jd->away_message = (message && *message) ? g_strdup(message) : NULL;
484
485        presence_send_update(ic);
486}
487
488static void jabber_add_buddy(struct im_connection *ic, char *who, char *group)
489{
490        if (g_strcasecmp(who, JABBER_XMLCONSOLE_HANDLE) == 0) {
491                jabber_xmlconsole_enable(ic);
492                return;
493        }
494
495        if (jabber_add_to_roster(ic, who, NULL, group)) {
496                presence_send_request(ic, who, "subscribe");
497        }
498}
499
500static void jabber_remove_buddy(struct im_connection *ic, char *who, char *group)
501{
502        struct jabber_data *jd = ic->proto_data;
503
504        if (g_strcasecmp(who, JABBER_XMLCONSOLE_HANDLE) == 0) {
505                jd->flags &= ~JFLAG_XMLCONSOLE;
506                /* Not necessary for now. And for now the code isn't too
507                   happy if the buddy is completely gone right after calling
508                   this function already.
509                imcb_remove_buddy( ic, JABBER_XMLCONSOLE_HANDLE, NULL );
510                */
511                return;
512        }
513
514        /* We should always do this part. Clean up our administration a little bit. */
515        jabber_buddy_remove_bare(ic, who);
516
517        if (jabber_remove_from_roster(ic, who)) {
518                presence_send_request(ic, who, "unsubscribe");
519        }
520}
521
522static struct groupchat *jabber_chat_join_(struct im_connection *ic, const char *room, const char *nick,
523                                           const char *password, set_t **sets)
524{
525        struct jabber_data *jd = ic->proto_data;
526        char *final_nick;
527
528        /* Ignore the passed nick parameter if we have our own default */
529        if (!(final_nick = set_getstr(sets, "nick")) &&
530            !(final_nick = set_getstr(&ic->acc->set, "display_name"))) {
531                /* Well, whatever, actually use the provided default, then */
532                final_nick = (char *) nick;
533        }
534
535        if (strchr(room, '@') == NULL) {
536                imcb_error(ic, "%s is not a valid Jabber room name. Maybe you mean %s@conference.%s?",
537                           room, room, jd->server);
538        } else if (jabber_chat_by_jid(ic, room)) {
539                imcb_error(ic, "Already present in chat `%s'", room);
540        } else {
541                return jabber_chat_join(ic, room, final_nick, set_getstr(sets, "password"));
542        }
543
544        return NULL;
545}
546
547static struct groupchat *jabber_chat_with_(struct im_connection *ic, char *who)
548{
549        return jabber_chat_with(ic, who);
550}
551
552static void jabber_chat_msg_(struct groupchat *c, char *message, int flags)
553{
554        if (c && message) {
555                jabber_chat_msg(c, message, flags);
556        }
557}
558
559static void jabber_chat_topic_(struct groupchat *c, char *topic)
560{
561        if (c && topic) {
562                jabber_chat_topic(c, topic);
563        }
564}
565
566static void jabber_chat_leave_(struct groupchat *c)
567{
568        if (c) {
569                jabber_chat_leave(c, NULL);
570        }
571}
572
573static void jabber_chat_invite_(struct groupchat *c, char *who, char *msg)
574{
575        struct jabber_data *jd = c->ic->proto_data;
576        struct jabber_chat *jc = c->data;
577        gchar *msg_alt = NULL;
578
579        if (msg == NULL) {
580                msg_alt = g_strdup_printf("%s invited you to %s", jd->me, jc->name);
581        }
582
583        if (c && who) {
584                jabber_chat_invite(c, who, msg ? msg : msg_alt);
585        }
586
587        g_free(msg_alt);
588}
589
590static void jabber_keepalive(struct im_connection *ic)
591{
592        /* Just any whitespace character is enough as a keepalive for XMPP sessions. */
593        if (!jabber_write(ic, "\n", 1)) {
594                return;
595        }
596
597        /* This runs the garbage collection every minute, which means every packet
598           is in the cache for about a minute (which should be enough AFAIK). */
599        jabber_cache_clean(ic);
600}
601
602static int jabber_send_typing(struct im_connection *ic, char *who, int typing)
603{
604        struct jabber_data *jd = ic->proto_data;
605        struct jabber_buddy *bud, *bare;
606
607        /* Enable typing notification related code from now. */
608        jd->flags |= JFLAG_WANT_TYPING;
609
610        if ((bud = jabber_buddy_by_jid(ic, who, 0)) == NULL ||
611            (bare = jabber_buddy_by_jid(ic, who, GET_BUDDY_BARE)) == NULL) {
612                /* Sending typing notifications to unknown buddies is
613                   unsupported for now. Shouldn't be a problem, I think. */
614                return 0;
615        }
616
617
618        if (bud->flags & JBFLAG_DOES_XEP85 || bare->flags & JBFLAG_DOES_XEP85) {
619                /* We're only allowed to send this stuff if we know the other
620                   side supports it. If the bare JID has the flag, all other
621                   resources get it, too (That is the case in gtalk) */
622
623                struct xt_node *node;
624                char *type;
625                int st;
626
627                if (typing & OPT_TYPING) {
628                        type = "composing";
629                } else if (typing & OPT_THINKING) {
630                        type = "paused";
631                } else {
632                        type = "active";
633                }
634
635                node = xt_new_node(type, NULL, NULL);
636                xt_add_attr(node, "xmlns", XMLNS_CHATSTATES);
637                node = jabber_make_packet("message", "chat", bud->full_jid, node);
638
639                st = jabber_write_packet(ic, node);
640                xt_free_node(node);
641
642                return st;
643        }
644
645        return 1;
646}
647
648void jabber_chat_add_settings(account_t *acc, set_t **head)
649{
650        /* Meh. Stupid room passwords. Not trying to obfuscate/hide
651           them from the user for now. */
652        set_add(head, "password", NULL, NULL, NULL);
653}
654
655void jabber_chat_free_settings(account_t *acc, set_t **head)
656{
657        set_del(head, "password");
658}
659
660GList *jabber_buddy_action_list(bee_user_t *bu)
661{
662        static GList *ret = NULL;
663
664        if (ret == NULL) {
665                static const struct buddy_action ba[2] = {
666                        { "VERSION", "Get client (version) information" },
667                };
668
669                ret = g_list_prepend(ret, (void *) ba + 0);
670        }
671
672        return ret;
673}
674
675void *jabber_buddy_action(struct bee_user *bu, const char *action, char * const args[], void *data)
676{
677        if (g_strcasecmp(action, "VERSION") == 0) {
678                struct jabber_buddy *bud;
679
680                if ((bud = jabber_buddy_by_ext_jid(bu->ic, bu->handle, 0)) == NULL) {
681                        bud = jabber_buddy_by_jid(bu->ic, bu->handle, GET_BUDDY_FIRST);
682                }
683                for (; bud; bud = bud->next) {
684                        jabber_iq_version_send(bu->ic, bud, data);
685                }
686        }
687
688        return NULL;
689}
690
691gboolean jabber_handle_is_self(struct im_connection *ic, const char *who)
692{
693        struct jabber_data *jd = ic->proto_data;
694
695        return ((g_strcasecmp(who, ic->acc->user) == 0) ||
696                (jd->internal_jid &&
697                 g_strcasecmp(who, jd->internal_jid) == 0));
698}
699
700void jabber_initmodule()
701{
702        struct prpl *ret = g_new0(struct prpl, 1);
703        struct prpl *hipchat = NULL;
704
705        ret->name = "jabber";
706        ret->mms = 0;                        /* no limit */
707        ret->login = jabber_login;
708        ret->init = jabber_init;
709        ret->logout = jabber_logout;
710        ret->buddy_msg = jabber_buddy_msg;
711        ret->away_states = jabber_away_states;
712        ret->set_away = jabber_set_away;
713//      ret->set_info = jabber_set_info;
714        ret->get_info = jabber_get_info;
715        ret->add_buddy = jabber_add_buddy;
716        ret->remove_buddy = jabber_remove_buddy;
717        ret->chat_msg = jabber_chat_msg_;
718        ret->chat_topic = jabber_chat_topic_;
719        ret->chat_invite = jabber_chat_invite_;
720        ret->chat_leave = jabber_chat_leave_;
721        ret->chat_join = jabber_chat_join_;
722        ret->chat_with = jabber_chat_with_;
723        ret->chat_add_settings = jabber_chat_add_settings;
724        ret->chat_free_settings = jabber_chat_free_settings;
725        ret->keepalive = jabber_keepalive;
726        ret->send_typing = jabber_send_typing;
727        ret->handle_cmp = g_strcasecmp;
728        ret->handle_is_self = jabber_handle_is_self;
729        ret->transfer_request = jabber_si_transfer_request;
730        ret->buddy_action_list = jabber_buddy_action_list;
731        ret->buddy_action = jabber_buddy_action;
732
733        register_protocol(ret);
734
735        /* Another one for hipchat, which has completely different logins */
736        hipchat = g_memdup(ret, sizeof(struct prpl));
737        hipchat->name = "hipchat";
738        register_protocol(hipchat);
739}
Note: See TracBrowser for help on using the repository browser.