1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
3 * vfolder-method.c - Gnome-VFS interface to manipulating vfolders.
5 * Copyright (C) 2002 Ximian, Inc.
7 * The Gnome Library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
12 * The Gnome Library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with the Gnome Library; see the file COPYING.LIB. If not,
19 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
22 * Author: Alex Graveley <alex@ximian.com>
23 * Based on original code by George Lebl <jirka@5z.com>.
32 #include <libgnomevfs/gnome-vfs-cancellable-ops.h>
33 #include <libgnomevfs/gnome-vfs-module.h>
34 #include <libgnomevfs/gnome-vfs-ops.h>
35 #include <libgnomevfs/gnome-vfs-utils.h>
36 #include <libgnomevfs/gnome-vfs-private-utils.h>
38 #include "vfolder-common.h"
39 #include "vfolder-util.h"
44 GnomeVFSHandle *handle;
50 file_handle_new (GnomeVFSHandle *file_handle,
55 if (file_handle != NULL) {
56 FileHandle *handle = g_new0 (FileHandle, 1);
58 handle->handle = file_handle;
60 handle->write = write;
62 handle->entry = entry;
71 file_handle_free (FileHandle *handle)
73 entry_unref (handle->entry);
77 #define NICE_UNLOCK_INFO(info, write) \
80 VFOLDER_INFO_WRITE_UNLOCK (info); \
82 VFOLDER_INFO_READ_UNLOCK (info); \
91 do_open (GnomeVFSMethod *method,
92 GnomeVFSMethodHandle **method_handle,
94 GnomeVFSOpenMode mode,
95 GnomeVFSContext *context)
97 GnomeVFSURI *file_uri;
98 GnomeVFSResult result = GNOME_VFS_OK;
102 GnomeVFSHandle *file_handle = NULL;
103 FileHandle *vfolder_handle;
105 gboolean want_write = mode & GNOME_VFS_OPEN_WRITE;
107 VFOLDER_URI_PARSE (uri, &vuri);
109 /* These can't be very nice FILE names */
110 if (!vuri.file || vuri.ends_in_slash)
111 return GNOME_VFS_ERROR_INVALID_URI;
113 info = vfolder_info_locate (vuri.scheme);
115 return GNOME_VFS_ERROR_INVALID_URI;
117 if (want_write && (info->read_only || vuri.is_all_scheme))
118 return GNOME_VFS_ERROR_READ_ONLY;
121 VFOLDER_INFO_WRITE_LOCK (info);
123 VFOLDER_INFO_READ_LOCK (info);
125 if (vuri.is_all_scheme) {
126 child.type = DESKTOP_FILE;
127 child.entry = vfolder_info_lookup_entry (info, vuri.file);
130 NICE_UNLOCK_INFO (info, want_write);
131 return GNOME_VFS_ERROR_NOT_FOUND;
134 parent = vfolder_info_get_parent (info, vuri.path);
136 NICE_UNLOCK_INFO (info, want_write);
137 return GNOME_VFS_ERROR_NOT_FOUND;
140 if (!folder_get_child (parent, vuri.file, &child)) {
141 NICE_UNLOCK_INFO (info, want_write);
142 return GNOME_VFS_ERROR_NOT_FOUND;
145 if (child.type == FOLDER) {
146 NICE_UNLOCK_INFO (info, want_write);
147 return GNOME_VFS_ERROR_IS_DIRECTORY;
151 if (!entry_make_user_private (child.entry, parent)) {
152 VFOLDER_INFO_WRITE_UNLOCK (info);
153 return GNOME_VFS_ERROR_READ_ONLY;
158 file_uri = entry_get_real_uri (child.entry);
159 result = gnome_vfs_open_uri_cancellable (&file_handle,
163 gnome_vfs_uri_unref (file_uri);
165 if (result == GNOME_VFS_ERROR_CANCELLED) {
166 NICE_UNLOCK_INFO (info, want_write);
170 vfolder_handle = file_handle_new (file_handle,
174 *method_handle = (GnomeVFSMethodHandle *) vfolder_handle;
176 NICE_UNLOCK_INFO (info, want_write);
182 static GnomeVFSResult
183 do_create (GnomeVFSMethod *method,
184 GnomeVFSMethodHandle **method_handle,
186 GnomeVFSOpenMode mode,
189 GnomeVFSContext *context)
191 GnomeVFSResult result = GNOME_VFS_OK;
192 GnomeVFSHandle *file_handle;
193 FileHandle *vfolder_handle;
194 GnomeVFSURI *file_uri;
200 const gchar *dirname;
201 gchar *filename, *basename;
203 VFOLDER_URI_PARSE (uri, &vuri);
205 /* These can't be very nice FILE names */
206 if (vuri.file == NULL || vuri.ends_in_slash)
207 return GNOME_VFS_ERROR_INVALID_URI;
209 if (!vfolder_check_extension (vuri.file, ".desktop") &&
210 !vfolder_check_extension (vuri.file, ".directory")) {
211 return GNOME_VFS_ERROR_INVALID_URI;
214 info = vfolder_info_locate (vuri.scheme);
216 return GNOME_VFS_ERROR_INVALID_URI;
218 if (info->read_only || vuri.is_all_scheme)
219 return GNOME_VFS_ERROR_READ_ONLY;
221 VFOLDER_INFO_WRITE_LOCK (info);
223 parent = vfolder_info_get_parent (info, vuri.path);
225 VFOLDER_INFO_WRITE_UNLOCK (info);
226 return GNOME_VFS_ERROR_NOT_FOUND;
229 if (folder_get_child (parent, vuri.file, &child)) {
230 VFOLDER_INFO_WRITE_UNLOCK (info);
232 if (child.type == FOLDER)
233 return GNOME_VFS_ERROR_IS_DIRECTORY;
234 else if (child.type == DESKTOP_FILE)
235 return GNOME_VFS_ERROR_FILE_EXISTS;
239 * make a user-local copy, so the folder will be written to the user's
240 * private .vfolder-info file
242 if (!folder_make_user_private (parent)) {
243 VFOLDER_INFO_WRITE_UNLOCK (info);
244 return GNOME_VFS_ERROR_READ_ONLY;
248 * Create file in writedir unless writedir is not set or folder is
249 * a <ParentLink>. Otherwise create in parent if exists.
251 if (info->write_dir && !parent->is_link) {
252 /* Create uniquely named file in write_dir */
253 dirname = info->write_dir;
254 basename = vfolder_timestamp_file_name (vuri.file);
255 filename = vfolder_build_uri (dirname, basename, NULL);
257 } else if (folder_get_extend_uri (parent)) {
258 /* No writedir, try modifying the parent */
259 dirname = folder_get_extend_uri (parent);
260 filename = vfolder_build_uri (dirname, vuri.file, NULL);
262 /* Nowhere to create file, fail */
263 VFOLDER_INFO_WRITE_UNLOCK (info);
264 return GNOME_VFS_ERROR_READ_ONLY;
267 /* Make sure the destination directory exists */
268 result = vfolder_make_directory_and_parents (dirname, FALSE, 0700);
269 if (result != GNOME_VFS_OK) {
270 VFOLDER_INFO_WRITE_UNLOCK (info);
275 file_uri = gnome_vfs_uri_new (filename);
276 result = gnome_vfs_create_uri_cancellable (&file_handle,
282 gnome_vfs_uri_unref (file_uri);
284 if (result != GNOME_VFS_OK) {
285 VFOLDER_INFO_WRITE_UNLOCK (info);
291 new_entry = entry_new (info,
294 TRUE /*user_private*/,
299 VFOLDER_INFO_WRITE_UNLOCK (info);
300 return GNOME_VFS_ERROR_READ_ONLY;
303 if (!parent->is_link)
304 folder_add_include (parent, entry_get_filename (new_entry));
306 folder_add_entry (parent, new_entry);
308 vfolder_handle = file_handle_new (file_handle,
311 mode & GNOME_VFS_OPEN_WRITE);
312 *method_handle = (GnomeVFSMethodHandle *) vfolder_handle;
314 VFOLDER_INFO_WRITE_UNLOCK (info);
316 vfolder_info_emit_change (info,
318 GNOME_VFS_MONITOR_EVENT_CREATED);
324 static GnomeVFSResult
325 do_close (GnomeVFSMethod *method,
326 GnomeVFSMethodHandle *method_handle,
327 GnomeVFSContext *context)
329 GnomeVFSResult result;
330 FileHandle *handle = (FileHandle *) method_handle;
332 if (method_handle == (GnomeVFSMethodHandle *) method)
335 result = gnome_vfs_close_cancellable (handle->handle, context);
338 VFOLDER_INFO_WRITE_LOCK (handle->info);
339 entry_set_dirty (handle->entry);
340 VFOLDER_INFO_WRITE_UNLOCK (handle->info);
343 file_handle_free (handle);
349 static GnomeVFSResult
350 do_read (GnomeVFSMethod *method,
351 GnomeVFSMethodHandle *method_handle,
353 GnomeVFSFileSize num_bytes,
354 GnomeVFSFileSize *bytes_read,
355 GnomeVFSContext *context)
357 GnomeVFSResult result;
358 FileHandle *handle = (FileHandle *)method_handle;
360 result = gnome_vfs_read_cancellable (handle->handle,
369 static GnomeVFSResult
370 do_write (GnomeVFSMethod *method,
371 GnomeVFSMethodHandle *method_handle,
372 gconstpointer buffer,
373 GnomeVFSFileSize num_bytes,
374 GnomeVFSFileSize *bytes_written,
375 GnomeVFSContext *context)
377 GnomeVFSResult result;
378 FileHandle *handle = (FileHandle *)method_handle;
380 result = gnome_vfs_write_cancellable (handle->handle,
389 static GnomeVFSResult
390 do_seek (GnomeVFSMethod *method,
391 GnomeVFSMethodHandle *method_handle,
392 GnomeVFSSeekPosition whence,
393 GnomeVFSFileOffset offset,
394 GnomeVFSContext *context)
396 GnomeVFSResult result;
397 FileHandle *handle = (FileHandle *)method_handle;
399 result = gnome_vfs_seek_cancellable (handle->handle,
407 static GnomeVFSResult
408 do_tell (GnomeVFSMethod *method,
409 GnomeVFSMethodHandle *method_handle,
410 GnomeVFSFileOffset *offset_return)
412 GnomeVFSResult result;
413 FileHandle *handle = (FileHandle *)method_handle;
415 result = gnome_vfs_tell (handle->handle, offset_return);
421 static GnomeVFSResult
422 do_truncate_handle (GnomeVFSMethod *method,
423 GnomeVFSMethodHandle *method_handle,
424 GnomeVFSFileSize where,
425 GnomeVFSContext *context)
427 GnomeVFSResult result;
428 FileHandle *handle = (FileHandle *)method_handle;
430 result = gnome_vfs_truncate_handle_cancellable (handle->handle,
438 static GnomeVFSResult
439 do_truncate (GnomeVFSMethod *method,
441 GnomeVFSFileSize where,
442 GnomeVFSContext *context)
444 GnomeVFSURI *file_uri;
445 GnomeVFSResult result = GNOME_VFS_OK;
451 VFOLDER_URI_PARSE (uri, &vuri);
453 /* These can't be very nice FILE names */
454 if (vuri.file == NULL || vuri.ends_in_slash)
455 return GNOME_VFS_ERROR_INVALID_URI;
457 info = vfolder_info_locate (vuri.scheme);
459 return GNOME_VFS_ERROR_INVALID_URI;
461 if (info->read_only || vuri.is_all_scheme)
462 return GNOME_VFS_ERROR_READ_ONLY;
464 VFOLDER_INFO_WRITE_LOCK (info);
466 parent = vfolder_info_get_parent (info, vuri.path);
468 VFOLDER_INFO_WRITE_UNLOCK (info);
469 return GNOME_VFS_ERROR_NOT_FOUND;
472 if (!folder_get_child (parent, vuri.file, &child)) {
473 VFOLDER_INFO_WRITE_UNLOCK (info);
474 return GNOME_VFS_ERROR_NOT_FOUND;
477 if (child.type == FOLDER) {
478 VFOLDER_INFO_WRITE_UNLOCK (info);
479 return GNOME_VFS_ERROR_IS_DIRECTORY;
482 if (!entry_make_user_private (child.entry, parent)) {
483 VFOLDER_INFO_WRITE_UNLOCK (info);
484 return GNOME_VFS_ERROR_READ_ONLY;
487 file_uri = entry_get_real_uri (child.entry);
489 VFOLDER_INFO_WRITE_UNLOCK (info);
491 result = gnome_vfs_truncate_uri_cancellable (file_uri, where, context);
492 gnome_vfs_uri_unref (file_uri);
502 GnomeVFSFileInfoOptions options;
504 /* List of Entries */
510 dir_handle_new (VFolderInfo *info,
512 GnomeVFSFileInfoOptions options)
514 DirHandle *ret = g_new0 (DirHandle, 1);
517 ret->options = options;
518 ret->folder = folder;
521 ret->list = ret->current = folder_list_children (folder);
527 dir_handle_new_all (VFolderInfo *info,
528 GnomeVFSFileInfoOptions options)
530 DirHandle *ret = g_new0 (DirHandle, 1);
533 iter = vfolder_info_list_all_entries (info);
534 for (; iter; iter = iter->next) {
535 Entry *entry = iter->data;
539 g_strdup (entry_get_displayname (entry)));
541 ret->list = g_slist_reverse (ret->list);
544 ret->options = options;
545 ret->current = ret->list;
551 dir_handle_free (DirHandle *handle)
554 folder_unref (handle->folder);
556 g_slist_foreach (handle->list, (GFunc) g_free, NULL);
557 g_slist_free (handle->list);
562 static GnomeVFSResult
563 do_open_directory (GnomeVFSMethod *method,
564 GnomeVFSMethodHandle **method_handle,
566 GnomeVFSFileInfoOptions options,
567 GnomeVFSContext *context)
570 DirHandle *dh = NULL;
574 VFOLDER_URI_PARSE (uri, &vuri);
576 /* Read lock is kept until close_directory */
577 info = vfolder_info_locate (vuri.scheme);
579 return GNOME_VFS_ERROR_INVALID_URI;
581 VFOLDER_INFO_READ_LOCK (info);
583 /* In the all- scheme just list all filenames */
584 if (vuri.is_all_scheme) {
585 /* Don't allow dirnames for all-applications:/ */
586 if (vuri.path && strrchr (vuri.path, '/') != vuri.path) {
587 VFOLDER_INFO_READ_UNLOCK (info);
588 return GNOME_VFS_ERROR_NOT_FOUND;
591 dh = dir_handle_new_all (info, options);
593 folder = vfolder_info_get_folder (info, vuri.path);
595 VFOLDER_INFO_READ_UNLOCK (info);
596 return GNOME_VFS_ERROR_NOT_FOUND;
599 dh = dir_handle_new (info, folder, options);
602 VFOLDER_INFO_READ_UNLOCK (info);
604 *method_handle = (GnomeVFSMethodHandle*) dh;
610 static GnomeVFSResult
611 do_close_directory (GnomeVFSMethod *method,
612 GnomeVFSMethodHandle *method_handle,
613 GnomeVFSContext *context)
617 dh = (DirHandle*) method_handle;
618 dir_handle_free (dh);
625 fill_file_info_for_directory (GnomeVFSFileInfo *file_info,
626 GnomeVFSFileInfoOptions options,
630 const gchar *link_ref)
632 file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
634 file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
635 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
637 GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
639 file_info->mime_type = g_strdup ("x-directory/vfolder-desktop");
640 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
642 file_info->ctime = mtime;
643 file_info->mtime = mtime;
644 file_info->valid_fields |= (GNOME_VFS_FILE_INFO_FIELDS_CTIME |
645 GNOME_VFS_FILE_INFO_FIELDS_MTIME);
647 file_info->name = g_strdup (name);
650 file_info->permissions = (GNOME_VFS_PERM_USER_READ |
651 GNOME_VFS_PERM_GROUP_READ |
652 GNOME_VFS_PERM_OTHER_READ);
653 file_info->valid_fields |=
654 GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS;
659 * FIXME: Idealy we'd be able to present links as actual symbolic links,
660 * but panel doesn't like symlinks in the menus, and nautilus seems to
661 * ignore it altogether.
664 if (options & GNOME_VFS_FILE_INFO_FOLLOW_LINKS)
665 file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
667 file_info->type = GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK;
669 GNOME_VFS_FILE_INFO_SET_SYMLINK (file_info, TRUE);
671 file_info->symlink_name = g_strdup (link_ref);
672 file_info->valid_fields |=
673 GNOME_VFS_FILE_INFO_FIELDS_SYMLINK_NAME;
678 #define UNSUPPORTED_INFO_FIELDS (GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS | \
679 GNOME_VFS_FILE_INFO_FIELDS_DEVICE | \
680 GNOME_VFS_FILE_INFO_FIELDS_INODE | \
681 GNOME_VFS_FILE_INFO_FIELDS_LINK_COUNT | \
682 GNOME_VFS_FILE_INFO_FIELDS_ATIME)
684 static GnomeVFSResult
685 get_file_info_internal (VFolderInfo *info,
687 GnomeVFSFileInfoOptions options,
688 GnomeVFSFileInfo *file_info,
689 GnomeVFSContext *context)
691 if (child->type == DESKTOP_FILE) {
692 GnomeVFSResult result;
693 GnomeVFSURI *file_uri;
696 /* we always get mime-type by forcing it below */
697 if (options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
698 options &= ~GNOME_VFS_FILE_INFO_GET_MIME_TYPE;
700 file_uri = entry_get_real_uri (child->entry);
701 displayname = g_strdup (entry_get_displayname (child->entry));
703 result = gnome_vfs_get_file_info_uri_cancellable (file_uri,
707 gnome_vfs_uri_unref (file_uri);
709 g_free (file_info->name);
710 file_info->name = displayname;
712 g_free (file_info->mime_type);
713 file_info->mime_type =
714 g_strdup ("application/x-gnome-app-info");
715 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
717 /* Now we wipe those fields we don't support */
718 file_info->valid_fields &= ~(UNSUPPORTED_INFO_FIELDS);
722 else if (child->type == FOLDER) {
724 fill_file_info_for_directory (
727 folder_get_name (child->folder),
728 info->modification_time,
729 child->folder->read_only || info->read_only,
730 folder_get_extend_uri (child->folder));
732 /* all-applications root dir */
733 fill_file_info_for_directory (file_info,
736 info->modification_time,
742 return GNOME_VFS_ERROR_GENERIC;
746 static GnomeVFSResult
747 do_read_directory (GnomeVFSMethod *method,
748 GnomeVFSMethodHandle *method_handle,
749 GnomeVFSFileInfo *file_info,
750 GnomeVFSContext *context)
752 GnomeVFSResult result;
757 dh = (DirHandle*) method_handle;
759 VFOLDER_INFO_READ_LOCK (dh->info);
764 VFOLDER_INFO_READ_UNLOCK (dh->info);
765 return GNOME_VFS_ERROR_EOF;
768 entry_name = dh->current->data;
769 dh->current = dh->current->next;
772 if (!folder_get_child (dh->folder, entry_name, &child))
773 goto READ_NEXT_ENTRY;
776 child.type = DESKTOP_FILE;
777 child.entry = vfolder_info_lookup_entry (dh->info, entry_name);
780 goto READ_NEXT_ENTRY;
783 if (child.type == FOLDER && folder_is_hidden (child.folder))
784 goto READ_NEXT_ENTRY;
786 result = get_file_info_internal (dh->info,
791 if (result != GNOME_VFS_OK)
792 goto READ_NEXT_ENTRY;
794 VFOLDER_INFO_READ_UNLOCK (dh->info);
800 static GnomeVFSResult
801 do_get_file_info (GnomeVFSMethod *method,
803 GnomeVFSFileInfo *file_info,
804 GnomeVFSFileInfoOptions options,
805 GnomeVFSContext *context)
807 GnomeVFSResult result = GNOME_VFS_OK;
813 VFOLDER_URI_PARSE (uri, &vuri);
815 info = vfolder_info_locate (vuri.scheme);
817 return GNOME_VFS_ERROR_INVALID_URI;
819 VFOLDER_INFO_READ_LOCK (info);
821 if (vuri.is_all_scheme) {
824 child.type = DESKTOP_FILE;
825 child.entry = vfolder_info_lookup_entry (info,
828 VFOLDER_INFO_READ_UNLOCK (info);
829 return GNOME_VFS_ERROR_NOT_FOUND;
832 /* all-scheme root folder */
837 parent = vfolder_info_get_parent (info, vuri.path);
839 VFOLDER_INFO_READ_UNLOCK (info);
840 return GNOME_VFS_ERROR_NOT_FOUND;
843 if (!folder_get_child (parent, vuri.file, &child)) {
844 VFOLDER_INFO_READ_UNLOCK (info);
845 return GNOME_VFS_ERROR_NOT_FOUND;
849 result = get_file_info_internal (info,
855 VFOLDER_INFO_READ_UNLOCK (info);
861 static GnomeVFSResult
862 do_get_file_info_from_handle (GnomeVFSMethod *method,
863 GnomeVFSMethodHandle *method_handle,
864 GnomeVFSFileInfo *file_info,
865 GnomeVFSFileInfoOptions options,
866 GnomeVFSContext *context)
868 GnomeVFSResult result;
869 FileHandle *handle = (FileHandle *) method_handle;
872 VFOLDER_INFO_READ_LOCK (handle->info);
874 child.type = DESKTOP_FILE;
875 child.entry = handle->entry;
877 result = get_file_info_internal (handle->info,
883 VFOLDER_INFO_READ_UNLOCK (handle->info);
890 do_is_local (GnomeVFSMethod *method,
891 const GnomeVFSURI *uri)
897 static GnomeVFSResult
898 do_make_directory (GnomeVFSMethod *method,
901 GnomeVFSContext *context)
904 Folder *parent, *folder;
907 VFOLDER_URI_PARSE (uri, &vuri);
909 /* Root folder always exists */
910 if (vuri.file == NULL)
911 return GNOME_VFS_ERROR_FILE_EXISTS;
913 info = vfolder_info_locate (vuri.scheme);
915 return GNOME_VFS_ERROR_INVALID_URI;
917 if (info->read_only || vuri.is_all_scheme)
918 return GNOME_VFS_ERROR_READ_ONLY;
920 VFOLDER_INFO_WRITE_LOCK (info);
922 parent = vfolder_info_get_parent (info, vuri.path);
924 VFOLDER_INFO_WRITE_UNLOCK (info);
925 return GNOME_VFS_ERROR_NOT_FOUND;
928 if (folder_get_entry (parent, vuri.file)) {
929 VFOLDER_INFO_WRITE_UNLOCK (info);
930 return GNOME_VFS_ERROR_FILE_EXISTS;
933 folder = folder_get_subfolder (parent, vuri.file);
935 if (!folder_is_hidden (folder)) {
936 VFOLDER_INFO_WRITE_UNLOCK (info);
937 return GNOME_VFS_ERROR_FILE_EXISTS;
940 if (!folder_make_user_private (folder)) {
941 VFOLDER_INFO_WRITE_UNLOCK (info);
942 return GNOME_VFS_ERROR_READ_ONLY;
945 if (folder->dont_show_if_empty) {
946 folder->dont_show_if_empty = FALSE;
947 vfolder_info_set_dirty (info);
952 /* Create in the parent as well as in our .vfolder-info */
953 if (parent->is_link) {
954 const gchar *extend_uri;
955 GnomeVFSURI *real_uri, *new_uri;
956 GnomeVFSResult result;
958 extend_uri = folder_get_extend_uri (parent);
959 real_uri = gnome_vfs_uri_new (extend_uri);
960 new_uri = gnome_vfs_uri_append_file_name (real_uri,
962 gnome_vfs_uri_unref (real_uri);
965 gnome_vfs_make_directory_for_uri_cancellable (
969 gnome_vfs_uri_unref (new_uri);
971 if (result != GNOME_VFS_OK) {
972 VFOLDER_INFO_WRITE_UNLOCK (info);
978 * Don't write to .vfolder-info file if our parent is a link
979 * directory, since we just created a real child directory.
981 folder = folder_new (info, vuri.file, parent->is_link == FALSE);
984 folder_remove_exclude (parent, folder_get_name (folder));
985 folder_add_subfolder (parent, folder);
986 folder_unref (folder);
988 VFOLDER_INFO_WRITE_UNLOCK (info);
990 vfolder_info_emit_change (info,
992 GNOME_VFS_MONITOR_EVENT_CREATED);
998 static GnomeVFSResult
999 do_remove_directory_unlocked (VFolderInfo *info,
1001 GnomeVFSContext *context)
1003 Folder *parent, *folder;
1004 GnomeVFSResult result;
1006 parent = vfolder_info_get_parent (info, vuri->path);
1008 return GNOME_VFS_ERROR_NOT_FOUND;
1010 folder = folder_get_subfolder (parent, vuri->file);
1012 return GNOME_VFS_ERROR_NOT_FOUND;
1014 if (folder_list_subfolders (folder) || folder_list_entries (folder))
1015 return GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY;
1017 if (!folder_make_user_private (parent))
1018 return GNOME_VFS_ERROR_READ_ONLY;
1020 if (folder->is_link) {
1022 GnomeVFSURI *new_uri;
1024 uristr = vfolder_build_uri (folder_get_extend_uri (folder),
1027 new_uri = gnome_vfs_uri_new (uristr);
1030 /* Remove from the parent as well as in our .vfolder-info */
1032 gnome_vfs_remove_directory_from_uri_cancellable (
1035 gnome_vfs_uri_unref (new_uri);
1037 if (result != GNOME_VFS_OK)
1041 folder_add_exclude (parent, folder_get_name (folder));
1042 folder_remove_subfolder (parent, folder);
1044 return GNOME_VFS_OK;
1047 static GnomeVFSResult
1048 do_remove_directory (GnomeVFSMethod *method,
1050 GnomeVFSContext *context)
1054 GnomeVFSResult result;
1056 VFOLDER_URI_PARSE (uri, &vuri);
1058 info = vfolder_info_locate (vuri.scheme);
1060 return GNOME_VFS_ERROR_INVALID_URI;
1062 if (info->read_only || vuri.is_all_scheme)
1063 return GNOME_VFS_ERROR_READ_ONLY;
1065 VFOLDER_INFO_WRITE_LOCK (info);
1066 result = do_remove_directory_unlocked (info, &vuri, context);
1067 VFOLDER_INFO_WRITE_UNLOCK (info);
1069 if (result == GNOME_VFS_OK)
1070 vfolder_info_emit_change (info,
1072 GNOME_VFS_MONITOR_EVENT_DELETED);
1078 static GnomeVFSResult
1079 do_unlink_unlocked (VFolderInfo *info,
1081 GnomeVFSContext *context)
1086 parent = vfolder_info_get_parent (info, vuri->path);
1088 return GNOME_VFS_ERROR_NOT_FOUND;
1090 entry = folder_get_entry (parent, vuri->file);
1092 if (folder_get_subfolder (parent, vuri->file))
1093 return GNOME_VFS_ERROR_IS_DIRECTORY;
1095 return GNOME_VFS_ERROR_NOT_FOUND;
1098 if (parent->is_link || entry_is_user_private (entry)) {
1100 GnomeVFSResult result;
1102 /* Delete our local copy, or the linked source */
1103 uri = entry_get_real_uri (entry);
1104 result = gnome_vfs_unlink_from_uri_cancellable (uri, context);
1105 gnome_vfs_uri_unref (uri);
1108 * We only care about the result if its a linked directory.
1109 * Otherwise we can just modify the .vfolder-info.
1111 if (parent->is_link && result != GNOME_VFS_OK)
1115 if (!parent->is_link) {
1116 if (!folder_make_user_private (parent))
1117 return GNOME_VFS_ERROR_READ_ONLY;
1119 /* Clear out the <Include> */
1120 if (entry_is_user_private (entry))
1121 folder_remove_include (parent,
1122 entry_get_filename (entry));
1124 folder_add_exclude (parent, entry_get_displayname (entry));
1127 folder_remove_entry (parent, entry);
1129 return GNOME_VFS_OK;
1132 static GnomeVFSResult
1133 do_unlink (GnomeVFSMethod *method,
1135 GnomeVFSContext *context)
1139 GnomeVFSResult result;
1141 VFOLDER_URI_PARSE (uri, &vuri);
1144 return GNOME_VFS_ERROR_INVALID_URI;
1145 else if (vuri.is_all_scheme)
1146 return GNOME_VFS_ERROR_READ_ONLY;
1148 info = vfolder_info_locate (vuri.scheme);
1150 return GNOME_VFS_ERROR_INVALID_URI;
1152 if (info->read_only)
1153 return GNOME_VFS_ERROR_READ_ONLY;
1155 VFOLDER_INFO_WRITE_LOCK (info);
1156 result = do_unlink_unlocked (info, &vuri, context);
1157 VFOLDER_INFO_WRITE_UNLOCK (info);
1159 if (result == GNOME_VFS_OK)
1160 vfolder_info_emit_change (info,
1162 GNOME_VFS_MONITOR_EVENT_DELETED);
1169 set_desktop_file_key (GString *fullbuf, gchar *key, gchar *value)
1171 gchar *key_idx, *val_end;
1173 /* Remove the name if it already exists */
1174 key_idx = strstr (fullbuf->str, key);
1175 if (key_idx && (key_idx == fullbuf->str ||
1176 key_idx [-1] == '\n' ||
1177 key_idx [-1] == '\r')) {
1178 /* Look for the end of the value */
1179 val_end = strchr (key_idx, '\n');
1181 val_end = strchr (key_idx, '\r');
1183 val_end = &fullbuf->str [fullbuf->len - 1];
1185 /* Erase the old name */
1186 g_string_erase (fullbuf,
1187 key_idx - fullbuf->str,
1191 /* Mkae sure we don't bump into the last attribute */
1192 if (fullbuf->len > 0 && (fullbuf->str [fullbuf->len - 1] != '\n' &&
1193 fullbuf->str [fullbuf->len - 1] != '\r'))
1194 g_string_append_c (fullbuf, '\n');
1196 g_string_append_printf (fullbuf, "%s=%s\n", key, value);
1200 set_desktop_file_locale_key (GString *fullbuf, gchar *key, gchar *value)
1203 const gchar *locale;
1206 /* Get the list of applicable locales */
1207 locale_list = gnome_vfs_i18n_get_language_list ("LC_MESSAGES");
1209 /* Get the localized keyname from the first locale */
1210 locale = locale_list ? locale_list->data : NULL;
1211 if (!locale || !strcmp (locale, "C"))
1212 locale_key = g_strdup (key);
1214 locale_key = g_strdup_printf ("%s[%s]", key, locale);
1216 set_desktop_file_key (fullbuf, locale_key, value);
1218 g_list_free (locale_list);
1219 g_free (locale_key);
1223 set_dot_directory_locale_name (Folder *folder, gchar *val)
1226 GnomeVFSHandle *handle;
1227 GnomeVFSFileSize readlen, writelen, offset = 0;
1232 dot_file = folder_get_entry (folder, ".directory");
1235 if (!entry_make_user_private (dot_file, folder))
1238 mode = (GNOME_VFS_OPEN_READ |
1239 GNOME_VFS_OPEN_WRITE |
1240 GNOME_VFS_OPEN_RANDOM);
1242 perm = (GNOME_VFS_PERM_USER_READ |
1243 GNOME_VFS_PERM_USER_WRITE |
1244 GNOME_VFS_PERM_GROUP_READ |
1245 GNOME_VFS_PERM_OTHER_READ);
1247 if (gnome_vfs_open (&handle,
1248 entry_get_filename (dot_file),
1249 mode) != GNOME_VFS_OK &&
1250 gnome_vfs_create (&handle,
1251 entry_get_filename (dot_file),
1254 perm) != GNOME_VFS_OK)
1257 /* read in the file contents to fullbuf */
1258 fullbuf = g_string_new (NULL);
1259 while (gnome_vfs_read (handle,
1262 &readlen) == GNOME_VFS_OK) {
1263 g_string_append_len (fullbuf, buf, readlen);
1266 /* set the key, replacing if necessary */
1267 set_desktop_file_locale_key (fullbuf, "Name", val);
1270 gnome_vfs_truncate_handle (handle, 0);
1271 gnome_vfs_seek (handle, GNOME_VFS_SEEK_START, 0);
1273 /* write the changed contents */
1274 while (fullbuf->len - offset > 0 &&
1275 gnome_vfs_write (handle,
1276 &fullbuf->str [offset],
1277 fullbuf->len - offset,
1278 &writelen) == GNOME_VFS_OK) {
1282 gnome_vfs_close (handle);
1283 g_string_free (fullbuf, TRUE);
1286 static GnomeVFSResult
1287 do_move (GnomeVFSMethod *method,
1288 GnomeVFSURI *old_uri,
1289 GnomeVFSURI *new_uri,
1290 gboolean force_replace,
1291 GnomeVFSContext *context)
1293 GnomeVFSResult result = GNOME_VFS_OK;
1295 Folder *old_parent, *new_parent;
1296 VFolderURI old_vuri, new_vuri;
1297 FolderChild old_child, existing_child;
1299 VFOLDER_URI_PARSE (old_uri, &old_vuri);
1300 VFOLDER_URI_PARSE (new_uri, &new_vuri);
1303 return GNOME_VFS_ERROR_INVALID_URI;
1305 if (old_vuri.is_all_scheme || new_vuri.is_all_scheme)
1306 return GNOME_VFS_ERROR_READ_ONLY;
1308 if (strcmp (old_vuri.scheme, new_vuri.scheme) != 0)
1309 return GNOME_VFS_ERROR_NOT_SAME_FILE_SYSTEM;
1311 info = vfolder_info_locate (old_vuri.scheme);
1313 return GNOME_VFS_ERROR_INVALID_URI;
1315 if (info->read_only)
1316 return GNOME_VFS_ERROR_READ_ONLY;
1318 VFOLDER_INFO_WRITE_LOCK (info);
1320 old_parent = vfolder_info_get_parent (info, old_vuri.path);
1322 !folder_get_child (old_parent, old_vuri.file, &old_child)) {
1323 VFOLDER_INFO_WRITE_UNLOCK (info);
1324 return GNOME_VFS_ERROR_NOT_FOUND;
1327 if (!folder_make_user_private (old_parent)) {
1328 VFOLDER_INFO_WRITE_UNLOCK (info);
1329 return GNOME_VFS_ERROR_READ_ONLY;
1332 new_parent = vfolder_info_get_parent (info, new_vuri.path);
1334 VFOLDER_INFO_WRITE_UNLOCK (info);
1335 return GNOME_VFS_ERROR_NOT_A_DIRECTORY;
1338 if (!folder_make_user_private (new_parent)) {
1339 VFOLDER_INFO_WRITE_UNLOCK (info);
1340 return GNOME_VFS_ERROR_READ_ONLY;
1343 if (folder_get_child (new_parent, new_vuri.file, &existing_child)) {
1344 if (!force_replace) {
1345 VFOLDER_INFO_WRITE_UNLOCK (info);
1346 return GNOME_VFS_ERROR_FILE_EXISTS;
1350 if (old_child.type == DESKTOP_FILE) {
1351 if (!vfolder_check_extension (new_vuri.file, ".desktop") &&
1352 !vfolder_check_extension (new_vuri.file, ".directory")) {
1353 VFOLDER_INFO_WRITE_UNLOCK (info);
1354 return GNOME_VFS_ERROR_INVALID_URI;
1357 if (existing_child.type == FOLDER) {
1358 VFOLDER_INFO_WRITE_UNLOCK (info);
1359 return GNOME_VFS_ERROR_IS_DIRECTORY;
1362 /* ref in case old_parent is new_parent */
1363 entry_ref (old_child.entry);
1365 if (existing_child.type == DESKTOP_FILE) {
1366 result = do_unlink_unlocked (info,
1369 if (result != GNOME_VFS_OK &&
1370 result != GNOME_VFS_ERROR_NOT_FOUND) {
1371 entry_unref (old_child.entry);
1372 VFOLDER_INFO_WRITE_UNLOCK (info);
1377 /* remove from old folder */
1378 folder_remove_entry (old_parent, old_child.entry);
1379 folder_add_exclude (old_parent,
1380 entry_get_filename (old_child.entry));
1382 /* basenames different, have to make a local copy */
1383 if (strcmp (entry_get_displayname (old_child.entry),
1384 new_vuri.file) != 0) {
1385 entry_set_displayname (old_child.entry, new_vuri.file);
1386 entry_make_user_private (old_child.entry, new_parent);
1389 /* add to new folder */
1390 folder_add_entry (new_parent, old_child.entry);
1391 folder_add_include (new_parent,
1392 entry_get_filename (old_child.entry));
1394 entry_unref (old_child.entry);
1396 vfolder_info_emit_change (info,
1398 GNOME_VFS_MONITOR_EVENT_DELETED);
1400 vfolder_info_emit_change (info,
1402 GNOME_VFS_MONITOR_EVENT_CREATED);
1404 else if (old_child.type == FOLDER) {
1407 if (existing_child.type && existing_child.type != FOLDER) {
1408 VFOLDER_INFO_WRITE_UNLOCK (info);
1409 return GNOME_VFS_ERROR_NOT_A_DIRECTORY;
1412 for (iter = new_parent->parent; iter; iter = iter->parent) {
1413 if (iter == old_child.folder) {
1414 VFOLDER_INFO_WRITE_UNLOCK (info);
1415 return GNOME_VFS_ERROR_LOOP;
1419 /* ref in case old_parent is new_parent */
1420 folder_ref (old_child.folder);
1422 if (old_parent != new_parent) {
1423 result = do_remove_directory_unlocked (info,
1426 if (result != GNOME_VFS_OK &&
1427 result != GNOME_VFS_ERROR_NOT_FOUND) {
1428 folder_unref (old_child.folder);
1429 VFOLDER_INFO_WRITE_UNLOCK (info);
1434 folder_remove_subfolder (old_parent, old_child.folder);
1435 folder_add_exclude (old_parent, old_vuri.file);
1437 folder_make_user_private (old_child.folder);
1438 folder_set_name (old_child.folder, new_vuri.file);
1439 folder_add_subfolder (new_parent, old_child.folder);
1441 /* do the .directory name change */
1442 set_dot_directory_locale_name (old_child.folder, new_vuri.file);
1444 vfolder_info_emit_change (info,
1446 GNOME_VFS_MONITOR_EVENT_DELETED);
1448 vfolder_info_emit_change (info,
1450 GNOME_VFS_MONITOR_EVENT_CREATED);
1452 folder_unref (old_child.folder);
1455 VFOLDER_INFO_WRITE_UNLOCK (info);
1457 return GNOME_VFS_OK;
1461 static GnomeVFSResult
1462 do_check_same_fs (GnomeVFSMethod *method,
1463 GnomeVFSURI *source_uri,
1464 GnomeVFSURI *target_uri,
1465 gboolean *same_fs_return,
1466 GnomeVFSContext *context)
1468 VFolderURI source_vuri, target_vuri;
1470 *same_fs_return = FALSE;
1472 VFOLDER_URI_PARSE (source_uri, &source_vuri);
1473 VFOLDER_URI_PARSE (target_uri, &target_vuri);
1475 if (strcmp (source_vuri.scheme, target_vuri.scheme) != 0 ||
1476 source_vuri.is_all_scheme != target_vuri.is_all_scheme)
1477 *same_fs_return = FALSE;
1479 *same_fs_return = TRUE;
1481 return GNOME_VFS_OK;
1485 static GnomeVFSResult
1486 do_set_file_info (GnomeVFSMethod *method,
1488 const GnomeVFSFileInfo *info,
1489 GnomeVFSSetFileInfoMask mask,
1490 GnomeVFSContext *context)
1494 VFOLDER_URI_PARSE (uri, &vuri);
1497 return GNOME_VFS_ERROR_INVALID_URI;
1499 if (mask & GNOME_VFS_SET_FILE_INFO_NAME) {
1500 GnomeVFSResult result = GNOME_VFS_OK;
1501 GnomeVFSURI *parent_uri, *new_uri;
1503 parent_uri = gnome_vfs_uri_get_parent (uri);
1504 new_uri = gnome_vfs_uri_append_file_name (parent_uri,
1506 gnome_vfs_uri_unref (parent_uri);
1509 return GNOME_VFS_ERROR_INVALID_URI;
1511 result = do_move (method,
1514 FALSE /* force_replace */,
1517 gnome_vfs_uri_unref (new_uri);
1521 * We don't support setting any of this other permission,
1522 * times and all that voodoo
1524 return GNOME_VFS_ERROR_NOT_SUPPORTED;
1529 static GnomeVFSResult
1530 do_create_symbolic_link (GnomeVFSMethod *method,
1532 const char *target_reference,
1533 GnomeVFSContext *context)
1539 GnomeVFSResult result;
1541 VFOLDER_URI_PARSE (uri, &vuri);
1543 return GNOME_VFS_ERROR_INVALID_URI;
1545 info = vfolder_info_locate (vuri.scheme);
1547 return GNOME_VFS_ERROR_INVALID_URI;
1549 if (info->read_only)
1550 return GNOME_VFS_ERROR_READ_ONLY;
1552 VFOLDER_INFO_WRITE_LOCK (info);
1554 parent = vfolder_info_get_parent (info, vuri.path);
1556 VFOLDER_INFO_WRITE_UNLOCK (info);
1557 return GNOME_VFS_ERROR_NOT_FOUND;
1560 if (folder_get_child (parent, vuri.file, &child)) {
1561 VFOLDER_INFO_WRITE_UNLOCK (info);
1562 return GNOME_VFS_ERROR_FILE_EXISTS;
1565 if (parent->is_link) {
1567 GnomeVFSURI *new_uri;
1569 VFOLDER_INFO_WRITE_UNLOCK (info);
1571 new_uristr = vfolder_build_uri (folder_get_extend_uri (parent),
1574 new_uri = gnome_vfs_uri_new (new_uristr);
1577 gnome_vfs_create_symbolic_link_cancellable (
1582 gnome_vfs_uri_unref (new_uri);
1586 GnomeVFSFileInfo *file_info;
1587 GnomeVFSURI *link_uri;
1590 if (!folder_make_user_private (parent)) {
1591 VFOLDER_INFO_WRITE_UNLOCK (info);
1592 return GNOME_VFS_ERROR_READ_ONLY;
1596 * FIXME: need to unlock here to get the file info so we can
1597 * check if the target file is a directory, avoiding a deadlock
1598 * when target is on the same method (always?).
1600 VFOLDER_INFO_WRITE_UNLOCK (info);
1602 link_uri = gnome_vfs_uri_new (target_reference);
1603 file_info = gnome_vfs_file_info_new ();
1605 gnome_vfs_get_file_info_uri_cancellable (
1608 GNOME_VFS_FILE_INFO_FOLLOW_LINKS,
1610 gnome_vfs_uri_unref (link_uri);
1612 if (result != GNOME_VFS_OK)
1613 return GNOME_VFS_ERROR_NOT_FOUND;
1615 /* We only support links to directories */
1616 if (file_info->type != GNOME_VFS_FILE_TYPE_DIRECTORY)
1617 return GNOME_VFS_ERROR_NOT_A_DIRECTORY;
1619 VFOLDER_INFO_WRITE_LOCK (info);
1622 * Reget parent, avoiding a race if it was removed while we were
1625 parent = vfolder_info_get_parent (info, vuri.path);
1627 VFOLDER_INFO_WRITE_UNLOCK (info);
1628 return GNOME_VFS_ERROR_NOT_FOUND;
1631 linkdir = folder_new (info, vuri.file, TRUE);
1632 folder_set_extend_uri (linkdir, target_reference);
1633 linkdir->is_link = TRUE;
1635 folder_add_subfolder (parent, linkdir);
1636 folder_unref (linkdir);
1638 VFOLDER_INFO_WRITE_UNLOCK (info);
1640 vfolder_info_emit_change (info,
1642 GNOME_VFS_MONITOR_EVENT_CREATED);
1644 return GNOME_VFS_OK;
1649 static GnomeVFSResult
1650 do_monitor_add (GnomeVFSMethod *method,
1651 GnomeVFSMethodHandle **method_handle_return,
1653 GnomeVFSMonitorType monitor_type)
1657 info = vfolder_info_locate (gnome_vfs_uri_get_scheme (uri));
1659 return GNOME_VFS_ERROR_INVALID_URI;
1661 VFOLDER_INFO_READ_LOCK (info);
1662 vfolder_info_add_monitor (info,
1665 method_handle_return);
1666 VFOLDER_INFO_READ_UNLOCK (info);
1668 return GNOME_VFS_OK;
1672 static GnomeVFSResult
1673 do_monitor_cancel (GnomeVFSMethod *method,
1674 GnomeVFSMethodHandle *method_handle)
1676 MonitorHandle *monitor = (MonitorHandle *) method_handle;
1679 if (method_handle == NULL)
1680 return GNOME_VFS_OK;
1682 info = monitor->info;
1684 VFOLDER_INFO_READ_LOCK (info);
1685 vfolder_info_cancel_monitor (method_handle);
1686 VFOLDER_INFO_READ_UNLOCK (info);
1688 return GNOME_VFS_OK;
1693 * GnomeVFS Registration
1695 static GnomeVFSMethod method = {
1696 sizeof (GnomeVFSMethod),
1709 do_get_file_info_from_handle,
1712 do_remove_directory,
1718 NULL /* find_directory */,
1719 do_create_symbolic_link,
1725 vfs_module_init (const char *method_name, const char *args)
1731 vfs_module_shutdown (GnomeVFSMethod *method)
1733 vfolder_info_destroy_all ();