source: protocols/nogaim.c @ 05a8932

Last change on this file since 05a8932 was ec86b22, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-15T23:28:16Z

Mainline merge. (Probably mostly irrelevant for this branch, oh well.)

  • Property mode set to 100644
File size: 34.6 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       
[839189b]667        if( set_getbool( &ic->irc->set, "show_offline" ) && !u->online )
668        {
669                /* always set users as online */
670                irc_spawn( ic->irc, u );
671                u->online = 1;
672                if( !( flags & OPT_LOGGED_IN ) )
673                {
674                        /* set away message if user isn't really online */
675                        u->away = g_strdup( "User is offline" );
676                }
677        }
678        else if( ( flags & OPT_LOGGED_IN ) && !u->online )
[b7d3cc34]679        {
[0da65d5]680                irc_spawn( ic->irc, u );
[b7d3cc34]681                u->online = 1;
682        }
[6bbb939]683        else if( !( flags & OPT_LOGGED_IN ) && u->online )
[b7d3cc34]684        {
[0da65d5]685                struct groupchat *c;
[b7d3cc34]686               
[839189b]687                if( set_getbool( &ic->irc->set, "show_offline" ) )
688                {
689                        /* keep offline users in channel and set away message to "offline" */
690                        u->away = g_strdup( "User is offline" );
691
692                        /* Keep showing him/her in the control channel but not in groupchats. */
693                        for( c = ic->groupchats; c; c = c->next )
694                        {
695                                if( remove_chat_buddy_silent( c, handle ) && c->joined )
696                                        irc_part( c->ic->irc, u, c->channel );
697                        }
698                }
699                else
700                {
701                        /* kill offline users */
702                        irc_kill( ic->irc, u );
703                        u->online = 0;
704
705                        /* Remove him/her from the groupchats to prevent PART messages after he/she QUIT already */
706                        for( c = ic->groupchats; c; c = c->next )
707                                remove_chat_buddy_silent( c, handle );
708                }
[b7d3cc34]709        }
[839189b]710
[6bbb939]711        if( flags & OPT_AWAY )
[b7d3cc34]712        {
[6bbb939]713                if( state && message )
714                {
715                        u->away = g_strdup_printf( "%s (%s)", state, message );
716                }
717                else if( state )
718                {
719                        u->away = g_strdup( state );
720                }
721                else if( message )
722                {
723                        u->away = g_strdup( message );
724                }
725                else
726                {
727                        u->away = g_strdup( "Away" );
728                }
[b7d3cc34]729        }
[449a51d]730        else
731        {
732                u->status_msg = g_strdup( message );
733        }
[b7d3cc34]734       
[839189b]735        /* early if-clause for show_offline even if there is some redundant code here because this isn't LISP but C ;) */
736        if( set_getbool( &ic->irc->set, "show_offline" ) && set_getbool( &ic->irc->set, "away_devoice" ) )
[b7d3cc34]737        {
[1186382]738                char *from;
739               
740                if( set_getbool( &ic->irc->set, "simulate_netsplit" ) )
741                {
742                        from = g_strdup( ic->irc->myhost );
743                }
744                else
745                {
746                        from = g_strdup_printf( "%s!%s@%s", ic->irc->mynick, ic->irc->mynick,
747                                                            ic->irc->myhost );
748                }
[839189b]749
750                /* if we use show_offline, we op online users, voice away users, and devoice/deop offline users */
751                if( flags & OPT_LOGGED_IN )
752                {
753                        /* user is "online" (either really online or away) */
754                        irc_write( ic->irc, ":%s MODE %s %cv%co %s %s", from, ic->irc->channel,
755                                                                  u->away?'+':'-', u->away?'-':'+', u->nick, u->nick );
756                }
757                else
758                {
759                        /* user is offline */
760                        irc_write( ic->irc, ":%s MODE %s -vo %s %s", from, ic->irc->channel, u->nick, u->nick );
761                }
762        }
763        else
764        { 
765                /* LISPy... */
766                if( ( set_getbool( &ic->irc->set, "away_devoice" ) ) &&         /* Don't do a thing when user doesn't want it */
767                    ( u->online ) &&                                            /* Don't touch offline people */
768                    ( ( ( u->online != oo ) && !u->away ) ||                    /* Voice joining people */
769                      ( ( u->online == oo ) && ( oa == !u->away ) ) ) )         /* (De)voice people changing state */
770                {
771                        char *from;
772
773                        if( set_getbool( &ic->irc->set, "simulate_netsplit" ) )
774                        {
775                                from = g_strdup( ic->irc->myhost );
776                        }
777                        else
778                        {
779                                from = g_strdup_printf( "%s!%s@%s", ic->irc->mynick, ic->irc->mynick,
780                                                                    ic->irc->myhost );
781                        }
782                        irc_write( ic->irc, ":%s MODE %s %cv %s", from, ic->irc->channel,
783                                                                  u->away?'-':'+', u->nick );
784                        g_free( from );
785                }
[b7d3cc34]786        }
787}
788
[c6ca3ee]789void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, uint32_t flags, time_t sent_at )
[b7d3cc34]790{
[0da65d5]791        irc_t *irc = ic->irc;
[5b9b2b6]792        char *wrapped, *ts = NULL;
[b7d3cc34]793        user_t *u;
794       
[0da65d5]795        u = user_findhandle( ic, handle );
[b7d3cc34]796       
797        if( !u )
798        {
[5c9512f]799                char *h = set_getstr( &irc->set, "handle_unknown" );
[b7d3cc34]800               
801                if( g_strcasecmp( h, "ignore" ) == 0 )
802                {
[d5ccd83]803                        if( set_getbool( &irc->set, "debug" ) )
[84b045d]804                                imcb_log( ic, "Ignoring message from unknown handle %s", handle );
[b7d3cc34]805                       
806                        return;
807                }
808                else if( g_strncasecmp( h, "add", 3 ) == 0 )
809                {
[d5ccd83]810                        int private = set_getbool( &irc->set, "private" );
[b7d3cc34]811                       
812                        if( h[3] )
813                        {
814                                if( g_strcasecmp( h + 3, "_private" ) == 0 )
815                                        private = 1;
816                                else if( g_strcasecmp( h + 3, "_channel" ) == 0 )
817                                        private = 0;
818                        }
819                       
[f0cb961]820                        imcb_add_buddy( ic, handle, NULL );
[0da65d5]821                        u = user_findhandle( ic, handle );
[b7d3cc34]822                        u->is_private = private;
823                }
824                else
825                {
[84b045d]826                        imcb_log( ic, "Message from unknown handle %s:", handle );
[b7d3cc34]827                        u = user_find( irc, irc->mynick );
828                }
829        }
830       
[0da65d5]831        if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) ||
[6bbb939]832            ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) )
[b7d3cc34]833                strip_html( msg );
[3e57660]834       
[5b9b2b6]835        if( set_getbool( &ic->irc->set, "display_timestamps" ) &&
836            ( ts = format_timestamp( irc, sent_at ) ) )
[3e57660]837        {
838                char *new = g_strconcat( ts, msg, NULL );
839                g_free( ts );
840                ts = msg = new;
841        }
842       
[d444c09]843        wrapped = word_wrap( msg, 425 );
844        irc_msgfrom( irc, u->nick, wrapped );
845        g_free( wrapped );
[3e57660]846        g_free( ts );
[b7d3cc34]847}
848
[52744f8]849void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags )
[b7d3cc34]850{
851        user_t *u;
852       
[0da65d5]853        if( !set_getbool( &ic->irc->set, "typing_notice" ) )
[b7d3cc34]854                return;
855       
[9624fdf]856        if( ( u = user_findhandle( ic, handle ) ) )
857        {
858                char buf[256]; 
859               
860                g_snprintf( buf, 256, "\1TYPING %d\1", ( flags >> 8 ) & 3 );
861                irc_privmsg( ic->irc, u, "PRIVMSG", ic->irc->nick, NULL, buf );
[e7f46c5]862        }
[b7d3cc34]863}
864
[94acdd0]865struct groupchat *imcb_chat_new( struct im_connection *ic, const char *handle )
[83ba3e5]866{
867        struct groupchat *c;
868       
869        /* This one just creates the conversation structure, user won't see anything yet */
870       
871        if( ic->groupchats )
872        {
873                for( c = ic->groupchats; c->next; c = c->next );
874                c = c->next = g_new0( struct groupchat, 1 );
875        }
876        else
877                ic->groupchats = c = g_new0( struct groupchat, 1 );
878       
879        c->ic = ic;
880        c->title = g_strdup( handle );
881        c->channel = g_strdup_printf( "&chat_%03d", ic->irc->c_id++ );
882        c->topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title );
883       
884        if( set_getbool( &ic->irc->set, "debug" ) )
885                imcb_log( ic, "Creating new conversation: (id=%p,handle=%s)", c, handle );
886       
887        return c;
888}
889
[cca0692]890void imcb_chat_name_hint( struct groupchat *c, const char *name )
891{
892        if( !c->joined )
893        {
894                struct im_connection *ic = c->ic;
895                char stripped[MAX_NICK_LENGTH+1], *full_name;
896               
897                strncpy( stripped, name, MAX_NICK_LENGTH );
898                stripped[MAX_NICK_LENGTH] = '\0';
899                nick_strip( stripped );
900                if( set_getbool( &ic->irc->set, "lcnicks" ) )
901                        nick_lc( stripped );
902               
903                full_name = g_strdup_printf( "&%s", stripped );
904               
905                if( stripped[0] &&
906                    nick_cmp( stripped, ic->irc->channel + 1 ) != 0 &&
907                    irc_chat_by_channel( ic->irc, full_name ) == NULL )
908                {
909                        g_free( c->channel );
910                        c->channel = full_name;
911                }
912                else
913                {
914                        g_free( full_name );
915                }
916        }
917}
918
[e35d1a1]919void imcb_chat_free( struct groupchat *c )
[b7d3cc34]920{
[0da65d5]921        struct im_connection *ic = c->ic;
[e35d1a1]922        struct groupchat *l;
[b7d3cc34]923        GList *ir;
924       
[0da65d5]925        if( set_getbool( &ic->irc->set, "debug" ) )
[56f260a]926                imcb_log( ic, "You were removed from conversation %p", c );
[b7d3cc34]927       
928        if( c )
929        {
930                if( c->joined )
931                {
932                        user_t *u, *r;
933                       
[0da65d5]934                        r = user_find( ic->irc, ic->irc->mynick );
935                        irc_privmsg( ic->irc, r, "PRIVMSG", c->channel, "", "Cleaning up channel, bye!" );
[b7d3cc34]936                       
[0da65d5]937                        u = user_find( ic->irc, ic->irc->nick );
938                        irc_kick( ic->irc, u, c->channel, r );
939                        /* irc_part( ic->irc, u, c->channel ); */
[b7d3cc34]940                }
941               
[e35d1a1]942                /* Find the previous chat in the linked list. */
943                for( l = ic->groupchats; l && l->next != c; l = l->next );
944               
[b7d3cc34]945                if( l )
946                        l->next = c->next;
947                else
[e35d1a1]948                        ic->groupchats = c->next;
[b7d3cc34]949               
950                for( ir = c->in_room; ir; ir = ir->next )
951                        g_free( ir->data );
952                g_list_free( c->in_room );
953                g_free( c->channel );
954                g_free( c->title );
[83ba3e5]955                g_free( c->topic );
[b7d3cc34]956                g_free( c );
957        }
958}
959
[c6ca3ee]960void imcb_chat_msg( struct groupchat *c, const char *who, char *msg, uint32_t flags, time_t sent_at )
[b7d3cc34]961{
[0da65d5]962        struct im_connection *ic = c->ic;
[d444c09]963        char *wrapped;
[b7d3cc34]964        user_t *u;
965       
966        /* Gaim sends own messages through this too. IRC doesn't want this, so kill them */
[c2fb3809]967        if( g_strcasecmp( who, ic->acc->user ) == 0 )
[b7d3cc34]968                return;
969       
[0da65d5]970        u = user_findhandle( ic, who );
[b7d3cc34]971       
[0da65d5]972        if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) ||
[6bbb939]973            ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) )
[b7d3cc34]974                strip_html( msg );
975       
[d444c09]976        wrapped = word_wrap( msg, 425 );
[b7d3cc34]977        if( c && u )
[d444c09]978        {
[5b9b2b6]979                char *ts = NULL;
980                if( set_getbool( &ic->irc->set, "display_timestamps" ) )
981                        ts = format_timestamp( ic->irc, sent_at );
[3e57660]982                irc_privmsg( ic->irc, u, "PRIVMSG", c->channel, ts ? : "", wrapped );
983                g_free( ts );
[d444c09]984        }
[b7d3cc34]985        else
[d444c09]986        {
[56f260a]987                imcb_log( ic, "Message from/to conversation %s@%p (unknown conv/user): %s", who, c, wrapped );
[d444c09]988        }
989        g_free( wrapped );
[b7d3cc34]990}
991
[31e5846]992void imcb_chat_log( struct groupchat *c, char *format, ... )
993{
994        irc_t *irc = c->ic->irc;
995        va_list params;
996        char *text;
997        user_t *u;
998       
999        va_start( params, format );
1000        text = g_strdup_vprintf( format, params );
1001        va_end( params );
1002       
1003        u = user_find( irc, irc->mynick );
1004       
1005        irc_privmsg( irc, u, "PRIVMSG", c->channel, "System message: ", text );
1006       
1007        g_free( text );
1008}
1009
[ef5c185]1010void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at )
[50e1776]1011{
1012        struct im_connection *ic = c->ic;
1013        user_t *u = NULL;
1014       
1015        if( who == NULL)
[ef5c185]1016                u = user_find( ic->irc, ic->irc->mynick );
[50e1776]1017        else if( g_strcasecmp( who, ic->acc->user ) == 0 )
[ef5c185]1018                u = user_find( ic->irc, ic->irc->nick );
[50e1776]1019        else
1020                u = user_findhandle( ic, who );
1021       
1022        if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) ||
1023            ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) )
1024                strip_html( topic );
1025       
1026        g_free( c->topic );
1027        c->topic = g_strdup( topic );
1028       
1029        if( c->joined && u )
1030                irc_write( ic->irc, ":%s!%s@%s TOPIC %s :%s", u->nick, u->user, u->host, c->channel, topic );
1031}
1032
[b7d3cc34]1033
1034/* buddy_chat.c */
1035
[c6ca3ee]1036void imcb_chat_add_buddy( struct groupchat *b, const char *handle )
[b7d3cc34]1037{
[0da65d5]1038        user_t *u = user_findhandle( b->ic, handle );
[b7d3cc34]1039        int me = 0;
1040       
[0da65d5]1041        if( set_getbool( &b->ic->irc->set, "debug" ) )
[56f260a]1042                imcb_log( b->ic, "User %s added to conversation %p", handle, b );
[b7d3cc34]1043       
1044        /* It might be yourself! */
[c2fb3809]1045        if( b->ic->acc->prpl->handle_cmp( handle, b->ic->acc->user ) == 0 )
[b7d3cc34]1046        {
[0da65d5]1047                u = user_find( b->ic->irc, b->ic->irc->nick );
[b7d3cc34]1048                if( !b->joined )
[0da65d5]1049                        irc_join( b->ic->irc, u, b->channel );
[b7d3cc34]1050                b->joined = me = 1;
1051        }
1052       
1053        /* Most protocols allow people to join, even when they're not in
1054           your contact list. Try to handle that here */
1055        if( !u )
1056        {
[f0cb961]1057                imcb_add_buddy( b->ic, handle, NULL );
[0da65d5]1058                u = user_findhandle( b->ic, handle );
[b7d3cc34]1059        }
1060       
1061        /* Add the handle to the room userlist, if it's not 'me' */
1062        if( !me )
1063        {
1064                if( b->joined )
[0da65d5]1065                        irc_join( b->ic->irc, u, b->channel );
[b7d3cc34]1066                b->in_room = g_list_append( b->in_room, g_strdup( handle ) );
1067        }
1068}
1069
[2d317bb]1070/* This function is one BIG hack... :-( EREWRITE */
[c6ca3ee]1071void imcb_chat_remove_buddy( struct groupchat *b, const char *handle, const char *reason )
[b7d3cc34]1072{
1073        user_t *u;
1074        int me = 0;
1075       
[0da65d5]1076        if( set_getbool( &b->ic->irc->set, "debug" ) )
[56f260a]1077                imcb_log( b->ic, "User %s removed from conversation %p (%s)", handle, b, reason ? reason : "" );
[b7d3cc34]1078       
1079        /* It might be yourself! */
[c2fb3809]1080        if( g_strcasecmp( handle, b->ic->acc->user ) == 0 )
[b7d3cc34]1081        {
[2d317bb]1082                if( b->joined == 0 )
1083                        return;
1084               
[0da65d5]1085                u = user_find( b->ic->irc, b->ic->irc->nick );
[b7d3cc34]1086                b->joined = 0;
1087                me = 1;
1088        }
1089        else
1090        {
[0da65d5]1091                u = user_findhandle( b->ic, handle );
[b7d3cc34]1092        }
1093       
[2d317bb]1094        if( me || ( remove_chat_buddy_silent( b, handle ) && b->joined && u ) )
1095                irc_part( b->ic->irc, u, b->channel );
[b7d3cc34]1096}
1097
[764b163d]1098static int remove_chat_buddy_silent( struct groupchat *b, const char *handle )
[b7d3cc34]1099{
1100        GList *i;
1101       
1102        /* Find the handle in the room userlist and shoot it */
1103        i = b->in_room;
1104        while( i )
1105        {
1106                if( g_strcasecmp( handle, i->data ) == 0 )
1107                {
1108                        g_free( i->data );
1109                        b->in_room = g_list_remove( b->in_room, i->data );
1110                        return( 1 );
1111                }
1112               
1113                i = i->next;
1114        }
1115       
1116        return( 0 );
1117}
1118
1119
1120/* Misc. BitlBee stuff which shouldn't really be here */
1121
[5c9512f]1122char *set_eval_away_devoice( set_t *set, char *value )
[b7d3cc34]1123{
[5c9512f]1124        irc_t *irc = set->data;
[b7d3cc34]1125        int st;
1126       
[7125cb3]1127        if( !is_bool( value ) )
1128                return SET_INVALID;
[b7d3cc34]1129       
[7125cb3]1130        st = bool2int( value );
[b7d3cc34]1131       
1132        /* Horror.... */
1133       
[d5ccd83]1134        if( st != set_getbool( &irc->set, "away_devoice" ) )
[b7d3cc34]1135        {
1136                char list[80] = "";
1137                user_t *u = irc->users;
1138                int i = 0, count = 0;
1139                char pm;
1140                char v[80];
1141               
1142                if( st )
1143                        pm = '+';
1144                else
1145                        pm = '-';
1146               
1147                while( u )
1148                {
[0da65d5]1149                        if( u->ic && u->online && !u->away )
[b7d3cc34]1150                        {
1151                                if( ( strlen( list ) + strlen( u->nick ) ) >= 79 )
1152                                {
1153                                        for( i = 0; i < count; v[i++] = 'v' ); v[i] = 0;
[2087159]1154                                        irc_write( irc, ":%s MODE %s %c%s%s",
1155                                                   irc->myhost,
[b7d3cc34]1156                                                   irc->channel, pm, v, list );
1157                                       
1158                                        *list = 0;
1159                                        count = 0;
1160                                }
1161                               
1162                                sprintf( list + strlen( list ), " %s", u->nick );
1163                                count ++;
1164                        }
1165                        u = u->next;
1166                }
1167               
1168                /* $v = 'v' x $i */
1169                for( i = 0; i < count; v[i++] = 'v' ); v[i] = 0;
[2087159]1170                irc_write( irc, ":%s MODE %s %c%s%s", irc->myhost,
[b7d3cc34]1171                                                            irc->channel, pm, v, list );
1172        }
1173       
[7125cb3]1174        return value;
[b7d3cc34]1175}
1176
[3e57660]1177char *set_eval_timezone( set_t *set, char *value )
1178{
1179        char *s;
1180       
1181        if( strcmp( value, "local" ) == 0 ||
1182            strcmp( value, "gmt" ) == 0 || strcmp( value, "utc" ) == 0 )
1183                return value;
1184       
1185        /* Otherwise: +/- at the beginning optional, then one or more numbers,
1186           possibly followed by a colon and more numbers. Don't bother bound-
1187           checking them since users are free to shoot themselves in the foot. */
1188        s = value;
1189        if( *s == '+' || *s == '-' )
1190                s ++;
1191       
1192        /* \d+ */
1193        if( !isdigit( *s ) )
1194                return SET_INVALID;
1195        while( *s && isdigit( *s ) ) s ++;
1196       
1197        /* EOS? */
1198        if( *s == '\0' )
1199                return value;
1200       
1201        /* Otherwise, colon */
1202        if( *s != ':' )
1203                return SET_INVALID;
1204        s ++;
1205       
1206        /* \d+ */
1207        if( !isdigit( *s ) )
1208                return SET_INVALID;
1209        while( *s && isdigit( *s ) ) s ++;
1210       
1211        /* EOS */
1212        return *s == '\0' ? value : SET_INVALID;
1213}
[226fce1]1214
[3e57660]1215static char *format_timestamp( irc_t *irc, time_t msg_ts )
1216{
1217        time_t now_ts = time( NULL );
1218        struct tm now, msg;
1219        char *set;
1220       
1221        /* If the timestamp is <= 0 or less than a minute ago, discard it as
1222           it doesn't seem to add to much useful info and/or might be noise. */
1223        if( msg_ts <= 0 || msg_ts > now_ts - 60 )
1224                return NULL;
1225       
1226        set = set_getstr( &irc->set, "timezone" );
1227        if( strcmp( set, "local" ) == 0 )
1228        {
1229                localtime_r( &now_ts, &now );
1230                localtime_r( &msg_ts, &msg );
1231        }
1232        else
1233        {
1234                int hr, min = 0, sign = 60;
1235               
1236                if( set[0] == '-' )
1237                {
1238                        sign *= -1;
1239                        set ++;
1240                }
1241                else if( set[0] == '+' )
1242                {
1243                        set ++;
1244                }
1245               
1246                if( sscanf( set, "%d:%d", &hr, &min ) >= 1 )
1247                {
1248                        msg_ts += sign * ( hr * 60 + min );
1249                        now_ts += sign * ( hr * 60 + min );
1250                }
1251               
1252                gmtime_r( &now_ts, &now );
1253                gmtime_r( &msg_ts, &msg );
1254        }
1255       
1256        if( msg.tm_year == now.tm_year && msg.tm_yday == now.tm_yday )
1257                return g_strdup_printf( "\x02[\x02\x02\x02%02d:%02d:%02d\x02]\x02 ",
1258                                        msg.tm_hour, msg.tm_min, msg.tm_sec );
1259        else
1260                return g_strdup_printf( "\x02[\x02\x02\x02%04d-%02d-%02d "
1261                                        "%02d:%02d:%02d\x02]\x02 ",
[4273158]1262                                        msg.tm_year + 1900, msg.tm_mon + 1, msg.tm_mday,
[3e57660]1263                                        msg.tm_hour, msg.tm_min, msg.tm_sec );
1264}
[226fce1]1265
1266/* The plan is to not allow straight calls to prpl functions anymore, but do
1267   them all from some wrappers. We'll start to define some down here: */
1268
[84b045d]1269int imc_buddy_msg( struct im_connection *ic, char *handle, char *msg, int flags )
[b7d3cc34]1270{
[e27661d]1271        char *buf = NULL;
1272        int st;
[b7d3cc34]1273       
[6bbb939]1274        if( ( ic->flags & OPT_DOES_HTML ) && ( g_strncasecmp( msg, "<html>", 6 ) != 0 ) )
[c572dd6]1275        {
[e27661d]1276                buf = escape_html( msg );
[c572dd6]1277                msg = buf;
[b7d3cc34]1278        }
1279       
[f6c963b]1280        st = ic->acc->prpl->buddy_msg( ic, handle, msg, flags );
[e27661d]1281        g_free( buf );
1282       
1283        return st;
[b7d3cc34]1284}
1285
[84b045d]1286int imc_chat_msg( struct groupchat *c, char *msg, int flags )
[b7d3cc34]1287{
[e27661d]1288        char *buf = NULL;
[b7d3cc34]1289       
[6bbb939]1290        if( ( c->ic->flags & OPT_DOES_HTML ) && ( g_strncasecmp( msg, "<html>", 6 ) != 0 ) )
[e27661d]1291        {
1292                buf = escape_html( msg );
[b7d3cc34]1293                msg = buf;
1294        }
1295       
[f6c963b]1296        c->ic->acc->prpl->chat_msg( c, msg, flags );
[e27661d]1297        g_free( buf );
1298       
[0da65d5]1299        return 1;
[b7d3cc34]1300}
[226fce1]1301
[34fbbf9]1302static char *imc_away_state_find( GList *gcm, char *away, char **message );
[226fce1]1303
[58adb7e]1304int imc_away_send_update( struct im_connection *ic )
[226fce1]1305{
[3e1ef92c]1306        char *away, *msg = NULL;
[226fce1]1307       
[91cec2f]1308        if( ic->acc->prpl->away_states == NULL ||
1309            ic->acc->prpl->set_away == NULL )
1310                return 0;
1311       
[58adb7e]1312        away = set_getstr( &ic->acc->set, "away" ) ?
1313             : set_getstr( &ic->irc->set, "away" );
[34fbbf9]1314        if( away && *away )
[226fce1]1315        {
[34fbbf9]1316                GList *m = ic->acc->prpl->away_states( ic );
[58adb7e]1317                msg = ic->acc->flags & ACC_FLAG_AWAY_MESSAGE ? away : NULL;
1318                away = imc_away_state_find( m, away, &msg ) ? : m->data;
[226fce1]1319        }
[58adb7e]1320        else if( ic->acc->flags & ACC_FLAG_STATUS_MESSAGE )
[226fce1]1321        {
[58adb7e]1322                away = NULL;
1323                msg = set_getstr( &ic->acc->set, "status" ) ?
1324                    : set_getstr( &ic->irc->set, "status" );
[226fce1]1325        }
1326       
[58adb7e]1327        ic->acc->prpl->set_away( ic, away, msg );
[226fce1]1328       
[34fbbf9]1329        return 1;
[226fce1]1330}
1331
[84b045d]1332static char *imc_away_alias_list[8][5] =
[226fce1]1333{
1334        { "Away from computer", "Away", "Extended away", NULL },
1335        { "NA", "N/A", "Not available", NULL },
1336        { "Busy", "Do not disturb", "DND", "Occupied", NULL },
1337        { "Be right back", "BRB", NULL },
1338        { "On the phone", "Phone", "On phone", NULL },
1339        { "Out to lunch", "Lunch", "Food", NULL },
1340        { "Invisible", "Hidden" },
1341        { NULL }
1342};
1343
[34fbbf9]1344static char *imc_away_state_find( GList *gcm, char *away, char **message )
[226fce1]1345{
1346        GList *m;
1347        int i, j;
1348       
[34fbbf9]1349        for( m = gcm; m; m = m->next )
1350                if( g_strncasecmp( m->data, away, strlen( m->data ) ) == 0 )
1351                {
1352                        /* At least the Yahoo! module works better if message
1353                           contains no data unless it adds something to what
1354                           we have in state already. */
1355                        if( strlen( m->data ) == strlen( away ) )
1356                                *message = NULL;
1357                       
1358                        return m->data;
1359                }
1360       
[84b045d]1361        for( i = 0; *imc_away_alias_list[i]; i ++ )
[226fce1]1362        {
[34fbbf9]1363                int keep_message;
1364               
[84b045d]1365                for( j = 0; imc_away_alias_list[i][j]; j ++ )
1366                        if( g_strncasecmp( away, imc_away_alias_list[i][j], strlen( imc_away_alias_list[i][j] ) ) == 0 )
[34fbbf9]1367                        {
1368                                keep_message = strlen( away ) != strlen( imc_away_alias_list[i][j] );
[226fce1]1369                                break;
[34fbbf9]1370                        }
[226fce1]1371               
[84b045d]1372                if( !imc_away_alias_list[i][j] )        /* If we reach the end, this row */
[226fce1]1373                        continue;                       /* is not what we want. Next!    */
1374               
1375                /* Now find an entry in this row which exists in gcm */
[84b045d]1376                for( j = 0; imc_away_alias_list[i][j]; j ++ )
[226fce1]1377                {
[34fbbf9]1378                        for( m = gcm; m; m = m->next )
[84b045d]1379                                if( g_strcasecmp( imc_away_alias_list[i][j], m->data ) == 0 )
[34fbbf9]1380                                {
1381                                        if( !keep_message )
1382                                                *message = NULL;
1383                                       
1384                                        return imc_away_alias_list[i][j];
1385                                }
[226fce1]1386                }
[34fbbf9]1387               
1388                /* No need to look further, apparently this state doesn't
1389                   have any good alias for this protocol. */
1390                break;
[226fce1]1391        }
1392       
[34fbbf9]1393        return NULL;
[226fce1]1394}
[da3b536]1395
[84b045d]1396void imc_add_allow( struct im_connection *ic, char *handle )
[da3b536]1397{
[0da65d5]1398        if( g_slist_find_custom( ic->permit, handle, (GCompareFunc) ic->acc->prpl->handle_cmp ) == NULL )
[da3b536]1399        {
[0da65d5]1400                ic->permit = g_slist_prepend( ic->permit, g_strdup( handle ) );
[da3b536]1401        }
1402       
[0da65d5]1403        ic->acc->prpl->add_permit( ic, handle );
[da3b536]1404}
1405
[84b045d]1406void imc_rem_allow( struct im_connection *ic, char *handle )
[da3b536]1407{
1408        GSList *l;
1409       
[0da65d5]1410        if( ( l = g_slist_find_custom( ic->permit, handle, (GCompareFunc) ic->acc->prpl->handle_cmp ) ) )
[da3b536]1411        {
1412                g_free( l->data );
[0da65d5]1413                ic->permit = g_slist_delete_link( ic->permit, l );
[da3b536]1414        }
1415       
[0da65d5]1416        ic->acc->prpl->rem_permit( ic, handle );
[da3b536]1417}
1418
[84b045d]1419void imc_add_block( struct im_connection *ic, char *handle )
[da3b536]1420{
[0da65d5]1421        if( g_slist_find_custom( ic->deny, handle, (GCompareFunc) ic->acc->prpl->handle_cmp ) == NULL )
[da3b536]1422        {
[0da65d5]1423                ic->deny = g_slist_prepend( ic->deny, g_strdup( handle ) );
[da3b536]1424        }
1425       
[0da65d5]1426        ic->acc->prpl->add_deny( ic, handle );
[da3b536]1427}
1428
[84b045d]1429void imc_rem_block( struct im_connection *ic, char *handle )
[da3b536]1430{
1431        GSList *l;
1432       
[0da65d5]1433        if( ( l = g_slist_find_custom( ic->deny, handle, (GCompareFunc) ic->acc->prpl->handle_cmp ) ) )
[da3b536]1434        {
1435                g_free( l->data );
[0da65d5]1436                ic->deny = g_slist_delete_link( ic->deny, l );
[da3b536]1437        }
1438       
[0da65d5]1439        ic->acc->prpl->rem_deny( ic, handle );
[da3b536]1440}
[85023c6]1441
1442void imcb_clean_handle( struct im_connection *ic, char *handle )
1443{
1444        /* Accepts a handle and does whatever is necessary to make it
1445           BitlBee-friendly. Currently this means removing everything
1446           outside 33-127 (ASCII printable excl spaces), @ (only one
1447           is allowed) and ! and : */
1448        char out[strlen(handle)+1];
1449        int s, d;
1450       
1451        s = d = 0;
1452        while( handle[s] )
1453        {
1454                if( handle[s] > ' ' && handle[s] != '!' && handle[s] != ':' &&
1455                    ( handle[s] & 0x80 ) == 0 )
1456                {
1457                        if( handle[s] == '@' )
1458                        {
1459                                /* See if we got an @ already? */
1460                                out[d] = 0;
1461                                if( strchr( out, '@' ) )
1462                                        continue;
1463                        }
1464                       
1465                        out[d++] = handle[s];
1466                }
1467                s ++;
1468        }
1469        out[d] = handle[s];
1470       
1471        strcpy( handle, out );
1472}
Note: See TracBrowser for help on using the repository browser.