[gvfs/wip/alexl/documents] Initial version to do simple read and replace from xdg-document-portal



commit 9b2439d22870baf33b6d87191202339db41f60f8
Author: Alexander Larsson <alexl redhat com>
Date:   Thu Apr 16 21:00:21 2015 +0200

    Initial version to do simple read and replace from xdg-document-portal
    
    For example:
    $ echo foo | gvfs-save document:1
    $ gvfs-cat document:1
    foo
    $ echo bar | gvfs-save document:1
    $ gvfs-cat document:1
    bar

 client/Makefile.am                |    3 +
 client/gdaemonvfs.c               |   12 +
 client/gvfsdocumentfile.c         |  691 +++++++++++++++++++++++++++++++++++++
 client/gvfsdocumentfile.h         |   58 +++
 client/gvfsdocumentinputstream.c  |  311 +++++++++++++++++
 client/gvfsdocumentinputstream.h  |   39 ++
 client/gvfsdocumentoutputstream.c |  426 +++++++++++++++++++++++
 client/gvfsdocumentoutputstream.h |   47 +++
 8 files changed, 1587 insertions(+), 0 deletions(-)
---
diff --git a/client/Makefile.am b/client/Makefile.am
index 8c7b76a..ce9c548 100644
--- a/client/Makefile.am
+++ b/client/Makefile.am
@@ -39,6 +39,9 @@ vfssources = \
        gvfsiconloadable.c gvfsiconloadable.h \
        gvfsuriutils.c gvfsuriutils.h \
        gvfsurimapper.c gvfsurimapper.h \
+       gvfsdocumentfile.c gvfsdocumentfile.h \
+       gvfsdocumentinputstream.c gvfsdocumentinputstream.h \
+       gvfsdocumentoutputstream.c gvfsdocumentoutputstream.h \
        $(URI_PARSER_SOURCES) \
        $(NULL)
 
diff --git a/client/gdaemonvfs.c b/client/gdaemonvfs.c
index 19e65b1..dfca085 100644
--- a/client/gdaemonvfs.c
+++ b/client/gdaemonvfs.c
@@ -29,6 +29,7 @@
 #include "gdaemonvfs.h"
 #include "gvfsuriutils.h"
 #include "gdaemonfile.h"
+#include "gvfsdocumentfile.h"
 #include <gio/gio.h>
 #include <gvfsdaemonprotocol.h>
 #include <gmodule.h>
@@ -410,6 +411,17 @@ g_daemon_vfs_get_file_for_uri (GVfs       *vfs,
       g_free (path);
       return file;
     }
