Mercurial > pidgin
comparison libpurple/protocols/qq/sendqueue.c @ 23050:51dbe83ebbd3
patch-04-tcp-pending
| author | SHiNE CsyFeK <csyfek@gmail.com> |
|---|---|
| date | Tue, 24 Jun 2008 12:22:40 +0000 |
| parents | ba41f2a60253 |
| children |
comparison
equal
deleted
inserted
replaced
| 23049:190bc4ecf6c3 | 23050:51dbe83ebbd3 |
|---|---|
| 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 } |
