source: protocols/purple/purple.c @ 2c5ab49

Last change on this file since 2c5ab49 was 2c5ab49, checked in by dequis <dx@…>, at 2015-03-15T04:34:43Z

purple: prplcb_close_request() improvements

  • Added support for PURPLE_REQUEST_INPUT
  • Changed memory management to do the free() of data through purple_request_close(), letting purple know that the request was answered, and fixing use-after-free issues with it
  • Property mode set to 100644
File size: 41.4 KB
Line 
1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  libpurple module - Main file                                             *
5*                                                                           *
6*  Copyright 2009-2012 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 "bitlbee.h"
25#include "bpurple.h"
26#include "help.h"
27
28#include <stdarg.h>
29
30#include <glib.h>
31#include <purple.h>
32
33GSList *purple_connections;
34
35/* This makes me VERY sad... :-( But some libpurple callbacks come in without
36   any context so this is the only way to get that. Don't want to support
37   libpurple in daemon mode anyway. */
38static bee_t *local_bee;
39
40static char *set_eval_display_name(set_t *set, char *value);
41
42void purple_request_input_callback(guint id, struct im_connection *ic,
43                                   const char *message, const char *who);
44
45/* purple_request_input specific stuff */
46typedef void (*ri_callback_t)(gpointer, const gchar *);
47
48struct request_input_data {
49        ri_callback_t data_callback;
50        void *user_data;
51        struct im_connection *ic;
52        char *buddy;
53        guint id;
54};
55
56struct im_connection *purple_ic_by_pa(PurpleAccount *pa)
57{
58        GSList *i;
59        struct purple_data *pd;
60
61        for (i = purple_connections; i; i = i->next) {
62                pd = ((struct im_connection *) i->data)->proto_data;
63                if (pd->account == pa) {
64                        return i->data;
65                }
66        }
67
68        return NULL;
69}
70
71static struct im_connection *purple_ic_by_gc(PurpleConnection *gc)
72{
73        return purple_ic_by_pa(purple_connection_get_account(gc));
74}
75
76static gboolean purple_menu_cmp(const char *a, const char *b)
77{
78        while (*a && *b) {
79                while (*a == '_') {
80                        a++;
81                }
82                while (*b == '_') {
83                        b++;
84                }
85                if (g_ascii_tolower(*a) != g_ascii_tolower(*b)) {
86                        return FALSE;
87                }
88
89                a++;
90                b++;
91        }
92
93        return (*a == '\0' && *b == '\0');
94}
95
96static void purple_init(account_t *acc)
97{
98        PurplePlugin *prpl = purple_plugins_find_with_id((char *) acc->prpl->data);
99        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
100        PurpleAccount *pa;
101        GList *i, *st;
102        set_t *s;
103        char help_title[64];
104        GString *help;
105        static gboolean dir_fixed = FALSE;
106
107        /* Layer violation coming up: Making an exception for libpurple here.
108           Dig in the IRC state a bit to get a username. Ideally we should
109           check if s/he identified but this info doesn't seem *that* important.
110           It's just that fecking libpurple can't *not* store this shit.
111
112           Remember that libpurple is not really meant to be used on public
113           servers anyway! */
114        if (!dir_fixed) {
115                irc_t *irc = acc->bee->ui_data;
116                char *dir;
117
118                dir = g_strdup_printf("%s/purple/%s", global.conf->configdir, irc->user->nick);
119                purple_util_set_user_dir(dir);
120                g_free(dir);
121
122                purple_blist_load();
123                purple_prefs_load();
124                dir_fixed = TRUE;
125        }
126
127        help = g_string_new("");
128        g_string_printf(help, "BitlBee libpurple module %s (%s).\n\nSupported settings:",
129                        (char *) acc->prpl->name, prpl->info->name);
130
131        if (pi->user_splits) {
132                GList *l;
133                g_string_append_printf(help, "\n* username: Username");
134                for (l = pi->user_splits; l; l = l->next) {
135                        g_string_append_printf(help, "%c%s",
136                                               purple_account_user_split_get_separator(l->data),
137                                               purple_account_user_split_get_text(l->data));
138                }
139        }
140
141        /* Convert all protocol_options into per-account setting variables. */
142        for (i = pi->protocol_options; i; i = i->next) {
143                PurpleAccountOption *o = i->data;
144                const char *name;
145                char *def = NULL;
146                set_eval eval = NULL;
147                void *eval_data = NULL;
148                GList *io = NULL;
149                GSList *opts = NULL;
150
151                name = purple_account_option_get_setting(o);
152
153                switch (purple_account_option_get_type(o)) {
154                case PURPLE_PREF_STRING:
155                        def = g_strdup(purple_account_option_get_default_string(o));
156
157                        g_string_append_printf(help, "\n* %s (%s), %s, default: %s",
158                                               name, purple_account_option_get_text(o),
159                                               "string", def);
160
161                        break;
162
163                case PURPLE_PREF_INT:
164                        def = g_strdup_printf("%d", purple_account_option_get_default_int(o));
165                        eval = set_eval_int;
166
167                        g_string_append_printf(help, "\n* %s (%s), %s, default: %s",
168                                               name, purple_account_option_get_text(o),
169                                               "integer", def);
170
171                        break;
172
173                case PURPLE_PREF_BOOLEAN:
174                        if (purple_account_option_get_default_bool(o)) {
175                                def = g_strdup("true");
176                        } else {
177                                def = g_strdup("false");
178                        }
179                        eval = set_eval_bool;
180
181                        g_string_append_printf(help, "\n* %s (%s), %s, default: %s",
182                                               name, purple_account_option_get_text(o),
183                                               "boolean", def);
184
185                        break;
186
187                case PURPLE_PREF_STRING_LIST:
188                        def = g_strdup(purple_account_option_get_default_list_value(o));
189
190                        g_string_append_printf(help, "\n* %s (%s), %s, default: %s",
191                                               name, purple_account_option_get_text(o),
192                                               "list", def);
193                        g_string_append(help, "\n  Possible values: ");
194
195                        for (io = purple_account_option_get_list(o); io; io = io->next) {
196                                PurpleKeyValuePair *kv = io->data;
197                                opts = g_slist_append(opts, kv->value);
198                                /* TODO: kv->value is not a char*, WTF? */
199                                if (strcmp(kv->value, kv->key) != 0) {
200                                        g_string_append_printf(help, "%s (%s), ", (char *) kv->value, kv->key);
201                                } else {
202                                        g_string_append_printf(help, "%s, ", (char *) kv->value);
203                                }
204                        }
205                        g_string_truncate(help, help->len - 2);
206                        eval = set_eval_list;
207                        eval_data = opts;
208
209                        break;
210
211                default:
212                        /** No way to talk to the user right now, invent one when
213                        this becomes important.
214                        irc_rootmsg( acc->irc, "Setting with unknown type: %s (%d) Expect stuff to break..\n",
215                                     name, purple_account_option_get_type( o ) );
216                        */
217                        g_string_append_printf(help, "\n* [%s] UNSUPPORTED (type %d)",
218                                               name, purple_account_option_get_type(o));
219                        name = NULL;
220                }
221
222                if (name != NULL) {
223                        s = set_add(&acc->set, name, def, eval, acc);
224                        s->flags |= ACC_SET_OFFLINE_ONLY;
225                        s->eval_data = eval_data;
226                        g_free(def);
227                }
228        }
229
230        g_snprintf(help_title, sizeof(help_title), "purple %s", (char *) acc->prpl->name);
231        help_add_mem(&global.help, help_title, help->str);
232        g_string_free(help, TRUE);
233
234        s = set_add(&acc->set, "display_name", NULL, set_eval_display_name, acc);
235        s->flags |= ACC_SET_ONLINE_ONLY;
236
237        if (pi->options & OPT_PROTO_MAIL_CHECK) {
238                s = set_add(&acc->set, "mail_notifications", "false", set_eval_bool, acc);
239                s->flags |= ACC_SET_OFFLINE_ONLY;
240        }
241
242        if (strcmp(prpl->info->name, "Gadu-Gadu") == 0) {
243                s = set_add(&acc->set, "gg_sync_contacts", "true", set_eval_bool, acc);
244        }
245
246        /* Go through all away states to figure out if away/status messages
247           are possible. */
248        pa = purple_account_new(acc->user, (char *) acc->prpl->data);
249        for (st = purple_account_get_status_types(pa); st; st = st->next) {
250                PurpleStatusPrimitive prim = purple_status_type_get_primitive(st->data);
251
252                if (prim == PURPLE_STATUS_AVAILABLE) {
253                        if (purple_status_type_get_attr(st->data, "message")) {
254                                acc->flags |= ACC_FLAG_STATUS_MESSAGE;
255                        }
256                } else if (prim != PURPLE_STATUS_OFFLINE) {
257                        if (purple_status_type_get_attr(st->data, "message")) {
258                                acc->flags |= ACC_FLAG_AWAY_MESSAGE;
259                        }
260                }
261        }
262        purple_accounts_remove(pa);
263}
264
265static void purple_sync_settings(account_t *acc, PurpleAccount *pa)
266{
267        PurplePlugin *prpl = purple_plugins_find_with_id(pa->protocol_id);
268        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
269        GList *i;
270
271        for (i = pi->protocol_options; i; i = i->next) {
272                PurpleAccountOption *o = i->data;
273                const char *name;
274                set_t *s;
275
276                name = purple_account_option_get_setting(o);
277                s = set_find(&acc->set, name);
278                if (s->value == NULL) {
279                        continue;
280                }
281
282                switch (purple_account_option_get_type(o)) {
283                case PURPLE_PREF_STRING:
284                case PURPLE_PREF_STRING_LIST:
285                        purple_account_set_string(pa, name, set_getstr(&acc->set, name));
286                        break;
287
288                case PURPLE_PREF_INT:
289                        purple_account_set_int(pa, name, set_getint(&acc->set, name));
290                        break;
291
292                case PURPLE_PREF_BOOLEAN:
293                        purple_account_set_bool(pa, name, set_getbool(&acc->set, name));
294                        break;
295
296                default:
297                        break;
298                }
299        }
300
301        if (pi->options & OPT_PROTO_MAIL_CHECK) {
302                purple_account_set_check_mail(pa, set_getbool(&acc->set, "mail_notifications"));
303        }
304}
305
306static void purple_login(account_t *acc)
307{
308        struct im_connection *ic = imcb_new(acc);
309        struct purple_data *pd;
310
311        if ((local_bee != NULL && local_bee != acc->bee) ||
312            (global.conf->runmode == RUNMODE_DAEMON && !getenv("BITLBEE_DEBUG"))) {
313                imcb_error(ic,  "Daemon mode detected. Do *not* try to use libpurple in daemon mode! "
314                           "Please use inetd or ForkDaemon mode instead.");
315                imc_logout(ic, FALSE);
316                return;
317        }
318        local_bee = acc->bee;
319
320        /* For now this is needed in the _connected() handlers if using
321           GLib event handling, to make sure we're not handling events
322           on dead connections. */
323        purple_connections = g_slist_prepend(purple_connections, ic);
324
325        ic->proto_data = pd = g_new0(struct purple_data, 1);
326        pd->account = purple_account_new(acc->user, (char *) acc->prpl->data);
327        pd->input_requests = g_hash_table_new_full(g_direct_hash, g_direct_equal,
328                                                   NULL, g_free);
329        pd->next_request_id = 0;
330        purple_account_set_password(pd->account, acc->pass);
331        purple_sync_settings(acc, pd->account);
332
333        purple_account_set_enabled(pd->account, "BitlBee", TRUE);
334}
335
336static void purple_logout(struct im_connection *ic)
337{
338        struct purple_data *pd = ic->proto_data;
339
340        purple_account_set_enabled(pd->account, "BitlBee", FALSE);
341        purple_connections = g_slist_remove(purple_connections, ic);
342        purple_accounts_remove(pd->account);
343        g_hash_table_destroy(pd->input_requests);
344        g_free(pd);
345}
346
347static int purple_buddy_msg(struct im_connection *ic, char *who, char *message, int flags)
348{
349        PurpleConversation *conv;
350        struct purple_data *pd = ic->proto_data;
351
352        if (!strncmp(who, PURPLE_REQUEST_HANDLE, sizeof(PURPLE_REQUEST_HANDLE) - 1)) {
353                guint request_id = atoi(who + sizeof(PURPLE_REQUEST_HANDLE));
354                purple_request_input_callback(request_id, ic, message, who);
355                return 1;
356        }
357
358        if ((conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
359                                                          who, pd->account)) == NULL) {
360                conv = purple_conversation_new(PURPLE_CONV_TYPE_IM,
361                                               pd->account, who);
362        }
363
364        purple_conv_im_send(purple_conversation_get_im_data(conv), message);
365
366        return 1;
367}
368
369static GList *purple_away_states(struct im_connection *ic)
370{
371        struct purple_data *pd = ic->proto_data;
372        GList *st, *ret = NULL;
373
374        for (st = purple_account_get_status_types(pd->account); st; st = st->next) {
375                PurpleStatusPrimitive prim = purple_status_type_get_primitive(st->data);
376                if (prim != PURPLE_STATUS_AVAILABLE && prim != PURPLE_STATUS_OFFLINE) {
377                        ret = g_list_append(ret, (void *) purple_status_type_get_name(st->data));
378                }
379        }
380
381        return ret;
382}
383
384static void purple_set_away(struct im_connection *ic, char *state_txt, char *message)
385{
386        struct purple_data *pd = ic->proto_data;
387        GList *status_types = purple_account_get_status_types(pd->account), *st;
388        PurpleStatusType *pst = NULL;
389        GList *args = NULL;
390
391        for (st = status_types; st; st = st->next) {
392                pst = st->data;
393
394                if (state_txt == NULL &&
395                    purple_status_type_get_primitive(pst) == PURPLE_STATUS_AVAILABLE) {
396                        break;
397                }
398
399                if (state_txt != NULL &&
400                    g_strcasecmp(state_txt, purple_status_type_get_name(pst)) == 0) {
401                        break;
402                }
403        }
404
405        if (message && purple_status_type_get_attr(pst, "message")) {
406                args = g_list_append(args, "message");
407                args = g_list_append(args, message);
408        }
409
410        purple_account_set_status_list(pd->account,
411                                       st ? purple_status_type_get_id(pst) : "away",
412                                       TRUE, args);
413
414        g_list_free(args);
415}
416
417static char *set_eval_display_name(set_t *set, char *value)
418{
419        account_t *acc = set->data;
420        struct im_connection *ic = acc->ic;
421
422        if (ic) {
423                imcb_log(ic, "Changing display_name not currently supported with libpurple!");
424        }
425
426        return NULL;
427}
428
429/* Bad bad gadu-gadu, not saving buddy list by itself */
430static void purple_gg_buddylist_export(PurpleConnection *gc)
431{
432        struct im_connection *ic = purple_ic_by_gc(gc);
433
434        if (set_getstr(&ic->acc->set, "gg_sync_contacts")) {
435                GList *actions = gc->prpl->info->actions(gc->prpl, gc);
436                GList *p;
437                for (p = g_list_first(actions); p; p = p->next) {
438                        if (((PurplePluginAction *) p->data) &&
439                            purple_menu_cmp(((PurplePluginAction *) p->data)->label,
440                                            "Upload buddylist to Server") == 0) {
441                                PurplePluginAction action;
442                                action.plugin = gc->prpl;
443                                action.context = gc;
444                                action.user_data = NULL;
445                                ((PurplePluginAction *) p->data)->callback(&action);
446                                break;
447                        }
448                }
449                g_list_free(actions);
450        }
451}
452
453static void purple_gg_buddylist_import(PurpleConnection *gc)
454{
455        struct im_connection *ic = purple_ic_by_gc(gc);
456
457        if (set_getstr(&ic->acc->set, "gg_sync_contacts")) {
458                GList *actions = gc->prpl->info->actions(gc->prpl, gc);
459                GList *p;
460                for (p = g_list_first(actions); p; p = p->next) {
461                        if (((PurplePluginAction *) p->data) &&
462                            purple_menu_cmp(((PurplePluginAction *) p->data)->label,
463                                            "Download buddylist from Server") == 0) {
464                                PurplePluginAction action;
465                                action.plugin = gc->prpl;
466                                action.context = gc;
467                                action.user_data = NULL;
468                                ((PurplePluginAction *) p->data)->callback(&action);
469                                break;
470                        }
471                }
472                g_list_free(actions);
473        }
474}
475
476static void purple_add_buddy(struct im_connection *ic, char *who, char *group)
477{
478        PurpleBuddy *pb;
479        PurpleGroup *pg = NULL;
480        struct purple_data *pd = ic->proto_data;
481
482        if (group && !(pg = purple_find_group(group))) {
483                pg = purple_group_new(group);
484                purple_blist_add_group(pg, NULL);
485        }
486
487        pb = purple_buddy_new(pd->account, who, NULL);
488        purple_blist_add_buddy(pb, NULL, pg, NULL);
489        purple_account_add_buddy(pd->account, pb);
490
491        purple_gg_buddylist_export(pd->account->gc);
492}
493
494static void purple_remove_buddy(struct im_connection *ic, char *who, char *group)
495{
496        PurpleBuddy *pb;
497        struct purple_data *pd = ic->proto_data;
498
499        pb = purple_find_buddy(pd->account, who);
500        if (pb != NULL) {
501                PurpleGroup *group;
502
503                group = purple_buddy_get_group(pb);
504                purple_account_remove_buddy(pd->account, pb, group);
505
506                purple_blist_remove_buddy(pb);
507        }
508
509        purple_gg_buddylist_export(pd->account->gc);
510}
511
512static void purple_add_permit(struct im_connection *ic, char *who)
513{
514        struct purple_data *pd = ic->proto_data;
515
516        purple_privacy_permit_add(pd->account, who, FALSE);
517}
518
519static void purple_add_deny(struct im_connection *ic, char *who)
520{
521        struct purple_data *pd = ic->proto_data;
522
523        purple_privacy_deny_add(pd->account, who, FALSE);
524}
525
526static void purple_rem_permit(struct im_connection *ic, char *who)
527{
528        struct purple_data *pd = ic->proto_data;
529
530        purple_privacy_permit_remove(pd->account, who, FALSE);
531}
532
533static void purple_rem_deny(struct im_connection *ic, char *who)
534{
535        struct purple_data *pd = ic->proto_data;
536
537        purple_privacy_deny_remove(pd->account, who, FALSE);
538}
539
540static void purple_get_info(struct im_connection *ic, char *who)
541{
542        struct purple_data *pd = ic->proto_data;
543
544        serv_get_info(purple_account_get_connection(pd->account), who);
545}
546
547static void purple_keepalive(struct im_connection *ic)
548{
549}
550
551static int purple_send_typing(struct im_connection *ic, char *who, int flags)
552{
553        PurpleTypingState state = PURPLE_NOT_TYPING;
554        struct purple_data *pd = ic->proto_data;
555
556        if (flags & OPT_TYPING) {
557                state = PURPLE_TYPING;
558        } else if (flags & OPT_THINKING) {
559                state = PURPLE_TYPED;
560        }
561
562        serv_send_typing(purple_account_get_connection(pd->account), who, state);
563
564        return 1;
565}
566
567static void purple_chat_msg(struct groupchat *gc, char *message, int flags)
568{
569        PurpleConversation *pc = gc->data;
570
571        purple_conv_chat_send(purple_conversation_get_chat_data(pc), message);
572}
573
574struct groupchat *purple_chat_with(struct im_connection *ic, char *who)
575{
576        /* No, "of course" this won't work this way. Or in fact, it almost
577           does, but it only lets you send msgs to it, you won't receive
578           any. Instead, we have to click the virtual menu item.
579        PurpleAccount *pa = ic->proto_data;
580        PurpleConversation *pc;
581        PurpleConvChat *pcc;
582        struct groupchat *gc;
583
584        gc = imcb_chat_new( ic, "BitlBee-libpurple groupchat" );
585        gc->data = pc = purple_conversation_new( PURPLE_CONV_TYPE_CHAT, pa, "BitlBee-libpurple groupchat" );
586        pc->ui_data = gc;
587
588        pcc = PURPLE_CONV_CHAT( pc );
589        purple_conv_chat_add_user( pcc, ic->acc->user, "", 0, TRUE );
590        purple_conv_chat_invite_user( pcc, who, "Please join my chat", FALSE );
591        //purple_conv_chat_add_user( pcc, who, "", 0, TRUE );
592        */
593
594        /* There went my nice afternoon. :-( */
595
596        struct purple_data *pd = ic->proto_data;
597        PurplePlugin *prpl = purple_plugins_find_with_id(pd->account->protocol_id);
598        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
599        PurpleBuddy *pb = purple_find_buddy(pd->account, who);
600        PurpleMenuAction *mi;
601        GList *menu;
602
603        void (*callback)(PurpleBlistNode *, gpointer); /* FFFFFFFFFFFFFUUUUUUUUUUUUUU */
604
605        if (!pb || !pi || !pi->blist_node_menu) {
606                return NULL;
607        }
608
609        menu = pi->blist_node_menu(&pb->node);
610        while (menu) {
611                mi = menu->data;
612                if (purple_menu_cmp(mi->label, "initiate chat") ||
613                    purple_menu_cmp(mi->label, "initiate conference")) {
614                        break;
615                }
616                menu = menu->next;
617        }
618
619        if (menu == NULL) {
620                return NULL;
621        }
622
623        /* Call the fucker. */
624        callback = (void *) mi->callback;
625        callback(&pb->node, menu->data);
626
627        return NULL;
628}
629
630void purple_chat_invite(struct groupchat *gc, char *who, char *message)
631{
632        PurpleConversation *pc = gc->data;
633        PurpleConvChat *pcc = PURPLE_CONV_CHAT(pc);
634        struct purple_data *pd = gc->ic->proto_data;
635
636        serv_chat_invite(purple_account_get_connection(pd->account),
637                         purple_conv_chat_get_id(pcc),
638                         message && *message ? message : "Please join my chat",
639                         who);
640}
641
642void purple_chat_kick(struct groupchat *gc, char *who, const char *message)
643{
644        PurpleConversation *pc = gc->data;
645        char *str = g_strdup_printf("kick %s %s", who, message);
646
647        purple_conversation_do_command(pc, str, NULL, NULL);
648        g_free(str);
649}
650
651void purple_chat_leave(struct groupchat *gc)
652{
653        PurpleConversation *pc = gc->data;
654
655        purple_conversation_destroy(pc);
656}
657
658struct groupchat *purple_chat_join(struct im_connection *ic, const char *room, const char *nick, const char *password,
659                                   set_t **sets)
660{
661        struct purple_data *pd = ic->proto_data;
662        PurplePlugin *prpl = purple_plugins_find_with_id(pd->account->protocol_id);
663        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
664        GHashTable *chat_hash;
665        PurpleConversation *conv;
666        GList *info, *l;
667
668        if (!pi->chat_info || !pi->chat_info_defaults ||
669            !(info = pi->chat_info(purple_account_get_connection(pd->account)))) {
670                imcb_error(ic, "Joining chatrooms not supported by this protocol");
671                return NULL;
672        }
673
674        if ((conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
675                                                          room, pd->account))) {
676                purple_conversation_destroy(conv);
677        }
678
679        chat_hash = pi->chat_info_defaults(
680                purple_account_get_connection(pd->account), room
681        );
682
683        for (l = info; l; l = l->next) {
684                struct proto_chat_entry *pce = l->data;
685
686                if (strcmp(pce->identifier, "handle") == 0) {
687                        g_hash_table_replace(chat_hash, "handle", g_strdup(nick));
688                } else if (strcmp(pce->identifier, "password") == 0) {
689                        g_hash_table_replace(chat_hash, "password", g_strdup(password));
690                } else if (strcmp(pce->identifier, "passwd") == 0) {
691                        g_hash_table_replace(chat_hash, "passwd", g_strdup(password));
692                }
693        }
694
695        serv_join_chat(purple_account_get_connection(pd->account), chat_hash);
696
697        return NULL;
698}
699
700void purple_transfer_request(struct im_connection *ic, file_transfer_t *ft, char *handle);
701
702static void purple_ui_init();
703
704GHashTable *prplcb_ui_info()
705{
706        static GHashTable *ret;
707
708        if (ret == NULL) {
709                ret = g_hash_table_new(g_str_hash, g_str_equal);
710                g_hash_table_insert(ret, "name", "BitlBee");
711                g_hash_table_insert(ret, "version", BITLBEE_VERSION);
712        }
713
714        return ret;
715}
716
717static PurpleCoreUiOps bee_core_uiops =
718{
719        NULL,
720        NULL,
721        purple_ui_init,
722        NULL,
723        prplcb_ui_info,
724};
725
726static void prplcb_conn_progress(PurpleConnection *gc, const char *text, size_t step, size_t step_count)
727{
728        struct im_connection *ic = purple_ic_by_gc(gc);
729
730        imcb_log(ic, "%s", text);
731}
732
733static void prplcb_conn_connected(PurpleConnection *gc)
734{
735        struct im_connection *ic = purple_ic_by_gc(gc);
736        const char *dn;
737        set_t *s;
738
739        imcb_connected(ic);
740
741        if ((dn = purple_connection_get_display_name(gc)) &&
742            (s = set_find(&ic->acc->set, "display_name"))) {
743                g_free(s->value);
744                s->value = g_strdup(dn);
745        }
746
747        // user list needs to be requested for Gadu-Gadu
748        purple_gg_buddylist_import(gc);
749
750        if (gc->flags & PURPLE_CONNECTION_HTML) {
751                ic->flags |= OPT_DOES_HTML;
752        }
753}
754
755static void prplcb_conn_disconnected(PurpleConnection *gc)
756{
757        struct im_connection *ic = purple_ic_by_gc(gc);
758
759        if (ic != NULL) {
760                imc_logout(ic, !gc->wants_to_die);
761        }
762}
763
764static void prplcb_conn_notice(PurpleConnection *gc, const char *text)
765{
766        struct im_connection *ic = purple_ic_by_gc(gc);
767
768        if (ic != NULL) {
769                imcb_log(ic, "%s", text);
770        }
771}
772
773static void prplcb_conn_report_disconnect_reason(PurpleConnection *gc, PurpleConnectionError reason, const char *text)
774{
775        struct im_connection *ic = purple_ic_by_gc(gc);
776
777        /* PURPLE_CONNECTION_ERROR_NAME_IN_USE means concurrent login,
778           should probably handle that. */
779        if (ic != NULL) {
780                imcb_error(ic, "%s", text);
781        }
782}
783
784static PurpleConnectionUiOps bee_conn_uiops =
785{
786        prplcb_conn_progress,
787        prplcb_conn_connected,
788        prplcb_conn_disconnected,
789        prplcb_conn_notice,
790        NULL,
791        NULL,
792        NULL,
793        prplcb_conn_report_disconnect_reason,
794};
795
796static void prplcb_blist_update(PurpleBuddyList *list, PurpleBlistNode *node)
797{
798        if (node->type == PURPLE_BLIST_BUDDY_NODE) {
799                PurpleBuddy *bud = (PurpleBuddy *) node;
800                PurpleGroup *group = purple_buddy_get_group(bud);
801                struct im_connection *ic = purple_ic_by_pa(bud->account);
802                PurpleStatus *as;
803                int flags = 0;
804
805                if (ic == NULL) {
806                        return;
807                }
808
809                if (bud->server_alias) {
810                        imcb_rename_buddy(ic, bud->name, bud->server_alias);
811                } else if (bud->alias) {
812                        imcb_rename_buddy(ic, bud->name, bud->alias);
813                }
814
815                if (group) {
816                        imcb_add_buddy(ic, bud->name, purple_group_get_name(group));
817                }
818
819                flags |= purple_presence_is_online(bud->presence) ? OPT_LOGGED_IN : 0;
820                flags |= purple_presence_is_available(bud->presence) ? 0 : OPT_AWAY;
821
822                as = purple_presence_get_active_status(bud->presence);
823
824                imcb_buddy_status(ic, bud->name, flags, purple_status_get_name(as),
825                                  purple_status_get_attr_string(as, "message"));
826
827                imcb_buddy_times(ic, bud->name,
828                                 purple_presence_get_login_time(bud->presence),
829                                 purple_presence_get_idle_time(bud->presence));
830        }
831}
832
833static void prplcb_blist_new(PurpleBlistNode *node)
834{
835        if (node->type == PURPLE_BLIST_BUDDY_NODE) {
836                PurpleBuddy *bud = (PurpleBuddy *) node;
837                struct im_connection *ic = purple_ic_by_pa(bud->account);
838
839                if (ic == NULL) {
840                        return;
841                }
842
843                imcb_add_buddy(ic, bud->name, NULL);
844
845                prplcb_blist_update(NULL, node);
846        }
847}
848
849static void prplcb_blist_remove(PurpleBuddyList *list, PurpleBlistNode *node)
850{
851/*
852        PurpleBuddy *bud = (PurpleBuddy*) node;
853
854        if( node->type == PURPLE_BLIST_BUDDY_NODE )
855        {
856                struct im_connection *ic = purple_ic_by_pa( bud->account );
857
858                if( ic == NULL )
859                        return;
860
861                imcb_remove_buddy( ic, bud->name, NULL );
862        }
863*/
864}
865
866static PurpleBlistUiOps bee_blist_uiops =
867{
868        NULL,
869        prplcb_blist_new,
870        NULL,
871        prplcb_blist_update,
872        prplcb_blist_remove,
873};
874
875void prplcb_conv_new(PurpleConversation *conv)
876{
877        if (conv->type == PURPLE_CONV_TYPE_CHAT) {
878                struct im_connection *ic = purple_ic_by_pa(conv->account);
879                struct groupchat *gc;
880
881                gc = imcb_chat_new(ic, conv->name);
882                if (conv->title != NULL) {
883                        imcb_chat_name_hint(gc, conv->title);
884                        imcb_chat_topic(gc, NULL, conv->title, 0);
885                }
886
887                conv->ui_data = gc;
888                gc->data = conv;
889
890                /* libpurple brokenness: Whatever. Show that we join right away,
891                   there's no clear "This is you!" signaling in _add_users so
892                   don't even try. */
893                imcb_chat_add_buddy(gc, gc->ic->acc->user);
894        }
895}
896
897void prplcb_conv_free(PurpleConversation *conv)
898{
899        struct groupchat *gc = conv->ui_data;
900
901        imcb_chat_free(gc);
902}
903
904void prplcb_conv_add_users(PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals)
905{
906        struct groupchat *gc = conv->ui_data;
907        GList *b;
908
909        for (b = cbuddies; b; b = b->next) {
910                PurpleConvChatBuddy *pcb = b->data;
911
912                imcb_chat_add_buddy(gc, pcb->name);
913        }
914}
915
916void prplcb_conv_del_users(PurpleConversation *conv, GList *cbuddies)
917{
918        struct groupchat *gc = conv->ui_data;
919        GList *b;
920
921        for (b = cbuddies; b; b = b->next) {
922                imcb_chat_remove_buddy(gc, b->data, "");
923        }
924}
925
926void prplcb_conv_chat_msg(PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags,
927                          time_t mtime)
928{
929        struct groupchat *gc = conv->ui_data;
930        PurpleBuddy *buddy;
931
932        /* ..._SEND means it's an outgoing message, no need to echo those. */
933        if (flags & PURPLE_MESSAGE_SEND) {
934                return;
935        }
936
937        buddy = purple_find_buddy(conv->account, who);
938        if (buddy != NULL) {
939                who = purple_buddy_get_name(buddy);
940        }
941
942        imcb_chat_msg(gc, who, (char *) message, 0, mtime);
943}
944
945static void prplcb_conv_im(PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags,
946                           time_t mtime)
947{
948        struct im_connection *ic = purple_ic_by_pa(conv->account);
949        PurpleBuddy *buddy;
950
951        /* ..._SEND means it's an outgoing message, no need to echo those. */
952        if (flags & PURPLE_MESSAGE_SEND) {
953                return;
954        }
955
956        buddy = purple_find_buddy(conv->account, who);
957        if (buddy != NULL) {
958                who = purple_buddy_get_name(buddy);
959        }
960
961        imcb_buddy_msg(ic, (char *) who, (char *) message, 0, mtime);
962}
963
964/* No, this is not a ui_op but a signal. */
965static void prplcb_buddy_typing(PurpleAccount *account, const char *who, gpointer null)
966{
967        PurpleConversation *conv;
968        PurpleConvIm *im;
969        int state;
970
971        if ((conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, account)) == NULL) {
972                return;
973        }
974
975        im = PURPLE_CONV_IM(conv);
976        switch (purple_conv_im_get_typing_state(im)) {
977        case PURPLE_TYPING:
978                state = OPT_TYPING;
979                break;
980        case PURPLE_TYPED:
981                state = OPT_THINKING;
982                break;
983        default:
984                state = 0;
985        }
986
987        imcb_buddy_typing(purple_ic_by_pa(account), who, state);
988}
989
990static PurpleConversationUiOps bee_conv_uiops =
991{
992        prplcb_conv_new,           /* create_conversation  */
993        prplcb_conv_free,          /* destroy_conversation */
994        prplcb_conv_chat_msg,      /* write_chat           */
995        prplcb_conv_im,            /* write_im             */
996        NULL,                      /* write_conv           */
997        prplcb_conv_add_users,     /* chat_add_users       */
998        NULL,                      /* chat_rename_user     */
999        prplcb_conv_del_users,     /* chat_remove_users    */
1000        NULL,                      /* chat_update_user     */
1001        NULL,                      /* present              */
1002        NULL,                      /* has_focus            */
1003        NULL,                      /* custom_smiley_add    */
1004        NULL,                      /* custom_smiley_write  */
1005        NULL,                      /* custom_smiley_close  */
1006        NULL,                      /* send_confirm         */
1007};
1008
1009struct prplcb_request_action_data {
1010        void *user_data, *bee_data;
1011        PurpleRequestActionCb yes, no;
1012        int yes_i, no_i;
1013};
1014
1015static void prplcb_request_action_yes(void *data)
1016{
1017        struct prplcb_request_action_data *pqad = data;
1018
1019        if (pqad->yes) {
1020                pqad->yes(pqad->user_data, pqad->yes_i);
1021        }
1022}
1023
1024static void prplcb_request_action_no(void *data)
1025{
1026        struct prplcb_request_action_data *pqad = data;
1027
1028        if (pqad->no) {
1029                pqad->no(pqad->user_data, pqad->no_i);
1030        }
1031}
1032
1033/* q->free() callback from query_del()*/
1034static void prplcb_request_action_free(void *data)
1035{
1036        struct prplcb_request_action_data *pqad = data;
1037
1038        pqad->bee_data = NULL;
1039        purple_request_close(PURPLE_REQUEST_ACTION, pqad);
1040}
1041
1042static void *prplcb_request_action(const char *title, const char *primary, const char *secondary,
1043                                   int default_action, PurpleAccount *account, const char *who,
1044                                   PurpleConversation *conv, void *user_data, size_t action_count,
1045                                   va_list actions)
1046{
1047        struct prplcb_request_action_data *pqad;
1048        int i;
1049        char *q;
1050
1051        pqad = g_new0(struct prplcb_request_action_data, 1);
1052
1053        for (i = 0; i < action_count; i++) {
1054                char *caption;
1055                void *fn;
1056
1057                caption = va_arg(actions, char*);
1058                fn = va_arg(actions, void*);
1059
1060                if (strstr(caption, "Accept") || strstr(caption, "OK")) {
1061                        pqad->yes = fn;
1062                        pqad->yes_i = i;
1063                } else if (strstr(caption, "Reject") || strstr(caption, "Cancel")) {
1064                        pqad->no = fn;
1065                        pqad->no_i = i;
1066                }
1067        }
1068
1069        pqad->user_data = user_data;
1070
1071        /* TODO: IRC stuff here :-( */
1072        q = g_strdup_printf("Request: %s\n\n%s\n\n%s", title, primary, secondary);
1073        pqad->bee_data = query_add(local_bee->ui_data, purple_ic_by_pa(account), q,
1074                                   prplcb_request_action_yes, prplcb_request_action_no,
1075                                   prplcb_request_action_free, pqad);
1076
1077        g_free(q);
1078
1079        return pqad;
1080}
1081
1082/* So it turns out some requests have no account context at all, because
1083 * libpurple hates us. This means that query_del_by_conn() won't remove those
1084 * on logout, and will segfault if the user replies. That's why this exists.
1085 */
1086static void prplcb_close_request(PurpleRequestType type, void *data)
1087{
1088        struct prplcb_request_action_data *pqad;
1089        struct request_input_data *ri;
1090        struct purple_data *pd;
1091
1092        if (!data) {
1093                return;
1094        }
1095
1096        switch (type) {
1097        case PURPLE_REQUEST_ACTION:
1098                pqad = data;
1099                /* if this is null, it's because query_del was run already */
1100                if (pqad->bee_data) {
1101                        query_del(local_bee->ui_data, pqad->bee_data);
1102                }
1103                g_free(pqad);
1104                break;
1105        case PURPLE_REQUEST_INPUT:
1106                ri = data;
1107                pd = ri->ic->proto_data;
1108                imcb_remove_buddy(ri->ic, ri->buddy, NULL);
1109                g_free(ri->buddy);
1110                g_hash_table_remove(pd->input_requests, GUINT_TO_POINTER(ri->id));
1111                break;
1112        default:
1113                g_free(data);
1114                break;
1115        }
1116
1117}
1118
1119void* prplcb_request_input(const char *title, const char *primary,
1120        const char *secondary, const char *default_value, gboolean multiline,
1121        gboolean masked, gchar *hint, const char *ok_text, GCallback ok_cb,
1122        const char *cancel_text, GCallback cancel_cb, PurpleAccount *account,
1123        const char *who, PurpleConversation *conv, void *user_data)
1124{
1125        struct im_connection *ic = purple_ic_by_pa(account);
1126        struct purple_data *pd = ic->proto_data;
1127        struct request_input_data *ri = g_new0(struct request_input_data, 1);
1128        guint id = pd->next_request_id++;
1129
1130        ri->id = id;
1131        ri->ic = ic;
1132        ri->buddy = g_strdup_printf("%s_%u", PURPLE_REQUEST_HANDLE, id);
1133        ri->data_callback = (ri_callback_t) ok_cb;
1134        ri->user_data = user_data;
1135        g_hash_table_insert(pd->input_requests, GUINT_TO_POINTER(id), ri);
1136
1137        imcb_add_buddy(ic, ri->buddy, NULL);
1138        imcb_buddy_msg(ic, ri->buddy, secondary, 0, 0);
1139
1140        return ri;
1141}
1142
1143void purple_request_input_callback(guint id, struct im_connection *ic,
1144                                   const char *message, const char *who)
1145{
1146        struct purple_data *pd = ic->proto_data;
1147        struct request_input_data *ri;
1148
1149        if (!(ri = g_hash_table_lookup(pd->input_requests, GUINT_TO_POINTER(id)))) {
1150                return;
1151        }
1152
1153        ri->data_callback(ri->user_data, message);
1154
1155        purple_request_close(PURPLE_REQUEST_INPUT, ri);
1156}
1157
1158
1159static PurpleRequestUiOps bee_request_uiops =
1160{
1161        prplcb_request_input,
1162        NULL,
1163        prplcb_request_action,
1164        NULL,
1165        NULL,
1166        prplcb_close_request,
1167        NULL,
1168};
1169
1170static void prplcb_privacy_permit_added(PurpleAccount *account, const char *name)
1171{
1172        struct im_connection *ic = purple_ic_by_pa(account);
1173
1174        if (!g_slist_find_custom(ic->permit, name, (GCompareFunc) ic->acc->prpl->handle_cmp)) {
1175                ic->permit = g_slist_prepend(ic->permit, g_strdup(name));
1176        }
1177}
1178
1179static void prplcb_privacy_permit_removed(PurpleAccount *account, const char *name)
1180{
1181        struct im_connection *ic = purple_ic_by_pa(account);
1182        void *n;
1183
1184        n = g_slist_find_custom(ic->permit, name, (GCompareFunc) ic->acc->prpl->handle_cmp);
1185        ic->permit = g_slist_remove(ic->permit, n);
1186}
1187
1188static void prplcb_privacy_deny_added(PurpleAccount *account, const char *name)
1189{
1190        struct im_connection *ic = purple_ic_by_pa(account);
1191
1192        if (!g_slist_find_custom(ic->deny, name, (GCompareFunc) ic->acc->prpl->handle_cmp)) {
1193                ic->deny = g_slist_prepend(ic->deny, g_strdup(name));
1194        }
1195}
1196
1197static void prplcb_privacy_deny_removed(PurpleAccount *account, const char *name)
1198{
1199        struct im_connection *ic = purple_ic_by_pa(account);
1200        void *n;
1201
1202        n = g_slist_find_custom(ic->deny, name, (GCompareFunc) ic->acc->prpl->handle_cmp);
1203        ic->deny = g_slist_remove(ic->deny, n);
1204}
1205
1206static PurplePrivacyUiOps bee_privacy_uiops =
1207{
1208        prplcb_privacy_permit_added,
1209        prplcb_privacy_permit_removed,
1210        prplcb_privacy_deny_added,
1211        prplcb_privacy_deny_removed,
1212};
1213
1214static void prplcb_debug_print(PurpleDebugLevel level, const char *category, const char *arg_s)
1215{
1216        fprintf(stderr, "DEBUG %s: %s", category, arg_s);
1217}
1218
1219static PurpleDebugUiOps bee_debug_uiops =
1220{
1221        prplcb_debug_print,
1222};
1223
1224static guint prplcb_ev_timeout_add(guint interval, GSourceFunc func, gpointer udata)
1225{
1226        return b_timeout_add(interval, (b_event_handler) func, udata);
1227}
1228
1229static guint prplcb_ev_input_add(int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata)
1230{
1231        return b_input_add(fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata);
1232}
1233
1234static gboolean prplcb_ev_remove(guint id)
1235{
1236        b_event_remove((gint) id);
1237        return TRUE;
1238}
1239
1240static PurpleEventLoopUiOps glib_eventloops =
1241{
1242        prplcb_ev_timeout_add,
1243        prplcb_ev_remove,
1244        prplcb_ev_input_add,
1245        prplcb_ev_remove,
1246};
1247
1248static void *prplcb_notify_email(PurpleConnection *gc, const char *subject, const char *from,
1249                                 const char *to, const char *url)
1250{
1251        struct im_connection *ic = purple_ic_by_gc(gc);
1252
1253        imcb_log(ic, "Received e-mail from %s for %s: %s <%s>", from, to, subject, url);
1254
1255        return NULL;
1256}
1257
1258static void *prplcb_notify_userinfo(PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info)
1259{
1260        struct im_connection *ic = purple_ic_by_gc(gc);
1261        GString *info = g_string_new("");
1262        GList *l = purple_notify_user_info_get_entries(user_info);
1263        char *key;
1264        const char *value;
1265        int n;
1266
1267        while (l) {
1268                PurpleNotifyUserInfoEntry *e = l->data;
1269
1270                switch (purple_notify_user_info_entry_get_type(e)) {
1271                case PURPLE_NOTIFY_USER_INFO_ENTRY_PAIR:
1272                case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER:
1273                        key = g_strdup(purple_notify_user_info_entry_get_label(e));
1274                        value = purple_notify_user_info_entry_get_value(e);
1275
1276                        if (key) {
1277                                strip_html(key);
1278                                g_string_append_printf(info, "%s: ", key);
1279
1280                                if (value) {
1281                                        n = strlen(value) - 1;
1282                                        while (g_ascii_isspace(value[n])) {
1283                                                n--;
1284                                        }
1285                                        g_string_append_len(info, value, n + 1);
1286                                }
1287                                g_string_append_c(info, '\n');
1288                                g_free(key);
1289                        }
1290
1291                        break;
1292                case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK:
1293                        g_string_append(info, "------------------------\n");
1294                        break;
1295                }
1296
1297                l = l->next;
1298        }
1299
1300        imcb_log(ic, "User %s info:\n%s", who, info->str);
1301        g_string_free(info, TRUE);
1302
1303        return NULL;
1304}
1305
1306static PurpleNotifyUiOps bee_notify_uiops =
1307{
1308        NULL,
1309        prplcb_notify_email,
1310        NULL,
1311        NULL,
1312        NULL,
1313        NULL,
1314        prplcb_notify_userinfo,
1315};
1316
1317static void *prplcb_account_request_authorize(PurpleAccount *account, const char *remote_user,
1318                                              const char *id, const char *alias, const char *message, gboolean on_list,
1319                                              PurpleAccountRequestAuthorizationCb authorize_cb,
1320                                              PurpleAccountRequestAuthorizationCb deny_cb, void *user_data)
1321{
1322        struct im_connection *ic = purple_ic_by_pa(account);
1323        char *q;
1324
1325        if (alias) {
1326                q = g_strdup_printf("%s (%s) wants to add you to his/her contact "
1327                                    "list. (%s)", alias, remote_user, message);
1328        } else {
1329                q = g_strdup_printf("%s wants to add you to his/her contact "
1330                                    "list. (%s)", remote_user, message);
1331        }
1332
1333        imcb_ask_with_free(ic, q, user_data, authorize_cb, deny_cb, NULL);
1334        g_free(q);
1335
1336        return NULL;
1337}
1338
1339static PurpleAccountUiOps bee_account_uiops =
1340{
1341        NULL,
1342        NULL,
1343        NULL,
1344        prplcb_account_request_authorize,
1345        NULL,
1346};
1347
1348extern PurpleXferUiOps bee_xfer_uiops;
1349
1350static void purple_ui_init()
1351{
1352        purple_connections_set_ui_ops(&bee_conn_uiops);
1353        purple_blist_set_ui_ops(&bee_blist_uiops);
1354        purple_conversations_set_ui_ops(&bee_conv_uiops);
1355        purple_request_set_ui_ops(&bee_request_uiops);
1356        purple_privacy_set_ui_ops(&bee_privacy_uiops);
1357        purple_notify_set_ui_ops(&bee_notify_uiops);
1358        purple_accounts_set_ui_ops(&bee_account_uiops);
1359        purple_xfers_set_ui_ops(&bee_xfer_uiops);
1360
1361        if (getenv("BITLBEE_DEBUG")) {
1362                purple_debug_set_ui_ops(&bee_debug_uiops);
1363        }
1364}
1365
1366void purple_initmodule()
1367{
1368        struct prpl funcs;
1369        GList *prots;
1370        GString *help;
1371        char *dir;
1372
1373        if (B_EV_IO_READ != PURPLE_INPUT_READ ||
1374            B_EV_IO_WRITE != PURPLE_INPUT_WRITE) {
1375                /* FIXME FIXME FIXME FIXME FIXME :-) */
1376                exit(1);
1377        }
1378
1379        dir = g_strdup_printf("%s/purple", global.conf->configdir);
1380        purple_util_set_user_dir(dir);
1381        g_free(dir);
1382
1383        purple_debug_set_enabled(FALSE);
1384        purple_core_set_ui_ops(&bee_core_uiops);
1385        purple_eventloop_set_ui_ops(&glib_eventloops);
1386        if (!purple_core_init("BitlBee")) {
1387                /* Initializing the core failed. Terminate. */
1388                fprintf(stderr, "libpurple initialization failed.\n");
1389                abort();
1390        }
1391
1392        if (proxytype != PROXY_NONE) {
1393                PurpleProxyInfo *pi = purple_global_proxy_get_info();
1394                switch (proxytype) {
1395                case PROXY_SOCKS4:
1396                        purple_proxy_info_set_type(pi, PURPLE_PROXY_SOCKS4);
1397                        break;
1398                case PROXY_SOCKS5:
1399                        purple_proxy_info_set_type(pi, PURPLE_PROXY_SOCKS5);
1400                        break;
1401                case PROXY_HTTP:
1402                        purple_proxy_info_set_type(pi, PURPLE_PROXY_HTTP);
1403                        break;
1404                }
1405                purple_proxy_info_set_host(pi, proxyhost);
1406                purple_proxy_info_set_port(pi, proxyport);
1407                purple_proxy_info_set_username(pi, proxyuser);
1408                purple_proxy_info_set_password(pi, proxypass);
1409        }
1410
1411        purple_set_blist(purple_blist_new());
1412
1413        /* No, really. So far there were ui_ops for everything, but now suddenly
1414           one needs to use signals for typing notification stuff. :-( */
1415        purple_signal_connect(purple_conversations_get_handle(), "buddy-typing",
1416                              &funcs, PURPLE_CALLBACK(prplcb_buddy_typing), NULL);
1417        purple_signal_connect(purple_conversations_get_handle(), "buddy-typed",
1418                              &funcs, PURPLE_CALLBACK(prplcb_buddy_typing), NULL);
1419        purple_signal_connect(purple_conversations_get_handle(), "buddy-typing-stopped",
1420                              &funcs, PURPLE_CALLBACK(prplcb_buddy_typing), NULL);
1421
1422        memset(&funcs, 0, sizeof(funcs));
1423        funcs.login = purple_login;
1424        funcs.init = purple_init;
1425        funcs.logout = purple_logout;
1426        funcs.buddy_msg = purple_buddy_msg;
1427        funcs.away_states = purple_away_states;
1428        funcs.set_away = purple_set_away;
1429        funcs.add_buddy = purple_add_buddy;
1430        funcs.remove_buddy = purple_remove_buddy;
1431        funcs.add_permit = purple_add_permit;
1432        funcs.add_deny = purple_add_deny;
1433        funcs.rem_permit = purple_rem_permit;
1434        funcs.rem_deny = purple_rem_deny;
1435        funcs.get_info = purple_get_info;
1436        funcs.keepalive = purple_keepalive;
1437        funcs.send_typing = purple_send_typing;
1438        funcs.handle_cmp = g_strcasecmp;
1439        /* TODO(wilmer): Set these only for protocols that support them? */
1440        funcs.chat_msg = purple_chat_msg;
1441        funcs.chat_with = purple_chat_with;
1442        funcs.chat_invite = purple_chat_invite;
1443        funcs.chat_kick = purple_chat_kick;
1444        funcs.chat_leave = purple_chat_leave;
1445        funcs.chat_join = purple_chat_join;
1446        funcs.transfer_request = purple_transfer_request;
1447
1448        help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n");
1449
1450        /* Add a protocol entry to BitlBee's structures for every protocol
1451           supported by this libpurple instance. */
1452        for (prots = purple_plugins_get_protocols(); prots; prots = prots->next) {
1453                PurplePlugin *prot = prots->data;
1454                struct prpl *ret;
1455
1456                /* If we already have this one (as a native module), don't
1457                   add a libpurple duplicate. */
1458                if (find_protocol(prot->info->id)) {
1459                        continue;
1460                }
1461
1462                ret = g_memdup(&funcs, sizeof(funcs));
1463                ret->name = ret->data = prot->info->id;
1464                if (strncmp(ret->name, "prpl-", 5) == 0) {
1465                        ret->name += 5;
1466                }
1467                register_protocol(ret);
1468
1469                g_string_append_printf(help, "\n* %s (%s)", ret->name, prot->info->name);
1470
1471                /* libpurple doesn't define a protocol called OSCAR, but we
1472                   need it to be compatible with normal BitlBee. */
1473                if (g_strcasecmp(prot->info->id, "prpl-aim") == 0) {
1474                        ret = g_memdup(&funcs, sizeof(funcs));
1475                        ret->name = "oscar";
1476                        ret->data = prot->info->id;
1477                        register_protocol(ret);
1478                }
1479        }
1480
1481        g_string_append(help, "\n\nFor used protocols, more information about available "
1482                        "settings can be found using \x02help purple <protocol name>\x02 "
1483                        "(create an account using that protocol first!)");
1484
1485        /* Add a simple dynamically-generated help item listing all
1486           the supported protocols. */
1487        help_add_mem(&global.help, "purple", help->str);
1488        g_string_free(help, TRUE);
1489}
Note: See TracBrowser for help on using the repository browser.