Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 399 lines (329 sloc) 10.043 kb
f27235b @rillian Move the xmpp library to its new name.
rillian authored
1 /* conn.c
2 ** connection object functions
3 **
4 ** Copyright 2005 OGG, LLC
5 */
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10
11 #include "xmpp.h"
12 #include "common.h"
13
14 #define DEFAULT_SEND_QUEUE_MAX 64
15 #define DISCONNECT_TIMEOUT 2000 /* 2 seconds */
16
17 static int _disconnect_cleanup(xmpp_conn_t * const conn,
18 void * const userdata);
19
20
21 xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t * const ctx)
22 {
23 xmpp_conn_t *conn = NULL;
24 xmpp_connlist_t *tail, *item;
25
26 if (ctx == NULL) return NULL;
27 conn = xmpp_alloc(ctx, sizeof(xmpp_conn_t));
28
29 if (conn != NULL) {
30 conn->ctx = ctx;
31
32 conn->type = XMPP_UNKNOWN;
33 conn->sock = -1;
34 conn->error = 0;
35 conn->stream_error = NULL;
36
37 /* default send parameters */
38 conn->blocking_send = 0;
39 conn->send_queue_max = DEFAULT_SEND_QUEUE_MAX;
40 conn->send_queue_len = 0;
41 conn->send_queue_head = NULL;
42 conn->send_queue_tail = NULL;
43
44 conn->lang = xmpp_strdup(conn->ctx, "en");
45 if (!conn->lang) {
46 xmpp_free(conn->ctx, conn);
47 return NULL;
48 }
49 conn->domain = NULL;
50 conn->jid = NULL;
51 conn->pass = NULL;
52 conn->stream_id = NULL;
53
54 conn->tls_support = 0;
55 conn->sasl_support = 0;
56
57 conn->parser = NULL;
58 conn->stanza = NULL;
59 parser_prepare_reset(conn, auth_handle_open);
60
61 conn->authenticated = 0;
62 conn->conn_handler = NULL;
63 conn->userdata = NULL;
64 conn->timed_handlers = NULL;
65 /* we own (and will free) the hash values */
66 conn->id_handlers = hash_new(conn->ctx, 32, NULL);
67 conn->handlers = NULL;
68
69 /* give the caller a reference to connection */
70 conn->ref = 1;
71
72 /* add connection to ctx->connlist */
73 tail = conn->ctx->connlist;
74 while (tail && tail->next) tail = tail->next;
75
76 item = xmpp_alloc(conn->ctx, sizeof(xmpp_connlist_t));
77 if (!item) {
78 xmpp_error(conn->ctx, "xmpp", "failed to allocate memory");
79 xmpp_free(conn->ctx, conn->lang);
80 XML_ParserFree(conn->parser);
81 xmpp_free(conn->ctx, conn);
82 conn = NULL;
83 } else {
84 item->conn = conn;
85 item->next = NULL;
86
87 if (tail) tail->next = item;
88 else conn->ctx->connlist = item;
89 }
90 }
91
92 return conn;
93 }
94
95 xmpp_conn_t * xmpp_conn_clone(xmpp_conn_t * const conn)
96 {
97 conn->ref++;
98 return conn;
99 }
100
101 void xmpp_conn_release(xmpp_conn_t * const conn)
102 {
103 xmpp_ctx_t *ctx;
104 xmpp_connlist_t *item, *prev;
105 xmpp_handlist_t *hlitem, *thli;
106 hash_iterator_t *iter;
107 const char *key;
108
109 if (conn->ref > 1)
110 conn->ref--;
111 else {
112 ctx = conn->ctx;
113
114 /* remove connection from context's connlist */
115 if (ctx->connlist->conn == conn) {
116 item = ctx->connlist;
117 ctx->connlist = item->next;
118 xmpp_free(ctx, item);
119 } else {
120 prev = NULL;
121 item = ctx->connlist;
122 while (item && item->conn != conn) {
123 prev = item;
124 item = item->next;
125 }
126
127 if (!item) {
128 xmpp_error(ctx, "xmpp", "Connection not in context's list\n");
129 } else {
130 prev->next = item->next;
131 xmpp_free(ctx, item);
132 }
133 }
134
135 /* free handler stuff
136 * note that userdata is the responsibility of the client
137 * and the handler pointers don't need to be freed since they
138 * are pointers to functions */
139
140 hlitem = conn->timed_handlers;
141 while (hlitem) {
142 thli = hlitem;
143 hlitem = hlitem->next;
144
145 xmpp_free(ctx, thli);
146 }
147
148 /* id handlers
149 * we have to traverse the hash table freeing list elements
150 * then release the hash table */
151 iter = hash_iter_new(conn->id_handlers);
152 while ((key = hash_iter_next(iter))) {
153 hlitem = (xmpp_handlist_t *)hash_get(conn->id_handlers, key);
154 while (hlitem) {
155 thli = hlitem;
156 hlitem = hlitem->next;
157 xmpp_free(conn->ctx, thli->id);
158 xmpp_free(conn->ctx, thli);
159 }
160 }
161 hash_iter_release(iter);
162 hash_release(conn->id_handlers);
163
164 hlitem = conn->handlers;
165 while (hlitem) {
166 thli = hlitem;
167 hlitem = hlitem->next;
168
169 if (hlitem->ns) xmpp_free(ctx, hlitem->ns);
170 if (hlitem->name) xmpp_free(ctx, hlitem->name);
171 if (hlitem->type) xmpp_free(ctx, hlitem->type);
172 xmpp_free(ctx, thli);
173 }
174
175 if (conn->stream_error) {
176 xmpp_stanza_release(conn->stream_error->stanza);
177 xmpp_free(ctx, conn->stream_error);
178 }
179
180 XML_ParserFree(conn->parser);
181
182 if (conn->domain) xmpp_free(ctx, conn->domain);
183 if (conn->jid) xmpp_free(ctx, conn->jid);
184 if (conn->pass) xmpp_free(ctx, conn->pass);
185 if (conn->stream_id) xmpp_free(ctx, conn->stream_id);
186 xmpp_free(ctx, conn);
187 }
188 }
189
190 const char *xmpp_conn_get_jid(const xmpp_conn_t * const conn)
191 {
192 return conn->jid;
193 }
194
195 /* set the jid of the user or the component name. in the first case,
196 * this can be a full jid, or a bare jid. in the second case, this will
197 * probably be a domain only.
198 */
199 void xmpp_conn_set_jid(xmpp_conn_t * const conn, const char * const jid)
200 {
201 if (conn->jid) xmpp_free(conn->ctx, conn->jid);
202 conn->jid = xmpp_strdup(conn->ctx, jid);
203 }
204
205 const char *xmpp_conn_get_pass(const xmpp_conn_t * const conn)
206 {
207 return conn->pass;
208 }
209
210 void xmpp_conn_set_pass(xmpp_conn_t * const conn, const char * const pass)
211 {
212 if (conn->pass) xmpp_free(conn->ctx, conn->pass);
213 conn->pass = xmpp_strdup(conn->ctx, pass);
214 }
215
216 int xmpp_connect_client(xmpp_conn_t * const conn,
217 const char * const domain,
218 xmpp_conn_handler callback,
219 void * const userdata)
220 {
221 conn->type = XMPP_CLIENT;
222
223 conn->domain = xmpp_strdup(conn->ctx, domain);
224 if (!conn->domain) return -1;
225
226 conn->sock = sock_connect(domain, 5222);
227 if (conn->sock < 0) return -1;
228
229 /* setup handler */
230 conn->conn_handler = callback;
231 conn->userdata = userdata;
232
233 /* FIXME: it could happen that the connect returns immediately as
234 * successful, though this is pretty unlikely. This would be a little
235 * hard to fix, since we'd have to detect and fire off the callback
236 * from within the event loop */
237
238 conn->state = XMPP_STATE_CONNECTING;
239 xmpp_debug(conn->ctx, "xmpp", "attempting to connect to %s", domain);
240
241 return 0;
242 }
243
244 /* this function is only called by the end tag handler. it is
245 * the only place where a conn_disconnect would be called during a clean
246 * disconnect sequence */
247 void conn_disconnect_clean(xmpp_conn_t * const conn)
248 {
249 /* remove the timed handler */
250 xmpp_timed_handler_delete(conn, _disconnect_cleanup);
251
252 conn_disconnect(conn);
253 }
254
255 void conn_disconnect(xmpp_conn_t * const conn)
256 {
257 xmpp_info(conn->ctx, "xmpp", "Disconnected from server");
258 conn->state = XMPP_STATE_DISCONNECTED;
259 sock_close(conn->sock);
260
261 /* fire off connection handler */
262 conn->conn_handler(conn, XMPP_CONN_DISCONNECT, conn->error,
263 conn->stream_error, conn->userdata);
264 }
265
266 /* timed handler for cleanup if normal disconnect procedure takes too long */
267 static int _disconnect_cleanup(xmpp_conn_t * const conn,
268 void * const userdata)
269 {
270 xmpp_debug(conn->ctx, "xmpp",
271 "disconnection forced by cleanup timeout");
272
273 conn_disconnect(conn);
274
275 return 0;
276 }
277
278 /* terminates the XMPP stream, closing the underlying socket,
279 * and calls the conn_handler. this function returns immediately
280 * without calling the handler if the connection is not active */
281 void xmpp_disconnect(xmpp_conn_t * const conn)
282 {
283 if (conn->state != XMPP_STATE_CONNECTING &&
284 conn->state != XMPP_STATE_CONNECTED)
285 return;
286
287 /* close the stream */
288 xmpp_send_raw_string(conn, "</stream:stream>");
289
290 /* setup timed handler in case disconnect takes too long */
291 handler_add_timed(conn, _disconnect_cleanup,
292 DISCONNECT_TIMEOUT, NULL);
293 }
294
295 /* convinience function for sending data to a connection */
296 void xmpp_send_raw_string(xmpp_conn_t * const conn,
297 const char * const fmt, ...)
298 {
299 va_list ap;
300 size_t len;
301 char buf[1024]; /* small buffer for common case */
302 char *bigbuf;
303
304 va_start(ap, fmt);
305 len = vsnprintf(buf, 1024, fmt, ap);
306 va_end(ap);
307
308 if (len >= 1024) {
309 /* we need more space for this data, so we allocate a big
310 * enough buffer and print to that */
311 len++; /* account for trailing \0 */
312 bigbuf = xmpp_alloc(conn->ctx, len);
313 if (!bigbuf) {
314 xmpp_debug(conn->ctx, "xmpp", "Could not allocate memory for send_raw_string");
315 return;
316 }
317 va_start(ap, fmt);
318 vsnprintf(bigbuf, len, fmt, ap);
319 va_end(ap);
320
321 xmpp_debug(conn->ctx, "conn", "SENT: %s", bigbuf);
322
323 /* len - 1 so we don't send trailing \0 */
324 xmpp_send_raw(conn, bigbuf, len - 1);
325
326 xmpp_free(conn->ctx, bigbuf);
327 } else {
328 xmpp_debug(conn->ctx, "conn", "SENT: %s", buf);
329
330 xmpp_send_raw(conn, buf, len);
331 }
332 }
333
334 /* adds data to the send queue for a connection */
335 void xmpp_send_raw(xmpp_conn_t * const conn,
336 const char * const data, const size_t len)
337 {
338 xmpp_send_queue_t *item;
339
340 if (conn->state != XMPP_STATE_CONNECTED) return;
341
342 /* create send queue item for queue */
343 item = xmpp_alloc(conn->ctx, sizeof(xmpp_send_queue_t));
344 if (!item) return;
345
346 item->data = xmpp_alloc(conn->ctx, len);
347 if (!item->data) {
348 xmpp_free(conn->ctx, item);
349 return;
350 }
351 memcpy(item->data, data, len);
352 item->len = len;
353 item->next = NULL;
354 item->written = 0;
355
356 /* add item to the send queue */
357 if (!conn->send_queue_tail) {
358 /* first item, set head and tail */
359 conn->send_queue_head = item;
360 conn->send_queue_tail = item;
361 } else {
362 /* add to the tail */
363 conn->send_queue_tail->next = item;
364 conn->send_queue_tail = item;
365 }
366 conn->send_queue_len++;
367 }
368
369 void xmpp_send(xmpp_conn_t * const conn,
370 xmpp_stanza_t * const stanza)
371 {
372 char *buf;
373 size_t len;
374 int ret;
375
376 if (conn->state == XMPP_STATE_CONNECTED) {
377 if ((ret = xmpp_stanza_to_text(stanza, &buf, &len)) == 0) {
378 xmpp_send_raw(conn, buf, len);
379 xmpp_debug(conn->ctx, "xmpp", "SENT: %s", buf);
380 xmpp_free(conn->ctx, buf);
381 }
382 }
383 }
384
385 void conn_open_stream(xmpp_conn_t * const conn)
386 {
387 xmpp_send_raw_string(conn,
388 "<?xml version=\"1.0\"?>" \
389 "<stream:stream to=\"%s\" " \
390 "xml:lang=\"%s\" " \
391 "version=\"1.0\" " \
392 "xmlns=\"%s\" " \
393 "xmlns:stream=\"%s\">",
394 conn->domain,
395 conn->lang,
396 conn->type == XMPP_CLIENT ? XMPP_NS_CLIENT : XMPP_NS_COMPONENT,
397 XMPP_NS_STREAMS);
398 }
Something went wrong with that request. Please try again.