aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--account.c12
-rw-r--r--bitlbee.c16
-rw-r--r--bitlbee.conf7
-rw-r--r--bitlbee.h2
-rw-r--r--conf.c14
-rw-r--r--conf.h2
-rw-r--r--doc/CHANGES14
-rw-r--r--doc/RELEASE-SPEECH-1.257
-rw-r--r--doc/user-guide/commands.xml10
-rw-r--r--irc.c296
-rw-r--r--irc.h5
-rw-r--r--irc_commands.c27
-rw-r--r--lib/arc.h4
-rw-r--r--lib/proxy.c11
-rw-r--r--protocols/jabber/jabber_util.c8
-rw-r--r--protocols/msn/msn.h2
-rw-r--r--protocols/msn/msn_util.c8
-rw-r--r--protocols/msn/ns.c24
-rw-r--r--protocols/nogaim.c17
-rw-r--r--protocols/nogaim.h3
-rw-r--r--protocols/oscar/oscar.c18
-rw-r--r--protocols/yahoo/yahoo.c8
-rw-r--r--query.c7
-rw-r--r--query.h8
-rw-r--r--root_commands.c60
-rw-r--r--set.c15
-rw-r--r--set.h1
-rw-r--r--tests/check_irc.c4
28 files changed, 411 insertions, 249 deletions
diff --git a/account.c b/account.c
index 4eb78faa..2c6e1069 100644
--- a/account.c
+++ b/account.c
@@ -181,19 +181,17 @@ void account_del( irc_t *irc, account_t *acc )
{
account_t *a, *l = NULL;
+ if( acc->ic )
+ /* Caller should have checked, accounts still in use can't be deleted. */
+ return;
+
for( a = irc->accounts; a; a = (l=a)->next )
if( a == acc )
{
- if( a->ic ) return; /* Caller should have checked, accounts still in use can't be deleted. */
-
if( l )
- {
l->next = a->next;
- }
else
- {
irc->accounts = a->next;
- }
while( a->set )
set_del( &a->set, a->set->key );
@@ -202,7 +200,7 @@ void account_del( irc_t *irc, account_t *acc )
g_free( a->user );
g_free( a->pass );
- if( a->server ) g_free( a->server );
+ g_free( a->server );
if( a->reconnect ) /* This prevents any reconnect still queued to happen */
cancel_auto_reconnect( a );
g_free( a );
diff --git a/bitlbee.c b/bitlbee.c
index 59a417f0..17431546 100644
--- a/bitlbee.c
+++ b/bitlbee.c
@@ -53,11 +53,11 @@ int bitlbee_daemon_init()
#endif
;
- i = getaddrinfo( global.conf->iface, global.conf->port, &hints, &addrinfo_bind );
+ i = getaddrinfo( global.conf->iface_in, global.conf->port, &hints, &addrinfo_bind );
if( i )
{
log_message( LOGLVL_ERROR, "Couldn't parse address `%s': %s",
- global.conf->iface, gai_strerror(i) );
+ global.conf->iface_in, gai_strerror(i) );
return -1;
}
@@ -225,12 +225,16 @@ gboolean bitlbee_io_current_client_write( gpointer data, gint fd, b_input_condit
if( st == size )
{
- g_free( irc->sendbuffer );
- irc->sendbuffer = NULL;
- irc->w_watch_source_id = 0;
-
if( irc->status & USTATUS_SHUTDOWN )
+ {
irc_free( irc );
+ }
+ else
+ {
+ g_free( irc->sendbuffer );
+ irc->sendbuffer = NULL;
+ irc->w_watch_source_id = 0;
+ }
return FALSE;
}
diff --git a/bitlbee.conf b/bitlbee.conf
index 99e8106d..c03a564f 100644
--- a/bitlbee.conf
+++ b/bitlbee.conf
@@ -34,6 +34,13 @@
# DaemonInterface = 0.0.0.0
# DaemonPort = 6667
+## ClientInterface:
+##
+## If for any reason, you want BitlBee to use a specific address/interface
+## for outgoing traffic (IM connections, HTTP(S), etc.), set it here.
+##
+# ClientInterface = 0.0.0.0
+
## AuthMode
##
## Open -- Accept connections from anyone, use NickServ for user authentication.
diff --git a/bitlbee.h b/bitlbee.h
index 420ab70a..60694a2a 100644
--- a/bitlbee.h
+++ b/bitlbee.h
@@ -159,6 +159,8 @@ void root_command_string( irc_t *irc, user_t *u, char *command, int flags );
void root_command( irc_t *irc, char *command[] );
gboolean bitlbee_shutdown( gpointer data, gint fd, b_input_condition cond );
+char *set_eval_root_nick( set_t *set, char *new_nick );
+
extern global_t global;
#endif
diff --git a/conf.c b/conf.c
index 339af618..8d6291ff 100644
--- a/conf.c
+++ b/conf.c
@@ -44,7 +44,8 @@ conf_t *conf_load( int argc, char *argv[] )
conf = g_new0( conf_t, 1 );
- conf->iface = NULL;
+ conf->iface_in = NULL;
+ conf->iface_out = NULL;
conf->port = g_strdup( "6667" );
conf->nofork = 0;
conf->verbose = 0;
@@ -81,7 +82,7 @@ conf_t *conf_load( int argc, char *argv[] )
{
if( opt == 'i' )
{
- conf->iface = g_strdup( optarg );
+ conf->iface_in = g_strdup( optarg );
}
else if( opt == 'p' )
{
@@ -201,14 +202,19 @@ static int conf_loadini( conf_t *conf, char *file )
}
else if( g_strcasecmp( ini->key, "daemoninterface" ) == 0 )
{
- g_free( conf->iface );
- conf->iface = g_strdup( ini->value );
+ g_free( conf->iface_in );
+ conf->iface_in = g_strdup( ini->value );
}
else if( g_strcasecmp( ini->key, "daemonport" ) == 0 )
{
g_free( conf->port );
conf->port = g_strdup( ini->value );
}
+ else if( g_strcasecmp( ini->key, "clientinterface" ) == 0 )
+ {
+ g_free( conf->iface_out );
+ conf->iface_out = g_strdup( ini->value );
+ }
else if( g_strcasecmp( ini->key, "authmode" ) == 0 )
{
if( g_strcasecmp( ini->value, "registered" ) == 0 )
diff --git a/conf.h b/conf.h
index d21ec577..c41fd096 100644
--- a/conf.h
+++ b/conf.h
@@ -31,7 +31,7 @@ typedef enum authmode { AUTHMODE_OPEN, AUTHMODE_CLOSED, AUTHMODE_REGISTERED } au
typedef struct conf
{
- char *iface;
+ char *iface_in, *iface_out;
char *port;
int nofork;
int verbose;
diff --git a/doc/CHANGES b/doc/CHANGES
index 65947617..93ad35e2 100644
--- a/doc/CHANGES
+++ b/doc/CHANGES
@@ -1,3 +1,17 @@
+Version 1.2.1:
+- Fixed proxy support.
+- Fixed stalling issues while connecting to Jabber when using the OpenSSL
+ module.
+- Fixed problem with GLib and ForkDaemon where processes didn't die when
+ the client disconnects.
+- Fixed handling of "set charset none". (Which pretty much breaks the account
+ completely in 1.2.)
+- You can now automatically identify yourself to BitlBee by setting a server
+ password in your IRC client.
+- Compatible with all crazy kinds of line endings that clients can send.
+
+Finished ...
+
Version 1.2:
- Added ForkDaemon mode next to the existing Daemon- and inetd modes. With
ForkDaemon you can run BitlBee as a stand-alone daemon and every connection
diff --git a/doc/RELEASE-SPEECH-1.2 b/doc/RELEASE-SPEECH-1.2
deleted file mode 100644
index ec7c48b4..00000000
--- a/doc/RELEASE-SPEECH-1.2
+++ /dev/null
@@ -1,57 +0,0 @@
-BitlBee ... is Bitl
--------------------
-
-"I CAN HAS SPEEECH?" This is how Wilmer announced to me that he was going to do
-another BitlBee release. I was as surprised as you are. I thought he had given
-up on this whole releasing business.
-
-There is some humor in using a LOLcats reference to announce a new Bee
-release. The last major BitlBee release (1.0) was done a long time before the
-hype even begun. Between then and now we've celebrated BitlBee's fifth birthday
-and had a handful of releases, but nothing big happened. As a user this lax
-release planning worries me. What if this means that later releases will take
-even longer? I don't think we should fear this. Let us explore why this release
-took so long.
-
-Personally, I blame Google.
-
-There, I've said it. Google. The guys who are so big on "Do No Evil" and all
-that jazz. They caused this release to be so late. Let me explain why.
-
-They made Wilmer an offer. Wilmer, being the sillily naive guy he is, couldn't
-refuse. There were no decapitated horses involved, but he couldn't refuse
-nonetheless. That's not a big problem of course. Lots of guys (and gals, sure)
-work for Google. It wouldn't have become a problem if they hadn't shipped him of
-to Ireland. That's where the trouble starts.
-
-Ireland. Home of the leprechauns. These rascals joined forces with aliens and
-LOLcats to abduct Wilmer. The aliens had been trying for years to kidnap him,
-but they were on his turf and his trusty sidekicks Jelmer and Maurits were there
-to help him. All that changed when he moved to Ireland.
-
-But it wasn't just that Wilmer was all alone in some strange place. It was also
-that the aliens had created the LOLcats and made an alliance with the
-leprechauns--the real rulers of Ireland. Wilmer is cool, but he's not that
-cool. So they abducted him for a few years. When he finally came back, he
-regathered his mad coding skills and hacked a new release together.
-
-By now you're thinking, what has happened to Jelmer and Maurits? Well, they're
-still here and they worked on the Bee while Wilmer was absent. But, like open
-source, they are very Bitl--they're cool, but they're ready when they're
-ready.
-
-Well, whatever you may think of my little theory, they did take forever to
-launch this release. We're not mad at them, because it shows. They have
-added some cool new features and made plenty of changes under the hood. Let me
-give you a sneak preview of some of the new features: Jabber groupchats are
-supported. Configuration files are stored encrypted. You can now use
-ForkDaemon mode, which gives better stability over normal daemon mode, but
-doesn't require inetd. Also, server owners can now use the /OPER command to
-spy on their users. And--as I was specifically asked to mention--this is the
-first stable release that supports plugins. That's some Bitl changes for ya.
-
-BitlBee is cool, but ready when it's ready. Very Bitl.
-
-Sjoerd. LOL.
-
-(LOLBee by Erik Bosman.)
diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml
index c45727b9..6d77f8cd 100644
--- a/doc/user-guide/commands.xml
+++ b/doc/user-guide/commands.xml
@@ -588,6 +588,16 @@
</description>
</bitlbee-setting>
+ <bitlbee-setting name="root_nick" type="string" scope="global">
+ <default>root</default>
+
+ <description>
+ <para>
+ Normally the "bot" that takes all your BitlBee commands is called "root". If you don't like this name, you can rename it to anything else using the <emphasis>rename</emphasis> command, or by changing this setting.
+ </para>
+ </description>
+ </bitlbee-setting>
+
<bitlbee-setting name="save_on_quit" type="boolean" scope="global">
<default>true</default>
diff --git a/irc.c b/irc.c
index 3589256e..a6220140 100644
--- a/irc.c
+++ b/irc.c
@@ -41,6 +41,35 @@ static char *passchange( set_t *set, char *value )
return NULL;
}
+static char *set_eval_charset( set_t *set, char *value )
+{
+ irc_t *irc = set->data;
+ GIConv ic, oc;
+
+ if( g_strcasecmp( value, "none" ) == 0 )
+ value = g_strdup( "utf-8" );
+
+ if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 )
+ {
+ return NULL;
+ }
+ if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 )
+ {
+ g_iconv_close( ic );
+ return NULL;
+ }
+
+ if( irc->iconv != (GIConv) -1 )
+ g_iconv_close( irc->iconv );
+ if( irc->oconv != (GIConv) -1 )
+ g_iconv_close( irc->oconv );
+
+ irc->iconv = ic;
+ irc->oconv = oc;
+
+ return value;
+}
+
irc_t *irc_new( int fd )
{
irc_t *irc;
@@ -64,6 +93,9 @@ irc_t *irc_new( int fd )
irc->mynick = g_strdup( ROOT_NICK );
irc->channel = g_strdup( ROOT_CHAN );
+ irc->iconv = (GIConv) -1;
+ irc->oconv = (GIConv) -1;
+
if( global.conf->hostname )
{
irc->myhost = g_strdup( global.conf->hostname );
@@ -118,6 +150,7 @@ irc_t *irc_new( int fd )
set_add( &irc->set, "password", NULL, passchange, irc );
set_add( &irc->set, "private", "true", set_eval_bool, irc );
set_add( &irc->set, "query_order", "lifo", NULL, irc );
+ set_add( &irc->set, "root_nick", irc->mynick, set_eval_root_nick, irc );
set_add( &irc->set, "save_on_quit", "true", set_eval_bool, irc );
set_add( &irc->set, "simulate_netsplit", "true", set_eval_bool, irc );
set_add( &irc->set, "strip_html", "true", NULL, irc );
@@ -126,6 +159,9 @@ irc_t *irc_new( int fd )
conf_loaddefaults( irc );
+ /* Evaluator sets the iconv/oconv structures. */
+ set_eval_charset( set_find( &irc->set, "charset" ), set_getstr( &irc->set, "charset" ) );
+
return( irc );
}
@@ -163,12 +199,14 @@ void irc_abort( irc_t *irc, int immed, char *format, ... )
irc->status |= USTATUS_SHUTDOWN;
if( irc->sendbuffer && !immed )
{
- /* We won't read from this socket anymore. Instead, we'll connect a timer
- to it that should shut down the connection in a second, just in case
- bitlbee_.._write doesn't do it first. */
+ /* Set up a timeout event that should shut down the connection
+ in a second, just in case ..._write doesn't do it first. */
b_event_remove( irc->r_watch_source_id );
- irc->r_watch_source_id = b_timeout_add( 1000, (b_event_handler) irc_free, irc );
+ irc->r_watch_source_id = 0;
+
+ b_event_remove( irc->ping_source_id );
+ irc->ping_source_id = b_timeout_add( 1000, (b_event_handler) irc_free, irc );
}
else
{
@@ -184,9 +222,8 @@ static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data )
}
/* Because we have no garbage collection, this is quite annoying */
-void irc_free(irc_t * irc)
+void irc_free( irc_t * irc )
{
- account_t *account;
user_t *user, *usertmp;
log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd );
@@ -195,76 +232,86 @@ void irc_free(irc_t * irc)
if( storage_save( irc, TRUE ) != STORAGE_OK )
irc_usermsg( irc, "Error while saving settings!" );
- closesocket( irc->fd );
-
- if( irc->ping_source_id > 0 )
- b_event_remove( irc->ping_source_id );
- b_event_remove( irc->r_watch_source_id );
- if( irc->w_watch_source_id > 0 )
- b_event_remove( irc->w_watch_source_id );
-
irc_connection_list = g_slist_remove( irc_connection_list, irc );
- for (account = irc->accounts; account; account = account->next) {
- if (account->ic) {
- imc_logout(account->ic, TRUE);
- } else if (account->reconnect) {
- cancel_auto_reconnect(account);
- }
- }
-
- g_free(irc->sendbuffer);
- g_free(irc->readbuffer);
-
- g_free(irc->nick);
- g_free(irc->user);
- g_free(irc->host);
- g_free(irc->realname);
- g_free(irc->password);
-
- g_free(irc->myhost);
- g_free(irc->mynick);
-
- g_free(irc->channel);
-
- while (irc->queries != NULL)
- query_del(irc, irc->queries);
-
- while (irc->accounts)
- if (irc->accounts->ic == NULL)
- account_del(irc, irc->accounts);
+ while( irc->accounts )
+ {
+ if( irc->accounts->ic )
+ imc_logout( irc->accounts->ic, FALSE );
+ else if( irc->accounts->reconnect )
+ cancel_auto_reconnect( irc->accounts );
+
+ if( irc->accounts->ic == NULL )
+ account_del( irc, irc->accounts );
else
/* Nasty hack, but account_del() doesn't work in this
case and we don't want infinite loops, do we? ;-) */
irc->accounts = irc->accounts->next;
+ }
+
+ while( irc->queries != NULL )
+ query_del( irc, irc->queries );
- while (irc->set)
- set_del(&irc->set, irc->set->key);
+ while( irc->set )
+ set_del( &irc->set, irc->set->key );
- if (irc->users != NULL) {
+ if (irc->users != NULL)
+ {
user = irc->users;
- while (user != NULL) {
- g_free(user->nick);
- g_free(user->away);
- g_free(user->handle);
- if(user->user!=user->nick) g_free(user->user);
- if(user->host!=user->nick) g_free(user->host);
- if(user->realname!=user->nick) g_free(user->realname);
- b_event_remove(user->sendbuf_timer);
+ while( user != NULL )
+ {
+ g_free( user->nick );
+ g_free( user->away );
+ g_free( user->handle );
+ if( user->user != user->nick ) g_free( user->user );
+ if( user->host != user->nick ) g_free( user->host );
+ if( user->realname != user->nick ) g_free( user->realname );
+ b_event_remove( user->sendbuf_timer );
usertmp = user;
user = user->next;
- g_free(usertmp);
+ g_free( usertmp );
}
}
- g_hash_table_foreach_remove(irc->userhash, irc_free_hashkey, NULL);
- g_hash_table_destroy(irc->userhash);
+ if( irc->ping_source_id > 0 )
+ b_event_remove( irc->ping_source_id );
+ if( irc->r_watch_source_id > 0 )
+ b_event_remove( irc->r_watch_source_id );
+ if( irc->w_watch_source_id > 0 )
+ b_event_remove( irc->w_watch_source_id );
+
+ closesocket( irc->fd );
+ irc->fd = -1;
+
+ g_hash_table_foreach_remove( irc->userhash, irc_free_hashkey, NULL );
+ g_hash_table_destroy( irc->userhash );
+
+ g_hash_table_foreach_remove( irc->watches, irc_free_hashkey, NULL );
+ g_hash_table_destroy( irc->watches );
+
+ if( irc->iconv != (GIConv) -1 )
+ g_iconv_close( irc->iconv );
+ if( irc->oconv != (GIConv) -1 )
+ g_iconv_close( irc->oconv );
+
+ g_free( irc->sendbuffer );
+ g_free( irc->readbuffer );
+
+ g_free( irc->nick );
+ g_free( irc->user );
+ g_free( irc->host );
+ g_free( irc->realname );
+ g_free( irc->password );
- g_hash_table_foreach_remove(irc->watches, irc_free_hashkey, NULL);
- g_hash_table_destroy(irc->watches);
+ g_free( irc->myhost );
+ g_free( irc->mynick );
- g_free(irc);
+ g_free( irc->channel );
+
+ g_free( irc->last_target );
+
+ g_free( irc );
if( global.conf->runmode == RUNMODE_INETD || global.conf->runmode == RUNMODE_FORKDAEMON )
b_main_quit();
@@ -285,7 +332,7 @@ void irc_setpass (irc_t *irc, const char *pass)
void irc_process( irc_t *irc )
{
- char **lines, *temp, **cmd, *cs;
+ char **lines, *temp, **cmd;
int i;
if( irc->readbuffer != NULL )
@@ -294,11 +341,10 @@ void irc_process( irc_t *irc )
for( i = 0; *lines[i] != '\0'; i ++ )
{
- char conv[IRC_MAX_LINE+1];
+ char *conv = NULL;
- /* [WvG] Because irc_tokenize splits at every newline, the lines[] list
- should end with an empty string. This is why this actually works.
- Took me a while to figure out, Maurits. :-P */
+ /* [WvG] If the last line isn't empty, it's an incomplete line and we
+ should wait for the rest to come in before processing it. */
if( lines[i+1] == NULL )
{
temp = g_strdup( lines[i] );
@@ -308,10 +354,14 @@ void irc_process( irc_t *irc )
break;
}
- if( ( cs = set_getstr( &irc->set, "charset" ) ) && g_strcasecmp( cs, "none" ) != 0 )
+ if( irc->iconv != (GIConv) -1 )
{
- conv[IRC_MAX_LINE] = 0;
- if( do_iconv( cs, "UTF-8", lines[i], conv, 0, IRC_MAX_LINE - 2 ) == -1 )
+ gsize bytes_read, bytes_written;
+
+ conv = g_convert_with_iconv( lines[i], -1, irc->iconv,
+ &bytes_read, &bytes_written, NULL );
+
+ if( conv == NULL || bytes_read != strlen( lines[i] ) )
{
/* GLib can do strange things if things are not in the expected charset,
so let's be a little bit paranoid here: */
@@ -323,15 +373,18 @@ void irc_process( irc_t *irc )
"that charset, or tell BitlBee which charset to "
"expect by changing the charset setting. See "
"`help set charset' for more information. Your "
- "message was ignored.", cs );
- *conv = 0;
+ "message was ignored.",
+ set_getstr( &irc->set, "charset" ) );
+
+ g_free( conv );
+ conv = NULL;
}
else
{
irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost,
"Warning: invalid characters received at login time." );
- strncpy( conv, lines[i], IRC_MAX_LINE );
+ conv = g_strdup( lines[i] );
for( temp = conv; *temp; temp ++ )
if( *temp & 0x80 )
*temp = '?';
@@ -340,11 +393,15 @@ void irc_process( irc_t *irc )
lines[i] = conv;
}
- if( ( cmd = irc_parse_line( lines[i] ) ) == NULL )
- continue;
- irc_exec( irc, cmd );
+ if( lines[i] )
+ {
+ if( ( cmd = irc_parse_line( lines[i] ) ) == NULL )
+ continue;
+ irc_exec( irc, cmd );
+ g_free( cmd );
+ }
- g_free( cmd );
+ g_free( conv );
/* Shouldn't really happen, but just in case... */
if( !g_slist_find( irc_connection_list, irc ) )
@@ -368,42 +425,41 @@ void irc_process( irc_t *irc )
contains an incomplete line at the end, ends with an empty string. */
char **irc_tokenize( char *buffer )
{
- int i, j;
+ int i, j, n = 3;
char **lines;
- /* Count the number of elements we're gonna need. */
- for( i = 0, j = 1; buffer[i] != '\0'; i ++ )
- {
- if( buffer[i] == '\n' )
- if( buffer[i+1] != '\r' && buffer[i+1] != '\n' )
- j ++;
- }
-
- /* Allocate j+1 elements. */
- lines = g_new( char *, j + 1 );
-
- /* NULL terminate our list. */
- lines[j] = NULL;
+ /* Allocate n+1 elements. */
+ lines = g_new( char *, n + 1 );
lines[0] = buffer;
- /* Split the buffer in several strings, using \r\n as our seperator, where \r is optional.
- * Although this is not in the RFC, some braindead ircds (newnet's) use this, so some clients might too.
- */
- for( i = 0, j = 0; buffer[i] != '\0'; i ++)
+ /* Split the buffer in several strings, and accept any kind of line endings,
+ * knowing that ERC on Windows may send something interesting like \r\r\n,
+ * and surely there must be clients that think just \n is enough... */
+ for( i = 0, j = 0; buffer[i] != '\0'; i ++ )
{
- if( buffer[i] == '\n' )
+ if( buffer[i] == '\r' || buffer[i] == '\n' )
{
- buffer[i] = '\0';
+ while( buffer[i] == '\r' || buffer[i] == '\n' )
+ buffer[i++] = '\0';
+
+ lines[++j] = buffer + i;
- if( i > 0 && buffer[i-1] == '\r' )
- buffer[i-1] = '\0';
- if( buffer[i+1] != '\r' && buffer[i+1] != '\n' )
- lines[++j] = buffer + i + 1;
+ if( j >= n )
+ {
+ n *= 2;
+ lines = g_renew( char *, lines, n + 1 );
+ }
+
+ if( buffer[i] == '\0' )
+ break;
}
}
- return( lines );
+ /* NULL terminate our list. */
+ lines[++j] = NULL;
+
+ return lines;
}
/* Split an IRC-style line into little parts/arguments. */
@@ -537,32 +593,35 @@ void irc_write( irc_t *irc, char *format, ... )
va_end( params );
return;
-
}
void irc_vawrite( irc_t *irc, char *format, va_list params )
{
int size;
- char line[IRC_MAX_LINE+1], *cs;
+ char line[IRC_MAX_LINE+1];
/* Don't try to write anything new anymore when shutting down. */
if( irc->status & USTATUS_SHUTDOWN )
return;
- line[IRC_MAX_LINE] = 0;
+ memset( line, 0, sizeof( line ) );
g_vsnprintf( line, IRC_MAX_LINE - 2, format, params );
-
strip_newlines( line );
- if( ( cs = set_getstr( &irc->set, "charset" ) ) &&
- g_strcasecmp( cs, "none" ) != 0 && g_strcasecmp( cs, "utf-8" ) != 0 )
+
+ if( irc->oconv != (GIConv) -1 )
{
- char conv[IRC_MAX_LINE+1];
+ gsize bytes_read, bytes_written;
+ char *conv;
+
+ conv = g_convert_with_iconv( line, -1, irc->oconv,
+ &bytes_read, &bytes_written, NULL );
+
+ if( bytes_read == strlen( line ) )
+ strncpy( line, conv, IRC_MAX_LINE - 2 );
- conv[IRC_MAX_LINE] = 0;
- if( do_iconv( "UTF-8", cs, line, conv, 0, IRC_MAX_LINE - 2 ) != -1 )
- strcpy( line, conv );
+ g_free( conv );
}
- strcat( line, "\r\n" );
+ g_strlcat( line, "\r\n", IRC_MAX_LINE + 1 );
if( irc->sendbuffer != NULL )
{
@@ -735,12 +794,27 @@ void irc_login( irc_t *irc )
u->online = 1;
irc_spawn( irc, u );
- irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\nIf you've never used BitlBee before, please do read the help information using the \x02help\x02 command. Lots of FAQs are answered there." );
+ irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\n"
+ "If you've never used BitlBee before, please do read the help "
+ "information using the \x02help\x02 command. Lots of FAQs are "
+ "answered there.\n"
+ "If you already have an account on this server, just use the "
+ "\x02identify\x02 command to identify yourself." );
if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON )
ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->host, irc->nick, irc->realname );
irc->status |= USTATUS_LOGGED_IN;
+
+ /* This is for bug #209 (use PASS to identify to NickServ). */
+ if( irc->password != NULL )
+ {
+ char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL };
+
+ irc_setpass( irc, NULL );
+ root_command( irc, send_cmd );
+ g_free( send_cmd[1] );
+ }
}
void irc_motd( irc_t *irc )
diff --git a/irc.h b/irc.h
index eb70ad1c..b8c52925 100644
--- a/irc.h
+++ b/irc.h
@@ -60,6 +60,7 @@ typedef struct irc
int pinging;
char *sendbuffer;
char *readbuffer;
+ GIConv iconv, oconv;
int sentbytes;
time_t oldtime;
@@ -68,7 +69,9 @@ typedef struct irc
char *user;
char *host;
char *realname;
- char *password;
+ char *password; /* HACK: Used to save the user's password, but before
+ logging in, this may contain a password we should
+ send to identify after USER/NICK are received. */
char umode[8];
diff --git a/irc_commands.c b/irc_commands.c
index b8bae541..6a47007a 100644
--- a/irc_commands.c
+++ b/irc_commands.c
@@ -29,7 +29,19 @@
static void irc_cmd_pass( irc_t *irc, char **cmd )
{
- if( global.conf->auth_pass &&
+ if( irc->status & USTATUS_LOGGED_IN )
+ {
+ char *send_cmd[] = { "identify", cmd[1], NULL };
+
+ /* We're already logged in, this client seems to send the PASS
+ command last. (Possibly it won't send it at all if it turns
+ out we don't require it, which will break this feature.)
+ Try to identify using the given password. */
+ return root_command( irc, send_cmd );
+ }
+ /* Handling in pre-logged-in state, first see if this server is
+ password-protected: */
+ else if( global.conf->auth_pass &&
( strncmp( global.conf->auth_pass, "md5:", 4 ) == 0 ?
md5_verify_password( cmd[1], global.conf->auth_pass + 4 ) == 0 :
strcmp( cmd[1], global.conf->auth_pass ) == 0 ) )
@@ -37,10 +49,16 @@ static void irc_cmd_pass( irc_t *irc, char **cmd )
irc->status |= USTATUS_AUTHORIZED;
irc_check_login( irc );
}
- else
+ else if( global.conf->auth_pass )
{
irc_reply( irc, 464, ":Incorrect password" );
}
+ else
+ {
+ /* Remember the password and try to identify after USER/NICK. */
+ irc_setpass( irc, cmd[1] );
+ irc_check_login( irc );
+ }
}
static void irc_cmd_user( irc_t *irc, char **cmd )
@@ -259,8 +277,7 @@ static void irc_cmd_privmsg( irc_t *irc, char **cmd )
if( cmd[1] != irc->last_target )
{
- if( irc->last_target )
- g_free( irc->last_target );
+ g_free( irc->last_target );
irc->last_target = g_strdup( cmd[1] );
}
}
@@ -580,7 +597,7 @@ static void irc_cmd_rehash( irc_t *irc, char **cmd )
}
static const command_t irc_commands[] = {
- { "pass", 1, irc_cmd_pass, IRC_CMD_PRE_LOGIN },
+ { "pass", 1, irc_cmd_pass, 0 },
{ "user", 4, irc_cmd_user, IRC_CMD_PRE_LOGIN },
{ "nick", 1, irc_cmd_nick, 0 },
{ "quit", 0, irc_cmd_quit, 0 },
diff --git a/lib/arc.h b/lib/arc.h
index 58f30d3d..816fa612 100644
--- a/lib/arc.h
+++ b/lib/arc.h
@@ -30,6 +30,10 @@ struct arc_state
unsigned char i, j;
};
+#ifndef G_GNUC_MALLOC
+#define G_GNUC_MALLOC
+#endif
+
G_GNUC_MALLOC struct arc_state *arc_keymaker( unsigned char *key, int kl, int cycles );
unsigned char arc_getbyte( struct arc_state *st );
int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password, int pad_to );
diff --git a/lib/proxy.c b/lib/proxy.c
index 53b89d64..91493557 100644
--- a/lib/proxy.c
+++ b/lib/proxy.c
@@ -113,6 +113,7 @@ static gboolean gaim_io_connected(gpointer data, gint source, b_input_condition
static int proxy_connect_none(const char *host, unsigned short port, struct PHB *phb)
{
struct sockaddr_in *sin;
+ struct sockaddr_in me;
int fd = -1;
if (!(sin = gaim_gethostbyname(host, port))) {
@@ -127,6 +128,16 @@ static int proxy_connect_none(const char *host, unsigned short port, struct PHB
sock_make_nonblocking(fd);
+ if( global.conf->iface_out )
+ {
+ me.sin_family = AF_INET;
+ me.sin_port = 0;
+ me.sin_addr.s_addr = inet_addr( global.conf->iface_out );
+
+ if( bind( fd, (struct sockaddr *) &me, sizeof( me ) ) != 0 )
+ event_debug( "bind( %d, \"%s\" ) failure\n", fd, global.conf->iface_out );
+ }
+
event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host, port, fd);
if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0 && !sockerr_again()) {
diff --git a/protocols/jabber/jabber_util.c b/protocols/jabber/jabber_util.c
index 6e872040..518624f6 100644
--- a/protocols/jabber/jabber_util.c
+++ b/protocols/jabber/jabber_util.c
@@ -245,8 +245,10 @@ struct jabber_buddy_ask_data
char *realname;
};
-static void jabber_buddy_ask_yes( gpointer w, struct jabber_buddy_ask_data *bla )
+static void jabber_buddy_ask_yes( void *data )
{
+ struct jabber_buddy_ask_data *bla = data;
+
presence_send_request( bla->ic, bla->handle, "subscribed" );
if( imcb_find_buddy( bla->ic, bla->handle ) == NULL )
@@ -256,8 +258,10 @@ static void jabber_buddy_ask_yes( gpointer w, struct jabber_buddy_ask_data *bla
g_free( bla );
}
-static void jabber_buddy_ask_no( gpointer w, struct jabber_buddy_ask_data *bla )
+static void jabber_buddy_ask_no( void *data )
{
+ struct jabber_buddy_ask_data *bla = data;
+
presence_send_request( bla->ic, bla->handle, "subscribed" );
g_free( bla->handle );
diff --git a/protocols/msn/msn.h b/protocols/msn/msn.h
index c8f4f4c6..63759303 100644
--- a/protocols/msn/msn.h
+++ b/protocols/msn/msn.h
@@ -28,7 +28,7 @@
#define TYPING_NOTIFICATION_MESSAGE "\r\r\rBEWARE, ME R TYPINK MESSAGE!!!!\r\r\r"
#define GROUPCHAT_SWITCHBOARD_MESSAGE "\r\r\rME WANT TALK TO MANY PEOPLE\r\r\r"
-#ifdef DEBUG
+#ifdef DEBUG_MSN
#define debug( text... ) imcb_log( ic, text );
#else
#define debug( text... )
diff --git a/protocols/msn/msn_util.c b/protocols/msn/msn_util.c
index fae2877d..58ad22f8 100644
--- a/protocols/msn/msn_util.c
+++ b/protocols/msn/msn_util.c
@@ -89,8 +89,10 @@ struct msn_buddy_ask_data
char *realname;
};
-static void msn_buddy_ask_yes( gpointer w, struct msn_buddy_ask_data *bla )
+static void msn_buddy_ask_yes( void *data )
{
+ struct msn_buddy_ask_data *bla = data;
+
msn_buddy_list_add( bla->ic, "AL", bla->handle, bla->realname );
if( imcb_find_buddy( bla->ic, bla->handle ) == NULL )
@@ -101,8 +103,10 @@ static void msn_buddy_ask_yes( gpointer w, struct msn_buddy_ask_data *bla )
g_free( bla );
}
-static void msn_buddy_ask_no( gpointer w, struct msn_buddy_ask_data *bla )
+static void msn_buddy_ask_no( void *data )
{
+ struct msn_buddy_ask_data *bla = data;
+
msn_buddy_list_add( bla->ic, "BL", bla->handle, bla->realname );
g_free( bla->handle );
diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c
index ff7da6ed..ffaa90a7 100644
--- a/protocols/msn/ns.c
+++ b/protocols/msn/ns.c
@@ -177,7 +177,15 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )
}
debug( "Connecting to a new switchboard with key %s", cmd[5] );
- sb = msn_sb_create( ic, server, port, cmd[5], MSN_SB_NEW );
+
+ if( ( sb = msn_sb_create( ic, server, port, cmd[5], MSN_SB_NEW ) ) == NULL )
+ {
+ /* Although this isn't strictly fatal for the NS connection, it's
+ definitely something serious (we ran out of file descriptors?). */
+ imcb_error( ic, "Could not create new switchboard" );
+ imc_logout( ic, TRUE );
+ return( 0 );
+ }
if( md->msgq )
{
@@ -467,8 +475,18 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )
debug( "Got a call from %s (session %d). Key = %s", cmd[5], session, cmd[4] );
- sb = msn_sb_create( ic, server, port, cmd[4], session );
- sb->who = g_strdup( cmd[5] );
+ if( ( sb = msn_sb_create( ic, server, port, cmd[4], session ) ) == NULL )
+ {
+ /* Although this isn't strictly fatal for the NS connection, it's
+ definitely something serious (we ran out of file descriptors?). */
+ imcb_error( ic, "Could not create new switchboard" );
+ imc_logout( ic, TRUE );
+ return( 0 );
+ }
+ else
+ {
+ sb->who = g_strdup( cmd[5] );
+ }
}
else if( strcmp( cmd[0], "ADD" ) == 0 )
{
diff --git a/protocols/nogaim.c b/protocols/nogaim.c
index 3ce15166..7466e93a 100644
--- a/protocols/nogaim.c
+++ b/protocols/nogaim.c
@@ -342,7 +342,8 @@ void imc_logout( struct im_connection *ic, int allow_reconnect )
/* dialogs.c */
-void imcb_ask( struct im_connection *ic, char *msg, void *data, void *doit, void *dont )
+void imcb_ask( struct im_connection *ic, char *msg, void *data,
+ query_callback doit, query_callback dont )
{
query_add( ic->irc, ic, msg, doit, dont, data );
}
@@ -494,18 +495,20 @@ struct show_got_added_data
char *handle;
};
-void show_got_added_no( gpointer w, struct show_got_added_data *data )
+void show_got_added_no( void *data )
{
- g_free( data->handle );
+ g_free( ((struct show_got_added_data*)data)->handle );
g_free( data );
}
-void show_got_added_yes( gpointer w, struct show_got_added_data *data )
+void show_got_added_yes( void *data )
{
- data->ic->acc->prpl->add_buddy( data->ic, data->handle, NULL );
- /* imcb_add_buddy( data->ic, NULL, data->handle, data->handle ); */
+ struct show_got_added_data *sga = data;
- return show_got_added_no( w, data );
+ sga->ic->acc->prpl->add_buddy( sga->ic, sga->handle, NULL );
+ /* imcb_add_buddy( sga->ic, NULL, sga->handle, sga->handle ); */
+
+ return show_got_added_no( data );
}
void imcb_ask_add( struct im_connection *ic, char *handle, const char *realname )
diff --git a/protocols/nogaim.h b/protocols/nogaim.h
index 7d391edd..bdd8bae2 100644
--- a/protocols/nogaim.h
+++ b/protocols/nogaim.h
@@ -41,6 +41,7 @@
#include "bitlbee.h"
#include "account.h"
#include "proxy.h"
+#include "query.h"
#include "md5.h"
#define BUF_LEN MSG_LEN
@@ -260,7 +261,7 @@ G_MODULE_EXPORT void imcb_error( struct im_connection *ic, char *format, ... ) G
* - 'data' can be your custom struct - it will be passed to the callbacks.
* - 'doit' or 'dont' will be called depending of the answer of the user.
*/
-G_MODULE_EXPORT void imcb_ask( struct im_connection *ic, char *msg, void *data, void *doit, void *dont );
+G_MODULE_EXPORT void imcb_ask( struct im_connection *ic, char *msg, void *data, query_callback doit, query_callback dont );
G_MODULE_EXPORT void imcb_ask_add( struct im_connection *ic, char *handle, const char *realname );
/* Buddy management */
diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c
index 9e5de70a..7738c31f 100644
--- a/protocols/oscar/oscar.c
+++ b/protocols/oscar/oscar.c
@@ -1083,8 +1083,8 @@ static int incomingim_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_
return 1;
}
-void oscar_accept_chat(gpointer w, struct aim_chat_invitation * inv);
-void oscar_reject_chat(gpointer w, struct aim_chat_invitation * inv);
+void oscar_accept_chat(void *data);
+void oscar_reject_chat(void *data);
static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) {
struct im_connection *ic = sess->aux_data;
@@ -1118,7 +1118,8 @@ static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_
return 1;
}
-static void gaim_icq_authgrant(gpointer w, struct icq_auth *data) {
+static void gaim_icq_authgrant(void *data_) {
+ struct icq_auth *data = data_;
char *uin, message;
struct oscar_data *od = (struct oscar_data *)data->ic->proto_data;
@@ -1133,7 +1134,8 @@ static void gaim_icq_authgrant(gpointer w, struct icq_auth *data) {
g_free(data);
}
-static void gaim_icq_authdeny(gpointer w, struct icq_auth *data) {
+static void gaim_icq_authdeny(void *data_) {
+ struct icq_auth *data = data_;
char *uin, *message;
struct oscar_data *od = (struct oscar_data *)data->ic->proto_data;
@@ -2587,15 +2589,19 @@ struct groupchat *oscar_chat_with(struct im_connection * ic, char *who)
return NULL;
}
-void oscar_accept_chat(gpointer w, struct aim_chat_invitation * inv)
+void oscar_accept_chat(void *data)
{
+ struct aim_chat_invitation * inv = data;
+
oscar_chat_join(inv->ic, inv->name, NULL, NULL);
g_free(inv->name);
g_free(inv);
}
-void oscar_reject_chat(gpointer w, struct aim_chat_invitation * inv)
+void oscar_reject_chat(void *data)
{
+ struct aim_chat_invitation * inv = data;
+
g_free(inv->name);
g_free(inv);
}
diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c
index 36579d66..ab30df4d 100644
--- a/protocols/yahoo/yahoo.c
+++ b/protocols/yahoo/yahoo.c
@@ -796,16 +796,20 @@ int ext_yahoo_connect(const char *host, int port)
return -1;
}
-static void byahoo_accept_conf( gpointer w, struct byahoo_conf_invitation *inv )
+static void byahoo_accept_conf( void *data )
{
+ struct byahoo_conf_invitation *inv = data;
+
yahoo_conference_logon( inv->yid, NULL, inv->members, inv->name );
imcb_chat_add_buddy( inv->c, inv->ic->acc->user );
g_free( inv->name );
g_free( inv );
}
-static void byahoo_reject_conf( gpointer w, struct byahoo_conf_invitation *inv )
+static void byahoo_reject_conf( void *data )
{
+ struct byahoo_conf_invitation *inv = data;
+
yahoo_conference_decline( inv->yid, NULL, inv->members, inv->name, "User rejected groupchat" );
imcb_chat_free( inv->c );
g_free( inv->name );
diff --git a/query.c b/query.c
index 6f9eb77f..e8f69572 100644
--- a/query.c
+++ b/query.c
@@ -29,7 +29,8 @@
static void query_display( irc_t *irc, query_t *q );
static query_t *query_default( irc_t *irc );
-query_t *query_add( irc_t *irc, struct im_connection *ic, char *question, void *yes, void *no, void *data )
+query_t *query_add( irc_t *irc, struct im_connection *ic, char *question,
+ query_callback yes, query_callback no, void *data )
{
query_t *q = g_new0( query_t, 1 );
@@ -143,7 +144,7 @@ void query_answer( irc_t *irc, query_t *q, int ans )
imcb_log( q->ic, "Accepted: %s", q->question );
else
irc_usermsg( irc, "Accepted: %s", q->question );
- q->yes( NULL, q->data );
+ q->yes( q->data );
}
else
{
@@ -151,7 +152,7 @@ void query_answer( irc_t *irc, query_t *q, int ans )
imcb_log( q->ic, "Rejected: %s", q->question );
else
irc_usermsg( irc, "Rejected: %s", q->question );
- q->no( NULL, q->data );
+ q->no( q->data );
}
q->data = NULL;
diff --git a/query.h b/query.h
index b64642c2..e0ca32ec 100644
--- a/query.h
+++ b/query.h
@@ -26,17 +26,19 @@
#ifndef _QUERY_H
#define _QUERY_H
+typedef void (*query_callback) ( void *data );
+
typedef struct query
{
struct im_connection *ic;
char *question;
- void (* yes) ( gpointer w, void *data );
- void (* no) ( gpointer w, void *data );
+ query_callback yes, no;
void *data;
struct query *next;
} query_t;
-query_t *query_add( irc_t *irc, struct im_connection *ic, char *question, void *yes, void *no, void *data );
+query_t *query_add( irc_t *irc, struct im_connection *ic, char *question,
+ query_callback yes, query_callback no, void *data );
void query_del( irc_t *irc, query_t *q );
void query_del_by_conn( irc_t *irc, struct im_connection *ic );
void query_answer( irc_t *irc, query_t *q, int ans );
diff --git a/root_commands.c b/root_commands.c
index 9a60b5af..f55c4b5e 100644
--- a/root_commands.c
+++ b/root_commands.c
@@ -203,24 +203,38 @@ static void cmd_drop( irc_t *irc, char **cmd )
}
}
-void cmd_account_del_yes( gpointer w, void *data )
+struct cmd_account_del_data
{
- account_t *a = data;
- irc_t *irc = a->irc;
+ account_t *a;
+ irc_t *irc;
+};
+
+void cmd_account_del_yes( void *data )
+{
+ struct cmd_account_del_data *cad = data;
+ account_t *a;
+
+ for( a = cad->irc->accounts; a && a != cad->a; a = a->next );
- if( a->ic )
+ if( a == NULL )
{
- irc_usermsg( irc, "Account is still logged in, can't delete" );
+ irc_usermsg( cad->irc, "Account already deleted" );
+ }
+ else if( a->ic )
+ {
+ irc_usermsg( cad->irc, "Account is still logged in, can't delete" );
}
else
{
- account_del( irc, a );
- irc_usermsg( irc, "Account deleted" );
+ account_del( cad->irc, a );
+ irc_usermsg( cad->irc, "Account deleted" );
}
+ g_free( data );
}
-void cmd_account_del_no( gpointer w, void *data )
+void cmd_account_del_no( void *data )
{
+ g_free( data );
}
static void cmd_account( irc_t *irc, char **cmd )
@@ -277,14 +291,19 @@ static void cmd_account( irc_t *irc, char **cmd )
}
else
{
+ struct cmd_account_del_data *cad;
char *msg;
+ cad = g_malloc( sizeof( struct cmd_account_del_data ) );
+ cad->a = a;
+ cad->irc = irc;
+
msg = g_strdup_printf( "If you remove this account (%s(%s)), BitlBee will "
"also forget all your saved nicknames. If you want "
"to change your username/password, use the `account "
"set' command. Are you sure you want to delete this "
"account?", a->prpl->name, a->user );
- query_add( irc, NULL, msg, cmd_account_del_yes, cmd_account_del_no, a );
+ query_add( irc, NULL, msg, cmd_account_del_yes, cmd_account_del_no, cad );
g_free( msg );
}
}
@@ -403,6 +422,12 @@ static void cmd_account( irc_t *irc, char **cmd )
else
acc_handle = g_strdup( cmd[2] );
+ if( !acc_handle )
+ {
+ irc_usermsg( irc, "Not enough parameters given (need %d)", 3 );
+ return;
+ }
+
if( ( tmp = strchr( acc_handle, '/' ) ) )
{
*tmp = 0;
@@ -583,6 +608,9 @@ static void cmd_rename( irc_t *irc, char **cmd )
{
g_free( irc->mynick );
irc->mynick = g_strdup( cmd[2] );
+
+ if( strcmp( cmd[0], "set_rename" ) != 0 )
+ set_setstr( &irc->set, "root_nick", cmd[2] );
}
else if( u->send_handler == buddy_send_handler )
{
@@ -593,6 +621,20 @@ static void cmd_rename( irc_t *irc, char **cmd )
}
}
+char *set_eval_root_nick( set_t *set, char *new_nick )
+{
+ irc_t *irc = set->data;
+
+ if( strcmp( irc->mynick, new_nick ) != 0 )
+ {
+ char *cmd[] = { "set_rename", irc->mynick, new_nick, NULL };
+
+ cmd_rename( irc, cmd );
+ }
+
+ return strcmp( irc->mynick, new_nick ) == 0 ? new_nick : NULL;
+}
+
static void cmd_remove( irc_t *irc, char **cmd )
{
user_t *u;
diff --git a/set.c b/set.c
index 90b29f91..112e6937 100644
--- a/set.c
+++ b/set.c
@@ -229,18 +229,3 @@ char *set_eval_ops( set_t *set, char *value )
return value;
}
-
-char *set_eval_charset( set_t *set, char *value )
-{
- GIConv cd;
-
- if( g_strcasecmp( value, "none" ) == 0 )
- return value;
-
- cd = g_iconv_open( "UTF-8", value );
- if( cd == (GIConv) -1 )
- return NULL;
-
- g_iconv_close( cd );
- return value;
-}
diff --git a/set.h b/set.h
index 7dcbb869..8c722877 100644
--- a/set.h
+++ b/set.h
@@ -96,6 +96,5 @@ char *set_eval_bool( set_t *set, char *value );
/* Some not very generic evaluators that really shouldn't be here... */
char *set_eval_to_char( set_t *set, char *value );
char *set_eval_ops( set_t *set, char *value );
-char *set_eval_charset( set_t *set, char *value );
#endif /* __SET_H__ */
diff --git a/tests/check_irc.c b/tests/check_irc.c
index c1cf05a5..66fe0021 100644
--- a/tests/check_irc.c
+++ b/tests/check_irc.c
@@ -36,8 +36,8 @@ START_TEST(test_login)
irc = irc_new(g_io_channel_unix_get_fd(ch1));
- fail_unless(g_io_channel_write_chars(ch2, "NICK bla\r\n"
- "USER a a a a\r\n", -1, NULL, NULL) == G_IO_STATUS_NORMAL);
+ fail_unless(g_io_channel_write_chars(ch2, "NICK bla\r\r\n"
+ "USER a a a a\n", -1, NULL, NULL) == G_IO_STATUS_NORMAL);
fail_unless(g_io_channel_flush(ch2, NULL) == G_IO_STATUS_NORMAL);
g_main_iteration(FALSE);