9 Commits

Author SHA1 Message Date
Steffen Jaeckel
dfb3e86824 Use void* to store function pointer when compiled as C23.
[0] decided that `rettype (*foo)();` must now be interpreted as
`rettype (*foo)(void);`. Luckily it also allows now to store function
pointers in a `void*` (c.f. Ch. J.5.7).

[0]: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3096.pdf

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
2025-03-26 17:24:12 +01:00
Steffen Jaeckel
a81f0a8a5f Auto-format according to new clang-format.
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
2025-03-26 15:18:52 +01:00
Steffen Jaeckel
5509ec3b35 Update CI
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
2025-03-26 14:17:54 +01:00
Steffen Jaeckel
67b1a2f3f4 Add support for DragonFlyBSD in configure.ac
Hinted to by gnemmi in profanity@rooms.dismail.de

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
2025-03-13 19:49:14 +01:00
Steffen Jaeckel
7c2e78937d Release libstrophe-0.14.0
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2025-03-13 14:44:37 +01:00
Steffen Jaeckel
b7bae1502f Update docs of SM-serialization.
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2025-03-13 14:44:08 +01:00
Steffen Jaeckel
e3d734c5ff Fix SM-serialization testcase
`xmpp_run_once()` uses `FD_SET()` to determine which sockets to `select()`
on. The socket gets initialized to `INVALID_SOCKET = -1` inside
`xmpp_conn_new()` which leads to a buffer overflow once `FD_SET()` is
called.

This is only exposed when enabling a higher optimization level, which
was only done in the `release-test` CI job.

* Fix this testcase by initializing the socket to a possible value.
* Build the Valgrind CI jobs with `-O2`.
* Make the output of the `release-test` more verbose.

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
2024-11-19 16:26:50 +01:00
Steffen Jaeckel
e7b08faaa7 Clean up SM state serialization
* Refactor `xmpp_sm_state_restore()`

Add new internal functions to load u32 and string values. This also brings
length checks in order to verify we don't read more sm_state than there
potentially is and type checks to ensure we have the correct CBOR types.

Each element is added to the queue right after creation, so it won't leak
in case creating its content fails.

* Refactor `sm_state_serialize()`

* Store & restore ints as/from big endian, CBOR requires that

* Bring API names in line with the usual naming

* Auto-format sources

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
2024-11-19 16:26:50 +01:00
Stephen Paul Weber
f3fa9f557b Allow serializing SM state
It is useful in some applications to get a snapshot of the stream
management state for storage outside the process, in order to recover
from crashes and other things.  The get_sm_state already present is not
suitable for this because it returns a live object tied in to the
current context and such, and containing much unneeded internal-api
data.

Introduce xmpp_sm_state_set_callback which is called every time the
state changes with a serialized state, and a dual xmpp_sm_state_restore
which takes in this serialized state and sets up a new sm_state based on
that.

The serialization is considered opaque from the PoV of the API, but is
based on CBOR to facilitate easy debugging.
2024-11-19 16:26:49 +01:00
31 changed files with 852 additions and 563 deletions

View File

@@ -10,12 +10,12 @@ on:
jobs:
linux-tests:
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
strategy:
matrix:
valgrind:
- { configure: '' , make: 'check' }
- { configure: '--enable-valgrind' , make: 'check-valgrind' }
- { configure: '' , cflags: '', make: 'check' }
- { configure: '--enable-valgrind' , cflags: '-O2', make: 'check-valgrind' }
options:
- { configure: '' }
- { configure: '--without-libxml2' }
@@ -35,7 +35,7 @@ jobs:
- name: Build the library
run: |
./bootstrap.sh
./configure ${{ matrix.options.configure }} ${{ matrix.valgrind.configure }} CFLAGS="-Werror -g3"
./configure ${{ matrix.options.configure }} ${{ matrix.valgrind.configure }} CFLAGS="-Werror -g3 ${{ matrix.valgrind.cflags }}"
make -j$(nproc)
- name: Run tests
run: |
@@ -46,7 +46,7 @@ jobs:
cat test-suite*.log || true
xssl-tests:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
strategy:
matrix:
xssl_versions:
@@ -135,9 +135,14 @@ jobs:
if: ${{ !failure() }}
run: |
cat testbuild.log
- name: Error logs
if: ${{ failure() }}
run: |
cat testbuild.log || true
cat testerr.log || true
code-style:
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
name: Check coding style
continue-on-error: true
steps:

2
.gitignore vendored
View File

@@ -46,6 +46,7 @@ examples/roster
examples/uuid
examples/vcard
testbuild*.log
testerr*.log
test-release/
test_stamp
test-suite*.log
@@ -65,6 +66,7 @@ tests/test_resolver
tests/test_sasl
tests/test_scram
tests/test_send_queue
tests/test_serialize_sm
tests/test_sha1
tests/test_sha256
tests/test_sha512

View File

