source: protocols/jabber/jabber.c @ 8fdeaa5

Last change on this file since 8fdeaa5 was 0db6618, checked in by dequis <dx@…>, at 2015-10-26T08:28:10Z

Use proxy_disconnect() in http, ssl, jabber, oscar

Twitter and MSN are all HTTP/SSL, so they don't need it either.

The out of tree facebook and steam plugins are also covered by the
HTTP/SSL changes.

Yahoo is written in a weird way and doesn't seem to need it (it seems it
doesn't immediately stop connections when you tell it to logout)

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