changeset 29812:365b90fa23cf

This patch comes from the combined work of contributors minstrel, NightFox, bob007, salieff, and nops (these are their trac usernames). I have made some minor tweaks to the patch, but these shouldn't be a problem. This patch needs some TLC before we can merge it anywhere else; it adds API so it must hit im.pidgin.pidgin.next.minor before hitting im.pidgin.pidgin. Refs #4508.
author John Bailey <rekkanoryo@rekkanoryo.org>
date Thu, 13 Nov 2008 17:04:53 +0000
parents f18f37a62971
children 297d83036107
files libpurple/protocols/oscar/family_buddy.c libpurple/protocols/oscar/family_icbm.c libpurple/protocols/oscar/family_locate.c libpurple/protocols/oscar/oscar.c libpurple/protocols/oscar/oscar.h libpurple/protocols/oscar/tlv.c libpurple/request.c libpurple/request.h pidgin/gtkrequest.c pidgin/pixmaps/Makefile.am
diffstat 10 files changed, 950 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/oscar/family_buddy.c	Thu Nov 13 09:00:45 2008 +0000
+++ b/libpurple/protocols/oscar/family_buddy.c	Thu Nov 13 17:04:53 2008 +0000
@@ -224,6 +224,10 @@
 	if (snac->subtype == SNAC_SUBTYPE_BUDDY_ONCOMING && userinfo.flags & AIM_FLAG_AWAY)
 		aim_locate_autofetch_away_message(od, userinfo.sn);
 
+	if (snac->subtype == SNAC_SUBTYPE_BUDDY_ONCOMING &&
+			userinfo.capabilities & OSCAR_CAPABILITY_XTRAZ && userinfo.customicon > 0)
+		icq_im_xstatus_request(od, userinfo.sn);
+
 	aim_info_free(&userinfo);
 
 	return ret;
--- a/libpurple/protocols/oscar/family_icbm.c	Thu Nov 13 09:00:45 2008 +0000
+++ b/libpurple/protocols/oscar/family_icbm.c	Thu Nov 13 17:04:53 2008 +0000
@@ -2533,6 +2533,15 @@
 	char *sn;
 	guchar *cookie;
 	guint8 snlen;
+	char *xml = NULL;
+	int hdrlen;
+	int curpos;
+	int num1,num2;
+	char *desc, *title, *temp;
+	PurpleAccount *account;
+	PurpleBuddy *buddy;
+	PurplePresence *presence;
+	PurpleStatus *status;
 
 	cookie = byte_stream_getraw(bs, 8);
 	channel = byte_stream_get16(bs);
@@ -2540,16 +2549,56 @@
 	sn = byte_stream_getstr(bs, snlen);
 	reason = byte_stream_get16(bs);
 
