libpurple/protocols/jabber/bosh.c

changeset 40002
f08d87a438c2
parent 39987
9aea69045c1f
child 40058
8a56f10bd1fb
equal deleted inserted replaced
39989:e557e081686c 40002:f08d87a438c2
23 #include "internal.h" 23 #include "internal.h"
24 #include "core.h" 24 #include "core.h"
25 #include "debug.h" 25 #include "debug.h"
26 #include "http.h" 26 #include "http.h"
27 27
28 #include <libsoup/soup.h>
29
28 #include "bosh.h" 30 #include "bosh.h"
29 31
30 /* 32 /*
31 TODO: test, what happens, if the http server (BOSH server) doesn't support 33 TODO: test, what happens, if the http server (BOSH server) doesn't support
32 keep-alive (sends connection: close). 34 keep-alive (sends connection: close).
38 40
39 static gchar *jabber_bosh_useragent = NULL; 41 static gchar *jabber_bosh_useragent = NULL;
40 42
41 struct _PurpleJabberBOSHConnection { 43 struct _PurpleJabberBOSHConnection {
42 JabberStream *js; 44 JabberStream *js;
43 PurpleHttpKeepalivePool *kapool; 45 SoupSession *payload_reqs;
44 PurpleHttpConnection *sc_req; /* Session Creation Request */
45 PurpleHttpConnectionSet *payload_reqs;
46 46
47 gchar *url; 47 gchar *url;
48 gboolean is_creating;
48 gboolean is_ssl; 49 gboolean is_ssl;
49 gboolean is_terminating; 50 gboolean is_terminating;
50 51
51 gchar *sid; 52 gchar *sid;
52 guint64 rid; /* Must be big enough to hold 2^53 - 1 */ 53 guint64 rid; /* Must be big enough to hold 2^53 - 1 */
53 54
54 GString *send_buff; 55 GString *send_buff;
55 guint send_timer; 56 guint send_timer;
56 }; 57 };
57 58
58 static PurpleHttpRequest * 59 static SoupMessage *jabber_bosh_connection_http_request_new(
59 jabber_bosh_connection_http_request_new(PurpleJabberBOSHConnection *conn, 60 PurpleJabberBOSHConnection *conn, const GString *data);
60 const GString *data);
61 static void 61 static void
62 jabber_bosh_connection_session_create(PurpleJabberBOSHConnection *conn); 62 jabber_bosh_connection_session_create(PurpleJabberBOSHConnection *conn);
63 static void 63 static void
64 jabber_bosh_connection_send_now(PurpleJabberBOSHConnection *conn); 64 jabber_bosh_connection_send_now(PurpleJabberBOSHConnection *conn);
65 65
91 91
92 PurpleJabberBOSHConnection* 92 PurpleJabberBOSHConnection*
93 jabber_bosh_connection_new(JabberStream *js, const gchar *url) 93 jabber_bosh_connection_new(JabberStream *js, const gchar *url)
94 { 94 {
95 PurpleJabberBOSHConnection *conn; 95 PurpleJabberBOSHConnection *conn;
96 PurpleHttpURL *url_p; 96 PurpleAccount *account;
97 97 GProxyResolver *resolver;
98 url_p = purple_http_url_parse(url); 98 GError *error = NULL;
99 if (!url_p) { 99 SoupURI *url_p;
100 purple_debug_error("jabber-bosh", "Unable to parse given URL.\n"); 100
101 account = purple_connection_get_account(js->gc);
102 resolver = purple_proxy_get_proxy_resolver(account, &error);
103 if (resolver == NULL) {
104 purple_debug_error("jabber-bosh",
105 "Unable to get account proxy resolver: %s",
106 error->message);
107 g_error_free(error);
101 return NULL; 108 return NULL;
102 } 109 }
103 110
111 url_p = soup_uri_new(url);
112 if (!SOUP_URI_VALID_FOR_HTTP(url_p)) {
113 purple_debug_error("jabber-bosh", "Unable to parse given BOSH URL: %s",
114 url);
115 g_object_unref(resolver);
116 return NULL;
117 }
118
104 conn = g_new0(PurpleJabberBOSHConnection, 1); 119 conn = g_new0(PurpleJabberBOSHConnection, 1);
105 conn->kapool = purple_http_keepalive_pool_new(); 120 conn->payload_reqs = soup_session_new_with_options(
106 conn->payload_reqs = purple_http_connection_set_new(); 121 SOUP_SESSION_PROXY_RESOLVER, resolver, SOUP_SESSION_TIMEOUT,
107 purple_http_keepalive_pool_set_limit_per_host(conn->kapool, 2); 122 JABBER_BOSH_TIMEOUT + 2, SOUP_SESSION_USER_AGENT,
123 jabber_bosh_useragent, NULL);
108 conn->url = g_strdup(url); 124 conn->url = g_strdup(url);
109 conn->js = js; 125 conn->js = js;
110 conn->is_ssl = (g_ascii_strcasecmp("https", 126 conn->is_ssl = (url_p->scheme == SOUP_URI_SCHEME_HTTPS);
111 purple_http_url_get_protocol(url_p)) == 0);
112 conn->send_buff = g_string_new(NULL); 127 conn->send_buff = g_string_new(NULL);
113 128
114 /* 129 /*
115 * Random 64-bit integer masked off by 2^52 - 1. 130 * Random 64-bit integer masked off by 2^52 - 1.
116 * 131 *
119 * rid. 134 * rid.
120 */ 135 */
121 conn->rid = (((guint64)g_random_int() << 32) | g_random_int()); 136 conn->rid = (((guint64)g_random_int() << 32) | g_random_int());
122 conn->rid &= 0xFFFFFFFFFFFFFLL; 137 conn->rid &= 0xFFFFFFFFFFFFFLL;
123 138
124 if (g_hostname_is_ip_address(purple_http_url_get_host(url_p))) { 139 if (g_hostname_is_ip_address(url_p->host)) {
125 js->serverFQDN = g_strdup(js->user->domain); 140 js->serverFQDN = g_strdup(js->user->domain);
126 } else { 141 } else {
127 js->serverFQDN = g_strdup(purple_http_url_get_host(url_p)); 142 js->serverFQDN = g_strdup(url_p->host);
128 } 143 }
129 144
130 purple_http_url_free(url_p); 145 soup_uri_free(url_p);
146 g_object_unref(resolver);
131 147
132 jabber_bosh_connection_session_create(conn); 148 jabber_bosh_connection_session_create(conn);
133 149
134 return conn; 150 return conn;
135 } 151 }
145 purple_debug_info("jabber-bosh", 161 purple_debug_info("jabber-bosh",
146 "Terminating a session for %p\n", conn); 162 "Terminating a session for %p\n", conn);
147 jabber_bosh_connection_send_now(conn); 163 jabber_bosh_connection_send_now(conn);
148 } 164 }
149 165
150 purple_http_connection_set_destroy(conn->payload_reqs);
151 conn->payload_reqs = NULL;
152
153 if (conn->send_timer) 166 if (conn->send_timer)
154 g_source_remove(conn->send_timer); 167 g_source_remove(conn->send_timer);
155 168
156 purple_http_conn_cancel(conn->sc_req); 169 soup_session_abort(conn->payload_reqs);
157 conn->sc_req = NULL; 170 conn->is_creating = FALSE;
158 171
159 purple_http_keepalive_pool_unref(conn->kapool); 172 g_clear_object(&conn->payload_reqs);
160 conn->kapool = NULL;
161 g_string_free(conn->send_buff, TRUE); 173 g_string_free(conn->send_buff, TRUE);
162 conn->send_buff = NULL; 174 conn->send_buff = NULL;
163 175
164 g_free(conn->sid); 176 g_free(conn->sid);
165 conn->sid = NULL; 177 conn->sid = NULL;
175 return conn->is_ssl; 187 return conn->is_ssl;
176 } 188 }
177 189
178 static PurpleXmlNode * 190 static PurpleXmlNode *
179 jabber_bosh_connection_parse(PurpleJabberBOSHConnection *conn, 191 jabber_bosh_connection_parse(PurpleJabberBOSHConnection *conn,
180 PurpleHttpResponse *response) 192 SoupMessage *response)
181 { 193 {
182 PurpleXmlNode *root; 194 PurpleXmlNode *root;
183 const gchar *data;
184 size_t data_len;
185 const gchar *type; 195 const gchar *type;
186 196
187 g_return_val_if_fail(conn != NULL, NULL); 197 g_return_val_if_fail(conn != NULL, NULL);
188 g_return_val_if_fail(response != NULL, NULL); 198 g_return_val_if_fail(response != NULL, NULL);
189 199
191 purple_connection_get_account(conn->js->gc))) 201 purple_connection_get_account(conn->js->gc)))
192 { 202 {
193 return NULL; 203 return NULL;
194 } 204 }
195 205
196 if (!purple_http_response_is_successful(response)) { 206 if (!SOUP_STATUS_IS_SUCCESSFUL(response->status_code)) {
207 gchar *tmp = g_strdup_printf(_("Unable to connect: %s"),
208 response->reason_phrase);
197 purple_connection_error(conn->js->gc, 209 purple_connection_error(conn->js->gc,
198 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, 210 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
199 _("Unable to connect")); 211 g_free(tmp);
200 return NULL; 212 return NULL;
201 } 213 }
202 214
203 data = purple_http_response_get_data(response, &data_len); 215 root = purple_xmlnode_from_str(response->response_body->data,
204 root = purple_xmlnode_from_str(data, data_len); 216 response->response_body->length);
205 217
206 type = purple_xmlnode_get_attrib(root, "type"); 218 type = purple_xmlnode_get_attrib(root, "type");
207 if (purple_strequal(type, "terminate")) { 219 if (purple_strequal(type, "terminate")) {
208 purple_connection_error(conn->js->gc, 220 purple_connection_error(conn->js->gc,
209 PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("The BOSH " 221 PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("The BOSH "
214 226
215 return root; 227 return root;
216 } 228 }
217 229
218 static void 230 static void
219 jabber_bosh_connection_recv(PurpleHttpConnection *http_conn, 231 jabber_bosh_connection_recv(SoupSession *session, SoupMessage *msg,
220 PurpleHttpResponse *response, gpointer _bosh_conn) 232 gpointer user_data)
221 { 233 {
222 PurpleJabberBOSHConnection *bosh_conn = _bosh_conn; 234 PurpleJabberBOSHConnection *bosh_conn = user_data;
223 PurpleXmlNode *node, *child; 235 PurpleXmlNode *node, *child;
224 236
225 if (purple_debug_is_verbose() && purple_debug_is_unsafe()) { 237 if (purple_debug_is_verbose() && purple_debug_is_unsafe()) {
226 purple_debug_misc("jabber-bosh", "received: %s\n", 238 purple_debug_misc("jabber-bosh", "received: %s\n",
227 purple_http_response_get_data(response, NULL)); 239 msg->response_body->data);
228 } 240 }
229 241
230 node = jabber_bosh_connection_parse(bosh_conn, response); 242 node = jabber_bosh_connection_parse(bosh_conn, msg);
231 if (node == NULL) 243 if (node == NULL)
232 return; 244 return;
233 245
234 child = node->child; 246 child = node->child;
235 while (child != NULL) { 247 while (child != NULL) {
263 } 275 }
264 276
265 static void 277 static void
266 jabber_bosh_connection_send_now(PurpleJabberBOSHConnection *conn) 278 jabber_bosh_connection_send_now(PurpleJabberBOSHConnection *conn)
267 { 279 {
268 PurpleHttpRequest *req; 280 SoupMessage *req;
269 GString *data; 281 GString *data;
270 282
271 g_return_if_fail(conn != NULL); 283 g_return_if_fail(conn != NULL);
272 284
273 if (conn->send_timer != 0) { 285 if (conn->send_timer != 0) {
303 315
304 if (purple_debug_is_verbose() && purple_debug_is_unsafe()) 316 if (purple_debug_is_verbose() && purple_debug_is_unsafe())
305 purple_debug_misc("jabber-bosh", "sending: %s\n", data->str); 317 purple_debug_misc("jabber-bosh", "sending: %s\n", data->str);
306 318
307 req = jabber_bosh_connection_http_request_new(conn, data); 319 req = jabber_bosh_connection_http_request_new(conn, data);
308 g_string_free(data, TRUE); 320 g_string_free(data, FALSE);
309 321
310 if (conn->is_terminating) { 322 if (conn->is_terminating) {
311 purple_http_request(NULL, req, NULL, NULL); 323 soup_session_send_async(conn->payload_reqs, req, NULL, NULL, NULL);
312 g_free(conn->sid); 324 g_free(conn->sid);
313 conn->sid = NULL; 325 conn->sid = NULL;
314 } else { 326 } else {
315 purple_http_connection_set_add(conn->payload_reqs, 327 soup_session_queue_message(conn->payload_reqs, req,
316 purple_http_request(conn->js->gc, req, 328 jabber_bosh_connection_recv, conn);
317 jabber_bosh_connection_recv, conn)); 329 }
318 }
319
320 purple_http_request_unref(req);
321 } 330 }
322 331
323 static gboolean 332 static gboolean
324 jabber_bosh_connection_send_delayed(gpointer _conn) 333 jabber_bosh_connection_send_delayed(gpointer _conn)
325 { 334 {
375 return FALSE; 384 return FALSE;
376 return TRUE; 385 return TRUE;
377 } 386 }
378 387
379 static void 388 static void
380 jabber_bosh_connection_session_created(PurpleHttpConnection *http_conn, 389 jabber_bosh_connection_session_created(SoupSession *session, SoupMessage *msg,
381 PurpleHttpResponse *response, gpointer _bosh_conn) 390 gpointer user_data)
382 { 391 {
383 PurpleJabberBOSHConnection *bosh_conn = _bosh_conn; 392 PurpleJabberBOSHConnection *bosh_conn = user_data;
384 PurpleXmlNode *node, *features; 393 PurpleXmlNode *node, *features;
385 const gchar *sid, *ver, *inactivity_str; 394 const gchar *sid, *ver, *inactivity_str;
386 int inactivity = 0; 395 int inactivity = 0;
387 396
388 bosh_conn->sc_req = NULL; 397 bosh_conn->is_creating = FALSE;
389 398
390 if (purple_debug_is_verbose() && purple_debug_is_unsafe()) { 399 if (purple_debug_is_verbose() && purple_debug_is_unsafe()) {
391 purple_debug_misc("jabber-bosh", 400 purple_debug_misc("jabber-bosh", "received (session creation): %s\n",
392 "received (session creation): %s\n", 401 msg->response_body->data);
393 purple_http_response_get_data(response, NULL)); 402 }
394 } 403
395 404 node = jabber_bosh_connection_parse(bosh_conn, msg);
396 node = jabber_bosh_connection_parse(bosh_conn, response);
397 if (node == NULL) 405 if (node == NULL)
398 return; 406 return;
399 407
400 sid = purple_xmlnode_get_attrib(node, "sid"); 408 sid = purple_xmlnode_get_attrib(node, "sid");
401 ver = purple_xmlnode_get_attrib(node, "ver"); 409 ver = purple_xmlnode_get_attrib(node, "ver");
458 } 466 }
459 467
460 static void 468 static void
461 jabber_bosh_connection_session_create(PurpleJabberBOSHConnection *conn) 469 jabber_bosh_connection_session_create(PurpleJabberBOSHConnection *conn)
462 { 470 {
463 PurpleHttpRequest *req; 471 SoupMessage *req;
464 GString *data; 472 GString *data;
465 473
466 g_return_if_fail(conn != NULL); 474 g_return_if_fail(conn != NULL);
467 475
468 if (conn->sid || conn->sc_req) 476 if (conn->sid || conn->is_creating) {
469 return; 477 return;
478 }
470 479
471 purple_debug_misc("jabber-bosh", "Requesting Session Create for %p\n", 480 purple_debug_misc("jabber-bosh", "Requesting Session Create for %p\n",
472 conn); 481 conn);
473 482
474 data = g_string_new(NULL); 483 data = g_string_new(NULL);
486 "xmlns:xmpp='urn:xmpp:xbosh' " 495 "xmlns:xmpp='urn:xmpp:xbosh' "
487 "/>", 496 "/>",
488 ++conn->rid, conn->js->user->domain, JABBER_BOSH_TIMEOUT); 497 ++conn->rid, conn->js->user->domain, JABBER_BOSH_TIMEOUT);
489 498
490 req = jabber_bosh_connection_http_request_new(conn, data); 499 req = jabber_bosh_connection_http_request_new(conn, data);
491 g_string_free(data, TRUE); 500 g_string_free(data, FALSE);
492 501
493 conn->sc_req = purple_http_request(conn->js->gc, req, 502 conn->is_creating = TRUE;
494 jabber_bosh_connection_session_created, conn); 503 soup_session_queue_message(conn->payload_reqs, req,
495 504 jabber_bosh_connection_session_created, conn);
496 purple_http_request_unref(req); 505 }
497 } 506
498 507 static SoupMessage *
499 static PurpleHttpRequest *
500 jabber_bosh_connection_http_request_new(PurpleJabberBOSHConnection *conn, 508 jabber_bosh_connection_http_request_new(PurpleJabberBOSHConnection *conn,
501 const GString *data) 509 const GString *data)
502 { 510 {
503 PurpleHttpRequest *req; 511 SoupMessage *req;
504 512
505 jabber_stream_restart_inactivity_timer(conn->js); 513 jabber_stream_restart_inactivity_timer(conn->js);
506 514
507 req = purple_http_request_new(conn->url); 515 req = soup_message_new("POST", conn->url);
508 purple_http_request_set_keepalive_pool(req, conn->kapool); 516 soup_message_set_request(req, "text/xml; charset=utf-8", SOUP_MEMORY_TAKE,
509 purple_http_request_set_method(req, "POST"); 517 data->str, data->len);
510 purple_http_request_set_timeout(req, JABBER_BOSH_TIMEOUT + 2);
511 purple_http_request_header_set(req, "User-Agent",
512 jabber_bosh_useragent);
513 purple_http_request_header_set(req, "Content-Encoding",
514 "text/xml; charset=utf-8");
515 purple_http_request_set_contents(req, data->str, data->len);
516 518
517 return req; 519 return req;
518 } 520 }

mercurial