1 /********************************************************************\
2 * BitlBee -- An IRC to other IM-networks gateway *
4 * Copyright 2002-2004 Wilmer van der Gaast and others *
5 \********************************************************************/
7 /* The IRC-based UI (for now the only one) */
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
30 GSList
*irc_connection_list
;
33 static gboolean
irc_userping( gpointer _irc
, gint fd
, b_input_condition cond
);
34 static char *set_eval_charset( set_t
*set
, char *value
);
35 static char *set_eval_password( set_t
*set
, char *value
);
36 static char *set_eval_bw_compat( set_t
*set
, char *value
);
38 irc_t
*irc_new( int fd
)
41 struct sockaddr_storage sock
;
42 socklen_t socklen
= sizeof( sock
);
43 char *host
= NULL
, *myhost
= NULL
;
49 irc
= g_new0( irc_t
, 1 );
52 sock_make_nonblocking( irc
->fd
);
54 irc
->r_watch_source_id
= b_input_add( irc
->fd
, B_EV_IO_READ
, bitlbee_io_current_client_read
, irc
);
56 irc
->status
= USTATUS_OFFLINE
;
57 irc
->last_pong
= gettime();
59 irc
->nick_user_hash
= g_hash_table_new( g_str_hash
, g_str_equal
);
60 irc
->watches
= g_hash_table_new( g_str_hash
, g_str_equal
);
62 irc
->iconv
= (GIConv
) -1;
63 irc
->oconv
= (GIConv
) -1;
65 if( global
.conf
->hostname
)
67 myhost
= g_strdup( global
.conf
->hostname
);
69 else if( getsockname( irc
->fd
, (struct sockaddr
*) &sock
, &socklen
) == 0 )
71 char buf
[NI_MAXHOST
+1];
73 if( getnameinfo( (struct sockaddr
*) &sock
, socklen
, buf
,
74 NI_MAXHOST
, NULL
, 0, 0 ) == 0 )
76 myhost
= g_strdup( ipv6_unwrap( buf
) );
80 if( getpeername( irc
->fd
, (struct sockaddr
*) &sock
, &socklen
) == 0 )
82 char buf
[NI_MAXHOST
+1];
84 if( getnameinfo( (struct sockaddr
*)&sock
, socklen
, buf
,
85 NI_MAXHOST
, NULL
, 0, 0 ) == 0 )
87 host
= g_strdup( ipv6_unwrap( buf
) );
92 host
= g_strdup( "localhost.localdomain" );
94 myhost
= g_strdup( "localhost.localdomain" );
96 if( global
.conf
->ping_interval
> 0 && global
.conf
->ping_timeout
> 0 )
97 irc
->ping_source_id
= b_timeout_add( global
.conf
->ping_interval
* 1000, irc_userping
, irc
);
99 irc_connection_list
= g_slist_append( irc_connection_list
, irc
);
101 b
= irc
->b
= bee_new();
103 b
->ui
= &irc_ui_funcs
;
105 s
= set_add( &b
->set
, "allow_takeover", "true", set_eval_bool
, irc
);
106 s
= set_add( &b
->set
, "away_devoice", "true", set_eval_bw_compat
, irc
);
107 s
->flags
|= SET_HIDDEN
;
108 s
= set_add( &b
->set
, "away_reply_timeout", "3600", set_eval_int
, irc
);
109 s
= set_add( &b
->set
, "charset", "utf-8", set_eval_charset
, irc
);
110 s
= set_add( &b
->set
, "default_target", "root", NULL
, irc
);
111 s
= set_add( &b
->set
, "display_namechanges", "false", set_eval_bool
, irc
);
112 s
= set_add( &b
->set
, "display_timestamps", "true", set_eval_bool
, irc
);
113 s
= set_add( &b
->set
, "handle_unknown", "add_channel", NULL
, irc
);
114 s
= set_add( &b
->set
, "last_version", "0", NULL
, irc
);
115 s
->flags
|= SET_HIDDEN
;
116 s
= set_add( &b
->set
, "lcnicks", "true", set_eval_bool
, irc
);
117 s
= set_add( &b
->set
, "nick_format", "%-@nick", NULL
, irc
);
118 s
= set_add( &b
->set
, "offline_user_quits", "true", set_eval_bool
, irc
);
119 s
= set_add( &b
->set
, "ops", "both", set_eval_irc_channel_ops
, irc
);
120 s
= set_add( &b
->set
, "paste_buffer", "false", set_eval_bool
, irc
);
121 s
->old_key
= g_strdup( "buddy_sendbuffer" );
122 s
= set_add( &b
->set
, "paste_buffer_delay", "200", set_eval_int
, irc
);
123 s
->old_key
= g_strdup( "buddy_sendbuffer_delay" );
124 s
= set_add( &b
->set
, "password", NULL
, set_eval_password
, irc
);
125 s
->flags
|= SET_NULL_OK
| SET_PASSWORD
;
126 s
= set_add( &b
->set
, "private", "true", set_eval_bool
, irc
);
127 s
= set_add( &b
->set
, "query_order", "lifo", NULL
, irc
);
128 s
= set_add( &b
->set
, "root_nick", ROOT_NICK
, set_eval_root_nick
, irc
);
129 s
->flags
|= SET_HIDDEN
;
130 s
= set_add( &b
->set
, "show_offline", "false", set_eval_bw_compat
, irc
);
131 s
->flags
|= SET_HIDDEN
;
132 s
= set_add( &b
->set
, "simulate_netsplit", "true", set_eval_bool
, irc
);
133 s
= set_add( &b
->set
, "timezone", "local", set_eval_timezone
, irc
);
134 s
= set_add( &b
->set
, "to_char", ": ", set_eval_to_char
, irc
);
135 s
= set_add( &b
->set
, "typing_notice", "false", set_eval_bool
, irc
);
137 irc
->root
= iu
= irc_user_new( irc
, ROOT_NICK
);
138 iu
->host
= g_strdup( myhost
);
139 iu
->fullname
= g_strdup( ROOT_FN
);
140 iu
->f
= &irc_user_root_funcs
;
142 iu
= irc_user_new( irc
, NS_NICK
);
143 iu
->host
= g_strdup( myhost
);
144 iu
->fullname
= g_strdup( ROOT_FN
);
145 iu
->f
= &irc_user_root_funcs
;
147 irc
->user
= g_new0( irc_user_t
, 1 );
148 irc
->user
->host
= g_strdup( host
);
150 conf_loaddefaults( irc
);
152 /* Evaluator sets the iconv/oconv structures. */
153 set_eval_charset( set_find( &b
->set
, "charset" ), set_getstr( &b
->set
, "charset" ) );
155 irc_write( irc
, ":%s NOTICE AUTH :%s", irc
->root
->host
, "BitlBee-IRCd initialized, please go on" );
156 if( isatty( irc
->fd
) )
157 irc_write( irc
, ":%s NOTICE AUTH :%s", irc
->root
->host
,
158 "If you read this, you most likely accidentally "
159 "started BitlBee in inetd mode on the command line. "
160 "You probably want to run it in (Fork)Daemon mode. "
161 "See doc/README for more information." );
166 /* libpurple doesn't like fork()s after initializing itself, so this
167 is the right moment to initialize it. */
172 for( l
= irc_plugins
; l
; l
= l
->next
)
174 irc_plugin_t
*p
= l
->data
;
182 /* immed=1 makes this function pretty much equal to irc_free(), except that
183 this one will "log". In case the connection is already broken and we
184 shouldn't try to write to it. */
185 void irc_abort( irc_t
*irc
, int immed
, char *format
, ... )
193 va_start( params
, format
);
194 reason
= g_strdup_vprintf( format
, params
);
199 irc_write( irc
, "ERROR :Closing link: %s", reason
);
201 ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
202 irc
->user
->nick
? irc
->user
->nick
: "(NONE)",
203 irc
->user
->host
, reason
? : "" );
214 b_event_remove( irc
->ping_source_id
);
215 irc
->ping_source_id
= b_timeout_add( 1, (b_event_handler
) irc_free
, irc
);
219 static gboolean
irc_free_hashkey( gpointer key
, gpointer value
, gpointer data
);
221 void irc_free( irc_t
* irc
)
225 irc
->status
|= USTATUS_SHUTDOWN
;
227 log_message( LOGLVL_INFO
, "Destroying connection with fd %d", irc
->fd
);
229 if( irc
->status
& USTATUS_IDENTIFIED
&& set_getbool( &irc
->b
->set
, "save_on_quit" ) )
230 if( storage_save( irc
, NULL
, TRUE
) != STORAGE_OK
)
231 log_message( LOGLVL_WARNING
, "Error while saving settings for user %s", irc
->user
->nick
);
233 for( l
= irc_plugins
; l
; l
= l
->next
)
235 irc_plugin_t
*p
= l
->data
;
240 irc_connection_list
= g_slist_remove( irc_connection_list
, irc
);
242 while( irc
->queries
!= NULL
)
243 query_del( irc
, irc
->queries
);
245 /* This is a little bit messy: bee_free() frees all b->users which
246 calls us back to free the corresponding irc->users. So do this
247 before we clear the remaining ones ourselves. */
251 irc_user_free( irc
, (irc_user_t
*) irc
->users
->data
);
253 while( irc
->channels
)
254 irc_channel_free( irc
->channels
->data
);
256 if( irc
->ping_source_id
> 0 )
257 b_event_remove( irc
->ping_source_id
);
258 if( irc
->r_watch_source_id
> 0 )
259 b_event_remove( irc
->r_watch_source_id
);
260 if( irc
->w_watch_source_id
> 0 )
261 b_event_remove( irc
->w_watch_source_id
);
263 closesocket( irc
->fd
);
266 g_hash_table_foreach_remove( irc
->nick_user_hash
, irc_free_hashkey
, NULL
);
267 g_hash_table_destroy( irc
->nick_user_hash
);
269 g_hash_table_foreach_remove( irc
->watches
, irc_free_hashkey
, NULL
);
270 g_hash_table_destroy( irc
->watches
);
272 if( irc
->iconv
!= (GIConv
) -1 )
273 g_iconv_close( irc
->iconv
);
274 if( irc
->oconv
!= (GIConv
) -1 )
275 g_iconv_close( irc
->oconv
);
277 g_free( irc
->sendbuffer
);
278 g_free( irc
->readbuffer
);
279 g_free( irc
->password
);
283 if( global
.conf
->runmode
== RUNMODE_INETD
||
284 global
.conf
->runmode
== RUNMODE_FORKDAEMON
||
285 ( global
.conf
->runmode
== RUNMODE_DAEMON
&&
286 global
.listen_socket
== -1 &&
287 irc_connection_list
== NULL
) )
291 static gboolean
irc_free_hashkey( gpointer key
, gpointer value
, gpointer data
)
299 Sets pass without checking */
300 void irc_setpass (irc_t
*irc
, const char *pass
)
302 g_free (irc
->password
);
305 irc
->password
= g_strdup (pass
);
307 irc
->password
= NULL
;
311 static char *set_eval_password( set_t
*set
, char *value
)
313 irc_t
*irc
= set
->data
;
315 if( irc
->status
& USTATUS_IDENTIFIED
&& value
)
317 irc_setpass( irc
, value
);
326 static char **irc_splitlines( char *buffer
);
328 void irc_process( irc_t
*irc
)
330 char **lines
, *temp
, **cmd
;
333 if( irc
->readbuffer
!= NULL
)
335 lines
= irc_splitlines( irc
->readbuffer
);
337 for( i
= 0; *lines
[i
] != '\0'; i
++ )
341 /* [WvG] If the last line isn't empty, it's an incomplete line and we
342 should wait for the rest to come in before processing it. */
343 if( lines
[i
+1] == NULL
)
345 temp
= g_strdup( lines
[i
] );
346 g_free( irc
->readbuffer
);
347 irc
->readbuffer
= temp
;
352 if( irc
->iconv
!= (GIConv
) -1 )
354 gsize bytes_read
, bytes_written
;
356 conv
= g_convert_with_iconv( lines
[i
], -1, irc
->iconv
,
357 &bytes_read
, &bytes_written
, NULL
);
359 if( conv
== NULL
|| bytes_read
!= strlen( lines
[i
] ) )
361 /* GLib can do strange things if things are not in the expected charset,
362 so let's be a little bit paranoid here: */
363 if( irc
->status
& USTATUS_LOGGED_IN
)
365 irc_rootmsg( irc
, "Error: Charset mismatch detected. The charset "
366 "setting is currently set to %s, so please make "
367 "sure your IRC client will send and accept text in "
368 "that charset, or tell BitlBee which charset to "
369 "expect by changing the charset setting. See "
370 "`help set charset' for more information. Your "
371 "message was ignored.",
372 set_getstr( &irc
->b
->set
, "charset" ) );
379 irc_write( irc
, ":%s NOTICE AUTH :%s", irc
->root
->host
,
380 "Warning: invalid characters received at login time." );
382 conv
= g_strdup( lines
[i
] );
383 for( temp
= conv
; *temp
; temp
++ )
391 if( lines
[i
] && ( cmd
= irc_parse_line( lines
[i
] ) ) )
393 irc_exec( irc
, cmd
);
399 /* Shouldn't really happen, but just in case... */
400 if( !g_slist_find( irc_connection_list
, irc
) )
407 if( lines
[i
] != NULL
)
409 g_free( irc
->readbuffer
);
410 irc
->readbuffer
= NULL
;
417 /* Splits a long string into separate lines. The array is NULL-terminated
418 and, unless the string contains an incomplete line at the end, ends with
419 an empty string. Could use g_strsplit() but this one does it in-place.
420 (So yes, it's destructive.) */
421 static char **irc_splitlines( char *buffer
)
426 /* Allocate n+1 elements. */
427 lines
= g_new( char *, n
+ 1 );
431 /* Split the buffer in several strings, and accept any kind of line endings,
432 * knowing that ERC on Windows may send something interesting like \r\r\n,
433 * and surely there must be clients that think just \n is enough... */
434 for( i
= 0, j
= 0; buffer
[i
] != '\0'; i
++ )
436 if( buffer
[i
] == '\r' || buffer
[i
] == '\n' )
438 while( buffer
[i
] == '\r' || buffer
[i
] == '\n' )
441 lines
[++j
] = buffer
+ i
;
446 lines
= g_renew( char *, lines
, n
+ 1 );
449 if( buffer
[i
] == '\0' )
454 /* NULL terminate our list. */
460 /* Split an IRC-style line into little parts/arguments. */
461 char **irc_parse_line( char *line
)
466 /* Move the line pointer to the start of the command, skipping spaces and the optional prefix. */
469 for( i
= 0; line
[i
] && line
[i
] != ' '; i
++ );
472 for( i
= 0; line
[i
] == ' '; i
++ );
475 /* If we're already at the end of the line, return. If not, we're going to need at least one element. */
479 /* Count the number of char **cmd elements we're going to need. */
481 for( i
= 0; line
[i
] != '\0'; i
++ )
487 if( line
[i
+1] == ':' )
492 /* Allocate the space we need. */
493 cmd
= g_new( char *, j
+ 1 );
496 /* Do the actual line splitting, format is:
497 * Input: "PRIVMSG #bitlbee :foo bar"
498 * Output: cmd[0]=="PRIVMSG", cmd[1]=="#bitlbee", cmd[2]=="foo bar", cmd[3]==NULL
502 for( i
= 0, j
= 0; line
[i
] != '\0'; i
++ )
507 cmd
[++j
] = line
+ i
+ 1;
509 if( line
[i
+1] == ':' )
520 /* Converts such an array back into a command string. Mainly used for the IPC code right now. */
521 char *irc_build_line( char **cmd
)
530 for( i
= 0; cmd
[i
]; i
++ )
531 len
+= strlen( cmd
[i
] ) + 1;
533 if( strchr( cmd
[i
-1], ' ' ) != NULL
)
536 s
= g_new0( char, len
+ 1 );
537 for( i
= 0; cmd
[i
]; i
++ )
539 if( cmd
[i
+1] == NULL
&& strchr( cmd
[i
], ' ' ) != NULL
)
552 void irc_write( irc_t
*irc
, char *format
, ... )
556 va_start( params
, format
);
557 irc_vawrite( irc
, format
, params
);
563 void irc_write_all( int now
, char *format
, ... )
568 va_start( params
, format
);
570 temp
= irc_connection_list
;
571 while( temp
!= NULL
)
573 irc_t
*irc
= temp
->data
;
577 g_free( irc
->sendbuffer
);
578 irc
->sendbuffer
= g_strdup( "\r\n" );
580 irc_vawrite( temp
->data
, format
, params
);
583 bitlbee_io_current_client_write( irc
, irc
->fd
, B_EV_IO_WRITE
);
592 void irc_vawrite( irc_t
*irc
, char *format
, va_list params
)
595 char line
[IRC_MAX_LINE
+1];
597 /* Don't try to write anything new anymore when shutting down. */
598 if( irc
->status
& USTATUS_SHUTDOWN
)
601 memset( line
, 0, sizeof( line
) );
602 g_vsnprintf( line
, IRC_MAX_LINE
- 2, format
, params
);
603 strip_newlines( line
);
605 if( irc
->oconv
!= (GIConv
) -1 )
607 gsize bytes_read
, bytes_written
;
610 conv
= g_convert_with_iconv( line
, -1, irc
->oconv
,
611 &bytes_read
, &bytes_written
, NULL
);
613 if( bytes_read
== strlen( line
) )
614 strncpy( line
, conv
, IRC_MAX_LINE
- 2 );
618 g_strlcat( line
, "\r\n", IRC_MAX_LINE
+ 1 );
620 if( irc
->sendbuffer
!= NULL
)
622 size
= strlen( irc
->sendbuffer
) + strlen( line
);
623 irc
->sendbuffer
= g_renew ( char, irc
->sendbuffer
, size
+ 1 );
624 strcpy( ( irc
->sendbuffer
+ strlen( irc
->sendbuffer
) ), line
);
628 irc
->sendbuffer
= g_strdup(line
);
631 if( irc
->w_watch_source_id
== 0 )
633 /* If the buffer is empty we can probably write, so call the write event handler
634 immediately. If it returns TRUE, it should be called again, so add the event to
635 the queue. If it's FALSE, we emptied the buffer and saved ourselves some work
636 in the event queue. */
637 /* Really can't be done as long as the code doesn't do error checking very well:
638 if( bitlbee_io_current_client_write( irc, irc->fd, B_EV_IO_WRITE ) ) */
640 /* So just always do it via the event handler. */
641 irc
->w_watch_source_id
= b_input_add( irc
->fd
, B_EV_IO_WRITE
, bitlbee_io_current_client_write
, irc
);
647 /* Flush sendbuffer if you can. If it fails, fail silently and let some
648 I/O event handler clean up. */
649 void irc_flush( irc_t
*irc
)
654 if( irc
->sendbuffer
== NULL
)
657 len
= strlen( irc
->sendbuffer
);
658 if( ( n
= send( irc
->fd
, irc
->sendbuffer
, len
, 0 ) ) == len
)
660 g_free( irc
->sendbuffer
);
661 irc
->sendbuffer
= NULL
;
663 b_event_remove( irc
->w_watch_source_id
);
664 irc
->w_watch_source_id
= 0;
668 char *s
= g_strdup( irc
->sendbuffer
+ n
);
669 g_free( irc
->sendbuffer
);
672 /* Otherwise something went wrong and we don't currently care
673 what the error was. We may or may not succeed later, we
674 were just trying to flush the buffer immediately. */
677 /* Meant for takeover functionality. Transfer an IRC connection to a different
679 void irc_switch_fd( irc_t
*irc
, int fd
)
681 irc_write( irc
, "ERROR :Transferring session to a new connection" );
682 irc_flush( irc
); /* Write it now or forget about it forever. */
684 if( irc
->sendbuffer
)
686 b_event_remove( irc
->w_watch_source_id
);
687 irc
->w_watch_source_id
= 0;
688 g_free( irc
->sendbuffer
);
689 irc
->sendbuffer
= NULL
;
692 b_event_remove( irc
->r_watch_source_id
);
693 closesocket( irc
->fd
);
695 irc
->r_watch_source_id
= b_input_add( irc
->fd
, B_EV_IO_READ
, bitlbee_io_current_client_read
, irc
);
698 void irc_sync( irc_t
*irc
)
702 irc_write( irc
, ":%s!%s@%s MODE %s :+%s", irc
->user
->nick
,
703 irc
->user
->user
, irc
->user
->host
, irc
->user
->nick
,
706 for( l
= irc
->channels
; l
; l
= l
->next
)
708 irc_channel_t
*ic
= l
->data
;
709 if( ic
->flags
& IRC_CHANNEL_JOINED
)
710 irc_send_join( ic
, irc
->user
);
713 /* We may be waiting for a PONG from the previous client connection. */
714 irc
->pinging
= FALSE
;
717 void irc_desync( irc_t
*irc
)
721 for( l
= irc
->channels
; l
; l
= l
->next
)
722 irc_channel_del_user( l
->data
, irc
->user
, IRC_CDU_KICK
,
723 "Switching to old session" );
725 irc_write( irc
, ":%s!%s@%s MODE %s :-%s", irc
->user
->nick
,
726 irc
->user
->user
, irc
->user
->host
, irc
->user
->nick
,
730 int irc_check_login( irc_t
*irc
)
732 if( irc
->user
->user
&& irc
->user
->nick
)
734 if( global
.conf
->authmode
== AUTHMODE_CLOSED
&& !( irc
->status
& USTATUS_AUTHORIZED
) )
736 irc_send_num( irc
, 464, ":This server is password-protected." );
742 irc_user_t
*iu
= irc
->user
;
744 irc
->user
= irc_user_new( irc
, iu
->nick
);
745 irc
->user
->user
= iu
->user
;
746 irc
->user
->host
= iu
->host
;
747 irc
->user
->fullname
= iu
->fullname
;
748 irc
->user
->f
= &irc_user_self_funcs
;
752 if( global
.conf
->runmode
== RUNMODE_FORKDAEMON
|| global
.conf
->runmode
== RUNMODE_DAEMON
)
753 ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc
->user
->host
, irc
->user
->nick
, irc
->user
->fullname
);
755 irc
->status
|= USTATUS_LOGGED_IN
;
757 irc_send_login( irc
);
759 irc
->umode
[0] = '\0';
760 irc_umode_set( irc
, "+" UMODE
, TRUE
);
762 ic
= irc
->default_channel
= irc_channel_new( irc
, ROOT_CHAN
);
763 irc_channel_set_topic( ic
, CONTROL_TOPIC
, irc
->root
);
764 set_setstr( &ic
->set
, "auto_join", "true" );
765 irc_channel_auto_joins( irc
, NULL
);
767 irc
->root
->last_channel
= irc
->default_channel
;
770 "Welcome to the BitlBee gateway!\n\n"
771 "If you've never used BitlBee before, please do read the help "
772 "information using the \x02help\x02 command. Lots of FAQs are "
774 "If you already have an account on this server, just use the "
775 "\x02identify\x02 command to identify yourself." );
777 /* This is for bug #209 (use PASS to identify to NickServ). */
778 if( irc
->password
!= NULL
)
780 char *send_cmd
[] = { "identify", g_strdup( irc
->password
), NULL
};
782 irc_setpass( irc
, NULL
);
783 root_command( irc
, send_cmd
);
784 g_free( send_cmd
[1] );
792 /* More information needed. */
797 /* TODO: This is a mess, but this function is a bit too complicated to be
798 converted to something more generic. */
799 void irc_umode_set( irc_t
*irc
, const char *s
, gboolean allow_priv
)
801 /* allow_priv: Set to 0 if s contains user input, 1 if you want
802 to set a "privileged" mode (+o, +R, etc). */
806 char changes
[512], st2
= 2;
809 memset( m
, 0, sizeof( m
) );
811 /* Keep track of which modes are enabled in this array. */
812 for( t
= irc
->umode
; *t
; t
++ )
813 if( *t
< sizeof( m
) )
817 for( t
= s
; *t
&& i
< sizeof( changes
) - 3; t
++ )
819 if( *t
== '+' || *t
== '-' )
821 else if( ( st
== 0 && ( !strchr( UMODES_KEEP
, *t
) || allow_priv
) ) ||
822 ( st
== 1 && strchr( UMODES
, *t
) ) ||
823 ( st
== 1 && allow_priv
&& strchr( UMODES_PRIV
, *t
) ) )
825 if( m
[(int)*t
] != st
)
827 /* If we're actually making a change, remember this
830 st2
= st
, changes
[i
++] = st
? '+' : '-';
840 /* Convert the m array back into an umode string. */
841 memset( irc
->umode
, 0, sizeof( irc
->umode
) );
842 for( i
= 'A'; i
<= 'z' && strlen( irc
->umode
) < ( sizeof( irc
->umode
) - 1 ); i
++ )
844 irc
->umode
[strlen(irc
->umode
)] = i
;
847 irc_send_num( irc
, 501, ":Unknown MODE flag" );
849 irc_write( irc
, ":%s!%s@%s MODE %s :%s", irc
->user
->nick
,
850 irc
->user
->user
, irc
->user
->host
, irc
->user
->nick
,
854 /* Returns 0 if everything seems to be okay, a number >0 when there was a
855 timeout. The number returned is the number of seconds we received no
856 pongs from the user. When not connected yet, we don't ping but drop the
857 connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
858 static gboolean
irc_userping( gpointer _irc
, gint fd
, b_input_condition cond
)
860 double now
= gettime();
864 if( !( irc
->status
& USTATUS_LOGGED_IN
) )
866 if( now
> ( irc
->last_pong
+ IRC_LOGIN_TIMEOUT
) )
867 fail
= now
- irc
->last_pong
;
871 if( now
> ( irc
->last_pong
+ global
.conf
->ping_timeout
) )
873 fail
= now
- irc
->last_pong
;
877 irc_write( irc
, "PING :%s", IRC_PING_STRING
);
883 irc_abort( irc
, 0, "Ping Timeout: %d seconds", fail
);
890 static char *set_eval_charset( set_t
*set
, char *value
)
892 irc_t
*irc
= (irc_t
*) set
->data
;
894 gsize test_bytes
= 0;
897 if( g_strcasecmp( value
, "none" ) == 0 )
898 value
= g_strdup( "utf-8" );
900 if( ( oc
= g_iconv_open( value
, "utf-8" ) ) == (GIConv
) -1 )
905 /* Do a test iconv to see if the user picked an IRC-compatible
906 charset (for example utf-16 goes *horribly* wrong). */
907 if( ( test
= g_convert_with_iconv( " ", 1, oc
, NULL
, &test_bytes
, NULL
) ) == NULL
||
912 irc_rootmsg( irc
, "Unsupported character set: The IRC protocol "
913 "only supports 8-bit character sets." );
918 if( ( ic
= g_iconv_open( "utf-8", value
) ) == (GIConv
) -1 )
924 if( irc
->iconv
!= (GIConv
) -1 )
925 g_iconv_close( irc
->iconv
);
926 if( irc
->oconv
!= (GIConv
) -1 )
927 g_iconv_close( irc
->oconv
);
935 /* Mostly meant for upgrades. If one of these is set to the non-default,
936 set show_users of all channels to something with the same effect. */
937 static char *set_eval_bw_compat( set_t
*set
, char *value
)
939 irc_t
*irc
= set
->data
;
943 irc_rootmsg( irc
, "Setting `%s' is obsolete, use the `show_users' "
944 "channel setting instead.", set
->key
);
946 if( strcmp( set
->key
, "away_devoice" ) == 0 && !bool2int( value
) )
948 else if( strcmp( set
->key
, "show_offline" ) == 0 && bool2int( value
) )
949 val
= "online@,away+,offline";
951 val
= "online+,away";
953 for( l
= irc
->channels
; l
; l
= l
->next
)
955 irc_channel_t
*ic
= l
->data
;
956 /* No need to check channel type, if the setting doesn't exist it
957 will just be ignored. */
958 set_setstr( &ic
->set
, "show_users", val
);
964 void register_irc_plugin( const struct irc_plugin
*p
)
966 irc_plugins
= g_slist_prepend( irc_plugins
, (gpointer
) p
);