Mercurial > pidgin
comparison src/protocols/yahoo/yahoo.c @ 3147:3805d9d344f2
[gaim-migrate @ 3163]
Please test this.
Yahoo changed it's authentication method and has started disallowing older clients (and third party clients using the older method) to connect to their servers. This impliments the new authentication method so that we won't get blocked from Yahoo, and Indian users (who have already been blocked) can connect again. It's not perfect--it should fail about 1 out of 16 times. It's an easy bug to find and fix, but I want to go to bed now. If you find yourself failing to connect more than this 1 of 16, please let me know, and I'll tell you how to help me debug it.
Thanks.
committer: Tailor Script <tailor@pidgin.im>
author | Sean Egan <seanegan@gmail.com> |
---|---|
date | Sun, 14 Apr 2002 07:50:48 +0000 |
parents | 793fb2e9d53b |
children | 2bb8b7380814 |
comparison
equal
deleted
inserted
replaced
3146:442ae0b81f08 | 3147:3805d9d344f2 |
---|---|
39 #include <ctype.h> | 39 #include <ctype.h> |
40 #include "multi.h" | 40 #include "multi.h" |
41 #include "prpl.h" | 41 #include "prpl.h" |
42 #include "gaim.h" | 42 #include "gaim.h" |
43 #include "proxy.h" | 43 #include "proxy.h" |
44 #include "md5.h" | |
44 | 45 |
45 extern char *yahoo_crypt(char *, char *); | 46 extern char *yahoo_crypt(char *, char *); |
46 | 47 |
47 #include "pixmaps/status-away.xpm" | 48 #include "pixmaps/status-away.xpm" |
48 #include "pixmaps/status-here.xpm" | 49 #include "pixmaps/status-here.xpm" |
80 #define YAHOO_DEBUG | 81 #define YAHOO_DEBUG |
81 | 82 |
82 #define USEROPT_MAIL 0 | 83 #define USEROPT_MAIL 0 |
83 | 84 |
84 #define USEROPT_PAGERHOST 3 | 85 #define USEROPT_PAGERHOST 3 |
85 #define YAHOO_PAGER_HOST "cs.yahoo.com" | 86 #define YAHOO_PAGER_HOST "scs.yahoo.com" |
86 #define USEROPT_PAGERPORT 4 | 87 #define USEROPT_PAGERPORT 4 |
87 #define YAHOO_PAGER_PORT 5050 | 88 #define YAHOO_PAGER_PORT 5050 |
88 | 89 |
89 enum yahoo_service { /* these are easier to see in hex */ | 90 enum yahoo_service { /* these are easier to see in hex */ |
90 YAHOO_SERVICE_LOGON = 1, | 91 YAHOO_SERVICE_LOGON = 1, |
120 YAHOO_SERVICE_GAMELOGON = 0x28, | 121 YAHOO_SERVICE_GAMELOGON = 0x28, |
121 YAHOO_SERVICE_GAMELOGOFF, | 122 YAHOO_SERVICE_GAMELOGOFF, |
122 YAHOO_SERVICE_GAMEMSG = 0x2a, | 123 YAHOO_SERVICE_GAMEMSG = 0x2a, |
123 YAHOO_SERVICE_FILETRANSFER = 0x46, | 124 YAHOO_SERVICE_FILETRANSFER = 0x46, |
124 YAHOO_SERVICE_NOTIFY = 0x4B, | 125 YAHOO_SERVICE_NOTIFY = 0x4B, |
126 YAHOO_SERVICE_AUTHRESP = 0x54, | |
125 YAHOO_SERVICE_LIST = 0x55, | 127 YAHOO_SERVICE_LIST = 0x55, |
128 YAHOO_SERVICE_AUTH = 0x57, | |
126 YAHOO_SERVICE_ADDBUDDY = 0x83, | 129 YAHOO_SERVICE_ADDBUDDY = 0x83, |
127 YAHOO_SERVICE_REMBUDDY = 0x84 | 130 YAHOO_SERVICE_REMBUDDY = 0x84 |
128 }; | 131 }; |
129 | 132 |
130 enum yahoo_status { | 133 enum yahoo_status { |
390 case 1: /* we don't get the full buddy list here. */ | 393 case 1: /* we don't get the full buddy list here. */ |
391 if (!yd->logged_in) { | 394 if (!yd->logged_in) { |
392 account_online(gc); | 395 account_online(gc); |
393 serv_finish_login(gc); | 396 serv_finish_login(gc); |
394 g_snprintf(gc->displayname, sizeof(gc->displayname), "%s", pair->value); | 397 g_snprintf(gc->displayname, sizeof(gc->displayname), "%s", pair->value); |
395 do_import(gc, NULL); | |
396 yd->logged_in = TRUE; | 398 yd->logged_in = TRUE; |
397 | 399 |
398 /* this requests the list. i have a feeling that this is very evil */ | 400 /* this requests the list. i have a feeling that this is very evil |
399 newpkt = yahoo_packet_new(YAHOO_SERVICE_LIST, YAHOO_STATUS_OFFLINE, 0); | 401 * |
400 yahoo_send_packet(yd, newpkt); | 402 * scs.yahoo.com sends you the list before this packet without it being |
401 yahoo_packet_free(newpkt); | 403 * requested |
402 } | 404 * |
405 * do_import(gc, NULL); | |
406 * newpkt = yahoo_packet_new(YAHOO_SERVICE_LIST, YAHOO_STATUS_OFFLINE, 0); | |
407 * yahoo_send_packet(yd, newpkt); | |
408 * yahoo_packet_free(newpkt); | |
409 */ | |
410 | |
411 } | |
403 break; | 412 break; |
404 case 8: /* how many online buddies we have */ | 413 case 8: /* how many online buddies we have */ |
405 break; | 414 break; |
406 case 7: /* the current buddy */ | 415 case 7: /* the current buddy */ |
407 name = pair->value; | 416 name = pair->value; |
424 } | 433 } |
425 if (g_hash_table_lookup(yd->games, name)) | 434 if (g_hash_table_lookup(yd->games, name)) |
426 gamestate = YAHOO_STATUS_GAME; | 435 gamestate = YAHOO_STATUS_GAME; |
427 if (state == YAHOO_STATUS_AVAILABLE) | 436 if (state == YAHOO_STATUS_AVAILABLE) |
428 serv_got_update(gc, name, 1, 0, 0, 0, gamestate, 0); | 437 serv_got_update(gc, name, 1, 0, 0, 0, gamestate, 0); |
429 else | 438 else |
430 serv_got_update(gc, name, 1, 0, 0, 0, (state << 2) | UC_UNAVAILABLE | gamestate, 0); | 439 serv_got_update(gc, name, 1, 0, 0, 0, (state << 2) | UC_UNAVAILABLE | gamestate, 0); |
431 if (state == YAHOO_STATUS_CUSTOM) { | 440 if (state == YAHOO_STATUS_CUSTOM) { |
432 gpointer val = g_hash_table_lookup(yd->hash, name); | 441 gpointer val = g_hash_table_lookup(yd->hash, name); |
433 if (val) { | 442 if (val) { |
434 g_free(val); | 443 g_free(val); |
468 l = l->next; | 477 l = l->next; |
469 | 478 |
470 if (pair->key != 87) | 479 if (pair->key != 87) |
471 continue; | 480 continue; |
472 | 481 |
482 do_import(gc, NULL); | |
473 lines = g_strsplit(pair->value, "\n", -1); | 483 lines = g_strsplit(pair->value, "\n", -1); |
474 for (tmp = lines; *tmp; tmp++) { | 484 for (tmp = lines; *tmp; tmp++) { |
475 split = g_strsplit(*tmp, ":", 2); | 485 split = g_strsplit(*tmp, ":", 2); |
476 if (!split) | 486 if (!split) |
477 continue; | 487 continue; |
664 connection_has_mail(gc, -1, from, subj, "http://mail.yahoo.com/"); | 674 connection_has_mail(gc, -1, from, subj, "http://mail.yahoo.com/"); |
665 g_free(from); | 675 g_free(from); |
666 } else | 676 } else |
667 connection_has_mail(gc, count, NULL, NULL, "http://mail.yahoo.com/"); | 677 connection_has_mail(gc, count, NULL, NULL, "http://mail.yahoo.com/"); |
668 } | 678 } |
679 /* This is the y64 alphabet... it's like base64, but has a . and a _ */ | |
680 char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._"; | |
681 | |
682 /* This is taken from Sylpheed by Hiroyuki Yamamoto. We have our own tobase64 function | |
683 * in util.c, but it has a bug I don't feel like finding right now ;) */ | |
684 void to_y64(unsigned char *out, const unsigned char *in, int inlen) | |
685 /* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */ | |
686 { | |
687 for (; inlen >= 3; inlen -= 3) | |
688 { | |
689 *out++ = base64digits[in[0] >> 2]; | |
690 *out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)]; | |
691 *out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)]; | |
692 *out++ = base64digits[in[2] & 0x3f]; | |
693 in += 3; | |
694 } | |
695 if (inlen > 0) | |
696 { | |
697 unsigned char fragment; | |
698 | |
699 *out++ = base64digits[in[0] >> 2]; | |
700 fragment = (in[0] << 4) & 0x30; | |
701 if (inlen > 1) | |
702 fragment |= in[1] >> 4; | |
703 *out++ = base64digits[fragment]; | |
704 *out++ = (inlen < 2) ? '-' : base64digits[(in[1] << 2) & 0x3c]; | |
705 *out++ = '-'; | |
706 } | |
707 *out = '\0'; | |
708 } | |
709 | |
710 static void yahoo_process_auth(struct gaim_connection *gc, struct yahoo_packet *pkt) | |
711 { | |
712 char *seed = NULL; | |
713 char *sn = NULL; | |
714 GSList *l = pkt->hash; | |
715 struct yahoo_data *yd = gc->proto_data; | |
716 | |
717 while (l) { | |
718 struct yahoo_pair *pair = l->data; | |
719 if (pair->key == 94) | |
720 seed = pair->value; | |
721 if (pair->key == 1) | |
722 sn = pair->value; | |
723 l = l->next; | |
724 } | |
725 | |
726 if (seed) { | |
727 struct yahoo_packet *pack; | |
728 | |
729 /* So, Yahoo has stopped supporting its older clients in India, and undoubtedly | |
730 * will soon do so in the rest of the world. | |
731 * | |
732 * The new clients use this authentication method. I warn you in advance, it's | |
733 * bizzare, convoluted, inordinately complicated. It's also no more secure than | |
734 * crypt() was. The only purpose this scheme could serve is to prevent third | |
735 * part clients from connecting to their servers. | |
736 * | |
737 * Sorry, Yahoo. | |
738 */ | |
739 | |
740 md5_byte_t result[16]; | |
741 md5_state_t ctx; | |
742 char *crypt_result; | |
743 char *password_hash = g_malloc(25); | |
744 char *crypt_hash = g_malloc(25); | |
745 char *hash_string_p = g_malloc(50 + strlen(sn)); | |
746 char *hash_string_c = g_malloc(50 + strlen(sn)); | |
747 | |
748 int ordering; | |
749 char checksum; | |
750 | |
751 char sv; | |
752 | |
753 char *result6 = g_malloc(25); | |
754 char *result96 = g_malloc(25); | |
755 | |
756 sv = seed[15]; | |
757 checksum = sv % 16; | |
758 | |
759 /* I bet there's some really cool mathematical pattern here if I looked hard enough. | |
760 * But, this works. */ | |
761 switch (checksum) { | |
762 case 1: | |
763 case 6: | |
764 case 9: | |
765 case 14: | |
766 | |
767 checksum = seed[9]; | |
768 break; | |
769 case 3: | |
770 case 11: | |
771 checksum = seed[1]; | |
772 break; | |
773 case 4: | |
774 case 12: | |
775 checksum = seed[3]; | |
776 break; | |
777 case 5: | |
778 case 8: | |
779 case 13: | |
780 case 0: | |
781 checksum = seed[7]; | |
782 break; | |
783 } | |
784 checksum = seed[checksum % 16]; | |
785 | |
786 ordering = sv % 8; | |
787 | |
788 md5_init(&ctx); | |
789 md5_append(&ctx, gc->password, strlen(gc->password)); | |
790 md5_finish(&ctx, result); | |
791 to_y64(password_hash, result, 16); | |
792 | |
793 md5_init(&ctx); | |
794 crypt_result = yahoo_crypt(gc->password, "$1$_2S43d5f$"); | |
795 md5_append(&ctx, crypt_result, strlen(crypt_result)); | |
796 md5_finish(&ctx, result); | |
797 to_y64(crypt_hash, result, 16); | |
798 | |
799 /* I bet there's a nice pattern here, too. */ | |
800 switch (ordering) { | |
801 case 1: | |
802 case 6: | |
803 g_snprintf(hash_string_p, strlen(sn) + 50, | |
804 "%c%s%s%s", checksum, gc->username, seed, password_hash); | |
805 g_snprintf(hash_string_c, strlen(sn) + 50, | |
806 "%c%s%s%s", checksum, gc->username, seed, crypt_hash); | |
807 break; | |
808 case 2: | |
809 case 7: | |
810 g_snprintf(hash_string_p, strlen(sn) + 50, | |
811 "%c%s%s%s", checksum, seed, password_hash, gc->username); | |
812 g_snprintf(hash_string_c, strlen(sn) + 50, | |
813 "%c%s%s%s", checksum, seed, crypt_hash, gc->username); | |
814 break; | |
815 case 3: | |
816 g_snprintf(hash_string_p, strlen(sn) + 50, | |
817 "%c%s%s%s", checksum, gc->username, password_hash, seed); | |
818 g_snprintf(hash_string_c, strlen(sn) + 50, | |
819 "%c%s%s%s", checksum, gc->username, crypt_hash, seed); | |
820 break; | |
821 case 4: | |
822 g_snprintf(hash_string_p, strlen(sn) + 50, | |
823 "%c%s%s%s", checksum, gc->username, password_hash, seed); | |
824 g_snprintf(hash_string_c, strlen(sn) + 50, | |
825 "%c%s%s%s", checksum, gc->username, crypt_hash, seed); | |
826 break; | |
827 case 0: | |
828 case 5: | |
829 g_snprintf(hash_string_p, strlen(sn) + 50, | |
830 "%c%s%s%s", checksum, password_hash, gc->username, seed); | |
831 g_snprintf(hash_string_c, strlen(sn) + 50, | |
832 "%c%s%s%s", checksum, crypt_hash, gc->username, seed); | |
833 break; | |
834 } | |
835 | |
836 debug_printf("\nPassword: %s\n", hash_string_p); | |
837 debug_printf("Crypt: %s\n\n", hash_string_c); | |
838 | |
839 md5_init(&ctx); | |
840 md5_append(&ctx, hash_string_p, strlen(hash_string_c)); | |
841 md5_finish(&ctx, result); | |
842 to_y64(result6, result, 16); | |
843 | |
844 md5_init(&ctx); | |
845 md5_append(&ctx, hash_string_c, strlen(hash_string_c)); | |
846 md5_finish(&ctx, result); | |
847 to_y64(result96, result, 16); | |
848 | |
849 md5_init(&ctx); | |
850 md5_append(&ctx, gc->password, strlen(gc->password)); | |
851 md5_finish(&ctx, result); | |
852 to_y64(password_hash, result, 16); | |
853 | |
854 pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, YAHOO_STATUS_AVAILABLE, 0); | |
855 yahoo_packet_hash(pack, 0, gc->username); | |
856 yahoo_packet_hash(pack, 6, result6); | |
857 yahoo_packet_hash(pack, 96, result96); | |
858 yahoo_packet_hash(pack, 1, gc->username); | |
859 | |
860 yahoo_send_packet(yd, pack); | |
861 | |
862 g_free(password_hash); | |
863 g_free(crypt_hash); | |
864 g_free(hash_string_p); | |
865 g_free(hash_string_c); | |
866 | |
867 yahoo_packet_free(pack); | |
868 } | |
869 } | |
669 | 870 |
670 static void yahoo_packet_process(struct gaim_connection *gc, struct yahoo_packet *pkt) | 871 static void yahoo_packet_process(struct gaim_connection *gc, struct yahoo_packet *pkt) |
671 { | 872 { |
672 switch (pkt->service) | 873 switch (pkt->service) |
673 { | 874 { |
693 yahoo_process_contact(gc, pkt); | 894 yahoo_process_contact(gc, pkt); |
694 break; | 895 break; |
695 case YAHOO_SERVICE_LIST: | 896 case YAHOO_SERVICE_LIST: |
696 yahoo_process_list(gc, pkt); | 897 yahoo_process_list(gc, pkt); |
697 break; | 898 break; |
899 case YAHOO_SERVICE_AUTH: | |
900 yahoo_process_auth(gc, pkt); | |
901 break; | |
698 default: | 902 default: |
699 debug_printf("unhandled service 0x%02x\n", pkt->service); | 903 debug_printf("unhandled service 0x%02x\n", pkt->service); |
700 break; | 904 break; |
701 } | 905 } |
702 } | 906 } |
783 } | 987 } |
784 | 988 |
785 yd = gc->proto_data; | 989 yd = gc->proto_data; |
786 yd->fd = source; | 990 yd->fd = source; |
787 | 991 |
788 pkt = yahoo_packet_new(YAHOO_SERVICE_LOGON, YAHOO_STATUS_AVAILABLE, 0); | 992 pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YAHOO_STATUS_AVAILABLE, 0); |
789 | 993 |
790 yahoo_packet_hash(pkt, 0, gc->username); | |
791 yahoo_packet_hash(pkt, 1, gc->username); | 994 yahoo_packet_hash(pkt, 1, gc->username); |
792 yahoo_packet_hash(pkt, 6, yahoo_crypt(gc->password, "$1$_2S43d5f$")); | |
793 | |
794 yahoo_send_packet(yd, pkt); | 995 yahoo_send_packet(yd, pkt); |
795 | 996 |
796 yahoo_packet_free(pkt); | 997 yahoo_packet_free(pkt); |
797 | 998 |
798 gc->inpa = gaim_input_add(yd->fd, GAIM_INPUT_READ, yahoo_pending, gc); | 999 gc->inpa = gaim_input_add(yd->fd, GAIM_INPUT_READ, yahoo_pending, gc); |
806 | 1007 |
807 yd->fd = -1; | 1008 yd->fd = -1; |
808 yd->hash = g_hash_table_new(g_str_hash, g_str_equal); | 1009 yd->hash = g_hash_table_new(g_str_hash, g_str_equal); |
809 yd->games = g_hash_table_new(g_str_hash, g_str_equal); | 1010 yd->games = g_hash_table_new(g_str_hash, g_str_equal); |
810 | 1011 |
1012 #if 0 | |
811 if (!g_strncasecmp(user->proto_opt[USEROPT_PAGERHOST], "scs.yahoo.com", strlen("scs.yahoo.com"))) { | 1013 if (!g_strncasecmp(user->proto_opt[USEROPT_PAGERHOST], "scs.yahoo.com", strlen("scs.yahoo.com"))) { |
812 /* As of this morning, Yahoo is no longer supporting its server at scs.yahoo.com | 1014 /* Figured out the new auth method */ |
813 * I don't like to edit the preferences in a prpl, but we'll keep this here | |
814 * for a while until everybody's happy again. -5 Feb 2002*/ | |
815 debug_printf("Setting new Yahoo! server.\n"); | 1015 debug_printf("Setting new Yahoo! server.\n"); |
816 g_snprintf(user->proto_opt[USEROPT_PAGERHOST], strlen("cs.yahoo.com") + 1, "cs.yahoo.com"); | 1016 g_snprintf(user->proto_opt[USEROPT_PAGERHOST], strlen("cs.yahoo.com") + 1, "cs.yahoo.com"); |
817 save_prefs(); | 1017 save_prefs(); |
818 } | 1018 } |
819 | 1019 |
1020 #endif /* 0 */ | |
1021 | |
820 if (proxy_connect(user->proto_opt[USEROPT_PAGERHOST][0] ? | 1022 if (proxy_connect(user->proto_opt[USEROPT_PAGERHOST][0] ? |
821 user->proto_opt[USEROPT_PAGERHOST] : YAHOO_PAGER_HOST, | 1023 user->proto_opt[USEROPT_PAGERHOST] : YAHOO_PAGER_HOST, |
822 user->proto_opt[USEROPT_PAGERPORT][0] ? | 1024 user->proto_opt[USEROPT_PAGERPORT][0] ? |
823 atoi(user->proto_opt[USEROPT_PAGERPORT]) : YAHOO_PAGER_PORT, | 1025 atoi(user->proto_opt[USEROPT_PAGERPORT]) : YAHOO_PAGER_PORT, |
824 yahoo_got_connected, gc) < 0) { | 1026 yahoo_got_connected, gc) < 0) { |