stropheruby 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/ext/event.c ADDED
@@ -0,0 +1,351 @@
1
+ /* event.c
2
+ ** strophe XMPP client library -- event loop and 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 loop and management.
17
+ */
18
+
19
+ /** @defgroup EventLoop Event loop
20
+ * These functions manage the Strophe event loop.
21
+ *
22
+ * Simple tools can use xmpp_run() and xmpp_stop() to manage the life
23
+ * cycle of the program. A common idiom is to set up a few initial
24
+ * event handers, call xmpp_run(), and then respond and react to
25
+ * events as they come in. At some point, one of the handlers will
26
+ * call xmpp_stop() to quit the event loop which leads to the program
27
+ * terminating.
28
+ *
29
+ * More complex programs will have their own event loops, and should
30
+ * ensure that xmpp_run_once() is called regularly from there. For
31
+ * example, a GUI program will already include an event loop to
32
+ * process UI events from users, and xmpp_run_once() would be called
33
+ * from an idle function.
34
+ */
35
+
36
+ #include <stdio.h>
37
+ #include <stdlib.h>
38
+ #include <string.h>
39
+
40
+ #ifndef _WIN32
41
+ #include <sys/select.h>
42
+ #include <errno.h>
43
+ #else
44
+ #include <winsock2.h>
45
+ #define ETIMEDOUT WSAETIMEDOUT
46
+ #define ECONNRESET WSAECONNRESET
47
+ #define ECONNABORTED WSAECONNABORTED
48
+ #endif
49
+
50
+ #include "strophe.h"
51
+ #include "common.h"
52
+
53
+ #ifndef DEFAULT_TIMEOUT
54
+ /** @def DEFAULT_TIMEOUT
55
+ * The default timeout in milliseconds for the event loop.
56
+ * This is set to 1 millisecond.
57
+ */
58
+ #define DEFAULT_TIMEOUT 1
59
+ #endif
60
+
61
+ /** Run the event loop once.
62
+ * This function will run send any data that has been queued by
63
+ * xmpp_send and related functions and run through the Strophe even
64
+ * loop a single time, and will not wait more than timeout
65
+ * milliseconds for events. This is provided to support integration
66
+ * with event loops outside the library, and if used, should be
67
+ * called regularly to achieve low latency event handling.
68
+ *
69
+ * @param ctx a Strophe context object
70
+ * @param timeout time to wait for events in milliseconds
71
+ *
72
+ * @ingroup EventLoop
73
+ */
74
+ int xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
75
+ {
76
+ xmpp_connlist_t *connitem;
77
+ xmpp_conn_t *conn;
78
+ fd_set rfds, wfds;
79
+ sock_t max = 0;
80
+ int ret;
81
+ struct timeval tv;
82
+ xmpp_send_queue_t *sq, *tsq;
83
+ int towrite;
84
+ char buf[4096];
85
+ uint64_t next;
86
+
87
+ if (ctx->loop_status == XMPP_LOOP_QUIT) return -2;
88
+ ctx->loop_status = XMPP_LOOP_RUNNING;
89
+
90
+ /* send queued data */
91
+ connitem = ctx->connlist;
92
+ while (connitem) {
93
+ conn = connitem->conn;
94
+ if (conn->state != XMPP_STATE_CONNECTED) {
95
+ connitem = connitem->next;
96
+ continue;
97
+ }
98
+
99
+ /* if we're running tls, there may be some remaining data waiting to
100
+ * be sent, so push that out */
101
+ if (conn->tls) {
102
+ ret = tls_clear_pending_write(conn->tls);
103
+
104
+ if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls))) {
105
+ /* an error occured */
106
+ xmpp_debug(ctx, "xmpp", "Send error occured, disconnecting.");
107
+ conn->error = ECONNABORTED;
108
+ conn_disconnect(conn);
109
+ }
110
+ }
111
+
112
+ /* write all data from the send queue to the socket */
113
+ sq = conn->send_queue_head;
114
+ while (sq) {
115
+ towrite = sq->len - sq->written;
116
+
117
+ if (conn->tls) {
118
+ ret = tls_write(conn->tls, &sq->data[sq->written], towrite);
119
+
120
+ if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls))) {
121
+ /* an error occured */
122
+ conn->error = tls_error(conn->tls);
123
+ break;
124
+ } else if (ret < towrite) {
125
+ /* not all data could be sent now */
126
+ if (ret >= 0) sq->written += ret;
127
+ break;
128
+ }
129
+
130
+ } else {
131
+ ret = sock_write(conn->sock, &sq->data[sq->written], towrite);
132
+
133
+ if (ret < 0 && !sock_is_recoverable(sock_error())) {
134
+ /* an error occured */
135
+ conn->error = sock_error();
136
+ break;
137
+ } else if (ret < towrite) {
138
+ /* not all data could be sent now */
139
+ if (ret >= 0) sq->written += ret;
140
+ break;
141
+ }
142
+ }
143
+
144
+ /* all data for this queue item written, delete and move on */
145
+ xmpp_free(ctx, sq->data);
146
+ tsq = sq;
147
+ sq = sq->next;
148
+ xmpp_free(ctx, tsq);
149
+
150
+ /* pop the top item */
151
+ conn->send_queue_head = sq;
152
+ /* if we've sent everything update the tail */
153
+ if (!sq) conn->send_queue_tail = NULL;
154
+ }
155
+
156
+ /* tear down connection on error */
157
+ if (conn->error) {
158
+ /* FIXME: need to tear down send queues and random other things
159
+ * maybe this should be abstracted */
160
+ xmpp_debug(ctx, "xmpp", "Send error occured, disconnecting.");
161
+ conn->error = ECONNABORTED;
162
+ conn_disconnect(conn);
163
+ }
164
+
165
+ connitem = connitem->next;
166
+ }
167
+
168
+ /* reset parsers if needed */
169
+ for (connitem = ctx->connlist; connitem; connitem = connitem->next) {
170
+ if (connitem->conn->reset_parser)
171
+ parser_reset(connitem->conn);
172
+ }
173
+
174
+
175
+ /* fire any ready timed handlers, then
176
+ make sure we don't wait past the time when timed handlers need
177
+ to be called */
178
+ next = handler_fire_timed(ctx);
179
+
180
+ long usec = ((next < timeout) ? next : timeout) * 1000;
181
+ tv.tv_sec = usec / 1000000;
182
+ tv.tv_usec = usec % 1000000;
183
+
184
+ FD_ZERO(&rfds);
185
+ FD_ZERO(&wfds);
186
+
187
+ /* find events to watch */
188
+ connitem = ctx->connlist;
189
+ while (connitem) {
190
+ conn = connitem->conn;
191
+
192
+ switch (conn->state) {
193
+ case XMPP_STATE_CONNECTING:
194
+ /* connect has been called and we're waiting for it to complete */
195
+ /* connection will give us write or error events */
196
+
197
+ /* make sure the timeout hasn't expired */
198
+ if (time_elapsed(conn->timeout_stamp, time_stamp()) <=
199
+ conn->connect_timeout)
200
+ FD_SET(conn->sock, &wfds);
201
+ else {
202
+ conn->error = ETIMEDOUT;
203
+ xmpp_info(ctx, "xmpp", "Connection attempt timed out.");
204
+ conn_disconnect(conn);
205
+ }
206
+ break;
207
+ case XMPP_STATE_CONNECTED:
208
+ FD_SET(conn->sock, &rfds);
209
+ break;
210
+ case XMPP_STATE_DISCONNECTED:
211
+ /* do nothing */
212
+ default:
213
+ break;
214
+ }
215
+
216
+ if (conn->sock > max) max = conn->sock;
217
+
218
+ connitem = connitem->next;
219
+ }
220
+
221
+ /* check for events */
222
+ ret = select(max + 1, &rfds, &wfds, NULL, &tv);
223
+
224
+ /* select errored */
225
+ if (ret < 0) {
226
+ if (!sock_is_recoverable(sock_error())) {
227
+ xmpp_error(ctx, "xmpp", "event watcher internal error %d",
228
+ sock_error());
229
+ conn->error = sock_error();
230
+ return -1;
231
+ }
232
+ }
233
+
234
+ /* no events happened */
235
+ if (ret == 0) {
236
+ conn->error = ETIMEDOUT;
237
+ return 1;
238
+ }
239
+
240
+ /* process events */
241
+ connitem = ctx->connlist;
242
+ while (connitem) {
243
+ conn = connitem->conn;
244
+
245
+ switch (conn->state) {
246
+ case XMPP_STATE_CONNECTING:
247
+ if (FD_ISSET(conn->sock, &wfds)) {
248
+ /* connection complete */
249
+
250
+ /* check for error */
251
+ conn->error = sock_connect_error(conn->sock);
252
+ if (conn->error != 0) {
253
+ /* connection failed */
254
+ xmpp_debug(ctx, "xmpp", "connection failed");
255
+ conn_disconnect(conn);
256
+ break;
257
+ }
258
+
259
+ conn->state = XMPP_STATE_CONNECTED;
260
+ xmpp_debug(ctx, "xmpp", "connection successful");
261
+
262
+
263
+ /* send stream init */
264
+ conn_open_stream(conn);
265
+ }
266
+
267
+ break;
268
+ case XMPP_STATE_CONNECTED:
269
+ if (FD_ISSET(conn->sock, &rfds)) {
270
+ if (conn->tls) {
271
+ ret = tls_read(conn->tls, buf, 4096);
272
+ } else {
273
+ ret = sock_read(conn->sock, buf, 4096);
274
+ }
275
+
276
+ if (ret > 0) {
277
+ ret = XML_Parse(conn->parser, buf, ret, 0);
278
+ if (!ret) {
279
+ /* parse error, we need to shut down */
280
+ /* FIXME */
281
+ xmpp_debug(ctx, "xmpp", "parse error, disconnecting");
282
+ conn -> error = -1;
283
+ conn_disconnect(conn);
284
+ }
285
+ } else {
286
+ if (conn->tls) {
287
+ if (!tls_is_recoverable(tls_error(conn->tls)))
288
+ {
289
+ xmpp_debug(ctx, "xmpp", "Unrecoverable TLS error, %d.", tls_error(conn->tls));
290
+ conn->error = tls_error(conn->tls);
291
+ conn_disconnect(conn);
292
+ }
293
+ } else {
294
+ /* return of 0 means socket closed by server */
295
+ xmpp_debug(ctx, "xmpp", "Socket closed by remote host.");
296
+ conn->error = ECONNRESET;
297
+ conn_disconnect(conn);
298
+ }
299
+ }
300
+ }
301
+
302
+ break;
303
+ case XMPP_STATE_DISCONNECTED:
304
+ /* do nothing */
305
+ default:
306
+ break;
307
+ }
308
+
309
+ connitem = connitem->next;
310
+ }
311
+
312
+ /* fire any ready handlers */
313
+ handler_fire_timed(ctx);
314
+ return 0;
315
+ }
316
+
317
+ /** Start the event loop.
318
+ * This function continuously calls xmpp_run_once and does not return
319
+ * until xmpp_stop has been called.
320
+ *
321
+ * @param ctx a Strophe context object
322
+ *
323
+ * @ingroup EventLoop
324
+ */
325
+ void xmpp_run(xmpp_ctx_t *ctx)
326
+ {
327
+ if (ctx->loop_status != XMPP_LOOP_NOTSTARTED) return;
328
+
329
+ ctx->loop_status = XMPP_LOOP_RUNNING;
330
+ while (ctx->loop_status == XMPP_LOOP_RUNNING) {
331
+ xmpp_run_once(ctx, DEFAULT_TIMEOUT);
332
+ }
333
+
334
+ xmpp_debug(ctx, "event", "Event loop completed.");
335
+ }
336
+
337
+ /** Stop the event loop.
338
+ * This will stop the event loop after the current iteration and cause
339
+ * xmpp_run to exit.
340
+ *
341
+ * @param ctx a Strophe context object
342
+ *
343
+ * @ingroup EventLoop
344
+ */
345
+ void xmpp_stop(xmpp_ctx_t *ctx)
346
+ {
347
+ xmpp_debug(ctx, "event", "Stopping event loop.");
348
+
349
+ if (ctx->loop_status == XMPP_LOOP_RUNNING)
350
+ ctx->loop_status = XMPP_LOOP_QUIT;
351
+ }
data/ext/extconf.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'mkmf'
2
+ have_library("expat")
3
+ have_library("resolv")
4
+ $CFLAGS = "#{ENV['CFLAGS']} -Wall -O3 -g"
5
+ if CONFIG["MAJOR"].to_i >= 1 && CONFIG["MINOR"].to_i >= 8
6
+ $CFLAGS << " -DHAVE_DEFINE_ALLOC_FUNCTION"
7
+ end
8
+ create_makefile('strophe_ruby')