source: protocols/purple/purple.c @ c735200

Last change on this file since c735200 was c735200, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-03-22T01:20:40Z

Incoming file transfers can now be accepted (and should work) and/or
rejected. Tested with Jabber and msn/msn-pecan so far.

  • Property mode set to 100644
File size: 23.5 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
39static struct 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 void purple_init( account_t *acc )
56{
57        PurplePlugin *prpl = purple_plugins_find_with_id( (char*) acc->prpl->data );
58        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
59        PurpleAccount *pa;
60        GList *i, *st;
61        set_t *s;
62        char help_title[64];
63        GString *help;
64       
65        help = g_string_new( "" );
66        g_string_printf( help, "BitlBee libpurple module %s (%s).\n\nSupported settings:",
67                                (char*) acc->prpl->name, prpl->info->name );
68       
69        /* Convert all protocol_options into per-account setting variables. */
70        for( i = pi->protocol_options; i; i = i->next )
71        {
72                PurpleAccountOption *o = i->data;
73                const char *name;
74                char *def = NULL;
75                set_eval eval = NULL;
76                void *eval_data = NULL;
77                GList *io = NULL;
78                GSList *opts = NULL;
79               
80                name = purple_account_option_get_setting( o );
81               
82                switch( purple_account_option_get_type( o ) )
83                {
84                case PURPLE_PREF_STRING:
85                        def = g_strdup( purple_account_option_get_default_string( o ) );
86                       
87                        g_string_append_printf( help, "\n* %s (%s), %s, default: %s",
88                                                name, purple_account_option_get_text( o ),
89                                                "string", def );
90                       
91                        break;
92               
93                case PURPLE_PREF_INT:
94                        def = g_strdup_printf( "%d", purple_account_option_get_default_int( o ) );
95                        eval = set_eval_int;
96                       
97                        g_string_append_printf( help, "\n* %s (%s), %s, default: %s",
98                                                name, purple_account_option_get_text( o ),
99                                                "integer", def );
100                       
101                        break;
102               
103                case PURPLE_PREF_BOOLEAN:
104                        if( purple_account_option_get_default_bool( o ) )
105                                def = g_strdup( "true" );
106                        else
107                                def = g_strdup( "false" );
108                        eval = set_eval_bool;
109                       
110                        g_string_append_printf( help, "\n* %s (%s), %s, default: %s",
111                                                name, purple_account_option_get_text( o ),
112                                                "boolean", def );
113                       
114                        break;
115               
116                case PURPLE_PREF_STRING_LIST:
117                        def = g_strdup( purple_account_option_get_default_list_value( o ) );
118                       
119                        g_string_append_printf( help, "\n* %s (%s), %s, default: %s",
120                                                name, purple_account_option_get_text( o ),
121                                                "list", def );
122                        g_string_append( help, "\n  Possible values: " );
123                       
124                        for( io = purple_account_option_get_list( o ); io; io = io->next )
125                        {
126                                PurpleKeyValuePair *kv = io->data;
127                                opts = g_slist_append( opts, kv->key );
128                                g_string_append_printf( help, "%s, ", kv->key );
129                        }
130                        g_string_truncate( help, help->len - 2 );
131                        eval = set_eval_list;
132                        eval_data = opts;
133                       
134                        break;
135                       
136                default:
137                        irc_usermsg( acc->irc, "Setting with unknown type: %s (%d) Expect stuff to break..\n",
138                                     name, purple_account_option_get_type( o ) );
139                        name = NULL;
140                }
141               
142                if( name != NULL )
143                {
144                        s = set_add( &acc->set, name, def, eval, acc );
145                        s->flags |= ACC_SET_OFFLINE_ONLY;
146                        s->eval_data = eval_data;
147                        g_free( def );
148                }
149        }
150       
151        g_snprintf( help_title, sizeof( help_title ), "purple %s", (char*) acc->prpl->name );
152        help_add_mem( &global.help, help_title, help->str );
153        g_string_free( help, TRUE );
154       
155        if( pi->options & OPT_PROTO_MAIL_CHECK )
156        {
157                s = set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc );
158                s->flags |= ACC_SET_OFFLINE_ONLY;
159        }
160       
161        /* Go through all away states to figure out if away/status messages
162           are possible. */
163        pa = purple_account_new( acc->user, (char*) acc->prpl->data );
164        for( st = purple_account_get_status_types( pa ); st; st = st->next )
165        {
166                PurpleStatusPrimitive prim = purple_status_type_get_primitive( st->data );
167               
168                if( prim == PURPLE_STATUS_AVAILABLE )
169                {
170                        if( purple_status_type_get_attr( st->data, "message" ) )
171                                acc->flags |= ACC_FLAG_STATUS_MESSAGE;
172                }
173                else if( prim != PURPLE_STATUS_OFFLINE )
174                {
175                        if( purple_status_type_get_attr( st->data, "message" ) )
176                                acc->flags |= ACC_FLAG_AWAY_MESSAGE;
177                }
178        }
179        purple_accounts_remove( pa );
180}
181
182static void purple_sync_settings( account_t *acc, PurpleAccount *pa )
183{
184        PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id );
185        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
186        GList *i;
187       
188        for( i = pi->protocol_options; i; i = i->next )
189        {
190                PurpleAccountOption *o = i->data;
191                const char *name;
192                set_t *s;
193               
194                name = purple_account_option_get_setting( o );
195                s = set_find( &acc->set, name );
196                if( s->value == NULL )
197                        continue;
198               
199                switch( purple_account_option_get_type( o ) )
200                {
201                case PURPLE_PREF_STRING:
202                case PURPLE_PREF_STRING_LIST:
203                        purple_account_set_string( pa, name, set_getstr( &acc->set, name ) );
204                        break;
205               
206                case PURPLE_PREF_INT:
207                        purple_account_set_int( pa, name, set_getint( &acc->set, name ) );
208                        break;
209               
210                case PURPLE_PREF_BOOLEAN:
211                        purple_account_set_bool( pa, name, set_getbool( &acc->set, name ) );
212                        break;
213               
214                default:
215                        break;
216                }
217        }
218       
219        if( pi->options & OPT_PROTO_MAIL_CHECK )
220                purple_account_set_check_mail( pa, set_getbool( &acc->set, "mail_notifications" ) );
221}
222
223static void purple_login( account_t *acc )
224{
225        struct im_connection *ic = imcb_new( acc );
226        PurpleAccount *pa;
227       
228        if( local_irc != NULL && local_irc != acc->irc )
229        {
230                irc_usermsg( acc->irc, "Daemon mode detected. Do *not* try to use libpurple in daemon mode! "
231                                       "Please use inetd or ForkDaemon mode instead." );
232                return;
233        }
234        local_irc = acc->irc;
235       
236        /* For now this is needed in the _connected() handlers if using
237           GLib event handling, to make sure we're not handling events
238           on dead connections. */
239        purple_connections = g_slist_prepend( purple_connections, ic );
240       
241        ic->proto_data = pa = purple_account_new( acc->user, (char*) acc->prpl->data );
242        purple_account_set_password( pa, acc->pass );
243        purple_sync_settings( acc, pa );
244       
245        purple_account_set_enabled( pa, "BitlBee", TRUE );
246}
247
248static void purple_logout( struct im_connection *ic )
249{
250        PurpleAccount *pa = ic->proto_data;
251       
252        purple_account_set_enabled( pa, "BitlBee", FALSE );
253        purple_connections = g_slist_remove( purple_connections, ic );
254        purple_accounts_remove( pa );
255}
256
257static int purple_buddy_msg( struct im_connection *ic, char *who, char *message, int flags )
258{
259        PurpleConversation *conv;
260       
261        if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM,
262                                                            who, ic->proto_data ) ) == NULL )
263        {
264                conv = purple_conversation_new( PURPLE_CONV_TYPE_IM,
265                                                ic->proto_data, who );
266        }
267       
268        purple_conv_im_send( purple_conversation_get_im_data( conv ), message );
269       
270        return 1;
271}
272
273static GList *purple_away_states( struct im_connection *ic )
274{
275        PurpleAccount *pa = ic->proto_data;
276        GList *st, *ret = NULL;
277       
278        for( st = purple_account_get_status_types( pa ); st; st = st->next )
279        {
280                PurpleStatusPrimitive prim = purple_status_type_get_primitive( st->data );
281                if( prim != PURPLE_STATUS_AVAILABLE && prim != PURPLE_STATUS_OFFLINE )
282                        ret = g_list_append( ret, (void*) purple_status_type_get_name( st->data ) );
283        }
284       
285        return ret;
286}
287
288static void purple_set_away( struct im_connection *ic, char *state_txt, char *message )
289{
290        PurpleAccount *pa = ic->proto_data;
291        GList *status_types = purple_account_get_status_types( pa ), *st;
292        PurpleStatusType *pst = NULL;
293        GList *args = NULL;
294       
295        for( st = status_types; st; st = st->next )
296        {
297                pst = st->data;
298               
299                if( state_txt == NULL &&
300                    purple_status_type_get_primitive( pst ) == PURPLE_STATUS_AVAILABLE )
301                        break;
302
303                if( state_txt != NULL &&
304                    g_strcasecmp( state_txt, purple_status_type_get_name( pst ) ) == 0 )
305                        break;
306        }
307       
308        if( message && purple_status_type_get_attr( pst, "message" ) )
309        {
310                args = g_list_append( args, "message" );
311                args = g_list_append( args, message );
312        }
313       
314        purple_account_set_status_list( pa, st ? purple_status_type_get_id( pst ) : "away",
315                                        TRUE, args );
316
317        g_list_free( args );
318}
319
320static void purple_add_buddy( struct im_connection *ic, char *who, char *group )
321{
322        PurpleBuddy *pb;
323       
324        pb = purple_buddy_new( (PurpleAccount*) ic->proto_data, who, NULL );
325        purple_blist_add_buddy( pb, NULL, NULL, NULL );
326        purple_account_add_buddy( (PurpleAccount*) ic->proto_data, pb );
327}
328
329static void purple_remove_buddy( struct im_connection *ic, char *who, char *group )
330{
331        PurpleBuddy *pb;
332       
333        pb = purple_find_buddy( (PurpleAccount*) ic->proto_data, who );
334        if( pb != NULL )
335        {
336                purple_account_remove_buddy( (PurpleAccount*) ic->proto_data, pb, NULL );
337                purple_blist_remove_buddy( pb );
338        }
339}
340
341static void purple_keepalive( struct im_connection *ic )
342{
343}
344
345static int purple_send_typing( struct im_connection *ic, char *who, int flags )
346{
347        PurpleTypingState state = PURPLE_NOT_TYPING;
348        PurpleConversation *conv;
349       
350        if( flags & OPT_TYPING )
351                state = PURPLE_TYPING;
352        else if( flags & OPT_THINKING )
353                state = PURPLE_TYPED;
354       
355        if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM,
356                                                            who, ic->proto_data ) ) == NULL )
357        {
358                purple_conv_im_set_typing_state( purple_conversation_get_im_data( conv ), state );
359                return 1;
360        }
361        else
362        {
363                return 0;
364        }
365}
366
367static void purple_ui_init();
368
369static PurpleCoreUiOps bee_core_uiops = 
370{
371        NULL,
372        NULL,
373        purple_ui_init,
374        NULL,
375};
376
377static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t step, size_t step_count )
378{
379        struct im_connection *ic = purple_ic_by_gc( gc );
380       
381        imcb_log( ic, "%s", text );
382}
383
384static void prplcb_conn_connected( PurpleConnection *gc )
385{
386        struct im_connection *ic = purple_ic_by_gc( gc );
387       
388        imcb_connected( ic );
389       
390        if( gc->flags & PURPLE_CONNECTION_HTML )
391                ic->flags |= OPT_DOES_HTML;
392}
393
394static void prplcb_conn_disconnected( PurpleConnection *gc )
395{
396        struct im_connection *ic = purple_ic_by_gc( gc );
397       
398        if( ic != NULL )
399        {
400                imc_logout( ic, TRUE );
401        }
402}
403
404static void prplcb_conn_notice( PurpleConnection *gc, const char *text )
405{
406        struct im_connection *ic = purple_ic_by_gc( gc );
407       
408        if( ic != NULL )
409                imcb_log( ic, "%s", text );
410}
411
412static void prplcb_conn_report_disconnect_reason( PurpleConnection *gc, PurpleConnectionError reason, const char *text )
413{
414        struct im_connection *ic = purple_ic_by_gc( gc );
415       
416        /* PURPLE_CONNECTION_ERROR_NAME_IN_USE means concurrent login,
417           should probably handle that. */
418        if( ic != NULL )
419                imcb_error( ic, "%s", text );
420}
421
422static PurpleConnectionUiOps bee_conn_uiops =
423{
424        prplcb_conn_progress,
425        prplcb_conn_connected,
426        prplcb_conn_disconnected,
427        prplcb_conn_notice,
428        NULL,
429        NULL,
430        NULL,
431        prplcb_conn_report_disconnect_reason,
432};
433
434static void prplcb_blist_new( PurpleBlistNode *node )
435{
436        PurpleBuddy *bud = (PurpleBuddy*) node;
437       
438        if( node->type == PURPLE_BLIST_BUDDY_NODE )
439        {
440                struct im_connection *ic = purple_ic_by_pa( bud->account );
441               
442                if( ic == NULL )
443                        return;
444               
445                imcb_add_buddy( ic, bud->name, NULL );
446                if( bud->server_alias )
447                {
448                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
449                        imcb_buddy_nick_hint( ic, bud->name, bud->server_alias );
450                }
451        }
452}
453
454static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node )
455{
456        PurpleBuddy *bud = (PurpleBuddy*) node;
457       
458        if( node->type == PURPLE_BLIST_BUDDY_NODE )
459        {
460                struct im_connection *ic = purple_ic_by_pa( bud->account );
461                PurpleStatus *as;
462                int flags = 0;
463               
464                if( ic == NULL )
465                        return;
466               
467                if( bud->server_alias )
468                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
469               
470                flags |= purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0;
471                flags |= purple_presence_is_available( bud->presence ) ? 0 : OPT_AWAY;
472               
473                as = purple_presence_get_active_status( bud->presence );
474               
475                imcb_buddy_status( ic, bud->name, flags, purple_status_get_name( as ),
476                                   purple_status_get_attr_string( as, "message" ) );
477        }
478}
479
480static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node )
481{
482        /*
483        PurpleBuddy *bud = (PurpleBuddy*) node;
484       
485        if( node->type == PURPLE_BLIST_BUDDY_NODE )
486        {
487                struct im_connection *ic = purple_ic_by_pa( bud->account );
488               
489                if( ic == NULL )
490                        return;
491               
492                imcb_remove_buddy( ic, bud->name, NULL );
493        }
494        */
495}
496
497static PurpleBlistUiOps bee_blist_uiops =
498{
499        NULL,
500        prplcb_blist_new,
501        NULL,
502        prplcb_blist_update,
503        prplcb_blist_remove,
504};
505
506static void prplcb_conv_im( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime )
507{
508        struct im_connection *ic = purple_ic_by_pa( conv->account );
509        PurpleBuddy *buddy;
510       
511        /* ..._SEND means it's an outgoing message, no need to echo those. */
512        if( flags & PURPLE_MESSAGE_SEND )
513                return;
514       
515        buddy = purple_find_buddy( conv->account, who );
516        if( buddy != NULL )
517                who = purple_buddy_get_name( buddy );
518       
519        imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime );
520}
521
522static PurpleConversationUiOps bee_conv_uiops = 
523{
524        NULL,                      /* create_conversation  */
525        NULL,                      /* destroy_conversation */
526        NULL,                      /* write_chat           */
527        prplcb_conv_im,            /* write_im             */
528        NULL,                      /* write_conv           */
529        NULL,                      /* chat_add_users       */
530        NULL,                      /* chat_rename_user     */
531        NULL,                      /* chat_remove_users    */
532        NULL,                      /* chat_update_user     */
533        NULL,                      /* present              */
534        NULL,                      /* has_focus            */
535        NULL,                      /* custom_smiley_add    */
536        NULL,                      /* custom_smiley_write  */
537        NULL,                      /* custom_smiley_close  */
538        NULL,                      /* send_confirm         */
539};
540
541struct prplcb_request_action_data
542{
543        void *user_data, *bee_data;
544        PurpleRequestActionCb yes, no;
545        int yes_i, no_i;
546};
547
548static void prplcb_request_action_yes( void *data )
549{
550        struct prplcb_request_action_data *pqad = data;
551       
552        pqad->yes( pqad->user_data, pqad->yes_i );
553        g_free( pqad );
554}
555
556static void prplcb_request_action_no( void *data )
557{
558        struct prplcb_request_action_data *pqad = data;
559       
560        pqad->no( pqad->user_data, pqad->no_i );
561        g_free( pqad );
562}
563
564static void *prplcb_request_action( const char *title, const char *primary, const char *secondary,
565                                    int default_action, PurpleAccount *account, const char *who,
566                                    PurpleConversation *conv, void *user_data, size_t action_count,
567                                    va_list actions )
568{
569        struct prplcb_request_action_data *pqad; 
570        int i;
571        char *q;
572       
573        pqad = g_new0( struct prplcb_request_action_data, 1 );
574       
575        for( i = 0; i < action_count; i ++ )
576        {
577                char *caption;
578                void *fn;
579               
580                caption = va_arg( actions, char* );
581                fn = va_arg( actions, void* );
582               
583                if( strstr( caption, "Accept" ) )
584                {
585                        pqad->yes = fn;
586                        pqad->yes_i = i;
587                }
588                else if( strstr( caption, "Reject" ) || strstr( caption, "Cancel" ) )
589                {
590                        pqad->no = fn;
591                        pqad->no_i = i;
592                }
593        }
594       
595        pqad->user_data = user_data;
596       
597        q = g_strdup_printf( "Request: %s\n\n%s\n\n%s", title, primary, secondary );
598        pqad->bee_data = query_add( local_irc, purple_ic_by_pa( account ), q,
599                prplcb_request_action_yes, prplcb_request_action_no, pqad );
600       
601        g_free( q );
602       
603        return pqad;
604}
605
606static PurpleRequestUiOps bee_request_uiops =
607{
608        NULL,
609        NULL,
610        prplcb_request_action,
611        NULL,
612        NULL,
613        NULL,
614        NULL,
615};
616
617static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s )
618{
619        fprintf( stderr, "DEBUG %s: %s", category, arg_s );
620}
621
622static PurpleDebugUiOps bee_debug_uiops =
623{
624        prplcb_debug_print,
625};
626
627static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer udata )
628{
629        return b_timeout_add( interval, (b_event_handler) func, udata );
630}
631
632static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata )
633{
634        return b_input_add( fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata );
635}
636
637static gboolean prplcb_ev_remove( guint id )
638{
639        b_event_remove( (gint) id );
640        return TRUE;
641}
642
643static PurpleEventLoopUiOps glib_eventloops = 
644{
645        prplcb_ev_timeout_add,
646        prplcb_ev_remove,
647        prplcb_ev_input_add,
648        prplcb_ev_remove,
649};
650
651static void *prplcb_notify_email( PurpleConnection *gc, const char *subject, const char *from,
652                                  const char *to, const char *url )
653{
654        struct im_connection *ic = purple_ic_by_gc( gc );
655       
656        imcb_log( ic, "Received e-mail from %s for %s: %s <%s>", from, to, subject, url );
657       
658        return NULL;
659}
660
661static PurpleNotifyUiOps bee_notify_uiops =
662{
663        NULL,
664        prplcb_notify_email,
665};
666
667struct prpl_xfer_data
668{
669        PurpleXfer *xfer;
670        file_transfer_t *ft;
671        gint ready_timer;
672};
673
674/* Glorious hack: We seem to have to remind at least some libpurple plugins
675   that we're ready because this info may get lost if we give it too early.
676   So just do it ten times a second. :-/ */
677static gboolean prplcb_xfer_write_request_cb( gpointer data, gint fd, b_input_condition cond )
678{
679        purple_xfer_ui_ready( data );
680        return TRUE;
681}
682
683static gboolean prpl_xfer_write_request( struct file_transfer *ft )
684{
685        struct prpl_xfer_data *px = ft->data;
686        px->ready_timer = b_timeout_add( 100, prplcb_xfer_write_request_cb, px->xfer );
687        return TRUE;
688}
689
690static gssize prplcb_xfer_write( PurpleXfer *xfer, const guchar *buffer, gssize size )
691{
692        struct prpl_xfer_data *px = xfer->ui_data;
693        gboolean st;
694       
695        b_event_remove( px->ready_timer );
696        px->ready_timer = 0;
697       
698        st = px->ft->write( px->ft, (char*) buffer, size );
699       
700        if( st && xfer->bytes_remaining == size )
701                imcb_file_finished( px->ft );
702       
703        return st ? size : 0;
704}
705
706static void prpl_xfer_accept( struct file_transfer *ft )
707{
708        struct prpl_xfer_data *px = ft->data;
709        purple_xfer_request_accepted( px->xfer, NULL );
710        prpl_xfer_write_request( ft );
711}
712
713static void prpl_xfer_canceled( struct file_transfer *ft, char *reason )
714{
715        struct prpl_xfer_data *px = ft->data;
716        purple_xfer_request_denied( px->xfer );
717}
718
719static gboolean prplcb_xfer_new_cb( gpointer data, gint fd, b_input_condition cond )
720{
721        PurpleXfer *xfer = data;
722        struct im_connection *ic = purple_ic_by_pa( xfer->account );
723        struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 );
724        PurpleBuddy *buddy;
725        const char *who;
726       
727        buddy = purple_find_buddy( xfer->account, xfer->who );
728        who = buddy ? purple_buddy_get_name( buddy ) : xfer->who;
729       
730        /* TODO(wilmer): After spreading some more const goodness in BitlBee,
731           remove the evil cast below. */
732        px->ft = imcb_file_send_start( ic, (char*) who, xfer->filename, xfer->size );
733        px->ft->data = px;
734        px->xfer = data;
735        px->xfer->ui_data = px;
736       
737        px->ft->accept = prpl_xfer_accept;
738        px->ft->canceled = prpl_xfer_canceled;
739        px->ft->write_request = prpl_xfer_write_request;
740       
741        return FALSE;
742}
743
744static void prplcb_xfer_new( PurpleXfer *xfer )
745{
746        /* This should suppress the stupid file dialog. */
747        purple_xfer_set_local_filename( xfer, "/tmp/wtf123" );
748       
749        /* Sadly the xfer struct is still empty ATM so come back after
750           the caller is done. */
751        b_timeout_add( 0, prplcb_xfer_new_cb, xfer );
752}
753
754static PurpleXferUiOps bee_xfer_uiops =
755{
756        prplcb_xfer_new,
757        NULL,
758        NULL,
759        NULL,
760        NULL,
761        NULL,
762        prplcb_xfer_write,
763};
764
765static void purple_ui_init()
766{
767        purple_blist_set_ui_ops( &bee_blist_uiops );
768        purple_connections_set_ui_ops( &bee_conn_uiops );
769        purple_conversations_set_ui_ops( &bee_conv_uiops );
770        purple_request_set_ui_ops( &bee_request_uiops );
771        purple_notify_set_ui_ops( &bee_notify_uiops );
772        purple_xfers_set_ui_ops( &bee_xfer_uiops );
773        //purple_debug_set_ui_ops( &bee_debug_uiops );
774}
775
776void purple_initmodule()
777{
778        struct prpl funcs;
779        GList *prots;
780        GString *help;
781       
782        if( B_EV_IO_READ != PURPLE_INPUT_READ ||
783            B_EV_IO_WRITE != PURPLE_INPUT_WRITE )
784        {
785                /* FIXME FIXME FIXME FIXME FIXME :-) */
786                exit( 1 );
787        }
788       
789        purple_util_set_user_dir("/tmp");
790        purple_debug_set_enabled(FALSE);
791        purple_core_set_ui_ops(&bee_core_uiops);
792        purple_eventloop_set_ui_ops(&glib_eventloops);
793        if( !purple_core_init( "BitlBee") )
794        {
795                /* Initializing the core failed. Terminate. */
796                fprintf( stderr, "libpurple initialization failed.\n" );
797                abort();
798        }
799       
800        /* This seems like stateful shit we don't want... */
801        purple_set_blist(purple_blist_new());
802        purple_blist_load();
803       
804        /* Meh? */
805        purple_prefs_load();
806       
807        memset( &funcs, 0, sizeof( funcs ) );
808        funcs.login = purple_login;
809        funcs.init = purple_init;
810        funcs.logout = purple_logout;
811        funcs.buddy_msg = purple_buddy_msg;
812        funcs.away_states = purple_away_states;
813        funcs.set_away = purple_set_away;
814        funcs.add_buddy = purple_add_buddy;
815        funcs.remove_buddy = purple_remove_buddy;
816        funcs.keepalive = purple_keepalive;
817        funcs.send_typing = purple_send_typing;
818        funcs.handle_cmp = g_strcasecmp;
819       
820        help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n");
821       
822        /* Add a protocol entry to BitlBee's structures for every protocol
823           supported by this libpurple instance. */     
824        for( prots = purple_plugins_get_protocols(); prots; prots = prots->next )
825        {
826                PurplePlugin *prot = prots->data;
827                struct prpl *ret;
828               
829                ret = g_memdup( &funcs, sizeof( funcs ) );
830                ret->name = ret->data = prot->info->id;
831                if( strncmp( ret->name, "prpl-", 5 ) == 0 )
832                        ret->name += 5;
833                register_protocol( ret );
834               
835                g_string_append_printf( help, "\n* %s (%s)", ret->name, prot->info->name );
836               
837                /* libpurple doesn't define a protocol called OSCAR, but we
838                   need it to be compatible with normal BitlBee. */
839                if( g_strcasecmp( prot->info->id, "prpl-aim" ) == 0 )
840                {
841                        ret = g_memdup( &funcs, sizeof( funcs ) );
842                        ret->name = "oscar";
843                        ret->data = prot->info->id;
844                        register_protocol( ret );
845                }
846        }
847       
848        g_string_append( help, "\n\nFor used protocols, more information about available "
849                         "settings can be found using \x02help purple <protocol name>\x02" );
850       
851        /* Add a simple dynamically-generated help item listing all
852           the supported protocols. */
853        help_add_mem( &global.help, "purple", help->str );
854        g_string_free( help, TRUE );
855}
Note: See TracBrowser for help on using the repository browser.