source: protocols/purple/purple.c @ 4aa0f6b

Last change on this file since 4aa0f6b was 4aa0f6b, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-06-07T14:31:07Z

Merging killerbee stuff, bringing all the bleeding-edge stuff together.

  • Property mode set to 100644
File size: 31.6 KB
Line 
1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  libpurple module - Main file                                             *
5*                                                                           *
6*  Copyright 2009-2010 Wilmer van der Gaast <wilmer@gaast.net>              *
7*                                                                           *
8*  This program is free software; you can redistribute it and/or modify     *
9*  it under the terms of the GNU General Public License as published by     *
10*  the Free Software Foundation; either version 2 of the License, or        *
11*  (at your option) any later version.                                      *
12*                                                                           *
13*  This program is distributed in the hope that it will be useful,          *
14*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
15*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
16*  GNU General Public License for more details.                             *
17*                                                                           *
18*  You should have received a copy of the GNU General Public License along  *
19*  with this program; if not, write to the Free Software Foundation, Inc.,  *
20*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
21*                                                                           *
22\***************************************************************************/
23
24#include "bitlbee.h"
25#include "help.h"
26
27#include <stdarg.h>
28
29#include <glib.h>
30#include <purple.h>
31
32GSList *purple_connections;
33
34/* This makes me VERY sad... :-( But some libpurple callbacks come in without
35   any context so this is the only way to get that. Don't want to support
36   libpurple in daemon mode anyway. */
37static 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}
691
692static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node )
693{
694        /*
695        PurpleBuddy *bud = (PurpleBuddy*) node;
696       
697        if( node->type == PURPLE_BLIST_BUDDY_NODE )
698        {
699                struct im_connection *ic = purple_ic_by_pa( bud->account );
700               
701                if( ic == NULL )
702                        return;
703               
704                imcb_remove_buddy( ic, bud->name, NULL );
705        }
706        */
707}
708
709static PurpleBlistUiOps bee_blist_uiops =
710{
711        NULL,
712        prplcb_blist_new,
713        NULL,
714        prplcb_blist_update,
715        prplcb_blist_remove,
716};
717
718void prplcb_conv_new( PurpleConversation *conv )
719{
720        if( conv->type == PURPLE_CONV_TYPE_CHAT )
721        {
722                struct im_connection *ic = purple_ic_by_pa( conv->account );
723                struct groupchat *gc;
724               
725                gc = imcb_chat_new( ic, conv->name );
726                conv->ui_data = gc;
727                gc->data = conv;
728               
729                /* libpurple brokenness: Whatever. Show that we join right away,
730                   there's no clear "This is you!" signaling in _add_users so
731                   don't even try. */
732                imcb_chat_add_buddy( gc, gc->ic->acc->user );
733        }
734}
735
736void prplcb_conv_free( PurpleConversation *conv )
737{
738        struct groupchat *gc = conv->ui_data;
739       
740        imcb_chat_free( gc );
741}
742
743void prplcb_conv_add_users( PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals )
744{
745        struct groupchat *gc = conv->ui_data;
746        GList *b;
747       
748        for( b = cbuddies; b; b = b->next )
749        {
750                PurpleConvChatBuddy *pcb = b->data;
751               
752                imcb_chat_add_buddy( gc, pcb->name );
753        }
754}
755
756void prplcb_conv_del_users( PurpleConversation *conv, GList *cbuddies )
757{
758        struct groupchat *gc = conv->ui_data;
759        GList *b;
760       
761        for( b = cbuddies; b; b = b->next )
762                imcb_chat_remove_buddy( gc, b->data, "" );
763}
764
765void prplcb_conv_chat_msg( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime )
766{
767        struct groupchat *gc = conv->ui_data;
768        PurpleBuddy *buddy;
769       
770        /* ..._SEND means it's an outgoing message, no need to echo those. */
771        if( flags & PURPLE_MESSAGE_SEND )
772                return;
773       
774        buddy = purple_find_buddy( conv->account, who );
775        if( buddy != NULL )
776                who = purple_buddy_get_name( buddy );
777       
778        imcb_chat_msg( gc, who, (char*) message, 0, mtime );
779}
780
781static void prplcb_conv_im( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime )
782{
783        struct im_connection *ic = purple_ic_by_pa( conv->account );
784        PurpleBuddy *buddy;
785       
786        /* ..._SEND means it's an outgoing message, no need to echo those. */
787        if( flags & PURPLE_MESSAGE_SEND )
788                return;
789       
790        buddy = purple_find_buddy( conv->account, who );
791        if( buddy != NULL )
792                who = purple_buddy_get_name( buddy );
793       
794        imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime );
795}
796
797static PurpleConversationUiOps bee_conv_uiops = 
798{
799        prplcb_conv_new,           /* create_conversation  */
800        prplcb_conv_free,          /* destroy_conversation */
801        prplcb_conv_chat_msg,      /* write_chat           */
802        prplcb_conv_im,            /* write_im             */
803        NULL,                      /* write_conv           */
804        prplcb_conv_add_users,     /* chat_add_users       */
805        NULL,                      /* chat_rename_user     */
806        prplcb_conv_del_users,     /* chat_remove_users    */
807        NULL,                      /* chat_update_user     */
808        NULL,                      /* present              */
809        NULL,                      /* has_focus            */
810        NULL,                      /* custom_smiley_add    */
811        NULL,                      /* custom_smiley_write  */
812        NULL,                      /* custom_smiley_close  */
813        NULL,                      /* send_confirm         */
814};
815
816struct prplcb_request_action_data
817{
818        void *user_data, *bee_data;
819        PurpleRequestActionCb yes, no;
820        int yes_i, no_i;
821};
822
823static void prplcb_request_action_yes( void *data )
824{
825        struct prplcb_request_action_data *pqad = data;
826       
827        pqad->yes( pqad->user_data, pqad->yes_i );
828        g_free( pqad );
829}
830
831static void prplcb_request_action_no( void *data )
832{
833        struct prplcb_request_action_data *pqad = data;
834       
835        pqad->no( pqad->user_data, pqad->no_i );
836        g_free( pqad );
837}
838
839static void *prplcb_request_action( const char *title, const char *primary, const char *secondary,
840                                    int default_action, PurpleAccount *account, const char *who,
841                                    PurpleConversation *conv, void *user_data, size_t action_count,
842                                    va_list actions )
843{
844        struct prplcb_request_action_data *pqad; 
845        int i;
846        char *q;
847       
848        pqad = g_new0( struct prplcb_request_action_data, 1 );
849       
850        for( i = 0; i < action_count; i ++ )
851        {
852                char *caption;
853                void *fn;
854               
855                caption = va_arg( actions, char* );
856                fn = va_arg( actions, void* );
857               
858                if( strstr( caption, "Accept" ) )
859                {
860                        pqad->yes = fn;
861                        pqad->yes_i = i;
862                }
863                else if( strstr( caption, "Reject" ) || strstr( caption, "Cancel" ) )
864                {
865                        pqad->no = fn;
866                        pqad->no_i = i;
867                }
868        }
869       
870        pqad->user_data = user_data;
871       
872        /* TODO: IRC stuff here :-( */
873        q = g_strdup_printf( "Request: %s\n\n%s\n\n%s", title, primary, secondary );
874        pqad->bee_data = query_add( local_bee->ui_data, purple_ic_by_pa( account ), q,
875                prplcb_request_action_yes, prplcb_request_action_no, pqad );
876       
877        g_free( q );
878       
879        return pqad;
880}
881
882static PurpleRequestUiOps bee_request_uiops =
883{
884        NULL,
885        NULL,
886        prplcb_request_action,
887        NULL,
888        NULL,
889        NULL,
890        NULL,
891};
892
893static void prplcb_privacy_permit_added( PurpleAccount *account, const char *name )
894{
895        struct im_connection *ic = purple_ic_by_pa( account );
896       
897        if( !g_slist_find_custom( ic->permit, name, (GCompareFunc) ic->acc->prpl->handle_cmp ) )
898                ic->permit = g_slist_prepend( ic->permit, g_strdup( name ) );
899}
900
901static void prplcb_privacy_permit_removed( PurpleAccount *account, const char *name )
902{
903        struct im_connection *ic = purple_ic_by_pa( account );
904        void *n;
905       
906        n = g_slist_find_custom( ic->permit, name, (GCompareFunc) ic->acc->prpl->handle_cmp );
907        ic->permit = g_slist_remove( ic->permit, n );
908}
909
910static void prplcb_privacy_deny_added( PurpleAccount *account, const char *name )
911{
912        struct im_connection *ic = purple_ic_by_pa( account );
913       
914        if( !g_slist_find_custom( ic->deny, name, (GCompareFunc) ic->acc->prpl->handle_cmp ) )
915                ic->deny = g_slist_prepend( ic->deny, g_strdup( name ) );
916}
917
918static void prplcb_privacy_deny_removed( PurpleAccount *account, const char *name )
919{
920        struct im_connection *ic = purple_ic_by_pa( account );
921        void *n;
922       
923        n = g_slist_find_custom( ic->deny, name, (GCompareFunc) ic->acc->prpl->handle_cmp );
924        ic->deny = g_slist_remove( ic->deny, n );
925}
926
927static PurplePrivacyUiOps bee_privacy_uiops =
928{
929        prplcb_privacy_permit_added,
930        prplcb_privacy_permit_removed,
931        prplcb_privacy_deny_added,
932        prplcb_privacy_deny_removed,
933};
934
935static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s )
936{
937        fprintf( stderr, "DEBUG %s: %s", category, arg_s );
938}
939
940static PurpleDebugUiOps bee_debug_uiops =
941{
942        prplcb_debug_print,
943};
944
945static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer udata )
946{
947        return b_timeout_add( interval, (b_event_handler) func, udata );
948}
949
950static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata )
951{
952        return b_input_add( fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata );
953}
954
955static gboolean prplcb_ev_remove( guint id )
956{
957        b_event_remove( (gint) id );
958        return TRUE;
959}
960
961static PurpleEventLoopUiOps glib_eventloops = 
962{
963        prplcb_ev_timeout_add,
964        prplcb_ev_remove,
965        prplcb_ev_input_add,
966        prplcb_ev_remove,
967};
968
969static void *prplcb_notify_email( PurpleConnection *gc, const char *subject, const char *from,
970                                  const char *to, const char *url )
971{
972        struct im_connection *ic = purple_ic_by_gc( gc );
973       
974        imcb_log( ic, "Received e-mail from %s for %s: %s <%s>", from, to, subject, url );
975       
976        return NULL;
977}
978
979static void *prplcb_notify_userinfo( PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info )
980{
981        struct im_connection *ic = purple_ic_by_gc( gc );
982        GString *info = g_string_new( "" );
983        GList *l = purple_notify_user_info_get_entries( user_info );
984        char *key;
985        const char *value;
986        int n;
987       
988        while( l )
989        {
990                PurpleNotifyUserInfoEntry *e = l->data;
991               
992                switch( purple_notify_user_info_entry_get_type( e ) )
993                {
994                case PURPLE_NOTIFY_USER_INFO_ENTRY_PAIR:
995                case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER:
996                        key = g_strdup( purple_notify_user_info_entry_get_label( e ) );
997                        value = purple_notify_user_info_entry_get_value( e );
998                       
999                        if( key )
1000                        {
1001                                strip_html( key );
1002                                g_string_append_printf( info, "%s: ", key );
1003                               
1004                                if( value )
1005                                {
1006                                        n = strlen( value ) - 1;
1007                                        while( isspace( value[n] ) )
1008                                                n --;
1009                                        g_string_append_len( info, value, n + 1 );
1010                                }
1011                                g_string_append_c( info, '\n' );
1012                                g_free( key );
1013                        }
1014                       
1015                        break;
1016                case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK:
1017                        g_string_append( info, "------------------------\n" );
1018                        break;
1019                }
1020               
1021                l = l->next;
1022        }
1023       
1024        imcb_log( ic, "User %s info:\n%s", who, info->str );
1025        g_string_free( info, TRUE );
1026       
1027        return NULL;
1028}
1029
1030static PurpleNotifyUiOps bee_notify_uiops =
1031{
1032        NULL,
1033        prplcb_notify_email,
1034        NULL,
1035        NULL,
1036        NULL,
1037        NULL,
1038        prplcb_notify_userinfo,
1039};
1040
1041extern PurpleXferUiOps bee_xfer_uiops;
1042
1043static void purple_ui_init()
1044{
1045        purple_blist_set_ui_ops( &bee_blist_uiops );
1046        purple_connections_set_ui_ops( &bee_conn_uiops );
1047        purple_conversations_set_ui_ops( &bee_conv_uiops );
1048        purple_request_set_ui_ops( &bee_request_uiops );
1049        purple_notify_set_ui_ops( &bee_notify_uiops );
1050        purple_xfers_set_ui_ops( &bee_xfer_uiops );
1051        purple_privacy_set_ui_ops( &bee_privacy_uiops );
1052       
1053        if( getenv( "BITLBEE_DEBUG" ) )
1054                purple_debug_set_ui_ops( &bee_debug_uiops );
1055}
1056
1057void purple_initmodule()
1058{
1059        struct prpl funcs;
1060        GList *prots;
1061        GString *help;
1062       
1063        if( B_EV_IO_READ != PURPLE_INPUT_READ ||
1064            B_EV_IO_WRITE != PURPLE_INPUT_WRITE )
1065        {
1066                /* FIXME FIXME FIXME FIXME FIXME :-) */
1067                exit( 1 );
1068        }
1069       
1070        purple_util_set_user_dir("/tmp");
1071        purple_debug_set_enabled(FALSE);
1072        purple_core_set_ui_ops(&bee_core_uiops);
1073        purple_eventloop_set_ui_ops(&glib_eventloops);
1074        if( !purple_core_init( "BitlBee") )
1075        {
1076                /* Initializing the core failed. Terminate. */
1077                fprintf( stderr, "libpurple initialization failed.\n" );
1078                abort();
1079        }
1080       
1081        /* This seems like stateful shit we don't want... */
1082        purple_set_blist(purple_blist_new());
1083        purple_blist_load();
1084       
1085        /* Meh? */
1086        purple_prefs_load();
1087       
1088        memset( &funcs, 0, sizeof( funcs ) );
1089        funcs.login = purple_login;
1090        funcs.init = purple_init;
1091        funcs.logout = purple_logout;
1092        funcs.buddy_msg = purple_buddy_msg;
1093        funcs.away_states = purple_away_states;
1094        funcs.set_away = purple_set_away;
1095        funcs.add_buddy = purple_add_buddy;
1096        funcs.remove_buddy = purple_remove_buddy;
1097        funcs.add_permit = purple_add_permit;
1098        funcs.add_deny = purple_add_deny;
1099        funcs.rem_permit = purple_rem_permit;
1100        funcs.rem_deny = purple_rem_deny;
1101        funcs.get_info = purple_get_info;
1102        funcs.keepalive = purple_keepalive;
1103        funcs.send_typing = purple_send_typing;
1104        funcs.handle_cmp = g_strcasecmp;
1105        /* TODO(wilmer): Set these only for protocols that support them? */
1106        funcs.chat_msg = purple_chat_msg;
1107        funcs.chat_with = purple_chat_with;
1108        funcs.chat_invite = purple_chat_invite;
1109        funcs.chat_leave = purple_chat_leave;
1110        funcs.chat_join = purple_chat_join;
1111        funcs.transfer_request = purple_transfer_request;
1112       
1113        help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n");
1114       
1115        /* Add a protocol entry to BitlBee's structures for every protocol
1116           supported by this libpurple instance. */     
1117        for( prots = purple_plugins_get_protocols(); prots; prots = prots->next )
1118        {
1119                PurplePlugin *prot = prots->data;
1120                struct prpl *ret;
1121               
1122                ret = g_memdup( &funcs, sizeof( funcs ) );
1123                ret->name = ret->data = prot->info->id;
1124                if( strncmp( ret->name, "prpl-", 5 ) == 0 )
1125                        ret->name += 5;
1126                register_protocol( ret );
1127               
1128                g_string_append_printf( help, "\n* %s (%s)", ret->name, prot->info->name );
1129               
1130                /* libpurple doesn't define a protocol called OSCAR, but we
1131                   need it to be compatible with normal BitlBee. */
1132                if( g_strcasecmp( prot->info->id, "prpl-aim" ) == 0 )
1133                {
1134                        ret = g_memdup( &funcs, sizeof( funcs ) );
1135                        ret->name = "oscar";
1136                        ret->data = prot->info->id;
1137                        register_protocol( ret );
1138                }
1139        }
1140       
1141        g_string_append( help, "\n\nFor used protocols, more information about available "
1142                         "settings can be found using \x02help purple <protocol name>\x02" );
1143       
1144        /* Add a simple dynamically-generated help item listing all
1145           the supported protocols. */
1146        help_add_mem( &global.help, "purple", help->str );
1147        g_string_free( help, TRUE );
1148}
Note: See TracBrowser for help on using the repository browser.