source: protocols/jabber/jabber.c @ 9b02bab

Last change on this file since 9b02bab was 9b02bab, checked in by dequis <dx@…>, at 2015-12-05T01:52:48Z

Remove facebook XMPP code, show error pointing at the new plugin

Facebook's oauth has been broken for months, and in the last few days
they broke plain logins too, so I just added an error message that says
this when you do "account on":

Facebook's XMPP service is gone. Try this instead:
https://wiki.bitlbee.org/HowtoFacebookMQTT

Also nuked all the oauth related code, except some parts of lib/oauth2.c
which seemed generic enough to maybe help in the future with other
not-really-compliant not-really-implementations of the not-really-oauth2
not-really-spec

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