Blame gio/gdocumentportal.c

Packit ae235b
/* GIO - GLib Input, Output and Streaming Library
Packit ae235b
 *
Packit ae235b
 * Copyright 2016 Endless Mobile, Inc.
Packit ae235b
 *
Packit ae235b
 * This library is free software; you can redistribute it and/or
Packit ae235b
 * modify it under the terms of the GNU Lesser General Public
Packit ae235b
 * License as published by the Free Software Foundation; either
Packit ae235b
 * version 2.1 of the License, or (at your option) any later version.
Packit ae235b
 *
Packit ae235b
 * This library is distributed in the hope that it will be useful,
Packit ae235b
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit ae235b
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit ae235b
 * Lesser General Public License for more details.
Packit ae235b
 *
Packit ae235b
 * You should have received a copy of the GNU Lesser General
Packit ae235b
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
Packit ae235b
 */
Packit ae235b
Packit ae235b
#include "config.h"
Packit ae235b
Packit ae235b
#include <sys/stat.h>
Packit ae235b
#include <fcntl.h>
Packit ae235b
#include <errno.h>
Packit ae235b
#include <string.h>
Packit ae235b
Packit ae235b
#include "gdocumentportal.h"
Packit ae235b
#include "xdp-dbus.h"
Packit ae235b
#include "gstdio.h"
Packit ae235b
Packit ae235b
#ifdef G_OS_UNIX
Packit ae235b
#include "gunixfdlist.h"
Packit ae235b
#endif
Packit ae235b
Packit ae235b
#ifndef O_PATH
Packit ae235b
#define O_PATH 0
Packit ae235b
#endif
Packit ae235b
#ifndef O_CLOEXEC
Packit ae235b
#define O_CLOEXEC 0
Packit ae235b
#else
Packit ae235b
#define HAVE_O_CLOEXEC 1
Packit ae235b
#endif
Packit ae235b
Packit ae235b
static GXdpDocuments *documents;
Packit ae235b
static char *documents_mountpoint;
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
init_document_portal (void)
Packit ae235b
{
Packit ae235b
  static gsize documents_inited = 0;
Packit ae235b
Packit ae235b
  if (g_once_init_enter (&documents_inited))
Packit ae235b
    {
Packit ae235b
      GError *error = NULL;
Packit ae235b
      GDBusConnection *connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
Packit ae235b
Packit ae235b
      if (connection != NULL)
Packit ae235b
        {
Packit ae235b
          documents = gxdp_documents_proxy_new_sync (connection, 0,
Packit ae235b
                                                     "org.freedesktop.portal.Documents",
Packit ae235b
                                                     "/org/freedesktop/portal/documents",
Packit ae235b
                                                     NULL, &error);
Packit ae235b
          if (documents != NULL)
Packit ae235b
            {
Packit ae235b
              gxdp_documents_call_get_mount_point_sync (documents,
Packit ae235b
                                                        &documents_mountpoint,
Packit ae235b
                                                        NULL, &error);
Packit ae235b
Packit ae235b
              if (error != NULL)
Packit ae235b
                {
Packit ae235b
                  g_warning ("Cannot get document portal mount point: %s", error->message);
Packit ae235b
                  g_error_free (error);
Packit ae235b
                }
Packit ae235b
            }
Packit ae235b
          else
Packit ae235b
            {
Packit ae235b
              g_warning ("Cannot create document portal proxy: %s", error->message);
Packit ae235b
              g_error_free (error);
Packit ae235b
            }
Packit ae235b
Packit ae235b
          g_object_unref (connection);
Packit ae235b
        }
Packit ae235b
      else
Packit ae235b
        {
Packit ae235b
          g_warning ("Cannot connect to session bus when initializing document portal: %s",
Packit ae235b
                     error->message);
Packit ae235b
          g_error_free (error);
Packit ae235b
        }
Packit ae235b
Packit ae235b
      g_once_init_leave (&documents_inited, 1);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return (documents != NULL && documents_mountpoint != NULL);
Packit ae235b
}
Packit ae235b
Packit ae235b
char *
Packit ae235b
g_document_portal_add_document (GFile   *file,
Packit ae235b
                                GError **error)
Packit ae235b
{
Packit ae235b
  char *doc_path, *basename;
Packit ae235b
  char *doc_id = NULL;
Packit ae235b
  char *doc_uri = NULL;
Packit ae235b
  char *path = NULL;
Packit ae235b
  GUnixFDList *fd_list = NULL;
Packit ae235b
  int fd, fd_in, errsv;
Packit ae235b
  gboolean ret;
Packit ae235b
Packit ae235b
  if (!init_document_portal ())
Packit ae235b
    {
Packit ae235b
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED,
Packit ae235b
                   "Document portal is not available");
Packit ae235b
      goto out;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  path = g_file_get_path (file);
Packit ae235b
  fd = g_open (path, O_PATH | O_CLOEXEC);
Packit ae235b
  errsv = errno;
Packit ae235b
Packit ae235b
  if (fd == -1)
Packit ae235b
    {
Packit ae235b
      g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
Packit ae235b
                   "Failed to open %s", path);
Packit ae235b
      goto out;
Packit ae235b
    }
Packit ae235b
Packit ae235b
#ifndef HAVE_O_CLOEXEC
Packit ae235b
  fcntl (fd, F_SETFD, FD_CLOEXEC);
Packit ae235b
#endif
Packit ae235b
Packit ae235b
  fd_list = g_unix_fd_list_new ();
Packit ae235b
  fd_in = g_unix_fd_list_append (fd_list, fd, error);
Packit ae235b
  g_close (fd, NULL);
Packit ae235b
Packit ae235b
  if (fd_in == -1)
Packit ae235b
    goto out;
Packit ae235b
Packit ae235b
  ret = gxdp_documents_call_add_sync (documents,
Packit ae235b
                                      g_variant_new_handle (fd_in),
Packit ae235b
                                      TRUE,
Packit ae235b
                                      TRUE,
Packit ae235b
                                      fd_list,
Packit ae235b
                                      &doc_id,
Packit ae235b
                                      NULL,
Packit ae235b
                                      NULL,
Packit ae235b
                                      error);
Packit ae235b
Packit ae235b
  if (!ret)
Packit ae235b
    goto out;
Packit ae235b
Packit ae235b
  basename = g_path_get_basename (path);
Packit ae235b
  doc_path = g_build_filename (documents_mountpoint, doc_id, basename, NULL);
Packit ae235b
  g_free (basename);
Packit ae235b
Packit ae235b
  doc_uri = g_filename_to_uri (doc_path, NULL, NULL);
Packit ae235b
  g_free (doc_path);
Packit ae235b
Packit ae235b
 out:
Packit ae235b
  if (fd_list)
Packit ae235b
    g_object_unref (fd_list);
Packit ae235b
  g_free (path);
Packit ae235b
  g_free (doc_id);
Packit ae235b
Packit ae235b
  return doc_uri;
Packit ae235b
}
Packit ae235b
Packit ae235b
/* Flags accepted by org.freedesktop.portal.Documents.AddFull */
Packit ae235b
enum {
Packit ae235b
  XDP_ADD_FLAGS_REUSE_EXISTING             =  (1 << 0),
Packit ae235b
  XDP_ADD_FLAGS_PERSISTENT                 =  (1 << 1),
Packit ae235b
  XDP_ADD_FLAGS_AS_NEEDED_BY_APP           =  (1 << 2),
Packit ae235b
  XDP_ADD_FLAGS_FLAGS_ALL                  = ((1 << 3) - 1)
Packit ae235b
};
Packit ae235b
Packit ae235b
GList *
Packit ae235b
g_document_portal_add_documents (GList       *uris,
Packit ae235b
                                 const char  *app_id,
Packit ae235b
                                 GError     **error)
