source: protocols/purple/purple.c @ 177ffd7

Last change on this file since 177ffd7 was 177ffd7, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-17T10:04:51Z

nick_gen() should also insert an underscore if the first character of a nick
would otherwise be a digit.

  • Property mode set to 100644
File size: 31.9 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 void prplcb_request_test()
887{
888        fprintf( stderr, "bla\n" );
889}
890
891static PurpleRequestUiOps bee_request_uiops =
892{
893        prplcb_request_test,
894        prplcb_request_test,
895        prplcb_request_action,
896        prplcb_request_test,
897        prplcb_request_test,
898        prplcb_request_test,
899        prplcb_request_test,
900};
901
902static void prplcb_privacy_permit_added( PurpleAccount *account, const char *name )
903{
904        struct im_connection *ic = purple_ic_by_pa( account );
905       
906        if( !g_slist_find_custom( ic->permit, name, (GCompareFunc) ic->acc->prpl->handle_cmp ) )
907                ic->permit = g_slist_prepend( ic->permit, g_strdup( name ) );
908}
909
910static void prplcb_privacy_permit_removed( PurpleAccount *account, const char *name )
911{
912        struct im_connection *ic = purple_ic_by_pa( account );
913        void *n;
914       
915        n = g_slist_find_custom( ic->permit, name, (GCompareFunc) ic->acc->prpl->handle_cmp );
916        ic->permit = g_slist_remove( ic->permit, n );
917}
918
919static void prplcb_privacy_deny_added( PurpleAccount *account, const char *name )
920{
921        struct im_connection *ic = purple_ic_by_pa( account );
922       
923        if( !g_slist_find_custom( ic->deny, name, (GCompareFunc) ic->acc->prpl->handle_cmp ) )
924                ic->deny = g_slist_prepend( ic->deny, g_strdup( name ) );
925}
926
927static void prplcb_privacy_deny_removed( PurpleAccount *account, const char *name )
928{
929        struct im_connection *ic = purple_ic_by_pa( account );
930        void *n;
931       
932        n = g_slist_find_custom( ic->deny, name, (GCompareFunc) ic->acc->prpl->handle_cmp );
933        ic->deny = g_slist_remove( ic->deny, n );
934}
935
936static PurplePrivacyUiOps bee_privacy_uiops =
937{
938        prplcb_privacy_permit_added,
939        prplcb_privacy_permit_removed,
940        prplcb_privacy_deny_added,
941        prplcb_privacy_deny_removed,
942};
943
944static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s )
945{
946        fprintf( stderr, "DEBUG %s: %s", category, arg_s );
947}
948
949static PurpleDebugUiOps bee_debug_uiops =
950{
951        prplcb_debug_print,
952};
953
954static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer udata )
955{
956        return b_timeout_add( interval, (b_event_handler) func, udata );
957}
958
959static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata )
960{
961        return b_input_add( fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata );
962}
963
964static gboolean prplcb_ev_remove( guint id )
965{
966        b_event_remove( (gint) id );
967        return TRUE;
968}
969
970static PurpleEventLoopUiOps glib_eventloops = 
971{
972        prplcb_ev_timeout_add,
973        prplcb_ev_remove,
974        prplcb_ev_input_add,
975        prplcb_ev_remove,
976};
977
978static void *prplcb_notify_email( PurpleConnection *gc, const char *subject, const char *from,
979                                  const char *to, const char *url )
980{
981        struct im_connection *ic = purple_ic_by_gc( gc );
982       
983        imcb_log( ic, "Received e-mail from %s for %s: %s <%s>", from, to, subject, url );
984       
985        return NULL;
986}
987
988static void *prplcb_notify_userinfo( PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info )
989{
990        struct im_connection *ic = purple_ic_by_gc( gc );
991        GString *info = g_string_new( "" );
992        GList *l = purple_notify_user_info_get_entries( user_info );
993        char *key;
994        const char *value;
995        int n;
996       
997        while( l )
998        {
999                PurpleNotifyUserInfoEntry *e = l->data;
1000               
1001                switch( purple_notify_user_info_entry_get_type( e ) )
1002                {
1003                case PURPLE_NOTIFY_USER_INFO_ENTRY_PAIR:
1004                case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER:
1005                        key = g_strdup( purple_notify_user_info_entry_get_label( e ) );
1006                        value = purple_notify_user_info_entry_get_value( e );
1007                       
1008                        if( key )
1009                        {
1010                                strip_html( key );
1011                                g_string_append_printf( info, "%s: ", key );
1012                               
1013                                if( value )
1014                                {
1015                                        n = strlen( value ) - 1;
1016                                        while( isspace( value[n] ) )
1017                                                n --;
1018                                        g_string_append_len( info, value, n + 1 );
1019                                }
1020                                g_string_append_c( info, '\n' );
1021                                g_free( key );
1022                        }
1023                       
1024                        break;
1025                case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK:
1026                        g_string_append( info, "------------------------\n" );
1027                        break;
1028                }
1029               
1030                l = l->next;
1031        }
1032       
1033        imcb_log( ic, "User %s info:\n%s", who, info->str );
1034        g_string_free( info, TRUE );
1035       
1036        return NULL;
1037}
1038
1039static PurpleNotifyUiOps bee_notify_uiops =
1040{
1041        NULL,
1042        prplcb_notify_email,
1043        NULL,
1044        NULL,
1045        NULL,
1046        NULL,
1047        prplcb_notify_userinfo,
1048};
1049
1050extern PurpleXferUiOps bee_xfer_uiops;
1051
1052static void purple_ui_init()
1053{
1054        purple_blist_set_ui_ops( &bee_blist_uiops );
1055        purple_connections_set_ui_ops( &bee_conn_uiops );
1056        purple_conversations_set_ui_ops( &bee_conv_uiops );
1057        purple_request_set_ui_ops( &bee_request_uiops );
1058        purple_notify_set_ui_ops( &bee_notify_uiops );
1059        purple_xfers_set_ui_ops( &bee_xfer_uiops );
1060        purple_privacy_set_ui_ops( &bee_privacy_uiops );
1061       
1062        if( getenv( "BITLBEE_DEBUG" ) )
1063                purple_debug_set_ui_ops( &bee_debug_uiops );
1064}
1065
1066void purple_initmodule()
1067{
1068        struct prpl funcs;
1069        GList *prots;
1070        GString *help;
1071       
1072        if( B_EV_IO_READ != PURPLE_INPUT_READ ||
1073            B_EV_IO_WRITE != PURPLE_INPUT_WRITE )
1074        {
1075                /* FIXME FIXME FIXME FIXME FIXME :-) */
1076                exit( 1 );
1077        }
1078       
1079        purple_util_set_user_dir("/tmp");
1080        purple_debug_set_enabled(FALSE);
1081        purple_core_set_ui_ops(&bee_core_uiops);
1082        purple_eventloop_set_ui_ops(&glib_eventloops);
1083        if( !purple_core_init( "BitlBee") )
1084        {
1085                /* Initializing the core failed. Terminate. */
1086                fprintf( stderr, "libpurple initialization failed.\n" );
1087                abort();
1088        }
1089       
1090        /* This seems like stateful shit we don't want... */
1091        purple_set_blist(purple_blist_new());
1092        purple_blist_load();
1093       
1094        /* Meh? */
1095        purple_prefs_load();
1096       
1097        memset( &funcs, 0, sizeof( funcs ) );
1098        funcs.login = purple_login;
1099        funcs.init = purple_init;
1100        funcs.logout = purple_logout;
1101        funcs.buddy_msg = purple_buddy_msg;
1102        funcs.away_states = purple_away_states;
1103        funcs.set_away = purple_set_away;
1104        funcs.add_buddy = purple_add_buddy;
1105        funcs.remove_buddy = purple_remove_buddy;
1106        funcs.add_permit = purple_add_permit;
1107        funcs.add_deny = purple_add_deny;
1108        funcs.rem_permit = purple_rem_permit;
1109        funcs.rem_deny = purple_rem_deny;
1110        funcs.get_info = purple_get_info;
1111        funcs.keepalive = purple_keepalive;
1112        funcs.send_typing = purple_send_typing;
1113        funcs.handle_cmp = g_strcasecmp;
1114        /* TODO(wilmer): Set these only for protocols that support them? */
1115        funcs.chat_msg = purple_chat_msg;
1116        funcs.chat_with = purple_chat_with;
1117        funcs.chat_invite = purple_chat_invite;
1118        funcs.chat_leave = purple_chat_leave;
1119        funcs.chat_join = purple_chat_join;
1120        funcs.transfer_request = purple_transfer_request;
1121       
1122        help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n");
1123       
1124        /* Add a protocol entry to BitlBee's structures for every protocol
1125           supported by this libpurple instance. */     
1126        for( prots = purple_plugins_get_protocols(); prots; prots = prots->next )
1127        {
1128                PurplePlugin *prot = prots->data;
1129                struct prpl *ret;
1130               
1131                ret = g_memdup( &funcs, sizeof( funcs ) );
1132                ret->name = ret->data = prot->info->id;
1133                if( strncmp( ret->name, "prpl-", 5 ) == 0 )
1134                        ret->name += 5;
1135                register_protocol( ret );
1136               
1137                g_string_append_printf( help, "\n* %s (%s)", ret->name, prot->info->name );
1138               
1139                /* libpurple doesn't define a protocol called OSCAR, but we
1140                   need it to be compatible with normal BitlBee. */
1141                if( g_strcasecmp( prot->info->id, "prpl-aim" ) == 0 )
1142                {
1143                        ret = g_memdup( &funcs, sizeof( funcs ) );
1144                        ret->name = "oscar";
1145                        ret->data = prot->info->id;
1146                        register_protocol( ret );
1147                }
1148        }
1149       
1150        g_string_append( help, "\n\nFor used protocols, more information about available "
1151                         "settings can be found using \x02help purple <protocol name>\x02" );
1152       
1153        /* Add a simple dynamically-generated help item listing all
1154           the supported protocols. */
1155        help_add_mem( &global.help, "purple", help->str );
1156        g_string_free( help, TRUE );
1157}
Note: See TracBrowser for help on using the repository browser.