sslconn: fix infinite wait in purple_ssl_close()

Fri, 29 Apr 2016 11:25:12 +0200

author
Jakub Adam <jakub.adam@ktknet.cz>
date
Fri, 29 Apr 2016 11:25:12 +0200
changeset 37682
c49f5f42b10e
parent 37678
9ab6c9a03ae5
child 37683
c6d4df666069

sslconn: fix infinite wait in purple_ssl_close()

If GIOStream has some unsent data and the connection is broken
(e.g. cable unplugged), g_io_stream_close() may wait indefinitely for
its internal buffer to get empty.

Do the close anynchronously and if it doesn't finish within 15 seconds,
cancel it and free the resources held by the IO stream.

libpurple/sslconn.c file | annotate | diff | comparison | revisions
--- a/libpurple/sslconn.c	Tue May 24 08:18:34 2016 +0200
+++ b/libpurple/sslconn.c	Fri Apr 29 11:25:12 2016 +0200
@@ -28,6 +28,8 @@
 #include "sslconn.h"
 #include "tls-certificate.h"
 
+#define CONNECTION_CLOSE_TIMEOUT 15
+
 static void
 emit_error(PurpleSslConnection *gsc, int error_code)
 {
@@ -264,6 +266,31 @@
 	return (PurpleSslConnection *)gsc;
 }
 
+static void
+connection_closed_cb(GObject *stream, GAsyncResult *result,
+		gpointer timeout_id)
+{
+	GError *error = NULL;
+
+	purple_timeout_remove(GPOINTER_TO_UINT(timeout_id));
+
+	g_io_stream_close_finish(G_IO_STREAM(stream), result, &error);
+
+	if (error) {
+		purple_debug_info("sslconn", "Connection close error: %s",
+				error->message);
+		g_clear_error(&error);
+	} else {
+		purple_debug_info("sslconn", "Connection closed.");
+	}
+}
+
+static void
+cleanup_cancellable_cb(gpointer data, GObject *where_the_object_was)
+{
+	g_object_unref(G_CANCELLABLE(data));
+}
+
 void
 purple_ssl_close(PurpleSslConnection *gsc)
 {
@@ -285,10 +312,20 @@
 	}
 
 	if (gsc->conn != NULL) {
-		/* Close the stream. Shouldn't take long and it can't
-		 * be further cancelled so don't pass a cancellable
-		 */
-		g_io_stream_close(G_IO_STREAM(gsc->conn), NULL, NULL);
+		GCancellable *cancellable;
+		guint timer_id;
+
+		cancellable = g_cancellable_new();
+		g_object_weak_ref(G_OBJECT(gsc->conn), cleanup_cancellable_cb,
+				cancellable);
+
+		timer_id = purple_timeout_add_seconds(CONNECTION_CLOSE_TIMEOUT,
+				(GSourceFunc)g_cancellable_cancel, cancellable);
+
+		g_io_stream_close_async(G_IO_STREAM(gsc->conn),
+				G_PRIORITY_DEFAULT, cancellable,
+				connection_closed_cb,
+				GUINT_TO_POINTER(timer_id));
 		g_clear_object(&gsc->conn);
 	}
 

mercurial