source: protocols/purple/purple.c @ c3caa46

Last change on this file since c3caa46 was c3caa46, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-22T13:46:25Z

Support for named groupchats, although not very solid.

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