Mercurial > pidgin
comparison libpurple/protocols/qq/qq_network.c @ 24090:6408be948d56
disapproval of revision '92d52eef2994d2697999177804e3665989cfa352'
author | Daniel Atallah <daniel.atallah@gmail.com> |
---|---|
date | Mon, 15 Sep 2008 03:03:59 +0000 |
parents | 2f5a7edd8f68 |
children | 5c030dc88356 |
comparison
equal
deleted
inserted
replaced
24089:2f5a7edd8f68 | 24090:6408be948d56 |
---|---|
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 | |
237 guint8 header_tag; | 239 guint8 header_tag; |
238 guint16 source_tag; | 240 guint16 source_tag; |
239 guint16 cmd; | 241 guint16 cmd; |
240 guint16 seq; /* May be ack_seq or send_seq, depends on cmd */ | 242 guint16 seq; /* May be ack_seq or send_seq, depends on cmd */ |
241 guint8 room_cmd; | 243 guint8 room_cmd; |
252 /* Len, header and tail tag have been checked before */ | 254 /* Len, header and tail tag have been checked before */ |
253 bytes = 0; | 255 bytes = 0; |
254 bytes += packet_get_header(&header_tag, &source_tag, &cmd, &seq, buf + bytes); | 256 bytes += packet_get_header(&header_tag, &source_tag, &cmd, &seq, buf + bytes); |
255 | 257 |
256 #if 1 | 258 #if 1 |
257 purple_debug_info("QQ", "==> [%05d] %s 0x%04X, source tag 0x%04X len %d\n", | 259 purple_debug_info("QQ", "==> [%05d] 0x%04X %s, source tag 0x%04X len %d\n", |
258 seq, qq_get_cmd_desc(cmd), cmd, source_tag, buf_len); | 260 seq, cmd, qq_get_cmd_desc(cmd), source_tag, buf_len); |
259 #endif | 261 #endif |
260 /* this is the length of all the encrypted data (also remove tail tag) */ | 262 /* this is the length of all the encrypted data (also remove tail tag) */ |
261 bytes_not_read = buf_len - bytes - 1; | 263 bytes_not_read = buf_len - bytes - 1; |
262 | 264 |
263 /* ack packet, we need to update send tranactions */ | 265 /* ack packet, we need to update send tranactions */ |
264 /* we do not check duplication for server ack */ | 266 /* we do not check duplication for server ack */ |
265 trans = qq_trans_find_rcved(gc, cmd, seq); | 267 trans = qq_trans_find_rcved(gc, cmd, seq); |
266 if (trans == NULL) { | 268 if (trans == NULL) { |
267 /* new server command */ | 269 /* new server command */ |
268 if ( !qd->is_login ) { | 270 qq_trans_add_server_cmd(gc, cmd, seq, buf + bytes, bytes_not_read); |
269 qq_trans_add_remain(gc, cmd, seq, buf + bytes, bytes_not_read); | 271 if ( qd->is_finish_update ) { |
270 } else { | 272 qq_proc_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read); |
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 | |
282 update_class = qq_trans_get_class(trans); | 289 update_class = qq_trans_get_class(trans); |
283 ship32 = qq_trans_get_ship(trans); | 290 ship32 = qq_trans_get_ship(trans); |
284 | 291 |
292 prev_update_status = qd->is_finish_update; | |
285 switch (cmd) { | 293 switch (cmd) { |
286 case QQ_CMD_TOKEN: | 294 case QQ_CMD_TOKEN: |
287 if (qq_process_token_reply(gc, buf + bytes, bytes_not_read) == QQ_TOKEN_REPLY_OK) { | 295 if (qq_process_token_reply(gc, buf + bytes, bytes_not_read) == QQ_TOKEN_REPLY_OK) { |
288 qq_send_packet_login(gc); | 296 qq_send_packet_login(gc); |
289 } | 297 } |
290 break; | 298 break; |
291 case QQ_CMD_LOGIN: | 299 case QQ_CMD_LOGIN: |
292 qq_proc_login_cmd(gc, buf + bytes, bytes_not_read); | 300 qq_proc_cmd_login(gc, buf + bytes, bytes_not_read); |
293 /* check is redirect or not, and do it now */ | 301 /* check is redirect or not, and do it now */ |
294 if (qd->redirect_ip.s_addr != 0) { | 302 if (qd->redirect_ip.s_addr != 0) { |
295 if (qd->check_watcher > 0) { | 303 if (qd->check_watcher > 0) { |
296 purple_timeout_remove(qd->check_watcher); | 304 purple_timeout_remove(qd->check_watcher); |
297 qd->check_watcher = 0; | 305 qd->check_watcher = 0; |
306 room_id = qq_trans_get_room_id(trans); | 314 room_id = qq_trans_get_room_id(trans); |
307 #if 1 | 315 #if 1 |
308 purple_debug_info("QQ", "%s (0x%02X) for room %d, len %d\n", | 316 purple_debug_info("QQ", "%s (0x%02X) for room %d, len %d\n", |
309 qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len); | 317 qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len); |
310 #endif | 318 #endif |
311 qq_proc_room_cmd(gc, seq, room_cmd, room_id, buf + bytes, bytes_not_read, update_class, ship32); | 319 qq_proc_room_cmd_reply(gc, seq, room_cmd, room_id, buf + bytes, bytes_not_read, update_class, ship32); |
312 break; | 320 break; |
313 default: | 321 default: |
314 qq_proc_client_cmd(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32); | 322 qq_proc_cmd_reply(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32); |
315 break; | 323 break; |
316 } | 324 } |
317 | 325 |
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 } | |
318 return TRUE; | 331 return TRUE; |
319 } | 332 } |
320 | 333 |
321 static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond) | 334 static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond) |
322 { | 335 { |
669 /* QQ use random seq, to minimize duplicated packets */ | 682 /* QQ use random seq, to minimize duplicated packets */ |
670 srand(time(NULL)); | 683 srand(time(NULL)); |
671 qd->send_seq = rand() & 0xffff; | 684 qd->send_seq = rand() & 0xffff; |
672 | 685 |
673 qd->is_login = FALSE; | 686 qd->is_login = FALSE; |
687 qd->is_finish_update = FALSE; | |
674 qd->channel = 1; | 688 qd->channel = 1; |
675 qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10); | 689 qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10); |
676 | 690 |
677 /* now generate md5 processed passwd */ | 691 /* now generate md5 processed passwd */ |
678 passwd = purple_account_get_password(purple_connection_get_account(gc)); | 692 passwd = purple_account_get_password(purple_connection_get_account(gc)); |
737 } | 751 } |
738 | 752 |
739 do_request_token( gc ); | 753 do_request_token( gc ); |
740 } | 754 } |
741 | 755 |
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 | |
876 gboolean connect_to_server(PurpleConnection *gc, gchar *server, gint port) | 756 gboolean connect_to_server(PurpleConnection *gc, gchar *server, gint port) |
877 { | 757 { |
878 PurpleAccount *account ; | 758 PurpleAccount *account ; |
879 qq_data *qd; | 759 qq_data *qd; |
880 gchar *conn_msg; | 760 gchar *conn_msg; |
897 | 777 |
898 if (qd->conn_data != NULL) { | 778 if (qd->conn_data != NULL) { |
899 purple_proxy_connect_cancel(qd->conn_data); | 779 purple_proxy_connect_cancel(qd->conn_data); |
900 qd->conn_data = NULL; | 780 qd->conn_data = NULL; |
901 } | 781 } |
902 | 782 qd->conn_data = purple_proxy_connect(gc, account, server, port, connect_cb, gc); |
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 } | |
909 if ( qd->conn_data == NULL ) { | 783 if ( qd->conn_data == NULL ) { |
910 purple_debug_error("QQ", _("Couldn't create socket")); | 784 purple_debug_error("QQ", _("Couldn't create socket")); |
911 return FALSE; | 785 return FALSE; |
912 } | 786 } |
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 | |
933 return TRUE; | 787 return TRUE; |
934 } | 788 } |
935 | 789 |
936 /* clean up qq_data structure and all its components | 790 /* clean up qq_data structure and all its components |
937 * always used before a redirectly connection */ | 791 * always used before a redirectly connection */ |
959 if (qd->conn_data != NULL) { | 813 if (qd->conn_data != NULL) { |
960 purple_debug_info("QQ", "Connect cancel\n"); | 814 purple_debug_info("QQ", "Connect cancel\n"); |
961 purple_proxy_connect_cancel(qd->conn_data); | 815 purple_proxy_connect_cancel(qd->conn_data); |
962 qd->conn_data = NULL; | 816 qd->conn_data = NULL; |
963 } | 817 } |
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 | |
975 connection_free_all(qd); | 818 connection_free_all(qd); |
976 qd->fd = -1; | 819 qd->fd = -1; |
977 | 820 |
978 qq_trans_remove_all(gc); | 821 qq_trans_remove_all(gc); |
979 | 822 |