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