source: irc.c @ fc0cf92

Last change on this file since fc0cf92 was fc0cf92, checked in by Wilmer van der Gaast <wilmer@…>, at 2008-01-13T00:15:12Z

Different handling of charset mismatches before login time. Ignoring a
USER command because of encoding issues isn't too great, so let's simply
replace them. The information isn't really used anywhere anyway.

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