source: protocols/purple/purple.c @ b6a3fbf

Last change on this file since b6a3fbf was b38f655, checked in by dequis <dx@…>, at 2015-05-28T05:26:30Z

Rename mail notification related settings for consistency

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