13870
|
1 /**
|
|
2 * The QQ2003C protocol plugin
|
|
3 *
|
|
4 * for gaim
|
|
5 *
|
|
6 * Copyright (C) 2004 Puzzlebird
|
|
7 *
|
|
8 * This program is free software; you can redistribute it and/or modify
|
|
9 * it under the terms of the GNU General Public License as published by
|
|
10 * the Free Software Foundation; either version 2 of the License, or
|
|
11 * (at your option) any later version.
|
|
12 *
|
|
13 * This program is distributed in the hope that it will be useful,
|
|
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
16 * GNU General Public License for more details.
|
|
17 *
|
|
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
|
|
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
21 */
|
|
22
|
|
23 #include "internal.h" // strlen, _("get_text)
|
|
24 #include "debug.h" // gaim_debug
|
|
25 #include "notify.h" // gaim_notify
|
13989
|
26 #include "request.h" // gaim_request_fields_new
|
13870
|
27
|
|
28 #include "utils.h" // uid_to_gaim_name
|
|
29 #include "packet_parse.h" // MAX_PACKET_SIZE
|
|
30 #include "buddy_info.h" //
|
|
31 #include "char_conv.h" // qq_to_utf8
|
|
32 #include "crypt.h" // qq_crypt
|
|
33 #include "header_info.h" // cmd alias
|
|
34 #include "keep_alive.h" // qq_update_buddy_contact
|
|
35 #include "send_core.h" // qq_send_cmd
|
|
36
|
13989
|
37 // Below is all of the information necessary to reconstruct the various
|
|
38 // information fields that one can set in the official client. When we need
|
|
39 // to know about a specific field (e.g., should "city" be a choice
|
|
40 // or text field?), we can simply look it up from the template. Note that
|
|
41 // there are a number of unidentified fields.
|
|
42
|
|
43 typedef struct _info_field {
|
|
44 gchar *title;
|
|
45 gchar *id; // used by gaim_request fields
|
|
46 gint pos;
|
|
47 gchar *group;
|
|
48 gint group_pos; // for display order in the UI
|
|
49 gint choice; // indicates which character array contains the choices
|
|
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[] = {
|
13993
|
110 "-", N_("A"), N_("B"), N_("O"), N_("AB"), N_("Other"), NULL
|
13989
|
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
|
13993
|
141 static const gint choice_sizes[] = { 0, 13, 13, 6, 2, 7, 34, 15 };
|
13989
|
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 };
|
13870
|
150
|
13989
|
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
|
14014
|
183 static gint info_field_compare(gconstpointer a, gconstpointer b)
|
13989
|
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]);
|
13999
|
208 group = g_list_insert_sorted(group, entry, info_field_compare);
|
13989
|
209 }
|
|
210 cur++;
|
|
211 }
|
|
212
|
|
213 return group;
|
|
214 }
|
13870
|
215
|
13989
|
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);
|
13993
|
224 // the server sends us an ascii index and none of the arrays has more than 99
|
13989
|
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
|
13870
|
283 void qq_send_packet_get_info(GaimConnection * gc, guint32 uid, gboolean show_window)
|
|
284 {
|
|
285 qq_data *qd;
|
|
286 gchar *uid_str;
|
|
287 qq_info_query *query;
|
|
288
|
|
289 g_return_if_fail(gc != NULL && gc->proto_data != NULL && uid != 0);
|
|
290
|
|
291 qd = (qq_data *) gc->proto_data;
|
|
292 uid_str = g_strdup_printf("%d", uid);
|
13989
|
293 qq_send_cmd(gc, QQ_CMD_GET_USER_INFO, TRUE, 0, TRUE, (guint8 *) uid_str, strlen(uid_str));
|
13870
|
294
|
|
295 query = g_new0(qq_info_query, 1);
|
|
296 query->uid = uid;
|
|
297 query->show_window = show_window;
|
13989
|
298 query->modify_info = FALSE;
|
13870
|
299 qd->info_query = g_list_append(qd->info_query, query);
|
|
300
|
|
301 g_free(uid_str);
|
13989
|
302 }
|
|
303
|
|
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;
|
13870
|
311
|
13989
|
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
|
14014
|
321 // send packet to modify personal information
|
|
322 void qq_send_packet_modify_info(GaimConnection *gc, contact_info *info)
|
13870
|
323 {
|
14014
|
324 gchar *info_field[QQ_CONTACT_FIELDS];
|
13870
|
325 gint i;
|
|
326 guint8 *raw_data, *cursor, bar;
|
|
327
|
|
328 g_return_if_fail(gc != NULL && info != NULL);
|
|
329
|
|
330 bar = 0x1f;
|
|
331 raw_data = g_newa(guint8, MAX_PACKET_SIZE - 128);
|
|
332 cursor = raw_data;
|
|
333
|
|
334 g_memmove(info_field, info, sizeof(gchar *) * QQ_CONTACT_FIELDS);
|
|
335
|
14014
|
336 create_packet_b(raw_data, &cursor, bar);
|
13870
|
337
|
|
338 // important!, skip the first uid entry
|
|
339 for (i = 1; i < QQ_CONTACT_FIELDS; i++) {
|
|
340 create_packet_b(raw_data, &cursor, bar);
|
13989
|
341 create_packet_data(raw_data, &cursor, (guint8 *) info_field[i], strlen(info_field[i]));
|
13870
|
342 }
|
|
343 create_packet_b(raw_data, &cursor, bar);
|
|
344
|
|
345 qq_send_cmd(gc, QQ_CMD_UPDATE_INFO, TRUE, 0, TRUE, raw_data, cursor - raw_data);
|
|
346
|
13989
|
347 }
|
|
348
|
|
349 static void modify_info_cancel_cb(modify_info_data *mid)
|
|
350 {
|
13993
|
351 qq_data *qd;
|
|
352
|
|
353 qd = (qq_data *) mid->gc->proto_data;
|
|
354 qd->modifying_info = FALSE;
|
|
355
|
13989
|
356 g_list_free(mid->misc);
|
|
357 g_free(mid);
|
|
358 }
|
|
359
|
|
360 // runs through all of the fields in the modify info UI and put
|
|
361 // their values into the outgoing packet
|
|
362 static void parse_field(gpointer field, gpointer outgoing_info)
|
|
363 {
|
|
364 GaimRequestField *f;
|
|
365 gchar **segments, *value;
|
|
366 const info_field *ft;
|
|
367 const gchar *id;
|
|
368
|
|
369 f = (GaimRequestField *) field;
|
|
370 segments = (gchar **) outgoing_info;
|
|
371 id = gaim_request_field_get_id(f);
|
|
372 ft = info_field_get_template(id);
|
|
373 if (ft->choice && !ft->customizable)
|
|
374 value = g_strdup_printf("%d", gaim_request_field_choice_get_value(f));
|
|
375 else {
|
|
376 value = (gchar *) gaim_request_field_string_get_value(f);
|
13993
|
377 if (value == NULL) value = g_strdup("-");
|
13989
|
378 else value = utf8_to_qq(value, QQ_CHARSET_DEFAULT);
|
|
379 }
|
|
380 segments[ft->pos] = value;
|
|
381 }
|
|
382
|
|
383 // dumps the uneditable information straight into the outgoing packet
|
|
384 static void parse_misc_field(gpointer field, gpointer outgoing_info)
|
|
385 {
|
|
386 info_field *f;
|
|
387 gchar **segments;
|
|
388
|
|
389 f = (info_field *) field;
|
|
390 segments = (gchar **) outgoing_info;
|
|
391 segments[f->pos] = g_strdup(f->value);
|
|
392 info_field_free(f);
|
|
393 }
|
|
394
|
|
395 // runs through all of the information fields and copies them into an
|
|
396 // outgoing packet, then sends that packet
|
|
397 static void modify_info_ok_cb(modify_info_data *mid, GaimRequestFields *fields)
|
|
398 {
|
|
399 GaimConnection *gc;
|
13993
|
400 qq_data *qd;
|
13989
|
401 GList *list, *groups, *group_node;
|
|
402 gchar *info_field[QQ_CONTACT_FIELDS];
|
|
403 contact_info *info;
|
|
404 gint i;
|
|
405
|
|
406 gc = mid->gc;
|
13993
|
407 qd = (qq_data *) gc->proto_data;
|
|
408 qd->modifying_info = FALSE;
|
13989
|
409 list = mid->misc;
|
|
410 g_list_foreach(list, parse_misc_field, info_field);
|
|
411 g_list_free(list);
|
|
412 groups = gaim_request_fields_get_groups(fields);
|
|
413 while(groups) {
|
|
414 group_node = groups;
|
|
415 list = gaim_request_field_group_get_fields(group_node->data);
|
|
416 g_list_foreach(list, parse_field, info_field);
|
|
417 groups = g_list_remove_link(groups, group_node);
|
|
418 }
|
|
419 info = (contact_info *) info_field;
|
|
420
|
14014
|
421 qq_send_packet_modify_info(gc, info);
|
13989
|
422 g_free(mid);
|
|
423 for (i = 0; i < QQ_CONTACT_FIELDS; i++)
|
|
424 g_free(info_field[i]);
|
|
425 }
|
13870
|
426
|
13989
|
427 // Sets up the display for one group of information. This includes
|
|
428 // managing which fields in the UI should be textfields and
|
|
429 // which choices, and also mapping ints to choice values when appropriate.
|
|
430 static void setup_group(gpointer field, gpointer group)
|
|
431 {
|
|
432 info_field *f;
|
|
433 GaimRequestFieldGroup *g;
|
|
434 GaimRequestField *rf;
|
|
435 gint choice, index, j;
|
|
436 gboolean customizable, valid_index, multiline;
|
|
437 gchar *id, *value;
|
|
438
|
|
439 f = (info_field *) field;
|
|
440 g = (GaimRequestFieldGroup *) group;
|
|
441 choice = f->choice;
|
|
442 customizable = f->customizable;
|
|
443 id = f->id;
|
|
444 valid_index = TRUE;
|
|
445
|
|
446 if (!choice || customizable) {
|
|
447 valid_index = is_valid_index(f->value, choice);
|
|
448 multiline = id == "intro";
|
|
449 if (valid_index) {
|
|
450 index = atoi(f->value);
|
|
451 value = (gchar *) choices[choice][index];
|
|
452 } else value = qq_to_utf8(f->value, QQ_CHARSET_DEFAULT);
|
|
453 rf = gaim_request_field_string_new(id, f->title, value, multiline);
|
|
454 } else {
|
|
455 index = atoi(f->value);
|
|
456 value = (gchar *) choices[choice][index];
|
|
457 rf = gaim_request_field_choice_new(id, f->title, index);
|
|
458 j = 0;
|
|
459 while(choices[choice][j] != NULL)
|
|
460 gaim_request_field_choice_add(rf, choices[choice][j++]);
|
|
461 }
|
|
462 gaim_request_field_group_add_field(g, rf);
|
|
463 if (!valid_index) g_free(value);
|
|
464 info_field_free(f);
|
|
465 }
|
|
466
|
|
467 // Takes the info returned by a get_info packet for the user and sets up
|
|
468 // a form using those values and the info_template.
|
|
469 static void create_modify_info_dialogue(GaimConnection *gc, const gchar **info)
|
|
470 {
|
13993
|
471 qq_data *qd;
|
13989
|
472 GaimRequestFields *fields;
|
|
473 GaimRequestFieldGroup *group;
|
|
474 GaimRequestField *field;
|
|
475 GList *group_list;
|
|
476 modify_info_data *mid;
|
|
477 gint i;
|
|
478
|
13993
|
479 // so we only have one dialog open at a time
|
|
480 qd = (qq_data *) gc->proto_data;
|
|
481 if (!qd->modifying_info) {
|
|
482 qd->modifying_info = TRUE;
|
13989
|
483
|
13993
|
484 fields = gaim_request_fields_new();
|
|
485
|
|
486 // we only care about the first 3 groups, not the miscellaneous stuff
|
|
487 for (i = 0; i < 3; i++) {
|
|
488 group = gaim_request_field_group_new(info_group_headers[i]);
|
|
489 gaim_request_fields_add_group(fields, group);
|
|
490 group_list = info_get_group(info, info_group_headers[i]);
|
|
491 g_list_foreach(group_list, setup_group, group);
|
|
492 g_list_free(group_list);
|
|
493 }
|
13989
|
494
|
13993
|
495 //set this manually here instead of generating a new template column
|
|
496 field = gaim_request_fields_get_field(fields, "uid");
|
|
497 gaim_request_field_string_set_editable(field, FALSE);
|
|
498
|
|
499 //we need to pass the info that doesn't get modified as aux data
|
|
500 //because we'll still need it when we send the modify_info packet
|
|
501 mid = g_new0(modify_info_data, 1);
|
|
502 mid->gc = gc;
|
|
503 mid->misc = info_get_group(info, info_group_headers[3]);
|
13989
|
504
|
13993
|
505 gaim_request_fields(gc, _("Modify my information"),
|
13989
|
506 _("Modify my information"), NULL, fields,
|
|
507 _("Update my information"), G_CALLBACK(modify_info_ok_cb),
|
|
508 _("Cancel"), G_CALLBACK(modify_info_cancel_cb),
|
|
509 mid);
|
13993
|
510 }
|
13989
|
511 }
|
|
512
|
|
513 // process the reply of modify_info packet
|
|
514 void qq_process_modify_info_reply(guint8 *buf, gint buf_len, GaimConnection *gc)
|
13870
|
515 {
|
|
516 qq_data *qd;
|
|
517 gint len;
|
|
518 guint8 *data;
|
|
519
|
|
520 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
|
|
521 g_return_if_fail(buf != NULL && buf_len != 0);
|
|
522
|
|
523 qd = (qq_data *) gc->proto_data;
|
|
524 len = buf_len;
|
|
525 data = g_newa(guint8, len);
|
|
526
|
|
527 if (qq_crypt(DECRYPT, buf, buf_len, qd->session_key, data, &len)) {
|
13993
|
528 data[len] = '\0';
|
13989
|
529 if (qd->uid == atoi((gchar *) data)) { // return should be my uid
|
13870
|
530 gaim_debug(GAIM_DEBUG_INFO, "QQ", "Update info ACK OK\n");
|
13989
|
531 gaim_notify_info(gc, NULL, _("Your information has been updated"), NULL);
|
13870
|
532 }
|
|
533 } else
|
|
534 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Error decrypt modify info reply\n");
|
|
535
|
13989
|
536 }
|
13870
|
537
|
|
538 // after getting info or modify myself, refresh the buddy list accordingly
|
13989
|
539 void qq_refresh_buddy_and_myself(contact_info *info, GaimConnection *gc)
|
13870
|
540 {
|
|
541 GaimBuddy *b;
|
|
542 qq_data *qd;
|
|
543 qq_buddy *q_bud;
|
|
544 gchar *alias_utf8;
|
|
545
|
|
546 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
|
|
547 qd = (qq_data *) gc->proto_data;
|
|
548
|
|
549 alias_utf8 = qq_to_utf8(info->nick, QQ_CHARSET_DEFAULT);
|
|
550 if (qd->uid == strtol(info->uid, NULL, 10)) { // it is me
|
|
551 qd->my_icon = strtol(info->face, NULL, 10);
|
|
552 if (alias_utf8 != NULL)
|
|
553 gaim_account_set_alias(gc->account, alias_utf8);
|
|
554 }
|
|
555 // update buddy list (including myself, if myself is the buddy)
|
|
556 b = gaim_find_buddy(gc->account, uid_to_gaim_name(strtol(info->uid, NULL, 10)));
|
|
557 q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
|
|
558 if (q_bud != NULL) { // I have this buddy
|
|
559 q_bud->age = strtol(info->age, NULL, 10);
|
|
560 q_bud->gender = strtol(info->gender, NULL, 10);
|
|
561 q_bud->icon = strtol(info->face, NULL, 10);
|
|
562 if (alias_utf8 != NULL)
|
|
563 q_bud->nickname = g_strdup(alias_utf8);
|
|
564 qq_update_buddy_contact(gc, q_bud);
|
13989
|
565 }
|
13870
|
566 g_free(alias_utf8);
|
13989
|
567 }
|
13870
|
568
|
|
569 // process reply to get_info packet
|
13989
|
570 void qq_process_get_info_reply(guint8 *buf, gint buf_len, GaimConnection *gc)
|
13870
|
571 {
|
|
572 gint len;
|
|
573 guint8 *data;
|
|
574 gchar **segments;
|
|
575 qq_info_query *query;
|
|
576 qq_data *qd;
|
|
577 contact_info *info;
|
|
578 GList *list, *query_list;
|
13989
|
579 GString *info_text;
|
13870
|
580
|
|
581 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
|
|
582 g_return_if_fail(buf != NULL && buf_len != 0);
|
|
583
|
|
584 qd = (qq_data *) gc->proto_data;
|
|
585 list = query_list = NULL;
|
|
586 len = buf_len;
|
|
587 data = g_newa(guint8, len);
|
|
588 info = NULL;
|
|
589
|
|
590 if (qq_crypt(DECRYPT, buf, buf_len, qd->session_key, data, &len)) {
|
|
591 if (NULL == (segments = split_data(data, len, "\x1e", QQ_CONTACT_FIELDS)))
|
|
592 return;
|
|
593
|
|
594 info = (contact_info *) segments;
|
|
595 qq_refresh_buddy_and_myself(info, gc);
|
|
596
|
|
597 query_list = qd->info_query;
|
13989
|
598 // ensure we're processing the right query
|
|
599 while (query_list) {
|
13870
|
600 query = (qq_info_query *) query_list->data;
|
|
601 if (query->uid == atoi(info->uid)) {
|
13989
|
602 if (query->show_window) {
|
|
603 info_text = info_to_str((const gchar **) segments);
|
|
604 gaim_notify_userinfo(gc, info->uid, info_text->str, NULL, NULL);
|
|
605 g_string_free(info_text, TRUE);
|
|
606 } else if (query->modify_info) {
|
|
607 create_modify_info_dialogue(gc, (const gchar **) segments);
|
|
608 }
|
13870
|
609 qd->info_query = g_list_remove(qd->info_query, qd->info_query->data);
|
|
610 g_free(query);
|
|
611 break;
|
|
612 }
|
|
613 query_list = query_list->next;
|
13989
|
614 }
|
13870
|
615
|
|
616 g_strfreev(segments);
|
|
617 } else
|
|
618 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Error decrypt get info reply\n");
|
|
619
|
13989
|
620 }
|
13870
|
621
|
|
622 void qq_info_query_free(qq_data * qd)
|
|
623 {
|
|
624 gint i;
|
|
625 qq_info_query *p;
|
|
626
|
|
627 g_return_if_fail(qd != NULL);
|
|
628
|
|
629 i = 0;
|
|
630 while (qd->info_query != NULL) {
|
|
631 p = (qq_info_query *) (qd->info_query->data);
|
|
632 qd->info_query = g_list_remove(qd->info_query, p);
|
|
633 g_free(p);
|
|
634 i++;
|
|
635 }
|
|
636 gaim_debug(GAIM_DEBUG_INFO, "QQ", "%d info queries are freed!\n", i);
|
13989
|
637 }
|