/* GIO - GLib Input, Output and Streaming Library * * Copyright (C) Carl-Anton Ingmarsson 2011 * * 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 */ #include #include #include #include #include #ifdef HAVE_GCRYPT #include #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 }