source: irc.c @ ad2d8bc

Last change on this file since ad2d8bc was a758ec1, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-21T19:48:41Z

Although the backward compatibility stuff for show_offline and away_devoice
was only meant to ease migration, people are now complaining that
"set show_offline off" doesn't work. Make this work, but at the same time
start hiding these two options to discourage people from using them.

  • Property mode set to 100644
File size: 24.7 KB
RevLine 
[b7d3cc34]1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2004 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
[3ddb7477]7/* The IRC-based UI (for now the only one)                              */
[b7d3cc34]8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
23  Suite 330, Boston, MA  02111-1307  USA
24*/
25
26#include "bitlbee.h"
[fb117aee]27#include "ipc.h"
[2ff2076]28#include "dcc.h"
[b7d3cc34]29
[3ddb7477]30GSList *irc_connection_list;
[b7d3cc34]31
[3923003]32static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond );
[3ddb7477]33static char *set_eval_charset( set_t *set, char *value );
[0d9d53e]34static char *set_eval_password( set_t *set, char *value );
[6d8cc05]35static char *set_eval_bw_compat( set_t *set, char *value );
[58adb7e]36
[b7d3cc34]37irc_t *irc_new( int fd )
38{
[e4d6271]39        irc_t *irc;
[e9b755e]40        struct sockaddr_storage sock;
[7435ccf]41        socklen_t socklen = sizeof( sock );
[3ddb7477]42        char *host = NULL, *myhost = NULL;
43        irc_user_t *iu;
[7125cb3]44        set_t *s;
[3ddb7477]45        bee_t *b;
[e4d6271]46       
47        irc = g_new0( irc_t, 1 );
[b7d3cc34]48       
49        irc->fd = fd;
[a0d04d6]50        sock_make_nonblocking( irc->fd );
51       
[e046390]52        irc->r_watch_source_id = b_input_add( irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc );
[b7d3cc34]53       
54        irc->status = USTATUS_OFFLINE;
55        irc->last_pong = gettime();
56       
[3ddb7477]57        irc->nick_user_hash = g_hash_table_new( g_str_hash, g_str_equal );
[b7d3cc34]58        irc->watches = g_hash_table_new( g_str_hash, g_str_equal );
59       
[f9756bd]60        irc->iconv = (GIConv) -1;
61        irc->oconv = (GIConv) -1;
62       
[b7d3cc34]63        if( global.conf->hostname )
64        {
[3ddb7477]65                myhost = g_strdup( global.conf->hostname );
[b7d3cc34]66        }
[7435ccf]67        else if( getsockname( irc->fd, (struct sockaddr*) &sock, &socklen ) == 0 ) 
[b7d3cc34]68        {
[2231302]69                char buf[NI_MAXHOST+1];
[e9b755e]70
[2231302]71                if( getnameinfo( (struct sockaddr *) &sock, socklen, buf,
[c84e31a]72                                 NI_MAXHOST, NULL, 0, 0 ) == 0 )
[2231302]73                {
[3ddb7477]74                        myhost = g_strdup( ipv6_unwrap( buf ) );
[2231302]75                }
[b7d3cc34]76        }
77       
[7435ccf]78        if( getpeername( irc->fd, (struct sockaddr*) &sock, &socklen ) == 0 )
[b7d3cc34]79        {
[2231302]80                char buf[NI_MAXHOST+1];
[e9b755e]81
[2231302]82                if( getnameinfo( (struct sockaddr *)&sock, socklen, buf,
[c84e31a]83                                 NI_MAXHOST, NULL, 0, 0 ) == 0 )
[2231302]84                {
[3ddb7477]85                        host = g_strdup( ipv6_unwrap( buf ) );
[2231302]86                }
[b7d3cc34]87        }
88       
[3ddb7477]89        if( host == NULL )
90                host = g_strdup( "localhost.localdomain" );
91        if( myhost == NULL )
92                myhost = g_strdup( "localhost.localdomain" );
[3e1e11af]93       
[3923003]94        if( global.conf->ping_interval > 0 && global.conf->ping_timeout > 0 )
95                irc->ping_source_id = b_timeout_add( global.conf->ping_interval * 1000, irc_userping, irc );
[b7d3cc34]96
97        irc_connection_list = g_slist_append( irc_connection_list, irc );
98       
[3ddb7477]99        b = irc->b = bee_new();
[d860a8d]100        b->ui_data = irc;
101        b->ui = &irc_ui_funcs;
[3ddb7477]102       
[af9f2ca]103        s = set_add( &b->set, "allow_takeover", "true", set_eval_bool, irc );
[6d8cc05]104        s = set_add( &b->set, "away_devoice", "true", set_eval_bw_compat, irc );
[a758ec1]105        s->flags |= SET_HIDDEN;
[1c8e5f7]106        s = set_add( &b->set, "away_reply_timeout", "3600", set_eval_int, irc );
[3ddb7477]107        s = set_add( &b->set, "charset", "utf-8", set_eval_charset, irc );
108        s = set_add( &b->set, "default_target", "root", NULL, irc );
109        s = set_add( &b->set, "display_namechanges", "false", set_eval_bool, irc );
[21c87a7]110        s = set_add( &b->set, "display_timestamps", "true", set_eval_bool, irc );
[16834a5]111        s = set_add( &b->set, "handle_unknown", "add_channel", NULL, irc );
[180ab31]112        s = set_add( &b->set, "last_version", NULL, NULL, irc );
113        s->flags |= SET_HIDDEN;
[3ddb7477]114        s = set_add( &b->set, "lcnicks", "true", set_eval_bool, irc );
[09dfb68]115        s = set_add( &b->set, "nick_format", "%-@nick", NULL, irc );
[0bd948e]116        s = set_add( &b->set, "offline_user_quits", "true", set_eval_bool, irc );
[c5aefa4]117        s = set_add( &b->set, "ops", "both", set_eval_irc_channel_ops, irc );
[88eaf4b]118        s = set_add( &b->set, "paste_buffer", "false", set_eval_bool, irc );
119        s->old_key = g_strdup( "buddy_sendbuffer" );
120        s = set_add( &b->set, "paste_buffer_delay", "200", set_eval_int, irc );
121        s->old_key = g_strdup( "buddy_sendbuffer_delay" );
[0d9d53e]122        s = set_add( &b->set, "password", NULL, set_eval_password, irc );
123        s->flags |= SET_NULL_OK;
[3ddb7477]124        s = set_add( &b->set, "private", "true", set_eval_bool, irc );
125        s = set_add( &b->set, "query_order", "lifo", NULL, irc );
[0a6e5d1]126        s = set_add( &b->set, "root_nick", ROOT_NICK, set_eval_root_nick, irc );
[180ab31]127        s->flags |= SET_HIDDEN;
[6d8cc05]128        s = set_add( &b->set, "show_offline", "false", set_eval_bw_compat, irc );
[a758ec1]129        s->flags |= SET_HIDDEN;
[3ddb7477]130        s = set_add( &b->set, "simulate_netsplit", "true", set_eval_bool, irc );
[21c87a7]131        s = set_add( &b->set, "timezone", "local", set_eval_timezone, irc );
[3ddb7477]132        s = set_add( &b->set, "to_char", ": ", set_eval_to_char, irc );
133        s = set_add( &b->set, "typing_notice", "false", set_eval_bool, irc );
134
135        irc->root = iu = irc_user_new( irc, ROOT_NICK );
136        iu->host = g_strdup( myhost );
137        iu->fullname = g_strdup( ROOT_FN );
[280c56a]138        iu->f = &irc_user_root_funcs;
[3ddb7477]139       
140        iu = irc_user_new( irc, NS_NICK );
141        iu->host = g_strdup( myhost );
142        iu->fullname = g_strdup( ROOT_FN );
[280c56a]143        iu->f = &irc_user_root_funcs;
[3ddb7477]144       
145        irc->user = g_new0( irc_user_t, 1 );
146        irc->user->host = g_strdup( host );
147       
[ebaebfe]148        conf_loaddefaults( irc );
[b7d3cc34]149       
[f9756bd]150        /* Evaluator sets the iconv/oconv structures. */
[3ddb7477]151        set_eval_charset( set_find( &b->set, "charset" ), set_getstr( &b->set, "charset" ) );
[f9756bd]152       
[3ddb7477]153        irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host, "BitlBee-IRCd initialized, please go on" );
[b925666]154        if( isatty( irc->fd ) )
155                irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host,
156                           "If you read this, you most likely accidentally "
157                           "started BitlBee in inetd mode on the command line. "
158                           "You probably want to run it in (Fork)Daemon mode. "
159                           "See doc/README for more information." );
[3ddb7477]160       
[ebaebfe]161        g_free( myhost );
162        g_free( host );
163       
[5674207]164        nogaim_init();
165       
[3ddb7477]166        return irc;
[b7d3cc34]167}
168
[f73b969]169/* immed=1 makes this function pretty much equal to irc_free(), except that
170   this one will "log". In case the connection is already broken and we
171   shouldn't try to write to it. */
[fc50d48]172void irc_abort( irc_t *irc, int immed, char *format, ... )
[c1826c6]173{
[82ca986]174        char *reason = NULL;
175       
[fc50d48]176        if( format != NULL )
177        {
[f73b969]178                va_list params;
[fc50d48]179               
180                va_start( params, format );
[f73b969]181                reason = g_strdup_vprintf( format, params );
[fc50d48]182                va_end( params );
183        }
184       
[82ca986]185        irc_write( irc, "ERROR :Closing link: %s", reason ? : "" );
186       
187        ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
188                           irc->user->nick ? irc->user->nick : "(NONE)",
189                           irc->user->host, reason ? : "" );
190       
191        g_free( reason );
192       
193        irc_flush( irc );
194        if( immed )
[c1826c6]195        {
[82ca986]196                irc_free( irc );
[c1826c6]197        }
198        else
199        {
[82ca986]200                b_event_remove( irc->ping_source_id );
201                irc->ping_source_id = b_timeout_add( 1, (b_event_handler) irc_free, irc );
[c1826c6]202        }
203}
204
[3ddb7477]205static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data );
[b7d3cc34]206
[fa75134]207void irc_free( irc_t * irc )
[b7d3cc34]208{
[82ca986]209        irc->status |= USTATUS_SHUTDOWN;
210       
[b7d3cc34]211        log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd );
212       
[3ddb7477]213        if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->b->set, "save_on_quit" ) ) 
[3183c21]214                if( storage_save( irc, NULL, TRUE ) != STORAGE_OK )
[d33679e]215                        log_message( LOGLVL_WARNING, "Error while saving settings for user %s", irc->user->nick );
[b7d3cc34]216       
217        irc_connection_list = g_slist_remove( irc_connection_list, irc );
218       
[fa75134]219        while( irc->queries != NULL )
220                query_del( irc, irc->queries );
[d33679e]221       
222        /* This is a little bit messy: bee_free() frees all b->users which
223           calls us back to free the corresponding irc->users. So do this
224           before we clear the remaining ones ourselves. */
225        bee_free( irc->b );
[5b52a48]226       
[3ddb7477]227        while( irc->users )
[eabc9d2]228                irc_user_free( irc, (irc_user_t *) irc->users->data );
[b7d3cc34]229       
[63a520b]230        while( irc->channels )
231                irc_channel_free( irc->channels->data );
232       
[fa75134]233        if( irc->ping_source_id > 0 )
234                b_event_remove( irc->ping_source_id );
[883a398]235        if( irc->r_watch_source_id > 0 )
236                b_event_remove( irc->r_watch_source_id );
[fa75134]237        if( irc->w_watch_source_id > 0 )
238                b_event_remove( irc->w_watch_source_id );
239       
240        closesocket( irc->fd );
241        irc->fd = -1;
242       
[3ddb7477]243        g_hash_table_foreach_remove( irc->nick_user_hash, irc_free_hashkey, NULL );
244        g_hash_table_destroy( irc->nick_user_hash );
[b7d3cc34]245       
[fa75134]246        g_hash_table_foreach_remove( irc->watches, irc_free_hashkey, NULL );
247        g_hash_table_destroy( irc->watches );
[b7d3cc34]248       
[f9756bd]249        if( irc->iconv != (GIConv) -1 )
250                g_iconv_close( irc->iconv );
251        if( irc->oconv != (GIConv) -1 )
252                g_iconv_close( irc->oconv );
253       
[fa75134]254        g_free( irc->sendbuffer );
255        g_free( irc->readbuffer );
256        g_free( irc->password );
257       
258        g_free( irc );
[b7d3cc34]259       
[565a1ea]260        if( global.conf->runmode == RUNMODE_INETD ||
261            global.conf->runmode == RUNMODE_FORKDAEMON ||
262            ( global.conf->runmode == RUNMODE_DAEMON &&
263              global.listen_socket == -1 &&
264              irc_connection_list == NULL ) )
[ba9edaa]265                b_main_quit();
[b7d3cc34]266}
267
[3ddb7477]268static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data )
[7cad7b4]269{
[3ddb7477]270        g_free( key );
[7cad7b4]271       
[3ddb7477]272        return( TRUE );
[7cad7b4]273}
274
[1f92a58]275/* USE WITH CAUTION!
276   Sets pass without checking */
277void irc_setpass (irc_t *irc, const char *pass)
278{
279        g_free (irc->password);
280       
281        if (pass) {
282                irc->password = g_strdup (pass);
283        } else {
284                irc->password = NULL;
285        }
286}
287
[0d9d53e]288static char *set_eval_password( set_t *set, char *value )
289{
290        irc_t *irc = set->data;
291       
292        if( irc->status & USTATUS_IDENTIFIED && value )
293        {
294                irc_setpass( irc, value );
295                return NULL;
296        }
297        else
298        {
299                return SET_INVALID;
300        }
301}
302
[3ddb7477]303static char **irc_splitlines( char *buffer );
304
[f73b969]305void irc_process( irc_t *irc )
[b7d3cc34]306{
[f9756bd]307        char **lines, *temp, **cmd;
[b7d3cc34]308        int i;
309
[de3e100]310        if( irc->readbuffer != NULL )
311        {
[3ddb7477]312                lines = irc_splitlines( irc->readbuffer );
[de3e100]313               
314                for( i = 0; *lines[i] != '\0'; i ++ )
315                {
[f9756bd]316                        char *conv = NULL;
[7d31002]317                       
[18ff38f]318                        /* [WvG] If the last line isn't empty, it's an incomplete line and we
319                           should wait for the rest to come in before processing it. */
[de3e100]320                        if( lines[i+1] == NULL )
321                        {
[b7d3cc34]322                                temp = g_strdup( lines[i] );
323                                g_free( irc->readbuffer );
324                                irc->readbuffer = temp;
[de3e100]325                                i ++;
[b7d3cc34]326                                break;
[e27661d]327                        }
328                       
[f9756bd]329                        if( irc->iconv != (GIConv) -1 )
[e27661d]330                        {
[f9756bd]331                                gsize bytes_read, bytes_written;
332                               
333                                conv = g_convert_with_iconv( lines[i], -1, irc->iconv,
334                                                             &bytes_read, &bytes_written, NULL );
335                               
336                                if( conv == NULL || bytes_read != strlen( lines[i] ) )
[94d52d64]337                                {
[fc0cf92]338                                        /* GLib can do strange things if things are not in the expected charset,
339                                           so let's be a little bit paranoid here: */
[94d52d64]340                                        if( irc->status & USTATUS_LOGGED_IN )
[fc0cf92]341                                        {
[43462708]342                                                irc_usermsg( irc, "Error: Charset mismatch detected. The charset "
[94d52d64]343                                                                  "setting is currently set to %s, so please make "
344                                                                  "sure your IRC client will send and accept text in "
345                                                                  "that charset, or tell BitlBee which charset to "
346                                                                  "expect by changing the charset setting. See "
347                                                                  "`help set charset' for more information. Your "
[f9756bd]348                                                                  "message was ignored.",
[3ddb7477]349                                                                  set_getstr( &irc->b->set, "charset" ) );
[f9756bd]350                                               
351                                                g_free( conv );
352                                                conv = NULL;
[fc0cf92]353                                        }
354                                        else
355                                        {
[3ddb7477]356                                                irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host,
[a83442a]357                                                           "Warning: invalid characters received at login time." );
[fc0cf92]358                                               
[f9756bd]359                                                conv = g_strdup( lines[i] );
[fc0cf92]360                                                for( temp = conv; *temp; temp ++ )
361                                                        if( *temp & 0x80 )
362                                                                *temp = '?';
363                                        }
[94d52d64]364                                }
365                                lines[i] = conv;
[e27661d]366                        }
[de3e100]367                       
[e1720ce]368                        if( lines[i] && ( cmd = irc_parse_line( lines[i] ) ) )
[f9756bd]369                        {
370                                irc_exec( irc, cmd );
371                                g_free( cmd );
372                        }
[f73b969]373                       
[f9756bd]374                        g_free( conv );
[f73b969]375                       
376                        /* Shouldn't really happen, but just in case... */
377                        if( !g_slist_find( irc_connection_list, irc ) )
[de3e100]378                        {
[b7d3cc34]379                                g_free( lines );
[f73b969]380                                return;
[b7d3cc34]381                        }
382                }
[de3e100]383               
384                if( lines[i] != NULL )
385                {
386                        g_free( irc->readbuffer );
[0431ea1]387                        irc->readbuffer = NULL;
[b7d3cc34]388                }
[de3e100]389               
[b7d3cc34]390                g_free( lines );
391        }
392}
393
[3ddb7477]394/* Splits a long string into separate lines. The array is NULL-terminated
395   and, unless the string contains an incomplete line at the end, ends with
396   an empty string. Could use g_strsplit() but this one does it in-place.
397   (So yes, it's destructive.) */
398static char **irc_splitlines( char *buffer )
[b7d3cc34]399{
[18ff38f]400        int i, j, n = 3;
[b7d3cc34]401        char **lines;
402
[18ff38f]403        /* Allocate n+1 elements. */
404        lines = g_new( char *, n + 1 );
[b7d3cc34]405       
[de3e100]406        lines[0] = buffer;
[b7d3cc34]407       
[18ff38f]408        /* Split the buffer in several strings, and accept any kind of line endings,
409         * knowing that ERC on Windows may send something interesting like \r\r\n,
410         * and surely there must be clients that think just \n is enough... */
411        for( i = 0, j = 0; buffer[i] != '\0'; i ++ )
[de3e100]412        {
[18ff38f]413                if( buffer[i] == '\r' || buffer[i] == '\n' )
[de3e100]414                {
[18ff38f]415                        while( buffer[i] == '\r' || buffer[i] == '\n' )
416                                buffer[i++] = '\0';
417                       
418                        lines[++j] = buffer + i;
[de3e100]419                       
[18ff38f]420                        if( j >= n )
421                        {
422                                n *= 2;
423                                lines = g_renew( char *, lines, n + 1 );
424                        }
425
426                        if( buffer[i] == '\0' )
427                                break;
[b7d3cc34]428                }
429        }
[de3e100]430       
[18ff38f]431        /* NULL terminate our list. */ 
432        lines[++j] = NULL;
433       
434        return lines;
[b7d3cc34]435}
436
[e27661d]437/* Split an IRC-style line into little parts/arguments. */
[0431ea1]438char **irc_parse_line( char *line )
[b7d3cc34]439{
440        int i, j;
441        char **cmd;
442       
443        /* Move the line pointer to the start of the command, skipping spaces and the optional prefix. */
[de3e100]444        if( line[0] == ':' )
445        {
[e1720ce]446                for( i = 0; line[i] && line[i] != ' '; i ++ );
[de3e100]447                line = line + i;
[b7d3cc34]448        }
[de3e100]449        for( i = 0; line[i] == ' '; i ++ );
450        line = line + i;
451       
[b7d3cc34]452        /* If we're already at the end of the line, return. If not, we're going to need at least one element. */
[de3e100]453        if( line[0] == '\0')
454                return NULL;
455       
456        /* Count the number of char **cmd elements we're going to need. */
457        j = 1;
458        for( i = 0; line[i] != '\0'; i ++ )
459        {
460                if( line[i] == ' ' )
461                {
462                        j ++;
[b7d3cc34]463                       
[de3e100]464                        if( line[i+1] == ':' )
465                                break;
466                }
[b7d3cc34]467        }       
468
469        /* Allocate the space we need. */
[de3e100]470        cmd = g_new( char *, j + 1 );
471        cmd[j] = NULL;
[b7d3cc34]472       
473        /* Do the actual line splitting, format is:
474         * Input: "PRIVMSG #bitlbee :foo bar"
475         * Output: cmd[0]=="PRIVMSG", cmd[1]=="#bitlbee", cmd[2]=="foo bar", cmd[3]==NULL
476         */
477
[de3e100]478        cmd[0] = line;
479        for( i = 0, j = 0; line[i] != '\0'; i ++ )
[b7d3cc34]480        {
[de3e100]481                if( line[i] == ' ' )
[b7d3cc34]482                {
[de3e100]483                        line[i] = '\0';
484                        cmd[++j] = line + i + 1;
[b7d3cc34]485                       
[de3e100]486                        if( line[i+1] == ':' )
[b7d3cc34]487                        {
[de3e100]488                                cmd[j] ++;
[b7d3cc34]489                                break;
490                        }
491                }
492        }
493       
[de3e100]494        return cmd;
[b7d3cc34]495}
496
[e27661d]497/* Converts such an array back into a command string. Mainly used for the IPC code right now. */
[74c119d]498char *irc_build_line( char **cmd )
499{
500        int i, len;
501        char *s;
[b7d3cc34]502       
[74c119d]503        if( cmd[0] == NULL )
504                return NULL;
[b7d3cc34]505       
[74c119d]506        len = 1;
507        for( i = 0; cmd[i]; i ++ )
508                len += strlen( cmd[i] ) + 1;
509       
510        if( strchr( cmd[i-1], ' ' ) != NULL )
511                len ++;
512       
513        s = g_new0( char, len + 1 );
514        for( i = 0; cmd[i]; i ++ )
[b7d3cc34]515        {
[74c119d]516                if( cmd[i+1] == NULL && strchr( cmd[i], ' ' ) != NULL )
517                        strcat( s, ":" );
[b7d3cc34]518               
[74c119d]519                strcat( s, cmd[i] );
[b7d3cc34]520               
[74c119d]521                if( cmd[i+1] )
522                        strcat( s, " " );
[b7d3cc34]523        }
[74c119d]524        strcat( s, "\r\n" );
[b7d3cc34]525       
[74c119d]526        return s;
[b7d3cc34]527}
528
[3ddb7477]529void irc_write( irc_t *irc, char *format, ... ) 
[b7d3cc34]530{
531        va_list params;
[3ddb7477]532
[b7d3cc34]533        va_start( params, format );
[3ddb7477]534        irc_vawrite( irc, format, params );     
[b7d3cc34]535        va_end( params );
[3ddb7477]536
[b7d3cc34]537        return;
538}
539
[3ddb7477]540void irc_write_all( int now, char *format, ... )
[b7d3cc34]541{
542        va_list params;
[3ddb7477]543        GSList *temp;   
[b7d3cc34]544       
545        va_start( params, format );
546       
[3ddb7477]547        temp = irc_connection_list;
548        while( temp != NULL )
549        {
550                irc_t *irc = temp->data;
551               
552                if( now )
553                {
554                        g_free( irc->sendbuffer );
555                        irc->sendbuffer = g_strdup( "\r\n" );
556                }
557                irc_vawrite( temp->data, format, params );
558                if( now )
559                {
[4aa0f6b]560                        bitlbee_io_current_client_write( irc, irc->fd, B_EV_IO_WRITE );
[3ddb7477]561                }
562                temp = temp->next;
563        }
564       
[b7d3cc34]565        va_end( params );
566        return;
[3ddb7477]567} 
[b7d3cc34]568
569void irc_vawrite( irc_t *irc, char *format, va_list params )
570{
571        int size;
[f9756bd]572        char line[IRC_MAX_LINE+1];
[d783e48]573               
[0356ae3]574        /* Don't try to write anything new anymore when shutting down. */
[5898ef8]575        if( irc->status & USTATUS_SHUTDOWN )
[b7d3cc34]576                return;
[d783e48]577       
[f9756bd]578        memset( line, 0, sizeof( line ) );
[d783e48]579        g_vsnprintf( line, IRC_MAX_LINE - 2, format, params );
[b7d3cc34]580        strip_newlines( line );
[f9756bd]581       
582        if( irc->oconv != (GIConv) -1 )
[d783e48]583        {
[f9756bd]584                gsize bytes_read, bytes_written;
585                char *conv;
586               
587                conv = g_convert_with_iconv( line, -1, irc->oconv,
588                                             &bytes_read, &bytes_written, NULL );
589
590                if( bytes_read == strlen( line ) )
591                        strncpy( line, conv, IRC_MAX_LINE - 2 );
[d783e48]592               
[f9756bd]593                g_free( conv );
[d783e48]594        }
[f9756bd]595        g_strlcat( line, "\r\n", IRC_MAX_LINE + 1 );
[d783e48]596       
[a0d04d6]597        if( irc->sendbuffer != NULL )
598        {
[b7d3cc34]599                size = strlen( irc->sendbuffer ) + strlen( line );
600                irc->sendbuffer = g_renew ( char, irc->sendbuffer, size + 1 );
601                strcpy( ( irc->sendbuffer + strlen( irc->sendbuffer ) ), line );
602        }
[a0d04d6]603        else
[b7d3cc34]604        {
[a0d04d6]605                irc->sendbuffer = g_strdup(line);
[b7d3cc34]606        }
607       
[a0d04d6]608        if( irc->w_watch_source_id == 0 )
[0356ae3]609        {
610                /* If the buffer is empty we can probably write, so call the write event handler
611                   immediately. If it returns TRUE, it should be called again, so add the event to
612                   the queue. If it's FALSE, we emptied the buffer and saved ourselves some work
613                   in the event queue. */
[bbb6ffb]614                /* Really can't be done as long as the code doesn't do error checking very well:
[e046390]615                if( bitlbee_io_current_client_write( irc, irc->fd, B_EV_IO_WRITE ) ) */
[bbb6ffb]616               
617                /* So just always do it via the event handler. */
[e046390]618                irc->w_watch_source_id = b_input_add( irc->fd, B_EV_IO_WRITE, bitlbee_io_current_client_write, irc );
[0356ae3]619        }
[a0d04d6]620       
[b7d3cc34]621        return;
622}
623
[f1c2b21]624/* Flush sendbuffer if you can. If it fails, fail silently and let some
625   I/O event handler clean up. */
626void irc_flush( irc_t *irc )
627{
628        ssize_t n;
629        size_t len;
630       
631        if( irc->sendbuffer == NULL )
632                return;
633       
634        len = strlen( irc->sendbuffer );
635        if( ( n = send( irc->fd, irc->sendbuffer, len, 0 ) ) == len )
636        {
637                g_free( irc->sendbuffer );
638                irc->sendbuffer = NULL;
639               
640                b_event_remove( irc->w_watch_source_id );
641                irc->w_watch_source_id = 0;
642        }
643        else if( n > 0 )
644        {
645                char *s = g_strdup( irc->sendbuffer + n );
646                g_free( irc->sendbuffer );
647                irc->sendbuffer = s;
648        }
649        /* Otherwise something went wrong and we don't currently care
650           what the error was. We may or may not succeed later, we
651           were just trying to flush the buffer immediately. */
652}
653
654/* Meant for takeover functionality. Transfer an IRC connection to a different
655   socket. */
656void irc_switch_fd( irc_t *irc, int fd )
657{
658        irc_write( irc, "ERROR :Transferring session to a new connection" );
659        irc_flush( irc ); /* Write it now or forget about it forever. */
660       
661        if( irc->sendbuffer )
662        {
663                b_event_remove( irc->w_watch_source_id );
[af9f2ca]664                irc->w_watch_source_id = 0;
[f1c2b21]665                g_free( irc->sendbuffer );
[af9f2ca]666                irc->sendbuffer = NULL;
[f1c2b21]667        }
668       
669        b_event_remove( irc->r_watch_source_id );
670        closesocket( irc->fd );
671        irc->fd = fd;
672        irc->r_watch_source_id = b_input_add( irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc );
673}
674
675void irc_sync( irc_t *irc )
676{
677        GSList *l;
678       
679        irc_write( irc, ":%s!%s@%s MODE %s :+%s", irc->user->nick,
680                   irc->user->user, irc->user->host, irc->user->nick,
681                   irc->umode );
682       
683        for( l = irc->channels; l; l = l->next )
684        {
685                irc_channel_t *ic = l->data;
686                if( ic->flags & IRC_CHANNEL_JOINED )
687                        irc_send_join( ic, irc->user );
688        }
689}
690
691void irc_desync( irc_t *irc )
692{
693        GSList *l;
694       
695        for( l = irc->channels; l; l = l->next )
696                irc_channel_del_user( l->data, irc->user, IRC_CDU_KICK,
697                                      "Switching to old session" );
698       
699        irc_write( irc, ":%s!%s@%s MODE %s :-%s", irc->user->nick,
700                   irc->user->user, irc->user->host, irc->user->nick,
701                   irc->umode );
702}
703
[edf9657]704int irc_check_login( irc_t *irc )
[b7d3cc34]705{
[3ddb7477]706        if( irc->user->user && irc->user->nick )
[edf9657]707        {
[3af70b0]708                if( global.conf->authmode == AUTHMODE_CLOSED && !( irc->status & USTATUS_AUTHORIZED ) )
[b7d3cc34]709                {
[3ddb7477]710                        irc_send_num( irc, 464, ":This server is password-protected." );
[edf9657]711                        return 0;
[b7d3cc34]712                }
[edf9657]713                else
[b7d3cc34]714                {
[4be8239]715                        irc_channel_t *ic;
716                        irc_user_t *iu = irc->user;
717                       
718                        irc->user = irc_user_new( irc, iu->nick );
719                        irc->user->user = iu->user;
[b95932e]720                        irc->user->host = iu->host;
[4be8239]721                        irc->user->fullname = iu->fullname;
[280c56a]722                        irc->user->f = &irc_user_self_funcs;
[4be8239]723                        g_free( iu->nick );
724                        g_free( iu );
725                       
726                        if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON )
727                                ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname );
728                       
729                        irc->status |= USTATUS_LOGGED_IN;
730                       
[3ddb7477]731                        irc_send_login( irc );
[4be8239]732                       
[b919363]733                        irc->umode[0] = '\0';
734                        irc_umode_set( irc, "+" UMODE, TRUE );
735                       
[fd45e1d1]736                        ic = irc->default_channel = irc_channel_new( irc, ROOT_CHAN );
[83e92bf]737                        irc_channel_set_topic( ic, CONTROL_TOPIC, irc->root );
[c8eeadd]738                        set_setstr( &ic->set, "auto_join", "true" );
739                        irc_channel_auto_joins( irc, NULL );
[4be8239]740                       
[f7ca587]741                        irc->root->last_channel = irc->default_channel;
[74f1cde]742                       
[f7ca587]743                        irc_usermsg( irc,
744                                     "Welcome to the BitlBee gateway!\n\n"
745                                     "If you've never used BitlBee before, please do read the help "
746                                     "information using the \x02help\x02 command. Lots of FAQs are "
747                                     "answered there.\n"
748                                     "If you already have an account on this server, just use the "
749                                     "\x02identify\x02 command to identify yourself." );
[e21c0f8]750                       
[70f69ecc]751                        /* This is for bug #209 (use PASS to identify to NickServ). */
752                        if( irc->password != NULL )
753                        {
754                                char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL };
755                               
756                                irc_setpass( irc, NULL );
757                                root_command( irc, send_cmd );
758                                g_free( send_cmd[1] );
759                        }
760                       
[edf9657]761                        return 1;
[b7d3cc34]762                }
[edf9657]763        }
764        else
765        {
766                /* More information needed. */
767                return 0;
768        }
[b7d3cc34]769}
770
[65016a6]771/* TODO: This is a mess, but this function is a bit too complicated to be
772   converted to something more generic. */
[b919363]773void irc_umode_set( irc_t *irc, const char *s, gboolean allow_priv )
774{
775        /* allow_priv: Set to 0 if s contains user input, 1 if you want
776           to set a "privileged" mode (+o, +R, etc). */
777        char m[128], st = 1;
778        const char *t;
779        int i;
780        char changes[512], *p, st2 = 2;
781        char badflag = 0;
782       
783        memset( m, 0, sizeof( m ) );
784       
785        for( t = irc->umode; *t; t ++ )
786                if( *t < sizeof( m ) )
787                        m[(int)*t] = 1;
788       
789        p = changes;
790        for( t = s; *t; t ++ )
791        {
792                if( *t == '+' || *t == '-' )
793                        st = *t == '+';
794                else if( ( st == 0 && ( !strchr( UMODES_KEEP, *t ) || allow_priv ) ) ||
795                         ( st == 1 && strchr( UMODES, *t ) ) ||
796                         ( st == 1 && allow_priv && strchr( UMODES_PRIV, *t ) ) )
797                {
798                        if( m[(int)*t] != st)
799                        {
800                                if( st != st2 )
801                                        st2 = st, *p++ = st ? '+' : '-';
802                                *p++ = *t;
803                        }
804                        m[(int)*t] = st;
805                }
806                else
807                        badflag = 1;
808        }
809        *p = '\0';
810       
811        memset( irc->umode, 0, sizeof( irc->umode ) );
812       
813        for( i = 'A'; i <= 'z' && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
814                if( m[i] )
815                        irc->umode[strlen(irc->umode)] = i;
816       
817        if( badflag )
818                irc_send_num( irc, 501, ":Unknown MODE flag" );
819        if( *changes )
820                irc_write( irc, ":%s!%s@%s MODE %s :%s", irc->user->nick,
821                           irc->user->user, irc->user->host, irc->user->nick,
822                           changes );
823}
824
[3923003]825/* Returns 0 if everything seems to be okay, a number >0 when there was a
826   timeout. The number returned is the number of seconds we received no
827   pongs from the user. When not connected yet, we don't ping but drop the
828   connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
829static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond )
830{
831        irc_t *irc = _irc;
832        int rv = 0;
833       
834        if( !( irc->status & USTATUS_LOGGED_IN ) )
835        {
836                if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) )
837                        rv = gettime() - irc->last_pong;
838        }
839        else
840        {
841                if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging )
842                {
843                        irc_write( irc, "PING :%s", IRC_PING_STRING );
844                        irc->pinging = 1;
845                }
846                else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) )
847                {
848                        rv = gettime() - irc->last_pong;
849                }
850        }
851       
852        if( rv > 0 )
853        {
854                irc_abort( irc, 0, "Ping Timeout: %d seconds", rv );
855                return FALSE;
856        }
857       
858        return TRUE;
859}
860
[3ddb7477]861static char *set_eval_charset( set_t *set, char *value )
[b7d3cc34]862{
[21c87a7]863        irc_t *irc = (irc_t*) set->data;
864        char *test;
865        gsize test_bytes = 0;
[3ddb7477]866        GIConv ic, oc;
[b7d3cc34]867
[3ddb7477]868        if( g_strcasecmp( value, "none" ) == 0 )
869                value = g_strdup( "utf-8" );
[b7d3cc34]870
[21c87a7]871        if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 )
[b7d3cc34]872        {
[3ddb7477]873                return NULL;
[b7d3cc34]874        }
[0e7ab64]875       
[21c87a7]876        /* Do a test iconv to see if the user picked an IRC-compatible
877           charset (for example utf-16 goes *horribly* wrong). */
878        if( ( test = g_convert_with_iconv( " ", 1, oc, NULL, &test_bytes, NULL ) ) == NULL ||
879            test_bytes > 1 )
[0e7ab64]880        {
[21c87a7]881                g_free( test );
882                g_iconv_close( oc );
883                irc_usermsg( irc, "Unsupported character set: The IRC protocol "
884                                  "only supports 8-bit character sets." );
885                return NULL;
[0e7ab64]886        }
[21c87a7]887        g_free( test );
[0e7ab64]888       
[21c87a7]889        if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 )
[b7d3cc34]890        {
[21c87a7]891                g_iconv_close( oc );
[3ddb7477]892                return NULL;
[b7d3cc34]893        }
894       
[3ddb7477]895        if( irc->iconv != (GIConv) -1 )
896                g_iconv_close( irc->iconv );
897        if( irc->oconv != (GIConv) -1 )
898                g_iconv_close( irc->oconv );
[b7d3cc34]899       
[3ddb7477]900        irc->iconv = ic;
901        irc->oconv = oc;
[0e7ab64]902
[3ddb7477]903        return value;
[0e7ab64]904}
[0e8b3e8]905
[6d8cc05]906/* Mostly meant for upgrades. If one of these is set to the non-default,
907   set show_users of all channels to something with the same effect. */
908static char *set_eval_bw_compat( set_t *set, char *value )
[0e8b3e8]909{
910        irc_t *irc = set->data;
[6d8cc05]911        char *val;
912        GSList *l;
[0e8b3e8]913       
[6d8cc05]914        irc_usermsg( irc, "Setting `%s' is obsolete, use the `show_users' "
915                     "channel setting instead.", set->key );
[0e8b3e8]916       
[6d8cc05]917        if( strcmp( set->key, "away_devoice" ) == 0 && !bool2int( value ) )
918                val = "online,away";
919        else if( strcmp( set->key, "show_offline" ) == 0 && bool2int( value ) )
920                val = "online@,away+,offline";
921        else
[a758ec1]922                val = "online+,away";
[0e8b3e8]923       
[6d8cc05]924        for( l = irc->channels; l; l = l->next )
925        {
926                irc_channel_t *ic = l->data;
927                /* No need to check channel type, if the setting doesn't exist it
928                   will just be ignored. */
929                set_setstr( &ic->set, "show_users", val );
930        }
[0e8b3e8]931       
[6d8cc05]932        return SET_INVALID;
[0e8b3e8]933}
Note: See TracBrowser for help on using the repository browser.