Debian package changelog entry.
[bitlbee.git] / irc.c
blobaa8bc140bc77e23ae91bda9ddf16f3d40ab25ef2
1 /********************************************************************\
2 * BitlBee -- An IRC to other IM-networks gateway *
3 * *
4 * Copyright 2002-2004 Wilmer van der Gaast and others *
5 \********************************************************************/
7 /* The big hairy IRCd part of the project */
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.
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.
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
26 #define BITLBEE_CORE
27 #include "bitlbee.h"
28 #include "sock.h"
29 #include "crypting.h"
30 #include "ipc.h"
32 static gboolean irc_userping( gpointer _irc, int fd, b_input_condition cond );
34 GSList *irc_connection_list = NULL;
36 static char *set_eval_password( set_t *set, char *value )
38 irc_t *irc = set->data;
40 if( irc->status & USTATUS_IDENTIFIED && value )
42 irc_setpass( irc, value );
43 return NULL;
45 else
47 return SET_INVALID;
51 static char *set_eval_charset( set_t *set, char *value )
53 irc_t *irc = set->data;
54 char *test;
55 gsize test_bytes = 0;
56 GIConv ic, oc;
58 if( g_strcasecmp( value, "none" ) == 0 )
59 value = g_strdup( "utf-8" );
61 if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 )
63 return NULL;
66 /* Do a test iconv to see if the user picked an IRC-compatible
67 charset (for example utf-16 goes *horribly* wrong). */
68 if( ( test = g_convert_with_iconv( " ", 1, oc, NULL, &test_bytes, NULL ) ) == NULL ||
69 test_bytes > 1 )
71 g_free( test );
72 g_iconv_close( oc );
73 irc_usermsg( irc, "Unsupported character set: The IRC protocol "
74 "only supports 8-bit character sets." );
75 return NULL;
77 g_free( test );
79 if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 )
81 g_iconv_close( oc );
82 return NULL;
85 if( irc->iconv != (GIConv) -1 )
86 g_iconv_close( irc->iconv );
87 if( irc->oconv != (GIConv) -1 )
88 g_iconv_close( irc->oconv );
90 irc->iconv = ic;
91 irc->oconv = oc;
93 return value;
96 static char *set_eval_away_status( set_t *set, char *value )
98 irc_t *irc = set->data;
99 account_t *a;
101 g_free( set->value );
102 set->value = g_strdup( value );
104 for( a = irc->accounts; a; a = a->next )
106 struct im_connection *ic = a->ic;
108 if( ic && ic->flags & OPT_LOGGED_IN )
109 imc_away_send_update( ic );
112 return value;
115 irc_t *irc_new( int fd )
117 irc_t *irc;
118 struct sockaddr_storage sock;
119 socklen_t socklen = sizeof( sock );
120 set_t *s;
122 irc = g_new0( irc_t, 1 );
124 irc->fd = fd;
125 sock_make_nonblocking( irc->fd );
127 irc->r_watch_source_id = b_input_add( irc->fd, GAIM_INPUT_READ, bitlbee_io_current_client_read, irc );
129 irc->status = USTATUS_OFFLINE;
130 irc->last_pong = gettime();
132 irc->userhash = g_hash_table_new( g_str_hash, g_str_equal );
133 irc->watches = g_hash_table_new( g_str_hash, g_str_equal );
135 strcpy( irc->umode, UMODE );
136 irc->mynick = g_strdup( ROOT_NICK );
137 irc->channel = g_strdup( ROOT_CHAN );
139 irc->iconv = (GIConv) -1;
140 irc->oconv = (GIConv) -1;
142 if( global.conf->hostname )
144 irc->myhost = g_strdup( global.conf->hostname );
146 else if( getsockname( irc->fd, (struct sockaddr*) &sock, &socklen ) == 0 )
148 char buf[NI_MAXHOST+1];
150 if( getnameinfo( (struct sockaddr *) &sock, socklen, buf,
151 NI_MAXHOST, NULL, 0, 0 ) == 0 )
153 irc->myhost = g_strdup( ipv6_unwrap( buf ) );
157 if( getpeername( irc->fd, (struct sockaddr*) &sock, &socklen ) == 0 )
159 char buf[NI_MAXHOST+1];
161 if( getnameinfo( (struct sockaddr *)&sock, socklen, buf,
162 NI_MAXHOST, NULL, 0, 0 ) == 0 )
164 irc->host = g_strdup( ipv6_unwrap( buf ) );
168 if( irc->host == NULL )
169 irc->host = g_strdup( "localhost.localdomain" );
170 if( irc->myhost == NULL )
171 irc->myhost = g_strdup( "localhost.localdomain" );
173 if( global.conf->ping_interval > 0 && global.conf->ping_timeout > 0 )
174 irc->ping_source_id = b_timeout_add( global.conf->ping_interval * 1000, irc_userping, irc );
176 irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost, "BitlBee-IRCd initialized, please go on" );
178 irc_connection_list = g_slist_append( irc_connection_list, irc );
180 s = set_add( &irc->set, "away", NULL, set_eval_away_status, irc );
181 s->flags |= SET_NULL_OK;
182 s = set_add( &irc->set, "away_devoice", "true", set_eval_away_devoice, irc );
183 s = set_add( &irc->set, "auto_connect", "true", set_eval_bool, irc );
184 s = set_add( &irc->set, "auto_reconnect", "true", set_eval_bool, irc );
185 s = set_add( &irc->set, "auto_reconnect_delay", "5*3<900", set_eval_account_reconnect_delay, irc );
186 s = set_add( &irc->set, "buddy_sendbuffer", "false", set_eval_bool, irc );
187 s = set_add( &irc->set, "buddy_sendbuffer_delay", "200", set_eval_int, irc );
188 s = set_add( &irc->set, "charset", "utf-8", set_eval_charset, irc );
189 s = set_add( &irc->set, "control_channel", irc->channel, set_eval_control_channel, irc );
190 s = set_add( &irc->set, "debug", "false", set_eval_bool, irc );
191 s = set_add( &irc->set, "default_target", "root", NULL, irc );
192 s = set_add( &irc->set, "display_namechanges", "false", set_eval_bool, irc );
193 s = set_add( &irc->set, "display_timestamps", "true", set_eval_bool, irc );
194 s = set_add( &irc->set, "handle_unknown", "root", NULL, irc );
195 s = set_add( &irc->set, "lcnicks", "true", set_eval_bool, irc );
196 s = set_add( &irc->set, "ops", "both", set_eval_ops, irc );
197 s = set_add( &irc->set, "password", NULL, set_eval_password, irc );
198 s->flags |= SET_NULL_OK;
199 s = set_add( &irc->set, "private", "true", set_eval_bool, irc );
200 s = set_add( &irc->set, "query_order", "lifo", NULL, irc );
201 s = set_add( &irc->set, "root_nick", irc->mynick, set_eval_root_nick, irc );
202 s = set_add( &irc->set, "save_on_quit", "true", set_eval_bool, irc );
203 s = set_add( &irc->set, "simulate_netsplit", "true", set_eval_bool, irc );
204 s = set_add( &irc->set, "status", NULL, set_eval_away_status, irc );
205 s->flags |= SET_NULL_OK;
206 s = set_add( &irc->set, "strip_html", "true", NULL, irc );
207 s = set_add( &irc->set, "timezone", "local", set_eval_timezone, irc );
208 s = set_add( &irc->set, "to_char", ": ", set_eval_to_char, irc );
209 s = set_add( &irc->set, "typing_notice", "false", set_eval_bool, irc );
211 conf_loaddefaults( irc );
213 /* Evaluator sets the iconv/oconv structures. */
214 set_eval_charset( set_find( &irc->set, "charset" ), set_getstr( &irc->set, "charset" ) );
216 return( irc );
219 /* immed=1 makes this function pretty much equal to irc_free(), except that
220 this one will "log". In case the connection is already broken and we
221 shouldn't try to write to it. */
222 void irc_abort( irc_t *irc, int immed, char *format, ... )
224 if( format != NULL )
226 va_list params;
227 char *reason;
229 va_start( params, format );
230 reason = g_strdup_vprintf( format, params );
231 va_end( params );
233 if( !immed )
234 irc_write( irc, "ERROR :Closing link: %s", reason );
236 ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
237 irc->nick ? irc->nick : "(NONE)", irc->host, reason );
239 g_free( reason );
241 else
243 if( !immed )
244 irc_write( irc, "ERROR :Closing link" );
246 ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
247 irc->nick ? irc->nick : "(NONE)", irc->host, "No reason given" );
250 irc->status |= USTATUS_SHUTDOWN;
251 if( irc->sendbuffer && !immed )
253 /* Set up a timeout event that should shut down the connection
254 in a second, just in case ..._write doesn't do it first. */
256 b_event_remove( irc->r_watch_source_id );
257 irc->r_watch_source_id = 0;
259 b_event_remove( irc->ping_source_id );
260 irc->ping_source_id = b_timeout_add( 1000, (b_event_handler) irc_free, irc );
262 else
264 irc_free( irc );
268 static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data )
270 g_free( key );
272 return( TRUE );
275 /* Because we have no garbage collection, this is quite annoying */
276 void irc_free( irc_t * irc )
278 user_t *user, *usertmp;
280 log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd );
282 if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->set, "save_on_quit" ) )
283 if( storage_save( irc, NULL, TRUE ) != STORAGE_OK )
284 irc_usermsg( irc, "Error while saving settings!" );
286 irc_connection_list = g_slist_remove( irc_connection_list, irc );
288 while( irc->accounts )
290 if( irc->accounts->ic )
291 imc_logout( irc->accounts->ic, FALSE );
292 else if( irc->accounts->reconnect )
293 cancel_auto_reconnect( irc->accounts );
295 if( irc->accounts->ic == NULL )
296 account_del( irc, irc->accounts );
297 else
298 /* Nasty hack, but account_del() doesn't work in this
299 case and we don't want infinite loops, do we? ;-) */
300 irc->accounts = irc->accounts->next;
303 while( irc->queries != NULL )
304 query_del( irc, irc->queries );
306 while( irc->set )
307 set_del( &irc->set, irc->set->key );
309 if (irc->users != NULL)
311 user = irc->users;
312 while( user != NULL )
314 g_free( user->nick );
315 g_free( user->away );
316 g_free( user->handle );
317 if( user->user != user->nick ) g_free( user->user );
318 if( user->host != user->nick ) g_free( user->host );
319 if( user->realname != user->nick ) g_free( user->realname );
320 b_event_remove( user->sendbuf_timer );
322 usertmp = user;
323 user = user->next;
324 g_free( usertmp );
328 if( irc->ping_source_id > 0 )
329 b_event_remove( irc->ping_source_id );
330 if( irc->r_watch_source_id > 0 )
331 b_event_remove( irc->r_watch_source_id );
332 if( irc->w_watch_source_id > 0 )
333 b_event_remove( irc->w_watch_source_id );
335 closesocket( irc->fd );
336 irc->fd = -1;
338 g_hash_table_foreach_remove( irc->userhash, irc_free_hashkey, NULL );
339 g_hash_table_destroy( irc->userhash );
341 g_hash_table_foreach_remove( irc->watches, irc_free_hashkey, NULL );
342 g_hash_table_destroy( irc->watches );
344 if( irc->iconv != (GIConv) -1 )
345 g_iconv_close( irc->iconv );
346 if( irc->oconv != (GIConv) -1 )
347 g_iconv_close( irc->oconv );
349 g_free( irc->sendbuffer );
350 g_free( irc->readbuffer );
352 g_free( irc->nick );
353 g_free( irc->user );
354 g_free( irc->host );
355 g_free( irc->realname );
356 g_free( irc->password );
358 g_free( irc->myhost );
359 g_free( irc->mynick );
361 g_free( irc->channel );
363 g_free( irc->last_target );
365 g_free( irc );
367 if( global.conf->runmode == RUNMODE_INETD ||
368 global.conf->runmode == RUNMODE_FORKDAEMON ||
369 ( global.conf->runmode == RUNMODE_DAEMON &&
370 global.listen_socket == -1 &&
371 irc_connection_list == NULL ) )
372 b_main_quit();
375 /* USE WITH CAUTION!
376 Sets pass without checking */
377 void irc_setpass (irc_t *irc, const char *pass)
379 g_free (irc->password);
381 if (pass) {
382 irc->password = g_strdup (pass);
383 } else {
384 irc->password = NULL;
388 void irc_process( irc_t *irc )
390 char **lines, *temp, **cmd;
391 int i;
393 if( irc->readbuffer != NULL )
395 lines = irc_tokenize( irc->readbuffer );
397 for( i = 0; *lines[i] != '\0'; i ++ )
399 char *conv = NULL;
401 /* [WvG] If the last line isn't empty, it's an incomplete line and we
402 should wait for the rest to come in before processing it. */
403 if( lines[i+1] == NULL )
405 temp = g_strdup( lines[i] );
406 g_free( irc->readbuffer );
407 irc->readbuffer = temp;
408 i ++;
409 break;
412 if( irc->iconv != (GIConv) -1 )
414 gsize bytes_read, bytes_written;
416 conv = g_convert_with_iconv( lines[i], -1, irc->iconv,
417 &bytes_read, &bytes_written, NULL );
419 if( conv == NULL || bytes_read != strlen( lines[i] ) )
421 /* GLib can do strange things if things are not in the expected charset,
422 so let's be a little bit paranoid here: */
423 if( irc->status & USTATUS_LOGGED_IN )
425 irc_usermsg( irc, "Error: Charset mismatch detected. The charset "
426 "setting is currently set to %s, so please make "
427 "sure your IRC client will send and accept text in "
428 "that charset, or tell BitlBee which charset to "
429 "expect by changing the charset setting. See "
430 "`help set charset' for more information. Your "
431 "message was ignored.",
432 set_getstr( &irc->set, "charset" ) );
434 g_free( conv );
435 conv = NULL;
437 else
439 irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost,
440 "Warning: invalid characters received at login time." );
442 conv = g_strdup( lines[i] );
443 for( temp = conv; *temp; temp ++ )
444 if( *temp & 0x80 )
445 *temp = '?';
448 lines[i] = conv;
451 if( lines[i] && ( cmd = irc_parse_line( lines[i] ) ) )
453 irc_exec( irc, cmd );
454 g_free( cmd );
457 g_free( conv );
459 /* Shouldn't really happen, but just in case... */
460 if( !g_slist_find( irc_connection_list, irc ) )
462 g_free( lines );
463 return;
467 if( lines[i] != NULL )
469 g_free( irc->readbuffer );
470 irc->readbuffer = NULL;
473 g_free( lines );
477 /* Splits a long string into separate lines. The array is NULL-terminated and, unless the string
478 contains an incomplete line at the end, ends with an empty string. */
479 char **irc_tokenize( char *buffer )
481 int i, j, n = 3;
482 char **lines;
484 /* Allocate n+1 elements. */
485 lines = g_new( char *, n + 1 );
487 lines[0] = buffer;
489 /* Split the buffer in several strings, and accept any kind of line endings,
490 * knowing that ERC on Windows may send something interesting like \r\r\n,
491 * and surely there must be clients that think just \n is enough... */
492 for( i = 0, j = 0; buffer[i] != '\0'; i ++ )
494 if( buffer[i] == '\r' || buffer[i] == '\n' )
496 while( buffer[i] == '\r' || buffer[i] == '\n' )
497 buffer[i++] = '\0';
499 lines[++j] = buffer + i;
501 if( j >= n )
503 n *= 2;
504 lines = g_renew( char *, lines, n + 1 );
507 if( buffer[i] == '\0' )
508 break;
512 /* NULL terminate our list. */
513 lines[++j] = NULL;
515 return lines;
518 /* Split an IRC-style line into little parts/arguments. */
519 char **irc_parse_line( char *line )
521 int i, j;
522 char **cmd;
524 /* Move the line pointer to the start of the command, skipping spaces and the optional prefix. */
525 if( line[0] == ':' )
527 for( i = 0; line[i] && line[i] != ' '; i ++ );
528 line = line + i;
530 for( i = 0; line[i] == ' '; i ++ );
531 line = line + i;
533 /* If we're already at the end of the line, return. If not, we're going to need at least one element. */
534 if( line[0] == '\0')
535 return NULL;
537 /* Count the number of char **cmd elements we're going to need. */
538 j = 1;
539 for( i = 0; line[i] != '\0'; i ++ )
541 if( line[i] == ' ' )
543 j ++;
545 if( line[i+1] == ':' )
546 break;
550 /* Allocate the space we need. */
551 cmd = g_new( char *, j + 1 );
552 cmd[j] = NULL;
554 /* Do the actual line splitting, format is:
555 * Input: "PRIVMSG #bitlbee :foo bar"
556 * Output: cmd[0]=="PRIVMSG", cmd[1]=="#bitlbee", cmd[2]=="foo bar", cmd[3]==NULL
559 cmd[0] = line;
560 for( i = 0, j = 0; line[i] != '\0'; i ++ )
562 if( line[i] == ' ' )
564 line[i] = '\0';
565 cmd[++j] = line + i + 1;
567 if( line[i+1] == ':' )
569 cmd[j] ++;
570 break;
575 return cmd;
578 /* Converts such an array back into a command string. Mainly used for the IPC code right now. */
579 char *irc_build_line( char **cmd )
581 int i, len;
582 char *s;
584 if( cmd[0] == NULL )
585 return NULL;
587 len = 1;
588 for( i = 0; cmd[i]; i ++ )
589 len += strlen( cmd[i] ) + 1;
591 if( strchr( cmd[i-1], ' ' ) != NULL )
592 len ++;
594 s = g_new0( char, len + 1 );
595 for( i = 0; cmd[i]; i ++ )
597 if( cmd[i+1] == NULL && strchr( cmd[i], ' ' ) != NULL )
598 strcat( s, ":" );
600 strcat( s, cmd[i] );
602 if( cmd[i+1] )
603 strcat( s, " " );
605 strcat( s, "\r\n" );
607 return s;
610 void irc_reply( irc_t *irc, int code, char *format, ... )
612 char text[IRC_MAX_LINE];
613 va_list params;
615 va_start( params, format );
616 g_vsnprintf( text, IRC_MAX_LINE, format, params );
617 va_end( params );
618 irc_write( irc, ":%s %03d %s %s", irc->myhost, code, irc->nick?irc->nick:"*", text );
620 return;
623 int irc_usermsg( irc_t *irc, char *format, ... )
625 char text[1024];
626 va_list params;
627 char is_private = 0;
628 user_t *u;
630 u = user_find( irc, irc->mynick );
631 is_private = u->is_private;
633 va_start( params, format );
634 g_vsnprintf( text, sizeof( text ), format, params );
635 va_end( params );
637 return( irc_msgfrom( irc, u->nick, text ) );
640 void irc_write( irc_t *irc, char *format, ... )
642 va_list params;
644 va_start( params, format );
645 irc_vawrite( irc, format, params );
646 va_end( params );
648 return;
651 void irc_vawrite( irc_t *irc, char *format, va_list params )
653 int size;
654 char line[IRC_MAX_LINE+1];
656 /* Don't try to write anything new anymore when shutting down. */
657 if( irc->status & USTATUS_SHUTDOWN )
658 return;
660 memset( line, 0, sizeof( line ) );
661 g_vsnprintf( line, IRC_MAX_LINE - 2, format, params );
662 strip_newlines( line );
664 if( irc->oconv != (GIConv) -1 )
666 gsize bytes_read, bytes_written;
667 char *conv;
669 conv = g_convert_with_iconv( line, -1, irc->oconv,
670 &bytes_read, &bytes_written, NULL );
672 if( bytes_read == strlen( line ) )
673 strncpy( line, conv, IRC_MAX_LINE - 2 );
675 g_free( conv );
677 g_strlcat( line, "\r\n", IRC_MAX_LINE + 1 );
679 if( irc->sendbuffer != NULL )
681 size = strlen( irc->sendbuffer ) + strlen( line );
682 irc->sendbuffer = g_renew ( char, irc->sendbuffer, size + 1 );
683 strcpy( ( irc->sendbuffer + strlen( irc->sendbuffer ) ), line );
685 else
687 irc->sendbuffer = g_strdup(line);
690 if( irc->w_watch_source_id == 0 )
692 /* If the buffer is empty we can probably write, so call the write event handler
693 immediately. If it returns TRUE, it should be called again, so add the event to
694 the queue. If it's FALSE, we emptied the buffer and saved ourselves some work
695 in the event queue. */
696 /* Really can't be done as long as the code doesn't do error checking very well:
697 if( bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE ) ) */
699 /* So just always do it via the event handler. */
700 irc->w_watch_source_id = b_input_add( irc->fd, GAIM_INPUT_WRITE, bitlbee_io_current_client_write, irc );
703 return;
706 void irc_write_all( int now, char *format, ... )
708 va_list params;
709 GSList *temp;
711 va_start( params, format );
713 temp = irc_connection_list;
714 while( temp != NULL )
716 irc_t *irc = temp->data;
718 if( now )
720 g_free( irc->sendbuffer );
721 irc->sendbuffer = g_strdup( "\r\n" );
723 irc_vawrite( temp->data, format, params );
724 if( now )
726 bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE );
728 temp = temp->next;
731 va_end( params );
732 return;
735 void irc_names( irc_t *irc, char *channel )
737 user_t *u;
738 char namelist[385] = "";
739 struct groupchat *c = NULL;
740 char *ops = set_getstr( &irc->set, "ops" );
742 /* RFCs say there is no error reply allowed on NAMES, so when the
743 channel is invalid, just give an empty reply. */
745 if( g_strcasecmp( channel, irc->channel ) == 0 )
747 for( u = irc->users; u; u = u->next ) if( u->online )
749 if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 )
751 irc_reply( irc, 353, "= %s :%s", channel, namelist );
752 *namelist = 0;
755 if( u->ic && !u->away && set_getbool( &irc->set, "away_devoice" ) )
756 strcat( namelist, "+" );
757 else if( ( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) == 0 ) ) ||
758 ( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) == 0 ) ) )
759 strcat( namelist, "@" );
761 strcat( namelist, u->nick );
762 strcat( namelist, " " );
765 else if( ( c = irc_chat_by_channel( irc, channel ) ) )
767 GList *l;
769 /* root and the user aren't in the channel userlist but should
770 show up in /NAMES, so list them first: */
771 sprintf( namelist, "%s%s %s%s ", strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) ? "@" : "", irc->mynick,
772 strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) ? "@" : "", irc->nick );
774 for( l = c->in_room; l; l = l->next ) if( ( u = user_findhandle( c->ic, l->data ) ) )
776 if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 )
778 irc_reply( irc, 353, "= %s :%s", channel, namelist );
779 *namelist = 0;
782 strcat( namelist, u->nick );
783 strcat( namelist, " " );
787 if( *namelist )
788 irc_reply( irc, 353, "= %s :%s", channel, namelist );
790 irc_reply( irc, 366, "%s :End of /NAMES list", channel );
793 int irc_check_login( irc_t *irc )
795 if( irc->user && irc->nick )
797 if( global.conf->authmode == AUTHMODE_CLOSED && !( irc->status & USTATUS_AUTHORIZED ) )
799 irc_reply( irc, 464, ":This server is password-protected." );
800 return 0;
802 else
804 irc_login( irc );
805 return 1;
808 else
810 /* More information needed. */
811 return 0;
815 void irc_login( irc_t *irc )
817 user_t *u;
819 irc_reply( irc, 1, ":Welcome to the BitlBee gateway, %s", irc->nick );
820 irc_reply( irc, 2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->myhost );
821 irc_reply( irc, 3, ":%s", IRCD_INFO );
822 irc_reply( irc, 4, "%s %s %s %s", irc->myhost, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES );
823 irc_reply( irc, 5, "PREFIX=(ov)@+ CHANTYPES=%s CHANMODES=,,,%s NICKLEN=%d NETWORK=BitlBee "
824 "CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 :are supported by this server",
825 CTYPES, CMODES, MAX_NICK_LENGTH - 1 );
826 irc_motd( irc );
827 irc->umode[0] = '\0';
828 irc_umode_set( irc, "+" UMODE, 1 );
830 u = user_add( irc, irc->mynick );
831 u->host = g_strdup( irc->myhost );
832 u->realname = g_strdup( ROOT_FN );
833 u->online = 1;
834 u->send_handler = root_command_string;
835 u->is_private = 0; /* [SH] The channel is root's personal playground. */
836 irc_spawn( irc, u );
838 u = user_add( irc, NS_NICK );
839 u->host = g_strdup( irc->myhost );
840 u->realname = g_strdup( ROOT_FN );
841 u->online = 0;
842 u->send_handler = root_command_string;
843 u->is_private = 1; /* [SH] NickServ is not in the channel, so should always /query. */
845 u = user_add( irc, irc->nick );
846 u->user = g_strdup( irc->user );
847 u->host = g_strdup( irc->host );
848 u->realname = g_strdup( irc->realname );
849 u->online = 1;
850 irc_spawn( irc, u );
852 irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\n"
853 "If you've never used BitlBee before, please do read the help "
854 "information using the \x02help\x02 command. Lots of FAQs are "
855 "answered there.\n"
856 "If you already have an account on this server, just use the "
857 "\x02identify\x02 command to identify yourself." );
859 if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON )
860 ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->host, irc->nick, irc->realname );
862 irc->status |= USTATUS_LOGGED_IN;
864 /* This is for bug #209 (use PASS to identify to NickServ). */
865 if( irc->password != NULL )
867 char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL };
869 irc_setpass( irc, NULL );
870 root_command( irc, send_cmd );
871 g_free( send_cmd[1] );
875 void irc_motd( irc_t *irc )
877 int fd;
879 fd = open( global.conf->motdfile, O_RDONLY );
880 if( fd == -1 )
882 irc_reply( irc, 422, ":We don't need MOTDs." );
884 else
886 char linebuf[80]; /* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */
887 char *add, max;
888 int len;
890 linebuf[79] = len = 0;
891 max = sizeof( linebuf ) - 1;
893 irc_reply( irc, 375, ":- %s Message Of The Day - ", irc->myhost );
894 while( read( fd, linebuf + len, 1 ) == 1 )
896 if( linebuf[len] == '\n' || len == max )
898 linebuf[len] = 0;
899 irc_reply( irc, 372, ":- %s", linebuf );
900 len = 0;
902 else if( linebuf[len] == '%' )
904 read( fd, linebuf + len, 1 );
905 if( linebuf[len] == 'h' )
906 add = irc->myhost;
907 else if( linebuf[len] == 'v' )
908 add = BITLBEE_VERSION;
909 else if( linebuf[len] == 'n' )
910 add = irc->nick;
911 else
912 add = "%";
914 strncpy( linebuf + len, add, max - len );
915 while( linebuf[++len] );
917 else if( len < max )
919 len ++;
922 irc_reply( irc, 376, ":End of MOTD" );
923 close( fd );
927 void irc_topic( irc_t *irc, char *channel )
929 struct groupchat *c = irc_chat_by_channel( irc, channel );
931 if( c && c->topic )
932 irc_reply( irc, 332, "%s :%s", channel, c->topic );
933 else if( g_strcasecmp( channel, irc->channel ) == 0 )
934 irc_reply( irc, 332, "%s :%s", channel, CONTROL_TOPIC );
935 else
936 irc_reply( irc, 331, "%s :No topic for this channel", channel );
939 void irc_umode_set( irc_t *irc, char *s, int allow_priv )
941 /* allow_priv: Set to 0 if s contains user input, 1 if you want
942 to set a "privileged" mode (+o, +R, etc). */
943 char m[256], st = 1, *t;
944 int i;
945 char changes[512], *p, st2 = 2;
946 char badflag = 0;
948 memset( m, 0, sizeof( m ) );
950 for( t = irc->umode; *t; t ++ )
951 m[(int)*t] = 1;
953 p = changes;
954 for( t = s; *t; t ++ )
956 if( *t == '+' || *t == '-' )
957 st = *t == '+';
958 else if( st == 0 || ( strchr( UMODES, *t ) || ( allow_priv && strchr( UMODES_PRIV, *t ) ) ) )
960 if( m[(int)*t] != st)
962 if( st != st2 )
963 st2 = st, *p++ = st ? '+' : '-';
964 *p++ = *t;
966 m[(int)*t] = st;
968 else
969 badflag = 1;
971 *p = '\0';
973 memset( irc->umode, 0, sizeof( irc->umode ) );
975 for( i = 0; i < 256 && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
976 if( m[i] )
977 irc->umode[strlen(irc->umode)] = i;
979 if( badflag )
980 irc_reply( irc, 501, ":Unknown MODE flag" );
981 /* Deliberately no !user@host on the prefix here */
982 if( *changes )
983 irc_write( irc, ":%s MODE %s %s", irc->nick, irc->nick, changes );
986 void irc_spawn( irc_t *irc, user_t *u )
988 irc_join( irc, u, irc->channel );
991 void irc_join( irc_t *irc, user_t *u, char *channel )
993 char *nick;
995 if( ( g_strcasecmp( channel, irc->channel ) != 0 ) || user_find( irc, irc->nick ) )
996 irc_write( irc, ":%s!%s@%s JOIN :%s", u->nick, u->user, u->host, channel );
998 if( nick_cmp( u->nick, irc->nick ) == 0 )
1000 irc_write( irc, ":%s MODE %s +%s", irc->myhost, channel, CMODE );
1001 irc_names( irc, channel );
1002 irc_topic( irc, channel );
1005 nick = g_strdup( u->nick );
1006 nick_lc( nick );
1007 if( g_hash_table_lookup( irc->watches, nick ) )
1009 irc_reply( irc, 600, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged online" );
1011 g_free( nick );
1014 void irc_part( irc_t *irc, user_t *u, char *channel )
1016 irc_write( irc, ":%s!%s@%s PART %s :%s", u->nick, u->user, u->host, channel, "" );
1019 void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker )
1021 irc_write( irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, kicker->host, channel, u->nick, "" );
1024 void irc_kill( irc_t *irc, user_t *u )
1026 char *nick, *s;
1027 char reason[128];
1029 if( u->ic && u->ic->flags & OPT_LOGGING_OUT && set_getbool( &irc->set, "simulate_netsplit" ) )
1031 if( u->ic->acc->server )
1032 g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost,
1033 u->ic->acc->server );
1034 else if( ( s = strchr( u->ic->acc->user, '@' ) ) )
1035 g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost,
1036 s + 1 );
1037 else
1038 g_snprintf( reason, sizeof( reason ), "%s %s.%s", irc->myhost,
1039 u->ic->acc->prpl->name, irc->myhost );
1041 /* proto_opt might contain garbage after the : */
1042 if( ( s = strchr( reason, ':' ) ) )
1043 *s = 0;
1045 else
1047 strcpy( reason, "Leaving..." );
1050 irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, reason );
1052 nick = g_strdup( u->nick );
1053 nick_lc( nick );
1054 if( g_hash_table_lookup( irc->watches, nick ) )
1056 irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged offline" );
1058 g_free( nick );
1061 int irc_send( irc_t *irc, char *nick, char *s, int flags )
1063 struct groupchat *c = NULL;
1064 user_t *u = NULL;
1066 if( strchr( CTYPES, *nick ) )
1068 if( !( c = irc_chat_by_channel( irc, nick ) ) )
1070 irc_reply( irc, 403, "%s :Channel does not exist", nick );
1071 return( 0 );
1074 else
1076 u = user_find( irc, nick );
1078 if( !u )
1080 if( irc->is_private )
1081 irc_reply( irc, 401, "%s :Nick does not exist", nick );
1082 else
1083 irc_usermsg( irc, "Nick `%s' does not exist!", nick );
1084 return( 0 );
1088 if( *s == 1 && s[strlen(s)-1] == 1 )
1090 if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 )
1092 if( s[7] == ' ' ) s ++;
1093 s += 3;
1094 *(s++) = '/';
1095 *(s++) = 'm';
1096 *(s++) = 'e';
1097 *(s++) = ' ';
1098 s -= 4;
1099 s[strlen(s)-1] = 0;
1101 else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 )
1103 u = user_find( irc, irc->mynick );
1104 irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" );
1105 return( 1 );
1107 else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 )
1109 u = user_find( irc, irc->mynick );
1110 irc_privmsg( irc, u, "NOTICE", irc->nick, "", s );
1111 return( 1 );
1113 else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 )
1115 if( u && u->ic && u->ic->acc->prpl->send_typing && strlen( s ) >= 10 )
1117 time_t current_typing_notice = time( NULL );
1119 if( current_typing_notice - u->last_typing_notice >= 5 )
1121 u->ic->acc->prpl->send_typing( u->ic, u->handle, ( s[8] - '0' ) << 8 );
1122 u->last_typing_notice = current_typing_notice;
1125 return( 1 );
1127 else
1129 irc_usermsg( irc, "Non-ACTION CTCP's aren't supported" );
1130 return( 0 );
1134 if( u )
1136 /* For the next message, we probably do have to send new notices... */
1137 u->last_typing_notice = 0;
1138 u->is_private = irc->is_private;
1140 if( u->is_private )
1142 if( !u->online )
1143 irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
1144 else if( u->away )
1145 irc_reply( irc, 301, "%s :%s", u->nick, u->away );
1148 if( u->send_handler )
1150 u->send_handler( irc, u, s, flags );
1151 return 1;
1154 else if( c && c->ic && c->ic->acc && c->ic->acc->prpl )
1156 return( imc_chat_msg( c, s, 0 ) );
1159 return( 0 );
1162 static gboolean buddy_send_handler_delayed( gpointer data, gint fd, b_input_condition cond )
1164 user_t *u = data;
1166 /* Shouldn't happen, but just to be sure. */
1167 if( u->sendbuf_len < 2 )
1168 return FALSE;
1170 u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */
1171 imc_buddy_msg( u->ic, u->handle, u->sendbuf, u->sendbuf_flags );
1173 g_free( u->sendbuf );
1174 u->sendbuf = NULL;
1175 u->sendbuf_len = 0;
1176 u->sendbuf_timer = 0;
1177 u->sendbuf_flags = 0;
1179 return FALSE;
1182 void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags )
1184 if( !u || !u->ic ) return;
1186 if( set_getbool( &irc->set, "buddy_sendbuffer" ) && set_getint( &irc->set, "buddy_sendbuffer_delay" ) > 0 )
1188 int delay;
1190 if( u->sendbuf_len > 0 && u->sendbuf_flags != flags)
1192 /* Flush the buffer */
1193 b_event_remove( u->sendbuf_timer );
1194 buddy_send_handler_delayed( u, -1, 0 );
1197 if( u->sendbuf_len == 0 )
1199 u->sendbuf_len = strlen( msg ) + 2;
1200 u->sendbuf = g_new( char, u->sendbuf_len );
1201 u->sendbuf[0] = 0;
1202 u->sendbuf_flags = flags;
1204 else
1206 u->sendbuf_len += strlen( msg ) + 1;
1207 u->sendbuf = g_renew( char, u->sendbuf, u->sendbuf_len );
1210 strcat( u->sendbuf, msg );
1211 strcat( u->sendbuf, "\n" );
1213 delay = set_getint( &irc->set, "buddy_sendbuffer_delay" );
1214 if( delay <= 5 )
1215 delay *= 1000;
1217 if( u->sendbuf_timer > 0 )
1218 b_event_remove( u->sendbuf_timer );
1219 u->sendbuf_timer = b_timeout_add( delay, buddy_send_handler_delayed, u );
1221 else
1223 imc_buddy_msg( u->ic, u->handle, msg, flags );
1227 int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg )
1229 char last = 0;
1230 char *s = msg, *line = msg;
1232 /* The almighty linesplitter .. woohoo!! */
1233 while( !last )
1235 if( *s == '\r' && *(s+1) == '\n' )
1236 *(s++) = 0;
1237 if( *s == '\n' )
1239 last = s[1] == 0;
1240 *s = 0;
1242 else
1244 last = s[0] == 0;
1246 if( *s == 0 )
1248 if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 )
1250 irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host,
1251 type, to, line + 4 );
1253 else
1255 irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host,
1256 type, to, prefix ? prefix : "", line );
1258 line = s + 1;
1260 s ++;
1263 return( 1 );
1266 int irc_msgfrom( irc_t *irc, char *nick, char *msg )
1268 user_t *u = user_find( irc, nick );
1269 static char *prefix = NULL;
1271 if( !u ) return( 0 );
1272 if( prefix && *prefix ) g_free( prefix );
1274 if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 )
1276 int len = strlen( irc->nick) + 3;
1277 prefix = g_new (char, len );
1278 g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( &irc->set, "to_char" ) );
1279 prefix[len-1] = 0;
1281 else
1283 prefix = "";
1286 return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) );
1289 int irc_noticefrom( irc_t *irc, char *nick, char *msg )
1291 user_t *u = user_find( irc, nick );
1293 if( u )
1294 return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) );
1295 else
1296 return( 0 );
1299 /* Returns 0 if everything seems to be okay, a number >0 when there was a
1300 timeout. The number returned is the number of seconds we received no
1301 pongs from the user. When not connected yet, we don't ping but drop the
1302 connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
1303 static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond )
1305 irc_t *irc = _irc;
1306 int rv = 0;
1308 if( !( irc->status & USTATUS_LOGGED_IN ) )
1310 if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) )
1311 rv = gettime() - irc->last_pong;
1313 else
1315 if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging )
1317 irc_write( irc, "PING :%s", IRC_PING_STRING );
1318 irc->pinging = 1;
1320 else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) )
1322 rv = gettime() - irc->last_pong;
1326 if( rv > 0 )
1328 irc_abort( irc, 0, "Ping Timeout: %d seconds", rv );
1329 return FALSE;
1332 return TRUE;
1335 struct groupchat *irc_chat_by_channel( irc_t *irc, char *channel )
1337 struct groupchat *c;
1338 account_t *a;
1340 /* This finds the connection which has a conversation which belongs to this channel */
1341 for( a = irc->accounts; a; a = a->next )
1343 if( a->ic == NULL )
1344 continue;
1346 c = a->ic->groupchats;
1347 while( c )
1349 if( c->channel && g_strcasecmp( c->channel, channel ) == 0 )
1350 return c;
1352 c = c->next;
1356 return NULL;