source: protocols/purple/purple.c @ a08e875

Last change on this file since a08e875 was a08e875, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-17T12:08:31Z

libpurple: Read group information of contacts.

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