yong-stropheruby 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/ext/handler.c ADDED
@@ -0,0 +1,592 @@
1
+ /* handler.c
2
+ ** strophe XMPP client library -- event handler management
3
+ **
4
+ ** Copyright (C) 2005-2008 OGG, LLC. All rights reserved.
5
+ **
6
+ ** This software is provided AS-IS with no warranty, either express
7
+ ** or implied.
8
+ **
9
+ ** This software is distributed under license and may not be copied,
10
+ ** modified or distributed except as expressly authorized under the
11
+ ** terms of the license contained in the file LICENSE.txt in this
12
+ ** distribution.
13
+ */
14
+
15
+ /** @file
16
+ * Event handler management.
17
+ */
18
+
19
+ /** @defgroup Handlers Stanza and timed event handlers
20
+ */
21
+
22
+ #include <stdio.h>
23
+ #include <stdlib.h>
24
+ #include <string.h>
25
+
26
+ #ifndef _WIN32
27
+ #include <stdint.h>
28
+ #else
29
+ #include "ostypes.h"
30
+ #endif
31
+
32
+ #include "strophe.h"
33
+ #include "common.h"
34
+
35
+ /** Fire off all stanza handlers that match.
36
+ * This function is called internally by the event loop whenever stanzas
37
+ * are received from the XMPP server.
38
+ *
39
+ * @param conn a Strophe connection object
40
+ * @param stanza a Strophe stanza object
41
+ */
42
+ void handler_fire_stanza(xmpp_conn_t * const conn,
43
+ xmpp_stanza_t * const stanza)
44
+ {
45
+ xmpp_handlist_t *item, *prev;
46
+ char *id, *ns, *name, *type;
47
+
48
+ /* call id handlers */
49
+ id = xmpp_stanza_get_id(stanza);
50
+ if (id) {
51
+ prev = NULL;
52
+ item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
53
+ while (item) {
54
+ xmpp_handlist_t *next = item->next;
55
+
56
+ if (item->user_handler && !conn->authenticated) {
57
+ item = next;
58
+ continue;
59
+ }
60
+
61
+ if (!((xmpp_handler)(item->handler))(conn, stanza, item->userdata)) {
62
+ /* handler is one-shot, so delete it */
63
+ if (prev)
64
+ prev->next = next;
65
+ else {
66
+ hash_drop(conn->id_handlers, id);
67
+ hash_add(conn->id_handlers, id, next);
68
+ }
69
+ xmpp_free(conn->ctx, item);
70
+ item = NULL;
71
+ }
72
+ if (item)
73
+ prev = item;
74
+ item = next;
75
+ }
76
+ }
77
+
78
+ /* call handlers */
79
+ ns = xmpp_stanza_get_ns(stanza);
80
+ name = xmpp_stanza_get_name(stanza);
81
+ type = xmpp_stanza_get_type(stanza);
82
+
83
+ /* enable all added handlers */
84
+ for (item = conn->handlers; item; item = item->next)
85
+ item->enabled = 1;
86
+
87
+ prev = NULL;
88
+ item = conn->handlers;
89
+ while (item) {
90
+ /* skip newly added handlers */
91
+ if (!item->enabled) {
92
+ prev = item;
93
+ item = item->next;
94
+ continue;
95
+ }
96
+
97
+ /* don't call user handlers until authentication succeeds */
98
+ if (item->user_handler && !conn->authenticated) {
99
+ prev = item;
100
+ item = item->next;
101
+ continue;
102
+ }
103
+
104
+ if ((!item->ns || (ns && strcmp(ns, item->ns) == 0) ||
105
+ xmpp_stanza_get_child_by_ns(stanza, item->ns)) &&
106
+ (!item->name || (name && strcmp(name, item->name) == 0)) &&
107
+ (!item->type || (type && strcmp(type, item->type) == 0)))
108
+ if (!((xmpp_handler)(item->handler))(conn, stanza, item->userdata)) {
109
+ /* handler is one-shot, so delete it */
110
+ if (prev)
111
+ prev->next = item->next;
112
+ else
113
+ conn->handlers = item->next;
114
+ xmpp_free(conn->ctx, item);
115
+ item = NULL;
116
+ }
117
+
118
+ if (item) {
119
+ prev = item;
120
+ item = item->next;
121
+ } else if (prev)
122
+ item = prev->next;
123
+ else
124
+ item = conn->handlers;
125
+ }
126
+ }
127
+
128
+ /** Fire off all timed handlers that are ready.
129
+ * This function is called internally by the event loop.
130
+ *
131
+ * @param ctx a Strophe context object
132
+ *
133
+ * @return the time in milliseconds until the next handler will be ready
134
+ */
135
+ uint64_t handler_fire_timed(xmpp_ctx_t * const ctx)
136
+ {
137
+ xmpp_connlist_t *connitem;
138
+ xmpp_handlist_t *handitem, *temp;
139
+ int ret, fired;
140
+ uint64_t elapsed, min;
141
+
142
+ min = (uint64_t)(-1);
143
+
144
+ connitem = ctx->connlist;
145
+ while (connitem) {
146
+ if (connitem->conn->state != XMPP_STATE_CONNECTED) {
147
+ connitem = connitem->next;
148
+ continue;
149
+ }
150
+
151
+ /* enable all handlers that were added */
152
+ for (handitem = connitem->conn->timed_handlers; handitem;
153
+ handitem = handitem->next)
154
+ handitem->enabled = 1;
155
+
156
+ handitem = connitem->conn->timed_handlers;
157
+ while (handitem) {
158
+ /* skip newly added handlers */
159
+ if (!handitem->enabled) {
160
+ handitem = handitem->next;
161
+ continue;
162
+ }
163
+
164
+ /* only fire user handlers after authentication */
165
+ if (handitem->user_handler && !connitem->conn->authenticated) {
166
+ handitem = handitem->next;
167
+ continue;
168
+ }
169
+
170
+ fired = 0;
171
+ elapsed = time_elapsed(handitem->last_stamp, time_stamp());
172
+ if (elapsed >= handitem->period) {
173
+ /* fire! */
174
+ fired = 1;
175
+ handitem->last_stamp = time_stamp();
176
+ ret = ((xmpp_timed_handler)handitem->handler)(connitem->conn, handitem->userdata);
177
+ } else if (min > (handitem->period - elapsed))
178
+ min = handitem->period - elapsed;
179
+
180
+ temp = handitem;
181
+ handitem = handitem->next;
182
+
183
+ /* delete handler if it returned false */
184
+ if (fired && !ret)
185
+ xmpp_timed_handler_delete(connitem->conn, temp->handler);
186
+ }
187
+
188
+ connitem = connitem->next;
189
+ }
190
+
191
+ return min;
192
+ }
193
+
194
+ /** Reset all timed handlers.
195
+ * This function is called internally when a connection is successful.
196
+ *
197
+ * @param conn a Strophe connection object
198
+ * @param user_only whether to reset all handlers or only user ones
199
+ */
200
+ void handler_reset_timed(xmpp_conn_t *conn, int user_only)
201
+ {
202
+ xmpp_handlist_t *handitem;
203
+
204
+ handitem = conn->timed_handlers;
205
+ while (handitem) {
206
+ if ((user_only && handitem->user_handler) || !user_only)
207
+ handitem->last_stamp = time_stamp();
208
+
209
+ handitem = handitem->next;
210
+ }
211
+ }
212
+
213
+ static void _timed_handler_add(xmpp_conn_t * const conn,
214
+ xmpp_timed_handler handler,
215
+ const unsigned long period,
216
+ void * const userdata,
217
+ const int user_handler)
218
+ {
219
+ xmpp_handlist_t *item, *tail;
220
+
221
+ /* check if handler is already in the list */
222
+ for (item = conn->timed_handlers; item; item = item->next) {
223
+ if (item->handler == (void *)handler)
224
+ break;
225
+ }
226
+ if (item) return;
227
+
228
+ /* build new item */
229
+ item = xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
230
+ if (!item) return;
231
+
232
+ item->user_handler = user_handler;
233
+ item->handler = (void *)handler;
234
+ item->userdata = userdata;
235
+ item->enabled = 0;
236
+ item->next = NULL;
237
+
238
+ item->period = period;
239
+ item->last_stamp = time_stamp();
240
+
241
+ /* append item to list */
242
+ if (!conn->timed_handlers)
243
+ conn->timed_handlers = item;
244
+ else {
245
+ tail = conn->timed_handlers;
246
+ while (tail->next)
247
+ tail = tail->next;
248
+ tail->next = item;
249
+ }
250
+ }
251
+
252
+ /** Delete a timed handler.
253
+ *
254
+ * @param conn a Strophe connection object
255
+ * @param handler function pointer to the handler
256
+ *
257
+ * @ingroup Handlers
258
+ */
259
+ void xmpp_timed_handler_delete(xmpp_conn_t * const conn,
260
+ xmpp_timed_handler handler)
261
+ {
262
+ xmpp_handlist_t *item, *prev;
263
+
264
+ if (!conn->timed_handlers) return;
265
+
266
+ prev = NULL;
267
+ item = conn->timed_handlers;
268
+ while (item) {
269
+ if (item->handler == (void *)handler)
270
+ break;
271
+ prev = item;
272
+ item = item->next;
273
+ }
274
+
275
+ if (item) {
276
+ if (prev)
277
+ prev->next = item->next;
278
+ else
279
+ conn->timed_handlers = item->next;
280
+
281
+ xmpp_free(conn->ctx, item);
282
+ }
283
+ }
284
+
285
+ static void _id_handler_add(xmpp_conn_t * const conn,
286
+ xmpp_handler handler,
287
+ const char * const id,
288
+ void * const userdata, int user_handler)
289
+ {
290
+ xmpp_handlist_t *item, *tail;
291
+
292
+ /* check if handler is already in the list */
293
+ item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
294
+ while (item) {
295
+ if (item->handler == (void *)handler)
296
+ break;
297
+ item = item->next;
298
+ }
299
+ if (item) return;
300
+
301
+ /* build new item */
302
+ item = xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
303
+ if (!item) return;
304
+
305
+ item->user_handler = user_handler;
306
+ item->handler = (void *)handler;
307
+ item->userdata = userdata;
308
+ item->enabled = 0;
309
+ item->next = NULL;
310
+
311
+ item->id = xmpp_strdup(conn->ctx, id);
312
+ if (!item->id) {
313
+ xmpp_free(conn->ctx, item);
314
+ return;
315
+ }
316
+
317
+ /* put on list in hash table */
318
+ tail = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
319
+ if (!tail)
320
+ hash_add(conn->id_handlers, id, item);
321
+ else {
322
+ while (tail->next)
323
+ tail = tail->next;
324
+ tail->next = item;
325
+ }
326
+ }
327
+
328
+ /** Delete an id based stanza handler.
329
+ *
330
+ * @param conn a Strophe connection object
331
+ * @param handler a function pointer to a stanza handler
332
+ * @param id a string containing the id the handler is for
333
+ *
334
+ * @ingroup Handlers
335
+ */
336
+ void xmpp_id_handler_delete(xmpp_conn_t * const conn,
337
+ xmpp_handler handler,
338
+ const char * const id)
339
+ {
340
+ xmpp_handlist_t *item, *prev;
341
+
342
+ prev = NULL;
343
+ item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
344
+ if (!item) return;
345
+
346
+ while (item) {
347
+ if (item->handler == (void *)handler)
348
+ break;
349
+
350
+ prev = item;
351
+ item = item->next;
352
+ }
353
+
354
+ if (item) {
355
+ if (prev)
356
+ prev->next = item->next;
357
+ else {
358
+ hash_drop(conn->id_handlers, id);
359
+ hash_add(conn->id_handlers, id, item->next);
360
+ }
361
+ xmpp_free(conn->ctx, item->id);
362
+ xmpp_free(conn->ctx, item);
363
+ }
364
+ }
365
+
366
+ /* add a stanza handler */
367
+ static void _handler_add(xmpp_conn_t * const conn,
368
+ xmpp_handler handler,
369
+ const char * const ns,
370
+ const char * const name,
371
+ const char * const type,
372
+ void * const userdata, int user_handler)
373
+ {
374
+ xmpp_handlist_t *item, *tail;
375
+
376
+ /* check if handler already in list */
377
+ for (item = conn->handlers; item; item = item->next) {
378
+ if (item->handler == (void *)handler)
379
+ break;
380
+ }
381
+ if (item) return;
382
+
383
+ /* build new item */
384
+ item = (xmpp_handlist_t *)xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
385
+ if (!item) return;
386
+
387
+ item->user_handler = user_handler;
388
+ item->handler = (void *)handler;
389
+ item->userdata = userdata;
390
+ item->enabled = 0;
391
+ item->next = NULL;
392
+
393
+ if (ns) {
394
+ item->ns = xmpp_strdup(conn->ctx, ns);
395
+ if (!item->ns) {
396
+ xmpp_free(conn->ctx, item);
397
+ return;
398
+ }
399
+ } else
400
+ item->ns = NULL;
401
+ if (name) {
402
+ item->name = xmpp_strdup(conn->ctx, name);
403
+ if (!item->name) {
404
+ if (item->ns) xmpp_free(conn->ctx, item->ns);
405
+ xmpp_free(conn->ctx, item);
406
+ return;
407
+ }
408
+ } else
409
+ item->name = NULL;
410
+ if (type) {
411
+ item->type = xmpp_strdup(conn->ctx, type);
412
+ if (!item->type) {
413
+ if (item->ns) xmpp_free(conn->ctx, item->ns);
414
+ if (item->name) xmpp_free(conn->ctx, item->name);
415
+ xmpp_free(conn->ctx, item);
416
+ }
417
+ } else
418
+ item->type = NULL;
419
+
420
+ /* append to list */
421
+ if (!conn->handlers)
422
+ conn->handlers = item;
423
+ else {
424
+ tail = conn->handlers;
425
+ while (tail->next)
426
+ tail = tail->next;
427
+ tail->next = item;
428
+ }
429
+ }
430
+
431
+ /** Delete a stanza handler.
432
+ *
433
+ * @param conn a Strophe connection object
434
+ * @param handler a function pointer to a stanza handler
435
+ *
436
+ * @ingroup Handlers
437
+ */
438
+ void xmpp_handler_delete(xmpp_conn_t * const conn,
439
+ xmpp_handler handler)
440
+ {
441
+ xmpp_handlist_t *prev, *item;
442
+
443
+ if (!conn->handlers) return;
444
+
445
+ prev = NULL;
446
+ item = conn->handlers;
447
+ while (item) {
448
+ if (item->handler == (void *)handler)
449
+ break;
450
+
451
+ prev = item;
452
+ item = item->next;
453
+ }
454
+
455
+ if (item) {
456
+ if (prev)
457
+ prev->next = item->next;
458
+ else
459
+ conn->handlers = item->next;
460
+
461
+ if (item->ns) xmpp_free(conn->ctx, item->ns);
462
+ if (item->name) xmpp_free(conn->ctx, item->name);
463
+ if (item->type) xmpp_free(conn->ctx, item->type);
464
+ xmpp_free(conn->ctx, item);
465
+ }
466
+ }
467
+
468
+ /** Add a timed handler.
469
+ * The handler will fire for the first time once the period has elapsed,
470
+ * and continue firing regularly after that. Strophe will try its best
471
+ * to fire handlers as close to the period times as it can, but accuracy
472
+ * will vary depending on the resolution of the event loop.
473
+ *
474
+ * @param conn a Strophe connection object
475
+ * @param handler a function pointer to a timed handler
476
+ * @param period the time in milliseconds between firings
477
+ * @param userdata an opaque data pointer that will be passed to the handler
478
+ *
479
+ * @ingroup Handlers
480
+ */
481
+ void xmpp_timed_handler_add(xmpp_conn_t * const conn,
482
+ xmpp_timed_handler handler,
483
+ const unsigned long period,
484
+ void * const userdata)
485
+ {
486
+ _timed_handler_add(conn, handler, period, userdata, 1);
487
+ }
488
+
489
+ /** Add a timed system handler.
490
+ * This function is used to add internal timed handlers and should not be
491
+ * used outside of the library.
492
+ *
493
+ * @param conn a Strophe connection object
494
+ * @param handler a function pointer to a timed handler
495
+ * @param period the time in milliseconds between firings
496
+ * @param userdata an opaque data pointer that will be passed to the handler
497
+ */
498
+ void handler_add_timed(xmpp_conn_t * const conn,
499
+ xmpp_timed_handler handler,
500
+ const unsigned long period,
501
+ void * const userdata)
502
+ {
503
+ _timed_handler_add(conn, handler, period, userdata, 0);
504
+ }
505
+
506
+ /** Add an id based stanza handler.
507
+
508
+ * This function adds a stanza handler for an &lt;iq/&gt; stanza of
509
+ * type 'result' or 'error' with a specific id attribute. This can
510
+ * be used to handle responses to specific &lt;iq/&gt;s.
511
+ *
512
+ * @param conn a Strophe connection object
513
+ * @param handler a function pointer to a stanza handler
514
+ * @param id a string with the id
515
+ * @param userdata an opaque data pointer that will be passed to the handler
516
+ *
517
+ * @ingroup Handlers
518
+ */
519
+ void xmpp_id_handler_add(xmpp_conn_t * const conn,
520
+ xmpp_handler handler,
521
+ const char * const id,
522
+ void * const userdata)
523
+ {
524
+ _id_handler_add(conn, handler, id, userdata, 1);
525
+ }
526
+
527
+ /** Add an id based system stanza handler.
528
+ * This function is used to add internal id based stanza handlers and should
529
+ * not be used outside of the library.
530
+ *
531
+ * @param conn a Strophe connection object
532
+ * @param handler a function pointer to a stanza handler
533
+ * @param id a string with the id
534
+ * @param userdata an opaque data pointer that will be passed to the handler
535
+ */
536
+ void handler_add_id(xmpp_conn_t * const conn,
537
+ xmpp_handler handler,
538
+ const char * const id,
539
+ void * const userdata)
540
+ {
541
+ _id_handler_add(conn, handler, id, userdata, 0);
542
+ }
543
+
544
+ /** Add a stanza handler.
545
+ * This function is used to add a stanza handler to a connection.
546
+ * The handler will be called when the any of the filters match. The
547
+ * name filter matches to the top level stanza name. The type filter
548
+ * matches the 'type' attribute of the top level stanza. The ns
549
+ * filter matches the namespace ('xmlns' attribute) of either the top
550
+ * level stanza or any of it's immediate children (this allows you do
551
+ * handle specific &lt;iq/&gt; stanzas based on the &lt;query/&gt;
552
+ * child namespace.
553
+ *
554
+ * @param conn a Strophe connection object
555
+ * @param handler a function pointer to a stanza handler
556
+ * @param ns a string with the namespace to match
557
+ * @param name a string with the stanza name to match
558
+ * @param type a string with the 'type' attribute to match
559
+ * @param userdata an opaque data pointer that will be passed to the handler
560
+ *
561
+ * @ingroup Handlers
562
+ */
563
+ void xmpp_handler_add(xmpp_conn_t * const conn,
564
+ xmpp_handler handler,
565
+ const char * const ns,
566
+ const char * const name,
567
+ const char * const type,
568
+ void * const userdata)
569
+ {
570
+ _handler_add(conn, handler, ns, name, type, userdata, 1);
571
+ }
572
+
573
+ /** Add a system stanza handler.
574
+ * This function is used to add internal stanza handlers and should
575
+ * not be used outside of the library.
576
+ *
577
+ * @param conn a Strophe connection object
578
+ * @param handler a function pointer to a stanza handler
579
+ * @param ns a string with the namespace to match
580
+ * @param name a string with the stanza name to match
581
+ * @param type a string with the 'type' attribute value to match
582
+ * @param userdata an opaque data pointer that will be passed to the handler
583
+ */
584
+ void handler_add(xmpp_conn_t * const conn,
585
+ xmpp_handler handler,
586
+ const char * const ns,
587
+ const char * const name,
588
+ const char * const type,
589
+ void * const userdata)
590
+ {
591
+ _handler_add(conn, handler, ns, name, type, userdata, 0);
592
+ }