changeset 14611:000b8c063121

[gaim-migrate @ 17339] My previous implementation was a bit awkward. This requires less overhead. Also, let's only show those info fields that a buddy has actually filled out. committer: Tailor Script <tailor@pidgin.im>
author Mark Huetsch <markhuetsch>
date Fri, 22 Sep 2006 17:03:54 +0000
parents 473b225e7352
children 1f46715c08d9
files libgaim/protocols/qq/buddy_info.c libgaim/protocols/qq/buddy_info.h
diffstat 2 files changed, 338 insertions(+), 401 deletions(-) [+]
line wrap: on
line diff
--- a/libgaim/protocols/qq/buddy_info.c	Fri Sep 22 16:05:09 2006 +0000
+++ b/libgaim/protocols/qq/buddy_info.c	Fri Sep 22 17:03:54 2006 +0000
@@ -34,253 +34,200 @@
 #include "keep_alive.h"
 #include "send_core.h"
 
-/* Below is all of the information necessary to reconstruct the various
- * information fields that one can set in the official client. When we need
- * to know about a specific field (e.g., should "city" be a choice
- * or text field?), we can simply look it up from the template. Note that
- * there are a number of unidentified fields. */
-typedef struct _info_field {
-	gchar *title;
-	gchar *id;		/* used by gaim_request fields */
-	gint pos;
-	gchar *group;
-	gint group_pos;		/* for display order in the UI */
-	gint choice;		/* indicates which character array contains the choices */
-	gboolean customizable;	/* whether a user can enter any text as a value, regardless of choice arrays */
-	gchar *value;
-} info_field;
+#define QQ_PRIMARY_INFORMATION _("Primary Information")
+#define QQ_ADDITIONAL_INFORMATION _("Additional Information")
+#define QQ_INTRO _("Personal Introduction")
+#define QQ_NUMBER _("QQ Number")
+#define QQ_NICKNAME _("Nickname")
+#define QQ_NAME _("Name")
+#define QQ_AGE _("Age")
+#define QQ_GENDER _("Gender")
+#define QQ_COUNTRY _("Country/Region")
+#define QQ_PROVINCE _("Province/State")
+#define QQ_CITY _("City")
+#define QQ_HOROSCOPE _("Horoscope Symbol")
+#define QQ_OCCUPATION _("Occupation")
+#define QQ_ZODIAC _("Zodiac Sign")
+#define QQ_BLOOD _("Blood Type")
+#define QQ_COLLEGE _("College")
+#define QQ_EMAIL _("Email")
+#define QQ_ADDRESS _("Address")
+#define QQ_ZIPCODE _("Zipcode")
+#define QQ_CELL _("Cellphone Number")
+#define QQ_TELEPHONE _("Phone Number")
+#define QQ_HOMEPAGE _("Homepage")
 