-	if (channel == 0x0002)
-	{
-		if (reason == 0x0003) /* channel-specific */
-			/* parse status note text */
-			parse_status_note_text(od, cookie, sn, bs);
-
-		byte_stream_get16(bs); /* Unknown */
-		byte_stream_get16(bs); /* Unknown */
-		if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
-			ret = userfunc(od, conn, frame, channel, sn, reason, cookie);
+	if (channel == 0x0002) {
+
+	 	hdrlen = byte_stream_getle16(bs);
+	 	if ( ((hdrlen == 27 ) && (bs->len > (27 + 51)))) {
+	 		byte_stream_advance(bs, 51);
+	 		num1 = byte_stream_getle16(bs); 
+	 		num2 = byte_stream_getle16(bs);
+	 		purple_debug_misc("oscar", "X-Status: Num1 %i, num2 %i\n",num1, num2);
+	 		
+	 		if(((num1 == 0x4f00)&&(num2 == 0x3b00))) {
+		 		byte_stream_advance(bs, 86);	
+		 		curpos = byte_stream_curpos(bs);
+		 		xml = byte_stream_getstr(bs, bs->len - curpos);
+		 		purple_debug_misc("oscar", "X-Status: Received XML reply\n");
+		 		if(xml) {
+ 				/* purple_debug_misc("oscar", "X-Status: XML reply: %s\n", (const char*) xml); */
+ 					if ((desc=strstr(xml,"&lt;desc&gt;")) != NULL) {
+ 						temp=strstr(xml,"&lt;/desc&gt;");
+ 						temp[0]=0;
+ 						desc=desc+12;
+ 					}
+ 					if ((title=strstr(xml,"&lt;title&gt;")) != NULL) {
+ 						temp=strstr(xml,"&lt;/title&gt;");
+ 						temp[0]=0;
+ 						title=title+13;
+ 					} else {
+ 						title="";
+ 					}
+ 					strcpy(xml,title);
+					if (desc) {
+ 						strcat(xml, " - ");
+ 						strcat(xml, desc);
+					}
+ 					purple_debug_misc("oscar", "X-Status reply: %s\n", (const char*)xml);
+ 					account = purple_connection_get_account(od->gc);
+ 					buddy = purple_find_buddy(account, sn);
+ 					presence = purple_buddy_get_presence(buddy);
+ 					status = purple_presence_get_active_status(presence);
+ 					purple_prpl_got_user_status(account, sn,
+  					    purple_status_get_id(status), "message", xml, NULL);
+		   		} else {
+			 		purple_debug_misc("oscar", "X-Status: Can't get XML reply string\n");
+				}
+	 		} else {
+		 		purple_debug_misc("oscar", "X-Status: 0x0004, 0x000b not an xstatus reply\n" );
+		 /*		if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
+		 		ret = userfunc(od, conn, frame, channel, sn, reason); */
+			}
+		
+	 	}
 
 	} else if (channel == 0x0004) { /* ICQ message */
 		switch (reason) {
@@ -2608,6 +2657,7 @@
 
 	g_free(cookie);
 	g_free(sn);
+	if (xml) g_free(xml);
 
 	return ret;
 }
@@ -2721,6 +2771,181 @@
 }
 
 /*
+ * Subtype 0x0006 - Send eXtra Status request
+ */
+int icq_im_xstatus_request(OscarData *od, const char *sn)
+{
+	FlapConnection *conn;
+	aim_snacid_t snacid;
+	guchar cookie[8];
+	GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
+	ByteStream bs, header, plugindata;
+	PurpleAccount *account;
+	const char *fmt;
+	char *statxml;
+	int xmllen;
+
+	static const guint8 pluginid[] = 
+	{
+	0x09, 0x46, 0x13, 0x49, 0x4C, 0x7F, 0x11, 0xD1, 
+	0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00
+	};
+	
+	static const guint8 c_plugindata[] =
+	{
+	0x1B, 0x00, 0x0A,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD1, 0x0E, 0x00, 0xF9, 0xD1, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x01, 0x00,
+	0x01, 0x00, 0x00, 0x4F, 0x00, 0x3B, 0x60, 0xB3, 0xEF, 0xD8, 0x2A, 0x6C, 0x45, 0xA4, 0xE0, 0x9C,
+	0x5A, 0x5E, 0x67, 0xE8, 0x65, 0x08, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x53, 0x63, 0x72, 0x69, 0x70,
+	0x74, 0x20, 0x50, 0x6C, 0x75, 0x67, 0x2D, 0x69, 0x6E, 0x3A, 0x20, 0x52, 0x65, 0x6D, 0x6F, 0x74,
+	0x65, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41,
+	0x72, 0x72, 0x69, 0x76, 0x65, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00
+	};
+	
+	if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
+		return -EINVAL;
+
+	if (!sn)
+		return -EINVAL;
+
+	fmt =  "<N><QUERY>&lt;Q&gt;&lt;PluginID&gt;srvMng&lt;/PluginID&gt;&lt;/Q&gt;</QUERY><NOTIFY>&lt;srv&gt;&lt;id&gt;cAwaySrv&lt;/id&gt;&lt;req&gt;&lt;id&gt;AwayStat&lt;/id&gt;&lt;trans&gt;2&lt;/trans&gt;&lt;senderId&gt;%s&lt;/senderId&gt;&lt;/req&gt;&lt;/srv&gt;</NOTIFY></N>\r\n";	
+
+	account = purple_connection_get_account(od->gc);
+	xmllen = strlen(fmt) - 2 + strlen(account->username);
+
+	statxml = (char*) g_malloc(xmllen);
+	snprintf(statxml, xmllen, fmt, account->username);
+
+	aim_icbm_makecookie(cookie);
+
+	byte_stream_new(&bs, 10 + 8 + 2 + 1 + strlen(sn) + 2
+					  + 2 + 2 + 8 + 16 + 2 + 2 + 2 + 2 + 2
+					  + 2 + 2 + sizeof(c_plugindata) + xmllen
+					  + 2 + 2);
+ 
+	snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+	aim_im_puticbm(&bs, cookie, 0x0002, sn);
+
+	byte_stream_new(&header, (7*2) + 16 + 8 + 2 + sizeof(c_plugindata) + xmllen); /* TLV 0x0005 Stream + Size */
+	byte_stream_new(&plugindata, (sizeof(c_plugindata) + xmllen));
+
+	byte_stream_put16(&header, 0x0000); /* Message Type: Request */
+	byte_stream_putraw(&header, cookie, sizeof(cookie)); /* Message ID */
+	byte_stream_putraw(&header, pluginid, sizeof(pluginid)); /* Plugin ID */
+	
+	aim_tlvlist_add_16(&inner_tlvlist, 0x000a, 0x0001);
+	aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
+	
+	/* Add Plugin Specific Data */
+	byte_stream_putraw(&plugindata, c_plugindata, sizeof(c_plugindata)); /* Content of TLV 0x2711 */
+	byte_stream_putstr(&plugindata, statxml);
+
+	aim_tlvlist_add_raw(&inner_tlvlist, 0x2711, (sizeof(c_plugindata) + xmllen), plugindata.data);
+	
+	aim_tlvlist_write(&header, &inner_tlvlist);
+	
+	
+	aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&header), header.data);
+	aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);		/* Empty TLV 0x0003 */
+	
+	aim_tlvlist_write(&bs, &outer_tlvlist);
+	
+	purple_debug_misc("oscar", "X-Status Request\n");
+	flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs, TRUE);
+
+	aim_tlvlist_free(inner_tlvlist);
+	aim_tlvlist_free(outer_tlvlist);
+	byte_stream_destroy(&header);
+	byte_stream_destroy(&plugindata);
+	byte_stream_destroy(&bs);
+	g_free(statxml);
+
+	return 0;
+}
+
+int icq_relay_xstatus(OscarData *od, const char *sn, const guchar *cookie)
+{
+	FlapConnection *conn;
+	ByteStream bs;
+	aim_snacid_t snacid;
+	PurpleAccount *account;
+	PurpleStatus *status;
+   const char *fmt;
+   const char *formatted_msg;
+   char *msg;
+   char *statxml;
+	const char *title;
+	int len;
+	
+	static const guint8 plugindata[] = {
+	0x1B, 0x00,
+	0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+	0x01, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD1, 0x0E, 0x00, 0xF9, 0xD1,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x4F, 
+	0x00, 0x3B, 0x60, 0xB3, 0xEF, 0xD8, 0x2A, 0x6C, 0x45, 0xA4, 0xE0, 
+	0x9C, 0x5A, 0x5E, 0x67, 0xE8, 0x65, 0x08, 0x00, 0x2A, 0x00, 0x00, 
+	0x00, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x50, 0x6C, 0x75, 
+	0x67, 0x2D, 0x69, 0x6E, 0x3A, 0x20, 0x52, 0x65, 0x6D, 0x6F, 0x74, 
+	0x65, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+	0x69, 0x6F, 0x6E, 0x20, 0x41, 0x72, 0x72, 0x69, 0x76, 0x65, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0xF3, 0x01, 0x00, 0x00, 0xEF, 0x01, 0x00, 0x00
+	};	
+
+	fmt = "<NR><RES>&lt;ret event='OnRemoteNotification'&gt;&lt;srv&gt;&lt;id&gt;cAwaySrv&lt;/id&gt;&lt;val srv_id='cAwaySrv'&gt;&lt;Root&gt;&lt;CASXtraSetAwayMessage&gt;&lt;/CASXtraSetAwayMessage&gt;&l t;uin&gt;%s&lt;/uin&gt;&lt;index&gt;1&lt;/index&gt;&lt;title&gt;%s&lt;/title&gt;&lt;desc&gt;%s&lt;/desc&gt;&lt;/Root&gt;&lt;/val&gt;&lt;/srv&gt;&lt;srv&gt;&lt;id&gt;cRandomizerSrv&lt;/id&gt;&lt;val srv_id='cRandomizerSrv'&gt;undefined&lt;/val&gt;&lt;/srv&gt;&lt;/ret&gt;</RES></NR>\r\n";
+	
+	
+	if (!od || !(conn = flap_connection_findbygroup(od, 0x0002)))
+		return -EINVAL;
+
+	if (!sn)
+		return -EINVAL;
+		
+	account = purple_connection_get_account(od->gc);
+	if(!account) return -EINVAL;
+	
+/*	if (!strcmp(account->username, sn))
+		icq_im_xstatus_request(od, sn); */
+	
+	status  = purple_presence_get_active_status(account->presence);
+	if (!status) return -EINVAL;
+   title = purple_status_get_name(status);
+   if (!title) return -EINVAL;
+	formatted_msg = purple_status_get_attr_string(status, "message");
+	if (!formatted_msg) return -EINVAL;
+   msg = purple_markup_strip_html(formatted_msg);
+	if (!msg) return -EINVAL;
+	len = strlen(fmt)-6+strlen(account->username)+strlen(title)+strlen(msg);
+	statxml = (char*) g_malloc(len);
+
+	snprintf(statxml, len, fmt, 
+		account->username, title, msg);
+
+	purple_debug_misc("oscar", "X-Status AutoReply: %s, %s\n", formatted_msg, msg);
+
+	byte_stream_new(&bs, 10 + 8 + 2 + 1 + strlen(sn) + 2 + sizeof(plugindata) + strlen(statxml)); /* 16 extra */
+
+	snacid = aim_cachesnac(od, 0x0004, 0x000b, 0x0000, NULL, 0);
+	aim_im_puticbm(&bs, cookie, 0x0002, sn);
+	byte_stream_put16(&bs, 0x0003);
+	byte_stream_putraw(&bs, plugindata, sizeof(plugindata));
+	byte_stream_putraw(&bs, (const guint8*)statxml, strlen(statxml));
+	
+	flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x000b, 0x0000, snacid, &bs, TRUE);
+
+	g_free(statxml);
+	g_free(msg);
+	byte_stream_destroy(&bs);
+
+	return 0;
+}
+
+/*
  * Subtype 0x0014 - Receive a mini typing notification (mtn) packet.
  *
  * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer,
--- a/libpurple/protocols/oscar/family_locate.c	Thu Nov 13 09:00:45 2008 +0000
+++ b/libpurple/protocols/oscar/family_locate.c	Thu Nov 13 17:04:53 2008 +0000
@@ -166,6 +166,16 @@
 	 {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
 	  0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
 
+	/* New format of caps (xtraz icons) */
+	{OSCAR_CAPABILITY_NEWCAPS,
+	 {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	/* Support xtraz statuses */
+	{OSCAR_CAPABILITY_XTRAZ,
+	 {0x1a, 0x09, 0x3c, 0x6c, 0xd7, 0xFD, 0x4e, 0xc5, 
+	  0x9d, 0x51, 0xa6, 0x47, 0x4e, 0x34, 0xf5, 0xa0}},
+
 	{OSCAR_CAPABILITY_SENDBUDDYLIST,
 	 {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1,
 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
@@ -242,6 +252,155 @@
 	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
 };
 
+#define AIM_CUSTOM_ICONS_COUNT 35
+
+static const struct {
+	char *filename;
+	char *descriptivename;
+	guint8 data[16];
+} aim_custom_icons[AIM_CUSTOM_ICONS_COUNT] = {
+	/* empty X-Status for the case when customicon == 0 */
+	{NULL, NULL,
+	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+
+	{"xstatus_thinking", "Thinking",
+	 {0x3f, 0xb0, 0xbd, 0x36, 0xaf, 0x3b, 0x4a, 0x60,
+	  0x9e, 0xef, 0xcf, 0x19, 0x0f, 0x6a, 0x5a, 0x7f}},
+
+	{"xstatus_busy", "Busy",
+	 {0x48, 0x8e, 0x14, 0x89, 0x8a, 0xca, 0x4a, 0x08,
+	  0x82, 0xaa, 0x77, 0xce, 0x7a, 0x16, 0x52, 0x08}},
+
+	{"xstatus_shopping", "Shopping",
+	 {0x63, 0x62, 0x73, 0x37, 0xa0, 0x3f, 0x49, 0xff,
+	  0x80, 0xe5, 0xf7, 0x09, 0xcd, 0xe0, 0xa4, 0xee}},
+
+	{"xstatus_typing", "Typing",
+	 {0x63, 0x4f, 0x6b, 0xd8 ,0xad, 0xd2, 0x4a, 0xa1,
+	  0xaa, 0xb9, 0x11, 0x5b, 0xc2, 0x6d, 0x05, 0xa1}},
+
+	{"xstatus_question", "Question Mark",
+	 {0x63, 0x14, 0x36, 0xff, 0x3f, 0x8a, 0x40, 0xd0,
+	  0xa5, 0xcb, 0x7b, 0x66, 0xe0, 0x51, 0xb3, 0x64}},
+
+	{"xstatus_angry", "Angry",
+	 {0x01, 0xd8, 0xd7, 0xee, 0xac, 0x3b, 0x49, 0x2a,
+	  0xa5, 0x8d, 0xd3, 0xd8, 0x77, 0xe6, 0x6b, 0x92}},
+
+	{"xstatus_eating", "Eating",
+	 {0xf8, 0xe8, 0xd7, 0xb2, 0x82, 0xc4, 0x41, 0x42,
+	  0x90, 0xf8, 0x10, 0xc6, 0xce, 0x0a, 0x89, 0xa6}},
+	  
+	{"xstatus_cinema", "Cinema",
+	 {0x10, 0x7a, 0x9a, 0x18, 0x12, 0x32, 0x4d, 0xa4,
+	  0xb6, 0xcd, 0x08, 0x79, 0xdb, 0x78, 0x0f, 0x09}},
+	
+	{"xstatus_sick", "Sick",
+	 {0x1f, 0x7a, 0x40, 0x71, 0xbf, 0x3b, 0x4e, 0x60,
+	  0xbc, 0x32, 0x4c, 0x57, 0x87, 0xb0, 0x4c, 0xf1}},
+	
+	{"xstatus_crap", "Unknown 1",
+	 {0x2c, 0xe0, 0xe4, 0xe5, 0x7c, 0x64, 0x43, 0x70,
+	  0x9c, 0x3a, 0x7a, 0x1c, 0xe8, 0x78, 0xa7, 0xdc}},
+	  
+	{"xstatus_iron", "Unknown 2",
+	 {0xb7, 0x08, 0x67, 0xf5, 0x38, 0x25, 0x43, 0x27,
+	  0xa1, 0xff, 0xcf, 0x4c, 0xc1, 0x93, 0x97, 0x97}},
+	
+	{"xstatus_bathing", "Bathing",
+	 {0x5a, 0x58, 0x1e, 0xa1, 0xe5, 0x80, 0x43, 0x0c,
+	  0xa0, 0x6f, 0x61, 0x22, 0x98, 0xb7, 0xe4, 0xc7}},
+	  
+	{"xstatus_tv", "Watching TV",
+	 {0x80, 0x53, 0x7d, 0xe2, 0xa4, 0x67, 0x4a, 0x76,
+	  0xb3, 0x54, 0x6d, 0xfd, 0x07, 0x5f, 0x5e, 0xc6}},
+	
+	{"xstatus_fun", "Having fun",
+	 {0x6f, 0x49, 0x30, 0x98, 0x4f, 0x7c, 0x4a, 0xff,
+	  0xa2, 0x76, 0x34, 0xa0, 0x3b, 0xce, 0xae, 0xa7}},
+
+	{"xstatus_sleeping", "Sleeping",
+	 {0x78, 0x5e, 0x8c, 0x48, 0x40, 0xd3, 0x4c, 0x65,
+	  0x88, 0x6f, 0x04, 0xcf, 0x3f, 0x3f, 0x43, 0xdf}},
+	
+	{"xstatus_pda", "PDA device",
+	 {0x10, 0x11, 0x17, 0xc9, 0xa3, 0xb0, 0x40, 0xf9,
+	  0x81, 0xac, 0x49, 0xe1, 0x59, 0xfb, 0xd5, 0xd4}},
+
+	{"xstatus_heart", "In love",
+	 {0xdd, 0xcf, 0x0e, 0xa9, 0x71, 0x95, 0x40, 0x48,
+	  0xa9, 0xc6, 0x41, 0x32, 0x06, 0xd6, 0xf2, 0x80}},
+
+	{"xstatus_tired", "Tired",
+	 {0x83, 0xc9, 0xb7, 0x8e, 0x77, 0xe7, 0x43, 0x78,
+	  0xb2, 0xc5, 0xfb, 0x6c, 0xfc, 0xc3, 0x5b, 0xec}},
+
+	{"xstatus_friends", "Friends",
+	 {0xf1, 0x8a, 0xb5, 0x2e, 0xdc, 0x57, 0x49, 0x1d,
+	  0x99, 0xdc, 0x64, 0x44, 0x50, 0x24, 0x57, 0xaf}},
+
+	{"xstatus_phone", "On the phone",
+	 {0x12, 0x92, 0xe5, 0x50, 0x1b, 0x64, 0x4f, 0x66,
+	  0xb2, 0x06, 0xb2, 0x9a, 0xf3, 0x78, 0xe4, 0x8d}},
+
+	{"xstatus_surfing", "Surfing",
+	 {0xa6, 0xed, 0x55, 0x7e, 0x6b, 0xf7, 0x44, 0xd4,
+	  0xa5, 0xd4, 0xd2, 0xe7, 0xd9, 0x5c, 0xe8, 0x1f}},
+
+	{"xstatus_mobile", "Cell phone",
+	 {0x16, 0x0c, 0x60, 0xbb, 0xdd, 0x44, 0x43, 0xf3,
+	  0x91, 0x40, 0x05, 0x0f, 0x00, 0xe6, 0xc0, 0x09}},
+
+	{"xstatus_google", "Googling",
+	 {0xd4, 0xe2, 0xb0, 0xba, 0x33, 0x4e, 0x4f, 0xa5,
+	  0x98, 0xd0, 0x11, 0x7d, 0xbf, 0x4d, 0x3c, 0xc8}},
+
+	{"xstatus_party", "Party",
+	 {0xe6, 0x01, 0xe4, 0x1c, 0x33, 0x73, 0x4b, 0xd1,
+	  0xbc, 0x06, 0x81, 0x1d, 0x6c, 0x32, 0x3d, 0x81}},
+
+	{"xstatus_coffee", "Coffee",
+	 {0x1b, 0x78, 0xae, 0x31, 0xfa, 0x0b, 0x4d, 0x38,
+	  0x93, 0xd1, 0x99, 0x7e, 0xee, 0xaf, 0xb2, 0x18}},
+
+	{"xstatus_gaming", "Playing",
+	 {0xd4, 0xa6, 0x11, 0xd0, 0x8f, 0x01, 0x4e, 0xc0,
+	  0x92, 0x23, 0xc5, 0xb6, 0xbe, 0xc6, 0xcc, 0xf0}},
+	
+	{"xstatus_internet", "Internet",
+	 {0x12, 0xd0, 0x7e, 0x3e, 0xf8, 0x85, 0x48, 0x9e,
+	  0x8e, 0x97, 0xa7, 0x2a, 0x65, 0x51, 0xe5, 0x8d}},
+	
+	{"xstatus_zzz", "Snoozing",
+	 {0x64, 0x43, 0xc6, 0xaf, 0x22, 0x60, 0x45, 0x17,
+	  0xb5, 0x8c, 0xd7, 0xdf, 0x8e, 0x29, 0x03, 0x52}},
+	
+	{"xstatus_writing", "Writing",
+	 {0x00, 0x72, 0xd9, 0x08, 0x4a, 0xd1, 0x43, 0xdd,
+	  0x91, 0x99, 0x6f, 0x02, 0x69, 0x66, 0x02, 0x6f}},
+	
+	{"xstatus_beer", "Drinking",
+	 {0x8c, 0x50, 0xdb, 0xae, 0x81, 0xed, 0x47, 0x86,
+	  0xac, 0xca, 0x16, 0xcc, 0x32, 0x13, 0xc7, 0xb7}},
+	
+	{"xstatus_music", "Music",
+	 {0x61, 0xbe, 0xe0, 0xdd, 0x8b, 0xdd, 0x47, 0x5d,
+	  0x8d, 0xee, 0x5f, 0x4b, 0xaa, 0xcf, 0x19, 0xa7}},
+	
+	{"xstatus_studying", "Studying",
+	 {0x60, 0x9d, 0x52, 0xf8, 0xa2, 0x9a, 0x49, 0xa6,
+	  0xb2, 0xa0, 0x25, 0x24, 0xc5, 0xe9, 0xd2, 0x60}},
+	
+	{"xstatus_engineering", "Engineering",
+	 {0xba, 0x74, 0xdb, 0x3e, 0x9e, 0x24, 0x43, 0x4b,
+	  0x87, 0xb6, 0x2f, 0x6b, 0x8d, 0xfe, 0xe5, 0x0f}},
+	
+	{"xstatus_crapping", "In the restroom",
+	 {0x16, 0xf5, 0xb7, 0x6f, 0xa9, 0xd2, 0x40, 0x35,
+	  0x8c, 0xc5, 0xc0, 0x84, 0x70, 0x3c, 0x98, 0xfa}}
+};
+
 /*
  * Add the userinfo to our linked list.  If we already have userinfo
  * for this buddy, then just overwrite parts of the old data.
@@ -278,6 +437,9 @@
 		cur->sessionlen = userinfo->sessionlen;
 	if (userinfo->capabilities != 0)
 		cur->capabilities = userinfo->capabilities;
+	if (userinfo->customicon != 0)
+		cur->customicon = userinfo->customicon;
+
 	cur->present |= userinfo->present;
 
 	if (userinfo->iconcsumlen > 0) {
@@ -469,13 +631,38 @@
 					cap[8], cap[9],
 					cap[10], cap[11], cap[12], cap[13],
 					cap[14], cap[15]);
-
 		g_free(cap);
 	}
 
 	return flags;
 }
 
+gint32
+aim_get_custom_icon(OscarData *od, ByteStream *bs, int len)
+{
+	int offset;
+	gint32 result = -1;
+
+	for (offset = 0; byte_stream_empty(bs) && (offset < len); offset += 0x10) {
+		/* check wheather this capability is a custom user icon */
+		guint8 *cap;
+		int i;
+
+		cap = byte_stream_getraw(bs, 0x10);
+
+		for (i = 1; i < AIM_CUSTOM_ICONS_COUNT; i++) {
+			if (memcmp(&aim_custom_icons[i].data, cap, 0x10) == 0) {
+				purple_debug_misc("oscar", "Custom user icon: %s\n", aim_custom_icons[i].descriptivename);		
+				result = i;
+				break; /* should only match once... */
+			}
+		}
+		g_free(cap);
+	}
+
+	return result;
+}
+
 guint32
 aim_locate_getcaps_short(OscarData *od, ByteStream *bs, int len)
 {
@@ -566,6 +753,37 @@
 	g_free(info->away_encoding);
 }
 
+#define ICQMOODS_COUNT 23
+
+static const struct {
+	char *mood;
+	gint32 icon_num;
+} icqmoods[ICQMOODS_COUNT] = {
+	{"icqmood1", 12},
+	{"icqmood2", 18},
+	{"icqmood3", 24},
+	{"icqmood4", 30},
+	{"icqmood5", 1},
+	{"icqmood6", 7},
+	{"icqmood7", 13},
+	{"icqmood8", 19},
+	{"icqmood9", 25},
+	{"icqmood10", 31},
+	{"icqmood11", 11},
+	{"icqmood12", 8},
+	{"icqmood13", 14},
+	{"icqmood14", 20},
+	{"icqmood15", 26},
+	{"icqmood16", 32},
+	{"icqmood17", 9},
+	{"icqmood18", 15},
+	{"icqmood19", 21},
+	{"icqmood20", 27},
+	{"icqmood21", 33},
+	{"icqmood22", 10},
+	{"icqmood23", 6},
+};
+
 /*
  * AIM is fairly regular about providing user info.  This is a generic
  * routine to extract it in its standard form.
@@ -606,11 +824,12 @@
 	for (curtlv = 0; curtlv < tlvcnt; curtlv++) {
 		guint16 type, length;
 		int endpos;
+		int curpos;
 
 		type = byte_stream_get16(bs);
 		length = byte_stream_get16(bs);
-
-		endpos = byte_stream_curpos(bs) + MIN(length, byte_stream_empty(bs));
+		curpos = byte_stream_curpos(bs);
+		endpos = curpos + MIN(length, byte_stream_empty(bs));
 
 		if (type == 0x0001) {
 			/*
@@ -725,6 +944,8 @@
 			 */
 			outinfo->capabilities |= aim_locate_getcaps(od, bs, length);
 			outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES;
+			byte_stream_setpos(bs, curpos);
+			outinfo->customicon = aim_get_custom_icon(od, bs, length);
 
 		} else if (type == 0x000e) {
 			/*
@@ -853,6 +1074,27 @@
 							outinfo->itmsurl_encoding = NULL;
 						}
 					} break;
+
+					case 0x000e: { /* ICQ mood */
+						char *mood;
+						gint32 i;
+						gint32 icon_num = -1;
+
+						mood = byte_stream_getstr(bs, length2);
+
+						for (i = 0; i < ICQMOODS_COUNT; i++)
+							if (!strcmp(mood, icqmoods[i].mood)) {
+								icon_num = icqmoods[i].icon_num;
+								break; /* should only match once... */
+							}
+
+						if (icon_num >= 0)
+							outinfo->customicon = icon_num;
+						else
+							purple_debug_warning("oscar", "Unknown icqmood: %s", mood);
+
+						g_free(mood);
+					} break;
 				}
 
 				/* Save ourselves. */
