source: protocols/jabber/jabber.c @ 0c78bb7

Last change on this file since 0c78bb7 was 0c78bb7, checked in by dequis <dx@…>, at 2015-10-21T06:05:17Z

jabber: Fix outgoing google talk typing notifications

Since bare JIDs from typing notifications are now accepted, the other
buddy objects never get the flag JBFLAG_DOES_XEP85 set. This fixes it by
checking both the buddy with resource and the bare one, with the
implication that if the bare JID has that flag, all other resources get
typing notifications.

Follow up to the previous commit, splitting since they are actually
unrelated fixes, although this one is a consequence of the previous one.

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