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 }