source: irc.c @ 16834a5

Last change on this file since 16834a5 was 16834a5, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-06-06T01:30:45Z

Set handle_unknown to add_channel by default.

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