source: protocols/purple/purple.c @ 52cae01

Last change on this file since 52cae01 was 52cae01, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-03-07T23:08:40Z

Set the ACC_FLAG_*_MESSAGE flags correctly depending on the prpl.

  • Property mode set to 100644
File size: 18.0 KB
RevLine 
[796da03]1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  libpurple module - Main file                                             *
5*                                                                           *
[52cae01]6*  Copyright 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
[7da726b]39static struct 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
[796da03]55static void purple_init( account_t *acc )
56{
[45a19e5]57        PurplePlugin *prpl = purple_plugins_find_with_id( (char*) acc->prpl->data );
[0f7ee7e5]58        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
[52cae01]59        PurpleAccount *pa;
60        GList *i, *st;
[0cbef26]61       
[52cae01]62        /* Convert all protocol_options into per-account setting variables. */
[0f7ee7e5]63        for( i = pi->protocol_options; i; i = i->next )
64        {
65                PurpleAccountOption *o = i->data;
66                const char *name;
67                char *def = NULL;
68                set_eval eval = NULL;
69                set_t *s;
70               
71                name = purple_account_option_get_setting( o );
72               
73                switch( purple_account_option_get_type( o ) )
74                {
75                case PURPLE_PREF_STRING:
76                        def = g_strdup( purple_account_option_get_default_string( o ) );
77                        break;
78               
79                case PURPLE_PREF_INT:
80                        def = g_strdup_printf( "%d", purple_account_option_get_default_int( o ) );
81                        eval = set_eval_int;
82                        break;
83               
84                case PURPLE_PREF_BOOLEAN:
85                        if( purple_account_option_get_default_bool( o ) )
86                                def = g_strdup( "true" );
87                        else
88                                def = g_strdup( "false" );
89                        eval = set_eval_bool;
90                        break;
91               
92                default:
93                        fprintf( stderr, "Setting with unknown type: %s (%d)\n", name, purple_account_option_get_type( o ) );
[b3117f2]94                        name = NULL;
[0f7ee7e5]95                }
96               
[b3117f2]97                if( name != NULL )
[0f7ee7e5]98                {
99                        s = set_add( &acc->set, name, def, eval, acc );
100                        s->flags |= ACC_SET_OFFLINE_ONLY;
101                        g_free( def );
102                }
103        }
[52cae01]104       
105        /* Go through all away states to figure out if away/status messages
106           are possible. */
107        pa = purple_account_new( acc->user, (char*) acc->prpl->data );
108        for( st = purple_account_get_status_types( pa ); st; st = st->next )
109        {
110                PurpleStatusPrimitive prim = purple_status_type_get_primitive( st->data );
111               
112                if( prim == PURPLE_STATUS_AVAILABLE )
113                {
114                        if( purple_status_type_get_attr( st->data, "message" ) )
115                                acc->flags |= ACC_FLAG_STATUS_MESSAGE;
116                }
117                else if( prim != PURPLE_STATUS_OFFLINE )
118                {
119                        if( purple_status_type_get_attr( st->data, "message" ) )
120                                acc->flags |= ACC_FLAG_AWAY_MESSAGE;
121                }
122        }
123        purple_accounts_remove( pa );
[796da03]124}
125
[b74b287]126static void purple_sync_settings( account_t *acc, PurpleAccount *pa )
127{
128        PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id );
129        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
130        GList *i;
131       
132        for( i = pi->protocol_options; i; i = i->next )
133        {
134                PurpleAccountOption *o = i->data;
135                const char *name;
136                set_t *s;
137               
138                name = purple_account_option_get_setting( o );
139                s = set_find( &acc->set, name );
140                if( s->value == NULL )
141                        continue;
142               
143                switch( purple_account_option_get_type( o ) )
144                {
145                case PURPLE_PREF_STRING:
146                        purple_account_set_string( pa, name, set_getstr( &acc->set, name ) );
147                        break;
148               
149                case PURPLE_PREF_INT:
150                        purple_account_set_int( pa, name, set_getint( &acc->set, name ) );
151                        break;
152               
153                case PURPLE_PREF_BOOLEAN:
154                        purple_account_set_bool( pa, name, set_getbool( &acc->set, name ) );
155                        break;
156               
157                default:
158                        break;
159                }
160        }
161}
162
[796da03]163static void purple_login( account_t *acc )
164{
165        struct im_connection *ic = imcb_new( acc );
[860ba6a]166        PurpleAccount *pa;
[796da03]167       
[0ac1a375]168        if( local_irc != NULL && local_irc != acc->irc )
[6967d01]169        {
[0ac1a375]170                irc_usermsg( acc->irc, "Daemon mode detected. Do *not* try to use libpurple in daemon mode! "
171                                       "Please use inetd or ForkDaemon mode instead." );
[6967d01]172                return;
173        }
[0ac1a375]174        local_irc = acc->irc;
[6967d01]175       
[796da03]176        /* For now this is needed in the _connected() handlers if using
177           GLib event handling, to make sure we're not handling events
178           on dead connections. */
179        purple_connections = g_slist_prepend( purple_connections, ic );
180       
[cd741d8]181        ic->proto_data = pa = purple_account_new( acc->user, (char*) acc->prpl->data );
[860ba6a]182        purple_account_set_password( pa, acc->pass );
[b74b287]183        purple_sync_settings( acc, pa );
[860ba6a]184       
185        purple_account_set_enabled( pa, "BitlBee", TRUE );
[796da03]186}
187
188static void purple_logout( struct im_connection *ic )
189{
[db4cd40]190        PurpleAccount *pa = ic->proto_data;
191       
192        purple_account_set_enabled( pa, "BitlBee", FALSE );
[796da03]193        purple_connections = g_slist_remove( purple_connections, ic );
[b74b287]194        purple_accounts_remove( pa );
[796da03]195}
196
197static int purple_buddy_msg( struct im_connection *ic, char *who, char *message, int flags )
198{
[389f7be]199        PurpleConversation *conv;
200       
201        if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM,
202                                                            who, ic->proto_data ) ) == NULL )
203        {
204                conv = purple_conversation_new( PURPLE_CONV_TYPE_IM,
205                                                ic->proto_data, who );
206        }
207       
208        purple_conv_im_send( purple_conversation_get_im_data( conv ), message );
[0cbef26]209       
210        return 1;
[796da03]211}
212
213static GList *purple_away_states( struct im_connection *ic )
214{
[ec5e57d]215        PurpleAccount *pa = ic->proto_data;
216        GList *st, *ret = NULL;
217       
218        for( st = purple_account_get_status_types( pa ); st; st = st->next )
[279607e]219        {
220                PurpleStatusPrimitive prim = purple_status_type_get_primitive( st->data );
221                if( prim != PURPLE_STATUS_AVAILABLE && prim != PURPLE_STATUS_OFFLINE )
222                        ret = g_list_append( ret, (void*) purple_status_type_get_name( st->data ) );
223        }
[ec5e57d]224       
225        return ret;
[796da03]226}
227
228static void purple_set_away( struct im_connection *ic, char *state_txt, char *message )
229{
[ec5e57d]230        PurpleAccount *pa = ic->proto_data;
231        GList *status_types = purple_account_get_status_types( pa ), *st;
232        PurpleStatusType *pst = NULL;
[279607e]233        GList *args = NULL;
[ec5e57d]234       
235        for( st = status_types; st; st = st->next )
236        {
237                pst = st->data;
238               
[279607e]239                if( state_txt == NULL &&
240                    purple_status_type_get_primitive( st->data ) == PURPLE_STATUS_AVAILABLE )
241                        break;
242
243                if( state_txt != NULL &&
244                    g_strcasecmp( state_txt, purple_status_type_get_name( pst ) ) == 0 )
[ec5e57d]245                        break;
246        }
247       
[52cae01]248        if( message && purple_status_type_get_attr( st, "message" ) )
[279607e]249        {
250                args = g_list_append( args, "message" );
251                args = g_list_append( args, message );
252        }
253       
254        purple_account_set_status_list( pa, st ? purple_status_type_get_id( pst ) : "away",
255                                        TRUE, args );
256
257        g_list_free( args );
[796da03]258}
259
260static void purple_add_buddy( struct im_connection *ic, char *who, char *group )
261{
[b3117f2]262        PurpleBuddy *pb;
263       
264        pb = purple_buddy_new( (PurpleAccount*) ic->proto_data, who, NULL );
265        purple_blist_add_buddy( pb, NULL, NULL, NULL );
266        purple_account_add_buddy( (PurpleAccount*) ic->proto_data, pb );
[796da03]267}
268
269static void purple_remove_buddy( struct im_connection *ic, char *who, char *group )
270{
[b3117f2]271        PurpleBuddy *pb;
272       
273        pb = purple_find_buddy( (PurpleAccount*) ic->proto_data, who );
274        if( pb != NULL )
275        {
276                purple_account_remove_buddy( (PurpleAccount*) ic->proto_data, pb, NULL );
277                purple_blist_remove_buddy( pb );
278        }
[796da03]279}
280
281static void purple_keepalive( struct im_connection *ic )
282{
283}
284
[487f555]285static int purple_send_typing( struct im_connection *ic, char *who, int flags )
[796da03]286{
[487f555]287        PurpleTypingState state = PURPLE_NOT_TYPING;
288        PurpleConversation *conv;
289       
290        if( flags & OPT_TYPING )
291                state = PURPLE_TYPING;
292        else if( flags & OPT_THINKING )
293                state = PURPLE_TYPED;
294       
295        if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM,
296                                                            who, ic->proto_data ) ) == NULL )
297        {
298                purple_conv_im_set_typing_state( purple_conversation_get_im_data( conv ), state );
299                return 1;
300        }
301        else
302        {
303                return 0;
304        }
[796da03]305}
306
[860ba6a]307static void purple_ui_init();
308
309static PurpleCoreUiOps bee_core_uiops = 
310{
311        NULL,
312        NULL,
313        purple_ui_init,
314        NULL,
315};
316
317static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t step, size_t step_count )
318{
[0cbef26]319        struct im_connection *ic = purple_ic_by_gc( gc );
320       
321        imcb_log( ic, "%s", text );
[860ba6a]322}
323
324static void prplcb_conn_connected( PurpleConnection *gc )
325{
[d250b2a]326        struct im_connection *ic = purple_ic_by_gc( gc );
327       
328        imcb_connected( ic );
329       
330        if( gc->flags & PURPLE_CONNECTION_HTML )
331                ic->flags |= OPT_DOES_HTML;
[860ba6a]332}
333
334static void prplcb_conn_disconnected( PurpleConnection *gc )
335{
[0cbef26]336        struct im_connection *ic = purple_ic_by_gc( gc );
337       
338        if( ic != NULL )
[b74b287]339        {
[0cbef26]340                imc_logout( ic, TRUE );
[b74b287]341        }
[860ba6a]342}
343
344static void prplcb_conn_notice( PurpleConnection *gc, const char *text )
345{
[0cbef26]346        struct im_connection *ic = purple_ic_by_gc( gc );
347       
348        if( ic != NULL )
349                imcb_log( ic, "%s", text );
[860ba6a]350}
351
352static void prplcb_conn_report_disconnect_reason( PurpleConnection *gc, PurpleConnectionError reason, const char *text )
353{
[0cbef26]354        struct im_connection *ic = purple_ic_by_gc( gc );
355       
[860ba6a]356        /* PURPLE_CONNECTION_ERROR_NAME_IN_USE means concurrent login,
357           should probably handle that. */
[0cbef26]358        if( ic != NULL )
359                imcb_error( ic, "%s", text );
[860ba6a]360}
361
362static PurpleConnectionUiOps bee_conn_uiops =
363{
364        prplcb_conn_progress,
365        prplcb_conn_connected,
366        prplcb_conn_disconnected,
367        prplcb_conn_notice,
368        NULL,
369        NULL,
370        NULL,
371        prplcb_conn_report_disconnect_reason,
372};
373
[7da726b]374static void prplcb_blist_new( PurpleBlistNode *node )
375{
376        PurpleBuddy *bud = (PurpleBuddy*) node;
377       
[db4cd40]378        if( node->type == PURPLE_BLIST_BUDDY_NODE )
[7da726b]379        {
[db4cd40]380                struct im_connection *ic = purple_ic_by_pa( bud->account );
381               
382                if( ic == NULL )
383                        return;
384               
[7da726b]385                imcb_add_buddy( ic, bud->name, NULL );
386                if( bud->server_alias )
[4524f66]387                {
388                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
[7da726b]389                        imcb_buddy_nick_hint( ic, bud->name, bud->server_alias );
[4524f66]390                }
[7da726b]391        }
392}
393
394static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node )
395{
396        PurpleBuddy *bud = (PurpleBuddy*) node;
397       
[db4cd40]398        if( node->type == PURPLE_BLIST_BUDDY_NODE )
[7da726b]399        {
[db4cd40]400                struct im_connection *ic = purple_ic_by_pa( bud->account );
[4f103ea]401                PurpleStatus *as;
402                int flags = 0;
403               
[db4cd40]404                if( ic == NULL )
405                        return;
406               
[4524f66]407                if( bud->server_alias )
408                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
409               
[4f103ea]410                flags |= purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0;
411                flags |= purple_presence_is_available( bud->presence ) ? 0 : OPT_AWAY;
412               
413                as = purple_presence_get_active_status( bud->presence );
414               
415                imcb_buddy_status( ic, bud->name, flags, purple_status_get_name( as ),
416                                   purple_status_get_attr_string( as, "message" ) );
[7da726b]417        }
418}
419
420static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node )
421{
[3e7b640]422        /*
[7da726b]423        PurpleBuddy *bud = (PurpleBuddy*) node;
424       
[db4cd40]425        if( node->type == PURPLE_BLIST_BUDDY_NODE )
[0cbef26]426        {
[db4cd40]427                struct im_connection *ic = purple_ic_by_pa( bud->account );
428               
429                if( ic == NULL )
430                        return;
431               
[0cbef26]432                imcb_remove_buddy( ic, bud->name, NULL );
433        }
[b3117f2]434        */
[7da726b]435}
436
437static PurpleBlistUiOps bee_blist_uiops =
438{
439        NULL,
440        prplcb_blist_new,
441        NULL,
442        prplcb_blist_update,
443        prplcb_blist_remove,
444};
445
[d250b2a]446static void prplcb_conv_im( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime )
447{
448        struct im_connection *ic = purple_ic_by_pa( conv->account );
[3e7b640]449        PurpleBuddy *buddy;
[d250b2a]450       
[389f7be]451        /* ..._SEND means it's an outgoing message, no need to echo those. */
[3e7b640]452        if( flags & PURPLE_MESSAGE_SEND )
453                return;
454       
455        buddy = purple_find_buddy( conv->account, who );
456        if( buddy != NULL )
[a19ea7a]457                who = purple_buddy_get_name( buddy );
[3e7b640]458       
459        imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime );
[d250b2a]460}
461
[860ba6a]462static PurpleConversationUiOps bee_conv_uiops = 
463{
464        NULL,                      /* create_conversation  */
465        NULL,                      /* destroy_conversation */
466        NULL,                      /* write_chat           */
[d250b2a]467        prplcb_conv_im,            /* write_im             */
[e046390]468        NULL,                      /* write_conv           */
[860ba6a]469        NULL,                      /* chat_add_users       */
470        NULL,                      /* chat_rename_user     */
471        NULL,                      /* chat_remove_users    */
472        NULL,                      /* chat_update_user     */
473        NULL,                      /* present              */
474        NULL,                      /* has_focus            */
475        NULL,                      /* custom_smiley_add    */
476        NULL,                      /* custom_smiley_write  */
477        NULL,                      /* custom_smiley_close  */
478        NULL,                      /* send_confirm         */
479};
480
[0ac1a375]481struct prplcb_request_action_data
482{
483        void *user_data, *bee_data;
484        PurpleRequestActionCb yes, no;
485        int yes_i, no_i;
486};
487
488static void prplcb_request_action_yes( void *data )
489{
490        struct prplcb_request_action_data *pqad = data;
491       
492        pqad->yes( pqad->user_data, pqad->yes_i );
493        g_free( pqad );
494}
495
496static void prplcb_request_action_no( void *data )
497{
498        struct prplcb_request_action_data *pqad = data;
499       
500        pqad->no( pqad->user_data, pqad->no_i );
501        g_free( pqad );
502}
503
504static void *prplcb_request_action( const char *title, const char *primary, const char *secondary,
505                                    int default_action, PurpleAccount *account, const char *who,
506                                    PurpleConversation *conv, void *user_data, size_t action_count,
507                                    va_list actions )
508{
509        struct prplcb_request_action_data *pqad; 
510        int i;
511        char *q;
512       
513        pqad = g_new0( struct prplcb_request_action_data, 1 );
514       
515        for( i = 0; i < action_count; i ++ )
516        {
517                char *caption;
518                void *fn;
519               
520                caption = va_arg( actions, char* );
521                fn = va_arg( actions, void* );
522               
523                if( strcmp( caption, "Accept" ) == 0 )
524                {
525                        pqad->yes = fn;
526                        pqad->yes_i = i;
527                }
528                else if( strcmp( caption, "Reject" ) == 0 )
529                {
530                        pqad->no = fn;
531                        pqad->no_i = i;
532                }
533        }
534       
535        pqad->user_data = user_data;
536       
537        q = g_strdup_printf( "Request: %s\n\n%s\n\n%s", title, primary, secondary );
538        pqad->bee_data = query_add( local_irc, purple_ic_by_pa( account ), q,
539                prplcb_request_action_yes, prplcb_request_action_no, pqad );
540       
541        g_free( q );
[e5d8d21]542       
543        return pqad;
[0ac1a375]544}
545
546static PurpleRequestUiOps bee_request_uiops =
547{
548        NULL,
549        NULL,
550        prplcb_request_action,
551        NULL,
552        NULL,
553        NULL,
554        NULL,
555};
556
[0cbef26]557static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s )
558{
[6967d01]559        fprintf( stderr, "DEBUG %s: %s", category, arg_s );
[0cbef26]560}
561
562static PurpleDebugUiOps bee_debug_uiops =
563{
564        prplcb_debug_print,
565};
566
[4164e62]567static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer udata )
568{
569        return b_timeout_add( interval, (b_event_handler) func, udata );
570}
571
572static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata )
573{
574        return b_input_add( fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata );
575}
576
577static gboolean prplcb_ev_remove( guint id )
578{
579        b_event_remove( (gint) id );
580        return TRUE;
581}
582
583static PurpleEventLoopUiOps glib_eventloops = 
584{
585        prplcb_ev_timeout_add,
586        prplcb_ev_remove,
587        prplcb_ev_input_add,
588        prplcb_ev_remove,
589};
590
[860ba6a]591static void purple_ui_init()
592{
[7da726b]593        purple_blist_set_ui_ops( &bee_blist_uiops );
[860ba6a]594        purple_connections_set_ui_ops( &bee_conn_uiops );
595        purple_conversations_set_ui_ops( &bee_conv_uiops );
[0ac1a375]596        purple_request_set_ui_ops( &bee_request_uiops );
[0cbef26]597        //purple_debug_set_ui_ops( &bee_debug_uiops );
[860ba6a]598}
599
[796da03]600void purple_initmodule()
601{
[cd741d8]602        struct prpl funcs;
[796da03]603        GList *prots;
[e5d8d21]604        GString *help;
[796da03]605       
[e046390]606        if( B_EV_IO_READ != PURPLE_INPUT_READ ||
607            B_EV_IO_WRITE != PURPLE_INPUT_WRITE )
608        {
609                /* FIXME FIXME FIXME FIXME FIXME :-) */
610                exit( 1 );
611        }
612       
[796da03]613        purple_util_set_user_dir("/tmp");
614        purple_debug_set_enabled(FALSE);
615        purple_core_set_ui_ops(&bee_core_uiops);
616        purple_eventloop_set_ui_ops(&glib_eventloops);
617        if( !purple_core_init( "BitlBee") )
618        {
619                /* Initializing the core failed. Terminate. */
620                fprintf( stderr, "libpurple initialization failed.\n" );
621                abort();
622        }
623       
624        /* This seems like stateful shit we don't want... */
625        purple_set_blist(purple_blist_new());
626        purple_blist_load();
627       
628        /* Meh? */
629        purple_prefs_load();
630       
[cd741d8]631        memset( &funcs, 0, sizeof( funcs ) );
632        funcs.login = purple_login;
633        funcs.init = purple_init;
634        funcs.logout = purple_logout;
635        funcs.buddy_msg = purple_buddy_msg;
636        funcs.away_states = purple_away_states;
637        funcs.set_away = purple_set_away;
638        funcs.add_buddy = purple_add_buddy;
639        funcs.remove_buddy = purple_remove_buddy;
640        funcs.keepalive = purple_keepalive;
641        funcs.send_typing = purple_send_typing;
642        funcs.handle_cmp = g_strcasecmp;
643       
[e5d8d21]644        help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n");
645       
[796da03]646        for( prots = purple_plugins_get_protocols(); prots; prots = prots->next )
647        {
648                PurplePlugin *prot = prots->data;
[cd741d8]649                struct prpl *ret;
[796da03]650               
[cd741d8]651                ret = g_memdup( &funcs, sizeof( funcs ) );
652                ret->name = ret->data = prot->info->id;
653                if( strncmp( ret->name, "prpl-", 5 ) == 0 )
654                        ret->name += 5;
[796da03]655                register_protocol( ret );
[cd741d8]656               
[e5d8d21]657                g_string_append_printf( help, "\n* %s (%s)", ret->name, prot->info->name );
658               
[cd741d8]659                if( g_strcasecmp( prot->info->id, "prpl-aim" ) == 0 )
660                {
661                        ret = g_memdup( &funcs, sizeof( funcs ) );
662                        ret->name = "oscar";
663                        ret->data = prot->info->id;
664                        register_protocol( ret );
665                }
[796da03]666        }
[e5d8d21]667       
668        help_add_mem( &global.help, "purple", help->str );
669        g_string_free( help, TRUE );
[796da03]670}
Note: See TracBrowser for help on using the repository browser.