@@ -933,7 +1175,7 @@
 #endif
 
 	if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES)
-		aim_tlvlist_add_caps(&tlvlist, 0x000d, info->capabilities);
+		aim_tlvlist_add_caps(&tlvlist, 0x000d, info->capabilities, info->customicon);
 
 	if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
 		aim_tlvlist_add_32(&tlvlist, (guint16)((info->flags & AIM_FLAG_AOL) ? 0x0010 : 0x000f), info->sessionlen);
@@ -1140,7 +1382,7 @@
 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)))
 		return -EINVAL;
 
-	aim_tlvlist_add_caps(&tlvlist, 0x0005, caps);
+	aim_tlvlist_add_caps(&tlvlist, 0x0005, caps, purple_account_get_int(purple_connection_get_account(od->gc), "customicon", -1));
 
 	byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
 
@@ -1226,6 +1468,8 @@
 		ByteStream cbs;
 		byte_stream_init(&cbs, tlv->value, tlv->length);
 		userinfo->capabilities = aim_locate_getcaps(od, &cbs, tlv->length);
+		byte_stream_rewind(&cbs);
+		userinfo->customicon = aim_get_custom_icon(od, &cbs, tlv->length);
 		userinfo->present = AIM_USERINFO_PRESENT_CAPABILITIES;
 	}
 	aim_tlvlist_free(tlvlist);
