source: protocols/purple/purple.c @ c521362

Last change on this file since c521362 was a897467, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-04-17T22:43:55Z

I should stop doing commits with the debugging stuff still enabled.

  • Property mode set to 100644
File size: 26.0 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
367void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle );
368
369static void purple_ui_init();
370
371static PurpleCoreUiOps bee_core_uiops = 
372{
373        NULL,
374        NULL,
375        purple_ui_init,
376        NULL,
377};
378
379static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t step, size_t step_count )
380{
381        struct im_connection *ic = purple_ic_by_gc( gc );
382       
383        imcb_log( ic, "%s", text );
384}
385
386static void prplcb_conn_connected( PurpleConnection *gc )
387{
388        struct im_connection *ic = purple_ic_by_gc( gc );
389       
390        imcb_connected( ic );
391       
392        if( gc->flags & PURPLE_CONNECTION_HTML )
393                ic->flags |= OPT_DOES_HTML;
394}
395
396static void prplcb_conn_disconnected( PurpleConnection *gc )
397{
398        struct im_connection *ic = purple_ic_by_gc( gc );
399       
400        if( ic != NULL )
401        {
402                imc_logout( ic, TRUE );
403        }
404}
405
406static void prplcb_conn_notice( PurpleConnection *gc, const char *text )
407{
408        struct im_connection *ic = purple_ic_by_gc( gc );
409       
410        if( ic != NULL )
411                imcb_log( ic, "%s", text );
412}
413
414static void prplcb_conn_report_disconnect_reason( PurpleConnection *gc, PurpleConnectionError reason, const char *text )
415{
416        struct im_connection *ic = purple_ic_by_gc( gc );
417       
418        /* PURPLE_CONNECTION_ERROR_NAME_IN_USE means concurrent login,
419           should probably handle that. */
420        if( ic != NULL )
421                imcb_error( ic, "%s", text );
422}
423
424static PurpleConnectionUiOps bee_conn_uiops =
425{
426        prplcb_conn_progress,
427        prplcb_conn_connected,
428        prplcb_conn_disconnected,
429        prplcb_conn_notice,
430        NULL,
431        NULL,
432        NULL,
433        prplcb_conn_report_disconnect_reason,
434};
435
436static void prplcb_blist_new( PurpleBlistNode *node )
437{
438        PurpleBuddy *bud = (PurpleBuddy*) node;
439       
440        if( node->type == PURPLE_BLIST_BUDDY_NODE )
441        {
442                struct im_connection *ic = purple_ic_by_pa( bud->account );
443               
444                if( ic == NULL )
445                        return;
446               
447                imcb_add_buddy( ic, bud->name, NULL );
448                if( bud->server_alias )
449                {
450                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
451                        imcb_buddy_nick_hint( ic, bud->name, bud->server_alias );
452                }
453        }
454}
455
456static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node )
457{
458        PurpleBuddy *bud = (PurpleBuddy*) node;
459       
460        if( node->type == PURPLE_BLIST_BUDDY_NODE )
461        {
462                struct im_connection *ic = purple_ic_by_pa( bud->account );
463                PurpleStatus *as;
464                int flags = 0;
465               
466                if( ic == NULL )
467                        return;
468               
469                if( bud->server_alias )
470                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
471               
472                flags |= purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0;
473                flags |= purple_presence_is_available( bud->presence ) ? 0 : OPT_AWAY;
474               
475                as = purple_presence_get_active_status( bud->presence );
476               
477                imcb_buddy_status( ic, bud->name, flags, purple_status_get_name( as ),
478                                   purple_status_get_attr_string( as, "message" ) );
479        }
480}
481
482static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node )
483{
484        /*
485        PurpleBuddy *bud = (PurpleBuddy*) node;
486       
487        if( node->type == PURPLE_BLIST_BUDDY_NODE )
488        {
489                struct im_connection *ic = purple_ic_by_pa( bud->account );
490               
491                if( ic == NULL )
492                        return;
493               
494                imcb_remove_buddy( ic, bud->name, NULL );
495        }
496        */
497}
498
499static PurpleBlistUiOps bee_blist_uiops =
500{
501        NULL,
502        prplcb_blist_new,
503        NULL,
504        prplcb_blist_update,
505        prplcb_blist_remove,
506};
507
508static void prplcb_conv_im( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime )
509{
510        struct im_connection *ic = purple_ic_by_pa( conv->account );
511        PurpleBuddy *buddy;
512       
513        /* ..._SEND means it's an outgoing message, no need to echo those. */
514        if( flags & PURPLE_MESSAGE_SEND )
515                return;
516       
517        buddy = purple_find_buddy( conv->account, who );
518        if( buddy != NULL )
519                who = purple_buddy_get_name( buddy );
520       
521        imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime );
522}
523
524static PurpleConversationUiOps bee_conv_uiops = 
525{
526        NULL,                      /* create_conversation  */
527        NULL,                      /* destroy_conversation */
528        NULL,                      /* write_chat           */
529        prplcb_conv_im,            /* write_im             */
530        NULL,                      /* write_conv           */
531        NULL,                      /* chat_add_users       */
532        NULL,                      /* chat_rename_user     */
533        NULL,                      /* chat_remove_users    */
534        NULL,                      /* chat_update_user     */
535        NULL,                      /* present              */
536        NULL,                      /* has_focus            */
537        NULL,                      /* custom_smiley_add    */
538        NULL,                      /* custom_smiley_write  */
539        NULL,                      /* custom_smiley_close  */
540        NULL,                      /* send_confirm         */
541};
542
543struct prplcb_request_action_data
544{
545        void *user_data, *bee_data;
546        PurpleRequestActionCb yes, no;
547        int yes_i, no_i;
548};
549
550static void prplcb_request_action_yes( void *data )
551{
552        struct prplcb_request_action_data *pqad = data;
553       
554        pqad->yes( pqad->user_data, pqad->yes_i );
555        g_free( pqad );
556}
557
558static void prplcb_request_action_no( void *data )
559{
560        struct prplcb_request_action_data *pqad = data;
561       
562        pqad->no( pqad->user_data, pqad->no_i );
563        g_free( pqad );
564}
565
566static void *prplcb_request_action( const char *title, const char *primary, const char *secondary,
567                                    int default_action, PurpleAccount *account, const char *who,
568                                    PurpleConversation *conv, void *user_data, size_t action_count,
569                                    va_list actions )
570{
571        struct prplcb_request_action_data *pqad; 
572        int i;
573        char *q;
574       
575        pqad = g_new0( struct prplcb_request_action_data, 1 );
576       
577        for( i = 0; i < action_count; i ++ )
578        {
579                char *caption;
580                void *fn;
581               
582                caption = va_arg( actions, char* );
583                fn = va_arg( actions, void* );
584               
585                if( strstr( caption, "Accept" ) )
586                {
587                        pqad->yes = fn;
588                        pqad->yes_i = i;
589                }
590                else if( strstr( caption, "Reject" ) || strstr( caption, "Cancel" ) )
591                {
592                        pqad->no = fn;
593                        pqad->no_i = i;
594                }
595        }
596       
597        pqad->user_data = user_data;
598       
599        q = g_strdup_printf( "Request: %s\n\n%s\n\n%s", title, primary, secondary );
600        pqad->bee_data = query_add( local_irc, purple_ic_by_pa( account ), q,
601                prplcb_request_action_yes, prplcb_request_action_no, pqad );
602       
603        g_free( q );
604       
605        return pqad;
606}
607
608static PurpleRequestUiOps bee_request_uiops =
609{
610        NULL,
611        NULL,
612        prplcb_request_action,
613        NULL,
614        NULL,
615        NULL,
616        NULL,
617};
618
619static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s )
620{
621        fprintf( stderr, "DEBUG %s: %s", category, arg_s );
622}
623
624static PurpleDebugUiOps bee_debug_uiops =
625{
626        prplcb_debug_print,
627};
628
629static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer udata )
630{
631        return b_timeout_add( interval, (b_event_handler) func, udata );
632}
633
634static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata )
635{
636        return b_input_add( fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata );
637}
638
639static gboolean prplcb_ev_remove( guint id )
640{
641        b_event_remove( (gint) id );
642        return TRUE;
643}
644
645static PurpleEventLoopUiOps glib_eventloops = 
646{
647        prplcb_ev_timeout_add,
648        prplcb_ev_remove,
649        prplcb_ev_input_add,
650        prplcb_ev_remove,
651};
652
653static void *prplcb_notify_email( PurpleConnection *gc, const char *subject, const char *from,
654                                  const char *to, const char *url )
655{
656        struct im_connection *ic = purple_ic_by_gc( gc );
657       
658        imcb_log( ic, "Received e-mail from %s for %s: %s <%s>", from, to, subject, url );
659       
660        return NULL;
661}
662
663static PurpleNotifyUiOps bee_notify_uiops =
664{
665        NULL,
666        prplcb_notify_email,
667};
668
669
670struct prpl_xfer_data
671{
672        PurpleXfer *xfer;
673        file_transfer_t *ft;
674        gint ready_timer;
675        char *buf;
676        int buf_len;
677};
678
679static file_transfer_t *next_ft;
680
681/* Glorious hack: We seem to have to remind at least some libpurple plugins
682   that we're ready because this info may get lost if we give it too early.
683   So just do it ten times a second. :-/ */
684static gboolean prplcb_xfer_write_request_cb( gpointer data, gint fd, b_input_condition cond )
685{
686        struct prpl_xfer_data *px = data;
687       
688        purple_xfer_ui_ready( px->xfer );
689       
690        return purple_xfer_get_type( px->xfer ) == PURPLE_XFER_RECEIVE;
691}
692
693static gboolean prpl_xfer_write_request( struct file_transfer *ft )
694{
695        struct prpl_xfer_data *px = ft->data;
696        px->ready_timer = b_timeout_add( 100, prplcb_xfer_write_request_cb, px );
697        return TRUE;
698}
699
700static gssize prplcb_xfer_write( PurpleXfer *xfer, const guchar *buffer, gssize size )
701{
702        struct prpl_xfer_data *px = xfer->ui_data;
703        gboolean st;
704       
705        b_event_remove( px->ready_timer );
706        px->ready_timer = 0;
707       
708        st = px->ft->write( px->ft, (char*) buffer, size );
709       
710        if( st && xfer->bytes_remaining == size )
711                imcb_file_finished( px->ft );
712       
713        return st ? size : 0;
714}
715
716static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len )
717{
718        struct prpl_xfer_data *px = ft->data;
719       
720        px->buf = g_memdup( buffer, len );
721        px->buf_len = len;
722       
723        //purple_xfer_ui_ready( px->xfer );
724        px->ready_timer = b_timeout_add( 0, prplcb_xfer_write_request_cb, px );
725       
726        return TRUE;
727}
728
729static void prpl_xfer_accept( struct file_transfer *ft )
730{
731        struct prpl_xfer_data *px = ft->data;
732        purple_xfer_request_accepted( px->xfer, NULL );
733        prpl_xfer_write_request( ft );
734}
735
736static void prpl_xfer_canceled( struct file_transfer *ft, char *reason )
737{
738        struct prpl_xfer_data *px = ft->data;
739        purple_xfer_request_denied( px->xfer );
740}
741
742static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond )
743{
744        PurpleXfer *xfer = data;
745        struct im_connection *ic = purple_ic_by_pa( xfer->account );
746        struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 );
747        PurpleBuddy *buddy;
748        const char *who;
749       
750        buddy = purple_find_buddy( xfer->account, xfer->who );
751        who = buddy ? purple_buddy_get_name( buddy ) : xfer->who;
752       
753        /* TODO(wilmer): After spreading some more const goodness in BitlBee,
754           remove the evil cast below. */
755        px->ft = imcb_file_send_start( ic, (char*) who, xfer->filename, xfer->size );
756        px->ft->data = px;
757        px->xfer = data;
758        px->xfer->ui_data = px;
759       
760        px->ft->accept = prpl_xfer_accept;
761        px->ft->canceled = prpl_xfer_canceled;
762        px->ft->write_request = prpl_xfer_write_request;
763       
764        return FALSE;
765}
766
767static void prplcb_xfer_new( PurpleXfer *xfer )
768{
769        if( purple_xfer_get_type( xfer ) == PURPLE_XFER_RECEIVE )
770        {
771                /* This should suppress the stupid file dialog. */
772                purple_xfer_set_local_filename( xfer, "/tmp/wtf123" );
773               
774                /* Sadly the xfer struct is still empty ATM so come back after
775                   the caller is done. */
776                b_timeout_add( 0, prplcb_xfer_new_send_cb, xfer );
777        }
778        else
779        {
780                struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 );
781               
782                px->ft = next_ft;
783                px->ft->data = px;
784                px->xfer = xfer;
785                px->xfer->ui_data = px;
786               
787                purple_xfer_set_filename( xfer, px->ft->file_name );
788                purple_xfer_set_size( xfer, px->ft->file_size );
789               
790                next_ft = NULL;
791        }
792}
793
794static void prplcb_xfer_dbg( PurpleXfer *xfer )
795{
796        fprintf( stderr, "prplcb_xfer_dbg 0x%p\n", xfer );
797}
798
799gssize prplcb_xfer_read( PurpleXfer *xfer, guchar **buffer, gssize size )
800{
801        struct prpl_xfer_data *px = xfer->ui_data;
802       
803        fprintf( stderr, "xfer_read %d %d\n", size, px->buf_len );
804
805        if( px->buf )
806        {
807                *buffer = px->buf;
808                px->buf = NULL;
809               
810                px->ft->write_request( px->ft );
811               
812                return px->buf_len;
813        }
814       
815        return 0;
816}
817
818static PurpleXferUiOps bee_xfer_uiops =
819{
820        prplcb_xfer_new,
821        prplcb_xfer_dbg,
822        prplcb_xfer_dbg,
823        prplcb_xfer_dbg,
824        prplcb_xfer_dbg,
825        prplcb_xfer_dbg,
826        prplcb_xfer_write,
827        prplcb_xfer_read,
828        prplcb_xfer_dbg,
829};
830
831static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond );
832
833void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle )
834{
835        PurpleAccount *pa = ic->proto_data;
836        struct prpl_xfer_data *px;
837       
838        /* xfer_new() will pick up this variable. It's a hack but we're not
839           multi-threaded anyway. */
840        next_ft = ft;
841        serv_send_file( purple_account_get_connection( pa ), handle, ft->file_name );
842       
843        ft->write = prpl_xfer_write;
844       
845        px = ft->data;
846        imcb_file_recv_start( ft );
847       
848        px->ready_timer = b_timeout_add( 100, prplcb_xfer_send_cb, px );
849}
850
851static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond )
852{
853        struct prpl_xfer_data *px = data;
854       
855        if( px->ft->status & FT_STATUS_TRANSFERRING )
856        {
857                fprintf( stderr, "The ft, it is ready...\n" );
858                px->ft->write_request( px->ft );
859               
860                return FALSE;
861        }
862       
863        return TRUE;
864}
865
866static void purple_ui_init()
867{
868        purple_blist_set_ui_ops( &bee_blist_uiops );
869        purple_connections_set_ui_ops( &bee_conn_uiops );
870        purple_conversations_set_ui_ops( &bee_conv_uiops );
871        purple_request_set_ui_ops( &bee_request_uiops );
872        purple_notify_set_ui_ops( &bee_notify_uiops );
873        purple_xfers_set_ui_ops( &bee_xfer_uiops );
874        //purple_debug_set_ui_ops( &bee_debug_uiops );
875}
876
877void purple_initmodule()
878{
879        struct prpl funcs;
880        GList *prots;
881        GString *help;
882       
883        if( B_EV_IO_READ != PURPLE_INPUT_READ ||
884            B_EV_IO_WRITE != PURPLE_INPUT_WRITE )
885        {
886                /* FIXME FIXME FIXME FIXME FIXME :-) */
887                exit( 1 );
888        }
889       
890        purple_util_set_user_dir("/tmp");
891        purple_debug_set_enabled(FALSE);
892        purple_core_set_ui_ops(&bee_core_uiops);
893        purple_eventloop_set_ui_ops(&glib_eventloops);
894        if( !purple_core_init( "BitlBee") )
895        {
896                /* Initializing the core failed. Terminate. */
897                fprintf( stderr, "libpurple initialization failed.\n" );
898                abort();
899        }
900       
901        /* This seems like stateful shit we don't want... */
902        purple_set_blist(purple_blist_new());
903        purple_blist_load();
904       
905        /* Meh? */
906        purple_prefs_load();
907       
908        memset( &funcs, 0, sizeof( funcs ) );
909        funcs.login = purple_login;
910        funcs.init = purple_init;
911        funcs.logout = purple_logout;
912        funcs.buddy_msg = purple_buddy_msg;
913        funcs.away_states = purple_away_states;
914        funcs.set_away = purple_set_away;
915        funcs.add_buddy = purple_add_buddy;
916        funcs.remove_buddy = purple_remove_buddy;
917        funcs.keepalive = purple_keepalive;
918        funcs.send_typing = purple_send_typing;
919        funcs.handle_cmp = g_strcasecmp;
920        /* TODO(wilmer): Set this one only for protocols that support it? */
921        funcs.transfer_request = purple_transfer_request;
922       
923        help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n");
924       
925        /* Add a protocol entry to BitlBee's structures for every protocol
926           supported by this libpurple instance. */     
927        for( prots = purple_plugins_get_protocols(); prots; prots = prots->next )
928        {
929                PurplePlugin *prot = prots->data;
930                struct prpl *ret;
931               
932                ret = g_memdup( &funcs, sizeof( funcs ) );
933                ret->name = ret->data = prot->info->id;
934                if( strncmp( ret->name, "prpl-", 5 ) == 0 )
935                        ret->name += 5;
936                register_protocol( ret );
937               
938                g_string_append_printf( help, "\n* %s (%s)", ret->name, prot->info->name );
939               
940                /* libpurple doesn't define a protocol called OSCAR, but we
941                   need it to be compatible with normal BitlBee. */
942                if( g_strcasecmp( prot->info->id, "prpl-aim" ) == 0 )
943                {
944                        ret = g_memdup( &funcs, sizeof( funcs ) );
945                        ret->name = "oscar";
946                        ret->data = prot->info->id;
947                        register_protocol( ret );
948                }
949        }
950       
951        g_string_append( help, "\n\nFor used protocols, more information about available "
952                         "settings can be found using \x02help purple <protocol name>\x02" );
953       
954        /* Add a simple dynamically-generated help item listing all
955           the supported protocols. */
956        help_add_mem( &global.help, "purple", help->str );
957        g_string_free( help, TRUE );
958}
Note: See TracBrowser for help on using the repository browser.