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