source: protocols/nogaim.c @ 7b05842

Last change on this file since 7b05842 was 0e788f5, checked in by Wilmer van der Gaast <wilmer@…>, at 2013-02-21T19:15:59Z

I'm still bored on a long flight. Wrote a script to automatically update
my copyright mentions since some were getting pretty stale. Left files not
touched since before 2012 alone so that this change doesn't touch almost
EVERY source file.

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