libpurple/protocols/qq/sendqueue.c

branch
release-2.4.3
changeset 23192
2f00b04db5cb
parent 21279
40685e1f50ca
equal deleted inserted replaced
23191:4e69ad828497 23192:2f00b04db5cb
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 }

mercurial