diff --git a/.gitignore b/.gitignore index f48fb82..1292f81 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ examples/bot examples/component examples/roster examples/uuid +examples/vcard test_stamp test-suite.log tests/*.o diff --git a/.travis.yml b/.travis.yml index 7260420..f5136e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,10 +2,14 @@ language: c install: - sudo apt-get update - sudo apt-get -y install libtool pkg-config libexpat1-dev libxml2-dev libssl-dev check -script: +before_script: - ./bootstrap.sh - - ./configure --without-libxml2 && make CFLAGS="-Wall -Werror" && make CFLAGS="-Wall -Werror" check-TESTS - - make clean - - ./configure --with-libxml2 && make CFLAGS="-Wall -Werror" && make CFLAGS="-Wall -Werror" check-TESTS - - make clean - - ./configure --disable-tls && make CFLAGS="-Wall -Werror" && make CFLAGS="-Wall -Werror" check-TESTS +script: + - ./configure ${CONFIGURE_OPT} && make && make check-TESTS +env: + - CONFIGURE_OPT="--without-libxml2" + - CONFIGURE_OPT="--with-libxml2" + - CONFIGURE_OPT="--disable-tls --without-libxml2" + - CONFIGURE_OPT="--disable-tls --with-libxml2" +matrix: + fast_finish: true diff --git a/ChangeLog b/ChangeLog index 35d7f09..13776dc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +0.9.2 + - OpenSSL tls module verifies certificate by default. Set flag + XMPP_CONN_FLAG_TRUST_TLS to ignore result of the verification + - Certificate hostname verification is forced for openssl-1.0.2 and + newer + - OpenSSL tls module disables insecure SSLv2 SSLv3 and TLSv1 + - Support of handlers with the same callback function, but different + userdata + - System handlers are deleted on xmpp_conn_t reconnection. Old system + handlers could cause problems + - Default timeout for xmpp_run() is increased from 1 millisecond to 1 + second in order to reduce CPU consumption + - Reduced memory usage in expat module + - New functions: + - xmpp_ctx_set_timeout() + - xmpp_sha1_digest() + 0.9.1 - Fixed bug #95 (DNS lookup failing on Cygwin) - Removed dependency on the check package diff --git a/Makefile.am b/Makefile.am index 0301bcc..1fe1a2a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -11,7 +11,7 @@ SSL_LIBS = @openssl_LIBS@ RESOLV_LIBS = @RESOLV_LIBS@ -MESODE_FLAGS = -I$(top_srcdir) +MESODE_FLAGS = -I$(top_srcdir) -Wall -Wextra -Wno-unused-parameter MESODE_LIBS = libmesode.la ## Main build targets @@ -21,14 +21,42 @@ libmesode_la_CFLAGS = $(SSL_CFLAGS) $(MESODE_FLAGS) $(PARSER_CFLAGS) libmesode_la_LDFLAGS = $(SSL_LIBS) $(PARSER_LIBS) $(RESOLV_LIBS) -no-undefined # Export only public API libmesode_la_LDFLAGS += -export-symbols-regex '^xmpp_' -libmesode_la_SOURCES = src/auth.c src/conn.c src/crypto.c src/ctx.c \ - src/event.c src/handler.c src/hash.c src/jid.c src/md5.c \ - src/resolver.c src/sasl.c src/scram.c src/sha1.c \ - src/snprintf.c src/sock.c src/stanza.c src/util.c \ - src/rand.c src/uuid.c \ - src/common.h src/hash.h src/md5.h src/ostypes.h src/parser.h \ - src/resolver.h src/sasl.h src/scram.h src/sha1.h src/snprintf.h \ - src/sock.h src/tls.h src/util.h src/rand.h + +libmesode_la_SOURCES = \ + src/auth.c \ + src/conn.c \ + src/crypto.c \ + src/ctx.c \ + src/event.c \ + src/handler.c \ + src/hash.c \ + src/jid.c \ + src/md5.c \ + src/rand.c \ + src/resolver.c \ + src/sasl.c \ + src/scram.c \ + src/sha1.c \ + src/snprintf.c \ + src/sock.c \ + src/stanza.c \ + src/util.c \ + src/uuid.c +libmesode_la_SOURCES += \ + src/common.h \ + src/hash.h \ + src/md5.h \ + src/ostypes.h \ + src/parser.h \ + src/rand.h \ + src/resolver.h \ + src/sasl.h \ + src/scram.h \ + src/sha1.h \ + src/snprintf.h \ + src/sock.h \ + src/tls.h \ + src/util.h if DISABLE_TLS libmesode_la_SOURCES += src/tls_dummy.c @@ -42,19 +70,32 @@ include_HEADERS = mesode.h pkgconfig_DATA = libmesode.pc -EXTRA_DIST = docs rpm Doxyfile LICENSE.txt GPL-LICENSE.txt MIT-LICENSE.txt \ - src/tls_dummy.c src/tls_gnutls.c src/tls_schannel.c \ - examples/README.md +EXTRA_DIST = \ + Doxyfile \ + GPL-LICENSE.txt \ + LICENSE.txt \ + MIT-LICENSE.txt \ + bootstrap.sh \ + build-android.sh \ + docs/footer.html \ + examples/README.md \ + jni/Android.mk \ + jni/Application.mk \ + tests/res_query_dump.c ## Examples -noinst_PROGRAMS = examples/active examples/roster examples/basic examples/bot \ - examples/component examples/uuid +noinst_PROGRAMS = \ + examples/active \ + examples/basic \ + examples/bot \ + examples/component \ + examples/roster \ + examples/uuid \ + examples/vcard + examples_active_SOURCES = examples/active.c examples_active_CFLAGS = $(MESODE_FLAGS) examples_active_LDADD = $(MESODE_LIBS) -examples_roster_SOURCES = examples/roster.c -examples_roster_CFLAGS = $(MESODE_FLAGS) -examples_roster_LDADD = $(MESODE_LIBS) examples_basic_SOURCES = examples/basic.c examples_basic_CFLAGS = $(MESODE_FLAGS) examples_basic_LDADD = $(MESODE_LIBS) @@ -64,14 +105,31 @@ examples_bot_LDADD = $(MESODE_LIBS) examples_component_SOURCES = examples/component.c examples_component_CFLAGS = $(MESODE_FLAGS) examples_component_LDADD = $(MESODE_LIBS) +examples_roster_SOURCES = examples/roster.c +examples_roster_CFLAGS = $(MESODE_FLAGS) +examples_roster_LDADD = $(MESODE_LIBS) examples_uuid_SOURCES = examples/uuid.c examples_uuid_CFLAGS = $(MESODE_FLAGS) examples_uuid_LDADD = $(MESODE_LIBS) +examples_vcard_SOURCES = examples/vcard.c +examples_vcard_CFLAGS = $(MESODE_FLAGS) +examples_vcard_LDADD = $(MESODE_LIBS) ## Tests -TESTS = tests/check_parser tests/test_sha1 tests/test_md5 tests/test_rand \ - tests/test_scram tests/test_ctx tests/test_base64 tests/test_jid \ - tests/test_snprintf tests/test_string tests/test_resolver +TESTS = \ + tests/check_parser \ + tests/test_sha1 \ + tests/test_md5 \ + tests/test_rand \ + tests/test_scram \ + tests/test_ctx \ + tests/test_base64 \ + tests/test_hash \ + tests/test_jid \ + tests/test_snprintf \ + tests/test_string \ + tests/test_resolver + check_PROGRAMS = $(TESTS) tests_check_parser_SOURCES = tests/check_parser.c tests/test.h @@ -90,6 +148,11 @@ tests_test_base64_CFLAGS = $(MESODE_FLAGS) -I$(top_srcdir)/src tests_test_base64_LDADD = $(MESODE_LIBS) tests_test_base64_LDFLAGS = -static +tests_test_hash_SOURCES = tests/test_hash.c +tests_test_hash_CFLAGS = $(MESODE_FLAGS) -I$(top_srcdir)/src +tests_test_hash_LDADD = $(MESODE_LIBS) +tests_test_hash_LDFLAGS = -static + tests_test_jid_SOURCES = tests/test_jid.c tests_test_jid_CFLAGS = $(MESODE_FLAGS) -I$(top_srcdir)/src tests_test_jid_LDADD = $(MESODE_LIBS) diff --git a/configure.ac b/configure.ac index 6d19f2e..5216074 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([libmesode], [0.9.1], [boothj5web@gmail.com]) +AC_INIT([libmesode], [0.9.2], [boothj5web@gmail.com]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([foreign]) LT_INIT([dlopen]) diff --git a/examples/active.c b/examples/active.c index d971612..93bd236 100644 --- a/examples/active.c +++ b/examples/active.c @@ -55,10 +55,7 @@ void conn_handler(xmpp_conn_t * const conn, const xmpp_conn_event_t status, fprintf(stderr, "DEBUG: connected\n"); /* create iq stanza for request */ - iq = xmpp_stanza_new(ctx); - xmpp_stanza_set_name(iq, "iq"); - xmpp_stanza_set_type(iq, "get"); - xmpp_stanza_set_id(iq, "active1"); + iq = xmpp_iq_new(ctx, "get", "active1"); xmpp_stanza_set_to(iq, "xxxxxxxxx.com"); query = xmpp_stanza_new(ctx); diff --git a/examples/bot.c b/examples/bot.c index f4c2e43..3cc56b9 100644 --- a/examples/bot.c +++ b/examples/bot.c @@ -73,15 +73,19 @@ int version_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void int message_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) { xmpp_ctx_t *ctx = (xmpp_ctx_t*)userdata; - xmpp_stanza_t *reply; + xmpp_stanza_t *body, *reply; + const char *type; char *intext, *replytext; + int quit = 0; - if (!xmpp_stanza_get_child_by_name(stanza, "body")) + body = xmpp_stanza_get_child_by_name(stanza, "body"); + if (body == NULL) return 1; - if (xmpp_stanza_get_type(stanza) != NULL && !strcmp(xmpp_stanza_get_type(stanza), "error")) + type = xmpp_stanza_get_type(stanza); + if (type != NULL && strcmp(type, "error") == 0) return 1; - intext = xmpp_stanza_get_text(xmpp_stanza_get_child_by_name(stanza, "body")); + intext = xmpp_stanza_get_text(body); printf("Incoming message from %s: %s\n", xmpp_stanza_get_from(stanza), intext); @@ -89,15 +93,24 @@ int message_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void if (xmpp_stanza_get_type(reply) == NULL) xmpp_stanza_set_type(reply, "chat"); - replytext = (char *) malloc(strlen(" to you too!") + strlen(intext) + 1); - strcpy(replytext, intext); - strcat(replytext, " to you too!"); + if (strcmp(intext, "quit") == 0) { + replytext = strdup("bye!"); + quit = 1; + } else { + replytext = (char *) malloc(strlen(" to you too!") + strlen(intext) + 1); + strcpy(replytext, intext); + strcat(replytext, " to you too!"); + } xmpp_free(ctx, intext); xmpp_message_set_body(reply, replytext); xmpp_send(conn, reply); xmpp_stanza_release(reply); free(replytext); + + if (quit) + xmpp_disconnect(conn); + return 1; } diff --git a/examples/roster.c b/examples/roster.c index 3be5914..b0c4d9b 100644 --- a/examples/roster.c +++ b/examples/roster.c @@ -62,10 +62,7 @@ void conn_handler(xmpp_conn_t * const conn, const xmpp_conn_event_t status, fprintf(stderr, "DEBUG: connected\n"); /* create iq stanza for request */ - iq = xmpp_stanza_new(ctx); - xmpp_stanza_set_name(iq, "iq"); - xmpp_stanza_set_type(iq, "get"); - xmpp_stanza_set_id(iq, "roster1"); + iq = xmpp_iq_new(ctx, "get", "roster1"); query = xmpp_stanza_new(ctx); xmpp_stanza_set_name(query, "query"); diff --git a/examples/vcard.c b/examples/vcard.c new file mode 100644 index 0000000..5832be7 --- /dev/null +++ b/examples/vcard.c @@ -0,0 +1,284 @@ +/* vcard.c + * strophe XMPP client library -- vCard example + * + * Copyright (C) 2016 Dmitry Podgorny + * + * This software is provided AS-IS with no warranty, either express + * or implied. + * + * This program is dual licensed under the MIT and GPLv3 licenses. + */ + +#include +#include /* basename */ +#include +#include +#include +#include + +typedef struct { + xmpp_ctx_t *ctx; + const char *recipient; + const char *img_path; +} vcard_t; + +typedef void (*vcard_cb_t)(vcard_t *, xmpp_stanza_t *); + +#define REQ_TIMEOUT 5000 +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +static void vcard_photo(vcard_t *vc, xmpp_stanza_t *stanza) +{ + xmpp_stanza_t *tmp; + char *s; + char *tok; + char *saveptr = NULL; + char *copy; + unsigned char *img; + size_t img_size; + size_t written; + FILE *fd; + + tmp = xmpp_stanza_get_child_by_name(stanza, "TYPE"); + assert(tmp != NULL); + s = xmpp_stanza_get_text(tmp); + assert(s != NULL); + printf("PHOTO: %s, saving to file %s\n", s, vc->img_path); + xmpp_free(vc->ctx, s); + + tmp = xmpp_stanza_get_child_by_name(stanza, "BINVAL"); + assert(tmp != NULL); + s = xmpp_stanza_get_text(tmp); + assert(s != NULL); + + /* remove \n and \r */ + copy = (char *)malloc(strlen(s) + 1); + assert(copy != NULL); + copy[0] = '\0'; + tok = strtok_r(s, "\n\r", &saveptr); + while (tok != NULL) { + strcat(copy, tok); + tok = strtok_r(NULL, "\n\r", &saveptr); + } + + xmpp_base64_decode_bin(vc->ctx, copy, strlen(copy), &img, &img_size); + assert(img != NULL); + + fd = fopen(vc->img_path, "w"); + assert(fd != NULL); + written = fwrite(img, 1, img_size, fd); + if (written < img_size) + printf("Saving photo failed\n"); + fclose(fd); + + free(copy); + xmpp_free(vc->ctx, s); + xmpp_free(vc->ctx, img); +} + +static void vcard_print_string(vcard_t *vc, xmpp_stanza_t *stanza, + const char *info) +{ + char *s = xmpp_stanza_get_text(stanza); + + assert(s != NULL); + printf("%s: %s\n", info, s); + xmpp_free(vc->ctx, s); +} + +static void vcard_bday(vcard_t *vc, xmpp_stanza_t *stanza) +{ + vcard_print_string(vc, stanza, "Birthday"); +} + +static void vcard_desc(vcard_t *vc, xmpp_stanza_t *stanza) +{ + vcard_print_string(vc, stanza, "Description"); +} + +static void vcard_email(vcard_t *vc, xmpp_stanza_t *stanza) +{ + xmpp_stanza_t *userid = xmpp_stanza_get_child_by_name(stanza, "USERID"); + + if (userid != NULL) + vcard_print_string(vc, userid, "E-mail"); +} + +static void vcard_fn(vcard_t *vc, xmpp_stanza_t *stanza) +{ + vcard_print_string(vc, stanza, "Full name"); +} + +static void vcard_name(vcard_t *vc, xmpp_stanza_t *stanza) +{ + xmpp_stanza_t *name = xmpp_stanza_get_child_by_name(stanza, "GIVEN"); + xmpp_stanza_t *family = xmpp_stanza_get_child_by_name(stanza, "FAMILY"); + + if (name != NULL) + vcard_print_string(vc, name, "Given name"); + if (family != NULL) + vcard_print_string(vc, family, "Family name"); +} + +static void vcard_nick(vcard_t *vc, xmpp_stanza_t *stanza) +{ + vcard_print_string(vc, stanza, "Nickname"); +} + +static void vcard_url(vcard_t *vc, xmpp_stanza_t *stanza) +{ + vcard_print_string(vc, stanza, "URL"); +} + +static vcard_cb_t vcard_cb_get(xmpp_stanza_t *stanza) +{ + vcard_cb_t cb = NULL; + const char *tag; + size_t i; + + static struct { + const char *tag; + vcard_cb_t cb; + } vcard_tbl[] = { + { "PHOTO", vcard_photo }, + { "BDAY", vcard_bday }, + { "DESC", vcard_desc }, + { "EMAIL", vcard_email }, + { "FN", vcard_fn }, + { "N", vcard_name }, + { "NICKNAME", vcard_nick }, + { "URL", vcard_url }, + }; + + tag = xmpp_stanza_get_name(stanza); + if (tag == NULL) + goto exit; + + for (i = 0; i < ARRAY_SIZE(vcard_tbl); ++i) { + if (strcmp(tag, vcard_tbl[i].tag) == 0) { + cb = vcard_tbl[i].cb; + break; + } + } + +exit: + return cb; +} + +static int timedout(xmpp_conn_t * const conn, void * const userdata) +{ + fprintf(stderr, "Timeout reached.\n"); + xmpp_disconnect(conn); + + return 0; +} + +static int recv_vcard(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, + void * const userdata) +{ + vcard_t *vc = userdata; + vcard_cb_t cb; + xmpp_stanza_t *child; + char *s; + size_t s_size; + int rc; + + printf("Received vCard.\n\n"); + + child = xmpp_stanza_get_child_by_name(stanza, "error"); + if (child != NULL) { + rc = xmpp_stanza_to_text(child, &s, &s_size); + assert(rc == XMPP_EOK); + printf("Error returned: %s.\n", s); + xmpp_free(vc->ctx, s); + goto exit; + } + + child = xmpp_stanza_get_child_by_name(stanza, "vCard"); + assert(child != NULL); + child = xmpp_stanza_get_children(child); + while (child != NULL) { + cb = vcard_cb_get(child); + if (cb != NULL) + cb(vc, child); + child = xmpp_stanza_get_next(child); + } + +exit: + xmpp_disconnect(conn); + + return 0; +} + +static void send_vcard_req(xmpp_conn_t *conn, const char *to, const char *id) +{ + printf("Requesting vCard from %s.\n", to); + xmpp_send_raw_string(conn, "" + "", + xmpp_conn_get_bound_jid(conn), to, id); +} + +static void conn_handler(xmpp_conn_t * const conn, + const xmpp_conn_event_t status, + const int error, + xmpp_stream_error_t * const stream_error, + void * const userdata) +{ + vcard_t *vc = userdata; + + if (status == XMPP_CONN_CONNECT) { + send_vcard_req(conn, vc->recipient, "vc1"); + xmpp_id_handler_add(conn, recv_vcard, "vc1", vc); + xmpp_timed_handler_add(conn, timedout, REQ_TIMEOUT, NULL); + } else { + if (error != 0) + fprintf(stderr, "Disconnected with error=%d.\n", error); + if (stream_error != NULL) + fprintf(stderr, "Stream error type=%d text=%s.\n", + stream_error->type, stream_error->text); + xmpp_stop(vc->ctx); + } +} + +int main(int argc, char **argv) +{ + xmpp_log_t *log; + xmpp_ctx_t *ctx; + xmpp_conn_t *conn; + const char *jid; + const char *pass; + char *prog; + vcard_t vcard; + + if (argc < 4 || argc > 5) { + prog = argc > 0 ? strdup(argv[0]) : NULL; + printf("Usage: %s " + "[image-file]\n\n", prog == NULL ? "vcard" : basename(prog)); + printf("If vCard contains a photo it will be stored to " + "image-file. If you don't provide the image-file " + "default filename will be generated.\n"); + free(prog); + return 1; + } + + jid = argv[1]; + pass = argv[2]; + vcard.recipient = argv[3]; + vcard.img_path = argc > 4 ? argv[4] : "vcard.jpg"; + + xmpp_initialize(); + log = xmpp_get_default_logger(XMPP_LEVEL_INFO); + ctx = xmpp_ctx_new(NULL, log); + conn = xmpp_conn_new(ctx); + xmpp_conn_set_jid(conn, jid); + xmpp_conn_set_pass(conn, pass); + vcard.ctx = ctx; + xmpp_connect_client(conn, NULL, 0, NULL, conn_handler, &vcard); + xmpp_run(ctx); + xmpp_conn_release(conn); + xmpp_ctx_free(ctx); + xmpp_shutdown(); + + return 0; +} + diff --git a/mesode.h b/mesode.h index e35bfa6..ce202ca 100644 --- a/mesode.h +++ b/mesode.h @@ -167,6 +167,10 @@ typedef struct _xmpp_stanza_t xmpp_stanza_t; #define XMPP_CONN_FLAG_DISABLE_TLS (1UL << 0) #define XMPP_CONN_FLAG_MANDATORY_TLS (1UL << 1) #define XMPP_CONN_FLAG_LEGACY_SSL (1UL << 2) +/** @def XMPP_CONN_FLAG_TRUST_TLS + * Trust server's certificate even if it is invalid. + */ +#define XMPP_CONN_FLAG_TRUST_TLS (1UL << 3) /* connect callback */ typedef enum { @@ -216,7 +220,7 @@ typedef void (*xmpp_conn_handler)(xmpp_conn_t * const conn, void * const userdata); typedef int (*xmpp_certfail_handler)(xmpp_tlscert_t *cert, const char * const errormsg); - +void xmpp_send_error(xmpp_conn_t * const conn, xmpp_error_type_t const type, char * const text); xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t * const ctx); xmpp_conn_t *xmpp_conn_clone(xmpp_conn_t * const conn); int xmpp_conn_release(xmpp_conn_t * const conn); @@ -396,6 +400,8 @@ int xmpp_message_set_body(xmpp_stanza_t *msg, const char * const text); xmpp_stanza_t *xmpp_iq_new(xmpp_ctx_t *ctx, const char * const type, const char * const id); xmpp_stanza_t *xmpp_presence_new(xmpp_ctx_t *ctx); +xmpp_stanza_t *xmpp_error_new(xmpp_ctx_t *ctx, xmpp_error_type_t const type, + const char * const text); /* jid */ @@ -413,6 +419,7 @@ char *xmpp_jid_resource(xmpp_ctx_t *ctx, const char *jid); void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout); void xmpp_run(xmpp_ctx_t *ctx); void xmpp_stop(xmpp_ctx_t *ctx); +void xmpp_ctx_set_timeout(xmpp_ctx_t * const ctx, const unsigned long timeout); /* UUID */ @@ -428,6 +435,8 @@ char *xmpp_uuid_gen(xmpp_ctx_t *ctx); typedef struct _xmpp_sha1_t xmpp_sha1_t; char *xmpp_sha1(xmpp_ctx_t *ctx, const unsigned char *data, size_t len); +void xmpp_sha1_digest(const unsigned char *data, size_t len, + unsigned char *digest); xmpp_sha1_t *xmpp_sha1_new(xmpp_ctx_t *ctx); void xmpp_sha1_free(xmpp_sha1_t *sha1); diff --git a/src/auth.c b/src/auth.c index 7e9fa22..64e35d6 100644 --- a/src/auth.c +++ b/src/auth.c @@ -62,6 +62,7 @@ static void _auth(xmpp_conn_t * const conn); static void _handle_open_sasl(xmpp_conn_t * const conn); +static void _handle_open_tls(xmpp_conn_t * const conn); static int _handle_component_auth(xmpp_conn_t * const conn); static int _handle_component_hs_response(xmpp_conn_t * const conn, @@ -288,7 +289,7 @@ static int _handle_proceedtls_default(xmpp_conn_t * const conn, xmpp_debug(conn->ctx, "xmpp", "proceeding with TLS"); if (conn_tls_start(conn) == 0) { - conn_prepare_reset(conn, auth_handle_open); + conn_prepare_reset(conn, _handle_open_tls); conn_open_stream(conn); } else { /* failed tls spoils the connection, so disconnect */ @@ -315,7 +316,7 @@ static int _handle_sasl_result(xmpp_conn_t * const conn, /* fall back to next auth method */ _auth(conn); } else if (strcmp(name, "success") == 0) { - /* SASL PLAIN auth successful, we need to restart the stream */ + /* SASL auth successful, we need to restart the stream */ xmpp_debug(conn->ctx, "xmpp", "SASL %s auth successful", (char *)userdata); @@ -564,7 +565,7 @@ static void _auth(xmpp_conn_t * const conn) } if (conn->tls_support) { - tls_t *tls = tls_new(conn->ctx, conn->sock, conn->certfail_handler, conn->tls_cert_path); + tls_t *tls = tls_new(conn); /* If we couldn't init tls, it isn't there, so go on */ if (!tls) { @@ -721,14 +722,11 @@ static void _auth(xmpp_conn_t * const conn) } else if (conn->type == XMPP_CLIENT) { /* legacy client authentication */ - iq = xmpp_stanza_new(conn->ctx); + iq = xmpp_iq_new(conn->ctx, "set", "_xmpp_auth1"); if (!iq) { disconnect_mem_error(conn); return; } - xmpp_stanza_set_name(iq, "iq"); - xmpp_stanza_set_type(iq, "set"); - xmpp_stanza_set_id(iq, "_xmpp_auth1"); query = xmpp_stanza_new(conn->ctx); if (!query) { @@ -838,15 +836,23 @@ void auth_handle_open(xmpp_conn_t * const conn) /* reset all timed handlers */ handler_reset_timed(conn, 0); - /* setup handler for stream:error */ - handler_add(conn, _handle_error, - XMPP_NS_STREAMS, "error", NULL, NULL); + /* setup handler for stream:error, we will keep this handler + * for reopened streams until connection is disconnected */ + handler_add(conn, _handle_error, XMPP_NS_STREAMS, "error", NULL, NULL); /* setup handlers for incoming */ handler_add(conn, _handle_features, XMPP_NS_STREAMS, "features", NULL, NULL); - handler_add_timed(conn, _handle_missing_features, - FEATURES_TIMEOUT, NULL); + handler_add_timed(conn, _handle_missing_features, FEATURES_TIMEOUT, NULL); +} + +/* called when stream:stream tag received after TLS establishment */ +static void _handle_open_tls(xmpp_conn_t * const conn) +{ + /* setup handlers for incoming */ + handler_add(conn, _handle_features, + XMPP_NS_STREAMS, "features", NULL, NULL); + handler_add_timed(conn, _handle_missing_features, FEATURES_TIMEOUT, NULL); } /* called when stream:stream tag received after SASL auth */ @@ -896,16 +902,12 @@ static int _handle_features_sasl(xmpp_conn_t * const conn, BIND_TIMEOUT, NULL); /* send bind request */ - iq = xmpp_stanza_new(conn->ctx); + iq = xmpp_iq_new(conn->ctx, "set", "_xmpp_bind1"); if (!iq) { disconnect_mem_error(conn); return 0; } - xmpp_stanza_set_name(iq, "iq"); - xmpp_stanza_set_type(iq, "set"); - xmpp_stanza_set_id(iq, "_xmpp_bind1"); - bind = xmpp_stanza_copy(bind); if (!bind) { xmpp_stanza_release(iq); @@ -1008,20 +1010,17 @@ static int _handle_bind(xmpp_conn_t * const conn, SESSION_TIMEOUT, NULL); /* send session request */ - iq = xmpp_stanza_new(conn->ctx); + iq = xmpp_iq_new(conn->ctx, "set", "_xmpp_session1"); if (!iq) { disconnect_mem_error(conn); return 0; } - xmpp_stanza_set_name(iq, "iq"); - xmpp_stanza_set_type(iq, "set"); - xmpp_stanza_set_id(iq, "_xmpp_session1"); - session = xmpp_stanza_new(conn->ctx); if (!session) { xmpp_stanza_release(iq); disconnect_mem_error(conn); + return 0; } xmpp_stanza_set_name(session, "session"); diff --git a/src/common.h b/src/common.h index bcd99bd..ac8d317 100644 --- a/src/common.h +++ b/src/common.h @@ -50,6 +50,8 @@ struct _xmpp_ctx_t { xmpp_rand_t *rand; xmpp_loop_status_t loop_status; xmpp_connlist_t *connlist; + + unsigned long timeout; }; /* convenience functions for accessing the context */ @@ -104,7 +106,7 @@ typedef struct _xmpp_handlist_t xmpp_handlist_t; struct _xmpp_handlist_t { /* common members */ int user_handler; - void *handler; + int (*handler)(); void *userdata; int enabled; /* handlers are added disabled and enabled after the * handler chain is processed to prevent stanzas from @@ -166,6 +168,7 @@ struct _xmpp_conn_t { char *tls_cert_path; int tls_mandatory; int tls_legacy_ssl; + int tls_trust; int tls_failed; /* set when tls fails, so we don't try again */ int sasl_support; /* if true, field is a bitfield of supported mechanisms */ @@ -265,6 +268,7 @@ void handler_add(xmpp_conn_t * const conn, const char * const name, const char * const type, void * const userdata); +void handler_system_delete_all(xmpp_conn_t *conn); /* utility functions */ void disconnect_mem_error(xmpp_conn_t * const conn); diff --git a/src/conn.c b/src/conn.c index 0af47ba..121d193 100644 --- a/src/conn.c +++ b/src/conn.c @@ -75,6 +75,15 @@ static int _conn_connect(xmpp_conn_t * const conn, xmpp_conn_handler callback, void * const userdata); +void xmpp_send_error(xmpp_conn_t * const conn, xmpp_error_type_t const type, char * const text) +{ + xmpp_stanza_t *error = xmpp_error_new(conn->ctx, type, text); + + xmpp_send(conn, error); + + xmpp_stanza_release(error); +} + /** Create a new Strophe connection object. * * @param ctx a Strophe context object @@ -131,6 +140,7 @@ xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t * const ctx) conn->tls_disabled = 0; conn->tls_mandatory = 0; conn->tls_legacy_ssl = 0; + conn->tls_trust = 0; conn->tls_failed = 0; conn->sasl_support = 0; conn->secured = 0; @@ -267,6 +277,8 @@ int xmpp_conn_release(xmpp_conn_t * const conn) } } + _conn_reset(conn); + /* free handler stuff * note that userdata is the responsibility of the client * and the handler pointers don't need to be freed since they @@ -308,7 +320,6 @@ int xmpp_conn_release(xmpp_conn_t * const conn) } parser_free(conn->parser); - _conn_reset(conn); if (conn->jid) xmpp_free(ctx, conn->jid); if (conn->pass) xmpp_free(ctx, conn->pass); @@ -880,7 +891,7 @@ int conn_tls_start(xmpp_conn_t * const conn) conn->tls = NULL; rc = XMPP_EINVOP; } else { - conn->tls = tls_new(conn->ctx, conn->sock, conn->certfail_handler, conn->tls_cert_path); + conn->tls = tls_new(conn); rc = conn->tls == NULL ? XMPP_EMEM : 0; } @@ -916,7 +927,8 @@ long xmpp_conn_get_flags(const xmpp_conn_t * const conn) flags = XMPP_CONN_FLAG_DISABLE_TLS * conn->tls_disabled | XMPP_CONN_FLAG_MANDATORY_TLS * conn->tls_mandatory | - XMPP_CONN_FLAG_LEGACY_SSL * conn->tls_legacy_ssl; + XMPP_CONN_FLAG_LEGACY_SSL * conn->tls_legacy_ssl | + XMPP_CONN_FLAG_TRUST_TLS * conn->tls_trust; return flags; } @@ -933,6 +945,7 @@ long xmpp_conn_get_flags(const xmpp_conn_t * const conn) * - XMPP_CONN_FLAG_DISABLE_TLS * - XMPP_CONN_FLAG_MANDATORY_TLS * - XMPP_CONN_FLAG_LEGACY_SSL + * - XMPP_CONN_FLAG_TRUST_TLS * * @param conn a Strophe connection object * @param flags ORed connection flags @@ -949,7 +962,8 @@ int xmpp_conn_set_flags(xmpp_conn_t * const conn, long flags) return XMPP_EINVOP; } if (flags & XMPP_CONN_FLAG_DISABLE_TLS && - flags & (XMPP_CONN_FLAG_MANDATORY_TLS | XMPP_CONN_FLAG_LEGACY_SSL)) { + flags & (XMPP_CONN_FLAG_MANDATORY_TLS | XMPP_CONN_FLAG_LEGACY_SSL | + XMPP_CONN_FLAG_TRUST_TLS)) { xmpp_error(conn->ctx, "conn", "Flags 0x%04lx conflict", flags); return XMPP_EINVOP; } @@ -957,6 +971,7 @@ int xmpp_conn_set_flags(xmpp_conn_t * const conn, long flags) conn->tls_disabled = (flags & XMPP_CONN_FLAG_DISABLE_TLS) ? 1 : 0; conn->tls_mandatory = (flags & XMPP_CONN_FLAG_MANDATORY_TLS) ? 1 : 0; conn->tls_legacy_ssl = (flags & XMPP_CONN_FLAG_LEGACY_SSL) ? 1 : 0; + conn->tls_trust = (flags & XMPP_CONN_FLAG_TRUST_TLS) ? 1 : 0; return 0; } @@ -1289,6 +1304,12 @@ static void _conn_reset(xmpp_conn_t * const conn) conn->secured = 0; conn->tls_failed = 0; conn->error = 0; + + conn->tls_support = 0; + conn->bind_required = 0; + conn->session_required = 0; + + handler_system_delete_all(conn); } static int _conn_connect(xmpp_conn_t * const conn, diff --git a/src/crypto.c b/src/crypto.c index 17e40ae..ef97dc2 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -61,7 +61,7 @@ static char *digest_to_string_alloc(xmpp_ctx_t *ctx, const uint8_t *digest) return s; } -/** Compute SHA1 message digest +/** Compute SHA1 message digest. * Returns an allocated string which represents SHA1 message digest in * hexadecimal notation. The string must be freed with xmpp_free(). * @@ -81,9 +81,37 @@ char *xmpp_sha1(xmpp_ctx_t *ctx, const unsigned char *data, size_t len) return digest_to_string_alloc(ctx, digest); } -/** Create new SHA1 object +/** Compute SHA1 message digest. + * Stores digest in user's buffer which must be at least XMPP_SHA1_DIGEST_SIZE + * bytes long. * - * @param ctx a Strophe context onject + * @param data buffer for digest computation + * @param len size of the data buffer + * @param digest output buffer of XMPP_SHA1_DIGEST_SIZE bytes + * + * @ingroup Digests + */ +void xmpp_sha1_digest(const unsigned char *data, size_t len, + unsigned char *digest) +{ + crypto_SHA1((const uint8_t *)data, len, digest); +} + +/** Create new SHA1 object. + * SHA1 object is used to compute SHA1 digest of a buffer that is split + * in multiple chunks or provided in stream mode. A single buffer can be + * processed by short functions xmpp_sha1() and xmpp_sha1_digest(). + * Follow the next use-case for xmpp_sha1_t object: + * @code + * xmpp_sha1_t *sha1 = xmpp_sha1_new(ctx); + * // Repeat update for all chunks of data + * xmpp_sha1_update(sha1, data, len); + * xmpp_sha1_final(sha1); + * char *digest = xmpp_sha1_to_string_alloc(sha1); + * xmpp_sha1_free(sha1); + * @endcode + * + * @param ctx a Strophe context object * * @return new SHA1 object * @@ -102,7 +130,7 @@ xmpp_sha1_t *xmpp_sha1_new(xmpp_ctx_t *ctx) return sha1; } -/** Destroy SHA1 object +/** Destroy SHA1 object. * * @param sha1 a SHA1 object * @@ -113,7 +141,7 @@ void xmpp_sha1_free(xmpp_sha1_t *sha1) xmpp_free(sha1->xmpp_ctx, sha1); } -/** Update SHA1 context with the next portion of data +/** Update SHA1 context with the next portion of data. * Can be called repeatedly. * * @param sha1 a SHA1 object @@ -127,7 +155,7 @@ void xmpp_sha1_update(xmpp_sha1_t *sha1, const unsigned char *data, size_t len) crypto_SHA1_Update(&sha1->ctx, data, len); } -/** Finish SHA1 computation +/** Finish SHA1 computation. * Don't call xmpp_sha1_update() after this function. Retrieve resulting * message digest with xmpp_sha1_to_string() or xmpp_sha1_to_digest(). * @@ -140,7 +168,7 @@ void xmpp_sha1_final(xmpp_sha1_t *sha1) crypto_SHA1_Final(&sha1->ctx, sha1->digest); } -/** Return message digest rendered as a string +/** Return message digest rendered as a string. * Stores the string to a user's buffer and returns the buffer. Call this * function after xmpp_sha1_final(). * @@ -157,7 +185,7 @@ char *xmpp_sha1_to_string(xmpp_sha1_t *sha1, char *s, size_t slen) return digest_to_string(sha1->digest, s, slen); } -/** Return message digest rendered as a string +/** Return message digest rendered as a string. * Returns an allocated string. Free the string using the Strophe context * which is passed to xmpp_sha1_new(). Call this function after * xmpp_sha1_final(). @@ -173,7 +201,7 @@ char *xmpp_sha1_to_string_alloc(xmpp_sha1_t *sha1) return digest_to_string_alloc(sha1->xmpp_ctx, sha1->digest); } -/** Stores message digest to a user's buffer +/** Stores message digest to a user's buffer. * * @param sha1 a SHA1 object * @param digest output buffer of XMPP_SHA1_DIGEST_SIZE bytes @@ -391,7 +419,7 @@ static void base64_decode(xmpp_ctx_t *ctx, *outlen = 0; } -/** Base64 encoding routine +/** Base64 encoding routine. * Returns an allocated string which must be freed with xmpp_free(). * * @param ctx a Strophe context @@ -407,7 +435,7 @@ char *xmpp_base64_encode(xmpp_ctx_t *ctx, const unsigned char *data, size_t len) return base64_encode(ctx, data, len); } -/** Base64 decoding routine +/** Base64 decoding routine. * Returns an allocated string which must be freed with xmpp_free(). User * calls this function when the result must be a string. When decoded buffer * contains '\0' NULL is returned. @@ -443,7 +471,7 @@ char *xmpp_base64_decode_str(xmpp_ctx_t *ctx, const char *base64, size_t len) return (char *)buf; } -/** Base64 decoding routine +/** Base64 decoding routine. * Returns an allocated buffer which must be freed with xmpp_free(). * * @param ctx a Strophe context diff --git a/src/ctx.c b/src/ctx.c index e14a8c7..b0b1529 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -89,6 +89,14 @@ void xmpp_shutdown(void) #define LIBXMPP_VERSION_MINOR (0) #endif +#ifndef EVENT_LOOP_DEFAULT_TIMEOUT +/** @def EVENT_LOOP_DEFAULT_TIMEOUT + * The default timeout in milliseconds for the event loop. + * This is set to 1 second. + */ +#define EVENT_LOOP_DEFAULT_TIMEOUT 1000 +#endif + /** Check that Strophe supports a specific API version. * * @param major the major version number @@ -180,7 +188,6 @@ xmpp_log_t *xmpp_get_default_logger(xmpp_log_level_t level) { /* clamp to the known range */ if (level > XMPP_LEVEL_ERROR) level = XMPP_LEVEL_ERROR; - if (level < XMPP_LEVEL_DEBUG) level = XMPP_LEVEL_DEBUG; return (xmpp_log_t*)&_xmpp_default_loggers[level]; } @@ -408,6 +415,7 @@ xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t * const mem, ctx->connlist = NULL; ctx->loop_status = XMPP_LOOP_NOTSTARTED; ctx->rand = xmpp_rand_new(ctx); + ctx->timeout = EVENT_LOOP_DEFAULT_TIMEOUT; if (ctx->rand == NULL) { xmpp_free(ctx, ctx); ctx = NULL; @@ -430,3 +438,14 @@ void xmpp_ctx_free(xmpp_ctx_t * const ctx) xmpp_free(ctx, ctx); /* pull the hole in after us */ } +/** Set the timeout to use when calling xmpp_run(). + * + * @param ctx a Strophe context object + * @param timeout the time to wait for events in milliseconds + * + * @ingroup Context + */ +void xmpp_ctx_set_timeout(xmpp_ctx_t * const ctx, const unsigned long timeout) +{ + ctx->timeout = timeout; +} diff --git a/src/event.c b/src/event.c index 3636284..6b2f0d2 100644 --- a/src/event.c +++ b/src/event.c @@ -43,14 +43,6 @@ #include "common.h" #include "parser.h" -#ifndef DEFAULT_TIMEOUT -/** @def DEFAULT_TIMEOUT - * The default timeout in milliseconds for the event loop. - * This is set to 1 millisecond. - */ -#define DEFAULT_TIMEOUT 1 -#endif - /** Run the event loop once. * This function will run send any data that has been queued by * xmpp_send and related functions and run through the Strophe even @@ -110,30 +102,17 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout) if (conn->tls) { ret = tls_write(conn->tls, &sq->data[sq->written], towrite); - - if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls))) { - /* an error occurred */ + if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls))) conn->error = tls_error(conn->tls); - break; - } else if (ret < towrite) { - /* not all data could be sent now */ - if (ret >= 0) sq->written += ret; - break; - } - } else { ret = sock_write(conn->sock, &sq->data[sq->written], towrite); - - if (ret < 0 && !sock_is_recoverable(sock_error())) { - /* an error occurred */ + if (ret < 0 && !sock_is_recoverable(sock_error())) conn->error = sock_error(); - break; - } else if (ret < towrite) { - /* not all data could be sent now */ - if (ret >= 0) sq->written += ret; - break; - } } + if (ret > 0 && ret < towrite) + sq->written += ret; /* not all data could be sent now */ + if (ret != towrite) + break; /* partial write or an error */ /* all data for this queue item written, delete and move on */ xmpp_free(ctx, sq->data); @@ -167,9 +146,8 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout) } - /* fire any ready timed handlers, then - make sure we don't wait past the time when timed handlers need - to be called */ + /* fire any ready timed handlers, then make sure we don't wait past + the time when timed handlers need to be called */ next = handler_fire_timed(ctx); usec = ((next < timeout) ? next : timeout) * 1000; @@ -201,6 +179,8 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout) break; case XMPP_STATE_CONNECTED: FD_SET(conn->sock, &rfds); + if (conn->send_queue_len > 0) + FD_SET(conn->sock, &wfds); break; case XMPP_STATE_DISCONNECTED: /* do nothing */ @@ -209,9 +189,8 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout) } /* Check if there is something in the SSL buffer. */ - if (conn->tls) { + if (conn->tls) tls_read_bytes += tls_pending(conn->tls); - } if (conn->state != XMPP_STATE_DISCONNECTED && conn->sock > max) max = conn->sock; @@ -275,15 +254,12 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout) if (ret > 0) { ret = parser_feed(conn->parser, buf, ret); if (!ret) { - /* parse error, we need to shut down */ - /* FIXME */ - xmpp_debug(ctx, "xmpp", "parse error, disconnecting"); - conn_disconnect(conn); + xmpp_debug(ctx, "xmpp", "parse error [%s]", buf); + xmpp_send_error(conn, XMPP_SE_INVALID_XML, "parse error"); } } else { if (conn->tls) { - if (!tls_is_recoverable(tls_error(conn->tls))) - { + if (!tls_is_recoverable(tls_error(conn->tls))) { xmpp_debug(ctx, "xmpp", "Unrecoverable TLS error, %d.", tls_error(conn->tls)); conn->error = tls_error(conn->tls); conn_disconnect(conn); @@ -325,7 +301,7 @@ void xmpp_run(xmpp_ctx_t *ctx) ctx->loop_status = XMPP_LOOP_RUNNING; while (ctx->loop_status == XMPP_LOOP_RUNNING) { - xmpp_run_once(ctx, DEFAULT_TIMEOUT); + xmpp_run_once(ctx, ctx->timeout); } /* make it possible to start event loop again */ diff --git a/src/handler.c b/src/handler.c index 47caf0d..b987319 100644 --- a/src/handler.c +++ b/src/handler.c @@ -24,6 +24,31 @@ #include "common.h" #include "ostypes.h" +/* Remove item from the list pointed by head, but don't free it. + * There can be a situation when user's handler deletes another handler which + * is the previous in the list. handler_fire_stanza() and handler_fire_timed() + * must handle this situation correctly. Current function helps to avoid + * list corruption in described scenario. + * + * TODO Convert handler lists to double-linked lists. Current implementation + * works for O(n). + */ +static void _handler_item_remove(xmpp_handlist_t **head, + xmpp_handlist_t *item) +{ + xmpp_handlist_t *i = *head; + + if (i == item) + *head = item->next; + else if (i != NULL) { + while (i->next != NULL && i->next != item) + i = i->next; + if (i->next == item) { + i->next = item->next; + } + } +} + /** Fire off all stanza handlers that match. * This function is called internally by the event loop whenever stanzas * are received from the XMPP server. @@ -34,36 +59,40 @@ void handler_fire_stanza(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza) { - xmpp_handlist_t *item, *prev; + xmpp_handlist_t *item, *next, *head, *head_old; const char *id, *ns, *name, *type; + int ret; /* call id handlers */ id = xmpp_stanza_get_id(stanza); if (id) { - prev = NULL; - item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id); - while (item) { - xmpp_handlist_t *next = item->next; + head = (xmpp_handlist_t *)hash_get(conn->id_handlers, id); + /* enable all added handlers */ + for (item = head; item; item = item->next) + item->enabled = 1; - if (item->user_handler && !conn->authenticated) { - item = next; - continue; + item = head; + while (item) { + /* don't fire user handlers until authentication succeeds and + and skip newly added handlers */ + if ((item->user_handler && !conn->authenticated) || !item->enabled) { + item = item->next; + continue; } - if (!((xmpp_handler)(item->handler))(conn, stanza, item->userdata)) { + ret = ((xmpp_handler)(item->handler))(conn, stanza, item->userdata); + next = item->next; + if (!ret) { /* handler is one-shot, so delete it */ - if (prev) - prev->next = next; - else { - hash_drop(conn->id_handlers, id); - hash_add(conn->id_handlers, id, next); + head_old = head; + _handler_item_remove(&head, item); + if (head != head_old) { + /* replace old value */ + hash_add(conn->id_handlers, id, head); } xmpp_free(conn->ctx, item->id); xmpp_free(conn->ctx, item); - item = NULL; } - if (item) - prev = item; item = next; } } @@ -77,47 +106,34 @@ void handler_fire_stanza(xmpp_conn_t * const conn, for (item = conn->handlers; item; item = item->next) item->enabled = 1; - prev = NULL; item = conn->handlers; while (item) { - /* skip newly added handlers */ - if (!item->enabled) { - prev = item; - item = item->next; - continue; - } - - /* don't call user handlers until authentication succeeds */ - if (item->user_handler && !conn->authenticated) { - prev = item; + /* don't fire user handlers until authentication succeeds and + skip newly added handlers */ + if ((item->user_handler && !conn->authenticated) || !item->enabled) { item = item->next; continue; } + next = item->next; if ((!item->ns || (ns && strcmp(ns, item->ns) == 0) || xmpp_stanza_get_child_by_ns(stanza, item->ns)) && (!item->name || (name && strcmp(name, item->name) == 0)) && - (!item->type || (type && strcmp(type, item->type) == 0))) - if (!((xmpp_handler)(item->handler))(conn, stanza, item->userdata)) { + (!item->type || (type && strcmp(type, item->type) == 0))) { + + ret = ((xmpp_handler)(item->handler))(conn, stanza, item->userdata); + /* list may be changed during execution of a handler */ + next = item->next; + if (!ret) { /* handler is one-shot, so delete it */ - if (prev) - prev->next = item->next; - else - conn->handlers = item->next; + _handler_item_remove(&conn->handlers, item); if (item->ns) xmpp_free(conn->ctx, item->ns); if (item->name) xmpp_free(conn->ctx, item->name); if (item->type) xmpp_free(conn->ctx, item->type); xmpp_free(conn->ctx, item); - item = NULL; } - - if (item) { - prev = item; - item = item->next; - } else if (prev) - item = prev->next; - else - item = conn->handlers; + } + item = next; } } @@ -131,54 +147,53 @@ void handler_fire_stanza(xmpp_conn_t * const conn, uint64_t handler_fire_timed(xmpp_ctx_t * const ctx) { xmpp_connlist_t *connitem; - xmpp_handlist_t *handitem, *temp; - int ret, fired; + xmpp_handlist_t *item, *next; + xmpp_conn_t *conn; uint64_t elapsed, min; + uint64_t timestamp; + int ret; min = (uint64_t)(-1); connitem = ctx->connlist; while (connitem) { - if (connitem->conn->state != XMPP_STATE_CONNECTED) { + conn = connitem->conn; + if (conn->state != XMPP_STATE_CONNECTED) { connitem = connitem->next; continue; } /* enable all handlers that were added */ - for (handitem = connitem->conn->timed_handlers; handitem; - handitem = handitem->next) - handitem->enabled = 1; - - handitem = connitem->conn->timed_handlers; - while (handitem) { - /* skip newly added handlers */ - if (!handitem->enabled) { - handitem = handitem->next; - continue; - } + for (item = conn->timed_handlers; item; item = item->next) + item->enabled = 1; - /* only fire user handlers after authentication */ - if (handitem->user_handler && !connitem->conn->authenticated) { - handitem = handitem->next; + item = conn->timed_handlers; + while (item) { + /* don't fire user handlers until authentication succeeds and + skip newly added handlers */ + if ((item->user_handler && !conn->authenticated) || !item->enabled) { + item = item->next; continue; } - fired = 0; - elapsed = time_elapsed(handitem->last_stamp, time_stamp()); - if (elapsed >= handitem->period) { + next = item->next; + timestamp = time_stamp(); + elapsed = time_elapsed(item->last_stamp, timestamp); + if (elapsed >= item->period) { /* fire! */ - fired = 1; - handitem->last_stamp = time_stamp(); - ret = ((xmpp_timed_handler)handitem->handler)(connitem->conn, handitem->userdata); - } else if (min > (handitem->period - elapsed)) - min = handitem->period - elapsed; - - temp = handitem; - handitem = handitem->next; - - /* delete handler if it returned false */ - if (fired && !ret) - xmpp_timed_handler_delete(connitem->conn, temp->handler); + item->last_stamp = timestamp; + ret = ((xmpp_timed_handler)item->handler)(conn, item->userdata); + /* list may be changed during execution of a handler */ + next = item->next; + if (!ret) { + /* delete handler if it returned false */ + _handler_item_remove(&conn->timed_handlers, item); + xmpp_free(conn->ctx, item); + } + } else if (min > (item->period - elapsed)) + min = item->period - elapsed; + + item = next; } connitem = connitem->next; @@ -216,8 +231,10 @@ static void _timed_handler_add(xmpp_conn_t * const conn, /* check if handler is already in the list */ for (item = conn->timed_handlers; item; item = item->next) { - if (item->handler == (void *)handler) + if (item->handler == handler && item->userdata == userdata) { + xmpp_warn(conn->ctx, "xmpp", "Timed handler already exists."); break; + } } if (item) return; @@ -226,7 +243,7 @@ static void _timed_handler_add(xmpp_conn_t * const conn, if (!item) return; item->user_handler = user_handler; - item->handler = (void *)handler; + item->handler = handler; item->userdata = userdata; item->enabled = 0; item->next = NULL; @@ -262,19 +279,18 @@ void xmpp_timed_handler_delete(xmpp_conn_t * const conn, prev = NULL; item = conn->timed_handlers; while (item) { - if (item->handler == (void *)handler) - break; - prev = item; - item = item->next; - } - - if (item) { - if (prev) - prev->next = item->next; - else - conn->timed_handlers = item->next; + if (item->handler == handler) { + if (prev) + prev->next = item->next; + else + conn->timed_handlers = item->next; - xmpp_free(conn->ctx, item); + xmpp_free(conn->ctx, item); + item = prev ? prev->next : conn->timed_handlers; + } else { + prev = item; + item = item->next; + } } } @@ -288,8 +304,10 @@ static void _id_handler_add(xmpp_conn_t * const conn, /* check if handler is already in the list */ item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id); while (item) { - if (item->handler == (void *)handler) + if (item->handler == handler && item->userdata == userdata) { + xmpp_warn(conn->ctx, "xmpp", "Id handler already exists."); break; + } item = item->next; } if (item) return; @@ -299,7 +317,7 @@ static void _id_handler_add(xmpp_conn_t * const conn, if (!item) return; item->user_handler = user_handler; - item->handler = (void *)handler; + item->handler = handler; item->userdata = userdata; item->enabled = 0; item->next = NULL; @@ -333,29 +351,30 @@ void xmpp_id_handler_delete(xmpp_conn_t * const conn, xmpp_handler handler, const char * const id) { - xmpp_handlist_t *item, *prev; + xmpp_handlist_t *item, *prev, *next; prev = NULL; item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id); if (!item) return; while (item) { - if (item->handler == (void *)handler) - break; - - prev = item; - item = item->next; - } + next = item->next; + + if (item->handler == handler) { + if (prev) + prev->next = next; + else { + hash_drop(conn->id_handlers, id); + hash_add(conn->id_handlers, id, next); + } - if (item) { - if (prev) - prev->next = item->next; - else { - hash_drop(conn->id_handlers, id); - hash_add(conn->id_handlers, id, item->next); + xmpp_free(conn->ctx, item->id); + xmpp_free(conn->ctx, item); + item = next; + } else { + prev = item; + item = next; } - xmpp_free(conn->ctx, item->id); - xmpp_free(conn->ctx, item); } } @@ -371,8 +390,12 @@ static void _handler_add(xmpp_conn_t * const conn, /* check if handler already in list */ for (item = conn->handlers; item; item = item->next) { - if (item->handler == (void *)handler) + /* same handler function can process different stanzas and + distinguish them according to userdata. */ + if (item->handler == handler && item->userdata == userdata) { + xmpp_warn(conn->ctx, "xmpp", "Stanza handler already exists."); break; + } } if (item) return; @@ -381,7 +404,7 @@ static void _handler_add(xmpp_conn_t * const conn, if (!item) return; item->user_handler = user_handler; - item->handler = (void *)handler; + item->handler = handler; item->userdata = userdata; item->enabled = 0; item->next = NULL; @@ -441,23 +464,21 @@ void xmpp_handler_delete(xmpp_conn_t * const conn, prev = NULL; item = conn->handlers; while (item) { - if (item->handler == (void *)handler) - break; + if (item->handler == handler) { + if (prev) + prev->next = item->next; + else + conn->handlers = item->next; - prev = item; - item = item->next; - } - - if (item) { - if (prev) - prev->next = item->next; - else - conn->handlers = item->next; - - if (item->ns) xmpp_free(conn->ctx, item->ns); - if (item->name) xmpp_free(conn->ctx, item->name); - if (item->type) xmpp_free(conn->ctx, item->type); - xmpp_free(conn->ctx, item); + if (item->ns) xmpp_free(conn->ctx, item->ns); + if (item->name) xmpp_free(conn->ctx, item->name); + if (item->type) xmpp_free(conn->ctx, item->type); + xmpp_free(conn->ctx, item); + item = prev ? prev->next : conn->handlers; + } else { + prev = item; + item = item->next; + } } } @@ -595,3 +616,75 @@ void handler_add(xmpp_conn_t * const conn, { _handler_add(conn, handler, ns, name, type, userdata, 0); } + +/** Delete all system handlers. + * This function is used to reset conn object before re-connecting. + * + * @param conn a Strophe connection object + */ +void handler_system_delete_all(xmpp_conn_t *conn) +{ + xmpp_handlist_t *item, *next, *head, *head_old; + hash_iterator_t *iter; + const char *key; + char *key2 = NULL; + + /* TODO unify all kinds of handlers and avoid copy-paste below */ + + item = conn->handlers; + while (item) { + if (!item->user_handler) { + next = item->next; + _handler_item_remove(&conn->handlers, item); + if (item->ns) xmpp_free(conn->ctx, item->ns); + if (item->name) xmpp_free(conn->ctx, item->name); + if (item->type) xmpp_free(conn->ctx, item->type); + xmpp_free(conn->ctx, item); + item = next; + } else + item = item->next; + } + + item = conn->timed_handlers; + while (item) { + if (!item->user_handler) { + next = item->next; + _handler_item_remove(&conn->timed_handlers, item); + xmpp_free(conn->ctx, item); + item = next; + } else + item = item->next; + } + + iter = hash_iter_new(conn->id_handlers); + key = iter == NULL ? NULL : hash_iter_next(iter); + while (key != NULL) { + head = head_old = (xmpp_handlist_t *)hash_get(conn->id_handlers, key); + item = head; + while (item) { + if (!item->user_handler) { + next = item->next; + _handler_item_remove(&head, item); + xmpp_free(conn->ctx, item->id); + xmpp_free(conn->ctx, item); + item = next; + } else + item = item->next; + } + if (head != head_old) + key2 = xmpp_strdup(conn->ctx, key); + /* Hash table implementation is not perfect, so we need to find next + key before dropping current one. Otherwise, we will get access to + freed memory. */ + key = hash_iter_next(iter); + if (head != head_old) { + /* hash_add() replaces value if the key exists */ + if (head != NULL) + hash_add(conn->id_handlers, key2, head); + else + hash_drop(conn->id_handlers, key2); + xmpp_free(conn->ctx, key2); + } + } + if (iter) hash_iter_release(iter); +} diff --git a/src/hash.c b/src/hash.c index 0d47c97..0d59b6b 100644 --- a/src/hash.c +++ b/src/hash.c @@ -106,18 +106,35 @@ void hash_release(hash_t * const table) /** hash a key for our table lookup */ static int _hash_key(hash_t *table, const char *key) { - int hash = 0; - int shift = 0; - const char *c = key; + unsigned hash = 0; + unsigned shift = 0; + const unsigned char *c = (const unsigned char *)key; - while (*c != '\0') { + while (*c != 0) { /* assume 32 bit ints */ - hash ^= ((int)*c++ << shift); + hash ^= ((unsigned)*c++ << shift); shift += 8; if (shift > 24) shift = 0; } + return hash % (unsigned)table->length; +} - return hash % table->length; +hashentry_t *_hash_entry_find(hash_t *table, const char *key) +{ + hashentry_t *entry; + int table_index = _hash_key(table, key); + + /* look up the hash entry */ + entry = table->entries[table_index]; + while (entry != NULL) { + /* traverse the linked list looking for the key */ + if (!strcmp(key, entry->key)) { + /* match */ + break; + } + entry = entry->next; + } + return entry; } /** add a key, value pair to a hash table. @@ -130,23 +147,25 @@ int hash_add(hash_t *table, const char * const key, void *data) hashentry_t *entry = NULL; int table_index = _hash_key(table, key); - /* drop existing entry, if any */ - hash_drop(table, key); - - /* allocate and fill a new entry */ - entry = xmpp_alloc(ctx, sizeof(hashentry_t)); - if (!entry) return -1; - entry->key = xmpp_strdup(ctx, key); - if (!entry->key) { - xmpp_free(ctx, entry); - return -1; + /* find and replace existing entry, if any */ + entry = _hash_entry_find(table, key); + + if (entry == NULL) { + /* allocate and fill a new entry */ + entry = xmpp_alloc(ctx, sizeof(hashentry_t)); + if (!entry) return -1; + entry->key = xmpp_strdup(ctx, key); + if (!entry->key) { + xmpp_free(ctx, entry); + return -1; + } + /* insert ourselves in the linked list */ + entry->next = table->entries[table_index]; + table->entries[table_index] = entry; + table->num_keys++; } + entry->value = data; - /* insert ourselves in the linked list */ - /* TODO: this leaks duplicate keys */ - entry->next = table->entries[table_index]; - table->entries[table_index] = entry; - table->num_keys++; return 0; } @@ -155,22 +174,9 @@ int hash_add(hash_t *table, const char * const key, void *data) void *hash_get(hash_t *table, const char *key) { hashentry_t *entry; - int table_index = _hash_key(table, key); - void *result = NULL; - /* look up the hash entry */ - entry = table->entries[table_index]; - while (entry != NULL) { - /* traverse the linked list looking for the key */ - if (!strcmp(key, entry->key)) { - /* match */ - result = entry->value; - return result; - } - entry = entry->next; - } - /* no match */ - return result; + entry = _hash_entry_find(table, key); + return entry == NULL ? NULL : entry->value; } /** delete a key from a hash table */ @@ -235,7 +241,7 @@ void hash_iter_release(hash_iterator_t *iter) iter->ref--; - if (iter->ref <= 0) { + if (iter->ref == 0) { // ref is unsigned!!! hash_release(iter->table); xmpp_free(ctx, iter); } @@ -273,4 +279,3 @@ const char * hash_iter_next(hash_iterator_t *iter) iter->entry = entry; return entry->key; } - diff --git a/src/jid.c b/src/jid.c index 0687123..ca5d2fc 100644 --- a/src/jid.c +++ b/src/jid.c @@ -72,15 +72,13 @@ char *xmpp_jid_new(xmpp_ctx_t *ctx, const char *node, char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid) { char *result; - const char *c; - - c = strchr(jid, '/'); - if (c == NULL) return xmpp_strdup(ctx, jid); + size_t len; - result = xmpp_alloc(ctx, c-jid+1); + len = strcspn(jid, "/"); + result = xmpp_alloc(ctx, len + 1); if (result != NULL) { - memcpy(result, jid, c-jid); - result[c-jid] = '\0'; + memcpy(result, jid, len); + result[len] = '\0'; } return result; @@ -121,7 +119,8 @@ char *xmpp_jid_node(xmpp_ctx_t *ctx, const char *jid) char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid) { char *result = NULL; - const char *c,*s; + const char *c; + size_t dlen; c = strchr(jid, '@'); if (c == NULL) { @@ -131,15 +130,11 @@ char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid) /* advance past the separator */ c++; } - s = strchr(c, '/'); - if (s == NULL) { - /* no resource */ - s = c + strlen(c); - } - result = xmpp_alloc(ctx, (s-c) + 1); + dlen = strcspn(c, "/"); /* do not include resource */ + result = xmpp_alloc(ctx, dlen + 1); if (result != NULL) { - memcpy(result, c, (s-c)); - result[s-c] = '\0'; + memcpy(result, c, dlen); + result[dlen] = '\0'; } return result; @@ -155,20 +150,8 @@ char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid) */ char *xmpp_jid_resource(xmpp_ctx_t *ctx, const char *jid) { - char *result = NULL; const char *c; - size_t len; c = strchr(jid, '/'); - if (c != NULL) { - c++; - len = strlen(c); - result = xmpp_alloc(ctx, len + 1); - if (result != NULL) { - memcpy(result, c, len); - result[len] = '\0'; - } - } - - return result; + return c != NULL ? xmpp_strdup(ctx, c + 1) : NULL; } diff --git a/src/parser_expat.c b/src/parser_expat.c index e38996d..38e95aa 100644 --- a/src/parser_expat.c +++ b/src/parser_expat.c @@ -26,6 +26,12 @@ /* Use the Unit Separator to delimit namespace and name in our XML*/ #define NAMESPACE_SEP ('\x1F') +/* Allocate inner text by this number bytes more. Expat splits string + * "new\nline" into 3 strings: "new" "\n" "line". Expecting this pattern, + * we can leave few bytes in the inner_text for "\n". It should reduce + * number of re-allocations in 2 times for multi-line texts. */ +#define INNER_TEXT_PADDING 2 + struct _parser_t { xmpp_ctx_t *ctx; XML_Parser expat; @@ -35,6 +41,11 @@ struct _parser_t { void *userdata; int depth; xmpp_stanza_t *stanza; + char* inner_text; + /* number of allocated bytes */ + int inner_text_size; + /* excluding terminal '\0' */ + int inner_text_used; }; /* return allocated string with the name from a delimited @@ -92,6 +103,26 @@ static void _set_attributes(xmpp_stanza_t *stanza, const XML_Char **attrs) } } +static void complete_inner_text(parser_t *parser) +{ + xmpp_stanza_t *stanza; + + if (parser->inner_text) { + /* create and populate stanza */ + stanza = xmpp_stanza_new(parser->ctx); + /* FIXME: disconnect on allocation error */ + if (stanza) { + xmpp_stanza_set_text(stanza, parser->inner_text); + xmpp_stanza_add_child(parser->stanza, stanza); + xmpp_stanza_release(stanza); + } + xmpp_free(parser->ctx, parser->inner_text); + parser->inner_text = NULL; + parser->inner_text_size = 0; + parser->inner_text_used = 0; + } +} + static void _start_element(void *userdata, const XML_Char *nsname, const XML_Char **attrs) @@ -125,6 +156,7 @@ static void _start_element(void *userdata, xmpp_stanza_set_ns(child, ns); if (parser->stanza != NULL) { + complete_inner_text(parser); xmpp_stanza_add_child(parser->stanza, child); xmpp_stanza_release(child); } @@ -149,36 +181,46 @@ static void _end_element(void *userdata, const XML_Char *name) if (parser->endcb) parser->endcb((char *)name, parser->userdata); } else { - if (parser->stanza->parent) { - /* we're finishing a child stanza, so set current to the parent */ - parser->stanza = parser->stanza->parent; - } else { + complete_inner_text(parser); + if (parser->stanza->parent) { + /* we're finishing a child stanza, so set current to the parent */ + parser->stanza = parser->stanza->parent; + } else { if (parser->stanzacb) parser->stanzacb(parser->stanza, parser->userdata); - xmpp_stanza_release(parser->stanza); - parser->stanza = NULL; - } + xmpp_stanza_release(parser->stanza); + parser->stanza = NULL; + } } } static void _characters(void *userdata, const XML_Char *s, int len) { parser_t *parser = (parser_t *)userdata; - xmpp_stanza_t *stanza; + char *p; if (parser->depth < 2) return; - /* create and populate stanza */ - stanza = xmpp_stanza_new(parser->ctx); - if (!stanza) { - /* FIXME: allocation error, disconnect */ - return; + /* Join all parts to a single resulting string. Stanza is created in + * _start_element() and _end_element(). */ + if (parser->inner_text_used + len >= parser->inner_text_size) { + parser->inner_text_size = parser->inner_text_used + len + 1 + + INNER_TEXT_PADDING; + p = xmpp_realloc(parser->ctx, parser->inner_text, + parser->inner_text_size); + if (p == NULL) { + xmpp_free(parser->ctx, parser->inner_text); + parser->inner_text = NULL; + parser->inner_text_used = 0; + parser->inner_text_size = 0; + return; + } + parser->inner_text = p; + parser->inner_text[parser->inner_text_used] = '\0'; } - xmpp_stanza_set_text_with_size(stanza, s, len); - - xmpp_stanza_add_child(parser->stanza, stanza); - xmpp_stanza_release(stanza); + parser->inner_text_used += len; + strncat(parser->inner_text, s, len); } parser_t *parser_new(xmpp_ctx_t *ctx, @@ -199,6 +241,9 @@ parser_t *parser_new(xmpp_ctx_t *ctx, parser->userdata = userdata; parser->depth = 0; parser->stanza = NULL; + parser->inner_text = NULL; + parser->inner_text_size = 0; + parser->inner_text_used = 0; parser_reset(parser); } @@ -217,6 +262,11 @@ void parser_free(parser_t *parser) if (parser->expat) XML_ParserFree(parser->expat); + if (parser->inner_text) { + xmpp_free (parser->ctx, parser->inner_text); + parser->inner_text = NULL; + } + xmpp_free(parser->ctx, parser); } @@ -235,6 +285,11 @@ int parser_reset(parser_t *parser) parser->depth = 0; parser->stanza = NULL; + if (parser->inner_text) { + xmpp_free (parser->ctx, parser->inner_text); + parser->inner_text = NULL; + } + XML_SetUserData(parser->expat, parser); XML_SetElementHandler(parser->expat, _start_element, _end_element); XML_SetCharacterDataHandler(parser->expat, _characters); diff --git a/src/resolver.c b/src/resolver.c index 83c6ee8..1100868 100644 --- a/src/resolver.c +++ b/src/resolver.c @@ -318,4 +318,3 @@ void resolver_srv_free(xmpp_ctx_t *ctx, resolver_srv_rr_t *srv_rr_list) srv_rr_list = rr; } } - diff --git a/src/sasl.c b/src/sasl.c index 4f4e064..0627f16 100644 --- a/src/sasl.c +++ b/src/sasl.c @@ -253,7 +253,8 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge, xmpp_rand_nonce(ctx->rand, cnonce, sizeof(cnonce)); hash_add(table, "cnonce", xmpp_strdup(ctx, cnonce)); hash_add(table, "nc", xmpp_strdup(ctx, "00000001")); - hash_add(table, "qop", xmpp_strdup(ctx, "auth")); + if (hash_get(table, "qop") == NULL) + hash_add(table, "qop", xmpp_strdup(ctx, "auth")); value = xmpp_alloc(ctx, 5 + strlen(domain) + 1); memcpy(value, "xmpp/", 5); memcpy(value+5, domain, strlen(domain)); diff --git a/src/snprintf.c b/src/snprintf.c index 3e854e5..651f6f0 100644 --- a/src/snprintf.c +++ b/src/snprintf.c @@ -286,6 +286,7 @@ static int dopr (char *buffer, size_t maxlen, const char *format, va_list args) break; case 'X': flags |= DP_F_UP; + //-fallthrough case 'x': flags |= DP_F_UNSIGNED; if (cflags == DP_C_SHORT) @@ -306,6 +307,7 @@ static int dopr (char *buffer, size_t maxlen, const char *format, va_list args) break; case 'E': flags |= DP_F_UP; + //-fallthrough case 'e': if (cflags == DP_C_LDOUBLE) fvalue = va_arg (args, LDOUBLE); @@ -314,6 +316,7 @@ static int dopr (char *buffer, size_t maxlen, const char *format, va_list args) break; case 'G': flags |= DP_F_UP; + //-fallthrough case 'g': if (cflags == DP_C_LDOUBLE) fvalue = va_arg (args, LDOUBLE); diff --git a/src/stanza.c b/src/stanza.c index 133edf4..846f509 100644 --- a/src/stanza.c +++ b/src/stanza.c @@ -40,15 +40,15 @@ xmpp_stanza_t *xmpp_stanza_new(xmpp_ctx_t *ctx) stanza = xmpp_alloc(ctx, sizeof(xmpp_stanza_t)); if (stanza != NULL) { - stanza->ref = 1; - stanza->ctx = ctx; - stanza->type = XMPP_STANZA_UNKNOWN; - stanza->prev = NULL; - stanza->next = NULL; - stanza->children = NULL; - stanza->parent = NULL; - stanza->data = NULL; - stanza->attributes = NULL; + stanza->ref = 1; + stanza->ctx = ctx; + stanza->type = XMPP_STANZA_UNKNOWN; + stanza->prev = NULL; + stanza->next = NULL; + stanza->children = NULL; + stanza->parent = NULL; + stanza->data = NULL; + stanza->attributes = NULL; } return stanza; @@ -76,35 +76,27 @@ xmpp_stanza_t *xmpp_stanza_clone(xmpp_stanza_t * const stanza) static int _stanza_copy_attributes(xmpp_stanza_t * dst, const xmpp_stanza_t * const src) { - hash_iterator_t *iter = NULL; + hash_iterator_t *iter; const char *key; - void *val; + const char *val; + int rc = XMPP_EOK; - dst->attributes = hash_new(src->ctx, 8, xmpp_free); - if (!dst->attributes) - return -1; iter = hash_iter_new(src->attributes); - if (!iter) - goto error; - while ((key = hash_iter_next(iter))) { - val = xmpp_strdup(src->ctx, - (char *)hash_get(src->attributes, key)); - if (!val) - goto error; - - if (hash_add(dst->attributes, key, val)) { - xmpp_free(src->ctx, val); - goto error; - } + if (!iter) rc = XMPP_EMEM; + + while (rc == XMPP_EOK && (key = hash_iter_next(iter))) { + val = hash_get(src->attributes, key); + if (!val) rc = XMPP_EINT; + if (rc == XMPP_EOK) + rc = xmpp_stanza_set_attribute(dst, key, val); } hash_iter_release(iter); - return 0; -error: - if (iter != NULL) - hash_iter_release(iter); - hash_release(dst->attributes); - return -1; + if (rc != XMPP_EOK && dst->attributes) { + hash_release(dst->attributes); + dst->attributes = NULL; + } + return rc; } /** Copy a stanza and its children. @@ -129,27 +121,27 @@ xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t * const stanza) copy->type = stanza->type; if (stanza->data) { - copy->data = xmpp_strdup(stanza->ctx, stanza->data); - if (!copy->data) goto copy_error; + copy->data = xmpp_strdup(stanza->ctx, stanza->data); + if (!copy->data) goto copy_error; } if (stanza->attributes) { - if (_stanza_copy_attributes(copy, stanza) == -1) + if (_stanza_copy_attributes(copy, stanza) == -1) goto copy_error; } tail = copy->children; for (child = stanza->children; child; child = child->next) { - copychild = xmpp_stanza_copy(child); - if (!copychild) goto copy_error; - copychild->parent = copy; - - if (tail) { - copychild->prev = tail; - tail->next = copychild; - } else - copy->children = copychild; - tail = copychild; + copychild = xmpp_stanza_copy(child); + if (!copychild) goto copy_error; + copychild->parent = copy; + + if (tail) { + copychild->prev = tail; + tail->next = copychild; + } else + copy->children = copychild; + tail = copychild; } return copy; @@ -177,20 +169,20 @@ int xmpp_stanza_release(xmpp_stanza_t * const stanza) /* release stanza */ if (stanza->ref > 1) - stanza->ref--; + stanza->ref--; else { - /* release all children */ - child = stanza->children; - while (child) { - tchild = child; - child = child->next; - xmpp_stanza_release(tchild); - } - - if (stanza->attributes) hash_release(stanza->attributes); - if (stanza->data) xmpp_free(stanza->ctx, stanza->data); - xmpp_free(stanza->ctx, stanza); - released = 1; + /* release all children */ + child = stanza->children; + while (child) { + tchild = child; + child = child->next; + xmpp_stanza_release(tchild); + } + + if (stanza->attributes) hash_release(stanza->attributes); + if (stanza->data) xmpp_free(stanza->ctx, stanza->data); + xmpp_free(stanza->ctx, stanza); + released = 1; } return released; @@ -282,17 +274,17 @@ static char *_escape_xml(xmpp_ctx_t * const ctx, char *text) /* small helper function */ static void _render_update(int *written, const int length, - const int lastwrite, - size_t *left, char **ptr) + const int lastwrite, + size_t *left, char **ptr) { *written += lastwrite; if (*written >= length) { - *left = 0; - *ptr = NULL; + *left = 0; + *ptr = NULL; } else { - *left -= lastwrite; - *ptr = &(*ptr)[lastwrite]; + *left -= lastwrite; + *ptr = &(*ptr)[lastwrite]; } } @@ -302,7 +294,7 @@ static void _render_update(int *written, const int length, * and return values > buflen indicate buffer was not large enough */ static int _render_stanza_recursive(xmpp_stanza_t *stanza, - char * const buf, size_t const buflen) + char * const buf, size_t const buflen) { char *ptr = buf; size_t left = buflen; @@ -317,80 +309,80 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza, if (stanza->type == XMPP_STANZA_UNKNOWN) return XMPP_EINVOP; if (stanza->type == XMPP_STANZA_TEXT) { - if (!stanza->data) return XMPP_EINVOP; - - tmp = _escape_xml(stanza->ctx, stanza->data); - if (tmp == NULL) return XMPP_EMEM; - ret = xmpp_snprintf(ptr, left, "%s", tmp); - xmpp_free(stanza->ctx, tmp); - if (ret < 0) return XMPP_EMEM; - _render_update(&written, buflen, ret, &left, &ptr); + if (!stanza->data) return XMPP_EINVOP; + + tmp = _escape_xml(stanza->ctx, stanza->data); + if (tmp == NULL) return XMPP_EMEM; + ret = xmpp_snprintf(ptr, left, "%s", tmp); + xmpp_free(stanza->ctx, tmp); + if (ret < 0) return XMPP_EMEM; + _render_update(&written, buflen, ret, &left, &ptr); } else { /* stanza->type == XMPP_STANZA_TAG */ - if (!stanza->data) return XMPP_EINVOP; - - /* write beginning of tag and attributes */ - ret = xmpp_snprintf(ptr, left, "<%s", stanza->data); - if (ret < 0) return XMPP_EMEM; - _render_update(&written, buflen, ret, &left, &ptr); - - if (stanza->attributes && hash_num_keys(stanza->attributes) > 0) { - iter = hash_iter_new(stanza->attributes); - while ((key = hash_iter_next(iter))) { - if (!strcmp(key, "xmlns")) { - /* don't output namespace if parent stanza is the same */ - if (stanza->parent && - stanza->parent->attributes && - hash_get(stanza->parent->attributes, key) && - !strcmp((char*)hash_get(stanza->attributes, key), - (char*)hash_get(stanza->parent->attributes, key))) - continue; - /* or if this is the stream namespace */ - if (!stanza->parent && - !strcmp((char*)hash_get(stanza->attributes, key), - XMPP_NS_CLIENT)) - continue; - } - tmp = _escape_xml(stanza->ctx, - (char *)hash_get(stanza->attributes, key)); - if (tmp == NULL) return XMPP_EMEM; - ret = xmpp_snprintf(ptr, left, " %s=\"%s\"", key, tmp); - xmpp_free(stanza->ctx, tmp); - if (ret < 0) return XMPP_EMEM; - _render_update(&written, buflen, ret, &left, &ptr); - } - hash_iter_release(iter); - } - - if (!stanza->children) { - /* write end if singleton tag */ - ret = xmpp_snprintf(ptr, left, "/>"); - if (ret < 0) return XMPP_EMEM; - _render_update(&written, buflen, ret, &left, &ptr); - } else { - /* this stanza has child stanzas */ - - /* write end of start tag */ - ret = xmpp_snprintf(ptr, left, ">"); - if (ret < 0) return XMPP_EMEM; - _render_update(&written, buflen, ret, &left, &ptr); - - /* iterate and recurse over child stanzas */ - child = stanza->children; - while (child) { - ret = _render_stanza_recursive(child, ptr, left); - if (ret < 0) return ret; - - _render_update(&written, buflen, ret, &left, &ptr); - - child = child->next; - } - - /* write end tag */ - ret = xmpp_snprintf(ptr, left, "", stanza->data); - if (ret < 0) return XMPP_EMEM; - - _render_update(&written, buflen, ret, &left, &ptr); - } + if (!stanza->data) return XMPP_EINVOP; + + /* write beginning of tag and attributes */ + ret = xmpp_snprintf(ptr, left, "<%s", stanza->data); + if (ret < 0) return XMPP_EMEM; + _render_update(&written, buflen, ret, &left, &ptr); + + if (stanza->attributes && hash_num_keys(stanza->attributes) > 0) { + iter = hash_iter_new(stanza->attributes); + while ((key = hash_iter_next(iter))) { + if (!strcmp(key, "xmlns")) { + /* don't output namespace if parent stanza is the same */ + if (stanza->parent && + stanza->parent->attributes && + hash_get(stanza->parent->attributes, key) && + !strcmp((char*)hash_get(stanza->attributes, key), + (char*)hash_get(stanza->parent->attributes, key))) + continue; + /* or if this is the stream namespace */ + if (!stanza->parent && + !strcmp((char*)hash_get(stanza->attributes, key), + XMPP_NS_CLIENT)) + continue; + } + tmp = _escape_xml(stanza->ctx, + (char *)hash_get(stanza->attributes, key)); + if (tmp == NULL) return XMPP_EMEM; + ret = xmpp_snprintf(ptr, left, " %s=\"%s\"", key, tmp); + xmpp_free(stanza->ctx, tmp); + if (ret < 0) return XMPP_EMEM; + _render_update(&written, buflen, ret, &left, &ptr); + } + hash_iter_release(iter); + } + + if (!stanza->children) { + /* write end if singleton tag */ + ret = xmpp_snprintf(ptr, left, "/>"); + if (ret < 0) return XMPP_EMEM; + _render_update(&written, buflen, ret, &left, &ptr); + } else { + /* this stanza has child stanzas */ + + /* write end of start tag */ + ret = xmpp_snprintf(ptr, left, ">"); + if (ret < 0) return XMPP_EMEM; + _render_update(&written, buflen, ret, &left, &ptr); + + /* iterate and recurse over child stanzas */ + child = stanza->children; + while (child) { + ret = _render_stanza_recursive(child, ptr, left); + if (ret < 0) return ret; + + _render_update(&written, buflen, ret, &left, &ptr); + + child = child->next; + } + + /* write end tag */ + ret = xmpp_snprintf(ptr, left, "", stanza->data); + if (ret < 0) return XMPP_EMEM; + + _render_update(&written, buflen, ret, &left, &ptr); + } } return written; @@ -423,27 +415,27 @@ int xmpp_stanza_to_text(xmpp_stanza_t *stanza, length = 1024; buffer = xmpp_alloc(stanza->ctx, length); if (!buffer) { - *buf = NULL; - *buflen = 0; - return XMPP_EMEM; + *buf = NULL; + *buflen = 0; + return XMPP_EMEM; } ret = _render_stanza_recursive(stanza, buffer, length); if (ret < 0) return ret; - if (ret > length - 1) { - tmp = xmpp_realloc(stanza->ctx, buffer, ret + 1); - if (!tmp) { - xmpp_free(stanza->ctx, buffer); - *buf = NULL; - *buflen = 0; - return XMPP_EMEM; - } - length = ret + 1; - buffer = tmp; - - ret = _render_stanza_recursive(stanza, buffer, length); - if (ret > length - 1) return XMPP_EMEM; + if ((size_t)ret > length - 1) { + tmp = xmpp_realloc(stanza->ctx, buffer, ret + 1); + if (!tmp) { + xmpp_free(stanza->ctx, buffer); + *buf = NULL; + *buflen = 0; + return XMPP_EMEM; + } + length = ret + 1; + buffer = tmp; + + ret = _render_stanza_recursive(stanza, buffer, length); + if ((size_t)ret > length - 1) return XMPP_EMEM; } buffer[length - 1] = 0; @@ -465,7 +457,7 @@ int xmpp_stanza_to_text(xmpp_stanza_t *stanza, * @ingroup Stanza */ int xmpp_stanza_set_name(xmpp_stanza_t *stanza, - const char * const name) + const char * const name) { if (stanza->type == XMPP_STANZA_TEXT) return XMPP_EINVOP; @@ -504,7 +496,7 @@ const char *xmpp_stanza_get_name(xmpp_stanza_t * const stanza) int xmpp_stanza_get_attribute_count(xmpp_stanza_t * const stanza) { if (stanza->attributes == NULL) { - return 0; + return 0; } return hash_num_keys(stanza->attributes); @@ -525,30 +517,30 @@ int xmpp_stanza_get_attribute_count(xmpp_stanza_t * const stanza) * @ingroup Stanza */ int xmpp_stanza_get_attributes(xmpp_stanza_t * const stanza, - const char **attr, int attrlen) + const char **attr, int attrlen) { hash_iterator_t *iter; const char *key; int num = 0; if (stanza->attributes == NULL) { - return 0; + return 0; } iter = hash_iter_new(stanza->attributes); while ((key = hash_iter_next(iter)) != NULL && attrlen) { - attr[num++] = key; - attrlen--; - if (attrlen == 0) { - hash_iter_release(iter); - return num; - } - attr[num++] = hash_get(stanza->attributes, key); - attrlen--; - if (attrlen == 0) { - hash_iter_release(iter); - return num; - } + attr[num++] = key; + attrlen--; + if (attrlen == 0) { + hash_iter_release(iter); + return num; + } + attr[num++] = hash_get(stanza->attributes, key); + attrlen--; + if (attrlen == 0) { + hash_iter_release(iter); + return num; + } } hash_iter_release(iter); @@ -566,16 +558,17 @@ int xmpp_stanza_get_attributes(xmpp_stanza_t * const stanza, * @ingroup Stanza */ int xmpp_stanza_set_attribute(xmpp_stanza_t * const stanza, - const char * const key, - const char * const value) + const char * const key, + const char * const value) { char *val; + int rc; if (stanza->type != XMPP_STANZA_TAG) return XMPP_EINVOP; if (!stanza->attributes) { - stanza->attributes = hash_new(stanza->ctx, 8, xmpp_free); - if (!stanza->attributes) return XMPP_EMEM; + stanza->attributes = hash_new(stanza->ctx, 8, xmpp_free); + if (!stanza->attributes) return XMPP_EMEM; } val = xmpp_strdup(stanza->ctx, value); @@ -584,7 +577,11 @@ int xmpp_stanza_set_attribute(xmpp_stanza_t * const stanza, return XMPP_EMEM; } - hash_add(stanza->attributes, key, val); + rc = hash_add(stanza->attributes, key, val); + if (rc < 0) { + xmpp_free(stanza->ctx, val); + return XMPP_EMEM; + } return XMPP_EOK; } @@ -601,7 +598,7 @@ int xmpp_stanza_set_attribute(xmpp_stanza_t * const stanza, * @ingroup Stanza */ int xmpp_stanza_set_ns(xmpp_stanza_t * const stanza, - const char * const ns) + const char * const ns) { return xmpp_stanza_set_attribute(stanza, "xmlns", ns); } @@ -627,12 +624,12 @@ int xmpp_stanza_add_child(xmpp_stanza_t *stanza, xmpp_stanza_t *child) child->parent = stanza; if (!stanza->children) - stanza->children = child; + stanza->children = child; else { - s = stanza->children; - while (s->next) s = s->next; - s->next = child; - child->prev = s; + s = stanza->children; + while (s->next) s = s->next; + s->next = child; + child->prev = s; } return XMPP_EOK; @@ -652,7 +649,7 @@ int xmpp_stanza_add_child(xmpp_stanza_t *stanza, xmpp_stanza_t *child) * @ingroup Stanza */ int xmpp_stanza_set_text(xmpp_stanza_t *stanza, - const char * const text) + const char * const text) { if (stanza->type == XMPP_STANZA_TAG) return XMPP_EINVOP; @@ -679,8 +676,8 @@ int xmpp_stanza_set_text(xmpp_stanza_t *stanza, * @ingroup Stanza */ int xmpp_stanza_set_text_with_size(xmpp_stanza_t *stanza, - const char * const text, - const size_t size) + const char * const text, + const size_t size) { if (stanza->type == XMPP_STANZA_TAG) return XMPP_EINVOP; @@ -783,14 +780,14 @@ const char *xmpp_stanza_get_from(xmpp_stanza_t * const stanza) * @ingroup Stanza */ xmpp_stanza_t *xmpp_stanza_get_child_by_name(xmpp_stanza_t * const stanza, - const char * const name) + const char * const name) { xmpp_stanza_t *child; for (child = stanza->children; child; child = child->next) { - if (child->type == XMPP_STANZA_TAG && - (strcmp(name, xmpp_stanza_get_name(child)) == 0)) - break; + if (child->type == XMPP_STANZA_TAG && + (strcmp(name, xmpp_stanza_get_name(child)) == 0)) + break; } return child; @@ -809,14 +806,14 @@ xmpp_stanza_t *xmpp_stanza_get_child_by_name(xmpp_stanza_t * const stanza, * @ingroup Stanza */ xmpp_stanza_t *xmpp_stanza_get_child_by_ns(xmpp_stanza_t * const stanza, - const char * const ns) + const char * const ns) { xmpp_stanza_t *child; for (child = stanza->children; child; child = child->next) { - if (xmpp_stanza_get_ns(child) && - strcmp(ns, xmpp_stanza_get_ns(child)) == 0) - break; + if (xmpp_stanza_get_ns(child) && + strcmp(ns, xmpp_stanza_get_ns(child)) == 0) + break; } return child; @@ -869,16 +866,16 @@ char *xmpp_stanza_get_text(xmpp_stanza_t * const stanza) char *text; if (stanza->type == XMPP_STANZA_TEXT) { - if (stanza->data) - return xmpp_strdup(stanza->ctx, stanza->data); - else - return NULL; + if (stanza->data) + return xmpp_strdup(stanza->ctx, stanza->data); + else + return NULL; } len = 0; for (child = stanza->children; child; child = child->next) - if (child->type == XMPP_STANZA_TEXT) - len += strlen(child->data); + if (child->type == XMPP_STANZA_TEXT) + len += strlen(child->data); if (len == 0) return NULL; @@ -887,11 +884,11 @@ char *xmpp_stanza_get_text(xmpp_stanza_t * const stanza) len = 0; for (child = stanza->children; child; child = child->next) - if (child->type == XMPP_STANZA_TEXT) { - clen = strlen(child->data); - memcpy(&text[len], child->data, clen); - len += clen; - } + if (child->type == XMPP_STANZA_TEXT) { + clen = strlen(child->data); + memcpy(&text[len], child->data, clen); + len += clen; + } text[len] = 0; @@ -914,7 +911,7 @@ char *xmpp_stanza_get_text(xmpp_stanza_t * const stanza) const char *xmpp_stanza_get_text_ptr(xmpp_stanza_t * const stanza) { if (stanza->type == XMPP_STANZA_TEXT) - return stanza->data; + return stanza->data; return NULL; } @@ -931,7 +928,7 @@ const char *xmpp_stanza_get_text_ptr(xmpp_stanza_t * const stanza) * @ingroup Stanza */ int xmpp_stanza_set_id(xmpp_stanza_t * const stanza, - const char * const id) + const char * const id) { return xmpp_stanza_set_attribute(stanza, "id", id); } @@ -948,7 +945,7 @@ int xmpp_stanza_set_id(xmpp_stanza_t * const stanza, * @ingroup Stanza */ int xmpp_stanza_set_type(xmpp_stanza_t * const stanza, - const char * const type) + const char * const type) { return xmpp_stanza_set_attribute(stanza, "type", type); } @@ -1004,10 +1001,10 @@ const char *xmpp_stanza_get_attribute(xmpp_stanza_t * const stanza, const char * const name) { if (stanza->type != XMPP_STANZA_TAG) - return NULL; + return NULL; if (!stanza->attributes) - return NULL; + return NULL; return hash_get(stanza->attributes, name); } @@ -1047,7 +1044,12 @@ int xmpp_stanza_del_attribute(xmpp_stanza_t * const stanza, */ xmpp_stanza_t *xmpp_stanza_reply(xmpp_stanza_t * const stanza) { - xmpp_stanza_t *copy; + xmpp_stanza_t *copy = NULL; + const char *from; + int rc; + + from = xmpp_stanza_get_from(stanza); + if (!from) goto copy_error; copy = xmpp_stanza_new(stanza->ctx); if (!copy) goto copy_error; @@ -1060,12 +1062,14 @@ xmpp_stanza_t *xmpp_stanza_reply(xmpp_stanza_t * const stanza) } if (stanza->attributes) { - if (_stanza_copy_attributes(copy, stanza) == -1) + if (_stanza_copy_attributes(copy, stanza) < 0) goto copy_error; } - xmpp_stanza_set_to(copy, xmpp_stanza_get_from(stanza)); + xmpp_stanza_del_attribute(copy, "to"); xmpp_stanza_del_attribute(copy, "from"); + rc = xmpp_stanza_set_to(copy, from); + if (rc != XMPP_EOK) goto copy_error; return copy; @@ -1214,3 +1218,122 @@ xmpp_stanza_t *xmpp_presence_new(xmpp_ctx_t *ctx) { return _stanza_new_with_attrs(ctx, "presence", NULL, NULL, NULL); } + +/** Create an stanza object with given type and error text. + * The error text is optional and may be NULL. + * + * @param ctx a Strophe context object + * @param type enum of xmpp_error_type_t + * @param text content of a 'text' + * + * @return a new Strophe stanza object + * + * @todo Handle errors in this function + * + * @ingroup Stanza + */ +xmpp_stanza_t *xmpp_error_new(xmpp_ctx_t *ctx, xmpp_error_type_t const type, + const char * const text) +{ + xmpp_stanza_t *error = _stanza_new_with_attrs(ctx, "stream:error", NULL, NULL, NULL); + xmpp_stanza_t *error_type = xmpp_stanza_new(ctx); + + switch(type) { + case XMPP_SE_BAD_FORMAT: + xmpp_stanza_set_name(error_type, "bad-format"); + break; + case XMPP_SE_BAD_NS_PREFIX: + xmpp_stanza_set_name(error_type, "bad-namespace-prefix"); + break; + case XMPP_SE_CONFLICT: + xmpp_stanza_set_name(error_type, "conflict"); + break; + case XMPP_SE_CONN_TIMEOUT: + xmpp_stanza_set_name(error_type, "connection-timeout"); + break; + case XMPP_SE_HOST_GONE: + xmpp_stanza_set_name(error_type, "host-gone"); + break; + case XMPP_SE_HOST_UNKNOWN: + xmpp_stanza_set_name(error_type, "host-unknown"); + break; + case XMPP_SE_IMPROPER_ADDR: + xmpp_stanza_set_name(error_type, "improper-addressing"); + break; + case XMPP_SE_INTERNAL_SERVER_ERROR: + xmpp_stanza_set_name(error_type, "internal-server-error"); + break; + case XMPP_SE_INVALID_FROM: + xmpp_stanza_set_name(error_type, "invalid-from"); + break; + case XMPP_SE_INVALID_ID: + xmpp_stanza_set_name(error_type, "invalid-id"); + break; + case XMPP_SE_INVALID_NS: + xmpp_stanza_set_name(error_type, "invalid-namespace"); + break; + case XMPP_SE_INVALID_XML: + xmpp_stanza_set_name(error_type, "invalid-xml"); + break; + case XMPP_SE_NOT_AUTHORIZED: + xmpp_stanza_set_name(error_type, "not-authorized"); + break; + case XMPP_SE_POLICY_VIOLATION: + xmpp_stanza_set_name(error_type, "policy-violation"); + break; + case XMPP_SE_REMOTE_CONN_FAILED: + xmpp_stanza_set_name(error_type, "remote-connection-failed"); + break; + case XMPP_SE_RESOURCE_CONSTRAINT: + xmpp_stanza_set_name(error_type, "resource-constraint"); + break; + case XMPP_SE_RESTRICTED_XML: + xmpp_stanza_set_name(error_type, "restricted-xml"); + break; + case XMPP_SE_SEE_OTHER_HOST: + xmpp_stanza_set_name(error_type, "see-other-host"); + break; + case XMPP_SE_SYSTEM_SHUTDOWN: + xmpp_stanza_set_name(error_type, "system-shutdown"); + break; + case XMPP_SE_UNDEFINED_CONDITION: + xmpp_stanza_set_name(error_type, "undefined-condition"); + break; + case XMPP_SE_UNSUPPORTED_ENCODING: + xmpp_stanza_set_name(error_type, "unsupported-encoding"); + break; + case XMPP_SE_UNSUPPORTED_STANZA_TYPE: + xmpp_stanza_set_name(error_type, "unsupported-stanza-type"); + break; + case XMPP_SE_UNSUPPORTED_VERSION: + xmpp_stanza_set_name(error_type, "unsupported-version"); + break; + case XMPP_SE_XML_NOT_WELL_FORMED: + xmpp_stanza_set_name(error_type, "xml-not-well-formed"); + break; + default: + xmpp_stanza_set_name(error_type, "internal-server-error"); + break; + } + + xmpp_stanza_set_ns(error_type, XMPP_NS_STREAMS_IETF); + xmpp_stanza_add_child(error, error_type); + xmpp_stanza_release(error_type); + + if (text) { + xmpp_stanza_t *error_text = xmpp_stanza_new(ctx); + xmpp_stanza_t *content = xmpp_stanza_new(ctx); + + xmpp_stanza_set_name(error_text, "text"); + xmpp_stanza_set_ns(error_text, XMPP_NS_STREAMS_IETF); + + xmpp_stanza_set_text(content, text); + xmpp_stanza_add_child(error_text, content); + xmpp_stanza_release(content); + + xmpp_stanza_add_child(error, error_text); + xmpp_stanza_release(error_text); + } + + return error; +} diff --git a/src/thread.c b/src/thread.c deleted file mode 100644 index 3a354de..0000000 --- a/src/thread.c +++ /dev/null @@ -1,83 +0,0 @@ -/* thread.c -** strophe XMPP client library -- thread abstraction -** -** Copyright (C) 2005-2009 Collecta, Inc. -** -** This software is provided AS-IS with no warranty, either express -** or implied. -** -** This program is dual licensed under the MIT and GPLv3 licenses. -*/ - -/** @file - * Thread absraction. - */ - -#include -#include - -#include - -#include "mesode.h" -#include "common.h" -#include "thread.h" - -struct _mutex_t { - const xmpp_ctx_t *ctx; - pthread_mutex_t *mutex; -}; - -/* mutex functions */ - -mutex_t *mutex_create(const xmpp_ctx_t * ctx) -{ - mutex_t *mutex; - - mutex = xmpp_alloc(ctx, sizeof(mutex_t)); - if (mutex) { - mutex->ctx = ctx; - mutex->mutex = xmpp_alloc(ctx, sizeof(pthread_mutex_t)); - if (mutex->mutex) - if (pthread_mutex_init(mutex->mutex, NULL) != 0) { - xmpp_free(ctx, mutex->mutex); - mutex->mutex = NULL; - } - if (!mutex->mutex) { - xmpp_free(ctx, mutex); - mutex = NULL; - } - } - - return mutex; -} - -int mutex_destroy(mutex_t *mutex) -{ - int ret = 1; - const xmpp_ctx_t *ctx; - - if (mutex->mutex) - ret = pthread_mutex_destroy(mutex->mutex) == 0; - ctx = mutex->ctx; - xmpp_free(ctx, mutex); - - return ret; -} - -int mutex_lock(mutex_t *mutex) -{ - int ret = pthread_mutex_lock(mutex->mutex) == 0; - return ret; -} - -int mutex_trylock(mutex_t *mutex) -{ - /* TODO */ - return 0; -} - -int mutex_unlock(mutex_t *mutex) -{ - int ret = pthread_mutex_unlock(mutex->mutex) == 0; - return ret; -} diff --git a/src/thread.h b/src/thread.h deleted file mode 100644 index 4d31f1c..0000000 --- a/src/thread.h +++ /dev/null @@ -1,36 +0,0 @@ -/* thread.h -** strophe XMPP client library -- thread abstraction header -** -** Copyright (C) 2005-2009 Collecta, Inc. -** -** This software is provided AS-IS with no warranty, either express -** or implied. -** -** This program is dual licensed under the MIT and GPLv3 licenses. -*/ - -/** @file - * Threading abstraction API. - */ - -#ifndef __LIBMESODE_THREAD_H__ -#define __LIBMESODE_THREAD_H__ - -#include -#include - -#include - -#include "mesode.h" - -typedef struct _mutex_t mutex_t; - -/* mutex functions */ - -mutex_t *mutex_create(const xmpp_ctx_t *ctx); -int mutex_destroy(mutex_t *mutex); -int mutex_lock(mutex_t *mutex); -int mutex_trylock(mutex_t *mutex); -int mutex_unlock(mutex_t *mutex); - -#endif /* __LIBMESODE_THREAD_H__ */ diff --git a/src/tls.h b/src/tls.h index 73bad97..550d1c3 100644 --- a/src/tls.h +++ b/src/tls.h @@ -36,7 +36,7 @@ struct _tlscert_t { void tls_initialize(void); void tls_shutdown(void); -tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock, xmpp_certfail_handler certfail_handler, char *tls_cert_path); +tls_t *tls_new(xmpp_conn_t *conn); void tls_free(tls_t *tls); xmpp_tlscert_t* tls_peer_cert(xmpp_conn_t *conn); diff --git a/src/tls_dummy.c b/src/tls_dummy.c index 3c21113..21c57ab 100644 --- a/src/tls_dummy.c +++ b/src/tls_dummy.c @@ -33,7 +33,7 @@ void tls_shutdown(void) return; } -tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock) +tls_t *tls_new(xmpp_conn_t *conn) { /* always fail */ return NULL; diff --git a/src/tls_openssl.c b/src/tls_openssl.c index 94e4c2b..83e52f9 100644 --- a/src/tls_openssl.c +++ b/src/tls_openssl.c @@ -20,6 +20,8 @@ #include #include +#include +#include #include "common.h" #include "tls.h" @@ -45,13 +47,34 @@ static void _tls_log_error(xmpp_ctx_t *ctx); void tls_initialize(void) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L SSL_library_init(); SSL_load_error_strings(); +#else + OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); +#endif } void tls_shutdown(void) { - return; + /* + * FIXME: Don't free global tables, program or other libraries may use + * openssl after libstrophe finalization. Maybe better leak some fixed + * memory rather than cause random crashes of the main program. + */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L + ERR_free_strings(); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + SSL_COMP_free_compression_methods(); +#endif +#if OPENSSL_VERSION_NUMBER < 0x10000000L + ERR_remove_state(0); +#else + ERR_remove_thread_state(NULL); +#endif +#endif } int tls_error(tls_t *tls) @@ -209,7 +232,7 @@ verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { STACK_OF(X509) *sk = X509_STORE_CTX_get1_chain(x509_ctx); int slen = sk_X509_num(sk); - unsigned i; + int i; X509 *certsk; xmpp_debug(_xmppctx, "TLS", "STACK"); for(i=0; ictx; + _certfail_handler = conn->certfail_handler; _cert_handled = 0; _last_cb_res = 0; - tls_t *tls = xmpp_alloc(ctx, sizeof(*tls)); + tls_t *tls = xmpp_alloc(conn->ctx, sizeof(*tls)); + int mode; - xmpp_debug(ctx, "TLS", "OpenSSL version: %s", SSLeay_version(SSLEAY_VERSION)); + xmpp_debug(conn->ctx, "TLS", "OpenSSL version: %s", SSLeay_version(SSLEAY_VERSION)); if (tls) { int ret; memset(tls, 0, sizeof(*tls)); - tls->ctx = ctx; - tls->sock = sock; + tls->ctx = conn->ctx; + tls->sock = conn->sock; tls->ssl_ctx = SSL_CTX_new(SSLv23_client_method()); if (tls->ssl_ctx == NULL) goto err; + /* Enable bug workarounds. */ + SSL_CTX_set_options(tls->ssl_ctx, SSL_OP_ALL); + + /* Disable insecure SSL/TLS versions. */ + SSL_CTX_set_options(tls->ssl_ctx, SSL_OP_NO_SSLv2); /* DROWN */ + SSL_CTX_set_options(tls->ssl_ctx, SSL_OP_NO_SSLv3); /* POODLE */ + SSL_CTX_set_options(tls->ssl_ctx, SSL_OP_NO_TLSv1); /* BEAST */ + SSL_CTX_set_client_cert_cb(tls->ssl_ctx, NULL); SSL_CTX_set_mode(tls->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); SSL_CTX_set_verify(tls->ssl_ctx, SSL_VERIFY_PEER, verify_callback); - if (tls_cert_path) { - SSL_CTX_load_verify_locations(tls->ssl_ctx, NULL, tls_cert_path); + if (conn->tls_cert_path) { + SSL_CTX_load_verify_locations(tls->ssl_ctx, NULL, conn->tls_cert_path); } + tls->ssl = SSL_new(tls->ssl_ctx); if (tls->ssl == NULL) goto err_free_ctx; - ret = SSL_set_fd(tls->ssl, sock); + /* Trust server's certificate when user sets the flag explicitly. */ + mode = conn->tls_trust ? SSL_VERIFY_NONE : SSL_VERIFY_PEER; + SSL_set_verify(tls->ssl, mode, 0); +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + /* Hostname verification is supported in OpenSSL 1.0.2 and newer. */ + X509_VERIFY_PARAM *param = SSL_get0_param(tls->ssl); + + /* + * Allow only complete wildcards. RFC 6125 discourages wildcard usage + * completely, and lists internationalized domain names as a reason + * against partial wildcards. + * See https://tools.ietf.org/html/rfc6125#section-7.2 for more information. + */ + X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); + X509_VERIFY_PARAM_set1_host(param, conn->domain, 0); +#endif + + ret = SSL_set_fd(tls->ssl, conn->sock); if (ret <= 0) goto err_free_ssl; } @@ -305,8 +356,8 @@ tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock, xmpp_certfail_handler certfail_hand err_free_ctx: SSL_CTX_free(tls->ssl_ctx); err: - xmpp_free(ctx, tls); - _tls_log_error(ctx); + xmpp_free(conn->ctx, tls); + _tls_log_error(conn->ctx); return NULL; } @@ -326,6 +377,7 @@ int tls_start(tls_t *tls) { int error; int ret; + long x509_res; /* Since we're non-blocking, loop the connect call until it succeeds or fails */ @@ -342,8 +394,12 @@ int tls_start(tls_t *tls) /* success or fatal error */ break; } - _tls_set_error(tls, error); + x509_res = SSL_get_verify_result(tls->ssl); + xmpp_debug(tls->ctx, "tls", "Certificate verification %s", + x509_res == X509_V_OK ? "passed" : "FAILED"); + + _tls_set_error(tls, error); return ret <= 0 ? 0 : 1; } @@ -363,6 +419,14 @@ int tls_stop(tls_t *tls) } _tls_sock_wait(tls, error); } + if (error == SSL_ERROR_SYSCALL && errno == 0) { + /* + * Handle special case when peer closes connection instead of + * proper shutdown. + */ + error = 0; + ret = 1; + } _tls_set_error(tls, error); return ret <= 0 ? 0 : 1; @@ -414,6 +478,8 @@ static void _tls_sock_wait(tls_t *tls, int error) int nfds; int ret; + if (error == SSL_ERROR_NONE) return; + FD_ZERO(&rfds); FD_ZERO(&wfds); if (error == SSL_ERROR_WANT_READ) @@ -432,6 +498,7 @@ static void _tls_sock_wait(tls_t *tls, int error) static void _tls_set_error(tls_t *tls, int error) { if (error != 0 && !tls_is_recoverable(error)) { + xmpp_debug(tls->ctx, "tls", "error=%d errno=%d", error, errno); _tls_log_error(tls->ctx); } tls->lasterror = error; diff --git a/tests/test_base64.c b/tests/test_base64.c index 1e84cfd..2c51861 100644 --- a/tests/test_base64.c +++ b/tests/test_base64.c @@ -134,8 +134,7 @@ int main(int argc, char *argv[]) unsigned char *udec; char *dec; char *enc; - size_t len; - int i; + size_t len, i; printf("BASE64 tests.\n"); diff --git a/tests/test_hash.c b/tests/test_hash.c index b75ba13..5eac9bb 100644 --- a/tests/test_hash.c +++ b/tests/test_hash.c @@ -76,6 +76,18 @@ int main(int argc, char **argv) return 1; } + /* test replacing old values */ + for (i = 0; i < nkeys; i++) { + err = hash_add(table, keys[0], (void*)values[i]); + if (err) return err; + if (hash_num_keys(table) != nkeys) return 1; + result = hash_get(table, keys[0]); + if (result == NULL) return 1; + if (strcmp(result, values[i]) != 0) return 1; + } + /* restore value for the 1st key */ + hash_add(table, keys[0], (void*)values[0]); + /* test cloning */ clone = hash_clone(table); diff --git a/tests/test_jid.c b/tests/test_jid.c index a7ba3eb..7b25b33 100644 --- a/tests/test_jid.c +++ b/tests/test_jid.c @@ -27,54 +27,67 @@ static const char *_s(const char *s) int test_jid(xmpp_ctx_t *ctx) { + char *bare; char *node; char *domain; char *resource; + bare = xmpp_jid_bare(ctx, jid1); node = xmpp_jid_node(ctx, jid1); domain = xmpp_jid_domain(ctx, jid1); resource = xmpp_jid_resource(ctx, jid1); printf("jid '%s' parsed to %s, %s, %s\n", jid1, _s(node), _s(domain), _s(resource)); + if (bare == NULL || strcmp(bare, "foo@bar.com")) return 1; if (node == NULL || strcmp(node, "foo")) return 1; if (domain == NULL || strcmp(domain, "bar.com")) return 1; if (resource != NULL) return 1; + if (bare) xmpp_free(ctx, bare); if (node) xmpp_free(ctx, node); if (domain) xmpp_free(ctx, domain); if (resource) xmpp_free(ctx, resource); + bare = xmpp_jid_bare(ctx, jid2); node = xmpp_jid_node(ctx, jid2); domain = xmpp_jid_domain(ctx, jid2); resource = xmpp_jid_resource(ctx, jid2); printf("jid '%s' parsed to %s, %s, %s\n", jid2, _s(node), _s(domain), _s(resource)); + if (bare == NULL || strcmp(bare, "anyone@example.com")) return 1; if (node == NULL || strcmp(node, "anyone")) return 1; if (domain == NULL || strcmp(domain, "example.com")) return 1; if (resource == NULL || strcmp(resource, "hullo")) return 1; + if (bare) xmpp_free(ctx, bare); if (node) xmpp_free(ctx, node); if (domain) xmpp_free(ctx, domain); if (resource) xmpp_free(ctx, resource); + bare = xmpp_jid_bare(ctx, jid3); node = xmpp_jid_node(ctx, jid3); domain = xmpp_jid_domain(ctx, jid3); resource = xmpp_jid_resource(ctx, jid3); printf("jid '%s' parsed to %s, %s, %s\n", jid3, _s(node), _s(domain), _s(resource)); + if (bare == NULL || strcmp(bare, "manic.porter@xyz.net")) return 1; if (node == NULL || strcmp(node, "manic.porter")) return 1; if (domain == NULL || strcmp(domain, "xyz.net")) return 1; if (resource == NULL || strcmp(resource, "frob")) return 1; + if (bare) xmpp_free(ctx, bare); if (node) xmpp_free(ctx, node); if (domain) xmpp_free(ctx, domain); if (resource) xmpp_free(ctx, resource); + bare = xmpp_jid_bare(ctx, jid4); node = xmpp_jid_node(ctx, jid4); domain = xmpp_jid_domain(ctx, jid4); resource = xmpp_jid_resource(ctx, jid4); printf("jid '%s' parsed to %s, %s, %s\n", jid4, _s(node), _s(domain), _s(resource)); + if (bare == NULL || strcmp(bare, "domain.tld")) return 1; if (node != NULL) return 1; if (domain == NULL || strcmp(domain, "domain.tld")) return 1; if (resource != NULL) return 1; + if (bare) xmpp_free(ctx, bare); if (node) xmpp_free(ctx, node); if (domain) xmpp_free(ctx, domain); if (resource) xmpp_free(ctx, resource); diff --git a/tests/test_string.c b/tests/test_string.c index c93ad60..6f8b800 100644 --- a/tests/test_string.c +++ b/tests/test_string.c @@ -20,6 +20,16 @@ #include "test.h" /* ARRAY_SIZE */ +/* strtok_s() has appeared in visual studio 2005. + Use own implementation for older versions. */ +#ifdef _MSC_VER +# if (_MSC_VER >= 1400) +# define strtok_r strtok_s +# else +# define strtok_r xmpp_strtok_r +# endif +#endif /* _MSC_VER */ + static int test_strtok_r(void) { const char *test = "-abc-=-def--"; @@ -79,7 +89,7 @@ static int test_strdup_one(xmpp_ctx_t *ctx, const char *s) static int test_strdup(void) { xmpp_ctx_t *ctx; - int i; + size_t i; int rc = 0; static const char *tests[] = { "", "\0", "test", "s p a c e", "\n\r" };