source: protocols/purple/purple.c @ dca8eff

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

Return ui_info so jabber:iq:version responses will not say just libpurple.

  • Property mode set to 100644
File size: 27.6 KB
Line 
1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  libpurple module - Main file                                             *
5*                                                                           *
6*  Copyright 2009-2010 Wilmer van der Gaast <wilmer@gaast.net>              *
7*                                                                           *
8*  This program is free software; you can redistribute it and/or modify     *
9*  it under the terms of the GNU General Public License as published by     *
10*  the Free Software Foundation; either version 2 of the License, or        *
11*  (at your option) any later version.                                      *
12*                                                                           *
13*  This program is distributed in the hope that it will be useful,          *
14*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
15*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
16*  GNU General Public License for more details.                             *
17*                                                                           *
18*  You should have received a copy of the GNU General Public License along  *
19*  with this program; if not, write to the Free Software Foundation, Inc.,  *
20*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
21*                                                                           *
22\***************************************************************************/
23
24#include "bitlbee.h"
25#include "help.h"
26
27#include <stdarg.h>
28
29#include <glib.h>
30#include <purple.h>
31
32GSList *purple_connections;
33
34/* This makes me VERY sad... :-( But some libpurple callbacks come in without
35   any context so this is the only way to get that. Don't want to support
36   libpurple in daemon mode anyway. */
37static irc_t *local_irc;
38
39struct im_connection *purple_ic_by_pa( PurpleAccount *pa )
40{
41        GSList *i;
42       
43        for( i = purple_connections; i; i = i->next )
44                if( ((struct im_connection *)i->data)->proto_data == pa )
45                        return i->data;
46       
47        return NULL;
48}
49
50static struct im_connection *purple_ic_by_gc( PurpleConnection *gc )
51{
52        return purple_ic_by_pa( purple_connection_get_account( gc ) );
53}
54
55static gboolean purple_menu_cmp( const char *a, const char *b )
56{
57        while( *a && *b )
58        {
59                while( *a == '_' ) a ++;
60                while( *b == '_' ) b ++;
61                if( tolower( *a ) != tolower( *b ) )
62                        return FALSE;
63               
64                a ++;
65                b ++;
66        }
67       
68        return ( *a == '\0' && *b == '\0' );
69}
70
71static void purple_init( account_t *acc )
72{
73        PurplePlugin *prpl = purple_plugins_find_with_id( (char*) acc->prpl->data );
74        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
75        PurpleAccount *pa;
76        GList *i, *st;
77        set_t *s;
78        char help_title[64];
79        GString *help;
80       
81        help = g_string_new( "" );
82        g_string_printf( help, "BitlBee libpurple module %s (%s).\n\nSupported settings:",
83                                (char*) acc->prpl->name, prpl->info->name );
84       
85        /* Convert all protocol_options into per-account setting variables. */
86        for( i = pi->protocol_options; i; i = i->next )
87        {
88                PurpleAccountOption *o = i->data;
89                const char *name;
90                char *def = NULL;
91                set_eval eval = NULL;
92                void *eval_data = NULL;
93                GList *io = NULL;
94                GSList *opts = NULL;
95               
96                name = purple_account_option_get_setting( o );
97               
98                switch( purple_account_option_get_type( o ) )
99                {
100                case PURPLE_PREF_STRING:
101                        def = g_strdup( purple_account_option_get_default_string( o ) );
102                       
103                        g_string_append_printf( help, "\n* %s (%s), %s, default: %s",
104                                                name, purple_account_option_get_text( o ),
105                                                "string", def );
106                       
107                        break;
108               
109                case PURPLE_PREF_INT:
110                        def = g_strdup_printf( "%d", purple_account_option_get_default_int( o ) );
111                        eval = set_eval_int;
112                       
113                        g_string_append_printf( help, "\n* %s (%s), %s, default: %s",
114                                                name, purple_account_option_get_text( o ),
115                                                "integer", def );
116                       
117                        break;
118               
119                case PURPLE_PREF_BOOLEAN:
120                        if( purple_account_option_get_default_bool( o ) )
121                                def = g_strdup( "true" );
122                        else
123                                def = g_strdup( "false" );
124                        eval = set_eval_bool;
125                       
126                        g_string_append_printf( help, "\n* %s (%s), %s, default: %s",
127                                                name, purple_account_option_get_text( o ),
128                                                "boolean", def );
129                       
130                        break;
131               
132                case PURPLE_PREF_STRING_LIST:
133                        def = g_strdup( purple_account_option_get_default_list_value( o ) );
134                       
135                        g_string_append_printf( help, "\n* %s (%s), %s, default: %s",
136                                                name, purple_account_option_get_text( o ),
137                                                "list", def );
138                        g_string_append( help, "\n  Possible values: " );
139                       
140                        for( io = purple_account_option_get_list( o ); io; io = io->next )
141                        {
142                                PurpleKeyValuePair *kv = io->data;
143                                opts = g_slist_append( opts, kv->value );
144                                /* 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
474GHashTable *prplcb_ui_info()
475{
476        static GHashTable *ret;
477       
478        if( ret == NULL )
479        {
480                ret = g_hash_table_new(g_str_hash, g_str_equal);
481                g_hash_table_insert( ret, "name", "BitlBee" );
482                g_hash_table_insert( ret, "version", BITLBEE_VERSION );
483        }
484       
485        return ret;
486}
487
488static PurpleCoreUiOps bee_core_uiops = 
489{
490        NULL,
491        NULL,
492        purple_ui_init,
493        NULL,
494        prplcb_ui_info,
495};
496
497static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t step, size_t step_count )
498{
499        struct im_connection *ic = purple_ic_by_gc( gc );
500       
501        imcb_log( ic, "%s", text );
502}
503
504static void prplcb_conn_connected( PurpleConnection *gc )
505{
506        struct im_connection *ic = purple_ic_by_gc( gc );
507       
508        imcb_connected( ic );
509       
510        if( gc->flags & PURPLE_CONNECTION_HTML )
511                ic->flags |= OPT_DOES_HTML;
512}
513
514static void prplcb_conn_disconnected( PurpleConnection *gc )
515{
516        struct im_connection *ic = purple_ic_by_gc( gc );
517       
518        if( ic != NULL )
519        {
520                imc_logout( ic, !gc->wants_to_die );
521        }
522}
523
524static void prplcb_conn_notice( PurpleConnection *gc, const char *text )
525{
526        struct im_connection *ic = purple_ic_by_gc( gc );
527       
528        if( ic != NULL )
529                imcb_log( ic, "%s", text );
530}
531
532static void prplcb_conn_report_disconnect_reason( PurpleConnection *gc, PurpleConnectionError reason, const char *text )
533{
534        struct im_connection *ic = purple_ic_by_gc( gc );
535       
536        /* PURPLE_CONNECTION_ERROR_NAME_IN_USE means concurrent login,
537           should probably handle that. */
538        if( ic != NULL )
539                imcb_error( ic, "%s", text );
540}
541
542static PurpleConnectionUiOps bee_conn_uiops =
543{
544        prplcb_conn_progress,
545        prplcb_conn_connected,
546        prplcb_conn_disconnected,
547        prplcb_conn_notice,
548        NULL,
549        NULL,
550        NULL,
551        prplcb_conn_report_disconnect_reason,
552};
553
554static void prplcb_blist_new( PurpleBlistNode *node )
555{
556        PurpleBuddy *bud = (PurpleBuddy*) node;
557       
558        if( node->type == PURPLE_BLIST_BUDDY_NODE )
559        {
560                struct im_connection *ic = purple_ic_by_pa( bud->account );
561               
562                if( ic == NULL )
563                        return;
564               
565                imcb_add_buddy( ic, bud->name, NULL );
566                if( bud->server_alias )
567                {
568                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
569                        imcb_buddy_nick_hint( ic, bud->name, bud->server_alias );
570                }
571        }
572}
573
574static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node )
575{
576        PurpleBuddy *bud = (PurpleBuddy*) node;
577       
578        if( node->type == PURPLE_BLIST_BUDDY_NODE )
579        {
580                struct im_connection *ic = purple_ic_by_pa( bud->account );
581                PurpleStatus *as;
582                int flags = 0;
583               
584                if( ic == NULL )
585                        return;
586               
587                if( bud->server_alias )
588                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
589               
590                flags |= purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0;
591                flags |= purple_presence_is_available( bud->presence ) ? 0 : OPT_AWAY;
592               
593                as = purple_presence_get_active_status( bud->presence );
594               
595                imcb_buddy_status( ic, bud->name, flags, purple_status_get_name( as ),
596                                   purple_status_get_attr_string( as, "message" ) );
597        }
598}
599
600static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node )
601{
602        /*
603        PurpleBuddy *bud = (PurpleBuddy*) node;
604       
605        if( node->type == PURPLE_BLIST_BUDDY_NODE )
606        {
607                struct im_connection *ic = purple_ic_by_pa( bud->account );
608               
609                if( ic == NULL )
610                        return;
611               
612                imcb_remove_buddy( ic, bud->name, NULL );
613        }
614        */
615}
616
617static PurpleBlistUiOps bee_blist_uiops =
618{
619        NULL,
620        prplcb_blist_new,
621        NULL,
622        prplcb_blist_update,
623        prplcb_blist_remove,
624};
625
626void prplcb_conv_new( PurpleConversation *conv )
627{
628        if( conv->type == PURPLE_CONV_TYPE_CHAT )
629        {
630                struct im_connection *ic = purple_ic_by_pa( conv->account );
631                struct groupchat *gc;
632               
633                gc = imcb_chat_new( ic, conv->name );
634                conv->ui_data = gc;
635                gc->data = conv;
636        }
637}
638
639void prplcb_conv_free( PurpleConversation *conv )
640{
641        struct groupchat *gc = conv->ui_data;
642       
643        imcb_chat_free( gc );
644}
645
646void prplcb_conv_add_users( PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals )
647{
648        struct groupchat *gc = conv->ui_data;
649        GList *b;
650       
651        if( !gc->joined && strcmp( conv->account->protocol_id, "prpl-msn" ) == 0 )
652        {
653                /* Work around the broken MSN module which fucks up the user's
654                   handle completely when informing him/her that he just
655                   successfully joined the room s/he just created (v2.6.6). */
656                imcb_chat_add_buddy( gc, gc->ic->acc->user );
657        }
658       
659        for( b = cbuddies; b; b = b->next )
660        {
661                PurpleConvChatBuddy *pcb = b->data;
662               
663                imcb_chat_add_buddy( gc, pcb->name );
664        }
665}
666
667void prplcb_conv_del_users( PurpleConversation *conv, GList *cbuddies )
668{
669        struct groupchat *gc = conv->ui_data;
670        GList *b;
671       
672        for( b = cbuddies; b; b = b->next )
673                imcb_chat_remove_buddy( gc, b->data, "" );
674}
675
676void prplcb_conv_chat_msg( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime )
677{
678        struct groupchat *gc = conv->ui_data;
679        PurpleBuddy *buddy;
680       
681        /* ..._SEND means it's an outgoing message, no need to echo those. */
682        if( flags & PURPLE_MESSAGE_SEND )
683                return;
684       
685        buddy = purple_find_buddy( conv->account, who );
686        if( buddy != NULL )
687                who = purple_buddy_get_name( buddy );
688       
689        imcb_chat_msg( gc, who, (char*) message, 0, mtime );
690}
691
692static void prplcb_conv_im( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime )
693{
694        struct im_connection *ic = purple_ic_by_pa( conv->account );
695        PurpleBuddy *buddy;
696       
697        /* ..._SEND means it's an outgoing message, no need to echo those. */
698        if( flags & PURPLE_MESSAGE_SEND )
699                return;
700       
701        buddy = purple_find_buddy( conv->account, who );
702        if( buddy != NULL )
703                who = purple_buddy_get_name( buddy );
704       
705        imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime );
706}
707
708static PurpleConversationUiOps bee_conv_uiops = 
709{
710        prplcb_conv_new,           /* create_conversation  */
711        prplcb_conv_free,          /* destroy_conversation */
712        prplcb_conv_chat_msg,      /* write_chat           */
713        prplcb_conv_im,            /* write_im             */
714        NULL,                      /* write_conv           */
715        prplcb_conv_add_users,     /* chat_add_users       */
716        NULL,                      /* chat_rename_user     */
717        prplcb_conv_del_users,     /* chat_remove_users    */
718        NULL,                      /* chat_update_user     */
719        NULL,                      /* present              */
720        NULL,                      /* has_focus            */
721        NULL,                      /* custom_smiley_add    */
722        NULL,                      /* custom_smiley_write  */
723        NULL,                      /* custom_smiley_close  */
724        NULL,                      /* send_confirm         */
725};
726
727struct prplcb_request_action_data
728{
729        void *user_data, *bee_data;
730        PurpleRequestActionCb yes, no;
731        int yes_i, no_i;
732};
733
734static void prplcb_request_action_yes( void *data )
735{
736        struct prplcb_request_action_data *pqad = data;
737       
738        pqad->yes( pqad->user_data, pqad->yes_i );
739        g_free( pqad );
740}
741
742static void prplcb_request_action_no( void *data )
743{
744        struct prplcb_request_action_data *pqad = data;
745       
746        pqad->no( pqad->user_data, pqad->no_i );
747        g_free( pqad );
748}
749
750static void *prplcb_request_action( const char *title, const char *primary, const char *secondary,
751                                    int default_action, PurpleAccount *account, const char *who,
752                                    PurpleConversation *conv, void *user_data, size_t action_count,
753                                    va_list actions )
754{
755        struct prplcb_request_action_data *pqad; 
756        int i;
757        char *q;
758       
759        pqad = g_new0( struct prplcb_request_action_data, 1 );
760       
761        for( i = 0; i < action_count; i ++ )
762        {
763                char *caption;
764                void *fn;
765               
766                caption = va_arg( actions, char* );
767                fn = va_arg( actions, void* );
768               
769                if( strstr( caption, "Accept" ) )
770                {
771                        pqad->yes = fn;
772                        pqad->yes_i = i;
773                }
774                else if( strstr( caption, "Reject" ) || strstr( caption, "Cancel" ) )
775                {
776                        pqad->no = fn;
777                        pqad->no_i = i;
778                }
779        }
780       
781        pqad->user_data = user_data;
782       
783        q = g_strdup_printf( "Request: %s\n\n%s\n\n%s", title, primary, secondary );
784        pqad->bee_data = query_add( local_irc, purple_ic_by_pa( account ), q,
785                prplcb_request_action_yes, prplcb_request_action_no, pqad );
786       
787        g_free( q );
788       
789        return pqad;
790}
791
792static PurpleRequestUiOps bee_request_uiops =
793{
794        NULL,
795        NULL,
796        prplcb_request_action,
797        NULL,
798        NULL,
799        NULL,
800        NULL,
801};
802
803static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s )
804{
805        fprintf( stderr, "DEBUG %s: %s", category, arg_s );
806}
807
808static PurpleDebugUiOps bee_debug_uiops =
809{
810        prplcb_debug_print,
811};
812
813static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer udata )
814{
815        return b_timeout_add( interval, (b_event_handler) func, udata );
816}
817
818static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata )
819{
820        return b_input_add( fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata );
821}
822
823static gboolean prplcb_ev_remove( guint id )
824{
825        b_event_remove( (gint) id );
826        return TRUE;
827}
828
829static PurpleEventLoopUiOps glib_eventloops = 
830{
831        prplcb_ev_timeout_add,
832        prplcb_ev_remove,
833        prplcb_ev_input_add,
834        prplcb_ev_remove,
835};
836
837static void *prplcb_notify_email( PurpleConnection *gc, const char *subject, const char *from,
838                                  const char *to, const char *url )
839{
840        struct im_connection *ic = purple_ic_by_gc( gc );
841       
842        imcb_log( ic, "Received e-mail from %s for %s: %s <%s>", from, to, subject, url );
843       
844        return NULL;
845}
846
847static void *prplcb_notify_userinfo( PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info )
848{
849        struct im_connection *ic = purple_ic_by_gc( gc );
850        GString *info = g_string_new( "" );
851        GList *l = purple_notify_user_info_get_entries( user_info );
852        char *key;
853        const char *value;
854        int n;
855       
856        while( l )
857        {
858                PurpleNotifyUserInfoEntry *e = l->data;
859               
860                switch( purple_notify_user_info_entry_get_type( e ) )
861                {
862                case PURPLE_NOTIFY_USER_INFO_ENTRY_PAIR:
863                case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER:
864                        key = g_strdup( purple_notify_user_info_entry_get_label( e ) );
865                        value = purple_notify_user_info_entry_get_value( e );
866                       
867                        if( key )
868                        {
869                                strip_html( key );
870                                g_string_append_printf( info, "%s: ", key );
871                               
872                                if( value )
873                                {
874                                        n = strlen( value ) - 1;
875                                        while( isspace( value[n] ) )
876                                                n --;
877                                        g_string_append_len( info, value, n + 1 );
878                                }
879                                g_string_append_c( info, '\n' );
880                                g_free( key );
881                        }
882                       
883                        break;
884                case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK:
885                        g_string_append( info, "------------------------\n" );
886                        break;
887                }
888               
889                l = l->next;
890        }
891       
892        imcb_log( ic, "User %s info:\n%s", who, info->str );
893        g_string_free( info, TRUE );
894       
895        return NULL;
896}
897
898static PurpleNotifyUiOps bee_notify_uiops =
899{
900        NULL,
901        prplcb_notify_email,
902        NULL,
903        NULL,
904        NULL,
905        NULL,
906        prplcb_notify_userinfo,
907};
908
909extern PurpleXferUiOps bee_xfer_uiops;
910
911static void purple_ui_init()
912{
913        purple_blist_set_ui_ops( &bee_blist_uiops );
914        purple_connections_set_ui_ops( &bee_conn_uiops );
915        purple_conversations_set_ui_ops( &bee_conv_uiops );
916        purple_request_set_ui_ops( &bee_request_uiops );
917        purple_notify_set_ui_ops( &bee_notify_uiops );
918        purple_xfers_set_ui_ops( &bee_xfer_uiops );
919       
920        if( getenv( "BITLBEE_DEBUG" ) )
921                purple_debug_set_ui_ops( &bee_debug_uiops );
922}
923
924void purple_initmodule()
925{
926        struct prpl funcs;
927        GList *prots;
928        GString *help;
929       
930        if( B_EV_IO_READ != PURPLE_INPUT_READ ||
931            B_EV_IO_WRITE != PURPLE_INPUT_WRITE )
932        {
933                /* FIXME FIXME FIXME FIXME FIXME :-) */
934                exit( 1 );
935        }
936       
937        purple_util_set_user_dir("/tmp");
938        purple_debug_set_enabled(FALSE);
939        purple_core_set_ui_ops(&bee_core_uiops);
940        purple_eventloop_set_ui_ops(&glib_eventloops);
941        if( !purple_core_init( "BitlBee") )
942        {
943                /* Initializing the core failed. Terminate. */
944                fprintf( stderr, "libpurple initialization failed.\n" );
945                abort();
946        }
947       
948        /* This seems like stateful shit we don't want... */
949        purple_set_blist(purple_blist_new());
950        purple_blist_load();
951       
952        /* Meh? */
953        purple_prefs_load();
954       
955        memset( &funcs, 0, sizeof( funcs ) );
956        funcs.login = purple_login;
957        funcs.init = purple_init;
958        funcs.logout = purple_logout;
959        funcs.buddy_msg = purple_buddy_msg;
960        funcs.away_states = purple_away_states;
961        funcs.set_away = purple_set_away;
962        funcs.add_buddy = purple_add_buddy;
963        funcs.remove_buddy = purple_remove_buddy;
964        funcs.get_info = purple_get_info;
965        funcs.keepalive = purple_keepalive;
966        funcs.send_typing = purple_send_typing;
967        funcs.handle_cmp = g_strcasecmp;
968        /* TODO(wilmer): Set these only for protocols that support them? */
969        funcs.chat_msg = purple_chat_msg;
970        funcs.chat_with = purple_chat_with;
971        funcs.chat_invite = purple_chat_invite;
972        funcs.chat_leave = purple_chat_leave;
973        funcs.transfer_request = purple_transfer_request;
974       
975        help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n");
976       
977        /* Add a protocol entry to BitlBee's structures for every protocol
978           supported by this libpurple instance. */     
979        for( prots = purple_plugins_get_protocols(); prots; prots = prots->next )
980        {
981                PurplePlugin *prot = prots->data;
982                struct prpl *ret;
983               
984                ret = g_memdup( &funcs, sizeof( funcs ) );
985                ret->name = ret->data = prot->info->id;
986                if( strncmp( ret->name, "prpl-", 5 ) == 0 )
987                        ret->name += 5;
988                register_protocol( ret );
989               
990                g_string_append_printf( help, "\n* %s (%s)", ret->name, prot->info->name );
991               
992                /* libpurple doesn't define a protocol called OSCAR, but we
993                   need it to be compatible with normal BitlBee. */
994                if( g_strcasecmp( prot->info->id, "prpl-aim" ) == 0 )
995                {
996                        ret = g_memdup( &funcs, sizeof( funcs ) );
997                        ret->name = "oscar";
998                        ret->data = prot->info->id;
999                        register_protocol( ret );
1000                }
1001        }
1002       
1003        g_string_append( help, "\n\nFor used protocols, more information about available "
1004                         "settings can be found using \x02help purple <protocol name>\x02" );
1005       
1006        /* Add a simple dynamically-generated help item listing all
1007           the supported protocols. */
1008        help_add_mem( &global.help, "purple", help->str );
1009        g_string_free( help, TRUE );
1010}
Note: See TracBrowser for help on using the repository browser.