diff 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
line wrap: on
line diff
--- a/libpurple/protocols/qq/qq_network.c	Mon Sep 15 02:59:23 2008 +0000
+++ b/libpurple/protocols/qq/qq_network.c	Mon Sep 15 03:01:03 2008 +0000
@@ -234,8 +234,6 @@
 	qq_data *qd;
 	gint bytes, bytes_not_read;
 
-	gboolean prev_update_status;
-
 	guint8 header_tag;
 	guint16 source_tag;
 	guint16 cmd;
@@ -256,8 +254,8 @@
 	bytes += packet_get_header(&header_tag, &source_tag, &cmd, &seq, buf + bytes);
 
 #if 1
-		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);
+		purple_debug_info("QQ", "==> [%05d] %s 0x%04X, source tag 0x%04X len %d\n",
+				seq, qq_get_cmd_desc(cmd), 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;
@@ -267,9 +265,11 @@
 	trans = qq_trans_find_rcved(gc, cmd, seq);
 	if (trans == NULL) {
 		/* new server command */
-		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);
+		if ( !qd->is_login ) {
+			qq_trans_add_remain(gc, cmd, seq, buf + bytes, bytes_not_read);
+		} else {
+			qq_trans_add_server_cmd(gc, cmd, seq, buf + bytes, bytes_not_read);
+			qq_proc_server_cmd(gc, cmd, seq, buf + bytes, bytes_not_read);
 		}
 		return TRUE;
 	}
@@ -279,17 +279,9 @@
 		return TRUE;
 	}
 
-	if (qq_trans_is_server(trans)) {
-		if ( qd->is_finish_update ) {
-			qq_proc_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read);
-		}
-		return TRUE;
-	}
-
 	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) {
@@ -297,7 +289,7 @@
 			}
 			break;
 		case QQ_CMD_LOGIN:
-			qq_proc_cmd_login(gc, buf + bytes, bytes_not_read);
+			qq_proc_login_cmd(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) {
@@ -316,18 +308,13 @@
 			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);
+			qq_proc_room_cmd(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);
+			qq_proc_client_cmd(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32);
 			break;
 	}
 
-	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;
 }
 
@@ -684,7 +671,6 @@
 	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);
 
@@ -753,6 +739,140 @@
 	do_request_token( gc );
 }
 
+#ifndef purple_proxy_connect_udp
+static void udp_can_write(gpointer data, gint source, PurpleInputCondition cond)
+{
+	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->udp_can_write_handler);
+	qd->udp_can_write_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));
+
+		connect_cb(gc, -1, _("Unable to connect"));
+		return;
+	}
+
+	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;
+
+	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;
+
+	if (!hosts || !hosts->data) {
+		purple_connection_error_reason(gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Couldn't resolve host"));
+		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_error("QQ",
+				"Unable to create socket: %s\n", g_strerror(errno));
+		return;
+	}
+
+	/* we use non-blocking mode to speed up connection */
+	flags = fcntl(fd, F_GETFL);
+	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+
+	/* 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_info("QQ", "Connected.\n");
+		flags = fcntl(fd, F_GETFL);
+		fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
+		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_warning( "QQ", "Connect in asynchronous mode.\n");
+			qd->udp_can_write_handler = purple_input_add(fd, PURPLE_INPUT_WRITE, udp_can_write, gc);
+			return;
+		}
+
+	purple_debug_error("QQ", "Connection failed: %s\n", g_strerror(errno));
+	close(fd);
+}
+#endif
+
 gboolean connect_to_server(PurpleConnection *gc, gchar *server, gint port)
 {
 	PurpleAccount *account ;
@@ -779,11 +899,37 @@
 		purple_proxy_connect_cancel(qd->conn_data);
 		qd->conn_data = NULL;
 	}
-	qd->conn_data = purple_proxy_connect(gc, account, server, port, connect_cb, gc);
+
+#ifdef purple_proxy_connect_udp
+	if (qd->use_tcp) {
+		qd->conn_data = purple_proxy_connect(gc, account, server, port, connect_cb, gc);
+	} else {
+		qd->conn_data = purple_proxy_connect_udp(gc, account, server, port, connect_cb, gc);
+	}
 	if ( qd->conn_data == NULL ) {
 		purple_debug_error("QQ", _("Couldn't create socket"));
 		return FALSE;
 	}
+#else
+	/* 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) {
+		qd->conn_data = purple_proxy_connect(gc, account, server, port, connect_cb, gc);
+		if ( qd->conn_data == NULL ) {
+			purple_debug_error("QQ", "Unable to connect.");
+			return FALSE;
+		}
+		return TRUE;
+	}
+
+	purple_debug_info("QQ", "UDP Connect to %s:%d\n", server, port);
+	qd->udp_query_data = purple_dnsquery_a(server, port, udp_host_resolved, gc);
+	if ( qd->udp_query_data == NULL ) {
+		purple_debug_error("QQ", "Could not resolve hostname");
+		return FALSE;
+	}
+#endif
 	return TRUE;
 }
 
@@ -815,6 +961,17 @@
 		purple_proxy_connect_cancel(qd->conn_data);
 		qd->conn_data = NULL;
 	}
+#ifndef purple_proxy_connect_udp
+	if (qd->udp_can_write_handler) {
+		purple_input_remove(qd->udp_can_write_handler);
+		qd->udp_can_write_handler = 0;
+	}
+	if (qd->udp_query_data != NULL) {
+		purple_debug_info("QQ", "destroy udp_query_data\n");
+		purple_dnsquery_destroy(qd->udp_query_data);
+		qd->udp_query_data = NULL;
+	}
+#endif
 	connection_free_all(qd);
 	qd->fd = -1;