source: protocols/purple/purple.c @ d0527c1

Last change on this file since d0527c1 was d0527c1, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-17T11:34:24Z

libpurple: Handle incoming authorization requests.

  • Property mode set to 100644
File size: 32.7 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 bee_t *local_bee;
38
39static char *set_eval_display_name( set_t *set, char *value );
40
41struct im_connection *purple_ic_by_pa( PurpleAccount *pa )
42{
43        GSList *i;
44       
45        for( i = purple_connections; i; i = i->next )
46                if( ((struct im_connection *)i->data)->proto_data == pa )
47                        return i->data;
48       
49        return NULL;
50}
51
52static struct im_connection *purple_ic_by_gc( PurpleConnection *gc )
53{
54        return purple_ic_by_pa( purple_connection_get_account( gc ) );
55}
56
57static gboolean purple_menu_cmp( const char *a, const char *b )
58{
59        while( *a && *b )
60        {
61                while( *a == '_' ) a ++;
62                while( *b == '_' ) b ++;
63                if( tolower( *a ) != tolower( *b ) )
64                        return FALSE;
65               
66                a ++;
67                b ++;
68        }
69       
70        return ( *a == '\0' && *b == '\0' );
71}
72
73static void purple_init( account_t *acc )
74{
75        PurplePlugin *prpl = purple_plugins_find_with_id( (char*) acc->prpl->data );
76        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
77        PurpleAccount *pa;
78        GList *i, *st;
79        set_t *s;
80        char help_title[64];
81        GString *help;
82       
83        help = g_string_new( "" );
84        g_string_printf( help, "BitlBee libpurple module %s (%s).\n\nSupported settings:",
85                                (char*) acc->prpl->name, prpl->info->name );
86       
87        /* Convert all protocol_options into per-account setting variables. */
88        for( i = pi->protocol_options; i; i = i->next )
89        {
90                PurpleAccountOption *o = i->data;
91                const char *name;
92                char *def = NULL;
93                set_eval eval = NULL;
94                void *eval_data = NULL;
95                GList *io = NULL;
96                GSList *opts = NULL;
97               
98                name = purple_account_option_get_setting( o );
99               
100                switch( purple_account_option_get_type( o ) )
101                {
102                case PURPLE_PREF_STRING:
103                        def = g_strdup( purple_account_option_get_default_string( o ) );
104                       
105                        g_string_append_printf( help, "\n* %s (%s), %s, default: %s",
106                                                name, purple_account_option_get_text( o ),
107                                                "string", def );
108                       
109                        break;
110               
111                case PURPLE_PREF_INT:
112                        def = g_strdup_printf( "%d", purple_account_option_get_default_int( o ) );
113                        eval = set_eval_int;
114                       
115                        g_string_append_printf( help, "\n* %s (%s), %s, default: %s",
116                                                name, purple_account_option_get_text( o ),
117                                                "integer", def );
118                       
119                        break;
120               
121                case PURPLE_PREF_BOOLEAN:
122                        if( purple_account_option_get_default_bool( o ) )
123                                def = g_strdup( "true" );
124                        else
125                                def = g_strdup( "false" );
126                        eval = set_eval_bool;
127                       
128                        g_string_append_printf( help, "\n* %s (%s), %s, default: %s",
129                                                name, purple_account_option_get_text( o ),
130                                                "boolean", def );
131                       
132                        break;
133               
134                case PURPLE_PREF_STRING_LIST:
135                        def = g_strdup( purple_account_option_get_default_list_value( o ) );
136                       
137                        g_string_append_printf( help, "\n* %s (%s), %s, default: %s",
138                                                name, purple_account_option_get_text( o ),
139                                                "list", def );
140                        g_string_append( help, "\n  Possible values: " );
141                       
142                        for( io = purple_account_option_get_list( o ); io; io = io->next )
143                        {
144                                PurpleKeyValuePair *kv = io->data;
145                                opts = g_slist_append( opts, kv->value );
146                                /* TODO: kv->value is not a char*, WTF? */
147                                if( strcmp( kv->value, kv->key ) != 0 )
148                                        g_string_append_printf( help, "%s (%s), ", (char*) kv->value, kv->key );
149                                else
150                                        g_string_append_printf( help, "%s, ", (char*) kv->value );
151                        }
152                        g_string_truncate( help, help->len - 2 );
153                        eval = set_eval_list;
154                        eval_data = opts;
155                       
156                        break;
157                       
158                default:
159                        /** No way to talk to the user right now, invent one when
160                        this becomes important.
161                        irc_usermsg( acc->irc, "Setting with unknown type: %s (%d) Expect stuff to break..\n",
162                                     name, purple_account_option_get_type( o ) );
163                        */
164                        name = NULL;
165                }
166               
167                if( name != NULL )
168                {
169                        s = set_add( &acc->set, name, def, eval, acc );
170                        s->flags |= ACC_SET_OFFLINE_ONLY;
171                        s->eval_data = eval_data;
172                        g_free( def );
173                }
174        }
175       
176        g_snprintf( help_title, sizeof( help_title ), "purple %s", (char*) acc->prpl->name );
177        help_add_mem( &global.help, help_title, help->str );
178        g_string_free( help, TRUE );
179       
180        s = set_add( &acc->set, "display_name", NULL, set_eval_display_name, acc );
181        s->flags |= ACC_SET_ONLINE_ONLY;
182       
183        if( pi->options & OPT_PROTO_MAIL_CHECK )
184        {
185                s = set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc );
186                s->flags |= ACC_SET_OFFLINE_ONLY;
187        }
188       
189        /* Go through all away states to figure out if away/status messages
190           are possible. */
191        pa = purple_account_new( acc->user, (char*) acc->prpl->data );
192        for( st = purple_account_get_status_types( pa ); st; st = st->next )
193        {
194                PurpleStatusPrimitive prim = purple_status_type_get_primitive( st->data );
195               
196                if( prim == PURPLE_STATUS_AVAILABLE )
197                {
198                        if( purple_status_type_get_attr( st->data, "message" ) )
199                                acc->flags |= ACC_FLAG_STATUS_MESSAGE;
200                }
201                else if( prim != PURPLE_STATUS_OFFLINE )
202                {
203                        if( purple_status_type_get_attr( st->data, "message" ) )
204                                acc->flags |= ACC_FLAG_AWAY_MESSAGE;
205                }
206        }
207        purple_accounts_remove( pa );
208}
209
210static void purple_sync_settings( account_t *acc, PurpleAccount *pa )
211{
212        PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id );
213        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
214        GList *i;
215       
216        for( i = pi->protocol_options; i; i = i->next )
217        {
218                PurpleAccountOption *o = i->data;
219                const char *name;
220                set_t *s;
221               
222                name = purple_account_option_get_setting( o );
223                s = set_find( &acc->set, name );
224                if( s->value == NULL )
225                        continue;
226               
227                switch( purple_account_option_get_type( o ) )
228                {
229                case PURPLE_PREF_STRING:
230                case PURPLE_PREF_STRING_LIST:
231                        purple_account_set_string( pa, name, set_getstr( &acc->set, name ) );
232                        break;
233               
234                case PURPLE_PREF_INT:
235                        purple_account_set_int( pa, name, set_getint( &acc->set, name ) );
236                        break;
237               
238                case PURPLE_PREF_BOOLEAN:
239                        purple_account_set_bool( pa, name, set_getbool( &acc->set, name ) );
240                        break;
241               
242                default:
243                        break;
244                }
245        }
246       
247        if( pi->options & OPT_PROTO_MAIL_CHECK )
248                purple_account_set_check_mail( pa, set_getbool( &acc->set, "mail_notifications" ) );
249}
250
251static void purple_login( account_t *acc )
252{
253        struct im_connection *ic = imcb_new( acc );
254        PurpleAccount *pa;
255       
256        if( local_bee != NULL && local_bee != acc->bee )
257        {
258                imcb_error( ic,  "Daemon mode detected. Do *not* try to use libpurple in daemon mode! "
259                                 "Please use inetd or ForkDaemon mode instead." );
260                imc_logout( ic, FALSE );
261                return;
262        }
263        local_bee = acc->bee;
264       
265        /* For now this is needed in the _connected() handlers if using
266           GLib event handling, to make sure we're not handling events
267           on dead connections. */
268        purple_connections = g_slist_prepend( purple_connections, ic );
269       
270        ic->proto_data = pa = purple_account_new( acc->user, (char*) acc->prpl->data );
271        purple_account_set_password( pa, acc->pass );
272        purple_sync_settings( acc, pa );
273       
274        purple_account_set_enabled( pa, "BitlBee", TRUE );
275}
276
277static void purple_logout( struct im_connection *ic )
278{
279        PurpleAccount *pa = ic->proto_data;
280       
281        purple_account_set_enabled( pa, "BitlBee", FALSE );
282        purple_connections = g_slist_remove( purple_connections, ic );
283        purple_accounts_remove( pa );
284}
285
286static int purple_buddy_msg( struct im_connection *ic, char *who, char *message, int flags )
287{
288        PurpleConversation *conv;
289       
290        if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM,
291                                                            who, ic->proto_data ) ) == NULL )
292        {
293                conv = purple_conversation_new( PURPLE_CONV_TYPE_IM,
294                                                ic->proto_data, who );
295        }
296       
297        purple_conv_im_send( purple_conversation_get_im_data( conv ), message );
298       
299        return 1;
300}
301
302static GList *purple_away_states( struct im_connection *ic )
303{
304        PurpleAccount *pa = ic->proto_data;
305        GList *st, *ret = NULL;
306       
307        for( st = purple_account_get_status_types( pa ); st; st = st->next )
308        {
309                PurpleStatusPrimitive prim = purple_status_type_get_primitive( st->data );
310                if( prim != PURPLE_STATUS_AVAILABLE && prim != PURPLE_STATUS_OFFLINE )
311                        ret = g_list_append( ret, (void*) purple_status_type_get_name( st->data ) );
312        }
313       
314        return ret;
315}
316
317static void purple_set_away( struct im_connection *ic, char *state_txt, char *message )
318{
319        PurpleAccount *pa = ic->proto_data;
320        GList *status_types = purple_account_get_status_types( pa ), *st;
321        PurpleStatusType *pst = NULL;
322        GList *args = NULL;
323       
324        for( st = status_types; st; st = st->next )
325        {
326                pst = st->data;
327               
328                if( state_txt == NULL &&
329                    purple_status_type_get_primitive( pst ) == PURPLE_STATUS_AVAILABLE )
330                        break;
331
332                if( state_txt != NULL &&
333                    g_strcasecmp( state_txt, purple_status_type_get_name( pst ) ) == 0 )
334                        break;
335        }
336       
337        if( message && purple_status_type_get_attr( pst, "message" ) )
338        {
339                args = g_list_append( args, "message" );
340                args = g_list_append( args, message );
341        }
342       
343        purple_account_set_status_list( pa, st ? purple_status_type_get_id( pst ) : "away",
344                                        TRUE, args );
345
346        g_list_free( args );
347}
348
349static char *set_eval_display_name( set_t *set, char *value )
350{
351        account_t *acc = set->data;
352        struct im_connection *ic = acc->ic;
353       
354        return NULL;
355}
356
357static void purple_add_buddy( struct im_connection *ic, char *who, char *group )
358{
359        PurpleBuddy *pb;
360       
361        pb = purple_buddy_new( (PurpleAccount*) ic->proto_data, who, NULL );
362        purple_blist_add_buddy( pb, NULL, NULL, NULL );
363        purple_account_add_buddy( (PurpleAccount*) ic->proto_data, pb );
364}
365
366static void purple_remove_buddy( struct im_connection *ic, char *who, char *group )
367{
368        PurpleBuddy *pb;
369       
370        pb = purple_find_buddy( (PurpleAccount*) ic->proto_data, who );
371        if( pb != NULL )
372        {
373                purple_account_remove_buddy( (PurpleAccount*) ic->proto_data, pb, NULL );
374                purple_blist_remove_buddy( pb );
375        }
376}
377
378static void purple_add_permit( struct im_connection *ic, char *who )
379{
380        PurpleAccount *pa = ic->proto_data;
381       
382        purple_privacy_permit_add( pa, who, FALSE );
383}
384
385static void purple_add_deny( struct im_connection *ic, char *who )
386{
387        PurpleAccount *pa = ic->proto_data;
388       
389        purple_privacy_deny_add( pa, who, FALSE );
390}
391
392static void purple_rem_permit( struct im_connection *ic, char *who )
393{
394        PurpleAccount *pa = ic->proto_data;
395       
396        purple_privacy_permit_remove( pa, who, FALSE );
397}
398
399static void purple_rem_deny( struct im_connection *ic, char *who )
400{
401        PurpleAccount *pa = ic->proto_data;
402       
403        purple_privacy_deny_remove( pa, who, FALSE );
404}
405
406static void purple_get_info( struct im_connection *ic, char *who )
407{
408        serv_get_info( purple_account_get_connection( ic->proto_data ), who );
409}
410
411static void purple_keepalive( struct im_connection *ic )
412{
413}
414
415static int purple_send_typing( struct im_connection *ic, char *who, int flags )
416{
417        PurpleTypingState state = PURPLE_NOT_TYPING;
418        PurpleConversation *conv;
419       
420        if( flags & OPT_TYPING )
421                state = PURPLE_TYPING;
422        else if( flags & OPT_THINKING )
423                state = PURPLE_TYPED;
424       
425        if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM,
426                                                            who, ic->proto_data ) ) == NULL )
427        {
428                purple_conv_im_set_typing_state( purple_conversation_get_im_data( conv ), state );
429                return 1;
430        }
431        else
432        {
433                return 0;
434        }
435}
436
437static void purple_chat_msg( struct groupchat *gc, char *message, int flags )
438{
439        PurpleConversation *pc = gc->data;
440       
441        purple_conv_chat_send( purple_conversation_get_chat_data( pc ), message );
442}
443
444struct groupchat *purple_chat_with( struct im_connection *ic, char *who )
445{
446        /* No, "of course" this won't work this way. Or in fact, it almost
447           does, but it only lets you send msgs to it, you won't receive
448           any. Instead, we have to click the virtual menu item.
449        PurpleAccount *pa = ic->proto_data;
450        PurpleConversation *pc;
451        PurpleConvChat *pcc;
452        struct groupchat *gc;
453       
454        gc = imcb_chat_new( ic, "BitlBee-libpurple groupchat" );
455        gc->data = pc = purple_conversation_new( PURPLE_CONV_TYPE_CHAT, pa, "BitlBee-libpurple groupchat" );
456        pc->ui_data = gc;
457       
458        pcc = PURPLE_CONV_CHAT( pc );
459        purple_conv_chat_add_user( pcc, ic->acc->user, "", 0, TRUE );
460        purple_conv_chat_invite_user( pcc, who, "Please join my chat", FALSE );
461        //purple_conv_chat_add_user( pcc, who, "", 0, TRUE );
462        */
463       
464        /* There went my nice afternoon. :-( */
465       
466        PurpleAccount *pa = ic->proto_data;
467        PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id );
468        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
469        PurpleBuddy *pb = purple_find_buddy( (PurpleAccount*) ic->proto_data, who );
470        PurpleMenuAction *mi;
471        GList *menu;
472        void (*callback)(PurpleBlistNode *, gpointer); /* FFFFFFFFFFFFFUUUUUUUUUUUUUU */
473       
474        if( !pb || !pi || !pi->blist_node_menu )
475                return NULL;
476       
477        menu = pi->blist_node_menu( &pb->node );
478        while( menu )
479        {
480                mi = menu->data;
481                if( purple_menu_cmp( mi->label, "initiate chat" ) ||
482                    purple_menu_cmp( mi->label, "initiate conference" ) )
483                        break;
484                menu = menu->next;
485        }
486       
487        if( menu == NULL )
488                return NULL;
489       
490        /* Call the fucker. */
491        callback = (void*) mi->callback;
492        callback( &pb->node, menu->data );
493       
494        return NULL;
495}
496
497void purple_chat_invite( struct groupchat *gc, char *who, char *message )
498{
499        PurpleConversation *pc = gc->data;
500        PurpleConvChat *pcc = PURPLE_CONV_CHAT( pc );
501       
502        serv_chat_invite( purple_account_get_connection( gc->ic->proto_data ),
503                          purple_conv_chat_get_id( pcc ), 
504                          message && *message ? message : "Please join my chat",
505                          who );
506}
507
508void purple_chat_leave( struct groupchat *gc )
509{
510        PurpleConversation *pc = gc->data;
511       
512        purple_conversation_destroy( pc );
513}
514
515struct groupchat *purple_chat_join( struct im_connection *ic, const char *room, const char *nick, const char *password )
516{
517        PurpleAccount *pa = ic->proto_data;
518        PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id );
519        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
520        GHashTable *chat_hash;
521        PurpleConversation *conv;
522        GList *info, *l;
523       
524        if( !pi->chat_info || !pi->chat_info_defaults ||
525            !( info = pi->chat_info( purple_account_get_connection( pa ) ) ) )
526        {
527                imcb_error( ic, "Joining chatrooms not supported by this protocol" );
528                return NULL;
529        }
530       
531        if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_CHAT, room, pa ) ) )
532                purple_conversation_destroy( conv );
533       
534        chat_hash = pi->chat_info_defaults( purple_account_get_connection( pa ), room );
535       
536        for( l = info; l; l = l->next )
537        {
538                struct proto_chat_entry *pce = l->data;
539               
540                if( strcmp( pce->identifier, "handle" ) == 0 )
541                        g_hash_table_replace( chat_hash, "handle", g_strdup( nick ) );
542                else if( strcmp( pce->identifier, "password" ) == 0 )
543                        g_hash_table_replace( chat_hash, "password", g_strdup( password ) );
544                else if( strcmp( pce->identifier, "passwd" ) == 0 )
545                        g_hash_table_replace( chat_hash, "passwd", g_strdup( password ) );
546        }
547       
548        serv_join_chat( purple_account_get_connection( pa ), chat_hash );
549       
550        return NULL;
551}
552
553void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle );
554
555static void purple_ui_init();
556
557GHashTable *prplcb_ui_info()
558{
559        static GHashTable *ret;
560       
561        if( ret == NULL )
562        {
563                ret = g_hash_table_new(g_str_hash, g_str_equal);
564                g_hash_table_insert( ret, "name", "BitlBee" );
565                g_hash_table_insert( ret, "version", BITLBEE_VERSION );
566        }
567       
568        return ret;
569}
570
571static PurpleCoreUiOps bee_core_uiops = 
572{
573        NULL,
574        NULL,
575        purple_ui_init,
576        NULL,
577        prplcb_ui_info,
578};
579
580static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t step, size_t step_count )
581{
582        struct im_connection *ic = purple_ic_by_gc( gc );
583       
584        imcb_log( ic, "%s", text );
585}
586
587static void prplcb_conn_connected( PurpleConnection *gc )
588{
589        struct im_connection *ic = purple_ic_by_gc( gc );
590        const char *dn;
591        set_t *s;
592       
593        imcb_connected( ic );
594       
595        if( ( dn = purple_connection_get_display_name( gc ) ) &&
596            ( s = set_find( &ic->acc->set, "display_name" ) ) )
597        {
598                g_free( s->value );
599                s->value = g_strdup( dn );
600        }
601       
602        if( gc->flags & PURPLE_CONNECTION_HTML )
603                ic->flags |= OPT_DOES_HTML;
604}
605
606static void prplcb_conn_disconnected( PurpleConnection *gc )
607{
608        struct im_connection *ic = purple_ic_by_gc( gc );
609       
610        if( ic != NULL )
611        {
612                imc_logout( ic, !gc->wants_to_die );
613        }
614}
615
616static void prplcb_conn_notice( PurpleConnection *gc, const char *text )
617{
618        struct im_connection *ic = purple_ic_by_gc( gc );
619       
620        if( ic != NULL )
621                imcb_log( ic, "%s", text );
622}
623
624static void prplcb_conn_report_disconnect_reason( PurpleConnection *gc, PurpleConnectionError reason, const char *text )
625{
626        struct im_connection *ic = purple_ic_by_gc( gc );
627       
628        /* PURPLE_CONNECTION_ERROR_NAME_IN_USE means concurrent login,
629           should probably handle that. */
630        if( ic != NULL )
631                imcb_error( ic, "%s", text );
632}
633
634static PurpleConnectionUiOps bee_conn_uiops =
635{
636        prplcb_conn_progress,
637        prplcb_conn_connected,
638        prplcb_conn_disconnected,
639        prplcb_conn_notice,
640        NULL,
641        NULL,
642        NULL,
643        prplcb_conn_report_disconnect_reason,
644};
645
646static void prplcb_blist_new( PurpleBlistNode *node )
647{
648        PurpleBuddy *bud = (PurpleBuddy*) node;
649       
650        if( node->type == PURPLE_BLIST_BUDDY_NODE )
651        {
652                struct im_connection *ic = purple_ic_by_pa( bud->account );
653               
654                if( ic == NULL )
655                        return;
656               
657                imcb_add_buddy( ic, bud->name, NULL );
658                if( bud->server_alias )
659                {
660                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
661                        imcb_buddy_nick_hint( ic, bud->name, bud->server_alias );
662                }
663        }
664}
665
666static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node )
667{
668        PurpleBuddy *bud = (PurpleBuddy*) node;
669       
670        if( node->type == PURPLE_BLIST_BUDDY_NODE )
671        {
672                struct im_connection *ic = purple_ic_by_pa( bud->account );
673                PurpleStatus *as;
674                int flags = 0;
675               
676                if( ic == NULL )
677                        return;
678               
679                if( bud->server_alias )
680                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
681               
682                flags |= purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0;
683                flags |= purple_presence_is_available( bud->presence ) ? 0 : OPT_AWAY;
684               
685                as = purple_presence_get_active_status( bud->presence );
686               
687                imcb_buddy_status( ic, bud->name, flags, purple_status_get_name( as ),
688                                   purple_status_get_attr_string( as, "message" ) );
689               
690                imcb_buddy_times( ic, bud->name,
691                                  purple_presence_get_login_time( bud->presence ),
692                                  purple_presence_get_idle_time( bud->presence ) );
693        }
694}
695
696static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node )
697{
698        /*
699        PurpleBuddy *bud = (PurpleBuddy*) node;
700       
701        if( node->type == PURPLE_BLIST_BUDDY_NODE )
702        {
703                struct im_connection *ic = purple_ic_by_pa( bud->account );
704               
705                if( ic == NULL )
706                        return;
707               
708                imcb_remove_buddy( ic, bud->name, NULL );
709        }
710        */
711}
712
713static PurpleBlistUiOps bee_blist_uiops =
714{
715        NULL,
716        prplcb_blist_new,
717        NULL,
718        prplcb_blist_update,
719        prplcb_blist_remove,
720};
721
722void prplcb_conv_new( PurpleConversation *conv )
723{
724        if( conv->type == PURPLE_CONV_TYPE_CHAT )
725        {
726                struct im_connection *ic = purple_ic_by_pa( conv->account );
727                struct groupchat *gc;
728               
729                gc = imcb_chat_new( ic, conv->name );
730                conv->ui_data = gc;
731                gc->data = conv;
732               
733                /* libpurple brokenness: Whatever. Show that we join right away,
734                   there's no clear "This is you!" signaling in _add_users so
735                   don't even try. */
736                imcb_chat_add_buddy( gc, gc->ic->acc->user );
737        }
738}
739
740void prplcb_conv_free( PurpleConversation *conv )
741{
742        struct groupchat *gc = conv->ui_data;
743       
744        imcb_chat_free( gc );
745}
746
747void prplcb_conv_add_users( PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals )
748{
749        struct groupchat *gc = conv->ui_data;
750        GList *b;
751       
752        for( b = cbuddies; b; b = b->next )
753        {
754                PurpleConvChatBuddy *pcb = b->data;
755               
756                imcb_chat_add_buddy( gc, pcb->name );
757        }
758}
759
760void prplcb_conv_del_users( PurpleConversation *conv, GList *cbuddies )
761{
762        struct groupchat *gc = conv->ui_data;
763        GList *b;
764       
765        for( b = cbuddies; b; b = b->next )
766                imcb_chat_remove_buddy( gc, b->data, "" );
767}
768
769void prplcb_conv_chat_msg( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime )
770{
771        struct groupchat *gc = conv->ui_data;
772        PurpleBuddy *buddy;
773       
774        /* ..._SEND means it's an outgoing message, no need to echo those. */
775        if( flags & PURPLE_MESSAGE_SEND )
776                return;
777       
778        buddy = purple_find_buddy( conv->account, who );
779        if( buddy != NULL )
780                who = purple_buddy_get_name( buddy );
781       
782        imcb_chat_msg( gc, who, (char*) message, 0, mtime );
783}
784
785static void prplcb_conv_im( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime )
786{
787        struct im_connection *ic = purple_ic_by_pa( conv->account );
788        PurpleBuddy *buddy;
789       
790        /* ..._SEND means it's an outgoing message, no need to echo those. */
791        if( flags & PURPLE_MESSAGE_SEND )
792                return;
793       
794        buddy = purple_find_buddy( conv->account, who );
795        if( buddy != NULL )
796                who = purple_buddy_get_name( buddy );
797       
798        imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime );
799}
800
801static PurpleConversationUiOps bee_conv_uiops = 
802{
803        prplcb_conv_new,           /* create_conversation  */
804        prplcb_conv_free,          /* destroy_conversation */
805        prplcb_conv_chat_msg,      /* write_chat           */
806        prplcb_conv_im,            /* write_im             */
807        NULL,                      /* write_conv           */
808        prplcb_conv_add_users,     /* chat_add_users       */
809        NULL,                      /* chat_rename_user     */
810        prplcb_conv_del_users,     /* chat_remove_users    */
811        NULL,                      /* chat_update_user     */
812        NULL,                      /* present              */
813        NULL,                      /* has_focus            */
814        NULL,                      /* custom_smiley_add    */
815        NULL,                      /* custom_smiley_write  */
816        NULL,                      /* custom_smiley_close  */
817        NULL,                      /* send_confirm         */
818};
819
820struct prplcb_request_action_data
821{
822        void *user_data, *bee_data;
823        PurpleRequestActionCb yes, no;
824        int yes_i, no_i;
825};
826
827static void prplcb_request_action_yes( void *data )
828{
829        struct prplcb_request_action_data *pqad = data;
830       
831        pqad->yes( pqad->user_data, pqad->yes_i );
832        g_free( pqad );
833}
834
835static void prplcb_request_action_no( void *data )
836{
837        struct prplcb_request_action_data *pqad = data;
838       
839        pqad->no( pqad->user_data, pqad->no_i );
840        g_free( pqad );
841}
842
843static void *prplcb_request_action( const char *title, const char *primary, const char *secondary,
844                                    int default_action, PurpleAccount *account, const char *who,
845                                    PurpleConversation *conv, void *user_data, size_t action_count,
846                                    va_list actions )
847{
848        struct prplcb_request_action_data *pqad; 
849        int i;
850        char *q;
851       
852        pqad = g_new0( struct prplcb_request_action_data, 1 );
853       
854        for( i = 0; i < action_count; i ++ )
855        {
856                char *caption;
857                void *fn;
858               
859                caption = va_arg( actions, char* );
860                fn = va_arg( actions, void* );
861               
862                if( strstr( caption, "Accept" ) )
863                {
864                        pqad->yes = fn;
865                        pqad->yes_i = i;
866                }
867                else if( strstr( caption, "Reject" ) || strstr( caption, "Cancel" ) )
868                {
869                        pqad->no = fn;
870                        pqad->no_i = i;
871                }
872        }
873       
874        pqad->user_data = user_data;
875       
876        /* TODO: IRC stuff here :-( */
877        q = g_strdup_printf( "Request: %s\n\n%s\n\n%s", title, primary, secondary );
878        pqad->bee_data = query_add( local_bee->ui_data, purple_ic_by_pa( account ), q,
879                prplcb_request_action_yes, prplcb_request_action_no, g_free, pqad );
880       
881        g_free( q );
882       
883        return pqad;
884}
885
886/*
887static void prplcb_request_test()
888{
889        fprintf( stderr, "bla\n" );
890}
891*/
892
893static PurpleRequestUiOps bee_request_uiops =
894{
895        NULL,
896        NULL,
897        prplcb_request_action,
898        NULL,
899        NULL,
900        NULL,
901        NULL,
902};
903
904static void prplcb_privacy_permit_added( PurpleAccount *account, const char *name )
905{
906        struct im_connection *ic = purple_ic_by_pa( account );
907       
908        if( !g_slist_find_custom( ic->permit, name, (GCompareFunc) ic->acc->prpl->handle_cmp ) )
909                ic->permit = g_slist_prepend( ic->permit, g_strdup( name ) );
910}
911
912static void prplcb_privacy_permit_removed( PurpleAccount *account, const char *name )
913{
914        struct im_connection *ic = purple_ic_by_pa( account );
915        void *n;
916       
917        n = g_slist_find_custom( ic->permit, name, (GCompareFunc) ic->acc->prpl->handle_cmp );
918        ic->permit = g_slist_remove( ic->permit, n );
919}
920
921static void prplcb_privacy_deny_added( PurpleAccount *account, const char *name )
922{
923        struct im_connection *ic = purple_ic_by_pa( account );
924       
925        if( !g_slist_find_custom( ic->deny, name, (GCompareFunc) ic->acc->prpl->handle_cmp ) )
926                ic->deny = g_slist_prepend( ic->deny, g_strdup( name ) );
927}
928
929static void prplcb_privacy_deny_removed( PurpleAccount *account, const char *name )
930{
931        struct im_connection *ic = purple_ic_by_pa( account );
932        void *n;
933       
934        n = g_slist_find_custom( ic->deny, name, (GCompareFunc) ic->acc->prpl->handle_cmp );
935        ic->deny = g_slist_remove( ic->deny, n );
936}
937
938static PurplePrivacyUiOps bee_privacy_uiops =
939{
940        prplcb_privacy_permit_added,
941        prplcb_privacy_permit_removed,
942        prplcb_privacy_deny_added,
943        prplcb_privacy_deny_removed,
944};
945
946static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s )
947{
948        fprintf( stderr, "DEBUG %s: %s", category, arg_s );
949}
950
951static PurpleDebugUiOps bee_debug_uiops =
952{
953        prplcb_debug_print,
954};
955
956static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer udata )
957{
958        return b_timeout_add( interval, (b_event_handler) func, udata );
959}
960
961static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata )
962{
963        return b_input_add( fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata );
964}
965
966static gboolean prplcb_ev_remove( guint id )
967{
968        b_event_remove( (gint) id );
969        return TRUE;
970}
971
972static PurpleEventLoopUiOps glib_eventloops = 
973{
974        prplcb_ev_timeout_add,
975        prplcb_ev_remove,
976        prplcb_ev_input_add,
977        prplcb_ev_remove,
978};
979
980static void *prplcb_notify_email( PurpleConnection *gc, const char *subject, const char *from,
981                                  const char *to, const char *url )
982{
983        struct im_connection *ic = purple_ic_by_gc( gc );
984       
985        imcb_log( ic, "Received e-mail from %s for %s: %s <%s>", from, to, subject, url );
986       
987        return NULL;
988}
989
990static void *prplcb_notify_userinfo( PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info )
991{
992        struct im_connection *ic = purple_ic_by_gc( gc );
993        GString *info = g_string_new( "" );
994        GList *l = purple_notify_user_info_get_entries( user_info );
995        char *key;
996        const char *value;
997        int n;
998       
999        while( l )
1000        {
1001                PurpleNotifyUserInfoEntry *e = l->data;
1002               
1003                switch( purple_notify_user_info_entry_get_type( e ) )
1004                {
1005                case PURPLE_NOTIFY_USER_INFO_ENTRY_PAIR:
1006                case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER:
1007                        key = g_strdup( purple_notify_user_info_entry_get_label( e ) );
1008                        value = purple_notify_user_info_entry_get_value( e );
1009                       
1010                        if( key )
1011                        {
1012                                strip_html( key );
1013                                g_string_append_printf( info, "%s: ", key );
1014                               
1015                                if( value )
1016                                {
1017                                        n = strlen( value ) - 1;
1018                                        while( isspace( value[n] ) )
1019                                                n --;
1020                                        g_string_append_len( info, value, n + 1 );
1021                                }
1022                                g_string_append_c( info, '\n' );
1023                                g_free( key );
1024                        }
1025                       
1026                        break;
1027                case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK:
1028                        g_string_append( info, "------------------------\n" );
1029                        break;
1030                }
1031               
1032                l = l->next;
1033        }
1034       
1035        imcb_log( ic, "User %s info:\n%s", who, info->str );
1036        g_string_free( info, TRUE );
1037       
1038        return NULL;
1039}
1040
1041static PurpleNotifyUiOps bee_notify_uiops =
1042{
1043        NULL,
1044        prplcb_notify_email,
1045        NULL,
1046        NULL,
1047        NULL,
1048        NULL,
1049        prplcb_notify_userinfo,
1050};
1051
1052static void *prplcb_account_request_authorize( PurpleAccount *account, const char *remote_user,
1053        const char *id, const char *alias, const char *message, gboolean on_list,
1054        PurpleAccountRequestAuthorizationCb authorize_cb, PurpleAccountRequestAuthorizationCb deny_cb, void *user_data )
1055{
1056        struct im_connection *ic = purple_ic_by_pa( account );
1057        char *q;
1058       
1059        if( alias )
1060                q = g_strdup_printf( "%s (%s) wants to add you to his/her contact "
1061                                     "list. (%s)", alias, remote_user, message );
1062        else
1063                q = g_strdup_printf( "%s wants to add you to his/her contact "
1064                                     "list. (%s)", remote_user, message );
1065       
1066        imcb_ask_with_free( ic, q, user_data, authorize_cb, deny_cb, NULL );
1067        g_free( q );
1068}
1069
1070static PurpleAccountUiOps bee_account_uiops =
1071{
1072        NULL,
1073        NULL,
1074        NULL,
1075        prplcb_account_request_authorize,
1076        NULL,
1077};
1078
1079extern PurpleXferUiOps bee_xfer_uiops;
1080
1081static void purple_ui_init()
1082{
1083        purple_connections_set_ui_ops( &bee_conn_uiops );
1084        purple_blist_set_ui_ops( &bee_blist_uiops );
1085        purple_conversations_set_ui_ops( &bee_conv_uiops );
1086        purple_request_set_ui_ops( &bee_request_uiops );
1087        purple_privacy_set_ui_ops( &bee_privacy_uiops );
1088        purple_notify_set_ui_ops( &bee_notify_uiops );
1089        purple_accounts_set_ui_ops( &bee_account_uiops );
1090        purple_xfers_set_ui_ops( &bee_xfer_uiops );
1091       
1092        if( getenv( "BITLBEE_DEBUG" ) )
1093                purple_debug_set_ui_ops( &bee_debug_uiops );
1094}
1095
1096void purple_initmodule()
1097{
1098        struct prpl funcs;
1099        GList *prots;
1100        GString *help;
1101       
1102        if( B_EV_IO_READ != PURPLE_INPUT_READ ||
1103            B_EV_IO_WRITE != PURPLE_INPUT_WRITE )
1104        {
1105                /* FIXME FIXME FIXME FIXME FIXME :-) */
1106                exit( 1 );
1107        }
1108       
1109        purple_util_set_user_dir( "/tmp" );
1110        purple_debug_set_enabled( FALSE );
1111        purple_core_set_ui_ops( &bee_core_uiops );
1112        purple_eventloop_set_ui_ops( &glib_eventloops );
1113        if( !purple_core_init( "BitlBee") )
1114        {
1115                /* Initializing the core failed. Terminate. */
1116                fprintf( stderr, "libpurple initialization failed.\n" );
1117                abort();
1118        }
1119       
1120        /* This seems like stateful shit we don't want... */
1121        purple_set_blist( purple_blist_new() );
1122        purple_blist_load();
1123       
1124        /* Meh? */
1125        purple_prefs_load();
1126       
1127        memset( &funcs, 0, sizeof( funcs ) );
1128        funcs.login = purple_login;
1129        funcs.init = purple_init;
1130        funcs.logout = purple_logout;
1131        funcs.buddy_msg = purple_buddy_msg;
1132        funcs.away_states = purple_away_states;
1133        funcs.set_away = purple_set_away;
1134        funcs.add_buddy = purple_add_buddy;
1135        funcs.remove_buddy = purple_remove_buddy;
1136        funcs.add_permit = purple_add_permit;
1137        funcs.add_deny = purple_add_deny;
1138        funcs.rem_permit = purple_rem_permit;
1139        funcs.rem_deny = purple_rem_deny;
1140        funcs.get_info = purple_get_info;
1141        funcs.keepalive = purple_keepalive;
1142        funcs.send_typing = purple_send_typing;
1143        funcs.handle_cmp = g_strcasecmp;
1144        /* TODO(wilmer): Set these only for protocols that support them? */
1145        funcs.chat_msg = purple_chat_msg;
1146        funcs.chat_with = purple_chat_with;
1147        funcs.chat_invite = purple_chat_invite;
1148        funcs.chat_leave = purple_chat_leave;
1149        funcs.chat_join = purple_chat_join;
1150        funcs.transfer_request = purple_transfer_request;
1151       
1152        help = g_string_new( "BitlBee libpurple module supports the following IM protocols:\n" );
1153       
1154        /* Add a protocol entry to BitlBee's structures for every protocol
1155           supported by this libpurple instance. */     
1156        for( prots = purple_plugins_get_protocols(); prots; prots = prots->next )
1157        {
1158                PurplePlugin *prot = prots->data;
1159                struct prpl *ret;
1160               
1161                ret = g_memdup( &funcs, sizeof( funcs ) );
1162                ret->name = ret->data = prot->info->id;
1163                if( strncmp( ret->name, "prpl-", 5 ) == 0 )
1164                        ret->name += 5;
1165                register_protocol( ret );
1166               
1167                g_string_append_printf( help, "\n* %s (%s)", ret->name, prot->info->name );
1168               
1169                /* libpurple doesn't define a protocol called OSCAR, but we
1170                   need it to be compatible with normal BitlBee. */
1171                if( g_strcasecmp( prot->info->id, "prpl-aim" ) == 0 )
1172                {
1173                        ret = g_memdup( &funcs, sizeof( funcs ) );
1174                        ret->name = "oscar";
1175                        ret->data = prot->info->id;
1176                        register_protocol( ret );
1177                }
1178        }
1179       
1180        g_string_append( help, "\n\nFor used protocols, more information about available "
1181                         "settings can be found using \x02help purple <protocol name>\x02" );
1182       
1183        /* Add a simple dynamically-generated help item listing all
1184           the supported protocols. */
1185        help_add_mem( &global.help, "purple", help->str );
1186        g_string_free( help, TRUE );
1187}
Note: See TracBrowser for help on using the repository browser.