diff --git a/ChangeLog b/ChangeLog index 4c7071f..334ae13 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,14 +6,15 @@ according to RFC2052. - xmpp_connect_raw() provides access to a xmpp_conn object just after establishing TCP connection. This allows to implement in-band - registration or authentication mechanisms. + registration, authentication mechanisms or serverless messaging. - xmpp_conn_t object is reusable now and can be reconnected with saving all handlers, flags, jid and password. - New API: - xmpp_uuid_gen() - xmpp_connect_raw() - - xmpp_conn_raw_open_stream() - - xmpp_conn_raw_tls_start() + - xmpp_conn_open_stream_default() + - xmpp_conn_open_stream() + - xmpp_conn_tls_start() - xmpp_conn_get_flags() - xmpp_conn_set_flags() - xmpp_conn_set_keepalive() diff --git a/src/auth.c b/src/auth.c index 44841e8..b41b4cd 100644 --- a/src/auth.c +++ b/src/auth.c @@ -1140,6 +1140,8 @@ static int _handle_missing_legacy(xmpp_conn_t * const conn, void auth_handle_component_open(xmpp_conn_t * const conn) { + int rc; + /* reset all timed handlers */ handler_reset_timed(conn, 0); @@ -1148,7 +1150,11 @@ void auth_handle_component_open(xmpp_conn_t * const conn) "handshake", NULL, NULL); handler_add_timed(conn, _handle_missing_handshake, HANDSHAKE_TIMEOUT, NULL); - _handle_component_auth(conn); + rc = _handle_component_auth(conn); + if (rc != 0) { + xmpp_error(conn->ctx, "auth", "Component authentication failed."); + xmpp_disconnect(conn); + } } /* Will compute SHA1 and authenticate the component to the server */ @@ -1159,6 +1165,11 @@ int _handle_component_auth(xmpp_conn_t * const conn) char *digest; size_t i; + if (conn->stream_id == NULL) { + xmpp_error(conn->ctx, "auth", "Received no stream id from the server."); + return XMPP_EINT; + } + /* Feed the session id and passphrase to the algorithm. * We need to compute SHA1(session_id + passphrase) */ @@ -1186,7 +1197,6 @@ int _handle_component_auth(xmpp_conn_t * const conn) } else { xmpp_debug(conn->ctx, "auth", "Couldn't allocate memory for component "\ "handshake digest."); - xmpp_disconnect(conn); return XMPP_EMEM; } diff --git a/src/conn.c b/src/conn.c index 7fc3134..b9f9f1f 100644 --- a/src/conn.c +++ b/src/conn.c @@ -50,7 +50,12 @@ static int _disconnect_cleanup(xmpp_conn_t * const conn, void * const userdata); - +static char *_conn_build_stream_tag(xmpp_conn_t * const conn, + char **attributes, size_t attributes_len); +static void _conn_attributes_new(xmpp_conn_t *conn, char **attrs, + char ***attributes, size_t *attributes_len); +static void _conn_attributes_destroy(xmpp_conn_t *conn, char **attributes, + size_t attributes_len); static void _handle_stream_start(char *name, char **attrs, void * const userdata); static void _handle_stream_end(char *name, @@ -531,9 +536,8 @@ int xmpp_connect_component(xmpp_conn_t * const conn, const char * const server, * the only required configuration is a domain (or full jid) passed via * xmpp_conn_set_jid(). * - * Next step should be xmpp_conn_raw_open_stream(). In case of legacy SSL, - * user might want to call xmpp_conn_raw_tls_start() before opening the - * stream. + * Next step should be xmpp_conn_open_stream(). In case of legacy SSL, + * user might want to call xmpp_conn_tls_start() before opening the stream. * * @see xmpp_connect_client() * @@ -574,15 +578,18 @@ void conn_established(xmpp_conn_t * const conn) } } -/** Send an opening stream tag. +/** Send the default opening stream tag. + * The default tag is the one sent by xmpp_connect_client(). * User's connection handler is called with event XMPP_CONN_CONNECT when * server replies with its opening tag. * * @return XMPP_EOK (0) on success a number less than 0 on failure * + * @note The connection must be connected with xmpp_connect_raw(). + * * @ingroup Connections */ -int xmpp_conn_raw_open_stream(xmpp_conn_t * const conn) +int xmpp_conn_open_stream_default(xmpp_conn_t * const conn) { if (!conn->is_raw) return XMPP_EINVOP; @@ -593,11 +600,48 @@ int xmpp_conn_raw_open_stream(xmpp_conn_t * const conn) return XMPP_EOK; } +/** Send an opening stream tag. + * User's connection handler is called with event XMPP_CONN_CONNECT when + * server replies with its opening tag. + * + * @param conn a Strophe connection object + * @param attributes Array of strings in format: even index points to + * an attribute name and odd index points to its value + * @param attributes_len Number of elements in the attributes array, it + * should be number of attributes multiplied by 2 + * + * @return XMPP_EOK (0) on success a number less than 0 on failure + * + * @note The connection must be connected with xmpp_connect_raw(). + * + * @ingroup Connections + */ +int xmpp_conn_open_stream(xmpp_conn_t * const conn, char **attributes, + size_t attributes_len) +{ + char *tag; + + if (!conn->is_raw) + return XMPP_EINVOP; + + tag = _conn_build_stream_tag(conn, attributes, attributes_len); + if (!tag) + return XMPP_EMEM; + + conn_prepare_reset(conn, auth_handle_open_raw); + xmpp_send_raw_string(conn, "%s", tag); + xmpp_free(conn->ctx, tag); + + return XMPP_EOK; +} + /** Start synchronous TLS handshake with the server. * * @return XMPP_EOK (0) on success a number less than 0 on failure + * + * @ingroup Connections */ -int xmpp_conn_raw_tls_start(xmpp_conn_t * const conn) +int xmpp_conn_tls_start(xmpp_conn_t * const conn) { return conn_tls_start(conn); } @@ -951,34 +995,99 @@ int xmpp_conn_is_secured(xmpp_conn_t * const conn) return conn->secured && !conn->tls_failed && conn->tls != NULL ? 1 : 0; } -static void _log_open_tag(xmpp_conn_t *conn, char **attrs) +static char *_conn_build_stream_tag(xmpp_conn_t * const conn, + char **attributes, size_t attributes_len) { - char buf[4096]; - size_t pos; - int len; - int i; - char *attr; + char *tag; + size_t len; + size_t i; - if (!attrs) return; + static const char *tag_head = "ctx, attrs[i]); - len = xmpp_snprintf(&buf[pos], 4096 - pos, " %s='%s'", - attr, attrs[i+1]); - xmpp_free(conn->ctx, attr); - if (len < 0) return; - pos += len; + len = strlen(tag_head) + strlen(tag_tail); + for (i = 0; i < attributes_len; ++i) + len += strlen(attributes[i]) + 2; + tag = xmpp_alloc(conn->ctx, len + 1); + if (!tag) return NULL; + + strcpy(tag, tag_head); + for (i = 0; i < attributes_len; ++i) { + if ((i & 1) == 0) { + strcat(tag, " "); + strcat(tag, attributes[i]); + strcat(tag, "=\""); + } else { + strcat(tag, attributes[i]); + strcat(tag, "\""); + } + } + strcat(tag, tag_tail); + + if (strlen(tag) != len) { + xmpp_error(conn->ctx, "xmpp", "Internal error in " + "_conn_build_stream_tag()."); + xmpp_free(conn->ctx, tag); + tag = NULL; } - len = xmpp_snprintf(&buf[pos], 4096 - pos, ">"); - if (len < 0) return; + return tag; +} - xmpp_debug(conn->ctx, "xmpp", "RECV: %s", buf); +static void _conn_attributes_new(xmpp_conn_t *conn, char **attrs, + char ***attributes, size_t *attributes_len) +{ + char **array = NULL; + size_t nr = 0; + size_t i; + + if (attrs) { + for (; attrs[nr]; ++nr); + array = xmpp_alloc(conn->ctx, sizeof(*array) * nr); + for (i = 0; array && i < nr; ++i) { + array[i] = (i & 1) == 0 ? parser_attr_name(conn->ctx, attrs[i]) + : xmpp_strdup(conn->ctx, attrs[i]); + if (array[i] == NULL) break; + } + if (!array || i < nr) { + xmpp_error(conn->ctx, "xmpp", "Memory allocation error."); + _conn_attributes_destroy(conn, array, i); + array = NULL; + nr = 0; + } + } + *attributes = array; + *attributes_len = nr; +} + +static void _conn_attributes_destroy(xmpp_conn_t *conn, char **attributes, + size_t attributes_len) +{ + size_t i; + + if (attributes) { + for (i = 0; i < attributes_len; ++i) + xmpp_free(conn->ctx, attributes[i]); + xmpp_free(conn->ctx, attributes); + } +} + +static void _log_open_tag(xmpp_conn_t *conn, char **attrs) +{ + char **attributes; + char *tag; + size_t nr; + + _conn_attributes_new(conn, attrs, &attributes, &nr); + tag = _conn_build_stream_tag(conn, attributes, nr); + if (tag) { + xmpp_debug(conn->ctx, "xmpp", "RECV: %s", tag); + xmpp_free(conn->ctx, tag); + } + _conn_attributes_destroy(conn, attributes, nr); } static char *_get_stream_attribute(char **attrs, char *name) @@ -999,6 +1108,7 @@ static void _handle_stream_start(char *name, char **attrs, { xmpp_conn_t *conn = (xmpp_conn_t *)userdata; char *id; + int failed = 0; if (conn->stream_id) xmpp_free(conn->ctx, conn->stream_id); conn->stream_id = NULL; @@ -1009,17 +1119,17 @@ static void _handle_stream_start(char *name, char **attrs, if (id) conn->stream_id = xmpp_strdup(conn->ctx, id); - /* check and log errors */ - if (!id) - xmpp_error(conn->ctx, "conn", "No id attribute."); - else if (!conn->stream_id) + if (id && !conn->stream_id) { xmpp_error(conn->ctx, "conn", "Memory allocation failed."); + failed = 1; + } } else { xmpp_error(conn->ctx, "conn", "Server did not open valid stream." " name = %s.", name); + failed = 1; } - if (conn->stream_id) { + if (!failed) { /* call stream open handler */ conn->open_handler(conn); } else { diff --git a/strophe.h b/strophe.h index 1f26421..5288f77 100644 --- a/strophe.h +++ b/strophe.h @@ -244,8 +244,10 @@ int xmpp_connect_raw(xmpp_conn_t * const conn, unsigned short altport, xmpp_conn_handler callback, void * const userdata); -int xmpp_conn_raw_open_stream(xmpp_conn_t * const conn); -int xmpp_conn_raw_tls_start(xmpp_conn_t * const conn); +int xmpp_conn_open_stream_default(xmpp_conn_t * const conn); +int xmpp_conn_open_stream(xmpp_conn_t * const conn, char **attributes, + size_t attributes_len); +int xmpp_conn_tls_start(xmpp_conn_t * const conn); void xmpp_disconnect(xmpp_conn_t * const conn);