@@ -1453,3 +1697,33 @@
 
 	return 0;
 }
+
+guint32
+aim_get_custom_icons_count()
+{
+	return AIM_CUSTOM_ICONS_COUNT;
+}
+
+char*
+aim_get_custom_icon_filename(gint32 no)
+{
+	if (no >= AIM_CUSTOM_ICONS_COUNT || no < 1)
+		return NULL;
+	return aim_custom_icons[no].filename;
+}
+
+char*
+aim_get_custom_icon_descriptivename(gint32 no)
+{
+	if (no >= AIM_CUSTOM_ICONS_COUNT || no < 1)
+		return NULL;
+	return aim_custom_icons[no].descriptivename;
+}
+
+guint8*
+aim_get_custom_icon_data(gint32 no)
+{
+	if (no >= AIM_CUSTOM_ICONS_COUNT || no < 1)
+		return NULL;
+	return (guint8 *)aim_custom_icons[no].data;
+}
--- a/libpurple/protocols/oscar/oscar.c	Thu Nov 13 09:00:45 2008 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Thu Nov 13 17:04:53 2008 +0000
@@ -59,6 +59,11 @@
 #define OSCAR_STATUS_ID_NA          "na"
 #define OSCAR_STATUS_ID_OCCUPIED    "occupied"
 #define OSCAR_STATUS_ID_FREE4CHAT   "free4chat"
