[glib/wip/namedpipe: 6/6] Add named pipe high level api



commit abb7e78b4f87431b6eabb9dad0a5943c1ee63846
Author: Ignacio Casal Quinteiro <icq gnome org>
Date:   Mon Feb 22 12:00:17 2016 +0100

    Add named pipe high level api
    
    It provides a GSocketListener like api using named pipes
    
    https://bugzilla.gnome.org/show_bug.cgi?id=745410

 gio/Makefile.am               |    3 +
 gio/gwin32namedpipeclient.c   |  224 ++++++++++++++
 gio/gwin32namedpipeclient.h   |   76 +++++
 gio/gwin32namedpipelistener.c |  665 +++++++++++++++++++++++++++++++++++++++++
 gio/gwin32namedpipelistener.h |   83 +++++
 5 files changed, 1051 insertions(+), 0 deletions(-)
---
diff --git a/gio/Makefile.am b/gio/Makefile.am
index e911d91..d57f001 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -313,6 +313,8 @@ win32_actual_sources = \
        gwin32outputstream.c \
        gwin32outputstream.h \
        gwin32networking.h \
+       gwin32namedpipelistener.c \
+       gwin32namedpipelistener.h \
        $(NULL)
 
 win32_more_sources_for_vcproj = \
@@ -334,6 +336,7 @@ giowin32includedir=$(includedir)/gio-win32-2.0/gio
 giowin32include_HEADERS = \
        gwin32inputstream.h \
        gwin32outputstream.h \
+       gwin32namedpipelistener.h \
        $(NULL)
 
 endif
