Mercurial > pidgin
comparison libpurple/protocols/yahoo/yahoo_picture.c @ 23296:78c832e03324
Fix setting buddy icons on yahoo. It looks like this must have been broken by
them starting to actually check the length field in the YMSG header. This also
now uses code stolen from kopete to generate the buddy icon checksum (it isn't
strictly necessary as far as I can tell, but it seems better). Fixes #4795.
author | Daniel Atallah <daniel.atallah@gmail.com> |
---|---|
date | Fri, 06 Jun 2008 06:41:07 +0000 |
parents | 49982266aa0f |
children | 25975a6a5b63 |
comparison
equal
deleted
inserted
replaced
23295:f8e0a90dd153 | 23296:78c832e03324 |
---|---|
242 } | 242 } |
243 l = l->next; | 243 l = l->next; |
244 } | 244 } |
245 | 245 |
246 if (url) { | 246 if (url) { |
247 if (yd->picture_url) | 247 g_free(yd->picture_url); |
248 g_free(yd->picture_url); | |
249 yd->picture_url = g_strdup(url); | 248 yd->picture_url = g_strdup(url); |
250 purple_account_set_string(account, YAHOO_PICURL_SETTING, url); | 249 purple_account_set_string(account, YAHOO_PICURL_SETTING, url); |
251 purple_account_set_int(account, YAHOO_PICCKSUM_SETTING, yd->picture_checksum); | 250 purple_account_set_int(account, YAHOO_PICCKSUM_SETTING, yd->picture_checksum); |
251 yahoo_send_picture_checksum(gc); | |
252 yahoo_send_picture_update(gc, 2); | 252 yahoo_send_picture_update(gc, 2); |
253 yahoo_send_picture_checksum(gc); | |
254 } | 253 } |
255 } | 254 } |
256 | 255 |
257 void yahoo_process_avatar_update(PurpleConnection *gc, struct yahoo_packet *pkt) | 256 void yahoo_process_avatar_update(PurpleConnection *gc, struct yahoo_packet *pkt) |
258 { | 257 { |
400 | 399 |
401 ret = read(d->fd, buf, sizeof(buf)); | 400 ret = read(d->fd, buf, sizeof(buf)); |
402 | 401 |
403 if (ret < 0 && errno == EAGAIN) | 402 if (ret < 0 && errno == EAGAIN) |
404 return; | 403 return; |
405 else if (ret <= 0) | 404 else if (ret <= 0) { |
405 purple_debug_info("yahoo", "Buddy icon upload response (%d) bytes (> ~400 indicates failure):\n%.*s\n", | |
406 d->str->len, d->str->len, d->str->str); | |
407 | |
406 yahoo_buddy_icon_upload_data_free(d); | 408 yahoo_buddy_icon_upload_data_free(d); |
409 return; | |
410 } | |
411 | |
412 g_string_append_len(d->str, buf, ret); | |
407 } | 413 } |
408 | 414 |
409 static void yahoo_buddy_icon_upload_pending(gpointer data, gint source, PurpleInputCondition condition) | 415 static void yahoo_buddy_icon_upload_pending(gpointer data, gint source, PurpleInputCondition condition) |
410 { | 416 { |
411 struct yahoo_buddy_icon_upload_data *d = data; | 417 struct yahoo_buddy_icon_upload_data *d = data; |
419 | 425 |
420 wrote = write(d->fd, d->str->str + d->pos, d->str->len - d->pos); | 426 wrote = write(d->fd, d->str->str + d->pos, d->str->len - d->pos); |
421 if (wrote < 0 && errno == EAGAIN) | 427 if (wrote < 0 && errno == EAGAIN) |
422 return; | 428 return; |
423 if (wrote <= 0) { | 429 if (wrote <= 0) { |
430 purple_debug_info("yahoo", "Error uploading buddy icon.\n"); | |
424 yahoo_buddy_icon_upload_data_free(d); | 431 yahoo_buddy_icon_upload_data_free(d); |
425 return; | 432 return; |
426 } | 433 } |
427 d->pos += wrote; | 434 d->pos += wrote; |
428 if (d->pos >= d->str->len) { | 435 if (d->pos >= d->str->len) { |
429 purple_debug_misc("yahoo", "Finished uploading buddy icon.\n"); | 436 purple_debug_misc("yahoo", "Finished uploading buddy icon.\n"); |
430 purple_input_remove(d->watcher); | 437 purple_input_remove(d->watcher); |
438 /* Clean out the sent buffer and reuse it to read the result */ | |
439 g_string_free(d->str, TRUE); | |
440 d->str = g_string_new(""); | |
431 d->watcher = purple_input_add(d->fd, PURPLE_INPUT_READ, yahoo_buddy_icon_upload_reading, d); | 441 d->watcher = purple_input_add(d->fd, PURPLE_INPUT_READ, yahoo_buddy_icon_upload_reading, d); |
432 } | 442 } |
433 } | 443 } |
434 | 444 |
435 static void yahoo_buddy_icon_upload_connected(gpointer data, gint source, const gchar *error_message) | 445 static void yahoo_buddy_icon_upload_connected(gpointer data, gint source, const gchar *error_message) |
436 { | 446 { |
437 struct yahoo_buddy_icon_upload_data *d = data; | 447 struct yahoo_buddy_icon_upload_data *d = data; |
438 struct yahoo_packet *pkt; | 448 struct yahoo_packet *pkt; |
439 gchar *size, *header; | 449 gchar *tmp, *header; |
440 guchar *pkt_buf; | 450 guchar *pkt_buf; |
441 const char *host; | 451 const char *host; |
442 int port; | 452 int port; |
443 size_t content_length, pkt_buf_len; | 453 gsize pkt_buf_len; |
444 PurpleConnection *gc; | 454 PurpleConnection *gc = d->gc; |
445 PurpleAccount *account; | 455 PurpleAccount *account; |
446 struct yahoo_data *yd; | 456 struct yahoo_data *yd; |
447 | 457 gboolean use_whole_url = FALSE; |
448 gc = d->gc; | 458 |
449 account = purple_connection_get_account(gc); | 459 account = purple_connection_get_account(gc); |
450 yd = gc->proto_data; | 460 yd = gc->proto_data; |
451 | 461 |
452 /* Buddy icon connect is now complete; clear the PurpleProxyConnectData */ | 462 /* Buddy icon connect is now complete; clear the PurpleProxyConnectData */ |
453 yd->buddy_icon_connect_data = NULL; | 463 yd->buddy_icon_connect_data = NULL; |
455 if (source < 0) { | 465 if (source < 0) { |
456 purple_debug_error("yahoo", "Buddy icon upload failed: %s\n", error_message); | 466 purple_debug_error("yahoo", "Buddy icon upload failed: %s\n", error_message); |
457 yahoo_buddy_icon_upload_data_free(d); | 467 yahoo_buddy_icon_upload_data_free(d); |
458 return; | 468 return; |
459 } | 469 } |
460 | 470 /* use whole URL if using HTTP Proxy */ |
461 pkt = yahoo_packet_new(0xc2, YAHOO_STATUS_AVAILABLE, yd->session_id); | 471 if ((gc->account->proxy_info) |
462 | 472 && (gc->account->proxy_info->type == PURPLE_PROXY_HTTP)) |
463 size = g_strdup_printf("%" G_GSIZE_FORMAT, d->str->len); | 473 use_whole_url = TRUE; |
474 | |
475 pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_UPLOAD, YAHOO_STATUS_AVAILABLE, yd->session_id); | |
476 | |
477 tmp = g_strdup_printf("%" G_GSIZE_FORMAT, d->str->len); | |
464 /* 1 = me, 38 = expire time(?), 0 = me, 28 = size, 27 = filename, 14 = NULL, 29 = data */ | 478 /* 1 = me, 38 = expire time(?), 0 = me, 28 = size, 27 = filename, 14 = NULL, 29 = data */ |
465 yahoo_packet_hash_str(pkt, 1, purple_connection_get_display_name(gc)); | 479 yahoo_packet_hash_str(pkt, 1, purple_connection_get_display_name(gc)); |
466 yahoo_packet_hash_str(pkt, 38, "604800"); /* time til expire */ | 480 yahoo_packet_hash_str(pkt, 38, "604800"); /* time til expire */ |
467 purple_account_set_int(account, YAHOO_PICEXPIRE_SETTING, time(NULL) + 604800); | 481 purple_account_set_int(account, YAHOO_PICEXPIRE_SETTING, time(NULL) + 604800); |
468 yahoo_packet_hash_str(pkt, 0, purple_connection_get_display_name(gc)); | 482 yahoo_packet_hash_str(pkt, 0, purple_connection_get_display_name(gc)); |
469 yahoo_packet_hash_str(pkt, 28, size); | 483 yahoo_packet_hash_str(pkt, 28, tmp); |
470 g_free(size); | 484 g_free(tmp); |
471 yahoo_packet_hash_str(pkt, 27, d->filename); | 485 yahoo_packet_hash_str(pkt, 27, d->filename); |
472 yahoo_packet_hash_str(pkt, 14, ""); | 486 yahoo_packet_hash_str(pkt, 14, ""); |
473 | 487 /* 4 padding for the 29 key name */ |
474 content_length = YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt); | 488 pkt_buf_len = yahoo_packet_build(pkt, 4, FALSE, yd->jp, &pkt_buf); |
489 yahoo_packet_free(pkt); | |
490 | |
491 /* header + packet + "29" + 0xc0 + 0x80) + pictureblob */ | |
475 | 492 |
476 host = purple_account_get_string(account, "xfer_host", YAHOO_XFER_HOST); | 493 host = purple_account_get_string(account, "xfer_host", YAHOO_XFER_HOST); |
477 port = purple_account_get_int(account, "xfer_port", YAHOO_XFER_PORT); | 494 port = purple_account_get_int(account, "xfer_port", YAHOO_XFER_PORT); |
478 header = g_strdup_printf( | 495 tmp = g_strdup_printf("%s:%d", host, port); |
479 "POST http://%s:%d/notifyft HTTP/1.0\r\n" | 496 header = g_strdup_printf("POST %s%s/notifyft HTTP/1.1\r\n" |
480 "Content-length: %" G_GSIZE_FORMAT "\r\n" | 497 "User-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\n" |
481 "Host: %s:%d\r\n" | 498 "Cookie: T=%s; Y=%s\r\n" |
482 "Cookie: Y=%s; T=%s\r\n" | 499 "Host: %s\r\n" |
483 "\r\n", | 500 "Content-Length: %" G_GSIZE_FORMAT "\r\n" |
484 host, port, content_length + 4 + d->str->len, | 501 "Cache-Control: no-cache\r\n\r\n", |
485 host, port, yd->cookie_y, yd->cookie_t); | 502 use_whole_url ? "http://" : "", use_whole_url ? tmp : "", |
503 yd->cookie_t, yd->cookie_y, | |
504 tmp, | |
505 pkt_buf_len + 4 + d->str->len); | |
506 g_free(tmp); | |
486 | 507 |
487 /* There's no magic here, we just need to prepend in reverse order */ | 508 /* There's no magic here, we just need to prepend in reverse order */ |
488 g_string_prepend(d->str, "29\xc0\x80"); | 509 g_string_prepend(d->str, "29\xc0\x80"); |
489 | 510 |
490 pkt_buf_len = yahoo_packet_build(pkt, 8, FALSE, yd->jp, &pkt_buf); | |
491 yahoo_packet_free(pkt); | |
492 g_string_prepend_len(d->str, (char *)pkt_buf, pkt_buf_len); | 511 g_string_prepend_len(d->str, (char *)pkt_buf, pkt_buf_len); |
493 g_free(pkt_buf); | 512 g_free(pkt_buf); |
494 | 513 |
495 g_string_prepend(d->str, header); | 514 g_string_prepend(d->str, header); |
496 g_free(header); | 515 g_free(header); |
516 | |
517 purple_debug_info("yahoo", "Buddy icon upload data:\n%.*s\n", d->str->len, d->str->str); | |
497 | 518 |
498 d->fd = source; | 519 d->fd = source; |
499 d->watcher = purple_input_add(d->fd, PURPLE_INPUT_WRITE, yahoo_buddy_icon_upload_pending, d); | 520 d->watcher = purple_input_add(d->fd, PURPLE_INPUT_WRITE, yahoo_buddy_icon_upload_pending, d); |
500 | 521 |
501 yahoo_buddy_icon_upload_pending(d, d->fd, PURPLE_INPUT_WRITE); | 522 yahoo_buddy_icon_upload_pending(d, d->fd, PURPLE_INPUT_WRITE); |
523 purple_debug_error("yahoo", "Uploading our buddy icon failed to connect.\n"); | 544 purple_debug_error("yahoo", "Uploading our buddy icon failed to connect.\n"); |
524 yahoo_buddy_icon_upload_data_free(d); | 545 yahoo_buddy_icon_upload_data_free(d); |
525 } | 546 } |
526 } | 547 } |
527 | 548 |
549 static int yahoo_buddy_icon_calculate_checksum(const guchar *data, gsize len) | |
550 { | |
551 /* This code is borrowed from Kopete, which seems to be managing to calculate | |
552 checksums in such a manner that Yahoo!'s servers are happy */ | |
553 | |
554 const guchar *p = data; | |
555 int checksum = 0, g, i = len; | |
556 | |
557 while(i--) { | |
558 checksum = (checksum << 4) + *p++; | |
559 | |
560 if((g = (checksum & 0xf0000000)) != 0) | |
561 checksum ^= g >> 23; | |
562 | |
563 checksum &= ~g; | |
564 } | |
565 | |
566 purple_debug_misc("yahoo", "Calculated buddy icon checksum: %d", checksum); | |
567 | |
568 return checksum; | |
569 } | |
570 | |
528 void yahoo_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img) | 571 void yahoo_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img) |
529 { | 572 { |
530 struct yahoo_data *yd = gc->proto_data; | 573 struct yahoo_data *yd = gc->proto_data; |
531 PurpleAccount *account = gc->account; | 574 PurpleAccount *account = gc->account; |
532 | 575 |
533 if (img == NULL) { | 576 if (img == NULL) { |
534 g_free(yd->picture_url); | 577 g_free(yd->picture_url); |
535 yd->picture_url = NULL; | 578 yd->picture_url = NULL; |
579 | |
580 /* TODO: don't we have to clear it on the server too?! */ | |
536 | 581 |
537 purple_account_set_string(account, YAHOO_PICURL_SETTING, NULL); | 582 purple_account_set_string(account, YAHOO_PICURL_SETTING, NULL); |
538 purple_account_set_int(account, YAHOO_PICCKSUM_SETTING, 0); | 583 purple_account_set_int(account, YAHOO_PICCKSUM_SETTING, 0); |
539 purple_account_set_int(account, YAHOO_PICEXPIRE_SETTING, 0); | 584 purple_account_set_int(account, YAHOO_PICEXPIRE_SETTING, 0); |
540 if (yd->logged_in) | 585 if (yd->logged_in) |
547 GString *s = g_string_new_len(data, len); | 592 GString *s = g_string_new_len(data, len); |
548 struct yahoo_buddy_icon_upload_data *d; | 593 struct yahoo_buddy_icon_upload_data *d; |
549 int oldcksum = purple_account_get_int(account, YAHOO_PICCKSUM_SETTING, 0); | 594 int oldcksum = purple_account_get_int(account, YAHOO_PICCKSUM_SETTING, 0); |
550 int expire = purple_account_get_int(account, YAHOO_PICEXPIRE_SETTING, 0); | 595 int expire = purple_account_get_int(account, YAHOO_PICEXPIRE_SETTING, 0); |
551 const char *oldurl = purple_account_get_string(account, YAHOO_PICURL_SETTING, NULL); | 596 const char *oldurl = purple_account_get_string(account, YAHOO_PICURL_SETTING, NULL); |
552 char *iconfile; | 597 |
553 | 598 yd->picture_checksum = yahoo_buddy_icon_calculate_checksum(data, len); |
554 /* TODO: At some point, it'd be nice to fix this for real, or | |
555 * TODO: at least change it to be something like: | |
556 * TODO: purple_imgstore_get_filename(img); | |
557 * TODO: But it would be great if we knew how to calculate the | |
558 * TODO: Checksum correctly. */ | |
559 yd->picture_checksum = g_string_hash(s); | |
560 | 599 |
561 if ((yd->picture_checksum == oldcksum) && | 600 if ((yd->picture_checksum == oldcksum) && |
562 (expire > (time(NULL) + 60*60*24)) && oldurl) | 601 (expire > (time(NULL) + 60*60*24)) && oldurl) |
563 { | 602 { |
564 purple_debug_misc("yahoo", "buddy icon is up to date. Not reuploading.\n"); | 603 purple_debug_misc("yahoo", "buddy icon is up to date. Not reuploading.\n"); |
567 yd->picture_url = g_strdup(oldurl); | 606 yd->picture_url = g_strdup(oldurl); |
568 return; | 607 return; |
569 } | 608 } |
570 | 609 |
571 /* We use this solely for sending a filename to the server */ | 610 /* We use this solely for sending a filename to the server */ |
572 iconfile = g_strdup(purple_imgstore_get_filename(img)); | |
573 d = g_new0(struct yahoo_buddy_icon_upload_data, 1); | 611 d = g_new0(struct yahoo_buddy_icon_upload_data, 1); |
574 d->gc = gc; | 612 d->gc = gc; |
575 d->str = s; | 613 d->str = s; |
576 d->fd = -1; | 614 d->fd = -1; |
577 d->filename = iconfile; | 615 d->filename = g_strdup(purple_imgstore_get_filename(img)); |
578 | 616 |
579 if (!yd->logged_in) { | 617 if (!yd->logged_in) { |
580 yd->picture_upload_todo = d; | 618 yd->picture_upload_todo = d; |
581 return; | 619 return; |
582 } | 620 } |