@@ -1,3 +1,13 @@
0.14.0
- Add SM-state serialization mechanism (#239)
- Add support for `SCRAM-SHA-512-PLUS` (da122981)
- Add support for `SSLKEYLOGFILE` with OpenSSL (d82df127)
- Fix some issues related to Stream Management (03d43132, 4359536a)
- Fix a potential segfault (9fef4b7d)
- New API:
- xmpp_conn_set_sm_callback()
- xmpp_conn_restore_sm_state()
0.13.1
- Fix SCRAM-*-PLUS SASL mechanisms with OpenSSL and TLS < v1.3 (40f2452)
- Only signal "stream negotiation success" once (1cf09b1)

View File

@@ -38,7 +38,7 @@ PROJECT_NAME = Strophe
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 0.13
PROJECT_NUMBER = 0.14
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a

View File

@@ -190,6 +190,7 @@ STATIC_TESTS = \
tests/test_jid \
tests/test_ctx \
tests/test_send_queue \
tests/test_serialize_sm \
tests/test_string \
tests/test_resolver
@@ -285,6 +286,11 @@ tests_test_send_queue_CFLAGS = -I$(top_srcdir)/src
tests_test_send_queue_LDADD = $(STROPHE_LIBS)
tests_test_send_queue_LDFLAGS = -static
tests_test_serialize_sm_SOURCES = tests/test_serialize_sm.c tests/test.c tests/test.h
tests_test_serialize_sm_CFLAGS = -I$(top_srcdir)/src
tests_test_serialize_sm_LDADD = $(STROPHE_LIBS)
tests_test_serialize_sm_LDFLAGS = -static
tests_test_snprintf_SOURCES = tests/test_snprintf.c
tests_test_snprintf_CFLAGS = -I$(top_srcdir)/src
@@ -327,6 +333,7 @@ dist-archives:
test-release: dist
@touch testbuild-$(PACKAGE_VERSION).log && ln -sf testbuild-$(PACKAGE_VERSION).log testbuild.log
@touch testerr-$(PACKAGE_VERSION).log && ln -sf testerr-$(PACKAGE_VERSION).log testerr.log
@mkdir -p test-release && cp $(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.* test-release && pushd test-release && \
tar xzf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.gz && pushd $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && ./testbuild.sh && popd && rm -rf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && \
echo "Success" && popd

View File

@@ -1,6 +1,6 @@
m4_define([v_maj], [0])
m4_define([v_min], [13])
m4_define([v_patch], [1])
m4_define([v_min], [14])
m4_define([v_patch], [0])
m4_define([project_version], [v_maj.v_min.v_patch])
m4_define([lt_cur], m4_eval(v_maj + v_min))
@@ -18,9 +18,10 @@ PKG_PROG_PKG_CONFIG
AC_CANONICAL_HOST
AS_CASE([$host_os],
[dragonfly*], [PLATFORM="bsd"],
[freebsd*], [PLATFORM="bsd"],
[openbsd*], [PLATFORM="bsd"],
[netbsd*], [PLATFORM="bsd"],
[openbsd*], [PLATFORM="bsd"],
[*nto*|*qnx*], [PLATFORM="qnx"],
[*solaris*], [PLATFORM="solaris"],
[*android*], [PLATFORM="android"],

View File

@@ -115,8 +115,8 @@ int main(int argc, char **argv)
*/
/* setup authentication information */
xmpp_conn_set_string(conn, XMPP_SETTING_JID, argv[1]);
xmpp_conn_set_string(conn, XMPP_SETTING_PASS, argv[2]);
xmpp_conn_set_jid(conn, argv[1]);
xmpp_conn_set_pass(conn, argv[2]);
/* initiate connection */
xmpp_connect_client(conn, NULL, 0, conn_handler, ctx);

View File

@@ -116,9 +116,9 @@ int main(int argc, char **argv)
/* setup authentication information */
if (jid)
xmpp_conn_set_string(conn, XMPP_SETTING_JID, jid);
xmpp_conn_set_jid(conn, jid);
if (password)
xmpp_conn_set_string(conn, XMPP_SETTING_PASS, password);
xmpp_conn_set_pass(conn, password);
/* initiate connection */
if (xmpp_connect_client(conn, host, port, conn_handler, ctx) == XMPP_EOK) {

View File

@@ -293,24 +293,20 @@ create_connection:
xmpp_conn_set_flags(conn, flags);
/* ask for a password if key is protected */
xmpp_conn_set_functionpointer(conn, XMPP_SETTING_PASSWORD_CALLBACK,
password_callback);
xmpp_conn_set_password_callback(conn, password_callback, NULL);
/* try at max 3 times in case the user enters the password wrong */
xmpp_conn_set_int(conn, XMPP_SETTING_PASSWORD_RETRIES, 3);
xmpp_conn_set_password_retries(conn, 3);
/* setup authentication information */
if (key) {
xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_CERT, cert);
xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_KEY, key);
}
if (key)
xmpp_conn_set_client_cert(conn, cert, key);
if (jid)
xmpp_conn_set_string(conn, XMPP_SETTING_JID, jid);
xmpp_conn_set_jid(conn, jid);
if (password)
xmpp_conn_set_string(conn, XMPP_SETTING_PASS, password);
xmpp_conn_set_pass(conn, password);
/* enable TCP keepalive, using canned callback function */
if (tcp_keepalive)
xmpp_conn_set_functionpointer(conn, XMPP_SETTING_SOCKOPT_CALLBACK,
xmpp_sockopt_cb_keepalive);
xmpp_conn_set_sockopt_callback(conn, xmpp_sockopt_cb_keepalive);
/* set Stream-Mangement state if available */
if (sm_state) {

View File

@@ -331,31 +331,27 @@ int main(int argc, char **argv)
xmpp_conn_set_flags(conn, flags);
/* configure TCP keepalive (optional) */
if (tcp_keepalive)
xmpp_conn_set_functionpointer(conn, XMPP_SETTING_SOCKOPT_CALLBACK,
sockopt_cb);
xmpp_conn_set_sockopt_callback(conn, sockopt_cb);
/* ask for a password if key is protected */
xmpp_conn_set_functionpointer(conn, XMPP_SETTING_PASSWORD_CALLBACK,
password_callback);
xmpp_conn_set_password_callback(conn, password_callback, NULL);
/* try at max 3 times in case the user enters the password wrong */
xmpp_conn_set_int(conn, XMPP_SETTING_PASSWORD_RETRIES, 3);
xmpp_conn_set_password_retries(conn, 3);
/* setup authentication information */
if (key) {
xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_CERT, cert);
xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_KEY, key);
xmpp_conn_set_client_cert(conn, cert, key);
}
if (jid)
xmpp_conn_set_string(conn, XMPP_SETTING_JID, jid);
xmpp_conn_set_jid(conn, jid);
if (password)
xmpp_conn_set_string(conn, XMPP_SETTING_PASS, password);
xmpp_conn_set_pass(conn, password);
if (certfail)
xmpp_conn_set_functionpointer(conn, XMPP_SETTING_CERTFAIL_HANDLER,
certfail_handler);
xmpp_conn_set_certfail_handler(conn, certfail_handler);
if (capath)
xmpp_conn_set_string(conn, XMPP_SETTING_CAPATH, capath);
xmpp_conn_set_capath(conn, capath);
if (cafile)
xmpp_conn_set_string(conn, XMPP_SETTING_CAFILE, cafile);
xmpp_conn_set_cafile(conn, cafile);
/* initiate connection */
if (xmpp_connect_client(conn, host, port, conn_handler, ctx) == XMPP_EOK) {

View File

@@ -80,8 +80,8 @@ int main(int argc, char **argv)
conn = xmpp_conn_new(ctx);
/* setup authentication information */
xmpp_conn_set_string(conn, XMPP_SETTING_JID, jid);
xmpp_conn_set_string(conn, XMPP_SETTING_PASS, pass);
xmpp_conn_set_jid(conn, jid);
xmpp_conn_set_pass(conn, pass);
/* initiate connection */
xmpp_connect_component(conn, host, port, conn_handler, ctx);

View File

@@ -342,7 +342,7 @@ int main(int argc, char **argv)
/* jid can be a jid or domain for "raw" connection */
domain = xmpp_jid_domain(ctx, jid);
xmpp_conn_set_string(conn, XMPP_SETTING_JID, domain);
xmpp_conn_set_jid(conn, domain);
xmpp_free(ctx, domain);
/* private data */

View File

@@ -118,8 +118,8 @@ int main(int argc, char **argv)
*/
/* setup authentication information */
xmpp_conn_set_string(conn, XMPP_SETTING_JID, argv[1]);
xmpp_conn_set_string(conn, XMPP_SETTING_PASS, argv[2]);
xmpp_conn_set_jid(conn, argv[1]);
xmpp_conn_set_pass(conn, argv[2]);
/* initiate connection */
xmpp_connect_client(conn, NULL, 0, conn_handler, ctx);

View File

@@ -269,8 +269,8 @@ int main(int argc, char **argv)
log = xmpp_get_default_logger(XMPP_LEVEL_INFO);
ctx = xmpp_ctx_new(NULL, log);
conn = xmpp_conn_new(ctx);
xmpp_conn_set_string(conn, XMPP_SETTING_JID, jid);
xmpp_conn_set_string(conn, XMPP_SETTING_PASS, pass);
xmpp_conn_set_jid(conn, jid);
xmpp_conn_set_pass(conn, pass);
vcard.ctx = ctx;
xmpp_connect_client(conn, NULL, 0, conn_handler, &vcard);
xmpp_run(ctx);

View File

@@ -1232,6 +1232,7 @@ static void _sm_enable(xmpp_conn_t *conn)
send_stanza(conn, enable, XMPP_QUEUE_SM_STROPHE);
conn->sm_state->sm_sent_nr = 0;
conn->sm_state->sm_enabled = 1;
trigger_sm_callback(conn);
}
static int
@@ -1486,6 +1487,8 @@ static int _handle_sm(xmpp_conn_t *const conn,
name = NULL;
}
trigger_sm_callback(conn);
err_sm:
if (!name) {
char *err = "Couldn't convert stanza to text!";

View File

@@ -30,12 +30,17 @@
#include "snprintf.h"
/** handlers **/
#if (__STDC_VERSION__ >= 202000L)
typedef void* xmpp_void_handler;
#else
typedef int (*xmpp_void_handler)();
#endif
typedef struct _xmpp_handlist_t xmpp_handlist_t;
struct _xmpp_handlist_t {
/* common members */
int user_handler;
int (*handler)();
xmpp_void_handler handler;
void *userdata;
int enabled; /* handlers are added disabled and enabled after the
* handler chain is processed to prevent stanzas from
@@ -280,8 +285,7 @@ struct _xmpp_conn_t {
struct {
struct xmpp_compression *state;
int allowed, dont_reset, level;
int supported;
int allowed, supported, dont_reset;
} compression;
char *lang;
@@ -324,6 +328,8 @@ struct _xmpp_conn_t {
hash_t *id_handlers;
xmpp_handlist_t *handlers;
xmpp_sockopt_callback sockopt_cb;
xmpp_sm_callback sm_callback;
void *sm_callback_ctx;
};
void conn_disconnect(xmpp_conn_t *conn);
@@ -377,6 +383,7 @@ void handler_add(xmpp_conn_t *conn,
void handler_system_delete_all(xmpp_conn_t *conn);
/* utility functions */
void trigger_sm_callback(xmpp_conn_t *conn);
void reset_sm_state(xmpp_sm_state_t *sm_state);
void disconnect_mem_error(xmpp_conn_t *conn);

View File

@@ -227,7 +227,7 @@ int compression_init(xmpp_conn_t *conn)
comp->compression.stream.next_out = comp->compression.buffer;
comp->compression.stream.avail_out = STROPHE_COMPRESSION_BUFFER_SIZE;
int ret = deflateInit(&comp->compression.stream, conn->compression.level);
int ret = deflateInit(&comp->compression.stream, Z_DEFAULT_COMPRESSION);
if (ret != Z_OK) {
strophe_free_and_null(conn->ctx, comp->compression.buffer);
conn->error = ret;

View File

@@ -21,6 +21,7 @@
*/
#include <errno.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <string.h>
#include <limits.h>
@@ -167,8 +168,6 @@ xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t *ctx)
tls_clear_password_cache(conn);
conn->password_retries = 1;
conn->compression.level = -1;
conn->parser =
parser_new(conn->ctx, _handle_stream_start, _handle_stream_end,
_handle_stream_stanza, conn);
@@ -219,6 +218,32 @@ xmpp_conn_t *xmpp_conn_clone(xmpp_conn_t *conn)
return conn;
}
/** Register sockopt callback
* Set function to be called when a new socket is created to allow setting
* socket options before connection is started.
*
* If the connection is already connected, this callback will be called
* immediately.
*
* To set options that can only be applied to disconnected sockets, the
* callback must be registered before connecting.
*
* @param conn The Strophe connection object this callback is being registered
* for
* @param callback a xmpp_sockopt_callback callback function that will receive
* notifications of connection status
*
* @ingroup Connections
*/
void xmpp_conn_set_sockopt_callback(xmpp_conn_t *conn,
xmpp_sockopt_callback callback)
{
conn->sockopt_cb = callback;
if (conn->state != XMPP_STATE_DISCONNECTED)
callback(conn, &conn->sock);
}
/** Release a Strophe connection object.
* Decrement the reference count by one for a connection, freeing the
* connection object if the count reaches 0.
@@ -342,204 +367,6 @@ int xmpp_conn_release(xmpp_conn_t *conn)
return released;
}
/** Configure a connection-related int setting
*
* `setting` can be one of:
* - \ref XMPP_SETTING_PASSWORD_RETRIES
* Set the number of retry attempts to decrypt a private key file. \n
* In case the user enters the password manually it can be useful to
* directly retry if the decryption of the key file failed.
*
* - \ref XMPP_SETTING_COMPRESSION_LEVEL
* Set the compression level. \n
* For zlib the valid range is `-1` to `9`.
*
* @param conn a Strophe connection object
* @param setting The setting that shall be configured
* @param value The value, the settings should get
*
* @ingroup Connections
*/
void xmpp_conn_set_int(xmpp_conn_t *conn,
xmpp_conn_setting_t setting,
int value)
{
switch (setting) {
case XMPP_SETTING_PASSWORD_RETRIES:
if (value <= 0)
conn->password_retries = 1;
else
conn->password_retries = value;
break;
case XMPP_SETTING_COMPRESSION_LEVEL:
conn->compression.level = value;
break;
default:
strophe_warn(conn->ctx, "xmpp", "Invalid Int setting %d", setting);
return;
}
}
/** Configure a connection-related string setting
*
* `setting` can be one of:
* - \ref XMPP_SETTING_JID
* Set the JID of the user that will be bound to the connection. \n
* This should not be used after a connection is created. \n
* If the supplied JID is missing the node, SASL ANONYMOUS authentication
* will be used.
*
* - \ref XMPP_SETTING_PASS
* Set the password used to authenticate the connection.
*
* - \ref XMPP_SETTING_CAFILE
* Set CAfile.
*
* - \ref XMPP_SETTING_CAPATH
* Set CApath.
*
* - \ref XMPP_SETTING_CLIENT_CERT
* Set the Client Certificate or PKCS#12 encoded file that will be bound to
* the connection. \n
* This should not be used after a connection is created. \n
* In case the PKCS#12 file is encrypted, a callback must be set via
* \ref xmpp_conn_set_functionpointer with the option
* \ref XMPP_SETTING_PASSWORD_CALLBACK so the TLS stack can retrieve the
* password.
*
* - \ref XMPP_SETTING_CLIENT_KEY
* Set the Private Key that will be bound to the connection. \n
* This should not be used after a connection is created. \n
* In case the Private Key is encrypted, a callback must be set via
* \ref xmpp_conn_set_functionpointer with the option
* \ref XMPP_SETTING_PASSWORD_CALLBACK so the TLS stack can retrieve the
* password.
*
*
*
* If any value was previously set, it will be discarded.
* The function will make a copy of the value given.
*
* @param conn a Strophe connection object
* @param setting The setting that shall be configured
* @param value The value, the settings should get
*
* @ingroup Connections
*/
void xmpp_conn_set_string(xmpp_conn_t *conn,
xmpp_conn_setting_t setting,
const char *value)
{
char **target;
switch (setting) {
case XMPP_SETTING_JID:
target = &conn->jid;
break;
case XMPP_SETTING_PASS:
target = &conn->pass;
break;
case XMPP_SETTING_CAFILE:
target = &conn->tls_cafile;
break;
case XMPP_SETTING_CAPATH:
target = &conn->tls_capath;
break;
case XMPP_SETTING_CLIENT_CERT:
target = &conn->tls_client_cert;
break;
case XMPP_SETTING_CLIENT_KEY:
target = &conn->tls_client_key;
break;
default:
strophe_warn(conn->ctx, "xmpp", "Invalid String setting %d", setting);
return;
}
if (*target)
strophe_free(conn->ctx, *target);
*target = NULL;
if (value) {
*target = strophe_strdup(conn->ctx, value);
}
}
/** Configure a connection-related pointer setting
*
* `setting` can be one of:
* - \ref XMPP_SETTING_PASSWORD_CALLBACK_USERDATA
* Set the userdata pointer that is pass when the password-retrieval
* callback is called. \ref XMPP_SETTING_PASSWORD_CALLBACK
*
* @param conn a Strophe connection object
* @param setting The setting that shall be configured
* @param value The value, the settings should get
*
* @ingroup Connections
*/
void xmpp_conn_set_pointer(xmpp_conn_t *conn,
xmpp_conn_setting_t setting,
void *value)
{
switch (setting) {
case XMPP_SETTING_PASSWORD_CALLBACK_USERDATA:
conn->password_callback_userdata = value;
break;
default:
strophe_warn(conn->ctx, "xmpp", "Invalid Pointer setting %d", setting);
return;
}
}
/** Configure a connection-related functionpointer setting
*
* `setting` can be one of:
* - \ref XMPP_SETTING_PASSWORD_CALLBACK
* Set the Callback function which will be called when the TLS stack can't
* decrypt a password protected key file. The userdata pointer can be set
* via \ref xmpp_conn_set_pointer with the setting
* \ref XMPP_SETTING_PASSWORD_CALLBACK_USERDATA
*
* - \ref XMPP_SETTING_CERTFAIL_HANDLER
* Set the Handler function which will be called when the TLS stack can't
* verify the CA of the server we're trying to connect to.
*
* - \ref XMPP_SETTING_SOCKOPT_CALLBACK
* Set function to be called when a new socket is created to allow setting
* socket options before connection is started. \n
* If the connection is already connected, this callback will be called
* immediately. \n
* To set options that can only be applied to disconnected sockets, the
* callback must be registered before connecting.
*
* @param conn a Strophe connection object
* @param setting The setting that shall be configured
* @param value The value, the settings should get
*
* @ingroup Connections
*/
void xmpp_conn_set_functionpointer_impl(xmpp_conn_t *conn,
xmpp_conn_setting_t setting,
xmpp_conn_pfn_t value)
{
switch (setting) {
case XMPP_SETTING_PASSWORD_CALLBACK:
conn->password_callback = (xmpp_password_callback)value;
break;
case XMPP_SETTING_CERTFAIL_HANDLER:
conn->certfail_handler = (xmpp_certfail_handler)value;
break;
case XMPP_SETTING_SOCKOPT_CALLBACK:
conn->sockopt_cb = (xmpp_sockopt_callback)value;
if (conn->state != XMPP_STATE_DISCONNECTED)
conn->sockopt_cb(conn, &conn->sock);
break;
default:
strophe_warn(conn->ctx, "xmpp", "Invalid Functionpointer setting %d",
setting);
return;
}
}
/** Get the JID which is or will be bound to the connection.
*
* @param conn a Strophe connection object
@@ -571,6 +398,66 @@ const char *xmpp_conn_get_bound_jid(const xmpp_conn_t *conn)
return conn->bound_jid;
}
/** Set the JID of the user that will be bound to the connection.
* If any JID was previously set, it will be discarded. This should not be
* be used after a connection is created. The function will make a copy of
* the JID string. If the supplied JID is missing the node, SASL
* ANONYMOUS authentication will be used.
*
* @param conn a Strophe connection object
* @param jid a full or bare JID
*
* @ingroup Connections
*/
void xmpp_conn_set_jid(xmpp_conn_t *conn, const char *jid)
{
if (conn->jid)
strophe_free(conn->ctx, conn->jid);
conn->jid = strophe_strdup(conn->ctx, jid);
}
/** Set the Handler function which will be called when the TLS stack can't
* verify the CA of the server we're trying to connect to.
*
* @param conn a Strophe connection object
* @param hndl certfail Handler function
*
* @ingroup TLS
*/
void xmpp_conn_set_certfail_handler(xmpp_conn_t *const conn,
xmpp_certfail_handler hndl)
{
conn->certfail_handler = hndl;
}
/** Set the CAfile
*
* @param conn a Strophe connection object
* @param path path to a certificate file
*
* @ingroup TLS
*/
void xmpp_conn_set_cafile(xmpp_conn_t *const conn, const char *path)
{
if (conn->tls_cafile)
strophe_free(conn->ctx, conn->tls_cafile);
conn->tls_cafile = strophe_strdup(conn->ctx, path);
}
/** Set the CApath
*
* @param conn a Strophe connection object
* @param path path to a folder containing certificates
*
* @ingroup TLS
*/
void xmpp_conn_set_capath(xmpp_conn_t *const conn, const char *path)
{
if (conn->tls_capath)
strophe_free(conn->ctx, conn->tls_capath);
conn->tls_capath = strophe_strdup(conn->ctx, path);
}
/** Retrieve the peer certificate
*
* The returned Certificate object must be free'd by calling
@@ -587,6 +474,41 @@ xmpp_tlscert_t *xmpp_conn_get_peer_cert(xmpp_conn_t *const conn)
return tls_peer_cert(conn);
}
/** Set the Callback function which will be called when the TLS stack can't
* decrypt a password protected key file.
*
* @param conn a Strophe connection object
* @param cb The callback function that shall be called
* @param userdata An opaque data pointer that will be passed to the callback
*
* @ingroup TLS
*/
void xmpp_conn_set_password_callback(xmpp_conn_t *conn,
xmpp_password_callback cb,
void *userdata)
{
conn->password_callback = cb;
conn->password_callback_userdata = userdata;
}
/** Set the number of retry attempts to decrypt a private key file.
*
* In case the user enters the password manually it can be useful to
* directly retry if the decryption of the key file failed.
*
* @param conn a Strophe connection object
* @param retries The number of retries that should be tried
*
* @ingroup TLS
*/
void xmpp_conn_set_password_retries(xmpp_conn_t *conn, unsigned int retries)
{
if (retries == 0)
conn->password_retries = 1;
else
conn->password_retries = retries;
}
/** Retrieve the path of the key file that shall be unlocked.
*
* This makes usually sense to be called from the
@@ -603,6 +525,49 @@ const char *xmpp_conn_get_keyfile(const xmpp_conn_t *conn)
return conn->tls_client_key;
}
/** Set the Client Certificate and Private Key or PKCS#12 encoded file that
* will be bound to the connection. If any of them was previously set, it
* will be discarded. This should not be used after a connection is created.
* The function will make a copy of the strings passed in.
*
* In case the Private Key is encrypted, a callback must be set via
* \ref xmpp_conn_set_password_callback so the TLS stack can retrieve the
* password.
*
* In case one wants to use a PKCS#12 encoded file, it should be passed via
* the `cert` parameter and `key` should be NULL. Passing a PKCS#12 file in
* `key` is deprecated.
*
* @param conn a Strophe connection object
* @param cert path to a certificate file or a P12 file
* @param key path to a private key file or a P12 file
*
* @ingroup TLS
*/
void xmpp_conn_set_client_cert(xmpp_conn_t *const conn,
const char *const cert,
const char *const key)
{
strophe_debug(conn->ctx, "conn", "set client cert %s %s", cert, key);
if (conn->tls_client_cert)
strophe_free(conn->ctx, conn->tls_client_cert);
conn->tls_client_cert = NULL;
if (conn->tls_client_key)
strophe_free(conn->ctx, conn->tls_client_key);
conn->tls_client_key = NULL;
if (cert && key) {
conn->tls_client_cert = strophe_strdup(conn->ctx, cert);
conn->tls_client_key = strophe_strdup(conn->ctx, key);
} else if (cert && !key) {
conn->tls_client_cert = strophe_strdup(conn->ctx, cert);
} else if (!cert && key) {
strophe_warn(conn->ctx, "xmpp",
"xmpp_conn_set_client_cert: Passing PKCS#12 in 'key' "
"parameter is deprecated. Use 'cert' instead");
conn->tls_client_cert = strophe_strdup(conn->ctx, key);
}
}
/** Get the number of xmppAddr entries in the client certificate.
*
* @param conn a Strophe connection object
@@ -643,6 +608,22 @@ const char *xmpp_conn_get_pass(const xmpp_conn_t *conn)
return conn->pass;
}
/** Set the password used to authenticate the connection.
* If any password was previously set, it will be discarded. The function
* will make a copy of the password string.
*
* @param conn a Strophe connection object
* @param pass the password
*
* @ingroup Connections
*/
void xmpp_conn_set_pass(xmpp_conn_t *conn, const char *pass)
{
if (conn->pass)
strophe_free(conn->ctx, conn->pass);
conn->pass = pass ? strophe_strdup(conn->ctx, pass) : NULL;
}
/** Get the strophe context that the connection is associated with.
* @param conn a Strophe connection object
*
@@ -1274,36 +1255,336 @@ int xmpp_conn_is_disconnected(xmpp_conn_t *conn)
}
/**
* This returns the Stream Management state of a connection object after
* it has been disconnected.
* One can then initialise a fresh connection object and set this Stream
* Management state by calling \ref xmpp_conn_set_sm_state
* This sets the Stream Management callback function
*
* In case one wants to dispose of the state w/o setting it into a fresh
* connection object, one can call \ref xmpp_free_sm_state
* After setting this API, the library will call the given callback function
* each time when the internal SM state is updated.
*
* After calling this function to retrieve the state, only call one of the
* other two.
* This can be used in conjunction with \ref xmpp_conn_restore_sm_state to
* e.g. implement a mechanism that retains an SM state over potential
* application terminations.
*
* @param conn a Strophe connection object
* @return The Stream Management state of the connection or NULL on error
* @param cb a callback function or NULL to disable
* @param ctx a context that will be passed on invocation of the callback
* function
*
* @ingroup Connections
*/
xmpp_sm_state_t *xmpp_conn_get_sm_state(xmpp_conn_t *conn)
void xmpp_conn_set_sm_callback(xmpp_conn_t *conn,
xmpp_sm_callback cb,
void *ctx)
{
xmpp_sm_state_t *ret;
conn->sm_callback = cb;
conn->sm_callback_ctx = ctx;
}
/* We can only return the SM state when we're disconnected */
if (conn->state != XMPP_STATE_DISCONNECTED)
return NULL;
struct sm_restore {
xmpp_conn_t *conn;
const unsigned char *state;
const unsigned char *const state_end, *const orig;
};
ret = conn->sm_state;
conn->sm_state = NULL;
static int sm_load_u32(struct sm_restore *sm, uint8_t type, uint32_t *val)
{
if (*sm->state != type) {
strophe_error(
sm->conn->ctx, "conn",
"Invalid CBOR type at position %u: 0x%02x, expected: 0x%02x",
sm->state - sm->orig, *sm->state, type);
return XMPP_EINVOP;
}
sm->state++;
if ((sm->state + 4) > sm->state_end) {
strophe_error(sm->conn->ctx, "conn",
"Provided sm_state data is too short");
return XMPP_EINVOP;
}
uint32_t v;
memcpy(&v, sm->state, 4);
sm->state += 4;
*val = ntohl(v);
return 0;
}
static int sm_load_string(struct sm_restore *sm, char **val, size_t *len)
{
uint32_t l;
int ret = sm_load_u32(sm, 0x7a, &l);
if (ret)
return ret;
if ((sm->state + l) > sm->state_end) {
strophe_error(sm->conn->ctx, "conn",
"Provided sm_state data is too short");
return XMPP_EINVOP;
}
*val = strophe_alloc(sm->conn->ctx, l + 1);
if (!*val)
return XMPP_EMEM;
memcpy(*val, sm->state, l);
(*val)[l] = '\0';
sm->state += l;
*len = l;
return 0;
}
/**
* This restores the serialized Stream Management state
*
* After setting this API, the library will call the given callback function
* each time when the internal SM state is updated.
*
* This can be used in conjunction with \ref xmpp_conn_restore_sm_state to
* e.g. implement a mechanism that retains an SM state over potential
* application terminations.
*
* @param conn a Strophe connection object
* @param sm_state a buffer as passed to the SM callback
* @param sm_state_len the length of `sm_state`
*
* @ingroup Connections
*/
int xmpp_conn_restore_sm_state(xmpp_conn_t *conn,
const unsigned char *sm_state,
size_t sm_state_len)
{
/* We can only set the SM state when we're disconnected */
if (conn->state != XMPP_STATE_DISCONNECTED) {
strophe_error(conn->ctx, "conn",
"SM state can only be set the when we're disconnected");
return XMPP_EINVOP;
}
if (conn->sm_state) {
strophe_error(conn->ctx, "conn", "SM state is already set!");
return XMPP_EINVOP;
}
if (sm_state_len < 5 * 6) {
strophe_error(conn->ctx, "conn", "Provided sm_state data is too short");
return XMPP_EINVOP;
}
struct sm_restore sm = {.conn = conn,
.state = sm_state,
.state_end = sm_state + sm_state_len,
.orig = sm_state};
/* Check for pointer wrap-around, which should never happen */
if (sm.state_end < sm.state) {
strophe_error(conn->ctx, "conn",
"Internal error, pointer wrapped around");
return XMPP_EINVOP;
}
if (memcmp(sm.state, "\x1a\x00\x00\x00\x00", 5) != 0) {
strophe_error(conn->ctx, "conn", "Unknown sm_state version");
return XMPP_EINVOP;
}
sm.state += 5;
conn->sm_state = strophe_alloc(conn->ctx, sizeof(*conn->sm_state));
if (!conn->sm_state)
return XMPP_EMEM;
memset(conn->sm_state, 0, sizeof(*conn->sm_state));
conn->sm_state->ctx = conn->ctx;
conn->sm_state->sm_support = 1;
conn->sm_state->sm_enabled = 1;
conn->sm_state->can_resume = 1;
conn->sm_state->resume = 1;
int ret;
ret = sm_load_u32(&sm, 0x1a, &conn->sm_state->sm_sent_nr);
if (ret)
goto err_reload;
ret = sm_load_u32(&sm, 0x1a, &conn->sm_state->sm_handled_nr);
if (ret)
goto err_reload;
size_t id_len;
ret = sm_load_string(&sm, &conn->sm_state->id, &id_len);
if (ret)
goto err_reload;
uint32_t len, i;
ret = sm_load_u32(&sm, 0x9a, &len);
if (ret)
goto err_reload;
conn->send_queue_user_len = conn->send_queue_len = len;
for (i = 0; i < len; i++) {
xmpp_send_queue_t *item = strophe_alloc(conn->ctx, sizeof(*item));
if (!item) {
ret = XMPP_EMEM;
goto err_reload;
}
memset(item, 0, sizeof(*item));
if (!conn->send_queue_tail) {
conn->send_queue_head = item;
conn->send_queue_tail = item;
} else {
conn->send_queue_tail->next = item;
conn->send_queue_tail = item;
}
ret = sm_load_string(&sm, &item->data, &item->len);
if (ret)
goto err_reload;
item->owner = XMPP_QUEUE_USER;
}
ret = sm_load_u32(&sm, 0xba, &len);
if (ret)
goto err_reload;
for (i = 0; i < len; i++) {
xmpp_send_queue_t *item = strophe_alloc(conn->ctx, sizeof(*item));
if (!item) {
ret = XMPP_EMEM;
goto err_reload;
}
memset(item, 0, sizeof(*item));
add_queue_back(&conn->sm_state->sm_queue, item);
ret = sm_load_u32(&sm, 0x1a, &item->sm_h);
if (ret)
goto err_reload;
ret = sm_load_string(&sm, &item->data, &item->len);
if (ret)
goto err_reload;
item->owner = XMPP_QUEUE_USER;
}
return XMPP_EOK;
err_reload:
xmpp_free_sm_state(conn->sm_state);
return ret;
}
static int sm_store_u32(unsigned char **next_,
const unsigned char *const end,
uint8_t type,
uint32_t val)
{
unsigned char *next = *next_;
if (next + 5 > end)
return 1;
*next++ = type;
uint32_t v = htonl(val);
memcpy(next, &v, 4);
next += 4;
*next_ = next;
return 0;
}
static size_t sm_state_serialize(xmpp_conn_t *conn, unsigned char **buf)
{
if (!conn->sm_state->sm_support || !conn->sm_state->sm_enabled ||
!conn->sm_state->can_resume) {
*buf = NULL;
return 0;
}
uint32_t id_len = strlen(conn->sm_state->id);
xmpp_send_queue_t *peek = conn->sm_state->sm_queue.head;
size_t sm_queue_len = 0;
size_t sm_queue_size = 0;
while (peek) {
sm_queue_len++;
sm_queue_size += 10 + peek->len;
peek = peek->next;
}
uint32_t send_queue_len = 0;
size_t send_queue_size = 0;
peek = conn->send_queue_head;
while (peek) {
send_queue_len++;
send_queue_size += 5 + peek->len;
peek = peek->next;
}
size_t buf_size =
5 + 5 + 5 + 5 + id_len + 5 + send_queue_size + 5 + sm_queue_size;
*buf = strophe_alloc(conn->ctx, buf_size);
if (*buf == NULL)
return 0;
unsigned char *next = *buf;
const unsigned char *const end = next + buf_size;
/* Check for pointer wrap-around, which should never happen */
if (end < next) {
strophe_error(conn->ctx, "conn",
"Internal error, pointer wrapped around");
return 0;
}
memcpy(next, "\x1a\x00\x00\x00\x00", 5); // Version
next += 5;
if (sm_store_u32(&next, end, 0x1a, conn->sm_state->sm_sent_nr))
goto err_serialize;
if (sm_store_u32(&next, end, 0x1a, conn->sm_state->sm_handled_nr))
goto err_serialize;
if (sm_store_u32(&next, end, 0x7a, id_len))
goto err_serialize;
memcpy(next, conn->sm_state->id, id_len);
next += id_len;
if (sm_store_u32(&next, end, 0x9a, send_queue_len))
goto err_serialize;
peek = conn->send_queue_head;
while (peek) {
if (sm_store_u32(&next, end, 0x7a, (uint32_t)peek->len))
goto err_serialize;
if (next + peek->len > end)
goto err_serialize;
memcpy(next, peek->data, peek->len);
next += peek->len;
peek = peek->next;
}
if (sm_store_u32(&next, end, 0xba, sm_queue_len))
goto err_serialize;
peek = conn->sm_state->sm_queue.head;
while (peek) {
if (sm_store_u32(&next, end, 0x1a, peek->sm_h))
goto err_serialize;
if (sm_store_u32(&next, end, 0x7a, (uint32_t)peek->len))
goto err_serialize;
if (next + peek->len > end)
goto err_serialize;
memcpy(next, peek->data, peek->len);
next += peek->len;
peek = peek->next;
}
return buf_size;
err_serialize:
strophe_error(conn->ctx, "conn", "Can't serialize more data, buffer full");
strophe_free(conn->ctx, buf);
return 0;
}
void trigger_sm_callback(xmpp_conn_t *conn)
{
if (!conn || !conn->sm_callback)
return;
unsigned char *buf;
size_t size = sm_state_serialize(conn, &buf);
conn->sm_callback(conn, conn->sm_callback_ctx, buf, size);
strophe_free(conn->ctx, buf);
}
static void _reset_sm_state_for_reconnect(xmpp_conn_t *conn)
{
xmpp_sm_state_t *s = conn->sm_state;
@@ -1332,6 +1613,37 @@ static void _reset_sm_state_for_reconnect(xmpp_conn_t *conn)
}
}
/**
* This returns the Stream Management state of a connection object after
* it has been disconnected.
* One can then initialise a fresh connection object and set this Stream
* Management state by calling \ref xmpp_conn_set_sm_state
*
* In case one wants to dispose of the state w/o setting it into a fresh
* connection object, one can call \ref xmpp_free_sm_state
*
* After calling this function to retrieve the state, only call one of the
* other two.
*
* @param conn a Strophe connection object
* @return The Stream Management state of the connection or NULL on error
*
* @ingroup Connections
*/
xmpp_sm_state_t *xmpp_conn_get_sm_state(xmpp_conn_t *conn)
{
xmpp_sm_state_t *ret;
/* We can only return the SM state when we're disconnected */
if (conn->state != XMPP_STATE_DISCONNECTED)
return NULL;
ret = conn->sm_state;
conn->sm_state = NULL;
return ret;
}
/**
* @param conn a Strophe connection object
* @param sm_state A Stream Management state returned from a call to
@@ -1510,7 +1822,9 @@ char *xmpp_conn_send_queue_drop_element(xmpp_conn_t *conn,
conn->sm_state->r_sent = 0;
}
/* Finally drop the element */
return _drop_send_queue_element(conn, t);
char *r = _drop_send_queue_element(conn, t);
trigger_sm_callback(conn);
return r;
}
/* timed handler for cleanup if normal disconnect procedure takes too long */
@@ -1704,6 +2018,7 @@ static void _handle_stream_end(char *name, void *userdata)
strophe_debug(conn->ctx, "xmpp", "RECV: </stream:stream>");
/* the session has been terminated properly, i.e. it can't be resumed */
conn->sm_state->can_resume = 0;
trigger_sm_callback(conn);
conn_disconnect_clean(conn);
}
@@ -1779,6 +2094,7 @@ static void _conn_sm_handle_stanza(xmpp_conn_t *const conn,
conn->sm_state->r_sent = 0;
}
}
trigger_sm_callback(conn);
}
static unsigned short _conn_default_port(xmpp_conn_t *conn,
@@ -2065,8 +2381,10 @@ static int _send_raw(xmpp_conn_t *conn,
strophe_debug_verbose(1, conn->ctx, "conn", "Q_ADD: %p", item);
if (!(owner & XMPP_QUEUE_SM) && conn->sm_state->sm_enabled &&
!conn->sm_state->r_sent) {
send_raw(conn, req_ack, strlen(req_ack), XMPP_QUEUE_SM_STROPHE, item);
conn->sm_state->r_sent = 1;
send_raw(conn, req_ack, strlen(req_ack), XMPP_QUEUE_SM_STROPHE, item);
} else {
trigger_sm_callback(conn);
}
return XMPP_EOK;
}

View File

@@ -19,7 +19,6 @@
*/
#include "common.h"
#include <limits.h>
/** Allocate memory in a Strophe context.
* All Strophe functions will use this to allocate memory.
@@ -87,10 +86,6 @@ char *xmpp_strndup(const xmpp_ctx_t *ctx, const char *s, size_t len)
return strophe_strndup(ctx, s, len);
}
/** Write to the log.
*
* @ingroup Deprecated
*/
void xmpp_log(const xmpp_ctx_t *ctx,
xmpp_log_level_t level,
const char *area,
@@ -222,10 +217,6 @@ char *xmpp_strtok_r(char *s, const char *delim, char **saveptr)
return strophe_strtok_r(s, delim, saveptr);
}
/** snprintf(3) implementation.
*
* @ingroup Deprecated
*/
int xmpp_snprintf(char *str, size_t count, const char *fmt, ...)
{
va_list ap;
@@ -237,184 +228,11 @@ int xmpp_snprintf(char *str, size_t count, const char *fmt, ...)
return ret;
}
/** vsnprintf(3) implementation.
*
* @ingroup Deprecated
*/
int xmpp_vsnprintf(char *str, size_t count, const char *fmt, va_list arg)
{
return strophe_vsnprintf(str, count, fmt, arg);
}
/** Set the JID of the user that will be bound to the connection.
* If any JID was previously set, it will be discarded. This should not be
* be used after a connection is created. The function will make a copy of
* the JID string. If the supplied JID is missing the node, SASL
* ANONYMOUS authentication will be used.
*
* @param conn a Strophe connection object
* @param jid a full or bare JID
*
* @ingroup Deprecated
*/
void xmpp_conn_set_jid(xmpp_conn_t *conn, const char *jid)
{
xmpp_conn_set_string(conn, XMPP_SETTING_JID, jid);
}
/** Set the password used to authenticate the connection.
* If any password was previously set, it will be discarded. The function
* will make a copy of the password string.
*
* @param conn a Strophe connection object
* @param pass the password
*
* @ingroup Deprecated
*/
void xmpp_conn_set_pass(xmpp_conn_t *conn, const char *pass)
{
xmpp_conn_set_string(conn, XMPP_SETTING_PASS, pass);
}
/** Set the CAfile
*
* @param conn a Strophe connection object
* @param path path to a certificate file
*
* @ingroup Deprecated
*/
void xmpp_conn_set_cafile(xmpp_conn_t *const conn, const char *path)
{
xmpp_conn_set_string(conn, XMPP_SETTING_CAFILE, path);
}
/** Set the CApath
*
* @param conn a Strophe connection object
* @param path path to a folder containing certificates
*
* @ingroup Deprecated
*/
void xmpp_conn_set_capath(xmpp_conn_t *const conn, const char *path)
{
xmpp_conn_set_string(conn, XMPP_SETTING_CAPATH, path);
}
/** Set the Client Certificate and Private Key or PKCS#12 encoded file that
* will be bound to the connection. If any of them was previously set, it
* will be discarded. This should not be used after a connection is created.
* The function will make a copy of the strings passed in.
*
* In case the Private Key is encrypted, a callback must be set via
* \ref xmpp_conn_set_password_callback so the TLS stack can retrieve the
* password.
*
* In case one wants to use a PKCS#12 encoded file, it should be passed via
* the `cert` parameter and `key` should be NULL. Passing a PKCS#12 file in
* `key` is deprecated.
*
* @param conn a Strophe connection object
* @param cert path to a certificate file or a P12 file
* @param key path to a private key file or a P12 file
*
* @ingroup Deprecated
*/
void xmpp_conn_set_client_cert(xmpp_conn_t *const conn,
const char *const cert,
const char *const key)
{
strophe_debug(conn->ctx, "conn", "set client cert %s %s", cert, key);
if (!cert && key) {
xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_CERT, key);
strophe_warn(conn->ctx, "xmpp",
"xmpp_conn_set_client_cert: Passing PKCS#12 in 'key' "
"parameter is deprecated. Use 'cert' instead");
} else {
xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_CERT, cert);
xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_KEY, key);
}
}
/** Set the number of retry attempts to decrypt a private key file.
*
* In case the user enters the password manually it can be useful to
* directly retry if the decryption of the key file failed.
*
* @param conn a Strophe connection object
* @param retries The number of retries that should be tried
*
* @ingroup Deprecated
*/
void xmpp_conn_set_password_retries(xmpp_conn_t *conn, unsigned int retries)
{
int val;
if (retries > INT_MAX) {
val = INT_MAX;
strophe_warn(conn->ctx, "xmpp", "retries capped from %u to %d", retries,
val);
} else {
val = (int)retries;
}
xmpp_conn_set_int(conn, XMPP_SETTING_PASSWORD_RETRIES, val);
}
/** Set the Callback function which will be called when the TLS stack can't
* decrypt a password protected key file.
*
* @param conn a Strophe connection object
* @param cb The callback function that shall be called
* @param userdata An opaque data pointer that will be passed to the callback
*
* @ingroup Deprecated
*/
void xmpp_conn_set_password_callback(xmpp_conn_t *conn,
xmpp_password_callback cb,
void *userdata)
{
xmpp_conn_set_functionpointer(conn, XMPP_SETTING_PASSWORD_CALLBACK, cb);
xmpp_conn_set_pointer(conn, XMPP_SETTING_PASSWORD_CALLBACK_USERDATA,
userdata);
}
/** Set the Handler function which will be called when the TLS stack can't
* verify the CA of the server we're trying to connect to.
*
* @param conn a Strophe connection object
* @param hndl certfail Handler function
*
* @ingroup Deprecated
*/
void xmpp_conn_set_certfail_handler(xmpp_conn_t *const conn,
xmpp_certfail_handler hndl)
{
xmpp_conn_set_functionpointer(conn, XMPP_SETTING_CERTFAIL_HANDLER, hndl);
}
/** Register sockopt callback
* Set function to be called when a new socket is created to allow setting
* socket options before connection is started.
*
* If the connection is already connected, this callback will be called
* immediately.
*
* To set options that can only be applied to disconnected sockets, the
* callback must be registered before connecting.
*
* @param conn The Strophe connection object this callback is being registered
* for
* @param callback a xmpp_sockopt_callback callback function that will receive
* notifications of connection status
*
* @ingroup Deprecated
*/
void xmpp_conn_set_sockopt_callback(xmpp_conn_t *conn,
xmpp_sockopt_callback callback)
{
xmpp_conn_set_functionpointer(conn, XMPP_SETTING_SOCKOPT_CALLBACK,
callback);
}
/** Set TCP keepalive parameters
* Turn on TCP keepalive and set timeout and interval. Zero timeout
* disables TCP keepalives. The parameters are applied immediately for
@@ -435,8 +253,7 @@ void xmpp_conn_set_keepalive(xmpp_conn_t *conn, int timeout, int interval)
conn->ka_timeout = timeout;
conn->ka_interval = interval;
conn->ka_count = 0;
xmpp_conn_set_functionpointer(conn, XMPP_SETTING_SOCKOPT_CALLBACK,
xmpp_sockopt_cb_keepalive);
xmpp_conn_set_sockopt_callback(conn, xmpp_sockopt_cb_keepalive);
}
/** Disable TLS for this connection, called by users of the library.

View File

@@ -39,7 +39,7 @@
#include <sys/select.h>
#include <errno.h>
#include <unistd.h>
#define _sleep(x) usleep((x)*1000)
#define _sleep(x) usleep((x) * 1000)
#else
#include <winsock2.h>
#ifndef ETIMEDOUT
@@ -174,6 +174,7 @@ void xmpp_run_once(xmpp_ctx_t *ctx, unsigned long timeout)
/* if we've sent everything update the tail */
if (!sq)
conn->send_queue_tail = NULL;
trigger_sm_callback(conn);
}
intf->flush(intf);

