1 /********************************************************************\
2 * BitlBee -- An IRC to other IM-networks gateway *
4 * Copyright 2002-2004 Wilmer van der Gaast and others *
5 \********************************************************************/
7 /* The big hairy IRCd part of the project */
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
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
);
51 static char *set_eval_charset( set_t
*set
, char *value
)
53 irc_t
*irc
= set
->data
;
58 if( g_strcasecmp( value
, "none" ) == 0 )
59 value
= g_strdup( "utf-8" );
61 if( ( oc
= g_iconv_open( value
, "utf-8" ) ) == (GIConv
) -1 )
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
||
73 irc_usermsg( irc
, "Unsupported character set: The IRC protocol "
74 "only supports 8-bit character sets." );
79 if( ( ic
= g_iconv_open( "utf-8", value
) ) == (GIConv
) -1 )
85 if( irc
->iconv
!= (GIConv
) -1 )
86 g_iconv_close( irc
->iconv
);
87 if( irc
->oconv
!= (GIConv
) -1 )
88 g_iconv_close( irc
->oconv
);
96 static char *set_eval_away_status( set_t
*set
, char *value
)
98 irc_t
*irc
= set
->data
;
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
);
115 irc_t
*irc_new( int fd
)
118 struct sockaddr_storage sock
;
119 socklen_t socklen
= sizeof( sock
);
122 irc
= g_new0( irc_t
, 1 );
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" ) );
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
, ... )
229 va_start( params
, format
);
230 reason
= g_strdup_vprintf( format
, params
);
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
);
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
);
268 static gboolean
irc_free_hashkey( gpointer key
, gpointer value
, gpointer data
)
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
);
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
);
307 set_del( &irc
->set
, irc
->set
->key
);
309 if (irc
->users
!= NULL
)
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
);
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
);
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
);
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
);
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
) )
376 Sets pass without checking */
377 void irc_setpass (irc_t
*irc
, const char *pass
)
379 g_free (irc
->password
);
382 irc
->password
= g_strdup (pass
);
384 irc
->password
= NULL
;
388 void irc_process( irc_t
*irc
)
390 char **lines
, *temp
, **cmd
;
393 if( irc
->readbuffer
!= NULL
)
395 lines
= irc_tokenize( irc
->readbuffer
);
397 for( i
= 0; *lines
[i
] != '\0'; i
++ )
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
;
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" ) );
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
++ )
451 if( lines
[i
] && ( cmd
= irc_parse_line( lines
[i
] ) ) )
453 irc_exec( irc
, cmd
);
459 /* Shouldn't really happen, but just in case... */
460 if( !g_slist_find( irc_connection_list
, irc
) )
467 if( lines
[i
] != NULL
)
469 g_free( irc
->readbuffer
);
470 irc
->readbuffer
= NULL
;
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
)
484 /* Allocate n+1 elements. */
485 lines
= g_new( char *, n
+ 1 );
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' )
499 lines
[++j
] = buffer
+ i
;
504 lines
= g_renew( char *, lines
, n
+ 1 );
507 if( buffer
[i
] == '\0' )
512 /* NULL terminate our list. */
518 /* Split an IRC-style line into little parts/arguments. */
519 char **irc_parse_line( char *line
)
524 /* Move the line pointer to the start of the command, skipping spaces and the optional prefix. */
527 for( i
= 0; line
[i
] && line
[i
] != ' '; i
++ );
530 for( i
= 0; line
[i
] == ' '; i
++ );
533 /* If we're already at the end of the line, return. If not, we're going to need at least one element. */
537 /* Count the number of char **cmd elements we're going to need. */
539 for( i
= 0; line
[i
] != '\0'; i
++ )
545 if( line
[i
+1] == ':' )
550 /* Allocate the space we need. */
551 cmd
= g_new( char *, j
+ 1 );
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
560 for( i
= 0, j
= 0; line
[i
] != '\0'; i
++ )
565 cmd
[++j
] = line
+ i
+ 1;
567 if( line
[i
+1] == ':' )
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
)
588 for( i
= 0; cmd
[i
]; i
++ )
589 len
+= strlen( cmd
[i
] ) + 1;
591 if( strchr( cmd
[i
-1], ' ' ) != NULL
)
594 s
= g_new0( char, len
+ 1 );
595 for( i
= 0; cmd
[i
]; i
++ )
597 if( cmd
[i
+1] == NULL
&& strchr( cmd
[i
], ' ' ) != NULL
)
610 void irc_reply( irc_t
*irc
, int code
, char *format
, ... )
612 char text
[IRC_MAX_LINE
];
615 va_start( params
, format
);
616 g_vsnprintf( text
, IRC_MAX_LINE
, format
, params
);
618 irc_write( irc
, ":%s %03d %s %s", irc
->myhost
, code
, irc
->nick
?irc
->nick
:"*", text
);
623 int irc_usermsg( irc_t
*irc
, char *format
, ... )
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
);
637 return( irc_msgfrom( irc
, u
->nick
, text
) );
640 void irc_write( irc_t
*irc
, char *format
, ... )
644 va_start( params
, format
);
645 irc_vawrite( irc
, format
, params
);
651 void irc_vawrite( irc_t
*irc
, char *format
, va_list params
)
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
)
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
;
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 );
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
);
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
);
706 void irc_write_all( int now
, char *format
, ... )
711 va_start( params
, format
);
713 temp
= irc_connection_list
;
714 while( temp
!= NULL
)
716 irc_t
*irc
= temp
->data
;
720 g_free( irc
->sendbuffer
);
721 irc
->sendbuffer
= g_strdup( "\r\n" );
723 irc_vawrite( temp
->data
, format
, params
);
726 bitlbee_io_current_client_write( irc
, irc
->fd
, GAIM_INPUT_WRITE
);
735 void irc_names( irc_t
*irc
, char *channel
)
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
);
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
) ) )
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
);
782 strcat( namelist
, u
->nick
);
783 strcat( 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." );
810 /* More information needed. */
815 void irc_login( irc_t
*irc
)
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 );
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
);
834 u
->send_handler
= root_command_string
;
835 u
->is_private
= 0; /* [SH] The channel is root's personal playground. */
838 u
= user_add( irc
, NS_NICK
);
839 u
->host
= g_strdup( irc
->myhost
);
840 u
->realname
= g_strdup( ROOT_FN
);
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
);
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 "
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
)
879 fd
= open( global
.conf
->motdfile
, O_RDONLY
);
882 irc_reply( irc
, 422, ":We don't need MOTDs." );
886 char linebuf
[80]; /* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */
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
)
899 irc_reply( irc
, 372, ":- %s", linebuf
);
902 else if( linebuf
[len
] == '%' )
904 read( fd
, linebuf
+ len
, 1 );
905 if( linebuf
[len
] == 'h' )
907 else if( linebuf
[len
] == 'v' )
908 add
= BITLBEE_VERSION
;
909 else if( linebuf
[len
] == 'n' )
914 strncpy( linebuf
+ len
, add
, max
- len
);
915 while( linebuf
[++len
] );
922 irc_reply( irc
, 376, ":End of MOTD" );
927 void irc_topic( irc_t
*irc
, char *channel
)
929 struct groupchat
*c
= irc_chat_by_channel( irc
, channel
);
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
);
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
;
945 char changes
[512], *p
, st2
= 2;
948 memset( m
, 0, sizeof( m
) );
950 for( t
= irc
->umode
; *t
; t
++ )
954 for( t
= s
; *t
; t
++ )
956 if( *t
== '+' || *t
== '-' )
958 else if( st
== 0 || ( strchr( UMODES
, *t
) || ( allow_priv
&& strchr( UMODES_PRIV
, *t
) ) ) )
960 if( m
[(int)*t
] != st
)
963 st2
= st
, *p
++ = st
? '+' : '-';
973 memset( irc
->umode
, 0, sizeof( irc
->umode
) );
975 for( i
= 0; i
< 256 && strlen( irc
->umode
) < ( sizeof( irc
->umode
) - 1 ); i
++ )
977 irc
->umode
[strlen(irc
->umode
)] = i
;
980 irc_reply( irc
, 501, ":Unknown MODE flag" );
981 /* Deliberately no !user@host on the prefix here */
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
)
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
);
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" );
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
)
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
,
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
, ':' ) ) )
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
);
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" );
1061 int irc_send( irc_t
*irc
, char *nick
, char *s
, int flags
)
1063 struct groupchat
*c
= 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
);
1076 u
= user_find( irc
, nick
);
1080 if( irc
->is_private
)
1081 irc_reply( irc
, 401, "%s :Nick does not exist", nick
);
1083 irc_usermsg( irc
, "Nick `%s' does not exist!", nick
);
1088 if( *s
== 1 && s
[strlen(s
)-1] == 1 )
1090 if( g_strncasecmp( s
+ 1, "ACTION", 6 ) == 0 )
1092 if( s
[7] == ' ' ) s
++;
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" );
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
);
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
;
1129 irc_usermsg( irc
, "Non-ACTION CTCP's aren't supported" );
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
;
1143 irc_reply( irc
, 301, "%s :%s", u
->nick
, "User is offline" );
1145 irc_reply( irc
, 301, "%s :%s", u
->nick
, u
->away
);
1148 if( u
->send_handler
)
1150 u
->send_handler( irc
, u
, s
, flags
);
1154 else if( c
&& c
->ic
&& c
->ic
->acc
&& c
->ic
->acc
->prpl
)
1156 return( imc_chat_msg( c
, s
, 0 ) );
1162 static gboolean
buddy_send_handler_delayed( gpointer data
, gint fd
, b_input_condition cond
)
1166 /* Shouldn't happen, but just to be sure. */
1167 if( u
->sendbuf_len
< 2 )
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
);
1176 u
->sendbuf_timer
= 0;
1177 u
->sendbuf_flags
= 0;
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 )
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
);
1202 u
->sendbuf_flags
= flags
;
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" );
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
);
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
)
1230 char *s
= msg
, *line
= msg
;
1232 /* The almighty linesplitter .. woohoo!! */
1235 if( *s
== '\r' && *(s
+1) == '\n' )
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 );
1255 irc_write( irc
, ":%s!%s@%s %s %s :%s%s", u
->nick
, u
->user
, u
->host
,
1256 type
, to
, prefix
? prefix
: "", line
);
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" ) );
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
);
1294 return( irc_privmsg( irc
, u
, "NOTICE", irc
->nick
, "", msg
) );
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
)
1308 if( !( irc
->status
& USTATUS_LOGGED_IN
) )
1310 if( gettime() > ( irc
->last_pong
+ IRC_LOGIN_TIMEOUT
) )
1311 rv
= gettime() - irc
->last_pong
;
1315 if( ( gettime() > ( irc
->last_pong
+ global
.conf
->ping_interval
) ) && !irc
->pinging
)
1317 irc_write( irc
, "PING :%s", IRC_PING_STRING
);
1320 else if( gettime() > ( irc
->last_pong
+ global
.conf
->ping_timeout
) )
1322 rv
= gettime() - irc
->last_pong
;
1328 irc_abort( irc
, 0, "Ping Timeout: %d seconds", rv
);
1335 struct groupchat
*irc_chat_by_channel( irc_t
*irc
, char *channel
)
1337 struct groupchat
*c
;
1340 /* This finds the connection which has a conversation which belongs to this channel */
1341 for( a
= irc
->accounts
; a
; a
= a
->next
)
1346 c
= a
->ic
->groupchats
;
1349 if( c
->channel
&& g_strcasecmp( c
->channel
, channel
) == 0 )