diff options
author | Wilmer van der Gaast <wilmer@gaast.net> | 2006-09-20 21:42:27 +0200 |
---|---|---|
committer | Wilmer van der Gaast <wilmer@gaast.net> | 2006-09-20 21:42:27 +0200 |
commit | 21167d2d14c333d67445546bb69dd52dd295287d (patch) | |
tree | 50c010702a3487c666ad8e614b25879de258d9cf /protocols/jabber/io.c | |
parent | f06894d8f55b50b632c1d81ad878f8581273ba66 (diff) |
It can send a valid (pre-XMPP) login packet. Lots of work to do, still...
Diffstat (limited to 'protocols/jabber/io.c')
-rw-r--r-- | protocols/jabber/io.c | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/protocols/jabber/io.c b/protocols/jabber/io.c new file mode 100644 index 00000000..db869714 --- /dev/null +++ b/protocols/jabber/io.c @@ -0,0 +1,237 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Jabber module - I/O stuff (plain, SSL), queues, etc * +* * +* Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + +#include "jabber.h" + +static gboolean jabber_write_callback( gpointer data, gint fd, b_input_condition cond ); + +int jabber_write_packet( struct gaim_connection *gc, struct xt_node *node ) +{ + char *buf; + int st; + + buf = xt_to_string( node ); + st = jabber_write( gc, buf, strlen( buf ) ); + g_free( buf ); + + return st; +} + +int jabber_write( struct gaim_connection *gc, char *buf, int len ) +{ + struct jabber_data *jd = gc->proto_data; + + if( jd->tx_len == 0 ) + { + /* If the queue is empty, allocate a new buffer. */ + jd->tx_len = len; + jd->txq = g_memdup( buf, len ); + + /* Try if we can write it immediately so we don't have to do + it via the event handler. If not, add the handler. (In + most cases it probably won't be necessary.) */ + if( jabber_write_callback( gc, jd->fd, GAIM_INPUT_WRITE ) ) + jd->w_inpa = b_input_add( jd->fd, GAIM_INPUT_WRITE, jabber_write_callback, gc ); + } + else + { + /* Just add it to the buffer if it's already filled. The + event handler is already set. */ + jd->txq = g_renew( char, jd->txq, jd->tx_len + len ); + memcpy( jd->txq + jd->tx_len, buf, len ); + jd->tx_len += len; + } + + /* FIXME: write_callback could've generated a real error! */ + return 1; +} + +static gboolean jabber_write_callback( gpointer data, gint fd, b_input_condition cond ) +{ + struct gaim_connection *gc = data; + struct jabber_data *jd = gc->proto_data; + int st; + + st = write( jd->fd, jd->txq, jd->tx_len ); + + if( st == jd->tx_len ) + { + /* We wrote everything, clear the buffer. */ + g_free( jd->txq ); + jd->txq = NULL; + jd->tx_len = 0; + + return FALSE; + } + else if( st == 0 || ( st < 0 && !sockerr_again() ) ) + { + hide_login_progress_error( gc, "Short write() to server" ); + signoff( gc ); + return FALSE; + } + else if( st > 0 ) + { + char *s; + + s = g_memdup( jd->txq + st, jd->tx_len - st ); + jd->tx_len -= st; + g_free( jd->txq ); + jd->txq = s; + + return FALSE; + } + else + { + /* Just in case we had EINPROGRESS/EAGAIN: */ + + return TRUE; + } +} + +static gboolean jabber_read_callback( gpointer data, gint fd, b_input_condition cond ) +{ + struct gaim_connection *gc = data; + struct jabber_data *jd = gc->proto_data; + char buf[512]; + int st; + + st = read( fd, buf, sizeof( buf ) ); + + if( st > 0 ) + { + /* Parse. */ + if( !xt_feed( jd->xt, buf, st ) ) + { + hide_login_progress_error( gc, "XML stream error" ); + signoff( gc ); + return FALSE; + } + + /* Execute all handlers. */ + if( !xt_handle( jd->xt, NULL ) ) + { + /* Don't do anything, the handlers should have + aborted the connection already... Or not? FIXME */ + return FALSE; + } + + /* Garbage collection. */ + xt_cleanup( jd->xt, NULL ); + + /* This is a bit hackish, unfortunately. Although xmltree + has nifty event handler stuff, it only calls handlers + when nodes are complete. Since the server should only + send an opening <stream:stream> tag, we have to check + this by hand. :-( */ + if( !( jd->flags & JFLAG_STREAM_STARTED ) && jd->xt && jd->xt->root ) + { + if( g_strcasecmp( jd->xt->root->name, "stream:stream" ) == 0 ) + { + jd->flags |= JFLAG_STREAM_STARTED; + return jabber_start_auth( gc ); + } + else + { + hide_login_progress_error( gc, "XML stream error" ); + signoff( gc ); + return FALSE; + } + } + } + else if( st == 0 || ( st < 0 && !sockerr_again() ) ) + { + hide_login_progress_error( gc, "Error while reading from server" ); + signoff( gc ); + return FALSE; + } + + /* EAGAIN/etc or a successful read. */ + return TRUE; +} + +static gboolean jabber_start_stream( struct gaim_connection *gc ); + +gboolean jabber_connected_plain( gpointer data, gint source, b_input_condition cond ) +{ + struct gaim_connection *gc = data; + + if( source == -1 ) + { + hide_login_progress( gc, "Could not connect to server" ); + signoff( gc ); + return FALSE; + } + + set_login_progress( gc, 1, "Connected to server, logging in" ); + + return jabber_start_stream( gc ); +} + +static xt_status jabber_end_of_stream( struct xt_node *node, gpointer data ) +{ + return XT_ABORT; +} + +static xt_status jabber_pkt_misc( struct xt_node *node, gpointer data ) +{ + printf( "Received unknown packet:\n" ); + xt_print( node ); + + return XT_HANDLED; +} + +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 }, + { NULL, "stream:stream", jabber_pkt_misc }, + { NULL, NULL, NULL } +}; + +static gboolean jabber_start_stream( struct gaim_connection *gc ) +{ + struct jabber_data *jd = gc->proto_data; + int st; + char *greet; + + /* We'll start our stream now, so prepare everything to receive one + from the server too. */ + xt_free( jd->xt ); /* In case we're RE-starting. */ + 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 ); + + greet = g_strdup_printf( "<?xml version='1.0' ?>" + "<stream:stream to=\"%s\" xmlns=\"jabber:client\" " + "xmlns:stream=\"http://etherx.jabber.org/streams\">", jd->server ); + /* Add this when TLS and SASL are supported? */ + // version=\"1.0\">" + + st = jabber_write( gc, greet, strlen( greet ) ); + + g_free( greet ); + + return st; +} |