[jdev] Yahoo Transport Patch: Yahoo Avatars

sabat sabat at eloan.com
Mon Aug 16 14:21:10 CDT 2004


I've hacked up the Yahoo Transport so that it automatically downloads 
Yahoo avatars. This would only be useful for you if you're using a 
jabber chat client that supports JEP-008. (Yes, I know, that JEP was 
retracted, but it's still the only standard out there, and there are 
clients that support it.)

The patch below is against version 2.3.2 of the transport. In order to 
use it, you first need to download and install the http_fetcher library 
at http://http-fetcher.sourceforge.net/. It should compile normally 
(albeit with some ignorable warnings).

To patch your Yahoo Transport, cd into it and run

patch -p1 < /path/to/the/patch/file

and then run 'make'.

When you run it and connect with a client that a) is registered with the 
Yahoo transport, b) has Yahoo buddies who have avatars, and c) supports 
jabber avatars via JEP-008, you should see Yahoo avatars in place of 
whatever generic placeholder normally appears.

I have only run this on linux, and cannot guarantee it will work, or 
that it won't hose up your server or something. Meaning: I wouldn't use 
this in production, just to fool around. Good luck, and may the Force be 
with you.

(I hereby release this hack into the public domain.)


<>diff -burN --exclude=CVS --exclude='*.o' --exclude='*.so' 
--exclude='*.gz' --exclude='*.xml' yahoo-transport-orig/Makefile 
yahoo-transport/Makefile
--- yahoo-transport-orig/Makefile 2004-06-25 11:33:56.000000000 -0700
+++ yahoo-transport/Makefile 2004-08-16 11:05:43.048965928 -0700
@@ -4,11 +4,11 @@
include ../platform-settings

CFLAGS:=$(CFLAGS) -I../jabberd `glib-config --cflags`
-LIBS:=$(LIBS) `glib-config --libs glib`
+LIBS:=$(LIBS) `glib-config --libs glib` -lhttp_fetcher

YAHOO_OBJECTS=yahoo-transport.o yahoo-session.o yahoo-phandler.o yahoo.o \
crypt.o gaim-sha.o yahoo-presence.o yahoo-server.o md5.o yahoo-message.o \
- yahoo-stats.o yahoo-composing.o yahoo-mail.o yahoo-auth.o
+ yahoo-stats.o yahoo-composing.o yahoo-mail.o yahoo-auth.o sha.o 
yahoo-avatar.o

all: yahoo-transport

