changeset 29829:11cb7f2bb6e8

Add a PURPLE_STATUS_MOOD primitive and change ICQ to use it.
author Richard Laager <rlaager@wiktel.com>
date Mon, 17 Nov 2008 00:43:59 +0000
parents 1034b0d09398
children b92ae9c512ec
files ChangeLog.API libpurple/protocols/jabber/jabber.c libpurple/protocols/oscar/family_buddy.c libpurple/protocols/oscar/family_locate.c libpurple/protocols/oscar/oscar.c libpurple/protocols/oscar/oscar.h libpurple/protocols/oscar/tlv.c libpurple/status.c libpurple/status.h pidgin/gtkblist.c pidgin/gtksavedstatuses.c
diffstat 11 files changed, 254 insertions(+), 155 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog.API	Sun Nov 16 19:17:27 2008 +0000
+++ b/ChangeLog.API	Mon Nov 17 00:43:59 2008 +0000
@@ -1,5 +1,10 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+version 2.6.0 (??/??/????):
+	libpurple:
+		Added:
+		* PURPLE_STATUS_MOOD as a new PurpleStatusPrimitive
+
 version 2.5.0 (08/18/2008):
 	libpurple:
 		Added:
--- a/libpurple/protocols/jabber/jabber.c	Sun Nov 16 19:17:27 2008 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Mon Nov 17 00:43:59 2008 +0000
@@ -1658,7 +1658,7 @@
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
 			"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
 			NULL);
-	types = g_list_append(types, type);
+	types = g_list_prepend(types, type);
 
 	priority_value = purple_value_new(PURPLE_TYPE_INT);
 	purple_value_set_int(priority_value, 1);
@@ -1672,7 +1672,7 @@
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
 			"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
 			NULL);
-	types = g_list_append(types, type);
+	types = g_list_prepend(types, type);
 
 	priority_value = purple_value_new(PURPLE_TYPE_INT);
 	purple_value_set_int(priority_value, 0);
@@ -1686,7 +1686,7 @@
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
 			"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
 			NULL);
-	types = g_list_append(types, type);
+	types = g_list_prepend(types, type);
 
 	priority_value = purple_value_new(PURPLE_TYPE_INT);
 	purple_value_set_int(priority_value, 0);
@@ -1700,7 +1700,7 @@
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
 			"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
 			NULL);
-	types = g_list_append(types, type);
+	types = g_list_prepend(types, type);
 
 	priority_value = purple_value_new(PURPLE_TYPE_INT);
 	purple_value_set_int(priority_value, 0);
@@ -1714,11 +1714,11 @@
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
 			"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
 			NULL);
-	types = g_list_append(types, type);
+	types = g_list_prepend(types, type);
 
 	/*
 	if(js->protocol_version == JABBER_PROTO_0_9)
-		m = g_list_append(m, _("Invisible"));
+		"Invisible"
 	*/
 
 	type = purple_status_type_new_with_attrs(PURPLE_STATUS_OFFLINE,
@@ -1726,7 +1726,7 @@
 			NULL, FALSE, TRUE, FALSE,
 			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
 			NULL);
-	types = g_list_append(types, type);
+	types = g_list_prepend(types, type);
 
 	type = purple_status_type_new_with_attrs(PURPLE_STATUS_TUNE,
 			"tune", NULL, TRUE, TRUE, TRUE,
@@ -1740,9 +1740,9 @@
 			PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
 			PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
 			NULL);
-	types = g_list_append(types, type);
+	types = g_list_prepend(types, type);
 
-	return types;
+	return g_list_reverse(types);
 }
 
 static void
--- a/libpurple/protocols/oscar/family_buddy.c	Sun Nov 16 19:17:27 2008 +0000
+++ b/libpurple/protocols/oscar/family_buddy.c	Mon Nov 17 00:43:59 2008 +0000
@@ -225,9 +225,17 @@
 		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);
+	    userinfo.capabilities & OSCAR_CAPABILITY_XTRAZ) {
+		PurpleAccount *account = purple_connection_get_account(od->gc);
+		PurpleBuddy *buddy = purple_find_buddy(account, userinfo.sn);
 
+		if (buddy) {
+			PurplePresence *presence = purple_buddy_get_presence(buddy);
+
+			if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOOD))
+				icq_im_xstatus_request(od, userinfo.sn);
+		}
+	}
 	aim_info_free(&userinfo);
 
 	return ret;
