source: protocols/purple/purple.c @ 8822d23

Last change on this file since 8822d23 was 2309152, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-17T00:14:14Z

Split off the file transfer stuff into a separate file. What a mess.

  • Property mode set to 100644
File size: 25.6 KB
Line 
1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  libpurple module - Main file                                             *
5*                                                                           *
6*  Copyright 2009-2010 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#include "help.h"
26
27#include <stdarg.h>
28
29#include <glib.h>
30#include <purple.h>
31
32GSList *purple_connections;
33
34/* This makes me VERY sad... :-( But some libpurple callbacks come in without
35   any context so this is the only way to get that. Don't want to support
36   libpurple in daemon mode anyway. */
37static irc_t *local_irc;
38
39struct im_connection *purple_ic_by_pa( PurpleAccount *pa )
40{
41        GSList *i;
42       
43        for( i = purple_connections; i; i = i->next )
44                if( ((struct im_connection *)i->data)->proto_data == pa )
45                        return i->data;
46       
47        return NULL;
48}
49
50static struct im_connection *purple_ic_by_gc( PurpleConnection *gc )
51{
52        return purple_ic_by_pa( purple_connection_get_account( gc ) );
53}
54
55static gboolean purple_menu_cmp( const char *a, const char *b )
56{
57        while( *a && *b )
58        {
59                while( *a == '_' ) a ++;
60                while( *b == '_' ) b ++;
61                if( tolower( *a ) != tolower( *b ) )
62                        return FALSE;
63               
64                a ++;
65                b ++;
66        }
67       
68        return ( *a == '\0' && *b == '\0' );
69}
70
71static void purple_init( account_t *acc )
72{
73        PurplePlugin *prpl = purple_plugins_find_with_id( (char*) acc->prpl->data );
74        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
75        PurpleAccount *pa;
76        GList *i, *st;
77        set_t *s;
78        char help_title[64];
79        GString *help;
80       
81        help = g_string_new( "" );
82        g_string_printf( help, "BitlBee libpurple module %s (%s).\n\nSupported settings:",
83                                (char*) acc->prpl->name, prpl->info->name );
84       
85        /* Convert all protocol_options into per-account setting variables. */
86        for( i = pi->protocol_options; i; i = i->next )
87        {
88                PurpleAccountOption *o = i->data;
89                const char *name;
90                char *def = NULL;
91                set_eval eval = NULL;
92                void *eval_data = NULL;
93                GList *io = NULL;
94                GSList *opts = NULL;
95               
96                name = purple_account_option_get_setting( o );
97               
98                switch( purple_account_option_get_type( o ) )
99                {
100                case PURPLE_PREF_STRING:
101                        def = g_strdup( purple_account_option_get_default_string( o ) );
102                       
103                        g_string_append_printf( help, "\n* %s (%s), %s, default: %s",
104                                                name, purple_account_option_get_text( o ),
105                                                "string", def );
106                       
107                        break;
108               
109                case PURPLE_PREF_INT:
110                        def = g_strdup_printf( "%d", purple_account_option_get_default_int( o ) );
111                        eval = set_eval_int;
112                       
113                        g_string_append_printf( help, "\n* %s (%s), %s, default: %s",
114                                                name, purple_account_option_get_text( o ),
115                                                "integer", def );
116                       
117                        break;
118               
119                case PURPLE_PREF_BOOLEAN:
120                        if( purple_account_option_get_default_bool( o ) )
121                                def = g_strdup( "true" );
122                        else
123                                def = g_strdup( "false" );
124                        eval = set_eval_bool;
125                       
126                        g_string_append_printf( help, "\n* %s (%s), %s, default: %s",
127                                                name, purple_account_option_get_text( o ),
128                                                "boolean", def );
129                       
130                        break;
131               
132                case PURPLE_PREF_STRING_LIST:
133                        def = g_strdup( purple_account_option_get_default_list_value( o ) );
134                       
135                        g_string_append_printf( help, "\n* %s (%s), %s, default: %s",
136                                                name, purple_account_option_get_text( o ),
137                                                "list", def );
138                        g_string_append( help, "\n  Possible values: " );
139                       
140                        for( io = purple_account_option_get_list( o ); io; io = io->next )
141                        {
142                                PurpleKeyValuePair *kv = io->data;
143                                opts = g_slist_append( opts, kv->value );
144                                if( strcmp( kv->value, kv->key ) != 0 )
145                                        g_string_append_printf( help, "%s (%s), ", kv->value, kv->key );
146                                else
147                                        g_string_append_printf( help, "%s, ", kv->value );
148                        }
149                        g_string_truncate( help, help->len - 2 );
150                        eval = set_eval_list;
151                        eval_data = opts;
152                       
153                        break;
154                       
155                default:
156                        irc_usermsg( acc->irc, "Setting with unknown type: %s (%d) Expect stuff to break..\n",
157                                     name, purple_account_option_get_type( o ) );
158                        name = NULL;
159                }
160               
161                if( name != NULL )
162                {
163                        s = set_add( &acc->set, name, def, eval, acc );
164                        s->flags |= ACC_SET_OFFLINE_ONLY;
165                        s->eval_data = eval_data;
166                        g_free( def );
167                }
168        }
169       
170        g_snprintf( help_title, sizeof( help_title ), "purple %s", (char*) acc->prpl->name );
171        help_add_mem( &global.help, help_title, help->str );
172        g_string_free( help, TRUE );
173       
174        if( pi->options & OPT_PROTO_MAIL_CHECK )
175        {
176                s = set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc );
177                s->flags |= ACC_SET_OFFLINE_ONLY;
178        }
179       
180        /* Go through all away states to figure out if away/status messages
181           are possible. */
182        pa = purple_account_new( acc->user, (char*) acc->prpl->data );
183        for( st = purple_account_get_status_types( pa ); st; st = st->next )
184        {
185                PurpleStatusPrimitive prim = purple_status_type_get_primitive( st->data );
186               
187                if( prim == PURPLE_STATUS_AVAILABLE )
188                {
189                        if( purple_status_type_get_attr( st->data, "message" ) )
190                                acc->flags |= ACC_FLAG_STATUS_MESSAGE;
191                }
192                else if( prim != PURPLE_STATUS_OFFLINE )
193                {
194                        if( purple_status_type_get_attr( st->data, "message" ) )
195                                acc->flags |= ACC_FLAG_AWAY_MESSAGE;
196                }
197        }
198        purple_accounts_remove( pa );
199}
200
201static void purple_sync_settings( account_t *acc, PurpleAccount *pa )
202{
203        PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id );
204        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
205        GList *i;
206       
207        for( i = pi->protocol_options; i; i = i->next )
208        {
209                PurpleAccountOption *o = i->data;
210                const char *name;
211                set_t *s;
212               
213                name = purple_account_option_get_setting( o );
214                s = set_find( &acc->set, name );
215                if( s->value == NULL )
216                        continue;
217               
218                switch( purple_account_option_get_type( o ) )
219                {
220                case PURPLE_PREF_STRING:
221                case PURPLE_PREF_STRING_LIST:
222                        purple_account_set_string( pa, name, set_getstr( &acc->set, name ) );
223                        break;
224               
225                case PURPLE_PREF_INT:
226                        purple_account_set_int( pa, name, set_getint( &acc->set, name ) );
227                        break;
228               
229                case PURPLE_PREF_BOOLEAN:
230                        purple_account_set_bool( pa, name, set_getbool( &acc->set, name ) );
231                        break;
232               
233                default:
234                        break;
235                }
236        }
237       
238        if( pi->options & OPT_PROTO_MAIL_CHECK )
239                purple_account_set_check_mail( pa, set_getbool( &acc->set, "mail_notifications" ) );
240}
241
242static void purple_login( account_t *acc )
243{
244        struct im_connection *ic = imcb_new( acc );
245        PurpleAccount *pa;
246       
247        if( local_irc != NULL && local_irc != acc->irc )
248        {
249                irc_usermsg( acc->irc, "Daemon mode detected. Do *not* try to use libpurple in daemon mode! "
250                                       "Please use inetd or ForkDaemon mode instead." );
251                return;
252        }
253        local_irc = acc->irc;
254       
255        /* For now this is needed in the _connected() handlers if using
256           GLib event handling, to make sure we're not handling events
257           on dead connections. */
258        purple_connections = g_slist_prepend( purple_connections, ic );
259       
260        ic->proto_data = pa = purple_account_new( acc->user, (char*) acc->prpl->data );
261        purple_account_set_password( pa, acc->pass );
262        purple_sync_settings( acc, pa );
263       
264        purple_account_set_enabled( pa, "BitlBee", TRUE );
265}
266
267static void purple_logout( struct im_connection *ic )
268{
269        PurpleAccount *pa = ic->proto_data;
270       
271        purple_account_set_enabled( pa, "BitlBee", FALSE );
272        purple_connections = g_slist_remove( purple_connections, ic );
273        purple_accounts_remove( pa );
274}
275
276static int purple_buddy_msg( struct im_connection *ic, char *who, char *message, int flags )
277{
278        PurpleConversation *conv;
279       
280        if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM,
281                                                            who, ic->proto_data ) ) == NULL )
282        {
283                conv = purple_conversation_new( PURPLE_CONV_TYPE_IM,
284                                                ic->proto_data, who );
285        }
286       
287        purple_conv_im_send( purple_conversation_get_im_data( conv ), message );
288       
289        return 1;
290}
291
292static GList *purple_away_states( struct im_connection *ic )
293{
294        PurpleAccount *pa = ic->proto_data;
295        GList *st, *ret = NULL;
296       
297        for( st = purple_account_get_status_types( pa ); st; st = st->next )
298        {
299                PurpleStatusPrimitive prim = purple_status_type_get_primitive( st->data );
300                if( prim != PURPLE_STATUS_AVAILABLE && prim != PURPLE_STATUS_OFFLINE )
301                        ret = g_list_append( ret, (void*) purple_status_type_get_name( st->data ) );
302        }
303       
304        return ret;
305}
306
307static void purple_set_away( struct im_connection *ic, char *state_txt, char *message )
308{
309        PurpleAccount *pa = ic->proto_data;
310        GList *status_types = purple_account_get_status_types( pa ), *st;
311        PurpleStatusType *pst = NULL;
312        GList *args = NULL;
313       
314        for( st = status_types; st; st = st->next )
315        {
316                pst = st->data;
317               
318                if( state_txt == NULL &&
319                    purple_status_type_get_primitive( pst ) == PURPLE_STATUS_AVAILABLE )
320                        break;
321
322                if( state_txt != NULL &&
323                    g_strcasecmp( state_txt, purple_status_type_get_name( pst ) ) == 0 )
324                        break;
325        }
326       
327        if( message && purple_status_type_get_attr( pst, "message" ) )
328        {
329                args = g_list_append( args, "message" );
330                args = g_list_append( args, message );
331        }
332       
333        purple_account_set_status_list( pa, st ? purple_status_type_get_id( pst ) : "away",
334                                        TRUE, args );
335
336        g_list_free( args );
337}
338
339static void purple_add_buddy( struct im_connection *ic, char *who, char *group )
340{
341        PurpleBuddy *pb;
342       
343        pb = purple_buddy_new( (PurpleAccount*) ic->proto_data, who, NULL );
344        purple_blist_add_buddy( pb, NULL, NULL, NULL );
345        purple_account_add_buddy( (PurpleAccount*) ic->proto_data, pb );
346}
347
348static void purple_remove_buddy( struct im_connection *ic, char *who, char *group )
349{
350        PurpleBuddy *pb;
351       
352        pb = purple_find_buddy( (PurpleAccount*) ic->proto_data, who );
353        if( pb != NULL )
354        {
355                purple_account_remove_buddy( (PurpleAccount*) ic->proto_data, pb, NULL );
356                purple_blist_remove_buddy( pb );
357        }
358}
359
360static void purple_keepalive( struct im_connection *ic )
361{
362}
363
364static int purple_send_typing( struct im_connection *ic, char *who, int flags )
365{
366        PurpleTypingState state = PURPLE_NOT_TYPING;
367        PurpleConversation *conv;
368       
369        if( flags & OPT_TYPING )
370                state = PURPLE_TYPING;
371        else if( flags & OPT_THINKING )
372                state = PURPLE_TYPED;
373       
374        if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM,
375                                                            who, ic->proto_data ) ) == NULL )
376        {
377                purple_conv_im_set_typing_state( purple_conversation_get_im_data( conv ), state );
378                return 1;
379        }
380        else
381        {
382                return 0;
383        }
384}
385
386static void purple_chat_msg( struct groupchat *gc, char *message, int flags )
387{
388        PurpleConversation *pc = gc->data;
389       
390        purple_conv_chat_send( purple_conversation_get_chat_data( pc ), message );
391}
392
393struct groupchat *purple_chat_with( struct im_connection *ic, char *who )
394{
395        /* No, "of course" this won't work this way. Or in fact, it almost
396           does, but it only lets you send msgs to it, you won't receive
397           any. Instead, we have to click the virtual menu item.
398        PurpleAccount *pa = ic->proto_data;
399        PurpleConversation *pc;
400        PurpleConvChat *pcc;
401        struct groupchat *gc;
402       
403        gc = imcb_chat_new( ic, "BitlBee-libpurple groupchat" );
404        gc->data = pc = purple_conversation_new( PURPLE_CONV_TYPE_CHAT, pa, "BitlBee-libpurple groupchat" );
405        pc->ui_data = gc;
406       
407        pcc = PURPLE_CONV_CHAT( pc );
408        purple_conv_chat_add_user( pcc, ic->acc->user, "", 0, TRUE );
409        purple_conv_chat_invite_user( pcc, who, "Please join my chat", FALSE );
410        //purple_conv_chat_add_user( pcc, who, "", 0, TRUE );
411        */
412       
413        /* There went my nice afternoon. :-( */
414       
415        PurpleAccount *pa = ic->proto_data;
416        PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id );
417        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
418        PurpleBuddy *pb = purple_find_buddy( (PurpleAccount*) ic->proto_data, who );
419        PurpleMenuAction *mi;
420        GList *menu;
421        void (*callback)(PurpleBlistNode *, gpointer); /* FFFFFFFFFFFFFUUUUUUUUUUUUUU */
422       
423        if( !pb || !pi || !pi->blist_node_menu )
424                return NULL;
425       
426        menu = pi->blist_node_menu( &pb->node );
427        while( menu )
428        {
429                mi = menu->data;
430                if( purple_menu_cmp( mi->label, "initiate chat" ) ||
431                    purple_menu_cmp( mi->label, "initiate conference" ) )
432                        break;
433                menu = menu->next;
434        }
435       
436        if( menu == NULL )
437                return NULL;
438       
439        /* Call the fucker. */
440        callback = (void*) mi->callback;
441        callback( &pb->node, menu->data );
442       
443        return NULL;
444}
445
446void purple_chat_invite( struct groupchat *gc, char *who, char *message )
447{
448        PurpleConversation *pc = gc->data;
449        PurpleConvChat *pcc = PURPLE_CONV_CHAT( pc );
450       
451        purple_conv_chat_invite_user( pcc, who, message && *message ? message : "Please join my chat", FALSE );
452}
453
454void purple_chat_leave( struct groupchat *gc, char *who )
455{
456        PurpleConversation *pc = gc->data;
457       
458        purple_conversation_destroy( pc );
459}
460
461void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle );
462
463static void purple_ui_init();
464
465static PurpleCoreUiOps bee_core_uiops = 
466{
467        NULL,
468        NULL,
469        purple_ui_init,
470        NULL,
471};
472
473static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t step, size_t step_count )
474{
475        struct im_connection *ic = purple_ic_by_gc( gc );
476       
477        imcb_log( ic, "%s", text );
478}
479
480static void prplcb_conn_connected( PurpleConnection *gc )
481{
482        struct im_connection *ic = purple_ic_by_gc( gc );
483       
484        imcb_connected( ic );
485       
486        if( gc->flags & PURPLE_CONNECTION_HTML )
487                ic->flags |= OPT_DOES_HTML;
488}
489
490static void prplcb_conn_disconnected( PurpleConnection *gc )
491{
492        struct im_connection *ic = purple_ic_by_gc( gc );
493       
494        if( ic != NULL )
495        {
496                imc_logout( ic, TRUE );
497        }
498}
499
500static void prplcb_conn_notice( PurpleConnection *gc, const char *text )
501{
502        struct im_connection *ic = purple_ic_by_gc( gc );
503       
504        if( ic != NULL )
505                imcb_log( ic, "%s", text );
506}
507
508static void prplcb_conn_report_disconnect_reason( PurpleConnection *gc, PurpleConnectionError reason, const char *text )
509{
510        struct im_connection *ic = purple_ic_by_gc( gc );
511       
512        /* PURPLE_CONNECTION_ERROR_NAME_IN_USE means concurrent login,
513           should probably handle that. */
514        if( ic != NULL )
515                imcb_error( ic, "%s", text );
516}
517
518static PurpleConnectionUiOps bee_conn_uiops =
519{
520        prplcb_conn_progress,
521        prplcb_conn_connected,
522        prplcb_conn_disconnected,
523        prplcb_conn_notice,
524        NULL,
525        NULL,
526        NULL,
527        prplcb_conn_report_disconnect_reason,
528};
529
530static void prplcb_blist_new( PurpleBlistNode *node )
531{
532        PurpleBuddy *bud = (PurpleBuddy*) node;
533       
534        if( node->type == PURPLE_BLIST_BUDDY_NODE )
535        {
536                struct im_connection *ic = purple_ic_by_pa( bud->account );
537               
538                if( ic == NULL )
539                        return;
540               
541                imcb_add_buddy( ic, bud->name, NULL );
542                if( bud->server_alias )
543                {
544                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
545                        imcb_buddy_nick_hint( ic, bud->name, bud->server_alias );
546                }
547        }
548}
549
550static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node )
551{
552        PurpleBuddy *bud = (PurpleBuddy*) node;
553       
554        if( node->type == PURPLE_BLIST_BUDDY_NODE )
555        {
556                struct im_connection *ic = purple_ic_by_pa( bud->account );
557                PurpleStatus *as;
558                int flags = 0;
559               
560                if( ic == NULL )
561                        return;
562               
563                if( bud->server_alias )
564                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
565               
566                flags |= purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0;
567                flags |= purple_presence_is_available( bud->presence ) ? 0 : OPT_AWAY;
568               
569                as = purple_presence_get_active_status( bud->presence );
570               
571                imcb_buddy_status( ic, bud->name, flags, purple_status_get_name( as ),
572                                   purple_status_get_attr_string( as, "message" ) );
573        }
574}
575
576static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node )
577{
578        /*
579        PurpleBuddy *bud = (PurpleBuddy*) node;
580       
581        if( node->type == PURPLE_BLIST_BUDDY_NODE )
582        {
583                struct im_connection *ic = purple_ic_by_pa( bud->account );
584               
585                if( ic == NULL )
586                        return;
587               
588                imcb_remove_buddy( ic, bud->name, NULL );
589        }
590        */
591}
592
593static PurpleBlistUiOps bee_blist_uiops =
594{
595        NULL,
596        prplcb_blist_new,
597        NULL,
598        prplcb_blist_update,
599        prplcb_blist_remove,
600};
601
602void prplcb_conv_new( PurpleConversation *conv )
603{
604        if( conv->type == PURPLE_CONV_TYPE_CHAT )
605        {
606                struct im_connection *ic = purple_ic_by_pa( conv->account );
607                struct groupchat *gc;
608               
609                gc = imcb_chat_new( ic, conv->name );
610                conv->ui_data = gc;
611                gc->data = conv;
612        }
613}
614
615void prplcb_conv_free( PurpleConversation *conv )
616{
617        struct groupchat *gc = conv->ui_data;
618       
619        imcb_chat_free( gc );
620}
621
622void prplcb_conv_add_users( PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals )
623{
624        struct groupchat *gc = conv->ui_data;
625        GList *b;
626       
627        if( !gc->joined && strcmp( conv->account->protocol_id, "prpl-msn" ) == 0 )
628        {
629                /* Work around the broken MSN module which fucks up the user's
630                   handle completely when informing him/her that he just
631                   successfully joined the room s/he just created (v2.6.6). */
632                imcb_chat_add_buddy( gc, gc->ic->acc->user );
633        }
634       
635        for( b = cbuddies; b; b = b->next )
636        {
637                PurpleConvChatBuddy *pcb = b->data;
638               
639                imcb_chat_add_buddy( gc, pcb->name );
640        }
641}
642
643void prplcb_conv_del_users( PurpleConversation *conv, GList *cbuddies )
644{
645        struct groupchat *gc = conv->ui_data;
646        GList *b;
647       
648        for( b = cbuddies; b; b = b->next )
649                imcb_chat_remove_buddy( gc, b->data, "" );
650}
651
652void prplcb_conv_chat_msg( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime )
653{
654        struct groupchat *gc = conv->ui_data;
655        PurpleBuddy *buddy;
656       
657        /* ..._SEND means it's an outgoing message, no need to echo those. */
658        if( flags & PURPLE_MESSAGE_SEND )
659                return;
660       
661        buddy = purple_find_buddy( conv->account, who );
662        if( buddy != NULL )
663                who = purple_buddy_get_name( buddy );
664       
665        imcb_chat_msg( gc, who, (char*) message, 0, mtime );
666}
667
668static void prplcb_conv_im( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime )
669{
670        struct im_connection *ic = purple_ic_by_pa( conv->account );
671        PurpleBuddy *buddy;
672       
673        /* ..._SEND means it's an outgoing message, no need to echo those. */
674        if( flags & PURPLE_MESSAGE_SEND )
675                return;
676       
677        buddy = purple_find_buddy( conv->account, who );
678        if( buddy != NULL )
679                who = purple_buddy_get_name( buddy );
680       
681        imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime );
682}
683
684static PurpleConversationUiOps bee_conv_uiops = 
685{
686        prplcb_conv_new,           /* create_conversation  */
687        prplcb_conv_free,          /* destroy_conversation */
688        prplcb_conv_chat_msg,      /* write_chat           */
689        prplcb_conv_im,            /* write_im             */
690        NULL,                      /* write_conv           */
691        prplcb_conv_add_users,     /* chat_add_users       */
692        NULL,                      /* chat_rename_user     */
693        prplcb_conv_del_users,     /* chat_remove_users    */
694        NULL,                      /* chat_update_user     */
695        NULL,                      /* present              */
696        NULL,                      /* has_focus            */
697        NULL,                      /* custom_smiley_add    */
698        NULL,                      /* custom_smiley_write  */
699        NULL,                      /* custom_smiley_close  */
700        NULL,                      /* send_confirm         */
701};
702
703struct prplcb_request_action_data
704{
705        void *user_data, *bee_data;
706        PurpleRequestActionCb yes, no;
707        int yes_i, no_i;
708};
709
710static void prplcb_request_action_yes( void *data )
711{
712        struct prplcb_request_action_data *pqad = data;
713       
714        pqad->yes( pqad->user_data, pqad->yes_i );
715        g_free( pqad );
716}
717
718static void prplcb_request_action_no( void *data )
719{
720        struct prplcb_request_action_data *pqad = data;
721       
722        pqad->no( pqad->user_data, pqad->no_i );
723        g_free( pqad );
724}
725
726static void *prplcb_request_action( const char *title, const char *primary, const char *secondary,
727                                    int default_action, PurpleAccount *account, const char *who,
728                                    PurpleConversation *conv, void *user_data, size_t action_count,
729                                    va_list actions )
730{
731        struct prplcb_request_action_data *pqad; 
732        int i;
733        char *q;
734       
735        pqad = g_new0( struct prplcb_request_action_data, 1 );
736       
737        for( i = 0; i < action_count; i ++ )
738        {
739                char *caption;
740                void *fn;
741               
742                caption = va_arg( actions, char* );
743                fn = va_arg( actions, void* );
744               
745                if( strstr( caption, "Accept" ) )
746                {
747                        pqad->yes = fn;
748                        pqad->yes_i = i;
749                }
750                else if( strstr( caption, "Reject" ) || strstr( caption, "Cancel" ) )
751                {
752                        pqad->no = fn;
753                        pqad->no_i = i;
754                }
755        }
756       
757        pqad->user_data = user_data;
758       
759        q = g_strdup_printf( "Request: %s\n\n%s\n\n%s", title, primary, secondary );
760        pqad->bee_data = query_add( local_irc, purple_ic_by_pa( account ), q,
761                prplcb_request_action_yes, prplcb_request_action_no, pqad );
762       
763        g_free( q );
764       
765        return pqad;
766}
767
768static PurpleRequestUiOps bee_request_uiops =
769{
770        NULL,
771        NULL,
772        prplcb_request_action,
773        NULL,
774        NULL,
775        NULL,
776        NULL,
777};
778
779static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s )
780{
781        fprintf( stderr, "DEBUG %s: %s", category, arg_s );
782}
783
784static PurpleDebugUiOps bee_debug_uiops =
785{
786        prplcb_debug_print,
787};
788
789static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer udata )
790{
791        return b_timeout_add( interval, (b_event_handler) func, udata );
792}
793
794static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata )
795{
796        return b_input_add( fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata );
797}
798
799static gboolean prplcb_ev_remove( guint id )
800{
801        b_event_remove( (gint) id );
802        return TRUE;
803}
804
805static PurpleEventLoopUiOps glib_eventloops = 
806{
807        prplcb_ev_timeout_add,
808        prplcb_ev_remove,
809        prplcb_ev_input_add,
810        prplcb_ev_remove,
811};
812
813static void *prplcb_notify_email( PurpleConnection *gc, const char *subject, const char *from,
814                                  const char *to, const char *url )
815{
816        struct im_connection *ic = purple_ic_by_gc( gc );
817       
818        imcb_log( ic, "Received e-mail from %s for %s: %s <%s>", from, to, subject, url );
819       
820        return NULL;
821}
822
823static PurpleNotifyUiOps bee_notify_uiops =
824{
825        NULL,
826        prplcb_notify_email,
827};
828
829extern PurpleXferUiOps bee_xfer_uiops;
830
831static void purple_ui_init()
832{
833        purple_blist_set_ui_ops( &bee_blist_uiops );
834        purple_connections_set_ui_ops( &bee_conn_uiops );
835        purple_conversations_set_ui_ops( &bee_conv_uiops );
836        purple_request_set_ui_ops( &bee_request_uiops );
837        purple_notify_set_ui_ops( &bee_notify_uiops );
838        purple_xfers_set_ui_ops( &bee_xfer_uiops );
839        //purple_debug_set_ui_ops( &bee_debug_uiops );
840}
841
842void purple_initmodule()
843{
844        struct prpl funcs;
845        GList *prots;
846        GString *help;
847       
848        if( B_EV_IO_READ != PURPLE_INPUT_READ ||
849            B_EV_IO_WRITE != PURPLE_INPUT_WRITE )
850        {
851                /* FIXME FIXME FIXME FIXME FIXME :-) */
852                exit( 1 );
853        }
854       
855        purple_util_set_user_dir("/tmp");
856        purple_debug_set_enabled(FALSE);
857        purple_core_set_ui_ops(&bee_core_uiops);
858        purple_eventloop_set_ui_ops(&glib_eventloops);
859        if( !purple_core_init( "BitlBee") )
860        {
861                /* Initializing the core failed. Terminate. */
862                fprintf( stderr, "libpurple initialization failed.\n" );
863                abort();
864        }
865       
866        /* This seems like stateful shit we don't want... */
867        purple_set_blist(purple_blist_new());
868        purple_blist_load();
869       
870        /* Meh? */
871        purple_prefs_load();
872       
873        memset( &funcs, 0, sizeof( funcs ) );
874        funcs.login = purple_login;
875        funcs.init = purple_init;
876        funcs.logout = purple_logout;
877        funcs.buddy_msg = purple_buddy_msg;
878        funcs.away_states = purple_away_states;
879        funcs.set_away = purple_set_away;
880        funcs.add_buddy = purple_add_buddy;
881        funcs.remove_buddy = purple_remove_buddy;
882        funcs.keepalive = purple_keepalive;
883        funcs.send_typing = purple_send_typing;
884        funcs.handle_cmp = g_strcasecmp;
885        /* TODO(wilmer): Set these only for protocols that support them? */
886        funcs.chat_msg = purple_chat_msg;
887        funcs.chat_with = purple_chat_with;
888        funcs.chat_invite = purple_chat_invite;
889        funcs.chat_leave = purple_chat_leave;
890        funcs.transfer_request = purple_transfer_request;
891       
892        help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n");
893       
894        /* Add a protocol entry to BitlBee's structures for every protocol
895           supported by this libpurple instance. */     
896        for( prots = purple_plugins_get_protocols(); prots; prots = prots->next )
897        {
898                PurplePlugin *prot = prots->data;
899                struct prpl *ret;
900               
901                ret = g_memdup( &funcs, sizeof( funcs ) );
902                ret->name = ret->data = prot->info->id;
903                if( strncmp( ret->name, "prpl-", 5 ) == 0 )
904                        ret->name += 5;
905                register_protocol( ret );
906               
907                g_string_append_printf( help, "\n* %s (%s)", ret->name, prot->info->name );
908               
909                /* libpurple doesn't define a protocol called OSCAR, but we
910                   need it to be compatible with normal BitlBee. */
911                if( g_strcasecmp( prot->info->id, "prpl-aim" ) == 0 )
912                {
913                        ret = g_memdup( &funcs, sizeof( funcs ) );
914                        ret->name = "oscar";
915                        ret->data = prot->info->id;
916                        register_protocol( ret );
917                }
918        }
919       
920        g_string_append( help, "\n\nFor used protocols, more information about available "
921                         "settings can be found using \x02help purple <protocol name>\x02" );
922       
923        /* Add a simple dynamically-generated help item listing all
924           the supported protocols. */
925        help_add_mem( &global.help, "purple", help->str );
926        g_string_free( help, TRUE );
927}
Note: See TracBrowser for help on using the repository browser.