| 1 /** |
|
| 2 * @file httpmethod.c HTTP connection method |
|
| 3 * |
|
| 4 * gaim |
|
| 5 * |
|
| 6 * Gaim is the legal property of its developers, whose names are too numerous |
|
| 7 * to list here. Please refer to the COPYRIGHT file distributed with this |
|
| 8 * source distribution. |
|
| 9 * |
|
| 10 * This program is free software; you can redistribute it and/or modify |
|
| 11 * it under the terms of the GNU General Public License as published by |
|
| 12 * the Free Software Foundation; either version 2 of the License, or |
|
| 13 * (at your option) any later version. |
|
| 14 * |
|
| 15 * This program is distributed in the hope that it will be useful, |
|
| 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 18 * GNU General Public License for more details. |
|
| 19 * |
|
| 20 * You should have received a copy of the GNU General Public License |
|
| 21 * along with this program; if not, write to the Free Software |
|
| 22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
| 23 */ |
|
| 24 #include "debug.h" |
|
| 25 #include "httpmethod.h" |
|
| 26 |
|
| 27 #define GET_NEXT(tmp) \ |
|
| 28 while (*(tmp) && *(tmp) != ' ' && *(tmp) != '\r') \ |
|
| 29 (tmp)++; \ |
|
| 30 if (*(tmp) != '\0') *(tmp)++ = '\0'; \ |
|
| 31 if (*(tmp) == '\n') (tmp)++; \ |
|
| 32 while (*(tmp) && *(tmp) == ' ') \ |
|
| 33 (tmp)++ |
|
| 34 |
|
| 35 #define GET_NEXT_LINE(tmp) \ |
|
| 36 while (*(tmp) && *(tmp) != '\r') \ |
|
| 37 (tmp)++; \ |
|
| 38 if (*(tmp) != '\0') *(tmp)++ = '\0'; \ |
|
| 39 if (*(tmp) == '\n') (tmp)++ |
|
| 40 |
|
| 41 typedef struct |
|
| 42 { |
|
| 43 MsnServConn *servconn; |
|
| 44 char *buffer; |
|
| 45 size_t size; |
|
| 46 const char *server_type; |
|
| 47 |
|
| 48 } MsnHttpQueueData; |
|
| 49 |
|
| 50 static gboolean |
|
| 51 http_poll(gpointer data) |
|
| 52 { |
|
| 53 MsnSession *session; |
|
| 54 GList *l; |
|
| 55 |
|
| 56 session = data; |
|
| 57 |
|
| 58 for (l = session->switches; l != NULL; l = l->next) |
|
| 59 { |
|
| 60 MsnSwitchBoard *swboard; |
|
| 61 |
|
| 62 swboard = l->data; |
|
| 63 |
|
| 64 g_return_val_if_fail(swboard->servconn->http_data != NULL, FALSE); |
|
| 65 |
|
| 66 if (swboard->servconn->http_data->dirty) |
|
| 67 { |
|
| 68 #if 0 |
|
| 69 gaim_debug_info("msn", "Polling server %s.\n", |
|
| 70 servconn->http_data->gateway_host); |
|
| 71 #endif |
|
| 72 msn_http_servconn_poll(swboard->servconn); |
|
| 73 } |
|
| 74 } |
|
| 75 |
|
| 76 if (session->notification->servconn->http_data->dirty) |
|
| 77 msn_http_servconn_poll(session->notification->servconn); |
|
| 78 |
|
| 79 return TRUE; |
|
| 80 } |
|
| 81 |
|
| 82 static void |
|
| 83 stop_timer(MsnSession *session) |
|
| 84 { |
|
| 85 if (session->http_poll_timer) |
|
| 86 { |
|
| 87 gaim_timeout_remove(session->http_poll_timer); |
|
| 88 session->http_poll_timer = 0; |
|
| 89 } |
|
| 90 } |
|
| 91 |
|
| 92 static void |
|
| 93 start_timer(MsnSession *session) |
|
| 94 { |
|
| 95 stop_timer(session); |
|
| 96 |
|
| 97 session->http_poll_timer = gaim_timeout_add(2000, http_poll, session); |
|
| 98 } |
|
| 99 |
|
| 100 void |
|
| 101 msn_http_session_init(MsnSession *session) |
|
| 102 { |
|
| 103 g_return_if_fail(session != NULL); |
|
| 104 |
|
| 105 start_timer(session); |
|
| 106 } |
|
| 107 |
|
| 108 void |
|
| 109 msn_http_session_uninit(MsnSession *session) |
|
| 110 { |
|
| 111 g_return_if_fail(session != NULL); |
|
| 112 |
|
| 113 stop_timer(session); |
|
| 114 } |
|
| 115 |
|
| 116 size_t |
|
| 117 msn_http_servconn_write(MsnServConn *servconn, const char *buf, size_t size, |
|
| 118 const char *server_type) |
|
| 119 { |
|
| 120 size_t s, needed; |
|
| 121 char *params; |
|
| 122 char *temp; |
|
| 123 gboolean first; |
|
| 124 int res; /* result of the write operation */ |
|
| 125 |
|
| 126 g_return_val_if_fail(servconn != NULL, 0); |
|
| 127 g_return_val_if_fail(buf != NULL, 0); |
|
| 128 g_return_val_if_fail(size > 0, 0); |
|
| 129 g_return_val_if_fail(servconn->http_data != NULL, 0); |
|
| 130 |
|
| 131 if (servconn->http_data->waiting_response) |
|
| 132 { |
|
| 133 MsnHttpQueueData *queue_data = g_new0(MsnHttpQueueData, 1); |
|
| 134 |
|
| 135 queue_data->servconn = servconn; |
|
| 136 queue_data->buffer = g_strdup(buf); |
|
| 137 queue_data->size = size; |
|
| 138 queue_data->server_type = server_type; |
|
| 139 |
|
| 140 servconn->http_data->queue = |
|
| 141 g_list_append(servconn->http_data->queue, queue_data); |
|
| 142 |
|
| 143 return size; |
|
| 144 } |
|
| 145 |
|
| 146 first = servconn->http_data->virgin; |
|
| 147 |
|
| 148 if (first) |
|
| 149 { |
|
| 150 if (server_type) |
|
| 151 { |
|
| 152 params = g_strdup_printf("Action=open&Server=%s&IP=%s", |
|
| 153 server_type, |
|
| 154 servconn->http_data->gateway_host); |
|
| 155 } |
|
| 156 else |
|
| 157 { |
|
| 158 params = g_strdup_printf("Action=open&IP=%s", |
|
| 159 servconn->http_data->gateway_host); |
|
| 160 } |
|
| 161 } |
|
| 162 else |
|
| 163 { |
|
| 164 params = g_strdup_printf("SessionID=%s", |
|
| 165 servconn->http_data->session_id); |
|
| 166 } |
|
| 167 |
|
| 168 temp = g_strdup_printf( |
|
| 169 "POST http://%s/gateway/gateway.dll?%s HTTP/1.1\r\n" |
|
| 170 "Accept: */*\r\n" |
|
| 171 "Accept-Language: en-us\r\n" |
|
| 172 "User-Agent: MSMSGS\r\n" |
|
| 173 "Host: %s\r\n" |
|
| 174 "Proxy-Connection: Keep-Alive\r\n" |
|
| 175 "Connection: Keep-Alive\r\n" |
|
| 176 "Pragma: no-cache\r\n" |
|
| 177 "Content-Type: application/x-msn-messenger\r\n" |
|
| 178 "Content-Length: %d\r\n" |
|
| 179 "\r\n" |
|
| 180 "%s", |
|
| 181 ((strcmp(server_type, "SB") == 0) && first |
|
| 182 ? "gateway.messenger.hotmail.com" |
|
| 183 : servconn->http_data->gateway_host), |
|
| 184 params, |
|
| 185 servconn->http_data->gateway_host, |
|
| 186 (int)size, |
|
| 187 buf); |
|
| 188 |
|
| 189 g_free(params); |
|
| 190 |
|
| 191 #if 0 |
|
| 192 gaim_debug_misc("msn", "Writing HTTP to fd %d: {%s}\n", |
|
| 193 servconn->fd, temp); |
|
| 194 #endif |
|
| 195 |
|
| 196 s = 0; |
|
| 197 needed = strlen(temp); |
|
| 198 |
|
| 199 do |
|
| 200 { |
|
| 201 res = write(servconn->fd, temp, needed); |
|
| 202 if (res >= 0) |
|
| 203 s += res; |
|
| 204 else if (errno != EAGAIN) |
|
| 205 { |
|
| 206 char *msg = g_strdup_printf("Unable to write to MSN server via HTTP (error %d)", errno); |
|
| 207 gaim_connection_error(servconn->session->account->gc, msg); |
|
| 208 g_free(msg); |
|
| 209 return -1; |
|
| 210 } |
|
| 211 } while (s < needed); |
|
| 212 |
|
| 213 g_free(temp); |
|
| 214 |
|
| 215 servconn->http_data->waiting_response = TRUE; |
|
| 216 servconn->http_data->virgin = FALSE; |
|
| 217 servconn->http_data->dirty = FALSE; |
|
| 218 |
|
| 219 return s; |
|
| 220 } |
|
| 221 |
|
| 222 void |
|
| 223 msn_http_servconn_poll(MsnServConn *servconn) |
|
| 224 { |
|
| 225 size_t s; |
|
| 226 char *temp; |
|
| 227 |
|
| 228 g_return_if_fail(servconn != NULL); |
|
| 229 g_return_if_fail(servconn->http_data != NULL); |
|
| 230 |
|
| 231 if (servconn->http_data->waiting_response || |
|
| 232 servconn->http_data->queue != NULL) |
|
| 233 { |
|
| 234 return; |
|
| 235 } |
|
| 236 |
|
| 237 temp = g_strdup_printf( |
|
| 238 "POST http://%s/gateway/gateway.dll?Action=poll&SessionID=%s HTTP/1.1\r\n" |
|
| 239 "Accept: */*\r\n" |
|
| 240 "Accept-Language: en-us\r\n" |
|
| 241 "User-Agent: MSMSGS\r\n" |
|
| 242 "Host: %s\r\n" |
|
| 243 "Proxy-Connection: Keep-Alive\r\n" |
|
| 244 "Connection: Keep-Alive\r\n" |
|
| 245 "Pragma: no-cache\r\n" |
|
| 246 "Content-Type: application/x-msn-messenger\r\n" |
|
| 247 "Content-Length: 0\r\n" |
|
| 248 "\r\n", |
|
| 249 servconn->http_data->gateway_host, |
|
| 250 servconn->http_data->session_id, |
|
| 251 servconn->http_data->gateway_host); |
|
| 252 |
|
| 253 #if 0 |
|
| 254 gaim_debug_misc("msn", "Writing to HTTP: {%s}\n", temp); |
|
| 255 #endif |
|
| 256 |
|
| 257 s = write(servconn->fd, temp, strlen(temp)); |
|
| 258 |
|
| 259 g_free(temp); |
|
| 260 |
|
| 261 servconn->http_data->waiting_response = TRUE; |
|
| 262 servconn->http_data->dirty = FALSE; |
|
| 263 |
|
| 264 if (s <= 0) |
|
| 265 gaim_connection_error(servconn->session->account->gc, |
|
| 266 _("Write error")); |
|
| 267 } |
|
| 268 |
|
| 269 gboolean |
|
| 270 msn_http_servconn_parse_data(MsnServConn *servconn, const char *buf, |
|
| 271 size_t size, char **ret_buf, size_t *ret_size, |
|
| 272 gboolean *error) |
|
| 273 { |
|
| 274 GaimConnection *gc; |
|
| 275 const char *s, *c; |
|
| 276 char *headers, *body; |
|
| 277 char *tmp; |
|
| 278 size_t len = 0; |
|
| 279 |
|
| 280 g_return_val_if_fail(servconn != NULL, FALSE); |
|
| 281 g_return_val_if_fail(buf != NULL, FALSE); |
|
| 282 g_return_val_if_fail(size > 0, FALSE); |
|
| 283 g_return_val_if_fail(ret_buf != NULL, FALSE); |
|
| 284 g_return_val_if_fail(ret_size != NULL, FALSE); |
|
| 285 g_return_val_if_fail(error != NULL, FALSE); |
|
| 286 |
|
| 287 #if 0 |
|
| 288 gaim_debug_info("msn", "parsing data {%s} from fd %d\n", |
|
| 289 buf, servconn->fd); |
|
| 290 #endif |
|
| 291 servconn->http_data->waiting_response = FALSE; |
|
| 292 |
|
| 293 gc = gaim_account_get_connection(servconn->session->account); |
|
| 294 |
|
| 295 /* Healthy defaults. */ |
|
| 296 *ret_buf = NULL; |
|
| 297 *ret_size = 0; |
|
| 298 *error = FALSE; |
|
| 299 |
|
| 300 /* First, some tests to see if we have a full block of stuff. */ |
|
| 301 if (((strncmp(buf, "HTTP/1.1 200 OK\r\n", 17) != 0) && |
|
| 302 (strncmp(buf, "HTTP/1.1 100 Continue\r\n", 23) != 0)) && |
|
| 303 ((strncmp(buf, "HTTP/1.0 200 OK\r\n", 17) != 0) && |
|
| 304 (strncmp(buf, "HTTP/1.0 100 Continue\r\n", 23) != 0))) |
|
| 305 { |
|
| 306 *error = TRUE; |
|
| 307 |
|
| 308 return FALSE; |
|
| 309 } |
|
| 310 |
|
| 311 if (strncmp(buf, "HTTP/1.1 100 Continue\r\n", 23) == 0) |
|
| 312 { |
|
| 313 if ((s = strstr(buf, "\r\n\r\n")) == NULL) |
|
| 314 return FALSE; |
|
| 315 |
|
| 316 s += 4; |
|
| 317 |
|
| 318 if (*s == '\0') |
|
| 319 { |
|
| 320 *ret_buf = g_strdup(""); |
|
| 321 *ret_size = 0; |
|
| 322 |
|
| 323 return TRUE; |
|
| 324 } |
|
| 325 |
|
| 326 buf = s; |
|
| 327 size -= (s - buf); |
|
| 328 } |
|
| 329 |
|
| 330 if ((s = strstr(buf, "\r\n\r\n")) == NULL) |
|
| 331 return FALSE; |
|
| 332 |
|
| 333 headers = g_strndup(buf, s - buf); |
|
| 334 s += 4; /* Skip \r\n */ |
|
| 335 body = g_strndup(s, size - (s - buf)); |
|
| 336 |
|
| 337 #if 0 |
|
| 338 gaim_debug_misc("msn", "Incoming HTTP buffer: {%s\r\n%s}", headers, body); |
|
| 339 #endif |
|
| 340 |
|
| 341 if ((s = strstr(headers, "Content-Length: ")) != NULL) |
|
| 342 { |
|
| 343 s += strlen("Content-Length: "); |
|
| 344 |
|
| 345 if ((c = strchr(s, '\r')) == NULL) |
|
| 346 { |
|
| 347 g_free(headers); |
|
| 348 g_free(body); |
|
| 349 |
|
| 350 return FALSE; |
|
| 351 } |
|
| 352 |
|
| 353 tmp = g_strndup(s, c - s); |
|
| 354 len = atoi(tmp); |
|
| 355 g_free(tmp); |
|
| 356 |
|
| 357 if (strlen(body) != len) |
|
| 358 { |
|
| 359 g_free(headers); |
|
| 360 g_free(body); |
|
| 361 |
|
| 362 gaim_debug_warning("msn", |
|
| 363 "body length (%d) != content length (%d)\n", |
|
| 364 strlen(body), len); |
|
| 365 return FALSE; |
|
| 366 } |
|
| 367 } |
|
| 368 |
|
| 369 /* Now we should be able to process the data. */ |
|
| 370 if ((s = strstr(headers, "X-MSN-Messenger: ")) != NULL) |
|
| 371 { |
|
| 372 char *session_id, *gw_ip; |
|
| 373 char *c2, *s2; |
|
| 374 |
|
| 375 s += strlen("X-MSN-Messenger: "); |
|
| 376 |
|
| 377 if ((c = strchr(s, '\r')) == NULL) |
|
| 378 { |
|
| 379 gaim_connection_error(gc, "Malformed X-MSN-Messenger field."); |
|
| 380 return FALSE; |
|
| 381 } |
|
| 382 |
|
| 383 tmp = g_strndup(s, c - s); |
|
| 384 |
|
| 385 /* Find the value for the Session ID */ |
|
| 386 if ((s2 = strchr(tmp, '=')) == NULL) |
|
| 387 { |
|
| 388 gaim_connection_error(gc, "Malformed X-MSN-Messenger field."); |
|
| 389 return FALSE; |
|
| 390 } |
|
| 391 |
|
| 392 s2++; |
|
| 393 |
|
| 394 /* Terminate the ; so we can g_strdup it. */ |
|
| 395 if ((c2 = strchr(s2, ';')) == NULL) |
|
| 396 { |
|
| 397 gaim_connection_error(gc, "Malformed X-MSN-Messenger field."); |
|
| 398 return FALSE; |
|
| 399 } |
|
| 400 |
|
| 401 *c2 = '\0'; |
|
| 402 c2++; |
|
| 403 |
|
| 404 /* Now grab that session ID. */ |
|
| 405 session_id = g_strdup(s2); |
|
| 406 |
|
| 407 /* Continue to the gateway IP */ |
|
| 408 if ((s2 = strchr(c2, '=')) == NULL) |
|
| 409 { |
|
| 410 gaim_connection_error(gc, "Malformed X-MSN-Messenger field."); |
|
| 411 return FALSE; |
|
| 412 } |
|
| 413 |
|
| 414 s2++; |
|
| 415 |
|
| 416 /* Grab the gateway IP */ |
|
| 417 gw_ip = g_strdup(s2); |
|
| 418 |
|
| 419 g_free(tmp); |
|
| 420 |
|
| 421 /* Set the new data. */ |
|
| 422 if (servconn->http_data->session_id != NULL) |
|
| 423 g_free(servconn->http_data->session_id); |
|
| 424 |
|
| 425 if (servconn->http_data->old_gateway_host != NULL) |
|
| 426 g_free(servconn->http_data->old_gateway_host); |
|
| 427 |
|
| 428 servconn->http_data->old_gateway_host = |
|
| 429 servconn->http_data->gateway_host; |
|
| 430 |
|
| 431 servconn->http_data->session_id = session_id; |
|
| 432 servconn->http_data->gateway_host = gw_ip; |
|
| 433 } |
|
| 434 |
|
| 435 g_free(headers); |
|
| 436 |
|
| 437 *ret_buf = body; |
|
| 438 *ret_size = len; |
|
| 439 |
|
| 440 if (servconn->http_data->queue != NULL) |
|
| 441 { |
|
| 442 MsnHttpQueueData *queue_data; |
|
| 443 |
|
| 444 queue_data = (MsnHttpQueueData *)servconn->http_data->queue->data; |
|
| 445 |
|
| 446 servconn->http_data->queue = |
|
| 447 g_list_remove(servconn->http_data->queue, queue_data); |
|
| 448 |
|
| 449 msn_http_servconn_write(queue_data->servconn, |
|
| 450 queue_data->buffer, |
|
| 451 queue_data->size, |
|
| 452 queue_data->server_type); |
|
| 453 |
|
| 454 g_free(queue_data->buffer); |
|
| 455 g_free(queue_data); |
|
| 456 } |
|
| 457 else |
|
| 458 servconn->http_data->dirty = TRUE; |
|
| 459 |
|
| 460 return TRUE; |
|
| 461 } |
|
| 462 |
|