libpurple/protocols/gg/tcpsocket.c

changeset 38139
42143502b9d0
parent 37417
b29ee022017f
child 38142
1dff6e343f9f
--- a/libpurple/protocols/gg/tcpsocket.c	Sun Sep 11 11:39:25 2016 -0500
+++ b/libpurple/protocols/gg/tcpsocket.c	Sun Sep 11 11:25:03 2016 -0500
@@ -30,32 +30,86 @@
 #include "gg.h"
 
 #include "debug.h"
-#include "purple-socket.h"
+#include "purple-gio.h"
+
+typedef struct {
+	GSocketConnection *conn;
+	GCancellable *cancellable;
+	PurpleConnection *gc;
+	gpointer priv_gg;
+} GGPTcpSocketData;
+
+static void
+ggp_tcp_socket_data_free(GGPTcpSocketData *data)
+{
+	g_return_if_fail(data != NULL);
+
+	if (data->cancellable != NULL) {
+		g_cancellable_cancel(data->cancellable);
+		g_clear_object(&data->cancellable);
+	}
+
+	if (data->conn != NULL) {
+		purple_gio_graceful_close(G_IO_STREAM(data->conn), NULL, NULL);
+		g_clear_object(&data->conn);
+	}
+
+	g_free(data);
+}
 
 static void
-ggp_tcpsocket_connected(PurpleSocket *ps, const gchar *error, gpointer priv_gg)
+ggp_tcpsocket_connected(GObject *source, GAsyncResult *res, gpointer user_data)
 {
-	PurpleConnection *gc = purple_socket_get_connection(ps);
-	GGPInfo *info = purple_connection_get_protocol_data(gc);
+	GGPTcpSocketData *data = user_data;
+	GSocketConnection *conn;
+	GSocket *socket;
 	int fd = -1;
+	GGPInfo *info;
+	GError *error = NULL;
+
+	conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source),
+			res, &error);
 
-	PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
+	if (conn == NULL) {
+		if (!g_error_matches(error,
+				G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+			purple_debug_error("gg", "socket failed to connect: %s",
+					error->message);
+		}
 
-	if (error == NULL)
-		fd = purple_socket_get_fd(ps);
+		g_clear_error(&error);
+	} else {
+		data->conn = conn;
 
-	if (!gg_socket_manager_connected(ps, priv_gg, fd)) {
-		purple_debug_error("gg", "socket not handled");
-		purple_socket_destroy(ps);
+		socket = g_socket_connection_get_socket(data->conn);
+
+		if (socket != NULL) {
+			fd = g_socket_get_fd(socket);
+		}
 	}
 
-	if (info->inpa > 0)
-		purple_input_remove(info->inpa);
+	PURPLE_ASSERT_CONNECTION_IS_VALID(data->gc);
+
+	if (!gg_socket_manager_connected(data, data->priv_gg, fd)) {
+		purple_debug_error("gg", "socket not handled");
+		ggp_tcp_socket_data_free(data);
+		return;
+	}
+
+	info = purple_connection_get_protocol_data(data->gc);
+
+	if (info->inpa > 0) {
+		g_source_remove(info->inpa);
+		info->inpa = 0;
+	}
+
 	if (info->session->fd < 0)
 		return;
+
+	/* XXX: This works, but not recommended to use GSocket FDs directly */
 	info->inpa = purple_input_add(info->session->fd,
 		ggp_tcpsocket_inputcond_gg_to_purple(info->session->check),
-		ggp_async_login_handler, gc);
+		ggp_async_login_handler, data->gc);
 }
 
 static void*
