source: protocols/jabber/jabber.c @ 42a418e

Last change on this file since 42a418e was 9c8dbc7, checked in by dequis <dx@…>, at 2015-11-23T17:49:09Z

hipchat: 'chat add hipchat "channel name"' now tries to guess the JID

It's basically prepending the organization id, appending the default MUC
host from the success packet, and generating a slug based on the name
for the middle part, which is replacing a few characters with
underscores and doing a unicode aware lowercasing.

Includes tests, which are useless other than validating the initial
implementation with the test vectors that i already tested manually.
Guaranteed to detect zero breakages in the future. Good test code.

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