source: protocols/nogaim.c @ 9d6c0f2

Last change on this file since 9d6c0f2 was 9d6c0f2, checked in by jgeboski <jgeboski@…>, at 2016-05-15T16:16:37Z

Implemented ABI checking for external plugins

As of now, bitlbee will load any plugin regardless of the ABI it was
built against. This is really problematic when structures or symbols
are changed within bitlbee. This often leads to the plugin not loading
or the plugin acting in an undefined way. Typically a simple rebuild of
the plugin will resolve such issues, but many users have no idea that
this is required after they have updated bitlbee.

Each plugin must now have an init_plugin_abi() function along with its
init_plugin() function. This new function is called very early in the
plugin loading process to ensure the plugin actually matches the ABI of
bitlbee. The constant value of BITLBEE_ABI_VERSION_CODE is returned by
the function, which embeds the constant value into the plugin. This
value is then read by bitlbee upon every plugin load to ensure that the
proper ABI is use. If not, bitlbee will refuse to load the plugin.

The boiler-plate implementation of init_plugin_abi():

guint init_plugin_abi(void)
{

return BITLBEE_ABI_VERSION_CODE;

}

Third party plugins may also want to provide backwards compatibility
with older bitlbee versions by defining the missing version macro:

#ifndef BITLBEE_ABI_VERSION_CODE
#define BITLBEE_ABI_VERSION_CODE 0
#endif

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