-static const info_field info_template_data[] = {
-	{ N_("User ID"), "uid", 		0, QQ_MAIN_INFO, 0, QQ_NO_CHOICE, TRUE, NULL },	
-	{ N_("Nickname"), "nick",		1, QQ_MAIN_INFO, 1, QQ_NO_CHOICE, TRUE, NULL },	
-	{ N_("Country/Region"), "country",	2, QQ_MAIN_INFO, 5, QQ_COUNTRY, TRUE, NULL },
-	{ N_("Province/State"), "province",	3, QQ_MAIN_INFO, 6, QQ_PROVINCE, TRUE, NULL },
-	{ N_("Zipcode"), "zipcode",		4, QQ_EXTRA_INFO, 7, QQ_NO_CHOICE, TRUE, NULL },
-	{ N_("Address"), "address",		5, QQ_EXTRA_INFO, 6, QQ_NO_CHOICE, TRUE, NULL },
-	{ N_("Phone Number"), "tel",		6, QQ_EXTRA_INFO, 9, QQ_NO_CHOICE, TRUE, NULL },
-	{ N_("Age"), "age",			7, QQ_MAIN_INFO, 3, QQ_NO_CHOICE, TRUE, NULL },
-	{ N_("Gender"), "gender",		8, QQ_MAIN_INFO, 4, QQ_GENDER, FALSE, NULL },
-	{ N_("Name"), "name",			9, QQ_MAIN_INFO, 2, QQ_NO_CHOICE, TRUE, NULL },
-	{ N_("Email"), "email",			10, QQ_EXTRA_INFO, 5, QQ_NO_CHOICE, TRUE, NULL },
-	{ "pager_sn", "pager_sn",		11, QQ_MISC, 0, QQ_NO_CHOICE, TRUE, NULL },
-	{ "pager_num", "pager_num",		12, QQ_MISC, 1, QQ_NO_CHOICE, TRUE, NULL },
-	{ "pager_sp", "pager_sp",		13, QQ_MISC, 2, QQ_NO_CHOICE, TRUE, NULL },
-	{ "pager_base_num", "pager_base_num",	14, QQ_MISC, 3, QQ_NO_CHOICE, TRUE, NULL },
-	{ "pager_type", "pager_type",		15, QQ_MISC, 4, QQ_NO_CHOICE, TRUE, NULL },
-	{ N_("Occupation"), "occupation",	16, QQ_EXTRA_INFO, 1, QQ_OCCUPATION, TRUE, NULL },
-	{ N_("Homepage"), "homepage",		17, QQ_EXTRA_INFO, 10, QQ_NO_CHOICE, TRUE, NULL },
-	{ "auth_type", "auth_type",		18, QQ_MISC, 5, QQ_NO_CHOICE, TRUE, NULL },
-	{ "unknown1", "unknown1",		19, QQ_MISC, 6, QQ_NO_CHOICE, TRUE, NULL },
-	{ "unknown2", "unknown2",		20, QQ_MISC, 7, QQ_NO_CHOICE, TRUE, NULL },
-	{ "face", "face",			21, QQ_MISC, 8, QQ_NO_CHOICE, TRUE, NULL },
-	{ N_("Cellphone Number"), "hp_num",	22, QQ_EXTRA_INFO, 8, QQ_NO_CHOICE, TRUE, NULL },
-	{ "hp_type", "hp_type",			23, QQ_MISC, 9, QQ_NO_CHOICE, TRUE, NULL },
-	{ N_("Personal Introduction"), "intro",	24, QQ_PERSONAL_INTRO, 0, QQ_NO_CHOICE, TRUE, NULL },
-	{ N_("City"), "city",			25, QQ_MAIN_INFO, 7, QQ_NO_CHOICE, TRUE, NULL },
-	{ "unknown3", "unknown3",		26, QQ_MISC, 10, QQ_NO_CHOICE, TRUE, NULL },
-	{ "unknown4", "unknown4",		27, QQ_MISC, 11, QQ_NO_CHOICE, TRUE, NULL },
-	{ "unknown5", "unknown5",		28, QQ_MISC, 12, QQ_NO_CHOICE, TRUE, NULL },
-	{ "is_open_hp",	"is_open_hp",		29, QQ_MISC, 13, QQ_NO_CHOICE, TRUE, NULL },
-	{ "is_open_contact", "is_open_contact",	30, QQ_MISC, 14, QQ_NO_CHOICE, TRUE, NULL },
-	{ N_("College"), "college",		31, QQ_EXTRA_INFO, 4, QQ_NO_CHOICE, TRUE, NULL },
-	{ N_("Horoscope Symbol"), "horoscope",	32, QQ_EXTRA_INFO, 0, QQ_HOROSCOPE, FALSE, NULL },
-	{ N_("Zodiac Symbol"), "zodiac",	33, QQ_EXTRA_INFO, 2, QQ_ZODIAC, FALSE, NULL },
-	{ N_("Blood Type"), "blood",		34, QQ_EXTRA_INFO, 3, QQ_BLOOD, FALSE, NULL },
-	{ "qq_show", "qq_show",			35, QQ_MISC, 15, QQ_NO_CHOICE, TRUE, NULL },
-	{ "unknown6", "unknown6",		36, QQ_MISC, 16, QQ_NO_CHOICE, TRUE, NULL },
-	{ NULL,	NULL, 0, NULL, 0, 0, 0, NULL	}
-};
-
-/* TODO: translate these arrays to their English equivalents
- * and move these characters to the zh_CN po file */
+#define QQ_HOROSCOPE_SIZE 13
 static const gchar *horoscope_names[] = {
-	"-", "水瓶座", "双鱼座", "牡羊座", "金牛座",
-	"双子座", "巨蟹座", "狮子座", "处女座", "天秤座",
-        "天蝎座", "射手座", "魔羯座", NULL
-};
-
-static const gchar *zodiac_names[] = {
-	"-", "鼠", "牛", "虎", "兔",
-	"龙", "蛇", "马", "羊", "猴",
-	"鸡", "狗", "猪", NULL
-};
-
-static const gchar *blood_types[] = {
-	"-", N_("A"), N_("B"), N_("O"), N_("AB"), N_("Other"), NULL
-};
-
-static const gchar *genders[] = {
-	N_("Male"),
-	N_("Female"),
-	NULL
-};
-
-static const gchar *country_names[] = {
-        "中国", "中国香港", "中国澳门", "中国台湾",
-        "新加坡", "马来西亚", "美国", NULL
+	"-", N_("Aquarius"), N_("Pisces"), N_("Aries"), N_("Taurus"),
+	N_("Gemini"), N_("Cancer"), N_("Leo"), N_("Virgo"), N_("Libra"),
+	N_("Scorpio"), N_("Sagittarius"), N_("Capricorn")
 };
 
