source: protocols/purple/purple.c @ 5d1b3a95

Last change on this file since 5d1b3a95 was 5d1b3a95, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-17T23:38:39Z

purple_conv_chat_invite_user() is libpurple >= 2.6.0, so use
serv_chat_invite() instead.

  • Property mode set to 100644
File size: 25.8 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        serv_chat_invite( purple_account_get_connection( gc->ic->proto_data ),
452                          purple_conv_chat_get_id( pcc ), 
453                          message && *message ? message : "Please join my chat",
454                          who );
455}
456
457void purple_chat_leave( struct groupchat *gc, char *who )
458{
459        PurpleConversation *pc = gc->data;
460       
461        purple_conversation_destroy( pc );
462}
463
464void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle );
465
466static void purple_ui_init();
467
468static PurpleCoreUiOps bee_core_uiops = 
469{
470        NULL,
471        NULL,
472        purple_ui_init,
473        NULL,
474};
475
476static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t step, size_t step_count )
477{
478        struct im_connection *ic = purple_ic_by_gc( gc );
479       
480        imcb_log( ic, "%s", text );
481}
482
483static void prplcb_conn_connected( PurpleConnection *gc )
484{
485        struct im_connection *ic = purple_ic_by_gc( gc );
486       
487        imcb_connected( ic );
488       
489        if( gc->flags & PURPLE_CONNECTION_HTML )
490                ic->flags |= OPT_DOES_HTML;
491}
492
493static void prplcb_conn_disconnected( PurpleConnection *gc )
494{
495        struct im_connection *ic = purple_ic_by_gc( gc );
496       
497        if( ic != NULL )
498        {
499                imc_logout( ic, TRUE );
500        }
501}
502
503static void prplcb_conn_notice( PurpleConnection *gc, const char *text )
504{
505        struct im_connection *ic = purple_ic_by_gc( gc );
506       
507        if( ic != NULL )
508                imcb_log( ic, "%s", text );
509}
510
511static void prplcb_conn_report_disconnect_reason( PurpleConnection *gc, PurpleConnectionError reason, const char *text )
512{
513        struct im_connection *ic = purple_ic_by_gc( gc );
514       
515        /* PURPLE_CONNECTION_ERROR_NAME_IN_USE means concurrent login,
516           should probably handle that. */
517        if( ic != NULL )
518                imcb_error( ic, "%s", text );
519}
520
521static PurpleConnectionUiOps bee_conn_uiops =
522{
523        prplcb_conn_progress,
524        prplcb_conn_connected,
525        prplcb_conn_disconnected,
526        prplcb_conn_notice,
527        NULL,
528        NULL,
529        NULL,
530        prplcb_conn_report_disconnect_reason,
531};
532
533static void prplcb_blist_new( PurpleBlistNode *node )
534{
535        PurpleBuddy *bud = (PurpleBuddy*) node;
536       
537        if( node->type == PURPLE_BLIST_BUDDY_NODE )
538        {
539                struct im_connection *ic = purple_ic_by_pa( bud->account );
540               
541                if( ic == NULL )
542                        return;
543               
544                imcb_add_buddy( ic, bud->name, NULL );
545                if( bud->server_alias )
546                {
547                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
548                        imcb_buddy_nick_hint( ic, bud->name, bud->server_alias );
549                }
550        }
551}
552
553static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node )
554{
555        PurpleBuddy *bud = (PurpleBuddy*) node;
556       
557        if( node->type == PURPLE_BLIST_BUDDY_NODE )
558        {
559                struct im_connection *ic = purple_ic_by_pa( bud->account );
560                PurpleStatus *as;
561                int flags = 0;
562               
563                if( ic == NULL )
564                        return;
565               
566                if( bud->server_alias )
567                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
568               
569                flags |= purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0;
570                flags |= purple_presence_is_available( bud->presence ) ? 0 : OPT_AWAY;
571               
572                as = purple_presence_get_active_status( bud->presence );
573               
574                imcb_buddy_status( ic, bud->name, flags, purple_status_get_name( as ),
575                                   purple_status_get_attr_string( as, "message" ) );
576        }
577}
578
579static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node )
580{
581        /*
582        PurpleBuddy *bud = (PurpleBuddy*) node;
583       
584        if( node->type == PURPLE_BLIST_BUDDY_NODE )
585        {
586                struct im_connection *ic = purple_ic_by_pa( bud->account );
587               
588                if( ic == NULL )
589                        return;
590               
591                imcb_remove_buddy( ic, bud->name, NULL );
592        }
593        */
594}
595
596static PurpleBlistUiOps bee_blist_uiops =
597{
598        NULL,
599        prplcb_blist_new,
600        NULL,
601        prplcb_blist_update,
602        prplcb_blist_remove,
603};
604
605void prplcb_conv_new( PurpleConversation *conv )
606{
607        if( conv->type == PURPLE_CONV_TYPE_CHAT )
608        {
609                struct im_connection *ic = purple_ic_by_pa( conv->account );
610                struct groupchat *gc;
611               
612                gc = imcb_chat_new( ic, conv->name );
613                conv->ui_data = gc;
614                gc->data = conv;
615        }
616}
617
618void prplcb_conv_free( PurpleConversation *conv )
619{
620        struct groupchat *gc = conv->ui_data;
621       
622        imcb_chat_free( gc );
623}
624
625void prplcb_conv_add_users( PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals )
626{
627        struct groupchat *gc = conv->ui_data;
628        GList *b;
629       
630        if( !gc->joined && strcmp( conv->account->protocol_id, "prpl-msn" ) == 0 )
631        {
632                /* Work around the broken MSN module which fucks up the user's
633                   handle completely when informing him/her that he just
634                   successfully joined the room s/he just created (v2.6.6). */
635                imcb_chat_add_buddy( gc, gc->ic->acc->user );
636        }
637       
638        for( b = cbuddies; b; b = b->next )
639        {
640                PurpleConvChatBuddy *pcb = b->data;
641               
642                imcb_chat_add_buddy( gc, pcb->name );
643        }
644}
645
646void prplcb_conv_del_users( PurpleConversation *conv, GList *cbuddies )
647{
648        struct groupchat *gc = conv->ui_data;
649        GList *b;
650       
651        for( b = cbuddies; b; b = b->next )
652                imcb_chat_remove_buddy( gc, b->data, "" );
653}
654
655void prplcb_conv_chat_msg( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime )
656{
657        struct groupchat *gc = conv->ui_data;
658        PurpleBuddy *buddy;
659       
660        /* ..._SEND means it's an outgoing message, no need to echo those. */
661        if( flags & PURPLE_MESSAGE_SEND )
662                return;
663       
664        buddy = purple_find_buddy( conv->account, who );
665        if( buddy != NULL )
666                who = purple_buddy_get_name( buddy );
667       
668        imcb_chat_msg( gc, who, (char*) message, 0, mtime );
669}
670
671static void prplcb_conv_im( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime )
672{
673        struct im_connection *ic = purple_ic_by_pa( conv->account );
674        PurpleBuddy *buddy;
675       
676        /* ..._SEND means it's an outgoing message, no need to echo those. */
677        if( flags & PURPLE_MESSAGE_SEND )
678                return;
679       
680        buddy = purple_find_buddy( conv->account, who );
681        if( buddy != NULL )
682                who = purple_buddy_get_name( buddy );
683       
684        imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime );
685}
686
687static PurpleConversationUiOps bee_conv_uiops = 
688{
689        prplcb_conv_new,           /* create_conversation  */
690        prplcb_conv_free,          /* destroy_conversation */
691        prplcb_conv_chat_msg,      /* write_chat           */
692        prplcb_conv_im,            /* write_im             */
693        NULL,                      /* write_conv           */
694        prplcb_conv_add_users,     /* chat_add_users       */
695        NULL,                      /* chat_rename_user     */
696        prplcb_conv_del_users,     /* chat_remove_users    */
697        NULL,                      /* chat_update_user     */
698        NULL,                      /* present              */
699        NULL,                      /* has_focus            */
700        NULL,                      /* custom_smiley_add    */
701        NULL,                      /* custom_smiley_write  */
702        NULL,                      /* custom_smiley_close  */
703        NULL,                      /* send_confirm         */
704};
705
706struct prplcb_request_action_data
707{
708        void *user_data, *bee_data;
709        PurpleRequestActionCb yes, no;
710        int yes_i, no_i;
711};
712
713static void prplcb_request_action_yes( void *data )
714{
715        struct prplcb_request_action_data *pqad = data;
716       
717        pqad->yes( pqad->user_data, pqad->yes_i );
718        g_free( pqad );
719}
720
721static void prplcb_request_action_no( void *data )
722{
723        struct prplcb_request_action_data *pqad = data;
724       
725        pqad->no( pqad->user_data, pqad->no_i );
726        g_free( pqad );
727}
728
729static void *prplcb_request_action( const char *title, const char *primary, const char *secondary,
730                                    int default_action, PurpleAccount *account, const char *who,
731                                    PurpleConversation *conv, void *user_data, size_t action_count,
732                                    va_list actions )
733{
734        struct prplcb_request_action_data *pqad; 
735        int i;
736        char *q;
737       
738        pqad = g_new0( struct prplcb_request_action_data, 1 );
739       
740        for( i = 0; i < action_count; i ++ )
741        {
742                char *caption;
743                void *fn;
744               
745                caption = va_arg( actions, char* );
746                fn = va_arg( actions, void* );
747               
748                if( strstr( caption, "Accept" ) )
749                {
750                        pqad->yes = fn;
751                        pqad->yes_i = i;
752                }
753                else if( strstr( caption, "Reject" ) || strstr( caption, "Cancel" ) )
754                {
755                        pqad->no = fn;
756                        pqad->no_i = i;
757                }
758        }
759       
760        pqad->user_data = user_data;
761       
762        q = g_strdup_printf( "Request: %s\n\n%s\n\n%s", title, primary, secondary );
763        pqad->bee_data = query_add( local_irc, purple_ic_by_pa( account ), q,
764                prplcb_request_action_yes, prplcb_request_action_no, pqad );
765       
766        g_free( q );
767       
768        return pqad;
769}
770
771static PurpleRequestUiOps bee_request_uiops =
772{
773        NULL,
774        NULL,
775        prplcb_request_action,
776        NULL,
777        NULL,
778        NULL,
779        NULL,
780};
781
782static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s )
783{
784        fprintf( stderr, "DEBUG %s: %s", category, arg_s );
785}
786
787static PurpleDebugUiOps bee_debug_uiops =
788{
789        prplcb_debug_print,
790};
791
792static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer udata )
793{
794        return b_timeout_add( interval, (b_event_handler) func, udata );
795}
796
797static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata )
798{
799        return b_input_add( fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata );
800}
801
802static gboolean prplcb_ev_remove( guint id )
803{
804        b_event_remove( (gint) id );
805        return TRUE;
806}
807
808static PurpleEventLoopUiOps glib_eventloops = 
809{
810        prplcb_ev_timeout_add,
811        prplcb_ev_remove,
812        prplcb_ev_input_add,
813        prplcb_ev_remove,
814};
815
816static void *prplcb_notify_email( PurpleConnection *gc, const char *subject, const char *from,
817                                  const char *to, const char *url )
818{
819        struct im_connection *ic = purple_ic_by_gc( gc );
820       
821        imcb_log( ic, "Received e-mail from %s for %s: %s <%s>", from, to, subject, url );
822       
823        return NULL;
824}
825
826static PurpleNotifyUiOps bee_notify_uiops =
827{
828        NULL,
829        prplcb_notify_email,
830};
831
832extern PurpleXferUiOps bee_xfer_uiops;
833
834static void purple_ui_init()
835{
836        purple_blist_set_ui_ops( &bee_blist_uiops );
837        purple_connections_set_ui_ops( &bee_conn_uiops );
838        purple_conversations_set_ui_ops( &bee_conv_uiops );
839        purple_request_set_ui_ops( &bee_request_uiops );
840        purple_notify_set_ui_ops( &bee_notify_uiops );
841        purple_xfers_set_ui_ops( &bee_xfer_uiops );
842        //purple_debug_set_ui_ops( &bee_debug_uiops );
843}
844
845void purple_initmodule()
846{
847        struct prpl funcs;
848        GList *prots;
849        GString *help;
850       
851        if( B_EV_IO_READ != PURPLE_INPUT_READ ||
852            B_EV_IO_WRITE != PURPLE_INPUT_WRITE )
853        {
854                /* FIXME FIXME FIXME FIXME FIXME :-) */
855                exit( 1 );
856        }
857       
858        purple_util_set_user_dir("/tmp");
859        purple_debug_set_enabled(FALSE);
860        purple_core_set_ui_ops(&bee_core_uiops);
861        purple_eventloop_set_ui_ops(&glib_eventloops);
862        if( !purple_core_init( "BitlBee") )
863        {
864                /* Initializing the core failed. Terminate. */
865                fprintf( stderr, "libpurple initialization failed.\n" );
866                abort();
867        }
868       
869        /* This seems like stateful shit we don't want... */
870        purple_set_blist(purple_blist_new());
871        purple_blist_load();
872       
873        /* Meh? */
874        purple_prefs_load();
875       
876        memset( &funcs, 0, sizeof( funcs ) );
877        funcs.login = purple_login;
878        funcs.init = purple_init;
879        funcs.logout = purple_logout;
880        funcs.buddy_msg = purple_buddy_msg;
881        funcs.away_states = purple_away_states;
882        funcs.set_away = purple_set_away;
883        funcs.add_buddy = purple_add_buddy;
884        funcs.remove_buddy = purple_remove_buddy;
885        funcs.keepalive = purple_keepalive;
886        funcs.send_typing = purple_send_typing;
887        funcs.handle_cmp = g_strcasecmp;
888        /* TODO(wilmer): Set these only for protocols that support them? */
889        funcs.chat_msg = purple_chat_msg;
890        funcs.chat_with = purple_chat_with;
891        funcs.chat_invite = purple_chat_invite;
892        funcs.chat_leave = purple_chat_leave;
893        funcs.transfer_request = purple_transfer_request;
894       
895        help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n");
896       
897        /* Add a protocol entry to BitlBee's structures for every protocol
898           supported by this libpurple instance. */     
899        for( prots = purple_plugins_get_protocols(); prots; prots = prots->next )
900        {
901                PurplePlugin *prot = prots->data;
902                struct prpl *ret;
903               
904                ret = g_memdup( &funcs, sizeof( funcs ) );
905                ret->name = ret->data = prot->info->id;
906                if( strncmp( ret->name, "prpl-", 5 ) == 0 )
907                        ret->name += 5;
908                register_protocol( ret );
909               
910                g_string_append_printf( help, "\n* %s (%s)", ret->name, prot->info->name );
911               
912                /* libpurple doesn't define a protocol called OSCAR, but we
913                   need it to be compatible with normal BitlBee. */
914                if( g_strcasecmp( prot->info->id, "prpl-aim" ) == 0 )
915                {
916                        ret = g_memdup( &funcs, sizeof( funcs ) );
917                        ret->name = "oscar";
918                        ret->data = prot->info->id;
919                        register_protocol( ret );
920                }
921        }
922       
923        g_string_append( help, "\n\nFor used protocols, more information about available "
924                         "settings can be found using \x02help purple <protocol name>\x02" );
925       
926        /* Add a simple dynamically-generated help item listing all
927           the supported protocols. */
928        help_add_mem( &global.help, "purple", help->str );
929        g_string_free( help, TRUE );
930}
Note: See TracBrowser for help on using the repository browser.