View File

@@ -25,8 +25,6 @@
#include "common.h"
#include "ostypes.h"
typedef int (*xmpp_void_handler)();
/* 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()

View File

@@ -37,7 +37,7 @@
#define PUT_32BIT_LSB_FIRST(cp, value) \
do { \
(cp)[0] = (value)&0xFF; \
(cp)[0] = (value) & 0xFF; \
(cp)[1] = ((value) >> 8) & 0xFF; \
(cp)[2] = ((value) >> 16) & 0xFF; \
(cp)[3] = ((value) >> 24) & 0xFF; \

View File

@@ -68,9 +68,9 @@ struct _xmpp_rand_t {
};
/* returns smallest number mupliple of y that not less than x */
#define round_up(x, y) (((x) + (y)-1) / (y) * (y))
#define round_up(x, y) (((x) + (y) - 1) / (y) * (y))
/* returns smallest integer number that not less than x/y */
#define div_round_up(x, y) (((x) + (y)-1) / (y))
#define div_round_up(x, y) (((x) + (y) - 1) / (y))
/* adds two arrays as numbers in big-endian representation and stores
* result in the first one.

View File

@@ -630,7 +630,7 @@ static void netbuf_add_16bitnum(unsigned char *buf,
/* assuming big endian */
*p++ = (num >> 8) & 0xff;
*p++ = (num)&0xff;
*p++ = (num) & 0xff;
*offset += 2;
}

