libpurple/protocols/jabber/bosh.c

branch
cpw.darkrain42.xmpp.bosh
changeset 24872
68de61b562f6
parent 23625
8d5884281cfb
child 24873
7d1e91d5ec46
equal deleted inserted replaced
24871:60f1695a34e8 24872:68de61b562f6
19 * 19 *
20 */ 20 */
21 #include "internal.h" 21 #include "internal.h"
22 #include "cipher.h" 22 #include "cipher.h"
23 #include "debug.h" 23 #include "debug.h"
24 #include "imgstore.h"
25 #include "prpl.h" 24 #include "prpl.h"
26 #include "notify.h"
27 #include "request.h"
28 #include "util.h" 25 #include "util.h"
29 #include "xmlnode.h" 26 #include "xmlnode.h"
30 27
31 #include "buddy.h" 28 #include "bosh.h"
32 #include "chat.h" 29
33 #include "jabber.h" 30 typedef struct _PurpleHTTPRequest PurpleHTTPRequest;
34 #include "iq.h" 31 typedef struct _PurpleHTTPResponse PurpleHTTPResponse;
35 #include "presence.h" 32 typedef struct _PurpleHTTPConnection PurpleHTTPConnection;
36 #include "xdata.h" 33
37 #include "pep.h" 34 typedef void (*PurpleHTTPConnectionConnectFunction)(PurpleHTTPConnection *conn);
38 #include "adhoccommands.h" 35 typedef void (*PurpleHTTPConnectionDisconnectFunction)(PurpleHTTPConnection *conn);
39 #include "connection.h" 36 typedef void (*PurpleHTTPRequestCallback)(PurpleHTTPRequest *req, PurpleHTTPResponse *res, void *userdata);
40 37 typedef void (*PurpleBOSHConnectionConnectFunction)(PurpleBOSHConnection *conn);
41 void jabber_bosh_connection_init(PurpleBOSHConnection *conn, PurpleAccount *account, JabberStream *js, char *url) { 38 typedef void (*PurpleBOSHConnectionReceiveFunction)(PurpleBOSHConnection *conn, xmlnode *node);
39
40 struct _PurpleBOSHConnection {
41 /* decoded URL */
42 char *host;
43 int port;
44 char *path;
45 char *user;
46 char *passwd;
47
48 int rid;
49 char *sid;
50 int wait;
51
52 JabberStream *js;
53 PurpleAccount *account;
54 gboolean pipelining;
55 PurpleHTTPConnection *conn_a;
56 PurpleHTTPConnection *conn_b;
57
58 gboolean ready;
59 PurpleBOSHConnectionConnectFunction connect_cb;
60 PurpleBOSHConnectionReceiveFunction receive_cb;
61 };
62
63 struct _PurpleHTTPConnection {
64 int fd;
65 char *host;
66 int port;
67 int handle;
68 int ie_handle;
69 PurpleConnection *conn;
70 GQueue *requests;
71
72 PurpleHTTPResponse *current_response;
73 char *current_data;
74 int current_len;
75
76 int pih;
77 PurpleHTTPConnectionConnectFunction connect_cb;
78 PurpleHTTPConnectionConnectFunction disconnect_cb;
79 void *userdata;
80 };
81
82 struct _PurpleHTTPRequest {
83 PurpleHTTPRequestCallback cb;
84 char *method;
85 char *path;
86 GHashTable *header;
87 char *data;
88 int data_len;
89 void *userdata;
90 };
91
92 struct _PurpleHTTPResponse {
93 int status;
94 GHashTable *header;
95 char *data;
96 int data_len;
97 };
98
99 static void jabber_bosh_connection_stream_restart(PurpleBOSHConnection *conn);
100 static gboolean jabber_bosh_connection_error_check(PurpleBOSHConnection *conn, xmlnode *node);
101 static void jabber_bosh_connection_received(PurpleBOSHConnection *conn, xmlnode *node);
102 static void jabber_bosh_connection_auth_response(PurpleBOSHConnection *conn, xmlnode *node);
103 static void jabber_bosh_connection_boot_response(PurpleBOSHConnection *conn, xmlnode *node);
104 static void jabber_bosh_connection_http_received_cb(PurpleHTTPRequest *req, PurpleHTTPResponse *res, void *userdata);
105 static void jabber_bosh_connection_send_native(PurpleBOSHConnection *conn, xmlnode *node);
106
107 static void jabber_bosh_http_connection_receive_parse_header(PurpleHTTPResponse *response, char **data, int *len);
108 static PurpleHTTPConnection* jabber_bosh_http_connection_init(const char *host, int port);
109 static void jabber_bosh_http_connection_connect(PurpleHTTPConnection *conn);
110 static void jabber_bosh_http_connection_send_request(PurpleHTTPConnection *conn, PurpleHTTPRequest *req);
111 static void jabber_bosh_http_connection_clean(PurpleHTTPConnection *conn);
112
113 static void jabber_bosh_http_request_init(PurpleHTTPRequest *req, const char *method, const char *path, PurpleHTTPRequestCallback cb, void *userdata);
114 static void jabber_bosh_http_request_add_to_header(PurpleHTTPRequest *req, const char *field, const char *value);
115 static void jabber_bosh_http_request_set_data(PurpleHTTPRequest *req, char *data, int len);
116 static void jabber_bosh_http_request_clean(PurpleHTTPRequest *req);
117
118 static void jabber_bosh_http_response_init(PurpleHTTPResponse *res);
119 static void jabber_bosh_http_response_clean(PurpleHTTPResponse *res);
120
121 PurpleBOSHConnection* jabber_bosh_connection_init(JabberStream *js, const char *url) {
122 PurpleBOSHConnection *conn;
123 char *host, *path, *user, *passwd;
124 int port;
125
126 if (!purple_url_parse(url, &host, &port, &path, &user, &passwd)) {
127 purple_debug_info("jabber", "Unable to parse given URL.\n");
128 return NULL;
129 }
130
131 conn = g_new0(PurpleBOSHConnection, 1);
132 conn->host = host;
133 conn->port = port;
134 conn->path = path;
135 conn->user = user;
136 conn->passwd = passwd;
42 conn->pipelining = TRUE; 137 conn->pipelining = TRUE;
43 conn->account = account; 138
44 if (!purple_url_parse(url, &(conn->host), &(conn->port), &(conn->path), &(conn->user), &(conn->passwd))) {
45 purple_debug_info("jabber", "Unable to parse given URL.\n");
46 return;
47 }
48 if (conn->user || conn->passwd) { 139 if (conn->user || conn->passwd) {
49 purple_debug_info("jabber", "Sorry, HTTP Authentication isn't supported yet. Username and password in the BOSH URL will be ignored.\n"); 140 purple_debug_info("jabber", "Ignoring unsupported BOSH HTTP "
50 } 141 "Authentication username and password.\n");
142 }
143
51 conn->js = js; 144 conn->js = js;
52 conn->rid = rand() % 100000 + 1728679472; 145 conn->rid = rand() % 100000 + 1728679472;
53 conn->ready = FALSE; 146 conn->ready = FALSE;
54 conn->conn_a = g_new0(PurpleHTTPConnection, 1); 147 conn->conn_a = jabber_bosh_http_connection_init(conn->host, conn->port);
55 jabber_bosh_http_connection_init(conn->conn_a, conn->account, conn->host, conn->port);
56 conn->conn_a->userdata = conn; 148 conn->conn_a->userdata = conn;
57 } 149
58 150 return conn;
59 void jabber_bosh_connection_stream_restart(PurpleBOSHConnection *conn) { 151 }
152
153 static void jabber_bosh_connection_stream_restart(PurpleBOSHConnection *conn) {
60 xmlnode *restart = xmlnode_new("body"); 154 xmlnode *restart = xmlnode_new("body");
61 char *tmp = NULL; 155 char *tmp = NULL;
62 conn->rid++; 156 conn->rid++;
63 xmlnode_set_attrib(restart, "rid", tmp = g_strdup_printf("%d", conn->rid)); 157 xmlnode_set_attrib(restart, "rid", tmp = g_strdup_printf("%d", conn->rid));
64 g_free(tmp); 158 g_free(tmp);
70 xmlnode_set_attrib(restart, "xmlns:xmpp", "urn:xmpp:xbosh"); 164 xmlnode_set_attrib(restart, "xmlns:xmpp", "urn:xmpp:xbosh");
71 165
72 jabber_bosh_connection_send_native(conn, restart); 166 jabber_bosh_connection_send_native(conn, restart);
73 } 167 }
74 168
75 gboolean jabber_bosh_connection_error_check(PurpleBOSHConnection *conn, xmlnode *node) { 169 static gboolean jabber_bosh_connection_error_check(PurpleBOSHConnection *conn, xmlnode *node) {
76 char *type; 170 const char *type;
77 171
78 if (!node) return FALSE; 172 if (!node) return FALSE;
79 type = xmlnode_get_attrib(node, "type"); 173 type = xmlnode_get_attrib(node, "type");
80 174
81 if (type != NULL && !strcmp(type, "terminate")) { 175 if (type != NULL && !strcmp(type, "terminate")) {
86 return TRUE; 180 return TRUE;
87 } 181 }
88 return FALSE; 182 return FALSE;
89 } 183 }
90 184
91 void jabber_bosh_connection_received(PurpleBOSHConnection *conn, xmlnode *node) { 185 static void jabber_bosh_connection_received(PurpleBOSHConnection *conn, xmlnode *node) {
92 xmlnode *child; 186 xmlnode *child;
93 JabberStream *js = conn->js; 187 JabberStream *js = conn->js;
94 188
95 if (node == NULL) return; 189 if (node == NULL) return;
96 190
108 } 202 }
109 child = child->next; 203 child = child->next;
110 } 204 }
111 } 205 }
112 206
113 void jabber_bosh_connection_auth_response(PurpleBOSHConnection *conn, xmlnode *node) { 207 static void jabber_bosh_connection_auth_response(PurpleBOSHConnection *conn, xmlnode *node) {
114 xmlnode *child = node->child; 208 xmlnode *child = node->child;
115 209
116 if (jabber_bosh_connection_error_check(conn, node) == TRUE) return; 210 if (jabber_bosh_connection_error_check(conn, node) == TRUE) return;
117 211
118 while(child != NULL && child->type != XMLNODE_TYPE_TAG) { 212 while(child != NULL && child->type != XMLNODE_TYPE_TAG) {
130 jabber_process_packet(js, &child); 224 jabber_process_packet(js, &child);
131 } 225 }
132 } else printf("\n!! no child!!\n"); 226 } else printf("\n!! no child!!\n");
133 } 227 }
134 228
135 void jabber_bosh_connection_boot_response(PurpleBOSHConnection *conn, xmlnode *node) { 229 static void jabber_bosh_connection_boot_response(PurpleBOSHConnection *conn, xmlnode *node) {
136 char *version; 230 char *version;
137 231
138 if (jabber_bosh_connection_error_check(conn, node) == TRUE) return; 232 if (jabber_bosh_connection_error_check(conn, node) == TRUE) return;
139 233
140 if (xmlnode_get_attrib(node, "sid")) { 234 if (xmlnode_get_attrib(node, "sid")) {
141 conn->sid = g_strdup(xmlnode_get_attrib(node, "sid")); 235 conn->sid = g_strdup(xmlnode_get_attrib(node, "sid"));
142 } else { 236 } else {
143 purple_debug_info("jabber", "Connection manager doesn't behave BOSH-like!\n"); 237 purple_debug_info("jabber", "Connection manager doesn't behave BOSH-like!\n");
144 } 238 }
145 239
146 if ((version = xmlnode_get_attrib(node, "ver"))) { 240 if ((version = g_strdup(xmlnode_get_attrib(node, "ver")))) {
147 version[1] = 0; 241 char *dot = strstr(version, ".");
148 if (!(atoi(version) >= 1 && atoi(&version[2]) >= 6)) purple_debug_info("jabber", "Unsupported version of BOSH protocol. The connection manager must at least support version 1.6!\n"); 242 int major = atoi(version);
149 else { 243 int minor = atoi(dot + 1);
244
245 if (major > 1 || (major == 1 && minor >= 6)) {
150 xmlnode *packet = xmlnode_get_child(node, "features"); 246 xmlnode *packet = xmlnode_get_child(node, "features");
151 conn->js->use_bosh = TRUE; 247 conn->js->use_bosh = TRUE;
152 conn->receive_cb = jabber_bosh_connection_auth_response; 248 conn->receive_cb = jabber_bosh_connection_auth_response;
153 jabber_stream_features_parse(conn->js, packet); 249 jabber_stream_features_parse(conn->js, packet);
154 } 250 } else {
155 version[1] = '.'; 251 purple_debug_info("jabber", "Unsupported version of BOSH protocol. The connection manager must at least support version 1.6!\n");
252 /* XXX This *must* handle this by killing the connection and
253 * reporting an error. */
254 }
255
256 g_free(version);
156 } else { 257 } else {
157 purple_debug_info("jabber", "Missing version in session creation response!\n"); 258 purple_debug_info("jabber", "Missing version in session creation response!\n");
158 } 259 }
159 } 260 }
160 261
178 279
179 conn->receive_cb = jabber_bosh_connection_boot_response; 280 conn->receive_cb = jabber_bosh_connection_boot_response;
180 jabber_bosh_connection_send_native(conn, init); 281 jabber_bosh_connection_send_native(conn, init);
181 } 282 }
182 283
183 void jabber_bosh_connection_http_received_cb(PurpleHTTPRequest *req, PurpleHTTPResponse *res, void *userdata) { 284 static void jabber_bosh_connection_http_received_cb(PurpleHTTPRequest *req, PurpleHTTPResponse *res, void *userdata) {
184 PurpleBOSHConnection *conn = userdata; 285 PurpleBOSHConnection *conn = userdata;
185 if (conn->receive_cb) { 286 if (conn->receive_cb) {
186 xmlnode *node = xmlnode_from_str(res->data, res->data_len); 287 xmlnode *node = xmlnode_from_str(res->data, res->data_len);
187 if (node) { 288 if (node) {
188 char *txt = xmlnode_to_formatted_str(node, NULL); 289 char *txt = xmlnode_to_formatted_str(node, NULL);
211 if (conn->ready == TRUE) xmlnode_set_attrib(node, "xmlns", "jabber:client"); 312 if (conn->ready == TRUE) xmlnode_set_attrib(node, "xmlns", "jabber:client");
212 } 313 }
213 jabber_bosh_connection_send_native(conn, packet); 314 jabber_bosh_connection_send_native(conn, packet);
214 } 315 }
215 316
216 void jabber_bosh_connection_send_native(PurpleBOSHConnection *conn, xmlnode *node) { 317 static void jabber_bosh_connection_send_native(PurpleBOSHConnection *conn, xmlnode *node) {
217 PurpleHTTPRequest *request = g_new0(PurpleHTTPRequest, 1); 318 PurpleHTTPRequest *request = g_new0(PurpleHTTPRequest, 1);
218 319
219 char *txt = xmlnode_to_formatted_str(node, NULL); 320 char *txt = xmlnode_to_formatted_str(node, NULL);
220 printf("\njabber_bosh_connection_send\n%s\n", txt); 321 printf("\njabber_bosh_connection_send\n%s\n", txt);
221 g_free(txt); 322 g_free(txt);
250 void jabber_bosh_connection_connect(PurpleBOSHConnection *conn) { 351 void jabber_bosh_connection_connect(PurpleBOSHConnection *conn) {
251 conn->conn_a->connect_cb = jabber_bosh_connection_connected; 352 conn->conn_a->connect_cb = jabber_bosh_connection_connected;
252 jabber_bosh_http_connection_connect(conn->conn_a); 353 jabber_bosh_http_connection_connect(conn->conn_a);
253 } 354 }
254 355
255 void jabber_bosh_http_connection_receive_parse_header(PurpleHTTPResponse *response, char **data, int *len) { 356 static void jabber_bosh_http_connection_receive_parse_header(PurpleHTTPResponse *response, char **data, int *len) {
256 GHashTable *header = response->header; 357 GHashTable *header = response->header;
257 char *beginning = *data; 358 char *beginning = *data;
258 char *found = g_strstr_len(*data, len, "\r\n\r\n"); 359 char *found = g_strstr_len(*data, *len, "\r\n\r\n");
259 char *field = NULL; 360 char *field = NULL;
260 char *value = NULL; 361 char *value = NULL;
261 char *old_data = *data; 362 char *old_data = *data;
262 363
263 while (*beginning != 'H') ++beginning; 364 while (*beginning != 'H') ++beginning;
310 g_free(old_data); 411 g_free(old_data);
311 } 412 }
312 413
313 if (!response) { 414 if (!response) {
314 /* check for header footer */ 415 /* check for header footer */
315 char *found = NULL; 416 char *found = g_strstr_len(conn->current_data, conn->current_len, "\r\n\r\n");
316 if (found = g_strstr_len(conn->current_data, conn->current_len, "\r\n\r\n")) { 417 if (found) {
317
318 // new response 418 // new response
319 response = conn->current_response = g_new0(PurpleHTTPResponse, 1); 419 response = conn->current_response = g_new0(PurpleHTTPResponse, 1);
320 jabber_bosh_http_response_init(response); 420 jabber_bosh_http_response_init(response);
321 jabber_bosh_http_connection_receive_parse_header(response, &(conn->current_data), &(conn->current_len)); 421 jabber_bosh_http_connection_receive_parse_header(response, &(conn->current_data), &(conn->current_len));
322 response->data_len = atoi(g_hash_table_lookup(response->header, "Content-Length")); 422 response->data_len = atoi(g_hash_table_lookup(response->header, "Content-Length"));
327 427
328 if (response) { 428 if (response) {
329 if (conn->current_len >= response->data_len) { 429 if (conn->current_len >= response->data_len) {
330 PurpleHTTPRequest *request = g_queue_pop_head(conn->requests); 430 PurpleHTTPRequest *request = g_queue_pop_head(conn->requests);
331 431
332 #warning for a pure HTTP 1.1 stack this would be needed to be handled elsewhereƄ 432 #warning For a pure HTTP 1.1 stack, this would need to be handled elsewhere.
333 if (bosh_conn->ready == TRUE && g_queue_is_empty(conn->requests) == TRUE) { 433 if (bosh_conn->ready == TRUE && g_queue_is_empty(conn->requests) == TRUE) {
334 jabber_bosh_connection_send(bosh_conn, NULL); 434 jabber_bosh_connection_send(bosh_conn, NULL);
335 printf("\n SEND AN EMPTY REQUEST \n"); 435 printf("\n SEND AN EMPTY REQUEST \n");
336 } 436 }
337 437
361 } else { 461 } else {
362 purple_debug_info("jabber", "jabber_bosh_http_connection_receive: problem receiving data (%d)\n", len); 462 purple_debug_info("jabber", "jabber_bosh_http_connection_receive: problem receiving data (%d)\n", len);
363 } 463 }
364 } 464 }
365 465
366 void jabber_bosh_http_connection_init(PurpleHTTPConnection *conn, PurpleAccount *account, char *host, int port) { 466 PurpleHTTPConnection *jabber_bosh_http_connection_init(const char *host, int port)
367 conn->account = account; 467 {
368 conn->host = host; 468 PurpleHTTPConnection *conn = g_new0(PurpleHTTPConnection, 1);
469 conn->host = g_strdup(host);
369 conn->port = port; 470 conn->port = port;
370 conn->connect_cb = NULL;
371 conn->current_response = NULL;
372 conn->current_data = NULL;
373 conn->requests = g_queue_new(); 471 conn->requests = g_queue_new();
472
473 return conn;
374 } 474 }
375 475
376 void jabber_bosh_http_connection_clean(PurpleHTTPConnection *conn) { 476 void jabber_bosh_http_connection_clean(PurpleHTTPConnection *conn) {
377 g_queue_free(conn->requests); 477 g_queue_free(conn->requests);
378 } 478 }
388 else purple_debug_info("jabber", "No connect callback for HTTP connection.\n"); 488 else purple_debug_info("jabber", "No connect callback for HTTP connection.\n");
389 conn->ie_handle = purple_input_add(conn->fd, PURPLE_INPUT_READ, jabber_bosh_http_connection_receive, conn); 489 conn->ie_handle = purple_input_add(conn->fd, PURPLE_INPUT_READ, jabber_bosh_http_connection_receive, conn);
390 } 490 }
391 491
392 void jabber_bosh_http_connection_connect(PurpleHTTPConnection *conn) { 492 void jabber_bosh_http_connection_connect(PurpleHTTPConnection *conn) {
393 if((purple_proxy_connect(&(conn->handle), conn->account, conn->host, conn->port, jabber_bosh_http_connection_callback, conn)) == NULL) { 493 PurpleBOSHConnection *bosh_conn = conn->userdata;
494 PurpleConnection *gc = bosh_conn->js->gc;
495 PurpleAccount *account = purple_connection_get_account(gc);
496
497 if((purple_proxy_connect(&(conn->handle), account, conn->host, conn->port, jabber_bosh_http_connection_callback, conn)) == NULL) {
394 purple_debug_info("jabber", "Unable to connect to %s.\n", conn->host); 498 purple_debug_info("jabber", "Unable to connect to %s.\n", conn->host);
395 } 499 }
396 } 500 }
397 501
398 static void jabber_bosh_http_connection_send_request_add_field_to_string(gpointer key, gpointer value, gpointer user_data) { 502 static void jabber_bosh_http_connection_send_request_add_field_to_string(gpointer key, gpointer value, gpointer user_data) {
399 char **ppacket = user_data; 503 char **ppacket = user_data;
400 char *tmp = *ppacket; 504 char *tmp = *ppacket;
424 req->cb = cb; 528 req->cb = cb;
425 req->userdata = userdata; 529 req->userdata = userdata;
426 req->header = g_hash_table_new(g_str_hash, g_str_equal); 530 req->header = g_hash_table_new(g_str_hash, g_str_equal);
427 } 531 }
428 532
429 void jabber_bosh_http_request_add_to_header(PurpleHTTPRequest *req, const char *field, const char *value) { 533 static void jabber_bosh_http_request_add_to_header(PurpleHTTPRequest *req, const char *field, const char *value) {
430 g_hash_table_replace(req->header, field, value); 534 char *f = g_strdup(field);
535 char *v = g_strdup(value);
536 g_hash_table_replace(req->header, f, v);
431 } 537 }
432 538
433 void jabber_bosh_http_request_set_data(PurpleHTTPRequest *req, char *data, int len) { 539 void jabber_bosh_http_request_set_data(PurpleHTTPRequest *req, char *data, int len) {
434 req->data = data; 540 req->data = data;
435 req->data_len = len; 541 req->data_len = len;

mercurial