| 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); |
| 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 |
| 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; |
| 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; |