aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--protocols/jabber/io.c67
-rw-r--r--protocols/jabber/iq.c2
-rw-r--r--protocols/jabber/jabber.h4
-rw-r--r--protocols/jabber/sasl.c20
-rw-r--r--protocols/nogaim.c5
5 files changed, 86 insertions, 12 deletions
diff --git a/protocols/jabber/io.c b/protocols/jabber/io.c
index c7c1d8d9..8f2ce0f1 100644
--- a/protocols/jabber/io.c
+++ b/protocols/jabber/io.c
@@ -72,6 +72,9 @@ static gboolean jabber_write_callback( gpointer data, gint fd, b_input_condition
struct jabber_data *jd = gc->proto_data;
int st;
+ if( jd->fd == -1 )
+ return FALSE;
+
st = write( jd->fd, jd->txq, jd->tx_len );
if( st == jd->tx_len )
@@ -85,6 +88,10 @@ static gboolean jabber_write_callback( gpointer data, gint fd, b_input_condition
}
else if( st == 0 || ( st < 0 && !sockerr_again() ) )
{
+ /* Set fd to -1 to make sure we won't write to it anymore. */
+ closesocket( jd->fd ); /* Shouldn't be necessary after errors? */
+ jd->fd = -1;
+
hide_login_progress_error( gc, "Short write() to server" );
signoff( gc );
return FALSE;
@@ -98,7 +105,7 @@ static gboolean jabber_write_callback( gpointer data, gint fd, b_input_condition
g_free( jd->txq );
jd->txq = s;
- return FALSE;
+ return TRUE;
}
else
{
@@ -115,6 +122,9 @@ static gboolean jabber_read_callback( gpointer data, gint fd, b_input_condition
char buf[512];
int st;
+ if( jd->fd == -1 )
+ return FALSE;
+
st = read( fd, buf, sizeof( buf ) );
if( st > 0 )
@@ -138,7 +148,6 @@ static gboolean jabber_read_callback( gpointer data, gint fd, b_input_condition
if( jd->flags & JFLAG_STREAM_RESTART )
{
jd->flags &= ~JFLAG_STREAM_RESTART;
- xt_reset( jd->xt );
jabber_start_stream( gc );
}
@@ -155,7 +164,12 @@ static gboolean jabber_read_callback( gpointer data, gint fd, b_input_condition
if( g_strcasecmp( jd->xt->root->name, "stream:stream" ) == 0 )
{
jd->flags |= JFLAG_STREAM_STARTED;
- return jabber_start_auth( gc );
+
+ /* If there's no version attribute, assume
+ this is an old server that can't do SASL
+ authentication. */
+ if( !sasl_supported( gc ) )
+ return jabber_start_iq_auth( gc );
}
else
{
@@ -167,6 +181,9 @@ static gboolean jabber_read_callback( gpointer data, gint fd, b_input_condition
}
else if( st == 0 || ( st < 0 && !sockerr_again() ) )
{
+ closesocket( jd->fd );
+ jd->fd = -1;
+
hide_login_progress_error( gc, "Error while reading from server" );
signoff( gc );
return FALSE;
@@ -197,6 +214,33 @@ static xt_status jabber_end_of_stream( struct xt_node *node, gpointer data )
return XT_ABORT;
}
+static xt_status jabber_pkt_features( struct xt_node *node, gpointer data )
+{
+ struct gaim_connection *gc = data;
+ struct jabber_data *jd = gc->proto_data;
+ struct xt_node *c;
+
+ c = xt_find_node( node->children, "starttls" );
+ if( c )
+ {
+ /*
+ jd->flags |= JFLAG_SUPPORTS_TLS;
+ if( xt_find_node( c->children, "required" ) )
+ jd->flags |= JFLAG_REQUIRES_TLS;
+ */
+ }
+
+ /* This flag is already set if we authenticated via SASL, so now
+ we can resume the session in the new stream. */
+ if( jd->flags & JFLAG_AUTHENTICATED )
+ {
+ if( !jabber_get_roster( gc ) )
+ return XT_ABORT;
+ }
+
+ return XT_HANDLED;
+}
+
static xt_status jabber_pkt_misc( struct xt_node *node, gpointer data )
{
printf( "Received unknown packet:\n" );
@@ -207,9 +251,10 @@ static xt_status jabber_pkt_misc( struct xt_node *node, gpointer data )
static const struct xt_handler_entry jabber_handlers[] = {
{ "stream:stream", "<root>", jabber_end_of_stream },
- { "iq", "stream:stream", jabber_pkt_iq },
{ "message", "stream:stream", jabber_pkt_message },
{ "presence", "stream:stream", jabber_pkt_presence },
+ { "iq", "stream:stream", jabber_pkt_iq },
+ { "stream:features", "stream:stream", jabber_pkt_features },
{ "mechanisms", "stream:features", sasl_pkt_mechanisms },
{ "challenge", "stream:stream", sasl_pkt_challenge },
{ "success", "stream:stream", sasl_pkt_result },
@@ -230,7 +275,8 @@ gboolean jabber_start_stream( struct gaim_connection *gc )
jd->xt = xt_new( gc );
jd->xt->handlers = (struct xt_handler_entry*) jabber_handlers;
- jd->r_inpa = b_input_add( jd->fd, GAIM_INPUT_READ, jabber_read_callback, gc );
+ if( jd->r_inpa <= 0 )
+ jd->r_inpa = b_input_add( jd->fd, GAIM_INPUT_READ, jabber_read_callback, gc );
greet = g_strdup_printf( "<?xml version='1.0' ?>"
"<stream:stream to=\"%s\" xmlns=\"jabber:client\" "
@@ -253,11 +299,14 @@ void jabber_end_stream( struct gaim_connection *gc )
{
char eos[] = "</stream:stream>";
struct xt_node *node;
- int st;
+ int st = 1;
- node = jabber_make_packet( "presence", "unavailable", NULL, NULL );
- st = jabber_write_packet( gc, node );
- xt_free_node( node );
+ if( gc->flags & OPT_LOGGED_IN )
+ {
+ node = jabber_make_packet( "presence", "unavailable", NULL, NULL );
+ st = jabber_write_packet( gc, node );
+ xt_free_node( node );
+ }
if( st )
jabber_write( gc, eos, strlen( eos ) );
diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c
index 119176f9..bcce5289 100644
--- a/protocols/jabber/iq.c
+++ b/protocols/jabber/iq.c
@@ -129,7 +129,7 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data )
return XT_HANDLED;
}
-int jabber_start_auth( struct gaim_connection *gc )
+int jabber_start_iq_auth( struct gaim_connection *gc )
{
struct jabber_data *jd = gc->proto_data;
struct xt_node *node;
diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h
index 03fe57a7..f8b604b0 100644
--- a/protocols/jabber/jabber.h
+++ b/protocols/jabber/jabber.h
@@ -34,12 +34,11 @@ typedef enum
JFLAG_STREAM_STARTED = 1, /* Set when we detected the beginning of the stream and want to do auth. */
JFLAG_AUTHENTICATED = 2, /* Set when we're successfully authenticatd. */
JFLAG_STREAM_RESTART = 4, /* Set when we want to restart the stream (after SASL or TLS). */
- JFLAG_SUPPORTS_TLS = 8, /* Set when there's <starttls/> in <stream:features>. */
} jabber_flags_t;
/* iq.c */
xt_status jabber_pkt_iq( struct xt_node *node, gpointer data );
-int jabber_start_auth( struct gaim_connection *gc );
+int jabber_start_iq_auth( struct gaim_connection *gc );
int jabber_get_roster( struct gaim_connection *gc );
xt_status jabber_pkt_message( struct xt_node *node, gpointer data );
@@ -65,6 +64,7 @@ void jabber_end_stream( struct gaim_connection *gc );
xt_status sasl_pkt_mechanisms( struct xt_node *node, gpointer data );
xt_status sasl_pkt_challenge( struct xt_node *node, gpointer data );
xt_status sasl_pkt_result( struct xt_node *node, gpointer data );
+gboolean sasl_supported( struct gaim_connection *gc );
struct jabber_data
{
diff --git a/protocols/jabber/sasl.c b/protocols/jabber/sasl.c
index da577877..2f75e733 100644
--- a/protocols/jabber/sasl.c
+++ b/protocols/jabber/sasl.c
@@ -34,6 +34,15 @@ xt_status sasl_pkt_mechanisms( struct xt_node *node, gpointer data )
char *s;
int sup_plain = 0, sup_digest = 0;
+ if( !sasl_supported( gc ) )
+ {
+ /* Should abort this now, since we should already be doing
+ IQ authentication. Strange things happen when you try
+ to do both... */
+ serv_got_crap( gc, "XMPP 1.0 non-compliant server seems to support SASL, please report this as a BitlBee bug!" );
+ return XT_HANDLED;
+ }
+
s = xt_find_attr( node, "xmlns" );
if( !s || strcmp( s, SASL_NS ) != 0 )
{
@@ -93,6 +102,7 @@ xt_status sasl_pkt_mechanisms( struct xt_node *node, gpointer data )
xt_status sasl_pkt_challenge( struct xt_node *node, gpointer data )
{
+ return XT_HANDLED;
}
xt_status sasl_pkt_result( struct xt_node *node, gpointer data )
@@ -122,3 +132,13 @@ xt_status sasl_pkt_result( struct xt_node *node, gpointer data )
return XT_HANDLED;
}
+
+/* This one is needed to judge if we'll do authentication using IQ or SASL.
+ It's done by checking if the <stream:stream> from the server has a
+ version attribute. I don't know if this is the right way though... */
+gboolean sasl_supported( struct gaim_connection *gc )
+{
+ struct jabber_data *jd = gc->proto_data;
+
+ return ( jd->xt && jd->xt->root && xt_find_attr( jd->xt->root, "version" ) ) != NULL;
+}
diff --git a/protocols/nogaim.c b/protocols/nogaim.c
index 47e2bda6..ec87ccd5 100644
--- a/protocols/nogaim.c
+++ b/protocols/nogaim.c
@@ -279,6 +279,11 @@ void signoff( struct gaim_connection *gc )
user_t *t, *u = irc->users;
account_t *a;
+ /* Nested calls might happen sometimes, this is probably the best
+ place to catch them. */
+ if( gc->flags & OPT_LOGGING_OUT )
+ return;
+
serv_got_crap( gc, "Signing off.." );
b_event_remove( gc->keepalive );