source: protocols/jabber/jabber.c @ 294b98d

Last change on this file since 294b98d was 246b98b, checked in by dequis <dx@…>, at 2018-03-31T03:05:46Z

jabber: try to join anyway after "Already present in chat"

Most of the time that error meant a confusion on bitlbee's side, often a
server for which we sent a join request and never got a reply for.

  • Property mode set to 100644
File size: 22.8 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        set_add(&acc->set, "oauth", "false", set_eval_oauth, acc);
85
86        if (strcmp(acc->prpl->name, "hipchat") == 0) {
87                set_setstr(&acc->set, "server", "chat.hipchat.com");
88        } else {
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        imcb_chat_list_free(ic);
324
325        while (jd->filetransfers) {
326                imcb_file_canceled(ic, (( struct jabber_transfer *) jd->filetransfers->data)->ft, "Logging out");
327        }
328
329        while (jd->streamhosts) {
330                jabber_streamhost_t *sh = jd->streamhosts->data;
331                jd->streamhosts = g_slist_remove(jd->streamhosts, sh);
332                g_free(sh->jid);
333                g_free(sh->host);
334                g_free(sh);
335        }
336
337        if (jd->fd >= 0) {
338                jabber_end_stream(ic);
339        }
340
341        while (ic->groupchats) {
342                jabber_chat_free(ic->groupchats->data);
343        }
344
345        if (jd->r_inpa >= 0) {
346                b_event_remove(jd->r_inpa);
347        }
348        if (jd->w_inpa >= 0) {
349                b_event_remove(jd->w_inpa);
350        }
351
352        if (jd->ssl) {
353                ssl_disconnect(jd->ssl);
354        }
355        if (jd->fd >= 0) {
356                proxy_disconnect(jd->fd);
357        }
358
359        if (jd->tx_len) {
360                g_free(jd->txq);
361        }
362
363        if (jd->node_cache) {
364                g_hash_table_destroy(jd->node_cache);
365        }
366
367        if (jd->buddies) {
368                jabber_buddy_remove_all(ic);
369        }
370
371        xt_free(jd->xt);
372
373        md5_free(&jd->cached_id_prefix);
374
375        g_free(jd->oauth2_access_token);
376        g_free(jd->away_message);
377        g_free(jd->internal_jid);
378        g_free(jd->gmail_tid);
379        g_free(jd->muc_host);
380        g_free(jd->username);
381        g_free(jd->me);
382        g_free(jd);
383
384        jabber_connections = g_slist_remove(jabber_connections, ic);
385}
386
387static int jabber_buddy_msg(struct im_connection *ic, char *who, char *message, int flags)
388{
389        struct jabber_data *jd = ic->proto_data;
390        struct jabber_buddy *bud;
391        struct xt_node *node;
392        char *s;
393        int st;
394
395        if (g_strcasecmp(who, JABBER_XMLCONSOLE_HANDLE) == 0) {
396                return jabber_write(ic, message, strlen(message));
397        }
398
399        if (g_strcasecmp(who, JABBER_OAUTH_HANDLE) == 0 &&
400            !(jd->flags & OPT_LOGGED_IN) && jd->fd == -1) {
401
402                if (jd->flags & JFLAG_HIPCHAT) {
403                        sasl_oauth2_got_token(ic, message, NULL, NULL);
404                        return 1;
405                } else if (sasl_oauth2_get_refresh_token(ic, message)) {
406                        return 1;
407                } else {
408                        imcb_error(ic, "OAuth failure");
409                        imc_logout(ic, TRUE);
410                        return 0;
411                }
412        }
413
414        if ((s = strchr(who, '=')) && jabber_chat_by_jid(ic, s + 1)) {
415                bud = jabber_buddy_by_ext_jid(ic, who, 0);
416        } else {
417                bud = jabber_buddy_by_jid(ic, who, GET_BUDDY_BARE_OK);
418        }
419
420        node = xt_new_node("body", message, NULL);
421        node = jabber_make_packet("message", "chat", bud ? bud->full_jid : who, node);
422
423        if (bud && (jd->flags & JFLAG_WANT_TYPING) &&
424            ((bud->flags & JBFLAG_DOES_XEP85) ||
425             !(bud->flags & JBFLAG_PROBED_XEP85))) {
426                struct xt_node *act;
427
428                /* If the user likes typing notification and if we don't know
429                   (and didn't probe before) if this resource supports XEP85,
430                   include a probe in this packet now. Also, if we know this
431                   buddy does support XEP85, we have to send this <active/>
432                   tag to tell that the user stopped typing (well, that's what
433                   we guess when s/he pressed Enter...). */
434                act = xt_new_node("active", NULL, NULL);
435                xt_add_attr(act, "xmlns", XMLNS_CHATSTATES);
436                xt_add_child(node, act);
437
438                /* Just make sure we do this only once. */
439                bud->flags |= JBFLAG_PROBED_XEP85;
440        }
441
442        /* XEP-0364 suggests we add message processing hints (XEP-0334) to OTR messages,
443           mostly to avoid carbons (XEP-0280) and server-side message archiving.
444           OTR messages are roughly like this: /^\?OTR(.*\?| Error:|:)/
445           But I'm going to simplify it to messages starting with "?OTR". */
446        if (g_str_has_prefix(message, "?OTR")) {
447                int i;
448                char *hints[] = {
449                        "no-copy", XMLNS_HINTS,
450                        "no-permanent-store", XMLNS_HINTS,
451                        "private", XMLNS_CARBONS,
452                        NULL
453                };
454                       
455                for (i = 0; hints[i]; i += 2) {
456                        struct xt_node *hint;
457                        hint = xt_new_node(hints[i], NULL, NULL);
458                        xt_add_attr(hint, "xmlns", hints[i + 1]);
459                        xt_add_child(node, hint);
460                }
461        }
462
463        st = jabber_write_packet(ic, node);
464        xt_free_node(node);
465
466        return st;
467}
468
469static GList *jabber_away_states(struct im_connection *ic)
470{
471        static GList *l = NULL;
472        int i;
473
474        if (l == NULL) {
475                for (i = 0; jabber_away_state_list[i].full_name; i++) {
476                        l = g_list_append(l, (void *) jabber_away_state_list[i].full_name);
477                }
478        }
479
480        return l;
481}
482
483static void jabber_get_info(struct im_connection *ic, char *who)
484{
485        struct jabber_buddy *bud;
486
487        bud = jabber_buddy_by_jid(ic, who, GET_BUDDY_FIRST);
488
489        while (bud) {
490                imcb_log(ic, "Buddy %s (%d) information:", bud->full_jid, bud->priority);
491                if (bud->away_state) {
492                        imcb_log(ic, "Away state: %s", bud->away_state->full_name);
493                }
494                imcb_log(ic, "Status message: %s", bud->away_message ? bud->away_message : "(none)");
495
496                bud = bud->next;
497        }
498
499        jabber_get_vcard(ic, who);
500}
501
502static void jabber_set_away(struct im_connection *ic, char *state_txt, char *message)
503{
504        struct jabber_data *jd = ic->proto_data;
505
506        /* state_txt == NULL -> Not away.
507           Unknown state -> fall back to the first defined away state. */
508        if (state_txt == NULL) {
509                jd->away_state = NULL;
510        } else if ((jd->away_state = jabber_away_state_by_name(state_txt)) == NULL) {
511                jd->away_state = jabber_away_state_list;
512        }
513
514        g_free(jd->away_message);
515        jd->away_message = (message && *message) ? g_strdup(message) : NULL;
516
517        presence_send_update(ic);
518}
519
520static void jabber_add_buddy(struct im_connection *ic, char *who, char *group)
521{
522        if (g_strcasecmp(who, JABBER_XMLCONSOLE_HANDLE) == 0) {
523                jabber_xmlconsole_enable(ic);
524                return;
525        }
526
527        if (jabber_add_to_roster(ic, who, NULL, group)) {
528                presence_send_request(ic, who, "subscribe");
529        }
530}
531
532static void jabber_remove_buddy(struct im_connection *ic, char *who, char *group)
533{
534        struct jabber_data *jd = ic->proto_data;
535
536        if (g_strcasecmp(who, JABBER_XMLCONSOLE_HANDLE) == 0) {
537                jd->flags &= ~JFLAG_XMLCONSOLE;
538                /* Not necessary for now. And for now the code isn't too
539                   happy if the buddy is completely gone right after calling
540                   this function already.
541                imcb_remove_buddy( ic, JABBER_XMLCONSOLE_HANDLE, NULL );
542                */
543                return;
544        }
545
546        /* We should always do this part. Clean up our administration a little bit. */
547        jabber_buddy_remove_bare(ic, who);
548
549        if (jabber_remove_from_roster(ic, who)) {
550                presence_send_request(ic, who, "unsubscribe");
551        }
552}
553
554static struct groupchat *jabber_chat_join_(struct im_connection *ic, const char *room, const char *nick,
555                                           const char *password, set_t **sets)
556{
557        struct jabber_data *jd = ic->proto_data;
558        char *final_nick;
559
560        /* Ignore the passed nick parameter if we have our own default */
561        if (!(final_nick = set_getstr(sets, "nick")) &&
562            !(final_nick = set_getstr(&ic->acc->set, "display_name"))) {
563                /* Well, whatever, actually use the provided default, then */
564                final_nick = (char *) nick;
565        }
566
567        if (jd->flags & JFLAG_HIPCHAT && jd->muc_host && !g_str_has_suffix(room, jd->muc_host)) {
568                char *guessed_name = hipchat_guess_channel_name(ic, room);
569                if (guessed_name) {
570                        set_setstr(sets, "room", guessed_name);
571                        g_free(guessed_name);
572
573                        /* call this same function again with the fixed name */
574                        return jabber_chat_join_(ic, set_getstr(sets, "room"), nick, password, sets);
575                }
576        }
577
578        if (strchr(room, '@') == NULL) {
579                imcb_error(ic, "%s is not a valid Jabber room name. Maybe you mean %s@conference.%s?",
580                           room, room, jd->server);
581        } else {
582                struct groupchat *old;
583
584                if ((old = jabber_chat_by_jid(ic, room))) {
585                        imcb_log(ic, "Warning: Already present in chat `%s' - trying to join anyway", room);
586                        jabber_chat_free(old);
587                }
588
589                /* jabber_chat_join without the underscore is the conference.c one */
590                return jabber_chat_join(ic, room, final_nick, set_getstr(sets, "password"),
591                                        set_getbool(sets, "always_use_nicks"));
592        }
593
594        return NULL;
595}
596
597static struct groupchat *jabber_chat_with_(struct im_connection *ic, char *who)
598{
599        return jabber_chat_with(ic, who);
600}
601
602static void jabber_chat_list_(struct im_connection *ic, const char *server)
603{
604        struct jabber_data *jd = ic->proto_data;
605
606        if (server && *server) {
607                jabber_iq_disco_muc(ic, server);
608        } else if (jd->muc_host && *jd->muc_host) {
609                jabber_iq_disco_muc(ic, jd->muc_host);
610        } else {
611                /* throw an error here, don't query conference.[server] directly.
612                 * for things like jabber.org it gets you 18000 results of garbage */
613                imcb_error(ic, "Please specify a server name such as `conference.%s'", jd->server);
614        }
615}
616
617static void jabber_chat_msg_(struct groupchat *c, char *message, int flags)
618{
619        if (c && message) {
620                jabber_chat_msg(c, message, flags);
621        }
622}
623
624static void jabber_chat_topic_(struct groupchat *c, char *topic)
625{
626        if (c && topic) {
627                jabber_chat_topic(c, topic);
628        }
629}
630
631static void jabber_chat_leave_(struct groupchat *c)
632{
633        if (c) {
634                jabber_chat_leave(c, NULL);
635        }
636}
637
638static void jabber_chat_invite_(struct groupchat *c, char *who, char *msg)
639{
640        struct jabber_data *jd = c->ic->proto_data;
641        struct jabber_chat *jc = c->data;
642        gchar *msg_alt = NULL;
643
644        if (msg == NULL) {
645                msg_alt = g_strdup_printf("%s invited you to %s", jd->me, jc->name);
646        }
647
648        if (c && who) {
649                jabber_chat_invite(c, who, msg ? msg : msg_alt);
650        }
651
652        g_free(msg_alt);
653}
654
655static void jabber_keepalive(struct im_connection *ic)
656{
657        /* Just any whitespace character is enough as a keepalive for XMPP sessions. */
658        if (!jabber_write(ic, "\n", 1)) {
659                return;
660        }
661
662        /* This runs the garbage collection every minute, which means every packet
663           is in the cache for about a minute (which should be enough AFAIK). */
664        jabber_cache_clean(ic);
665}
666
667static int jabber_send_typing(struct im_connection *ic, char *who, int typing)
668{
669        struct jabber_data *jd = ic->proto_data;
670        struct jabber_buddy *bud, *bare;
671
672        /* Enable typing notification related code from now. */
673        jd->flags |= JFLAG_WANT_TYPING;
674
675        if ((bud = jabber_buddy_by_jid(ic, who, 0)) == NULL ||
676            (bare = jabber_buddy_by_jid(ic, who, GET_BUDDY_BARE)) == NULL) {
677                /* Sending typing notifications to unknown buddies is
678                   unsupported for now. Shouldn't be a problem, I think. */
679                return 0;
680        }
681
682
683        if (bud->flags & JBFLAG_DOES_XEP85 || bare->flags & JBFLAG_DOES_XEP85) {
684                /* We're only allowed to send this stuff if we know the other
685                   side supports it. If the bare JID has the flag, all other
686                   resources get it, too (That is the case in gtalk) */
687
688                struct xt_node *node;
689                char *type;
690                int st;
691
692                if (typing & OPT_TYPING) {
693                        type = "composing";
694                } else if (typing & OPT_THINKING) {
695                        type = "paused";
696                } else {
697                        type = "active";
698                }
699
700                node = xt_new_node(type, NULL, NULL);
701                xt_add_attr(node, "xmlns", XMLNS_CHATSTATES);
702                node = jabber_make_packet("message", "chat", bud->full_jid, node);
703
704                st = jabber_write_packet(ic, node);
705                xt_free_node(node);
706
707                return st;
708        }
709
710        return 1;
711}
712
713void jabber_chat_add_settings(account_t *acc, set_t **head)
714{
715        set_add(head, "always_use_nicks", "false", set_eval_bool, NULL);
716
717        /* Meh. Stupid room passwords. Not trying to obfuscate/hide
718           them from the user for now. */
719        set_add(head, "password", NULL, NULL, NULL);
720}
721
722void jabber_chat_free_settings(account_t *acc, set_t **head)
723{
724        set_del(head, "always_use_nicks");
725
726        set_del(head, "password");
727}
728
729GList *jabber_buddy_action_list(bee_user_t *bu)
730{
731        static GList *ret = NULL;
732
733        if (ret == NULL) {
734                static const struct buddy_action ba[2] = {
735                        { "VERSION", "Get client (version) information" },
736                };
737
738                ret = g_list_prepend(ret, (void *) ba + 0);
739        }
740
741        return ret;
742}
743
744void *jabber_buddy_action(struct bee_user *bu, const char *action, char * const args[], void *data)
745{
746        if (g_strcasecmp(action, "VERSION") == 0) {
747                struct jabber_buddy *bud;
748
749                if ((bud = jabber_buddy_by_ext_jid(bu->ic, bu->handle, 0)) == NULL) {
750                        bud = jabber_buddy_by_jid(bu->ic, bu->handle, GET_BUDDY_FIRST);
751                }
752                for (; bud; bud = bud->next) {
753                        jabber_iq_version_send(bu->ic, bud, data);
754                }
755        }
756
757        return NULL;
758}
759
760gboolean jabber_handle_is_self(struct im_connection *ic, const char *who)
761{
762        struct jabber_data *jd = ic->proto_data;
763
764        return ((g_strcasecmp(who, ic->acc->user) == 0) ||
765                (jd->internal_jid &&
766                 g_strcasecmp(who, jd->internal_jid) == 0));
767}
768
769void jabber_initmodule()
770{
771        struct prpl *ret = g_new0(struct prpl, 1);
772        struct prpl *hipchat = NULL;
773
774        ret->name = "jabber";
775        ret->mms = 0;                        /* no limit */
776        ret->login = jabber_login;
777        ret->init = jabber_init;
778        ret->logout = jabber_logout;
779        ret->buddy_msg = jabber_buddy_msg;
780        ret->away_states = jabber_away_states;
781        ret->set_away = jabber_set_away;
782//      ret->set_info = jabber_set_info;
783        ret->get_info = jabber_get_info;
784        ret->add_buddy = jabber_add_buddy;
785        ret->remove_buddy = jabber_remove_buddy;
786        ret->chat_msg = jabber_chat_msg_;
787        ret->chat_topic = jabber_chat_topic_;
788        ret->chat_invite = jabber_chat_invite_;
789        ret->chat_leave = jabber_chat_leave_;
790        ret->chat_join = jabber_chat_join_;
791        ret->chat_with = jabber_chat_with_;
792        ret->chat_list = jabber_chat_list_;
793        ret->chat_add_settings = jabber_chat_add_settings;
794        ret->chat_free_settings = jabber_chat_free_settings;
795        ret->keepalive = jabber_keepalive;
796        ret->send_typing = jabber_send_typing;
797        ret->handle_cmp = g_strcasecmp;
798        ret->handle_is_self = jabber_handle_is_self;
799        ret->transfer_request = jabber_si_transfer_request;
800        ret->buddy_action_list = jabber_buddy_action_list;
801        ret->buddy_action = jabber_buddy_action;
802
803        register_protocol(ret);
804
805        /* Another one for hipchat, which has completely different logins */
806        hipchat = g_memdup(ret, sizeof(struct prpl));
807        hipchat->name = "hipchat";
808        register_protocol(hipchat);
809}
Note: See TracBrowser for help on using the repository browser.