summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordefanor <defanor@uberspace.net>2023-09-08 17:23:54 +0300
committerdefanor <defanor@uberspace.net>2023-09-08 17:23:54 +0300
commitaaae19eb8462c784daab0cf9afddc934fdbd1b75 (patch)
tree62e605e8e3c016074b9a62f799d1d638c737405d
parent55db67a6abebeda001feccbbf2b7615d19cb93e5 (diff)
Support libexpat as an alternative XML parser
-rw-r--r--README13
-rw-r--r--configure.ac10
-rw-r--r--emacs/xml_interface.c20
-rw-r--r--emacs/xmpp.el3
-rw-r--r--examples/basic.c17
-rw-r--r--src/Makefile.am18
-rw-r--r--src/rexmpp.c135
-rw-r--r--src/rexmpp.h8
-rw-r--r--src/rexmpp_jingle.c16
-rw-r--r--src/rexmpp_roster.c2
-rw-r--r--src/rexmpp_tls.c11
-rw-r--r--src/rexmpp_xml.c276
-rw-r--r--src/rexmpp_xml.h56
-rw-r--r--src/rexmpp_xml.rs26
-rw-r--r--src/rexmpp_xml_parser.c318
-rw-r--r--src/rexmpp_xml_parser.h103
16 files changed, 700 insertions, 332 deletions
diff --git a/README b/README
index e980262..f170859 100644
--- a/README
+++ b/README
@@ -1,7 +1,5 @@
rexmpp - a reusable XMPP IM client library
-This is currently at a draft/prototype stage.
-
The goal is to produce a library reusable from different languages
(via C FFI), without hijacking an event loop, requiring any specific
one, or otherwise restricting a user, and fairly feature-rich. The
@@ -14,9 +12,11 @@ rely on any particular UI, should be flexible and not stay in the way
of implementing additional XEPs on top of it, and should try to make
it easy to implement a decent client application using it.
-Current dependencies: libxml2, libgcrypt. Optionally gsasl, libunbound
-or c-ares, gnutls with gnutls-dane or openssl, icu-i18n, gpgme, curl,
-libnice (with glib), libsrtp2, rustc.
+Current dependencies: libxml2 or libexpat, libgcrypt. Optionally
+gsasl, libunbound or c-ares, gnutls with gnutls-dane or openssl,
+icu-i18n, gpgme, curl, libnice (with glib), libsrtp2. For use of the
+alternative Rust implementations, rustc and cargo, with libc and errno
+Rust libraries.
A rough roadmap:
@@ -53,8 +53,7 @@ A rough roadmap:
[.] Texinfo manual.
[.] Proper JID handling (RFC 7622).
[+] Abstraction of the used XML, SASL, TLS, and DNS libraries, and
- optional usage of alternative ones. Though only supporting libxml2
- for XML parsing and printing for now.
+ optional usage of alternative ones.
[.] Automated testing.
[.] Alternative module implementations in Rust.
diff --git a/configure.ac b/configure.ac
index 251a94f..ed9f44c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -29,7 +29,15 @@ LT_INIT
# Checks for libraries and related parameters.
-PKG_CHECK_MODULES([LIBXML], [libxml-2.0])
+AC_ARG_WITH([expat],
+ AS_HELP_STRING([--with-expat], [use libexpat instead of libxml2]))
+
+AS_IF([test "x$with_expat" == "xyes"],
+ [PKG_CHECK_MODULES([EXPAT], [expat],
+ [AC_DEFINE([USE_EXPAT], [1], [Use libexpat])])],
+ [PKG_CHECK_MODULES([LIBXML2], [libxml-2.0],
+ [AC_DEFINE([USE_LIBXML2], [1], [Use libxml2])])])
+
AM_PATH_LIBGCRYPT
diff --git a/emacs/xml_interface.c b/emacs/xml_interface.c
index 76e888c..66cbe7f 100644
--- a/emacs/xml_interface.c
+++ b/emacs/xml_interface.c
@@ -9,8 +9,8 @@ A basic and ad hoc XML interface. The parent process (e.g., Emacs) is
supposed to respond to requests starting with the most recent one.
This program's output is separated with NUL ('\0') characters, to
-simplify parsing in Emacs, while the input is separated with newline
-and EOF ones, to simplify reading with libxml2.
+simplify parsing in Emacs, while the input is separated with newlines,
+to simplify reading with rexmpp_xml_read_fd (getline).
*/
@@ -31,18 +31,6 @@ void print_xml (rexmpp_xml_t *node) {
free(s);
}
-rexmpp_xml_t *read_xml () {
- rexmpp_xml_t *elem = NULL;
- xmlDocPtr doc = xmlReadFd(STDIN_FILENO, "", "utf-8", 0);
- if (doc != NULL) {
- elem = rexmpp_xml_from_libxml2(xmlDocGetRootElement(doc));
- xmlFreeDoc(doc);
- return elem;
- }
- return NULL;
-}
-
-
char *request (rexmpp_t *s, rexmpp_xml_t *payload)
{
rexmpp_xml_t *req = rexmpp_xml_new_elem("request", NULL);
@@ -58,7 +46,7 @@ void req_process (rexmpp_t *s,
rexmpp_xml_t *elem);
rexmpp_xml_t *read_response (rexmpp_t *s, const char *id) {
- rexmpp_xml_t *elem = read_xml();
+ rexmpp_xml_t *elem = rexmpp_xml_read_fd(stdin);
if (elem != NULL) {
if (rexmpp_xml_match(elem, NULL, "response")) {
const char *resp_id = rexmpp_xml_find_attr_val(elem, "id");
@@ -339,7 +327,7 @@ int main (int argc, char **argv) {
do {
/* Check if we have some user input. */
if (n > 0 && FD_ISSET(STDIN_FILENO, &read_fds)) {
- rexmpp_xml_t *elem = read_xml();
+ rexmpp_xml_t *elem = rexmpp_xml_read_fd(stdin);
if (elem != NULL) {
req_process(&s, elem);
rexmpp_xml_free(elem);
diff --git a/emacs/xmpp.el b/emacs/xmpp.el
index feb201f..1dbd34b 100644
--- a/emacs/xmpp.el
+++ b/emacs/xmpp.el
@@ -186,8 +186,7 @@ its printing--which doesn't handle namespaces--can be used too."
(with-temp-buffer
(xml-print xml)
(insert "\n")
- (process-send-region cur-proc (point-min) (point-max))
- (process-send-eof cur-proc))))
+ (process-send-region cur-proc (point-min) (point-max)))))
(defun xmpp-with-message-body (proc message-xml func)
(let* ((message-contents (xml-node-children message-xml))
diff --git a/examples/basic.c b/examples/basic.c
index 0b77145..528cdf9 100644
--- a/examples/basic.c
+++ b/examples/basic.c
@@ -177,20 +177,11 @@ int main (int argc, char **argv) {
if (strlen(input) != 0) {
if (input[0] == '<' && xml_console) {
/* Raw XML input. */
- xmlDocPtr doc = xmlReadMemory(input, input_len, "", "utf-8", 0);
- if (doc != NULL) {
- xmlNodePtr node_libxml2 = xmlDocGetRootElement(doc);
- if (node_libxml2 != NULL) {
- xmlUnlinkNode(node_libxml2);
- rexmpp_xml_t *node = rexmpp_xml_from_libxml2(node_libxml2);
- xmlFreeNode(node_libxml2);
- rexmpp_send(&s, node);
- } else {
- puts("No root node");
- }
- xmlFreeDoc(doc);
+ rexmpp_xml_t *node = rexmpp_xml_parse(input, input_len);
+ if (node != NULL) {
+ rexmpp_send(&s, node);
} else {
- puts("Failed to read a document");
+ puts("Failed to parse XML");
}
} else if (txt_console) {
rexmpp_console_feed(&s, input, input_len);
diff --git a/src/Makefile.am b/src/Makefile.am
index 5a5cbc3..eea4f8f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,11 +1,4 @@
-AM_CFLAGS = -Werror -Wall -Wextra -pedantic -std=gnu99 \
- -Wno-pointer-sign
-
-# -Wno-pointer-sign is used to suppress libxml2-related warnings.
-# Since we only care about UTF-8, and in almost all cases just its
-# ASCII subset (comparing or setting fixed namespaces, element names,
-# etc), it shouldn't matter. Later it would be nice to abstract XML
-# manipulations anyway, to allow libexpat as an alternative.
+AM_CFLAGS = -Werror -Wall -Wextra -pedantic -std=gnu99
lib_LTLIBRARIES = librexmpp.la
@@ -22,18 +15,19 @@ librexmpp_la_SOURCES = rexmpp_roster.h rexmpp_roster.c \
rexmpp_base64.h rexmpp_base64.c \
rexmpp_sasl.h rexmpp_sasl.c \
rexmpp_xml.h rexmpp_xml.c \
- rexmpp_utf8.h
+ rexmpp_utf8.h \
+ rexmpp_xml_parser.h rexmpp_xml_parser.c
include_HEADERS = config.h rexmpp_roster.h rexmpp_tcp.h rexmpp_socks.h rexmpp.h \
rexmpp_dns.h rexmpp_tls.h rexmpp_jid.h rexmpp_openpgp.h rexmpp_console.h \
rexmpp_pubsub.h rexmpp_http_upload.h rexmpp_jingle.h rexmpp_base64.h \
- rexmpp_sasl.h rexmpp_xml.h
-librexmpp_la_CFLAGS = $(AM_CFLAGS) $(LIBXML_CFLAGS) \
+ rexmpp_sasl.h rexmpp_xml.h rexmpp_utf8.h rexmpp_xml_parser.h
+librexmpp_la_CFLAGS = $(AM_CFLAGS) $(LIBXML2_CFLAGS) $(EXPAT_CFLAGS) \
$(GNUTLS_CFLAGS) $(LIBDANE_CFLAGS) $(OPENSSL_CFLAGS) \
$(GSASL_CFLAGS) $(UNBOUND_CFLAGS) $(CARES_CFLAGS) $(GPGME_CFLAGS) \
$(ICU_I18N_CFLAGS) $(LIBGCRYPT_CFLAGS) $(CURL_CFLAGS) \
$(NICE_CFLAGS) $(GLIB_CFLAGS) $(SRTP_CFLAGS)
-librexmpp_la_LIBADD = $(LIBXML_LIBS) \
+librexmpp_la_LIBADD = $(LIBXML2_LIBS) $(EXPAT_LIBS) \
$(GNUTLS_LIBS) $(LIBDANE_LIBS) $(OPENSSL_LIBS) \
$(GSASL_LIBS) $(UNBOUND_LIBS) $(CARES_LIBS) $(GPGME_LIBS) $(ICU_I18N_LIBS) \
$(LIBGCRYPT_LIBS) $(CURL_LIBS) $(NICE_LIBS) $(GLIB_LIBS) $(SRTP_LIBS)
diff --git a/src/rexmpp.c b/src/rexmpp.c
index bdbfb94..886f091 100644
--- a/src/rexmpp.c
+++ b/src/rexmpp.c
@@ -18,8 +18,6 @@
#include "config.h"
#include <gcrypt.h>
-#include <libxml/tree.h>
-#include <libxml/xmlsave.h>
#include <unbound.h>
#ifdef HAVE_GPGME
#include <gpgme.h>
@@ -89,21 +87,13 @@ const char *rexmpp_strerror (rexmpp_err_t error) {
}
void rexmpp_sax_start_elem_ns (rexmpp_t *s,
- const char *localname,
- const char *prefix,
- const char *URI,
- int nb_namespaces,
- const char **namespaces,
- int nb_attributes,
- int nb_defaulted,
- const char **attributes);
-
-void rexmpp_sax_end_elem_ns(rexmpp_t *s,
- const char *localname,
- const char *prefix,
- const char *URI);
-
-void rexmpp_sax_characters (rexmpp_t *s, const char * ch, int len);
+ const char *name,
+ const char *namespace,
+ rexmpp_xml_attr_t *attributes);
+
+void rexmpp_sax_end_elem_ns(rexmpp_t *s);
+
+void rexmpp_sax_characters (rexmpp_t *s, const char * ch, size_t len);
void rexmpp_log (rexmpp_t *s, int priority, const char *format, ...)
{
@@ -479,17 +469,17 @@ rexmpp_xml_t *rexmpp_disco_info (rexmpp_t *s) {
return s->disco_info;
}
+struct rexmpp_xml_parser_handlers sax = {
+ (rexmpp_xml_parser_element_start)rexmpp_sax_start_elem_ns,
+ (rexmpp_xml_parser_element_end)rexmpp_sax_end_elem_ns,
+ (rexmpp_xml_parser_characters)rexmpp_sax_characters
+};
+
rexmpp_err_t rexmpp_init (rexmpp_t *s,
const char *jid,
log_function_t log_func)
{
int err;
- xmlSAXHandler sax = {
- .initialized = XML_SAX2_MAGIC,
- .characters = (charactersSAXFunc)rexmpp_sax_characters,
- .startElementNs = (startElementNsSAX2Func)rexmpp_sax_start_elem_ns,
- .endElementNs = (endElementNsSAX2Func)rexmpp_sax_end_elem_ns,
- };
s->tcp_state = REXMPP_TCP_NONE;
s->resolver_state = REXMPP_RESOLVER_NONE;
@@ -625,7 +615,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
}
- s->xml_parser = xmlCreatePushParserCtxt(&sax, s, "", 0, NULL);
+ s->xml_parser = rexmpp_xml_parser_new(&sax, s);
if (s->xml_parser == NULL) {
rexmpp_log(s, LOG_CRIT, "Failed to create an XML parser context.");
@@ -633,13 +623,13 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
}
if (rexmpp_dns_ctx_init(s)) {
- xmlFreeParserCtxt(s->xml_parser);
+ rexmpp_xml_parser_free(s->xml_parser);
return REXMPP_E_DNS;
}
if (rexmpp_tls_init(s)) {
rexmpp_dns_ctx_deinit(s);
- xmlFreeParserCtxt(s->xml_parser);
+ rexmpp_xml_parser_free(s->xml_parser);
return REXMPP_E_TLS;
}
@@ -647,7 +637,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
if (err) {
rexmpp_tls_deinit(s);
rexmpp_dns_ctx_deinit(s);
- xmlFreeParserCtxt(s->xml_parser);
+ rexmpp_xml_parser_free(s->xml_parser);
return REXMPP_E_SASL;
}
@@ -655,7 +645,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
rexmpp_sasl_ctx_deinit(s);
rexmpp_tls_deinit(s);
rexmpp_dns_ctx_deinit(s);
- xmlFreeParserCtxt(s->xml_parser);
+ rexmpp_xml_parser_free(s->xml_parser);
}
#ifdef HAVE_GPGME
@@ -668,7 +658,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
rexmpp_tls_deinit(s);
rexmpp_dns_ctx_deinit(s);
rexmpp_jingle_stop(s);
- xmlFreeParserCtxt(s->xml_parser);
+ rexmpp_xml_parser_free(s->xml_parser);
return REXMPP_E_PGP;
}
#else
@@ -774,7 +764,7 @@ void rexmpp_done (rexmpp_t *s) {
rexmpp_sasl_ctx_deinit(s);
rexmpp_tls_deinit(s);
rexmpp_dns_ctx_deinit(s);
- xmlFreeParserCtxt(s->xml_parser);
+ rexmpp_xml_parser_free(s->xml_parser);
if (s->jingle_rtp_description != NULL) {
rexmpp_xml_free(s->jingle_rtp_description);
s->jingle_rtp_description = NULL;
@@ -941,7 +931,7 @@ rexmpp_err_t rexmpp_send_continue (rexmpp_t *s)
s->send_buffer = NULL;
if (s->send_queue != NULL) {
rexmpp_xml_t *node = s->send_queue;
- unsigned char *buf = rexmpp_xml_serialize(node, 0);
+ char *buf = rexmpp_xml_serialize(node, 0);
ret = rexmpp_send_start(s, buf, strlen(buf));
free(buf);
if (ret != REXMPP_SUCCESS) {
@@ -1034,7 +1024,7 @@ rexmpp_err_t rexmpp_send (rexmpp_t *s, rexmpp_xml_t *node)
}
if (s->send_buffer == NULL) {
- unsigned char *buf = rexmpp_xml_serialize(node, 0);
+ char *buf = rexmpp_xml_serialize(node, 0);
ret = rexmpp_send_raw(s, buf, strlen(buf));
free(buf);
rexmpp_xml_free(node);
@@ -1207,7 +1197,8 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, rexmpp_xml_t *elem);
rexmpp_err_t rexmpp_recv (rexmpp_t *s) {
char chunk_raw[4096], *chunk;
- ssize_t chunk_raw_len, chunk_len;
+ size_t chunk_len;
+ ssize_t chunk_raw_len;
int sasl_err;
rexmpp_tls_err_t recv_err;
rexmpp_err_t err = REXMPP_SUCCESS;
@@ -1234,7 +1225,7 @@ rexmpp_err_t rexmpp_recv (rexmpp_t *s) {
chunk = chunk_raw;
chunk_len = chunk_raw_len;
}
- xmlParseChunk(s->xml_parser, chunk, chunk_len, 0);
+ rexmpp_xml_parser_feed(s->xml_parser, chunk, chunk_len);
if (chunk != chunk_raw && chunk != NULL) {
free(chunk);
}
@@ -1399,7 +1390,7 @@ rexmpp_process_tls_conn_err (rexmpp_t *s,
return rexmpp_stream_open(s);
} else {
/* A STARTTLS connection, restart the stream. */
- xmlCtxtResetPush(s->xml_parser, "", 0, "", "utf-8");
+ s->xml_parser = rexmpp_xml_parser_reset(s->xml_parser);
return rexmpp_stream_open(s);
}
} else {
@@ -1414,7 +1405,7 @@ rexmpp_err_t rexmpp_connected_to_server (rexmpp_t *s) {
"Connected to the server, the used address record was %s",
s->server_socket_dns_secure ? "secure" : "not secure");
s->reconnect_number = 0;
- xmlCtxtResetPush(s->xml_parser, "", 0, "", "utf-8");
+ s->xml_parser = rexmpp_xml_parser_reset(s->xml_parser);
if (s->tls_state == REXMPP_TLS_AWAITING_DIRECT) {
return rexmpp_process_tls_conn_err(s, rexmpp_tls_connect(s));
} else {
@@ -2135,7 +2126,7 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, rexmpp_xml_t *elem) {
return REXMPP_E_SASL;
}
s->sasl_state = REXMPP_SASL_ACTIVE;
- xmlCtxtResetPush(s->xml_parser, "", 0, "", "utf-8");
+ s->xml_parser = rexmpp_xml_parser_reset(s->xml_parser);
return rexmpp_stream_open(s);
} else if (rexmpp_xml_match(elem, "urn:ietf:params:xml:ns:xmpp-sasl",
"failure")) {
@@ -2221,37 +2212,37 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, rexmpp_xml_t *elem) {
}
-void rexmpp_sax_characters (rexmpp_t *s, const char *ch, int len)
+/* These SAX handlers are similar to those in rexmpp_xml.c, might be
+ nice to reuse them. */
+void rexmpp_sax_characters (rexmpp_t *s, const char *ch, size_t len)
{
if (s->current_element != NULL) {
- rexmpp_xml_t *text_node = rexmpp_xml_new_text_len(ch, len);
- if (text_node != NULL) {
- text_node->next = s->current_element->alt.elem.children;
- s->current_element->alt.elem.children = text_node;
+ rexmpp_xml_t *last_node = s->current_element->alt.elem.children;
+ if (last_node != NULL && last_node->type == REXMPP_XML_TEXT) {
+ /* The last child is textual as well, just extend it */
+ size_t last_len = strlen(last_node->alt.text);
+ last_node->alt.text = realloc(last_node->alt.text, last_len + len + 1);
+ strncpy(last_node->alt.text + last_len, ch, len);
+ last_node->alt.text[last_len + len] = '\0';
+ } else {
+ rexmpp_xml_t *text_node = rexmpp_xml_new_text_len(ch, len);
+ if (text_node != NULL) {
+ text_node->next = s->current_element->alt.elem.children;
+ s->current_element->alt.elem.children = text_node;
+ }
}
}
}
void rexmpp_sax_start_elem_ns (rexmpp_t *s,
- const char *localname,
- const char *prefix,
- const char *URI,
- int nb_namespaces,
- const char **namespaces,
- int nb_attributes,
- int nb_defaulted,
- const char **attributes)
+ const char *name,
+ const char *namespace,
+ rexmpp_xml_attr_t *attributes)
{
- /* Not checking namespaces beyond URI. */
- (void)nb_namespaces;
- (void)namespaces;
- (void)nb_defaulted;
- (void)prefix;
-
- int i;
if (s->stream_state == REXMPP_STREAM_OPENING &&
- strcmp(localname, "stream") == 0 &&
- strcmp(URI, "http://etherx.jabber.org/streams") == 0) {
+ s->current_element == NULL &&
+ strcmp(name, "stream") == 0 &&
+ strcmp(namespace, "http://etherx.jabber.org/streams") == 0) {
rexmpp_log(s, LOG_DEBUG, "stream start");
s->stream_state = REXMPP_STREAM_NEGOTIATION;
return;
@@ -2259,37 +2250,23 @@ void rexmpp_sax_start_elem_ns (rexmpp_t *s,
if (s->stream_state != REXMPP_STREAM_OPENING) {
if (s->current_element == NULL) {
- s->current_element = rexmpp_xml_new_elem(localname, URI);
+ s->current_element = rexmpp_xml_new_elem(name, namespace);
s->current_element_root = s->current_element;
} else {
- rexmpp_xml_t *node = rexmpp_xml_new_elem(localname, URI);
+ rexmpp_xml_t *node = rexmpp_xml_new_elem(name, namespace);
node->next = s->current_element->alt.elem.children;
s->current_element->alt.elem.children = node;
s->current_element = node;
}
- for (i = 0; i < nb_attributes; i++) {
- size_t attr_len = attributes[i * 5 + 4] - attributes[i * 5 + 3];
- char *attr_val = malloc(attr_len + 1);
- attr_val[attr_len] = '\0';
- strncpy(attr_val, attributes[i * 5 + 3], attr_len);
- rexmpp_xml_add_attr_ns(s->current_element,
- attributes[i * 5],
- NULL, attr_val);
- free(attr_val);
- }
+ s->current_element->alt.elem.attributes = attributes;
}
}
-void rexmpp_sax_end_elem_ns (rexmpp_t *s,
- const char *localname,
- const char *prefix,
- const char *URI)
+void rexmpp_sax_end_elem_ns (rexmpp_t *s)
{
- (void)prefix; /* Not interested in prefix here. */
if ((s->stream_state == REXMPP_STREAM_CLOSING ||
s->stream_state == REXMPP_STREAM_ERROR) &&
- strcmp(localname, "stream") == 0 &&
- strcmp(URI, "http://etherx.jabber.org/streams") == 0) {
+ s->current_element == NULL) {
rexmpp_log(s, LOG_DEBUG, "stream end");
if (s->sasl_state == REXMPP_SASL_ACTIVE) {
rexmpp_sasl_ctx_cleanup(s);
@@ -2318,7 +2295,7 @@ void rexmpp_sax_end_elem_ns (rexmpp_t *s,
} else {
/* Done parsing this element; reverse all the lists of children
and queue it. */
- s->current_element = rexmpp_xml_reverse_all(s->current_element);
+ rexmpp_xml_reverse_children(s->current_element);
if (s->input_queue == NULL) {
s->input_queue = s->current_element;
s->input_queue_last = s->current_element;
diff --git a/src/rexmpp.h b/src/rexmpp.h
index 4cde101..a3df757 100644
--- a/src/rexmpp.h
+++ b/src/rexmpp.h
@@ -13,7 +13,6 @@
#include "config.h"
-#include <libxml/tree.h>
#ifdef HAVE_GPGME
#include <gpgme.h>
#endif
@@ -188,6 +187,7 @@ enum tls_pol {
typedef enum rexmpp_err rexmpp_err_t;
#include "rexmpp_xml.h"
+#include "rexmpp_xml_parser.h"
#include "rexmpp_tcp.h"
#include "rexmpp_socks.h"
#include "rexmpp_dns.h"
@@ -362,8 +362,8 @@ struct rexmpp
NULL if there is anything in the send queue). Not appending data
to it, see send_queue for queuing. */
char *send_buffer;
- ssize_t send_buffer_len;
- ssize_t send_buffer_sent;
+ size_t send_buffer_len;
+ size_t send_buffer_sent;
/* A queue of XML elements to send. */
rexmpp_xml_t *send_queue;
@@ -374,7 +374,7 @@ struct rexmpp
/* XML parser context, and current element pointer for building
XML nodes with a SAX2 parser interface. */
- xmlParserCtxtPtr xml_parser;
+ rexmpp_xml_parser_ctx_t xml_parser;
/* The children are stored in reverse order during building. */
rexmpp_xml_t *current_element_root;
diff --git a/src/rexmpp_jingle.c b/src/rexmpp_jingle.c
index c91c35d..0351f1d 100644
--- a/src/rexmpp_jingle.c
+++ b/src/rexmpp_jingle.c
@@ -454,7 +454,7 @@ rexmpp_jingle_send_file (rexmpp_t *s,
char *hash_base64 = NULL;
size_t hash_base64_len = 0;
- rexmpp_base64_to(gcry_md_read(hd, GCRY_MD_SHA256),
+ rexmpp_base64_to((char*)gcry_md_read(hd, GCRY_MD_SHA256),
gcry_md_get_algo_dlen(GCRY_MD_SHA256),
&hash_base64,
&hash_base64_len);
@@ -467,7 +467,7 @@ rexmpp_jingle_send_file (rexmpp_t *s,
hash_base64 = NULL;
hash_base64_len = 0;
- rexmpp_base64_to(gcry_md_read(hd, GCRY_MD_SHA3_256),
+ rexmpp_base64_to((char*)gcry_md_read(hd, GCRY_MD_SHA3_256),
gcry_md_get_algo_dlen(GCRY_MD_SHA3_256),
&hash_base64,
&hash_base64_len);
@@ -740,7 +740,7 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent,
rexmpp_jingle_session_t *sess = data;
gnutls_x509_crt_t *cert_list;
- int cert_list_size = 0;
+ unsigned int cert_list_size = 0;
/* We'll need a certificate a bit later, but checking it before
allocating other things. */
int err = gnutls_certificate_get_x509_crt(sess->s->tls->dtls_cred, 0,
@@ -755,7 +755,7 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent,
char fp[32], fp_str[97];
size_t fp_size = 32;
gnutls_x509_crt_get_fingerprint(cert_list[0], GNUTLS_DIG_SHA256, fp, &fp_size);
- int i;
+ unsigned int i;
for (i = 0; i < 32; i++) {
snprintf(fp_str + i * 3, 4, "%02X:", fp[i] & 0xFF);
}
@@ -1166,15 +1166,15 @@ rexmpp_jingle_ice_recv_cb (NiceAgent *agent, guint stream_id, guint component_id
}
uint16_t port_out = comp->udp_port_out;
if (component_id == 1) {
- err = srtp_unprotect(srtp_in, buf, &len);
+ err = srtp_unprotect(srtp_in, buf, (int*)&len);
if (err == srtp_err_status_auth_fail && comp->session->rtcp_mux) {
/* Try to demultiplex. Maybe there's a better way to do it,
but this will do for now. */
- err = srtp_unprotect_rtcp(srtp_in, buf, &len);
+ err = srtp_unprotect_rtcp(srtp_in, buf, (int*)&len);
port_out = comp->session->component[0].udp_port_out;
}
} else {
- err = srtp_unprotect_rtcp(srtp_in, buf, &len);
+ err = srtp_unprotect_rtcp(srtp_in, buf, (int*)&len);
}
if (err) {
rexmpp_log(comp->s, LOG_ERR, "SRT(C)P unprotect error %d on component %d",
@@ -1818,7 +1818,7 @@ rexmpp_jingle_run (rexmpp_t *s,
char key_mat[4096];
int err;
gnutls_datum_t client_key, client_salt, server_key, server_salt;
- char client_sess_key[SRTP_AES_ICM_128_KEY_LEN_WSALT * 2],
+ unsigned char client_sess_key[SRTP_AES_ICM_128_KEY_LEN_WSALT * 2],
server_sess_key[SRTP_AES_ICM_128_KEY_LEN_WSALT * 2];
for (sess = s->jingle->sessions; sess != NULL; sess = sess->next) {
char input[4096 + SRTP_MAX_TRAILER_LEN];
diff --git a/src/rexmpp_roster.c b/src/rexmpp_roster.c
index 6bc05fc..0048848 100644
--- a/src/rexmpp_roster.c
+++ b/src/rexmpp_roster.c
@@ -10,8 +10,6 @@
#include "rexmpp_xml.h"
#include <syslog.h>
#include <string.h>
-#include <libxml/tree.h>
-#include <libxml/xmlsave.h>
rexmpp_xml_t *
rexmpp_roster_find_item (rexmpp_t *s,
diff --git a/src/rexmpp_tls.c b/src/rexmpp_tls.c
index 96c042a..e1de5ea 100644
--- a/src/rexmpp_tls.c
+++ b/src/rexmpp_tls.c
@@ -139,7 +139,8 @@ rexmpp_tls_err_t
rexmpp_tls_connect (rexmpp_t *s) {
#if defined(USE_GNUTLS)
if (s->tls_state != REXMPP_TLS_HANDSHAKE) {
- gnutls_datum_t xmpp_client_protocol = {"xmpp-client", strlen("xmpp-client")};
+ gnutls_datum_t xmpp_client_protocol =
+ {(unsigned char*)"xmpp-client", strlen("xmpp-client")};
rexmpp_log(s, LOG_DEBUG, "starting TLS");
gnutls_init(&s->tls->gnutls_session, GNUTLS_CLIENT);
gnutls_session_set_ptr(s->tls->gnutls_session, s);
@@ -172,7 +173,7 @@ rexmpp_tls_connect (rexmpp_t *s) {
rexmpp_log(s, LOG_DEBUG, "Waiting for TLS handshake to complete");
return REXMPP_TLS_E_AGAIN;
} else if (ret == 0) {
- int status;
+ unsigned int status;
int srv_is_secure = 0;
if (s->stream_state == REXMPP_STREAM_NONE &&
@@ -329,7 +330,8 @@ rexmpp_tls_send (rexmpp_t *s, void *data, size_t data_size, ssize_t *written)
}
#elif defined(USE_OPENSSL)
*written = -1;
- int ret = SSL_write_ex(s->tls->openssl_conn, data, data_size, written);
+ int ret = SSL_write_ex(s->tls->openssl_conn, data, data_size,
+ (size_t*)written);
if (ret > 0) {
return REXMPP_TLS_SUCCESS;
} else {
@@ -360,7 +362,8 @@ rexmpp_tls_recv (rexmpp_t *s, void *data, size_t data_size, ssize_t *received) {
}
#elif defined(USE_OPENSSL)
*received = -1;
- int ret = SSL_read_ex(s->tls->openssl_conn, data, data_size, received);
+ int ret = SSL_read_ex(s->tls->openssl_conn, data, data_size,
+ (size_t*)received);
if (ret > 0) {
return REXMPP_TLS_SUCCESS;
} else {
diff --git a/src/rexmpp_xml.c b/src/rexmpp_xml.c
index 862a783..4907d4e 100644
--- a/src/rexmpp_xml.c
+++ b/src/rexmpp_xml.c
@@ -8,8 +8,6 @@
#include <string.h>
#include <stdio.h>
-#include <libxml/tree.h>
-#include <libxml/xmlsave.h>
#include "rexmpp.h"
#include "rexmpp_utf8.h"
#include "rexmpp_xml.h"
@@ -119,135 +117,7 @@ rexmpp_xml_t *rexmpp_xml_clone_list (rexmpp_xml_t *node) {
}
return first;
}
-#endif
-
-rexmpp_xml_t *rexmpp_xml_from_libxml2 (xmlNodePtr from) {
- if (from == NULL) {
- return NULL;
- }
-
- rexmpp_xml_t *to = NULL;
- if (from->type == XML_ELEMENT_NODE) {
- to = malloc(sizeof(rexmpp_xml_t));
-
- /* Type */
- to->type = REXMPP_XML_ELEMENT;
-
- /* Name and namespace */
- to->alt.elem.qname.name = strdup(from->name);
- if (from->nsDef != NULL && from->nsDef->href != NULL) {
- to->alt.elem.qname.namespace = strdup(from->nsDef->href);
- } else {
- to->alt.elem.qname.namespace = NULL;
- }
-
- /* Attributes */
- to->alt.elem.attributes = NULL;
- struct _xmlAttr *from_attr;
- rexmpp_xml_attr_t **to_next_attr = &(to->alt.elem.attributes);
- for (from_attr = from->properties;
- from_attr != NULL;
- from_attr = from_attr->next)
- {
- rexmpp_xml_attr_t *to_attr =
- malloc(sizeof(rexmpp_xml_attr_t));
- to_attr->qname.name = strdup(from_attr->name);
- to_attr->qname.namespace = NULL;
- if (from_attr->ns != NULL && from_attr->ns->href != NULL) {
- to_attr->qname.namespace = strdup(from_attr->ns->href);
- to_attr->value =
- xmlGetNsProp(from, to_attr->qname.name, to_attr->qname.namespace);
- } else {
- to_attr->value = xmlGetProp(from, to_attr->qname.name);
- }
- to_attr->next = NULL;
-
- *to_next_attr = to_attr;
- to_next_attr = &(to_attr->next);
- }
-
- /* Children */
- to->alt.elem.children = NULL;
- xmlNodePtr from_child;
- rexmpp_xml_t **to_next_child = &(to->alt.elem.children);
- for (from_child = from->children;
- from_child != NULL;
- from_child = from_child->next)
- {
- rexmpp_xml_t *next_child = rexmpp_xml_from_libxml2(from_child);
- if (next_child != NULL) {
- *to_next_child = next_child;
- to_next_child = &(next_child->next);
- }
- }
-
- /* Next */
- to->next = NULL;
-
- } else if (from->type == XML_TEXT_NODE) {
- to = malloc(sizeof(rexmpp_xml_t));
- to->type = REXMPP_XML_TEXT;
- to->alt.text = xmlNodeGetContent(from);
- to->next = NULL;
- }
- return to;
-}
-
-rexmpp_xml_t *rexmpp_xml_from_libxml2_list (xmlNodePtr from) {
- if (from == NULL) {
- return NULL;
- }
- rexmpp_xml_t *to = rexmpp_xml_from_libxml2(from);
- if (from->next != NULL) {
- to->next = rexmpp_xml_from_libxml2_list(from->next);
- }
- return to;
-}
-
-xmlNodePtr rexmpp_xml_to_libxml2 (rexmpp_xml_t *from) {
- if (from == NULL) {
- return NULL;
- }
-
- if (from->type == REXMPP_XML_TEXT) {
- xmlNodePtr to = xmlNewText(from->alt.text);
- to->next = rexmpp_xml_to_libxml2(from->next);
- return to;
- }
-
- /* Name and namespace */
- xmlNodePtr to = xmlNewNode(NULL, from->alt.elem.qname.name);
- if (from->alt.elem.qname.namespace != NULL) {
- xmlNewNs(to, from->alt.elem.qname.namespace, NULL);
- }
-
- /* Attributes */
- rexmpp_xml_attr_t *attr = from->alt.elem.attributes;
- while (attr != NULL) {
- /* TODO: Would be nice to take namespaces into account, though
- they are currently not used for attributes. */
- xmlNewProp(to, attr->qname.name, attr->value);
- attr = attr->next;
- }
-
- /* Children */
- rexmpp_xml_t *child = from->alt.elem.children;
- while (child != NULL) {
- xmlAddChild(to, rexmpp_xml_to_libxml2(child));
- child = child->next;
- }
- return to;
-}
-
-xmlNodePtr rexmpp_xml_to_libxml2_list (rexmpp_xml_t *from) {
- xmlNodePtr to = rexmpp_xml_to_libxml2(from);
- if (from->next != NULL) {
- xmlAddNextSibling(to, rexmpp_xml_to_libxml2_list(from->next));
- }
- return to;
-}
-#ifndef USE_RUST
rexmpp_xml_t *rexmpp_xml_new_text (const char *str) {
rexmpp_xml_t *node = malloc(sizeof(rexmpp_xml_t));
node->type = REXMPP_XML_TEXT;
@@ -590,32 +460,126 @@ rexmpp_xml_add_id (rexmpp_t *s,
}
#endif
-xmlNodePtr rexmpp_xml_parse_libxml2 (const char *str, int str_len) {
- xmlNodePtr elem = NULL;
- xmlDocPtr doc = xmlReadMemory(str, str_len, "", "utf-8", XML_PARSE_NONET);
- if (doc != NULL) {
- elem = xmlCopyNode(xmlDocGetRootElement(doc), 1);
- xmlFreeDoc(doc);
+/* These SAX handlers are similar to those from rexmpp.c, and perhaps
+ can be reused. */
+void rexmpp_xml_parse_sax_characters (struct rexmpp_xml_builder *builder,
+ const char *ch,
+ size_t len)
+{
+ if (builder->current != NULL) {
+ rexmpp_xml_t *last_node = builder->current->alt.elem.children;
+ if (last_node != NULL && last_node->type == REXMPP_XML_TEXT) {
+ /* The last child is textual as well, just extend it */
+ size_t last_len = strlen(last_node->alt.text);
+ last_node->alt.text = realloc(last_node->alt.text, last_len + len + 1);
+ strncpy(last_node->alt.text + last_len, ch, len);
+ last_node->alt.text[last_len + len] = '\0';
+ } else {
+ rexmpp_xml_t *text_node = rexmpp_xml_new_text_len(ch, len);
+ if (text_node != NULL) {
+ text_node->next = builder->current->alt.elem.children;
+ builder->current->alt.elem.children = text_node;
+ }
+ }
+ }
+}
+
+void rexmpp_xml_parse_sax_start_elem_ns (struct rexmpp_xml_builder *builder,
+ const char *name,
+ const char *namespace,
+ rexmpp_xml_attr_t *attributes)
+{
+ if (builder->current == NULL && builder->root == NULL) {
+ /* Just started */
+ builder->current = rexmpp_xml_new_elem(name, namespace);
+ builder->root = builder->current;
+ } else if (builder->current != NULL) {
+ /* Parsing is in progress */
+ rexmpp_xml_t *node = rexmpp_xml_new_elem(name, namespace);
+ node->next = builder->current->alt.elem.children;
+ builder->current->alt.elem.children = node;
+ builder->current = node;
+ } else {
+ /* The parsind is over, but we are receiving these events
+ still. Just free the attribute lists, ignore the rest. */
+ rexmpp_xml_attribute_free_list(attributes);
}
- return elem;
+ builder->current->alt.elem.attributes = attributes;
}
+void rexmpp_xml_parse_sax_end_elem_ns (struct rexmpp_xml_builder *builder)
+{
+ if (builder->current != builder->root) {
+ /* Find the parent, set it as current element. */
+ rexmpp_xml_t *parent = builder->root;
+ while (parent->alt.elem.children != builder->current) {
+ parent = parent->alt.elem.children;
+ }
+ builder->current = parent;
+ } else {
+ /* Done parsing this element; reverse all the lists of children. */
+ builder->current = NULL;
+ rexmpp_xml_reverse_children(builder->root);
+ }
+}
+
+struct rexmpp_xml_parser_handlers builder_sax = {
+ (rexmpp_xml_parser_element_start)rexmpp_xml_parse_sax_start_elem_ns,
+ (rexmpp_xml_parser_element_end)rexmpp_xml_parse_sax_end_elem_ns,
+ (rexmpp_xml_parser_characters)rexmpp_xml_parse_sax_characters
+};
+
rexmpp_xml_t *rexmpp_xml_parse (const char *str, int str_len) {
- xmlNodePtr node_lxml2 = rexmpp_xml_parse_libxml2(str, str_len);
- if (node_lxml2 != NULL) {
- rexmpp_xml_t *node = rexmpp_xml_from_libxml2(node_lxml2);
- xmlFreeNode(node_lxml2);
- return node;
+ struct rexmpp_xml_builder builder = { NULL, NULL };
+ rexmpp_xml_parser_ctx_t parser =
+ rexmpp_xml_parser_new(&builder_sax, &builder);
+ rexmpp_xml_parser_feed(parser, str, str_len);
+ rexmpp_xml_parser_free(parser);
+ if (builder.current != NULL) {
+ /* The parsing is not complete. */
+ rexmpp_xml_free(builder.root);
+ return NULL;
}
- return NULL;
+ return builder.root;
+}
+
+rexmpp_xml_t *rexmpp_xml_read_fd (FILE *fd) {
+ struct rexmpp_xml_builder builder = { NULL, NULL };
+ rexmpp_xml_parser_ctx_t parser =
+ rexmpp_xml_parser_new(&builder_sax, &builder);
+ if (parser == NULL) {
+ return NULL;
+ }
+
+ char *buf;
+ size_t len = 0;
+ do {
+ len = getline(&buf, &len, fd);
+ if (len > 0) {
+ rexmpp_xml_parser_feed(parser, buf, len);
+ }
+ } while (len > 0 &&
+ ! (builder.root != NULL && builder.current == NULL) );
+
+ rexmpp_xml_parser_free(parser);
+
+ if (builder.current != NULL) {
+ /* The parsing is not complete. */
+ rexmpp_xml_free(builder.root);
+ return NULL;
+ }
+
+ return builder.root;
}
rexmpp_xml_t *rexmpp_xml_read_file (const char *path) {
- xmlDocPtr doc = xmlReadFile(path, "utf-8", XML_PARSE_NONET);
- xmlNodePtr lxml2 = xmlDocGetRootElement(doc);
- rexmpp_xml_t *ret = rexmpp_xml_from_libxml2(lxml2);
- xmlFreeDoc(doc);
- return ret;
+ FILE *fd = fopen(path, "r");
+ if (fd == NULL) {
+ return NULL;
+ }
+ rexmpp_xml_t *node = rexmpp_xml_read_fd(fd);
+ fclose(fd);
+ return node;
}
#ifndef USE_RUST
@@ -806,7 +770,7 @@ char *rexmpp_xml_text_child (rexmpp_xml_t *node) {
return rexmpp_xml_text(rexmpp_xml_children(node));
}
-rexmpp_xml_t *rexmpp_xml_reverse (rexmpp_xml_t *node) {
+rexmpp_xml_t *rexmpp_xml_reverse_list (rexmpp_xml_t *node) {
rexmpp_xml_t *next, *prev = NULL;
while (node != NULL) {
next = node->next;
@@ -817,15 +781,17 @@ rexmpp_xml_t *rexmpp_xml_reverse (rexmpp_xml_t *node) {
return prev;
}
-rexmpp_xml_t *rexmpp_xml_reverse_all (rexmpp_xml_t *node) {
- node = rexmpp_xml_reverse(node);
+void rexmpp_xml_reverse_children (rexmpp_xml_t *node) {
+ if (node == NULL || node->type != REXMPP_XML_ELEMENT) {
+ return;
+ }
+ node->alt.elem.children = rexmpp_xml_reverse_list(node->alt.elem.children);
rexmpp_xml_t *cur;
- for (cur = node; cur != NULL; cur = cur->next) {
- if (cur->type == REXMPP_XML_ELEMENT) {
- cur->alt.elem.children = rexmpp_xml_reverse_all(cur->alt.elem.children);
+ for (cur = node->alt.elem.children; cur != NULL; cur = cur->next) {
+ if (cur->type == REXMPP_XML_ELEMENT && cur->alt.elem.children != NULL) {
+ rexmpp_xml_reverse_children(cur);
}
}
- return node;
}
#endif
diff --git a/src/rexmpp_xml.h b/src/rexmpp_xml.h
index 972c75c..d5a9af1 100644
--- a/src/rexmpp_xml.h
+++ b/src/rexmpp_xml.h
@@ -9,8 +9,6 @@
#ifndef REXMPP_XML_H
#define REXMPP_XML_H
-#include <libxml/tree.h>
-
typedef struct rexmpp_xml_qname rexmpp_xml_qname_t;
typedef struct rexmpp_xml_attribute rexmpp_xml_attr_t;
typedef struct rexmpp_xml_node rexmpp_xml_t;
@@ -46,6 +44,10 @@ struct rexmpp_xml_node {
rexmpp_xml_t *next;
};
+struct rexmpp_xml_builder {
+ rexmpp_xml_t *current;
+ rexmpp_xml_t *root;
+};
void rexmpp_xml_qname_free (rexmpp_xml_qname_t *qname);
void rexmpp_xml_attribute_free (rexmpp_xml_attr_t *attr);
@@ -72,22 +74,6 @@ rexmpp_xml_t *rexmpp_xml_clone (rexmpp_xml_t *node);
rexmpp_xml_t *rexmpp_xml_clone_list (rexmpp_xml_t *node);
/**
- @brief Creates a single ::rexmpp_xml_t XML node out of libxml2's
- xmlNode, without siblings.
-*/
-rexmpp_xml_t *rexmpp_xml_from_libxml2 (xmlNodePtr from);
-
-/**
- @brief Creates a ::rexmpp_xml_t XML node out of libxml2's xmlNode,
- with siblings.
-*/
-rexmpp_xml_t *rexmpp_xml_from_libxml2_list (xmlNodePtr from);
-
-xmlNodePtr rexmpp_xml_to_libxml2 (rexmpp_xml_t *from);
-
-xmlNodePtr rexmpp_xml_to_libxml2_list (rexmpp_xml_t *from);
-
-/**
@brief Creates a textual ::rexmpp_xml_t XML node (with type =
::REXMPP_XML_TEXT).
*/
@@ -243,10 +229,40 @@ int rexmpp_xml_eq (rexmpp_xml_t *n1, rexmpp_xml_t *n2);
*/
rexmpp_xml_t *rexmpp_xml_parse (const char *str, int str_len);
+/**
+ @brief Reads XML from a file stream, reading the stream line by
+ line.
+ @param[in] fd A file stream
+ @returns Parsed XML, or NULL on failure.
+*/
+rexmpp_xml_t *rexmpp_xml_read_fd (FILE *fd);
+
+/**
+ @brief Reads XML from a file
+ @param[in] path A file path
+ @returns Parsed XML, or NULL on failure.
+*/
rexmpp_xml_t *rexmpp_xml_read_file (const char *path);
+
+/**
+ @brief Writes XML into a file
+ @param[in] path A file path
+ @param[in] node XML to write
+ @returns 0 on success, -1 on failure.
+*/
int rexmpp_xml_write_file (const char *path, rexmpp_xml_t* node);
-rexmpp_xml_t *rexmpp_xml_reverse (rexmpp_xml_t *node);
-rexmpp_xml_t *rexmpp_xml_reverse_all (rexmpp_xml_t *node);
+/**
+ @brief Reverses a linked list of XML nodes
+ @param[in,out] node The head of the list to reverse
+ @returns The new head of the list
+*/
+rexmpp_xml_t *rexmpp_xml_reverse_list (rexmpp_xml_t *node);
+
+/**
+ @brief Recursively reverses children of an XML element
+ @param[in,out] node The root XML element
+*/
+void rexmpp_xml_reverse_children (rexmpp_xml_t *node);
#endif
diff --git a/src/rexmpp_xml.rs b/src/rexmpp_xml.rs
index 1f9f4d6..717501d 100644
--- a/src/rexmpp_xml.rs
+++ b/src/rexmpp_xml.rs
@@ -975,8 +975,8 @@ fn rexmpp_xml_text_child (node: *mut RexmppXML)
#[no_mangle]
extern "C"
-fn rexmpp_xml_reverse (mut node: *mut RexmppXML)
- -> *mut RexmppXML {
+fn rexmpp_xml_reverse_list (mut node: *mut RexmppXML)
+ -> *mut RexmppXML {
let mut next;
let mut prev = ptr::null_mut();
while node != ptr::null_mut() {
@@ -992,14 +992,22 @@ fn rexmpp_xml_reverse (mut node: *mut RexmppXML)
#[no_mangle]
extern "C"
-fn rexmpp_xml_reverse_all (node: *mut RexmppXML)
- -> *mut RexmppXML {
- let mut cur = node;
- while cur != ptr::null_mut() {
- unsafe {
- if (*cur).node_type == NodeType::Element {
+fn rexmpp_xml_reverse_children (node: *mut RexmppXML)
+ -> *mut RexmppXML {
+ unsafe {
+ if node == ptr::null_mut() || (*node).node_type != NodeType::Element {
+ return node;
+ }
+ (*node).alt.elem.children =
+ rexmpp_xml_reverse_list((*node).alt.elem.children);;
+
+ let mut cur = node;
+ while cur != ptr::null_mut() {
+ if (*cur).node_type == NodeType::Element &&
+ (*cur).alt.elem.children != ptr::null_mut()
+ {
(*cur).alt.elem.children =
- rexmpp_xml_reverse_all((*cur).alt.elem.children);
+ rexmpp_xml_reverse_children((*cur).alt.elem.children);
}
cur = (*cur).next;
}
diff --git a/src/rexmpp_xml_parser.c b/src/rexmpp_xml_parser.c
new file mode 100644
index 0000000..d30d630
--- /dev/null
+++ b/src/rexmpp_xml_parser.c
@@ -0,0 +1,318 @@
+/**
+ @file rexmpp_xml_parser.c
+ @brief XML parsing for rexmpp
+ @author defanor <defanor@uberspace.net>
+ @date 2023
+ @copyright MIT license.
+*/
+
+#include "rexmpp.h"
+#include "rexmpp_xml.h"
+#include "rexmpp_xml_parser.h"
+#include "config.h"
+
+#if defined(USE_LIBXML2)
+
+void rexmpp_xml_sax_characters (rexmpp_xml_parser_ctx_t ctx,
+ const char *ch,
+ int len)
+{
+ ctx->handlers->text(ctx->user_data, ch, len);
+}
+
+void rexmpp_xml_sax_elem_start (rexmpp_xml_parser_ctx_t ctx,
+ const char *localname,
+ const char *prefix,
+ const char *URI,
+ int nb_namespaces,
+ const char **namespaces,
+ int nb_attributes,
+ int nb_defaulted,
+ const char **attributes)
+{
+ (void)prefix;
+ (void)nb_namespaces;
+ (void)namespaces;
+ (void)nb_defaulted;
+ rexmpp_xml_attr_t *attrs = NULL;
+ int i;
+ for (i = nb_attributes - 1; i >= 0; i--) {
+ size_t attr_len = attributes[i * 5 + 4] - attributes[i * 5 + 3];
+ char *attr_val = malloc(attr_len + 1);
+ attr_val[attr_len] = '\0';
+ strncpy(attr_val, attributes[i * 5 + 3], attr_len);
+ rexmpp_xml_attr_t *attr =
+ rexmpp_xml_attr_new(attributes[i * 5], NULL, attr_val);
+ free(attr_val);
+ attr->next = attrs;
+ attrs = attr;
+ }
+
+ ctx->handlers->elem_start(ctx->user_data, localname, URI, attrs);
+}
+
+void rexmpp_xml_sax_elem_end (rexmpp_xml_parser_ctx_t ctx,
+ const char *localname,
+ const char *prefix,
+ const char *URI)
+{
+ (void)localname;
+ (void)prefix;
+ (void)URI;
+ ctx->handlers->elem_end(ctx->user_data);
+}
+
+xmlSAXHandler rexmpp_xml_parser_sax = {
+ .initialized = XML_SAX2_MAGIC,
+ .characters = (charactersSAXFunc)rexmpp_xml_sax_characters,
+ .startElementNs = (startElementNsSAX2Func)rexmpp_xml_sax_elem_start,
+ .endElementNs = (endElementNsSAX2Func)rexmpp_xml_sax_elem_end,
+};
+
+
+/* rexmpp_xml_t *rexmpp_xml_from_libxml2 (xmlNodePtr from) { */
+/* if (from == NULL) { */
+/* return NULL; */
+/* } */
+
+/* rexmpp_xml_t *to = NULL; */
+/* if (from->type == XML_ELEMENT_NODE) { */
+/* to = malloc(sizeof(rexmpp_xml_t)); */
+
+/* /\* Type *\/ */
+/* to->type = REXMPP_XML_ELEMENT; */
+
+/* /\* Name and namespace *\/ */
+/* to->alt.elem.qname.name = strdup(from->name); */
+/* if (from->nsDef != NULL && from->nsDef->href != NULL) { */
+/* to->alt.elem.qname.namespace = strdup(from->nsDef->href); */
+/* } else { */
+/* to->alt.elem.qname.namespace = NULL; */
+/* } */
+
+/* /\* Attributes *\/ */
+/* to->alt.elem.attributes = NULL; */
+/* struct _xmlAttr *from_attr; */
+/* rexmpp_xml_attr_t **to_next_attr = &(to->alt.elem.attributes); */
+/* for (from_attr = from->properties; */
+/* from_attr != NULL; */
+/* from_attr = from_attr->next) */
+/* { */
+/* rexmpp_xml_attr_t *to_attr = */
+/* malloc(sizeof(rexmpp_xml_attr_t)); */
+/* to_attr->qname.name = strdup(from_attr->name); */
+/* to_attr->qname.namespace = NULL; */
+/* if (from_attr->ns != NULL && from_attr->ns->href != NULL) { */
+/* to_attr->qname.namespace = strdup(from_attr->ns->href); */
+/* to_attr->value = */
+/* xmlGetNsProp(from, to_attr->qname.name, to_attr->qname.namespace); */
+/* } else { */
+/* to_attr->value = xmlGetProp(from, to_attr->qname.name); */
+/* } */
+/* to_attr->next = NULL; */
+
+/* *to_next_attr = to_attr; */
+/* to_next_attr = &(to_attr->next); */
+/* } */
+
+/* /\* Children *\/ */
+/* to->alt.elem.children = NULL; */
+/* xmlNodePtr from_child; */
+/* rexmpp_xml_t **to_next_child = &(to->alt.elem.children); */
+/* for (from_child = from->children; */
+/* from_child != NULL; */
+/* from_child = from_child->next) */
+/* { */
+/* rexmpp_xml_t *next_child = rexmpp_xml_from_libxml2(from_child); */
+/* if (next_child != NULL) { */
+/* *to_next_child = next_child; */
+/* to_next_child = &(next_child->next); */
+/* } */
+/* } */
+
+/* /\* Next *\/ */
+/* to->next = NULL; */
+
+/* } else if (from->type == XML_TEXT_NODE) { */
+/* to = malloc(sizeof(rexmpp_xml_t)); */
+/* to->type = REXMPP_XML_TEXT; */
+/* to->alt.text = xmlNodeGetContent(from); */
+/* to->next = NULL; */
+/* } */
+/* return to; */
+/* } */
+
+/* rexmpp_xml_t *rexmpp_xml_from_libxml2_list (xmlNodePtr from) { */
+/* if (from == NULL) { */
+/* return NULL; */
+/* } */
+/* rexmpp_xml_t *to = rexmpp_xml_from_libxml2(from); */
+/* if (from->next != NULL) { */
+/* to->next = rexmpp_xml_from_libxml2_list(from->next); */
+/* } */
+/* return to; */
+/* } */
+
+/* xmlNodePtr rexmpp_xml_to_libxml2 (rexmpp_xml_t *from) { */
+/* if (from == NULL) { */
+/* return NULL; */
+/* } */
+
+/* if (from->type == REXMPP_XML_TEXT) { */
+/* xmlNodePtr to = xmlNewText(from->alt.text); */
+/* to->next = rexmpp_xml_to_libxml2(from->next); */
+/* return to; */
+/* } */
+
+/* /\* Name and namespace *\/ */
+/* xmlNodePtr to = xmlNewNode(NULL, from->alt.elem.qname.name); */
+/* if (from->alt.elem.qname.namespace != NULL) { */
+/* xmlNewNs(to, from->alt.elem.qname.namespace, NULL); */
+/* } */
+
+/* /\* Attributes *\/ */
+/* rexmpp_xml_attr_t *attr = from->alt.elem.attributes; */
+/* while (attr != NULL) { */
+/* /\* TODO: Would be nice to take namespaces into account, though */
+/* they are currently not used for attributes. *\/ */
+/* xmlNewProp(to, attr->qname.name, attr->value); */
+/* attr = attr->next; */
+/* } */
+
+/* /\* Children *\/ */
+/* rexmpp_xml_t *child = from->alt.elem.children; */
+/* while (child != NULL) { */
+/* xmlAddChild(to, rexmpp_xml_to_libxml2(child)); */
+/* child = child->next; */
+/* } */
+/* return to; */
+/* } */
+
+/* xmlNodePtr rexmpp_xml_to_libxml2_list (rexmpp_xml_t *from) { */
+/* xmlNodePtr to = rexmpp_xml_to_libxml2(from); */
+/* if (from->next != NULL) { */
+/* xmlAddNextSibling(to, rexmpp_xml_to_libxml2_list(from->next)); */
+/* } */
+/* return to; */
+/* } */
+
+#elif defined(USE_EXPAT)
+
+void XMLCALL
+rexmpp_xml_sax_elem_start (rexmpp_xml_parser_ctx_t ctx,
+ const char *el,
+ const char **attributes)
+{
+ char *buf = strdup(el);
+ char *name = NULL, *namespace = buf;
+ size_t i;
+ for (i = 0; i < strlen(namespace); i++) {
+ if (namespace[i] == '\xff') {
+ name = namespace + i + 1;
+ namespace[i] = '\0';
+ }
+ }
+ if (name == NULL) {
+ name = namespace;
+ namespace = NULL;
+ }
+ rexmpp_xml_attr_t *attrs = NULL;
+ for (i = 0; attributes[i] != NULL; i += 2) {
+ rexmpp_xml_attr_t *attr =
+ rexmpp_xml_attr_new(attributes[i], NULL, attributes[i + 1]);
+ attr->next = attrs;
+ attrs = attr;
+ }
+
+ ctx->handlers->elem_start(ctx->user_data, name, namespace, attrs);
+ free(buf);
+}
+
+void XMLCALL
+rexmpp_xml_sax_elem_end(rexmpp_xml_parser_ctx_t ctx,
+ const XML_Char *name)
+{
+ (void)name;
+ ctx->handlers->elem_end(ctx->user_data);
+}
+
+void XMLCALL
+rexmpp_xml_sax_characters (rexmpp_xml_parser_ctx_t ctx,
+ const XML_Char *ch,
+ int len)
+{
+ ctx->handlers->text(ctx->user_data, ch, len);
+}
+
+#endif
+
+
+
+rexmpp_xml_parser_ctx_t
+rexmpp_xml_parser_new (rexmpp_xml_parser_handlers_t handlers,
+ void *data)
+{
+ rexmpp_xml_parser_ctx_t ctx = malloc(sizeof(struct rexmpp_xml_parser_ctx));
+ if (ctx == NULL) {
+ return NULL;
+ }
+#if defined(USE_LIBXML2)
+ xmlParserCtxtPtr p =
+ xmlCreatePushParserCtxt(&rexmpp_xml_parser_sax, ctx, "", 0, NULL);
+#elif defined(USE_EXPAT)
+ XML_Parser p = XML_ParserCreateNS("utf-8", '\xff');
+ XML_SetUserData(p, ctx);
+ XML_SetStartElementHandler(p, (XML_StartElementHandler)
+ rexmpp_xml_sax_elem_start);
+ XML_SetEndElementHandler(p, (XML_EndElementHandler)
+ rexmpp_xml_sax_elem_end);
+ XML_SetCharacterDataHandler(p, (XML_CharacterDataHandler)
+ rexmpp_xml_sax_characters);
+#endif
+ if (p == NULL) {
+ free(ctx);
+ return NULL;
+ }
+
+ ctx->xml_parser = p;
+ ctx->handlers = handlers;
+ ctx->user_data = data;
+ return ctx;
+}
+
+void rexmpp_xml_parser_free (rexmpp_xml_parser_ctx_t ctx) {
+#if defined(USE_LIBXML2)
+ xmlFreeParserCtxt(ctx->xml_parser);
+#elif defined(USE_EXPAT)
+ XML_ParserFree(ctx->xml_parser);
+#endif
+ free(ctx);
+}
+
+rexmpp_xml_parser_ctx_t rexmpp_xml_parser_reset (rexmpp_xml_parser_ctx_t ctx) {
+#if defined(USE_LIBXML2)
+ xmlCtxtResetPush(ctx->xml_parser, "", 0, "", "utf-8");
+#elif defined(USE_EXPAT)
+ XML_ParserReset(ctx->xml_parser, "utf-8");
+ XML_SetUserData(ctx->xml_parser, ctx);
+ XML_SetStartElementHandler(ctx->xml_parser, (XML_StartElementHandler)
+ rexmpp_xml_sax_elem_start);
+ XML_SetEndElementHandler(ctx->xml_parser, (XML_EndElementHandler)
+ rexmpp_xml_sax_elem_end);
+ XML_SetCharacterDataHandler(ctx->xml_parser, (XML_CharacterDataHandler)
+ rexmpp_xml_sax_characters);
+#endif
+ return ctx;
+}
+
+void
+rexmpp_xml_parser_feed (rexmpp_xml_parser_ctx_t ctx,
+ const char *chunk,
+ size_t len)
+{
+#if defined(USE_LIBXML2)
+ xmlParseChunk(ctx->xml_parser, chunk, len, 0);
+#elif defined(USE_EXPAT)
+ XML_Parse(ctx->xml_parser, chunk, len, 0);
+#endif
+}
diff --git a/src/rexmpp_xml_parser.h b/src/rexmpp_xml_parser.h
new file mode 100644
index 0000000..07464a2
--- /dev/null
+++ b/src/rexmpp_xml_parser.h
@@ -0,0 +1,103 @@
+/**
+ @file rexmpp_xml_parser.h
+ @brief XML parsing for rexmpp
+ @author defanor <defanor@uberspace.net>
+ @date 2023
+ @copyright MIT license.
+*/
+
+#ifndef REXMPP_XML_PARSER_H
+#define REXMPP_XML_PARSER_H
+
+
+#if defined(USE_LIBXML2)
+ #include <libxml/tree.h>
+#elif defined(USE_EXPAT)
+ #include <expat.h>
+#endif
+
+#include "config.h"
+
+typedef void (*rexmpp_xml_parser_element_start) (void *data,
+ const char *name,
+ const char *namespace,
+ rexmpp_xml_attr_t *attributes);
+typedef void (*rexmpp_xml_parser_element_end) (void *data);
+typedef void (*rexmpp_xml_parser_characters) (void *data,
+ const char *ch,
+ size_t len);
+
+struct rexmpp_xml_parser_handlers {
+ rexmpp_xml_parser_element_start elem_start;
+ rexmpp_xml_parser_element_end elem_end;
+ rexmpp_xml_parser_characters text;
+};
+
+
+typedef struct rexmpp_xml_parser_ctx* rexmpp_xml_parser_ctx_t;
+typedef struct rexmpp_xml_parser_handlers* rexmpp_xml_parser_handlers_t;
+
+struct rexmpp_xml_parser_ctx {
+#if defined(USE_LIBXML2)
+ xmlParserCtxtPtr xml_parser;
+#elif defined(USE_EXPAT)
+ XML_Parser xml_parser;
+#endif
+ rexmpp_xml_parser_handlers_t handlers;
+ void *user_data;
+};
+
+/**
+ @brief Allocates a new XML parser context
+ @param[in] handlers SAX-like parser event handlers
+ @param[in] data User-provided data to pass to the handlers
+ @returns A parser context pointer, or NULL on failure.
+*/
+rexmpp_xml_parser_ctx_t
+rexmpp_xml_parser_new (rexmpp_xml_parser_handlers_t handlers,
+ void *data);
+
+/**
+ @brief Frees an XML parser context
+ @param[in] ctx An XML parser context
+*/
+void rexmpp_xml_parser_free (rexmpp_xml_parser_ctx_t ctx);
+
+/**
+ @brief Feeds data to parse into an XML parser
+ @param[in] ctx An XML parser context
+ @param[in] chunk A chunk of data to parse
+ @param[in] len Length of the data chunk
+*/
+void
+rexmpp_xml_parser_feed (rexmpp_xml_parser_ctx_t ctx,
+ const char *chunk,
+ size_t len);
+
+/**
+ @brief Resets a parser context
+ @param[in] ctx An XML parser context
+ @returns A new pointer, since it may change during a reset
+*/
+rexmpp_xml_parser_ctx_t rexmpp_xml_parser_reset (rexmpp_xml_parser_ctx_t ctx);
+
+
+/* #if defined(USE_LIBXML2) */
+/* /\** */
+/* @brief Creates a single ::rexmpp_xml_t XML node out of libxml2's */
+/* xmlNode, without siblings. */
+/* *\/ */
+/* rexmpp_xml_t *rexmpp_xml_from_libxml2 (xmlNodePtr from); */
+
+/* /\** */
+/* @brief Creates a ::rexmpp_xml_t XML node out of libxml2's xmlNode, */
+/* with siblings. */
+/* *\/ */
+/* rexmpp_xml_t *rexmpp_xml_from_libxml2_list (xmlNodePtr from); */
+
+/* xmlNodePtr rexmpp_xml_to_libxml2 (rexmpp_xml_t *from); */
+
+/* xmlNodePtr rexmpp_xml_to_libxml2_list (rexmpp_xml_t *from); */
+/* #endif */
+
+#endif