Line data Source code
1 : /* conn.c
2 : ** strophe XMPP client library -- connection object functions
3 : **
4 : ** Copyright (C) 2005-2009 Collecta, Inc.
5 : **
6 : ** This software is provided AS-IS with no warranty, either express
7 : ** or implied.
8 : **
9 : ** This program is dual licensed under the MIT and GPLv3 licenses.
10 : */
11 :
12 : /** @file
13 : * Connection management.
14 : */
15 :
16 : /** @defgroup Connections Connection management
17 : * These functions manage a connection object.
18 : *
19 : * A part of those functions is listed under the \ref TLS section.
20 : */
21 :
22 : #include <errno.h>
23 : #include <stdarg.h>
24 : #include <string.h>
25 : #include <limits.h>
26 :
27 : #include "strophe.h"
28 :
29 : #include "common.h"
30 : #include "util.h"
31 : #include "parser.h"
32 :
33 : #ifndef DEFAULT_SEND_QUEUE_MAX
34 : /** @def DEFAULT_SEND_QUEUE_MAX
35 : * The default maximum send queue size. This is currently unused.
36 : */
37 : #define DEFAULT_SEND_QUEUE_MAX 64
38 : #endif
39 : #ifndef DISCONNECT_TIMEOUT
40 : /** @def DISCONNECT_TIMEOUT
41 : * The time to wait (in milliseconds) for graceful disconnection to
42 : * complete before the connection is reset. The default is 2 seconds.
43 : */
44 : #define DISCONNECT_TIMEOUT 2000 /* 2 seconds */
45 : #endif
46 : #ifndef CONNECT_TIMEOUT
47 : /** @def CONNECT_TIMEOUT
48 : * The time to wait (in milliseconds) for a connection attempt to succeed
49 : * or error. The default is 5 seconds.
50 : */
51 : #define CONNECT_TIMEOUT 5000 /* 5 seconds */
52 : #endif
53 :
54 : #ifndef KEEPALIVE_TIMEOUT
55 : /** @def KEEPALIVE_TIMEOUT
56 : * The time (in seconds) the connection needs to remain idle before TCP starts
57 : * sending keepalive probes, if the socket option SO_KEEPALIVE has been set on
58 : * this socket.
59 : * c.f. `TCP_KEEPIDLE` in `man 7 tcp` for linux, FreeBSD and some others or
60 : * `TCP_KEEPALIVE` on MacOS.
61 : */
62 : #define KEEPALIVE_TIMEOUT 60
63 : #endif
64 : #ifndef KEEPALIVE_INTERVAL
65 : /** @def KEEPALIVE_INTERVAL
66 : * The time (in seconds) between individual keepalive probes.
67 : * c.f. `TCP_KEEPINTVL` in `man 7 tcp`
68 : */
69 : #define KEEPALIVE_INTERVAL 30
70 : #endif
71 : #ifndef KEEPALIVE_COUNT
72 : /** @def KEEPALIVE_COUNT
73 : * The maximum number of keepalive probes TCP should send before dropping the
74 : * connection.
75 : * c.f. `TCP_KEEPCNT` in `man 7 tcp`
76 : */
77 : #define KEEPALIVE_COUNT 3
78 : #endif
79 :
80 : static int _disconnect_cleanup(xmpp_conn_t *conn, void *userdata);
81 : static void _reset_sm_state_for_reconnect(xmpp_conn_t *conn);
82 : static char *_conn_build_stream_tag(xmpp_conn_t *conn,
83 : char **attributes,
84 : size_t attributes_len);
85 : static int _conn_open_stream_with_attributes(xmpp_conn_t *conn,
86 : char **attributes,
87 : size_t attributes_len);
88 : static void _conn_attributes_new(xmpp_conn_t *conn,
89 : char **attrs,
90 : char ***attributes,
91 : size_t *attributes_len);
92 : static void _conn_attributes_destroy(xmpp_conn_t *conn,
93 : char **attributes,
94 : size_t attributes_len);
95 : static void _handle_stream_start(char *name, char **attrs, void *userdata);
96 : static void _handle_stream_end(char *name, void *userdata);
97 : static void _handle_stream_stanza(xmpp_stanza_t *stanza, void *userdata);
98 : static void _conn_sm_handle_stanza(xmpp_conn_t *const conn,
99 : xmpp_stanza_t *stanza);
100 : static unsigned short _conn_default_port(xmpp_conn_t *conn,
101 : xmpp_conn_type_t type);
102 : static void _conn_reset(xmpp_conn_t *conn);
103 : static int _conn_connect(xmpp_conn_t *conn,
104 : const char *domain,
105 : xmpp_conn_type_t type,
106 : xmpp_conn_handler callback,
107 : void *userdata);
108 : static void _send_valist(xmpp_conn_t *conn,
109 : const char *fmt,
110 : va_list ap,
111 : xmpp_send_queue_owner_t owner);
112 : static int _send_raw(xmpp_conn_t *conn,
113 : char *data,
114 : size_t len,
115 : xmpp_send_queue_owner_t owner,
116 : void *userdata);
117 :
118 0 : void xmpp_send_error(xmpp_conn_t *conn, xmpp_error_type_t type, char *text)
119 : {
120 0 : xmpp_stanza_t *error = xmpp_error_new(conn->ctx, type, text);
121 :
122 0 : send_stanza(conn, error, XMPP_QUEUE_STROPHE);
123 0 : }
124 :
125 : /** Create a new Strophe connection object.
126 : *
127 : * @param ctx a Strophe context object
128 : *
129 : * @return a Strophe connection object or NULL on an error
130 : *
131 : * @ingroup Connections
132 : */
133 8 : xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t *ctx)
134 : {
135 8 : xmpp_conn_t *conn = NULL;
136 8 : xmpp_connlist_t *tail, *item;
137 :
138 8 : if (ctx == NULL)
139 : return NULL;
140 :
141 8 : conn = strophe_alloc(ctx, sizeof(xmpp_conn_t));
142 8 : if (conn != NULL) {
143 8 : conn->ctx = ctx;
144 :
145 8 : conn->type = XMPP_UNKNOWN;
146 8 : conn->state = XMPP_STATE_DISCONNECTED;
147 :
148 8 : conn->xsock = NULL;
149 8 : conn->sock = INVALID_SOCKET;
150 8 : conn->ka_timeout = KEEPALIVE_TIMEOUT;
151 8 : conn->ka_interval = KEEPALIVE_INTERVAL;
152 8 : conn->ka_count = KEEPALIVE_COUNT;
153 8 : conn->tls = NULL;
154 8 : conn->timeout_stamp = 0;
155 8 : conn->error = 0;
156 8 : conn->stream_error = NULL;
157 :
158 : /* default send parameters */
159 8 : conn->blocking_send = 0;
160 8 : conn->send_queue_max = DEFAULT_SEND_QUEUE_MAX;
161 8 : conn->send_queue_len = 0;
162 8 : conn->send_queue_user_len = 0;
163 8 : conn->send_queue_head = NULL;
164 8 : conn->send_queue_tail = NULL;
165 :
166 : /* default timeouts */
167 8 : conn->connect_timeout = CONNECT_TIMEOUT;
168 :
169 8 : conn->lang = strophe_strdup(conn->ctx, "en");
170 8 : if (!conn->lang) {
171 0 : strophe_free(conn->ctx, conn);
172 0 : return NULL;
173 : }
174 8 : conn->domain = NULL;
175 8 : conn->jid = NULL;
176 8 : conn->pass = NULL;
177 8 : conn->stream_id = NULL;
178 8 : conn->bound_jid = NULL;
179 :
180 8 : conn->is_raw = 0;
181 8 : conn->tls_support = 0;
182 8 : conn->tls_disabled = 0;
183 8 : conn->tls_mandatory = 0;
184 8 : conn->tls_legacy_ssl = 0;
185 8 : conn->tls_trust = 0;
186 8 : conn->tls_failed = 0;
187 8 : conn->tls_cafile = NULL;
188 8 : conn->tls_capath = NULL;
189 8 : conn->tls_client_cert = NULL;
190 8 : conn->tls_client_key = NULL;
191 8 : conn->sasl_support = 0;
192 8 : conn->auth_legacy_enabled = 0;
193 8 : conn->secured = 0;
194 8 : conn->certfail_handler = NULL;
195 8 : conn->password_callback = NULL;
196 8 : conn->password_callback_userdata = NULL;
197 8 : tls_clear_password_cache(conn);
198 8 : conn->password_retries = 1;
199 :
200 8 : conn->bind_required = 0;
201 8 : conn->session_required = 0;
202 8 : conn->sm_state = NULL;
203 :
204 16 : conn->parser =
205 8 : parser_new(conn->ctx, _handle_stream_start, _handle_stream_end,
206 : _handle_stream_stanza, conn);
207 8 : conn->reset_parser = 0;
208 :
209 8 : conn->authenticated = 0;
210 8 : conn->conn_handler = NULL;
211 8 : conn->userdata = NULL;
212 8 : conn->timed_handlers = NULL;
213 : /* we own (and will free) the hash values */
214 8 : conn->id_handlers = hash_new(conn->ctx, 32, NULL);
215 8 : conn->handlers = NULL;
216 8 : conn->sockopt_cb = NULL;
217 :
218 : /* give the caller a reference to connection */
219 8 : conn->ref = 1;
220 :
221 : /* add connection to ctx->connlist */
222 8 : tail = conn->ctx->connlist;
223 8 : while (tail && tail->next)
224 : tail = tail->next;
225 :
226 8 : item = strophe_alloc(conn->ctx, sizeof(xmpp_connlist_t));
227 8 : if (!item) {
228 0 : strophe_error(conn->ctx, "xmpp", "failed to allocate memory");
229 0 : strophe_free(conn->ctx, conn->lang);
230 0 : parser_free(conn->parser);
231 0 : strophe_free(conn->ctx, conn);
232 0 : conn = NULL;
233 : } else {
234 8 : item->conn = conn;
235 8 : item->next = NULL;
236 :
237 8 : if (tail)
238 0 : tail->next = item;
239 : else
240 8 : conn->ctx->connlist = item;
241 : }
242 : }
243 :
244 : return conn;
245 : }
246 :
247 : /** Clone a Strophe connection object.
248 : *
249 : * @param conn a Strophe connection object
250 : *
251 : * @return the same conn object passed in with its reference count
252 : * incremented by 1
253 : *
254 : * @ingroup Connections
255 : */
256 0 : xmpp_conn_t *xmpp_conn_clone(xmpp_conn_t *conn)
257 : {
258 0 : conn->ref++;
259 0 : return conn;
260 : }
261 :
262 : /** Register sockopt callback
263 : * Set function to be called when a new socket is created to allow setting
264 : * socket options before connection is started.
265 : *
266 : * If the connection is already connected, this callback will be called
267 : * immediately.
268 : *
269 : * To set options that can only be applied to disconnected sockets, the
270 : * callback must be registered before connecting.
271 : *
272 : * @param conn The Strophe connection object this callback is being registered
273 : * for
274 : * @param callback a xmpp_sockopt_callback callback function that will receive
275 : * notifications of connection status
276 : *
277 : * @ingroup Connections
278 : */
279 :
280 0 : void xmpp_conn_set_sockopt_callback(xmpp_conn_t *conn,
281 : xmpp_sockopt_callback callback)
282 : {
283 0 : conn->sockopt_cb = callback;
284 0 : if (conn->state != XMPP_STATE_DISCONNECTED)
285 0 : callback(conn, &conn->sock);
286 0 : }
287 :
288 : /** Release a Strophe connection object.
289 : * Decrement the reference count by one for a connection, freeing the
290 : * connection object if the count reaches 0.
291 : *
292 : * @param conn a Strophe connection object
293 : *
294 : * @return TRUE if the connection object was freed and FALSE otherwise
295 : *
296 : * @ingroup Connections
297 : */
298 8 : int xmpp_conn_release(xmpp_conn_t *conn)
299 : {
300 8 : xmpp_ctx_t *ctx;
301 8 : xmpp_connlist_t *item, *prev;
302 8 : xmpp_handlist_t *hlitem, *thli;
303 8 : hash_iterator_t *iter;
304 8 : const char *key;
305 8 : int released = 0;
306 :
307 8 : if (conn->ref > 1)
308 0 : conn->ref--;
309 : else {
310 8 : ctx = conn->ctx;
311 :
312 8 : if (conn->state == XMPP_STATE_CONNECTING ||
313 : conn->state == XMPP_STATE_CONNECTED) {
314 0 : conn_disconnect(conn);
315 : }
316 :
317 : /* remove connection from context's connlist */
318 8 : if (ctx->connlist->conn == conn) {
319 8 : item = ctx->connlist;
320 8 : ctx->connlist = item->next;
321 8 : strophe_free(ctx, item);
322 : } else {
323 : prev = NULL;
324 : item = ctx->connlist;
325 0 : while (item && item->conn != conn) {
326 0 : prev = item;
327 0 : item = item->next;
328 : }
329 :
330 0 : if (!item) {
331 0 : strophe_error(ctx, "xmpp",
332 : "Connection not in context's list\n");
333 : } else {
334 0 : prev->next = item->next;
335 0 : strophe_free(ctx, item);
336 : }
337 : }
338 :
339 8 : _conn_reset(conn);
340 :
341 : /* free handler stuff
342 : * note that userdata is the responsibility of the client
343 : * and the handler pointers don't need to be freed since they
344 : * are pointers to functions */
345 :
346 8 : hlitem = conn->timed_handlers;
347 8 : while (hlitem) {
348 0 : thli = hlitem;
349 0 : hlitem = hlitem->next;
350 :
351 0 : strophe_free(ctx, thli);
352 : }
353 :
354 : /* id handlers
355 : * we have to traverse the hash table freeing list elements
356 : * then release the hash table */
357 8 : iter = hash_iter_new(conn->id_handlers);
358 8 : while ((key = hash_iter_next(iter))) {
359 0 : hlitem = (xmpp_handlist_t *)hash_get(conn->id_handlers, key);
360 0 : while (hlitem) {
361 0 : thli = hlitem;
362 0 : hlitem = hlitem->next;
363 0 : strophe_free(conn->ctx, thli->u.id);
364 0 : strophe_free(conn->ctx, thli);
365 : }
366 : }
367 8 : hash_iter_release(iter);
368 8 : hash_release(conn->id_handlers);
369 :
370 8 : hlitem = conn->handlers;
371 8 : while (hlitem) {
372 0 : thli = hlitem;
373 0 : hlitem = hlitem->next;
374 :
375 0 : if (thli->u.ns)
376 0 : strophe_free(ctx, thli->u.ns);
377 0 : if (thli->u.name)
378 0 : strophe_free(ctx, thli->u.name);
379 0 : if (thli->u.type)
380 0 : strophe_free(ctx, thli->u.type);
381 0 : strophe_free(ctx, thli);
382 : }
383 :
384 8 : parser_free(conn->parser);
385 :
386 8 : if (conn->jid)
387 0 : strophe_free(ctx, conn->jid);
388 8 : if (conn->pass)
389 0 : strophe_free(ctx, conn->pass);
390 8 : if (conn->lang)
391 8 : strophe_free(ctx, conn->lang);
392 8 : if (conn->tls_client_cert)
393 8 : strophe_free(ctx, conn->tls_client_cert);
394 8 : if (conn->tls_client_key)
395 2 : strophe_free(ctx, conn->tls_client_key);
396 8 : if (conn->tls_cafile)
397 0 : strophe_free(ctx, conn->tls_cafile);
398 8 : if (conn->tls_capath)
399 0 : strophe_free(ctx, conn->tls_capath);
400 8 : if (conn->sm_state)
401 0 : xmpp_free_sm_state(conn->sm_state);
402 8 : tls_clear_password_cache(conn);
403 8 : sock_free(conn->xsock);
404 8 : strophe_free(ctx, conn);
405 8 : released = 1;
406 : }
407 :
408 8 : return released;
409 : }
410 :
411 : /** Get the JID which is or will be bound to the connection.
412 : *
413 : * @param conn a Strophe connection object
414 : *
415 : * @return a string containing the full JID or NULL if it has not been set
416 : *
417 : * @ingroup Connections
418 : */
419 0 : const char *xmpp_conn_get_jid(const xmpp_conn_t *conn)
420 : {
421 0 : return conn->jid;
422 : }
423 :
424 : /**
425 : * Get the JID discovered during binding time.
426 : *
427 : * This JID will contain the resource used by the current connection.
428 : * This is useful in the case where a resource was not specified for
429 : * binding.
430 : *
431 : * @param conn a Strophe connection object.
432 : *
433 : * @return a string containing the full JID or NULL if it's not been discovered
434 : *
435 : * @ingroup Connections
436 : */
437 0 : const char *xmpp_conn_get_bound_jid(const xmpp_conn_t *conn)
438 : {
439 0 : return conn->bound_jid;
440 : }
441 :
442 : /** Set the JID of the user that will be bound to the connection.
443 : * If any JID was previously set, it will be discarded. This should not be
444 : * be used after a connection is created. The function will make a copy of
445 : * the JID string. If the supplied JID is missing the node, SASL
446 : * ANONYMOUS authentication will be used.
447 : *
448 : * @param conn a Strophe connection object
449 : * @param jid a full or bare JID
450 : *
451 : * @ingroup Connections
452 : */
453 0 : void xmpp_conn_set_jid(xmpp_conn_t *conn, const char *jid)
454 : {
455 0 : if (conn->jid)
456 0 : strophe_free(conn->ctx, conn->jid);
457 0 : conn->jid = strophe_strdup(conn->ctx, jid);
458 0 : }
459 :
460 : /** Set the Handler function which will be called when the TLS stack can't
461 : * verify the CA of the server we're trying to connect to.
462 : *
463 : * @param conn a Strophe connection object
464 : * @param hndl certfail Handler function
465 : *
466 : * @ingroup TLS
467 : */
468 0 : void xmpp_conn_set_certfail_handler(xmpp_conn_t *const conn,
469 : xmpp_certfail_handler hndl)
470 : {
471 0 : conn->certfail_handler = hndl;
472 0 : }
473 :
474 : /** Set the CAfile
475 : *
476 : * @param conn a Strophe connection object
477 : * @param path path to a certificate file
478 : *
479 : * @ingroup TLS
480 : */
481 0 : void xmpp_conn_set_cafile(xmpp_conn_t *const conn, const char *path)
482 : {
483 0 : if (conn->tls_cafile)
484 0 : strophe_free(conn->ctx, conn->tls_cafile);
485 0 : conn->tls_cafile = strophe_strdup(conn->ctx, path);
486 0 : }
487 :
488 : /** Set the CApath
489 : *
490 : * @param conn a Strophe connection object
491 : * @param path path to a folder containing certificates
492 : *
493 : * @ingroup TLS
494 : */
495 0 : void xmpp_conn_set_capath(xmpp_conn_t *const conn, const char *path)
496 : {
497 0 : if (conn->tls_capath)
498 0 : strophe_free(conn->ctx, conn->tls_capath);
499 0 : conn->tls_capath = strophe_strdup(conn->ctx, path);
500 0 : }
501 :
502 : /** Retrieve the peer certificate
503 : *
504 : * The returned Certificate object must be free'd by calling
505 : * \ref xmpp_tlscert_free
506 : *
507 : * @param conn a Strophe connection object
508 : *
509 : * @return a Strophe Certificate object
510 : *
511 : * @ingroup TLS
512 : */
513 0 : xmpp_tlscert_t *xmpp_conn_get_peer_cert(xmpp_conn_t *const conn)
514 : {
515 0 : return tls_peer_cert(conn);
516 : }
517 :
518 : /** Set the Callback function which will be called when the TLS stack can't
519 : * decrypt a password protected key file.
520 : *
521 : * @param conn a Strophe connection object
522 : * @param cb The callback function that shall be called
523 : * @param userdata An opaque data pointer that will be passed to the callback
524 : *
525 : * @ingroup TLS
526 : */
527 3 : void xmpp_conn_set_password_callback(xmpp_conn_t *conn,
528 : xmpp_password_callback cb,
529 : void *userdata)
530 : {
531 3 : conn->password_callback = cb;
532 3 : conn->password_callback_userdata = userdata;
533 3 : }
534 :
535 : /** Set the number of retry attempts to decrypt a private key file.
536 : *
537 : * In case the user enters the password manually it can be useful to
538 : * directly retry if the decryption of the key file failed.
539 : *
540 : * @param conn a Strophe connection object
541 : * @param retries The number of retries that should be tried
542 : *
543 : * @ingroup TLS
544 : */
545 0 : void xmpp_conn_set_password_retries(xmpp_conn_t *conn, unsigned int retries)
546 : {
547 0 : if (retries == 0)
548 0 : conn->password_retries = 1;
549 : else
550 0 : conn->password_retries = retries;
551 0 : }
552 :
553 : /** Retrieve the path of the key file that shall be unlocked.
554 : *
555 : * This makes usually sense to be called from the
556 : * \ref xmpp_password_callback .
557 : *
558 : * @param conn a Strophe connection object
559 : *
560 : * @return a String of the path to the key file
561 : *
562 : * @ingroup TLS
563 : */
564 0 : const char *xmpp_conn_get_keyfile(const xmpp_conn_t *conn)
565 : {
566 0 : return conn->tls_client_key;
567 : }
568 :
569 : /** Set the Client Certificate and Private Key or PKCS#12 encoded file that
570 : * will be bound to the connection. If any of them was previously set, it
571 : * will be discarded. This should not be used after a connection is created.
572 : * The function will make a copy of the strings passed in.
573 : *
574 : * In case the Private Key is encrypted, a callback must be set via
575 : * \ref xmpp_conn_set_password_callback so the TLS stack can retrieve the
576 : * password.
577 : *
578 : * In case one wants to use a PKCS#12 encoded file, it should be passed via
579 : * the `cert` parameter and `key` should be NULL. Passing a PKCS#12 file in
580 : * `key` is deprecated.
581 : *
582 : * @param conn a Strophe connection object
583 : * @param cert path to a certificate file or a P12 file
584 : * @param key path to a private key file or a P12 file
585 : *
586 : * @ingroup TLS
587 : */
588 8 : void xmpp_conn_set_client_cert(xmpp_conn_t *const conn,
589 : const char *const cert,
590 : const char *const key)
591 : {
592 8 : strophe_debug(conn->ctx, "conn", "set client cert %s %s", cert, key);
593 8 : if (conn->tls_client_cert)
594 0 : strophe_free(conn->ctx, conn->tls_client_cert);
595 8 : conn->tls_client_cert = NULL;
596 8 : if (conn->tls_client_key)
597 0 : strophe_free(conn->ctx, conn->tls_client_key);
598 8 : conn->tls_client_key = NULL;
599 8 : if (cert && key) {
600 2 : conn->tls_client_cert = strophe_strdup(conn->ctx, cert);
601 2 : conn->tls_client_key = strophe_strdup(conn->ctx, key);
602 6 : } else if (cert && !key) {
603 3 : conn->tls_client_cert = strophe_strdup(conn->ctx, cert);
604 3 : } else if (!cert && key) {
605 3 : strophe_warn(conn->ctx, "xmpp",
606 : "xmpp_conn_set_client_cert: Passing PKCS#12 in 'key' "
607 : "parameter is deprecated. Use 'cert' instead");
608 3 : conn->tls_client_cert = strophe_strdup(conn->ctx, key);
609 : }
610 8 : }
611 :
612 : /** Get the number of xmppAddr entries in the client certificate.
613 : *
614 : * @param conn a Strophe connection object
615 : *
616 : * @return the number of xmppAddr entries in the client certificate
617 : *
618 : * @ingroup TLS
619 : */
620 8 : unsigned int xmpp_conn_cert_xmppaddr_num(xmpp_conn_t *const conn)
621 : {
622 8 : return tls_id_on_xmppaddr_num(conn);
623 : }
624 :
625 : /** Get a specific xmppAddr entry.
626 : *
627 : * @param conn a Strophe connection object
628 : * @param n the index of the entry, starting at 0
629 : *
630 : * @return a string containing the xmppAddr or NULL if n is out of range
631 : *
632 : * @ingroup TLS
633 : */
634 24 : char *xmpp_conn_cert_xmppaddr(xmpp_conn_t *const conn, unsigned int n)
635 : {
636 24 : return tls_id_on_xmppaddr(conn, n);
637 : }
638 :
639 : /** Get the password used for authentication of a connection.
640 : *
641 : * @param conn a Strophe connection object
642 : *
643 : * @return a string containing the password or NULL if it has not been set
644 : *
645 : * @ingroup Connections
646 : */
647 0 : const char *xmpp_conn_get_pass(const xmpp_conn_t *conn)
648 : {
649 0 : return conn->pass;
650 : }
651 :
652 : /** Set the password used to authenticate the connection.
653 : * If any password was previously set, it will be discarded. The function
654 : * will make a copy of the password string.
655 : *
656 : * @param conn a Strophe connection object
657 : * @param pass the password
658 : *
659 : * @ingroup Connections
660 : */
661 0 : void xmpp_conn_set_pass(xmpp_conn_t *conn, const char *pass)
662 : {
663 0 : if (conn->pass)
664 0 : strophe_free(conn->ctx, conn->pass);
665 0 : conn->pass = pass ? strophe_strdup(conn->ctx, pass) : NULL;
666 0 : }
667 :
668 : /** Get the strophe context that the connection is associated with.
669 : * @param conn a Strophe connection object
670 : *
671 : * @return a Strophe context
672 : *
673 : * @ingroup Connections
674 : */
675 0 : xmpp_ctx_t *xmpp_conn_get_context(xmpp_conn_t *conn)
676 : {
677 0 : return conn->ctx;
678 : }
679 :
680 : /** Initiate a connection to the XMPP server.
681 : * This function returns immediately after starting the connection
682 : * process to the XMPP server, and notifications of connection state changes
683 : * will be sent to the callback function. The domain and port to connect to
684 : * are usually determined by an SRV lookup for the xmpp-client service at
685 : * the domain specified in the JID. If SRV lookup fails, altdomain and
686 : * altport will be used instead if specified.
687 : *
688 : * @param conn a Strophe connection object
689 : * @param altdomain a string with domain to use if SRV lookup fails. If this
690 : * is NULL, the domain from the JID will be used.
691 : * @param altport an integer port number to use if SRV lookup fails. If this
692 : * is 0, the default port will be assumed.
693 : * @param callback a xmpp_conn_handler callback function that will receive
694 : * notifications of connection status
695 : * @param userdata an opaque data pointer that will be passed to the callback
696 : *
697 : * @return XMPP_EOK (0) on success or a number less than 0 on failure
698 : *
699 : * @ingroup Connections
700 : */
701 0 : int xmpp_connect_client(xmpp_conn_t *conn,
702 : const char *altdomain,
703 : unsigned short altport,
704 : xmpp_conn_handler callback,
705 : void *userdata)
706 : {
707 0 : char *domain;
708 0 : int rc;
709 :
710 0 : if (!conn->jid && (conn->tls_client_cert || conn->tls_client_key)) {
711 0 : if (tls_id_on_xmppaddr_num(conn) != 1) {
712 0 : strophe_debug(conn->ctx, "xmpp",
713 : "Client certificate contains multiple or no xmppAddr "
714 : "and no JID was given to be used.");
715 0 : return XMPP_EINVOP;
716 : }
717 0 : conn->jid = tls_id_on_xmppaddr(conn, 0);
718 0 : if (!conn->jid)
719 : return XMPP_EMEM;
720 0 : strophe_debug(conn->ctx, "xmpp", "Use jid %s from id-on-xmppAddr.",
721 : conn->jid);
722 : }
723 :
724 0 : if (!conn->jid) {
725 0 : strophe_error(conn->ctx, "xmpp", "JID is not set.");
726 0 : return XMPP_EINVOP;
727 : }
728 :
729 0 : domain = xmpp_jid_domain(conn->ctx, conn->jid);
730 0 : if (!domain)
731 : return XMPP_EMEM;
732 :
733 0 : if (!conn->sm_state) {
734 0 : conn->sm_state = strophe_alloc(conn->ctx, sizeof(*conn->sm_state));
735 0 : if (!conn->sm_state)
736 0 : goto err_mem;
737 0 : memset(conn->sm_state, 0, sizeof(*conn->sm_state));
738 0 : conn->sm_state->ctx = conn->ctx;
739 : }
740 :
741 0 : if (altdomain != NULL)
742 0 : strophe_debug(conn->ctx, "conn", "Connecting via altdomain.");
743 :
744 0 : if (conn->tls_legacy_ssl && !altdomain) {
745 : /* SSL tunneled connection on 5223 port is legacy and doesn't
746 : * have an SRV record. */
747 0 : altdomain = domain;
748 : }
749 0 : altport = altport ? altport : _conn_default_port(conn, XMPP_CLIENT);
750 :
751 0 : if (conn->xsock)
752 0 : sock_free(conn->xsock);
753 0 : conn->xsock = sock_new(conn, domain, altdomain, altport);
754 0 : if (!conn->xsock)
755 0 : goto err_mem;
756 :
757 0 : rc = _conn_connect(conn, domain, XMPP_CLIENT, callback, userdata);
758 0 : strophe_free(conn->ctx, domain);
759 :
760 0 : return rc;
761 :
762 0 : err_mem:
763 0 : strophe_free(conn->ctx, domain);
764 0 : return XMPP_EMEM;
765 : }
766 :
767 : /** Initiate a component connection to server.
768 : * This function returns immediately after starting the connection
769 : * process to the XMPP server, and notifications of connection state changes
770 : * will be sent to the internal callback function that will set up handler
771 : * for the component handshake as defined in XEP-0114.
772 : * The domain and port to connect to must be provided in this case as the JID
773 : * provided to the call serves as component identifier to the server and is
774 : * not subject to DNS resolution.
775 : *
776 : * @param conn a Strophe connection object
777 : * @param server a string with domain to use directly as the domain can't be
778 : * extracted from the component name/JID. If this is not set, the call
779 : * will fail.
780 : * @param port an integer port number to use to connect to server expecting
781 : * an external component. If this is 0, the port 5347 will be assumed.
782 : * @param callback a xmpp_conn_handler callback function that will receive
783 : * notifications of connection status
784 : * @param userdata an opaque data pointer that will be passed to the callback
785 : *
786 : * @return XMPP_EOK (0) on success or a number less than 0 on failure
787 : *
788 : * @ingroup Connections
789 : */
790 0 : int xmpp_connect_component(xmpp_conn_t *conn,
791 : const char *server,
792 : unsigned short port,
793 : xmpp_conn_handler callback,
794 : void *userdata)
795 : {
796 : /* The server domain, jid and password MUST be specified. */
797 0 : if (!(server && conn->jid && conn->pass))
798 : return XMPP_EINVOP;
799 :
800 : /* XEP-0114 does not support TLS */
801 0 : xmpp_conn_disable_tls(conn);
802 0 : if (!conn->tls_disabled) {
803 0 : strophe_error(conn->ctx, "conn",
804 : "Failed to disable TLS. "
805 : "XEP-0114 does not support TLS");
806 0 : return XMPP_EINT;
807 : }
808 :
809 0 : port = port ? port : _conn_default_port(conn, XMPP_COMPONENT);
810 0 : if (conn->xsock)
811 0 : sock_free(conn->xsock);
812 0 : conn->xsock = sock_new(conn, NULL, server, port);
813 0 : if (!conn->xsock)
814 : return XMPP_EMEM;
815 :
816 : /* JID serves as an identifier here and will be used as "to" attribute
817 : of the stream */
818 0 : return _conn_connect(conn, conn->jid, XMPP_COMPONENT, callback, userdata);
819 : }
820 :
821 : /** Initiate a raw connection to the XMPP server.
822 : * Arguments and behaviour of the function are similar to
823 : * xmpp_connect_client(), but it skips authentication process. In opposite to
824 : * xmpp_connect_client() during connection process two events are generated
825 : * instead of one. User's callback is called with event XMPP_CONN_RAW_CONNECT
826 : * when the TCP connection with the server is established. At this point user
827 : * might want to open an XMPP stream with xmpp_conn_open_stream() or establish
828 : * TLS session with xmpp_conn_tls_start(). Event XMPP_CONN_CONNECT is generated
829 : * when the XMPP stream is opened successfully and user may send stanzas over
830 : * the connection.
831 : *
832 : * This function doesn't use password nor node part of a jid. Therefore,
833 : * the only required configuration is a domain (or full jid) passed via
834 : * xmpp_conn_set_jid().
835 : *
836 : * @see xmpp_connect_client()
837 : *
838 : * @return XMPP_EOK (0) on success a number less than 0 on failure
839 : *
840 : * @ingroup Connections
841 : */
842 0 : int xmpp_connect_raw(xmpp_conn_t *conn,
843 : const char *altdomain,
844 : unsigned short altport,
845 : xmpp_conn_handler callback,
846 : void *userdata)
847 : {
848 0 : conn->is_raw = 1;
849 0 : return xmpp_connect_client(conn, altdomain, altport, callback, userdata);
850 : }
851 :
852 : /* Called when tcp connection is established. */
853 0 : void conn_established(xmpp_conn_t *conn)
854 : {
855 0 : if (conn->tls_legacy_ssl && !conn->is_raw) {
856 0 : strophe_debug(conn->ctx, "xmpp", "using legacy SSL connection");
857 0 : if (conn_tls_start(conn) != 0) {
858 0 : conn_disconnect(conn);
859 0 : return;
860 : }
861 : }
862 :
863 0 : if (conn->is_raw) {
864 0 : handler_reset_timed(conn, 0);
865 : /* we skip authentication for a "raw" connection, but the event loop
866 : ignores user's handlers when conn->authenticated is not set. */
867 0 : conn->authenticated = 1;
868 0 : conn->conn_handler(conn, XMPP_CONN_RAW_CONNECT, 0, NULL,
869 : conn->userdata);
870 : } else {
871 : /* send stream init */
872 0 : conn_open_stream(conn);
873 : }
874 : }
875 :
876 : /** Send the default opening stream tag.
877 : * The default tag is the one sent by xmpp_connect_client().
878 : * User's connection handler is called with event XMPP_CONN_CONNECT when
879 : * server replies with its opening tag.
880 : *
881 : * @return XMPP_EOK (0) on success a number less than 0 on failure
882 : *
883 : * @note The connection must be connected with xmpp_connect_raw().
884 : *
885 : * @ingroup Connections
886 : */
887 0 : int xmpp_conn_open_stream_default(xmpp_conn_t *conn)
888 : {
889 0 : if (!conn->is_raw)
890 : return XMPP_EINVOP;
891 :
892 0 : conn_prepare_reset(conn, auth_handle_open_raw);
893 0 : conn_open_stream(conn);
894 :
895 0 : return XMPP_EOK;
896 : }
897 :
898 : /** Send an opening stream tag.
899 : * User's connection handler is called with event XMPP_CONN_CONNECT when
900 : * server replies with its opening tag.
901 : *
902 : * @param conn a Strophe connection object
903 : * @param attributes Array of strings in format: even index points to
904 : * an attribute name and odd index points to its value
905 : * @param attributes_len Number of elements in the attributes array, it
906 : * should be number of attributes multiplied by 2
907 : *
908 : * @return XMPP_EOK (0) on success a number less than 0 on failure
909 : *
910 : * @note The connection must be connected with xmpp_connect_raw().
911 : *
912 : * @ingroup Connections
913 : */
914 0 : int xmpp_conn_open_stream(xmpp_conn_t *conn,
915 : char **attributes,
916 : size_t attributes_len)
917 : {
918 0 : if (!conn->is_raw)
919 : return XMPP_EINVOP;
920 :
921 0 : conn_prepare_reset(conn, auth_handle_open_raw);
922 :
923 0 : return _conn_open_stream_with_attributes(conn, attributes, attributes_len);
924 : }
925 :
926 : /** Start synchronous TLS handshake with the server.
927 : *
928 : * @return XMPP_EOK (0) on success a number less than 0 on failure
929 : *
930 : * @ingroup Connections
931 : */
932 0 : int xmpp_conn_tls_start(xmpp_conn_t *conn)
933 : {
934 0 : return conn_tls_start(conn);
935 : }
936 :
937 : /** Cleanly disconnect the connection.
938 : * This function is only called by the stream parser when </stream:stream>
939 : * is received, and it not intended to be called by code outside of Strophe.
940 : *
941 : * @param conn a Strophe connection object
942 : */
943 0 : void conn_disconnect_clean(xmpp_conn_t *conn)
944 : {
945 : /* remove the timed handler */
946 0 : xmpp_timed_handler_delete(conn, _disconnect_cleanup);
947 :
948 0 : conn_disconnect(conn);
949 0 : }
950 :
951 : /** Disconnect from the XMPP server.
952 : * This function immediately disconnects from the XMPP server, and should
953 : * not be used outside of the Strophe library.
954 : *
955 : * @param conn a Strophe connection object
956 : */
957 0 : void conn_disconnect(xmpp_conn_t *conn)
958 : {
959 0 : strophe_debug(conn->ctx, "xmpp", "Closing socket.");
960 0 : conn->state = XMPP_STATE_DISCONNECTED;
961 0 : if (conn->tls) {
962 0 : tls_stop(conn->tls);
963 0 : tls_free(conn->tls);
964 0 : conn->tls = NULL;
965 : }
966 0 : if (conn->sock != INVALID_SOCKET)
967 0 : sock_close(conn->sock);
968 0 : _reset_sm_state_for_reconnect(conn);
969 :
970 : /* fire off connection handler */
971 0 : conn->conn_handler(conn, XMPP_CONN_DISCONNECT, conn->error,
972 : conn->stream_error, conn->userdata);
973 0 : }
974 :
975 : /* prepares a parser reset. this is called from handlers. we can't
976 : * reset the parser immediately as it is not re-entrant. */
977 0 : void conn_prepare_reset(xmpp_conn_t *conn, xmpp_open_handler handler)
978 : {
979 0 : conn->reset_parser = 1;
980 0 : conn->open_handler = handler;
981 0 : }
982 :
983 : /* reset the parser */
984 0 : void conn_parser_reset(xmpp_conn_t *conn)
985 : {
986 0 : conn->reset_parser = 0;
987 0 : parser_reset(conn->parser);
988 0 : }
989 :
990 : /** Initiate termination of the connection to the XMPP server.
991 : * This function starts the disconnection sequence by sending
992 : * </stream:stream> to the XMPP server. This function will do nothing
993 : * if the connection state is different from CONNECTING or CONNECTED.
994 : *
995 : * @param conn a Strophe connection object
996 : *
997 : * @ingroup Connections
998 : */
999 0 : void xmpp_disconnect(xmpp_conn_t *conn)
1000 : {
1001 0 : if (conn->state != XMPP_STATE_CONNECTING &&
1002 : conn->state != XMPP_STATE_CONNECTED)
1003 : return;
1004 :
1005 : /* close the stream */
1006 0 : send_raw_string(conn, "</stream:stream>");
1007 :
1008 : /* setup timed handler in case disconnect takes too long */
1009 0 : handler_add_timed(conn, _disconnect_cleanup, DISCONNECT_TIMEOUT, NULL);
1010 : }
1011 :
1012 : /** Send a raw string to the XMPP server.
1013 : * This function is a convenience function to send raw string data to the
1014 : * XMPP server. It is used by Strophe to send short messages instead of
1015 : * building up an XML stanza with DOM methods. This should be used with care
1016 : * as it does not validate the data; invalid data may result in immediate
1017 : * stream termination by the XMPP server.
1018 : *
1019 : * @param conn a Strophe connection object
1020 : * @param fmt a printf-style format string followed by a variable list of
1021 : * arguments to format
1022 : *
1023 : * @ingroup Connections
1024 : */
1025 0 : void xmpp_send_raw_string(xmpp_conn_t *conn, const char *fmt, ...)
1026 : {
1027 0 : va_list ap;
1028 :
1029 0 : if (conn->state != XMPP_STATE_CONNECTED)
1030 0 : return;
1031 :
1032 0 : va_start(ap, fmt);
1033 0 : _send_valist(conn, fmt, ap, XMPP_QUEUE_USER);
1034 0 : va_end(ap);
1035 : }
1036 :
1037 : /** Send raw bytes to the XMPP server.
1038 : * This function is a convenience function to send raw bytes to the
1039 : * XMPP server. It is used primarily by xmpp_send_raw_string(). This
1040 : * function should be used with care as it does not validate the bytes and
1041 : * invalid data may result in stream termination by the XMPP server.
1042 : *
1043 : * @param conn a Strophe connection object
1044 : * @param data a buffer of raw bytes
1045 : * @param len the length of the data in the buffer
1046 : *
1047 : * @ingroup Connections
1048 : */
1049 0 : void xmpp_send_raw(xmpp_conn_t *conn, const char *data, size_t len)
1050 : {
1051 0 : send_raw(conn, data, len, XMPP_QUEUE_USER, NULL);
1052 0 : }
1053 :
1054 : /** Send an XML stanza to the XMPP server.
1055 : * This is the main way to send data to the XMPP server. The function will
1056 : * terminate without action if the connection state is not CONNECTED.
1057 : *
1058 : * @param conn a Strophe connection object
1059 : * @param stanza a Strophe stanza object
1060 : *
1061 : * @ingroup Connections
1062 : */
1063 0 : void xmpp_send(xmpp_conn_t *conn, xmpp_stanza_t *stanza)
1064 : {
1065 0 : send_stanza(conn, xmpp_stanza_clone(stanza), XMPP_QUEUE_USER);
1066 0 : }
1067 :
1068 : /** Send the opening <stream:stream> tag to the server.
1069 : * This function is used by Strophe to begin an XMPP stream. It should
1070 : * not be used outside of the library.
1071 : *
1072 : * @param conn a Strophe connection object
1073 : */
1074 0 : void conn_open_stream(xmpp_conn_t *conn)
1075 : {
1076 0 : size_t attributes_len;
1077 0 : int rc;
1078 0 : char *from = NULL;
1079 0 : char *ns = conn->type == XMPP_CLIENT ? XMPP_NS_CLIENT : XMPP_NS_COMPONENT;
1080 0 : char *attributes[12] = {
1081 0 : "to", conn->domain, "xml:lang", conn->lang,
1082 : "version", "1.0", "xmlns", ns,
1083 : "xmlns:stream", XMPP_NS_STREAMS, "from", NULL};
1084 :
1085 0 : attributes_len = ARRAY_SIZE(attributes);
1086 0 : if (conn->tls && conn->jid && strchr(conn->jid, '@') != NULL)
1087 0 : from = xmpp_jid_bare(conn->ctx, conn->jid);
1088 :
1089 0 : if (from)
1090 0 : attributes[attributes_len - 1] = from;
1091 : else
1092 : attributes_len -= 2;
1093 :
1094 0 : rc = _conn_open_stream_with_attributes(conn, attributes, attributes_len);
1095 0 : if (rc != XMPP_EOK) {
1096 0 : strophe_error(conn->ctx, "conn",
1097 : "Cannot build stream tag: memory error");
1098 0 : conn_disconnect(conn);
1099 : }
1100 0 : if (from)
1101 0 : strophe_free(conn->ctx, from);
1102 0 : }
1103 :
1104 0 : int conn_tls_start(xmpp_conn_t *conn)
1105 : {
1106 0 : int rc;
1107 :
1108 0 : if (conn->tls_disabled) {
1109 0 : conn->tls = NULL;
1110 0 : rc = XMPP_EINVOP;
1111 : } else {
1112 0 : conn->tls = tls_new(conn);
1113 0 : rc = conn->tls == NULL ? XMPP_EMEM : 0;
1114 : }
1115 :
1116 0 : if (conn->tls != NULL) {
1117 0 : if (tls_start(conn->tls)) {
1118 0 : conn->secured = 1;
1119 : } else {
1120 0 : rc = XMPP_EINT;
1121 0 : conn->error = tls_error(conn->tls);
1122 0 : tls_free(conn->tls);
1123 0 : conn->tls = NULL;
1124 0 : conn->tls_failed = 1;
1125 : }
1126 : }
1127 0 : if (rc != 0) {
1128 0 : strophe_debug(conn->ctx, "conn",
1129 : "Couldn't start TLS! "
1130 : "error %d tls_error %d",
1131 : rc, conn->error);
1132 : }
1133 0 : return rc;
1134 : }
1135 :
1136 : /** Return applied flags for the connection.
1137 : *
1138 : * @param conn a Strophe connection object
1139 : *
1140 : * @return ORed connection flags that are applied for the connection.
1141 : *
1142 : * @ingroup Connections
1143 : */
1144 0 : long xmpp_conn_get_flags(const xmpp_conn_t *conn)
1145 : {
1146 0 : long flags;
1147 :
1148 0 : flags = XMPP_CONN_FLAG_DISABLE_TLS * conn->tls_disabled |
1149 0 : XMPP_CONN_FLAG_MANDATORY_TLS * conn->tls_mandatory |
1150 0 : XMPP_CONN_FLAG_LEGACY_SSL * conn->tls_legacy_ssl |
1151 0 : XMPP_CONN_FLAG_TRUST_TLS * conn->tls_trust |
1152 0 : XMPP_CONN_FLAG_LEGACY_AUTH * conn->auth_legacy_enabled;
1153 :
1154 0 : return flags;
1155 : }
1156 :
1157 : /** Set flags for the connection.
1158 : * This function applies set flags and resets unset ones. Default connection
1159 : * configuration is all flags unset. Flags can be applied only for a connection
1160 : * in disconnected state.
1161 : * All unsupported flags are ignored. If a flag is unset after successful set
1162 : * operation then the flag is not supported by current version.
1163 : *
1164 : * Supported flags are:
1165 : *
1166 : * - XMPP_CONN_FLAG_DISABLE_TLS
1167 : * - XMPP_CONN_FLAG_MANDATORY_TLS
1168 : * - XMPP_CONN_FLAG_LEGACY_SSL
1169 : * - XMPP_CONN_FLAG_TRUST_TLS
1170 : * - XMPP_CONN_FLAG_LEGACY_AUTH
1171 : * - XMPP_CONN_FLAG_DISABLE_SM
1172 : *
1173 : * @param conn a Strophe connection object
1174 : * @param flags ORed connection flags
1175 : *
1176 : * @return XMPP_EOK (0) on success or a number less than 0 on failure
1177 : *
1178 : * @ingroup Connections
1179 : */
1180 0 : int xmpp_conn_set_flags(xmpp_conn_t *conn, long flags)
1181 : {
1182 0 : if (conn->state != XMPP_STATE_DISCONNECTED) {
1183 0 : strophe_error(conn->ctx, "conn",
1184 : "Flags can be set only "
1185 : "for disconnected connection");
1186 0 : return XMPP_EINVOP;
1187 : }
1188 0 : if ((flags & XMPP_CONN_FLAG_DISABLE_TLS) &&
1189 : (flags & (XMPP_CONN_FLAG_MANDATORY_TLS | XMPP_CONN_FLAG_LEGACY_SSL |
1190 : XMPP_CONN_FLAG_TRUST_TLS))) {
1191 0 : strophe_error(conn->ctx, "conn", "Flags 0x%04lx conflict", flags);
1192 0 : return XMPP_EINVOP;
1193 : }
1194 :
1195 0 : conn->tls_disabled = (flags & XMPP_CONN_FLAG_DISABLE_TLS) ? 1 : 0;
1196 0 : conn->tls_mandatory = (flags & XMPP_CONN_FLAG_MANDATORY_TLS) ? 1 : 0;
1197 0 : conn->tls_legacy_ssl = (flags & XMPP_CONN_FLAG_LEGACY_SSL) ? 1 : 0;
1198 0 : conn->tls_trust = (flags & XMPP_CONN_FLAG_TRUST_TLS) ? 1 : 0;
1199 0 : conn->auth_legacy_enabled = (flags & XMPP_CONN_FLAG_LEGACY_AUTH) ? 1 : 0;
1200 0 : conn->sm_disable = (flags & XMPP_CONN_FLAG_DISABLE_SM) ? 1 : 0;
1201 :
1202 0 : return 0;
1203 : }
1204 :
1205 : /** Disable TLS for this connection, called by users of the library.
1206 : * Occasionally a server will be misconfigured to send the starttls
1207 : * feature, but will not support the handshake.
1208 : *
1209 : * @param conn a Strophe connection object
1210 : *
1211 : * @note this function is deprecated
1212 : * @see xmpp_conn_set_flags()
1213 : *
1214 : * @ingroup Connections
1215 : */
1216 0 : void xmpp_conn_disable_tls(xmpp_conn_t *conn)
1217 : {
1218 0 : long flags = xmpp_conn_get_flags(conn);
1219 :
1220 0 : flags |= XMPP_CONN_FLAG_DISABLE_TLS;
1221 0 : (void)xmpp_conn_set_flags(conn, flags);
1222 0 : }
1223 :
1224 : /** Return whether TLS session is established or not.
1225 : *
1226 : * @return TRUE if TLS session is established and FALSE otherwise
1227 : *
1228 : * @ingroup Connections
1229 : */
1230 0 : int xmpp_conn_is_secured(xmpp_conn_t *conn)
1231 : {
1232 0 : return conn->secured && !conn->tls_failed && conn->tls != NULL;
1233 : }
1234 :
1235 : /**
1236 : * @return TRUE if connection is in connecting state and FALSE otherwise
1237 : *
1238 : * @ingroup Connections
1239 : */
1240 0 : int xmpp_conn_is_connecting(xmpp_conn_t *conn)
1241 : {
1242 0 : return conn->state == XMPP_STATE_CONNECTING;
1243 : }
1244 :
1245 : /**
1246 : * @return TRUE if connection is in connected state and FALSE otherwise
1247 : *
1248 : * @ingroup Connections
1249 : */
1250 0 : int xmpp_conn_is_connected(xmpp_conn_t *conn)
1251 : {
1252 0 : return conn->state == XMPP_STATE_CONNECTED;
1253 : }
1254 :
1255 : /**
1256 : * @return TRUE if connection is in disconnected state and FALSE otherwise
1257 : *
1258 : * @ingroup Connections
1259 : */
1260 0 : int xmpp_conn_is_disconnected(xmpp_conn_t *conn)
1261 : {
1262 0 : return conn->state == XMPP_STATE_DISCONNECTED;
1263 : }
1264 :
1265 : /**
1266 : * This returns the Stream Management state of a connection object after
1267 : * it has been disconnected.
1268 : * One can then initialise a fresh connection object and set this Stream
1269 : * Management state by calling \ref xmpp_conn_set_sm_state
1270 : *
1271 : * In case one wants to dispose of the state w/o setting it into a fresh
1272 : * connection object, one can call \ref xmpp_free_sm_state
1273 : *
1274 : * After calling this function to retrieve the state, only call one of the
1275 : * other two.
1276 : *
1277 : * @param conn a Strophe connection object
1278 : * @return The Stream Management state of the connection or NULL on error
1279 : *
1280 : * @ingroup Connections
1281 : */
1282 0 : xmpp_sm_state_t *xmpp_conn_get_sm_state(xmpp_conn_t *conn)
1283 : {
1284 0 : xmpp_sm_state_t *ret;
1285 :
1286 : /* We can only return the SM state when we're disconnected */
1287 0 : if (conn->state != XMPP_STATE_DISCONNECTED)
1288 : return NULL;
1289 :
1290 0 : ret = conn->sm_state;
1291 0 : conn->sm_state = NULL;
1292 :
1293 0 : return ret;
1294 : }
1295 :
1296 0 : static void _reset_sm_state_for_reconnect(xmpp_conn_t *conn)
1297 : {
1298 0 : xmpp_sm_state_t *s = conn->sm_state;
1299 :
1300 0 : if (s->previd) {
1301 0 : strophe_free(conn->ctx, s->previd);
1302 0 : s->previd = NULL;
1303 : }
1304 :
1305 0 : if (s->can_resume) {
1306 0 : s->previd = s->id;
1307 0 : s->id = NULL;
1308 :
1309 0 : s->bound_jid = conn->bound_jid;
1310 0 : conn->bound_jid = NULL;
1311 0 : } else if (s->id) {
1312 0 : strophe_free(conn->ctx, s->id);
1313 0 : s->id = NULL;
1314 : }
1315 :
1316 0 : s->sm_enabled = s->sm_support = s->resume = 0;
1317 :
1318 0 : if (s->bind) {
1319 0 : xmpp_stanza_release(s->bind);
1320 0 : s->bind = NULL;
1321 : }
1322 0 : }
1323 :
1324 : /**
1325 : * @param conn a Strophe connection object
1326 : * @param sm_state A Stream Management state returned from a call to
1327 : * `xmpp_conn_get_sm_state()`
1328 : *
1329 : * @return XMPP_EOK (0) on success or a number less than 0 on failure
1330 : *
1331 : * @ingroup Connections
1332 : */
1333 0 : int xmpp_conn_set_sm_state(xmpp_conn_t *conn, xmpp_sm_state_t *sm_state)
1334 : {
1335 : /* We can only set the SM state when we're disconnected */
1336 0 : if (conn->state != XMPP_STATE_DISCONNECTED)
1337 : return XMPP_EINVOP;
1338 :
1339 0 : if (conn->sm_state) {
1340 0 : strophe_error(conn->ctx, "conn", "SM state is already set!");
1341 0 : return XMPP_EINVOP;
1342 : }
1343 :
1344 0 : if (conn->ctx != sm_state->ctx) {
1345 0 : strophe_error(
1346 : conn->ctx, "conn",
1347 : "SM state has to be assigned to connection that stems from "
1348 : "the same context!");
1349 0 : return XMPP_EINVOP;
1350 : }
1351 :
1352 0 : conn->sm_state = sm_state;
1353 0 : return XMPP_EOK;
1354 : }
1355 :
1356 : /** c.f. \ref xmpp_conn_get_sm_state for usage documentation
1357 : *
1358 : * @param sm_state A Stream Management state returned from a call to
1359 : * `xmpp_conn_get_sm_state()`
1360 : *
1361 : * @ingroup Connections
1362 : */
1363 0 : void xmpp_free_sm_state(xmpp_sm_state_t *sm_state)
1364 : {
1365 0 : xmpp_ctx_t *ctx;
1366 0 : xmpp_send_queue_t *smq;
1367 :
1368 0 : if (!sm_state || !sm_state->ctx)
1369 : return;
1370 :
1371 : ctx = sm_state->ctx;
1372 :
1373 0 : while ((smq = pop_queue_front(&sm_state->sm_queue))) {
1374 0 : strophe_free(ctx, queue_element_free(ctx, smq));
1375 : }
1376 :
1377 0 : if (sm_state->bind)
1378 0 : xmpp_stanza_release(sm_state->bind);
1379 0 : if (sm_state->id)
1380 0 : strophe_free(ctx, sm_state->id);
1381 0 : if (sm_state->previd)
1382 0 : strophe_free(ctx, sm_state->previd);
1383 0 : if (sm_state->bound_jid)
1384 0 : strophe_free(ctx, sm_state->bound_jid);
1385 0 : memset(sm_state, 0, sizeof(*sm_state));
1386 0 : strophe_free(ctx, sm_state);
1387 : }
1388 :
1389 : /**
1390 : * @return The number of entries in the send queue
1391 : *
1392 : * @ingroup Connections
1393 : */
1394 0 : int xmpp_conn_send_queue_len(const xmpp_conn_t *conn)
1395 : {
1396 0 : if (conn->send_queue_head && conn->send_queue_head->wip &&
1397 0 : conn->send_queue_head->owner == XMPP_QUEUE_USER)
1398 0 : return conn->send_queue_user_len - 1;
1399 : else
1400 0 : return conn->send_queue_user_len;
1401 : }
1402 :
1403 0 : static char *_drop_send_queue_element(xmpp_conn_t *conn, xmpp_send_queue_t *e)
1404 : {
1405 0 : if (e == conn->send_queue_head)
1406 0 : conn->send_queue_head = e->next;
1407 0 : if (e == conn->send_queue_tail)
1408 0 : conn->send_queue_tail = e->prev;
1409 0 : if (!conn->send_queue_head)
1410 0 : conn->send_queue_tail = NULL;
1411 0 : if (e->prev)
1412 0 : e->prev->next = e->next;
1413 0 : if (e->next)
1414 0 : e->next->prev = e->prev;
1415 0 : conn->send_queue_len--;
1416 0 : if (e->owner == XMPP_QUEUE_USER)
1417 0 : conn->send_queue_user_len--;
1418 0 : return queue_element_free(conn->ctx, e);
1419 : }
1420 :
1421 : /** Drop an element of the send queue.
1422 : * This can be used to manage the send queue in case a server
1423 : * isn't fast enough in processing the elements you're trying
1424 : * to send or your outgoing bandwidth isn't fast enough to transfer
1425 : * everything you want to send out.
1426 : *
1427 : * @param conn a Strophe connection object
1428 : * @param which the element that shall be removed
1429 : *
1430 : * @return The rendered stanza. The pointer returned has to be free'd by the
1431 : * caller of this function.
1432 : *
1433 : * @ingroup Connections
1434 : */
1435 0 : char *xmpp_conn_send_queue_drop_element(xmpp_conn_t *conn,
1436 : xmpp_queue_element_t which)
1437 : {
1438 0 : xmpp_send_queue_t *t;
1439 :
1440 : /* Fast return paths */
1441 : /* empty queue */
1442 0 : if (!conn->send_queue_head)
1443 : return NULL;
1444 : /* one element in queue */
1445 0 : if (conn->send_queue_head == conn->send_queue_tail) {
1446 : /* head is already sent out partially */
1447 0 : if (conn->send_queue_head->wip)
1448 : return NULL;
1449 : /* the element is no USER element */
1450 0 : if (conn->send_queue_head->owner != XMPP_QUEUE_USER)
1451 : return NULL;
1452 : }
1453 :
1454 : /* Regular flow */
1455 0 : if (which == XMPP_QUEUE_OLDEST) {
1456 : t = conn->send_queue_head;
1457 0 : } else if (which == XMPP_QUEUE_YOUNGEST) {
1458 : t = conn->send_queue_tail;
1459 : /* search backwards to find last USER element */
1460 0 : while (t && t->owner != XMPP_QUEUE_USER)
1461 0 : t = t->prev;
1462 : } else {
1463 0 : strophe_error(conn->ctx, "conn", "Unknown queue element %d", which);
1464 0 : return NULL;
1465 : }
1466 : /* there was no USER element in the queue */
1467 0 : if (!t)
1468 : return NULL;
1469 :
1470 : /* head is already sent out partially */
1471 0 : if (t == conn->send_queue_head && t->wip)
1472 0 : t = t->next;
1473 :
1474 : /* search forward to find the first USER element */
1475 0 : while (t && t->owner != XMPP_QUEUE_USER)
1476 0 : t = t->next;
1477 :
1478 : /* there was no USER element in the queue we could drop */
1479 0 : if (!t)
1480 : return NULL;
1481 :
1482 : /* In case there exists a SM stanza that is linked to the
1483 : * one we're currently dropping, also delete that one.
1484 : */
1485 0 : if (t->next && t->next->userdata == t)
1486 0 : strophe_free(conn->ctx, _drop_send_queue_element(conn, t->next));
1487 : /* Finally drop the element */
1488 0 : return _drop_send_queue_element(conn, t);
1489 : }
1490 :
1491 : /* timed handler for cleanup if normal disconnect procedure takes too long */
1492 0 : static int _disconnect_cleanup(xmpp_conn_t *conn, void *userdata)
1493 : {
1494 0 : UNUSED(userdata);
1495 :
1496 0 : strophe_debug(conn->ctx, "xmpp", "disconnection forced by cleanup timeout");
1497 :
1498 0 : conn_disconnect(conn);
1499 :
1500 0 : return 0;
1501 : }
1502 :
1503 0 : static char *_conn_build_stream_tag(xmpp_conn_t *conn,
1504 : char **attributes,
1505 : size_t attributes_len)
1506 : {
1507 0 : char *tag;
1508 0 : size_t len;
1509 0 : size_t i;
1510 :
1511 0 : static const char *tag_head = "<stream:stream";
1512 0 : static const char *tag_tail = ">";
1513 :
1514 : /* ignore the last element unless number is even */
1515 0 : attributes_len &= ~(size_t)1;
1516 :
1517 0 : len = strlen(tag_head) + strlen(tag_tail);
1518 0 : for (i = 0; i < attributes_len; ++i)
1519 0 : len += strlen(attributes[i]) + 2;
1520 0 : tag = strophe_alloc(conn->ctx, len + 1);
1521 0 : if (!tag)
1522 : return NULL;
1523 :
1524 0 : strcpy(tag, tag_head);
1525 0 : for (i = 0; i < attributes_len; ++i) {
1526 0 : if ((i & 1) == 0) {
1527 0 : strcat(tag, " ");
1528 0 : strcat(tag, attributes[i]);
1529 0 : strcat(tag, "=\"");
1530 : } else {
1531 0 : strcat(tag, attributes[i]);
1532 0 : strcat(tag, "\"");
1533 : }
1534 : }
1535 0 : strcat(tag, tag_tail);
1536 :
1537 0 : if (strlen(tag) != len) {
1538 0 : strophe_error(conn->ctx, "xmpp",
1539 : "Internal error in "
1540 : "_conn_build_stream_tag().");
1541 0 : strophe_free(conn->ctx, tag);
1542 0 : tag = NULL;
1543 : }
1544 :
1545 : return tag;
1546 : }
1547 :
1548 0 : static int _conn_open_stream_with_attributes(xmpp_conn_t *conn,
1549 : char **attributes,
1550 : size_t attributes_len)
1551 : {
1552 0 : char *tag;
1553 :
1554 0 : tag = _conn_build_stream_tag(conn, attributes, attributes_len);
1555 0 : if (!tag)
1556 : return XMPP_EMEM;
1557 :
1558 0 : send_raw_string(conn, "<?xml version=\"1.0\"?>%s", tag);
1559 0 : strophe_free(conn->ctx, tag);
1560 :
1561 0 : return XMPP_EOK;
1562 : }
1563 :
1564 0 : static void _conn_attributes_new(xmpp_conn_t *conn,
1565 : char **attrs,
1566 : char ***attributes,
1567 : size_t *attributes_len)
1568 : {
1569 0 : char **array = NULL;
1570 0 : size_t nr = 0;
1571 0 : size_t i;
1572 :
1573 0 : if (attrs) {
1574 0 : for (; attrs[nr]; ++nr)
1575 : ;
1576 0 : array = strophe_alloc(conn->ctx, sizeof(*array) * nr);
1577 0 : for (i = 0; array && i < nr; ++i) {
1578 0 : array[i] = (i & 1) == 0 ? parser_attr_name(conn->ctx, attrs[i])
1579 0 : : strophe_strdup(conn->ctx, attrs[i]);
1580 0 : if (array[i] == NULL)
1581 : break;
1582 : }
1583 0 : if (!array || i < nr) {
1584 0 : strophe_error(conn->ctx, "xmpp", "Memory allocation error.");
1585 0 : _conn_attributes_destroy(conn, array, i);
1586 0 : array = NULL;
1587 0 : nr = 0;
1588 : }
1589 : }
1590 0 : *attributes = array;
1591 0 : *attributes_len = nr;
1592 0 : }
1593 :
1594 0 : static void _conn_attributes_destroy(xmpp_conn_t *conn,
1595 : char **attributes,
1596 : size_t attributes_len)
1597 : {
1598 0 : size_t i;
1599 :
1600 0 : if (attributes) {
1601 0 : for (i = 0; i < attributes_len; ++i)
1602 0 : strophe_free(conn->ctx, attributes[i]);
1603 0 : strophe_free(conn->ctx, attributes);
1604 : }
1605 0 : }
1606 :
1607 0 : static void _log_open_tag(xmpp_conn_t *conn, char **attrs)
1608 : {
1609 0 : char **attributes;
1610 0 : char *tag;
1611 0 : size_t nr;
1612 :
1613 0 : _conn_attributes_new(conn, attrs, &attributes, &nr);
1614 0 : tag = _conn_build_stream_tag(conn, attributes, nr);
1615 0 : if (tag) {
1616 0 : strophe_debug(conn->ctx, "xmpp", "RECV: %s", tag);
1617 0 : strophe_free(conn->ctx, tag);
1618 : }
1619 0 : _conn_attributes_destroy(conn, attributes, nr);
1620 0 : }
1621 :
1622 0 : static char *_get_stream_attribute(char **attrs, char *name)
1623 : {
1624 0 : int i;
1625 :
1626 0 : if (!attrs)
1627 : return NULL;
1628 :
1629 0 : for (i = 0; attrs[i]; i += 2)
1630 0 : if (strcmp(name, attrs[i]) == 0)
1631 0 : return attrs[i + 1];
1632 :
1633 : return NULL;
1634 : }
1635 :
1636 0 : static void _handle_stream_start(char *name, char **attrs, void *userdata)
1637 : {
1638 0 : xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
1639 0 : char *id;
1640 0 : int failed = 0;
1641 :
1642 0 : if (conn->stream_id)
1643 0 : strophe_free(conn->ctx, conn->stream_id);
1644 0 : conn->stream_id = NULL;
1645 :
1646 0 : if (strcmp(name, "stream") == 0) {
1647 0 : _log_open_tag(conn, attrs);
1648 0 : id = _get_stream_attribute(attrs, "id");
1649 0 : if (id)
1650 0 : conn->stream_id = strophe_strdup(conn->ctx, id);
1651 :
1652 0 : if (id && !conn->stream_id) {
1653 0 : strophe_error(conn->ctx, "conn", "Memory allocation failed.");
1654 0 : failed = 1;
1655 : }
1656 : } else {
1657 0 : strophe_error(conn->ctx, "conn",
1658 : "Server did not open valid stream."
1659 : " name = %s.",
1660 : name);
1661 0 : failed = 1;
1662 : }
1663 :
1664 0 : if (!failed) {
1665 : /* call stream open handler */
1666 0 : conn->open_handler(conn);
1667 : } else {
1668 0 : conn_disconnect(conn);
1669 : }
1670 0 : }
1671 :
1672 0 : static void _handle_stream_end(char *name, void *userdata)
1673 : {
1674 0 : xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
1675 :
1676 0 : UNUSED(name);
1677 :
1678 : /* stream is over */
1679 0 : strophe_debug(conn->ctx, "xmpp", "RECV: </stream:stream>");
1680 : /* the session has been terminated properly, i.e. it can't be resumed */
1681 0 : conn->sm_state->can_resume = 0;
1682 0 : conn_disconnect_clean(conn);
1683 0 : }
1684 :
1685 0 : static void _handle_stream_stanza(xmpp_stanza_t *stanza, void *userdata)
1686 : {
1687 0 : xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
1688 0 : char *buf;
1689 0 : size_t len;
1690 :
1691 0 : if (xmpp_stanza_to_text(stanza, &buf, &len) == 0) {
1692 0 : strophe_debug(conn->ctx, "xmpp", "RECV: %s", buf);
1693 0 : strophe_free(conn->ctx, buf);
1694 : }
1695 :
1696 0 : handler_fire_stanza(conn, stanza);
1697 0 : if (conn->sm_state->sm_enabled)
1698 0 : _conn_sm_handle_stanza(conn, stanza);
1699 0 : }
1700 :
1701 : /* XEP-0198 stream management */
1702 0 : static void _conn_sm_handle_stanza(xmpp_conn_t *const conn,
1703 : xmpp_stanza_t *stanza)
1704 : {
1705 0 : xmpp_stanza_t *a;
1706 0 : xmpp_send_queue_t *e;
1707 0 : char *c;
1708 0 : const char *name, *ns, *attr_h;
1709 0 : char h[11];
1710 0 : unsigned long ul_h;
1711 :
1712 0 : ns = xmpp_stanza_get_ns(stanza);
1713 0 : if (ns && strcmp(ns, XMPP_NS_SM) != 0)
1714 0 : ++conn->sm_state->sm_handled_nr;
1715 : else {
1716 0 : name = xmpp_stanza_get_name(stanza);
1717 0 : if (!name)
1718 0 : return;
1719 0 : if (strcmp(name, "r") == 0) {
1720 0 : a = xmpp_stanza_new(conn->ctx);
1721 0 : if (!a) {
1722 0 : strophe_debug(conn->ctx, "conn", "Couldn't create <a> stanza.");
1723 0 : return;
1724 : }
1725 0 : xmpp_stanza_set_name(a, "a");
1726 0 : xmpp_stanza_set_ns(a, XMPP_NS_SM);
1727 0 : strophe_snprintf(h, sizeof(h), "%u", conn->sm_state->sm_handled_nr);
1728 0 : xmpp_stanza_set_attribute(a, "h", h);
1729 0 : send_stanza(conn, a, XMPP_QUEUE_SM_STROPHE);
1730 0 : } else if (strcmp(name, "a") == 0) {
1731 0 : attr_h = xmpp_stanza_get_attribute(stanza, "h");
1732 0 : if (!attr_h) {
1733 0 : strophe_debug(conn->ctx, "conn", "Didn't find 'h' attribute.");
1734 0 : return;
1735 : }
1736 0 : if (string_to_ul(attr_h, &ul_h)) {
1737 0 : strophe_error(
1738 0 : conn->ctx, "conn",
1739 : "Error on strtoul() of '%s', returned value is %llu.",
1740 : attr_h, ul_h);
1741 : /* We continue here and drop the complete SM queue instead of
1742 : * returning and letting the queue fill up.
1743 : */
1744 0 : ul_h = ULONG_MAX;
1745 : }
1746 0 : while (conn->sm_state->sm_queue.head &&
1747 0 : conn->sm_state->sm_queue.head->sm_h < ul_h) {
1748 0 : e = pop_queue_front(&conn->sm_state->sm_queue);
1749 0 : strophe_debug_verbose(2, conn->ctx, "conn", "SM_Q_DROP: %p", e);
1750 0 : c = queue_element_free(conn->ctx, e);
1751 0 : strophe_free(conn->ctx, c);
1752 : }
1753 : }
1754 : }
1755 : }
1756 :
1757 0 : static unsigned short _conn_default_port(xmpp_conn_t *conn,
1758 : xmpp_conn_type_t type)
1759 : {
1760 0 : switch (type) {
1761 0 : case XMPP_CLIENT:
1762 0 : return conn->tls_legacy_ssl ? XMPP_PORT_CLIENT_LEGACY_SSL
1763 0 : : XMPP_PORT_CLIENT;
1764 : case XMPP_COMPONENT:
1765 : return XMPP_PORT_COMPONENT;
1766 : default:
1767 : return 0;
1768 : };
1769 : }
1770 :
1771 0 : char *queue_element_free(xmpp_ctx_t *ctx, xmpp_send_queue_t *e)
1772 : {
1773 0 : char *ret = e->data;
1774 0 : strophe_debug_verbose(2, ctx, "conn", "Q_FREE: %p", e);
1775 0 : memset(e, 0, sizeof(*e));
1776 0 : strophe_free(ctx, e);
1777 0 : strophe_debug_verbose(3, ctx, "conn", "Q_CONTENT: %s", ret);
1778 0 : return ret;
1779 : }
1780 :
1781 8 : static void _conn_reset(xmpp_conn_t *conn)
1782 : {
1783 8 : xmpp_ctx_t *ctx = conn->ctx;
1784 8 : xmpp_send_queue_t *sq, *tsq;
1785 :
1786 8 : if (conn->state != XMPP_STATE_DISCONNECTED) {
1787 0 : strophe_debug(ctx, "conn", "Can't reset connected object.");
1788 0 : return;
1789 : }
1790 :
1791 : /* free queued */
1792 8 : sq = conn->send_queue_head;
1793 8 : while (sq) {
1794 0 : tsq = sq;
1795 0 : sq = sq->next;
1796 0 : strophe_free(ctx, queue_element_free(ctx, tsq));
1797 : }
1798 8 : conn->send_queue_head = NULL;
1799 8 : conn->send_queue_tail = NULL;
1800 8 : conn->send_queue_len = 0;
1801 8 : conn->send_queue_user_len = 0;
1802 :
1803 8 : if (conn->stream_error) {
1804 0 : xmpp_stanza_release(conn->stream_error->stanza);
1805 0 : if (conn->stream_error->text)
1806 0 : strophe_free(ctx, conn->stream_error->text);
1807 0 : strophe_free(ctx, conn->stream_error);
1808 0 : conn->stream_error = NULL;
1809 : }
1810 :
1811 8 : if (conn->domain)
1812 0 : strophe_free(ctx, conn->domain);
1813 8 : if (conn->bound_jid)
1814 0 : strophe_free(ctx, conn->bound_jid);
1815 8 : if (conn->stream_id)
1816 0 : strophe_free(ctx, conn->stream_id);
1817 8 : conn->domain = NULL;
1818 8 : conn->bound_jid = NULL;
1819 8 : conn->stream_id = NULL;
1820 8 : conn->authenticated = 0;
1821 8 : conn->secured = 0;
1822 8 : conn->tls_failed = 0;
1823 8 : conn->error = 0;
1824 :
1825 8 : conn->tls_support = 0;
1826 :
1827 8 : conn->bind_required = 0;
1828 8 : conn->session_required = 0;
1829 :
1830 8 : handler_system_delete_all(conn);
1831 : }
1832 :
1833 0 : static int _conn_connect(xmpp_conn_t *conn,
1834 : const char *domain,
1835 : xmpp_conn_type_t type,
1836 : xmpp_conn_handler callback,
1837 : void *userdata)
1838 : {
1839 0 : xmpp_open_handler open_handler;
1840 :
1841 0 : if (conn->state != XMPP_STATE_DISCONNECTED)
1842 : return XMPP_EINVOP;
1843 0 : if (type != XMPP_CLIENT && type != XMPP_COMPONENT)
1844 : return XMPP_EINVOP;
1845 :
1846 0 : _conn_reset(conn);
1847 :
1848 0 : conn->type = type;
1849 0 : conn->domain = strophe_strdup(conn->ctx, domain);
1850 0 : if (!conn->domain)
1851 : return XMPP_EMEM;
1852 :
1853 0 : conn->sock = sock_connect(conn->xsock);
1854 0 : if (conn->sock == INVALID_SOCKET)
1855 : return XMPP_EINT;
1856 :
1857 : /* setup handler */
1858 0 : conn->conn_handler = callback;
1859 0 : conn->userdata = userdata;
1860 :
1861 0 : open_handler = conn->is_raw ? auth_handle_open_stub
1862 0 : : type == XMPP_CLIENT ? auth_handle_open
1863 0 : : auth_handle_component_open;
1864 0 : conn_prepare_reset(conn, open_handler);
1865 :
1866 : /* FIXME: it could happen that the connect returns immediately as
1867 : * successful, though this is pretty unlikely. This would be a little
1868 : * hard to fix, since we'd have to detect and fire off the callback
1869 : * from within the event loop */
1870 :
1871 0 : conn->state = XMPP_STATE_CONNECTING;
1872 0 : conn->timeout_stamp = time_stamp();
1873 :
1874 0 : return 0;
1875 : }
1876 :
1877 0 : void send_raw(xmpp_conn_t *conn,
1878 : const char *data,
1879 : size_t len,
1880 : xmpp_send_queue_owner_t owner,
1881 : void *userdata)
1882 : {
1883 0 : char *d;
1884 :
1885 0 : if (conn->state != XMPP_STATE_CONNECTED)
1886 : return;
1887 :
1888 0 : d = strophe_strndup(conn->ctx, data, len);
1889 0 : if (!d) {
1890 0 : strophe_error(conn->ctx, "conn", "Failed to strndup");
1891 0 : return;
1892 : }
1893 :
1894 0 : _send_raw(conn, d, len, owner, userdata);
1895 : }
1896 :
1897 0 : static void _send_valist(xmpp_conn_t *conn,
1898 : const char *fmt,
1899 : va_list ap,
1900 : xmpp_send_queue_owner_t owner)
1901 : {
1902 0 : va_list apdup;
1903 0 : size_t len;
1904 0 : char buf[1024]; /* small buffer for common case */
1905 0 : char *bigbuf;
1906 :
1907 0 : if (conn->state != XMPP_STATE_CONNECTED)
1908 0 : return;
1909 :
1910 0 : va_copy(apdup, ap);
1911 0 : len = strophe_vsnprintf(buf, sizeof(buf), fmt, apdup);
1912 0 : va_end(apdup);
1913 :
1914 0 : if (len >= sizeof(buf)) {
1915 : /* we need more space for this data, so we allocate a big
1916 : * enough buffer and print to that */
1917 0 : len++; /* account for trailing \0 */
1918 0 : bigbuf = strophe_alloc(conn->ctx, len);
1919 0 : if (!bigbuf) {
1920 0 : strophe_debug(conn->ctx, "xmpp",
1921 : "Could not allocate memory for send_raw_string");
1922 0 : return;
1923 : }
1924 0 : va_copy(apdup, ap);
1925 0 : strophe_vsnprintf(bigbuf, len, fmt, apdup);
1926 0 : va_end(apdup);
1927 :
1928 : /* len - 1 so we don't send trailing \0 */
1929 0 : _send_raw(conn, bigbuf, len - 1, owner, NULL);
1930 : } else {
1931 : /* go through send_raw() which does the strdup() for us */
1932 0 : send_raw(conn, buf, len, owner, NULL);
1933 : }
1934 : }
1935 :
1936 0 : void send_raw_string(xmpp_conn_t *conn, const char *fmt, ...)
1937 : {
1938 0 : va_list ap;
1939 :
1940 0 : if (conn->state != XMPP_STATE_CONNECTED)
1941 0 : return;
1942 :
1943 0 : va_start(ap, fmt);
1944 0 : _send_valist(conn, fmt, ap, XMPP_QUEUE_SM_STROPHE);
1945 0 : va_end(ap);
1946 : }
1947 :
1948 0 : void send_stanza(xmpp_conn_t *conn,
1949 : xmpp_stanza_t *stanza,
1950 : xmpp_send_queue_owner_t owner)
1951 : {
1952 0 : char *buf = NULL;
1953 0 : size_t len;
1954 :
1955 0 : if (conn->state != XMPP_STATE_CONNECTED)
1956 0 : goto out;
1957 :
1958 0 : if (xmpp_stanza_to_text(stanza, &buf, &len) != 0) {
1959 0 : strophe_error(conn->ctx, "conn", "Failed to stanza_to_text");
1960 0 : goto out;
1961 : }
1962 :
1963 0 : _send_raw(conn, buf, len, owner, NULL);
1964 0 : out:
1965 0 : xmpp_stanza_release(stanza);
1966 0 : }
1967 :
1968 0 : void add_queue_back(xmpp_queue_t *queue, xmpp_send_queue_t *item)
1969 : {
1970 0 : item->next = NULL;
1971 0 : if (!queue->tail) {
1972 0 : item->prev = NULL;
1973 0 : queue->head = item;
1974 0 : queue->tail = item;
1975 : } else {
1976 0 : item->prev = queue->tail;
1977 0 : queue->tail->next = item;
1978 0 : queue->tail = item;
1979 : }
1980 0 : }
1981 :
1982 0 : xmpp_send_queue_t *pop_queue_front(xmpp_queue_t *queue)
1983 : {
1984 0 : xmpp_send_queue_t *ret = queue->head;
1985 0 : if (queue->head) {
1986 0 : queue->head = queue->head->next;
1987 0 : if (!queue->head) {
1988 0 : queue->tail = NULL;
1989 : } else {
1990 0 : queue->head->prev = NULL;
1991 : }
1992 0 : ret->prev = ret->next = NULL;
1993 : }
1994 0 : return ret;
1995 : }
1996 :
1997 0 : static int _send_raw(xmpp_conn_t *conn,
1998 : char *data,
1999 : size_t len,
2000 : xmpp_send_queue_owner_t owner,
2001 : void *userdata)
2002 : {
2003 0 : xmpp_send_queue_t *item;
2004 0 : const char *req_ack = "<r xmlns='urn:xmpp:sm:3'/>";
2005 :
2006 : /* create send queue item for queue */
2007 0 : item = strophe_alloc(conn->ctx, sizeof(xmpp_send_queue_t));
2008 0 : if (!item) {
2009 0 : strophe_error(conn->ctx, "conn", "DROPPED: %s", data);
2010 0 : strophe_free(conn->ctx, data);
2011 0 : return XMPP_EMEM;
2012 : }
2013 :
2014 0 : item->data = data;
2015 0 : item->len = len;
2016 0 : item->next = NULL;
2017 0 : item->prev = conn->send_queue_tail;
2018 0 : item->written = 0;
2019 0 : item->wip = 0;
2020 0 : item->userdata = userdata;
2021 0 : item->owner = owner;
2022 :
2023 0 : if (!conn->send_queue_tail) {
2024 : /* first item, set head and tail */
2025 0 : conn->send_queue_head = item;
2026 0 : conn->send_queue_tail = item;
2027 : } else {
2028 : /* add to the tail */
2029 0 : conn->send_queue_tail->next = item;
2030 0 : conn->send_queue_tail = item;
2031 : }
2032 0 : conn->send_queue_len++;
2033 0 : if (owner == XMPP_QUEUE_USER)
2034 0 : conn->send_queue_user_len++;
2035 0 : strophe_debug_verbose(3, conn->ctx, "conn", "QUEUED: %s", data);
2036 0 : strophe_debug_verbose(1, conn->ctx, "conn", "Q_ADD: %p", item);
2037 0 : if (!(owner & XMPP_QUEUE_SM) && conn->sm_state->sm_enabled) {
2038 0 : send_raw(conn, req_ack, strlen(req_ack), XMPP_QUEUE_SM_STROPHE, item);
2039 : }
2040 : return XMPP_EOK;
2041 : }
|