+#define OSCAR_STATUS_ID_EVIL        "evil"
+#define OSCAR_STATUS_ID_DEPRESSION	 "depression"
+#define OSCAR_STATUS_ID_ATHOME      "athome"
+#define OSCAR_STATUS_ID_ATWORK      "atwork"
+#define OSCAR_STATUS_ID_LUNCH       "lunch"
 #define OSCAR_STATUS_ID_CUSTOM      "custom"
 #define OSCAR_STATUS_ID_MOBILE	    "mobile"
 
@@ -68,7 +73,7 @@
 
 static OscarCapability purple_caps = (OSCAR_CAPABILITY_CHAT | OSCAR_CAPABILITY_BUDDYICON | OSCAR_CAPABILITY_DIRECTIM |
 									  OSCAR_CAPABILITY_SENDFILE | OSCAR_CAPABILITY_UNICODE | OSCAR_CAPABILITY_INTEROPERATE |
-									  OSCAR_CAPABILITY_SHORTCAPS);
+									  OSCAR_CAPABILITY_SHORTCAPS |  OSCAR_CAPABILITY_ICQSERVERRELAY | OSCAR_CAPABILITY_NEWCAPS | OSCAR_CAPABILITY_XTRAZ);
 
 static guint8 features_aim[] = {0x01, 0x01, 0x01, 0x02};
 static guint8 features_icq[] = {0x01, 0x06};
@@ -670,6 +675,9 @@
 			case OSCAR_CAPABILITY_GAMES:
 			case OSCAR_CAPABILITY_GAMES2:
 				tmp = _("Games");
+			case OSCAR_CAPABILITY_XTRAZ:
+			case OSCAR_CAPABILITY_NEWCAPS:
+				tmp = _("ICQ Xtraz");
 				break;
 			case OSCAR_CAPABILITY_ADDINS:
 				tmp = _("Add-Ins");
@@ -750,9 +758,17 @@
 		return g_strdup_printf(_("Away"));
 	else if (state & AIM_ICQ_STATE_WEBAWARE)
 		return g_strdup_printf(_("Web Aware"));
-	else if (state & AIM_ICQ_STATE_INVISIBLE)
-		return g_strdup_printf(_("Invisible"));
-	else
+	else if (state & AIM_ICQ_STATE_EVIL)
+		return g_strdup_printf(_("Evil"));
+	else if (state & AIM_ICQ_STATE_DEPRESSION)
+		return g_strdup_printf(_("Depression"));
+	else if (state & AIM_ICQ_STATE_ATHOME)
+		return g_strdup_printf(_("At home"));
+	else if (state & AIM_ICQ_STATE_ATWORK)
+		return g_strdup_printf(_("At work"));
+	else if (state & AIM_ICQ_STATE_LUNCH)
+		return g_strdup_printf(_("At lunch"));
+	else 
 		return g_strdup_printf(_("Online"));
 }
 
@@ -1992,6 +2008,16 @@
 			status_id = OSCAR_STATUS_ID_AWAY;
 		else if (type & AIM_ICQ_STATE_INVISIBLE)
 			status_id = OSCAR_STATUS_ID_INVISIBLE;
+		else if (type & AIM_ICQ_STATE_EVIL)
+			status_id = OSCAR_STATUS_ID_EVIL;
+		else if (type & AIM_ICQ_STATE_DEPRESSION)
+			status_id = OSCAR_STATUS_ID_DEPRESSION;
+		else if (type & AIM_ICQ_STATE_ATHOME)
+			status_id = OSCAR_STATUS_ID_ATHOME;
+		else if (type & AIM_ICQ_STATE_ATWORK)
+			status_id = OSCAR_STATUS_ID_ATWORK;
+		else if (type & AIM_ICQ_STATE_LUNCH)
+			status_id = OSCAR_STATUS_ID_LUNCH;
 		else
 			status_id = OSCAR_STATUS_ID_AVAILABLE;
 	} else {
@@ -2308,7 +2334,9 @@
 {
 	PurpleConnection *gc;
 	PurpleAccount *account;
+	PurpleMessageFlags flags = 0;
 	char *message = NULL;
+	char *rtfmsg = NULL;
 
 	g_return_val_if_fail(od != NULL, 0);
 	g_return_val_if_fail(od->gc != NULL, 0);
@@ -2338,6 +2366,21 @@
 		}
 	}
 
+
+	if (args->info.rtfmsg.rtfmsg != NULL)
+	{
+		if (args->encoding != NULL)
+		{
+			char *encoding = NULL;
+			encoding = oscar_encoding_extract(args->encoding);
+			rtfmsg = oscar_encoding_to_utf8(account, encoding, args->info.rtfmsg.rtfmsg,
+			                                 strlen(args->info.rtfmsg.rtfmsg));
+			g_free(encoding);
+		} else {
+			if (g_utf8_validate(args->info.rtfmsg.rtfmsg, strlen(args->info.rtfmsg.rtfmsg), NULL))
+				rtfmsg = g_strdup(args->info.rtfmsg.rtfmsg);
+		}
+	}
 	if (args->type & OSCAR_CAPABILITY_CHAT)
 	{
 		char *encoding, *utf8name, *tmp;
@@ -2425,6 +2468,21 @@
 	{
 		purple_debug_error("oscar", "Got an ICQ Server Relay message of "
 				"type %d\n", args->info.rtfmsg.msgtype);
+		purple_debug_error("oscar", "Sending X-Status Reply\n");
+
+		if(args->info.rtfmsg.msgtype == 26)
+		icq_relay_xstatus(od, userinfo->sn, args->cookie);
+		
+		if(args->info.rtfmsg.msgtype == 1)
+		{
+		if(rtfmsg)
+		serv_got_im(gc, userinfo->sn, rtfmsg, flags,
+		time(NULL));
+		else
+		serv_got_im(gc, userinfo->sn, args->info.rtfmsg.rtfmsg, flags,
+		time(NULL));
+		
+		}
 	}
 
 	else
@@ -2828,7 +2886,6 @@
 			args = va_arg(ap, struct aim_incomingim_ch4_args *);
 			ret = incomingim_chan4(od, conn, userinfo, args, 0);
 		} break;
