source: protocols/nogaim.c @ 4a9c6b0

Last change on this file since 4a9c6b0 was eb73d05, checked in by dequis <dx@…>, at 2017-05-07T19:06:29Z

Fix status message being set to null accidentally

In other words, "account jabber set away Message" resulted in away
status being set, but not "Message".

Fixes trac ticket 1291

This was broken by ac68733ad7a5127395d6367b655a2d9de5d911b5 (3.4.2)

That commit added another call of imc_away_state_find() with the fixed
string "away", so purple protocols can become away by setting away to
any string. But by doing so, it wiped the status message because it's
the same as the name of the away state.

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