/* -*- Mode: C; tab-width: 8; indent-tabs-mode: 8; c-basic-offset: 8 -*- */ /* desktop-method.c Copyright (C) 2001 Red Hat, Inc. The Gnome Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The Gnome 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the Gnome Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* URI scheme for remapping directories under magic URIs, used * for the magic desktop file directories such as start-here. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* FIXME Maybe when chaining to file:, we should call the gnome-vfs wrapper * functions, instead of the file: methods directly. */ #define N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0])) static GnomeVFSURI* desktop_uri_to_file_uri (GnomeVFSURI *desktop_uri); static GnomeVFSResult open_merged_directory (GnomeVFSMethod *method, GnomeVFSMethodHandle **method_handle, GnomeVFSURI *uri, GnomeVFSFileInfoOptions options, GnomeVFSContext *context); static char* create_file_uri_in_dir (const char *dir, const char *filename); static GnomeVFSMethod *parent_method = NULL; static GnomeVFSResult do_open (GnomeVFSMethod *method, GnomeVFSMethodHandle **method_handle, GnomeVFSURI *uri, GnomeVFSOpenMode mode, GnomeVFSContext *context) { GnomeVFSURI *file_uri; GnomeVFSResult result; file_uri = desktop_uri_to_file_uri (uri); result = (* parent_method->open) (parent_method, method_handle, file_uri, mode, context); gnome_vfs_uri_unref (file_uri); return result; } static GnomeVFSResult do_create (GnomeVFSMethod *method, GnomeVFSMethodHandle **method_handle, GnomeVFSURI *uri, GnomeVFSOpenMode mode, gboolean exclusive, guint perm, GnomeVFSContext *context) { GnomeVFSURI *file_uri; GnomeVFSResult result; file_uri = desktop_uri_to_file_uri (uri); result = (* parent_method->create) (parent_method, method_handle, file_uri, mode, exclusive, perm, context); gnome_vfs_uri_unref (file_uri); return result; } static GnomeVFSResult do_close (GnomeVFSMethod *method, GnomeVFSMethodHandle *method_handle, GnomeVFSContext *context) { GnomeVFSResult result; result = (* parent_method->close) (parent_method, method_handle, context); return result; } static GnomeVFSResult do_read (GnomeVFSMethod *method, GnomeVFSMethodHandle *method_handle, gpointer buffer, GnomeVFSFileSize num_bytes, GnomeVFSFileSize *bytes_read, GnomeVFSContext *context) { GnomeVFSResult result; result = (* parent_method->read) (parent_method, method_handle, buffer, num_bytes, bytes_read, context); return result; } static GnomeVFSResult do_write (GnomeVFSMethod *method, GnomeVFSMethodHandle *method_handle, gconstpointer buffer, GnomeVFSFileSize num_bytes, GnomeVFSFileSize *bytes_written, GnomeVFSContext *context) { GnomeVFSResult result; result = (* parent_method->write) (parent_method, method_handle, buffer, num_bytes, bytes_written, context); return result; } static GnomeVFSResult do_seek (GnomeVFSMethod *method, GnomeVFSMethodHandle *method_handle, GnomeVFSSeekPosition whence, GnomeVFSFileOffset offset, GnomeVFSContext *context) { GnomeVFSResult result; result = (* parent_method->seek) (parent_method, method_handle, whence, offset, context); return result; } static GnomeVFSResult do_tell (GnomeVFSMethod *method, GnomeVFSMethodHandle *method_handle, GnomeVFSFileOffset *offset_return) { GnomeVFSResult result; result = (* parent_method->tell) (parent_method, method_handle, offset_return); return result; } static GnomeVFSResult do_truncate_handle (GnomeVFSMethod *method, GnomeVFSMethodHandle *method_handle, GnomeVFSFileSize where, GnomeVFSContext *context) { GnomeVFSResult result; result = (* parent_method->truncate_handle) (parent_method, method_handle, where, context); return result; } static GnomeVFSResult do_truncate (GnomeVFSMethod *method, GnomeVFSURI *uri, GnomeVFSFileSize where, GnomeVFSContext *context) { GnomeVFSURI *file_uri; GnomeVFSResult result; file_uri = desktop_uri_to_file_uri (uri); result = (* parent_method->truncate) (parent_method, file_uri, where, context); gnome_vfs_uri_unref (file_uri); return result; } typedef struct _DirHandle DirHandle; struct _DirHandle { GSList *next; GSList *handles; }; static GnomeVFSResult do_open_directory (GnomeVFSMethod *method, GnomeVFSMethodHandle **method_handle, GnomeVFSURI *uri, GnomeVFSFileInfoOptions options, GnomeVFSContext *context) { return open_merged_directory (method, method_handle, uri, options, context); } static GnomeVFSResult do_close_directory (GnomeVFSMethod *method, GnomeVFSMethodHandle *method_handle, GnomeVFSContext *context) { GnomeVFSResult result; GSList *tmp; DirHandle *dh; dh = (DirHandle*) method_handle; result = GNOME_VFS_OK; tmp = dh->handles; while (tmp != NULL) { GnomeVFSResult this_result; this_result = (* parent_method->close_directory) (parent_method, tmp->data, context); if (this_result != GNOME_VFS_OK) result = this_result; tmp = tmp->next; } g_slist_free (dh->handles); g_free (dh); return result; } static GnomeVFSResult do_read_directory (GnomeVFSMethod *method, GnomeVFSMethodHandle *method_handle, GnomeVFSFileInfo *file_info, GnomeVFSContext *context) { GnomeVFSResult result; GnomeVFSMethodHandle *parent_handle; DirHandle *dh; dh = (DirHandle*) method_handle; if (dh->next == NULL) { return GNOME_VFS_ERROR_EOF; } next: parent_handle = dh->next->data; result = (* parent_method->read_directory) (parent_method, parent_handle, file_info, context); if (result != GNOME_VFS_OK) { dh->next = dh->next->next; if (dh->next) goto next; else return result; } else { return GNOME_VFS_OK; } } static void set_directory_mime_type (GnomeVFSFileInfo *file_info) { g_free (file_info->mime_type); file_info->mime_type = g_strdup ("x-directory/vfolder-desktop"); file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE; } static GnomeVFSResult do_get_file_info (GnomeVFSMethod *method, GnomeVFSURI *uri, GnomeVFSFileInfo *file_info, GnomeVFSFileInfoOptions options, GnomeVFSContext *context) { GnomeVFSURI *file_uri; GnomeVFSResult result; file_uri = desktop_uri_to_file_uri (uri); result = (* parent_method->get_file_info) (parent_method, file_uri, file_info, options, context); if (file_info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) set_directory_mime_type (file_info); gnome_vfs_uri_unref (file_uri); return result; } static GnomeVFSResult do_get_file_info_from_handle (GnomeVFSMethod *method, GnomeVFSMethodHandle *method_handle, GnomeVFSFileInfo *file_info, GnomeVFSFileInfoOptions options, GnomeVFSContext *context) { GnomeVFSResult result; result = (* parent_method->get_file_info_from_handle) (parent_method, method_handle, file_info, options, context); if (file_info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) set_directory_mime_type (file_info); return result; } static gboolean do_is_local (GnomeVFSMethod *method, const GnomeVFSURI *uri) { return TRUE; } static GnomeVFSResult do_make_directory (GnomeVFSMethod *method, GnomeVFSURI *uri, guint perm, GnomeVFSContext *context) { GnomeVFSURI *file_uri; GnomeVFSResult result; file_uri = desktop_uri_to_file_uri (uri); result = (* parent_method->make_directory) (parent_method, file_uri, perm, context); gnome_vfs_uri_unref (file_uri); return result; } static GnomeVFSResult do_remove_directory (GnomeVFSMethod *method, GnomeVFSURI *uri, GnomeVFSContext *context) { GnomeVFSURI *file_uri; GnomeVFSResult result; file_uri = desktop_uri_to_file_uri (uri); result = (* parent_method->remove_directory) (parent_method, file_uri, context); gnome_vfs_uri_unref (file_uri); return result; } static GnomeVFSResult do_find_directory (GnomeVFSMethod *method, GnomeVFSURI *near_uri, GnomeVFSFindDirectoryKind kind, GnomeVFSURI **result_uri, gboolean create_if_needed, gboolean find_if_needed, guint permissions, GnomeVFSContext *context) { GnomeVFSURI *file_uri; GnomeVFSURI *file_result_uri; GnomeVFSResult result; file_result_uri = NULL; file_uri = desktop_uri_to_file_uri (near_uri); result = (* parent_method->find_directory) (parent_method, file_uri, kind, &file_result_uri, create_if_needed, find_if_needed, permissions, context); gnome_vfs_uri_unref (file_uri); if (result_uri) *result_uri = file_result_uri; if (file_result_uri == NULL) result = GNOME_VFS_ERROR_NOT_FOUND; return result; } static GnomeVFSResult do_move (GnomeVFSMethod *method, GnomeVFSURI *old_uri, GnomeVFSURI *new_uri, gboolean force_replace, GnomeVFSContext *context) { GnomeVFSURI *old_file_uri; GnomeVFSURI *new_file_uri; GnomeVFSResult result; old_file_uri = desktop_uri_to_file_uri (old_uri); new_file_uri = desktop_uri_to_file_uri (new_uri); result = (* parent_method->move) (parent_method, old_file_uri, new_file_uri, force_replace, context); gnome_vfs_uri_unref (old_file_uri); gnome_vfs_uri_unref (new_file_uri); return result; } static GnomeVFSResult do_unlink (GnomeVFSMethod *method, GnomeVFSURI *uri, GnomeVFSContext *context) { GnomeVFSURI *file_uri; GnomeVFSResult result; file_uri = desktop_uri_to_file_uri (uri); result = (* parent_method->unlink) (parent_method, file_uri, context); gnome_vfs_uri_unref (file_uri); return result; } static GnomeVFSResult do_create_symbolic_link (GnomeVFSMethod *method, GnomeVFSURI *uri, const char *target_reference, GnomeVFSContext *context) { GnomeVFSURI *file_uri; GnomeVFSResult result; file_uri = desktop_uri_to_file_uri (uri); result = (* parent_method->create_symbolic_link) (parent_method, file_uri, target_reference, context); gnome_vfs_uri_unref (file_uri); return result; } static GnomeVFSResult do_check_same_fs (GnomeVFSMethod *method, GnomeVFSURI *source_uri, GnomeVFSURI *target_uri, gboolean *same_fs_return, GnomeVFSContext *context) { GnomeVFSURI *source_file_uri; GnomeVFSURI *target_file_uri; GnomeVFSResult result; source_file_uri = desktop_uri_to_file_uri (source_uri); target_file_uri = desktop_uri_to_file_uri (target_uri); result = (* parent_method->check_same_fs) (parent_method, source_file_uri, target_file_uri, same_fs_return, context); gnome_vfs_uri_unref (source_file_uri); gnome_vfs_uri_unref (target_file_uri); return result; } static GnomeVFSResult do_set_file_info (GnomeVFSMethod *method, GnomeVFSURI *uri, const GnomeVFSFileInfo *info, GnomeVFSSetFileInfoMask mask, GnomeVFSContext *context) { GnomeVFSURI *file_uri; GnomeVFSResult result; file_uri = desktop_uri_to_file_uri (uri); result = (* parent_method->set_file_info) (parent_method, file_uri, info, mask, context); gnome_vfs_uri_unref (file_uri); return result; } typedef struct { GnomeVFSMonitorHandle *handle; GnomeVFSURI *desktop_uri; } DesktopMonitorHandle; static void monitor_notify_cb (GnomeVFSMonitorHandle *handle, const gchar *monitor_uri, const gchar *info_uri, GnomeVFSMonitorEventType event_type, gpointer user_data) { DesktopMonitorHandle *monitor_handle; GnomeVFSURI *desktop_info_uri; const gchar *uri_diff; gint monitor_uri_len; monitor_handle = (DesktopMonitorHandle *) user_data; desktop_info_uri = NULL; monitor_uri_len = strlen (monitor_uri); if (info_uri != NULL && strncmp (info_uri, monitor_uri, monitor_uri_len) == 0) { uri_diff = &info_uri [monitor_uri_len]; if (*uri_diff != '\0') { desktop_info_uri = gnome_vfs_uri_append_string ( monitor_handle->desktop_uri, uri_diff); } else { desktop_info_uri = monitor_handle->desktop_uri; gnome_vfs_uri_ref (desktop_info_uri); } } gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *) monitor_handle, desktop_info_uri, event_type); gnome_vfs_uri_unref (desktop_info_uri); } static GnomeVFSResult do_monitor_add (GnomeVFSMethod *method, GnomeVFSMethodHandle **method_handle_return, GnomeVFSURI *uri, GnomeVFSMonitorType monitor_type) { DesktopMonitorHandle *monitor_handle; GnomeVFSURI *file_uri; GnomeVFSResult result; monitor_handle = g_new0 (DesktopMonitorHandle, 1); monitor_handle->desktop_uri = uri; gnome_vfs_uri_ref (uri); file_uri = desktop_uri_to_file_uri (uri); result = _gnome_vfs_monitor_do_add (parent_method, &monitor_handle->handle, file_uri, monitor_type, monitor_notify_cb, monitor_handle); gnome_vfs_uri_unref (file_uri); if (result != GNOME_VFS_OK) { gnome_vfs_uri_unref (monitor_handle->desktop_uri); g_free (monitor_handle); } *method_handle_return = (GnomeVFSMethodHandle *) monitor_handle; return result; } static GnomeVFSResult do_monitor_cancel (GnomeVFSMethod *method, GnomeVFSMethodHandle *method_handle) { DesktopMonitorHandle *monitor_handle; GnomeVFSResult result; monitor_handle = (DesktopMonitorHandle *) method_handle; result = _gnome_vfs_monitor_do_cancel (monitor_handle->handle); gnome_vfs_uri_unref (monitor_handle->desktop_uri); g_free (monitor_handle); return result; } /* gnome-vfs bureaucracy */ static GnomeVFSMethod method = { sizeof (GnomeVFSMethod), do_open, do_create, do_close, do_read, do_write, do_seek, do_tell, do_truncate_handle, do_open_directory, do_close_directory, do_read_directory, do_get_file_info, do_get_file_info_from_handle, do_is_local, do_make_directory, do_remove_directory, do_move, do_unlink, do_check_same_fs, do_set_file_info, do_truncate, do_find_directory, do_create_symbolic_link, do_monitor_add, do_monitor_cancel }; typedef enum { SCHEME_FAVORITES, SCHEME_PREFERENCES, SCHEME_START_HERE, SCHEME_SYSTEM_SETTINGS, SCHEME_SERVER_SETTINGS, SCHEME_PROGRAMS, SCHEME_NETWORK } SchemeID; #define MAX_DIRECTORIES 3 #define DIRECTORIES_INITIALIZER { NULL, NULL, NULL } typedef struct _SchemeDescription SchemeDescription; struct _SchemeDescription { SchemeID id; const char *scheme; char *directories[MAX_DIRECTORIES]; }; static SchemeDescription schemes[] = { { SCHEME_FAVORITES, "favorites", DIRECTORIES_INITIALIZER }, { SCHEME_PREFERENCES, "preferences", DIRECTORIES_INITIALIZER }, { SCHEME_START_HERE, "start-here", DIRECTORIES_INITIALIZER }, { SCHEME_SYSTEM_SETTINGS, "system-settings", DIRECTORIES_INITIALIZER }, { SCHEME_SERVER_SETTINGS, "server-settings", DIRECTORIES_INITIALIZER }, { SCHEME_PROGRAMS, "programs", DIRECTORIES_INITIALIZER }, { SCHEME_NETWORK, "network", DIRECTORIES_INITIALIZER } }; GnomeVFSMethod * vfs_module_init (const char *method_name, const char *args) { int i; parent_method = gnome_vfs_method_get ("file"); if (parent_method == NULL) { g_error ("Could not find 'file' method for gnome-vfs"); return NULL; } i = 0; while (i < N_ELEMENTS (schemes)) { switch (schemes[i].id) { case SCHEME_FAVORITES: schemes[i].directories[0] = g_strconcat (g_get_home_dir (), "/.gnome/apps", NULL); break; case SCHEME_PREFERENCES: /* FIXME I think the GNOME 2 control center will move * this, but we don't know where to yet */ schemes[i].directories[0] = g_strconcat (DATADIR, "/control-center/capplets", NULL); break; case SCHEME_START_HERE: schemes[i].directories[0] = g_strconcat (SYSCONFDIR, "/X11/starthere", NULL); break; case SCHEME_SYSTEM_SETTINGS: schemes[i].directories[0] = g_strconcat (SYSCONFDIR, "/X11/sysconfig", NULL); break; case SCHEME_SERVER_SETTINGS: schemes[i].directories[0] = g_strconcat (SYSCONFDIR, "/X11/serverconfig", NULL); break; case SCHEME_PROGRAMS: schemes[i].directories[0] = g_strconcat (SYSCONFDIR, "/X11/applnk", NULL); schemes[i].directories[1] = g_strconcat (DATADIR, "gnome/apps", NULL); break; case SCHEME_NETWORK: schemes[i].directories[0] = g_build_filename (DATADIR, "gnome/network", NULL); break; default: g_assert_not_reached (); break; } ++i; } return &method; } void vfs_module_shutdown (GnomeVFSMethod *method) { int i; i = 0; while (i < N_ELEMENTS (schemes)) { int j; j = 0; while (j < MAX_DIRECTORIES) { g_free (schemes[i].directories[j]); schemes[i].directories[j] = NULL; ++j; } ++i; } } static const SchemeDescription* get_desc_for_uri (GnomeVFSURI *desktop_uri) { const SchemeDescription *desc; int i; const char *scheme; scheme = gnome_vfs_uri_get_scheme (desktop_uri); desc = NULL; i = 0; while (i < N_ELEMENTS (schemes)) { if (strcmp (schemes[i].scheme, scheme) == 0) { desc = &schemes[i]; break; } ++i; } return desc; } static GnomeVFSURI* desktop_uri_to_file_uri (GnomeVFSURI *desktop_uri) { const SchemeDescription *desc; GnomeVFSURI *new_uri; const char *path; int i; desc = get_desc_for_uri (desktop_uri); if (desc == NULL) { gnome_vfs_uri_ref (desktop_uri); return desktop_uri; } /* Prepend the base for the desktop URI. * If the SchemeDescription contains > 1 directory, we use the directory * after the first if the given file actually exists there. */ new_uri = NULL; path = gnome_vfs_uri_get_path (desktop_uri); i = 0; while (desc->directories[i]) ++i; do { char *s; --i; s = create_file_uri_in_dir (desc->directories[i], path); new_uri = gnome_vfs_uri_new (s); g_free (s); if (i == 0 || gnome_vfs_uri_exists (new_uri)) { return new_uri; } else { gnome_vfs_uri_unref (new_uri); new_uri = NULL; } } while (i > 0); g_assert_not_reached (); return NULL; } static GnomeVFSResult open_merged_directory (GnomeVFSMethod *method, GnomeVFSMethodHandle **method_handle, GnomeVFSURI *desktop_uri, GnomeVFSFileInfoOptions options, GnomeVFSContext *context) { GnomeVFSResult result; DirHandle *dh; const SchemeDescription *desc; int i; gboolean found; const char *path; desc = get_desc_for_uri (desktop_uri); if (desc == NULL) { return GNOME_VFS_ERROR_NOT_FOUND; } dh = g_new0 (DirHandle, 1); /* Prepend the base for the desktop URI. * If the SchemeDescription contains > 1 directory, we use the directory * after the first if the given file actually exists there. */ found = FALSE; path = gnome_vfs_uri_get_path (desktop_uri); i = 0; while (desc->directories[i]) { char *s; GnomeVFSURI *file_uri; GnomeVFSMethodHandle *parent_handle = NULL; s = create_file_uri_in_dir (desc->directories[i], path); file_uri = gnome_vfs_uri_new (s); g_free (s); result = (* parent_method->open_directory) (parent_method, &parent_handle, file_uri, options, context); if (result == GNOME_VFS_OK) { found = TRUE; dh->handles = g_slist_prepend (dh->handles, parent_handle); } gnome_vfs_uri_unref (file_uri); ++i; } dh->next = dh->handles; *method_handle = (GnomeVFSMethodHandle*) dh; return found ? GNOME_VFS_OK : GNOME_VFS_ERROR_NOT_FOUND; } static char* create_file_uri_in_dir (const char *dir, const char *filename) { char *dir_uri; char *retval; dir_uri = gnome_vfs_get_uri_from_local_path (dir); retval = g_strconcat (dir_uri, "/", filename, NULL); g_free (dir_uri); return retval; }