source: protocols/nogaim.c @ b6dd429

Last change on this file since b6dd429 was 7125cb3, checked in by Wilmer van der Gaast <wilmer@…>, at 2008-08-24T18:01:05Z

Added SET_INVALID, which set evaluators should now return instead of NULL
when the given value is not accepted. This to allow certain variables
actually be set to NULL (server, for example). This should fully close
#444.

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