source: protocols/purple/purple.c @ 0cb71a6

Last change on this file since 0cb71a6 was 7c5affca, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-03-12T01:47:44Z

Add some simple information about available settings to the online help
command.

  • Property mode set to 100644
File size: 21.0 KB
Line 
1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  libpurple module - Main file                                             *
5*                                                                           *
6*  Copyright 2009-2010 Wilmer van der Gaast <wilmer@gaast.net>              *
7*                                                                           *
8*  This program is free software; you can redistribute it and/or modify     *
9*  it under the terms of the GNU General Public License as published by     *
10*  the Free Software Foundation; either version 2 of the License, or        *
11*  (at your option) any later version.                                      *
12*                                                                           *
13*  This program is distributed in the hope that it will be useful,          *
14*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
15*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
16*  GNU General Public License for more details.                             *
17*                                                                           *
18*  You should have received a copy of the GNU General Public License along  *
19*  with this program; if not, write to the Free Software Foundation, Inc.,  *
20*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
21*                                                                           *
22\***************************************************************************/
23
24#include "bitlbee.h"
25#include "help.h"
26
27#include <stdarg.h>
28
29#include <glib.h>
30#include <purple.h>
31
32GSList *purple_connections;
33
34/* This makes me VERY sad... :-( But some libpurple callbacks come in without
35   any context so this is the only way to get that. Don't want to support
36   libpurple in daemon mode anyway. */
37static irc_t *local_irc;
38
39static struct im_connection *purple_ic_by_pa( PurpleAccount *pa )
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
50static struct im_connection *purple_ic_by_gc( PurpleConnection *gc )
51{
52        return purple_ic_by_pa( purple_connection_get_account( gc ) );
53}
54
55static void purple_init( account_t *acc )
56{
57        PurplePlugin *prpl = purple_plugins_find_with_id( (char*) acc->prpl->data );
58        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
59        PurpleAccount *pa;
60        GList *i, *st;
61        set_t *s;
62        char help_title[64];
63        GString *help;
64       
65        help = g_string_new( "" );
66        g_string_printf( help, "BitlBee libpurple module %s (%s).\n\nSupported settings:",
67                                (char*) acc->prpl->name, prpl->info->name );
68       
69        /* Convert all protocol_options into per-account setting variables. */
70        for( i = pi->protocol_options; i; i = i->next )
71        {
72                PurpleAccountOption *o = i->data;
73                const char *name;
74                char *def = NULL;
75                set_eval eval = NULL;
76                void *eval_data = NULL;
77                GList *io = NULL;
78                GSList *opts = NULL;
79               
80                name = purple_account_option_get_setting( o );
81               
82                switch( purple_account_option_get_type( o ) )
83                {
84                case PURPLE_PREF_STRING:
85                        def = g_strdup( purple_account_option_get_default_string( o ) );
86                       
87                        g_string_append_printf( help, "\n* %s (%s), %s, default: %s",
88                                                name, purple_account_option_get_text( o ),
89                                                "string", def );
90                       
91                        break;
92               
93                case PURPLE_PREF_INT:
94                        def = g_strdup_printf( "%d", purple_account_option_get_default_int( o ) );
95                        eval = set_eval_int;
96                       
97                        g_string_append_printf( help, "\n* %s (%s), %s, default: %s",
98                                                name, purple_account_option_get_text( o ),
99                                                "integer", def );
100                       
101                        break;
102               
103                case PURPLE_PREF_BOOLEAN:
104                        if( purple_account_option_get_default_bool( o ) )
105                                def = g_strdup( "true" );
106                        else
107                                def = g_strdup( "false" );
108                        eval = set_eval_bool;
109                       
110                        g_string_append_printf( help, "\n* %s (%s), %s, default: %s",
111                                                name, purple_account_option_get_text( o ),
112                                                "boolean", def );
113                       
114                        break;
115               
116                case PURPLE_PREF_STRING_LIST:
117                        def = g_strdup( purple_account_option_get_default_list_value( o ) );
118                       
119                        g_string_append_printf( help, "\n* %s (%s), %s, default: %s",
120                                                name, purple_account_option_get_text( o ),
121                                                "list", def );
122                        g_string_append( help, "\n  Possible values: " );
123                       
124                        for( io = purple_account_option_get_list( o ); io; io = io->next )
125                        {
126                                PurpleKeyValuePair *kv = io->data;
127                                opts = g_slist_append( opts, kv->key );
128                                g_string_append_printf( help, "%s, ", kv->key );
129                        }
130                        g_string_truncate( help, help->len - 2 );
131                        eval = set_eval_list;
132                        eval_data = opts;
133                       
134                        break;
135                       
136                default:
137                        irc_usermsg( acc->irc, "Setting with unknown type: %s (%d) Expect stuff to break..\n",
138                                     name, purple_account_option_get_type( o ) );
139                        name = NULL;
140                }
141               
142                if( name != NULL )
143                {
144                        s = set_add( &acc->set, name, def, eval, acc );
145                        s->flags |= ACC_SET_OFFLINE_ONLY;
146                        s->eval_data = eval_data;
147                        g_free( def );
148                }
149        }
150       
151        g_snprintf( help_title, sizeof( help_title ), "purple %s", (char*) acc->prpl->name );
152        help_add_mem( &global.help, help_title, help->str );
153        g_string_free( help, TRUE );
154       
155        if( pi->options & OPT_PROTO_MAIL_CHECK )
156        {
157                s = set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc );
158                s->flags |= ACC_SET_OFFLINE_ONLY;
159        }
160       
161        /* Go through all away states to figure out if away/status messages
162           are possible. */
163        pa = purple_account_new( acc->user, (char*) acc->prpl->data );
164        for( st = purple_account_get_status_types( pa ); st; st = st->next )
165        {
166                PurpleStatusPrimitive prim = purple_status_type_get_primitive( st->data );
167               
168                if( prim == PURPLE_STATUS_AVAILABLE )
169                {
170                        if( purple_status_type_get_attr( st->data, "message" ) )
171                                acc->flags |= ACC_FLAG_STATUS_MESSAGE;
172                }
173                else if( prim != PURPLE_STATUS_OFFLINE )
174                {
175                        if( purple_status_type_get_attr( st->data, "message" ) )
176                                acc->flags |= ACC_FLAG_AWAY_MESSAGE;
177                }
178        }
179        purple_accounts_remove( pa );
180}
181
182static void purple_sync_settings( account_t *acc, PurpleAccount *pa )
183{
184        PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id );
185        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
186        GList *i;
187       
188        for( i = pi->protocol_options; i; i = i->next )
189        {
190                PurpleAccountOption *o = i->data;
191                const char *name;
192                set_t *s;
193               
194                name = purple_account_option_get_setting( o );
195                s = set_find( &acc->set, name );
196                if( s->value == NULL )
197                        continue;
198               
199                switch( purple_account_option_get_type( o ) )
200                {
201                case PURPLE_PREF_STRING:
202                case PURPLE_PREF_STRING_LIST:
203                        purple_account_set_string( pa, name, set_getstr( &acc->set, name ) );
204                        break;
205               
206                case PURPLE_PREF_INT:
207                        purple_account_set_int( pa, name, set_getint( &acc->set, name ) );
208                        break;
209               
210                case PURPLE_PREF_BOOLEAN:
211                        purple_account_set_bool( pa, name, set_getbool( &acc->set, name ) );
212                        break;
213               
214                default:
215                        break;
216                }
217        }
218       
219        if( pi->options & OPT_PROTO_MAIL_CHECK )
220                purple_account_set_check_mail( pa, set_getbool( &acc->set, "mail_notifications" ) );
221}
222
223static void purple_login( account_t *acc )
224{
225        struct im_connection *ic = imcb_new( acc );
226        PurpleAccount *pa;
227       
228        if( local_irc != NULL && local_irc != acc->irc )
229        {
230                irc_usermsg( acc->irc, "Daemon mode detected. Do *not* try to use libpurple in daemon mode! "
231                                       "Please use inetd or ForkDaemon mode instead." );
232                return;
233        }
234        local_irc = acc->irc;
235       
236        /* For now this is needed in the _connected() handlers if using
237           GLib event handling, to make sure we're not handling events
238           on dead connections. */
239        purple_connections = g_slist_prepend( purple_connections, ic );
240       
241        ic->proto_data = pa = purple_account_new( acc->user, (char*) acc->prpl->data );
242        purple_account_set_password( pa, acc->pass );
243        purple_sync_settings( acc, pa );
244       
245        purple_account_set_enabled( pa, "BitlBee", TRUE );
246}
247
248static void purple_logout( struct im_connection *ic )
249{
250        PurpleAccount *pa = ic->proto_data;
251       
252        purple_account_set_enabled( pa, "BitlBee", FALSE );
253        purple_connections = g_slist_remove( purple_connections, ic );
254        purple_accounts_remove( pa );
255}
256
257static int purple_buddy_msg( struct im_connection *ic, char *who, char *message, int flags )
258{
259        PurpleConversation *conv;
260       
261        if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM,
262                                                            who, ic->proto_data ) ) == NULL )
263        {
264                conv = purple_conversation_new( PURPLE_CONV_TYPE_IM,
265                                                ic->proto_data, who );
266        }
267       
268        purple_conv_im_send( purple_conversation_get_im_data( conv ), message );
269       
270        return 1;
271}
272
273static GList *purple_away_states( struct im_connection *ic )
274{
275        PurpleAccount *pa = ic->proto_data;
276        GList *st, *ret = NULL;
277       
278        for( st = purple_account_get_status_types( pa ); st; st = st->next )
279        {
280                PurpleStatusPrimitive prim = purple_status_type_get_primitive( st->data );
281                if( prim != PURPLE_STATUS_AVAILABLE && prim != PURPLE_STATUS_OFFLINE )
282                        ret = g_list_append( ret, (void*) purple_status_type_get_name( st->data ) );
283        }
284       
285        return ret;
286}
287
288static void purple_set_away( struct im_connection *ic, char *state_txt, char *message )
289{
290        PurpleAccount *pa = ic->proto_data;
291        GList *status_types = purple_account_get_status_types( pa ), *st;
292        PurpleStatusType *pst = NULL;
293        GList *args = NULL;
294       
295        for( st = status_types; st; st = st->next )
296        {
297                pst = st->data;
298               
299                if( state_txt == NULL &&
300                    purple_status_type_get_primitive( pst ) == PURPLE_STATUS_AVAILABLE )
301                        break;
302
303                if( state_txt != NULL &&
304                    g_strcasecmp( state_txt, purple_status_type_get_name( pst ) ) == 0 )
305                        break;
306        }
307       
308        if( message && purple_status_type_get_attr( pst, "message" ) )
309        {
310                args = g_list_append( args, "message" );
311                args = g_list_append( args, message );
312        }
313       
314        purple_account_set_status_list( pa, st ? purple_status_type_get_id( pst ) : "away",
315                                        TRUE, args );
316
317        g_list_free( args );
318}
319
320static void purple_add_buddy( struct im_connection *ic, char *who, char *group )
321{
322        PurpleBuddy *pb;
323       
324        pb = purple_buddy_new( (PurpleAccount*) ic->proto_data, who, NULL );
325        purple_blist_add_buddy( pb, NULL, NULL, NULL );
326        purple_account_add_buddy( (PurpleAccount*) ic->proto_data, pb );
327}
328
329static void purple_remove_buddy( struct im_connection *ic, char *who, char *group )
330{
331        PurpleBuddy *pb;
332       
333        pb = purple_find_buddy( (PurpleAccount*) ic->proto_data, who );
334        if( pb != NULL )
335        {
336                purple_account_remove_buddy( (PurpleAccount*) ic->proto_data, pb, NULL );
337                purple_blist_remove_buddy( pb );
338        }
339}
340
341static void purple_keepalive( struct im_connection *ic )
342{
343}
344
345static int purple_send_typing( struct im_connection *ic, char *who, int flags )
346{
347        PurpleTypingState state = PURPLE_NOT_TYPING;
348        PurpleConversation *conv;
349       
350        if( flags & OPT_TYPING )
351                state = PURPLE_TYPING;
352        else if( flags & OPT_THINKING )
353                state = PURPLE_TYPED;
354       
355        if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM,
356                                                            who, ic->proto_data ) ) == NULL )
357        {
358                purple_conv_im_set_typing_state( purple_conversation_get_im_data( conv ), state );
359                return 1;
360        }
361        else
362        {
363                return 0;
364        }
365}
366
367static void purple_ui_init();
368
369static PurpleCoreUiOps bee_core_uiops = 
370{
371        NULL,
372        NULL,
373        purple_ui_init,
374        NULL,
375};
376
377static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t step, size_t step_count )
378{
379        struct im_connection *ic = purple_ic_by_gc( gc );
380       
381        imcb_log( ic, "%s", text );
382}
383
384static void prplcb_conn_connected( PurpleConnection *gc )
385{
386        struct im_connection *ic = purple_ic_by_gc( gc );
387       
388        imcb_connected( ic );
389       
390        if( gc->flags & PURPLE_CONNECTION_HTML )
391                ic->flags |= OPT_DOES_HTML;
392}
393
394static void prplcb_conn_disconnected( PurpleConnection *gc )
395{
396        struct im_connection *ic = purple_ic_by_gc( gc );
397       
398        if( ic != NULL )
399        {
400                imc_logout( ic, TRUE );
401        }
402}
403
404static void prplcb_conn_notice( PurpleConnection *gc, const char *text )
405{
406        struct im_connection *ic = purple_ic_by_gc( gc );
407       
408        if( ic != NULL )
409                imcb_log( ic, "%s", text );
410}
411
412static void prplcb_conn_report_disconnect_reason( PurpleConnection *gc, PurpleConnectionError reason, const char *text )
413{
414        struct im_connection *ic = purple_ic_by_gc( gc );
415       
416        /* PURPLE_CONNECTION_ERROR_NAME_IN_USE means concurrent login,
417           should probably handle that. */
418        if( ic != NULL )
419                imcb_error( ic, "%s", text );
420}
421
422static PurpleConnectionUiOps bee_conn_uiops =
423{
424        prplcb_conn_progress,
425        prplcb_conn_connected,
426        prplcb_conn_disconnected,
427        prplcb_conn_notice,
428        NULL,
429        NULL,
430        NULL,
431        prplcb_conn_report_disconnect_reason,
432};
433
434static void prplcb_blist_new( PurpleBlistNode *node )
435{
436        PurpleBuddy *bud = (PurpleBuddy*) node;
437       
438        if( node->type == PURPLE_BLIST_BUDDY_NODE )
439        {
440                struct im_connection *ic = purple_ic_by_pa( bud->account );
441               
442                if( ic == NULL )
443                        return;
444               
445                imcb_add_buddy( ic, bud->name, NULL );
446                if( bud->server_alias )
447                {
448                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
449                        imcb_buddy_nick_hint( ic, bud->name, bud->server_alias );
450                }
451        }
452}
453
454static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node )
455{
456        PurpleBuddy *bud = (PurpleBuddy*) node;
457       
458        if( node->type == PURPLE_BLIST_BUDDY_NODE )
459        {
460                struct im_connection *ic = purple_ic_by_pa( bud->account );
461                PurpleStatus *as;
462                int flags = 0;
463               
464                if( ic == NULL )
465                        return;
466               
467                if( bud->server_alias )
468                        imcb_rename_buddy( ic, bud->name, bud->server_alias );
469               
470                flags |= purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0;
471                flags |= purple_presence_is_available( bud->presence ) ? 0 : OPT_AWAY;
472               
473                as = purple_presence_get_active_status( bud->presence );
474               
475                imcb_buddy_status( ic, bud->name, flags, purple_status_get_name( as ),
476                                   purple_status_get_attr_string( as, "message" ) );
477        }
478}
479
480static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node )
481{
482        /*
483        PurpleBuddy *bud = (PurpleBuddy*) node;
484       
485        if( node->type == PURPLE_BLIST_BUDDY_NODE )
486        {
487                struct im_connection *ic = purple_ic_by_pa( bud->account );
488               
489                if( ic == NULL )
490                        return;
491               
492                imcb_remove_buddy( ic, bud->name, NULL );
493        }
494        */
495}
496
497static PurpleBlistUiOps bee_blist_uiops =
498{
499        NULL,
500        prplcb_blist_new,
501        NULL,
502        prplcb_blist_update,
503        prplcb_blist_remove,
504};
505
506static void prplcb_conv_im( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime )
507{
508        struct im_connection *ic = purple_ic_by_pa( conv->account );
509        PurpleBuddy *buddy;
510       
511        /* ..._SEND means it's an outgoing message, no need to echo those. */
512        if( flags & PURPLE_MESSAGE_SEND )
513                return;
514       
515        buddy = purple_find_buddy( conv->account, who );
516        if( buddy != NULL )
517                who = purple_buddy_get_name( buddy );
518       
519        imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime );
520}
521
522static PurpleConversationUiOps bee_conv_uiops = 
523{
524        NULL,                      /* create_conversation  */
525        NULL,                      /* destroy_conversation */
526        NULL,                      /* write_chat           */
527        prplcb_conv_im,            /* write_im             */
528        NULL,                      /* write_conv           */
529        NULL,                      /* chat_add_users       */
530        NULL,                      /* chat_rename_user     */
531        NULL,                      /* chat_remove_users    */
532        NULL,                      /* chat_update_user     */
533        NULL,                      /* present              */
534        NULL,                      /* has_focus            */
535        NULL,                      /* custom_smiley_add    */
536        NULL,                      /* custom_smiley_write  */
537        NULL,                      /* custom_smiley_close  */
538        NULL,                      /* send_confirm         */
539};
540
541struct prplcb_request_action_data
542{
543        void *user_data, *bee_data;
544        PurpleRequestActionCb yes, no;
545        int yes_i, no_i;
546};
547
548static void prplcb_request_action_yes( void *data )
549{
550        struct prplcb_request_action_data *pqad = data;
551       
552        pqad->yes( pqad->user_data, pqad->yes_i );
553        g_free( pqad );
554}
555
556static void prplcb_request_action_no( void *data )
557{
558        struct prplcb_request_action_data *pqad = data;
559       
560        pqad->no( pqad->user_data, pqad->no_i );
561        g_free( pqad );
562}
563
564static void *prplcb_request_action( const char *title, const char *primary, const char *secondary,
565                                    int default_action, PurpleAccount *account, const char *who,
566                                    PurpleConversation *conv, void *user_data, size_t action_count,
567                                    va_list actions )
568{
569        struct prplcb_request_action_data *pqad; 
570        int i;
571        char *q;
572       
573        pqad = g_new0( struct prplcb_request_action_data, 1 );
574       
575        for( i = 0; i < action_count; i ++ )
576        {
577                char *caption;
578                void *fn;
579               
580                caption = va_arg( actions, char* );
581                fn = va_arg( actions, void* );
582               
583                if( strcmp( caption, "Accept" ) == 0 )
584                {
585                        pqad->yes = fn;
586                        pqad->yes_i = i;
587                }
588                else if( strcmp( caption, "Reject" ) == 0 )
589                {
590                        pqad->no = fn;
591                        pqad->no_i = i;
592                }
593        }
594       
595        pqad->user_data = user_data;
596       
597        q = g_strdup_printf( "Request: %s\n\n%s\n\n%s", title, primary, secondary );
598        pqad->bee_data = query_add( local_irc, purple_ic_by_pa( account ), q,
599                prplcb_request_action_yes, prplcb_request_action_no, pqad );
600       
601        g_free( q );
602       
603        return pqad;
604}
605
606static PurpleRequestUiOps bee_request_uiops =
607{
608        NULL,
609        NULL,
610        prplcb_request_action,
611        NULL,
612        NULL,
613        NULL,
614        NULL,
615};
616
617static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s )
618{
619        fprintf( stderr, "DEBUG %s: %s", category, arg_s );
620}
621
622static PurpleDebugUiOps bee_debug_uiops =
623{
624        prplcb_debug_print,
625};
626
627static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer udata )
628{
629        return b_timeout_add( interval, (b_event_handler) func, udata );
630}
631
632static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata )
633{
634        return b_input_add( fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata );
635}
636
637static gboolean prplcb_ev_remove( guint id )
638{
639        b_event_remove( (gint) id );
640        return TRUE;
641}
642
643static PurpleEventLoopUiOps glib_eventloops = 
644{
645        prplcb_ev_timeout_add,
646        prplcb_ev_remove,
647        prplcb_ev_input_add,
648        prplcb_ev_remove,
649};
650
651static void *prplcb_notify_email( PurpleConnection *gc, const char *subject, const char *from,
652                                  const char *to, const char *url )
653{
654        struct im_connection *ic = purple_ic_by_gc( gc );
655       
656        imcb_log( ic, "Received e-mail from %s for %s: %s <%s>", from, to, subject, url );
657       
658        return NULL;
659}
660
661static  PurpleNotifyUiOps bee_notify_uiops =
662{
663        NULL,
664        prplcb_notify_email,
665};
666
667static void purple_ui_init()
668{
669        purple_blist_set_ui_ops( &bee_blist_uiops );
670        purple_connections_set_ui_ops( &bee_conn_uiops );
671        purple_conversations_set_ui_ops( &bee_conv_uiops );
672        purple_request_set_ui_ops( &bee_request_uiops );
673        purple_notify_set_ui_ops(&bee_notify_uiops);
674        //purple_debug_set_ui_ops( &bee_debug_uiops );
675}
676
677void purple_initmodule()
678{
679        struct prpl funcs;
680        GList *prots;
681        GString *help;
682       
683        if( B_EV_IO_READ != PURPLE_INPUT_READ ||
684            B_EV_IO_WRITE != PURPLE_INPUT_WRITE )
685        {
686                /* FIXME FIXME FIXME FIXME FIXME :-) */
687                exit( 1 );
688        }
689       
690        purple_util_set_user_dir("/tmp");
691        purple_debug_set_enabled(FALSE);
692        purple_core_set_ui_ops(&bee_core_uiops);
693        purple_eventloop_set_ui_ops(&glib_eventloops);
694        if( !purple_core_init( "BitlBee") )
695        {
696                /* Initializing the core failed. Terminate. */
697                fprintf( stderr, "libpurple initialization failed.\n" );
698                abort();
699        }
700       
701        /* This seems like stateful shit we don't want... */
702        purple_set_blist(purple_blist_new());
703        purple_blist_load();
704       
705        /* Meh? */
706        purple_prefs_load();
707       
708        memset( &funcs, 0, sizeof( funcs ) );
709        funcs.login = purple_login;
710        funcs.init = purple_init;
711        funcs.logout = purple_logout;
712        funcs.buddy_msg = purple_buddy_msg;
713        funcs.away_states = purple_away_states;
714        funcs.set_away = purple_set_away;
715        funcs.add_buddy = purple_add_buddy;
716        funcs.remove_buddy = purple_remove_buddy;
717        funcs.keepalive = purple_keepalive;
718        funcs.send_typing = purple_send_typing;
719        funcs.handle_cmp = g_strcasecmp;
720       
721        help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n");
722       
723        /* Add a protocol entry to BitlBee's structures for every protocol
724           supported by this libpurple instance. */     
725        for( prots = purple_plugins_get_protocols(); prots; prots = prots->next )
726        {
727                PurplePlugin *prot = prots->data;
728                struct prpl *ret;
729               
730                ret = g_memdup( &funcs, sizeof( funcs ) );
731                ret->name = ret->data = prot->info->id;
732                if( strncmp( ret->name, "prpl-", 5 ) == 0 )
733                        ret->name += 5;
734                register_protocol( ret );
735               
736                g_string_append_printf( help, "\n* %s (%s)", ret->name, prot->info->name );
737               
738                /* libpurple doesn't define a protocol called OSCAR, but we
739                   need it to be compatible with normal BitlBee. */
740                if( g_strcasecmp( prot->info->id, "prpl-aim" ) == 0 )
741                {
742                        ret = g_memdup( &funcs, sizeof( funcs ) );
743                        ret->name = "oscar";
744                        ret->data = prot->info->id;
745                        register_protocol( ret );
746                }
747        }
748       
749        g_string_append( help, "\n\nFor used protocols, more information about available "
750                         "settings can be found using \x02help purple <protocol name>\x02" );
751       
752        /* Add a simple dynamically-generated help item listing all
753           the supported protocols. */
754        help_add_mem( &global.help, "purple", help->str );
755        g_string_free( help, TRUE );
756}
Note: See TracBrowser for help on using the repository browser.