source: protocols/nogaim.c @ 4e04194

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

Merging BitlBee 1.2.4+

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