source: irc.c @ 0d9d53e

Last change on this file since 0d9d53e was 0d9d53e, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-06-07T00:58:07Z

Fixed "set password" and "set auto_reconnect_delay".

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