source: protocols/nogaim.c @ d8acfd3

Last change on this file since d8acfd3 was ae3dc99, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-04-24T17:02:07Z

Merging stuff from mainline (1.2.6).

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