source: protocols/purple/purple.c @ acd7959

Last change on this file since acd7959 was 3e59c8d, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-18T15:31:58Z

libpurple: Add contacts to groups when requested. Still not dealing well
with contacts in multiple groups.

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