| 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) { |
| 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 } |