source: irc.c @ c735200

Last change on this file since c735200 was 85693e6, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-03-21T16:17:24Z

Merging in killerbee stuff (just file transfers and maybe a few things from
mainline). Once I add ft support glue to protocols/purple/ I guess this will
all go into killerbee.

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