Skip to content

Commit 6b2910c

Browse files
committed
First version of XEP-0138 support
Signed-off-by: Steffen Jaeckel <[email protected]>
1 parent bfd0872 commit 6b2910c

File tree

9 files changed

+419
-65
lines changed

9 files changed

+419
-65
lines changed

Makefile.am

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ COVERAGE_POST=@COVERAGE_POST@
1010
PARSER_CFLAGS=@PARSER_CFLAGS@
1111
PARSER_LIBS=@PARSER_LIBS@
1212

13+
ZLIB_CFLAGS=@ZLIB_CFLAGS@
14+
ZLIB_LIBS=@ZLIB_LIBS@
15+
1316
if TLS_WITH_GNUTLS
1417
SSL_CFLAGS = @gnutls_CFLAGS@
1518
SSL_LIBS = @gnutls_LIBS@
@@ -32,8 +35,8 @@ STROPHE_LIBS = $(COVERAGE_PRE) libstrophe.la $(COVERAGE_POST) $(COVERAGE_LDFLAGS
3235
## Main build targets
3336
lib_LTLIBRARIES = libstrophe.la
3437

35-
libstrophe_la_CFLAGS = $(SSL_CFLAGS) $(STROPHE_FLAGS) $(PARSER_CFLAGS) $(RESOLV_CFLAGS) $(COVERAGE_CFLAGS)
36-
libstrophe_la_LDFLAGS = $(SSL_LIBS) $(PARSER_LIBS) $(RESOLV_LIBS) $(MINGW_LIBS) -no-undefined
38+
libstrophe_la_CFLAGS = $(SSL_CFLAGS) $(STROPHE_FLAGS) $(PARSER_CFLAGS) $(RESOLV_CFLAGS) $(COVERAGE_CFLAGS) $(ZLIB_CFLAGS)
39+
libstrophe_la_LDFLAGS = $(SSL_LIBS) $(PARSER_LIBS) $(RESOLV_LIBS) $(MINGW_LIBS) $(ZLIB_LIBS) -no-undefined
3740
# Export only public API
3841
libstrophe_la_LDFLAGS += -export-symbols-regex '^xmpp_' -version-info @VERSION_INFO@
3942

configure.ac

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ AC_ARG_ENABLE([cares],
5151
[AS_HELP_STRING([--enable-cares], [use c-ares for DNS resolution])])
5252
AC_ARG_ENABLE([getrandom],
5353
[AS_HELP_STRING([--disable-getrandom], [disable usage of the getrandom() system call])])
54+
AC_ARG_ENABLE([zlib],
55+
[AS_HELP_STRING([--disable-zlib], [disable compression support])])
5456

5557
AC_ARG_ENABLE([fuzzing],
5658
[AS_HELP_STRING([--enable-fuzzing], [turn on fuzzing test])],
@@ -247,6 +249,18 @@ fi
247249

248250
fi
249251

252+
if test "x$enable_zlib" != xno; then
253+
PKG_CHECK_MODULES([zlib], [zlib >= 1.2.0],
254+
[
255+
PC_REQUIRES="libzlib ${PC_REQUIRES}"
256+
ZLIB_CFLAGS=$zlib_CFLAGS
257+
ZLIB_LIBS=$zlib_LIBS
258+
AC_DEFINE([HAVE_ZLIB])
259+
],
260+
[AC_MSG_ERROR([zlib not found])])
261+
# TODO: if pkg-config doesn't find, check the library manually
262+
fi
263+
250264
m4_ifdef([PKG_INSTALLDIR], [PKG_INSTALLDIR],
251265
[AC_ARG_WITH([pkgconfigdir],
252266
[AS_HELP_STRING([--with-pkgconfigdir],
@@ -290,5 +304,7 @@ AC_SUBST(PARSER_LIBS)
290304
AC_SUBST(RESOLV_CFLAGS)
291305
AC_SUBST(RESOLV_LIBS)
292306
AC_SUBST(WARNING_FLAGS)
307+
AC_SUBST(ZLIB_CFLAGS)
308+
AC_SUBST(ZLIB_LIBS)
293309
AC_CONFIG_FILES([Makefile libstrophe.pc])
294310
AC_OUTPUT

examples/bot.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,10 @@ static void usage(int exit_code)
214214
" --legacy-ssl Use old style SSL.\n"
215215
" --legacy-auth Allow legacy authentication.\n"
216216
"Note: --disable-tls conflicts with --mandatory-tls or "
217-
"--legacy-ssl\n");
217+
"--legacy-ssl\n"
218+
" --zlib Enable compression via zlib.\n"
219+
" --dont-flush When using zlib, don't flush after "
220+
"compression.\n");
218221

219222
exit(exit_code);
220223
}
@@ -244,6 +247,10 @@ int main(int argc, char **argv)
244247
flags |= XMPP_CONN_FLAG_LEGACY_SSL;
245248
else if (strcmp(argv[i], "--legacy-auth") == 0)
246249
flags |= XMPP_CONN_FLAG_LEGACY_AUTH;
250+
else if (strcmp(argv[i], "--zlib") == 0)
251+
flags |= XMPP_CONN_FLAG_ENABLE_COMPRESSION;
252+
else if (strcmp(argv[i], "--dont-flush") == 0)
253+
flags |= XMPP_CONN_FLAG_COMPRESSION_DONT_FLUSH;
247254
else if ((strcmp(argv[i], "--jid") == 0) && (++i < argc))
248255
jid = argv[i];
249256
else if ((strcmp(argv[i], "--pass") == 0) && (++i < argc))

examples/complex.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,9 @@ static void usage(int exit_code)
239239
" --enable-certfail Enable certfail handler.\n"
240240
" --legacy-ssl Use old style SSL.\n"
241241
" --legacy-auth Allow legacy authentication.\n"
242+
" --zlib Enable compression via zlib.\n"
243+
" --dont-flush When using zlib, don't flush after "
244+
"compression.\n"
242245
" --verbose Increase the verbosity level.\n"
243246
" --tcp-keepalive Configure TCP keepalive.\n\n"
244247
"Note: --disable-tls conflicts with --mandatory-tls or "
@@ -273,6 +276,10 @@ int main(int argc, char **argv)
273276
flags |= XMPP_CONN_FLAG_LEGACY_SSL;
274277
else if (strcmp(argv[i], "--legacy-auth") == 0)
275278
flags |= XMPP_CONN_FLAG_LEGACY_AUTH;
279+
else if (strcmp(argv[i], "--zlib") == 0)
280+
flags |= XMPP_CONN_FLAG_ENABLE_COMPRESSION;
281+
else if (strcmp(argv[i], "--dont-flush") == 0)
282+
flags |= XMPP_CONN_FLAG_COMPRESSION_DONT_FLUSH;
276283
else if (strcmp(argv[i], "--verbose") == 0)
277284
verbosity++;
278285
else if (strcmp(argv[i], "--tcp-keepalive") == 0)

src/auth.c

Lines changed: 127 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262

6363
static void _auth(xmpp_conn_t *conn);
6464
static void _auth_legacy(xmpp_conn_t *conn);
65+
static void _handle_open_compress(xmpp_conn_t *conn);
6566
static void _handle_open_sasl(xmpp_conn_t *conn);
6667
static void _handle_open_tls(xmpp_conn_t *conn);
6768

@@ -72,6 +73,9 @@ static int _handle_component_hs_response(xmpp_conn_t *conn,
7273

7374
static int
7475
_handle_features_sasl(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata);
76+
static int _handle_features_compress(xmpp_conn_t *conn,
77+
xmpp_stanza_t *stanza,
78+
void *userdata);
7579
static int
7680
_handle_sasl_result(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata);
7781
static int _handle_digestmd5_challenge(xmpp_conn_t *conn,
@@ -207,10 +211,61 @@ static int _handle_missing_features(xmpp_conn_t *conn, void *userdata)
207211
return 0;
208212
}
209213

214+
typedef void (*text_handler)(xmpp_conn_t *conn, const char *text);
215+
static void _foreach_child(xmpp_conn_t *conn,
216+
xmpp_stanza_t *parent,
217+
const char *name,
218+
text_handler hndl)
219+
{
220+
xmpp_stanza_t *children;
221+
for (children = xmpp_stanza_get_children(parent); children;
222+
children = xmpp_stanza_get_next(children)) {
223+
const char *child_name = xmpp_stanza_get_name(children);
224+
if (child_name && strcmp(child_name, name) == 0) {
225+
char *text = xmpp_stanza_get_text(children);
226+
if (text == NULL)
227+
continue;
228+
229+
hndl(conn, text);
230+
231+
strophe_free(conn->ctx, text);
232+
}
233+
}
234+
}
235+
236+
static void _handle_sasl_children(xmpp_conn_t *conn, const char *text)
237+
{
238+
if (strcasecmp(text, "PLAIN") == 0) {
239+
conn->sasl_support |= SASL_MASK_PLAIN;
240+
} else if (strcasecmp(text, "EXTERNAL") == 0 &&
241+
(conn->tls_client_cert || conn->tls_client_key)) {
242+
conn->sasl_support |= SASL_MASK_EXTERNAL;
243+
} else if (strcasecmp(text, "DIGEST-MD5") == 0) {
244+
conn->sasl_support |= SASL_MASK_DIGESTMD5;
245+
} else if (strcasecmp(text, "ANONYMOUS") == 0) {
246+
conn->sasl_support |= SASL_MASK_ANONYMOUS;
247+
} else {
248+
size_t n;
249+
for (n = 0; n < scram_algs_num; ++n) {
250+
if (strcasecmp(text, scram_algs[n]->scram_name) == 0) {
251+
conn->sasl_support |= scram_algs[n]->mask;
252+
break;
253+
}
254+
}
255+
}
256+
}
257+
258+
static void _handle_compression_children(xmpp_conn_t *conn, const char *text)
259+
{
260+
if (strcasecmp(text, "zlib") == 0) {
261+
conn->compression_supported = 1;
262+
}
263+
}
264+
210265
static int
211266
_handle_features(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
212267
{
213-
xmpp_stanza_t *child, *mech;
268+
xmpp_stanza_t *child, *children;
214269
const char *ns;
215270
char *text;
216271

@@ -222,56 +277,33 @@ _handle_features(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
222277
/* check for TLS */
223278
if (!conn->secured) {
224279
if (!conn->tls_disabled) {
225-
child = xmpp_stanza_get_child_by_name(stanza, "starttls");
226-
if (child) {
227-
ns = xmpp_stanza_get_ns(child);
228-
conn->tls_support = ns != NULL && strcmp(ns, XMPP_NS_TLS) == 0;
280+
if (xmpp_stanza_get_child_by_name_and_ns(stanza, "starttls",
281+
XMPP_NS_TLS)) {
282+
conn->tls_support = 1;
229283
}
230284
} else {
231285
conn->tls_support = 0;
232286
}
233287
}
234288

235289
/* check for SASL */
236-
child = xmpp_stanza_get_child_by_name(stanza, "mechanisms");
237-
ns = child ? xmpp_stanza_get_ns(child) : NULL;
238-
if (child && ns && strcmp(ns, XMPP_NS_SASL) == 0) {
239-
for (mech = xmpp_stanza_get_children(child); mech;
240-
mech = xmpp_stanza_get_next(mech)) {
241-
if (xmpp_stanza_get_name(mech) &&
242-
strcmp(xmpp_stanza_get_name(mech), "mechanism") == 0) {
243-
text = xmpp_stanza_get_text(mech);
244-
if (text == NULL)
245-
continue;
246-
247-
if (strcasecmp(text, "PLAIN") == 0) {
248-
conn->sasl_support |= SASL_MASK_PLAIN;
249-
} else if (strcasecmp(text, "EXTERNAL") == 0 &&
250-
(conn->tls_client_cert || conn->tls_client_key)) {
251-
conn->sasl_support |= SASL_MASK_EXTERNAL;
252-
} else if (strcasecmp(text, "DIGEST-MD5") == 0) {
253-
conn->sasl_support |= SASL_MASK_DIGESTMD5;
254-
} else if (strcasecmp(text, "ANONYMOUS") == 0) {
255-
conn->sasl_support |= SASL_MASK_ANONYMOUS;
256-
} else {
257-
size_t n;
258-
for (n = 0; n < scram_algs_num; ++n) {
259-
if (strcasecmp(text, scram_algs[n]->scram_name) == 0) {
260-
conn->sasl_support |= scram_algs[n]->mask;
261-
break;
262-
}
263-
}
264-
}
265-
266-
strophe_free(conn->ctx, text);
267-
}
268-
}
290+
child = xmpp_stanza_get_child_by_name_and_ns(stanza, "mechanisms",
291+
XMPP_NS_SASL);
292+
if (child) {
293+
_foreach_child(conn, child, "mechanism", _handle_sasl_children);
269294
}
270295

271296
/* Disable PLAIN when other secure mechanisms are supported */
272297
if (conn->sasl_support & ~(SASL_MASK_PLAIN | SASL_MASK_ANONYMOUS))
273298
conn->sasl_support &= ~SASL_MASK_PLAIN;
274299

300+
/* check for compression */
301+
child = xmpp_stanza_get_child_by_name_and_ns(stanza, "compression",
302+
XMPP_NS_COMPRESSION);
303+
if (conn->compression_allowed && child) {
304+
_foreach_child(conn, child, "method", _handle_compression_children);
305+
}
306+
275307
_auth(conn);
276308

277309
return 0;
@@ -339,7 +371,9 @@ _handle_sasl_result(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
339371
(char *)userdata);
340372

341373
/* reset parser */
342-
conn_prepare_reset(conn, _handle_open_sasl);
374+
conn_prepare_reset(conn, conn->compression_allowed
375+
? _handle_open_compress
376+
: _handle_open_sasl);
343377

344378
/* send stream tag */
345379
conn_open_stream(conn);
@@ -950,6 +984,17 @@ static void _handle_open_sasl(xmpp_conn_t *conn)
950984
NULL);
951985
}
952986

987+
/* called when stream:stream tag received after compression has been enabled */
988+
static void _handle_open_compress(xmpp_conn_t *conn)
989+
{
990+
strophe_debug(conn->ctx, "xmpp", "Reopened stream successfully.");
991+
992+
/* setup stream:features handlers */
993+
handler_add(conn, _handle_features_compress, XMPP_NS_STREAMS, "features",
994+
NULL, NULL);
995+
handler_add_timed(conn, _handle_missing_features, FEATURES_TIMEOUT, NULL);
996+
}
997+
953998
static int _do_bind(xmpp_conn_t *conn, xmpp_stanza_t *bind)
954999
{
9551000
xmpp_stanza_t *iq, *res, *text;
@@ -1006,6 +1051,49 @@ static int _do_bind(xmpp_conn_t *conn, xmpp_stanza_t *bind)
10061051
return 0;
10071052
}
10081053

1054+
static int _handle_compress_result(xmpp_conn_t *const conn,
1055+
xmpp_stanza_t *const stanza,
1056+
void *const userdata)
1057+
{
1058+
const char *name = xmpp_stanza_get_name(stanza);
1059+
1060+
if (!name)
1061+
return 0;
1062+
if (strcmp(name, "compressed") == 0) {
1063+
/* Stream compression enabled, we need to restart the stream */
1064+
strophe_debug(conn->ctx, "xmpp", "Stream compression enabled");
1065+
1066+
/* reset parser */
1067+
conn_prepare_reset(conn, _handle_open_sasl);
1068+
1069+
/* make compression effective */
1070+
conn->compress = 1;
1071+
1072+
/* send stream tag */
1073+
conn_open_stream(conn);
1074+
}
1075+
return 0;
1076+
}
1077+
1078+
static int _handle_features_compress(xmpp_conn_t *conn,
1079+
xmpp_stanza_t *stanza,
1080+
void *userdata)
1081+
{
1082+
const char *compress = "<compress xmlns='" XMPP_NS_COMPRESSION
1083+
"'><method>zlib</method></compress>";
1084+
1085+
UNUSED(userdata);
1086+
1087+
/* remove missing features handler */
1088+
xmpp_timed_handler_delete(conn, _handle_missing_features);
1089+
1090+
send_raw(conn, compress, strlen(compress), XMPP_QUEUE_STROPHE, NULL);
1091+
handler_add(conn, _handle_compress_result, XMPP_NS_COMPRESSION, NULL, NULL,
1092+
NULL);
1093+
1094+
return 0;
1095+
}
1096+
10091097
static int
10101098
_handle_features_sasl(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
10111099
{

src/common.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include <stdio.h>
2121
#include <stdarg.h>
22+
#include <zlib.h>
2223

2324
#include "strophe.h"
2425
#include "ostypes.h"
@@ -249,6 +250,13 @@ struct _xmpp_conn_t {
249250
int sm_disable;
250251
xmpp_sm_state_t *sm_state;
251252

253+
int compression_allowed, compression_supported;
254+
int compress, compression_dont_flush;
255+
struct zlib_compression {
256+
void *buffer, *buffer_end;
257+
z_stream stream;
258+
} compression, decompression;
259+
252260
char *lang;
253261
char *domain;
254262
char *jid;

0 commit comments

Comments
 (0)