Mercurial > pidgin
annotate src/protocols/msn/httpconn.c @ 12645:fc28451f5d96
[gaim-migrate @ 14983]
SF Patch #1314512 from Sadrul (who has a patch for everything)
"This patch introduces a flag for protocol plugins that
support offline messages (like Y!M and ICQ). This was
encouraged by the following conversation:
<sadrul> should offline buddies be listed/enabled in
the send-to menu?
<rekkanoryo> i would think only for protocols that
support offline messaging, if it's indicated that the
buddy is offline
-- <snip> --
<Bleeter> sadrul: personally, I'd like to see a
'supports offline' flag of some description
<Bleeter> one could then redirect (via plugins) through
email or alternative methods
<Bleeter> just a thought
<Paco-Paco> yeah, that sounds like a reasonble thing to have
This patch uses this flag to disable the buddies in the
send-to menu who are offline and the protocol doesn't
support offline messages."
I made this make the label insensitive instead of the whole menuitem. This
should address SimGuy's concerns about inconsistency (i.e. you could create a
conversation with someone via the buddy list that you couldn't create via the
Send To menu). I also hacked up some voodoo to show the label as sensitive when
moused-over, as that looks better (given the label-insensitive thing is itself a
hack). I think this works quite well.
BUG NOTE:
This makes more obvious an existing bug. The Send To menu isn't updated when
buddies sign on or off or change status (at least under some circumstances).
We need to fix that anyway, so I'm not going to let it hold up this commit.
Switching tabs will clear it up. I'm thinking we just might want to build the
contents of that menu when it is selected. That would save us a mess of
inefficient signal callbacks that update the Send To menus in open windows all
the time.
AIM NOTE:
This assumes that AIM can't offline message. That's not strictly true. You can
message invisible users on AIM. However, by design, we can't tell when a user
is invisible without resorting to dirty hackery. In practice, this isn't a
problem, as you can still select the AIM user from the menu. And really, how
often will you be choosing the Invisible contact, rather than the user going
Invisible in the middle of a conversation or IMing you while they're Invisible?
JABBER NOTE:
This assumes that Jabber can always offline message. This isn't strictly true.
Sadrul said:
I have updated Jabber according to this link which seems to
talk about how to determine the existence offline-message
support in a server:
http://www.jabber.org/jeps/jep-0013.html#discover
However, jabber.org doesn't seem to send the required
info. So I am not sure about it.
He later said:
I talked to Nathan and he said offline message support is
mostly assumed for most jabber servers. GTalk doesn't yet
support it, but they are working on it. So I have made
jabber to always return TRUE.
If there is truly no way to detect offline messaging capability, then this is
an acceptable solution. We could special case Google Talk because of its
popularity, and remove that later. It's probably not worth it though.
MSN NOTE:
This assumes that MSN can never offline message. That's effectively true, but
to be technically correct, MSN can offline message if there's already a
switchboard conversation open with a user. We could write an offline_message
function in the MSN prpl to detect that, but it'd be of limited usefulness,
especially given that under most circumstances (where this might matter), the
switchboard connection will be closed almost immediately.
CVS NOTE:
I'm writing to share a tragic little story.
I have a PC that I use for Gaim development. One day, I was writing a commit
message on it, when all of a suddent it went berserk. The screen started
flashing, and the whole commit message just disappeared. All of it. And it was
a good commit message! I had to cram and rewrite it really quickly. Needless to
say, my rushed commit message wasn't nearly as good, and I blame the PC for that.
Seriously, though, what kind of version control system loses your commit
message on a broken connection to the server? Stupid!
committer: Tailor Script <tailor@pidgin.im>
| author | Richard Laager <rlaager@wiktel.com> |
|---|---|
| date | Fri, 23 Dec 2005 19:26:04 +0000 |
| parents | fc464a0abccc |
| children | 33bef17125c2 |
| rev | line source |
|---|---|
| 10463 | 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 "msn.h" | |
| 25 #include "debug.h" | |
| 26 #include "httpconn.h" | |
| 27 | |
| 28 typedef struct | |
| 29 { | |
| 30 MsnHttpConn *httpconn; | |
| 10481 | 31 char *data; |
| 10463 | 32 size_t size; |
| 33 | |
| 34 } MsnHttpQueueData; | |
| 35 | |
| 36 static void read_cb(gpointer data, gint source, GaimInputCondition cond); | |
| 37 void msn_httpconn_process_queue(MsnHttpConn *httpconn); | |
| 38 gboolean msn_httpconn_parse_data(MsnHttpConn *httpconn, const char *buf, | |
| 39 size_t size, char **ret_buf, size_t *ret_size, | |
| 40 gboolean *error); | |
| 41 | |
| 42 MsnHttpConn * | |
| 43 msn_httpconn_new(MsnServConn *servconn) | |
| 44 { | |
| 45 MsnHttpConn *httpconn; | |
| 46 | |
| 47 g_return_val_if_fail(servconn != NULL, NULL); | |
| 48 | |
| 49 httpconn = g_new0(MsnHttpConn, 1); | |
| 50 | |
| 10481 | 51 gaim_debug_info("msn", "new httpconn (%p)\n", httpconn); |
| 52 | |
| 10463 | 53 /* TODO: Remove this */ |
| 54 httpconn->session = servconn->session; | |
| 55 | |
| 56 httpconn->servconn = servconn; | |
| 57 | |
| 58 return httpconn; | |
| 59 } | |
| 60 | |
| 61 void | |
| 62 msn_httpconn_destroy(MsnHttpConn *httpconn) | |
| 63 { | |
| 64 g_return_if_fail(httpconn != NULL); | |
| 65 | |
| 10481 | 66 gaim_debug_info("msn", "destroy httpconn (%p)\n", httpconn); |
| 67 | |
| 10463 | 68 if (httpconn->connected) |
| 69 msn_httpconn_disconnect(httpconn); | |
| 70 | |
| 10504 | 71 if (httpconn->full_session_id != NULL) |
| 72 g_free(httpconn->full_session_id); | |
| 73 | |
| 74 if (httpconn->session_id != NULL) | |
| 75 g_free(httpconn->session_id); | |
| 76 | |
| 77 if (httpconn->host != NULL) | |
| 78 g_free(httpconn->host); | |
| 79 | |
| 10463 | 80 g_free(httpconn); |
| 81 } | |
| 82 | |
| 10568 | 83 static char * |
| 84 msn_httpconn_proxy_auth(MsnHttpConn *httpconn) | |
| 85 { | |
| 86 GaimAccount *account; | |
| 87 GaimProxyInfo *gpi; | |
| 88 const char *username, *password; | |
| 89 char *auth = NULL; | |
| 90 | |
| 91 account = httpconn->session->account; | |
| 92 | |
| 93 if (gaim_account_get_proxy_info(account) == NULL) | |
| 94 gpi = gaim_global_proxy_get_info(); | |
| 95 else | |
| 96 gpi = gaim_account_get_proxy_info(account); | |
| 97 | |
| 98 if (gpi == NULL || !(gaim_proxy_info_get_type(gpi) == GAIM_PROXY_HTTP || | |
| 99 gaim_proxy_info_get_type(gpi) == GAIM_PROXY_USE_ENVVAR)) | |
| 100 return NULL; | |
| 101 | |
| 102 username = gaim_proxy_info_get_username(gpi); | |
| 103 password = gaim_proxy_info_get_password(gpi); | |
| 104 | |
| 105 if (username != NULL) { | |
| 106 char *tmp; | |
| 107 auth = g_strdup_printf("%s:%s", username, password ? password : ""); | |
| 11137 | 108 tmp = gaim_base64_encode((const guchar *)auth, strlen(auth)); |
| 10568 | 109 g_free(auth); |
| 110 auth = g_strdup_printf("Proxy-Authorization: Basic %s\r\n", tmp); | |
| 111 g_free(tmp); | |
| 112 } | |
| 113 | |
| 114 return auth; | |
| 115 } | |
| 116 | |
| 10481 | 117 static ssize_t |
| 118 write_raw(MsnHttpConn *httpconn, const char *header, | |
| 119 const char *body, size_t body_len) | |
| 10463 | 120 { |
| 10481 | 121 char *buf; |
| 122 size_t buf_len; | |
| 10463 | 123 |
| 124 ssize_t s; | |
| 125 ssize_t res; /* result of the write operation */ | |
| 126 | |
| 127 #ifdef MSN_DEBUG_HTTP | |
| 10481 | 128 gaim_debug_misc("msn", "Writing HTTP (header): {%s}\n", header); |
| 10463 | 129 #endif |
| 130 | |
| 10481 | 131 buf = g_strdup_printf("%s\r\n", header); |
| 132 buf_len = strlen(buf); | |
| 133 | |
| 134 if (body != NULL) | |
| 135 { | |
| 136 buf = g_realloc(buf, buf_len + body_len); | |
| 137 memcpy(buf + buf_len, body, body_len); | |
| 138 buf_len += body_len; | |
| 139 } | |
| 140 | |
| 10463 | 141 s = 0; |
| 142 | |
| 143 do | |
| 144 { | |
|
11340
e9d0d944b9d2
[gaim-migrate @ 13553]
Richard Laager <rlaager@wiktel.com>
parents:
11137
diff
changeset
|
145 res = write(httpconn->fd, buf + s, buf_len - s); |
| 10463 | 146 if (res >= 0) |
| 147 { | |
| 148 s += res; | |
| 149 } | |
| 150 else if (errno != EAGAIN) | |
| 151 { | |
| 10481 | 152 msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_WRITE); |
| 10463 | 153 return -1; |
| 154 } | |
| 10481 | 155 } while (s < buf_len); |
| 156 | |
| 157 g_free(buf); | |
| 10463 | 158 |
| 159 return s; | |
| 160 } | |
| 161 | |
|
12323
fc464a0abccc
[gaim-migrate @ 14627]
Richard Laager <rlaager@wiktel.com>
parents:
12213
diff
changeset
|
162 static void |
| 10463 | 163 msn_httpconn_poll(MsnHttpConn *httpconn) |
| 164 { | |
| 10481 | 165 char *header; |
| 10568 | 166 char *auth; |
| 10463 | 167 int r; |
| 168 | |
| 169 g_return_if_fail(httpconn != NULL); | |
| 170 | |
| 171 if (httpconn->waiting_response || | |
| 172 httpconn->queue != NULL) | |
| 173 { | |
| 174 return; | |
| 175 } | |
| 176 | |
| 10568 | 177 auth = msn_httpconn_proxy_auth(httpconn); |
| 178 | |
| 10481 | 179 header = g_strdup_printf( |
| 10463 | 180 "POST http://%s/gateway/gateway.dll?Action=poll&SessionID=%s HTTP/1.1\r\n" |
| 181 "Accept: */*\r\n" | |
| 182 "Accept-Language: en-us\r\n" | |
| 183 "User-Agent: MSMSGS\r\n" | |
| 184 "Host: %s\r\n" | |
| 185 "Proxy-Connection: Keep-Alive\r\n" | |
| 10568 | 186 "%s" /* Proxy auth */ |
| 10463 | 187 "Connection: Keep-Alive\r\n" |
| 188 "Pragma: no-cache\r\n" | |
| 189 "Content-Type: application/x-msn-messenger\r\n" | |
| 10481 | 190 "Content-Length: 0\r\n", |
| 10463 | 191 httpconn->host, |
| 192 httpconn->full_session_id, | |
| 10568 | 193 httpconn->host, |
| 194 auth ? auth : ""); | |
| 195 | |
| 196 if (auth != NULL) | |
| 197 g_free(auth); | |
| 10463 | 198 |
| 10481 | 199 r = write_raw(httpconn, header, NULL, -1); |
| 10463 | 200 |
| 10481 | 201 g_free(header); |
| 10463 | 202 |
| 203 if (r > 0) | |
| 204 { | |
| 205 httpconn->waiting_response = TRUE; | |
| 206 httpconn->dirty = FALSE; | |
| 207 } | |
| 208 } | |
| 209 | |
| 210 static gboolean | |
| 10543 | 211 do_poll(gpointer data) |
| 10463 | 212 { |
| 213 MsnHttpConn *httpconn; | |
| 214 | |
| 215 httpconn = data; | |
| 216 | |
| 10568 | 217 g_return_val_if_fail(httpconn != NULL, TRUE); |
| 218 | |
| 10463 | 219 #if 0 |
| 220 gaim_debug_info("msn", "polling from %s\n", httpconn->session_id); | |
| 221 #endif | |
| 222 | |
| 10568 | 223 if ((httpconn->host == NULL) || (httpconn->full_session_id == NULL)) |
| 224 { | |
| 225 gaim_debug_warning("msn", "Attempted HTTP poll before session is established\n"); | |
| 226 return TRUE; | |
| 227 } | |
| 228 | |
| 10463 | 229 if (httpconn->dirty) |
| 230 msn_httpconn_poll(httpconn); | |
| 231 | |
| 232 return TRUE; | |
| 233 } | |
| 234 | |
| 235 static void | |
| 236 connect_cb(gpointer data, gint source, GaimInputCondition cond) | |
| 237 { | |
| 238 MsnHttpConn *httpconn = data; | |
| 239 | |
| 240 httpconn->fd = source; | |
| 241 | |
| 242 if (source > 0) | |
| 243 { | |
| 244 httpconn->inpa = gaim_input_add(httpconn->fd, GAIM_INPUT_READ, | |
| 245 read_cb, data); | |
| 246 | |
| 10543 | 247 httpconn->timer = gaim_timeout_add(2000, do_poll, httpconn); |
| 10463 | 248 |
| 249 httpconn->waiting_response = FALSE; | |
| 250 msn_httpconn_process_queue(httpconn); | |
| 251 } | |
| 252 else | |
| 253 { | |
| 254 gaim_debug_error("msn", "HTTP: Connection error\n"); | |
| 10481 | 255 msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_CONNECT); |
| 10463 | 256 } |
| 257 } | |
| 258 | |
| 259 gboolean | |
| 260 msn_httpconn_connect(MsnHttpConn *httpconn, const char *host, int port) | |
| 261 { | |
| 262 int r; | |
| 263 | |
| 264 g_return_val_if_fail(httpconn != NULL, FALSE); | |
| 265 g_return_val_if_fail(host != NULL, FALSE); | |
| 266 g_return_val_if_fail(port > 0, FALSE); | |
| 267 | |
| 268 if (httpconn->connected) | |
| 269 msn_httpconn_disconnect(httpconn); | |
| 270 | |
| 271 r = gaim_proxy_connect(httpconn->session->account, | |
| 272 "gateway.messenger.hotmail.com", 80, connect_cb, | |
| 273 httpconn); | |
| 274 | |
| 275 if (r == 0) | |
| 276 { | |
| 277 httpconn->waiting_response = TRUE; | |
| 278 httpconn->connected = TRUE; | |
| 279 } | |
| 280 | |
| 281 return httpconn->connected; | |
| 282 } | |
| 283 | |
| 284 void | |
| 285 msn_httpconn_disconnect(MsnHttpConn *httpconn) | |
| 286 { | |
| 287 g_return_if_fail(httpconn != NULL); | |
| 10481 | 288 |
| 289 if (!httpconn->connected) | |
| 290 return; | |
| 10463 | 291 |
| 292 if (httpconn->timer) | |
| 293 gaim_timeout_remove(httpconn->timer); | |
| 294 | |
| 295 httpconn->timer = 0; | |
| 296 | |
| 297 if (httpconn->inpa > 0) | |
| 298 { | |
| 299 gaim_input_remove(httpconn->inpa); | |
| 300 httpconn->inpa = 0; | |
| 301 } | |
| 302 | |
| 303 close(httpconn->fd); | |
| 304 | |
| 305 httpconn->rx_buf = NULL; | |
| 306 httpconn->rx_len = 0; | |
| 307 | |
| 308 httpconn->connected = FALSE; | |
| 309 | |
| 310 /* msn_servconn_disconnect(httpconn->servconn); */ | |
| 311 } | |
| 312 | |
| 313 static void | |
| 314 read_cb(gpointer data, gint source, GaimInputCondition cond) | |
| 315 { | |
| 316 MsnHttpConn *httpconn; | |
| 317 MsnServConn *servconn; | |
| 318 MsnSession *session; | |
| 319 char buf[MSN_BUF_LEN]; | |
| 320 char *cur, *end, *old_rx_buf; | |
| 321 int len, cur_len; | |
| 322 char *result_msg = NULL; | |
| 323 size_t result_len = 0; | |
| 324 gboolean error; | |
| 325 | |
| 326 httpconn = data; | |
| 327 servconn = NULL; | |
| 328 session = httpconn->session; | |
| 329 | |
| 330 len = read(httpconn->fd, buf, sizeof(buf) - 1); | |
| 331 | |
| 332 if (len <= 0) | |
| 333 { | |
| 334 gaim_debug_error("msn", "HTTP: Read error\n"); | |
| 10481 | 335 msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_READ); |
| 10463 | 336 |
| 337 return; | |
| 338 } | |
| 339 | |
| 340 buf[len] = '\0'; | |
| 341 | |
| 342 httpconn->rx_buf = g_realloc(httpconn->rx_buf, len + httpconn->rx_len + 1); | |
| 343 memcpy(httpconn->rx_buf + httpconn->rx_len, buf, len + 1); | |
| 344 httpconn->rx_len += len; | |
| 345 | |
| 346 if (!msn_httpconn_parse_data(httpconn, httpconn->rx_buf, httpconn->rx_len, | |
| 347 &result_msg, &result_len, &error)) | |
| 348 { | |
| 10568 | 349 /* We must wait for more input, or something went wrong */ |
| 350 if (error) | |
| 351 msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_READ); | |
| 10463 | 352 |
| 353 return; | |
| 354 } | |
| 355 | |
| 356 httpconn->servconn->processing = FALSE; | |
| 357 | |
| 358 servconn = httpconn->servconn; | |
| 359 | |
| 360 if (error) | |
| 361 { | |
| 362 gaim_debug_error("msn", "HTTP: Special error\n"); | |
| 10481 | 363 msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_READ); |
| 10463 | 364 |
| 365 return; | |
| 366 } | |
| 367 | |
| 10533 | 368 if (httpconn->rx_buf != NULL) |
| 369 g_free(httpconn->rx_buf); | |
| 370 httpconn->rx_buf = NULL; | |
| 371 httpconn->rx_len = 0; | |
| 372 | |
| 10463 | 373 if (result_len == 0) |
| 374 { | |
| 375 /* Nothing to do here */ | |
| 376 #if 0 | |
| 377 gaim_debug_info("msn", "HTTP: nothing to do here\n"); | |
| 378 #endif | |
| 10481 | 379 g_free(result_msg); |
| 10463 | 380 return; |
| 381 } | |
| 382 | |
| 10533 | 383 if (servconn->rx_buf != NULL) |
| 384 g_free(servconn->rx_buf); | |
| 10463 | 385 servconn->rx_buf = result_msg; |
| 386 servconn->rx_len = result_len; | |
| 387 | |
| 388 end = old_rx_buf = servconn->rx_buf; | |
| 389 | |
| 390 servconn->processing = TRUE; | |
| 391 | |
| 392 do | |
| 393 { | |
| 394 cur = end; | |
| 395 | |
| 396 if (servconn->payload_len) | |
| 397 { | |
| 398 if (servconn->payload_len > servconn->rx_len) | |
| 399 /* The payload is still not complete. */ | |
| 400 break; | |
| 401 | |
| 402 cur_len = servconn->payload_len; | |
| 403 end += cur_len; | |
| 404 } | |
| 405 else | |
| 406 { | |
| 407 end = strstr(cur, "\r\n"); | |
| 408 | |
| 409 if (end == NULL) | |
| 410 /* The command is still not complete. */ | |
| 411 break; | |
| 412 | |
| 413 *end = '\0'; | |
| 414 end += 2; | |
| 415 cur_len = end - cur; | |
| 416 } | |
| 417 | |
| 418 servconn->rx_len -= cur_len; | |
| 419 | |
| 420 if (servconn->payload_len) | |
| 421 { | |
| 422 msn_cmdproc_process_payload(servconn->cmdproc, cur, cur_len); | |
| 423 servconn->payload_len = 0; | |
| 424 } | |
| 425 else | |
| 426 { | |
| 427 msn_cmdproc_process_cmd_text(servconn->cmdproc, cur); | |
| 428 } | |
| 429 } while (servconn->connected && servconn->rx_len > 0); | |
| 430 | |
| 431 if (servconn->connected) | |
| 432 { | |
| 433 if (servconn->rx_len > 0) | |
| 434 servconn->rx_buf = g_memdup(cur, servconn->rx_len); | |
| 435 else | |
| 436 servconn->rx_buf = NULL; | |
| 437 } | |
| 438 | |
| 439 servconn->processing = FALSE; | |
| 440 | |
| 441 if (servconn->wasted) | |
| 442 msn_servconn_destroy(servconn); | |
| 443 | |
| 444 g_free(old_rx_buf); | |
| 445 } | |
| 446 | |
| 447 void | |
| 448 msn_httpconn_process_queue(MsnHttpConn *httpconn) | |
| 449 { | |
| 450 if (httpconn->queue != NULL) | |
| 451 { | |
| 452 MsnHttpQueueData *queue_data; | |
| 453 | |
| 454 queue_data = (MsnHttpQueueData *)httpconn->queue->data; | |
| 455 | |
| 456 httpconn->queue = g_list_remove(httpconn->queue, queue_data); | |
| 457 | |
| 458 msn_httpconn_write(queue_data->httpconn, | |
| 10481 | 459 queue_data->data, |
| 10463 | 460 queue_data->size); |
| 461 | |
| 10481 | 462 g_free(queue_data->data); |
| 10463 | 463 g_free(queue_data); |
| 464 } | |
| 465 else | |
| 466 { | |
| 467 httpconn->dirty = TRUE; | |
| 468 } | |
| 469 } | |
| 470 | |
| 471 size_t | |
| 10481 | 472 msn_httpconn_write(MsnHttpConn *httpconn, const char *data, size_t size) |
| 10463 | 473 { |
| 474 char *params; | |
| 10481 | 475 char *header; |
| 10568 | 476 char *auth; |
| 10463 | 477 gboolean first; |
| 478 const char *server_types[] = { "NS", "SB" }; | |
| 479 const char *server_type; | |
| 480 size_t r; /* result of the write operation */ | |
| 481 char *host; | |
| 482 MsnServConn *servconn; | |
| 483 | |
| 484 /* TODO: remove http data from servconn */ | |
| 485 | |
| 486 g_return_val_if_fail(httpconn != NULL, 0); | |
| 10481 | 487 g_return_val_if_fail(data != NULL, 0); |
| 10463 | 488 g_return_val_if_fail(size > 0, 0); |
| 489 | |
| 490 servconn = httpconn->servconn; | |
| 491 | |
| 492 if (httpconn->waiting_response) | |
| 493 { | |
| 494 MsnHttpQueueData *queue_data = g_new0(MsnHttpQueueData, 1); | |
| 495 | |
| 496 queue_data->httpconn = httpconn; | |
| 10481 | 497 queue_data->data = g_memdup(data, size); |
| 10463 | 498 queue_data->size = size; |
| 499 | |
| 500 httpconn->queue = g_list_append(httpconn->queue, queue_data); | |
| 501 /* httpconn->dirty = TRUE; */ | |
| 502 | |
| 503 /* servconn->processing = TRUE; */ | |
| 504 | |
| 505 return size; | |
| 506 } | |
| 507 | |
| 508 first = httpconn->virgin; | |
| 509 server_type = server_types[servconn->type]; | |
| 510 | |
| 511 if (first) | |
| 512 { | |
| 513 host = "gateway.messenger.hotmail.com"; | |
| 514 | |
| 515 /* The first time servconn->host is the host we should connect to. */ | |
| 516 params = g_strdup_printf("Action=open&Server=%s&IP=%s", | |
| 517 server_type, | |
| 518 servconn->host); | |
| 519 } | |
| 520 else | |
| 521 { | |
| 522 /* The rest of the times servconn->host is the gateway host. */ | |
| 523 host = httpconn->host; | |
| 524 | |
| 10568 | 525 if (host == NULL || httpconn->full_session_id == NULL) |
| 526 { | |
| 527 gaim_debug_warning("msn", "Attempted HTTP write before session is established\n"); | |
| 528 return -1; | |
| 529 } | |
| 530 | |
| 10463 | 531 params = g_strdup_printf("SessionID=%s", |
| 532 httpconn->full_session_id); | |
| 533 } | |
| 534 | |
| 10568 | 535 auth = msn_httpconn_proxy_auth(httpconn); |
| 536 | |
| 10481 | 537 header = g_strdup_printf( |
| 10463 | 538 "POST http://%s/gateway/gateway.dll?%s HTTP/1.1\r\n" |
| 539 "Accept: */*\r\n" | |
| 540 "Accept-Language: en-us\r\n" | |
| 541 "User-Agent: MSMSGS\r\n" | |
| 542 "Host: %s\r\n" | |
| 543 "Proxy-Connection: Keep-Alive\r\n" | |
| 10568 | 544 "%s" /* Proxy auth */ |
| 10463 | 545 "Connection: Keep-Alive\r\n" |
| 546 "Pragma: no-cache\r\n" | |
| 547 "Content-Type: application/x-msn-messenger\r\n" | |
| 10481 | 548 "Content-Length: %d\r\n", |
| 10463 | 549 host, |
| 550 params, | |
| 551 host, | |
| 10568 | 552 auth ? auth : "", |
| 10463 | 553 (int)size); |
| 554 | |
| 555 g_free(params); | |
| 556 | |
| 10568 | 557 if (auth != NULL) |
| 558 g_free(auth); | |
| 559 | |
| 10481 | 560 r = write_raw(httpconn, header, data, size); |
| 10463 | 561 |
| 10481 | 562 g_free(header); |
| 10463 | 563 |
| 564 if (r > 0) | |
| 565 { | |
| 566 httpconn->virgin = FALSE; | |
| 567 httpconn->waiting_response = TRUE; | |
| 568 httpconn->dirty = FALSE; | |
| 569 } | |
| 570 | |
| 571 return r; | |
| 572 } | |
| 573 | |
| 574 gboolean | |
| 575 msn_httpconn_parse_data(MsnHttpConn *httpconn, const char *buf, | |
| 576 size_t size, char **ret_buf, size_t *ret_size, | |
| 577 gboolean *error) | |
| 578 { | |
| 579 GaimConnection *gc; | |
| 580 const char *s, *c; | |
| 10481 | 581 char *header, *body; |
| 10463 | 582 const char *body_start; |
| 583 char *tmp; | |
| 584 size_t body_len = 0; | |
| 585 gboolean wasted = FALSE; | |
| 586 | |
| 587 g_return_val_if_fail(httpconn != NULL, FALSE); | |
| 588 g_return_val_if_fail(buf != NULL, FALSE); | |
| 589 g_return_val_if_fail(size > 0, FALSE); | |
| 590 g_return_val_if_fail(ret_buf != NULL, FALSE); | |
| 591 g_return_val_if_fail(ret_size != NULL, FALSE); | |
| 592 g_return_val_if_fail(error != NULL, FALSE); | |
| 593 | |
| 594 #if 0 | |
| 595 gaim_debug_info("msn", "HTTP: parsing data {%s}\n", buf); | |
| 596 #endif | |
| 597 | |
| 598 httpconn->waiting_response = FALSE; | |
| 599 | |
| 600 gc = gaim_account_get_connection(httpconn->session->account); | |
| 601 | |
| 602 /* Healthy defaults. */ | |
| 603 body = NULL; | |
| 604 | |
| 605 *ret_buf = NULL; | |
| 606 *ret_size = 0; | |
| 607 *error = FALSE; | |
| 608 | |
| 609 /* First, some tests to see if we have a full block of stuff. */ | |
| 610 if (((strncmp(buf, "HTTP/1.1 200 OK\r\n", 17) != 0) && | |
| 611 (strncmp(buf, "HTTP/1.1 100 Continue\r\n", 23) != 0)) && | |
| 612 ((strncmp(buf, "HTTP/1.0 200 OK\r\n", 17) != 0) && | |
| 613 (strncmp(buf, "HTTP/1.0 100 Continue\r\n", 23) != 0))) | |
| 614 { | |
| 615 *error = TRUE; | |
| 616 | |
| 617 return FALSE; | |
| 618 } | |
| 619 | |
| 620 if (strncmp(buf, "HTTP/1.1 100 Continue\r\n", 23) == 0) | |
| 621 { | |
| 622 if ((s = strstr(buf, "\r\n\r\n")) == NULL) | |
| 623 return FALSE; | |
| 624 | |
| 625 s += 4; | |
| 626 | |
| 627 if (*s == '\0') | |
| 628 { | |
| 629 *ret_buf = g_strdup(""); | |
| 630 *ret_size = 0; | |
| 631 | |
| 632 msn_httpconn_process_queue(httpconn); | |
| 633 | |
| 634 return TRUE; | |
| 635 } | |
| 636 | |
| 637 buf = s; | |
| 638 size -= (s - buf); | |
| 639 } | |
| 640 | |
| 641 if ((s = strstr(buf, "\r\n\r\n")) == NULL) | |
| 642 return FALSE; | |
| 643 | |
| 10718 | 644 s += 4; /* Skip \r\n */ |
| 10481 | 645 header = g_strndup(buf, s - buf); |
| 10463 | 646 body_start = s; |
| 647 body_len = size - (body_start - buf); | |
| 648 | |
| 12213 | 649 if ((s = gaim_strcasestr(header, "Content-Length: ")) != NULL) |
| 10463 | 650 { |
| 651 int tmp_len; | |
| 652 | |
| 653 s += strlen("Content-Length: "); | |
| 654 | |
| 655 if ((c = strchr(s, '\r')) == NULL) | |
| 656 { | |
| 10481 | 657 g_free(header); |
| 10463 | 658 |
| 659 return FALSE; | |
| 660 } | |
| 661 | |
| 662 tmp = g_strndup(s, c - s); | |
| 663 tmp_len = atoi(tmp); | |
| 664 g_free(tmp); | |
| 665 | |
| 666 if (body_len != tmp_len) | |
| 667 { | |
| 10481 | 668 g_free(header); |
| 10463 | 669 |
| 670 #if 0 | |
| 671 gaim_debug_warning("msn", | |
| 672 "body length (%d) != content length (%d)\n", | |
| 673 body_len, tmp_len); | |
| 674 #endif | |
| 675 | |
| 676 return FALSE; | |
| 677 } | |
| 678 } | |
| 679 | |
| 10481 | 680 body = g_malloc0(body_len + 1); |
| 681 memcpy(body, body_start, body_len); | |
| 10463 | 682 |
| 683 #ifdef MSN_DEBUG_HTTP | |
| 10481 | 684 gaim_debug_misc("msn", "Incoming HTTP buffer (header): {%s\r\n}\n", |
| 685 header); | |
| 10463 | 686 #endif |
| 687 | |
| 688 /* Now we should be able to process the data. */ | |
| 12213 | 689 if ((s = gaim_strcasestr(header, "X-MSN-Messenger: ")) != NULL) |
| 10463 | 690 { |
| 691 char *full_session_id, *gw_ip, *session_action; | |
| 692 char *t, *session_id; | |
| 693 char **elems, **cur, **tokens; | |
| 694 | |
| 695 full_session_id = gw_ip = session_action = NULL; | |
| 696 | |
| 697 s += strlen("X-MSN-Messenger: "); | |
| 698 | |
| 699 if ((c = strchr(s, '\r')) == NULL) | |
| 700 { | |
| 10481 | 701 msn_session_set_error(httpconn->session, |
| 702 MSN_ERROR_HTTP_MALFORMED, NULL); | |
| 10463 | 703 gaim_debug_error("msn", "Malformed X-MSN-Messenger field.\n{%s}", |
| 704 buf); | |
| 705 | |
| 10481 | 706 g_free(body); |
| 10463 | 707 return FALSE; |
| 708 } | |
| 709 | |
| 710 tmp = g_strndup(s, c - s); | |
| 711 | |
| 712 elems = g_strsplit(tmp, "; ", 0); | |
| 713 | |
| 714 for (cur = elems; *cur != NULL; cur++) | |
| 715 { | |
| 716 tokens = g_strsplit(*cur, "=", 2); | |
| 717 | |
| 718 if (strcmp(tokens[0], "SessionID") == 0) | |
| 719 full_session_id = tokens[1]; | |
| 720 else if (strcmp(tokens[0], "GW-IP") == 0) | |
| 721 gw_ip = tokens[1]; | |
| 722 else if (strcmp(tokens[0], "Session") == 0) | |
| 723 session_action = tokens[1]; | |
| 724 | |
| 725 g_free(tokens[0]); | |
| 726 /* Don't free each of the tokens, only the array. */ | |
| 727 g_free(tokens); | |
| 728 } | |
| 729 | |
| 730 g_strfreev(elems); | |
| 731 | |
| 732 g_free(tmp); | |
| 733 | |
| 734 if ((session_action != NULL) && (strcmp(session_action, "close") == 0)) | |
| 735 wasted = TRUE; | |
| 736 | |
| 737 g_free(session_action); | |
| 738 | |
| 739 t = strchr(full_session_id, '.'); | |
| 740 session_id = g_strndup(full_session_id, t - full_session_id); | |
| 741 | |
| 742 if (!wasted) | |
| 743 { | |
| 10504 | 744 if (httpconn->full_session_id != NULL) |
| 10463 | 745 g_free(httpconn->full_session_id); |
| 746 | |
| 747 httpconn->full_session_id = full_session_id; | |
| 748 | |
| 10504 | 749 if (httpconn->session_id != NULL) |
| 10463 | 750 g_free(httpconn->session_id); |
| 751 | |
| 752 httpconn->session_id = session_id; | |
| 753 | |
| 10504 | 754 if (httpconn->host != NULL) |
| 10463 | 755 g_free(httpconn->host); |
| 756 | |
| 757 httpconn->host = gw_ip; | |
| 758 } | |
| 759 else | |
| 760 { | |
| 761 MsnServConn *servconn; | |
| 762 | |
| 763 /* It's going to die. */ | |
| 10504 | 764 /* poor thing */ |
| 10463 | 765 |
| 766 servconn = httpconn->servconn; | |
| 767 | |
| 10533 | 768 /* I'll be honest, I don't fully understand all this, but this |
| 769 * causes crashes, Stu. */ | |
| 770 /* if (servconn != NULL) | |
| 771 servconn->wasted = TRUE; */ | |
| 10463 | 772 |
| 773 g_free(full_session_id); | |
| 10504 | 774 g_free(session_id); |
| 10463 | 775 g_free(gw_ip); |
| 776 } | |
| 777 } | |
| 778 | |
| 10481 | 779 g_free(header); |
| 10463 | 780 |
| 781 *ret_buf = body; | |
| 782 *ret_size = body_len; | |
| 783 | |
| 784 msn_httpconn_process_queue(httpconn); | |
| 785 | |
| 786 return TRUE; | |
| 787 } |
