Mercurial > pidgin.yaz
diff libpurple/protocols/qq/qq_network.c @ 24019:147ada94a1d8
2008.08.16 - ccpaging <ecc_hy(at)hotmail.com>
* Rename group to room. If you used pidginqq before, this may create a new room with same title, you may
delete old one
* Replace purple_debug with purple_debug_info, purple_debug_warning, purple_debug_error
* Add server notice and server new, and two options to turn on/off
* Minor modify for reducing transaction's debug infor
* Minor modifies for system notice and QQ news.
* Add 4 new strings need translate compare with p10.
author | SHiNE CsyFeK <csyfek@gmail.com> |
---|---|
date | Thu, 11 Sep 2008 13:25:07 +0000 |
parents | 91169093449d |
children | 2f5a7edd8f68 25f62d21b3f8 |
line wrap: on
line diff
--- a/libpurple/protocols/qq/qq_network.c Thu Sep 11 04:19:37 2008 +0000 +++ b/libpurple/protocols/qq/qq_network.c Thu Sep 11 13:25:07 2008 +0000 @@ -26,11 +26,6 @@ #include "debug.h" #include "internal.h" -#ifdef _WIN32 -#define random rand -#define srandom srand -#endif - #include "buddy_info.h" #include "group_info.h" #include "group_free.h" @@ -44,63 +39,100 @@ #include "utils.h" #include "qq_process.h" -/* set QQ_RECONNECT_MAX to 1, when test reconnecting */ -#define QQ_RECONNECT_MAX 4 -#define QQ_RECONNECT_INTERVAL 5000 -#define QQ_KEEP_ALIVE_INTERVAL 60000 -#define QQ_TRANS_INTERVAL 10000 +#define QQ_DEFAULT_PORT 8000 + +/* set QQ_CONNECT_MAX to 1, when test reconnecting */ +#define QQ_CONNECT_MAX 3 +#define QQ_CONNECT_INTERVAL 2 +#define QQ_CONNECT_CHECK 5 +#define QQ_KEEP_ALIVE_INTERVAL 60 +#define QQ_TRANS_INTERVAL 10 + +gboolean connect_to_server(PurpleConnection *gc, gchar *server, gint port); + +static qq_connection *connection_find(qq_data *qd, int fd) { + qq_connection *ret = NULL; + GSList *entry = qd->openconns; + while(entry) { + ret = entry->data; + if(ret->fd == fd) return ret; + entry = entry->next; + } + return NULL; +} +static qq_connection *connection_create(qq_data *qd, int fd) { + qq_connection *ret = g_new0(qq_connection, 1); + ret->fd = fd; + qd->openconns = g_slist_append(qd->openconns, ret); + return ret; +} + +static void connection_remove(qq_data *qd, int fd) { + qq_connection *conn = connection_find(qd, fd); + qd->openconns = g_slist_remove(qd->openconns, conn); + + g_return_if_fail( conn != NULL ); + + purple_debug_info("QQ", "Close socket %d\n", conn->fd); + if(conn->input_handler > 0) purple_input_remove(conn->input_handler); + if(conn->can_write_handler > 0) purple_input_remove(conn->can_write_handler); + + if (conn->fd >= 0) close(conn->fd); + if(conn->tcp_txbuf != NULL) purple_circ_buffer_destroy(conn->tcp_txbuf); + if (conn->tcp_rxqueue != NULL) g_free(conn->tcp_rxqueue); + + g_free(conn); +} + +static void connection_free_all(qq_data *qd) { + qq_connection *ret = NULL; + GSList *entry = qd->openconns; + while(entry) { + ret = entry->data; + connection_remove(qd, ret->fd); + entry = qd->openconns; + } +} static gboolean set_new_server(qq_data *qd) { gint count; gint index; GList *it = NULL; - + g_return_val_if_fail(qd != NULL, FALSE); if (qd->servers == NULL) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server list is NULL\n"); + purple_debug_info("QQ", "Server list is NULL\n"); return FALSE; } - if (qd->real_hostname) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n"); - g_free(qd->real_hostname); - qd->real_hostname = NULL; - } + /* remove server used before */ + if (qd->curr_server != NULL) { + purple_debug_info("QQ", + "Remove current [%s] from server list\n", qd->curr_server); + qd->servers = g_list_remove(qd->servers, qd->curr_server); + qd->curr_server = NULL; + } - /* remove server used before */ - if (qd->server_name != NULL) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "Remove previous server [%s]\n", qd->server_name); - qd->servers = g_list_remove(qd->servers, qd->server_name); - qd->server_name = NULL; - } - count = g_list_length(qd->servers); - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server list has %d\n", count); + purple_debug_info("QQ", "Server list has %d\n", count); if (count <= 0) { /* no server left, disconnect when result is false */ qd->servers = NULL; return FALSE; } - + /* get new server */ - index = random() % count; + index = rand() % count; it = g_list_nth(qd->servers, index); - qd->server_name = it->data; /* do not free server_name */ - if (qd->server_name == NULL || strlen(qd->server_name) <= 0 ) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server name at %d is empty\n", index); + qd->curr_server = it->data; /* do not free server_name */ + if (qd->curr_server == NULL || strlen(qd->curr_server) <= 0 ) { + purple_debug_info("QQ", "Server name at %d is empty\n", index); return FALSE; } - qd->real_hostname = g_strdup(qd->server_name); - qd->real_port = qd->user_port; - - qd->reconnect_times = QQ_RECONNECT_MAX; - - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "set new server to %s:%d\n", qd->real_hostname, qd->real_port); + purple_debug_info("QQ", "set new server to %s\n", qd->curr_server); return TRUE; } @@ -115,152 +147,208 @@ return bytes; } -static gboolean reconnect_later_cb(gpointer data) +static gboolean connect_check(gpointer data) { - PurpleConnection *gc; + PurpleConnection *gc = (PurpleConnection *) data; qq_data *qd; - gc = (PurpleConnection *) data; + g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE); + qd = (qq_data *) gc->proto_data; + + if (qd->connect_watcher > 0) { + purple_timeout_remove(qd->connect_watcher); + qd->connect_watcher = 0; + } + + if (qd->fd >= 0 && qd->token != NULL && qd->token_len >= 0) { + purple_debug_info("QQ", "Connect ok\n"); + return FALSE; + } + + qd->connect_watcher = purple_timeout_add_seconds(0, qq_connect_later, gc); + return FALSE; +} + +/* Warning: qq_connect_later destory all connection + * Any function should be care of use qq_data after call this function + * Please conside tcp_pending and udp_pending */ +gboolean qq_connect_later(gpointer data) +{ + PurpleConnection *gc = (PurpleConnection *) data; + qq_data *qd; + char *server; + int port; + gchar **segments; + g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE); qd = (qq_data *) gc->proto_data; - qd->reconnect_timeout = 0; + if (qd->check_watcher > 0) { + purple_timeout_remove(qd->check_watcher); + qd->check_watcher = 0; + } + qq_disconnect(gc); + + if (qd->redirect_ip.s_addr != 0) { + /* redirect to new server */ + server = g_strdup_printf("%s:%d", inet_ntoa(qd->redirect_ip), qd->redirect_port); + qd->servers = g_list_append(qd->servers, server); + qd->curr_server = server; + + qd->redirect_ip.s_addr = 0; + qd->redirect_port = 0; + qd->connect_retry = QQ_CONNECT_MAX; + } - qq_connect(gc->account); + if (qd->curr_server == NULL || strlen (qd->curr_server) == 0 || qd->connect_retry <= 0) { + if ( set_new_server(qd) != TRUE) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Failed to connect all servers")); + return FALSE; + } + qd->connect_retry = QQ_CONNECT_MAX; + } + + segments = g_strsplit_set(qd->curr_server, ":", 0); + server = g_strdup(segments[0]); + port = atoi(segments[1]); + if (port <= 0) { + purple_debug_info("QQ", "Port not define in %s\n", qd->curr_server); + port = QQ_DEFAULT_PORT; + } + g_strfreev(segments); + + qd->connect_retry--; + if ( !connect_to_server(gc, server, port) ) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Unable to connect.")); + } + + qd->check_watcher = purple_timeout_add_seconds(QQ_CONNECT_CHECK, connect_check, gc); return FALSE; /* timeout callback stops */ } -static void reconnect_later(PurpleConnection *gc) -{ - qq_data *qd; - - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - qd->reconnect_times--; - if (qd->reconnect_times < 0) { - if ( set_new_server(qd) != TRUE) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Failed to connect server")); - return; - } - } - - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "Reconnect to server %s:%d next retries %d in %d ms\n", - qd->real_hostname, qd->real_port, - qd->reconnect_times, QQ_RECONNECT_INTERVAL); - - qd->reconnect_timeout = purple_timeout_add(QQ_RECONNECT_INTERVAL, - reconnect_later_cb, gc); -} - /* process the incoming packet from qq_pending */ -static void packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len) +static gboolean packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len) { qq_data *qd; gint bytes, bytes_not_read; - gboolean prev_login_status; - + gboolean prev_update_status; + guint8 header_tag; guint16 source_tag; guint16 cmd; guint16 seq; /* May be ack_seq or send_seq, depends on cmd */ - guint8 room_cmd; guint32 room_id; + gint update_class; + guint32 ship32; qq_transaction *trans; - g_return_if_fail(buf != NULL && buf_len > 0); + g_return_val_if_fail(buf != NULL && buf_len > 0, TRUE); qd = (qq_data *) gc->proto_data; - prev_login_status = qd->logged_in; - /* Len, header and tail tag have been checked before */ bytes = 0; bytes += packet_get_header(&header_tag, &source_tag, &cmd, &seq, buf + bytes); #if 1 - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "==> [%05d] 0x%04X %s, from (0x%04X %s) len %d\n", - seq, cmd, qq_get_cmd_desc(cmd), source_tag, qq_get_ver_desc(source_tag), buf_len); -#endif + purple_debug_info("QQ", "==> [%05d] 0x%04X %s, source tag 0x%04X len %d\n", + seq, cmd, qq_get_cmd_desc(cmd), source_tag, buf_len); +#endif + /* this is the length of all the encrypted data (also remove tail tag) */ bytes_not_read = buf_len - bytes - 1; /* ack packet, we need to update send tranactions */ /* we do not check duplication for server ack */ - trans = qq_trans_find_rcved(qd, cmd, seq); + trans = qq_trans_find_rcved(gc, cmd, seq); if (trans == NULL) { /* new server command */ - qq_trans_add_server_cmd(qd, cmd, seq, buf + bytes, bytes_not_read); - if ( qd->logged_in ) { + qq_trans_add_server_cmd(gc, cmd, seq, buf + bytes, bytes_not_read); + if ( qd->is_finish_update ) { qq_proc_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read); } - return; + return TRUE; } if (qq_trans_is_dup(trans)) { - purple_debug(PURPLE_DEBUG_WARNING, - "QQ", "dup [%05d] %s, discard...\n", seq, qq_get_cmd_desc(cmd)); - return; + purple_debug_info("QQ", "dup [%05d] %s, discard...\n", seq, qq_get_cmd_desc(cmd)); + return TRUE; } if (qq_trans_is_server(trans)) { - if ( qd->logged_in ) { + if ( qd->is_finish_update ) { qq_proc_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read); } - return; + return TRUE; } - /* this is the length of all the encrypted data (also remove tail tag */ - if (cmd == QQ_CMD_ROOM) { - room_cmd = qq_trans_get_room_cmd(trans); - room_id = qq_trans_get_room_id(trans); + update_class = qq_trans_get_class(trans); + ship32 = qq_trans_get_ship(trans); + + prev_update_status = qd->is_finish_update; + switch (cmd) { + case QQ_CMD_TOKEN: + if (qq_process_token_reply(gc, buf + bytes, bytes_not_read) == QQ_TOKEN_REPLY_OK) { + qq_send_packet_login(gc); + } + break; + case QQ_CMD_LOGIN: + qq_proc_cmd_login(gc, buf + bytes, bytes_not_read); + /* check is redirect or not, and do it now */ + if (qd->redirect_ip.s_addr != 0) { + if (qd->check_watcher > 0) { + purple_timeout_remove(qd->check_watcher); + qd->check_watcher = 0; + } + if (qd->connect_watcher > 0) purple_timeout_remove(qd->connect_watcher); + qd->connect_watcher = purple_timeout_add_seconds(QQ_CONNECT_INTERVAL, qq_connect_later, gc); + return FALSE; /* do nothing after this function and return now */ + } + break; + case QQ_CMD_ROOM: + room_cmd = qq_trans_get_room_cmd(trans); + room_id = qq_trans_get_room_id(trans); #if 1 - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "%s (0x%02X ) for room %d, len %d\n", - qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len); -#endif - qq_proc_room_cmd_reply(gc, seq, room_cmd, room_id, buf + bytes, bytes_not_read); - } else { - qq_proc_cmd_reply(gc, cmd, seq, buf + bytes, bytes_not_read); - } - - /* check is redirect or not, and do it now */ - if (qd->is_redirect) { - /* free resource except real_hostname and port */ - qq_disconnect(gc); - qd->reconnect_times = QQ_RECONNECT_MAX; - reconnect_later(gc); - return; + purple_debug_info("QQ", "%s (0x%02X) for room %d, len %d\n", + qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len); +#endif + qq_proc_room_cmd_reply(gc, seq, room_cmd, room_id, buf + bytes, bytes_not_read, update_class, ship32); + break; + default: + qq_proc_cmd_reply(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32); + break; } - if (prev_login_status != qd->logged_in && qd->logged_in == TRUE) { - /* logged_in, but we have packets before login */ - qq_trans_process_before_login(qd); + if (prev_update_status != qd->is_finish_update && qd->is_finish_update == TRUE) { + /* is_login, but we have packets before login */ + qq_trans_process_before_login(gc); + return TRUE; } + return TRUE; } static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond) { - PurpleConnection *gc; + PurpleConnection *gc = (PurpleConnection *) data; qq_data *qd; + qq_connection *conn; guint8 buf[1024]; /* set to 16 when test tcp_rxqueue */ gint buf_len; gint bytes; - + guint8 *pkt; guint16 pkt_len; - + gchar *error_msg; guint8 *jump; gint jump_len; - gc = (PurpleConnection *) data; g_return_if_fail(gc != NULL && gc->proto_data != NULL); + qd = (qq_data *) gc->proto_data; if(cond != PURPLE_INPUT_READ) { purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, @@ -268,8 +356,9 @@ return; } - qd = (qq_data *) gc->proto_data; - + conn = connection_find(qd, source); + g_return_if_fail(conn != NULL); + /* test code, not using tcp_rxqueue memset(pkt,0, sizeof(pkt)); buf_len = read(qd->fd, pkt, sizeof(pkt)); @@ -278,8 +367,8 @@ } return; */ - - buf_len = read(qd->fd, buf, sizeof(buf)); + + buf_len = read(source, buf, sizeof(buf)); if (buf_len < 0) { if (errno == EAGAIN) /* No worries */ @@ -299,93 +388,93 @@ * QQ need a keep alive packet in every 60 seconds gc->last_received = time(NULL); */ - /* - purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", - "Read %d bytes from socket, rxlen is %d\n", buf_len, qd->tcp_rxlen); - */ - qd->tcp_rxqueue = g_realloc(qd->tcp_rxqueue, buf_len + qd->tcp_rxlen); - memcpy(qd->tcp_rxqueue + qd->tcp_rxlen, buf, buf_len); - qd->tcp_rxlen += buf_len; - + /* purple_debug_info("TCP_PENDING", "Read %d bytes, rxlen is %d\n", buf_len, conn->tcp_rxlen); */ + conn->tcp_rxqueue = g_realloc(conn->tcp_rxqueue, buf_len + conn->tcp_rxlen); + memcpy(conn->tcp_rxqueue + conn->tcp_rxlen, buf, buf_len); + conn->tcp_rxlen += buf_len; + pkt = g_newa(guint8, MAX_PACKET_SIZE); - while (1) { - if (qd->tcp_rxlen < QQ_TCP_HEADER_LENGTH) { + while (PURPLE_CONNECTION_IS_VALID(gc)) { + if (qd->openconns == NULL) { break; } - - bytes = 0; - bytes += qq_get16(&pkt_len, qd->tcp_rxqueue + bytes); - if (qd->tcp_rxlen < pkt_len) { + if (conn->tcp_rxqueue == NULL) { + conn->tcp_rxlen = 0; + break; + } + if (conn->tcp_rxlen < QQ_TCP_HEADER_LENGTH) { break; } - /* - purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", - "Packet len is %d bytes, rxlen is %d\n", pkt_len, qd->tcp_rxlen); - */ - if ( pkt_len < QQ_TCP_HEADER_LENGTH - || *(qd->tcp_rxqueue + bytes) != QQ_PACKET_TAG - || *(qd->tcp_rxqueue + pkt_len - 1) != QQ_PACKET_TAIL) { - /* HEY! This isn't even a QQ. What are you trying to pull? */ + bytes = 0; + bytes += qq_get16(&pkt_len, conn->tcp_rxqueue + bytes); + if (conn->tcp_rxlen < pkt_len) { + break; + } - purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", - "Packet error, failed to check header and tail tag\n"); + /* purple_debug_info("TCP_PENDING", "Packet len=%d, rxlen=%d\n", pkt_len, conn->tcp_rxlen); */ + if ( pkt_len < QQ_TCP_HEADER_LENGTH + || *(conn->tcp_rxqueue + bytes) != QQ_PACKET_TAG + || *(conn->tcp_rxqueue + pkt_len - 1) != QQ_PACKET_TAIL) { + /* HEY! This isn't even a QQ. What are you trying to pull? */ + purple_debug_warning("TCP_PENDING", "Packet error, no header or tail tag\n"); - jump = memchr(qd->tcp_rxqueue + 1, QQ_PACKET_TAIL, qd->tcp_rxlen - 1); + jump = memchr(conn->tcp_rxqueue + 1, QQ_PACKET_TAIL, conn->tcp_rxlen - 1); if ( !jump ) { - purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", - "Failed to find next QQ_PACKET_TAIL, clear receive buffer\n"); - g_free(qd->tcp_rxqueue); - qd->tcp_rxqueue = NULL; - qd->tcp_rxlen = 0; + purple_debug_warning("TCP_PENDING", "Failed to find next tail, clear receive buffer\n"); + g_free(conn->tcp_rxqueue); + conn->tcp_rxqueue = NULL; + conn->tcp_rxlen = 0; return; } /* jump and over QQ_PACKET_TAIL */ - jump_len = (jump - qd->tcp_rxqueue) + 1; - purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", - "Find next QQ_PACKET_TAIL at %d, jump %d bytes\n", jump_len, jump_len + 1); - g_memmove(qd->tcp_rxqueue, jump, qd->tcp_rxlen - jump_len); - qd->tcp_rxlen -= jump_len; + jump_len = (jump - conn->tcp_rxqueue) + 1; + purple_debug_warning("TCP_PENDING", "Find next tail at %d, jump %d\n", jump_len, jump_len + 1); + g_memmove(conn->tcp_rxqueue, jump, conn->tcp_rxlen - jump_len); + conn->tcp_rxlen -= jump_len; continue; } memset(pkt, 0, MAX_PACKET_SIZE); - g_memmove(pkt, qd->tcp_rxqueue + bytes, pkt_len - bytes); - + g_memmove(pkt, conn->tcp_rxqueue + bytes, pkt_len - bytes); + /* jump to next packet */ - qd->tcp_rxlen -= pkt_len; - if (qd->tcp_rxlen) { - /* - purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", "shrink tcp_rxqueue to %d\n", qd->tcp_rxlen); - */ - jump = g_memdup(qd->tcp_rxqueue + pkt_len, qd->tcp_rxlen); - g_free(qd->tcp_rxqueue); - qd->tcp_rxqueue = jump; + conn->tcp_rxlen -= pkt_len; + if (conn->tcp_rxlen) { + /* purple_debug_info("TCP_PENDING", "shrink tcp_rxqueue to %d\n", conn->tcp_rxlen); */ + jump = g_memdup(conn->tcp_rxqueue + pkt_len, conn->tcp_rxlen); + g_free(conn->tcp_rxqueue); + conn->tcp_rxqueue = jump; } else { - /* purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", "free tcp_rxqueue\n"); */ - g_free(qd->tcp_rxqueue); - qd->tcp_rxqueue = NULL; + /* purple_debug_info("TCP_PENDING", "free tcp_rxqueue\n"); */ + g_free(conn->tcp_rxqueue); + conn->tcp_rxqueue = NULL; } if (pkt == NULL) { continue; } - /* do not call packet_process before jump - * packet_process may call disconnect and destory tcp_rxqueue */ - packet_process(gc, pkt, pkt_len - bytes); + /* packet_process may call disconnect and destory data like conn + * do not call packet_process before jump, + * break if packet_process return FALSE */ + if (packet_process(gc, pkt, pkt_len - bytes) == FALSE) { + purple_debug_info("TCP_PENDING", "Connection has been destory\n"); + break; + } } } static void udp_pending(gpointer data, gint source, PurpleInputCondition cond) { - PurpleConnection *gc; + PurpleConnection *gc = (PurpleConnection *) data; qq_data *qd; guint8 *buf; gint buf_len; gc = (PurpleConnection *) data; g_return_if_fail(gc != NULL && gc->proto_data != NULL); + qd = (qq_data *) gc->proto_data; if(cond != PURPLE_INPUT_READ) { purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, @@ -393,13 +482,10 @@ return; } - qd = (qq_data *) gc->proto_data; - g_return_if_fail(qd->fd >= 0); - buf = g_newa(guint8, MAX_PACKET_SIZE); /* here we have UDP proxy suppport */ - buf_len = read(qd->fd, buf, MAX_PACKET_SIZE); + buf_len = read(source, buf, MAX_PACKET_SIZE); if (buf_len <= 0) { purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to read from socket")); @@ -419,73 +505,95 @@ return; } } - + + /* packet_process may call disconnect and destory data like conn + * do not call packet_process before jump, + * break if packet_process return FALSE */ packet_process(gc, buf, buf_len); } -static gint udp_send_out(qq_data *qd, guint8 *data, gint data_len) +static gint udp_send_out(PurpleConnection *gc, guint8 *data, gint data_len) { + qq_data *qd; gint ret; - g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1); + g_return_val_if_fail(data != NULL && data_len > 0, -1); + + g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); + qd = (qq_data *) gc->proto_data; - /* - purple_debug(PURPLE_DEBUG_INFO, "UDP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd); - */ - +#if 0 + purple_debug_info("UDP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd); +#endif + errno = 0; ret = send(qd->fd, data, data_len, 0); if (ret < 0 && errno == EAGAIN) { return ret; } - + if (ret < 0) { /* TODO: what to do here - do we really have to disconnect? */ - purple_debug(PURPLE_DEBUG_ERROR, "UDP_SEND_OUT", "Send failed: %d, %s\n", errno, g_strerror(errno)); - purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno)); + purple_debug_error("UDP_SEND_OUT", "Send failed: %d, %s\n", errno, g_strerror(errno)); + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno)); } return ret; } static void tcp_can_write(gpointer data, gint source, PurpleInputCondition cond) { - qq_data *qd = data; + PurpleConnection *gc = (PurpleConnection *) data; + qq_data *qd; + qq_connection *conn; int ret, writelen; - writelen = purple_circ_buffer_get_max_read(qd->tcp_txbuf); + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + qd = (qq_data *) gc->proto_data; + + conn = connection_find(qd, source); + g_return_if_fail(conn != NULL); + + writelen = purple_circ_buffer_get_max_read(conn->tcp_txbuf); if (writelen == 0) { - purple_input_remove(qd->tx_handler); - qd->tx_handler = 0; + purple_input_remove(conn->can_write_handler); + conn->can_write_handler = 0; return; } - ret = write(qd->fd, qd->tcp_txbuf->outptr, writelen); - purple_debug(PURPLE_DEBUG_ERROR, "TCP_CAN_WRITE", - "total %d bytes is sent %d\n", writelen, ret); + ret = write(source, conn->tcp_txbuf->outptr, writelen); + purple_debug_info("TCP_CAN_WRITE", "total %d bytes is sent %d\n", writelen, ret); if (ret < 0 && errno == EAGAIN) return; else if (ret < 0) { /* TODO: what to do here - do we really have to disconnect? */ - purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Write Error")); return; } - purple_circ_buffer_mark_read(qd->tcp_txbuf, ret); + purple_circ_buffer_mark_read(conn->tcp_txbuf, ret); } -static gint tcp_send_out(qq_data *qd, guint8 *data, gint data_len) +static gint tcp_send_out(PurpleConnection *gc, guint8 *data, gint data_len) { + qq_data *qd; + qq_connection *conn; gint ret; - g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1); + g_return_val_if_fail(data != NULL && data_len > 0, -1); + + g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); + qd = (qq_data *) gc->proto_data; - /* - purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd); - */ + conn = connection_find(qd, qd->fd); + g_return_val_if_fail(conn, -1); - if (qd->tx_handler == 0) { +#if 0 + purple_debug_info("TCP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd); +#endif + + if (conn->can_write_handler == 0) { ret = write(qd->fd, data, data_len); } else { ret = -1; @@ -493,28 +601,28 @@ } /* - purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", + purple_debug_info("TCP_SEND_OUT", "Socket %d, total %d bytes is sent %d\n", qd->fd, data_len, ret); */ if (ret < 0 && errno == EAGAIN) { /* socket is busy, send later */ - purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Socket is busy and send later\n"); + purple_debug_info("TCP_SEND_OUT", "Socket is busy and send later\n"); ret = 0; } else if (ret <= 0) { /* TODO: what to do here - do we really have to disconnect? */ - purple_debug(PURPLE_DEBUG_ERROR, "TCP_SEND_OUT", + purple_debug_error("TCP_SEND_OUT", "Send to socket %d failed: %d, %s\n", qd->fd, errno, g_strerror(errno)); - purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno)); + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno)); return ret; } if (ret < data_len) { - purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", + purple_debug_info("TCP_SEND_OUT", "Add %d bytes to buffer\n", data_len - ret); - if (qd->tx_handler == 0) { - qd->tx_handler = purple_input_add(qd->fd, PURPLE_INPUT_WRITE, tcp_can_write, qd); + if (conn->can_write_handler == 0) { + conn->can_write_handler = purple_input_add(qd->fd, PURPLE_INPUT_WRITE, tcp_can_write, gc); } - purple_circ_buffer_append(qd->tcp_txbuf, data + ret, data_len - ret); + purple_circ_buffer_append(conn->tcp_txbuf, data + ret, data_len - ret); } return ret; } @@ -528,17 +636,17 @@ g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, TRUE); qd = (qq_data *) gc->proto_data; - is_lost_conn = qq_trans_scan(qd); + is_lost_conn = qq_trans_scan(gc); if (is_lost_conn) { purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost")); return TRUE; } - if ( !qd->logged_in ) { + if ( !qd->is_login ) { return TRUE; } - + qd->itv_count.keep_alive--; if (qd->itv_count.keep_alive <= 0) { qd->itv_count.keep_alive = qd->itv_config.keep_alive; @@ -553,55 +661,30 @@ qd->itv_count.update--; if (qd->itv_count.update <= 0) { qd->itv_count.update = qd->itv_config.update; - qq_send_packet_get_buddies_online(gc, 0); - - qq_send_cmd_group_all_get_online_members(gc); + qq_update_online(gc, 0); return TRUE; } return TRUE; /* if return FALSE, timeout callback stops */ } -/* the callback function after socket is built - * we setup the qq protocol related configuration here */ -static void qq_connect_cb(gpointer data, gint source, const gchar *error_message) +static void do_request_token(PurpleConnection *gc) { qq_data *qd; - PurpleConnection *gc; gchar *conn_msg; const gchar *passwd; - PurpleAccount *account ; - - gc = (PurpleConnection *) data; - - if (!PURPLE_CONNECTION_IS_VALID(gc)) { - purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Invalid connection\n"); - close(source); - return; - } - - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - - qd = (qq_data *) gc->proto_data; - account = purple_connection_get_account(gc); - - /* Connect is now complete; clear the PurpleProxyConnectData */ - qd->connect_data = NULL; - - if (source < 0) { /* socket returns -1 */ - purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Invalid connection, source is < 0\n"); - qq_disconnect(gc); - reconnect_later(gc); - return; - } /* _qq_show_socket("Got login socket", source); */ + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + qd = (qq_data *) gc->proto_data; + /* QQ use random seq, to minimize duplicated packets */ - srandom(time(NULL)); - qd->send_seq = random() & 0x0000ffff; - qd->fd = source; - qd->logged_in = FALSE; + srand(time(NULL)); + qd->send_seq = rand() & 0xffff; + + qd->is_login = FALSE; + qd->is_finish_update = FALSE; qd->channel = 1; qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10); @@ -614,260 +697,94 @@ qq_get_md5(qd->password_twice_md5, sizeof(qd->password_twice_md5), qd->password_twice_md5, sizeof(qd->password_twice_md5)); - g_return_if_fail(qd->network_timeout == 0); - qd->itv_config.resend = purple_account_get_int(account, "resend_interval", 10); - if (qd->itv_config.resend <= 0) qd->itv_config.resend = 10; - - qd->itv_config.keep_alive = purple_account_get_int(account, "keep_alive_interval", 60); - if (qd->itv_config.keep_alive < 30) qd->itv_config.keep_alive = 30; - qd->itv_config.keep_alive /= qd->itv_config.resend; - qd->itv_count.keep_alive = qd->itv_config.keep_alive; - - qd->itv_config.update = purple_account_get_int(account, "update_interval", 300); - if (qd->itv_config.update > 0) { - if (qd->itv_config.update < qd->itv_config.keep_alive) { - qd->itv_config.update = qd->itv_config.keep_alive; - } - qd->itv_config.update /= qd->itv_config.resend; - qd->itv_count.update = qd->itv_config.update; - } else { - qd->itv_config.update = 0; - } - - qd->network_timeout = purple_timeout_add(qd->itv_config.resend *1000, network_timeout, gc); - - if (qd->use_tcp) - gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, tcp_pending, gc); - else - gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, udp_pending, gc); + g_return_if_fail(qd->network_watcher == 0); + qd->network_watcher = purple_timeout_add_seconds(qd->itv_config.resend, network_timeout, gc); /* Update the login progress status display */ - conn_msg = g_strdup_printf("Login as %d", qd->uid); - purple_connection_update_progress(gc, conn_msg, QQ_CONNECT_STEPS - 1, QQ_CONNECT_STEPS); + conn_msg = g_strdup_printf(_("Request token")); + purple_connection_update_progress(gc, conn_msg, 2, QQ_CONNECT_STEPS); g_free(conn_msg); qq_send_packet_token(gc); } -static void udp_can_write(gpointer data, gint source, PurpleInputCondition cond) +/* the callback function after socket is built + * we setup the qq protocol related configuration here */ +static void connect_cb(gpointer data, gint source, const gchar *error_message) { PurpleConnection *gc; qq_data *qd; - socklen_t len; - int error=0, ret; - - gc = (PurpleConnection *) data; - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - - qd = (qq_data *) gc->proto_data; - - - purple_debug_info("proxy", "Connected.\n"); - - /* - * getsockopt after a non-blocking connect returns -1 if something is - * really messed up (bad descriptor, usually). Otherwise, it returns 0 and - * error holds what connect would have returned if it blocked until now. - * Thus, error == 0 is success, error == EINPROGRESS means "try again", - * and anything else is a real error. - * - * (error == EINPROGRESS can happen after a select because the kernel can - * be overly optimistic sometimes. select is just a hint that you might be - * able to do something.) - */ - len = sizeof(error); - ret = getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len); - if (ret == 0 && error == EINPROGRESS) - return; /* we'll be called again later */ - - purple_input_remove(qd->tx_handler); - qd->tx_handler = 0; - if (ret < 0 || error != 0) { - if(ret != 0) - error = errno; - - close(source); - - purple_debug_error("proxy", "getsockopt SO_ERROR check: %s\n", g_strerror(error)); - - qq_connect_cb(gc, -1, _("Unable to connect")); - return; - } - - qq_connect_cb(gc, source, NULL); -} - -static void udp_host_resolved(GSList *hosts, gpointer data, const char *error_message) { - PurpleConnection *gc; - qq_data *qd; - struct sockaddr server_addr; - int addr_size; - gint fd = -1; - int flags; + PurpleAccount *account ; + qq_connection *conn; gc = (PurpleConnection *) data; g_return_if_fail(gc != NULL && gc->proto_data != NULL); qd = (qq_data *) gc->proto_data; - - /* udp_query_data must be set as NULL. - * Otherwise purple_dnsquery_destroy in qq_disconnect cause glib double free error */ - qd->udp_query_data = NULL; + account = purple_connection_get_account(gc); - if (!hosts || !hosts->data) { - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Couldn't resolve host")); + /* conn_data will be destoryed */ + qd->conn_data = NULL; + + if (!PURPLE_CONNECTION_IS_VALID(gc)) { + purple_debug_info("QQ_CONN", "Invalid connection\n"); + close(source); return; } - addr_size = GPOINTER_TO_INT(hosts->data); - hosts = g_slist_remove(hosts, hosts->data); - memcpy(&server_addr, hosts->data, addr_size); - g_free(hosts->data); - - hosts = g_slist_remove(hosts, hosts->data); - while(hosts) { - hosts = g_slist_remove(hosts, hosts->data); - g_free(hosts->data); - hosts = g_slist_remove(hosts, hosts->data); - } - - fd = socket(PF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Unable to create socket: %s\n", g_strerror(errno)); + if (source < 0) { /* socket returns -1 */ + purple_debug_info("QQ_CONN", + "Could not establish a connection with the server:\n%s\n", + error_message); + if (qd->connect_watcher > 0) purple_timeout_remove(qd->connect_watcher); + qd->connect_watcher = purple_timeout_add_seconds(QQ_CONNECT_INTERVAL, qq_connect_later, gc); return; } - /* we use non-blocking mode to speed up connection */ - flags = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, flags | O_NONBLOCK); -#ifndef _WIN32 - fcntl(fd, F_SETFD, FD_CLOEXEC); -#endif + /* _qq_show_socket("Got login socket", source); */ + qd->fd = source; + conn = connection_create(qd, source); + if (qd->use_tcp) { + conn->input_handler = purple_input_add(source, PURPLE_INPUT_READ, tcp_pending, gc); + } else { + conn->input_handler = purple_input_add(source, PURPLE_INPUT_READ, udp_pending, gc); + } - /* From Unix-socket-FAQ: http://www.faqs.org/faqs/unix-faq/socket/ - * - * If a UDP socket is unconnected, which is the normal state after a - * bind() call, then send() or write() are not allowed, since no - * destination is available; only sendto() can be used to send data. - * - * Calling connect() on the socket simply records the specified address - * and port number as being the desired communications partner. That - * means that send() or write() are now allowed; they use the destination - * address and port given on the connect call as the destination of packets. - */ - if (connect(fd, &server_addr, addr_size) >= 0) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connected.\n"); - flags = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); - qq_connect_cb(gc, fd, NULL); - return; - } - - /* [EINPROGRESS] - * The socket is marked as non-blocking and the connection cannot be - * completed immediately. It is possible to select for completion by - * selecting the socket for writing. - * [EINTR] - * A signal interrupted the call. - * The connection is established asynchronously. - */ - if ((errno == EINPROGRESS) || (errno == EINTR)) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n"); - qd->tx_handler = purple_input_add(fd, PURPLE_INPUT_WRITE, udp_can_write, gc); - return; - } - - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection failed: %s\n", g_strerror(errno)); - close(fd); + do_request_token( gc ); } -/* establish a generic QQ connection - * TCP/UDP, and direct/redirected */ -void qq_connect(PurpleAccount *account) +gboolean connect_to_server(PurpleConnection *gc, gchar *server, gint port) { - PurpleConnection *gc; + PurpleAccount *account ; qq_data *qd; gchar *conn_msg; - gc = purple_account_get_connection(account); - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - + g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE); + account = purple_connection_get_account(gc); qd = (qq_data *) gc->proto_data; - - /* test set_new_server - while (set_new_server(qd)) { - purple_debug(PURPLE_DEBUG_INFO, "QQ_TEST", - "New server %s:%d Real server %s:%d\n", - qd->server_name, qd->user_port, qd->real_hostname, qd->real_port); - } - purple_debug(PURPLE_DEBUG_INFO, "QQ_TEST", "qd->servers %lu\n", - qd->servers); - exit(1); - */ - if (qd->server_name == NULL) { - /* must be first call this function */ - if ( set_new_server(qd) != TRUE) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Failed to connect server")); - return; - } + if (server == NULL || strlen(server) == 0 || port == 0) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Invalid server or port")); + return FALSE; } - if (qd->real_hostname == NULL || qd->real_port == 0) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("hostname is NULL or port is 0")); - return; - } - - conn_msg = g_strdup_printf( _("Connecting server %s, retries %d"), - qd->real_hostname, qd->reconnect_times); + conn_msg = g_strdup_printf( _("Connecting server %s, retries %d"), server, port); purple_connection_update_progress(gc, conn_msg, 1, QQ_CONNECT_STEPS); g_free(conn_msg); - if (qd->is_redirect) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Redirect to %s:%d\n", - qd->real_hostname, qd->real_port); - } - qd->is_redirect = FALSE; - - qd->fd = -1; - qd->tx_handler = 0; - - /* QQ connection via UDP/TCP. - * Now use Purple proxy function to provide TCP proxy support, - * and qq_udp_proxy.c to add UDP proxy support (thanks henry) */ - if(qd->use_tcp) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "TCP Connect to %s:%d\n", - qd->real_hostname, qd->real_port); - - /* TODO: is there a good default grow size? */ - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Create tcp_txbuf\n"); - qd->tcp_txbuf = purple_circ_buffer_new(0); + purple_debug_info("QQ", "Connect to %s:%d\n", server, port); - qd->connect_data = purple_proxy_connect(NULL, account, - qd->real_hostname, qd->real_port, qq_connect_cb, gc); - if (qd->connect_data == NULL) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to connect.")); - } - return; + if (qd->conn_data != NULL) { + purple_proxy_connect_cancel(qd->conn_data); + qd->conn_data = NULL; } - - purple_debug(PURPLE_DEBUG_INFO, "QQ", "UDP Connect to %s:%d\n", - qd->real_hostname, qd->real_port); - - g_return_if_fail(qd->udp_query_data == NULL); - qd->udp_query_data = purple_dnsquery_a(qd->real_hostname, qd->real_port, - udp_host_resolved, gc); - if (qd->udp_query_data == NULL) { - purple_connection_error_reason(qd->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Could not resolve hostname")); + qd->conn_data = purple_proxy_connect(gc, account, server, port, connect_cb, gc); + if ( qd->conn_data == NULL ) { + purple_debug_error("QQ", _("Couldn't create socket")); + return FALSE; } + return TRUE; } /* clean up qq_data structure and all its components @@ -879,65 +796,32 @@ g_return_if_fail(gc != NULL && gc->proto_data != NULL); qd = (qq_data *) gc->proto_data; - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Disconnecting ...\n"); + purple_debug_info("QQ", "Disconnecting ...\n"); - if (qd->network_timeout > 0) { - purple_timeout_remove(qd->network_timeout); - qd->network_timeout = 0; + if (qd->network_watcher > 0) { + purple_debug_info("QQ", "Remove network watcher\n"); + purple_timeout_remove(qd->network_watcher); + qd->network_watcher = 0; } /* finish all I/O */ - if (qd->fd >= 0 && qd->logged_in) { + if (qd->fd >= 0 && qd->is_login) { qq_send_packet_logout(gc); } - if (gc->inpa > 0) { - purple_input_remove(gc->inpa); - gc->inpa = 0; - } - - if (qd->fd >= 0) { - close(qd->fd); - qd->fd = -1; - } - - if (qd->reconnect_timeout > 0) { - purple_timeout_remove(qd->reconnect_timeout); - qd->reconnect_timeout = 0; - } - - if (qd->connect_data != NULL) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Cancel connect_data\n"); - purple_proxy_connect_cancel(qd->connect_data); + /* not connected */ + if (qd->conn_data != NULL) { + purple_debug_info("QQ", "Connect cancel\n"); + purple_proxy_connect_cancel(qd->conn_data); + qd->conn_data = NULL; } - - if(qd->tcp_txbuf != NULL) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy tcp_txbuf\n"); - purple_circ_buffer_destroy(qd->tcp_txbuf); - qd->tcp_txbuf = NULL; - } - - if (qd->tx_handler) { - purple_input_remove(qd->tx_handler); - qd->tx_handler = 0; - } - if (qd->tcp_rxqueue != NULL) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy tcp_rxqueue\n"); - g_free(qd->tcp_rxqueue); - qd->tcp_rxqueue = NULL; - qd->tcp_rxlen = 0; - } - - if (qd->udp_query_data != NULL) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy udp_query_data\n"); - purple_dnsquery_destroy(qd->udp_query_data); - qd->udp_query_data = NULL; - } + connection_free_all(qd); + qd->fd = -1; - qq_trans_remove_all(qd); - + qq_trans_remove_all(gc); + if (qd->token) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "free token\n"); + purple_debug_info("QQ", "free token\n"); g_free(qd->token); qd->token = NULL; qd->token_len = 0; @@ -955,13 +839,13 @@ qq_buddies_list_free(gc->account, qd); } -static gint encap(qq_data *qd, guint8 *buf, gint maxlen, guint16 cmd, guint16 seq, +static gint packet_encap(qq_data *qd, guint8 *buf, gint maxlen, guint16 cmd, guint16 seq, guint8 *data, gint data_len) { gint bytes = 0; g_return_val_if_fail(qd != NULL && buf != NULL && maxlen > 0, -1); g_return_val_if_fail(data != NULL && data_len > 0, -1); - + /* QQ TCP packet has two bytes in the begining defines packet length * so leave room here to store packet size */ if (qd->use_tcp) { @@ -971,7 +855,7 @@ bytes += qq_put8(buf + bytes, QQ_PACKET_TAG); bytes += qq_put16(buf + bytes, QQ_CLIENT); bytes += qq_put16(buf + bytes, cmd); - + bytes += qq_put16(buf + bytes, seq); bytes += qq_put32(buf + bytes, qd->uid); @@ -987,109 +871,144 @@ } /* data has been encrypted before */ -gint qq_send_data(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack, - guint8 *data, gint data_len) +static gint packet_send_out(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len) { + qq_data *qd; guint8 *buf; gint buf_len; gint bytes_sent; - g_return_val_if_fail(qd != NULL, -1); + g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); + qd = (qq_data *)gc->proto_data; g_return_val_if_fail(data != NULL && data_len > 0, -1); buf = g_newa(guint8, MAX_PACKET_SIZE); memset(buf, 0, MAX_PACKET_SIZE); - buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, data, data_len); + buf_len = packet_encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, data, data_len); if (buf_len <= 0) { return -1; } if (qd->use_tcp) { - bytes_sent = tcp_send_out(qd, buf, buf_len); + bytes_sent = tcp_send_out(gc, buf, buf_len); } else { - bytes_sent = udp_send_out(qd, buf, buf_len); + bytes_sent = udp_send_out(gc, buf, buf_len); } - if (need_ack) { - qq_trans_add_client_cmd(qd, cmd, seq, data, data_len); - } - -#if 1 - /* qq_show_packet("QQ_SEND_DATA", buf, buf_len); */ - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "<== [%05d], 0x%04X %s, total %d bytes is sent %d\n", - seq, cmd, qq_get_cmd_desc(cmd), buf_len, bytes_sent); -#endif return bytes_sent; } -/* Encrypt data with session_key, then call qq_send_data */ -gint qq_send_cmd_detail(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack, - guint8 *data, gint data_len) +gint qq_send_cmd_encrypted(PurpleConnection *gc, guint16 cmd, guint16 seq, + guint8 *data, gint data_len, gboolean need_ack) { + gint send_len; + +#if 1 + purple_debug_info("QQ", "<== [%05d], %s(0x%04X), datalen %d\n", + seq, qq_get_cmd_desc(cmd), cmd, data_len); +#endif + + send_len = packet_send_out(gc, cmd, seq, data, data_len); + if (need_ack) { + qq_trans_add_client_cmd(gc, cmd, seq, data, data_len, 0, 0); + } + return send_len; +} + +/* Encrypt data with session_key, and send packet out */ +static gint send_cmd_detail(PurpleConnection *gc, guint16 cmd, guint16 seq, + guint8 *data, gint data_len, gboolean need_ack, gint update_class, guint32 ship32) +{ + qq_data *qd; guint8 *encrypted_data; gint encrypted_len; + gint bytes_sent; - g_return_val_if_fail(qd != NULL, -1); + g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); + qd = (qq_data *)gc->proto_data; g_return_val_if_fail(data != NULL && data_len > 0, -1); /* at most 16 bytes more */ encrypted_data = g_newa(guint8, data_len + 16); -#if 0 - purple_debug(PURPLE_DEBUG_INFO, "QQ_ENCRYPT", - "Before %d: [%05d] 0x%04X %s\n", - data_len, seq, cmd, qq_get_cmd_desc(cmd)); -#endif encrypted_len = qq_encrypt(encrypted_data, data, data_len, qd->session_key); if (encrypted_len < 16) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ_ENCRYPT", - "Error len %d: [%05d] 0x%04X %s\n", + purple_debug_error("QQ_ENCRYPT", "Error len %d: [%05d] 0x%04X %s\n", encrypted_len, seq, cmd, qq_get_cmd_desc(cmd)); return -1; } -#if 0 - purple_debug(PURPLE_DEBUG_INFO, "QQ_ENCRYPT", - "After %d: [%05d] 0x%04X %s\n", - encrypted_len, seq, cmd, qq_get_cmd_desc(cmd)); -#endif - return qq_send_data(qd, cmd, seq, need_ack, encrypted_data, encrypted_len); + bytes_sent = packet_send_out(gc, cmd, seq, encrypted_data, encrypted_len); + + if (need_ack) { + qq_trans_add_client_cmd(gc, cmd, seq, encrypted_data, encrypted_len, update_class, ship32); + } + return bytes_sent; } -/* set seq and need_ack, then call qq_send_cmd_detail */ -gint qq_send_cmd(qq_data *qd, guint16 cmd, guint8 *data, gint data_len) +gint qq_send_cmd_mess(PurpleConnection *gc, guint16 cmd, guint8 *data, gint data_len, + gint update_class, guint32 ship32) { - g_return_val_if_fail(qd != NULL, -1); + qq_data *qd; + guint16 seq; + + g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); + qd = (qq_data *) gc->proto_data; g_return_val_if_fail(data != NULL && data_len > 0, -1); - qd->send_seq++; - return qq_send_cmd_detail(qd, cmd, qd->send_seq, TRUE, data, data_len); + seq = ++qd->send_seq; +#if 1 + purple_debug_info("QQ", "<== [%05d], %s(0x%04X), datalen %d\n", + seq, qq_get_cmd_desc(cmd), cmd, data_len); +#endif + return send_cmd_detail(gc, cmd, seq, data, data_len, TRUE, update_class, ship32); } -gint qq_send_room_cmd_noid(PurpleConnection *gc, guint8 room_cmd, - guint8 *data, gint data_len) +/* set seq and need_ack, then call send_cmd_detail */ +gint qq_send_cmd(PurpleConnection *gc, guint16 cmd, guint8 *data, gint data_len) { - return qq_send_room_cmd(gc, room_cmd, 0, data, data_len); + qq_data *qd; + guint16 seq; + gboolean need_ack; + + g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); + qd = (qq_data *) gc->proto_data; + g_return_val_if_fail(data != NULL && data_len > 0, -1); + + if (cmd != QQ_CMD_LOGOUT) { + seq = ++qd->send_seq; + need_ack = TRUE; + } else { + seq = 0xFFFF; + need_ack = FALSE; + } +#if 1 + purple_debug_info("QQ", "<== [%05d], %s(0x%04X), datalen %d\n", + seq, qq_get_cmd_desc(cmd), cmd, data_len); +#endif + return send_cmd_detail(gc, cmd, seq, data, data_len, need_ack, 0, 0); } -gint qq_send_room_cmd_only(PurpleConnection *gc, guint8 room_cmd, guint32 room_id) +/* set seq and need_ack, then call send_cmd_detail */ +gint qq_send_server_reply(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len) { - g_return_val_if_fail(room_cmd > 0 && room_id > 0, -1); - return qq_send_room_cmd(gc, room_cmd, room_id, NULL, 0); +#if 1 + purple_debug_info("QQ", "<== [SRV-%05d], %s(0x%04X), datalen %d\n", + seq, qq_get_cmd_desc(cmd), cmd, data_len); +#endif + return send_cmd_detail(gc, cmd, seq, data, data_len, FALSE, 0, 0); } -gint qq_send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id, - guint8 *data, gint data_len) +static gint send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id, + guint8 *data, gint data_len, gint update_class, guint32 ship32) { qq_data *qd; - guint8 *buf; gint buf_len; guint8 *encrypted_data; gint encrypted_len; gint bytes_sent; guint16 seq; - + g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); qd = (qq_data *) gc->proto_data; @@ -1106,6 +1025,7 @@ if (data != NULL && data_len > 0) { buf_len += qq_putdata(buf + buf_len, data, data_len); } + qd->send_seq++; seq = qd->send_seq; @@ -1114,32 +1034,43 @@ encrypted_data = g_newa(guint8, buf_len + 16); encrypted_len = qq_encrypt(encrypted_data, buf, buf_len, qd->session_key); if (encrypted_len < 16) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ_ENCRYPT", - "Error len %d: [%05d] QQ_CMD_ROOM.(0x%02X %s)\n", - encrypted_len, seq, room_cmd, qq_get_room_cmd_desc(room_cmd)); - return -1; - } - - /* Encap header to buf */ - buf_len = encap(qd, buf, MAX_PACKET_SIZE, QQ_CMD_ROOM, seq, encrypted_data, encrypted_len); - if (buf_len <= 0) { + purple_debug_error("QQ_ENCRYPT", "Error len %d: [%05d] %s (0x%02X)\n", + encrypted_len, seq, qq_get_room_cmd_desc(room_cmd), room_cmd); return -1; } - if (qd->use_tcp) { - bytes_sent = tcp_send_out(qd, buf, buf_len); - } else { - bytes_sent = udp_send_out(qd, buf, buf_len); - } - - qq_trans_add_room_cmd(qd, seq, room_cmd, room_id, buf, buf_len); - + bytes_sent = packet_send_out(gc, QQ_CMD_ROOM, seq, encrypted_data, encrypted_len); #if 1 /* qq_show_packet("QQ_SEND_DATA", buf, buf_len); */ - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "<== [%05d], QQ_CMD_ROOM.(0x%02X %s) to room %d, total %d bytes is sent %d\n", - seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, - buf_len, bytes_sent); + purple_debug_info("QQ", + "<== [%05d], %s (0x%02X) to room %d, datalen %d\n", + seq, qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len); #endif + + qq_trans_add_room_cmd(gc, seq, room_cmd, room_id, buf, buf_len, update_class, ship32); return bytes_sent; } + +gint qq_send_room_cmd_mess(PurpleConnection *gc, guint8 room_cmd, guint32 room_id, + guint8 *data, gint data_len, gint update_class, guint32 ship32) +{ + return send_room_cmd(gc, room_cmd, room_id, data, data_len, update_class, ship32); +} + +gint qq_send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id, + guint8 *data, gint data_len) +{ + return send_room_cmd(gc, room_cmd, room_id, data, data_len, 0, 0); +} + +gint qq_send_room_cmd_noid(PurpleConnection *gc, guint8 room_cmd, + guint8 *data, gint data_len) +{ + return send_room_cmd(gc, room_cmd, 0, data, data_len, 0, 0); +} + +gint qq_send_room_cmd_only(PurpleConnection *gc, guint8 room_cmd, guint32 room_id) +{ + g_return_val_if_fail(room_cmd > 0 && room_id > 0, -1); + return send_room_cmd(gc, room_cmd, room_id, NULL, 0, 0, 0); +}