source: protocols/nogaim.c @ 5a8afc3

Last change on this file since 5a8afc3 was 5a8afc3, checked in by dequis <dx@…>, at 2016-11-21T06:58:47Z

Manual merge with wilmer's approach to handling missing protocols

Turns out he already implemented pretty much the same thing in the
parson branch... last year.

The differences between the two approaches are subtle (there aren't too
many ways to do this, some lines are the exact same thing) but I decided
I like his version better, so this mostly reverts a handful of my
changes while keeping others. The main advantage of his approach is that
no fake protocols are registered, no actual prpl functions are called,
and the missing prpl is a singleton constant.

New things compared to the implementation in the other branch:

  • The explain_unknown_protocol() function.
  • Fixed named chatrooms throwing a warning and losing the "account" setting when saving. See changes in irc_im.c
  • Fixed the "server" setting dropping when saving. See account.c

Differences with my previous implementation:

  • Accounts with missing protocols don't autoconnect
  • 'account list' marks them as "(missing!)"
  • Property mode set to 100644
File size: 20.8 KB
RevLine 
[5ebff60]1/********************************************************************\
[b7d3cc34]2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
[0e788f5]4  * Copyright 2002-2012 Wilmer van der Gaast and others                *
[b7d3cc34]5  \********************************************************************/
6
7/*
8 * nogaim
9 *
10 * Gaim without gaim - for BitlBee
11 *
12 * This file contains functions called by the Gaim IM-modules. It's written
13 * from scratch for BitlBee and doesn't contain any code from Gaim anymore
14 * (except for the function names).
15 */
16
17/*
18  This program is free software; you can redistribute it and/or modify
19  it under the terms of the GNU General Public License as published by
20  the Free Software Foundation; either version 2 of the License, or
21  (at your option) any later version.
22
23  This program is distributed in the hope that it will be useful,
24  but WITHOUT ANY WARRANTY; without even the implied warranty of
25  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26  GNU General Public License for more details.
27
28  You should have received a copy of the GNU General Public License with
29  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
[6f10697]30  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
31  Fifth Floor, Boston, MA  02110-1301  USA
[b7d3cc34]32*/
33
34#define BITLBEE_CORE
35#include <ctype.h>
36
[4cf80bb]37#include "nogaim.h"
38
[b7d3cc34]39GSList *connections;
40
[65e2ce1]41#ifdef WITH_PLUGINS
[d28fe1c4]42GList *plugins = NULL;
43
44static gint pluginscmp(gconstpointer a, gconstpointer b, gpointer data)
45{
46        const struct plugin_info *ia = a;
47        const struct plugin_info *ib = b;
48
49        return g_strcasecmp(ia->name, ib->name);
50}
51
[7b23afd]52gboolean load_plugin(char *path)
53{
[d28fe1c4]54        GList *l;
55        struct plugin_info *i;
56        struct plugin_info *info;
57        struct plugin_info * (*info_function) (void) = NULL;
[7b23afd]58        void (*init_function) (void);
[5ebff60]59
[7b23afd]60        GModule *mod = g_module_open(path, G_MODULE_BIND_LAZY);
[d28fe1c4]61        gboolean loaded = FALSE;
[7b23afd]62
[5ebff60]63        if (!mod) {
[4fe91a1]64                log_message(LOGLVL_ERROR, "Error loading plugin `%s': %s\n", path, g_module_error());
[7b23afd]65                return FALSE;
66        }
67
[d28fe1c4]68        if (g_module_symbol(mod, "init_plugin_info", (gpointer *) &info_function)) {
69                info = info_function();
70
71                if (info->abiver != BITLBEE_ABI_VERSION_CODE) {
72                        log_message(LOGLVL_ERROR,
73                                    "`%s' uses ABI %u but %u is required\n",
74                                    path, info->abiver,
75                                    BITLBEE_ABI_VERSION_CODE);
76                        g_module_close(mod);
77                        return FALSE;
78                }
79
80                if (!info->name || !info->version) {
81                        log_message(LOGLVL_ERROR,
82                                    "Name or version missing from the "
83                                    "plugin info in `%s'\n", path);
84                        g_module_close(mod);
85                        return FALSE;
86                }
87
88                for (l = plugins; l; l = l->next) {
89                        i = l->data;
90
91                        if (g_strcasecmp(i->name, info->name) == 0) {
92                                loaded = TRUE;
93                                break;
94                        }
95                }
96
97                if (loaded) {
98                        log_message(LOGLVL_WARNING,
99                                    "%s plugin already loaded\n",
100                                    info->name);
101                        g_module_close(mod);
102                        return FALSE;
103                }
104        } else {
105                log_message(LOGLVL_WARNING, "Can't find function `init_plugin_info' in `%s'\n", path);
106        }
107
[5ebff60]108        if (!g_module_symbol(mod, "init_plugin", (gpointer *) &init_function)) {
[7b23afd]109                log_message(LOGLVL_WARNING, "Can't find function `init_plugin' in `%s'\n", path);
[d28fe1c4]110                g_module_close(mod);
[7b23afd]111                return FALSE;
112        }
113
[d28fe1c4]114        if (info_function) {
115                plugins = g_list_insert_sorted_with_data(plugins, info,
116                                                         pluginscmp, NULL);
117        }
[7b23afd]118
[d28fe1c4]119        init_function();
[7b23afd]120        return TRUE;
121}
[b7d3cc34]122
[65e2ce1]123void load_plugins(void)
124{
125        GDir *dir;
126        GError *error = NULL;
127
[4bfca70]128        dir = g_dir_open(global.conf->plugindir, 0, &error);
[65e2ce1]129
130        if (dir) {
131                const gchar *entry;
132                char *path;
133
134                while ((entry = g_dir_read_name(dir))) {
[35712b7]135                        if (!g_str_has_suffix(entry, "." G_MODULE_SUFFIX)) {
136                                continue;
137                        }
138
[4bfca70]139                        path = g_build_filename(global.conf->plugindir, entry, NULL);
[5ebff60]140                        if (!path) {
[65e2ce1]141                                log_message(LOGLVL_WARNING, "Can't build path for %s\n", entry);
142                                continue;
143                        }
144
145                        load_plugin(path);
146
147                        g_free(path);
148                }
149
150                g_dir_close(dir);
151        }
152}
[d28fe1c4]153
154GList *get_plugins()
155{
156        return plugins;
157}
[65e2ce1]158#endif
[b7d3cc34]159
[7b23afd]160GList *protocols = NULL;
[ad9ac5d]161GList *disabled_protocols = NULL;
[5ebff60]162
163void register_protocol(struct prpl *p)
[7b23afd]164{
[90cd6c4]165        int i;
166        gboolean refused = global.conf->protocols != NULL;
[5ebff60]167
168        for (i = 0; global.conf->protocols && global.conf->protocols[i]; i++) {
169                if (g_strcasecmp(p->name, global.conf->protocols[i]) == 0) {
[90cd6c4]170                        refused = FALSE;
[5ebff60]171                }
172        }
[90cd6c4]173
[5ebff60]174        if (refused) {
[ad9ac5d]175                disabled_protocols = g_list_append(disabled_protocols, p);
[5ebff60]176        } else {
[90cd6c4]177                protocols = g_list_append(protocols, p);
[5ebff60]178        }
[7b23afd]179}
180
[ad9ac5d]181static int proto_name_cmp(const void *proto_, const void *name)
[7b23afd]182{
[ad9ac5d]183        const struct prpl *proto = proto_;
184        return g_strcasecmp(proto->name, name);
185}
[5ebff60]186
[ad9ac5d]187struct prpl *find_protocol(const char *name)
188{
189        GList *gl = g_list_find_custom(protocols, name, proto_name_cmp);
190        return gl ? gl->data: NULL;
191}
[5ebff60]192
[ad9ac5d]193gboolean is_protocol_disabled(const char *name)
194{
195        return g_list_find_custom(disabled_protocols, name, proto_name_cmp) != NULL;
[7b23afd]196}
197
[b4f496e]198/* Returns heap allocated string with text attempting to explain why a protocol is missing
199 * Free the return value with g_free() */
200char *explain_unknown_protocol(const char *name)
201{
202        char *extramsg = NULL;
203
204        if (is_protocol_disabled(name)) {
205                return g_strdup("Protocol disabled in the global config (bitlbee.conf)");
206        }
207
208        if (strcmp(name, "yahoo") == 0) {
209                return g_strdup("The old yahoo protocol is gone, try the funyahoo++ libpurple plugin instead.");
210        }
211
212#ifdef WITH_PURPLE
213        if ((strcmp(name, "msn") == 0) ||
214            (strcmp(name, "loubserp-mxit") == 0) ||
215            (strcmp(name, "myspace") == 0)) {
216                return g_strdup("This protocol has been removed from your libpurple version.");
217        }
218
219        if (strcmp(name, "hipchat") == 0) {
220                return g_strdup("This account type isn't supported by libpurple's jabber.");
221        }
222
223#else
224        if (strcmp(name, "aim") == 0 || strcmp(name, "icq") == 0) {
225                return g_strdup("This account uses libpurple specific aliases for oscar. "
226                                "Re-add the account with `account add oscar ...'");
227        }
228
229        extramsg = "If this is a libpurple plugin, you might need to install bitlbee-libpurple instead.";
230#endif
231        return g_strconcat("The protocol plugin is not installed or could not be loaded. "
232                           "Use the `plugins' command to list available protocols. ",
233                           extramsg, NULL);
234}
235
[b7d3cc34]236void nogaim_init()
237{
[0da65d5]238        extern void msn_initmodule();
239        extern void oscar_initmodule();
240        extern void jabber_initmodule();
[1b221e0]241        extern void twitter_initmodule();
[796da03]242        extern void purple_initmodule();
[7b23afd]243
[b7d3cc34]244#ifdef WITH_MSN
[0da65d5]245        msn_initmodule();
[b7d3cc34]246#endif
247
248#ifdef WITH_OSCAR
[0da65d5]249        oscar_initmodule();
[b7d3cc34]250#endif
[5ebff60]251
[b7d3cc34]252#ifdef WITH_JABBER
[0da65d5]253        jabber_initmodule();
[b7d3cc34]254#endif
[7b23afd]255
[1b221e0]256#ifdef WITH_TWITTER
257        twitter_initmodule();
258#endif
259
[796da03]260#ifdef WITH_PURPLE
261        purple_initmodule();
262#endif
[7b23afd]263
[65e2ce1]264#ifdef WITH_PLUGINS
265        load_plugins();
[b7d3cc34]266#endif
267}
268
[808825e]269GList *get_protocols()
270{
271        return protocols;
272}
273
274GList *get_protocols_disabled()
275{
276        return disabled_protocols;
277}
278
[5ebff60]279GSList *get_connections()
280{
281        return connections;
282}
[b7d3cc34]283
[5ebff60]284struct im_connection *imcb_new(account_t *acc)
[b7d3cc34]285{
[0da65d5]286        struct im_connection *ic;
[5ebff60]287
288        ic = g_new0(struct im_connection, 1);
289
[81e04e1]290        ic->bee = acc->bee;
[0da65d5]291        ic->acc = acc;
292        acc->ic = ic;
[5ebff60]293
294        connections = g_slist_append(connections, ic);
295
296        return(ic);
[b7d3cc34]297}
298
[5ebff60]299void imc_free(struct im_connection *ic)
[b7d3cc34]300{
301        account_t *a;
[5ebff60]302
[b7d3cc34]303        /* Destroy the pointer to this connection from the account list */
[5ebff60]304        for (a = ic->bee->accounts; a; a = a->next) {
305                if (a->ic == ic) {
[0da65d5]306                        a->ic = NULL;
[b7d3cc34]307                        break;
308                }
[5ebff60]309        }
310
311        connections = g_slist_remove(connections, ic);
312        g_free(ic);
[b7d3cc34]313}
314
[5ebff60]315static void serv_got_crap(struct im_connection *ic, char *format, ...)
[b7d3cc34]316{
317        va_list params;
[e27661d]318        char *text;
[dfde8e0]319        account_t *a;
[5ebff60]320
[03df717]321        if (!ic->bee->ui->log) {
322                return;
323        }
324
[5ebff60]325        va_start(params, format);
326        text = g_strdup_vprintf(format, params);
327        va_end(params);
328
329        if ((g_strcasecmp(set_getstr(&ic->bee->set, "strip_html"), "always") == 0) ||
330            ((ic->flags & OPT_DOES_HTML) && set_getbool(&ic->bee->set, "strip_html"))) {
331                strip_html(text);
332        }
333
[dfde8e0]334        /* Try to find a different connection on the same protocol. */
[5ebff60]335        for (a = ic->bee->accounts; a; a = a->next) {
336                if (a->prpl == ic->acc->prpl && a->ic != ic) {
[dfde8e0]337                        break;
[5ebff60]338                }
339        }
340
[e27661d]341        /* If we found one, include the screenname in the message. */
[5ebff60]342        if (a) {
[03df717]343                ic->bee->ui->log(ic->bee, ic->acc->tag, text);
[5ebff60]344        } else {
[03df717]345                ic->bee->ui->log(ic->bee, ic->acc->prpl->name, text);
[5ebff60]346        }
347
348        g_free(text);
[b7d3cc34]349}
350
[5ebff60]351void imcb_log(struct im_connection *ic, char *format, ...)
[aef4828]352{
353        va_list params;
354        char *text;
[5ebff60]355
356        va_start(params, format);
357        text = g_strdup_vprintf(format, params);
358        va_end(params);
359
360        if (ic->flags & OPT_LOGGED_IN) {
361                serv_got_crap(ic, "%s", text);
362        } else {
363                serv_got_crap(ic, "Logging in: %s", text);
364        }
365
366        g_free(text);
[aef4828]367}
368
[5ebff60]369void imcb_error(struct im_connection *ic, char *format, ...)
[aef4828]370{
371        va_list params;
372        char *text;
[5ebff60]373
374        va_start(params, format);
375        text = g_strdup_vprintf(format, params);
376        va_end(params);
377
378        if (ic->flags & OPT_LOGGED_IN) {
379                serv_got_crap(ic, "Error: %s", text);
380        } else {
381                serv_got_crap(ic, "Login error: %s", text);
382        }
383
384        g_free(text);
[aef4828]385}
386
[5ebff60]387static gboolean send_keepalive(gpointer d, gint fd, b_input_condition cond)
[b7d3cc34]388{
[0da65d5]389        struct im_connection *ic = d;
[5ebff60]390
391        if ((ic->flags & OPT_PONGS) && !(ic->flags & OPT_PONGED)) {
[e132b60]392                /* This protocol is expected to ack keepalives and hasn't
393                   since the last time we were here. */
[5ebff60]394                imcb_error(ic, "Connection timeout");
395                imc_logout(ic, TRUE);
[e132b60]396                return FALSE;
397        }
398        ic->flags &= ~OPT_PONGED;
[5ebff60]399
400        if (ic->acc->prpl->keepalive) {
401                ic->acc->prpl->keepalive(ic);
402        }
403
[b7d3cc34]404        return TRUE;
405}
406
[5ebff60]407void start_keepalives(struct im_connection *ic, int interval)
[e132b60]408{
[5ebff60]409        b_event_remove(ic->keepalive);
410        ic->keepalive = b_timeout_add(interval, send_keepalive, ic);
411
[e132b60]412        /* Connecting successfully counts as a first successful pong. */
[5ebff60]413        if (ic->flags & OPT_PONGS) {
[e132b60]414                ic->flags |= OPT_PONGED;
[5ebff60]415        }
[e132b60]416}
417
[5ebff60]418void imcb_connected(struct im_connection *ic)
[b7d3cc34]419{
420        /* MSN servers sometimes redirect you to a different server and do
[84c1a0a]421           the whole login sequence again, so these "late" calls to this
[b7d3cc34]422           function should be handled correctly. (IOW, ignored) */
[5ebff60]423        if (ic->flags & OPT_LOGGED_IN) {
[b7d3cc34]424                return;
[5ebff60]425        }
[11e7828]426
[5ebff60]427        if (ic->acc->flags & ACC_FLAG_LOCAL) {
[11e7828]428                GHashTableIter nicks;
429                gpointer k, v;
[5ebff60]430                g_hash_table_iter_init(&nicks, ic->acc->nicks);
431                while (g_hash_table_iter_next(&nicks, &k, &v)) {
432                        ic->acc->prpl->add_buddy(ic, (char *) k, NULL);
[11e7828]433                }
434        }
[5ebff60]435
436        imcb_log(ic, "Logged in");
437
[0da65d5]438        ic->flags |= OPT_LOGGED_IN;
[5ebff60]439        start_keepalives(ic, 60000);
440
[58adb7e]441        /* Necessary to send initial presence status, even if we're not away. */
[5ebff60]442        imc_away_send_update(ic);
443
[280e655]444        /* Apparently we're connected successfully, so reset the
445           exponential backoff timer. */
446        ic->acc->auto_reconnect_delay = 0;
[5ebff60]447
448        if (ic->bee->ui->imc_connected) {
449                ic->bee->ui->imc_connected(ic);
450        }
[b7d3cc34]451}
452
[5ebff60]453gboolean auto_reconnect(gpointer data, gint fd, b_input_condition cond)
[b7d3cc34]454{
455        account_t *a = data;
[5ebff60]456
[b7d3cc34]457        a->reconnect = 0;
[5ebff60]458        account_on(a->bee, a);
459
460        return(FALSE);          /* Only have to run the timeout once */
[b7d3cc34]461}
462
[5ebff60]463void cancel_auto_reconnect(account_t *a)
[b7d3cc34]464{
[5ebff60]465        b_event_remove(a->reconnect);
[b7d3cc34]466        a->reconnect = 0;
467}
468
[5ebff60]469void imc_logout(struct im_connection *ic, int allow_reconnect)
[b7d3cc34]470{
[81e04e1]471        bee_t *bee = ic->bee;
[b7d3cc34]472        account_t *a;
[81e04e1]473        GSList *l;
[4230221]474        int delay;
[5ebff60]475
[8d74291]476        /* Nested calls might happen sometimes, this is probably the best
477           place to catch them. */
[5ebff60]478        if (ic->flags & OPT_LOGGING_OUT) {
[8d74291]479                return;
[5ebff60]480        } else {
[0da65d5]481                ic->flags |= OPT_LOGGING_OUT;
[5ebff60]482        }
483
484        if (ic->bee->ui->imc_disconnected) {
485                ic->bee->ui->imc_disconnected(ic);
486        }
487
488        imcb_log(ic, "Signing off..");
489
[0dd6570]490        /* TBH I don't remember anymore why I didn't just use ic->acc... */
[5ebff60]491        for (a = bee->accounts; a; a = a->next) {
492                if (a->ic == ic) {
[0dd6570]493                        break;
[5ebff60]494                }
495        }
496
497        if (a && !allow_reconnect && !(ic->flags & OPT_LOGGED_IN) &&
498            set_getbool(&a->set, "oauth")) {
[0dd6570]499                /* If this account supports OAuth, we're not logged in yet and
500                   not allowed to retry, assume there were auth issues. Give a
501                   helpful message on what might be necessary to fix this. */
[5ebff60]502                imcb_log(ic, "If you're having problems logging in, try re-requesting "
503                         "an OAuth token: account %s set password \"\"", a->tag);
[0dd6570]504        }
[5ebff60]505
506        for (l = bee->users; l; ) {
[81e04e1]507                bee_user_t *bu = l->data;
[eabc9d2]508                GSList *next = l->next;
[5ebff60]509
510                if (bu->ic == ic) {
511                        bee_user_free(bee, bu);
512                }
513
[eabc9d2]514                l = next;
[b7d3cc34]515        }
[5ebff60]516
517        b_event_remove(ic->keepalive);
[be7a180]518        ic->keepalive = 0;
[5ebff60]519        ic->acc->prpl->logout(ic);
520        b_event_remove(ic->inpa);
521
522        g_free(ic->away);
[be7a180]523        ic->away = NULL;
[5ebff60]524
525        query_del_by_conn((irc_t *) ic->bee->ui_data, ic);
526
527        if (!a) {
[b7d3cc34]528                /* Uhm... This is very sick. */
[5ebff60]529        } else if (allow_reconnect && set_getbool(&bee->set, "auto_reconnect") &&
530                   set_getbool(&a->set, "auto_reconnect") &&
531                   (delay = account_reconnect_delay(a)) > 0) {
532                imcb_log(ic, "Reconnecting in %d seconds..", delay);
533                a->reconnect = b_timeout_add(delay * 1000, auto_reconnect, a);
[b7d3cc34]534        }
[5ebff60]535
536        imc_free(ic);
[b7d3cc34]537}
538
[5ebff60]539void imcb_ask(struct im_connection *ic, char *msg, void *data,
540              query_callback doit, query_callback dont)
[b7d3cc34]541{
[5ebff60]542        query_add((irc_t *) ic->bee->ui_data, ic, msg, doit, dont, g_free, data);
[b7d3cc34]543}
544
[5ebff60]545void imcb_ask_with_free(struct im_connection *ic, char *msg, void *data,
546                        query_callback doit, query_callback dont, query_callback myfree)
[d0527c1]547{
[5ebff60]548        query_add((irc_t *) ic->bee->ui_data, ic, msg, doit, dont, myfree, data);
[d0527c1]549}
550
[5ebff60]551void imcb_add_buddy(struct im_connection *ic, const char *handle, const char *group)
[b7d3cc34]552{
[81e04e1]553        bee_user_t *bu;
554        bee_t *bee = ic->bee;
[8b61469]555        bee_group_t *oldg;
[5ebff60]556
557        if (!(bu = bee_user_by_handle(bee, ic, handle))) {
558                bu = bee_user_new(bee, ic, handle, 0);
559        }
560
[8b61469]561        oldg = bu->group;
[5ebff60]562        bu->group = bee_group_by_name(bee, group, TRUE);
563
564        if (bee->ui->user_group && bu->group != oldg) {
565                bee->ui->user_group(bee, bu);
566        }
[b7d3cc34]567}
568
[5ebff60]569void imcb_rename_buddy(struct im_connection *ic, const char *handle, const char *fullname)
[b7d3cc34]570{
[1d39159]571        bee_t *bee = ic->bee;
[5ebff60]572        bee_user_t *bu = bee_user_by_handle(bee, ic, handle);
573
574        if (!bu || !fullname) {
575                return;
576        }
577
578        if (!bu->fullname || strcmp(bu->fullname, fullname) != 0) {
579                g_free(bu->fullname);
580                bu->fullname = g_strdup(fullname);
581
582                if (bee->ui->user_fullname) {
583                        bee->ui->user_fullname(bee, bu);
584                }
[b7d3cc34]585        }
586}
587
[5ebff60]588void imcb_remove_buddy(struct im_connection *ic, const char *handle, char *group)
[998b103]589{
[5ebff60]590        bee_user_free(ic->bee, bee_user_by_handle(ic->bee, ic, handle));
[998b103]591}
592
[a42fda4]593/* Implements either imcb_buddy_nick_hint() or imcb_buddy_nick_change() depending on the value of 'change' */
594static void buddy_nick_hint_or_change(struct im_connection *ic, const char *handle, const char *nick, gboolean change)
[d06eabf]595{
[6ef9065]596        bee_t *bee = ic->bee;
[5ebff60]597        bee_user_t *bu = bee_user_by_handle(bee, ic, handle);
598
599        if (!bu || !nick) {
600                return;
601        }
602
603        g_free(bu->nick);
604        bu->nick = g_strdup(nick);
605
[a42fda4]606        if (change && bee->ui->user_nick_change) {
607                bee->ui->user_nick_change(bee, bu, nick);
608        } else if (!change && bee->ui->user_nick_hint) {
[5ebff60]609                bee->ui->user_nick_hint(bee, bu, nick);
610        }
[d06eabf]611}
[b7d3cc34]612
[a42fda4]613/* Soft variant, for newly created users. Does nothing if it's already online */
614void imcb_buddy_nick_hint(struct im_connection *ic, const char *handle, const char *nick)
615{
616        buddy_nick_hint_or_change(ic, handle, nick, FALSE);
617}
618
619/* Hard variant, always changes the nick */
620void imcb_buddy_nick_change(struct im_connection *ic, const char *handle, const char *nick)
621{
622        buddy_nick_hint_or_change(ic, handle, nick, TRUE);
623}
[b7d3cc34]624
[5ebff60]625struct imcb_ask_cb_data {
[0da65d5]626        struct im_connection *ic;
[7bf0f5f0]627        char *handle;
628};
629
[098a75b]630static void imcb_ask_cb_free(void *data)
631{
632        struct imcb_ask_cb_data *cbd = data;
633
634        g_free(cbd->handle);
635        g_free(cbd);
636}
637
[5ebff60]638static void imcb_ask_auth_cb_no(void *data)
[7bf0f5f0]639{
[fa295e36]640        struct imcb_ask_cb_data *cbd = data;
[5ebff60]641
642        cbd->ic->acc->prpl->auth_deny(cbd->ic, cbd->handle);
643
[098a75b]644        imcb_ask_cb_free(cbd);
[fa295e36]645}
646
[5ebff60]647static void imcb_ask_auth_cb_yes(void *data)
[fa295e36]648{
649        struct imcb_ask_cb_data *cbd = data;
[5ebff60]650
651        cbd->ic->acc->prpl->auth_allow(cbd->ic, cbd->handle);
652
[098a75b]653        imcb_ask_cb_free(cbd);
[fa295e36]654}
655
[5ebff60]656void imcb_ask_auth(struct im_connection *ic, const char *handle, const char *realname)
[fa295e36]657{
[5ebff60]658        struct imcb_ask_cb_data *data = g_new0(struct imcb_ask_cb_data, 1);
[fa295e36]659        char *s, *realname_ = NULL;
[5ebff60]660
661        if (realname != NULL) {
662                realname_ = g_strdup_printf(" (%s)", realname);
663        }
664
665        s = g_strdup_printf("The user %s%s wants to add you to his/her buddy list.",
666                            handle, realname_ ? realname_ : "");
667
668        g_free(realname_);
669
[fa295e36]670        data->ic = ic;
[5ebff60]671        data->handle = g_strdup(handle);
672        query_add((irc_t *) ic->bee->ui_data, ic, s,
[098a75b]673                  imcb_ask_auth_cb_yes, imcb_ask_auth_cb_no, imcb_ask_cb_free, data);
[fa295e36]674
[098a75b]675        g_free(s);
[7bf0f5f0]676}
677
[5ebff60]678static void imcb_ask_add_cb_yes(void *data)
[7bf0f5f0]679{
[fa295e36]680        struct imcb_ask_cb_data *cbd = data;
[5ebff60]681
682        cbd->ic->acc->prpl->add_buddy(cbd->ic, cbd->handle, NULL);
683
[098a75b]684        imcb_ask_cb_free(data);
[7bf0f5f0]685}
686
[5ebff60]687void imcb_ask_add(struct im_connection *ic, const char *handle, const char *realname)
[b7d3cc34]688{
[098a75b]689        struct imcb_ask_cb_data *data;
[7bf0f5f0]690        char *s;
[5ebff60]691
[7bf0f5f0]692        /* TODO: Make a setting for this! */
[5ebff60]693        if (bee_user_by_handle(ic->bee, ic, handle) != NULL) {
[7bf0f5f0]694                return;
[5ebff60]695        }
696
[098a75b]697        data = g_new0(struct imcb_ask_cb_data, 1);
698
[5ebff60]699        s = g_strdup_printf("The user %s is not in your buddy list yet. Do you want to add him/her now?", handle);
700
[0da65d5]701        data->ic = ic;
[5ebff60]702        data->handle = g_strdup(handle);
703        query_add((irc_t *) ic->bee->ui_data, ic, s,
[098a75b]704                  imcb_ask_add_cb_yes, imcb_ask_cb_free, imcb_ask_cb_free, data);
705
706        g_free(s);
[b7d3cc34]707}
708
[5ebff60]709struct bee_user *imcb_buddy_by_handle(struct im_connection *ic, const char *handle)
[81e04e1]710{
[5ebff60]711        return bee_user_by_handle(ic->bee, ic, handle);
[b7d3cc34]712}
713
[226fce1]714/* The plan is to not allow straight calls to prpl functions anymore, but do
715   them all from some wrappers. We'll start to define some down here: */
716
[5ebff60]717int imc_chat_msg(struct groupchat *c, char *msg, int flags)
[b7d3cc34]718{
[e27661d]719        char *buf = NULL;
[5ebff60]720
721        if ((c->ic->flags & OPT_DOES_HTML) && (g_strncasecmp(msg, "<html>", 6) != 0)) {
722                buf = escape_html(msg);
[b7d3cc34]723                msg = buf;
724        }
[5ebff60]725
726        c->ic->acc->prpl->chat_msg(c, msg, flags);
727        g_free(buf);
728
[0da65d5]729        return 1;
[b7d3cc34]730}
[226fce1]731
[5ebff60]732static char *imc_away_state_find(GList *gcm, char *away, char **message);
[226fce1]733
[5ebff60]734int imc_away_send_update(struct im_connection *ic)
[226fce1]735{
[3e1ef92c]736        char *away, *msg = NULL;
[5ebff60]737
738        if (ic->acc->prpl->away_states == NULL ||
739            ic->acc->prpl->set_away == NULL) {
[91cec2f]740                return 0;
[58adb7e]741        }
[5ebff60]742
743        away = set_getstr(&ic->acc->set, "away") ?
744               : set_getstr(&ic->bee->set, "away");
745        if (away && *away) {
[977a9d5]746                GList *m = ic->acc->prpl->away_states(ic);
[ff468a7]747                if (m == NULL) {
748                        return 0;
749                }
[5ebff60]750                msg = ic->acc->flags & ACC_FLAG_AWAY_MESSAGE ? away : NULL;
[ac68733a]751                away = imc_away_state_find(m, away, &msg) ? :
752                       (imc_away_state_find(m, "away", &msg) ? : m->data);
[5ebff60]753        } else if (ic->acc->flags & ACC_FLAG_STATUS_MESSAGE) {
[58adb7e]754                away = NULL;
[5ebff60]755                msg = set_getstr(&ic->acc->set, "status") ?
756                      : set_getstr(&ic->bee->set, "status");
[226fce1]757        }
[5ebff60]758
759        ic->acc->prpl->set_away(ic, away, msg);
760
[34fbbf9]761        return 1;
[226fce1]762}
763
[84b045d]764static char *imc_away_alias_list[8][5] =
[226fce1]765{
766        { "Away from computer", "Away", "Extended away", NULL },
767        { "NA", "N/A", "Not available", NULL },
768        { "Busy", "Do not disturb", "DND", "Occupied", NULL },
769        { "Be right back", "BRB", NULL },
770        { "On the phone", "Phone", "On phone", NULL },
771        { "Out to lunch", "Lunch", "Food", NULL },
772        { "Invisible", "Hidden" },
773        { NULL }
774};
775
[5ebff60]776static char *imc_away_state_find(GList *gcm, char *away, char **message)
[226fce1]777{
778        GList *m;
779        int i, j;
[5ebff60]780
781        for (m = gcm; m; m = m->next) {
782                if (g_strncasecmp(m->data, away, strlen(m->data)) == 0) {
[34fbbf9]783                        /* At least the Yahoo! module works better if message
784                           contains no data unless it adds something to what
785                           we have in state already. */
[5ebff60]786                        if (strlen(m->data) == strlen(away)) {
[34fbbf9]787                                *message = NULL;
[5ebff60]788                        }
789
[34fbbf9]790                        return m->data;
791                }
[5ebff60]792        }
793
794        for (i = 0; *imc_away_alias_list[i]; i++) {
[34fbbf9]795                int keep_message;
[5ebff60]796
797                for (j = 0; imc_away_alias_list[i][j]; j++) {
798                        if (g_strncasecmp(away, imc_away_alias_list[i][j], strlen(imc_away_alias_list[i][j])) == 0) {
799                                keep_message = strlen(away) != strlen(imc_away_alias_list[i][j]);
[226fce1]800                                break;
[34fbbf9]801                        }
[5ebff60]802                }
803
804                if (!imc_away_alias_list[i][j]) {       /* If we reach the end, this row */
805                        continue;                       /* is not what we want. Next!    */
806
807                }
[226fce1]808                /* Now find an entry in this row which exists in gcm */
[5ebff60]809                for (j = 0; imc_away_alias_list[i][j]; j++) {
810                        for (m = gcm; m; m = m->next) {
811                                if (g_strcasecmp(imc_away_alias_list[i][j], m->data) == 0) {
812                                        if (!keep_message) {
[34fbbf9]813                                                *message = NULL;
[5ebff60]814                                        }
815
[34fbbf9]816                                        return imc_away_alias_list[i][j];
817                                }
[5ebff60]818                        }
[226fce1]819                }
[5ebff60]820
[34fbbf9]821                /* No need to look further, apparently this state doesn't
822                   have any good alias for this protocol. */
823                break;
[226fce1]824        }
[5ebff60]825
[34fbbf9]826        return NULL;
[226fce1]827}
[da3b536]828
[5ebff60]829void imc_add_allow(struct im_connection *ic, char *handle)
[da3b536]830{
[5ebff60]831        if (g_slist_find_custom(ic->permit, handle, (GCompareFunc) ic->acc->prpl->handle_cmp) == NULL) {
832                ic->permit = g_slist_prepend(ic->permit, g_strdup(handle));
[da3b536]833        }
[5ebff60]834
835        ic->acc->prpl->add_permit(ic, handle);
[da3b536]836}
837
[5ebff60]838void imc_rem_allow(struct im_connection *ic, char *handle)
[da3b536]839{
840        GSList *l;
[5ebff60]841
842        if ((l = g_slist_find_custom(ic->permit, handle, (GCompareFunc) ic->acc->prpl->handle_cmp))) {
843                g_free(l->data);
844                ic->permit = g_slist_delete_link(ic->permit, l);
[da3b536]845        }
[5ebff60]846
847        ic->acc->prpl->rem_permit(ic, handle);
[da3b536]848}
849
[5ebff60]850void imc_add_block(struct im_connection *ic, char *handle)
[da3b536]851{
[5ebff60]852        if (g_slist_find_custom(ic->deny, handle, (GCompareFunc) ic->acc->prpl->handle_cmp) == NULL) {
853                ic->deny = g_slist_prepend(ic->deny, g_strdup(handle));
[da3b536]854        }
[5ebff60]855
856        ic->acc->prpl->add_deny(ic, handle);
[da3b536]857}
858
[5ebff60]859void imc_rem_block(struct im_connection *ic, char *handle)
[da3b536]860{
861        GSList *l;
[5ebff60]862
863        if ((l = g_slist_find_custom(ic->deny, handle, (GCompareFunc) ic->acc->prpl->handle_cmp))) {
864                g_free(l->data);
865                ic->deny = g_slist_delete_link(ic->deny, l);
[da3b536]866        }
[5ebff60]867
868        ic->acc->prpl->rem_deny(ic, handle);
[da3b536]869}
[85023c6]870
[d6e2aa8]871/* Deprecated: using this function resulted in merging several handles accidentally
872 * Also the irc layer handles this decently nowadays */
[5ebff60]873void imcb_clean_handle(struct im_connection *ic, char *handle)
[85023c6]874{
875}
Note: See TracBrowser for help on using the repository browser.