Mercurial > pidgin.yaz
comparison libpurple/protocols/qq/qq_trans.c @ 23683:1c50f12b1c52
2008.08.02 - csyfek <csyfek(at)gmail.com>
* Commit to Pidgin
* Tickets:
Fixes #1861
Fixes #1902
References #5112
2008.08.02 - ccpaging <ecc_hy(at)hotmail.com>
* Store all keys and md5 values of qq_data in char[QQ_KEY_LENGTH]
* Use random value in inikey
* TEA header padding in crypt.c
* Rewrite login part of qq_process
2008.07.31 - ccpaging <ecc_hy(at)hotmail.com>
* Fixed: send reply when get duplicate server command. The server may not get our reply before.
* Tag custom picture as text "(Broken)"
2008.07.30 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com>
* Change some debug message
* Modify buddy status flag according to eva for QQ2006
* Modify buddy status parse and correspond to eva2
* Add getIP/putIP functions to packet_parse.c, and replace some gen_ip_str
* Replace guint32 *ip with struct in_addr, and reduce g_new/g_free operation
* Source file changed:
Merge buddy_status into buddy_list
Change login_logout to qq_base
Merge keep_alive into qq_base
New qq_process extract from qq_network
* Fixed: Byte alignment bug in crypt.c, tested in ARM PDA
* Fixed: group chat message may get in before getting group info, and so group info is empty
* Add qq_send_cmd_group_get_group_info when joined a group chat in group_im.c
* Add some new group command identify according eva but further program
* Add some new QQ client version identify
* Fixed: Identify buddy's client version by IM packet, and not by status
* Add some new info in buddy's tooltip text
* Add video falg to buddy's emblem. But those flag in buddy status may not prasing correctly
* Use new timeout function to handle send keep_alive, resend packet, update buddy status
* Add new advanced options:
The end user may change interval of keep_alive, resend packet, update buddy status to feed their
need.
For example, saving network flow when use mobile phone.
Keep alive packet must be sent in 60-120 seconds whatever client rcved data of not.
The intervals of keep alive and update status should be multiple of resend's interval,
Since we use counter not time() in a single timeout function for efficiency.
* Rewrite qq_trans.c, and use one g_list to manage:
Store server packet before login, and prase all of them when get login
Store client send packet for resend scanning, confirm server reply, filter duplicate server reply
Store server packet for filter out duplicate
* Add QQ_MSG_SYS_NOTICE = 0x06 in sys_msg.c
* Rewrite qq_proc_cmd_reply and qq_proc_cmd_server:
In QQ protocol, one packet reply may need a new packet send later.
We may call it packet trigger. The triggers always is hided in every qq_process_reply.
Now we try to extract those triggers and put into a single function,
and then every trigger should be obviously and easy to manage.
author | SHiNE CsyFeK <csyfek@gmail.com> |
---|---|
date | Sat, 02 Aug 2008 15:00:46 +0000 |
parents | bdb38a8bf721 |
children | 5f454b975a99 |
comparison
equal
deleted
inserted
replaced
23682:21e591b55339 | 23683:1c50f12b1c52 |
---|---|
30 #include "prefs.h" | 30 #include "prefs.h" |
31 #include "request.h" | 31 #include "request.h" |
32 | 32 |
33 #include "header_info.h" | 33 #include "header_info.h" |
34 #include "qq_network.h" | 34 #include "qq_network.h" |
35 #include "qq_process.h" | |
35 #include "qq_trans.h" | 36 #include "qq_trans.h" |
36 | 37 |
37 #define QQ_RESEND_MAX 8 /* max resend per packet */ | 38 #define QQ_RESEND_MAX 3 /* max resend per packet */ |
38 | 39 |
39 typedef struct _transaction { | 40 qq_transaction *qq_trans_find_rcved(qq_data *qd, guint16 cmd, guint16 seq) |
40 guint16 seq; | 41 { |
41 guint16 cmd; | 42 GList *curr; |
42 guint8 *buf; | 43 GList *next; |
43 gint buf_len; | 44 qq_transaction *trans; |
44 | 45 |
45 gint fd; | 46 if (qd->transactions == NULL) { |
46 gint retries; | 47 return NULL; |
47 time_t create_time; | 48 } |
48 } transaction; | 49 |
49 | 50 next = qd->transactions; |
50 void qq_send_trans_append(qq_data *qd, guint8 *buf, gint buf_len, guint16 cmd, guint16 seq) | 51 while( (curr = next) ) { |
51 { | 52 next = curr->next; |
52 transaction *trans = g_new0(transaction, 1); | 53 |
54 trans = (qq_transaction *) (curr->data); | |
55 if(trans->cmd == cmd && trans->seq == seq) { | |
56 if (trans->rcved_times == 0) { | |
57 trans->scan_times = 0; | |
58 } | |
59 trans->rcved_times++; | |
60 if (qq_trans_is_server(trans) && qq_trans_is_dup(trans)) { | |
61 /* server may not get our confirm reply before, send reply again*/ | |
62 if (trans->data != NULL && trans->data_len > 0) { | |
63 qq_send_data(qd, trans->cmd, trans->seq, FALSE, trans->data, trans->data_len); | |
64 } | |
65 } | |
66 return trans; | |
67 } | |
68 } | |
69 | |
70 return NULL; | |
71 } | |
72 | |
73 gboolean qq_trans_is_server(qq_transaction *trans) | |
74 { | |
75 g_return_val_if_fail(trans != NULL, FALSE); | |
76 | |
77 if (trans->flag & QQ_TRANS_IS_SERVER) | |
78 return TRUE; | |
79 else | |
80 return FALSE; | |
81 } | |
82 | |
83 gboolean qq_trans_is_dup(qq_transaction *trans) | |
84 { | |
85 g_return_val_if_fail(trans != NULL, TRUE); | |
86 | |
87 if (trans->rcved_times > 1) | |
88 return TRUE; | |
89 else | |
90 return FALSE; | |
91 } | |
92 | |
93 /* Remove a packet with seq from send trans */ | |
94 static void trans_remove(qq_data *qd, qq_transaction *trans) | |
95 { | |
96 g_return_if_fail(qd != NULL && trans != NULL); | |
97 | |
98 purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", | |
99 "Remove [%s%05d] retry %d rcved %d scan %d %s\n", | |
100 (trans->flag & QQ_TRANS_IS_SERVER) ? "SRV-" : "", | |
101 trans->seq, | |
102 trans->send_retries, trans->rcved_times, trans->scan_times, | |
103 qq_get_cmd_desc(trans->cmd)); | |
104 | |
105 if (trans->data) g_free(trans->data); | |
106 qd->transactions = g_list_remove(qd->transactions, trans); | |
107 g_free(trans); | |
108 } | |
109 | |
110 void qq_trans_add_client_cmd(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len) | |
111 { | |
112 qq_transaction *trans = g_new0(qq_transaction, 1); | |
53 | 113 |
54 g_return_if_fail(trans != NULL); | 114 g_return_if_fail(trans != NULL); |
55 | 115 |
116 trans->flag = 0; | |
117 if (cmd == QQ_CMD_TOKEN || cmd == QQ_CMD_LOGIN || cmd == QQ_CMD_KEEP_ALIVE) { | |
118 trans->flag |= QQ_TRANS_CLI_IMPORT; | |
119 } | |
56 trans->fd = qd->fd; | 120 trans->fd = qd->fd; |
57 trans->cmd = cmd; | 121 trans->cmd = cmd; |
58 trans->seq = seq; | 122 trans->seq = seq; |
59 trans->retries = QQ_RESEND_MAX; | 123 trans->send_retries = QQ_RESEND_MAX; |
60 trans->create_time = time(NULL); | 124 trans->rcved_times = 0; |
61 trans->buf = g_memdup(buf, buf_len); /* don't use g_strdup, may have 0x00 */ | 125 trans->scan_times = 0; |
62 trans->buf_len = buf_len; | 126 trans->data = NULL; |
63 | 127 trans->data_len = 0; |
64 purple_debug(PURPLE_DEBUG_ERROR, "QQ", | 128 if (data != NULL && data_len > 0) { |
65 "Add to transaction, seq = %d, buf = %p, len = %d\n", | 129 trans->data = g_memdup(data, data_len); /* don't use g_strdup, may have 0x00 */ |
66 trans->seq, trans->buf, trans->buf_len); | 130 trans->data_len = data_len; |
67 qd->send_trans = g_list_append(qd->send_trans, trans); | 131 } |
68 } | 132 purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS", |
69 | 133 "Add client cmd, seq = %d, data = %p, len = %d\n", |
70 /* Remove a packet with seq from send trans */ | 134 trans->seq, trans->data, trans->data_len); |
71 void qq_send_trans_remove(qq_data *qd, gpointer data) | 135 qd->transactions = g_list_append(qd->transactions, trans); |
72 { | 136 } |
73 transaction *trans = (transaction *)data; | 137 |
74 | 138 void qq_trans_add_server_cmd(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len) |
75 g_return_if_fail(qd != NULL && data != NULL); | 139 { |
76 | 140 qq_transaction *trans = g_new0(qq_transaction, 1); |
77 purple_debug(PURPLE_DEBUG_INFO, "QQ", | 141 |
78 "ack [%05d] %s, remove from send tranactions\n", | 142 g_return_if_fail(trans != NULL); |
143 | |
144 trans->flag = QQ_TRANS_IS_SERVER; | |
145 if ( !qd->logged_in ) { | |
146 trans->flag |= QQ_TRANS_BEFORE_LOGIN; | |
147 } | |
148 trans->fd = qd->fd; | |
149 trans->cmd = cmd; | |
150 trans->seq = seq; | |
151 trans->send_retries = 0; | |
152 trans->rcved_times = 1; | |
153 trans->scan_times = 0; | |
154 trans->data = NULL; | |
155 trans->data_len = 0; | |
156 if (data != NULL && data_len > 0) { | |
157 trans->data = g_memdup(data, data_len); /* don't use g_strdup, may have 0x00 */ | |
158 trans->data_len = data_len; | |
159 } | |
160 purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS", | |
161 "Add server cmd, seq = %d, data = %p, len = %d\n", | |
162 trans->seq, trans->data, trans->data_len); | |
163 qd->transactions = g_list_append(qd->transactions, trans); | |
164 } | |
165 | |
166 void qq_trans_process_before_login(qq_data *qd) | |
167 { | |
168 GList *curr; | |
169 GList *next; | |
170 qq_transaction *trans; | |
171 | |
172 g_return_if_fail(qd != NULL); | |
173 | |
174 next = qd->transactions; | |
175 while( (curr = next) ) { | |
176 next = curr->next; | |
177 trans = (qq_transaction *) (curr->data); | |
178 /* purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS", "Scan [%d]\n", trans->seq); */ | |
179 | |
180 if ( !(trans->flag & QQ_TRANS_IS_SERVER) ) { | |
181 continue; | |
182 } | |
183 if ( !(trans->flag & QQ_TRANS_BEFORE_LOGIN) ) { | |
184 continue; | |
185 } | |
186 // set QQ_TRANS_BEFORE_LOGIN off | |
187 trans->flag &= ~QQ_TRANS_BEFORE_LOGIN; | |
188 | |
189 purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS", | |
190 "Process server cmd before login, seq %d, data %p, len %d, send_retries %d\n", | |
191 trans->seq, trans->data, trans->data_len, trans->send_retries); | |
192 | |
193 qq_proc_cmd_reply(qd->gc, trans->seq, trans->cmd, trans->data, trans->data_len); | |
194 } | |
195 | |
196 /* purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Scan finished\n"); */ | |
197 return; | |
198 } | |
199 | |
200 gboolean qq_trans_scan(qq_data *qd) | |
201 { | |
202 GList *curr; | |
203 GList *next; | |
204 qq_transaction *trans; | |
205 | |
206 g_return_val_if_fail(qd != NULL, FALSE); | |
207 | |
208 next = qd->transactions; | |
209 while( (curr = next) ) { | |
210 next = curr->next; | |
211 trans = (qq_transaction *) (curr->data); | |
212 /* purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Scan [%d]\n", trans->seq); */ | |
213 | |
214 if (trans->flag & QQ_TRANS_BEFORE_LOGIN) { | |
215 /* keep server cmd before login*/ | |
216 continue; | |
217 } | |
218 | |
219 trans->scan_times++; | |
220 if (trans->scan_times <= 1) { | |
221 /* skip in 10 seconds */ | |
222 continue; | |
223 } | |
224 | |
225 if (trans->rcved_times > 0) { | |
226 /* Has been received */ | |
227 trans_remove(qd, trans); | |
228 continue; | |
229 } | |
230 | |
231 if (trans->flag & QQ_TRANS_IS_SERVER) { | |
232 continue; | |
233 } | |
234 | |
235 /* Never get reply */ | |
236 trans->send_retries--; | |
237 if (trans->send_retries <= 0) { | |
238 purple_debug(PURPLE_DEBUG_WARNING, "QQ_TRANS", | |
239 "[%d] %s is lost.\n", | |
79 trans->seq, qq_get_cmd_desc(trans->cmd)); | 240 trans->seq, qq_get_cmd_desc(trans->cmd)); |
80 | 241 if (trans->flag & QQ_TRANS_CLI_IMPORT) { |
81 if (trans->buf) g_free(trans->buf); | 242 return TRUE; |
82 qd->send_trans = g_list_remove(qd->send_trans, trans); | 243 } |
83 g_free(trans); | 244 |
84 } | 245 purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS", |
85 | 246 "Lost [%d] %s, data %p, len %d, retries %d\n", |
86 gpointer qq_send_trans_find(qq_data *qd, guint16 cmd, guint16 seq) | 247 trans->seq, qq_get_cmd_desc(trans->cmd), |
87 { | 248 trans->data, trans->data_len, trans->send_retries); |
88 GList *curr; | 249 trans_remove(qd, trans); |
89 GList *next; | 250 continue; |
90 transaction *trans; | 251 } |
91 | 252 |
92 curr = qd->send_trans; | 253 purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS", |
254 "Resend [%d] %s data %p, len %d, send_retries %d\n", | |
255 trans->seq, qq_get_cmd_desc(trans->cmd), | |
256 trans->data, trans->data_len, trans->send_retries); | |
257 qq_send_data(qd, trans->cmd, trans->seq, FALSE, trans->data, trans->data_len); | |
258 } | |
259 | |
260 /* purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Scan finished\n"); */ | |
261 return FALSE; | |
262 } | |
263 | |
264 /* clean up send trans and free all contents */ | |
265 void qq_trans_remove_all(qq_data *qd) | |
266 { | |
267 GList *curr; | |
268 GList *next; | |
269 qq_transaction *trans; | |
270 gint count = 0; | |
271 | |
272 curr = qd->transactions; | |
93 while(curr) { | 273 while(curr) { |
94 next = curr->next; | 274 next = curr->next; |
95 trans = (transaction *) (curr->data); | 275 |
96 if(trans->cmd == cmd && trans->seq == seq) { | 276 trans = (qq_transaction *) (curr->data); |
97 return trans; | |
98 } | |
99 curr = next; | |
100 } | |
101 | |
102 return NULL; | |
103 } | |
104 | |
105 /* clean up send trans and free all contents */ | |
106 void qq_send_trans_remove_all(qq_data *qd) | |
107 { | |
108 GList *curr; | |
109 GList *next; | |
110 transaction *trans; | |
111 gint count = 0; | |
112 | |
113 curr = qd->send_trans; | |
114 while(curr) { | |
115 next = curr->next; | |
116 | |
117 trans = (transaction *) (curr->data); | |
118 /* | 277 /* |
119 purple_debug(PURPLE_DEBUG_ERROR, "QQ", | 278 purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS", |
120 "Remove to transaction, seq = %d, buf = %p, len = %d\n", | 279 "Remove to transaction, seq = %d, buf = %p, len = %d\n", |
121 trans->seq, trans->buf, trans->len); | 280 trans->seq, trans->buf, trans->len); |
122 */ | 281 */ |
123 qq_send_trans_remove(qd, trans); | 282 trans_remove(qd, trans); |
124 | 283 |
125 count++; | 284 count++; |
126 curr = next; | 285 curr = next; |
127 } | 286 } |
128 g_list_free(qd->send_trans); | 287 g_list_free(qd->transactions); |
129 | 288 |
130 purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in send tranactions are freed!\n", count); | 289 purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Free all %d packets\n", count); |
131 } | 290 } |
132 | |
133 gint qq_send_trans_scan(qq_data *qd, gint *start, | |
134 guint8 *buf, gint maxlen, guint16 *cmd, gint *retries) | |
135 { | |
136 GList *curr; | |
137 GList *next = NULL; | |
138 transaction *trans; | |
139 gint copylen; | |
140 | |
141 g_return_val_if_fail(qd != NULL && *start >= 0 && maxlen > 0, -1); | |
142 | |
143 /* purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Scan from %d\n", *start); */ | |
144 curr = g_list_nth(qd->send_trans, *start); | |
145 while(curr) { | |
146 next = curr->next; | |
147 *start = g_list_position(qd->send_trans, next); | |
148 | |
149 trans = (transaction *) (curr->data); | |
150 if (trans->buf == NULL || trans->buf_len <= 0) { | |
151 qq_send_trans_remove(qd, trans); | |
152 curr = next; | |
153 continue; | |
154 } | |
155 | |
156 if (trans->retries < 0) { | |
157 purple_debug(PURPLE_DEBUG_ERROR, "QQ", | |
158 "Remove transaction, seq %d, buf %p, len %d, retries %d, next %d\n", | |
159 trans->seq, trans->buf, trans->buf_len, trans->retries, *start); | |
160 qq_send_trans_remove(qd, trans); | |
161 curr = next; | |
162 continue; | |
163 } | |
164 | |
165 purple_debug(PURPLE_DEBUG_ERROR, "QQ", | |
166 "Resend transaction, seq %d, buf %p, len %d, retries %d, next %d\n", | |
167 trans->seq, trans->buf, trans->buf_len, trans->retries, *start); | |
168 copylen = MIN(trans->buf_len, maxlen); | |
169 g_memmove(buf, trans->buf, copylen); | |
170 | |
171 *cmd = trans->cmd; | |
172 *retries = trans->retries; | |
173 trans->retries--; | |
174 return copylen; | |
175 } | |
176 | |
177 /* purple_debug(PURPLE_DEBUG_INFO, "QQ", "Scan finished\n"); */ | |
178 return -1; | |
179 } | |
180 | |
181 void qq_rcv_trans_push(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len) | |
182 { | |
183 transaction *trans = g_new0(transaction, 1); | |
184 | |
185 g_return_if_fail(data != NULL && data_len > 0); | |
186 g_return_if_fail(trans != NULL); | |
187 | |
188 trans->cmd = cmd; | |
189 trans->seq = seq; | |
190 trans->buf = g_memdup(data, data_len); | |
191 trans->buf_len = data_len; | |
192 trans->create_time = time(NULL); | |
193 | |
194 if (qd->rcv_trans == NULL) | |
195 qd->rcv_trans = g_queue_new(); | |
196 | |
197 g_queue_push_head(qd->rcv_trans, trans); | |
198 } | |
199 | |
200 gint qq_rcv_trans_pop(qq_data *qd, guint16 *cmd, guint16 *seq, guint8 *data, gint max_len) | |
201 { | |
202 transaction *trans = NULL; | |
203 gint copy_len; | |
204 | |
205 g_return_val_if_fail(data != NULL && max_len > 0, -1); | |
206 | |
207 if (g_queue_is_empty(qd->rcv_trans)) { | |
208 return -1; | |
209 } | |
210 trans = (transaction *) g_queue_pop_head(qd->rcv_trans); | |
211 if (trans == NULL) { | |
212 return 0; | |
213 } | |
214 if (trans->buf == NULL || trans->buf_len <= 0) { | |
215 return 0; | |
216 } | |
217 | |
218 copy_len = MIN(max_len, trans->buf_len); | |
219 g_memmove(data, trans->buf, copy_len); | |
220 *cmd = trans->cmd; | |
221 *seq = trans->seq; | |
222 | |
223 g_free(trans->buf); | |
224 g_free(trans); | |
225 return copy_len; | |
226 } | |
227 | |
228 /* clean up the packets before login */ | |
229 void qq_rcv_trans_remove_all(qq_data *qd) | |
230 { | |
231 transaction *trans = NULL; | |
232 gint count = 0; | |
233 | |
234 g_return_if_fail(qd != NULL); | |
235 | |
236 /* now clean up my own data structures */ | |
237 if (qd->rcv_trans != NULL) { | |
238 while (NULL != (trans = g_queue_pop_tail(qd->rcv_trans))) { | |
239 g_free(trans->buf); | |
240 g_free(trans); | |
241 count++; | |
242 } | |
243 g_queue_free(qd->rcv_trans); | |
244 } | |
245 purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in receive tranactions are freed!\n", count); | |
246 } |