summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README45
-rw-r--r--configure.ac51
-rw-r--r--doap.xml219
-rw-r--r--emacs/xml_interface.c203
-rw-r--r--emacs/xmpp.el50
-rw-r--r--examples/basic.c51
-rw-r--r--src/Cargo.toml17
-rw-r--r--src/Makefile.am50
-rw-r--r--src/rexmpp.c1444
-rw-r--r--src/rexmpp.h255
-rw-r--r--src/rexmpp.rs280
-rw-r--r--src/rexmpp_base64.h25
-rw-r--r--src/rexmpp_console.c645
-rw-r--r--src/rexmpp_console.h4
-rw-r--r--src/rexmpp_digest.c125
-rw-r--r--src/rexmpp_digest.h85
-rw-r--r--src/rexmpp_dns.c137
-rw-r--r--src/rexmpp_dns.h29
-rw-r--r--src/rexmpp_dns.rs62
-rw-r--r--src/rexmpp_http_upload.c79
-rw-r--r--src/rexmpp_jid.c6
-rw-r--r--src/rexmpp_jid.rs17
-rw-r--r--src/rexmpp_jingle.c1956
-rw-r--r--src/rexmpp_jingle.h74
-rw-r--r--src/rexmpp_openpgp.c286
-rw-r--r--src/rexmpp_openpgp.h10
-rw-r--r--src/rexmpp_pubsub.c51
-rw-r--r--src/rexmpp_pubsub.h4
-rw-r--r--src/rexmpp_random.c34
-rw-r--r--src/rexmpp_random.h29
-rw-r--r--src/rexmpp_random.rs5
-rw-r--r--src/rexmpp_roster.c96
-rw-r--r--src/rexmpp_roster.h14
-rw-r--r--src/rexmpp_rust.rs8
-rw-r--r--src/rexmpp_sasl.c82
-rw-r--r--src/rexmpp_socks.rs180
-rw-r--r--src/rexmpp_tcp.c87
-rw-r--r--src/rexmpp_tcp.h17
-rw-r--r--src/rexmpp_tcp.rs469
-rw-r--r--src/rexmpp_tls.c781
-rw-r--r--src/rexmpp_tls.h82
-rw-r--r--src/rexmpp_utf8.h93
-rw-r--r--src/rexmpp_xml.c805
-rw-r--r--src/rexmpp_xml.h269
-rw-r--r--src/rexmpp_xml.rs1040
-rw-r--r--src/rexmpp_xml_parser.c323
-rw-r--r--src/rexmpp_xml_parser.h106
-rw-r--r--src/rexmpp_xml_parser.rs145
-rw-r--r--tests/Makefile.am28
-rw-r--r--tests/base64.c18
-rw-r--r--tests/send_to_self.c48
-rw-r--r--tests/xml_parse_and_print.c29
-rw-r--r--tests/xml_print_and_parse.c64
53 files changed, 8568 insertions, 2544 deletions
diff --git a/README b/README
index 6e6e27a..858b59c 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,31 @@ 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.
+Mandatory dependencies:
+
+- libxml2, libexpat, or rxml (Rust)
+- gcrypt, nettle, or openssl
+
+Optional dependencies:
+
+- gsasl
+- libunbound or c-ares
+- gnutls with gnutls-dane or openssl
+- icu-i18n
+- gpgme
+- curl
+
+For Jingle calls:
+
+- libnice (with glib)
+- gnutls or openssl
+- libsrtp2
+- portaudio
+- opus (optional)
+
+For use of the alternative Rust implementations, rustc and cargo
+should be available, and the following Rust libraries will be pulled:
+libc, errno, rxml.
A rough roadmap:
@@ -53,9 +73,10 @@ 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 left libxml2 for now:
- could reuse existing libxml2 bindings that way.
+ optional usage of alternative ones.
[.] Automated testing.
+[.] Alternative module implementations in Rust (looks like there is a
+ memory leak somewhere around those though).
- IM features:
@@ -68,20 +89,18 @@ A rough roadmap:
[+] XEP-0092 v1.1: Software Version
[+] XEP-0172 v1.1: User Nickname
[+] XEP-0373 v0.6: OpenPGP for XMPP
-[+] XEP-0402 v1.1: PEP Native Bookmarks (autojoin conferences)
+[+] XEP-0402 v1.1: PEP Native Bookmarks
+[+] XEP-0410 v1.1: MUC Self-Ping
[+] XEP-0363 v1.0: HTTP File Upload (when built with curl)
[+] XEP-0166 v1.1: Jingle
[+] XEP-0234 v0.19: Jingle File Transfer (sending and accepting, but
no requests and no ranged transfers)
[+] XEP-0261 v1.0: Jingle In-Band Bytestreams Transport Method
-[+] XEP-0167 v1.2: Jingle RTP Sessions (relaying RTP packets over an
- encrypted channel to/from local UDP ports, for use with external
- players and streamers)
+[+] XEP-0167 v1.2: Jingle RTP Sessions (uses PortAudio and Opus)
[+] XEP-0176 v1.1: Jingle ICE-UDP Transport Method (uses libnice,
discovers TURN/STUN servers with XEP-0215: External Service
Discovery)
-[+] XEP-0320 v1.0: Use of DTLS-SRTP in Jingle Sessions (uses gnutls
- and libsrtp2)
+[+] XEP-0320 v1.0: Use of DTLS-SRTP in Jingle Sessions
[ ] XEP-0260: Jingle SOCKS5 Bytestreams Transport Method?
[ ] XEP-0391: Jingle Encrypted Transports?
[ ] XEP-0184: Message Delivery Receipts?
diff --git a/configure.ac b/configure.ac
index 26d0a76..bb25c33 100644
--- a/configure.ac
+++ b/configure.ac
@@ -13,19 +13,57 @@ AC_CONFIG_FILES([Makefile src/Makefile tests/Makefile rexmpp.pc Doxyfile])
AC_PROG_CC
AM_PROG_AR
-LT_INIT
# Checks for libraries and related parameters.
-PKG_CHECK_MODULES([LIBXML], [libxml-2.0])
+AC_ARG_WITH([rust],
+ AS_HELP_STRING([--with-rust],
+ [use available Rust implementations of modules]))
+AC_ARG_WITH([expat],
+ AS_HELP_STRING([--with-expat], [use libexpat instead of libxml2]))
+
+AS_IF([test "x$with_rust" == "xyes"],
+ [AC_PATH_PROG([RUSTC], [rustc], [notfound])
+ AS_IF([test "x$RUSTC" == "xnotfound"], [AC_MSG_ERROR([rustc is required])])
+ AC_PATH_PROG([CARGO], [cargo], [notfound])
+ AS_IF([test "x$CARGO" == "xnotfound"], [AC_MSG_ERROR([cargo is required])])
+ AC_DEFINE([USE_RUST], [1], [Use Rust sources over C ones])],
+ [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_CONDITIONAL([USE_RUST], [test "x$with_rust" == "xyes"])
+
+LT_INIT
+
+
+# Cryptographic libraries, for hashing
+
+AC_ARG_WITH([gcrypt],
+ AS_HELP_STRING([--without-gcrypt], [do not use gcrypt]))
+AC_ARG_WITH([nettle],
+ AS_HELP_STRING([--without-nettle], [do not use nettle]))
+
+AS_IF([test "x$with_gcrypt" != "xno"],
+ [AM_PATH_LIBGCRYPT([],
+ [AC_DEFINE([HAVE_GCRYPT], [1], [Libgcrypt is available])])])
+
+AS_IF([test "x$with_gcrypt" == "xno" -a "x$with_nettle" != "xno"],
+ [PKG_CHECK_MODULES([NETTLE], [nettle],
+ [AC_DEFINE([HAVE_NETTLE], [1], [Libnettle is available])])])
+
+AS_IF([test "x$with_gcrypt" == "xno" -a "x$with_nettle" == "xno"],
+ [PKG_CHECK_MODULES([OPENSSL], [openssl],
+ [AC_DEFINE([HAVE_OPENSSL], [1], [OpenSSL is available])])])
-AM_PATH_LIBGCRYPT
# libnice (+ glib) and libsrtp for media calls, optional
AC_ARG_ENABLE([calls], AS_HELP_STRING([--disable-calls],
[build without Jingle media call support]))
-
+AC_ARG_WITH([opus],
+ AS_HELP_STRING([--without-opus], [Do not use libopus]))
AS_IF([test "x$enable_calls" != "xno"],
[PKG_CHECK_MODULES([NICE], [nice],
[AC_DEFINE([HAVE_NICE], [1], [libnice is available])])
@@ -33,6 +71,11 @@ AS_IF([test "x$enable_calls" != "xno"],
[AC_DEFINE([HAVE_GLIB], [1], [glib is available])])
PKG_CHECK_MODULES([SRTP], [libsrtp2],
[AC_DEFINE([HAVE_SRTP], [1], [libsrtp2 is available])])
+ PKG_CHECK_MODULES([PORTAUDIO], [portaudio-2.0],
+ [AC_DEFINE([HAVE_PORTAUDIO], [1], [portaudio-2.0 is available])])
+ AS_IF([test "x$with_opus" != "xno"],
+ PKG_CHECK_MODULES([OPUS], [opus],
+ [AC_DEFINE([HAVE_OPUS], [1], [libopus is available])]))
AC_DEFINE([ENABLE_CALLS], [1], [Jingle ICE-UDP DTLS-SRTP calls are enabled])])
# GSASL, optional
diff --git a/doap.xml b/doap.xml
new file mode 100644
index 0000000..c05815d
--- /dev/null
+++ b/doap.xml
@@ -0,0 +1,219 @@
+<?xml version="1.0" encoding="utf-8"?>
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:xmpp='https://linkmauve.fr/ns/xmpp-doap#'
+ xmlns="http://usefulinc.com/ns/doap#">
+ <Project xml:lang='en'>
+ <name>rexmpp</name>
+ <shortdesc>A reusable XMPP IM client library</shortdesc>
+ <programming-language>C</programming-language>
+ <language>en</language>
+ <license>MIT</license>
+ <repository>
+ <GitRepository>
+ <browse rdf:resource="https://git.uberspace.net/rexmpp/" />
+ <location rdf:resource="https://git.uberspace.net/rexmpp/" />
+ </GitRepository>
+ </repository>
+ <os>Linux</os>
+ <category rdf:resource="https://linkmauve.fr/ns/xmpp-doap#category-library" />
+ <maintainer rdf:resource="https://defanor.uberspace.net/about.xhtml#me" />
+
+ <!-- XMPP core -->
+ <implements rdf:resource="https://xmpp.org/rfcs/rfc6120.html" />
+ <!-- XMPP IM -->
+ <implements rdf:resource="https://xmpp.org/rfcs/rfc6121.html" />
+ <!--XMPP address format -->
+ <implements rdf:resource="https://datatracker.ietf.org/doc/html/rfc7622" />
+ <!-- SOCKS5 -->
+ <implements rdf:resource="https://datatracker.ietf.org/doc/html/rfc1928" />
+ <!-- Happy Eyeballs -->
+ <implements rdf:resource="https://datatracker.ietf.org/doc/html/rfc8305" />
+
+ <!-- todo: maybe add DNSSEC, DANE RFCs -->
+
+ <implements>
+ <xmpp:SupportedXep>
+ <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0198.html"/>
+ <xmpp:status>complete</xmpp:status>
+ <xmpp:version>1.6</xmpp:version>
+ <xmpp:since>0.0</xmpp:since>
+ </xmpp:SupportedXep>
+ </implements>
+
+ <implements>
+ <xmpp:SupportedXep>
+ <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0280.html"/>
+ <xmpp:status>complete</xmpp:status>
+ <xmpp:version>0.13</xmpp:version>
+ <xmpp:since>0.0</xmpp:since>
+ </xmpp:SupportedXep>
+ </implements>
+
+ <implements>
+ <xmpp:SupportedXep>
+ <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0368.html"/>
+ <xmpp:status>complete</xmpp:status>
+ <xmpp:version>1.1</xmpp:version>
+ <xmpp:since>0.0</xmpp:since>
+ </xmpp:SupportedXep>
+ </implements>
+
+ <implements>
+ <xmpp:SupportedXep>
+ <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0199.html"/>
+ <xmpp:status>complete</xmpp:status>
+ <xmpp:version>2.0</xmpp:version>
+ <xmpp:since>0.0</xmpp:since>
+ </xmpp:SupportedXep>
+ </implements>
+
+ <implements>
+ <xmpp:SupportedXep>
+ <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0030.html"/>
+ <xmpp:status>complete</xmpp:status>
+ <xmpp:version>2.5</xmpp:version>
+ <xmpp:since>0.0</xmpp:since>
+ </xmpp:SupportedXep>
+ </implements>
+
+ <implements>
+ <xmpp:SupportedXep>
+ <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0115.html"/>
+ <xmpp:status>complete</xmpp:status>
+ <xmpp:version>1.5</xmpp:version>
+ <xmpp:since>0.0</xmpp:since>
+ </xmpp:SupportedXep>
+ </implements>
+
+ <implements>
+ <xmpp:SupportedXep>
+ <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0092.html"/>
+ <xmpp:status>complete</xmpp:status>
+ <xmpp:version>1.1</xmpp:version>
+ <xmpp:since>0.0</xmpp:since>
+ </xmpp:SupportedXep>
+ </implements>
+
+ <implements>
+ <xmpp:SupportedXep>
+ <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0172.html"/>
+ <xmpp:status>complete</xmpp:status>
+ <xmpp:version>1.1</xmpp:version>
+ <xmpp:since>0.0</xmpp:since>
+ </xmpp:SupportedXep>
+ </implements>
+
+ <implements>
+ <xmpp:SupportedXep>
+ <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0373.html"/>
+ <xmpp:status>complete</xmpp:status>
+ <xmpp:version>0.6</xmpp:version>
+ <xmpp:since>0.0</xmpp:since>
+ </xmpp:SupportedXep>
+ </implements>
+
+ <implements>
+ <xmpp:SupportedXep>
+ <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0402.html"/>
+ <xmpp:status>complete</xmpp:status>
+ <xmpp:version>1.1</xmpp:version>
+ <xmpp:since>0.0</xmpp:since>
+ </xmpp:SupportedXep>
+ </implements>
+
+ <implements>
+ <xmpp:SupportedXep>
+ <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0410.html"/>
+ <xmpp:status>complete</xmpp:status>
+ <xmpp:version>1.1</xmpp:version>
+ <xmpp:since>0.0</xmpp:since>
+ </xmpp:SupportedXep>
+ </implements>
+
+ <implements>
+ <xmpp:SupportedXep>
+ <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0363.html"/>
+ <xmpp:status>complete</xmpp:status>
+ <xmpp:version>1.0</xmpp:version>
+ <xmpp:since>0.0</xmpp:since>
+ </xmpp:SupportedXep>
+ </implements>
+
+ <implements>
+ <xmpp:SupportedXep>
+ <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0166.html"/>
+ <xmpp:status>complete</xmpp:status>
+ <xmpp:version>1.1</xmpp:version>
+ <xmpp:since>0.0</xmpp:since>
+ </xmpp:SupportedXep>
+ </implements>
+
+ <implements>
+ <xmpp:SupportedXep>
+ <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0234.html"/>
+ <xmpp:status>partial</xmpp:status>
+ <xmpp:note>sending and accepting, but no requests and no
+ ranged transfers</xmpp:note>
+ <xmpp:version>0.19</xmpp:version>
+ <xmpp:since>0.0</xmpp:since>
+ </xmpp:SupportedXep>
+ </implements>
+
+ <implements>
+ <xmpp:SupportedXep>
+ <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0261.html"/>
+ <xmpp:status>complete</xmpp:status>
+ <xmpp:version>1.0</xmpp:version>
+ <xmpp:since>0.0</xmpp:since>
+ </xmpp:SupportedXep>
+ </implements>
+
+ <implements>
+ <xmpp:SupportedXep>
+ <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0167.html"/>
+ <xmpp:status>complete</xmpp:status>
+ <xmpp:version>1.2</xmpp:version>
+ <xmpp:since>0.0</xmpp:since>
+ </xmpp:SupportedXep>
+ </implements>
+
+ <implements>
+ <xmpp:SupportedXep>
+ <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0176.html"/>
+ <xmpp:status>complete</xmpp:status>
+ <xmpp:version>1.1</xmpp:version>
+ <xmpp:since>0.0</xmpp:since>
+ </xmpp:SupportedXep>
+ </implements>
+
+ <implements>
+ <xmpp:SupportedXep>
+ <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0320.html"/>
+ <xmpp:status>complete</xmpp:status>
+ <xmpp:version>1.0</xmpp:version>
+ <xmpp:since>0.0</xmpp:since>
+ </xmpp:SupportedXep>
+ </implements>
+
+ <implements>
+ <xmpp:SupportedXep>
+ <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0163.html"/>
+ <xmpp:status>complete</xmpp:status>
+ <xmpp:version>1.2</xmpp:version>
+ <xmpp:since>0.0</xmpp:since>
+ <xmpp:note>tracks contacts' published items</xmpp:note>
+ </xmpp:SupportedXep>
+ </implements>
+
+ <implements>
+ <xmpp:SupportedXep>
+ <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0060.html"/>
+ <xmpp:status>partial</xmpp:status>
+ <xmpp:version>1.2</xmpp:version>
+ <xmpp:since>0.0</xmpp:since>
+ <xmpp:note>helper functions</xmpp:note>
+ </xmpp:SupportedXep>
+ </implements>
+
+ </Project>
+</rdf:RDF>
diff --git a/emacs/xml_interface.c b/emacs/xml_interface.c
index 7db2c63..f98d5d1 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.
*/
@@ -20,91 +20,83 @@ and EOF ones, to simplify reading with libxml2.
#include <syslog.h>
#include <gnutls/gnutls.h>
#include <rexmpp.h>
+#include <rexmpp_xml.h>
#include <rexmpp_openpgp.h>
+#include <rexmpp_http_upload.h>
-void print_xml (xmlNodePtr node) {
- char *s = rexmpp_xml_serialize(node);
+void print_xml (rexmpp_xml_t *node) {
+ char *s = rexmpp_xml_serialize(node, 0);
printf("%s%c\n", s, '\0');
free(s);
}
-xmlNodePtr read_xml () {
- xmlNodePtr elem = NULL;
- xmlDocPtr doc = xmlReadFd(STDIN_FILENO, "", "utf-8", 0);
- if (doc != NULL) {
- elem = xmlCopyNode(xmlDocGetRootElement(doc), 1);
- xmlFreeDoc(doc);
- return elem;
- }
- return NULL;
-}
-
-
-char *request (rexmpp_t *s, xmlNodePtr payload)
+char *request (rexmpp_t *s, rexmpp_xml_t *payload)
{
- xmlNodePtr req = rexmpp_xml_add_id(s, xmlNewNode(NULL, "request"));
- xmlAddChild(req, payload);
+ rexmpp_xml_t *req = rexmpp_xml_new_elem("request", NULL);
+ rexmpp_xml_add_id(req);
+ rexmpp_xml_add_child(req, payload);
print_xml(req);
- char *id = xmlGetProp(req, "id");
- xmlFreeNode(req);
+ char *id = strdup(rexmpp_xml_find_attr_val(req, "id"));
+ rexmpp_xml_free(req);
return id;
}
-xmlNodePtr read_response (rexmpp_t *s, const char *id) {
- xmlNodePtr elem = read_xml();
+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 = rexmpp_xml_read_fd(STDIN_FILENO);
if (elem != NULL) {
if (rexmpp_xml_match(elem, NULL, "response")) {
- char *resp_id = xmlGetProp(elem, "id");
+ const char *resp_id = rexmpp_xml_find_attr_val(elem, "id");
if (resp_id != NULL) {
- int matches = (strcmp(resp_id, id) == 0);
- free(resp_id);
- if (matches) {
+ if (strcmp(resp_id, id) == 0) {
return elem;
} else {
/* Just fail for now, to avoid deadlocks. Though this
shouldn't happen. */
- xmlFreeNode(elem);
+ rexmpp_xml_free(elem);
rexmpp_log(s, LOG_ERR, "Unexpected response ID received.");
return NULL;
}
}
}
req_process(s, elem);
- xmlFreeNode(elem);
+ rexmpp_xml_free(elem);
}
return read_response(s, id);
}
-xmlNodePtr req_block (rexmpp_t *s, xmlNodePtr req) {
+rexmpp_xml_t *req_block (rexmpp_t *s, rexmpp_xml_t *req) {
char *id = request(s, req);
- xmlNodePtr resp = read_response(s, id);
+ rexmpp_xml_t *resp = read_response(s, id);
free(id);
return resp;
}
void respond_xml (rexmpp_t *s,
const char *id,
- xmlNodePtr payload) {
- xmlNodePtr response = xmlNewNode(NULL, "response");
- xmlNewProp(response, "id", id);
+ rexmpp_xml_t *payload) {
+ rexmpp_xml_t *response = rexmpp_xml_new_elem("response", NULL);
+ rexmpp_xml_add_attr(response, "id", id);
if (payload != NULL) {
- xmlAddChild(response, payload);
+ rexmpp_xml_add_child(response, payload);
}
print_xml(response);
- xmlFreeNode(response);
+ rexmpp_xml_free(response);
}
void respond_text (rexmpp_t *s,
const char *id,
const char *buf) {
- xmlNodePtr response = xmlNewNode(NULL, "response");
- xmlNewProp(response, "id", id);
+ rexmpp_xml_t *response = rexmpp_xml_new_elem("response", NULL);
+ rexmpp_xml_add_attr(response, "id", id);
if (buf != NULL) {
- xmlNodeAddContent(response, buf);
+ rexmpp_xml_add_text(response, buf);
}
print_xml(response);
- xmlFreeNode(response);
+ rexmpp_xml_free(response);
}
void on_http_upload (rexmpp_t *s, void *cb_data, const char *url) {
@@ -114,84 +106,99 @@ void on_http_upload (rexmpp_t *s, void *cb_data, const char *url) {
}
void req_process (rexmpp_t *s,
- xmlNodePtr elem)
+ rexmpp_xml_t *elem)
{
- char *id = xmlGetProp(elem, "id");
+ const char *id = rexmpp_xml_find_attr_val(elem, "id");
if (id == NULL) {
return;
}
rexmpp_err_t err;
char buf[64];
- xmlNodePtr child = xmlFirstElementChild(elem);
+ rexmpp_xml_t *child = rexmpp_xml_first_elem_child(elem);
if (rexmpp_xml_match(child, NULL, "stop")) {
snprintf(buf, 64, "%d", rexmpp_stop(s));
respond_text(s, id, buf);
} else if (rexmpp_xml_match(child, NULL, "console")) {
- char *in = xmlNodeGetContent(child);
+ char *in = strdup(rexmpp_xml_text_child(child));
rexmpp_console_feed(s, in, strlen(in));
free(in);
respond_text(s, id, NULL);
} else if (rexmpp_xml_match(child, NULL, "send")) {
- if (xmlFirstElementChild(child)) {
- xmlNodePtr stanza = xmlCopyNode(xmlFirstElementChild(child), 1);
+ if (rexmpp_xml_first_elem_child(child)) {
+ rexmpp_xml_t *stanza =
+ rexmpp_xml_clone(rexmpp_xml_first_elem_child(child));
snprintf(buf, 64, "%d", rexmpp_send(s, stanza));
respond_text(s, id, buf);
}
} else if (rexmpp_xml_match(child, NULL, "openpgp-decrypt-message")) {
int valid;
- xmlNodePtr plaintext =
- rexmpp_openpgp_decrypt_verify_message(s, xmlFirstElementChild(child),
+ rexmpp_xml_t *plaintext =
+ rexmpp_openpgp_decrypt_verify_message(s, rexmpp_xml_first_elem_child(child),
&valid);
/* todo: wrap into another element, with the 'valid' attribute */
respond_xml(s, id, plaintext);
} else if (rexmpp_xml_match(child, NULL, "openpgp-payload")) {
enum rexmpp_ox_mode mode = REXMPP_OX_CRYPT;
- char *mode_str = xmlGetProp(child, "mode");
+ const char *mode_str = rexmpp_xml_find_attr_val(child, "mode");
if (strcmp(mode_str, "sign") == 0) {
mode = REXMPP_OX_SIGN;
} else if (strcmp(mode_str, "signcrypt") == 0) {
mode = REXMPP_OX_SIGNCRYPT;
}
- free(mode_str);
- xmlNodePtr payload_xml =
- xmlFirstElementChild(rexmpp_xml_find_child(child, NULL, "payload"));
+ rexmpp_xml_t *payload_xml =
+ rexmpp_xml_first_elem_child(rexmpp_xml_find_child(child, NULL, "payload"));
- char **recipients[16];
+ char *recipients[16];
int recipients_num = 0;
- xmlNodePtr plchild;
- for (plchild = xmlFirstElementChild(child);
+ rexmpp_xml_t *plchild;
+ for (plchild = rexmpp_xml_first_elem_child(child);
plchild != NULL && recipients_num < 15;
plchild = plchild->next) {
if (rexmpp_xml_match(plchild, NULL, "to")) {
- recipients[recipients_num] = xmlNodeGetContent(plchild);
+ recipients[recipients_num] = strdup(rexmpp_xml_text_child(plchild));
recipients_num++;
}
}
recipients[recipients_num] = NULL;
char *payload_str =
- rexmpp_openpgp_payload(s, xmlCopyNode(payload_xml, 1), recipients, NULL, mode);
+ rexmpp_openpgp_payload(s, rexmpp_xml_clone(payload_xml),
+ (const char **)recipients, NULL, mode);
for (recipients_num = 0; recipients[recipients_num] != NULL; recipients_num++) {
free(recipients[recipients_num]);
}
respond_text(s, id, payload_str);
free(payload_str);
} else if (rexmpp_xml_match(child, NULL, "get-name")) {
- char *jid = xmlNodeGetContent(child);
+ const char *jid = rexmpp_xml_text_child(child);
if (jid != NULL) {
char *name = rexmpp_get_name(s, jid);
if (name != NULL) {
respond_text(s, id, name);
free(name);
}
- free(jid);
}
} else if (rexmpp_xml_match(child, NULL, "http-upload")) {
- char *in = xmlNodeGetContent(child);
+ char *in = strdup(rexmpp_xml_text_child(child));
rexmpp_http_upload_path(s, NULL, in, NULL, on_http_upload, strdup(id));
free(in);
+ /* Responding from on_http_upload */
+ } else if (rexmpp_xml_match(child, NULL, "muc-ping-set")) {
+ const char *occupant_jid = rexmpp_xml_find_attr_val(child, "occupant-jid");
+ const char *delay = rexmpp_xml_find_attr_val(child, "delay");
+ const char *password = rexmpp_xml_find_attr_val(child, "password");
+ if (occupant_jid != NULL && delay != NULL) {
+ snprintf(buf, 64, "%d",
+ rexmpp_muc_ping_set(s, occupant_jid, password, atoi(delay)));
+ respond_text(s, id, buf);
+ }
+ } else if (rexmpp_xml_match(child, NULL, "muc-ping-remove")) {
+ const char *occupant_jid = rexmpp_xml_find_attr_val(child, "occupant-jid");
+ if (occupant_jid != NULL) {
+ snprintf(buf, 64, "%d", rexmpp_muc_ping_remove(s, occupant_jid));
+ respond_text(s, id, buf);
+ }
}
- free(id);
return;
}
@@ -210,22 +217,23 @@ void my_logger (rexmpp_t *s, int priority, const char *fmt, va_list args) {
case LOG_INFO: priority_str = "info"; break;
case LOG_DEBUG: priority_str = "debug"; break;
}
- xmlNodePtr node = xmlNewNode(NULL, "log");
- xmlNewProp(node, "priority", priority_str);
- xmlNodeAddContent(node, buf);
+ rexmpp_xml_t *node = rexmpp_xml_new_elem("log", NULL);
+ rexmpp_xml_add_attr(node, "priority", priority_str);
+ rexmpp_xml_add_text(node, buf);
free(buf);
print_xml(node);
- xmlFreeNode(node);
+ rexmpp_xml_free(node);
}
-void my_console_print_cb (rexmpp_t *s, const char *fmt, va_list args) {
+int my_console_print_cb (rexmpp_t *s, const char *fmt, va_list args) {
char *buf = malloc(1024 * 20);
vsnprintf(buf, 1024 * 20, fmt, args);
- xmlNodePtr node = xmlNewNode(NULL, "console");
- xmlNodeAddContent(node, buf);
+ rexmpp_xml_t *node = rexmpp_xml_new_elem("console", NULL);
+ rexmpp_xml_add_text(node, buf);
free(buf);
print_xml(node);
- xmlFreeNode(node);
+ rexmpp_xml_free(node);
+ return 0;
}
int my_sasl_property_cb (rexmpp_t *s, rexmpp_sasl_property prop) {
@@ -239,53 +247,50 @@ int my_sasl_property_cb (rexmpp_t *s, rexmpp_sasl_property prop) {
case REXMPP_SASL_PROP_AUTHID: prop_str = "authid"; break;
default: return -1;
}
- xmlNodePtr req = xmlNewNode(NULL, "sasl");
- xmlNewProp(req, "property", prop_str);
- xmlNodePtr rep = req_block(s, req);
+ rexmpp_xml_t *req = rexmpp_xml_new_elem("sasl", NULL);
+ rexmpp_xml_add_attr(req, "property", prop_str);
+ rexmpp_xml_t *rep = req_block(s, req);
if (rep == NULL) {
return -1;
}
- char *val = xmlNodeGetContent(rep);
- xmlFreeNode(rep);
+ const char *val = rexmpp_xml_text_child(rep);
if (val == NULL) {
return -1;
}
rexmpp_sasl_property_set (s, prop, val);
- free(val);
+ rexmpp_xml_free(rep);
return GSASL_OK;
}
-int my_xml_in_cb (rexmpp_t *s, xmlNodePtr node) {
- xmlNodePtr req = xmlNewNode(NULL, "xml-in");
- xmlAddChild(req, xmlCopyNode(node, 1));
- xmlNodePtr rep = req_block(s, req);
+int my_xml_in_cb (rexmpp_t *s, rexmpp_xml_t *node) {
+ rexmpp_xml_t *req = rexmpp_xml_new_elem("xml-in", NULL);
+ rexmpp_xml_add_child(req, rexmpp_xml_clone(node));
+ rexmpp_xml_t *rep = req_block(s, req);
if (rep == NULL) {
return 0;
}
- char *val = xmlNodeGetContent(rep);
- xmlFreeNode(rep);
+ const char *val = rexmpp_xml_text_child(rep);
if (val == NULL) {
return 0;
}
int n = atoi(val);
- free(val);
+ rexmpp_xml_free(rep);
return n;
}
-int my_xml_out_cb (rexmpp_t *s, xmlNodePtr node) {
- xmlNodePtr req = xmlNewNode(NULL, "xml-out");
- xmlAddChild(req, xmlCopyNode(node, 1));
- xmlNodePtr rep = req_block(s, req);
+int my_xml_out_cb (rexmpp_t *s, rexmpp_xml_t *node) {
+ rexmpp_xml_t *req = rexmpp_xml_new_elem("xml-out", NULL);
+ rexmpp_xml_add_child(req, rexmpp_xml_clone(node));
+ rexmpp_xml_t *rep = req_block(s, req);
if (rep == NULL) {
return 0;
}
- char *val = xmlNodeGetContent(rep);
- xmlFreeNode(rep);
+ const char *val = rexmpp_xml_text_child(rep);
if (val == NULL) {
return 0;
}
int n = atoi(val);
- free(val);
+ rexmpp_xml_free(rep);
return n;
}
@@ -329,17 +334,19 @@ int main (int argc, char **argv) {
fd_set read_fds, write_fds;
int nfds;
- struct timeval tv;
- struct timeval *mtv;
+ struct timespec tv;
+ struct timespec *mtv;
+ struct timeval tv_ms;
+ struct timeval *mtv_ms;
int n = 0;
do {
/* Check if we have some user input. */
if (n > 0 && FD_ISSET(STDIN_FILENO, &read_fds)) {
- xmlNodePtr elem = read_xml();
+ rexmpp_xml_t *elem = rexmpp_xml_read_fd(STDIN_FILENO);
if (elem != NULL) {
req_process(&s, elem);
- xmlFreeNode(elem);
+ rexmpp_xml_free(elem);
}
}
@@ -367,7 +374,13 @@ int main (int argc, char **argv) {
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
nfds = rexmpp_fds(&s, &read_fds, &write_fds);
- mtv = rexmpp_timeout(&s, NULL, (struct timeval*)&tv);
+ mtv = rexmpp_timeout(&s, NULL, &tv);
+ mtv_ms = NULL;
+ if (mtv != NULL) {
+ tv_ms.tv_sec = mtv->tv_sec;
+ tv_ms.tv_usec = mtv->tv_nsec / 1000;
+ mtv_ms = &tv_ms;
+ }
/* Add other file descriptors we are interested in, particularly
stdin for user input. */
@@ -375,7 +388,7 @@ int main (int argc, char **argv) {
/* Run select(2) with all those file descriptors and timeouts,
waiting for either user input or some rexmpp event to occur. */
- n = select(nfds, &read_fds, &write_fds, NULL, mtv);
+ n = select(nfds, &read_fds, &write_fds, NULL, mtv_ms);
if (n == -1) {
printf("select error: %s\n", strerror(errno));
break;
diff --git a/emacs/xmpp.el b/emacs/xmpp.el
index feb201f..ebe6911 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))
@@ -299,7 +298,7 @@ its printing--which doesn't handle namespaces--can be used too."
(add-face-text-property
0
(length from-nick)
- (if (equal xmpp-muc-my-nick from-nick)
+ (if (equal xmpp-muc-my-occupant-jid message-from)
'xmpp-my-nick
'xmpp-other-nick)
nil
@@ -349,9 +348,20 @@ its printing--which doesn't handle namespaces--can be used too."
(concat
(xmpp-timestamp-string) ", "
my-name
- (xmpp-message-string (car (xml-node-children message-body)))
- "\n")))))))
- ("groupchat" nil)))))))))
+ (xmpp-message-string
+ (car (xml-node-children message-body)))
+ "\n")))))))
+ ("groupchat" nil))))))))
+ (when (and (xmpp-xml-match xml 'presence "jabber:client")
+ (or (not (xml-get-attribute-or-nil xml 'type))
+ (equal (xml-get-attribute-or-nil xml 'type) "available"))
+ (xmpp-xml-child xml 'x "http://jabber.org/protocol/muc"))
+ ;; Joining a MUC
+ (let* ((occupant-jid (xml-get-attribute xml 'to))
+ (muc-jid (xmpp-jid-to-bare occupant-jid))
+ (buf (xmpp-muc-buffer muc-jid proc)))
+ (with-current-buffer buf
+ (setq-local xmpp-muc-my-occupant-jid occupant-jid)))))
(defun xmpp-process (proc xml)
(let* ((buf (process-buffer proc))
@@ -464,6 +474,7 @@ its printing--which doesn't handle namespaces--can be used too."
(delete-region (point-min) zero)
(setq zero (search-forward "\0" nil t)))))))))
+;;;###autoload
(defun xmpp (jid)
"Initiates a new XMPP session."
(interactive "sJID: ")
@@ -645,20 +656,25 @@ its printing--which doesn't handle namespaces--can be used too."
(id . ,(xmpp-gen-id))
(to . ,full-jid))
(x ((xmlns . "http://jabber.org/protocol/muc")))))
- (let ((buf (xmpp-muc-buffer jid proc)))
- (with-current-buffer buf
- (setq-local xmpp-muc-my-nick my-nick))
- buf))))
+ (xmpp-request
+ `(muc-ping-set ((occupant-jid . ,full-jid)
+ (delay . "600"))
+ nil)
+ nil
+ proc))))
(defun xmpp-muc-leave (jid &optional proc)
(interactive "sConference JID: ")
- (let ((process (or proc xmpp-proc))
- (full-jid (concat jid "/" xmpp-muc-my-nick)))
- (xmpp-send `(presence ((xmlns . "jabber:client")
- (id . ,(xmpp-gen-id))
- (to . ,full-jid)
- (type . "unavailable"))))))
-
+ (with-current-buffer (process-buffer (or proc xmpp-proc))
+ (with-current-buffer (cdr (assoc jid xmpp-muc-buffers))
+ (xmpp-send `(presence ((xmlns . "jabber:client")
+ (id . ,(xmpp-gen-id))
+ (to . ,xmpp-muc-my-occupant-jid)
+ (type . "unavailable"))))
+ (xmpp-request
+ `(muc-ping-remove ((occupant-jid . ,xmpp-muc-my-occupant-jid))
+ nil)
+ nil))))
(defun xmpp-muc-buffer (jid &optional proc)
(let* ((process (or proc xmpp-proc))
diff --git a/examples/basic.c b/examples/basic.c
index 6e195bd..5df4f65 100644
--- a/examples/basic.c
+++ b/examples/basic.c
@@ -11,7 +11,10 @@
#include <errno.h>
#include <syslog.h>
#include <gsasl.h>
+#include <time.h>
+#include <stdlib.h>
#include <rexmpp.h>
+#include <rexmpp_xml.h>
#include <rexmpp_sasl.h>
int log_level = 8;
@@ -65,16 +68,16 @@ int my_sasl_property_cb (rexmpp_t *s, rexmpp_sasl_property prop) {
}
/* An XML in callback, printing what was received. */
-int my_xml_in_cb (rexmpp_t *s, xmlNodePtr node) {
- char *xml_buf = rexmpp_xml_serialize(node);
+int my_xml_in_cb (rexmpp_t *s, rexmpp_xml_t *node) {
+ char *xml_buf = rexmpp_xml_serialize(node, 0);
printf("recv: %s\n", xml_buf);
free(xml_buf);
return 0;
}
/* An XML out callback, printing what is about to be sent. */
-int my_xml_out_cb (rexmpp_t *s, xmlNodePtr node) {
- char *xml_buf = rexmpp_xml_serialize(node);
+int my_xml_out_cb (rexmpp_t *s, rexmpp_xml_t *node) {
+ char *xml_buf = rexmpp_xml_serialize(node, 0);
printf("send: %s\n", xml_buf);
free(xml_buf);
return 0;
@@ -85,6 +88,11 @@ int my_console_print_cb (rexmpp_t *s, const char *fmt, va_list args) {
return 0;
}
+/* void my_socket_options(rexmpp_t *s, int sock) { */
+/* int pmtudisc = IP_PMTUDISC_WANT; */
+/* setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, &pmtudisc, sizeof(pmtudisc)); */
+/* } */
+
void print_help (char *prog_name) {
printf("Usage: %s [options] <jid>\n" \
"Options:\n" \
@@ -137,7 +145,8 @@ int main (int argc, char **argv) {
/* Could set a client certificate for SASL EXTERNAL authentication
and Jingle's DTLS here. */
- rexmpp_tls_set_x509_key_file(&s, "client.crt", "client.key");
+ s.x509_key_file = "client.key";
+ s.x509_cert_file = "client.crt";
/* Could also set various other things manually. */
/* s.socks_host = "127.0.0.1"; */
@@ -148,6 +157,7 @@ int main (int argc, char **argv) {
/* rexmpp_openpgp_set_home_dir(&s, "pgp"); */
s.roster_cache_file = "roster.xml";
/* s.tls_policy = REXMPP_TLS_AVOID; */
+ /* s.socket_cb = my_socket_options; */
/* Once the main structure is initialised and everything is
sufficiently configured, we are ready to run the main loop and
@@ -155,8 +165,10 @@ int main (int argc, char **argv) {
fd_set read_fds, write_fds;
int nfds;
- struct timeval tv;
- struct timeval *mtv;
+ struct timespec tv;
+ struct timespec *mtv;
+ struct timeval tv_ms;
+ struct timeval *mtv_ms;
int n = 0;
do {
@@ -173,18 +185,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 = xmlDocGetRootElement(doc);
- if (node != NULL) {
- xmlUnlinkNode(node);
- 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);
@@ -217,7 +222,13 @@ int main (int argc, char **argv) {
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
nfds = rexmpp_fds(&s, &read_fds, &write_fds);
- mtv = rexmpp_timeout(&s, NULL, (struct timeval*)&tv);
+ mtv = rexmpp_timeout(&s, NULL, &tv);
+ mtv_ms = NULL;
+ if (mtv != NULL) {
+ tv_ms.tv_sec = mtv->tv_sec;
+ tv_ms.tv_usec = mtv->tv_nsec / 1000;
+ mtv_ms = &tv_ms;
+ }
/* Add other file descriptors we are interested in, particularly
stdin for user input. */
@@ -225,7 +236,7 @@ int main (int argc, char **argv) {
/* Run select(2) with all those file descriptors and timeouts,
waiting for either user input or some rexmpp event to occur. */
- n = select(nfds, &read_fds, &write_fds, NULL, mtv);
+ n = select(nfds, &read_fds, &write_fds, NULL, mtv_ms);
if (n == -1) {
printf("select error: %s\n", strerror(errno));
break;
diff --git a/src/Cargo.toml b/src/Cargo.toml
new file mode 100644
index 0000000..65a0ff4
--- /dev/null
+++ b/src/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "rexmpp_rust"
+version = "0.1.0"
+authors = ["defanor <defanor@uberspace.net>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[lib]
+name = "rexmpp_rust"
+crate-type = ["staticlib"]
+path = "rexmpp_rust.rs"
+
+[dependencies]
+libc = "0.2"
+errno = "0.3"
+rxml = "0.9"
diff --git a/src/Makefile.am b/src/Makefile.am
index 9fff4c8..2dfc581 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,18 +1,8 @@
-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
librexmpp_la_SOURCES = rexmpp_roster.h rexmpp_roster.c \
- rexmpp_tcp.h rexmpp_tcp.c \
- rexmpp_socks.h rexmpp_socks.c \
rexmpp.h rexmpp.c \
rexmpp_dns.h rexmpp_dns.c \
rexmpp_tls.h rexmpp_tls.c \
@@ -23,17 +13,43 @@ librexmpp_la_SOURCES = rexmpp_roster.h rexmpp_roster.c \
rexmpp_http_upload.h rexmpp_http_upload.c \
rexmpp_jingle.h rexmpp_jingle.c \
rexmpp_base64.h rexmpp_base64.c \
- rexmpp_sasl.h rexmpp_sasl.c
+ rexmpp_sasl.h rexmpp_sasl.c \
+ rexmpp_xml.h rexmpp_xml.c \
+ rexmpp_utf8.h \
+ rexmpp_random.h rexmpp_random.c \
+ rexmpp_digest.h rexmpp_digest.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
-librexmpp_la_CFLAGS = $(AM_CFLAGS) $(LIBXML_CFLAGS) \
+ rexmpp_sasl.h rexmpp_xml.h rexmpp_utf8.h rexmpp_xml_parser.h \
+ rexmpp_random.h rexmpp_digest.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) \
+ $(NICE_CFLAGS) $(GLIB_CFLAGS) $(SRTP_CFLAGS) \
+ $(PORTAUDIO_CFLAGS) $(OPUS_CFLAGS) $(NETTLE_CFLAGS)
+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)
+ $(LIBGCRYPT_LIBS) $(CURL_LIBS) $(NICE_LIBS) $(GLIB_LIBS) $(SRTP_LIBS) \
+ $(PORTAUDIO_LIBS) $(OPUS_LIBS) $(NETTLE_LIBS)
+librexmpp_la_LDFLAGS = []
+
+if USE_RUST
+target_debug_librexmpp_rust_a_SOURCES = \
+ rexmpp_rust.rs rexmpp.rs rexmpp_jid.rs rexmpp_dns.rs rexmpp_tcp.rs \
+ rexmpp_socks.rs rexmpp_xml.rs rexmpp_xml_parser.rs
+noinst_LIBRARIES = target/debug/librexmpp_rust.a
+librexmpp_la_LIBADD += target/debug/librexmpp_rust.a
+librexmpp_la_LDFLAGS += -L. -lpthread -ldl
+
+target/debug/librexmpp_rust.a: $(target_debug_librexmpp_rust_a_SOURCES)
+ $(CARGO) build
+
+else
+librexmpp_la_SOURCES += rexmpp_tcp.h rexmpp_tcp.c \
+ rexmpp_socks.h rexmpp_socks.c \
+ rexmpp_xml_parser.h rexmpp_xml_parser.c
+endif
diff --git a/src/rexmpp.c b/src/rexmpp.c
index d7e1364..24b9965 100644
--- a/src/rexmpp.c
+++ b/src/rexmpp.c
@@ -14,13 +14,17 @@
#include <arpa/nameser.h>
#include <sys/types.h>
#include <sys/socket.h>
+#include <assert.h>
+#include <stdbool.h>
#include "config.h"
+#ifdef HAVE_GCRYPT
#include <gcrypt.h>
-#include <libxml/tree.h>
-#include <libxml/xmlsave.h>
+#endif
+#ifdef USE_UNBOUND
#include <unbound.h>
+#endif
#ifdef HAVE_GPGME
#include <gpgme.h>
#endif
@@ -29,6 +33,7 @@
#endif
#include "rexmpp.h"
+#include "rexmpp_xml.h"
#include "rexmpp_tcp.h"
#include "rexmpp_socks.h"
#include "rexmpp_roster.h"
@@ -40,6 +45,8 @@
#include "rexmpp_jingle.h"
#include "rexmpp_base64.h"
#include "rexmpp_sasl.h"
+#include "rexmpp_random.h"
+#include "rexmpp_digest.h"
struct rexmpp_iq_cacher {
rexmpp_iq_callback_t cb;
@@ -88,21 +95,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, ...)
{
@@ -114,157 +113,225 @@ void rexmpp_log (rexmpp_t *s, int priority, const char *format, ...)
}
}
-char *rexmpp_capabilities_string (rexmpp_t *s, xmlNodePtr info) {
- /* Assuming the info is sorted already. Would be better to sort it
- here (todo). */
- xmlNodePtr cur;
- int buf_len = 1024, str_len = 0;
- char *str = malloc(buf_len);
- for (cur = info; cur; cur = cur->next) {
- if (strcmp(cur->name, "identity") == 0) {
- int cur_len = 5; /* ///< for an empty identity */
-
- /* Collect the properties we'll need. */
- char *category = xmlGetProp(cur, "category");
- char *type = xmlGetProp(cur, "type");
- char *lang = xmlGetProp(cur, "xml:lang");
- char *name = xmlGetProp(cur, "name");
-
- /* Calculate the length needed. */
- if (category != NULL) {
- cur_len += strlen(category);
- }
- if (type != NULL) {
- cur_len += strlen(type);
- }
- if (lang != NULL) {
- cur_len += strlen(lang);
- }
- if (name != NULL) {
- cur_len += strlen(name);
- }
+rexmpp_err_t rexmpp_muc_ping_set (rexmpp_t *s,
+ const char *occupant_jid,
+ const char *password,
+ unsigned int delay)
+{
+ /* At first try to edit an existing record. */
+ rexmpp_muc_ping_t *mp = s->muc_ping;
+ while (mp != NULL) {
+ if (strcmp(mp->jid, occupant_jid) == 0) {
+ mp->delay = delay;
+ return REXMPP_SUCCESS;
+ }
+ mp = mp->next;
+ }
- /* Reallocate the buffer if necessary. */
- if (cur_len > buf_len - str_len) {
- while (cur_len > buf_len - str_len) {
- buf_len *= 2;
- }
- str = realloc(str, buf_len);
- }
+ /* No existing self-ping record for this occupant JID; create a new
+ one. */
+ mp = malloc(sizeof(rexmpp_muc_ping_t));
+ if (mp == NULL) {
+ rexmpp_log(s, LOG_ERR,
+ "Failed to allocate memory for a MUC self-ping record: %s",
+ strerror(errno));
+ return REXMPP_E_MALLOC;
+ }
+ mp->jid = strdup(occupant_jid);
+ if (mp->jid == NULL) {
+ rexmpp_log(s, LOG_ERR,
+ "Failed to duplicate JID string for a MUC self-ping record: %s",
+ strerror(errno));
+ free(mp);
+ return REXMPP_E_OTHER;
+ }
+ if (password != NULL) {
+ mp->password = strdup(password);
+ } else {
+ mp->password = NULL;
+ }
- /* Fill the data. */
- if (category != NULL) {
- strcpy(str + str_len, category);
- str_len += strlen(category);
- }
- str[str_len] = '/';
- str_len++;
- if (type != NULL) {
- strcpy(str + str_len, type);
- str_len += strlen(type);
- }
- str[str_len] = '/';
- str_len++;
- if (lang != NULL) {
- strcpy(str + str_len, lang);
- str_len += strlen(lang);
- }
- str[str_len] = '/';
- str_len++;
- if (name != NULL) {
- strcpy(str + str_len, name);
- str_len += strlen(name);
- }
- str[str_len] = '<';
- str_len++;
+ mp->delay = delay;
+ mp->requested = 0;
+ mp->last_activity.tv_sec = 0;
+ mp->last_activity.tv_nsec = 0;
+ mp->next = s->muc_ping;
+ s->muc_ping = mp;
+ return REXMPP_SUCCESS;
+}
- /* Free the values. */
- if (category != NULL) {
- free(category);
+rexmpp_err_t rexmpp_muc_ping_remove (rexmpp_t *s, const char *occupant_jid) {
+ rexmpp_muc_ping_t **mp = &(s->muc_ping);
+ while (*mp != NULL) {
+ if (strcmp(occupant_jid, (*mp)->jid) == 0) {
+ rexmpp_muc_ping_t *found = *mp;
+ *mp = found->next;
+ free(found->jid);
+ if (found->password != NULL) {
+ free(found->password);
}
- if (type != NULL) {
- free(type);
- }
- if (lang != NULL) {
- free(lang);
- }
- if (name != NULL) {
- free(name);
+ free(found);
+ return REXMPP_SUCCESS;
+ }
+ mp = &((*mp)->next);
+ }
+ rexmpp_log(s, LOG_WARNING,
+ "Removal of MUC self-ping record for JID %s is requested, "
+ "but no such record is found",
+ occupant_jid);
+ return REXMPP_E_OTHER;
+}
+
+rexmpp_err_t rexmpp_muc_join (rexmpp_t *s,
+ const char *occupant_jid,
+ const char *password,
+ unsigned int ping_delay)
+{
+ rexmpp_xml_t *presence =
+ rexmpp_xml_new_elem("presence", "jabber:client");
+ rexmpp_xml_add_id(presence);
+ rexmpp_xml_add_attr(presence, "from", s->assigned_jid.full);
+ rexmpp_xml_add_attr(presence, "to", occupant_jid);
+ rexmpp_xml_t *x =
+ rexmpp_xml_new_elem("x", "http://jabber.org/protocol/muc");
+ rexmpp_xml_add_child(presence, x);
+ rexmpp_err_t ret = rexmpp_send(s, presence);
+ if (ping_delay > 0) {
+ rexmpp_muc_ping_set(s, occupant_jid, password, ping_delay);
+ }
+ return ret;
+}
+
+rexmpp_err_t rexmpp_muc_leave (rexmpp_t *s, const char *occupant_jid) {
+ rexmpp_muc_ping_remove(s, occupant_jid);
+ rexmpp_xml_t *presence =
+ rexmpp_xml_new_elem("presence", "jabber:client");
+ rexmpp_xml_add_id(presence);
+ rexmpp_xml_add_attr(presence, "from", s->assigned_jid.full);
+ rexmpp_xml_add_attr(presence, "to", occupant_jid);
+ rexmpp_xml_add_attr(presence, "type", "unavailable");
+ return rexmpp_send(s, presence);
+}
+
+void rexmpp_muc_pong (rexmpp_t *s,
+ void *ptr,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
+ int success)
+{
+ rexmpp_muc_ping_t *mp = ptr;
+ clock_gettime(CLOCK_MONOTONIC, &(mp->last_activity));
+ mp->requested = 0;
+ if (! success) {
+ const char *jid = rexmpp_xml_find_attr_val(req, "to");
+ rexmpp_xml_t *error = rexmpp_xml_first_elem_child(response);
+ if (error == NULL) {
+ rexmpp_log(s, LOG_ERR,
+ "MUC self-ping failure for %s, and no error element received",
+ jid);
+ rexmpp_muc_ping_remove(s, jid);
+ } else {
+ rexmpp_log(s, LOG_WARNING, "MUC self-ping failure for %s: %s",
+ jid, error->alt.elem.qname.name);
+ if (rexmpp_xml_match(error, NULL, "service-unavailable") ||
+ rexmpp_xml_match(error, NULL, "feature-not-implemented") ||
+ rexmpp_xml_match(error, NULL, "remote-server-not-found") ||
+ rexmpp_xml_match(error, NULL, "remote-server-timeout")) {
+ rexmpp_log(s, LOG_WARNING, "Giving up on pinging it");
+ rexmpp_muc_ping_remove(s, jid);
+ } else if (rexmpp_xml_match(error, NULL, "item-not-found")) {
+ /* Ignore and keep pinging? */
+ } else {
+ /* Some other error, re-join. */
+ rexmpp_muc_join(s, jid, NULL, 0);
}
- } else if (strcmp(cur->name, "feature") == 0) {
- char *var = xmlGetProp(cur, "var");
- int cur_len = 2 + strlen(var);
- if (cur_len > buf_len - str_len) {
- while (cur_len > buf_len - str_len) {
- buf_len *= 2;
+ }
+ }
+}
+
+char *rexmpp_capabilities_hash (rexmpp_t *s,
+ rexmpp_xml_t *info)
+{
+ /* Assuming the info is sorted already. Might be useful to sort it
+ here. */
+ rexmpp_digest_t digest_ctx;
+ if (rexmpp_digest_init(&digest_ctx, REXMPP_DIGEST_SHA1)) {
+ rexmpp_log(s, LOG_ERR, "Failed to initialize a digest object");
+ return NULL;
+ }
+
+ rexmpp_xml_t *cur;
+ for (cur = info; cur; cur = cur->next) {
+ if (strcmp(cur->alt.elem.qname.name, "identity") == 0) {
+ const char *identity_strings[4] =
+ { rexmpp_xml_find_attr_val(cur, "category"),
+ rexmpp_xml_find_attr_val(cur, "type"),
+ rexmpp_xml_find_attr_val(cur, "xml:lang"),
+ rexmpp_xml_find_attr_val(cur, "name")
+ };
+ int i;
+ for (i = 0; i < 4; i++) {
+ if (identity_strings[i] != NULL) {
+ rexmpp_digest_update(&digest_ctx,
+ identity_strings[i],
+ strlen(identity_strings[i]));
}
- str = realloc(str, buf_len);
+ rexmpp_digest_update(&digest_ctx, (i < 3) ? "/" : "<", 1);
+ }
+ } else if (strcmp(cur->alt.elem.qname.name, "feature") == 0) {
+ const char *var = rexmpp_xml_find_attr_val(cur, "var");
+ if (var != NULL) {
+ rexmpp_digest_update(&digest_ctx, var, strlen(var));
+ rexmpp_digest_update(&digest_ctx, "<", 1);
+ } else {
+ rexmpp_log(s, LOG_ERR, "Found an empty feature var");
}
- strcpy(str + str_len, var);
- str_len += strlen(var);
- str[str_len] = '<';
- str_len++;
- free(var);
} else {
rexmpp_log(s, LOG_ERR,
- "Unsupported node type in disco info: %s", cur->name);
+ "Unsupported node type in disco info: %s",
+ cur->alt.elem.qname.name);
}
}
- str[str_len] = '\0';
- return str;
-}
-char *rexmpp_capabilities_hash (rexmpp_t *s,
- xmlNodePtr info)
-{
- char *out = NULL;
- size_t out_len = 0;
- char *str = rexmpp_capabilities_string(s, info);
- if (str != NULL) {
- unsigned int sha1_len = gcry_md_get_algo_dlen(GCRY_MD_SHA1);
- char *sha1 = malloc(sha1_len);
- if (sha1 != NULL) {
- gcry_md_hash_buffer(GCRY_MD_SHA1, sha1, str, strlen(str));
- rexmpp_base64_to(sha1, sha1_len, &out, &out_len);
- free(sha1);
- }
- free(str);
- }
- return out;
+ size_t digest_len = rexmpp_digest_len(REXMPP_DIGEST_SHA1);
+ char *digest_buf = malloc(digest_len);
+ rexmpp_digest_finish(&digest_ctx, digest_buf, digest_len);
+ char *digest_base64 = NULL;
+ size_t digest_base64_len = 0;
+ rexmpp_base64_to(digest_buf, digest_len, &digest_base64, &digest_base64_len);
+ free(digest_buf);
+ return digest_base64;
}
-xmlNodePtr rexmpp_find_event (rexmpp_t *s,
- const char *from,
- const char *node,
- xmlNodePtr *prev_event)
+rexmpp_xml_t *rexmpp_find_event (rexmpp_t *s,
+ const char *from,
+ const char *node,
+ rexmpp_xml_t **prev_event)
{
- xmlNodePtr prev, cur;
+ rexmpp_xml_t *prev, *cur;
for (prev = NULL, cur = s->roster_events;
cur != NULL;
- prev = cur, cur = xmlNextElementSibling(cur)) {
- char *cur_from = xmlGetProp(cur, "from");
+ prev = cur, cur = cur->next) {
+ const char *cur_from = rexmpp_xml_find_attr_val(cur, "from");
if (cur_from == NULL) {
continue;
}
- xmlNodePtr cur_event =
+ rexmpp_xml_t *cur_event =
rexmpp_xml_find_child(cur,
"http://jabber.org/protocol/pubsub#event",
"event");
- xmlNodePtr cur_items =
+ rexmpp_xml_t *cur_items =
rexmpp_xml_find_child(cur_event,
"http://jabber.org/protocol/pubsub#event",
"items");
if (cur_items == NULL) {
- free(cur_from);
continue;
}
- char *cur_node = xmlGetProp(cur_items, "node");
+ const char *cur_node = rexmpp_xml_find_attr_val(cur_items, "node");
if (cur_node == NULL) {
continue;
}
int match = (strcmp(cur_from, from) == 0 && strcmp(cur_node, node) == 0);
- free(cur_node);
- free(cur_from);
if (match) {
if (prev_event != NULL) {
*prev_event = prev;
@@ -282,36 +349,39 @@ char *rexmpp_get_name (rexmpp_t *s, const char *jid_str) {
return NULL;
}
if (s->manage_roster) {
- xmlNodePtr roster_item = rexmpp_roster_find_item(s, jid.bare, NULL);
- if (roster_item) {
- char *name = xmlGetProp(roster_item, "name");
+ rexmpp_xml_t *roster_item = rexmpp_roster_find_item(s, jid.bare, NULL);
+ if (roster_item != NULL) {
+ const char *name = rexmpp_xml_find_attr_val(roster_item, "name");
if (name != NULL) {
- return name;
+ return strdup(name);
}
}
if (s->track_roster_events) {
- xmlNodePtr elem =
+ rexmpp_xml_t *elem =
rexmpp_find_event(s, jid.bare, "http://jabber.org/protocol/nick", NULL);
if (elem != NULL) {
- xmlNodePtr event =
+ rexmpp_xml_t *event =
rexmpp_xml_find_child(elem,
"http://jabber.org/protocol/pubsub#event",
"event");
- xmlNodePtr items =
+ rexmpp_xml_t *items =
rexmpp_xml_find_child(event,
"http://jabber.org/protocol/pubsub#event",
"items");
- xmlNodePtr item =
+ rexmpp_xml_t *item =
rexmpp_xml_find_child(items,
"http://jabber.org/protocol/pubsub#event",
"item");
if (item != NULL) {
- xmlNodePtr nick =
+ rexmpp_xml_t *nick =
rexmpp_xml_find_child(item,
"http://jabber.org/protocol/nick",
"nick");
- if (nick != NULL) {
- return xmlNodeGetContent(nick);
+ if (nick != NULL &&
+ nick->type == REXMPP_XML_ELEMENT &&
+ nick->alt.elem.children != NULL &&
+ nick->alt.elem.children->type == REXMPP_XML_TEXT) {
+ return strdup(rexmpp_xml_text_child(nick));
}
}
}
@@ -323,93 +393,75 @@ char *rexmpp_get_name (rexmpp_t *s, const char *jid_str) {
return strdup(jid.bare);
}
-xmlNodePtr rexmpp_xml_feature (const char *var) {
- xmlNodePtr feature = xmlNewNode(NULL, "feature");
- xmlNewProp(feature, "var", var);
+rexmpp_xml_t *rexmpp_xml_feature (const char *var) {
+ rexmpp_xml_t *feature = rexmpp_xml_new_elem("feature", NULL);
+ rexmpp_xml_add_attr(feature, "var", var);
return feature;
}
-xmlNodePtr rexmpp_xml_new_node (const char *name, const char *namespace) {
- xmlNodePtr node = xmlNewNode(NULL, name);
- xmlNewNs(node, namespace, NULL);
- return node;
-}
-
-xmlNodePtr rexmpp_xml_error (const char *type, const char *condition) {
- xmlNodePtr error = xmlNewNode(NULL, "error");
- xmlNewProp(error, "type", type);
- xmlNodePtr cond = xmlNewNode(NULL, condition);
- xmlNewNs(cond, "urn:ietf:params:xml:ns:xmpp-stanzas", NULL);
- xmlAddChild(error, cond);
- return error;
-}
-
void rexmpp_disco_find_feature_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
struct rexmpp_feature_search *search = ptr;
if (! success) {
- char *to = xmlGetProp(request, "to");
- xmlNodePtr query = xmlFirstElementChild(request);
- rexmpp_log(s, LOG_ERR, "Failed to query %s for %s.", to, query->nsDef->href);
- free(to);
+ const char *to = rexmpp_xml_find_attr_val(request, "to");
+ rexmpp_xml_t *query = request->alt.elem.children;
+ rexmpp_log(s, LOG_ERR, "Failed to query %s for %s.", to, query->alt.elem.qname.namespace);
} else if (! search->found) {
- xmlNodePtr query = xmlFirstElementChild(response);
+ rexmpp_xml_t *query = rexmpp_xml_first_elem_child(response);
if (rexmpp_xml_match(query, "http://jabber.org/protocol/disco#info",
"query")) {
- xmlNodePtr child = xmlFirstElementChild(query);
+ rexmpp_xml_t *child = rexmpp_xml_first_elem_child(query);
while (child != NULL && (! search->found)) {
if (rexmpp_xml_match(child, "http://jabber.org/protocol/disco#info",
"feature")) {
- char *var = xmlGetProp(child, "var");
+ const char *var = rexmpp_xml_find_attr_val(child, "var");
if (var != NULL) {
if (strcmp(var, search->feature_var) == 0) {
search->cb(s, search->cb_data, request, response, success);
search->found = 1;
}
- free(var);
}
}
- child = child->next;
+ child = rexmpp_xml_next_elem_sibling(child);
}
if ((! search->found) && (search->max_requests > 0)) {
/* Still not found, request items */
- char *jid = xmlGetProp(request, "to");
+ const char *jid = rexmpp_xml_find_attr_val(request, "to");
if (jid != NULL) {
search->pending++;
search->max_requests--;
- xmlNodePtr query =
- rexmpp_xml_new_node("query",
+ rexmpp_xml_t *query =
+ rexmpp_xml_new_elem("query",
"http://jabber.org/protocol/disco#items");
rexmpp_cached_iq_new(s, "get", jid, query,
rexmpp_disco_find_feature_cb,
search, search->fresh);
- free(jid);
}
}
} else if (rexmpp_xml_match(query,
"http://jabber.org/protocol/disco#items",
"query")) {
- xmlNodePtr child = xmlFirstElementChild(query);
+ rexmpp_xml_t *child = rexmpp_xml_first_elem_child(query);
while (child != NULL && (search->max_requests > 0)) {
if (rexmpp_xml_match(child, "http://jabber.org/protocol/disco#items",
"item")) {
- char *jid = xmlGetProp(child, "jid");
+ const char *jid = rexmpp_xml_find_attr_val(child, "jid");
if (jid != NULL) {
search->pending++;
search->max_requests--;
- xmlNodePtr query =
- rexmpp_xml_new_node("query",
+ rexmpp_xml_t *query =
+ rexmpp_xml_new_elem("query",
"http://jabber.org/protocol/disco#info");
rexmpp_cached_iq_new(s, "get", jid, query,
rexmpp_disco_find_feature_cb,
search, search->fresh);
}
}
- child = child->next;
+ child = rexmpp_xml_next_elem_sibling(child);
}
}
}
@@ -433,6 +485,9 @@ rexmpp_disco_find_feature (rexmpp_t *s,
{
struct rexmpp_feature_search *search =
malloc(sizeof(struct rexmpp_feature_search));
+ if (search == NULL) {
+ return REXMPP_E_MALLOC;
+ }
search->max_requests = max_requests - 1;
search->found = 0;
search->pending = 1;
@@ -440,8 +495,8 @@ rexmpp_disco_find_feature (rexmpp_t *s,
search->cb_data = cb_data;
search->fresh = fresh;
search->feature_var = feature_var;
- xmlNodePtr query =
- rexmpp_xml_new_node("query", "http://jabber.org/protocol/disco#info");
+ rexmpp_xml_t *query =
+ rexmpp_xml_new_elem("query", "http://jabber.org/protocol/disco#info");
if (jid == NULL) {
jid = s->initial_jid.domain;
}
@@ -449,18 +504,18 @@ rexmpp_disco_find_feature (rexmpp_t *s,
rexmpp_disco_find_feature_cb, search, fresh);
}
-xmlNodePtr rexmpp_disco_info (rexmpp_t *s) {
+rexmpp_xml_t *rexmpp_disco_info (rexmpp_t *s) {
if (s->disco_info != NULL) {
return s->disco_info;
}
- xmlNodePtr prev = NULL, cur;
+ rexmpp_xml_t *prev = NULL, *cur;
/* There must be at least one identity, so filling in somewhat
sensible defaults. A basic client may leave them be, while an
advanced one would adjust and/or extend them. */
- s->disco_info = xmlNewNode(NULL, "identity");
- xmlNewProp(s->disco_info, "category", "client");
- xmlNewProp(s->disco_info, "type", s->client_type);
- xmlNewProp(s->disco_info, "name", s->client_name);
+ s->disco_info = rexmpp_xml_new_elem("identity", NULL);
+ rexmpp_xml_add_attr(s->disco_info, "category", "client");
+ rexmpp_xml_add_attr(s->disco_info, "type", s->client_type);
+ rexmpp_xml_add_attr(s->disco_info, "name", s->client_name);
prev = s->disco_info;
cur = rexmpp_xml_feature("http://jabber.org/protocol/disco#info");
prev->next = cur;
@@ -511,17 +566,17 @@ xmlNodePtr 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;
@@ -532,29 +587,30 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
s->carbons_state = REXMPP_CARBONS_INACTIVE;
s->manual_host = NULL;
s->manual_port = 5222;
- s->manual_direct_tls = 0;
+ s->manual_direct_tls = false;
s->disco_node = "rexmpp";
s->socks_host = NULL;
s->server_host = NULL;
- s->enable_carbons = 1;
- s->manage_roster = 1;
+ s->enable_carbons = true;
+ s->manage_roster = true;
s->roster_cache_file = NULL;
- s->track_roster_presence = 1;
- s->track_roster_events = 1;
- s->nick_notifications = 1;
+ s->track_roster_presence = true;
+ s->track_roster_events = true;
+ s->nick_notifications = true;
#ifdef HAVE_GPGME
- s->retrieve_openpgp_keys = 1;
+ s->retrieve_openpgp_keys = true;
#else
- s->retrieve_openpgp_keys = 0;
+ s->retrieve_openpgp_keys = false;
#endif
- s->autojoin_bookmarked_mucs = 1;
+ s->autojoin_bookmarked_mucs = true;
s->tls_policy = REXMPP_TLS_REQUIRE;
- s->enable_jingle = 1;
+ s->enable_jingle = true;
s->client_name = PACKAGE_NAME;
s->client_type = "console";
s->client_version = PACKAGE_VERSION;
s->local_address = NULL;
- s->jingle_prefer_rtcp_mux = 1;
+ s->jingle_prefer_rtcp_mux = true;
+ s->muc_ping_default_delay = 600;
s->send_buffer = NULL;
s->send_queue = NULL;
s->server_srv = NULL;
@@ -562,6 +618,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
s->server_srv_tls = NULL;
s->server_srv_tls_cur = -1;
s->server_socket = -1;
+ s->server_socket_dns_secure = false;
s->current_element_root = NULL;
s->current_element = NULL;
s->input_queue = NULL;
@@ -577,7 +634,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
s->iq_cache = NULL;
s->reconnect_number = 0;
s->next_reconnect_time.tv_sec = 0;
- s->next_reconnect_time.tv_usec = 0;
+ s->next_reconnect_time.tv_nsec = 0;
s->initial_jid.full[0] = '\0';
s->assigned_jid.full[0] = '\0';
s->stanza_queue_size = 1024;
@@ -585,52 +642,50 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
s->iq_queue_size = 1024;
s->iq_cache_size = 1024;
s->max_jingle_sessions = 1024;
+ s->x509_cert_file = NULL;
+ s->x509_key_file = NULL;
+ s->x509_trust_file = NULL;
s->log_function = log_func;
s->sasl_property_cb = NULL;
s->xml_in_cb = NULL;
s->xml_out_cb = NULL;
s->roster_modify_cb = NULL;
s->console_print_cb = NULL;
+ s->socket_cb = NULL;
s->ping_delay = 600;
- s->ping_requested = 0;
- s->last_network_activity = 0;
+ s->ping_requested = false;
+ s->last_network_activity.tv_sec = 0;
+ s->last_network_activity.tv_nsec = 0;
+ s->muc_ping = NULL;
s->disco_info = NULL;
- /* The default description. Since the players and streamers are
- external for now, this may be adjusted by an application or a
- user. */
s->jingle_rtp_description =
- rexmpp_xml_new_node("description", "urn:xmpp:jingle:apps:rtp:1");
- xmlNewProp(s->jingle_rtp_description, "media", "audio");
- xmlNodePtr pl_type;
-
- pl_type = rexmpp_xml_new_node("payload-type", "urn:xmpp:jingle:apps:rtp:1");
- xmlNewProp(pl_type, "id", "97");
- xmlNewProp(pl_type, "name", "opus");
- xmlNewProp(pl_type, "clockrate", "48000");
- xmlNewProp(pl_type, "channels", "2");
- xmlAddChild(s->jingle_rtp_description, pl_type);
-
- pl_type = rexmpp_xml_new_node("payload-type", "urn:xmpp:jingle:apps:rtp:1");
- xmlNewProp(pl_type, "id", "96");
- xmlNewProp(pl_type, "name", "speex");
- xmlNewProp(pl_type, "clockrate", "32000");
- xmlNewProp(pl_type, "channels", "1");
- xmlAddChild(s->jingle_rtp_description, pl_type);
-
- pl_type = rexmpp_xml_new_node("payload-type", "urn:xmpp:jingle:apps:rtp:1");
- xmlNewProp(pl_type, "id", "0");
- xmlNewProp(pl_type, "name", "PCMU");
- xmlNewProp(pl_type, "clockrate", "8000");
- xmlNewProp(pl_type, "channels", "1");
- xmlAddChild(s->jingle_rtp_description, pl_type);
-
- pl_type = rexmpp_xml_new_node("payload-type", "urn:xmpp:jingle:apps:rtp:1");
- xmlNewProp(pl_type, "id", "8");
- xmlNewProp(pl_type, "name", "PCMA");
- xmlNewProp(pl_type, "clockrate", "8000");
- xmlNewProp(pl_type, "channels", "1");
- xmlAddChild(s->jingle_rtp_description, pl_type);
+ rexmpp_xml_new_elem("description", "urn:xmpp:jingle:apps:rtp:1");
+ rexmpp_xml_add_attr(s->jingle_rtp_description, "media", "audio");
+ rexmpp_xml_t *pl_type;
+
+#ifdef HAVE_OPUS
+ pl_type = rexmpp_xml_new_elem("payload-type", "urn:xmpp:jingle:apps:rtp:1");
+ rexmpp_xml_add_attr(pl_type, "id", "97");
+ rexmpp_xml_add_attr(pl_type, "name", "opus");
+ rexmpp_xml_add_attr(pl_type, "clockrate", "48000");
+ rexmpp_xml_add_attr(pl_type, "channels", "2");
+ rexmpp_xml_add_child(s->jingle_rtp_description, pl_type);
+#endif
+
+ pl_type = rexmpp_xml_new_elem("payload-type", "urn:xmpp:jingle:apps:rtp:1");
+ rexmpp_xml_add_attr(pl_type, "id", "0");
+ rexmpp_xml_add_attr(pl_type, "name", "PCMU");
+ rexmpp_xml_add_attr(pl_type, "clockrate", "8000");
+ rexmpp_xml_add_attr(pl_type, "channels", "1");
+ rexmpp_xml_add_child(s->jingle_rtp_description, pl_type);
+
+ pl_type = rexmpp_xml_new_elem("payload-type", "urn:xmpp:jingle:apps:rtp:1");
+ rexmpp_xml_add_attr(pl_type, "id", "8");
+ rexmpp_xml_add_attr(pl_type, "name", "PCMA");
+ rexmpp_xml_add_attr(pl_type, "clockrate", "8000");
+ rexmpp_xml_add_attr(pl_type, "channels", "1");
+ rexmpp_xml_add_child(s->jingle_rtp_description, pl_type);
if (jid == NULL) {
rexmpp_log(s, LOG_CRIT, "No initial JID is provided.");
@@ -646,6 +701,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
return REXMPP_E_JID;
}
+#ifdef HAVE_GCRYPT
if (! gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) {
rexmpp_log(s, LOG_DEBUG, "Initializing libgcrypt");
if (gcry_check_version(NULL) == NULL) {
@@ -654,8 +710,9 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
}
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
}
+#endif
- 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.");
@@ -663,13 +720,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;
}
@@ -677,7 +734,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;
}
@@ -685,7 +742,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
@@ -698,9 +755,11 @@ 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
+ s->pgp_ctx = NULL;
#endif
#ifdef HAVE_CURL
if (curl_global_init(CURL_GLOBAL_ALL) != 0) {
@@ -711,6 +770,8 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
rexmpp_log(s, LOG_CRIT, "Failed to initialize curl_multi");
/* todo: free other structures and fail */
}
+#else
+ s->curl_multi = NULL;
#endif
return REXMPP_SUCCESS;
@@ -729,6 +790,7 @@ void rexmpp_cleanup (rexmpp_t *s) {
if (s->tcp_state == REXMPP_TCP_CONNECTING) {
int sock = rexmpp_tcp_conn_finish(&s->server_connection);
if (sock != -1) {
+ rexmpp_log(s, LOG_DEBUG, "TCP disconnected");
close(sock);
}
s->tcp_state = REXMPP_TCP_NONE;
@@ -743,20 +805,20 @@ void rexmpp_cleanup (rexmpp_t *s) {
s->send_buffer = NULL;
}
if (s->stream_features != NULL) {
- xmlFreeNode(s->stream_features);
+ rexmpp_xml_free(s->stream_features);
s->stream_features = NULL;
}
if (s->send_queue != NULL) {
- xmlFreeNodeList(s->send_queue);
+ rexmpp_xml_free_list(s->send_queue);
s->send_queue = NULL;
}
if (s->current_element_root != NULL) {
- xmlFreeNode(s->current_element_root);
+ rexmpp_xml_free_list(s->current_element_root);
s->current_element_root = NULL;
s->current_element = NULL;
}
if (s->input_queue != NULL) {
- xmlFreeNodeList(s->input_queue);
+ rexmpp_xml_free_list(s->input_queue);
s->input_queue = NULL;
s->input_queue_last = NULL;
}
@@ -777,12 +839,12 @@ void rexmpp_cleanup (rexmpp_t *s) {
void rexmpp_iq_finish (rexmpp_t *s,
rexmpp_iq_t *iq,
int success,
- xmlNodePtr response)
+ rexmpp_xml_t *response)
{
if (iq->cb != NULL) {
iq->cb(s, iq->cb_data, iq->request, response, success);
}
- xmlFreeNode(iq->request);
+ rexmpp_xml_free(iq->request);
free(iq);
}
@@ -800,9 +862,9 @@ 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) {
- xmlFreeNode(s->jingle_rtp_description);
+ rexmpp_xml_free(s->jingle_rtp_description);
s->jingle_rtp_description = NULL;
}
if (s->stream_id != NULL) {
@@ -810,27 +872,33 @@ void rexmpp_done (rexmpp_t *s) {
s->stream_id = NULL;
}
if (s->roster_items != NULL) {
- xmlFreeNodeList(s->roster_items);
+ rexmpp_xml_free_list(s->roster_items);
s->roster_items = NULL;
}
if (s->roster_presence != NULL) {
- xmlFreeNodeList(s->roster_presence);
+ rexmpp_xml_free_list(s->roster_presence);
s->roster_presence = NULL;
}
if (s->roster_events != NULL) {
- xmlFreeNodeList(s->roster_events);
+ rexmpp_xml_free_list(s->roster_events);
s->roster_events = NULL;
}
if (s->roster_ver != NULL) {
free(s->roster_ver);
s->roster_ver = NULL;
}
+ while (s->muc_ping != NULL) {
+ rexmpp_muc_ping_t *mp_next = s->muc_ping->next;
+ free(s->muc_ping->jid);
+ free(s->muc_ping);
+ s->muc_ping = mp_next;
+ }
if (s->disco_info != NULL) {
- xmlFreeNodeList(s->disco_info);
+ rexmpp_xml_free_list(s->disco_info);
s->disco_info = NULL;
}
if (s->stanza_queue != NULL) {
- xmlFreeNodeList(s->stanza_queue);
+ rexmpp_xml_free_list(s->stanza_queue);
s->stanza_queue = NULL;
}
while (s->active_iq != NULL) {
@@ -840,7 +908,7 @@ void rexmpp_done (rexmpp_t *s) {
rexmpp_iq_finish(s, iq, 0, NULL);
}
if (s->iq_cache != NULL) {
- xmlFreeNodeList(s->iq_cache);
+ rexmpp_xml_free_list(s->iq_cache);
s->iq_cache = NULL;
}
}
@@ -853,7 +921,7 @@ void rexmpp_schedule_reconnect (rexmpp_t *s) {
return;
}
if (s->reconnect_number == 0) {
- gcry_create_nonce((char*)&s->reconnect_seconds, sizeof(time_t));
+ rexmpp_random_buf((char*)&s->reconnect_seconds, sizeof(time_t));
if (s->reconnect_seconds < 0) {
s->reconnect_seconds = - s->reconnect_seconds;
}
@@ -866,7 +934,7 @@ void rexmpp_schedule_reconnect (rexmpp_t *s) {
if (seconds > 3600) {
seconds = 3600;
}
- gettimeofday(&(s->next_reconnect_time), NULL);
+ clock_gettime(CLOCK_MONOTONIC, &(s->next_reconnect_time));
s->next_reconnect_time.tv_sec += seconds;
rexmpp_log(s, LOG_DEBUG, "Scheduled reconnect number %d, in %d seconds",
s->reconnect_number,
@@ -884,96 +952,7 @@ const char *jid_bare_to_host (const char *jid_bare) {
return NULL;
}
-char *rexmpp_gen_id (rexmpp_t *s) {
- (void)s;
- char buf_raw[18], *buf_base64 = NULL;
- size_t buf_base64_len = 0;
- gcry_create_nonce(buf_raw, 18);
- rexmpp_base64_to(buf_raw, 18, &buf_base64, &buf_base64_len);
- return buf_base64;
-}
-
-xmlNodePtr rexmpp_xml_add_id (rexmpp_t *s, xmlNodePtr node) {
- char *buf = rexmpp_gen_id(s);
- if (buf == NULL) {
- return NULL;
- }
- xmlNewProp(node, "id", buf);
- free(buf);
- return node;
-}
-
-unsigned int rexmpp_xml_siblings_count (xmlNodePtr node) {
- unsigned int i;
- for (i = 0; node != NULL; i++) {
- node = xmlNextElementSibling(node);
- }
- return i;
-}
-
-int rexmpp_xml_match (xmlNodePtr node,
- const char *namespace,
- const char *name)
-{
- if (node == NULL) {
- return 0;
- }
- if (name != NULL) {
- if (strcmp(name, node->name) != 0) {
- return 0;
- }
- }
- if (namespace != NULL) {
- if (node->nsDef == NULL || node->nsDef->href == NULL) {
- if (strcmp(namespace, "jabber:client") != 0) {
- return 0;
- }
- } else {
- if (node->nsDef) {
- if (strcmp(namespace, node->nsDef->href) != 0) {
- return 0;
- }
- } else {
- if (namespace != NULL) {
- return 0;
- }
- }
- }
- }
- return 1;
-}
-
-int rexmpp_xml_eq (xmlNodePtr n1, xmlNodePtr n2) {
- /* Just serialize and compare strings for now: awkward, but
- simple. */
- char *n1str = rexmpp_xml_serialize(n1);
- char *n2str = rexmpp_xml_serialize(n2);
- int eq = (strcmp(n1str, n2str) == 0);
- free(n1str);
- free(n2str);
- return eq;
-}
-
-xmlNodePtr rexmpp_xml_find_child (xmlNodePtr node,
- const char *namespace,
- const char *name)
-{
- if (node == NULL) {
- return NULL;
- }
- xmlNodePtr child;
- for (child = xmlFirstElementChild(node);
- child != NULL;
- child = xmlNextElementSibling(child))
- {
- if (rexmpp_xml_match(child, namespace, name)) {
- return child;
- }
- }
- return NULL;
-}
-
-xmlNodePtr rexmpp_xml_set_delay (rexmpp_t *s, xmlNodePtr node) {
+rexmpp_xml_t *rexmpp_xml_set_delay (rexmpp_t *s, rexmpp_xml_t *node) {
if (rexmpp_xml_find_child (node, NULL, "delay")) {
return node;
}
@@ -982,42 +961,15 @@ xmlNodePtr rexmpp_xml_set_delay (rexmpp_t *s, xmlNodePtr node) {
struct tm utc_time;
gmtime_r(&t, &utc_time);
strftime(buf, 42, "%FT%TZ", &utc_time);
- xmlNodePtr delay = xmlNewChild(node, NULL, "delay", NULL);
- xmlNewProp(delay, "stamp", buf);
+ rexmpp_xml_t *delay = rexmpp_xml_new_elem("delay", NULL);
+ rexmpp_xml_add_child(node, delay);
+ rexmpp_xml_add_attr(delay, "stamp", buf);
if (s != NULL && s->assigned_jid.full[0]) {
- xmlNewProp(delay, "from", s->assigned_jid.full);
+ rexmpp_xml_add_attr(delay, "from", s->assigned_jid.full);
}
return node;
}
-xmlNodePtr rexmpp_xml_parse (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);
- }
- return elem;
-}
-
-char *rexmpp_xml_serialize (xmlNodePtr node) {
- xmlBufferPtr buf = xmlBufferCreate();
- xmlSaveCtxtPtr ctx = xmlSaveToBuffer(buf, "utf-8", 0);
- xmlSaveTree(ctx, node);
- xmlSaveFlush(ctx);
- xmlSaveClose(ctx);
- unsigned char *out = xmlBufferDetach(buf);
- xmlBufferFree(buf);
- return out;
-}
-
-int rexmpp_xml_is_stanza (xmlNodePtr node) {
- return rexmpp_xml_match(node, "jabber:client", "message") ||
- rexmpp_xml_match(node, "jabber:client", "iq") ||
- rexmpp_xml_match(node, "jabber:client", "presence");
-}
-
-
rexmpp_err_t rexmpp_send_start (rexmpp_t *s, const void *data, size_t data_len)
{
int sasl_err;
@@ -1052,61 +1004,64 @@ rexmpp_err_t rexmpp_send_continue (rexmpp_t *s)
}
ssize_t ret;
rexmpp_tls_err_t err;
- int tls_was_active;
while (1) {
- tls_was_active = (s->tls_state == REXMPP_TLS_ACTIVE);
- if (tls_was_active) {
+ if (s->tls_state == REXMPP_TLS_ACTIVE) {
err = rexmpp_tls_send (s,
+ s->tls,
s->send_buffer,
s->send_buffer_len,
&ret);
- } else {
- ret = send (s->server_socket,
- s->send_buffer + s->send_buffer_sent,
- s->send_buffer_len - s->send_buffer_sent,
- 0);
- }
- if (ret > 0) {
- s->last_network_activity = time(NULL);
- s->send_buffer_sent += ret;
- if (s->send_buffer_sent == s->send_buffer_len) {
- free(s->send_buffer);
- s->send_buffer = NULL;
- if (s->send_queue != NULL) {
- xmlNodePtr node = s->send_queue;
- unsigned char *buf = rexmpp_xml_serialize(node);
- ret = rexmpp_send_start(s, buf, strlen(buf));
- free(buf);
- if (ret != REXMPP_SUCCESS) {
- return ret;
- }
- s->send_queue = xmlNextElementSibling(s->send_queue);
- xmlFreeNode(node);
- } else {
- return REXMPP_SUCCESS;
- }
- }
- } else {
- if (tls_was_active) {
+ if (ret <= 0) {
if (err != REXMPP_TLS_E_AGAIN) {
s->tls_state = REXMPP_TLS_ERROR;
/* Assume a TCP error for now as well. */
rexmpp_cleanup(s);
s->tcp_state = REXMPP_TCP_ERROR;
rexmpp_schedule_reconnect(s);
- return REXMPP_E_AGAIN;
}
- } else {
+ /* Returning E_AGAIN for now, since the error is potentially
+ recoverable after the scheduled reconnect. */
+ return REXMPP_E_AGAIN;
+ }
+ } else {
+ ret = send (s->server_socket,
+ s->send_buffer + s->send_buffer_sent,
+ s->send_buffer_len - s->send_buffer_sent,
+ 0);
+ if (ret <= 0) {
if (errno != EAGAIN) {
rexmpp_log(s, LOG_ERR, "TCP send error: %s", strerror(errno));
rexmpp_cleanup(s);
s->tcp_state = REXMPP_TCP_ERROR;
rexmpp_schedule_reconnect(s);
- return REXMPP_E_AGAIN;
}
+ /* E_AGAIN, similarly to the TLS case. */
+ return REXMPP_E_AGAIN;
}
- return REXMPP_E_AGAIN;
}
+
+ assert(ret > 0);
+
+ clock_gettime(CLOCK_MONOTONIC, &(s->last_network_activity));
+ s->send_buffer_sent += ret;
+ if (s->send_buffer_sent == s->send_buffer_len) {
+ free(s->send_buffer);
+ s->send_buffer = NULL;
+ if (s->send_queue != NULL) {
+ rexmpp_xml_t *node = s->send_queue;
+ char *buf = rexmpp_xml_serialize(node, 0);
+ ret = rexmpp_send_start(s, buf, strlen(buf));
+ free(buf);
+ if (ret != REXMPP_SUCCESS) {
+ return ret;
+ }
+ s->send_queue = s->send_queue->next;
+ rexmpp_xml_free(node);
+ } else {
+ return REXMPP_SUCCESS;
+ }
+ }
+
}
}
@@ -1121,19 +1076,19 @@ rexmpp_err_t rexmpp_send_raw (rexmpp_t *s, const void *data, size_t data_len)
rexmpp_err_t rexmpp_sm_send_req (rexmpp_t *s);
-rexmpp_err_t rexmpp_send (rexmpp_t *s, xmlNodePtr node)
+rexmpp_err_t rexmpp_send (rexmpp_t *s, rexmpp_xml_t *node)
{
int need_ack = 0;
int ret;
if (s->xml_out_cb != NULL && s->xml_out_cb(s, node) == 1) {
- xmlFreeNode(node);
+ rexmpp_xml_free(node);
rexmpp_log(s, LOG_WARNING, "Message sending was cancelled by xml_out_cb.");
return REXMPP_E_CANCELLED;
}
if (rexmpp_xml_siblings_count(s->send_queue) >= s->send_queue_size) {
- xmlFreeNode(node);
+ rexmpp_xml_free(node);
rexmpp_log(s, LOG_ERR, "The send queue is full, not sending.");
return REXMPP_E_SEND_QUEUE_FULL;
}
@@ -1144,20 +1099,21 @@ rexmpp_err_t rexmpp_send (rexmpp_t *s, xmlNodePtr node)
if (s->sm_state == REXMPP_SM_ACTIVE) {
if (s->stanzas_out_count >=
s->stanza_queue_size + s->stanzas_out_acknowledged) {
- xmlFreeNode(node);
+ rexmpp_xml_free(node);
rexmpp_log(s, LOG_ERR, "The stanza queue is full, not sending.");
return REXMPP_E_STANZA_QUEUE_FULL;
}
need_ack = 1;
- xmlNodePtr queued_stanza = rexmpp_xml_set_delay(s, xmlCopyNode(node, 1));
+ rexmpp_xml_t *queued_stanza =
+ rexmpp_xml_set_delay(s, rexmpp_xml_clone(node));
if (s->stanza_queue == NULL) {
s->stanza_queue = queued_stanza;
} else {
- xmlNodePtr last = s->stanza_queue;
- while (xmlNextElementSibling(last) != NULL) {
- last = xmlNextElementSibling(last);
+ rexmpp_xml_t *last = s->stanza_queue;
+ while (last->next != NULL) {
+ last = last->next;
}
- xmlAddNextSibling(last, queued_stanza);
+ last->next = queued_stanza;
}
}
if (s->sm_state != REXMPP_SM_INACTIVE) {
@@ -1166,10 +1122,10 @@ rexmpp_err_t rexmpp_send (rexmpp_t *s, xmlNodePtr node)
}
if (s->send_buffer == NULL) {
- unsigned char *buf = rexmpp_xml_serialize(node);
+ char *buf = rexmpp_xml_serialize(node, 0);
ret = rexmpp_send_raw(s, buf, strlen(buf));
free(buf);
- xmlFreeNode(node);
+ rexmpp_xml_free(node);
if (ret != REXMPP_SUCCESS && ret != REXMPP_E_AGAIN) {
return ret;
}
@@ -1177,11 +1133,11 @@ rexmpp_err_t rexmpp_send (rexmpp_t *s, xmlNodePtr node)
if (s->send_queue == NULL) {
s->send_queue = node;
} else {
- xmlNodePtr last = s->send_queue;
- while (xmlNextElementSibling(last) != NULL) {
- last = xmlNextElementSibling(last);
+ rexmpp_xml_t *last = s->send_queue;
+ while (last->next != NULL) {
+ last = last->next;
}
- xmlAddNextSibling(last, node);
+ last->next = node;
}
ret = REXMPP_E_AGAIN;
}
@@ -1192,28 +1148,25 @@ rexmpp_err_t rexmpp_send (rexmpp_t *s, xmlNodePtr node)
}
void rexmpp_iq_reply (rexmpp_t *s,
- xmlNodePtr req,
+ rexmpp_xml_t *req,
const char *type,
- xmlNodePtr payload)
+ rexmpp_xml_t *payload)
{
- xmlNodePtr iq_stanza = xmlNewNode(NULL, "iq");
- xmlNewNs(iq_stanza, "jabber:client", NULL);
- xmlNewProp(iq_stanza, "type", type);
- char *id = xmlGetProp(req, "id");
+ rexmpp_xml_t *iq_stanza = rexmpp_xml_new_elem("iq", "jabber:client");
+ rexmpp_xml_add_attr(iq_stanza, "type", type);
+ const char *id = rexmpp_xml_find_attr_val(req, "id");
if (id != NULL) {
- xmlNewProp(iq_stanza, "id", id);
- free(id);
+ rexmpp_xml_add_attr(iq_stanza, "id", id);
}
- char *to = xmlGetProp(req, "from");
+ const char *to = rexmpp_xml_find_attr_val(req, "from");
if (to != NULL) {
- xmlNewProp(iq_stanza, "to", to);
- free(to);
+ rexmpp_xml_add_attr(iq_stanza, "to", to);
}
if (s->assigned_jid.full[0]) {
- xmlNewProp(iq_stanza, "from", s->assigned_jid.full);
+ rexmpp_xml_add_attr(iq_stanza, "from", s->assigned_jid.full);
}
if (payload != NULL) {
- xmlAddChild(iq_stanza, payload);
+ rexmpp_xml_add_child(iq_stanza, payload);
}
rexmpp_send(s, iq_stanza);
}
@@ -1221,7 +1174,7 @@ void rexmpp_iq_reply (rexmpp_t *s,
rexmpp_err_t rexmpp_iq_new (rexmpp_t *s,
const char *type,
const char *to,
- xmlNodePtr payload,
+ rexmpp_xml_t *payload,
rexmpp_iq_callback_t cb,
void *cb_data)
{
@@ -1232,24 +1185,30 @@ rexmpp_err_t rexmpp_iq_new (rexmpp_t *s,
last = last->next;
}
if (i >= s->iq_queue_size && s->iq_queue_size > 0) {
+ assert(prev != NULL);
+ assert(last != NULL);
rexmpp_log(s, LOG_WARNING,
"The IQ queue limit is reached, giving up on the oldest IQ.");
prev->next = NULL;
rexmpp_iq_finish(s, last, 0, NULL);
}
- xmlNodePtr iq_stanza = rexmpp_xml_add_id(s, xmlNewNode(NULL, "iq"));
- xmlNewNs(iq_stanza, "jabber:client", NULL);
- xmlNewProp(iq_stanza, "type", type);
+ rexmpp_xml_t *iq_stanza =
+ rexmpp_xml_new_elem("iq", "jabber:client");
+ rexmpp_xml_add_id(iq_stanza);
+ rexmpp_xml_add_attr(iq_stanza, "type", type);
if (to != NULL) {
- xmlNewProp(iq_stanza, "to", to);
+ rexmpp_xml_add_attr(iq_stanza, "to", to);
}
if (s->assigned_jid.full[0]) {
- xmlNewProp(iq_stanza, "from", s->assigned_jid.full);
+ rexmpp_xml_add_attr(iq_stanza, "from", s->assigned_jid.full);
}
- xmlAddChild(iq_stanza, payload);
+ rexmpp_xml_add_child(iq_stanza, payload);
rexmpp_iq_t *iq = malloc(sizeof(rexmpp_iq_t));
- iq->request = xmlCopyNode(iq_stanza, 1);
+ if (iq == NULL) {
+ return REXMPP_E_MALLOC;
+ }
+ iq->request = rexmpp_xml_clone(iq_stanza);
iq->cb = cb;
iq->cb_data = cb_data;
iq->next = s->active_iq;
@@ -1259,12 +1218,12 @@ rexmpp_err_t rexmpp_iq_new (rexmpp_t *s,
void rexmpp_iq_cache_cb (rexmpp_t *s,
void *cb_data,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
if (success && response != NULL) {
- xmlNodePtr prev_last = NULL, last = NULL, ciq = s->iq_cache;
+ rexmpp_xml_t *prev_last = NULL, *last = NULL, *ciq = s->iq_cache;
uint32_t size = 0;
while (ciq != NULL && ciq->next != NULL) {
prev_last = last;
@@ -1273,12 +1232,12 @@ void rexmpp_iq_cache_cb (rexmpp_t *s,
ciq = ciq->next->next;
}
if (size >= s->iq_queue_size && prev_last != NULL) {
- xmlFreeNode(last->next);
- xmlFreeNode(last);
+ rexmpp_xml_free(last->next);
+ rexmpp_xml_free(last);
prev_last->next->next = NULL;
}
- xmlNodePtr req = xmlCopyNode(request, 1);
- xmlNodePtr resp = xmlCopyNode(response, 1);
+ rexmpp_xml_t *req = rexmpp_xml_clone(request);
+ rexmpp_xml_t *resp = rexmpp_xml_clone(response);
req->next = resp;
resp->next = s->iq_cache;
s->iq_cache = req;
@@ -1293,24 +1252,22 @@ void rexmpp_iq_cache_cb (rexmpp_t *s,
rexmpp_err_t rexmpp_cached_iq_new (rexmpp_t *s,
const char *type,
const char *to,
- xmlNodePtr payload,
+ rexmpp_xml_t *payload,
rexmpp_iq_callback_t cb,
void *cb_data,
int fresh)
{
if (! fresh) {
- xmlNodePtr ciq = s->iq_cache;
+ rexmpp_xml_t *ciq = s->iq_cache;
while (ciq != NULL && ciq->next != NULL) {
- xmlNodePtr ciq_pl = xmlFirstElementChild(ciq);
- char *ciq_type = xmlGetProp(ciq, "type");
- char *ciq_to = xmlGetProp(ciq, "to");
+ rexmpp_xml_t *ciq_pl = ciq->alt.elem.children;
+ const char *ciq_type = rexmpp_xml_find_attr_val(ciq, "type");
+ const char *ciq_to = rexmpp_xml_find_attr_val(ciq, "to");
int matches = (rexmpp_xml_eq(ciq_pl, payload) &&
strcmp(ciq_type, type) == 0 &&
strcmp(ciq_to, to) == 0);
- free(ciq_to);
- free(ciq_type);
if (matches) {
- xmlFreeNode(payload);
+ rexmpp_xml_free(payload);
if (cb != NULL) {
cb(s, cb_data, ciq, ciq->next, 1);
}
@@ -1328,24 +1285,23 @@ rexmpp_err_t rexmpp_cached_iq_new (rexmpp_t *s,
rexmpp_err_t rexmpp_sm_ack (rexmpp_t *s) {
char buf[11];
- xmlNodePtr ack = xmlNewNode(NULL, "a");
- xmlNewNs(ack, "urn:xmpp:sm:3", NULL);
+ rexmpp_xml_t *ack = rexmpp_xml_new_elem("a", "urn:xmpp:sm:3");
snprintf(buf, 11, "%u", s->stanzas_in_count);
- xmlNewProp(ack, "h", buf);
+ rexmpp_xml_add_attr(ack, "h", buf);
return rexmpp_send(s, ack);
}
rexmpp_err_t rexmpp_sm_send_req (rexmpp_t *s) {
- xmlNodePtr ack = xmlNewNode(NULL, "r");
- xmlNewNs(ack, "urn:xmpp:sm:3", NULL);
- return rexmpp_send(s, ack);
+ rexmpp_xml_t *req = rexmpp_xml_new_elem("r", "urn:xmpp:sm:3");
+ return rexmpp_send(s, req);
}
-rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem);
+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;
@@ -1355,12 +1311,12 @@ rexmpp_err_t rexmpp_recv (rexmpp_t *s) {
do {
tls_was_active = (s->tls_state == REXMPP_TLS_ACTIVE);
if (tls_was_active) {
- recv_err = rexmpp_tls_recv(s, chunk_raw, 4096, &chunk_raw_len);
+ recv_err = rexmpp_tls_recv(s, s->tls, chunk_raw, 4096, &chunk_raw_len);
} else {
chunk_raw_len = recv(s->server_socket, chunk_raw, 4096, 0);
}
if (chunk_raw_len > 0) {
- s->last_network_activity = time(NULL);
+ clock_gettime(CLOCK_MONOTONIC, &(s->last_network_activity));
if (s->sasl_state == REXMPP_SASL_ACTIVE) {
sasl_err = rexmpp_sasl_decode(s, chunk_raw, chunk_raw_len,
&chunk, &chunk_len);
@@ -1372,13 +1328,13 @@ 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, 0);
if (chunk != chunk_raw && chunk != NULL) {
free(chunk);
}
chunk = NULL;
- xmlNodePtr elem;
+ rexmpp_xml_t *elem;
for (elem = s->input_queue;
/* Skipping everything after an error. Might be better to
process it anyway, but it could lead to more errors if
@@ -1393,7 +1349,7 @@ rexmpp_err_t rexmpp_recv (rexmpp_t *s) {
err = rexmpp_process_element(s, elem);
}
}
- xmlFreeNodeList(s->input_queue);
+ rexmpp_xml_free_list(s->input_queue);
s->input_queue = NULL;
s->input_queue_last = NULL;
if (err != REXMPP_SUCCESS && err != REXMPP_E_AGAIN) {
@@ -1402,9 +1358,9 @@ rexmpp_err_t rexmpp_recv (rexmpp_t *s) {
} else if (chunk_raw_len == 0) {
if (tls_was_active) {
s->tls_state = REXMPP_TLS_CLOSED;
- rexmpp_log(s, LOG_INFO, "TLS disconnected");
+ rexmpp_log(s, LOG_DEBUG, "TLS disconnected");
}
- rexmpp_log(s, LOG_INFO, "TCP disconnected");
+ rexmpp_log(s, LOG_DEBUG, "TCP disconnected");
rexmpp_cleanup(s);
if (s->stream_state == REXMPP_STREAM_READY ||
s->stream_state == REXMPP_STREAM_ERROR_RECONNECT) {
@@ -1537,7 +1493,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 {
@@ -1552,7 +1508,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 {
@@ -1631,10 +1587,10 @@ void rexmpp_srv_cb (rexmpp_t *s,
rexmpp_err_t rexmpp_resend_stanzas (rexmpp_t *s) {
uint32_t i, count;
rexmpp_err_t ret = REXMPP_SUCCESS;
- xmlNodePtr sq;
+ rexmpp_xml_t *sq;
count = s->stanzas_out_count - s->stanzas_out_acknowledged;
for (i = 0; i < count && s->stanza_queue != NULL; i++) {
- sq = xmlNextElementSibling(s->stanza_queue);
+ sq = s->stanza_queue->next;
ret = rexmpp_send(s, s->stanza_queue);
if (ret > REXMPP_E_AGAIN) {
return ret;
@@ -1651,12 +1607,11 @@ rexmpp_err_t rexmpp_resend_stanzas (rexmpp_t *s) {
return ret;
}
-void rexmpp_sm_handle_ack (rexmpp_t *s, xmlNodePtr elem) {
- char *h = xmlGetProp(elem, "h");
+void rexmpp_sm_handle_ack (rexmpp_t *s, rexmpp_xml_t *elem) {
+ const char *h = rexmpp_xml_find_attr_val(elem, "h");
if (h != NULL) {
uint32_t prev_ack = s->stanzas_out_acknowledged;
s->stanzas_out_acknowledged = strtoul(h, NULL, 10);
- xmlFree(h);
rexmpp_log(s, LOG_DEBUG,
"server acknowledged %u out of %u sent stanzas",
s->stanzas_out_acknowledged,
@@ -1665,8 +1620,8 @@ void rexmpp_sm_handle_ack (rexmpp_t *s, xmlNodePtr elem) {
if (prev_ack <= s->stanzas_out_acknowledged) {
uint32_t i;
for (i = prev_ack; i < s->stanzas_out_acknowledged; i++) {
- xmlNodePtr sq = xmlNextElementSibling(s->stanza_queue);
- xmlFreeNode(s->stanza_queue);
+ rexmpp_xml_t *sq = s->stanza_queue->next;
+ rexmpp_xml_free(s->stanza_queue);
s->stanza_queue = sq;
}
} else {
@@ -1685,8 +1640,8 @@ void rexmpp_sm_handle_ack (rexmpp_t *s, xmlNodePtr elem) {
void rexmpp_carbons_enabled (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)ptr;
@@ -1703,8 +1658,8 @@ void rexmpp_carbons_enabled (rexmpp_t *s,
void rexmpp_pong (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)ptr;
@@ -1716,15 +1671,15 @@ void rexmpp_pong (rexmpp_t *s,
void rexmpp_disco_carbons_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success) {
(void)ptr;
(void)req;
(void)response;
if (success) {
- xmlNodePtr carbons_enable =
- rexmpp_xml_new_node("enable", "urn:xmpp:carbons:2");
+ rexmpp_xml_t *carbons_enable =
+ rexmpp_xml_new_elem("enable", "urn:xmpp:carbons:2");
s->carbons_state = REXMPP_CARBONS_NEGOTIATION;
rexmpp_iq_new(s, "set", NULL, carbons_enable,
rexmpp_carbons_enabled, NULL);
@@ -1747,27 +1702,31 @@ void rexmpp_stream_is_ready(rexmpp_t *s) {
if (s->roster_cache_file != NULL) {
rexmpp_roster_cache_read(s);
}
- xmlNodePtr roster_query = xmlNewNode(NULL, "query");
- xmlNewNs(roster_query, "jabber:iq:roster", NULL);
+ rexmpp_xml_t *roster_query =
+ rexmpp_xml_new_elem("query", "jabber:iq:roster");
if (s->roster_ver != NULL) {
- xmlNewProp(roster_query, "ver", s->roster_ver);
+ rexmpp_xml_add_attr(roster_query, "ver", s->roster_ver);
} else {
- xmlNewProp(roster_query, "ver", "");
+ rexmpp_xml_add_attr(roster_query, "ver", "");
}
rexmpp_iq_new(s, "get", NULL,
roster_query, rexmpp_iq_roster_get, NULL);
}
- xmlNodePtr presence = rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence"));
+ rexmpp_xml_t *presence =
+ rexmpp_xml_new_elem("presence", "jabber:client");
+ rexmpp_xml_add_id(presence);
+
char *caps_hash = rexmpp_capabilities_hash(s, rexmpp_disco_info(s));
if (caps_hash != NULL) {
- xmlNodePtr c = xmlNewNode(NULL, "c");
- xmlNewNs(c, "http://jabber.org/protocol/caps", NULL);
- xmlNewProp(c, "hash", "sha-1");
- xmlNewProp(c, "node", s->disco_node);
- xmlNewProp(c, "ver", caps_hash);
- xmlAddChild(presence, c);
+ rexmpp_xml_t *c =
+ rexmpp_xml_new_elem("c", "http://jabber.org/protocol/caps");
+ rexmpp_xml_add_attr(c, "hash", "sha-1");
+ rexmpp_xml_add_attr(c, "node", s->disco_node);
+ rexmpp_xml_add_attr(c, "ver", caps_hash);
+ rexmpp_xml_add_child(presence, c);
free(caps_hash);
}
+
rexmpp_send(s, presence);
}
@@ -1775,8 +1734,8 @@ void rexmpp_stream_is_ready(rexmpp_t *s) {
https://tools.ietf.org/html/rfc6120#section-7 */
void rexmpp_bound (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)ptr;
@@ -1787,24 +1746,22 @@ void rexmpp_bound (rexmpp_t *s,
return;
}
/* todo: handle errors */
- xmlNodePtr child = xmlFirstElementChild(response);
+ rexmpp_xml_t *child = rexmpp_xml_first_elem_child(response);
if (rexmpp_xml_match(child, "urn:ietf:params:xml:ns:xmpp-bind", "bind")) {
- xmlNodePtr jid = xmlFirstElementChild(child);
+ rexmpp_xml_t *jid = rexmpp_xml_first_elem_child(child);
if (rexmpp_xml_match(jid, "urn:ietf:params:xml:ns:xmpp-bind", "jid")) {
- char *jid_str = xmlNodeGetContent(jid);
+ const char *jid_str = rexmpp_xml_text_child(jid);
rexmpp_log(s, LOG_INFO, "jid: %s", jid_str);
rexmpp_jid_parse(jid_str, &(s->assigned_jid));
- free(jid_str);
}
if (s->stream_id == NULL &&
- (child = rexmpp_xml_find_child(s->stream_features, "urn:xmpp:sm:3",
- "sm"))) {
+ (rexmpp_xml_find_child(s->stream_features, "urn:xmpp:sm:3",
+ "sm") != NULL)) {
/* Try to resume a stream. */
s->sm_state = REXMPP_SM_NEGOTIATION;
s->stream_state = REXMPP_STREAM_SM_FULL;
- xmlNodePtr sm_enable = xmlNewNode(NULL, "enable");
- xmlNewNs(sm_enable, "urn:xmpp:sm:3", NULL);
- xmlNewProp(sm_enable, "resume", "true");
+ rexmpp_xml_t *sm_enable =
+ rexmpp_xml_new_elem("enable", "urn:xmpp:sm:3");
rexmpp_send(s, sm_enable);
s->stanzas_out_count = 0;
s->stanzas_out_acknowledged = 0;
@@ -1819,12 +1776,12 @@ void rexmpp_bound (rexmpp_t *s,
rexmpp_err_t rexmpp_stream_bind (rexmpp_t *s) {
/* Issue a bind request. */
s->stream_state = REXMPP_STREAM_BIND;
- xmlNodePtr bind_cmd = xmlNewNode(NULL, "bind");
- xmlNewNs(bind_cmd, "urn:ietf:params:xml:ns:xmpp-bind", NULL);
+ rexmpp_xml_t *bind_cmd =
+ rexmpp_xml_new_elem("bind", "urn:ietf:params:xml:ns:xmpp-bind");
return rexmpp_iq_new(s, "set", NULL, bind_cmd, rexmpp_bound, NULL);
}
-rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
+rexmpp_err_t rexmpp_process_element (rexmpp_t *s, rexmpp_xml_t *elem) {
rexmpp_console_on_recv(s, elem);
/* Stream negotiation,
@@ -1834,22 +1791,23 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
/* Remember features. */
if (s->stream_features != NULL) {
- xmlFreeNode(s->stream_features);
+ rexmpp_xml_free(s->stream_features);
}
- s->stream_features = xmlCopyNode(elem, 1);
+ s->stream_features = rexmpp_xml_clone(elem);
/* TODO: check for required features properly here. Currently
assuming that STARTTLS, SASL, and BIND (with an exception for
SM) are always required if they are present. */
- xmlNodePtr starttls =
+ rexmpp_xml_t *starttls =
rexmpp_xml_find_child(elem, "urn:ietf:params:xml:ns:xmpp-tls",
"starttls");
- xmlNodePtr sasl =
+ rexmpp_xml_t *sasl =
rexmpp_xml_find_child(elem, "urn:ietf:params:xml:ns:xmpp-sasl",
"mechanisms");
- xmlNodePtr bind =
+ rexmpp_xml_t *bind =
rexmpp_xml_find_child(elem, "urn:ietf:params:xml:ns:xmpp-bind", "bind");
- xmlNodePtr sm = rexmpp_xml_find_child(elem, "urn:xmpp:sm:3", "sm");
+ rexmpp_xml_t *sm =
+ rexmpp_xml_find_child(elem, "urn:xmpp:sm:3", "sm");
if (starttls != NULL) {
/* Go for TLS, unless we're both trying to avoid it, and have
@@ -1857,8 +1815,8 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
if (! (s->tls_policy == REXMPP_TLS_AVOID &&
(sasl != NULL || bind != NULL || sm != NULL))) {
s->stream_state = REXMPP_STREAM_STARTTLS;
- xmlNodePtr starttls_cmd = xmlNewNode(NULL, "starttls");
- xmlNewNs(starttls_cmd, "urn:ietf:params:xml:ns:xmpp-tls", NULL);
+ rexmpp_xml_t *starttls_cmd =
+ rexmpp_xml_new_elem("starttls", "urn:ietf:params:xml:ns:xmpp-tls");
rexmpp_send(s, starttls_cmd);
return REXMPP_SUCCESS;
}
@@ -1872,7 +1830,7 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
}
/* Nothing to negotiate. */
- if (xmlFirstElementChild(elem) == NULL) {
+ if (rexmpp_xml_first_elem_child(elem) == NULL) {
rexmpp_stream_is_ready(s);
return REXMPP_SUCCESS;
}
@@ -1882,18 +1840,17 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
s->sasl_state = REXMPP_SASL_NEGOTIATION;
char mech_list[2048]; /* todo: perhaps grow it dynamically */
mech_list[0] = '\0';
- xmlNodePtr mechanism;
- for (mechanism = xmlFirstElementChild(sasl);
+ rexmpp_xml_t *mechanism;
+ for (mechanism = rexmpp_xml_first_elem_child(sasl);
mechanism != NULL;
- mechanism = xmlNextElementSibling(mechanism)) {
+ mechanism = rexmpp_xml_next_elem_sibling(mechanism)) {
if (rexmpp_xml_match(mechanism, "urn:ietf:params:xml:ns:xmpp-sasl",
"mechanism")) {
- char *mech_str = xmlNodeGetContent(mechanism);
+ const char *mech_str = rexmpp_xml_text_child(mechanism);
snprintf(mech_list + strlen(mech_list),
2048 - strlen(mech_list),
"%s ",
mech_str);
- free(mech_str);
}
}
const char *mech = rexmpp_sasl_suggest_mechanism(s, mech_list);
@@ -1912,10 +1869,10 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
s->sasl_state = REXMPP_SASL_ERROR;
return REXMPP_E_SASL;
}
- xmlNodePtr auth_cmd = xmlNewNode(NULL, "auth");
- xmlNewProp(auth_cmd, "mechanism", mech);
- xmlNewNs(auth_cmd, "urn:ietf:params:xml:ns:xmpp-sasl", NULL);
- xmlNodeAddContent(auth_cmd, sasl_buf);
+ rexmpp_xml_t *auth_cmd =
+ rexmpp_xml_new_elem("auth", "urn:ietf:params:xml:ns:xmpp-sasl");
+ rexmpp_xml_add_attr(auth_cmd, "mechanism", mech);
+ rexmpp_xml_add_text(auth_cmd, sasl_buf);
free(sasl_buf);
rexmpp_send(s, auth_cmd);
return REXMPP_SUCCESS;
@@ -1925,10 +1882,10 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
s->stream_state = REXMPP_STREAM_SM_RESUME;
char buf[11];
snprintf(buf, 11, "%u", s->stanzas_in_count);
- xmlNodePtr sm_resume = xmlNewNode(NULL, "resume");
- xmlNewNs(sm_resume, "urn:xmpp:sm:3", NULL);
- xmlNewProp(sm_resume, "previd", s->stream_id);
- xmlNewProp(sm_resume, "h", buf);
+ rexmpp_xml_t *sm_resume =
+ rexmpp_xml_new_elem("resume", "urn:xmpp:sm:3");
+ rexmpp_xml_add_attr(sm_resume, "previd", s->stream_id);
+ rexmpp_xml_add_attr(sm_resume, "h", buf);
rexmpp_send(s, sm_resume);
return REXMPP_SUCCESS;
}
@@ -1937,7 +1894,8 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
return rexmpp_stream_bind(s);
}
} else {
- rexmpp_log(s, LOG_ERR, "Expected stream features, received %s", elem->name);
+ rexmpp_log(s, LOG_ERR, "Expected stream features, received %s",
+ elem->alt.elem.qname.name);
return REXMPP_E_STREAM;
}
}
@@ -1947,18 +1905,18 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
should cancel further processing by the library (so we can send
errors for unhandled IQs here). */
if (rexmpp_xml_match(elem, "jabber:client", "iq")) {
- char *type = xmlGetProp(elem, "type");
+ const char *type = rexmpp_xml_find_attr_val(elem, "type");
/* IQ responses. */
if (strcmp(type, "result") == 0 || strcmp(type, "error") == 0) {
- char *id = xmlGetProp(elem, "id");
+ const char *id = rexmpp_xml_find_attr_val(elem, "id");
rexmpp_iq_t *req = s->active_iq, *prev_req = NULL;
int found = 0;
while (req != NULL && found == 0) {
- char *req_id = xmlGetProp(req->request, "id");
- char *req_to = xmlGetProp(req->request, "to");
- char *rep_from = xmlGetProp(elem, "from");
+ const char *req_id = rexmpp_xml_find_attr_val(req->request, "id");
+ const char *req_to = rexmpp_xml_find_attr_val(req->request, "to");
+ const char *rep_from = rexmpp_xml_find_attr_val(elem, "from");
rexmpp_iq_t *req_next = req->next;
- int id_matches = (strcmp(id, req_id) == 0);
+ int id_matches = (req_id != NULL) && (strcmp(id, req_id) == 0);
int jid_matches = 0;
if (rep_from == NULL) {
jid_matches = 1;
@@ -1980,29 +1938,20 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
/* Finish and free the IQ request structure. */
rexmpp_iq_finish(s, req, success, elem);
}
- if (req_to != NULL) {
- free(req_to);
- }
- if (rep_from != NULL) {
- free(rep_from);
- }
- free(req_id);
prev_req = req;
req = req_next;
}
- free(id);
} else if (! rexmpp_jingle_iq(s, elem)) {
if (strcmp(type, "set") == 0) {
- xmlNodePtr query = xmlFirstElementChild(elem);
+ rexmpp_xml_t *query = rexmpp_xml_first_elem_child(elem);
int from_server = 0;
- char *from = xmlGetProp(elem, "from");
+ const char *from = rexmpp_xml_find_attr_val(elem, "from");
if (from == NULL) {
from_server = 1;
} else {
if (strcmp(from, s->initial_jid.domain) == 0) {
from_server = 1;
}
- free(from);
}
if (from_server &&
s->manage_roster &&
@@ -2011,8 +1960,12 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
if (s->roster_ver != NULL) {
free(s->roster_ver);
}
- s->roster_ver = xmlGetProp(query, "ver");
- rexmpp_modify_roster(s, xmlFirstElementChild(query));
+ s->roster_ver = NULL;
+ const char *roster_ver = rexmpp_xml_find_attr_val(query, "ver");
+ if (roster_ver != NULL) {
+ s->roster_ver = strdup(roster_ver);
+ }
+ rexmpp_modify_roster(s, rexmpp_xml_first_elem_child(query));
/* todo: check for errors */
rexmpp_iq_reply(s, elem, "result", NULL);
if (s->roster_cache_file != NULL) {
@@ -2024,9 +1977,9 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
rexmpp_xml_error("cancel", "service-unavailable"));
}
} else if (strcmp(type, "get") == 0) {
- xmlNodePtr query = xmlFirstElementChild(elem);
+ rexmpp_xml_t *query = rexmpp_xml_first_elem_child(elem);
if (rexmpp_xml_match(query, "http://jabber.org/protocol/disco#info", "query")) {
- char *node = xmlGetProp(query, "node");
+ const char *node = rexmpp_xml_find_attr_val(query, "node");
char *caps_hash = rexmpp_capabilities_hash(s, rexmpp_disco_info(s));
if (node == NULL ||
(caps_hash != NULL &&
@@ -2035,12 +1988,13 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
strncmp(node, s->disco_node, strlen(s->disco_node)) == 0 &&
node[strlen(s->disco_node)] == '#' &&
strcmp(node + strlen(s->disco_node) + 1, caps_hash) == 0)) {
- xmlNodePtr result = xmlNewNode(NULL, "query");
- xmlNewNs(result, "http://jabber.org/protocol/disco#info", NULL);
+ rexmpp_xml_t *result =
+ rexmpp_xml_new_elem("query", "http://jabber.org/protocol/disco#info");
if (node != NULL) {
- xmlNewProp(result, "node", node);
+ rexmpp_xml_add_attr(result, "node", node);
}
- xmlAddChild(result, xmlCopyNodeList(rexmpp_disco_info(s)));
+ rexmpp_xml_add_child(result,
+ rexmpp_xml_clone_list(rexmpp_disco_info(s)));
rexmpp_iq_reply(s, elem, "result", result);
} else {
rexmpp_log(s, LOG_WARNING,
@@ -2051,20 +2005,17 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
if (caps_hash != NULL) {
free(caps_hash);
}
- if (node != NULL) {
- free(node);
- }
} else if (rexmpp_xml_match(query, "urn:xmpp:ping", "ping")) {
rexmpp_iq_reply(s, elem, "result", NULL);
} else if (rexmpp_xml_match(query, "jabber:iq:version", "query")) {
- xmlNodePtr reply = xmlNewNode(NULL, "query");
- xmlNewNs(reply, "jabber:iq:version", NULL);
- xmlNodePtr name = xmlNewNode(NULL, "name");
- xmlNodeAddContent(name, s->client_name);
- xmlAddChild(reply, name);
- xmlNodePtr version = xmlNewNode(NULL, "version");
- xmlNodeAddContent(version, s->client_version);
- xmlAddChild(reply, version);
+ rexmpp_xml_t *reply =
+ rexmpp_xml_new_elem("query", "jabber:iq:version");
+ rexmpp_xml_t *name = rexmpp_xml_new_elem("name", NULL);
+ rexmpp_xml_add_text(name, s->client_name);
+ rexmpp_xml_add_child(reply, name);
+ rexmpp_xml_t *version = rexmpp_xml_new_elem("version", NULL);
+ rexmpp_xml_add_text(version, s->client_version);
+ rexmpp_xml_add_child(reply, version);
rexmpp_iq_reply(s, elem, "result", reply);
} else {
/* An unknown request. */
@@ -2073,49 +2024,44 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
}
}
}
- free(type);
}
/* Incoming presence information. */
if (rexmpp_xml_match(elem, "jabber:client", "presence") &&
s->manage_roster &&
s->track_roster_presence) {
- char *from = xmlGetProp(elem, "from");
+ const char *from = rexmpp_xml_find_attr_val(elem, "from");
if (from != NULL) {
struct rexmpp_jid from_jid;
rexmpp_jid_parse(from, &from_jid);
- xmlFree(from);
if (rexmpp_roster_find_item(s, from_jid.bare, NULL) != NULL) {
/* The bare JID is in the roster. */
- char *type = xmlGetProp(elem, "type");
- xmlNodePtr cur, prev;
+ const char *type = rexmpp_xml_find_attr_val(elem, "type");
+ rexmpp_xml_t *cur, *prev;
if (type == NULL || strcmp(type, "unavailable") == 0) {
/* Either a new "available" presence or an "unavailable"
one: remove the previously stored presence for this
JID. */
for (prev = NULL, cur = s->roster_presence;
cur != NULL;
- prev = cur, cur = xmlNextElementSibling(cur)) {
- char *cur_from = xmlGetProp(cur, "from");
+ prev = cur, cur = cur->next) {
+ const char *cur_from = rexmpp_xml_find_attr_val(cur, "from");
if (strcmp(cur_from, from_jid.full) == 0) {
if (prev == NULL) {
s->roster_presence = cur->next;
} else {
prev->next = cur->next;
}
- xmlFreeNode(cur);
- cur = NULL;
+ rexmpp_xml_free(cur);
+ break;
}
- free(cur_from);
}
}
if (type == NULL) {
/* An "available" presence: add it. */
- xmlNodePtr presence = xmlCopyNode(elem, 1);
+ rexmpp_xml_t *presence = rexmpp_xml_clone(elem);
presence->next = s->roster_presence;
s->roster_presence = presence;
- } else {
- free(type);
}
}
}
@@ -2123,28 +2069,27 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
/* Incoming messages. */
if (rexmpp_xml_match(elem, "jabber:client", "message")) {
- char *from = xmlGetProp(elem, "from");
+ const char *from = rexmpp_xml_find_attr_val(elem, "from");
if (from != NULL) {
struct rexmpp_jid from_jid;
rexmpp_jid_parse(from, &from_jid);
- xmlFree(from);
if (rexmpp_roster_find_item(s, from_jid.bare, NULL) != NULL ||
strcmp(from_jid.bare, s->assigned_jid.bare) == 0) {
- xmlNodePtr event =
+ rexmpp_xml_t *event =
rexmpp_xml_find_child(elem,
"http://jabber.org/protocol/pubsub#event",
"event");
if (event != NULL && s->manage_roster && s->track_roster_events) {
- xmlNodePtr items =
+ rexmpp_xml_t *items =
rexmpp_xml_find_child(event,
"http://jabber.org/protocol/pubsub#event",
"items");
if (items != NULL) {
- char *node = xmlGetProp(items, "node");
+ const char *node = rexmpp_xml_find_attr_val(items, "node");
if (node != NULL) {
/* Remove the previously stored items for the same sender
and node, if any. */
- xmlNodePtr prev, cur;
+ rexmpp_xml_t *prev, *cur;
cur = rexmpp_find_event(s, from_jid.bare, node, &prev);
if (cur) {
if (prev == NULL) {
@@ -2152,12 +2097,12 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
} else {
prev->next = cur->next;
}
- xmlFreeNode(cur);
+ rexmpp_xml_free(cur);
cur = NULL;
}
/* Add the new message. */
- xmlNodePtr message = xmlCopyNode(elem, 1);
+ rexmpp_xml_t *message = rexmpp_xml_clone(elem);
message->next = s->roster_events;
s->roster_events = message;
@@ -2169,56 +2114,50 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
if (s->autojoin_bookmarked_mucs &&
strcmp(node, "urn:xmpp:bookmarks:1") == 0 &&
strcmp(from_jid.bare, s->assigned_jid.bare) == 0) {
- xmlNodePtr item;
- for (item = xmlFirstElementChild(items);
+ rexmpp_xml_t *item;
+ for (item = rexmpp_xml_first_elem_child(items);
item != NULL;
- item = xmlNextElementSibling(item)) {
- xmlNodePtr conference =
+ item = rexmpp_xml_next_elem_sibling(item)) {
+ rexmpp_xml_t *conference =
rexmpp_xml_find_child(item,
"urn:xmpp:bookmarks:1",
"conference");
if (conference == NULL) {
continue;
}
- char *item_id = xmlGetProp(item, "id");
+ const char *item_id = rexmpp_xml_find_attr_val(item, "id");
if (item_id == NULL) {
continue;
}
- char *autojoin = xmlGetProp(conference, "autojoin");
+ const char *autojoin =
+ rexmpp_xml_find_attr_val(conference, "autojoin");
if (autojoin == NULL) {
- free(item_id);
continue;
}
if (strcmp(autojoin, "true") == 0 ||
strcmp(autojoin, "1") == 0) {
- xmlNodePtr presence =
- rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence"));
- xmlNewProp(presence, "from", s->assigned_jid.full);
- xmlNodePtr nick =
+ rexmpp_xml_t *nick =
rexmpp_xml_find_child(conference,
"urn:xmpp:bookmarks:1",
"nick");
- char *nick_str;
+ const char *nick_str = NULL;
if (nick != NULL) {
- nick_str = xmlNodeGetContent(nick);
- } else {
- nick_str = strdup(s->initial_jid.local);
+ nick_str = rexmpp_xml_text_child(nick);
+ }
+ if (nick_str == NULL) {
+ nick_str = s->initial_jid.local;
}
- char *jid = malloc(strlen(item_id) + strlen(nick_str) + 2);
- sprintf(jid, "%s/%s", item_id, nick_str);
- free(nick_str);
- xmlNewProp(presence, "to", jid);
- free(jid);
- xmlNodePtr x = xmlNewNode(NULL, "x");
- xmlNewNs(x, "http://jabber.org/protocol/muc", NULL);
- xmlAddChild(presence, x);
- rexmpp_send(s, presence);
+ char *occupant_jid =
+ malloc(strlen(item_id) + strlen(nick_str) + 2);
+ sprintf(occupant_jid, "%s/%s", item_id, nick_str);
+ const char *password =
+ rexmpp_xml_find_attr_val(conference, "password");
+ rexmpp_muc_join(s, occupant_jid, password,
+ s->muc_ping_default_delay);
+ free(occupant_jid);
}
- free(item_id);
- free(autojoin);
}
}
- free(node);
}
}
}
@@ -2263,23 +2202,21 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
int sasl_err;
if (rexmpp_xml_match(elem, "urn:ietf:params:xml:ns:xmpp-sasl",
"challenge")) {
- char *challenge = xmlNodeGetContent(elem);
+ const char *challenge = rexmpp_xml_text_child(elem);
sasl_err = rexmpp_sasl_step64 (s, challenge, (char**)&sasl_buf);
- free(challenge);
if (sasl_err) {
s->sasl_state = REXMPP_SASL_ERROR;
return REXMPP_E_SASL;
}
- xmlNodePtr response = xmlNewNode(NULL, "response");
- xmlNewNs(response, "urn:ietf:params:xml:ns:xmpp-sasl", NULL);
- xmlNodeAddContent(response, sasl_buf);
+ rexmpp_xml_t *response =
+ rexmpp_xml_new_elem("response", "urn:ietf:params:xml:ns:xmpp-sasl");
+ rexmpp_xml_add_text(response, sasl_buf);
free(sasl_buf);
- return rexmpp_send(s, response);
+ rexmpp_send(s, response);
} else if (rexmpp_xml_match(elem, "urn:ietf:params:xml:ns:xmpp-sasl",
"success")) {
- char *success = xmlNodeGetContent(elem);
+ const char *success = rexmpp_xml_text_child(elem);
sasl_err = rexmpp_sasl_step64 (s, success, (char**)&sasl_buf);
- free(success);
free(sasl_buf);
if (! sasl_err) {
rexmpp_log(s, LOG_DEBUG, "SASL success");
@@ -2288,7 +2225,7 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr 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")) {
@@ -2302,20 +2239,23 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
if (s->stream_state == REXMPP_STREAM_SM_FULL) {
if (rexmpp_xml_match(elem, "urn:xmpp:sm:3", "enabled")) {
s->sm_state = REXMPP_SM_ACTIVE;
- char *resume = xmlGetProp(elem, "resume");
+ const char *resume = rexmpp_xml_find_attr_val(elem, "resume");
if (resume != NULL) {
if (s->stream_id != NULL) {
free(s->stream_id);
}
- s->stream_id = xmlGetProp(elem, "id");
- xmlFree(resume);
+ const char *stream_id = rexmpp_xml_find_attr_val(elem, "id");
+ s->stream_id = NULL;
+ if (stream_id != NULL) {
+ s->stream_id = strdup(stream_id);
+ }
}
rexmpp_stream_is_ready(s);
} else if (rexmpp_xml_match(elem, "urn:xmpp:sm:3", "failed")) {
s->stream_state = REXMPP_STREAM_SM_ACKS;
s->sm_state = REXMPP_SM_NEGOTIATION;
- xmlNodePtr sm_enable = xmlNewNode(NULL, "enable");
- xmlNewNs(sm_enable, "urn:xmpp:sm:3", NULL);
+ rexmpp_xml_t *sm_enable =
+ rexmpp_xml_new_elem("enable", "urn:xmpp:sm:3");
rexmpp_send(s, sm_enable);
}
} else if (s->stream_state == REXMPP_STREAM_SM_ACKS) {
@@ -2327,8 +2267,8 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
}
} else if (rexmpp_xml_match(elem, "urn:xmpp:sm:3", "failed")) {
s->sm_state = REXMPP_SM_INACTIVE;
- xmlNodePtr sm_enable = xmlNewNode(NULL, "enable");
- xmlNewNs(sm_enable, "urn:xmpp:sm:3", NULL);
+ rexmpp_xml_t *sm_enable =
+ rexmpp_xml_new_elem("enable", "urn:xmpp:sm:3");
rexmpp_send(s, sm_enable);
}
rexmpp_stream_is_ready(s);
@@ -2349,7 +2289,7 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
s->active_iq = next;
rexmpp_iq_finish(s, iq, 0, NULL);
}
- xmlNodePtr child =
+ rexmpp_xml_t *child =
rexmpp_xml_find_child(s->stream_features,
"urn:ietf:params:xml:ns:xmpp-bind",
"bind");
@@ -2371,69 +2311,69 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr 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) {
- xmlNodeAddContentLen(s->current_element, ch, len);
+ 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);
+ char *new_alt_text = realloc(last_node->alt.text, last_len + len + 1);
+ if (new_alt_text == NULL) {
+ rexmpp_log(s, LOG_ERR,
+ "Failed to reallocate the XML element text buffer: %s",
+ strerror(errno));
+ return;
+ }
+ last_node->alt.text = new_alt_text;
+ 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;
-
- 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;
+ rexmpp_xml_attribute_free_list(attributes);
return;
}
if (s->stream_state != REXMPP_STREAM_OPENING) {
if (s->current_element == NULL) {
- s->current_element = xmlNewNode(NULL, localname);
+ s->current_element = rexmpp_xml_new_elem(name, namespace);
s->current_element_root = s->current_element;
} else {
- xmlNodePtr node = xmlNewNode(NULL, localname);
- xmlAddChild(s->current_element, node);
+ 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;
}
- xmlNsPtr ns = xmlNewNs(s->current_element, URI, prefix);
- s->current_element->ns = ns;
- 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);
- xmlNewProp(s->current_element, attributes[i * 5], 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);
@@ -2453,8 +2393,16 @@ void rexmpp_sax_end_elem_ns (rexmpp_t *s,
}
if (s->current_element != s->current_element_root) {
- s->current_element = s->current_element->parent;
+ /* Find the parent, set it as current element. */
+ rexmpp_xml_t *parent = s->current_element_root;
+ while (parent->alt.elem.children != s->current_element) {
+ parent = parent->alt.elem.children;
+ }
+ s->current_element = parent;
} else {
+ /* Done parsing this element; reverse all the lists of children
+ and queue it. */
+ 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;
@@ -2474,12 +2422,16 @@ rexmpp_err_t rexmpp_close (rexmpp_t *s) {
}
rexmpp_err_t rexmpp_stop (rexmpp_t *s) {
- s->stream_state = REXMPP_STREAM_CLOSE_REQUESTED;
if (s->stream_state == REXMPP_STREAM_READY) {
- xmlNodePtr presence = rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence"));
- xmlNewProp(presence, "type", "unavailable");
+ rexmpp_xml_t *presence =
+ rexmpp_xml_new_elem("presence", "jabber:client");
+ rexmpp_xml_add_id(presence);
+ rexmpp_xml_add_attr(presence, "type", "unavailable");
rexmpp_send(s, presence);
}
+
+ s->stream_state = REXMPP_STREAM_CLOSE_REQUESTED;
+
if (s->sm_state == REXMPP_SM_ACTIVE) {
int ret = rexmpp_sm_ack(s);
if (ret > REXMPP_E_AGAIN) {
@@ -2494,8 +2446,8 @@ rexmpp_err_t rexmpp_stop (rexmpp_t *s) {
}
rexmpp_err_t rexmpp_run (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
- struct timeval now;
- if (gettimeofday(&now, NULL) != 0) {
+ struct timespec now;
+ if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) {
rexmpp_log(s, LOG_ERR, "Failed to get time: %s", strerror(errno));
return REXMPP_E_OTHER;
}
@@ -2636,16 +2588,52 @@ rexmpp_err_t rexmpp_run (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
}
}
+ /* MUC self-pinging. */
+ if (s->tcp_state == REXMPP_TCP_CONNECTED &&
+ s->stream_state == REXMPP_STREAM_READY)
+ {
+ rexmpp_muc_ping_t *mp = s->muc_ping;
+ while (mp != NULL) {
+ if (mp->last_activity.tv_sec + mp->delay <= now.tv_sec) {
+ if (mp->requested == 0) {
+ clock_gettime(CLOCK_MONOTONIC, &(mp->last_activity));
+ mp->requested = 1;
+ rexmpp_xml_t *ping_cmd =
+ rexmpp_xml_new_elem("ping", "urn:xmpp:ping");
+ rexmpp_iq_new(s, "get", mp->jid,
+ ping_cmd, rexmpp_muc_pong, mp);
+ } else {
+ /* Requested already, and delay time passed again, without
+ a reply (not even an error). Warn the user, remove this
+ MUC. */
+ char *occupant_jid_to_remove = mp->jid;
+ rexmpp_log(s, LOG_WARNING,
+ "No MUC self-ping reply for %s, "
+ "disabling self-ping for it",
+ mp->jid);
+ mp = mp->next;
+ rexmpp_muc_ping_remove(s, occupant_jid_to_remove);
+ continue;
+ }
+ }
+ mp = mp->next;
+ }
+ }
+
/* Pinging the server. */
if (s->tcp_state == REXMPP_TCP_CONNECTED &&
- s->last_network_activity + s->ping_delay <= time(NULL)) {
+ s->stream_state == REXMPP_STREAM_READY &&
+ s->last_network_activity.tv_sec + s->ping_delay <= now.tv_sec) {
if (s->ping_requested == 0) {
s->ping_requested = 1;
- xmlNodePtr ping_cmd = xmlNewNode(NULL, "ping");
- xmlNewNs(ping_cmd, "urn:xmpp:ping", NULL);
+ rexmpp_xml_t *ping_cmd =
+ rexmpp_xml_new_elem("ping", "urn:xmpp:ping");
rexmpp_iq_new(s, "get", s->initial_jid.domain,
ping_cmd, rexmpp_pong, NULL);
} else {
+ /* Last network activity is updated on sending as well as on
+ receiving, so this will not be triggered right after sending
+ the request. */
rexmpp_log(s, LOG_WARNING, "Ping timeout, reconnecting.");
rexmpp_cleanup(s);
rexmpp_schedule_reconnect(s);
@@ -2694,12 +2682,13 @@ rexmpp_err_t rexmpp_run (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
if (s->tcp_state == REXMPP_TCP_CONNECTED &&
s->stream_state == REXMPP_STREAM_CLOSED &&
s->tls_state == REXMPP_TLS_CLOSING) {
- rexmpp_tls_err_t err = rexmpp_tls_disconnect(s);
+ rexmpp_tls_err_t err = rexmpp_tls_disconnect(s, s->tls);
if (err == REXMPP_TLS_SUCCESS) {
+ rexmpp_log(s, LOG_DEBUG, "TLS disconnected");
s->tls_state = REXMPP_TLS_INACTIVE;
rexmpp_cleanup(s);
s->tcp_state = REXMPP_TCP_CLOSED;
- } else {
+ } else if (err != REXMPP_TLS_E_AGAIN) {
s->tls_state = REXMPP_TLS_ERROR;
return REXMPP_E_TLS;
}
@@ -2771,11 +2760,11 @@ int rexmpp_fds (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
return max_fd;
}
-struct timeval *rexmpp_timeout (rexmpp_t *s,
- struct timeval *max_tv,
- struct timeval *tv)
+struct timespec *rexmpp_timeout (rexmpp_t *s,
+ struct timespec *max_tv,
+ struct timespec *tv)
{
- struct timeval *ret = max_tv;
+ struct timespec *ret = max_tv;
if (s->resolver_state != REXMPP_RESOLVER_NONE &&
s->resolver_state != REXMPP_RESOLVER_READY) {
@@ -2786,36 +2775,55 @@ struct timeval *rexmpp_timeout (rexmpp_t *s,
ret = rexmpp_jingle_timeout(s, ret, tv);
- struct timeval now;
- gettimeofday(&now, NULL);
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
if (s->reconnect_number > 0 &&
s->next_reconnect_time.tv_sec > now.tv_sec &&
(ret == NULL ||
s->next_reconnect_time.tv_sec - now.tv_sec < ret->tv_sec)) {
tv->tv_sec = s->next_reconnect_time.tv_sec - now.tv_sec;
- tv->tv_usec = 0;
+ tv->tv_nsec = 0;
ret = tv;
}
if (s->tcp_state == REXMPP_TCP_CONNECTED &&
- s->last_network_activity + s->ping_delay > now.tv_sec) {
- time_t next_ping = s->last_network_activity + s->ping_delay - now.tv_sec;
+ s->stream_state == REXMPP_STREAM_READY) {
+ rexmpp_muc_ping_t *mp = s->muc_ping;
+ while (mp != NULL) {
+ if (mp->last_activity.tv_sec + mp->delay > now.tv_sec) {
+ time_t next_ping =
+ mp->last_activity.tv_sec + mp->delay - now.tv_sec;
+ if (ret == NULL || next_ping < ret->tv_sec) {
+ tv->tv_sec = next_ping;
+ tv->tv_nsec = 0;
+ ret = tv;
+ }
+ }
+ mp = mp->next;
+ }
+ }
+
+ if (s->tcp_state == REXMPP_TCP_CONNECTED &&
+ s->stream_state == REXMPP_STREAM_READY &&
+ s->last_network_activity.tv_sec + s->ping_delay > now.tv_sec) {
+ time_t next_ping =
+ s->last_network_activity.tv_sec + s->ping_delay - now.tv_sec;
if (ret == NULL || next_ping < ret->tv_sec) {
tv->tv_sec = next_ping;
- tv->tv_usec = 0;
+ tv->tv_nsec = 0;
ret = tv;
}
}
#ifdef HAVE_CURL
- long curl_timeout;
+ long curl_timeout; /* in milliseconds */
curl_multi_timeout(s->curl_multi, &curl_timeout);
if (curl_timeout >= 0 &&
(curl_timeout / 1000 < ret->tv_sec ||
(curl_timeout / 1000 == ret->tv_sec &&
- (curl_timeout % 1000) * 1000 < ret->tv_usec))) {
+ (curl_timeout % 1000) * 1000000 < ret->tv_nsec))) {
tv->tv_sec = curl_timeout / 1000;
- tv->tv_usec = (curl_timeout % 1000) * 1000;
+ tv->tv_nsec = (curl_timeout % 1000) * 1000000;
ret = tv;
}
#endif
diff --git a/src/rexmpp.h b/src/rexmpp.h
index c6d4428..0aa6252 100644
--- a/src/rexmpp.h
+++ b/src/rexmpp.h
@@ -10,10 +10,10 @@
#define REXMPP_H
#include <stdint.h>
+#include <stdbool.h>
#include "config.h"
-#include <libxml/tree.h>
#ifdef HAVE_GPGME
#include <gpgme.h>
#endif
@@ -187,6 +187,8 @@ 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"
@@ -207,8 +209,8 @@ typedef enum rexmpp_err rexmpp_err_t;
*/
typedef void (*rexmpp_iq_callback_t) (rexmpp_t *s,
void *cb_data,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success);
typedef struct rexmpp_iq rexmpp_iq_t;
@@ -217,7 +219,7 @@ typedef struct rexmpp_iq rexmpp_iq_t;
struct rexmpp_iq
{
/** @brief The sent request. */
- xmlNodePtr request;
+ rexmpp_xml_t *request;
/** @brief A callback to call on reply. */
rexmpp_iq_callback_t cb;
/** @brief User-supplied data, to pass to a callback function. */
@@ -226,12 +228,31 @@ struct rexmpp_iq
rexmpp_iq_t *next;
};
+typedef struct rexmpp_muc_ping rexmpp_muc_ping_t;
+
+/** @brief MUC self-ping data. */
+struct rexmpp_muc_ping
+{
+ /** @brief Own occupant JID to ping. */
+ char *jid;
+ /** @brief Optional password to rejoin with. */
+ char *password;
+ /** @brief Ping delay, in seconds. */
+ unsigned int delay;
+ /** @brief Whether a ping is requested (pending) already. */
+ int requested;
+ /** @brief When the MUC was active. */
+ struct timespec last_activity;
+ rexmpp_muc_ping_t *next;
+};
+
typedef void (*log_function_t) (rexmpp_t *s, int priority, const char *format, va_list args);
typedef int (*sasl_property_cb_t) (rexmpp_t *s, rexmpp_sasl_property prop);
-typedef int (*xml_in_cb_t) (rexmpp_t *s, xmlNodePtr node);
-typedef int (*xml_out_cb_t) (rexmpp_t *s, xmlNodePtr node);
-typedef void (*roster_modify_cb_t) (rexmpp_t *s, xmlNodePtr item);
+typedef int (*xml_in_cb_t) (rexmpp_t *s, rexmpp_xml_t *node);
+typedef int (*xml_out_cb_t) (rexmpp_t *s, rexmpp_xml_t *node);
+typedef void (*roster_modify_cb_t) (rexmpp_t *s, rexmpp_xml_t *item);
typedef int (*console_print_cb_t) (rexmpp_t *s, const char *format, va_list args);
+typedef void (*socket_cb_t) (rexmpp_t *s, int socket);
/** @brief Complete connection state */
struct rexmpp
@@ -253,7 +274,7 @@ struct rexmpp
/* Manual host/port configuration. */
const char *manual_host;
uint16_t manual_port;
- int manual_direct_tls;
+ bool manual_direct_tls;
/* Miscellaneous settings */
const char *disco_node;
@@ -263,21 +284,23 @@ struct rexmpp
uint16_t socks_port;
/* Various knobs (these are used instead of loadable modules). */
- int enable_carbons; /* XEP-0280 */
- int manage_roster;
+ bool enable_carbons; /* XEP-0280 */
+ bool manage_roster;
const char *roster_cache_file;
- int track_roster_presence;
- int track_roster_events; /* XEP-0163 */
- int nick_notifications; /* XEP-0172 */
- int retrieve_openpgp_keys; /* XEP-0373 */
- int autojoin_bookmarked_mucs; /* XEP-0402 */
+ bool track_roster_presence;
+ bool track_roster_events; /* XEP-0163 */
+ bool nick_notifications; /* XEP-0172 */
+ bool retrieve_openpgp_keys; /* XEP-0373 */
+ bool autojoin_bookmarked_mucs; /* XEP-0402 */
enum tls_pol tls_policy;
- int enable_jingle;
+ bool enable_jingle;
const char *client_name; /* XEP-0030, XEP-0092 */
const char *client_type; /* XEP-0030 */
const char *client_version; /* XEP-0092 */
const char *local_address; /* For ICE, XEP-0176 */
- int jingle_prefer_rtcp_mux;
+ bool jingle_prefer_rtcp_mux;
+ /* A delay in seconds, to use for MUC self-ping by default */
+ unsigned int muc_ping_default_delay;
/* Resource limits. */
uint32_t stanza_queue_size;
@@ -286,6 +309,12 @@ struct rexmpp
uint32_t iq_cache_size;
uint32_t max_jingle_sessions;
+ /* X.509 settings: for TLS and DTLS, to use for SASL EXTERNAL
+ authentication and DTLS-SRTP on Jingle calls. */
+ const char *x509_key_file;
+ const char *x509_cert_file;
+ const char *x509_trust_file;
+
/* Callbacks. */
log_function_t log_function;
sasl_property_cb_t sasl_property_cb;
@@ -293,44 +322,48 @@ struct rexmpp
xml_out_cb_t xml_out_cb;
roster_modify_cb_t roster_modify_cb;
console_print_cb_t console_print_cb;
+ socket_cb_t socket_cb;
/* Stream-related state. */
struct rexmpp_jid assigned_jid;
- xmlNodePtr stream_features;
- xmlNodePtr roster_items;
+ rexmpp_xml_t *stream_features;
+ rexmpp_xml_t *roster_items;
char *roster_ver;
- xmlNodePtr roster_presence;
- xmlNodePtr roster_events;
+ rexmpp_xml_t *roster_presence;
+ rexmpp_xml_t *roster_events;
/* Other dynamic data. */
- xmlNodePtr disco_info;
+ rexmpp_xml_t *disco_info;
/* Includes Jingle RTP session candidates; rexmpp prioritizes the
ones listed earlier on incoming calls. */
- xmlNodePtr jingle_rtp_description;
+ rexmpp_xml_t *jingle_rtp_description;
/* IQs we're waiting for responses to. */
rexmpp_iq_t *active_iq;
/* Cached IQ requests and responses. */
- xmlNodePtr iq_cache;
+ rexmpp_xml_t *iq_cache;
/* Jingle context. */
- rexmpp_jingle_ctx_t jingle;
+ rexmpp_jingle_ctx_t *jingle;
/* Connection and stream management. */
unsigned int reconnect_number;
time_t reconnect_seconds;
- struct timeval next_reconnect_time;
- xmlNodePtr stanza_queue;
+ struct timespec next_reconnect_time;
+ rexmpp_xml_t *stanza_queue;
uint32_t stanzas_out_count;
uint32_t stanzas_out_acknowledged;
uint32_t stanzas_in_count;
char *stream_id;
/* Server ping configuration and state. */
- int ping_delay;
- int ping_requested;
- time_t last_network_activity;
+ unsigned int ping_delay;
+ bool ping_requested;
+ struct timespec last_network_activity;
+
+ /* MUC self-ping */
+ rexmpp_muc_ping_t *muc_ping;
/* DNS-related structures. */
rexmpp_dns_ctx_t resolver;
@@ -348,7 +381,7 @@ struct rexmpp
int server_socket;
/* Whether the address it's connected to was verified with
DNSSEC. */
- int server_socket_dns_secure;
+ bool server_socket_dns_secure;
/* A structure used to establish a TCP connection. */
rexmpp_tcp_conn_t server_connection;
@@ -359,34 +392,42 @@ 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. */
- xmlNodePtr send_queue;
+ rexmpp_xml_t *send_queue;
+
+ /* An input queue of parsed XML structures. */
+ rexmpp_xml_t *input_queue;
+ rexmpp_xml_t *input_queue_last;
/* XML parser context, and current element pointer for building
XML nodes with a SAX2 parser interface. */
- xmlParserCtxtPtr xml_parser;
- xmlNodePtr current_element_root;
- xmlNodePtr current_element;
- xmlNodePtr input_queue;
- xmlNodePtr input_queue_last;
+ rexmpp_xml_parser_ctx_t xml_parser;
+
+ /* The children are stored in reverse order during building. */
+ rexmpp_xml_t *current_element_root;
+ rexmpp_xml_t *current_element;
/* TLS structures. */
- rexmpp_tls_t tls;
+ rexmpp_tls_t *tls;
/* SASL structures. */
- rexmpp_sasl_ctx_t sasl;
+ rexmpp_sasl_ctx_t *sasl;
/* OpenPGP structures */
#ifdef HAVE_GPGME
gpgme_ctx_t pgp_ctx;
+#else
+ void *pgp_ctx;
#endif
/* curl structures */
#ifdef HAVE_CURL
CURLM *curl_multi;
+#else
+ void *curl_multi;
#endif
};
@@ -429,7 +470,7 @@ rexmpp_err_t rexmpp_stop (rexmpp_t *s);
@param[in] node An XML element to send. The library assumes
ownership of the element, so it must not be freed by the caller.
*/
-rexmpp_err_t rexmpp_send (rexmpp_t *s, xmlNodePtr node);
+rexmpp_err_t rexmpp_send (rexmpp_t *s, rexmpp_xml_t *node);
/**
@brief Prepare and send a new info/query request.
@@ -448,7 +489,7 @@ rexmpp_err_t rexmpp_send (rexmpp_t *s, xmlNodePtr node);
rexmpp_err_t rexmpp_iq_new (rexmpp_t *s,
const char *type,
const char *to,
- xmlNodePtr payload,
+ rexmpp_xml_t *payload,
rexmpp_iq_callback_t cb,
void *cb_data);
@@ -460,7 +501,7 @@ rexmpp_err_t rexmpp_iq_new (rexmpp_t *s,
rexmpp_err_t rexmpp_cached_iq_new (rexmpp_t *s,
const char *type,
const char *to,
- xmlNodePtr payload,
+ rexmpp_xml_t *payload,
rexmpp_iq_callback_t cb,
void *cb_data,
int fresh);
@@ -469,9 +510,9 @@ rexmpp_err_t rexmpp_cached_iq_new (rexmpp_t *s,
@brief Reply to an IQ.
*/
void rexmpp_iq_reply (rexmpp_t *s,
- xmlNodePtr req,
+ rexmpp_xml_t *req,
const char *type,
- xmlNodePtr payload);
+ rexmpp_xml_t *payload);
/**
@brief Determines the maximum time to wait before the next
@@ -479,13 +520,13 @@ void rexmpp_iq_reply (rexmpp_t *s,
@param[in] s ::rexmpp
@param[in] max_tv An existing timeout (can be NULL), to return if
there's no more urgent timeouts.
- @param[in,out] tv An allocated timeval structure, to store the time
- in.
+ @param[in,out] tv An allocated timespec structure, to store the
+ time in.
@returns A pointer to either max_tv or tv.
*/
-struct timeval *rexmpp_timeout (rexmpp_t *s,
- struct timeval *max_tv,
- struct timeval *tv);
+struct timespec *rexmpp_timeout (rexmpp_t *s,
+ struct timespec *max_tv,
+ struct timespec *tv);
/**
@brief Sets file descriptors to watch.
@@ -500,35 +541,6 @@ struct timeval *rexmpp_timeout (rexmpp_t *s,
int rexmpp_fds (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds);
/**
- @brief Compose an 'error' element.
-*/
-xmlNodePtr rexmpp_xml_error (const char *type, const char *condition);
-
-/**
- @brief A helper function for XML parsing.
- @param[in] str A string to parse.
- @param[in] str_len String length.
- @returns Parsed XML, or NULL on failure.
-*/
-xmlNodePtr rexmpp_xml_parse (const char *str, int str_len);
-
-/**
- @brief A helper function for XML serialisation.
- @param[in] node An XML node.
- @returns A string (must be freed by the caller).
-*/
-char *rexmpp_xml_serialize (xmlNodePtr node);
-
-/**
- @brief Adds an "id" attribute to an XML stanza.
- @param[in,out] s ::rexmpp
- @param[in] node A pointer to an XML stanza.
- @returns The same pointer as on input, for more convenient
- composition.
-*/
-xmlNodePtr rexmpp_xml_add_id (rexmpp_t *s, xmlNodePtr node);
-
-/**
@brief The logging function.
@param[in] s ::rexmpp
@param[in] priority A syslog priority.
@@ -547,42 +559,6 @@ void rexmpp_log (rexmpp_t *s, int priority, const char *format, ...);
char *rexmpp_get_name (rexmpp_t *s, const char *jid_str);
/**
- @brief Compares two XML elements.
-*/
-int rexmpp_xml_eq (xmlNodePtr n1, xmlNodePtr n2);
-
-/**
- @brief Matches an XML node against a namespace and an element name.
- @param[in] node An XML node to match.
- @param[in] namespace An XML namespace. Can be NULL (matches
- anything), and it is assumed that the default namespace is
- "jabber:client" (so if it is "jabber:client" and an element doesn't
- have a namespace defined, this function counts that as a match).
- @param[in] name Element name. Can be NULL (matches anything).
- @returns 1 on a successful match, 0 otherwise.
-*/
-int rexmpp_xml_match (xmlNodePtr node,
- const char *namespace,
- const char *name);
-
-/**
- @brief Finds a child element of an XML node, which matches the
- given namespace and name.
- @param[in] node The node containing child nodes.
- @param[in] namespace The namespace to look for.
- @param[in] name The element name to look for.
- @returns A pointer to the first matching child node, or NULL if no
- matching child elements found.
-*/
-xmlNodePtr rexmpp_xml_find_child (xmlNodePtr node,
- const char *namespace,
- const char *name);
-
-xmlNodePtr rexmpp_xml_new_node (const char *name, const char *namespace);
-
-char *rexmpp_gen_id (rexmpp_t *s);
-
-/**
@brief Finds a PEP event.
@param[in] s ::rexmpp
@param[in] from JID.
@@ -591,10 +567,10 @@ char *rexmpp_gen_id (rexmpp_t *s);
@returns A pointer to the message announcing an event, or NULL on
failure.
*/
-xmlNodePtr rexmpp_find_event (rexmpp_t *s,
- const char *from,
- const char *node,
- xmlNodePtr *prev_event);
+rexmpp_xml_t *rexmpp_find_event (rexmpp_t *s,
+ const char *from,
+ const char *node,
+ rexmpp_xml_t **prev_event);
void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len);
@@ -632,4 +608,43 @@ rexmpp_disco_find_feature (rexmpp_t *s,
int fresh,
int max_requests);
+/**
+ @brief Add a MUC JID to self-ping
+ @param[in,out] s ::rexmpp
+ @param[in] jid Own occupant JID to ping
+ @param[in] password Optional password to rejoin with
+ @param[in] delay How often to ping, in seconds
+*/
+rexmpp_err_t rexmpp_muc_ping_set (rexmpp_t *s,
+ const char *occupant_jid,
+ const char *password,
+ unsigned int delay);
+
+/**
+ @brief Remove a MUC JID to self-ping
+ @param[in,out] s ::rexmpp
+ @param[in] jid Own occupant JID
+*/
+rexmpp_err_t rexmpp_muc_ping_remove (rexmpp_t *s,
+ const char *occupant_jid);
+
+/**
+ @brief Join a MUC, optionally setting self-ping
+ @param[in,out] s ::rexmpp
+ @param[in] occupant_jid Occupant JID
+ @param[in] password Optional password
+ @param[in] ping_delay MUC self-ping delay, 0 to not set it
+*/
+rexmpp_err_t rexmpp_muc_join (rexmpp_t *s,
+ const char *occupant_jid,
+ const char *password,
+ unsigned int ping_delay);
+
+/**
+ @brief Leave a MUC, stop self-pinging it
+ @param[in,out] s ::rexmpp
+ @param[in] occupant_jid Occupant JID
+*/
+rexmpp_err_t rexmpp_muc_leave (rexmpp_t *s, const char *occupant_jid);
+
#endif
diff --git a/src/rexmpp.rs b/src/rexmpp.rs
new file mode 100644
index 0000000..250da00
--- /dev/null
+++ b/src/rexmpp.rs
@@ -0,0 +1,280 @@
+extern crate libc;
+use std::os::raw::{c_char, c_int, c_void, c_uint};
+use libc::{time_t, timespec};
+
+use super::{rexmpp_jid, rexmpp_xml, rexmpp_dns, rexmpp_tcp, rexmpp_socks};
+
+#[derive(PartialEq)]
+#[repr(C)]
+pub enum ResolverState {
+ Ready,
+ SRV,
+ SRV2,
+ Failure
+}
+
+#[derive(PartialEq)]
+#[repr(C)]
+pub enum TCPState {
+ None,
+ Connecting,
+ SOCKS,
+ Connected,
+ Closed,
+ ConnectionFailure,
+ Error
+}
+
+#[derive(PartialEq)]
+#[repr(C)]
+pub enum StreamState {
+ None,
+ Opening,
+ StartTLS,
+ SASL,
+ Bind,
+ SMFull,
+ SMAcks,
+ SMResume,
+ Ready,
+ CloseRequested,
+ Closing,
+ Closed,
+ Error,
+ ErrorReconnect
+}
+
+#[derive(PartialEq)]
+#[repr(C)]
+pub enum TLSState {
+ Inactive,
+ AwaitingDirect,
+ Handshake,
+ Active,
+ Closing,
+ Closed,
+ Error
+}
+
+#[derive(PartialEq)]
+#[repr(C)]
+pub enum SASLState {
+ Inactive,
+ Negotiation,
+ Active,
+ Error
+}
+
+#[derive(PartialEq)]
+#[repr(C)]
+pub enum SMState {
+ Inactive,
+ Negotiation,
+ Active
+}
+
+#[derive(PartialEq)]
+#[repr(C)]
+pub enum CarbonsState {
+ Inactive,
+ Negotiation,
+ Active
+}
+
+#[derive(PartialEq)]
+#[repr(C)]
+pub enum TLSPolicy {
+ Require,
+ Prefer,
+ Avoid
+}
+
+type IQCallback = unsafe extern "C"
+fn (s: *mut Rexmpp, cb_data: *mut c_void,
+ request: *mut rexmpp_xml::RexmppXML, response: *mut rexmpp_xml::RexmppXML,
+ success: c_int) -> ();
+
+type SocketCallback = unsafe extern "C"
+fn (s: *mut Rexmpp, socket: c_int) -> ();
+
+#[repr(C)]
+pub struct RexmppIQ {
+ pub requset: *mut rexmpp_xml::RexmppXML,
+ pub cb: IQCallback,
+ pub cb_data: *const c_void,
+ pub next: *mut RexmppIQ
+}
+
+#[repr(C)]
+pub struct RexmppMUCPing {
+ pub jid: *mut c_char,
+ pub password: *mut c_char,
+ pub delay: c_uint,
+ pub requested: bool,
+ pub last_activity: timespec,
+ pub next: *mut RexmppMUCPing
+}
+
+#[repr(C)]
+pub struct Rexmpp {
+ pub resolver_state: ResolverState,
+ pub tcp_state: TCPState,
+ pub stream_state: StreamState,
+ pub tls_state: TLSState,
+ pub sasl_state: SASLState,
+ pub sm_state: SMState,
+ pub carbons_state: CarbonsState,
+
+ // Basic configuration
+ pub initial_jid: rexmpp_jid::RexmppJID,
+
+ // Manual host/port configuration
+ pub manual_host: *const c_char,
+ pub manual_port: u16,
+ pub manual_direct_tls: bool,
+
+ // Miscellaneous settings
+ pub disco_node: *const c_char,
+
+ // SOCKS settings
+ pub socks_host: *const c_char,
+ pub socks_port: u16,
+
+ // Various knobs (these are used instead of loadable modules)
+ pub enable_carbons: bool, // XEP-0280
+ pub manage_roster: bool,
+ pub roster_cache_file: *const c_char,
+ pub track_roster_presence: bool,
+ pub track_roster_events: bool, // XEP-0163
+ pub nick_notifications: bool, // XEP-0172
+ pub retrieve_openpgp_keys: bool, // XEP-0373
+ pub autojoin_bookmarked_mucs: bool, // XEP-0402
+ pub tls_policy: TLSPolicy,
+ pub enable_jingle: bool,
+ pub client_name: *const c_char, // XEP-0030, XEP-0092
+ pub client_type: *const c_char, // XEP-0030
+ pub client_version: *const c_char, // XEP-0092
+ pub local_address: *const c_char, // For ICE, XEP-0176
+ pub jingle_prefer_rtcp_mux: bool,
+ // A delay in seconds, to use for MUC self-ping by default
+ pub muc_ping_default_delay: c_uint,
+ // Resource limits
+ pub stanza_queue_size: u32,
+ pub send_queue_size: u32,
+ pub iq_queue_size: u32,
+ pub iq_cache_size: u32,
+ pub max_jingle_sessions: u32,
+
+ // X.509 settings (for TLS and DTLS)
+ pub x509_key_file: *const c_char,
+ pub x509_cert_file: *const c_char,
+ pub x509_trust_file: *const c_char,
+
+ // Callbacks
+
+ // c_variadic is experimental and cannot be used on the stable
+ // release channel, so skipping the log function callback.
+ pub log_function: *const c_void,
+ // Actually skipping proper definitions of others for now as well
+ // (TODO: add them).
+ pub sasl_property_cb: *const c_void,
+ pub xml_in_cb: *const c_void,
+ pub xml_out_cb: *const c_void,
+ pub roster_modify_cb: *const c_void,
+ pub console_print_cb: *const c_void,
+ pub socket_cb: Option<SocketCallback>,
+
+ // Stream-related state
+ pub assigned_jid: rexmpp_jid::RexmppJID,
+ pub stream_features: *mut rexmpp_xml::RexmppXML,
+ pub roster_items: *mut rexmpp_xml::RexmppXML,
+ pub roster_ver: *mut c_char,
+ pub roster_presence: *mut rexmpp_xml::RexmppXML,
+ pub roster_events: *mut rexmpp_xml::RexmppXML,
+
+ // Other dynamic data
+ pub disco_info: *mut rexmpp_xml::RexmppXML,
+ // Includes Jingle RTP session candidates; rexmpp prioritizes the
+ // ones listed earlier on incoming calls
+ pub jingle_rtp_description: *mut rexmpp_xml::RexmppXML,
+
+ // IQs we're waiting for responses to
+ pub active_iq: *mut RexmppIQ,
+ pub iq_cache: *mut rexmpp_xml::RexmppXML,
+
+ // Jingle context
+ pub jingle: *const c_void, // TODO
+
+ // Connection and stream management
+ pub reconnect_number: c_uint,
+ pub reconnect_seconds: time_t,
+ pub next_reconnect_time: timespec,
+ pub stanza_queue: *mut rexmpp_xml::RexmppXML,
+ pub stanzas_out_count: u32,
+ pub stanzas_out_acknowledged: u32,
+ pub stanzas_in_count: u32,
+ pub stream_id: *mut c_char,
+
+ // Server ping configuration and state
+ pub ping_delay: c_uint,
+ pub ping_requested: bool,
+ pub last_network_activity: timespec,
+
+ // MUC self-ping
+ pub muc_ping: *mut RexmppMUCPing,
+
+ // DNS-related structures
+ pub resolver: *mut c_void,
+ pub server_srv: *mut rexmpp_dns::RexmppDNSResult,
+ pub server_srv_cur: c_int,
+ pub server_srv_tls: *mut rexmpp_dns::RexmppDNSResult,
+ pub server_srv_tls_cur: c_int,
+ pub server_active_srv: *mut rexmpp_dns::RexmppDNSSRV,
+
+ // The XMPP server we are connecting to
+ pub server_host: *const c_char,
+ pub server_port: u16,
+
+ // The primary socket used for communication with the server
+ pub server_socket: c_int,
+ // Whether the address it's connected to was verified with DNSSEC
+ pub server_socket_dns_secure: bool,
+
+ // A structure used to establish a TCP connection
+ pub server_connection: rexmpp_tcp::RexmppTCPConnection,
+ pub server_socks_conn: rexmpp_socks::RexmppSocks,
+
+ // Send buffer. NULL if there is nothing to send (and must not be
+ // NULL if there is anything in the send queue). Not appending
+ // data to it, see send_queue for queuing.
+ pub send_buffer: *mut c_char,
+ pub send_buffer_len: isize,
+ pub send_buffer_sent: isize,
+
+ // A queue of XML elements to send
+ pub send_queue: *mut rexmpp_xml::RexmppXML,
+
+ // An input queue of parsed XML structures
+ pub input_queue: *mut rexmpp_xml::RexmppXML,
+ pub input_queue_last: *mut rexmpp_xml::RexmppXML,
+
+ // XML parser context, and current element pointer for building
+ // XML nodes with a SAX2 parser interface
+ pub xml_parser: *mut c_void,
+
+ // The children are stored in reverse order during building
+ pub current_element_root: *mut rexmpp_xml::RexmppXML,
+ pub current_element: *mut rexmpp_xml::RexmppXML,
+
+ // TLS structures
+ pub tls: *mut c_void,
+
+ // SASL structures
+ pub sasl: *mut c_void,
+
+ // OpenPGP structures
+ pub pgp_ctx: *mut c_void,
+
+ // curl structures
+ pub curl_multi: *mut c_void
+}
diff --git a/src/rexmpp_base64.h b/src/rexmpp_base64.h
index e681ed9..d11cd91 100644
--- a/src/rexmpp_base64.h
+++ b/src/rexmpp_base64.h
@@ -10,5 +10,26 @@
#include <stddef.h>
-int rexmpp_base64_to (const char *in, size_t in_len, char **out, size_t *out_len);
-int rexmpp_base64_from (const char *in, size_t in_len, char **out, size_t *out_len);
+/**
+ @brief Encodes data in Base64
+ @param[in] in Data to encode
+ @param[in] in_len Length of the input data
+ @param[out] out A pointer to the output buffer; its memory will be
+ allocated by the function, the caller receives ownership over it
+ @param[out] out_len Length of the produced Base64-encoded string
+ @returns 0 on success, a non-zero value otherwise
+*/
+int rexmpp_base64_to (const char *in, size_t in_len,
+ char **out, size_t *out_len);
+
+/**
+ @brief Decodes data from Base64
+ @param[in] in Data to decode
+ @param[in] in_len Length of the input data
+ @param[out] out A pointer to the output buffer; its memory will be
+ allocated by the function, the caller receives ownership over it
+ @param[out] out_len Length of the decoded string
+ @returns 0 on success, a non-zero value otherwise
+*/
+int rexmpp_base64_from (const char *in, size_t in_len,
+ char **out, size_t *out_len);
diff --git a/src/rexmpp_console.c b/src/rexmpp_console.c
index f2d748f..a6f859a 100644
--- a/src/rexmpp_console.c
+++ b/src/rexmpp_console.c
@@ -11,11 +11,15 @@
*/
#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
#include "rexmpp.h"
+#include "rexmpp_xml.h"
#include "rexmpp_openpgp.h"
#include "rexmpp_http_upload.h"
#include "rexmpp_jingle.h"
+#include "rexmpp_pubsub.h"
#include "rexmpp_console.h"
@@ -29,61 +33,60 @@ void rexmpp_console_printf (rexmpp_t *s, const char *format, ...)
}
}
-char *rexmpp_console_message_string (rexmpp_t *s, xmlNodePtr node) {
- char *ret = NULL;
- xmlNodePtr openpgp =
+const char *rexmpp_console_message_string (rexmpp_t *s, rexmpp_xml_t *node) {
+ const char *ret = NULL;
+ rexmpp_xml_t *openpgp =
rexmpp_xml_find_child(node, "urn:xmpp:openpgp:0", "openpgp");
if (openpgp != NULL) {
int valid;
- xmlNodePtr elem = rexmpp_openpgp_decrypt_verify_message(s, node, &valid);
+ rexmpp_xml_t *elem = rexmpp_openpgp_decrypt_verify_message(s, node, &valid);
if (! valid) {
rexmpp_console_printf(s, "An invalid OpenPGP message!\n");
}
if (elem != NULL) {
- xmlNodePtr payload =
+ rexmpp_xml_t *payload =
rexmpp_xml_find_child(elem, "urn:xmpp:openpgp:0", "payload");
if (payload != NULL) {
- xmlNodePtr pl_body =
+ rexmpp_xml_t *pl_body =
rexmpp_xml_find_child(payload, "jabber:client", "body");
if (pl_body != NULL) {
- ret = xmlNodeGetContent(pl_body);
+ ret = rexmpp_xml_text_child(pl_body);
}
}
- xmlFreeNode(elem);
+ rexmpp_xml_free(elem);
}
}
if (ret == NULL) {
- xmlNodePtr body = rexmpp_xml_find_child(node, "jabber:client", "body");
- ret = xmlNodeGetContent(body);
+ rexmpp_xml_t *body =
+ rexmpp_xml_find_child(node, "jabber:client", "body");
+ ret = rexmpp_xml_text_child(body);
}
return ret;
}
-void rexmpp_console_on_send (rexmpp_t *s, xmlNodePtr node) {
+void rexmpp_console_on_send (rexmpp_t *s, rexmpp_xml_t *node) {
if (rexmpp_xml_match(node, "jabber:client", "message")) {
- char *to = xmlGetProp(node, "to");
+ const char *to = rexmpp_xml_find_attr_val(node, "to");
if (to != NULL) {
/* "from" should be set for verification. */
- char *from = xmlGetProp(node, "from");
- xmlAttrPtr fromProp = NULL;
- if (from == NULL) {
- fromProp = xmlNewProp(node, "from", to);
+ int added_from = 0;
+ if (rexmpp_xml_find_attr_val(node, "from") == NULL) {
+ rexmpp_xml_add_attr(node, "from", to);
+ added_from = 1;
}
- char *str = rexmpp_console_message_string(s, node);
- if (fromProp != NULL) {
- xmlRemoveProp(fromProp);
+ const char *str = rexmpp_console_message_string(s, node);
+ if (added_from) {
+ rexmpp_xml_remove_attr(node, "from");
}
if (str != NULL) {
rexmpp_console_printf(s, "You tell %s: %s\n", to, str);
- free(str);
}
- free(to);
}
}
if (rexmpp_xml_match(node, "jabber:client", "presence")) {
- char *presence_type = xmlGetProp(node, "type");
- char *presence_to = xmlGetProp(node, "to");
+ const char *presence_type = rexmpp_xml_find_attr_val(node, "type");
+ const char *presence_to = rexmpp_xml_find_attr_val(node, "to");
if (presence_to == NULL) {
rexmpp_console_printf(s, "Becoming %s\n",
(presence_type == NULL) ?
@@ -105,72 +108,58 @@ void rexmpp_console_on_send (rexmpp_t *s, xmlNodePtr node) {
"Denying %s's presence subscription request.\n",
presence_to);
}
- free(presence_to);
- }
- if (presence_type != NULL) {
- free(presence_type);
}
}
}
-void rexmpp_console_on_recv (rexmpp_t *s, xmlNodePtr node) {
+void rexmpp_console_on_recv (rexmpp_t *s, rexmpp_xml_t *node) {
if (rexmpp_xml_match(node, "jabber:client", "message")) {
- xmlNodePtr sent = rexmpp_xml_find_child(node, "urn:xmpp:carbons:2", "sent");
+ rexmpp_xml_t *sent = rexmpp_xml_find_child(node, "urn:xmpp:carbons:2", "sent");
if (sent != NULL) {
- xmlNodePtr fwd =
+ rexmpp_xml_t *fwd =
rexmpp_xml_find_child(sent, "urn:xmpp:forward:0", "forwarded");
if (fwd != NULL) {
- xmlNodePtr msg =
+ rexmpp_xml_t *msg =
rexmpp_xml_find_child(fwd, "jabber:client", "message");
if (msg != NULL) {
- char *to = xmlGetProp(msg, "to");
- char *str = rexmpp_console_message_string(s, msg);
+ const char *to = rexmpp_xml_find_attr_val(msg, "to");
+ const char *str = rexmpp_console_message_string(s, msg);
if (str != NULL) {
rexmpp_console_printf(s, "You tell %s: %s\n", to, str);
- free(str);
- }
- if (to != NULL) {
- free(to);
}
}
}
}
- xmlNodePtr received =
+ rexmpp_xml_t *received =
rexmpp_xml_find_child(node, "urn:xmpp:carbons:2", "received");
if (received != NULL) {
- xmlNodePtr fwd =
+ rexmpp_xml_t *fwd =
rexmpp_xml_find_child(received, "urn:xmpp:forward:0", "forwarded");
if (fwd != NULL) {
- xmlNodePtr msg =
+ rexmpp_xml_t *msg =
rexmpp_xml_find_child(fwd, "jabber:client", "message");
if (msg != NULL) {
- char *from = xmlGetProp(msg, "from");
- char *str = rexmpp_console_message_string(s, msg);
+ const char *from = rexmpp_xml_find_attr_val(msg, "from");
+ const char *str = rexmpp_console_message_string(s, msg);
if (str != NULL) {
rexmpp_console_printf(s, "%s tells you: %s\n", from, str);
- free(str);
- }
- if (from != NULL) {
- free(from);
}
}
}
}
- char *from = xmlGetProp(node, "from");
+ const char *from = rexmpp_xml_find_attr_val(node, "from");
if (from != NULL) {
- char *str = rexmpp_console_message_string(s, node);
+ const char *str = rexmpp_console_message_string(s, node);
if (str != NULL) {
rexmpp_console_printf(s, "%s tells you: %s\n", from, str);
- free(str);
}
- free(from);
}
}
if (rexmpp_xml_match(node, "jabber:client", "presence")) {
- char *presence_type = xmlGetProp(node, "type");
- char *from = xmlGetProp(node, "from");
+ const char *presence_type = rexmpp_xml_find_attr_val(node, "type");
+ const char *from = rexmpp_xml_find_attr_val(node, "from");
if (presence_type != NULL && ! strcmp(presence_type, "subscribe")) {
rexmpp_console_printf(s, "%s requests a presence subscription\n", from);
} else if (presence_type != NULL && ! strcmp(presence_type, "subscribed")) {
@@ -182,74 +171,64 @@ void rexmpp_console_on_recv (rexmpp_t *s, xmlNodePtr node) {
(presence_type == NULL) ?
"available" :
presence_type);
- xmlNodePtr show = rexmpp_xml_find_child(node, "jabber:client", "show");
+ rexmpp_xml_t *show =
+ rexmpp_xml_find_child(node, "jabber:client", "show");
if (show != NULL) {
- char *show_str = xmlNodeGetContent(show);
- rexmpp_console_printf(s, " (%s)", show_str);
- free(show_str);
- show_str = NULL;
+ rexmpp_console_printf(s, " (%s)",
+ rexmpp_xml_text_child(show));
}
- xmlNodePtr status = rexmpp_xml_find_child(node, "jabber:client", "status");
+ rexmpp_xml_t *status =
+ rexmpp_xml_find_child(node, "jabber:client", "status");
if (status != NULL) {
- char *status_str = xmlNodeGetContent(status);
- rexmpp_console_printf(s, ": %s", status_str);
- free(status_str);
- status_str = NULL;
+ rexmpp_console_printf(s, ": %s",
+ rexmpp_xml_text_child(status));
}
rexmpp_console_printf(s, "\n");
}
- if (presence_type != NULL) {
- free(presence_type);
- }
- if (from != NULL) {
- free(from);
- }
}
}
void rexmpp_console_roster_deleted (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)ptr;
(void)response;
- xmlNodePtr item =
+ rexmpp_xml_t *item =
rexmpp_xml_find_child(rexmpp_xml_find_child(req,
"jabber:iq:roster",
"query"),
"jabber:iq:roster", "item");
- char *jid = xmlGetProp(item, "jid");
+ const char *jid = rexmpp_xml_find_attr_val(item, "jid");
if (success) {
rexmpp_console_printf(s, "Deleted %s from the roster.\n", jid);
} else {
rexmpp_console_printf(s, "Failed to delete %s from the roster.\n", jid);
}
- free(jid);
}
void rexmpp_console_roster_added (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)ptr;
(void)response;
- xmlNodePtr item =
+ rexmpp_xml_t *item =
rexmpp_xml_find_child(rexmpp_xml_find_child(req,
"jabber:iq:roster",
"query"),
"jabber:iq:roster", "item");
- char *jid = xmlGetProp(item, "jid");
+ const char *jid = rexmpp_xml_find_attr_val(item, "jid");
if (success) {
rexmpp_console_printf(s, "Added %s into the roster.\n", jid);
} else {
rexmpp_console_printf(s, "Failed to add %s into the roster.\n", jid);
}
- free(jid);
}
void rexmpp_console_on_run (rexmpp_t *s, rexmpp_err_t result) {
@@ -269,11 +248,210 @@ void rexmpp_console_on_upload (rexmpp_t *s, void *cb_data, const char *url) {
free(fpath);
}
+void rexmpp_console_disco_info (rexmpp_t *s,
+ void *ptr,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
+ int success)
+{
+ (void)ptr;
+ (void)req;
+ if (! success) {
+ rexmpp_console_printf(s, "Failed to discover info.\n");
+ return;
+ }
+ rexmpp_xml_t *query =
+ rexmpp_xml_find_child(response, "http://jabber.org/protocol/disco#info",
+ "query");
+ if (query == NULL) {
+ rexmpp_console_printf(s, "No disco#info query in response.\n");
+ return;
+ }
+ const char *from = rexmpp_xml_find_attr_val(response, "from");
+ if (from == NULL) {
+ rexmpp_console_printf(s, "No 'from' property in response.\n");
+ return;
+ }
+ rexmpp_console_printf(s, "Discovered info for %s:\n", from);
+ rexmpp_xml_t *child = rexmpp_xml_first_elem_child(query);
+ while (child != NULL) {
+ if (rexmpp_xml_match(child, "http://jabber.org/protocol/disco#info",
+ "feature")) {
+ const char *var = rexmpp_xml_find_attr_val(child, "var");
+ rexmpp_console_printf(s, "- feature var %s\n", var);
+ } else if (rexmpp_xml_match(child, "http://jabber.org/protocol/disco#info",
+ "identity")) {
+ const char *category = rexmpp_xml_find_attr_val(child, "category");
+ const char *type = rexmpp_xml_find_attr_val(child, "type");
+ const char *name = rexmpp_xml_find_attr_val(child, "name");
+ rexmpp_console_printf(s, "- identity name %s, type %s, category %s\n",
+ name, type, category);
+ } else {
+ rexmpp_console_printf(s, "Encountered an unknown disco#info element.\n");
+ }
+ child = rexmpp_xml_next_elem_sibling(child);
+ }
+ rexmpp_console_printf(s, "(end of discovered info for %s)\n", from);
+}
+
+void rexmpp_console_disco_items (rexmpp_t *s,
+ void *ptr,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
+ int success)
+{
+ (void)ptr;
+ (void)req;
+ if (! success) {
+ rexmpp_console_printf(s, "Failed to discover items.\n");
+ return;
+ }
+ rexmpp_xml_t *query =
+ rexmpp_xml_find_child(response, "http://jabber.org/protocol/disco#items",
+ "query");
+ if (query == NULL) {
+ rexmpp_console_printf(s, "No disco#items query in response.\n");
+ return;
+ }
+ const char *from = rexmpp_xml_find_attr_val(response, "from");
+ if (from == NULL) {
+ rexmpp_console_printf(s, "No 'from' property in response.\n");
+ return;
+ }
+ rexmpp_console_printf(s, "Discovered items for %s:\n", from);
+ rexmpp_xml_t *child = rexmpp_xml_first_elem_child(query);
+ while (child != NULL) {
+ if (rexmpp_xml_match(child, "http://jabber.org/protocol/disco#items",
+ "item")) {
+ const char *jid = rexmpp_xml_find_attr_val(child, "jid");
+ const char *name = rexmpp_xml_find_attr_val(child, "name");
+ const char *node = rexmpp_xml_find_attr_val(child, "node");
+ rexmpp_console_printf(s, "- item jid %s, name %s, node %s\n", jid, name, node);
+ } else {
+ rexmpp_console_printf(s, "Encountered an unknown disco#items element.\n");
+ }
+ child = rexmpp_xml_next_elem_sibling(child);
+ }
+ rexmpp_console_printf(s, "(end of discovered items for %s)\n", from);
+}
+
+void rexmpp_console_pubsub_node_deleted (rexmpp_t *s,
+ void *ptr,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
+ int success)
+{
+ (void)ptr;
+ (void)req;
+ (void)response;
+ if (success) {
+ rexmpp_console_printf(s, "Deleted the pubsub node.\n");
+ } else {
+ rexmpp_console_printf(s, "Failed to delete the pubsub node.\n");
+ }
+}
+
+void rexmpp_bookmark_added (rexmpp_t *s,
+ void *ptr,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
+ int success)
+{
+ (void)ptr;
+ (void)req;
+ (void)response;
+ if (success) {
+ rexmpp_console_printf(s, "Added a bookmark.\n");
+ } else {
+ rexmpp_console_printf(s, "Failed to add a bookmark.\n");
+ }
+}
+
+void rexmpp_bookmark_removed (rexmpp_t *s,
+ void *ptr,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
+ int success)
+{
+ (void)ptr;
+ (void)req;
+ (void)response;
+ if (success) {
+ rexmpp_console_printf(s, "Removed a bookmark.\n");
+ } else {
+ rexmpp_console_printf(s, "Failed to remove a bookmark.\n");
+ }
+}
+
+void rexmpp_console_blocklist (rexmpp_t *s,
+ void *ptr,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
+ int success)
+{
+ (void)ptr;
+ (void)req;
+ if (success) {
+ rexmpp_xml_t *bl =
+ rexmpp_xml_find_child(response, "urn:xmpp:blocking", "blocklist");
+ if (bl == NULL) {
+ rexmpp_console_printf(s, "No blocklist element in the response.\n");
+ return;
+ }
+ rexmpp_console_printf(s, "Block list:");
+ rexmpp_xml_t *child = rexmpp_xml_first_elem_child(bl);
+ while (child != NULL) {
+ if (rexmpp_xml_match(child, "urn:xmpp:blocking", "item")) {
+ const char *jid = rexmpp_xml_find_attr_val(child, "jid");
+ rexmpp_console_printf(s, " %s", jid);
+ } else {
+ rexmpp_console_printf(s, "Encountered an unknown blocklist child element.\n");
+ }
+ child = rexmpp_xml_next_elem_sibling(child);
+ }
+ rexmpp_console_printf(s, "\n");
+ } else {
+ rexmpp_console_printf(s, "Failed to retrieve block list.\n");
+ }
+}
+
+void rexmpp_console_blocklist_blocked (rexmpp_t *s,
+ void *ptr,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
+ int success)
+{
+ (void)ptr;
+ (void)req;
+ (void)response;
+ if (success) {
+ rexmpp_console_printf(s, "Blocklisted successfully.\n");
+ } else {
+ rexmpp_console_printf(s, "Failed to blocklist.\n");
+ }
+}
+
+void rexmpp_console_blocklist_unblocked (rexmpp_t *s,
+ void *ptr,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
+ int success)
+{
+ (void)ptr;
+ (void)req;
+ (void)response;
+ if (success) {
+ rexmpp_console_printf(s, "Un-blocklisted successfully.\n");
+ } else {
+ rexmpp_console_printf(s, "Failed to un-blocklist.\n");
+ }
+}
+
void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
/* todo: buffering */
(void)str_len; /* Unused for now (todo). */
char *words_save_ptr;
- xmlNodePtr presence;
+ rexmpp_xml_t *presence;
char *word, *jid_str, *msg_text;
struct rexmpp_jid jid;
word = strtok_r(str, " ", &words_save_ptr);
@@ -294,6 +472,8 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
"muc join <conference> [as] <nick>\n"
"muc leave <conference> [as] <nick>\n"
"muc tell <conference> <message>\n"
+ "bookmark add <conference> [autojoin] [nick] [password]\n"
+ "bookmark remove <conference>\n"
"roster list\n"
"roster add <jid>\n"
"roster delete <jid>\n"
@@ -305,8 +485,14 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
"jingle decline <sid>\n"
"jingle accept-file <sid> <file path>\n"
"jingle send-file <jid> <file path>\n"
- "jingle accept-call <sid> <in port> <out port>\n"
- "jingle call <jid> <in port> <out port>\n"
+ "jingle accept-call <sid>\n"
+ "jingle call <jid>\n"
+ "disco info <jid>\n"
+ "disco items <jid>\n"
+ "pubsub node delete <service_jid> <node>\n"
+ "blocklist\n"
+ "blocklist block <jid>\n"
+ "blocklist unblock <jid>\n"
;
if (! strcmp(word, "help")) {
@@ -337,11 +523,14 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
return;
}
msg_text = jid_str + strlen(jid_str) + 1;
- xmlNodePtr msg = rexmpp_xml_add_id(s, xmlNewNode(NULL, "message"));
- xmlNewNs(msg, "jabber:client", NULL);
- xmlNewProp(msg, "to", jid.full);
- xmlNewProp(msg, "type", "chat");
- xmlNewTextChild(msg, NULL, "body", msg_text);
+
+ rexmpp_xml_t *msg = rexmpp_xml_new_elem("message", "jabber:client");
+ rexmpp_xml_add_id(msg);
+ rexmpp_xml_add_attr(msg, "to", jid.full);
+ rexmpp_xml_add_attr(msg, "type", "chat");
+ rexmpp_xml_t *body = rexmpp_xml_new_elem("body", NULL);
+ rexmpp_xml_add_text(body, msg_text);
+ rexmpp_xml_add_child(msg, body);
rexmpp_send(s, msg);
}
@@ -353,9 +542,9 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
return;
}
msg_text = jid_str + strlen(jid_str) + 1;
- xmlNodePtr body = xmlNewNode(NULL, "body");
- xmlNewNs(body, "jabber:client", NULL);
- xmlNodeAddContent(body, msg_text);
+ rexmpp_xml_t *body =
+ rexmpp_xml_new_elem("body", "jabber:client");
+ rexmpp_xml_add_text(body, msg_text);
const char *rcpt[2];
rcpt[0] = jid.full;
rcpt[1] = NULL;
@@ -367,21 +556,20 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
} else if (strcmp(word, "crypt") == 0) {
b64 = rexmpp_openpgp_payload(s, body, rcpt, NULL, REXMPP_OX_CRYPT);
}
- xmlNodePtr openpgp = xmlNewNode(NULL, "openpgp");
- openpgp->ns = xmlNewNs(openpgp, "urn:xmpp:openpgp:0", NULL);
- xmlNodeAddContent(openpgp, b64);
+ rexmpp_xml_t *openpgp =
+ rexmpp_xml_new_elem("openpgp", "urn:xmpp:openpgp:0");
+ rexmpp_xml_add_text(openpgp, b64);
free(b64);
- xmlNodePtr msg = rexmpp_xml_add_id(s, xmlNewNode(NULL, "message"));
- xmlNewNs(msg, "jabber:client", NULL);
- xmlNewProp(msg, "to", jid.full);
- xmlNewProp(msg, "type", "chat");
- xmlAddChild(msg, openpgp);
+ rexmpp_xml_t *msg = rexmpp_xml_new_elem("message", "jabber:client");
+ rexmpp_xml_add_id(msg);
+ rexmpp_xml_add_attr(msg, "to", jid.full);
+ rexmpp_xml_add_attr(msg, "type", "chat");
+ rexmpp_xml_add_child(msg, openpgp);
- body = xmlNewNode(NULL, "body");
- xmlNewNs(body, "jabber:client", NULL);
- xmlNodeAddContent(body, "This is a secret message.");
- xmlAddChild(msg, body);
+ body = rexmpp_xml_new_elem("body", "jabber:client");
+ rexmpp_xml_add_text(body, "This is a secret message.");
+ rexmpp_xml_add_child(msg, body);
rexmpp_send(s, msg);
}
@@ -394,11 +582,14 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
return;
}
msg_text = jid_str + strlen(jid_str) + 1;
- xmlNodePtr msg = rexmpp_xml_add_id(s, xmlNewNode(NULL, "message"));
- xmlNewNs(msg, "jabber:client", NULL);
- xmlNewProp(msg, "to", jid.full);
- xmlNewProp(msg, "type", "groupchat");
- xmlNewTextChild(msg, NULL, "body", msg_text);
+
+ rexmpp_xml_t *msg = rexmpp_xml_new_elem("message", "jabber:client");
+ rexmpp_xml_add_id(msg);
+ rexmpp_xml_add_attr(msg, "to", jid.full);
+ rexmpp_xml_add_attr(msg, "type", "groupchat");
+ rexmpp_xml_t *body = rexmpp_xml_new_elem("body", NULL);
+ rexmpp_xml_add_text(body, msg_text);
+ rexmpp_xml_add_child(msg, body);
rexmpp_send(s, msg);
}
if (! strcmp(word, "join")) {
@@ -407,23 +598,20 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
return;
}
word = strtok_r(NULL, " ", &words_save_ptr);
+ if (word == NULL) {
+ return;
+ }
if (! strcmp(word, "as")) {
word = strtok_r(NULL, " ", &words_save_ptr);
}
if (word == NULL) {
return;
}
- char *full_jid = malloc(strlen(jid.bare) + strlen(word) + 2);
- snprintf(full_jid, strlen(jid_str) + strlen(word) + 2, "%s/%s",
+ char *occupant_jid = malloc(strlen(jid.bare) + strlen(word) + 2);
+ snprintf(occupant_jid, strlen(jid_str) + strlen(word) + 2, "%s/%s",
jid.bare, word);
- presence = rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence"));
- xmlNewProp(presence, "from", s->assigned_jid.full);
- xmlNewProp(presence, "to", full_jid);
- xmlNodePtr x = xmlNewNode(NULL, "x");
- xmlNewNs(x, "http://jabber.org/protocol/muc", NULL);
- xmlAddChild(presence, x);
- rexmpp_send(s, presence);
- free(full_jid);
+ rexmpp_muc_join(s, occupant_jid, NULL, s->muc_ping_default_delay);
+ free(occupant_jid);
}
if (! strcmp(word, "leave")) {
jid_str = strtok_r(NULL, " ", &words_save_ptr);
@@ -431,21 +619,58 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
return;
}
word = strtok_r(NULL, " ", &words_save_ptr);
+ if (word == NULL) {
+ return;
+ }
if (! strcmp(word, "as")) {
word = strtok_r(NULL, " ", &words_save_ptr);
}
if (word == NULL) {
return;
}
- char *full_jid = malloc(strlen(jid.bare) + strlen(word) + 2);
- snprintf(full_jid, strlen(jid_str) + strlen(word) + 2, "%s/%s",
+ char *occupant_jid = malloc(strlen(jid.bare) + strlen(word) + 2);
+ snprintf(occupant_jid, strlen(jid_str) + strlen(word) + 2, "%s/%s",
jid.bare, word);
- presence = rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence"));
- xmlNewProp(presence, "from", s->assigned_jid.full);
- xmlNewProp(presence, "to", full_jid);
- xmlNewProp(presence, "type", "unavailable");
- rexmpp_send(s, presence);
- free(full_jid);
+ rexmpp_muc_leave(s, occupant_jid);
+ free(occupant_jid);
+ }
+ }
+
+ if (! strcmp(word, "bookmark")) {
+ word = strtok_r(NULL, " ", &words_save_ptr);
+ if (! strcmp(word, "add")) {
+ char *muc_jid = strtok_r(NULL, " ", &words_save_ptr);
+ if (muc_jid == NULL) {
+ return;
+ }
+ char *autojoin = strtok_r(NULL, " ", &words_save_ptr);
+ char *nick_str = strtok_r(NULL, " ", &words_save_ptr);
+ char *password = strtok_r(NULL, " ", &words_save_ptr);
+
+ rexmpp_xml_t *conference =
+ rexmpp_xml_new_elem("conference", "urn:xmpp:bookmarks:1");
+ if (autojoin != NULL) {
+ rexmpp_xml_add_attr(conference, "autojoin", autojoin);
+ }
+ if (password != NULL) {
+ rexmpp_xml_add_attr(conference, "password", password);
+ }
+ if (nick_str != NULL) {
+ rexmpp_xml_t *nick =
+ rexmpp_xml_new_elem("nick", "urn:xmpp:bookmarks:1");
+ rexmpp_xml_add_text(nick, nick_str);
+ rexmpp_xml_add_child(conference, nick);
+ }
+ rexmpp_pubsub_item_publish(s, NULL, "urn:xmpp:bookmarks:1", muc_jid,
+ conference, rexmpp_bookmark_added, NULL);
+ }
+ if (! strcmp(word, "remove")) {
+ char *muc_jid = strtok_r(NULL, " ", &words_save_ptr);
+ if (muc_jid == NULL) {
+ return;
+ }
+ rexmpp_pubsub_item_retract(s, NULL, "urn:xmpp:bookmarks:1", muc_jid,
+ rexmpp_bookmark_removed, NULL);
}
}
@@ -455,25 +680,26 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
return;
}
if (! strcmp(word, "list")) {
- xmlNodePtr item;
+ rexmpp_xml_t *item;
for (item = s->roster_items;
item != NULL;
- item = xmlNextElementSibling(item)) {
- char *item_jid = xmlGetProp(item, "jid");
- char *item_ask = xmlGetProp(item, "ask");
- char *item_subscription = xmlGetProp(item, "subscription");
+ item = item->next) {
+ const char *item_jid = rexmpp_xml_find_attr_val(item, "jid");
+ const char *item_ask = rexmpp_xml_find_attr_val(item, "ask");
+ const char *item_subscription =
+ rexmpp_xml_find_attr_val(item, "subscription");
char *item_presence = "unavailable";
if (s->track_roster_presence) {
for (presence = s->roster_presence;
presence != NULL;
- presence = xmlNextElementSibling(presence)) {
- char *presence_from = xmlGetProp(presence, "from");
+ presence = presence->next) {
+ const char *presence_from =
+ rexmpp_xml_find_attr_val(presence, "from");
if (presence_from != NULL) {
rexmpp_jid_parse(presence_from, &jid);
if (! strcmp(jid.bare, item_jid)) {
item_presence = "available";
}
- free(presence_from);
}
}
}
@@ -482,28 +708,19 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
"presence = %s\n",
item_jid, item_subscription, item_ask,
item_presence);
- if (item_jid != NULL) {
- free(item_jid);
- }
- if (item_ask != NULL) {
- free(item_ask);
- }
- if (item_subscription != NULL) {
- free(item_subscription);
- }
}
} else if (! strcmp(word, "delete")) {
word = strtok_r(NULL, " ", &words_save_ptr);
if (word == NULL) {
return;
}
- xmlNodePtr delete_item = xmlNewNode(NULL, "item");
- delete_item->ns = xmlNewNs(delete_item, "jabber:iq:roster", NULL);
- xmlNewProp(delete_item, "jid", word);
- xmlNewProp(delete_item, "subscription", "remove");
- xmlNodePtr delete_query = xmlNewNode(NULL, "query");
- delete_query->ns = xmlNewNs(delete_query, "jabber:iq:roster", NULL);
- xmlAddChild(delete_query, delete_item);
+ rexmpp_xml_t *delete_item =
+ rexmpp_xml_new_elem("item", "jabber:iq:roster");
+ rexmpp_xml_add_attr(delete_item, "jid", word);
+ rexmpp_xml_add_attr(delete_item, "subscription", "remove");
+ rexmpp_xml_t *delete_query =
+ rexmpp_xml_new_elem("query", "jabber:iq:roster");
+ rexmpp_xml_add_child(delete_query, delete_item);
rexmpp_iq_new(s, "set", NULL, delete_query,
rexmpp_console_roster_deleted, NULL);
} else if (! strcmp(word, "add")) {
@@ -511,13 +728,13 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
if (word == NULL) {
return;
}
- xmlNodePtr delete_item = xmlNewNode(NULL, "item");
- delete_item->ns = xmlNewNs(delete_item, "jabber:iq:roster", NULL);
- xmlNewProp(delete_item, "jid", word);
- xmlNodePtr delete_query = xmlNewNode(NULL, "query");
- delete_query->ns = xmlNewNs(delete_query, "jabber:iq:roster", NULL);
- xmlAddChild(delete_query, delete_item);
- rexmpp_iq_new(s, "set", NULL, delete_query,
+ rexmpp_xml_t *add_item =
+ rexmpp_xml_new_elem("item", "jabber:iq:roster");
+ rexmpp_xml_add_attr(add_item, "jid", word);
+ rexmpp_xml_t *add_query =
+ rexmpp_xml_new_elem("query", "jabber:iq:roster");
+ rexmpp_xml_add_child(add_query, add_item);
+ rexmpp_iq_new(s, "set", NULL, add_query,
rexmpp_console_roster_added, NULL);
}
}
@@ -532,9 +749,10 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
if (word == NULL) {
return;
}
- presence = rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence"));
- xmlNewProp(presence, "to", word);
- xmlNewProp(presence, "type", "subscribe");
+ presence = rexmpp_xml_new_elem("presence", "jabber:client");
+ rexmpp_xml_add_id(presence);
+ rexmpp_xml_add_attr(presence, "to", word);
+ rexmpp_xml_add_attr(presence, "type", "subscribe");
rexmpp_send(s, presence);
}
if (! strcmp(word, "approve")) {
@@ -542,9 +760,10 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
if (word == NULL) {
return;
}
- presence = rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence"));
- xmlNewProp(presence, "to", word);
- xmlNewProp(presence, "type", "subscribed");
+ presence = rexmpp_xml_new_elem("presence", "jabber:client");
+ rexmpp_xml_add_id(presence);
+ rexmpp_xml_add_attr(presence, "to", word);
+ rexmpp_xml_add_attr(presence, "type", "subscribed");
rexmpp_send(s, presence);
}
if (! strcmp(word, "deny")) {
@@ -552,9 +771,10 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
if (word == NULL) {
return;
}
- presence = rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence"));
- xmlNewProp(presence, "to", word);
- xmlNewProp(presence, "type", "unsubscribed");
+ presence = rexmpp_xml_new_elem("presence", "jabber:client");
+ rexmpp_xml_add_id(presence);
+ rexmpp_xml_add_attr(presence, "to", word);
+ rexmpp_xml_add_attr(presence, "type", "unsubscribed");
rexmpp_send(s, presence);
}
}
@@ -574,7 +794,7 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
char *sid = strtok_r(NULL, " ", &words_save_ptr);
if (sid != NULL) {
rexmpp_jingle_session_terminate(s, sid,
- rexmpp_xml_new_node("success",
+ rexmpp_xml_new_elem("success",
"urn:xmpp:jingle:1"),
NULL);
}
@@ -582,7 +802,7 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
char *sid = strtok_r(NULL, " ", &words_save_ptr);
if (sid != NULL) {
rexmpp_jingle_session_terminate(s, sid,
- rexmpp_xml_new_node("decline",
+ rexmpp_xml_new_elem("decline",
"urn:xmpp:jingle:1"),
NULL);
}
@@ -600,18 +820,95 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
}
} else if (! strcmp(word, "accept-call")) {
char *sid = strtok_r(NULL, " ", &words_save_ptr);
- char *port_in = strtok_r(NULL, " ", &words_save_ptr);
- char *port_out = strtok_r(NULL, " ", &words_save_ptr);
- if (sid != NULL && port_in != NULL && port_out != NULL) {
- rexmpp_jingle_call_accept(s, sid, atoi(port_in), atoi(port_out));
+ if (sid != NULL) {
+ rexmpp_jingle_call_accept(s, sid);
}
} else if (! strcmp(word, "call")) {
char *jid = strtok_r(NULL, " ", &words_save_ptr);
- char *port_in = strtok_r(NULL, " ", &words_save_ptr);
- char *port_out = strtok_r(NULL, " ", &words_save_ptr);
- if (jid != NULL && port_in != NULL && port_out != NULL) {
- rexmpp_jingle_call(s, jid, atoi(port_in), atoi(port_out));
+ if (jid != NULL) {
+ rexmpp_jingle_call(s, jid);
+ }
+ }
+ }
+
+ if (! strcmp(word, "disco")) {
+ word = strtok_r(NULL, " ", &words_save_ptr);
+ if (word == NULL) {
+ return;
+ }
+ if (! strcmp(word, "info")) {
+ char *jid = strtok_r(NULL, " ", &words_save_ptr);
+ if (jid == NULL) {
+ return;
+ }
+ rexmpp_xml_t *query =
+ rexmpp_xml_new_elem("query",
+ "http://jabber.org/protocol/disco#info");
+ rexmpp_iq_new(s, "get", jid, query, rexmpp_console_disco_info, NULL);
+ }
+ if (! strcmp(word, "items")) {
+ char *jid = strtok_r(NULL, " ", &words_save_ptr);
+ if (jid == NULL) {
+ return;
+ }
+ rexmpp_xml_t *query =
+ rexmpp_xml_new_elem("query",
+ "http://jabber.org/protocol/disco#items");
+ rexmpp_iq_new(s, "get", jid, query, rexmpp_console_disco_items, NULL);
+ }
+ }
+
+ if (! strcmp(word, "pubsub")) {
+ word = strtok_r(NULL, " ", &words_save_ptr);
+ if (word == NULL) {
+ return;
+ }
+ if (! strcmp(word, "node")) {
+ word = strtok_r(NULL, " ", &words_save_ptr);
+ if (word == NULL) {
+ return;
+ }
+ if (! strcmp(word, "delete")) {
+ char *service_jid = strtok_r(NULL, " ", &words_save_ptr);
+ char *node = strtok_r(NULL, " ", &words_save_ptr);
+ if (service_jid == NULL || node == NULL) {
+ return;
+ }
+ rexmpp_pubsub_node_delete(s, service_jid, node, rexmpp_console_pubsub_node_deleted, NULL);
+ }
+ }
+ }
+
+ if (! strcmp(word, "blocklist")) {
+ word = strtok_r(NULL, " ", &words_save_ptr);
+ if (word == NULL) {
+ rexmpp_xml_t *bl =
+ rexmpp_xml_new_elem("blocklist", "urn:xmpp:blocking");
+ rexmpp_iq_new(s, "get", NULL, bl, rexmpp_console_blocklist, NULL);
+ } else if (! strcmp(word, "block")) {
+ char *jid = strtok_r(NULL, " ", &words_save_ptr);
+ if (jid == NULL) {
+ return;
+ }
+ rexmpp_xml_t *bl =
+ rexmpp_xml_new_elem("block", "urn:xmpp:blocking");
+ rexmpp_xml_t *item =
+ rexmpp_xml_new_elem("item", "urn:xmpp:blocking");
+ rexmpp_xml_add_attr(item, "jid", jid);
+ rexmpp_xml_add_child(bl, item);
+ rexmpp_iq_new(s, "set", NULL, bl, rexmpp_console_blocklist_blocked, NULL);
+ } else if (! strcmp(word, "unblock")) {
+ char *jid = strtok_r(NULL, " ", &words_save_ptr);
+ if (jid == NULL) {
+ return;
}
+ rexmpp_xml_t *bl =
+ rexmpp_xml_new_elem("unblock", "urn:xmpp:blocking");
+ rexmpp_xml_t *item =
+ rexmpp_xml_new_elem("item", "urn:xmpp:blocking");
+ rexmpp_xml_add_attr(item, "jid", jid);
+ rexmpp_xml_add_child(bl, item);
+ rexmpp_iq_new(s, "set", NULL, bl, rexmpp_console_blocklist_unblocked, NULL);
}
}
}
diff --git a/src/rexmpp_console.h b/src/rexmpp_console.h
index 85e3d75..bb2aed7 100644
--- a/src/rexmpp_console.h
+++ b/src/rexmpp_console.h
@@ -11,8 +11,8 @@
#include "rexmpp.h"
-void rexmpp_console_on_send (rexmpp_t *s, xmlNodePtr node);
-void rexmpp_console_on_recv (rexmpp_t *s, xmlNodePtr node);
+void rexmpp_console_on_send (rexmpp_t *s, rexmpp_xml_t *node);
+void rexmpp_console_on_recv (rexmpp_t *s, rexmpp_xml_t *node);
void rexmpp_console_on_run (rexmpp_t *s, rexmpp_err_t result);
void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len);
diff --git a/src/rexmpp_digest.c b/src/rexmpp_digest.c
new file mode 100644
index 0000000..c1dd436
--- /dev/null
+++ b/src/rexmpp_digest.c
@@ -0,0 +1,125 @@
+/**
+ @file rexmpp_digest.c
+ @brief Cryptographic functions
+ @author defanor <defanor@uberspace.net>
+ @date 2023
+ @copyright MIT license.
+*/
+
+#include "config.h"
+#include "rexmpp_digest.h"
+
+#if defined(HAVE_GCRYPT)
+#include <gcrypt.h>
+#elif defined(HAVE_NETTLE)
+#include <nettle/nettle-meta.h>
+#include <stdlib.h>
+#elif defined(HAVE_OPENSSL)
+#include <openssl/evp.h>
+#endif
+
+size_t rexmpp_digest_len (rexmpp_digest_algorithm algo) {
+ switch (algo) {
+ case REXMPP_DIGEST_SHA1: return 20;
+ case REXMPP_DIGEST_SHA256: return 32;
+ case REXMPP_DIGEST_SHA3_256: return 32;
+ default: return 0;
+ }
+}
+
+int rexmpp_digest_buffer (rexmpp_digest_algorithm algo,
+ const void *in,
+ size_t in_len,
+ void *out,
+ size_t out_len)
+{
+ rexmpp_digest_t ctx;
+ int err = rexmpp_digest_init(&ctx, algo);
+ if (err) {
+ return err;
+ }
+ err = rexmpp_digest_update(&ctx, in, in_len);
+ if (err) {
+ return err;
+ }
+ return rexmpp_digest_finish(&ctx, out, out_len);
+}
+
+int rexmpp_digest_init (rexmpp_digest_t *ctx, rexmpp_digest_algorithm algo) {
+#if defined(HAVE_GCRYPT)
+ int gcry_algo = GCRY_MD_NONE;
+ switch (algo) {
+ case REXMPP_DIGEST_SHA1: gcry_algo = GCRY_MD_SHA1; break;
+ case REXMPP_DIGEST_SHA256: gcry_algo = GCRY_MD_SHA256; break;
+ case REXMPP_DIGEST_SHA3_256: gcry_algo = GCRY_MD_SHA3_256; break;
+ default: return -1;
+ }
+ gcry_error_t err = gcry_md_open(ctx, gcry_algo, 0);
+ if (err != GPG_ERR_NO_ERROR) {
+ return -1;
+ }
+#elif defined(HAVE_NETTLE)
+ ctx->nh = NULL;
+ switch (algo) {
+ case REXMPP_DIGEST_SHA1: ctx->nh = &nettle_sha1; break;
+ case REXMPP_DIGEST_SHA256: ctx->nh = &nettle_sha256; break;
+ case REXMPP_DIGEST_SHA3_256: ctx->nh = &nettle_sha3_256; break;
+ default: return -1;
+ }
+ ctx->nh_ctx = malloc(ctx->nh->context_size);
+ ctx->nh->init(ctx->nh_ctx);
+#elif defined(HAVE_OPENSSL)
+ const EVP_MD *md = NULL;
+ switch (algo) {
+ case REXMPP_DIGEST_SHA1: md = EVP_sha1(); break;
+ case REXMPP_DIGEST_SHA256: md = EVP_sha256(); break;
+ case REXMPP_DIGEST_SHA3_256: md = EVP_sha3_256(); break;
+ default: return -1;
+ }
+ *ctx = EVP_MD_CTX_new();
+ if (! EVP_DigestInit(*ctx, md)) {
+ EVP_MD_CTX_free(*ctx);
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+int rexmpp_digest_update (rexmpp_digest_t *ctx, const void *in, size_t len) {
+#if defined(HAVE_GCRYPT)
+ gcry_md_write(*ctx, in, len);
+#elif defined(HAVE_NETTLE)
+ ctx->nh->update(ctx->nh_ctx, len, in);
+#elif defined(HAVE_OPENSSL)
+ if (! EVP_DigestUpdate(*ctx, in, len)) {
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+int rexmpp_digest_finish (rexmpp_digest_t *ctx, void *out, size_t len) {
+ int ret = 0;
+#if defined(HAVE_GCRYPT)
+ if (out != NULL) {
+ unsigned char *result = gcry_md_read(*ctx, 0);
+ if (result != NULL) {
+ memcpy(out, result, len);
+ } else {
+ ret = -1;
+ }
+ }
+ gcry_md_close(*ctx);
+#elif defined(HAVE_NETTLE)
+ ctx->nh->digest(ctx->nh_ctx, len, out);
+ free(ctx->nh_ctx);
+#elif defined(HAVE_OPENSSL)
+ (void)len;
+ if (! EVP_DigestFinal_ex(*ctx, out, NULL)) {
+ ret = -1;
+ }
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+#endif
+ return ret;
+}
diff --git a/src/rexmpp_digest.h b/src/rexmpp_digest.h
new file mode 100644
index 0000000..5001e12
--- /dev/null
+++ b/src/rexmpp_digest.h
@@ -0,0 +1,85 @@
+/**
+ @file rexmpp_digest.h
+ @brief Cryptographic functions
+ @author defanor <defanor@uberspace.net>
+ @date 2023
+ @copyright MIT license.
+*/
+
+#ifndef REXMPP_DIGEST_H
+#define REXMPP_DIGEST_H
+
+typedef enum {
+ REXMPP_DIGEST_SHA1,
+ REXMPP_DIGEST_SHA256,
+ REXMPP_DIGEST_SHA3_256
+} rexmpp_digest_algorithm;
+
+
+#if defined(HAVE_GCRYPT)
+#include <gcrypt.h>
+typedef gcry_md_hd_t rexmpp_digest_t;
+#elif defined(HAVE_NETTLE)
+#include <nettle/nettle-meta.h>
+struct rexmpp_digest {
+ const struct nettle_hash *nh;
+ void *nh_ctx;
+};
+typedef struct rexmpp_digest rexmpp_digest_t;
+#elif defined(HAVE_OPENSSL)
+#include <openssl/evp.h>
+typedef EVP_MD_CTX* rexmpp_digest_t;
+#endif
+
+/**
+ @brief Finds the digest length for a given algorithm.
+ @param[in] algo An algorithm.
+ @returns Digest length in bytes.
+*/
+size_t rexmpp_digest_len (rexmpp_digest_algorithm algo);
+
+/**
+ @brief Computes a digest for a buffer.
+ @param[in] algo An algorithm.
+ @param[in] in Input data.
+ @param[in] in_len Input data length.
+ @param[out] out Output buffer.
+ @param[in] out_len Output buffer length.
+ @returns 0 on success, non-zero on failure.
+*/
+int rexmpp_digest_buffer (rexmpp_digest_algorithm algo,
+ const void *in,
+ size_t in_len,
+ void *out,
+ size_t out_len);
+
+/**
+ @brief Initializes a digest context.
+ @param[out] ctx Pointer to an allocated ::rexmpp_digest_t context
+ to initialize.
+ @param[in] algo An algorithm to use.
+ @returns 0 on success, non-zero on failure.
+*/
+int rexmpp_digest_init (rexmpp_digest_t *ctx, rexmpp_digest_algorithm algo);
+
+/**
+ @brief Updates a digest computation.
+ @param[in,out] ctx Context pointer.
+ @param[in] in Input data.
+ @param[in] len Length of the input buffer.
+ @returns 0 on success, non-zero on failure.
+*/
+int rexmpp_digest_update (rexmpp_digest_t *ctx, const void *in, size_t len);
+
+/**
+ @brief Finishes a digest computation, freeing the context and
+ providing the output.
+ @param[in,out] ctx Context pointer.
+ @param[out] out A place to write the computed digest into, can be
+ NULL to just free the context.
+ @param[in] len Length of the allocated output buffer.
+ @returns 0 on success, non-zero on failure.
+*/
+int rexmpp_digest_finish (rexmpp_digest_t *ctx, void *out, size_t len);
+
+#endif
diff --git a/src/rexmpp_dns.c b/src/rexmpp_dns.c
index 5c3f4f6..d56aa10 100644
--- a/src/rexmpp_dns.c
+++ b/src/rexmpp_dns.c
@@ -7,6 +7,7 @@
*/
#include <memory.h>
+#include <stdlib.h>
#include <syslog.h>
#include "config.h"
@@ -64,6 +65,7 @@ int rexmpp_parse_srv (char *in, int in_len, struct rexmpp_dns_srv *out) {
}
+#ifndef USE_RUST
void rexmpp_dns_result_free (rexmpp_dns_result_t *result) {
if (result->data != NULL) {
int i;
@@ -79,20 +81,37 @@ void rexmpp_dns_result_free (rexmpp_dns_result_t *result) {
}
free(result);
}
+#endif
rexmpp_dns_result_t *result_from_hostent (struct hostent *hostinfo) {
rexmpp_dns_result_t *r = malloc(sizeof(rexmpp_dns_result_t));
+ if (r == NULL) {
+ return NULL;
+ }
r->secure = 0;
int i, size = 0;
while (hostinfo->h_addr_list[size] != NULL) {
size++;
}
r->data = malloc(sizeof(void *) * (size + 1));
+ if (r->data == NULL) {
+ free(r);
+ return NULL;
+ }
r->len = malloc(sizeof(int) * size);
+ if (r->len == NULL) {
+ free(r->data);
+ free(r);
+ return NULL;
+ }
for (i = 0; i < size; i++) {
r->len[i] = hostinfo->h_length;
r->data[i] = malloc(r->len[i]);
- memcpy(r->data[i], hostinfo->h_addr_list[i], hostinfo->h_length);
+ if (r->data[i] != NULL) {
+ memcpy(r->data[i], hostinfo->h_addr_list[i], hostinfo->h_length);
+ } else {
+ return r;
+ }
}
r->data[size] = NULL;
return r;
@@ -102,22 +121,22 @@ rexmpp_dns_result_t *result_from_hostent (struct hostent *hostinfo) {
int rexmpp_dns_ctx_init (rexmpp_t *s) {
#if defined(USE_UNBOUND)
int err;
- s->resolver.ctx = ub_ctx_create();
- if (s->resolver.ctx == NULL) {
+ s->resolver = ub_ctx_create();
+ if (s->resolver == NULL) {
rexmpp_log(s, LOG_CRIT, "Failed to create resolver context");
return 1;
}
- err = ub_ctx_resolvconf(s->resolver.ctx, NULL);
+ err = ub_ctx_resolvconf(s->resolver, NULL);
if (err != 0) {
rexmpp_log(s, LOG_WARNING, "Failed to read resolv.conf: %s",
ub_strerror(err));
}
- err = ub_ctx_hosts(s->resolver.ctx, NULL);
+ err = ub_ctx_hosts(s->resolver, NULL);
if (err != 0) {
rexmpp_log(s, LOG_WARNING, "Failed to read hosts file: %s",
ub_strerror(err));
}
- err = ub_ctx_add_ta_file(s->resolver.ctx, DNSSEC_TRUST_ANCHOR_FILE);
+ err = ub_ctx_add_ta_file(s->resolver, DNSSEC_TRUST_ANCHOR_FILE);
if (err != 0) {
rexmpp_log(s, LOG_WARNING, "Failed to set root key file for DNSSEC: %s",
ub_strerror(err));
@@ -130,7 +149,7 @@ int rexmpp_dns_ctx_init (rexmpp_t *s) {
ares_strerror(err));
return 1;
}
- err = ares_init(&(s->resolver.channel));
+ err = ares_init(&(s->resolver));
if (err) {
rexmpp_log(s, LOG_CRIT, "ares channel initialisation error: %s",
ares_strerror(err));
@@ -139,7 +158,7 @@ int rexmpp_dns_ctx_init (rexmpp_t *s) {
}
return 0;
#else
- (void)s;
+ s->resolver = NULL;
return 0;
#endif
}
@@ -151,12 +170,15 @@ void rexmpp_dns_ctx_cleanup (rexmpp_t *s) {
void rexmpp_dns_ctx_deinit (rexmpp_t *s) {
#if defined(USE_UNBOUND)
- if (s->resolver.ctx != NULL) {
- ub_ctx_delete(s->resolver.ctx);
- s->resolver.ctx = NULL;
+ if (s->resolver != NULL) {
+ ub_ctx_delete(s->resolver);
+ s->resolver = NULL;
}
#elif defined(USE_CARES)
- ares_destroy(s->resolver.channel);
+ if (s->resolver != NULL) {
+ ares_destroy(s->resolver);
+ s->resolver = NULL;
+ }
ares_library_cleanup();
#else
(void)s;
@@ -166,13 +188,13 @@ void rexmpp_dns_ctx_deinit (rexmpp_t *s) {
int rexmpp_dns_fds (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
#if defined(USE_UNBOUND)
(void)write_fds;
- int max_fd = ub_fd(s->resolver.ctx) + 1;
+ int max_fd = ub_fd(s->resolver) + 1;
if (max_fd != 0) {
FD_SET(max_fd - 1, read_fds);
}
return max_fd;
#elif defined(USE_CARES)
- return ares_fds(s->resolver.channel, read_fds, write_fds);
+ return ares_fds(s->resolver, read_fds, write_fds);
#else
(void)s;
(void)read_fds;
@@ -181,16 +203,30 @@ int rexmpp_dns_fds (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
#endif
}
-struct timeval * rexmpp_dns_timeout (rexmpp_t *s,
- struct timeval *max_tv,
- struct timeval *tv)
+struct timespec * rexmpp_dns_timeout (rexmpp_t *s,
+ struct timespec *max_tv,
+ struct timespec *tv)
{
#if defined(USE_UNBOUND)
(void)s;
(void)tv;
return max_tv;
#elif defined(USE_CARES)
- return ares_timeout(s->resolver.channel, max_tv, tv);
+ struct timeval tv_ms;
+ struct timeval *max_tv_ms = NULL;
+ if (max_tv != NULL) {
+ max_tv_ms = &tv_ms;
+ tv_ms.tv_sec = tv->tv_sec;
+ tv_ms.tv_usec = tv->tv_nsec / 1000;
+ }
+ struct timeval *ret_ms = ares_timeout(s->resolver, max_tv_ms, &tv_ms);
+ if (ret_ms == max_tv_ms) {
+ return max_tv;
+ } else {
+ tv->tv_sec = tv_ms.tv_sec;
+ tv->tv_nsec = tv_ms.tv_usec * 1000;
+ return tv;
+ }
#else
(void)s;
(void)max_tv;
@@ -237,20 +273,54 @@ void rexmpp_dns_cb (void *ptr,
size++;
}
rexmpp_dns_result_t *res = malloc(sizeof(rexmpp_dns_result_t));
- res->data = malloc(sizeof(void *) * (size + 1));
+ if (res == NULL) {
+ rexmpp_log(s, LOG_DEBUG,
+ "Failed to allocate memory for a DNS resolution result");
+ ub_resolve_free(result);
+ d->cb(s, d->ptr, NULL);
+ free(d);
+ return;
+ }
+ res->data = calloc((size + 1), sizeof(void *));
+ if (res->data == NULL) {
+ rexmpp_log(s, LOG_DEBUG,
+ "Failed to allocate memory for a DNS resolution result's data");
+ free(res);
+ ub_resolve_free(result);
+ d->cb(s, d->ptr, NULL);
+ free(d);
+ return;
+ }
res->len = malloc(sizeof(int) * size);
+ if (res->len == NULL) {
+ rexmpp_log(s, LOG_DEBUG,
+ "Failed to allocate memory for a DNS resolution result's len");
+ free(res->data);
+ free(res);
+ ub_resolve_free(result);
+ d->cb(s, d->ptr, NULL);
+ free(d);
+ return;
+ }
for (i = 0; i < size; i++) {
if (result->qtype == 33) {
/* SRV */
res->len[i] = sizeof(rexmpp_dns_srv_t);
res->data[i] = malloc(res->len[i]);
+ if (res->data[i] == NULL) {
+ rexmpp_log(s, LOG_ERR,
+ "Failed to allocate memory for an SRV record");
+ d->cb(s, d->ptr, res);
+ rexmpp_dns_result_free(res);
+ free(d);
+ return;
+ }
int err = rexmpp_parse_srv(result->data[i], result->len[i],
(rexmpp_dns_srv_t*)res->data[i]);
if (err) {
rexmpp_log(s, LOG_WARNING, "Failed to parse an SRV record");
- res->data[i + 1] = NULL;
+ d->cb(s, d->ptr, res);
rexmpp_dns_result_free(res);
- d->cb(s, d->ptr, NULL);
free(d);
return;
}
@@ -258,7 +328,12 @@ void rexmpp_dns_cb (void *ptr,
/* Non-SRV, for now that's just A or AAAA */
res->len[i] = result->len[i];
res->data[i] = malloc(res->len[i]);
- memcpy(res->data[i], result->data[i], res->len[i]);
+ if (res->data[i] == NULL) {
+ rexmpp_log(s, LOG_WARNING,
+ "Failed to allocate memory for a DNS result's data record");
+ } else {
+ memcpy(res->data[i], result->data[i], res->len[i]);
+ }
}
}
res->data[size] = NULL;
@@ -332,10 +407,14 @@ int rexmpp_dns_resolve (rexmpp_t *s,
#if defined(USE_UNBOUND)
struct rexmpp_dns_query_cb_data *d =
malloc(sizeof(struct rexmpp_dns_query_cb_data));
+ if (d == NULL) {
+ rexmpp_log(s, LOG_ERR, "Failed to allocate memory for a DNS query");
+ return 1;
+ }
d->s = s;
d->cb = callback;
d->ptr = ptr;
- int err = ub_resolve_async(s->resolver.ctx, query, rrtype, rrclass,
+ int err = ub_resolve_async(s->resolver, query, rrtype, rrclass,
d, rexmpp_dns_cb, NULL);
if (err) {
rexmpp_log(s, LOG_ERR, "Failed to query %s: %s",
@@ -345,10 +424,14 @@ int rexmpp_dns_resolve (rexmpp_t *s,
#elif defined(USE_CARES)
struct rexmpp_dns_query_cb_data *d =
malloc(sizeof(struct rexmpp_dns_query_cb_data));
+ if (d == NULL) {
+ rexmpp_log(s, LOG_ERR, "Failed to allocate memory for a DNS query");
+ return 1;
+ }
d->s = s;
d->cb = callback;
d->ptr = ptr;
- ares_query(s->resolver.channel, query, rrclass, rrtype, rexmpp_dns_cb, d);
+ ares_query(s->resolver, query, rrclass, rrtype, rexmpp_dns_cb, d);
#else
if (rrclass == 1) {
if (rrtype == 1 || rrtype == 28) {
@@ -381,8 +464,8 @@ int rexmpp_dns_process (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
#if defined(USE_UNBOUND)
(void)read_fds;
(void)write_fds;
- if (ub_poll(s->resolver.ctx)) {
- int err = ub_process(s->resolver.ctx);
+ if (ub_poll(s->resolver)) {
+ int err = ub_process(s->resolver);
if (err != 0) {
rexmpp_log(s, LOG_ERR, "DNS query processing error: %s",
ub_strerror(err));
@@ -391,7 +474,7 @@ int rexmpp_dns_process (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
}
return 0;
#elif defined(USE_CARES)
- ares_process(s->resolver.channel, read_fds, write_fds);
+ ares_process(s->resolver, read_fds, write_fds);
return 0;
#else
(void)s;
diff --git a/src/rexmpp_dns.h b/src/rexmpp_dns.h
index 6641238..7abd2ef 100644
--- a/src/rexmpp_dns.h
+++ b/src/rexmpp_dns.h
@@ -12,6 +12,7 @@
#define REXMPP_DNS_H
#include <stdint.h>
+#include <stdbool.h>
#include "config.h"
#include "rexmpp.h"
@@ -21,21 +22,21 @@
*/
#if defined(USE_UNBOUND)
#include <unbound.h>
-struct rexmpp_dns_ctx {
- struct ub_ctx *ctx;
-};
+typedef struct ub_ctx* rexmpp_dns_ctx_t;
+/* struct rexmpp_dns_ctx { */
+/* struct ub_ctx *ctx; */
+/* }; */
#elif defined(USE_CARES)
#include <ares.h>
-struct rexmpp_dns_ctx {
- ares_channel channel;
-};
+typedef ares_channel rexmpp_dns_ctx_t;
+/* struct rexmpp_dns_ctx { */
+/* ares_channel channel; */
+/* }; */
#else
-struct rexmpp_dns_ctx {
- int dummy;
-};
+typedef void* rexmpp_dns_ctx_t;
#endif
-typedef struct rexmpp_dns_ctx rexmpp_dns_ctx_t;
+/* typedef struct rexmpp_dns_ctx rexmpp_dns_ctx_t; */
struct rexmpp_dns_srv {
uint16_t priority;
@@ -58,7 +59,7 @@ struct rexmpp_dns_result {
int *len;
/** @brief Whether the result was retrieved securely (that is,
verified with DNSSEC). */
- int secure;
+ bool secure;
};
typedef struct rexmpp_dns_result rexmpp_dns_result_t;
@@ -103,9 +104,9 @@ int rexmpp_dns_fds (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds);
/**
@brief Reports timeouts.
*/
-struct timeval * rexmpp_dns_timeout (rexmpp_t *s,
- struct timeval *max_tv,
- struct timeval *tv);
+struct timespec * rexmpp_dns_timeout (rexmpp_t *s,
+ struct timespec *max_tv,
+ struct timespec *tv);
typedef void (*dns_query_cb_t) (rexmpp_t *s, void *ptr, rexmpp_dns_result_t *result);
diff --git a/src/rexmpp_dns.rs b/src/rexmpp_dns.rs
new file mode 100644
index 0000000..1489835
--- /dev/null
+++ b/src/rexmpp_dns.rs
@@ -0,0 +1,62 @@
+use std::os::raw::{c_int, c_void, c_char};
+use libc::*;
+use std::ptr;
+
+use super::rexmpp;
+
+type DNSQueryCB = unsafe extern "C"
+fn (s: *mut rexmpp::Rexmpp, ptr: *mut c_void, result: *mut RexmppDNSResult) -> ();
+
+extern {
+ pub fn rexmpp_dns_resolve (s: *mut rexmpp::Rexmpp,
+ query: *const c_char,
+ rrtype: c_int,
+ rrclass: c_int,
+ ptr: *mut c_void,
+ callback: DNSQueryCB) -> c_int;
+ pub fn rexmpp_dns_process (s: *mut rexmpp::Rexmpp,
+ read_fds: *mut fd_set,
+ write_fds: *mut fd_set) -> c_int;
+ pub fn rexmpp_dns_fds (s: *mut rexmpp::Rexmpp,
+ read_fds: *mut fd_set,
+ write_fds: *mut fd_set) -> c_int;
+ pub fn rexmpp_dns_timeout (s: *mut rexmpp::Rexmpp,
+ max_tv: *mut timespec,
+ tv: *mut timespec) -> *mut timespec;
+}
+
+#[repr(C)]
+pub struct RexmppDNSResult {
+ pub data: *mut *mut c_void,
+ pub len: *mut c_int,
+ pub secure: bool
+}
+
+#[repr(C)]
+pub struct RexmppDNSSRV {
+ pub priority: u16,
+ pub weight: u16,
+ pub port: u16,
+ pub target: [c_char; 256]
+}
+
+
+#[no_mangle]
+pub unsafe extern "C"
+fn rexmpp_dns_result_free (result: *mut RexmppDNSResult) {
+ if (*result).data != ptr::null_mut() {
+ let mut i = 0;
+ let data_ptr: *mut *mut c_void = (*result).data;
+ while *(data_ptr.offset(i)) != ptr::null_mut() {
+ free(*(data_ptr.offset(i)));
+ i += 1;
+ }
+ free((*result).data as *mut c_void);
+ (*result).data = ptr::null_mut();
+ }
+ if (*result).len != ptr::null_mut() {
+ free((*result).len as *mut c_void);
+ (*result).len = ptr::null_mut();
+ }
+ free(result as *mut c_void);
+}
diff --git a/src/rexmpp_http_upload.c b/src/rexmpp_http_upload.c
index 62d371b..18a3302 100644
--- a/src/rexmpp_http_upload.c
+++ b/src/rexmpp_http_upload.c
@@ -10,6 +10,7 @@
#include <string.h>
#include <libgen.h>
#include <errno.h>
+#include <stdlib.h>
#include "config.h"
@@ -18,6 +19,7 @@
#endif
#include "rexmpp.h"
+#include "rexmpp_xml.h"
#include "rexmpp_http_upload.h"
@@ -43,46 +45,51 @@ void rexmpp_upload_task_finish (struct rexmpp_http_upload_task *task) {
void rexmpp_http_upload_slot_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)request;
struct rexmpp_http_upload_task *task = ptr;
if (success) {
- xmlNodePtr slot = rexmpp_xml_find_child(response, "urn:xmpp:http:upload:0", "slot");
- xmlNodePtr put = rexmpp_xml_find_child(slot, "urn:xmpp:http:upload:0", "put");
- xmlNodePtr get = rexmpp_xml_find_child(slot, "urn:xmpp:http:upload:0", "get");
+ rexmpp_xml_t *slot = rexmpp_xml_find_child(response, "urn:xmpp:http:upload:0", "slot");
+ rexmpp_xml_t *put = rexmpp_xml_find_child(slot, "urn:xmpp:http:upload:0", "put");
+ rexmpp_xml_t *get = rexmpp_xml_find_child(slot, "urn:xmpp:http:upload:0", "get");
if (put != NULL && get != NULL) {
- char *put_url = xmlGetProp(put, "url");
- char *get_url = xmlGetProp(get, "url");
+ const char *put_url = rexmpp_xml_find_attr_val(put, "url");
+ const char *get_url = rexmpp_xml_find_attr_val(get, "url");
if (put_url != NULL && get_url != NULL) {
- task->get_url = get_url;
+ task->get_url = strdup(get_url);
CURL *ce = curl_easy_init();
curl_easy_setopt(ce, CURLOPT_PRIVATE, task);
curl_easy_setopt(ce, CURLOPT_UPLOAD, 1);
curl_easy_setopt(ce, CURLOPT_READDATA, task->fh);
curl_easy_setopt(ce, CURLOPT_URL, put_url);
+ curl_easy_setopt(ce, CURLOPT_INFILESIZE, task->fsize);
- xmlNodePtr header = xmlFirstElementChild(put);
+ rexmpp_xml_t *header = rexmpp_xml_first_elem_child(put);
while (header) {
- char *header_name = xmlGetProp(header, "name");
+ const char *header_name = rexmpp_xml_find_attr_val(header, "name");
if (header_name != NULL) {
- char *header_str = xmlNodeGetContent(header);
+ const char *header_str = rexmpp_xml_text_child(header);
if (header_str != NULL) {
- size_t full_header_str_len = strlen(header_name) + 3 + strlen(header_str);
+ size_t full_header_str_len =
+ strlen(header_name) + 3 + strlen(header_str);
char *full_header_str = malloc(full_header_str_len);
- snprintf(full_header_str, full_header_str_len, "%s: %s",
- header_name, header_str);
- task->http_headers =
- curl_slist_append(task->http_headers, full_header_str);
- free(full_header_str);
- free(header_str);
+ if (full_header_str != NULL) {
+ snprintf(full_header_str, full_header_str_len, "%s: %s",
+ header_name, header_str);
+ task->http_headers =
+ curl_slist_append(task->http_headers, full_header_str);
+ free(full_header_str);
+ } else {
+ rexmpp_log(s, LOG_ERR,
+ "Failed to allocate memory for a header");
+ }
}
- free(header_name);
}
- header = header->next;
+ header = rexmpp_xml_next_elem_sibling(header);
}
curl_easy_setopt(ce, CURLOPT_HTTPHEADER, task->http_headers);
@@ -91,12 +98,6 @@ void rexmpp_http_upload_slot_cb (rexmpp_t *s,
return;
} else {
rexmpp_log(s, LOG_ERR, "Unexpected structure for a HTTP file upload slot.");
- if (get_url != NULL) {
- free(get_url);
- }
- }
- if (put_url != NULL) {
- free(put_url);
}
} else {
rexmpp_log(s, LOG_ERR, "Unexpected structure for a HTTP file upload slot.");
@@ -109,8 +110,8 @@ void rexmpp_http_upload_slot_cb (rexmpp_t *s,
void rexmpp_http_upload_feature_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)response;
@@ -120,17 +121,17 @@ void rexmpp_http_upload_feature_cb (rexmpp_t *s,
rexmpp_upload_task_finish(task);
return;
}
- xmlNodePtr req = rexmpp_xml_new_node("request", "urn:xmpp:http:upload:0");
- xmlNewProp(req, "filename", task->fname);
+ rexmpp_xml_t *req =
+ rexmpp_xml_new_elem("request", "urn:xmpp:http:upload:0");
+ rexmpp_xml_add_attr(req, "filename", task->fname);
char buf[11];
snprintf(buf, 11, "%u", task->fsize);
- xmlNewProp(req, "size", buf);
+ rexmpp_xml_add_attr(req, "size", buf);
if (task->content_type) {
- xmlNewProp(req, "content-type", task->content_type);
+ rexmpp_xml_add_attr(req, "content-type", task->content_type);
}
- char *to = xmlGetProp(request, "to");
+ const char *to = rexmpp_xml_find_attr_val(request, "to");
rexmpp_iq_new(s, "get", to, req, rexmpp_http_upload_slot_cb, task);
- free(to);
}
rexmpp_err_t
@@ -143,8 +144,18 @@ rexmpp_http_upload (rexmpp_t *s,
http_upload_cb cb,
void *cb_data)
{
+ if (fname == NULL) {
+ rexmpp_log(s, LOG_ERR, "No file name is provided");
+ fclose(fh);
+ return REXMPP_E_PARAM;
+ }
struct rexmpp_http_upload_task *task =
malloc(sizeof(struct rexmpp_http_upload_task));
+ if (task == NULL) {
+ rexmpp_log(s, LOG_ERR, "Failed to allocate memory for an upload task");
+ fclose(fh);
+ return REXMPP_E_MALLOC;
+ }
task->fname = strdup(fname);
task->fsize = fsize;
task->fh = fh;
diff --git a/src/rexmpp_jid.c b/src/rexmpp_jid.c
index 522a492..f1c371c 100644
--- a/src/rexmpp_jid.c
+++ b/src/rexmpp_jid.c
@@ -64,8 +64,10 @@ int rexmpp_jid_parse (const char *str, struct rexmpp_jid *jid) {
jid->local[local_len] = '\0';
strncpy(jid->domain, domain, domain_len);
jid->domain[domain_len] = '\0';
- strncpy(jid->resource, resource, resource_len);
- jid->resource[resource_len] = '\0';
+ if (resource != NULL) {
+ strncpy(jid->resource, resource, resource_len);
+ jid->resource[resource_len] = '\0';
+ }
return 0;
}
diff --git a/src/rexmpp_jid.rs b/src/rexmpp_jid.rs
new file mode 100644
index 0000000..7730b2f
--- /dev/null
+++ b/src/rexmpp_jid.rs
@@ -0,0 +1,17 @@
+use std::os::raw::{c_char};
+
+#[repr(C)]
+pub struct RexmppJID {
+ local: [c_char; 1024],
+ domain: [c_char; 1024],
+ resource: [c_char; 1024],
+ bare: [c_char; 2048],
+ full: [c_char; 3072]
+}
+
+// #[no_mangle]
+// extern "C"
+// fn rexmpp_jid_parse (str: *const c_char, jid : &mut RexmppJID) -> c_int
+// {
+
+// }
diff --git a/src/rexmpp_jingle.c b/src/rexmpp_jingle.c
index 3a72a95..2b84445 100644
--- a/src/rexmpp_jingle.c
+++ b/src/rexmpp_jingle.c
@@ -27,24 +27,251 @@ A/V calls over ICE-UDP + DTLS-SRTP:
#include <syslog.h>
#include <errno.h>
#include <libgen.h>
-#include <gcrypt.h>
#include "config.h"
#ifdef ENABLE_CALLS
+#include <inttypes.h>
#include <glib.h>
#include <gio/gnetworking.h>
#include <nice.h>
#include <agent.h>
-#include <gnutls/dtls.h>
-#include <gnutls/gnutls.h>
-#include <gnutls/x509.h>
#include <srtp2/srtp.h>
+#include <math.h>
+#include "portaudio.h"
+#ifdef HAVE_OPUS
+#include <opus.h>
+#endif
#endif
#include "rexmpp.h"
+#include "rexmpp_xml.h"
#include "rexmpp_jingle.h"
#include "rexmpp_base64.h"
+#include "rexmpp_random.h"
+#include "rexmpp_tls.h"
+#include "rexmpp_digest.h"
+
+
+/* https://en.wikipedia.org/wiki/G.711 */
+uint8_t rexmpp_pcma_encode (int16_t x) {
+ uint8_t sign = 0x80;
+ uint8_t pos;
+ uint8_t val = 0;
+ if (x < 0) {
+ x = -x;
+ sign = 0;
+ }
+ x >>= 3;
+ for (pos = 11; pos > 5 && ! (x & (1 << pos)); pos--);
+ val = (x >> (pos - 4)) & 0xf;
+ return (sign | ((pos - 4) << 4) | val) ^ 0x55;
+}
+
+int16_t rexmpp_pcma_decode (uint8_t x) {
+ x ^= 0x55;
+ int16_t sign = -1;
+ uint8_t shift = (x >> 4) & 7;
+ int16_t val = x & 0xf;
+ if (x & 0x80) {
+ x ^= 0x80;
+ sign = 1;
+ }
+ val = (val << 1) | 1;
+ if (shift > 0) {
+ val = (val | 0x20) << (shift - 1);
+ }
+ val <<= 3;
+ return sign * val;
+}
+
+uint8_t rexmpp_pcmu_encode (int16_t x) {
+ uint8_t sign = 0;
+ uint8_t pos;
+ uint8_t val = 0;
+ if (x < 0) {
+ x = -x;
+ sign = 0x80;
+ }
+ x >>= 2;
+ for (pos = 12; pos > 5 && ! (x & (1 << pos)); pos--);
+ val = (x >> (pos - 4)) & 0xf;
+ return (sign | ((pos - 4) << 4) | val) ^ 0xff;
+}
+
+int16_t rexmpp_pcmu_decode (uint8_t x) {
+ x ^= 0xff;
+ int16_t sign = 1;
+ uint8_t shift = (x >> 4) & 7;
+ int16_t val = x & 0xf;
+ if (x & 0x80) {
+ x ^= 0x80;
+ sign = -1;
+ }
+ val = (val << 1) | 1;
+ if (shift > 0) {
+ val = (val | 0x20) << (shift - 1);
+ }
+ val <<= 2;
+ return sign * val;
+}
+
+rexmpp_xml_t *
+rexmpp_jingle_session_payload_by_id (rexmpp_jingle_session_t *sess,
+ int payload_type_id)
+{
+ if (sess->accept == NULL) {
+ return NULL;
+ }
+ rexmpp_xml_t *descr_child =
+ rexmpp_xml_first_elem_child
+ (rexmpp_xml_find_child
+ (rexmpp_xml_find_child
+ (sess->accept, "urn:xmpp:jingle:1", "content"),
+ "urn:xmpp:jingle:apps:rtp:1", "description"));
+ while (descr_child != NULL) {
+ if (rexmpp_xml_match(descr_child, "urn:xmpp:jingle:apps:rtp:1",
+ "payload-type"))
+ {
+ const char *pl_id = rexmpp_xml_find_attr_val(descr_child, "id");
+ if (pl_id != NULL && atoi(pl_id) == payload_type_id) {
+ return descr_child;
+ }
+ }
+ descr_child = rexmpp_xml_next_elem_sibling(descr_child);
+ }
+ return NULL;
+}
+
+#ifdef ENABLE_CALLS
+int rexmpp_jingle_session_configure_audio (rexmpp_jingle_session_t *sess) {
+ if (sess->accept == NULL) {
+ return 0;
+ }
+ rexmpp_xml_t *descr_child =
+ rexmpp_xml_first_elem_child
+ (rexmpp_xml_find_child
+ (rexmpp_xml_find_child(sess->accept, "urn:xmpp:jingle:1", "content"),
+ "urn:xmpp:jingle:apps:rtp:1", "description"));
+ while (descr_child != NULL) {
+ if (rexmpp_xml_match(descr_child, "urn:xmpp:jingle:apps:rtp:1",
+ "payload-type"))
+ {
+ const char *pl_id = rexmpp_xml_find_attr_val(descr_child, "id");
+ if (pl_id != NULL) {
+ if (atoi(pl_id) == 0) {
+ rexmpp_log(sess->s, LOG_INFO,
+ "Setting the codec to PCMU (0) for Jingle session %s",
+ sess->sid);
+ sess->codec = REXMPP_CODEC_PCMU;
+ sess->payload_type = 0;
+ return sess->payload_type;
+ }
+ if (atoi(pl_id) == 8) {
+ rexmpp_log(sess->s, LOG_INFO,
+ "Setting the codec to PCMA (8) for Jingle session %s",
+ sess->sid);
+ sess->codec = REXMPP_CODEC_PCMA;
+ sess->payload_type = 8;
+ return sess->payload_type;
+ }
+#ifdef HAVE_OPUS
+ const char *pl_name = rexmpp_xml_find_attr_val(descr_child, "name");
+ if ((pl_name != NULL) && (strcmp(pl_name, "opus") == 0)) {
+ sess->codec = REXMPP_CODEC_OPUS;
+ sess->payload_type = atoi(pl_id);
+ rexmpp_log(sess->s, LOG_INFO,
+ "Setting the codec to Opus (%u) for Jingle session %s",
+ sess->payload_type, sess->sid);
+ return sess->payload_type;
+ }
+#endif /* HAVE_OPUS */
+ }
+ }
+ descr_child = rexmpp_xml_next_elem_sibling(descr_child);
+ }
+ return 0;
+}
+
+
+static int rexmpp_pa_callback (const void *input,
+ void *output,
+ unsigned long frameCount,
+ const PaStreamCallbackTimeInfo* timeInfo,
+ PaStreamCallbackFlags statusFlags,
+ void *userData)
+{
+ (void)timeInfo;
+ (void)statusFlags;
+ struct pa_buffers *data = (struct pa_buffers*)userData;
+ int16_t *out = (int16_t*)output;
+ int16_t *in = (int16_t*)input;
+ unsigned int i;
+ for (i = 0; i < frameCount; i++) {
+ if (in != NULL) {
+ data->capture.buf[data->capture.write_pos] = in[i];
+ data->capture.write_pos++;
+ data->capture.write_pos %= PA_BUF_SIZE;
+ }
+
+ if (data->playback.read_pos != data->playback.write_pos) {
+ out[i] = data->playback.buf[data->playback.read_pos];
+ data->playback.read_pos++;
+ data->playback.read_pos %= PA_BUF_SIZE;
+ } else {
+ out[i] = 0;
+ }
+ }
+ return 0;
+}
+
+void
+rexmpp_jingle_run_audio (rexmpp_jingle_session_t *sess) {
+ rexmpp_random_buf(&(sess->rtp_seq_num), sizeof(uint16_t));
+ sess->rtp_last_seq_num = 0xffff;
+ rexmpp_random_buf(&(sess->rtp_timestamp), sizeof(uint32_t));
+ rexmpp_random_buf(&(sess->rtp_ssrc), sizeof(uint32_t));
+
+ int rate = 8000;
+ int channels = 1;
+#ifdef HAVE_OPUS
+ if (sess->codec == REXMPP_CODEC_OPUS) {
+ rate = 48000;
+ channels = 2;
+ int opus_error;
+ sess->opus_enc =
+ opus_encoder_create(rate, channels, OPUS_APPLICATION_VOIP, &opus_error);
+ sess->opus_dec =
+ opus_decoder_create(rate, channels, &opus_error);
+ }
+#endif /* HAVE_OPUS */
+
+ rexmpp_log(sess->s, LOG_DEBUG,
+ "Setting up an audio stream: SSRC %" PRIx32
+ ", %d Hz, %d channels",
+ sess->rtp_ssrc, rate, channels);
+
+ PaError err = Pa_OpenDefaultStream (&(sess->pa_stream),
+ channels,
+ channels,
+ paInt16,
+ /* paFloat32, */
+ rate,
+ /* 480, */
+ paFramesPerBufferUnspecified,
+ rexmpp_pa_callback,
+ &(sess->ring_buffers));
+ if (err != paNoError) {
+ rexmpp_log(sess->s, LOG_ERR, "Failed to open a PA stream: %s",
+ Pa_GetErrorText(err));
+ }
+ err = Pa_StartStream(sess->pa_stream);
+ if (err != paNoError) {
+ rexmpp_log(sess->s, LOG_ERR, "Failed to start a PA stream: %s",
+ Pa_GetErrorText(err));
+ }
+}
+#endif /* ENABLE_CALLS */
rexmpp_jingle_session_t *
@@ -52,7 +279,7 @@ rexmpp_jingle_session_by_id (rexmpp_t *s, const char *sid) {
if (sid == NULL) {
return NULL;
}
- rexmpp_jingle_session_t *cur = s->jingle.sessions;
+ rexmpp_jingle_session_t *cur = s->jingle->sessions;
while (cur != NULL) {
if (strcmp(cur->sid, sid) == 0) {
return cur;
@@ -71,17 +298,36 @@ void rexmpp_jingle_session_destroy (rexmpp_jingle_session_t *session) {
free(session->sid);
}
if (session->initiate != NULL) {
- xmlFreeNodeList(session->initiate);
+ rexmpp_xml_free_list(session->initiate);
}
if (session->accept != NULL) {
- xmlFreeNodeList(session->accept);
+ rexmpp_xml_free_list(session->accept);
}
if (session->ibb_fh != NULL) {
fclose(session->ibb_fh);
}
- #ifdef ENABLE_CALLS
+#ifdef ENABLE_CALLS
if (session->type == REXMPP_JINGLE_SESSION_MEDIA) {
int i;
+ if (session->pa_stream != NULL) {
+ PaError err = Pa_StopStream(session->pa_stream);
+ if (err != paNoError) {
+ rexmpp_log(session->s, LOG_ERR,
+ "Failed to close a PortAudio stream: %s",
+ Pa_GetErrorText(err));
+ }
+ session->pa_stream = NULL;
+ }
+#ifdef HAVE_OPUS
+ if (session->opus_enc != NULL) {
+ opus_encoder_destroy(session->opus_enc);
+ session->opus_enc = NULL;
+ }
+ if (session->opus_dec != NULL) {
+ opus_decoder_destroy(session->opus_dec);
+ session->opus_dec = NULL;
+ }
+#endif /* HAVE_OPUS */
for (i = 0; i < 2; i++) {
rexmpp_jingle_component_t *comp = &session->component[i];
if (comp->dtls_state == REXMPP_TLS_ACTIVE ||
@@ -90,22 +336,29 @@ void rexmpp_jingle_session_destroy (rexmpp_jingle_session_t *session) {
/* SRTP structures are allocated upon a TLS connection, so
using the TLS state to find when they should be
deallocated. */
- srtp_dealloc(comp->srtp_in);
- srtp_dealloc(comp->srtp_out);
+ if (comp->srtp_in != NULL) {
+ srtp_dealloc(comp->srtp_in);
+ }
+ if (comp->srtp_out != NULL) {
+ srtp_dealloc(comp->srtp_out);
+ }
}
if (comp->dtls_state == REXMPP_TLS_HANDSHAKE ||
comp->dtls_state == REXMPP_TLS_ACTIVE ||
comp->dtls_state == REXMPP_TLS_CLOSING ||
comp->dtls_state == REXMPP_TLS_CLOSED) {
- gnutls_deinit(comp->dtls_session);
+ if (comp->dtls_state == REXMPP_TLS_HANDSHAKE ||
+ comp->dtls_state == REXMPP_TLS_ACTIVE) {
+ rexmpp_tls_disconnect(comp->s, comp->dtls);
+ }
+ rexmpp_tls_session_free(comp->dtls);
+ rexmpp_tls_ctx_free(comp->dtls);
+ comp->dtls = NULL;
comp->dtls_state = REXMPP_TLS_INACTIVE;
}
- if (comp->udp_socket != -1) {
- close(comp->udp_socket);
- comp->udp_socket = -1;
- }
}
if (session->ice_agent != NULL) {
+ nice_agent_close_async(session->ice_agent, NULL, NULL);
g_object_unref(session->ice_agent);
session->ice_agent = NULL;
}
@@ -126,7 +379,7 @@ void rexmpp_jingle_session_destroy (rexmpp_jingle_session_t *session) {
session->turn_password = NULL;
}
}
- #endif
+#endif /* ENABLE_CALLS */
free(session);
}
@@ -135,11 +388,11 @@ void rexmpp_jingle_session_delete (rexmpp_t *s, rexmpp_jingle_session_t *sess) {
return;
}
rexmpp_log(s, LOG_DEBUG, "Removing Jingle session %s", sess->sid);
- rexmpp_jingle_session_t *cur = s->jingle.sessions, *prev = NULL;
+ rexmpp_jingle_session_t *cur = s->jingle->sessions, *prev = NULL;
while (cur != NULL) {
if (sess == cur) {
if (prev == NULL) {
- s->jingle.sessions = cur->next;
+ s->jingle->sessions = cur->next;
} else {
prev->next = cur->next;
}
@@ -157,7 +410,7 @@ void rexmpp_jingle_session_delete_by_id (rexmpp_t *s, const char *sid) {
int rexmpp_jingle_session_add (rexmpp_t *s, rexmpp_jingle_session_t *sess) {
uint32_t sessions_num = 0;
- rexmpp_jingle_session_t *cur = s->jingle.sessions;
+ rexmpp_jingle_session_t *cur = s->jingle->sessions;
while (cur != NULL) {
sessions_num++;
cur = cur->next;
@@ -168,8 +421,8 @@ int rexmpp_jingle_session_add (rexmpp_t *s, rexmpp_jingle_session_t *sess) {
return 0;
}
rexmpp_log(s, LOG_DEBUG, "Adding Jingle session %s", sess->sid);
- sess->next = s->jingle.sessions;
- s->jingle.sessions = sess;
+ sess->next = s->jingle->sessions;
+ s->jingle->sessions = sess;
return 1;
}
@@ -184,45 +437,61 @@ rexmpp_jingle_session_create (rexmpp_t *s,
{
rexmpp_jingle_session_t *sess = malloc(sizeof(rexmpp_jingle_session_t));
if (sess != NULL) {
- sess->s = s;
sess->jid = jid;
sess->sid = sid;
- sess->type = type;
- sess->initiator = initiator;
sess->initiate = NULL;
sess->accept = NULL;
+ sess->s = s;
+ sess->initiator = initiator;
+ sess->type = type;
sess->ibb_fh = NULL;
sess->ibb_sid = NULL;
sess->ibb_seq = 0;
#ifdef ENABLE_CALLS
+ sess->stun_host = NULL;
+ sess->stun_port = 0;
+ sess->turn_host = NULL;
+ sess->turn_port = 0;
+ sess->turn_username = NULL;
+ sess->turn_password = NULL;
+
int i;
for (i = 0; i < 2; i++) {
sess->component[i].component_id = i + 1;
sess->component[i].session = sess;
sess->component[i].s = s;
sess->component[i].dtls_state = REXMPP_TLS_INACTIVE;
- sess->component[i].dtls_buf_len = 0;
- sess->component[i].udp_socket = -1;
+ sess->component[i].dtls = rexmpp_tls_ctx_new(s, 1);
+ sess->component[i].srtp_out = NULL;
+ sess->component[i].srtp_in = NULL;
}
- sess->ice_agent = NULL;
+
sess->rtcp_mux = s->jingle_prefer_rtcp_mux;
+ sess->ice_agent = NULL;
- sess->stun_host = NULL;
- sess->stun_port = 0;
- sess->turn_host = NULL;
- sess->turn_port = 0;
- sess->turn_username = NULL;
- sess->turn_password = NULL;
- /* rexmpp_jingle_ice_agent_init(sess); */
-#endif
+ sess->pa_stream = NULL;
+
+ sess->ring_buffers.capture.read_pos = 0;
+ sess->ring_buffers.capture.write_pos = 0;
+ sess->ring_buffers.playback.read_pos = 0;
+ sess->ring_buffers.playback.write_pos = 0;
+ sess->codec = REXMPP_CODEC_UNDEFINED;
+ sess->payload_type = 0;
+
+#ifdef HAVE_OPUS
+ sess->opus_enc = NULL;
+ sess->opus_dec = NULL;
+#endif /* HAVE_POUS */
+#endif /* ENABLE_CALLS */
if (! rexmpp_jingle_session_add(s, sess)) {
- rexmpp_jingle_session_destroy(sess);
- sess = NULL;
+ /* The session is destroyed by rexmpp_jingle_session_add */
+ return NULL;
}
+ return sess;
} else {
rexmpp_log(s, LOG_ERR, "Failed to allocate memory for a Jingle session");
+ return NULL;
}
- return sess;
}
rexmpp_jingle_session_t *
@@ -230,7 +499,7 @@ rexmpp_jingle_session_by_ibb_sid (rexmpp_t *s, const char *ibb_sid) {
if (ibb_sid == NULL) {
return NULL;
}
- rexmpp_jingle_session_t *cur = s->jingle.sessions;
+ rexmpp_jingle_session_t *cur = s->jingle->sessions;
while (cur != NULL) {
if (cur->type == REXMPP_JINGLE_SESSION_FILE &&
strcmp(cur->ibb_sid, ibb_sid) == 0) {
@@ -244,31 +513,37 @@ rexmpp_jingle_session_by_ibb_sid (rexmpp_t *s, const char *ibb_sid) {
}
int rexmpp_jingle_init (rexmpp_t *s) {
- s->jingle.sessions = NULL;
+ s->jingle = malloc(sizeof(struct rexmpp_jingle_ctx));
+ s->jingle->sessions = NULL;
#ifdef ENABLE_CALLS
g_networking_init();
srtp_init();
- s->jingle.gloop = g_main_loop_new(NULL, FALSE);
-#endif
+ s->jingle->gloop = g_main_loop_new(NULL, FALSE);
+ Pa_Initialize();
+ nice_debug_enable(1);
+#endif /* ENABLE_CALLS */
return 0;
}
void rexmpp_jingle_stop (rexmpp_t *s) {
- while (s->jingle.sessions != NULL) {
- rexmpp_jingle_session_delete(s, s->jingle.sessions);
+ while (s->jingle->sessions != NULL) {
+ rexmpp_jingle_session_delete(s, s->jingle->sessions);
}
#ifdef ENABLE_CALLS
- g_main_loop_quit(s->jingle.gloop);
- s->jingle.gloop = NULL;
+ g_main_loop_quit(s->jingle->gloop);
+ s->jingle->gloop = NULL;
srtp_shutdown();
-#endif
+ Pa_Terminate();
+#endif /* ENABLE_CALLS */
+ free(s->jingle);
+ s->jingle = NULL;
}
void rexmpp_jingle_accept_file_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)request;
@@ -292,15 +567,16 @@ rexmpp_jingle_accept_file (rexmpp_t *s,
path, strerror(errno));
return REXMPP_E_OTHER;
}
- xmlNodePtr jingle = session->initiate;
- xmlNodePtr content = rexmpp_xml_find_child(jingle, "urn:xmpp:jingle:1", "content");
+ rexmpp_xml_t *jingle = session->initiate;
+ rexmpp_xml_t *content = rexmpp_xml_find_child(jingle, "urn:xmpp:jingle:1", "content");
- xmlNodePtr new_jingle = rexmpp_xml_new_node("jingle", "urn:xmpp:jingle:1");
- xmlNewProp(new_jingle, "action", "session-accept");
- xmlNewProp(new_jingle, "responder", s->assigned_jid.full);
- xmlNewProp(new_jingle, "sid", session->sid);
- xmlAddChild(new_jingle, xmlCopyNode(content, 1));
- session->accept = xmlCopyNode(new_jingle, 1);
+ rexmpp_xml_t *new_jingle =
+ rexmpp_xml_new_elem("jingle", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_attr(new_jingle, "action", "session-accept");
+ rexmpp_xml_add_attr(new_jingle, "responder", s->assigned_jid.full);
+ rexmpp_xml_add_attr(new_jingle, "sid", session->sid);
+ rexmpp_xml_add_child(new_jingle, rexmpp_xml_clone(content));
+ session->accept = rexmpp_xml_clone(new_jingle);
return rexmpp_iq_new(s, "set", session->jid, new_jingle,
rexmpp_jingle_accept_file_cb, strdup(session->sid));
}
@@ -319,8 +595,8 @@ rexmpp_jingle_accept_file_by_id (rexmpp_t *s,
void rexmpp_jingle_session_terminate_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)request;
@@ -336,24 +612,27 @@ void rexmpp_jingle_session_terminate_cb (rexmpp_t *s,
rexmpp_err_t
rexmpp_jingle_session_terminate (rexmpp_t *s,
const char *sid,
- xmlNodePtr reason_node,
+ rexmpp_xml_t *reason_node,
const char *reason_text)
{
rexmpp_jingle_session_t *session = rexmpp_jingle_session_by_id(s, sid);
if (session == NULL) {
return REXMPP_E_OTHER;
}
- xmlNodePtr jingle = rexmpp_xml_new_node("jingle", "urn:xmpp:jingle:1");
- xmlNewProp(jingle, "action", "session-terminate");
- xmlNewProp(jingle, "sid", sid);
- xmlNodePtr reason = rexmpp_xml_new_node("reason", "urn:xmpp:jingle:1");
+ rexmpp_xml_t *jingle =
+ rexmpp_xml_new_elem("jingle", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_attr(jingle, "action", "session-terminate");
+ rexmpp_xml_add_attr(jingle, "sid", sid);
+ rexmpp_xml_t *reason =
+ rexmpp_xml_new_elem("reason", "urn:xmpp:jingle:1");
if (reason_text != NULL) {
- xmlNodePtr text = rexmpp_xml_new_node("text", "urn:xmpp:jingle:1");
- xmlNodeAddContent(text, reason_text);
- xmlAddChild(reason, text);
+ rexmpp_xml_t *text =
+ rexmpp_xml_new_elem("text", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_text(text, reason_text);
+ rexmpp_xml_add_child(reason, text);
}
- xmlAddChild(reason, reason_node);
- xmlAddChild(jingle, reason);
+ rexmpp_xml_add_child(reason, reason_node);
+ rexmpp_xml_add_child(jingle, reason);
rexmpp_err_t ret = rexmpp_iq_new(s, "set", session->jid, jingle,
rexmpp_jingle_session_terminate_cb,
strdup(sid));
@@ -363,8 +642,8 @@ rexmpp_jingle_session_terminate (rexmpp_t *s,
void rexmpp_jingle_send_file_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)request;
@@ -391,95 +670,99 @@ rexmpp_jingle_send_file (rexmpp_t *s,
}
char buf[4096];
- gcry_md_hd_t hd;
- gcry_error_t err = gcry_md_open(&hd, GCRY_MD_SHA256, 0);
- if (err != GPG_ERR_NO_ERROR) {
- rexmpp_log(s, LOG_ERR, "Failed to create a MD object: %s",
- gcry_strerror(err));
+ rexmpp_digest_t sha256, sha3_256;
+ if (rexmpp_digest_init(&sha256, REXMPP_DIGEST_SHA256)) {
+ rexmpp_log(s, LOG_ERR, "Failed to initialize a SHA-256 digest object");
fclose(fh);
return REXMPP_E_OTHER;
}
- err = gcry_md_enable(hd, GCRY_MD_SHA3_256);
- if (err != GPG_ERR_NO_ERROR) {
- rexmpp_log(s, LOG_ERR, "Failed to add sha3-256 to the MD object: %s",
- gcry_strerror(err));
+ if (rexmpp_digest_init(&sha3_256, REXMPP_DIGEST_SHA3_256)) {
+ rexmpp_log(s, LOG_ERR, "Failed to initialize a SHA-3-256 digest object");
+ rexmpp_digest_finish(&sha256, NULL, 0);
fclose(fh);
return REXMPP_E_OTHER;
}
size_t len = fread(buf, 1, 4096, fh);
while (len > 0) {
- gcry_md_write(hd, buf, len);
+ rexmpp_digest_update(&sha256, buf, len);
+ rexmpp_digest_update(&sha3_256, buf, len);
len = fread(buf, 1, 4096, fh);
}
- gcry_md_final(hd);
-
- char *sid = rexmpp_gen_id(s);
- char *ibb_sid = rexmpp_gen_id(s);
-
- xmlNodePtr jingle = rexmpp_xml_new_node("jingle", "urn:xmpp:jingle:1");
- xmlNewProp(jingle, "action", "session-initiate");
- xmlNewProp(jingle, "sid", sid);
- xmlNewProp(jingle, "initiator", s->assigned_jid.full);
-
- xmlNodePtr content = rexmpp_xml_new_node("content", "urn:xmpp:jingle:1");
- xmlNewProp(content, "creator", "initiator");
- xmlNewProp(content, "name", "IBB file");
- xmlAddChild(jingle, content);
-
- xmlNodePtr transport =
- rexmpp_xml_new_node("transport", "urn:xmpp:jingle:transports:ibb:1");
- xmlNewProp(transport, "block-size", "4096");
- xmlNewProp(transport, "sid", ibb_sid);
- xmlAddChild(content, transport);
- xmlNodePtr description =
- rexmpp_xml_new_node("description", "urn:xmpp:jingle:apps:file-transfer:5");
- xmlAddChild(content, description);
- xmlNodePtr file =
- rexmpp_xml_new_node("file", "urn:xmpp:jingle:apps:file-transfer:5");
- xmlAddChild(description, file);
- xmlNodePtr file_name =
- rexmpp_xml_new_node("name", "urn:xmpp:jingle:apps:file-transfer:5");
- xmlNodeAddContent(file_name, basename(path));
- xmlAddChild(file, file_name);
+ char *sid = rexmpp_random_id();
+ char *ibb_sid = rexmpp_random_id();
+
+ rexmpp_xml_t *jingle =
+ rexmpp_xml_new_elem("jingle", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_attr(jingle, "action", "session-initiate");
+ rexmpp_xml_add_attr(jingle, "sid", sid);
+ rexmpp_xml_add_attr(jingle, "initiator", s->assigned_jid.full);
+
+ rexmpp_xml_t *content =
+ rexmpp_xml_new_elem("content", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_attr(content, "creator", "initiator");
+ rexmpp_xml_add_attr(content, "name", "IBB file");
+ rexmpp_xml_add_child(jingle, content);
+
+ rexmpp_xml_t *transport =
+ rexmpp_xml_new_elem("transport", "urn:xmpp:jingle:transports:ibb:1");
+ rexmpp_xml_add_attr(transport, "block-size", "4096");
+ rexmpp_xml_add_attr(transport, "sid", ibb_sid);
+ rexmpp_xml_add_child(content, transport);
+ rexmpp_xml_t *description =
+ rexmpp_xml_new_elem("description", "urn:xmpp:jingle:apps:file-transfer:5");
+ rexmpp_xml_add_child(content, description);
+ rexmpp_xml_t *file =
+ rexmpp_xml_new_elem("file", "urn:xmpp:jingle:apps:file-transfer:5");
+ rexmpp_xml_add_child(description, file);
+ rexmpp_xml_t *file_name =
+ rexmpp_xml_new_elem("name", "urn:xmpp:jingle:apps:file-transfer:5");
+ rexmpp_xml_add_text(file_name, basename(path));
+ rexmpp_xml_add_child(file, file_name);
+
+ char hash[32];
char *hash_base64 = NULL;
size_t hash_base64_len = 0;
- rexmpp_base64_to(gcry_md_read(hd, GCRY_MD_SHA256),
- gcry_md_get_algo_dlen(GCRY_MD_SHA256),
+
+ rexmpp_digest_finish(&sha256, hash,
+ rexmpp_digest_len(REXMPP_DIGEST_SHA256));
+ rexmpp_base64_to(hash,
+ rexmpp_digest_len(REXMPP_DIGEST_SHA256),
&hash_base64,
&hash_base64_len);
- xmlNodePtr file_hash = rexmpp_xml_new_node("hash", "urn:xmpp:hashes:2");
- xmlNewProp(file_hash, "algo", "sha-256");
- xmlNodeAddContent(file_hash, hash_base64);
+ rexmpp_xml_t *file_hash =
+ rexmpp_xml_new_elem("hash", "urn:xmpp:hashes:2");
+ rexmpp_xml_add_attr(file_hash, "algo", "sha-256");
+ rexmpp_xml_add_text(file_hash, hash_base64);
free(hash_base64);
- xmlAddChild(file, file_hash);
+ rexmpp_xml_add_child(file, file_hash);
hash_base64 = NULL;
hash_base64_len = 0;
- rexmpp_base64_to(gcry_md_read(hd, GCRY_MD_SHA3_256),
- gcry_md_get_algo_dlen(GCRY_MD_SHA3_256),
+ rexmpp_digest_finish(&sha3_256, hash,
+ rexmpp_digest_len(REXMPP_DIGEST_SHA3_256));
+ rexmpp_base64_to(hash,
+ rexmpp_digest_len(REXMPP_DIGEST_SHA3_256),
&hash_base64,
&hash_base64_len);
- file_hash = rexmpp_xml_new_node("hash", "urn:xmpp:hashes:2");
- xmlNewProp(file_hash, "algo", "sha3-256");
- xmlNodeAddContent(file_hash, hash_base64);
+ file_hash = rexmpp_xml_new_elem("hash", "urn:xmpp:hashes:2");
+ rexmpp_xml_add_attr(file_hash, "algo", "sha3-256");
+ rexmpp_xml_add_text(file_hash, hash_base64);
free(hash_base64);
- xmlAddChild(file, file_hash);
-
- gcry_md_close(hd);
+ rexmpp_xml_add_child(file, file_hash);
long fsize = ftell(fh);
fseek(fh, 0, SEEK_SET);
snprintf(buf, 11, "%ld", fsize);
- xmlNodePtr file_size =
- rexmpp_xml_new_node("size", "urn:xmpp:jingle:apps:file-transfer:5");
- xmlNodeAddContent(file_size, buf);
- xmlAddChild(file, file_size);
+ rexmpp_xml_t *file_size =
+ rexmpp_xml_new_elem("size", "urn:xmpp:jingle:apps:file-transfer:5");
+ rexmpp_xml_add_text(file_size, buf);
+ rexmpp_xml_add_child(file, file_size);
rexmpp_jingle_session_t *sess =
rexmpp_jingle_session_create(s, strdup(jid), sid, REXMPP_JINGLE_SESSION_FILE, 1);
if (sess != NULL) {
- sess->initiate = xmlCopyNode(jingle, 1);
+ sess->initiate = rexmpp_xml_clone(jingle);
sess->ibb_sid = ibb_sid;
sess->ibb_fh = fh;
return rexmpp_iq_new(s, "set", sess->jid, jingle,
@@ -492,8 +775,8 @@ rexmpp_jingle_send_file (rexmpp_t *s,
void rexmpp_jingle_ibb_close_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)request;
@@ -509,8 +792,8 @@ void rexmpp_jingle_ibb_close_cb (rexmpp_t *s,
void rexmpp_jingle_ibb_send_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)request;
@@ -529,8 +812,9 @@ void rexmpp_jingle_ibb_send_cb (rexmpp_t *s,
return;
}
if (feof(session->ibb_fh)) {
- xmlNodePtr close = rexmpp_xml_new_node("close", "http://jabber.org/protocol/ibb");
- xmlNewProp(close, "sid", session->ibb_sid);
+ rexmpp_xml_t *close =
+ rexmpp_xml_new_elem("close", "http://jabber.org/protocol/ibb");
+ rexmpp_xml_add_attr(close, "sid", session->ibb_sid);
rexmpp_iq_new(s, "set", session->jid, close,
rexmpp_jingle_ibb_close_cb, sid);
return;
@@ -538,15 +822,16 @@ void rexmpp_jingle_ibb_send_cb (rexmpp_t *s,
char buf[4096];
size_t len = fread(buf, 1, 4096, session->ibb_fh);
if (len > 0) {
- xmlNodePtr data = rexmpp_xml_new_node("data", "http://jabber.org/protocol/ibb");
- xmlNewProp(data, "sid", session->ibb_sid);
+ rexmpp_xml_t *data =
+ rexmpp_xml_new_elem("data", "http://jabber.org/protocol/ibb");
+ rexmpp_xml_add_attr(data, "sid", session->ibb_sid);
char *out = NULL;
size_t out_len = 0;
rexmpp_base64_to(buf, len, &out, &out_len);
- xmlNodeAddContent(data, out);
+ rexmpp_xml_add_text(data, out);
free(out);
snprintf(buf, 11, "%u", session->ibb_seq);
- xmlNewProp(data, "seq", buf);
+ rexmpp_xml_add_attr(data, "seq", buf);
session->ibb_seq++;
rexmpp_iq_new(s, "set", session->jid, data,
rexmpp_jingle_ibb_send_cb, sid);
@@ -554,7 +839,7 @@ void rexmpp_jingle_ibb_send_cb (rexmpp_t *s,
} else {
rexmpp_log(s, LOG_ERR, "Failed to read from a file: %s ", strerror(errno));
rexmpp_jingle_session_terminate(s, sid,
- rexmpp_xml_new_node("media-error",
+ rexmpp_xml_new_elem("media-error",
"urn:xmpp:jingle:1"),
"File reading error");
}
@@ -565,8 +850,8 @@ void rexmpp_jingle_ibb_send_cb (rexmpp_t *s,
#ifdef ENABLE_CALLS
void rexmpp_jingle_call_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)request;
@@ -581,31 +866,29 @@ void rexmpp_jingle_call_cb (rexmpp_t *s,
void
rexmpp_jingle_ice_udp_add_remote (rexmpp_jingle_session_t *sess,
- xmlNodePtr transport)
+ rexmpp_xml_t *transport)
{
if (sess->ice_agent == NULL) {
/* Must be an incoming call; just add candidates to
session-initiate's transport. */
- xmlNodePtr old_transport =
+ rexmpp_xml_t *old_transport =
rexmpp_xml_find_child(rexmpp_xml_find_child(sess->initiate,
"urn:xmpp:jingle:1",
"content"),
"urn:xmpp:jingle:transports:ice-udp:1",
"transport");
- xmlNodePtr candidate = xmlFirstElementChild(transport);
+ rexmpp_xml_t *candidate = rexmpp_xml_first_elem_child(transport);
while (rexmpp_xml_match(candidate, "urn:xmpp:jingle:transports:ice-udp:1",
"candidate")) {
- xmlAddChild(old_transport, xmlCopyNode(candidate, 1));
- candidate = candidate->next;
+ rexmpp_xml_add_child(old_transport, rexmpp_xml_clone(candidate));
+ candidate = rexmpp_xml_next_elem_sibling(candidate);
}
return;
}
- char *ufrag = xmlGetProp(transport, "ufrag");
- char *password = xmlGetProp(transport, "pwd");
+ const char *ufrag = rexmpp_xml_find_attr_val(transport, "ufrag");
+ const char *password = rexmpp_xml_find_attr_val(transport, "pwd");
nice_agent_set_remote_credentials(sess->ice_agent, sess->ice_stream_id,
ufrag, password);
- free(ufrag);
- free(password);
int component_id;
@@ -614,59 +897,63 @@ rexmpp_jingle_ice_udp_add_remote (rexmpp_jingle_session_t *sess,
nice_agent_get_remote_candidates(sess->ice_agent,
sess->ice_stream_id,
component_id);
- xmlNodePtr candidate = xmlFirstElementChild(transport);
- while (rexmpp_xml_match(candidate, "urn:xmpp:jingle:transports:ice-udp:1",
- "candidate")) {
- char *component = xmlGetProp(candidate, "component");
- if (component[0] == component_id + '0') {
- char *type_str = xmlGetProp(candidate, "type");
- int type_n = NICE_CANDIDATE_TYPE_HOST;
- if (strcmp(type_str, "host") == 0) {
- type_n = NICE_CANDIDATE_TYPE_HOST;
- } else if (strcmp(type_str, "srflx") == 0) {
- type_n = NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE;
- } else if (strcmp(type_str, "prflx") == 0) {
- type_n = NICE_CANDIDATE_TYPE_PEER_REFLEXIVE;
- } else if (strcmp(type_str, "relay") == 0) {
- type_n = NICE_CANDIDATE_TYPE_RELAYED;
- }
- free(type_str);
- NiceCandidate *c = nice_candidate_new(type_n);
- c->component_id = component_id;
- c->stream_id = sess->ice_stream_id;
+ rexmpp_xml_t *candidate = rexmpp_xml_first_elem_child(transport);
+ while (candidate != NULL) {
+ if (rexmpp_xml_match(candidate, "urn:xmpp:jingle:transports:ice-udp:1",
+ "candidate")) {
+ const char *component = rexmpp_xml_find_attr_val(candidate, "component");
+ if (component[0] == component_id + '0') {
+ const char *type_str = rexmpp_xml_find_attr_val(candidate, "type");
+ int type_n = NICE_CANDIDATE_TYPE_HOST;
+ if (strcmp(type_str, "host") == 0) {
+ type_n = NICE_CANDIDATE_TYPE_HOST;
+ } else if (strcmp(type_str, "srflx") == 0) {
+ type_n = NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE;
+ } else if (strcmp(type_str, "prflx") == 0) {
+ type_n = NICE_CANDIDATE_TYPE_PEER_REFLEXIVE;
+ } else if (strcmp(type_str, "relay") == 0) {
+ type_n = NICE_CANDIDATE_TYPE_RELAYED;
+ }
+ NiceCandidate *c = nice_candidate_new(type_n);
+ c->component_id = component_id;
+ c->stream_id = sess->ice_stream_id;
- char *foundation = xmlGetProp(candidate, "foundation");
- strncpy(c->foundation, foundation, NICE_CANDIDATE_MAX_FOUNDATION - 1);
- c->foundation[NICE_CANDIDATE_MAX_FOUNDATION - 1] = 0;
- free(foundation);
+ const char *foundation = rexmpp_xml_find_attr_val(candidate, "foundation");
+ strncpy(c->foundation, foundation, NICE_CANDIDATE_MAX_FOUNDATION - 1);
+ c->foundation[NICE_CANDIDATE_MAX_FOUNDATION - 1] = 0;
- c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
+ c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
- char *priority = xmlGetProp(candidate, "priority");
- c->priority = atoi(priority);
- free(priority);
+ const char *priority = rexmpp_xml_find_attr_val(candidate, "priority");
+ c->priority = atoi(priority);
- char *ip = xmlGetProp(candidate, "ip");
- if (! nice_address_set_from_string(&c->addr, ip)) {
- rexmpp_log(sess->s, LOG_ERR,
- "Failed to parse an ICE-UDP candidate's address: %s",
- ip);
- }
- free(ip);
+ const char *ip = rexmpp_xml_find_attr_val(candidate, "ip");
+ if (! nice_address_set_from_string(&c->addr, ip)) {
+ rexmpp_log(sess->s, LOG_ERR,
+ "Failed to parse an ICE-UDP candidate's address: %s",
+ ip);
+ }
- char *port = xmlGetProp(candidate, "port");
- nice_address_set_port(&c->addr, atoi(port));
- free(port);
+ const char *port = rexmpp_xml_find_attr_val(candidate, "port");
+ nice_address_set_port(&c->addr, atoi(port));
- remote_candidates = g_slist_prepend(remote_candidates, c);
+ remote_candidates = g_slist_prepend(remote_candidates, c);
+ }
}
- free(component);
- candidate = candidate->next;
+ candidate = rexmpp_xml_next_elem_sibling(candidate);
}
if (remote_candidates != NULL) {
+ rexmpp_log(sess->s, LOG_DEBUG,
+ "Setting %d remote candidates for component %d",
+ g_slist_length(remote_candidates),
+ component_id);
nice_agent_set_remote_candidates(sess->ice_agent, sess->ice_stream_id,
component_id, remote_candidates);
g_slist_free_full(remote_candidates, (GDestroyNotify)&nice_candidate_free);
+ } else {
+ rexmpp_log(sess->s, LOG_WARNING,
+ "No remote candidates for component %d",
+ component_id);
}
}
}
@@ -674,7 +961,7 @@ rexmpp_jingle_ice_udp_add_remote (rexmpp_jingle_session_t *sess,
/* Checks whether we are in the active (client) role for DTLS, based
on either "session-initiate" or "session-accept" message. */
int rexmpp_jingle_dtls_is_active (rexmpp_jingle_session_t *sess, int in_initiate) {
- xmlNodePtr fingerprint =
+ rexmpp_xml_t *fingerprint =
rexmpp_xml_find_child
(rexmpp_xml_find_child
(rexmpp_xml_find_child
@@ -687,7 +974,7 @@ int rexmpp_jingle_dtls_is_active (rexmpp_jingle_session_t *sess, int in_initiate
in_initiate ? "initiate" : "accept");
return 0;
}
- char *fingerprint_setup = xmlGetProp(fingerprint, "setup");
+ const char *fingerprint_setup = rexmpp_xml_find_attr_val(fingerprint, "setup");
if (fingerprint_setup == NULL) {
rexmpp_log(sess->s, LOG_ERR, "No 'setup' attribute for a fingerprint element");
return 0;
@@ -706,15 +993,14 @@ int rexmpp_jingle_dtls_is_active (rexmpp_jingle_session_t *sess, int in_initiate
active = (strcmp(fingerprint_setup, "active") == 0);
}
}
- free(fingerprint_setup);
return active;
}
void rexmpp_transport_info_call_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)request;
@@ -735,126 +1021,110 @@ 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;
- /* We'll need a certificate a bit later, but checking it before
- allocating other things. */
- int err = gnutls_certificate_get_x509_crt(sess->s->jingle.dtls_cred, 0,
- &cert_list, &cert_list_size);
- if (err) {
- rexmpp_log(sess->s, LOG_ERR,
- "Failed to read own certificate list: %s",
- gnutls_strerror(err));
- return;
- }
+ rexmpp_log(sess->s, LOG_DEBUG, "ICE agent candidate gathering is done");
- char fp[32], fp_str[97];
+ /* We'll need a fingerprint a bit later, but checking it before
+ allocating other things. */
+ char fp[32], fp_str[32 * 3 + 1];
size_t fp_size = 32;
- gnutls_x509_crt_get_fingerprint(cert_list[0], GNUTLS_DIG_SHA256, fp, &fp_size);
- int i;
- for (i = 0; i < 32; i++) {
- snprintf(fp_str + i * 3, 4, "%02X:", fp[i] & 0xFF);
- }
- fp_str[95] = 0;
-
- for (i = 0; i < cert_list_size; i++) {
- gnutls_x509_crt_deinit(cert_list[i]);
+ if (rexmpp_tls_my_fp(sess->s, fp, fp_str, &fp_size)) {
+ return;
}
- gnutls_free(cert_list);
- xmlNodePtr jingle = rexmpp_xml_new_node("jingle", "urn:xmpp:jingle:1");
- xmlNewProp(jingle, "sid", sess->sid);
+ rexmpp_xml_t *jingle = rexmpp_xml_new_elem("jingle", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_attr(jingle, "sid", sess->sid);
- xmlNodePtr content = rexmpp_xml_new_node("content", "urn:xmpp:jingle:1");
- xmlNewProp(content, "creator", "initiator");
- xmlNewProp(content, "senders", "both");
- xmlNodePtr description;
+ rexmpp_xml_t *content = rexmpp_xml_new_elem("content", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_attr(content, "creator", "initiator");
+ rexmpp_xml_add_attr(content, "senders", "both");
+ rexmpp_xml_t *description;
if (sess->initiator) {
- xmlNewProp(jingle, "action", "session-initiate");
- xmlNewProp(jingle, "initiator", sess->s->assigned_jid.full);
- xmlNewProp(content, "name", "call");
+ /* We are the intiator: send the predefined options. */
+ rexmpp_xml_add_attr(jingle, "action", "session-initiate");
+ rexmpp_xml_add_attr(jingle, "initiator", sess->s->assigned_jid.full);
+ rexmpp_xml_add_attr(content, "name", "call");
/* https://datatracker.ietf.org/doc/html/rfc4568 */
- xmlNodePtr encryption =
- rexmpp_xml_new_node("encryption", "urn:xmpp:jingle:apps:rtp:1");
- xmlNewProp(encryption, "required", "true");
- xmlAddChild(content, encryption);
- xmlNodePtr crypto = rexmpp_xml_new_node("crypto", "urn:xmpp:jingle:apps:rtp:1");
- xmlNewProp(crypto, "crypto-suite", "AES_CM_128_HMAC_SHA1_80");
- xmlNewProp(crypto, "tag", "1");
- xmlAddChild(encryption, crypto);
-
- description = xmlCopyNode(sess->s->jingle_rtp_description, 1);
+ rexmpp_xml_t *encryption =
+ rexmpp_xml_new_elem("encryption", "urn:xmpp:jingle:apps:rtp:1");
+ rexmpp_xml_add_attr(encryption, "required", "true");
+ rexmpp_xml_add_child(content, encryption);
+ rexmpp_xml_t *crypto =
+ rexmpp_xml_new_elem("crypto", "urn:xmpp:jingle:apps:rtp:1");
+ rexmpp_xml_add_attr(crypto, "crypto-suite", "AES_CM_128_HMAC_SHA1_80");
+ rexmpp_xml_add_attr(crypto, "tag", "1");
+ rexmpp_xml_add_child(encryption, crypto);
+
+ description = rexmpp_xml_clone(sess->s->jingle_rtp_description);
} else {
- xmlNodePtr init_jingle = sess->initiate;
- xmlNodePtr init_content =
+ /* Accepting the call, will have to compare payload type options
+ to supported and preferred ones, and pick some from those. */
+ rexmpp_xml_t *init_jingle = sess->initiate;
+ rexmpp_xml_t *init_content =
rexmpp_xml_find_child(init_jingle, "urn:xmpp:jingle:1", "content");
- char *init_content_name = xmlGetProp(init_content, "name");
+ const char *init_content_name = rexmpp_xml_find_attr_val(init_content, "name");
if (init_content_name != NULL) {
- xmlNewProp(content, "name", init_content_name);
- free(init_content_name);
+ rexmpp_xml_add_attr(content, "name", init_content_name);
} else {
rexmpp_log(sess->s, LOG_ERR,
"Empty content name for Jingle session %s with %s",
sess->sid, sess->jid);
}
- xmlNewProp(jingle, "action", "session-accept");
- xmlNewProp(jingle, "initiator", sess->jid);
- xmlNewProp(jingle, "responder", sess->s->assigned_jid.full);
+ rexmpp_xml_add_attr(jingle, "action", "session-accept");
+ rexmpp_xml_add_attr(jingle, "initiator", sess->jid);
+ rexmpp_xml_add_attr(jingle, "responder", sess->s->assigned_jid.full);
- description = xmlCopyNode(sess->s->jingle_rtp_description, 2);
+ description = rexmpp_xml_clone(sess->s->jingle_rtp_description);
/* Find the first matching payload-type and add that */
- xmlNodePtr pl_type =
- xmlFirstElementChild(sess->s->jingle_rtp_description);
- xmlNodePtr selected_pl = NULL;
- while (pl_type != NULL && selected_pl == NULL) {
+ rexmpp_xml_t *pl_type =
+ rexmpp_xml_first_elem_child(sess->s->jingle_rtp_description);
+ rexmpp_xml_t *selected_pl = NULL;
+ while (pl_type != NULL) {
if (rexmpp_xml_match(pl_type, "urn:xmpp:jingle:apps:rtp:1", "payload-type")) {
- char *pl_id = xmlGetProp(pl_type, "id");
+ const char *pl_id = rexmpp_xml_find_attr_val(pl_type, "id");
if (pl_id != NULL) {
int pl_id_num = atoi(pl_id);
- xmlNodePtr proposed_pl_type =
- xmlFirstElementChild
+ rexmpp_xml_t *proposed_pl_type =
+ rexmpp_xml_first_elem_child
(rexmpp_xml_find_child
(rexmpp_xml_find_child(sess->initiate,
"urn:xmpp:jingle:1", "content"),
"urn:xmpp:jingle:apps:rtp:1", "description"));
while (proposed_pl_type != NULL && selected_pl == NULL) {
if (rexmpp_xml_match(proposed_pl_type, "urn:xmpp:jingle:apps:rtp:1", "payload-type")) {
- char *proposed_pl_id = xmlGetProp(proposed_pl_type, "id");
+ const char *proposed_pl_id = rexmpp_xml_find_attr_val(proposed_pl_type, "id");
if (proposed_pl_id != NULL) {
int proposed_pl_id_num = atoi(proposed_pl_id);
if (pl_id_num < 96 && pl_id_num == proposed_pl_id_num) {
selected_pl = pl_type;
} else {
- char *pl_name = xmlGetProp(pl_type, "name");
+ const char *pl_name =
+ rexmpp_xml_find_attr_val(pl_type, "name");
if (pl_name != NULL) {
- char *proposed_pl_name = xmlGetProp(proposed_pl_type, "name");
+ const char *proposed_pl_name =
+ rexmpp_xml_find_attr_val(proposed_pl_type, "name");
if (proposed_pl_name != NULL) {
if (strcmp(pl_name, proposed_pl_name) == 0) {
/* todo: compare clock rates, numbers of
channels, parameters */
selected_pl = pl_type;
}
- free(proposed_pl_name);
}
- free(pl_name);
}
}
- free(proposed_pl_id);
}
}
- proposed_pl_type = proposed_pl_type->next;
+ proposed_pl_type = rexmpp_xml_next_elem_sibling(proposed_pl_type);
}
- free(pl_id);
} else {
rexmpp_log(sess->s, LOG_ERR,
- "No 'id' specified for a pyaload-type element.");
+ "No 'id' specified for a payload-type element.");
}
}
pl_type = pl_type->next;
}
if (selected_pl != NULL) {
- xmlAddChild(description, xmlCopyNode(selected_pl, 1));
+ rexmpp_xml_add_child(description, rexmpp_xml_clone(selected_pl));
} else {
rexmpp_log(sess->s, LOG_ERR, "No suitable payload type found");
/* todo: fail if it's NULL, though it shouldn't happen, since
@@ -862,69 +1132,70 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent,
}
}
- xmlAddChild(jingle, content);
- xmlAddChild(content, description);
+ rexmpp_xml_add_child(jingle, content);
+ rexmpp_xml_add_child(content, description);
if (sess->rtcp_mux) {
- xmlNodePtr rtcp_mux =
- rexmpp_xml_new_node("rtcp-mux", "urn:xmpp:jingle:apps:rtp:1");
- xmlAddChild(description, rtcp_mux);
+ rexmpp_xml_t *rtcp_mux =
+ rexmpp_xml_new_elem("rtcp-mux", "urn:xmpp:jingle:apps:rtp:1");
+ rexmpp_xml_add_child(description, rtcp_mux);
}
- xmlNodePtr transport =
- rexmpp_xml_new_node("transport", "urn:xmpp:jingle:transports:ice-udp:1");
+ rexmpp_xml_t *transport =
+ rexmpp_xml_new_elem("transport", "urn:xmpp:jingle:transports:ice-udp:1");
gchar *ufrag = NULL;
gchar *password = NULL;
nice_agent_get_local_credentials(agent, stream_id, &ufrag, &password);
- xmlNewProp(transport, "ufrag", ufrag);
- xmlNewProp(transport, "pwd", password);
+ rexmpp_xml_add_attr(transport, "ufrag", ufrag);
+ rexmpp_xml_add_attr(transport, "pwd", password);
g_free(ufrag);
g_free(password);
- xmlAddChild(content, transport);
+ rexmpp_xml_add_child(content, transport);
int component_id;
- xmlNodePtr postponed_candidates = NULL;
+ rexmpp_xml_t *postponed_candidates = NULL;
for (component_id = 1; component_id <= (sess->rtcp_mux ? 1 : 2); component_id++) {
GSList *candidates = nice_agent_get_local_candidates(agent, stream_id, component_id);
GSList *cand_cur = candidates;
int cand_num = 0;
while (cand_cur != NULL) {
- xmlNodePtr candidate =
- rexmpp_xml_new_node("candidate", "urn:xmpp:jingle:transports:ice-udp:1");
+ rexmpp_xml_t *candidate =
+ rexmpp_xml_new_elem("candidate", "urn:xmpp:jingle:transports:ice-udp:1");
char buf[INET6_ADDRSTRLEN];
NiceCandidate *c = (NiceCandidate *)cand_cur->data;
snprintf(buf, 11, "%u", component_id);
- xmlNewProp(candidate, "component", buf);
- xmlNewProp(candidate, "foundation", c->foundation);
- xmlNewProp(candidate, "generation", "0");
- char *cid = rexmpp_gen_id(sess->s);
- xmlNewProp(candidate, "id", cid);
+ rexmpp_xml_add_attr(candidate, "component", buf);
+ rexmpp_xml_add_attr(candidate, "foundation", c->foundation);
+ rexmpp_xml_add_attr(candidate, "generation", "0");
+ char *cid = rexmpp_random_id();
+ rexmpp_xml_add_attr(candidate, "id", cid);
free(cid);
nice_address_to_string(&c->addr, buf);
- xmlNewProp(candidate, "ip", buf);
+ rexmpp_xml_add_attr(candidate, "ip", buf);
snprintf(buf, 11, "%u", nice_address_get_port(&c->addr));
- xmlNewProp(candidate, "port", buf);
- xmlNewProp(candidate, "network", "0");
- xmlNewProp(candidate, "protocol", "udp");
+ rexmpp_xml_add_attr(candidate, "port", buf);
+ rexmpp_xml_add_attr(candidate, "network", "0");
+ rexmpp_xml_add_attr(candidate, "protocol", "udp");
snprintf(buf, 11, "%u", c->priority);
- xmlNewProp(candidate, "priority", buf);
+ rexmpp_xml_add_attr(candidate, "priority", buf);
char *nice_type[] = {"host", "srflx", "prflx", "relay"};
if (c->type < 4) {
- xmlNewProp(candidate, "type", nice_type[c->type]);
+ rexmpp_xml_add_attr(candidate, "type", nice_type[c->type]);
}
/* Can't send too many candidates, since stanza sizes are usually
- limited, and then it breaks the stream. Limiting to 10 per
- component, sending the rest later, via transport-info. */
+ limited, and then it breaks the stream. Limiting to 10 per
+ component, sending the rest later, via transport-info. */
if (cand_num < 10) {
- xmlAddChild(transport, candidate);
+ rexmpp_xml_add_child(transport, candidate);
} else {
- xmlNodePtr jingle_ti = rexmpp_xml_new_node("jingle", "urn:xmpp:jingle:1");
- xmlNewProp(jingle_ti, "sid", sess->sid);
- xmlNewProp(jingle_ti, "action", "transport-info");
- xmlNodePtr content_copy = xmlCopyNode(content, 2);
- xmlNodePtr transport_copy = xmlCopyNode(transport, 2);
- xmlAddChild(jingle_ti, content_copy);
- xmlAddChild(content_copy, transport_copy);
- xmlAddChild(transport_copy, candidate);
+ rexmpp_xml_t *jingle_ti =
+ rexmpp_xml_new_elem("jingle", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_attr(jingle_ti, "sid", sess->sid);
+ rexmpp_xml_add_attr(jingle_ti, "action", "transport-info");
+ rexmpp_xml_t *content_copy = rexmpp_xml_clone(content);
+ rexmpp_xml_t *transport_copy = rexmpp_xml_clone(transport);
+ rexmpp_xml_add_child(jingle_ti, content_copy);
+ rexmpp_xml_add_child(content_copy, transport_copy);
+ rexmpp_xml_add_child(transport_copy, candidate);
jingle_ti->next = postponed_candidates;
postponed_candidates = jingle_ti;
}
@@ -936,24 +1207,26 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent,
}
}
- xmlNodePtr fingerprint =
- rexmpp_xml_new_node("fingerprint", "urn:xmpp:jingle:apps:dtls:0");
- xmlNewProp(fingerprint, "hash", "sha-256");
+ rexmpp_xml_t *fingerprint =
+ rexmpp_xml_new_elem("fingerprint", "urn:xmpp:jingle:apps:dtls:0");
+ rexmpp_xml_add_attr(fingerprint, "hash", "sha-256");
if (sess->initiator) {
- xmlNewProp(fingerprint, "setup", "actpass");
+ rexmpp_xml_add_attr(fingerprint, "setup", "actpass");
} else if (rexmpp_jingle_dtls_is_active(sess, 1)) {
- xmlNewProp(fingerprint, "setup", "active");
+ rexmpp_xml_add_attr(fingerprint, "setup", "active");
} else {
- xmlNewProp(fingerprint, "setup", "passive");
+ rexmpp_xml_add_attr(fingerprint, "setup", "passive");
}
- xmlNodeAddContent(fingerprint, fp_str);
- xmlAddChild(transport, fingerprint);
+ rexmpp_xml_add_text(fingerprint, fp_str);
+ rexmpp_xml_add_child(transport, fingerprint);
if (sess->initiator) {
- sess->initiate = xmlCopyNode(jingle, 1);
+ sess->initiate = rexmpp_xml_clone(jingle);
} else {
- sess->accept = xmlCopyNode(jingle, 1);
+ sess->accept = rexmpp_xml_clone(jingle);
+ rexmpp_jingle_session_configure_audio(sess);
+ rexmpp_jingle_run_audio(sess);
}
rexmpp_iq_new(sess->s, "set", sess->jid, jingle,
@@ -962,7 +1235,7 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent,
/* Now send transport-info messages with candidates that didn't fit
initially. */
while (postponed_candidates != NULL) {
- xmlNodePtr pc_next = postponed_candidates->next;
+ rexmpp_xml_t *pc_next = postponed_candidates->next;
postponed_candidates->next = NULL;
rexmpp_iq_new(sess->s, "set", sess->jid, postponed_candidates,
rexmpp_transport_info_call_cb, strdup(sess->sid));
@@ -971,7 +1244,7 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent,
}
ssize_t
-rexmpp_jingle_dtls_push_func (gnutls_transport_ptr_t p, const void *data, size_t size)
+rexmpp_jingle_dtls_push_func (void *p, const void *data, size_t size)
{
rexmpp_jingle_component_t *comp = p;
rexmpp_jingle_session_t *sess = comp->session;
@@ -979,58 +1252,21 @@ rexmpp_jingle_dtls_push_func (gnutls_transport_ptr_t p, const void *data, size_t
comp->component_id, size, data);
}
-ssize_t rexmpp_jingle_dtls_generic_pull_func (rexmpp_jingle_session_t *sess,
- char *tls_buf,
- size_t *tls_buf_len,
- gnutls_session_t tls_session,
- void *data,
- size_t size)
-{
- (void)sess;
- size_t ret = -1;
- if (*tls_buf_len > 0) {
- if (size >= *tls_buf_len) {
- memcpy(data, tls_buf, *tls_buf_len);
- ret = *tls_buf_len;
- *tls_buf_len = 0;
- } else {
- if (size > DTLS_SRTP_BUF_SIZE) {
- size = DTLS_SRTP_BUF_SIZE;
- }
- memcpy(data, tls_buf, size);
- memmove(tls_buf, tls_buf + size, DTLS_SRTP_BUF_SIZE - size);
- ret = size;
- *tls_buf_len = *tls_buf_len - size;
- }
- } else {
- gnutls_transport_set_errno(tls_session, EAGAIN);
- ret = -1;
- }
-
- return ret;
+rexmpp_tls_t *rexmpp_jingle_component_dtls(void *p) {
+ rexmpp_jingle_component_t *comp = p;
+ return comp->dtls;
}
-ssize_t
-rexmpp_jingle_dtls_pull_func (gnutls_transport_ptr_t p,
- void *data,
- size_t size)
+/* The timeout is always zero for DTLS. */
+int rexmpp_jingle_dtls_pull_timeout_func (void *p,
+ unsigned int ms)
{
rexmpp_jingle_component_t *comp = p;
rexmpp_jingle_session_t *sess = comp->session;
- return
- rexmpp_jingle_dtls_generic_pull_func(sess,
- comp->dtls_buf,
- &comp->dtls_buf_len,
- comp->dtls_session,
- data,
- size);
-}
+ /* if (comp->dtls->dtls_buf_len > 0) { */
+ /* return comp->dtls->dtls_buf_len; */
+ /* } */
-int
-rexmpp_jingle_dtls_generic_pull_timeout_func (rexmpp_jingle_session_t *sess,
- unsigned int ms,
- guint component_id)
-{
fd_set rfds;
struct timeval tv;
@@ -1040,38 +1276,72 @@ rexmpp_jingle_dtls_generic_pull_timeout_func (rexmpp_jingle_session_t *sess,
char c;
FD_ZERO(&rfds);
-
- GSocket *sock =
- nice_agent_get_selected_socket(sess->ice_agent,
- sess->ice_stream_id, component_id);
- int fd = g_socket_get_fd(sock);
- FD_SET(fd, &rfds);
+ int fd, nfds = -1;
+
+ GPtrArray *sockets =
+ nice_agent_get_sockets(sess->ice_agent,
+ sess->ice_stream_id, comp->component_id);
+ guint i;
+ for (i = 0; i < sockets->len; i++) {
+ fd = g_socket_get_fd(sockets->pdata[i]);
+ FD_SET(fd, &rfds);
+ if (fd > nfds) {
+ nfds = fd;
+ }
+ }
+ g_ptr_array_unref(sockets);
tv.tv_sec = ms / 1000;
tv.tv_usec = (ms % 1000) * 1000;
- ret = select(fd + 1, &rfds, NULL, NULL, &tv);
- if (ret <= 0) {
+ ret = select(nfds + 1, &rfds, NULL, NULL, &tv);
+ if (ret < 0) {
+ rexmpp_log(sess->s, LOG_WARNING,
+ "DTLS pull function: select failed: %s",
+ strerror(errno));
return ret;
}
cli_addr_size = sizeof(cli_addr);
- ret =
- recvfrom(fd, &c, 1, MSG_PEEK,
- (struct sockaddr *) &cli_addr, &cli_addr_size);
- if (ret > 0) {
- return 1;
+ ret = 0;
+ sockets =
+ nice_agent_get_sockets(sess->ice_agent,
+ sess->ice_stream_id, comp->component_id);
+ for (i = 0; i < sockets->len; i++) {
+ int err =
+ recvfrom(g_socket_get_fd(sockets->pdata[i]), &c, 1, MSG_PEEK,
+ (struct sockaddr *) &cli_addr, &cli_addr_size);
+ if (err == -1) {
+ /* ENOTCONN and EAGAIN are common here, but report other
+ errors. */
+ if (errno != ENOTCONN && errno != EAGAIN) {
+ rexmpp_log(sess->s, LOG_WARNING,
+ "DTLS pull function: failed to peek a socket: %s",
+ strerror(errno));
+ }
+ } else {
+ ret += err;
+ }
}
+ g_ptr_array_unref(sockets);
+ if (ret > 0) {
+ return ret;
+ }
return 0;
}
-int rexmpp_jingle_dtls_pull_timeout_func (gnutls_transport_ptr_t p,
- unsigned int ms)
-{
- rexmpp_jingle_component_t *comp = p;
- return rexmpp_jingle_dtls_generic_pull_timeout_func(comp->session, ms,
- comp->component_id);
+const char *rexmpp_ice_component_state_text(int state) {
+ switch (state) {
+ case NICE_COMPONENT_STATE_DISCONNECTED: return "disconnected";
+ case NICE_COMPONENT_STATE_GATHERING: return "gathering";
+ case NICE_COMPONENT_STATE_CONNECTING: return "connecting";
+ case NICE_COMPONENT_STATE_CONNECTED: return "connected";
+ case NICE_COMPONENT_STATE_READY: return "ready";
+ case NICE_COMPONENT_STATE_FAILED: return "failed";
+ case NICE_COMPONENT_STATE_LAST: return "last";
+ default: return "unknown";
+ }
}
void
@@ -1083,6 +1353,9 @@ rexmpp_jingle_component_state_changed_cb (NiceAgent *agent,
{
rexmpp_jingle_session_t *sess = data;
(void)agent;
+ rexmpp_log(sess->s, LOG_DEBUG,
+ "ICE agent component %d state for stream %d changed to %s",
+ component_id, stream_id, rexmpp_ice_component_state_text(state));
if (component_id < 1 || component_id > 2) {
rexmpp_log(sess->s, LOG_CRIT, "Unexpected ICE component_id: %d",
component_id);
@@ -1101,30 +1374,13 @@ rexmpp_jingle_component_state_changed_cb (NiceAgent *agent,
return;
}
- int active_role = rexmpp_jingle_dtls_is_active(sess, 0);
-
- gnutls_session_t *tls_session = &sess->component[component_id - 1].dtls_session;
- gnutls_init(tls_session,
- (active_role ? GNUTLS_CLIENT : GNUTLS_SERVER) |
- GNUTLS_DATAGRAM |
- GNUTLS_NONBLOCK);
- if (! active_role) {
- gnutls_certificate_server_set_request(*tls_session, GNUTLS_CERT_REQUEST);
- }
- gnutls_set_default_priority(*tls_session);
- gnutls_credentials_set(*tls_session, GNUTLS_CRD_CERTIFICATE,
- sess->s->jingle.dtls_cred);
-
- gnutls_transport_set_ptr(*tls_session, &sess->component[component_id - 1]);
- gnutls_transport_set_push_function(*tls_session, rexmpp_jingle_dtls_push_func);
- gnutls_transport_set_pull_function(*tls_session, rexmpp_jingle_dtls_pull_func);
- gnutls_transport_set_pull_timeout_function(*tls_session,
- rexmpp_jingle_dtls_pull_timeout_func);
+ rexmpp_jingle_component_t *comp = &(sess->component[component_id - 1]);
+ rexmpp_dtls_connect(sess->s,
+ comp->dtls,
+ comp,
+ rexmpp_jingle_dtls_is_active(sess, 0));
sess->component[component_id - 1].dtls_state = REXMPP_TLS_HANDSHAKE;
- /* todo: use the profile/crypto-suite from <crypto/> element */
- gnutls_srtp_set_profile(*tls_session, GNUTLS_SRTP_AES128_CM_HMAC_SHA1_80);
- gnutls_handshake(*tls_session);
-
+ rexmpp_tls_handshake(sess->s, comp->dtls);
} else if (state == NICE_COMPONENT_STATE_FAILED) {
rexmpp_log(sess->s, LOG_ERR,
"ICE connection failed for Jingle session %s, ICE stream %d, component %d",
@@ -1136,19 +1392,20 @@ rexmpp_jingle_component_state_changed_cb (NiceAgent *agent,
void
rexmpp_jingle_ice_recv_cb (NiceAgent *agent, guint stream_id, guint component_id,
- guint len, gchar *buf, gpointer data)
+ guint len, gchar *gbuf, gpointer data)
{
/* Demultiplexing here for DTLS and SRTP:
https://datatracker.ietf.org/doc/html/rfc5764#section-5.1.2 */
(void)agent;
(void)stream_id;
(void)component_id;
+ uint8_t *buf = (uint8_t *)gbuf;
rexmpp_jingle_component_t *comp = data;
if (len == 0) {
rexmpp_log(comp->s, LOG_WARNING, "Received an empty ICE message");
return;
}
- if (127 < (uint8_t)buf[0] && (uint8_t)buf[0] < 192) {
+ if (127 < buf[0] && buf[0] < 192) {
int err;
srtp_ctx_t *srtp_in;
if (comp->dtls_state == REXMPP_TLS_ACTIVE) {
@@ -1162,43 +1419,178 @@ rexmpp_jingle_ice_recv_cb (NiceAgent *agent, guint stream_id, guint component_id
"Received an SRTP packet while DTLS is inactive");
return;
}
- uint16_t port_out = comp->udp_port_out;
+ if (srtp_in == NULL) {
+ rexmpp_log(comp->s, LOG_WARNING,
+ "Received an SRTP packet while SRTP is not set up");
+ return;
+ }
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);
- port_out = comp->session->component[0].udp_port_out;
+ err = srtp_unprotect_rtcp(srtp_in, buf, (int*)&len);
}
} 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",
err, component_id);
} else {
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = inet_addr("127.0.0.1");
- addr.sin_port = htons(port_out);
- sendto(comp->udp_socket, buf, len, 0,
- (struct sockaddr*)&addr, sizeof(struct sockaddr_in));
+ /* TODO: apparently sometimes there is more than one RTCP packet
+ in the decrypted data; parse them all. */
+ uint8_t version = (buf[0] & 0xc0) >> 6;
+ if (version == 2 && len >= 12) {
+ size_t i;
+ uint8_t payload_type = buf[1] & 0x7f;
+ uint8_t csrc_count = buf[0] & 0xF;
+ uint16_t seq_num = ((uint16_t)buf[2] << 8) | buf[3];
+ int data_start_pos = 12 + csrc_count * 4;
+
+ /* Exclude possible RTCP packets here, RFC 5761 */
+ if (component_id == 1
+ && (payload_type < 64 || payload_type > 80)) {
+ if ((seq_num > comp->session->rtp_last_seq_num)
+ || (comp->session->rtp_last_seq_num > 65500)) {
+ if ((comp->session->rtp_last_seq_num <= 65500)
+ && (seq_num - comp->session->rtp_last_seq_num > 1)) {
+ rexmpp_log(comp->s, LOG_NOTICE,
+ "%d RTP packets lost",
+ seq_num - comp->session->rtp_last_seq_num - 1);
+ }
+ struct ring_buf *playback = &(comp->session->ring_buffers.playback);
+
+ /* Skip the RTP header. */
+ if (payload_type == 0) {
+ /* PCMU */
+ for (i = data_start_pos; i < len; i++) {
+ playback->buf[playback->write_pos] = rexmpp_pcmu_decode(buf[i]);
+ playback->write_pos++;
+ playback->write_pos %= PA_BUF_SIZE;
+ }
+ } else if (payload_type == 8) {
+ /* PCMA */
+ for (i = data_start_pos; i < len; i++) {
+ playback->buf[playback->write_pos] = rexmpp_pcma_decode(buf[i]);
+ playback->write_pos++;
+ playback->write_pos %= PA_BUF_SIZE;
+ }
+ }
+#ifdef HAVE_OPUS
+ else if (payload_type == comp->session->payload_type
+ && comp->session->codec == REXMPP_CODEC_OPUS) {
+ /* The same payload type as for output, which is Opus */
+ opus_int16 decoded[5760 * 2];
+ int decoded_len;
+ decoded_len =
+ opus_decode(comp->session->opus_dec,
+ (const unsigned char *)buf + data_start_pos,
+ len - data_start_pos,
+ decoded,
+ 5760,
+ 0);
+ int j;
+ for (j = 0; j < decoded_len; j++) {
+ playback->buf[playback->write_pos] = decoded[j];
+ playback->write_pos++;
+ playback->write_pos %= PA_BUF_SIZE;
+ }
+ }
+#endif /* HAVE_OPUS */
+ else {
+ /* Some other payload type, possibly with a dynamic ID */
+ rexmpp_xml_t *payload =
+ rexmpp_jingle_session_payload_by_id(comp->session,
+ payload_type);
+ const char *pl_name = rexmpp_xml_find_attr_val(payload, "name");
+ rexmpp_log(comp->s, LOG_WARNING,
+ "Unhandled payload type %d, '%s'",
+ payload_type, pl_name);
+ }
+ comp->session->rtp_last_seq_num = seq_num;
+ } else {
+ rexmpp_log(comp->s, LOG_NOTICE,
+ "Out of order RTP packets received: %d after %d",
+ seq_num, comp->session->rtp_last_seq_num);
+ }
+ } else {
+ uint8_t packet_type = buf[1];
+ unsigned int pos = 0;
+ unsigned int rtcp_len = (buf[2] << 8) | buf[3];
+ if (packet_type == 200 && len >= 28) {
+ uint32_t rtcp_ssrc =
+ (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
+ uint64_t rtcp_ntp_timestamp = 0;
+ for (i = 8; i < 16; i++) {
+ rtcp_ntp_timestamp <<= 8;
+ rtcp_ntp_timestamp |= buf[i];
+ }
+ uint32_t rtcp_rtp_timestamp =
+ (buf[16] << 24) | (buf[17] << 16) | (buf[18] << 8) | buf[19];
+ uint32_t rtcp_packet_count =
+ (buf[20] << 24) | (buf[21] << 16) | (buf[22] << 8) | buf[23];
+ uint32_t rtcp_octet_count =
+ (buf[24] << 24) | (buf[25] << 16) | (buf[26] << 8) | buf[27];
+ rexmpp_log(comp->s, LOG_DEBUG,
+ "RTCP SR received: SSRC % " PRIx32 ", NTP TS %" PRIu64
+ ", RTP TS %" PRIu32 ", PC %" PRIu32 ", OC %" PRIu32,
+ rtcp_ssrc, rtcp_ntp_timestamp, rtcp_rtp_timestamp,
+ rtcp_packet_count, rtcp_octet_count);
+ pos = 28;
+ } else if (packet_type == 201 && len >= 8) {
+ uint32_t rtcp_ssrc =
+ (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
+ rexmpp_log(comp->s, LOG_DEBUG,
+ "RTCP RR received: SSRC %x",
+ rtcp_ssrc);
+ pos = 8;
+ } else {
+ rexmpp_log(comp->s, LOG_DEBUG, "RTCP packet received, PT %d",
+ packet_type);
+ }
+
+ while ((pos > 0)
+ && ((pos + 24) <= len)
+ && ((pos + 24) <= (rtcp_len + 1) * 4)) {
+ uint32_t rtcp_ssrc = (buf[pos] << 24) | (buf[pos + 1] << 16)
+ | (buf[pos + 2] << 8) | buf[pos + 3];
+ uint8_t fraction_lost = buf[pos + 4];
+ uint32_t packets_lost =
+ (buf[pos + 5] << 24) | (buf[pos + 6] << 16) | buf[pos + 7];
+ uint32_t rtcp_ehsn = (buf[pos + 8] << 24) | (buf[pos + 9] << 16)
+ | (buf[pos + 10] << 8) | buf[pos + 11];
+ uint32_t rtcp_jitter = (buf[pos + 12] << 24) | (buf[pos + 13] << 16)
+ | (buf[pos + 14] << 8) | buf[pos + 15];
+ uint32_t rtcp_lsr = (buf[pos + 16] << 24) | (buf[pos + 17] << 16)
+ | (buf[pos + 18] << 8) | buf[pos + 19];
+ uint32_t rtcp_dlsr = (buf[pos + 20] << 24) | (buf[pos + 21] << 16)
+ | (buf[pos + 22] << 8) | buf[pos + 23];
+ rexmpp_log(comp->s, LOG_DEBUG,
+ "RTCP report block: SSRC % " PRIx32 ", lost %" PRIu32
+ "%, %" PRIu8 " packets, "
+ "highest seq num %" PRIu32
+ ", jitter %" PRIu32 ", LSR %" PRIu32 ", DLSR %" PRIu32,
+ rtcp_ssrc, fraction_lost, packets_lost,
+ rtcp_ehsn, rtcp_jitter, rtcp_lsr, rtcp_dlsr);
+ pos += 24;
+ }
+ }
+ } else {
+ rexmpp_log(comp->s, LOG_WARNING,
+ "Unhandled RT(C)P packet: version %d, length %d",
+ version, len);
+ }
}
} else {
- if (comp->dtls_buf_len + len < DTLS_SRTP_BUF_SIZE) {
- memcpy(comp->dtls_buf + comp->dtls_buf_len, buf, len);
- comp->dtls_buf_len += len;
- } else {
- rexmpp_log(comp->s, LOG_WARNING, "Dropping a DTLS packet");
- }
+ rexmpp_dtls_feed(comp->s, comp->dtls, buf, len);
}
}
int
rexmpp_jingle_ice_agent_init (rexmpp_jingle_session_t *sess)
{
- sess->ice_agent = nice_agent_new(g_main_loop_get_context (sess->s->jingle.gloop),
+ sess->ice_agent = nice_agent_new(g_main_loop_get_context (sess->s->jingle->gloop),
NICE_COMPATIBILITY_RFC5245);
if (sess->s->local_address != NULL) {
NiceAddress *address = nice_address_new();
@@ -1223,7 +1615,7 @@ rexmpp_jingle_ice_agent_init (rexmpp_jingle_session_t *sess)
int i;
for (i = 0; i < (sess->rtcp_mux ? 1 : 2); i++) {
nice_agent_attach_recv(sess->ice_agent, sess->ice_stream_id, i + 1,
- g_main_loop_get_context (sess->s->jingle.gloop),
+ g_main_loop_get_context (sess->s->jingle->gloop),
rexmpp_jingle_ice_recv_cb,
&sess->component[i]);
}
@@ -1231,29 +1623,6 @@ rexmpp_jingle_ice_agent_init (rexmpp_jingle_session_t *sess)
return 1;
}
-void
-rexmpp_jingle_bind_sockets (rexmpp_jingle_session_t *sess,
- uint16_t rtp_port_in, uint16_t rtp_port_out)
-{
- sess->component[0].udp_port_in = rtp_port_in;
- sess->component[0].udp_port_out = rtp_port_out;
- sess->component[1].udp_port_in = rtp_port_in + 1;
- sess->component[1].udp_port_out = rtp_port_out + 1;
- int i;
- for (i = 0; i < 2; i++) {
- sess->component[i].udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = inet_addr("127.0.0.1");
- addr.sin_port = htons(sess->component[i].udp_port_in);
- if (bind (sess->component[i].udp_socket,
- (struct sockaddr*)&addr, sizeof(struct sockaddr_in))) {
- rexmpp_log(sess->s, LOG_ERR, "Failed to bind a UDP socket on port %u",
- sess->component[i].udp_port_in);
- }
- }
-}
-
void rexmpp_jingle_turn_dns_cb (rexmpp_t *s, void *ptr, rexmpp_dns_result_t *result) {
rexmpp_jingle_session_t *sess = ptr;
if (result != NULL && result->data != NULL) {
@@ -1306,25 +1675,25 @@ void rexmpp_jingle_stun_dns_cb (rexmpp_t *s, void *ptr, rexmpp_dns_result_t *res
void rexmpp_jingle_turn_cb (rexmpp_t *s,
void *sess_ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)req;
rexmpp_jingle_session_t *sess = sess_ptr;
if (success) {
/* use credentials */
- xmlNodePtr services = xmlFirstElementChild(response);
+ rexmpp_xml_t *services = rexmpp_xml_first_elem_child(response);
if (rexmpp_xml_match(services, "urn:xmpp:extdisco:2", "services")) {
- xmlNodePtr service = xmlFirstElementChild(services);
+ rexmpp_xml_t *service = rexmpp_xml_first_elem_child(services);
while (service != NULL) {
if (rexmpp_xml_match(service, "urn:xmpp:extdisco:2", "service")) {
- char *type = xmlGetProp(service, "type");
- char *transport = xmlGetProp(service, "transport");
- char *host = xmlGetProp(service, "host");
- char *port = xmlGetProp(service, "port");
- char *username = xmlGetProp(service, "username");
- char *password = xmlGetProp(service, "password");
+ const char *type = rexmpp_xml_find_attr_val(service, "type");
+ const char *transport = rexmpp_xml_find_attr_val(service, "transport");
+ const char *host = rexmpp_xml_find_attr_val(service, "host");
+ const char *port = rexmpp_xml_find_attr_val(service, "port");
+ const char *username = rexmpp_xml_find_attr_val(service, "username");
+ const char *password = rexmpp_xml_find_attr_val(service, "password");
if (sess->stun_host == NULL &&
type != NULL && transport != NULL && host != NULL && port != NULL &&
@@ -1344,27 +1713,8 @@ void rexmpp_jingle_turn_cb (rexmpp_t *s,
sess->turn_password = strdup(password);
rexmpp_log(s, LOG_DEBUG, "Setting TURN server to %s:%s", host, port);
}
-
- if (type != NULL) {
- free(type);
- }
- if (transport != NULL) {
- free(transport);
- }
- if (host != NULL) {
- free(host);
- }
- if (port != NULL) {
- free(port);
- }
- if (username != NULL) {
- free(username);
- }
- if (password != NULL) {
- free(password);
- }
}
- service = service->next;
+ service = rexmpp_xml_next_elem_sibling(service);
}
if (sess->stun_host != NULL) {
/* Resolve, then resolve STUN host, then connect. */
@@ -1392,25 +1742,24 @@ void rexmpp_jingle_turn_cb (rexmpp_t *s,
void rexmpp_jingle_discover_turn_cb (rexmpp_t *s,
void *sess_ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)req;
- char *response_from = xmlGetProp(response, "from");
+ const char *response_from = rexmpp_xml_find_attr_val(response, "from");
rexmpp_jingle_session_t *sess = sess_ptr;
if (success) {
- xmlNodePtr services = rexmpp_xml_new_node("services", "urn:xmpp:extdisco:2");
- xmlNewProp(services, "type", "turn");
- rexmpp_iq_new(s, "get", response_from, services, rexmpp_jingle_turn_cb, sess_ptr);
+ rexmpp_xml_t *services =
+ rexmpp_xml_new_elem("services", "urn:xmpp:extdisco:2");
+ rexmpp_xml_add_attr(services, "type", "turn");
+ rexmpp_iq_new(s, "get", response_from, services,
+ rexmpp_jingle_turn_cb, sess_ptr);
} else {
rexmpp_log(s, LOG_DEBUG,
"No external service discovery, trying to connect without STUN/TURN");
nice_agent_gather_candidates(sess->ice_agent, sess->ice_stream_id);
}
- if (response_from != NULL) {
- free(response_from);
- }
}
void rexmpp_jingle_discover_turn (rexmpp_t *s, rexmpp_jingle_session_t *sess) {
@@ -1420,37 +1769,36 @@ void rexmpp_jingle_discover_turn (rexmpp_t *s, rexmpp_jingle_session_t *sess) {
rexmpp_err_t
rexmpp_jingle_call (rexmpp_t *s,
- const char *jid,
- uint16_t rtp_port_in,
- uint16_t rtp_port_out)
+ const char *jid)
{
rexmpp_jingle_session_t *sess =
- rexmpp_jingle_session_create(s, strdup(jid), rexmpp_gen_id(s),
+ rexmpp_jingle_session_create(s, strdup(jid), rexmpp_random_id(),
REXMPP_JINGLE_SESSION_MEDIA, 1);
- rexmpp_jingle_ice_agent_init(sess);
- rexmpp_jingle_bind_sockets(sess, rtp_port_in, rtp_port_out);
- rexmpp_jingle_discover_turn(s, sess);
- return REXMPP_SUCCESS;
+ if (sess != NULL) {
+ rexmpp_jingle_ice_agent_init(sess);
+ rexmpp_jingle_discover_turn(s, sess);
+ return REXMPP_SUCCESS;
+ } else {
+ rexmpp_log(s, LOG_ERR, "Failed to create a Jingle session for a call");
+ return REXMPP_E_OTHER;
+ }
}
rexmpp_err_t
rexmpp_jingle_call_accept (rexmpp_t *s,
- const char *sid,
- uint16_t rtp_port_in,
- uint16_t rtp_port_out)
+ const char *sid)
{
rexmpp_jingle_session_t *sess = rexmpp_jingle_session_by_id(s, sid);
if (sess == NULL) {
return REXMPP_E_OTHER;
}
rexmpp_jingle_ice_agent_init(sess);
- rexmpp_jingle_bind_sockets(sess, rtp_port_in, rtp_port_out);
- xmlNodePtr content =
+ rexmpp_xml_t *content =
rexmpp_xml_find_child(sess->initiate,
"urn:xmpp:jingle:1",
"content");
- xmlNodePtr ice_udp_transport =
+ rexmpp_xml_t * ice_udp_transport =
rexmpp_xml_find_child(content,
"urn:xmpp:jingle:transports:ice-udp:1",
"transport");
@@ -1458,7 +1806,7 @@ rexmpp_jingle_call_accept (rexmpp_t *s,
rexmpp_log(s, LOG_ERR, "No ICE-UDP transport defined for session %s", sid);
rexmpp_jingle_session_terminate
(s, sid,
- rexmpp_xml_new_node("unsupported-transports", "urn:xmpp:jingle:1"),
+ rexmpp_xml_new_elem("unsupported-transports", "urn:xmpp:jingle:1"),
"No ICE-UDP transport defined");
return REXMPP_E_OTHER;
}
@@ -1466,71 +1814,74 @@ rexmpp_jingle_call_accept (rexmpp_t *s,
rexmpp_jingle_discover_turn(s, sess);
return REXMPP_SUCCESS;
}
-#else
+#else /* ENABLE_CALLS */
+
+ssize_t
+rexmpp_jingle_dtls_push_func (void *p, const void *data, size_t size)
+{
+ (void)p;
+ (void)data;
+ (void)size;
+ return -1;
+}
rexmpp_err_t
rexmpp_jingle_call (rexmpp_t *s,
- const char *jid,
- uint16_t rtp_port_in,
- uint16_t rtp_port_out)
+ const char *jid)
{
(void)jid;
- (void)rtp_port_in;
- (void)rtp_port_out;
rexmpp_log(s, LOG_ERR, "rexmpp is compiled without support for media calls");
return REXMPP_E_OTHER;
}
rexmpp_err_t
rexmpp_jingle_call_accept (rexmpp_t *s,
- const char *sid,
- uint16_t rtp_port_in,
- uint16_t rtp_port_out)
+ const char *sid)
{
(void)sid;
- (void)rtp_port_in;
- (void)rtp_port_out;
rexmpp_log(s, LOG_ERR, "rexmpp is compiled without support for media calls");
return REXMPP_E_OTHER;
}
-#endif
+#endif /* ENABLE_CALLS */
-int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem) {
+int rexmpp_jingle_iq (rexmpp_t *s, rexmpp_xml_t *elem) {
int handled = 0;
if (! s->enable_jingle) {
return handled;
}
- xmlNodePtr jingle = rexmpp_xml_find_child(elem, "urn:xmpp:jingle:1", "jingle");
+ rexmpp_xml_t *jingle =
+ rexmpp_xml_find_child(elem, "urn:xmpp:jingle:1", "jingle");
if (jingle != NULL) {
handled = 1;
- char *action = xmlGetProp(jingle, "action");
- char *sid = xmlGetProp(jingle, "sid");
- char *from_jid = xmlGetProp(elem, "from");
+ const char *action = rexmpp_xml_find_attr_val(jingle, "action");
+ const char *sid = rexmpp_xml_find_attr_val(jingle, "sid");
+ const char *from_jid = rexmpp_xml_find_attr_val(elem, "from");
if (action != NULL && sid != NULL && from_jid != NULL) {
if (strcmp(action, "session-initiate") == 0) {
/* todo: could be more than one content element, handle that */
- xmlNodePtr content =
+ rexmpp_xml_t *content =
rexmpp_xml_find_child(jingle, "urn:xmpp:jingle:1", "content");
if (content == NULL) {
- rexmpp_iq_reply(s, elem, "error", rexmpp_xml_error("cancel", "bad-request"));
+ rexmpp_iq_reply(s, elem, "error",
+ rexmpp_xml_error("cancel", "bad-request"));
} else {
rexmpp_iq_reply(s, elem, "result", NULL);
- xmlNodePtr file_description =
+ rexmpp_xml_t *file_description =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:apps:file-transfer:5",
"description");
- xmlNodePtr ibb_transport =
+ rexmpp_xml_t *ibb_transport =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:transports:ibb:1",
"transport");
- xmlNodePtr ice_udp_transport =
+ rexmpp_xml_t *ice_udp_transport =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:transports:ice-udp:1",
"transport");
- xmlNodePtr rtp_description =
+ rexmpp_xml_t *rtp_description =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:apps:rtp:1",
"description");
if (file_description != NULL && ibb_transport != NULL) {
- char *ibb_sid = xmlGetProp(ibb_transport, "sid");
+ const char *ibb_sid = rexmpp_xml_find_attr_val(ibb_transport, "sid");
if (ibb_sid != NULL) {
rexmpp_log(s, LOG_DEBUG,
"Jingle session-initiate from %s, sid %s, ibb sid %s",
@@ -1539,19 +1890,21 @@ int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem) {
rexmpp_jingle_session_create(s, strdup(from_jid), strdup(sid),
REXMPP_JINGLE_SESSION_FILE, 0);
if (sess != NULL) {
- sess->initiate = xmlCopyNode(jingle, 1);
- sess->ibb_sid = ibb_sid;
+ sess->initiate = rexmpp_xml_clone(jingle);
+ sess->ibb_sid = strdup(ibb_sid);
} else {
- rexmpp_jingle_session_terminate(s, sid,
- rexmpp_xml_new_node("failed-transport",
- "urn:xmpp:jingle:1"),
- NULL);
+ rexmpp_jingle_session_terminate
+ (s, sid,
+ rexmpp_xml_new_elem("failed-transport",
+ "urn:xmpp:jingle:1"),
+ NULL);
}
} else {
- rexmpp_log(s, LOG_ERR, "Jingle IBB transport doesn't have a sid attribute");
+ rexmpp_log(s, LOG_ERR,
+ "Jingle IBB transport doesn't have a sid attribute");
rexmpp_jingle_session_terminate
(s, sid,
- rexmpp_xml_new_node("unsupported-transports",
+ rexmpp_xml_new_elem("unsupported-transports",
"urn:xmpp:jingle:1"),
NULL);
}
@@ -1562,25 +1915,28 @@ int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem) {
rexmpp_jingle_session_t *sess =
rexmpp_jingle_session_create(s, strdup(from_jid), strdup(sid),
REXMPP_JINGLE_SESSION_MEDIA, 0);
- sess->rtcp_mux =
- (rexmpp_xml_find_child(rtp_description,
- "urn:xmpp:jingle:apps:rtp:1",
- "rtcp-mux") != NULL);
- sess->initiate = xmlCopyNode(jingle, 1);
-#endif
+ if (sess != NULL) {
+ sess->rtcp_mux =
+ (rexmpp_xml_find_child(rtp_description,
+ "urn:xmpp:jingle:apps:rtp:1",
+ "rtcp-mux") != NULL);
+ sess->initiate = rexmpp_xml_clone(jingle);
+ }
+#endif /* ENABLE_CALLS */
} else if (file_description == NULL &&
rtp_description == NULL) {
rexmpp_jingle_session_terminate
(s, sid,
- rexmpp_xml_new_node("unsupported-applications",
+ rexmpp_xml_new_elem("unsupported-applications",
"urn:xmpp:jingle:1"),
NULL);
} else if (ibb_transport == NULL &&
ice_udp_transport == NULL) {
- rexmpp_jingle_session_terminate(s, sid,
- rexmpp_xml_new_node("unsupported-transports",
- "urn:xmpp:jingle:1"),
- NULL);
+ rexmpp_jingle_session_terminate
+ (s, sid,
+ rexmpp_xml_new_elem("unsupported-transports",
+ "urn:xmpp:jingle:1"),
+ NULL);
} else {
/* todo: some other error */
}
@@ -1593,107 +1949,106 @@ int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem) {
rexmpp_iq_reply(s, elem, "result", NULL);
rexmpp_jingle_session_t *session = rexmpp_jingle_session_by_id(s, sid);
if (session != NULL) {
- session->accept = xmlCopyNode(jingle, 1);
- xmlNodePtr content =
+ session->accept = rexmpp_xml_clone(jingle);
+ rexmpp_xml_t *content =
rexmpp_xml_find_child(jingle, "urn:xmpp:jingle:1", "content");
- xmlNodePtr file_description =
+ rexmpp_xml_t *file_description =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:apps:file-transfer:5",
"description");
- xmlNodePtr ibb_transport =
+ rexmpp_xml_t *ibb_transport =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:transports:ibb:1",
"transport");
if (ibb_transport != NULL && file_description != NULL) {
- xmlNodePtr open =
- rexmpp_xml_new_node("open", "http://jabber.org/protocol/ibb");
- xmlNewProp(open, "sid", session->ibb_sid);
- xmlNewProp(open, "block-size", "4096");
- xmlNewProp(open, "stanza", "iq");
+ rexmpp_xml_t *open =
+ rexmpp_xml_new_elem("open", "http://jabber.org/protocol/ibb");
+ rexmpp_xml_add_attr(open, "sid", session->ibb_sid);
+ rexmpp_xml_add_attr(open, "block-size", "4096");
+ rexmpp_xml_add_attr(open, "stanza", "iq");
rexmpp_iq_new(s, "set", session->jid, open,
rexmpp_jingle_ibb_send_cb, strdup(sid));
} else {
#ifdef ENABLE_CALLS
- xmlNodePtr ice_udp_transport =
+ rexmpp_jingle_session_configure_audio(session);
+ rexmpp_jingle_run_audio(session);
+ rexmpp_xml_t *ice_udp_transport =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:transports:ice-udp:1",
"transport");
if (ice_udp_transport != NULL) {
rexmpp_jingle_ice_udp_add_remote(session, ice_udp_transport);
+ } else {
+ rexmpp_log(s, LOG_WARNING,
+ "ICE-UDP transport is unset in session-accept");
}
-#endif
+#endif /* ENABLE_CALLS */
}
+ } else {
+ rexmpp_log(s, LOG_WARNING, "Jingle session %s is not found", sid);
}
} else if (strcmp(action, "transport-info") == 0) {
rexmpp_iq_reply(s, elem, "result", NULL);
rexmpp_jingle_session_t *session = rexmpp_jingle_session_by_id(s, sid);
if (session != NULL) {
#ifdef ENABLE_CALLS
- xmlNodePtr content =
+ rexmpp_xml_t *content =
rexmpp_xml_find_child(jingle, "urn:xmpp:jingle:1", "content");
- xmlNodePtr ice_udp_transport =
+ rexmpp_xml_t *ice_udp_transport =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:transports:ice-udp:1",
"transport");
if (ice_udp_transport != NULL) {
rexmpp_jingle_ice_udp_add_remote(session, ice_udp_transport);
}
-#endif
+#endif /* ENABLE_CALLS */
}
} else {
rexmpp_log(s, LOG_WARNING, "Unknown Jingle action: %s", action);
- rexmpp_iq_reply(s, elem, "error", rexmpp_xml_error("cancel", "bad-request"));
+ rexmpp_iq_reply(s, elem, "error",
+ rexmpp_xml_error("cancel", "bad-request"));
}
} else {
rexmpp_log(s, LOG_WARNING, "Received a malformed Jingle element");
- rexmpp_iq_reply(s, elem, "error", rexmpp_xml_error("cancel", "bad-request"));
- }
- if (action != NULL) {
- free(action);
- }
- if (sid != NULL) {
- free(sid);
- }
- if (from_jid != NULL) {
- free(from_jid);
+ rexmpp_iq_reply(s, elem, "error",
+ rexmpp_xml_error("cancel", "bad-request"));
}
}
/* XEP-0261: Jingle In-Band Bytestreams Transport Method */
- xmlNodePtr ibb_open =
+ rexmpp_xml_t *ibb_open =
rexmpp_xml_find_child(elem, "http://jabber.org/protocol/ibb", "open");
if (ibb_open != NULL) {
handled = 1;
/* no-op, though could check sid here. */
rexmpp_iq_reply(s, elem, "result", NULL);
}
- xmlNodePtr ibb_close =
+ rexmpp_xml_t *ibb_close =
rexmpp_xml_find_child(elem, "http://jabber.org/protocol/ibb", "close");
if (ibb_close != NULL) {
handled = 1;
rexmpp_iq_reply(s, elem, "result", NULL);
- char *sid = xmlGetProp(ibb_close, "sid");
+ const char *sid = rexmpp_xml_find_attr_val(ibb_close, "sid");
if (sid != NULL) {
rexmpp_jingle_session_t *session = rexmpp_jingle_session_by_ibb_sid(s, sid);
if (session != NULL) {
rexmpp_jingle_session_terminate
(s, session->sid,
- rexmpp_xml_new_node("success", "urn:xmpp:jingle:1"), NULL);
+ rexmpp_xml_new_elem("success", "urn:xmpp:jingle:1"), NULL);
}
- free(sid);
}
}
- xmlNodePtr ibb_data =
+ rexmpp_xml_t *ibb_data =
rexmpp_xml_find_child(elem, "http://jabber.org/protocol/ibb", "data");
if (ibb_data != NULL) {
handled = 1;
- char *sid = xmlGetProp(ibb_data, "sid");
+ const char *sid = rexmpp_xml_find_attr_val(ibb_data, "sid");
if (sid != NULL) {
rexmpp_jingle_session_t *session = rexmpp_jingle_session_by_ibb_sid(s, sid);
if (session != NULL && session->ibb_fh != NULL) {
- char *data = NULL, *data_base64 = xmlNodeGetContent(ibb_data);
+ char *data = NULL;
+ const char *data_base64 = rexmpp_xml_text_child(ibb_data);
if (data_base64 != NULL) {
size_t data_len = 0;
int base64_err = rexmpp_base64_from(data_base64, strlen(data_base64),
- &data, &data_len);
- free(data_base64);
+ &data, &data_len);
if (base64_err != 0) {
rexmpp_log(s, LOG_ERR, "Base-64 decoding failure");
} else {
@@ -1706,7 +2061,6 @@ int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem) {
}
}
}
- free(sid);
}
/* todo: report errors */
rexmpp_iq_reply(s, elem, "result", NULL);
@@ -1719,7 +2073,7 @@ int rexmpp_jingle_fds(rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
#ifdef ENABLE_CALLS
gint poll_timeout;
GPollFD poll_fds[10];
- GMainContext* gctx = g_main_loop_get_context(s->jingle.gloop);
+ GMainContext* gctx = g_main_loop_get_context(s->jingle->gloop);
if (g_main_context_acquire(gctx)) {
gint poll_fds_n = g_main_context_query(gctx,
G_PRIORITY_HIGH,
@@ -1741,29 +2095,23 @@ int rexmpp_jingle_fds(rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
}
rexmpp_jingle_session_t *sess;
- for (sess = s->jingle.sessions; sess != NULL; sess = sess->next) {
+ for (sess = s->jingle->sessions; sess != NULL; sess = sess->next) {
for (i = 0; i < 2; i++) {
if (sess->component[i].dtls_state != REXMPP_TLS_INACTIVE &&
sess->component[i].dtls_state != REXMPP_TLS_CLOSED &&
sess->component[i].dtls_state != REXMPP_TLS_ERROR) {
- GSocket *sock =
- nice_agent_get_selected_socket(sess->ice_agent,
- sess->ice_stream_id,
- i + 1);
- if (sock != NULL) {
- int fd = g_socket_get_fd(sock);
- g_object_unref(sock);
+ GPtrArray *sockets =
+ nice_agent_get_sockets(sess->ice_agent,
+ sess->ice_stream_id, i + 1);
+ guint i;
+ for (i = 0; i < sockets->len; i++) {
+ int fd = g_socket_get_fd(sockets->pdata[i]);
FD_SET(fd, read_fds);
if (fd > nfds) {
nfds = fd;
}
}
- if (sess->component[i].udp_socket != -1) {
- FD_SET(sess->component[i].udp_socket, read_fds);
- if (sess->component[i].udp_socket > nfds) {
- nfds = sess->component[i].udp_socket;
- }
- }
+ g_ptr_array_unref(sockets);
}
}
}
@@ -1771,21 +2119,21 @@ int rexmpp_jingle_fds(rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
rexmpp_log(s, LOG_ERR,
"Failed to acquire GMainContext in rexmpp_jingle_fds");
}
-#else
+#else /* ENABLE_CALLS */
(void)s;
(void)read_fds;
(void)write_fds;
-#endif
+#endif /* ENABLE_CALLS */
return (nfds + 1);
}
-struct timeval * rexmpp_jingle_timeout (rexmpp_t *s,
- struct timeval *max_tv,
- struct timeval *tv) {
+struct timespec * rexmpp_jingle_timeout (rexmpp_t *s,
+ struct timespec *max_tv,
+ struct timespec *tv) {
#ifdef ENABLE_CALLS
gint poll_timeout;
GPollFD poll_fds[10];
- GMainContext* gctx = g_main_loop_get_context(s->jingle.gloop);
+ GMainContext* gctx = g_main_loop_get_context(s->jingle->gloop);
if (g_main_context_acquire(gctx)) {
g_main_context_query(gctx,
G_PRIORITY_HIGH,
@@ -1795,28 +2143,34 @@ struct timeval * rexmpp_jingle_timeout (rexmpp_t *s,
g_main_context_release(gctx);
rexmpp_jingle_session_t *sess;
- for (sess = s->jingle.sessions; sess != NULL; sess = sess->next) {
+ for (sess = s->jingle->sessions; sess != NULL; sess = sess->next) {
int i;
for (i = 0; i < 2; i++) {
if (sess->component[i].dtls_state != REXMPP_TLS_INACTIVE &&
sess->component[i].dtls_state != REXMPP_TLS_CLOSED &&
sess->component[i].dtls_state != REXMPP_TLS_ERROR) {
- int tms = gnutls_dtls_get_timeout(sess->component[i].dtls_session);
+ int tms = rexmpp_dtls_timeout(sess->s, sess->component[i].dtls);
if (tms > 0 && (poll_timeout < 0 || tms < poll_timeout)) {
poll_timeout = tms;
}
+ /* Set poll timeout to at most 5 ms if there are connected
+ components, for timely transmission of Jingle data. */
+ if (sess->component[i].dtls_state == REXMPP_TLS_ACTIVE &&
+ (poll_timeout < 0 || poll_timeout > 5)) {
+ poll_timeout = 5;
+ }
}
}
}
if (poll_timeout >= 0) {
int sec = poll_timeout / 1000;
- int usec = (poll_timeout % 1000) * 1000;
+ int nsec = (poll_timeout % 1000) * 1000000;
if (max_tv == NULL ||
(max_tv->tv_sec > sec ||
- (max_tv->tv_sec == sec && max_tv->tv_usec > usec))) {
+ (max_tv->tv_sec == sec && max_tv->tv_nsec > nsec))) {
tv->tv_sec = sec;
- tv->tv_usec = usec;
+ tv->tv_nsec = nsec;
max_tv = tv;
}
}
@@ -1824,10 +2178,10 @@ struct timeval * rexmpp_jingle_timeout (rexmpp_t *s,
rexmpp_log(s, LOG_ERR,
"Failed to acquire GMainContext in rexmpp_jingle_timeout");
}
-#else
+#else /* ENABLE_CALLS */
(void)s;
(void)tv;
-#endif
+#endif /* ENABLE_CALLS */
return max_tv;
}
@@ -1837,232 +2191,282 @@ rexmpp_jingle_run (rexmpp_t *s,
fd_set *write_fds)
{
(void)write_fds;
+ (void)read_fds;
#ifdef ENABLE_CALLS
rexmpp_jingle_session_t *sess;
- int key_mat_size;
- 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],
- server_sess_key[SRTP_AES_ICM_128_KEY_LEN_WSALT * 2];
- for (sess = s->jingle.sessions; sess != NULL; sess = sess->next) {
+ unsigned char key_mat[2 * (SRTP_AES_ICM_128_KEY_LEN_WSALT)],
+ client_sess_key[SRTP_AES_ICM_128_KEY_LEN_WSALT],
+ server_sess_key[SRTP_AES_ICM_128_KEY_LEN_WSALT];
+ for (sess = s->jingle->sessions; sess != NULL; sess = sess->next) {
char input[4096 + SRTP_MAX_TRAILER_LEN];
int input_len;
int comp_id;
+
for (comp_id = 0; comp_id < 2; comp_id++) {
rexmpp_jingle_component_t *comp = &sess->component[comp_id];
if (comp->dtls_state == REXMPP_TLS_HANDSHAKE) {
- int ret = gnutls_handshake(comp->dtls_session);
- if (ret == 0) {
+ int ret = rexmpp_tls_handshake(s, comp->dtls);
+ if (ret == REXMPP_TLS_SUCCESS) {
rexmpp_log(s, LOG_DEBUG,
"DTLS connected for Jingle session %s, component %d",
sess->sid, comp->component_id);
comp->dtls_state = REXMPP_TLS_ACTIVE;
- /* Verify the peer's fingerprint */
-
- unsigned int cert_list_size = 0;
- const gnutls_datum_t *cert_list;
- cert_list =
- gnutls_certificate_get_peers(comp->dtls_session, &cert_list_size);
- if (cert_list_size != 1) {
+ rexmpp_xml_t *jingle = comp->session->initiator
+ ? comp->session->accept
+ : comp->session->initiate;
+ rexmpp_xml_t *fingerprint =
+ rexmpp_xml_find_child
+ (rexmpp_xml_find_child
+ (rexmpp_xml_find_child
+ (jingle, "urn:xmpp:jingle:1", "content"),
+ "urn:xmpp:jingle:transports:ice-udp:1", "transport"),
+ "urn:xmpp:jingle:apps:dtls:0", "fingerprint");
+ if (fingerprint == NULL) {
+ /* todo: might be neater to check it upon receiving the
+ stanzas, instead of checking it here */
rexmpp_log(comp->s, LOG_ERR,
- "Unexpected peer certificate list size: %d",
- cert_list_size);
+ "No fingerprint in the peer's Jingle element");
rexmpp_jingle_session_terminate
(s, sess->sid,
- rexmpp_xml_new_node("security-error", "urn:xmpp:jingle:1"),
- "Unexpected certificate list size; expected exactly 1.");
+ rexmpp_xml_new_elem("connectivity-error", "urn:xmpp:jingle:1"),
+ "No fingerprint element");
+ return REXMPP_E_TLS;
} else {
- xmlNodePtr jingle = comp->session->initiator
- ? comp->session->accept
- : comp->session->initiate;
- xmlNodePtr fingerprint =
- rexmpp_xml_find_child
- (rexmpp_xml_find_child
- (rexmpp_xml_find_child
- (jingle, "urn:xmpp:jingle:1", "content"),
- "urn:xmpp:jingle:transports:ice-udp:1", "transport"),
- "urn:xmpp:jingle:apps:dtls:0", "fingerprint");
- if (fingerprint == NULL) {
- /* todo: might be neater to check it upon receiving the
- stanzas, instead of checking it here */
+ const char *hash_str = rexmpp_xml_find_attr_val(fingerprint, "hash");
+ if (hash_str == NULL) {
rexmpp_log(comp->s, LOG_ERR,
- "No fingerprint in the peer's Jingle element");
+ "No hash attribute in the peer's fingerprint element");
rexmpp_jingle_session_terminate
(s, sess->sid,
- rexmpp_xml_new_node("connectivity-error", "urn:xmpp:jingle:1"),
- "No fingerprint element");
+ rexmpp_xml_new_elem("connectivity-error", "urn:xmpp:jingle:1"),
+ "No hash attribute in the fingerprint element");
+ return REXMPP_E_TLS;
} else {
- char *hash_str = xmlGetProp(fingerprint, "hash");
- if (hash_str == NULL) {
- rexmpp_log(comp->s, LOG_ERR,
- "No hash attribute in the peer's fingerprint element");
- rexmpp_jingle_session_terminate
- (s, sess->sid,
- rexmpp_xml_new_node("connectivity-error", "urn:xmpp:jingle:1"),
- "No hash attribute in the fingerprint element");
- break;
- } else {
- gnutls_digest_algorithm_t algo = GNUTLS_DIG_UNKNOWN;
- /* gnutls_digest_get_id uses different names, so
- checking manually here. These are SDP options,
- <https://datatracker.ietf.org/doc/html/rfc4572#page-8>. */
- if (strcmp(hash_str, "sha-1") == 0) {
- algo = GNUTLS_DIG_SHA1;
- } else if (strcmp(hash_str, "sha-224") == 0) {
- algo = GNUTLS_DIG_SHA224;
- } else if (strcmp(hash_str, "sha-256") == 0) {
- algo = GNUTLS_DIG_SHA256;
- } else if (strcmp(hash_str, "sha-384") == 0) {
- algo = GNUTLS_DIG_SHA384;
- } else if (strcmp(hash_str, "sha-512") == 0) {
- algo = GNUTLS_DIG_SHA512;
- } else if (strcmp(hash_str, "md5") == 0) {
- algo = GNUTLS_DIG_MD5;
- }
- free(hash_str);
- if (algo == GNUTLS_DIG_UNKNOWN) {
+ char fp[64], fp_str[64 * 3];
+ size_t fp_size = 64;
+ if (rexmpp_tls_peer_fp(comp->s, comp->dtls, hash_str,
+ fp, fp_str, &fp_size))
+ {
+ rexmpp_jingle_session_terminate
+ (s, sess->sid,
+ rexmpp_xml_new_elem("connectivity-error",
+ "urn:xmpp:jingle:1"),
+ "Failed to obtain the DTLS certificate fingerprint");
+ return REXMPP_E_TLS;
+ } else {
+ const char *fingerprint_cont =
+ rexmpp_xml_text_child(fingerprint);
+ /* Fingerprint string should be uppercase, but
+ allowing any case for now, while Dino uses
+ lowercase. */
+ int fingerprint_mismatch = strcasecmp(fingerprint_cont, fp_str);
+ if (fingerprint_mismatch) {
rexmpp_log(comp->s, LOG_ERR,
- "Unknown hash algorithm in the peer's fingerprint");
+ "Peer's fingerprint mismatch: expected %s,"
+ " calculated %s",
+ fingerprint_cont, fp_str);
rexmpp_jingle_session_terminate
(s, sess->sid,
- rexmpp_xml_new_node("connectivity-error", "urn:xmpp:jingle:1"),
- "Unknown hash algorithm for a DTLS certificate fingerprint");
- break;
+ rexmpp_xml_new_elem("security-error", "urn:xmpp:jingle:1"),
+ "DTLS certificate fingerprint mismatch");
+ return REXMPP_E_TLS;
} else {
-
- char fp[64], fp_str[64 * 3];
- size_t fp_size = 64;
- gnutls_fingerprint(algo, cert_list, fp, &fp_size);
- size_t i;
- for (i = 0; i < fp_size; i++) {
- snprintf(fp_str + i * 3, 4, "%02X:", fp[i] & 0xFF);
+ /* The fingerprint is fine, proceed to SRTP. */
+ rexmpp_log(comp->s, LOG_DEBUG,
+ "Peer's fingerprint: %s", fp_str);
+
+ rexmpp_tls_srtp_get_keys(s, comp->dtls,
+ SRTP_AES_128_KEY_LEN, SRTP_SALT_LEN,
+ key_mat);
+ /* client key */
+ memcpy(client_sess_key, key_mat, SRTP_AES_128_KEY_LEN);
+ /* server key */
+ memcpy(server_sess_key, key_mat + SRTP_AES_128_KEY_LEN,
+ SRTP_AES_128_KEY_LEN);
+ /* client salt */
+ memcpy(client_sess_key + SRTP_AES_128_KEY_LEN,
+ key_mat + SRTP_AES_128_KEY_LEN * 2,
+ SRTP_SALT_LEN);
+ /* server salt */
+ memcpy(server_sess_key + SRTP_AES_128_KEY_LEN,
+ key_mat + SRTP_AES_128_KEY_LEN * 2 + SRTP_SALT_LEN,
+ SRTP_SALT_LEN);
+
+ int active_role = rexmpp_jingle_dtls_is_active(sess, 0);
+
+ srtp_policy_t inbound;
+ memset(&inbound, 0x0, sizeof(srtp_policy_t));
+ srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtp);
+ srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtcp);
+ inbound.ssrc.type = ssrc_any_inbound;
+ inbound.key = active_role ? server_sess_key : client_sess_key;
+ inbound.window_size = 1024;
+ inbound.allow_repeat_tx = 1;
+ inbound.next = NULL;
+ err = srtp_create(&(comp->srtp_in), &inbound);
+ if (err) {
+ rexmpp_log(s, LOG_ERR, "Failed to create srtp_in");
}
- fp_str[fp_size * 3 - 1] = 0;
-
- char *fingerprint_cont = xmlNodeGetContent(fingerprint);
- /* Fingerprint string should be uppercase, but
- allowing any case for now, while Dino uses
- lowercase. */
- int fingerprint_mismatch = strcasecmp(fingerprint_cont, fp_str);
- free(fingerprint_cont);
- if (fingerprint_mismatch) {
- rexmpp_log(comp->s, LOG_ERR,
- "Peer's fingerprint mismatch: expected %s, calculated %s",
- fingerprint_cont, fp_str);
- rexmpp_jingle_session_terminate
- (s, sess->sid,
- rexmpp_xml_new_node("security-error", "urn:xmpp:jingle:1"),
- "DTLS certificate fingerprint mismatch");
- break;
- } else {
- /* The fingerprint is fine, proceed to SRTP. */
- rexmpp_log(comp->s, LOG_DEBUG, "Peer's fingerprint: %s", fp_str);
-
- key_mat_size =
- gnutls_srtp_get_keys(comp->dtls_session, key_mat,
- SRTP_AES_ICM_128_KEY_LEN_WSALT * 2,
- &client_key, &client_salt,
- &server_key, &server_salt);
- rexmpp_log(s, LOG_DEBUG, "SRTP key material size: %d",
- key_mat_size);
- memcpy(client_sess_key, client_key.data,
- SRTP_AES_128_KEY_LEN);
- memcpy(client_sess_key + SRTP_AES_128_KEY_LEN,
- client_salt.data, SRTP_SALT_LEN);
-
- memcpy(server_sess_key, server_key.data,
- SRTP_AES_128_KEY_LEN);
- memcpy(server_sess_key + SRTP_AES_128_KEY_LEN,
- server_salt.data, SRTP_SALT_LEN);
-
- int active_role = rexmpp_jingle_dtls_is_active(sess, 0);
-
- srtp_policy_t inbound;
- memset(&inbound, 0x0, sizeof(srtp_policy_t));
- srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtp);
- srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtcp);
- inbound.ssrc.type = ssrc_any_inbound;
- inbound.key = active_role ? server_sess_key : client_sess_key;
- inbound.window_size = 1024;
- inbound.allow_repeat_tx = 1;
- inbound.next = NULL;
- err = srtp_create(&(comp->srtp_in), &inbound);
- if (err) {
- rexmpp_log(s, LOG_ERR, "Failed to create srtp_in");
- }
- srtp_policy_t outbound;
- memset(&outbound, 0x0, sizeof(srtp_policy_t));
- srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtp);
- srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtcp);
- outbound.ssrc.type = ssrc_any_outbound;
- outbound.key = active_role ? client_sess_key : server_sess_key;
- outbound.window_size = 1024;
- outbound.allow_repeat_tx = 1;
- outbound.next = NULL;
- err = srtp_create(&(comp->srtp_out), &outbound);
- if (err) {
- rexmpp_log(s, LOG_ERR, "Failed to create srtp_out");
- }
+ srtp_policy_t outbound;
+ memset(&outbound, 0x0, sizeof(srtp_policy_t));
+ srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtp);
+ srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtcp);
+ outbound.ssrc.type = ssrc_any_outbound;
+ outbound.key = active_role ? client_sess_key : server_sess_key;
+ outbound.window_size = 1024;
+ outbound.allow_repeat_tx = 1;
+ outbound.next = NULL;
+ err = srtp_create(&(comp->srtp_out), &outbound);
+ if (err) {
+ rexmpp_log(s, LOG_ERR, "Failed to create srtp_out");
}
}
}
}
}
- } else if (ret != GNUTLS_E_AGAIN) {
- rexmpp_log(s, LOG_ERR, "DTLS error for session %s, component %d: %s",
- sess->sid, comp->component_id, gnutls_strerror(ret));
+ } else if (ret != REXMPP_TLS_E_AGAIN) {
+ rexmpp_log(s, LOG_ERR, "DTLS error for session %s, component %d",
+ sess->sid, comp->component_id);
comp->dtls_state = REXMPP_TLS_ERROR;
if (comp->component_id == 1) {
rexmpp_jingle_session_terminate
(s, sess->sid,
- rexmpp_xml_new_node("connectivity-error", "urn:xmpp:jingle:1"),
+ rexmpp_xml_new_elem("connectivity-error", "urn:xmpp:jingle:1"),
"DTLS connection error");
- break;
+ return REXMPP_E_TLS;
}
}
}
- /* Handle outbound packets */
- srtp_ctx_t *srtp_out;
+ /* Check on the DTLS session, too. */
if (comp->dtls_state == REXMPP_TLS_ACTIVE) {
- srtp_out = comp->srtp_out;
- } else if ((comp->dtls_state == REXMPP_TLS_ERROR || comp->session->rtcp_mux) &&
- comp->session->component[0].dtls_state == REXMPP_TLS_ACTIVE) {
- /* Try to reuse the first component's session. */
- srtp_out = comp->session->component[0].srtp_out;
- } else {
- break;
+ ssize_t received;
+ rexmpp_tls_err_t err =
+ rexmpp_tls_recv(s, comp->dtls, input, 4096, &received);
+ if (err != REXMPP_TLS_SUCCESS && err != REXMPP_TLS_E_AGAIN) {
+ rexmpp_log(s, LOG_ERR,
+ "Error on rexmpp_tls_recv (component id %d), "
+ "terminating Jingle session %s",
+ comp->component_id, sess->sid);
+ rexmpp_jingle_session_terminate
+ (s, sess->sid,
+ rexmpp_xml_new_elem("connectivity-error", "urn:xmpp:jingle:1"),
+ "TLS reading error");
+ return REXMPP_E_TLS;
+ }
}
+ }
- if (FD_ISSET(comp->udp_socket, read_fds)) {
- input_len = recv(comp->udp_socket, input, 4096, 0);
- if (comp->component_id == 1) {
- err = srtp_protect(srtp_out, input, &input_len);
- } else {
- err = srtp_protect_rtcp(srtp_out, input, &input_len);
+ /* Send captured audio frames for established sessions. */
+ if ((sess->component[0].dtls_state == REXMPP_TLS_ACTIVE)
+ && (sess->component[0].srtp_out != NULL)
+ && (sess->codec != REXMPP_CODEC_UNDEFINED)) {
+ struct ring_buf *capture = &(sess->ring_buffers.capture);
+ while (capture->write_pos != capture->read_pos) {
+ if (sess->codec == REXMPP_CODEC_PCMU) {
+ for (input_len = 12;
+ input_len < 4096 && capture->write_pos != capture->read_pos;
+ input_len++)
+ {
+ input[input_len] =
+ rexmpp_pcmu_encode(capture->buf[capture->read_pos]);
+ capture->read_pos++;
+ capture->read_pos %= PA_BUF_SIZE;
+ sess->rtp_timestamp++;
+ }
+ } else if (sess->codec == REXMPP_CODEC_PCMA) {
+ for (input_len = 12;
+ input_len < 4096 && capture->write_pos != capture->read_pos;
+ input_len++)
+ {
+ input[input_len] =
+ rexmpp_pcma_encode(capture->buf[capture->read_pos]);
+ capture->read_pos++;
+ capture->read_pos %= PA_BUF_SIZE;
+ sess->rtp_timestamp++;
+ }
+ }
+#ifdef HAVE_OPUS
+ else if (sess->codec == REXMPP_CODEC_OPUS) {
+ unsigned int samples_available;
+ if (capture->write_pos > capture->read_pos) {
+ samples_available = capture->write_pos - capture->read_pos;
+ } else {
+ samples_available =
+ (PA_BUF_SIZE - capture->read_pos) + capture->write_pos;
+ }
+ unsigned int frame_size = 480;
+ if (samples_available >= frame_size * 2) {
+ opus_int16 pcm[4096];
+ /* Prepare a regular buffer */
+ unsigned int i;
+ for (i = 0;
+ (i < frame_size * 2) &&
+ (capture->write_pos != capture->read_pos);
+ i++)
+ {
+ pcm[i] = capture->buf[capture->read_pos];
+ capture->read_pos++;
+ capture->read_pos %= PA_BUF_SIZE;
+ sess->rtp_timestamp++;
+ }
+ /* Encode it */
+ int encoded_len;
+ encoded_len = opus_encode(sess->opus_enc,
+ pcm,
+ frame_size,
+ (unsigned char*)input + 12,
+ 4096);
+ if (encoded_len < 0) {
+ rexmpp_log(s, LOG_ERR, "Failed to encode an Opus frame");
+ break;
+ }
+ input_len = 12 + encoded_len;
+ } else {
+ break;
+ }
}
+#endif /* HAVE_OPUS */
+
+ /* Setup an RTP header */
+ uint32_t hl, nl;
+ hl = (2 << 30) /* version */
+ | (0 << 29) /* padding */
+ | (0 << 28) /* extension */
+ | (0 << 24) /* CSRC count */
+ | (0 << 23) /* marker */
+ | (sess->payload_type << 16) /* paylaod type, RFC 3551 */
+ | sess->rtp_seq_num;
+ sess->rtp_seq_num++;
+ nl = htonl(hl);
+ memcpy(input, &nl, sizeof(uint32_t));
+ nl = htonl(sess->rtp_timestamp);
+ memcpy(input + 4, &nl, sizeof(uint32_t));
+ nl = htonl(sess->rtp_ssrc);
+ memcpy(input + 8, &nl, sizeof(uint32_t));
+ /* The RTP header is ready */
+
+ srtp_ctx_t *srtp_out = sess->component[0].srtp_out;
+ err = srtp_protect(srtp_out, input, &input_len);
if (err) {
- rexmpp_log(s, LOG_ERR, "SRT(C)P protect error %d\n", err);
+ rexmpp_log(s, LOG_ERR, "SRTP protect error %d", err);
} else {
nice_agent_send(sess->ice_agent, sess->ice_stream_id,
- sess->rtcp_mux ? 1 : comp->component_id,
+ /* sess->rtcp_mux ? 1 : comp->component_id, */
+ 1,
input_len, input);
}
}
- /* Check on the DTLS session too. */
- if (comp->dtls_state == REXMPP_TLS_ACTIVE) {
- input_len = gnutls_record_recv(comp->dtls_session, input, 4096);
- }
}
}
- g_main_context_iteration(g_main_loop_get_context(s->jingle.gloop), 0);
-#else
+ g_main_context_iteration(g_main_loop_get_context(s->jingle->gloop), 0);
+#else /* ENABLE_CALLS */
(void)s;
(void)read_fds;
-#endif
+#endif /* ENABLE_CALLS */
return REXMPP_SUCCESS;
}
diff --git a/src/rexmpp_jingle.h b/src/rexmpp_jingle.h
index cd23efb..189614a 100644
--- a/src/rexmpp_jingle.h
+++ b/src/rexmpp_jingle.h
@@ -16,15 +16,20 @@
#ifdef ENABLE_CALLS
#include <glib.h>
#include <agent.h>
-#include <gnutls/gnutls.h>
#include <srtp2/srtp.h>
-#define DTLS_SRTP_BUF_SIZE 0x4000
+#include "portaudio.h"
+#ifdef HAVE_OPUS
+#include <opus/opus.h>
+#endif
+#define PA_BUF_SIZE 0x4000
#endif
#include "rexmpp.h"
+#include "rexmpp_tls.h"
+
/** @brief Processes incoming Jingle IQs. */
-int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem);
+int rexmpp_jingle_iq (rexmpp_t *s, rexmpp_xml_t *elem);
/** @brief Destroys Jingle sessions. */
void rexmpp_jingle_stop (rexmpp_t *s);
@@ -45,7 +50,7 @@ rexmpp_jingle_send_file (rexmpp_t *s,
rexmpp_err_t
rexmpp_jingle_session_terminate (rexmpp_t *s,
const char *sid,
- xmlNodePtr reason_node,
+ rexmpp_xml_t *reason_node,
const char *reason_text);
typedef struct rexmpp_jingle_component rexmpp_jingle_component_t;
@@ -57,6 +62,13 @@ enum rexmpp_jingle_session_type {
REXMPP_JINGLE_SESSION_MEDIA
};
+enum rexmpp_codec {
+ REXMPP_CODEC_UNDEFINED,
+ REXMPP_CODEC_PCMU,
+ REXMPP_CODEC_PCMA,
+ REXMPP_CODEC_OPUS
+};
+
#ifdef ENABLE_CALLS
/* A structure used for callbacks, to pass rexmpp_t,
rexmpp_jingle_session_t, and the component ID. */
@@ -64,23 +76,31 @@ struct rexmpp_jingle_component {
rexmpp_t *s;
rexmpp_jingle_session_t *session;
int component_id;
- gnutls_session_t dtls_session;
- char dtls_buf[DTLS_SRTP_BUF_SIZE];
+ rexmpp_tls_t *dtls;
enum tls_st dtls_state;
- size_t dtls_buf_len;
srtp_t srtp_in;
srtp_t srtp_out;
- uint16_t udp_port_in;
- uint16_t udp_port_out;
- int udp_socket;
+};
+
+struct ring_buf
+{
+ int16_t buf[PA_BUF_SIZE];
+ unsigned int write_pos;
+ unsigned int read_pos;
+};
+
+struct pa_buffers
+{
+ struct ring_buf capture;
+ struct ring_buf playback;
};
#endif
struct rexmpp_jingle_session {
char *jid;
char *sid;
- xmlNodePtr initiate;
- xmlNodePtr accept;
+ rexmpp_xml_t *initiate;
+ rexmpp_xml_t *accept;
rexmpp_jingle_session_t *next;
/* Sessions are commonly passed to callbacks by external libraries,
so it's convenient to have the corresponding rexmpp_t accessible
@@ -107,13 +127,25 @@ struct rexmpp_jingle_session {
int rtcp_mux;
NiceAgent *ice_agent;
int ice_stream_id;
-#endif
+ PaStream *pa_stream;
+ /* The default codec and payload type for this stream. */
+ enum rexmpp_codec codec;
+ uint8_t payload_type;
+ struct pa_buffers ring_buffers;
+ uint16_t rtp_seq_num;
+ uint16_t rtp_last_seq_num;
+ uint32_t rtp_timestamp;
+ uint32_t rtp_ssrc;
+#ifdef HAVE_OPUS
+ OpusEncoder *opus_enc;
+ OpusDecoder *opus_dec;
+#endif /* HAVE_POUS */
+#endif /* ENABLE_CALLS */
};
struct rexmpp_jingle_ctx {
#ifdef ENABLE_CALLS
GMainLoop* gloop;
- gnutls_certificate_credentials_t dtls_cred;
#endif
rexmpp_jingle_session_t *sessions;
};
@@ -121,20 +153,16 @@ struct rexmpp_jingle_ctx {
int rexmpp_jingle_init (rexmpp_t *s);
rexmpp_err_t rexmpp_jingle_run (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds);
-struct timeval * rexmpp_jingle_timeout (rexmpp_t *s,
- struct timeval *max_tv,
- struct timeval *tv);
+struct timespec * rexmpp_jingle_timeout (rexmpp_t *s,
+ struct timespec *max_tv,
+ struct timespec *tv);
int rexmpp_jingle_fds(rexmpp_t *s, fd_set *read_fds, fd_set *write_fds);
rexmpp_err_t
rexmpp_jingle_call (rexmpp_t *s,
- const char *jid,
- uint16_t rtp_port_in,
- uint16_t rtp_port_out);
+ const char *jid);
rexmpp_err_t
rexmpp_jingle_call_accept (rexmpp_t *s,
- const char *sid,
- uint16_t rtp_port_in,
- uint16_t rtp_port_out);
+ const char *sid);
#endif
diff --git a/src/rexmpp_openpgp.c b/src/rexmpp_openpgp.c
index 13cee44..14e2d35 100644
--- a/src/rexmpp_openpgp.c
+++ b/src/rexmpp_openpgp.c
@@ -47,22 +47,23 @@ Possible future improvements:
#ifdef HAVE_GPGME
#include <gpgme.h>
#endif
-#include <libxml/tree.h>
#include <gcrypt.h>
#include "rexmpp.h"
+#include "rexmpp_xml.h"
#include "rexmpp_openpgp.h"
#include "rexmpp_jid.h"
#include "rexmpp_pubsub.h"
#include "rexmpp_base64.h"
+#include "rexmpp_random.h"
#ifdef HAVE_GPGME
void rexmpp_pgp_fp_reply (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)ptr;
@@ -71,33 +72,33 @@ void rexmpp_pgp_fp_reply (rexmpp_t *s,
rexmpp_log(s, LOG_WARNING, "Failed to retrieve an OpenpPGP key");
return;
}
- xmlNodePtr pubsub =
+ rexmpp_xml_t *pubsub =
rexmpp_xml_find_child(response, "http://jabber.org/protocol/pubsub",
"pubsub");
if (pubsub == NULL) {
rexmpp_log(s, LOG_ERR, "OpenPGP key retrieval: not a pubsub response");
return;
}
- xmlNodePtr items =
- rexmpp_xml_find_child(pubsub, "http://jabber.org/protocol/pubsub",
- "items");
+ rexmpp_xml_t *items =
+ rexmpp_xml_find_child(pubsub, "http://jabber.org/protocol/pubsub",
+ "items");
if (items == NULL) {
rexmpp_log(s, LOG_ERR, "OpenPGP key retrieval: no items in pubsub element");
return;
}
- xmlNodePtr item =
+ rexmpp_xml_t *item =
rexmpp_xml_find_child(items, "http://jabber.org/protocol/pubsub", "item");
if (item == NULL) {
rexmpp_log(s, LOG_ERR, "OpenPGP key retrieval: no item in items");
return;
}
- xmlNodePtr pubkey =
+ rexmpp_xml_t *pubkey =
rexmpp_xml_find_child(item, "urn:xmpp:openpgp:0", "pubkey");
if (pubkey == NULL) {
rexmpp_log(s, LOG_ERR, "OpenPGP key retrieval: no pubkey in item");
return;
}
- xmlNodePtr data =
+ rexmpp_xml_t *data =
rexmpp_xml_find_child(pubkey, "urn:xmpp:openpgp:0", "data");
if (data == NULL) {
rexmpp_log(s, LOG_ERR, "OpenPGP key retrieval: no data in pubkey");
@@ -106,10 +107,9 @@ void rexmpp_pgp_fp_reply (rexmpp_t *s,
char *key_raw = NULL;
size_t key_raw_len = 0;
- char *key_base64 = xmlNodeGetContent(data);
+ const char *key_base64 = rexmpp_xml_text_child(data);
int base64_err =
rexmpp_base64_from(key_base64, strlen(key_base64), &key_raw, &key_raw_len);
- free(key_base64);
if (base64_err != 0) {
rexmpp_log(s, LOG_ERR, "Base-64 key decoding failure");
return;
@@ -140,18 +140,19 @@ void rexmpp_pgp_fp_reply (rexmpp_t *s,
rexmpp_err_t
rexmpp_openpgp_check_keys (rexmpp_t *s,
const char *jid,
- xmlNodePtr items)
+ rexmpp_xml_t *items)
{
- xmlNodePtr item =
+ rexmpp_xml_t *item =
rexmpp_xml_find_child(items, "http://jabber.org/protocol/pubsub#event",
"item");
- xmlNodePtr list =
- rexmpp_xml_find_child(item, "urn:xmpp:openpgp:0", "public-keys-list");
- xmlNodePtr metadata;
- for (metadata = xmlFirstElementChild(list);
+ rexmpp_xml_t *list =
+ rexmpp_xml_find_child(item, "urn:xmpp:openpgp:0", "public-keys-list");
+ rexmpp_xml_t *metadata;
+ for (metadata = rexmpp_xml_first_elem_child(list);
metadata != NULL;
- metadata = xmlNextElementSibling(metadata)) {
- char *fingerprint = xmlGetProp(metadata, "v4-fingerprint");
+ metadata = rexmpp_xml_next_elem_sibling(metadata)) {
+ const char *fingerprint =
+ rexmpp_xml_find_attr_val(metadata, "v4-fingerprint");
gpgme_key_t key;
gpgme_error_t err;
err = gpgme_get_key(s->pgp_ctx, fingerprint, &key, 0);
@@ -162,97 +163,95 @@ rexmpp_openpgp_check_keys (rexmpp_t *s,
rexmpp_log(s, LOG_DEBUG,
"Unknown OpenPGP key fingerprint for %s: %s",
jid, fingerprint);
- xmlNodePtr fp_req = xmlNewNode(NULL, "pubsub");
- xmlNewNs(fp_req, "http://jabber.org/protocol/pubsub", NULL);
- xmlNodePtr fp_req_items = xmlNewNode(NULL, "items");
- xmlNewProp(fp_req_items, "max_items", "1");
+ rexmpp_xml_t *fp_req =
+ rexmpp_xml_new_elem("pubsub", "http://jabber.org/protocol/pubsub");
+ rexmpp_xml_t *fp_req_items =
+ rexmpp_xml_new_elem("items", NULL);
+ rexmpp_xml_add_attr(fp_req_items, "max_items", "1");
char key_node[72];
snprintf(key_node, 72, "urn:xmpp:openpgp:0:public-keys:%s", fingerprint);
- xmlNewProp(fp_req_items, "node", key_node);
- xmlAddChild(fp_req, fp_req_items);
+ rexmpp_xml_add_attr(fp_req_items, "node", key_node);
+ rexmpp_xml_add_child(fp_req, fp_req_items);
rexmpp_iq_new(s, "get", jid, fp_req, rexmpp_pgp_fp_reply, NULL);
} else if (gpg_err_code(err) != GPG_ERR_NO_ERROR) {
rexmpp_log(s, LOG_WARNING,
"OpenPGP error when looking for a key: %s",
gpgme_strerror(err));
}
- free(fingerprint);
}
return REXMPP_SUCCESS;
}
-xmlNodePtr rexmpp_published_fingerprints (rexmpp_t *s, const char *jid) {
- xmlNodePtr published =
+rexmpp_xml_t *rexmpp_published_fingerprints (rexmpp_t *s, const char *jid) {
+ rexmpp_xml_t *published =
rexmpp_find_event(s, jid, "urn:xmpp:openpgp:0:public-keys", NULL);
if (published == NULL) {
return NULL;
}
- xmlNodePtr event =
+ rexmpp_xml_t *event =
rexmpp_xml_find_child(published, "http://jabber.org/protocol/pubsub#event",
"event");
- xmlNodePtr items =
+ rexmpp_xml_t *items =
rexmpp_xml_find_child(event, "http://jabber.org/protocol/pubsub#event",
"items");
- xmlNodePtr item =
+ rexmpp_xml_t *item =
rexmpp_xml_find_child(items, "http://jabber.org/protocol/pubsub#event",
"item");
- xmlNodePtr list =
+ rexmpp_xml_t *list =
rexmpp_xml_find_child(item, "urn:xmpp:openpgp:0",
"public-keys-list");
- xmlNodePtr published_fps = xmlFirstElementChild(list);
+ rexmpp_xml_t *published_fps = list->alt.elem.children;
return published_fps;
}
int rexmpp_openpgp_key_is_published (rexmpp_t *s, const char *fp) {
- xmlNodePtr metadata;
+ rexmpp_xml_t *metadata;
for (metadata = rexmpp_published_fingerprints(s, s->assigned_jid.bare);
metadata != NULL;
- metadata = xmlNextElementSibling(metadata)) {
+ metadata = metadata->next) {
if (! rexmpp_xml_match(metadata, "urn:xmpp:openpgp:0", "pubkey-metadata")) {
continue;
}
- char *fingerprint = xmlGetProp(metadata, "v4-fingerprint");
+ const char *fingerprint = rexmpp_xml_find_attr_val(metadata, "v4-fingerprint");
if (fingerprint == NULL) {
rexmpp_log(s, LOG_WARNING, "No fingerprint found in pubkey-metadata");
continue;
}
- int matches = (strcmp(fingerprint, fp) == 0);
- free(fingerprint);
- if (matches) {
+ if (strcmp(fingerprint, fp) == 0) {
return 1;
}
}
return 0;
}
-xmlNodePtr
+rexmpp_xml_t *
rexmpp_openpgp_remove_key_from_list (rexmpp_t *s,
const char *fp)
{
- xmlNodePtr fps =
- xmlCopyNodeList(rexmpp_published_fingerprints(s, s->assigned_jid.bare));
- xmlNodePtr metadata, prev = NULL;
+ rexmpp_xml_t *fps =
+ rexmpp_xml_clone_list(rexmpp_published_fingerprints(s, s->assigned_jid.bare));
+ rexmpp_xml_t *metadata, *prev = NULL;
for (metadata = fps;
metadata != NULL;
- prev = metadata, metadata = xmlNextElementSibling(metadata)) {
+ prev = metadata, metadata = rexmpp_xml_next_elem_sibling(metadata)) {
if (! rexmpp_xml_match(metadata, "urn:xmpp:openpgp:0", "pubkey-metadata")) {
continue;
}
- char *fingerprint = xmlGetProp(metadata, "v4-fingerprint");
+ const char *fingerprint =
+ rexmpp_xml_find_attr_val(metadata, "v4-fingerprint");
if (fingerprint == NULL) {
rexmpp_log(s, LOG_WARNING, "No fingerprint found in pubkey-metadata");
continue;
}
int matches = (strcmp(fingerprint, fp) == 0);
- free(fingerprint);
if (matches) {
if (prev != NULL) {
prev->next = metadata->next;
} else {
fps = metadata->next;
}
- xmlFreeNode(metadata);
+ rexmpp_xml_free(metadata);
return fps;
}
}
@@ -261,8 +260,8 @@ rexmpp_openpgp_remove_key_from_list (rexmpp_t *s,
void rexmpp_pgp_key_publish_list_iq (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)ptr;
@@ -275,18 +274,18 @@ void rexmpp_pgp_key_publish_list_iq (rexmpp_t *s,
rexmpp_log(s, LOG_INFO, "Published an OpenpPGP key list");
}
-void rexmpp_pgp_key_fp_list_upload (rexmpp_t *s, xmlNodePtr metadata) {
- xmlNodePtr keylist = xmlNewNode(NULL, "public-keys-list");
- xmlNewNs(keylist, "urn:xmpp:openpgp:0", NULL);
- xmlAddChild(keylist, metadata);
+void rexmpp_pgp_key_fp_list_upload (rexmpp_t *s, rexmpp_xml_t *metadata) {
+ rexmpp_xml_t *keylist =
+ rexmpp_xml_new_elem("public-keys-list", "urn:xmpp:openpgp:0");
+ rexmpp_xml_add_child(keylist, metadata);
rexmpp_pubsub_item_publish(s, NULL, "urn:xmpp:openpgp:0:public-keys",
NULL, keylist, rexmpp_pgp_key_publish_list_iq, NULL);
}
void rexmpp_pgp_key_delete_iq (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)ptr;
@@ -295,18 +294,17 @@ void rexmpp_pgp_key_delete_iq (rexmpp_t *s,
rexmpp_log(s, LOG_WARNING, "Failed to delete an OpenpPGP key");
return;
}
- xmlNodePtr pubsub = xmlFirstElementChild(req);
- xmlNodePtr publish = xmlFirstElementChild(pubsub);
- char *node = xmlGetProp(publish, "node");
- char *fingerprint = node + 31;
+ rexmpp_xml_t *pubsub = req->alt.elem.children;
+ rexmpp_xml_t *publish = pubsub->alt.elem.children;;
+ const char *node = rexmpp_xml_find_attr_val(publish, "node");
+ const char *fingerprint = node + 31;
rexmpp_log(s, LOG_INFO, "Removed OpenpPGP key %s", fingerprint);
- free(node);
}
void rexmpp_pgp_key_publish_iq (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)ptr;
@@ -316,10 +314,10 @@ void rexmpp_pgp_key_publish_iq (rexmpp_t *s,
return;
}
rexmpp_log(s, LOG_INFO, "Uploaded an OpenpPGP key");
- xmlNodePtr pubsub = xmlFirstElementChild(req);
- xmlNodePtr publish = xmlFirstElementChild(pubsub);
- char *node = xmlGetProp(publish, "node");
- char *fingerprint = node + 31;
+ rexmpp_xml_t *pubsub = req->alt.elem.children;
+ rexmpp_xml_t *publish = pubsub->alt.elem.children;;
+ const char *node = rexmpp_xml_find_attr_val(publish, "node");
+ const char *fingerprint = node + 31;
char time_str[42];
time_t t = time(NULL);
@@ -327,22 +325,20 @@ void rexmpp_pgp_key_publish_iq (rexmpp_t *s,
gmtime_r(&t, &utc_time);
strftime(time_str, 42, "%FT%TZ", &utc_time);
- xmlNodePtr metadata = xmlNewNode(NULL, "pubkey-metadata");
- xmlNewNs(metadata, "urn:xmpp:openpgp:0", NULL);
- xmlNewProp(metadata, "date", time_str);
- xmlNewProp(metadata, "v4-fingerprint", fingerprint);
+ rexmpp_xml_t *metadata =
+ rexmpp_xml_new_elem("pubkey-metadata", "urn:xmpp:openpgp:0");
+ rexmpp_xml_add_attr(metadata, "date", time_str);
+ rexmpp_xml_add_attr(metadata, "v4-fingerprint", fingerprint);
- free(node);
-
- xmlNodePtr fps = rexmpp_openpgp_remove_key_from_list(s, fingerprint);
+ rexmpp_xml_t *fps = rexmpp_openpgp_remove_key_from_list(s, fingerprint);
if (fps != NULL) {
- metadata->next = xmlCopyNodeList(fps);
+ metadata->next = fps;
}
rexmpp_pgp_key_fp_list_upload(s, metadata);
}
void rexmpp_openpgp_retract_key (rexmpp_t *s, const char *fp) {
- xmlNodePtr new_fp_list = rexmpp_openpgp_remove_key_from_list(s, fp);
+ rexmpp_xml_t *new_fp_list = rexmpp_openpgp_remove_key_from_list(s, fp);
if (new_fp_list != NULL) {
rexmpp_pgp_key_fp_list_upload(s, new_fp_list);
}
@@ -382,14 +378,14 @@ rexmpp_err_t rexmpp_openpgp_publish_key (rexmpp_t *s, const char *fp) {
key_raw = gpgme_data_release_and_get_mem(key_dh, &key_raw_len);
rexmpp_base64_to(key_raw, key_raw_len, &key_base64, &key_base64_len);
free(key_raw);
- xmlNodePtr data = xmlNewNode(NULL, "data");
- xmlNewNs(data, "urn:xmpp:openpgp:0", NULL);
- xmlNodeAddContent(data, key_base64);
+ rexmpp_xml_t *data =
+ rexmpp_xml_new_elem("data", "urn:xmpp:openpgp:0");
+ rexmpp_xml_add_text(data, key_base64);
free(key_base64);
- xmlNodePtr pubkey = xmlNewNode(NULL, "pubkey");
- xmlNewNs(pubkey, "urn:xmpp:openpgp:0", NULL);
- xmlAddChild(pubkey, data);
+ rexmpp_xml_t *pubkey =
+ rexmpp_xml_new_elem("pubkey", "urn:xmpp:openpgp:0");
+ rexmpp_xml_add_child(pubkey, data);
char time_str[42];
time_t t = time(NULL);
@@ -421,9 +417,9 @@ int rexmpp_openpgp_fingerprint_matches (const char *f1, const char *f2) {
return 1;
}
-xmlNodePtr
+rexmpp_xml_t *
rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s,
- xmlNodePtr message,
+ rexmpp_xml_t *message,
int *valid)
{
gpgme_error_t err;
@@ -433,14 +429,13 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s,
rexmpp_log(s, LOG_ERR, "Not a message element");
return NULL;
}
- char *from_str = xmlGetProp(message, "from");
+ const char *from_str = rexmpp_xml_find_attr_val(message, "from");
if (from_str == NULL) {
rexmpp_log(s, LOG_ERR, "No 'from' attribute");
return NULL;
}
rexmpp_jid_parse(from_str, &from);
- free(from_str);
- char *to_str = xmlGetProp(message, "to");
+ const char *to_str = rexmpp_xml_find_attr_val(message, "to");
if (to_str == NULL) {
if (strcmp(from.bare, s->assigned_jid.bare) != 0) {
rexmpp_log(s, LOG_ERR, "No 'to' attribute");
@@ -449,18 +444,16 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s,
rexmpp_jid_parse(from.full, &to);
} else {
rexmpp_jid_parse(to_str, &to);
- free(to_str);
}
- xmlNodePtr openpgp =
+ rexmpp_xml_t *openpgp =
rexmpp_xml_find_child(message, "urn:xmpp:openpgp:0", "openpgp");
if (openpgp == NULL) {
rexmpp_log(s, LOG_ERR, "No 'openpgp' child element");
return NULL;
}
- char *cipher_str = xmlNodeGetContent(openpgp);
- xmlNodePtr plain =
+ const char *cipher_str = rexmpp_xml_text_child(openpgp);
+ rexmpp_xml_t *plain =
rexmpp_openpgp_decrypt_verify(s, cipher_str);
- free(cipher_str);
if (plain == NULL) {
return NULL;
}
@@ -476,23 +469,20 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s,
return plain;
}
- xmlNodePtr child;
+ rexmpp_xml_t *child;
int found = 0;
- for (child = xmlFirstElementChild(plain);
+ for (child = rexmpp_xml_first_elem_child(plain);
child != NULL && ! found;
- child = xmlNextElementSibling(child))
+ child = rexmpp_xml_next_elem_sibling(child))
{
if (rexmpp_xml_match(child, "urn:xmpp:openpgp:0", "to")) {
- char *to_jid = xmlGetProp(child, "jid");
+ const char *to_jid = rexmpp_xml_find_attr_val(child, "jid");
if (to_jid == NULL) {
rexmpp_log(s, LOG_WARNING,
"Found a 'to' element without a 'jid' attribute");
} else if (strcmp(to_jid, to.bare) == 0) {
found = 1;
}
- if (to_jid != NULL) {
- free(to_jid);
- }
}
}
if (! found) {
@@ -520,11 +510,11 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s,
}
found = 0;
- xmlNodePtr metadata;
+ rexmpp_xml_t *metadata;
for (metadata = rexmpp_published_fingerprints(s, from.bare);
metadata != NULL && ! found;
- metadata = xmlNextElementSibling(metadata)) {
- char *fingerprint = xmlGetProp(metadata, "v4-fingerprint");
+ metadata = metadata->next) {
+ const char *fingerprint = rexmpp_xml_find_attr_val(metadata, "v4-fingerprint");
if (fingerprint == NULL) {
rexmpp_log(s, LOG_WARNING, "No fingerprint found in pubkey-metadata");
continue;
@@ -532,7 +522,6 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s,
if (rexmpp_openpgp_fingerprint_matches(fingerprint, sig->fpr)) {
found = 1;
}
- free(fingerprint);
}
if (! found) {
rexmpp_log(s, LOG_ERR, "No %s's known key matches that of the signature",
@@ -570,7 +559,7 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s,
return plain;
}
-xmlNodePtr
+rexmpp_xml_t *
rexmpp_openpgp_decrypt_verify (rexmpp_t *s,
const char *cipher_base64)
{
@@ -600,7 +589,7 @@ rexmpp_openpgp_decrypt_verify (rexmpp_t *s,
rexmpp_log(s, LOG_ERR, "Failed to release and get memory");
return NULL;
}
- xmlNodePtr elem = rexmpp_xml_parse(plain, plain_len);
+ rexmpp_xml_t *elem = rexmpp_xml_parse(plain, plain_len);
if(elem == NULL) {
rexmpp_log(s, LOG_ERR, "Failed to parse an XML document");
}
@@ -615,11 +604,11 @@ void rexmpp_openpgp_add_keys (rexmpp_t *s,
int *allocated)
{
gpgme_error_t err;
- xmlNodePtr metadata;
+ rexmpp_xml_t *metadata;
for (metadata = rexmpp_published_fingerprints(s, jid);
metadata != NULL;
- metadata = xmlNextElementSibling(metadata)) {
- char *fingerprint = xmlGetProp(metadata, "v4-fingerprint");
+ metadata = metadata->next) {
+ const char *fingerprint = rexmpp_xml_find_attr_val(metadata, "v4-fingerprint");
if (fingerprint == NULL) {
rexmpp_log(s, LOG_WARNING, "No fingerprint found in pubkey-metadata");
continue;
@@ -630,7 +619,15 @@ void rexmpp_openpgp_add_keys (rexmpp_t *s,
*nkeys = *nkeys + 1;
if (*nkeys == *allocated) {
*allocated = *allocated * 2;
- *keys = realloc(*keys, sizeof(gpgme_key_t *) * *allocated);
+ gpgme_key_t *new_keys =
+ realloc(*keys, sizeof(gpgme_key_t *) * *allocated);
+ if (new_keys == NULL) {
+ rexmpp_log(s, LOG_ERR,
+ "Failed to reallocate the OpenPGP keys array: %s",
+ strerror(errno));
+ continue;
+ }
+ *keys = new_keys;
}
} else {
gpgme_key_unref((*keys)[*nkeys]);
@@ -643,19 +640,18 @@ void rexmpp_openpgp_add_keys (rexmpp_t *s,
rexmpp_log(s, LOG_ERR, "Failed to read key %s: %s",
fingerprint, gpgme_strerror(err));
}
- free(fingerprint);
}
}
void rexmpp_openpgp_set_signers (rexmpp_t *s) {
gpgme_error_t err;
- xmlNodePtr metadata;
+ rexmpp_xml_t *metadata;
gpgme_key_t sec_key;
gpgme_signers_clear(s->pgp_ctx);
for (metadata = rexmpp_published_fingerprints(s, s->initial_jid.bare);
metadata != NULL;
- metadata = xmlNextElementSibling(metadata)) {
- char *fingerprint = xmlGetProp(metadata, "v4-fingerprint");
+ metadata = metadata->next) {
+ const char *fingerprint = rexmpp_xml_find_attr_val(metadata, "v4-fingerprint");
if (fingerprint == NULL) {
rexmpp_log(s, LOG_WARNING, "No fingerprint found in pubkey-metadata");
continue;
@@ -670,12 +666,11 @@ void rexmpp_openpgp_set_signers (rexmpp_t *s) {
rexmpp_log(s, LOG_ERR, "Failed to read key %s: %s",
fingerprint, gpgme_strerror(err));
}
- free(fingerprint);
}
}
char *rexmpp_openpgp_payload (rexmpp_t *s,
- xmlNodePtr payload,
+ rexmpp_xml_t *payload,
const char **recipients,
const char **signers,
enum rexmpp_ox_mode mode)
@@ -693,8 +688,8 @@ char *rexmpp_openpgp_payload (rexmpp_t *s,
} else if (mode == REXMPP_OX_CRYPT) {
elem_name = "crypt";
}
- xmlNodePtr elem = xmlNewNode(NULL, elem_name);
- xmlNewNs(elem, "urn:xmpp:openpgp:0", NULL);
+ rexmpp_xml_t *elem =
+ rexmpp_xml_new_elem(elem_name, "urn:xmpp:openpgp:0");
if (mode == REXMPP_OX_SIGN || mode == REXMPP_OX_SIGNCRYPT) {
if (signers == NULL) {
@@ -717,10 +712,10 @@ char *rexmpp_openpgp_payload (rexmpp_t *s,
/* Add all the recipients. */
for (i = 0; recipients[i] != NULL; i++) {
- xmlNodePtr to = xmlNewNode(NULL, "to");
- xmlNewNs(to, "urn:xmpp:openpgp:0", NULL);
- xmlNewProp(to, "jid", recipients[i]);
- xmlAddChild(elem, to);
+ rexmpp_xml_t *to =
+ rexmpp_xml_new_elem("to", "urn:xmpp:openpgp:0");
+ rexmpp_xml_add_attr(to, "jid", recipients[i]);
+ rexmpp_xml_add_child(elem, to);
}
}
@@ -730,21 +725,26 @@ char *rexmpp_openpgp_payload (rexmpp_t *s,
struct tm utc_time;
gmtime_r(&t, &utc_time);
strftime(time_str, 42, "%FT%TZ", &utc_time);
- xmlNodePtr time = xmlNewNode(NULL, "time");
- xmlNewNs(time, "urn:xmpp:openpgp:0", NULL);
- xmlNewProp(time, "stamp", time_str);
- xmlAddChild(elem, time);
+ rexmpp_xml_t *time =
+ rexmpp_xml_new_elem("time", "urn:xmpp:openpgp:0");
+ rexmpp_xml_add_attr(time, "stamp", time_str);
+ rexmpp_xml_add_child(elem, time);
/* Add the payload. */
- xmlNodePtr pl = xmlNewNode(NULL, "payload");
- xmlNewNs(pl, "urn:xmpp:openpgp:0", NULL);
- xmlAddChild(pl, payload);
- xmlAddChild(elem, pl);
+ rexmpp_xml_t *pl =
+ rexmpp_xml_new_elem("payload", "urn:xmpp:openpgp:0");
+ rexmpp_xml_add_child(pl, payload);
+ rexmpp_xml_add_child(elem, pl);
if (mode == REXMPP_OX_CRYPT || mode == REXMPP_OX_SIGNCRYPT) {
/* Add keys for encryption. */
allocated = 8;
keys = malloc(sizeof(gpgme_key_t *) * allocated);
+ if (keys == NULL) {
+ rexmpp_log(s, LOG_ERR, "Failed to allocate memory for keys");
+ rexmpp_xml_free(elem);
+ return NULL;
+ }
keys[0] = NULL;
rexmpp_openpgp_add_keys(s, s->initial_jid.bare, &keys, &nkeys, &allocated);
for (i = 0; recipients[i] != NULL; i++) {
@@ -753,21 +753,21 @@ char *rexmpp_openpgp_payload (rexmpp_t *s,
/* A random-length random-content padding. */
char *rand_str, rand[256];
- gcry_create_nonce(rand, 1);
+ rexmpp_random_buf(rand, 1);
size_t rand_str_len = 0, rand_len = (unsigned char)rand[0] % (255 - 16) + 16;
- gcry_create_nonce(rand, rand_len);
+ rexmpp_random_buf(rand, rand_len);
rexmpp_base64_to(rand, rand_len, &rand_str, &rand_str_len);
- xmlNodePtr rpad = xmlNewNode(NULL, "rpad");
- xmlNewNs(rpad, "urn:xmpp:openpgp:0", NULL);
- xmlNodeAddContent(rpad, rand_str);
+ rexmpp_xml_t *rpad =
+ rexmpp_xml_new_elem("rpad", "urn:xmpp:openpgp:0");
+ rexmpp_xml_add_text(rpad, rand_str);
free(rand_str);
- xmlAddChild(elem, rpad);
+ rexmpp_xml_add_child(elem, rpad);
}
/* Serialize the resulting XML. */
- char *plaintext = rexmpp_xml_serialize(elem);
- xmlFreeNode(elem);
+ char *plaintext = rexmpp_xml_serialize(elem, 0);
+ rexmpp_xml_free(elem);
/* Encrypt, base64-encode. */
gpgme_data_t cipher_dh, plain_dh;
@@ -832,7 +832,7 @@ rexmpp_err_t gpgme_not_supported(rexmpp_t *s) {
rexmpp_err_t
rexmpp_openpgp_check_keys (rexmpp_t *s,
const char *jid,
- xmlNodePtr items) {
+ rexmpp_xml_t *items) {
(void)jid;
(void)items;
return gpgme_not_supported(s);
@@ -848,7 +848,7 @@ void rexmpp_openpgp_retract_key (rexmpp_t *s, const char *fp) {
gpgme_not_supported(s);
}
-xmlNodePtr
+rexmpp_xml_t *
rexmpp_openpgp_decrypt_verify (rexmpp_t *s,
const char *cipher_base64) {
(void)cipher_base64;
@@ -856,9 +856,9 @@ rexmpp_openpgp_decrypt_verify (rexmpp_t *s,
return NULL;
}
-xmlNodePtr
+rexmpp_xml_t *
rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s,
- xmlNodePtr message,
+ rexmpp_xml_t *message,
int *valid) {
(void)message;
(void)valid;
@@ -867,14 +867,14 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s,
}
char *rexmpp_openpgp_payload (rexmpp_t *s,
- xmlNodePtr payload,
+ rexmpp_xml_t *payload,
const char **recipients,
const char **signers,
enum rexmpp_ox_mode mode) {
(void)recipients;
(void)signers;
(void)mode;
- xmlFreeNode(payload);
+ rexmpp_xml_free(payload);
gpgme_not_supported(s);
return NULL;
}
diff --git a/src/rexmpp_openpgp.h b/src/rexmpp_openpgp.h
index 7470347..2132930 100644
--- a/src/rexmpp_openpgp.h
+++ b/src/rexmpp_openpgp.h
@@ -29,7 +29,7 @@ enum rexmpp_ox_mode {
rexmpp_err_t
rexmpp_openpgp_check_keys (rexmpp_t *s,
const char *jid,
- xmlNodePtr items);
+ rexmpp_xml_t *items);
/**
@brief Publishes a key via PEP/pubsub.
@@ -52,7 +52,7 @@ void rexmpp_openpgp_retract_key (rexmpp_t *s, const char *fp);
@param[in] cipher_base64 An OpenPGP ciphertext.
@returns A plaintext message body.
*/
-xmlNodePtr
+rexmpp_xml_t *
rexmpp_openpgp_decrypt_verify (rexmpp_t *s,
const char *cipher_base64);
@@ -65,9 +65,9 @@ rexmpp_openpgp_decrypt_verify (rexmpp_t *s,
valid.
@returns A decrypted message body.
*/
-xmlNodePtr
+rexmpp_xml_t *
rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s,
- xmlNodePtr message,
+ rexmpp_xml_t *message,
int *valid);
/**
@@ -83,7 +83,7 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s,
@returns An encoded <openpgp> payload.
*/
char *rexmpp_openpgp_payload (rexmpp_t *s,
- xmlNodePtr payload,
+ rexmpp_xml_t *payload,
const char **recipients,
const char **signers,
enum rexmpp_ox_mode mode);
diff --git a/src/rexmpp_pubsub.c b/src/rexmpp_pubsub.c
index 145a352..58cb060 100644
--- a/src/rexmpp_pubsub.c
+++ b/src/rexmpp_pubsub.c
@@ -7,25 +7,22 @@
*/
#include "rexmpp.h"
+#include "rexmpp_xml.h"
void
rexmpp_pubsub_iq (rexmpp_t *s,
const char *iq_type,
const char *pubsub_namespace,
const char *service_jid,
- xmlNodePtr payload,
+ rexmpp_xml_t *payload,
rexmpp_iq_callback_t callback,
void *cb_data)
{
- xmlNodePtr pubsub = xmlNewNode(NULL, "pubsub");
if (pubsub_namespace == NULL) {
- xmlNewNs(pubsub, "http://jabber.org/protocol/pubsub", NULL);
- } else {
- xmlNewNs(pubsub, pubsub_namespace, NULL);
+ pubsub_namespace = "http://jabber.org/protocol/pubsub";
}
-
- xmlAddChild(pubsub, payload);
-
+ rexmpp_xml_t *pubsub = rexmpp_xml_new_elem("pubsub", pubsub_namespace);
+ rexmpp_xml_add_child(pubsub, payload);
rexmpp_iq_new(s, iq_type, service_jid, pubsub, callback, cb_data);
}
@@ -34,21 +31,21 @@ rexmpp_pubsub_item_publish (rexmpp_t *s,
const char *service_jid,
const char *node,
const char *item_id,
- xmlNodePtr payload,
+ rexmpp_xml_t *payload,
rexmpp_iq_callback_t callback,
void *cb_data)
{
- xmlNodePtr item = xmlNewNode(NULL, "item");
- xmlNewNs(item, "http://jabber.org/protocol/pubsub", NULL);
+ rexmpp_xml_t *item =
+ rexmpp_xml_new_elem("item", "http://jabber.org/protocol/pubsub");
if (item_id != NULL) {
- xmlNewProp(item, "id", item_id);
+ rexmpp_xml_add_attr(item, "id", item_id);
}
- xmlAddChild(item, payload);
+ rexmpp_xml_add_child(item, payload);
- xmlNodePtr publish = xmlNewNode(NULL, "publish");
- xmlNewNs(publish, "http://jabber.org/protocol/pubsub", NULL);
- xmlNewProp(publish, "node", node);
- xmlAddChild(publish, item);
+ rexmpp_xml_t *publish =
+ rexmpp_xml_new_elem("publish", "http://jabber.org/protocol/pubsub");
+ rexmpp_xml_add_attr(publish, "node", node);
+ rexmpp_xml_add_child(publish, item);
rexmpp_pubsub_iq(s, "set", NULL, service_jid, publish, callback, cb_data);
}
@@ -61,16 +58,16 @@ rexmpp_pubsub_item_retract (rexmpp_t *s,
rexmpp_iq_callback_t callback,
void *cb_data)
{
- xmlNodePtr item = xmlNewNode(NULL, "item");
- xmlNewNs(item, "http://jabber.org/protocol/pubsub", NULL);
+ rexmpp_xml_t *item =
+ rexmpp_xml_new_elem("item", "http://jabber.org/protocol/pubsub");
if (item_id != NULL) {
- xmlNewProp(item, "id", item_id);
+ rexmpp_xml_add_attr(item, "id", item_id);
}
- xmlNodePtr retract = xmlNewNode(NULL, "retract");
- xmlNewNs(retract, "http://jabber.org/protocol/pubsub", NULL);
- xmlNewProp(retract, "node", node);
- xmlAddChild(retract, item);
+ rexmpp_xml_t *retract =
+ rexmpp_xml_new_elem("retract", "http://jabber.org/protocol/pubsub");
+ rexmpp_xml_add_attr(retract, "node", node);
+ rexmpp_xml_add_child(retract, item);
rexmpp_pubsub_iq(s, "set", NULL, service_jid, retract, callback, cb_data);
}
@@ -82,9 +79,9 @@ rexmpp_pubsub_node_delete (rexmpp_t *s,
rexmpp_iq_callback_t callback,
void *cb_data)
{
- xmlNodePtr delete = xmlNewNode(NULL, "delete");
- xmlNewNs(delete, "http://jabber.org/protocol/pubsub#owner", NULL);
- xmlNewProp(delete, "node", node);
+ rexmpp_xml_t *delete =
+ rexmpp_xml_new_elem("delete", "http://jabber.org/protocol/pubsub#owner");
+ rexmpp_xml_add_attr(delete, "node", node);
rexmpp_pubsub_iq(s, "set", "http://jabber.org/protocol/pubsub#owner",
service_jid, delete, callback, cb_data);
diff --git a/src/rexmpp_pubsub.h b/src/rexmpp_pubsub.h
index b5a7c3e..86675d3 100644
--- a/src/rexmpp_pubsub.h
+++ b/src/rexmpp_pubsub.h
@@ -11,7 +11,7 @@ rexmpp_pubsub_iq (rexmpp_t *s,
const char *iq_type,
const char *pubsub_namespace,
const char *service_jid,
- xmlNodePtr payload,
+ rexmpp_xml_t *payload,
rexmpp_iq_callback_t callback,
void *cb_data);
@@ -20,7 +20,7 @@ rexmpp_pubsub_item_publish (rexmpp_t *s,
const char *service_jid,
const char *node,
const char *item_id,
- xmlNodePtr payload,
+ rexmpp_xml_t *payload,
rexmpp_iq_callback_t callback,
void *cb_data);
diff --git a/src/rexmpp_random.c b/src/rexmpp_random.c
new file mode 100644
index 0000000..11e2b73
--- /dev/null
+++ b/src/rexmpp_random.c
@@ -0,0 +1,34 @@
+/**
+ @file rexmpp_random.c
+ @brief Random generation
+ @author defanor <defanor@uberspace.net>
+ @date 2023
+ @copyright MIT license.
+*/
+
+#include "config.h"
+#include "rexmpp_base64.h"
+
+#ifdef HAVE_GCRYPT
+#include <gcrypt.h>
+#else
+#define _GNU_SOURCE
+#include <stdlib.h>
+#endif
+
+
+void rexmpp_random_buf (void *buf, size_t len) {
+#ifdef HAVE_GCRYPT
+ gcry_create_nonce(buf, len);
+#else
+ arc4random_buf(buf, len);
+#endif
+}
+
+char *rexmpp_random_id () {
+ char buf_raw[18], *buf_base64 = NULL;
+ size_t buf_base64_len = 0;
+ rexmpp_random_buf(buf_raw, 18);
+ rexmpp_base64_to(buf_raw, 18, &buf_base64, &buf_base64_len);
+ return buf_base64;
+}
diff --git a/src/rexmpp_random.h b/src/rexmpp_random.h
new file mode 100644
index 0000000..b0f1978
--- /dev/null
+++ b/src/rexmpp_random.h
@@ -0,0 +1,29 @@
+/**
+ @file rexmpp_random.h
+ @brief Random generation
+ @author defanor <defanor@uberspace.net>
+ @date 2023
+ @copyright MIT license.
+*/
+
+#ifndef REXMPP_RANDOM_H
+#define REXMPP_RANDOM_H
+
+/**
+ @brief Fills a buffer with cryptographically-secure random data.
+ @param[out] buf A buffer to write into.
+ @param[in] len The number of bytes to fill.
+
+ Uses arc4random_buf or gcry_create_nonce, depending on what is
+ available.
+*/
+void rexmpp_random_buf (void *buf, size_t len);
+
+/**
+ @brief Generates a random ASCII identifier.
+ @returns A null-terminated string, which must be freed by the
+ caller.
+*/
+char *rexmpp_random_id ();
+
+#endif
diff --git a/src/rexmpp_random.rs b/src/rexmpp_random.rs
new file mode 100644
index 0000000..dbabc71
--- /dev/null
+++ b/src/rexmpp_random.rs
@@ -0,0 +1,5 @@
+use std::os::raw::{c_char};
+
+extern {
+ pub fn rexmpp_random_id () -> *mut c_char;
+}
diff --git a/src/rexmpp_roster.c b/src/rexmpp_roster.c
index 63a52b9..3deb5b7 100644
--- a/src/rexmpp_roster.c
+++ b/src/rexmpp_roster.c
@@ -7,24 +7,24 @@
*/
#include "rexmpp.h"
+#include "rexmpp_xml.h"
#include <syslog.h>
#include <string.h>
-#include <libxml/tree.h>
-#include <libxml/xmlsave.h>
+#include <stdlib.h>
-xmlNodePtr rexmpp_roster_find_item (rexmpp_t *s,
- const char *jid,
- xmlNodePtr *prev_item)
+rexmpp_xml_t *
+rexmpp_roster_find_item (rexmpp_t *s,
+ const char *jid,
+ rexmpp_xml_t **prev_item)
{
- xmlNodePtr prev = NULL, cur = s->roster_items;
+ rexmpp_xml_t *prev = NULL, *cur = s->roster_items;
while (cur != NULL) {
- char *cur_jid = xmlGetProp(cur, "jid");
+ const char *cur_jid = rexmpp_xml_find_attr_val(cur, "jid");
if (cur_jid == NULL) {
rexmpp_log(s, LOG_ALERT, "No jid found in a roster item.");
return NULL;
}
int match = (strcmp(cur_jid, jid) == 0);
- free(cur_jid);
if (match) {
if (prev_item != NULL) {
*prev_item = prev;
@@ -32,75 +32,75 @@ xmlNodePtr rexmpp_roster_find_item (rexmpp_t *s,
return cur;
}
prev = cur;
- cur = cur->next;
+ cur = rexmpp_xml_next_elem_sibling(cur);
}
return NULL;
}
-rexmpp_err_t rexmpp_modify_roster (rexmpp_t *s, xmlNodePtr item) {
+rexmpp_err_t rexmpp_modify_roster (rexmpp_t *s, rexmpp_xml_t *item) {
rexmpp_err_t ret = REXMPP_SUCCESS;
if (! rexmpp_xml_match(item, "jabber:iq:roster", "item")) {
rexmpp_log(s, LOG_ERR, "No roster item.");
return REXMPP_E_PARAM;
}
- char *subscription = xmlGetProp(item, "subscription");
- char *jid = xmlGetProp(item, "jid");
+ const char *subscription = rexmpp_xml_find_attr_val(item, "subscription");
+ const char *jid = rexmpp_xml_find_attr_val(item, "jid");
if (subscription != NULL && strcmp(subscription, "remove") == 0) {
/* Delete the item. */
- xmlNodePtr prev, cur;
+ rexmpp_xml_t *prev, *cur;
cur = rexmpp_roster_find_item(s, jid, &prev);
if (cur != NULL) {
if (prev != NULL) {
- prev->next = cur->next;
+ prev->next = rexmpp_xml_next_elem_sibling(cur);
} else {
- s->roster_items = cur->next;
+ s->roster_items = rexmpp_xml_next_elem_sibling(cur);
}
- xmlFreeNode(cur);
+ rexmpp_xml_free(cur);
} else {
ret = REXMPP_E_ROSTER_ITEM_NOT_FOUND;
}
} else {
/* Add or modify the item. */
- xmlNodePtr cur, prev;
+ rexmpp_xml_t *cur, *prev;
cur = rexmpp_roster_find_item(s, jid, &prev);
/* Remove the item if it was in the roster before. */
if (cur != NULL) {
if (prev != NULL) {
- prev->next = cur->next;
+ prev->next = rexmpp_xml_next_elem_sibling(cur);
} else {
- s->roster_items = cur->next;
+ s->roster_items = rexmpp_xml_next_elem_sibling(cur);
}
- xmlFreeNode(cur);
+ rexmpp_xml_free(cur);
}
/* Add the new item. */
- xmlNodePtr new_item = xmlCopyNode(item, 1);
+ rexmpp_xml_t *new_item = rexmpp_xml_clone(item);
new_item->next = s->roster_items;
s->roster_items = new_item;
}
- free(jid);
- if (subscription != NULL) {
- free(subscription);
- }
if (s->roster_modify_cb != NULL) {
s->roster_modify_cb(s, item);
}
return ret;
}
-void rexmpp_roster_set (rexmpp_t *s, xmlNodePtr query) {
+void rexmpp_roster_set (rexmpp_t *s, rexmpp_xml_t *query) {
if (s->roster_items != NULL) {
- xmlFreeNodeList(s->roster_items);
+ rexmpp_xml_free_list(s->roster_items);
}
if (s->roster_ver != NULL) {
free(s->roster_ver);
}
- s->roster_ver = xmlGetProp(query, "ver");
- s->roster_items = xmlCopyNodeList(xmlFirstElementChild(query));
+ const char *roster_ver = rexmpp_xml_find_attr_val(query, "ver");
+ s->roster_ver = NULL;
+ if (roster_ver != NULL) {
+ s->roster_ver = strdup(roster_ver);
+ }
+ s->roster_items = rexmpp_xml_clone_list(rexmpp_xml_first_elem_child(query));
if (s->roster_modify_cb != NULL) {
- xmlNodePtr item;
- for (item = xmlFirstElementChild(query);
+ rexmpp_xml_t *item;
+ for (item = rexmpp_xml_first_elem_child(query);
item != NULL;
- item = xmlNextElementSibling(item))
+ item = rexmpp_xml_next_elem_sibling(item))
{
s->roster_modify_cb(s, item);
}
@@ -112,10 +112,11 @@ void rexmpp_roster_cache_read (rexmpp_t *s) {
rexmpp_log(s, LOG_WARNING, "No roster cache file path is set.");
return;
}
- xmlDocPtr doc = xmlReadFile(s->roster_cache_file, "utf-8", XML_PARSE_NONET);
- xmlNodePtr query = xmlDocGetRootElement(doc);
- rexmpp_roster_set(s, query);
- xmlFreeDoc(doc);
+ rexmpp_xml_t *query = rexmpp_xml_read_file(s->roster_cache_file);
+ if (query != NULL) {
+ rexmpp_roster_set(s, query);
+ rexmpp_xml_free(query);
+ }
}
void rexmpp_roster_cache_write (rexmpp_t *s) {
@@ -123,24 +124,22 @@ void rexmpp_roster_cache_write (rexmpp_t *s) {
rexmpp_log(s, LOG_WARNING, "No roster cache file path is set.");
return;
}
- xmlDocPtr doc = xmlNewDoc("1.0");
- xmlNodePtr query = xmlNewDocNode(doc, NULL, "query", NULL);
- xmlDocSetRootElement(doc, query);
- xmlNewNs(query, "jabber:iq:roster", NULL);
+
+ rexmpp_xml_t *query = rexmpp_xml_new_elem("query", "jabber:iq:roster");
if (s->roster_ver != NULL) {
- xmlNewProp(query, "ver", s->roster_ver);
+ rexmpp_xml_add_attr(query, "ver", s->roster_ver);
}
if (s->roster_items != NULL) {
- xmlAddChild(query, xmlDocCopyNodeList(doc, s->roster_items));
+ rexmpp_xml_add_child(query, rexmpp_xml_clone_list(s->roster_items));
}
- xmlSaveFileEnc(s->roster_cache_file, doc, "utf-8");
- xmlFreeDoc(doc);
+ rexmpp_xml_write_file(s->roster_cache_file, query);
+ rexmpp_xml_free(query);
}
void rexmpp_iq_roster_get (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)ptr;
@@ -149,8 +148,9 @@ void rexmpp_iq_roster_get (rexmpp_t *s,
rexmpp_log(s, LOG_ERR, "Roster loading failed.");
return;
}
- xmlNodePtr query = xmlFirstElementChild(response);
- if (! rexmpp_xml_match(query, "jabber:iq:roster", "query")) {
+ rexmpp_xml_t *query =
+ rexmpp_xml_find_child(response, "jabber:iq:roster", "query");
+ if (query == NULL) {
rexmpp_log(s, LOG_DEBUG, "No roster query in reply.");
return;
}
diff --git a/src/rexmpp_roster.h b/src/rexmpp_roster.h
index 1fa9183..f613a7b 100644
--- a/src/rexmpp_roster.h
+++ b/src/rexmpp_roster.h
@@ -7,15 +7,15 @@
*/
-xmlNodePtr rexmpp_roster_find_item (rexmpp_t *s,
- const char *jid,
- xmlNodePtr *prev_item);
-rexmpp_err_t rexmpp_modify_roster (rexmpp_t *s, xmlNodePtr item);
-void rexmpp_roster_set (rexmpp_t *s, xmlNodePtr query);
+rexmpp_xml_t *rexmpp_roster_find_item (rexmpp_t *s,
+ const char *jid,
+ rexmpp_xml_t **prev_item);
+rexmpp_err_t rexmpp_modify_roster (rexmpp_t *s, rexmpp_xml_t *item);
+void rexmpp_roster_set (rexmpp_t *s, rexmpp_xml_t *query);
void rexmpp_roster_cache_read (rexmpp_t *s);
void rexmpp_roster_cache_write (rexmpp_t *s);
void rexmpp_iq_roster_get (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success);
diff --git a/src/rexmpp_rust.rs b/src/rexmpp_rust.rs
new file mode 100644
index 0000000..ba80598
--- /dev/null
+++ b/src/rexmpp_rust.rs
@@ -0,0 +1,8 @@
+mod rexmpp_jid;
+mod rexmpp_xml;
+mod rexmpp_xml_parser;
+mod rexmpp_dns;
+mod rexmpp_tcp;
+mod rexmpp_socks;
+mod rexmpp_random;
+mod rexmpp;
diff --git a/src/rexmpp_sasl.c b/src/rexmpp_sasl.c
index 7dd16ba..20c4ba0 100644
--- a/src/rexmpp_sasl.c
+++ b/src/rexmpp_sasl.c
@@ -8,6 +8,7 @@
*/
#include <syslog.h>
+#include <stdlib.h>
#include "config.h"
#include "rexmpp.h"
@@ -31,28 +32,33 @@ int rexmpp_sasl_cb (Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop) {
}
int rexmpp_sasl_ctx_init (rexmpp_t *s) {
- int err = gsasl_init(&(s->sasl.ctx));
+ s->sasl = malloc(sizeof(struct rexmpp_sasl_ctx));
+ int err = gsasl_init(&(s->sasl->ctx));
if (err != GSASL_OK) {
rexmpp_log(s, LOG_CRIT, "gsasl initialisation error: %s",
gsasl_strerror(err));
return -1;
}
- gsasl_callback_hook_set(s->sasl.ctx, s);
- gsasl_callback_set(s->sasl.ctx, rexmpp_sasl_cb);
+ gsasl_callback_hook_set(s->sasl->ctx, s);
+ gsasl_callback_set(s->sasl->ctx, rexmpp_sasl_cb);
return 0;
}
void rexmpp_sasl_ctx_deinit (rexmpp_t *s) {
- gsasl_done(s->sasl.ctx);
+ gsasl_done(s->sasl->ctx);
+ if (s->sasl != NULL) {
+ free(s->sasl);
+ s->sasl = NULL;
+ }
}
void rexmpp_sasl_ctx_cleanup (rexmpp_t *s) {
- gsasl_finish(s->sasl.session);
- s->sasl.session = NULL;
+ gsasl_finish(s->sasl->session);
+ s->sasl->session = NULL;
}
int rexmpp_sasl_encode (rexmpp_t *s, const char *in, size_t in_len, char **out, size_t *out_len) {
- int sasl_err = gsasl_encode (s->sasl.session, in, in_len, out, out_len);
+ int sasl_err = gsasl_encode (s->sasl->session, in, in_len, out, out_len);
if (sasl_err != GSASL_OK) {
rexmpp_log(s, LOG_ERR, "SASL encoding error: %s", gsasl_strerror(sasl_err));
return -1;
@@ -61,7 +67,7 @@ int rexmpp_sasl_encode (rexmpp_t *s, const char *in, size_t in_len, char **out,
}
int rexmpp_sasl_decode (rexmpp_t *s, const char *in, size_t in_len, char **out, size_t *out_len) {
- int sasl_err = gsasl_decode(s->sasl.session, in, in_len, out, out_len);
+ int sasl_err = gsasl_decode(s->sasl->session, in, in_len, out, out_len);
if (sasl_err != GSASL_OK) {
rexmpp_log(s, LOG_ERR, "SASL decoding error: %s", gsasl_strerror(sasl_err));
return -1;
@@ -70,15 +76,15 @@ int rexmpp_sasl_decode (rexmpp_t *s, const char *in, size_t in_len, char **out,
}
const char *rexmpp_sasl_suggest_mechanism (rexmpp_t *s, const char *mech_list) {
- return gsasl_client_suggest_mechanism(s->sasl.ctx, mech_list);
+ return gsasl_client_suggest_mechanism(s->sasl->ctx, mech_list);
}
void rexmpp_sasl_property_set (rexmpp_t *s, rexmpp_sasl_property prop, const char *data) {
- gsasl_property_set (s->sasl.session, (Gsasl_property)prop, data);
+ gsasl_property_set (s->sasl->session, (Gsasl_property)prop, data);
}
int rexmpp_sasl_start (rexmpp_t *s, const char *mech) {
- int sasl_err = gsasl_client_start(s->sasl.ctx, mech, &(s->sasl.session));
+ int sasl_err = gsasl_client_start(s->sasl->ctx, mech, &(s->sasl->session));
if (sasl_err != GSASL_OK) {
rexmpp_log(s, LOG_CRIT, "Failed to initialise SASL session: %s",
gsasl_strerror(sasl_err));
@@ -88,7 +94,7 @@ int rexmpp_sasl_start (rexmpp_t *s, const char *mech) {
}
int rexmpp_sasl_step64 (rexmpp_t *s, const char *b64_in, char **b64_out) {
- int sasl_err = gsasl_step64 (s->sasl.session, b64_in, b64_out);
+ int sasl_err = gsasl_step64 (s->sasl->session, b64_in, b64_out);
if (sasl_err != GSASL_OK) {
if (sasl_err == GSASL_NEEDS_MORE) {
rexmpp_log(s, LOG_DEBUG, "SASL needs more data");
@@ -106,26 +112,30 @@ int rexmpp_sasl_step64 (rexmpp_t *s, const char *b64_in, char **b64_out) {
#include <memory.h>
int rexmpp_sasl_ctx_init (rexmpp_t *s) {
- s->sasl.mech = REXMPP_SASL_MECH_UNKNOWN;
- s->sasl.authid = NULL;
- s->sasl.password = NULL;
+ s->sasl = malloc(sizeof(struct rexmpp_sasl_ctx));
+ s->sasl->mech = REXMPP_SASL_MECH_UNKNOWN;
+ s->sasl->authid = NULL;
+ s->sasl->password = NULL;
return 0;
}
void rexmpp_sasl_ctx_cleanup (rexmpp_t *s) {
- s->sasl.mech = REXMPP_SASL_MECH_UNKNOWN;
- if (s->sasl.authid != NULL) {
- free(s->sasl.authid);
- s->sasl.authid = NULL;
+ s->sasl->mech = REXMPP_SASL_MECH_UNKNOWN;
+ if (s->sasl->authid != NULL) {
+ free(s->sasl->authid);
+ s->sasl->authid = NULL;
}
- if (s->sasl.password != NULL) {
- free(s->sasl.password);
- s->sasl.password = NULL;
+ if (s->sasl->password != NULL) {
+ free(s->sasl->password);
+ s->sasl->password = NULL;
}
}
void rexmpp_sasl_ctx_deinit (rexmpp_t *s) {
- (void)s;
+ if (s->sasl != NULL) {
+ free(s->sasl);
+ s->sasl = NULL;
+ }
}
int rexmpp_sasl_encode (rexmpp_t *s, const char *in, size_t in_len, char **out, size_t *out_len) {
@@ -186,7 +196,7 @@ const char *rexmpp_sasl_suggest_mechanism (rexmpp_t *s, const char *mech_list) {
int rexmpp_sasl_start (rexmpp_t *s, const char *mech) {
rexmpp_sasl_mechanism m = rexmpp_sasl_mech_read(mech);
if (m != REXMPP_SASL_MECH_UNKNOWN) {
- s->sasl.mech = m;
+ s->sasl->mech = m;
return 0;
}
return -1;
@@ -194,15 +204,15 @@ int rexmpp_sasl_start (rexmpp_t *s, const char *mech) {
const char *rexmpp_sasl_get_prop (rexmpp_t *s, rexmpp_sasl_property prop) {
if (prop == REXMPP_SASL_PROP_AUTHID) {
- if (s->sasl.authid == NULL) {
+ if (s->sasl->authid == NULL) {
s->sasl_property_cb(s, prop);
}
- return s->sasl.authid;
+ return s->sasl->authid;
} else if (prop == REXMPP_SASL_PROP_PASSWORD) {
- if (s->sasl.password == NULL) {
+ if (s->sasl->password == NULL) {
s->sasl_property_cb(s, prop);
}
- return s->sasl.password;
+ return s->sasl->password;
}
return NULL;
}
@@ -210,7 +220,7 @@ const char *rexmpp_sasl_get_prop (rexmpp_t *s, rexmpp_sasl_property prop) {
int rexmpp_sasl_step64 (rexmpp_t *s, const char *b64_in, char **b64_out) {
(void)s;
(void)b64_in;
- if (s->sasl.mech == REXMPP_SASL_MECH_PLAIN) {
+ if (s->sasl->mech == REXMPP_SASL_MECH_PLAIN) {
/* RFC 4616 */
const char *authid = rexmpp_sasl_get_prop(s, REXMPP_SASL_PROP_AUTHID);
const char *password = rexmpp_sasl_get_prop(s, REXMPP_SASL_PROP_PASSWORD);
@@ -226,7 +236,7 @@ int rexmpp_sasl_step64 (rexmpp_t *s, const char *b64_in, char **b64_out) {
free(auth);
return 0;
}
- } else if (s->sasl.mech == REXMPP_SASL_MECH_EXTERNAL) {
+ } else if (s->sasl->mech == REXMPP_SASL_MECH_EXTERNAL) {
*b64_out = strdup("");
return 0;
}
@@ -237,15 +247,15 @@ void rexmpp_sasl_property_set (rexmpp_t *s, rexmpp_sasl_property prop, const cha
(void)s;
(void)data;
if (prop == REXMPP_SASL_PROP_AUTHID) {
- if (s->sasl.authid != NULL) {
- free(s->sasl.authid);
+ if (s->sasl->authid != NULL) {
+ free(s->sasl->authid);
}
- s->sasl.authid = strdup(data);
+ s->sasl->authid = strdup(data);
} else if (prop == REXMPP_SASL_PROP_PASSWORD) {
- if (s->sasl.password != NULL) {
- free(s->sasl.password);
+ if (s->sasl->password != NULL) {
+ free(s->sasl->password);
}
- s->sasl.password = strdup(data);
+ s->sasl->password = strdup(data);
}
}
diff --git a/src/rexmpp_socks.rs b/src/rexmpp_socks.rs
new file mode 100644
index 0000000..1828de1
--- /dev/null
+++ b/src/rexmpp_socks.rs
@@ -0,0 +1,180 @@
+// For rustc and libstd-rust version 1.48
+
+use std::os::raw::{c_int, c_char};
+use std::ffi::CStr;
+use std::net::TcpStream;
+use std::os::unix::io::{FromRawFd, IntoRawFd};
+use std::io::Write;
+use std::io::Read;
+use std::io::ErrorKind;
+use std::convert::TryFrom;
+
+const REXMPP_SOCKS_BUF_LEN: usize = 300;
+
+#[derive(PartialEq)]
+#[repr(C)]
+enum SocksIOState {
+ Writing,
+ Reading
+}
+
+#[derive(PartialEq)]
+#[repr(C)]
+enum SocksStage {
+ Auth,
+ Cmd,
+ Done
+}
+
+#[derive(PartialEq)]
+#[repr(C)]
+enum SocksErr {
+ Connected,
+ EAgain,
+ ETCP,
+ EReply,
+ EVersion,
+ ESocks,
+ EHost
+}
+
+#[repr(C)]
+pub struct RexmppSocks {
+ fd: c_int,
+ host: *const c_char,
+ port: u16,
+ stage: SocksStage,
+ io_state: SocksIOState,
+ socks_error: c_int,
+ buf: [u8; REXMPP_SOCKS_BUF_LEN],
+ buf_len: usize,
+ buf_sent: usize
+}
+
+#[no_mangle]
+extern "C" fn rexmpp_socks_proceed (s : &mut RexmppSocks) -> SocksErr {
+ if s.io_state == SocksIOState::Writing {
+ let mut stream : TcpStream = unsafe { TcpStream::from_raw_fd(s.fd) };
+ let ret = stream.write(&s.buf[s.buf_sent .. s.buf_len]);
+ // Make sure the connection is not closed by TcpStream.
+ TcpStream::into_raw_fd(stream);
+ match ret {
+ Ok(sent) => {
+ s.buf_sent += sent;
+ if s.buf_len == s.buf_sent {
+ s.buf_len = 0;
+ s.io_state = SocksIOState::Reading;
+ }
+ }
+ Err(error) => match error.kind() {
+ ErrorKind::WouldBlock => return SocksErr::EAgain,
+ _ => return SocksErr::ETCP
+ }
+ }
+ } else if s.io_state == SocksIOState::Reading {
+ let mut stream : TcpStream = unsafe { TcpStream::from_raw_fd(s.fd) };
+ let ret = stream.read(&mut s.buf[s.buf_len ..]);
+ // Make sure the connection is not closed by TcpStream.
+ TcpStream::into_raw_fd(stream);
+ match ret {
+ Ok(received) => {
+ s.buf_len += received;
+ if s.buf[0] != 5 {
+ return SocksErr::EVersion;
+ }
+ if s.buf_len >= 2 {
+ s.socks_error = s.buf[1].into();
+ }
+ if s.stage == SocksStage::Auth {
+ if s.buf_len > 2 {
+ return SocksErr::EReply;
+ }
+ if s.buf_len == 2 {
+ if s.socks_error != 0 {
+ return SocksErr::ESocks;
+ }
+ s.buf[0] = 5; // SOCKS version 5
+ s.buf[1] = 1; // Connect
+ s.buf[2] = 0; // Reserved
+ s.buf[3] = 3; // Domain name (todo: IP addresses)
+ let host_cstr : &CStr =
+ unsafe { CStr::from_ptr(s.host) };
+ let host_len = host_cstr.to_bytes().len();
+ match u8::try_from(host_len) {
+ Ok(u) => { s.buf[4] = u }
+ Err(_) => return SocksErr::EHost
+ }
+ s.buf[5 .. 5 + host_len].
+ copy_from_slice(&host_cstr.to_bytes());
+ s.buf[5 + host_len .. 7 + host_len].
+ copy_from_slice(&s.port.to_be_bytes());
+ s.buf_len = 7 + host_len;
+ s.buf_sent = 0;
+ s.stage = SocksStage::Cmd;
+ s.io_state = SocksIOState::Writing;
+ return rexmpp_socks_proceed(s);
+ }
+ } else if s.stage == SocksStage::Cmd {
+ if s.buf_len >= 5 {
+ let mut full_len : usize = 6;
+ match s.buf[3] {
+ // IPv4
+ 1 => full_len += 4,
+ // Domain name
+ 3 => full_len += usize::from(s.buf[4]) + 1,
+ // IPv6
+ 4 => full_len += 16,
+ _ => return SocksErr::EReply
+ }
+ if s.buf_len > full_len {
+ return SocksErr::EReply;
+ }
+ if s.buf_len == full_len {
+ if s.socks_error != 0 {
+ return SocksErr::ESocks;
+ }
+ // We are done
+ s.stage = SocksStage::Done;
+ return SocksErr::Connected;
+ }
+ }
+ }
+ }
+ Err(error) => match error.kind() {
+ ErrorKind::WouldBlock => return SocksErr::EAgain,
+ _ => return SocksErr::ETCP
+ }
+ }
+ }
+ return SocksErr::EAgain
+}
+
+#[no_mangle]
+extern "C" fn rexmpp_socks_init (
+ s : &mut RexmppSocks,
+ fd: c_int,
+ host: *const c_char,
+ port: u16
+)
+ -> SocksErr
+{
+ s.fd = fd;
+ s.host = host;
+ s.port = port;
+ s.socks_error = 0;
+
+ let host_cstr : &CStr = unsafe { CStr::from_ptr(host) };
+ if host_cstr.to_bytes().len() > 255 {
+ return SocksErr::EHost;
+ }
+
+ // Request authentication
+ s.stage = SocksStage::Auth;
+ s.io_state = SocksIOState::Writing;
+ s.buf[0] = 5; // SOCKS version 5
+ s.buf[1] = 1; // 1 supported method
+ s.buf[2] = 0; // No authentication required
+ s.buf_len = 3;
+ s.buf_sent = 0;
+ return rexmpp_socks_proceed(s);
+}
diff --git a/src/rexmpp_tcp.c b/src/rexmpp_tcp.c
index 3022182..c6a53a5 100644
--- a/src/rexmpp_tcp.c
+++ b/src/rexmpp_tcp.c
@@ -50,10 +50,10 @@ void rexmpp_tcp_dns_a_cb (rexmpp_t *s,
conn->addr_cur_v4 = -1;
if (conn->resolution_v6 == REXMPP_CONN_RESOLUTION_WAITING) {
/* Wait for 50 ms for IPv6. */
- gettimeofday(&(conn->next_connection_time), NULL);
- conn->next_connection_time.tv_usec += REXMPP_TCP_IPV6_DELAY_MS * 1000;
- if (conn->next_connection_time.tv_usec >= 1000000) {
- conn->next_connection_time.tv_usec -= 1000000;
+ clock_gettime(CLOCK_MONOTONIC, &(conn->next_connection_time));
+ conn->next_connection_time.tv_nsec += REXMPP_TCP_IPV6_DELAY_MS * 1000000;
+ if (conn->next_connection_time.tv_nsec >= 1000000000) {
+ conn->next_connection_time.tv_nsec -= 1000000000;
conn->next_connection_time.tv_sec++;
}
}
@@ -99,6 +99,21 @@ rexmpp_tcp_connected (rexmpp_tcp_conn_t *conn, int fd) {
return REXMPP_CONN_DONE;
}
+int rexmpp_tcp_socket(rexmpp_t *s, int domain) {
+ int sock = socket(domain, SOCK_STREAM, 0);
+
+ /* Make it non-blocking */
+ int flags = fcntl(sock, F_GETFL, 0);
+ fcntl(sock, F_SETFL, flags | O_NONBLOCK);
+
+ /* Call the socket creation callback, if provided */
+ if (s->socket_cb != NULL) {
+ s->socket_cb(s, sock);
+ }
+
+ return sock;
+}
+
rexmpp_tcp_conn_error_t
rexmpp_tcp_conn_init (rexmpp_t *s,
rexmpp_tcp_conn_t *conn,
@@ -116,20 +131,16 @@ rexmpp_tcp_conn_init (rexmpp_t *s,
conn->fd = -1;
conn->dns_secure = 0;
conn->next_connection_time.tv_sec = 0;
- conn->next_connection_time.tv_usec = 0;
+ conn->next_connection_time.tv_nsec = 0;
conn->resolution_v4 = REXMPP_CONN_RESOLUTION_INACTIVE;
conn->resolution_v6 = REXMPP_CONN_RESOLUTION_INACTIVE;
struct sockaddr_in addr_v4;
- int flags;
- if (inet_pton(AF_INET, host, &addr_v4)) {
+ if (inet_pton(AF_INET, host, &(addr_v4.sin_addr))) {
addr_v4.sin_family = AF_INET;
addr_v4.sin_port = htons(port);
- conn->sockets[conn->connection_attempts] =
- socket(AF_INET, SOCK_STREAM, 0);
- flags = fcntl(conn->sockets[conn->connection_attempts], F_GETFL, 0);
- fcntl(conn->sockets[conn->connection_attempts], F_SETFL, flags | O_NONBLOCK);
+ conn->sockets[conn->connection_attempts] = rexmpp_tcp_socket(s, AF_INET);
if (connect(conn->sockets[conn->connection_attempts],
(struct sockaddr*)&addr_v4,
sizeof(addr_v4))) {
@@ -144,15 +155,12 @@ rexmpp_tcp_conn_init (rexmpp_t *s,
return REXMPP_CONN_IN_PROGRESS;
}
struct sockaddr_in6 addr_v6;
- if (inet_pton(AF_INET6, host, &addr_v6)) {
+ if (inet_pton(AF_INET6, host, &(addr_v6.sin6_addr))) {
addr_v6.sin6_family = AF_INET6;
addr_v6.sin6_port = htons(port);
addr_v6.sin6_flowinfo = 0;
addr_v6.sin6_scope_id = 0;
- conn->sockets[conn->connection_attempts] =
- socket(AF_INET6, SOCK_STREAM, 0);
- flags = fcntl(conn->sockets[conn->connection_attempts], F_GETFL, 0);
- fcntl(conn->sockets[conn->connection_attempts], F_SETFL, flags | O_NONBLOCK);
+ conn->sockets[conn->connection_attempts] = rexmpp_tcp_socket(s, AF_INET6);
if (connect(conn->sockets[conn->connection_attempts],
(struct sockaddr*)&addr_v6,
sizeof(addr_v6))) {
@@ -201,7 +209,7 @@ rexmpp_tcp_conn_proceed (rexmpp_t *s,
fd_set *write_fds)
{
(void)read_fds; /* Not checking any read FDs at the moment. */
- struct timeval now;
+ struct timespec now;
int i;
/* Check for successful connections. */
@@ -241,10 +249,10 @@ rexmpp_tcp_conn_proceed (rexmpp_t *s,
if (conn->connection_attempts < REXMPP_TCP_MAX_CONNECTION_ATTEMPTS &&
(rexmpp_tcp_conn_ipv4_available(conn) ||
rexmpp_tcp_conn_ipv6_available(conn))) {
- gettimeofday(&now, NULL);
+ clock_gettime(CLOCK_MONOTONIC, &now);
if (now.tv_sec > conn->next_connection_time.tv_sec ||
(now.tv_sec == conn->next_connection_time.tv_sec &&
- now.tv_usec >= conn->next_connection_time.tv_usec)) {
+ now.tv_nsec >= conn->next_connection_time.tv_nsec)) {
/* Time to attempt a new connection. */
int use_ipv6 = 0;
if (rexmpp_tcp_conn_ipv4_available(conn) &&
@@ -295,16 +303,13 @@ rexmpp_tcp_conn_proceed (rexmpp_t *s,
addrlen = sizeof(addr_v4);
}
- conn->sockets[conn->connection_attempts] =
- socket(domain, SOCK_STREAM, 0);
- int flags = fcntl(conn->sockets[conn->connection_attempts], F_GETFL, 0);
- fcntl(conn->sockets[conn->connection_attempts], F_SETFL, flags | O_NONBLOCK);
+ conn->sockets[conn->connection_attempts] = rexmpp_tcp_socket(s, domain);
if (connect(conn->sockets[conn->connection_attempts], addr, addrlen)) {
if (errno == EINPROGRESS) {
- gettimeofday(&(conn->next_connection_time), NULL);
- conn->next_connection_time.tv_usec += REXMPP_TCP_CONN_DELAY_MS * 1000;
- if (conn->next_connection_time.tv_usec >= 1000000) {
- conn->next_connection_time.tv_usec -= 1000000;
+ clock_gettime(CLOCK_MONOTONIC, &(conn->next_connection_time));
+ conn->next_connection_time.tv_nsec += REXMPP_TCP_CONN_DELAY_MS * 1000000;
+ if (conn->next_connection_time.tv_nsec >= 1000000000) {
+ conn->next_connection_time.tv_nsec -= 1000000000;
conn->next_connection_time.tv_sec++;
}
conn->connection_attempts++;
@@ -333,14 +338,14 @@ rexmpp_tcp_conn_proceed (rexmpp_t *s,
}
}
- gettimeofday(&now, NULL);
+ clock_gettime(CLOCK_MONOTONIC, &now);
if (active_connections ||
conn->resolution_v4 == REXMPP_CONN_RESOLUTION_WAITING ||
conn->resolution_v6 == REXMPP_CONN_RESOLUTION_WAITING ||
(conn->next_connection_time.tv_sec > now.tv_sec ||
(conn->next_connection_time.tv_sec == now.tv_sec &&
- conn->next_connection_time.tv_usec > now.tv_usec))) {
+ conn->next_connection_time.tv_nsec > now.tv_nsec))) {
return REXMPP_CONN_IN_PROGRESS;
} else {
return REXMPP_CONN_FAILURE;
@@ -368,13 +373,13 @@ int rexmpp_tcp_conn_fds (rexmpp_t *s,
return max_fd;
}
-struct timeval *rexmpp_tcp_conn_timeout (rexmpp_t *s,
- rexmpp_tcp_conn_t *conn,
- struct timeval *max_tv,
- struct timeval *tv)
+struct timespec *rexmpp_tcp_conn_timeout (rexmpp_t *s,
+ rexmpp_tcp_conn_t *conn,
+ struct timespec *max_tv,
+ struct timespec *tv)
{
- struct timeval now;
- struct timeval *ret = max_tv;
+ struct timespec now;
+ struct timespec *ret = max_tv;
if (conn->resolution_v4 == REXMPP_CONN_RESOLUTION_WAITING ||
conn->resolution_v6 == REXMPP_CONN_RESOLUTION_WAITING) {
ret = rexmpp_dns_timeout(s, max_tv, tv);
@@ -383,20 +388,20 @@ struct timeval *rexmpp_tcp_conn_timeout (rexmpp_t *s,
conn->resolution_v6 == REXMPP_CONN_RESOLUTION_SUCCESS ||
(conn->resolution_v4 == REXMPP_CONN_RESOLUTION_INACTIVE &&
conn->resolution_v6 == REXMPP_CONN_RESOLUTION_INACTIVE)) {
- gettimeofday(&now, NULL);
+ clock_gettime(CLOCK_MONOTONIC, &now);
if (now.tv_sec < conn->next_connection_time.tv_sec ||
(now.tv_sec == conn->next_connection_time.tv_sec &&
- now.tv_usec <= conn->next_connection_time.tv_usec)) {
+ now.tv_nsec <= conn->next_connection_time.tv_nsec)) {
if (ret == NULL ||
ret->tv_sec > conn->next_connection_time.tv_sec - now.tv_sec ||
(ret->tv_sec == conn->next_connection_time.tv_sec - now.tv_sec &&
- ret->tv_usec > conn->next_connection_time.tv_usec - now.tv_usec)) {
+ ret->tv_nsec > conn->next_connection_time.tv_nsec - now.tv_nsec)) {
ret = tv;
tv->tv_sec = conn->next_connection_time.tv_sec - now.tv_sec;
- if (conn->next_connection_time.tv_usec > now.tv_usec) {
- tv->tv_usec = conn->next_connection_time.tv_usec - now.tv_usec;
+ if (conn->next_connection_time.tv_nsec > now.tv_nsec) {
+ tv->tv_nsec = conn->next_connection_time.tv_nsec - now.tv_nsec;
} else {
- tv->tv_usec = conn->next_connection_time.tv_usec + 1000000 - now.tv_usec;
+ tv->tv_nsec = conn->next_connection_time.tv_nsec + 1000000000 - now.tv_nsec;
tv->tv_sec--;
}
}
diff --git a/src/rexmpp_tcp.h b/src/rexmpp_tcp.h
index 5a296cc..8ee32a0 100644
--- a/src/rexmpp_tcp.h
+++ b/src/rexmpp_tcp.h
@@ -20,6 +20,7 @@
#define REXMPP_TCP_H
#include <sys/time.h>
+#include <stdbool.h>
#include "rexmpp.h"
#include "rexmpp_dns.h"
@@ -98,13 +99,13 @@ struct rexmpp_tcp_connection {
/** @brief The number of connection attempts so far. */
int connection_attempts;
- /** @brief Next scheduled connection time. */
- struct timeval next_connection_time;
+ /** @brief Next scheduled connection time (monotonic). */
+ struct timespec next_connection_time;
/** @brief File descriptor of a connected socket. */
int fd;
/** @brief Whether the A or AAAA records used to establish the final
connection were verified with DNSSEC. */
- int dns_secure;
+ bool dns_secure;
};
/**
@@ -175,13 +176,13 @@ int rexmpp_tcp_conn_fds (rexmpp_t *s,
@param[in] s ::rexmpp
@param[in] conn An active connection structure.
@param[in] max_tv An existing maximum timeout.
- @param[out] tv A timeval structure to store a new timeout in.
+ @param[out] tv A timespec structure to store a new timeout in.
@returns A pointer to either max_tv or tv, depending on which one
is smaller.
*/
-struct timeval *rexmpp_tcp_conn_timeout (rexmpp_t *s,
- rexmpp_tcp_conn_t *conn,
- struct timeval *max_tv,
- struct timeval *tv);
+struct timespec *rexmpp_tcp_conn_timeout (rexmpp_t *s,
+ rexmpp_tcp_conn_t *conn,
+ struct timespec *max_tv,
+ struct timespec *tv);
#endif
diff --git a/src/rexmpp_tcp.rs b/src/rexmpp_tcp.rs
new file mode 100644
index 0000000..56c204c
--- /dev/null
+++ b/src/rexmpp_tcp.rs
@@ -0,0 +1,469 @@
+use std::os::raw::{c_int, c_char};
+use libc::*;
+use std::ptr::{null_mut,null};
+use std::mem;
+use errno::{errno};
+
+use super::{rexmpp_dns, rexmpp};
+
+
+#[link(name = "libc")]
+extern {
+ fn inet_pton (af: c_int, src: *const c_char, dst: *mut c_void) -> c_int;
+}
+
+
+const REXMPP_TCP_MAX_CONNECTION_ATTEMPTS: usize = 20;
+const REXMPP_TCP_IPV6_DELAY_MS: i64 = 50;
+const REXMPP_TCP_CONN_DELAY_MS: i64 = 250;
+
+#[derive(PartialEq, Copy, Clone)]
+#[repr(C)]
+pub enum ResolutionStatus {
+ Inactive,
+ Waiting,
+ Success,
+ Failure
+}
+
+#[derive(PartialEq, Copy, Clone)]
+#[repr(C)]
+pub enum ConnectionError {
+ Done,
+ ResolverError,
+ InProgress,
+ Failure,
+ Error
+}
+
+#[repr(C)]
+pub struct RexmppTCPConnection {
+ pub host: *const c_char,
+ pub port: u16,
+ pub resolution_v4: ResolutionStatus,
+ pub resolver_status_v4: c_int,
+ pub resolved_v4: *mut rexmpp_dns::RexmppDNSResult,
+ pub addr_cur_v4: c_int,
+ pub resolution_v6: ResolutionStatus,
+ pub resolver_status_v6: c_int,
+ pub resolved_v6: *mut rexmpp_dns::RexmppDNSResult,
+ pub addr_cur_v6: c_int,
+ pub sockets: [c_int; REXMPP_TCP_MAX_CONNECTION_ATTEMPTS],
+ pub connection_attempts: c_int,
+ pub next_connection_time: timespec,
+ pub fd: c_int,
+ pub dns_secure: bool
+}
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_dns_aaaa_cb (_s: *mut rexmpp::Rexmpp,
+ ptr: *mut c_void,
+ result: *mut rexmpp_dns::RexmppDNSResult)
+ -> () {
+ let conn = ptr as *mut RexmppTCPConnection;
+ (*conn).resolved_v6 = result;
+ if result != null_mut() {
+ (*conn).resolution_v6 = ResolutionStatus::Success;
+ (*conn).addr_cur_v6 = -1;
+ } else {
+ (*conn).resolution_v6 = ResolutionStatus::Failure;
+ }
+}
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_dns_a_cb (_s: *mut rexmpp::Rexmpp,
+ ptr: *mut c_void,
+ result: *mut rexmpp_dns::RexmppDNSResult)
+ -> () {
+ let conn = ptr as *mut RexmppTCPConnection;
+ (*conn).resolved_v4 = result;
+ if result != null_mut() {
+ (*conn).resolution_v4 = ResolutionStatus::Success;
+ (*conn).addr_cur_v4 = -1;
+ if (*conn).resolution_v6 == ResolutionStatus::Waiting {
+ // Wait a bit (usually 50 ms) for IPv6
+ clock_gettime(CLOCK_MONOTONIC, &mut (*conn).next_connection_time);
+ (*conn).next_connection_time.tv_nsec += REXMPP_TCP_IPV6_DELAY_MS * 1000000;
+ if (*conn).next_connection_time.tv_nsec >= 1000000000 {
+ (*conn).next_connection_time.tv_nsec -= 1000000000;
+ (*conn).next_connection_time.tv_sec += 1;
+ }
+ }
+ } else {
+ (*conn).resolution_v4 = ResolutionStatus::Failure;
+ }
+}
+
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_cleanup (conn: *mut RexmppTCPConnection) -> () {
+ for i in 0..=(REXMPP_TCP_MAX_CONNECTION_ATTEMPTS - 1) {
+ if (*conn).sockets[i] != -1 && (*conn).sockets[i] != (*conn).fd {
+ close((*conn).sockets[i]);
+ (*conn).sockets[i] = -1;
+ }
+ }
+ if (*conn).resolution_v4 != ResolutionStatus::Inactive {
+ (*conn).resolution_v4 = ResolutionStatus::Inactive;
+ (*conn).resolution_v6 = ResolutionStatus::Inactive;
+ }
+ if (*conn).resolved_v4 != null_mut() {
+ rexmpp_dns::rexmpp_dns_result_free((*conn).resolved_v4);
+ (*conn).resolved_v4 = null_mut();
+ }
+ if (*conn).resolved_v6 != null_mut() {
+ rexmpp_dns::rexmpp_dns_result_free((*conn).resolved_v6);
+ (*conn).resolved_v6 = null_mut();
+ }
+}
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_connected (conn: *mut RexmppTCPConnection, fd: c_int)
+ -> ConnectionError {
+ let mut sa_ptr = mem::MaybeUninit::<sockaddr>::uninit();
+ let mut sa_len : socklen_t = mem::size_of::<sockaddr>() as u32;
+ getsockname(fd, sa_ptr.as_mut_ptr(), &mut sa_len);
+ let sa = sa_ptr.assume_init();
+ if sa.sa_family == (AF_INET as u16)
+ && (*conn).resolved_v4 != null_mut() {
+ (*conn).dns_secure = (*(*conn).resolved_v4).secure;
+ }
+ else if sa.sa_family == (AF_INET6 as u16)
+ && (*conn).resolved_v6 != null_mut() {
+ (*conn).dns_secure = (*(*conn).resolved_v6).secure;
+ }
+ (*conn).fd = fd;
+ rexmpp_tcp_cleanup(conn);
+ return ConnectionError::Done;
+}
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_socket (s: *mut rexmpp::Rexmpp, domain: c_int) -> c_int {
+ let sock: c_int = socket(domain, SOCK_STREAM, 0);
+
+ // Make it non-blocking
+ let flags: c_int = fcntl(sock, F_GETFL, 0);
+ fcntl(sock, F_SETFL, flags | O_NONBLOCK);
+
+ // Call the socket creation callback, if provided
+ ((*s).socket_cb).map(|cb| cb(s, sock));
+
+ return sock;
+}
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_conn_init (s: *mut rexmpp::Rexmpp,
+ conn: *mut RexmppTCPConnection,
+ host: *const c_char,
+ port: u16)
+ -> ConnectionError {
+ for i in 0..=(REXMPP_TCP_MAX_CONNECTION_ATTEMPTS - 1) {
+ (*conn).sockets[i] = -1;
+ }
+ (*conn).connection_attempts = 0;
+ (*conn).port = port;
+ (*conn).resolved_v4 = null_mut();
+ (*conn).resolved_v6 = null_mut();
+ (*conn).fd = -1;
+ (*conn).dns_secure = false;
+ (*conn).next_connection_time.tv_sec = 0;
+ (*conn).next_connection_time.tv_nsec = 0;
+
+ (*conn).resolution_v4 = ResolutionStatus::Inactive;
+ (*conn).resolution_v6 = ResolutionStatus::Inactive;
+
+ let mut addr_v4 = mem::MaybeUninit::<sockaddr_in>::uninit();
+ if inet_pton(AF_INET, host,
+ &mut ((*addr_v4.as_mut_ptr()).sin_addr)
+ as *mut in_addr as *mut c_void) == 1 {
+ (*addr_v4.as_mut_ptr()).sin_family = AF_INET as u16;
+ (*addr_v4.as_mut_ptr()).sin_port = port.to_be();
+ (*conn).sockets[(*conn).connection_attempts as usize] =
+ rexmpp_tcp_socket(s, AF_INET);
+ if connect((*conn).sockets[(*conn).connection_attempts as usize],
+ addr_v4.as_mut_ptr() as *mut sockaddr,
+ mem::size_of::<sockaddr_in>() as u32) != 0 {
+ if errno().0 != EINPROGRESS {
+ return ConnectionError::Error;
+ }
+ } else {
+ return rexmpp_tcp_connected(conn,
+ (*conn).sockets[(*conn).connection_attempts as usize]);
+ }
+ (*conn).connection_attempts += 1;
+ return ConnectionError::InProgress;
+ }
+
+ let mut addr_v6 = mem::MaybeUninit::<sockaddr_in6>::uninit();
+ if inet_pton(AF_INET6, host,
+ &mut ((*addr_v6.as_mut_ptr()).sin6_addr)
+ as *mut in6_addr as *mut c_void) == 1 {
+ (*addr_v6.as_mut_ptr()).sin6_family = AF_INET as u16;
+ (*addr_v6.as_mut_ptr()).sin6_port = port.to_be();
+ (*addr_v6.as_mut_ptr()).sin6_flowinfo = 0;
+ (*addr_v6.as_mut_ptr()).sin6_scope_id = 0;
+ (*conn).sockets[(*conn).connection_attempts as usize] =
+ rexmpp_tcp_socket(s, AF_INET6);
+ if connect((*conn).sockets[(*conn).connection_attempts as usize],
+ addr_v6.as_mut_ptr() as *mut sockaddr,
+ mem::size_of::<sockaddr_in6>() as u32) != 0 {
+ if errno().0 != EINPROGRESS {
+ return ConnectionError::Error;
+ }
+ } else {
+ return rexmpp_tcp_connected(conn,
+ (*conn).sockets[(*conn).connection_attempts as usize]);
+ }
+ (*conn).connection_attempts += 1;
+ return ConnectionError::InProgress;
+ }
+ (*conn).resolution_v4 = ResolutionStatus::Waiting;
+ (*conn).resolution_v6 = ResolutionStatus::Waiting;
+
+ rexmpp_dns::rexmpp_dns_resolve(s, host, 28, 1,
+ conn as *mut c_void,
+ rexmpp_tcp_dns_aaaa_cb);
+ rexmpp_dns::rexmpp_dns_resolve(s, host, 1, 1,
+ conn as *mut c_void,
+ rexmpp_tcp_dns_a_cb);
+ return ConnectionError::InProgress;
+}
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_conn_finish (conn: *mut RexmppTCPConnection) -> c_int {
+ rexmpp_tcp_cleanup(conn);
+ return (*conn).fd;
+}
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_conn_ipv4_available (conn: *mut RexmppTCPConnection) -> bool {
+ (*conn).resolution_v4 == ResolutionStatus::Success
+ && (*conn).resolved_v4 != null_mut()
+ && *(*(*conn).resolved_v4).data
+ .offset(((*conn).addr_cur_v4 + 1) as isize) != null_mut()
+}
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_conn_ipv6_available (conn: *mut RexmppTCPConnection) -> bool {
+ (*conn).resolution_v6 == ResolutionStatus::Success
+ && (*conn).resolved_v6 != null_mut()
+ && *(*(*conn).resolved_v6).data
+ .offset(((*conn).addr_cur_v6 + 1) as isize) != null_mut()
+}
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_conn_proceed (s: *mut rexmpp::Rexmpp,
+ conn: *mut RexmppTCPConnection,
+ read_fds: *mut fd_set,
+ write_fds: *mut fd_set) -> ConnectionError {
+ // Check for successful connections.
+ for i in 0..=(REXMPP_TCP_MAX_CONNECTION_ATTEMPTS - 1) {
+ if (*conn).sockets[i] != -1 && FD_ISSET((*conn).sockets[i], write_fds) {
+ let mut err: c_int = 0;
+ let mut err_len: socklen_t = mem::size_of::<c_int>() as u32;
+ if getsockopt((*conn).sockets[i], SOL_SOCKET, SO_ERROR,
+ &mut err as *mut c_int as *mut c_void,
+ &mut err_len) < 0 {
+ return ConnectionError::Error;
+ } else {
+ if err == 0 {
+ return rexmpp_tcp_connected(conn, (*conn).sockets[i]);
+ } else if err != EINPROGRESS {
+ close((*conn).sockets[i]);
+ (*conn).sockets[i] = -1;
+ }
+ }
+ }
+ }
+
+ // Name resolution
+ if (*conn).resolution_v4 == ResolutionStatus::Waiting
+ || (*conn).resolution_v6 == ResolutionStatus::Waiting {
+ rexmpp_dns::rexmpp_dns_process(s, read_fds, write_fds);
+ }
+ if (*conn).resolution_v4 == ResolutionStatus::Failure
+ && (*conn).resolution_v6 == ResolutionStatus::Failure {
+ // Failed to resolve anything
+ return ConnectionError::Failure;
+ }
+
+ // New connections
+ let mut repeat: bool;
+ let mut now = mem::MaybeUninit::<timespec>::uninit();
+ let now_ptr = now.as_mut_ptr();
+ loop {
+ repeat = false;
+ if (*conn).connection_attempts < REXMPP_TCP_MAX_CONNECTION_ATTEMPTS as i32
+ && (rexmpp_tcp_conn_ipv4_available(conn)
+ || rexmpp_tcp_conn_ipv6_available(conn)) {
+ clock_gettime(CLOCK_MONOTONIC, now_ptr);
+ if (*now_ptr).tv_sec > (*conn).next_connection_time.tv_sec
+ || ((*now_ptr).tv_sec == (*conn).next_connection_time.tv_sec
+ && (*now_ptr).tv_nsec >= (*conn).next_connection_time.tv_nsec) {
+ // Time to attempt a new connection
+ let mut use_ipv6 = false;
+ if rexmpp_tcp_conn_ipv4_available(conn) &&
+ rexmpp_tcp_conn_ipv6_available(conn) {
+ if (*conn).addr_cur_v4 >= (*conn).addr_cur_v6 {
+ use_ipv6 = true;
+ }
+ } else if rexmpp_tcp_conn_ipv6_available(conn) {
+ use_ipv6 = true;
+ }
+
+ let addr: *mut sockaddr;
+ let addrlen: socklen_t;
+ let domain: c_int;
+ if use_ipv6 {
+ let mut addr_v6: sockaddr_in6 = mem::zeroed();
+ (*conn).addr_cur_v6 += 1;
+ let len = (mem::size_of::<in6_addr>() as i32)
+ .min(*(*(*conn).resolved_v6).len.offset((*conn).addr_cur_v6 as isize));
+ memcpy(&mut addr_v6.sin6_addr as *mut in6_addr as *mut c_void,
+ *(*(*conn).resolved_v6).data.offset((*conn).addr_cur_v6 as isize),
+ len as usize);
+ addr_v6.sin6_family = AF_INET6 as u16;
+ addr_v6.sin6_port = (*conn).port.to_be();
+ addr_v6.sin6_flowinfo = 0;
+ addr_v6.sin6_scope_id = 0;
+ domain = AF_INET6;
+ addr = &mut addr_v6 as *mut sockaddr_in6 as *mut sockaddr;
+ addrlen = mem::size_of::<sockaddr_in6>() as u32;
+ } else {
+ let mut addr_v4: sockaddr_in = mem::zeroed();
+ (*conn).addr_cur_v4 += 1;
+ let len = (mem::size_of::<in_addr>() as i32)
+ .min(*(*(*conn).resolved_v4).len.offset((*conn).addr_cur_v4 as isize));
+ memcpy(&mut addr_v4.sin_addr as *mut in_addr as *mut c_void,
+ *(*(*conn).resolved_v4).data.offset((*conn).addr_cur_v4 as isize),
+ len as usize);
+ addr_v4.sin_family = AF_INET as u16;
+ addr_v4.sin_port = (*conn).port.to_be();
+ domain = AF_INET;
+ addr = &mut addr_v4 as *mut sockaddr_in as *mut sockaddr;
+ addrlen = mem::size_of::<sockaddr_in>() as u32;
+ }
+ (*conn).sockets[(*conn).connection_attempts as usize] =
+ rexmpp_tcp_socket(s, domain);
+ if connect((*conn).sockets[(*conn).connection_attempts as usize],
+ addr, addrlen) != 0 {
+ if errno().0 == EINPROGRESS {
+ clock_gettime(CLOCK_MONOTONIC, &mut (*conn).next_connection_time);
+ (*conn).next_connection_time.tv_nsec +=
+ REXMPP_TCP_CONN_DELAY_MS * 1000000;
+ if (*conn).next_connection_time.tv_nsec >= 1000000000 {
+ (*conn).next_connection_time.tv_nsec -= 1000000000;
+ (*conn).next_connection_time.tv_sec += 1;
+ }
+ (*conn).connection_attempts += 1;
+ } else {
+ close((*conn).sockets[(*conn).connection_attempts as usize]);
+ (*conn).sockets[(*conn).connection_attempts as usize] = -1;
+ if (*conn).connection_attempts < REXMPP_TCP_MAX_CONNECTION_ATTEMPTS as i32
+ && (rexmpp_tcp_conn_ipv4_available(conn) ||
+ rexmpp_tcp_conn_ipv6_available(conn)) {
+ repeat = true;
+ }
+ }
+ } else {
+ return rexmpp_tcp_connected(conn,
+ (*conn).sockets[(*conn).connection_attempts as usize]);
+ }
+ }
+ }
+ if ! repeat {
+ break;
+ }
+ }
+
+ let mut active_connections = false;
+ for i in 0..=(REXMPP_TCP_MAX_CONNECTION_ATTEMPTS - 1) {
+ if (*conn).sockets[i] != -1 {
+ active_connections = true;
+ break;
+ }
+ }
+
+ clock_gettime(CLOCK_MONOTONIC, now_ptr);
+
+ if active_connections
+ || (*conn).resolution_v4 == ResolutionStatus::Waiting
+ || (*conn).resolution_v6 == ResolutionStatus::Waiting
+ || ((*conn).next_connection_time.tv_sec > (*now_ptr).tv_sec
+ || ((*conn).next_connection_time.tv_sec == (*now_ptr).tv_sec
+ && (*conn).next_connection_time.tv_nsec > (*now_ptr).tv_nsec)) {
+ ConnectionError::InProgress
+ } else {
+ ConnectionError::Failure
+ }
+}
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_conn_fds (s: *mut rexmpp::Rexmpp,
+ conn: *mut RexmppTCPConnection,
+ read_fds: *mut fd_set,
+ write_fds: *mut fd_set) -> c_int {
+ let mut max_fd: c_int = 0;
+ if (*conn).resolution_v4 == ResolutionStatus::Waiting
+ || (*conn).resolution_v6 == ResolutionStatus::Waiting {
+ max_fd = rexmpp_dns::rexmpp_dns_fds(s, read_fds, write_fds);
+ }
+ for i in 0..=(REXMPP_TCP_MAX_CONNECTION_ATTEMPTS - 1) {
+ if (*conn).sockets[i] != -1 {
+ FD_SET((*conn).sockets[i], write_fds);
+ if max_fd < (*conn).sockets[i] + 1 {
+ max_fd = (*conn).sockets[i] + 1;
+ }
+ }
+ }
+ max_fd
+}
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_conn_timeout (s: *mut rexmpp::Rexmpp,
+ conn: *mut RexmppTCPConnection,
+ max_tv: *mut timespec,
+ tv: *mut timespec) -> *mut timespec {
+ let mut now: timespec = mem::zeroed();
+ let mut ret: *mut timespec = max_tv;
+ if (*conn).resolution_v4 == ResolutionStatus::Waiting
+ || (*conn).resolution_v6 == ResolutionStatus::Waiting {
+ ret = rexmpp_dns::rexmpp_dns_timeout(s, max_tv, tv);
+ }
+ if (*conn).resolution_v4 == ResolutionStatus::Success
+ || (*conn).resolution_v6 == ResolutionStatus::Success
+ || ((*conn).resolution_v4 == ResolutionStatus::Inactive
+ && (*conn).resolution_v4 == ResolutionStatus::Inactive) {
+ clock_gettime(CLOCK_MONOTONIC, &mut now);
+ if now.tv_sec < (*conn).next_connection_time.tv_sec
+ || (now.tv_sec == (*conn).next_connection_time.tv_sec
+ && now.tv_nsec <= (*conn).next_connection_time.tv_nsec) {
+ if ret == null_mut()
+ || (*ret).tv_sec > (*conn).next_connection_time.tv_sec - now.tv_sec
+ || ((*ret).tv_sec == (*conn).next_connection_time.tv_sec - now.tv_sec
+ && (*ret).tv_nsec > (*conn).next_connection_time.tv_nsec - now.tv_sec) {
+ ret = tv;
+ (*tv).tv_sec = (*conn).next_connection_time.tv_sec - now.tv_sec;
+ if (*conn).next_connection_time.tv_nsec > now.tv_nsec {
+ (*tv).tv_nsec = (*conn).next_connection_time.tv_nsec - now.tv_nsec;
+ } else {
+ (*tv).tv_nsec = (*conn).next_connection_time.tv_nsec + 1000000000 - now.tv_nsec;
+ (*tv).tv_sec -= 1;
+ }
+ }
+ }
+ }
+ ret
+}
diff --git a/src/rexmpp_tls.c b/src/rexmpp_tls.c
index 7919556..2a7c903 100644
--- a/src/rexmpp_tls.c
+++ b/src/rexmpp_tls.c
@@ -8,6 +8,7 @@
#include <syslog.h>
#include <string.h>
+#include <stdlib.h>
#include "config.h"
@@ -16,160 +17,382 @@
#include <gnutls/crypto.h>
#include <gnutls/x509.h>
#include <gnutls/dane.h>
+#include <gnutls/dtls.h>
#elif defined(USE_OPENSSL)
#include <openssl/ssl.h>
+#include <openssl/x509.h>
+#include <openssl/err.h>
#endif
#include "rexmpp.h"
+#include "rexmpp_digest.h"
#include "rexmpp_tls.h"
+rexmpp_tls_t *rexmpp_jingle_component_dtls(void *p);
+ssize_t
+rexmpp_jingle_dtls_push_func (void *p, const void *data, size_t size);
+int rexmpp_jingle_dtls_pull_timeout_func (void *p,
+ unsigned int ms);
+
#if defined(USE_OPENSSL)
-rexmpp_tls_err_t rexmpp_process_openssl_ret (rexmpp_t *s, int ret) {
- int err = SSL_get_error(s->tls.openssl_conn, ret);
- s->tls.openssl_direction = REXMPP_OPENSSL_NONE;
+rexmpp_tls_err_t rexmpp_process_openssl_ret (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ const char *func,
+ int ret)
+{
+ int err = SSL_get_error(tls_ctx->openssl_conn, ret);
+ tls_ctx->openssl_direction = REXMPP_OPENSSL_NONE;
if (ret == 1) {
return REXMPP_TLS_SUCCESS;
} else if (err == SSL_ERROR_WANT_READ) {
- s->tls.openssl_direction = REXMPP_OPENSSL_READ;
+ tls_ctx->openssl_direction = REXMPP_OPENSSL_READ;
return REXMPP_TLS_E_AGAIN;
} else if (err == SSL_ERROR_WANT_WRITE) {
- s->tls.openssl_direction = REXMPP_OPENSSL_WRITE;
+ tls_ctx->openssl_direction = REXMPP_OPENSSL_WRITE;
return REXMPP_TLS_E_AGAIN;
} else {
- rexmpp_log(s, LOG_ERR, "OpenSSL error %d", err);
+ rexmpp_log(s, LOG_ERR, "OpenSSL error %d (ret %d) in %s",
+ err, ret, func);
+ ERR_print_errors_fp(stderr);
return REXMPP_TLS_E_OTHER;
}
}
#endif
-int rexmpp_tls_init (rexmpp_t *s) {
+rexmpp_tls_t *rexmpp_tls_ctx_new (rexmpp_t *s, int dtls) {
+ rexmpp_tls_t *tls_ctx = malloc(sizeof(rexmpp_tls_t));
+ if (tls_ctx == NULL) {
+ rexmpp_log(s, LOG_CRIT, "Failed to allocate memory for a TLS context");
+ return NULL;
+ }
#if defined(USE_GNUTLS)
+ (void)dtls;
int err;
- s->tls.tls_session_data = NULL;
- s->tls.tls_session_data_size = 0;
+ tls_ctx->tls_session_data = NULL;
+ tls_ctx->tls_session_data_size = 0;
- err = gnutls_certificate_allocate_credentials(&(s->tls.gnutls_cred));
+ err = gnutls_certificate_allocate_credentials(&(tls_ctx->gnutls_cred));
if (err) {
rexmpp_log(s, LOG_CRIT, "gnutls credentials allocation error: %s",
gnutls_strerror(err));
- return 1;
+ free(tls_ctx);
+ return NULL;
+ }
+ if (! dtls) {
+ err = gnutls_certificate_set_x509_system_trust(tls_ctx->gnutls_cred);
}
- err = gnutls_certificate_set_x509_system_trust(s->tls.gnutls_cred);
if (err < 0) {
rexmpp_log(s, LOG_CRIT, "Certificates loading error: %s",
gnutls_strerror(err));
- return 1;
+ free(tls_ctx);
+ return NULL;
}
-#ifdef ENABLE_CALLS
- err = gnutls_certificate_allocate_credentials(&(s->jingle.dtls_cred));
- if (err) {
- gnutls_certificate_free_credentials(s->tls.gnutls_cred);
- rexmpp_log(s, LOG_CRIT, "gnutls credentials allocation error: %s",
- gnutls_strerror(err));
- return 1;
- }
-#endif
- return 0;
+
+ tls_ctx->dtls_buf_len = 0;
#elif defined(USE_OPENSSL)
- SSL_library_init();
- SSL_load_error_strings();
- s->tls.openssl_direction = REXMPP_OPENSSL_NONE;
- s->tls.openssl_conn = NULL;
- s->tls.openssl_ctx = SSL_CTX_new(TLS_client_method());
- if (s->tls.openssl_ctx == NULL) {
+ tls_ctx->openssl_direction = REXMPP_OPENSSL_NONE;
+ tls_ctx->openssl_conn = NULL;
+ tls_ctx->openssl_ctx = SSL_CTX_new(dtls
+ ? DTLS_method()
+ : TLS_method());
+ if (tls_ctx->openssl_ctx == NULL) {
rexmpp_log(s, LOG_CRIT, "OpenSSL context creation error");
- return 1;
+ free(tls_ctx);
+ return NULL;
}
- SSL_CTX_set_verify(s->tls.openssl_ctx, SSL_VERIFY_PEER, NULL);
- if (SSL_CTX_set_default_verify_paths(s->tls.openssl_ctx) == 0) {
- rexmpp_log(s, LOG_CRIT, "Failed to set default verify paths for OpenSSL context");
- SSL_CTX_free(s->tls.openssl_ctx);
- s->tls.openssl_ctx = NULL;
- return 1;
+ SSL_CTX_set_verify(tls_ctx->openssl_ctx, SSL_VERIFY_PEER, NULL);
+ if (SSL_CTX_set_default_verify_paths(tls_ctx->openssl_ctx) == 0) {
+ rexmpp_log(s, LOG_CRIT,
+ "Failed to set default verify paths for OpenSSL context");
+ SSL_CTX_free(tls_ctx->openssl_ctx);
+ tls_ctx->openssl_ctx = NULL;
+ free(tls_ctx);
+ return NULL;
}
- return 0;
#else
(void)s;
- return 0;
+ (void)dtls;
#endif
+ return tls_ctx;
}
+void rexmpp_tls_ctx_free (rexmpp_tls_t *tls_ctx) {
+#if defined(USE_GNUTLS)
+ gnutls_certificate_free_credentials(tls_ctx->gnutls_cred);
+ if (tls_ctx->tls_session_data != NULL) {
+ free(tls_ctx->tls_session_data);
+ tls_ctx->tls_session_data = NULL;
+ }
+#elif defined(USE_OPENSSL)
+ if (tls_ctx->openssl_ctx != NULL) {
+ SSL_CTX_free(tls_ctx->openssl_ctx);
+ }
+ tls_ctx->openssl_ctx = NULL;
+#endif
+ free(tls_ctx);
+}
-void rexmpp_tls_cleanup (rexmpp_t *s) {
- if (s->tls_state != REXMPP_TLS_INACTIVE &&
- s->tls_state != REXMPP_TLS_AWAITING_DIRECT) {
+int rexmpp_tls_init (rexmpp_t *s) {
+#if defined(USE_OPENSSL)
+ SSL_library_init();
+ SSL_load_error_strings();
+#endif
+ s->tls = rexmpp_tls_ctx_new(s, 0);
+ return (s->tls == NULL);
+}
+
+void rexmpp_tls_session_free (rexmpp_tls_t *tls_ctx) {
#if defined(USE_GNUTLS)
- gnutls_deinit(s->tls.gnutls_session);
+ gnutls_deinit(tls_ctx->gnutls_session);
#elif defined(USE_OPENSSL)
- if (s->tls.openssl_conn != NULL) {
- SSL_free(s->tls.openssl_conn);
- s->tls.openssl_conn = NULL;
+ if (tls_ctx->openssl_conn != NULL) {
+ SSL_free(tls_ctx->openssl_conn);
+ tls_ctx->openssl_conn = NULL;
+ /* bio_conn is freed implicitly by SSL_free. */
+ tls_ctx->bio_conn = NULL;
}
- s->tls.openssl_direction = REXMPP_OPENSSL_NONE;
+ if (tls_ctx->bio_io != NULL) {
+ BIO_free(tls_ctx->bio_io);
+ tls_ctx->bio_io = NULL;
+ }
+ tls_ctx->openssl_direction = REXMPP_OPENSSL_NONE;
#else
- (void)s;
+ (void)tls_ctx;
#endif
+}
+
+void rexmpp_tls_cleanup (rexmpp_t *s) {
+ if (s->tls_state != REXMPP_TLS_INACTIVE &&
+ s->tls_state != REXMPP_TLS_AWAITING_DIRECT) {
+ rexmpp_tls_session_free(s->tls);
}
}
void rexmpp_tls_deinit (rexmpp_t *s) {
+ if (s->tls != NULL) {
+ rexmpp_tls_ctx_free(s->tls);
+ s->tls = NULL;
+ }
+}
+
+#if defined(USE_GNUTLS)
+ssize_t
+rexmpp_dtls_jingle_pull_func_gnutls (gnutls_transport_ptr_t p,
+ void *data,
+ size_t size)
+{
+ rexmpp_tls_t *tls_ctx = rexmpp_jingle_component_dtls(p);
+ ssize_t received;
+
+ char *tls_buf = tls_ctx->dtls_buf;
+ size_t *tls_buf_len = &(tls_ctx->dtls_buf_len);
+
+ rexmpp_tls_err_t ret = REXMPP_TLS_SUCCESS;
+ if (*tls_buf_len > 0) {
+ if (size >= *tls_buf_len) {
+ memcpy(data, tls_buf, *tls_buf_len);
+ received = *tls_buf_len;
+ *tls_buf_len = 0;
+ } else {
+ if (size > DTLS_SRTP_BUF_SIZE) {
+ size = DTLS_SRTP_BUF_SIZE;
+ }
+ memcpy(data, tls_buf, size);
+ memmove(tls_buf, tls_buf + size, DTLS_SRTP_BUF_SIZE - size);
+ received = size;
+ *tls_buf_len = *tls_buf_len - size;
+ }
+ } else {
+ ret = REXMPP_TLS_E_AGAIN;
+ }
+
+ if (ret == REXMPP_TLS_SUCCESS) {
+ return received;
+ } else if (ret == REXMPP_TLS_E_AGAIN) {
+ gnutls_transport_set_errno(tls_ctx->gnutls_session, EAGAIN);
+ }
+ return -1;
+}
+#endif
+
+#if defined(USE_OPENSSL)
+long rexmpp_dtls_openssl_bio_cb(BIO *b, int oper, const char *argp,
+ size_t len, int argi,
+ long argl, int ret, size_t *processed) {
+ (void)argi;
+ (void)argl;
+ (void)processed;
+ if (oper == BIO_CB_WRITE) {
+ rexmpp_jingle_dtls_push_func(BIO_get_callback_arg(b), argp, len);
+ }
+ return ret;
+}
+#endif
+
+#if defined(USE_OPENSSL)
+int rexmpp_openssl_verify_accept_all (int preverify_ok,
+ X509_STORE_CTX *x509_ctx)
+{
+ (void)preverify_ok;
+ (void)x509_ctx;
+ return 1;
+}
+#endif
+
+rexmpp_tls_err_t
+rexmpp_dtls_connect (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ void *user_data,
+ int client) {
#if defined(USE_GNUTLS)
- gnutls_certificate_free_credentials(s->tls.gnutls_cred);
- if (s->tls.tls_session_data != NULL) {
- free(s->tls.tls_session_data);
- s->tls.tls_session_data = NULL;
+ gnutls_session_t *tls_session = &(tls_ctx->gnutls_session);
+ gnutls_init(tls_session,
+ (client ? GNUTLS_CLIENT : GNUTLS_SERVER) |
+ GNUTLS_DATAGRAM |
+ GNUTLS_NONBLOCK);
+ if (! client) {
+ gnutls_certificate_server_set_request(*tls_session, GNUTLS_CERT_REQUIRE);
+ }
+ gnutls_set_default_priority(*tls_session);
+ rexmpp_tls_set_x509_key_file(s, tls_ctx, NULL, NULL);
+ gnutls_credentials_set(*tls_session, GNUTLS_CRD_CERTIFICATE,
+ tls_ctx->gnutls_cred);
+
+ gnutls_transport_set_ptr(*tls_session, user_data);
+ gnutls_transport_set_push_function
+ (*tls_session, rexmpp_jingle_dtls_push_func);
+ gnutls_transport_set_pull_function
+ (*tls_session, rexmpp_dtls_jingle_pull_func_gnutls);
+ gnutls_transport_set_pull_timeout_function
+ (*tls_session, rexmpp_jingle_dtls_pull_timeout_func);
+ /* todo: use the profile/crypto-suite from <crypto/> element */
+ gnutls_srtp_set_profile(*tls_session, GNUTLS_SRTP_AES128_CM_HMAC_SHA1_80);
+ return REXMPP_TLS_SUCCESS;
+#elif defined(USE_OPENSSL)
+ (void)client;
+ int err;
+ /* Setup credentials */
+ rexmpp_tls_set_x509_key_file(s, tls_ctx, NULL, NULL);
+ /* Create a connection. */
+ tls_ctx->openssl_conn = SSL_new(tls_ctx->openssl_ctx);
+ SSL_set_verify(tls_ctx->openssl_conn,
+ SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+ rexmpp_openssl_verify_accept_all);
+ /* Set a BIO */
+ BIO_new_bio_pair(&(tls_ctx->bio_conn), 4096, &(tls_ctx->bio_io), 4096);
+ BIO_up_ref(tls_ctx->bio_conn);
+ SSL_set0_rbio(tls_ctx->openssl_conn, tls_ctx->bio_conn);
+ SSL_set0_wbio(tls_ctx->openssl_conn, tls_ctx->bio_conn);
+ /* Set a callback to track writes */
+ BIO_set_callback_ex(tls_ctx->bio_conn, rexmpp_dtls_openssl_bio_cb);
+ BIO_set_callback_arg(tls_ctx->bio_conn, user_data);
+ BIO_set_ssl(tls_ctx->bio_conn, tls_ctx->openssl_conn, BIO_NOCLOSE);
+ /* Enable SRTP (TODO: support different profiles) */
+ err = SSL_set_tlsext_use_srtp(tls_ctx->openssl_conn,
+ "SRTP_AES128_CM_SHA1_80");
+ if (err) {
+ rexmpp_log(s, LOG_ERR, "Failed to setup SRTP for the DTLS connection");
+ return REXMPP_TLS_E_OTHER;
+ }
+ if (client) {
+ err = SSL_connect(tls_ctx->openssl_conn);
+ } else {
+ err = SSL_accept(tls_ctx->openssl_conn);
}
-#ifdef ENABLE_CALLS
- gnutls_certificate_free_credentials(s->jingle.dtls_cred);
+ return rexmpp_process_openssl_ret(s, tls_ctx, "rexmpp_dtls_connect", err);
+#else
+ (void)s;
+ (void)tls_ctx;
+ (void)user_data;
+ (void)client;
+ return REXMPP_TLS_E_OTHER;
#endif
+}
+
+void rexmpp_dtls_feed(rexmpp_t *s, rexmpp_tls_t *tls_ctx, uint8_t *buf, size_t len) {
+#if defined(USE_GNUTLS)
+ if (tls_ctx->dtls_buf_len + len < DTLS_SRTP_BUF_SIZE) {
+ memcpy(tls_ctx->dtls_buf + tls_ctx->dtls_buf_len, buf, len);
+ tls_ctx->dtls_buf_len += len;
+ } else {
+ rexmpp_log(s, LOG_WARNING, "Dropping a DTLS packet");
+ }
#elif defined(USE_OPENSSL)
- if (s->tls.openssl_ctx != NULL) {
- SSL_CTX_free(s->tls.openssl_ctx);
+ (void)s;
+ BIO_write(tls_ctx->bio_io, buf, len);
+#else
+ (void)s;
+ (void)tls_ctx;
+ (void)buf;
+ (void)len;
+#endif
+}
+
+rexmpp_tls_err_t rexmpp_tls_handshake (rexmpp_t *s, rexmpp_tls_t *tls_ctx) {
+#if defined(USE_GNUTLS)
+ int ret = gnutls_handshake(tls_ctx->gnutls_session);
+ if (ret == 0) {
+ return REXMPP_TLS_SUCCESS;
+ } else if (ret == GNUTLS_E_AGAIN) {
+ return REXMPP_TLS_E_AGAIN;
+ } else {
+ rexmpp_log(s, LOG_ERR, "Error during a TLS handshake: %s",
+ gnutls_strerror(ret));
+ return REXMPP_TLS_E_OTHER;
}
- s->tls.openssl_ctx = NULL;
+#elif defined(USE_OPENSSL)
+ return rexmpp_process_openssl_ret(s, tls_ctx, "rexmpp_tls_handshake",
+ SSL_do_handshake(tls_ctx->openssl_conn));
#else
(void)s;
+ (void)tls_ctx;
+ return REXMPP_TLS_E_OTHER;
#endif
}
rexmpp_tls_err_t
rexmpp_tls_connect (rexmpp_t *s) {
+ if (s->x509_key_file != NULL && s->x509_cert_file != NULL) {
+ rexmpp_tls_set_x509_key_file(s, s->tls, NULL, NULL);
+ }
+
#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);
- gnutls_alpn_set_protocols(s->tls.gnutls_session, &xmpp_client_protocol, 1, 0);
- gnutls_server_name_set(s->tls.gnutls_session, GNUTLS_NAME_DNS,
+ gnutls_init(&s->tls->gnutls_session, GNUTLS_CLIENT);
+ gnutls_session_set_ptr(s->tls->gnutls_session, s);
+ gnutls_alpn_set_protocols(s->tls->gnutls_session, &xmpp_client_protocol, 1, 0);
+ gnutls_server_name_set(s->tls->gnutls_session, GNUTLS_NAME_DNS,
s->initial_jid.domain,
strlen(s->initial_jid.domain));
- gnutls_set_default_priority(s->tls.gnutls_session);
- gnutls_credentials_set(s->tls.gnutls_session, GNUTLS_CRD_CERTIFICATE,
- s->tls.gnutls_cred);
- gnutls_transport_set_int(s->tls.gnutls_session, s->server_socket);
- gnutls_handshake_set_timeout(s->tls.gnutls_session,
+ gnutls_set_default_priority(s->tls->gnutls_session);
+ gnutls_credentials_set(s->tls->gnutls_session, GNUTLS_CRD_CERTIFICATE,
+ s->tls->gnutls_cred);
+ gnutls_transport_set_int(s->tls->gnutls_session, s->server_socket);
+ gnutls_handshake_set_timeout(s->tls->gnutls_session,
GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
- if (s->tls.tls_session_data != NULL) {
- int ret = gnutls_session_set_data(s->tls.gnutls_session,
- s->tls.tls_session_data,
- s->tls.tls_session_data_size);
+ if (s->tls->tls_session_data != NULL) {
+ int ret = gnutls_session_set_data(s->tls->gnutls_session,
+ s->tls->tls_session_data,
+ s->tls->tls_session_data_size);
if (ret != GNUTLS_E_SUCCESS) {
rexmpp_log(s, LOG_WARNING, "Failed to set TLS session data: %s",
gnutls_strerror(ret));
- free(s->tls.tls_session_data);
- s->tls.tls_session_data = NULL;
- s->tls.tls_session_data_size = 0;
+ free(s->tls->tls_session_data);
+ s->tls->tls_session_data = NULL;
+ s->tls->tls_session_data_size = 0;
}
}
}
- int ret = gnutls_handshake(s->tls.gnutls_session);
+ int ret = gnutls_handshake(s->tls->gnutls_session);
if (ret == GNUTLS_E_AGAIN) {
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 &&
@@ -190,7 +413,7 @@ rexmpp_tls_connect (rexmpp_t *s) {
service/source host
(<https://tools.ietf.org/html/rfc7712#section-5.1>,
<https://tools.ietf.org/html/rfc7673#section-6>). */
- ret = dane_verify_session_crt(NULL, s->tls.gnutls_session, s->server_host,
+ ret = dane_verify_session_crt(NULL, s->tls->gnutls_session, s->server_host,
"tcp", s->server_port, 0, 0, &status);
if (ret) {
rexmpp_log(s, LOG_WARNING, "DANE verification error: %s",
@@ -212,7 +435,7 @@ rexmpp_tls_connect (rexmpp_t *s) {
}
}
- ret = gnutls_certificate_verify_peers3(s->tls.gnutls_session,
+ ret = gnutls_certificate_verify_peers3(s->tls->gnutls_session,
s->initial_jid.domain,
&status);
if (ret || status) {
@@ -224,23 +447,23 @@ rexmpp_tls_connect (rexmpp_t *s) {
} else {
rexmpp_log(s, LOG_ERR, "Untrusted certificate");
}
- gnutls_bye(s->tls.gnutls_session, GNUTLS_SHUT_RDWR);
+ gnutls_bye(s->tls->gnutls_session, GNUTLS_SHUT_RDWR);
return REXMPP_TLS_E_OTHER;
}
- if (gnutls_session_is_resumed(s->tls.gnutls_session)) {
+ if (gnutls_session_is_resumed(s->tls->gnutls_session)) {
rexmpp_log(s, LOG_INFO, "TLS session is resumed");
} else {
- if (s->tls.tls_session_data != NULL) {
+ if (s->tls->tls_session_data != NULL) {
rexmpp_log(s, LOG_DEBUG, "TLS session is not resumed");
- free(s->tls.tls_session_data);
- s->tls.tls_session_data = NULL;
+ free(s->tls->tls_session_data);
+ s->tls->tls_session_data = NULL;
}
- gnutls_session_get_data(s->tls.gnutls_session, NULL,
- &s->tls.tls_session_data_size);
- s->tls.tls_session_data = malloc(s->tls.tls_session_data_size);
- ret = gnutls_session_get_data(s->tls.gnutls_session, s->tls.tls_session_data,
- &s->tls.tls_session_data_size);
+ gnutls_session_get_data(s->tls->gnutls_session, NULL,
+ &s->tls->tls_session_data_size);
+ s->tls->tls_session_data = malloc(s->tls->tls_session_data_size);
+ ret = gnutls_session_get_data(s->tls->gnutls_session, s->tls->tls_session_data,
+ &s->tls->tls_session_data_size);
if (ret != GNUTLS_E_SUCCESS) {
rexmpp_log(s, LOG_ERR, "Failed to get TLS session data: %s",
gnutls_strerror(ret));
@@ -256,26 +479,27 @@ rexmpp_tls_connect (rexmpp_t *s) {
}
#elif defined(USE_OPENSSL)
if (s->tls_state != REXMPP_TLS_HANDSHAKE) {
- s->tls.openssl_conn = SSL_new(s->tls.openssl_ctx);
- if (s->tls.openssl_conn == NULL) {
+ s->tls->openssl_conn = SSL_new(s->tls->openssl_ctx);
+ if (s->tls->openssl_conn == NULL) {
rexmpp_log(s, LOG_ERR, "Failed to create an OpenSSL connection object");
return REXMPP_TLS_E_OTHER;
}
- if (SSL_set_fd(s->tls.openssl_conn, s->server_socket) == 0) {
+ if (SSL_set_fd(s->tls->openssl_conn, s->server_socket) == 0) {
rexmpp_log(s, LOG_ERR, "Failed to set a file descriptor for OpenSSL connection");
return REXMPP_TLS_E_OTHER;
}
- if (SSL_set1_host(s->tls.openssl_conn, s->initial_jid.domain) == 0) {
+ if (SSL_set1_host(s->tls->openssl_conn, s->initial_jid.domain) == 0) {
rexmpp_log(s, LOG_ERR, "Failed to set a hostname for OpenSSL connection");
return REXMPP_TLS_E_OTHER;
}
/* For SNI */
- if (SSL_set_tlsext_host_name(s->tls.openssl_conn, s->initial_jid.domain) == 0) {
+ if (SSL_set_tlsext_host_name(s->tls->openssl_conn, s->initial_jid.domain) == 0) {
rexmpp_log(s, LOG_ERR, "Failed to set a tlsext hostname for OpenSSL connection");
return REXMPP_TLS_E_OTHER;
}
}
- return rexmpp_process_openssl_ret(s, SSL_connect(s->tls.openssl_conn));
+ return rexmpp_process_openssl_ret(s, s->tls, "rexmpp_tls_connect",
+ SSL_connect(s->tls->openssl_conn));
#else
rexmpp_log(s, LOG_ERR, "rexmpp is compiled without TLS support");
return REXMPP_TLS_E_OTHER;
@@ -283,36 +507,84 @@ rexmpp_tls_connect (rexmpp_t *s) {
}
rexmpp_tls_err_t
-rexmpp_tls_disconnect (rexmpp_t *s) {
+rexmpp_tls_disconnect (rexmpp_t *s, rexmpp_tls_t *tls_ctx) {
#if defined(USE_GNUTLS)
- int ret = gnutls_bye(s->tls.gnutls_session, GNUTLS_SHUT_RDWR);
+ int ret = gnutls_bye(tls_ctx->gnutls_session, GNUTLS_SHUT_RDWR);
if (ret == GNUTLS_E_SUCCESS) {
return REXMPP_TLS_SUCCESS;
+ } else if (ret == GNUTLS_E_AGAIN) {
+ return REXMPP_TLS_E_AGAIN;
} else {
rexmpp_log(s, LOG_WARNING, "Failed to close TLS connection: %s",
gnutls_strerror(ret));
return REXMPP_TLS_E_OTHER;
}
#elif defined(USE_OPENSSL)
- int ret = SSL_shutdown(s->tls.openssl_conn);
+ int ret = SSL_shutdown(tls_ctx->openssl_conn);
if (ret == 0) {
- s->tls.openssl_direction = REXMPP_OPENSSL_READ;
+ tls_ctx->openssl_direction = REXMPP_OPENSSL_READ;
return REXMPP_TLS_E_AGAIN;
} else {
- return rexmpp_process_openssl_ret(s, ret);
+ return rexmpp_process_openssl_ret(s, tls_ctx,
+ "rexmpp_tls_disconnect", ret);
}
#else
+ (void)tls_ctx;
rexmpp_log(s, LOG_ERR, "rexmpp is compiled without TLS support");
return REXMPP_TLS_E_OTHER;
#endif
}
-rexmpp_tls_err_t
-rexmpp_tls_send (rexmpp_t *s, void *data, size_t data_size, ssize_t *written)
+int
+rexmpp_tls_srtp_get_keys (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ size_t key_len,
+ size_t salt_len,
+ unsigned char *key_mat)
{
#if defined(USE_GNUTLS)
+ int key_mat_size;
+ key_mat_size =
+ gnutls_srtp_get_keys(tls_ctx->gnutls_session,
+ key_mat, (key_len + salt_len) * 2,
+ NULL, NULL, NULL, NULL);
+ if (key_mat_size == GNUTLS_E_SHORT_MEMORY_BUFFER ||
+ key_mat_size < 0) {
+ rexmpp_log(s, LOG_ERR,
+ "Failed to retrieve DTLS key material for SRTP: %s",
+ gnutls_strerror(key_mat_size));
+ }
+ return 0;
+#elif defined(USE_OPENSSL)
+ /* https://www.rfc-editor.org/rfc/rfc5764.html */
+ const char *extractor = "EXTRACTOR-dtls_srtp";
+ int err = SSL_export_keying_material(tls_ctx->openssl_conn,
+ key_mat, 2 * (key_len + salt_len),
+ extractor, strlen(extractor),
+ NULL, 0, 0);
+ return rexmpp_process_openssl_ret(s, tls_ctx,
+ "rexmpp_tls_srtp_get_keys", err);
+#else
+ (void)s;
+ (void)tls_ctx;
+ (void)key_len;
+ (void)salt_len;
+ (void)key_mat;
+ rexmpp_log(s, LOG_ERR, "rexmpp is compiled without TLS support");
+ return -1;
+#endif
+}
+
+rexmpp_tls_err_t
+rexmpp_tls_send (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ void *data,
+ size_t data_size,
+ ssize_t *written)
+{
*written = -1;
- ssize_t ret = gnutls_record_send(s->tls.gnutls_session,
+#if defined(USE_GNUTLS)
+ ssize_t ret = gnutls_record_send(tls_ctx->gnutls_session,
data,
data_size);
if (ret >= 0) {
@@ -325,27 +597,32 @@ rexmpp_tls_send (rexmpp_t *s, void *data, size_t data_size, ssize_t *written)
return REXMPP_TLS_E_OTHER;
}
#elif defined(USE_OPENSSL)
- *written = -1;
- int ret = SSL_write_ex(s->tls.openssl_conn, data, data_size, written);
+ int ret = SSL_write_ex(tls_ctx->openssl_conn, data, data_size,
+ (size_t*)written);
if (ret > 0) {
return REXMPP_TLS_SUCCESS;
} else {
- return rexmpp_process_openssl_ret(s, ret);
+ return rexmpp_process_openssl_ret(s, tls_ctx, "rexmpp_tls_send", ret);
}
#else
(void)data;
(void)data_size;
- (void)written;
+ (void)tls_ctx;
rexmpp_log(s, LOG_ERR, "rexmpp is compiled without TLS support");
return REXMPP_TLS_E_OTHER;
#endif
}
rexmpp_tls_err_t
-rexmpp_tls_recv (rexmpp_t *s, void *data, size_t data_size, ssize_t *received) {
+rexmpp_tls_recv (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ void *data,
+ size_t data_size,
+ ssize_t *received)
+{
#if defined(USE_GNUTLS)
*received = -1;
- ssize_t ret = gnutls_record_recv(s->tls.gnutls_session, data, data_size);
+ ssize_t ret = gnutls_record_recv(tls_ctx->gnutls_session, data, data_size);
if (ret >= 0) {
*received = ret;
return REXMPP_TLS_SUCCESS;
@@ -357,35 +634,47 @@ 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(tls_ctx->openssl_conn, data, data_size,
+ (size_t*)received);
if (ret > 0) {
return REXMPP_TLS_SUCCESS;
} else {
- return rexmpp_process_openssl_ret(s, ret);
+ return rexmpp_process_openssl_ret(s, tls_ctx, "rexmpp_tls_recv", ret);
}
#else
(void)data;
(void)data_size;
(void)received;
+ (void)tls_ctx;
rexmpp_log(s, LOG_ERR, "rexmpp is compiled without TLS support");
return REXMPP_TLS_E_OTHER;
#endif
}
+unsigned int rexmpp_dtls_timeout (rexmpp_t *s, rexmpp_tls_t *tls_ctx) {
+ (void)s;
+#if defined(USE_GNUTLS)
+ return gnutls_dtls_get_timeout(tls_ctx->gnutls_session);
+#else
+ (void)tls_ctx;
+ return -1;
+#endif
+}
+
int rexmpp_tls_fds (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
#if defined(USE_GNUTLS)
- if (gnutls_record_get_direction(s->tls.gnutls_session) == 0) {
+ if (gnutls_record_get_direction(s->tls->gnutls_session) == 0) {
FD_SET(s->server_socket, read_fds);
} else {
FD_SET(s->server_socket, write_fds);
}
return s->server_socket + 1;
#elif defined(USE_OPENSSL)
- if (s->tls.openssl_direction == REXMPP_OPENSSL_READ) {
+ if (s->tls->openssl_direction == REXMPP_OPENSSL_READ) {
FD_SET(s->server_socket, read_fds);
return s->server_socket + 1;
}
- if (s->tls.openssl_direction == REXMPP_OPENSSL_WRITE) {
+ if (s->tls->openssl_direction == REXMPP_OPENSSL_WRITE) {
FD_SET(s->server_socket, write_fds);
return s->server_socket + 1;
}
@@ -400,20 +689,25 @@ int rexmpp_tls_fds (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
rexmpp_tls_err_t
rexmpp_tls_set_x509_key_file (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
const char *cert_file,
const char *key_file)
{
+ if (cert_file == NULL) {
+ cert_file = s->x509_cert_file;
+ }
+ if (key_file == NULL) {
+ key_file = s->x509_key_file;
+ }
+ if (cert_file == NULL || key_file == NULL) {
+ rexmpp_log(s, LOG_ERR, "No certificate or key file defined");
+ return REXMPP_TLS_E_OTHER;
+ }
#if defined(USE_GNUTLS)
- int ret = gnutls_certificate_set_x509_key_file(s->tls.gnutls_cred,
+ int ret = gnutls_certificate_set_x509_key_file(tls_ctx->gnutls_cred,
cert_file,
key_file,
- GNUTLS_X509_FMT_PEM);
-#ifdef ENABLE_CALLS
- gnutls_certificate_set_x509_key_file(s->jingle.dtls_cred,
- cert_file,
- key_file,
- GNUTLS_X509_FMT_PEM);
-#endif
+ GNUTLS_X509_FMT_DER);
if (ret == 0) {
return REXMPP_TLS_SUCCESS;
} else {
@@ -422,15 +716,15 @@ rexmpp_tls_set_x509_key_file (rexmpp_t *s,
return REXMPP_TLS_E_OTHER;
}
#elif defined(USE_OPENSSL)
- if (SSL_CTX_use_certificate_file(s->tls.openssl_ctx,
+ if (SSL_CTX_use_certificate_file(tls_ctx->openssl_ctx,
cert_file,
- SSL_FILETYPE_PEM) != 1) {
+ SSL_FILETYPE_ASN1) != 1) {
rexmpp_log(s, LOG_ERR, "Failed to set a certificate file");
return REXMPP_TLS_E_OTHER;
}
- if (SSL_CTX_use_PrivateKey_file(s->tls.openssl_ctx,
+ if (SSL_CTX_use_PrivateKey_file(tls_ctx->openssl_ctx,
key_file,
- SSL_FILETYPE_PEM) != 1) {
+ SSL_FILETYPE_ASN1) != 1) {
rexmpp_log(s, LOG_ERR, "Failed to set a key file");
return REXMPP_TLS_E_OTHER;
}
@@ -438,6 +732,7 @@ rexmpp_tls_set_x509_key_file (rexmpp_t *s,
#else
(void)cert_file;
(void)key_file;
+ (void)tls_ctx;
rexmpp_log(s, LOG_ERR, "rexmpp is compiled without TLS support");
return REXMPP_TLS_E_OTHER;
#endif
@@ -445,22 +740,248 @@ rexmpp_tls_set_x509_key_file (rexmpp_t *s,
rexmpp_tls_err_t
rexmpp_tls_set_x509_trust_file (rexmpp_t *s,
- const char *cert_file)
+ rexmpp_tls_t *tls_ctx,
+ const char *trust_file)
{
+ if (trust_file == NULL) {
+ trust_file = s->x509_trust_file;
+ }
+ if (trust_file == NULL) {
+ rexmpp_log(s, LOG_ERR, "No trust file is defined");
+ return REXMPP_TLS_E_OTHER;
+ }
#if defined(USE_GNUTLS)
- gnutls_certificate_set_x509_trust_file(s->tls.gnutls_cred,
- cert_file,
- GNUTLS_X509_FMT_PEM);
+ gnutls_certificate_set_x509_trust_file(tls_ctx->gnutls_cred,
+ trust_file,
+ GNUTLS_X509_FMT_DER);
return REXMPP_TLS_SUCCESS;
#elif defined(USE_OPENSSL)
- if (SSL_CTX_load_verify_locations(s->tls.openssl_ctx, cert_file, NULL) != 1) {
+ if (SSL_CTX_load_verify_locations(tls_ctx->openssl_ctx, trust_file, NULL) != 1) {
rexmpp_log(s, LOG_ERR, "Failed to set a trusted certificate file");
return REXMPP_TLS_E_OTHER;
}
return REXMPP_TLS_SUCCESS;
#else
- (void)cert_file;
+ (void)trust_file;
+ (void)tls_ctx;
rexmpp_log(s, LOG_ERR, "rexmpp is compiled without TLS support");
return REXMPP_TLS_E_OTHER;
#endif
}
+
+
+int rexmpp_tls_peer_fp (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ const char *algo_str,
+ char *raw_fp,
+ char *fp_str,
+ size_t *fp_size)
+{
+#if defined(USE_GNUTLS)
+ unsigned int cert_list_size = 0;
+ const gnutls_datum_t *cert_list;
+ cert_list =
+ gnutls_certificate_get_peers(tls_ctx->gnutls_session, &cert_list_size);
+ if (cert_list_size != 1) {
+ rexmpp_log(s, LOG_ERR,
+ "Unexpected peer certificate list size: %d",
+ cert_list_size);
+ return -1;
+ }
+ return rexmpp_x509_raw_cert_fp(s, algo_str, cert_list,
+ raw_fp, fp_str, fp_size);
+#elif defined(USE_OPENSSL)
+ if (strcmp(algo_str, "sha-256") != 0) {
+ rexmpp_log(s, LOG_ERR,
+ "Unsupported hash function algorithm: %s", algo_str);
+ return -1;
+ }
+ X509 *peer_cert = SSL_get0_peer_certificate(tls_ctx->openssl_conn);
+ if (peer_cert == NULL) {
+ rexmpp_log(s, LOG_ERR, "No peer certificate found");
+ return -1;
+ }
+ unsigned int len;
+ X509_digest(peer_cert, EVP_sha256(), (unsigned char*)raw_fp, &len);
+ *fp_size = len;
+ size_t i;
+ for (i = 0; i < *fp_size; i++) {
+ snprintf(fp_str + i * 3, 4, "%02X:", raw_fp[i] & 0xFF);
+ }
+ fp_str[*fp_size * 3 - 1] = 0;
+ return 0;
+#else
+ (void)tls_ctx;
+ (void)algo_str;
+ (void)raw_fp;
+ (void)fp_str;
+ (void)fp_size;
+ rexmpp_log(s, LOG_ERR, "rexmpp is compiled without TLS support");
+ return -1;
+#endif
+}
+
+/* TODO: handle different algorithms, and maybe apply this to
+ arbitrary files. */
+int rexmpp_tls_my_fp (rexmpp_t *s,
+ char *raw_fp,
+ char *fp_str,
+ size_t *fp_size)
+{
+ rexmpp_digest_t digest_ctx;
+ if (rexmpp_digest_init(&digest_ctx, REXMPP_DIGEST_SHA256)) {
+ rexmpp_log(s, LOG_ERR, "Failed to initialize a digest object");
+ return -1;
+ }
+
+ if (s->x509_cert_file == NULL) {
+ rexmpp_log(s, LOG_WARNING, "No X.509 certificate file defined");
+ return -1;
+ }
+ FILE *fh = fopen(s->x509_cert_file, "r");
+ if (fh == NULL) {
+ rexmpp_log(s, LOG_ERR, "Failed to open the X.509 certificate file");
+ return -1;
+ }
+ unsigned char *buf[4096];
+ size_t len = fread(buf, 1, 4096, fh);
+ while (len > 0) {
+ rexmpp_digest_update(&digest_ctx, buf, len);
+ len = fread(buf, 1, 4096, fh);
+ }
+ fclose(fh);
+
+ *fp_size = rexmpp_digest_len(REXMPP_DIGEST_SHA256);
+ rexmpp_digest_finish(&digest_ctx, raw_fp, *fp_size);
+
+ size_t i;
+ for (i = 0; i < (*fp_size); i++) {
+ snprintf(fp_str + i * 3, 4, "%02X:", raw_fp[i] & 0xFF);
+ }
+ fp_str[(*fp_size) * 3 - 1] = 0;
+ return 0;
+}
+
+int rexmpp_tls_session_fp (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ const char *algo_str,
+ char *raw_fp,
+ char *fp_str,
+ size_t *fp_size)
+{
+#if defined(USE_GNUTLS)
+ gnutls_x509_crt_t *cert_list;
+ unsigned int cert_list_size = 0;
+ int err =
+ gnutls_certificate_get_x509_crt(tls_ctx->gnutls_cred,
+ 0, &cert_list, &cert_list_size);
+ if (err) {
+ rexmpp_log(s, LOG_ERR,
+ "Failed to read own certificate list: %s",
+ gnutls_strerror(err));
+ return -1;
+ }
+
+ err = rexmpp_x509_cert_fp(s, algo_str, cert_list[0], raw_fp, fp_str, fp_size);
+
+ size_t i;
+ for (i = 0; i < cert_list_size; i++) {
+ gnutls_x509_crt_deinit(cert_list[i]);
+ }
+ gnutls_free(cert_list);
+ return err;
+#else
+ (void)s;
+ (void)tls_ctx;
+ (void)algo_str;
+ (void)raw_fp;
+ (void)fp_str;
+ (void)fp_size;
+ return -1;
+#endif
+}
+
+int rexmpp_x509_cert_fp (rexmpp_t *s,
+ const char *algo_str,
+ void *cert,
+ char *raw_fp,
+ char *fp_str,
+ size_t *fp_size)
+{
+#if defined(USE_GNUTLS)
+ gnutls_datum_t raw_cert;
+ int err = gnutls_x509_crt_export2(cert, GNUTLS_X509_FMT_DER, &raw_cert);
+ if (err != GNUTLS_E_SUCCESS) {
+ rexmpp_log(s, LOG_ERR, "Failed to export a certificate: %s",
+ gnutls_strerror(err));
+ return err;
+ }
+ err = rexmpp_x509_raw_cert_fp(s, algo_str, &raw_cert, raw_fp, fp_str, fp_size);
+ gnutls_free(raw_cert.data);
+ return err;
+#else
+ (void)s;
+ (void)algo_str;
+ (void)cert;
+ (void)raw_fp;
+ (void)fp_str;
+ (void)fp_size;
+ return -1;
+#endif
+}
+
+int rexmpp_x509_raw_cert_fp (rexmpp_t *s,
+ const char *algo_str,
+ const void *raw_cert,
+ char *raw_fp,
+ char *fp_str,
+ size_t *fp_size)
+{
+#if defined(USE_GNUTLS)
+ const gnutls_datum_t *cert = (const gnutls_datum_t*)raw_cert;
+ gnutls_digest_algorithm_t algo = GNUTLS_DIG_UNKNOWN;
+ /* gnutls_digest_get_id uses different names, so
+ checking manually here. These are SDP options,
+ <https://datatracker.ietf.org/doc/html/rfc4572#page-8>. */
+ if (strcmp(algo_str, "sha-1") == 0) {
+ algo = GNUTLS_DIG_SHA1;
+ } else if (strcmp(algo_str, "sha-224") == 0) {
+ algo = GNUTLS_DIG_SHA224;
+ } else if (strcmp(algo_str, "sha-256") == 0) {
+ algo = GNUTLS_DIG_SHA256;
+ } else if (strcmp(algo_str, "sha-384") == 0) {
+ algo = GNUTLS_DIG_SHA384;
+ } else if (strcmp(algo_str, "sha-512") == 0) {
+ algo = GNUTLS_DIG_SHA512;
+ } else if (strcmp(algo_str, "md5") == 0) {
+ algo = GNUTLS_DIG_MD5;
+ }
+ if (algo == GNUTLS_DIG_UNKNOWN) {
+ rexmpp_log(s, LOG_ERR, "Unknown hash algorithm: %s", algo_str);
+ return -1;
+ }
+
+ int err = gnutls_fingerprint(algo, cert, raw_fp, fp_size);
+ if (err != GNUTLS_E_SUCCESS) {
+ rexmpp_log(s, LOG_ERR, "Failed to calculate a fingerprint: %s",
+ gnutls_strerror(err));
+ return -1;
+ }
+ if (fp_str != NULL) {
+ size_t i;
+ for (i = 0; i < (*fp_size); i++) {
+ snprintf(fp_str + i * 3, 4, "%02X:", raw_fp[i] & 0xFF);
+ }
+ fp_str[(*fp_size) * 3 - 1] = 0;
+ }
+ return 0;
+#else
+ (void)s;
+ (void)algo_str;
+ (void)raw_cert;
+ (void)raw_fp;
+ (void)fp_str;
+ (void)fp_size;
+ return -1;
+#endif
+}
diff --git a/src/rexmpp_tls.h b/src/rexmpp_tls.h
index 24ba042..4a966ca 100644
--- a/src/rexmpp_tls.h
+++ b/src/rexmpp_tls.h
@@ -20,6 +20,8 @@ to write logs and read other values (including server socket).
#include "rexmpp.h"
#include "config.h"
+#define DTLS_SRTP_BUF_SIZE 0x4000
+
typedef struct rexmpp_tls rexmpp_tls_t;
/**
@@ -43,6 +45,8 @@ struct rexmpp_tls {
size_t tls_session_data_size;
gnutls_session_t gnutls_session;
gnutls_certificate_credentials_t gnutls_cred;
+ char dtls_buf[DTLS_SRTP_BUF_SIZE];
+ size_t dtls_buf_len;
};
#elif defined(USE_OPENSSL)
#include <openssl/ssl.h>
@@ -54,6 +58,8 @@ enum rexmpp_openssl_direction {
struct rexmpp_tls {
SSL_CTX *openssl_ctx;
SSL *openssl_conn;
+ BIO *bio_conn;
+ BIO *bio_io;
enum rexmpp_openssl_direction openssl_direction;
};
#else
@@ -66,26 +72,90 @@ int rexmpp_tls_init(rexmpp_t *s);
void rexmpp_tls_cleanup(rexmpp_t *s);
void rexmpp_tls_deinit(rexmpp_t *s);
-rexmpp_tls_err_t rexmpp_tls_connect(rexmpp_t *s);
-rexmpp_tls_err_t rexmpp_tls_disconnect(rexmpp_t *s);
+rexmpp_tls_t *rexmpp_tls_ctx_new (rexmpp_t *s, int dtls);
+void rexmpp_tls_ctx_free (rexmpp_tls_t *tls_ctx);
+
+void rexmpp_tls_session_free (rexmpp_tls_t *tls_ctx);
+
+rexmpp_tls_err_t rexmpp_tls_connect (rexmpp_t *s);
+rexmpp_tls_err_t rexmpp_tls_handshake (rexmpp_t *s, rexmpp_tls_t *tls_ctx);
+rexmpp_tls_err_t rexmpp_tls_disconnect (rexmpp_t *s, rexmpp_tls_t *tls_ctx);
+rexmpp_tls_err_t
+rexmpp_dtls_connect (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ void *user_data,
+ int client);
+void rexmpp_dtls_feed(rexmpp_t *s, rexmpp_tls_t *tls_ctx, uint8_t *buf, size_t len);
+
+int
+rexmpp_tls_srtp_get_keys (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ size_t key_len,
+ size_t salt_len,
+ unsigned char *key_mat);
-rexmpp_tls_err_t rexmpp_tls_send(rexmpp_t *s, void *data, size_t data_size, ssize_t *written);
-rexmpp_tls_err_t rexmpp_tls_recv(rexmpp_t *s, void *data, size_t data_size, ssize_t *received);
+rexmpp_tls_err_t
+rexmpp_tls_send (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ void *data,
+ size_t data_size,
+ ssize_t *written);
+rexmpp_tls_err_t
+rexmpp_tls_recv (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ void *data,
+ size_t data_size,
+ ssize_t *received);
+unsigned int rexmpp_dtls_timeout (rexmpp_t *s, rexmpp_tls_t *tls_ctx);
int rexmpp_tls_fds(rexmpp_t *s, fd_set *read_fds, fd_set *write_fds);
/**
- @brief Sets credentials for both client authentication to the
- server (SASL EXTERNAL) and DTLS connections in Jingle sessions.
+ @brief Sets credentials for a given TLS context: either provided
+ ones or defined for the whole ::rexmpp structure.
*/
rexmpp_tls_err_t
rexmpp_tls_set_x509_key_file (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
const char *cert_file,
const char *key_file);
rexmpp_tls_err_t
rexmpp_tls_set_x509_trust_file (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
const char *cert_file);
+int rexmpp_tls_peer_fp (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ const char *algo_str,
+ char *raw_fp,
+ char *fp_str,
+ size_t *fp_size);
+
+int rexmpp_tls_my_fp (rexmpp_t *s,
+ char *raw_fp,
+ char *fp_str,
+ size_t *fp_size);
+
+int rexmpp_tls_session_fp (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ const char *algo_str,
+ char *raw_fp,
+ char *fp_str,
+ size_t *fp_size);
+
+int rexmpp_x509_cert_fp (rexmpp_t *s,
+ const char *algo_str,
+ void *cert,
+ char *raw_fp,
+ char *fp_str,
+ size_t *fp_size);
+
+int rexmpp_x509_raw_cert_fp (rexmpp_t *s,
+ const char *algo_str,
+ const void *raw_cert,
+ char *raw_fp,
+ char *fp_str,
+ size_t *fp_size);
#endif
diff --git a/src/rexmpp_utf8.h b/src/rexmpp_utf8.h
new file mode 100644
index 0000000..4096aac
--- /dev/null
+++ b/src/rexmpp_utf8.h
@@ -0,0 +1,93 @@
+/**
+ @file rexmpp_utf8.h
+ @brief UTF-8 helper functions
+ @author defanor <defanor@uberspace.net>
+ @date 2023
+ @copyright MIT license.
+*/
+
+#ifndef REXMPP_UTF8_H
+#define REXMPP_UTF8_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef HAVE_ICU
+
+#include <unicode/utf8.h>
+#define REXMPP_U8_NEXT U8_NEXT
+
+#else
+
+#define REXMPP_U8_NEXT(str, pos, len, out) \
+ rexmpp_utf8_next(str, &pos, len, &out);
+
+/**
+ @brief Similar to libicu's U8_NEXT macros: reads a single UTF-8
+ code point, advances the position.
+ @param[in] str A string to read.
+ @param[in,out] pos Byte position within the string. Advanced by the
+ number of bytes read to produce a code point, not advanced on
+ failure.
+ @param[in] len String length.
+ @param[in,out] out A pointer to the location for writing the code
+ point.
+ @returns 0 on failure, 1 on success.
+*/
+inline static
+void rexmpp_utf8_next (const uint8_t *str,
+ size_t *pos,
+ size_t len,
+ int32_t *out)
+{
+ if (*pos >= len) {
+ *out = -1;
+ return;
+ }
+
+ if ((str[*pos] & 0x80) == 0
+ && *pos + 1 <= len)
+ /* U+0000 to U+007F: 0xxxxxxx */
+ {
+ *out = str[*pos];
+ *pos = *pos + 1;
+ } else if ((str[*pos] & 0xe0) == 0xc0
+ && *pos + 2 <= len
+ && (str[*pos + 1] & 0xc0) == 0x80)
+ /* U+0080 to U+07FF: 110xxxxx 10xxxxxx */
+ {
+ *out = (((int32_t)(str[*pos] & 0x1f) << 6)
+ | ((int32_t)str[*pos + 1] & 0x3f));
+ *pos = *pos + 2;
+ } else if ((str[*pos] & 0xf0) == 0xe0
+ && *pos + 3 <= len
+ && (str[*pos + 1] & 0xc0) == 0x80
+ && (str[*pos + 2] & 0xc0) == 0x80)
+ /* U+0800 to U+FFFF: 1110xxxx 10xxxxxx 10xxxxxx */
+ {
+ *out = (((((int32_t)(str[*pos] & 0xf) << 6)
+ | ((int32_t)str[*pos + 1] & 0x3f)) << 6)
+ | ((int32_t)str[*pos + 2] & 0x3f));
+ *pos = *pos + 3;
+ } else if ((str[*pos] & 0xf8) == 0xf0
+ && *pos + 4 <= len
+ && (str[*pos + 1] & 0xc0) == 0x80
+ && (str[*pos + 2] & 0xc0) == 0x80
+ && (str[*pos + 3] & 0xc0) == 0x80)
+ /* U+10000 to U+10FFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
+ {
+ *out = (((((((int32_t)(str[*pos] & 7) << 6)
+ | ((int32_t)str[*pos + 1] & 0x3f)) << 6)
+ | (((int32_t)str[*pos + 2] & 0x3f))) << 6)
+ | ((int32_t)str[*pos + 3] & 0x3f));
+ *pos = *pos + 4;
+ } else
+ /* Invalid UTF-8 */
+ {
+ *out = -1;
+ }
+}
+
+#endif /* HAVE_ICU */
+
+#endif /* REXMPP_UTF8_H */
diff --git a/src/rexmpp_xml.c b/src/rexmpp_xml.c
new file mode 100644
index 0000000..8b106ab
--- /dev/null
+++ b/src/rexmpp_xml.c
@@ -0,0 +1,805 @@
+/**
+ @file rexmpp_xml.c
+ @brief XML structures and functions for rexmpp
+ @author defanor <defanor@uberspace.net>
+ @date 2023
+ @copyright MIT license.
+*/
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include "rexmpp.h"
+#include "rexmpp_utf8.h"
+#include "rexmpp_xml.h"
+#include "rexmpp_random.h"
+
+#ifndef USE_RUST
+void rexmpp_xml_qname_free (rexmpp_xml_qname_t *qname) {
+ if (qname->name != NULL) {
+ free(qname->name);
+ qname->name = NULL;
+ }
+ if (qname->namespace != NULL) {
+ free(qname->namespace);
+ qname->namespace = NULL;
+ }
+}
+
+void rexmpp_xml_attribute_free (rexmpp_xml_attr_t *attr) {
+ if (attr == NULL) {
+ return;
+ }
+ rexmpp_xml_qname_free(&(attr->qname));
+ if (attr->value != NULL) {
+ free(attr->value);
+ attr->value = NULL;
+ }
+ free(attr);
+}
+
+void rexmpp_xml_attribute_free_list (rexmpp_xml_attr_t *attr) {
+ rexmpp_xml_attr_t *next = attr;
+ while (attr != NULL) {
+ next = attr->next;
+ rexmpp_xml_attribute_free(attr);
+ attr = next;
+ }
+}
+
+void rexmpp_xml_free (rexmpp_xml_t *node) {
+ if (node == NULL) {
+ return;
+ }
+ if (node->type == REXMPP_XML_TEXT) {
+ if (node->alt.text != NULL) {
+ free(node->alt.text);
+ node->alt.text = NULL;
+ }
+ } if (node->type == REXMPP_XML_ELEMENT) {
+ rexmpp_xml_qname_free(&(node->alt.elem.qname));
+ rexmpp_xml_attribute_free_list(node->alt.elem.attributes);
+ rexmpp_xml_free_list(node->alt.elem.children);
+ }
+ free(node);
+}
+
+void rexmpp_xml_free_list (rexmpp_xml_t *node) {
+ rexmpp_xml_t *next = node;
+ while (node != NULL) {
+ next = node->next;
+ rexmpp_xml_free(node);
+ node = next;
+ }
+}
+
+rexmpp_xml_t *rexmpp_xml_clone (rexmpp_xml_t *node) {
+ if (node == NULL) {
+ return NULL;
+ }
+
+ if (node->type == REXMPP_XML_TEXT) {
+ return rexmpp_xml_new_text(node->alt.text);
+ } else if (node->type == REXMPP_XML_ELEMENT) {
+ rexmpp_xml_t *ret =
+ rexmpp_xml_new_elem(node->alt.elem.qname.name,
+ node->alt.elem.qname.namespace);
+ rexmpp_xml_attr_t **next_attr = &(ret->alt.elem.attributes);
+ rexmpp_xml_attr_t *old_attr;
+ for (old_attr = node->alt.elem.attributes;
+ old_attr != NULL;
+ old_attr = old_attr->next)
+ {
+ rexmpp_xml_attr_t *new_attr =
+ rexmpp_xml_attr_new(old_attr->qname.name,
+ old_attr->qname.namespace,
+ old_attr->value);
+ *next_attr = new_attr;
+ next_attr = &(new_attr->next);
+ }
+
+ ret->alt.elem.children =
+ rexmpp_xml_clone_list(node->alt.elem.children);
+ return ret;
+ }
+ return NULL;
+}
+
+rexmpp_xml_t *rexmpp_xml_clone_list (rexmpp_xml_t *node) {
+ rexmpp_xml_t *first, *last;
+ if (node == NULL) {
+ return NULL;
+ }
+ first = rexmpp_xml_clone(node);
+ for (last = first, node = node->next;
+ node != NULL;
+ last = last->next, node = node->next)
+ {
+ last->next = rexmpp_xml_clone(node);
+ }
+ return first;
+}
+
+rexmpp_xml_t *rexmpp_xml_new_text (const char *str) {
+ rexmpp_xml_t *node = malloc(sizeof(rexmpp_xml_t));
+ node->type = REXMPP_XML_TEXT;
+ node->alt.text = strdup(str);
+ node->next = NULL;
+ return node;
+}
+
+rexmpp_xml_t *rexmpp_xml_new_text_len (const char *str, size_t len) {
+ rexmpp_xml_t *node = malloc(sizeof(rexmpp_xml_t));
+ node->type = REXMPP_XML_TEXT;
+ node->alt.text = strndup(str, len);
+ node->next = NULL;
+ return node;
+}
+
+void rexmpp_xml_add_child (rexmpp_xml_t *node,
+ rexmpp_xml_t *child)
+{
+ rexmpp_xml_t **last_ptr = &(node->alt.elem.children);
+ while (*last_ptr != NULL) {
+ last_ptr = &((*last_ptr)->next);
+ }
+ *last_ptr = child;
+}
+
+int rexmpp_xml_add_text (rexmpp_xml_t *node,
+ const char *str)
+{
+ rexmpp_xml_t *text_node = rexmpp_xml_new_text(str);
+ if (text_node != NULL) {
+ rexmpp_xml_add_child(node, text_node);
+ return 0;
+ }
+ return -1;
+}
+
+int rexmpp_xml_add_text_len (rexmpp_xml_t *node,
+ const char *str,
+ size_t len)
+{
+ rexmpp_xml_t *text_node = rexmpp_xml_new_text_len(str, len);
+ if (text_node != NULL) {
+ rexmpp_xml_add_child(node, text_node);
+ return 0;
+ }
+ return -1;
+}
+
+rexmpp_xml_t *rexmpp_xml_new_elem (const char *name,
+ const char *namespace)
+{
+ rexmpp_xml_t *node = malloc(sizeof(rexmpp_xml_t));
+ node->type = REXMPP_XML_ELEMENT;
+ node->alt.elem.qname.name = strdup(name);
+ if (namespace != NULL) {
+ node->alt.elem.qname.namespace = strdup(namespace);
+ } else {
+ node->alt.elem.qname.namespace = NULL;
+ }
+ node->alt.elem.attributes = NULL;
+ node->alt.elem.children = NULL;
+ node->next = NULL;
+ return node;
+}
+
+rexmpp_xml_attr_t *rexmpp_xml_attr_new (const char *name,
+ const char *namespace,
+ const char *value)
+{
+ rexmpp_xml_attr_t *attr = malloc(sizeof(rexmpp_xml_attr_t));
+ attr->qname.name = strdup(name);
+ if (namespace != NULL) {
+ attr->qname.namespace = strdup(namespace);
+ } else {
+ attr->qname.namespace = NULL;
+ }
+ attr->value = strdup(value);
+ attr->next = NULL;
+ return attr;
+}
+
+int rexmpp_xml_add_attr_ns (rexmpp_xml_t *node,
+ const char *name,
+ const char *namespace,
+ const char *value)
+{
+ if (node == NULL || node->type != REXMPP_XML_ELEMENT) {
+ return -1;
+ }
+ rexmpp_xml_attr_t *attr =
+ rexmpp_xml_attr_new(name, namespace, value);
+ attr->next = node->alt.elem.attributes;
+ node->alt.elem.attributes = attr;
+ return 0;
+}
+
+int rexmpp_xml_remove_attr_ns (rexmpp_xml_t *node,
+ const char *name,
+ const char *namespace) {
+ if (node == NULL || node->type != REXMPP_XML_ELEMENT) {
+ return -1;
+ }
+
+ rexmpp_xml_attr_t **attr, *next_attr;
+ for (attr = &(node->alt.elem.attributes); *attr != NULL; attr = &((*attr)->next)) {
+ if (rexmpp_xml_attr_match(*attr, namespace, name)) {
+ next_attr = (*attr)->next;
+ rexmpp_xml_attribute_free(*attr);
+ *attr = next_attr;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int rexmpp_xml_add_attr (rexmpp_xml_t *node,
+ const char *name,
+ const char *value)
+{
+ return rexmpp_xml_add_attr_ns(node, name, NULL, value);
+}
+
+int rexmpp_xml_remove_attr (rexmpp_xml_t *node,
+ const char *name) {
+ return rexmpp_xml_remove_attr_ns(node, name, NULL);
+}
+
+/* Adds a character, grows the string as needed. */
+static inline char *rexmpp_str_putc (char *str, size_t *len, char c) {
+ char *ret = str;
+ if ((*len) % 1024 == 0) {
+ ret = realloc(str, (*len) + 1024);
+ if (ret == NULL) {
+ /* A failure to realloc. */
+ if (str != NULL) {
+ free(str);
+ }
+ return NULL;
+ }
+ }
+ ret[*len] = c;
+ *len = (*len) + 1;
+ return ret;
+}
+
+static inline
+char *rexmpp_str_putc_escaped (char *str, size_t *len, char c) {
+ char *ret = str;
+ char buf[7];
+ char *esc = buf;
+ size_t i = 0;
+ size_t esc_len;
+ if (c == '<') {
+ esc = "&lt;";
+ } else if (c == '>') {
+ esc = "&gt;";
+ } else if (c == '&') {
+ esc = "&amp;";
+ } else if (c == '\'') {
+ esc = "&apos;";
+ } else if (c == '"') {
+ esc = "&quot;";
+ } else {
+ snprintf(esc, 7, "&#%u;", c);
+ }
+ esc_len = strlen(esc);
+ while (i < esc_len) {
+ ret = rexmpp_str_putc(ret, len, esc[i]);
+ i++;
+ }
+ return ret;
+}
+
+char *rexmpp_xml_print_name (char *str, size_t *len, const char *name) {
+ char *ret = str;
+ size_t name_len = strlen(name);
+ size_t i = 0;
+ int32_t c = 0; /* matches ICU's UChar32 */
+ size_t prev_i = 0, j;
+ do {
+ REXMPP_U8_NEXT(name, i, name_len, c);
+ if (c >= 0) {
+ if (c == ':'
+ || (c >= 'A' && c <= 'Z')
+ || c == '_'
+ || (c >= 'a' && c <= 'z')
+ || (c >= 0xC0 && c <= 0xD6)
+ || (c >= 0xD8 && c <= 0xF6)
+ || (c >= 0xF8 && c <= 0x2FF)
+ || (c >= 0x370 && c <= 0x37D)
+ || (c >= 0x37F && c <= 0x1FFF)
+ || (c >= 0x200C && c <= 0x200D)
+ || (c >= 0x2070 && c <= 0x218F)
+ || (c >= 0x2C00 && c <= 0x2FEF)
+ || (c >= 0x3001 && c <= 0xD7FF)
+ || (c >= 0xF900 && c <= 0xFDCF)
+ || (c >= 0xFDF0 && c <= 0xFFF0)
+ || (c >= 0x10000 && c <= 0xEFFFF)
+ || ((i > 0) &&
+ (c == '-'
+ || c == '.'
+ || (c >= '0' && c <= '9')
+ || c == 0xB7
+ || (c >= 0x0300 && c <= 0x036F)
+ || (c >= 0x203F && c <= 0x2040))))
+ {
+ /* Print the allowed characters. */
+ for (j = prev_i; j < i; j++) {
+ ret = rexmpp_str_putc(ret, len, name[j]);
+ }
+ }
+ } else {
+ /* Skip invalid characters. */
+ i++;
+ }
+ prev_i = i;
+ } while (i < name_len);
+ return ret;
+}
+
+char *rexmpp_xml_print_text (char *str, size_t *len, const char *text) {
+ char *ret = str;
+ size_t i = 0;
+ size_t text_len = strlen(text);
+ while (i < text_len && ret != NULL) {
+ char c = text[i];
+ if (strchr("<&>'\"", c)) {
+ /* Escape the few special characters. */
+ ret = rexmpp_str_putc_escaped(ret, len, c);
+ } else {
+ /* Write others as is. */
+ ret = rexmpp_str_putc(ret, len, c);
+ }
+ i++;
+ }
+ return ret;
+}
+
+char *rexmpp_xml_print_raw (char *str, size_t *len, const char *text) {
+ char *ret = str;
+ size_t i = 0;
+ size_t text_len = strlen(text);
+ while (i < text_len && ret != NULL) {
+ char c = text[i];
+ ret = rexmpp_str_putc(ret, len, c);
+ i++;
+ }
+ return ret;
+}
+
+static inline char *rexmpp_xml_print_indent (char *str,
+ size_t *len,
+ int indent) {
+ if (indent <= 0) {
+ return str;
+ }
+ int i;
+ char *ret = str;
+ for (i = 0; i < indent * 2; i++) {
+ ret = rexmpp_str_putc(ret, len, ' ');
+ }
+ return ret;
+}
+
+char *rexmpp_xml_print (char *str,
+ size_t *len,
+ const rexmpp_xml_t *node,
+ int indent) {
+ char *ret = str;
+ if (node->type == REXMPP_XML_TEXT) {
+ ret = rexmpp_xml_print_text(ret, len, node->alt.text);
+ } else if (node->type == REXMPP_XML_ELEMENT) {
+ if (indent > 0) {
+ ret = rexmpp_str_putc(ret, len, '\n');
+ ret = rexmpp_xml_print_indent(ret, len, indent);
+ }
+ ret = rexmpp_str_putc(ret, len, '<');
+ ret = rexmpp_xml_print_name(ret, len, node->alt.elem.qname.name);
+ if (node->alt.elem.qname.namespace != NULL) {
+ ret = rexmpp_xml_print_raw(ret, len, " xmlns=\"");
+ ret = rexmpp_xml_print_text(ret, len, node->alt.elem.qname.namespace);
+ ret = rexmpp_str_putc(ret, len, '"');
+ }
+ if (node->alt.elem.attributes != NULL) {
+ rexmpp_xml_attr_t *attr;
+ for (attr = node->alt.elem.attributes; attr != NULL; attr = attr->next) {
+ ret = rexmpp_str_putc(ret, len, ' ');
+ /* Ignoring namespaces here for now. */
+ ret = rexmpp_xml_print_name(ret, len, attr->qname.name);
+ ret = rexmpp_xml_print_raw(ret, len, "=\"");
+ ret = rexmpp_xml_print_text(ret, len, attr->value);
+ ret = rexmpp_str_putc(ret, len, '"');
+ }
+ }
+ if (node->alt.elem.children == NULL) {
+ ret = rexmpp_xml_print_raw(ret, len, "/>");
+ } else {
+ ret = rexmpp_str_putc(ret, len, '>');
+ rexmpp_xml_t *child;
+ int last_child_is_textual = 0;
+ for (child = rexmpp_xml_children(node);
+ child != NULL;
+ child = child->next)
+ {
+ ret = rexmpp_xml_print(ret, len, child,
+ indent > -1 ? indent + 1 : -1);
+ last_child_is_textual = child->type == REXMPP_XML_TEXT;
+ }
+ if (indent >= 0 && ! last_child_is_textual) {
+ ret = rexmpp_str_putc(ret, len, '\n');
+ ret = rexmpp_xml_print_indent(ret, len, indent);
+ }
+ ret = rexmpp_xml_print_raw(ret, len, "</");
+ ret = rexmpp_xml_print_name(ret, len, node->alt.elem.qname.name);
+ ret = rexmpp_str_putc(ret, len, '>');
+ }
+ }
+ return ret;
+}
+
+char *rexmpp_xml_serialize (const rexmpp_xml_t *node, int pretty) {
+ size_t s_len = 0;
+ char *s = NULL;
+ s = rexmpp_xml_print(s, &s_len, node, pretty ? 0 : -1);
+ s = rexmpp_str_putc(s, &s_len, '\0');
+ return s;
+}
+
+rexmpp_xml_t *
+rexmpp_xml_add_id (rexmpp_xml_t *node)
+{
+ char *buf = rexmpp_random_id();
+ if (buf == NULL) {
+ return NULL;
+ }
+ rexmpp_xml_add_attr(node, "id", buf);
+ free(buf);
+ return node;
+}
+#endif
+
+/* 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);
+ char *new_alt_text = realloc(last_node->alt.text, last_len + len + 1);
+ if (new_alt_text == NULL) {
+ /* TODO: Would be nice a report an error here. */
+ return;
+ }
+ last_node->alt.text = new_alt_text;
+ 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);
+ }
+ 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) {
+ 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, 1);
+ 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_fd (int 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[4096];
+ ssize_t buf_len = 0;
+ do {
+ buf_len = read(fd, buf, 4096);
+ if (buf_len > 0) {
+ rexmpp_xml_parser_feed(parser, buf, buf_len, 0);
+ }
+ } while (buf_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) {
+ int fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ return NULL;
+ }
+ rexmpp_xml_t *node = rexmpp_xml_read_fd(fd);
+ close(fd);
+ return node;
+}
+
+#ifndef USE_RUST
+int rexmpp_xml_write_file (const char *path, rexmpp_xml_t* node) {
+ FILE *fd = fopen(path, "w");
+ if (fd == NULL) {
+ return -1;
+ }
+ char *serialized = rexmpp_xml_serialize(node, 1);
+ fputs("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n", fd);
+ fputs(serialized, fd);
+ fclose(fd);
+ return 0;
+}
+
+unsigned int rexmpp_xml_siblings_count (rexmpp_xml_t *node) {
+ unsigned int i = 0;
+ for (i = 0; node != NULL; i++) {
+ node = node->next;
+ }
+ return i;
+}
+
+int rexmpp_xml_match (rexmpp_xml_t *node,
+ const char *namespace,
+ const char *name)
+{
+ if (node == NULL) {
+ return 0;
+ }
+ if (node->type != REXMPP_XML_ELEMENT) {
+ return 0;
+ }
+ if (name != NULL) {
+ if (strcmp(name, node->alt.elem.qname.name) != 0) {
+ return 0;
+ }
+ }
+ if (namespace != NULL) {
+ if (node->alt.elem.qname.namespace == NULL &&
+ strcmp(namespace, "jabber:client") != 0) {
+ return 0;
+ } else if (node->alt.elem.qname.namespace != NULL) {
+ if (strcmp(namespace, node->alt.elem.qname.namespace) != 0) {
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+int rexmpp_xml_attr_match (rexmpp_xml_attr_t *attr,
+ const char *namespace,
+ const char *name)
+{
+ if (attr == NULL) {
+ return 0;
+ }
+ if (name != NULL) {
+ if (strcmp(name, attr->qname.name) != 0) {
+ return 0;
+ }
+ }
+ if (namespace != NULL) {
+ if (attr->qname.namespace == NULL &&
+ strcmp(namespace, "jabber:client") != 0) {
+ return 0;
+ } else if (strcmp(namespace, attr->qname.namespace) != 0) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int rexmpp_xml_is_stanza (rexmpp_xml_t *node) {
+ return rexmpp_xml_match(node, "jabber:client", "message") ||
+ rexmpp_xml_match(node, "jabber:client", "iq") ||
+ rexmpp_xml_match(node, "jabber:client", "presence");
+}
+
+rexmpp_xml_t *rexmpp_xml_error (const char *type, const char *condition) {
+ rexmpp_xml_t * error = rexmpp_xml_new_elem("error", NULL);
+ rexmpp_xml_add_attr(error, "type", type);
+ rexmpp_xml_t * cond =
+ rexmpp_xml_new_elem(condition, "urn:ietf:params:xml:ns:xmpp-stanzas");
+ rexmpp_xml_add_child(error, cond);
+ return error;
+}
+
+rexmpp_xml_attr_t *
+rexmpp_xml_find_attr (rexmpp_xml_t *node,
+ const char *name,
+ const char *namespace)
+{
+ if (node == NULL || node->type != REXMPP_XML_ELEMENT) {
+ return NULL;
+ }
+ rexmpp_xml_attr_t *attr;
+ for (attr = node->alt.elem.attributes; attr != NULL; attr = attr->next) {
+ if (rexmpp_xml_attr_match(attr, namespace, name)) {
+ return attr;
+ }
+ }
+ return NULL;
+}
+
+const char *rexmpp_xml_find_attr_val_ns (rexmpp_xml_t *node,
+ const char *name,
+ const char *namespace) {
+ rexmpp_xml_attr_t *attr = rexmpp_xml_find_attr(node, name, namespace);
+ if (attr != NULL) {
+ return attr->value;
+ }
+ return NULL;
+}
+
+const char *rexmpp_xml_find_attr_val (rexmpp_xml_t *node,
+ const char *name) {
+ return rexmpp_xml_find_attr_val_ns(node, name, NULL);
+}
+
+rexmpp_xml_t *
+rexmpp_xml_find_child (rexmpp_xml_t *node,
+ const char *namespace,
+ const char *name)
+{
+ if (node == NULL || node->type != REXMPP_XML_ELEMENT) {
+ return NULL;
+ }
+ rexmpp_xml_t *child;
+ for (child = node->alt.elem.children; child != NULL; child = child->next) {
+ if (rexmpp_xml_match(child, namespace, name)) {
+ return child;
+ }
+ }
+ return NULL;
+}
+
+int rexmpp_xml_eq (rexmpp_xml_t *n1, rexmpp_xml_t *n2) {
+ /* Just serialize and compare strings for now: awkward, but
+ simple. */
+ char *n1str = rexmpp_xml_serialize(n1, 0);
+ char *n2str = rexmpp_xml_serialize(n2, 0);
+ int eq = (strcmp(n1str, n2str) == 0);
+ free(n1str);
+ free(n2str);
+ return eq;
+}
+
+rexmpp_xml_t *rexmpp_xml_children (const rexmpp_xml_t *node) {
+ if (node != NULL && node->type == REXMPP_XML_ELEMENT) {
+ return node->alt.elem.children;
+ }
+ return NULL;
+}
+
+rexmpp_xml_t *rexmpp_xml_first_elem_child (rexmpp_xml_t *node) {
+ rexmpp_xml_t *child;
+ for (child = rexmpp_xml_children(node); child != NULL; child = child->next) {
+ if (child->type == REXMPP_XML_ELEMENT) {
+ return child;
+ }
+ }
+ return NULL;
+}
+
+rexmpp_xml_t *rexmpp_xml_next_elem_sibling (rexmpp_xml_t *node) {
+ if (node == NULL) {
+ return NULL;
+ }
+ rexmpp_xml_t *sibling;
+ for (sibling = node->next; sibling != NULL; sibling = sibling->next) {
+ if (sibling->type == REXMPP_XML_ELEMENT) {
+ return sibling;
+ }
+ }
+ return NULL;
+}
+
+char *rexmpp_xml_text (rexmpp_xml_t *node) {
+ if (node != NULL && node->type == REXMPP_XML_TEXT) {
+ return node->alt.text;
+ }
+ return NULL;
+}
+
+char *rexmpp_xml_text_child (rexmpp_xml_t *node) {
+ return rexmpp_xml_text(rexmpp_xml_children(node));
+}
+
+rexmpp_xml_t *rexmpp_xml_reverse_list (rexmpp_xml_t *node) {
+ rexmpp_xml_t *next, *prev = NULL;
+ while (node != NULL) {
+ next = node->next;
+ node->next = prev;
+ prev = node;
+ node = next;
+ }
+ return prev;
+}
+
+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->alt.elem.children; cur != NULL; cur = cur->next) {
+ if (cur->type == REXMPP_XML_ELEMENT && cur->alt.elem.children != NULL) {
+ rexmpp_xml_reverse_children(cur);
+ }
+ }
+}
+
+#endif
diff --git a/src/rexmpp_xml.h b/src/rexmpp_xml.h
new file mode 100644
index 0000000..38142ae
--- /dev/null
+++ b/src/rexmpp_xml.h
@@ -0,0 +1,269 @@
+/**
+ @file rexmpp_xml.h
+ @brief XML structures and functions for rexmpp
+ @author defanor <defanor@uberspace.net>
+ @date 2023
+ @copyright MIT license.
+*/
+
+#ifndef REXMPP_XML_H
+#define REXMPP_XML_H
+
+#include <stdio.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;
+
+struct rexmpp_xml_qname {
+ char *name;
+ char *namespace;
+};
+
+struct rexmpp_xml_attribute {
+ rexmpp_xml_qname_t qname;
+ char *value;
+ rexmpp_xml_attr_t *next;
+};
+
+enum rexmpp_xml_node_type {
+ REXMPP_XML_ELEMENT,
+ REXMPP_XML_TEXT
+};
+
+typedef enum rexmpp_xml_node_type rexmpp_xml_node_type_t;
+
+struct rexmpp_xml_node {
+ rexmpp_xml_node_type_t type;
+ union {
+ struct {
+ rexmpp_xml_qname_t qname;
+ rexmpp_xml_attr_t *attributes;
+ rexmpp_xml_t *children;
+ } elem;
+ char *text;
+ } alt;
+ 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);
+void rexmpp_xml_attribute_free_list (rexmpp_xml_attr_t *attr);
+
+/**
+ @brief Frees a single XML node. Does not free its siblings.
+*/
+void rexmpp_xml_free (rexmpp_xml_t *node);
+
+/**
+ @brief Frees an XML node and its siblings.
+*/
+void rexmpp_xml_free_list (rexmpp_xml_t *node);
+
+/**
+ @brief Clones a single XML node, without its siblings.
+*/
+rexmpp_xml_t *rexmpp_xml_clone (rexmpp_xml_t *node);
+
+/**
+ @brief Clones an XML node, together with its siblings.
+*/
+rexmpp_xml_t *rexmpp_xml_clone_list (rexmpp_xml_t *node);
+
+/**
+ @brief Creates a textual ::rexmpp_xml_t XML node (with type =
+ ::REXMPP_XML_TEXT).
+*/
+rexmpp_xml_t *rexmpp_xml_new_text (const char *str);
+
+/**
+ @brief Creates a textual ::rexmpp_xml_t XML node (with type =
+ ::REXMPP_XML_TEXT).
+*/
+rexmpp_xml_t *rexmpp_xml_new_text_len (const char *str, size_t len);
+
+/**
+ @brief Creates an element ::rexmpp_xml_t XML node (with type =
+ ::REXMPP_XML_ELEMENT).
+*/
+rexmpp_xml_t *rexmpp_xml_new_elem (const char *name,
+ const char *namespace);
+
+/**
+ @brief Adds a child node.
+*/
+void rexmpp_xml_add_child (rexmpp_xml_t *node,
+ rexmpp_xml_t *child);
+
+/**
+ @brief Creates a text node, and adds it as a child.
+*/
+int rexmpp_xml_add_text (rexmpp_xml_t *node,
+ const char *str);
+
+/**
+ @brief Creates a text node, and adds it as a child.
+*/
+int rexmpp_xml_add_text_len (rexmpp_xml_t *node,
+ const char *str,
+ size_t len);
+
+rexmpp_xml_attr_t *rexmpp_xml_attr_new (const char *name,
+ const char *namespace,
+ const char *value);
+
+int rexmpp_xml_add_attr (rexmpp_xml_t *node,
+ const char *name,
+ const char *value);
+
+int rexmpp_xml_remove_attr_ns (rexmpp_xml_t *node,
+ const char *name,
+ const char *namespace);
+
+int rexmpp_xml_remove_attr (rexmpp_xml_t *node,
+ const char *name);
+
+int rexmpp_xml_add_attr_ns (rexmpp_xml_t *node,
+ const char *name,
+ const char *namespace,
+ const char *value);
+
+/**
+ @brief Adds an "id" attribute to an XML stanza.
+ @param[in,out] s ::rexmpp
+ @param[in] node A pointer to an XML stanza.
+ @returns The same pointer as on input, for more convenient
+ composition.
+*/
+rexmpp_xml_t *
+rexmpp_xml_add_id (rexmpp_xml_t *node);
+
+/**
+ @brief A helper function for XML serialisation.
+ @param[in] node An XML node.
+ @returns A string (must be freed by the caller).
+*/
+char *rexmpp_xml_serialize (const rexmpp_xml_t *node, int pretty);
+
+/**
+ @brief Count the number of siblings after a given node.
+*/
+unsigned int rexmpp_xml_siblings_count (rexmpp_xml_t *node);
+
+/**
+ @brief Compares the node's name and namespace to given ones.
+*/
+int rexmpp_xml_match (rexmpp_xml_t *node,
+ const char *namespace,
+ const char *name);
+
+int rexmpp_xml_is_stanza (rexmpp_xml_t *node);
+
+/**
+ @brief Compose an 'error' element.
+*/
+rexmpp_xml_t *rexmpp_xml_error (const char *type, const char *condition);
+
+/**
+ @brief Matches an XML node against a namespace and an element name.
+ @param[in] node An XML node to match.
+ @param[in] namespace An XML namespace. Can be NULL (matches
+ anything), and it is assumed that the default namespace is
+ "jabber:client" (so if it is "jabber:client" and an element doesn't
+ have a namespace defined, this function counts that as a match).
+ @param[in] name Element name. Can be NULL (matches anything).
+ @returns 1 on a successful match, 0 otherwise.
+*/
+int rexmpp_xml_attr_match (rexmpp_xml_attr_t *attr,
+ const char *namespace,
+ const char *name);
+
+rexmpp_xml_attr_t *rexmpp_xml_find_attr (rexmpp_xml_t *node,
+ const char *name,
+ const char *namespace);
+
+const char *rexmpp_xml_find_attr_val_ns (rexmpp_xml_t *node,
+ const char *name,
+ const char *namespace);
+
+const char *rexmpp_xml_find_attr_val (rexmpp_xml_t *node,
+ const char *name);
+
+/**
+ @brief Finds a child element of an XML node, which matches the
+ given namespace and name.
+ @param[in] node The node containing child nodes.
+ @param[in] namespace The namespace to look for.
+ @param[in] name The element name to look for.
+ @returns A pointer to the first matching child node, or NULL if no
+ matching child elements found.
+*/
+rexmpp_xml_t *rexmpp_xml_find_child (rexmpp_xml_t *node,
+ const char *namespace,
+ const char *name);
+
+rexmpp_xml_t *rexmpp_xml_children (const rexmpp_xml_t *node);
+
+char *rexmpp_xml_text (rexmpp_xml_t *node);
+
+char *rexmpp_xml_text_child (rexmpp_xml_t *node);
+
+rexmpp_xml_t *rexmpp_xml_first_elem_child (rexmpp_xml_t *node);
+
+rexmpp_xml_t *rexmpp_xml_next_elem_sibling (rexmpp_xml_t *node);
+
+/**
+ @brief Compares two XML elements.
+*/
+int rexmpp_xml_eq (rexmpp_xml_t *n1, rexmpp_xml_t *n2);
+
+/**
+ @brief A helper function for XML parsing.
+ @param[in] str A string to parse.
+ @param[in] str_len String length.
+ @returns Parsed XML, or NULL on failure.
+*/
+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 descriptor
+ @returns Parsed XML, or NULL on failure.
+*/
+rexmpp_xml_t *rexmpp_xml_read_fd (int 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);
+
+/**
+ @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
new file mode 100644
index 0000000..f0d292a
--- /dev/null
+++ b/src/rexmpp_xml.rs
@@ -0,0 +1,1040 @@
+extern crate libc;
+use libc::{strdup, strndup, free, strcmp};
+use std::os::raw::{c_char, c_int, c_void, c_uint};
+use std::ptr;
+use std::ffi::{CStr, CString};
+use std::clone::Clone;
+use std::fs::File;
+use std::io::Write;
+
+use super::{rexmpp};
+use super::{rexmpp_random};
+
+// extern {
+// fn rexmpp_xml_parse (str: *mut c_char, str_len: c_int) -> *mut RexmppXML;
+// }
+
+#[repr(C)]
+pub struct RexmppXMLQName {
+ pub name: *mut c_char,
+ pub namespace: *mut c_char
+}
+
+impl Copy for RexmppXMLQName { }
+
+impl Clone for RexmppXMLQName {
+ fn clone(&self) -> RexmppXMLQName {
+ RexmppXMLQName {
+ name: unsafe { strdup(self.name) },
+ namespace: if self.namespace != ptr::null_mut() {
+ unsafe { strdup(self.namespace) }
+ } else {
+ ptr::null_mut()
+ }
+ }
+ }
+}
+
+#[repr(C)]
+pub struct RexmppXMLAttribute {
+ pub qname: RexmppXMLQName,
+ pub value: *mut c_char,
+ pub next: *mut RexmppXMLAttribute
+}
+
+impl Copy for RexmppXMLAttribute { }
+
+impl Clone for RexmppXMLAttribute {
+ fn clone(&self) -> RexmppXMLAttribute {
+ RexmppXMLAttribute {
+ qname: Clone::clone(&self.qname),
+ value: unsafe { strdup(self.value) },
+ next: ptr::null_mut()
+ }
+ }
+}
+
+#[derive(Copy, Clone)]
+#[derive(PartialEq)]
+#[repr(C)]
+pub enum NodeType {
+ Element,
+ Text
+}
+
+#[repr(C)]
+pub struct RexmppXMLAltElem {
+ pub qname: RexmppXMLQName,
+ pub attributes: *mut RexmppXMLAttribute,
+ pub children: *mut RexmppXML
+}
+
+impl Copy for RexmppXMLAltElem { }
+
+impl Clone for RexmppXMLAltElem {
+ fn clone(&self) -> RexmppXMLAltElem {
+ unsafe {
+ let mut ret = RexmppXMLAltElem {
+ qname: Clone::clone(&self.qname),
+ attributes: ptr::null_mut(),
+ children: ptr::null_mut()
+ };
+ let mut old_attr_ptr = self.attributes;
+ let mut next_attr_ptr_ptr : *mut *mut RexmppXMLAttribute = &mut ret.attributes;
+ loop {
+ match old_attr_ptr.as_mut() {
+ None => break,
+ Some(old_attr) => {
+ let new_attr_ptr = rexmpp_xml_attr_new(old_attr.qname.name,
+ old_attr.qname.namespace,
+ old_attr.value);
+ next_attr_ptr_ptr.write(new_attr_ptr);
+ next_attr_ptr_ptr = &mut ((*new_attr_ptr).next);
+ old_attr_ptr = old_attr.next;
+ }
+ }
+ }
+ ret.children = rexmpp_xml_clone_list(self.children);
+ return ret;
+ }
+ }
+}
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+pub enum RexmppXMLAlt {
+ Elem(RexmppXMLAltElem),
+ Text(*mut c_char)
+}
+
+#[repr(C)]
+pub struct RexmppXML {
+ pub alt: RexmppXMLAlt,
+ pub next: *mut RexmppXML
+}
+
+impl Copy for RexmppXML { }
+
+impl Clone for RexmppXML {
+ fn clone(&self) -> RexmppXML {
+ RexmppXML {
+ alt: match self.alt {
+ RexmppXMLAlt::Text(text) =>
+ RexmppXMLAlt::Text(unsafe { strdup(text) }),
+ RexmppXMLAlt::Elem(e) =>
+ RexmppXMLAlt::Elem(Clone::clone(& e))
+ },
+ next: ptr::null_mut()
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_qname_free (qname_ptr: *mut RexmppXMLQName) {
+ match unsafe { qname_ptr.as_mut() } {
+ None => return,
+ Some(qname) => {
+ if qname.name != ptr::null_mut() {
+ unsafe { free(qname.name as *mut c_void) };
+ qname.name = ptr::null_mut();
+ }
+ if qname.namespace != ptr::null_mut() {
+ unsafe { free(qname.namespace as *mut c_void) };
+ qname.namespace = ptr::null_mut();
+ }
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_attribute_free (attr_ptr: *mut RexmppXMLAttribute) {
+ if attr_ptr == ptr::null_mut() {
+ return;
+ }
+ let mut attr : RexmppXMLAttribute = unsafe { *Box::from_raw(attr_ptr) };
+ rexmpp_xml_qname_free(&mut (attr.qname));
+ if attr.value != ptr::null_mut() {
+ unsafe { free(attr.value as *mut c_void) }
+ attr.value = ptr::null_mut();
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_attribute_free_list (mut attr_ptr: *mut RexmppXMLAttribute) {
+ let mut next;
+ while attr_ptr != ptr::null_mut() {
+ next = unsafe { (*attr_ptr).next };
+ rexmpp_xml_attribute_free(attr_ptr);
+ attr_ptr = next;
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_free (node_ptr: *mut RexmppXML) {
+ if node_ptr == ptr::null_mut() {
+ return;
+ }
+ let mut node : RexmppXML = unsafe { *Box::from_raw(node_ptr) };
+ unsafe {
+ match node.alt {
+ RexmppXMLAlt::Text(mut t) => {
+ free(t as *mut c_void);
+ t = ptr::null_mut();
+ },
+ RexmppXMLAlt::Elem(mut element) => {
+ rexmpp_xml_qname_free(&mut (element.qname));
+ rexmpp_xml_attribute_free_list(element.attributes);
+ rexmpp_xml_free_list(element.children);
+ }
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_free_list (mut node_ptr: *mut RexmppXML) {
+ let mut next;
+ while node_ptr != ptr::null_mut() {
+ next = unsafe { (*node_ptr).next };
+ rexmpp_xml_free(node_ptr);
+ node_ptr = next;
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_clone (node_ptr: *mut RexmppXML) -> *mut RexmppXML {
+ if node_ptr == ptr::null_mut() {
+ return ptr::null_mut();
+ }
+ return Box::into_raw(Box::new(Clone::clone(& unsafe { *node_ptr })));
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_clone_list (mut node_ptr: *mut RexmppXML) -> *mut RexmppXML {
+ if node_ptr == ptr::null_mut() {
+ return ptr::null_mut();
+ }
+ let first_ptr = rexmpp_xml_clone(node_ptr);
+ let mut last_ptr = first_ptr;
+ node_ptr = unsafe { (*node_ptr).next };
+ while node_ptr != ptr::null_mut() {
+ unsafe { (*last_ptr).next = rexmpp_xml_clone(node_ptr) };
+ last_ptr = unsafe { (*last_ptr).next };
+ node_ptr = unsafe { (*node_ptr).next };
+ }
+ return first_ptr;
+}
+
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_new_text (str: *const c_char) -> *mut RexmppXML {
+ let node = RexmppXML {
+ alt: RexmppXMLAlt::Text( unsafe { strdup(str) } ),
+ next: ptr::null_mut()
+ };
+ let b = Box::new(node);
+ return Box::into_raw(b);
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_new_text_len (str: *const c_char, len: usize) -> *mut RexmppXML {
+ let node = RexmppXML {
+ alt: RexmppXMLAlt::Text( unsafe { strndup(str, len) } ),
+ next: ptr::null_mut()
+ };
+ let b = Box::new(node);
+ return Box::into_raw(b);
+}
+
+#[no_mangle]
+pub extern "C" fn rexmpp_xml_add_child (node: *mut RexmppXML,
+ child: *mut RexmppXML) -> () {
+ // It is important to wrap everything in "unsafe" here; somehow
+ // the enum fields are not mutated otherwise.
+ unsafe {
+ match (*node).alt {
+ RexmppXMLAlt::Elem(ref mut elem) => {
+ let mut last_ptr : &mut *mut RexmppXML =
+ &mut ((*elem).children);
+ while *last_ptr != ptr::null_mut() {
+ last_ptr = &mut ((*(* last_ptr)).next);
+ }
+ *last_ptr = child;
+ },
+ _ => ()
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn rexmpp_xml_add_text (node: *mut RexmppXML,
+ str: *const c_char) -> c_int {
+ let text_node : *mut RexmppXML = rexmpp_xml_new_text(str);
+ if text_node != ptr::null_mut() {
+ rexmpp_xml_add_child(node, text_node);
+ return 1;
+ }
+ return 0;
+}
+
+#[no_mangle]
+pub extern "C" fn rexmpp_xml_add_text_len (node: *mut RexmppXML,
+ str: *const c_char,
+ len: usize) -> c_int {
+ let text_node : *mut RexmppXML = rexmpp_xml_new_text_len(str, len);
+ if text_node != ptr::null_mut() {
+ rexmpp_xml_add_child(node, text_node);
+ return 1;
+ }
+ return 0;
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_new_elem (name: *const c_char,
+ namespace: *const c_char) -> *mut RexmppXML {
+ let node = RexmppXML {
+ alt: RexmppXMLAlt::Elem (
+ RexmppXMLAltElem {
+ qname: RexmppXMLQName {
+ name: unsafe { strdup(name) },
+ namespace: if namespace == ptr::null_mut() {
+ ptr::null_mut()
+ } else {
+ unsafe { strdup(namespace) }
+ }
+ },
+ attributes: ptr::null_mut(),
+ children: ptr::null_mut()
+ }
+ ),
+ next: ptr::null_mut()
+ };
+ let b = Box::new(node);
+ return Box::into_raw(b);
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_attr_new (name: *const c_char,
+ namespace: *const c_char,
+ value: *const c_char) -> *mut RexmppXMLAttribute {
+ let node = RexmppXMLAttribute {
+ qname: RexmppXMLQName {
+ name: unsafe { strdup(name) },
+ namespace: if namespace == ptr::null_mut() {
+ ptr::null_mut()
+ } else {
+ unsafe { strdup(namespace) }
+ }
+ },
+ value: unsafe { strdup(value) },
+ next: ptr::null_mut()
+ };
+ return Box::into_raw(Box::new(node));
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_add_attr_ns (node: *mut RexmppXML,
+ name: *const c_char,
+ namespace: *const c_char,
+ value: *const c_char) -> c_int {
+ if node == ptr::null_mut() {
+ return -1;
+ }
+ // Wrapping everything into "unsafe", otherwise enum fields are
+ // not mutated.
+ unsafe {
+ match(*node).alt {
+ RexmppXMLAlt::Elem(ref mut elem) => {
+ let attr = rexmpp_xml_attr_new(name, namespace, value);
+ (*attr).next = (*elem).attributes;
+ (*elem).attributes = attr;
+ 0
+ },
+ _ => -1
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_remove_attr_ns (node: *mut RexmppXML,
+ name: *const c_char,
+ namespace: *const c_char) -> c_int {
+ if node == ptr::null_mut() {
+ return -1;
+ }
+ // Wrapping everything into "unsafe", otherwise enum fields are
+ // not mutated.
+ unsafe {
+ match (*node).alt {
+ RexmppXMLAlt::Elem(ref mut elem) => {
+ let mut attr_ptr_ptr: *mut *mut RexmppXMLAttribute =
+ &mut (*elem).attributes;
+ while *attr_ptr_ptr != ptr::null_mut() {
+ if rexmpp_xml_attr_match(*attr_ptr_ptr,
+ namespace, name) > 0 {
+ let next_attr_ptr : *mut RexmppXMLAttribute =
+ (**attr_ptr_ptr).next;
+ rexmpp_xml_attribute_free(*attr_ptr_ptr);
+ *attr_ptr_ptr = next_attr_ptr;
+ return 0;
+ }
+ attr_ptr_ptr = &mut (**attr_ptr_ptr).next;
+ }
+ 1
+ },
+ _ => -1,
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_add_attr (node: *mut RexmppXML,
+ name: *const c_char,
+ value: *const c_char) -> c_int {
+ rexmpp_xml_add_attr_ns(node, name, ptr::null_mut(), value)
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_remove_attr (node: *mut RexmppXML,
+ name: *const c_char) -> c_int {
+ rexmpp_xml_remove_attr_ns(node, name, ptr::null_mut())
+}
+
+
+fn rexmpp_xml_push_escaped (c: char, s: &mut String) {
+ if c == '<' {
+ s.push_str("&lt;")
+ } else if c == '>' {
+ s.push_str("&gt;")
+ } else if c == '&' {
+ s.push_str("&amp;")
+ } else if c == '\'' {
+ s.push_str("&apos;")
+ } else if c == '"' {
+ s.push_str("&quot;")
+ } else {
+ s.push_str(format!("&#{};", u32::from(c)).as_str());
+ };
+}
+
+fn rexmpp_xml_print_text (c: char, s: &mut String) {
+ if "<&>'\"".chars().any(|sc| sc == c) {
+ rexmpp_xml_push_escaped(c, s);
+ } else {
+ s.push(c);
+ }
+}
+
+fn rexmpp_xml_print_name (i: usize, c: char, s: &mut String) {
+ if c == ':'
+ || (c >= 'A' && c <= 'Z')
+ || c == '_'
+ || (c >= 'a' && c <= 'z')
+ || (c >= '\u{C0}' && c <= '\u{D6}')
+ || (c >= '\u{D8}' && c <= '\u{F6}')
+ || (c >= '\u{F8}' && c <= '\u{2FF}')
+ || (c >= '\u{370}' && c <= '\u{37D}')
+ || (c >= '\u{37F}' && c <= '\u{1FFF}')
+ || (c >= '\u{200C}' && c <= '\u{200D}')
+ || (c >= '\u{2070}' && c <= '\u{218F}')
+ || (c >= '\u{2C00}' && c <= '\u{2FEF}')
+ || (c >= '\u{3001}' && c <= '\u{D7FF}')
+ || (c >= '\u{F900}' && c <= '\u{FDCF}')
+ || (c >= '\u{FDF0}' && c <= '\u{FFF0}')
+ || (c >= '\u{10000}' && c <= '\u{EFFFF}')
+ || ((i > 0) &&
+ (c == '-'
+ || c == '.'
+ || (c >= '0' && c <= '9')
+ || c == '\u{B7}'
+ || (c >= '\u{0300}' && c <= '\u{036F}')
+ || (c >= '\u{203F}' && c <= '\u{2040}')))
+ {
+ // Print the allowed characters.
+ s.push(c);
+ }
+}
+
+fn rexmpp_xml_print_indent (indent: i32, s: &mut String) {
+ let mut i = 0;
+ while i < indent {
+ s.push_str(" ");
+ i = i + 1;
+ }
+}
+
+fn rexmpp_xml_print (node_ptr: *const RexmppXML,
+ ret: &mut String,
+ indent: i32)
+ -> ()
+{
+ unsafe {
+ let node : RexmppXML = *node_ptr;
+ match node {
+ RexmppXML { alt : RexmppXMLAlt::Text (text_ptr),
+ next: _} => {
+ let text_cstr : &CStr = CStr::from_ptr(text_ptr);
+ let text_str : String =
+ String::from_utf8_lossy(text_cstr.to_bytes())
+ .to_string();
+ // let mut escaped = String::with_capacity(text_str.capacity());
+ text_str.chars().
+ for_each(|c| rexmpp_xml_print_text(c, ret));
+ },
+ RexmppXML { alt : RexmppXMLAlt::Elem (element),
+ next: _} => {
+ // let mut ret = String::with_capacity(1024);
+ let name_cstr : &CStr =
+ CStr::from_ptr(element.qname.name);
+ let name_str : String =
+ String::from_utf8_lossy(name_cstr.to_bytes())
+ .to_string();
+ if indent > 0 {
+ ret.push('\n');
+ rexmpp_xml_print_indent(indent, ret);
+ }
+ ret.push('<');
+ name_str.char_indices().
+ for_each(|(i, c)| rexmpp_xml_print_name(i, c, ret));
+ if element.qname.namespace != ptr::null_mut() {
+ let namespace_cstr : &CStr =
+ CStr::from_ptr(element.qname.namespace);
+ let namespace_str : String =
+ String::from_utf8_lossy(namespace_cstr.to_bytes())
+ .to_string();
+ ret.push_str(" xmlns=\"");
+ namespace_str.chars().
+ for_each(|c| rexmpp_xml_print_text(c, ret));
+ ret.push('"');
+ }
+ if element.attributes != ptr::null_mut() {
+ let mut attr_ptr : *mut RexmppXMLAttribute =
+ element.attributes;
+ while attr_ptr != ptr::null_mut() {
+ let attr : RexmppXMLAttribute = *attr_ptr;
+ let attr_name_cstr =
+ CStr::from_ptr(attr.qname.name);
+ let attr_name_str =
+ String::from_utf8_lossy(attr_name_cstr.to_bytes())
+ .to_string();
+ // TODO: handle attribute namespaces someday.
+ let attr_value_cstr =
+ CStr::from_ptr(attr.value);
+ let attr_value_str =
+ String::from_utf8_lossy(attr_value_cstr.to_bytes())
+ .to_string();
+ ret.push(' ');
+ attr_name_str.char_indices().
+ for_each(|(i, c)|
+ rexmpp_xml_print_name(i, c, ret));
+ ret.push_str("=\"");
+ attr_value_str.chars().
+ for_each(|c| rexmpp_xml_print_text(c, ret));
+ ret.push('"');
+ attr_ptr = (*attr_ptr).next;
+ }
+ }
+ if element.children == ptr::null_mut() {
+ ret.push_str("/>");
+ } else {
+ ret.push('>');
+ let mut child = rexmpp_xml_children(node_ptr);
+ let mut last_child_is_textual = false;
+ while child != ptr::null_mut() {
+ rexmpp_xml_print(child, ret,
+ if indent > -1 { indent + 1 }
+ else { -1 } );
+ last_child_is_textual =
+ matches!((*child).alt, RexmppXMLAlt::Text(_));
+ child = (*child).next;
+ }
+ if indent > 0 && ! last_child_is_textual {
+ ret.push('\n');
+ rexmpp_xml_print_indent(indent, ret);
+ }
+ ret.push_str("</");
+ name_str.char_indices().
+ for_each(|(i, c)|
+ rexmpp_xml_print_name(i, c, ret));
+ ret.push('>');
+ }
+ }
+ }
+ }
+}
+
+fn rexmpp_xml_serialize_str (node_ptr: *const RexmppXML,
+ pretty: bool)
+ -> String
+{
+ let mut out = String::with_capacity(4096);
+ rexmpp_xml_print(node_ptr, &mut out, if pretty { 0 } else { -1 });
+ return out;
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_serialize (node_ptr: *const RexmppXML,
+ pretty: bool)
+ -> *mut c_char
+{
+ let out = rexmpp_xml_serialize_str(node_ptr, pretty);
+ match CString::new(out) {
+ Ok(cstr) => unsafe { strdup(cstr.as_ptr()) },
+ Err(_) => ptr::null_mut()
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_add_id (node: *mut RexmppXML)
+ -> *mut RexmppXML
+{
+ match CString::new("id") {
+ Err(_) => return ptr::null_mut(),
+ Ok(id_cstr) => {
+ let buf = unsafe { rexmpp_random::rexmpp_random_id() };
+ if buf == ptr::null_mut() {
+ return ptr::null_mut();
+ }
+ rexmpp_xml_add_attr(node, id_cstr.as_ptr(), buf);
+ unsafe { free(buf as *mut c_void) };
+ return node;
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_write_file (path: *const c_char,
+ node: *const RexmppXML)
+ -> c_int
+{
+ let path_cstr : &CStr = unsafe { CStr::from_ptr(path) };
+ let path_str : String =
+ String::from_utf8_lossy(path_cstr.to_bytes())
+ .to_string();
+ match File::create(path_str) {
+ Ok(mut fd) => {
+ fd.write_all(b"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
+ fd.write_all(rexmpp_xml_serialize_str(node, false).as_bytes());
+ },
+ Err(_) => { return -1; }
+ }
+ return 0;
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_siblings_count (mut node: *const RexmppXML) -> c_uint {
+ let mut i : c_uint = 0;
+ while node != ptr::null() {
+ node = unsafe { (*node).next };
+ i = i + 1;
+ }
+ return i;
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_match (node: *const RexmppXML,
+ namespace: *const c_char,
+ name: *const c_char) -> c_int {
+ if node == ptr::null_mut() {
+ return 0;
+ }
+ match unsafe { (*node).alt } {
+ RexmppXMLAlt::Text(_) => return 0,
+ RexmppXMLAlt::Elem(elem) => {
+ if name != ptr::null_mut() {
+ let name_cstr : &CStr = unsafe { CStr::from_ptr(name) };
+ let elem_name_cstr : &CStr =
+ unsafe { CStr::from_ptr(elem.qname.name) };
+ if name_cstr != elem_name_cstr {
+ return 0;
+ }
+ }
+ if namespace != ptr::null_mut() {
+ let namespace_cstr : &CStr = unsafe { CStr::from_ptr(namespace) };
+ if unsafe { elem.qname.namespace } == ptr::null_mut() {
+ match CStr::to_str(namespace_cstr) {
+ Ok(namespace_str) => if namespace_str == "jabber:client" {
+ return 1;
+ },
+ Err(_) => return 0
+ }
+ return 0;
+ }
+ let elem_namespace_cstr : &CStr =
+ unsafe { CStr::from_ptr(elem.qname.namespace) };
+ if namespace_cstr != elem_namespace_cstr {
+ return 0;
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_attr_match (attr: *const RexmppXMLAttribute,
+ namespace: *const c_char,
+ name: *const c_char) -> c_int {
+ if attr == ptr::null() {
+ return 0;
+ }
+ if name != ptr::null() {
+ let name_cstr : &CStr = unsafe { CStr::from_ptr(name) };
+ let attr_name_cstr : &CStr = unsafe { CStr::from_ptr((*attr).qname.name) };
+ if name_cstr != attr_name_cstr {
+ return 0;
+ }
+ }
+ if namespace != ptr::null() {
+ let namespace_cstr : &CStr = unsafe { CStr::from_ptr(namespace) };
+ if unsafe { (*attr).qname.namespace } == ptr::null_mut() {
+ match CStr::to_str(namespace_cstr) {
+ Ok(namespace_str) => if namespace_str != "jabber:client" {
+ return 0;
+ },
+ Err(_) => return 0
+ }
+ } else {
+ let attr_namespace_cstr : &CStr =
+ unsafe { CStr::from_ptr((*attr).qname.namespace) };
+ if namespace_cstr != attr_namespace_cstr {
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_is_stanza (node: *const RexmppXML) -> c_int {
+ if rexmpp_xml_match(node,
+ CString::new("jabber:client").expect("CString::new failed").as_ptr(),
+ CString::new("message").expect("CString::new failed").as_ptr()) == 1
+ || rexmpp_xml_match(node,
+ CString::new("jabber:client").expect("CString::new failed").as_ptr(),
+ CString::new("iq").expect("CString::new failed").as_ptr()) == 1
+ || rexmpp_xml_match(node,
+ CString::new("jabber:client").expect("CString::new failed").as_ptr(),
+ CString::new("presence").expect("CString::new failed").as_ptr()) == 1
+ {
+ return 1;
+ }
+ return 0;
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_error (error_type: *const c_char, condition: *const c_char)
+ -> *mut RexmppXML {
+ let error : *mut RexmppXML =
+ rexmpp_xml_new_elem(CString::new("error")
+ .expect("CString::new failed")
+ .as_ptr(),
+ ptr::null_mut());
+ rexmpp_xml_add_attr(error,
+ CString::new("type")
+ .expect("CString::new failed")
+ .as_ptr(),
+ error_type);
+ let cond =
+ rexmpp_xml_new_elem(condition,
+ CString::new("urn:ietf:params:xml:ns:xmpp-stanzas")
+ .expect("CString::new failed")
+ .as_ptr());
+ rexmpp_xml_add_child(error, cond);
+ return error;
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_find_attr (node: *mut RexmppXML,
+ name: *const c_char,
+ namespace: *const c_char)
+ -> *mut RexmppXMLAttribute {
+ if node == ptr::null_mut() {
+ return ptr::null_mut();
+ }
+ match unsafe { (*node).alt } {
+ RexmppXMLAlt::Elem(elem) => {
+ let mut attr_ptr : *mut RexmppXMLAttribute =
+ unsafe { elem.attributes };
+ while attr_ptr != ptr::null_mut() {
+ if rexmpp_xml_attr_match(attr_ptr, namespace, name) > 0 {
+ return attr_ptr;
+ }
+ unsafe { attr_ptr = (*attr_ptr).next };
+ }
+ return ptr::null_mut();
+ },
+ _ => return ptr::null_mut(),
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_find_attr_val_ns (node: *mut RexmppXML,
+ name: *const c_char,
+ namespace: *const c_char)
+ -> *const c_char {
+ let attr : *mut RexmppXMLAttribute =
+ rexmpp_xml_find_attr(node, name, namespace);
+ if attr != ptr::null_mut() {
+ return unsafe { (*attr).value };
+ }
+ return ptr::null_mut();
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_find_attr_val (node: *mut RexmppXML,
+ name: *const c_char)
+ -> *const c_char {
+ rexmpp_xml_find_attr_val_ns(node, name, ptr::null_mut())
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_find_child (node: *mut RexmppXML,
+ namespace: *const c_char,
+ name: *const c_char)
+ -> *mut RexmppXML {
+ if node == ptr::null_mut() {
+ return ptr::null_mut();
+ }
+ match unsafe { (*node).alt } {
+ RexmppXMLAlt::Elem(elem) => {
+ let mut child: *mut RexmppXML = unsafe { elem.children };
+ while child != ptr::null_mut() {
+ if rexmpp_xml_match(child, namespace, name) > 0 {
+ return child;
+ }
+ unsafe { child = (*child).next };
+ }
+ ptr::null_mut()
+ },
+ _ => ptr::null_mut()
+ }
+}
+
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_eq (n1: *const RexmppXML, n2: *const RexmppXML) -> bool {
+ if n1 == n2 {
+ return true;
+ }
+ if n1 == ptr::null_mut() || n1 == ptr::null_mut() {
+ return false;
+ }
+ unsafe {
+ match (*n1, *n2) {
+ (RexmppXML { alt : RexmppXMLAlt::Text (text1),
+ next: _ },
+ RexmppXML { alt : RexmppXMLAlt::Text (text2),
+ next: _ }
+ ) => strcmp(text1, text2) == 0,
+ (RexmppXML
+ { alt : RexmppXMLAlt::Elem
+ ( RexmppXMLAltElem {
+ qname: RexmppXMLQName {
+ name: name1,
+ namespace: namespace1
+ },
+ attributes: attributes1,
+ children: children1
+ } ),
+ next: _},
+ RexmppXML
+ { alt : RexmppXMLAlt::Elem
+ ( RexmppXMLAltElem {
+ qname: RexmppXMLQName {
+ name: name2,
+ namespace: namespace2
+ },
+ attributes: attributes2,
+ children: children2
+ } ),
+ next: _}
+ ) => {
+ // Compare names
+ if strcmp(name1, name2) != 0
+ { return false; }
+ // Compare namespaces
+ if namespace1 != namespace2 &&
+ (namespace1 == ptr::null_mut() ||
+ namespace2 == ptr::null_mut() ||
+ strcmp(namespace1, namespace2) != 0)
+ { return false; }
+ // Compare attributes
+ let mut attr1 = attributes1;
+ let mut attr2 = attributes2;
+ while ! (attr1 == ptr::null_mut() && attr2 == ptr::null_mut()) {
+ if attr1 == ptr::null_mut() {
+ return false;
+ }
+ if attr2 == ptr::null_mut() {
+ return false;
+ }
+ if strcmp((*attr1).qname.name, (*attr2).qname.name) != 0 {
+ return false;
+ }
+ if strcmp((*attr1).value, (*attr2).value) != 0 {
+ return false;
+ }
+ attr1 = (*attr1).next;
+ attr2 = (*attr2).next;
+ }
+ // Compare children
+ let mut child1 = children1;
+ let mut child2 = children2;
+ while ! (child1 == ptr::null_mut() && child2 == ptr::null_mut())
+ {
+ if child1 == ptr::null_mut() {
+ return false;
+ }
+ if child2 == ptr::null_mut() {
+ return false;
+ }
+ if ! rexmpp_xml_eq(child1, child2) {
+ return false;
+ }
+ child1 = (*child1).next;
+ child2 = (*child2).next;
+ }
+ true
+ }
+ _ => false
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_children (node: *const RexmppXML)
+ -> *mut RexmppXML {
+ if node == ptr::null_mut() {
+ return ptr::null_mut();
+ }
+ match unsafe { (*node).alt } {
+ RexmppXMLAlt::Elem(elem) => elem.children,
+ _ => ptr::null_mut()
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_first_elem_child (node: *mut RexmppXML)
+ -> *mut RexmppXML {
+ let mut child: *mut RexmppXML = rexmpp_xml_children(node);
+ while child != ptr::null_mut() {
+ if matches!(unsafe { (*child).alt }, RexmppXMLAlt::Elem(_)) {
+ return child;
+ }
+ unsafe { child = (*child).next };
+ }
+ return ptr::null_mut();
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_next_elem_sibling (node: *mut RexmppXML)
+ -> *mut RexmppXML {
+ if node == ptr::null_mut() {
+ return ptr::null_mut();
+ }
+ let mut sibling: *mut RexmppXML = unsafe { (*node).next };
+ while sibling != ptr::null_mut() {
+ if matches!(unsafe { (*sibling).alt }, RexmppXMLAlt::Elem(_)) {
+ return sibling;
+ }
+ unsafe { sibling = (*sibling).next };
+ }
+ return ptr::null_mut();
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_text (node: *mut RexmppXML)
+ -> *mut c_char {
+ if node == ptr::null_mut() {
+ return ptr::null_mut();
+ }
+ match unsafe { (*node).alt } {
+ RexmppXMLAlt::Text(text) => text,
+ _ => ptr::null_mut()
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_text_child (node: *mut RexmppXML)
+ -> *mut c_char {
+ rexmpp_xml_text(rexmpp_xml_children(node))
+}
+
+#[no_mangle]
+pub extern "C"
+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() {
+ unsafe {
+ next = (*node).next;
+ (*node).next = prev;
+ prev = node;
+ node = next;
+ }
+ }
+ return prev;
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_reverse_children (node: *mut RexmppXML)
+ -> *mut RexmppXML {
+ unsafe {
+ if node == ptr::null_mut() {
+ return node;
+ }
+ match (*node).alt {
+ RexmppXMLAlt::Elem(ref mut elem) => {
+ (*elem).children = rexmpp_xml_reverse_list((*elem).children);
+ let mut cur = node;
+ while cur != ptr::null_mut() {
+ match (*cur).alt {
+ RexmppXMLAlt::Elem(ref mut cur_elem) => {
+ (*cur_elem).children =
+ rexmpp_xml_reverse_children((*cur_elem).children);
+ },
+ _ => ()
+ }
+ cur = (*cur).next;
+ }
+ },
+ _ => ()
+ }
+ }
+ return node;
+}
diff --git a/src/rexmpp_xml_parser.c b/src/rexmpp_xml_parser.c
new file mode 100644
index 0000000..3f485d7
--- /dev/null
+++ b/src/rexmpp_xml_parser.c
@@ -0,0 +1,323 @@
+/**
+ @file rexmpp_xml_parser.c
+ @brief XML parsing for rexmpp
+ @author defanor <defanor@uberspace.net>
+ @date 2023
+ @copyright MIT license.
+*/
+
+#include <string.h>
+
+#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);
+ if (attr_val != NULL) {
+ 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,
+ int final)
+{
+#if defined(USE_LIBXML2)
+ xmlParseChunk(ctx->xml_parser, chunk, len, final);
+#elif defined(USE_EXPAT)
+ XML_Parse(ctx->xml_parser, chunk, len, final);
+#endif
+}
diff --git a/src/rexmpp_xml_parser.h b/src/rexmpp_xml_parser.h
new file mode 100644
index 0000000..66627ab
--- /dev/null
+++ b/src/rexmpp_xml_parser.h
@@ -0,0 +1,106 @@
+/**
+ @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;
+#else
+ void *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,
+ int final);
+
+/**
+ @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
diff --git a/src/rexmpp_xml_parser.rs b/src/rexmpp_xml_parser.rs
new file mode 100644
index 0000000..037c2f2
--- /dev/null
+++ b/src/rexmpp_xml_parser.rs
@@ -0,0 +1,145 @@
+extern crate libc;
+extern crate rxml;
+use libc::{free, strndup};
+use std::ptr;
+use std::os::raw::{c_char, c_void};
+use std::ffi::{CStr, CString};
+use std::slice;
+use rxml::{FeedParser, Error, ResolvedEvent, XmlVersion, EventRead, CData};
+use std::io;
+use super::{rexmpp_xml};
+
+type RexmppXMLParserElementStart = unsafe extern "C"
+fn (data: *mut c_void,
+ name: *const c_char,
+ namespace: *const c_char,
+ attributes: *mut rexmpp_xml::RexmppXMLAttribute) -> ();
+
+type RexmppXMLParserElementEnd = unsafe extern "C"
+fn (data: *mut c_void) -> ();
+
+type RexmppXMLParserCharacters = unsafe extern "C"
+fn (data: *mut c_void,
+ ch: *const c_char,
+ len: usize) -> ();
+
+#[repr(C)]
+struct RexmppXMLParserHandlers {
+ elem_start: RexmppXMLParserElementStart,
+ elem_end: RexmppXMLParserElementEnd,
+ text: RexmppXMLParserCharacters
+}
+
+#[repr(C)]
+struct RexmppXMLParserCtx {
+ xml_parser: *mut FeedParser,
+ handlers: *mut RexmppXMLParserHandlers,
+ user_data: *mut c_void
+}
+
+#[no_mangle]
+extern "C"
+fn rexmpp_xml_parser_new (handlers: *mut RexmppXMLParserHandlers,
+ data: *mut c_void)
+ -> *mut RexmppXMLParserCtx
+{
+ let mut fp = FeedParser::default();
+ let ctx = RexmppXMLParserCtx {
+ xml_parser: Box::into_raw(Box::new(fp)),
+ handlers: handlers,
+ user_data: data
+ };
+ Box::into_raw(Box::new(ctx))
+}
+
+#[no_mangle]
+extern "C"
+fn rexmpp_xml_parser_free (ctx: *mut RexmppXMLParserCtx) {
+ unsafe { free(ctx as *mut c_void) };
+}
+
+#[no_mangle]
+extern "C"
+fn rexmpp_xml_parser_feed (ctx: *mut RexmppXMLParserCtx,
+ chunk: *const c_char,
+ len: usize,
+ is_final: bool)
+{
+ unsafe {
+ // todo: maybe duplicate the string, since apparently a
+ // mutable one is expected by the parser.
+ let mut buf : &[u8] = slice::from_raw_parts(chunk as *mut u8, len);
+ let user_data_ptr = (*ctx).user_data;
+ let handlers = (*ctx).handlers;
+ (*((*ctx).xml_parser)).parse_all(&mut buf, is_final, |ev| {
+ match ev {
+ ResolvedEvent::StartElement(_, (namespace, name), attrs) =>
+ {
+ let name_str = name.to_string();
+ let ns_opt_cstr : Option<CString> = match namespace {
+ None => None,
+ Some(ns_arc_name) => {
+ match CString::new(ns_arc_name.to_string()) {
+ Ok(cstr) => Some(cstr),
+ Err(_) => None
+ }
+ }
+ };
+ match CString::new(name_str) {
+ Ok(name_cstr) => {
+ let name_cstr_ptr = name_cstr.as_ptr();
+ let namespace_cstr_ptr =
+ match ns_opt_cstr {
+ None => ptr::null_mut(),
+ // "ref" is important to use here,
+ // otherwise the pointer will be
+ // wrong.
+ Some(ref ns_cstr) => ns_cstr.as_ptr()
+ };
+ let mut attributes = ptr::null_mut();
+ for ((_, attr_name), attr_val) in attrs.iter() {
+ match (CString::new(attr_name.to_string()),
+ CString::new(attr_val.to_string())) {
+ (Ok(attr_name_cstr), Ok(attr_val_cstr)) => {
+ let attr =
+ rexmpp_xml::rexmpp_xml_attr_new
+ (attr_name_cstr.as_ptr(),
+ ptr::null_mut(),
+ attr_val_cstr.as_ptr());
+ (*attr).next = attributes;
+ attributes = attr;
+ },
+ _ => ()
+ }
+ }
+ ((*handlers).elem_start)
+ (user_data_ptr,
+ name_cstr_ptr,
+ namespace_cstr_ptr,
+ attributes);
+ },
+ Err(_) => ()
+ }
+ },
+ ResolvedEvent::EndElement(_) =>
+ ((*handlers).elem_end)(user_data_ptr),
+ ResolvedEvent::Text(_, cd) =>
+ ((*handlers).text)(
+ user_data_ptr,
+ cd.as_ptr() as *const i8,
+ cd.len()
+ ),
+ _ => ()
+ }
+ });
+ }
+}
+
+#[no_mangle]
+extern "C"
+fn rexmpp_xml_parser_reset (ctx_raw: *mut RexmppXMLParserCtx)
+ -> *mut RexmppXMLParserCtx
+{
+ let ctx = unsafe { Box::from_raw(ctx_raw) };
+ rexmpp_xml_parser_new((*ctx).handlers, (*ctx).user_data)
+}
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 7aba0b7..93c0b82 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,20 +1,24 @@
-AM_CFLAGS = -Werror -Wall -Wextra -pedantic -std=gnu99 \
- -Wno-pointer-sign
+AM_CFLAGS = -Werror -Wall -Wextra -pedantic -std=gnu99
-COMMON_FLAGS = $(AM_CFLAGS) $(LIBXML_CFLAGS) \
+COMMON_FLAGS = $(AM_CFLAGS) $(LIBXML_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) \
- -I$(top_srcdir)/src
-COMMON_LIBS = $(LIBXML_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) \
- -L$(top_builddir)/src -lrexmpp
+ $(PORTAUDIO_CFLAGS) $(OPUS_CFLAGS) $(NETTLE_CFLAGS)
+COMMON_LDADD = -L$(top_builddir)/src -lrexmpp
send_to_self_CFLAGS = $(COMMON_FLAGS)
-send_to_self_LDADD = $(COMMON_LIBS)
+xml_parse_and_print_CFLAGS = $(COMMON_FLAGS)
+xml_print_and_parse_CFLAGS = $(COMMON_FLAGS)
+base64_CFLAGS = $(COMMON_FLAGS)
-check_PROGRAMS = send_to_self
-TESTS = send_to_self
+send_to_self_LDADD = $(COMMON_LDADD)
+xml_parse_and_print_LDADD = $(COMMON_LDADD)
+xml_print_and_parse_LDADD = $(COMMON_LDADD)
+base64_LDADD = $(COMMON_LDADD)
+
+check_PROGRAMS = send_to_self \
+ xml_parse_and_print xml_print_and_parse base64
+TESTS = send_to_self \
+ xml_parse_and_print xml_print_and_parse base64
diff --git a/tests/base64.c b/tests/base64.c
new file mode 100644
index 0000000..b69ba0e
--- /dev/null
+++ b/tests/base64.c
@@ -0,0 +1,18 @@
+#include <string.h>
+#include "rexmpp_base64.h"
+
+int main () {
+ char *original_plain = "test string";
+ char *original_base64 = "dGVzdCBzdHJpbmc=";
+ char *encoded, *decoded;
+ size_t encoded_len, decoded_len;
+ if (rexmpp_base64_to(original_plain, strlen(original_plain),
+ &encoded, &encoded_len)) {
+ return -1;
+ }
+ if (rexmpp_base64_from(original_base64, strlen(original_base64),
+ &decoded, &decoded_len)) {
+ return -1;
+ }
+ return strcmp(original_plain, decoded) || strcmp(original_base64, encoded);
+}
diff --git a/tests/send_to_self.c b/tests/send_to_self.c
index 9c8c514..2fcb80f 100644
--- a/tests/send_to_self.c
+++ b/tests/send_to_self.c
@@ -16,7 +16,6 @@ that it's the expected message.
#include <stdio.h>
#include <errno.h>
#include <syslog.h>
-#include <gsasl.h>
#include <rexmpp.h>
#include <rexmpp_sasl.h>
@@ -62,37 +61,34 @@ int my_sasl_property_cb (rexmpp_t *s, rexmpp_sasl_property prop) {
return -1;
}
-int my_xml_in_cb (rexmpp_t *s, xmlNodePtr node) {
+int my_xml_in_cb (rexmpp_t *s, rexmpp_xml_t *node) {
(void)s;
- char *xml_buf = rexmpp_xml_serialize(node);
+ char *xml_buf = rexmpp_xml_serialize(node, 0);
printf("recv: %s\n", xml_buf);
free(xml_buf);
if (stage == TEST_MESSAGE_SENT && rexmpp_xml_match(node, "jabber:client", "message")) {
- xmlNodePtr body = rexmpp_xml_find_child(node, "jabber:client", "body");
+ rexmpp_xml_t *body = rexmpp_xml_find_child(node, "jabber:client", "body");
if (body != NULL) {
- char *txt = xmlNodeGetContent(body);
+ char *txt = rexmpp_xml_text_child(body);
if (txt != NULL) {
if (strcmp(txt, msg_text) == 0) {
stage = TEST_MESSAGE_RECEIVED;
}
- free(txt);
}
}
}
return 0;
}
-int my_xml_out_cb (rexmpp_t *s, xmlNodePtr node) {
+int my_xml_out_cb (rexmpp_t *s, rexmpp_xml_t *node) {
(void)s;
- char *xml_buf = rexmpp_xml_serialize(node);
+ char *xml_buf = rexmpp_xml_serialize(node, 0);
printf("send: %s\n", xml_buf);
free(xml_buf);
return 0;
}
-int main (int argc, char **argv) {
- (void)argc;
- (void)argv;
+int main () {
jid = getenv("JID");
pass = getenv("PASS");
char *tls_policy = getenv("TLS_POLICY");
@@ -125,8 +121,10 @@ int main (int argc, char **argv) {
fd_set read_fds, write_fds;
int nfds;
- struct timeval tv;
- struct timeval *mtv;
+ struct timespec tv;
+ struct timespec *mtv;
+ struct timeval tv_ms;
+ struct timeval *mtv_ms;
int n = 0;
do {
@@ -141,11 +139,13 @@ int main (int argc, char **argv) {
}
if (stage == TEST_CONNECTING && s.stream_state == REXMPP_STREAM_READY) {
- xmlNodePtr msg = rexmpp_xml_add_id(&s, xmlNewNode(NULL, "message"));
- xmlNewNs(msg, "jabber:client", NULL);
- xmlNewProp(msg, "to", jid);
- xmlNewProp(msg, "type", "chat");
- xmlNewTextChild(msg, NULL, "body", msg_text);
+ rexmpp_xml_t *msg =
+ rexmpp_xml_add_id(rexmpp_xml_new_elem("message", "jabber:client"));
+ rexmpp_xml_add_attr(msg, "to", jid);
+ rexmpp_xml_add_attr(msg, "type", "chat");
+ rexmpp_xml_t *body = rexmpp_xml_new_elem("body", NULL);
+ rexmpp_xml_add_text(body, msg_text);
+ rexmpp_xml_add_child(msg, body);
rexmpp_send(&s, msg);
stage = TEST_MESSAGE_SENT;
} else if (stage == TEST_MESSAGE_RECEIVED) {
@@ -170,10 +170,16 @@ int main (int argc, char **argv) {
FD_ZERO(&write_fds);
nfds = rexmpp_fds(&s, &read_fds, &write_fds);
tv.tv_sec = TIMEOUT;
- tv.tv_usec = 0;
- mtv = rexmpp_timeout(&s, (struct timeval*)&tv, (struct timeval*)&tv);
+ tv.tv_nsec = 0;
+ mtv = rexmpp_timeout(&s, &tv, &tv);
+ mtv_ms = NULL;
+ if (mtv != NULL) {
+ tv_ms.tv_sec = mtv->tv_sec;
+ tv_ms.tv_usec = mtv->tv_nsec / 1000;
+ mtv_ms = &tv_ms;
+ }
- n = select(nfds, &read_fds, &write_fds, NULL, mtv);
+ n = select(nfds, &read_fds, &write_fds, NULL, mtv_ms);
if (n == -1) {
printf("select error: %s\n", strerror(errno));
break;
diff --git a/tests/xml_parse_and_print.c b/tests/xml_parse_and_print.c
new file mode 100644
index 0000000..c76b06f
--- /dev/null
+++ b/tests/xml_parse_and_print.c
@@ -0,0 +1,29 @@
+#include <string.h>
+#include <stdlib.h>
+#include "rexmpp_xml.h"
+
+int main () {
+ int ret = 0;
+
+ char *str = "<foo bar=\"baz\">"
+ "<qux xmlns=\"urn:test\">a b c d</qux>"
+ "<quux e=\"f\" g=\"h\"/>"
+ "</foo>";
+ rexmpp_xml_t *xml = rexmpp_xml_parse (str, strlen(str));
+
+ printf("Input:\n%s\n\n", str);
+ if (xml == NULL) {
+ ret = -1;
+ } else {
+ char *str_new = rexmpp_xml_serialize (xml, 0);
+ if (str_new == NULL) {
+ ret = -2;
+ } else {
+ printf("Output:\n%s\n", str_new);
+ ret = strcmp(str, str_new);
+ free(str_new);
+ }
+ rexmpp_xml_free(xml);
+ }
+ return ret;
+}
diff --git a/tests/xml_print_and_parse.c b/tests/xml_print_and_parse.c
new file mode 100644
index 0000000..9bd7a3f
--- /dev/null
+++ b/tests/xml_print_and_parse.c
@@ -0,0 +1,64 @@
+#include <string.h>
+#include <stdlib.h>
+#include "rexmpp_xml.h"
+
+int main () {
+ int ret = 0;
+
+ rexmpp_xml_attr_t
+ foo_attributes = { .qname = {"bar", NULL},
+ .value = "baz",
+ .next = NULL },
+ quux_attributes_g = { .qname = {"g", NULL},
+ .value = "h",
+ .next = NULL },
+ quux_attributes =
+ { .qname = {"e", NULL},
+ .value = "f",
+ .next = &quux_attributes_g };
+ rexmpp_xml_t
+ quux = { .type = REXMPP_XML_ELEMENT,
+ .alt.elem =
+ { .qname = {"quux", NULL},
+ .attributes = &quux_attributes,
+ .children = NULL
+ },
+ .next = NULL
+ },
+ qux_text = { .type = REXMPP_XML_TEXT,
+ .alt.text = "a b c d",
+ .next = NULL },
+ qux = { .type = REXMPP_XML_ELEMENT,
+ .alt.elem =
+ { .qname = {"qux", "urn:dummy"},
+ .attributes = NULL,
+ .children = &qux_text
+ },
+ .next = &quux
+ },
+ xml =
+ { .type = REXMPP_XML_ELEMENT,
+ .alt.elem =
+ { .qname = {"foo", NULL},
+ .attributes = &foo_attributes,
+ .children = &qux
+ },
+ .next = NULL
+ };
+
+ char *str_new = rexmpp_xml_serialize (&xml, 0);
+ if (str_new == NULL) {
+ ret = -1;
+ } else {
+ rexmpp_xml_t *xml_new = rexmpp_xml_parse (str_new, strlen(str_new));
+ if (xml_new == NULL) {
+ ret = -2;
+ } else {
+ /* Compare the XML structures. */
+ ret = (rexmpp_xml_eq(&xml, xml_new) == 0);
+ rexmpp_xml_free(xml_new);
+ }
+ free(str_new);
+ }
+ return ret;
+}