source: irc.c @ 4aa0f6b

Last change on this file since 4aa0f6b was 4aa0f6b, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-06-07T14:31:07Z

Merging killerbee stuff, bringing all the bleeding-edge stuff together.

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