--- a/libpurple/protocols/oscar/family_locate.c	Sun Nov 16 19:17:27 2008 +0000
+++ b/libpurple/protocols/oscar/family_locate.c	Mon Nov 17 00:43:59 2008 +0000
@@ -252,13 +252,11 @@
 	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
 };
 
-#define AIM_CUSTOM_ICONS_COUNT 35
-
 static const struct {
-	char *filename;
+	char *mood;
 	char *descriptivename;
 	guint8 data[16];
-} aim_custom_icons[AIM_CUSTOM_ICONS_COUNT] = {
+} aim_custom_icons[] = {
 	/* empty X-Status for the case when customicon == 0 */
 	{NULL, NULL,
 	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -443,8 +441,6 @@
 		cur->sessionlen = userinfo->sessionlen;
 	if (userinfo->capabilities != 0)
 		cur->capabilities = userinfo->capabilities;
-	if (userinfo->customicon != 0)
-		cur->customicon = userinfo->customicon;
 
 	cur->present |= userinfo->present;
 
@@ -643,11 +639,11 @@
 	return flags;
 }
 
-gint32
-aim_get_custom_icon(OscarData *od, ByteStream *bs, int len)
+static const char *
+aim_receive_custom_icon(OscarData *od, ByteStream *bs, int len)
 {
 	int offset;
-	gint32 result = -1;
+	const char *result = NULL;
 
 	for (offset = 0; byte_stream_empty(bs) && (offset < len); offset += 0x10) {
 		/* check wheather this capability is a custom user icon */
@@ -656,10 +652,10 @@
 
 		cap = byte_stream_getraw(bs, 0x10);
 
-		for (i = 1; i < AIM_CUSTOM_ICONS_COUNT; i++) {
+		for (i = 1; i < G_N_ELEMENTS(aim_custom_icons); i++) {
 			if (memcmp(&aim_custom_icons[i].data, cap, 0x10) == 0) {
 				purple_debug_misc("oscar", "Custom status icon: %s\n", aim_custom_icons[i].descriptivename);		
-				result = i;
+				result = aim_custom_icons[i].mood;
 				break; /* should only match once... */
 			}
 		}
@@ -759,36 +755,36 @@
 	g_free(info->away_encoding);
 }
 
-#define ICQMOODS_COUNT 23
+static const struct {
+	char *icqmood;
+	const char *mood;
+} icqmoods[] = {
+	{"icqmood0",  "shopping"},
+	{"icqmood1",  "bathing"},
+	{"icqmood2",  "yawn"},
+	{"icqmood3",  "party"},
+	{"icqmood4",  "beer"},
+	{"icqmood5",  "thinking"},
+	{"icqmood6",  "plate"},
+	{"icqmood7",  "tv"},
+	{"icqmood8",  "meeting"},
+	{"icqmood9",  "coffee"},
+	{"icqmood10", "music"},
+	{"icqmood11", "suit"},
+	{"icqmood12", "cinema"},
+	{"icqmood13", "smile-big"},
+	{"icqmood14", "phone"},
+	{"icqmood15", "console"},
+	{"icqmood16", "studying"},
+	{"icqmood17", "sick"},
+	{"icqmood18", "sleepy"},
+	{"icqmood19", "surfing"},
+	{"icqmood20", "internet"},
+	{"icqmood21", "working"},
+	{"icqmood22", "typing"},
+	{"icqmood23", "angry"},
+	{NULL, 0}
 
-static const struct {
-	char *mood;
-	gint32 icon_num;
-} icqmoods[ICQMOODS_COUNT] = {
-	{"icqmood0", 3},
-	{"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},
 };
 
 /*
@@ -946,13 +942,23 @@
 			outinfo->present |= AIM_USERINFO_PRESENT_ICQDATA;
 
 		} else if (type == 0x000d) {
+			PurpleAccount *account = purple_connection_get_account(od->gc);
+			const char *mood;
+
 			/*
 			 * OSCAR Capability information
 			 */
 			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);
+
+			mood = aim_receive_custom_icon(od, bs, length);
+			if (mood)
+				purple_prpl_got_user_status(account, outinfo->sn, "mood",
+						PURPLE_MOOD_NAME, mood,
+						NULL);
+			else
+				purple_prpl_got_user_status_deactive(account, outinfo->sn, "mood");
 
 		} else if (type == 0x000e) {
 			/*
@@ -1083,33 +1089,34 @@
 					} break;
 
 					case 0x000e: { /* ICQ mood */
-						char *mood;
+						PurpleAccount *account = purple_connection_get_account(od->gc);
+						char *icqmood;
 						gint32 i;
-						gint32 icon_num = -1;
+						const char *mood = NULL;
 
-						mood = byte_stream_getstr(bs, length2);
+						icqmood = byte_stream_getstr(bs, length2);
 
-						/* The official clients allow
-						 * you to set your custom icon
-						 * to the "default" icon, to
-						 * allow setting a status
-						 * message.  We'll ignore it.
-						 */
-						if (!*mood)
-							break;
-
-						for (i = 0; i < ICQMOODS_COUNT; i++)
-							if (!strcmp(mood, icqmoods[i].mood)) {
-								icon_num = icqmoods[i].icon_num;
-								break; /* should only match once... */
+						/* icqmood = "" means X-Status
+						 * with no mood icon. */
+						if (*icqmood) {
+							for (i = 0; icqmoods[i].icqmood; i++) {
+								if (!strcmp(icqmood, icqmoods[i].icqmood)) {
+									mood = icqmoods[i].mood;
+									break; /* should only match once... */
+								}
 							}
 
-						if (icon_num >= 0)
-							outinfo->customicon = icon_num;
+							if (!mood)
+								purple_debug_warning("oscar", "Unknown icqmood: %s\n", icqmood);
+						}
+						g_free(icqmood);
+
+						if (mood)
+							purple_prpl_got_user_status(account, outinfo->sn, "mood",
+									PURPLE_MOOD_NAME, mood,
+									NULL);
 						else
-							purple_debug_warning("oscar", "Unknown icqmood: %s\n", mood);
-
-						g_free(mood);
+							purple_prpl_got_user_status_deactive(account, outinfo->sn, "mood");
 					} break;
 				}
 
@@ -1155,6 +1162,10 @@
 	return 0;
 }
 
+/* Apparently, this is never called.
+ * If you activate it, figure out a way to know what mood to pass to
+ * aim_tlvlist_add_caps() below. --rlaager */
+#if 0
 /*
  * Inverse of aim_info_extract()
  */
@@ -1190,8 +1201,9 @@
 	}
 #endif
 
-	if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES)
-		aim_tlvlist_add_caps(&tlvlist, 0x000d, info->capabilities, info->customicon);
+	if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES) {
+		aim_tlvlist_add_caps(&tlvlist, 0x000d, info->capabilities, NULL);
+	}
 
 	if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
 		aim_tlvlist_add_32(&tlvlist, (guint16)((info->flags & AIM_FLAG_AOL) ? 0x0010 : 0x000f), info->sessionlen);
