comparison libpurple/protocols/qq/qq_network.c @ 24089:2f5a7edd8f68

2008.09.02 - ccpaging <ccpaging(at)gmail.com> * Bugfix: can not send message to the QUN blocked adding * Tickets: Fixes #6957 Fixes #6990 2008.09.02 - ccpaging <ccpaging(at)gmail.com> * Use new tactics of information update: 1. send next package till the previous package received 2. fix duplicated get_room_info and get_room_buddies command
author SHiNE CsyFeK <csyfek@gmail.com>
date Thu, 11 Sep 2008 13:29:26 +0000
parents 147ada94a1d8
children 6408be948d56 5f964757f517
comparison
equal deleted inserted replaced
24088:147ada94a1d8 24089:2f5a7edd8f68
232 static gboolean packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len) 232 static gboolean packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len)
233 { 233 {
234 qq_data *qd; 234 qq_data *qd;
235 gint bytes, bytes_not_read; 235 gint bytes, bytes_not_read;
236 236
237 gboolean prev_update_status;
238
239 guint8 header_tag; 237 guint8 header_tag;
240 guint16 source_tag; 238 guint16 source_tag;
241 guint16 cmd; 239 guint16 cmd;
242 guint16 seq; /* May be ack_seq or send_seq, depends on cmd */ 240 guint16 seq; /* May be ack_seq or send_seq, depends on cmd */
243 guint8 room_cmd; 241 guint8 room_cmd;
254 /* Len, header and tail tag have been checked before */ 252 /* Len, header and tail tag have been checked before */
255 bytes = 0; 253 bytes = 0;
256 bytes += packet_get_header(&header_tag, &source_tag, &cmd, &seq, buf + bytes); 254 bytes += packet_get_header(&header_tag, &source_tag, &cmd, &seq, buf + bytes);
257 255
258 #if 1 256 #if 1
259 purple_debug_info("QQ", "==> [%05d] 0x%04X %s, source tag 0x%04X len %d\n", 257 purple_debug_info("QQ", "==> [%05d] %s 0x%04X, source tag 0x%04X len %d\n",
260 seq, cmd, qq_get_cmd_desc(cmd), source_tag, buf_len); 258 seq, qq_get_cmd_desc(cmd), cmd, source_tag, buf_len);
261 #endif 259 #endif
262 /* this is the length of all the encrypted data (also remove tail tag) */ 260 /* this is the length of all the encrypted data (also remove tail tag) */
263 bytes_not_read = buf_len - bytes - 1; 261 bytes_not_read = buf_len - bytes - 1;
264 262
265 /* ack packet, we need to update send tranactions */ 263 /* ack packet, we need to update send tranactions */
266 /* we do not check duplication for server ack */ 264 /* we do not check duplication for server ack */
267 trans = qq_trans_find_rcved(gc, cmd, seq); 265 trans = qq_trans_find_rcved(gc, cmd, seq);
268 if (trans == NULL) { 266 if (trans == NULL) {
269 /* new server command */ 267 /* new server command */
270 qq_trans_add_server_cmd(gc, cmd, seq, buf + bytes, bytes_not_read); 268 if ( !qd->is_login ) {
271 if ( qd->is_finish_update ) { 269 qq_trans_add_remain(gc, cmd, seq, buf + bytes, bytes_not_read);
272 qq_proc_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read); 270 } else {
271 qq_trans_add_server_cmd(gc, cmd, seq, buf + bytes, bytes_not_read);
272 qq_proc_server_cmd(gc, cmd, seq, buf + bytes, bytes_not_read);
273 } 273 }
274 return TRUE; 274 return TRUE;
275 } 275 }
276 276
277 if (qq_trans_is_dup(trans)) { 277 if (qq_trans_is_dup(trans)) {
278 purple_debug_info("QQ", "dup [%05d] %s, discard...\n", seq, qq_get_cmd_desc(cmd)); 278 purple_debug_info("QQ", "dup [%05d] %s, discard...\n", seq, qq_get_cmd_desc(cmd));
279 return TRUE; 279 return TRUE;
280 } 280 }
281 281
282 if (qq_trans_is_server(trans)) {
283 if ( qd->is_finish_update ) {
284 qq_proc_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read);
285 }
286 return TRUE;
287 }
288
289 update_class = qq_trans_get_class(trans); 282 update_class = qq_trans_get_class(trans);
290 ship32 = qq_trans_get_ship(trans); 283 ship32 = qq_trans_get_ship(trans);
291 284
292 prev_update_status = qd->is_finish_update;
293 switch (cmd) { 285 switch (cmd) {
294 case QQ_CMD_TOKEN: 286 case QQ_CMD_TOKEN:
295 if (qq_process_token_reply(gc, buf + bytes, bytes_not_read) == QQ_TOKEN_REPLY_OK) { 287 if (qq_process_token_reply(gc, buf + bytes, bytes_not_read) == QQ_TOKEN_REPLY_OK) {
296 qq_send_packet_login(gc); 288 qq_send_packet_login(gc);
297 } 289 }
298 break; 290 break;
299 case QQ_CMD_LOGIN: 291 case QQ_CMD_LOGIN:
300 qq_proc_cmd_login(gc, buf + bytes, bytes_not_read); 292 qq_proc_login_cmd(gc, buf + bytes, bytes_not_read);
301 /* check is redirect or not, and do it now */ 293 /* check is redirect or not, and do it now */
302 if (qd->redirect_ip.s_addr != 0) { 294 if (qd->redirect_ip.s_addr != 0) {
303 if (qd->check_watcher > 0) { 295 if (qd->check_watcher > 0) {
304 purple_timeout_remove(qd->check_watcher); 296 purple_timeout_remove(qd->check_watcher);
305 qd->check_watcher = 0; 297 qd->check_watcher = 0;
314 room_id = qq_trans_get_room_id(trans); 306 room_id = qq_trans_get_room_id(trans);
315 #if 1 307 #if 1
316 purple_debug_info("QQ", "%s (0x%02X) for room %d, len %d\n", 308 purple_debug_info("QQ", "%s (0x%02X) for room %d, len %d\n",
317 qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len); 309 qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len);
318 #endif 310 #endif
319 qq_proc_room_cmd_reply(gc, seq, room_cmd, room_id, buf + bytes, bytes_not_read, update_class, ship32); 311 qq_proc_room_cmd(gc, seq, room_cmd, room_id, buf + bytes, bytes_not_read, update_class, ship32);
320 break; 312 break;
321 default: 313 default:
322 qq_proc_cmd_reply(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32); 314 qq_proc_client_cmd(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32);
323 break; 315 break;
324 } 316 }
325 317
326 if (prev_update_status != qd->is_finish_update && qd->is_finish_update == TRUE) {
327 /* is_login, but we have packets before login */
328 qq_trans_process_before_login(gc);
329 return TRUE;
330 }
331 return TRUE; 318 return TRUE;
332 } 319 }
333 320
334 static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond) 321 static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond)
335 { 322 {
682 /* QQ use random seq, to minimize duplicated packets */ 669 /* QQ use random seq, to minimize duplicated packets */
683 srand(time(NULL)); 670 srand(time(NULL));
684 qd->send_seq = rand() & 0xffff; 671 qd->send_seq = rand() & 0xffff;
685 672
686 qd->is_login = FALSE; 673 qd->is_login = FALSE;
687 qd->is_finish_update = FALSE;
688 qd->channel = 1; 674 qd->channel = 1;
689 qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10); 675 qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10);
690 676
691 /* now generate md5 processed passwd */ 677 /* now generate md5 processed passwd */
692 passwd = purple_account_get_password(purple_connection_get_account(gc)); 678 passwd = purple_account_get_password(purple_connection_get_account(gc));
751 } 737 }
752 738
753 do_request_token( gc ); 739 do_request_token( gc );
754 } 740 }
755 741
742 #ifndef purple_proxy_connect_udp
743 static void udp_can_write(gpointer data, gint source, PurpleInputCondition cond)
744 {
745 PurpleConnection *gc;
746 qq_data *qd;
747 socklen_t len;
748 int error=0, ret;
749
750 gc = (PurpleConnection *) data;
751 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
752
753 qd = (qq_data *) gc->proto_data;
754
755
756 purple_debug_info("proxy", "Connected.\n");
757
758 /*
759 * getsockopt after a non-blocking connect returns -1 if something is
760 * really messed up (bad descriptor, usually). Otherwise, it returns 0 and
761 * error holds what connect would have returned if it blocked until now.
762 * Thus, error == 0 is success, error == EINPROGRESS means "try again",
763 * and anything else is a real error.
764 *
765 * (error == EINPROGRESS can happen after a select because the kernel can
766 * be overly optimistic sometimes. select is just a hint that you might be
767 * able to do something.)
768 */
769 len = sizeof(error);
770 ret = getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len);
771 if (ret == 0 && error == EINPROGRESS)
772 return; /* we'll be called again later */
773
774 purple_input_remove(qd->udp_can_write_handler);
775 qd->udp_can_write_handler = 0;
776 if (ret < 0 || error != 0) {
777 if(ret != 0)
778 error = errno;
779
780 close(source);
781
782 purple_debug_error("proxy", "getsockopt SO_ERROR check: %s\n", g_strerror(error));
783
784 connect_cb(gc, -1, _("Unable to connect"));
785 return;
786 }
787
788 connect_cb(gc, source, NULL);
789 }
790
791 static void udp_host_resolved(GSList *hosts, gpointer data, const char *error_message) {
792 PurpleConnection *gc;
793 qq_data *qd;
794 struct sockaddr server_addr;
795 int addr_size;
796 gint fd = -1;
797 int flags;
798
799 gc = (PurpleConnection *) data;
800 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
801
802 qd = (qq_data *) gc->proto_data;
803
804 /* udp_query_data must be set as NULL.
805 * Otherwise purple_dnsquery_destroy in qq_disconnect cause glib double free error */
806 qd->udp_query_data = NULL;
807
808 if (!hosts || !hosts->data) {
809 purple_connection_error_reason(gc,
810 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
811 _("Couldn't resolve host"));
812 return;
813 }
814
815 addr_size = GPOINTER_TO_INT(hosts->data);
816 hosts = g_slist_remove(hosts, hosts->data);
817 memcpy(&server_addr, hosts->data, addr_size);
818 g_free(hosts->data);
819
820 hosts = g_slist_remove(hosts, hosts->data);
821 while(hosts) {
822 hosts = g_slist_remove(hosts, hosts->data);
823 g_free(hosts->data);
824 hosts = g_slist_remove(hosts, hosts->data);
825 }
826
827 fd = socket(PF_INET, SOCK_DGRAM, 0);
828 if (fd < 0) {
829 purple_debug_error("QQ",
830 "Unable to create socket: %s\n", g_strerror(errno));
831 return;
832 }
833
834 /* we use non-blocking mode to speed up connection */
835 flags = fcntl(fd, F_GETFL);
836 fcntl(fd, F_SETFL, flags | O_NONBLOCK);
837
838 /* From Unix-socket-FAQ: http://www.faqs.org/faqs/unix-faq/socket/
839 *
840 * If a UDP socket is unconnected, which is the normal state after a
841 * bind() call, then send() or write() are not allowed, since no
842 * destination is available; only sendto() can be used to send data.
843 *
844 * Calling connect() on the socket simply records the specified address
845 * and port number as being the desired communications partner. That
846 * means that send() or write() are now allowed; they use the destination
847 * address and port given on the connect call as the destination of packets.
848 */
849 if (connect(fd, &server_addr, addr_size) >= 0) {
850 purple_debug_info("QQ", "Connected.\n");
851 flags = fcntl(fd, F_GETFL);
852 fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
853 connect_cb(gc, fd, NULL);
854 return;
855 }
856
857 /* [EINPROGRESS]
858 * The socket is marked as non-blocking and the connection cannot be
859 * completed immediately. It is possible to select for completion by
860 * selecting the socket for writing.
861 * [EINTR]
862 * A signal interrupted the call.
863 * The connection is established asynchronously.
864 */
865 if ((errno == EINPROGRESS) || (errno == EINTR)) {
866 purple_debug_warning( "QQ", "Connect in asynchronous mode.\n");
867 qd->udp_can_write_handler = purple_input_add(fd, PURPLE_INPUT_WRITE, udp_can_write, gc);
868 return;
869 }
870
871 purple_debug_error("QQ", "Connection failed: %s\n", g_strerror(errno));
872 close(fd);
873 }
874 #endif
875
756 gboolean connect_to_server(PurpleConnection *gc, gchar *server, gint port) 876 gboolean connect_to_server(PurpleConnection *gc, gchar *server, gint port)
757 { 877 {
758 PurpleAccount *account ; 878 PurpleAccount *account ;
759 qq_data *qd; 879 qq_data *qd;
760 gchar *conn_msg; 880 gchar *conn_msg;
777 897
778 if (qd->conn_data != NULL) { 898 if (qd->conn_data != NULL) {
779 purple_proxy_connect_cancel(qd->conn_data); 899 purple_proxy_connect_cancel(qd->conn_data);
780 qd->conn_data = NULL; 900 qd->conn_data = NULL;
781 } 901 }
782 qd->conn_data = purple_proxy_connect(gc, account, server, port, connect_cb, gc); 902
903 #ifdef purple_proxy_connect_udp
904 if (qd->use_tcp) {
905 qd->conn_data = purple_proxy_connect(gc, account, server, port, connect_cb, gc);
906 } else {
907 qd->conn_data = purple_proxy_connect_udp(gc, account, server, port, connect_cb, gc);
908 }
783 if ( qd->conn_data == NULL ) { 909 if ( qd->conn_data == NULL ) {
784 purple_debug_error("QQ", _("Couldn't create socket")); 910 purple_debug_error("QQ", _("Couldn't create socket"));
785 return FALSE; 911 return FALSE;
786 } 912 }
913 #else
914 /* QQ connection via UDP/TCP.
915 * Now use Purple proxy function to provide TCP proxy support,
916 * and qq_udp_proxy.c to add UDP proxy support (thanks henry) */
917 if(qd->use_tcp) {
918 qd->conn_data = purple_proxy_connect(gc, account, server, port, connect_cb, gc);
919 if ( qd->conn_data == NULL ) {
920 purple_debug_error("QQ", "Unable to connect.");
921 return FALSE;
922 }
923 return TRUE;
924 }
925
926 purple_debug_info("QQ", "UDP Connect to %s:%d\n", server, port);
927 qd->udp_query_data = purple_dnsquery_a(server, port, udp_host_resolved, gc);
928 if ( qd->udp_query_data == NULL ) {
929 purple_debug_error("QQ", "Could not resolve hostname");
930 return FALSE;
931 }
932 #endif
787 return TRUE; 933 return TRUE;
788 } 934 }
789 935
790 /* clean up qq_data structure and all its components 936 /* clean up qq_data structure and all its components
791 * always used before a redirectly connection */ 937 * always used before a redirectly connection */
813 if (qd->conn_data != NULL) { 959 if (qd->conn_data != NULL) {
814 purple_debug_info("QQ", "Connect cancel\n"); 960 purple_debug_info("QQ", "Connect cancel\n");
815 purple_proxy_connect_cancel(qd->conn_data); 961 purple_proxy_connect_cancel(qd->conn_data);
816 qd->conn_data = NULL; 962 qd->conn_data = NULL;
817 } 963 }
964 #ifndef purple_proxy_connect_udp
965 if (qd->udp_can_write_handler) {
966 purple_input_remove(qd->udp_can_write_handler);
967 qd->udp_can_write_handler = 0;
968 }
969 if (qd->udp_query_data != NULL) {
970 purple_debug_info("QQ", "destroy udp_query_data\n");
971 purple_dnsquery_destroy(qd->udp_query_data);
972 qd->udp_query_data = NULL;
973 }
974 #endif
818 connection_free_all(qd); 975 connection_free_all(qd);
819 qd->fd = -1; 976 qd->fd = -1;
820 977
821 qq_trans_remove_all(gc); 978 qq_trans_remove_all(gc);
822 979