Mercurial > pidgin
comparison libpurple/protocols/qq/qq_network.c @ 23879:23cec4360d4a
applied changes from 8cebefbc6cd5d84acb69c74e69e8821f11dd225d
through 92d52eef2994d2697999177804e3665989cfa352
Reapplied 92d52eef2994d2697999177804e3665989cfa352 at the right time.
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
committer: Daniel Atallah <daniel.atallah@gmail.com>
author | SHiNE CsyFeK <csyfek@gmail.com> |
---|---|
date | Mon, 15 Sep 2008 03:01:03 +0000 |
parents | 967344bc404d |
children | 1a0caf9983fa |
comparison
equal
deleted
inserted
replaced
23878:967344bc404d | 23879:23cec4360d4a |
---|---|
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 |