@@ -1202,6 +1214,7 @@
 
 	return 0;
 }
+#endif
 
 /*
  * Subtype 0x0001
@@ -1391,6 +1404,10 @@
 aim_locate_setcaps(OscarData *od, guint32 caps)
 {
 	FlapConnection *conn;
+	PurpleAccount *account = purple_connection_get_account(od->gc);
+	PurplePresence *presence = purple_account_get_presence(account);
+	PurpleStatus *status = purple_presence_get_status(presence, "mood");
+	const char *mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
 	ByteStream bs;
 	aim_snacid_t snacid;
 	GSList *tlvlist = NULL;
@@ -1398,7 +1415,7 @@
 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)))
 		return -EINVAL;
 
-	aim_tlvlist_add_caps(&tlvlist, 0x0005, caps, purple_account_get_int(purple_connection_get_account(od->gc), "customicon", -1));
+	aim_tlvlist_add_caps(&tlvlist, 0x0005, caps, mood);
 
 	byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
 
@@ -1482,11 +1499,21 @@
 	/* Caps will be 5 */
 	if ((tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1))) {
 		ByteStream cbs;
+		PurpleAccount *account = purple_connection_get_account(od->gc);
+		const char *mood;
+
 		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;
+
+		mood = aim_receive_custom_icon(od, &cbs, tlv->length);
+		if (mood)
+			purple_prpl_got_user_status(account, userinfo->sn, "mood",
+					PURPLE_MOOD_NAME, mood,
+					NULL);
+		else
+			purple_prpl_got_user_status_deactive(account, userinfo->sn, "mood");
 	}
 	aim_tlvlist_free(tlvlist);
 
