source: protocols/nogaim.c @ 1239d05

Last change on this file since 1239d05 was 977a9d5, checked in by dequis <dx@…>, at 2015-12-09T07:58:59Z

Revert "imc_away_send_update: Fix leak of away_states linked list"

This reverts commit ed431c389887080dc4fa45e30d051ce733f4ce57.

I'm going to let this leak. Turns out only purple allocates an empty
list for every time this is called. Other protocols have statics, and
they always return the same thing, can't free those. Whatever. The
purple leak was insignificant, just more scratching of itches.

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