source: protocols/purple/purple.c @ 0ac1a375

Last change on this file since 0ac1a375 was 0ac1a375, checked in by Wilmer van der Gaast <wilmer@…>, at 2009-11-25T00:19:45Z

Added enough code to handle one class of queries (action-based), enough
to make the "Please accept this SSL certificate" question work.

Need to extend the BitlBee API a bit to *really* support this well though
(yes/no is not enough).

  • Property mode set to 100644
File size: 15.9 KB
Line 
1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  libpurple module - Main file                                             *
5*                                                                           *
6*  Copyright 2009 Wilmer van der Gaast <wilmer@gaast.net>                   *
7*                                                                           *
8*  This program is free software; you can redistribute it and/or modify     *
9*  it under the terms of the GNU General Public License as published by     *
10*  the Free Software Foundation; either version 2 of the License, or        *
11*  (at your option) any later version.                                      *
12*                                                                           *
13*  This program is distributed in the hope that it will be useful,          *
14*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
15*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
16*  GNU General Public License for more details.                             *
17*                                                                           *
18*  You should have received a copy of the GNU General Public License along  *
19*  with this program; if not, write to the Free Software Foundation, Inc.,  *
20*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
21*                                                                           *
22\***************************************************************************/
23
24#include "bitlbee.h"
25
26#include <stdarg.h>
27
28#include <glib.h>
29#include <purple.h>
30
31GSList *purple_connections;
32
33/* This makes me VERY sad... :-( But some libpurple callbacks come in without
34   any context so this is the only way to get that. Don't want to support
35   libpurple in daemon mode anyway. */
36static irc_t *local_irc;
37
38static struct im_connection *purple_ic_by_pa( PurpleAccount *pa )
39{
40        GSList *i;
41       
42        for( i = purple_connections; i; i = i->next )
43                if( ((struct im_connection *)i->data)->proto_data == pa )
44                        return i->data;
45       
46        return NULL;
47}
48
49static struct im_connection *purple_ic_by_gc( PurpleConnection *gc )
50{
51        return purple_ic_by_pa( purple_connection_get_account( gc ) );
52}
53
54static void purple_init( account_t *acc )
55{
56        PurplePlugin *prpl = purple_plugins_find_with_id( (char*) acc->prpl->data );
57        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
58        GList *i;
59       
60        for( i = pi->protocol_options; i; i = i->next )
61        {
62                PurpleAccountOption *o = i->data;
63                const char *name;
64                char *def = NULL;
65                set_eval eval = NULL;
66                set_t *s;
67               
68                name = purple_account_option_get_setting( o );
69               
70                switch( purple_account_option_get_type( o ) )
71                {
72                case PURPLE_PREF_STRING:
73                        def = g_strdup( purple_account_option_get_default_string( o ) );
74                        break;
75               
76                case PURPLE_PREF_INT:
77                        def = g_strdup_printf( "%d", purple_account_option_get_default_int( o ) );
78                        eval = set_eval_int;
79                        break;
80               
81                case PURPLE_PREF_BOOLEAN:
82                        if( purple_account_option_get_default_bool( o ) )
83                                def = g_strdup( "true" );
84                        else
85                                def = g_strdup( "false" );
86                        eval = set_eval_bool;
87                        break;
88               
89                default:
90                        fprintf( stderr, "Setting with unknown type: %s (%d)\n", name, purple_account_option_get_type( o ) );
91                        name = NULL;
92                }
93               
94                if( name != NULL )
95                {
96                        s = set_add( &acc->set, name, def, eval, acc );
97                        s->flags |= ACC_SET_OFFLINE_ONLY;
98                        g_free( def );
99                }
100        }
101}
102
103static void purple_sync_settings( account_t *acc, PurpleAccount *pa )
104{
105        PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id );
106        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
107        GList *i;
108       
109        for( i = pi->protocol_options; i; i = i->next )
110        {
111                PurpleAccountOption *o = i->data;
112                const char *name;
113                set_t *s;
114               
115                name = purple_account_option_get_setting( o );
116                s = set_find( &acc->set, name );
117                if( s->value == NULL )
118                        continue;
119               
120                switch( purple_account_option_get_type( o ) )
121                {
122                case PURPLE_PREF_STRING:
123                        purple_account_set_string( pa, name, set_getstr( &acc->set, name ) );
124                        break;
125               
126                case PURPLE_PREF_INT:
127                        purple_account_set_int( pa, name, set_getint( &acc->set, name ) );
128                        break;
129               
130                case PURPLE_PREF_BOOLEAN:
131                        purple_account_set_bool( pa, name, set_getbool( &acc->set, name ) );
132                        break;
133               
134                default:
135                        break;
136                }
137        }
138}
139
140static void purple_login( account_t *acc )
141{
142        struct im_connection *ic = imcb_new( acc );
143        PurpleAccount *pa;
144       
145        if( local_irc != NULL && local_irc != acc->irc )
146        {
147                irc_usermsg( acc->irc, "Daemon mode detected. Do *not* try to use libpurple in daemon mode! "
148                                       "Please use inetd or ForkDaemon mode instead." );
149                return;
150        }
151        local_irc = acc->irc;
152       
153        /* For now this is needed in the _connected() handlers if using
154           GLib event handling, to make sure we're not handling events
155           on dead connections. */
156        purple_connections = g_slist_prepend( purple_connections, ic );
157       
158        ic->proto_data = pa = purple_account_new( acc->user, (char*) acc->prpl->data );
159        purple_account_set_password( pa, acc->pass );
160        purple_sync_settings( acc, pa );
161       
162        purple_account_set_enabled( pa, "BitlBee", TRUE );
163}
164
165static void purple_logout( struct im_connection *ic )
166{
167        PurpleAccount *pa = ic->proto_data;
168       
169        purple_account_set_enabled( pa, "BitlBee", FALSE );
170        purple_connections = g_slist_remove( purple_connections, ic );
171        purple_accounts_remove( pa );
172}
173
174static int purple_buddy_msg( struct im_connection *ic, char *who, char *message, int flags )
175{
176        PurpleConversation *conv;
177       
178        if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM,
179                                                            who, ic->proto_data ) ) == NULL )
180        {
181                conv = purple_conversation_new( PURPLE_CONV_TYPE_IM,
182                                                ic->proto_data, who );
183        }
184       
185        purple_conv_im_send( purple_conversation_get_im_data( conv ), message );
186       
187        return 1;
188}
189
190static GList *purple_away_states( struct im_connection *ic )
191{
192        PurpleAccount *pa = ic->proto_data;
193        GList *st, *ret = NULL;
194       
195        for( st = purple_account_get_status_types( pa ); st; st = st->next )
196                ret = g_list_append( ret, (void*) purple_status_type_get_name( st->data ) );
197       
198        return ret;
199}
200
201static void purple_set_away( struct im_connection *ic, char *state_txt, char *message )
202{
203        PurpleAccount *pa = ic->proto_data;
204        GList *status_types = purple_account_get_status_types( pa ), *st;
205        PurpleStatusType *pst = NULL;
206       
207        for( st = status_types; st; st = st->next )
208        {
209                pst = st->data;
210               
211                if( g_strcasecmp( state_txt, purple_status_type_get_name( pst ) ) == 0 )
212                        break;
213        }
214       
215        purple_account_set_status( pa, st ? purple_status_type_get_id( pst ) : "away",
216                                   TRUE, "message", message, NULL );
217}
218
219static void purple_add_buddy( struct im_connection *ic, char *who, char *group )
220{
221        PurpleBuddy *pb;
222       
223        pb = purple_buddy_new( (PurpleAccount*) ic->proto_data, who, NULL );
224        purple_blist_add_buddy( pb, NULL, NULL, NULL );
225        purple_account_add_buddy( (PurpleAccount*) ic->proto_data, pb );
226}
227
228static void purple_remove_buddy( struct im_connection *ic, char *who, char *group )
229{
230        PurpleBuddy *pb;
231       
232        pb = purple_find_buddy( (PurpleAccount*) ic->proto_data, who );
233        if( pb != NULL )
234        {
235                purple_account_remove_buddy( (PurpleAccount*) ic->proto_data, pb, NULL );
236                purple_blist_remove_buddy( pb );
237        }
238}
239
240static void purple_keepalive( struct im_connection *ic )
241{
242}
243
244static int purple_send_typing( struct im_connection *ic, char *who, int typing )
245{
246        return 1;
247}
248
249static void purple_ui_init();
250
251static PurpleCoreUiOps bee_core_uiops = 
252{
253        NULL,
254        NULL,
255        purple_ui_init,
256        NULL,
257};
258
259static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t step, size_t step_count )
260{
261        struct im_connection *ic = purple_ic_by_gc( gc );
262       
263        imcb_log( ic, "%s", text );
264}
265
266static void prplcb_conn_connected( PurpleConnection *gc )
267{
268        struct im_connection *ic = purple_ic_by_gc( gc );
269       
270        imcb_connected( ic );
271       
272        if( gc->flags & PURPLE_CONNECTION_HTML )
273                ic->flags |= OPT_DOES_HTML;
274}
275
276static void prplcb_conn_disconnected( PurpleConnection *gc )
277{
278        struct im_connection *ic = purple_ic_by_gc( gc );
279       
280        if( ic != NULL )
281        {
282                imc_logout( ic, TRUE );
283        }
284}
285
286static void prplcb_conn_notice( PurpleConnection *gc, const char *text )
287{
288        struct im_connection *ic = purple_ic_by_gc( gc );
289       
290        if( ic != NULL )
291                imcb_log( ic, "%s", text );
292}
293
294static void prplcb_conn_report_disconnect_reason( PurpleConnection *gc, PurpleConnectionError reason, const char *text )
295{
296        struct im_connection *ic = purple_ic_by_gc( gc );
297       
298        /* PURPLE_CONNECTION_ERROR_NAME_IN_USE means concurrent login,
299           should probably handle that. */
300        if( ic != NULL )
301                imcb_error( ic, "%s", text );
302}
303
304static PurpleConnectionUiOps bee_conn_uiops =
305{
306        prplcb_conn_progress,
307        prplcb_conn_connected,
308        prplcb_conn_disconnected,
309        prplcb_conn_notice,
310        NULL,
311        NULL,
312        NULL,
313        prplcb_conn_report_disconnect_reason,
314};
315
316static void prplcb_blist_new( PurpleBlistNode *node )
317{
318        PurpleBuddy *bud = (PurpleBuddy*) node;
319       
320        if( node->type == PURPLE_BLIST_BUDDY_NODE )
321        {
322                struct im_connection *ic = purple_ic_by_pa( bud->account );
323               
324                if( ic == NULL )
325                        return;
326               
327                imcb_add_buddy( ic, bud->name, NULL );
328                if( bud->server_alias )
329                {
330                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
331                        imcb_buddy_nick_hint( ic, bud->name, bud->server_alias );
332                }
333        }
334}
335
336static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node )
337{
338        PurpleBuddy *bud = (PurpleBuddy*) node;
339       
340        if( node->type == PURPLE_BLIST_BUDDY_NODE )
341        {
342                struct im_connection *ic = purple_ic_by_pa( bud->account );
343                PurpleStatus *as;
344                int flags = 0;
345               
346                if( ic == NULL )
347                        return;
348               
349                if( bud->server_alias )
350                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
351               
352                flags |= purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0;
353                flags |= purple_presence_is_available( bud->presence ) ? 0 : OPT_AWAY;
354               
355                as = purple_presence_get_active_status( bud->presence );
356               
357                imcb_buddy_status( ic, bud->name, flags, purple_status_get_name( as ),
358                                   purple_status_get_attr_string( as, "message" ) );
359        }
360}
361
362static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node )
363{
364        PurpleBuddy *bud = (PurpleBuddy*) node;
365       
366        /*
367        if( node->type == PURPLE_BLIST_BUDDY_NODE )
368        {
369                struct im_connection *ic = purple_ic_by_pa( bud->account );
370               
371                if( ic == NULL )
372                        return;
373               
374                imcb_remove_buddy( ic, bud->name, NULL );
375        }
376        */
377}
378
379static PurpleBlistUiOps bee_blist_uiops =
380{
381        NULL,
382        prplcb_blist_new,
383        NULL,
384        prplcb_blist_update,
385        prplcb_blist_remove,
386};
387
388static void prplcb_conv_im( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime )
389{
390        struct im_connection *ic = purple_ic_by_pa( conv->account );
391       
392        /* ..._SEND means it's an outgoing message, no need to echo those. */
393        if( !( flags & PURPLE_MESSAGE_SEND ) )
394                imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime );
395}
396
397static PurpleConversationUiOps bee_conv_uiops = 
398{
399        NULL,                      /* create_conversation  */
400        NULL,                      /* destroy_conversation */
401        NULL,                      /* write_chat           */
402        prplcb_conv_im,            /* write_im             */
403        NULL,                      /* write_conv           */
404        NULL,                      /* chat_add_users       */
405        NULL,                      /* chat_rename_user     */
406        NULL,                      /* chat_remove_users    */
407        NULL,                      /* chat_update_user     */
408        NULL,                      /* present              */
409        NULL,                      /* has_focus            */
410        NULL,                      /* custom_smiley_add    */
411        NULL,                      /* custom_smiley_write  */
412        NULL,                      /* custom_smiley_close  */
413        NULL,                      /* send_confirm         */
414};
415
416struct prplcb_request_action_data
417{
418        void *user_data, *bee_data;
419        PurpleRequestActionCb yes, no;
420        int yes_i, no_i;
421};
422
423static void prplcb_request_action_yes( void *data )
424{
425        struct prplcb_request_action_data *pqad = data;
426       
427        pqad->yes( pqad->user_data, pqad->yes_i );
428        g_free( pqad );
429}
430
431static void prplcb_request_action_no( void *data )
432{
433        struct prplcb_request_action_data *pqad = data;
434       
435        pqad->no( pqad->user_data, pqad->no_i );
436        g_free( pqad );
437}
438
439static void *prplcb_request_action( const char *title, const char *primary, const char *secondary,
440                                    int default_action, PurpleAccount *account, const char *who,
441                                    PurpleConversation *conv, void *user_data, size_t action_count,
442                                    va_list actions )
443{
444        struct prplcb_request_action_data *pqad; 
445        int i;
446        char *q;
447       
448        pqad = g_new0( struct prplcb_request_action_data, 1 );
449       
450        for( i = 0; i < action_count; i ++ )
451        {
452                char *caption;
453                void *fn;
454               
455                caption = va_arg( actions, char* );
456                fn = va_arg( actions, void* );
457               
458                if( strcmp( caption, "Accept" ) == 0 )
459                {
460                        pqad->yes = fn;
461                        pqad->yes_i = i;
462                }
463                else if( strcmp( caption, "Reject" ) == 0 )
464                {
465                        pqad->no = fn;
466                        pqad->no_i = i;
467                }
468        }
469       
470        pqad->user_data = user_data;
471       
472        q = g_strdup_printf( "Request: %s\n\n%s\n\n%s", title, primary, secondary );
473        pqad->bee_data = query_add( local_irc, purple_ic_by_pa( account ), q,
474                prplcb_request_action_yes, prplcb_request_action_no, pqad );
475       
476        g_free( q );
477}
478
479static PurpleRequestUiOps bee_request_uiops =
480{
481        NULL,
482        NULL,
483        prplcb_request_action,
484        NULL,
485        NULL,
486        NULL,
487        NULL,
488};
489
490static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s )
491{
492        fprintf( stderr, "DEBUG %s: %s", category, arg_s );
493}
494
495static PurpleDebugUiOps bee_debug_uiops =
496{
497        prplcb_debug_print,
498};
499
500static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer udata )
501{
502        return b_timeout_add( interval, (b_event_handler) func, udata );
503}
504
505static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata )
506{
507        return b_input_add( fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata );
508}
509
510static gboolean prplcb_ev_remove( guint id )
511{
512        b_event_remove( (gint) id );
513        return TRUE;
514}
515
516static PurpleEventLoopUiOps glib_eventloops = 
517{
518        prplcb_ev_timeout_add,
519        prplcb_ev_remove,
520        prplcb_ev_input_add,
521        prplcb_ev_remove,
522};
523
524static void purple_ui_init()
525{
526        purple_blist_set_ui_ops( &bee_blist_uiops );
527        purple_connections_set_ui_ops( &bee_conn_uiops );
528        purple_conversations_set_ui_ops( &bee_conv_uiops );
529        purple_request_set_ui_ops( &bee_request_uiops );
530        //purple_debug_set_ui_ops( &bee_debug_uiops );
531}
532
533void purple_initmodule()
534{
535        struct prpl funcs;
536        GList *prots;
537       
538        if( B_EV_IO_READ != PURPLE_INPUT_READ ||
539            B_EV_IO_WRITE != PURPLE_INPUT_WRITE )
540        {
541                /* FIXME FIXME FIXME FIXME FIXME :-) */
542                exit( 1 );
543        }
544       
545        purple_util_set_user_dir("/tmp");
546        purple_debug_set_enabled(FALSE);
547        purple_core_set_ui_ops(&bee_core_uiops);
548        purple_eventloop_set_ui_ops(&glib_eventloops);
549        if( !purple_core_init( "BitlBee") )
550        {
551                /* Initializing the core failed. Terminate. */
552                fprintf( stderr, "libpurple initialization failed.\n" );
553                abort();
554        }
555       
556        /* This seems like stateful shit we don't want... */
557        purple_set_blist(purple_blist_new());
558        purple_blist_load();
559       
560        /* Meh? */
561        purple_prefs_load();
562       
563        memset( &funcs, 0, sizeof( funcs ) );
564        funcs.login = purple_login;
565        funcs.init = purple_init;
566        funcs.logout = purple_logout;
567        funcs.buddy_msg = purple_buddy_msg;
568        funcs.away_states = purple_away_states;
569        funcs.set_away = purple_set_away;
570        funcs.add_buddy = purple_add_buddy;
571        funcs.remove_buddy = purple_remove_buddy;
572        funcs.keepalive = purple_keepalive;
573        funcs.send_typing = purple_send_typing;
574        funcs.handle_cmp = g_strcasecmp;
575       
576        for( prots = purple_plugins_get_protocols(); prots; prots = prots->next )
577        {
578                PurplePlugin *prot = prots->data;
579                struct prpl *ret;
580               
581                ret = g_memdup( &funcs, sizeof( funcs ) );
582                ret->name = ret->data = prot->info->id;
583                if( strncmp( ret->name, "prpl-", 5 ) == 0 )
584                        ret->name += 5;
585                register_protocol( ret );
586               
587                if( g_strcasecmp( prot->info->id, "prpl-aim" ) == 0 )
588                {
589                        ret = g_memdup( &funcs, sizeof( funcs ) );
590                        ret->name = "oscar";
591                        ret->data = prot->info->id;
592                        register_protocol( ret );
593                }
594        }
595}
Note: See TracBrowser for help on using the repository browser.