-static const gchar *province_names[] = {
-        "北京", "天津", "上海", "重庆", "香港",
-        "河北", "山西", "内蒙古", "辽宁", "吉林",
-        "黑龙江", "江西", "浙江", "江苏", "安徽",
-        "福建", "山东", "河南", "湖北", "湖南",
-        "广东", "广西", "海南", "四川", "贵州",
-        "云南", "西藏", "陕西", "甘肃", "宁夏",
-        "青海", "新疆", "台湾", "澳门", NULL
-};
-
-static const gchar *occupation_names[] = {
-        "全职", "兼职", "制造业", "商业", "失业中",
-        "学生", "工程师", "政府部门", "教育业", "服务行业",
-        "老板", "计算机业", "退休", "金融业",
-        "销售/广告/市场", NULL
+#define QQ_ZODIAC_SIZE 13
+static const gchar *zodiac_names[] = {
+	"-", N_("Rat"), N_("Ox"), N_("Tiger"), N_("Rabbit"),
+	N_("Dragon"), N_("Snake"), N_("Horse"), N_("Goat"), N_("Monkey"),
+	N_("Rooster"), N_("Dog"), N_("Pig")
 };
 
-static const gint choice_sizes[] = { 0, 13, 13, 6, 2, 7, 34, 15 };
-
-
-static const gchar *info_group_headers[] = {
-       QQ_MAIN_INFO,
-       QQ_EXTRA_INFO,
-       QQ_PERSONAL_INTRO,
-       QQ_MISC
+#define QQ_BLOOD_SIZE 6
+static const gchar *blood_types[] = {
+	"-", "A", "B", "O", "AB", N_("Other")
 };
 
-static const gchar **choices[] = {
-        NULL,
-        horoscope_names,
-        zodiac_names,
-        blood_types,
-        genders,
-        country_names,
-        province_names,
-        occupation_names
+#define QQ_GENDER_SIZE 2
+static const gchar *genders[] = {
+	N_("Male"),
+	N_("Female")
 };
 
-/************************ info and info_field methods ************************/
+#define QQ_CONTACT_FIELDS                               37
 
-/* Given an id, return the template for that field.
- * Returns NULL if the id is not found. */
-static const info_field *info_field_get_template(const gchar *id)
-{
-	const info_field *cur_field;
-	const gchar *cur_id;
-	
-	cur_field = info_template_data;
-	cur_id = cur_field->id;
-	while(cur_id != NULL) {
-		if (g_ascii_strcasecmp(cur_id, id) == 0) 
-			return cur_field;
-		cur_field++;
-		cur_id = cur_field->id;
-	}
-	gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Info field with id %s not found!", id);
-	return NULL;
-}
-
-/* info_fields are compared by their group positions */
-static gint info_field_compare(gconstpointer a, gconstpointer b)
-{
-	return ((info_field *) a)->group_pos - ((info_field *) b)->group_pos;
-}
+/* There is no user id stored in the reply packet for information query
+ * we have to manually store the query, so that we know the query source */
+typedef struct _qq_info_query {
+        guint32 uid;
+        gboolean show_window;
+        gboolean modify_info;
+} qq_info_query;
 
-static void info_field_free(info_field *i)
-{
-	g_free(i->value);
-	g_free(i);
-}
-
-/* Parses the info_template_data above and returns a newly-allocated list
- * containing the desired fields from segments. This list is ordered by
- * group_pos. */
-static GList *info_get_group(const gchar **info, const gchar *group_name)
-{
-	const info_field *cur;
-       	info_field *entry;
-	GList *group = NULL;
+/* We get an info packet on ourselves before we modify our information.
+ * Even though not all of the information is modifiable, it still
+ * all needs to be there when we send out the modify info packet */
+typedef struct _modify_info_data {
+        GaimConnection *gc;
+	contact_info *info;
+} modify_info_data;
 
-	cur = info_template_data;
-	while (cur->id != NULL) {
-		if (g_ascii_strcasecmp(group_name, cur->group) == 0) {
-			entry = g_memdup(cur, sizeof(info_field));
-			entry->value = g_strdup(info[entry->pos]);
-			group = g_list_insert_sorted(group, entry, info_field_compare);
-		}
-		cur++;
-	}
-
-	return group;
-}
-
-/* Determines if the given text value and choice group require
- * a lookup from the choice arrays. */
-static gboolean is_valid_index(gchar *value, gint choice)
+/* return -1 as a sentinel */
+static gint choice_index(const gchar *value, const gchar **choice, gint choice_size)
 {
 	gint len, i;
 
-	if (choice == 0) return FALSE;
 	len = strlen(value);
-	/* the server sends us an ascii index and none of the arrays has more than 99
-	 * elements */
-	if (len > 3 || len == 0) return FALSE;
-	for (i = 0; i < len; i++)
+	if (len > 3 || len == 0) return -1;
+	for (i = 0; i < len; i++) {
 		if (!g_ascii_isdigit(value[i])) 
-			return FALSE;
-	i = atoi(value);
-	if (i < 0 || i >= choice_sizes[choice]) 
-		return FALSE; 
-	return TRUE;
+			return -1;
+	}
+	i = strtol(value, NULL, 10);
+	if (i >= choice_size)
+		return -1;
+
+	return i;
+}
+
+/* return should be freed */
+static gchar *field_value(const gchar *field, const gchar **choice, gint choice_size)
+{
+	gint index, len;
+
+	len = strlen(field);
+	if (len == 0) {
+		return NULL;
+	} else if (choice != NULL) {
+		/* some choice fields are also customizable */
+		index = choice_index(field, choice, choice_size);
+		if (index == -1) {
+			if (strcmp(field, "-") != 0) {
+				return qq_to_utf8(field, QQ_CHARSET_DEFAULT);
+			} else {
+				return NULL;
+			}
+		/* else ASCIIized index */
+		} else {
+			if (strcmp(choice[index], "-") != 0)
+				return g_strdup(choice[index]);
+			else
+				return NULL;
+		}
+	} else {
+		if (strcmp(field, "-") != 0) {
+			return qq_to_utf8(field, QQ_CHARSET_DEFAULT);
+		} else {
+			return NULL;
+		}
+	}
+}
+
+static void append_field_value(GString *info_text, const gchar *field, 
+		const gchar *title, const gchar **choice, gint choice_size)
+{
+	gchar *value = field_value(field, choice, choice_size);
+	
+	if (value != NULL) {
+		g_string_append_printf(info_text, "<br /><b>%s:</b> %s", title, value);
+		g_free(value);
+	}
 }
 
-/* formats a field for printing */
-static void append_field_to_str(gpointer field, gpointer str)
+static GString *info_to_str(const contact_info *info)
 {
-	info_field *f;
-	gint choice;
-	gboolean valid_index;
-	gchar *value;
-
-	f = (info_field *) field;
-	choice = f->choice;
-	valid_index = is_valid_index(f->value, choice);
-	if (choice && valid_index) value = g_strdup(choices[choice][atoi(f->value)]);
-	else value = qq_to_utf8(f->value, QQ_CHARSET_DEFAULT);
-	g_string_append_printf((GString *) str, "<b>%s:</b> %s<br />",
-			f->title, value);
-	g_free(value);
-	info_field_free(f);
-}
-
-/* formats a group of information for printing */
-static void append_group_to_str(GString *str, const gchar *group_name, const gchar **info)
-{
-	GList *group;
-
-	group = info_get_group(info, group_name);
-	g_string_append_printf(str, "<b>%s</b><br /><br />", (*(info_field *) group->data).group);
-	g_list_foreach(group, append_field_to_str, str);
-	g_list_free(group);
-	g_string_append_printf(str, "<br />");
-}
-
-/* Takes a contact_info struct and outputs the appropriate fields in
- * a printable format for our upcoming call to gaim_notify_userinfo. */
-static GString *info_to_str(const gchar **info)
-{
-	GString *info_text;
+	GString *info_text, *extra_info;
+	const gchar *intro;
+	gint len;
 
 	info_text = g_string_new("");
-	append_group_to_str(info_text, QQ_MAIN_INFO, info);
-	append_group_to_str(info_text, QQ_EXTRA_INFO, info);
-	append_group_to_str(info_text, QQ_PERSONAL_INTRO, info);
-	/* append_group_to_str(info_text, QQ_MISC, info); */
+	g_string_append_printf(info_text, "<b>%s</b><br /><br />", QQ_PRIMARY_INFORMATION);
+	g_string_append_printf(info_text, "<b>%s:</b> %s", QQ_NUMBER, info->uid);
+	append_field_value(info_text, info->nick, QQ_NICKNAME, NULL, 0);
+	append_field_value(info_text, info->name, QQ_NAME, NULL, 0);
+	append_field_value(info_text, info->age, QQ_AGE, NULL, 0);
+	append_field_value(info_text, info->gender, QQ_GENDER, genders, QQ_GENDER_SIZE);
+	append_field_value(info_text, info->country, QQ_COUNTRY, NULL, 0);
+	append_field_value(info_text, info->province, QQ_PROVINCE, NULL, 0);
+	append_field_value(info_text, info->city, QQ_CITY, NULL, 0);
+
+	extra_info = g_string_new("");
+	g_string_append_printf(extra_info, "<br /><br /><b>%s</b><br />", QQ_ADDITIONAL_INFORMATION);
+	len = extra_info->len;
+	append_field_value(extra_info, info->horoscope, QQ_HOROSCOPE, horoscope_names, QQ_HOROSCOPE_SIZE);
+	append_field_value(extra_info, info->occupation, QQ_OCCUPATION, NULL, 0);
+	append_field_value(extra_info, info->zodiac, QQ_ZODIAC, zodiac_names, QQ_ZODIAC_SIZE);
+	append_field_value(extra_info, info->blood, QQ_BLOOD, blood_types, QQ_BLOOD_SIZE);
+	append_field_value(extra_info, info->college, QQ_COLLEGE, NULL, 0);
+	append_field_value(extra_info, info->email, QQ_EMAIL, NULL, 0);
+	append_field_value(extra_info, info->address, QQ_ADDRESS, NULL, 0);
+	append_field_value(extra_info, info->zipcode, QQ_ZIPCODE, NULL, 0);
+	append_field_value(extra_info, info->hp_num, QQ_CELL, NULL, 0);
+	append_field_value(extra_info, info->tel, QQ_TELEPHONE, NULL, 0);
+	append_field_value(extra_info, info->homepage, QQ_HOMEPAGE, NULL, 0);
+	if (len != extra_info->len)
+		g_string_append(info_text, extra_info->str);
+	g_string_free(extra_info, TRUE);
+
+	intro = field_value(info->intro, NULL, 0);
+	if (intro) {
+		g_string_append_printf(info_text, "<br /><br /><b>%s</b><br /><br />", QQ_INTRO);
+		g_string_append(info_text, intro);
+	}
+
+	/* for debugging */
+	/*
+	g_string_append_printf(info_text, "<br /><br /><b>%s</b><br />", "Miscellaneous");
+	append_field_value(info_text, info->pager_sn, "pager_sn", NULL, 0);
+	append_field_value(info_text, info->pager_num, "pager_num", NULL, 0);
+	append_field_value(info_text, info->pager_sp, "pager_sp", NULL, 0);
+	append_field_value(info_text, info->pager_base_num, "pager_base_num", NULL, 0);
+	append_field_value(info_text, info->pager_type, "pager_type", NULL, 0);
+	append_field_value(info_text, info->auth_type, "auth_type", NULL, 0);
+	append_field_value(info_text, info->unknown1, "unknown1", NULL, 0);
+	append_field_value(info_text, info->unknown2, "unknown2", NULL, 0);
+	append_field_value(info_text, info->face, "face", NULL, 0);
+	append_field_value(info_text, info->hp_type, "hp_type", NULL, 0);
+	append_field_value(info_text, info->unknown3, "unknown3", NULL, 0);
+	append_field_value(info_text, info->unknown4, "unknown4", NULL, 0);
+	append_field_value(info_text, info->unknown5, "unknown5", NULL, 0);
+	append_field_value(info_text, info->is_open_hp, "is_open_hp", NULL, 0);
+	append_field_value(info_text, info->is_open_contact, "is_open_contact", NULL, 0);
+	append_field_value(info_text, info->qq_show, "qq_show", NULL, 0);
+	append_field_value(info_text, info->unknown6, "unknown6", NULL, 0);
+	*/
 
 	return info_text;
 }
 
-/************************ packets and UI management **************************/
-
 /* send a packet to get detailed information of uid */
 void qq_send_packet_get_info(GaimConnection *gc, guint32 uid, gboolean show_window)
 {
@@ -320,26 +267,23 @@
 }
 
 /* send packet to modify personal information */
-void qq_send_packet_modify_info(GaimConnection *gc, contact_info *info)
+static void qq_send_packet_modify_info(GaimConnection *gc, gchar **segments)
 {
-	gchar *info_field[QQ_CONTACT_FIELDS];
 	gint i;
 	guint8 *raw_data, *cursor, bar;
 
-	g_return_if_fail(gc != NULL && info != NULL);
+	g_return_if_fail(gc != NULL && segments != NULL);
 
 	bar = 0x1f;
 	raw_data = g_newa(guint8, MAX_PACKET_SIZE - 128);
 	cursor = raw_data;
 
-	g_memmove(info_field, info, sizeof(gchar *) * QQ_CONTACT_FIELDS);
-
 	create_packet_b(raw_data, &cursor, bar);
 
-	/* important!, skip the first uid entry */
+	/* important! skip the first uid entry */
 	for (i = 1; i < QQ_CONTACT_FIELDS; i++) {
 		create_packet_b(raw_data, &cursor, bar);
-		create_packet_data(raw_data, &cursor, (guint8 *) info_field[i], strlen(info_field[i]));
+		create_packet_data(raw_data, &cursor, (guint8 *) segments[i], strlen(segments[i]));
 	}
 	create_packet_b(raw_data, &cursor, bar);
 
@@ -354,133 +298,122 @@
 	qd = (qq_data *) mid->gc->proto_data;
 	qd->modifying_info = FALSE;
 
-	g_list_free(mid->misc);
+	g_strfreev((gchar **) mid->info);
 	g_free(mid);
 }
 
-/* Runs through all of the fields in the modify info UI and puts
- * their values into the outgoing packet. */
-static void parse_field(gpointer field, gpointer outgoing_info)
+static gchar *parse_field(GList **list, gboolean choice)
 {
-	GaimRequestField *f;
-	gchar **segments, *value;
-	const info_field *ft;
-	const gchar *id;
+	gchar *value;
+	GaimRequestField *field;
 
-	f = (GaimRequestField *) field;
-	segments = (gchar **) outgoing_info;
-	id = gaim_request_field_get_id(f);
-	ft = info_field_get_template(id);
-	if (ft->choice && !ft->customizable) {
-		value = g_strdup_printf("%d", gaim_request_field_choice_get_value(f));
+	field = (GaimRequestField *) (*list)->data;
+	if (choice) {
+		value = g_strdup_printf("%d", gaim_request_field_choice_get_value(field));
 	} else {
-		value = (gchar *) gaim_request_field_string_get_value(f);
+		value = (gchar *) gaim_request_field_string_get_value(field);
 		if (value == NULL) 
 			value = g_strdup("-");
 		else 
 			value = utf8_to_qq(value, QQ_CHARSET_DEFAULT);
 	}
-	segments[ft->pos] = value;
+	*list = g_list_remove_link(*list, *list);
+
+	return value;
 }
 
-/* dumps the uneditable information straight into the outgoing packet */
-static void parse_misc_field(gpointer field, gpointer outgoing_info)
-{
-	info_field *f;
-	gchar **segments;
-
-	f = (info_field *) field;
-	segments = (gchar **) outgoing_info;
-	segments[f->pos] = g_strdup(f->value);
-	info_field_free(f);
-}
-
-/* Runs through all of the information fields and copies them into an
- * outgoing packet, then sends that packet. */
+/* parse fields and send info packet */
 static void modify_info_ok_cb(modify_info_data *mid, GaimRequestFields *fields)
 {
 	GaimConnection *gc;
 	qq_data *qd;
-	GList *list,  *groups, *group_node;
-	gchar *info_field[QQ_CONTACT_FIELDS];
+	GList *list,  *groups;
 	contact_info *info;
-	gint i;
 
 	gc = mid->gc;
 	qd = (qq_data *) gc->proto_data;
 	qd->modifying_info = FALSE;
-	list = mid->misc;
-	g_list_foreach(list, parse_misc_field, info_field);
-	g_list_free(list);
+
+	info = mid->info;
+
 	groups = gaim_request_fields_get_groups(fields);
-	while(groups) {
-		group_node = groups;
-		list = gaim_request_field_group_get_fields(group_node->data);
-		g_list_foreach(list, parse_field, info_field);
-		groups = g_list_remove_link(groups, group_node);
-	}
-	info = (contact_info *) info_field;
+	list = gaim_request_field_group_get_fields(groups->data);
+	info->uid = parse_field(&list, FALSE);
+	info->nick = parse_field(&list, FALSE);
+	info->name = parse_field(&list, FALSE);
+	info->age = parse_field(&list, FALSE);
+	info->gender = parse_field(&list, TRUE);
+	info->country = parse_field(&list, FALSE);
+	info->province = parse_field(&list, FALSE);
+	info->city = parse_field(&list, FALSE);
+	groups = g_list_remove_link(groups, groups);
+	list = gaim_request_field_group_get_fields(groups->data);
+	info->horoscope = parse_field(&list, TRUE);
+	info->occupation = parse_field(&list, FALSE);
+	info->zodiac = parse_field(&list, TRUE);
+	info->blood = parse_field(&list, TRUE);
+	info->college = parse_field(&list, FALSE);
+	info->email = parse_field(&list, FALSE);
+	info->address = parse_field(&list, FALSE);
+	info->zipcode = parse_field(&list, FALSE);
+	info->hp_num = parse_field(&list, FALSE);
+	info->tel = parse_field(&list, FALSE);
+	info->homepage = parse_field(&list, FALSE);
+	groups = g_list_remove_link(groups, groups);
+	list = gaim_request_field_group_get_fields(groups->data);
+	info->intro = parse_field(&list, FALSE);
+	groups = g_list_remove_link(groups, groups);
 
-	qq_send_packet_modify_info(gc, info);
+	qq_send_packet_modify_info(gc, (gchar **) info);
+
+	g_strfreev((gchar **) mid->info);
 	g_free(mid);
-	for (i = 0; i < QQ_CONTACT_FIELDS; i++)
-		g_free(info_field[i]);
 }
 
-/* Sets up the display for one group of information. This includes
- * managing which fields in the UI should be textfields and
- * which choices, and also mapping ints to choice values when appropriate. */
-static void setup_group(gpointer field, gpointer group)
+static GaimRequestFieldGroup *setup_field_group(GaimRequestFields *fields, const gchar *title)
 {
-	info_field *f;
-	GaimRequestFieldGroup *g;
-	GaimRequestField *rf;
-	gint choice, index, j;
-	gboolean customizable, valid_index, multiline;
-	gchar *id, *value;
+	GaimRequestFieldGroup *group;
 
-	f = (info_field *) field;
-	g = (GaimRequestFieldGroup *) group;
-	choice = f->choice;
-	customizable = f->customizable;
-	id = f->id;
-	valid_index = TRUE;
+	group = gaim_request_field_group_new(title);
+	gaim_request_fields_add_group(fields, group);
+
+	return group;
+}
 
-	if (!choice || customizable) {
-		valid_index = is_valid_index(f->value, choice);
-		multiline = id == "intro";
-		if (valid_index) {
-			index = atoi(f->value);
-			value = (gchar *) choices[choice][index];
-		} else {
-			value = qq_to_utf8(f->value, QQ_CHARSET_DEFAULT);
-		}
-		rf = gaim_request_field_string_new(id, f->title, value, multiline);
-	} else {
-		index = atoi(f->value);
-		value = (gchar *) choices[choice][index];
-		rf = gaim_request_field_choice_new(id, f->title, index);
-		j = 0;
-		while(choices[choice][j] != NULL)
-			gaim_request_field_choice_add(rf, choices[choice][j++]);
-	}
-	gaim_request_field_group_add_field(g, rf);
-	if (!valid_index) 
-		g_free(value);
-	info_field_free(f);
+static void add_string_field_to_group(GaimRequestFieldGroup *group, 
+		const gchar *id, const gchar *title, const gchar *value)
+{
+	GaimRequestField *field;
+	gchar *utf8_value;
+       
+	utf8_value = qq_to_utf8(value, QQ_CHARSET_DEFAULT);
+	field = gaim_request_field_string_new(id, title, utf8_value, FALSE);
+	gaim_request_field_group_add_field(group, field);
+	g_free(utf8_value);
 }
 
-/* Takes the info returned by a get_info packet for the user and sets up
- * a form using those values and the info_template. */
-static void create_modify_info_dialogue(GaimConnection *gc, const gchar **info)
+static void add_choice_field_to_group(GaimRequestFieldGroup *group, 
+		const gchar *id, const gchar *title, const gchar *value, 
+		const gchar **choice, gint choice_size)
+{
+	GaimRequestField *field;
+	gint i, index;
+	
+	index = choice_index(value, choice, choice_size);
+	field = gaim_request_field_choice_new(id, title, index);
+	for (i = 0; i < choice_size; i++)
+		gaim_request_field_choice_add(field, choice[i]);
+	gaim_request_field_group_add_field(group, field);
+}
+
+/* take the info returned by a get_info packet for myself and set up a request form */
+static void create_modify_info_dialogue(GaimConnection *gc, const contact_info *info)
 {
 	qq_data *qd;
+	GaimRequestFieldGroup *group;
 	GaimRequestFields *fields;
-	GaimRequestFieldGroup *group;
 	GaimRequestField *field;
-	GList *group_list;
 	modify_info_data *mid;
-	gint i;
 
 	/* so we only have one dialog open at a time */
 	qd = (qq_data *) gc->proto_data;
@@ -488,22 +421,58 @@
 		qd->modifying_info = TRUE;
 
 		fields = gaim_request_fields_new();
-	
-		/* we only care about the first 3 groups, not the miscellaneous stuff */
-		for (i = 0; i < 3; i++) {
-			group = gaim_request_field_group_new(info_group_headers[i]);
-			gaim_request_fields_add_group(fields, group);
-			group_list = info_get_group(info, info_group_headers[i]);
-			g_list_foreach(group_list, setup_group, group);
-			g_list_free(group_list);
-		}
 
-		field = gaim_request_fields_get_field(fields, "uid");
+		group = setup_field_group(fields, QQ_PRIMARY_INFORMATION);
+		field = gaim_request_field_string_new("uid", QQ_NUMBER, info->uid, FALSE);
+		gaim_request_field_group_add_field(group, field);
 		gaim_request_field_string_set_editable(field, FALSE);
+		add_string_field_to_group(group, "nick", QQ_NICKNAME, info->nick);
+		add_string_field_to_group(group, "name", QQ_NAME, info->name);
+		add_string_field_to_group(group, "age", QQ_AGE, info->age);
+		add_choice_field_to_group(group, "gender", QQ_GENDER, info->gender, genders, QQ_GENDER_SIZE);
+		add_string_field_to_group(group, "country", QQ_COUNTRY, info->country);
+		add_string_field_to_group(group, "province", QQ_PROVINCE, info->province);
+		add_string_field_to_group(group, "city", QQ_CITY, info->city);
+		group = setup_field_group(fields, QQ_ADDITIONAL_INFORMATION);
+		add_choice_field_to_group(group, "horoscope", QQ_HOROSCOPE, info->horoscope, horoscope_names, QQ_HOROSCOPE_SIZE);
+		add_string_field_to_group(group, "occupation", QQ_OCCUPATION, info->occupation);
+		add_choice_field_to_group(group, "zodiac", QQ_ZODIAC, info->zodiac, zodiac_names, QQ_ZODIAC_SIZE);
+		add_choice_field_to_group(group, "blood", QQ_BLOOD, info->blood, blood_types, QQ_BLOOD_SIZE);
+		add_string_field_to_group(group, "college", QQ_COLLEGE, info->college);
+		add_string_field_to_group(group, "email", QQ_EMAIL, info->email);
+		add_string_field_to_group(group, "address", QQ_ADDRESS, info->address);
+		add_string_field_to_group(group, "zipcode", QQ_ZIPCODE, info->zipcode);
+		add_string_field_to_group(group, "hp_num", QQ_CELL, info->hp_num);
+		add_string_field_to_group(group, "tel", QQ_TELEPHONE, info->tel);
+		add_string_field_to_group(group, "homepage", QQ_HOMEPAGE, info->homepage);
 
+		group = setup_field_group(fields, QQ_INTRO);
+		field = gaim_request_field_string_new("intro", QQ_INTRO, info->intro, TRUE);
+		gaim_request_field_group_add_field(group, field);
+
+		/* prepare unmodifiable info */
 		mid = g_new0(modify_info_data, 1);
 		mid->gc = gc;
-		mid->misc = info_get_group(info, info_group_headers[3]);
+		/* QQ_CONTACT_FIELDS+1 so that the array is NULL-terminated and can be g_strfreev()'ed later */
+		mid->info = (contact_info *) g_new0(gchar *, QQ_CONTACT_FIELDS+1);
+		mid->info->pager_sn = g_strdup(info->pager_sn);
+		mid->info->pager_num = g_strdup(info->pager_num);
+		mid->info->pager_sp = g_strdup(info->pager_sp);
+		mid->info->pager_base_num = g_strdup(info->pager_base_num);
+		mid->info->pager_type = g_strdup(info->pager_type);
+		mid->info->auth_type = g_strdup(info->auth_type);
+		mid->info->unknown1 = g_strdup(info->unknown1);
+		mid->info->unknown2 = g_strdup(info->unknown2);
+		mid->info->face = g_strdup(info->face);
+		mid->info->hp_type = g_strdup(info->hp_type);
+		mid->info->unknown3 = g_strdup(info->unknown3);
+		mid->info->unknown4 = g_strdup(info->unknown4);
+		mid->info->unknown5 = g_strdup(info->unknown5);
+		/* TODO stop hiding these 2 */
+		mid->info->is_open_hp = g_strdup(info->is_open_hp);
+		mid->info->is_open_contact = g_strdup(info->is_open_contact);
+		mid->info->qq_show = g_strdup(info->qq_show);
+		mid->info->unknown6 = g_strdup(info->unknown6);
 	
 		gaim_request_fields(gc, _("Modify my information"),
 			_("Modify my information"), NULL, fields,
@@ -599,7 +568,7 @@
 			gchar *icon = g_strdup_printf("%i", qd->my_icon);
 			qd->modifying_face = FALSE;
 			memcpy(info->face, icon, 2);
-			qq_send_packet_modify_info(gc, info);
+			qq_send_packet_modify_info(gc, segments);
 			g_free(icon);
 		}
 
@@ -611,11 +580,11 @@
 			query = (qq_info_query *) query_list->data;
 			if (query->uid == atoi(info->uid)) {
 				if (query->show_window) {
-					info_text = info_to_str((const gchar **) segments);
+					info_text = info_to_str(info);
 					gaim_notify_userinfo(gc, info->uid, info_text->str, NULL, NULL);
 					g_string_free(info_text, TRUE);
 				} else if (query->modify_info) {
-					create_modify_info_dialogue(gc, (const gchar **) segments);
+					create_modify_info_dialogue(gc, info);
 				}
 				qd->info_query = g_list_remove(qd->info_query, qd->info_query->data);
 				g_free(query);
--- a/libgaim/protocols/qq/buddy_info.h	Fri Sep 22 16:05:09 2006 +0000
+++ b/libgaim/protocols/qq/buddy_info.h	Fri Sep 22 17:03:54 2006 +0000
@@ -25,6 +25,7 @@
 
 #include <glib.h>
 #include "connection.h"
+
 #include "buddy_opt.h"
 #include "qq.h"
 
@@ -39,80 +40,47 @@
 #define QQ_BUDDY_GENDER_UNKNOWN     0xff
 
 typedef struct _contact_info {
-	gchar *uid;
-	gchar *nick;
-	gchar *country;
-	gchar *province;
-	gchar *zipcode;
-	gchar *address;
-	gchar *tel;
-	gchar *age;
-	gchar *gender;
-	gchar *name;
-	gchar *email;
-	gchar *pager_sn;
-	gchar *pager_num;
-	gchar *pager_sp;
-	gchar *pager_base_num;
-	gchar *pager_type;
-	gchar *occupation;
-	gchar *homepage;
-	gchar *auth_type;
-	gchar *unknown1;
-	gchar *unknown2;
-	gchar *face;
-	gchar *hp_num;
-	gchar *hp_type;
-	gchar *intro;
-	gchar *city;
-	gchar *unknown3;
-	gchar *unknown4;
-	gchar *unknown5;
-	gchar *is_open_hp;
-	gchar *is_open_contact;	
-	gchar *college;
-	gchar *horoscope;
-	gchar *zodiac;
-	gchar *blood;
-	gchar *qq_show;
-	gchar *unknown6;	/* always 0x2D */
+        gchar *uid;
+        gchar *nick;
+        gchar *country;
+        gchar *province;
+        gchar *zipcode;
+        gchar *address;
+        gchar *tel;
+        gchar *age;
+        gchar *gender;
+        gchar *name;
+        gchar *email;
+        gchar *pager_sn;
+        gchar *pager_num;
+        gchar *pager_sp;
+        gchar *pager_base_num;
+        gchar *pager_type;
+        gchar *occupation;
+        gchar *homepage;
+        gchar *auth_type;
+        gchar *unknown1;
+        gchar *unknown2;
+        gchar *face;
+        gchar *hp_num;
+        gchar *hp_type;
+        gchar *intro;
+        gchar *city;
+        gchar *unknown3;
+        gchar *unknown4;
+        gchar *unknown5;
+        gchar *is_open_hp;
+        gchar *is_open_contact;
+        gchar *college;
+        gchar *horoscope;
+        gchar *zodiac;
+        gchar *blood;
+        gchar *qq_show;
+        gchar *unknown6;        /* always 0x2D */
 } contact_info;
 
-/* There is no user id stored in the reply packet for information query
- * we have to manually store the query, so that we know the query source */
-typedef struct _qq_info_query {
-	guint32 uid;
-	gboolean show_window;
-	gboolean modify_info;
-} qq_info_query;
-
-/* We get an info packet on ourselves before we modify our information.
- * Even though not all of the information is currently modifiable, it still
- * all needs to be there when we send out the modify info packet */
-typedef struct _modify_info_data {
-	GaimConnection *gc;
-	GList *misc, *node;
-} modify_info_data;
-
-#define QQ_CONTACT_FIELDS 				37
-
-#define QQ_MAIN_INFO		"Primary Information"
-#define QQ_EXTRA_INFO		"Detailed Information"
-#define QQ_PERSONAL_INTRO	"Personal Introduction"
-#define QQ_MISC			"Miscellaneous"
-
-#define QQ_NO_CHOICE		0
-#define QQ_HOROSCOPE		 1
-#define QQ_ZODIAC		 2
-#define QQ_BLOOD		 3
-#define QQ_GENDER		 4
-#define QQ_COUNTRY		 5
-#define QQ_PROVINCE		 6
-#define QQ_OCCUPATION		 7
-
 void qq_refresh_buddy_and_myself(contact_info *info, GaimConnection *gc);
 void qq_send_packet_get_info(GaimConnection *gc, guint32 uid, gboolean show_window);
-void qq_send_packet_modify_info(GaimConnection *gc, contact_info *info);
 void qq_prepare_modify_info(GaimConnection *gc);
 void qq_process_modify_info_reply(guint8 *buf, gint buf_len, GaimConnection *gc);
 void qq_process_get_info_reply(guint8 *buf, gint buf_len, GaimConnection *gc);