From d20957a98d6c1ea0700a3162347d6567d29d780a Mon Sep 17 00:00:00 2001 From: Dmitry Podgorny Date: Sat, 15 Jul 2017 21:11:03 +0300 Subject: [PATCH] WIP XEP-0198: implement stream management Stream management is enabled when server supports it. There is no option to disable it. --- src/auth.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++----- src/common.h | 4 ++++ src/conn.c | 38 ++++++++++++++++++++++++++++++++++++++ strophe.h | 4 ++++ 4 files changed, 92 insertions(+), 5 deletions(-) diff --git a/src/auth.c b/src/auth.c index 50af8f0..dadd8c3 100644 --- a/src/auth.c +++ b/src/auth.c @@ -99,6 +99,9 @@ static int _handle_session(xmpp_conn_t * const conn, void * const userdata); static int _handle_missing_session(xmpp_conn_t * const conn, void * const userdata); +static int _handle_sm(xmpp_conn_t * const conn, + xmpp_stanza_t * const stanza, + void * const userdata); static int _handle_missing_handshake(xmpp_conn_t * const conn, void * const userdata); @@ -814,6 +817,12 @@ static int _handle_features_sasl(xmpp_conn_t * const conn, strcmp(ns, XMPP_NS_SESSION) == 0; } + opt = xmpp_stanza_get_child_by_ns(stanza, XMPP_NS_SM); + if (opt && strcmp(xmpp_stanza_get_name(opt), "sm") == 0) { + /* stream management supported */ + conn->sm_support = 1; + } + /* if bind is required, go ahead and start it */ if (conn->bind_required) { /* bind resource */ @@ -902,7 +911,7 @@ static int _handle_bind(xmpp_conn_t * const conn, void * const userdata) { const char *type; - xmpp_stanza_t *iq, *session; + xmpp_stanza_t *iq, *enable, *session, *binding, *jid_stanza; /* delete missing bind handler */ xmpp_timed_handler_delete(conn, _handle_missing_bind); @@ -913,12 +922,11 @@ static int _handle_bind(xmpp_conn_t * const conn, xmpp_error(conn->ctx, "xmpp", "Binding failed."); xmpp_disconnect(conn); } else if (type && strcmp(type, "result") == 0) { - xmpp_stanza_t *binding = xmpp_stanza_get_child_by_name(stanza, "bind"); + binding = xmpp_stanza_get_child_by_name(stanza, "bind"); xmpp_debug(conn->ctx, "xmpp", "Bind successful."); if (binding) { - xmpp_stanza_t *jid_stanza = xmpp_stanza_get_child_by_name(binding, - "jid"); + jid_stanza = xmpp_stanza_get_child_by_name(binding, "jid"); if (jid_stanza) { conn->bound_jid = xmpp_stanza_get_text(jid_stanza); } @@ -954,7 +962,23 @@ static int _handle_bind(xmpp_conn_t * const conn, /* send session establishment request */ xmpp_send(conn, iq); xmpp_stanza_release(iq); - } else { + } + + if (conn->sm_support) { + enable = xmpp_stanza_new(conn->ctx); + if (!enable) { + disconnect_mem_error(conn); + return 0; + } + xmpp_stanza_set_name(enable, "enable"); + xmpp_stanza_set_ns(enable, XMPP_NS_SM); + handler_add(conn, _handle_sm, XMPP_NS_SM, NULL, NULL, NULL); + xmpp_send(conn, enable); + xmpp_stanza_release(enable); + conn->sm_sent_nr = 0; + } + + if (!conn->session_required) { conn->authenticated = 1; /* call connection handler */ @@ -1023,6 +1047,23 @@ static int _handle_missing_legacy(xmpp_conn_t * const conn, return 0; } +static int _handle_sm(xmpp_conn_t * const conn, + xmpp_stanza_t * const stanza, + void * const userdata) +{ + const char *name; + + name = xmpp_stanza_get_name(stanza); + if (name && strcmp(name, "enabled") == 0) { + conn->sm_enabled = 1; + conn->sm_handled_nr = 0; + } else { + conn->sm_enabled = 0; + xmpp_warn(conn->ctx, "auth", "Stream management failed."); + } + return 0; +} + static int _handle_legacy(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) diff --git a/src/common.h b/src/common.h index 16d53de..b277fc1 100644 --- a/src/common.h +++ b/src/common.h @@ -178,6 +178,10 @@ struct _xmpp_conn_t { /* if server returns or we must do them */ int bind_required; int session_required; + int sm_support; + int sm_enabled; + uint32_t sm_handled_nr; + uint32_t sm_sent_nr; char *lang; char *domain; diff --git a/src/conn.c b/src/conn.c index 696fcff..5a6f2b0 100644 --- a/src/conn.c +++ b/src/conn.c @@ -62,6 +62,8 @@ static void _handle_stream_end(char *name, void * const userdata); static void _handle_stream_stanza(xmpp_stanza_t *stanza, void * const userdata); +static void _conn_sm_handle_stanza(xmpp_conn_t * const conn, + xmpp_stanza_t *stanza); static unsigned short _conn_default_port(xmpp_conn_t * const conn, xmpp_conn_type_t type); static void _conn_reset(xmpp_conn_t * const conn); @@ -145,6 +147,10 @@ xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t * const ctx) conn->bind_required = 0; conn->session_required = 0; + conn->sm_support = 0; + conn->sm_enabled = 0; + conn->sm_handled_nr = 0; + conn->sm_sent_nr = 0; conn->parser = parser_new(conn->ctx, _handle_stream_start, @@ -1216,6 +1222,36 @@ static void _handle_stream_stanza(xmpp_stanza_t *stanza, } handler_fire_stanza(conn, stanza); + if (conn->sm_enabled) + _conn_sm_handle_stanza(conn, stanza); +} + +/* XEP-0198 stream management */ +static void _conn_sm_handle_stanza(xmpp_conn_t * const conn, + xmpp_stanza_t *stanza) +{ + xmpp_stanza_t *a; + const char *name; + const char *ns; + char h[11]; + + ns = xmpp_stanza_get_ns(stanza); + if (ns && strcmp(ns, XMPP_NS_SM) != 0) + ++conn->sm_handled_nr; + else { + name = xmpp_stanza_get_name(stanza); + if (name && strcmp(name, "r") == 0) { + a = xmpp_stanza_new(conn->ctx); + if (!a) + return; /* XXX */ + xmpp_stanza_set_name(a, "a"); + xmpp_stanza_set_ns(a, XMPP_NS_SM); + xmpp_snprintf(h, sizeof(h), "%u", conn->sm_handled_nr); + xmpp_stanza_set_attribute(a, "h", h); + xmpp_send(conn, a); + xmpp_stanza_release(a); + } + } } static unsigned short _conn_default_port(xmpp_conn_t * const conn, @@ -1274,6 +1310,8 @@ static void _conn_reset(xmpp_conn_t * const conn) conn->error = 0; conn->tls_support = 0; + conn->sm_support = 0; + conn->sm_enabled = 0; conn->bind_required = 0; conn->session_required = 0; diff --git a/strophe.h b/strophe.h index a348f13..deb7a50 100644 --- a/strophe.h +++ b/strophe.h @@ -75,6 +75,10 @@ extern "C" { * Namespace definition for 'jabber:iq:register'. */ #define XMPP_NS_REGISTER "jabber:iq:register" +/** @def XMPP_NS_SM + * Namespace definition for Stream Management. + */ +#define XMPP_NS_SM "urn:xmpp:sm:3" /* error defines */ /** @def XMPP_EOK