source: irc.c @ c7eb771

Last change on this file since c7eb771 was 1c8e5f7, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-06-11T15:12:27Z

Added away_reply_timeout setting so BitlBee will suppress away messages sent
in response to PRIVMSG if one was sent recently - some IRC clients including
irssi don't do this very well (when talking to >1 people who are away for
example).

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