source: irc.c @ bb09b3c

Last change on this file since bb09b3c was bb09b3c, checked in by Sven Moritz Hallberg <pesco@…>, at 2010-06-03T21:13:57Z

merge in bitlbee 1.2.7

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