source: protocols/jabber/jabber.c @ 709f41f

Last change on this file since 709f41f was 1bdc669, checked in by GitHub <noreply@…>, at 2023-02-23T23:48:10Z

Migrate internal users of md5.h to using GChecksum directly (#169)

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