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