@@ -63,7 +117,9 @@
 	int is_async, void *priv)
 {
 	PurpleConnection *gc = _gc;
-	PurpleSocket *ps;
+	GGPTcpSocketData *data;
+	GSocketClient *client;
+	GError *error = NULL;
 
 	PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
 	g_return_val_if_fail(!purple_connection_is_disconnecting(gc), NULL);
@@ -74,40 +130,100 @@
 	purple_debug_misc("gg", "ggp_tcpsocket_connect(%p, %s:%d, %s, %p)",
 		gc, host, port, is_tls ? "tls" : "tcp", priv);
 
-	ps = purple_socket_new(gc);
-	purple_socket_set_tls(ps, is_tls);
-	purple_socket_set_host(ps, host);
-	purple_socket_set_port(ps, port);
-	if (!purple_socket_connect(ps, ggp_tcpsocket_connected, priv)) {
-		purple_socket_destroy(ps);
+	client = purple_gio_socket_client_new(
+			purple_connection_get_account(gc), &error);
+
+	if (client == NULL) {
+		purple_debug_error("gg", "unable to connect: %s",
+				error->message);
+		g_clear_error(&error);
 		return NULL;
 	}
 
-	return ps;
+	g_socket_client_set_tls(client, is_tls);
+
+	data = g_new0(GGPTcpSocketData, 1);
+	data->cancellable = g_cancellable_new();
+	data->gc = gc;
+	data->priv_gg = priv;
+
+	g_socket_client_connect_to_host_async(client, host, port,
+			data->cancellable, ggp_tcpsocket_connected, data);
+	g_object_unref(client);
+
+	return data;
 }
 
 static void
-ggp_tcpsocket_close(void *_gc, void *_ps)
+ggp_tcpsocket_close(void *_gc, void *_data)
 {
-	PurpleSocket *ps = _ps;
+	GGPTcpSocketData *data = _data;
 
-	purple_socket_destroy(ps);
+	ggp_tcp_socket_data_free(data);
 }
 
 static ssize_t
-ggp_tcpsocket_read(void *_gc, void *_ps, unsigned char *buffer, size_t bufsize)
+ggp_tcpsocket_read(void *_gc, void *_data, unsigned char *buffer, size_t bufsize)
 {
-	PurpleSocket *ps = _ps;
+	GGPTcpSocketData *data = _data;
+	GPollableInputStream *input;
+	gssize ret;
+	GError *error = NULL;
+
+	if (data->conn == NULL) {
+		return -1;
+	}
+
+	input = G_POLLABLE_INPUT_STREAM(
+			g_io_stream_get_input_stream(G_IO_STREAM(data->conn)));
+	ret = g_pollable_input_stream_read_nonblocking(input,
+			buffer, bufsize, NULL, &error);
 
-	return purple_socket_read(ps, buffer, bufsize);
+	if (ret < 0) {
+		if (g_error_matches(error,
+				G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
+			errno = EAGAIN;
+		} else {
+			purple_debug_error("gg", "socket read error: %s",
+					error->message);
+		}
+
+		g_clear_error(&error);
+	}
+
+	return ret;
 }
 
 static ssize_t
-ggp_tcpsocket_write(void *_gc, void *_ps, const unsigned char *data, size_t len)
+ggp_tcpsocket_write(void *_gc, void *_data, const unsigned char *data_buf, size_t len)
 {
-	PurpleSocket *ps = _ps;
+	GGPTcpSocketData *data = _data;
+	GPollableOutputStream *output;
+	gssize ret;
+	GError *error = NULL;
+
+	if (data->conn == NULL) {
+		return -1;
+	}
+
+	output = G_POLLABLE_OUTPUT_STREAM(
+			g_io_stream_get_output_stream(G_IO_STREAM(data->conn)));
+	ret = g_pollable_output_stream_write_nonblocking(output,
+			data_buf, len, NULL, &error);
 
-	return purple_socket_write(ps, data, len);
+	if (ret < 0) {
+		if (g_error_matches(error,
+				G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
+			errno = EAGAIN;
+		} else {
+			purple_debug_error("gg", "socket write error: %s",
+					error->message);
+		}
+
+		g_clear_error(&error);
+	}
+
+	return ret;
 }
 
 void

mercurial