source: protocols/purple/purple.c @ 1e52e1f

Last change on this file since 1e52e1f was 1e52e1f, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-11T10:30:27Z

When cleaning up queries, q->data is free()d. Even if it turns out to be
the "struct irc" containing all data belonging to a session. Sanitise
memory management a little bit here. (There are some memory leaks in here
too that need to be fixed at some point.)

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