Line data Source code
1 : /* SPDX-License-Identifier: MIT OR GPL-3.0-only */
2 : /* handler.c
3 : ** strophe XMPP client library -- event handler management
4 : **
5 : ** Copyright (C) 2005-2009 Collecta, Inc.
6 : **
7 : ** This software is provided AS-IS with no warranty, either express
8 : ** or implied.
9 : **
10 : ** This program is dual licensed under the MIT or GPLv3 licenses.
11 : */
12 :
13 : /** @file
14 : * Event handler management.
15 : */
16 :
17 : /** @defgroup Handlers Stanza and timed event handlers
18 : */
19 :
20 : #include <stdio.h>
21 : #include <stdlib.h>
22 : #include <string.h>
23 :
24 : #include "strophe.h"
25 : #include "common.h"
26 : #include "ostypes.h"
27 :
28 : typedef int (*xmpp_void_handler)();
29 :
30 : /* Remove item from the list pointed by head, but don't free it.
31 : * There can be a situation when user's handler deletes another handler which
32 : * is the previous in the list. handler_fire_stanza() and handler_fire_timed()
33 : * must handle this situation correctly. Current function helps to avoid
34 : * list corruption in described scenario.
35 : *
36 : * TODO Convert handler lists to double-linked lists. Current implementation
37 : * works for O(n).
38 : */
39 0 : static void _handler_item_remove(xmpp_handlist_t **head, xmpp_handlist_t *item)
40 : {
41 0 : while (*head) {
42 0 : if (*head == item) {
43 0 : *head = item->next;
44 0 : break;
45 : }
46 0 : head = &(*head)->next;
47 : }
48 : }
49 :
50 0 : static void _free_handlist_item(xmpp_ctx_t *ctx, xmpp_handlist_t *item)
51 : {
52 0 : if (item->u.ns)
53 0 : strophe_free(ctx, item->u.ns);
54 0 : if (item->u.name)
55 0 : strophe_free(ctx, item->u.name);
56 0 : if (item->u.type)
57 0 : strophe_free(ctx, item->u.type);
58 0 : strophe_free(ctx, item);
59 0 : }
60 :
61 : /** Fire off all stanza handlers that match.
62 : * This function is called internally by the event loop whenever stanzas
63 : * are received from the XMPP server.
64 : *
65 : * @param conn a Strophe connection object
66 : * @param stanza a Strophe stanza object
67 : */
68 0 : void handler_fire_stanza(xmpp_conn_t *conn, xmpp_stanza_t *stanza)
69 : {
70 0 : xmpp_handlist_t *item, *next, *head, *head_old;
71 0 : const char *id, *ns, *name, *type;
72 0 : int ret;
73 :
74 : /* call id handlers */
75 0 : id = xmpp_stanza_get_id(stanza);
76 0 : if (id) {
77 0 : head = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
78 : /* enable all added handlers */
79 0 : for (item = head; item; item = item->next)
80 0 : item->enabled = 1;
81 :
82 : item = head;
83 0 : while (item) {
84 : /* don't fire user handlers until stream negotiation has completed
85 : and skip newly added handlers */
86 0 : if ((item->user_handler && !conn->stream_negotiation_completed) ||
87 0 : !item->enabled) {
88 0 : item = item->next;
89 0 : continue;
90 : }
91 :
92 0 : ret = ((xmpp_handler)(item->handler))(conn, stanza, item->userdata);
93 0 : next = item->next;
94 0 : if (!ret) {
95 : /* handler is one-shot, so delete it */
96 0 : head_old = head;
97 0 : _handler_item_remove(&head, item);
98 0 : if (head != head_old) {
99 : /* replace old value */
100 0 : hash_add(conn->id_handlers, id, head);
101 : }
102 0 : strophe_free(conn->ctx, item->u.id);
103 0 : strophe_free(conn->ctx, item);
104 : }
105 : item = next;
106 : }
107 : }
108 :
109 : /* call handlers */
110 0 : ns = xmpp_stanza_get_ns(stanza);
111 0 : name = xmpp_stanza_get_name(stanza);
112 0 : type = xmpp_stanza_get_type(stanza);
113 :
114 : /* enable all added handlers */
115 0 : for (item = conn->handlers; item; item = item->next)
116 0 : item->enabled = 1;
117 :
118 : item = conn->handlers;
119 0 : while (item) {
120 : /* don't fire user handlers until stream negotiation has completed and
121 : skip newly added handlers */
122 0 : if ((item->user_handler && !conn->stream_negotiation_completed) ||
123 0 : !item->enabled) {
124 0 : item = item->next;
125 0 : continue;
126 : }
127 :
128 0 : next = item->next;
129 0 : if ((!item->u.ns || (ns && strcmp(ns, item->u.ns) == 0) ||
130 0 : xmpp_stanza_get_child_by_ns(stanza, item->u.ns)) &&
131 0 : (!item->u.name || (name && strcmp(name, item->u.name) == 0)) &&
132 0 : (!item->u.type || (type && strcmp(type, item->u.type) == 0))) {
133 :
134 0 : ret = ((xmpp_handler)(item->handler))(conn, stanza, item->userdata);
135 : /* list may be changed during execution of a handler */
136 0 : next = item->next;
137 0 : if (!ret) {
138 : /* handler is one-shot, so delete it */
139 0 : _handler_item_remove(&conn->handlers, item);
140 0 : _free_handlist_item(conn->ctx, item);
141 : }
142 : }
143 : item = next;
144 : }
145 0 : }
146 :
147 : /** Fire off all timed handlers that are ready.
148 : * This function is called internally by the event loop.
149 : *
150 : * @param ctx a Strophe context object
151 : *
152 : * @return the time in milliseconds until the next handler will be ready
153 : */
154 0 : uint64_t handler_fire_timed(xmpp_ctx_t *ctx)
155 : {
156 0 : xmpp_connlist_t *connitem;
157 0 : xmpp_handlist_t *item, *next;
158 0 : xmpp_conn_t *conn;
159 0 : uint64_t elapsed, min;
160 0 : uint64_t timestamp;
161 0 : int ret;
162 :
163 0 : min = (uint64_t)(-1);
164 :
165 0 : connitem = ctx->connlist;
166 0 : while (connitem) {
167 0 : conn = connitem->conn;
168 0 : if (conn->state != XMPP_STATE_CONNECTED) {
169 0 : connitem = connitem->next;
170 0 : continue;
171 : }
172 :
173 : /* enable all handlers that were added */
174 0 : for (item = conn->timed_handlers; item; item = item->next)
175 0 : item->enabled = 1;
176 :
177 : item = conn->timed_handlers;
178 0 : while (item) {
179 : /* don't fire user handlers until stream negotiation has completed
180 : and skip newly added handlers */
181 0 : if ((item->user_handler && !conn->stream_negotiation_completed) ||
182 0 : !item->enabled) {
183 0 : item = item->next;
184 0 : continue;
185 : }
186 :
187 0 : next = item->next;
188 0 : timestamp = time_stamp();
189 0 : elapsed = time_elapsed(item->u.last_stamp, timestamp);
190 0 : if (elapsed >= item->u.period) {
191 : /* fire! */
192 0 : item->u.last_stamp = timestamp;
193 0 : ret = ((xmpp_timed_handler)item->handler)(conn, item->userdata);
194 : /* list may be changed during execution of a handler */
195 0 : next = item->next;
196 0 : if (!ret) {
197 : /* delete handler if it returned false */
198 0 : _handler_item_remove(&conn->timed_handlers, item);
199 0 : strophe_free(ctx, item);
200 : }
201 0 : } else if (min > (item->u.period - elapsed))
202 : min = item->u.period - elapsed;
203 :
204 : item = next;
205 : }
206 :
207 0 : connitem = connitem->next;
208 : }
209 :
210 : /*
211 : * Check timed handlers in context. These handlers fire periodically
212 : * regardless of connections state.
213 : * TODO Reduce copy-paste.
214 : */
215 0 : item = ctx->timed_handlers;
216 0 : while (item) {
217 0 : next = item->next;
218 0 : timestamp = time_stamp();
219 0 : elapsed = time_elapsed(item->u.last_stamp, timestamp);
220 0 : if (elapsed >= item->u.period) {
221 : /* fire! */
222 0 : item->u.last_stamp = timestamp;
223 0 : ret =
224 0 : ((xmpp_global_timed_handler)item->handler)(ctx, item->userdata);
225 : /* list may be changed during execution of a handler */
226 0 : next = item->next;
227 0 : if (!ret) {
228 : /* delete handler if it returned false */
229 0 : _handler_item_remove(&ctx->timed_handlers, item);
230 0 : strophe_free(ctx, item);
231 : }
232 0 : } else if (min > (item->u.period - elapsed))
233 : min = item->u.period - elapsed;
234 :
235 : item = next;
236 : }
237 :
238 0 : return min;
239 : }
240 :
241 : /** Reset all timed handlers.
242 : * This function is called internally when a connection is successful.
243 : *
244 : * @param conn a Strophe connection object
245 : * @param user_only whether to reset all handlers or only user ones
246 : */
247 0 : void handler_reset_timed(xmpp_conn_t *conn, int user_only)
248 : {
249 0 : xmpp_handlist_t *handitem;
250 :
251 0 : handitem = conn->timed_handlers;
252 0 : while (handitem) {
253 0 : if ((user_only && handitem->user_handler) || !user_only)
254 0 : handitem->u.last_stamp = time_stamp();
255 :
256 0 : handitem = handitem->next;
257 : }
258 0 : }
259 :
260 0 : static void _timed_handler_add(xmpp_ctx_t *ctx,
261 : xmpp_handlist_t **handlers_list,
262 : xmpp_void_handler handler,
263 : unsigned long period,
264 : void *userdata,
265 : int user_handler)
266 : {
267 0 : xmpp_handlist_t *item;
268 :
269 : /* check if handler is already in the list */
270 0 : for (item = *handlers_list; item; item = item->next) {
271 0 : if (item->handler == handler && item->userdata == userdata) {
272 0 : strophe_warn(ctx, "xmpp", "Timed handler already exists.");
273 0 : break;
274 : }
275 : }
276 0 : if (item)
277 : return;
278 :
279 : /* build new item */
280 0 : item = strophe_alloc(ctx, sizeof(xmpp_handlist_t));
281 0 : if (!item)
282 : return;
283 :
284 0 : item->user_handler = user_handler;
285 0 : item->handler = handler;
286 0 : item->userdata = userdata;
287 0 : item->enabled = 0;
288 :
289 0 : item->u.period = period;
290 0 : item->u.last_stamp = time_stamp();
291 :
292 : /* append item to list */
293 0 : item->next = *handlers_list;
294 0 : *handlers_list = item;
295 : }
296 :
297 0 : static void _timed_handler_delete(xmpp_ctx_t *ctx,
298 : xmpp_handlist_t **handlers_list,
299 : xmpp_void_handler handler)
300 : {
301 0 : xmpp_handlist_t *item;
302 :
303 0 : while (*handlers_list) {
304 0 : item = *handlers_list;
305 0 : if (item->handler == handler) {
306 0 : *handlers_list = item->next;
307 0 : strophe_free(ctx, item);
308 : } else {
309 0 : handlers_list = &item->next;
310 : }
311 : }
312 0 : }
313 :
314 : /** Delete a timed handler.
315 : *
316 : * @param conn a Strophe connection object
317 : * @param handler function pointer to the handler
318 : *
319 : * @ingroup Handlers
320 : */
321 0 : void xmpp_timed_handler_delete(xmpp_conn_t *conn, xmpp_timed_handler handler)
322 : {
323 0 : _timed_handler_delete(conn->ctx, &conn->timed_handlers, handler);
324 0 : }
325 :
326 0 : static void _id_handler_add(xmpp_conn_t *conn,
327 : xmpp_handler handler,
328 : const char *id,
329 : void *userdata,
330 : int user_handler)
331 : {
332 0 : xmpp_handlist_t *item, *tail;
333 :
334 : /* check if handler is already in the list */
335 0 : item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
336 0 : while (item) {
337 0 : if (item->handler == handler && item->userdata == userdata) {
338 0 : strophe_warn(conn->ctx, "xmpp", "Id handler already exists.");
339 0 : break;
340 : }
341 0 : item = item->next;
342 : }
343 0 : if (item)
344 : return;
345 :
346 : /* build new item */
347 0 : item = strophe_alloc(conn->ctx, sizeof(xmpp_handlist_t));
348 0 : if (!item)
349 : return;
350 :
351 0 : item->user_handler = user_handler;
352 0 : item->handler = handler;
353 0 : item->userdata = userdata;
354 0 : item->enabled = 0;
355 0 : item->next = NULL;
356 :
357 0 : item->u.id = strophe_strdup(conn->ctx, id);
358 0 : if (!item->u.id) {
359 0 : strophe_free(conn->ctx, item);
360 0 : return;
361 : }
362 :
363 : /* put on list in hash table */
364 0 : tail = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
365 0 : if (!tail)
366 0 : hash_add(conn->id_handlers, id, item);
367 : else {
368 0 : while (tail->next)
369 : tail = tail->next;
370 0 : tail->next = item;
371 : }
372 : }
373 :
374 : /** Delete an id based stanza handler.
375 : *
376 : * @param conn a Strophe connection object
377 : * @param handler a function pointer to a stanza handler
378 : * @param id a string containing the id the handler is for
379 : *
380 : * @ingroup Handlers
381 : */
382 0 : void xmpp_id_handler_delete(xmpp_conn_t *conn,
383 : xmpp_handler handler,
384 : const char *id)
385 : {
386 0 : xmpp_handlist_t *item, *prev, *next;
387 :
388 0 : prev = NULL;
389 0 : item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
390 0 : if (!item)
391 : return;
392 :
393 0 : while (item) {
394 0 : next = item->next;
395 :
396 0 : if (item->handler == handler) {
397 0 : if (prev)
398 0 : prev->next = next;
399 : else {
400 0 : hash_drop(conn->id_handlers, id);
401 0 : hash_add(conn->id_handlers, id, next);
402 : }
403 :
404 0 : strophe_free(conn->ctx, item->u.id);
405 0 : strophe_free(conn->ctx, item);
406 0 : item = next;
407 : } else {
408 : prev = item;
409 : item = next;
410 : }
411 : }
412 : }
413 :
414 0 : static int _dup_string(xmpp_ctx_t *ctx, const char *src, char **dest)
415 : {
416 0 : if (src) {
417 0 : *dest = strophe_strdup(ctx, src);
418 0 : if (!(*dest))
419 : return 1;
420 : }
421 : return 0;
422 : }
423 :
424 : /* add a stanza handler */
425 0 : static void _handler_add(xmpp_conn_t *conn,
426 : xmpp_handler handler,
427 : const char *ns,
428 : const char *name,
429 : const char *type,
430 : void *userdata,
431 : int user_handler)
432 : {
433 0 : xmpp_handlist_t *item, *tail;
434 :
435 : /* check if handler already in list */
436 0 : for (item = conn->handlers; item; item = item->next) {
437 : /* same handler function can process different stanzas and
438 : distinguish them according to userdata. */
439 0 : if (item->handler == handler && item->userdata == userdata) {
440 0 : strophe_warn(conn->ctx, "xmpp", "Stanza handler already exists.");
441 0 : break;
442 : }
443 : }
444 0 : if (item)
445 : return;
446 :
447 : /* build new item */
448 0 : item = (xmpp_handlist_t *)strophe_alloc(conn->ctx, sizeof(xmpp_handlist_t));
449 0 : if (!item)
450 : return;
451 :
452 0 : memset(item, 0, sizeof(*item));
453 0 : item->user_handler = user_handler;
454 0 : item->handler = handler;
455 0 : item->userdata = userdata;
456 :
457 0 : if (_dup_string(conn->ctx, ns, &item->u.ns))
458 0 : goto error_out;
459 0 : if (_dup_string(conn->ctx, name, &item->u.name))
460 0 : goto error_out;
461 0 : if (_dup_string(conn->ctx, type, &item->u.type))
462 0 : goto error_out;
463 :
464 : /* append to list */
465 0 : if (!conn->handlers)
466 0 : conn->handlers = item;
467 : else {
468 : tail = conn->handlers;
469 0 : while (tail->next)
470 : tail = tail->next;
471 0 : tail->next = item;
472 : }
473 :
474 : return;
475 :
476 0 : error_out:
477 0 : _free_handlist_item(conn->ctx, item);
478 : }
479 :
480 : /** Delete a stanza handler.
481 : *
482 : * @param conn a Strophe connection object
483 : * @param handler a function pointer to a stanza handler
484 : *
485 : * @ingroup Handlers
486 : */
487 0 : void xmpp_handler_delete(xmpp_conn_t *conn, xmpp_handler handler)
488 : {
489 0 : xmpp_handlist_t *prev, *item;
490 :
491 0 : if (!conn->handlers)
492 : return;
493 :
494 : prev = NULL;
495 : item = conn->handlers;
496 0 : while (item) {
497 0 : if (item->handler == handler) {
498 0 : if (prev)
499 0 : prev->next = item->next;
500 : else
501 0 : conn->handlers = item->next;
502 :
503 0 : _free_handlist_item(conn->ctx, item);
504 0 : item = prev ? prev->next : conn->handlers;
505 : } else {
506 0 : prev = item;
507 0 : item = item->next;
508 : }
509 : }
510 : }
511 :
512 : /** Add a timed handler.
513 : * The handler will fire for the first time once the period has elapsed,
514 : * and continue firing regularly after that. Strophe will try its best
515 : * to fire handlers as close to the period times as it can, but accuracy
516 : * will vary depending on the resolution of the event loop.
517 : *
518 : * If the handler function returns true, it will be kept, and if it
519 : * returns false, it will be deleted from the list of handlers.
520 : *
521 : * @param conn a Strophe connection object
522 : * @param handler a function pointer to a timed handler
523 : * @param period the time in milliseconds between firings
524 : * @param userdata an opaque data pointer that will be passed to the handler
525 : *
526 : * @ingroup Handlers
527 : */
528 0 : void xmpp_timed_handler_add(xmpp_conn_t *conn,
529 : xmpp_timed_handler handler,
530 : unsigned long period,
531 : void *userdata)
532 : {
533 0 : _timed_handler_add(conn->ctx, &conn->timed_handlers, handler, period,
534 : userdata, 1);
535 0 : }
536 :
537 : /** Add a timed system handler.
538 : * This function is used to add internal timed handlers and should not be
539 : * used outside of the library.
540 : *
541 : * @param conn a Strophe connection object
542 : * @param handler a function pointer to a timed handler
543 : * @param period the time in milliseconds between firings
544 : * @param userdata an opaque data pointer that will be passed to the handler
545 : */
546 0 : void handler_add_timed(xmpp_conn_t *conn,
547 : xmpp_timed_handler handler,
548 : unsigned long period,
549 : void *userdata)
550 : {
551 0 : _timed_handler_add(conn->ctx, &conn->timed_handlers, handler, period,
552 : userdata, 0);
553 0 : }
554 :
555 : /** Add an id based stanza handler.
556 :
557 : * This function adds a stanza handler for an <iq/> stanza of
558 : * type 'result' or 'error' with a specific id attribute. This can
559 : * be used to handle responses to specific <iq/>s.
560 : *
561 : * If the handler function returns true, it will be kept, and if it
562 : * returns false, it will be deleted from the list of handlers.
563 : *
564 : * @param conn a Strophe connection object
565 : * @param handler a function pointer to a stanza handler
566 : * @param id a string with the id
567 : * @param userdata an opaque data pointer that will be passed to the handler
568 : *
569 : * @ingroup Handlers
570 : */
571 0 : void xmpp_id_handler_add(xmpp_conn_t *conn,
572 : xmpp_handler handler,
573 : const char *id,
574 : void *userdata)
575 : {
576 0 : _id_handler_add(conn, handler, id, userdata, 1);
577 0 : }
578 :
579 : /** Add an id based system stanza handler.
580 : * This function is used to add internal id based stanza handlers and should
581 : * not be used outside of the library.
582 : *
583 : * @param conn a Strophe connection object
584 : * @param handler a function pointer to a stanza handler
585 : * @param id a string with the id
586 : * @param userdata an opaque data pointer that will be passed to the handler
587 : */
588 0 : void handler_add_id(xmpp_conn_t *conn,
589 : xmpp_handler handler,
590 : const char *id,
591 : void *userdata)
592 : {
593 0 : _id_handler_add(conn, handler, id, userdata, 0);
594 0 : }
595 :
596 : /** Add a stanza handler.
597 : * This function is used to add a stanza handler to a connection.
598 : * The handler will be called when the any of the filters match. The
599 : * name filter matches to the top level stanza name. The type filter
600 : * matches the 'type' attribute of the top level stanza. The ns
601 : * filter matches the namespace ('xmlns' attribute) of either the top
602 : * level stanza or any of it's immediate children (this allows you do
603 : * handle specific <iq/> stanzas based on the <query/>
604 : * child namespace.
605 : *
606 : * If the handler function returns true, it will be kept, and if it
607 : * returns false, it will be deleted from the list of handlers.
608 : *
609 : * @param conn a Strophe connection object
610 : * @param handler a function pointer to a stanza handler
611 : * @param ns a string with the namespace to match
612 : * @param name a string with the stanza name to match
613 : * @param type a string with the 'type' attribute to match
614 : * @param userdata an opaque data pointer that will be passed to the handler
615 : *
616 : * @ingroup Handlers
617 : */
618 0 : void xmpp_handler_add(xmpp_conn_t *conn,
619 : xmpp_handler handler,
620 : const char *ns,
621 : const char *name,
622 : const char *type,
623 : void *userdata)
624 : {
625 0 : _handler_add(conn, handler, ns, name, type, userdata, 1);
626 0 : }
627 :
628 : /** Add a system stanza handler.
629 : * This function is used to add internal stanza handlers and should
630 : * not be used outside of the library.
631 : *
632 : * @param conn a Strophe connection object
633 : * @param handler a function pointer to a stanza handler
634 : * @param ns a string with the namespace to match
635 : * @param name a string with the stanza name to match
636 : * @param type a string with the 'type' attribute value to match
637 : * @param userdata an opaque data pointer that will be passed to the handler
638 : */
639 0 : void handler_add(xmpp_conn_t *conn,
640 : xmpp_handler handler,
641 : const char *ns,
642 : const char *name,
643 : const char *type,
644 : void *userdata)
645 : {
646 0 : _handler_add(conn, handler, ns, name, type, userdata, 0);
647 0 : }
648 :
649 : /** Delete all system handlers.
650 : * This function is used to reset conn object before re-connecting.
651 : *
652 : * @param conn a Strophe connection object
653 : */
654 8 : void handler_system_delete_all(xmpp_conn_t *conn)
655 : {
656 8 : xmpp_handlist_t *item, *next, *head, *head_old;
657 8 : hash_iterator_t *iter;
658 8 : const char *key, *key2;
659 :
660 : /* TODO unify all kinds of handlers and avoid copy-paste below */
661 :
662 8 : item = conn->handlers;
663 8 : while (item) {
664 0 : if (!item->user_handler) {
665 0 : next = item->next;
666 0 : _handler_item_remove(&conn->handlers, item);
667 0 : _free_handlist_item(conn->ctx, item);
668 : item = next;
669 : } else
670 0 : item = item->next;
671 : }
672 :
673 8 : item = conn->timed_handlers;
674 8 : while (item) {
675 0 : if (!item->user_handler) {
676 0 : next = item->next;
677 0 : _handler_item_remove(&conn->timed_handlers, item);
678 0 : strophe_free(conn->ctx, item);
679 : item = next;
680 : } else
681 0 : item = item->next;
682 : }
683 :
684 8 : iter = hash_iter_new(conn->id_handlers);
685 8 : key = iter == NULL ? NULL : hash_iter_next(iter);
686 8 : while (key != NULL) {
687 0 : head = head_old = (xmpp_handlist_t *)hash_get(conn->id_handlers, key);
688 0 : item = head;
689 0 : while (item) {
690 0 : if (!item->user_handler) {
691 0 : next = item->next;
692 0 : _handler_item_remove(&head, item);
693 0 : strophe_free(conn->ctx, item->u.id);
694 0 : strophe_free(conn->ctx, item);
695 : item = next;
696 : } else
697 0 : item = item->next;
698 : }
699 : /* Hash table implementation is not perfect, so we need to find next
700 : key before dropping current one. Otherwise, we will get access to
701 : freed memory. */
702 0 : key2 = hash_iter_next(iter);
703 0 : if (head != head_old) {
704 : /* hash_add() replaces value if the key exists */
705 0 : if (head != NULL)
706 0 : hash_add(conn->id_handlers, key, head);
707 : else
708 0 : hash_drop(conn->id_handlers, key);
709 : }
710 : key = key2;
711 : }
712 8 : if (iter)
713 8 : hash_iter_release(iter);
714 8 : }
715 :
716 : /** Add a global timed handler.
717 : * The handler will fire for the first time once the period has elapsed,
718 : * and continue firing regularly after that. Strophe will try its best
719 : * to fire handlers as close to the period times as it can, but accuracy
720 : * will vary depending on the resolution of the event loop.
721 : *
722 : * The main difference between global and ordinary handlers:
723 : * - Ordinary handler is related to a connection, fires only when the
724 : * connection is in connected state and is removed once the connection is
725 : * destroyed.
726 : * - Global handler fires regardless of connections state and is related to
727 : * a Strophe context.
728 : *
729 : * The handler is executed in context of the respective event loop.
730 : *
731 : * If the handler function returns true, it will be kept, and if it
732 : * returns false, it will be deleted from the list of handlers.
733 : *
734 : * Notice, the same handler pointer may be added multiple times with different
735 : * userdata pointers. However, xmpp_global_timed_handler_delete() deletes
736 : * all occurrences.
737 : *
738 : * @param ctx a Strophe context object
739 : * @param handler a function pointer to a timed handler
740 : * @param period the time in milliseconds between firings
741 : * @param userdata an opaque data pointer that will be passed to the handler
742 : *
743 : * @ingroup Handlers
744 : */
745 0 : void xmpp_global_timed_handler_add(xmpp_ctx_t *ctx,
746 : xmpp_global_timed_handler handler,
747 : unsigned long period,
748 : void *userdata)
749 : {
750 0 : _timed_handler_add(ctx, &ctx->timed_handlers, handler, period, userdata, 1);
751 0 : }
752 :
753 : /** Delete a global timed handler.
754 : *
755 : * @param ctx a Strophe context object
756 : * @param handler function pointer to the handler
757 : *
758 : * @ingroup Handlers
759 : */
760 0 : void xmpp_global_timed_handler_delete(xmpp_ctx_t *ctx,
761 : xmpp_global_timed_handler handler)
762 : {
763 0 : _timed_handler_delete(ctx, &ctx->timed_handlers, handler);
764 0 : }
|