From 57bc4bbb75fe19d7b286cfa469bdde99c4196bf0 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 55c61f9..21ff896 100644 --- a/src/auth.c +++ b/src/auth.c @@ -92,6 +92,9 @@ static int _handle_session(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata); static int _handle_missing_session(xmpp_conn_t *conn, void *userdata); static int _handle_missing_handshake(xmpp_conn_t *conn, void *userdata); +static int _handle_sm(xmpp_conn_t *const conn, + xmpp_stanza_t *const stanza, + void *const userdata); /* stream:error handler */ static int @@ -908,6 +911,12 @@ _handle_features_sasl(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) ns != NULL && 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 */ @@ -997,7 +1006,7 @@ static int _handle_bind(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) { const char *type; - xmpp_stanza_t *iq, *session; + xmpp_stanza_t *iq, *enable, *session, *binding, *jid_stanza; UNUSED(userdata); @@ -1010,12 +1019,11 @@ _handle_bind(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) strophe_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"); strophe_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); } @@ -1051,7 +1059,23 @@ _handle_bind(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) /* 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) { _auth_success(conn); } } else { @@ -1120,6 +1144,23 @@ static int _handle_missing_legacy(xmpp_conn_t *conn, void *userdata) 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; + strophe_warn(conn->ctx, "auth", "Stream management failed."); + } + return 0; +} + static int _handle_legacy(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) { diff --git a/src/common.h b/src/common.h index 2cdd42e..76d75e6 100644 --- a/src/common.h +++ b/src/common.h @@ -204,6 +204,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 0a88a1f..b8fbae2 100644 --- a/src/conn.c +++ b/src/conn.c @@ -94,6 +94,8 @@ static void _conn_attributes_destroy(xmpp_conn_t *conn, static void _handle_stream_start(char *name, char **attrs, void *userdata); static void _handle_stream_end(char *name, void *userdata); static void _handle_stream_stanza(xmpp_stanza_t *stanza, void *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 *conn, xmpp_conn_type_t type); static void _conn_reset(xmpp_conn_t *conn); @@ -189,6 +191,10 @@ xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t *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, _handle_stream_end, @@ -1486,6 +1492,36 @@ static void _handle_stream_stanza(xmpp_stanza_t *stanza, void *userdata) } 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); + strophe_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 *conn, @@ -1547,6 +1583,8 @@ static void _conn_reset(xmpp_conn_t *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 5e3839c..8b180db 100644 --- a/strophe.h +++ b/strophe.h @@ -79,6 +79,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