-
 		default: {
 			purple_debug_warning("oscar",
 					   "ICBM received on unsupported channel (channel "
@@ -2976,6 +3033,28 @@
 
 		} break;
 
+		case 0x0006: { /* Reply from an ICQ status message request */
+			char *statusmsg, **splitmsg;
+			PurpleNotifyUserInfo *user_info;
+
+			/* Split at (carriage return/newline)'s, then rejoin later with BRs between. */
+			statusmsg = oscar_icqstatus(state);
+			splitmsg = g_strsplit(msg, "\r\n", 0);
+			
+			user_info = purple_notify_user_info_new();
+				
+			purple_notify_user_info_add_pair(user_info, _("UIN"), who);
+			purple_notify_user_info_add_pair(user_info, _("Status"), statusmsg);
+			purple_notify_user_info_add_section_break(user_info);
+			purple_notify_user_info_add_pair(user_info, NULL, g_strjoinv("<BR>", splitmsg));
+
+			g_free(statusmsg);
+			g_strfreev(splitmsg);
+
+			purple_notify_userinfo(gc, who, user_info, NULL, NULL);
+			purple_notify_user_info_destroy(user_info);
+
+		} break;
 		default: {
 			purple_debug_warning("oscar",
 					   "Received an unknown client auto-response from %s.  "
@@ -4629,10 +4708,22 @@
 		data |= AIM_ICQ_STATE_CHAT;
 	else if (!strcmp(status_id, OSCAR_STATUS_ID_INVISIBLE))
 		data |= AIM_ICQ_STATE_INVISIBLE;
+	else if (!strcmp(status_id, OSCAR_STATUS_ID_EVIL))
+		data |= AIM_ICQ_STATE_EVIL;
+	else if (!strcmp(status_id, OSCAR_STATUS_ID_DEPRESSION))
+		data |= AIM_ICQ_STATE_DEPRESSION;
+	else if (!strcmp(status_id, OSCAR_STATUS_ID_ATWORK))
+		data |= AIM_ICQ_STATE_ATWORK;
+	else if (!strcmp(status_id, OSCAR_STATUS_ID_ATHOME))
+		data |= AIM_ICQ_STATE_ATHOME;
+	else if (!strcmp(status_id, OSCAR_STATUS_ID_LUNCH))
+		data |= AIM_ICQ_STATE_LUNCH;
 	else if (!strcmp(status_id, OSCAR_STATUS_ID_CUSTOM))
 		data |= AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY;
 
+	
 	aim_srv_setextrainfo(od, TRUE, data, FALSE, NULL, NULL);
+	
 }
 
 static void
@@ -5777,6 +5868,7 @@
 			return "secure";
 		if (userinfo->icqinfo.status & AIM_ICQ_STATE_BIRTHDAY)
 			return "birthday";
+		return aim_get_custom_icon_filename(userinfo->customicon);
 	}
 	return NULL;
 }
@@ -5998,9 +6090,53 @@
 										   purple_value_new(PURPLE_TYPE_STRING), NULL);
 	status_types = g_list_prepend(status_types, type);
 
-	type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE,
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
 									 OSCAR_STATUS_ID_FREE4CHAT,
-									 _("Free For Chat"), TRUE, is_icq, FALSE);
+									 _("Free For Chat"), TRUE, is_icq, FALSE,
+									 "message", _("Message"),
+				purple_value_new(PURPLE_TYPE_STRING), NULL);
+
+	status_types = g_list_prepend(status_types, type);
+	
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
+									 OSCAR_STATUS_ID_EVIL,
+									 _("Evil"), TRUE, is_icq, FALSE,
+				 "message", _("Message"),
+				purple_value_new(PURPLE_TYPE_STRING), NULL);
+	status_types = g_list_prepend(status_types, type);
+
+
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
+									 OSCAR_STATUS_ID_DEPRESSION,
+									 _("Depression"), TRUE, is_icq, FALSE,
+				 "message", _("Message"),
+				purple_value_new(PURPLE_TYPE_STRING), NULL);
+	status_types = g_list_prepend(status_types, type);
+
+
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
+									 OSCAR_STATUS_ID_ATHOME,
+									 _("At home"), TRUE, is_icq, FALSE,
+				"message", _("Message"),
+				purple_value_new(PURPLE_TYPE_STRING), NULL);
+	status_types = g_list_prepend(status_types, type);
+
+
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
+									 OSCAR_STATUS_ID_ATWORK,
+									 _("At work"), TRUE, is_icq, FALSE,
+				"message", _("Message"),
+				purple_value_new(PURPLE_TYPE_STRING), NULL);
+
+	status_types = g_list_prepend(status_types, type);
+
+
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
+									 OSCAR_STATUS_ID_LUNCH,
+									 _("Lunch"), TRUE, is_icq, FALSE,
+				"message", _("Message"),
+				purple_value_new(PURPLE_TYPE_STRING), NULL);
+
 	status_types = g_list_prepend(status_types, type);
 
 	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY,
@@ -6010,9 +6146,12 @@
 										   purple_value_new(PURPLE_TYPE_STRING), NULL);
 	status_types = g_list_prepend(status_types, type);
 
-	type = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE,
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_INVISIBLE,
 									 OSCAR_STATUS_ID_INVISIBLE,
-									 NULL, TRUE, TRUE, FALSE);
+									 NULL, TRUE, TRUE, FALSE,
+									 "message", _("Message"),
+									  purple_value_new(PURPLE_TYPE_STRING), NULL);
+
 	status_types = g_list_prepend(status_types, type);
 
 	type = purple_status_type_new_full(PURPLE_STATUS_MOBILE, OSCAR_STATUS_ID_MOBILE, NULL, FALSE, FALSE, TRUE);
@@ -6180,6 +6319,23 @@
 	aim_locate_getinfoshort(gc->proto_data, purple_buddy_get_name(buddy), 0x00000003);
 }
 
+static void oscar_get_icqxstatusmsg (PurpleBlistNode *node, gpointer ignore)
+{
+	PurpleBuddy *buddy;
+	PurpleConnection *gc;
+	PurpleAccount *account;
+	
+	
+	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (PurpleBuddy *)node;
+	gc = purple_account_get_connection(buddy->account);
+	account = purple_connection_get_account(gc);
+	purple_debug_info("oscar",   "Manual X-Status Get From %s to %s:\n", purple_buddy_get_name(buddy), account->username);
+
+	icq_im_xstatus_request(gc->proto_data, purple_buddy_get_name(buddy));
+}
+
 static GList *
 oscar_buddy_menu(PurpleBuddy *buddy) {
 
@@ -6207,15 +6363,14 @@
 	                           NULL, NULL);
 	menu = g_list_prepend(menu, act);
 