@@ -1714,32 +1741,43 @@
 	return 0;
 }
 
-guint32
-aim_get_custom_icons_count()
+#if 1 //rlaager
+size_t aim_get_custom_icons_count(void)
 {
-	return AIM_CUSTOM_ICONS_COUNT;
+	return G_N_ELEMENTS(aim_custom_icons);
 }
 
-char*
-aim_get_custom_icon_filename(gint32 no)
+char* aim_get_custom_icon_mood(gint32 no)
 {
-	if (no >= AIM_CUSTOM_ICONS_COUNT || no < 1)
+	if (no >= G_N_ELEMENTS(aim_custom_icons) || no < 1)
 		return NULL;
-	return aim_custom_icons[no].filename;
+	return aim_custom_icons[no].mood;
 }
 
-char*
-aim_get_custom_icon_descriptivename(gint32 no)
+char* aim_get_custom_icon_descriptivename(gint32 no)
 {
-	if (no >= AIM_CUSTOM_ICONS_COUNT || no < 1)
+	if (no >= G_N_ELEMENTS(aim_custom_icons) || no < 1)
 		return NULL;
 	return aim_custom_icons[no].descriptivename;
 }
+#endif
 
 guint8*
-aim_get_custom_icon_data(gint32 no)
+aim_get_custom_icon_data(const char *mood)
 {
-	if (no >= AIM_CUSTOM_ICONS_COUNT || no < 1)
+	int i;
+
+	if (!(mood && *mood))
 		return NULL;
-	return (guint8 *)aim_custom_icons[no].data;
+
+	for (i = 1; i < G_N_ELEMENTS(aim_custom_icons); i++) {
+		/* We check that descriptivename is not NULL to exclude
+		 * duplicates, like the typing duplicate. */
+		if (aim_custom_icons[i].descriptivename &&
+		    aim_custom_icons[i].mood &&
+		    !strcmp(mood, aim_custom_icons[i].mood)) {
+			return (guint8 *)aim_custom_icons[i].data;
+		}
+	}
+	return NULL;
 }
--- a/libpurple/protocols/oscar/oscar.c	Sun Nov 16 19:17:27 2008 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Mon Nov 17 00:43:59 2008 +0000
@@ -4865,6 +4865,10 @@
 	if (!purple_account_is_connected(account))
 		return;
 
+	/* There's no need to do the stuff below for mood updates. */
+	if (purple_status_type_get_primitive(purple_status_get_type(status)) == PURPLE_STATUS_MOOD)
+		return;
+
 	/* Set the AIM-style away message for both AIM and ICQ accounts */
 	oscar_set_info_and_status(account, FALSE, NULL, TRUE, status);
 
@@ -5853,7 +5857,6 @@
 	}
 
 	if (userinfo != NULL ) {
-		const char *icon;
 		if (userinfo->flags & AIM_FLAG_ADMINISTRATOR)
 			return "admin";
 		if (userinfo->flags & AIM_FLAG_ACTIVEBUDDY)
@@ -5862,8 +5865,11 @@
 			return "secure";
 		if (userinfo->icqinfo.status & AIM_ICQ_STATE_BIRTHDAY)
 			return "birthday";
-		if ((icon = aim_get_custom_icon_filename(userinfo->customicon)))
-			return icon;
+
+		/* Make the mood icon override anything below this. */
+		if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOOD))
+			return NULL;
+
 		if (userinfo->capabilities & OSCAR_CAPABILITY_HIPTOP)
 			return "hiptop";
 	}
@@ -6181,9 +6187,14 @@
 									 NULL, TRUE, TRUE, FALSE);
 	status_types = g_list_prepend(status_types, type);
 
-	status_types = g_list_reverse(status_types);
-
-	return status_types;
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_MOOD,
+			"mood", NULL, TRUE, is_icq, TRUE,
+			PURPLE_MOOD_NAME, _("Mood Name"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_MOOD_COMMENT, _("Mood Comment"), purple_value_new(PURPLE_TYPE_STRING),
+			NULL);
+	status_types = g_list_prepend(status_types, type);
+
+	return g_list_reverse(status_types);
 }
 
 static void oscar_ssi_editcomment(struct name_data *data, const char *text) {
@@ -6751,87 +6762,98 @@
 		purple_xfer_request(xfer);
 }
 
+/* XXX: rlaager wants UI in the UI. */
+#if 1
 static void
