diff --git a/ChangeLog b/ChangeLog index baff0df..4694dee 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,9 @@ - SCRAM-SHA-256 and SCRAM-SHA-512 support - c-ares support - LibreSSL support + - Introduced global timed handlers that fire periodically regardless of + connections status, such a handler can be used to implement deferred + re-connection - examples/register implements XEP-0077 - Fixed issue with IPv6 on Windows (#153) - Improved portability across systems such as Haiku, Windows @@ -13,6 +16,8 @@ - xmpp_conn_is_disconnected() - xmpp_stanza_add_child_ex() - xmpp_stanza_get_context() + - xmpp_global_timed_handler_add() + - xmpp_global_timed_handler_delete() 0.9.3 - PLAIN mechanism is used only when no other mechanisms are supported diff --git a/src/common.h b/src/common.h index 51247db..ddc30fd 100644 --- a/src/common.h +++ b/src/common.h @@ -29,6 +29,38 @@ #include "rand.h" #include "snprintf.h" +/** handlers **/ + +typedef struct _xmpp_handlist_t xmpp_handlist_t; +struct _xmpp_handlist_t { + /* common members */ + int user_handler; + int (*handler)(); + void *userdata; + int enabled; /* handlers are added disabled and enabled after the + * handler chain is processed to prevent stanzas from + * getting processed by newly added handlers */ + xmpp_handlist_t *next; + + union { + /* timed handlers */ + struct { + unsigned long period; + uint64_t last_stamp; + }; + /* id handlers */ + struct { + char *id; + }; + /* normal handlers */ + struct { + char *ns; + char *name; + char *type; + }; + } u; +}; + /** run-time context **/ typedef enum { @@ -49,6 +81,7 @@ struct _xmpp_ctx_t { xmpp_rand_t *rand; xmpp_loop_status_t loop_status; xmpp_connlist_t *connlist; + xmpp_handlist_t *timed_handlers; unsigned long timeout; }; @@ -100,36 +133,6 @@ struct _xmpp_send_queue_t { xmpp_send_queue_t *next; }; -typedef struct _xmpp_handlist_t xmpp_handlist_t; -struct _xmpp_handlist_t { - /* common members */ - int user_handler; - int (*handler)(); - void *userdata; - int enabled; /* handlers are added disabled and enabled after the - * handler chain is processed to prevent stanzas from - * getting processed by newly added handlers */ - xmpp_handlist_t *next; - - union { - /* timed handlers */ - struct { - unsigned long period; - uint64_t last_stamp; - }; - /* id handlers */ - struct { - char *id; - }; - /* normal handlers */ - struct { - char *ns; - char *name; - char *type; - }; - } u; -}; - #define UNUSED(x) ((void)(x)) #define MAX_DOMAIN_LEN 256 diff --git a/src/ctx.c b/src/ctx.c index f1b9784..3491616 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -419,6 +419,7 @@ xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t *const mem, ctx->log = log; ctx->connlist = NULL; + ctx->timed_handlers = NULL; ctx->loop_status = XMPP_LOOP_NOTSTARTED; ctx->rand = xmpp_rand_new(ctx); ctx->timeout = EVENT_LOOP_DEFAULT_TIMEOUT; diff --git a/src/handler.c b/src/handler.c index 74f30b7..2d8aa1d 100644 --- a/src/handler.c +++ b/src/handler.c @@ -24,6 +24,8 @@ #include "common.h" #include "ostypes.h" +typedef int (*xmpp_void_handler)(); + /* Remove item from the list pointed by head, but don't free it. * There can be a situation when user's handler deletes another handler which * is the previous in the list. handler_fire_stanza() and handler_fire_timed() @@ -35,16 +37,12 @@ */ static void _handler_item_remove(xmpp_handlist_t **head, xmpp_handlist_t *item) { - xmpp_handlist_t *i = *head; - - if (i == item) - *head = item->next; - else if (i != NULL) { - while (i->next != NULL && i->next != item) - i = i->next; - if (i->next == item) { - i->next = item->next; + while (*head) { + if (*head == item) { + *head = item->next; + break; } + head = &(*head)->next; } } @@ -191,7 +189,7 @@ uint64_t handler_fire_timed(xmpp_ctx_t *const ctx) if (!ret) { /* delete handler if it returned false */ _handler_item_remove(&conn->timed_handlers, item); - xmpp_free(conn->ctx, item); + xmpp_free(ctx, item); } } else if (min > (item->u.period - elapsed)) min = item->u.period - elapsed; @@ -202,6 +200,34 @@ uint64_t handler_fire_timed(xmpp_ctx_t *const ctx) connitem = connitem->next; } + /* + * Check timed handlers in context. These handlers fire periodically + * regardless of connections state. + * TODO Reduce copy-paste. + */ + item = ctx->timed_handlers; + while (item) { + next = item->next; + timestamp = time_stamp(); + elapsed = time_elapsed(item->u.last_stamp, timestamp); + if (elapsed >= item->u.period) { + /* fire! */ + item->u.last_stamp = timestamp; + ret = + ((xmpp_global_timed_handler)item->handler)(ctx, item->userdata); + /* list may be changed during execution of a handler */ + next = item->next; + if (!ret) { + /* delete handler if it returned false */ + _handler_item_remove(&ctx->timed_handlers, item); + xmpp_free(ctx, item); + } + } else if (min > (item->u.period - elapsed)) + min = item->u.period - elapsed; + + item = next; + } + return min; } @@ -224,18 +250,19 @@ void handler_reset_timed(xmpp_conn_t *conn, int user_only) } } -static void _timed_handler_add(xmpp_conn_t *const conn, - xmpp_timed_handler handler, +static void _timed_handler_add(xmpp_ctx_t *ctx, + xmpp_handlist_t **handlers_list, + xmpp_void_handler handler, const unsigned long period, void *const userdata, const int user_handler) { - xmpp_handlist_t *item, *tail; + xmpp_handlist_t *item; /* check if handler is already in the list */ - for (item = conn->timed_handlers; item; item = item->next) { + for (item = *handlers_list; item; item = item->next) { if (item->handler == handler && item->userdata == userdata) { - xmpp_warn(conn->ctx, "xmpp", "Timed handler already exists."); + xmpp_warn(ctx, "xmpp", "Timed handler already exists."); break; } } @@ -243,7 +270,7 @@ static void _timed_handler_add(xmpp_conn_t *const conn, return; /* build new item */ - item = xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t)); + item = xmpp_alloc(ctx, sizeof(xmpp_handlist_t)); if (!item) return; @@ -251,19 +278,29 @@ static void _timed_handler_add(xmpp_conn_t *const conn, item->handler = handler; item->userdata = userdata; item->enabled = 0; - item->next = NULL; item->u.period = period; item->u.last_stamp = time_stamp(); /* append item to list */ - if (!conn->timed_handlers) - conn->timed_handlers = item; - else { - tail = conn->timed_handlers; - while (tail->next) - tail = tail->next; - tail->next = item; + item->next = *handlers_list; + *handlers_list = item; +} + +static void _timed_handler_delete(xmpp_ctx_t *ctx, + xmpp_handlist_t **handlers_list, + xmpp_void_handler handler) +{ + xmpp_handlist_t *item; + + while (*handlers_list) { + item = *handlers_list; + if (item->handler == handler) { + *handlers_list = item->next; + xmpp_free(ctx, item); + } else { + handlers_list = &item->next; + } } } @@ -277,27 +314,7 @@ static void _timed_handler_add(xmpp_conn_t *const conn, void xmpp_timed_handler_delete(xmpp_conn_t *const conn, xmpp_timed_handler handler) { - xmpp_handlist_t *item, *prev; - - if (!conn->timed_handlers) - return; - - prev = NULL; - item = conn->timed_handlers; - while (item) { - if (item->handler == handler) { - if (prev) - prev->next = item->next; - else - conn->timed_handlers = item->next; - - xmpp_free(conn->ctx, item); - item = prev ? prev->next : conn->timed_handlers; - } else { - prev = item; - item = item->next; - } - } + _timed_handler_delete(conn->ctx, &conn->timed_handlers, handler); } static void _id_handler_add(xmpp_conn_t *const conn, @@ -524,7 +541,8 @@ void xmpp_timed_handler_add(xmpp_conn_t *const conn, const unsigned long period, void *const userdata) { - _timed_handler_add(conn, handler, period, userdata, 1); + _timed_handler_add(conn->ctx, &conn->timed_handlers, handler, period, + userdata, 1); } /** Add a timed system handler. @@ -541,7 +559,8 @@ void handler_add_timed(xmpp_conn_t *const conn, const unsigned long period, void *const userdata) { - _timed_handler_add(conn, handler, period, userdata, 0); + _timed_handler_add(conn->ctx, &conn->timed_handlers, handler, period, + userdata, 0); } /** Add an id based stanza handler. @@ -713,3 +732,17 @@ void handler_system_delete_all(xmpp_conn_t *conn) if (iter) hash_iter_release(iter); } + +void xmpp_global_timed_handler_add(xmpp_ctx_t *const ctx, + xmpp_global_timed_handler handler, + const unsigned long period, + void *const userdata) +{ + _timed_handler_add(ctx, &ctx->timed_handlers, handler, period, userdata, 1); +} + +void xmpp_global_timed_handler_delete(xmpp_ctx_t *const ctx, + xmpp_global_timed_handler handler) +{ + _timed_handler_delete(ctx, &ctx->timed_handlers, handler); +} diff --git a/strophe.h b/strophe.h index acf22a5..19898bb 100644 --- a/strophe.h +++ b/strophe.h @@ -279,7 +279,7 @@ void xmpp_send_raw(xmpp_conn_t *const conn, /* handlers */ -/* if the handle returns false it is removed */ +/* if the handler returns false it is removed */ typedef int (*xmpp_timed_handler)(xmpp_conn_t *const conn, void *const userdata); @@ -290,6 +290,17 @@ void xmpp_timed_handler_add(xmpp_conn_t *const conn, void xmpp_timed_handler_delete(xmpp_conn_t *const conn, xmpp_timed_handler handler); +/* if the handler returns false it is removed */ +typedef int (*xmpp_global_timed_handler)(xmpp_ctx_t *const ctx, + void *const userdata); + +void xmpp_global_timed_handler_add(xmpp_ctx_t *const ctx, + xmpp_global_timed_handler handler, + const unsigned long period, + void *const userdata); +void xmpp_global_timed_handler_delete(xmpp_ctx_t *const ctx, + xmpp_global_timed_handler handler); + /* if the handler returns false it is removed */ typedef int (*xmpp_handler)(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza,