comparison libpurple/protocols/jabber/bosh.c @ 25184:1ce32c6752fc

Make the Request ID a long long (fit in 2^53-1) Standardize the BOSHConnection receive callbacks and drop two prototypes Some annoying whitespace changes snuck in there, too. Sorry.
author Paul Aurich <paul@darkrain42.org>
date Fri, 05 Dec 2008 06:51:39 +0000
parents 62d9bce9ff74
children 592c2cf00fea
comparison
equal deleted inserted replaced
25183:62d9bce9ff74 25184:1ce32c6752fc
43 struct _PurpleBOSHConnection { 43 struct _PurpleBOSHConnection {
44 /* decoded URL */ 44 /* decoded URL */
45 char *host; 45 char *host;
46 int port; 46 int port;
47 char *path; 47 char *path;
48 48
49 int rid; 49 /* Must be big enough to hold 2^53 - 1 */
50 guint64 rid;
50 char *sid; 51 char *sid;
51 int wait; 52 int wait;
52 53
53 JabberStream *js; 54 JabberStream *js;
54 gboolean pipelining; 55 gboolean pipelining;
55 PurpleHTTPConnection *conn_a; 56 PurpleHTTPConnection *conn_a;
56 PurpleHTTPConnection *conn_b; 57 PurpleHTTPConnection *conn_b;
57 58
58 gboolean ready; 59 gboolean ready;
59 PurpleBOSHConnectionConnectFunction connect_cb; 60 PurpleBOSHConnectionConnectFunction connect_cb;
60 PurpleBOSHConnectionReceiveFunction receive_cb; 61 PurpleBOSHConnectionReceiveFunction receive_cb;
61 }; 62 };
62 63
95 }; 96 };
96 97
97 static void jabber_bosh_connection_stream_restart(PurpleBOSHConnection *conn); 98 static void jabber_bosh_connection_stream_restart(PurpleBOSHConnection *conn);
98 static gboolean jabber_bosh_connection_error_check(PurpleBOSHConnection *conn, xmlnode *node); 99 static gboolean jabber_bosh_connection_error_check(PurpleBOSHConnection *conn, xmlnode *node);
99 static void jabber_bosh_connection_received(PurpleBOSHConnection *conn, xmlnode *node); 100 static void jabber_bosh_connection_received(PurpleBOSHConnection *conn, xmlnode *node);
100 static void jabber_bosh_connection_auth_response(PurpleBOSHConnection *conn, xmlnode *node);
101 static void jabber_bosh_connection_boot_response(PurpleBOSHConnection *conn, xmlnode *node);
102 static void jabber_bosh_connection_http_received_cb(PurpleHTTPRequest *req, PurpleHTTPResponse *res, void *userdata); 101 static void jabber_bosh_connection_http_received_cb(PurpleHTTPRequest *req, PurpleHTTPResponse *res, void *userdata);
103 static void jabber_bosh_connection_send_native(PurpleBOSHConnection *conn, xmlnode *node); 102 static void jabber_bosh_connection_send_native(PurpleBOSHConnection *conn, xmlnode *node);
104 103
105 static void jabber_bosh_http_connection_connect(PurpleHTTPConnection *conn); 104 static void jabber_bosh_http_connection_connect(PurpleHTTPConnection *conn);
106 static void jabber_bosh_http_connection_send_request(PurpleHTTPConnection *conn, PurpleHTTPRequest *req); 105 static void jabber_bosh_http_connection_send_request(PurpleHTTPConnection *conn, PurpleHTTPRequest *req);
251 } 250 }
252 251
253 void jabber_bosh_connection_close(PurpleBOSHConnection *conn) 252 void jabber_bosh_connection_close(PurpleBOSHConnection *conn)
254 { 253 {
255 xmlnode *packet = xmlnode_new("body"); 254 xmlnode *packet = xmlnode_new("body");
256 char *tmp; 255 /* XEP-0124: The rid must not exceed 16 characters */
257 256 char rid[17];
257 g_snprintf(rid, sizeof(rid), "%" G_GUINT64_FORMAT, ++conn->rid);
258 xmlnode_set_attrib(packet, "type", "terminate"); 258 xmlnode_set_attrib(packet, "type", "terminate");
259 xmlnode_set_attrib(packet, "xmlns", "http://jabber.org/protocol/httpbind"); 259 xmlnode_set_attrib(packet, "xmlns", "http://jabber.org/protocol/httpbind");
260 xmlnode_set_attrib(packet, "sid", conn->sid); 260 xmlnode_set_attrib(packet, "sid", conn->sid);
261 tmp = g_strdup_printf("%d", ++conn->rid); 261 xmlnode_set_attrib(packet, "rid", rid);
262 xmlnode_set_attrib(packet, "rid", tmp);
263 g_free(tmp);
264 262
265 jabber_bosh_connection_send_native(conn, packet); 263 jabber_bosh_connection_send_native(conn, packet);
266 xmlnode_free(packet); 264 xmlnode_free(packet);
267 } 265 }
268 266
269 static void jabber_bosh_connection_stream_restart(PurpleBOSHConnection *conn) { 267 static void jabber_bosh_connection_stream_restart(PurpleBOSHConnection *conn) {
270 xmlnode *restart = xmlnode_new("body"); 268 xmlnode *restart = xmlnode_new("body");
271 char *tmp = NULL; 269 /* XEP-0124: The rid must not exceed 16 characters */
272 conn->rid++; 270 char rid[17];
273 xmlnode_set_attrib(restart, "rid", tmp = g_strdup_printf("%d", conn->rid)); 271 g_snprintf(rid, sizeof(rid), "%" G_GUINT64_FORMAT, ++conn->rid);
274 g_free(tmp); 272 xmlnode_set_attrib(restart, "rid", rid);
275 xmlnode_set_attrib(restart, "sid", conn->sid); 273 xmlnode_set_attrib(restart, "sid", conn->sid);
276 xmlnode_set_attrib(restart, "to", conn->js->user->domain); 274 xmlnode_set_attrib(restart, "to", conn->js->user->domain);
277 xmlnode_set_attrib(restart, "xml:lang", "en"); 275 xmlnode_set_attrib(restart, "xml:lang", "en");
278 xmlnode_set_attrib(restart, "xmpp:restart", "true"); 276 xmlnode_set_attrib(restart, "xmpp:restart", "true");
279 xmlnode_set_attrib(restart, "xmlns", "http://jabber.org/protocol/httpbind"); 277 xmlnode_set_attrib(restart, "xmlns", "http://jabber.org/protocol/httpbind");
283 xmlnode_free(restart); 281 xmlnode_free(restart);
284 } 282 }
285 283
286 static gboolean jabber_bosh_connection_error_check(PurpleBOSHConnection *conn, xmlnode *node) { 284 static gboolean jabber_bosh_connection_error_check(PurpleBOSHConnection *conn, xmlnode *node) {
287 const char *type; 285 const char *type;
288 286
289 if (!node) return FALSE;
290 type = xmlnode_get_attrib(node, "type"); 287 type = xmlnode_get_attrib(node, "type");
291 288
292 if (type != NULL && !strcmp(type, "terminate")) { 289 if (type != NULL && !strcmp(type, "terminate")) {
293 conn->ready = FALSE; 290 conn->ready = FALSE;
294 purple_connection_error_reason (conn->js->gc, 291 purple_connection_error_reason (conn->js->gc,
300 } 297 }
301 298
302 static void jabber_bosh_connection_received(PurpleBOSHConnection *conn, xmlnode *node) { 299 static void jabber_bosh_connection_received(PurpleBOSHConnection *conn, xmlnode *node) {
303 xmlnode *child; 300 xmlnode *child;
304 JabberStream *js = conn->js; 301 JabberStream *js = conn->js;
305 302
306 if (node == NULL) return; 303 g_return_if_fail(node == NULL);
307 304 if (jabber_bosh_connection_error_check(conn, node))
308 if (jabber_bosh_connection_error_check(conn, node) == TRUE) return; 305 return;
309 306
310 child = node->child; 307 child = node->child;
311 while (child != 0) { 308 while (child != NULL) {
312 if (child->type == XMLNODE_TYPE_TAG) { 309 if (child->type == XMLNODE_TYPE_TAG) {
313 xmlnode *session = NULL; 310 xmlnode *session = NULL;
314 if (!strcmp(child->name, "iq")) session = xmlnode_get_child(child, "session"); 311 if (!strcmp(child->name, "iq")) session = xmlnode_get_child(child, "session");
315 if (session) { 312 if (session) {
316 conn->ready = TRUE; 313 conn->ready = TRUE;
319 } 316 }
320 child = child->next; 317 child = child->next;
321 } 318 }
322 } 319 }
323 320
324 static void jabber_bosh_connection_auth_response(PurpleBOSHConnection *conn, xmlnode *node) { 321 static void auth_response_cb(PurpleBOSHConnection *conn, xmlnode *node) {
325 xmlnode *child = node->child; 322 xmlnode *child;
326 323
327 if (jabber_bosh_connection_error_check(conn, node) == TRUE) return; 324 g_return_if_fail(node == NULL);
328 325 if (jabber_bosh_connection_error_check(conn, node))
326 return;
327
328 child = node->child;
329 while(child != NULL && child->type != XMLNODE_TYPE_TAG) { 329 while(child != NULL && child->type != XMLNODE_TYPE_TAG) {
330 child = child->next; 330 child = child->next;
331 } 331 }
332 332
333 /* We're only expecting one XML node here, so only process the first one */
333 if (child != NULL && child->type == XMLNODE_TYPE_TAG) { 334 if (child != NULL && child->type == XMLNODE_TYPE_TAG) {
334 JabberStream *js = conn->js; 335 JabberStream *js = conn->js;
335 if (!strcmp(child->name, "success")) { 336 if (!strcmp(child->name, "success")) {
336 jabber_bosh_connection_stream_restart(conn); 337 jabber_bosh_connection_stream_restart(conn);
337 jabber_process_packet(js, &child); 338 jabber_process_packet(js, &child);
338 conn->receive_cb = jabber_bosh_connection_received; 339 conn->receive_cb = jabber_bosh_connection_received;
339 } else { 340 } else {
340 js->state = JABBER_STREAM_AUTHENTICATING; 341 js->state = JABBER_STREAM_AUTHENTICATING;
341 jabber_process_packet(js, &child); 342 jabber_process_packet(js, &child);
342 } 343 }
343 } else printf("\n!! no child!!\n");
344 }
345
346 static void jabber_bosh_connection_boot_response(PurpleBOSHConnection *conn, xmlnode *node) {
347 char *version;
348
349 if (jabber_bosh_connection_error_check(conn, node) == TRUE) return;
350
351 if (xmlnode_get_attrib(node, "sid")) {
352 conn->sid = g_strdup(xmlnode_get_attrib(node, "sid"));
353 } else { 344 } else {
354 purple_debug_info("jabber", "Connection manager doesn't behave BOSH-like!\n"); 345 purple_debug_warning("jabber", "Received unexepcted empty BOSH packet.\n");
355 } 346 }
356 347 }
357 if ((version = g_strdup(xmlnode_get_attrib(node, "ver")))) { 348
358 char *dot = strstr(version, "."); 349 static void boot_response_cb(PurpleBOSHConnection *conn, xmlnode *node) {
350 const char *sid, *version;
351
352 g_return_if_fail(node != NULL);
353 if (jabber_bosh_connection_error_check(conn, node))
354 return;
355
356 sid = xmlnode_get_attrib(node, "sid");
357 version = xmlnode_get_attrib(node, "ver");
358
359 if (sid) {
360 conn->sid = g_strdup(sid);
361 } else {
362 purple_connection_error_reason(conn->js->gc,
363 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
364 _("No session ID given"));
365 return;
366 }
367
368 if (version) {
369 const char *dot = strstr(version, ".");
359 int major = atoi(version); 370 int major = atoi(version);
360 int minor = atoi(dot + 1); 371 int minor = atoi(dot + 1);
361 372
362 purple_debug_info("jabber", "BOSH connection manager version %s\n", version); 373 purple_debug_info("jabber", "BOSH connection manager version %s\n", version);
363 374
375 /* TODO: Are major increments incompatible? */
364 if (major > 1 || (major == 1 && minor >= 6)) { 376 if (major > 1 || (major == 1 && minor >= 6)) {
365 xmlnode *packet = xmlnode_get_child(node, "features"); 377 xmlnode *packet = xmlnode_get_child(node, "features");
366 conn->js->use_bosh = TRUE; 378 conn->js->use_bosh = TRUE;
367 conn->receive_cb = jabber_bosh_connection_auth_response; 379 conn->receive_cb = auth_response_cb;
368 jabber_stream_features_parse(conn->js, packet); 380 jabber_stream_features_parse(conn->js, packet);
369 } else { 381 } else {
370 purple_connection_error_reason(conn->js->gc, 382 purple_connection_error_reason(conn->js->gc,
371 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, 383 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
372 _("Unsupported version of BOSH protocol")); 384 _("Unsupported version of BOSH protocol"));
373 } 385 }
374
375 g_free(version);
376 } else { 386 } else {
377 purple_debug_info("jabber", "Missing version in BOSH initiation\n"); 387 purple_debug_info("jabber", "Missing version in BOSH initiation\n");
378 } 388 }
379 } 389 }
380 390
381 static void jabber_bosh_connection_boot(PurpleBOSHConnection *conn) { 391 static void jabber_bosh_connection_boot(PurpleBOSHConnection *conn) {
382 char *tmp;
383 xmlnode *init = xmlnode_new("body"); 392 xmlnode *init = xmlnode_new("body");
393 /* XEP-0124: The rid must not exceed 16 characters */
394 char rid[17];
395 g_snprintf(rid, sizeof(rid), "%" G_GUINT64_FORMAT, ++conn->rid);
384 xmlnode_set_attrib(init, "content", "text/xml; charset=utf-8"); 396 xmlnode_set_attrib(init, "content", "text/xml; charset=utf-8");
385 xmlnode_set_attrib(init, "secure", "true"); 397 xmlnode_set_attrib(init, "secure", "true");
386 //xmlnode_set_attrib(init, "route", tmp = g_strdup_printf("xmpp:%s:5222", conn->js->user->domain)); 398 /*
387 //g_free(tmp); 399 xmlnode_set_attrib(init, "route", tmp = g_strdup_printf("xmpp:%s:5222", conn->js->user->domain));
400 g_free(tmp);
401 */
388 xmlnode_set_attrib(init, "to", conn->js->user->domain); 402 xmlnode_set_attrib(init, "to", conn->js->user->domain);
389 xmlnode_set_attrib(init, "xml:lang", "en"); 403 xmlnode_set_attrib(init, "xml:lang", "en");
390 xmlnode_set_attrib(init, "xmpp:version", "1.0"); 404 xmlnode_set_attrib(init, "xmpp:version", "1.0");
391 xmlnode_set_attrib(init, "ver", "1.6"); 405 xmlnode_set_attrib(init, "ver", "1.6");
392 xmlnode_set_attrib(init, "xmlns:xmpp", "urn:xmpp:xbosh"); 406 xmlnode_set_attrib(init, "xmlns:xmpp", "urn:xmpp:xbosh");
393 xmlnode_set_attrib(init, "rid", tmp = g_strdup_printf("%d", conn->rid)); 407 xmlnode_set_attrib(init, "rid", rid);
394 g_free(tmp);
395 xmlnode_set_attrib(init, "wait", "60"); /* this should be adjusted automatically according to real time network behavior */ 408 xmlnode_set_attrib(init, "wait", "60"); /* this should be adjusted automatically according to real time network behavior */
396 xmlnode_set_attrib(init, "xmlns", "http://jabber.org/protocol/httpbind"); 409 xmlnode_set_attrib(init, "xmlns", "http://jabber.org/protocol/httpbind");
397 xmlnode_set_attrib(init, "hold", "1"); 410 xmlnode_set_attrib(init, "hold", "1");
398 411
399 conn->receive_cb = jabber_bosh_connection_boot_response; 412 conn->receive_cb = boot_response_cb;
400 jabber_bosh_connection_send_native(conn, init); 413 jabber_bosh_connection_send_native(conn, init);
401 xmlnode_free(init); 414 xmlnode_free(init);
402 } 415 }
403 416
404 static void jabber_bosh_connection_http_received_cb(PurpleHTTPRequest *req, PurpleHTTPResponse *res, void *userdata) { 417 static void jabber_bosh_connection_http_received_cb(PurpleHTTPRequest *req, PurpleHTTPResponse *res, void *userdata) {
417 } else purple_debug_info("jabber", "missing receive_cb of PurpleBOSHConnection.\n"); 430 } else purple_debug_info("jabber", "missing receive_cb of PurpleBOSHConnection.\n");
418 } 431 }
419 432
420 void jabber_bosh_connection_send(PurpleBOSHConnection *conn, xmlnode *node) { 433 void jabber_bosh_connection_send(PurpleBOSHConnection *conn, xmlnode *node) {
421 xmlnode *packet = xmlnode_new("body"); 434 xmlnode *packet = xmlnode_new("body");
422 char *tmp; 435 /* XEP-0124: The rid must not exceed 16 characters */
423 conn->rid++; 436 char rid[17];
437 g_snprintf(rid, sizeof(rid), "%" G_GUINT64_FORMAT, ++conn->rid);
424 xmlnode_set_attrib(packet, "xmlns", "http://jabber.org/protocol/httpbind"); 438 xmlnode_set_attrib(packet, "xmlns", "http://jabber.org/protocol/httpbind");
425 xmlnode_set_attrib(packet, "sid", conn->sid); 439 xmlnode_set_attrib(packet, "sid", conn->sid);
426 tmp = g_strdup_printf("%d", conn->rid); 440 xmlnode_set_attrib(packet, "rid", rid);
427 xmlnode_set_attrib(packet, "rid", tmp);
428 g_free(tmp);
429 441
430 if (node) { 442 if (node) {
431 xmlnode_insert_child(packet, node); 443 xmlnode_insert_child(packet, node);
432 if (conn->ready == TRUE) xmlnode_set_attrib(node, "xmlns", "jabber:client"); 444 if (conn->ready == TRUE) xmlnode_set_attrib(node, "xmlns", "jabber:client");
433 } 445 }