source: irc.c @ 814aa52

Last change on this file since 814aa52 was 814aa52, checked in by Sven Moritz Hallberg <pesco@…>, at 2010-06-03T11:00:45Z

merge in bitlbee 1.2.6

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