Packit ae235b
{
Packit ae235b
  int length;
Packit ae235b
  GList *ruris = NULL;
Packit ae235b
  gboolean *as_is;
Packit ae235b
  GVariantBuilder builder;
Packit ae235b
  GUnixFDList *fd_list = NULL;
Packit ae235b
  GList *l;
Packit ae235b
  gsize i, j;
Packit ae235b
  const char *permissions[] = { "read", "write", NULL };
Packit ae235b
  char **doc_ids = NULL;
Packit ae235b
  GVariant *extra_out = NULL;
Packit ae235b
Packit ae235b
  if (!init_document_portal ())
Packit ae235b
    {
Packit ae235b
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED,
Packit ae235b
                   "Document portal is not available");
Packit ae235b
      return NULL;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  length = g_list_length (uris);
Packit ae235b
  as_is = g_new0 (gboolean, length);
Packit ae235b
Packit ae235b
  g_variant_builder_init (&builder, G_VARIANT_TYPE ("ah"));
Packit ae235b
Packit ae235b
  fd_list = g_unix_fd_list_new ();
Packit ae235b
  for (l = uris, i = 0; l; l = l->next, i++)
Packit ae235b
    {
Packit ae235b
      const char *uri = l->data;
Packit ae235b
      int idx = -1;
Packit ae235b
      g_autofree char *path = NULL;
Packit ae235b
Packit ae235b
      path = g_filename_from_uri (uri, NULL, NULL);
Packit ae235b
      if (path != NULL)
Packit ae235b
        {
Packit ae235b
          int fd;
Packit ae235b
Packit ae235b
          fd = g_open (path, O_CLOEXEC | O_PATH);
Packit ae235b
          if (fd >= 0)
Packit ae235b
            {
Packit ae235b
#ifndef HAVE_O_CLOEXEC
Packit ae235b
              fcntl (fd, F_SETFD, FD_CLOEXEC);
Packit ae235b
#endif
Packit ae235b
              idx = g_unix_fd_list_append (fd_list, fd, NULL);
Packit ae235b
              close (fd);
Packit ae235b
            }
Packit ae235b
        }
Packit ae235b
Packit ae235b
      if (idx != -1)
Packit ae235b
        g_variant_builder_add (&builder, "h", idx);
Packit ae235b
      else
Packit ae235b
        as_is[i] = TRUE;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  if (g_unix_fd_list_get_length (fd_list) > 0)
Packit ae235b
    {
Packit ae235b
      if (!gxdp_documents_call_add_full_sync (documents,
Packit ae235b
                                              g_variant_builder_end (&builder),
Packit ae235b
                                              XDP_ADD_FLAGS_AS_NEEDED_BY_APP,
Packit ae235b
                                              app_id,
Packit ae235b
                                              permissions,
Packit ae235b
                                              fd_list,
Packit ae235b
                                              &doc_ids,
Packit ae235b
                                              &extra_out,
Packit ae235b
                                              NULL,
Packit ae235b
                                              NULL,
Packit ae235b
                                              error))
Packit ae235b
        goto out;
Packit ae235b
Packit ae235b
      for (l = uris, i = 0, j = 0; l; l = l->next, i++)
Packit ae235b
        {
Packit ae235b
          const char *uri = l->data;
Packit ae235b
          char *ruri;
Packit ae235b
Packit ae235b
          if (as_is[i]) /* use as-is, not a file uri */
Packit ae235b
            {
Packit ae235b
              ruri = g_strdup (uri);
Packit ae235b
            }
Packit ae235b
          else if (strcmp (doc_ids[j], "") == 0) /* not rewritten */
Packit ae235b
            {
Packit ae235b
              ruri = g_strdup (uri);
Packit ae235b
              j++;
Packit ae235b
            }
Packit ae235b
          else
Packit ae235b
            {
Packit ae235b
              char *basename = g_path_get_basename (uri + strlen ("file:"));
Packit ae235b
              char *doc_path = g_build_filename (documents_mountpoint, doc_ids[j], basename, NULL);
Packit ae235b
              ruri = g_strconcat ("file:", doc_path, NULL);
Packit ae235b
              g_free (basename);
Packit ae235b
              g_free (doc_path);
Packit ae235b
              j++;
Packit ae235b
            }
Packit ae235b
Packit ae235b
          ruris = g_list_prepend (ruris, ruri);
Packit ae235b
        }
Packit ae235b
Packit ae235b
      ruris = g_list_reverse (ruris);
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      ruris = g_list_copy_deep (uris, (GCopyFunc)g_strdup, NULL);
Packit ae235b
    }
Packit ae235b
Packit ae235b
out:
Packit ae235b
  g_clear_object (&fd_list);
Packit ae235b
  g_clear_pointer (&extra_out, g_variant_unref);
Packit ae235b
  g_clear_pointer (&doc_ids, g_strfreev);
Packit ae235b
  g_free (as_is);
Packit ae235b
Packit ae235b
  return ruris;
Packit ae235b
}