source: protocols/purple/purple.c @ e77c264

Last change on this file since e77c264 was e77c264, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-22T00:58:59Z

Added support for the info command.

  • Property mode set to 100644
File size: 27.3 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_get_info( struct im_connection *ic, char *who )
362{
363        serv_get_info( purple_account_get_connection( ic->proto_data ), who );
364}
365
366static void purple_keepalive( struct im_connection *ic )
367{
368}
369
370static int purple_send_typing( struct im_connection *ic, char *who, int flags )
371{
372        PurpleTypingState state = PURPLE_NOT_TYPING;
373        PurpleConversation *conv;
374       
375        if( flags & OPT_TYPING )
376                state = PURPLE_TYPING;
377        else if( flags & OPT_THINKING )
378                state = PURPLE_TYPED;
379       
380        if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM,
381                                                            who, ic->proto_data ) ) == NULL )
382        {
383                purple_conv_im_set_typing_state( purple_conversation_get_im_data( conv ), state );
384                return 1;
385        }
386        else
387        {
388                return 0;
389        }
390}
391
392static void purple_chat_msg( struct groupchat *gc, char *message, int flags )
393{
394        PurpleConversation *pc = gc->data;
395       
396        purple_conv_chat_send( purple_conversation_get_chat_data( pc ), message );
397}
398
399struct groupchat *purple_chat_with( struct im_connection *ic, char *who )
400{
401        /* No, "of course" this won't work this way. Or in fact, it almost
402           does, but it only lets you send msgs to it, you won't receive
403           any. Instead, we have to click the virtual menu item.
404        PurpleAccount *pa = ic->proto_data;
405        PurpleConversation *pc;
406        PurpleConvChat *pcc;
407        struct groupchat *gc;
408       
409        gc = imcb_chat_new( ic, "BitlBee-libpurple groupchat" );
410        gc->data = pc = purple_conversation_new( PURPLE_CONV_TYPE_CHAT, pa, "BitlBee-libpurple groupchat" );
411        pc->ui_data = gc;
412       
413        pcc = PURPLE_CONV_CHAT( pc );
414        purple_conv_chat_add_user( pcc, ic->acc->user, "", 0, TRUE );
415        purple_conv_chat_invite_user( pcc, who, "Please join my chat", FALSE );
416        //purple_conv_chat_add_user( pcc, who, "", 0, TRUE );
417        */
418       
419        /* There went my nice afternoon. :-( */
420       
421        PurpleAccount *pa = ic->proto_data;
422        PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id );
423        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
424        PurpleBuddy *pb = purple_find_buddy( (PurpleAccount*) ic->proto_data, who );
425        PurpleMenuAction *mi;
426        GList *menu;
427        void (*callback)(PurpleBlistNode *, gpointer); /* FFFFFFFFFFFFFUUUUUUUUUUUUUU */
428       
429        if( !pb || !pi || !pi->blist_node_menu )
430                return NULL;
431       
432        menu = pi->blist_node_menu( &pb->node );
433        while( menu )
434        {
435                mi = menu->data;
436                if( purple_menu_cmp( mi->label, "initiate chat" ) ||
437                    purple_menu_cmp( mi->label, "initiate conference" ) )
438                        break;
439                menu = menu->next;
440        }
441       
442        if( menu == NULL )
443                return NULL;
444       
445        /* Call the fucker. */
446        callback = (void*) mi->callback;
447        callback( &pb->node, menu->data );
448       
449        return NULL;
450}
451
452void purple_chat_invite( struct groupchat *gc, char *who, char *message )
453{
454        PurpleConversation *pc = gc->data;
455        PurpleConvChat *pcc = PURPLE_CONV_CHAT( pc );
456       
457        serv_chat_invite( purple_account_get_connection( gc->ic->proto_data ),
458                          purple_conv_chat_get_id( pcc ), 
459                          message && *message ? message : "Please join my chat",
460                          who );
461}
462
463void purple_chat_leave( struct groupchat *gc )
464{
465        PurpleConversation *pc = gc->data;
466       
467        purple_conversation_destroy( pc );
468}
469
470void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle );
471
472static void purple_ui_init();
473
474static PurpleCoreUiOps bee_core_uiops = 
475{
476        NULL,
477        NULL,
478        purple_ui_init,
479        NULL,
480};
481
482static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t step, size_t step_count )
483{
484        struct im_connection *ic = purple_ic_by_gc( gc );
485       
486        imcb_log( ic, "%s", text );
487}
488
489static void prplcb_conn_connected( PurpleConnection *gc )
490{
491        struct im_connection *ic = purple_ic_by_gc( gc );
492       
493        imcb_connected( ic );
494       
495        if( gc->flags & PURPLE_CONNECTION_HTML )
496                ic->flags |= OPT_DOES_HTML;
497}
498
499static void prplcb_conn_disconnected( PurpleConnection *gc )
500{
501        struct im_connection *ic = purple_ic_by_gc( gc );
502       
503        if( ic != NULL )
504        {
505                imc_logout( ic, !gc->wants_to_die );
506        }
507}
508
509static void prplcb_conn_notice( PurpleConnection *gc, const char *text )
510{
511        struct im_connection *ic = purple_ic_by_gc( gc );
512       
513        if( ic != NULL )
514                imcb_log( ic, "%s", text );
515}
516
517static void prplcb_conn_report_disconnect_reason( PurpleConnection *gc, PurpleConnectionError reason, const char *text )
518{
519        struct im_connection *ic = purple_ic_by_gc( gc );
520       
521        /* PURPLE_CONNECTION_ERROR_NAME_IN_USE means concurrent login,
522           should probably handle that. */
523        if( ic != NULL )
524                imcb_error( ic, "%s", text );
525}
526
527static PurpleConnectionUiOps bee_conn_uiops =
528{
529        prplcb_conn_progress,
530        prplcb_conn_connected,
531        prplcb_conn_disconnected,
532        prplcb_conn_notice,
533        NULL,
534        NULL,
535        NULL,
536        prplcb_conn_report_disconnect_reason,
537};
538
539static void prplcb_blist_new( PurpleBlistNode *node )
540{
541        PurpleBuddy *bud = (PurpleBuddy*) node;
542       
543        if( node->type == PURPLE_BLIST_BUDDY_NODE )
544        {
545                struct im_connection *ic = purple_ic_by_pa( bud->account );
546               
547                if( ic == NULL )
548                        return;
549               
550                imcb_add_buddy( ic, bud->name, NULL );
551                if( bud->server_alias )
552                {
553                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
554                        imcb_buddy_nick_hint( ic, bud->name, bud->server_alias );
555                }
556        }
557}
558
559static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node )
560{
561        PurpleBuddy *bud = (PurpleBuddy*) node;
562       
563        if( node->type == PURPLE_BLIST_BUDDY_NODE )
564        {
565                struct im_connection *ic = purple_ic_by_pa( bud->account );
566                PurpleStatus *as;
567                int flags = 0;
568               
569                if( ic == NULL )
570                        return;
571               
572                if( bud->server_alias )
573                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
574               
575                flags |= purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0;
576                flags |= purple_presence_is_available( bud->presence ) ? 0 : OPT_AWAY;
577               
578                as = purple_presence_get_active_status( bud->presence );
579               
580                imcb_buddy_status( ic, bud->name, flags, purple_status_get_name( as ),
581                                   purple_status_get_attr_string( as, "message" ) );
582        }
583}
584
585static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node )
586{
587        /*
588        PurpleBuddy *bud = (PurpleBuddy*) node;
589       
590        if( node->type == PURPLE_BLIST_BUDDY_NODE )
591        {
592                struct im_connection *ic = purple_ic_by_pa( bud->account );
593               
594                if( ic == NULL )
595                        return;
596               
597                imcb_remove_buddy( ic, bud->name, NULL );
598        }
599        */
600}
601
602static PurpleBlistUiOps bee_blist_uiops =
603{
604        NULL,
605        prplcb_blist_new,
606        NULL,
607        prplcb_blist_update,
608        prplcb_blist_remove,
609};
610
611void prplcb_conv_new( PurpleConversation *conv )
612{
613        if( conv->type == PURPLE_CONV_TYPE_CHAT )
614        {
615                struct im_connection *ic = purple_ic_by_pa( conv->account );
616                struct groupchat *gc;
617               
618                gc = imcb_chat_new( ic, conv->name );
619                conv->ui_data = gc;
620                gc->data = conv;
621        }
622}
623
624void prplcb_conv_free( PurpleConversation *conv )
625{
626        struct groupchat *gc = conv->ui_data;
627       
628        imcb_chat_free( gc );
629}
630
631void prplcb_conv_add_users( PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals )
632{
633        struct groupchat *gc = conv->ui_data;
634        GList *b;
635       
636        if( !gc->joined && strcmp( conv->account->protocol_id, "prpl-msn" ) == 0 )
637        {
638                /* Work around the broken MSN module which fucks up the user's
639                   handle completely when informing him/her that he just
640                   successfully joined the room s/he just created (v2.6.6). */
641                imcb_chat_add_buddy( gc, gc->ic->acc->user );
642        }
643       
644        for( b = cbuddies; b; b = b->next )
645        {
646                PurpleConvChatBuddy *pcb = b->data;
647               
648                imcb_chat_add_buddy( gc, pcb->name );
649        }
650}
651
652void prplcb_conv_del_users( PurpleConversation *conv, GList *cbuddies )
653{
654        struct groupchat *gc = conv->ui_data;
655        GList *b;
656       
657        for( b = cbuddies; b; b = b->next )
658                imcb_chat_remove_buddy( gc, b->data, "" );
659}
660
661void prplcb_conv_chat_msg( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime )
662{
663        struct groupchat *gc = conv->ui_data;
664        PurpleBuddy *buddy;
665       
666        /* ..._SEND means it's an outgoing message, no need to echo those. */
667        if( flags & PURPLE_MESSAGE_SEND )
668                return;
669       
670        buddy = purple_find_buddy( conv->account, who );
671        if( buddy != NULL )
672                who = purple_buddy_get_name( buddy );
673       
674        imcb_chat_msg( gc, who, (char*) message, 0, mtime );
675}
676
677static void prplcb_conv_im( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime )
678{
679        struct im_connection *ic = purple_ic_by_pa( conv->account );
680        PurpleBuddy *buddy;
681       
682        /* ..._SEND means it's an outgoing message, no need to echo those. */
683        if( flags & PURPLE_MESSAGE_SEND )
684                return;
685       
686        buddy = purple_find_buddy( conv->account, who );
687        if( buddy != NULL )
688                who = purple_buddy_get_name( buddy );
689       
690        imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime );
691}
692
693static PurpleConversationUiOps bee_conv_uiops = 
694{
695        prplcb_conv_new,           /* create_conversation  */
696        prplcb_conv_free,          /* destroy_conversation */
697        prplcb_conv_chat_msg,      /* write_chat           */
698        prplcb_conv_im,            /* write_im             */
699        NULL,                      /* write_conv           */
700        prplcb_conv_add_users,     /* chat_add_users       */
701        NULL,                      /* chat_rename_user     */
702        prplcb_conv_del_users,     /* chat_remove_users    */
703        NULL,                      /* chat_update_user     */
704        NULL,                      /* present              */
705        NULL,                      /* has_focus            */
706        NULL,                      /* custom_smiley_add    */
707        NULL,                      /* custom_smiley_write  */
708        NULL,                      /* custom_smiley_close  */
709        NULL,                      /* send_confirm         */
710};
711
712struct prplcb_request_action_data
713{
714        void *user_data, *bee_data;
715        PurpleRequestActionCb yes, no;
716        int yes_i, no_i;
717};
718
719static void prplcb_request_action_yes( void *data )
720{
721        struct prplcb_request_action_data *pqad = data;
722       
723        pqad->yes( pqad->user_data, pqad->yes_i );
724        g_free( pqad );
725}
726
727static void prplcb_request_action_no( void *data )
728{
729        struct prplcb_request_action_data *pqad = data;
730       
731        pqad->no( pqad->user_data, pqad->no_i );
732        g_free( pqad );
733}
734
735static void *prplcb_request_action( const char *title, const char *primary, const char *secondary,
736                                    int default_action, PurpleAccount *account, const char *who,
737                                    PurpleConversation *conv, void *user_data, size_t action_count,
738                                    va_list actions )
739{
740        struct prplcb_request_action_data *pqad; 
741        int i;
742        char *q;
743       
744        pqad = g_new0( struct prplcb_request_action_data, 1 );
745       
746        for( i = 0; i < action_count; i ++ )
747        {
748                char *caption;
749                void *fn;
750               
751                caption = va_arg( actions, char* );
752                fn = va_arg( actions, void* );
753               
754                if( strstr( caption, "Accept" ) )
755                {
756                        pqad->yes = fn;
757                        pqad->yes_i = i;
758                }
759                else if( strstr( caption, "Reject" ) || strstr( caption, "Cancel" ) )
760                {
761                        pqad->no = fn;
762                        pqad->no_i = i;
763                }
764        }
765       
766        pqad->user_data = user_data;
767       
768        q = g_strdup_printf( "Request: %s\n\n%s\n\n%s", title, primary, secondary );
769        pqad->bee_data = query_add( local_irc, purple_ic_by_pa( account ), q,
770                prplcb_request_action_yes, prplcb_request_action_no, pqad );
771       
772        g_free( q );
773       
774        return pqad;
775}
776
777static PurpleRequestUiOps bee_request_uiops =
778{
779        NULL,
780        NULL,
781        prplcb_request_action,
782        NULL,
783        NULL,
784        NULL,
785        NULL,
786};
787
788static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s )
789{
790        fprintf( stderr, "DEBUG %s: %s", category, arg_s );
791}
792
793static PurpleDebugUiOps bee_debug_uiops =
794{
795        prplcb_debug_print,
796};
797
798static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer udata )
799{
800        return b_timeout_add( interval, (b_event_handler) func, udata );
801}
802
803static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata )
804{
805        return b_input_add( fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata );
806}
807
808static gboolean prplcb_ev_remove( guint id )
809{
810        b_event_remove( (gint) id );
811        return TRUE;
812}
813
814static PurpleEventLoopUiOps glib_eventloops = 
815{
816        prplcb_ev_timeout_add,
817        prplcb_ev_remove,
818        prplcb_ev_input_add,
819        prplcb_ev_remove,
820};
821
822static void *prplcb_notify_email( PurpleConnection *gc, const char *subject, const char *from,
823                                  const char *to, const char *url )
824{
825        struct im_connection *ic = purple_ic_by_gc( gc );
826       
827        imcb_log( ic, "Received e-mail from %s for %s: %s <%s>", from, to, subject, url );
828       
829        return NULL;
830}
831
832static void *prplcb_notify_userinfo( PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info )
833{
834        struct im_connection *ic = purple_ic_by_gc( gc );
835        GString *info = g_string_new( "" );
836        GList *l = purple_notify_user_info_get_entries( user_info );
837        char *key;
838        const char *value;
839        int n;
840       
841        while( l )
842        {
843                PurpleNotifyUserInfoEntry *e = l->data;
844               
845                switch( purple_notify_user_info_entry_get_type( e ) )
846                {
847                case PURPLE_NOTIFY_USER_INFO_ENTRY_PAIR:
848                case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER:
849                        key = g_strdup( purple_notify_user_info_entry_get_label( e ) );
850                        value = purple_notify_user_info_entry_get_value( e );
851                       
852                        if( key )
853                        {
854                                strip_html( key );
855                                g_string_append_printf( info, "%s: ", key );
856                               
857                                if( value )
858                                {
859                                        n = strlen( value ) - 1;
860                                        while( isspace( value[n] ) )
861                                                n --;
862                                        g_string_append_len( info, value, n + 1 );
863                                }
864                                g_string_append_c( info, '\n' );
865                                g_free( key );
866                        }
867                       
868                        break;
869                case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK:
870                        g_string_append( info, "------------------------\n" );
871                        break;
872                }
873               
874                l = l->next;
875        }
876       
877        imcb_log( ic, "User %s info:\n%s", who, info->str );
878        g_string_free( info, TRUE );
879       
880        return NULL;
881}
882
883static PurpleNotifyUiOps bee_notify_uiops =
884{
885        NULL,
886        prplcb_notify_email,
887        NULL,
888        NULL,
889        NULL,
890        NULL,
891        prplcb_notify_userinfo,
892};
893
894extern PurpleXferUiOps bee_xfer_uiops;
895
896static void purple_ui_init()
897{
898        purple_blist_set_ui_ops( &bee_blist_uiops );
899        purple_connections_set_ui_ops( &bee_conn_uiops );
900        purple_conversations_set_ui_ops( &bee_conv_uiops );
901        purple_request_set_ui_ops( &bee_request_uiops );
902        purple_notify_set_ui_ops( &bee_notify_uiops );
903        purple_xfers_set_ui_ops( &bee_xfer_uiops );
904       
905        if( getenv( "BITLBEE_DEBUG" ) )
906                purple_debug_set_ui_ops( &bee_debug_uiops );
907}
908
909void purple_initmodule()
910{
911        struct prpl funcs;
912        GList *prots;
913        GString *help;
914       
915        if( B_EV_IO_READ != PURPLE_INPUT_READ ||
916            B_EV_IO_WRITE != PURPLE_INPUT_WRITE )
917        {
918                /* FIXME FIXME FIXME FIXME FIXME :-) */
919                exit( 1 );
920        }
921       
922        purple_util_set_user_dir("/tmp");
923        purple_debug_set_enabled(FALSE);
924        purple_core_set_ui_ops(&bee_core_uiops);
925        purple_eventloop_set_ui_ops(&glib_eventloops);
926        if( !purple_core_init( "BitlBee") )
927        {
928                /* Initializing the core failed. Terminate. */
929                fprintf( stderr, "libpurple initialization failed.\n" );
930                abort();
931        }
932       
933        /* This seems like stateful shit we don't want... */
934        purple_set_blist(purple_blist_new());
935        purple_blist_load();
936       
937        /* Meh? */
938        purple_prefs_load();
939       
940        memset( &funcs, 0, sizeof( funcs ) );
941        funcs.login = purple_login;
942        funcs.init = purple_init;
943        funcs.logout = purple_logout;
944        funcs.buddy_msg = purple_buddy_msg;
945        funcs.away_states = purple_away_states;
946        funcs.set_away = purple_set_away;
947        funcs.add_buddy = purple_add_buddy;
948        funcs.remove_buddy = purple_remove_buddy;
949        funcs.get_info = purple_get_info;
950        funcs.keepalive = purple_keepalive;
951        funcs.send_typing = purple_send_typing;
952        funcs.handle_cmp = g_strcasecmp;
953        /* TODO(wilmer): Set these only for protocols that support them? */
954        funcs.chat_msg = purple_chat_msg;
955        funcs.chat_with = purple_chat_with;
956        funcs.chat_invite = purple_chat_invite;
957        funcs.chat_leave = purple_chat_leave;
958        funcs.transfer_request = purple_transfer_request;
959       
960        help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n");
961       
962        /* Add a protocol entry to BitlBee's structures for every protocol
963           supported by this libpurple instance. */     
964        for( prots = purple_plugins_get_protocols(); prots; prots = prots->next )
965        {
966                PurplePlugin *prot = prots->data;
967                struct prpl *ret;
968               
969                ret = g_memdup( &funcs, sizeof( funcs ) );
970                ret->name = ret->data = prot->info->id;
971                if( strncmp( ret->name, "prpl-", 5 ) == 0 )
972                        ret->name += 5;
973                register_protocol( ret );
974               
975                g_string_append_printf( help, "\n* %s (%s)", ret->name, prot->info->name );
976               
977                /* libpurple doesn't define a protocol called OSCAR, but we
978                   need it to be compatible with normal BitlBee. */
979                if( g_strcasecmp( prot->info->id, "prpl-aim" ) == 0 )
980                {
981                        ret = g_memdup( &funcs, sizeof( funcs ) );
982                        ret->name = "oscar";
983                        ret->data = prot->info->id;
984                        register_protocol( ret );
985                }
986        }
987       
988        g_string_append( help, "\n\nFor used protocols, more information about available "
989                         "settings can be found using \x02help purple <protocol name>\x02" );
990       
991        /* Add a simple dynamically-generated help item listing all
992           the supported protocols. */
993        help_add_mem( &global.help, "purple", help->str );
994        g_string_free( help, TRUE );
995}
Note: See TracBrowser for help on using the repository browser.