diff options
-rw-r--r-- | protocols/jabber/io.c | 67 | ||||
-rw-r--r-- | protocols/jabber/iq.c | 2 | ||||
-rw-r--r-- | protocols/jabber/jabber.h | 4 | ||||
-rw-r--r-- | protocols/jabber/sasl.c | 20 | ||||
-rw-r--r-- | protocols/nogaim.c | 5 |
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 ); |