aboutsummaryrefslogtreecommitdiffstats
path: root/protocols/jabber/io.c
diff options
context:
space:
mode:
authorWilmer van der Gaast <wilmer@gaast.net>2006-09-20 21:42:27 +0200
committerWilmer van der Gaast <wilmer@gaast.net>2006-09-20 21:42:27 +0200
commit21167d2d14c333d67445546bb69dd52dd295287d (patch)
tree50c010702a3487c666ad8e614b25879de258d9cf /protocols/jabber/io.c
parentf06894d8f55b50b632c1d81ad878f8581273ba66 (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.c237
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;
+}