yong-stropheruby 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }