source: protocols/purple/purple.c @ 04dc563

Last change on this file since 04dc563 was f85e9d6, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-24T21:24:53Z

Read display names. Setting them is going to be an awesome hack.

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