-oscar_show_icq_custom_icons_cb(PurpleConnection *gc, PurpleRequestFields *fields) {
+oscar_show_icq_moods_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");
+	f = purple_request_fields_get_field(fields, "mood");
 	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));
-	}
-	
+		const char *mood = purple_request_field_list_get_data(f, l->data);
+		PurpleAccount *account = purple_connection_get_account(gc);
+
+		if (mood != NULL) {
+			purple_account_set_status(account, "mood", TRUE,
+			                          PURPLE_MOOD_NAME, mood,
+			                          NULL);
+		} else {
+			purple_account_set_status(account, "mood", FALSE, NULL);
+		}
+	}
+
 	aim_locate_setcaps(od, purple_caps);
 }
 
 static void
-oscar_show_icq_custom_icons(PurplePluginAction *action)
+oscar_show_icq_moods(PurplePluginAction *action)
 {
-	guint32 i;
-	gint32 customicon;
 	PurpleConnection *gc = (PurpleConnection *) action->context;
 	PurpleAccount *account = purple_connection_get_account(gc);
+	PurplePresence *presence = purple_account_get_presence(account);
+	PurpleStatus *status = purple_presence_get_status(presence, "mood");
+	const char *current_mood;
 	PurpleRequestFields *fields;
 	PurpleRequestFieldGroup *g;
 	PurpleRequestField *f;
+	guint32 i;
 	char* na_fn;
 
-	customicon = purple_account_get_int(account, "customicon", 0);
-	
+	current_mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
+
 	fields = purple_request_fields_new();
 
 	g = purple_request_field_group_new(NULL);
-	
-	f = purple_request_field_list_new("customicon", _("Choose a custom status icon"));
+
+	f = purple_request_field_list_new("mood", _("Please select your mood from the list."));
 
 	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_icon(f, _("None"), na_fn, NULL);
+	if (current_mood == NULL)
 		purple_request_field_list_add_selected(f, _("None"));
 
 	g_free(na_fn);
 
 	/* TODO: rlaager wants this sorted. */
 	for (i = 1; i < aim_get_custom_icons_count(); i++) {
-		const char *icon_filename = aim_get_custom_icon_filename(i);
+		const char *mood = aim_get_custom_icon_mood(i);
 		const char *icon_desc = aim_get_custom_icon_descriptivename(i);
 		char *icon_path;
 		char *filename;
 
-		if (icon_filename == NULL || icon_desc == NULL)
+		if (mood == NULL || icon_desc == NULL)
 			continue;
 
-		icon_path = g_strdup_printf("%s.png", icon_filename);
+		icon_path = g_strdup_printf("%s.png", mood);
 		filename = g_build_filename("pixmaps", "pidgin",
 		                            "emblems", "16",
 		                             icon_path, NULL);
 		g_free(icon_path);
 
 		purple_request_field_list_add_icon(f, _(icon_desc),
-				filename, GINT_TO_POINTER(i));
+				filename, (gpointer)mood);
 		g_free(filename);
 
-		if (customicon == i)
+		if (current_mood && !strcmp(current_mood, mood))
 			purple_request_field_list_add_selected(f, _(icon_desc));
 	}
 	purple_request_field_group_add_field(g, f);
 	
 	purple_request_fields_add_group(fields, g);
 	
-	purple_request_fields(gc, _("Custom Status Icon"), _("Custom Status Icon"),
+	purple_request_fields(gc, _("Edit User Mood"), _("Edit User Mood"),
 						NULL, fields,
-						_("OK"), G_CALLBACK(oscar_show_icq_custom_icons_cb),
+						_("OK"), G_CALLBACK(oscar_show_icq_moods_cb),
 						_("Cancel"), NULL,
 						purple_connection_get_account(gc), NULL, NULL,
 						gc);
 }
-
+#endif
 
 GList *
 oscar_actions(PurplePlugin *plugin, gpointer context)
@@ -6875,10 +6897,13 @@
 		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 Status Icon..."),
-				oscar_show_icq_custom_icons);
+
+/* XXX: rlaager wants UI in the UI. */
+#if 1
+		act = purple_plugin_action_new(_("Set Mood..."),
+				oscar_show_icq_moods);
 		menu = g_list_prepend(menu, act);
