source: protocols/nogaim.c @ fb98634

Last change on this file since fb98634 was 94acdd0, checked in by Wilmer van der Gaast <wilmer@…>, at 2008-09-28T11:18:19Z

Restored support for password-protected chatrooms (for now only by accepting
a password in the IRC JOIN command).

  • Property mode set to 100644
File size: 27.4 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2006 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/*
8 * nogaim
9 *
10 * Gaim without gaim - for BitlBee
11 *
12 * This file contains functions called by the Gaim IM-modules. It's written
13 * from scratch for BitlBee and doesn't contain any code from Gaim anymore
14 * (except for the function names).
15 */
16
17/*
18  This program is free software; you can redistribute it and/or modify
19  it under the terms of the GNU General Public License as published by
20  the Free Software Foundation; either version 2 of the License, or
21  (at your option) any later version.
22
23  This program is distributed in the hope that it will be useful,
24  but WITHOUT ANY WARRANTY; without even the implied warranty of
25  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26  GNU General Public License for more details.
27
28  You should have received a copy of the GNU General Public License with
29  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
30  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
31  Suite 330, Boston, MA  02111-1307  USA
32*/
33
34#define BITLBEE_CORE
35#include "nogaim.h"
36#include <ctype.h>
37
38static int remove_chat_buddy_silent( struct groupchat *b, const char *handle );
39
40GSList *connections;
41
42#ifdef WITH_PLUGINS
43gboolean load_plugin(char *path)
44{
45        void (*init_function) (void);
46       
47        GModule *mod = g_module_open(path, G_MODULE_BIND_LAZY);
48
49        if(!mod) {
50                log_message(LOGLVL_ERROR, "Can't find `%s', not loading (%s)\n", path, g_module_error());
51                return FALSE;
52        }
53
54        if(!g_module_symbol(mod,"init_plugin",(gpointer *) &init_function)) {
55                log_message(LOGLVL_WARNING, "Can't find function `init_plugin' in `%s'\n", path);
56                return FALSE;
57        }
58
59        init_function();
60
61        return TRUE;
62}
63
64void load_plugins(void)
65{
66        GDir *dir;
67        GError *error = NULL;
68
69        dir = g_dir_open(global.conf->plugindir, 0, &error);
70
71        if (dir) {
72                const gchar *entry;
73                char *path;
74
75                while ((entry = g_dir_read_name(dir))) {
76                        path = g_build_filename(global.conf->plugindir, entry, NULL);
77                        if(!path) {
78                                log_message(LOGLVL_WARNING, "Can't build path for %s\n", entry);
79                                continue;
80                        }
81
82                        load_plugin(path);
83
84                        g_free(path);
85                }
86
87                g_dir_close(dir);
88        }
89}
90#endif
91
92/* nogaim.c */
93
94GList *protocols = NULL;
95 
96void register_protocol (struct prpl *p)
97{
98        protocols = g_list_append(protocols, p);
99}
100
101struct prpl *find_protocol(const char *name)
102{
103        GList *gl;
104        for (gl = protocols; gl; gl = gl->next) 
105        {
106                struct prpl *proto = gl->data;
107                if(!g_strcasecmp(proto->name, name)) 
108                        return proto;
109        }
110        return NULL;
111}
112
113/* nogaim.c */
114void nogaim_init()
115{
116        extern void msn_initmodule();
117        extern void oscar_initmodule();
118        extern void byahoo_initmodule();
119        extern void jabber_initmodule();
120
121#ifdef WITH_MSN
122        msn_initmodule();
123#endif
124
125#ifdef WITH_OSCAR
126        oscar_initmodule();
127#endif
128       
129#ifdef WITH_YAHOO
130        byahoo_initmodule();
131#endif
132       
133#ifdef WITH_JABBER
134        jabber_initmodule();
135#endif
136
137#ifdef WITH_PLUGINS
138        load_plugins();
139#endif
140}
141
142GSList *get_connections() { return connections; }
143
144/* multi.c */
145
146struct im_connection *imcb_new( account_t *acc )
147{
148        struct im_connection *ic;
149       
150        ic = g_new0( struct im_connection, 1 );
151       
152        ic->irc = acc->irc;
153        ic->acc = acc;
154        acc->ic = ic;
155       
156        connections = g_slist_append( connections, ic );
157       
158        return( ic );
159}
160
161void imc_free( struct im_connection *ic )
162{
163        account_t *a;
164       
165        /* Destroy the pointer to this connection from the account list */
166        for( a = ic->irc->accounts; a; a = a->next )
167                if( a->ic == ic )
168                {
169                        a->ic = NULL;
170                        break;
171                }
172       
173        connections = g_slist_remove( connections, ic );
174        g_free( ic );
175}
176
177static void serv_got_crap( struct im_connection *ic, char *format, ... )
178{
179        va_list params;
180        char *text;
181        account_t *a;
182       
183        va_start( params, format );
184        text = g_strdup_vprintf( format, params );
185        va_end( params );
186
187        if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) ||
188            ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) )
189                strip_html( text );
190       
191        /* Try to find a different connection on the same protocol. */
192        for( a = ic->irc->accounts; a; a = a->next )
193                if( a->prpl == ic->acc->prpl && a->ic != ic )
194                        break;
195       
196        /* If we found one, include the screenname in the message. */
197        if( a )
198                irc_usermsg( ic->irc, "%s(%s) - %s", ic->acc->prpl->name, ic->acc->user, text );
199        else
200                irc_usermsg( ic->irc, "%s - %s", ic->acc->prpl->name, text );
201       
202        g_free( text );
203}
204
205void imcb_log( struct im_connection *ic, char *format, ... )
206{
207        va_list params;
208        char *text;
209       
210        va_start( params, format );
211        text = g_strdup_vprintf( format, params );
212        va_end( params );
213       
214        if( ic->flags & OPT_LOGGED_IN )
215                serv_got_crap( ic, "%s", text );
216        else
217                serv_got_crap( ic, "Logging in: %s", text );
218       
219        g_free( text );
220}
221
222void imcb_error( struct im_connection *ic, char *format, ... )
223{
224        va_list params;
225        char *text;
226       
227        va_start( params, format );
228        text = g_strdup_vprintf( format, params );
229        va_end( params );
230       
231        if( ic->flags & OPT_LOGGED_IN )
232                serv_got_crap( ic, "Error: %s", text );
233        else
234                serv_got_crap( ic, "Couldn't log in: %s", text );
235       
236        g_free( text );
237}
238
239static gboolean send_keepalive( gpointer d, gint fd, b_input_condition cond )
240{
241        struct im_connection *ic = d;
242       
243        if( ic->acc->prpl->keepalive )
244                ic->acc->prpl->keepalive( ic );
245       
246        return TRUE;
247}
248
249void imcb_connected( struct im_connection *ic )
250{
251        irc_t *irc = ic->irc;
252        struct chat *c;
253        user_t *u;
254       
255        /* MSN servers sometimes redirect you to a different server and do
256           the whole login sequence again, so these "late" calls to this
257           function should be handled correctly. (IOW, ignored) */
258        if( ic->flags & OPT_LOGGED_IN )
259                return;
260       
261        u = user_find( ic->irc, ic->irc->nick );
262       
263        imcb_log( ic, "Logged in" );
264       
265        ic->keepalive = b_timeout_add( 60000, send_keepalive, ic );
266        ic->flags |= OPT_LOGGED_IN;
267       
268        /* Also necessary when we're not away, at least for some of the
269           protocols. */
270        imc_set_away( ic, u->away );
271       
272        /* Apparently we're connected successfully, so reset the
273           exponential backoff timer. */
274        ic->acc->auto_reconnect_delay = 0;
275       
276        for( c = irc->chatrooms; c; c = c->next )
277        {
278                if( c->acc != ic->acc )
279                        continue;
280               
281                if( set_getbool( &c->set, "auto_join" ) )
282                        chat_join( irc, c, NULL );
283        }
284}
285
286gboolean auto_reconnect( gpointer data, gint fd, b_input_condition cond )
287{
288        account_t *a = data;
289       
290        a->reconnect = 0;
291        account_on( a->irc, a );
292       
293        return( FALSE );        /* Only have to run the timeout once */
294}
295
296void cancel_auto_reconnect( account_t *a )
297{
298        b_event_remove( a->reconnect );
299        a->reconnect = 0;
300}
301
302void imc_logout( struct im_connection *ic, int allow_reconnect )
303{
304        irc_t *irc = ic->irc;
305        user_t *t, *u;
306        account_t *a;
307        int delay;
308       
309        /* Nested calls might happen sometimes, this is probably the best
310           place to catch them. */
311        if( ic->flags & OPT_LOGGING_OUT )
312                return;
313        else
314                ic->flags |= OPT_LOGGING_OUT;
315       
316        imcb_log( ic, "Signing off.." );
317       
318        b_event_remove( ic->keepalive );
319        ic->keepalive = 0;
320        ic->acc->prpl->logout( ic );
321        b_event_remove( ic->inpa );
322       
323        u = irc->users;
324        while( u )
325        {
326                if( u->ic == ic )
327                {
328                        t = u->next;
329                        user_del( irc, u->nick );
330                        u = t;
331                }
332                else
333                        u = u->next;
334        }
335       
336        query_del_by_conn( ic->irc, ic );
337       
338        for( a = irc->accounts; a; a = a->next )
339                if( a->ic == ic )
340                        break;
341       
342        if( !a )
343        {
344                /* Uhm... This is very sick. */
345        }
346        else if( allow_reconnect && set_getbool( &irc->set, "auto_reconnect" ) &&
347                 set_getbool( &a->set, "auto_reconnect" ) &&
348                 ( delay = account_reconnect_delay( a ) ) > 0 )
349        {
350                imcb_log( ic, "Reconnecting in %d seconds..", delay );
351                a->reconnect = b_timeout_add( delay * 1000, auto_reconnect, a );
352        }
353       
354        imc_free( ic );
355}
356
357
358/* dialogs.c */
359
360void imcb_ask( struct im_connection *ic, char *msg, void *data,
361               query_callback doit, query_callback dont )
362{
363        query_add( ic->irc, ic, msg, doit, dont, data );
364}
365
366
367/* list.c */
368
369void imcb_add_buddy( struct im_connection *ic, char *handle, char *group )
370{
371        user_t *u;
372        char nick[MAX_NICK_LENGTH+1], *s;
373        irc_t *irc = ic->irc;
374       
375        if( user_findhandle( ic, handle ) )
376        {
377                if( set_getbool( &irc->set, "debug" ) )
378                        imcb_log( ic, "User already exists, ignoring add request: %s", handle );
379               
380                return;
381               
382                /* Buddy seems to exist already. Let's ignore this request then...
383                   Eventually subsequent calls to this function *should* be possible
384                   when a buddy is in multiple groups. But for now BitlBee doesn't
385                   even support groups so let's silently ignore this for now. */
386        }
387       
388        memset( nick, 0, MAX_NICK_LENGTH + 1 );
389        strcpy( nick, nick_get( ic->acc, handle ) );
390       
391        u = user_add( ic->irc, nick );
392       
393//      if( !realname || !*realname ) realname = nick;
394//      u->realname = g_strdup( realname );
395       
396        if( ( s = strchr( handle, '@' ) ) )
397        {
398                u->host = g_strdup( s + 1 );
399                u->user = g_strndup( handle, s - handle );
400        }
401        else if( ic->acc->server )
402        {
403                u->host = g_strdup( ic->acc->server );
404                u->user = g_strdup( handle );
405               
406                /* s/ /_/ ... important for AOL screennames */
407                for( s = u->user; *s; s ++ )
408                        if( *s == ' ' )
409                                *s = '_';
410        }
411        else
412        {
413                u->host = g_strdup( ic->acc->prpl->name );
414                u->user = g_strdup( handle );
415        }
416       
417        u->ic = ic;
418        u->handle = g_strdup( handle );
419        if( group ) u->group = g_strdup( group );
420        u->send_handler = buddy_send_handler;
421        u->last_typing_notice = 0;
422}
423
424struct buddy *imcb_find_buddy( struct im_connection *ic, char *handle )
425{
426        static struct buddy b[1];
427        user_t *u;
428       
429        u = user_findhandle( ic, handle );
430       
431        if( !u )
432                return( NULL );
433       
434        memset( b, 0, sizeof( b ) );
435        strncpy( b->name, handle, 80 );
436        strncpy( b->show, u->realname, BUDDY_ALIAS_MAXLEN );
437        b->present = u->online;
438        b->ic = u->ic;
439       
440        return( b );
441}
442
443void imcb_rename_buddy( struct im_connection *ic, char *handle, char *realname )
444{
445        user_t *u = user_findhandle( ic, handle );
446       
447        if( !u || !realname ) return;
448       
449        if( g_strcasecmp( u->realname, realname ) != 0 )
450        {
451                if( u->realname != u->nick ) g_free( u->realname );
452               
453                u->realname = g_strdup( realname );
454               
455                if( ( ic->flags & OPT_LOGGED_IN ) && set_getbool( &ic->irc->set, "display_namechanges" ) )
456                        imcb_log( ic, "User `%s' changed name to `%s'", u->nick, u->realname );
457        }
458}
459
460void imcb_remove_buddy( struct im_connection *ic, char *handle, char *group )
461{
462        user_t *u;
463       
464        if( ( u = user_findhandle( ic, handle ) ) )
465                user_del( ic->irc, u->nick );
466}
467
468/* Mainly meant for ICQ (and now also for Jabber conferences) to allow IM
469   modules to suggest a nickname for a handle. */
470void imcb_buddy_nick_hint( struct im_connection *ic, char *handle, char *nick )
471{
472        user_t *u = user_findhandle( ic, handle );
473        char newnick[MAX_NICK_LENGTH+1], *orig_nick;
474       
475        if( u && !u->online && !nick_saved( ic->acc, handle ) )
476        {
477                /* Only do this if the person isn't online yet (which should
478                   be the case if we just added it) and if the user hasn't
479                   assigned a nickname to this buddy already. */
480               
481                strncpy( newnick, nick, MAX_NICK_LENGTH );
482                newnick[MAX_NICK_LENGTH] = 0;
483               
484                /* Some processing to make sure this string is a valid IRC nickname. */
485                nick_strip( newnick );
486                if( set_getbool( &ic->irc->set, "lcnicks" ) )
487                        nick_lc( newnick );
488               
489                if( strcmp( u->nick, newnick ) != 0 )
490                {
491                        /* Only do this if newnick is different from the current one.
492                           If rejoining a channel, maybe we got this nick already
493                           (and dedupe would only add an underscore. */
494                        nick_dedupe( ic->acc, handle, newnick );
495                       
496                        /* u->nick will be freed halfway the process, so it can't be
497                           passed as an argument. */
498                        orig_nick = g_strdup( u->nick );
499                        user_rename( ic->irc, orig_nick, newnick );
500                        g_free( orig_nick );
501                }
502        }
503}
504
505/* prpl.c */
506
507struct show_got_added_data
508{
509        struct im_connection *ic;
510        char *handle;
511};
512
513void show_got_added_no( void *data )
514{
515        g_free( ((struct show_got_added_data*)data)->handle );
516        g_free( data );
517}
518
519void show_got_added_yes( void *data )
520{
521        struct show_got_added_data *sga = data;
522       
523        sga->ic->acc->prpl->add_buddy( sga->ic, sga->handle, NULL );
524        /* imcb_add_buddy( sga->ic, NULL, sga->handle, sga->handle ); */
525       
526        return show_got_added_no( data );
527}
528
529void imcb_ask_add( struct im_connection *ic, char *handle, const char *realname )
530{
531        struct show_got_added_data *data = g_new0( struct show_got_added_data, 1 );
532        char *s;
533       
534        /* TODO: Make a setting for this! */
535        if( user_findhandle( ic, handle ) != NULL )
536                return;
537       
538        s = g_strdup_printf( "The user %s is not in your buddy list yet. Do you want to add him/her now?", handle );
539       
540        data->ic = ic;
541        data->handle = g_strdup( handle );
542        query_add( ic->irc, ic, s, show_got_added_yes, show_got_added_no, data );
543}
544
545
546/* server.c */                   
547
548void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, const char *state, const char *message )
549{
550        user_t *u;
551        int oa, oo;
552       
553        u = user_findhandle( ic, (char*) handle );
554       
555        if( !u )
556        {
557                if( g_strcasecmp( set_getstr( &ic->irc->set, "handle_unknown" ), "add" ) == 0 )
558                {
559                        imcb_add_buddy( ic, (char*) handle, NULL );
560                        u = user_findhandle( ic, (char*) handle );
561                }
562                else
563                {
564                        if( set_getbool( &ic->irc->set, "debug" ) || g_strcasecmp( set_getstr( &ic->irc->set, "handle_unknown" ), "ignore" ) != 0 )
565                        {
566                                imcb_log( ic, "imcb_buddy_status() for unknown handle %s:", handle );
567                                imcb_log( ic, "flags = %d, state = %s, message = %s", flags,
568                                          state ? state : "NULL", message ? message : "NULL" );
569                        }
570                       
571                        return;
572                }
573        }
574       
575        oa = u->away != NULL;
576        oo = u->online;
577       
578        if( u->away )
579        {
580                g_free( u->away );
581                u->away = NULL;
582        }
583       
584        if( ( flags & OPT_LOGGED_IN ) && !u->online )
585        {
586                irc_spawn( ic->irc, u );
587                u->online = 1;
588        }
589        else if( !( flags & OPT_LOGGED_IN ) && u->online )
590        {
591                struct groupchat *c;
592               
593                irc_kill( ic->irc, u );
594                u->online = 0;
595               
596                /* Remove him/her from the groupchats to prevent PART messages after he/she QUIT already */
597                for( c = ic->groupchats; c; c = c->next )
598                        remove_chat_buddy_silent( c, handle );
599        }
600       
601        if( flags & OPT_AWAY )
602        {
603                if( state && message )
604                {
605                        u->away = g_strdup_printf( "%s (%s)", state, message );
606                }
607                else if( state )
608                {
609                        u->away = g_strdup( state );
610                }
611                else if( message )
612                {
613                        u->away = g_strdup( message );
614                }
615                else
616                {
617                        u->away = g_strdup( "Away" );
618                }
619        }
620        /* else waste_any_state_information_for_now(); */
621       
622        /* LISPy... */
623        if( ( set_getbool( &ic->irc->set, "away_devoice" ) ) &&         /* Don't do a thing when user doesn't want it */
624            ( u->online ) &&                                            /* Don't touch offline people */
625            ( ( ( u->online != oo ) && !u->away ) ||                    /* Voice joining people */
626              ( ( u->online == oo ) && ( oa == !u->away ) ) ) )         /* (De)voice people changing state */
627        {
628                char *from;
629               
630                if( set_getbool( &ic->irc->set, "simulate_netsplit" ) )
631                {
632                        from = g_strdup( ic->irc->myhost );
633                }
634                else
635                {
636                        from = g_strdup_printf( "%s!%s@%s", ic->irc->mynick, ic->irc->mynick,
637                                                            ic->irc->myhost );
638                }
639                irc_write( ic->irc, ":%s MODE %s %cv %s", from, ic->irc->channel,
640                                                          u->away?'-':'+', u->nick );
641                g_free( from );
642        }
643}
644
645void imcb_buddy_msg( struct im_connection *ic, char *handle, char *msg, uint32_t flags, time_t sent_at )
646{
647        irc_t *irc = ic->irc;
648        char *wrapped;
649        user_t *u;
650       
651        u = user_findhandle( ic, handle );
652       
653        if( !u )
654        {
655                char *h = set_getstr( &irc->set, "handle_unknown" );
656               
657                if( g_strcasecmp( h, "ignore" ) == 0 )
658                {
659                        if( set_getbool( &irc->set, "debug" ) )
660                                imcb_log( ic, "Ignoring message from unknown handle %s", handle );
661                       
662                        return;
663                }
664                else if( g_strncasecmp( h, "add", 3 ) == 0 )
665                {
666                        int private = set_getbool( &irc->set, "private" );
667                       
668                        if( h[3] )
669                        {
670                                if( g_strcasecmp( h + 3, "_private" ) == 0 )
671                                        private = 1;
672                                else if( g_strcasecmp( h + 3, "_channel" ) == 0 )
673                                        private = 0;
674                        }
675                       
676                        imcb_add_buddy( ic, handle, NULL );
677                        u = user_findhandle( ic, handle );
678                        u->is_private = private;
679                }
680                else
681                {
682                        imcb_log( ic, "Message from unknown handle %s:", handle );
683                        u = user_find( irc, irc->mynick );
684                }
685        }
686       
687        if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) ||
688            ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) )
689                strip_html( msg );
690
691        wrapped = word_wrap( msg, 425 );
692        irc_msgfrom( irc, u->nick, wrapped );
693        g_free( wrapped );
694}
695
696void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags )
697{
698        user_t *u;
699       
700        if( !set_getbool( &ic->irc->set, "typing_notice" ) )
701                return;
702       
703        if( ( u = user_findhandle( ic, handle ) ) )
704        {
705                char buf[256]; 
706               
707                g_snprintf( buf, 256, "\1TYPING %d\1", ( flags >> 8 ) & 3 );
708                irc_privmsg( ic->irc, u, "PRIVMSG", ic->irc->nick, NULL, buf );
709        }
710}
711
712struct groupchat *imcb_chat_new( struct im_connection *ic, const char *handle )
713{
714        struct groupchat *c;
715       
716        /* This one just creates the conversation structure, user won't see anything yet */
717       
718        if( ic->groupchats )
719        {
720                for( c = ic->groupchats; c->next; c = c->next );
721                c = c->next = g_new0( struct groupchat, 1 );
722        }
723        else
724                ic->groupchats = c = g_new0( struct groupchat, 1 );
725       
726        c->ic = ic;
727        c->title = g_strdup( handle );
728        c->channel = g_strdup_printf( "&chat_%03d", ic->irc->c_id++ );
729        c->topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title );
730       
731        if( set_getbool( &ic->irc->set, "debug" ) )
732                imcb_log( ic, "Creating new conversation: (id=%p,handle=%s)", c, handle );
733       
734        return c;
735}
736
737void imcb_chat_free( struct groupchat *c )
738{
739        struct im_connection *ic = c->ic;
740        struct groupchat *l;
741        GList *ir;
742       
743        if( set_getbool( &ic->irc->set, "debug" ) )
744                imcb_log( ic, "You were removed from conversation %p", c );
745       
746        if( c )
747        {
748                if( c->joined )
749                {
750                        user_t *u, *r;
751                       
752                        r = user_find( ic->irc, ic->irc->mynick );
753                        irc_privmsg( ic->irc, r, "PRIVMSG", c->channel, "", "Cleaning up channel, bye!" );
754                       
755                        u = user_find( ic->irc, ic->irc->nick );
756                        irc_kick( ic->irc, u, c->channel, r );
757                        /* irc_part( ic->irc, u, c->channel ); */
758                }
759               
760                /* Find the previous chat in the linked list. */
761                for( l = ic->groupchats; l && l->next != c; l = l->next );
762               
763                if( l )
764                        l->next = c->next;
765                else
766                        ic->groupchats = c->next;
767               
768                for( ir = c->in_room; ir; ir = ir->next )
769                        g_free( ir->data );
770                g_list_free( c->in_room );
771                g_free( c->channel );
772                g_free( c->title );
773                g_free( c->topic );
774                g_free( c );
775        }
776}
777
778void imcb_chat_msg( struct groupchat *c, char *who, char *msg, uint32_t flags, time_t sent_at )
779{
780        struct im_connection *ic = c->ic;
781        char *wrapped;
782        user_t *u;
783       
784        /* Gaim sends own messages through this too. IRC doesn't want this, so kill them */
785        if( g_strcasecmp( who, ic->acc->user ) == 0 )
786                return;
787       
788        u = user_findhandle( ic, who );
789       
790        if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) ||
791            ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) )
792                strip_html( msg );
793       
794        wrapped = word_wrap( msg, 425 );
795        if( c && u )
796        {
797                irc_privmsg( ic->irc, u, "PRIVMSG", c->channel, "", wrapped );
798        }
799        else
800        {
801                imcb_log( ic, "Message from/to conversation %s@%p (unknown conv/user): %s", who, c, wrapped );
802        }
803        g_free( wrapped );
804}
805
806void imcb_chat_log( struct groupchat *c, char *format, ... )
807{
808        irc_t *irc = c->ic->irc;
809        va_list params;
810        char *text;
811        user_t *u;
812       
813        va_start( params, format );
814        text = g_strdup_vprintf( format, params );
815        va_end( params );
816       
817        u = user_find( irc, irc->mynick );
818       
819        irc_privmsg( irc, u, "PRIVMSG", c->channel, "System message: ", text );
820       
821        g_free( text );
822}
823
824void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at )
825{
826        struct im_connection *ic = c->ic;
827        user_t *u = NULL;
828       
829        if( who == NULL)
830                u = user_find( ic->irc, ic->irc->mynick );
831        else if( g_strcasecmp( who, ic->acc->user ) == 0 )
832                u = user_find( ic->irc, ic->irc->nick );
833        else
834                u = user_findhandle( ic, who );
835       
836        if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) ||
837            ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) )
838                strip_html( topic );
839       
840        g_free( c->topic );
841        c->topic = g_strdup( topic );
842       
843        if( c->joined && u )
844                irc_write( ic->irc, ":%s!%s@%s TOPIC %s :%s", u->nick, u->user, u->host, c->channel, topic );
845}
846
847
848/* buddy_chat.c */
849
850void imcb_chat_add_buddy( struct groupchat *b, char *handle )
851{
852        user_t *u = user_findhandle( b->ic, handle );
853        int me = 0;
854       
855        if( set_getbool( &b->ic->irc->set, "debug" ) )
856                imcb_log( b->ic, "User %s added to conversation %p", handle, b );
857       
858        /* It might be yourself! */
859        if( b->ic->acc->prpl->handle_cmp( handle, b->ic->acc->user ) == 0 )
860        {
861                u = user_find( b->ic->irc, b->ic->irc->nick );
862                if( !b->joined )
863                        irc_join( b->ic->irc, u, b->channel );
864                b->joined = me = 1;
865        }
866       
867        /* Most protocols allow people to join, even when they're not in
868           your contact list. Try to handle that here */
869        if( !u )
870        {
871                imcb_add_buddy( b->ic, handle, NULL );
872                u = user_findhandle( b->ic, handle );
873        }
874       
875        /* Add the handle to the room userlist, if it's not 'me' */
876        if( !me )
877        {
878                if( b->joined )
879                        irc_join( b->ic->irc, u, b->channel );
880                b->in_room = g_list_append( b->in_room, g_strdup( handle ) );
881        }
882}
883
884/* This function is one BIG hack... :-( EREWRITE */
885void imcb_chat_remove_buddy( struct groupchat *b, char *handle, char *reason )
886{
887        user_t *u;
888        int me = 0;
889       
890        if( set_getbool( &b->ic->irc->set, "debug" ) )
891                imcb_log( b->ic, "User %s removed from conversation %p (%s)", handle, b, reason ? reason : "" );
892       
893        /* It might be yourself! */
894        if( g_strcasecmp( handle, b->ic->acc->user ) == 0 )
895        {
896                if( b->joined == 0 )
897                        return;
898               
899                u = user_find( b->ic->irc, b->ic->irc->nick );
900                b->joined = 0;
901                me = 1;
902        }
903        else
904        {
905                u = user_findhandle( b->ic, handle );
906        }
907       
908        if( me || ( remove_chat_buddy_silent( b, handle ) && b->joined && u ) )
909                irc_part( b->ic->irc, u, b->channel );
910}
911
912static int remove_chat_buddy_silent( struct groupchat *b, const char *handle )
913{
914        GList *i;
915       
916        /* Find the handle in the room userlist and shoot it */
917        i = b->in_room;
918        while( i )
919        {
920                if( g_strcasecmp( handle, i->data ) == 0 )
921                {
922                        g_free( i->data );
923                        b->in_room = g_list_remove( b->in_room, i->data );
924                        return( 1 );
925                }
926               
927                i = i->next;
928        }
929       
930        return( 0 );
931}
932
933
934/* Misc. BitlBee stuff which shouldn't really be here */
935
936char *set_eval_away_devoice( set_t *set, char *value )
937{
938        irc_t *irc = set->data;
939        int st;
940       
941        if( !is_bool( value ) )
942                return SET_INVALID;
943       
944        st = bool2int( value );
945       
946        /* Horror.... */
947       
948        if( st != set_getbool( &irc->set, "away_devoice" ) )
949        {
950                char list[80] = "";
951                user_t *u = irc->users;
952                int i = 0, count = 0;
953                char pm;
954                char v[80];
955               
956                if( st )
957                        pm = '+';
958                else
959                        pm = '-';
960               
961                while( u )
962                {
963                        if( u->ic && u->online && !u->away )
964                        {
965                                if( ( strlen( list ) + strlen( u->nick ) ) >= 79 )
966                                {
967                                        for( i = 0; i < count; v[i++] = 'v' ); v[i] = 0;
968                                        irc_write( irc, ":%s MODE %s %c%s%s",
969                                                   irc->myhost,
970                                                   irc->channel, pm, v, list );
971                                       
972                                        *list = 0;
973                                        count = 0;
974                                }
975                               
976                                sprintf( list + strlen( list ), " %s", u->nick );
977                                count ++;
978                        }
979                        u = u->next;
980                }
981               
982                /* $v = 'v' x $i */
983                for( i = 0; i < count; v[i++] = 'v' ); v[i] = 0;
984                irc_write( irc, ":%s MODE %s %c%s%s", irc->myhost,
985                                                            irc->channel, pm, v, list );
986        }
987       
988        return value;
989}
990
991
992
993
994/* The plan is to not allow straight calls to prpl functions anymore, but do
995   them all from some wrappers. We'll start to define some down here: */
996
997int imc_buddy_msg( struct im_connection *ic, char *handle, char *msg, int flags )
998{
999        char *buf = NULL;
1000        int st;
1001       
1002        if( ( ic->flags & OPT_DOES_HTML ) && ( g_strncasecmp( msg, "<html>", 6 ) != 0 ) )
1003        {
1004                buf = escape_html( msg );
1005                msg = buf;
1006        }
1007       
1008        st = ic->acc->prpl->buddy_msg( ic, handle, msg, flags );
1009        g_free( buf );
1010       
1011        return st;
1012}
1013
1014int imc_chat_msg( struct groupchat *c, char *msg, int flags )
1015{
1016        char *buf = NULL;
1017       
1018        if( ( c->ic->flags & OPT_DOES_HTML ) && ( g_strncasecmp( msg, "<html>", 6 ) != 0 ) )
1019        {
1020                buf = escape_html( msg );
1021                msg = buf;
1022        }
1023       
1024        c->ic->acc->prpl->chat_msg( c, msg, flags );
1025        g_free( buf );
1026       
1027        return 1;
1028}
1029
1030static char *imc_away_alias_find( GList *gcm, char *away );
1031
1032int imc_set_away( struct im_connection *ic, char *away )
1033{
1034        GList *m, *ms;
1035        char *s;
1036       
1037        if( !away ) away = "";
1038        ms = m = ic->acc->prpl->away_states( ic );
1039       
1040        while( m )
1041        {
1042                if( *away )
1043                {
1044                        if( g_strncasecmp( m->data, away, strlen( m->data ) ) == 0 )
1045                                break;
1046                }
1047                else
1048                {
1049                        if( g_strcasecmp( m->data, "Available" ) == 0 )
1050                                break;
1051                        if( g_strcasecmp( m->data, "Online" ) == 0 )
1052                                break;
1053                }
1054                m = m->next;
1055        }
1056       
1057        if( m )
1058        {
1059                ic->acc->prpl->set_away( ic, m->data, *away ? away : NULL );
1060        }
1061        else
1062        {
1063                s = imc_away_alias_find( ms, away );
1064                if( s )
1065                {
1066                        ic->acc->prpl->set_away( ic, s, away );
1067                        if( set_getbool( &ic->irc->set, "debug" ) )
1068                                imcb_log( ic, "Setting away state to %s", s );
1069                }
1070                else
1071                        ic->acc->prpl->set_away( ic, GAIM_AWAY_CUSTOM, away );
1072        }
1073       
1074        return( 1 );
1075}
1076
1077static char *imc_away_alias_list[8][5] =
1078{
1079        { "Away from computer", "Away", "Extended away", NULL },
1080        { "NA", "N/A", "Not available", NULL },
1081        { "Busy", "Do not disturb", "DND", "Occupied", NULL },
1082        { "Be right back", "BRB", NULL },
1083        { "On the phone", "Phone", "On phone", NULL },
1084        { "Out to lunch", "Lunch", "Food", NULL },
1085        { "Invisible", "Hidden" },
1086        { NULL }
1087};
1088
1089static char *imc_away_alias_find( GList *gcm, char *away )
1090{
1091        GList *m;
1092        int i, j;
1093       
1094        for( i = 0; *imc_away_alias_list[i]; i ++ )
1095        {
1096                for( j = 0; imc_away_alias_list[i][j]; j ++ )
1097                        if( g_strncasecmp( away, imc_away_alias_list[i][j], strlen( imc_away_alias_list[i][j] ) ) == 0 )
1098                                break;
1099               
1100                if( !imc_away_alias_list[i][j] )        /* If we reach the end, this row */
1101                        continue;                       /* is not what we want. Next!    */
1102               
1103                /* Now find an entry in this row which exists in gcm */
1104                for( j = 0; imc_away_alias_list[i][j]; j ++ )
1105                {
1106                        m = gcm;
1107                        while( m )
1108                        {
1109                                if( g_strcasecmp( imc_away_alias_list[i][j], m->data ) == 0 )
1110                                        return( imc_away_alias_list[i][j] );
1111                                m = m->next;
1112                        }
1113                }
1114        }
1115       
1116        return( NULL );
1117}
1118
1119void imc_add_allow( struct im_connection *ic, char *handle )
1120{
1121        if( g_slist_find_custom( ic->permit, handle, (GCompareFunc) ic->acc->prpl->handle_cmp ) == NULL )
1122        {
1123                ic->permit = g_slist_prepend( ic->permit, g_strdup( handle ) );
1124        }
1125       
1126        ic->acc->prpl->add_permit( ic, handle );
1127}
1128
1129void imc_rem_allow( struct im_connection *ic, char *handle )
1130{
1131        GSList *l;
1132       
1133        if( ( l = g_slist_find_custom( ic->permit, handle, (GCompareFunc) ic->acc->prpl->handle_cmp ) ) )
1134        {
1135                g_free( l->data );
1136                ic->permit = g_slist_delete_link( ic->permit, l );
1137        }
1138       
1139        ic->acc->prpl->rem_permit( ic, handle );
1140}
1141
1142void imc_add_block( struct im_connection *ic, char *handle )
1143{
1144        if( g_slist_find_custom( ic->deny, handle, (GCompareFunc) ic->acc->prpl->handle_cmp ) == NULL )
1145        {
1146                ic->deny = g_slist_prepend( ic->deny, g_strdup( handle ) );
1147        }
1148       
1149        ic->acc->prpl->add_deny( ic, handle );
1150}
1151
1152void imc_rem_block( struct im_connection *ic, char *handle )
1153{
1154        GSList *l;
1155       
1156        if( ( l = g_slist_find_custom( ic->deny, handle, (GCompareFunc) ic->acc->prpl->handle_cmp ) ) )
1157        {
1158                g_free( l->data );
1159                ic->deny = g_slist_delete_link( ic->deny, l );
1160        }
1161       
1162        ic->acc->prpl->rem_deny( ic, handle );
1163}
1164
1165void imcb_clean_handle( struct im_connection *ic, char *handle )
1166{
1167        /* Accepts a handle and does whatever is necessary to make it
1168           BitlBee-friendly. Currently this means removing everything
1169           outside 33-127 (ASCII printable excl spaces), @ (only one
1170           is allowed) and ! and : */
1171        char out[strlen(handle)+1];
1172        int s, d;
1173       
1174        s = d = 0;
1175        while( handle[s] )
1176        {
1177                if( handle[s] > ' ' && handle[s] != '!' && handle[s] != ':' &&
1178                    ( handle[s] & 0x80 ) == 0 )
1179                {
1180                        if( handle[s] == '@' )
1181                        {
1182                                /* See if we got an @ already? */
1183                                out[d] = 0;
1184                                if( strchr( out, '@' ) )
1185                                        continue;
1186                        }
1187                       
1188                        out[d++] = handle[s];
1189                }
1190                s ++;
1191        }
1192        out[d] = handle[s];
1193       
1194        strcpy( handle, out );
1195}
Note: See TracBrowser for help on using the repository browser.