source: protocols/purple/purple.c @ 05a8932

Last change on this file since 05a8932 was 05a8932, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-22T12:21:27Z

Enable changing and viewing of block/allow lists.

  • Property mode set to 100644
File size: 29.7 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                                /* TODO: kv->value is not a char*, WTF? */
145                                if( strcmp( kv->value, kv->key ) != 0 )
146                                        g_string_append_printf( help, "%s (%s), ", (char*) kv->value, kv->key );
147                                else
148                                        g_string_append_printf( help, "%s, ", (char*) kv->value );
149                        }
150                        g_string_truncate( help, help->len - 2 );
151                        eval = set_eval_list;
152                        eval_data = opts;
153                       
154                        break;
155                       
156                default:
157                        irc_usermsg( acc->irc, "Setting with unknown type: %s (%d) Expect stuff to break..\n",
158                                     name, purple_account_option_get_type( o ) );
159                        name = NULL;
160                }
161               
162                if( name != NULL )
163                {
164                        s = set_add( &acc->set, name, def, eval, acc );
165                        s->flags |= ACC_SET_OFFLINE_ONLY;
166                        s->eval_data = eval_data;
167                        g_free( def );
168                }
169        }
170       
171        g_snprintf( help_title, sizeof( help_title ), "purple %s", (char*) acc->prpl->name );
172        help_add_mem( &global.help, help_title, help->str );
173        g_string_free( help, TRUE );
174       
175        if( pi->options & OPT_PROTO_MAIL_CHECK )
176        {
177                s = set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc );
178                s->flags |= ACC_SET_OFFLINE_ONLY;
179        }
180       
181        /* Go through all away states to figure out if away/status messages
182           are possible. */
183        pa = purple_account_new( acc->user, (char*) acc->prpl->data );
184        for( st = purple_account_get_status_types( pa ); st; st = st->next )
185        {
186                PurpleStatusPrimitive prim = purple_status_type_get_primitive( st->data );
187               
188                if( prim == PURPLE_STATUS_AVAILABLE )
189                {
190                        if( purple_status_type_get_attr( st->data, "message" ) )
191                                acc->flags |= ACC_FLAG_STATUS_MESSAGE;
192                }
193                else if( prim != PURPLE_STATUS_OFFLINE )
194                {
195                        if( purple_status_type_get_attr( st->data, "message" ) )
196                                acc->flags |= ACC_FLAG_AWAY_MESSAGE;
197                }
198        }
199        purple_accounts_remove( pa );
200}
201
202static void purple_sync_settings( account_t *acc, PurpleAccount *pa )
203{
204        PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id );
205        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
206        GList *i;
207       
208        for( i = pi->protocol_options; i; i = i->next )
209        {
210                PurpleAccountOption *o = i->data;
211                const char *name;
212                set_t *s;
213               
214                name = purple_account_option_get_setting( o );
215                s = set_find( &acc->set, name );
216                if( s->value == NULL )
217                        continue;
218               
219                switch( purple_account_option_get_type( o ) )
220                {
221                case PURPLE_PREF_STRING:
222                case PURPLE_PREF_STRING_LIST:
223                        purple_account_set_string( pa, name, set_getstr( &acc->set, name ) );
224                        break;
225               
226                case PURPLE_PREF_INT:
227                        purple_account_set_int( pa, name, set_getint( &acc->set, name ) );
228                        break;
229               
230                case PURPLE_PREF_BOOLEAN:
231                        purple_account_set_bool( pa, name, set_getbool( &acc->set, name ) );
232                        break;
233               
234                default:
235                        break;
236                }
237        }
238       
239        if( pi->options & OPT_PROTO_MAIL_CHECK )
240                purple_account_set_check_mail( pa, set_getbool( &acc->set, "mail_notifications" ) );
241}
242
243static void purple_login( account_t *acc )
244{
245        struct im_connection *ic = imcb_new( acc );
246        PurpleAccount *pa;
247       
248        if( local_irc != NULL && local_irc != acc->irc )
249        {
250                irc_usermsg( acc->irc, "Daemon mode detected. Do *not* try to use libpurple in daemon mode! "
251                                       "Please use inetd or ForkDaemon mode instead." );
252                return;
253        }
254        local_irc = acc->irc;
255       
256        /* For now this is needed in the _connected() handlers if using
257           GLib event handling, to make sure we're not handling events
258           on dead connections. */
259        purple_connections = g_slist_prepend( purple_connections, ic );
260       
261        ic->proto_data = pa = purple_account_new( acc->user, (char*) acc->prpl->data );
262        purple_account_set_password( pa, acc->pass );
263        purple_sync_settings( acc, pa );
264       
265        purple_account_set_enabled( pa, "BitlBee", TRUE );
266}
267
268static void purple_logout( struct im_connection *ic )
269{
270        PurpleAccount *pa = ic->proto_data;
271       
272        purple_account_set_enabled( pa, "BitlBee", FALSE );
273        purple_connections = g_slist_remove( purple_connections, ic );
274        purple_accounts_remove( pa );
275}
276
277static int purple_buddy_msg( struct im_connection *ic, char *who, char *message, int flags )
278{
279        PurpleConversation *conv;
280       
281        if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM,
282                                                            who, ic->proto_data ) ) == NULL )
283        {
284                conv = purple_conversation_new( PURPLE_CONV_TYPE_IM,
285                                                ic->proto_data, who );
286        }
287       
288        purple_conv_im_send( purple_conversation_get_im_data( conv ), message );
289       
290        return 1;
291}
292
293static GList *purple_away_states( struct im_connection *ic )
294{
295        PurpleAccount *pa = ic->proto_data;
296        GList *st, *ret = NULL;
297       
298        for( st = purple_account_get_status_types( pa ); st; st = st->next )
299        {
300                PurpleStatusPrimitive prim = purple_status_type_get_primitive( st->data );
301                if( prim != PURPLE_STATUS_AVAILABLE && prim != PURPLE_STATUS_OFFLINE )
302                        ret = g_list_append( ret, (void*) purple_status_type_get_name( st->data ) );
303        }
304       
305        return ret;
306}
307
308static void purple_set_away( struct im_connection *ic, char *state_txt, char *message )
309{
310        PurpleAccount *pa = ic->proto_data;
311        GList *status_types = purple_account_get_status_types( pa ), *st;
312        PurpleStatusType *pst = NULL;
313        GList *args = NULL;
314       
315        for( st = status_types; st; st = st->next )
316        {
317                pst = st->data;
318               
319                if( state_txt == NULL &&
320                    purple_status_type_get_primitive( pst ) == PURPLE_STATUS_AVAILABLE )
321                        break;
322
323                if( state_txt != NULL &&
324                    g_strcasecmp( state_txt, purple_status_type_get_name( pst ) ) == 0 )
325                        break;
326        }
327       
328        if( message && purple_status_type_get_attr( pst, "message" ) )
329        {
330                args = g_list_append( args, "message" );
331                args = g_list_append( args, message );
332        }
333       
334        purple_account_set_status_list( pa, st ? purple_status_type_get_id( pst ) : "away",
335                                        TRUE, args );
336
337        g_list_free( args );
338}
339
340static void purple_add_buddy( struct im_connection *ic, char *who, char *group )
341{
342        PurpleBuddy *pb;
343       
344        pb = purple_buddy_new( (PurpleAccount*) ic->proto_data, who, NULL );
345        purple_blist_add_buddy( pb, NULL, NULL, NULL );
346        purple_account_add_buddy( (PurpleAccount*) ic->proto_data, pb );
347}
348
349static void purple_remove_buddy( struct im_connection *ic, char *who, char *group )
350{
351        PurpleBuddy *pb;
352       
353        pb = purple_find_buddy( (PurpleAccount*) ic->proto_data, who );
354        if( pb != NULL )
355        {
356                purple_account_remove_buddy( (PurpleAccount*) ic->proto_data, pb, NULL );
357                purple_blist_remove_buddy( pb );
358        }
359}
360
361static void purple_add_permit( struct im_connection *ic, char *who )
362{
363        PurpleAccount *pa = ic->proto_data;
364       
365        purple_privacy_permit_add( pa, who, FALSE );
366}
367
368static void purple_add_deny( struct im_connection *ic, char *who )
369{
370        PurpleAccount *pa = ic->proto_data;
371       
372        purple_privacy_deny_add( pa, who, FALSE );
373}
374
375static void purple_rem_permit( struct im_connection *ic, char *who )
376{
377        PurpleAccount *pa = ic->proto_data;
378       
379        purple_privacy_permit_remove( pa, who, FALSE );
380}
381
382static void purple_rem_deny( struct im_connection *ic, char *who )
383{
384        PurpleAccount *pa = ic->proto_data;
385       
386        purple_privacy_deny_remove( pa, who, FALSE );
387}
388
389static void purple_get_info( struct im_connection *ic, char *who )
390{
391        serv_get_info( purple_account_get_connection( ic->proto_data ), who );
392}
393
394static void purple_keepalive( struct im_connection *ic )
395{
396}
397
398static int purple_send_typing( struct im_connection *ic, char *who, int flags )
399{
400        PurpleTypingState state = PURPLE_NOT_TYPING;
401        PurpleConversation *conv;
402       
403        if( flags & OPT_TYPING )
404                state = PURPLE_TYPING;
405        else if( flags & OPT_THINKING )
406                state = PURPLE_TYPED;
407       
408        if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM,
409                                                            who, ic->proto_data ) ) == NULL )
410        {
411                purple_conv_im_set_typing_state( purple_conversation_get_im_data( conv ), state );
412                return 1;
413        }
414        else
415        {
416                return 0;
417        }
418}
419
420static void purple_chat_msg( struct groupchat *gc, char *message, int flags )
421{
422        PurpleConversation *pc = gc->data;
423       
424        purple_conv_chat_send( purple_conversation_get_chat_data( pc ), message );
425}
426
427struct groupchat *purple_chat_with( struct im_connection *ic, char *who )
428{
429        /* No, "of course" this won't work this way. Or in fact, it almost
430           does, but it only lets you send msgs to it, you won't receive
431           any. Instead, we have to click the virtual menu item.
432        PurpleAccount *pa = ic->proto_data;
433        PurpleConversation *pc;
434        PurpleConvChat *pcc;
435        struct groupchat *gc;
436       
437        gc = imcb_chat_new( ic, "BitlBee-libpurple groupchat" );
438        gc->data = pc = purple_conversation_new( PURPLE_CONV_TYPE_CHAT, pa, "BitlBee-libpurple groupchat" );
439        pc->ui_data = gc;
440       
441        pcc = PURPLE_CONV_CHAT( pc );
442        purple_conv_chat_add_user( pcc, ic->acc->user, "", 0, TRUE );
443        purple_conv_chat_invite_user( pcc, who, "Please join my chat", FALSE );
444        //purple_conv_chat_add_user( pcc, who, "", 0, TRUE );
445        */
446       
447        /* There went my nice afternoon. :-( */
448       
449        PurpleAccount *pa = ic->proto_data;
450        PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id );
451        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
452        PurpleBuddy *pb = purple_find_buddy( (PurpleAccount*) ic->proto_data, who );
453        PurpleMenuAction *mi;
454        GList *menu;
455        void (*callback)(PurpleBlistNode *, gpointer); /* FFFFFFFFFFFFFUUUUUUUUUUUUUU */
456       
457        if( !pb || !pi || !pi->blist_node_menu )
458                return NULL;
459       
460        menu = pi->blist_node_menu( &pb->node );
461        while( menu )
462        {
463                mi = menu->data;
464                if( purple_menu_cmp( mi->label, "initiate chat" ) ||
465                    purple_menu_cmp( mi->label, "initiate conference" ) )
466                        break;
467                menu = menu->next;
468        }
469       
470        if( menu == NULL )
471                return NULL;
472       
473        /* Call the fucker. */
474        callback = (void*) mi->callback;
475        callback( &pb->node, menu->data );
476       
477        return NULL;
478}
479
480void purple_chat_invite( struct groupchat *gc, char *who, char *message )
481{
482        PurpleConversation *pc = gc->data;
483        PurpleConvChat *pcc = PURPLE_CONV_CHAT( pc );
484       
485        serv_chat_invite( purple_account_get_connection( gc->ic->proto_data ),
486                          purple_conv_chat_get_id( pcc ), 
487                          message && *message ? message : "Please join my chat",
488                          who );
489}
490
491void purple_chat_leave( struct groupchat *gc )
492{
493        PurpleConversation *pc = gc->data;
494       
495        purple_conversation_destroy( pc );
496}
497
498void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle );
499
500static void purple_ui_init();
501
502GHashTable *prplcb_ui_info()
503{
504        static GHashTable *ret;
505       
506        if( ret == NULL )
507        {
508                ret = g_hash_table_new(g_str_hash, g_str_equal);
509                g_hash_table_insert( ret, "name", "BitlBee" );
510                g_hash_table_insert( ret, "version", BITLBEE_VERSION );
511        }
512       
513        return ret;
514}
515
516static PurpleCoreUiOps bee_core_uiops = 
517{
518        NULL,
519        NULL,
520        purple_ui_init,
521        NULL,
522        prplcb_ui_info,
523};
524
525static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t step, size_t step_count )
526{
527        struct im_connection *ic = purple_ic_by_gc( gc );
528       
529        imcb_log( ic, "%s", text );
530}
531
532static void prplcb_conn_connected( PurpleConnection *gc )
533{
534        struct im_connection *ic = purple_ic_by_gc( gc );
535       
536        imcb_connected( ic );
537       
538        if( gc->flags & PURPLE_CONNECTION_HTML )
539                ic->flags |= OPT_DOES_HTML;
540}
541
542static void prplcb_conn_disconnected( PurpleConnection *gc )
543{
544        struct im_connection *ic = purple_ic_by_gc( gc );
545       
546        if( ic != NULL )
547        {
548                imc_logout( ic, !gc->wants_to_die );
549        }
550}
551
552static void prplcb_conn_notice( PurpleConnection *gc, const char *text )
553{
554        struct im_connection *ic = purple_ic_by_gc( gc );
555       
556        if( ic != NULL )
557                imcb_log( ic, "%s", text );
558}
559
560static void prplcb_conn_report_disconnect_reason( PurpleConnection *gc, PurpleConnectionError reason, const char *text )
561{
562        struct im_connection *ic = purple_ic_by_gc( gc );
563       
564        /* PURPLE_CONNECTION_ERROR_NAME_IN_USE means concurrent login,
565           should probably handle that. */
566        if( ic != NULL )
567                imcb_error( ic, "%s", text );
568}
569
570static PurpleConnectionUiOps bee_conn_uiops =
571{
572        prplcb_conn_progress,
573        prplcb_conn_connected,
574        prplcb_conn_disconnected,
575        prplcb_conn_notice,
576        NULL,
577        NULL,
578        NULL,
579        prplcb_conn_report_disconnect_reason,
580};
581
582static void prplcb_blist_new( PurpleBlistNode *node )
583{
584        PurpleBuddy *bud = (PurpleBuddy*) node;
585       
586        if( node->type == PURPLE_BLIST_BUDDY_NODE )
587        {
588                struct im_connection *ic = purple_ic_by_pa( bud->account );
589               
590                if( ic == NULL )
591                        return;
592               
593                imcb_add_buddy( ic, bud->name, NULL );
594                if( bud->server_alias )
595                {
596                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
597                        imcb_buddy_nick_hint( ic, bud->name, bud->server_alias );
598                }
599        }
600}
601
602static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node )
603{
604        PurpleBuddy *bud = (PurpleBuddy*) node;
605       
606        if( node->type == PURPLE_BLIST_BUDDY_NODE )
607        {
608                struct im_connection *ic = purple_ic_by_pa( bud->account );
609                PurpleStatus *as;
610                int flags = 0;
611               
612                if( ic == NULL )
613                        return;
614               
615                if( bud->server_alias )
616                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
617               
618                flags |= purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0;
619                flags |= purple_presence_is_available( bud->presence ) ? 0 : OPT_AWAY;
620               
621                as = purple_presence_get_active_status( bud->presence );
622               
623                imcb_buddy_status( ic, bud->name, flags, purple_status_get_name( as ),
624                                   purple_status_get_attr_string( as, "message" ) );
625        }
626}
627
628static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node )
629{
630        /*
631        PurpleBuddy *bud = (PurpleBuddy*) node;
632       
633        if( node->type == PURPLE_BLIST_BUDDY_NODE )
634        {
635                struct im_connection *ic = purple_ic_by_pa( bud->account );
636               
637                if( ic == NULL )
638                        return;
639               
640                imcb_remove_buddy( ic, bud->name, NULL );
641        }
642        */
643}
644
645static PurpleBlistUiOps bee_blist_uiops =
646{
647        NULL,
648        prplcb_blist_new,
649        NULL,
650        prplcb_blist_update,
651        prplcb_blist_remove,
652};
653
654void prplcb_conv_new( PurpleConversation *conv )
655{
656        if( conv->type == PURPLE_CONV_TYPE_CHAT )
657        {
658                struct im_connection *ic = purple_ic_by_pa( conv->account );
659                struct groupchat *gc;
660               
661                gc = imcb_chat_new( ic, conv->name );
662                conv->ui_data = gc;
663                gc->data = conv;
664        }
665}
666
667void prplcb_conv_free( PurpleConversation *conv )
668{
669        struct groupchat *gc = conv->ui_data;
670       
671        imcb_chat_free( gc );
672}
673
674void prplcb_conv_add_users( PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals )
675{
676        struct groupchat *gc = conv->ui_data;
677        GList *b;
678       
679        if( !gc->joined && strcmp( conv->account->protocol_id, "prpl-msn" ) == 0 )
680        {
681                /* Work around the broken MSN module which fucks up the user's
682                   handle completely when informing him/her that he just
683                   successfully joined the room s/he just created (v2.6.6). */
684                imcb_chat_add_buddy( gc, gc->ic->acc->user );
685        }
686       
687        for( b = cbuddies; b; b = b->next )
688        {
689                PurpleConvChatBuddy *pcb = b->data;
690               
691                imcb_chat_add_buddy( gc, pcb->name );
692        }
693}
694
695void prplcb_conv_del_users( PurpleConversation *conv, GList *cbuddies )
696{
697        struct groupchat *gc = conv->ui_data;
698        GList *b;
699       
700        for( b = cbuddies; b; b = b->next )
701                imcb_chat_remove_buddy( gc, b->data, "" );
702}
703
704void prplcb_conv_chat_msg( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime )
705{
706        struct groupchat *gc = conv->ui_data;
707        PurpleBuddy *buddy;
708       
709        /* ..._SEND means it's an outgoing message, no need to echo those. */
710        if( flags & PURPLE_MESSAGE_SEND )
711                return;
712       
713        buddy = purple_find_buddy( conv->account, who );
714        if( buddy != NULL )
715                who = purple_buddy_get_name( buddy );
716       
717        imcb_chat_msg( gc, who, (char*) message, 0, mtime );
718}
719
720static void prplcb_conv_im( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime )
721{
722        struct im_connection *ic = purple_ic_by_pa( conv->account );
723        PurpleBuddy *buddy;
724       
725        /* ..._SEND means it's an outgoing message, no need to echo those. */
726        if( flags & PURPLE_MESSAGE_SEND )
727                return;
728       
729        buddy = purple_find_buddy( conv->account, who );
730        if( buddy != NULL )
731                who = purple_buddy_get_name( buddy );
732       
733        imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime );
734}
735
736static PurpleConversationUiOps bee_conv_uiops = 
737{
738        prplcb_conv_new,           /* create_conversation  */
739        prplcb_conv_free,          /* destroy_conversation */
740        prplcb_conv_chat_msg,      /* write_chat           */
741        prplcb_conv_im,            /* write_im             */
742        NULL,                      /* write_conv           */
743        prplcb_conv_add_users,     /* chat_add_users       */
744        NULL,                      /* chat_rename_user     */
745        prplcb_conv_del_users,     /* chat_remove_users    */
746        NULL,                      /* chat_update_user     */
747        NULL,                      /* present              */
748        NULL,                      /* has_focus            */
749        NULL,                      /* custom_smiley_add    */
750        NULL,                      /* custom_smiley_write  */
751        NULL,                      /* custom_smiley_close  */
752        NULL,                      /* send_confirm         */
753};
754
755struct prplcb_request_action_data
756{
757        void *user_data, *bee_data;
758        PurpleRequestActionCb yes, no;
759        int yes_i, no_i;
760};
761
762static void prplcb_request_action_yes( void *data )
763{
764        struct prplcb_request_action_data *pqad = data;
765       
766        pqad->yes( pqad->user_data, pqad->yes_i );
767        g_free( pqad );
768}
769
770static void prplcb_request_action_no( void *data )
771{
772        struct prplcb_request_action_data *pqad = data;
773       
774        pqad->no( pqad->user_data, pqad->no_i );
775        g_free( pqad );
776}
777
778static void *prplcb_request_action( const char *title, const char *primary, const char *secondary,
779                                    int default_action, PurpleAccount *account, const char *who,
780                                    PurpleConversation *conv, void *user_data, size_t action_count,
781                                    va_list actions )
782{
783        struct prplcb_request_action_data *pqad; 
784        int i;
785        char *q;
786       
787        pqad = g_new0( struct prplcb_request_action_data, 1 );
788       
789        for( i = 0; i < action_count; i ++ )
790        {
791                char *caption;
792                void *fn;
793               
794                caption = va_arg( actions, char* );
795                fn = va_arg( actions, void* );
796               
797                if( strstr( caption, "Accept" ) )
798                {
799                        pqad->yes = fn;
800                        pqad->yes_i = i;
801                }
802                else if( strstr( caption, "Reject" ) || strstr( caption, "Cancel" ) )
803                {
804                        pqad->no = fn;
805                        pqad->no_i = i;
806                }
807        }
808       
809        pqad->user_data = user_data;
810       
811        q = g_strdup_printf( "Request: %s\n\n%s\n\n%s", title, primary, secondary );
812        pqad->bee_data = query_add( local_irc, purple_ic_by_pa( account ), q,
813                prplcb_request_action_yes, prplcb_request_action_no, pqad );
814       
815        g_free( q );
816       
817        return pqad;
818}
819
820static PurpleRequestUiOps bee_request_uiops =
821{
822        NULL,
823        NULL,
824        prplcb_request_action,
825        NULL,
826        NULL,
827        NULL,
828        NULL,
829};
830
831static void prplcb_privacy_permit_added( PurpleAccount *account, const char *name )
832{
833        struct im_connection *ic = purple_ic_by_pa( account );
834       
835        if( !g_slist_find_custom( ic->permit, name, (GCompareFunc) ic->acc->prpl->handle_cmp ) )
836                ic->permit = g_slist_prepend( ic->permit, g_strdup( name ) );
837}
838
839static void prplcb_privacy_permit_removed( PurpleAccount *account, const char *name )
840{
841        struct im_connection *ic = purple_ic_by_pa( account );
842        void *n;
843       
844        n = g_slist_find_custom( ic->permit, name, (GCompareFunc) ic->acc->prpl->handle_cmp );
845        ic->permit = g_slist_remove( ic->permit, n );
846}
847
848static void prplcb_privacy_deny_added( PurpleAccount *account, const char *name )
849{
850        struct im_connection *ic = purple_ic_by_pa( account );
851       
852        if( !g_slist_find_custom( ic->deny, name, (GCompareFunc) ic->acc->prpl->handle_cmp ) )
853                ic->deny = g_slist_prepend( ic->deny, g_strdup( name ) );
854}
855
856static void prplcb_privacy_deny_removed( PurpleAccount *account, const char *name )
857{
858        struct im_connection *ic = purple_ic_by_pa( account );
859        void *n;
860       
861        n = g_slist_find_custom( ic->deny, name, (GCompareFunc) ic->acc->prpl->handle_cmp );
862        ic->deny = g_slist_remove( ic->deny, n );
863}
864
865static PurplePrivacyUiOps bee_privacy_uiops =
866{
867        prplcb_privacy_permit_added,
868        prplcb_privacy_permit_removed,
869        prplcb_privacy_deny_added,
870        prplcb_privacy_deny_removed,
871};
872
873static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s )
874{
875        fprintf( stderr, "DEBUG %s: %s", category, arg_s );
876}
877
878static PurpleDebugUiOps bee_debug_uiops =
879{
880        prplcb_debug_print,
881};
882
883static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer udata )
884{
885        return b_timeout_add( interval, (b_event_handler) func, udata );
886}
887
888static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata )
889{
890        return b_input_add( fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata );
891}
892
893static gboolean prplcb_ev_remove( guint id )
894{
895        b_event_remove( (gint) id );
896        return TRUE;
897}
898
899static PurpleEventLoopUiOps glib_eventloops = 
900{
901        prplcb_ev_timeout_add,
902        prplcb_ev_remove,
903        prplcb_ev_input_add,
904        prplcb_ev_remove,
905};
906
907static void *prplcb_notify_email( PurpleConnection *gc, const char *subject, const char *from,
908                                  const char *to, const char *url )
909{
910        struct im_connection *ic = purple_ic_by_gc( gc );
911       
912        imcb_log( ic, "Received e-mail from %s for %s: %s <%s>", from, to, subject, url );
913       
914        return NULL;
915}
916
917static void *prplcb_notify_userinfo( PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info )
918{
919        struct im_connection *ic = purple_ic_by_gc( gc );
920        GString *info = g_string_new( "" );
921        GList *l = purple_notify_user_info_get_entries( user_info );
922        char *key;
923        const char *value;
924        int n;
925       
926        while( l )
927        {
928                PurpleNotifyUserInfoEntry *e = l->data;
929               
930                switch( purple_notify_user_info_entry_get_type( e ) )
931                {
932                case PURPLE_NOTIFY_USER_INFO_ENTRY_PAIR:
933                case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER:
934                        key = g_strdup( purple_notify_user_info_entry_get_label( e ) );
935                        value = purple_notify_user_info_entry_get_value( e );
936                       
937                        if( key )
938                        {
939                                strip_html( key );
940                                g_string_append_printf( info, "%s: ", key );
941                               
942                                if( value )
943                                {
944                                        n = strlen( value ) - 1;
945                                        while( isspace( value[n] ) )
946                                                n --;
947                                        g_string_append_len( info, value, n + 1 );
948                                }
949                                g_string_append_c( info, '\n' );
950                                g_free( key );
951                        }
952                       
953                        break;
954                case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK:
955                        g_string_append( info, "------------------------\n" );
956                        break;
957                }
958               
959                l = l->next;
960        }
961       
962        imcb_log( ic, "User %s info:\n%s", who, info->str );
963        g_string_free( info, TRUE );
964       
965        return NULL;
966}
967
968static PurpleNotifyUiOps bee_notify_uiops =
969{
970        NULL,
971        prplcb_notify_email,
972        NULL,
973        NULL,
974        NULL,
975        NULL,
976        prplcb_notify_userinfo,
977};
978
979extern PurpleXferUiOps bee_xfer_uiops;
980
981static void purple_ui_init()
982{
983        purple_blist_set_ui_ops( &bee_blist_uiops );
984        purple_connections_set_ui_ops( &bee_conn_uiops );
985        purple_conversations_set_ui_ops( &bee_conv_uiops );
986        purple_request_set_ui_ops( &bee_request_uiops );
987        purple_notify_set_ui_ops( &bee_notify_uiops );
988        purple_xfers_set_ui_ops( &bee_xfer_uiops );
989        purple_privacy_set_ui_ops( &bee_privacy_uiops );
990       
991        if( getenv( "BITLBEE_DEBUG" ) )
992                purple_debug_set_ui_ops( &bee_debug_uiops );
993}
994
995void purple_initmodule()
996{
997        struct prpl funcs;
998        GList *prots;
999        GString *help;
1000       
1001        if( B_EV_IO_READ != PURPLE_INPUT_READ ||
1002            B_EV_IO_WRITE != PURPLE_INPUT_WRITE )
1003        {
1004                /* FIXME FIXME FIXME FIXME FIXME :-) */
1005                exit( 1 );
1006        }
1007       
1008        purple_util_set_user_dir("/tmp");
1009        purple_debug_set_enabled(FALSE);
1010        purple_core_set_ui_ops(&bee_core_uiops);
1011        purple_eventloop_set_ui_ops(&glib_eventloops);
1012        if( !purple_core_init( "BitlBee") )
1013        {
1014                /* Initializing the core failed. Terminate. */
1015                fprintf( stderr, "libpurple initialization failed.\n" );
1016                abort();
1017        }
1018       
1019        /* This seems like stateful shit we don't want... */
1020        purple_set_blist(purple_blist_new());
1021        purple_blist_load();
1022       
1023        /* Meh? */
1024        purple_prefs_load();
1025       
1026        memset( &funcs, 0, sizeof( funcs ) );
1027        funcs.login = purple_login;
1028        funcs.init = purple_init;
1029        funcs.logout = purple_logout;
1030        funcs.buddy_msg = purple_buddy_msg;
1031        funcs.away_states = purple_away_states;
1032        funcs.set_away = purple_set_away;
1033        funcs.add_buddy = purple_add_buddy;
1034        funcs.remove_buddy = purple_remove_buddy;
1035        funcs.add_permit = purple_add_permit;
1036        funcs.add_deny = purple_add_deny;
1037        funcs.rem_permit = purple_rem_permit;
1038        funcs.rem_deny = purple_rem_deny;
1039        funcs.get_info = purple_get_info;
1040        funcs.keepalive = purple_keepalive;
1041        funcs.send_typing = purple_send_typing;
1042        funcs.handle_cmp = g_strcasecmp;
1043        /* TODO(wilmer): Set these only for protocols that support them? */
1044        funcs.chat_msg = purple_chat_msg;
1045        funcs.chat_with = purple_chat_with;
1046        funcs.chat_invite = purple_chat_invite;
1047        funcs.chat_leave = purple_chat_leave;
1048        funcs.transfer_request = purple_transfer_request;
1049       
1050        help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n");
1051       
1052        /* Add a protocol entry to BitlBee's structures for every protocol
1053           supported by this libpurple instance. */     
1054        for( prots = purple_plugins_get_protocols(); prots; prots = prots->next )
1055        {
1056                PurplePlugin *prot = prots->data;
1057                struct prpl *ret;
1058               
1059                ret = g_memdup( &funcs, sizeof( funcs ) );
1060                ret->name = ret->data = prot->info->id;
1061                if( strncmp( ret->name, "prpl-", 5 ) == 0 )
1062                        ret->name += 5;
1063                register_protocol( ret );
1064               
1065                g_string_append_printf( help, "\n* %s (%s)", ret->name, prot->info->name );
1066               
1067                /* libpurple doesn't define a protocol called OSCAR, but we
1068                   need it to be compatible with normal BitlBee. */
1069                if( g_strcasecmp( prot->info->id, "prpl-aim" ) == 0 )
1070                {
1071                        ret = g_memdup( &funcs, sizeof( funcs ) );
1072                        ret->name = "oscar";
1073                        ret->data = prot->info->id;
1074                        register_protocol( ret );
1075                }
1076        }
1077       
1078        g_string_append( help, "\n\nFor used protocols, more information about available "
1079                         "settings can be found using \x02help purple <protocol name>\x02" );
1080       
1081        /* Add a simple dynamically-generated help item listing all
1082           the supported protocols. */
1083        help_add_mem( &global.help, "purple", help->str );
1084        g_string_free( help, TRUE );
1085}
Note: See TracBrowser for help on using the repository browser.