source: irc.c @ c8eeadd

Last change on this file since c8eeadd was c8eeadd, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-04T10:16:07Z

Added automatic joining of channels. Auto-rejoin functionality for
groupchats not reimplemented yet but that's the next step.

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