-#if 0
 	if (od->icq)
 	{
-		act = purple_menu_action_new(_("Get Status Msg"),
-		                           PURPLE_CALLBACK(oscar_get_icqstatusmsg),
+		act = purple_menu_action_new(_("Get X-Status Msg"),
+		                           PURPLE_CALLBACK(oscar_get_icqxstatusmsg),
 		                           NULL, NULL);
 		menu = g_list_prepend(menu, act);
 	}
-#endif
+	
 
 	if (userinfo &&
 		aim_sncmp(purple_account_get_username(buddy->account), buddy->name) &&
@@ -6599,6 +6754,79 @@
 		purple_xfer_request(xfer);
 }
 
+static void
+oscar_show_icq_custom_icons_cb(PurpleConnection *gc, PurpleRequestFields *fields) {
+	OscarData *od = gc->proto_data;
+	PurpleAccount *account = purple_connection_get_account(gc);
+	PurpleRequestField *f;
+	GList *l;
+
+	f = purple_request_fields_get_field(fields, "customicon");
+	l = purple_request_field_list_get_selected(f);
+	
+	if (l) {
+		gpointer d = purple_request_field_list_get_data(f, l->data);		
+		purple_account_set_int(account, "customicon", GPOINTER_TO_INT(d));
+	}
+	
+	aim_locate_setcaps(od, purple_caps);
+}
+
+static void
+oscar_show_icq_custom_icons(PurplePluginAction *action)
+{
+	guint32 i;
+	gint32 customicon;
+	PurpleConnection *gc = (PurpleConnection *) action->context;
+	PurpleAccount *account = purple_connection_get_account(gc);
+	PurpleRequestFields *fields;
+	PurpleRequestFieldGroup *g;
+	PurpleRequestField *f;
+	char* na_fn;
+
+	customicon = purple_account_get_int(account, "customicon", 0);
+	
+	fields = purple_request_fields_new();
+
+	g = purple_request_field_group_new(NULL);
+	
+	f = purple_request_field_list_new("customicon", _("XStatus"));
+
+	purple_request_field_list_set_pixbuf(f, TRUE);
+
+	na_fn = g_build_filename("pixmaps", "pidgin", "emblems", "16", "not-authorized.png", NULL);
+
+	purple_request_field_list_add_icon(f, _("None"), na_fn, GINT_TO_POINTER(-1));
+	if (customicon == 0)
+		purple_request_field_list_add_selected(f, _("None"));
+	
+	g_free(na_fn);
+	
+	for (i = 1; i < aim_get_custom_icons_count(); i++) {
+		char* icon_path = g_strdup_printf("%s.png", aim_get_custom_icon_filename(i));
+		char* filename = g_build_filename("pixmaps", "pidgin", "emblems", "16", icon_path, NULL);
+
+		purple_request_field_list_add_icon(f, _(aim_get_custom_icon_descriptivename(i)), filename, GINT_TO_POINTER(i));
+
+		if (customicon == i)
+			purple_request_field_list_add_selected(f, _(aim_get_custom_icon_descriptivename(i)));
+
+		g_free(filename);
+		g_free(icon_path);
+	}
+	purple_request_field_group_add_field(g, f);
+	
+	purple_request_fields_add_group(fields, g);
+	
+	purple_request_fields(gc, _("Set Custom Icon"), _("Set Custom Icon"),
+						NULL, fields,
+						_("OK"), G_CALLBACK(oscar_show_icq_custom_icons_cb),
+						_("Cancel"), NULL,
+						purple_connection_get_account(gc), NULL, NULL,
+						gc);
+}
+
+
 GList *
 oscar_actions(PurplePlugin *plugin, gpointer context)
 {
@@ -6641,6 +6869,10 @@
 		act = purple_plugin_action_new(_("Set Privacy Options..."),
 				oscar_show_icq_privacy_opts);
 		menu = g_list_prepend(menu, act);
+		
+		act = purple_plugin_action_new(_("Set Custom Icon..."),
+				oscar_show_icq_custom_icons);
+		menu = g_list_prepend(menu, act);
 	}
 	else
 	{
--- a/libpurple/protocols/oscar/oscar.h	Thu Nov 13 09:00:45 2008 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Thu Nov 13 17:04:53 2008 +0000
@@ -362,8 +362,10 @@
 	OSCAR_CAPABILITY_LIVEVIDEO            = 0x02000000,
 	OSCAR_CAPABILITY_CAMERA               = 0x04000000,
 	OSCAR_CAPABILITY_ICHAT_SCREENSHARE    = 0x08000000,
-	OSCAR_CAPABILITY_GENERICUNKNOWN       = 0x10000000,
-	OSCAR_CAPABILITY_LAST                 = 0x20000000
+	OSCAR_CAPABILITY_NEWCAPS              = 0x10000000,
+	OSCAR_CAPABILITY_XTRAZ                = 0x20000000,
+	OSCAR_CAPABILITY_GENERICUNKNOWN       = 0x40000000,
+	OSCAR_CAPABILITY_LAST                 = 0x80000000
 } OscarCapability;
 
 /*
@@ -557,6 +559,12 @@
 #define AIM_ICQ_STATE_OUT               0x00000004
 #define AIM_ICQ_STATE_BUSY              0x00000010
 #define AIM_ICQ_STATE_CHAT              0x00000020
+#define AIM_ICQ_STATE_EVIL              0x00003000
+#define AIM_ICQ_STATE_DEPRESSION        0x00004000
+#define AIM_ICQ_STATE_ATHOME            0x00005000
+#define AIM_ICQ_STATE_ATWORK            0x00006000
+#define AIM_ICQ_STATE_LUNCH             0x00002001
+#define AIM_ICQ_STATE_EVIL              0x00003000
 #define AIM_ICQ_STATE_INVISIBLE         0x00000100
 #define AIM_ICQ_STATE_WEBAWARE          0x00010000
 #define AIM_ICQ_STATE_HIDEIP            0x00020000
@@ -966,6 +974,7 @@
 /* 0x000b */ int aim_im_denytransfer(OscarData *od, const char *sn, const guchar *cookie, guint16 code);
 /* 0x0010 */ int aim_im_reqofflinemsgs(OscarData *od);
 /* 0x0014 */ int aim_im_sendmtn(OscarData *od, guint16 type1, const char *sn, guint16 type2);
+/* 0x000b */ int icq_relay_xstatus (OscarData *od, const char *sn, const guchar* cookie);
 void aim_icbm_makecookie(guchar* cookie);
 gchar *oscar_encoding_extract(const char *encoding);
 gchar *oscar_encoding_to_utf8(PurpleAccount *account, const char *encoding, const char *text, int textlen);
@@ -1019,6 +1028,7 @@
 	guint32 onlinesince; /* time_t */
 	guint32 sessionlen;  /* in seconds */
 	guint32 capabilities;
+	gint32 customicon;
 	struct {
 		guint32 status;
 		guint32 ipaddr;
@@ -1097,8 +1107,12 @@
 void aim_info_free(aim_userinfo_t *);
 int aim_info_extract(OscarData *od, ByteStream *bs, aim_userinfo_t *);
 int aim_putuserinfo(ByteStream *bs, aim_userinfo_t *info);
-
-
+gint32 aim_get_custom_icon(OscarData *od, ByteStream *bs, int len);
+guint32 aim_get_custom_icons_count(void);
+char* aim_get_custom_icon_filename(gint32 no);
+char* aim_get_custom_icon_descriptivename(gint32 no);
+guint8* aim_get_custom_icon_data(gint32 no);
+int icq_im_xstatus_request(OscarData *od, const char *sn);
 
 /* 0x0003 - family_buddy.c */
 /* 0x0002 */ void aim_buddylist_reqrights(OscarData *, FlapConnection *);
@@ -1428,7 +1442,7 @@
 int aim_tlvlist_add_16(GSList **list, const guint16 type, const guint16 value);
 int aim_tlvlist_add_32(GSList **list, const guint16 type, const guint32 value);
 int aim_tlvlist_add_str(GSList **list, const guint16 type, const char *value);
-int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint32 caps);
+int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint32 caps, gint32 customicon);
 int aim_tlvlist_add_userinfo(GSList **list, guint16 type, aim_userinfo_t *userinfo);
 int aim_tlvlist_add_chatroom(GSList **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance);
 int aim_tlvlist_add_frozentlvlist(GSList **list, guint16 type, GSList **tl);
--- a/libpurple/protocols/oscar/tlv.c	Thu Nov 13 09:00:45 2008 +0000
+++ b/libpurple/protocols/oscar/tlv.c	Thu Nov 13 17:04:53 2008 +0000
@@ -407,10 +407,11 @@
  * @param caps Bitfield of capability flags to send
  * @return The size of the value added.
  */
-int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint32 caps)
+int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint32 caps, const gint32 customicon)
 {
 	guint8 buf[256]; /* TODO: Don't use a fixed length buffer */
 	ByteStream bs;
+	guint8 *data;
 
 	if (caps == 0)
 		return 0; /* nothing there anyway */
@@ -418,6 +419,12 @@
 	byte_stream_init(&bs, buf, sizeof(buf));
 
 	byte_stream_putcaps(&bs, caps);
+	
+	/* adding of custom icon GUID */
+	data = aim_get_custom_icon_data(customicon);	
+	if (data != NULL) {
+		byte_stream_putraw(&bs, data, 16);
+	}
 
 	return aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), buf);
 }
--- a/libpurple/request.c	Thu Nov 13 09:00:45 2008 +0000
+++ b/libpurple/request.c	Thu Nov 13 17:04:53 2008 +0000
@@ -790,6 +790,25 @@
 }
 
 void
+purple_request_field_list_set_pixbuf(PurpleRequestField *field,
+										 gboolean pixbuf)
+{
+	g_return_if_fail(field != NULL);
+	g_return_if_fail(field->type == PURPLE_REQUEST_FIELD_LIST);
+
+	field->u.list.pixbuf = pixbuf;
+}
+
+gboolean
+purple_request_field_list_get_pixbuf(const PurpleRequestField *field)
+{
+	g_return_val_if_fail(field != NULL, FALSE);
+	g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_LIST, FALSE);
+
+	return field->u.list.pixbuf;
+}
+
+void
 purple_request_field_list_set_multi_select(PurpleRequestField *field,
 										 gboolean multi_select)
 {
@@ -834,6 +853,22 @@
 }
 
 void
