aboutsummaryrefslogtreecommitdiffstats
path: root/protocols
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
parentf06894d8f55b50b632c1d81ad878f8581273ba66 (diff)
It can send a valid (pre-XMPP) login packet. Lots of work to do, still...
Diffstat (limited to 'protocols')
-rw-r--r--protocols/jabber/Makefile2
-rw-r--r--protocols/jabber/io.c237
-rw-r--r--protocols/jabber/iq.c81
-rw-r--r--protocols/jabber/jabber.c69
-rw-r--r--protocols/jabber/jabber.h33
-rw-r--r--protocols/jabber/jabber_util.c22
-rw-r--r--protocols/jabber/message.c2
-rw-r--r--protocols/jabber/presence.c2
-rw-r--r--protocols/jabber/xmltree.c13
9 files changed, 430 insertions, 31 deletions
diff --git a/protocols/jabber/Makefile b/protocols/jabber/Makefile
index bf2424ba..d4dcc652 100644
--- a/protocols/jabber/Makefile
+++ b/protocols/jabber/Makefile
@@ -9,7 +9,7 @@
-include ../../Makefile.settings
# [SH] Program variables
-objects = iq.o jabber.o jabber_util.o message.o presence.o xmltree.o
+objects = io.o iq.o jabber.o jabber_util.o message.o presence.o xmltree.o
CFLAGS += -Wall
LFLAGS += -r
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;
+}
diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c
index e3553637..58e3f33c 100644
--- a/protocols/jabber/iq.c
+++ b/protocols/jabber/iq.c
@@ -23,13 +23,88 @@
#include "jabber.h"
+/*
+<iq xmlns="jabber:client" id="BeeX00000001" type="result"><query
+xmlns="jabber:iq:auth"><username>wilmer</username><resource/><password/><digest/>
+<sequence>499</sequence><token>450D1FFD</token></query></iq>
+*/
+
xt_status jabber_pkt_iq( struct xt_node *node, gpointer data )
{
- char *from = xt_find_attr( node, "from" );
+ struct gaim_connection *gc = data;
+ struct jabber_data *jd = gc->proto_data;
+ struct xt_node *query, *reply = NULL;
+ char *s;
+ int st;
+
+ query = xt_find_node( node->children, "query" );
- printf( "Received IQ from %s:\n", from );
- xt_print( node );
+ if( !query )
+ return XT_HANDLED; /* Ignore it for now, don't know what's best... */
+
+ if( ( s = xt_find_attr( query, "xmlns" ) ) && strcmp( s, "jabber:iq:auth" ) == 0 )
+ {
+ /* Time to authenticate ourselves! */
+ reply = xt_new_node( "query", NULL, NULL );
+ xt_add_attr( reply, "xmlns", "jabber:iq:auth" );
+ xt_add_child( reply, xt_new_node( "username", jd->username, NULL ) );
+ xt_add_child( reply, xt_new_node( "resource", set_getstr( &gc->acc->set, "resource" ), NULL ) );
+
+ if( xt_find_node( query->children, "digest" ) && ( s = xt_find_attr( jd->xt->root, "id" ) ) )
+ {
+ /* We can do digest authentication, it seems, and of
+ course we prefer that. */
+ SHA_CTX sha;
+ char hash_hex[40];
+ unsigned char hash[20];
+ int i;
+
+ shaInit( &sha );
+ shaUpdate( &sha, (unsigned char*) s, strlen( s ) );
+ shaUpdate( &sha, (unsigned char*) gc->acc->pass, strlen( gc->acc->pass ) );
+ shaFinal( &sha, hash );
+
+ for( i = 0; i < 20; i ++ )
+ sprintf( hash_hex + i * 2, "%02x", hash[i] );
+
+ xt_add_child( reply, xt_new_node( "digest", hash_hex, NULL ) );
+ }
+ else if( xt_find_node( query->children, "password" ) )
+ {
+ /* We'll have to stick with plaintext. Let's hope we're using SSL/TLS... */
+ xt_add_child( reply, xt_new_node( "password", gc->acc->pass, NULL ) );
+ }
+ else
+ {
+ xt_free_node( reply );
+
+ hide_login_progress_error( gc, "Can't find suitable authentication method" );
+ signoff( gc );
+ return XT_ABORT;
+ }
+
+ reply = jabber_make_packet( "iq", "set", NULL, reply );
+ st = jabber_write_packet( gc, reply );
+ xt_free_node( reply );
+
+ return st ? XT_HANDLED : XT_ABORT;
+ }
return XT_HANDLED;
}
+int jabber_start_auth( struct gaim_connection *gc )
+{
+ struct jabber_data *jd = gc->proto_data;
+ struct xt_node *node;
+ int st;
+
+ node = xt_new_node( "query", NULL, xt_new_node( "username", jd->username, NULL ) );
+ xt_add_attr( node, "xmlns", "jabber:iq:auth" );
+ node = jabber_make_packet( "iq", "get", NULL, node );
+
+ st = jabber_write_packet( gc, node );
+
+ xt_free_node( node );
+ return st;
+}
diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c
index 0a091b06..9732d6eb 100644
--- a/protocols/jabber/jabber.c
+++ b/protocols/jabber/jabber.c
@@ -27,6 +27,7 @@
#include <ctype.h>
#include <stdio.h>
+#include "ssl_client.h"
#include "xmltree.h"
#include "bitlbee.h"
#include "jabber.h"
@@ -54,14 +55,58 @@ static void jabber_acc_init( account_t *acc )
static void jabber_login( account_t *acc )
{
+ struct gaim_connection *gc = new_gaim_conn( acc );
+ struct jabber_data *jd = g_new0( struct jabber_data, 1 );
+
+ jd->gc = gc;
+ gc->proto_data = jd;
+
+ jd->username = g_strdup( acc->user );
+ jd->server = strchr( jd->username, '@' );
+
+ if( jd->server == NULL )
+ {
+ hide_login_progress( gc, "Incomplete account name (format it like <username@jabberserver.name>)" );
+ signoff( gc );
+ return;
+ }
+
+ /* So don't think of free()ing jd->server.. :-) */
+ *jd->server = 0;
+ jd->server ++;
+
+ if( set_getbool( &acc->set, "ssl" ) )
+ {
+ signoff( gc );
+ /* TODO! */
+ }
+ else
+ {
+ jd->fd = proxy_connect( jd->server, set_getint( &acc->set, "port" ), jabber_connected_plain, gc );
+ }
}
static void jabber_close( struct gaim_connection *gc )
{
+ struct jabber_data *jd = gc->proto_data;
+
+ if( jd->r_inpa >= 0 )
+ b_event_remove( jd->r_inpa );
+ if( jd->w_inpa >= 0 )
+ b_event_remove( jd->w_inpa );
+
+ if( jd->ssl )
+ ssl_disconnect( jd->ssl );
+ if( jd->fd >= 0 )
+ closesocket( jd->fd );
+
+ g_free( jd->username );
+ g_free( jd );
}
static int jabber_send_im( struct gaim_connection *gc, char *who, char *message, int len, int away )
{
+ return 0;
}
void jabber_init()
@@ -95,28 +140,6 @@ void jabber_init()
register_protocol(ret);
}
-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 }
-};
-
#if 0
int main( int argc, char *argv[] )
{
@@ -144,4 +167,4 @@ int main( int argc, char *argv[] )
xt_free( xt );
}
-#endif \ No newline at end of file
+#endif
diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h
index 72fae75d..d812ec54 100644
--- a/protocols/jabber/jabber.h
+++ b/protocols/jabber/jabber.h
@@ -29,11 +29,44 @@
#include "xmltree.h"
#include "bitlbee.h"
+typedef enum
+{
+ JFLAG_STREAM_STARTED = 1,
+ JFLAG_AUTHENTICATED = 2,
+} jabber_flags_t;
+
+/* iq.c */
xt_status jabber_pkt_iq( struct xt_node *node, gpointer data );
+int jabber_start_auth( struct gaim_connection *gc );
+
xt_status jabber_pkt_message( struct xt_node *node, gpointer data );
xt_status jabber_pkt_presence( struct xt_node *node, gpointer data );
+/* jabber_util.c */
char *set_eval_resprio( set_t *set, char *value );
char *set_eval_tls( set_t *set, char *value );
+struct xt_node *jabber_make_packet( char *name, char *type, char *to, struct xt_node *children );
+
+/* io.c */
+int jabber_write_packet( struct gaim_connection *gc, struct xt_node *node );
+int jabber_write( struct gaim_connection *gc, char *buf, int len );
+gboolean jabber_connected_plain( gpointer data, gint source, b_input_condition cond );
+
+struct jabber_data
+{
+ struct gaim_connection *gc;
+
+ int fd;
+ void *ssl;
+ char *txq;
+ int tx_len;
+ int r_inpa, w_inpa;
+
+ struct xt_parser *xt;
+ jabber_flags_t flags;
+
+ char *username; /* USERNAME@server */
+ char *server; /* username@SERVER -=> server/domain, not hostname */
+};
#endif
diff --git a/protocols/jabber/jabber_util.c b/protocols/jabber/jabber_util.c
index ff79cb16..46811d05 100644
--- a/protocols/jabber/jabber_util.c
+++ b/protocols/jabber/jabber_util.c
@@ -1,7 +1,7 @@
/***************************************************************************\
* *
* BitlBee - An IRC to IM gateway *
-* Jabber module - Main file *
+* Jabber module - Misc. stuff *
* *
* Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> *
* *
@@ -23,6 +23,8 @@
#include "jabber.h"
+static int next_id = 1;
+
char *set_eval_resprio( set_t *set, char *value )
{
account_t *acc = set->data;
@@ -46,3 +48,21 @@ char *set_eval_tls( set_t *set, char *value )
else
return set_eval_bool( set, value );
}
+
+struct xt_node *jabber_make_packet( char *name, char *type, char *to, struct xt_node *children )
+{
+ char *id = g_strdup_printf( "BeeX%04x", next_id++ );
+ struct xt_node *node;
+
+ node = xt_new_node( name, NULL, children );
+
+ xt_add_attr( node, "id", id );
+ if( type )
+ xt_add_attr( node, "type", type );
+ if( to )
+ xt_add_attr( node, "to", to );
+
+ g_free( id );
+
+ return node;
+}
diff --git a/protocols/jabber/message.c b/protocols/jabber/message.c
index d4326063..e5f75464 100644
--- a/protocols/jabber/message.c
+++ b/protocols/jabber/message.c
@@ -1,7 +1,7 @@
/***************************************************************************\
* *
* BitlBee - An IRC to IM gateway *
-* Jabber module - Main file *
+* Jabber module - Handling of message(s) (tags), etc *
* *
* Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> *
* *
diff --git a/protocols/jabber/presence.c b/protocols/jabber/presence.c
index 5383d7aa..83b69013 100644
--- a/protocols/jabber/presence.c
+++ b/protocols/jabber/presence.c
@@ -1,7 +1,7 @@
/***************************************************************************\
* *
* BitlBee - An IRC to IM gateway *
-* Jabber module - Main file *
+* Jabber module - Handling of presence (tags), etc *
* *
* Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> *
* *
diff --git a/protocols/jabber/xmltree.c b/protocols/jabber/xmltree.c
index 68c9560d..3d0a2919 100644
--- a/protocols/jabber/xmltree.c
+++ b/protocols/jabber/xmltree.c
@@ -205,7 +205,9 @@ void xt_cleanup( struct xt_parser *xt, struct xt_node *node )
{
struct xt_node *c, *prev;
- /* Let's just hope xt->root isn't NULL! */
+ if( !xt || !xt->root )
+ return;
+
if( node == NULL )
return xt_cleanup( xt, xt->root );
@@ -356,6 +358,9 @@ void xt_free_node( struct xt_node *node )
{
int i;
+ if( !node )
+ return;
+
g_free( node->name );
g_free( node->text );
@@ -379,6 +384,9 @@ void xt_free_node( struct xt_node *node )
void xt_free( struct xt_parser *xt )
{
+ if( !xt )
+ return;
+
if( xt->root )
xt_free_node( xt->root );
@@ -407,6 +415,9 @@ char *xt_find_attr( struct xt_node *node, char *key )
{
int i;
+ if( !node )
+ return NULL;
+
for( i = 0; node->attr[i].key; i ++ )
if( g_strcasecmp( node->attr[i].key, key ) == 0 )
break;