+#endif
 	}
 	else
 	{
--- a/libpurple/protocols/oscar/oscar.h	Sun Nov 16 19:17:27 2008 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Mon Nov 17 00:43:59 2008 +0000
@@ -1028,7 +1028,6 @@
 	guint32 onlinesince; /* time_t */
 	guint32 sessionlen;  /* in seconds */
 	guint32 capabilities;
-	gint32 customicon;
 	struct {
 		guint32 status;
 		guint32 ipaddr;
@@ -1106,12 +1105,15 @@
 guint32 aim_locate_getcaps_short(OscarData *od, ByteStream *bs, int len);
 void aim_info_free(aim_userinfo_t *);
 int aim_info_extract(OscarData *od, ByteStream *bs, aim_userinfo_t *);
+#if 0
 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);
+#endif
+#if 1
+size_t aim_get_custom_icons_count(void); //rlaager
+char* aim_get_custom_icon_mood(gint32 no);// rlaager
+char* aim_get_custom_icon_descriptivename(gint32 no); // rlaager
+#endif
+guint8* aim_get_custom_icon_data(const char *mood);
 int icq_im_xstatus_request(OscarData *od, const char *sn);
 
 /* 0x0003 - family_buddy.c */
@@ -1442,7 +1444,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, gint32 customicon);
+int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint32 caps, const char *mood);
 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	Sun Nov 16 19:17:27 2008 +0000
+++ b/libpurple/protocols/oscar/tlv.c	Mon Nov 17 00:43:59 2008 +0000
@@ -407,7 +407,7 @@
  * @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, const gint32 customicon)
+int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint32 caps, const char *mood)
 {
 	guint8 buf[256]; /* TODO: Don't use a fixed length buffer */
 	ByteStream bs;
@@ -421,10 +421,9 @@
 	byte_stream_putcaps(&bs, caps);
 	
 	/* adding of custom icon GUID */
-	data = aim_get_custom_icon_data(customicon);	
-	if (data != NULL) {
+	data = aim_get_custom_icon_data(mood);
+	if (data != NULL)
 		byte_stream_putraw(&bs, data, 16);
-	}
 
 	return aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), buf);
 }
--- a/libpurple/status.c	Sun Nov 16 19:17:27 2008 +0000
+++ b/libpurple/status.c	Mon Nov 17 00:43:59 2008 +0000
@@ -135,6 +135,7 @@
 	-200,   /* extended away            */
 	-400,   /* mobile                   */
 	0,      /* tune                     */
+	0,      /* mood                     */
 	-10,    /* idle, special case.      */
 	-5,     /* idle time, special case. */
 	10      /* Offline messageable      */
@@ -155,15 +156,16 @@
 
 } const status_primitive_map[] =
 {
-	{ PURPLE_STATUS_UNSET,           "unset",           N_("Unset")           },
-	{ PURPLE_STATUS_OFFLINE,         "offline",         N_("Offline")         },
-	{ PURPLE_STATUS_AVAILABLE,       "available",       N_("Available")       },
-	{ PURPLE_STATUS_UNAVAILABLE,     "unavailable",     N_("Do not disturb")     },
-	{ PURPLE_STATUS_INVISIBLE,       "invisible",       N_("Invisible")       },
-	{ PURPLE_STATUS_AWAY,            "away",            N_("Away")            },
-	{ PURPLE_STATUS_EXTENDED_AWAY,   "extended_away",   N_("Extended away")   },
-	{ PURPLE_STATUS_MOBILE,          "mobile",          N_("Mobile")          },
-	{ PURPLE_STATUS_TUNE,            "tune",            N_("Listening to music") }
+	{ PURPLE_STATUS_UNSET,           "unset",           N_("Unset")               },
+	{ PURPLE_STATUS_OFFLINE,         "offline",         N_("Offline")             },
+	{ PURPLE_STATUS_AVAILABLE,       "available",       N_("Available")           },
+	{ PURPLE_STATUS_UNAVAILABLE,     "unavailable",     N_("Do not disturb")      },
+	{ PURPLE_STATUS_INVISIBLE,       "invisible",       N_("Invisible")           },
+	{ PURPLE_STATUS_AWAY,            "away",            N_("Away")                },
+	{ PURPLE_STATUS_EXTENDED_AWAY,   "extended_away",   N_("Extended away")       },
+	{ PURPLE_STATUS_MOBILE,          "mobile",          N_("Mobile")              },
+	{ PURPLE_STATUS_TUNE,            "tune",            N_("Listening to music"), },
+	{ PURPLE_STATUS_MOOD,            "mood",            N_("Feeling")             },
 };
 
 const char *
