source: protocols/purple/purple.c @ cd741d8

Last change on this file since cd741d8 was cd741d8, checked in by Wilmer van der Gaast <wilmer@…>, at 2009-11-23T23:23:37Z

Fixed compatibility with non-libpurple version: oscar is now recognized
as a protocol name, and removed prpl- hack from nogaim.c.

  • Property mode set to 100644
File size: 14.0 KB
Line 
1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  libpurple module - Main file                                             *
5*                                                                           *
6*  Copyright 2009 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
26#include <glib.h>
27#include <purple.h>
28
29GSList *purple_connections;
30
31static struct im_connection *purple_ic_by_pa( PurpleAccount *pa )
32{
33        GSList *i;
34       
35        for( i = purple_connections; i; i = i->next )
36                if( ((struct im_connection *)i->data)->proto_data == pa )
37                        return i->data;
38       
39        return NULL;
40}
41
42static struct im_connection *purple_ic_by_gc( PurpleConnection *gc )
43{
44        return purple_ic_by_pa( purple_connection_get_account( gc ) );
45}
46
47static void purple_init( account_t *acc )
48{
49        PurplePlugin *prpl = purple_plugins_find_with_id( acc->prpl->name );
50        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
51        GList *i;
52       
53        for( i = pi->protocol_options; i; i = i->next )
54        {
55                PurpleAccountOption *o = i->data;
56                const char *name;
57                char *def = NULL;
58                set_eval eval = NULL;
59                set_t *s;
60               
61                name = purple_account_option_get_setting( o );
62               
63                switch( purple_account_option_get_type( o ) )
64                {
65                case PURPLE_PREF_STRING:
66                        def = g_strdup( purple_account_option_get_default_string( o ) );
67                        break;
68               
69                case PURPLE_PREF_INT:
70                        def = g_strdup_printf( "%d", purple_account_option_get_default_int( o ) );
71                        eval = set_eval_int;
72                        break;
73               
74                case PURPLE_PREF_BOOLEAN:
75                        if( purple_account_option_get_default_bool( o ) )
76                                def = g_strdup( "true" );
77                        else
78                                def = g_strdup( "false" );
79                        eval = set_eval_bool;
80                        break;
81               
82                default:
83                        fprintf( stderr, "Setting with unknown type: %s (%d)\n", name, purple_account_option_get_type( o ) );
84                        name = NULL;
85                }
86               
87                if( name != NULL )
88                {
89                        s = set_add( &acc->set, name, def, eval, acc );
90                        s->flags |= ACC_SET_OFFLINE_ONLY;
91                        g_free( def );
92                }
93        }
94}
95
96static void purple_sync_settings( account_t *acc, PurpleAccount *pa )
97{
98        PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id );
99        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
100        GList *i;
101       
102        for( i = pi->protocol_options; i; i = i->next )
103        {
104                PurpleAccountOption *o = i->data;
105                const char *name;
106                set_t *s;
107               
108                name = purple_account_option_get_setting( o );
109                s = set_find( &acc->set, name );
110                if( s->value == NULL )
111                        continue;
112               
113                switch( purple_account_option_get_type( o ) )
114                {
115                case PURPLE_PREF_STRING:
116                        purple_account_set_string( pa, name, set_getstr( &acc->set, name ) );
117                        break;
118               
119                case PURPLE_PREF_INT:
120                        purple_account_set_int( pa, name, set_getint( &acc->set, name ) );
121                        break;
122               
123                case PURPLE_PREF_BOOLEAN:
124                        purple_account_set_bool( pa, name, set_getbool( &acc->set, name ) );
125                        break;
126               
127                default:
128                        break;
129                }
130        }
131}
132
133static void purple_login( account_t *acc )
134{
135        struct im_connection *ic = imcb_new( acc );
136        static void *irc_check = NULL;
137        PurpleAccount *pa;
138       
139        if( irc_check != NULL && irc_check != acc->irc )
140        {
141                irc_usermsg( acc->irc, "Daemon mode detected. Do *not* try to use libpurple in daemon mode! Please use inetd or ForkDaemon mode instead." );
142                return;
143        }
144        irc_check = acc->irc;
145       
146        /* For now this is needed in the _connected() handlers if using
147           GLib event handling, to make sure we're not handling events
148           on dead connections. */
149        purple_connections = g_slist_prepend( purple_connections, ic );
150       
151        ic->proto_data = pa = purple_account_new( acc->user, (char*) acc->prpl->data );
152        purple_account_set_password( pa, acc->pass );
153        purple_sync_settings( acc, pa );
154       
155        purple_account_set_enabled( pa, "BitlBee", TRUE );
156}
157
158static void purple_logout( struct im_connection *ic )
159{
160        PurpleAccount *pa = ic->proto_data;
161       
162        purple_account_set_enabled( pa, "BitlBee", FALSE );
163        purple_connections = g_slist_remove( purple_connections, ic );
164        purple_accounts_remove( pa );
165}
166
167static int purple_buddy_msg( struct im_connection *ic, char *who, char *message, int flags )
168{
169        PurpleConversation *conv;
170       
171        if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM,
172                                                            who, ic->proto_data ) ) == NULL )
173        {
174                conv = purple_conversation_new( PURPLE_CONV_TYPE_IM,
175                                                ic->proto_data, who );
176        }
177       
178        purple_conv_im_send( purple_conversation_get_im_data( conv ), message );
179       
180        return 1;
181}
182
183static GList *purple_away_states( struct im_connection *ic )
184{
185        PurpleAccount *pa = ic->proto_data;
186        GList *st, *ret = NULL;
187       
188        for( st = purple_account_get_status_types( pa ); st; st = st->next )
189                ret = g_list_append( ret, (void*) purple_status_type_get_name( st->data ) );
190       
191        return ret;
192}
193
194static void purple_set_away( struct im_connection *ic, char *state_txt, char *message )
195{
196        PurpleAccount *pa = ic->proto_data;
197        GList *status_types = purple_account_get_status_types( pa ), *st;
198        PurpleStatusType *pst = NULL;
199       
200        for( st = status_types; st; st = st->next )
201        {
202                pst = st->data;
203               
204                if( g_strcasecmp( state_txt, purple_status_type_get_name( pst ) ) == 0 )
205                        break;
206        }
207       
208        purple_account_set_status( pa, st ? purple_status_type_get_id( pst ) : "away",
209                                   TRUE, "message", message, NULL );
210}
211
212static void purple_add_buddy( struct im_connection *ic, char *who, char *group )
213{
214        PurpleBuddy *pb;
215       
216        pb = purple_buddy_new( (PurpleAccount*) ic->proto_data, who, NULL );
217        purple_blist_add_buddy( pb, NULL, NULL, NULL );
218        purple_account_add_buddy( (PurpleAccount*) ic->proto_data, pb );
219}
220
221static void purple_remove_buddy( struct im_connection *ic, char *who, char *group )
222{
223        PurpleBuddy *pb;
224       
225        pb = purple_find_buddy( (PurpleAccount*) ic->proto_data, who );
226        if( pb != NULL )
227        {
228                purple_account_remove_buddy( (PurpleAccount*) ic->proto_data, pb, NULL );
229                purple_blist_remove_buddy( pb );
230        }
231}
232
233static void purple_keepalive( struct im_connection *ic )
234{
235}
236
237static int purple_send_typing( struct im_connection *ic, char *who, int typing )
238{
239        return 1;
240}
241
242static void purple_ui_init();
243
244static PurpleCoreUiOps bee_core_uiops = 
245{
246        NULL,
247        NULL,
248        purple_ui_init,
249        NULL,
250};
251
252static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t step, size_t step_count )
253{
254        struct im_connection *ic = purple_ic_by_gc( gc );
255       
256        imcb_log( ic, "%s", text );
257}
258
259static void prplcb_conn_connected( PurpleConnection *gc )
260{
261        struct im_connection *ic = purple_ic_by_gc( gc );
262       
263        imcb_connected( ic );
264       
265        if( gc->flags & PURPLE_CONNECTION_HTML )
266                ic->flags |= OPT_DOES_HTML;
267}
268
269static void prplcb_conn_disconnected( PurpleConnection *gc )
270{
271        struct im_connection *ic = purple_ic_by_gc( gc );
272       
273        if( ic != NULL )
274        {
275                imc_logout( ic, TRUE );
276        }
277}
278
279static void prplcb_conn_notice( PurpleConnection *gc, const char *text )
280{
281        struct im_connection *ic = purple_ic_by_gc( gc );
282       
283        if( ic != NULL )
284                imcb_log( ic, "%s", text );
285}
286
287static void prplcb_conn_report_disconnect_reason( PurpleConnection *gc, PurpleConnectionError reason, const char *text )
288{
289        struct im_connection *ic = purple_ic_by_gc( gc );
290       
291        /* PURPLE_CONNECTION_ERROR_NAME_IN_USE means concurrent login,
292           should probably handle that. */
293        if( ic != NULL )
294                imcb_error( ic, "%s", text );
295}
296
297static PurpleConnectionUiOps bee_conn_uiops =
298{
299        prplcb_conn_progress,
300        prplcb_conn_connected,
301        prplcb_conn_disconnected,
302        prplcb_conn_notice,
303        NULL,
304        NULL,
305        NULL,
306        prplcb_conn_report_disconnect_reason,
307};
308
309static void prplcb_blist_new( PurpleBlistNode *node )
310{
311        PurpleBuddy *bud = (PurpleBuddy*) node;
312       
313        if( node->type == PURPLE_BLIST_BUDDY_NODE )
314        {
315                struct im_connection *ic = purple_ic_by_pa( bud->account );
316               
317                if( ic == NULL )
318                        return;
319               
320                imcb_add_buddy( ic, bud->name, NULL );
321                if( bud->server_alias )
322                {
323                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
324                        imcb_buddy_nick_hint( ic, bud->name, bud->server_alias );
325                }
326        }
327}
328
329static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node )
330{
331        PurpleBuddy *bud = (PurpleBuddy*) node;
332       
333        if( node->type == PURPLE_BLIST_BUDDY_NODE )
334        {
335                struct im_connection *ic = purple_ic_by_pa( bud->account );
336                PurpleStatus *as;
337                int flags = 0;
338               
339                if( ic == NULL )
340                        return;
341               
342                if( bud->server_alias )
343                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
344               
345                flags |= purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0;
346                flags |= purple_presence_is_available( bud->presence ) ? 0 : OPT_AWAY;
347               
348                as = purple_presence_get_active_status( bud->presence );
349               
350                imcb_buddy_status( ic, bud->name, flags, purple_status_get_name( as ),
351                                   purple_status_get_attr_string( as, "message" ) );
352        }
353}
354
355static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node )
356{
357        PurpleBuddy *bud = (PurpleBuddy*) node;
358       
359        /*
360        if( node->type == PURPLE_BLIST_BUDDY_NODE )
361        {
362                struct im_connection *ic = purple_ic_by_pa( bud->account );
363               
364                if( ic == NULL )
365                        return;
366               
367                imcb_remove_buddy( ic, bud->name, NULL );
368        }
369        */
370}
371
372static PurpleBlistUiOps bee_blist_uiops =
373{
374        NULL,
375        prplcb_blist_new,
376        NULL,
377        prplcb_blist_update,
378        prplcb_blist_remove,
379};
380
381static void prplcb_conv_im( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime )
382{
383        struct im_connection *ic = purple_ic_by_pa( conv->account );
384       
385        /* ..._SEND means it's an outgoing message, no need to echo those. */
386        if( !( flags & PURPLE_MESSAGE_SEND ) )
387                imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime );
388}
389
390static PurpleConversationUiOps bee_conv_uiops = 
391{
392        NULL,                      /* create_conversation  */
393        NULL,                      /* destroy_conversation */
394        NULL,                      /* write_chat           */
395        prplcb_conv_im,            /* write_im             */
396        NULL,                      /* write_conv           */
397        NULL,                      /* chat_add_users       */
398        NULL,                      /* chat_rename_user     */
399        NULL,                      /* chat_remove_users    */
400        NULL,                      /* chat_update_user     */
401        NULL,                      /* present              */
402        NULL,                      /* has_focus            */
403        NULL,                      /* custom_smiley_add    */
404        NULL,                      /* custom_smiley_write  */
405        NULL,                      /* custom_smiley_close  */
406        NULL,                      /* send_confirm         */
407};
408
409static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s )
410{
411        fprintf( stderr, "DEBUG %s: %s", category, arg_s );
412}
413
414static PurpleDebugUiOps bee_debug_uiops =
415{
416        prplcb_debug_print,
417};
418
419static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer udata )
420{
421        return b_timeout_add( interval, (b_event_handler) func, udata );
422}
423
424static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata )
425{
426        return b_input_add( fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata );
427}
428
429static gboolean prplcb_ev_remove( guint id )
430{
431        b_event_remove( (gint) id );
432        return TRUE;
433}
434
435static PurpleEventLoopUiOps glib_eventloops = 
436{
437        prplcb_ev_timeout_add,
438        prplcb_ev_remove,
439        prplcb_ev_input_add,
440        prplcb_ev_remove,
441};
442
443static void purple_ui_init()
444{
445        purple_blist_set_ui_ops( &bee_blist_uiops );
446        purple_connections_set_ui_ops( &bee_conn_uiops );
447        purple_conversations_set_ui_ops( &bee_conv_uiops );
448        //purple_debug_set_ui_ops( &bee_debug_uiops );
449}
450
451void purple_initmodule()
452{
453        struct prpl funcs;
454        GList *prots;
455       
456        if( B_EV_IO_READ != PURPLE_INPUT_READ ||
457            B_EV_IO_WRITE != PURPLE_INPUT_WRITE )
458        {
459                /* FIXME FIXME FIXME FIXME FIXME :-) */
460                exit( 1 );
461        }
462       
463        purple_util_set_user_dir("/tmp");
464        purple_debug_set_enabled(FALSE);
465        purple_core_set_ui_ops(&bee_core_uiops);
466        purple_eventloop_set_ui_ops(&glib_eventloops);
467        if( !purple_core_init( "BitlBee") )
468        {
469                /* Initializing the core failed. Terminate. */
470                fprintf( stderr, "libpurple initialization failed.\n" );
471                abort();
472        }
473       
474        /* This seems like stateful shit we don't want... */
475        purple_set_blist(purple_blist_new());
476        purple_blist_load();
477       
478        /* Meh? */
479        purple_prefs_load();
480       
481        memset( &funcs, 0, sizeof( funcs ) );
482        funcs.login = purple_login;
483        funcs.init = purple_init;
484        funcs.logout = purple_logout;
485        funcs.buddy_msg = purple_buddy_msg;
486        funcs.away_states = purple_away_states;
487        funcs.set_away = purple_set_away;
488        funcs.add_buddy = purple_add_buddy;
489        funcs.remove_buddy = purple_remove_buddy;
490        funcs.keepalive = purple_keepalive;
491        funcs.send_typing = purple_send_typing;
492        funcs.handle_cmp = g_strcasecmp;
493       
494        for( prots = purple_plugins_get_protocols(); prots; prots = prots->next )
495        {
496                PurplePlugin *prot = prots->data;
497                struct prpl *ret;
498               
499                ret = g_memdup( &funcs, sizeof( funcs ) );
500                ret->name = ret->data = prot->info->id;
501                if( strncmp( ret->name, "prpl-", 5 ) == 0 )
502                        ret->name += 5;
503                register_protocol( ret );
504               
505                if( g_strcasecmp( prot->info->id, "prpl-aim" ) == 0 )
506                {
507                        ret = g_memdup( &funcs, sizeof( funcs ) );
508                        ret->name = "oscar";
509                        ret->data = prot->info->id;
510                        register_protocol( ret );
511                }
512        }
513}
Note: See TracBrowser for help on using the repository browser.