merge upstream 1.50.3

This commit is contained in:
Yue-Lan 2023-04-07 11:45:59 +08:00
parent e118d16b14
commit cfb097cdca
126 changed files with 41792 additions and 31832 deletions

43
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,43 @@
fedora:
image:
name: registry.gitlab.gnome.org/gnome/gvfs:latest
entrypoint: ["/bin/sh", "-c"]
tags:
- x86_64
- ipv6
script:
- meson
-Dinstalled_tests=true
-Ddevel_utils=true
-Dman=true
-Dafc=true
-Darchive=true
-Dsftp=true
-Dsmb=true
-Dudisks2=true
-Dhttp=true
-Dgphoto2=true
--prefix /usr --werror build
- sudo ninja -C build install
- GIO_USE_VOLUME_MONITOR=unix gnome-desktop-testing-runner gvfs
allow_failure: true
update-image:
variables:
STORAGE_DRIVER: vfs
BUILDAH_FORMAT: docker
BUILDAH_ISOLATION: chroot
image: registry.fedoraproject.org/fedora:latest
script:
- dnf install -y buildah runc
- sed -i '/^mountopt =.*/d' /etc/containers/storage.conf
- buildah bud --tag $CI_REGISTRY_IMAGE -f .gitlab-ci/Dockerfile
- buildah tag $CI_REGISTRY_IMAGE "$CI_REGISTRY_IMAGE:v$CI_JOB_ID"
- buildah login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- buildah push --creds $CI_REGISTRY_USER:$CI_REGISTRY_PASSWORD $CI_REGISTRY_IMAGE
- buildah push --creds $CI_REGISTRY_USER:$CI_REGISTRY_PASSWORD "$CI_REGISTRY_IMAGE:v$CI_JOB_ID"
when: manual
only:
variables:
- $CI_PROJECT_NAMESPACE == "GNOME"

46
.gitlab-ci/Dockerfile Normal file
View File

@ -0,0 +1,46 @@
FROM fedora:latest
RUN dnf install --nogpg -y dnf-plugins-core git gnome-desktop-testing dbus-daemon python3-twisted python3-gobject procps-ng bzip2 httpd mod_ssl openssh-server passwd gcc-c++ \
&& dnf builddep --nogpg -y gvfs \
&& dnf clean all
RUN dnf builddep --nogpg -y glib \
&& dnf clean all \
&& git clone --depth 1 https://gitlab.gnome.org/GNOME/glib.git \
&& cd glib \
&& meson . _build --prefix=/usr \
&& ninja -C _build \
&& ninja -C _build install \
&& cd .. \
&& rm -rf glib
RUN dnf builddep --nogpg -y glib-networking \
&& dnf clean all \
&& git clone --depth 1 https://gitlab.gnome.org/GNOME/glib-networking.git \
&& cd glib-networking \
&& meson . _build --prefix=/usr \
&& ninja -C _build \
&& ninja -C _build install \
&& cd .. \
&& rm -rf glib-networking
RUN dnf builddep --nogpg -y libsoup \
&& dnf install -y --nogpg libnghttp2-devel \
&& dnf clean all \
&& git clone --depth 1 https://gitlab.gnome.org/GNOME/libsoup.git \
&& cd libsoup \
&& meson . _build --prefix=/usr \
&& ninja -C _build \
&& ninja -C _build install \
&& cd .. \
&& rm -rf libsoup
RUN sed -i -e 's/# %wheel/%wheel/' /etc/sudoers
RUN useradd -G wheel -m user
RUN passwd -d user
USER user
WORKDIR /home/user
ENV USER user
ENV XDG_RUNTIME_DIR /home/user
RUN ssh-keygen -t rsa -q -N "" -f ~/.ssh/id_rsa

View File

@ -8,6 +8,6 @@ GVfs source repository is at https://gitlab.gnome.org/GNOME/gvfs.
## Code Contribution
See https://wiki.gnome.org/GitLab for general informations about GitLab workflow
See https://wiki.gnome.org/GitLab for general information about GitLab workflow
for code contribution. GVfs still uses linear GIT history without merge commits,
please see general commit guidelines at https://wiki.gnome.org/Git/CommitMessages.

118
NEWS
View File

@ -1,3 +1,121 @@
Major changes in 1.50.3
=======================
* dav: Prevent usage of NULL when user is not specified (Ondrej Holy)
* ftp: Fix hangs when the connection is released (wangrong)
* fuse: Decrease file handle reference when open file fail (wangrong)
* sftp: PATH-expand the ssh client (Alex Stewart)
* test: Several smaller enhancements (Sébastien Bacher)
* backend: Add support for xx-large and x-large thumbnails (Ondrej Holy)
* goa: Prevent automounts when resuming from suspension (Ondrej Holy)
* Translation updates (GNOME Translation Project contributors)
Major changes in 1.50.2
=======================
* smb: Rework anonymous handling to avoid EINVAL (Ondrej Holy)
* http: Unescape prefix to fix handling of encoded URIs (Ondrej Holy)
* build: Fix build without Avahi support (Ondrej Holy)
Major changes in 1.50.1
=======================
* dav: Drop user from URI as a workaround for Nextcloud bug (Ondrej Holy)
* dav: Port DNS-SD resolver to async API to fix hangs when mounting (Ondrej Holy)
* smb: Ignore EINVAL for kerberos/ccache login (Ondrej Holy)
* dav: Rewrite to libsoup async API to fix crashes (Daniel Kolesa)
* dav: Do not lose userinfo when copying URIs (Daniel Kolesa)
Major changes in 1.50.0
=======================
* Translation updates
Major changes in 1.49.90
========================
* http/dav: Port to libsoup3 (Daniel Kolesa)
* http: Do not silently accept invalid certificates (Daniel Kolesa)
* build: Remove incorrect i18n.merge_file argument to fix build (Ondrej Holy)
* Translation updates
Major changes in 1.49.1
=======================
* sftp: Adapt on new OpenSSH password prompts
* build: Set of improvements for meson
* Add PartOf=graphical-session.target to all systemd units
* Move systemd services to session slice
* Translation updates
Major changes in 1.48.1
=======================
* build: Use install prefix in systemd files paths
* client: Prevent socket leaks if socket dir is inaccessible from client
* admin: Fix regressions caused by port to named sockets
* udisks2: Report unmount progress after showing blocking processes
* Translation updates
Major changes in 1.48.0
=======================
* Translation updates
Major changes in 1.47.91
========================
* Translation updates
Major changes in 1.47.90
========================
* google: Add Shared drives folder
* google: Add Shared with me folder
* google: Improve performance for folders with large number of files
* daemon: Set filesystem::use-preview explicitly and consistently
* daemon: Unify and shorten strings for prompt dialog titles
* Translation updates
Major changes in 1.47.1
=======================
* mtp: Fix crashes when storage descriptions are not provided
* trash: Explicitly cancel file monitor to prevent deadlock
* admin: Add copy and push implementations for better performance
* client: Add fallback to session bus for synchronous API
* daemon: Use named sockets to avoid network permission requirement
* smb: Set fast content type independently of other attributes
* ftp: Prevent source file removal in case of transfer failure
* google: Report progress for file transfers from local filesystem
* sftp: Add support for two factor authentication
* sftp: Use connection multiplexing instead of multiple connections
* smb: Report progress when move operation is done
* google: Set the display name for the root folder also
* Translation updates
Major changes in 1.46.1
=======================
* trash: Do not fail when G_FILE_COPY_NO_FALLBACK_FOR_MOVE is used
* dav: Be sure that enumeration is possible when looking for a root
Major changes in 1.46.0
=======================
* No changes
Major changes in 1.45.92
========================
* build: Link libgvfscommon to libmetadata
* Translation updates
Major changes in 1.45.90
========================
* trash: Add support for x-gvfs-notrash option to ignore mounts
* recent: Port to GDateTime
* Translation updates
Major changes in 1.45.3
=======================
* client: Add support for zone identifiers in IPv6 addresses
* afc: Add support for libplist-2.2
* Translation updates
Major changes in 1.45.2
=======================
* metadata: Emit D-Bus signal when metadata are modified
* sftp: Preserve timestamps during copy and move operations
* dav: Add support for Negotiate and NTLM authentication
* Translation updates
Major changes in 1.44.1
=======================
* udisks2: Fix several memory leaks

View File