diff --git a/gio/gwin32namedpipeclient.c b/gio/gwin32namedpipeclient.c
new file mode 100644
index 0000000..273b77c
--- /dev/null
+++ b/gio/gwin32namedpipeclient.c
@@ -0,0 +1,224 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (C) 2015-2016 NICE s.r.l.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "dcv-win32-pipe-client.h"
+#include "dcv-fd-io-stream.h"
+
+#include <Windows.h>
+
+
+struct _DcvWin32PipeClient
+{
+    GObject parent_instance;
+
+    guint timeout;
+};
+
+enum
+{
+    PROP_0,
+    PROP_TIMEOUT
+};
+
+G_DEFINE_TYPE(DcvWin32PipeClient, dcv_win32_pipe_client, G_TYPE_OBJECT)
+
+static void
+dcv_win32_pipe_client_finalize(GObject *object)
+{
+    G_OBJECT_CLASS(dcv_win32_pipe_client_parent_class)->finalize(object);
+}
+
+static void
+dcv_win32_pipe_client_get_property(GObject    *object,
+                                   guint       prop_id,
+                                   GValue     *value,
+                                   GParamSpec *pspec)
+{
+    DcvWin32PipeClient *client = DCV_WIN32_PIPE_CLIENT(object);
+
+    switch (prop_id) {
+    case PROP_TIMEOUT:
+        g_value_set_uint(value, client->timeout);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+dcv_win32_pipe_client_set_property(GObject      *object,
+                                   guint         prop_id,
+                                   const GValue *value,
+                                   GParamSpec   *pspec)
+{
+    DcvWin32PipeClient *client = DCV_WIN32_PIPE_CLIENT(object);
+
+    switch (prop_id) {
+    case PROP_TIMEOUT:
+        client->timeout = g_value_get_uint(value);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+dcv_win32_pipe_client_class_init(DcvWin32PipeClientClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+    object_class->finalize = dcv_win32_pipe_client_finalize;
+    object_class->get_property = dcv_win32_pipe_client_get_property;
+    object_class->set_property = dcv_win32_pipe_client_set_property;
+
+    g_object_class_install_property(object_class,
+                                    PROP_TIMEOUT,
+                                    g_param_spec_uint("timeout",
+                                                      "Timeout",
+                                                      "The timeout in milliseconds to wait or 0 for none",
+                                                      0,
+                                                      G_MAXUINT,
+                                                      0,
+                                                      G_PARAM_READWRITE |
+                                                      G_PARAM_CONSTRUCT_ONLY |
+                                                      G_PARAM_STATIC_STRINGS));
+}
+
+static void
+dcv_win32_pipe_client_init(DcvWin32PipeClient *self)
+{
+}
+
+DcvWin32PipeClient *
+dcv_win32_pipe_client_new(void)
+{
+    return g_object_new(DCV_TYPE_WIN32_PIPE_CLIENT, NULL);
+}
+
+GIOStream *
+dcv_win32_pipe_client_connect(DcvWin32PipeClient  *client,
+                              const gchar         *pipe_name,
+                              GCancellable        *cancellable,
+                              GError             **error)
+{
+    DWORD timeout;
+    HANDLE handle = INVALID_HANDLE_VALUE;
+    GIOStream *io_stream = NULL;
+    wchar_t *pipe_namew;
+
+    g_return_val_if_fail(DCV_IS_WIN32_PIPE_CLIENT(client), NULL);
+    g_return_val_if_fail(pipe_name != NULL, NULL);
+    g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
+
+    timeout = client->timeout == 0 ? NMPWAIT_WAIT_FOREVER : client->timeout;
+
+    if (g_cancellable_set_error_if_cancelled(cancellable, error)) {
+        return NULL;
+    }
+
+    pipe_namew = g_utf8_to_utf16(pipe_name, -1, NULL, NULL, NULL);
+
+    if (WaitNamedPipeW(pipe_namew, timeout)) {
+        handle = CreateFileW(pipe_namew,
+                             GENERIC_READ | GENERIC_WRITE,
+                             0,
+                             NULL,
+                             OPEN_EXISTING,
+                             0,
+                             NULL);
+
+        if (g_cancellable_set_error_if_cancelled(cancellable, error)) {
+            goto end;
+        }
+
+        io_stream = dcv_io_stream_new_from_fd((gint)handle);
+    } else {
+        gchar *err;
+
+        err = g_win32_error_message(GetLastError());
+        g_set_error_literal(error, G_IO_ERROR, g_io_error_from_win32_error(GetLastError()),
+                            err);
+        g_free(err);
+    }
+
+end:
+    g_free(pipe_namew);
+
+    return io_stream;
+}
+
+static void
+client_connect_thread(GTask        *task,
+                      gpointer      source_object,
+                      gpointer      task_data,
+                      GCancellable *cancellable)
+{
+    DcvWin32PipeClient *client = DCV_WIN32_PIPE_CLIENT(source_object);
+    gchar *pipe_name = (gchar *)task_data;
+    GIOStream *io_stream;
+    GError *error = NULL;
+
+    io_stream = dcv_win32_pipe_client_connect(client, pipe_name,
+                                              cancellable, &error);
+
+    if (io_stream == NULL) {
+        g_task_return_error(task, error);
+    } else {
+        g_task_return_pointer(task, io_stream, (GDestroyNotify)g_object_unref);
+    }
+}
+
+void
+dcv_win32_pipe_client_connect_async(DcvWin32PipeClient  *client,
+                                    const gchar         *pipe_name,
+                                    GCancellable        *cancellable,
+                                    GAsyncReadyCallback  callback,
+                                    gpointer             user_data)
+{
+    GTask *task;
+
+    g_return_if_fail(DCV_IS_WIN32_PIPE_CLIENT(client));
+    g_return_if_fail(pipe_name != NULL);
+
+    task = g_task_new(client, cancellable, callback, user_data);
+    g_task_set_task_data(task, g_strdup(pipe_name), g_free);
+
+    g_task_run_in_thread(task, client_connect_thread);
+    g_object_unref(task);
+}
+
+GIOStream *
+dcv_win32_pipe_client_connect_finish(DcvWin32PipeClient  *client,
+                                     GAsyncResult        *result,
+                                     GError             **error)
+{
+    g_return_val_if_fail(DCV_IS_WIN32_PIPE_CLIENT(client), NULL);
+    g_return_val_if_fail(g_task_is_valid(result, client), NULL);
+
+    return g_task_propagate_pointer(G_TASK(result), error);
+}
+
+/* ex:set ts=4 et: */
diff --git a/gio/gwin32namedpipeclient.h b/gio/gwin32namedpipeclient.h
new file mode 100644
index 0000000..5b7cd64
--- /dev/null
+++ b/gio/gwin32namedpipeclient.h
@@ -0,0 +1,76 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2016 NICE s.r.l.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __G_WIN32_NAMED_PIPE_CLIENT_H__
+#define __G_WIN32_NAMED_PIPE_CLIENT_H__
+
+#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
+#error "Only <gio/gio.h> can be included directly."
+#endif
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_WIN32_NAMED_PIPE_CLIENT            (g_win32_named_pipe_client_get_type ())
+#define G_WIN32_NAMED_PIPE_CLIENT(o)              (G_TYPE_CHECK_INSTANCE_CAST ((o), 
G_TYPE_WIN32_NAMED_PIPE_CLIENT, GWin32NamedPipeClient))
+#define G_WIN32_NAMED_PIPE_CLIENT_CLASS(k)        (G_TYPE_CHECK_CLASS_CAST ((k), 
G_TYPE_WIN32_NAMED_PIPE_CLIENT, GWin32NamedPipeClientClass))
+#define G_IS_WIN32_NAMED_PIPE_CLIENT(o)           (G_TYPE_CHECK_INSTANCE_TYPE ((o), 
G_TYPE_WIN32_NAMED_PIPE_CLIENT))
+#define G_IS_WIN32_NAMED_PIPE_CLIENT_CLASS(k)     (G_TYPE_CHECK_CLASS_TYPE ((k),  
G_TYPE_WIN32_NAMED_PIPE_CLIENT))
+#define G_WIN32_NAMED_PIPE_CLIENT_GET_CLASS(o)    (G_TYPE_INSTANCE_GET_CLASS ((o), 
G_TYPE_WIN32_NAMED_PIPE_CLIENT, GWin32NamedPipeClientClass))
+
+typedef struct _GWin32NamedPipeClient                       GWin32NamedPipeClient;
+typedef struct _GWin32NamedPipeClientClass                  GWin32NamedPipeClientClass;
+
+struct _GWin32NamedPipeClientClass
+{
+  GObjectClass parent_class;
+};
+
+struct _GWin32NamedPipeClient
+{
+  GObject parent_instance;
+};
+
+GLIB_AVAILABLE_IN_2_48
+GType                       g_win32_named_pipe_client_get_type         (void) G_GNUC_CONST;
+
+GLIB_AVAILABLE_IN_2_48
+GWin32NamedPipeClient      *g_win32_named_pipe_client_new              (void);
+
+GLIB_AVAILABLE_IN_2_48
+GIOStream                  *g_win32_named_pipe_client_connect          (GWin32NamedPipeClient  *client,
+                                                                        const gchar            
*named_pipe_name,
+                                                                        GCancellable           *cancellable,
+                                                                        GError                **error);
+
+GLIB_AVAILABLE_IN_2_48
+void                        g_win32_named_pipe_client_connect_async    (GWin32NamedPipeClient  *client,
+                                                                        const gchar            
*named_pipe_name,
+                                                                        GCancellable           *cancellable,
+                                                                        GAsyncReadyCallback     callback,
+                                                                        gpointer                user_data);
+
+GLIB_AVAILABLE_IN_2_48
+GIOStream                  *g_win32_named_pipe_client_connect_finish   (GWin32NamedPipeClient  *client,
+                                                                        GAsyncResult           *result,
+                                                                        GError                **error);
+
+G_END_DECLS
+
+#endif /* __G_WIN32_NAMED_PIPE_CLIENT_H__ */
diff --git a/gio/gwin32namedpipelistener.c b/gio/gwin32namedpipelistener.c
new file mode 100644
index 0000000..66cfb34
--- /dev/null
+++ b/gio/gwin32namedpipelistener.c
@@ -0,0 +1,665 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ * Copyright (C) 2016 NICE s.r.l.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gwin32namedpipelistener.h"
+
+#include <windows.h>
+
+typedef struct
+{
+  const gchar *pipe_name;
+  HANDLE handle;
+  OVERLAPPED overlapped;
+  gboolean already_connected;
+} PipeData;
+
+struct _GWin32NamedPipeListenerPrivate
+{
+  GPtrArray *named_pipes;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GWin32NamedPipeListener, g_win32_named_pipe_listener, G_TYPE_OBJECT)
+
+static GQuark source_quark = 0;
+
+typedef struct {
+  GSource source;
+  GPollFD pollfd;
+} GWin32HandleSource;
+
+typedef gboolean (* GWin32HandleSourceFunc) (HANDLE   handle,
+                                             gpointer user_data);
+
+static gboolean
+g_win32_handle_source_prepare (GSource *source,
+                               gint    *timeout)
+{
+  *timeout = -1;
+  return FALSE;
+}
+
+static gboolean
+g_win32_handle_source_check (GSource *source)
+{
+  GWin32HandleSource *hsource = (GWin32HandleSource *)source;
+
+  return hsource->pollfd.revents;
+}
+
+static gboolean
+g_win32_handle_source_dispatch (GSource     *source,
+                                GSourceFunc  callback,
+                                gpointer     user_data)
+{
+  GWin32HandleSourceFunc func = (GWin32HandleSourceFunc)callback;
+  GWin32HandleSource *hsource = (GWin32HandleSource *)source;
+
+  return func (hsource->pollfd.fd, user_data);
+}
+
+static void
+g_win32_handle_source_finalize (GSource *source)
+{
+}
+
+static gboolean
+g_win32_handle_source_closure_callback (HANDLE   handle,
+                                        gpointer data)
+{
+  GClosure *closure = data;
+
+  GValue param = G_VALUE_INIT;
+  GValue result_value = G_VALUE_INIT;
+  gboolean result;
+
+  g_value_init (&result_value, G_TYPE_BOOLEAN);
+
+  g_value_init (&param, G_TYPE_POINTER);
+  g_value_set_pointer (&param, handle);
+
+  g_closure_invoke (closure, &result_value, 1, &param, NULL);
+
+  result = g_value_get_boolean (&result_value);
+  g_value_unset (&result_value);
+  g_value_unset (&param);
+
+  return result;
+}
+
+GSourceFuncs g_win32_handle_source_funcs = {
+  g_win32_handle_source_prepare,
+  g_win32_handle_source_check,
+  g_win32_handle_source_dispatch,
+  g_win32_handle_source_finalize,
+  (GSourceFunc)g_win32_handle_source_closure_callback,
+};
+
+static GSource *
+g_win32_handle_create_source (HANDLE        handle,
+                              GCancellable *cancellable)
+{
+  GWin32HandleSource *hsource;
+  GSource *source;
+
+  source = g_source_new (&g_win32_handle_source_funcs, sizeof (GWin32HandleSource));
+  hsource = (GWin32HandleSource *)source;
+  g_source_set_name (source, "GWin32Handle");
+
+  if (cancellable)
+    {
+      GSource *cancellable_source;
+
+      cancellable_source = g_cancellable_source_new (cancellable);
+      g_source_add_child_source (source, cancellable_source);
+      g_source_set_dummy_callback (cancellable_source);
+      g_source_unref (cancellable_source);
+    }
+
+  hsource->pollfd.fd = (gint)handle;
+  hsource->pollfd.events = G_IO_IN;
+  hsource->pollfd.revents = 0;
+  g_source_add_poll (source, &hsource->pollfd);
+
+  return source;
+}
+
+static PipeData *
+pipe_data_new (const gchar *pipe_name,
+               HANDLE       handle,
+               GObject     *source_object)
+{
+  PipeData *data;
+
+  data = g_slice_new0 (PipeData);
+  data->pipe_name = g_strdup (pipe_name);
+  data->handle = handle;
+  data->overlapped.hEvent = CreateEvent (NULL, /* default security attribute */
+                                         TRUE, /* manual-reset event */
+                                         TRUE, /* initial state = signaled */
+                                         NULL); /* unnamed event object */
+  if (source_object)
+    data->source_object = g_object_ref (source_object);
+
+  return data;
+}
+
+static void
+pipe_data_free (PipeData *data)
+{
+  g_free (data->pipe_name);
+  CloseHandle (data->handle);
+  CloseHandle (data->overlapped.hEvent);
+  g_clear_object (&data->source_object);
+  g_slice_free (PipeData, data);
+}
+
+static void
+g_win32_named_pipe_listener_finalize (GObject *object)
+{
+  GWin32NamedPipeListener *listener = G_WIN32_NAMED_PIPE_LISTENER (object);
+  GWin32NamedPipeListenerPrivate *priv;
+
+  priv = g_win32_named_pipe_listener_get_instance_private (listener);
+
+  g_ptr_array_free (priv->named_pipes, TRUE);
+
+  G_OBJECT_CLASS (g_win32_named_pipe_listener_parent_class)->finalize (object);
+}
+
+static void
+g_win32_named_pipe_listener_class_init (GWin32NamedPipeListenerClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = g_win32_named_pipe_listener_finalize;
+
+  source_quark = g_quark_from_static_string ("g-win32-named-pipe-listener-source");
+}
+
+static void
+g_win32_named_pipe_listener_init (GWin32NamedPipeListener *listener)
+{
+  GWin32NamedPipeListenerPrivate *priv;
+
+  priv = g_win32_named_pipe_listener_get_instance_private (listener);
+
+  priv->named_pipes = g_ptr_array_new_with_free_func ((GDestroyNotify) pipe_data_free);
+}
+
+/**
+ * g_win32_named_pipe_listener_new:
+ *
+ * Creates a new #GWin32NamedPipeListener.
+ *
+ * Returns: (transfer full): a new #GWin32NamedPipeListener.
+ *
+ * Since: 2.48
+ */
+GWin32NamedPipeListener *
+g_win32_named_pipe_listener_new (void)
+{
+  return g_object_new (G_TYPE_WIN32_NAMED_PIPE_LISTENER, NULL);
+}
+
+/**
+ * g_win32_named_pipe_listener_add_named_pipe:
+ * @listener: a #GWin32NamedPipeListener.
+ * @pipe_name: a name for the pipe.
+ * @source_object: (allow-none): Optional #GObject identifying this source
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Adds @named_pipe to the set of named pipes that we try to accept clients
+ * from.
+ *
+ * @source_object will be passed out in the various calls
+ * to accept to identify this particular source, which is
+ * useful if you're listening on multiple pipes and do
+ * different things depending on what pipe is connected to.
+ *
+ * Returns: %TRUE on success, %FALSE on error.
+ *
+ * Since: 2.48
+ */
+gboolean
+g_win32_named_pipe_listener_add_named_pipe (GWin32NamedPipeListener  *listener,
+                                            const gchar              *pipe_name,
+                                            GObject                  *source_object,
+                                            GError                  **error)
+{
+  GWin32NamedPipeListenerPrivate *priv;
+  gunichar2 *pipe_namew;
+  PipeData *pipe_data;
+  HANDLE handle;
+
+  g_return_if_fail (G_IS_WIN32_NAMED_PIPE_LISTENER (listener));
+  g_return_if_fail (G_IS_WIN32_NAMED_PIPE (named_pipe));
+
+  priv = g_win32_named_pipe_listener_get_instance_private (listener);
+
+  pipe_namew = g_utf8_to_utf16 (pipe_name, -1, NULL, NULL, NULL);
+
+  handle = CreateNamedPipeW (pipe_namew,
+                             PIPE_ACCESS_DUPLEX |
+                             FILE_FLAG_OVERLAPPED,
+                             PIPE_TYPE_BYTE |
+                             PIPE_READMODE_BYTE |
+                             PIPE_WAIT,
+                             PIPE_UNLIMITED_INSTANCES,
+                             DEFAULT_PIPE_BUF_SIZE,
+                             DEFAULT_PIPE_BUF_SIZE,
+                             0, NULL);
+  g_free (pipe_namew);
+
+  if (handle == INVALID_HANDLE_VALUE)
+    {
+      int errsv = GetLastError ();
+      gchar *emsg = g_win32_error_message (errsv);
+
+      g_set_error (error,
+                   G_IO_ERROR,
+                   g_io_error_from_win32_error (errsv),
+                   _("Error creating named pipe '%s': %s"),
+                   pipe_name, emsg);
+
+      g_free (emsg);
+      return FALSE;
+    }
+
+  data = pipe_data_new (pipe_name, handle, source_object);
+
+  if (!ConnectNamedPipe (handle, &data->overlapped))
+    {
+      switch (GetLastError ())
+      {
+      case ERROR_IO_PENDING:
+        break;
+      case ERROR_PIPE_CONNECTED:
+        data->already_connected = TRUE;
+        break;
+      default:
+        {
+          int errsv = GetLastError ();
+          gchar *emsg = g_win32_error_message (errsv);
+
+          g_task_return_new_error (G_OBJECT (listener),
+                                   callback, user_data,
+                                   G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                                   _("Failed to connect named pipe '%s': %s"),
+                                   pipe_name, emsg);
+          g_free (emsg);
+          pipe_data_free (data);
+
+          return FALSE;
+        }
+      }
+    }
+
+  g_ptr_array_add (priv->named_pipes, data);
+
+  return TRUE;
+}
+
+static GIOStream *
+io_stream_new_from_handle (HANDLE handle)
+{
+  GIOStream *io_stream;
+  GInputStream *in;
+  GOutputStream *out;
+
+  in = g_win32_input_stream_new (handle, FALSE);
+  out = g_win32_output_stream_new (handle, FALSE);
+
+  io_stream = g_simple_io_stream_new (in, out);
+  g_object_unref (in);
+  g_object_unref (out);
+
+  return io_stream;
+}
+
+static gboolean
+connect_ready (HANDLE   handle,
+               gpointer user_data)
+{
+  GTask *task = user_data;
+  GWin32NamedPipeListenerPrivate *priv;
+  PipeData *pipe_data = NULL;
+  gulong cbret;
+  int i;
+
+  priv = g_win32_named_pipe_listener_get_instance_private (listener);
+
+  for (i = 0; i < priv->named_pipes->len; i++)
+    {
+      PipeData *pdata;
+
+      pdata = priv->named_pipes->pdata[i];
+      if (pdata->overlapped.hEvent == handle)
+        {
+          pipe_data = pdata;
+          break;
+        }
+    }
+
+  g_return_val_if_fail (pipe_data != NULL, FALSE);
+
+  if (GetOverlappedResult (pipe_data->handle, &pipe_data->overlapped, &cbret, FALSE))
+    {
+      int errsv = GetLastError ();
+      gchar *emsg = g_win32_error_message (errsv);
+
+      g_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_INVALID_ARGUMENT,
+                               _("There was an error querying the named pipe: %s"),
+                               emsg);
+      g_free (emsg);
+    }
+  else
+    {
+      GIOStream *io_stream;
+
+      if (pipe_data->source_object != NULL)
+        g_object_set_qdata_full (G_OBJECT (task),
+                                 source_quark,
+                                 g_object_ref (pipe_data->source_object),
+                                 g_object_unref);
+
+      io_stream = io_stream_new_from_handle (pipe_data->handle);
+      g_task_return_pointer (task, io_stream, g_object_unref);
+    }
+
+  g_object_unref (task);
+
+  return FALSE;
+}
+
+static GList *
+add_sources (GWin32NamedPipeListener *listener,
+             GSourceFunc              callback,
+             gpointer                 callback_data,
+             GCancellable            *cancellable,
+             GMainContext            *context,
+             GPtrArray               *sources)
+{
+  GWin32NamedPipeListenerPrivate *priv;
+  PipeData *data;
+  GSource *source;
+  GList *sources;
+  int i;
+
+  priv = g_win32_named_pipe_listener_get_instance_private (listener);
+
+  sources = NULL;
+  for (i = 0; i < priv->named_pipes->len; i++)
+    {
+      data = priv->named_pipes->pdata[i];
+
+      source = g_win32_handle_create_source (data->overlapped.hEvent,
+                                             cancellable);
+      g_source_set_callback (source,
+                             (GSourceFunc) callback,
+                             callback_data, NULL);
+      g_source_attach (source, context);
+
+      sources = g_list_prepend (sources, source);
+    }
+
+  return sources;
+}
+
+static void
+free_sources (GList *sources)
+{
+  GSource *source;
+  while (sources != NULL)
+    {
+      source = sources->data;
+      sources = g_list_delete_link (sources, sources);
+      g_source_destroy (source);
+      g_source_unref (source);
+    }
+}
+
+struct AcceptData {
+  GWin32NamedPipeListener *listener;
+  GMainLoop *loop;
+  PipeData *pipe_data;
+};
+
+static gboolean
+accept_callback (HANDLE   handle,
+                 gpointer user_data)
+{
+  struct AcceptData *data = user_data;
+  GWin32NamedPipeListenerPrivate *priv;
+  PipeData *pipe_data = NULL;
+  int i;
+
+  priv = g_win32_named_pipe_listener_get_instance_private (listener);
+
+  for (i = 0; i < priv->named_pipes->len; i++)
+    {
+      PipeData *pdata;
+
+      pdata = priv->named_pipes->pdata[i];
+      if (pdata->overlapped.hEvent == handle)
+        {
+          pipe_data = pdata;
+          break;
+        }
+    }
+
+  data->pipe_data = pipe_data;
+  g_main_loop_quit (data->loop);
+
+  return TRUE;
+}
+
+/**
+ * g_win32_named_pipe_listener_accept:
+ * @listener: a #GWin32NamedPipeListener
+ * @source_object: (out) (transfer none) (allow-none): location where #GObject pointer will be stored, or 
%NULL.
+ * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore.
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Blocks waiting for a client to connect to any of the named pipes added
+ * to the listener. Returns the #GIOStream that was accepted.
+ *
+ * If @source_object is not %NULL it will be filled out with the source
+ * object specified when the corresponding named pipe was added
+ * to the listener.
+ *
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
+ *
+ * Returns: (transfer full): a #GIOStream on success, %NULL on error.
+ *
+ * Since: 2.48
+ */
+GIOStream *
+g_win32_named_pipe_listener_accept (GWin32NamedPipeListener  *listener,
+                                    GObject                 **source_object,
+                                    GCancellable             *cancellable,
+                                    GError                  **error)
+{
+  GWin32NamedPipeListenerPrivate *priv;
+  PipeData *pipe_data = NULL;
+  GIOStream *io_stream = NULL;
+
+  g_return_val_if_fail (G_IS_WIN32_NAMED_PIPE_LISTENER (listener), NULL);
+
+  priv = g_win32_named_pipe_listener_get_instance_private (listener);
+
+  if (priv->named_pipes->len == 1)
+    {
+      gboolean success;
+
+      pipe_data = priv->named_pipes->pdata[0];
+      success = data->already_connected;
+
+      if (!success)
+        success = WaitForSingleObject (pipe_data->overlapped.hEvent, INFINITE) == WAIT_OBJECT_0;
+
+      if (!success)
+        pipe_data = NULL;
+    }
+  else
+    {
+      int i;
+
+      /* First we check if any of the named pipes is already connected and
+       * pick the the first one.
+       */
+      for (i = 0; i < priv->named_pipes->len; i++)
+        {
+          PipeData *pdata = priv->named_pipes->pdata[i];
+
+          if (pdata->already_connected)
+            pipe_data = pdata;
+        }
+
+      if (pipe_data == NULL)
+        {
+          GList *sources;
+          struct AcceptData data;
+          GMainLoop *loop;
+
+          if (priv->main_context == NULL)
+            priv->main_context = g_main_context_new ();
+
+          loop = g_main_loop_new (priv->main_context, FALSE);
+          data.loop = loop;
+          data.listener = listener;
+
+          sources = add_sources (listener,
+                                 accept_callback,
+                                 &data,
+                                 cancellable,
+                                 priv->main_context);
+          g_main_loop_run (loop);
+          pipe_data = data.pipe_data;
+          free_sources (sources);
+          g_main_loop_unref (loop);
+        }
+    }
+
+  if (pipe_data != NULL)
+    {
+      io_stream = io_stream_new_from_handle (pipe_data->handle);
+
+      if (source_object)
+        *source_object = pipe_data->source_object;
+    }
+
+  return io_stream;
+}
+
+/**
+ * g_win32_named_pipe_listener_accept_async:
+ * @listener: a #GWin32NamedPipeListener
+ * @cancellable: (allow-none): a #GCancellable, or %NULL
+ * @callback: (scope async): a #GAsyncReadyCallback
+ * @user_data: (closure): user data for the callback
+ *
+ * This is the asynchronous version of g_win32_named_pipe_listener_accept().
+ *
+ * When the operation is finished @callback will be
+ * called. You can then call g_win32_named_pipe_listener_accept_finish()
+ * to get the result of the operation.
+ *
+ * Since: 2.48
+ */
+void
+g_win32_named_pipe_listener_accept_async (GWin32NamedPipeListener *listener,
+                                          GCancellable            *cancellable,
+                                          GAsyncReadyCallback      callback,
+                                          gpointer                 user_data)
+{
+  GWin32NamedPipeListenerPrivate *priv;
+  PipeData *pipe_data;
+  GTask *task;
+  GList *sources;
+  int i;
+
+  task = g_task_new (listener, cancellable, callback, user_data);
+
+  priv = g_win32_named_pipe_listener_get_instance_private (listener);
+
+  /* First we check if any of the named pipes is already connected and pick the
+   * the first one.
+   */
+  for (i = 0; i < priv->named_pipes->len; i++)
+    {
+      pipe_data = priv->named_pipes->pdata[i];
+
+      if (pipe_data->already_connected)
+        {
+          GIOStream *io_stream;
+
+          if (pipe_data->source_object)
+            g_object_set_qdata_full (G_OBJECT (task),
+                                     source_quark,
+                                     g_object_ref (pipe_data->source_object),
+                                     g_object_unref);
+
+          io_stream = io_stream_new_from_handle (pipe_data->handle);
+          g_task_return_pointer (task, io_stream, g_object_unref);
+
+          return;
+        }
+    }
+
+  sources = add_sources (listener,
+                         connect_ready,
+                         task,
+                         cancellable,
+                         g_main_context_get_thread_default ());
+  g_task_set_task_data (task, sources, (GDestroyNotify) free_sources);
+}
+
+/**
+ * g_win32_named_pipe_listener_accept_finish:
+ * @listener: a #GWin32NamedPipeListener.
+ * @result: a #GAsyncResult.
+ * @source_object: (out) (transfer none) (allow-none): Optional #GObject identifying this source
+ * @error: a #GError location to store the error occurring, or %NULL to ignore.
+ *
+ * Finishes an async accept operation. See g_win32_named_pipe_listener_accept_async()
+ *
+ * Returns: (transfer full): a #GIOStream on success, %NULL on error.
+ *
+ * Since: 2.48
+ */
+GIOStream *
+g_win32_named_pipe_listener_accept_finish (GWin32NamedPipeListener  *listener,
+                                           GAsyncResult             *result,
+                                           GObject                 **source_object,
+                                           GError                  **error)
+{
+  g_return_val_if_fail (G_IS_WIN32_NAMED_PIPE_LISTENER (listener), NULL);
+  g_return_val_if_fail (g_task_is_valid (result, listener), NULL);
+
+  if (source_object)
+    *source_object = g_object_get_qdata (G_OBJECT (result), source_quark);
+
+  return g_task_propagate_pointer (G_TASK (result), error);
+}
diff --git a/gio/gwin32namedpipelistener.h b/gio/gwin32namedpipelistener.h
new file mode 100644
index 0000000..9e3a1d4
--- /dev/null
+++ b/gio/gwin32namedpipelistener.h
@@ -0,0 +1,83 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ * Copyright (C) 2016 NICE s.r.l.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __G_WIN32_NAMED_PIPE_LISTENER_H__
+#define __G_WIN32_NAMED_PIPE_LISTENER_H__
+
+#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
+#error "Only <gio/gio.h> can be included directly."
+#endif
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_WIN32_NAMED_PIPE_LISTENER            (g_win32_named_pipe_listener_get_type ())
+#define G_WIN32_NAMED_PIPE_LISTENER(o)              (G_TYPE_CHECK_INSTANCE_CAST ((o), 
G_TYPE_WIN32_NAMED_PIPE_LISTENER, GWin32NamedPipeListener))
+#define G_WIN32_NAMED_PIPE_LISTENER_CLASS(k)        (G_TYPE_CHECK_CLASS_CAST ((k), 
G_TYPE_WIN32_NAMED_PIPE_LISTENER, GWin32NamedPipeListenerClass))
+#define G_IS_WIN32_NAMED_PIPE_LISTENER(o)           (G_TYPE_CHECK_INSTANCE_TYPE ((o), 
G_TYPE_WIN32_NAMED_PIPE_LISTENER))
+#define G_IS_WIN32_NAMED_PIPE_LISTENER_CLASS(k)     (G_TYPE_CHECK_CLASS_TYPE ((k),  
G_TYPE_WIN32_NAMED_PIPE_LISTENER))
+#define G_WIN32_NAMED_PIPE_LISTENER_GET_CLASS(o)    (G_TYPE_INSTANCE_GET_CLASS ((o), 
G_TYPE_WIN32_NAMED_PIPE_LISTENER, GWin32NamedPipeListenerClass))
+
+typedef struct _GWin32NamedPipeListener                       GWin32NamedPipeListener;
+typedef struct _GWin32NamedPipeListenerClass                  GWin32NamedPipeListenerClass;
+
+struct _GWin32NamedPipeListenerClass
+{
+  GObjectClass parent_class;
+};
+
+struct _GWin32NamedPipeListener
+{
+  GObject parent_instance;
+};
+
+GLIB_AVAILABLE_IN_2_48
+GType                       g_win32_named_pipe_listener_get_type       (void) G_GNUC_CONST;
+
+GLIB_AVAILABLE_IN_2_48
+GWin32NamedPipeListener    *g_win32_named_pipe_listener_new            (void);
+
+GLIB_AVAILABLE_IN_2_48
+gboolean                    g_win32_named_pipe_listener_add_named_pipe (GWin32NamedPipeListener  *listener,
+                                                                        const gchar              *pipe_name,
+                                                                        GObject                  
*source_object,
+                                                                        GError                  **error);
+
+GLIB_AVAILABLE_IN_2_48
+GIOStream                  *g_win32_named_pipe_listener_accept         (GWin32NamedPipeListener  *listener,
+                                                                        GObject                 
**source_object,
+                                                                        GCancellable             
*cancellable,
+                                                                        GError                  **error);
+
+GLIB_AVAILABLE_IN_2_48
+void                        g_win32_named_pipe_listener_accept_async   (GWin32NamedPipeListener  *listener,
+                                                                        GCancellable             
*cancellable,
+                                                                        GAsyncReadyCallback       callback,
+                                                                        gpointer                  user_data);
+
+GLIB_AVAILABLE_IN_2_48
+GIOStream                  *g_win32_named_pipe_listener_accept_finish  (GWin32NamedPipeListener  *listener,
+                                                                        GAsyncResult             *result,
+                                                                        GObject                 
**source_object,
+                                                                        GError                  **error);
+
+G_END_DECLS
+
+#endif /* __G_WIN32_NAMED_PIPE_LISTENER_H__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]