source: irc.c @ 33b306e

Last change on this file since 33b306e was 33b306e, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-04-07T02:27:55Z

Don't allow non-8-bit character sets like utf-16 which completely break the
IRC protocol. (Happened to at least two public server users by now and it
renders the accounts useless without manual intervention.)

  • Property mode set to 100644
File size: 34.1 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
7/* The big hairy IRCd part of the project                               */
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#define BITLBEE_CORE
27#include "bitlbee.h"
[7d3ef7b]28#include "sock.h"
[b7d3cc34]29#include "crypting.h"
[2face62]30#include "ipc.h"
[b7d3cc34]31
[ba9edaa]32static gboolean irc_userping( gpointer _irc, int fd, b_input_condition cond );
[b7d3cc34]33
34GSList *irc_connection_list = NULL;
35
[f3579fd]36static char *set_eval_password( set_t *set, char *value )
[c2295f7]37{
[0383943]38        irc_t *irc = set->data;
39       
[3183c21]40        if( irc->status & USTATUS_IDENTIFIED && value )
[f3579fd]41        {
42                irc_setpass( irc, value );
43                return NULL;
44        }
45        else
46        {
47                return SET_INVALID;
48        }
[c2295f7]49}
50
[f9756bd]51static char *set_eval_charset( set_t *set, char *value )
52{
53        irc_t *irc = set->data;
[33b306e]54        char *test;
55        gsize test_bytes = 0;
[f9756bd]56        GIConv ic, oc;
57
58        if( g_strcasecmp( value, "none" ) == 0 )
59                value = g_strdup( "utf-8" );
60
[33b306e]61        if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 )
[f9756bd]62        {
63                return NULL;
64        }
[33b306e]65        if( ( test = g_convert_with_iconv( " ", 1, oc, NULL, &test_bytes, NULL ) ) == NULL ||
66            test_bytes > 1 )
67        {
68                g_free( test );
69                g_iconv_close( oc );
70                irc_usermsg( irc, "Unsupported character set: The IRC protocol "
71                                  "only supports 8-bit character sets." );
72                return NULL;
73        }
74        if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 )
[f9756bd]75        {
[33b306e]76                g_iconv_close( oc );
[f9756bd]77                return NULL;
78        }
79       
80        if( irc->iconv != (GIConv) -1 )
81                g_iconv_close( irc->iconv );
82        if( irc->oconv != (GIConv) -1 )
83                g_iconv_close( irc->oconv );
84       
85        irc->iconv = ic;
86        irc->oconv = oc;
87
88        return value;
89}
90
[58adb7e]91static char *set_eval_away_status( set_t *set, char *value )
92{
93        irc_t *irc = set->data;
94        account_t *a;
95       
96        g_free( set->value );
97        set->value = g_strdup( value );
98       
99        for( a = irc->accounts; a; a = a->next )
100        {
101                struct im_connection *ic = a->ic;
102               
103                if( ic && ic->flags & OPT_LOGGED_IN )
104                        imc_away_send_update( ic );
105        }
106       
107        return value;
108}
109
[b7d3cc34]110irc_t *irc_new( int fd )
111{
[e4d6271]112        irc_t *irc;
[e9b755e]113        struct sockaddr_storage sock;
[7435ccf]114        socklen_t socklen = sizeof( sock );
[7125cb3]115        set_t *s;
[e4d6271]116       
117        irc = g_new0( irc_t, 1 );
[b7d3cc34]118       
119        irc->fd = fd;
[a0d04d6]120        sock_make_nonblocking( irc->fd );
121       
[ba9edaa]122        irc->r_watch_source_id = b_input_add( irc->fd, GAIM_INPUT_READ, bitlbee_io_current_client_read, irc );
[b7d3cc34]123       
124        irc->status = USTATUS_OFFLINE;
125        irc->last_pong = gettime();
126       
127        irc->userhash = g_hash_table_new( g_str_hash, g_str_equal );
128        irc->watches = g_hash_table_new( g_str_hash, g_str_equal );
129       
130        strcpy( irc->umode, UMODE );
131        irc->mynick = g_strdup( ROOT_NICK );
132        irc->channel = g_strdup( ROOT_CHAN );
133       
[f9756bd]134        irc->iconv = (GIConv) -1;
135        irc->oconv = (GIConv) -1;
136       
[b7d3cc34]137        if( global.conf->hostname )
138        {
139                irc->myhost = g_strdup( global.conf->hostname );
140        }
[7435ccf]141        else if( getsockname( irc->fd, (struct sockaddr*) &sock, &socklen ) == 0 ) 
[b7d3cc34]142        {
[2231302]143                char buf[NI_MAXHOST+1];
[e9b755e]144
[2231302]145                if( getnameinfo( (struct sockaddr *) &sock, socklen, buf,
[c84e31a]146                                 NI_MAXHOST, NULL, 0, 0 ) == 0 )
[2231302]147                {
148                        irc->myhost = g_strdup( ipv6_unwrap( buf ) );
149                }
[b7d3cc34]150        }
151       
[7435ccf]152        if( getpeername( irc->fd, (struct sockaddr*) &sock, &socklen ) == 0 )
[b7d3cc34]153        {
[2231302]154                char buf[NI_MAXHOST+1];
[e9b755e]155
[2231302]156                if( getnameinfo( (struct sockaddr *)&sock, socklen, buf,
[c84e31a]157                                 NI_MAXHOST, NULL, 0, 0 ) == 0 )
[2231302]158                {
[2a6ca4f]159                        irc->host = g_strdup( ipv6_unwrap( buf ) );
[2231302]160                }
[b7d3cc34]161        }
162       
[3e1e11af]163        if( irc->host == NULL )
164                irc->host = g_strdup( "localhost.localdomain" );
165        if( irc->myhost == NULL )
166                irc->myhost = g_strdup( "localhost.localdomain" );
167       
[b7d3cc34]168        if( global.conf->ping_interval > 0 && global.conf->ping_timeout > 0 )
[ba9edaa]169                irc->ping_source_id = b_timeout_add( global.conf->ping_interval * 1000, irc_userping, irc );
[b7d3cc34]170       
171        irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost, "BitlBee-IRCd initialized, please go on" );
172
173        irc_connection_list = g_slist_append( irc_connection_list, irc );
174       
[58adb7e]175        s = set_add( &irc->set, "away", NULL,  set_eval_away_status, irc );
176        s->flags |= SET_NULL_OK;
[7125cb3]177        s = set_add( &irc->set, "away_devoice", "true",  set_eval_away_devoice, irc );
178        s = set_add( &irc->set, "auto_connect", "true", set_eval_bool, irc );
[435f552]179        s = set_add( &irc->set, "auto_reconnect", "true", set_eval_bool, irc );
[7125cb3]180        s = set_add( &irc->set, "auto_reconnect_delay", "5*3<900", set_eval_account_reconnect_delay, irc );
181        s = set_add( &irc->set, "buddy_sendbuffer", "false", set_eval_bool, irc );
182        s = set_add( &irc->set, "buddy_sendbuffer_delay", "200", set_eval_int, irc );
183        s = set_add( &irc->set, "charset", "utf-8", set_eval_charset, irc );
[0baed0d]184        s = set_add( &irc->set, "control_channel", irc->channel, set_eval_control_channel, irc );
[7125cb3]185        s = set_add( &irc->set, "debug", "false", set_eval_bool, irc );
186        s = set_add( &irc->set, "default_target", "root", NULL, irc );
187        s = set_add( &irc->set, "display_namechanges", "false", set_eval_bool, irc );
188        s = set_add( &irc->set, "handle_unknown", "root", NULL, irc );
189        s = set_add( &irc->set, "lcnicks", "true", set_eval_bool, irc );
190        s = set_add( &irc->set, "ops", "both", set_eval_ops, irc );
[f3579fd]191        s = set_add( &irc->set, "password", NULL, set_eval_password, irc );
[7125cb3]192        s->flags |= SET_NULL_OK;
193        s = set_add( &irc->set, "private", "true", set_eval_bool, irc );
194        s = set_add( &irc->set, "query_order", "lifo", NULL, irc );
195        s = set_add( &irc->set, "root_nick", irc->mynick, set_eval_root_nick, irc );
196        s = set_add( &irc->set, "save_on_quit", "true", set_eval_bool, irc );
197        s = set_add( &irc->set, "simulate_netsplit", "true", set_eval_bool, irc );
[58adb7e]198        s = set_add( &irc->set, "status", NULL,  set_eval_away_status, irc );
199        s->flags |= SET_NULL_OK;
[7125cb3]200        s = set_add( &irc->set, "strip_html", "true", NULL, irc );
201        s = set_add( &irc->set, "to_char", ": ", set_eval_to_char, irc );
202        s = set_add( &irc->set, "typing_notice", "false", set_eval_bool, irc );
[b7d3cc34]203       
204        conf_loaddefaults( irc );
205       
[f9756bd]206        /* Evaluator sets the iconv/oconv structures. */
207        set_eval_charset( set_find( &irc->set, "charset" ), set_getstr( &irc->set, "charset" ) );
208       
[b7d3cc34]209        return( irc );
210}
211
[f73b969]212/* immed=1 makes this function pretty much equal to irc_free(), except that
213   this one will "log". In case the connection is already broken and we
214   shouldn't try to write to it. */
[fc50d48]215void irc_abort( irc_t *irc, int immed, char *format, ... )
[c1826c6]216{
[fc50d48]217        if( format != NULL )
218        {
[f73b969]219                va_list params;
[fc50d48]220                char *reason;
221               
222                va_start( params, format );
[f73b969]223                reason = g_strdup_vprintf( format, params );
[fc50d48]224                va_end( params );
225               
226                if( !immed )
227                        irc_write( irc, "ERROR :Closing link: %s", reason );
228               
229                ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
[f73b969]230                                   irc->nick ? irc->nick : "(NONE)", irc->host, reason );
[fc50d48]231               
232                g_free( reason );
233        }
234        else
235        {
236                if( !immed )
237                        irc_write( irc, "ERROR :Closing link" );
238               
239                ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
[f73b969]240                                   irc->nick ? irc->nick : "(NONE)", irc->host, "No reason given" );
[fc50d48]241        }
242       
[79e826a]243        irc->status |= USTATUS_SHUTDOWN;
[fc50d48]244        if( irc->sendbuffer && !immed )
[c1826c6]245        {
[883a398]246                /* Set up a timeout event that should shut down the connection
247                   in a second, just in case ..._write doesn't do it first. */
[fc50d48]248               
[ba9edaa]249                b_event_remove( irc->r_watch_source_id );
[883a398]250                irc->r_watch_source_id = 0;
251               
252                b_event_remove( irc->ping_source_id );
253                irc->ping_source_id = b_timeout_add( 1000, (b_event_handler) irc_free, irc );
[c1826c6]254        }
255        else
256        {
257                irc_free( irc );
258        }
259}
260
[36fa9bd]261static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data )
[b7d3cc34]262{
263        g_free( key );
264       
265        return( TRUE );
266}
267
268/* Because we have no garbage collection, this is quite annoying */
[fa75134]269void irc_free( irc_t * irc )
[b7d3cc34]270{
271        user_t *user, *usertmp;
272       
273        log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd );
274       
[d5ccd83]275        if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->set, "save_on_quit" ) ) 
[3183c21]276                if( storage_save( irc, NULL, TRUE ) != STORAGE_OK )
[b7d3cc34]277                        irc_usermsg( irc, "Error while saving settings!" );
278       
279        irc_connection_list = g_slist_remove( irc_connection_list, irc );
280       
[fa75134]281        while( irc->accounts )
282        {
283                if( irc->accounts->ic )
284                        imc_logout( irc->accounts->ic, FALSE );
285                else if( irc->accounts->reconnect )
286                        cancel_auto_reconnect( irc->accounts );
287               
288                if( irc->accounts->ic == NULL )
289                        account_del( irc, irc->accounts );
[f959495]290                else
291                        /* Nasty hack, but account_del() doesn't work in this
292                           case and we don't want infinite loops, do we? ;-) */
293                        irc->accounts = irc->accounts->next;
[fa75134]294        }
295       
296        while( irc->queries != NULL )
297                query_del( irc, irc->queries );
[5b52a48]298       
[fa75134]299        while( irc->set )
300                set_del( &irc->set, irc->set->key );
[b7d3cc34]301       
[fa75134]302        if (irc->users != NULL)
303        {
[b7d3cc34]304                user = irc->users;
[fa75134]305                while( user != NULL )
306                {
307                        g_free( user->nick );
308                        g_free( user->away );
309                        g_free( user->handle );
310                        if( user->user != user->nick ) g_free( user->user );
311                        if( user->host != user->nick ) g_free( user->host );
312                        if( user->realname != user->nick ) g_free( user->realname );
313                        b_event_remove( user->sendbuf_timer );
[b7d3cc34]314                                       
315                        usertmp = user;
316                        user = user->next;
[fa75134]317                        g_free( usertmp );
[b7d3cc34]318                }
319        }
320       
[fa75134]321        if( irc->ping_source_id > 0 )
322                b_event_remove( irc->ping_source_id );
[883a398]323        if( irc->r_watch_source_id > 0 )
324                b_event_remove( irc->r_watch_source_id );
[fa75134]325        if( irc->w_watch_source_id > 0 )
326                b_event_remove( irc->w_watch_source_id );
327       
328        closesocket( irc->fd );
329        irc->fd = -1;
330       
331        g_hash_table_foreach_remove( irc->userhash, irc_free_hashkey, NULL );
332        g_hash_table_destroy( irc->userhash );
[b7d3cc34]333       
[fa75134]334        g_hash_table_foreach_remove( irc->watches, irc_free_hashkey, NULL );
335        g_hash_table_destroy( irc->watches );
[b7d3cc34]336       
[f9756bd]337        if( irc->iconv != (GIConv) -1 )
338                g_iconv_close( irc->iconv );
339        if( irc->oconv != (GIConv) -1 )
340                g_iconv_close( irc->oconv );
341       
[fa75134]342        g_free( irc->sendbuffer );
343        g_free( irc->readbuffer );
344       
345        g_free( irc->nick );
346        g_free( irc->user );
347        g_free( irc->host );
348        g_free( irc->realname );
349        g_free( irc->password );
350       
351        g_free( irc->myhost );
352        g_free( irc->mynick );
353       
354        g_free( irc->channel );
355       
[f9756bd]356        g_free( irc->last_target );
357       
[fa75134]358        g_free( irc );
[b7d3cc34]359       
[565a1ea]360        if( global.conf->runmode == RUNMODE_INETD ||
361            global.conf->runmode == RUNMODE_FORKDAEMON ||
362            ( global.conf->runmode == RUNMODE_DAEMON &&
363              global.listen_socket == -1 &&
364              irc_connection_list == NULL ) )
[ba9edaa]365                b_main_quit();
[b7d3cc34]366}
367
[7cad7b4]368/* USE WITH CAUTION!
369   Sets pass without checking */
370void irc_setpass (irc_t *irc, const char *pass) 
371{
[6e1fed7]372        g_free (irc->password);
[7cad7b4]373       
374        if (pass) {
375                irc->password = g_strdup (pass);
376        } else {
377                irc->password = NULL;
378        }
379}
380
[f73b969]381void irc_process( irc_t *irc )
[b7d3cc34]382{
[f9756bd]383        char **lines, *temp, **cmd;
[b7d3cc34]384        int i;
385
[de3e100]386        if( irc->readbuffer != NULL )
387        {
388                lines = irc_tokenize( irc->readbuffer );
389               
390                for( i = 0; *lines[i] != '\0'; i ++ )
391                {
[f9756bd]392                        char *conv = NULL;
[7d31002]393                       
[18ff38f]394                        /* [WvG] If the last line isn't empty, it's an incomplete line and we
395                           should wait for the rest to come in before processing it. */
[de3e100]396                        if( lines[i+1] == NULL )
397                        {
[b7d3cc34]398                                temp = g_strdup( lines[i] );
399                                g_free( irc->readbuffer );
400                                irc->readbuffer = temp;
[de3e100]401                                i ++;
[b7d3cc34]402                                break;
[e27661d]403                        }
404                       
[f9756bd]405                        if( irc->iconv != (GIConv) -1 )
[e27661d]406                        {
[f9756bd]407                                gsize bytes_read, bytes_written;
408                               
409                                conv = g_convert_with_iconv( lines[i], -1, irc->iconv,
410                                                             &bytes_read, &bytes_written, NULL );
411                               
412                                if( conv == NULL || bytes_read != strlen( lines[i] ) )
[94d52d64]413                                {
[fc0cf92]414                                        /* GLib can do strange things if things are not in the expected charset,
415                                           so let's be a little bit paranoid here: */
[94d52d64]416                                        if( irc->status & USTATUS_LOGGED_IN )
[fc0cf92]417                                        {
[43462708]418                                                irc_usermsg( irc, "Error: Charset mismatch detected. The charset "
[94d52d64]419                                                                  "setting is currently set to %s, so please make "
420                                                                  "sure your IRC client will send and accept text in "
421                                                                  "that charset, or tell BitlBee which charset to "
422                                                                  "expect by changing the charset setting. See "
423                                                                  "`help set charset' for more information. Your "
[f9756bd]424                                                                  "message was ignored.",
425                                                                  set_getstr( &irc->set, "charset" ) );
426                                               
427                                                g_free( conv );
428                                                conv = NULL;
[fc0cf92]429                                        }
430                                        else
431                                        {
432                                                irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost,
[a83442a]433                                                           "Warning: invalid characters received at login time." );
[fc0cf92]434                                               
[f9756bd]435                                                conv = g_strdup( lines[i] );
[fc0cf92]436                                                for( temp = conv; *temp; temp ++ )
437                                                        if( *temp & 0x80 )
438                                                                *temp = '?';
439                                        }
[94d52d64]440                                }
441                                lines[i] = conv;
[e27661d]442                        }
[de3e100]443                       
[e1720ce]444                        if( lines[i] && ( cmd = irc_parse_line( lines[i] ) ) )
[f9756bd]445                        {
446                                irc_exec( irc, cmd );
447                                g_free( cmd );
448                        }
[f73b969]449                       
[f9756bd]450                        g_free( conv );
[f73b969]451                       
452                        /* Shouldn't really happen, but just in case... */
453                        if( !g_slist_find( irc_connection_list, irc ) )
[de3e100]454                        {
[b7d3cc34]455                                g_free( lines );
[f73b969]456                                return;
[b7d3cc34]457                        }
458                }
[de3e100]459               
460                if( lines[i] != NULL )
461                {
462                        g_free( irc->readbuffer );
[0431ea1]463                        irc->readbuffer = NULL;
[b7d3cc34]464                }
[de3e100]465               
[b7d3cc34]466                g_free( lines );
467        }
468}
469
[e27661d]470/* Splits a long string into separate lines. The array is NULL-terminated and, unless the string
471   contains an incomplete line at the end, ends with an empty string. */
[b7d3cc34]472char **irc_tokenize( char *buffer )
473{
[18ff38f]474        int i, j, n = 3;
[b7d3cc34]475        char **lines;
476
[18ff38f]477        /* Allocate n+1 elements. */
478        lines = g_new( char *, n + 1 );
[b7d3cc34]479       
[de3e100]480        lines[0] = buffer;
[b7d3cc34]481       
[18ff38f]482        /* Split the buffer in several strings, and accept any kind of line endings,
483         * knowing that ERC on Windows may send something interesting like \r\r\n,
484         * and surely there must be clients that think just \n is enough... */
485        for( i = 0, j = 0; buffer[i] != '\0'; i ++ )
[de3e100]486        {
[18ff38f]487                if( buffer[i] == '\r' || buffer[i] == '\n' )
[de3e100]488                {
[18ff38f]489                        while( buffer[i] == '\r' || buffer[i] == '\n' )
490                                buffer[i++] = '\0';
491                       
492                        lines[++j] = buffer + i;
[de3e100]493                       
[18ff38f]494                        if( j >= n )
495                        {
496                                n *= 2;
497                                lines = g_renew( char *, lines, n + 1 );
498                        }
499
500                        if( buffer[i] == '\0' )
501                                break;
[b7d3cc34]502                }
503        }
[de3e100]504       
[18ff38f]505        /* NULL terminate our list. */ 
506        lines[++j] = NULL;
507       
508        return lines;
[b7d3cc34]509}
510
[e27661d]511/* Split an IRC-style line into little parts/arguments. */
[0431ea1]512char **irc_parse_line( char *line )
[b7d3cc34]513{
514        int i, j;
515        char **cmd;
516       
517        /* Move the line pointer to the start of the command, skipping spaces and the optional prefix. */
[de3e100]518        if( line[0] == ':' )
519        {
[e1720ce]520                for( i = 0; line[i] && line[i] != ' '; i ++ );
[de3e100]521                line = line + i;
[b7d3cc34]522        }
[de3e100]523        for( i = 0; line[i] == ' '; i ++ );
524        line = line + i;
525       
[b7d3cc34]526        /* If we're already at the end of the line, return. If not, we're going to need at least one element. */
[de3e100]527        if( line[0] == '\0')
528                return NULL;
529       
530        /* Count the number of char **cmd elements we're going to need. */
531        j = 1;
532        for( i = 0; line[i] != '\0'; i ++ )
533        {
534                if( line[i] == ' ' )
535                {
536                        j ++;
[b7d3cc34]537                       
[de3e100]538                        if( line[i+1] == ':' )
539                                break;
540                }
[b7d3cc34]541        }       
542
543        /* Allocate the space we need. */
[de3e100]544        cmd = g_new( char *, j + 1 );
545        cmd[j] = NULL;
[b7d3cc34]546       
547        /* Do the actual line splitting, format is:
548         * Input: "PRIVMSG #bitlbee :foo bar"
549         * Output: cmd[0]=="PRIVMSG", cmd[1]=="#bitlbee", cmd[2]=="foo bar", cmd[3]==NULL
550         */
551
[de3e100]552        cmd[0] = line;
553        for( i = 0, j = 0; line[i] != '\0'; i ++ )
[b7d3cc34]554        {
[de3e100]555                if( line[i] == ' ' )
[b7d3cc34]556                {
[de3e100]557                        line[i] = '\0';
558                        cmd[++j] = line + i + 1;
[b7d3cc34]559                       
[de3e100]560                        if( line[i+1] == ':' )
[b7d3cc34]561                        {
[de3e100]562                                cmd[j] ++;
[b7d3cc34]563                                break;
564                        }
565                }
566        }
567       
[de3e100]568        return cmd;
[b7d3cc34]569}
570
[e27661d]571/* Converts such an array back into a command string. Mainly used for the IPC code right now. */
[74c119d]572char *irc_build_line( char **cmd )
573{
574        int i, len;
575        char *s;
[b7d3cc34]576       
[74c119d]577        if( cmd[0] == NULL )
578                return NULL;
[b7d3cc34]579       
[74c119d]580        len = 1;
581        for( i = 0; cmd[i]; i ++ )
582                len += strlen( cmd[i] ) + 1;
583       
584        if( strchr( cmd[i-1], ' ' ) != NULL )
585                len ++;
586       
587        s = g_new0( char, len + 1 );
588        for( i = 0; cmd[i]; i ++ )
[b7d3cc34]589        {
[74c119d]590                if( cmd[i+1] == NULL && strchr( cmd[i], ' ' ) != NULL )
591                        strcat( s, ":" );
[b7d3cc34]592               
[74c119d]593                strcat( s, cmd[i] );
[b7d3cc34]594               
[74c119d]595                if( cmd[i+1] )
596                        strcat( s, " " );
[b7d3cc34]597        }
[74c119d]598        strcat( s, "\r\n" );
[b7d3cc34]599       
[74c119d]600        return s;
[b7d3cc34]601}
602
603void irc_reply( irc_t *irc, int code, char *format, ... )
604{
605        char text[IRC_MAX_LINE];
606        va_list params;
607       
608        va_start( params, format );
609        g_vsnprintf( text, IRC_MAX_LINE, format, params );
610        va_end( params );
611        irc_write( irc, ":%s %03d %s %s", irc->myhost, code, irc->nick?irc->nick:"*", text );
612       
613        return;
614}
615
616int irc_usermsg( irc_t *irc, char *format, ... )
617{
618        char text[1024];
619        va_list params;
620        char is_private = 0;
621        user_t *u;
622       
623        u = user_find( irc, irc->mynick );
[dd89a55]624        is_private = u->is_private;
[b7d3cc34]625       
626        va_start( params, format );
627        g_vsnprintf( text, sizeof( text ), format, params );
628        va_end( params );
629       
630        return( irc_msgfrom( irc, u->nick, text ) );
631}
632
633void irc_write( irc_t *irc, char *format, ... ) 
634{
635        va_list params;
636
637        va_start( params, format );
638        irc_vawrite( irc, format, params );     
639        va_end( params );
640
641        return;
642}
643
644void irc_vawrite( irc_t *irc, char *format, va_list params )
645{
646        int size;
[f9756bd]647        char line[IRC_MAX_LINE+1];
[d783e48]648               
[0356ae3]649        /* Don't try to write anything new anymore when shutting down. */
[5898ef8]650        if( irc->status & USTATUS_SHUTDOWN )
[b7d3cc34]651                return;
[d783e48]652       
[f9756bd]653        memset( line, 0, sizeof( line ) );
[d783e48]654        g_vsnprintf( line, IRC_MAX_LINE - 2, format, params );
[b7d3cc34]655        strip_newlines( line );
[f9756bd]656       
657        if( irc->oconv != (GIConv) -1 )
[d783e48]658        {
[f9756bd]659                gsize bytes_read, bytes_written;
660                char *conv;
661               
662                conv = g_convert_with_iconv( line, -1, irc->oconv,
663                                             &bytes_read, &bytes_written, NULL );
664
665                if( bytes_read == strlen( line ) )
666                        strncpy( line, conv, IRC_MAX_LINE - 2 );
[d783e48]667               
[f9756bd]668                g_free( conv );
[d783e48]669        }
[f9756bd]670        g_strlcat( line, "\r\n", IRC_MAX_LINE + 1 );
[d783e48]671       
[a0d04d6]672        if( irc->sendbuffer != NULL )
673        {
[b7d3cc34]674                size = strlen( irc->sendbuffer ) + strlen( line );
675                irc->sendbuffer = g_renew ( char, irc->sendbuffer, size + 1 );
676                strcpy( ( irc->sendbuffer + strlen( irc->sendbuffer ) ), line );
677        }
[a0d04d6]678        else
[b7d3cc34]679        {
[a0d04d6]680                irc->sendbuffer = g_strdup(line);
[b7d3cc34]681        }
682       
[a0d04d6]683        if( irc->w_watch_source_id == 0 )
[0356ae3]684        {
685                /* If the buffer is empty we can probably write, so call the write event handler
686                   immediately. If it returns TRUE, it should be called again, so add the event to
687                   the queue. If it's FALSE, we emptied the buffer and saved ourselves some work
688                   in the event queue. */
[bbb6ffb]689                /* Really can't be done as long as the code doesn't do error checking very well:
690                if( bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE ) ) */
691               
692                /* So just always do it via the event handler. */
693                irc->w_watch_source_id = b_input_add( irc->fd, GAIM_INPUT_WRITE, bitlbee_io_current_client_write, irc );
[0356ae3]694        }
[a0d04d6]695       
[b7d3cc34]696        return;
697}
698
[22d41a2]699void irc_write_all( int now, char *format, ... )
[b7d3cc34]700{
701        va_list params;
702        GSList *temp;   
[22d41a2]703       
[b7d3cc34]704        va_start( params, format );
[22d41a2]705       
[b7d3cc34]706        temp = irc_connection_list;
[22d41a2]707        while( temp != NULL )
708        {
709                irc_t *irc = temp->data;
710               
711                if( now )
712                {
713                        g_free( irc->sendbuffer );
714                        irc->sendbuffer = g_strdup( "\r\n" );
715                }
[b7d3cc34]716                irc_vawrite( temp->data, format, params );
[22d41a2]717                if( now )
718                {
[a0d04d6]719                        bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE );
[22d41a2]720                }
[b7d3cc34]721                temp = temp->next;
722        }
[22d41a2]723       
[b7d3cc34]724        va_end( params );
725        return;
726} 
727
728void irc_names( irc_t *irc, char *channel )
729{
[3f9440d]730        user_t *u;
731        char namelist[385] = "";
[0da65d5]732        struct groupchat *c = NULL;
[04026d4]733        char *ops = set_getstr( &irc->set, "ops" );
[b7d3cc34]734       
[2f13222]735        /* RFCs say there is no error reply allowed on NAMES, so when the
[b7d3cc34]736           channel is invalid, just give an empty reply. */
737       
[3f9440d]738        if( g_strcasecmp( channel, irc->channel ) == 0 )
[b7d3cc34]739        {
[3f9440d]740                for( u = irc->users; u; u = u->next ) if( u->online )
[b7d3cc34]741                {
[3f9440d]742                        if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 )
[b7d3cc34]743                        {
[3f9440d]744                                irc_reply( irc, 353, "= %s :%s", channel, namelist );
745                                *namelist = 0;
[b7d3cc34]746                        }
[3f9440d]747                       
[0da65d5]748                        if( u->ic && !u->away && set_getbool( &irc->set, "away_devoice" ) )
[3f9440d]749                                strcat( namelist, "+" );
[a93e3c8]750                        else if( ( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) == 0 ) ) ||
751                                 ( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) == 0 ) ) )
752                                strcat( namelist, "@" );
[3f9440d]753                       
754                        strcat( namelist, u->nick );
755                        strcat( namelist, " " );
[b7d3cc34]756                }
757        }
[0e7ab64]758        else if( ( c = irc_chat_by_channel( irc, channel ) ) )
[b7d3cc34]759        {
760                GList *l;
[3f9440d]761               
762                /* root and the user aren't in the channel userlist but should
763                   show up in /NAMES, so list them first: */
764                sprintf( namelist, "%s%s %s%s ", strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) ? "@" : "", irc->mynick,
765                                                 strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) ? "@" : "", irc->nick );
[b7d3cc34]766               
[0da65d5]767                for( l = c->in_room; l; l = l->next ) if( ( u = user_findhandle( c->ic, l->data ) ) )
[3f9440d]768                {
769                        if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 )
770                        {
771                                irc_reply( irc, 353, "= %s :%s", channel, namelist );
772                                *namelist = 0;
773                        }
774                       
775                        strcat( namelist, u->nick );
776                        strcat( namelist, " " );
777                }
[b7d3cc34]778        }
779       
[3f9440d]780        if( *namelist )
781                irc_reply( irc, 353, "= %s :%s", channel, namelist );
782       
[b7d3cc34]783        irc_reply( irc, 366, "%s :End of /NAMES list", channel );
784}
785
[edf9657]786int irc_check_login( irc_t *irc )
[b7d3cc34]787{
[edf9657]788        if( irc->user && irc->nick )
789        {
[3af70b0]790                if( global.conf->authmode == AUTHMODE_CLOSED && !( irc->status & USTATUS_AUTHORIZED ) )
[b7d3cc34]791                {
[edf9657]792                        irc_reply( irc, 464, ":This server is password-protected." );
793                        return 0;
[b7d3cc34]794                }
[edf9657]795                else
[b7d3cc34]796                {
[edf9657]797                        irc_login( irc );
798                        return 1;
[b7d3cc34]799                }
[edf9657]800        }
801        else
802        {
803                /* More information needed. */
804                return 0;
805        }
[b7d3cc34]806}
807
808void irc_login( irc_t *irc )
809{
810        user_t *u;
811       
812        irc_reply( irc,   1, ":Welcome to the BitlBee gateway, %s", irc->nick );
813        irc_reply( irc,   2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->myhost );
814        irc_reply( irc,   3, ":%s", IRCD_INFO );
[238f828]815        irc_reply( irc,   4, "%s %s %s %s", irc->myhost, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES );
[39f93f0]816        irc_reply( irc,   5, "PREFIX=(ov)@+ CHANTYPES=%s CHANMODES=,,,%s NICKLEN=%d NETWORK=BitlBee "
817                             "CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 :are supported by this server",
818                             CTYPES, CMODES, MAX_NICK_LENGTH - 1 );
[b7d3cc34]819        irc_motd( irc );
[2f13222]820        irc->umode[0] = '\0';
[238f828]821        irc_umode_set( irc, "+" UMODE, 1 );
[b7d3cc34]822
823        u = user_add( irc, irc->mynick );
824        u->host = g_strdup( irc->myhost );
825        u->realname = g_strdup( ROOT_FN );
826        u->online = 1;
827        u->send_handler = root_command_string;
828        u->is_private = 0; /* [SH] The channel is root's personal playground. */
829        irc_spawn( irc, u );
830       
831        u = user_add( irc, NS_NICK );
832        u->host = g_strdup( irc->myhost );
833        u->realname = g_strdup( ROOT_FN );
834        u->online = 0;
835        u->send_handler = root_command_string;
836        u->is_private = 1; /* [SH] NickServ is not in the channel, so should always /query. */
837       
838        u = user_add( irc, irc->nick );
839        u->user = g_strdup( irc->user );
840        u->host = g_strdup( irc->host );
841        u->realname = g_strdup( irc->realname );
842        u->online = 1;
843        irc_spawn( irc, u );
844       
[a199d33]845        irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\n"
846                          "If you've never used BitlBee before, please do read the help "
847                          "information using the \x02help\x02 command. Lots of FAQs are "
848                          "answered there.\n"
849                          "If you already have an account on this server, just use the "
850                          "\x02identify\x02 command to identify yourself." );
[b7d3cc34]851       
[bd9b00f]852        if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON )
[2face62]853                ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->host, irc->nick, irc->realname );
854       
[79e826a]855        irc->status |= USTATUS_LOGGED_IN;
[a199d33]856       
857        /* This is for bug #209 (use PASS to identify to NickServ). */
858        if( irc->password != NULL )
859        {
860                char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL };
861               
862                irc_setpass( irc, NULL );
863                root_command( irc, send_cmd );
864                g_free( send_cmd[1] );
865        }
[b7d3cc34]866}
867
868void irc_motd( irc_t *irc )
869{
870        int fd;
871       
872        fd = open( global.conf->motdfile, O_RDONLY );
873        if( fd == -1 )
874        {
875                irc_reply( irc, 422, ":We don't need MOTDs." );
876        }
877        else
878        {
879                char linebuf[80];       /* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */
880                char *add, max;
881                int len;
882               
883                linebuf[79] = len = 0;
884                max = sizeof( linebuf ) - 1;
885               
886                irc_reply( irc, 375, ":- %s Message Of The Day - ", irc->myhost );
887                while( read( fd, linebuf + len, 1 ) == 1 )
888                {
889                        if( linebuf[len] == '\n' || len == max )
890                        {
891                                linebuf[len] = 0;
892                                irc_reply( irc, 372, ":- %s", linebuf );
893                                len = 0;
894                        }
895                        else if( linebuf[len] == '%' )
896                        {
897                                read( fd, linebuf + len, 1 );
898                                if( linebuf[len] == 'h' )
899                                        add = irc->myhost;
900                                else if( linebuf[len] == 'v' )
901                                        add = BITLBEE_VERSION;
902                                else if( linebuf[len] == 'n' )
903                                        add = irc->nick;
904                                else
905                                        add = "%";
906                               
907                                strncpy( linebuf + len, add, max - len );
908                                while( linebuf[++len] );
909                        }
910                        else if( len < max )
911                        {
912                                len ++;
913                        }
914                }
915                irc_reply( irc, 376, ":End of MOTD" );
[d990997]916                close( fd );
[b7d3cc34]917        }
918}
919
920void irc_topic( irc_t *irc, char *channel )
921{
[50e1776]922        struct groupchat *c = irc_chat_by_channel( irc, channel );
923       
924        if( c && c->topic )
925                irc_reply( irc, 332, "%s :%s", channel, c->topic );
926        else if( g_strcasecmp( channel, irc->channel ) == 0 )
[b7d3cc34]927                irc_reply( irc, 332, "%s :%s", channel, CONTROL_TOPIC );
928        else
[50e1776]929                irc_reply( irc, 331, "%s :No topic for this channel", channel );
[b7d3cc34]930}
931
[238f828]932void irc_umode_set( irc_t *irc, char *s, int allow_priv )
[b7d3cc34]933{
[238f828]934        /* allow_priv: Set to 0 if s contains user input, 1 if you want
935           to set a "privileged" mode (+o, +R, etc). */
[b7d3cc34]936        char m[256], st = 1, *t;
937        int i;
[2f13222]938        char changes[512], *p, st2 = 2;
939        char badflag = 0;
[b7d3cc34]940       
941        memset( m, 0, sizeof( m ) );
942       
943        for( t = irc->umode; *t; t ++ )
944                m[(int)*t] = 1;
[2f13222]945
946        p = changes;
[b7d3cc34]947        for( t = s; *t; t ++ )
948        {
949                if( *t == '+' || *t == '-' )
950                        st = *t == '+';
[238f828]951                else if( st == 0 || ( strchr( UMODES, *t ) || ( allow_priv && strchr( UMODES_PRIV, *t ) ) ) )
[2f13222]952                {
953                        if( m[(int)*t] != st)
954                        {
955                                if( st != st2 )
956                                        st2 = st, *p++ = st ? '+' : '-';
957                                *p++ = *t;
958                        }
[b7d3cc34]959                        m[(int)*t] = st;
[2f13222]960                }
961                else
962                        badflag = 1;
[b7d3cc34]963        }
[2f13222]964        *p = '\0';
[b7d3cc34]965       
966        memset( irc->umode, 0, sizeof( irc->umode ) );
967       
968        for( i = 0; i < 256 && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
[238f828]969                if( m[i] )
[b7d3cc34]970                        irc->umode[strlen(irc->umode)] = i;
971       
[2f13222]972        if( badflag )
973                irc_reply( irc, 501, ":Unknown MODE flag" );
974        /* Deliberately no !user@host on the prefix here */
975        if( *changes )
976                irc_write( irc, ":%s MODE %s %s", irc->nick, irc->nick, changes );
[b7d3cc34]977}
978
979void irc_spawn( irc_t *irc, user_t *u )
980{
981        irc_join( irc, u, irc->channel );
982}
983
984void irc_join( irc_t *irc, user_t *u, char *channel )
985{
986        char *nick;
987       
988        if( ( g_strcasecmp( channel, irc->channel ) != 0 ) || user_find( irc, irc->nick ) )
989                irc_write( irc, ":%s!%s@%s JOIN :%s", u->nick, u->user, u->host, channel );
990       
991        if( nick_cmp( u->nick, irc->nick ) == 0 )
992        {
993                irc_write( irc, ":%s MODE %s +%s", irc->myhost, channel, CMODE );
994                irc_names( irc, channel );
995                irc_topic( irc, channel );
996        }
997       
998        nick = g_strdup( u->nick );
999        nick_lc( nick );
1000        if( g_hash_table_lookup( irc->watches, nick ) )
1001        {
[fc630f9]1002                irc_reply( irc, 600, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged online" );
[b7d3cc34]1003        }
1004        g_free( nick );
1005}
1006
1007void irc_part( irc_t *irc, user_t *u, char *channel )
1008{
1009        irc_write( irc, ":%s!%s@%s PART %s :%s", u->nick, u->user, u->host, channel, "" );
1010}
1011
1012void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker )
1013{
1014        irc_write( irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, kicker->host, channel, u->nick, "" );
1015}
1016
1017void irc_kill( irc_t *irc, user_t *u )
1018{
[fb62f81f]1019        char *nick, *s;
[0a3c243]1020        char reason[128];
[fb62f81f]1021       
[1186382]1022        if( u->ic && u->ic->flags & OPT_LOGGING_OUT && set_getbool( &irc->set, "simulate_netsplit" ) )
[fb62f81f]1023        {
[0da65d5]1024                if( u->ic->acc->server )
[fb62f81f]1025                        g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost,
[0da65d5]1026                                    u->ic->acc->server );
[c2fb3809]1027                else if( ( s = strchr( u->ic->acc->user, '@' ) ) )
[fb62f81f]1028                        g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost,
1029                                    s + 1 );
1030                else
1031                        g_snprintf( reason, sizeof( reason ), "%s %s.%s", irc->myhost,
[0da65d5]1032                                    u->ic->acc->prpl->name, irc->myhost );
[fb62f81f]1033               
1034                /* proto_opt might contain garbage after the : */
1035                if( ( s = strchr( reason, ':' ) ) )
1036                        *s = 0;
1037        }
1038        else
1039        {
1040                strcpy( reason, "Leaving..." );
1041        }
[b7d3cc34]1042       
[fb62f81f]1043        irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, reason );
[b7d3cc34]1044       
1045        nick = g_strdup( u->nick );
1046        nick_lc( nick );
1047        if( g_hash_table_lookup( irc->watches, nick ) )
1048        {
[fc630f9]1049                irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged offline" );
[b7d3cc34]1050        }
1051        g_free( nick );
1052}
1053
1054int irc_send( irc_t *irc, char *nick, char *s, int flags )
1055{
[0da65d5]1056        struct groupchat *c = NULL;
[b7d3cc34]1057        user_t *u = NULL;
1058       
[39f93f0]1059        if( strchr( CTYPES, *nick ) )
[b7d3cc34]1060        {
[0e7ab64]1061                if( !( c = irc_chat_by_channel( irc, nick ) ) )
[b7d3cc34]1062                {
1063                        irc_reply( irc, 403, "%s :Channel does not exist", nick );
1064                        return( 0 );
1065                }
1066        }
1067        else
1068        {
1069                u = user_find( irc, nick );
1070               
1071                if( !u )
1072                {
1073                        if( irc->is_private )
1074                                irc_reply( irc, 401, "%s :Nick does not exist", nick );
1075                        else
1076                                irc_usermsg( irc, "Nick `%s' does not exist!", nick );
1077                        return( 0 );
1078                }
1079        }
1080       
1081        if( *s == 1 && s[strlen(s)-1] == 1 )
1082        {
1083                if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 )
1084                {
1085                        if( s[7] == ' ' ) s ++;
1086                        s += 3;
1087                        *(s++) = '/';
1088                        *(s++) = 'm';
1089                        *(s++) = 'e';
1090                        *(s++) = ' ';
1091                        s -= 4;
1092                        s[strlen(s)-1] = 0;
1093                }
1094                else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 )
1095                {
1096                        u = user_find( irc, irc->mynick );
1097                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" );
1098                        return( 1 );
1099                }
1100                else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 )
1101                {
1102                        u = user_find( irc, irc->mynick );
1103                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", s );
1104                        return( 1 );
1105                }
1106                else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 )
1107                {
[0da65d5]1108                        if( u && u->ic && u->ic->acc->prpl->send_typing && strlen( s ) >= 10 )
[b7d3cc34]1109                        {
1110                                time_t current_typing_notice = time( NULL );
1111                               
1112                                if( current_typing_notice - u->last_typing_notice >= 5 )
1113                                {
[df1fb67]1114                                        u->ic->acc->prpl->send_typing( u->ic, u->handle, ( s[8] - '0' ) << 8 );
[b7d3cc34]1115                                        u->last_typing_notice = current_typing_notice;
1116                                }
1117                        }
1118                        return( 1 );
1119                }
1120                else
1121                {
1122                        irc_usermsg( irc, "Non-ACTION CTCP's aren't supported" );
1123                        return( 0 );
1124                }
1125        }
1126       
1127        if( u )
1128        {
1129                /* For the next message, we probably do have to send new notices... */
1130                u->last_typing_notice = 0;
1131                u->is_private = irc->is_private;
1132               
1133                if( u->is_private )
1134                {
1135                        if( !u->online )
1136                                irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
1137                        else if( u->away )
1138                                irc_reply( irc, 301, "%s :%s", u->nick, u->away );
1139                }
1140               
1141                if( u->send_handler )
[f73b969]1142                {
1143                        u->send_handler( irc, u, s, flags );
1144                        return 1;
1145                }
[b7d3cc34]1146        }
[0da65d5]1147        else if( c && c->ic && c->ic->acc && c->ic->acc->prpl )
[b7d3cc34]1148        {
[84b045d]1149                return( imc_chat_msg( c, s, 0 ) );
[b7d3cc34]1150        }
1151       
1152        return( 0 );
1153}
1154
[ba9edaa]1155static gboolean buddy_send_handler_delayed( gpointer data, gint fd, b_input_condition cond )
[b7d3cc34]1156{
1157        user_t *u = data;
1158       
[226fce1]1159        /* Shouldn't happen, but just to be sure. */
1160        if( u->sendbuf_len < 2 )
1161                return FALSE;
1162       
[b7d3cc34]1163        u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */
[84b045d]1164        imc_buddy_msg( u->ic, u->handle, u->sendbuf, u->sendbuf_flags );
[b7d3cc34]1165       
1166        g_free( u->sendbuf );
1167        u->sendbuf = NULL;
1168        u->sendbuf_len = 0;
1169        u->sendbuf_timer = 0;
1170        u->sendbuf_flags = 0;
1171       
[ba9edaa]1172        return FALSE;
[b7d3cc34]1173}
1174
[f73b969]1175void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags )
[b7d3cc34]1176{
[0da65d5]1177        if( !u || !u->ic ) return;
[b7d3cc34]1178       
[d5ccd83]1179        if( set_getbool( &irc->set, "buddy_sendbuffer" ) && set_getint( &irc->set, "buddy_sendbuffer_delay" ) > 0 )
[b7d3cc34]1180        {
[834ff44]1181                int delay;
1182               
[b7d3cc34]1183                if( u->sendbuf_len > 0 && u->sendbuf_flags != flags)
1184                {
[226fce1]1185                        /* Flush the buffer */
[ba9edaa]1186                        b_event_remove( u->sendbuf_timer );
1187                        buddy_send_handler_delayed( u, -1, 0 );
[b7d3cc34]1188                }
1189
1190                if( u->sendbuf_len == 0 )
1191                {
1192                        u->sendbuf_len = strlen( msg ) + 2;
[226fce1]1193                        u->sendbuf = g_new( char, u->sendbuf_len );
[b7d3cc34]1194                        u->sendbuf[0] = 0;
1195                        u->sendbuf_flags = flags;
1196                }
1197                else
1198                {
1199                        u->sendbuf_len += strlen( msg ) + 1;
[226fce1]1200                        u->sendbuf = g_renew( char, u->sendbuf, u->sendbuf_len );
[b7d3cc34]1201                }
1202               
1203                strcat( u->sendbuf, msg );
1204                strcat( u->sendbuf, "\n" );
1205               
[5c9512f]1206                delay = set_getint( &irc->set, "buddy_sendbuffer_delay" );
[834ff44]1207                if( delay <= 5 )
1208                        delay *= 1000;
1209               
[b7d3cc34]1210                if( u->sendbuf_timer > 0 )
[ba9edaa]1211                        b_event_remove( u->sendbuf_timer );
1212                u->sendbuf_timer = b_timeout_add( delay, buddy_send_handler_delayed, u );
[b7d3cc34]1213        }
1214        else
1215        {
[84b045d]1216                imc_buddy_msg( u->ic, u->handle, msg, flags );
[b7d3cc34]1217        }
1218}
1219
1220int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg )
1221{
1222        char last = 0;
1223        char *s = msg, *line = msg;
1224       
1225        /* The almighty linesplitter .. woohoo!! */
1226        while( !last )
1227        {
1228                if( *s == '\r' && *(s+1) == '\n' )
1229                        *(s++) = 0;
1230                if( *s == '\n' )
1231                {
1232                        last = s[1] == 0;
1233                        *s = 0;
1234                }
1235                else
1236                {
1237                        last = s[0] == 0;
1238                }
1239                if( *s == 0 )
1240                {
1241                        if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 )
1242                        {
1243                                irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host,
1244                                           type, to, line + 4 );
1245                        }
1246                        else
1247                        {
1248                                irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host,
[25d1be7]1249                                           type, to, prefix ? prefix : "", line );
[b7d3cc34]1250                        }
1251                        line = s + 1;
1252                }
1253                s ++;
1254        }
1255       
1256        return( 1 );
1257}
1258
1259int irc_msgfrom( irc_t *irc, char *nick, char *msg )
1260{
1261        user_t *u = user_find( irc, nick );
1262        static char *prefix = NULL;
1263       
1264        if( !u ) return( 0 );
1265        if( prefix && *prefix ) g_free( prefix );
1266       
1267        if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 )
1268        {
1269                int len = strlen( irc->nick) + 3;
1270                prefix = g_new (char, len );
[5c9512f]1271                g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( &irc->set, "to_char" ) );
[b7d3cc34]1272                prefix[len-1] = 0;
1273        }
1274        else
1275        {
1276                prefix = "";
1277        }
1278       
1279        return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) );
1280}
1281
1282int irc_noticefrom( irc_t *irc, char *nick, char *msg )
1283{
1284        user_t *u = user_find( irc, nick );
1285       
1286        if( u )
1287                return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) );
1288        else
1289                return( 0 );
1290}
1291
1292/* Returns 0 if everything seems to be okay, a number >0 when there was a
1293   timeout. The number returned is the number of seconds we received no
1294   pongs from the user. When not connected yet, we don't ping but drop the
1295   connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
[ba9edaa]1296static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond )
[b7d3cc34]1297{
1298        irc_t *irc = _irc;
1299        int rv = 0;
1300       
[3af70b0]1301        if( !( irc->status & USTATUS_LOGGED_IN ) )
[b7d3cc34]1302        {
1303                if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) )
1304                        rv = gettime() - irc->last_pong;
1305        }
1306        else
1307        {
1308                if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging )
1309                {
1310                        irc_write( irc, "PING :%s", IRC_PING_STRING );
1311                        irc->pinging = 1;
1312                }
1313                else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) )
1314                {
1315                        rv = gettime() - irc->last_pong;
1316                }
1317        }
1318       
1319        if( rv > 0 )
1320        {
[f73b969]1321                irc_abort( irc, 0, "Ping Timeout: %d seconds", rv );
[b7d3cc34]1322                return FALSE;
1323        }
1324       
1325        return TRUE;
1326}
[0e7ab64]1327
1328struct groupchat *irc_chat_by_channel( irc_t *irc, char *channel )
1329{
1330        struct groupchat *c;
1331        account_t *a;
1332       
1333        /* This finds the connection which has a conversation which belongs to this channel */
1334        for( a = irc->accounts; a; a = a->next )
1335        {
[bdda9e9]1336                if( a->ic == NULL )
1337                        continue;
1338               
1339                c = a->ic->groupchats;
1340                while( c )
1341                {
1342                        if( c->channel && g_strcasecmp( c->channel, channel ) == 0 )
1343                                return c;
1344                       
1345                        c = c->next;
1346                }
[0e7ab64]1347        }
1348       
1349        return NULL;
1350}
Note: See TracBrowser for help on using the repository browser.