diff --git a/.gitignore b/.gitignore
index 54cf14f..2479024 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,7 @@ autom4te.cache
src/*.o
examples/*.o
tests/*.o
+tests/check_parser
*.a
examples/basic
examples/active
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..7a5cd57
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,60 @@
+AUTOMAKE_OPTIONS = subdir-objects
+
+CFLAGS = -g -Wall
+
+builtin_CFLAGS = -I$(top_srcdir)/expat/lib
+builtin_LIBS = libexpat.a
+
+PARSER_CFLAGS=@PARSER_CFLAGS@
+PARSER_LIBS=@PARSER_LIBS@
+
+STROPHE_FLAGS = -I$(top_srcdir)
+STROPHE_LIBS = libstrophe.a $(PARSER_LIBS) -lssl -lresolv
+
+## Main build targets
+if BUILD_EXPAT
+lib_LIBRARIES = libstrophe.a libexpat.a
+else
+lib_LIBRARIES = libstrophe.a
+endif
+
+libstrophe_a_CFLAGS=$(STROPHE_FLAGS) $(PARSER_CFLAGS)
+libstrophe_a_SOURCES = src/auth.c src/conn.c src/ctx.c \
+ src/event.c src/handler.c src/hash.c \
+ src/jid.c src/md5.c src/sasl.c src/sha1.c \
+ src/snprintf.c src/sock.c src/stanza.c src/thread.c \
+ src/tls_openssl.c src/util.c
+if PARSER_EXPAT
+libstrophe_a_SOURCES += src/parser_expat.c
+else
+libstrophe_a_SOURCES += src/parser_libxml2.c
+endif
+
+libexpat_a_CFLAGS=-DXML_DTD -DXML_NS -DXML_CONTEXT_BYTES=1024 -DXML_STATIC \
+ -I$(top_srcdir)/expat/lib
+libexpat_a_SOURCES=expat/lib/xmlparse.c expat/lib/xmltok.c expat/lib/xmlrole.c
+
+
+## Examples
+noinst_PROGRAMS = examples/active examples/roster examples/basic examples/bot
+examples_active_SOURCES = examples/active.c
+examples_active_CFLAGS = $(STROPHE_FLAGS)
+examples_active_LDADD = $(STROPHE_LIBS)
+examples_roster_SOURCES = examples/roster.c
+examples_roster_CFLAGS = $(STROPHE_FLAGS)
+examples_roster_LDADD = $(STROPHE_LIBS)
+examples_basic_SOURCES = examples/basic.c
+examples_basic_CFLAGS = $(STROPHE_FLAGS)
+examples_basic_LDADD = $(STROPHE_LIBS)
+examples_bot_SOURCES = examples/bot.c
+examples_bot_CFLAGS = $(STROPHE_FLAGS)
+examples_bot_LDADD = $(STROPHE_LIBS)
+
+
+## Tests
+TESTS = tests/check_parser
+check_PROGRAMS = tests/check_parser
+tests_check_parser_SOURCES = tests/check_parser.c
+tests_check_parser_CFLAGS = @check_CFLAGS@ $(PARSER_CFLAGS) $(STROPHE_FLAGS) \
+ -I$(top_srcdir)/src
+tests_check_parser_LDADD = @check_LIBS@ $(STROPHE_LIBS)
diff --git a/bootstrap.sh b/bootstrap.sh
new file mode 100755
index 0000000..c546bd9
--- /dev/null
+++ b/bootstrap.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+aclocal -I /usr/local/share/aclocal
+automake --add-missing --foreign --copy
+autoconf
\ No newline at end of file
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..2f1ac77
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,44 @@
+AC_INIT([libstrophe], [trunk], [jack@metajack.im])
+AM_INIT_AUTOMAKE
+
+AC_PROG_CC
+AC_PROG_RANLIB
+
+PKG_CHECK_MODULES([check], [check >= 0.9.4])
+
+AC_ARG_WITH([libxml2],
+ [AS_HELP_STRING([--with-libxml2], [use libxml2 for XML parsing])],
+ [with_libxml2=check],
+ [with_libxml2=no])
+
+if test "x$with_libxml2" != xno; then
+ PKG_CHECK_MODULES([libxml2], [libxml-2.0 >= 2.7],
+ [with_libxml2=yes],
+ [AC_MSG_ERROR([couldn't find libxml2])])
+else
+ AC_CHECK_FUNCS(memmove)
+ AC_C_BIGENDIAN([byteorder=1234], [byteorder=4321], [], [])
+fi
+
+if test "x$with_libxml2" = xyes; then
+ with_parser=libxml2
+ PARSER_NAME=libxml2
+ PARSER_CFLAGS=\$\(libxml2_CFLAGS\)
+ PARSER_LIBS=\$\(libxml2_LIBS\)
+else
+ with_parser=builtin
+ PARSER_NAME=expat
+ PARSER_CFLAGS=\$\(builtin_CFLAGS\)
+ PARSER_LIBS=\$\(builtin_LIBS\)
+fi
+
+AC_MSG_NOTICE([libstrophe will use the $with_parser XML parser])
+
+AM_CONDITIONAL([BUILD_EXPAT], [test x$with_parser = xbuiltin])
+AM_CONDITIONAL([PARSER_EXPAT], [test x$with_parser != xlibxml2])
+AC_DEFINE_UNQUOTED([BYTEORDER], [$byteorder])
+AC_SUBST(PARSER_NAME)
+AC_SUBST(PARSER_CFLAGS)
+AC_SUBST(PARSER_LIBS)
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
\ No newline at end of file
diff --git a/src/auth.c b/src/auth.c
index dd0b9ee..260da39 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -208,9 +208,11 @@ static int _handle_features(xmpp_conn_t * const conn,
xmpp_timed_handler_delete(conn, _handle_missing_features);
/* check for TLS */
- child = xmpp_stanza_get_child_by_name(stanza, "starttls");
- if (child && (strcmp(xmpp_stanza_get_ns(child), XMPP_NS_TLS) == 0))
- conn->tls_support = 1;
+ if (!conn->secured) {
+ child = xmpp_stanza_get_child_by_name(stanza, "starttls");
+ if (child && (strcmp(xmpp_stanza_get_ns(child), XMPP_NS_TLS) == 0))
+ conn->tls_support = 1;
+ }
/* check for SASL */
child = xmpp_stanza_get_child_by_name(stanza, "mechanisms");
@@ -277,7 +279,8 @@ static int _handle_proceedtls_default(xmpp_conn_t * const conn,
}
else
{
- parser_prepare_reset(conn, _handle_open_tls);
+ conn->secured = 1;
+ conn_prepare_reset(conn, auth_handle_open);
conn_open_stream(conn);
}
@@ -307,7 +310,7 @@ static int _handle_sasl_result(xmpp_conn_t * const conn,
(char *)userdata);
/* reset parser */
- parser_prepare_reset(conn, _handle_open_sasl);
+ conn_prepare_reset(conn, _handle_open_sasl);
/* send stream tag */
conn_open_stream(conn);
@@ -692,16 +695,6 @@ void auth_handle_open(xmpp_conn_t * const conn)
FEATURES_TIMEOUT, NULL);
}
-/* called when stream:stream tag received after TLS connection */
-static void _handle_open_tls(xmpp_conn_t * const conn)
-{
- xmpp_debug(conn->ctx, "xmpp", "TLS successful, proceeding with SASL");
-
- /* go straight to SASL auth */
- _auth(conn);
-}
-
-
/* called when stream:stream tag received after SASL auth */
static void _handle_open_sasl(xmpp_conn_t * const conn)
{
diff --git a/src/common.h b/src/common.h
index 8ecbb78..c8fb4a5 100644
--- a/src/common.h
+++ b/src/common.h
@@ -31,8 +31,7 @@
#include "tls.h"
#include "hash.h"
#include "util.h"
-
-#include "expat.h"
+#include "parser.h"
/** run-time context **/
@@ -167,6 +166,7 @@ struct _xmpp_conn_t {
int tls_failed; /* set when tls fails, so we don't try again */
int sasl_support; /* if true, field is a bitfield of supported
mechanisms */
+ int secured; /* set when stream is secured with TLS */
/* if server returns or we must do them */
int bind_required;
@@ -189,9 +189,7 @@ struct _xmpp_conn_t {
/* xml parser */
int reset_parser;
- XML_Parser parser;
- int depth;
- xmpp_stanza_t *stanza;
+ parser_t *parser;
/* timeouts */
unsigned int connect_timeout;
@@ -217,6 +215,8 @@ struct _xmpp_conn_t {
void conn_disconnect(xmpp_conn_t * const conn);
void conn_disconnect_clean(xmpp_conn_t * const conn);
void conn_open_stream(xmpp_conn_t * const conn);
+void conn_prepare_reset(xmpp_conn_t * const conn, xmpp_open_handler handler);
+void conn_parser_reset(xmpp_conn_t * const conn);
typedef enum {
@@ -241,19 +241,6 @@ struct _xmpp_stanza_t {
hash_t *attributes;
};
-int xmpp_stanza_set_attributes(xmpp_stanza_t * const stanza,
- const char * const * const attr);
-
-/* parser functions */
-void parser_handle_start(void *userdata,
- const XML_Char *name,
- const XML_Char **attr);
-void parser_handle_character(void *userdata, const XML_Char *s, int len);
-void parser_handle_end(void *userdata, const XML_Char *name);
-void parser_prepare_reset(xmpp_conn_t * const conn,
- xmpp_open_handler handler);
-int parser_reset(xmpp_conn_t * const conn);
-
/* handler management */
void handler_fire_stanza(xmpp_conn_t * const conn,
xmpp_stanza_t * const stanza);
diff --git a/src/conn.c b/src/conn.c
index 96fe693..af69cba 100644
--- a/src/conn.c
+++ b/src/conn.c
@@ -23,9 +23,11 @@
#include
#include
-#include "strophe.h"
+#include
+
#include "common.h"
#include "util.h"
+#include "parser.h"
#ifndef DEFAULT_SEND_QUEUE_MAX
/** @def DEFAULT_SEND_QUEUE_MAX
@@ -51,6 +53,13 @@
static int _disconnect_cleanup(xmpp_conn_t * const conn,
void * const userdata);
+static void _handle_stream_start(char *name, char **attrs,
+ void * const userdata);
+static void _handle_stream_end(char *name,
+ void * const userdata);
+static void _handle_stream_stanza(xmpp_stanza_t *stanza,
+ void * const userdata);
+
/** Create a new Strophe connection object.
*
* @param ctx a Strophe context object
@@ -100,13 +109,18 @@ xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t * const ctx)
conn->tls_support = 0;
conn->tls_failed = 0;
conn->sasl_support = 0;
+ conn->secured = 0;
conn->bind_required = 0;
conn->session_required = 0;
- conn->parser = NULL;
- conn->stanza = NULL;
- parser_prepare_reset(conn, auth_handle_open);
+ conn->parser = parser_new(conn->ctx,
+ _handle_stream_start,
+ _handle_stream_end,
+ _handle_stream_stanza,
+ conn);
+ conn->reset_parser = 0;
+ conn_prepare_reset(conn, auth_handle_open);
conn->authenticated = 0;
conn->conn_handler = NULL;
@@ -127,7 +141,7 @@ xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t * const ctx)
if (!item) {
xmpp_error(conn->ctx, "xmpp", "failed to allocate memory");
xmpp_free(conn->ctx, conn->lang);
- XML_ParserFree(conn->parser);
+ parser_free(conn->parser);
xmpp_free(conn->ctx, conn);
conn = NULL;
} else {
@@ -249,7 +263,7 @@ int xmpp_conn_release(xmpp_conn_t * const conn)
xmpp_free(ctx, conn->stream_error);
}
- XML_ParserFree(conn->parser);
+ parser_free(conn->parser);
if (conn->domain) xmpp_free(ctx, conn->domain);
if (conn->jid) xmpp_free(ctx, conn->jid);
@@ -361,7 +375,7 @@ int xmpp_connect_client(xmpp_conn_t * const conn,
{
char connectdomain[2048];
int connectport;
- char *domain;
+ const char * domain;
conn->type = XMPP_CLIENT;
@@ -434,6 +448,21 @@ void conn_disconnect(xmpp_conn_t * const conn)
conn->stream_error, conn->userdata);
}
+/* prepares a parser reset. this is called from handlers. we can't
+ * reset the parser immediately as it is not re-entrant. */
+void conn_prepare_reset(xmpp_conn_t * const conn, xmpp_open_handler handler)
+{
+ conn->reset_parser = 1;
+ conn->open_handler = handler;
+}
+
+/* reset the parser */
+void conn_parser_reset(xmpp_conn_t * const conn)
+{
+ conn->reset_parser = 0;
+ parser_reset(conn->parser);
+}
+
/* timed handler for cleanup if normal disconnect procedure takes too long */
static int _disconnect_cleanup(xmpp_conn_t * const conn,
void * const userdata)
@@ -607,3 +636,97 @@ void conn_open_stream(xmpp_conn_t * const conn)
conn->type == XMPP_CLIENT ? XMPP_NS_CLIENT : XMPP_NS_COMPONENT,
XMPP_NS_STREAMS);
}
+
+static void _log_open_tag(xmpp_conn_t *conn, char **attrs)
+{
+ char buf[4096];
+ size_t len, pos;
+ int i;
+
+ if (!attrs) return;
+
+ pos = 0;
+ len = xmpp_snprintf(buf, 4096, "");
+ if (len < 0) return;
+
+ xmpp_debug(conn->ctx, "xmpp", "RECV: %s", buf);
+}
+
+static char *_get_stream_attribute(char **attrs, char *name)
+{
+ int i;
+
+ if (!attrs) return NULL;
+
+ for (i = 0; attrs[i]; i += 2)
+ if (strcmp(name, attrs[i]) == 0)
+ return attrs[i+1];
+
+ return NULL;
+}
+
+static void _handle_stream_start(char *name, char **attrs,
+ void * const userdata)
+{
+ xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
+ char *id;
+
+ if (strcmp(name, "stream:stream") != 0) {
+ printf("name = %s\n", name);
+ xmpp_error(conn->ctx, "conn", "Server did not open valid stream.");
+ conn_disconnect(conn);
+ } else {
+ _log_open_tag(conn, attrs);
+
+ if (conn->stream_id) xmpp_free(conn->ctx, conn->stream_id);
+
+ id = _get_stream_attribute(attrs, "id");
+ if (id)
+ conn->stream_id = xmpp_strdup(conn->ctx, id);
+
+ if (!conn->stream_id) {
+ xmpp_error(conn->ctx, "conn", "Memory allocation failed.");
+ conn_disconnect(conn);
+ }
+ }
+
+ /* call stream open handler */
+ conn->open_handler(conn);
+}
+
+static void _handle_stream_end(char *name,
+ void * const userdata)
+{
+ xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
+
+ /* stream is over */
+ xmpp_debug(conn->ctx, "xmpp", "RECV: ");
+ conn_disconnect_clean(conn);
+}
+
+static void _handle_stream_stanza(xmpp_stanza_t *stanza,
+ void * const userdata)
+{
+ xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
+ char *buf;
+ size_t len;
+
+ if (xmpp_stanza_to_text(stanza, &buf, &len) == 0) {
+ xmpp_debug(conn->ctx, "xmpp", "RECV: %s", buf);
+ xmpp_free(conn->ctx, buf);
+ }
+
+ handler_fire_stanza(conn, stanza);
+}
diff --git a/src/event.c b/src/event.c
index 571a7ee..556b672 100644
--- a/src/event.c
+++ b/src/event.c
@@ -47,8 +47,9 @@
#define ECONNABORTED WSAECONNABORTED
#endif
-#include "strophe.h"
+#include
#include "common.h"
+#include "parser.h"
#ifndef DEFAULT_TIMEOUT
/** @def DEFAULT_TIMEOUT
@@ -168,7 +169,7 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
/* reset parsers if needed */
for (connitem = ctx->connlist; connitem; connitem = connitem->next) {
if (connitem->conn->reset_parser)
- parser_reset(connitem->conn);
+ conn_parser_reset(connitem->conn);
}
@@ -267,7 +268,7 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
}
if (ret > 0) {
- ret = XML_Parse(conn->parser, buf, ret, 0);
+ ret = parser_feed(conn->parser, buf, ret);
if (!ret) {
/* parse error, we need to shut down */
/* FIXME */
diff --git a/src/parser.c b/src/parser.c
deleted file mode 100644
index 55ca56b..0000000
--- a/src/parser.c
+++ /dev/null
@@ -1,206 +0,0 @@
-/* parser.c
-** strophe XMPP client library -- xml parser handlers and utility functions
-**
-** Copyright (C) 2005-2008 OGG, LLC. All rights reserved.
-**
-** This software is provided AS-IS with no warranty, either express
-** or implied.
-**
-** This software is distributed under license and may not be copied,
-** modified or distributed except as expressly authorized under the
-** terms of the license contained in the file LICENSE.txt in this
-** distribution.
-*/
-
-/** @file
- * XML parser handlers.
- */
-
-#include
-#include
-#include
-
-#include "expat.h"
-
-#include "strophe.h"
-#include "common.h"
-
-static void _log_open_tag(xmpp_conn_t * const conn,
- const XML_Char **attr)
-{
- char buf[4096];
- size_t len, pos;
- int i;
-
- pos = 0;
- len = xmpp_snprintf(buf, 4096, "");
- if (len < 0) return;
-
- xmpp_debug(conn->ctx, "xmpp", "RECV: %s", buf);
-}
-
-void parser_handle_start(void *userdata,
- const XML_Char *name,
- const XML_Char **attr)
-{
- xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
- xmpp_stanza_t *child;
-
- if (conn->depth == 0) {
- /* we're expecting a stream:stream tag. */
- if (strcmp(name, "stream:stream") != 0) {
- xmpp_error(conn->ctx, "xmpp",
- "Server did not open valid stream.");
- conn_disconnect(conn);
- } else {
- _log_open_tag(conn, attr);
-
- if (conn->stream_id) xmpp_free(conn->ctx, conn->stream_id);
- conn->stream_id = xmpp_strdup(conn->ctx, "foo");
- if (!conn->stream_id) {
- xmpp_error(conn->ctx, "xmpp",
- "Memory allocation failure.");
- conn_disconnect(conn);
- }
-
- /* call stream open handler */
- conn->open_handler(conn);
- }
- } else {
- /* build stanzas at depth 1 */
- if (!conn->stanza && conn->depth != 1) {
- /* something terrible happened */
- /* FIXME: shutdown disconnect */
- xmpp_debug(conn->ctx, "xmpp", "oops, where did our stanza go?");
- } else if (!conn->stanza) {
- /* starting a new toplevel stanza */
- conn->stanza = xmpp_stanza_new(conn->ctx);
- if (!conn->stanza) {
- /* FIXME: can't allocate, disconnect */
- }
- xmpp_stanza_set_name(conn->stanza, name);
- xmpp_stanza_set_attributes(conn->stanza, attr);
- } else {
- /* starting a child of conn->stanza */
- child = xmpp_stanza_new(conn->ctx);
- if (!child) {
- /* FIXME: can't allocate, disconnect */
- }
- xmpp_stanza_set_name(child, name);
- xmpp_stanza_set_attributes(child, attr);
-
- /* add child to parent */
- xmpp_stanza_add_child(conn->stanza, child);
-
- /* the child is owned by the toplevel stanza now */
- xmpp_stanza_release(child);
-
- /* make child the current stanza */
- conn->stanza = child;
- }
- }
-
- conn->depth++;
-}
-
-void parser_handle_end(void *userdata, const XML_Char *name)
-{
- xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
- char *buf;
- size_t len;
- xmpp_stanza_t *stanza;
-
- conn->depth--;
-
- if (conn->depth == 0) {
- /* got a closing stream tag */
- xmpp_debug(conn->ctx, "xmpp", "RECV: ");
- conn_disconnect_clean(conn);
- } else {
- if (conn->stanza->parent) {
- /* we're finishing a child stanza, so set current to the parent */
- conn->stanza = conn->stanza->parent;
- } else {
- /* we're finishing a toplevel stanza, so fire off handler */
- if (xmpp_stanza_to_text(conn->stanza, &buf, &len) == 0) {
- xmpp_debug(conn->ctx, "xmpp", "RECV: %s", buf);
- xmpp_free(conn->ctx, buf);
- }
-
- stanza = xmpp_stanza_clone(conn->stanza);
- xmpp_stanza_release(conn->stanza);
- conn->stanza = NULL;
-
- /* fire handlers */
- handler_fire_stanza(conn, stanza);
-
- xmpp_stanza_release(stanza);
- }
- }
-}
-
-void parser_handle_character(void *userdata, const XML_Char *s, int len)
-{
- xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
- xmpp_stanza_t *stanza;
-
- if (conn->depth < 2) return;
-
- /* create and populate stanza */
- stanza = xmpp_stanza_new(conn->ctx);
- if (!stanza) {
- /* FIXME: allocation error, disconnect */
- return;
- }
- xmpp_stanza_set_text_with_size(stanza, s, len);
-
- xmpp_stanza_add_child(conn->stanza, stanza);
- xmpp_stanza_release(stanza);
-}
-
-/* prepares a parser reset. this is called from handlers. we can't
- * reset the parser immediately as it is not reentrant. */
-void parser_prepare_reset(xmpp_conn_t * const conn,
- xmpp_open_handler handler)
-{
- conn->reset_parser = 1;
- conn->open_handler = handler;
-}
-
-/* shuts down and restarts XML parser. true on success */
-int parser_reset(xmpp_conn_t * const conn)
-{
- conn->reset_parser = 0;
-
- if (conn->parser)
- XML_ParserFree(conn->parser);
-
- if (conn->stanza)
- xmpp_stanza_release(conn->stanza);
-
- conn->parser = XML_ParserCreate(NULL);
- if (!conn->parser) return 0;
-
- conn->depth = 0;
- conn->stanza = NULL;
- XML_SetUserData(conn->parser, conn);
- XML_SetElementHandler(conn->parser, parser_handle_start,
- parser_handle_end);
- XML_SetCharacterDataHandler(conn->parser, parser_handle_character);
-
- return 1;
-}
-
diff --git a/src/parser.h b/src/parser.h
new file mode 100644
index 0000000..f8bfaec
--- /dev/null
+++ b/src/parser.h
@@ -0,0 +1,43 @@
+/* parser.h
+** strophe XMPP client library -- parser structures and functions
+**
+** Copyright (C) 2005-2009 Stanziq, Inc. All rights reserved.
+**
+** This software is provided AS-IS with no warranty, either express or
+** implied.
+**
+** This software is distributed under license and may not be copied,
+** modified or distributed except as expressly authorized under the
+** terms of the license contained in the file LICENSE.txt in this
+** distribution.
+*/
+
+/** @file
+ * Internally used functions and structures.
+ */
+
+#ifndef __LIBSTROPHE_PARSER_H__
+#define __LIBSTROPHE_PARSER_H__
+
+#include "strophe.h"
+
+typedef struct _parser_t parser_t;
+
+typedef void (*parser_start_callback)(char *name,
+ char **attrs,
+ void * const userdata);
+typedef void (*parser_end_callback)(char *name, void * const userdata);
+typedef void (*parser_stanza_callback)(xmpp_stanza_t *stanza,
+ void * const userdata);
+
+
+parser_t *parser_new(xmpp_ctx_t *ctx,
+ parser_start_callback startcb,
+ parser_end_callback endcb,
+ parser_stanza_callback stanzacb,
+ void *userdata);
+void parser_free(parser_t * const parser);
+int parser_reset(parser_t *parser);
+int parser_feed(parser_t *parser, char *chunk, int len);
+
+#endif /* __LIBSTROPHE_PARSER_H__ */
diff --git a/src/parser_expat.c b/src/parser_expat.c
new file mode 100644
index 0000000..94859b9
--- /dev/null
+++ b/src/parser_expat.c
@@ -0,0 +1,202 @@
+/* parser.c
+** strophe XMPP client library -- xml parser handlers and utility functions
+**
+** Copyright (C) 2005-2008 OGG, LLC. All rights reserved.
+**
+** This software is provided AS-IS with no warranty, either express
+** or implied.
+**
+** This software is distributed under license and may not be copied,
+** modified or distributed except as expressly authorized under the
+** terms of the license contained in the file LICENSE.txt in this
+** distribution.
+*/
+
+/** @file
+ * XML parser handlers.
+ */
+
+#include
+#include
+#include
+
+#include
+
+#include
+#include "common.h"
+#include "parser.h"
+
+struct _parser_t {
+ xmpp_ctx_t *ctx;
+ XML_Parser expat;
+ parser_start_callback startcb;
+ parser_end_callback endcb;
+ parser_stanza_callback stanzacb;
+ void *userdata;
+ int depth;
+ xmpp_stanza_t *stanza;
+};
+
+static void _set_attributes(xmpp_stanza_t *stanza, const XML_Char **attrs)
+{
+ int i;
+
+ if (!attrs) return;
+
+ for (i = 0; attrs[i]; i += 2) {
+ xmpp_stanza_set_attribute(stanza, attrs[i], attrs[i+1]);
+ }
+}
+
+static void _start_element(void *userdata,
+ const XML_Char *name,
+ const XML_Char **attrs)
+{
+ parser_t *parser = (parser_t *)userdata;
+ xmpp_stanza_t *child;
+
+ if (parser->depth == 0) {
+ /* notify the owner */
+ if (parser->startcb)
+ parser->startcb((char *)name, (char **)attrs,
+ parser->userdata);
+ } else {
+ /* build stanzas at depth 1 */
+ if (!parser->stanza && parser->depth != 1) {
+ /* something terrible happened */
+ /* FIXME: shutdown disconnect */
+ xmpp_error(parser->ctx, "parser", "oops, where did our stanza go?");
+ } else if (!parser->stanza) {
+ /* starting a new toplevel stanza */
+ parser->stanza = xmpp_stanza_new(parser->ctx);
+ if (!parser->stanza) {
+ /* FIXME: can't allocate, disconnect */
+ }
+ xmpp_stanza_set_name(parser->stanza, name);
+ _set_attributes(parser->stanza, attrs);
+ } else {
+ /* starting a child of parser->stanza */
+ child = xmpp_stanza_new(parser->ctx);
+ if (!child) {
+ /* FIXME: can't allocate, disconnect */
+ }
+ xmpp_stanza_set_name(child, name);
+ _set_attributes(child, attrs);
+
+ /* add child to parent */
+ xmpp_stanza_add_child(parser->stanza, child);
+
+ /* the child is owned by the toplevel stanza now */
+ xmpp_stanza_release(child);
+
+ /* make child the current stanza */
+ parser->stanza = child;
+ }
+ }
+
+ parser->depth++;
+}
+
+static void _end_element(void *userdata, const XML_Char *name)
+{
+ parser_t *parser = (parser_t *)userdata;
+
+ parser->depth--;
+
+ if (parser->depth == 0) {
+ /* notify the owner */
+ if (parser->endcb)
+ parser->endcb((char *)name, parser->userdata);
+ } else {
+ if (parser->stanza->parent) {
+ /* we're finishing a child stanza, so set current to the parent */
+ parser->stanza = parser->stanza->parent;
+ } else {
+ if (parser->stanzacb)
+ parser->stanzacb(xmpp_stanza_clone(parser->stanza),
+ parser->userdata);
+ xmpp_stanza_release(parser->stanza);
+ parser->stanza = NULL;
+ }
+ }
+}
+
+static void _characters(void *userdata, const XML_Char *s, int len)
+{
+ parser_t *parser = (parser_t *)userdata;
+ xmpp_stanza_t *stanza;
+
+ if (parser->depth < 2) return;
+
+ /* create and populate stanza */
+ stanza = xmpp_stanza_new(parser->ctx);
+ if (!stanza) {
+ /* FIXME: allocation error, disconnect */
+ return;
+ }
+ xmpp_stanza_set_text_with_size(stanza, s, len);
+
+ xmpp_stanza_add_child(parser->stanza, stanza);
+ xmpp_stanza_release(stanza);
+}
+
+parser_t *parser_new(xmpp_ctx_t *ctx,
+ parser_start_callback startcb,
+ parser_end_callback endcb,
+ parser_stanza_callback stanzacb,
+ void *userdata)
+{
+ parser_t *parser;
+
+ parser = xmpp_alloc(ctx, sizeof(parser_t));
+ if (parser != NULL) {
+ parser->ctx = ctx;
+ parser->expat = NULL;
+ parser->startcb = startcb;
+ parser->endcb = endcb;
+ parser->stanzacb = stanzacb;
+ parser->userdata = userdata;
+ parser->depth = 0;
+ parser->stanza = NULL;
+
+ parser_reset(parser);
+ }
+
+ return parser;
+}
+
+/* free a parser */
+void parser_free(parser_t *parser)
+{
+ if (parser->expat)
+ XML_ParserFree(parser->expat);
+
+ xmpp_free(parser->ctx, parser);
+}
+
+/* shuts down and restarts XML parser. true on success */
+int parser_reset(parser_t *parser)
+{
+ if (parser->expat)
+ XML_ParserFree(parser->expat);
+
+ if (parser->stanza)
+ xmpp_stanza_release(parser->stanza);
+
+ parser->expat = XML_ParserCreate(NULL);
+ if (!parser->expat) return 0;
+
+ parser->depth = 0;
+ parser->stanza = NULL;
+
+ XML_SetUserData(parser->expat, parser);
+ XML_SetElementHandler(parser->expat, _start_element, _end_element);
+ XML_SetCharacterDataHandler(parser->expat, _characters);
+
+ return 1;
+}
+
+int parser_feed(parser_t *parser, char *chunk, int len)
+{
+ return XML_Parse(parser->expat, chunk, len, 0);
+}
diff --git a/src/parser_libxml2.c b/src/parser_libxml2.c
new file mode 100644
index 0000000..1d9d271
--- /dev/null
+++ b/src/parser_libxml2.c
@@ -0,0 +1,206 @@
+/* parser.c
+** strophe XMPP client library -- xml parser handlers and utility functions
+**
+** Copyright (C) 2005-2008 OGG, LLC. All rights reserved.
+**
+** This software is provided AS-IS with no warranty, either express
+** or implied.
+**
+** This software is distributed under license and may not be copied,
+** modified or distributed except as expressly authorized under the
+** terms of the license contained in the file LICENSE.txt in this
+** distribution.
+*/
+
+/** @file
+ * XML parser handlers.
+ */
+
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include "common.h"
+#include "parser.h"
+
+struct _parser_t {
+ xmpp_ctx_t *ctx;
+ xmlParserCtxtPtr xmlctx;
+ xmlSAXHandler handlers;
+ parser_start_callback startcb;
+ parser_end_callback endcb;
+ parser_stanza_callback stanzacb;
+ void *userdata;
+ int depth;
+ xmpp_stanza_t *stanza;
+};
+
+static void _set_attributes(xmpp_stanza_t *stanza, const xmlChar **attrs)
+{
+ int i;
+
+ if (!attrs) return;
+
+ for (i = 0; attrs[i]; i += 2) {
+ xmpp_stanza_set_attribute(stanza, attrs[i], attrs[i+1]);
+ }
+}
+
+static void _start_element(void *userdata,
+ const xmlChar *name, const xmlChar **attrs)
+{
+ parser_t *parser = (parser_t *)userdata;
+ xmpp_stanza_t *child;
+
+ if (parser->depth == 0) {
+ /* notify the owner */
+ if (parser->startcb)
+ parser->startcb((char *)name, (char **)attrs,
+ parser->userdata);
+ } else {
+ /* build stanzas at depth 1 */
+ if (!parser->stanza && parser->depth != 1) {
+ /* something terrible happened */
+ /* FIXME: we should probably trigger a disconnect */
+ xmpp_error(parser->ctx, "parser", "oops, where did our stanza go?");
+ } else if (!parser->stanza) {
+ /* starting a new toplevel stanza */
+ parser->stanza = xmpp_stanza_new(parser->ctx);
+ if (!parser->stanza) {
+ /* FIXME: can't allocate, disconnect */
+ }
+ xmpp_stanza_set_name(parser->stanza, (char *)name);
+ _set_attributes(parser->stanza, attrs);
+ } else {
+ /* starting a child of conn->stanza */
+ child = xmpp_stanza_new(parser->ctx);
+ if (!child) {
+ /* FIXME: can't allocate, disconnect */
+ }
+ xmpp_stanza_set_name(child, (char *)name);
+ _set_attributes(child, attrs);
+
+ /* add child to parent */
+ xmpp_stanza_add_child(parser->stanza, child);
+
+ /* the child is owned by the toplevel stanza now */
+ xmpp_stanza_release(child);
+
+ /* make child the current stanza */
+ parser->stanza = child;
+ }
+ }
+
+ parser->depth++;
+}
+
+static void _end_element(void *userdata, const xmlChar *name)
+{
+ parser_t *parser = (parser_t *)userdata;
+
+ parser->depth--;
+
+ if (parser->depth == 0) {
+ /* notify owner */
+ if (parser->endcb)
+ parser->endcb((char *)name, parser->userdata);
+ } else {
+ if (parser->stanza->parent) {
+ /* we're finishing a child stanza, so set current to the parent */
+ parser->stanza = parser->stanza->parent;
+ } else {
+ if (parser->stanzacb)
+ parser->stanzacb(xmpp_stanza_clone(parser->stanza),
+ parser->userdata);
+ xmpp_stanza_release(parser->stanza);
+ parser->stanza = NULL;
+ }
+ }
+}
+
+static void _characters(void *userdata, const xmlChar *chr, int len)
+{
+ parser_t *parser = (parser_t *)userdata;
+ xmpp_stanza_t *stanza;
+
+ /* skip unimportant whitespace, etc */
+ if (parser->depth < 2) return;
+
+ /* create and populate stanza */
+ stanza = xmpp_stanza_new(parser->ctx);
+ if (!stanza) {
+ /* FIXME: allocation error, disconnect */
+ return;
+ }
+ xmpp_stanza_set_text_with_size(stanza, (char *)chr, len);
+
+ xmpp_stanza_add_child(parser->stanza, stanza);
+ xmpp_stanza_release(stanza);
+}
+
+/* create a new parser */
+parser_t *parser_new(xmpp_ctx_t *ctx,
+ parser_start_callback startcb,
+ parser_end_callback endcb,
+ parser_stanza_callback stanzacb,
+ void *userdata)
+{
+ parser_t *parser;
+
+ parser = xmpp_alloc(ctx, sizeof(parser_t));
+ if (parser != NULL) {
+ parser->ctx = ctx;
+ parser->xmlctx = NULL;
+ memset(&parser->handlers, 0, sizeof(xmlSAXHandler));
+ parser->handlers.startElement = _start_element;
+ parser->handlers.endElement = _end_element;
+ parser->handlers.characters = _characters;
+ parser->startcb = startcb;
+ parser->endcb = endcb;
+ parser->stanzacb = stanzacb;
+ parser->userdata = userdata;
+ parser->depth = 0;
+ parser->stanza = NULL;
+
+ parser_reset(parser);
+ }
+
+ return parser;
+}
+
+/* free a parser */
+void parser_free(parser_t *parser)
+{
+ if (parser->xmlctx)
+ xmlFreeParserCtxt(parser->xmlctx);
+ xmpp_free(parser->ctx, parser);
+}
+
+/* shuts down and restarts XML parser. true on success */
+int parser_reset(parser_t *parser)
+{
+ if (parser->xmlctx)
+ xmlFreeParserCtxt(parser->xmlctx);
+
+ if (parser->stanza)
+ xmpp_stanza_release(parser->stanza);
+
+ parser->xmlctx = xmlCreatePushParserCtxt(&parser->handlers,
+ parser, NULL, 0, NULL);
+ if (!parser->xmlctx) return 0;
+
+ parser->depth = 0;
+ parser->stanza = NULL;
+
+ return 1;
+}
+
+/* feed a chunk of data to the parser */
+int parser_feed(parser_t *parser, char *chunk, int len)
+{
+ return xmlParseChunk(parser->xmlctx, chunk, len, 0);
+}
diff --git a/src/stanza.c b/src/stanza.c
index 24ca1a5..064385e 100644
--- a/src/stanza.c
+++ b/src/stanza.c
@@ -400,45 +400,6 @@ char *xmpp_stanza_get_name(xmpp_stanza_t * const stanza)
return stanza->data;
}
-/** Set or replace attributes on a stanza.
- * This function replaces all previous attributes (if any) with the
- * attributes given. It is used primarily by the XML parser during
- * stanza creation. All strings in the array are copied before placing them
- * inside the stanza object.
- *
- * @param stanza a Strophe stanza object
- * @param attr an array of strings with the attributes in the following
- * format: attr[i] = attribute name, attr[i+1] = attribute value
- *
- * @return XMPP_EOK on success, a number less than 0 on failure (XMPP_EMEM,
- * XMPP_EINVOP)
- *
- * @ingroup Stanza
- */
-int xmpp_stanza_set_attributes(xmpp_stanza_t * const stanza,
- const char * const * const attr)
-{
- int i;
- char *value;
-
- if (stanza->attributes != NULL)
- hash_release(stanza->attributes);
-
- stanza->attributes = hash_new(stanza->ctx, 8, xmpp_free);
- if (!stanza->attributes) return XMPP_EMEM;
-
- for (i = 0; attr[i]; i += 2) {
- value = xmpp_strdup(stanza->ctx, attr[i + 1]);
- if (!value) {
- /* FIXME: memory allocation error */
- continue;
- }
- hash_add(stanza->attributes, attr[i], value);
- }
-
- return XMPP_EOK;
-}
-
/** Count the attributes in a stanza object.
*
* @param stanza a Strophe stanza object
diff --git a/tests/check_parser.c b/tests/check_parser.c
new file mode 100644
index 0000000..d2dae66
--- /dev/null
+++ b/tests/check_parser.c
@@ -0,0 +1,93 @@
+/* check_parser.h
+** strophe XMPP client library -- parser tests
+**
+** Copyright (C) 2005-2009 Stanziq, Inc. All rights reserved.
+**
+** This software is provided AS-IS with no warranty, either express or
+** implied.
+**
+** This software is distributed under license and may not be copied,
+** modified or distributed except as expressly authorized under the
+** terms of the license contained in the file LICENSE.txt in this
+** distribution.
+*/
+
+#include
+
+#include
+
+#include
+#include "parser.h"
+
+#include "test.h"
+
+START_TEST(create_destroy)
+{
+ xmpp_ctx_t *ctx;
+ parser_t *parser;
+
+ ctx = xmpp_ctx_new(NULL, NULL);
+ parser = parser_new(ctx, NULL, NULL, NULL, NULL);
+ fail_unless(parser != NULL, "Parser creation failed.");
+ parser_free(parser);
+ xmpp_ctx_free(ctx);
+}
+END_TEST
+
+int cbtest_got_start = 0;
+void cbtest_handle_start(char *name, char **attrs, void *userdata)
+{
+ if (strcmp(name, "stream:stream") == 0)
+ cbtest_got_start = 1;
+}
+
+int cbtest_got_end = 0;
+void cbtest_handle_end(char *name, void *userdata)
+{
+ if (strcmp(name, "stream:stream") == 0)
+ cbtest_got_end = 1;
+}
+
+int cbtest_got_stanza = 0;
+void cbtest_handle_stanza(xmpp_stanza_t *stanza, void *userdata)
+{
+ if (strcmp(xmpp_stanza_get_name(stanza), "message") == 0)
+ cbtest_got_stanza = 1;
+}
+
+START_TEST(callbacks)
+{
+ xmpp_ctx_t *ctx;
+ parser_t *parser;
+ int ret;
+
+ ctx = xmpp_ctx_new(NULL, NULL);
+ parser = parser_new(ctx,
+ cbtest_handle_start,
+ cbtest_handle_end,
+ cbtest_handle_stanza, NULL);
+
+ ret = parser_feed(parser, "", 15);
+ ret = parser_feed(parser, "", 10);
+ parser_feed(parser, "", 16);
+
+ fail_unless(cbtest_got_start == 1);
+ fail_unless(cbtest_got_end == 1);
+ fail_unless(cbtest_got_stanza == 1);
+
+ parser_free(parser);
+ xmpp_ctx_free(ctx);
+}
+END_TEST
+
+Suite *parser_suite(void)
+{
+ Suite *s = suite_create("Parser");
+ TCase *tc_core = tcase_create("Core");
+ tcase_add_test(tc_core, create_destroy);
+ tcase_add_test(tc_core, callbacks);
+ suite_add_tcase(s, tc_core);
+ return s;
+}
+
+TEST_MAIN
diff --git a/tests/test.h b/tests/test.h
new file mode 100644
index 0000000..134d5b4
--- /dev/null
+++ b/tests/test.h
@@ -0,0 +1,16 @@
+#ifndef __LIBSTROPHE_TEST_H__
+#define __LIBSTROPHE_TEST_H__
+
+#define TEST_MAIN \
+ int main(int argc, char **argv) {\
+ int num_failed;\
+ Suite *s = parser_suite();\
+ SRunner *sr = srunner_create(s);\
+ srunner_run_all(sr, CK_NORMAL);\
+ num_failed = srunner_ntests_failed(sr);\
+ srunner_free(sr);\
+ return (num_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;\
+ }\
+
+
+#endif /* __LIBSTROPHE_TEST_H__ */