+
+  if (g_ascii_strncasecmp (uri, "document:", 9) == 0)
+    {
+      file = gvfs_document_file_new (uri);
+
+      if (file == NULL)
+       /* Dummy file */
+       return g_vfs_get_file_for_uri (G_DAEMON_VFS (vfs)->wrapped_vfs, uri);
+
+      return file;
+    }
   
   if (get_mountspec_from_uri (daemon_vfs, uri, &spec, &path))
     {
diff --git a/client/gvfsdocumentfile.c b/client/gvfsdocumentfile.c
new file mode 100644
index 0000000..a38d997
--- /dev/null
+++ b/client/gvfsdocumentfile.c
@@ -0,0 +1,691 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * 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 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, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: Alexander Larsson <alexl redhat com>
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "gvfsdocumentfile.h"
+#include "gvfsdocumentinputstream.h"
+#include "gvfsdocumentoutputstream.h"
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+#include <gvfsdbus.h>
+#include <gio/gunixfdlist.h>
+
+static void gvfs_document_file_file_iface_init (GFileIface       *iface);
+
+static void gvfs_document_file_read_async (GFile *file,
+                                          int io_priority,
+                                          GCancellable *cancellable,
+                                          GAsyncReadyCallback callback,
+                                          gpointer callback_data);
+
+G_DEFINE_TYPE_WITH_CODE (GVfsDocumentFile, gvfs_document_file, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_FILE,
+                                               gvfs_document_file_file_iface_init))
+
+static void
+gvfs_document_file_finalize (GObject *object)
+{
+  GVfsDocumentFile *doc;
+
+  doc = GVFS_DOCUMENT_FILE (object);
+
+  g_free (doc->path);
+  
+  if (G_OBJECT_CLASS (gvfs_document_file_parent_class)->finalize)
+    (*G_OBJECT_CLASS (gvfs_document_file_parent_class)->finalize) (object);
+}
+
+static void
+gvfs_document_file_class_init (GVfsDocumentFileClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  
+  gobject_class->finalize = gvfs_document_file_finalize;
+}
+
+static void
+gvfs_document_file_init (GVfsDocumentFile *doc)
+{
+}
+
+static char *
+canonicalize_path (char *path)
+{
+  char *p, *q;
+
+  /* Canonicalize multiple consecutive slashes */
+  p = path;
+  while (*p != 0)
+    {
+      q = p;
+      while (*q && *q == '/')
+       q++;
+
+      if (q > p + 1)
+       memmove (p+1, q, strlen (q)+1);
+
+      /* Skip over the one separator */
+      p++;
+
+      /* Drop trailing (not first) slash */
+      if (*p == 0 && p > path + 1)
+       {
+         p--;
+         *p = 0;
+       }
+      
+      /* Skip until next separator */
+      while (*p != 0 && *p != '/')
+       p++;
+    }
+
+  return path;
+}
+
+static char *
+path_from_uri (const char *uri)
+{
+  char *to_free = NULL;
+  char *path, *res, *p, *q;
+  const char *path_part, *hash;
+  int len = -1;
+
+  path_part = uri + strlen ("document:");
+
+  if (g_str_has_prefix (path_part, "///")) 
+    path_part += 2;
+  else if (g_str_has_prefix (path_part, "//"))
+    return NULL; /* Has hostname, not valid */
+
+  hash = strchr (path_part, '#');
+  if (hash != NULL)
+    {
+      len = hash - path_part;
+      path_part = to_free = g_strndup (path_part, len);
+    }
+
+  res = g_uri_unescape_string (path_part, "/");
+
+  g_clear_pointer (&to_free, g_free);
+
+  if (res == NULL)
+    return NULL;
+
+  if (*res != '/')
+    {
+      to_free = res;
+      res = g_strconcat ("/", res, NULL);
+      g_free (to_free);
+    }
+
+  return canonicalize_path (res);
+}
+
+/* Takes ownership of path */
+static GFile *
+gvfs_document_file_new_steals_path (char *path)
+{
+  GVfsDocumentFile *doc;
+  
+  doc = g_object_new (GVFS_TYPE_DOCUMENT_FILE, NULL);
+  doc->path = path;
+ 
+  return G_FILE (doc);
+}
+
+GFile *
+gvfs_document_file_new (const char *uri)
+{
+  GVfsDocumentFile *doc;
+  char *path;
+
+  path = path_from_uri (uri);
+
+  if (path == NULL)
+    return NULL; /* Creates a dummy GFile */
+
+  return gvfs_document_file_new_steals_path (path);
+}
+
+static gboolean
+gvfs_document_file_is_native (GFile *file)
+{
+  return FALSE;
+}
+
+static gboolean
+gvfs_document_file_has_uri_scheme (GFile *file,
+                                  const char *uri_scheme)
+{
+  GVfsDocumentFile *doc = GVFS_DOCUMENT_FILE (file);
+
+  return g_ascii_strcasecmp ("document", uri_scheme) == 0;
+}
+
+static char *
+gvfs_document_file_get_uri_scheme (GFile *file)
+{
+  GVfsDocumentFile *doc = GVFS_DOCUMENT_FILE (file);
+  const char *scheme;
+
+  return g_strdup ("document");
+}
+
+static char *
+gvfs_document_file_get_basename (GFile *file)
+{
+  GVfsDocumentFile *doc = GVFS_DOCUMENT_FILE (file);
+  char *last_slash;    
+
+  return g_path_get_basename (doc->path);
+}
+
+static char *
+gvfs_document_file_get_path (GFile *file)
+{
+  return NULL;
+}
+
+static char *
+gvfs_document_file_get_uri (GFile *file)
+{
+  GVfsDocumentFile *doc = GVFS_DOCUMENT_FILE (file);
+  char *res, *escaped_path;
+
+  escaped_path = g_uri_escape_string (doc->path,
+                                     G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE);
+  res = g_strconcat ("document://", escaped_path, NULL);
+  g_free (escaped_path);
+
+  return res;
+}
+
+static char *
+gvfs_document_file_get_parse_name (GFile *file)
+{
+  GVfsDocumentFile *doc = GVFS_DOCUMENT_FILE (file);
+
+  return gvfs_document_file_get_uri (file);
+}
+
+static GFile *
+gvfs_document_file_get_parent (GFile *file)
+{
+  GVfsDocumentFile *doc = GVFS_DOCUMENT_FILE (file);
+  GVfsDocumentFile *parent;
+  char *dirname;
+
+  if (strcmp (doc->path, "/"))
+    return NULL;
+
+  dirname = g_path_get_dirname (doc->path);
+
+  return gvfs_document_file_new_steals_path (dirname);
+}
+
+static GFile *
+gvfs_document_file_dup (GFile *file)
+{
+  GVfsDocumentFile *doc = GVFS_DOCUMENT_FILE (file);
+
+  return gvfs_document_file_new_steals_path (g_strdup (doc->path));
+}
+
+static guint
+gvfs_document_file_hash (GFile *file)
+{
+  GVfsDocumentFile *doc = GVFS_DOCUMENT_FILE (file);
+
+  return g_str_hash (doc->path);
+}
+
+static gboolean
+gvfs_document_file_equal (GFile *file1,
+                         GFile *file2)
+{
+  GVfsDocumentFile *doc1 = GVFS_DOCUMENT_FILE (file1);
+  GVfsDocumentFile *doc2 = GVFS_DOCUMENT_FILE (file2);
+
+  return g_str_equal (doc1->path, doc2->path);
+}
+
+static const char *
+match_prefix (const char *path, 
+              const char *prefix)
+{
+  int prefix_len;
+
+  prefix_len = strlen (prefix);
+  if (strncmp (path, prefix, prefix_len) != 0)
+    return NULL;
+  
+  /* Handle the case where prefix is the root, so that
+   * the IS_DIR_SEPRARATOR check below works */
+  if (prefix_len > 0 &&
+      prefix[prefix_len-1] == '/')
+    prefix_len--;
+  
+  return path + prefix_len;
+}
+
+static gboolean
+gvfs_document_file_prefix_matches (GFile *parent,
+                                  GFile *descendant)
+{
+  GVfsDocumentFile *parent_doc = GVFS_DOCUMENT_FILE (parent);
+  GVfsDocumentFile *descendant_doc = GVFS_DOCUMENT_FILE (descendant);
+  const char *remainder;
+
+  remainder = match_prefix (descendant_doc->path, parent_doc->path);
+  if (remainder != NULL && *remainder == '/')
+    return TRUE;
+  return FALSE;
+}
+
+static char *
+gvfs_document_file_get_relative_path (GFile *parent,
+                                     GFile *descendant)
+{
+  GVfsDocumentFile *parent_doc = GVFS_DOCUMENT_FILE (parent);
+  GVfsDocumentFile *descendant_doc = GVFS_DOCUMENT_FILE (descendant);
+  const char *remainder;
+
+  remainder = match_prefix (descendant_doc->path, parent_doc->path);
+  if (remainder != NULL && *remainder == '/')
+    return g_strdup (remainder + 1);
+  return NULL;
+}
+
+static GFile *
+gvfs_document_file_resolve_relative_path (GFile *file,
+                                         const char *relative_path)
+{
+  GVfsDocumentFile *doc = GVFS_DOCUMENT_FILE (file);
+  char *filename;
+  GFile *child;
+
+  if (g_path_is_absolute (relative_path))
+    return gvfs_document_file_new_steals_path (canonicalize_path (g_strdup (doc->path)));
+  
+  filename = g_build_filename (doc->path, relative_path, NULL);
+  child = gvfs_document_file_new_steals_path (canonicalize_path (filename));
+  g_free (filename);
+  
+  return child;
+}
+
+static GFileEnumerator *
+gvfs_document_file_enumerate_children (GFile      *file,
+                                      const char *attributes,
+                                      GFileQueryInfoFlags flags,
+                                      GCancellable *cancellable,
+                                      GError **error)
+{
+  return NULL;
+}
+
+static GFileInfo *
+gvfs_document_file_query_info (GFile                *file,
+                              const char           *attributes,
+                              GFileQueryInfoFlags   flags,
+                              GCancellable         *cancellable,
+                              GError              **error)
+{
+  return NULL;
+}
+
+static void
+gvfs_document_file_query_info_async (GFile                      *file,
+                                    const char                 *attributes,
+                                    GFileQueryInfoFlags         flags,
+                                    int                         io_priority,
+                                    GCancellable               *cancellable,
+                                    GAsyncReadyCallback         callback,
+                                    gpointer                    user_data)
+{
+}
+
+static GFileInfo *
+gvfs_document_file_query_info_finish (GFile                      *file,
+                                     GAsyncResult               *res,
+                                     GError                    **error)
+{
+  return NULL;
+}
+
+static void
+gvfs_document_file_read_async (GFile *file,
+                              int io_priority,
+                              GCancellable *cancellable,
+                              GAsyncReadyCallback callback,
+                              gpointer callback_data)
+{
+}
+
+static GFileInputStream *
+gvfs_document_file_read_finish (GFile                  *file,
+                               GAsyncResult           *res,
+                               GError                **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  gpointer op;
+
+  op = g_simple_async_result_get_op_res_gpointer (simple);
+  if (op)
+    return g_object_ref (op);
+  
+  return NULL;
+}
+
+static gboolean
+verify_file_path (GVfsDocumentFile *doc,
+                 GError **error)
+{
+  if (strcmp (doc->path, "/") == 0)
+    {
+      g_set_error_literal (error, G_IO_ERROR,
+                          G_IO_ERROR_IS_DIRECTORY,
+                          _("Can't open directory"));
+      return FALSE;
+    }
+
+  if (strchr (doc->path + 1, '/') != NULL)
+    {
+      g_set_error_literal (error, G_IO_ERROR,
+                          G_IO_ERROR_NOT_FOUND,
+                          _("No such file"));
+      return FALSE;
+    }
+
+  return TRUE;
+
+}
+
+static GVariant *
+sync_document_call (GVfsDocumentFile *doc,
+                   const char *method,
+                   GVariant *parameters,
+                   const GVariantType  *reply_type,
+                   GUnixFDList  **out_fd_list,
+                   GCancellable *cancellable,
+                   GError **error)
+{
+  GVariant *res;
+  GDBusConnection *bus;
+  GUnixFDList *ret_fd_list;
+  GVariant *reply, *fd_v;
+  char *path;
+  int handle, fd;
+
+  if (!verify_file_path (doc, error))
+    return NULL;
+ 
+  bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
+  if (bus == NULL)
+    return NULL;
+
+  path = g_build_filename ("/org/freedesktop/portal/document", doc->path, NULL);
+
+  res = g_dbus_connection_call_with_unix_fd_list_sync (bus,
+                                                      "org.freedesktop.portal.DocumentPortal",
+                                                      path,
+                                                      "org.freedesktop.portal.Document",
+                                                      method,
+                                                      parameters, reply_type,
+                                                      G_DBUS_CALL_FLAGS_NONE,
+                                                      -1,
+                                                      NULL,
+                                                      out_fd_list,
+                                                      cancellable,
+                                                      error);
+
+  g_object_unref (bus);
+  return res;
+}
+
+static GFileInputStream *
+gvfs_document_file_read (GFile *file,
+                        GCancellable *cancellable,
+                        GError **error)
+{
+  GVfsDocumentFile *doc = GVFS_DOCUMENT_FILE (file);
+  GDBusConnection *bus;
+  GUnixFDList *ret_fd_list;
+  GVariant *reply, *fd_v;
+  char *path;
+  int handle, fd;
+
+  reply = sync_document_call (doc, "Read",
+                             g_variant_new ("()"),
+                             G_VARIANT_TYPE("(h)"),
+                             &ret_fd_list,
+                             cancellable, error);
+  if (reply == NULL)
+    return NULL;
+
+  if (ret_fd_list == NULL)
+    {
+      g_variant_unref (reply);
+      g_set_error_literal (error, G_IO_ERROR,
+                          G_IO_ERROR_FAILED,
+                          _("No file descriptor returned"));
+      return NULL;
+    }
+      
+  g_variant_get (reply, "(@h)", &fd_v);
+  handle = g_variant_get_handle (fd_v);
+  g_variant_unref (reply);
+
+  fd = g_unix_fd_list_get (ret_fd_list, handle, error);
+  g_object_unref (ret_fd_list);
+  if (fd == -1)
+    return NULL;
+
+  return _gvfs_document_input_stream_new (fd);
+}
+
+static GFileOutputStream *
+gvfs_document_file_create (GFile *file,
+                          GFileCreateFlags flags,
+                          GCancellable *cancellable,
+                          GError **error)
+{
+  return NULL;
+}
+
+static GFileOutputStream *
+gvfs_document_file_replace (GFile *file,
+                           const char *etag,
+                           gboolean make_backup,
+                           GFileCreateFlags flags,
+                           GCancellable *cancellable,
+                           GError **error)
+{
+  GVfsDocumentFile *doc = GVFS_DOCUMENT_FILE (file);
+  GDBusConnection *bus;
+  GUnixFDList *ret_fd_list;
+  GVariant *reply, *fd_v;
+  char *path;
+  int handle, fd;
+  guint32 id;
+  char *flags_array[] = {
+    NULL
+  };
+
+  reply = sync_document_call (doc, "PrepareUpdate",
+                             g_variant_new ("(s^as)",
+                                            etag ? etag : "",
+                                            flags_array),
+                             G_VARIANT_TYPE("(uh)"),
+                             &ret_fd_list,
+                             cancellable, error);
+  if (reply == NULL)
+    return NULL;
+
+  if (ret_fd_list == NULL)
+    {
+      g_variant_unref (reply);
+      g_set_error_literal (error, G_IO_ERROR,
+                          G_IO_ERROR_FAILED,
+                          _("No file descriptor returned"));
+      return NULL;
+    }
+      
+  g_variant_get (reply, "(u h)", &id, &fd_v);
+  handle = g_variant_get_handle (fd_v);
+  g_variant_unref (reply);
+
+  fd = g_unix_fd_list_get (ret_fd_list, handle, error);
+  g_object_unref (ret_fd_list);
+  if (fd == -1)
+    return NULL;
+
+  return gvfs_document_output_stream_new (doc->path, id, fd);
+}
+
+static void
+gvfs_document_file_create_async (GFile                      *file,
+                                GFileCreateFlags            flags,
+                                int                         io_priority,
+                                GCancellable               *cancellable,
+                                GAsyncReadyCallback         callback,
+                                gpointer                    user_data)
+{
+  file_open_write_async (file,
+                         0, "", FALSE, flags, io_priority,
+                         cancellable,
+                         callback, user_data);
+}
+
+static GFileOutputStream *
+gvfs_document_file_create_finish (GFile                      *file,
+                                 GAsyncResult               *res,
+                                 GError                    **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  GFileOutputStream *output_stream;
+
+  output_stream = g_simple_async_result_get_op_res_gpointer (simple);
+  if (output_stream)
+    return g_object_ref (output_stream);
+
+  return NULL;
+}
+
+static void
+gvfs_document_file_enumerate_children_async (GFile                      *file,
+                                        const char                 *attributes,
+                                        GFileQueryInfoFlags         flags,
+                                        int                         io_priority,
+                                        GCancellable               *cancellable,
+                                        GAsyncReadyCallback         callback,
+                                        gpointer                    user_data)
+{
+}
+
+static GFileEnumerator *
+gvfs_document_file_enumerate_children_finish (GFile                      *file,
+                                         GAsyncResult               *res,
+                                         GError                    **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  GFileEnumerator *enumerator;
+
+  enumerator = g_simple_async_result_get_op_res_gpointer (simple);
+  if (enumerator)
+    return g_object_ref (enumerator);
+
+  return NULL;
+}
+
+static void
+gvfs_document_file_replace_async (GFile                      *file,
+                             const char                 *etag,
+                             gboolean                    make_backup,
+                             GFileCreateFlags            flags,
+                             int                         io_priority,
+                             GCancellable               *cancellable,
+                             GAsyncReadyCallback         callback,
+                             gpointer                    user_data)
+{
+}
+
+static GFileOutputStream *
+gvfs_document_file_replace_finish (GFile                      *file,
+                              GAsyncResult               *res,
+                              GError                    **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  GFileOutputStream *output_stream;
+
+  output_stream = g_simple_async_result_get_op_res_gpointer (simple);
+  if (output_stream)
+    return g_object_ref (output_stream);
+
+  return NULL;
+}
+
+static void
+gvfs_document_file_file_iface_init (GFileIface *iface)
+{
+  iface->dup = gvfs_document_file_dup;
+  iface->hash = gvfs_document_file_hash;
+  iface->equal = gvfs_document_file_equal;
+  iface->is_native = gvfs_document_file_is_native;
+  iface->has_uri_scheme = gvfs_document_file_has_uri_scheme;
+  iface->get_uri_scheme = gvfs_document_file_get_uri_scheme;
+  iface->get_basename = gvfs_document_file_get_basename;
+  iface->get_path = gvfs_document_file_get_path;
+  iface->get_uri = gvfs_document_file_get_uri;
+  iface->get_parse_name = gvfs_document_file_get_parse_name;
+  iface->get_parent = gvfs_document_file_get_parent;
+  iface->prefix_matches = gvfs_document_file_prefix_matches;
+  iface->get_relative_path = gvfs_document_file_get_relative_path;
+  iface->resolve_relative_path = gvfs_document_file_resolve_relative_path;
+  iface->enumerate_children = gvfs_document_file_enumerate_children;
+  iface->query_info = gvfs_document_file_query_info;
+  iface->query_info_async = gvfs_document_file_query_info_async;
+  iface->query_info_finish = gvfs_document_file_query_info_finish;
+  iface->read_fn = gvfs_document_file_read;
+  iface->create = gvfs_document_file_create;
+  iface->replace = gvfs_document_file_replace;
+
+  /* Async operations */
+
+  /*
+  iface->read_async = gvfs_document_file_read_async;
+  iface->read_finish = gvfs_document_file_read_finish;
+  iface->create_async = gvfs_document_file_create_async;
+  iface->create_finish = gvfs_document_file_create_finish;
+  iface->enumerate_children_async = gvfs_document_file_enumerate_children_async;
+  iface->enumerate_children_finish = gvfs_document_file_enumerate_children_finish;
+  iface->replace_async = gvfs_document_file_replace_async;
+  iface->replace_finish = gvfs_document_file_replace_finish;
+  */
+}
diff --git a/client/gvfsdocumentfile.h b/client/gvfsdocumentfile.h
new file mode 100644
index 0000000..3e2a1ee
--- /dev/null
+++ b/client/gvfsdocumentfile.h
@@ -0,0 +1,58 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * 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 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, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: Alexander Larsson <alexl redhat com>
+ */
+
+#ifndef __G_DOCUMENT_FILE_H__
+#define __G_DOCUMENT_FILE_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GVFS_TYPE_DOCUMENT_FILE         (gvfs_document_file_get_type ())
+#define GVFS_DOCUMENT_FILE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GVFS_TYPE_DOCUMENT_FILE, 
GVfsDocumentFile))
+#define GVFS_DOCUMENT_FILE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GVFS_TYPE_DOCUMENT_FILE, 
GVfsDocumentFileClass))
+#define GVFS_IS_DOCUMENT_FILE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVFS_TYPE_DOCUMENT_FILE))
+#define GVFS_IS_DOCUMENT_FILE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GVFS_TYPE_DOCUMENT_FILE))
+#define GVFS_DOCUMENT_FILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_VFS_TYPE_DOCUMENT_FILE, 
GVfsDocumentFileClass))
+
+typedef struct _GVfsDocumentFile        GVfsDocumentFile;
+typedef struct _GVfsDocumentFileClass   GVfsDocumentFileClass;
+
+struct _GVfsDocumentFileClass
+{
+  GObjectClass parent_class;
+};
+
+struct _GVfsDocumentFile
+{
+  GObject parent_instance;
+
+  char *path;
+};
+
+GType gvfs_document_file_get_type (void) G_GNUC_CONST;
+  
+GFile * gvfs_document_file_new (const char *uri);
+
+G_END_DECLS
+
+#endif /* __G_DOCUMENT_FILE_H__ */
diff --git a/client/gvfsdocumentinputstream.c b/client/gvfsdocumentinputstream.c
new file mode 100644
index 0000000..f1ea855
--- /dev/null
+++ b/client/gvfsdocumentinputstream.c
@@ -0,0 +1,311 @@
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include "gvfsdocumentinputstream.h"
+
+#include <unistd.h>
+#include "glib-unix.h"
+#include <gio/gfiledescriptorbased.h>
+#include <glib/gi18n-lib.h>
+
+
+struct _GVfsDocumentInputStreamPrivate {
+  int fd;
+};
+
+static void       g_file_descriptor_based_iface_init   (GFileDescriptorBasedIface *iface);
+
+#define gvfs_document_input_stream_get_type _gvfs_document_input_stream_get_type
+G_DEFINE_TYPE_WITH_CODE (GVfsDocumentInputStream, gvfs_document_input_stream, G_TYPE_FILE_INPUT_STREAM,
+                         G_ADD_PRIVATE (GVfsDocumentInputStream)
+                        G_IMPLEMENT_INTERFACE (G_TYPE_FILE_DESCRIPTOR_BASED,
+                                               g_file_descriptor_based_iface_init))
+
+static gssize     gvfs_document_input_stream_read       (GInputStream      *stream,
+                                                       void              *buffer,
+                                                       gsize              count,
+                                                       GCancellable      *cancellable,
+                                                       GError           **error);
+static gssize     gvfs_document_input_stream_skip       (GInputStream      *stream,
+                                                       gsize              count,
+                                                       GCancellable      *cancellable,
+                                                       GError           **error);
+static gboolean   gvfs_document_input_stream_close      (GInputStream      *stream,
+                                                       GCancellable      *cancellable,
+                                                       GError           **error);
+static goffset    gvfs_document_input_stream_tell       (GFileInputStream  *stream);
+static gboolean   gvfs_document_input_stream_can_seek   (GFileInputStream  *stream);
+static gboolean   gvfs_document_input_stream_seek       (GFileInputStream  *stream,
+                                                       goffset            offset,
+                                                       GSeekType          type,
+                                                       GCancellable      *cancellable,
+                                                       GError           **error);
+static GFileInfo *gvfs_document_input_stream_query_info (GFileInputStream  *stream,
+                                                       const char        *attributes,
+                                                       GCancellable      *cancellable,
+                                                       GError           **error);
+static int        gvfs_document_input_stream_get_fd     (GFileDescriptorBased *stream);
+
+static void
+gvfs_document_input_stream_class_init (GVfsDocumentInputStreamClass *klass)
+{
+  GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass);
+  GFileInputStreamClass *file_stream_class = G_FILE_INPUT_STREAM_CLASS (klass);
+
+  stream_class->read_fn = gvfs_document_input_stream_read;
+  stream_class->skip = gvfs_document_input_stream_skip;
+  stream_class->close_fn = gvfs_document_input_stream_close;
+  file_stream_class->tell = gvfs_document_input_stream_tell;
+  file_stream_class->can_seek = gvfs_document_input_stream_can_seek;
+  file_stream_class->seek = gvfs_document_input_stream_seek;
+  file_stream_class->query_info = gvfs_document_input_stream_query_info;
+}
+
+static void
+g_file_descriptor_based_iface_init (GFileDescriptorBasedIface *iface)
+{
+  iface->get_fd = gvfs_document_input_stream_get_fd;
+}
+
+static void
+gvfs_document_input_stream_init (GVfsDocumentInputStream *info)
+{
+  info->priv = gvfs_document_input_stream_get_instance_private (info);
+}
+
+GFileInputStream *
+_gvfs_document_input_stream_new (int fd)
+{
+  GVfsDocumentInputStream *stream;
+
+  stream = g_object_new (GVFS_TYPE_DOCUMENT_INPUT_STREAM, NULL);
+  stream->priv->fd = fd;
+  
+  return G_FILE_INPUT_STREAM (stream);
+}
+
+static gssize
+gvfs_document_input_stream_read (GInputStream  *stream,
+                                void          *buffer,
+                                gsize          count,
+                                GCancellable  *cancellable,
+                                GError       **error)
+{
+  GVfsDocumentInputStream *file;
+  gssize res;
+
+  file = GVFS_DOCUMENT_INPUT_STREAM (stream);
+
+  res = -1;
+  while (1)
+    {
+      if (g_cancellable_set_error_if_cancelled (cancellable, error))
+       break;
+      res = read (file->priv->fd, buffer, count);
+      if (res == -1)
+       {
+          int errsv = errno;
+
+         if (errsv == EINTR)
+           continue;
+         
+         g_set_error (error, G_IO_ERROR,
+                      g_io_error_from_errno (errsv),
+                      _("Error reading from file: %s"),
+                      g_strerror (errsv));
+       }
+      
+      break;
+    }
+  
+  return res;
+}
+
+static gssize
+gvfs_document_input_stream_skip (GInputStream  *stream,
+                                gsize          count,
+                                GCancellable  *cancellable,
+                                GError       **error)
+{
+  off_t start, end;
+  GVfsDocumentInputStream *file;
+
+  file = GVFS_DOCUMENT_INPUT_STREAM (stream);
+  
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return -1;
+  
+  start = lseek (file->priv->fd, 0, SEEK_CUR);
+  if (start == -1)
+    {
+      int errsv = errno;
+
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errsv),
+                  _("Error seeking in file: %s"),
+                  g_strerror (errsv));
+      return -1;
+    }
+  
+  end = lseek (file->priv->fd, 0, SEEK_END);
+  if (end == -1)
+    {
+      int errsv = errno;
+
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errsv),
+                  _("Error seeking in file: %s"),
+                  g_strerror (errsv));
+      return -1;
+    }
+
+  if (end - start > count)
+    {
+      end = lseek (file->priv->fd, count - (end - start), SEEK_CUR);
+      if (end == -1)
+       {
+         int errsv = errno;
+
+         g_set_error (error, G_IO_ERROR,
+                      g_io_error_from_errno (errsv),
+                      _("Error seeking in file: %s"),
+                      g_strerror (errsv));
+         return -1;
+       }
+    }
+
+  return end - start;
+}
+
+static gboolean
+gvfs_document_input_stream_close (GInputStream  *stream,
+                                 GCancellable  *cancellable,
+                                 GError       **error)
+{
+  GVfsDocumentInputStream *file;
+
+  file = GVFS_DOCUMENT_INPUT_STREAM (stream);
+
+  if (!g_close (file->priv->fd, NULL))
+    {
+      int errsv = errno;
+      
+      g_set_error (error, G_IO_ERROR,
+                   g_io_error_from_errno (errsv),
+                   _("Error closing file: %s"),
+                   g_strerror (errsv));
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+
+static goffset
+gvfs_document_input_stream_tell (GFileInputStream *stream)
+{
+  GVfsDocumentInputStream *file;
+  off_t pos;
+
+  file = GVFS_DOCUMENT_INPUT_STREAM (stream);
+  
+  pos = lseek (file->priv->fd, 0, SEEK_CUR);
+
+  if (pos == (off_t)-1)
+    return 0;
+  
+  return pos;
+}
+
+static gboolean
+gvfs_document_input_stream_can_seek (GFileInputStream *stream)
+{
+  GVfsDocumentInputStream *file;
+  off_t pos;
+
+  file = GVFS_DOCUMENT_INPUT_STREAM (stream);
+  
+  pos = lseek (file->priv->fd, 0, SEEK_CUR);
+
+  if (pos == (off_t)-1 && errno == ESPIPE)
+    return FALSE;
+  
+  return TRUE;
+}
+
+static int
+seek_type_to_lseek (GSeekType type)
+{
+  switch (type)
+    {
+    default:
+    case G_SEEK_CUR:
+      return SEEK_CUR;
+      
+    case G_SEEK_SET:
+      return SEEK_SET;
+      
+    case G_SEEK_END:
+      return SEEK_END;
+    }
+}
+
+static gboolean
+gvfs_document_input_stream_seek (GFileInputStream  *stream,
+                                goffset            offset,
+                                GSeekType          type,
+                                GCancellable      *cancellable,
+                                GError           **error)
+{
+  GVfsDocumentInputStream *file;
+  off_t pos;
+
+  file = GVFS_DOCUMENT_INPUT_STREAM (stream);
+
+  pos = lseek (file->priv->fd, offset, seek_type_to_lseek (type));
+
+  if (pos == (off_t)-1)
+    {
+      int errsv = errno;
+
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errsv),
+                  _("Error seeking in file: %s"),
+                  g_strerror (errsv));
+      return FALSE;
+    }
+  
+  return TRUE;
+}
+
+static GFileInfo *
+gvfs_document_input_stream_query_info (GFileInputStream  *stream,
+                                      const char        *attributes,
+                                      GCancellable      *cancellable,
+                                      GError           **error)
+{
+  GVfsDocumentInputStream *file;
+  GFileInfo *info;
+
+  file = GVFS_DOCUMENT_INPUT_STREAM (stream);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return NULL;
+
+  info = g_file_info_new ();
+  
+  return info;
+}
+
+static int
+gvfs_document_input_stream_get_fd (GFileDescriptorBased *fd_based)
+{
+  GVfsDocumentInputStream *stream = GVFS_DOCUMENT_INPUT_STREAM (fd_based);
+  return stream->priv->fd;
+}
diff --git a/client/gvfsdocumentinputstream.h b/client/gvfsdocumentinputstream.h
new file mode 100644
index 0000000..f8488c4
--- /dev/null
+++ b/client/gvfsdocumentinputstream.h
@@ -0,0 +1,39 @@
+
+#ifndef __GVFS_DOCUMENT_INPUT_STREAM_H__
+#define __GVFS_DOCUMENT_INPUT_STREAM_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GVFS_TYPE_DOCUMENT_INPUT_STREAM         (_gvfs_document_input_stream_get_type ())
+#define GVFS_DOCUMENT_INPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), 
GVFS_TYPE_DOCUMENT_INPUT_STREAM, GVfsDocumentInputStream))
+#define GVFS_DOCUMENT_INPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), 
GVFS_TYPE_DOCUMENT_INPUT_STREAM, GVfsDocumentInputStreamClass))
+#define GVFS_IS_DOCUMENT_INPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), 
GVFS_TYPE_DOCUMENT_INPUT_STREAM))
+#define GVFS_IS_DOCUMENT_INPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), 
GVFS_TYPE_DOCUMENT_INPUT_STREAM))
+#define GVFS_DOCUMENT_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), 
GVFS_TYPE_DOCUMENT_INPUT_STREAM, GVfsDocumentInputStreamClass))
+
+typedef struct _GVfsDocumentInputStream         GVfsDocumentInputStream;
+typedef struct _GVfsDocumentInputStreamClass    GVfsDocumentInputStreamClass;
+typedef struct _GVfsDocumentInputStreamPrivate  GVfsDocumentInputStreamPrivate;
+
+struct _GVfsDocumentInputStream
+{
+  GFileInputStream parent_instance;
+
+  /*< private >*/
+  GVfsDocumentInputStreamPrivate *priv;
+};
+
+struct _GVfsDocumentInputStreamClass
+{
+  GFileInputStreamClass parent_class;
+};
+
+GType              _gvfs_document_input_stream_get_type (void) G_GNUC_CONST;
+
+GFileInputStream *_gvfs_document_input_stream_new (int fd);
+
+G_END_DECLS
+
+#endif /* __GVFS_DOCUMENT_INPUT_STREAM_H__ */
diff --git a/client/gvfsdocumentoutputstream.c b/client/gvfsdocumentoutputstream.c
new file mode 100644
index 0000000..ab30e15
--- /dev/null
+++ b/client/gvfsdocumentoutputstream.c
@@ -0,0 +1,426 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * 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 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/>.
+ *
+ * Author: Alexander Larsson <alexl redhat com>
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+
+#include <unistd.h>
+#include <gio/gfiledescriptorbased.h>
+
+#include "gvfsdocumentoutputstream.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+struct _GVfsDocumentOutputStreamPrivate {
+  char *etag;
+  int fd;
+  guint32 id;
+  char *doc_handle;
+};
+
+static void       g_file_descriptor_based_iface_init   (GFileDescriptorBasedIface *iface);
+
+#define gvfs_document_output_stream_get_type _gvfs_document_output_stream_get_type
+G_DEFINE_TYPE_WITH_CODE (GVfsDocumentOutputStream, gvfs_document_output_stream, G_TYPE_FILE_OUTPUT_STREAM,
+                         G_ADD_PRIVATE (GVfsDocumentOutputStream)
+                        G_IMPLEMENT_INTERFACE (G_TYPE_FILE_DESCRIPTOR_BASED,
+                                               g_file_descriptor_based_iface_init))
+
+
+
+static gssize     gvfs_document_output_stream_write        (GOutputStream         *stream,
+                                                                const void            *buffer,
+                                                                gsize                  count,
+                                                                GCancellable          *cancellable,
+                                                                GError               **error);
+static gboolean   gvfs_document_output_stream_close        (GOutputStream         *stream,
+                                                                GCancellable          *cancellable,
+                                                                GError               **error);
+static GFileInfo *gvfs_document_output_stream_query_info   (GFileOutputStream     *stream,
+                                                                const char            *attributes,
+                                                                GCancellable          *cancellable,
+                                                                GError               **error);
+static char *     gvfs_document_output_stream_get_etag     (GFileOutputStream     *stream);
+static goffset    gvfs_document_output_stream_tell         (GFileOutputStream     *stream);
+static gboolean   gvfs_document_output_stream_can_seek     (GFileOutputStream     *stream);
+static gboolean   gvfs_document_output_stream_seek         (GFileOutputStream     *stream,
+                                                                goffset                offset,
+                                                                GSeekType              type,
+                                                                GCancellable          *cancellable,
+                                                                GError               **error);
+static gboolean   gvfs_document_output_stream_can_truncate (GFileOutputStream     *stream);
+static gboolean   gvfs_document_output_stream_truncate     (GFileOutputStream     *stream,
+                                                                goffset                size,
+                                                                GCancellable          *cancellable,
+                                                                GError               **error);
+static int        gvfs_document_output_stream_get_fd       (GFileDescriptorBased  *stream);
+
+
+static void
+gvfs_document_output_stream_finalize (GObject *object)
+{
+  GVfsDocumentOutputStream *file;
+  
+  file = GVFS_DOCUMENT_OUTPUT_STREAM (object);
+  
+  g_free (file->priv->doc_handle);
+  g_free (file->priv->etag);
+
+  G_OBJECT_CLASS (gvfs_document_output_stream_parent_class)->finalize (object);
+}
+
+static void
+gvfs_document_output_stream_class_init (GVfsDocumentOutputStreamClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (klass);
+  GFileOutputStreamClass *file_stream_class = G_FILE_OUTPUT_STREAM_CLASS (klass);
+
+  gobject_class->finalize = gvfs_document_output_stream_finalize;
+
+  stream_class->write_fn = gvfs_document_output_stream_write;
+  stream_class->close_fn = gvfs_document_output_stream_close;
+  file_stream_class->query_info = gvfs_document_output_stream_query_info;
+  file_stream_class->get_etag = gvfs_document_output_stream_get_etag;
+  file_stream_class->tell = gvfs_document_output_stream_tell;
+  file_stream_class->can_seek = gvfs_document_output_stream_can_seek;
+  file_stream_class->seek = gvfs_document_output_stream_seek;
+  file_stream_class->can_truncate = gvfs_document_output_stream_can_truncate;
+  file_stream_class->truncate_fn = gvfs_document_output_stream_truncate;
+}
+
+static void
+g_file_descriptor_based_iface_init (GFileDescriptorBasedIface *iface)
+{
+  iface->get_fd = gvfs_document_output_stream_get_fd;
+}
+
+static void
+gvfs_document_output_stream_init (GVfsDocumentOutputStream *stream)
+{
+  stream->priv = gvfs_document_output_stream_get_instance_private (stream);
+}
+
+static gssize
+gvfs_document_output_stream_write (GOutputStream  *stream,
+                                       const void     *buffer,
+                                       gsize           count,
+                                       GCancellable   *cancellable,
+                                       GError        **error)
+{
+  GVfsDocumentOutputStream *file;
+  gssize res;
+
+  file = GVFS_DOCUMENT_OUTPUT_STREAM (stream);
+
+  while (1)
+    {
+      if (g_cancellable_set_error_if_cancelled (cancellable, error))
+       return -1;
+      res = write (file->priv->fd, buffer, count);
+      if (res == -1)
+       {
+          int errsv = errno;
+
+         if (errsv == EINTR)
+           continue;
+         
+         g_set_error (error, G_IO_ERROR,
+                      g_io_error_from_errno (errsv),
+                      _("Error writing to file: %s"),
+                      g_strerror (errsv));
+       }
+      
+      break;
+    }
+  
+  return res;
+}
+
+static GVariant *
+sync_document_call (GVfsDocumentOutputStream *stream,
+                   const char *method,
+                   GVariant *parameters,
+                   const GVariantType  *reply_type,
+                   GUnixFDList  **out_fd_list,
+                   GCancellable *cancellable,
+                   GError **error)
+{
+  GVariant *res;
+  GDBusConnection *bus;
+  GUnixFDList *ret_fd_list;
+  GVariant *reply, *fd_v;
+  char *path;
+  int handle, fd;
+
+  bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
+  if (bus == NULL)
+    return NULL;
+
+  path = g_build_filename ("/org/freedesktop/portal/document", stream->priv->doc_handle, NULL);
+
+  res = g_dbus_connection_call_with_unix_fd_list_sync (bus,
+                                                      "org.freedesktop.portal.DocumentPortal",
+                                                      path,
+                                                      "org.freedesktop.portal.Document",
+                                                      method,
+                                                      parameters, reply_type,
+                                                      G_DBUS_CALL_FLAGS_NONE,
+                                                      -1,
+                                                      NULL,
+                                                      out_fd_list,
+                                                      cancellable,
+                                                      error);
+
+  g_object_unref (bus);
+  return res;
+}
+
+static gboolean
+gvfs_document_output_stream_close (GOutputStream  *stream,
+                                       GCancellable   *cancellable,
+                                       GError        **error)
+{
+  GVfsDocumentOutputStream *file;
+  gboolean failed;
+  GVariant *reply;
+  
+  file = GVFS_DOCUMENT_OUTPUT_STREAM (stream);
+
+  failed = g_cancellable_set_error_if_cancelled (cancellable, error);
+
+  /* Always close, even if cancelled */
+  if (!g_close (file->priv->fd, NULL) && !failed)
+    {
+      int errsv = errno;
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errsv),
+                  _("Error closing file: %s"),
+                  g_strerror (errsv));
+    }
+
+  if (failed)
+    {
+      reply = sync_document_call (file, "AbortUpdate",
+                                 g_variant_new ("(u)", file->priv->id),
+                                 G_VARIANT_TYPE("()"),
+                                 NULL,
+                                 NULL, NULL);
+      g_variant_unref (reply);
+      return FALSE;
+    }
+
+  /* TODO: What if this is cancelled? Do we leak the update? */
+  reply = sync_document_call (file, "FinishUpdate",
+                             g_variant_new ("(u)", file->priv->id),
+                             G_VARIANT_TYPE("()"),
+                             NULL,
+                             cancellable, error);
+  if (reply == NULL)
+    return FALSE;
+
+  g_variant_unref (reply);
+
+  return TRUE;
+}
+
+static char *
+gvfs_document_output_stream_get_etag (GFileOutputStream *stream)
+{
+  GVfsDocumentOutputStream *file;
+
+  file = GVFS_DOCUMENT_OUTPUT_STREAM (stream);
+  
+  return g_strdup (file->priv->etag);
+}
+
+static goffset
+gvfs_document_output_stream_tell (GFileOutputStream *stream)
+{
+  GVfsDocumentOutputStream *file;
+  off_t pos;
+
+  file = GVFS_DOCUMENT_OUTPUT_STREAM (stream);
+  
+  pos = lseek (file->priv->fd, 0, SEEK_CUR);
+
+  if (pos == (off_t)-1)
+    return 0;
+  
+  return pos;
+}
+
+static gboolean
+gvfs_document_output_stream_can_seek (GFileOutputStream *stream)
+{
+  GVfsDocumentOutputStream *file;
+  off_t pos;
+
+  file = GVFS_DOCUMENT_OUTPUT_STREAM (stream);
+  
+  pos = lseek (file->priv->fd, 0, SEEK_CUR);
+
+  if (pos == (off_t)-1 && errno == ESPIPE)
+    return FALSE;
+  
+  return TRUE;
+}
+
+static int
+seek_type_to_lseek (GSeekType type)
+{
+  switch (type)
+    {
+    default:
+    case G_SEEK_CUR:
+      return SEEK_CUR;
+      
+    case G_SEEK_SET:
+      return SEEK_SET;
+      
+    case G_SEEK_END:
+      return SEEK_END;
+    }
+}
+
+static gboolean
+gvfs_document_output_stream_seek (GFileOutputStream  *stream,
+                                      goffset             offset,
+                                      GSeekType           type,
+                                      GCancellable       *cancellable,
+                                      GError            **error)
+{
+  GVfsDocumentOutputStream *file;
+  off_t pos;
+
+  file = GVFS_DOCUMENT_OUTPUT_STREAM (stream);
+
+  pos = lseek (file->priv->fd, offset, seek_type_to_lseek (type));
+
+  if (pos == (off_t)-1)
+    {
+      int errsv = errno;
+
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errsv),
+                  _("Error seeking in file: %s"),
+                  g_strerror (errsv));
+      return FALSE;
+    }
+  
+  return TRUE;
+}
+
+static gboolean
+gvfs_document_output_stream_can_truncate (GFileOutputStream *stream)
+{
+  /* We can't truncate pipes and stuff where we can't seek */
+  return gvfs_document_output_stream_can_seek (stream);
+}
+
+static gboolean
+gvfs_document_output_stream_truncate (GFileOutputStream  *stream,
+                                          goffset             size,
+                                          GCancellable       *cancellable,
+                                          GError            **error)
+{
+  GVfsDocumentOutputStream *file;
+  int res;
+
+  file = GVFS_DOCUMENT_OUTPUT_STREAM (stream);
+
+ restart:
+  res = ftruncate (file->priv->fd, size);
+  
+  if (res == -1)
+    {
+      int errsv = errno;
+
+      if (errsv == EINTR)
+       {
+         if (g_cancellable_set_error_if_cancelled (cancellable, error))
+           return FALSE;
+         goto restart;
+       }
+
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errsv),
+                  _("Error truncating file: %s"),
+                  g_strerror (errsv));
+      return FALSE;
+    }
+  
+  return TRUE;
+}
+
+
+static GFileInfo *
+gvfs_document_output_stream_query_info (GFileOutputStream  *stream,
+                                            const char         *attributes,
+                                            GCancellable       *cancellable,
+                                            GError            **error)
+{
+  GVfsDocumentOutputStream *file;
+  GFileInfo *info;
+
+  file = GVFS_DOCUMENT_OUTPUT_STREAM (stream);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return NULL;
+
+  info = g_file_info_new ();
+  
+  return info;
+}
+
+GFileOutputStream *
+gvfs_document_output_stream_new (const char *handle,
+                                      guint32   id,
+                                      int fd)
+{
+  GVfsDocumentOutputStream *stream;
+
+  stream = g_object_new (GVFS_TYPE_DOCUMENT_OUTPUT_STREAM, NULL);
+  stream->priv->doc_handle = g_strdup (handle);
+  stream->priv->id = id;
+  stream->priv->fd = fd;
+
+  return G_FILE_OUTPUT_STREAM (stream);
+}
+
+static int
+gvfs_document_output_stream_get_fd (GFileDescriptorBased *fd_based)
+{
+  GVfsDocumentOutputStream *stream = GVFS_DOCUMENT_OUTPUT_STREAM (fd_based);
+
+  return stream->priv->fd;
+}
diff --git a/client/gvfsdocumentoutputstream.h b/client/gvfsdocumentoutputstream.h
new file mode 100644
index 0000000..f8b9248
--- /dev/null
+++ b/client/gvfsdocumentoutputstream.h
@@ -0,0 +1,47 @@
+
+#ifndef __GVFS_DOCUMENT_OUTPUT_STREAM_H__
+#define __GVFS_DOCUMENT_OUTPUT_STREAM_H__
+
+#include <gio/gfileoutputstream.h>
+
+G_BEGIN_DECLS
+
+#define GVFS_TYPE_DOCUMENT_OUTPUT_STREAM         (_gvfs_document_output_stream_get_type ())
+#define GVFS_DOCUMENT_OUTPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), 
GVFS_TYPE_DOCUMENT_OUTPUT_STREAM, GVfsDocumentOutputStream))
+#define GVFS_DOCUMENT_OUTPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), 
GVFS_TYPE_DOCUMENT_OUTPUT_STREAM, GVfsDocumentOutputStreamClass))
+#define GVFS_IS_DOCUMENT_OUTPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), 
GVFS_TYPE_DOCUMENT_OUTPUT_STREAM))
+#define GVFS_IS_DOCUMENT_OUTPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), 
GVFS_TYPE_DOCUMENT_OUTPUT_STREAM))
+#define GVFS_DOCUMENT_OUTPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), 
GVFS_TYPE_DOCUMENT_OUTPUT_STREAM, GVfsDocumentOutputStreamClass))
+
+typedef struct _GVfsDocumentOutputStream         GVfsDocumentOutputStream;
+typedef struct _GVfsDocumentOutputStreamClass    GVfsDocumentOutputStreamClass;
+typedef struct _GVfsDocumentOutputStreamPrivate  GVfsDocumentOutputStreamPrivate;
+
+struct _GVfsDocumentOutputStream
+{
+  GFileOutputStream parent_instance;
+
+  /*< private >*/
+  GVfsDocumentOutputStreamPrivate *priv;
+};
+
+struct _GVfsDocumentOutputStreamClass
+{
+  GFileOutputStreamClass parent_class;
+};
+
+GType    gvfs_document_output_stream_get_type (void) G_GNUC_CONST;
+
+void     gvfs_document_output_stream_set_do_close (GVfsDocumentOutputStream *out,
+                                                  gboolean do_close);
+gboolean gvfs_document_output_stream_really_close (GVfsDocumentOutputStream *out,
+                                                  GCancellable   *cancellable,
+                                                  GError        **error);
+
+GFileOutputStream * gvfs_document_output_stream_new  (const char *handle,
+                                                     guint32   id,
+                                                     int       fd);
+
+G_END_DECLS
+
+#endif /* __GVFS_DOCUMENT_OUTPUT_STREAM_H__ */



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