source: irc.c @ a93e3c8

Last change on this file since a93e3c8 was a93e3c8, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-07-09T10:54:45Z

Fixed irc_names() (forgot to add @s for user/root in the new version).

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