source: irc.c @ 8e9e2b7

Last change on this file since 8e9e2b7 was 8e9e2b7, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-10-03T02:45:26Z

Merging mainline, which includes a huge msnp13 merge.

Not 100% sure about the OpenSSL merge, should double check that but I'm
currently offline.

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