comparison src/protocols/qq/buddy_info.c @ 13989:16102b9c5c4a

[gaim-migrate @ 16562] *Eliminated all Gtk-related code from the prpl. Notably, this included the group ("Qun") administrative dialog and a dialog for setting and viewing personal information. Code for the latter now uses the gaim UI, while the former is currently disabled. *Disabled a few non-functional/non-essential menu actions. These included: IP lookup, system logging, about dialog, and qq_buddy_menu. committer: Tailor Script <tailor@pidgin.im>
author Mark Huetsch <markhuetsch>
date Mon, 24 Jul 2006 13:39:12 +0000
parents 983fd420e86b
children e6977f9435a1
comparison
equal deleted inserted replaced
13988:4d5cc9e4cb12 13989:16102b9c5c4a
18 * You should have received a copy of the GNU General Public License 18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software 19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */ 21 */
22 22
23 // START OF FILE
24 /*****************************************************************************/
25 #include "internal.h" // strlen, _("get_text) 23 #include "internal.h" // strlen, _("get_text)
26 #include "debug.h" // gaim_debug 24 #include "debug.h" // gaim_debug
27 #include "notify.h" // gaim_notify 25 #include "notify.h" // gaim_notify
26 #include "request.h" // gaim_request_fields_new
28 27
29 #include "utils.h" // uid_to_gaim_name 28 #include "utils.h" // uid_to_gaim_name
30 #include "packet_parse.h" // MAX_PACKET_SIZE 29 #include "packet_parse.h" // MAX_PACKET_SIZE
31 #include "buddy_info.h" // 30 #include "buddy_info.h" //
32 #include "char_conv.h" // qq_to_utf8 31 #include "char_conv.h" // qq_to_utf8
33 #include "crypt.h" // qq_crypt 32 #include "crypt.h" // qq_crypt
34 #include "header_info.h" // cmd alias 33 #include "header_info.h" // cmd alias
35 #include "infodlg.h" // info_window
36 #include "keep_alive.h" // qq_update_buddy_contact 34 #include "keep_alive.h" // qq_update_buddy_contact
37 #include "send_core.h" // qq_send_cmd 35 #include "send_core.h" // qq_send_cmd
38 36
39 // amount of fiedls in user info 37 // Below is all of the information necessary to reconstruct the various
40 #define QQ_CONTACT_FIELDS 37 38 // information fields that one can set in the official client. When we need
41 39 // to know about a specific field (e.g., should "city" be a choice
42 // There is no user id stored in the reply packet for information query 40 // or text field?), we can simply look it up from the template. Note that
43 // we have to manually store the query, so that we know the query source 41 // there are a number of unidentified fields.
44 typedef struct _qq_info_query { 42
45 guint32 uid; 43 typedef struct _info_field {
46 gboolean show_window; 44 gchar *title;
47 contact_info *ret_info; 45 gchar *id; // used by gaim_request fields
48 } qq_info_query; 46 gint pos;
49 47 gchar *group;
50 /*****************************************************************************/ 48 gint group_pos; // for display order in the UI
51 // send a packet to get detailed information of uid, 49 gint choice; // indicates which character array contains the choices
52 // if show_window, a info window will be display upon receiving a reply 50 gboolean customizable; // whether a user can enter any text as a value, regardless of choice arrays
51 gchar *value;
52 } info_field;
53
54 static const info_field info_template_data[] = {
55 { N_("User ID"), "uid", 0, QQ_MAIN_INFO, 0, QQ_NO_CHOICE, TRUE, NULL },
56 { N_("Nickname"), "nick", 1, QQ_MAIN_INFO, 1, QQ_NO_CHOICE, TRUE, NULL },
57 { N_("Country/Region"), "country", 2, QQ_MAIN_INFO, 5, QQ_COUNTRY, TRUE, NULL },
58 { N_("Province/State"), "province", 3, QQ_MAIN_INFO, 6, QQ_PROVINCE, TRUE, NULL },
59 { N_("Zipcode"), "zipcode", 4, QQ_EXTRA_INFO, 7, QQ_NO_CHOICE, TRUE, NULL },
60 { N_("Address"), "address", 5, QQ_EXTRA_INFO, 6, QQ_NO_CHOICE, TRUE, NULL },
61 { N_("Phone Number"), "tel", 6, QQ_EXTRA_INFO, 9, QQ_NO_CHOICE, TRUE, NULL },
62 { N_("Age"), "age", 7, QQ_MAIN_INFO, 3, QQ_NO_CHOICE, TRUE, NULL },
63 { N_("Gender"), "gender", 8, QQ_MAIN_INFO, 4, QQ_GENDER, FALSE, NULL },
64 { N_("Name"), "name", 9, QQ_MAIN_INFO, 2, QQ_NO_CHOICE, TRUE, NULL },
65 { N_("Email"), "email", 10, QQ_EXTRA_INFO, 5, QQ_NO_CHOICE, TRUE, NULL },
66 { "pager_sn", "pager_sn", 11, QQ_MISC, 0, QQ_NO_CHOICE, TRUE, NULL },
67 { "pager_num", "pager_num", 12, QQ_MISC, 1, QQ_NO_CHOICE, TRUE, NULL },
68 { "pager_sp", "pager_sp", 13, QQ_MISC, 2, QQ_NO_CHOICE, TRUE, NULL },
69 { "pager_base_num", "pager_base_num", 14, QQ_MISC, 3, QQ_NO_CHOICE, TRUE, NULL },
70 { "pager_type", "pager_type", 15, QQ_MISC, 4, QQ_NO_CHOICE, TRUE, NULL },
71 { N_("Occupation"), "occupation", 16, QQ_EXTRA_INFO, 1, QQ_OCCUPATION, TRUE, NULL },
72 { N_("Homepage"), "homepage", 17, QQ_EXTRA_INFO, 10, QQ_NO_CHOICE, TRUE, NULL },
73 { "auth_type", "auth_type", 18, QQ_MISC, 5, QQ_NO_CHOICE, TRUE, NULL },
74 { "unknown1", "unknown1", 19, QQ_MISC, 6, QQ_NO_CHOICE, TRUE, NULL },
75 { "unknown2", "unknown2", 20, QQ_MISC, 7, QQ_NO_CHOICE, TRUE, NULL },
76 { "face", "face", 21, QQ_MISC, 8, QQ_NO_CHOICE, TRUE, NULL },
77 { N_("Cellphone Number"), "hp_num", 22, QQ_EXTRA_INFO, 8, QQ_NO_CHOICE, TRUE, NULL },
78 { "hp_type", "hp_type", 23, QQ_MISC, 9, QQ_NO_CHOICE, TRUE, NULL },
79 { N_("Personal Introduction"), "intro", 24, QQ_PERSONAL_INTRO, 0, QQ_NO_CHOICE, TRUE, NULL },
80 { N_("City"), "city", 25, QQ_MAIN_INFO, 7, QQ_NO_CHOICE, TRUE, NULL },
81 { "unknown3", "unknown3", 26, QQ_MISC, 10, QQ_NO_CHOICE, TRUE, NULL },
82 { "unknown4", "unknown4", 27, QQ_MISC, 11, QQ_NO_CHOICE, TRUE, NULL },
83 { "unknown5", "unknown5", 28, QQ_MISC, 12, QQ_NO_CHOICE, TRUE, NULL },
84 { "is_open_hp", "is_open_hp", 29, QQ_MISC, 13, QQ_NO_CHOICE, TRUE, NULL },
85 { "is_open_contact", "is_open_contact", 30, QQ_MISC, 14, QQ_NO_CHOICE, TRUE, NULL },
86 { N_("College"), "college", 31, QQ_EXTRA_INFO, 4, QQ_NO_CHOICE, TRUE, NULL },
87 { N_("Horoscope Symbol"), "horoscope", 32, QQ_EXTRA_INFO, 0, QQ_HOROSCOPE, FALSE, NULL },
88 { N_("Zodiac Symbol"), "zodiac", 33, QQ_EXTRA_INFO, 2, QQ_ZODIAC, FALSE, NULL },
89 { N_("Blood Type"), "blood", 34, QQ_EXTRA_INFO, 3, QQ_BLOOD, FALSE, NULL },
90 { "qq_show", "qq_show", 35, QQ_MISC, 15, QQ_NO_CHOICE, TRUE, NULL },
91 { "unknown6", "unknown6", 36, QQ_MISC, 16, QQ_NO_CHOICE, TRUE, NULL },
92 { NULL, NULL, 0, NULL, 0, 0, 0, NULL } //NULL termination
93 };
94
95 //TODO: translate these arrays to their English equivalents
96 // and move these characters to the zh_CN po file
97 static const gchar *horoscope_names[] = {
98 "-", "水瓶座", "双鱼座", "牡羊座", "金牛座",
99 "双子座", "巨蟹座", "狮子座", "处女座", "天秤座",
100 "天蝎座", "射手座", "魔羯座", NULL
101 };
102
103 static const gchar *zodiac_names[] = {
104 "-", "鼠", "牛", "虎", "兔",
105 "龙", "蛇", "马", "羊", "猴",
106 "鸡", "狗", "猪", NULL
107 };
108
109 static const gchar *blood_types[] = {
110 "其它", "A型", "B型", "O型", "AB型", NULL
111 };
112
113 static const gchar *genders[] = {
114 N_("Male"),
115 N_("Female"),
116 NULL
117 };
118
119 static const gchar *country_names[] = {
120 "中国", "中国香港", "中国澳门", "中国台湾",
121 "新加坡", "马来西亚", "美国", NULL
122 };
123
124 static const gchar *province_names[] = {
125 "北京", "天津", "上海", "重庆", "香港",
126 "河北", "山西", "内蒙古", "辽宁", "吉林",
127 "黑龙江", "江西", "浙江", "江苏", "安徽",
128 "福建", "山东", "河南", "湖北", "湖南",
129 "广东", "广西", "海南", "四川", "贵州",
130 "云南", "西藏", "陕西", "甘肃", "宁夏",
131 "青海", "新疆", "台湾", "澳门", NULL
132 };
133
134 static const gchar *occupation_names[] = {
135 "全职", "兼职", "制造业", "商业", "失业中",
136 "学生", "工程师", "政府部门", "教育业", "服务行业",
137 "老板", "计算机业", "退休", "金融业",
138 "销售/广告/市场", NULL
139 };
140
141 static const gint choice_sizes[] = { 0, 13, 13, 5, 2, 7, 34, 15 };
142
143
144 static const gchar *info_group_headers[] = {
145 QQ_MAIN_INFO,
146 QQ_EXTRA_INFO,
147 QQ_PERSONAL_INTRO,
148 QQ_MISC
149 };
150
151 static const gchar **choices[] = {
152 NULL,
153 horoscope_names,
154 zodiac_names,
155 blood_types,
156 genders,
157 country_names,
158 province_names,
159 occupation_names
160 };
161
162 /*************** info and info_field methods *****************/
163
164 // Given an id, return the template for that field.
165 // Returns NULL if the id is not found.
166 static const info_field *info_field_get_template(const gchar *id)
167 {
168 const info_field *cur_field;
169 const gchar *cur_id;
170
171 cur_field = info_template_data;
172 cur_id = cur_field->id;
173 while(cur_id != NULL) {
174 if (g_ascii_strcasecmp(cur_id, id) == 0) return cur_field;
175 cur_field++;
176 cur_id = cur_field->id;
177 }
178 gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Info field with id %s not found!", id);
179 return NULL;
180 }
181
182 // info_fields are compared by their group positions
183 static gint info_field_compare(gconstpointer a, gconstpointer b, gpointer unused)
184 {
185 return ((info_field *) a)->group_pos - ((info_field *) b)->group_pos;
186 }
187
188 static void info_field_free(info_field *i)
189 {
190 g_free(i->value);
191 g_free(i);
192 }
193
194 // Parses the info_template_data above and returns a newly-allocated list
195 // containing the desired fields from segments. This list is ordered by
196 // group_pos.
197 static GList *info_get_group(const gchar **info, const gchar *group_name)
198 {
199 const info_field *cur;
200 info_field *entry;
201 GList *group = NULL;
202
203 cur = info_template_data;
204 while (cur->id != NULL) {
205 if (g_ascii_strcasecmp(group_name, cur->group) == 0) {
206 entry = g_memdup(cur, sizeof(info_field));
207 entry->value = g_strdup(info[entry->pos]);
208 group = g_list_insert_sorted_with_data(group, entry, info_field_compare, NULL);
209 }
210 cur++;
211 }
212
213 return group;
214 }
215
216 // determines if the given text value and choice group require
217 // a lookup from the choice arrays
218 static gboolean is_valid_index(gchar *value, gint choice)
219 {
220 gint len, i;
221
222 if (choice == 0) return FALSE;
223 len = strlen(value);
224 // the server sends us an ascii index and none of arrays has more than 99
225 // elements
226 if (len > 3 || len == 0) return FALSE;
227 for (i = 0; i < len; i++)
228 if (!g_ascii_isdigit(value[i])) return FALSE;
229 i = atoi(value);
230 if (i < 0 || i >= choice_sizes[choice]) return FALSE;
231 return TRUE;
232 }
233
234 // formats a field for printing
235 static void append_field_to_str(gpointer field, gpointer str)
236 {
237 info_field *f;
238 gint choice;
239 gboolean valid_index;
240 gchar *value;
241
242 f = (info_field *) field;
243 choice = f->choice;
244 valid_index = is_valid_index(f->value, choice);
245 if (choice && valid_index) value = g_strdup(choices[choice][atoi(f->value)]);
246 else value = qq_to_utf8(f->value, QQ_CHARSET_DEFAULT);
247 g_string_append_printf((GString *) str, "<b>%s:</b> %s<br />",
248 f->title, value);
249 g_free(value);
250 info_field_free(f);
251 }
252
253 // formats a group of information for printing
254 static void append_group_to_str(GString *str, const gchar *group_name, const gchar **info)
255 {
256 GList *group;
257
258 group = info_get_group(info, group_name);
259 g_string_append_printf(str, "<b>%s</b><br /><br />", (*(info_field *) group->data).group);
260 g_list_foreach(group, append_field_to_str, str);
261 g_list_free(group);
262 g_string_append_printf(str, "<br />");
263 }
264
265 // takes a contact_info struct and outputs the appropriate fields in
266 // a printable format for our upcoming call to gaim_notify_userinfo
267 static GString *info_to_str(const gchar **info)
268 {
269 GString *info_text;
270
271 info_text = g_string_new("");
272 append_group_to_str(info_text, QQ_MAIN_INFO, info);
273 append_group_to_str(info_text, QQ_EXTRA_INFO, info);
274 append_group_to_str(info_text, QQ_PERSONAL_INTRO, info);
275 //if (QQ_DEBUG) append_group_to_str(info_text, QQ_MISC, info);
276
277 return info_text;
278 }
279
280 /*************** packets and UI management *****************/
281
282 // send a packet to get detailed information of uid
53 void qq_send_packet_get_info(GaimConnection * gc, guint32 uid, gboolean show_window) 283 void qq_send_packet_get_info(GaimConnection * gc, guint32 uid, gboolean show_window)
54 { 284 {
55 qq_data *qd; 285 qq_data *qd;
56 gchar *uid_str; 286 gchar *uid_str;
57 GList *list;
58 qq_info_query *query; 287 qq_info_query *query;
59 gboolean is_exist;
60 contact_info_window *info_window;
61 288
62 g_return_if_fail(gc != NULL && gc->proto_data != NULL && uid != 0); 289 g_return_if_fail(gc != NULL && gc->proto_data != NULL && uid != 0);
63 290
64 qd = (qq_data *) gc->proto_data; 291 qd = (qq_data *) gc->proto_data;
65 uid_str = g_strdup_printf("%d", uid); 292 uid_str = g_strdup_printf("%d", uid);
66 qq_send_cmd(gc, QQ_CMD_GET_USER_INFO, TRUE, 0, TRUE, uid_str, strlen(uid_str)); 293 qq_send_cmd(gc, QQ_CMD_GET_USER_INFO, TRUE, 0, TRUE, (guint8 *) uid_str, strlen(uid_str));
67
68 if (show_window) { // prepare the window
69 is_exist = FALSE; // see if there is already a window for this uid
70 list = qd->contact_info_window;
71 while (list != NULL) {
72 info_window = (contact_info_window *) list->data;
73 if (uid == info_window->uid) {
74 is_exist = TRUE;
75 break;
76 } else
77 list = list->next;
78 } // while list
79 if (!is_exist) { // create a new one
80 info_window = g_new0(contact_info_window, 1);
81 info_window->uid = uid;
82 qd->contact_info_window = g_list_append(qd->contact_info_window, info_window);
83 } // if !is_exist
84 } // if show_window
85 294
86 query = g_new0(qq_info_query, 1); 295 query = g_new0(qq_info_query, 1);
87 query->uid = uid; 296 query->uid = uid;
88 query->show_window = show_window; 297 query->show_window = show_window;
298 query->modify_info = FALSE;
89 qd->info_query = g_list_append(qd->info_query, query); 299 qd->info_query = g_list_append(qd->info_query, query);
90 300
91 g_free(uid_str); 301 g_free(uid_str);
92 } // qq_send_packet_get_info 302 }
93 303
94 /*****************************************************************************/ 304 // set up the fields requesting personal information and send a get_info packet
305 // for myself
306 void qq_prepare_modify_info(GaimConnection *gc)
307 {
308 qq_data *qd;
309 GList *ql;
310 qq_info_query *query;
311
312 qd = (qq_data *) gc->proto_data;
313 qq_send_packet_get_info(gc, qd->uid, FALSE);
314 // traverse backwards so we get the most recent info_query
315 for (ql = g_list_last(qd->info_query); ql != NULL; ql = g_list_previous(ql)) {
316 query = ql->data;
317 if (query->uid == qd->uid) query->modify_info = TRUE;
318 }
319 }
320
95 // send packet to modify personal information, and/or change password 321 // send packet to modify personal information, and/or change password
96 void qq_send_packet_modify_info(GaimConnection * gc, contact_info * info, gchar * new_passwd) 322 void qq_send_packet_modify_info(GaimConnection *gc, contact_info *info, gchar *new_passwd)
97 { 323 {
98 GaimAccount *a; 324 GaimAccount *a;
99 gchar *old_passwd, *info_field[QQ_CONTACT_FIELDS]; 325 gchar *old_passwd, *info_field[QQ_CONTACT_FIELDS];
100 gint i; 326 gint i;
101 guint8 *raw_data, *cursor, bar; 327 guint8 *raw_data, *cursor, bar;
110 336
111 g_memmove(info_field, info, sizeof(gchar *) * QQ_CONTACT_FIELDS); 337 g_memmove(info_field, info, sizeof(gchar *) * QQ_CONTACT_FIELDS);
112 338
113 if (new_passwd == NULL || strlen(new_passwd) == 0) 339 if (new_passwd == NULL || strlen(new_passwd) == 0)
114 create_packet_b(raw_data, &cursor, bar); 340 create_packet_b(raw_data, &cursor, bar);
115 else { // we gonna change passwd 341 else { // we're gonna change passwd
116 create_packet_data(raw_data, &cursor, old_passwd, strlen(old_passwd)); 342 create_packet_data(raw_data, &cursor, (guint8 *) old_passwd, strlen(old_passwd));
117 create_packet_b(raw_data, &cursor, bar); 343 create_packet_b(raw_data, &cursor, bar);
118 create_packet_data(raw_data, &cursor, new_passwd, strlen(new_passwd)); 344 create_packet_data(raw_data, &cursor, (guint8 *) new_passwd, strlen(new_passwd));
119 } 345 }
120 346
121 // important!, skip the first uid entry 347 // important!, skip the first uid entry
122 for (i = 1; i < QQ_CONTACT_FIELDS; i++) { 348 for (i = 1; i < QQ_CONTACT_FIELDS; i++) {
123 create_packet_b(raw_data, &cursor, bar); 349 create_packet_b(raw_data, &cursor, bar);
124 create_packet_data(raw_data, &cursor, info_field[i], strlen(info_field[i])); 350 create_packet_data(raw_data, &cursor, (guint8 *) info_field[i], strlen(info_field[i]));
125 } 351 }
126 create_packet_b(raw_data, &cursor, bar); 352 create_packet_b(raw_data, &cursor, bar);
127 353
128 qq_send_cmd(gc, QQ_CMD_UPDATE_INFO, TRUE, 0, TRUE, raw_data, cursor - raw_data); 354 qq_send_cmd(gc, QQ_CMD_UPDATE_INFO, TRUE, 0, TRUE, raw_data, cursor - raw_data);
129 355
130 } // qq_send_packet_modify_info 356 }
131 357
132 /*****************************************************************************/ 358 static void modify_info_cancel_cb(modify_info_data *mid)
133 // process the reply of modidy_info packet 359 {
134 void qq_process_modify_info_reply(guint8 * buf, gint buf_len, GaimConnection * gc) 360 g_list_free(mid->misc);
361 g_free(mid);
362 }
363
364 // runs through all of the fields in the modify info UI and put
365 // their values into the outgoing packet
366 static void parse_field(gpointer field, gpointer outgoing_info)
367 {
368 GaimRequestField *f;
369 gchar **segments, *value;
370 const info_field *ft;
371 const gchar *id;
372
373 f = (GaimRequestField *) field;
374 segments = (gchar **) outgoing_info;
375 id = gaim_request_field_get_id(f);
376 ft = info_field_get_template(id);
377 if (ft->choice && !ft->customizable)
378 value = g_strdup_printf("%d", gaim_request_field_choice_get_value(f));
379 else {
380 value = (gchar *) gaim_request_field_string_get_value(f);
381 if (value == NULL) value = g_strdup("");
382 else value = utf8_to_qq(value, QQ_CHARSET_DEFAULT);
383 }
384 segments[ft->pos] = value;
385 }
386
387 // dumps the uneditable information straight into the outgoing packet
388 static void parse_misc_field(gpointer field, gpointer outgoing_info)
389 {
390 info_field *f;
391 gchar **segments;
392
393 f = (info_field *) field;
394 segments = (gchar **) outgoing_info;
395 segments[f->pos] = g_strdup(f->value);
396 info_field_free(f);
397 }
398
399 // runs through all of the information fields and copies them into an
400 // outgoing packet, then sends that packet
401 static void modify_info_ok_cb(modify_info_data *mid, GaimRequestFields *fields)
402 {
403 GaimConnection *gc;
404 GList *list, *groups, *group_node;
405 gchar *info_field[QQ_CONTACT_FIELDS];
406 contact_info *info;
407 gint i;
408
409 gc = mid->gc;
410 list = mid->misc;
411 g_list_foreach(list, parse_misc_field, info_field);
412 g_list_free(list);
413 groups = gaim_request_fields_get_groups(fields);
414 while(groups) {
415 group_node = groups;
416 list = gaim_request_field_group_get_fields(group_node->data);
417 g_list_foreach(list, parse_field, info_field);
418 groups = g_list_remove_link(groups, group_node);
419 }
420 info = (contact_info *) info_field;
421
422 qq_send_packet_modify_info(gc, info, NULL);
423 g_free(mid);
424 for (i = 0; i < QQ_CONTACT_FIELDS; i++)
425 g_free(info_field[i]);
426 }
427
428 // Sets up the display for one group of information. This includes
429 // managing which fields in the UI should be textfields and
430 // which choices, and also mapping ints to choice values when appropriate.
431 static void setup_group(gpointer field, gpointer group)
432 {
433 info_field *f;
434 GaimRequestFieldGroup *g;
435 GaimRequestField *rf;
436 gint choice, index, j;
437 gboolean customizable, valid_index, multiline;
438 gchar *id, *value;
439
440 f = (info_field *) field;
441 g = (GaimRequestFieldGroup *) group;
442 choice = f->choice;
443 customizable = f->customizable;
444 id = f->id;
445 valid_index = TRUE;
446
447 if (!choice || customizable) {
448 valid_index = is_valid_index(f->value, choice);
449 multiline = id == "intro";
450 if (valid_index) {
451 index = atoi(f->value);
452 value = (gchar *) choices[choice][index];
453 } else value = qq_to_utf8(f->value, QQ_CHARSET_DEFAULT);
454 rf = gaim_request_field_string_new(id, f->title, value, multiline);
455 } else {
456 index = atoi(f->value);
457 value = (gchar *) choices[choice][index];
458 rf = gaim_request_field_choice_new(id, f->title, index);
459 j = 0;
460 while(choices[choice][j] != NULL)
461 gaim_request_field_choice_add(rf, choices[choice][j++]);
462 }
463 gaim_request_field_group_add_field(g, rf);
464 if (!valid_index) g_free(value);
465 info_field_free(f);
466 }
467
468 // Takes the info returned by a get_info packet for the user and sets up
469 // a form using those values and the info_template.
470 static void create_modify_info_dialogue(GaimConnection *gc, const gchar **info)
471 {
472 GaimRequestFields *fields;
473 GaimRequestFieldGroup *group;
474 GaimRequestField *field;
475 GList *group_list;
476 modify_info_data *mid;
477 gint i;
478
479 fields = gaim_request_fields_new();
480
481 // we only care about the first 3 groups, not the miscellaneous stuff
482 for (i = 0; i < 3; i++) {
483 group = gaim_request_field_group_new(info_group_headers[i]);
484 gaim_request_fields_add_group(fields, group);
485 group_list = info_get_group(info, info_group_headers[i]);
486 g_list_foreach(group_list, setup_group, group);
487 g_list_free(group_list);
488 }
489
490 //set this manually here instead of generating a new template column
491 field = gaim_request_fields_get_field(fields, "uid");
492 gaim_request_field_string_set_editable(field, FALSE);
493
494 //we need to pass the info that doesn't get modified as aux data
495 //because we'll still need it when we send the modify_info packet
496 mid = g_new0(modify_info_data, 1);
497 mid->gc = gc;
498 mid->misc = info_get_group(info, info_group_headers[3]);
499
500 gaim_request_fields(gc, _("Modify my information"),
501 _("Modify my information"), NULL, fields,
502 _("Update my information"), G_CALLBACK(modify_info_ok_cb),
503 _("Cancel"), G_CALLBACK(modify_info_cancel_cb),
504 mid);
505 }
506
507 // process the reply of modify_info packet
508 void qq_process_modify_info_reply(guint8 *buf, gint buf_len, GaimConnection *gc)
135 { 509 {
136 qq_data *qd; 510 qq_data *qd;
137 gint len; 511 gint len;
138 guint8 *data; 512 guint8 *data;
139 513
143 qd = (qq_data *) gc->proto_data; 517 qd = (qq_data *) gc->proto_data;
144 len = buf_len; 518 len = buf_len;
145 data = g_newa(guint8, len); 519 data = g_newa(guint8, len);
146 520
147 if (qq_crypt(DECRYPT, buf, buf_len, qd->session_key, data, &len)) { 521 if (qq_crypt(DECRYPT, buf, buf_len, qd->session_key, data, &len)) {
148 if (qd->uid == atoi(data)) { // return should be my uid 522 if (qd->uid == atoi((gchar *) data)) { // return should be my uid
149 gaim_debug(GAIM_DEBUG_INFO, "QQ", "Update info ACK OK\n"); 523 gaim_debug(GAIM_DEBUG_INFO, "QQ", "Update info ACK OK\n");
150 gaim_notify_info(gc, NULL, _("You information have been updated"), NULL); 524 gaim_notify_info(gc, NULL, _("Your information has been updated"), NULL);
151 } 525 }
152 } else 526 } else
153 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Error decrypt modify info reply\n"); 527 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Error decrypt modify info reply\n");
154 528
155 } // qq_process_modify_info_reply 529 }
156 530
157 /*****************************************************************************/
158 // after getting info or modify myself, refresh the buddy list accordingly 531 // after getting info or modify myself, refresh the buddy list accordingly
159 void qq_refresh_buddy_and_myself(contact_info * info, GaimConnection * gc) 532 void qq_refresh_buddy_and_myself(contact_info *info, GaimConnection *gc)
160 { 533 {
161 GaimBuddy *b; 534 GaimBuddy *b;
162 qq_data *qd; 535 qq_data *qd;
163 qq_buddy *q_bud; 536 qq_buddy *q_bud;
164 gchar *alias_utf8; 537 gchar *alias_utf8;
180 q_bud->gender = strtol(info->gender, NULL, 10); 553 q_bud->gender = strtol(info->gender, NULL, 10);
181 q_bud->icon = strtol(info->face, NULL, 10); 554 q_bud->icon = strtol(info->face, NULL, 10);
182 if (alias_utf8 != NULL) 555 if (alias_utf8 != NULL)
183 q_bud->nickname = g_strdup(alias_utf8); 556 q_bud->nickname = g_strdup(alias_utf8);
184 qq_update_buddy_contact(gc, q_bud); 557 qq_update_buddy_contact(gc, q_bud);
185 } // if q_bud 558 }
186 g_free(alias_utf8); 559 g_free(alias_utf8);
187 } // qq_refresh_buddy_and_myself 560 }
188 561
189 /*****************************************************************************/ 562 // XXX When we don't have any immediate response, we send duplicate get info packets
563 // to the server. If the server ends up responding to multiple packets, we get multiple
564 // modify info dialogues, which is annoying. Fix this.
565
190 // process reply to get_info packet 566 // process reply to get_info packet
191 void qq_process_get_info_reply(guint8 * buf, gint buf_len, GaimConnection * gc) 567 void qq_process_get_info_reply(guint8 *buf, gint buf_len, GaimConnection *gc)
192 { 568 {
193 gint len; 569 gint len;
194 guint8 *data; 570 guint8 *data;
195 gchar **segments; 571 gchar **segments;
196 qq_info_query *query; 572 qq_info_query *query;
197 qq_data *qd; 573 qq_data *qd;
198 contact_info *info; 574 contact_info *info;
199 contact_info_window *info_window;
200 gboolean show_window;
201 GList *list, *query_list; 575 GList *list, *query_list;
576 GString *info_text;
202 577
203 g_return_if_fail(gc != NULL && gc->proto_data != NULL); 578 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
204 g_return_if_fail(buf != NULL && buf_len != 0); 579 g_return_if_fail(buf != NULL && buf_len != 0);
205 580
206 qd = (qq_data *) gc->proto_data; 581 qd = (qq_data *) gc->proto_data;
215 590
216 info = (contact_info *) segments; 591 info = (contact_info *) segments;
217 qq_refresh_buddy_and_myself(info, gc); 592 qq_refresh_buddy_and_myself(info, gc);
218 593
219 query_list = qd->info_query; 594 query_list = qd->info_query;
220 show_window = FALSE; 595 // ensure we're processing the right query
221 while (query_list != NULL) { 596 while (query_list) {
222 query = (qq_info_query *) query_list->data; 597 query = (qq_info_query *) query_list->data;
223 if (query->uid == atoi(info->uid)) { 598 if (query->uid == atoi(info->uid)) {
224 show_window = query->show_window; 599 if (query->show_window) {
600 info_text = info_to_str((const gchar **) segments);
601 gaim_notify_userinfo(gc, info->uid, info_text->str, NULL, NULL);
602 g_string_free(info_text, TRUE);
603 } else if (query->modify_info) {
604 create_modify_info_dialogue(gc, (const gchar **) segments);
605 }
225 qd->info_query = g_list_remove(qd->info_query, qd->info_query->data); 606 qd->info_query = g_list_remove(qd->info_query, qd->info_query->data);
226 g_free(query); 607 g_free(query);
227 break; 608 break;
228 } 609 }
229 query_list = query_list->next; 610 query_list = query_list->next;
230 } // while query_list
231
232 if (!show_window) {
233 g_strfreev(segments);
234 return;
235 } 611 }
236 // if not show_window, we can not find the window here either 612
237 list = qd->contact_info_window;
238 while (list != NULL) {
239 info_window = (contact_info_window *) (list->data);
240 if (info_window->uid == atoi(info->uid)) {
241 if (info_window->window)
242 qq_refresh_contact_info_dialog(info, gc, info_window);
243 else
244 qq_show_contact_info_dialog(info, gc, info_window);
245 break;
246 } else
247 list = list->next;
248 } // while list
249 g_strfreev(segments); 613 g_strfreev(segments);
250 } else 614 } else
251 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Error decrypt get info reply\n"); 615 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Error decrypt get info reply\n");
252 616
253 } // qq_process_get_info_reply 617 }
254 618
255 /*****************************************************************************/
256 void qq_info_query_free(qq_data * qd) 619 void qq_info_query_free(qq_data * qd)
257 { 620 {
258 gint i; 621 gint i;
259 qq_info_query *p; 622 qq_info_query *p;
260 623
266 qd->info_query = g_list_remove(qd->info_query, p); 629 qd->info_query = g_list_remove(qd->info_query, p);
267 g_free(p); 630 g_free(p);
268 i++; 631 i++;
269 } 632 }
270 gaim_debug(GAIM_DEBUG_INFO, "QQ", "%d info queries are freed!\n", i); 633 gaim_debug(GAIM_DEBUG_INFO, "QQ", "%d info queries are freed!\n", i);
271 } // qq_add_buddy_request_free 634 }
272
273 /*****************************************************************************/
274 // END OF FILE