+purple_request_field_list_add_icon(PurpleRequestField *field, const char *item, const char* icon_path,
+							void *data)
+{
+	g_return_if_fail(field != NULL);
+	g_return_if_fail(item  != NULL);
+	g_return_if_fail(data  != NULL);
+	g_return_if_fail(icon_path != NULL);
+	g_return_if_fail(field->type == PURPLE_REQUEST_FIELD_LIST);
+
+	field->u.list.items = g_list_append(field->u.list.items, g_strdup(item));
+	field->u.list.icons = g_list_append(field->u.list.icons, g_strdup(icon_path));
+
+	g_hash_table_insert(field->u.list.item_data, g_strdup(item), data);
+}
+
+void
 purple_request_field_list_add_selected(PurpleRequestField *field, const char *item)
 {
 	g_return_if_fail(field != NULL);
@@ -935,6 +970,16 @@
 	return field->u.list.items;
 }
 
+GList *
+purple_request_field_list_get_icons(const PurpleRequestField *field)
+{
+	g_return_val_if_fail(field != NULL, NULL);
+	g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_LIST, NULL);
+	g_return_val_if_fail(field->u.list.pixbuf == TRUE, NULL);
+
+	return field->u.list.icons;
+}
+
 PurpleRequestField *
 purple_request_field_label_new(const char *id, const char *text)
 {
--- a/libpurple/request.h	Thu Nov 13 09:00:45 2008 +0000
+++ b/libpurple/request.h	Thu Nov 13 17:04:53 2008 +0000
@@ -147,10 +147,12 @@
 		{
 			GList *items;
 			GHashTable *item_data;
+			GList *icons;
 			GList *selected;
 			GHashTable *selected_table;
 
 			gboolean multiple_selection;
+			gboolean pixbuf;
 
 		} list;
 
@@ -877,6 +879,26 @@
 PurpleRequestField *purple_request_field_list_new(const char *id, const char *text);
 
 /**
+ * Sets whether or not a list field allows icons.
+ *
+ * @param field        The list field.
+ * @param multi_select TRUE if icons are enabled,
+ *                     or FALSE otherwise.
+ */
+void purple_request_field_list_set_pixbuf(PurpleRequestField *field,
+											  gboolean pixbuf);
+
+/**
+ * Returns whether or not a list field allows icons.
+ *
+ * @param field The list field.
+ *
+ * @return TRUE if icons are enabled, or FALSE otherwise.
+ */
+gboolean purple_request_field_list_get_pixbuf(
+	const PurpleRequestField *field);
+
+/**
  * Sets whether or not a list field allows multiple selection.
  *
  * @param field        The list field.
@@ -918,6 +940,17 @@
 								 const char *item, void *data);
 
 /**
+ * Adds an item with icon to a list field.
+ *
+ * @param field The list field.
+ * @param item  The list item.
+ * @param icon_path The path to icon file.
+ * @param data  The associated data.
+ */
+void purple_request_field_list_add_icon(PurpleRequestField *field,
+								 const char *item, const char* icon_path, void* data);
+
+/**
  * Adds a selected item to the list field.
  *
  * @param field The field.
@@ -975,6 +1008,15 @@
  */
 GList *purple_request_field_list_get_items(const PurpleRequestField *field);
 
+/**
+ * Returns a list of icons in a list field.
+ *
+ * @param field The field.
+ *
+ * @constreturn The list of icons.
+ */
+GList *purple_request_field_list_get_icons(const PurpleRequestField *field);
+
 /*@}*/
 
 /**************************************************************************/
--- a/pidgin/gtkrequest.c	Thu Nov 13 09:00:45 2008 +0000
+++ b/pidgin/gtkrequest.c	Thu Nov 13 17:04:53 2008 +0000
@@ -995,7 +995,9 @@
 	GtkTreeSelection *sel;
 	GtkTreeViewColumn *column;
 	GtkTreeIter iter;
-	GList *l;
+	GList *l, *ic = NULL;
+	GdkPixbuf* pixbuf;
+	gboolean icon = purple_request_field_list_get_pixbuf(field);
 
 	/* Create the scrolled window */
 	sw = gtk_scrolled_window_new(NULL, NULL);
@@ -1007,7 +1009,10 @@
 	gtk_widget_show(sw);
 
 	/* Create the list store */
-	store = gtk_list_store_new(2, G_TYPE_POINTER, G_TYPE_STRING);
+	if (icon)
+		store = gtk_list_store_new(3, G_TYPE_POINTER, G_TYPE_STRING, GDK_TYPE_PIXBUF);
+	else
+		store = gtk_list_store_new(2, G_TYPE_POINTER, G_TYPE_STRING);
 
 	/* Create the tree view */
 	treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
@@ -1026,13 +1031,42 @@
 	gtk_tree_view_column_pack_start(column, renderer, TRUE);
 	gtk_tree_view_column_add_attribute(column, renderer, "text", 1);
 
+	if(icon == TRUE)
+	{
+		renderer = gtk_cell_renderer_pixbuf_new();
+		gtk_tree_view_column_pack_start(column, renderer, TRUE);
+		gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", 2);
+
+		gtk_widget_set_size_request(treeview, 200, 400);
+	}
+
+	if(icon == TRUE)
+		ic = purple_request_field_list_get_icons(field);
+
 	for (l = purple_request_field_list_get_items(field); l != NULL; l = l->next)
 	{
 		const char *text = (const char *)l->data;
 
 		gtk_list_store_append(store, &iter);
 
-		gtk_list_store_set(store, &iter,
+		if(icon == TRUE)
+		{
+			const char *icon_path = (const char *)ic->data;
+			char* filename = g_build_filename(DATADIR, icon_path, NULL);
+
+			pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
+
+			g_free(filename);
+
+			gtk_list_store_set(store, &iter,
+						   0, purple_request_field_list_get_data(field, text),
+						   1, text,
+						   2, pixbuf,
+						   -1);
+			ic = ic->next;
+		}
+		else
+			gtk_list_store_set(store, &iter,
 						   0, purple_request_field_list_get_data(field, text),
 						   1, text,
 						   -1);
--- a/pidgin/pixmaps/Makefile.am	Thu Nov 13 09:00:45 2008 +0000
+++ b/pidgin/pixmaps/Makefile.am	Thu Nov 13 17:04:53 2008 +0000
@@ -126,7 +126,42 @@
 		emblems/16/secure.png \
 		emblems/16/unavailable.png \
 		emblems/16/video.png \
-		emblems/16/voice.png
+		emblems/16/voice.png \
+		emblems/16/xstatus_angry.png \
+		emblems/16/xstatus_bathing.png \
+		emblems/16/xstatus_beer.png \
+		emblems/16/xstatus_busy.png \
+		emblems/16/xstatus_cinema.png \
+		emblems/16/xstatus_coffee.png \
+		emblems/16/xstatus_crap.png \
+		emblems/16/xstatus_crapping.png \
+		emblems/16/xstatus_eating.png \
+		emblems/16/xstatus_engineering.png \
+		emblems/16/xstatus_friends.png \
+		emblems/16/xstatus_fun.png \
+		emblems/16/xstatus_gaming.png \
+		emblems/16/xstatus_google.png \
+		emblems/16/xstatus_heart.png \
+		emblems/16/xstatus_internet.png \
+		emblems/16/xstatus_iron.png \
+		emblems/16/xstatus_mobile.png \
+		emblems/16/xstatus_music.png \
+		emblems/16/xstatus_party.png \
+		emblems/16/xstatus_pda.png \
+		emblems/16/xstatus_phone.png \
+		emblems/16/xstatus_question.png \
+		emblems/16/xstatus_shopping.png \
+		emblems/16/xstatus_sick.png \
+		emblems/16/xstatus_sleeping.png \
+		emblems/16/xstatus_studying.png \
+		emblems/16/xstatus_surfing.png \
+		emblems/16/xstatus_thinking.png \
+		emblems/16/xstatus_tired.png \
+		emblems/16/xstatus_tv.png \
+		emblems/16/xstatus_typing.png \
+		emblems/16/xstatus_writing.png \
+		emblems/16/xstatus_zzz.png
+
 
 EMOTES_DEFAULT_24_SCALABLE = \
 		emotes/default/24/scalable/airplane.svg \