@ -7,15 +7,16 @@ implement volume monitors and persistent metadata storage. There is also FUSE
support that provides limited access to the GVfs filesystems for applications
not using GIO.
For more info about GVfs see https://wiki.gnome.org/Projects/gvfs.
For more info about GVfs, see https://wiki.gnome.org/Projects/gvfs.
## Reporting Bugs
Bug reports can be found and filed at https://gitlab.gnome.org/GNOME/gvfs/issues.
For security related issues, please use [security@gnome.org](mailto:security@gnome.org).
For security-related issues, please use https://security.gnome.org/.
See https://wiki.gnome.org/Projects/gvfs/debugging for info about getting debug
logs.
## Ask Questions
For questions use gvfs mailing list [gvfs-list@gnome.org](mailto:gvfs-list@gnome.org).
See https://mail.gnome.org/mailman/listinfo/gvfs-list for subscription info.
Alternatively you use irc://irc.gnome.org/nautilus.
For questions use [GNOME Discourse](https://discourse.gnome.org/).

View File

@ -438,6 +438,17 @@ create_proxy_for_file2 (GFile *file1,
}
connection = _g_dbus_connection_get_sync (mount_info1->dbus_id, cancellable, &local_error);
if (connection == NULL && !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
{
g_dbus_error_strip_remote_error (local_error);
g_warning ("The peer-to-peer connection failed: %s. Falling back to the "
"session bus. Your application is probably missing "
"--filesystem=xdg-run/gvfsd privileges.", local_error->message);
g_clear_error (&local_error);
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, cancellable, &local_error);
}
if (connection == NULL)
goto out;
@ -613,8 +624,19 @@ async_got_connection_cb (GDBusConnection *connection,
if (connection == NULL)
{
/* TODO: we should probably test if we really want a session bus;
* for now, this code is on par with the old dbus code */
g_dbus_error_strip_remote_error (io_error);
if (g_error_matches (io_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
{
g_task_return_error (data->task, g_error_copy (io_error));
async_proxy_create_free (data);
return;
}
g_warning ("The peer-to-peer connection failed: %s. Falling back to the "
"session bus. Your application is probably missing "
"--filesystem=xdg-run/gvfsd privileges.", io_error->message);
g_bus_get (G_BUS_TYPE_SESSION,
g_task_get_cancellable (data->task),
bus_get_cb,
@ -2689,14 +2711,6 @@ file_transfer (GFile *source,
return FALSE;
}
if (!native_transfer && remove_source &&
(flags & G_FILE_COPY_NO_FALLBACK_FOR_MOVE))
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Operation not supported"));
return FALSE;
}
if (!native_transfer && local_path == NULL)
{
/* This will cause the fallback code to be involved */

View File

@ -178,6 +178,17 @@ g_daemon_file_monitor_new (const char *remote_id,
daemon_monitor->remote_obj_path = g_strdup (remote_obj_path);
connection = _g_dbus_connection_get_sync (daemon_monitor->remote_id, NULL, &error);
if (connection == NULL && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
{
g_dbus_error_strip_remote_error (error);
g_warning ("The peer-to-peer connection failed: %s. Falling back to the "
"session bus. Your application is probably missing "
"--filesystem=xdg-run/gvfsd privileges.", error->message);
g_clear_error (&error);
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
}
if (connection == NULL)
{
g_printerr ("Error getting connection for monitoring: %s (%s, %d)\n",

View File

@ -315,8 +315,19 @@ async_got_connection_cb (GDBusConnection *connection,
if (connection == NULL)
{
/* TODO: we should probably test if we really want a session bus;
* for now, this code is on par with the old dbus code */
g_dbus_error_strip_remote_error (io_error);
if (g_error_matches (io_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
{
g_task_return_error (task, g_error_copy (io_error));
g_object_unref (task);
return;
}
g_warning ("The peer-to-peer connection failed: %s. Falling back to the "
"session bus. Your application is probably missing "
"--filesystem=xdg-run/gvfsd privileges.", io_error->message);
g_bus_get (G_BUS_TYPE_SESSION,
g_task_get_cancellable (task),
bus_get_cb,

View File

@ -1434,7 +1434,7 @@ g_daemon_vfs_deserialize_icon (GVfs *vfs,
GDBusConnection *
_g_daemon_vfs_get_async_bus (void)
{
return the_vfs ? the_vfs->async_bus : NULL;
return the_vfs->async_bus;
}
static gboolean

View File

@ -31,12 +31,14 @@
#include <errno.h>
#include <glib/gi18n-lib.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include "gvfsdaemondbus.h"
#include <gvfsdaemonprotocol.h>
#include <gdaemonvfs.h>
#include <gvfsdbus.h>
#include <gvfsutils.h>
/* Extra vfs-specific data for GDBusConnections */
typedef struct {
@ -156,6 +158,7 @@ set_connection_for_async (GDBusConnection *connection, const char *dbus_id)
typedef struct {
char *dbus_id;
GVfsDBusDaemon *proxy;
GDBusConnection *connection;
GCancellable *cancellable;
@ -174,6 +177,7 @@ async_call_finish (AsyncDBusCall *async_call)
async_call->io_error,
async_call->callback_data);
g_clear_object (&async_call->proxy);
g_clear_object (&async_call->connection);
g_clear_object (&async_call->cancellable);
g_clear_error (&async_call->io_error);
@ -260,32 +264,67 @@ async_get_connection_response (GVfsDBusDaemon *proxy,
g_free (address1);
}
static void
socket_dir_query_info_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
AsyncDBusCall *async_call = user_data;
g_autoptr (GFileInfo) socket_dir_info = NULL;
socket_dir_info = g_file_query_info_finish (G_FILE (source_object),
res,
&async_call->io_error);
if (socket_dir_info == NULL ||
!g_file_info_get_attribute_boolean (socket_dir_info,
G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE))
{
if (!async_call->io_error)
async_call->io_error = g_error_new_literal (G_IO_ERROR,
G_IO_ERROR_PERMISSION_DENIED,
_("Permission denied"));
async_call_finish (async_call);
return;
}
g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (async_call->proxy), G_VFS_DBUS_TIMEOUT_MSECS);
gvfs_dbus_daemon_call_get_connection (async_call->proxy,
async_call->cancellable,
(GAsyncReadyCallback) async_get_connection_response,
async_call);
}
static void
open_connection_async_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
GVfsDBusDaemon *proxy;
AsyncDBusCall *async_call = user_data;
GError *error = NULL;
proxy = gvfs_dbus_daemon_proxy_new_finish (res, &error);
if (proxy == NULL)
g_autofree gchar *socket_dir_path = NULL;
g_autoptr (GFile) socket_dir = NULL;
async_call->proxy = gvfs_dbus_daemon_proxy_new_finish (res, &error);
if (async_call->proxy == NULL)
{
async_call->io_error = g_error_copy (error);
g_error_free (error);
async_call_finish (async_call);
return;
}
g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (proxy), G_VFS_DBUS_TIMEOUT_MSECS);
gvfs_dbus_daemon_call_get_connection (proxy,
async_call->cancellable,
(GAsyncReadyCallback) async_get_connection_response,
async_call);
g_object_unref (proxy);
/* This is needed to prevent socket leaks. */
socket_dir_path = gvfs_get_socket_dir ();
socket_dir = g_file_new_for_path (socket_dir_path);
g_file_query_info_async (socket_dir,
G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
G_FILE_QUERY_INFO_NONE,
G_PRIORITY_DEFAULT,
async_call->cancellable,
socket_dir_query_info_cb,
user_data);
}
static void
@ -522,6 +561,9 @@ _g_dbus_connection_get_sync (const char *dbus_id,
gchar *address1;
GVfsDBusDaemon *daemon_proxy;
gboolean res;
g_autofree gchar *socket_dir_path = NULL;
g_autoptr (GFile) socket_dir = NULL;
g_autoptr (GFileInfo) socket_dir_info = NULL;
if (g_cancellable_set_error_if_cancelled (cancellable, error))
return NULL;
@ -591,6 +633,26 @@ _g_dbus_connection_get_sync (const char *dbus_id,
if (daemon_proxy == NULL)
return NULL;
/* This is needed to prevent socket leaks. */
socket_dir_path = gvfs_get_socket_dir ();
socket_dir = g_file_new_for_path (socket_dir_path);
socket_dir_info = g_file_query_info (socket_dir,
G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
G_FILE_QUERY_INFO_NONE,
cancellable,
error);
if (socket_dir_info == NULL ||
!g_file_info_get_attribute_boolean (socket_dir_info,
G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE))
{
if (error && !*error)
*error = g_error_new_literal (G_IO_ERROR,
G_IO_ERROR_PERMISSION_DENIED,
_("Permission denied"));
return NULL;
}
address1 = NULL;
res = gvfs_dbus_daemon_call_get_connection_sync (daemon_proxy,
&address1,

View File

@ -43,7 +43,8 @@
#include <gvfsdbus.h>
#include <gvfsutils.h>
#define FUSE_USE_VERSION 26
#define FUSE_USE_VERSION 30
#include <fuse.h>
#include <fuse_lowlevel.h>
@ -859,7 +860,7 @@ getattr_for_file_handle (FileHandle *fh, struct stat *sbuf)
}
static gint
vfs_getattr (const gchar *path, struct stat *sbuf)
vfs_getattr (const gchar *path, struct stat *sbuf, struct fuse_file_info *fi)
{
GFile *file;
gint result = 0;
@ -1090,6 +1091,9 @@ open_common (const gchar *path, struct fuse_file_info *fi, GFile *file, int outp
g_mutex_unlock (&fh->mutex);
if (result < 0)
file_handle_unref (fh);
/* The added reference to the file handle is released in vfs_release() */
return result;
}
@ -1581,12 +1585,12 @@ readdir_for_file (GFile *base_file, gpointer buf, fuse_fill_dir_t filler)
return result;
}
filler (buf, ".", NULL, 0);
filler (buf, "..", NULL, 0);
filler (buf, ".", NULL, 0, 0);
filler (buf, "..", NULL, 0, 0);
while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &error)) != NULL)
{
filler (buf, g_file_info_get_name (file_info), NULL, 0);
filler (buf, g_file_info_get_name (file_info), NULL, 0, 0);
g_object_unref (file_info);
}
@ -1597,7 +1601,7 @@ readdir_for_file (GFile *base_file, gpointer buf, fuse_fill_dir_t filler)
static gint
vfs_readdir (const gchar *path, gpointer buf, fuse_fill_dir_t filler, off_t offset,
struct fuse_file_info *fi)
struct fuse_file_info *fi, enum fuse_readdir_flags fl)
{
GFile *base_file;
gint result = 0;
@ -1610,8 +1614,8 @@ vfs_readdir (const gchar *path, gpointer buf, fuse_fill_dir_t filler, off_t offs
/* Mount list */
filler (buf, ".", NULL, 0);
filler (buf, "..", NULL, 0);
filler (buf, ".", NULL, 0, 0);
filler (buf, "..", NULL, 0, 0);
mount_list_lock ();
@ -1619,7 +1623,7 @@ vfs_readdir (const gchar *path, gpointer buf, fuse_fill_dir_t filler, off_t offs
{
MountRecord *mount_record = l->data;
filler (buf, mount_record->name, NULL, 0);
filler (buf, mount_record->name, NULL, 0, 0);
}
mount_list_unlock ();
@ -1643,13 +1647,21 @@ vfs_readdir (const gchar *path, gpointer buf, fuse_fill_dir_t filler, off_t offs
}
static gint
vfs_rename (const gchar *old_path, const gchar *new_path)
vfs_rename (const gchar *old_path, const gchar *new_path, unsigned int vfs_flags)
{
GFile *old_file;
GFile *new_file;
GFileCopyFlags flags = G_FILE_COPY_OVERWRITE;
GError *error = NULL;
gint result = 0;
/* Can not implement this flag because limitation of GFile. */
if (vfs_flags & RENAME_EXCHANGE)
return -EINVAL;
if (vfs_flags & RENAME_NOREPLACE)
flags = G_FILE_COPY_NONE;
g_debug ("vfs_rename: %s -> %s\n", old_path, new_path);
old_file = file_from_full_path (old_path);
@ -1665,7 +1677,7 @@ vfs_rename (const gchar *old_path, const gchar *new_path)
file_handle_close_stream (fh);
}
g_file_move (old_file, new_file, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error);
g_file_move (old_file, new_file, flags, NULL, NULL, NULL, &error);
if (error)
{
@ -2072,6 +2084,15 @@ vfs_truncate (const gchar *path, off_t size)
return result;
}
static gint
vfs_truncate_dispatch (const gchar *path, off_t size, struct fuse_file_info *fi)
{
if (fi)
return vfs_ftruncate (path, size, fi);
return vfs_truncate (path, size);
}
static gint
vfs_symlink (const gchar *path_old, const gchar *path_new)
{
@ -2178,7 +2199,7 @@ vfs_access (const gchar *path, gint mode)
}
static gint
vfs_utimens (const gchar *path, const struct timespec tv [2])
vfs_utimens (const gchar *path, const struct timespec tv [2], struct fuse_file_info *fi)
{
GFile *file;
GError *error = NULL;
@ -2253,7 +2274,7 @@ vfs_utimens (const gchar *path, const struct timespec tv [2])
}
static gint
vfs_chmod (const gchar *path, mode_t mode)
vfs_chmod (const gchar *path, mode_t mode, struct fuse_file_info *fi)
{
GFile *file;
GError *error = NULL;
@ -2376,7 +2397,7 @@ register_fuse_cb (GVfsDBusMountTracker *proxy,
}
static gpointer
vfs_init (struct fuse_conn_info *conn)
vfs_init (struct fuse_conn_info *conn, struct fuse_config *cfg)
{
GVfsDBusMountTracker *proxy;
GError *error;
@ -2452,11 +2473,7 @@ vfs_init (struct fuse_conn_info *conn)
conn->want |= FUSE_CAP_ATOMIC_O_TRUNC;
/* Prevent out-of-order readahead */
conn->async_read = 0;
/* Use up to a 64KiB write block size. Only has an effect if -o big_writes
* is given on the command-line. */
conn->max_write = 65536;
conn->want &= ~FUSE_CAP_ASYNC_READ;
return NULL;
}
@ -2503,8 +2520,7 @@ static struct fuse_operations vfs_oper =
.unlink = vfs_unlink,
.mkdir = vfs_mkdir,
.rmdir = vfs_rmdir,
.ftruncate = vfs_ftruncate,
.truncate = vfs_truncate,
.truncate = vfs_truncate_dispatch,
.symlink = vfs_symlink,
.access = vfs_access,
.utimens = vfs_utimens,
@ -2516,6 +2532,7 @@ static struct fuse_operations vfs_oper =
.getxattr = vfs_getxattr,
.listxattr = vfs_listxattr,
.removexattr = vfs_removexattr,
.ftruncate = vfs_ftruncate,
#endif
};
@ -2537,35 +2554,65 @@ gint
main (gint argc, gchar *argv [])
{
struct fuse *fuse;
struct fuse_chan *ch;
struct fuse_session *se;
char *mountpoint;
int multithreaded;
int res;
struct fuse_cmdline_opts opts;
struct fuse_args args = FUSE_ARGS_INIT (argc, argv);
fuse = fuse_setup (argc, argv, &vfs_oper, sizeof (vfs_oper), &mountpoint,
&multithreaded, NULL /* user data */);
if (fuse_opt_parse (&args, NULL, NULL, NULL) == -1)
return 1;
if (fuse_parse_cmdline (&args, &opts) != 0)
return 1;
if (opts.show_version)
{
printf ("%s\n", PACKAGE_STRING);
fuse_lowlevel_version ();
return 0;
}
if (opts.show_help)
{
printf ("usage: %s [options] <mountpoint>\n\n", argv[0]);
printf ("FUSE options:\n");
fuse_cmdline_help ();
return 0;
}
if (!opts.mountpoint)
{
fprintf (stderr, "error: no mountpoint specified\n");
return 1;
}
fuse = fuse_new (&args, &vfs_oper, sizeof (vfs_oper), NULL /* user data */);
if (fuse == NULL)
return 1;
if (multithreaded)
res = fuse_loop_mt (fuse);
else
res = fuse_loop (fuse);
if (fuse_mount (fuse, opts.mountpoint) != 0)
return 1;
if (fuse_daemonize (opts.foreground) != 0)
return 1;
se = fuse_get_session (fuse);
ch = fuse_session_next_chan (se, NULL);
if (fuse_set_signal_handlers (se) != 0)
return 1;
if (opts.singlethread)
res = fuse_loop (fuse);
else
res = fuse_loop_mt (fuse, opts.clone_fd);
/* Ignore new signals during exit procedure in order to terminate properly */
set_custom_signal_handlers (SIG_IGN);
fuse_remove_signal_handlers (se);
fuse_unmount (mountpoint, ch);
fuse_unmount (fuse);
fuse_destroy (fuse);
free (mountpoint);
free (opts.mountpoint);
fuse_opt_free_args (&args);
if (res == -1)
return 1;
return 0;
return res;
}

View File

@ -58,6 +58,17 @@ create_proxy_for_icon (GVfsIcon *vfs_icon,
goto out;
connection = _g_dbus_connection_get_sync (mount_info->dbus_id, cancellable, &local_error);
if (connection == NULL && !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
{
g_dbus_error_strip_remote_error (local_error);
g_warning ("The peer-to-peer connection failed: %s. Falling back to the "
"session bus. Your application is probably missing "
"--filesystem=xdg-run/gvfsd privileges.", local_error->message);
g_clear_error (&local_error);
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, cancellable, &local_error);
}
if (connection == NULL)
goto out;
@ -196,20 +207,9 @@ async_proxy_new_cb (GObject *source_object,
}
static void
async_got_connection_cb (GDBusConnection *connection,
GError *io_error,
gpointer callback_data)
async_construct_proxy (GDBusConnection *connection,
AsyncPathCall *data)
{
AsyncPathCall *data = callback_data;
if (connection == NULL)
{
g_dbus_error_strip_remote_error (io_error);
g_task_return_error (data->task, io_error);
async_path_call_free (data);
return;
}
data->connection = g_object_ref (connection);
gvfs_dbus_mount_proxy_new (connection,
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
@ -220,6 +220,62 @@ async_got_connection_cb (GDBusConnection *connection,
data);
}
static void
bus_get_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
AsyncPathCall *data = user_data;
GDBusConnection *connection;
GError *error = NULL;
connection = g_bus_get_finish (res, &error);
if (connection == NULL)
{
g_dbus_error_strip_remote_error (error);
g_task_return_error (data->task, error);
async_path_call_free (data);
return;
}
async_construct_proxy (connection, data);
g_object_unref (connection);
}
static void
async_got_connection_cb (GDBusConnection *connection,
GError *io_error,
gpointer callback_data)
{
AsyncPathCall *data = callback_data;
if (connection == NULL)
{
g_dbus_error_strip_remote_error (io_error);
if (g_error_matches (io_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
{
g_task_return_error (data->task, g_error_copy (io_error));
async_path_call_free (data);
return;
}
g_warning ("The peer-to-peer connection failed: %s. Falling back to the "
"session bus. Your application is probably missing "
"--filesystem=xdg-run/gvfsd privileges.", io_error->message);
g_bus_get (G_BUS_TYPE_SESSION,
g_task_get_cancellable (data->task),
bus_get_cb,
data);
return;
}
async_construct_proxy (connection, data);
}
static void
async_got_mount_info (GMountInfo *mount_info,
gpointer _data,

View File

@ -245,7 +245,21 @@ g_vfs_decode_uri (const char *uri)
decoded->port = -1;
}
decoded->host = g_uri_unescape_segment (host_start, host_end, NULL);
/* Let's use the IPv6 address without unescaping. This is needed in order
* to prevent g_uri_unescape_segment failures when zone identifier
* separated by the bare % as it is defined by RFC 4007 is used here. The
* zone identifier should contain just ASCII characters as per RFC 4007,
* so it doesn't need to be unescaped. I intentionally don't support here
* what is suggested by RFC 6874, which changes the separator to %25 and
* at the same time, it suggests that the bare % sign should still be
* accepted in user interfaces. Such a thing would make this too complex
* and lead to various problems (e.g. it would not be clear what separator
* should be used for g_file_get_uri function)...
*/
if (*host_start == '[')
decoded->host = g_strndup (host_start, host_end - host_start);
else
decoded->host = g_uri_unescape_segment (host_start, host_end, NULL);
hier_part_start = authority_end;
}

View File

@ -33,7 +33,6 @@ sources = uri_parser_sources + uri_utils + files(
deps = [
gio_unix_dep,
libgvfscommon_dep,
libmetadata_dep,
]

View File

@ -129,19 +129,16 @@ gvfs_setup_debug_handler (void)
gboolean
gvfs_is_ipv6 (const char *host)
{
const char *p = host;
g_return_val_if_fail (host != NULL, FALSE);
if (*p != '[')
return FALSE;
while (++p)
if (!g_ascii_isxdigit (*p) && *p != ':')
break;
if (*p != ']' || *(p + 1) != '\0')
if (*host != '[' || host[strlen (host) - 1] != ']')
return FALSE;
return TRUE;
}
gchar *
gvfs_get_socket_dir (void)
{
return g_build_filename (g_get_user_runtime_dir (), "gvfsd", NULL);
}

View File

@ -31,6 +31,7 @@ void gvfs_set_debug (gboolean debugging
void gvfs_setup_debug_handler (void);
gboolean gvfs_is_ipv6 (const char *host);
gchar * gvfs_get_socket_dir (void);
G_END_DECLS

View File

@ -1,7 +1,7 @@
[Mount]
Type=admin
# Add a dummy argument after pkexec, or '/bin/sh -c' will eat the first argument in '$@'
Exec=/bin/sh -c 'pkexec @libexecdir@/gvfsd-admin "$@" --address $DBUS_SESSION_BUS_ADDRESS' gvfsd-admin
Exec=/bin/sh -c 'pkexec @libexecdir@/gvfsd-admin "$@" --address $DBUS_SESSION_BUS_ADDRESS --dir $XDG_RUNTIME_DIR' gvfsd-admin
AutoMount=false
DBusName=org.gtk.vfs.mountpoint_admin
MountPerClient=true

View File

@ -1,7 +1,9 @@
[Unit]
Description=Virtual filesystem service
PartOf=graphical-session.target
[Service]
ExecStart=@libexecdir@/gvfsd
Type=dbus
BusName=org.gtk.vfs.Daemon
Slice=session.slice

View File

@ -1053,22 +1053,17 @@ g_vfs_afp_server_login (GVfsAfpServer *server,
gboolean aborted;
g_free (prompt);
g_clear_error (&err);
str = g_string_new (NULL);
if (err)
{
g_string_append_printf (str, "%s\n", err->message);
g_clear_error (&err);
}
/* create prompt */
if (initial_user)
/* translators: %s here is the hostname */
g_string_append_printf (str, _("Enter your password for the server “%s”."), server_name);
/* Translators: the first %s is the username, the second the host name */
g_string_append_printf (str, _("Authentication Required\nEnter password for “%s” on “%s”:"), initial_user, server_name);
else
/* translators: %s here is the hostname */
g_string_append_printf (str, _("Enter your name and password for the server “%s”."), server_name);
/* Translators: %s here is the hostname */
g_string_append_printf (str, _("Authentication Required\nEnter user and password for “%s”:"), server_name);
prompt = g_string_free (str, FALSE);

View File

@ -524,6 +524,8 @@ get_thumbnail_attributes (const char *uri,
GChecksum *checksum;
char *filename;
char *basename;
const char *size_dirs[4] = { "xx-large", "x-large", "large", "normal" };
gsize i;
checksum = g_checksum_new (G_CHECKSUM_MD5);
g_checksum_update (checksum, (const guchar *) uri, strlen (uri));
@ -531,34 +533,31 @@ get_thumbnail_attributes (const char *uri,
basename = g_strconcat (g_checksum_get_string (checksum), ".png", NULL);
g_checksum_free (checksum);
filename = g_build_filename (g_get_user_cache_dir (),
"thumbnails", "large", basename,
NULL);
for (i = 0; i < G_N_ELEMENTS (size_dirs); i++)
{
filename = g_build_filename (g_get_user_cache_dir (),
"thumbnails", size_dirs[i], basename,
NULL);
if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
break;
if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
g_clear_pointer (&filename, g_free);
}
if (filename)
g_file_info_set_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH, filename);
else
{
g_free (filename);
filename = g_build_filename (g_get_user_cache_dir (),
"thumbnails", "normal", basename,
"thumbnails", "fail",
"gnome-thumbnail-factory",
basename,
NULL);
if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
g_file_info_set_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH, filename);
else
{
g_free (filename);
filename = g_build_filename (g_get_user_cache_dir (),
"thumbnails", "fail",
"gnome-thumbnail-factory",
basename,
NULL);
if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED, TRUE);
}
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED, TRUE);
}
g_free (basename);
g_free (filename);
}

View File

@ -95,6 +95,15 @@ check_permission (GVfsBackendAdmin *self,
invocation = dbus_job->invocation;
connection = g_dbus_method_invocation_get_connection (invocation);
credentials = g_dbus_connection_get_peer_credentials (connection);
if (!credentials)
{
g_warning ("The admin backend doesn't work with the session bus "
"fallback. Your application is probably missing "
"--filesystem=xdg-run/gvfsd privileges.");
g_vfs_job_failed_literal (job, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Operation not supported"));
return FALSE;
}
pid = g_credentials_get_unix_pid (credentials, &error);
if (error != NULL)
@ -807,6 +816,36 @@ do_move (GVfsBackend *backend,
complete_job (job, error);
}
static void
do_copy (GVfsBackend *backend,
GVfsJobCopy *copy_job,
const char *source,
const char *destination,
GFileCopyFlags flags,
GFileProgressCallback progress_callback,
gpointer progress_callback_data)
{
GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
GVfsJob *job = G_VFS_JOB (copy_job);
GError *error = NULL;
GFile *src_file, *dst_file;
if (!check_permission (self, job))
return;
src_file = g_file_new_for_path (source);
dst_file = g_file_new_for_path (destination);
g_file_copy (src_file, dst_file, flags,
job->cancellable,
progress_callback, progress_callback_data,
&error);
g_object_unref (src_file);
g_object_unref (dst_file);
complete_job (job, error);
}
static void
do_pull (GVfsBackend *backend,
GVfsJobPull *pull_job,
@ -852,6 +891,40 @@ do_pull (GVfsBackend *backend,
complete_job (job, error);
}
static void
do_push (GVfsBackend *backend,
GVfsJobPush *push_job,
const char *destination,
const char *local_path,
GFileCopyFlags flags,
gboolean remove_source,
GFileProgressCallback progress_callback,
gpointer progress_callback_data)
{
GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
GVfsJob *job = G_VFS_JOB (push_job);
GError *error = NULL;
GFile *src_file, *dst_file;
if (!check_permission (self, job))
return;
src_file = g_file_new_for_path (local_path);
dst_file = g_file_new_for_path (destination);
if (remove_source)
g_file_move (src_file, dst_file, flags, job->cancellable,
progress_callback, progress_callback_data, &error);
else
g_file_copy (src_file, dst_file, flags, job->cancellable,
progress_callback, progress_callback_data, &error);
g_object_unref (src_file);
g_object_unref (dst_file);
complete_job (job, error);
}
static void
do_query_settable_attributes (GVfsBackend *backend,
GVfsJobQueryAttributes *query_job,
@ -972,7 +1045,9 @@ g_vfs_backend_admin_class_init (GVfsBackendAdminClass * klass)
backend_class->set_attribute = do_set_attribute;
backend_class->delete = do_delete;
backend_class->move = do_move;
backend_class->copy = do_copy;
backend_class->pull = do_pull;
backend_class->push = do_push;
backend_class->query_settable_attributes = do_query_settable_attributes;
backend_class->query_writable_namespaces = do_query_writable_namespaces;
}
@ -1028,8 +1103,10 @@ acquire_caps (uid_t uid)
}
static char *session_address = NULL;
static char *runtime_dir = NULL;
static GOptionEntry entries[] = {
{ "address", 0, 0, G_OPTION_ARG_STRING, &session_address, "DBus session address", NULL },
{ "dir", 0, 0, G_OPTION_ARG_STRING, &runtime_dir, "Runtime dir", NULL },
{ NULL }
};
@ -1068,4 +1145,7 @@ g_vfs_backend_admin_pre_setup (int *argc,
acquire_caps (uid);
g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_address, TRUE);
if (runtime_dir)
g_setenv ("XDG_RUNTIME_DIR", runtime_dir, TRUE);
}

View File

@ -574,7 +574,7 @@ g_vfs_backend_afc_mount (GVfsBackend *backend,
/* translators:
* %s is the device name. 'Try again' is the caption of the button
* shown in the dialog which is defined above. */
message = g_strdup_printf (_("The device “%s” is locked. Enter the passcode on the device and click “Try again”."), display_name);
message = g_strdup_printf (_("Device Locked\nThe device “%s” is locked.\n\nEnter the passcode on the device and click “Try again”."), display_name);
}
else if (lerr == LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING)
{
@ -582,7 +582,7 @@ g_vfs_backend_afc_mount (GVfsBackend *backend,
* %s is the device name. 'Try again' is the caption of the button
* shown in the dialog which is defined above. 'Trust' is the caption
* of the button shown in the device. */
message = g_strdup_printf (_("The device “%s” is not trusted yet. Select “Trust” on the device and click “Try again”."), display_name);
message = g_strdup_printf (_("Untrusted Device\nThe device “%s” is not trusted yet.\n\nSelect “Trust” on the device and click “Try again”."), display_name);
}
else
g_assert_not_reached ();
@ -2186,6 +2186,7 @@ g_vfs_backend_afc_query_fs_info (GVfsBackend *backend,
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "afc");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, FALSE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_IF_ALWAYS);
if (!self->connected)
{

View File

@ -1821,6 +1821,7 @@ try_query_fs_info (GVfsBackend *backend,
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "afp");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_IF_ALWAYS);
if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE))
vol_bitmap |= AFP_VOLUME_BITMAP_EXT_BYTES_TOTAL_BIT;

View File

@ -540,6 +540,7 @@ try_query_fs_info (GVfsBackend *backend,
{
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "afp");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_NEVER);
g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE;
}

View File

@ -1033,6 +1033,7 @@ try_query_fs_info (GVfsBackend *backend,
{
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "burn");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, FALSE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_IF_LOCAL);
g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE;
}

View File

@ -1424,6 +1424,7 @@ try_query_fs_info (GVfsBackend *backend,
{
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "computer");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, FALSE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_NEVER);
g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE;
}

File diff suppressed because it is too large Load Diff

View File

@ -842,6 +842,7 @@ try_query_fs_info (GVfsBackend *backend,
{
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "dns-sd");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_NEVER);
g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE;
}

View File

@ -527,10 +527,10 @@ restart:
{
if (ftp->has_initial_user)
/* Translators: the first %s is the username, the second the host name */
prompt = g_strdup_printf (_("Enter password for %s on %s"), ftp->user, ftp->host_display_name);
prompt = g_strdup_printf (_("Authentication Required\nEnter password for %s on %s”:"), ftp->user, ftp->host_display_name);
else
/* translators: %s here is the hostname */
prompt = g_strdup_printf (_("Enter password for %s"), ftp->host_display_name);
/* Translators: %s here is the hostname */
prompt = g_strdup_printf (_("Authentication Required\nEnter user and password for %s”:"), ftp->host_display_name);
}
flags = G_ASK_PASSWORD_NEED_PASSWORD;
@ -1533,47 +1533,6 @@ out:
g_vfs_ftp_task_done (&task);
}
static gssize
ftp_output_stream_splice (GOutputStream *output,
GInputStream *input,
goffset total_size,
GFileProgressCallback progress_callback,
gpointer progress_callback_data,
GCancellable *cancellable,
GError **error)
{
gssize n_read, n_written;
gssize bytes_copied;
char buffer[8192], *p;
bytes_copied = 0;
for (;;)
{
n_read = g_input_stream_read (input, buffer, sizeof (buffer), cancellable, error);
if (n_read == -1)
return -1;
if (n_read == 0)
break;
p = buffer;
while (n_read > 0)
{
n_written = g_output_stream_write (output, p, n_read, cancellable, error);
if (n_written == -1)
return -1;
p += n_written;
n_read -= n_written;
bytes_copied += n_written;
if (progress_callback)
progress_callback (bytes_copied, total_size, progress_callback_data);
}
}
return bytes_copied;
}
static void
do_pull_improve_error_message (GVfsFtpTask *task,
GFile *dest,
@ -1657,6 +1616,15 @@ do_pull (GVfsBackend * backend,
src = g_vfs_ftp_file_new_from_gvfs (ftp, source);
dest = g_file_new_for_path (local_path);
if (remove_source && (flags & G_FILE_COPY_NO_FALLBACK_FOR_MOVE))
{
g_set_error_literal (&task.error,
G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
_("Operation not supported"));
goto out;
}
/* If the source is a symlink, then it needs to be handled specially. */
if (flags & G_FILE_COPY_NOFOLLOW_SYMLINKS)
{
@ -1726,18 +1694,19 @@ do_pull (GVfsBackend * backend,
}
input = g_io_stream_get_input_stream (g_vfs_ftp_connection_get_data_stream (task.conn));
ftp_output_stream_splice (output,
input,
total_size,
progress_callback,
progress_callback_data,
task.cancellable,
&task.error);
gvfs_output_stream_splice (output,
input,
G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
total_size,
progress_callback,
progress_callback_data,
task.cancellable,
&task.error);
g_vfs_ftp_task_close_data_connection (&task);
g_vfs_ftp_task_receive (&task, 0, NULL);
g_object_unref (output);
if (remove_source)
if (!g_vfs_ftp_task_is_in_error (&task) && remove_source)
{
g_vfs_ftp_task_send (&task,
G_VFS_FTP_PASS_500,
@ -1759,6 +1728,7 @@ try_query_fs_info (GVfsBackend *backend,
{
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "ftp");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_IF_ALWAYS);
g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE;
}

View File

@ -49,17 +49,22 @@
#include "gvfsjobsetdisplayname.h"
#include "gvfsjobwrite.h"
#include "gvfsmonitor.h"
#include "gvfsdaemonutils.h"
struct _GVfsBackendGoogle
{
GVfsBackend parent;
GDataDocumentsService *service;
GDataEntry *root;
GDataEntry *home;
GDataEntry *shared_with_me_dir;
GDataEntry *shared_drives_dir;
GHashTable *entries; /* gchar *entry_id -> GDataEntry */
GHashTable *dir_entries; /* DirEntriesKey -> GDataEntry */
GHashTable *dir_timestamps; /* gchar *entry_id -> gint64 *timestamp */
GHashTable *monitors;
GList *dir_collisions;
GList *shared_drives;
GRecMutex mutex; /* guards cache */
GoaClient *client;
gchar *account_identity;
@ -79,14 +84,16 @@ G_DEFINE_TYPE(GVfsBackendGoogle, g_vfs_backend_google, G_VFS_TYPE_BACKEND)
#define CONTENT_TYPE_PREFIX_GOOGLE "application/vnd.google-apps"
#define MAX_RESULTS 50
#define REBUILD_ENTRIES_TIMEOUT 60 /* s */
#define URI_PREFIX "https://www.googleapis.com/drive/v2/files/"
#define SOURCE_ID_PROPERTY_KEY "GVfsSourceID"
#define PARENT_ID_PROPERTY_KEY "GVfsParentID"
#define ROOT_ID "GVfsRoot"
#define SHARED_WITH_ME_ID "GVfsSharedWithMe"
#define SHARED_DRIVES_ID "GVfsSharedDrives"
/* ---------------------------------------------------------------------------------------------------- */
typedef struct
@ -441,6 +448,12 @@ is_owner (GVfsBackendGoogle *self, GDataEntry *entry)
return FALSE;
}
static gboolean
is_shared_with_me (GDataEntry *entry)
{
return gdata_documents_entry_get_shared_with_me_date (GDATA_DOCUMENTS_ENTRY (entry)) > 0;
}
/* ---------------------------------------------------------------------------------------------------- */
static const gchar *
@ -717,6 +730,27 @@ insert_entry (GVfsBackendGoogle *self,
insert_entry_full (self, entry, TRUE);
}
static void
insert_custom_entry (GVfsBackendGoogle *self,
GDataEntry *entry,
const gchar *parent_id)
{
DirEntriesKey *k;
const gchar *id;
const gchar *title;
id = gdata_entry_get_id (entry);
title = gdata_entry_get_title (entry);
g_hash_table_insert (self->entries, g_strdup (id), g_object_ref (entry));
k = dir_entries_key_new (id, parent_id);
g_hash_table_insert (self->dir_entries, k, g_object_ref (entry));
k = dir_entries_key_new (title, parent_id);
g_hash_table_insert (self->dir_entries, k, g_object_ref (entry));
}
static void
remove_entry_full (GVfsBackendGoogle *self,
GDataEntry *entry,
@ -733,6 +767,9 @@ remove_entry_full (GVfsBackendGoogle *self,
g_hash_table_remove (self->entries, id);
if (is_shared_with_me (entry))
g_hash_table_remove (self->dir_timestamps, SHARED_WITH_ME_ID);
parent_ids = get_parent_ids (self, entry);
for (ll = parent_ids; ll != NULL; ll = ll->next)
{
@ -875,6 +912,9 @@ is_entry_valid (GDataEntry *entry)
gint64 *timestamp;
timestamp = g_object_get_data (G_OBJECT (entry), "timestamp");
if (timestamp == NULL)
return TRUE;
return (g_get_real_time () - *timestamp < REBUILD_ENTRIES_TIMEOUT * G_USEC_PER_SEC);
}
@ -883,6 +923,9 @@ is_dir_listing_valid (GVfsBackendGoogle *self, GDataEntry *parent)
{
gint64 *timestamp;
if (parent == self->root)
return TRUE;
timestamp = g_hash_table_lookup (self->dir_timestamps, gdata_entry_get_id (parent));
if (timestamp != NULL)
return (g_get_real_time () - *timestamp < REBUILD_ENTRIES_TIMEOUT * G_USEC_PER_SEC);
@ -890,6 +933,48 @@ is_dir_listing_valid (GVfsBackendGoogle *self, GDataEntry *parent)
return FALSE;
}
static void
rebuild_shared_drives_dir (GVfsBackendGoogle *self,
GCancellable *cancellable,
GError **error)
{
GDataAuthorizationDomain *auth_domain;
GList *l;
gint64 *timestamp;
auth_domain = gdata_documents_service_get_primary_authorization_domain ();
remove_dir (self, self->shared_drives_dir);
for (l = self->shared_drives; l != NULL; l = l->next)
{
GDataDocumentsDrive *drive = GDATA_DOCUMENTS_DRIVE (l->data);
GDataEntry *entry = NULL;
entry = gdata_service_query_single_entry (GDATA_SERVICE (self->service),
auth_domain,
gdata_entry_get_id (GDATA_ENTRY (drive)),
NULL,
GDATA_TYPE_DOCUMENTS_FOLDER,
cancellable,
error);
if (entry == NULL)
return;
/* Replace "My Drive" title by the real name of the Drive. */
gdata_entry_set_title (entry, gdata_documents_drive_get_name (drive));
insert_custom_entry (self, entry, SHARED_DRIVES_ID);
g_object_unref (entry);
}
timestamp = g_new (gint64, 1);
*timestamp = g_get_real_time ();
g_hash_table_insert (self->dir_timestamps, SHARED_DRIVES_ID, timestamp);
}
static void
rebuild_dir (GVfsBackendGoogle *self,
GDataEntry *parent,
@ -902,11 +987,20 @@ rebuild_dir (GVfsBackendGoogle *self,
gchar *search;
gchar *parent_id;
if (parent == self->shared_drives_dir)
{
rebuild_shared_drives_dir (self, cancellable, error);
return;
}
/* g_strdup() is necessary to prevent segfault because gdata_entry_get_id() calls g_free() */
parent_id = g_strdup (gdata_entry_get_id (parent));
search = g_strdup_printf ("'%s' in parents", parent_id);
query = gdata_documents_query_new_with_limits (search, 1, MAX_RESULTS);
if (parent == self->shared_with_me_dir)
search = g_strdup ("sharedWithMe");
else
search = g_strdup_printf ("'%s' in parents", parent_id);
query = gdata_documents_query_new_with_limits (search, 1, G_MAXUINT);
gdata_documents_query_set_show_folders (query, TRUE);
g_free (search);
@ -972,10 +1066,16 @@ resolve_child (GVfsBackendGoogle *self,
GDataEntry *entry;
const gchar *parent_id;
GError *local_error = NULL;
gboolean is_shared_with_me_dir = (parent == self->shared_with_me_dir);
parent_id = gdata_entry_get_id (parent);
k = dir_entries_key_new (basename, parent_id);
entry = g_hash_table_lookup (self->dir_entries, k);
if (is_shared_with_me_dir)
entry = g_hash_table_lookup (self->entries, basename);
else
entry = g_hash_table_lookup (self->dir_entries, k);
if ((entry == NULL && !is_dir_listing_valid (self, parent)) ||
(entry != NULL && !is_entry_valid (entry)))
{
@ -986,7 +1086,10 @@ resolve_child (GVfsBackendGoogle *self,
goto out;
}
entry = g_hash_table_lookup (self->dir_entries, k);
if (is_shared_with_me_dir)
entry = g_hash_table_lookup (self->entries, basename);
else
entry = g_hash_table_lookup (self->dir_entries, k);
}
if (entry == NULL)
@ -1036,8 +1139,26 @@ resolve (GVfsBackendGoogle *self,
ret_val = resolve_child (self, parent, basename, cancellable, &local_error);
if (ret_val == NULL)
{
g_propagate_error (error, local_error);
goto out;
/* This fallback provides volatile entries for URIs which was used
* before My Drive folder was added in the root. */
if (parent == self->root)
{
g_clear_error (&local_error);
ret_val = resolve_child (self, self->home, basename, cancellable, &local_error);
if (ret_val != NULL && out_path != NULL)
{
gchar *tmp;
tmp = g_build_path ("/", *out_path, gdata_entry_get_id (self->home), NULL);
g_free (*out_path);
*out_path = tmp;
}
}
if (ret_val == NULL)
{
g_propagate_error (error, local_error);
goto out;
}
}
if (out_path != NULL)
@ -1213,6 +1334,10 @@ build_file_info (GVfsBackendGoogle *self,
gint64 ctime;
gint64 mtime;
gsize i;
gboolean is_shared_with_me_dir = (entry == self->shared_with_me_dir);
gboolean is_home = (entry == self->home);
gboolean is_shared_drives_dir = (entry == self->shared_drives_dir);
gboolean can_edit;
if (GDATA_IS_DOCUMENTS_FOLDER (entry))
is_folder = TRUE;
@ -1226,7 +1351,11 @@ build_file_info (GVfsBackendGoogle *self,
symlink_name = g_path_get_basename (filename);
}
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, !is_root);
/* TODO: It is not always possible to rename, delete, or list children.
* However, the proper implementation of gdata_documents_entry_can_rename/
* _delete/_list_children would require port to Google Drive API v3. */
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME,
!is_root && !is_home && !is_shared_with_me_dir && !is_shared_drives_dir);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, is_folder);
@ -1234,7 +1363,13 @@ build_file_info (GVfsBackendGoogle *self,
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_VOLATILE, is_symlink);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, !is_root);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE,
!is_root && !is_home && !is_shared_with_me_dir && !is_shared_drives_dir);
can_edit = gdata_documents_entry_can_edit (GDATA_DOCUMENTS_ENTRY (entry));
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
!is_root && !is_shared_with_me_dir && !is_shared_drives_dir && can_edit);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ, TRUE);
if (is_folder)
{
@ -1286,10 +1421,28 @@ build_file_info (GVfsBackendGoogle *self,
g_file_info_set_content_type (info, content_type);
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE, content_type);
icon = g_content_type_get_icon (content_type);
g_file_info_set_icon (info, icon);
if (is_home)
{
icon = g_themed_icon_new_with_default_fallbacks ("user-home");
symbolic_icon = g_themed_icon_new_with_default_fallbacks ("user-home-symbolic");
}
else if (is_shared_with_me_dir)
{
icon = g_themed_icon_new_with_default_fallbacks ("folder-publicshare");
symbolic_icon = g_themed_icon_new_with_default_fallbacks ("folder-publicshare-symbolic");
}
else if (is_shared_drives_dir)
{
icon = g_themed_icon_new_with_default_fallbacks ("folder-remote");
symbolic_icon = g_themed_icon_new_with_default_fallbacks ("folder-remote-symbolic");
}
else
{
icon = g_content_type_get_icon (content_type);
symbolic_icon = g_content_type_get_symbolic_icon (content_type);
}
symbolic_icon = g_content_type_get_symbolic_icon (content_type);
g_file_info_set_icon (info, icon);
g_file_info_set_symbolic_icon (info, symbolic_icon);
g_object_unref (icon);
@ -1298,13 +1451,12 @@ build_file_info (GVfsBackendGoogle *self,
g_file_info_set_file_type (info, file_type);
if (is_root)
goto out;
id = gdata_entry_get_id (entry);
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILE, id);
if (is_symlink)
if (is_root)
name = "/";
else if (is_symlink)
name = symlink_name;
else
name = id;
@ -1315,6 +1467,9 @@ build_file_info (GVfsBackendGoogle *self,
g_file_info_set_display_name (info, title);
g_file_info_set_edit_name (info, title);
if (is_root || is_home || is_shared_with_me_dir || is_shared_drives_dir)
goto out;
copy_name = generate_copy_name (self, entry, entry_path);
/* Sanitize copy-name by replacing slashes with dashes. This is
@ -1461,6 +1616,14 @@ g_vfs_backend_google_copy (GVfsBackend *_self,
goto out;
}
if (source_entry == self->root || source_parent == self->root || source_parent == self->shared_drives_dir ||
destination_parent == self->root || destination_parent == self->shared_with_me_dir ||
destination_parent == self->shared_drives_dir)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
goto out;
}
id = gdata_entry_get_id (source_entry);
title = gdata_entry_get_title (source_entry);
source_parent_id = gdata_entry_get_id (source_parent);
@ -1875,6 +2038,14 @@ g_vfs_backend_google_move (GVfsBackend *_self,
goto out;
}
if (source_entry == self->root || source_parent == self->root || source_parent == self->shared_drives_dir ||
destination_parent == self->root || destination_parent == self->shared_with_me_dir ||
destination_parent == self->shared_drives_dir)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
goto out;
}
source_id = gdata_entry_get_id (source_entry);
source_parent_id = gdata_entry_get_id (source_parent);
destination_parent_id = gdata_entry_get_id (destination_parent);
@ -2288,7 +2459,18 @@ g_vfs_backend_google_delete (GVfsBackend *_self,
/* g_strdup() is necessary to prevent segfault because gdata_entry_get_id() calls g_free() */
id = g_strdup (gdata_entry_get_id (entry));
if (GDATA_IS_DOCUMENTS_FOLDER (entry))
parent = resolve_dir (self, filename, cancellable, NULL, NULL, &error);
if (error != NULL)
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
g_error_free (error);
goto out;
}
/* The G_IO_ERROR_NOT_EMPTY error is not intentionally returned for folders in
* Shared with me folder, because the recursive delete would not work, or could
* really remove the files from the original folder also for the owner... */
if (GDATA_IS_DOCUMENTS_FOLDER (entry) && parent != self->shared_with_me_dir)
{
GHashTableIter iter;
DirEntriesKey *key;
@ -2318,17 +2500,10 @@ g_vfs_backend_google_delete (GVfsBackend *_self,
}
}
parent = resolve_dir (self, filename, cancellable, NULL, NULL, &error);
if (error != NULL)
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
g_error_free (error);
goto out;
}
g_debug (" entry path: %s\n", entry_path);
if (entry == self->root)
if (entry == self->root || entry == self->home || entry == self->shared_with_me_dir ||
entry == self->shared_drives_dir || parent == self->shared_drives_dir)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
goto out;
@ -2342,7 +2517,45 @@ g_vfs_backend_google_delete (GVfsBackend *_self,
parent_ids = get_parent_ids (self, entry);
parent_ids_len = g_list_length (parent_ids);
if (parent_ids_len > 1 || !is_owner (self, GDATA_ENTRY (entry)))
/* The files in Shared with me folder doesn't have a parent and also we don't
* have enough permissions to physically delete them. But they can be removed
* by removal of our permissions... */
if (parent == self->shared_with_me_dir)
{
GDataFeed *acl_feed;
acl_feed = gdata_access_handler_get_rules (GDATA_ACCESS_HANDLER (entry),
GDATA_SERVICE (self->service),
cancellable,
NULL, NULL, &error);
if (error == NULL)
{
GDataGoaAuthorizer *goa_authorizer;
GoaAccount *account;
const gchar *account_identity;
GDataAuthorizationDomain *auth_domain;
GList *entries;
goa_authorizer = GDATA_GOA_AUTHORIZER (gdata_service_get_authorizer (GDATA_SERVICE (self->service)));
account = goa_object_peek_account (gdata_goa_authorizer_get_goa_object (goa_authorizer));
account_identity = goa_account_get_identity (account);
auth_domain = gdata_documents_service_get_primary_authorization_domain ();
for (entries = gdata_feed_get_entries (acl_feed); entries != NULL; entries = entries->next)
{
const gchar *scope_value = NULL;
GDataAccessRule *rule = GDATA_ACCESS_RULE (entries->data);
gdata_access_rule_get_scope (rule, NULL, &scope_value);
if (g_strcmp0 (scope_value, account_identity) == 0)
gdata_service_delete_entry (GDATA_SERVICE (self->service), auth_domain, GDATA_ENTRY (rule), NULL, &error);
}
g_object_unref (acl_feed);
}
}
else if (parent_ids_len > 1 || !is_owner (self, GDATA_ENTRY (entry)))
{
/* gdata_documents_service_remove_entry_from_folder () returns the
* updated entry variable provided as argument with an increased ref.
@ -2400,10 +2613,12 @@ g_vfs_backend_google_enumerate (GVfsBackend *_self,
GVfsBackendGoogle *self = G_VFS_BACKEND_GOOGLE (_self);
GCancellable *cancellable = G_VFS_JOB (job)->cancellable;
GDataEntry *entry;
GDataEntry *child;
GError *error;
GHashTableIter iter;
char *parent_path;
char *id = NULL;
gboolean is_shared_with_me_dir;
g_rec_mutex_lock (&self->mutex);
g_debug ("+ enumerate: %s\n", filename);
@ -2439,18 +2654,20 @@ g_vfs_backend_google_enumerate (GVfsBackend *_self,
/* g_strdup() is necessary to prevent segfault because gdata_entry_get_id() calls g_free() */
id = g_strdup (gdata_entry_get_id (entry));
is_shared_with_me_dir = (entry == self->shared_with_me_dir);
g_hash_table_iter_init (&iter, self->entries);
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &entry))
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &child))
{
DirEntriesKey *k;
GDataEntry *child;
gchar *child_id;
/* g_strdup() is necessary to prevent segfault because gdata_entry_get_id() calls g_free() */
child_id = g_strdup (gdata_entry_get_id (entry));
child_id = g_strdup (gdata_entry_get_id (child));
k = dir_entries_key_new (child_id, id);
if ((child = g_hash_table_lookup (self->dir_entries, k)) != NULL)
if ((is_shared_with_me_dir && is_shared_with_me (child)) ||
(!is_shared_with_me_dir && g_hash_table_lookup (self->dir_entries, k) != NULL))
{
GFileInfo *info;
gchar *entry_path;
@ -2468,7 +2685,7 @@ g_vfs_backend_google_enumerate (GVfsBackend *_self,
info = g_file_info_new ();
entry_path = g_build_path ("/", parent_path, child_id, NULL);
child_filename = g_build_filename (filename, child_id, NULL);
build_file_info (self, entry, flags, info, matcher, child_filename, entry_path, NULL);
build_file_info (self, child, flags, info, matcher, child_filename, entry_path, NULL);
g_vfs_job_enumerate_add_info (job, info);
g_object_unref (info);
g_free (entry_path);
@ -2619,6 +2836,48 @@ g_vfs_backend_google_make_directory (GVfsBackend *_self,
/* ---------------------------------------------------------------------------------------------------- */
static GList *
query_shared_drives (GVfsBackendGoogle *self,
GCancellable *cancellable,
GError **error)
{
GDataDocumentsDriveQuery *query;
GList *shared_drives = NULL;
query = gdata_documents_drive_query_new (NULL);
while (TRUE)
{
GDataDocumentsFeed *feed;
GList *entries;
feed = gdata_documents_service_query_drives (self->service,
query,
cancellable,
NULL,
NULL,
error);
if (feed == NULL)
break;
entries = gdata_feed_get_entries (GDATA_FEED (feed));
if (entries == NULL)
{
g_object_unref (feed);
break;
}
shared_drives = g_list_concat (shared_drives,
g_list_copy_deep (entries, (GCopyFunc) g_object_ref, NULL));
gdata_query_next_page (GDATA_QUERY (query));
g_object_unref (feed);
}
g_clear_object (&query);
return shared_drives;
}
static void
g_vfs_backend_google_mount (GVfsBackend *_self,
GVfsJobMount *job,
@ -2687,8 +2946,11 @@ g_vfs_backend_google_mount (GVfsBackend *_self,
auth_domain = gdata_documents_service_get_primary_authorization_domain ();
self->root = GDATA_ENTRY (gdata_documents_folder_new (ROOT_ID));
gdata_entry_set_title (self->root, self->account_identity);
error = NULL;
self->root = gdata_service_query_single_entry (GDATA_SERVICE (self->service),
self->home = gdata_service_query_single_entry (GDATA_SERVICE (self->service),
auth_domain,
"root",
NULL,
@ -2702,6 +2964,32 @@ g_vfs_backend_google_mount (GVfsBackend *_self,
g_error_free (error);
goto out;
}
insert_custom_entry (self, self->home, ROOT_ID);
self->shared_with_me_dir = GDATA_ENTRY (gdata_documents_folder_new (SHARED_WITH_ME_ID));
/* Translators: This is the "Shared with me" folder on https://drive.google.com. */
gdata_entry_set_title (self->shared_with_me_dir, _("Shared with me"));
insert_custom_entry (self, self->shared_with_me_dir, ROOT_ID);
self->shared_drives = query_shared_drives (self, cancellable, &error);
if (error != NULL)
{
sanitize_error (&error);
g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
g_error_free (error);
goto out;
}
if (self->shared_drives)
{
self->shared_drives_dir = GDATA_ENTRY (gdata_documents_folder_new (SHARED_DRIVES_ID));
/* Translators: This is the "Shared drives" folder on https://drive.google.com. */
gdata_entry_set_title (self->shared_drives_dir, _("Shared drives"));
insert_custom_entry (self, self->shared_drives_dir, ROOT_ID);
}
/* TODO: Make it work with GOA volume monitor resp. shadow mounts. */
g_vfs_backend_set_default_location (_self, gdata_entry_get_id (self->home));
g_vfs_backend_set_mount_spec (_self, spec);
g_vfs_backend_set_display_name (_self, self->account_identity);
@ -2772,11 +3060,16 @@ g_vfs_backend_google_push (GVfsBackend *_self,
gchar *entry_path = NULL;
gchar *parent_path = NULL;
gchar *local_file_title = NULL;
goffset size;
g_rec_mutex_lock (&self->mutex);
g_debug ("+ push: %s -> %s, %d\n", local_path, destination, flags);
if (remove_source && (flags & G_FILE_COPY_NO_FALLBACK_FOR_MOVE))
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
goto out;
}
if (flags & G_FILE_COPY_BACKUP)
{
/* Return G_IO_ERROR_NOT_SUPPORTED instead of
@ -2793,7 +3086,8 @@ g_vfs_backend_google_push (GVfsBackend *_self,
info = g_file_query_info (local_file,
G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE","
G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME","
G_FILE_ATTRIBUTE_STANDARD_TYPE,
G_FILE_ATTRIBUTE_STANDARD_TYPE","
G_FILE_ATTRIBUTE_STANDARD_SIZE,
G_FILE_QUERY_INFO_NONE,
cancellable,
&error);
@ -2815,6 +3109,12 @@ g_vfs_backend_google_push (GVfsBackend *_self,
goto out;
}
if (destination_parent == self->root || destination_parent == self->shared_with_me_dir || destination_parent == self->shared_drives_dir)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
goto out;
}
existing_entry = resolve_child (self, destination_parent, destination_basename, cancellable, NULL);
if (existing_entry != NULL)
{
@ -2969,11 +3269,14 @@ g_vfs_backend_google_push (GVfsBackend *_self,
}
error = NULL;
g_output_stream_splice (G_OUTPUT_STREAM (ostream),
G_INPUT_STREAM (istream),
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
cancellable,
&error);
gvfs_output_stream_splice (G_OUTPUT_STREAM (ostream),
G_INPUT_STREAM (istream),
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
g_file_info_get_size (info),
progress_callback,
progress_callback_data,
cancellable,
&error);
if (error != NULL)
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
@ -3012,8 +3315,6 @@ g_vfs_backend_google_push (GVfsBackend *_self,
}
}
size = gdata_documents_entry_get_file_size (GDATA_DOCUMENTS_ENTRY (new_document));
g_vfs_job_progress_callback (size, size, job);
g_vfs_job_succeeded (G_VFS_JOB (job));
out:
@ -3089,6 +3390,7 @@ g_vfs_backend_google_query_fs_info (GVfsBackend *_self,
type = g_mount_spec_get_type (spec);
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, type);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_IF_ALWAYS);
if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE) ||
g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_FREE) ||
@ -3441,6 +3743,7 @@ g_vfs_backend_google_set_display_name (GVfsBackend *_self,
GDataAuthorizationDomain *auth_domain;
GDataEntry *entry;
GDataEntry *new_entry = NULL;
GDataEntry *parent;
GError *error;
gchar *entry_path = NULL;
@ -3458,7 +3761,15 @@ g_vfs_backend_google_set_display_name (GVfsBackend *_self,
g_debug (" entry path: %s\n", entry_path);
if (entry == self->root)
parent = resolve_dir (self, filename, cancellable, NULL, NULL, &error);
if (error != NULL)
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
g_error_free (error);
goto out;
}
if (entry == self->root || entry == self->home || entry == self->shared_with_me_dir || parent == self->shared_drives_dir)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
goto out;
@ -3536,6 +3847,12 @@ g_vfs_backend_google_create (GVfsBackend *_self,
g_debug (" parent path: %s\n", parent_path);
if (parent == self->root || parent == self->shared_with_me_dir || parent == self->shared_drives_dir)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
goto out;
}
existing_entry = resolve_child (self, parent, basename, cancellable, NULL);
if (existing_entry != NULL)
{
@ -3645,6 +3962,12 @@ g_vfs_backend_google_replace (GVfsBackend *_self,
g_debug (" parent path: %s\n", parent_path);
if (parent == self->root || parent == self->shared_with_me_dir || parent == self->shared_drives_dir)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
goto out;
}
existing_entry = resolve_child (self, parent, basename, cancellable, NULL);
if (existing_entry != NULL)
{
@ -3875,6 +4198,10 @@ g_vfs_backend_google_dispose (GObject *_self)
g_clear_object (&self->service);
g_clear_object (&self->root);
g_clear_object (&self->home);
g_clear_object (&self->shared_with_me_dir);
g_clear_object (&self->shared_drives_dir);
g_clear_list (&self->shared_drives, g_object_unref);
g_clear_object (&self->client);
g_clear_pointer (&self->entries, g_hash_table_unref);
g_clear_pointer (&self->dir_entries, g_hash_table_unref);

View File

@ -3243,6 +3243,14 @@ do_pull (GVfsBackend *backend,
split_filename_with_ignore_prefix (gphoto2_backend, source, &dir, &name);
if (remove_source && (flags & G_FILE_COPY_NO_FALLBACK_FOR_MOVE))
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
_("Operation not supported"));
goto out;
}
if (remove_source && !gphoto2_backend->can_delete)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,

View File

@ -1,6 +1,7 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2008 Red Hat, Inc.
* Copyright (C) 2021 Igalia S.L.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -51,7 +52,7 @@
#include "gvfsdaemonprotocol.h"
#include "gvfsdaemonutils.h"
static SoupSession *the_session;
static SoupSession *the_session = NULL;
G_DEFINE_TYPE (GVfsBackendHttp, g_vfs_backend_http, G_VFS_TYPE_BACKEND)
@ -63,7 +64,7 @@ g_vfs_backend_http_finalize (GObject *object)
backend = G_VFS_BACKEND_HTTP (object);
if (backend->mount_base)
soup_uri_free (backend->mount_base);
g_uri_unref (backend->mount_base);
g_object_unref (backend->session);
@ -77,18 +78,89 @@ g_vfs_backend_http_init (GVfsBackendHttp *backend)
{
g_vfs_backend_set_user_visible (G_VFS_BACKEND (backend), FALSE);
backend->session = g_object_ref (the_session);
/* attempt to use libsoup's default values */
backend->session = g_object_ref (http_try_init_session (-1, -1));
}
/* ************************************************************************* */
/* public utility functions */
SoupURI *
GUri *
http_backend_get_mount_base (GVfsBackend *backend)
{
return G_VFS_BACKEND_HTTP (backend)->mount_base;
}
#define DEBUG_MAX_BODY_SIZE (100 * 1024 * 1024)
/* initializes the session singleton; if max_conns is lower than 0, the
* libsoup defaults are used for max-conns and max-conns-per-host, this
* is called in the instance constructor, so if they are to be overridden,
* all one has to do is make sure to call it with the desired values before
* any instance is created (most likely in the class constructor of the
* derived class, see dav backend)
*/
SoupSession *
http_try_init_session (gint max_conns, gint max_conns_per_host)
{
const char *debug;
SoupSessionFeature *cookie_jar;
if (the_session)
return the_session;
/* Initialize the SoupSession, common to all backend instances */
if (max_conns < 0)
the_session = soup_session_new_with_options ("user-agent",
"gvfs/" VERSION, NULL);
else
the_session = soup_session_new_with_options ("user-agent",
"gvfs/" VERSION,
"max-conns",
max_conns,
"max-conns-per-host",
max_conns_per_host,
NULL);
/* Cookie handling - stored temporarlly in memory, mostly useful for
* authentication in WebDAV. */
cookie_jar = g_object_new (SOUP_TYPE_COOKIE_JAR, NULL);
soup_session_add_feature (the_session, cookie_jar);
g_object_unref (cookie_jar);
/* Send Accept-Language header (see bug 166795) */
soup_session_set_accept_language_auto (the_session, TRUE);
/* Prevent connection timeouts during long operations like COPY. */
soup_session_set_timeout (the_session, 0);
/* Logging */
debug = g_getenv ("GVFS_HTTP_DEBUG");
if (debug)
{
SoupLogger *logger;
SoupLoggerLogLevel level;
if (g_ascii_strcasecmp (debug, "all") == 0 ||
g_ascii_strcasecmp (debug, "body") == 0)
level = SOUP_LOGGER_LOG_BODY;
else if (g_ascii_strcasecmp (debug, "header") == 0)
level = SOUP_LOGGER_LOG_HEADERS;
else
level = SOUP_LOGGER_LOG_MINIMAL;
logger = soup_logger_new (level);
g_object_set (G_OBJECT (logger),
"max-body-size",
DEBUG_MAX_BODY_SIZE,
NULL);
soup_session_add_feature (the_session, SOUP_SESSION_FEATURE (logger));
g_object_unref (logger);
}
return the_session;
}
char *
http_path_get_basename (const char *path)
{
@ -138,7 +210,7 @@ http_uri_get_basename (const char *uri_str)
basename = http_path_get_basename (uri_str);
decoded = soup_uri_decode (basename);
decoded = g_uri_unescape_string (basename, NULL);
g_free (basename);
return decoded;
@ -149,13 +221,6 @@ http_error_code_from_status (guint status)
{
switch (status) {
case SOUP_STATUS_CANT_RESOLVE:
case SOUP_STATUS_CANT_RESOLVE_PROXY:
return G_IO_ERROR_HOST_NOT_FOUND;
case SOUP_STATUS_CANCELLED:
return G_IO_ERROR_CANCELLED;
case SOUP_STATUS_UNAUTHORIZED:
case SOUP_STATUS_PAYMENT_REQUIRED:
case SOUP_STATUS_FORBIDDEN:
@ -184,45 +249,27 @@ http_error_code_from_status (guint status)
void
http_job_failed (GVfsJob *job, SoupMessage *msg)
{
switch (msg->status_code) {
switch (soup_message_get_status(msg)) {
case SOUP_STATUS_NOT_FOUND:
g_vfs_job_failed_literal (job, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
msg->reason_phrase);
soup_message_get_reason_phrase(msg));
break;
case SOUP_STATUS_UNAUTHORIZED:
case SOUP_STATUS_PAYMENT_REQUIRED:
case SOUP_STATUS_FORBIDDEN:
g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
_("HTTP Client Error: %s"), msg->reason_phrase);
_("HTTP Client Error: %s"),
soup_message_get_reason_phrase(msg));
break;
default:
g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
_("HTTP Error: %s"), msg->reason_phrase);
_("HTTP Error: %s"),
soup_message_get_reason_phrase(msg));
}
}
guint
http_backend_send_message (GVfsBackend *backend,
SoupMessage *msg)
{
GVfsBackendHttp *op_backend = G_VFS_BACKEND_HTTP (backend);
return soup_session_send_message (op_backend->session, msg);
}
void
http_backend_queue_message (GVfsBackend *backend,
SoupMessage *msg,
SoupSessionCallback callback,
gpointer user_data)
{
GVfsBackendHttp *op_backend = G_VFS_BACKEND_HTTP (backend);
soup_session_queue_message (op_backend->session, msg,
callback, user_data);
}
/* ************************************************************************* */
/* virtual functions overrides */
@ -235,8 +282,8 @@ try_mount (GVfsBackend *backend,
{
GVfsBackendHttp *op_backend;
const char *uri_str;
char *path;
SoupURI *uri;
const char *path;
GUri *uri;
GMountSpec *real_mount_spec;
op_backend = G_VFS_BACKEND_HTTP (backend);
@ -245,7 +292,7 @@ try_mount (GVfsBackend *backend,
uri_str = g_mount_spec_get (mount_spec, "uri");
if (uri_str)
uri = soup_uri_new (uri_str);
uri = g_uri_parse (uri_str, SOUP_HTTP_URI_FLAGS, NULL);
g_debug ("+ try_mount: %s\n", uri_str ? uri_str : "(null)");
@ -260,12 +307,11 @@ try_mount (GVfsBackend *backend,
real_mount_spec = g_mount_spec_new ("http");
g_mount_spec_set (real_mount_spec, "uri", uri_str);
if (uri->path != NULL)
path = g_uri_unescape_string (g_uri_get_path (uri), "/");
if (path[0])
{
path = g_uri_unescape_string (uri->path, "/");
g_free (real_mount_spec->mount_prefix);
real_mount_spec->mount_prefix = g_mount_spec_canonicalize_path (path);
g_free (path);
}
g_vfs_backend_set_mount_spec (backend, real_mount_spec);
@ -309,7 +355,7 @@ open_for_read_ready (GObject *source_object,
}
msg = g_vfs_http_input_stream_get_message (stream);
if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
if (!SOUP_STATUS_IS_SUCCESSFUL (soup_message_get_status (msg)))
{
http_job_failed (G_VFS_JOB (job), msg);
g_object_unref (msg);
@ -330,7 +376,7 @@ try_open_for_read (GVfsBackend *backend,
GVfsJobOpenForRead *job,
const char *filename)
{
SoupURI *uri;
GUri *uri;
uri = http_backend_get_mount_base (backend);
http_backend_open_for_read (backend, G_VFS_JOB (job), uri);
@ -341,7 +387,7 @@ try_open_for_read (GVfsBackend *backend,
void
http_backend_open_for_read (GVfsBackend *backend,
GVfsJob *job,
SoupURI *uri)
GUri *uri)
{
GVfsBackendHttp *op_backend;
GInputStream *stream;
@ -507,7 +553,7 @@ file_info_from_message (SoupMessage *msg,
/* prefer the filename from the Content-Disposition (rfc2183) header
if one if present. See bug 551298. */
if (soup_message_headers_get_content_disposition (msg->response_headers,
if (soup_message_headers_get_content_disposition (soup_message_get_response_headers (msg),
NULL, &params))
{
const char *name = g_hash_table_lookup (params, "filename");
@ -520,10 +566,10 @@ file_info_from_message (SoupMessage *msg,
if (basename == NULL)
{
const SoupURI *uri;
GUri *uri;
uri = soup_message_get_uri (msg);
basename = http_uri_get_basename (uri->path);
basename = http_uri_get_basename (g_uri_get_path (uri));
}
g_debug ("basename:%s\n", basename);
@ -540,12 +586,12 @@ file_info_from_message (SoupMessage *msg,
g_free (basename);
g_free (ed_name);
if (soup_message_headers_get_encoding (msg->response_headers) == SOUP_ENCODING_CONTENT_LENGTH)
if (soup_message_headers_get_encoding (soup_message_get_response_headers (msg)) == SOUP_ENCODING_CONTENT_LENGTH)
{
goffset start, end, length;
gboolean ret;
ret = soup_message_headers_get_content_range (msg->response_headers,
ret = soup_message_headers_get_content_range (soup_message_get_response_headers (msg),
&start, &end, &length);
if (ret && length != -1)
{
@ -553,14 +599,14 @@ file_info_from_message (SoupMessage *msg,
}
else if (!ret)
{
length = soup_message_headers_get_content_length (msg->response_headers);
length = soup_message_headers_get_content_length (soup_message_get_response_headers (msg));
g_file_info_set_size (info, length);
}
}
g_file_info_set_file_type (info, G_FILE_TYPE_REGULAR);
text = soup_message_headers_get_content_type (msg->response_headers, NULL);
text = soup_message_headers_get_content_type (soup_message_get_response_headers (msg), NULL);
if (text)
{
GIcon *icon;
@ -578,23 +624,25 @@ file_info_from_message (SoupMessage *msg,
}
text = soup_message_headers_get_one (msg->response_headers,
text = soup_message_headers_get_one (soup_message_get_response_headers (msg),
"Last-Modified");
if (text)
{
SoupDate *sd;
GDateTime *gd;
sd = soup_date_new_from_string(text);
if (sd)
gd = soup_date_time_new_from_http_string (text);
if (gd)
{
g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED, soup_date_to_time_t (sd));
g_file_info_set_attribute_uint64 (info,
G_FILE_ATTRIBUTE_TIME_MODIFIED,
g_date_time_to_unix (gd));
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, 0);
soup_date_free (sd);
g_date_time_unref (gd);
}
}
text = soup_message_headers_get_one (msg->response_headers,
text = soup_message_headers_get_one (soup_message_get_response_headers (msg),
"ETag");
if (text)
{
@ -605,19 +653,25 @@ file_info_from_message (SoupMessage *msg,
}
static void
query_info_ready (SoupSession *session,
SoupMessage *msg,
gpointer user_data)
query_info_ready (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
GFileAttributeMatcher *matcher;
GVfsJobQueryInfo *job;
GFileInfo *info;
GVfsJobQueryInfo *job = G_VFS_JOB_QUERY_INFO (user_data);
GFileAttributeMatcher *matcher = job->attribute_matcher;
GFileInfo *info = job->file_info;
GInputStream *res;
GError *error = NULL;
SoupMessage *msg = G_VFS_JOB (job)->backend_data;
job = G_VFS_JOB_QUERY_INFO (user_data);
info = job->file_info;
matcher = job->attribute_matcher;
res = soup_session_send_finish (SOUP_SESSION (object), result, &error);
if (!res)
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
return;
}
if (! SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
if (!SOUP_STATUS_IS_SUCCESSFUL (soup_message_get_status (msg)))
{
http_job_failed (G_VFS_JOB (job), msg);
return;
@ -625,10 +679,11 @@ query_info_ready (SoupSession *session,
file_info_from_message (msg, info, matcher);
g_object_unref (res);
g_vfs_job_succeeded (G_VFS_JOB (job));
}
static gboolean
try_query_info (GVfsBackend *backend,
GVfsJobQueryInfo *job,
@ -637,8 +692,9 @@ try_query_info (GVfsBackend *backend,
GFileInfo *info,
GFileAttributeMatcher *attribute_matcher)
{
GVfsBackendHttp *op_backend = G_VFS_BACKEND_HTTP (backend);
SoupMessage *msg;
SoupURI *uri;
GUri *uri;
if (g_file_attribute_matcher_matches_only (attribute_matcher,
G_FILE_ATTRIBUTE_THUMBNAIL_PATH))
@ -650,7 +706,10 @@ try_query_info (GVfsBackend *backend,
uri = http_backend_get_mount_base (backend);
msg = soup_message_new_from_uri (SOUP_METHOD_HEAD, uri);
http_backend_queue_message (backend, msg, query_info_ready, job);
g_vfs_job_set_backend_data (G_VFS_JOB (job), msg, NULL);
soup_session_send_async (op_backend->session, msg, G_PRIORITY_DEFAULT,
NULL, query_info_ready, job);
return TRUE;
}
@ -682,19 +741,14 @@ try_query_fs_info (GVfsBackend *backend,
{
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "http");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_IF_ALWAYS);
g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE;
}
#define DEBUG_MAX_BODY_SIZE (100 * 1024 * 1024)
static void
g_vfs_backend_http_class_init (GVfsBackendHttpClass *klass)
{
const char *debug;
SoupSessionFeature *cookie_jar;
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GVfsBackendClass *backend_class;
@ -710,43 +764,4 @@ g_vfs_backend_http_class_init (GVfsBackendHttpClass *klass)
backend_class->try_query_info = try_query_info;
backend_class->try_query_info_on_read = try_query_info_on_read;
backend_class->try_query_fs_info = try_query_fs_info;
/* Initialize the SoupSession, common to all backend instances */
the_session = soup_session_new_with_options ("user-agent",
"gvfs/" VERSION,
NULL);
g_object_set (the_session, "ssl-strict", FALSE, NULL);
/* Cookie handling - stored temporarlly in memory, mostly useful for
* authentication in WebDAV. */
cookie_jar = g_object_new (SOUP_TYPE_COOKIE_JAR, NULL);
soup_session_add_feature (the_session, cookie_jar);
g_object_unref (cookie_jar);
/* Send Accept-Language header (see bug 166795) */
g_object_set (the_session, "accept-language-auto", TRUE, NULL);
/* Prevent connection timeouts during long operations like COPY. */
g_object_set (the_session, "timeout", 0, NULL);
/* Logging */
debug = g_getenv ("GVFS_HTTP_DEBUG");
if (debug)
{
SoupLogger *logger;
SoupLoggerLogLevel level;
if (g_ascii_strcasecmp (debug, "all") == 0 ||
g_ascii_strcasecmp (debug, "body") == 0)
level = SOUP_LOGGER_LOG_BODY;
else if (g_ascii_strcasecmp (debug, "header") == 0)
level = SOUP_LOGGER_LOG_HEADERS;
else
level = SOUP_LOGGER_LOG_MINIMAL;
logger = soup_logger_new (level, DEBUG_MAX_BODY_SIZE);
soup_session_add_feature (the_session, SOUP_SESSION_FEATURE (logger));
g_object_unref (logger);
}
}

View File

@ -1,6 +1,7 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2008 Red Hat, Inc.
* Copyright (C) 2021 Igalia S.L.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -49,7 +50,7 @@ struct _GVfsBackendHttp
{
GVfsBackend parent_instance;
SoupURI *mount_base;
GUri *mount_base;
SoupSession *session;
};
@ -61,19 +62,14 @@ char * http_path_get_basename (const char *path_str);
int http_error_code_from_status (guint status);
SoupURI * http_backend_get_mount_base (GVfsBackend *backend);
SoupSession * http_try_init_session (gint max_conns,
gint max_conns_per_host);
guint http_backend_send_message (GVfsBackend *backend,
SoupMessage *msg);
void http_backend_queue_message (GVfsBackend *backend,
SoupMessage *msg,
SoupSessionCallback callback,
gpointer user_data);
GUri * http_backend_get_mount_base (GVfsBackend *backend);
void http_backend_open_for_read (GVfsBackend *backend,
GVfsJob *job,
SoupURI *uri);
GUri *uri);
void http_job_failed (GVfsJob *job,
SoupMessage *msg);

View File

@ -390,6 +390,7 @@ do_query_fs_info (GVfsBackend *backend,
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "localtest");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, FALSE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_IF_LOCAL);
file = get_g_file_from_local (filename, G_VFS_JOB (job));

View File

@ -132,6 +132,18 @@ handle_event (EventData *data, GVfsBackendMtp *backend);
* Storage name helper
************************************************/
/**
* create_storage_name:
*
* Returns a unique, printable storage name for a LIBMTP_devicestorage_t
* based on its StorageDescription, appending the storage ID if necessary
* to make it unique.
*
* The caller takes ownership of the returned string.
* This function never returns NULL strings.
*
* The passed-in `storage->StorageDescription` may be NULL.
*/
static char *create_storage_name (const LIBMTP_devicestorage_t *storage)
{
/* The optional post-fixing of storage's name with ID requires us to
@ -139,9 +151,15 @@ static char *create_storage_name (const LIBMTP_devicestorage_t *storage)
or not. Since this function is called in several places, it is
safest to perform this check here, each time that storage name needs
to be created. */
/* TODO: The returned name is not unique if suffix-adding happens
to introduce a collision with another storage's unsuffixed
description; unlikely but possible. */
gboolean is_unique = TRUE;
const LIBMTP_devicestorage_t *tmp_storage;
/* `storage->StorageDescription` may be NULL, so we ensure to only use
functions that can handle this, like `g_strcmp0()`. */
/* Forward search for duplicates */
for (tmp_storage = storage->next; tmp_storage != 0; tmp_storage = tmp_storage->next) {
if (!g_strcmp0 (storage->StorageDescription, tmp_storage->StorageDescription)) {
@ -164,7 +182,17 @@ static char *create_storage_name (const LIBMTP_devicestorage_t *storage)
/* If description is unique, we can use it as storage name; otherwise,
we add storage ID to it */
if (is_unique) {
return g_strdup (storage->StorageDescription);
/* Never return a NULL string (`g_strdup` returns NULL on NULL).
Use the storage ID on empty strings to avoid duplicate entries
for devices with multiple storages without description. */
if (storage->StorageDescription && strlen (storage->StorageDescription) > 0) {
return g_strdup (storage->StorageDescription);
} else {
/* Translators: This is shown as the name for MTP devices
* without StorageDescription.
* The %X is the formatted storage ID. */
return g_strdup_printf (_("Storage (%X)"), storage->id);
}
} else {
return g_strdup_printf ("%s (%X)", storage->StorageDescription, storage->id);
}
@ -1217,6 +1245,7 @@ get_storage_info (LIBMTP_devicestorage_t *storage, GFileInfo *info) {
g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, storage->FreeSpaceInBytes);
g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE, storage->MaxCapacity);
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "mtpfs");
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_NEVER);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, FALSE);
g_debug ("(II) get_storage_info done.\n");
@ -1711,6 +1740,13 @@ do_pull (GVfsBackend *backend,
GFileInfo *info = NULL;
guint64 mtime;
if (remove_source && (flags & G_FILE_COPY_NO_FALLBACK_FOR_MOVE)) {
g_vfs_job_failed (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Operation not supported"));
goto exit;
}
CacheEntry *entry = get_cache_entry (G_VFS_BACKEND_MTP (backend), source);
if (entry == NULL) {
g_vfs_job_failed_literal (G_VFS_JOB (job),
@ -2005,6 +2041,13 @@ do_push (GVfsBackend *backend,
gchar **elements = g_strsplit_set (destination, "/", -1);
unsigned int ne = g_strv_length (elements);
if (remove_source && (flags & G_FILE_COPY_NO_FALLBACK_FOR_MOVE)) {
g_vfs_job_failed (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Operation not supported"));
goto exit;
}
if (ne < 3) {
g_vfs_job_failed_literal (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,

View File

@ -868,6 +868,7 @@ try_query_fs_info (GVfsBackend *backend,
{
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "network");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_NEVER);
g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE;
}

View File

@ -1607,6 +1607,9 @@ try_query_fs_info (GVfsBackend *backend,
G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "nfs");
g_file_info_set_attribute_boolean (info,
G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info,
G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW,
G_FILESYSTEM_PREVIEW_TYPE_IF_ALWAYS);
if (g_file_attribute_matcher_matches (matcher,
G_FILE_ATTRIBUTE_FILESYSTEM_SIZE) ||

View File

@ -30,7 +30,7 @@ typedef struct {
char *uri;
char *display_name;
GFile *file;
time_t modified;
GDateTime *modified;
} RecentItem;
struct OPAQUE_TYPE__GVfsBackendRecent
@ -326,7 +326,8 @@ recent_backend_add_info (RecentItem *item,
TRUE);
/* G_FILE_ATTRIBUTE_RECENT_MODIFIED */
g_file_info_set_attribute_int64 (info, "recent::modified", item->modified);
g_file_info_set_attribute_int64 (info, "recent::modified",
g_date_time_to_unix (item->modified));
}
static gboolean
@ -375,6 +376,7 @@ recent_item_free (RecentItem *item)
g_free (item->display_name);
g_free (item->guid);
g_clear_object (&item->file);
g_date_time_unref (item->modified);
g_free (item);
}
@ -382,7 +384,7 @@ static gboolean
recent_item_update (RecentItem *item,
const gchar *uri,
const gchar *display_name,
time_t modified)
GDateTime *modified)
{
gboolean changed = FALSE;
@ -403,10 +405,11 @@ recent_item_update (RecentItem *item,
item->display_name = g_strdup (display_name);
}
if (item->modified != modified)
if (!g_date_time_equal (item->modified, modified))
{
changed = TRUE;
item->modified = modified;
g_date_time_unref (item->modified);
item->modified = g_date_time_ref (modified);
}
return changed;
@ -415,11 +418,12 @@ recent_item_update (RecentItem *item,
static RecentItem *
recent_item_new (const gchar *uri,
const gchar *display_name,
time_t modified)
GDateTime *modified)
{
RecentItem *item;
item = g_new0 (RecentItem, 1);
item->guid = g_dbus_generate_guid ();
item->modified = g_date_time_ref (modified);
recent_item_update (item, uri, display_name, modified);
@ -508,12 +512,12 @@ reload_recent_items (GVfsBackendRecent *backend)
const char *uri = uris[i];
const char *guid;
char *display_name;
time_t modified;
GDateTime *modified;
if (should_include (backend->bookmarks, uri))
{
display_name = get_display_name (backend->bookmarks, uri);
modified = g_bookmark_file_get_modified (backend->bookmarks, uri, NULL);
modified = g_bookmark_file_get_modified_date_time (backend->bookmarks, uri, NULL);
guid = g_hash_table_lookup (backend->uri_map, uri);
if (guid)
{

View File

@ -465,7 +465,7 @@ setup_ssh_environment (void)
}
static char **
setup_ssh_commandline (GVfsBackend *backend)
setup_ssh_commandline (GVfsBackend *backend, const gchar *control_path)
{
GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
guint last_arg;
@ -488,7 +488,8 @@ setup_ssh_commandline (GVfsBackend *backend)
#ifndef USE_PTY
args[last_arg++] = g_strdup ("-oBatchMode yes");
#endif
args[last_arg++] = g_strdup ("-oControlMaster auto");
args[last_arg++] = g_strdup_printf ("-oControlPath=%s/%%C", control_path);
}
else if (op_backend->client_vendor == SFTP_VENDOR_SSH)
args[last_arg++] = g_strdup ("-x");
@ -1105,10 +1106,10 @@ handle_login (GVfsBackend *backend,
if (g_str_has_suffix (buffer, "password: ") ||
g_str_has_suffix (buffer, "Password: ") ||
g_str_has_suffix (buffer, "Password:") ||
g_str_has_prefix (buffer, "Password for ") ||
g_str_has_prefix (buffer, "Enter Kerberos password") ||
g_str_has_prefix (buffer, "Enter passphrase for key") ||
g_str_has_prefix (buffer, "Enter PASSCODE"))
strstr (buffer, "Password for ") ||
strstr (buffer, "Enter Kerberos password") ||
strstr (buffer, "Enter passphrase for key") ||
strstr (buffer, "Enter PASSCODE"))
{
gboolean aborted = FALSE;
gsize bytes_written;
@ -1160,17 +1161,17 @@ handle_login (GVfsBackend *backend,
if (op_backend->user_specified)
if (strcmp (authtype, "publickey") == 0)
/* Translators: the first %s is the username, the second the host name */
prompt = g_strdup_printf (_("Enter passphrase for secure key for %s on %s"), op_backend->user, op_backend->host);
prompt = g_strdup_printf (_("Authentication Required\nEnter passphrase for secure key for %s on %s”:"), op_backend->user, op_backend->host);
else
/* Translators: the first %s is the username, the second the host name */
prompt = g_strdup_printf (_("Enter password for %s on %s"), op_backend->user, hostname ? hostname : op_backend->host);
prompt = g_strdup_printf (_("Authentication Required\nEnter password for %s on %s”:"), op_backend->user, hostname ? hostname : op_backend->host);
else
if (strcmp (authtype, "publickey") == 0)
/* Translators: %s is the hostname */
prompt = g_strdup_printf (_("Enter passphrase for secure key for %s"), op_backend->host);
prompt = g_strdup_printf (_("Authentication Required\nEnter passphrase for secure key for %s”:"), op_backend->host);
else
/* Translators: %s is the hostname */
prompt = g_strdup_printf (_("Enter password for %s"), hostname ? hostname : op_backend->host);
prompt = g_strdup_printf (_("Authentication Required\nEnter user and password for %s”:"), hostname ? hostname : op_backend->host);
if (!g_mount_source_ask_password (mount_source,
prompt,
@ -1264,6 +1265,55 @@ handle_login (GVfsBackend *backend,
break;
}
}
else if (strstr (buffer, "Verification code") ||
strstr (buffer, "One-time password"))
{
gchar *verification_code = NULL;
gboolean aborted = FALSE;
g_debug ("handle_login #%d - asking for verification code...\n", i);
if (op_backend->user_specified)
/* Translators: the first %s is the username, the second the host name */
prompt = g_strdup_printf (_("Enter verification code for %s on %s"),
op_backend->user, op_backend->host);
else
/* Translators: %s is the hostname */
prompt = g_strdup_printf (_("Enter verification code for %s"),
op_backend->host);
if (!g_mount_source_ask_password (mount_source, prompt,
op_backend->user, NULL,
G_ASK_PASSWORD_NEED_PASSWORD,
&aborted, &verification_code,
NULL, NULL, NULL, NULL) ||
aborted)
{
g_set_error_literal (error, G_IO_ERROR, aborted ?
G_IO_ERROR_FAILED_HANDLED :
G_IO_ERROR_PERMISSION_DENIED,
_("Password dialog cancelled"));
ret_val = FALSE;
break;
}
g_free (prompt);
if (!g_output_stream_write_all (reply_stream, verification_code,
strlen (verification_code), NULL,
NULL, NULL) ||
!g_output_stream_write_all (reply_stream, "\n", 1, NULL, NULL,
NULL))
{
g_free (verification_code);
g_set_error_literal (error,
G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
_("Cant send password"));
ret_val = FALSE;
break;
}
g_free (verification_code);
}
else if (g_str_has_prefix (buffer, "The authenticity of host '") ||
strstr (buffer, "Key fingerprint:") != NULL)
{
@ -1275,8 +1325,10 @@ handle_login (GVfsBackend *backend,
get_hostname_and_fingerprint_from_line (buffer, &hostname, &fingerprint);
message = g_strdup_printf (_("Cant verify the identity of “%s”.\n"
"This happens when you log in to a computer the first time.\n\n"
/* Translators: the first %s is the hostname, the second the key fingerprint */
message = g_strdup_printf (_("Identity Verification Failed\n"
"Verifying the identity of “%s” failed, this happens when "
"you log in to a computer the first time.\n\n"
"The identity sent by the remote computer is “%s”. "
"If you want to be absolutely sure it is safe to continue, "
"contact the system administrator."),
@ -1302,7 +1354,9 @@ handle_login (GVfsBackend *backend,
get_hostname_and_ip_address (buffer, &hostname, &ip_address);
message = g_strdup_printf (_("The host key for “%s” differs from the key for the IP address “%s”\n"
/* Translators: the first %s is the hostname, the second is an ip address */
message = g_strdup_printf (_("Identity Verification Failed\n"
"The host key for “%s” differs from the key for the IP address “%s”\n"
"If you want to be absolutely sure it is safe to continue, "
"contact the system administrator."),
hostname ? hostname : op_backend->host,
@ -1841,8 +1895,13 @@ setup_connection (GVfsBackend *backend,
gboolean res;
char *extension_name, *extension_data;
int i;
gchar *control_path = NULL;
args = setup_ssh_commandline (backend);
control_path = g_build_filename (g_get_user_runtime_dir (), "gvfsd-sftp", NULL);
g_mkdir (control_path, 0700);
args = setup_ssh_commandline (backend, control_path);
g_free (control_path);
if (!spawn_ssh (backend,
args, &pid,
@ -4734,6 +4793,9 @@ try_query_fs_info (GVfsBackend *backend,
G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "sftp");
g_file_info_set_attribute_boolean (info,
G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info,
G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW,
G_FILESYSTEM_PREVIEW_TYPE_IF_ALWAYS);
if (has_extension (op_backend, SFTP_EXT_OPENSSH_STATVFS) &&
(g_file_attribute_matcher_matches (matcher,
@ -5491,6 +5553,8 @@ typedef struct {
/* fstat information */
goffset size;
guint32 permissions;
guint64 mtime;
guint64 atime;
/* state */
goffset offset;
@ -5640,8 +5704,15 @@ push_close_deleted_file (GVfsBackendSftp *backend,
}
static void
push_close_delete_or_succeed (SftpPushHandle *handle)
push_close_delete_or_succeed (GVfsBackendSftp *backend,
int reply_type,
GDataInputStream *reply,
guint32 len,
GVfsJob *job,
gpointer user_data)
{
SftpPushHandle *handle = user_data;
if (handle->tempname)
{
/* If we wrote to a temp file, do delete then rename. */
@ -5662,15 +5733,52 @@ push_close_delete_or_succeed (SftpPushHandle *handle)
}
static void
push_close_restore_permissions (GVfsBackendSftp *backend,
int reply_type,
GDataInputStream *reply,
guint32 len,
GVfsJob *job,
gpointer user_data)
push_close_restore_permissions (SftpPushHandle *handle)
{
/* We don't care if setting the permissions succeeded or not. */
push_close_delete_or_succeed (user_data);
gboolean default_perms = (handle->op_job->flags & G_FILE_COPY_TARGET_DEFAULT_PERMS);
guint32 flags = SSH_FILEXFER_ATTR_ACMODTIME;
if (!default_perms)
flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
/* Restore the source file's permissions and timestamps. */
GDataOutputStream *command = new_command_stream (handle->backend, SSH_FXP_SETSTAT);
put_string (command, handle->tempname ? handle->tempname : handle->op_job->destination);
g_data_output_stream_put_uint32 (command, flags, NULL, NULL);
if (!default_perms)
g_data_output_stream_put_uint32 (command, handle->permissions, NULL, NULL);
g_data_output_stream_put_uint32 (command, handle->atime, NULL, NULL);
g_data_output_stream_put_uint32 (command, handle->mtime, NULL, NULL);
queue_command_stream_and_free (&handle->backend->command_connection, command,
push_close_delete_or_succeed,
handle->job, handle);
}
static void
push_close_stat_reply (GVfsBackendSftp *backend,
int reply_type,
GDataInputStream *reply,
guint32 len,
GVfsJob *job,
gpointer user_data)
{
SftpPushHandle *handle = user_data;
if (reply_type == SSH_FXP_ATTRS)
{
GFileInfo *info = g_file_info_new ();
parse_attributes (backend, info, NULL, reply, NULL);
/* Don't fail on error, but fall back to the local atime
* (assigned in push_source_fstat_cb). */
if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_ACCESS))
handle->atime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS);
g_object_unref (info);
}
push_close_restore_permissions (handle);
}
static void
@ -5688,19 +5796,19 @@ push_close_write_reply (GVfsBackendSftp *backend,
guint32 code = read_status_code (reply);
if (code == SSH_FX_OK)
{
if (handle->op_job->flags & G_FILE_COPY_TARGET_DEFAULT_PERMS)
push_close_delete_or_succeed (handle);
else
/* Atime is COPY_WHEN_MOVED, but not COPY_WITH_FILE. */
if (!handle->op_job->remove_source &&
!(handle->op_job->flags & G_FILE_COPY_ALL_METADATA))
{
/* Restore the source file's permissions. */
GDataOutputStream *command = new_command_stream (backend, SSH_FXP_SETSTAT);
put_string (command, handle->tempname ? handle->tempname : handle->op_job->destination);
g_data_output_stream_put_uint32 (command, SSH_FILEXFER_ATTR_PERMISSIONS, NULL, NULL);
g_data_output_stream_put_uint32 (command, handle->permissions, NULL, NULL);
GDataOutputStream *command = new_command_stream (backend, SSH_FXP_LSTAT);
put_string (command, handle->op_job->destination);
queue_command_stream_and_free (&backend->command_connection, command,
push_close_restore_permissions,
push_close_stat_reply,
job, handle);
return;
}
push_close_restore_permissions (handle);
return;
}
else
@ -6070,6 +6178,8 @@ push_source_fstat_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{
handle->permissions = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE) & 0777;
handle->size = g_file_info_get_size (info);
handle->mtime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
handle->atime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS);
command = new_command_stream (handle->backend, SSH_FXP_OPEN);
put_string (command, handle->op_job->destination);
@ -6102,7 +6212,9 @@ push_source_open_cb (GObject *source, GAsyncResult *res, gpointer user_data)
g_file_input_stream_query_info_async (fin,
G_FILE_ATTRIBUTE_STANDARD_SIZE ","
G_FILE_ATTRIBUTE_UNIX_MODE,
G_FILE_ATTRIBUTE_UNIX_MODE ","
G_FILE_ATTRIBUTE_TIME_MODIFIED ","
G_FILE_ATTRIBUTE_TIME_ACCESS,
0, NULL,
push_source_fstat_cb, handle);
}
@ -6166,6 +6278,14 @@ try_push (GVfsBackend *backend,
GFile *source;
SftpPushHandle *handle;
if (remove_source && (flags & G_FILE_COPY_NO_FALLBACK_FOR_MOVE))
{
g_vfs_job_failed (G_VFS_JOB (op_job),
G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Operation not supported"));
return TRUE;
}
if (!connection_is_usable (&op_backend->data_connection))
{
g_vfs_job_failed (G_VFS_JOB (op_job),
@ -6211,6 +6331,8 @@ typedef struct {
/* fstat information */
goffset size;
guint32 mode;
guint64 mtime;
guint64 atime;
/* state */
goffset offset;
@ -6317,12 +6439,22 @@ pull_close_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{
g_vfs_job_progress_callback (handle->n_written, handle->n_written, handle->job);
if (handle->size >= 0 && !(handle->op_job->flags & G_FILE_COPY_TARGET_DEFAULT_PERMS))
if (handle->size >= 0)
{
GFileInfo *info = g_file_info_new ();
g_file_info_set_attribute_uint32 (info,
G_FILE_ATTRIBUTE_UNIX_MODE,
handle->mode);
if (!(handle->op_job->flags & G_FILE_COPY_TARGET_DEFAULT_PERMS))
g_file_info_set_attribute_uint32 (info,
G_FILE_ATTRIBUTE_UNIX_MODE,
handle->mode);
g_file_info_set_attribute_uint64 (info,
G_FILE_ATTRIBUTE_TIME_MODIFIED,
handle->mtime);
/* Atime is COPY_WHEN_MOVED, but not COPY_WITH_FILE. */
if (handle->op_job->remove_source ||
(handle->op_job->flags & G_FILE_COPY_ALL_METADATA))
g_file_info_set_attribute_uint64 (info,
G_FILE_ATTRIBUTE_TIME_ACCESS,
handle->atime);
g_file_set_attributes_async (handle->dest,
info,
G_FILE_QUERY_INFO_NONE,
@ -6573,6 +6705,10 @@ pull_fstat_reply (GVfsBackendSftp *backend,
handle->size = g_file_info_get_size (info);
handle->mode = g_file_info_get_attribute_uint32 (info,
G_FILE_ATTRIBUTE_UNIX_MODE);
handle->mtime = g_file_info_get_attribute_uint64 (info,
G_FILE_ATTRIBUTE_TIME_MODIFIED);
handle->atime = g_file_info_get_attribute_uint64 (info,
G_FILE_ATTRIBUTE_TIME_ACCESS);
g_object_unref (info);
}
else
@ -6699,6 +6835,14 @@ try_pull (GVfsBackend *backend,
SftpPullHandle *handle;
Command commands[2];
if (remove_source && (flags & G_FILE_COPY_NO_FALLBACK_FOR_MOVE))
{
g_vfs_job_failed (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Operation not supported"));
return TRUE;
}
if (!connection_is_usable (&op_backend->data_connection))
{
g_vfs_job_failed (G_VFS_JOB (job),

View File

@ -49,6 +49,7 @@
#include "gvfsjobqueryfsinfo.h"
#include "gvfsjobqueryattributes.h"
#include "gvfsjobenumerate.h"
#include "gvfsjobmove.h"
#include "gvfsdaemonprotocol.h"
#include "gvfsdaemonutils.h"
#include "gvfsutils.h"
@ -77,9 +78,7 @@ struct _GVfsBackendSmb
GMountSource *mount_source; /* Only used/set during mount */
int mount_try;
gboolean mount_try_again;
gboolean mount_cancelled;
gboolean use_anonymous;
gboolean password_in_keyring;
GPasswordSave password_save;
@ -202,25 +201,14 @@ auth_callback (SMBCCTX *context,
backend->user == NULL &&
backend->domain == NULL)
{
/* Try again if kerberos login fails */
backend->mount_try_again = TRUE;
g_debug ("auth_callback - kerberos pass\n");
}
else if (backend->mount_try == 1 &&
backend->user == NULL &&
backend->domain == NULL)
{
/* Try again if ccache login fails */
backend->mount_try_again = TRUE;
g_debug ("auth_callback - ccache pass\n");
}
else if (backend->use_anonymous)
{
/* Try again if anonymous login fails */
backend->use_anonymous = FALSE;
backend->mount_try_again = TRUE;
g_debug ("auth_callback - anonymous login pass\n");
}
else
{
gboolean in_keyring = FALSE;
@ -263,9 +251,19 @@ auth_callback (SMBCCTX *context,
g_debug ("auth_callback - asking for password...\n");
/* translators: First %s is a share name, second is a server name */
message = g_strdup_printf (_("Password required for share %s on %s"),
share_name, server_name);
if (backend->user)
{
/* Translators: First %s is a share name, second is a server name */
message = g_strdup_printf (_("Authentication Required\nEnter password for share “%s” on “%s”:"),
share_name, server_name);
}
else
{
/* Translators: First %s is a share name, second is a server name */
message = g_strdup_printf (_("Authentication Required\nEnter user and password for share “%s” on “%s”:"),
share_name, server_name);
}
handled = g_mount_source_ask_password (backend->mount_source,
message,
username_out,
@ -290,13 +288,13 @@ auth_callback (SMBCCTX *context,
}
}
/* Try again if this fails */
backend->mount_try_again = TRUE;
smbc_setOptionNoAutoAnonymousLogin (backend->smb_context,
!anonymous);
if (anonymous)
{
backend->use_anonymous = TRUE;
backend->password_save = FALSE;
g_debug ("auth_callback - anonymous enabled\n");
}
else
{
@ -486,7 +484,6 @@ do_mount (GVfsBackend *backend,
*/
do
{
op_backend->mount_try_again = FALSE;
op_backend->mount_cancelled = FALSE;
g_debug ("do_mount - try #%d \n", op_backend->mount_try);
@ -502,7 +499,13 @@ do_mount (GVfsBackend *backend,
if (res == 0)
break;
if (op_backend->mount_cancelled || (errsv != EACCES && errsv != EPERM))
if (errsv == EINVAL && op_backend->mount_try <= 1 && op_backend->user == NULL)
{
/* EINVAL is "expected" when kerberos/ccache is misconfigured, see:
* https://gitlab.gnome.org/GNOME/gvfs/-/issues/611
*/
}
else if (op_backend->mount_cancelled || (errsv != EACCES && errsv != EPERM))
{
g_debug ("do_mount - (errno != EPERM && errno != EACCES), cancelled = %d, breaking\n", op_backend->mount_cancelled);
break;
@ -518,15 +521,9 @@ do_mount (GVfsBackend *backend,
smbc_setOptionFallbackAfterKerberos (op_backend->smb_context, 1);
}
/* If the AskPassword reply requested anonymous login, enable the
* anonymous fallback and try again.
*/
smbc_setOptionNoAutoAnonymousLogin (op_backend->smb_context,
!op_backend->use_anonymous);
op_backend->mount_try ++;
}
while (op_backend->mount_try_again);
while (TRUE);
g_free (uri);
@ -1434,6 +1431,8 @@ set_info_from_stat (GVfsBackendSmb *backend,
if (g_file_attribute_matcher_matches (matcher,
G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE) ||
g_file_attribute_matcher_matches (matcher,
G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE) ||
g_file_attribute_matcher_matches (matcher,
G_FILE_ATTRIBUTE_STANDARD_ICON) ||
g_file_attribute_matcher_matches (matcher,
@ -1587,6 +1586,7 @@ do_query_fs_info (GVfsBackend *backend,
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "cifs");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_IF_ALWAYS);
if (g_file_attribute_matcher_matches (attribute_matcher,
G_FILE_ATTRIBUTE_FILESYSTEM_SIZE) ||
@ -2041,6 +2041,7 @@ do_move (GVfsBackend *backend,
smbc_stat_fn smbc_stat;
smbc_rename_fn smbc_rename;
smbc_unlink_fn smbc_unlink;
goffset size;
source_uri = create_smb_uri (op_backend->server, op_backend->port, op_backend->share, source);
@ -2062,8 +2063,9 @@ do_move (GVfsBackend *backend,
g_free (source_uri);
return;
}
else
source_is_dir = S_ISDIR (statbuf.st_mode);
source_is_dir = S_ISDIR (statbuf.st_mode);
size = statbuf.st_size;
dest_uri = create_smb_uri (op_backend->server, op_backend->port, op_backend->share, destination);
@ -2158,7 +2160,10 @@ do_move (GVfsBackend *backend,
g_vfs_job_failed_from_errno (G_VFS_JOB (job), errsv);
}
else
g_vfs_job_succeeded (G_VFS_JOB (job));
{
g_vfs_job_progress_callback (size, size, job);
g_vfs_job_succeeded (G_VFS_JOB (job));
}
}
static void

View File

@ -367,9 +367,19 @@ auth_callback (SMBCCTX *context,
g_debug ("auth_callback - asking for password...\n");
/* translators: %s is a server name */
message = g_strdup_printf (_("Password required for %s"),
server_name);
if (backend->user)
{
/* Translators: %s is a server name */
message = g_strdup_printf (_("Authentication Required\nEnter password for “%s”:"),
server_name);
}
else
{
/* Translators: %s is a server name */
message = g_strdup_printf (_("Authentication Required\nEnter user and password for “%s”:"),
server_name);
}
handled = g_mount_source_ask_password (backend->mount_source,
message,
username_out,
@ -957,8 +967,14 @@ do_mount (GVfsBackend *backend,
uri, op_backend->mount_try, dir, op_backend->mount_cancelled,
errsv, g_strerror (errsv));
if (dir == NULL &&
(op_backend->mount_cancelled || (errsv != EPERM && errsv != EACCES)))
if (errsv == EINVAL && op_backend->mount_try == 0 && op_backend->user == NULL)
{
/* EINVAL is "expected" when kerberos is misconfigured, see:
* https://gitlab.gnome.org/GNOME/gvfs/-/issues/611
*/
}
else if (dir == NULL &&
(op_backend->mount_cancelled || (errsv != EPERM && errsv != EACCES)))
{
g_debug ("do_mount - (errno != EPERM && errno != EACCES), cancelled = %d, breaking\n", op_backend->mount_cancelled);
break;
@ -1496,6 +1512,7 @@ try_query_fs_info (GVfsBackend *backend,
{
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "cifs");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_NEVER);
g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE;
}

View File

@ -612,8 +612,7 @@ send_reply_cb (GObject *source_object,
job = channel->priv->current_job;
channel->priv->current_job = NULL;
if (job)
g_vfs_job_emit_finished (job);
g_vfs_job_emit_finished (job);
class = G_VFS_CHANNEL_GET_CLASS (channel);
@ -634,7 +633,7 @@ send_reply_cb (GObject *source_object,
}
/* Start queued request or readahead */
else if (!start_queued_request (channel) &&
class->readahead && job)
class->readahead)
{
/* No queued requests, maybe we want to do a readahead call */
channel->priv->current_job = class->readahead (channel, job);
@ -644,7 +643,6 @@ send_reply_cb (GObject *source_object,
}
g_object_unref (job);
g_object_unref (channel);
}
/* Might be called on an i/o thread */
@ -668,7 +666,7 @@ g_vfs_channel_send_reply (GVfsChannel *channel,
channel->priv->reply_buffer,
G_VFS_DAEMON_SOCKET_PROTOCOL_REPLY_SIZE,
0, NULL,
send_reply_cb, g_object_ref (channel));
send_reply_cb, channel);
}
else
{
@ -677,7 +675,7 @@ g_vfs_channel_send_reply (GVfsChannel *channel,
channel->priv->output_data,
channel->priv->output_data_size,
0, NULL,
send_reply_cb, g_object_ref (channel));
send_reply_cb, channel);
}
}

View File

@ -87,14 +87,6 @@ struct _GVfsDaemon
gboolean lost_main_daemon;
};
typedef struct {
GVfsDaemon *daemon;
char *socket_dir;
GDBusServer *server;
GDBusConnection *conn;
} NewConnectionData;
static guint signals[LAST_SIGNAL] = { 0 };
static void g_vfs_daemon_get_property (GObject *object,
@ -209,7 +201,6 @@ job_handler_callback (gpointer data,
GVfsJob *job = G_VFS_JOB (data);
g_vfs_job_run (job);
g_object_unref (job);
}
static void
@ -649,30 +640,10 @@ g_vfs_daemon_queue_job (GVfsDaemon *daemon,
if (!g_vfs_job_try (job))
{
/* Couldn't finish / run async, queue worker thread */
if (!g_thread_pool_push (daemon->thread_pool, g_object_ref (job), NULL)) /* TODO: Check error */
g_object_unref (job);
g_thread_pool_push (daemon->thread_pool, job, NULL); /* TODO: Check error */
}
}
static void
new_connection_data_free (void *memory)
{
NewConnectionData *data = memory;
gchar *socket;
/* Remove the socket and dir after connected */
if (data->socket_dir)
{
socket = g_strdup_printf ("%s/socket", data->socket_dir);
g_unlink (socket);
g_free (socket);
rmdir (data->socket_dir);
g_free (data->socket_dir);
}
g_free (data);
}
static void
peer_unregister_skeleton (const gchar *obj_path,
RegisteredPath *reg_path,
@ -723,21 +694,19 @@ peer_connection_closed (GDBusConnection *connection,
daemon_skeleton = g_object_get_data (G_OBJECT (connection), "daemon_skeleton");
/* daemon_skeleton should be always valid in this case */
g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (daemon_skeleton));
g_hash_table_remove (daemon->client_connections, connection);
/* Unexport the registered interface skeletons */
g_hash_table_foreach (daemon->registered_paths, (GHFunc) peer_unregister_skeleton, connection);
/* The peer-to-peer connection was disconnected */
g_signal_handlers_disconnect_by_data (connection, user_data);
g_object_unref (connection);
g_hash_table_remove (daemon->client_connections, connection);
}
static void
daemon_peer_connection_setup (GVfsDaemon *daemon,
GDBusConnection *dbus_conn,
NewConnectionData *data)
GDBusConnection *dbus_conn)
{
GVfsDBusDaemon *daemon_skeleton;
GError *error;
@ -754,133 +723,34 @@ daemon_peer_connection_setup (GVfsDaemon *daemon,
g_warning ("Failed to accept client: %s, %s (%s, %d)", "object registration failed",
error->message, g_quark_to_string (error->domain), error->code);
g_error_free (error);
g_object_unref (data->conn);
goto error_out;
return;
}
g_object_set_data_full (G_OBJECT (data->conn), "daemon_skeleton", daemon_skeleton, (GDestroyNotify) g_object_unref);
g_object_set_data_full (G_OBJECT (dbus_conn), "daemon_skeleton",
daemon_skeleton, (GDestroyNotify) g_object_unref);
/* Export registered interface skeletons on this new connection */
g_hash_table_foreach (daemon->registered_paths, (GHFunc) peer_register_skeleton, dbus_conn);
g_hash_table_insert (daemon->client_connections, g_object_ref (dbus_conn), NULL);
g_signal_connect (data->conn, "closed", G_CALLBACK (peer_connection_closed), data->daemon);
error_out:
new_connection_data_free (data);
g_signal_connect (dbus_conn, "closed", G_CALLBACK (peer_connection_closed), daemon);
}
#ifdef __linux__
#define USE_ABSTRACT_SOCKETS
#endif
#ifndef USE_ABSTRACT_SOCKETS
static gboolean
test_safe_socket_dir (const char *dirname)
{
struct stat statbuf;
if (g_stat (dirname, &statbuf) != 0)
return FALSE;
#ifndef G_PLATFORM_WIN32
if (statbuf.st_uid != getuid ())
return FALSE;
if ((statbuf.st_mode & (S_IRWXG|S_IRWXO)) ||
!S_ISDIR (statbuf.st_mode))
return FALSE;
#endif
return TRUE;
}
static char *
create_socket_dir (void)
{
char *dirname;
long iteration = 0;
char *safe_dir;
gchar tmp[9];
int i;
safe_dir = NULL;
do
{
g_free (safe_dir);
gvfs_randomize_string (tmp, 8);
tmp[8] = '\0';
dirname = g_strdup_printf ("gvfs-%s-%s",
g_get_user_name (), tmp);
safe_dir = g_build_filename (g_get_tmp_dir (), dirname, NULL);
g_free (dirname);
if (g_mkdir (safe_dir, 0700) < 0)
{
switch (errno)
{
case EACCES:
g_error ("I can't write to '%s', daemon init failed",
safe_dir);
break;
case ENAMETOOLONG:
g_error ("Name '%s' too long your system is broken",
safe_dir);
break;
case ENOMEM:
#ifdef ELOOP
case ELOOP:
#endif
case ENOSPC:
case ENOTDIR:
case ENOENT:
g_error ("Resource problem creating '%s'", safe_dir);
break;
default: /* carry on going */
break;
}
}
/* Possible race - so we re-scan. */
if (iteration++ == 1000)
g_error ("Cannot find a safe socket path in '%s'", g_get_tmp_dir ());
}
while (!test_safe_socket_dir (safe_dir));
return safe_dir;
}
#endif
static void
generate_address (char **address,
char **folder)
generate_address (gchar **address, gchar **socket_path)
{
*address = NULL;
*folder = NULL;
gchar tmp[16] = "socket-";
gchar *socket_dir;
#ifdef USE_ABSTRACT_SOCKETS
{
gchar tmp[9];
gvfs_randomize_string (tmp + 7, 8);
tmp[15] = '\0';
gvfs_randomize_string (tmp, 8);
tmp[8] = '\0';
*address = g_strdup_printf ("unix:abstract=/dbus-vfs-daemon/socket-%s", tmp);
}
#else
{
char *dir;
dir = create_socket_dir ();
*address = g_strdup_printf ("unix:path=%s/socket", dir);
*folder = dir;
}
#endif
socket_dir = gvfs_get_socket_dir ();
*socket_path = g_build_filename (socket_dir, tmp, NULL);
*address = g_strdup_printf ("unix:path=%s", *socket_path);
g_free (socket_dir);
}
static gboolean
@ -888,14 +758,9 @@ daemon_new_connection_func (GDBusServer *server,
GDBusConnection *connection,
gpointer user_data)
{
NewConnectionData *data;
GVfsDaemon *daemon = user_data;
data = user_data;
/* Take ownership */
data->conn = g_object_ref (connection);
daemon_peer_connection_setup (data->daemon, data->conn, data);
daemon_peer_connection_setup (daemon, connection);
/* Kill the server, no more need for it */
g_dbus_server_stop (server);
@ -913,16 +778,11 @@ handle_get_connection (GVfsDBusDaemon *object,
GDBusServer *server;
GError *error;
gchar *address1;
NewConnectionData *data;
char *socket_dir;
gchar *socket_path;
gchar *guid;
generate_address (&address1, &socket_dir);
const char *pkexec_uid;
data = g_new (NewConnectionData, 1);
data->daemon = daemon;
data->socket_dir = socket_dir;
data->conn = NULL;
generate_address (&address1, &socket_path);
guid = g_dbus_generate_guid ();
error = NULL;
@ -943,21 +803,34 @@ handle_get_connection (GVfsDBusDaemon *object,
}
g_dbus_server_start (server);
data->server = server;
g_signal_connect (server, "new-connection", G_CALLBACK (daemon_new_connection_func), data);
/* This is needed for gvfsd-admin to ensure correct ownership. */
pkexec_uid = g_getenv ("PKEXEC_UID");
if (pkexec_uid != NULL)
{
uid_t uid;
uid = strtol (pkexec_uid, NULL, 10);
if (uid != 0)
if (chown (socket_path, uid, (gid_t)-1) < 0)
g_warning ("Failed to change socket ownership: %s", g_strerror (errno));
}
g_signal_connect (server, "new-connection", G_CALLBACK (daemon_new_connection_func), daemon);
gvfs_dbus_daemon_complete_get_connection (object,
invocation,
address1,
"");
g_free (address1);
g_free (socket_path);
return TRUE;
error_out:
new_connection_data_free (data);
g_free (address1);
g_unlink (socket_path);
g_free (socket_path);
return TRUE;
}
@ -1171,8 +1044,7 @@ void
g_vfs_daemon_run_job_in_thread (GVfsDaemon *daemon,
GVfsJob *job)
{
if (!g_thread_pool_push (daemon->thread_pool, g_object_ref (job), NULL)) /* TODO: Check error */
g_object_unref (job);
g_thread_pool_push (daemon->thread_pool, job, NULL); /* TODO: Check error */
}
void

View File

@ -332,7 +332,9 @@ gvfs_accept_certificate (GMountSource *mount_source,
certificate_str = certificate_to_string (certificate);
reason = certificate_flags_to_string (errors);
message = g_strdup_printf (_("The sites identity cant be verified:"
/* Translators: The first %s is the reason why verification failed, the second a certificate */
message = g_strdup_printf (_("Identity Verification Failed\n"
"%s\n\n"
"%s\n\n"
"Are you really sure you would like to continue?"),
@ -361,3 +363,77 @@ gvfs_accept_certificate (GMountSource *mount_source,
return FALSE;
}
#endif
gssize
gvfs_output_stream_splice (GOutputStream *stream,
GInputStream *source,
GOutputStreamSpliceFlags flags,
goffset total_size,
GFileProgressCallback progress_callback,
gpointer progress_callback_data,
GCancellable *cancellable,
GError **error)
{
gssize n_read, n_written;
gsize bytes_copied;
char buffer[8192], *p;
gboolean res;
bytes_copied = 0;
res = TRUE;
do
{
n_read = g_input_stream_read (source, buffer, sizeof (buffer), cancellable, error);
if (n_read == -1)
{
res = FALSE;
break;
}
if (n_read == 0)
break;
p = buffer;
while (n_read > 0)
{
n_written = g_output_stream_write (stream, p, n_read, cancellable, error);
if (n_written == -1)
{
res = FALSE;
break;
}
p += n_written;
n_read -= n_written;
bytes_copied += n_written;
if (progress_callback)
progress_callback (bytes_copied, total_size, progress_callback_data);
}
if (bytes_copied > G_MAXSSIZE)
bytes_copied = G_MAXSSIZE;
}
while (res);
if (!res)
error = NULL; /* Ignore further errors */
if (flags & G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE)
{
/* Don't care about errors in source here */
g_input_stream_close (source, cancellable, NULL);
}
if (flags & G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET)
{
/* But write errors on close are bad! */
if (!g_output_stream_close (stream, cancellable, error))
res = FALSE;
}
if (res)
return bytes_copied;
return -1;
}

View File

@ -47,6 +47,15 @@ gboolean gvfs_accept_certificate (GMountSource *mount_source,
GTlsCertificate *certificate,
GTlsCertificateFlags errors);
gssize gvfs_output_stream_splice (GOutputStream *stream,
GInputStream *source,
GOutputStreamSpliceFlags flags,
goffset total_size,
GFileProgressCallback progress_callback,
gpointer progress_callback_data,
GCancellable *cancellable,
GError **error);
G_END_DECLS
#endif /* __G_VFS_DAEMON_UTILS_H__ */

View File

@ -231,6 +231,7 @@ g_vfs_ftp_task_acquire_connection (GVfsFtpTask *task)
if (g_vfs_ftp_connection_is_usable (task->conn))
break;
ftp->connections--;
g_vfs_ftp_connection_free (task->conn);
task->conn = NULL;
}

View File

@ -1,6 +1,7 @@
/* gvfshttpinputstream.c: seekable wrapper around SoupRequestHTTP
*
* Copyright (C) 2006, 2007, 2012 Red Hat, Inc.
* Copyright (C) 2021 Igalia S.L.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -29,13 +30,13 @@
#include <libsoup/soup.h>
#include "gvfshttpinputstream.h"
#include "gvfsbackendhttp.h"
static void g_vfs_http_input_stream_seekable_iface_init (GSeekableIface *seekable_iface);
struct GVfsHttpInputStreamPrivate {
SoupURI *uri;
GUri *uri;
SoupSession *session;
SoupRequest *req;
SoupMessage *msg;
GInputStream *stream;
@ -61,9 +62,8 @@ g_vfs_http_input_stream_finalize (GObject *object)
GVfsHttpInputStream *stream = G_VFS_HTTP_INPUT_STREAM (object);
GVfsHttpInputStreamPrivate *priv = stream->priv;
g_clear_pointer (&priv->uri, soup_uri_free);
g_clear_pointer (&priv->uri, g_uri_unref);
g_clear_object (&priv->session);
g_clear_object (&priv->req);
g_clear_object (&priv->msg);
g_clear_object (&priv->stream);
g_free (priv->range);
@ -74,7 +74,7 @@ g_vfs_http_input_stream_finalize (GObject *object)
/**
* g_vfs_http_input_stream_new:
* @session: a #SoupSession
* @uri: a #SoupURI
* @uri: a #GUri
*
* Prepares to send a GET request for @uri on @session, and returns a
* #GInputStream that can be used to read the response.
@ -89,7 +89,7 @@ g_vfs_http_input_stream_finalize (GObject *object)
**/
GInputStream *
g_vfs_http_input_stream_new (SoupSession *session,
SoupURI *uri)
GUri *uri)
{
GVfsHttpInputStream *stream;
GVfsHttpInputStreamPrivate *priv;
@ -98,30 +98,27 @@ g_vfs_http_input_stream_new (SoupSession *session,
priv = stream->priv;
priv->session = g_object_ref (session);
priv->uri = soup_uri_copy (uri);
priv->uri = g_uri_ref (uri);
return G_INPUT_STREAM (stream);
}
static SoupRequest *
g_vfs_http_input_stream_ensure_request (GInputStream *stream)
static SoupMessage *
g_vfs_http_input_stream_ensure_msg (GInputStream *stream)
{
GVfsHttpInputStreamPrivate *priv = G_VFS_HTTP_INPUT_STREAM (stream)->priv;
if (!priv->req)
if (!priv->msg)
{
GError *error = NULL;
priv->req = soup_session_request_uri (priv->session, priv->uri, &error);
g_assert_no_error (error);
priv->msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (priv->req));
priv->msg = soup_message_new_from_uri (SOUP_METHOD_GET, priv->uri);
priv->offset = 0;
}
if (priv->range)
soup_message_headers_replace (priv->msg->request_headers, "Range", priv->range);
soup_message_headers_replace (soup_message_get_request_headers (priv->msg),
"Range", priv->range);
return priv->req;
return priv->msg;
}
static void
@ -136,7 +133,7 @@ send_callback (GObject *object,
g_input_stream_clear_pending (http_stream);
priv->stream = soup_request_send_finish (SOUP_REQUEST (object), result, &error);
priv->stream = soup_session_send_finish (SOUP_SESSION (object), result, &error);
if (priv->stream)
g_task_return_boolean (task, TRUE);
else
@ -188,9 +185,9 @@ g_vfs_http_input_stream_send_async (GInputStream *stream,
return;
}
g_vfs_http_input_stream_ensure_request (stream);
soup_request_send_async (priv->req, cancellable,
send_callback, task);
g_vfs_http_input_stream_ensure_msg (stream);
soup_session_send_async (priv->session, priv->msg, G_PRIORITY_DEFAULT,
cancellable, send_callback, task);
}
/**
@ -253,16 +250,16 @@ read_send_callback (GObject *object,
ReadAfterSendData *rasd = g_task_get_task_data (task);
GError *error = NULL;
priv->stream = soup_request_send_finish (SOUP_REQUEST (object), result, &error);
priv->stream = soup_session_send_finish (SOUP_SESSION (object), result, &error);
if (!priv->stream)
{
g_task_return_error (task, error);
g_object_unref (task);
return;
}
if (!SOUP_STATUS_IS_SUCCESSFUL (priv->msg->status_code))
if (!SOUP_STATUS_IS_SUCCESSFUL (soup_message_get_status (priv->msg)))
{
if (priv->msg->status_code == SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE)
if (soup_message_get_status (priv->msg) == SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE)
{
g_input_stream_close (priv->stream, NULL, NULL);
g_task_return_int (task, 0);
@ -271,9 +268,9 @@ read_send_callback (GObject *object,
return;
}
g_task_return_new_error (task,
SOUP_HTTP_ERROR,
priv->msg->status_code,
"%s", priv->msg->reason_phrase);
G_IO_ERROR,
http_error_code_from_status (soup_message_get_status (priv->msg)),
_("HTTP Error: %s"), soup_message_get_reason_phrase (priv->msg));
g_object_unref (task);
return;
}
@ -282,7 +279,7 @@ read_send_callback (GObject *object,
gboolean status;
goffset start, end;
status = soup_message_headers_get_content_range (priv->msg->response_headers,
status = soup_message_headers_get_content_range (soup_message_get_response_headers (priv->msg),
&start, &end, NULL);
if (!status || start != priv->request_offset)
{
@ -325,9 +322,9 @@ g_vfs_http_input_stream_read_async (GInputStream *stream,
rasd->count = count;
g_task_set_task_data (task, rasd, g_free);
g_vfs_http_input_stream_ensure_request (stream);
soup_request_send_async (priv->req, cancellable,
read_send_callback, task);
g_vfs_http_input_stream_ensure_msg (stream);
soup_session_send_async (priv->session, priv->msg, G_PRIORITY_DEFAULT,
cancellable, read_send_callback, task);
return;
}
@ -419,7 +416,7 @@ g_vfs_http_input_stream_seek (GSeekable *seekable,
if (type == G_SEEK_END && priv->msg)
{
goffset content_length = soup_message_headers_get_content_length (priv->msg->response_headers);
goffset content_length = soup_message_headers_get_content_length (soup_message_get_response_headers (priv->msg));
if (content_length)
{
@ -498,7 +495,7 @@ g_vfs_http_input_stream_get_message (GInputStream *stream)
{
GVfsHttpInputStreamPrivate *priv = G_VFS_HTTP_INPUT_STREAM (stream)->priv;
g_vfs_http_input_stream_ensure_request (stream);
g_vfs_http_input_stream_ensure_msg (stream);
return g_object_ref (priv->msg);
}

View File

@ -1,4 +1,5 @@
/* Copyright (C) 2006, 2007, 2012 Red Hat, Inc.
* Copyright (C) 2021 Igalia S.L.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -57,7 +58,7 @@ struct GVfsHttpInputStreamClass
GType g_vfs_http_input_stream_get_type (void) G_GNUC_CONST;
GInputStream *g_vfs_http_input_stream_new (SoupSession *session,
SoupURI *uri);
GUri *uri);
void g_vfs_http_input_stream_send_async (GInputStream *stream,
int io_priority,

View File

@ -36,7 +36,7 @@ static guint signals[LAST_SIGNAL] = { 0 };
GType
g_vfs_job_source_get_type (void)
{
static volatile gsize g_define_type_id__volatile = 0;
static gsize g_define_type_id__volatile = 0;
if (g_once_init_enter (&g_define_type_id__volatile))
{

View File

@ -96,9 +96,7 @@ on_name_acquired (GDBusConnection *connection,
argv2[0] = LIBEXEC_DIR "/gvfsd-fuse";
argv2[1] = fuse_path;
argv2[2] = "-f";
argv2[3] = "-o";
argv2[4] = "big_writes";
argv2[5] = NULL;
argv2[3] = NULL;
g_spawn_async (NULL,
argv2,
@ -145,6 +143,7 @@ main (int argc, char *argv[])
guint name_owner_id;
GBusNameOwnerFlags flags;
GOptionContext *context;
gchar *socket_dir;
const GOptionEntry options[] = {
{ "replace", 'r', 0, G_OPTION_ARG_NONE, &replace, N_("Replace old daemon."), NULL },
{ "no-fuse", 0, 0, G_OPTION_ARG_NONE, &no_fuse, N_("Dont start fuse."), NULL },
@ -214,6 +213,11 @@ main (int argc, char *argv[])
if (daemon == NULL)
return 1;
/* This is needed for gvfsd-admin to ensure correct ownership. */
socket_dir = gvfs_get_socket_dir ();
g_mkdir (socket_dir, 0700);
g_free (socket_dir);
g_signal_connect (daemon, "shutdown",
G_CALLBACK (daemon_shutdown), loop);

View File

@ -128,7 +128,7 @@ libgvfsdaemon = shared_library(
)
libgvfsdaemon_dep = declare_dependency(
include_directories: include_directories('.'),
include_directories: '.',
dependencies: libgvfscommon_dep,
compile_args: cflags,
link_with: libgvfsdaemon,
@ -154,7 +154,7 @@ daemon_main_sources = files(
'daemon-main-generic.c',
)
programs = []
programs = {}
mounts = []
schema_data = []
convert_data = []
@ -165,7 +165,7 @@ cflags = [
'-DBACKEND_TYPES="localtest", G_VFS_TYPE_BACKEND_LOCALTEST,',
]
programs += [['gvfsd-localtest', {'sources': files('gvfsbackendlocaltest.c'), 'c_args': cflags}]]
programs += {'gvfsd-localtest': {'sources': files('gvfsbackendlocaltest.c'), 'c_args': cflags}}
mounts += ['localtest']
sources = files(
@ -184,7 +184,7 @@ cflags = [
'-DMAX_JOB_THREADS=10',
]
programs += [['gvfsd-ftp', {'sources': sources, 'c_args': cflags}]]
programs += {'gvfsd-ftp': {'sources': sources, 'c_args': cflags}}
mounts += ['ftp', 'ftps', 'ftpis']
cflags = [
@ -194,7 +194,7 @@ cflags = [
'-DMAX_JOB_THREADS=10',
]
programs += [['gvfsd-trash', {'sources': files('gvfsbackendtrash.c'), 'dependencies': [libtrash_dep], 'c_args': cflags}]]
programs += {'gvfsd-trash': {'sources': files('gvfsbackendtrash.c'), 'dependencies': [libtrash_dep], 'c_args': cflags}}
mounts += ['trash']
cflags = [
@ -205,7 +205,7 @@ cflags = [
'-DMAX_JOB_THREADS=10',
]
programs += [['gvfsd-recent', {'sources': files('gvfsbackendrecent.c'), 'c_args': cflags}]]
programs += {'gvfsd-recent': {'sources': files('gvfsbackendrecent.c'), 'c_args': cflags}}
mounts += ['recent']
cflags = [
@ -216,7 +216,7 @@ cflags = [
'-DMAX_JOB_THREADS=1',
]
programs += [['gvfsd-computer', {'sources': files('gvfsbackendcomputer.c'), 'dependencies': [gio_unix_dep], 'c_args': cflags}]]
programs += {'gvfsd-computer': {'sources': files('gvfsbackendcomputer.c'), 'dependencies': [gio_unix_dep], 'c_args': cflags}}
mounts += ['computer']
cflags = [
@ -227,7 +227,7 @@ cflags = [
'-DMAX_JOB_THREADS=1',
]
programs += [['gvfsd-network', {'sources': files('gvfsbackendnetwork.c'), 'c_args': cflags}]]
programs += {'gvfsd-network': {'sources': files('gvfsbackendnetwork.c'), 'c_args': cflags}}
mounts += ['network']
cflags = [
@ -237,7 +237,7 @@ cflags = [
'-DMAX_JOB_THREADS=1',
]
programs += [['gvfsd-burn', {'sources': files('gvfsbackendburn.c'), 'dependencies': [gio_unix_dep], 'c_args': cflags}]]
programs += {'gvfsd-burn': {'sources': files('gvfsbackendburn.c'), 'dependencies': [gio_unix_dep], 'c_args': cflags}}
mounts += ['burn']
if enable_sftp
@ -256,10 +256,10 @@ if enable_sftp
'-DDEFAULT_BACKEND_TYPE=sftp',
'-DBACKEND_TYPES="sftp", G_VFS_TYPE_BACKEND_SFTP,',
'-DMAX_JOB_THREADS=1',
'-DSSH_PROGRAM="@0@"'.format(ssh.path()),
'-DSSH_PROGRAM="ssh"',
]
programs += [['gvfsd-sftp', {'sources': sources, 'dependencies': deps, 'c_args': cflags}]]
programs += {'gvfsd-sftp': {'sources': sources, 'dependencies': deps, 'c_args': cflags}}
mounts += ['sftp']
endif
@ -273,7 +273,7 @@ if enable_samba
'-DMAX_JOB_THREADS=1',
]
programs += [['gvfsd-smb', {'sources': sources, 'dependencies': [smbclient_dep], 'c_args': cflags}]]
programs += {'gvfsd-smb': {'sources': sources, 'dependencies': [smbclient_dep], 'c_args': cflags}}
mounts += ['smb']
schema_data += files('org.gnome.system.smb.gschema.xml')
convert_data += files('gvfs-smb.convert')
@ -286,7 +286,7 @@ if enable_samba
'-DMOUNTABLE_DBUS_NAME=' + gvfs_namespace + '.mountpoint_smb_browse',
]
programs += [['gvfsd-smb-browse', {'sources': sources + files('gvfsbackendsmbbrowse.c'), 'dependencies': [smbclient_dep], 'c_args': cflags}]]
programs += {'gvfsd-smb-browse': {'sources': sources + files('gvfsbackendsmbbrowse.c'), 'dependencies': [smbclient_dep], 'c_args': cflags}}
mounts += ['smb-browse']
endif
@ -305,7 +305,7 @@ if enable_dnssd
'-DMOUNTABLE_DBUS_NAME=' + gvfs_namespace + '.mountpoint_dnssd',
]
programs += [['gvfsd-dnssd', {'sources': files('gvfsbackenddnssd.c'), 'dependencies': deps, 'c_args': cflags}]]
programs += {'gvfsd-dnssd': {'sources': files('gvfsbackenddnssd.c'), 'dependencies': deps, 'c_args': cflags}}
mounts += ['dns-sd']
schema_data += files('org.gnome.system.dns_sd.gschema.xml')
convert_data += files('gvfs-dns-sd.convert')
@ -320,7 +320,7 @@ if enable_archive
'-DBACKEND_USES_GVFS=1',
]
programs += [['gvfsd-archive', {'sources': files('gvfsbackendarchive.c'), 'dependencies': [libarchive_dep], 'c_args': cflags}]]
programs += {'gvfsd-archive': {'sources': files('gvfsbackendarchive.c'), 'dependencies': [libarchive_dep], 'c_args': cflags}}
mounts += ['archive']
endif
@ -337,7 +337,7 @@ if enable_cdda
'-DMAX_JOB_THREADS=1',
]
programs += [['gvfsd-cdda', {'sources': files('gvfsbackendcdda.c'), 'dependencies': deps, 'c_args': cflags}]]
programs += {'gvfsd-cdda': {'sources': files('gvfsbackendcdda.c'), 'dependencies': deps, 'c_args': cflags}}
mounts += ['cdda']
endif
@ -354,27 +354,27 @@ if enable_admin
'-DMOUNTABLE_DBUS_NAME=' + gvfs_namespace + '.mountpoint_admin',
]
programs += [['gvfsd-admin', {'sources': files('gvfsbackendadmin.c'), 'dependencies': deps, 'c_args': cflags}]]
programs += {'gvfsd-admin': {'sources': files('gvfsbackendadmin.c'), 'dependencies': deps, 'c_args': cflags}}
mounts += ['admin']
policy = gvfs_namespace + '.file-operations.policy'
policy_in = configure_file(
input: policy + '.in.in',
output: '@BASENAME@',
configuration: service_conf,
)
i18n.merge_file(
input: policy_in,
input: configure_file(
input: policy + '.in.in',
output: '@BASENAME@',
configuration: service_conf,
),
output: '@BASENAME@',
po_dir: po_dir,
install: true,
install_dir: gvfs_datadir / 'polkit-1/actions',
)
install_data(
gvfs_namespace + '.file-operations.rules',
configure_file(
input: gvfs_namespace + '.file-operations.rules.in',
output: '@BASENAME@',
configuration: {'PRIVILEGED_GROUP': privileged_group},
install_dir: gvfs_datadir / 'polkit-1/rules.d',
)
endif
@ -391,7 +391,7 @@ if enable_google
'-DBACKEND_TYPES="google-drive", G_VFS_TYPE_BACKEND_GOOGLE,',
]
programs += [['gvfsd-google', {'sources': files('gvfsbackendgoogle.c'), 'dependencies': deps, 'c_args': cflags}]]
programs += {'gvfsd-google': {'sources': files('gvfsbackendgoogle.c'), 'dependencies': deps, 'c_args': cflags}}
mounts += ['google']
endif
@ -409,7 +409,7 @@ if enable_gphoto2
'-DMAX_JOB_THREADS=1',
]
programs += [['gvfsd-gphoto2', {'sources': files('gvfsbackendgphoto2.c'), 'dependencies': deps, 'c_args': cflags}]]
programs += {'gvfsd-gphoto2': {'sources': files('gvfsbackendgphoto2.c'), 'dependencies': deps, 'c_args': cflags}}
mounts += ['gphoto2']
endif
@ -430,7 +430,7 @@ if enable_mtp
deps += libusb_dep
endif
programs += [['gvfsd-mtp', {'sources': files('gvfsbackendmtp.c'), 'dependencies': deps, 'c_args': cflags}]]
programs += {'gvfsd-mtp': {'sources': files('gvfsbackendmtp.c'), 'dependencies': deps, 'c_args': cflags}}
mounts += ['mtp']
endif
@ -453,7 +453,7 @@ if enable_http
'-DMOUNTABLE_DBUS_NAME=' + gvfs_namespace + '.mountpoint_http',
]
programs += [['gvfsd-http', {'sources': sources, 'dependencies': deps, 'c_args': cflags}]]
programs += {'gvfsd-http': {'sources': sources, 'dependencies': deps, 'c_args': cflags}}
mounts += ['http']
cflags = [
@ -472,7 +472,7 @@ if enable_http
cflags += '-DBACKEND_TYPES="dav", G_VFS_TYPE_BACKEND_DAV,'
endif
programs += [['gvfsd-dav', {'sources': sources + files('gvfsbackenddav.c'), 'dependencies': deps, 'c_args': cflags}]]
programs += {'gvfsd-dav': {'sources': sources + files('gvfsbackenddav.c'), 'dependencies': deps, 'c_args': cflags}}
mounts += ['dav']
endif
@ -490,7 +490,7 @@ if enable_afc
'-DBACKEND_USES_GVFS=1',
]
programs += [['gvfsd-afc', {'sources': files('gvfsbackendafc.c'), 'dependencies': deps, 'c_args': cflags}]]
programs += {'gvfsd-afc': {'sources': files('gvfsbackendafc.c'), 'dependencies': deps, 'c_args': cflags}}
mounts += ['afc']
endif
@ -515,7 +515,7 @@ if enable_afp
'-DMAX_JOB_THREADS=1',
]
programs += [['gvfsd-afp', {'sources': common_sources + files('gvfsbackendafp.c'), 'dependencies': deps, 'c_args': cflags}]]
programs += {'gvfsd-afp': {'sources': common_sources + files('gvfsbackendafp.c'), 'dependencies': deps, 'c_args': cflags}}
mounts += ['afp']
cflags = [
@ -525,7 +525,7 @@ if enable_afp
'-DMAX_JOB_THREADS=1',
]
programs += [['gvfsd-afp-browse', {'sources': common_sources + files('gvfsbackendafpbrowse.c'), 'dependencies': deps, 'c_args': cflags}]]
programs += {'gvfsd-afp-browse': {'sources': common_sources + files('gvfsbackendafpbrowse.c'), 'dependencies': deps, 'c_args': cflags}}
mounts += ['afp-browse']
endif
@ -537,12 +537,11 @@ if enable_nfs
'-DMAX_JOB_THREADS=1',
]
programs += [['gvfsd-nfs', {'sources': files('gvfsbackendnfs.c'), 'dependencies': [libnfs_dep], 'c_args': cflags}]]
programs += {'gvfsd-nfs': {'sources': files('gvfsbackendnfs.c'), 'dependencies': [libnfs_dep], 'c_args': cflags}}
mounts += ['nfs']
endif
foreach program: programs
options = program[1]
foreach program, options: programs
kwargs = {
'sources': daemon_main_sources + options.get('sources', []),
'dependencies': [libgvfsdaemon_dep] + options.get('dependencies', []),
@ -550,7 +549,7 @@ foreach program: programs
}
executable(
program[0],
program,
include_directories: top_inc,
kwargs: kwargs,
install: true,

View File

@ -415,6 +415,30 @@ child_watch_cb (GPid pid,
gint status,
gpointer user_data)
{
MountData *data = user_data;
GError *error = NULL;
gint code = 0;
if (!g_spawn_check_wait_status (status, &error))
{
if (error->domain == G_SPAWN_EXIT_ERROR)
code = error->code;
g_clear_error (&error);
}
/* GVfs daemons always exit with 0, but gvfsd-admin is spawned over pkexec,
* which can fail when the authentication dialog is dismissed for example.
*/
if (code == 126 || code == 127)
{
error = g_error_new_literal (G_IO_ERROR,
G_IO_ERROR_PERMISSION_DENIED,
_("Permission denied"));
mount_finish (data, error);
g_error_free (error);
}
g_spawn_close_pid (pid);
}
@ -485,7 +509,7 @@ spawn_mount (MountData *data)
}
else
{
g_child_watch_add (pid, child_watch_cb, NULL);
g_child_watch_add (pid, child_watch_cb, data);
}
g_strfreev (argv);

View File

@ -1,13 +1,13 @@
// Allows users belonging to wheel group to start gvfsd-admin without
// Allows users belonging to privileged group to start gvfsd-admin without
// authorization. This prevents redundant password prompt when starting
// gvfsd-admin. The gvfsd-admin causes another password prompts to be shown
// gvfsd-admin. The gvfsd-admin causes another password prompt to be shown
// for each client process using the different action id and for the subject
// based on the client process.
polkit.addRule(function(action, subject) {
if ((action.id == "org.gtk.vfs.file-operations-helper") &&
subject.local &&
subject.active &&
subject.isInGroup ("sudo")) {
subject.isInGroup ("@PRIVILEGED_GROUP@")) {
return polkit.Result.YES;
}
});

View File

@ -272,10 +272,10 @@ dir_watch_free (DirWatch *watch)
if (watch != NULL)
{
if (watch->parent_monitor)
{
g_file_monitor_cancel (watch->parent_monitor);
g_object_unref (watch->parent_monitor);
}
{
g_file_monitor_cancel (watch->parent_monitor);
g_object_unref (watch->parent_monitor);
}
g_object_unref (watch->directory);
g_object_unref (watch->topdir);

View File

@ -211,6 +211,34 @@ trash_mount_remove (TrashMount **mount_ptr)
g_slice_free (TrashMount, mount);
}
static gboolean
ignore_trash_mount (GUnixMountEntry *mount)
{
GUnixMountPoint *mount_point = NULL;
const gchar *mount_options;
gboolean retval = TRUE;
if (g_unix_mount_is_system_internal (mount))
return TRUE;
mount_options = g_unix_mount_get_options (mount);
if (mount_options == NULL)
{
mount_point = g_unix_mount_point_at (g_unix_mount_get_mount_path (mount),
NULL);
if (mount_point != NULL)
mount_options = g_unix_mount_point_get_options (mount_point);
}
if (mount_options == NULL ||
strstr (mount_options, "x-gvfs-notrash") == NULL)
retval = FALSE;
g_clear_pointer (&mount_point, g_unix_mount_point_free);
return retval;
}
static void
trash_watcher_remount (TrashWatcher *watcher)
{
@ -229,7 +257,7 @@ trash_watcher_remount (TrashWatcher *watcher)
{
int result;
if (new && g_unix_mount_is_system_internal (new->data))
if (new && ignore_trash_mount (new->data))
{
g_unix_mount_free (new->data);
new = new->next;

6
debian/changelog vendored
View File

@ -1,8 +1,8 @@
gvfs (1.44.1-ok6) yangtze; urgency=medium
gvfs (1.50.3-ok1~0407) yangtze; urgency=medium
* update version info
* New upstream release.
-- luzhiping <luzhiping@kylinos.cn> Mon, 22 Aug 2022 13:58:48 +0800
-- Yue Lan <lanyue@kylinos.cn> Fri, 07 Apr 2023 11:40:43 +0800
gvfs (1.44.1-ok5~0629) yangtze; urgency=medium

2
debian/control vendored
View File

@ -5,7 +5,7 @@
Source: gvfs
Section: gnome
Priority: optional
Maintainer: Openkylin Developers <packaging@lists.openkylin.top>
Maintainer: Debian GNOME Maintainers <pkg-gnome-maintainers@lists.alioth.debian.org>
Uploaders: Iain Lane <laney@debian.org>, Jeremy Bicha <jbicha@debian.org>, Laurent Bigonville <bigon@debian.org>
Build-Depends: debhelper-compat (= 12),
dh-exec (>= 0.13),

View File

@ -10,7 +10,7 @@
<description xml:lang="en">GVfs is a userspace virtual filesystem implementation for GIO (a library available in GLib). GVfs comes with a set of backends, including trash support, SFTP, SMB, HTTP, DAV, and many others. GVfs also contains modules for GIO that implement volume monitors and persistent metadata storage. There is also FUSE support that provides limited access to the GVfs filesystems for applications not using GIO.</description>
<homepage rdf:resource="https://wiki.gnome.org/Projects/gvfs" />
<mailing-list rdf:resource="http://mail.gnome.org/mailman/listinfo/gvfs-list" />
<mailing-list rdf:resource="https://discourse.gnome.org" />
<download-page rdf:resource="http://download.gnome.org/sources/gvfs/" />
<bug-database rdf:resource="https://gitlab.gnome.org/GNOME/gvfs/issues/" />
<category rdf:resource="http://api.gnome.org/doap-extensions#core" />
@ -20,7 +20,7 @@
<maintainer>
<foaf:Person>
<foaf:name>Ondrej Holy</foaf:name>
<foaf:mbox rdf:resource="mailto:oholy@redhat.com" />
<foaf:mbox rdf:resource="mailto:oholy@gnome.org" />
<gnome:userid>oholy</gnome:userid>
</foaf:Person>
</maintainer>

View File

@ -14,20 +14,20 @@ xsltproc_cmd = [
'@INPUT@',
]
mans = [
['gvfs', '7'],
['gvfsd', '1'],
['gvfsd-fuse', '1'],
['gvfsd-metadata', '1'],
]
mans = {
'gvfs': '7',
'gvfsd': '1',
'gvfsd-fuse': '1',
'gvfsd-metadata': '1',
}
foreach man: mans
foreach program, section: mans
custom_target(
man[0] + man[1],
input: man[0] + '.xml',
output: '@BASENAME@.' + man[1],
program + section,
input: program + '.xml',
output: '@BASENAME@.' + section,
command: xsltproc_cmd,
install: true,
install_dir: gvfs_mandir / ('man' + man[1]),
install_dir: gvfs_mandir / ('man' + section),
)
endforeach

View File

@ -1,9 +1,9 @@
project(
'gvfs', 'c',
version: '1.44.1',
version: '1.50.3',
license: 'LGPL2+',
default_options: 'buildtype=debugoptimized',
meson_version: '>= 0.50.0',
meson_version: '>= 0.56.0',
)
gvfs_name = meson.project_name()
@ -44,16 +44,16 @@ cc = meson.get_compiler('c')
config_h = configuration_data()
# defines
set_defines = [
set_defines = {
# package
['PACKAGE_STRING', '@0@ @1@'.format(gvfs_name, gvfs_version)],
['VERSION', gvfs_version],
'PACKAGE_STRING': '@0@ @1@'.format(gvfs_name, gvfs_version),
'VERSION': gvfs_version,
# i18n
['GETTEXT_PACKAGE', gvfs_name],
]
'GETTEXT_PACKAGE': gvfs_name,
}
foreach define: set_defines
config_h.set_quoted(define[0], define[1])
foreach define, value: set_defines
config_h.set_quoted(define, value)
endforeach
# Globally define_GNU_SOURCE and therefore enable the GNU extensions
@ -179,17 +179,17 @@ foreach name: ['mkdev', 'sysmacros']
endforeach
# types
check_types = [
check_types = {
# type, fallback type
['gid_t', 'int'],
['pid_t', 'int'],
['size_t', 'unsigned int'],
['uid_t', 'int'],
]
'gid_t': 'int',
'pid_t': 'int',
'size_t': 'unsigned int',
'uid_t': 'int',
}
foreach type: check_types
if not cc.has_type(type[0], prefix: '#include<sys/types.h>')
config_h.set(type[0], type[1])
foreach type, value: check_types
if not cc.has_type(type, prefix: '#include<sys/types.h>')
config_h.set(type, value)
endif
endforeach
@ -231,7 +231,7 @@ have_version_script = cc.has_link_argument('@0@,@1@'.format(version_script_ldfla
gio_dep = dependency('gio-2.0')
gio_unix_dep = dependency('gio-unix-2.0')
glib_dep = dependency('glib-2.0', version: '>= 2.57.2')
glib_dep = dependency('glib-2.0', version: '>= 2.70.0')
gobject_dep = dependency('gobject-2.0')
gsettings_desktop_schemas_dep = dependency('gsettings-desktop-schemas', version: '>= 3.33.0')
@ -249,23 +249,23 @@ endif
config_h.set('HAVE_GCRYPT', enable_gcrypt)
# *** Check for dbus service dir ***
dbus_session_bus_services_dir = dependency('dbus-1').get_pkgconfig_variable(
'session_bus_services_dir',
define_variable: ['datadir', gvfs_prefix / gvfs_datadir],
dbus_session_bus_services_dir = dependency('dbus-1').get_variable(
pkgconfig: 'session_bus_services_dir',
pkgconfig_define: ['datadir', gvfs_prefix / gvfs_datadir],
)
dbus_service_in = files('dbus.service.in')
# *** Check for giomoduledir and schemasdir ***
gio_giomoduledir = gio_dep.get_pkgconfig_variable(
'giomoduledir',
define_variable: ['libdir', gvfs_prefix / gvfs_libdir],
gio_giomoduledir = gio_dep.get_variable(
pkgconfig: 'giomoduledir',
pkgconfig_define: ['libdir', gvfs_prefix / gvfs_libdir],
)
gio_schemasdir = gio_dep.get_pkgconfig_variable(
'schemasdir',
define_variable: ['datadir', gvfs_prefix / gvfs_datadir],
default: gvfs_prefix / gvfs_datadir / 'glib-2.0/schemas',
gio_schemasdir = gio_dep.get_variable(
pkgconfig: 'schemasdir',
pkgconfig_define: ['datadir', gvfs_prefix / gvfs_datadir],
default_value: gvfs_prefix / gvfs_datadir / 'glib-2.0/schemas',
)
# *** Check for systemd options ***
@ -281,12 +281,12 @@ if install_systemd_systemduserunitdir or install_systemd_tmpfilesdir
if install_systemd_systemduserunitdir and systemd_systemduserunitdir == ''
assert(systemd_dep.found(), 'systemd required but not found, please provide a valid systemd user unit dir or disable it')
systemd_systemduserunitdir = systemd_dep.get_pkgconfig_variable('systemduserunitdir')
systemd_systemduserunitdir = systemd_dep.get_variable(pkgconfig: 'systemduserunitdir', pkgconfig_define: ['prefix', gvfs_prefix])
endif
if install_systemd_tmpfilesdir and systemd_tmpfilesdir == ''
assert(systemd_dep.found(), 'systemd not found, if you use opentmpfiles please provide a valid systemd user unit dir or disable it')
systemd_tmpfilesdir = systemd_dep.get_pkgconfig_variable('tmpfilesdir')
systemd_tmpfilesdir = systemd_dep.get_variable(pkgconfig: 'tmpfilesdir', pkgconfig_define: ['prefix', gvfs_prefix])
endif
endif
endif
@ -299,17 +299,18 @@ endif
config_h.set('HAVE_GCR', enable_gcr)
# *** Check if we should build with admin backend ***
privileged_group = get_option('privileged_group')
enable_admin = get_option('admin')
if enable_admin
libcap_dep = dependency('libcap')
polkit_gobject_dep = dependency('polkit-gobject-1')
polkit_gobject_dep = dependency('polkit-gobject-1', version: '>= 0.114')
endif
# *** Check if we should build with http backend ***
enable_http = get_option('http')
if enable_http
assert(have_libxml, 'http required but libxml-2.0 not found')
libsoup_dep = dependency('libsoup-2.4', version: '>= 2.58.0')
libsoup_dep = dependency('libsoup-3.0', version: '>= 3.0.0')
endif
# *** Check if we should build with DNS-SD backend ***
@ -330,7 +331,7 @@ config_h.set('HAVE_GUDEV', enable_gudev)
# *** Check for FUSE ***
enable_fuse = get_option('fuse')
if enable_fuse
fuse_dep = dependency('fuse', version: '>= 2.8.0')
fuse_dep = dependency('fuse3', version: '>= 3.0.0')
endif
config_h.set('HAVE_FUSE', enable_fuse)
@ -357,7 +358,10 @@ config_h.set('HAVE_LOGIND', enable_logind)
enable_afc = get_option('afc')
if enable_afc
libimobiledevice_dep = dependency('libimobiledevice-1.0', version: '>= 1.2')
libplist_dep = dependency('libplist', version: '>= 0.15')
libplist_dep = dependency('libplist-2.0', required: false)
if not libplist_dep.found()
libplist_dep = dependency('libplist', version: '>= 0.15')
endif
endif
# *** Check if we should build with GOA volume monitor ***
@ -416,7 +420,7 @@ enable_google = get_option('google')
if enable_google
assert(enable_goa, 'Google backend requested but GOA is required')
libgdata_dep = dependency('libgdata', version: '>= 0.17.11')
libgdata_dep = dependency('libgdata', version: '>= 0.18.0')
endif
# *** Check for gphoto2 ***
@ -453,10 +457,6 @@ endif
# *** SFTP backend ***
enable_sftp = get_option('sftp')
if enable_sftp
ssh = find_program('ssh', required: false)
assert(ssh.found(), 'SFTP backend requested but a ssh client is required')
endif
# *** Enable development utils ***
enable_devel_utils = get_option('devel_utils')
@ -487,37 +487,43 @@ meson.add_install_script(
gio_giomoduledir,
)
output = gvfs_name + ' ' + gvfs_version + ' configuration summary:\n'
output += '\n'
output += ' systemduserunitdir: ' + systemd_systemduserunitdir + '\n'
output += ' tmpfilesdir: ' + systemd_tmpfilesdir + '\n'
output += '\n'
output += ' admin: ' + enable_admin.to_string() + '\n'
output += ' afc: ' + enable_afc.to_string() + '\n'
output += ' afp: ' + enable_afp.to_string() + '\n'
output += ' archive: ' + enable_archive.to_string() + '\n'
output += ' cdda: ' + enable_cdda.to_string() + '\n'
output += ' dnssd: ' + enable_dnssd.to_string() + '\n'
output += ' goa: ' + enable_goa.to_string() + '\n'
output += ' google: ' + enable_google.to_string() + '\n'
output += ' gphoto2: ' + enable_gphoto2.to_string() + '\n'
output += ' http: ' + enable_http.to_string() + '\n'
output += ' mtp: ' + enable_mtp.to_string() + '\n'
output += ' nfs: ' + enable_nfs.to_string() + '\n'
output += ' sftp: ' + enable_sftp.to_string() + '\n'
output += ' smb: ' + enable_samba.to_string() + '\n'
output += ' udisks2: ' + enable_udisks2.to_string() + '\n'
output += '\n'
output += ' bluray: ' + enable_bluray.to_string() + '\n'
output += ' fuse: ' + enable_fuse.to_string() + '\n'
output += ' gcr: ' + enable_gcr.to_string() + '\n'
output += ' gcrypt: ' + enable_gcrypt.to_string() + '\n'
output += ' gudev: ' + enable_gudev.to_string() + '\n'
output += ' keyring: ' + enable_keyring.to_string() + '\n'
output += ' logind: ' + enable_logind.to_string() + '\n'
output += ' libusb: ' + enable_libusb.to_string() + '\n'
output += '\n'
output += ' devel_utils: ' + enable_devel_utils.to_string() + '\n'
output += ' installed_tests: ' + enable_installed_tests.to_string() + '\n'
output += ' man: ' + enable_man.to_string() + '\n'
message(output)
summary({
'systemduserunitdir': systemd_systemduserunitdir,
'tmpfilesdir': systemd_tmpfilesdir,
'privileged_group': privileged_group,
}, section: 'Configuration')
summary({
'admin': enable_admin,
'afc': enable_afc,
'afp': enable_afp,
'archive': enable_archive,
'cdda': enable_cdda,
'dnssd': enable_dnssd,
'goa': enable_goa,
'google': enable_google,
'gphoto2': enable_gphoto2,
'http': enable_http,
'mtp': enable_mtp,
'nfs': enable_nfs,
'sftp': enable_sftp,
'smb': enable_samba,
'udisks2': enable_udisks2,
}, section: 'Backends')
summary({
'bluray': enable_bluray,
'fuse': enable_fuse,
'gcr': enable_gcr,
'gcrypt': enable_gcrypt,
'gudev': enable_gudev,
'keyring': enable_keyring,
'logind': enable_logind,
'libusb': enable_libusb,
}, section: 'Dependencies')
summary({
'devel_utils': enable_devel_utils,
'installed_tests': enable_installed_tests,
'man': enable_man,
})

View File

@ -1,5 +1,6 @@
option('systemduserunitdir', type: 'string', value: '', description: 'custom directory for systemd user units, or \'no\' to disable')
option('tmpfilesdir', type: 'string', value: '', description: 'custom directory for tmpfiles.d config files, or \'no\' to disable')
option('privileged_group', type: 'string', value: 'wheel', description: 'custom name for group that has elevated permissions')
option('admin', type: 'boolean', value: true, description: 'build with admin backend')
option('afc', type: 'boolean', value: true, description: 'build with afc backend and volume monitor')

View File

@ -45,6 +45,10 @@
<arg type='u' name='minor' direction='in'/>
<arg type='s' name='tree' direction='out'/>
</method>
<signal name="AttributeChanged">
<arg type='s' name='tree_path'/>
<arg type='s' name='file_path'/>
</signal>
</interface>
</node>

View File

@ -1,7 +1,9 @@
[Unit]
Description=Virtual filesystem metadata service
PartOf=graphical-session.target
[Service]
ExecStart=@libexecdir@/gvfsd-metadata
Type=dbus
BusName=org.gtk.vfs.Metadata
Slice=session.slice

View File

@ -47,6 +47,7 @@ sources = files(
deps = [
gio_dep,
glib_dep,
libgvfscommon_dep,
]
cflags = [
@ -58,7 +59,7 @@ cflags = [
libmetadata = static_library(
'metadata',
sources: sources + [dbus_sources],
include_directories: [top_inc, common_inc],
include_directories: top_inc,
dependencies: deps + [gio_unix_dep],
c_args: cflags,
pic: true,
@ -71,10 +72,7 @@ libmetadata_dep = declare_dependency(
link_with: libmetadata,
)
deps = [
libgvfscommon_dep,
libmetadata_dep,
]
deps = [libmetadata_dep]
if enable_gudev
deps += gudev_dep
@ -104,10 +102,7 @@ if enable_devel_utils
app,
app + '.c',
include_directories: top_inc,
dependencies: [
libgvfscommon_dep,
libmetadata_dep,
],
dependencies: libmetadata_dep,
c_args: cflags,
)
endforeach

View File

@ -43,6 +43,7 @@
#define WRITEOUT_TIMEOUT_SECS 60
#define WRITEOUT_TIMEOUT_SECS_NFS 15
#define WRITEOUT_TIMEOUT_SECS_DBUS 1
typedef struct {
char *filename;
@ -50,11 +51,19 @@ typedef struct {
guint writeout_timeout;
} TreeInfo;
typedef struct {
gchar *treefile;
gchar *path;
GVfsMetadata *object;
guint timeout_id;
} BusNotificationInfo;
static GHashTable *tree_infos = NULL;
static GVfsMetadata *skeleton = NULL;
#ifdef HAVE_GUDEV
static GUdevClient *gudev_client = NULL;
#endif
static GList *dbus_notification_list = NULL;
static void
tree_info_free (TreeInfo *info)
@ -105,8 +114,78 @@ flush_single (const gchar *filename,
}
static void
flush_all ()
free_bus_notification_info (BusNotificationInfo *info)
{
dbus_notification_list = g_list_remove (dbus_notification_list,
info);
g_object_unref (info->object);
g_source_remove (info->timeout_id);
g_free (info->path);
g_free (info->treefile);
g_free (info);
}
static gboolean
notify_attribute_change (gpointer data)
{
BusNotificationInfo *info;
info = (BusNotificationInfo *) data;
gvfs_metadata_emit_attribute_changed (info->object,
info->treefile,
info->path);
free_bus_notification_info (info);
return G_SOURCE_REMOVE;
}
static void
emit_attribute_change (GVfsMetadata *object,
const gchar *treefile,
const gchar *path)
{
GList *iter;
BusNotificationInfo *info;
for (iter = dbus_notification_list; iter != NULL; iter = iter->next)
{
info = iter->data;
if (g_str_equal (info->treefile, treefile) &&
g_str_equal (info->path, path))
{
break;
}
}
if (iter == NULL)
{
info = g_new0 (BusNotificationInfo, 1);
info->treefile = g_strdup (treefile);
info->path = g_strdup (path);
info->object = g_object_ref (object);
dbus_notification_list = g_list_prepend (dbus_notification_list,
info);
}
else
{
g_source_remove (info->timeout_id);
}
info->timeout_id = g_timeout_add_seconds (WRITEOUT_TIMEOUT_SECS_DBUS,
notify_attribute_change,
info);
}
static void
flush_all (gboolean send_pending_notifications)
{
BusNotificationInfo *info;
while (dbus_notification_list != NULL)
{
info = (BusNotificationInfo *) dbus_notification_list->data;
if (send_pending_notifications)
notify_attribute_change (info);
else
free_bus_notification_info (info);
}
g_hash_table_foreach (tree_infos, (GHFunc) flush_single, NULL);
}
@ -222,6 +301,7 @@ handle_set (GVfsMetadata *object,
}
else
{
emit_attribute_change (object, arg_treefile, arg_path);
gvfs_metadata_complete_set (object, invocation);
}
@ -257,6 +337,7 @@ handle_remove (GVfsMetadata *object,
return TRUE;
}
emit_attribute_change (object, arg_treefile, arg_path);
tree_info_schedule_writeout (info);
gvfs_metadata_complete_remove (object, invocation);
@ -297,6 +378,8 @@ handle_move (GVfsMetadata *object,
/* Remove source if copy succeeded (ignoring errors) */
meta_tree_remove (info->tree, arg_path);
emit_attribute_change (object, arg_treefile, arg_path);
emit_attribute_change (object, arg_treefile, arg_dest_path);
tree_info_schedule_writeout (info);
gvfs_metadata_complete_move (object, invocation);
@ -344,7 +427,7 @@ on_name_lost (GDBusConnection *connection,
GMainLoop *loop = user_data;
/* means that someone has claimed our name (we allow replacement) */
flush_all ();
flush_all (TRUE);
g_main_loop_quit (loop);
}
@ -357,7 +440,7 @@ on_connection_closed (GDBusConnection *connection,
GMainLoop *loop = user_data;
/* session bus died */
flush_all ();
flush_all (FALSE);
g_main_loop_quit (loop);
}

View File

@ -1234,7 +1234,7 @@ meta_builder_write (MetaBuilder *builder,
data = mmap (NULL, RANDOM_TAG_OFFSET + 4, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0);
if (data)
if (data != MAP_FAILED)
{
old_tag = GUINT32_FROM_BE (*(guint32 *)(data + RANDOM_TAG_OFFSET));
*(guint32 *)(data + ROTATED_OFFSET) = 0xffffffff;

View File

@ -170,7 +170,7 @@ GVfsMetadata *
meta_tree_get_metadata_proxy ()
{
static GVfsMetadata *proxy = NULL;
static volatile gsize initialized = 0;
static gsize initialized = 0;
if (g_once_init_enter (&initialized))
{
@ -451,15 +451,7 @@ meta_tree_init (MetaTree *tree)
if (memcmp (tree->header->magic, MAGIC, MAGIC_LEN) != 0)
{
g_warning ("can't init metadata tree %s: wrong magic", tree->filename);
if (!tree->for_write)
goto err;
meta_tree_clear (tree);
if (g_unlink (tree->filename) != 0)
goto err;
goto retry;
goto err;
}
if (tree->header->major != MAJOR_VERSION)

View File

@ -1,7 +1,9 @@
[Unit]
Description=Virtual filesystem service - Apple File Conduit monitor
PartOf=graphical-session.target
[Service]
ExecStart=@libexecdir@/gvfs-afc-volume-monitor
Type=dbus
BusName=org.gtk.vfs.AfcVolumeMonitor
Slice=session.slice

View File

@ -99,8 +99,6 @@ account_attention_needed_cb (GObject *_object, GParamSpec *pspec, gpointer user_
g_clear_object (&self->mount);
}
}
else
g_volume_mount (G_VOLUME (self), G_MOUNT_MOUNT_NONE, NULL, NULL, NULL, NULL);
}
/* ---------------------------------------------------------------------------------------------------- */

View File

@ -326,7 +326,7 @@ get_goa_client_sync (GError **error)
{
static GoaClient *client = NULL;
static GError *_error = NULL;
static volatile gsize initialized = 0;
static gsize initialized = 0;
if (g_once_init_enter (&initialized))
{

View File

@ -1,7 +1,9 @@
[Unit]
Description=Virtual filesystem service - GNOME Online Accounts monitor
PartOf=graphical-session.target
[Service]
ExecStart=@libexecdir@/gvfs-goa-volume-monitor
Type=dbus
BusName=org.gtk.vfs.GoaVolumeMonitor
Slice=session.slice

View File

@ -1,7 +1,9 @@
[Unit]
Description=Virtual filesystem service - digital camera monitor
PartOf=graphical-session.target
[Service]
ExecStart=@libexecdir@/gvfs-gphoto2-volume-monitor
Type=dbus
BusName=org.gtk.vfs.GPhoto2VolumeMonitor
Slice=session.slice

View File

@ -1,33 +1,33 @@
subdir('proxy')
# [[service name suffix, install monitor test data]]
monitors = []
monitors = {}
if enable_afc
monitors += [['Afc', true]]
monitors += {'Afc': true}
endif
if enable_goa
monitors += [['Goa', false]]
monitors += {'Goa': false}
endif
if enable_gphoto2
monitors += [['GPhoto2', true]]
monitors += {'GPhoto2': true}
endif
if enable_mtp
monitors += [['MTP', false]]
monitors += {'MTP': false}
endif
if enable_udisks2
monitors += [['UDisks2', true]]
monitors += {'UDisks2': true}
endif
monitors_test_data = []
foreach monitor: monitors
monitor_name = monitor[0].to_lower()
foreach monitor, monitor_test: monitors
monitor_name = monitor.to_lower()
dbus_service = '@0@.@1@VolumeMonitor'.format(gvfs_namespace, monitor[0])
dbus_service = '@0@.@1@VolumeMonitor'.format(gvfs_namespace, monitor)
dbus_exec = 'gvfs-@0@-volume-monitor'.format(monitor_name)
dbus_systemd_service = ''
@ -66,7 +66,7 @@ foreach monitor: monitors
install_dir: dbus_session_bus_services_dir,
)
if monitor[1]
if monitor_test
monitors_test_data += [monitor_data, monitor_service]
endif

View File

@ -1,7 +1,9 @@
[Unit]
Description=Virtual filesystem service - Media Transfer Protocol monitor
PartOf=graphical-session.target
[Service]
ExecStart=@libexecdir@/gvfs-mtp-volume-monitor
Type=dbus
BusName=org.gtk.vfs.MTPVolumeMonitor
Slice=session.slice

View File

@ -1,7 +1,9 @@
[Unit]
Description=Virtual filesystem service - disk device monitor
PartOf=graphical-session.target
[Service]
ExecStart=@libexecdir@/gvfs-udisks2-volume-monitor
Type=dbus
BusName=org.gtk.vfs.UDisks2VolumeMonitor
Slice=session.slice

View File

@ -580,7 +580,6 @@ typedef struct {
GDrive *drive;
GMountOperation *op;
gboolean op_aborted;
gboolean show_processes_up;
guint unmount_timer_id;
@ -687,13 +686,6 @@ unmount_notify_op_show_processes (UnmountNotifyData *data)
data->show_processes_up = TRUE;
}
static void
unmount_notify_op_aborted (UnmountNotifyData *data)
{
unmount_notify_stop_timer (data);
data->op_aborted = TRUE;
}
static void
unmount_notify_op_reply (UnmountNotifyData *data,
GMountOperationResult result)
@ -704,7 +696,7 @@ unmount_notify_op_reply (UnmountNotifyData *data,
if ((result == G_MOUNT_OPERATION_HANDLED && data->show_processes_up && choice == 1) ||
result == G_MOUNT_OPERATION_ABORTED)
unmount_notify_op_aborted (data);
unmount_notify_stop_timer (data);
else if (result == G_MOUNT_OPERATION_HANDLED)
unmount_notify_ensure_timer (data);
@ -748,7 +740,7 @@ unmount_notify_data_for_operation (GMountOperation *op,
unmount_notify_data_free);
g_signal_connect_swapped (data->op, "aborted",
G_CALLBACK (unmount_notify_op_aborted), data);
G_CALLBACK (unmount_notify_stop_timer), data);
g_signal_connect_swapped (data->op, "show-processes",
G_CALLBACK (unmount_notify_op_show_processes), data);
g_signal_connect_swapped (data->op, "reply",
@ -780,7 +772,7 @@ gvfs_udisks2_unmount_notify_stop (GMountOperation *op,
unmount_notify_stop_timer (data);
if (data->op_aborted || unmount_failed)
if (unmount_failed)
return;
name = unmount_notify_get_name (data);

View File

@ -1496,13 +1496,14 @@ do_unlock (GTask *task)
task);
if (g_strcmp0 (type, "crypto_unknown") == 0)
/* Translators: %s is the description of the volume that is being unlocked */
message = g_strdup_printf (_("Enter a passphrase to unlock the volume\n"
"The volume %s might be a VeraCrypt volume as it contains random data."),
message = g_strdup_printf (_("Authentication Required\n"
"A passphrase is needed to access encrypted data on “%s”.\n"
"The volume might be a VeraCrypt volume as it contains random data."),
data->desc_of_encrypted_to_unlock);
else
/* Translators: %s is the description of the volume that is being unlocked */
message = g_strdup_printf (_("Enter a passphrase to unlock the volume\n"
"The passphrase is needed to access encrypted data on %s."),
message = g_strdup_printf (_("Authentication Required\n"
"A passphrase is needed to access encrypted data on %s."),
data->desc_of_encrypted_to_unlock);
pw_ask_flags = G_ASK_PASSWORD_NEED_PASSWORD | G_ASK_PASSWORD_SAVING_SUPPORTED;

View File

@ -437,7 +437,7 @@ get_udisks_client_sync (GError **error)
{
static UDisksClient *_client = NULL;
static GError *_error = NULL;
static volatile gsize initialized = 0;
static gsize initialized = 0;
if (g_once_init_enter (&initialized))
{
@ -606,37 +606,6 @@ update_all (GVfsUDisks2VolumeMonitor *monitor,
/* ---------------------------------------------------------------------------------------------------- */
static GUnixMountPoint *
get_mount_point_for_mount (GUnixMountEntry *mount_entry)
{
GUnixMountPoint *ret = NULL;
GList *mount_points, *l;
mount_points = g_unix_mount_points_get (NULL);
for (l = mount_points; l != NULL; l = l->next)
{
GUnixMountPoint *mount_point = l->data;
if (g_strcmp0 (g_unix_mount_get_mount_path (mount_entry),
g_unix_mount_point_get_mount_path (mount_point)) == 0)
{
ret = mount_point;
goto out;
}
}
out:
for (l = mount_points; l != NULL; l = l->next)
{
GUnixMountPoint *mount_point = l->data;
if (G_LIKELY (mount_point != ret))
g_unix_mount_point_free (mount_point);
}
g_list_free (mount_points);
return ret;
}
/* ---------------------------------------------------------------------------------------------------- */
static gboolean
should_include (const gchar *mount_path,
const gchar *options)
@ -739,7 +708,7 @@ should_include_mount (GVfsUDisks2VolumeMonitor *monitor,
* in prior to g_unix_mount_get_options to keep support of "comment=" options,
* see https://gitlab.gnome.org/GNOME/gvfs/issues/348.
*/
mount_point = get_mount_point_for_mount (mount_entry);
mount_point = g_unix_mount_point_at (g_unix_mount_get_mount_path (mount_entry), NULL);
if (mount_point != NULL)
{
ret = should_include_mount_point (monitor, mount_point);

View File

@ -1,4 +1,5 @@
# please keep this list sorted alphabetically
ab
af
ar
as
@ -33,8 +34,10 @@ hr
hu
hi
id
ie
it
ja
ka
kk
kn
ko

2213
po/ab.po Normal file

File diff suppressed because it is too large Load Diff

2500
po/be.po

File diff suppressed because it is too large Load Diff

3133
po/bg.po

File diff suppressed because it is too large Load Diff

886
po/ca.po

File diff suppressed because it is too large Load Diff

879
po/cs.po

File diff suppressed because it is too large Load Diff

1791
po/da.po

File diff suppressed because it is too large Load Diff

910
po/de.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

905
po/es.po

File diff suppressed because it is too large Load Diff

968
po/eu.po

File diff suppressed because it is too large Load Diff

4954
po/fa.po

File diff suppressed because it is too large Load Diff

1044
po/fi.po

File diff suppressed because it is too large Load Diff

969
po/fr.po

File diff suppressed because it is too large Load Diff

982
po/fur.po

File diff suppressed because it is too large Load Diff

912
po/gl.po

File diff suppressed because it is too large Load Diff

1887
po/he.po

File diff suppressed because it is too large Load Diff

890
po/hr.po

File diff suppressed because it is too large Load Diff

946
po/hu.po

File diff suppressed because it is too large Load Diff

944
po/id.po

File diff suppressed because it is too large Load Diff

3420
po/ie.po Normal file

File diff suppressed because it is too large Load Diff

879
po/it.po

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More