source: protocols/purple/purple.c @ 75c3ff7

Last change on this file since 75c3ff7 was 75c3ff7, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-21T00:09:29Z

Fixed sending with proper filenames by creating a temporary directory with
the file in it; protocol modules are mostly hardcoded to use the filename
from the filesystem with no way to override this.

Also improved robustness a little bit.

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