Make it possible to reset password to NULL. It is not required for ANONYMOUS authentication. Also, report an error and disconnect if password is not set and libstrophe should try authentication mechanisms other than ANONYMOUS.
1373 lines
41 KiB
C
1373 lines
41 KiB
C
/* conn.c
|
|
** strophe XMPP client library -- connection object functions
|
|
**
|
|
** Copyright (C) 2005-2009 Collecta, Inc.
|
|
**
|
|
** This software is provided AS-IS with no warranty, either express
|
|
** or implied.
|
|
**
|
|
** This program is dual licensed under the MIT and GPLv3 licenses.
|
|
*/
|
|
|
|
/** @file
|
|
* Connection management.
|
|
*/
|
|
|
|
/** @defgroup Connections Connection management
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
|
|
#include "strophe.h"
|
|
|
|
#include "common.h"
|
|
#include "util.h"
|
|
#include "parser.h"
|
|
#include "resolver.h"
|
|
|
|
#ifndef DEFAULT_SEND_QUEUE_MAX
|
|
/** @def DEFAULT_SEND_QUEUE_MAX
|
|
* The default maximum send queue size. This is currently unused.
|
|
*/
|
|
#define DEFAULT_SEND_QUEUE_MAX 64
|
|
#endif
|
|
#ifndef DISCONNECT_TIMEOUT
|
|
/** @def DISCONNECT_TIMEOUT
|
|
* The time to wait (in milliseconds) for graceful disconnection to
|
|
* complete before the connection is reset. The default is 2 seconds.
|
|
*/
|
|
#define DISCONNECT_TIMEOUT 2000 /* 2 seconds */
|
|
#endif
|
|
#ifndef CONNECT_TIMEOUT
|
|
/** @def CONNECT_TIMEOUT
|
|
* The time to wait (in milliseconds) for a connection attempt to succeed
|
|
* or error. The default is 5 seconds.
|
|
*/
|
|
#define CONNECT_TIMEOUT 5000 /* 5 seconds */
|
|
#endif
|
|
|
|
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, void *const userdata);
|
|
static void _handle_stream_stanza(xmpp_stanza_t *stanza, void *const userdata);
|
|
static unsigned short _conn_default_port(xmpp_conn_t *const conn,
|
|
xmpp_conn_type_t type);
|
|
static void _conn_reset(xmpp_conn_t *const conn);
|
|
static int _conn_connect(xmpp_conn_t *const conn,
|
|
const char *const domain,
|
|
const char *const host,
|
|
unsigned short port,
|
|
xmpp_conn_type_t type,
|
|
xmpp_conn_handler callback,
|
|
void *const userdata);
|
|
|
|
void xmpp_send_error(xmpp_conn_t *const conn,
|
|
xmpp_error_type_t const type,
|
|
char *const text)
|
|
{
|
|
xmpp_stanza_t *error = xmpp_error_new(conn->ctx, type, text);
|
|
|
|
xmpp_send(conn, error);
|
|
|
|
xmpp_stanza_release(error);
|
|
}
|
|
|
|
/** Create a new Strophe connection object.
|
|
*
|
|
* @param ctx a Strophe context object
|
|
*
|
|
* @return a Strophe connection object or NULL on an error
|
|
*
|
|
* @ingroup Connections
|
|
*/
|
|
xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t *const ctx)
|
|
{
|
|
xmpp_conn_t *conn = NULL;
|
|
xmpp_connlist_t *tail, *item;
|
|
|
|
if (ctx == NULL)
|
|
return NULL;
|
|
|
|
conn = xmpp_alloc(ctx, sizeof(xmpp_conn_t));
|
|
if (conn != NULL) {
|
|
conn->ctx = ctx;
|
|
|
|
conn->type = XMPP_UNKNOWN;
|
|
conn->state = XMPP_STATE_DISCONNECTED;
|
|
conn->sock = -1;
|
|
conn->ka_timeout = 0;
|
|
conn->ka_interval = 0;
|
|
conn->tls = NULL;
|
|
conn->timeout_stamp = 0;
|
|
conn->error = 0;
|
|
conn->stream_error = NULL;
|
|
|
|
/* default send parameters */
|
|
conn->blocking_send = 0;
|
|
conn->send_queue_max = DEFAULT_SEND_QUEUE_MAX;
|
|
conn->send_queue_len = 0;
|
|
conn->send_queue_head = NULL;
|
|
conn->send_queue_tail = NULL;
|
|
|
|
/* default timeouts */
|
|
conn->connect_timeout = CONNECT_TIMEOUT;
|
|
|
|
conn->lang = xmpp_strdup(conn->ctx, "en");
|
|
if (!conn->lang) {
|
|
xmpp_free(conn->ctx, conn);
|
|
return NULL;
|
|
}
|
|
conn->domain = NULL;
|
|
conn->jid = NULL;
|
|
conn->pass = NULL;
|
|
conn->stream_id = NULL;
|
|
conn->bound_jid = NULL;
|
|
|
|
conn->is_raw = 0;
|
|
conn->tls_support = 0;
|
|
conn->tls_disabled = 0;
|
|
conn->tls_mandatory = 0;
|
|
conn->tls_legacy_ssl = 0;
|
|
conn->tls_trust = 0;
|
|
conn->tls_failed = 0;
|
|
conn->sasl_support = 0;
|
|
conn->auth_legacy_enabled = 0;
|
|
conn->secured = 0;
|
|
|
|
conn->bind_required = 0;
|
|
conn->session_required = 0;
|
|
|
|
conn->parser =
|
|
parser_new(conn->ctx, _handle_stream_start, _handle_stream_end,
|
|
_handle_stream_stanza, conn);
|
|
conn->reset_parser = 0;
|
|
|
|
conn->authenticated = 0;
|
|
conn->conn_handler = NULL;
|
|
conn->userdata = NULL;
|
|
conn->timed_handlers = NULL;
|
|
/* we own (and will free) the hash values */
|
|
conn->id_handlers = hash_new(conn->ctx, 32, NULL);
|
|
conn->handlers = NULL;
|
|
|
|
/* give the caller a reference to connection */
|
|
conn->ref = 1;
|
|
|
|
/* add connection to ctx->connlist */
|
|
tail = conn->ctx->connlist;
|
|
while (tail && tail->next)
|
|
tail = tail->next;
|
|
|
|
item = xmpp_alloc(conn->ctx, sizeof(xmpp_connlist_t));
|
|
if (!item) {
|
|
xmpp_error(conn->ctx, "xmpp", "failed to allocate memory");
|
|
xmpp_free(conn->ctx, conn->lang);
|
|
parser_free(conn->parser);
|
|
xmpp_free(conn->ctx, conn);
|
|
conn = NULL;
|
|
} else {
|
|
item->conn = conn;
|
|
item->next = NULL;
|
|
|
|
if (tail)
|
|
tail->next = item;
|
|
else
|
|
conn->ctx->connlist = item;
|
|
}
|
|
}
|
|
|
|
return conn;
|
|
}
|
|
|
|
/** Clone a Strophe connection object.
|
|
*
|
|
* @param conn a Strophe connection object
|
|
*
|
|
* @return the same conn object passed in with its reference count
|
|
* incremented by 1
|
|
*
|
|
* @ingroup Connections
|
|
*/
|
|
xmpp_conn_t *xmpp_conn_clone(xmpp_conn_t *const conn)
|
|
{
|
|
conn->ref++;
|
|
return conn;
|
|
}
|
|
|
|
/** Set TCP keepalive parameters
|
|
* Turn on TCP keepalive and set timeout and interval. Zero timeout
|
|
* disables TCP keepalives. The parameters are applied immediately for
|
|
* a non disconnected object. Also, they are applied when the connection
|
|
* object connects successfully.
|
|
*
|
|
* @param conn a Strophe connection object
|
|
* @param timeout TCP keepalive timeout in seconds
|
|
* @param interval TCP keepalive interval in seconds
|
|
*
|
|
* @ingroup Connections
|
|
*/
|
|
void xmpp_conn_set_keepalive(xmpp_conn_t *const conn, int timeout, int interval)
|
|
{
|
|
int ret = 0;
|
|
|
|
conn->ka_timeout = timeout;
|
|
conn->ka_interval = interval;
|
|
|
|
if (conn->state != XMPP_STATE_DISCONNECTED)
|
|
ret = sock_set_keepalive(conn->sock, timeout, interval);
|
|
|
|
if (ret < 0) {
|
|
xmpp_error(conn->ctx, "xmpp", "Setting TCP keepalive (%d,%d) error: %d",
|
|
timeout, interval, sock_error());
|
|
}
|
|
}
|
|
|
|
/** Release a Strophe connection object.
|
|
* Decrement the reference count by one for a connection, freeing the
|
|
* connection object if the count reaches 0.
|
|
*
|
|
* @param conn a Strophe connection object
|
|
*
|
|
* @return TRUE if the connection object was freed and FALSE otherwise
|
|
*
|
|
* @ingroup Connections
|
|
*/
|
|
int xmpp_conn_release(xmpp_conn_t *const conn)
|
|
{
|
|
xmpp_ctx_t *ctx;
|
|
xmpp_connlist_t *item, *prev;
|
|
xmpp_handlist_t *hlitem, *thli;
|
|
hash_iterator_t *iter;
|
|
const char *key;
|
|
int released = 0;
|
|
|
|
if (conn->ref > 1)
|
|
conn->ref--;
|
|
else {
|
|
ctx = conn->ctx;
|
|
|
|
if (conn->state == XMPP_STATE_CONNECTING ||
|
|
conn->state == XMPP_STATE_CONNECTED) {
|
|
conn_disconnect(conn);
|
|
}
|
|
|
|
/* remove connection from context's connlist */
|
|
if (ctx->connlist->conn == conn) {
|
|
item = ctx->connlist;
|
|
ctx->connlist = item->next;
|
|
xmpp_free(ctx, item);
|
|
} else {
|
|
prev = NULL;
|
|
item = ctx->connlist;
|
|
while (item && item->conn != conn) {
|
|
prev = item;
|
|
item = item->next;
|
|
}
|
|
|
|
if (!item) {
|
|
xmpp_error(ctx, "xmpp", "Connection not in context's list\n");
|
|
} else {
|
|
prev->next = item->next;
|
|
xmpp_free(ctx, item);
|
|
}
|
|
}
|
|
|
|
_conn_reset(conn);
|
|
|
|
/* free handler stuff
|
|
* note that userdata is the responsibility of the client
|
|
* and the handler pointers don't need to be freed since they
|
|
* are pointers to functions */
|
|
|
|
hlitem = conn->timed_handlers;
|
|
while (hlitem) {
|
|
thli = hlitem;
|
|
hlitem = hlitem->next;
|
|
|
|
xmpp_free(ctx, thli);
|
|
}
|
|
|
|
/* id handlers
|
|
* we have to traverse the hash table freeing list elements
|
|
* then release the hash table */
|
|
iter = hash_iter_new(conn->id_handlers);
|
|
while ((key = hash_iter_next(iter))) {
|
|
hlitem = (xmpp_handlist_t *)hash_get(conn->id_handlers, key);
|
|
while (hlitem) {
|
|
thli = hlitem;
|
|
hlitem = hlitem->next;
|
|
xmpp_free(conn->ctx, thli->u.id);
|
|
xmpp_free(conn->ctx, thli);
|
|
}
|
|
}
|
|
hash_iter_release(iter);
|
|
hash_release(conn->id_handlers);
|
|
|
|
hlitem = conn->handlers;
|
|
while (hlitem) {
|
|
thli = hlitem;
|
|
hlitem = hlitem->next;
|
|
|
|
if (thli->u.ns)
|
|
xmpp_free(ctx, thli->u.ns);
|
|
if (thli->u.name)
|
|
xmpp_free(ctx, thli->u.name);
|
|
if (thli->u.type)
|
|
xmpp_free(ctx, thli->u.type);
|
|
xmpp_free(ctx, thli);
|
|
}
|
|
|
|
parser_free(conn->parser);
|
|
|
|
if (conn->jid)
|
|
xmpp_free(ctx, conn->jid);
|
|
if (conn->pass)
|
|
xmpp_free(ctx, conn->pass);
|
|
if (conn->lang)
|
|
xmpp_free(ctx, conn->lang);
|
|
xmpp_free(ctx, conn);
|
|
released = 1;
|
|
}
|
|
|
|
return released;
|
|
}
|
|
|
|
/** Get the JID which is or will be bound to the connection.
|
|
*
|
|
* @param conn a Strophe connection object
|
|
*
|
|
* @return a string containing the full JID or NULL if it has not been set
|
|
*
|
|
* @ingroup Connections
|
|
*/
|
|
const char *xmpp_conn_get_jid(const xmpp_conn_t *const conn)
|
|
{
|
|
return conn->jid;
|
|
}
|
|
|
|
/**
|
|
* Get the JID discovered during binding time.
|
|
*
|
|
* 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.
|
|
*
|
|
* @param conn a Strophe connection object.
|
|
*
|
|
* @return a string containing the full JID or NULL if it's not been discovered
|
|
*
|
|
* @ingroup Connections
|
|
*/
|
|
const char *xmpp_conn_get_bound_jid(const xmpp_conn_t *const 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 *const conn, const char *const jid)
|
|
{
|
|
if (conn->jid)
|
|
xmpp_free(conn->ctx, conn->jid);
|
|
conn->jid = xmpp_strdup(conn->ctx, jid);
|
|
}
|
|
|
|
/** Get the password used for authentication of a connection.
|
|
*
|
|
* @param conn a Strophe connection object
|
|
*
|
|
* @return a string containing the password or NULL if it has not been set
|
|
*
|
|
* @ingroup Connections
|
|
*/
|
|
const char *xmpp_conn_get_pass(const xmpp_conn_t *const 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 *const conn, const char *const pass)
|
|
{
|
|
if (conn->pass)
|
|
xmpp_free(conn->ctx, conn->pass);
|
|
conn->pass = pass ? xmpp_strdup(conn->ctx, pass) : NULL;
|
|
}
|
|
|
|
/** Get the strophe context that the connection is associated with.
|
|
* @param conn a Strophe connection object
|
|
*
|
|
* @return a Strophe context
|
|
*
|
|
* @ingroup Connections
|
|
*/
|
|
xmpp_ctx_t *xmpp_conn_get_context(xmpp_conn_t *const conn)
|
|
{
|
|
return conn->ctx;
|
|
}
|
|
|
|
/** Initiate a connection to the XMPP server.
|
|
* This function returns immediately after starting the connection
|
|
* process to the XMPP server, and notifications of connection state changes
|
|
* will be sent to the callback function. The domain and port to connect to
|
|
* are usually determined by an SRV lookup for the xmpp-client service at
|
|
* the domain specified in the JID. If SRV lookup fails, altdomain and
|
|
* altport will be used instead if specified.
|
|
*
|
|
* @param conn a Strophe connection object
|
|
* @param altdomain a string with domain to use if SRV lookup fails. If this
|
|
* is NULL, the domain from the JID will be used.
|
|
* @param altport an integer port number to use if SRV lookup fails. If this
|
|
* is 0, the default port will be assumed.
|
|
* @param callback a xmpp_conn_handler callback function that will receive
|
|
* notifications of connection status
|
|
* @param userdata an opaque data pointer that will be passed to the callback
|
|
*
|
|
* @return XMPP_EOK (0) on success or a number less than 0 on failure
|
|
*
|
|
* @ingroup Connections
|
|
*/
|
|
int xmpp_connect_client(xmpp_conn_t *const conn,
|
|
const char *const altdomain,
|
|
unsigned short altport,
|
|
xmpp_conn_handler callback,
|
|
void *const userdata)
|
|
{
|
|
resolver_srv_rr_t *srv_rr_list = NULL;
|
|
resolver_srv_rr_t *rr;
|
|
char *domain;
|
|
const char *host = NULL;
|
|
unsigned short port = 0;
|
|
int found = XMPP_DOMAIN_NOT_FOUND;
|
|
int rc;
|
|
|
|
domain = xmpp_jid_domain(conn->ctx, conn->jid);
|
|
if (!domain)
|
|
return XMPP_EMEM;
|
|
|
|
if (altdomain != NULL) {
|
|
xmpp_debug(conn->ctx, "xmpp", "Connecting via altdomain.");
|
|
host = altdomain;
|
|
port = altport ? altport : _conn_default_port(conn, XMPP_CLIENT);
|
|
found = XMPP_DOMAIN_ALTDOMAIN;
|
|
|
|
/* SSL tunneled connection on 5223 port is legacy and doesn't
|
|
* have an SRV record. */
|
|
} else if (!conn->tls_legacy_ssl) {
|
|
found = resolver_srv_lookup(conn->ctx, "xmpp-client", "tcp", domain,
|
|
&srv_rr_list);
|
|
}
|
|
|
|
if (XMPP_DOMAIN_NOT_FOUND == found) {
|
|
xmpp_debug(conn->ctx, "xmpp",
|
|
"SRV lookup failed, "
|
|
"connecting via domain.");
|
|
host = domain;
|
|
port = altport ? altport : _conn_default_port(conn, XMPP_CLIENT);
|
|
found = XMPP_DOMAIN_ALTDOMAIN;
|
|
}
|
|
|
|
rr = srv_rr_list;
|
|
do {
|
|
if (XMPP_DOMAIN_FOUND == found && rr != NULL) {
|
|
host = rr->target;
|
|
port = rr->port;
|
|
rr = rr->next;
|
|
}
|
|
rc = _conn_connect(conn, domain, host, port, XMPP_CLIENT, callback,
|
|
userdata);
|
|
} while (rc != 0 && rr != NULL);
|
|
|
|
xmpp_free(conn->ctx, domain);
|
|
resolver_srv_free(conn->ctx, srv_rr_list);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/** Initiate a component connection to server.
|
|
* This function returns immediately after starting the connection
|
|
* process to the XMPP server, and notifications of connection state changes
|
|
* will be sent to the internal callback function that will set up handler
|
|
* for the component handshake as defined in XEP-0114.
|
|
* The domain and port to connect to must be provided in this case as the JID
|
|
* provided to the call serves as component identifier to the server and is
|
|
* not subject to DNS resolution.
|
|
*
|
|
* @param conn a Strophe connection object
|
|
* @param server a string with domain to use directly as the domain can't be
|
|
* extracted from the component name/JID. If this is not set, the call
|
|
* will fail.
|
|
* @param port an integer port number to use to connect to server expecting
|
|
* an external component. If this is 0, the port 5347 will be assumed.
|
|
* @param callback a xmpp_conn_handler callback function that will receive
|
|
* notifications of connection status
|
|
* @param userdata an opaque data pointer that will be passed to the callback
|
|
*
|
|
* @return XMPP_EOK (0) on success or a number less than 0 on failure
|
|
*
|
|
* @ingroup Connections
|
|
*/
|
|
int xmpp_connect_component(xmpp_conn_t *const conn,
|
|
const char *const server,
|
|
unsigned short port,
|
|
xmpp_conn_handler callback,
|
|
void *const userdata)
|
|
{
|
|
/* The server domain, jid and password MUST be specified. */
|
|
if (!(server && conn->jid && conn->pass))
|
|
return XMPP_EINVOP;
|
|
|
|
/* XEP-0114 does not support TLS */
|
|
xmpp_conn_disable_tls(conn);
|
|
if (!conn->tls_disabled) {
|
|
xmpp_error(conn->ctx, "conn",
|
|
"Failed to disable TLS. "
|
|
"XEP-0114 does not support TLS");
|
|
return XMPP_EINT;
|
|
}
|
|
|
|
port = port ? port : _conn_default_port(conn, XMPP_COMPONENT);
|
|
/* JID serves as an identifier here and will be used as "to" attribute
|
|
of the stream */
|
|
return _conn_connect(conn, conn->jid, server, port, XMPP_COMPONENT,
|
|
callback, userdata);
|
|
}
|
|
|
|
/** Initiate a raw connection to the XMPP server.
|
|
* Arguments and behaviour of the function are similar to
|
|
* xmpp_connect_client(), but it skips authentication process. In opposite to
|
|
* xmpp_connect_client() during connection process two events are generated
|
|
* instead of one. User's callback is called with event XMPP_CONN_RAW_CONNECT
|
|
* when the TCP connection with the server is established. At this point user
|
|
* might want to open an XMPP stream with xmpp_conn_open_stream() or establish
|
|
* TLS session with xmpp_conn_tls_start(). Event XMPP_CONN_CONNECT is generated
|
|
* when the XMPP stream is opened successfully and user may send stanzas over
|
|
* the connection.
|
|
*
|
|
* This function doesn't use password nor node part of a jid. Therefore,
|
|
* the only required configuration is a domain (or full jid) passed via
|
|
* xmpp_conn_set_jid().
|
|
*
|
|
* @see xmpp_connect_client()
|
|
*
|
|
* @return XMPP_EOK (0) on success a number less than 0 on failure
|
|
*
|
|
* @ingroup Connections
|
|
*/
|
|
int xmpp_connect_raw(xmpp_conn_t *const conn,
|
|
const char *const altdomain,
|
|
unsigned short altport,
|
|
xmpp_conn_handler callback,
|
|
void *const userdata)
|
|
{
|
|
conn->is_raw = 1;
|
|
return xmpp_connect_client(conn, altdomain, altport, callback, userdata);
|
|
}
|
|
|
|
/* Called when tcp connection is established. */
|
|
void conn_established(xmpp_conn_t *const conn)
|
|
{
|
|
if (conn->tls_legacy_ssl && !conn->is_raw) {
|
|
xmpp_debug(conn->ctx, "xmpp", "using legacy SSL connection");
|
|
if (conn_tls_start(conn) != 0) {
|
|
conn_disconnect(conn);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (conn->is_raw) {
|
|
handler_reset_timed(conn, 0);
|
|
/* we skip authentication for a "raw" connection, but the event loop
|
|
ignores user's handlers when conn->authenticated is not set. */
|
|
conn->authenticated = 1;
|
|
conn->conn_handler(conn, XMPP_CONN_RAW_CONNECT, 0, NULL,
|
|
conn->userdata);
|
|
} else {
|
|
/* send stream init */
|
|
conn_open_stream(conn);
|
|
}
|
|
}
|
|
|
|
/** 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_open_stream_default(xmpp_conn_t *const conn)
|
|
{
|
|
if (!conn->is_raw)
|
|
return XMPP_EINVOP;
|
|
|
|
conn_prepare_reset(conn, auth_handle_open_raw);
|
|
conn_open_stream(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, "<?xml version=\"1.0\"?>%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_tls_start(xmpp_conn_t *const conn)
|
|
{
|
|
return conn_tls_start(conn);
|
|
}
|
|
|
|
/** Cleanly disconnect the connection.
|
|
* This function is only called by the stream parser when </stream:stream>
|
|
* is received, and it not intended to be called by code outside of Strophe.
|
|
*
|
|
* @param conn a Strophe connection object
|
|
*/
|
|
void conn_disconnect_clean(xmpp_conn_t *const conn)
|
|
{
|
|
/* remove the timed handler */
|
|
xmpp_timed_handler_delete(conn, _disconnect_cleanup);
|
|
|
|
conn_disconnect(conn);
|
|
}
|
|
|
|
/** Disconnect from the XMPP server.
|
|
* This function immediately disconnects from the XMPP server, and should
|
|
* not be used outside of the Strophe library.
|
|
*
|
|
* @param conn a Strophe connection object
|
|
*/
|
|
void conn_disconnect(xmpp_conn_t *const conn)
|
|
{
|
|
xmpp_debug(conn->ctx, "xmpp", "Closing socket.");
|
|
conn->state = XMPP_STATE_DISCONNECTED;
|
|
if (conn->tls) {
|
|
tls_stop(conn->tls);
|
|
tls_free(conn->tls);
|
|
conn->tls = NULL;
|
|
}
|
|
sock_close(conn->sock);
|
|
|
|
/* fire off connection handler */
|
|
conn->conn_handler(conn, XMPP_CONN_DISCONNECT, conn->error,
|
|
conn->stream_error, conn->userdata);
|
|
}
|
|
|
|
/* prepares a parser reset. this is called from handlers. we can't
|
|
* reset the parser immediately as it is not re-entrant. */
|
|
void conn_prepare_reset(xmpp_conn_t *const conn, xmpp_open_handler handler)
|
|
{
|
|
conn->reset_parser = 1;
|
|
conn->open_handler = handler;
|
|
}
|
|
|
|
/* reset the parser */
|
|
void conn_parser_reset(xmpp_conn_t *const conn)
|
|
{
|
|
conn->reset_parser = 0;
|
|
parser_reset(conn->parser);
|
|
}
|
|
|
|
/** Initiate termination of the connection to the XMPP server.
|
|
* This function starts the disconnection sequence by sending
|
|
* </stream:stream> to the XMPP server. This function will do nothing
|
|
* if the connection state is different from CONNECTING or CONNECTED.
|
|
*
|
|
* @param conn a Strophe connection object
|
|
*
|
|
* @ingroup Connections
|
|
*/
|
|
void xmpp_disconnect(xmpp_conn_t *const conn)
|
|
{
|
|
if (conn->state != XMPP_STATE_CONNECTING &&
|
|
conn->state != XMPP_STATE_CONNECTED)
|
|
return;
|
|
|
|
/* close the stream */
|
|
xmpp_send_raw_string(conn, "</stream:stream>");
|
|
|
|
/* setup timed handler in case disconnect takes too long */
|
|
handler_add_timed(conn, _disconnect_cleanup, DISCONNECT_TIMEOUT, NULL);
|
|
}
|
|
|
|
/** Send a raw string to the XMPP server.
|
|
* This function is a convenience function to send raw string data to the
|
|
* XMPP server. It is used by Strophe to send short messages instead of
|
|
* building up an XML stanza with DOM methods. This should be used with care
|
|
* as it does not validate the data; invalid data may result in immediate
|
|
* stream termination by the XMPP server.
|
|
*
|
|
* @param conn a Strophe connection object
|
|
* @param fmt a printf-style format string followed by a variable list of
|
|
* arguments to format
|
|
*
|
|
* @ingroup Connections
|
|
*/
|
|
void xmpp_send_raw_string(xmpp_conn_t *const conn, const char *const fmt, ...)
|
|
{
|
|
va_list ap;
|
|
size_t len;
|
|
char buf[1024]; /* small buffer for common case */
|
|
char *bigbuf;
|
|
|
|
va_start(ap, fmt);
|
|
len = xmpp_vsnprintf(buf, sizeof(buf), fmt, ap);
|
|
va_end(ap);
|
|
|
|
if (len >= sizeof(buf)) {
|
|
/* we need more space for this data, so we allocate a big
|
|
* enough buffer and print to that */
|
|
len++; /* account for trailing \0 */
|
|
bigbuf = xmpp_alloc(conn->ctx, len);
|
|
if (!bigbuf) {
|
|
xmpp_debug(conn->ctx, "xmpp",
|
|
"Could not allocate memory for send_raw_string");
|
|
return;
|
|
}
|
|
va_start(ap, fmt);
|
|
xmpp_vsnprintf(bigbuf, len, fmt, ap);
|
|
va_end(ap);
|
|
|
|
xmpp_debug(conn->ctx, "conn", "SENT: %s", bigbuf);
|
|
|
|
/* len - 1 so we don't send trailing \0 */
|
|
xmpp_send_raw(conn, bigbuf, len - 1);
|
|
|
|
xmpp_free(conn->ctx, bigbuf);
|
|
} else {
|
|
xmpp_debug(conn->ctx, "conn", "SENT: %s", buf);
|
|
xmpp_send_raw(conn, buf, len);
|
|
}
|
|
}
|
|
|
|
/** Send raw bytes to the XMPP server.
|
|
* This function is a convenience function to send raw bytes to the
|
|
* XMPP server. It is used primarily by xmpp_send_raw_string(). This
|
|
* function should be used with care as it does not validate the bytes and
|
|
* invalid data may result in stream termination by the XMPP server.
|
|
*
|
|
* @param conn a Strophe connection object
|
|
* @param data a buffer of raw bytes
|
|
* @param len the length of the data in the buffer
|
|
*
|
|
* @ingroup Connections
|
|
*/
|
|
void xmpp_send_raw(xmpp_conn_t *const conn,
|
|
const char *const data,
|
|
const size_t len)
|
|
{
|
|
xmpp_send_queue_t *item;
|
|
|
|
if (conn->state != XMPP_STATE_CONNECTED)
|
|
return;
|
|
|
|
/* create send queue item for queue */
|
|
item = xmpp_alloc(conn->ctx, sizeof(xmpp_send_queue_t));
|
|
if (!item)
|
|
return;
|
|
|
|
item->data = xmpp_alloc(conn->ctx, len);
|
|
if (!item->data) {
|
|
xmpp_free(conn->ctx, item);
|
|
return;
|
|
}
|
|
memcpy(item->data, data, len);
|
|
item->len = len;
|
|
item->next = NULL;
|
|
item->written = 0;
|
|
|
|
/* add item to the send queue */
|
|
if (!conn->send_queue_tail) {
|
|
/* first item, set head and tail */
|
|
conn->send_queue_head = item;
|
|
conn->send_queue_tail = item;
|
|
} else {
|
|
/* add to the tail */
|
|
conn->send_queue_tail->next = item;
|
|
conn->send_queue_tail = item;
|
|
}
|
|
conn->send_queue_len++;
|
|
}
|
|
|
|
/** Send an XML stanza to the XMPP server.
|
|
* This is the main way to send data to the XMPP server. The function will
|
|
* terminate without action if the connection state is not CONNECTED.
|
|
*
|
|
* @param conn a Strophe connection object
|
|
* @param stanza a Strophe stanza object
|
|
*
|
|
* @ingroup Connections
|
|
*/
|
|
void xmpp_send(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza)
|
|
{
|
|
char *buf;
|
|
size_t len;
|
|
|
|
if (conn->state == XMPP_STATE_CONNECTED) {
|
|
if (xmpp_stanza_to_text(stanza, &buf, &len) == 0) {
|
|
xmpp_send_raw(conn, buf, len);
|
|
xmpp_debug(conn->ctx, "conn", "SENT: %s", buf);
|
|
xmpp_free(conn->ctx, buf);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Send the opening <stream:stream> tag to the server.
|
|
* This function is used by Strophe to begin an XMPP stream. It should
|
|
* not be used outside of the library.
|
|
*
|
|
* @param conn a Strophe connection object
|
|
*/
|
|
void conn_open_stream(xmpp_conn_t *const conn)
|
|
{
|
|
xmpp_send_raw_string(conn,
|
|
"<?xml version=\"1.0\"?>"
|
|
"<stream:stream to=\"%s\" "
|
|
"xml:lang=\"%s\" "
|
|
"version=\"1.0\" "
|
|
"xmlns=\"%s\" "
|
|
"xmlns:stream=\"%s\">",
|
|
conn->domain, conn->lang,
|
|
conn->type == XMPP_CLIENT ? XMPP_NS_CLIENT
|
|
: XMPP_NS_COMPONENT,
|
|
XMPP_NS_STREAMS);
|
|
}
|
|
|
|
int conn_tls_start(xmpp_conn_t *const conn)
|
|
{
|
|
int rc;
|
|
|
|
if (conn->tls_disabled) {
|
|
conn->tls = NULL;
|
|
rc = XMPP_EINVOP;
|
|
} else {
|
|
conn->tls = tls_new(conn);
|
|
rc = conn->tls == NULL ? XMPP_EMEM : 0;
|
|
}
|
|
|
|
if (conn->tls != NULL) {
|
|
if (tls_start(conn->tls)) {
|
|
conn->secured = 1;
|
|
} else {
|
|
rc = XMPP_EINT;
|
|
conn->error = tls_error(conn->tls);
|
|
tls_free(conn->tls);
|
|
conn->tls = NULL;
|
|
conn->tls_failed = 1;
|
|
}
|
|
}
|
|
if (rc != 0) {
|
|
xmpp_debug(conn->ctx, "conn",
|
|
"Couldn't start TLS! "
|
|
"error %d tls_error %d",
|
|
rc, conn->error);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/** Return applied flags for the connection.
|
|
*
|
|
* @param conn a Strophe connection object
|
|
*
|
|
* @return ORed connection flags that are applied for the connection.
|
|
*
|
|
* @ingroup Connections
|
|
*/
|
|
long xmpp_conn_get_flags(const xmpp_conn_t *const conn)
|
|
{
|
|
long flags;
|
|
|
|
flags = XMPP_CONN_FLAG_DISABLE_TLS * conn->tls_disabled |
|
|
XMPP_CONN_FLAG_MANDATORY_TLS * conn->tls_mandatory |
|
|
XMPP_CONN_FLAG_LEGACY_SSL * conn->tls_legacy_ssl |
|
|
XMPP_CONN_FLAG_TRUST_TLS * conn->tls_trust |
|
|
XMPP_CONN_FLAG_LEGACY_AUTH * conn->auth_legacy_enabled;
|
|
;
|
|
|
|
return flags;
|
|
}
|
|
|
|
/** Set flags for the connection.
|
|
* This function applies set flags and resets unset ones. Default connection
|
|
* configuration is all flags unset. Flags can be applied only for a connection
|
|
* in disconnected state.
|
|
* All unsupported flags are ignored. If a flag is unset after successful set
|
|
* operation then the flag is not supported by current version.
|
|
*
|
|
* Supported flags are:
|
|
*
|
|
* - XMPP_CONN_FLAG_DISABLE_TLS
|
|
* - XMPP_CONN_FLAG_MANDATORY_TLS
|
|
* - XMPP_CONN_FLAG_LEGACY_SSL
|
|
* - XMPP_CONN_FLAG_TRUST_TLS
|
|
* - XMPP_CONN_FLAG_LEGACY_AUTH
|
|
*
|
|
* @param conn a Strophe connection object
|
|
* @param flags ORed connection flags
|
|
*
|
|
* @return XMPP_EOK (0) on success or a number less than 0 on failure
|
|
*
|
|
* @ingroup Connections
|
|
*/
|
|
int xmpp_conn_set_flags(xmpp_conn_t *const conn, long flags)
|
|
{
|
|
if (conn->state != XMPP_STATE_DISCONNECTED) {
|
|
xmpp_error(conn->ctx, "conn",
|
|
"Flags can be set only "
|
|
"for disconnected connection");
|
|
return XMPP_EINVOP;
|
|
}
|
|
if (flags & XMPP_CONN_FLAG_DISABLE_TLS &&
|
|
flags & (XMPP_CONN_FLAG_MANDATORY_TLS | XMPP_CONN_FLAG_LEGACY_SSL |
|
|
XMPP_CONN_FLAG_TRUST_TLS)) {
|
|
xmpp_error(conn->ctx, "conn", "Flags 0x%04lx conflict", flags);
|
|
return XMPP_EINVOP;
|
|
}
|
|
|
|
conn->tls_disabled = (flags & XMPP_CONN_FLAG_DISABLE_TLS) ? 1 : 0;
|
|
conn->tls_mandatory = (flags & XMPP_CONN_FLAG_MANDATORY_TLS) ? 1 : 0;
|
|
conn->tls_legacy_ssl = (flags & XMPP_CONN_FLAG_LEGACY_SSL) ? 1 : 0;
|
|
conn->tls_trust = (flags & XMPP_CONN_FLAG_TRUST_TLS) ? 1 : 0;
|
|
conn->auth_legacy_enabled = (flags & XMPP_CONN_FLAG_LEGACY_AUTH) ? 1 : 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** Disable TLS for this connection, called by users of the library.
|
|
* Occasionally a server will be misconfigured to send the starttls
|
|
* feature, but will not support the handshake.
|
|
*
|
|
* @param conn a Strophe connection object
|
|
*
|
|
* @note this function is deprecated
|
|
* @see xmpp_conn_set_flags()
|
|
*
|
|
* @ingroup Connections
|
|
*/
|
|
void xmpp_conn_disable_tls(xmpp_conn_t *const conn)
|
|
{
|
|
long flags = xmpp_conn_get_flags(conn);
|
|
|
|
flags |= XMPP_CONN_FLAG_DISABLE_TLS;
|
|
(void)xmpp_conn_set_flags(conn, flags);
|
|
}
|
|
|
|
/** Return whether TLS session is established or not.
|
|
*
|
|
* @return TRUE if TLS session is established and FALSE otherwise
|
|
*
|
|
* @ingroup Connections
|
|
*/
|
|
int xmpp_conn_is_secured(xmpp_conn_t *const conn)
|
|
{
|
|
return conn->secured && !conn->tls_failed && conn->tls != NULL;
|
|
}
|
|
|
|
/**
|
|
* @return TRUE if connection is in connecting state and FALSE otherwise
|
|
*
|
|
* @ingroup Connections
|
|
*/
|
|
int xmpp_conn_is_connecting(xmpp_conn_t *const conn)
|
|
{
|
|
return conn->state == XMPP_STATE_CONNECTING;
|
|
}
|
|
|
|
/**
|
|
* @return TRUE if connection is in connected state and FALSE otherwise
|
|
*
|
|
* @ingroup Connections
|
|
*/
|
|
int xmpp_conn_is_connected(xmpp_conn_t *const conn)
|
|
{
|
|
return conn->state == XMPP_STATE_CONNECTED;
|
|
}
|
|
|
|
/**
|
|
* @return TRUE if connection is in disconnected state and FALSE otherwise
|
|
*
|
|
* @ingroup Connections
|
|
*/
|
|
int xmpp_conn_is_disconnected(xmpp_conn_t *const conn)
|
|
{
|
|
return conn->state == XMPP_STATE_DISCONNECTED;
|
|
}
|
|
|
|
/* timed handler for cleanup if normal disconnect procedure takes too long */
|
|
static int _disconnect_cleanup(xmpp_conn_t *const conn, void *const userdata)
|
|
{
|
|
UNUSED(userdata);
|
|
|
|
xmpp_debug(conn->ctx, "xmpp", "disconnection forced by cleanup timeout");
|
|
|
|
conn_disconnect(conn);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static char *_conn_build_stream_tag(xmpp_conn_t *const conn,
|
|
char **attributes,
|
|
size_t attributes_len)
|
|
{
|
|
char *tag;
|
|
size_t len;
|
|
size_t i;
|
|
|
|
static const char *tag_head = "<stream:stream";
|
|
static const char *tag_tail = ">";
|
|
|
|
/* ignore the last element unless number is even */
|
|
attributes_len &= ~(size_t)1;
|
|
|
|
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;
|
|
}
|
|
|
|
return tag;
|
|
}
|
|
|
|
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)
|
|
{
|
|
int i;
|
|
|
|
if (!attrs)
|
|
return NULL;
|
|
|
|
for (i = 0; attrs[i]; i += 2)
|
|
if (strcmp(name, attrs[i]) == 0)
|
|
return attrs[i + 1];
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void _handle_stream_start(char *name, char **attrs, void *const userdata)
|
|
{
|
|
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;
|
|
|
|
if (strcmp(name, "stream") == 0) {
|
|
_log_open_tag(conn, attrs);
|
|
id = _get_stream_attribute(attrs, "id");
|
|
if (id)
|
|
conn->stream_id = xmpp_strdup(conn->ctx, 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 (!failed) {
|
|
/* call stream open handler */
|
|
conn->open_handler(conn);
|
|
} else {
|
|
conn_disconnect(conn);
|
|
}
|
|
}
|
|
|
|
static void _handle_stream_end(char *name, void *const userdata)
|
|
{
|
|
xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
|
|
|
|
UNUSED(name);
|
|
|
|
/* stream is over */
|
|
xmpp_debug(conn->ctx, "xmpp", "RECV: </stream:stream>");
|
|
conn_disconnect_clean(conn);
|
|
}
|
|
|
|
static void _handle_stream_stanza(xmpp_stanza_t *stanza, void *const userdata)
|
|
{
|
|
xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
|
|
char *buf;
|
|
size_t len;
|
|
|
|
if (xmpp_stanza_to_text(stanza, &buf, &len) == 0) {
|
|
xmpp_debug(conn->ctx, "xmpp", "RECV: %s", buf);
|
|
xmpp_free(conn->ctx, buf);
|
|
}
|
|
|
|
handler_fire_stanza(conn, stanza);
|
|
}
|
|
|
|
static unsigned short _conn_default_port(xmpp_conn_t *const conn,
|
|
xmpp_conn_type_t type)
|
|
{
|
|
switch (type) {
|
|
case XMPP_CLIENT:
|
|
return conn->tls_legacy_ssl ? XMPP_PORT_CLIENT_LEGACY_SSL
|
|
: XMPP_PORT_CLIENT;
|
|
case XMPP_COMPONENT:
|
|
return XMPP_PORT_COMPONENT;
|
|
default:
|
|
return 0;
|
|
};
|
|
}
|
|
|
|
static void _conn_reset(xmpp_conn_t *const conn)
|
|
{
|
|
xmpp_ctx_t *ctx = conn->ctx;
|
|
xmpp_send_queue_t *sq, *tsq;
|
|
|
|
if (conn->state != XMPP_STATE_DISCONNECTED) {
|
|
xmpp_debug(ctx, "conn", "Can't reset connected object.");
|
|
return;
|
|
}
|
|
|
|
/* free queued */
|
|
sq = conn->send_queue_head;
|
|
while (sq) {
|
|
tsq = sq;
|
|
sq = sq->next;
|
|
xmpp_free(ctx, tsq->data);
|
|
xmpp_free(ctx, tsq);
|
|
}
|
|
conn->send_queue_head = NULL;
|
|
conn->send_queue_tail = NULL;
|
|
conn->send_queue_len = 0;
|
|
|
|
if (conn->stream_error) {
|
|
xmpp_stanza_release(conn->stream_error->stanza);
|
|
if (conn->stream_error->text)
|
|
xmpp_free(ctx, conn->stream_error->text);
|
|
xmpp_free(ctx, conn->stream_error);
|
|
conn->stream_error = NULL;
|
|
}
|
|
|
|
if (conn->domain)
|
|
xmpp_free(ctx, conn->domain);
|
|
if (conn->bound_jid)
|
|
xmpp_free(ctx, conn->bound_jid);
|
|
if (conn->stream_id)
|
|
xmpp_free(ctx, conn->stream_id);
|
|
conn->domain = NULL;
|
|
conn->bound_jid = NULL;
|
|
conn->stream_id = NULL;
|
|
conn->authenticated = 0;
|
|
conn->secured = 0;
|
|
conn->tls_failed = 0;
|
|
conn->error = 0;
|
|
|
|
conn->tls_support = 0;
|
|
conn->bind_required = 0;
|
|
conn->session_required = 0;
|
|
|
|
handler_system_delete_all(conn);
|
|
}
|
|
|
|
static int _conn_connect(xmpp_conn_t *const conn,
|
|
const char *const domain,
|
|
const char *const host,
|
|
unsigned short port,
|
|
xmpp_conn_type_t type,
|
|
xmpp_conn_handler callback,
|
|
void *const userdata)
|
|
{
|
|
xmpp_open_handler open_handler;
|
|
|
|
if (conn->state != XMPP_STATE_DISCONNECTED)
|
|
return XMPP_EINVOP;
|
|
if (type != XMPP_CLIENT && type != XMPP_COMPONENT)
|
|
return XMPP_EINVOP;
|
|
if (host == NULL || port == 0)
|
|
return XMPP_EINT;
|
|
|
|
_conn_reset(conn);
|
|
|
|
conn->type = type;
|
|
conn->domain = xmpp_strdup(conn->ctx, domain);
|
|
if (!conn->domain)
|
|
return XMPP_EMEM;
|
|
|
|
conn->sock = sock_connect(host, port);
|
|
xmpp_debug(conn->ctx, "xmpp", "sock_connect() to %s:%u returned %d", host,
|
|
port, conn->sock);
|
|
if (conn->sock == -1)
|
|
return XMPP_EINT;
|
|
if (conn->ka_timeout || conn->ka_interval)
|
|
sock_set_keepalive(conn->sock, conn->ka_timeout, conn->ka_interval);
|
|
|
|
/* setup handler */
|
|
conn->conn_handler = callback;
|
|
conn->userdata = userdata;
|
|
|
|
open_handler = conn->is_raw
|
|
? auth_handle_open_stub
|
|
: type == XMPP_CLIENT ? auth_handle_open
|
|
: auth_handle_component_open;
|
|
conn_prepare_reset(conn, open_handler);
|
|
|
|
/* FIXME: it could happen that the connect returns immediately as
|
|
* successful, though this is pretty unlikely. This would be a little
|
|
* hard to fix, since we'd have to detect and fire off the callback
|
|
* from within the event loop */
|
|
|
|
conn->state = XMPP_STATE_CONNECTING;
|
|
conn->timeout_stamp = time_stamp();
|
|
xmpp_debug(conn->ctx, "xmpp", "Attempting to connect to %s", host);
|
|
|
|
return 0;
|
|
}
|