| 29 #include "notify.h" |
29 #include "notify.h" |
| 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_proxy.h" |
34 #include "qq_network.h" |
| 35 #include "sendqueue.h" |
35 #include "sendqueue.h" |
| 36 |
36 |
| 37 #define QQ_RESEND_MAX 8 /* max resend per packet */ |
37 #define QQ_RESEND_MAX 8 /* max resend per packet */ |
| 38 |
38 |
| 39 typedef struct _gc_and_packet gc_and_packet; |
39 typedef struct _transaction { |
| 40 |
40 guint16 seq; |
| 41 struct _gc_and_packet { |
41 guint16 cmd; |
| 42 PurpleConnection *gc; |
42 guint8 *buf; |
| 43 qq_sendpacket *packet; |
43 gint buf_len; |
| 44 }; |
44 |
| 45 |
45 gint fd; |
| 46 /* Remove a packet with send_seq from sendqueue */ |
46 gint retries; |
| 47 void qq_sendqueue_remove(qq_data *qd, guint16 send_seq) |
47 time_t create_time; |
| 48 { |
48 } transaction; |
| 49 GList *list; |
49 |
| 50 qq_sendpacket *p; |
50 void qq_trans_append(qq_data *qd, guint8 *buf, gint buf_len, guint16 cmd, guint16 seq) |
| 51 |
51 { |
| 52 list = qd->sendqueue; |
52 transaction *trans = g_new0(transaction, 1); |
| 53 while (list != NULL) { |
53 |
| 54 p = (qq_sendpacket *) (list->data); |
54 g_return_if_fail(trans != NULL); |
| 55 if (p->send_seq == send_seq) { |
55 |
| 56 qd->sendqueue = g_list_remove(qd->sendqueue, p); |
56 trans->fd = qd->fd; |
| 57 g_free(p->buf); |
57 trans->cmd = cmd; |
| 58 g_free(p); |
58 trans->seq = seq; |
| 59 break; |
59 trans->retries = QQ_RESEND_MAX; |
| 60 } |
60 trans->create_time = time(NULL); |
| 61 list = list->next; |
61 trans->buf = g_memdup(buf, buf_len); /* don't use g_strdup, may have 0x00 */ |
| 62 } |
62 trans->buf_len = buf_len; |
| 63 } |
63 |
| |
64 purple_debug(PURPLE_DEBUG_ERROR, "QQ", |
| |
65 "Add to transaction, seq = %d, buf = %lu, len = %d\n", |
| |
66 trans->seq, trans->buf, trans->buf_len); |
| |
67 qd->transactions = g_list_append(qd->transactions, trans); |
| |
68 } |
| |
69 |
| |
70 /* Remove a packet with seq from sendqueue */ |
| |
71 void qq_trans_remove(qq_data *qd, gpointer data) |
| |
72 { |
| |
73 transaction *trans = (transaction *)data; |
| |
74 |
| |
75 g_return_if_fail(qd != NULL && data != NULL); |
| |
76 |
| |
77 purple_debug(PURPLE_DEBUG_INFO, "QQ", |
| |
78 "ack [%05d] %s, remove from sendqueue\n", |
| |
79 trans->seq, qq_get_cmd_desc(trans->cmd)); |
| |
80 |
| |
81 if (trans->buf) g_free(trans->buf); |
| |
82 qd->transactions = g_list_remove(qd->transactions, trans); |
| |
83 g_free(trans); |
| |
84 } |
| |
85 |
| |
86 gpointer qq_trans_find(qq_data *qd, guint16 seq) |
| |
87 { |
| |
88 GList *curr; |
| |
89 GList *next; |
| |
90 transaction *trans; |
| |
91 |
| |
92 curr = qd->transactions; |
| |
93 while(curr) { |
| |
94 next = curr->next; |
| |
95 trans = (transaction *) (curr->data); |
| |
96 if(trans->seq == seq) { |
| |
97 return trans; |
| |
98 } |
| |
99 curr = next; |
| |
100 } |
| |
101 |
| |
102 return NULL; |
| |
103 } |
| |
104 |
| |
105 /* clean up sendqueue and free all contents */ |
| |
106 void qq_trans_remove_all(qq_data *qd) |
| |
107 { |
| |
108 GList *curr; |
| |
109 GList *next; |
| |
110 transaction *trans; |
| |
111 gint count = 0; |
| |
112 |
| |
113 curr = qd->transactions; |
| |
114 while(curr) { |
| |
115 next = curr->next; |
| 64 |
116 |
| 65 /* clean up sendqueue and free all contents */ |
117 trans = (transaction *) (curr->data); |
| 66 void qq_sendqueue_free(qq_data *qd) |
118 /* |
| 67 { |
119 purple_debug(PURPLE_DEBUG_ERROR, "QQ", |
| 68 qq_sendpacket *p; |
120 "Remove to transaction, seq = %d, buf = %lu, len = %d\n", |
| 69 gint i; |
121 trans->seq, trans->buf, trans->len); |
| 70 |
122 */ |
| 71 i = 0; |
123 qq_trans_remove(qd, trans); |
| 72 while (qd->sendqueue != NULL) { |
124 |
| 73 p = (qq_sendpacket *) (qd->sendqueue->data); |
125 count++; |
| 74 qd->sendqueue = g_list_remove(qd->sendqueue, p); |
126 curr = next; |
| 75 g_free(p->buf); |
127 } |
| 76 g_free(p); |
128 g_list_free(qd->transactions); |
| 77 i++; |
129 |
| 78 } |
130 purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in sendqueue are freed!\n", count); |
| 79 purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in sendqueue are freed!\n", i); |
131 } |
| 80 } |
132 |
| 81 |
133 gint qq_trans_scan(qq_data *qd, gint *start, |
| 82 /* FIXME We shouldn't be dropping packets, but for now we have to because |
134 guint8 *buf, gint maxlen, guint16 *cmd, gint *retries) |
| 83 * somewhere we're generating invalid packets that the server won't ack. |
135 { |
| 84 * Given enough time, a buildup of those packets would crash the client. */ |
136 GList *curr; |
| 85 gboolean qq_sendqueue_timeout_callback(gpointer data) |
137 GList *next = NULL; |
| 86 { |
138 transaction *trans; |
| 87 PurpleConnection *gc; |
139 gint copylen; |
| 88 qq_data *qd; |
140 |
| 89 GList *list; |
141 g_return_val_if_fail(qd != NULL && *start >= 0 && maxlen > 0, -1); |
| 90 qq_sendpacket *p; |
142 |
| 91 time_t now; |
143 //purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Scan from %d\n", *start); |
| 92 gint wait_time; |
144 curr = g_list_nth(qd->transactions, *start); |
| 93 |
145 while(curr) { |
| 94 gc = (PurpleConnection *) data; |
146 next = curr->next; |
| 95 qd = (qq_data *) gc->proto_data; |
147 *start = g_list_position(qd->transactions, next); |
| 96 now = time(NULL); |
148 |
| 97 list = qd->sendqueue; |
149 trans = (transaction *) (curr->data); |
| 98 |
150 if (trans->buf == NULL || trans->buf_len <= 0) { |
| 99 /* empty queue, return TRUE so that timeout continues functioning */ |
151 qq_trans_remove(qd, trans); |
| 100 if (qd->sendqueue == NULL) |
152 curr = next; |
| 101 return TRUE; |
153 continue; |
| 102 |
154 } |
| 103 while (list != NULL) { /* remove all packet whose resend_times == -1 */ |
155 |
| 104 p = (qq_sendpacket *) list->data; |
156 if (trans->retries < 0) { |
| 105 if (p->resend_times == -1) { /* to remove */ |
157 purple_debug(PURPLE_DEBUG_ERROR, "QQ", |
| 106 qd->sendqueue = g_list_remove(qd->sendqueue, p); |
158 "Remove transaction, seq %d, buf %lu, len %d, retries %d, next %d\n", |
| 107 g_free(p->buf); |
159 trans->seq, trans->buf, trans->buf_len, trans->retries, *start); |
| 108 g_free(p); |
160 qq_trans_remove(qd, trans); |
| 109 list = qd->sendqueue; |
161 curr = next; |
| 110 } else { |
162 continue; |
| 111 list = list->next; |
163 } |
| 112 } |
164 |
| 113 } |
165 purple_debug(PURPLE_DEBUG_ERROR, "QQ", |
| 114 |
166 "Resend transaction, seq %d, buf %lu, len %d, retries %d, next %d\n", |
| 115 list = qd->sendqueue; |
167 trans->seq, trans->buf, trans->buf_len, trans->retries, *start); |
| 116 while (list != NULL) { |
168 copylen = MIN(trans->buf_len, maxlen); |
| 117 p = (qq_sendpacket *) list->data; |
169 g_memmove(buf, trans->buf, copylen); |
| 118 if (p->resend_times == QQ_RESEND_MAX) { /* reach max */ |
170 |
| 119 switch (p->cmd) { |
171 *cmd = trans->cmd; |
| 120 case QQ_CMD_KEEP_ALIVE: |
172 *retries = trans->retries; |
| 121 if (qd->logged_in) { |
173 trans->retries--; |
| 122 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection lost!\n"); |
174 return copylen; |
| 123 purple_connection_error_reason(gc, |
175 } |
| 124 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost")); |
176 |
| 125 qd->logged_in = FALSE; |
177 // purple_debug(PURPLE_DEBUG_INFO, "QQ", "Scan finished\n"); |
| 126 } |
178 return -1; |
| 127 p->resend_times = -1; |
179 } |
| 128 break; |
180 |
| 129 case QQ_CMD_LOGIN: |
181 void qq_packet_push(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len) |
| 130 case QQ_CMD_REQUEST_LOGIN_TOKEN: |
182 { |
| 131 if (!qd->logged_in) /* cancel login progress */ |
183 transaction *trans = g_new0(transaction, 1); |
| 132 purple_connection_error_reason(gc, |
184 |
| 133 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Login failed, no reply")); |
185 g_return_if_fail(data != NULL && data_len > 0); |
| 134 p->resend_times = -1; |
186 g_return_if_fail(trans != NULL); |
| 135 break; |
187 |
| 136 default:{ |
188 trans->cmd = cmd; |
| 137 purple_debug(PURPLE_DEBUG_WARNING, "QQ", |
189 trans->seq = seq; |
| 138 "%s packet sent %d times but not acked. Not resending it.\n", |
190 trans->buf = g_memdup(data, data_len); |
| 139 qq_get_cmd_desc(p->cmd), QQ_RESEND_MAX); |
191 trans->buf_len = data_len; |
| 140 } |
192 trans->create_time = time(NULL); |
| 141 p->resend_times = -1; |
193 |
| 142 } |
194 if (qd->rcv_trans == NULL) |
| 143 } else { /* resend_times < QQ_RESEND_MAX, so sent it again */ |
195 qd->rcv_trans = g_queue_new(); |
| 144 wait_time = (gint) (QQ_SENDQUEUE_TIMEOUT / 1000); |
196 |
| 145 if (difftime(now, p->sendtime) > (wait_time * (p->resend_times + 1))) { |
197 g_queue_push_head(qd->rcv_trans, trans); |
| 146 qq_proxy_write(qd, p->buf, p->len); |
198 } |
| 147 p->resend_times++; |
199 |
| 148 purple_debug(PURPLE_DEBUG_INFO, |
200 gint qq_packet_pop(qq_data *qd, guint16 *cmd, guint16 *seq, guint8 *data, gint max_len) |
| 149 "QQ", "<<< [%05d] send again for %d times!\n", |
201 { |
| 150 p->send_seq, p->resend_times); |
202 transaction *trans = NULL; |
| 151 } |
203 gint copy_len; |
| 152 } |
204 |
| 153 list = list->next; |
205 g_return_val_if_fail(data != NULL && max_len > 0, -1); |
| 154 } |
206 |
| 155 return TRUE; /* if we return FALSE, the timeout callback stops functioning */ |
207 if (g_queue_is_empty(qd->rcv_trans)) { |
| 156 } |
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_packet_remove_all(qq_data *qd) |
| |
230 { |
| |
231 transaction *trans = NULL; |
| |
232 |
| |
233 g_return_if_fail(qd != NULL); |
| |
234 |
| |
235 /* now clean up my own data structures */ |
| |
236 if (qd->rcv_trans != NULL) { |
| |
237 while (NULL != (trans = g_queue_pop_tail(qd->rcv_trans))) { |
| |
238 g_free(trans->buf); |
| |
239 g_free(trans); |
| |
240 } |
| |
241 g_queue_free(qd->rcv_trans); |
| |
242 } |
| |
243 } |