--- a/libpurple/status.h	Sun Nov 16 19:17:27 2008 +0000
+++ b/libpurple/status.h	Mon Nov 17 00:43:59 2008 +0000
@@ -96,8 +96,7 @@
  */
 /*
  * If you add a value to this enum, make sure you update
- * the status_primitive_map array in status.c and the special-cases for idle
- * and offline-messagable just below it.
+ * the status_primitive_map and primitive_scores arrays in status.c.
  */
 typedef enum
 {
@@ -110,6 +109,7 @@
 	PURPLE_STATUS_EXTENDED_AWAY,
 	PURPLE_STATUS_MOBILE,
 	PURPLE_STATUS_TUNE,
+	PURPLE_STATUS_MOOD,
 	PURPLE_STATUS_NUM_PRIMITIVES
 } PurpleStatusPrimitive;
 
@@ -129,6 +129,9 @@
 #define PURPLE_TUNE_URL		"tune_url"
 #define PURPLE_TUNE_FULL	"tune_full"
 
+#define PURPLE_MOOD_NAME	"mood"
+#define PURPLE_MOOD_COMMENT	"moodtext"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
--- a/pidgin/gtkblist.c	Sun Nov 16 19:17:27 2008 +0000
+++ b/pidgin/gtkblist.c	Mon Nov 17 00:43:59 2008 +0000
@@ -3606,7 +3606,7 @@
 	PurplePluginProtocolInfo *prpl_info;
 	const char *name = NULL;
 	char *filename, *path;
-	PurplePresence *p;
+	PurplePresence *presence = NULL;
 
 	if(PURPLE_BLIST_NODE_IS_CONTACT(node)) {
 		if(!gtknode->contact_expanded) {
@@ -3616,8 +3616,8 @@
 	} else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
 		buddy = (PurpleBuddy*)node;
 		gtkbuddynode = node->ui_data;
-		p = purple_buddy_get_presence(buddy);
-		if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) {
+		presence = purple_buddy_get_presence(buddy);
+		if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOBILE)) {
 			path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems",
 						"16", "mobile.png", NULL);
 			return _pidgin_blist_get_cached_emblem(path);
@@ -3639,13 +3639,17 @@
 		return _pidgin_blist_get_cached_emblem(path);
 	}
 
-	p = purple_buddy_get_presence(buddy);
-	if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) {
+	/* If we came through the contact code flow above, we didn't need
+	 * to get the presence until now. */
+	if (presence == NULL)
+		presence = purple_buddy_get_presence(buddy);
+
+	if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOBILE)) {
 		path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "mobile.png", NULL);
 		return _pidgin_blist_get_cached_emblem(path);
 	}
 
-	if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_TUNE)) {
+	if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) {
 		path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "music.png", NULL);
 		return _pidgin_blist_get_cached_emblem(path);
 	}
@@ -3659,9 +3663,15 @@
 		name = prpl_info->list_emblem(buddy);
 
 	if (name == NULL) {
-		PurpleStatus *status = purple_presence_get_active_status(p);
-		name = purple_status_get_attr_string(status, "mood");
-		if(!(name && *name))
+		PurpleStatus *status;
+
+		if (!purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOOD))
+			return NULL;
+
+		status = purple_presence_get_status(presence, "mood");
+		name = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
+		
+		if (!(name && *name))
 			return NULL;
 	}
 
--- a/pidgin/gtksavedstatuses.c	Sun Nov 16 19:17:27 2008 +0000
+++ b/pidgin/gtksavedstatuses.c	Mon Nov 17 00:43:59 2008 +0000
@@ -921,7 +921,14 @@
 
 	for (i = PURPLE_STATUS_UNSET + 1; i < PURPLE_STATUS_NUM_PRIMITIVES; i++)
 	{
-		if (i == PURPLE_STATUS_MOBILE || i == PURPLE_STATUS_TUNE)
+		/* Someone should fix this for 3.0.0.  The independent boolean
+		 * should probably be set on the status type, not the status.
+		 * I guess that would prevent third party plugins from creating
+		 * independent statuses?
+		 */
+		if (i == PURPLE_STATUS_MOBILE ||
+		    i == PURPLE_STATUS_MOOD ||
+		    i == PURPLE_STATUS_TUNE)
 			/*
 			 * Special-case these.  They're intended to be independent
 			 * status types, so don't show them in the list.