--- 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