ftp://ftp.redhat.com/pub/redhat/linux/rawhide/SRPMS/SRPMS/gnome-vfs2-2.3.8-1.src.rpm
[gnome-vfs-httpcaptive.git] / modules / vfolder / vfolder-method.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* 
3  * vfolder-method.c - Gnome-VFS interface to manipulating vfolders.
4  *
5  * Copyright (C) 2002 Ximian, Inc.
6  *
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.
11  *
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.
16  *
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.
21  *
22  * Author: Alex Graveley <alex@ximian.com>
23  *         Based on original code by George Lebl <jirka@5z.com>.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include <config.h>
28 #endif
29
30 #include <string.h>
31
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>
37
38 #include "vfolder-common.h"
39 #include "vfolder-util.h"
40
41 \f
42 typedef struct {
43         VFolderInfo    *info;
44         GnomeVFSHandle *handle;
45         Entry          *entry;
46         gboolean        write;
47 } FileHandle;
48
49 static FileHandle *
50 file_handle_new (GnomeVFSHandle *file_handle,
51                  VFolderInfo *info,
52                  Entry *entry,
53                  gboolean write)
54 {
55         if (file_handle != NULL) {
56                 FileHandle *handle = g_new0 (FileHandle, 1);
57
58                 handle->handle = file_handle;
59                 handle->info = info;
60                 handle->write = write;
61
62                 handle->entry = entry;
63                 entry_ref (entry);
64
65                 return handle;
66         } else
67                 return NULL;
68 }
69
70 static void
71 file_handle_free (FileHandle *handle)
72 {
73         entry_unref (handle->entry);
74         g_free (handle);
75 }
76
77 #define NICE_UNLOCK_INFO(info, write)                     \
78         do {                                              \
79                 if (write) {                              \
80                         VFOLDER_INFO_WRITE_UNLOCK (info); \
81                 } else {                                  \
82                         VFOLDER_INFO_READ_UNLOCK (info);  \
83                 }                                         \
84         } while (0)
85
86 \f
87 /*
88  * GnomeVFS Callbacks
89  */
90 static GnomeVFSResult
91 do_open (GnomeVFSMethod *method,
92          GnomeVFSMethodHandle **method_handle,
93          GnomeVFSURI *uri,
94          GnomeVFSOpenMode mode,
95          GnomeVFSContext *context)
96 {
97         GnomeVFSURI *file_uri;
98         GnomeVFSResult result = GNOME_VFS_OK;
99         VFolderInfo *info;
100         Folder *parent;
101         FolderChild child;
102         GnomeVFSHandle *file_handle = NULL;
103         FileHandle *vfolder_handle;
104         VFolderURI vuri;
105         gboolean want_write = mode & GNOME_VFS_OPEN_WRITE;
106
107         VFOLDER_URI_PARSE (uri, &vuri);
108
109         /* These can't be very nice FILE names */
110         if (!vuri.file || vuri.ends_in_slash)
111                 return GNOME_VFS_ERROR_INVALID_URI;
112
113         info = vfolder_info_locate (vuri.scheme);
114         if (!info)
115                 return GNOME_VFS_ERROR_INVALID_URI;
116
117         if (want_write && (info->read_only || vuri.is_all_scheme))
118                 return GNOME_VFS_ERROR_READ_ONLY;
119
120         if (want_write) 
121                 VFOLDER_INFO_WRITE_LOCK (info);
122         else 
123                 VFOLDER_INFO_READ_LOCK (info);
124
125         if (vuri.is_all_scheme) {
126                 child.type = DESKTOP_FILE;
127                 child.entry = vfolder_info_lookup_entry (info, vuri.file);
128
129                 if (!child.entry) {
130                         NICE_UNLOCK_INFO (info, want_write);
131                         return GNOME_VFS_ERROR_NOT_FOUND;
132                 }
133         } else {
134                 parent = vfolder_info_get_parent (info, vuri.path);
135                 if (!parent) {
136                         NICE_UNLOCK_INFO (info, want_write);
137                         return GNOME_VFS_ERROR_NOT_FOUND;
138                 }
139
140                 if (!folder_get_child (parent, vuri.file, &child)) {
141                         NICE_UNLOCK_INFO (info, want_write);
142                         return GNOME_VFS_ERROR_NOT_FOUND;
143                 }
144
145                 if (child.type == FOLDER) {
146                         NICE_UNLOCK_INFO (info, want_write);
147                         return GNOME_VFS_ERROR_IS_DIRECTORY;
148                 }
149
150                 if (want_write) {
151                         if (!entry_make_user_private (child.entry, parent)) {
152                                 VFOLDER_INFO_WRITE_UNLOCK (info);
153                                 return GNOME_VFS_ERROR_READ_ONLY;
154                         }
155                 }
156         }
157
158         file_uri = entry_get_real_uri (child.entry);
159         result = gnome_vfs_open_uri_cancellable (&file_handle,
160                                                  file_uri,
161                                                  mode,
162                                                  context);
163         gnome_vfs_uri_unref (file_uri);
164
165         if (result == GNOME_VFS_ERROR_CANCELLED) {
166                 NICE_UNLOCK_INFO (info, want_write);
167                 return result;
168         }
169
170         vfolder_handle = file_handle_new (file_handle, 
171                                           info, 
172                                           child.entry, 
173                                           want_write);
174         *method_handle = (GnomeVFSMethodHandle *) vfolder_handle;
175
176         NICE_UNLOCK_INFO (info, want_write);
177
178         return result;
179 }
180
181 \f
182 static GnomeVFSResult
183 do_create (GnomeVFSMethod *method,
184            GnomeVFSMethodHandle **method_handle,
185            GnomeVFSURI *uri,
186            GnomeVFSOpenMode mode,
187            gboolean exclusive,
188            guint perm,
189            GnomeVFSContext *context)
190 {
191         GnomeVFSResult result = GNOME_VFS_OK;
192         GnomeVFSHandle *file_handle;
193         FileHandle *vfolder_handle;
194         GnomeVFSURI *file_uri;
195         VFolderURI vuri;
196         VFolderInfo *info;
197         Folder *parent;
198         FolderChild child;
199         Entry *new_entry;
200         const gchar *dirname;
201         gchar *filename, *basename;
202
203         VFOLDER_URI_PARSE (uri, &vuri);
204
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;
208         
209         if (!vfolder_check_extension (vuri.file, ".desktop") &&
210             !vfolder_check_extension (vuri.file, ".directory")) {
211                 return GNOME_VFS_ERROR_INVALID_URI;
212         }
213
214         info = vfolder_info_locate (vuri.scheme);
215         if (!info)
216                 return GNOME_VFS_ERROR_INVALID_URI;
217
218         if (info->read_only || vuri.is_all_scheme)
219                 return GNOME_VFS_ERROR_READ_ONLY;
220
221         VFOLDER_INFO_WRITE_LOCK (info);
222
223         parent = vfolder_info_get_parent (info, vuri.path);
224         if (!parent) {
225                 VFOLDER_INFO_WRITE_UNLOCK (info);
226                 return GNOME_VFS_ERROR_NOT_FOUND;
227         }
228
229         if (folder_get_child (parent, vuri.file, &child)) {
230                 VFOLDER_INFO_WRITE_UNLOCK (info);
231
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;
236         }
237
238         /* 
239          * make a user-local copy, so the folder will be written to the user's
240          * private .vfolder-info file 
241          */
242         if (!folder_make_user_private (parent)) {
243                 VFOLDER_INFO_WRITE_UNLOCK (info);
244                 return GNOME_VFS_ERROR_READ_ONLY;
245         }
246
247         /* 
248          * Create file in writedir unless writedir is not set or folder is
249          * a <ParentLink>.  Otherwise create in parent if exists.
250          */
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);
256                 g_free (basename);
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);
261         } else {
262                 /* Nowhere to create file, fail */
263                 VFOLDER_INFO_WRITE_UNLOCK (info);
264                 return GNOME_VFS_ERROR_READ_ONLY;
265         }
266
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);
271                 g_free (filename);
272                 return result;
273         }
274
275         file_uri = gnome_vfs_uri_new (filename);
276         result = gnome_vfs_create_uri_cancellable (&file_handle,
277                                                    file_uri,
278                                                    mode,
279                                                    exclusive,
280                                                    perm,
281                                                    context);
282         gnome_vfs_uri_unref (file_uri);
283
284         if (result != GNOME_VFS_OK) {
285                 VFOLDER_INFO_WRITE_UNLOCK (info);
286                 g_free (filename);
287                 return result;
288         }
289
290         /* Create it */
291         new_entry = entry_new (info, 
292                                filename, 
293                                vuri.file, 
294                                TRUE /*user_private*/,
295                                1000 /*weight*/);
296         g_free (filename);
297
298         if (!new_entry) {
299                 VFOLDER_INFO_WRITE_UNLOCK (info);
300                 return GNOME_VFS_ERROR_READ_ONLY;
301         }
302
303         if (!parent->is_link)
304                 folder_add_include (parent, entry_get_filename (new_entry));
305
306         folder_add_entry (parent, new_entry);
307
308         vfolder_handle = file_handle_new (file_handle,
309                                           info,
310                                           new_entry,
311                                           mode & GNOME_VFS_OPEN_WRITE);
312         *method_handle = (GnomeVFSMethodHandle *) vfolder_handle;
313
314         VFOLDER_INFO_WRITE_UNLOCK (info);
315
316         vfolder_info_emit_change (info, 
317                                   uri->text,
318                                   GNOME_VFS_MONITOR_EVENT_CREATED);
319
320         return result;
321 }
322
323 \f
324 static GnomeVFSResult
325 do_close (GnomeVFSMethod *method,
326           GnomeVFSMethodHandle *method_handle,
327           GnomeVFSContext *context)
328 {
329         GnomeVFSResult result;
330         FileHandle *handle = (FileHandle *) method_handle;
331
332         if (method_handle == (GnomeVFSMethodHandle *) method)
333                 return GNOME_VFS_OK;
334         
335         result = gnome_vfs_close_cancellable (handle->handle, context);
336
337         if (handle->write) {
338                 VFOLDER_INFO_WRITE_LOCK (handle->info);
339                 entry_set_dirty (handle->entry);
340                 VFOLDER_INFO_WRITE_UNLOCK (handle->info);
341         } 
342
343         file_handle_free (handle);
344
345         return result;
346 }
347
348 \f
349 static GnomeVFSResult
350 do_read (GnomeVFSMethod *method,
351          GnomeVFSMethodHandle *method_handle,
352          gpointer buffer,
353          GnomeVFSFileSize num_bytes,
354          GnomeVFSFileSize *bytes_read,
355          GnomeVFSContext *context)
356 {
357         GnomeVFSResult result;
358         FileHandle *handle = (FileHandle *)method_handle;
359         
360         result = gnome_vfs_read_cancellable (handle->handle,
361                                              buffer, num_bytes,
362                                              bytes_read,
363                                              context);
364
365         return result;
366 }
367
368 \f
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)
376 {
377         GnomeVFSResult result;
378         FileHandle *handle = (FileHandle *)method_handle;
379
380         result = gnome_vfs_write_cancellable (handle->handle,
381                                               buffer, num_bytes,
382                                               bytes_written,
383                                               context);
384
385         return result;
386 }
387
388 \f
389 static GnomeVFSResult
390 do_seek (GnomeVFSMethod *method,
391          GnomeVFSMethodHandle *method_handle,
392          GnomeVFSSeekPosition whence,
393          GnomeVFSFileOffset offset,
394          GnomeVFSContext *context)
395 {
396         GnomeVFSResult result;
397         FileHandle *handle = (FileHandle *)method_handle;
398         
399         result = gnome_vfs_seek_cancellable (handle->handle,
400                                              whence, offset,
401                                              context);
402
403         return result;
404 }
405
406 \f
407 static GnomeVFSResult
408 do_tell (GnomeVFSMethod *method,
409          GnomeVFSMethodHandle *method_handle,
410          GnomeVFSFileOffset *offset_return)
411 {
412         GnomeVFSResult result;
413         FileHandle *handle = (FileHandle *)method_handle;
414         
415         result = gnome_vfs_tell (handle->handle, offset_return);
416
417         return result;
418 }
419
420 \f
421 static GnomeVFSResult
422 do_truncate_handle (GnomeVFSMethod *method,
423                     GnomeVFSMethodHandle *method_handle,
424                     GnomeVFSFileSize where,
425                     GnomeVFSContext *context)
426 {
427         GnomeVFSResult result;
428         FileHandle *handle = (FileHandle *)method_handle;
429         
430         result = gnome_vfs_truncate_handle_cancellable (handle->handle,
431                                                         where,
432                                                         context);
433
434         return result;
435 }
436
437 \f
438 static GnomeVFSResult
439 do_truncate (GnomeVFSMethod *method,
440              GnomeVFSURI *uri,
441              GnomeVFSFileSize where,
442              GnomeVFSContext *context)
443 {
444         GnomeVFSURI *file_uri;
445         GnomeVFSResult result = GNOME_VFS_OK;
446         VFolderInfo *info;
447         Folder *parent;
448         FolderChild child;
449         VFolderURI vuri;
450
451         VFOLDER_URI_PARSE (uri, &vuri);
452
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;
456
457         info = vfolder_info_locate (vuri.scheme);
458         if (!info)
459                 return GNOME_VFS_ERROR_INVALID_URI;
460
461         if (info->read_only || vuri.is_all_scheme)
462                 return GNOME_VFS_ERROR_READ_ONLY;
463
464         VFOLDER_INFO_WRITE_LOCK (info);
465
466         parent = vfolder_info_get_parent (info, vuri.path);
467         if (!parent) {
468                 VFOLDER_INFO_WRITE_UNLOCK (info);
469                 return GNOME_VFS_ERROR_NOT_FOUND;
470         }
471
472         if (!folder_get_child (parent, vuri.file, &child)) {
473                 VFOLDER_INFO_WRITE_UNLOCK (info);
474                 return GNOME_VFS_ERROR_NOT_FOUND;
475         }
476
477         if (child.type == FOLDER) {
478                 VFOLDER_INFO_WRITE_UNLOCK (info);
479                 return GNOME_VFS_ERROR_IS_DIRECTORY;
480         }
481
482         if (!entry_make_user_private (child.entry, parent)) {
483                 VFOLDER_INFO_WRITE_UNLOCK (info);
484                 return GNOME_VFS_ERROR_READ_ONLY;
485         }
486
487         file_uri = entry_get_real_uri (child.entry);
488         
489         VFOLDER_INFO_WRITE_UNLOCK (info);
490
491         result = gnome_vfs_truncate_uri_cancellable (file_uri, where, context);
492         gnome_vfs_uri_unref (file_uri);
493
494         return result;
495 }
496
497 \f
498 typedef struct {
499         VFolderInfo             *info;
500         Folder                  *folder;
501
502         GnomeVFSFileInfoOptions  options;
503
504         /* List of Entries */
505         GSList                  *list;
506         GSList                  *current;
507 } DirHandle;
508
509 static DirHandle *
510 dir_handle_new (VFolderInfo             *info,
511                 Folder                  *folder,
512                 GnomeVFSFileInfoOptions  options)
513 {
514         DirHandle *ret = g_new0 (DirHandle, 1);
515
516         ret->info = info;
517         ret->options = options;
518         ret->folder = folder;
519         folder_ref (folder);
520
521         ret->list = ret->current = folder_list_children (folder);
522
523         return ret;
524 }
525
526 static DirHandle *
527 dir_handle_new_all (VFolderInfo             *info,
528                     GnomeVFSFileInfoOptions  options)
529 {
530         DirHandle *ret = g_new0 (DirHandle, 1);
531         const GSList *iter;
532
533         iter = vfolder_info_list_all_entries (info);
534         for (; iter; iter = iter->next) {
535                 Entry *entry = iter->data;
536                 ret->list = 
537                         g_slist_prepend (
538                                 ret->list, 
539                                 g_strdup (entry_get_displayname (entry)));
540         }
541         ret->list = g_slist_reverse (ret->list);
542                          
543         ret->info = info;
544         ret->options = options;
545         ret->current = ret->list;
546
547         return ret;
548 }
549
550 static void
551 dir_handle_free (DirHandle *handle)
552 {
553         if (handle->folder) 
554                 folder_unref (handle->folder);
555
556         g_slist_foreach (handle->list, (GFunc) g_free, NULL);
557         g_slist_free (handle->list);
558         g_free (handle);
559 }
560
561 \f
562 static GnomeVFSResult
563 do_open_directory (GnomeVFSMethod *method,
564                    GnomeVFSMethodHandle **method_handle,
565                    GnomeVFSURI *uri,
566                    GnomeVFSFileInfoOptions options,
567                    GnomeVFSContext *context)
568 {
569         VFolderURI vuri;
570         DirHandle *dh = NULL;
571         Folder *folder;
572         VFolderInfo *info;
573
574         VFOLDER_URI_PARSE (uri, &vuri);
575
576         /* Read lock is kept until close_directory */
577         info = vfolder_info_locate (vuri.scheme);
578         if (!info)
579                 return GNOME_VFS_ERROR_INVALID_URI;
580
581         VFOLDER_INFO_READ_LOCK (info);
582
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;
589                 }
590
591                 dh = dir_handle_new_all (info, options);
592         } else {
593                 folder = vfolder_info_get_folder (info, vuri.path);
594                 if (!folder) {
595                         VFOLDER_INFO_READ_UNLOCK (info);
596                         return GNOME_VFS_ERROR_NOT_FOUND;
597                 }
598
599                 dh = dir_handle_new (info, folder, options);
600         }
601
602         VFOLDER_INFO_READ_UNLOCK (info);
603
604         *method_handle = (GnomeVFSMethodHandle*) dh;
605
606         return GNOME_VFS_OK;
607 }
608
609 \f
610 static GnomeVFSResult
611 do_close_directory (GnomeVFSMethod *method,
612                     GnomeVFSMethodHandle *method_handle,
613                     GnomeVFSContext *context)
614 {
615         DirHandle *dh;
616
617         dh = (DirHandle*) method_handle;
618         dir_handle_free (dh);
619
620         return GNOME_VFS_OK;
621 }
622
623 \f
624 static void
625 fill_file_info_for_directory (GnomeVFSFileInfo        *file_info,
626                               GnomeVFSFileInfoOptions  options,
627                               const gchar             *name,
628                               time_t                   mtime,
629                               gboolean                 read_only,
630                               const gchar             *link_ref)
631 {
632         file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
633
634         file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
635         file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
636
637         GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
638
639         file_info->mime_type = g_strdup ("x-directory/vfolder-desktop");
640         file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
641
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);
646
647         file_info->name = g_strdup (name);
648
649         if (read_only) {
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;
655         }
656
657 #if 0
658         /* 
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.  
662          */
663         if (link_ref) {
664                 if (options & GNOME_VFS_FILE_INFO_FOLLOW_LINKS)
665                         file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
666                 else
667                         file_info->type = GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK;
668
669                 GNOME_VFS_FILE_INFO_SET_SYMLINK (file_info, TRUE);
670
671                 file_info->symlink_name = g_strdup (link_ref);
672                 file_info->valid_fields |= 
673                         GNOME_VFS_FILE_INFO_FIELDS_SYMLINK_NAME;
674         }
675 #endif
676 }
677
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)
683
684 static GnomeVFSResult
685 get_file_info_internal (VFolderInfo             *info,
686                         FolderChild             *child,
687                         GnomeVFSFileInfoOptions  options,
688                         GnomeVFSFileInfo        *file_info,
689                         GnomeVFSContext         *context)
690 {
691         if (child->type == DESKTOP_FILE) {
692                 GnomeVFSResult result;
693                 GnomeVFSURI *file_uri;
694                 gchar *displayname;
695
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;
699
700                 file_uri = entry_get_real_uri (child->entry);
701                 displayname = g_strdup (entry_get_displayname (child->entry));
702
703                 result = gnome_vfs_get_file_info_uri_cancellable (file_uri,
704                                                                   file_info,
705                                                                   options,
706                                                                   context);
707                 gnome_vfs_uri_unref (file_uri);
708
709                 g_free (file_info->name);
710                 file_info->name = displayname;
711
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;
716
717                 /* Now we wipe those fields we don't support */
718                 file_info->valid_fields &= ~(UNSUPPORTED_INFO_FIELDS);
719
720                 return result;
721         } 
722         else if (child->type == FOLDER) {
723                 if (child->folder)
724                         fill_file_info_for_directory (
725                                 file_info,
726                                 options,
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));
731                 else
732                         /* all-applications root dir */
733                         fill_file_info_for_directory (file_info,
734                                                       options,
735                                                       "/",
736                                                       info->modification_time,
737                                                       TRUE /*read-only*/,
738                                                       NULL);
739                 return GNOME_VFS_OK;
740         } 
741         else
742                 return GNOME_VFS_ERROR_GENERIC;
743 }
744
745 \f
746 static GnomeVFSResult
747 do_read_directory (GnomeVFSMethod *method,
748                    GnomeVFSMethodHandle *method_handle,
749                    GnomeVFSFileInfo *file_info,
750                    GnomeVFSContext *context)
751 {
752         GnomeVFSResult result;
753         DirHandle *dh;
754         gchar *entry_name;
755         FolderChild child;
756
757         dh = (DirHandle*) method_handle;
758
759         VFOLDER_INFO_READ_LOCK (dh->info);
760
761  READ_NEXT_ENTRY:
762
763         if (!dh->current) {
764                 VFOLDER_INFO_READ_UNLOCK (dh->info);
765                 return GNOME_VFS_ERROR_EOF;
766         }
767
768         entry_name = dh->current->data;
769         dh->current = dh->current->next;
770
771         if (dh->folder) {
772                 if (!folder_get_child (dh->folder, entry_name, &child))
773                         goto READ_NEXT_ENTRY;
774         } else {
775                 /* all-scheme */
776                 child.type = DESKTOP_FILE;
777                 child.entry = vfolder_info_lookup_entry (dh->info, entry_name);
778
779                 if (!child.entry)
780                         goto READ_NEXT_ENTRY;
781         }
782
783         if (child.type == FOLDER && folder_is_hidden (child.folder))
784                 goto READ_NEXT_ENTRY;
785
786         result =  get_file_info_internal (dh->info,
787                                           &child, 
788                                           dh->options,
789                                           file_info,
790                                           context);
791         if (result != GNOME_VFS_OK)
792                 goto READ_NEXT_ENTRY;
793
794         VFOLDER_INFO_READ_UNLOCK (dh->info);
795
796         return result;
797 }
798
799 \f
800 static GnomeVFSResult
801 do_get_file_info (GnomeVFSMethod *method,
802                   GnomeVFSURI *uri,
803                   GnomeVFSFileInfo *file_info,
804                   GnomeVFSFileInfoOptions options,
805                   GnomeVFSContext *context)
806 {
807         GnomeVFSResult result = GNOME_VFS_OK;
808         VFolderURI vuri;
809         VFolderInfo *info;
810         Folder *parent;
811         FolderChild child;
812
813         VFOLDER_URI_PARSE (uri, &vuri);
814
815         info = vfolder_info_locate (vuri.scheme);
816         if (!info)
817                 return GNOME_VFS_ERROR_INVALID_URI;
818
819         VFOLDER_INFO_READ_LOCK (info);
820
821         if (vuri.is_all_scheme) {
822                 if (vuri.file) {
823                         /* all-scheme */
824                         child.type = DESKTOP_FILE;
825                         child.entry = vfolder_info_lookup_entry (info, 
826                                                                  vuri.file);
827                         if (!child.entry) {
828                                 VFOLDER_INFO_READ_UNLOCK (info);
829                                 return GNOME_VFS_ERROR_NOT_FOUND;
830                         }
831                 } else {
832                         /* all-scheme root folder */
833                         child.type = FOLDER;
834                         child.folder = NULL;
835                 }
836         } else {
837                 parent = vfolder_info_get_parent (info, vuri.path);
838                 if (!parent) {
839                         VFOLDER_INFO_READ_UNLOCK (info);
840                         return GNOME_VFS_ERROR_NOT_FOUND;
841                 }
842
843                 if (!folder_get_child (parent, vuri.file, &child)) {
844                         VFOLDER_INFO_READ_UNLOCK (info);
845                         return GNOME_VFS_ERROR_NOT_FOUND;
846                 }
847         }
848
849         result = get_file_info_internal (info,
850                                          &child, 
851                                          options, 
852                                          file_info, 
853                                          context);
854
855         VFOLDER_INFO_READ_UNLOCK (info);
856
857         return result;
858 }
859
860 \f
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)
867 {
868         GnomeVFSResult result;
869         FileHandle *handle = (FileHandle *) method_handle;
870         FolderChild child;
871
872         VFOLDER_INFO_READ_LOCK (handle->info);
873
874         child.type = DESKTOP_FILE;
875         child.entry = handle->entry;
876
877         result = get_file_info_internal (handle->info,
878                                          &child, 
879                                          options, 
880                                          file_info, 
881                                          context);
882
883         VFOLDER_INFO_READ_UNLOCK (handle->info);
884
885         return result;
886 }
887
888 \f
889 static gboolean
890 do_is_local (GnomeVFSMethod *method,
891              const GnomeVFSURI *uri)
892 {
893         return TRUE;
894 }
895
896 \f
897 static GnomeVFSResult
898 do_make_directory (GnomeVFSMethod *method,
899                    GnomeVFSURI *uri,
900                    guint perm,
901                    GnomeVFSContext *context)
902 {
903         VFolderInfo *info;
904         Folder *parent, *folder;
905         VFolderURI vuri;
906
907         VFOLDER_URI_PARSE (uri, &vuri);
908
909         /* Root folder always exists */
910         if (vuri.file == NULL)
911                 return GNOME_VFS_ERROR_FILE_EXISTS;
912
913         info = vfolder_info_locate (vuri.scheme);
914         if (!info)
915                 return GNOME_VFS_ERROR_INVALID_URI;
916
917         if (info->read_only || vuri.is_all_scheme)
918                 return GNOME_VFS_ERROR_READ_ONLY;
919
920         VFOLDER_INFO_WRITE_LOCK (info);
921
922         parent = vfolder_info_get_parent (info, vuri.path);
923         if (!parent) {
924                 VFOLDER_INFO_WRITE_UNLOCK (info);
925                 return GNOME_VFS_ERROR_NOT_FOUND;
926         }
927
928         if (folder_get_entry (parent, vuri.file)) {
929                 VFOLDER_INFO_WRITE_UNLOCK (info);
930                 return GNOME_VFS_ERROR_FILE_EXISTS;
931         }
932
933         folder = folder_get_subfolder (parent, vuri.file);
934         if (folder) {
935                 if (!folder_is_hidden (folder)) {
936                         VFOLDER_INFO_WRITE_UNLOCK (info);
937                         return GNOME_VFS_ERROR_FILE_EXISTS;
938                 }
939
940                 if (!folder_make_user_private (folder)) {
941                         VFOLDER_INFO_WRITE_UNLOCK (info);
942                         return GNOME_VFS_ERROR_READ_ONLY;
943                 }
944
945                 if (folder->dont_show_if_empty) {
946                         folder->dont_show_if_empty = FALSE;
947                         vfolder_info_set_dirty (info);
948                 }
949
950                 folder_ref (folder);
951         } else {
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;
957
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, 
961                                                                   vuri.file);
962                         gnome_vfs_uri_unref (real_uri);
963                         
964                         result = 
965                                 gnome_vfs_make_directory_for_uri_cancellable (
966                                         new_uri,
967                                         perm,
968                                         context);
969                         gnome_vfs_uri_unref (new_uri);
970
971                         if (result != GNOME_VFS_OK) {
972                                 VFOLDER_INFO_WRITE_UNLOCK (info);
973                                 return result;
974                         }
975                 }
976
977                 /* 
978                  * Don't write to .vfolder-info file if our parent is a link
979                  * directory, since we just created a real child directory.
980                  */
981                 folder = folder_new (info, vuri.file, parent->is_link == FALSE);
982         }
983
984         folder_remove_exclude (parent, folder_get_name (folder));
985         folder_add_subfolder (parent, folder);
986         folder_unref (folder);
987
988         VFOLDER_INFO_WRITE_UNLOCK (info);
989
990         vfolder_info_emit_change (info, 
991                                   uri->text,
992                                   GNOME_VFS_MONITOR_EVENT_CREATED);     
993
994         return GNOME_VFS_OK;
995 }
996
997 \f
998 static GnomeVFSResult
999 do_remove_directory_unlocked (VFolderInfo *info,
1000                               VFolderURI  *vuri,
1001                               GnomeVFSContext *context)
1002 {
1003         Folder *parent, *folder;
1004         GnomeVFSResult result;
1005
1006         parent = vfolder_info_get_parent (info, vuri->path);
1007         if (!parent)
1008                 return GNOME_VFS_ERROR_NOT_FOUND;
1009
1010         folder = folder_get_subfolder (parent, vuri->file);
1011         if (!folder)
1012                 return GNOME_VFS_ERROR_NOT_FOUND;
1013
1014         if (folder_list_subfolders (folder) || folder_list_entries (folder))
1015                 return GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY;
1016
1017         if (!folder_make_user_private (parent))
1018                 return GNOME_VFS_ERROR_READ_ONLY;
1019
1020         if (folder->is_link) {
1021                 gchar *uristr;
1022                 GnomeVFSURI *new_uri;
1023
1024                 uristr = vfolder_build_uri (folder_get_extend_uri (folder),
1025                                             vuri->file,
1026                                             NULL);
1027                 new_uri = gnome_vfs_uri_new (uristr);
1028                 g_free (uristr);
1029
1030                 /* Remove from the parent as well as in our .vfolder-info */
1031                 result = 
1032                         gnome_vfs_remove_directory_from_uri_cancellable (
1033                                 new_uri,
1034                                 context);
1035                 gnome_vfs_uri_unref (new_uri);
1036
1037                 if (result != GNOME_VFS_OK)
1038                         return result;
1039         } 
1040
1041         folder_add_exclude (parent, folder_get_name (folder));
1042         folder_remove_subfolder (parent, folder);
1043
1044         return GNOME_VFS_OK;
1045 }
1046
1047 static GnomeVFSResult
1048 do_remove_directory (GnomeVFSMethod *method,
1049                      GnomeVFSURI *uri,
1050                      GnomeVFSContext *context)
1051 {
1052         VFolderInfo *info;
1053         VFolderURI vuri;
1054         GnomeVFSResult result;
1055
1056         VFOLDER_URI_PARSE (uri, &vuri);
1057
1058         info = vfolder_info_locate (vuri.scheme);
1059         if (!info)
1060                 return GNOME_VFS_ERROR_INVALID_URI;
1061
1062         if (info->read_only || vuri.is_all_scheme)
1063                 return GNOME_VFS_ERROR_READ_ONLY;
1064
1065         VFOLDER_INFO_WRITE_LOCK (info);
1066         result = do_remove_directory_unlocked (info, &vuri, context);
1067         VFOLDER_INFO_WRITE_UNLOCK (info);
1068
1069         if (result == GNOME_VFS_OK)
1070                 vfolder_info_emit_change (info, 
1071                                           uri->text, 
1072                                           GNOME_VFS_MONITOR_EVENT_DELETED);
1073
1074         return result;
1075 }
1076
1077 \f
1078 static GnomeVFSResult
1079 do_unlink_unlocked (VFolderInfo *info,
1080                     VFolderURI  *vuri,
1081                     GnomeVFSContext *context)
1082 {
1083         Folder *parent;
1084         Entry *entry;
1085
1086         parent = vfolder_info_get_parent (info, vuri->path);
1087         if (!parent)
1088                 return GNOME_VFS_ERROR_NOT_FOUND;
1089
1090         entry = folder_get_entry (parent, vuri->file);
1091         if (!entry) {
1092                 if (folder_get_subfolder (parent, vuri->file))
1093                         return GNOME_VFS_ERROR_IS_DIRECTORY;
1094                 else
1095                         return GNOME_VFS_ERROR_NOT_FOUND;
1096         }
1097
1098         if (parent->is_link || entry_is_user_private (entry)) {
1099                 GnomeVFSURI *uri;
1100                 GnomeVFSResult result;
1101                 
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);
1106
1107                 /* 
1108                  * We only care about the result if its a linked directory.
1109                  * Otherwise we can just modify the .vfolder-info.
1110                  */
1111                 if (parent->is_link && result != GNOME_VFS_OK)
1112                         return result;
1113         }
1114
1115         if (!parent->is_link) {
1116                 if (!folder_make_user_private (parent))
1117                         return GNOME_VFS_ERROR_READ_ONLY;
1118
1119                 /* Clear out the <Include> */
1120                 if (entry_is_user_private (entry))
1121                         folder_remove_include (parent, 
1122                                                entry_get_filename (entry));
1123
1124                 folder_add_exclude (parent, entry_get_displayname (entry));
1125         }
1126
1127         folder_remove_entry (parent, entry);
1128
1129         return GNOME_VFS_OK;
1130 }
1131
1132 static GnomeVFSResult
1133 do_unlink (GnomeVFSMethod *method,
1134            GnomeVFSURI *uri,
1135            GnomeVFSContext *context)
1136 {
1137         VFolderInfo *info;
1138         VFolderURI vuri;
1139         GnomeVFSResult result;
1140
1141         VFOLDER_URI_PARSE (uri, &vuri);
1142
1143         if (!vuri.file)
1144                 return GNOME_VFS_ERROR_INVALID_URI;
1145         else if (vuri.is_all_scheme)
1146                 return GNOME_VFS_ERROR_READ_ONLY;
1147
1148         info = vfolder_info_locate (vuri.scheme);
1149         if (!info)
1150                 return GNOME_VFS_ERROR_INVALID_URI;
1151
1152         if (info->read_only)
1153                 return GNOME_VFS_ERROR_READ_ONLY;
1154
1155         VFOLDER_INFO_WRITE_LOCK (info);
1156         result = do_unlink_unlocked (info, &vuri, context);
1157         VFOLDER_INFO_WRITE_UNLOCK (info);
1158
1159         if (result == GNOME_VFS_OK)
1160                 vfolder_info_emit_change (info, 
1161                                           uri->text, 
1162                                           GNOME_VFS_MONITOR_EVENT_DELETED);
1163
1164         return result;
1165 }
1166
1167 \f
1168 static void
1169 set_desktop_file_key (GString *fullbuf, gchar *key, gchar *value)
1170 {
1171         gchar *key_idx, *val_end;
1172
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');
1180                 if (val_end < 0)
1181                         val_end = strchr (key_idx, '\r');
1182                 if (val_end < 0)
1183                         val_end = &fullbuf->str [fullbuf->len - 1];
1184
1185                 /* Erase the old name */
1186                 g_string_erase (fullbuf, 
1187                                 key_idx - fullbuf->str, 
1188                                 val_end - key_idx);
1189         }
1190
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');
1195
1196         g_string_append_printf (fullbuf, "%s=%s\n", key, value);
1197 }
1198
1199 static void
1200 set_desktop_file_locale_key (GString *fullbuf, gchar *key, gchar *value)
1201 {
1202         GList *locale_list;
1203         const gchar *locale;
1204         gchar *locale_key;
1205
1206         /* Get the list of applicable locales */
1207         locale_list = gnome_vfs_i18n_get_language_list ("LC_MESSAGES");
1208
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);
1213         else
1214                 locale_key = g_strdup_printf ("%s[%s]", key, locale);
1215
1216         set_desktop_file_key (fullbuf, locale_key, value);
1217
1218         g_list_free (locale_list);
1219         g_free (locale_key);
1220 }
1221
1222 static void
1223 set_dot_directory_locale_name (Folder *folder, gchar *val)
1224 {
1225         Entry *dot_file;
1226         GnomeVFSHandle *handle;
1227         GnomeVFSFileSize readlen, writelen, offset = 0;
1228         GString *fullbuf;
1229         char buf[2048];
1230         guint mode, perm;
1231
1232         dot_file = folder_get_entry (folder, ".directory");
1233         if (!dot_file)
1234                 return;
1235         if (!entry_make_user_private (dot_file, folder))
1236                 return;
1237
1238         mode = (GNOME_VFS_OPEN_READ  | 
1239                 GNOME_VFS_OPEN_WRITE | 
1240                 GNOME_VFS_OPEN_RANDOM);
1241
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);
1246
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),
1252                               mode,
1253                               TRUE,
1254                               perm) != GNOME_VFS_OK)
1255                 return;
1256
1257         /* read in the file contents to fullbuf */
1258         fullbuf = g_string_new (NULL);
1259         while (gnome_vfs_read (handle, 
1260                                buf, 
1261                                sizeof (buf), 
1262                                &readlen) == GNOME_VFS_OK) {
1263                 g_string_append_len (fullbuf, buf, readlen);
1264         }
1265
1266         /* set the key, replacing if necessary */
1267         set_desktop_file_locale_key (fullbuf, "Name", val);
1268
1269         /* clear it */
1270         gnome_vfs_truncate_handle (handle, 0);
1271         gnome_vfs_seek (handle, GNOME_VFS_SEEK_START, 0);
1272
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) {
1279                 offset += writelen;
1280         }
1281
1282         gnome_vfs_close (handle);
1283         g_string_free (fullbuf, TRUE);
1284 }
1285
1286 static GnomeVFSResult
1287 do_move (GnomeVFSMethod *method,
1288          GnomeVFSURI *old_uri,
1289          GnomeVFSURI *new_uri,
1290          gboolean force_replace,
1291          GnomeVFSContext *context)
1292 {
1293         GnomeVFSResult result = GNOME_VFS_OK;
1294         VFolderInfo *info;
1295         Folder *old_parent, *new_parent;
1296         VFolderURI old_vuri, new_vuri;
1297         FolderChild old_child, existing_child;
1298
1299         VFOLDER_URI_PARSE (old_uri, &old_vuri);
1300         VFOLDER_URI_PARSE (new_uri, &new_vuri);
1301
1302         if (!old_vuri.file)
1303                 return GNOME_VFS_ERROR_INVALID_URI;
1304
1305         if (old_vuri.is_all_scheme || new_vuri.is_all_scheme)
1306                 return GNOME_VFS_ERROR_READ_ONLY;
1307
1308         if (strcmp (old_vuri.scheme, new_vuri.scheme) != 0)
1309                 return GNOME_VFS_ERROR_NOT_SAME_FILE_SYSTEM;
1310
1311         info = vfolder_info_locate (old_vuri.scheme);
1312         if (!info)
1313                 return GNOME_VFS_ERROR_INVALID_URI;
1314         
1315         if (info->read_only)
1316                 return GNOME_VFS_ERROR_READ_ONLY;
1317
1318         VFOLDER_INFO_WRITE_LOCK (info);
1319
1320         old_parent = vfolder_info_get_parent (info, old_vuri.path);
1321         if (!old_parent || 
1322             !folder_get_child (old_parent, old_vuri.file, &old_child)) {
1323                 VFOLDER_INFO_WRITE_UNLOCK (info);
1324                 return GNOME_VFS_ERROR_NOT_FOUND;
1325         }
1326
1327         if (!folder_make_user_private (old_parent)) {
1328                 VFOLDER_INFO_WRITE_UNLOCK (info);
1329                 return GNOME_VFS_ERROR_READ_ONLY;
1330         }
1331
1332         new_parent = vfolder_info_get_parent (info, new_vuri.path);
1333         if (!new_parent) {
1334                 VFOLDER_INFO_WRITE_UNLOCK (info);
1335                 return GNOME_VFS_ERROR_NOT_A_DIRECTORY;
1336         }
1337         
1338         if (!folder_make_user_private (new_parent)) {
1339                 VFOLDER_INFO_WRITE_UNLOCK (info);
1340                 return GNOME_VFS_ERROR_READ_ONLY;
1341         }
1342
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;
1347                 }
1348         }
1349
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;
1355                 }
1356
1357                 if (existing_child.type == FOLDER) {
1358                         VFOLDER_INFO_WRITE_UNLOCK (info);
1359                         return GNOME_VFS_ERROR_IS_DIRECTORY;
1360                 }
1361
1362                 /* ref in case old_parent is new_parent */
1363                 entry_ref (old_child.entry);
1364
1365                 if (existing_child.type == DESKTOP_FILE) {
1366                         result = do_unlink_unlocked (info,
1367                                                      &new_vuri,
1368                                                      context);
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);
1373                                 return result;
1374                         }
1375                 }
1376
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));
1381
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);
1387                 }
1388
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));
1393
1394                 entry_unref (old_child.entry);
1395
1396                 vfolder_info_emit_change (info, 
1397                                           old_uri->text,
1398                                           GNOME_VFS_MONITOR_EVENT_DELETED);
1399
1400                 vfolder_info_emit_change (info, 
1401                                           new_uri->text,
1402                                           GNOME_VFS_MONITOR_EVENT_CREATED);
1403         } 
1404         else if (old_child.type == FOLDER) {
1405                 Folder *iter;
1406
1407                 if (existing_child.type && existing_child.type != FOLDER) {
1408                         VFOLDER_INFO_WRITE_UNLOCK (info);
1409                         return GNOME_VFS_ERROR_NOT_A_DIRECTORY;
1410                 }
1411
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;
1416                         }
1417                 }
1418
1419                 /* ref in case old_parent is new_parent */
1420                 folder_ref (old_child.folder);
1421
1422                 if (old_parent != new_parent) {
1423                         result = do_remove_directory_unlocked (info, 
1424                                                                &new_vuri,
1425                                                                context);
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);
1430                                 return result;
1431                         }
1432                 }
1433
1434                 folder_remove_subfolder (old_parent, old_child.folder);
1435                 folder_add_exclude (old_parent, old_vuri.file);
1436
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);
1440
1441                 /* do the .directory name change */
1442                 set_dot_directory_locale_name (old_child.folder, new_vuri.file);
1443
1444                 vfolder_info_emit_change (info, 
1445                                           old_uri->text,
1446                                           GNOME_VFS_MONITOR_EVENT_DELETED);
1447
1448                 vfolder_info_emit_change (info, 
1449                                           new_uri->text,
1450                                           GNOME_VFS_MONITOR_EVENT_CREATED);
1451
1452                 folder_unref (old_child.folder);
1453         }
1454
1455         VFOLDER_INFO_WRITE_UNLOCK (info);
1456
1457         return GNOME_VFS_OK;
1458 }
1459
1460 \f
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)
1467 {
1468         VFolderURI source_vuri, target_vuri;
1469
1470         *same_fs_return = FALSE;
1471
1472         VFOLDER_URI_PARSE (source_uri, &source_vuri);
1473         VFOLDER_URI_PARSE (target_uri, &target_vuri);
1474
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;
1478         else
1479                 *same_fs_return = TRUE;
1480
1481         return GNOME_VFS_OK;
1482 }
1483
1484 \f
1485 static GnomeVFSResult
1486 do_set_file_info (GnomeVFSMethod *method,
1487                   GnomeVFSURI *uri,
1488                   const GnomeVFSFileInfo *info,
1489                   GnomeVFSSetFileInfoMask mask,
1490                   GnomeVFSContext *context)
1491 {
1492         VFolderURI vuri;
1493
1494         VFOLDER_URI_PARSE (uri, &vuri);
1495
1496         if (!vuri.file)
1497                 return GNOME_VFS_ERROR_INVALID_URI;
1498
1499         if (mask & GNOME_VFS_SET_FILE_INFO_NAME) {
1500                 GnomeVFSResult result = GNOME_VFS_OK;
1501                 GnomeVFSURI *parent_uri, *new_uri;
1502
1503                 parent_uri = gnome_vfs_uri_get_parent (uri);
1504                 new_uri = gnome_vfs_uri_append_file_name (parent_uri, 
1505                                                           info->name);
1506                 gnome_vfs_uri_unref (parent_uri);
1507
1508                 if (!new_uri)
1509                         return GNOME_VFS_ERROR_INVALID_URI;
1510
1511                 result = do_move (method,
1512                                   uri,
1513                                   new_uri,
1514                                   FALSE /* force_replace */,
1515                                   context);
1516
1517                 gnome_vfs_uri_unref (new_uri);  
1518                 return result;
1519         } else {
1520                 /* 
1521                  * We don't support setting any of this other permission,
1522                  * times and all that voodoo 
1523                  */
1524                 return GNOME_VFS_ERROR_NOT_SUPPORTED;
1525         }
1526 }
1527
1528 \f
1529 static GnomeVFSResult
1530 do_create_symbolic_link (GnomeVFSMethod *method,
1531                          GnomeVFSURI *uri,
1532                          const char *target_reference,
1533                          GnomeVFSContext *context)
1534 {
1535         VFolderURI vuri;
1536         VFolderInfo *info;
1537         Folder *parent;
1538         FolderChild child;
1539         GnomeVFSResult result;
1540
1541         VFOLDER_URI_PARSE (uri, &vuri);
1542         if (!vuri.file)
1543                 return GNOME_VFS_ERROR_INVALID_URI;
1544
1545         info = vfolder_info_locate (vuri.scheme);
1546         if (!info)
1547                 return GNOME_VFS_ERROR_INVALID_URI;
1548         
1549         if (info->read_only)
1550                 return GNOME_VFS_ERROR_READ_ONLY;
1551
1552         VFOLDER_INFO_WRITE_LOCK (info);
1553
1554         parent = vfolder_info_get_parent (info, vuri.path);
1555         if (!parent) {
1556                 VFOLDER_INFO_WRITE_UNLOCK (info);
1557                 return GNOME_VFS_ERROR_NOT_FOUND;
1558         }
1559
1560         if (folder_get_child (parent, vuri.file, &child)) {
1561                 VFOLDER_INFO_WRITE_UNLOCK (info);
1562                 return GNOME_VFS_ERROR_FILE_EXISTS;
1563         }
1564
1565         if (parent->is_link) {
1566                 gchar *new_uristr;
1567                 GnomeVFSURI *new_uri;
1568
1569                 VFOLDER_INFO_WRITE_UNLOCK (info);
1570
1571                 new_uristr = vfolder_build_uri (folder_get_extend_uri (parent),
1572                                                 vuri.file,
1573                                                 NULL);
1574                 new_uri = gnome_vfs_uri_new (new_uristr);
1575                 
1576                 result = 
1577                         gnome_vfs_create_symbolic_link_cancellable (
1578                                 new_uri,
1579                                 target_reference,
1580                                 context);
1581
1582                 gnome_vfs_uri_unref (new_uri);
1583
1584                 return result;
1585         } else {
1586                 GnomeVFSFileInfo *file_info;
1587                 GnomeVFSURI *link_uri;
1588                 Folder *linkdir;
1589
1590                 if (!folder_make_user_private (parent)) {
1591                         VFOLDER_INFO_WRITE_UNLOCK (info);
1592                         return GNOME_VFS_ERROR_READ_ONLY;
1593                 }
1594
1595                 /* 
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?).  
1599                  */
1600                 VFOLDER_INFO_WRITE_UNLOCK (info);
1601
1602                 link_uri = gnome_vfs_uri_new (target_reference);
1603                 file_info = gnome_vfs_file_info_new ();
1604                 result = 
1605                         gnome_vfs_get_file_info_uri_cancellable (
1606                                 link_uri,
1607                                 file_info,
1608                                 GNOME_VFS_FILE_INFO_FOLLOW_LINKS, 
1609                                 context);
1610                 gnome_vfs_uri_unref (link_uri);
1611
1612                 if (result != GNOME_VFS_OK)
1613                         return GNOME_VFS_ERROR_NOT_FOUND;
1614
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;
1618
1619                 VFOLDER_INFO_WRITE_LOCK (info);
1620
1621                 /* 
1622                  * Reget parent, avoiding a race if it was removed while we were
1623                  * unlocked.
1624                  */
1625                 parent = vfolder_info_get_parent (info, vuri.path);
1626                 if (!parent) {
1627                         VFOLDER_INFO_WRITE_UNLOCK (info);
1628                         return GNOME_VFS_ERROR_NOT_FOUND;
1629                 }
1630
1631                 linkdir = folder_new (info, vuri.file, TRUE);
1632                 folder_set_extend_uri (linkdir, target_reference);
1633                 linkdir->is_link = TRUE;
1634
1635                 folder_add_subfolder (parent, linkdir);
1636                 folder_unref (linkdir);
1637
1638                 VFOLDER_INFO_WRITE_UNLOCK (info);
1639
1640                 vfolder_info_emit_change (info, 
1641                                           uri->text, 
1642                                           GNOME_VFS_MONITOR_EVENT_CREATED);
1643
1644                 return GNOME_VFS_OK;
1645         }
1646 }
1647
1648 \f
1649 static GnomeVFSResult
1650 do_monitor_add (GnomeVFSMethod *method,
1651                 GnomeVFSMethodHandle **method_handle_return,
1652                 GnomeVFSURI *uri,
1653                 GnomeVFSMonitorType monitor_type)
1654 {
1655         VFolderInfo *info;
1656
1657         info = vfolder_info_locate (gnome_vfs_uri_get_scheme (uri));
1658         if (!info)
1659                 return GNOME_VFS_ERROR_INVALID_URI;
1660
1661         VFOLDER_INFO_READ_LOCK (info);
1662         vfolder_info_add_monitor (info, 
1663                                   monitor_type, 
1664                                   uri, 
1665                                   method_handle_return);
1666         VFOLDER_INFO_READ_UNLOCK (info);
1667
1668         return GNOME_VFS_OK;
1669 }
1670
1671 \f
1672 static GnomeVFSResult
1673 do_monitor_cancel (GnomeVFSMethod *method,
1674                    GnomeVFSMethodHandle *method_handle)
1675 {
1676         MonitorHandle *monitor = (MonitorHandle *) method_handle;
1677         VFolderInfo *info;
1678
1679         if (method_handle == NULL)
1680                 return GNOME_VFS_OK;
1681
1682         info = monitor->info;
1683
1684         VFOLDER_INFO_READ_LOCK (info);
1685         vfolder_info_cancel_monitor (method_handle);
1686         VFOLDER_INFO_READ_UNLOCK (info);
1687
1688         return GNOME_VFS_OK;
1689 }
1690
1691 \f
1692 /*
1693  * GnomeVFS Registration
1694  */
1695 static GnomeVFSMethod method = {
1696         sizeof (GnomeVFSMethod),
1697         do_open,
1698         do_create,
1699         do_close,
1700         do_read,
1701         do_write,
1702         do_seek,
1703         do_tell,
1704         do_truncate_handle,
1705         do_open_directory,
1706         do_close_directory,
1707         do_read_directory,
1708         do_get_file_info,
1709         do_get_file_info_from_handle,
1710         do_is_local,
1711         do_make_directory,
1712         do_remove_directory,
1713         do_move,
1714         do_unlink,
1715         do_check_same_fs,
1716         do_set_file_info,
1717         do_truncate,
1718         NULL /* find_directory */,
1719         do_create_symbolic_link,
1720         do_monitor_add,
1721         do_monitor_cancel
1722 };
1723
1724 GnomeVFSMethod *
1725 vfs_module_init (const char *method_name, const char *args)
1726 {
1727         return &method;
1728 }
1729
1730 void
1731 vfs_module_shutdown (GnomeVFSMethod *method)
1732 {
1733         vfolder_info_destroy_all ();
1734 }