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 }