mirror of https://gitee.com/openkylin/gvfs.git
575 lines
16 KiB
C
575 lines
16 KiB
C
/* GIO - GLib Input, Output and Streaming Library
|
||
*
|
||
* Copyright (C) Carl-Anton Ingmarsson 2011 <ca.ingmarsson@gmail.com>
|
||
*
|
||
* This library is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU Lesser General Public
|
||
* License as published by the Free Software Foundation; either
|
||
* version 2 of the License, or (at your option) any later version.
|
||
*
|
||
* This library is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
* Lesser General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU Lesser General
|
||
* Public License along with this library; if not, write to the
|
||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||
* Boston, MA 02110-1301, USA.
|
||
*
|
||
* Author: Carl-Anton Ingmarsson <ca.ingmarsson@gmail.com>
|
||
*/
|
||
|
||
#include <config.h>
|
||
|
||
#include <stdlib.h>
|
||
#include <glib/gstdio.h>
|
||
#include <glib/gi18n.h>
|
||
#include <gio/gio.h>
|
||
|
||
#ifdef HAVE_GCRYPT
|
||
#include <gcrypt.h>
|
||
#endif
|
||
|
||
#include "gvfsjobmount.h"
|
||
#include "gvfsjobunmount.h"
|
||
#include "gvfsjobqueryinfo.h"
|
||
#include "gvfsjobenumerate.h"
|
||
#include "gvfsjobmountmountable.h"
|
||
#include "gmounttracker.h"
|
||
|
||
#include "gvfsafpserver.h"
|
||
#include "gvfsafpconnection.h"
|
||
|
||
#include "gvfsbackendafpbrowse.h"
|
||
|
||
struct _GVfsBackendAfpBrowseClass
|
||
{
|
||
GVfsBackendClass parent_class;
|
||
};
|
||
|
||
struct _GVfsBackendAfpBrowse
|
||
{
|
||
GVfsBackend parent_instance;
|
||
|
||
GNetworkAddress *addr;
|
||
char *user;
|
||
|
||
GMountTracker *mount_tracker;
|
||
GVfsAfpServer *server;
|
||
|
||
char *logged_in_user;
|
||
GPtrArray *volumes;
|
||
};
|
||
|
||
|
||
G_DEFINE_TYPE (GVfsBackendAfpBrowse, g_vfs_backend_afp_browse, G_VFS_TYPE_BACKEND);
|
||
|
||
|
||
static void
|
||
get_volumes_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
|
||
{
|
||
GVfsAfpServer *server = G_VFS_AFP_SERVER (source_object);
|
||
GTask *task = G_TASK (user_data);
|
||
GVfsBackendAfpBrowse *afp_backend;
|
||
GPtrArray *volumes;
|
||
GError *err = NULL;
|
||
|
||
afp_backend = G_VFS_BACKEND_AFP_BROWSE (g_task_get_source_object (task));
|
||
|
||
volumes = g_vfs_afp_server_get_volumes_finish (server, res, &err);
|
||
if (!volumes)
|
||
{
|
||
g_task_return_error (task, err);
|
||
g_object_unref (task);
|
||
return;
|
||
}
|
||
|
||
if (afp_backend->volumes)
|
||
g_ptr_array_unref (afp_backend->volumes);
|
||
afp_backend->volumes = volumes;
|
||
|
||
g_task_return_boolean (task, TRUE);
|
||
g_object_unref (task);
|
||
}
|
||
|
||
static void
|
||
update_cache (GVfsBackendAfpBrowse *afp_backend,
|
||
GCancellable *cancellable,
|
||
GAsyncReadyCallback callback,
|
||
gpointer user_data)
|
||
{
|
||
GTask *task;
|
||
|
||
task = g_task_new (afp_backend, cancellable, callback, user_data);
|
||
g_task_set_source_tag (task, update_cache);
|
||
|
||
g_vfs_afp_server_get_volumes (afp_backend->server, cancellable, get_volumes_cb, task);
|
||
}
|
||
|
||
static gboolean
|
||
update_cache_finish (GVfsBackendAfpBrowse *afp_backend,
|
||
GAsyncResult *res,
|
||
GError **error)
|
||
{
|
||
g_return_val_if_fail (g_task_is_valid (res, afp_backend), FALSE);
|
||
g_return_val_if_fail (g_async_result_is_tagged (res, update_cache), FALSE);
|
||
|
||
return g_task_propagate_boolean (G_TASK (res), error);
|
||
}
|
||
|
||
static GVfsAfpVolumeData *
|
||
find_volume (GVfsBackendAfpBrowse *afp_backend,
|
||
char *filename)
|
||
{
|
||
char *end;
|
||
guint len;
|
||
guint i;
|
||
|
||
while (*filename == '/')
|
||
filename++;
|
||
|
||
end = strchr (filename, '/');
|
||
if (end)
|
||
{
|
||
len = end - filename;
|
||
|
||
while (*end == '/')
|
||
end++;
|
||
|
||
if (*end != 0)
|
||
return NULL;
|
||
}
|
||
else
|
||
len = strlen (filename);
|
||
|
||
for (i = 0; i < afp_backend->volumes->len; i++)
|
||
{
|
||
GVfsAfpVolumeData *vol_data = g_ptr_array_index (afp_backend->volumes, i);
|
||
|
||
if (strlen (vol_data->name) == len && strncmp (vol_data->name, filename, len) == 0)
|
||
return vol_data;
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
static void
|
||
mount_mountable_cb (GObject *source_object,
|
||
GAsyncResult *res,
|
||
gpointer user_data)
|
||
{
|
||
GVfsBackendAfpBrowse *afp_backend = G_VFS_BACKEND_AFP_BROWSE (source_object);
|
||
GVfsJobMountMountable *job = G_VFS_JOB_MOUNT_MOUNTABLE (user_data);
|
||
|
||
GError *err;
|
||
GVfsAfpVolumeData *vol_data;
|
||
GMountSpec *mount_spec;
|
||
|
||
if (!update_cache_finish (afp_backend, res, &err))
|
||
{
|
||
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
|
||
g_error_free (err);
|
||
return;
|
||
}
|
||
|
||
vol_data = find_volume (afp_backend, job->filename);
|
||
if (!vol_data)
|
||
{
|
||
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||
_("File doesn’t exist"));
|
||
return;
|
||
}
|
||
|
||
mount_spec = g_mount_spec_new ("afp-volume");
|
||
g_mount_spec_set (mount_spec, "host",
|
||
g_network_address_get_hostname (G_NETWORK_ADDRESS (afp_backend->addr)));
|
||
g_mount_spec_set (mount_spec, "volume", vol_data->name);
|
||
g_mount_spec_set (mount_spec, "user", afp_backend->logged_in_user);
|
||
|
||
g_vfs_job_mount_mountable_set_target (job, mount_spec, "/", TRUE);
|
||
g_mount_spec_unref (mount_spec);
|
||
|
||
g_vfs_job_succeeded (G_VFS_JOB (job));
|
||
}
|
||
|
||
static gboolean
|
||
try_mount_mountable (GVfsBackend *backend,
|
||
GVfsJobMountMountable *job,
|
||
const char *filename,
|
||
GMountSource *mount_source)
|
||
{
|
||
GVfsBackendAfpBrowse *afp_backend = G_VFS_BACKEND_AFP_BROWSE (backend);
|
||
|
||
if (is_root (filename))
|
||
{
|
||
g_vfs_job_failed (G_VFS_JOB (job),
|
||
G_IO_ERROR, G_IO_ERROR_NOT_MOUNTABLE_FILE,
|
||
_("Not a mountable file"));
|
||
return TRUE;
|
||
}
|
||
|
||
update_cache (afp_backend, G_VFS_JOB (job)->cancellable, mount_mountable_cb, job);
|
||
|
||
return TRUE;
|
||
|
||
}
|
||
|
||
static void
|
||
fill_info (GFileInfo *info, GVfsAfpVolumeData *vol_data, GVfsBackendAfpBrowse *afp_backend)
|
||
{
|
||
GIcon *icon;
|
||
GMountSpec *mount_spec;
|
||
char *uri;
|
||
|
||
g_file_info_set_name (info, vol_data->name);
|
||
g_file_info_set_display_name (info, vol_data->name);
|
||
g_file_info_set_edit_name (info, vol_data->name);
|
||
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_VIRTUAL, TRUE);
|
||
g_file_info_set_content_type (info, "inode/directory");
|
||
g_file_info_set_file_type (info, G_FILE_TYPE_MOUNTABLE);
|
||
|
||
g_file_info_set_attribute_boolean (info, "afp::volume-password-protected", (vol_data->flags & 0x01));
|
||
|
||
icon = g_themed_icon_new_with_default_fallbacks ("folder-remote-afp");
|
||
g_file_info_set_icon (info, icon);
|
||
g_object_unref (icon);
|
||
|
||
icon = g_themed_icon_new_with_default_fallbacks ("folder-remote-symbolic");
|
||
g_file_info_set_symbolic_icon (info, icon);
|
||
g_object_unref (icon);
|
||
|
||
mount_spec = g_mount_spec_new ("afp-volume");
|
||
g_mount_spec_set (mount_spec, "host",
|
||
g_network_address_get_hostname (G_NETWORK_ADDRESS (afp_backend->addr)));
|
||
g_mount_spec_set (mount_spec, "volume", vol_data->name);
|
||
g_mount_spec_set (mount_spec, "user", afp_backend->logged_in_user);
|
||
|
||
if (g_mount_tracker_has_mount_spec (afp_backend->mount_tracker, mount_spec))
|
||
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT, FALSE);
|
||
else
|
||
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT, TRUE);
|
||
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT, FALSE);
|
||
g_mount_spec_unref (mount_spec);
|
||
|
||
uri = g_strdup_printf ("afp://%s/%s",
|
||
g_network_address_get_hostname (G_NETWORK_ADDRESS (afp_backend->addr)),
|
||
vol_data->name);
|
||
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI,
|
||
uri);
|
||
g_free (uri);
|
||
}
|
||
|
||
static void
|
||
enumerate_cache_updated_cb (GObject *source_object,
|
||
GAsyncResult *res,
|
||
gpointer user_data)
|
||
{
|
||
GVfsBackendAfpBrowse *afp_backend = G_VFS_BACKEND_AFP_BROWSE (source_object);
|
||
GVfsJobEnumerate *job = G_VFS_JOB_ENUMERATE (user_data);
|
||
|
||
GError *err = NULL;
|
||
guint i;
|
||
|
||
if (!update_cache_finish (afp_backend, res, &err))
|
||
{
|
||
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
|
||
g_error_free (err);
|
||
return;
|
||
}
|
||
|
||
g_vfs_job_succeeded (G_VFS_JOB (job));
|
||
|
||
for (i = 0; i < afp_backend->volumes->len; i++)
|
||
{
|
||
GVfsAfpVolumeData *vol_data = g_ptr_array_index (afp_backend->volumes, i);
|
||
|
||
GFileInfo *info;
|
||
|
||
info = g_file_info_new ();
|
||
fill_info (info, vol_data, afp_backend);
|
||
g_vfs_job_enumerate_add_info (job, info);
|
||
|
||
g_object_unref (info);
|
||
}
|
||
|
||
g_vfs_job_enumerate_done (job);
|
||
}
|
||
|
||
static gboolean
|
||
try_enumerate (GVfsBackend *backend,
|
||
GVfsJobEnumerate *job,
|
||
const char *filename,
|
||
GFileAttributeMatcher *attribute_matcher,
|
||
GFileQueryInfoFlags flags)
|
||
{
|
||
GVfsBackendAfpBrowse *afp_backend = G_VFS_BACKEND_AFP_BROWSE (backend);
|
||
|
||
if (!is_root(filename))
|
||
{
|
||
g_vfs_job_failed (G_VFS_JOB (job),
|
||
G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||
_("File doesn’t exist"));
|
||
return TRUE;
|
||
}
|
||
|
||
update_cache (afp_backend, G_VFS_JOB (job)->cancellable,
|
||
enumerate_cache_updated_cb, job);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static void
|
||
query_info_cb (GObject *source_object,
|
||
GAsyncResult *res,
|
||
gpointer user_data)
|
||
{
|
||
GVfsBackendAfpBrowse *afp_backend = G_VFS_BACKEND_AFP_BROWSE (source_object);
|
||
GVfsJobQueryInfo *job = G_VFS_JOB_QUERY_INFO (user_data);
|
||
|
||
GError *err = NULL;
|
||
GVfsAfpVolumeData *vol_data;
|
||
|
||
if (!update_cache_finish (afp_backend, res, &err))
|
||
{
|
||
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
|
||
g_error_free (err);
|
||
return;
|
||
}
|
||
|
||
vol_data = find_volume (afp_backend, job->filename);
|
||
if (!vol_data)
|
||
{
|
||
g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||
_("File doesn’t exist"));
|
||
return;
|
||
}
|
||
|
||
fill_info (job->file_info, vol_data, afp_backend);
|
||
g_vfs_job_succeeded (G_VFS_JOB (job));
|
||
}
|
||
|
||
static gboolean
|
||
try_query_info (GVfsBackend *backend,
|
||
GVfsJobQueryInfo *job,
|
||
const char *filename,
|
||
GFileQueryInfoFlags flags,
|
||
GFileInfo *info,
|
||
GFileAttributeMatcher *matcher)
|
||
{
|
||
if (is_root (filename))
|
||
{
|
||
GIcon *icon;
|
||
|
||
g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
|
||
g_file_info_set_name (info, "/");
|
||
g_file_info_set_display_name (info, g_vfs_backend_get_display_name (backend));
|
||
g_file_info_set_content_type (info, "inode/directory");
|
||
icon = g_vfs_backend_get_icon (backend);
|
||
if (icon != NULL)
|
||
g_file_info_set_icon (info, icon);
|
||
|
||
icon = g_vfs_backend_get_symbolic_icon (backend);
|
||
if (icon != NULL)
|
||
g_file_info_set_symbolic_icon (info, icon);
|
||
|
||
g_vfs_job_succeeded (G_VFS_JOB (job));
|
||
}
|
||
else
|
||
update_cache (G_VFS_BACKEND_AFP_BROWSE (backend), G_VFS_JOB (job)->cancellable,
|
||
query_info_cb, job);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static void
|
||
do_unmount (GVfsBackend *backend,
|
||
GVfsJobUnmount *job,
|
||
GMountUnmountFlags flags,
|
||
GMountSource *mount_source)
|
||
{
|
||
GVfsBackendAfpBrowse *afp_backend = G_VFS_BACKEND_AFP_BROWSE (backend);
|
||
|
||
if (!(flags & G_MOUNT_UNMOUNT_FORCE))
|
||
{
|
||
g_vfs_afp_server_logout_sync (afp_backend->server, G_VFS_JOB (job)->cancellable,
|
||
NULL);
|
||
}
|
||
|
||
g_vfs_job_succeeded (G_VFS_JOB (job));
|
||
}
|
||
|
||
static void
|
||
do_mount (GVfsBackend *backend,
|
||
GVfsJobMount *job,
|
||
GMountSpec *mount_spec,
|
||
GMountSource *mount_source,
|
||
gboolean is_automount)
|
||
{
|
||
GVfsBackendAfpBrowse *afp_backend = G_VFS_BACKEND_AFP_BROWSE (backend);
|
||
|
||
gboolean res;
|
||
GError *err = NULL;
|
||
|
||
const GVfsAfpServerInfo *info;
|
||
GMountSpec *afp_mount_spec;
|
||
char *server_name;
|
||
char *display_name;
|
||
|
||
afp_backend->server = g_vfs_afp_server_new (afp_backend->addr);
|
||
|
||
res = g_vfs_afp_server_login (afp_backend->server, afp_backend->user, mount_source,
|
||
&afp_backend->logged_in_user,
|
||
G_VFS_JOB (job)->cancellable, &err);
|
||
if (!res)
|
||
goto error;
|
||
|
||
/* set mount info */
|
||
afp_mount_spec = g_mount_spec_new ("afp-server");
|
||
g_mount_spec_set (afp_mount_spec, "host",
|
||
g_network_address_get_hostname (G_NETWORK_ADDRESS (afp_backend->addr)));
|
||
if (afp_backend->user)
|
||
g_mount_spec_set (afp_mount_spec, "user", afp_backend->user);
|
||
|
||
g_vfs_backend_set_mount_spec (backend, afp_mount_spec);
|
||
g_mount_spec_unref (afp_mount_spec);
|
||
|
||
info = g_vfs_afp_server_get_info (afp_backend->server);
|
||
|
||
if (info->utf8_server_name)
|
||
server_name = info->utf8_server_name;
|
||
else
|
||
server_name = info->server_name;
|
||
|
||
if (afp_backend->user)
|
||
/* Translators: first %s is username and second serververname */
|
||
display_name = g_strdup_printf (_("%s on %s"), afp_backend->user,
|
||
server_name);
|
||
else
|
||
/* Translators: %s is the servername */
|
||
display_name = g_strdup_printf (_("%s"),
|
||
server_name);
|
||
g_vfs_backend_set_display_name (backend, display_name);
|
||
g_free (display_name);
|
||
|
||
g_vfs_backend_set_icon_name (backend, "network-server-afp");
|
||
g_vfs_backend_set_user_visible (backend, FALSE);
|
||
|
||
|
||
g_vfs_job_succeeded (G_VFS_JOB (job));
|
||
return;
|
||
|
||
error:
|
||
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
|
||
}
|
||
|
||
static gboolean
|
||
try_mount (GVfsBackend *backend,
|
||
GVfsJobMount *job,
|
||
GMountSpec *mount_spec,
|
||
GMountSource *mount_source,
|
||
gboolean is_automount)
|
||
{
|
||
GVfsBackendAfpBrowse *afp_backend = G_VFS_BACKEND_AFP_BROWSE (backend);
|
||
|
||
const char *host, *portstr, *user;
|
||
guint16 port = 548;
|
||
|
||
host = g_mount_spec_get (mount_spec, "host");
|
||
if (host == NULL)
|
||
{
|
||
g_vfs_job_failed (G_VFS_JOB (job),
|
||
G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("No hostname specified"));
|
||
return TRUE;
|
||
}
|
||
|
||
portstr = g_mount_spec_get (mount_spec, "port");
|
||
if (portstr != NULL)
|
||
{
|
||
port = atoi (portstr);
|
||
}
|
||
|
||
afp_backend->addr = G_NETWORK_ADDRESS (g_network_address_new (host, port));
|
||
|
||
user = g_mount_spec_get (mount_spec, "user");
|
||
afp_backend->user = g_strdup (user);
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static void
|
||
g_vfs_backend_afp_browse_init (GVfsBackendAfpBrowse *object)
|
||
{
|
||
GVfsBackendAfpBrowse *afp_backend = G_VFS_BACKEND_AFP_BROWSE (object);
|
||
|
||
afp_backend->mount_tracker = g_mount_tracker_new (NULL, FALSE);
|
||
|
||
afp_backend->addr = NULL;
|
||
afp_backend->user = NULL;
|
||
|
||
afp_backend->logged_in_user = NULL;
|
||
afp_backend->volumes = NULL;
|
||
}
|
||
|
||
static void
|
||
g_vfs_backend_afp_browse_finalize (GObject *object)
|
||
{
|
||
GVfsBackendAfpBrowse *afp_backend = G_VFS_BACKEND_AFP_BROWSE (object);
|
||
|
||
g_object_unref (afp_backend->mount_tracker);
|
||
|
||
if (afp_backend->addr)
|
||
g_object_unref (afp_backend->addr);
|
||
|
||
g_free (afp_backend->user);
|
||
|
||
g_free (afp_backend->logged_in_user);
|
||
if (afp_backend->volumes)
|
||
g_ptr_array_unref (afp_backend->volumes);
|
||
|
||
G_OBJECT_CLASS (g_vfs_backend_afp_browse_parent_class)->finalize (object);
|
||
}
|
||
|
||
static gboolean
|
||
try_query_fs_info (GVfsBackend *backend,
|
||
GVfsJobQueryFsInfo *job,
|
||
const char *filename,
|
||
GFileInfo *info,
|
||
GFileAttributeMatcher *matcher)
|
||
{
|
||
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;
|
||
}
|
||
|
||
static void
|
||
g_vfs_backend_afp_browse_class_init (GVfsBackendAfpBrowseClass *klass)
|
||
{
|
||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||
GVfsBackendClass *backend_class = G_VFS_BACKEND_CLASS (klass);
|
||
|
||
object_class->finalize = g_vfs_backend_afp_browse_finalize;
|
||
|
||
backend_class->try_mount = try_mount;
|
||
backend_class->mount = do_mount;
|
||
backend_class->unmount = do_unmount;
|
||
backend_class->try_query_info = try_query_info;
|
||
backend_class->try_enumerate = try_enumerate;
|
||
backend_class->try_mount_mountable = try_mount_mountable;
|
||
backend_class->try_query_fs_info = try_query_fs_info;
|
||
}
|
||
|
||
void
|
||
g_vfs_afp_browse_daemon_init (void)
|
||
{
|
||
g_set_application_name (_("Apple Filing Protocol Service"));
|
||
|
||
#ifdef HAVE_GCRYPT
|
||
gcry_check_version (NULL);
|
||
gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
|
||
#endif
|
||
}
|