View File

@@ -53,7 +53,7 @@ extern "C" {
(y)[0] = (uint8_t)(((x) >> 24) & 255); \
(y)[1] = (uint8_t)(((x) >> 16) & 255); \
(y)[2] = (uint8_t)(((x) >> 8) & 255); \
(y)[3] = (uint8_t)((x)&255); \
(y)[3] = (uint8_t)((x) & 255); \
} while (0)
#define LOAD32H(x, y) \
@@ -72,7 +72,7 @@ extern "C" {
(y)[4] = (uint8_t)(((x) >> 24) & 255); \
(y)[5] = (uint8_t)(((x) >> 16) & 255); \
(y)[6] = (uint8_t)(((x) >> 8) & 255); \
(y)[7] = (uint8_t)((x)&255); \
(y)[7] = (uint8_t)((x) & 255); \
} while (0)
#define LOAD64H(x, y) \
@@ -96,13 +96,13 @@ extern "C" {
#define CONST64(n) n##ULL
#endif
#define RORc(x, y) \
(((((uint32_t)(x)&0xFFFFFFFFUL) >> (uint32_t)((y)&31)) | \
((uint32_t)(x) << (uint32_t)((32 - ((y)&31)) & 31))) & \
#define RORc(x, y) \
(((((uint32_t)(x) & 0xFFFFFFFFUL) >> (uint32_t)((y) & 31)) | \
((uint32_t)(x) << (uint32_t)((32 - ((y) & 31)) & 31))) & \
0xFFFFFFFFUL)
#define ROR64c(x, y) \
(((((x)&CONST64(0xFFFFFFFFFFFFFFFF)) >> ((uint64_t)(y)&CONST64(63))) | \
((x) << (((uint64_t)64 - ((y)&63)) & 63))) & \
#define ROR64c(x, y) \
(((((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((uint64_t)(y) & CONST64(63))) | \
((x) << (((uint64_t)64 - ((y) & 63)) & 63))) & \
CONST64(0xFFFFFFFFFFFFFFFF))
#ifdef __cplusplus

View File

@@ -9,7 +9,7 @@
#define Ch(x, y, z) (z ^ (x & (y ^ z)))
#define Maj(x, y, z) (((x | y) & z) | (x & y))
#define S(x, n) RORc((x), (n))
#define R(x, n) (((x)&0xFFFFFFFFUL) >> (n))
#define R(x, n) (((x) & 0xFFFFFFFFUL) >> (n))
#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22))
#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25))
#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3))

View File

@@ -52,7 +52,7 @@ static const uint64_t K[80] = {
#define Ch(x, y, z) (z ^ (x & (y ^ z)))
#define Maj(x, y, z) (((x | y) & z) | (x & y))
#define S(x, n) ROR64c(x, n)
#define R(x, n) (((x)&CONST64(0xFFFFFFFFFFFFFFFF)) >> ((uint64_t)n))
#define R(x, n) (((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((uint64_t)n))
#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39))
#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41))
#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7))

126
strophe.h
View File

@@ -18,6 +18,7 @@
#define __LIBSTROPHE_STROPHE_H__
#include <stddef.h> /* size_t */
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
@@ -371,70 +372,37 @@ typedef int (*xmpp_sockopt_callback)(xmpp_conn_t *conn, void *sock);
/* an example callback that sets basic keepalive parameters */
int xmpp_sockopt_cb_keepalive(xmpp_conn_t *conn, void *sock);
void xmpp_send_error(xmpp_conn_t *conn, xmpp_error_type_t type, char *text);
xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t *ctx);
xmpp_conn_t *xmpp_conn_clone(xmpp_conn_t *conn);
int xmpp_conn_release(xmpp_conn_t *conn);
/** Connection settings
*/
typedef enum xmpp_conn_setting_t {
/* String values */
XMPP_SETTING_JID = 0x0, /**< JID */
XMPP_SETTING_PASS, /**< Password */
XMPP_SETTING_CAFILE, /**< CAfile */
XMPP_SETTING_CAPATH, /**< CApath */
XMPP_SETTING_CLIENT_CERT, /**< Client Certificate */
XMPP_SETTING_CLIENT_KEY, /**< Key of Client Certificate */
/* Int values */
XMPP_SETTING_PASSWORD_RETRIES = 0x40, /**< Number of retry attempts to
decrypt a private key file. */
XMPP_SETTING_COMPRESSION_LEVEL, /**< Compression level. */
/* Pointer values */
XMPP_SETTING_PASSWORD_CALLBACK_USERDATA = 0x80, /**< Userdata for password
callback function */
/* Functionpointer values */
XMPP_SETTING_PASSWORD_CALLBACK = 0xC0, /**< Callback function to retrieve
password for key file. */
XMPP_SETTING_CERTFAIL_HANDLER, /**< Handler function when certificate chain
can't be verified */
XMPP_SETTING_SOCKOPT_CALLBACK, /**< Callback function when a new socket is
created. */
} xmpp_conn_setting_t;
void xmpp_conn_set_int(xmpp_conn_t *conn,
xmpp_conn_setting_t setting,
int value);
void xmpp_conn_set_string(xmpp_conn_t *conn,
xmpp_conn_setting_t setting,
const char *value);
void xmpp_conn_set_pointer(xmpp_conn_t *conn,
xmpp_conn_setting_t setting,
void *value);
/* Provide a wrapper macro, so users don't have to cast their function pointer
* type manually. */
typedef int (*xmpp_conn_pfn_t)();
#define xmpp_conn_set_functionpointer(c, s, p) \
do { \
xmpp_conn_set_functionpointer_impl(c, s, (xmpp_conn_pfn_t)p); \
} while (0)
void xmpp_conn_set_functionpointer_impl(xmpp_conn_t *conn,
xmpp_conn_setting_t setting,
xmpp_conn_pfn_t pfn);
long xmpp_conn_get_flags(const xmpp_conn_t *conn);
int xmpp_conn_set_flags(xmpp_conn_t *conn, long flags);
const char *xmpp_conn_get_jid(const xmpp_conn_t *conn);
const char *xmpp_conn_get_bound_jid(const xmpp_conn_t *conn);
void xmpp_conn_set_jid(xmpp_conn_t *conn, const char *jid);
void xmpp_conn_set_cafile(xmpp_conn_t *const conn, const char *path);
void xmpp_conn_set_capath(xmpp_conn_t *const conn, const char *path);
void xmpp_conn_set_certfail_handler(xmpp_conn_t *const conn,
xmpp_certfail_handler hndl);
xmpp_tlscert_t *xmpp_conn_get_peer_cert(xmpp_conn_t *const conn);
void xmpp_conn_set_password_callback(xmpp_conn_t *conn,
xmpp_password_callback cb,
void *userdata);
void xmpp_conn_set_password_retries(xmpp_conn_t *conn, unsigned int retries);
const char *xmpp_conn_get_keyfile(const xmpp_conn_t *conn);
void xmpp_conn_set_client_cert(xmpp_conn_t *conn,
const char *cert,
const char *key);
unsigned int xmpp_conn_cert_xmppaddr_num(xmpp_conn_t *conn);
char *xmpp_conn_cert_xmppaddr(xmpp_conn_t *conn, unsigned int n);
const char *xmpp_conn_get_pass(const xmpp_conn_t *conn);
void xmpp_conn_set_pass(xmpp_conn_t *conn, const char *pass);
xmpp_ctx_t *xmpp_conn_get_context(xmpp_conn_t *conn);
int xmpp_conn_is_secured(xmpp_conn_t *conn);
void xmpp_conn_set_sockopt_callback(xmpp_conn_t *conn,
xmpp_sockopt_callback callback);
int xmpp_conn_is_connecting(xmpp_conn_t *conn);
int xmpp_conn_is_connected(xmpp_conn_t *conn);
int xmpp_conn_is_disconnected(xmpp_conn_t *conn);
@@ -447,8 +415,38 @@ typedef enum {
char *xmpp_conn_send_queue_drop_element(xmpp_conn_t *conn,
xmpp_queue_element_t which);
xmpp_sm_state_t *xmpp_conn_get_sm_state(xmpp_conn_t *conn);
int xmpp_conn_set_sm_state(xmpp_conn_t *conn, xmpp_sm_state_t *sm_state);
xmpp_sm_state_t *xmpp_conn_get_sm_state(xmpp_conn_t *conn);
/** The function which will be called when Strophe updates its internal SM
* state.
*
* Please note that you have to create a copy of the buffer, since the library
* will free the buffer right after return of the callback function.
*
*
* @param conn The Strophe connection object this callback
* originates from.
* @param ctx The `ctx` pointer as passed to
* \ref xmpp_conn_set_sm_callback
* @param sm_state A pointer to a buffer containing the serialized SM
* state.
* @param sm_state_len The length of `sm_state`.
*
* @return 0 on success, -1 on error
*
* @ingroup Connections
*/
typedef void (*xmpp_sm_callback)(xmpp_conn_t *conn,
void *ctx,
const unsigned char *sm_state,
size_t sm_state_len);
void xmpp_conn_set_sm_callback(xmpp_conn_t *conn,
xmpp_sm_callback cb,
void *ctx);
int xmpp_conn_restore_sm_state(xmpp_conn_t *conn,
const unsigned char *sm_state,
size_t sm_state_len);
void xmpp_free_sm_state(xmpp_sm_state_t *sm_state);
@@ -478,7 +476,6 @@ int xmpp_conn_tls_start(xmpp_conn_t *conn);
void xmpp_disconnect(xmpp_conn_t *conn);
void xmpp_send(xmpp_conn_t *conn, xmpp_stanza_t *stanza);
void xmpp_send_error(xmpp_conn_t *conn, xmpp_error_type_t type, char *text);
void xmpp_send_raw_string(xmpp_conn_t *conn, const char *fmt, ...);
void xmpp_send_raw(xmpp_conn_t *conn, const char *data, size_t len);
@@ -746,7 +743,6 @@ void xmpp_rand_nonce(xmpp_rand_t *rand, char *output, size_t len);
* deprecation */
#include <stdarg.h>
#if !defined(XMPP_DEPRECATED)
/**
* XMPP_DEPRECATED(x) macro to show a compiler warning for deprecated API
* functions
@@ -765,7 +761,6 @@ void xmpp_rand_nonce(xmpp_rand_t *rand, char *output, size_t len);
#else
#define XMPP_DEPRECATED(x)
#endif
#endif
XMPP_DEPRECATED(internal) void *xmpp_alloc(const xmpp_ctx_t *ctx, size_t size);
XMPP_DEPRECATED(internal)
@@ -805,31 +800,6 @@ void xmpp_conn_set_keepalive(xmpp_conn_t *conn, int timeout, int interval);
XMPP_DEPRECATED(xmpp_conn_set_flags)
void xmpp_conn_disable_tls(xmpp_conn_t *conn);
XMPP_DEPRECATED(xmpp_conn_set_string)
void xmpp_conn_set_jid(xmpp_conn_t *conn, const char *jid);
XMPP_DEPRECATED(xmpp_conn_set_string)
void xmpp_conn_set_pass(xmpp_conn_t *conn, const char *pass);
XMPP_DEPRECATED(xmpp_conn_set_string)
void xmpp_conn_set_cafile(xmpp_conn_t *const conn, const char *path);
XMPP_DEPRECATED(xmpp_conn_set_string)
void xmpp_conn_set_capath(xmpp_conn_t *const conn, const char *path);
XMPP_DEPRECATED(xmpp_conn_set_string)
void xmpp_conn_set_client_cert(xmpp_conn_t *conn,
const char *cert,
const char *key);
XMPP_DEPRECATED(xmpp_conn_set_int)
void xmpp_conn_set_password_retries(xmpp_conn_t *conn, unsigned int retries);
XMPP_DEPRECATED(xmpp_conn_set_functionpointer)
void xmpp_conn_set_password_callback(xmpp_conn_t *conn,
xmpp_password_callback cb,
void *userdata);
XMPP_DEPRECATED(xmpp_conn_set_functionpointer)
void xmpp_conn_set_certfail_handler(xmpp_conn_t *const conn,
xmpp_certfail_handler hndl);
XMPP_DEPRECATED(xmpp_conn_set_functionpointer)
void xmpp_conn_set_sockopt_callback(xmpp_conn_t *conn,
xmpp_sockopt_callback callback);
#ifdef __cplusplus
}
#endif

View File

@@ -1,6 +1,7 @@
#!/bin/sh
logfile="../../testbuild.log"
errfile="../../testerr.log"
err_out() {
tail $logfile
@@ -8,6 +9,6 @@ err_out() {
}
./bootstrap.sh
./configure >> $logfile || err_out
make -j$(( `nproc` * 2 + 1 )) >> $logfile || err_out
make check >> $logfile || err_out
./configure >> $logfile 2>> $errfile || err_out
make -j$(( `nproc` * 2 + 1 )) >> $logfile 2>> $errfile || err_out
make check >> $logfile 2>> $errfile || err_out

166
tests/test_serialize_sm.c Normal file
View File

@@ -0,0 +1,166 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* test_serialize_sm.c
** libstrophe XMPP client library -- test routines for the send queue
**
** Copyright (C) 2024 Stephen Paul Weber
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "strophe.h"
#include "common.h"
#include "test.h"
void callback(xmpp_conn_t *conn,
void *ctx,
const unsigned char *sm_state,
size_t sm_state_len)
{
int *callback_count = ctx;
(*callback_count)++;
COMPARE_BUF("\x1a\x00\x00\x00\x00", 5, sm_state, 5);
COMPARE_BUF("\x1a\x00\x00\x00\x00", 5, sm_state + 10, 5);
COMPARE_BUF("\x7a\x00\x00\x00\x04SMID", 9, sm_state + 15, 9);
if (*callback_count == 1) {
COMPARE_BUF("\x1a\x00\x00\x00\x00", 5, sm_state + 5, 5);
COMPARE_BUF("\x9a\x00\x00\x00\x02", 5, sm_state + 24, 5);
COMPARE_BUF("\x7a\x00\x00\x00\x03"
"foo",
8, sm_state + 29, 8);
COMPARE_BUF("\x7a\x00\x00\x00\x1a<r xmlns='urn:xmpp:sm:3'/>", 31,
sm_state + 37, 31);
COMPARE_BUF("\xba\x00\x00\x00\x00", 5, sm_state + 68, 5);
ENSURE_EQ(sm_state_len, 73);
xmpp_conn_t *newconn = xmpp_conn_new(conn->ctx);
xmpp_conn_restore_sm_state(newconn, sm_state, sm_state_len);
ENSURE_EQ(newconn->sm_state->sm_sent_nr, 0);
ENSURE_EQ(newconn->sm_state->sm_handled_nr, 0);
COMPARE(newconn->sm_state->id, "SMID");
ENSURE_EQ(newconn->send_queue_len, 2);
ENSURE_EQ(newconn->send_queue_user_len, 2);
ENSURE_EQ((size_t)(newconn->sm_state->sm_queue.head), 0);
xmpp_conn_release(newconn);
}
if (*callback_count == 2) {
COMPARE_BUF("\x1a\x00\x00\x00\x01", 5, sm_state + 5, 5);
COMPARE_BUF("\x9a\x00\x00\x00\x01", 5, sm_state + 24, 5);
COMPARE_BUF("\x7a\x00\x00\x00\x1a<r xmlns='urn:xmpp:sm:3'/>", 31,
sm_state + 29, 31);
COMPARE_BUF("\xba\x00\x00\x00\x01", 5, sm_state + 60, 5);
COMPARE_BUF("\x1a\x00\x00\x00\x00", 5, sm_state + 65, 5);
COMPARE_BUF("\x7a\x00\x00\x00\x03"
"foo",
8, sm_state + 70, 8);
ENSURE_EQ(sm_state_len, 78);
xmpp_conn_t *newconn = xmpp_conn_new(conn->ctx);
xmpp_conn_restore_sm_state(newconn, sm_state, sm_state_len);
ENSURE_EQ(newconn->sm_state->sm_sent_nr, 1);
ENSURE_EQ(newconn->sm_state->sm_handled_nr, 0);
COMPARE(newconn->sm_state->id, "SMID");
ENSURE_EQ(newconn->send_queue_len, 1);
ENSURE_EQ(newconn->send_queue_user_len, 1);
ENSURE_EQ(newconn->sm_state->sm_queue.head->sm_h, 0);
xmpp_conn_release(newconn);
}
if (*callback_count == 3) {
COMPARE_BUF("\x1a\x00\x00\x00\x01", 5, sm_state + 5, 5);
COMPARE_BUF("\x9a\x00\x00\x00\x00", 5, sm_state + 24, 5);
COMPARE_BUF("\xba\x00\x00\x00\x01", 5, sm_state + 29, 5);
COMPARE_BUF("\x1a\x00\x00\x00\x00", 5, sm_state + 34, 5);
COMPARE_BUF("\x7a\x00\x00\x00\x03"
"foo",
8, sm_state + 39, 8);
ENSURE_EQ(sm_state_len, 47);
xmpp_conn_t *newconn = xmpp_conn_new(conn->ctx);
xmpp_conn_restore_sm_state(newconn, sm_state, sm_state_len);
ENSURE_EQ(newconn->sm_state->sm_sent_nr, 1);
ENSURE_EQ(newconn->sm_state->sm_handled_nr, 0);
COMPARE(newconn->sm_state->id, "SMID");
ENSURE_EQ(newconn->send_queue_len, 0);
ENSURE_EQ(newconn->send_queue_user_len, 0);
ENSURE_EQ(newconn->sm_state->sm_queue.head->sm_h, 0);
xmpp_conn_release(newconn);
}
}
int fake_read(struct conn_interface *intf, void *buff, size_t len)
{
return 0;
}
int fake_write(struct conn_interface *intf, const void *buff, size_t len)
{
return len;
}
int fake_flush(struct conn_interface *intf)
{
return 0;
}
int fake_error_is_recoverable(struct conn_interface *intf, int err)
{
return 0;
}
int main()
{
xmpp_ctx_t *ctx;
xmpp_conn_t *conn;
xmpp_log_t *log;
xmpp_conn_state_t state;
xmpp_sm_state_t *sm_state;
char *ret;
unsigned int n;
int callback_count = 0;
xmpp_initialize();
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
ctx = xmpp_ctx_new(NULL, log);
conn = xmpp_conn_new(ctx);
sm_state = strophe_alloc(ctx, sizeof(*sm_state));
memset(sm_state, 0, sizeof(*sm_state));
sm_state->ctx = ctx;
sm_state->sm_support = sm_state->sm_enabled = sm_state->can_resume = 1;
sm_state->id = strophe_strdup(ctx, "SMID");
xmpp_conn_set_sm_state(conn, sm_state);
xmpp_conn_set_sm_callback(conn, callback, &callback_count);
ENSURE_EQ(xmpp_conn_send_queue_len(conn), 0);
struct conn_interface intf = {fake_read, fake_write,
fake_flush, fake_flush,
fake_flush, fake_error_is_recoverable,
conn};
conn->intf = intf;
state = conn->state;
conn->state = XMPP_STATE_CONNECTED;
conn->sock = 123;
xmpp_send_raw(conn, "foo", 3);
ENSURE_EQ(xmpp_conn_send_queue_len(conn), 1);
ENSURE_EQ(callback_count, 1);
xmpp_run_once(ctx, 0);
ENSURE_EQ(callback_count, 3);
conn->state = state;
xmpp_conn_release(conn);
xmpp_ctx_free(ctx);
xmpp_shutdown();
return 0;
}

View File

@@ -16,7 +16,6 @@
#include <string.h>
#include <sys/param.h>
#define XMPP_DEPRECATED(x)
#include "strophe.h"
#include "test.h"
@@ -42,14 +41,12 @@ int main()
} client_cert[] = {
{0, "tests/cert.pem", "tests/key.pem"},
{1, "tests/cert.pem", "tests/key_encrypted.pem"},
{0, "tests/cert.emptypass.pfx", NULL},
{0, "tests/cert.nopass.pfx", NULL},
{1, "tests/cert.pfx", NULL},
/* Backward compatibility checks for change introduced in #208
* To be removed, once xmpp_conn_set_client_cert() is gone */
{0, NULL, "tests/cert.emptypass.pfx"},
{0, NULL, "tests/cert.nopass.pfx"},
{1, NULL, "tests/cert.pfx"},
{0, "tests/cert.emptypass.pfx", NULL},
{0, "tests/cert.nopass.pfx", NULL},
{1, "tests/cert.pfx", NULL},
};
const char *srcdir;
@@ -81,15 +78,9 @@ int main()
keyfile = NULL;
if (client_cert[m].needs_callback)
xmpp_conn_set_functionpointer(conn, XMPP_SETTING_PASSWORD_CALLBACK,
password_callback);
xmpp_conn_set_password_callback(conn, password_callback, NULL);
if (certfile) {
xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_CERT, certfile);
xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_KEY, keyfile);
} else {
xmpp_conn_set_client_cert(conn, certfile, keyfile);
}
xmpp_conn_set_client_cert(conn, certfile, keyfile);
xmppaddr_num[0] = '0' + xmpp_conn_cert_xmppaddr_num(conn);