From 06f2ba19b4ca460a972864ded8ccc0f2d8646e23 Mon Sep 17 00:00:00 2001 From: Steffen Jaeckel Date: Mon, 29 Jan 2024 12:01:34 +0100 Subject: [PATCH 1/2] Introduce generic `xmpp_conn_set_*()` API Instead of having a multitude of setter API functions for configuration options, provide generic ones for the different types we have. Signed-off-by: Steffen Jaeckel --- examples/active.c | 4 +- examples/basic.c | 4 +- examples/bot.c | 18 ++- examples/complex.c | 22 +-- examples/component.c | 4 +- examples/register.c | 2 +- examples/roster.c | 4 +- examples/vcard.c | 4 +- src/conn.c | 357 ++++++++++++++++++++++-------------------- src/deprecated.c | 185 +++++++++++++++++++++- strophe.h | 92 +++++++++-- tests/test_xmppaddr.c | 21 ++- 12 files changed, 494 insertions(+), 223 deletions(-) diff --git a/examples/active.c b/examples/active.c index 937e2e35..d3a8ab8f 100644 --- a/examples/active.c +++ b/examples/active.c @@ -115,8 +115,8 @@ int main(int argc, char **argv) */ /* setup authentication information */ - xmpp_conn_set_jid(conn, argv[1]); - xmpp_conn_set_pass(conn, argv[2]); + xmpp_conn_set_string(conn, XMPP_SETTING_JID, argv[1]); + xmpp_conn_set_string(conn, XMPP_SETTING_PASS, argv[2]); /* initiate connection */ xmpp_connect_client(conn, NULL, 0, conn_handler, ctx); diff --git a/examples/basic.c b/examples/basic.c index fff46954..90fa5e95 100644 --- a/examples/basic.c +++ b/examples/basic.c @@ -116,9 +116,9 @@ int main(int argc, char **argv) /* setup authentication information */ if (jid) - xmpp_conn_set_jid(conn, jid); + xmpp_conn_set_string(conn, XMPP_SETTING_JID, jid); if (password) - xmpp_conn_set_pass(conn, password); + xmpp_conn_set_string(conn, XMPP_SETTING_PASS, password); /* initiate connection */ if (xmpp_connect_client(conn, host, port, conn_handler, ctx) == XMPP_EOK) { diff --git a/examples/bot.c b/examples/bot.c index 7980053a..4b2b3b32 100644 --- a/examples/bot.c +++ b/examples/bot.c @@ -293,20 +293,24 @@ int main(int argc, char **argv) xmpp_conn_set_flags(conn, flags); /* ask for a password if key is protected */ - xmpp_conn_set_password_callback(conn, password_callback, NULL); + xmpp_conn_set_functionpointer(conn, XMPP_SETTING_PASSWORD_CALLBACK, + password_callback); /* try at max 3 times in case the user enters the password wrong */ - xmpp_conn_set_password_retries(conn, 3); + xmpp_conn_set_int(conn, XMPP_SETTING_PASSWORD_RETRIES, 3); /* setup authentication information */ - if (key) - xmpp_conn_set_client_cert(conn, cert, key); + if (key) { + xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_CERT, cert); + xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_KEY, key); + } if (jid) - xmpp_conn_set_jid(conn, jid); + xmpp_conn_set_string(conn, XMPP_SETTING_JID, jid); if (password) - xmpp_conn_set_pass(conn, password); + xmpp_conn_set_string(conn, XMPP_SETTING_PASS, password); /* enable TCP keepalive, using canned callback function */ if (tcp_keepalive) - xmpp_conn_set_sockopt_callback(conn, xmpp_sockopt_cb_keepalive); + xmpp_conn_set_functionpointer(conn, XMPP_SETTING_SOCKOPT_CALLBACK, + xmpp_sockopt_cb_keepalive); /* set Stream-Mangement state if available */ if (sm_state) { diff --git a/examples/complex.c b/examples/complex.c index 4f980f30..2c293d04 100644 --- a/examples/complex.c +++ b/examples/complex.c @@ -331,27 +331,31 @@ int main(int argc, char **argv) xmpp_conn_set_flags(conn, flags); /* configure TCP keepalive (optional) */ if (tcp_keepalive) - xmpp_conn_set_sockopt_callback(conn, sockopt_cb); + xmpp_conn_set_functionpointer(conn, XMPP_SETTING_SOCKOPT_CALLBACK, + sockopt_cb); /* ask for a password if key is protected */ - xmpp_conn_set_password_callback(conn, password_callback, NULL); + xmpp_conn_set_functionpointer(conn, XMPP_SETTING_PASSWORD_CALLBACK, + password_callback); /* try at max 3 times in case the user enters the password wrong */ - xmpp_conn_set_password_retries(conn, 3); + xmpp_conn_set_int(conn, XMPP_SETTING_PASSWORD_RETRIES, 3); /* setup authentication information */ if (key) { - xmpp_conn_set_client_cert(conn, cert, key); + xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_CERT, cert); + xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_KEY, key); } if (jid) - xmpp_conn_set_jid(conn, jid); + xmpp_conn_set_string(conn, XMPP_SETTING_JID, jid); if (password) - xmpp_conn_set_pass(conn, password); + xmpp_conn_set_string(conn, XMPP_SETTING_PASS, password); if (certfail) - xmpp_conn_set_certfail_handler(conn, certfail_handler); + xmpp_conn_set_functionpointer(conn, XMPP_SETTING_CERTFAIL_HANDLER, + certfail_handler); if (capath) - xmpp_conn_set_capath(conn, capath); + xmpp_conn_set_string(conn, XMPP_SETTING_CAPATH, capath); if (cafile) - xmpp_conn_set_cafile(conn, cafile); + xmpp_conn_set_string(conn, XMPP_SETTING_CAFILE, cafile); /* initiate connection */ if (xmpp_connect_client(conn, host, port, conn_handler, ctx) == XMPP_EOK) { diff --git a/examples/component.c b/examples/component.c index 7e9d1a6b..68deb637 100644 --- a/examples/component.c +++ b/examples/component.c @@ -80,8 +80,8 @@ int main(int argc, char **argv) conn = xmpp_conn_new(ctx); /* setup authentication information */ - xmpp_conn_set_jid(conn, jid); - xmpp_conn_set_pass(conn, pass); + xmpp_conn_set_string(conn, XMPP_SETTING_JID, jid); + xmpp_conn_set_string(conn, XMPP_SETTING_PASS, pass); /* initiate connection */ xmpp_connect_component(conn, host, port, conn_handler, ctx); diff --git a/examples/register.c b/examples/register.c index e382c99b..752a5750 100644 --- a/examples/register.c +++ b/examples/register.c @@ -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_jid(conn, domain); + xmpp_conn_set_string(conn, XMPP_SETTING_JID, domain); xmpp_free(ctx, domain); /* private data */ diff --git a/examples/roster.c b/examples/roster.c index 6779fc8f..4f95aa0d 100644 --- a/examples/roster.c +++ b/examples/roster.c @@ -118,8 +118,8 @@ int main(int argc, char **argv) */ /* setup authentication information */ - xmpp_conn_set_jid(conn, argv[1]); - xmpp_conn_set_pass(conn, argv[2]); + xmpp_conn_set_string(conn, XMPP_SETTING_JID, argv[1]); + xmpp_conn_set_string(conn, XMPP_SETTING_PASS, argv[2]); /* initiate connection */ xmpp_connect_client(conn, NULL, 0, conn_handler, ctx); diff --git a/examples/vcard.c b/examples/vcard.c index 1ed8cdf2..2af55292 100644 --- a/examples/vcard.c +++ b/examples/vcard.c @@ -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_jid(conn, jid); - xmpp_conn_set_pass(conn, pass); + xmpp_conn_set_string(conn, XMPP_SETTING_JID, jid); + xmpp_conn_set_string(conn, XMPP_SETTING_PASS, pass); vcard.ctx = ctx; xmpp_connect_client(conn, NULL, 0, conn_handler, &vcard); xmpp_run(ctx); diff --git a/src/conn.c b/src/conn.c index d8c7ab1e..f9305643 100644 --- a/src/conn.c +++ b/src/conn.c @@ -217,32 +217,6 @@ 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. @@ -366,95 +340,226 @@ int xmpp_conn_release(xmpp_conn_t *conn) return released; } -/** Get the JID which is or will be bound to the connection. +/** Configure a connection-related int setting * - * @param conn a Strophe connection object + * `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. * - * @return a string containing the full JID or NULL if it has not been set + * @param conn a Strophe connection object + * @param setting The setting that shall be configured + * @param value The value, the settings should get * * @ingroup Connections */ -const char *xmpp_conn_get_jid(const xmpp_conn_t *conn) -{ - return conn->jid; +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; + default: + strophe_warn(conn->ctx, "xmpp", "Invalid Int setting %d", setting); + return; + } } -/** - * Get the JID discovered during binding time. +/** Configure a connection-related string setting * - * This JID will contain the resource used by the current connection. - * This is useful in the case where a resource was not specified for - * binding. + * `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. * - * @param conn a Strophe connection object. + * - \ref XMPP_SETTING_PASS + * Set the password used to authenticate the connection. * - * @return a string containing the full JID or NULL if it's not been discovered + * - \ref XMPP_SETTING_CAFILE + * Set CAfile. * - * @ingroup Connections + * - \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 */ -const char *xmpp_conn_get_bound_jid(const xmpp_conn_t *conn) -{ - return conn->bound_jid; +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); + } } -/** 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. +/** Configure a connection-related pointer setting * - * @param conn a Strophe connection object - * @param jid a full or bare JID + * `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_jid(xmpp_conn_t *conn, const char *jid) -{ - if (conn->jid) - strophe_free(conn->ctx, conn->jid); - conn->jid = strophe_strdup(conn->ctx, jid); +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; + } } -/** 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. +/** Configure a connection-related functionpointer setting * - * @param conn a Strophe connection object - * @param hndl certfail Handler function + * `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 * - * @ingroup TLS + * - \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_certfail_handler(xmpp_conn_t *const conn, - xmpp_certfail_handler hndl) -{ - conn->certfail_handler = hndl; +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; + } } -/** Set the CAfile +/** Get the JID which is or will be bound to the connection. * * @param conn a Strophe connection object - * @param path path to a certificate file * - * @ingroup TLS + * @return a string containing the full JID or NULL if it has not been set + * + * @ingroup Connections */ -void xmpp_conn_set_cafile(xmpp_conn_t *const conn, const char *path) +const char *xmpp_conn_get_jid(const xmpp_conn_t *conn) { - if (conn->tls_cafile) - strophe_free(conn->ctx, conn->tls_cafile); - conn->tls_cafile = strophe_strdup(conn->ctx, path); + return conn->jid; } -/** Set the CApath +/** + * Get the JID discovered during binding time. * - * @param conn a Strophe connection object - * @param path path to a folder containing certificates + * This JID will contain the resource used by the current connection. + * This is useful in the case where a resource was not specified for + * binding. * - * @ingroup TLS + * @param conn a Strophe connection object. + * + * @return a string containing the full JID or NULL if it's not been discovered + * + * @ingroup Connections */ -void xmpp_conn_set_capath(xmpp_conn_t *const conn, const char *path) +const char *xmpp_conn_get_bound_jid(const xmpp_conn_t *conn) { - if (conn->tls_capath) - strophe_free(conn->ctx, conn->tls_capath); - conn->tls_capath = strophe_strdup(conn->ctx, path); + return conn->bound_jid; } /** Retrieve the peer certificate @@ -473,41 +578,6 @@ 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 @@ -524,49 +594,6 @@ 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 @@ -607,22 +634,6 @@ 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 * diff --git a/src/deprecated.c b/src/deprecated.c index 76018a95..e1809f7a 100644 --- a/src/deprecated.c +++ b/src/deprecated.c @@ -19,6 +19,7 @@ */ #include "common.h" +#include /** Allocate memory in a Strophe context. * All Strophe functions will use this to allocate memory. @@ -86,6 +87,10 @@ 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, @@ -217,6 +222,10 @@ 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; @@ -228,11 +237,184 @@ 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 @@ -253,7 +435,8 @@ 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_sockopt_callback(conn, xmpp_sockopt_cb_keepalive); + xmpp_conn_set_functionpointer(conn, XMPP_SETTING_SOCKOPT_CALLBACK, + xmpp_sockopt_cb_keepalive); } /** Disable TLS for this connection, called by users of the library. diff --git a/strophe.h b/strophe.h index b273ffa4..be40bb4a 100644 --- a/strophe.h +++ b/strophe.h @@ -371,37 +371,69 @@ 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. */ + + /* 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); @@ -445,6 +477,7 @@ 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); @@ -712,6 +745,7 @@ void xmpp_rand_nonce(xmpp_rand_t *rand, char *output, size_t len); * deprecation */ #include +#if !defined(XMPP_DEPRECATED) /** * XMPP_DEPRECATED(x) macro to show a compiler warning for deprecated API * functions @@ -730,6 +764,7 @@ 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) @@ -769,6 +804,31 @@ 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 diff --git a/tests/test_xmppaddr.c b/tests/test_xmppaddr.c index 4b25535e..04acd23d 100644 --- a/tests/test_xmppaddr.c +++ b/tests/test_xmppaddr.c @@ -16,6 +16,7 @@ #include #include +#define XMPP_DEPRECATED(x) #include "strophe.h" #include "test.h" @@ -41,12 +42,14 @@ int main() } client_cert[] = { {0, "tests/cert.pem", "tests/key.pem"}, {1, "tests/cert.pem", "tests/key_encrypted.pem"}, - {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}, + /* 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"}, }; const char *srcdir; @@ -78,9 +81,15 @@ int main() keyfile = NULL; if (client_cert[m].needs_callback) - xmpp_conn_set_password_callback(conn, password_callback, NULL); - - xmpp_conn_set_client_cert(conn, certfile, keyfile); + xmpp_conn_set_functionpointer(conn, XMPP_SETTING_PASSWORD_CALLBACK, + password_callback); + + 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); + } xmppaddr_num[0] = '0' + xmpp_conn_cert_xmppaddr_num(conn); From 541c22cb13539854bbc1319d3cd35df82968f237 Mon Sep 17 00:00:00 2001 From: Steffen Jaeckel Date: Mon, 29 Jan 2024 12:17:56 +0100 Subject: [PATCH 2/2] Allow configuring compression level Signed-off-by: Steffen Jaeckel --- src/common.h | 3 ++- src/compression.c | 2 +- src/conn.c | 9 +++++++++ strophe.h | 1 + 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/common.h b/src/common.h index fbb40344..86f7eb6e 100644 --- a/src/common.h +++ b/src/common.h @@ -280,7 +280,8 @@ struct _xmpp_conn_t { struct { struct xmpp_compression *state; - int allowed, supported, dont_reset; + int allowed, dont_reset, level; + int supported; } compression; char *lang; diff --git a/src/compression.c b/src/compression.c index b1a2c3a7..fca2d8b6 100644 --- a/src/compression.c +++ b/src/compression.c @@ -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, Z_DEFAULT_COMPRESSION); + int ret = deflateInit(&comp->compression.stream, conn->compression.level); if (ret != Z_OK) { strophe_free_and_null(conn->ctx, comp->compression.buffer); conn->error = ret; diff --git a/src/conn.c b/src/conn.c index f9305643..708eecd6 100644 --- a/src/conn.c +++ b/src/conn.c @@ -167,6 +167,8 @@ 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); @@ -348,6 +350,10 @@ int xmpp_conn_release(xmpp_conn_t *conn) * 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 @@ -365,6 +371,9 @@ void xmpp_conn_set_int(xmpp_conn_t *conn, 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; diff --git a/strophe.h b/strophe.h index be40bb4a..29293322 100644 --- a/strophe.h +++ b/strophe.h @@ -389,6 +389,7 @@ typedef enum xmpp_conn_setting_t { /* 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