source: protocols/purple/purple.c @ 6f0ea57

Last change on this file since 6f0ea57 was a91550c, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-17T12:29:51Z

Try to pass a group when removing a contact. No idea what will happen if
a contact is in multiple groups, for now I'm not supporting it.

Also cleaning up query code to avoid calling NULL.

  • Property mode set to 100644
File size: 32.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                PurpleGroup *group;
374               
375                group = purple_buddy_get_group( pb );
376                purple_account_remove_buddy( (PurpleAccount*) ic->proto_data, pb, group );
377               
378                purple_blist_remove_buddy( pb );
379        }
380}
381
382static void purple_add_permit( struct im_connection *ic, char *who )
383{
384        PurpleAccount *pa = ic->proto_data;
385       
386        purple_privacy_permit_add( pa, who, FALSE );
387}
388
389static void purple_add_deny( struct im_connection *ic, char *who )
390{
391        PurpleAccount *pa = ic->proto_data;
392       
393        purple_privacy_deny_add( pa, who, FALSE );
394}
395
396static void purple_rem_permit( struct im_connection *ic, char *who )
397{
398        PurpleAccount *pa = ic->proto_data;
399       
400        purple_privacy_permit_remove( pa, who, FALSE );
401}
402
403static void purple_rem_deny( struct im_connection *ic, char *who )
404{
405        PurpleAccount *pa = ic->proto_data;
406       
407        purple_privacy_deny_remove( pa, who, FALSE );
408}
409
410static void purple_get_info( struct im_connection *ic, char *who )
411{
412        serv_get_info( purple_account_get_connection( ic->proto_data ), who );
413}
414
415static void purple_keepalive( struct im_connection *ic )
416{
417}
418
419static int purple_send_typing( struct im_connection *ic, char *who, int flags )
420{
421        PurpleTypingState state = PURPLE_NOT_TYPING;
422        PurpleConversation *conv;
423       
424        if( flags & OPT_TYPING )
425                state = PURPLE_TYPING;
426        else if( flags & OPT_THINKING )
427                state = PURPLE_TYPED;
428       
429        if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM,
430                                                            who, ic->proto_data ) ) == NULL )
431        {
432                purple_conv_im_set_typing_state( purple_conversation_get_im_data( conv ), state );
433                return 1;
434        }
435        else
436        {
437                return 0;
438        }
439}
440
441static void purple_chat_msg( struct groupchat *gc, char *message, int flags )
442{
443        PurpleConversation *pc = gc->data;
444       
445        purple_conv_chat_send( purple_conversation_get_chat_data( pc ), message );
446}
447
448struct groupchat *purple_chat_with( struct im_connection *ic, char *who )
449{
450        /* No, "of course" this won't work this way. Or in fact, it almost
451           does, but it only lets you send msgs to it, you won't receive
452           any. Instead, we have to click the virtual menu item.
453        PurpleAccount *pa = ic->proto_data;
454        PurpleConversation *pc;
455        PurpleConvChat *pcc;
456        struct groupchat *gc;
457       
458        gc = imcb_chat_new( ic, "BitlBee-libpurple groupchat" );
459        gc->data = pc = purple_conversation_new( PURPLE_CONV_TYPE_CHAT, pa, "BitlBee-libpurple groupchat" );
460        pc->ui_data = gc;
461       
462        pcc = PURPLE_CONV_CHAT( pc );
463        purple_conv_chat_add_user( pcc, ic->acc->user, "", 0, TRUE );
464        purple_conv_chat_invite_user( pcc, who, "Please join my chat", FALSE );
465        //purple_conv_chat_add_user( pcc, who, "", 0, TRUE );
466        */
467       
468        /* There went my nice afternoon. :-( */
469       
470        PurpleAccount *pa = ic->proto_data;
471        PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id );
472        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
473        PurpleBuddy *pb = purple_find_buddy( (PurpleAccount*) ic->proto_data, who );
474        PurpleMenuAction *mi;
475        GList *menu;
476        void (*callback)(PurpleBlistNode *, gpointer); /* FFFFFFFFFFFFFUUUUUUUUUUUUUU */
477       
478        if( !pb || !pi || !pi->blist_node_menu )
479                return NULL;
480       
481        menu = pi->blist_node_menu( &pb->node );
482        while( menu )
483        {
484                mi = menu->data;
485                if( purple_menu_cmp( mi->label, "initiate chat" ) ||
486                    purple_menu_cmp( mi->label, "initiate conference" ) )
487                        break;
488                menu = menu->next;
489        }
490       
491        if( menu == NULL )
492                return NULL;
493       
494        /* Call the fucker. */
495        callback = (void*) mi->callback;
496        callback( &pb->node, menu->data );
497       
498        return NULL;
499}
500
501void purple_chat_invite( struct groupchat *gc, char *who, char *message )
502{
503        PurpleConversation *pc = gc->data;
504        PurpleConvChat *pcc = PURPLE_CONV_CHAT( pc );
505       
506        serv_chat_invite( purple_account_get_connection( gc->ic->proto_data ),
507                          purple_conv_chat_get_id( pcc ), 
508                          message && *message ? message : "Please join my chat",
509                          who );
510}
511
512void purple_chat_leave( struct groupchat *gc )
513{
514        PurpleConversation *pc = gc->data;
515       
516        purple_conversation_destroy( pc );
517}
518
519struct groupchat *purple_chat_join( struct im_connection *ic, const char *room, const char *nick, const char *password )
520{
521        PurpleAccount *pa = ic->proto_data;
522        PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id );
523        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
524        GHashTable *chat_hash;
525        PurpleConversation *conv;
526        GList *info, *l;
527       
528        if( !pi->chat_info || !pi->chat_info_defaults ||
529            !( info = pi->chat_info( purple_account_get_connection( pa ) ) ) )
530        {
531                imcb_error( ic, "Joining chatrooms not supported by this protocol" );
532                return NULL;
533        }
534       
535        if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_CHAT, room, pa ) ) )
536                purple_conversation_destroy( conv );
537       
538        chat_hash = pi->chat_info_defaults( purple_account_get_connection( pa ), room );
539       
540        for( l = info; l; l = l->next )
541        {
542                struct proto_chat_entry *pce = l->data;
543               
544                if( strcmp( pce->identifier, "handle" ) == 0 )
545                        g_hash_table_replace( chat_hash, "handle", g_strdup( nick ) );
546                else if( strcmp( pce->identifier, "password" ) == 0 )
547                        g_hash_table_replace( chat_hash, "password", g_strdup( password ) );
548                else if( strcmp( pce->identifier, "passwd" ) == 0 )
549                        g_hash_table_replace( chat_hash, "passwd", g_strdup( password ) );
550        }
551       
552        serv_join_chat( purple_account_get_connection( pa ), chat_hash );
553       
554        return NULL;
555}
556
557void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle );
558
559static void purple_ui_init();
560
561GHashTable *prplcb_ui_info()
562{
563        static GHashTable *ret;
564       
565        if( ret == NULL )
566        {
567                ret = g_hash_table_new(g_str_hash, g_str_equal);
568                g_hash_table_insert( ret, "name", "BitlBee" );
569                g_hash_table_insert( ret, "version", BITLBEE_VERSION );
570        }
571       
572        return ret;
573}
574
575static PurpleCoreUiOps bee_core_uiops = 
576{
577        NULL,
578        NULL,
579        purple_ui_init,
580        NULL,
581        prplcb_ui_info,
582};
583
584static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t step, size_t step_count )
585{
586        struct im_connection *ic = purple_ic_by_gc( gc );
587       
588        imcb_log( ic, "%s", text );
589}
590
591static void prplcb_conn_connected( PurpleConnection *gc )
592{
593        struct im_connection *ic = purple_ic_by_gc( gc );
594        const char *dn;
595        set_t *s;
596       
597        imcb_connected( ic );
598       
599        if( ( dn = purple_connection_get_display_name( gc ) ) &&
600            ( s = set_find( &ic->acc->set, "display_name" ) ) )
601        {
602                g_free( s->value );
603                s->value = g_strdup( dn );
604        }
605       
606        if( gc->flags & PURPLE_CONNECTION_HTML )
607                ic->flags |= OPT_DOES_HTML;
608}
609
610static void prplcb_conn_disconnected( PurpleConnection *gc )
611{
612        struct im_connection *ic = purple_ic_by_gc( gc );
613       
614        if( ic != NULL )
615        {
616                imc_logout( ic, !gc->wants_to_die );
617        }
618}
619
620static void prplcb_conn_notice( PurpleConnection *gc, const char *text )
621{
622        struct im_connection *ic = purple_ic_by_gc( gc );
623       
624        if( ic != NULL )
625                imcb_log( ic, "%s", text );
626}
627
628static void prplcb_conn_report_disconnect_reason( PurpleConnection *gc, PurpleConnectionError reason, const char *text )
629{
630        struct im_connection *ic = purple_ic_by_gc( gc );
631       
632        /* PURPLE_CONNECTION_ERROR_NAME_IN_USE means concurrent login,
633           should probably handle that. */
634        if( ic != NULL )
635                imcb_error( ic, "%s", text );
636}
637
638static PurpleConnectionUiOps bee_conn_uiops =
639{
640        prplcb_conn_progress,
641        prplcb_conn_connected,
642        prplcb_conn_disconnected,
643        prplcb_conn_notice,
644        NULL,
645        NULL,
646        NULL,
647        prplcb_conn_report_disconnect_reason,
648};
649
650static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node )
651{
652        if( node->type == PURPLE_BLIST_BUDDY_NODE )
653        {
654                PurpleBuddy *bud = (PurpleBuddy*) node;
655                PurpleGroup *group = purple_buddy_get_group( bud );
656                struct im_connection *ic = purple_ic_by_pa( bud->account );
657                PurpleStatus *as;
658                int flags = 0;
659               
660                if( ic == NULL )
661                        return;
662               
663                if( bud->server_alias )
664                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
665               
666                if( group )
667                        imcb_add_buddy( ic, bud->name, purple_group_get_name( group ) );
668               
669                flags |= purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0;
670                flags |= purple_presence_is_available( bud->presence ) ? 0 : OPT_AWAY;
671               
672                as = purple_presence_get_active_status( bud->presence );
673               
674                imcb_buddy_status( ic, bud->name, flags, purple_status_get_name( as ),
675                                   purple_status_get_attr_string( as, "message" ) );
676               
677                imcb_buddy_times( ic, bud->name,
678                                  purple_presence_get_login_time( bud->presence ),
679                                  purple_presence_get_idle_time( bud->presence ) );
680        }
681}
682
683static void prplcb_blist_new( PurpleBlistNode *node )
684{
685        if( node->type == PURPLE_BLIST_BUDDY_NODE )
686        {
687                PurpleBuddy *bud = (PurpleBuddy*) node;
688                struct im_connection *ic = purple_ic_by_pa( bud->account );
689               
690                if( ic == NULL )
691                        return;
692               
693                imcb_add_buddy( ic, bud->name, NULL );
694               
695                prplcb_blist_update( NULL, node );
696        }
697}
698
699static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node )
700{
701/*
702        PurpleBuddy *bud = (PurpleBuddy*) node;
703       
704        if( node->type == PURPLE_BLIST_BUDDY_NODE )
705        {
706                struct im_connection *ic = purple_ic_by_pa( bud->account );
707               
708                if( ic == NULL )
709                        return;
710               
711                imcb_remove_buddy( ic, bud->name, NULL );
712        }
713*/
714}
715
716static PurpleBlistUiOps bee_blist_uiops =
717{
718        NULL,
719        prplcb_blist_new,
720        NULL,
721        prplcb_blist_update,
722        prplcb_blist_remove,
723};
724
725void prplcb_conv_new( PurpleConversation *conv )
726{
727        if( conv->type == PURPLE_CONV_TYPE_CHAT )
728        {
729                struct im_connection *ic = purple_ic_by_pa( conv->account );
730                struct groupchat *gc;
731               
732                gc = imcb_chat_new( ic, conv->name );
733                conv->ui_data = gc;
734                gc->data = conv;
735               
736                /* libpurple brokenness: Whatever. Show that we join right away,
737                   there's no clear "This is you!" signaling in _add_users so
738                   don't even try. */
739                imcb_chat_add_buddy( gc, gc->ic->acc->user );
740        }
741}
742
743void prplcb_conv_free( PurpleConversation *conv )
744{
745        struct groupchat *gc = conv->ui_data;
746       
747        imcb_chat_free( gc );
748}
749
750void prplcb_conv_add_users( PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals )
751{
752        struct groupchat *gc = conv->ui_data;
753        GList *b;
754       
755        for( b = cbuddies; b; b = b->next )
756        {
757                PurpleConvChatBuddy *pcb = b->data;
758               
759                imcb_chat_add_buddy( gc, pcb->name );
760        }
761}
762
763void prplcb_conv_del_users( PurpleConversation *conv, GList *cbuddies )
764{
765        struct groupchat *gc = conv->ui_data;
766        GList *b;
767       
768        for( b = cbuddies; b; b = b->next )
769                imcb_chat_remove_buddy( gc, b->data, "" );
770}
771
772void prplcb_conv_chat_msg( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime )
773{
774        struct groupchat *gc = conv->ui_data;
775        PurpleBuddy *buddy;
776       
777        /* ..._SEND means it's an outgoing message, no need to echo those. */
778        if( flags & PURPLE_MESSAGE_SEND )
779                return;
780       
781        buddy = purple_find_buddy( conv->account, who );
782        if( buddy != NULL )
783                who = purple_buddy_get_name( buddy );
784       
785        imcb_chat_msg( gc, who, (char*) message, 0, mtime );
786}
787
788static void prplcb_conv_im( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime )
789{
790        struct im_connection *ic = purple_ic_by_pa( conv->account );
791        PurpleBuddy *buddy;
792       
793        /* ..._SEND means it's an outgoing message, no need to echo those. */
794        if( flags & PURPLE_MESSAGE_SEND )
795                return;
796       
797        buddy = purple_find_buddy( conv->account, who );
798        if( buddy != NULL )
799                who = purple_buddy_get_name( buddy );
800       
801        imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime );
802}
803
804static PurpleConversationUiOps bee_conv_uiops = 
805{
806        prplcb_conv_new,           /* create_conversation  */
807        prplcb_conv_free,          /* destroy_conversation */
808        prplcb_conv_chat_msg,      /* write_chat           */
809        prplcb_conv_im,            /* write_im             */
810        NULL,                      /* write_conv           */
811        prplcb_conv_add_users,     /* chat_add_users       */
812        NULL,                      /* chat_rename_user     */
813        prplcb_conv_del_users,     /* chat_remove_users    */
814        NULL,                      /* chat_update_user     */
815        NULL,                      /* present              */
816        NULL,                      /* has_focus            */
817        NULL,                      /* custom_smiley_add    */
818        NULL,                      /* custom_smiley_write  */
819        NULL,                      /* custom_smiley_close  */
820        NULL,                      /* send_confirm         */
821};
822
823struct prplcb_request_action_data
824{
825        void *user_data, *bee_data;
826        PurpleRequestActionCb yes, no;
827        int yes_i, no_i;
828};
829
830static void prplcb_request_action_yes( void *data )
831{
832        struct prplcb_request_action_data *pqad = data;
833       
834        if( pqad->yes )
835                pqad->yes( pqad->user_data, pqad->yes_i );
836        g_free( pqad );
837}
838
839static void prplcb_request_action_no( void *data )
840{
841        struct prplcb_request_action_data *pqad = data;
842       
843        if( pqad->no )
844                pqad->no( pqad->user_data, pqad->no_i );
845        g_free( pqad );
846}
847
848static void *prplcb_request_action( const char *title, const char *primary, const char *secondary,
849                                    int default_action, PurpleAccount *account, const char *who,
850                                    PurpleConversation *conv, void *user_data, size_t action_count,
851                                    va_list actions )
852{
853        struct prplcb_request_action_data *pqad; 
854        int i;
855        char *q;
856       
857        pqad = g_new0( struct prplcb_request_action_data, 1 );
858       
859        for( i = 0; i < action_count; i ++ )
860        {
861                char *caption;
862                void *fn;
863               
864                caption = va_arg( actions, char* );
865                fn = va_arg( actions, void* );
866               
867                if( strstr( caption, "Accept" ) || strstr( caption, "OK" ) )
868                {
869                        pqad->yes = fn;
870                        pqad->yes_i = i;
871                }
872                else if( strstr( caption, "Reject" ) || strstr( caption, "Cancel" ) )
873                {
874                        pqad->no = fn;
875                        pqad->no_i = i;
876                }
877        }
878       
879        pqad->user_data = user_data;
880       
881        /* TODO: IRC stuff here :-( */
882        q = g_strdup_printf( "Request: %s\n\n%s\n\n%s", title, primary, secondary );
883        pqad->bee_data = query_add( local_bee->ui_data, purple_ic_by_pa( account ), q,
884                prplcb_request_action_yes, prplcb_request_action_no, g_free, pqad );
885       
886        g_free( q );
887       
888        return pqad;
889}
890
891/*
892static void prplcb_request_test()
893{
894        fprintf( stderr, "bla\n" );
895}
896*/
897
898static PurpleRequestUiOps bee_request_uiops =
899{
900        NULL,
901        NULL,
902        prplcb_request_action,
903        NULL,
904        NULL,
905        NULL,
906        NULL,
907};
908
909static void prplcb_privacy_permit_added( PurpleAccount *account, const char *name )
910{
911        struct im_connection *ic = purple_ic_by_pa( account );
912       
913        if( !g_slist_find_custom( ic->permit, name, (GCompareFunc) ic->acc->prpl->handle_cmp ) )
914                ic->permit = g_slist_prepend( ic->permit, g_strdup( name ) );
915}
916
917static void prplcb_privacy_permit_removed( PurpleAccount *account, const char *name )
918{
919        struct im_connection *ic = purple_ic_by_pa( account );
920        void *n;
921       
922        n = g_slist_find_custom( ic->permit, name, (GCompareFunc) ic->acc->prpl->handle_cmp );
923        ic->permit = g_slist_remove( ic->permit, n );
924}
925
926static void prplcb_privacy_deny_added( PurpleAccount *account, const char *name )
927{
928        struct im_connection *ic = purple_ic_by_pa( account );
929       
930        if( !g_slist_find_custom( ic->deny, name, (GCompareFunc) ic->acc->prpl->handle_cmp ) )
931                ic->deny = g_slist_prepend( ic->deny, g_strdup( name ) );
932}
933
934static void prplcb_privacy_deny_removed( PurpleAccount *account, const char *name )
935{
936        struct im_connection *ic = purple_ic_by_pa( account );
937        void *n;
938       
939        n = g_slist_find_custom( ic->deny, name, (GCompareFunc) ic->acc->prpl->handle_cmp );
940        ic->deny = g_slist_remove( ic->deny, n );
941}
942
943static PurplePrivacyUiOps bee_privacy_uiops =
944{
945        prplcb_privacy_permit_added,
946        prplcb_privacy_permit_removed,
947        prplcb_privacy_deny_added,
948        prplcb_privacy_deny_removed,
949};
950
951static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s )
952{
953        fprintf( stderr, "DEBUG %s: %s", category, arg_s );
954}
955
956static PurpleDebugUiOps bee_debug_uiops =
957{
958        prplcb_debug_print,
959};
960
961static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer udata )
962{
963        return b_timeout_add( interval, (b_event_handler) func, udata );
964}
965
966static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata )
967{
968        return b_input_add( fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata );
969}
970
971static gboolean prplcb_ev_remove( guint id )
972{
973        b_event_remove( (gint) id );
974        return TRUE;
975}
976
977static PurpleEventLoopUiOps glib_eventloops = 
978{
979        prplcb_ev_timeout_add,
980        prplcb_ev_remove,
981        prplcb_ev_input_add,
982        prplcb_ev_remove,
983};
984
985static void *prplcb_notify_email( PurpleConnection *gc, const char *subject, const char *from,
986                                  const char *to, const char *url )
987{
988        struct im_connection *ic = purple_ic_by_gc( gc );
989       
990        imcb_log( ic, "Received e-mail from %s for %s: %s <%s>", from, to, subject, url );
991       
992        return NULL;
993}
994
995static void *prplcb_notify_userinfo( PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info )
996{
997        struct im_connection *ic = purple_ic_by_gc( gc );
998        GString *info = g_string_new( "" );
999        GList *l = purple_notify_user_info_get_entries( user_info );
1000        char *key;
1001        const char *value;
1002        int n;
1003       
1004        while( l )
1005        {
1006                PurpleNotifyUserInfoEntry *e = l->data;
1007               
1008                switch( purple_notify_user_info_entry_get_type( e ) )
1009                {
1010                case PURPLE_NOTIFY_USER_INFO_ENTRY_PAIR:
1011                case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER:
1012                        key = g_strdup( purple_notify_user_info_entry_get_label( e ) );
1013                        value = purple_notify_user_info_entry_get_value( e );
1014                       
1015                        if( key )
1016                        {
1017                                strip_html( key );
1018                                g_string_append_printf( info, "%s: ", key );
1019                               
1020                                if( value )
1021                                {
1022                                        n = strlen( value ) - 1;
1023                                        while( isspace( value[n] ) )
1024                                                n --;
1025                                        g_string_append_len( info, value, n + 1 );
1026                                }
1027                                g_string_append_c( info, '\n' );
1028                                g_free( key );
1029                        }
1030                       
1031                        break;
1032                case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK:
1033                        g_string_append( info, "------------------------\n" );
1034                        break;
1035                }
1036               
1037                l = l->next;
1038        }
1039       
1040        imcb_log( ic, "User %s info:\n%s", who, info->str );
1041        g_string_free( info, TRUE );
1042       
1043        return NULL;
1044}
1045
1046static PurpleNotifyUiOps bee_notify_uiops =
1047{
1048        NULL,
1049        prplcb_notify_email,
1050        NULL,
1051        NULL,
1052        NULL,
1053        NULL,
1054        prplcb_notify_userinfo,
1055};
1056
1057static void *prplcb_account_request_authorize( PurpleAccount *account, const char *remote_user,
1058        const char *id, const char *alias, const char *message, gboolean on_list,
1059        PurpleAccountRequestAuthorizationCb authorize_cb, PurpleAccountRequestAuthorizationCb deny_cb, void *user_data )
1060{
1061        struct im_connection *ic = purple_ic_by_pa( account );
1062        char *q;
1063       
1064        if( alias )
1065                q = g_strdup_printf( "%s (%s) wants to add you to his/her contact "
1066                                     "list. (%s)", alias, remote_user, message );
1067        else
1068                q = g_strdup_printf( "%s wants to add you to his/her contact "
1069                                     "list. (%s)", remote_user, message );
1070       
1071        imcb_ask_with_free( ic, q, user_data, authorize_cb, deny_cb, NULL );
1072        g_free( q );
1073}
1074
1075static PurpleAccountUiOps bee_account_uiops =
1076{
1077        NULL,
1078        NULL,
1079        NULL,
1080        prplcb_account_request_authorize,
1081        NULL,
1082};
1083
1084extern PurpleXferUiOps bee_xfer_uiops;
1085
1086static void purple_ui_init()
1087{
1088        purple_connections_set_ui_ops( &bee_conn_uiops );
1089        purple_blist_set_ui_ops( &bee_blist_uiops );
1090        purple_conversations_set_ui_ops( &bee_conv_uiops );
1091        purple_request_set_ui_ops( &bee_request_uiops );
1092        purple_privacy_set_ui_ops( &bee_privacy_uiops );
1093        purple_notify_set_ui_ops( &bee_notify_uiops );
1094        purple_accounts_set_ui_ops( &bee_account_uiops );
1095        purple_xfers_set_ui_ops( &bee_xfer_uiops );
1096       
1097        if( getenv( "BITLBEE_DEBUG" ) )
1098                purple_debug_set_ui_ops( &bee_debug_uiops );
1099}
1100
1101void purple_initmodule()
1102{
1103        struct prpl funcs;
1104        GList *prots;
1105        GString *help;
1106       
1107        if( B_EV_IO_READ != PURPLE_INPUT_READ ||
1108            B_EV_IO_WRITE != PURPLE_INPUT_WRITE )
1109        {
1110                /* FIXME FIXME FIXME FIXME FIXME :-) */
1111                exit( 1 );
1112        }
1113       
1114        purple_util_set_user_dir( "/tmp" );
1115        purple_debug_set_enabled( FALSE );
1116        purple_core_set_ui_ops( &bee_core_uiops );
1117        purple_eventloop_set_ui_ops( &glib_eventloops );
1118        if( !purple_core_init( "BitlBee") )
1119        {
1120                /* Initializing the core failed. Terminate. */
1121                fprintf( stderr, "libpurple initialization failed.\n" );
1122                abort();
1123        }
1124       
1125        /* This seems like stateful shit we don't want... */
1126        purple_set_blist( purple_blist_new() );
1127        purple_blist_load();
1128       
1129        /* Meh? */
1130        purple_prefs_load();
1131       
1132        memset( &funcs, 0, sizeof( funcs ) );
1133        funcs.login = purple_login;
1134        funcs.init = purple_init;
1135        funcs.logout = purple_logout;
1136        funcs.buddy_msg = purple_buddy_msg;
1137        funcs.away_states = purple_away_states;
1138        funcs.set_away = purple_set_away;
1139        funcs.add_buddy = purple_add_buddy;
1140        funcs.remove_buddy = purple_remove_buddy;
1141        funcs.add_permit = purple_add_permit;
1142        funcs.add_deny = purple_add_deny;
1143        funcs.rem_permit = purple_rem_permit;
1144        funcs.rem_deny = purple_rem_deny;
1145        funcs.get_info = purple_get_info;
1146        funcs.keepalive = purple_keepalive;
1147        funcs.send_typing = purple_send_typing;
1148        funcs.handle_cmp = g_strcasecmp;
1149        /* TODO(wilmer): Set these only for protocols that support them? */
1150        funcs.chat_msg = purple_chat_msg;
1151        funcs.chat_with = purple_chat_with;
1152        funcs.chat_invite = purple_chat_invite;
1153        funcs.chat_leave = purple_chat_leave;
1154        funcs.chat_join = purple_chat_join;
1155        funcs.transfer_request = purple_transfer_request;
1156       
1157        help = g_string_new( "BitlBee libpurple module supports the following IM protocols:\n" );
1158       
1159        /* Add a protocol entry to BitlBee's structures for every protocol
1160           supported by this libpurple instance. */     
1161        for( prots = purple_plugins_get_protocols(); prots; prots = prots->next )
1162        {
1163                PurplePlugin *prot = prots->data;
1164                struct prpl *ret;
1165               
1166                ret = g_memdup( &funcs, sizeof( funcs ) );
1167                ret->name = ret->data = prot->info->id;
1168                if( strncmp( ret->name, "prpl-", 5 ) == 0 )
1169                        ret->name += 5;
1170                register_protocol( ret );
1171               
1172                g_string_append_printf( help, "\n* %s (%s)", ret->name, prot->info->name );
1173               
1174                /* libpurple doesn't define a protocol called OSCAR, but we
1175                   need it to be compatible with normal BitlBee. */
1176                if( g_strcasecmp( prot->info->id, "prpl-aim" ) == 0 )
1177                {
1178                        ret = g_memdup( &funcs, sizeof( funcs ) );
1179                        ret->name = "oscar";
1180                        ret->data = prot->info->id;
1181                        register_protocol( ret );
1182                }
1183        }
1184       
1185        g_string_append( help, "\n\nFor used protocols, more information about available "
1186                         "settings can be found using \x02help purple <protocol name>\x02" );
1187       
1188        /* Add a simple dynamically-generated help item listing all
1189           the supported protocols. */
1190        help_add_mem( &global.help, "purple", help->str );
1191        g_string_free( help, TRUE );
1192}
Note: See TracBrowser for help on using the repository browser.