source: irc.c @ a199d33

Last change on this file since a199d33 was a199d33, checked in by Wilmer van der Gaast <wilmer@…>, at 2008-03-29T22:19:17Z

Closing bug #209: The PASS command can now be used to identify yourself
to BitlBee. The advantage: No more messing with NickServ hooks. Just set
a server password.

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