@@ -38,3 +38,4 @@
yahoo-stats.o: yahoo-stats.c yahoo-transport.h
yahoo.o: yahoo.c yahoo-transport.h
yahoo-auth.o: yahoo-auth.c yahoo-auth.h
+yahoo-avatar.o: yahoo-avatar.c
diff -burN --exclude=CVS --exclude='*.o' --exclude='*.so' 
--exclude='*.gz' --exclude='*.xml' yahoo-transport-orig/sha.c 
yahoo-transport/sha.c
--- yahoo-transport-orig/sha.c 1969-12-31 16:00:00.000000000 -0800
+++ yahoo-transport/sha.c 2004-08-04 18:14:45.000000000 -0700
@@ -0,0 +1,101 @@
+/*
+ Implements the Secure Hash Algorithm (1)
+
+ Copyright (C) 1999 Scott G. Miller
+
+ Released under the terms of the GNU General Public License v2
+ see file COPYING for details
+*/
+
+#define Ai 0x67452301
+#define Bi 0xefcdab89
+#define Ci 0x98badcfe
+#define Di 0x10325476
+#define Ei 0xc3d2e1f0
+
+#define A 0
+#define B 1
+#define C 2
+#define D 3
+#define E 4
+
+#define K1 0x5a827999
+#define K2 0x6ed9eba1
+#define K3 0x8f1bbcdc
+#define K4 0xca62c1d6
+
+#define f1(X,Y,Z) (X & Y) | ((!X) ^ Z)
+#define f2(X,Y,Z) (X ^ Y ^ Z)
+#define f3(X,Y,Z) (X & Y) | (X & Z) | (Y & Z)
+
+#define rol1(x) (x<<1) | ((x>>31) & 1)
+#define rol5(x) (x<<5) | ((x>>27) & 0x1f)
+#define rol30(x) (x<<30) | ((x>>2) & 0x3fffffff)
+
+int sha_hash(int *data, int *hash) {
+ int W[80];
+ int a=hash[A], b=hash[B], c=hash[C], d=hash[D], e=hash[E], t, x, TEMP;
+
+ /** Data expansion from 16 to 80 blocks **/
+ for (t=0; t<16; t++) {
+ W[t]=data[t];
+ }
+ for (t=16; t<80; t++) {
+ x=W[t-3] ^ W[t-8] ^ W[t-16];
+ W[t]=rol1(x);
+ }
+
+ /** Main loops **/
+ for (t=0; t<20; t++) {
+ TEMP=rol5(a) + f1(b,c,d) + e + W[t] + K1;
+ e=d;
+ d=c;
+ c=rol30(b);
+ b=a;
+ a=TEMP;
+ }
+ for (; t<40; t++) {
+ TEMP=rol5(a) + f2(b,c,d) + e + W[t] + K2;
+ e=d;
+ d=c;
+ c=rol30(b);
+ b=a;
+ a=TEMP;
+ }
+ for (; t<60; t++) {
+ TEMP=rol5(a) + f3(b,c,d) + e + W[t] + K3;
+ e=d;
+ d=c;
+ c=rol30(b);
+ b=a;
+ a=TEMP;
+ }
+ for (; t<80; t++) {
+ TEMP=rol5(a) + f2(b,c,d) + e + W[t] + K4;
+ e=d;
+ d=c;
+ c=rol30(b);
+ b=a;
+ a=TEMP;
+ }
+ hash[A]+=a;
+ hash[B]+=b;
+ hash[C]+=c;
+ hash[D]+=d;
+ hash[E]+=e;
+}
+
+int sha_init(int *hash) {
+ hash[A]=Ai;
+ hash[B]=Bi;
+ hash[C]=Ci;
+ hash[D]=Di;
+ hash[E]=Ei;
+}
+
+
+
+
+
+
+
diff -burN --exclude=CVS --exclude='*.o' --exclude='*.so' 
--exclude='*.gz' --exclude='*.xml' yahoo-transport-orig/sha.h 
yahoo-transport/sha.h
--- yahoo-transport-orig/sha.h 1969-12-31 16:00:00.000000000 -0800
+++ yahoo-transport/sha.h 2004-08-04 18:14:47.000000000 -0700
@@ -0,0 +1,2 @@
+int sha_hash(int *data, int *hash);
+int sha_init(int *hash);
diff -burN --exclude=CVS --exclude='*.o' --exclude='*.so' 
--exclude='*.gz' --exclude='*.xml' yahoo-transport-orig/yahoo-avatar.c 
yahoo-transport/yahoo-avatar.c
--- yahoo-transport-orig/yahoo-avatar.c 1969-12-31 16:00:00.000000000 -0800
+++ yahoo-transport/yahoo-avatar.c 2004-08-16 10:22:34.635706632 -0700
@@ -0,0 +1,206 @@
+#include "yahoo-transport.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/* This is taken from Sylpheed by Hiroyuki Yamamoto. We have our own 
tobase64 function
+ * in util.c, but it has a bug I don't feel like finding right now ;) */
+
+/* Note: had to put this here and modify it because it didn't seem to 
output base64
+ * that is standard. The code had the final 2 chars in base64digits as 
._ instead of
+ * the apparent standard of +/ */
+
+void get_avatar64(unsigned char *out, const unsigned char *in, int inlen) {
+ char base64digits[] = 
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ /* raw bytes in quasi-big-endian order to base 64 string 
(NUL-terminated) */
+
+ for (; inlen >= 3; inlen -= 3) {
+ *out++ = base64digits[in[0] >> 2];
+ *out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)];
+ *out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)];
+ *out++ = base64digits[in[2] & 0x3f];
+ in += 3;
+ }
+ if (inlen > 0) {
+ unsigned char fragment;
+
+ *out++ = base64digits[in[0] >> 2];
+ fragment = (in[0] << 4) & 0x30;
+ if (inlen > 1)
+ fragment |= in[1] >> 4;
+ *out++ = base64digits[fragment];
+ *out++ = (inlen < 2) ? '-' : base64digits[(in[1] << 2) & 0x3c];
+ *out++ = '-';
+ }
+ *out = '\0';
+}
+
+void hex_sha_hash(int *hashval, char *buffer) {
+ int x;
+ char tmp[9];
+
+ // I presume that buffer is already memsetted to 0s
+
+ for (x=0; x<5; x++) {
+ sprintf(tmp, "%08x", hashval[x]);
+ strcat(buffer, tmp);
+ }
+
+ return;
+}
+
+void av_sha_hash(char *filebuf, int length, char *av_hex_hash) {
+ int c=0;
+ int i;
+ int hashlength=0;
+ char buffer[64];
+ int av_bin_hash[Y_AVATAR_HASH_SIZE];
+
+ sha_init(av_bin_hash);
+
+ do {
+
+ if (c+64>length) {
+ memcpy(buffer, filebuf+c, length-c);
+// printf("memcpying the last %i bytes of filebuf into buffer\n", 
length-c);
+ hashlength+=(length-c);
+// printf("length is %i\n", hashlength);
+ for (i=length-c; i<61; i++) {
+ if (i==length-c)
+ buffer[i]=0x10;
+ else if (i==60)
+ ((int*)buffer)[15]=hashlength*8;
+ else
+ buffer[i]=0;
+ }
+ c+=i;
+ } else {
+// printf("doing byte %i to %i -- length is %i\n", c, c+64, length);
+ memcpy(buffer, filebuf+c, 64);
+ c+=64;
+ hashlength+=64;
+ }
+
+// for(q=0; q<64; q++) { printf("%i ", buffer[q]); }; printf("\n\n");
+ sha_hash((int*)buffer, av_bin_hash);
+ } while (c <= length);
+
+ memset(av_hex_hash, 0, 42);
+ hex_sha_hash(av_bin_hash, av_hex_hash);
+}
+
+int get_avatar_img(char *hash_string, char* filebuf) {
+ int pngsize;
+ char url[ sizeof(YAHOO_URL_PRE) + 65 + sizeof(YAHOO_URL_POST) ];
+
+ strcpy(url, YAHOO_URL_PRE);
+ strcat(url, hash_string);
+ strcat(url, YAHOO_URL_POST);
+
+ pngsize = http_fetch(url, (char **)filebuf);
+log_debug(ZONE, "[YAHOO]: addr of filebuf is %x", filebuf);
+
+ return(pngsize);
+}
+
+void get_avatar_sha_hash(char* filebuf, int pngsize, char* 
avatar_sha_string) {
+ /* ok, we have a string and we want to store it.
+
+ Do a web hit to yahoo and get the png img.
+
+ Get the SHA-1 hex hash of the img.
+
+ Base64-encode the img.
+
+ Send it to the jabber server's public storage.
+
+ Inject the SHA-1 hex hash of the img into the
+ current presence packet so the client knows
+ that there's an avatar available.
+
+ */
+
+ memset(avatar_sha_string, 0, 42);
+ av_sha_hash(filebuf, pngsize, avatar_sha_string);
+}
+
+void set_avatar_tag_info(xmlnode x, char* sha_hash) {
+ xmlnode query = xmlnode_insert_tag(x, "x");
+ xmlnode hashtag = xmlnode_insert_tag(query, "hash");
+
+ xmlnode_put_attrib(query, "xmlns", "jabber:x:avatar");
+ xmlnode_insert_cdata(hashtag, sha_hash, strlen(sha_hash));
+ // log_debug(ZONE, "[YAHOO]: inserted sha-1 avatar hash into presence 
packet at host %s, me = %s, server = %s", yd->yi->i->id, 
jid_full(yd->me), yd->me->server);
+ log_debug(ZONE, "[YAHOO]: j avatar hash is %s", sha_hash);
+}
+
+void store_avatar(struct yahoo_data *yd, char *contact_name, char* 
base64, char* sha_hash, char* y_hash) {
+
+ log_debug(ZONE, "[YAHOO]: store_avatar received y_hash of %s", y_hash);
+
+ xmlnode avatag_b64 = xmlnode_new_tag("yahoo_avatar");
+ xmlnode avatag_sha_hash = xmlnode_new_tag("yahoo_avatar_sha_hash");
+ xmlnode avatag_y_hash = xmlnode_new_tag("yahoo_avatar_y_hash");
+ pool p = pool_new();
+
+ jid j = jid_new(p, spools(p, contact_name, "@", "yahoo.com", p));
+
+ xmlnode_put_attrib(avatag_b64, "xmlns", "yahootrans:base64");
+ xmlnode_put_attrib(avatag_sha_hash, "xmlns", "yahootrans:sha_hash");
+ xmlnode_put_attrib(avatag_y_hash, "xmlns", "yahootrans:y_hash");
+
+ xmlnode_insert_cdata(avatag_b64, base64, strlen(base64));
+ xmlnode_insert_cdata(avatag_sha_hash, sha_hash, strlen(sha_hash));
+ xmlnode_insert_cdata(avatag_y_hash, y_hash, strlen(y_hash));
+
+ log_debug(ZONE, "[YAHOO]: doing an xdb set with owner %s", jid_full(j));
+
+ xdb_set(yd->yi->xc, j, "yahootrans:base64", avatag_b64);
+ xdb_set(yd->yi->xc, j, "yahootrans:sha_hash", avatag_sha_hash);
+ xdb_set(yd->yi->xc, j, "yahootrans:y_hash", avatag_y_hash);
+
+ xmlnode_free(avatag_b64);
+ xmlnode_free(avatag_sha_hash);
+ xmlnode_free(avatag_y_hash);
+}
+
+void retrieve_avatar_sha_hash(char* sha_hash, xdbcache xc, char* jid_str) {
+ pool p = pool_new();
+ jid j = jid_new(p, jid_str);
+
+ log_notice(ZONE, "[YAHOO]: trying to get db entry for %s", jid_full(j));
+
+ xmlnode sha_hash_xml = xdb_get(xc, j, "yahootrans:sha_hash");
+
+ if (sha_hash_xml != NULL)
+ strcpy(sha_hash, xmlnode_get_data(sha_hash_xml));
+ else
+ log_notice(ZONE, "[YAHOO]: no avatar retrieved!!");
+
+ xmlnode_free(sha_hash_xml);
+ pool_free(p);
+}
+
+int have_avatar_img_already(char* name, char* y_hash, xdbcache xc) {
+ int ret = 0;
+ pool p = pool_new();
+ jid j = jid_new(p, spools(p, name, "@", "yahoo.com", p));
+
+ log_notice(ZONE, "[YAHOO]: have_avatar_img_already is trying to get db 
entry for %s", jid_full(j));
+
+ xmlnode y_hash_xml = xdb_get(xc, j, "yahootrans:y_hash");
+ if (y_hash_xml != NULL)
+ if (strcmp(xmlnode_get_data(y_hash_xml), y_hash) == 0)
+ ret = 1;
+
+ log_notice(ZONE, "[YAHOO]: do we already have avatar for '%s' ? = * %i 
*", jid_full(j), ret);
+log_notice(ZONE, "[YAHOO]: y_hash received from yahoo is %s", y_hash);
+log_notice(ZONE, "[YAHOO]: y_hash stored is %s", xmlnode2str(y_hash_xml));
+
+ xmlnode_free(y_hash_xml);
+ pool_free(p);
+
+ return(ret);
+}
+
diff -burN --exclude=CVS --exclude='*.o' --exclude='*.so' 
--exclude='*.gz' --exclude='*.xml' yahoo-transport-orig/yahoo.c 
yahoo-transport/yahoo.c
--- yahoo-transport-orig/yahoo.c 2004-07-01 12:13:29.000000000 -0700
+++ yahoo-transport/yahoo.c 2004-08-16 10:51:21.132747351 -0700
@@ -480,6 +489,41 @@
case 16: /* Custom error message */
log_debug(ZONE, "[YAHOO]: Error Message: %s\n", pair->value);
break;
+ case 197: /* avatar hash */
+
+ log_debug(ZONE, "[YAHOO]: received y_avatar hash of %s\n", pair->value);
+ char avatar_y_hash[Y_AVATAR_HASH_SIZE];
+ strncpy(avatar_y_hash, pair->value, Y_AVATAR_HASH_SIZE);
+
+ log_debug(ZONE, "[YAHOO]: buddy name is %s", name);
+
+ if (!have_avatar_img_already(name, avatar_y_hash, yd->yi->xc)) {
+
+ log_debug(ZONE, "[YAHOO]: the 'already' routine sez we don't have it 
for %s (hash %s), loading", name, avatar_y_hash);
+
+ char avatar_sha_hash[Y_AVATAR_HASH_SIZE];
+ char hash64[Y_AVATAR_B64_SIZE]; // 16k is probably overkill but just 
in case ...
+ char *filebuf;
+ int pngsize;
+
+ pngsize = get_avatar_img(avatar_y_hash, &filebuf);
+
+ get_avatar_sha_hash(filebuf, pngsize, avatar_sha_hash);
+ log_debug(ZONE, "[YAHOO]: got sha-1 avatar hash %s\n", avatar_sha_hash);
+
+ get_avatar64(hash64, filebuf, pngsize);
+ store_avatar(yd, name, hash64, avatar_sha_hash, avatar_y_hash);
+ }
+
+ /*
+ set the presence for the sole purpose of announcing we have an avatar
+ for this user. That means there's no message (e.g. <show>away</show>)
+ so the 'state' variable can be 0 (normally meaning Available) because
+ it has no effect with a NULL message.
+ */
+ yahoo_set_jabber_presence(yd, name, 0, NULL);
+
+ break;
default:
log_debug(ZONE, "[YAHOO]: unknown status key %d\n", pair->key);
break;
diff -burN --exclude=CVS --exclude='*.o' --exclude='*.so' 
--exclude='*.gz' --exclude='*.xml' yahoo-transport-orig/yahoo-presence.c 
yahoo-transport/yahoo-presence.c
--- yahoo-transport-orig/yahoo-presence.c 2004-01-16 16:07:12.000000000 
-0800
+++ yahoo-transport/yahoo-presence.c 2004-08-16 10:29:53.517937073 -0700
@@ -27,6 +27,7 @@

void yahoo_set_jabber_presence(struct yahoo_data *yd, char 
*contact_name, int state, char *msg) {
xmlnode x = NULL;
+ char avatar_hash[Y_AVATAR_HASH_SIZE] = "";
pool p;

p = pool_new();
@@ -36,14 +37,23 @@
case 0: // Available
x = jutil_presnew(JPACKET__AVAILABLE, jid_full(yd->me), msg);
xmlnode_put_attrib(x, "from", spools(p, contact_name, "@", 
yd->yi->i->id, p));
+
+ retrieve_avatar_sha_hash(avatar_hash, yd->yi->xc, spools(p, 
contact_name, "@", YAHOO_AVATAR_DOMAIN, p));
+ if (*avatar_hash != 0)
+ set_avatar_tag_info(x, avatar_hash);
+
log_debug(ZONE, "[YAHOO]: Presence for '%s' = available", xmlnode2str(x));
break;

-
case 1: // Away
x = jutil_presnew(JPACKET__AVAILABLE, jid_full(yd->me), msg);
xmlnode_put_attrib(x, "from", spools(p, contact_name, "@", 
yd->yi->i->id, p));
xmlnode_insert_cdata(xmlnode_insert_tag(x,"show"), "away", -1);
+
+ retrieve_avatar_sha_hash(avatar_hash, yd->yi->xc, spools(p, 
contact_name, "@", YAHOO_AVATAR_DOMAIN, p));
+ if (*avatar_hash != 0)
+ set_avatar_tag_info(x, avatar_hash);
+
log_debug(ZONE, "[YAHOO]: Presence for '%s' = away", xmlnode2str(x));
break;

diff -burN --exclude=CVS --exclude='*.o' --exclude='*.so' 
--exclude='*.gz' --exclude='*.xml' yahoo-transport-orig/yahoo-server.c 
yahoo-transport/yahoo-server.c
--- yahoo-transport-orig/yahoo-server.c 2004-07-01 12:13:29.000000000 -0700
+++ yahoo-transport/yahoo-server.c 2004-08-16 10:50:49.185937205 -0700
@@ -238,6 +239,47 @@
version, time, etc.
*/

+ if (NSCHECK(jp->iq, NS_IQ_AVATAR)) {
+
+ /*
+ If we get a jabber:iq:avatar packet, it means someone in jabber-land
+ is asking if the jabber public storage has a particular avatar img
+ stored as base64. I have not bothered to put in a check to see if
+ the result is an empty packet, but I think that's ok. It would be
+ like answering: "yes, I have no avatar."
+ */
+
+ log_notice(ZONE, "[YAHOO]: I just saw a jabber:iq:avatar packet");
+ log_notice(ZONE, "[YAHOO]: to user = %s, from server = %s, to server = 
%s", jp->to->user, jp->from->server, jp->to->server);
+
+ // grab the base64-encoded image from the public storage
+ pool p = pool_new();
+ jid j = jid_new(p, spools(p, jp->to->user, "@", YAHOO_AVATAR_DOMAIN, p));
+
+ log_notice(ZONE, "[YAHOO]: trying to get db entry for %s", jid_full(j));
+
+ xmlnode avatar64 = xdb_get(yi->xc, j, "yahootrans:base64");
+
+ log_notice(ZONE, "[YAHOO]: picked up this xdb name: %s", 
xmlnode_get_name(avatar64));
+
+log_notice(ZONE, "[YAHOO]: picked up this avatar:\n%s\n", 
xmlnode_get_data(avatar64));
+
+ jutil_iqresult(jp->x);
+ jpacket_reset(jp);
+ query = xmlnode_insert_tag(jp->x, "query");
+ xmlnode_put_attrib(query, "xmlns", NS_IQ_AVATAR);
+ xmlnode adata = xmlnode_insert_tag(query, "data");
+ xmlnode_put_attrib(adata, "mimetype", "image/png"); // yahoo avatar 
imgs are PNGs
+ xmlnode_insert_cdata(adata, xmlnode_get_data(avatar64), -1);
+
+ yahoo_deliver(NULL,jp->x);
+
+ xmlnode_free(avatar64);
+ pool_free(p);
+
+ return;
+ }
+
if (NSCHECK(jp->iq, NS_STATS)) {
yahoo_stats(jp);
return;
diff -burN --exclude=CVS --exclude='*.o' --exclude='*.so' 
--exclude='*.gz' --exclude='*.xml' 
yahoo-transport-orig/yahoo-transport.h yahoo-transport/yahoo-transport.h
--- yahoo-transport-orig/yahoo-transport.h 2004-07-01 09:06:34.000000000 
-0700
+++ yahoo-transport/yahoo-transport.h 2004-08-16 10:31:24.796937932 -0700
@@ -37,6 +37,28 @@
#include <glib.h>
#include "md5.h"

+
+// Avatar stuff
+
+// this is the domain used for the jabber server public storage
+#define YAHOO_AVATAR_DOMAIN "yahoo.com"
+
+#define Y_AVATAR_HASH_SIZE 64
+#define Y_AVATAR_B64_SIZE 16*1024
+#define NS_IQ_AVATAR "jabber:iq:avatar"
+#define NS_X_AVATAR "jabber:x:avatar"
+#define YAHOO_URL_PRE "img1.avatar.vip.dcn.yahoo.com/users/"
+#define YAHOO_URL_POST ".medium.png"
+
+// hex sha library
+#include "sha.h"
+
+// http library to fetch avatars from yahoo
+#include <http_fetcher.h>
+
+//
+
+
#ifdef _JCOMP
#define YAHOO_VERSION "2.3.2-JCR"

#else




More information about the JDev mailing list