ftp://ftp.redhat.com/pub/redhat/linux/rawhide/SRPMS/SRPMS/gnome-vfs2-2.3.8-1.src.rpm
[gnome-vfs-httpcaptive.git] / modules / vfolder-desktop-method.c.read-only
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: 8; c-basic-offset: 8 -*- */
2
3 /* vfolder-desktop-method.c
4
5    Copyright (C) 2001 Red Hat, Inc.
6    Copyright (C) 2001 The Dark Prince
7
8    The Gnome Library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Library General Public License as
10    published by the Free Software Foundation; either version 2 of the
11    License, or (at your option) any later version.
12
13    The Gnome Library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Library General Public License for more details.
17
18    You should have received a copy of the GNU Library General Public
19    License along with the Gnome Library; see the file COPYING.LIB.  If not,
20    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21    Boston, MA 02111-1307, USA.
22 */
23
24 /* URI scheme for reading the "applications:" vfolder and other
25  * vfolder schemes.  Lots of code stolen from the original desktop
26  * reading URI scheme.
27  */
28
29 #ifdef HAVE_CONFIG_H
30 # include <config.h>
31 #endif
32
33 /* Debugging foo: */
34 /*#define D(x) x */
35 #define D(x) ;
36
37 #include <glib.h>
38 #include <sys/types.h>
39 #include <dirent.h>
40 #include <errno.h>
41 #include <string.h>
42 #include <time.h>
43 #include <sys/stat.h>
44 #include <unistd.h>
45 #include <fcntl.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48
49 #include <libxml/parser.h>
50 #include <libxml/tree.h>
51 #include <libxml/xmlmemory.h>
52
53 #include <libgnomevfs/gnome-vfs-mime.h>
54
55 #include <libgnomevfs/gnome-vfs-module.h>
56 #include <libgnomevfs/gnome-vfs-method.h>
57 #include <libgnomevfs/gnome-vfs-utils.h>
58 #include <libgnomevfs/gnome-vfs-ops.h>
59 #include <libgnomevfs/gnome-vfs-module-shared.h>
60 #include <libgnomevfs/gnome-vfs-monitor-private.h>
61
62 #define DOT_GNOME ".gnome2"
63
64 typedef struct _VFolderInfo VFolderInfo;
65 typedef struct _Query Query;
66 typedef struct _QueryKeyword QueryKeyword;
67 typedef struct _QueryFilename QueryFilename;
68 typedef struct _Entry Entry;
69 typedef struct _Folder Folder;
70 typedef struct _EntryFile EntryFile;
71 typedef struct _Keyword Keyword;
72 typedef struct _FileMonitorHandle FileMonitorHandle;
73 typedef struct _StatLoc StatLoc;
74 typedef struct _VFolderURI VFolderURI;
75
76 /* TODO before 2.0: */
77 /* FIXME: also check/monitor desktop_dirs like we do the vfolder
78  * file and the item dirs */
79 /* FIXME: check if thread locks are not completely on crack which
80  * is likely given my experience with threads */
81 /* FIXME: use filename locking, currently we are full of races if
82  * multiple processes write to this filesystem */
83 /* FIXME: implement monitors */
84
85 /* TODO for later (star trek future): */
86 /* FIXME: Maybe when chaining to file:, we should call the gnome-vfs wrapper
87  * functions, instead of the file: methods directly.  */
88 /* FIXME: related to the above: we should support things being on non
89  * file: filesystems.  Such as having the vfolder info file on http
90  * somewhere or some such nonsense :) */
91
92 static GnomeVFSMethod *parent_method = NULL;
93
94 static GHashTable *infos = NULL;
95
96 /* Note: I have no clue about how to write thread safe code and this
97  * is my first attempt, so it's probably wrong
98  * -George */
99 G_LOCK_DEFINE_STATIC (vfolder_lock);
100
101 /* Note: all keywords are quarks */
102 /* Note: basenames are unique */
103
104 #define UNSUPPORTED_INFO_FIELDS (GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS | \
105                                  GNOME_VFS_FILE_INFO_FIELDS_DEVICE | \
106                                  GNOME_VFS_FILE_INFO_FIELDS_INODE | \
107                                  GNOME_VFS_FILE_INFO_FIELDS_LINK_COUNT | \
108                                  GNOME_VFS_FILE_INFO_FIELDS_ATIME)
109
110
111 enum {
112         QUERY_OR,
113         QUERY_AND,
114         QUERY_KEYWORD,
115         QUERY_FILENAME
116 };
117
118 struct _Query {
119         int type;
120         gboolean not;
121         GSList *queries;
122 };
123
124 struct _QueryKeyword {
125         int type;
126         gboolean not;
127         GQuark keyword;
128 };
129
130 struct _QueryFilename {
131         int type;
132         gboolean not;
133         char *filename;
134 };
135
136 enum {
137         ENTRY_FILE,
138         ENTRY_FOLDER
139 };
140
141 struct _Entry {
142         int type;
143         int refcount;
144         int alloc; /* not really useful for folders,
145                       but oh well, whatever.  It's the number
146                       of times this is queried in some directory,
147                       used for the Unallocated query type */
148         char *name;
149
150         GSList *monitors;
151 };
152
153 struct _EntryFile {
154         Entry entry;
155
156         char *filename;
157         gboolean per_user;
158         GSList *keywords;
159
160         gboolean implicit_keywords; /* the keywords were added by us */
161 };
162
163 struct _Folder {
164         Entry entry;
165
166         Folder *parent;
167
168         char *desktop_file; /* the .directory file */
169
170         Query *query;
171
172         /* The following is for per file
173          * access */
174         /* excluded by filename */
175         GHashTable *excludes;
176         /* included by filename */
177         GSList *includes;
178         GHashTable *includes_ht;
179
180         GSList *subfolders;
181
182         /* Some flags */
183         gboolean read_only;
184         gboolean dont_show_if_empty;
185         gboolean only_unallocated; /* include only unallocated items */
186
187         /* lazily done, will run query only when it
188          * needs to */
189         gboolean up_to_date;
190         gboolean sorted;
191         GSList *entries;
192 };
193
194 struct _StatLoc {
195         time_t ctime;
196         time_t last_stat;
197         gboolean trigger_next; /* if true, next check will fail */
198         char name[1]; /* the structure will be long enough to
199                          fit name */
200 };
201
202 struct _VFolderInfo {
203         char *scheme;
204
205         char *filename;
206         char *user_filename;
207         time_t user_filename_last_write;
208         char *desktop_dir; /* directory with .directorys */
209         char *user_desktop_dir; /* directory with .directorys */
210         gboolean user_file_active; /* if using user_filename and
211                                       not filename */
212
213         GSList *item_dirs;
214         char *user_item_dir; /* dir where user changes to
215                                 items are stored */
216
217         /* old style dirs to merge in */
218         GSList *merge_dirs;
219
220         /* if entries are valid, else
221          * they need to be (re)read */
222         gboolean entries_valid;
223
224         GSList *entries;
225
226         /* entry hash by basename */
227         GHashTable *entries_ht;
228
229         /* The root folder */
230         Folder *root;
231
232         /* The unallocated folders, the folder which only
233          * include unallocated items */
234         GSList *unallocated_folders;
235
236         /* some flags */
237         gboolean read_only;
238
239         gboolean dirty;
240
241         int inhibit_write;
242
243         /* change monitoring stuff */
244         GnomeVFSMonitorHandle *filename_monitor;
245         GnomeVFSMonitorHandle *user_filename_monitor;
246
247         /* stat locations (in case we aren't monitoring) */
248         StatLoc *filename_statloc;
249         StatLoc *user_filename_statloc;
250
251         /* for .directory dirs */
252         /* FIXME: */GnomeVFSMonitorHandle *desktop_dir_monitor;
253         /* FIXME: */GnomeVFSMonitorHandle *user_desktop_dir_monitor;
254
255         /* stat locations (in case we aren't monitoring) */
256         /* FIXME: */StatLoc *desktop_dir_statloc;
257         /* FIXME: */StatLoc *user_desktop_dir_statloc;
258
259         /* FIXME: */GSList *file_monitors; /* FileMonitorHandle */
260         /* FIXME: */GSList *free_file_monitors; /* FileMonitorHandle */
261         GSList *folder_monitors; /* FileMonitorHandle */
262         GSList *free_folder_monitors; /* FileMonitorHandle */
263
264         GSList *item_dir_monitors; /* GnomeVFSMonitorHandle */
265
266         /* item dirs to stat */
267         GSList *stat_dirs;
268
269         /* ctime for folders */
270         time_t modification_time;
271
272         guint reread_queue;
273 };
274
275 struct _FileMonitorHandle {
276         int refcount;
277         gboolean exists;
278         gboolean dir_monitor; /* TRUE if dir, FALSE if file */
279         GnomeVFSURI *uri;
280         GnomeVFSMonitorHandle *handle; /* A real handle if we're monitoring
281                                           an actual file here, or NULL */
282         char *filename; /* Just the basename, used in the free_file_list */
283         gboolean is_directory_file;
284 };
285
286 struct _VFolderURI {
287         const gchar *scheme;
288         gboolean     is_all_scheme;
289         gboolean     ends_in_slash;
290         gchar       *path;
291         gchar       *file;
292         GnomeVFSURI *uri;
293 };
294
295
296 static Entry *  entry_ref                       (Entry *entry);
297 static Entry *  entry_ref_alloc                 (Entry *entry);
298 static void     entry_unref                     (Entry *entry);
299 static void     entry_unref_dealloc             (Entry *entry);
300 static void     query_destroy                   (Query *query);
301 static void     ensure_folder                   (VFolderInfo *info,
302                                                  Folder *folder,
303                                                  gboolean subfolders,
304                                                  Folder *except,
305                                                  gboolean ignore_unallocated);
306 static void     ensure_folder_unlocked          (VFolderInfo *info,
307                                                  Folder *folder,
308                                                  gboolean subfolders,
309                                                  Folder *except,
310                                                  gboolean ignore_unallocated);
311 /* An EVIL function for quick reading of .desktop files,
312  * only reads in one or two keys, but that's ALL we need */
313 static void     readitem_entry                  (const char *filename,
314                                                  const char *key1,
315                                                  char **result1,
316                                                  const char *key2,
317                                                  char **result2);
318 static gboolean vfolder_info_reload             (VFolderInfo *info,
319                                                  GnomeVFSResult *result,
320                                                  GnomeVFSContext *context,
321                                                  gboolean force_read_items);
322 static gboolean vfolder_info_reload_unlocked    (VFolderInfo *info,
323                                                  GnomeVFSResult *result,
324                                                  GnomeVFSContext *context,
325                                                  gboolean force_read_items);
326 static void     invalidate_folder_subfolders    (Folder   *folder,
327                                                  gboolean  lock_taken);
328 static Folder * resolve_folder                  (VFolderInfo *info,
329                                                  const char *path,
330                                                  gboolean ignore_basename,
331                                                  GnomeVFSResult *result,
332                                                  GnomeVFSContext *context);
333 static gboolean vfolder_info_read_items         (VFolderInfo *info,
334                                                  GnomeVFSResult *result,
335                                                  GnomeVFSContext *context);
336
337 /* assumes vuri->path already set */
338 static gboolean
339 vfolder_uri_parse_internal (GnomeVFSURI *uri, VFolderURI *vuri)
340 {
341         vuri->scheme = (gchar *) gnome_vfs_uri_get_scheme (uri);
342
343         vuri->ends_in_slash = FALSE;
344
345         if (strncmp (vuri->scheme, "all-", strlen ("all-")) == 0) {
346                 vuri->scheme += strlen ("all-");
347                 vuri->is_all_scheme = TRUE;
348         } else
349                 vuri->is_all_scheme = FALSE;
350
351         if (vuri->path != NULL) {
352                 int last_slash = strlen (vuri->path) - 1;
353                 char *first;
354
355                 /* Note: This handling of paths is somewhat evil, may need a
356                  * bit of a rework */
357
358                 /* kill leading slashes, that is make sure there is
359                  * only one */
360                 for (first = vuri->path; *first == '/'; first++)
361                         ;
362                 if (first != vuri->path) {
363                         first--;
364                         vuri->path = first;
365                 }
366
367                 /* kill trailing slashes (leave first if all slashes) */
368                 while (last_slash > 0 && vuri->path [last_slash] == '/') {
369                         vuri->path [last_slash--] = '\0';
370                         vuri->ends_in_slash = TRUE;
371                 }
372
373                 /* get basename start */
374                 while (last_slash >= 0 && vuri->path [last_slash] != '/')
375                         last_slash--;
376
377                 if (last_slash > -1)
378                         vuri->file = vuri->path + last_slash + 1;
379                 else
380                         vuri->file = vuri->path;
381
382                 if (vuri->file[0] == '\0' &&
383                     strcmp (vuri->path, "/") == 0) {
384                         vuri->file = NULL;
385                 }
386         } else {
387                 vuri->ends_in_slash = TRUE;
388                 vuri->path = "/";
389                 vuri->file = NULL;
390         }
391
392         vuri->uri = uri;
393
394         return TRUE;
395 }
396
397 #define VFOLDER_URI_PARSE(_uri, _vuri) {                                    \
398         gchar *path;                                                        \
399         path = gnome_vfs_unescape_string ((_uri)->text, G_DIR_SEPARATOR_S); \
400         if (path != NULL) {                                                 \
401                 (_vuri)->path = g_alloca (strlen (path) + 1);               \
402                 strcpy ((_vuri)->path, path);                               \
403                 g_free (path);                                              \
404         } else {                                                            \
405                 (_vuri)->path = NULL;                                       \
406         }                                                                   \
407         vfolder_uri_parse_internal ((_uri), (_vuri));                       \
408 }
409
410 static FileMonitorHandle *
411 file_monitor_handle_ref_unlocked (FileMonitorHandle *h)
412 {
413         h->refcount ++;
414         return h;
415 }
416
417 static void
418 file_monitor_handle_unref_unlocked (FileMonitorHandle *h)
419 {
420         h->refcount --;
421         if (h->refcount == 0) {
422                 gnome_vfs_uri_unref (h->uri);
423                 h->uri = NULL;
424
425                 g_free (h->filename);
426                 h->filename = NULL;
427
428                 if (h->handle != NULL) {
429                         gnome_vfs_monitor_cancel (h->handle);
430                         h->handle = NULL;
431                 }
432         }
433 }
434
435 /* This includes the .directory files */
436 static void
437 emit_monitor (Folder *folder, int type)
438 {
439         GSList *li;
440         for (li = ((Entry *)folder)->monitors;
441              li != NULL;
442              li = li->next) {
443                 FileMonitorHandle *handle = li->data;
444                 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *) handle,
445                                             handle->uri, type);
446         }
447 }
448
449 static void
450 emit_file_deleted_monitor (VFolderInfo *info, Entry *entry, Folder *folder)
451 {
452         GSList *li;
453         for (li = entry->monitors;
454              li != NULL;
455              li = li->next) {
456                 VFolderURI vuri;
457                 Folder *f;
458                 GnomeVFSResult result;
459                 FileMonitorHandle *handle = li->data;
460
461                 /* Evil! EVIL URI PARSING. this will eat a lot of
462                  * stack if we have lots of monitors */
463
464                 VFOLDER_URI_PARSE (handle->uri, &vuri);
465
466                 f = resolve_folder (info, 
467                                     vuri.path,
468                                     TRUE /* ignore_basename */,
469                                     &result,
470                                     NULL);
471
472                 if (f == folder)
473                         gnome_vfs_monitor_callback
474                                 ((GnomeVFSMethodHandle *) handle,
475                                  handle->uri,
476                                  GNOME_VFS_MONITOR_EVENT_DELETED);
477         }
478 }
479
480 static void
481 emit_and_delete_monitor (VFolderInfo *info, Folder *folder)
482 {
483         GSList *li;
484         for (li = ((Entry *)folder)->monitors;
485              li != NULL;
486              li = li->next) {
487                 FileMonitorHandle *handle = li->data;
488                 li->data = NULL;
489
490                 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *) handle,
491                                             handle->uri,
492                                             GNOME_VFS_MONITOR_EVENT_DELETED);
493
494                 if (handle->dir_monitor)
495                         info->free_folder_monitors = 
496                                 g_slist_prepend (info->free_folder_monitors,
497                                                  handle);
498                 else
499                         info->free_file_monitors = 
500                                 g_slist_prepend (info->free_file_monitors,
501                                                  handle);
502         }
503         g_slist_free (((Entry *)folder)->monitors);
504         ((Entry *)folder)->monitors = NULL;
505 }
506
507 static gboolean
508 check_ext (const char *name, const char *ext_check)
509 {
510         const char *ext;
511
512         ext = strrchr (name, '.');
513         if (ext == NULL ||
514             strcmp (ext, ext_check) != 0)
515                 return FALSE;
516         else
517                 return TRUE;
518 }
519
520 static StatLoc *
521 bake_statloc (const char *name,
522               time_t curtime)
523 {
524         struct stat s;
525         StatLoc *sl = NULL;
526         if (stat (name, &s) != 0) {
527                 if (errno == ENOENT) {
528                         sl = g_malloc0 (sizeof (StatLoc) +
529                                         strlen (name) + 1);
530                         sl->last_stat = curtime;
531                         sl->ctime = 0;
532                         sl->trigger_next = FALSE;
533                         strcpy (sl->name, name);
534                 }
535                 return sl;
536         }
537
538         sl = g_malloc0 (sizeof (StatLoc) +
539                         strlen (name) + 1);
540         sl->last_stat = curtime;
541         sl->ctime = s.st_ctime;
542         sl->trigger_next = FALSE;
543         strcpy (sl->name, name);
544
545         return sl;
546 }
547
548 /* returns FALSE if we must reread */
549 static gboolean
550 check_statloc (StatLoc *sl,
551                time_t curtime)
552 {
553         struct stat s;
554
555         if (sl->trigger_next) {
556                 sl->trigger_next = FALSE;
557                 return FALSE;
558         }
559
560         /* don't stat more then once every 3 seconds */
561         if (curtime <= sl->last_stat + 3)
562                 return TRUE;
563
564         sl->last_stat = curtime;
565
566         if (stat (sl->name, &s) != 0) {
567                 if (errno == ENOENT &&
568                     sl->ctime == 0)
569                         return TRUE;
570                 else
571                         return FALSE;
572         }
573
574         if (sl->ctime == s.st_ctime)
575                 return TRUE;
576         else
577                 return FALSE;
578 }
579
580 static gboolean
581 ensure_dir (const char *dirname, gboolean ignore_basename)
582 {
583         char *parsed, *p;
584
585         if (dirname == NULL)
586                 return FALSE;
587
588         if (ignore_basename)
589                 parsed = g_path_get_dirname (dirname);
590         else
591                 parsed = g_strdup (dirname);
592
593         if (g_file_test (parsed, G_FILE_TEST_IS_DIR)) {
594                 g_free (parsed);
595                 return TRUE;
596         }
597
598         p = strchr (parsed, '/');
599         if (p == parsed)
600                 p = strchr (p+1, '/');
601
602         while (p != NULL) {
603                 *p = '\0';
604                 if (mkdir (parsed, 0700) != 0 &&
605                     errno != EEXIST) {
606                         g_free (parsed);
607                         return FALSE;
608                 }
609                 *p = '/';
610                 p = strchr (p+1, '/');
611         }
612
613         if (mkdir (parsed, 0700) != 0 &&
614             errno != EEXIST) {
615                 g_free (parsed);
616                 return FALSE;
617         }
618
619         g_free (parsed);
620         return TRUE;
621 }
622
623 /* check for any directory name other then root */
624 static gboolean
625 any_subdir (const char *dirname)
626 {
627         const char *p;
628         if (dirname == NULL)
629                 return FALSE;
630
631         for (p = dirname; *p != '\0'; p++) {
632                 if (*p != '/') {
633                         return TRUE;
634                 }
635         }
636         return FALSE;
637 }
638
639 static void
640 destroy_entry_file (EntryFile *efile)
641 {
642         if (efile == NULL)
643                 return;
644
645         g_free (efile->filename);
646         efile->filename = NULL;
647
648         g_slist_free (efile->keywords);
649         efile->keywords = NULL;
650
651         g_free (efile);
652 }
653
654 static void
655 destroy_folder (Folder *folder)
656 {
657         GSList *list;
658
659         if (folder == NULL)
660                 return;
661
662         if (folder->parent != NULL) {
663                 folder->parent->subfolders =
664                         g_slist_remove (folder->parent->subfolders, folder);
665                 folder->parent->up_to_date = FALSE;
666                 folder->parent = NULL;
667         }
668
669         g_free (folder->desktop_file);
670         folder->desktop_file = NULL;
671
672         query_destroy (folder->query);
673         folder->query = NULL;
674
675         if (folder->excludes != NULL) {
676                 g_hash_table_destroy (folder->excludes);
677                 folder->excludes = NULL;
678         }
679
680         g_slist_foreach (folder->includes, (GFunc)g_free, NULL);
681         g_slist_free (folder->includes);
682         folder->includes = NULL;
683         if (folder->includes_ht != NULL) {
684                 g_hash_table_destroy (folder->includes_ht);
685                 folder->includes_ht = NULL;
686         }
687
688         list = folder->subfolders;
689         folder->subfolders = NULL;
690         g_slist_foreach (list, (GFunc)entry_unref, NULL);
691         g_slist_free (list);
692
693         list = folder->entries;
694         folder->entries = NULL;
695         g_slist_foreach (list, (GFunc)entry_unref, NULL);
696         g_slist_free (list);
697
698         g_free (folder);
699 }
700
701 static Entry *
702 entry_ref (Entry *entry)
703 {
704         if (entry != NULL)
705                 entry->refcount++;
706         return entry;
707 }
708
709 static Entry *
710 entry_ref_alloc (Entry *entry)
711 {
712         entry_ref (entry);
713
714         if (entry != NULL)
715                 entry->alloc++;
716
717         return entry;
718 }
719
720 static void
721 entry_unref (Entry *entry)
722 {
723         if (entry == NULL)
724                 return;
725
726         entry->refcount--;
727
728         if (entry->refcount == 0) {
729                 g_free (entry->name);
730                 entry->name = NULL;
731
732                 g_slist_foreach (entry->monitors,
733                                  (GFunc)file_monitor_handle_unref_unlocked,
734                                  NULL);
735                 g_slist_free (entry->monitors);
736                 entry->monitors = NULL;
737
738                 if (entry->type == ENTRY_FILE)
739                         destroy_entry_file ((EntryFile *)entry);
740                 else /* ENTRY_FOLDER */
741                         destroy_folder ((Folder *)entry);
742         }
743 }
744
745 static void
746 entry_unref_dealloc (Entry *entry)
747 {
748         if (entry != NULL) {
749                 entry->alloc --;
750                 entry_unref (entry);
751         }
752 }
753
754 /* Handles ONLY files, not dirs */
755 /* Also allocates the entries as well as refs them */
756 static GSList *
757 alloc_entries_from_files (VFolderInfo *info, GSList *filenames)
758 {
759         GSList *li;
760         GSList *files;
761
762         files = NULL;
763         for (li = filenames; li != NULL; li = li->next) {
764                 char *filename = li->data;
765                 GSList *entry_list = g_hash_table_lookup (info->entries_ht, filename);
766                 if (entry_list != NULL)
767                         files = g_slist_prepend (files,
768                                                  entry_ref_alloc (entry_list->data));
769         }
770
771         return files;
772 }
773
774 static gboolean
775 matches_query (VFolderInfo *info,
776                Folder *folder,
777                EntryFile *efile,
778                Query *query)
779 {
780         GSList *li;
781
782         if (query == NULL)
783                 return TRUE;
784
785 #define INVERT_IF_NEEDED(val) (query->not ? !(val) : (val))
786         switch (query->type) {
787         case QUERY_OR:
788                 for (li = query->queries; li != NULL; li = li->next) {
789                         Query *subquery = li->data;
790                         if (matches_query (info, folder, efile, subquery))
791                                 return INVERT_IF_NEEDED (TRUE);
792                 }
793                 return INVERT_IF_NEEDED (FALSE);
794         case QUERY_AND:
795                 for (li = query->queries; li != NULL; li = li->next) {
796                         Query *subquery = li->data;
797                         if ( ! matches_query (info, folder, efile, subquery))
798                                 return INVERT_IF_NEEDED (FALSE);
799                 }
800                 return INVERT_IF_NEEDED (TRUE);
801         case QUERY_KEYWORD:
802                 {
803                         QueryKeyword *qkeyword = (QueryKeyword *)query;
804                         for (li = efile->keywords; li != NULL; li = li->next) {
805                                 GQuark keyword = GPOINTER_TO_INT (li->data);
806                                 if (keyword == qkeyword->keyword)
807                                         return INVERT_IF_NEEDED (TRUE);
808                         }
809                         return INVERT_IF_NEEDED (FALSE);
810                 }
811         case QUERY_FILENAME:
812                 {
813                         QueryFilename *qfilename = (QueryFilename *)query;
814                         if (strcmp (qfilename->filename, ((Entry *)efile)->name) == 0) {
815                                 return INVERT_IF_NEEDED (TRUE);
816                         } else {
817                                 return INVERT_IF_NEEDED (FALSE);
818                         }
819                 }
820         }
821 #undef INVERT_IF_NEEDED
822         g_assert_not_reached ();
823         /* huh? */
824         return FALSE;
825 }
826
827 static void
828 dump_unallocated_folders (Folder *folder)
829 {
830         GSList *li;
831         for (li = folder->subfolders; li != NULL; li = li->next)
832                 dump_unallocated_folders (li->data);
833
834         if (folder->only_unallocated &&
835             folder->entries != NULL) {
836                 g_slist_foreach (folder->entries,
837                                  (GFunc)entry_unref_dealloc, NULL);
838                 g_slist_free (folder->entries);
839                 folder->entries = NULL;
840         }
841 }
842
843 /* Run query, allocs and refs the entries */
844 static void
845 append_query (VFolderInfo *info, Folder *folder)
846 {
847         GSList *li;
848
849         if (folder->query == NULL &&
850             ! folder->only_unallocated)
851                 return;
852
853         if (folder->only_unallocated) {
854                 /* dump all folders that use unallocated
855                  * items only.  This sucks if you keep
856                  * reading one and then another such
857                  * folder, but oh well, life sucks for
858                  * you then, but at least you get
859                  * consistent results */
860                 dump_unallocated_folders (info->root);
861
862                 /* ensure all other folders, so that
863                  * after this we know which ones are
864                  * unallocated */
865                 ensure_folder_unlocked (info,
866                                         info->root,
867                                         TRUE /* subfolders */,
868                                         folder /* except */,
869                                         /* avoid infinite loops */
870                                         TRUE /* ignore_unallocated */);
871         }
872
873         for (li = info->entries; li != NULL; li = li->next) {
874                 Entry *entry = li->data;
875                 
876                 if (/* if not file */
877                     entry->type != ENTRY_FILE ||
878                     /* if already included */
879                     (folder->includes_ht != NULL &&
880                      g_hash_table_lookup (folder->includes_ht,
881                                           entry->name) != NULL))
882                         continue;
883
884                 if (folder->only_unallocated &&
885                     entry->alloc != 0)
886                         continue;
887
888                 if (matches_query (info, folder, (EntryFile *)entry,
889                                    folder->query))
890                         folder->entries = g_slist_prepend
891                                 (folder->entries, entry_ref_alloc (entry));
892         }
893 }
894
895 /* get entries in folder */
896 /* FIXME: support cancellation here */
897 static void
898 ensure_folder_unlocked (VFolderInfo *info,
899                         Folder *folder,
900                         gboolean subfolders,
901                         Folder *except,
902                         gboolean ignore_unallocated)
903 {
904         if (subfolders) {
905                 GSList *li;
906                 for (li = folder->subfolders; li != NULL; li = li->next)
907                         ensure_folder_unlocked (info, li->data, subfolders,
908                                                 except, ignore_unallocated);
909         }
910
911         if (except == folder)
912                 return;
913
914         if (ignore_unallocated &&
915             folder->only_unallocated)
916                 return;
917
918         if (folder->up_to_date)
919                 return;
920
921         if (folder->entries != NULL) {
922                 g_slist_foreach (folder->entries,
923                                  (GFunc)entry_unref_dealloc, NULL);
924                 g_slist_free (folder->entries);
925                 folder->entries = NULL;
926         }
927
928         /* Include includes */
929         folder->entries = alloc_entries_from_files (info, folder->includes);
930
931         /* Run query */
932         append_query (info, folder);
933
934         /* We were prepending all this time */
935         folder->entries = g_slist_reverse (folder->entries);
936
937         /* Include subfolders */
938         /* we always whack them onto the beginning */
939         if (folder->subfolders != NULL) {
940                 GSList *subfolders = g_slist_copy (folder->subfolders);
941                 g_slist_foreach (subfolders, (GFunc)entry_ref_alloc, NULL);
942                 folder->entries = g_slist_concat (subfolders, folder->entries);
943         }
944
945         /* Exclude excludes */
946         if (folder->excludes != NULL) {
947                 GSList *li;
948                 GSList *entries = folder->entries;
949                 folder->entries = NULL;
950                 for (li = entries; li != NULL; li = li->next) {
951                         Entry *entry = li->data;
952                         if (g_hash_table_lookup (folder->excludes, entry->name) == NULL)
953                                 folder->entries = g_slist_prepend (folder->entries, entry);
954                         else
955                                 entry_unref_dealloc (entry);
956
957                 }
958                 g_slist_free (entries);
959
960                 /* to preserve the Folders then everything else order */
961                 folder->entries = g_slist_reverse (folder->entries);
962         }
963
964         folder->up_to_date = TRUE;
965         /* not yet sorted */
966         folder->sorted = FALSE;
967 }
968
969 static void
970 ensure_folder (VFolderInfo *info,
971                Folder *folder,
972                gboolean subfolders,
973                Folder *except,
974                gboolean ignore_unallocated)
975 {
976         G_LOCK (vfolder_lock);
977         ensure_folder_unlocked (info, folder, subfolders, except, ignore_unallocated);
978         G_UNLOCK (vfolder_lock);
979 }
980
981 static char *
982 get_directory_file_unlocked (VFolderInfo *info, Folder *folder)
983 {
984         char *filename;
985
986         /* FIXME: cache dir_files */
987         
988         if (folder->desktop_file == NULL)
989                 return NULL;
990
991         if (folder->desktop_file[0] == G_DIR_SEPARATOR)
992                 return g_strdup (folder->desktop_file);
993
994         /* Now try the user directory */
995         if (info->user_desktop_dir != NULL) {
996                 filename = g_build_filename (info->user_desktop_dir,
997                                              folder->desktop_file,
998                                              NULL);
999                 if (access (filename, F_OK) == 0) {
1000                         return filename;
1001                 }
1002
1003                 g_free (filename);
1004         }
1005
1006         filename = g_build_filename (info->desktop_dir, folder->desktop_file, NULL);
1007         if (access (filename, F_OK) == 0) {
1008                 return filename;
1009         }
1010         g_free (filename);
1011
1012         return NULL;
1013 }
1014
1015 static char *
1016 get_directory_file (VFolderInfo *info, Folder *folder)
1017 {
1018         char *ret;
1019
1020         G_LOCK (vfolder_lock);
1021         ret = get_directory_file_unlocked (info, folder);
1022         G_UNLOCK (vfolder_lock);
1023
1024         return ret;
1025 }
1026
1027 static GSList *
1028 get_sort_order (VFolderInfo *info, Folder *folder)
1029 {
1030         GSList *list;
1031         char **parsed;
1032         char *order;
1033         int i;
1034         char *filename;
1035
1036         filename = get_directory_file_unlocked (info, folder);
1037         if (filename == NULL)
1038                 return NULL;
1039
1040         order = NULL;
1041         readitem_entry (filename,
1042                         "SortOrder",
1043                         &order,
1044                         NULL,
1045                         NULL);
1046         g_free (filename);
1047
1048         if (order == NULL)
1049                 return NULL;
1050
1051         parsed = g_strsplit (order, ":", -1);
1052
1053         g_free (order);
1054
1055         list = NULL;
1056         for (i = 0; parsed[i] != NULL; i++) {
1057                 char *word = parsed[i];
1058                 /* steal */
1059                 parsed[i] = NULL;
1060                 /* ignore empty */
1061                 if (word[0] == '\0') {
1062                         g_free (word);
1063                         continue;
1064                 }
1065                 list = g_slist_prepend (list, word);
1066         }
1067         /* we've stolen all strings from it */
1068         g_free (parsed);
1069
1070         return g_slist_reverse (list);
1071 }
1072
1073 /* get entries in folder */
1074 static void
1075 ensure_folder_sort (VFolderInfo *info, Folder *folder)
1076 {
1077         GSList *li, *sort_order;
1078         GSList *entries;
1079         GHashTable *entry_hash;
1080
1081         ensure_folder (info, folder,
1082                        FALSE /* subfolders */,
1083                        NULL /* except */,
1084                        FALSE /* ignore_unallocated */);
1085         if (folder->sorted)
1086                 return;
1087
1088         G_LOCK (vfolder_lock);
1089
1090         sort_order = get_sort_order (info, folder);
1091         if (sort_order == NULL) {
1092                 folder->sorted = TRUE;
1093                 G_UNLOCK (vfolder_lock);
1094                 return;
1095         }
1096
1097         entries = folder->entries;
1098         folder->entries = NULL;
1099
1100         entry_hash = g_hash_table_new (g_str_hash, g_str_equal);
1101         for (li = entries; li != NULL; li = li->next) {
1102                 Entry *entry = li->data;
1103                 g_hash_table_insert (entry_hash, entry->name, li);
1104         }
1105
1106         for (li = sort_order; li != NULL; li = li->next) {
1107                 char *word = li->data;
1108                 GSList *entry_list;
1109                 Entry *entry;
1110
1111                 /* we kill the words here */
1112                 li->data = NULL;
1113
1114                 entry_list = g_hash_table_lookup (entry_hash, word);
1115                 g_free (word);
1116
1117                 if (entry_list == NULL)
1118                         continue;
1119
1120                 entry = entry_list->data;
1121
1122                 entries = g_slist_delete_link (entries, entry_list);
1123
1124                 folder->entries = g_slist_prepend (folder->entries,
1125                                                    entry);
1126         }
1127
1128         /* put on those that weren't mentioned in the sort */
1129         for (li = entries; li != NULL; li = li->next) {
1130                 Entry *entry = li->data;
1131
1132                 folder->entries = g_slist_prepend (folder->entries,
1133                                                    entry);
1134         }
1135
1136         g_hash_table_destroy (entry_hash);
1137         g_slist_free (entries);
1138         g_slist_free (sort_order);
1139
1140         folder->sorted = TRUE;
1141
1142         G_UNLOCK (vfolder_lock);
1143 }
1144
1145 static EntryFile *
1146 file_new (const char *name)
1147 {
1148         EntryFile *efile = g_new0 (EntryFile, 1);
1149
1150         efile->entry.type = ENTRY_FILE;
1151         efile->entry.name = g_strdup (name);
1152         efile->entry.refcount = 1;
1153
1154         return efile;
1155 }
1156
1157 static Folder *
1158 folder_new (const char *name)
1159 {
1160         Folder *folder = g_new0 (Folder, 1);
1161
1162         folder->entry.type = ENTRY_FOLDER;
1163         folder->entry.name = g_strdup (name);
1164         folder->entry.refcount = 1;
1165
1166         return folder;
1167 }
1168
1169 static Query *
1170 query_new (int type)
1171 {
1172         Query *query;
1173
1174         if (type == QUERY_KEYWORD)
1175                 query = (Query *)g_new0 (QueryKeyword, 1);
1176         else if (type == QUERY_FILENAME)
1177                 query = (Query *)g_new0 (QueryFilename, 1);
1178         else
1179                 query = g_new0 (Query, 1);
1180
1181         query->type = type;
1182
1183         return query;
1184 }
1185
1186 static void
1187 query_destroy (Query *query)
1188 {
1189         if (query == NULL)
1190                 return;
1191
1192         if (query->type == QUERY_FILENAME) {
1193                 QueryFilename *qfile = (QueryFilename *)query;
1194                 g_free (qfile->filename);
1195                 qfile->filename = NULL;
1196         } else if (query->type == QUERY_OR ||
1197                    query->type == QUERY_AND) {
1198                 g_slist_foreach (query->queries, (GFunc)query_destroy, NULL);
1199                 g_slist_free (query->queries);
1200                 query->queries = NULL;
1201         }
1202
1203         g_free (query);
1204 }
1205
1206 static void
1207 add_folder_monitor_unlocked (VFolderInfo *info,
1208                              FileMonitorHandle *handle)
1209 {
1210         VFolderURI vuri;
1211         GnomeVFSResult result;
1212         Folder *folder;
1213
1214         VFOLDER_URI_PARSE (handle->uri, &vuri);
1215
1216         file_monitor_handle_ref_unlocked (handle);
1217
1218         info->folder_monitors = 
1219                 g_slist_prepend (info->folder_monitors, handle);
1220
1221         folder = resolve_folder (info, 
1222                                  vuri.path,
1223                                  FALSE /* ignore_basename */,
1224                                  &result,
1225                                  NULL);
1226
1227         if (folder == NULL) {
1228                 file_monitor_handle_ref_unlocked (handle);
1229
1230                 info->free_folder_monitors = 
1231                         g_slist_prepend (info->free_folder_monitors, handle);
1232
1233                 if (handle->exists) {
1234                         handle->exists = FALSE;
1235                         gnome_vfs_monitor_callback
1236                                 ((GnomeVFSMethodHandle *)handle,
1237                                  handle->uri, 
1238                                  GNOME_VFS_MONITOR_EVENT_DELETED);
1239                 }
1240         } else {
1241                 file_monitor_handle_ref_unlocked (handle);
1242
1243                 ((Entry *)folder)->monitors = 
1244                         g_slist_prepend (((Entry *)folder)->monitors, handle);
1245
1246                 if ( ! handle->exists) {
1247                         handle->exists = TRUE;
1248                         gnome_vfs_monitor_callback
1249                                 ((GnomeVFSMethodHandle *)handle,
1250                                  handle->uri, 
1251                                  GNOME_VFS_MONITOR_EVENT_CREATED);
1252                 }
1253         }
1254
1255 }
1256
1257 static inline void
1258 invalidate_folder_T (Folder *folder)
1259 {
1260         folder->up_to_date = FALSE;
1261
1262         invalidate_folder_subfolders (folder, TRUE);
1263 }
1264
1265 static inline void
1266 invalidate_folder (Folder *folder)
1267 {
1268         G_LOCK (vfolder_lock);
1269         folder->up_to_date = FALSE;
1270         G_UNLOCK (vfolder_lock);
1271
1272         invalidate_folder_subfolders (folder, FALSE);
1273 }
1274
1275 static void
1276 invalidate_folder_subfolders (Folder   *folder,
1277                               gboolean  lock_taken)
1278 {
1279         GSList *li;
1280
1281         for (li = folder->subfolders; li != NULL; li = li->next) {
1282                 Folder *subfolder = li->data;
1283
1284                 if (!lock_taken)
1285                         invalidate_folder (subfolder);
1286                 else
1287                         invalidate_folder_T (subfolder);
1288         }
1289
1290         emit_monitor (folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
1291 }
1292
1293 /* FIXME: this is UGLY!, we need to figure out when the file
1294  * got finished changing! */
1295 static gboolean
1296 reread_timeout (gpointer data)
1297 {
1298         VFolderInfo *info = data;
1299         gboolean force_read_items = info->file_monitors != NULL;
1300         vfolder_info_reload (info, NULL, NULL, force_read_items);
1301         return FALSE;
1302 }
1303
1304 static void
1305 queue_reread_in (VFolderInfo *info, int msec)
1306 {
1307         G_LOCK (vfolder_lock);
1308         if (info->reread_queue != 0)
1309                 g_source_remove (info->reread_queue);
1310         info->reread_queue = g_timeout_add (msec, reread_timeout, info);
1311         G_UNLOCK (vfolder_lock);
1312 }
1313
1314 static void
1315 vfolder_desktop_dir_monitor (GnomeVFSMonitorHandle *handle,
1316                              const gchar *monitor_uri,
1317                              const gchar *info_uri,
1318                              GnomeVFSMonitorEventType event_type,
1319                              gpointer user_data)
1320 {
1321         /* FIXME: implement */
1322 }
1323
1324 static void
1325 vfolder_user_desktop_dir_monitor (GnomeVFSMonitorHandle *handle,
1326                                   const gchar *monitor_uri,
1327                                   const gchar *info_uri,
1328                                   GnomeVFSMonitorEventType event_type,
1329                                   gpointer user_data)
1330 {
1331         /* FIXME: implement */
1332 }
1333
1334 static void
1335 vfolder_filename_monitor (GnomeVFSMonitorHandle *handle,
1336                           const gchar *monitor_uri,
1337                           const gchar *info_uri,
1338                           GnomeVFSMonitorEventType event_type,
1339                           gpointer user_data)
1340 {
1341         VFolderInfo *info = user_data;
1342
1343         if ((event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
1344              event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) &&
1345             ! info->user_file_active) {
1346                 queue_reread_in (info, 200);
1347         } else if (event_type == GNOME_VFS_MONITOR_EVENT_DELETED &&
1348                    ! info->user_file_active) {
1349                 /* FIXME: is this correct?  I mean now
1350                  * there probably isn't ANY vfolder file, so we
1351                  * init to default values really.  I have no clue what's
1352                  * right here */
1353                 vfolder_info_reload (info, NULL, NULL,
1354                                      TRUE /* force read items */);
1355         }
1356 }
1357
1358 static void
1359 vfolder_user_filename_monitor (GnomeVFSMonitorHandle *handle,
1360                                const gchar *monitor_uri,
1361                                const gchar *info_uri,
1362                                GnomeVFSMonitorEventType event_type,
1363                                gpointer user_data)
1364 {
1365         VFolderInfo *info = user_data;
1366
1367         if ((event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
1368              event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) &&
1369             info->user_file_active) {
1370                 struct stat s;
1371
1372                 /* see if this was really our own change */
1373                 if (info->user_filename_last_write == time (NULL))
1374                         return;
1375                 /* anal retentive */
1376                 if (stat (info->user_filename, &s) == 0 &&
1377                     info->user_filename_last_write == s.st_ctime)
1378                         return;
1379
1380                 queue_reread_in (info, 200);
1381         } else if ((event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
1382                     event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) &&
1383                     ! info->user_file_active) {
1384                 queue_reread_in (info, 200);
1385         } else if (event_type == GNOME_VFS_MONITOR_EVENT_DELETED &&
1386                    info->user_file_active) {
1387                 gboolean force_read_items = info->file_monitors != NULL;
1388                 vfolder_info_reload (info, NULL, NULL, force_read_items);
1389         }
1390 }
1391
1392 static void
1393 item_dir_monitor (GnomeVFSMonitorHandle *handle,
1394                   const gchar *monitor_uri,
1395                   const gchar *info_uri,
1396                   GnomeVFSMonitorEventType event_type,
1397                   gpointer user_data)
1398 {
1399         VFolderInfo *info = user_data;
1400
1401         if (event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
1402             event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) {
1403                 /* first invalidate all folders */
1404                 invalidate_folder (info->root);
1405                 /* second invalidate all entries */
1406                 info->entries_valid = FALSE;
1407
1408                 if (info->file_monitors != NULL) {
1409                         GnomeVFSResult result;
1410                         GSList *li;
1411
1412                         /* Whack all monitors here! */
1413                         for (li = info->file_monitors;
1414                              li != NULL;
1415                              li = li->next) {
1416                                 FileMonitorHandle *h = li->data;
1417                                 if (h->handle != NULL)
1418                                         gnome_vfs_monitor_cancel (h->handle);
1419                                 h->handle = NULL;
1420                         }
1421
1422                         if (vfolder_info_read_items (info, &result, NULL)) {
1423                                 info->entries_valid = TRUE;
1424                         }
1425                 }
1426         }
1427 }
1428
1429 static gboolean
1430 setup_dir_monitor (VFolderInfo *info, const char *dir, gboolean subdirs,
1431                    GnomeVFSResult *result,
1432                    GnomeVFSContext *context)
1433 {
1434         GnomeVFSMonitorHandle *handle;
1435         DIR *dh;
1436         struct dirent *de;
1437         char *uri;
1438
1439         uri = gnome_vfs_get_uri_from_local_path (dir);
1440
1441         if (gnome_vfs_monitor_add (&handle,
1442                                    uri,
1443                                    GNOME_VFS_MONITOR_DIRECTORY,
1444                                    item_dir_monitor,
1445                                    info) != GNOME_VFS_OK) {
1446                 StatLoc *sl = bake_statloc (dir, time (NULL));
1447                 if (sl != NULL)
1448                         info->stat_dirs = g_slist_prepend (info->stat_dirs, sl);
1449                 g_free (uri);
1450                 return TRUE;
1451         }
1452         g_free (uri);
1453
1454         if (gnome_vfs_context_check_cancellation (context)) {
1455                 gnome_vfs_monitor_cancel (handle);
1456                 *result = GNOME_VFS_ERROR_CANCELLED;
1457                 return FALSE;
1458         }
1459
1460         info->item_dir_monitors =
1461                 g_slist_prepend (info->item_dir_monitors, handle);
1462
1463         if ( ! subdirs)
1464                 return TRUE;
1465
1466         dh = opendir (dir);
1467         if (dh == NULL)
1468                 return TRUE;
1469
1470         while ((de = readdir (dh)) != NULL) {
1471                 char *full_path;
1472
1473                 if (gnome_vfs_context_check_cancellation (context)) {
1474                         *result = GNOME_VFS_ERROR_CANCELLED;
1475                         closedir (dh);
1476                         return FALSE;
1477                 }
1478
1479                 if (de->d_name[0] == '.')
1480                         continue;
1481
1482                 full_path = g_build_filename (dir, de->d_name, NULL);
1483                 if (g_file_test (full_path, G_FILE_TEST_IS_DIR)) {
1484                         if ( ! setup_dir_monitor (info, full_path,
1485                                                   TRUE /* subdirs */,
1486                                                   result, context)) {
1487                                 closedir (dh);
1488                                 return FALSE;
1489                         }
1490                 }
1491                 g_free (full_path);
1492         }
1493
1494         closedir (dh);
1495
1496         return TRUE;
1497 }
1498
1499 static gboolean
1500 monitor_setup (VFolderInfo *info,
1501                gboolean setup_filenames,
1502                gboolean setup_itemdirs,
1503                gboolean setup_desktop_dirs,
1504                GnomeVFSResult *result,
1505                GnomeVFSContext *context)
1506 {
1507         char *uri;
1508         GSList *li;
1509
1510         if (setup_filenames) {
1511                 uri = gnome_vfs_get_uri_from_local_path
1512                         (info->filename);
1513
1514                 if (gnome_vfs_monitor_add (&info->filename_monitor,
1515                                            uri,
1516                                            GNOME_VFS_MONITOR_FILE,
1517                                            vfolder_filename_monitor,
1518                                            info) != GNOME_VFS_OK) {
1519                         info->filename_monitor = NULL;
1520                         info->filename_statloc = bake_statloc (info->filename,
1521                                                                time (NULL));
1522                 }
1523                 g_free (uri);
1524         }
1525         if (setup_filenames &&
1526             info->user_filename != NULL) {
1527                 uri = gnome_vfs_get_uri_from_local_path
1528                         (info->user_filename);
1529                 if (gnome_vfs_monitor_add (&info->user_filename_monitor,
1530                                            uri,
1531                                            GNOME_VFS_MONITOR_FILE,
1532                                            vfolder_user_filename_monitor,
1533                                            info) != GNOME_VFS_OK) {
1534                         info->user_filename_monitor = NULL;
1535                         info->user_filename_statloc =
1536                                 bake_statloc (info->user_filename,
1537                                               time (NULL));
1538                 }
1539
1540                 g_free (uri);
1541         }
1542
1543         if (gnome_vfs_context_check_cancellation (context)) {
1544                 *result = GNOME_VFS_ERROR_CANCELLED;
1545                 return FALSE;
1546         }
1547
1548         if (setup_itemdirs) {
1549                 for (li = info->item_dirs; li != NULL; li = li->next) {
1550                         const char *dir = li->data;
1551                         if ( ! setup_dir_monitor (info, dir,
1552                                                   FALSE /* subdirs */,
1553                                                   result, context))
1554                                 return FALSE;
1555                 }
1556                 if (info->user_item_dir != NULL) {
1557                         if ( ! setup_dir_monitor (info, info->user_item_dir,
1558                                                   FALSE /* subdirs */,
1559                                                   result, context))
1560                                 return FALSE;
1561                 }
1562                 for (li = info->merge_dirs; li != NULL; li = li->next) {
1563                         const char *dir = li->data;
1564                         if ( ! setup_dir_monitor (info, dir,
1565                                                   TRUE /* subdirs */,
1566                                                   result, context))
1567                                 return FALSE;
1568                 }
1569         }
1570
1571         if (setup_desktop_dirs) {
1572                 uri = gnome_vfs_get_uri_from_local_path
1573                         (info->desktop_dir);
1574
1575                 if (gnome_vfs_monitor_add (&info->desktop_dir_monitor,
1576                                            uri,
1577                                            GNOME_VFS_MONITOR_FILE,
1578                                            vfolder_desktop_dir_monitor,
1579                                            info) != GNOME_VFS_OK) {
1580                         info->desktop_dir_monitor = NULL;
1581                         info->desktop_dir_statloc =
1582                                 bake_statloc (info->desktop_dir,
1583                                               time (NULL));
1584                 }
1585                 g_free (uri);
1586         }
1587         if (setup_desktop_dirs &&
1588             info->user_desktop_dir != NULL) {
1589                 uri = gnome_vfs_get_uri_from_local_path
1590                         (info->user_desktop_dir);
1591                 if (gnome_vfs_monitor_add (&info->user_desktop_dir_monitor,
1592                                            uri,
1593                                            GNOME_VFS_MONITOR_DIRECTORY,
1594                                            vfolder_user_desktop_dir_monitor,
1595                                            info) != GNOME_VFS_OK) {
1596                         info->user_desktop_dir_monitor = NULL;
1597                         info->user_desktop_dir_statloc =
1598                                 bake_statloc (info->user_desktop_dir,
1599                                               time (NULL));
1600                 }
1601
1602                 g_free (uri);
1603         }
1604
1605         return TRUE;
1606 }
1607
1608 static void
1609 vfolder_info_init (VFolderInfo *info, const char *scheme)
1610 {
1611         const char *path;
1612         GSList *list;
1613
1614         info->scheme = g_strdup (scheme);
1615
1616         info->filename = g_strconcat (SYSCONFDIR, "/X11/desktop-menus/",
1617                                       scheme, ".menu",
1618                                       NULL);
1619         info->user_filename = g_strconcat (g_get_home_dir (),
1620                                            "/" DOT_GNOME "/vfolders/",
1621                                            scheme, ".vfolder-info",
1622                                            NULL);
1623         info->desktop_dir = g_strconcat (SYSCONFDIR,
1624                                          "/gnome-vfs-2.0/vfolders/",
1625                                          NULL);
1626         info->user_desktop_dir = g_strconcat (g_get_home_dir (),
1627                                               "/" DOT_GNOME "/vfolders/",
1628                                               NULL);
1629
1630         /* Init the desktop paths */
1631         list = NULL;
1632         list = g_slist_prepend (list, g_strdup ("/usr/share/applications/"));
1633         if (strcmp ("/usr/share/applications/", DATADIR "/applications/") != 0)
1634                 list = g_slist_prepend (list, g_strdup (DATADIR "/applications/"));
1635         path = g_getenv ("DESKTOP_FILE_PATH");
1636         if (path != NULL) {
1637                 int i;
1638                 char **ppath = g_strsplit (path, ":", -1);
1639                 for (i = 0; ppath[i] != NULL; i++) {
1640                         const char *dir = ppath[i];
1641                         list = g_slist_prepend (list, g_strdup (dir));
1642                 }
1643                 g_strfreev (ppath);
1644         }
1645         info->item_dirs = g_slist_reverse (list);
1646
1647         info->user_item_dir = g_strconcat (g_get_home_dir (),
1648                                            "/" DOT_GNOME "/vfolders/",
1649                                            scheme,
1650                                            NULL);
1651
1652         info->entries_ht = g_hash_table_new (g_str_hash, g_str_equal);
1653
1654         info->root = folder_new ("Root");
1655
1656         info->modification_time = time (NULL);
1657 }
1658
1659 static void
1660 vfolder_info_free_internals_unlocked (VFolderInfo *info)
1661 {
1662         if (info == NULL)
1663                 return;
1664         
1665         if (info->filename_monitor != NULL) {
1666                 gnome_vfs_monitor_cancel (info->filename_monitor);
1667                 info->filename_monitor = NULL;
1668         }
1669
1670         if (info->user_filename_monitor != NULL) {
1671                 gnome_vfs_monitor_cancel (info->user_filename_monitor);
1672                 info->user_filename_monitor = NULL;
1673         }
1674
1675         g_free (info->filename_statloc);
1676         info->filename_statloc = NULL;
1677
1678         g_free (info->user_filename_statloc);
1679         info->user_filename_statloc = NULL;
1680
1681
1682         if (info->desktop_dir_monitor != NULL) {
1683                 gnome_vfs_monitor_cancel (info->desktop_dir_monitor);
1684                 info->desktop_dir_monitor = NULL;
1685         }
1686
1687         if (info->user_desktop_dir_monitor != NULL) {
1688                 gnome_vfs_monitor_cancel (info->user_desktop_dir_monitor);
1689                 info->user_desktop_dir_monitor = NULL;
1690         }
1691
1692         g_free (info->desktop_dir_statloc);
1693         info->desktop_dir_statloc = NULL;
1694
1695         g_free (info->user_desktop_dir_statloc);
1696         info->user_desktop_dir_statloc = NULL;
1697
1698
1699         g_slist_foreach (info->item_dir_monitors,
1700                          (GFunc)gnome_vfs_monitor_cancel, NULL);
1701         g_slist_free (info->item_dir_monitors);
1702         info->item_dir_monitors = NULL;
1703
1704         g_free (info->scheme);
1705         info->scheme = NULL;
1706
1707         g_free (info->filename);
1708         info->filename = NULL;
1709
1710         g_free (info->user_filename);
1711         info->user_filename = NULL;
1712
1713         g_free (info->desktop_dir);
1714         info->desktop_dir = NULL;
1715
1716         g_free (info->user_desktop_dir);
1717         info->user_desktop_dir = NULL;
1718
1719         g_slist_foreach (info->item_dirs, (GFunc)g_free, NULL);
1720         g_slist_free (info->item_dirs);
1721         info->item_dirs = NULL;
1722
1723         g_free (info->user_item_dir);
1724         info->user_item_dir = NULL;
1725
1726         g_slist_foreach (info->merge_dirs, (GFunc)g_free, NULL);
1727         g_slist_free (info->merge_dirs);
1728         info->merge_dirs = NULL;
1729
1730         g_slist_foreach (info->entries, (GFunc)entry_unref, NULL);
1731         g_slist_free (info->entries);
1732         info->entries = NULL;
1733
1734         if (info->entries_ht != NULL)
1735                 g_hash_table_destroy (info->entries_ht);
1736         info->entries_ht = NULL;
1737
1738         g_slist_foreach (info->unallocated_folders,
1739                          (GFunc)entry_unref,
1740                          NULL);
1741         g_slist_free (info->unallocated_folders);
1742         info->unallocated_folders = NULL;
1743
1744         entry_unref ((Entry *)info->root);
1745         info->root = NULL;
1746
1747         g_slist_foreach (info->stat_dirs, (GFunc)g_free, NULL);
1748         g_slist_free (info->stat_dirs);
1749         info->stat_dirs = NULL;
1750
1751         g_slist_foreach (info->folder_monitors,
1752                          (GFunc)file_monitor_handle_unref_unlocked, NULL);
1753         g_slist_free (info->folder_monitors);
1754         info->folder_monitors = NULL;
1755
1756         g_slist_foreach (info->free_folder_monitors,
1757                          (GFunc)file_monitor_handle_unref_unlocked, NULL);
1758         g_slist_free (info->free_folder_monitors);
1759         info->free_folder_monitors = NULL;
1760
1761         g_slist_foreach (info->file_monitors,
1762                          (GFunc)file_monitor_handle_unref_unlocked, NULL);
1763         g_slist_free (info->file_monitors);
1764         info->file_monitors = NULL;
1765
1766         g_slist_foreach (info->free_file_monitors,
1767                          (GFunc)file_monitor_handle_unref_unlocked, NULL);
1768         g_slist_free (info->free_file_monitors);
1769         info->free_file_monitors = NULL;
1770
1771         if (info->reread_queue != 0)
1772                 g_source_remove (info->reread_queue);
1773         info->reread_queue = 0;
1774 }
1775
1776 static void
1777 vfolder_info_free_internals (VFolderInfo *info)
1778 {
1779         G_LOCK (vfolder_lock);
1780         vfolder_info_free_internals_unlocked (info);
1781         G_UNLOCK (vfolder_lock);
1782 }
1783
1784 static void
1785 vfolder_info_destroy (VFolderInfo *info)
1786 {
1787         vfolder_info_free_internals (info);
1788         g_free (info);
1789 }
1790
1791 static Query *
1792 single_query_read (xmlNode *qnode)
1793 {
1794         Query *query;
1795         xmlNode *node;
1796
1797         if (qnode->type != XML_ELEMENT_NODE ||
1798             qnode->name == NULL)
1799                 return NULL;
1800
1801         query = NULL;
1802
1803         if (g_ascii_strcasecmp (qnode->name, "Not") == 0 &&
1804             qnode->xmlChildrenNode != NULL) {
1805                 xmlNode *iter;
1806                 query = NULL;
1807                 for (iter = qnode->xmlChildrenNode;
1808                      iter != NULL && query == NULL;
1809                      iter = iter->next)
1810                         query = single_query_read (iter);
1811                 if (query != NULL) {
1812                         query->not = ! query->not;
1813                 }
1814                 return query;
1815         } else if (g_ascii_strcasecmp (qnode->name, "Keyword") == 0) {
1816                 xmlChar *word = xmlNodeGetContent (qnode);
1817                 if (word != NULL) {
1818                         query = query_new (QUERY_KEYWORD);
1819                         ((QueryKeyword *)query)->keyword =
1820                                 g_quark_from_string (word);
1821
1822                         xmlFree (word);
1823                 }
1824                 return query;
1825         } else if (g_ascii_strcasecmp (qnode->name, "Filename") == 0) {
1826                 xmlChar *file = xmlNodeGetContent (qnode);
1827                 if (file != NULL) {
1828                         query = query_new (QUERY_FILENAME);
1829                         ((QueryFilename *)query)->filename =
1830                                 g_strdup (file);
1831
1832                         xmlFree (file);
1833                 }
1834                 return query;
1835         } else if (g_ascii_strcasecmp (qnode->name, "And") == 0) {
1836                 query = query_new (QUERY_AND);
1837         } else if (g_ascii_strcasecmp (qnode->name, "Or") == 0) {
1838                 query = query_new (QUERY_OR);
1839         } else {
1840                 /* We don't understand */
1841                 return NULL;
1842         }
1843
1844         /* This must be OR or AND */
1845         g_assert (query != NULL);
1846
1847         for (node = qnode->xmlChildrenNode; node != NULL; node = node->next) {
1848                 Query *new_query = single_query_read (node);
1849
1850                 if (new_query != NULL)
1851                         query->queries = g_slist_prepend
1852                                 (query->queries, new_query);
1853         }
1854
1855         query->queries = g_slist_reverse (query->queries);
1856
1857         return query;
1858 }
1859
1860 static void
1861 add_or_set_query (Query **query, Query *new_query)
1862 {
1863         if (*query == NULL) {
1864                 *query = new_query;
1865         } else {
1866                 Query *old_query = *query;
1867                 *query = query_new (QUERY_OR);
1868                 (*query)->queries = 
1869                         g_slist_append ((*query)->queries, old_query);
1870                 (*query)->queries = 
1871                         g_slist_append ((*query)->queries, new_query);
1872         }
1873 }
1874
1875 static Query *
1876 query_read (xmlNode *qnode)
1877 {
1878         Query *query;
1879         xmlNode *node;
1880
1881         query = NULL;
1882
1883         for (node = qnode->xmlChildrenNode; node != NULL; node = node->next) {
1884                 if (node->type != XML_ELEMENT_NODE ||
1885                     node->name == NULL)
1886                         continue;
1887
1888                 if (g_ascii_strcasecmp (node->name, "Not") == 0 &&
1889                     node->xmlChildrenNode != NULL) {
1890                         xmlNode *iter;
1891                         Query *new_query = NULL;
1892
1893                         for (iter = node->xmlChildrenNode;
1894                              iter != NULL && new_query == NULL;
1895                              iter = iter->next)
1896                                 new_query = single_query_read (iter);
1897                         if (new_query != NULL) {
1898                                 new_query->not = ! new_query->not;
1899                                 add_or_set_query (&query, new_query);
1900                         }
1901                 } else {
1902                         Query *new_query = single_query_read (node);
1903                         if (new_query != NULL)
1904                                 add_or_set_query (&query, new_query);
1905                 }
1906         }
1907
1908         return query;
1909 }
1910
1911 static Folder *
1912 folder_read (VFolderInfo *info, xmlNode *fnode)
1913 {
1914         Folder *folder;
1915         xmlNode *node;
1916
1917         folder = folder_new (NULL);
1918
1919         for (node = fnode->xmlChildrenNode; node != NULL; node = node->next) {
1920                 if (node->type != XML_ELEMENT_NODE ||
1921                     node->name == NULL)
1922                         continue;
1923
1924                 if (g_ascii_strcasecmp (node->name, "Name") == 0) {
1925                         xmlChar *name = xmlNodeGetContent (node);
1926                         if (name != NULL) {
1927                                 g_free (folder->entry.name);
1928                                 folder->entry.name = g_strdup (name);
1929                                 xmlFree (name);
1930                         }
1931                 } else if (g_ascii_strcasecmp (node->name, "Desktop") == 0) {
1932                         xmlChar *desktop = xmlNodeGetContent (node);
1933                         if (desktop != NULL) {
1934                                 g_free (folder->desktop_file);
1935                                 folder->desktop_file = g_strdup (desktop);
1936                                 xmlFree (desktop);
1937                         }
1938                 } else if (g_ascii_strcasecmp (node->name, "Include") == 0) {
1939                         xmlChar *file = xmlNodeGetContent (node);
1940                         if (file != NULL) {
1941                                 GSList *li;
1942                                 char *str = g_strdup (file);
1943                                 folder->includes = g_slist_prepend
1944                                         (folder->includes, str);
1945                                 if (folder->includes_ht == NULL) {
1946                                         folder->includes_ht =
1947                                                 g_hash_table_new_full
1948                                                 (g_str_hash,
1949                                                  g_str_equal,
1950                                                  NULL,
1951                                                  NULL);
1952                                 }
1953                                 li = g_hash_table_lookup (folder->includes_ht,
1954                                                           file);
1955                                 if (li != NULL) {
1956                                         g_free (li->data);
1957                                         /* Note: this will NOT change folder->includes
1958                                          * pointer! */
1959                                         folder->includes = g_slist_delete_link
1960                                                 (folder->includes, li);
1961                                 }
1962                                 g_hash_table_replace (folder->includes_ht, 
1963                                                       file, folder->includes);
1964                                 xmlFree (file);
1965                         }
1966                 } else if (g_ascii_strcasecmp (node->name, "Exclude") == 0) {
1967                         xmlChar *file = xmlNodeGetContent (node);
1968                         if (file != NULL) {
1969                                 char *s;
1970                                 if (folder->excludes == NULL) {
1971                                         folder->excludes = g_hash_table_new_full
1972                                                 (g_str_hash,
1973                                                  g_str_equal,
1974                                                  (GDestroyNotify)g_free,
1975                                                  NULL);
1976                                 }
1977                                 s = g_strdup (file);
1978                                 g_hash_table_replace (folder->excludes, s, s);
1979                                 xmlFree (file);
1980                         }
1981                 } else if (g_ascii_strcasecmp (node->name, "Query") == 0) {
1982                         Query *query;
1983
1984                         query = query_read (node);
1985
1986                         if (query != NULL) {
1987                                 if (folder->query != NULL)
1988                                         query_destroy (folder->query);
1989                                 folder->query = query;
1990                         }
1991                 } else if (g_ascii_strcasecmp (node->name, "OnlyUnallocated") == 0) {
1992                         info->unallocated_folders = 
1993                                 g_slist_prepend (info->unallocated_folders,
1994                                                  (Folder *)entry_ref ((Entry *)folder));
1995                         folder->only_unallocated = TRUE;
1996                 } else if (g_ascii_strcasecmp (node->name, "Folder") == 0) {
1997                         Folder *new_folder = folder_read (info, node);
1998                         if (new_folder != NULL) {
1999                                 folder->subfolders =
2000                                         g_slist_append (folder->subfolders,
2001                                                         new_folder);
2002                                 new_folder->parent = folder;
2003                         }
2004                 } else if (g_ascii_strcasecmp (node->name, "ReadOnly") == 0) {
2005                         folder->read_only = TRUE;
2006                 } else if (g_ascii_strcasecmp (node->name,
2007                                                "DontShowIfEmpty") == 0) {
2008                         folder->dont_show_if_empty = TRUE;
2009                 }
2010         }
2011
2012         /* Name is required */
2013         if (folder->entry.name == NULL) {
2014                 entry_unref ((Entry *)folder);
2015                 folder = NULL;
2016         }
2017
2018         folder->includes = g_slist_reverse (folder->includes);
2019
2020         return folder;
2021 }
2022
2023 static char *
2024 subst_home (const char *dir)
2025 {
2026         if (dir[0] == '~')
2027                 return g_strconcat (g_get_home_dir (),
2028                                     &dir[1],
2029                                     NULL);
2030         else    
2031                 return g_strdup (dir);
2032 }
2033
2034 /* FORMAT looks like:
2035  * <VFolderInfo>
2036  *   <!-- Merge dirs optional -->
2037  *   <MergeDir>/etc/X11/applnk</MergeDir>
2038  *   <!-- Only specify if it should override standard location -->
2039  *   <ItemDir>/usr/share/applications</ItemDir>
2040  *   <!-- This is where the .directories are -->
2041  *   <DesktopDir>/etc/X11/gnome/vfolders</DesktopDir>
2042  *   <!-- Root folder -->
2043  *   <Folder>
2044  *     <Name>Root</Name>
2045  *
2046  *     <Include>important.desktop</Include>
2047  *
2048  *     <!-- Other folders -->
2049  *     <Folder>
2050  *       <Name>SomeFolder</Name>
2051  *     </Folder>
2052  *     <Folder>
2053  *       <Name>Test_Folder</Name>
2054  *       <!-- could also be absolute -->
2055  *       <Desktop>Test_Folder.directory</Desktop>
2056  *       <Query>
2057  *         <Or>
2058  *           <And>
2059  *             <Keyword>Application</Keyword>
2060  *             <Keyword>Game</Keyword>
2061  *           </And>
2062  *           <Keyword>Clock</Keyword>
2063  *         </Or>
2064  *       </Query>
2065  *       <Include>somefile.desktop</Include>
2066  *       <Include>someotherfile.desktop</Include>
2067  *       <Exclude>yetanother.desktop</Exclude>
2068  *     </Folder>
2069  *   </Folder>
2070  * </VFolderInfo>
2071  */
2072
2073 static gboolean
2074 vfolder_info_read_info (VFolderInfo *info,
2075                         GnomeVFSResult *result,
2076                         GnomeVFSContext *context)
2077 {
2078         xmlDoc *doc;
2079         xmlNode *node;
2080         gboolean got_a_vfolder_dir = FALSE;
2081
2082         doc = NULL;
2083         if (info->user_filename != NULL &&
2084             access (info->user_filename, F_OK) == 0) {
2085                 doc = xmlParseFile (info->user_filename); 
2086                 if (doc != NULL)
2087                         info->user_file_active = TRUE;
2088         }
2089         if (doc == NULL &&
2090             access (info->filename, F_OK) == 0)
2091                 doc = xmlParseFile (info->filename); 
2092
2093         if (gnome_vfs_context_check_cancellation (context)) {
2094                 xmlFreeDoc(doc);
2095                 *result = GNOME_VFS_ERROR_CANCELLED;
2096                 return FALSE;
2097         }
2098
2099         if (doc == NULL
2100             || doc->xmlRootNode == NULL
2101             || doc->xmlRootNode->name == NULL
2102             || g_ascii_strcasecmp (doc->xmlRootNode->name, "VFolderInfo") != 0) {
2103                 xmlFreeDoc(doc);
2104                 return TRUE; /* FIXME: really, shouldn't we error out? */
2105         }
2106
2107         for (node = doc->xmlRootNode->xmlChildrenNode; node != NULL; node = node->next) {
2108                 if (node->type != XML_ELEMENT_NODE ||
2109                     node->name == NULL)
2110                         continue;
2111
2112                 if (gnome_vfs_context_check_cancellation (context)) {
2113                         xmlFreeDoc(doc);
2114                         *result = GNOME_VFS_ERROR_CANCELLED;
2115                         return FALSE;
2116                 }
2117
2118                 if (g_ascii_strcasecmp (node->name, "MergeDir") == 0) {
2119                         xmlChar *dir = xmlNodeGetContent (node);
2120                         if (dir != NULL) {
2121                                 info->merge_dirs = g_slist_append (info->merge_dirs,
2122                                                                    g_strdup (dir));
2123                                 xmlFree (dir);
2124                         }
2125                 } else if (g_ascii_strcasecmp (node->name, "ItemDir") == 0) {
2126                         xmlChar *dir = xmlNodeGetContent (node);
2127                         if (dir != NULL) {
2128                                 if ( ! got_a_vfolder_dir) {
2129                                         g_slist_foreach (info->item_dirs,
2130                                                          (GFunc)g_free, NULL);
2131                                         g_slist_free (info->item_dirs);
2132                                         info->item_dirs = NULL;
2133                                 }
2134                                 got_a_vfolder_dir = TRUE;
2135                                 info->item_dirs = g_slist_append (info->item_dirs,
2136                                                                   g_strdup (dir));
2137                                 xmlFree (dir);
2138                         }
2139                 } else if (g_ascii_strcasecmp (node->name, "UserItemDir") == 0) {
2140                         xmlChar *dir = xmlNodeGetContent (node);
2141                         if (dir != NULL) {
2142                                 g_free (info->user_item_dir);
2143                                 info->user_item_dir = subst_home (dir);
2144                                 xmlFree (dir);
2145                         }
2146                 } else if (g_ascii_strcasecmp (node->name, "DesktopDir") == 0) {
2147                         xmlChar *dir = xmlNodeGetContent (node);
2148                         if (dir != NULL) {
2149                                 g_free (info->desktop_dir);
2150                                 info->desktop_dir = g_strdup (dir);
2151                                 xmlFree (dir);
2152                         }
2153                 } else if (g_ascii_strcasecmp (node->name, "UserDesktopDir") == 0) {
2154                         xmlChar *dir = xmlNodeGetContent (node);
2155                         if (dir != NULL) {
2156                                 g_free (info->user_desktop_dir);
2157                                 info->user_desktop_dir = subst_home (dir);
2158                                 xmlFree (dir);
2159                         }
2160                 } else if (g_ascii_strcasecmp (node->name, "Folder") == 0) {
2161                         Folder *folder = folder_read (info, node);
2162                         if (folder != NULL) {
2163                                 if (info->root != NULL)
2164                                         entry_unref ((Entry *)info->root);
2165                                 info->root = folder;
2166                         }
2167                 } else if (g_ascii_strcasecmp (node->name, "ReadOnly") == 0) {
2168                         info->read_only = TRUE;
2169                 }
2170         }
2171
2172         xmlFreeDoc(doc);
2173
2174         return TRUE;
2175 }
2176
2177 static void
2178 add_xml_tree_from_query (xmlNode *parent, Query *query)
2179 {
2180         xmlNode *real_parent;
2181
2182         if (query->not)
2183                 real_parent = xmlNewChild (parent /* parent */,
2184                                            NULL /* ns */,
2185                                            "Not" /* name */,
2186                                            NULL /* content */);
2187         else
2188                 real_parent = parent;
2189
2190         if (query->type == QUERY_KEYWORD) {
2191                 QueryKeyword *qkeyword = (QueryKeyword *)query;
2192                 const char *string = g_quark_to_string (qkeyword->keyword);
2193
2194                 xmlNewChild (real_parent /* parent */,
2195                              NULL /* ns */,
2196                              "Keyword" /* name */,
2197                              string /* content */);
2198         } else if (query->type == QUERY_FILENAME) {
2199                 QueryFilename *qfilename = (QueryFilename *)query;
2200
2201                 xmlNewChild (real_parent /* parent */,
2202                              NULL /* ns */,
2203                              "Filename" /* name */,
2204                              qfilename->filename /* content */);
2205         } else if (query->type == QUERY_OR ||
2206                    query->type == QUERY_AND) {
2207                 xmlNode *node;
2208                 const char *name;
2209                 GSList *li;
2210
2211                 if (query->type == QUERY_OR)
2212                         name = "Or";
2213                 else /* QUERY_AND */
2214                         name = "And";
2215
2216                 node = xmlNewChild (real_parent /* parent */,
2217                                     NULL /* ns */,
2218                                     name /* name */,
2219                                     NULL /* content */);
2220
2221                 for (li = query->queries; li != NULL; li = li->next) {
2222                         Query *subquery = li->data;
2223                         add_xml_tree_from_query (node, subquery);
2224                 }
2225         } else {
2226                 g_assert_not_reached ();
2227         }
2228 }
2229
2230 static void
2231 add_excludes_to_xml (gpointer key, gpointer value, gpointer user_data)
2232 {
2233         const char *filename = key;
2234         xmlNode *folder_node = user_data;
2235
2236         xmlNewChild (folder_node /* parent */,
2237                      NULL /* ns */,
2238                      "Exclude" /* name */,
2239                      filename /* content */);
2240 }
2241
2242 static void
2243 add_xml_tree_from_folder (xmlNode *parent, Folder *folder)
2244 {
2245         GSList *li;
2246         xmlNode *folder_node;
2247
2248
2249         folder_node = xmlNewChild (parent /* parent */,
2250                                    NULL /* ns */,
2251                                    "Folder" /* name */,
2252                                    NULL /* content */);
2253
2254         xmlNewChild (folder_node /* parent */,
2255                      NULL /* ns */,
2256                      "Name" /* name */,
2257                      folder->entry.name /* content */);
2258
2259         if (folder->desktop_file != NULL) {
2260                 xmlNewChild (folder_node /* parent */,
2261                              NULL /* ns */,
2262                              "Desktop" /* name */,
2263                              folder->desktop_file /* content */);
2264         }
2265
2266         if (folder->read_only)
2267                 xmlNewChild (folder_node /* parent */,
2268                              NULL /* ns */,
2269                              "ReadOnly" /* name */,
2270                              NULL /* content */);
2271         if (folder->dont_show_if_empty)
2272                 xmlNewChild (folder_node /* parent */,
2273                              NULL /* ns */,
2274                              "DontShowIfEmpty" /* name */,
2275                              NULL /* content */);
2276         if (folder->only_unallocated)
2277                 xmlNewChild (folder_node /* parent */,
2278                              NULL /* ns */,
2279                              "OnlyUnallocated" /* name */,
2280                              NULL /* content */);
2281
2282         for (li = folder->subfolders; li != NULL; li = li->next) {
2283                 Folder *subfolder = li->data;
2284                 add_xml_tree_from_folder (folder_node, subfolder);
2285         }
2286
2287         for (li = folder->includes; li != NULL; li = li->next) {
2288                 const char *include = li->data;
2289                 xmlNewChild (folder_node /* parent */,
2290                              NULL /* ns */,
2291                              "Include" /* name */,
2292                              include /* content */);
2293         }
2294
2295         if (folder->excludes) {
2296                 g_hash_table_foreach (folder->excludes,
2297                                       add_excludes_to_xml,
2298                                       folder_node);
2299         }
2300
2301         if (folder->query != NULL) {
2302                 xmlNode *query_node;
2303                 query_node = xmlNewChild (folder_node /* parent */,
2304                                           NULL /* ns */,
2305                                           "Query" /* name */,
2306                                           NULL /* content */);
2307
2308                 add_xml_tree_from_query (query_node, folder->query);
2309         }
2310 }
2311
2312 static xmlDoc *
2313 xml_tree_from_vfolder (VFolderInfo *info)
2314 {
2315         xmlDoc *doc;
2316         xmlNode *topnode;
2317         GSList *li;
2318
2319         doc = xmlNewDoc ("1.0");
2320
2321         topnode = xmlNewDocNode (doc /* doc */,
2322                                  NULL /* ns */,
2323                                  "VFolderInfo" /* name */,
2324                                  NULL /* content */);
2325         doc->xmlRootNode = topnode;
2326
2327         for (li = info->merge_dirs; li != NULL; li = li->next) {
2328                 const char *merge_dir = li->data;
2329                 xmlNewChild (topnode /* parent */,
2330                              NULL /* ns */,
2331                              "MergeDir" /* name */,
2332                              merge_dir /* content */);
2333         }
2334         
2335         for (li = info->item_dirs; li != NULL; li = li->next) {
2336                 const char *item_dir = li->data;
2337                 xmlNewChild (topnode /* parent */,
2338                              NULL /* ns */,
2339                              "ItemDir" /* name */,
2340                              item_dir /* content */);
2341         }
2342
2343         if (info->user_item_dir != NULL) {
2344                 xmlNewChild (topnode /* parent */,
2345                              NULL /* ns */,
2346                              "UserItemDir" /* name */,
2347                              info->user_item_dir /* content */);
2348         }
2349
2350         if (info->desktop_dir != NULL) {
2351                 xmlNewChild (topnode /* parent */,
2352                              NULL /* ns */,
2353                              "DesktopDir" /* name */,
2354                              info->desktop_dir /* content */);
2355         }
2356
2357         if (info->user_desktop_dir != NULL) {
2358                 xmlNewChild (topnode /* parent */,
2359                              NULL /* ns */,
2360                              "UserDesktopDir" /* name */,
2361                              info->user_desktop_dir /* content */);
2362         }
2363
2364         if (info->root != NULL)
2365                 add_xml_tree_from_folder (topnode, info->root);
2366
2367         return doc;
2368 }
2369
2370 /* FIXME: what to do about errors */
2371 static void
2372 vfolder_info_write_user (VFolderInfo *info)
2373 {
2374         xmlDoc *doc;
2375
2376         if (info->inhibit_write > 0)
2377                 return;
2378
2379         if (info->user_filename == NULL)
2380                 return;
2381
2382         doc = xml_tree_from_vfolder (info);
2383         if (doc == NULL)
2384                 return;
2385
2386         /* FIXME: errors, anyone? */
2387         ensure_dir (info->user_filename,
2388                     TRUE /* ignore_basename */);
2389
2390         xmlSaveFormatFile (info->user_filename, doc, TRUE /* format */);
2391         /* not as good as a stat, but cheaper ... hmmm what is
2392          * the likelyhood of this not being the same as ctime */
2393         info->user_filename_last_write = time (NULL);
2394
2395         xmlFreeDoc(doc);
2396
2397         info->user_file_active = TRUE;
2398         info->dirty = FALSE;
2399
2400         info->modification_time = time (NULL);
2401 }
2402
2403 /* An EVIL function for quick reading of .desktop files,
2404  * only reads in one or two keys, but that's ALL we need */
2405 static void
2406 readitem_entry (const char *filename,
2407                 const char *key1,
2408                 char **result1,
2409                 const char *key2,
2410                 char **result2)
2411 {
2412         FILE *fp;
2413         char buf[1024];
2414         int keylen1, keylen2;
2415
2416         *result1 = NULL;
2417         if (result2 != NULL)
2418                 *result2 = NULL;
2419
2420         fp = fopen (filename, "r");
2421
2422         if (fp == NULL)
2423                 return;
2424
2425         keylen1 = strlen (key1);
2426         if (key2 != NULL)
2427                 keylen2 = strlen (key2);
2428         else
2429                 keylen2 = -1;
2430
2431         /* This is slightly wrong, it should only look
2432          * at the correct section */
2433         while (fgets (buf, sizeof (buf), fp) != NULL) {
2434                 char *p;
2435                 int len;
2436                 int keylen;
2437                 char **result = NULL;
2438
2439                 /* check if it's one of the keys */
2440                 if (strncmp (buf, key1, keylen1) == 0) {
2441                         result = result1;
2442                         keylen = keylen1;
2443                 } else if (keylen2 >= 0 &&
2444                            strncmp (buf, key2, keylen2) == 0) {
2445                         result = result2;
2446                         keylen = keylen2;
2447                 } else {
2448                         continue;
2449                 }
2450
2451                 p = &buf[keylen];
2452
2453                 /* still not our key */
2454                 if (!(*p == '=' || *p == ' ')) {
2455                         continue;
2456                 }
2457                 do
2458                         p++;
2459                 while (*p == ' ' || *p == '=');
2460
2461                 /* get rid of trailing \n */
2462                 len = strlen (p);
2463                 if (p[len-1] == '\n' ||
2464                     p[len-1] == '\r')
2465                         p[len-1] = '\0';
2466
2467                 *result = g_strdup (p);
2468
2469                 if (*result1 != NULL &&
2470                     (result2 == NULL || *result2 != NULL))
2471                         break;
2472         }
2473
2474         fclose (fp);
2475 }
2476
2477 static void
2478 vfolder_info_insert_entry (VFolderInfo *info, EntryFile *efile)
2479 {
2480         GSList *entry_list;
2481
2482         entry_ref ((Entry *)efile);
2483
2484         entry_list = g_hash_table_lookup (info->entries_ht, efile->entry.name);
2485
2486         info->entries = g_slist_prepend (info->entries, efile);
2487         /* The hash table contains the GSList pointer */
2488         g_hash_table_replace (info->entries_ht, efile->entry.name, 
2489                              info->entries);
2490
2491         if (entry_list != NULL) {
2492                 Entry *entry = entry_list->data;
2493                 info->entries = g_slist_delete_link (info->entries, 
2494                                                      entry_list);
2495                 entry_unref (entry);
2496         }
2497 }
2498
2499 static void
2500 set_keywords (EntryFile *efile, const char *keywords)
2501 {
2502         if (keywords != NULL) {
2503                 int i;
2504                 char **parsed = g_strsplit (keywords, ";", -1);
2505                 for (i = 0; parsed[i] != NULL; i++) {
2506                         GQuark quark;
2507                         const char *word = parsed[i];
2508                         /* ignore empties (including end of list) */
2509                         if (word[0] == '\0')
2510                                 continue;
2511                         quark = g_quark_from_string (word);
2512                         efile->keywords = g_slist_prepend
2513                                 (efile->keywords,
2514                                  GINT_TO_POINTER (quark));
2515                 }
2516                 g_strfreev (parsed);
2517         }
2518 }
2519
2520 static EntryFile *
2521 make_entry_file (const char *dir, const char *name)
2522 {
2523         EntryFile *efile;
2524         char *categories;
2525         char *only_show_in;
2526         char *filename;
2527         int i;
2528
2529         filename = g_build_filename (dir, name, NULL);
2530
2531         readitem_entry (filename,
2532                         "Categories",
2533                         &categories,
2534                         "OnlyShowIn",
2535                         &only_show_in);
2536
2537         if (only_show_in != NULL) {
2538                 gboolean show = FALSE;
2539                 char **parsed = g_strsplit (only_show_in, ";", -1);
2540                 for (i = 0; parsed[i] != NULL; i++) {
2541                         if (strcmp (parsed[i], "GNOME") == 0) {
2542                                 show = TRUE;
2543                                 break;
2544                         }
2545                 }
2546                 g_strfreev (parsed);
2547                 if ( ! show) {
2548                         g_free (filename);
2549                         g_free (only_show_in);
2550                         g_free (categories);
2551                         return NULL;
2552                 }
2553         }
2554
2555         efile = file_new (name);
2556         efile->filename = filename;
2557
2558         set_keywords (efile, categories);
2559
2560         g_free (only_show_in);
2561         g_free (categories);
2562
2563         return efile;
2564 }
2565
2566 static gboolean
2567 vfolder_info_read_items_from (VFolderInfo *info,
2568                               const char *item_dir,
2569                               gboolean per_user,
2570                               GnomeVFSResult *result,
2571                               GnomeVFSContext *context)
2572 {
2573         DIR *dir;
2574         struct dirent *de;
2575
2576         dir = opendir (item_dir);
2577         if (dir == NULL)
2578                 return TRUE;
2579
2580         while ((de = readdir (dir)) != NULL) {
2581                 EntryFile *efile;
2582
2583                 if (gnome_vfs_context_check_cancellation (context)) {
2584                         closedir (dir);
2585                         *result = GNOME_VFS_ERROR_CANCELLED;
2586                         return FALSE;
2587                 }
2588
2589                 /* files MUST be called .desktop */
2590                 if (de->d_name[0] == '.' ||
2591                     ! check_ext (de->d_name, ".desktop"))
2592                         continue;
2593
2594                 efile = make_entry_file (item_dir, de->d_name);
2595                 if (efile == NULL)
2596                         continue;
2597
2598                 efile->per_user = per_user;
2599
2600                 vfolder_info_insert_entry (info, efile);
2601                 entry_unref ((Entry *)efile);
2602         }
2603
2604         closedir (dir);
2605
2606         return TRUE;
2607 }
2608
2609 static gboolean
2610 vfolder_info_read_items_merge (VFolderInfo *info,
2611                                const char *merge_dir,
2612                                const char *subdir,
2613                                GQuark inherited_keyword,
2614                                GnomeVFSResult *result,
2615                                GnomeVFSContext *context)
2616 {
2617         DIR *dir;
2618         struct dirent *de;
2619         GQuark extra_keyword;
2620         GQuark Application;
2621         GQuark Merged;
2622         GQuark inheritance;
2623         gboolean pass_down_extra_keyword = TRUE;
2624
2625         dir = opendir (merge_dir);
2626         if (dir == NULL)
2627                 return TRUE;
2628
2629         Application = g_quark_from_static_string ("Application");
2630         Merged = g_quark_from_static_string ("Merged");
2631
2632         /* FIXME: this should be a hash or something */
2633         extra_keyword = 0;
2634         if (subdir == NULL) {
2635                 extra_keyword = g_quark_from_static_string ("Core");
2636                 pass_down_extra_keyword = FALSE;
2637         } else if (g_ascii_strcasecmp (subdir, "Development") == 0)
2638                 extra_keyword = g_quark_from_static_string ("Development");
2639         else if (g_ascii_strcasecmp (subdir, "Editors") == 0)
2640                 extra_keyword = g_quark_from_static_string ("TextEditor");
2641         else if (g_ascii_strcasecmp (subdir, "Games") == 0)
2642                 extra_keyword = g_quark_from_static_string ("Game");
2643         else if (g_ascii_strcasecmp (subdir, "Graphics") == 0)
2644                 extra_keyword = g_quark_from_static_string ("Graphics");
2645         else if (g_ascii_strcasecmp (subdir, "Internet") == 0)
2646                 extra_keyword = g_quark_from_static_string ("Network");
2647         else if (g_ascii_strcasecmp (subdir, "Multimedia") == 0)
2648                 extra_keyword = g_quark_from_static_string ("AudioVideo");
2649         else if (g_ascii_strcasecmp (subdir, "Office") == 0)
2650                 extra_keyword = g_quark_from_static_string ("Office");
2651         else if (g_ascii_strcasecmp (subdir, "Settings") == 0)
2652                 extra_keyword = g_quark_from_static_string ("Settings");
2653         else if (g_ascii_strcasecmp (subdir, "System") == 0)
2654                 extra_keyword = g_quark_from_static_string ("System");
2655         else if (g_ascii_strcasecmp (subdir, "Utilities") == 0)
2656                 extra_keyword = g_quark_from_static_string ("Utility");
2657
2658         while ((de = readdir (dir)) != NULL) {
2659                 EntryFile *efile;
2660
2661                 if (gnome_vfs_context_check_cancellation (context)) {
2662                         closedir (dir);
2663                         *result = GNOME_VFS_ERROR_CANCELLED;
2664                         return FALSE;
2665                 }
2666
2667                 /* ignore hidden */
2668                 if (de->d_name[0] == '.')
2669                         continue;
2670
2671                 /* files MUST be called .desktop, so
2672                  * treat all others as dirs.  If we're wrong,
2673                  * the open will fail, which is ok */
2674                 if ( ! check_ext (de->d_name, ".desktop")) {
2675                         /* if this is a directory recurse */
2676                         char *fullname = g_build_filename (merge_dir, de->d_name, NULL);
2677                         if ((pass_down_extra_keyword == TRUE) && (extra_keyword != 0)) {
2678                                 inheritance = extra_keyword;
2679                         } else {
2680                                 inheritance = inherited_keyword;
2681                         }
2682
2683                         if ( ! vfolder_info_read_items_merge (info,
2684                                                               fullname,
2685                                                               de->d_name,
2686                                                               inheritance,
2687                                                               result,
2688                                                               context)) {
2689                                 g_free (fullname);
2690                                 return FALSE;
2691                         }
2692                         g_free (fullname);
2693                         continue;
2694                 }
2695
2696                 /* FIXME: add some keywords about some known apps
2697                  * like gimp and whatnot, perhaps take these from the vfolder
2698                  * file or some such */
2699
2700                 efile = make_entry_file (merge_dir, de->d_name);
2701                 if (efile == NULL)
2702                         continue;
2703
2704                 /* If no keywords set, then add the standard ones */
2705                 if (efile->keywords == NULL) {
2706                         efile->keywords = g_slist_prepend
2707                                 (efile->keywords,
2708                                  GINT_TO_POINTER (Application));
2709
2710                         efile->keywords = g_slist_prepend
2711                                 (efile->keywords,
2712                                  GINT_TO_POINTER (Merged));
2713
2714                         if (inherited_keyword != 0) {
2715                                 efile->keywords = g_slist_prepend
2716                                         (efile->keywords,
2717                                          GINT_TO_POINTER (inherited_keyword));
2718                         }
2719                         
2720                         if (extra_keyword != 0) {
2721                                 efile->keywords = g_slist_prepend
2722                                         (efile->keywords,
2723                                          GINT_TO_POINTER (extra_keyword));
2724                         }
2725                         efile->implicit_keywords = TRUE;
2726                 }
2727
2728                 vfolder_info_insert_entry (info, efile);
2729                 entry_unref ((Entry *)efile);
2730         }
2731
2732         closedir (dir);
2733
2734         return TRUE;
2735 }
2736
2737 static Entry *
2738 find_entry (GSList *list, const char *name)
2739 {
2740         GSList *li;
2741
2742         for (li = list; li != NULL; li = li->next) {
2743                 Entry *entry = li->data;
2744                 if (strcmp (name, entry->name) == 0)
2745                         return entry;
2746         }
2747         return NULL;
2748 }
2749
2750 static void
2751 file_monitor (GnomeVFSMonitorHandle *handle,
2752               const gchar *monitor_uri,
2753               const gchar *info_uri,
2754               GnomeVFSMonitorEventType event_type,
2755               gpointer user_data)
2756 {
2757         FileMonitorHandle *h = user_data;
2758
2759         /* proxy the event through if it is a changed event
2760          * only */
2761
2762         if (event_type == GNOME_VFS_MONITOR_EVENT_CHANGED &&
2763             h->handle != NULL)
2764                 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *) h,
2765                                             h->uri, event_type);
2766 }
2767
2768 static void
2769 try_free_file_monitors_create_files_unlocked (VFolderInfo *info)
2770 {
2771         GSList *li, *list;
2772
2773         list = g_slist_copy (info->free_file_monitors);
2774
2775         for (li = list; li != NULL; li = li->next) {
2776                 FileMonitorHandle *handle = li->data;
2777                 Entry *entry;
2778                 GnomeVFSResult result;
2779                 char *dirfile = NULL;
2780
2781                 if (handle->is_directory_file) {
2782                         VFolderURI vuri;
2783                         Folder *folder;
2784
2785                         /* Evil! EVIL URI PARSING. this will eat a lot of
2786                          * stack if we have lots of free monitors */
2787
2788                         VFOLDER_URI_PARSE (handle->uri, &vuri);
2789
2790                         folder = resolve_folder (info, 
2791                                                  vuri.path,
2792                                                  TRUE /* ignore_basename */,
2793                                                  &result,
2794                                                  NULL);
2795
2796                         if (folder == NULL)
2797                                 continue;
2798
2799                         dirfile = get_directory_file_unlocked (info, folder);
2800                         if (dirfile == NULL)
2801                                 continue;
2802
2803                         entry = (Entry *)folder;
2804                 } else {
2805                         VFolderURI vuri;
2806                         Folder *f;
2807                         GnomeVFSResult result;
2808
2809                         entry = NULL;
2810
2811                         /* Evil! EVIL URI PARSING. this will eat a lot of
2812                          * stack if we have lots of monitors */
2813
2814                         VFOLDER_URI_PARSE (handle->uri, &vuri);
2815
2816                         f = resolve_folder (info, 
2817                                             vuri.path,
2818                                             TRUE /* ignore_basename */,
2819                                             &result,
2820                                             NULL);
2821
2822                         if (f != NULL) {
2823                                 ensure_folder_unlocked (
2824                                         info, f,
2825                                         FALSE /* subfolders */,
2826                                         NULL /* except */,
2827                                         FALSE /* ignore_unallocated */);
2828                                 entry = find_entry (f->entries, vuri.file);
2829                         }
2830
2831                         if (entry == NULL)
2832                                 continue;
2833                 }
2834
2835                 info->free_file_monitors =
2836                         g_slist_remove (info->free_file_monitors, handle);
2837                 entry->monitors =
2838                         g_slist_prepend (entry->monitors, handle);
2839
2840                 handle->exists = TRUE;
2841                 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *)handle,
2842                                             handle->uri, 
2843                                             GNOME_VFS_MONITOR_EVENT_CREATED);
2844
2845                 /* recreate a handle */
2846                 if (handle->handle == NULL &&
2847                     entry->type == ENTRY_FILE) {
2848                         EntryFile *efile = (EntryFile *)entry;
2849                         char *uri = gnome_vfs_get_uri_from_local_path
2850                                 (efile->filename);
2851
2852                         gnome_vfs_monitor_add (&(handle->handle),
2853                                                uri,
2854                                                GNOME_VFS_MONITOR_FILE,
2855                                                file_monitor,
2856                                                handle);
2857
2858                         g_free (uri);
2859                 } else if (handle->handle == NULL &&
2860                            dirfile != NULL) {
2861                         char *uri = gnome_vfs_get_uri_from_local_path (dirfile);
2862
2863                         gnome_vfs_monitor_add (&(handle->handle),
2864                                                uri,
2865                                                GNOME_VFS_MONITOR_FILE,
2866                                                file_monitor,
2867                                                handle);
2868
2869                         g_free (uri);
2870                 }
2871
2872                 g_free (dirfile);
2873         }
2874
2875         g_slist_free (list);
2876 }
2877
2878 static void /* unlocked */
2879 rescan_monitors (VFolderInfo *info)
2880 {
2881         GSList *li;
2882
2883         if (info->file_monitors == NULL)
2884                 return;
2885
2886         for (li = info->file_monitors; li != NULL; li = li->next) {
2887                 FileMonitorHandle *h = li->data;
2888                 GnomeVFSResult result;
2889                 Entry *entry;
2890                 char *dirfile = NULL;
2891
2892                 /* these are handled below */
2893                 if ( ! h->exists)
2894                         continue;
2895
2896                 if (h->is_directory_file) {
2897                         VFolderURI vuri;
2898                         Folder *folder;
2899
2900                         /* Evil! EVIL URI PARSING. this will eat a lot of
2901                          * stack if we have lots of monitors */
2902
2903                         VFOLDER_URI_PARSE (h->uri, &vuri);
2904
2905                         folder = resolve_folder (info, 
2906                                                  vuri.path,
2907                                                  TRUE /* ignore_basename */,
2908                                                  &result,
2909                                                  NULL);
2910                         if (folder != NULL)
2911                                 dirfile = get_directory_file_unlocked (info,
2912                                                                        folder);
2913
2914                         if (dirfile == NULL) {
2915                                 h->exists = FALSE;
2916                                 gnome_vfs_monitor_callback
2917                                         ((GnomeVFSMethodHandle *)h,
2918                                          h->uri, 
2919                                          GNOME_VFS_MONITOR_EVENT_DELETED);
2920                                 info->free_file_monitors = g_slist_prepend
2921                                         (info->free_file_monitors, h);
2922                                 file_monitor_handle_ref_unlocked (h);
2923                                 /* it has been unreffed when the entry was
2924                                  * whacked */
2925                                 continue;
2926                         }
2927
2928                         entry = (Entry *)folder;
2929                 } else {
2930                         VFolderURI vuri;
2931                         Folder *f;
2932                         GnomeVFSResult result;
2933
2934                         entry = NULL;
2935
2936                         /* Evil! EVIL URI PARSING. this will eat a lot of
2937                          * stack if we have lots of monitors */
2938
2939                         VFOLDER_URI_PARSE (h->uri, &vuri);
2940
2941                         f = resolve_folder (info, 
2942                                             vuri.path,
2943                                             TRUE /* ignore_basename */,
2944                                             &result,
2945                                             NULL);
2946
2947                         if (f != NULL) {
2948                                 ensure_folder_unlocked (
2949                                         info, f,
2950                                         FALSE /* subfolders */,
2951                                         NULL /* except */,
2952                                         FALSE /* ignore_unallocated */);
2953                                 entry = find_entry (f->entries, vuri.file);
2954                         }
2955
2956                         if (entry == NULL) {
2957                                 h->exists = FALSE;
2958                                 gnome_vfs_monitor_callback
2959                                         ((GnomeVFSMethodHandle *)h,
2960                                          h->uri, 
2961                                          GNOME_VFS_MONITOR_EVENT_DELETED);
2962                                 info->free_file_monitors = g_slist_prepend
2963                                         (info->free_file_monitors, h);
2964                                 file_monitor_handle_ref_unlocked (h);
2965                                 /* it has been unreffed when the entry was
2966                                  * whacked */
2967                                 continue;
2968                         }
2969                 }
2970
2971                 /* recreate a handle */
2972                 if (h->handle == NULL &&
2973                     entry->type == ENTRY_FILE) {
2974                         EntryFile *efile = (EntryFile *)entry;
2975                         char *uri = gnome_vfs_get_uri_from_local_path
2976                                 (efile->filename);
2977
2978                         gnome_vfs_monitor_add (&(h->handle),
2979                                                uri,
2980                                                GNOME_VFS_MONITOR_FILE,
2981                                                file_monitor,
2982                                                h);
2983
2984                         g_free (uri);
2985                 } else if (h->handle == NULL &&
2986                            dirfile != NULL) {
2987                         char *uri = gnome_vfs_get_uri_from_local_path (dirfile);
2988
2989                         gnome_vfs_monitor_add (&(h->handle),
2990                                                uri,
2991                                                GNOME_VFS_MONITOR_FILE,
2992                                                file_monitor,
2993                                                h);
2994
2995                         g_free (uri);
2996                 }
2997
2998                 g_free (dirfile);
2999         }
3000
3001         try_free_file_monitors_create_files_unlocked (info);
3002 }
3003
3004 static gboolean /* unlocked */
3005 vfolder_info_read_items (VFolderInfo *info,
3006                          GnomeVFSResult *result,
3007                          GnomeVFSContext *context)
3008 {
3009         GSList *li;
3010
3011         /* First merge */
3012         for (li = info->merge_dirs; li != NULL; li = li->next) {
3013                 const char *merge_dir = li->data;
3014
3015                 if ( ! vfolder_info_read_items_merge (info, merge_dir, NULL, FALSE,
3016                                                       result, context))
3017                         return FALSE;
3018         }
3019
3020         /* Then read the real thing (later overrides) */
3021         for (li = info->item_dirs; li != NULL; li = li->next) {
3022                 const char *item_dir = li->data;
3023
3024                 if ( ! vfolder_info_read_items_from (info, item_dir,
3025                                                      FALSE /* per_user */,
3026                                                      result, context))
3027                         return FALSE;
3028         }
3029
3030         if (info->user_item_dir != NULL) {
3031                 if ( ! vfolder_info_read_items_from (info,
3032                                                      info->user_item_dir,
3033                                                      TRUE /* per_user */,
3034                                                      result, context))
3035                         return FALSE;
3036         }
3037
3038         rescan_monitors (info);
3039
3040         return TRUE;
3041 }
3042
3043 static gboolean
3044 string_slist_equal (GSList *list1, GSList *list2)
3045 {
3046         GSList *li1, *li2;
3047
3048         for (li1 = list1, li2 = list2;
3049              li1 != NULL && li2 != NULL;
3050              li1 = li1->next, li2 = li2->next) {
3051                 const char *s1 = li1->data;
3052                 const char *s2 = li2->data;
3053                 if (strcmp (s1, s2) != 0)
3054                         return FALSE;
3055         }
3056         /* if both are not NULL, then lengths are
3057          * different */
3058         if (li1 != li2)
3059                 return FALSE;
3060         return TRUE;
3061 }
3062
3063 static gboolean
3064 safe_string_same (const char *string1, const char *string2)
3065 {
3066         if (string1 == string2 &&
3067             string1 == NULL)
3068                 return TRUE;
3069
3070         if (string1 != NULL && string2 != NULL &&
3071             strcmp (string1, string2) == 0)
3072                 return TRUE;
3073         
3074         return FALSE;
3075 }
3076
3077 static gboolean
3078 vfolder_info_item_dirs_same (VFolderInfo *info1, VFolderInfo *info2)
3079 {
3080         if ( ! string_slist_equal (info1->item_dirs,
3081                                    info2->item_dirs))
3082                 return FALSE;
3083
3084         if ( ! string_slist_equal (info1->merge_dirs,
3085                                    info2->merge_dirs))
3086                 return FALSE;
3087
3088         if ( ! safe_string_same (info1->user_item_dir,
3089                                  info2->user_item_dir))
3090                 return FALSE;
3091
3092         return TRUE;
3093 }
3094
3095 static gboolean
3096 vfolder_info_reload_unlocked (VFolderInfo *info,
3097                               GnomeVFSResult *result,
3098                               GnomeVFSContext *context,
3099                               gboolean force_read_items)
3100 {
3101         VFolderInfo *newinfo;
3102         gboolean setup_filenames;
3103         gboolean setup_itemdirs;
3104         GSList *li;
3105
3106         /* FIXME: Hmmm, race, there is no locking YAIKES,
3107          * we need filename locking for changes.  eek, eek, eek */
3108         if (info->dirty) {
3109                 return TRUE;
3110         }
3111
3112         newinfo = g_new0 (VFolderInfo, 1);
3113         vfolder_info_init (newinfo, info->scheme);
3114
3115         g_free (newinfo->filename);
3116         g_free (newinfo->user_filename);
3117         newinfo->filename = g_strdup (info->filename);
3118         newinfo->user_filename = g_strdup (info->user_filename);
3119
3120         if (gnome_vfs_context_check_cancellation (context)) {
3121                 vfolder_info_destroy (newinfo);
3122                 *result = GNOME_VFS_ERROR_CANCELLED;
3123                 return FALSE;
3124         }
3125
3126         if ( ! vfolder_info_read_info (newinfo, result, context)) {
3127                 vfolder_info_destroy (newinfo);
3128                 return FALSE;
3129         }
3130
3131         /* FIXME: reload logic for 'desktop_dir' and
3132          * 'user_desktop_dir' */
3133
3134         setup_itemdirs = TRUE;
3135
3136         /* Validity of entries and item dirs and all that is unchanged */
3137         if (vfolder_info_item_dirs_same (info, newinfo)) {
3138                 newinfo->entries = info->entries;
3139                 info->entries = NULL;
3140                 newinfo->entries_ht = info->entries_ht;
3141                 info->entries_ht = NULL /* some places assume this
3142                                            non-null, but we're only
3143                                            going to destroy this */;
3144                 newinfo->entries_valid = info->entries_valid;
3145
3146                 /* move over the monitors/statlocs since those are valid */
3147                 newinfo->item_dir_monitors = info->item_dir_monitors;
3148                 info->item_dir_monitors = NULL;
3149                 newinfo->stat_dirs = info->stat_dirs;
3150                 info->stat_dirs = NULL;
3151
3152                 /* No need to resetup dir monitors */
3153                 setup_itemdirs = FALSE;
3154
3155                 /* No need to do anything with file monitors */
3156         } else {
3157                 /* Whack all monitors here! */
3158                 for (li = info->file_monitors; li != NULL; li = li->next) {
3159                         FileMonitorHandle *h = li->data;
3160                         if (h->handle != NULL)
3161                                 gnome_vfs_monitor_cancel (h->handle);
3162                         h->handle = NULL;
3163                 }
3164         }
3165
3166         setup_filenames = TRUE;
3167
3168         if (safe_string_same (info->filename, newinfo->filename) &&
3169             safe_string_same (info->user_filename, newinfo->user_filename)) {
3170                 newinfo->user_filename_last_write =
3171                         info->user_filename_last_write;
3172
3173                 /* move over the monitors/statlocs since those are valid */
3174                 newinfo->filename_monitor = info->filename_monitor;
3175                 info->filename_monitor = NULL;
3176                 newinfo->user_filename_monitor = info->user_filename_monitor;
3177                 info->user_filename_monitor = NULL;
3178
3179                 if (info->filename_statloc != NULL &&
3180                     info->filename != NULL)
3181                         newinfo->filename_statloc =
3182                                 bake_statloc (info->filename,
3183                                               time (NULL));
3184                 if (info->user_filename_statloc != NULL &&
3185                     info->user_filename != NULL)
3186                         newinfo->user_filename_statloc =
3187                                 bake_statloc (info->user_filename,
3188                                               time (NULL));
3189
3190                 /* No need to resetup filename monitors */
3191                 setup_filenames = FALSE;
3192         }
3193
3194         /* Note: not cancellable anymore, since we've
3195          * already started nibbling on the info structure,
3196          * so we'd need to back things out or some such,
3197          * too complex, so screw that */
3198         monitor_setup (info,
3199                        setup_filenames,
3200                        setup_itemdirs,
3201                        /* FIXME: setup_desktop_dirs */ TRUE,
3202                        NULL, NULL);
3203
3204         for (li = info->folder_monitors;
3205              li != NULL;
3206              li = li->next) {
3207                 FileMonitorHandle *handle = li->data;
3208                 li->data = NULL;
3209
3210                 add_folder_monitor_unlocked (newinfo, handle);
3211
3212                 file_monitor_handle_unref_unlocked (handle);
3213         }
3214         g_slist_free (info->folder_monitors);
3215         info->folder_monitors = NULL;
3216
3217         g_slist_foreach (info->free_folder_monitors,
3218                          (GFunc)file_monitor_handle_unref_unlocked, NULL);
3219         g_slist_free (info->free_folder_monitors);
3220         info->folder_monitors = NULL;
3221
3222         /* we can just copy these for now, they will be readded
3223          * and all the fun stuff will be done with them later */
3224         newinfo->file_monitors = info->file_monitors;
3225         info->file_monitors = NULL;
3226         newinfo->free_file_monitors = info->free_file_monitors;
3227         info->free_file_monitors = NULL;
3228
3229         /* emit changed on all folders, a bit drastic, but oh well,
3230          * we also invalidate all folders at the same time, but that is
3231          * irrelevant since they should all just be invalid to begin with */
3232         invalidate_folder_T (info->root);
3233
3234         /* FIXME: make sure if this was enough, I think it was */
3235
3236         vfolder_info_free_internals_unlocked (info);
3237         memcpy (info, newinfo, sizeof (VFolderInfo));
3238         g_free (newinfo);
3239
3240         /* must rescan the monitors here */
3241         if (info->entries_valid) {
3242                 rescan_monitors (info);
3243         }
3244
3245         if ( ! info->entries_valid &&
3246             force_read_items) {
3247                 GnomeVFSResult res;
3248                 /* FIXME: I bet cancelation plays havoc with monitors,
3249                  * I'm not sure however */
3250                 if (info->file_monitors != NULL) {
3251                         vfolder_info_read_items (info, &res, NULL);
3252                 } else {
3253                         if ( ! vfolder_info_read_items (info, result, context))
3254                                 return FALSE;
3255                 }
3256                 info->entries_valid = TRUE;
3257         }
3258
3259         return TRUE;
3260 }
3261
3262 static gboolean
3263 vfolder_info_reload (VFolderInfo *info,
3264                      GnomeVFSResult *result,
3265                      GnomeVFSContext *context,
3266                      gboolean force_read_items)
3267 {
3268         G_LOCK (vfolder_lock);
3269         if (vfolder_info_reload_unlocked (info, result, context,
3270                                           force_read_items)) {
3271                 G_UNLOCK (vfolder_lock);
3272                 return TRUE;
3273         } else {
3274                 G_UNLOCK (vfolder_lock);
3275                 return FALSE;
3276         }
3277 }
3278
3279 static gboolean
3280 vfolder_info_recheck (VFolderInfo *info,
3281                       GnomeVFSResult *result,
3282                       GnomeVFSContext *context)
3283 {
3284         GSList *li;
3285         time_t curtime = time (NULL);
3286         gboolean reread = FALSE;
3287
3288         if (info->filename_statloc != NULL &&
3289              ! check_statloc (info->filename_statloc, curtime)) {
3290                 if ( ! vfolder_info_reload_unlocked (info, result, context, 
3291                                                      FALSE /* force read items */)) {
3292                         /* we have failed, make sure we fail
3293                          * next time too */
3294                         info->filename_statloc->trigger_next = TRUE;
3295                         return FALSE;
3296                 }
3297                 reread = TRUE;
3298         }
3299         if ( ! reread &&
3300             info->user_filename_statloc != NULL &&
3301              ! check_statloc (info->user_filename_statloc, curtime)) {
3302                 if ( ! vfolder_info_reload_unlocked (info, result, context, 
3303                                                      FALSE /* force read items */)) {
3304                         /* we have failed, make sure we fail
3305                          * next time too */
3306                         info->user_filename_statloc->trigger_next = TRUE;
3307                         return FALSE;
3308                 }
3309                 reread = TRUE;
3310         }
3311
3312         if (info->entries_valid) {
3313                 for (li = info->stat_dirs; li != NULL; li = li->next) {
3314                         StatLoc *sl = li->data;
3315                         if ( ! check_statloc (sl, curtime)) {
3316                                 info->entries_valid = FALSE;
3317                                 break;
3318                         }                      
3319                 }
3320         }
3321         return TRUE;
3322 }
3323
3324 static VFolderInfo *
3325 get_vfolder_info_unlocked (const char      *scheme,
3326                            GnomeVFSResult  *result,
3327                            GnomeVFSContext *context)
3328 {
3329         VFolderInfo *info;
3330
3331         if (infos != NULL &&
3332             (info = g_hash_table_lookup (infos, scheme)) != NULL) {
3333                 if ( ! vfolder_info_recheck (info, result, context)) {
3334                         return NULL;
3335                 }
3336                 if ( ! info->entries_valid) {
3337                         g_slist_foreach (info->entries,
3338                                          (GFunc)entry_unref, NULL);
3339                         g_slist_free (info->entries);
3340                         info->entries = NULL;
3341
3342                         if (info->entries_ht != NULL)
3343                                 g_hash_table_destroy (info->entries_ht);
3344                         info->entries_ht = g_hash_table_new (g_str_hash,
3345                                                              g_str_equal);
3346
3347                         if ( ! vfolder_info_read_items (info,
3348                                                         result, context)) {
3349                                 info->entries_valid = FALSE;
3350                                 return NULL;
3351                         }
3352
3353                         invalidate_folder_T (info->root);
3354
3355                         info->entries_valid = TRUE;
3356
3357                         /* Update modification time of all folders,
3358                          * kind of evil, but it will make adding new items work
3359                          * I hope.  This is because rereading usually means
3360                          * something changed */
3361                         info->modification_time = time (NULL);
3362                 }
3363                 return info;
3364         }
3365
3366         if (gnome_vfs_context_check_cancellation (context)) {
3367                 *result = GNOME_VFS_ERROR_CANCELLED;
3368                 return NULL;
3369         }
3370
3371         if (infos == NULL)
3372                 infos = g_hash_table_new_full
3373                         (g_str_hash, g_str_equal,
3374                          (GDestroyNotify)g_free,
3375                          (GDestroyNotify)vfolder_info_destroy);
3376
3377         info = g_new0 (VFolderInfo, 1);
3378         vfolder_info_init (info, scheme);
3379
3380         if (gnome_vfs_context_check_cancellation (context)) {
3381                 vfolder_info_destroy (info);
3382                 *result = GNOME_VFS_ERROR_CANCELLED;
3383                 return NULL;
3384         }
3385
3386         if ( ! vfolder_info_read_info (info, result, context)) {
3387                 vfolder_info_destroy (info);
3388                 return NULL;
3389         }
3390
3391         if ( ! monitor_setup (info,
3392                               TRUE /* setup_filenames */,
3393                               TRUE /* setup_itemdirs */,
3394                               TRUE /* setup_desktop_dirs */,
3395                               result, context)) {
3396                 vfolder_info_destroy (info);
3397                 return NULL;
3398         }
3399
3400         g_hash_table_insert (infos, g_strdup (scheme), info);
3401
3402         if ( ! vfolder_info_read_items (info, result, context)) {
3403                 info->entries_valid = FALSE;
3404                 return NULL;
3405         }
3406         info->entries_valid = TRUE;
3407
3408         return info;
3409 }
3410
3411 static VFolderInfo *
3412 get_vfolder_info (const char *scheme,
3413                   GnomeVFSResult *result,
3414                   GnomeVFSContext *context)
3415 {
3416         VFolderInfo *info;
3417         G_LOCK (vfolder_lock);
3418         info = get_vfolder_info_unlocked (scheme, result, context);
3419         G_UNLOCK (vfolder_lock);
3420         return info;
3421 }
3422
3423
3424 static char *
3425 keywords_to_string (GSList *keywords)
3426 {
3427         GSList *li;
3428         GString *str = g_string_new (NULL);
3429
3430         for (li = keywords; li != NULL; li = li->next) {
3431                 GQuark word = GPOINTER_TO_INT (li->data);
3432                 g_string_append (str, g_quark_to_string (word));
3433                 g_string_append_c (str, ';');
3434         }
3435
3436         return g_string_free (str, FALSE);
3437 }
3438
3439 /* copy file and add keywords line */
3440 static gboolean
3441 copy_file_with_keywords (const char *from, const char *to, GSList *keywords)
3442 {
3443         FILE *fp;
3444         FILE *wfp;
3445         int wfd;
3446         char buf[BUFSIZ];
3447         char *keyword_string;
3448
3449         if ( ! ensure_dir (to,
3450                            TRUE /* ignore_basename */))
3451                 return FALSE;
3452
3453         wfd = open (to, O_CREAT | O_WRONLY | O_TRUNC, 0600);
3454         if (wfd < 0) {
3455                 return FALSE;
3456         }
3457
3458         keyword_string = keywords_to_string (keywords);
3459
3460         wfp = fdopen (wfd, "w");
3461
3462         fp = fopen (from, "r");
3463         if (fp != NULL) {
3464                 gboolean wrote_keywords = FALSE;
3465                 while (fgets (buf, sizeof (buf), fp) != NULL) {
3466                         fprintf (wfp, "%s", buf);
3467                         if ( ! wrote_keywords &&
3468                             (strncmp (buf, "[Desktop Entry]",
3469                                       strlen ("[Desktop Entry]")) == 0 ||
3470                              strncmp (buf, "[KDE Desktop Entry]",
3471                                       strlen ("[KDE Desktop Entry]")) == 0)) {
3472                                 fprintf (wfp, "Categories=%s\n",
3473                                          keyword_string);
3474                                 wrote_keywords = TRUE;
3475                         }
3476                 }
3477
3478                 fclose (fp);
3479         } else {
3480                 fprintf (wfp, "[Desktop Entry]\nCategories=%s\n",
3481                          keyword_string);
3482         }
3483
3484         /* FIXME: does this close wfd???? */
3485         fclose (wfp);
3486
3487         close (wfd);
3488
3489         g_free (keyword_string);
3490
3491         return TRUE;
3492 }
3493
3494 static gboolean
3495 copy_file (const char *from, const char *to)
3496 {
3497         int fd;
3498         int wfd;
3499
3500         if ( ! ensure_dir (to,
3501                            TRUE /* ignore_basename */))
3502                 return FALSE;
3503
3504         wfd = open (to, O_CREAT | O_WRONLY | O_TRUNC, 0600);
3505         if (wfd < 0) {
3506                 return FALSE;
3507         }
3508
3509         fd = open (from, O_RDONLY);
3510         if (fd >= 0) {
3511                 char buf[1024];
3512                 ssize_t n;
3513
3514                 while ((n = read (fd, buf, sizeof(buf))) > 0) {
3515                         write (wfd, buf, n);
3516                 }
3517
3518                 close (fd);
3519         }
3520
3521         close (wfd);
3522
3523         return TRUE;
3524 }
3525
3526 static gboolean
3527 make_file_private (VFolderInfo *info, EntryFile *efile)
3528 {
3529         char *newfname;
3530         Entry *entry = (Entry *)efile;
3531
3532         if (efile->per_user)
3533                 return TRUE;
3534
3535         /* this file already exists so whack its monitors */
3536         if (efile->filename != NULL) {
3537                 GSList *li;
3538
3539                 for (li = entry->monitors; li != NULL; li = li->next) {
3540                         FileMonitorHandle *h = li->data;
3541                         if (h->handle != NULL)
3542                                 gnome_vfs_monitor_cancel (h->handle);
3543                         h->handle = NULL;
3544                 }
3545         }
3546
3547         newfname = g_build_filename (g_get_home_dir (),
3548                                      DOT_GNOME,
3549                                      "vfolders",
3550                                      info->scheme,
3551                                      efile->entry.name,
3552                                      NULL);
3553
3554         if (efile->implicit_keywords) {
3555                 if (efile->filename != NULL &&
3556                     ! copy_file_with_keywords (efile->filename,
3557                                                newfname,
3558                                                efile->keywords)) {
3559                         /* FIXME: what to do with monitors here, they
3560                          * have already been whacked, a corner case
3561                          * not handled! */
3562                         g_free (newfname);
3563                         return FALSE;
3564                 }
3565         } else {
3566                 if (efile->filename != NULL &&
3567                     ! copy_file (efile->filename, newfname)) {
3568                         /* FIXME: what to do with monitors here, they
3569                          * have already been whacked, a corner case
3570                          * not handled! */
3571                         g_free (newfname);
3572                         return FALSE;
3573                 }
3574         }
3575
3576         /* we didn't copy but ensure path anyway */
3577         if (efile->filename == NULL &&
3578             ! ensure_dir (newfname,
3579                           TRUE /* ignore_basename */)) {
3580                 g_free (newfname);
3581                 return FALSE;
3582         }
3583
3584         /* this file already exists so re-add monitors at the new location */
3585         if (efile->filename != NULL) {
3586                 GSList *li;
3587                 char *uri = gnome_vfs_get_uri_from_local_path (newfname);
3588
3589                 for (li = entry->monitors; li != NULL; li = li->next) {
3590                         FileMonitorHandle *h = li->data;
3591
3592                         gnome_vfs_monitor_add (&(h->handle),
3593                                                uri,
3594                                                GNOME_VFS_MONITOR_FILE,
3595                                                file_monitor,
3596                                                h);
3597                 }
3598
3599                 g_free (uri);
3600         }
3601
3602         g_free (efile->filename);
3603         efile->filename = newfname;
3604         efile->per_user = TRUE;
3605
3606         return TRUE;
3607 }
3608
3609 static void
3610 try_free_file_monitors_create_dirfile_unlocked (VFolderInfo *info,
3611                                                 Folder *folder)
3612 {
3613         GSList *li, *list;
3614
3615         list = g_slist_copy (info->free_file_monitors);
3616
3617         for (li = list; li != NULL; li = li->next) {
3618                 FileMonitorHandle *handle = li->data;
3619                 Folder *f;
3620                 VFolderURI vuri;
3621                 GnomeVFSResult result;
3622
3623                 if ( ! handle->is_directory_file)
3624                         continue;
3625
3626                 /* Evil! EVIL URI PARSING. this will eat a lot of stack if we
3627                  * have lots of free monitors */
3628
3629                 VFOLDER_URI_PARSE (handle->uri, &vuri);
3630
3631                 f = resolve_folder (info, 
3632                                     vuri.path,
3633                                     TRUE /* ignore_basename */,
3634                                     &result,
3635                                     NULL);
3636
3637                 if (folder != f)
3638                         continue;
3639
3640                 info->free_file_monitors =
3641                         g_slist_remove (info->free_file_monitors, handle);
3642                 ((Entry *)folder)->monitors =
3643                         g_slist_prepend (((Entry *)folder)->monitors, handle);
3644
3645                 handle->exists = TRUE;
3646                 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *)handle,
3647                                             handle->uri, 
3648                                             GNOME_VFS_MONITOR_EVENT_CREATED);
3649         }
3650
3651         g_slist_free (list);
3652 }
3653
3654 static void
3655 make_new_dirfile (VFolderInfo *info, Folder *folder)
3656 {
3657         char *name = g_strdup (folder->entry.name);
3658         char *fname;
3659         char *p;
3660         int i;
3661         int fd;
3662
3663         for (p = name; *p != '\0'; p++) {
3664                 if ( ! ( (*p >= 'a' && *p <= 'z') ||
3665                          (*p >= 'A' && *p <= 'Z') ||
3666                          (*p >= '0' && *p <= '9') ||
3667                          *p == '_')) {
3668                         *p = '_';
3669                 }
3670         }
3671
3672         i = 0;
3673         fname = NULL;
3674         do {
3675                 char *fullname;
3676
3677                 g_free (fname);
3678
3679                 if (i > 0) {
3680                         fname = g_strdup_printf ("%s-%d.directory", name, i);
3681                 } else {
3682                         fname = g_strdup_printf ("%s.directory", name);
3683                 }
3684
3685                 fullname = g_build_filename
3686                         (info->user_desktop_dir, fname, NULL);
3687                 fd = open (fullname, O_CREAT | O_WRONLY | O_EXCL, 0600);
3688                 g_free (fullname);
3689         } while (fd < 0);
3690
3691         close (fd);
3692
3693         folder->desktop_file = fname;
3694         info->dirty = TRUE;
3695
3696         try_free_file_monitors_create_dirfile_unlocked (info, folder);
3697 }
3698
3699 static gboolean
3700 make_dirfile_private (VFolderInfo *info, Folder *folder)
3701 {
3702         char *fname;
3703         char *desktop_file;
3704         GSList *li;
3705         char *uri;
3706         gboolean ret;
3707
3708         if (info->user_desktop_dir == NULL)
3709                 return FALSE;
3710
3711         if ( ! ensure_dir (info->user_desktop_dir,
3712                            FALSE /* ignore_basename */))
3713                 return FALSE;
3714
3715
3716         if (folder->desktop_file == NULL) {
3717                 make_new_dirfile (info, folder);
3718                 return TRUE;
3719         }
3720
3721         /* FIXME: this is broken!  What if the desktop file exists
3722          * in the local but there is a different (but with a same name)
3723          * .directory in the system. */
3724         fname = g_build_filename (info->user_desktop_dir,
3725                                   folder->desktop_file,
3726                                   NULL);
3727
3728         if (access (fname, F_OK) == 0) {
3729                 g_free (fname);
3730                 return TRUE;
3731         }
3732
3733         desktop_file = get_directory_file (info, folder);
3734
3735         if (desktop_file == NULL) {
3736                 int fd = open (fname, O_CREAT | O_EXCL | O_WRONLY, 0600);
3737                 g_free (fname);
3738                 if (fd >= 0) {
3739                         close (fd);
3740                         return TRUE;
3741                 }
3742                 return FALSE;
3743         }
3744
3745         for (li = ((Entry *)folder)->monitors; li != NULL; li = li->next) {
3746                 FileMonitorHandle *h = li->data;
3747                 if (h->is_directory_file) {
3748                         if (h->handle != NULL)
3749                                 gnome_vfs_monitor_cancel (h->handle);
3750                         h->handle = NULL;
3751                 }
3752         }
3753
3754         ret = TRUE;
3755
3756         if ( ! copy_file (desktop_file, fname)) {
3757                 ret = FALSE;
3758                 g_free (fname);
3759                 fname = desktop_file;
3760                 desktop_file = NULL;
3761         }
3762
3763         uri = gnome_vfs_get_uri_from_local_path (fname);
3764
3765         for (li = ((Entry *)folder)->monitors; li != NULL; li = li->next) {
3766                 FileMonitorHandle *h = li->data;
3767
3768                 if (h->is_directory_file) {
3769                         gnome_vfs_monitor_add (&(h->handle),
3770                                                uri,
3771                                                GNOME_VFS_MONITOR_FILE,
3772                                                file_monitor,
3773                                                h);
3774                 }
3775         }
3776
3777         g_free (uri);
3778
3779         g_free (desktop_file);
3780         g_free (fname);
3781
3782         return ret;
3783 }
3784
3785 static Folder *
3786 resolve_folder (VFolderInfo *info,
3787                 const char *path,
3788                 gboolean ignore_basename,
3789                 GnomeVFSResult *result,
3790                 GnomeVFSContext *context)
3791 {
3792         char **ppath;
3793         int i;
3794         Folder *folder = info->root;
3795
3796         ppath = g_strsplit (path, "/", -1);
3797
3798         if (ppath == NULL ||
3799             ppath[0] == NULL) {
3800                 g_strfreev (ppath);
3801                 *result = GNOME_VFS_ERROR_INVALID_URI;
3802                 return NULL;
3803         }
3804
3805         for (i = 0; ppath [i] != NULL; i++) {
3806                 const char *segment = ppath[i];
3807
3808                 if (*segment == '\0')
3809                         continue;
3810
3811                 if (ignore_basename && ppath [i + 1] == NULL)
3812                         break;
3813                 else {
3814                         folder = (Folder *) find_entry (folder->subfolders, 
3815                                                         segment);
3816                         if (folder == NULL)
3817                                 break;
3818                 }
3819         }
3820         g_strfreev (ppath);
3821
3822         if (gnome_vfs_context_check_cancellation (context)) {
3823                 *result = GNOME_VFS_ERROR_CANCELLED;
3824                 return NULL;
3825         }
3826
3827         if (folder == NULL)
3828                 *result = GNOME_VFS_ERROR_NOT_FOUND;
3829
3830         return folder;
3831 }
3832
3833 static Entry *
3834 resolve_path (VFolderInfo *info,
3835               const char *path,
3836               const char *basename,
3837               Folder **return_folder,
3838               GnomeVFSResult *result,
3839               GnomeVFSContext *context)
3840 {
3841         Entry *entry;
3842         Folder *folder;
3843
3844         if (strcmp (path, "/") == 0)
3845                 return (Entry *)info->root;
3846
3847         folder = resolve_folder (info, path,
3848                                  TRUE /* ignore_basename */,
3849                                  result, context);
3850
3851         if (return_folder != NULL)
3852                 *return_folder = folder;
3853
3854         if (folder == NULL) {
3855                 return NULL;
3856         }
3857
3858         /* Make sure we have the entries here */
3859         ensure_folder_unlocked (info, folder,
3860                                 FALSE /* subfolders */,
3861                                 NULL /* except */,
3862                                 FALSE /* ignore_unallocated */);
3863
3864         entry = find_entry (folder->entries, basename);
3865
3866         if (entry == NULL)
3867                 *result = GNOME_VFS_ERROR_NOT_FOUND;
3868
3869         return entry;
3870 }
3871
3872 static Entry *
3873 get_entry_unlocked (VFolderURI *vuri,
3874                     Folder **parent,
3875                     gboolean *is_directory_file,
3876                     GnomeVFSResult *result,
3877                     GnomeVFSContext *context)
3878 {
3879         VFolderInfo *info;
3880         Entry *entry;
3881
3882         if (is_directory_file != NULL)
3883                 *is_directory_file = FALSE;
3884         if (parent != NULL)
3885                 *parent = NULL;
3886
3887         info = get_vfolder_info_unlocked (vuri->scheme, result, context);
3888         if (info == NULL)
3889                 return NULL;
3890
3891         if (gnome_vfs_context_check_cancellation (context)) {
3892                 *result = GNOME_VFS_ERROR_CANCELLED;
3893                 return NULL;
3894         }
3895
3896         if (vuri->is_all_scheme) {
3897                 GSList *efile_list;
3898
3899                 if (vuri->file == NULL) {
3900                         entry = resolve_path (info, 
3901                                               vuri->path, 
3902                                               vuri->file, 
3903                                               parent, 
3904                                               result, 
3905                                               context);
3906                         return entry;
3907                 }
3908
3909                 efile_list = g_hash_table_lookup (info->entries_ht, vuri->file);
3910
3911                 if (efile_list == NULL) {
3912                         *result = GNOME_VFS_ERROR_NOT_FOUND;
3913                         return NULL;
3914                 } else {
3915                         return efile_list->data;
3916                 }
3917         }
3918
3919         if (vuri->file != NULL && 
3920             check_ext (vuri->file, ".directory") == TRUE) {
3921                 Folder *folder;
3922
3923                 folder = resolve_folder (info, vuri->path,
3924                                          TRUE /* ignore_basename */,
3925                                          result, context);
3926                 if (folder == NULL) {
3927                         return NULL;
3928                 }
3929
3930                 if (is_directory_file != NULL)
3931                         *is_directory_file = TRUE;
3932
3933                 if (parent != NULL)
3934                         *parent = folder;
3935
3936                 return (Entry *)folder;
3937         } else {
3938                 entry = resolve_path (info, vuri->path, vuri->file, parent, 
3939                                       result, context);
3940                 return entry;
3941         }
3942 }
3943
3944 static Entry *
3945 get_entry (VFolderURI *vuri,
3946            Folder **parent,
3947            gboolean *is_directory_file,
3948            GnomeVFSResult *result,
3949            GnomeVFSContext *context)
3950 {
3951         Entry *entry;
3952
3953         G_LOCK (vfolder_lock);
3954         entry = get_entry_unlocked (vuri, 
3955                                     parent, 
3956                                     is_directory_file, 
3957                                     result, context);
3958         G_UNLOCK (vfolder_lock);
3959
3960         return entry;
3961 }
3962
3963 /* only works for files and only those that exist */
3964 /* unlocked function */
3965 static GnomeVFSURI *
3966 desktop_uri_to_file_uri (VFolderInfo *info,
3967                          VFolderURI *desktop_vuri,
3968                          Entry **the_entry,
3969                          gboolean *the_is_directory_file,
3970                          Folder **the_folder,
3971                          gboolean privatize,
3972                          GnomeVFSResult *result,
3973                          GnomeVFSContext *context)
3974 {
3975         gboolean is_directory_file;
3976         GnomeVFSURI *ret_uri;
3977         Folder *folder = NULL;
3978         Entry *entry;
3979
3980         entry = get_entry_unlocked (desktop_vuri,
3981                                     &folder,
3982                                     &is_directory_file,
3983                                     result,
3984                                     context);
3985         if (entry == NULL)
3986                 return NULL;
3987
3988         if (gnome_vfs_context_check_cancellation (context)) {
3989                 *result = GNOME_VFS_ERROR_CANCELLED;
3990                 return NULL;
3991         }
3992
3993         if (the_folder != NULL)
3994                 *the_folder = folder;
3995
3996         if (the_entry != NULL)
3997                 *the_entry = entry;
3998         if (the_is_directory_file != NULL)
3999                 *the_is_directory_file = is_directory_file;
4000
4001         if (is_directory_file &&
4002             entry->type == ENTRY_FOLDER) {
4003                 char *desktop_file;
4004
4005                 folder = (Folder *)entry;
4006
4007                 if (the_folder != NULL)
4008                         *the_folder = folder;
4009
4010                 /* we'll be doing something write like */
4011                 if (folder->read_only &&
4012                     privatize) {
4013                         *result = GNOME_VFS_ERROR_READ_ONLY;
4014                         return NULL;
4015                 }
4016
4017                 if (privatize) {
4018                         char *fname;
4019
4020                         if (gnome_vfs_context_check_cancellation (context)) {
4021                                 *result = GNOME_VFS_ERROR_CANCELLED;
4022                                 return NULL;
4023                         }
4024
4025                         if ( ! make_dirfile_private (info, folder)) {
4026                                 *result = GNOME_VFS_ERROR_GENERIC;
4027                                 return NULL;
4028                         }
4029                         fname = g_build_filename (g_get_home_dir (),
4030                                                   folder->desktop_file,
4031                                                   NULL);
4032                         ret_uri = gnome_vfs_uri_new (fname);
4033                         g_free (fname);
4034                         return ret_uri;
4035                 }
4036
4037                 desktop_file = get_directory_file_unlocked (info, folder);
4038                 if (desktop_file != NULL) {
4039                         char *s = gnome_vfs_get_uri_from_local_path
4040                                 (desktop_file);
4041
4042                         g_free (desktop_file);
4043
4044                         ret_uri = gnome_vfs_uri_new (s);
4045                         g_free (s);
4046
4047                         return ret_uri;
4048                 } else {
4049                         *result = GNOME_VFS_ERROR_NOT_FOUND;
4050                         return NULL;
4051                 }
4052         } else if (entry->type == ENTRY_FILE) {
4053                 EntryFile *efile = (EntryFile *)entry;
4054                 char *s;
4055
4056                 /* we'll be doing something write like */
4057                 if (folder != NULL &&
4058                     folder->read_only &&
4059                     privatize) {
4060                         *result = GNOME_VFS_ERROR_READ_ONLY;
4061                         return NULL;
4062                 }
4063
4064                 if (gnome_vfs_context_check_cancellation (context)) {
4065                         *result = GNOME_VFS_ERROR_CANCELLED;
4066                         return NULL;
4067                 }
4068
4069                 if (privatize &&
4070                     ! make_file_private (info, efile)) {
4071                         *result = GNOME_VFS_ERROR_GENERIC;
4072                         return NULL;
4073                 }
4074
4075                 s = gnome_vfs_get_uri_from_local_path (efile->filename);
4076                 ret_uri = gnome_vfs_uri_new (s);
4077                 g_free (s);
4078
4079                 return ret_uri;
4080         } else {
4081                 if (the_folder != NULL)
4082                         *the_folder = (Folder *)entry;
4083                 *result = GNOME_VFS_ERROR_IS_DIRECTORY;
4084                 return NULL;
4085         }
4086 }
4087
4088 static void
4089 remove_file (Folder *folder, const char *basename)
4090 {
4091         GSList *li;
4092         char *s;
4093
4094         if (folder->includes_ht != NULL) {
4095                 li = g_hash_table_lookup (folder->includes_ht, basename);
4096                 if (li != NULL) {
4097                         char *name = li->data;
4098                         folder->includes = g_slist_delete_link
4099                                 (folder->includes, li);
4100                         g_hash_table_remove (folder->includes_ht, basename);
4101                         g_free (name);
4102                 }
4103         }
4104
4105         if (folder->excludes == NULL) {
4106                 folder->excludes = g_hash_table_new_full
4107                         (g_str_hash, g_str_equal,
4108                          (GDestroyNotify)g_free,
4109                          NULL);
4110         }
4111         s = g_strdup (basename);
4112         g_hash_table_replace (folder->excludes, s, s);
4113 }
4114
4115 static void
4116 add_file (Folder *folder, const char *basename)
4117 {
4118         GSList *li = NULL;
4119
4120         if (folder->includes_ht != NULL) {
4121                 li = g_hash_table_lookup (folder->includes_ht, basename);
4122         }
4123
4124         /* if not found */
4125         if (li == NULL) {
4126                 char *str = g_strdup (basename);
4127                 folder->includes =
4128                         g_slist_prepend (folder->includes, str);
4129                 if (folder->includes_ht == NULL) {
4130                         folder->includes_ht =
4131                                 g_hash_table_new_full (g_str_hash,
4132                                                        g_str_equal,
4133                                                        NULL,
4134                                                        NULL);
4135                 }
4136                 g_hash_table_replace (folder->includes_ht,
4137                                       str, folder->includes);
4138         }
4139         if (folder->excludes != NULL)
4140                 g_hash_table_remove (folder->excludes, basename);
4141 }
4142
4143 typedef struct _FileHandle FileHandle;
4144 struct _FileHandle {
4145         VFolderInfo *info;
4146         GnomeVFSMethodHandle *handle;
4147         Entry *entry;
4148         gboolean write;
4149         gboolean is_directory_file;
4150 };
4151
4152 static void
4153 make_handle (GnomeVFSMethodHandle **method_handle,
4154              GnomeVFSMethodHandle *file_handle,
4155              VFolderInfo *info,
4156              Entry *entry,
4157              gboolean is_directory_file,
4158              gboolean write)
4159 {
4160         if (file_handle != NULL) {
4161                 FileHandle *handle = g_new0 (FileHandle, 1);
4162
4163                 handle->info = info;
4164                 handle->handle = file_handle;
4165                 handle->entry = entry_ref (entry);
4166                 handle->is_directory_file = is_directory_file;
4167                 handle->write = write;
4168
4169                 *method_handle = (GnomeVFSMethodHandle *) handle;
4170         } else {
4171                 *method_handle = NULL;
4172         }
4173 }
4174
4175 static void
4176 whack_handle (FileHandle *handle)
4177 {
4178         entry_unref (handle->entry);
4179         handle->entry = NULL;
4180
4181         handle->handle = NULL;
4182         handle->info = NULL;
4183
4184         g_free (handle);
4185 }
4186
4187 static GnomeVFSResult
4188 do_open (GnomeVFSMethod *method,
4189          GnomeVFSMethodHandle **method_handle,
4190          GnomeVFSURI *uri,
4191          GnomeVFSOpenMode mode,
4192          GnomeVFSContext *context)
4193 {
4194         GnomeVFSURI *file_uri;
4195         GnomeVFSResult result = GNOME_VFS_OK;
4196         VFolderInfo *info;
4197         Entry *entry;
4198         gboolean is_directory_file;
4199         GnomeVFSMethodHandle *file_handle = NULL;
4200         VFolderURI vuri;
4201
4202         VFOLDER_URI_PARSE (uri, &vuri);
4203
4204         /* These can't be very nice FILE names */
4205         if (vuri.file == NULL ||
4206             vuri.ends_in_slash)
4207                 return GNOME_VFS_ERROR_INVALID_URI;
4208
4209         info = get_vfolder_info (vuri.scheme, &result, context);
4210         if (info == NULL)
4211                 return result;
4212
4213         if (mode & GNOME_VFS_OPEN_WRITE && 
4214             (info->read_only || vuri.is_all_scheme))
4215                 return GNOME_VFS_ERROR_READ_ONLY;
4216
4217         G_LOCK (vfolder_lock);
4218         file_uri = desktop_uri_to_file_uri (info,
4219                                             &vuri,
4220                                             &entry,
4221                                             &is_directory_file,
4222                                             NULL /* the_folder */,
4223                                             mode & GNOME_VFS_OPEN_WRITE,
4224                                             &result,
4225                                             context);
4226
4227         if (file_uri == NULL) {
4228                 G_UNLOCK (vfolder_lock);
4229                 return result;
4230         }
4231
4232         result = (* parent_method->open) (parent_method,
4233                                           &file_handle,
4234                                           file_uri,
4235                                           mode,
4236                                           context);
4237
4238         if (result == GNOME_VFS_ERROR_CANCELLED) {
4239                 G_UNLOCK (vfolder_lock);
4240                 gnome_vfs_uri_unref (file_uri);
4241                 return result;
4242         }
4243
4244         make_handle (method_handle,
4245                      file_handle,
4246                      info,
4247                      entry,
4248                      is_directory_file,
4249                      mode & GNOME_VFS_OPEN_WRITE);
4250
4251         gnome_vfs_uri_unref (file_uri);
4252
4253         if (info->dirty) {
4254                 vfolder_info_write_user (info);
4255         }
4256
4257         G_UNLOCK (vfolder_lock);
4258
4259         return result;
4260 }
4261
4262 static void
4263 remove_from_all_except (Folder *root,
4264                         const char *name,
4265                         Folder *except)
4266 {
4267         GSList *li;
4268
4269         if (root != except) {
4270                 remove_file (root, name);
4271                 if (root->up_to_date) {
4272                         for (li = root->entries; li != NULL; li = li->next) {
4273                                 Entry *entry = li->data;
4274                                 if (strcmp (name, entry->name) == 0) {
4275                                         root->entries = 
4276                                                 g_slist_delete_link
4277                                                    (root->entries, li);
4278                                         break;
4279                                 }
4280                         }
4281                 }
4282         }
4283
4284         for (li = root->subfolders; li != NULL; li = li->next) {
4285                 Folder *subfolder = li->data;
4286
4287                 remove_from_all_except (subfolder, name, except);
4288         }
4289 }
4290
4291 static GnomeVFSResult
4292 do_create (GnomeVFSMethod *method,
4293            GnomeVFSMethodHandle **method_handle,
4294            GnomeVFSURI *uri,
4295            GnomeVFSOpenMode mode,
4296            gboolean exclusive,
4297            guint perm,
4298            GnomeVFSContext *context)
4299 {
4300         GnomeVFSResult result = GNOME_VFS_OK;
4301         GnomeVFSMethodHandle *file_handle;
4302         GnomeVFSURI *file_uri;
4303         VFolderURI vuri;
4304         VFolderInfo *info;
4305         Folder *parent;
4306         Entry *entry;
4307         EntryFile *efile;
4308         char *s;
4309         GSList *li;
4310
4311         VFOLDER_URI_PARSE (uri, &vuri);
4312
4313         /* These can't be very nice FILE names */
4314         if (vuri.file == NULL ||
4315             vuri.ends_in_slash)
4316                 return GNOME_VFS_ERROR_INVALID_URI;
4317         
4318         if ( ! check_ext (vuri.file, ".desktop") &&
4319              ! strcmp (vuri.file, ".directory") == 0) {
4320                 return GNOME_VFS_ERROR_INVALID_URI;
4321         }
4322
4323         /* all scheme is read only */
4324         if (vuri.is_all_scheme)
4325                 return GNOME_VFS_ERROR_READ_ONLY;
4326
4327         info = get_vfolder_info (vuri.scheme, &result, context);
4328         if (info == NULL)
4329                 return result;
4330
4331         if (info->user_filename == NULL ||
4332             info->read_only)
4333                 return GNOME_VFS_ERROR_READ_ONLY;
4334
4335         parent = resolve_folder (info, vuri.path,
4336                                  TRUE /* ignore_basename */,
4337                                  &result, context);
4338         if (parent == NULL)
4339                 return result;
4340
4341         if (parent->read_only)
4342                 return GNOME_VFS_ERROR_READ_ONLY;
4343
4344         if (strcmp (vuri.file, ".directory") == 0) {
4345                 char *fname;
4346
4347                 G_LOCK (vfolder_lock);
4348
4349                 if (exclusive) {
4350                         char *desktop_file;
4351                         desktop_file = get_directory_file_unlocked (info, parent);
4352                         if (desktop_file != NULL) {
4353                                 g_free (desktop_file);
4354                                 G_UNLOCK (vfolder_lock);
4355                                 return GNOME_VFS_ERROR_FILE_EXISTS;
4356                         }
4357                 }
4358
4359                 if ( ! make_dirfile_private (info, parent)) {
4360                         G_UNLOCK (vfolder_lock);
4361                         return GNOME_VFS_ERROR_GENERIC;
4362                 }
4363                 fname = g_build_filename (g_get_home_dir (),
4364                                           parent->desktop_file,
4365                                           NULL);
4366                 s = gnome_vfs_get_uri_from_local_path (fname);
4367                 file_uri = gnome_vfs_uri_new (s);
4368                 g_free (fname);
4369                 g_free (s);
4370
4371                 if (file_uri == NULL) {
4372                         G_UNLOCK (vfolder_lock);
4373                         return GNOME_VFS_ERROR_GENERIC;
4374                 }
4375
4376                 result = (* parent_method->create) (parent_method,
4377                                                     &file_handle,
4378                                                     file_uri,
4379                                                     mode,
4380                                                     exclusive,
4381                                                     perm,
4382                                                     context);
4383                 gnome_vfs_uri_unref (file_uri);
4384
4385                 make_handle (method_handle,
4386                              file_handle,
4387                              info,
4388                              (Entry *)parent,
4389                              TRUE /* is_directory_file */,
4390                              TRUE /* write */);
4391
4392                 if (info->dirty)
4393                         vfolder_info_write_user (info);
4394
4395                 G_UNLOCK (vfolder_lock);
4396
4397                 return result;
4398         }
4399
4400         ensure_folder (info, parent,
4401                        FALSE /* subfolders */,
4402                        NULL /* except */,
4403                        FALSE /* ignore_unallocated */);
4404
4405         entry = find_entry (parent->entries, vuri.file);
4406
4407         if (entry != NULL &&
4408             entry->type == ENTRY_FOLDER)
4409                 return GNOME_VFS_ERROR_IS_DIRECTORY;
4410
4411         efile = (EntryFile *)entry;
4412
4413         if (efile != NULL) {
4414                 if (exclusive)
4415                         return GNOME_VFS_ERROR_FILE_EXISTS;
4416
4417                 G_LOCK (vfolder_lock);
4418                 if ( ! make_file_private (info, efile)) {
4419                         G_UNLOCK (vfolder_lock);
4420                         return GNOME_VFS_ERROR_GENERIC;
4421                 }
4422
4423                 s = gnome_vfs_get_uri_from_local_path (efile->filename);
4424                 file_uri = gnome_vfs_uri_new (s);
4425                 g_free (s);
4426
4427                 if (file_uri == NULL) {
4428                         G_UNLOCK (vfolder_lock);
4429                         return GNOME_VFS_ERROR_GENERIC;
4430                 }
4431
4432                 result = (* parent_method->create) (parent_method,
4433                                                     &file_handle,
4434                                                     file_uri,
4435                                                     mode,
4436                                                     exclusive,
4437                                                     perm,
4438                                                     context);
4439                 gnome_vfs_uri_unref (file_uri);
4440
4441                 make_handle (method_handle,
4442                              file_handle,
4443                              info,
4444                              (Entry *)efile,
4445                              FALSE /* is_directory_file */,
4446                              TRUE /* write */);
4447
4448                 G_UNLOCK (vfolder_lock);
4449
4450                 return result;
4451         }
4452
4453         G_LOCK (vfolder_lock);
4454         
4455         li = g_hash_table_lookup (info->entries_ht, vuri.file);
4456
4457         if (exclusive && li != NULL) {
4458                 G_UNLOCK (vfolder_lock);
4459                 return GNOME_VFS_ERROR_FILE_EXISTS;
4460         }
4461
4462         if (li == NULL) {
4463                 efile = file_new (vuri.file);
4464                 vfolder_info_insert_entry (info, efile);
4465                 entry_unref ((Entry *)efile);
4466         } else {
4467                 efile = li->data;
4468         }
4469
4470         /* this will make a private name for this */
4471         if ( ! make_file_private (info, efile)) {
4472                 G_UNLOCK (vfolder_lock);
4473                 return GNOME_VFS_ERROR_GENERIC;
4474         }
4475
4476         add_file (parent, vuri.file);
4477         parent->sorted = FALSE;
4478
4479         if (parent->up_to_date)
4480                 parent->entries = g_slist_prepend (parent->entries, efile);
4481
4482         /* if we created a brand new name, then we exclude it
4483          * from everywhere else to ensure overall sanity */
4484         if (li == NULL)
4485                 remove_from_all_except (info->root, vuri.file, parent);
4486
4487         s = gnome_vfs_get_uri_from_local_path (efile->filename);
4488         file_uri = gnome_vfs_uri_new (s);
4489         g_free (s);
4490
4491         result = (* parent_method->create) (parent_method,
4492                                             &file_handle,
4493                                             file_uri,
4494                                             mode,
4495                                             exclusive,
4496                                             perm,
4497                                             context);
4498         gnome_vfs_uri_unref (file_uri);
4499
4500         make_handle (method_handle,
4501                      file_handle,
4502                      info,
4503                      (Entry *)efile,
4504                      FALSE /* is_directory_file */,
4505                      TRUE /* write */);
4506
4507         vfolder_info_write_user (info);
4508
4509         G_UNLOCK (vfolder_lock);
4510
4511         return result;
4512 }
4513
4514 static GnomeVFSResult
4515 do_close (GnomeVFSMethod *method,
4516           GnomeVFSMethodHandle *method_handle,
4517           GnomeVFSContext *context)
4518 {
4519         GnomeVFSResult result;
4520         FileHandle *handle = (FileHandle *)method_handle;
4521         if (method_handle == (GnomeVFSMethodHandle *)method)
4522                 return GNOME_VFS_OK;
4523
4524         G_LOCK (vfolder_lock);
4525         
4526         result = (* parent_method->close) (parent_method,
4527                                            handle->handle,
4528                                            context);
4529         handle->handle = NULL;
4530
4531         /* we reread the Categories keyword */
4532         if (handle->write &&
4533             handle->entry != NULL &&
4534             handle->entry->type == ENTRY_FILE) {
4535                 EntryFile *efile = (EntryFile *)handle->entry;
4536                 char *categories;
4537                 readitem_entry (efile->filename,
4538                                 "Categories",
4539                                 &categories,
4540                                 NULL,
4541                                 NULL);
4542                 set_keywords (efile, categories);
4543                 g_free (categories);
4544                 /* FIXME: what about OnlyShowIn */
4545
4546                 /* FIXME: check if the keywords changed, if not, do
4547                  * nothing */
4548
4549                 /* Perhaps a bit drastic */
4550                 /* also this emits the CHANGED monitor signal */
4551                 invalidate_folder_T (handle->info->root);
4552
4553                 /* the file changed monitor will happen by itself
4554                  * as the underlying file is changed */
4555         } else if (handle->write &&
4556                    handle->entry != NULL &&
4557                    handle->entry->type == ENTRY_FOLDER &&
4558                    handle->is_directory_file) {
4559                 /* if we're monitoring this directory, emit the CHANGED
4560                  * monitor thing, it will also emit a changed on
4561                  * the file itself.  It is better to emit changed
4562                  * just in case. */
4563                 emit_monitor ((Folder *)(handle->entry),
4564                               GNOME_VFS_MONITOR_EVENT_CHANGED);
4565         }
4566
4567         whack_handle (handle);
4568
4569         G_UNLOCK (vfolder_lock);
4570
4571         return result;
4572 }
4573
4574 static void
4575 fill_buffer (gpointer buffer,
4576              GnomeVFSFileSize num_bytes,
4577              GnomeVFSFileSize *bytes_read)
4578 {
4579         char *buf = buffer;
4580         GnomeVFSFileSize i;
4581         for (i = 0; i < num_bytes; i++) {
4582                 if (rand () % 32 == 0 ||
4583                     i == num_bytes-1)
4584                         buf[i] = '\n';
4585                 else
4586                         buf[i] = ((rand()>>4) % 94) + 32;
4587         }
4588         if (bytes_read != 0)
4589                 *bytes_read = i;
4590 }
4591
4592 static GnomeVFSResult
4593 do_read (GnomeVFSMethod *method,
4594          GnomeVFSMethodHandle *method_handle,
4595          gpointer buffer,
4596          GnomeVFSFileSize num_bytes,
4597          GnomeVFSFileSize *bytes_read,
4598          GnomeVFSContext *context)
4599 {
4600         GnomeVFSResult result;
4601         FileHandle *handle = (FileHandle *)method_handle;
4602
4603         if (method_handle == (GnomeVFSMethodHandle *)method) {
4604                 if ((rand () >> 4) & 0x3) {
4605                         fill_buffer (buffer, num_bytes, bytes_read);
4606                         return GNOME_VFS_OK;
4607                 } else {
4608                         return GNOME_VFS_ERROR_EOF;
4609                 }
4610         }
4611         
4612         result = (* parent_method->read) (parent_method,
4613                                           handle->handle,
4614                                           buffer, num_bytes,
4615                                           bytes_read,
4616                                           context);
4617
4618         return result;
4619 }
4620
4621 static GnomeVFSResult
4622 do_write (GnomeVFSMethod *method,
4623           GnomeVFSMethodHandle *method_handle,
4624           gconstpointer buffer,
4625           GnomeVFSFileSize num_bytes,
4626           GnomeVFSFileSize *bytes_written,
4627           GnomeVFSContext *context)
4628 {
4629         GnomeVFSResult result;
4630         FileHandle *handle = (FileHandle *)method_handle;
4631
4632         if (method_handle == (GnomeVFSMethodHandle *)method)
4633                 return GNOME_VFS_OK;
4634
4635         result = (* parent_method->write) (parent_method,
4636                                            handle->handle,
4637                                            buffer, num_bytes,
4638                                            bytes_written,
4639                                            context);
4640
4641         return result;
4642 }
4643
4644
4645 static GnomeVFSResult
4646 do_seek (GnomeVFSMethod *method,
4647          GnomeVFSMethodHandle *method_handle,
4648          GnomeVFSSeekPosition whence,
4649          GnomeVFSFileOffset offset,
4650          GnomeVFSContext *context)
4651 {
4652         GnomeVFSResult result;
4653         FileHandle *handle = (FileHandle *)method_handle;
4654
4655         if (method_handle == (GnomeVFSMethodHandle *)method)
4656                 return GNOME_VFS_OK;
4657         
4658         result = (* parent_method->seek) (parent_method,
4659                                           handle->handle,
4660                                           whence, offset,
4661                                           context);
4662
4663         return result;
4664 }
4665
4666 static GnomeVFSResult
4667 do_tell (GnomeVFSMethod *method,
4668          GnomeVFSMethodHandle *method_handle,
4669          GnomeVFSFileOffset *offset_return)
4670 {
4671         GnomeVFSResult result;
4672         FileHandle *handle = (FileHandle *)method_handle;
4673         
4674         result = (* parent_method->tell) (parent_method,
4675                                           handle->handle,
4676                                           offset_return);
4677
4678         return result;
4679 }
4680
4681
4682 static GnomeVFSResult
4683 do_truncate_handle (GnomeVFSMethod *method,
4684                     GnomeVFSMethodHandle *method_handle,
4685                     GnomeVFSFileSize where,
4686                     GnomeVFSContext *context)
4687 {
4688         GnomeVFSResult result;
4689         FileHandle *handle = (FileHandle *)method_handle;
4690
4691         if (method_handle == (GnomeVFSMethodHandle *)method)
4692                 return GNOME_VFS_OK;
4693         
4694         result = (* parent_method->truncate_handle) (parent_method,
4695                                                      handle->handle,
4696                                                      where,
4697                                                      context);
4698
4699         return result;
4700 }
4701
4702 static GnomeVFSResult
4703 do_truncate (GnomeVFSMethod *method,
4704              GnomeVFSURI *uri,
4705              GnomeVFSFileSize where,
4706              GnomeVFSContext *context)
4707 {
4708         GnomeVFSURI *file_uri;
4709         GnomeVFSResult result = GNOME_VFS_OK;
4710         VFolderInfo *info;
4711         Entry *entry;
4712         VFolderURI vuri;
4713
4714         VFOLDER_URI_PARSE (uri, &vuri);
4715
4716         /* These can't be very nice FILE names */
4717         if (vuri.file == NULL ||
4718             vuri.ends_in_slash)
4719                 return GNOME_VFS_ERROR_INVALID_URI;
4720
4721         if (vuri.is_all_scheme)
4722                 return GNOME_VFS_ERROR_READ_ONLY;
4723
4724         info = get_vfolder_info (vuri.scheme, &result, context);
4725         if (info == NULL)
4726                 return result;
4727
4728         if (info->read_only)
4729                 return GNOME_VFS_ERROR_READ_ONLY;
4730
4731         G_LOCK (vfolder_lock);
4732         file_uri = desktop_uri_to_file_uri (info,
4733                                             &vuri,
4734                                             &entry,
4735                                             NULL /* the_is_directory_file */,
4736                                             NULL /* the_folder */,
4737                                             TRUE /* privatize */,
4738                                             &result,
4739                                             context);
4740         G_UNLOCK (vfolder_lock);
4741
4742         if (file_uri == NULL)
4743                 return result;
4744
4745         result = (* parent_method->truncate) (parent_method,
4746                                               file_uri,
4747                                               where,
4748                                               context);
4749
4750         gnome_vfs_uri_unref (file_uri);
4751
4752         if (info->dirty) {
4753                 G_LOCK (vfolder_lock);
4754                 vfolder_info_write_user (info);
4755                 G_UNLOCK (vfolder_lock);
4756         }
4757
4758         if (entry->type == ENTRY_FILE) {
4759                 EntryFile *efile = (EntryFile *)entry;
4760
4761                 G_LOCK (vfolder_lock);
4762                 g_slist_free (efile->keywords);
4763                 efile->keywords = NULL;
4764                 G_UNLOCK (vfolder_lock);
4765         }
4766
4767         /* Perhaps a bit drastic, but oh well */
4768         invalidate_folder (info->root);
4769
4770         return result;
4771 }
4772
4773 typedef struct _DirHandle DirHandle;
4774 struct _DirHandle {
4775         VFolderInfo *info;
4776         Folder *folder;
4777
4778         GnomeVFSFileInfoOptions options;
4779
4780         /* List of Entries */
4781         GSList *list;
4782         GSList *current;
4783 };
4784
4785 static GnomeVFSResult
4786 do_open_directory (GnomeVFSMethod *method,
4787                    GnomeVFSMethodHandle **method_handle,
4788                    GnomeVFSURI *uri,
4789                    GnomeVFSFileInfoOptions options,
4790                    GnomeVFSContext *context)
4791 {
4792         GnomeVFSResult result = GNOME_VFS_OK;
4793         VFolderURI vuri;
4794         DirHandle *dh;
4795         Folder *folder;
4796         VFolderInfo *info;
4797         char *desktop_file;
4798
4799         VFOLDER_URI_PARSE (uri, &vuri);
4800
4801         info = get_vfolder_info (vuri.scheme, &result, context);
4802         if (info == NULL)
4803                 return result;
4804
4805         /* In the all- scheme just list all filenames */
4806         if (vuri.is_all_scheme) {
4807                 if (any_subdir (vuri.path))
4808                         return GNOME_VFS_ERROR_NOT_FOUND;
4809
4810                 dh = g_new0 (DirHandle, 1);
4811                 dh->info = info;
4812                 dh->options = options;
4813                 dh->folder = NULL;
4814
4815                 G_LOCK (vfolder_lock);
4816                 dh->list = g_slist_copy (info->entries);
4817                 g_slist_foreach (dh->list, (GFunc)entry_ref, NULL);
4818                 dh->current = dh->list;
4819                 G_UNLOCK (vfolder_lock);
4820
4821                 *method_handle = (GnomeVFSMethodHandle*) dh;
4822                 return GNOME_VFS_OK;
4823         }
4824
4825         folder = resolve_folder (info, vuri.path,
4826                                  FALSE /* ignore_basename */,
4827                                  &result, context);
4828         if (folder == NULL)
4829                 return result;
4830
4831         /* Make sure we have the entries and sorted here */
4832         ensure_folder_sort (info, folder);
4833
4834         dh = g_new0 (DirHandle, 1);
4835         dh->info = info;
4836         dh->options = options;
4837
4838         G_LOCK (vfolder_lock);
4839         dh->folder = (Folder *)entry_ref ((Entry *)folder);
4840         dh->list = g_slist_copy (folder->entries);
4841         g_slist_foreach (folder->entries, (GFunc)entry_ref, NULL);
4842         G_UNLOCK (vfolder_lock);
4843
4844         desktop_file = get_directory_file (info, folder);
4845         if (desktop_file != NULL) {
4846                 EntryFile *efile = file_new (".directory");
4847                 dh->list = g_slist_prepend (dh->list, efile);
4848                 g_free (desktop_file);
4849         }
4850
4851         dh->current = dh->list;
4852
4853         *method_handle = (GnomeVFSMethodHandle*) dh;
4854
4855         return GNOME_VFS_OK;
4856 }
4857
4858 static GnomeVFSResult
4859 do_close_directory (GnomeVFSMethod *method,
4860                     GnomeVFSMethodHandle *method_handle,
4861                     GnomeVFSContext *context)
4862 {
4863         DirHandle *dh;
4864
4865         dh = (DirHandle*) method_handle;
4866
4867         G_LOCK (vfolder_lock);
4868
4869         g_slist_foreach (dh->list, (GFunc)entry_unref, NULL);
4870         g_slist_free (dh->list);
4871         dh->list = NULL;
4872
4873         dh->current = NULL;
4874
4875         if (dh->folder != NULL)
4876                 entry_unref ((Entry *)dh->folder);
4877         dh->folder = NULL;
4878
4879         dh->info = NULL;
4880
4881         g_free (dh);
4882
4883         G_UNLOCK (vfolder_lock);
4884
4885         return GNOME_VFS_OK;
4886 }
4887
4888 static GnomeVFSResult
4889 do_read_directory (GnomeVFSMethod *method,
4890                    GnomeVFSMethodHandle *method_handle,
4891                    GnomeVFSFileInfo *file_info,
4892                    GnomeVFSContext *context)
4893 {
4894         DirHandle *dh;
4895         Entry *entry;
4896         GnomeVFSFileInfoOptions options;
4897
4898         dh = (DirHandle*) method_handle;
4899
4900 read_directory_again:
4901
4902         if (dh->current == NULL) {
4903                 return GNOME_VFS_ERROR_EOF;
4904         }
4905
4906         entry = dh->current->data;
4907         dh->current = dh->current->next;
4908
4909         options = dh->options;
4910
4911         if (entry->type == ENTRY_FILE &&
4912             ((EntryFile *)entry)->filename != NULL) {
4913                 EntryFile *efile = (EntryFile *)entry;
4914                 char *furi = gnome_vfs_get_uri_from_local_path (efile->filename);
4915                 GnomeVFSURI *uri = gnome_vfs_uri_new (furi);
4916
4917                 /* we always get mime-type by forcing it below */
4918                 if (options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
4919                         options &= ~GNOME_VFS_FILE_INFO_GET_MIME_TYPE;
4920
4921                 file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
4922
4923                 /* Get the file info for this */
4924                 (* parent_method->get_file_info) (parent_method,
4925                                                   uri,
4926                                                   file_info,
4927                                                   options,
4928                                                   context);
4929
4930                 /* we ignore errors from this since the file_info just
4931                  * won't be filled completely if there's an error, that's all */
4932
4933                 g_free (file_info->mime_type);
4934                 file_info->mime_type = g_strdup ("application/x-gnome-app-info");
4935                 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
4936
4937                 /* Now we wipe those fields we don't support */
4938                 file_info->valid_fields &= ~(UNSUPPORTED_INFO_FIELDS);
4939
4940                 gnome_vfs_uri_unref (uri);
4941                 g_free (furi);
4942         } else if (entry->type == ENTRY_FILE) {
4943                 file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
4944
4945                 file_info->name = g_strdup (entry->name);
4946                 GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
4947
4948                 file_info->type = GNOME_VFS_FILE_TYPE_REGULAR;
4949                 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
4950
4951                 /* FIXME: Is this correct? isn't there an xdg mime type? */
4952                 file_info->mime_type = g_strdup ("application/x-gnome-app-info");
4953                 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
4954
4955                 /* FIXME: get some ctime/mtime */
4956         } else /* ENTRY_FOLDER */ {
4957                 Folder *folder = (Folder *)entry;
4958
4959                 /* Skip empty folders if they have
4960                  * the flag set */
4961                 if (folder->dont_show_if_empty) {
4962                         /* Make sure we have the entries */
4963                         ensure_folder (dh->info, folder,
4964                                        FALSE /* subfolders */,
4965                                        NULL /* except */,
4966                                        FALSE /* ignore_unallocated */);
4967
4968                         if (folder->entries == NULL) {
4969                                 /* start this function over on the
4970                                  * next item */
4971                                 goto read_directory_again;
4972                         }
4973                 }
4974
4975                 file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
4976
4977                 file_info->name = g_strdup (entry->name);
4978                 GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
4979
4980                 file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
4981                 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
4982
4983                 file_info->mime_type = g_strdup ("x-directory/vfolder-desktop");
4984                 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
4985
4986                 file_info->ctime = dh->info->modification_time;
4987                 file_info->mtime = dh->info->modification_time;
4988                 file_info->valid_fields |= (GNOME_VFS_FILE_INFO_FIELDS_CTIME |
4989                                             GNOME_VFS_FILE_INFO_FIELDS_MTIME);
4990         }
4991
4992         return GNOME_VFS_OK;
4993 }
4994
4995 static GnomeVFSResult
4996 do_get_file_info (GnomeVFSMethod *method,
4997                   GnomeVFSURI *uri,
4998                   GnomeVFSFileInfo *file_info,
4999                   GnomeVFSFileInfoOptions options,
5000                   GnomeVFSContext *context)
5001 {
5002         GnomeVFSURI *file_uri;
5003         GnomeVFSResult result = GNOME_VFS_OK;
5004         Folder *folder;
5005         VFolderInfo *info;
5006         VFolderURI vuri;
5007
5008         VFOLDER_URI_PARSE (uri, &vuri);
5009
5010         info = get_vfolder_info (vuri.scheme, &result, context);
5011         if (info == NULL)
5012                 return result;
5013
5014         G_LOCK (vfolder_lock);
5015         file_uri = desktop_uri_to_file_uri (info,
5016                                             &vuri,
5017                                             NULL /* the_entry */,
5018                                             NULL /* the_is_directory_file */,
5019                                             &folder,
5020                                             FALSE /* privatize */,
5021                                             &result,
5022                                             context);
5023         G_UNLOCK (vfolder_lock);
5024
5025         if (file_uri == NULL &&
5026             result != GNOME_VFS_ERROR_IS_DIRECTORY)
5027                 return result;
5028
5029         if (file_uri != NULL) {
5030                 /* we always get mime-type by forcing it below */
5031                 if (options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
5032                         options &= ~GNOME_VFS_FILE_INFO_GET_MIME_TYPE;
5033
5034                 result = (* parent_method->get_file_info) (parent_method,
5035                                                            file_uri,
5036                                                            file_info,
5037                                                            options,
5038                                                            context);
5039
5040                 g_free (file_info->mime_type);
5041                 file_info->mime_type = g_strdup ("application/x-gnome-app-info");
5042                 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
5043
5044                 /* Now we wipe those fields we don't support */
5045                 file_info->valid_fields &= ~(UNSUPPORTED_INFO_FIELDS);
5046
5047                 gnome_vfs_uri_unref (file_uri);
5048
5049                 return result;
5050         } else if (folder != NULL) {
5051                 file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
5052
5053                 file_info->name = g_strdup (folder->entry.name);
5054                 GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
5055
5056                 file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
5057                 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
5058
5059                 file_info->mime_type = g_strdup ("x-directory/vfolder-desktop");
5060                 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
5061
5062                 file_info->ctime = info->modification_time;
5063                 file_info->mtime = info->modification_time;
5064                 file_info->valid_fields |= (GNOME_VFS_FILE_INFO_FIELDS_CTIME |
5065                                             GNOME_VFS_FILE_INFO_FIELDS_MTIME);
5066
5067                 return GNOME_VFS_OK;
5068         } else {
5069                 return GNOME_VFS_ERROR_NOT_FOUND;
5070         }
5071 }
5072
5073 static GnomeVFSResult
5074 do_get_file_info_from_handle (GnomeVFSMethod *method,
5075                               GnomeVFSMethodHandle *method_handle,
5076                               GnomeVFSFileInfo *file_info,
5077                               GnomeVFSFileInfoOptions options,
5078                               GnomeVFSContext *context)
5079 {
5080         GnomeVFSResult result;
5081         FileHandle *handle = (FileHandle *)method_handle;
5082
5083         if (method_handle == (GnomeVFSMethodHandle *)method) {
5084                 g_free (file_info->mime_type);
5085                 file_info->mime_type = g_strdup ("text/plain");
5086                 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
5087                 return GNOME_VFS_OK;
5088         }
5089
5090         /* we always get mime-type by forcing it below */
5091         if (options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
5092                 options &= ~GNOME_VFS_FILE_INFO_GET_MIME_TYPE;
5093
5094         result = (* parent_method->get_file_info_from_handle) (parent_method,
5095                                                                handle->handle,
5096                                                                file_info,
5097                                                                options,
5098                                                                context);
5099
5100         /* any file is of the .desktop type */
5101         g_free (file_info->mime_type);
5102         file_info->mime_type = g_strdup ("application/x-gnome-app-info");
5103         file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
5104
5105         /* Now we wipe those fields we don't support */
5106         file_info->valid_fields &= ~(UNSUPPORTED_INFO_FIELDS);
5107
5108         return result;
5109 }
5110
5111
5112 static gboolean
5113 do_is_local (GnomeVFSMethod *method,
5114              const GnomeVFSURI *uri)
5115 {
5116         return TRUE;
5117 }
5118
5119 static void
5120 try_free_folder_monitors_create_unlocked (VFolderInfo *info,
5121                                           Folder *folder)
5122 {
5123         GSList *li, *list;
5124
5125         list = g_slist_copy (info->free_folder_monitors);
5126
5127         for (li = list; li != NULL; li = li->next) {
5128                 FileMonitorHandle *handle = li->data;
5129                 Folder *f;
5130                 VFolderURI vuri;
5131                 GnomeVFSResult result;
5132
5133                 /* Evil! EVIL URI PARSING. this will eat a lot of stack if we
5134                  * have lots of free monitors */
5135
5136                 VFOLDER_URI_PARSE (handle->uri, &vuri);
5137
5138                 f = resolve_folder (info, 
5139                                          vuri.path,
5140                                          FALSE /* ignore_basename */,
5141                                          &result,
5142                                          NULL);
5143
5144                 if (folder != f)
5145                         continue;
5146
5147                 info->free_folder_monitors =
5148                         g_slist_remove (info->free_folder_monitors, handle);
5149                 ((Entry *)folder)->monitors =
5150                         g_slist_prepend (((Entry *)folder)->monitors, handle);
5151
5152                 handle->exists = TRUE;
5153                 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *)handle,
5154                                             handle->uri, 
5155                                             GNOME_VFS_MONITOR_EVENT_CREATED);
5156         }
5157 }
5158
5159
5160 static GnomeVFSResult
5161 do_make_directory (GnomeVFSMethod *method,
5162                    GnomeVFSURI *uri,
5163                    guint perm,
5164                    GnomeVFSContext *context)
5165 {
5166         GnomeVFSResult result = GNOME_VFS_OK;
5167         VFolderInfo *info;
5168         Folder *parent, *folder;
5169         VFolderURI vuri;
5170
5171         VFOLDER_URI_PARSE (uri, &vuri);
5172
5173         if (vuri.is_all_scheme)
5174                 return GNOME_VFS_ERROR_READ_ONLY;
5175
5176         info = get_vfolder_info (vuri.scheme, &result, context);
5177         if (info == NULL)
5178                 return result;
5179
5180         if (info->user_filename == NULL ||
5181             info->read_only)
5182                 return GNOME_VFS_ERROR_READ_ONLY;
5183
5184         parent = resolve_folder (info, vuri.path,
5185                                  TRUE /* ignore_basename */,
5186                                  &result, context);
5187         if (parent == NULL)
5188                 return result;
5189         else if (parent->read_only)
5190                 return GNOME_VFS_ERROR_READ_ONLY;
5191
5192         G_LOCK (vfolder_lock);
5193
5194         folder = (Folder *)find_entry (parent->subfolders,
5195                                        vuri.file);
5196         if (folder != NULL) {
5197                 G_UNLOCK (vfolder_lock);
5198                 return GNOME_VFS_ERROR_FILE_EXISTS;
5199         }
5200
5201         folder = folder_new (vuri.file);
5202         parent->subfolders = g_slist_append (parent->subfolders, folder);
5203         folder->parent = parent;
5204         parent->up_to_date = FALSE;
5205
5206         try_free_folder_monitors_create_unlocked (info, folder);
5207
5208         /* parent changed */
5209         emit_monitor (parent, GNOME_VFS_MONITOR_EVENT_CHANGED);
5210
5211         vfolder_info_write_user (info);
5212         G_UNLOCK (vfolder_lock);
5213
5214         return GNOME_VFS_OK;
5215 }
5216
5217 static GnomeVFSResult
5218 do_remove_directory (GnomeVFSMethod *method,
5219                      GnomeVFSURI *uri,
5220                      GnomeVFSContext *context)
5221 {
5222         GnomeVFSResult result = GNOME_VFS_OK;
5223         Folder *folder;
5224         VFolderInfo *info;
5225         VFolderURI vuri;
5226
5227         VFOLDER_URI_PARSE (uri, &vuri);
5228
5229         if (vuri.is_all_scheme)
5230                 return GNOME_VFS_ERROR_READ_ONLY;
5231
5232         info = get_vfolder_info (vuri.scheme, &result, context);
5233         if (info == NULL)
5234                 return result;
5235
5236         if (info->user_filename == NULL ||
5237             info->read_only)
5238                 return GNOME_VFS_ERROR_READ_ONLY;
5239
5240         G_LOCK (vfolder_lock);
5241
5242         folder = resolve_folder (info, vuri.path,
5243                                  FALSE /* ignore_basename */,
5244                                  &result, context);
5245         if (folder == NULL) {
5246                 G_UNLOCK (vfolder_lock);
5247                 return result;
5248         }
5249
5250         if (folder->read_only ||
5251             (folder->parent != NULL &&
5252              folder->parent->read_only)) {
5253                 G_UNLOCK (vfolder_lock);
5254                 return GNOME_VFS_ERROR_READ_ONLY;
5255         }
5256
5257         /* don't make removing directories easy */
5258         if (folder->desktop_file != NULL) {
5259                 G_UNLOCK (vfolder_lock);
5260                 return GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY;
5261         }
5262
5263         /* Make sure we have the entries */
5264         ensure_folder_unlocked (info, folder,
5265                                 FALSE /* subfolders */,
5266                                 NULL /* except */,
5267                                 FALSE /* ignore_unallocated */);
5268
5269         /* don't make removing directories easy */
5270         if (folder->entries != NULL) {
5271                 G_UNLOCK (vfolder_lock);
5272                 return GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY;
5273         }
5274
5275         emit_and_delete_monitor (info, folder);
5276
5277         if (folder->only_unallocated) {
5278                 GSList *li = g_slist_find (info->unallocated_folders,
5279                                            folder);
5280                 if (li != NULL) {
5281                         info->unallocated_folders = g_slist_delete_link
5282                                 (info->unallocated_folders, li);
5283                         entry_unref ((Entry *)folder);
5284                 }
5285         }
5286
5287         if (folder == info->root) {
5288                 info->root = NULL;
5289                 entry_unref ((Entry *)folder);
5290                 info->root = folder_new ("Root");
5291         } else {
5292                 Folder *parent = folder->parent;
5293
5294                 g_assert (parent != NULL);
5295
5296                 parent->subfolders =
5297                         g_slist_remove (parent->subfolders, folder);
5298
5299                 parent->up_to_date = FALSE;
5300
5301                 entry_unref ((Entry *)folder);
5302
5303                 /* parent changed */
5304                 emit_monitor (parent, GNOME_VFS_MONITOR_EVENT_CHANGED);
5305         }
5306
5307         vfolder_info_write_user (info);
5308
5309         G_UNLOCK (vfolder_lock);
5310
5311         return GNOME_VFS_OK;
5312 }
5313
5314 /* a fairly evil function that does the whole move bit by copy and
5315  * remove */
5316 static GnomeVFSResult
5317 long_move (GnomeVFSMethod *method,
5318            VFolderURI *old_vuri,
5319            VFolderURI *new_vuri,
5320            gboolean force_replace,
5321            GnomeVFSContext *context)
5322 {
5323         GnomeVFSResult result;
5324         GnomeVFSMethodHandle *handle;
5325         GnomeVFSURI *file_uri;
5326         const char *path;
5327         int fd;
5328         char buf[BUFSIZ];
5329         int bytes;
5330         VFolderInfo *info;
5331
5332         info = get_vfolder_info (old_vuri->scheme, &result, context);
5333         if (info == NULL)
5334                 return result;
5335
5336         G_LOCK (vfolder_lock);
5337         file_uri = desktop_uri_to_file_uri (info,
5338                                             old_vuri,
5339                                             NULL /* the_entry */,
5340                                             NULL /* the_is_directory_file */,
5341                                             NULL /* the_folder */,
5342                                             FALSE /* privatize */,
5343                                             &result,
5344                                             context);
5345         G_UNLOCK (vfolder_lock);
5346
5347         if (file_uri == NULL)
5348                 return result;
5349
5350         path = gnome_vfs_uri_get_path (file_uri);
5351         if (path == NULL) {
5352                 gnome_vfs_uri_unref (file_uri);
5353                 return GNOME_VFS_ERROR_INVALID_URI;
5354         }
5355
5356         fd = open (path, O_RDONLY);
5357         if (fd < 0) {
5358                 gnome_vfs_uri_unref (file_uri);
5359                 return gnome_vfs_result_from_errno ();
5360         }
5361
5362         gnome_vfs_uri_unref (file_uri);
5363
5364         info->inhibit_write++;
5365
5366         result = method->create (method,
5367                                  &handle,
5368                                  new_vuri->uri,
5369                                  GNOME_VFS_OPEN_WRITE,
5370                                  force_replace /* exclusive */,
5371                                  0600 /* perm */,
5372                                  context);
5373         if (result != GNOME_VFS_OK) {
5374                 close (fd);
5375                 info->inhibit_write--;
5376                 return result;
5377         }
5378
5379         while ((bytes = read (fd, buf, BUFSIZ)) > 0) {
5380                 GnomeVFSFileSize bytes_written = 0;
5381                 result = method->write (method,
5382                                         handle,
5383                                         buf,
5384                                         bytes,
5385                                         &bytes_written,
5386                                         context);
5387                 if (result == GNOME_VFS_OK &&
5388                     bytes_written != bytes)
5389                         result = GNOME_VFS_ERROR_NO_SPACE;
5390                 if (result != GNOME_VFS_OK) {
5391                         close (fd);
5392                         method->close (method, handle, context);
5393                         /* FIXME: is this completely correct ? */
5394                         method->unlink (method,
5395                                         new_vuri->uri,
5396                                         context);
5397                         G_LOCK (vfolder_lock);
5398                         info->inhibit_write--;
5399                         vfolder_info_write_user (info);
5400                         G_UNLOCK (vfolder_lock);
5401                         return result;
5402                 }
5403         }
5404
5405         close (fd);
5406
5407         result = method->close (method, handle, context);
5408         if (result != GNOME_VFS_OK) {
5409                 G_LOCK (vfolder_lock);
5410                 info->inhibit_write--;
5411                 vfolder_info_write_user (info);
5412                 G_UNLOCK (vfolder_lock);
5413                 return result;
5414         }
5415
5416         result = method->unlink (method, old_vuri->uri, context);
5417
5418         G_LOCK (vfolder_lock);
5419         info->inhibit_write--;
5420         vfolder_info_write_user (info);
5421         G_UNLOCK (vfolder_lock);
5422
5423         return result;
5424 }
5425
5426 static GnomeVFSResult
5427 move_directory_file (VFolderInfo *info,
5428                      Folder *old_folder,
5429                      Folder *new_folder)
5430 {
5431         if (old_folder->desktop_file == NULL)
5432                 return GNOME_VFS_ERROR_NOT_FOUND;
5433
5434         /* "move" the desktop file */
5435         g_free (new_folder->desktop_file);
5436         new_folder->desktop_file = old_folder->desktop_file;
5437         old_folder->desktop_file = NULL;
5438
5439         /* is this too drastic, it will requery the folder? */
5440         new_folder->up_to_date = FALSE;
5441         old_folder->up_to_date = FALSE;
5442
5443         emit_monitor (new_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5444         emit_monitor (old_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5445
5446         vfolder_info_write_user (info);
5447
5448         return GNOME_VFS_OK;
5449 }
5450
5451 static gboolean
5452 is_sub (Folder *master, Folder *sub)
5453 {
5454         GSList *li;
5455
5456         for (li = master->subfolders; li != NULL; li = li->next) {
5457                 Folder *subfolder = li->data;
5458
5459                 if (subfolder == sub ||
5460                     is_sub (subfolder, sub))
5461                         return TRUE;
5462         }
5463
5464         return FALSE;
5465 }
5466
5467 static GnomeVFSResult
5468 move_folder (VFolderInfo *info,
5469              Folder *old_folder, Entry *old_entry,
5470              Folder *new_folder, Entry *new_entry)
5471 {
5472         Folder *source = (Folder *)old_entry;
5473         Folder *target;
5474
5475         if (new_entry != NULL &&
5476             new_entry->type != ENTRY_FOLDER)
5477                 return GNOME_VFS_ERROR_NOT_A_DIRECTORY;
5478         
5479         if (new_entry != NULL) {
5480                 target = (Folder *)new_entry;
5481         } else {
5482                 target = new_folder;
5483         }
5484
5485         /* move to where we are, yay, we're done :) */
5486         if (source->parent == target)
5487                 return GNOME_VFS_OK;
5488
5489         if (source == target ||
5490             is_sub (source, target))
5491                 return GNOME_VFS_ERROR_LOOP;
5492
5493         /* this will never happen, but we're paranoid */
5494         if (source->parent == NULL)
5495                 return GNOME_VFS_ERROR_LOOP;
5496
5497         source->parent->subfolders = g_slist_remove (source->parent->subfolders,
5498                                                      source);
5499         target->subfolders = g_slist_append (target->subfolders,
5500                                              source);
5501
5502         source->parent = target;
5503
5504         source->up_to_date = FALSE;
5505         target->up_to_date = FALSE;
5506
5507         emit_monitor (source, GNOME_VFS_MONITOR_EVENT_CHANGED);
5508         emit_monitor (target, GNOME_VFS_MONITOR_EVENT_CHANGED);
5509
5510         vfolder_info_write_user (info);
5511
5512         return GNOME_VFS_OK;
5513 }
5514
5515 static GnomeVFSResult
5516 do_move (GnomeVFSMethod *method,
5517          GnomeVFSURI *old_uri,
5518          GnomeVFSURI *new_uri,
5519          gboolean force_replace,
5520          GnomeVFSContext *context)
5521 {
5522         GnomeVFSResult result = GNOME_VFS_OK;
5523         VFolderInfo *info;
5524         Folder *old_folder, *new_folder;
5525         Entry *old_entry, *new_entry;
5526         gboolean old_is_directory_file, new_is_directory_file;
5527         VFolderURI old_vuri, new_vuri;
5528
5529         VFOLDER_URI_PARSE (old_uri, &old_vuri);
5530         VFOLDER_URI_PARSE (new_uri, &new_vuri);
5531
5532         if (old_vuri.file == NULL)
5533                 return GNOME_VFS_ERROR_INVALID_URI;
5534
5535         if (old_vuri.is_all_scheme)
5536                 return GNOME_VFS_ERROR_READ_ONLY;
5537
5538         if (strcmp (old_vuri.scheme, new_vuri.scheme) != 0)
5539                 return GNOME_VFS_ERROR_NOT_SAME_FILE_SYSTEM;
5540
5541         info = get_vfolder_info (old_vuri.scheme, &result, context);
5542         if (info == NULL)
5543                 return result;
5544
5545         if (info->read_only)
5546                 return GNOME_VFS_ERROR_READ_ONLY;
5547
5548         old_entry = get_entry (&old_vuri,
5549                                &old_folder,
5550                                &old_is_directory_file,
5551                                &result,
5552                                context);
5553         if (old_entry == NULL)
5554                 return result;
5555
5556         if (old_folder != NULL && old_folder->read_only)
5557                 return GNOME_VFS_ERROR_READ_ONLY;
5558
5559         new_entry = get_entry (&new_vuri,
5560                                &new_folder,
5561                                &new_is_directory_file,
5562                                &result,
5563                                context);
5564         if (new_entry == NULL && new_folder == NULL)
5565                 return result;
5566
5567         if (new_folder != NULL && new_folder->read_only)
5568                 return GNOME_VFS_ERROR_READ_ONLY;
5569
5570         if (new_is_directory_file != old_is_directory_file) {
5571                 /* this will do another set of lookups
5572                  * perhaps this can be done in a nicer way,
5573                  * but is this the common case? I don't think so */
5574                 return long_move (method, &old_vuri, &new_vuri,
5575                                   force_replace, context);
5576         }
5577         
5578         if (new_is_directory_file) {
5579                 g_assert (old_entry != NULL);
5580                 g_assert (new_entry != NULL);
5581                 G_LOCK (vfolder_lock);
5582                 result = move_directory_file (info,
5583                                               (Folder *)old_entry,
5584                                               (Folder *)new_entry);
5585                 G_UNLOCK (vfolder_lock);
5586                 return result;
5587         }
5588
5589         if (old_entry->type == ENTRY_FOLDER) {
5590                 G_LOCK (vfolder_lock);
5591                 result = move_folder (info,
5592                                       old_folder, old_entry,
5593                                       new_folder, new_entry);
5594                 G_UNLOCK (vfolder_lock);
5595                 return result;
5596         }
5597
5598         /* move into self, just whack the old one */
5599         if (old_entry == new_entry) {
5600                 /* same folder */
5601                 if (new_folder == old_folder)
5602                         return GNOME_VFS_OK;
5603
5604                 if ( ! force_replace)
5605                         return GNOME_VFS_ERROR_FILE_EXISTS;
5606
5607                 G_LOCK (vfolder_lock);
5608
5609                 remove_file (old_folder, old_vuri.file);
5610
5611                 old_folder->entries = g_slist_remove (old_folder->entries, 
5612                                                       old_entry);
5613                 entry_unref (old_entry);
5614
5615                 emit_monitor (old_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5616
5617                 vfolder_info_write_user (info);
5618
5619                 G_UNLOCK (vfolder_lock);
5620
5621                 return GNOME_VFS_OK;
5622         }
5623
5624         /* this is a simple move */
5625         if (new_entry == NULL ||
5626             new_entry->type == ENTRY_FOLDER) {
5627                 if (new_entry != NULL) {
5628                         new_folder = (Folder *)new_entry;
5629                 } else {
5630                         /* a file and a totally different one */
5631                         if (strcmp (new_vuri.file, old_entry->name) != 0) {
5632                                 /* yay, a long move */
5633                                 /* this will do another set of lookups
5634                                  * perhaps this can be done in a nicer way,
5635                                  * but is this the common case? I don't think
5636                                  * so */
5637                                 return long_move (method, &old_vuri, &new_vuri,
5638                                                   force_replace, context);
5639                         }
5640                 }
5641
5642                 /* same folder */
5643                 if (new_folder == old_folder)
5644                         return GNOME_VFS_OK;
5645
5646                 G_LOCK (vfolder_lock);
5647
5648                 remove_file (old_folder, old_entry->name);
5649                 add_file (new_folder, old_entry->name);
5650
5651                 new_folder->entries = g_slist_prepend (new_folder->entries, 
5652                                                        old_entry);
5653                 entry_ref (old_entry);
5654                 new_folder->sorted = FALSE;
5655
5656                 old_folder->entries = g_slist_remove (old_folder->entries, 
5657                                                       old_entry);
5658                 entry_unref (old_entry);
5659
5660                 emit_monitor (new_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5661                 emit_monitor (old_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5662
5663                 vfolder_info_write_user (info);
5664
5665                 G_UNLOCK (vfolder_lock);
5666
5667                 return GNOME_VFS_OK;
5668         }
5669
5670         /* do we EVER get here? */
5671
5672         /* this will do another set of lookups
5673          * perhaps this can be done in a nicer way,
5674          * but is this the common case? I don't think so */
5675         return long_move (method, &old_vuri, &new_vuri,
5676                           force_replace, context);
5677 }
5678
5679 static GnomeVFSResult
5680 do_unlink (GnomeVFSMethod *method,
5681            GnomeVFSURI *uri,
5682            GnomeVFSContext *context)
5683 {
5684         GnomeVFSResult result = GNOME_VFS_OK;
5685         Entry *entry;
5686         Folder *the_folder;
5687         gboolean is_directory_file;
5688         VFolderInfo *info;
5689         VFolderURI vuri;
5690         GSList *li;
5691
5692         VFOLDER_URI_PARSE (uri, &vuri);
5693
5694         if (vuri.file == NULL)
5695                 return GNOME_VFS_ERROR_INVALID_URI;
5696         
5697         if (vuri.is_all_scheme == TRUE)
5698                 return GNOME_VFS_ERROR_READ_ONLY;
5699
5700         info = get_vfolder_info (vuri.scheme, &result, context);
5701         if (info == NULL)
5702                 return result;
5703         else if (info->read_only)
5704                 return GNOME_VFS_ERROR_READ_ONLY;
5705
5706         entry = get_entry (&vuri,
5707                            &the_folder,
5708                            &is_directory_file,
5709                            &result, context);
5710         if (entry == NULL) 
5711                 return result;
5712         else if (the_folder != NULL &&
5713                  the_folder->read_only)
5714                 return GNOME_VFS_ERROR_READ_ONLY;
5715
5716         if (entry->type == ENTRY_FOLDER &&
5717             is_directory_file) {
5718                 Folder *folder = (Folder *)entry;
5719
5720                 if (folder->desktop_file == NULL)
5721                         return GNOME_VFS_ERROR_NOT_FOUND;
5722
5723                 G_LOCK (vfolder_lock);
5724
5725                 g_free (folder->desktop_file);
5726                 folder->desktop_file = NULL;
5727
5728                 emit_monitor (folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5729
5730                 vfolder_info_write_user (info);
5731
5732                 G_UNLOCK (vfolder_lock);
5733
5734                 return GNOME_VFS_OK;
5735         } else if (entry->type == ENTRY_FOLDER) {
5736                 return GNOME_VFS_ERROR_IS_DIRECTORY;
5737         } else if (the_folder == NULL) {
5738                 return GNOME_VFS_ERROR_NOT_FOUND;
5739         }
5740
5741         G_LOCK (vfolder_lock);
5742
5743         the_folder->entries = g_slist_remove (the_folder->entries,
5744                                               entry);
5745         entry_unref (entry);
5746
5747         remove_file (the_folder, vuri.file);
5748
5749         emit_monitor (the_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5750
5751         /* evil, we must remove this from the unallocated folders as well
5752          * so that it magically doesn't appear there.  But it's not so simple.
5753          * We only want to remove it if it isn't in that folder already. */
5754         for (li = info->unallocated_folders;
5755              li != NULL;
5756              li = li->next) {
5757                 Folder *folder = li->data;
5758                 GSList *l;
5759
5760                 /* This is actually really evil since ensuring 
5761                  * an unallocated folder clears all other unallocated
5762                  * folders in it's wake.  I'm not sure it's worth
5763                  * optimizing however */
5764                 ensure_folder_unlocked (info, folder,
5765                                         FALSE /* subfolders */,
5766                                         NULL /* except */,
5767                                         FALSE /* ignore_unallocated */);
5768                 l = g_slist_find (folder->entries, entry);
5769                 if (l == NULL) {
5770                         remove_file (folder, vuri.file);
5771                 }
5772         }
5773
5774         emit_file_deleted_monitor (info, entry, the_folder);
5775
5776         /* FIXME: if this was a user file and this is the only
5777          * reference to it, unlink it. */
5778
5779         vfolder_info_write_user (info);
5780
5781         G_UNLOCK (vfolder_lock);
5782
5783         return GNOME_VFS_OK;
5784 }
5785
5786 static GnomeVFSResult
5787 do_check_same_fs (GnomeVFSMethod *method,
5788                   GnomeVFSURI *source_uri,
5789                   GnomeVFSURI *target_uri,
5790                   gboolean *same_fs_return,
5791                   GnomeVFSContext *context)
5792 {
5793         VFolderURI source_vuri, target_vuri;
5794
5795         *same_fs_return = FALSE;
5796
5797         VFOLDER_URI_PARSE (source_uri, &source_vuri);
5798         VFOLDER_URI_PARSE (target_uri, &target_vuri);
5799
5800         if (strcmp (source_vuri.scheme, target_vuri.scheme) != 0 ||
5801             source_vuri.is_all_scheme != target_vuri.is_all_scheme)
5802                 *same_fs_return = FALSE;
5803         else
5804                 *same_fs_return = TRUE;
5805
5806         return GNOME_VFS_OK;
5807 }
5808
5809 static GnomeVFSResult
5810 do_set_file_info (GnomeVFSMethod *method,
5811                   GnomeVFSURI *uri,
5812                   const GnomeVFSFileInfo *info,
5813                   GnomeVFSSetFileInfoMask mask,
5814                   GnomeVFSContext *context)
5815 {
5816         VFolderURI vuri;
5817
5818         VFOLDER_URI_PARSE (uri, &vuri);
5819
5820         if (vuri.file == NULL)
5821                 return GNOME_VFS_ERROR_INVALID_URI;
5822
5823         if (mask & GNOME_VFS_SET_FILE_INFO_NAME) {
5824                 GnomeVFSResult result = GNOME_VFS_OK;
5825                 char *dirname = gnome_vfs_uri_extract_dirname (uri);
5826                 GnomeVFSURI *new_uri = gnome_vfs_uri_dup (uri);
5827
5828                 G_LOCK (vfolder_lock);
5829                 g_free (new_uri->text);
5830                 new_uri->text = g_build_path ("/", dirname, info->name, NULL);
5831                 G_UNLOCK (vfolder_lock);
5832
5833                 result = do_move (method,
5834                                   uri,
5835                                   new_uri,
5836                                   FALSE /* force_replace */,
5837                                   context);
5838
5839                 g_free (dirname);
5840                 gnome_vfs_uri_unref (new_uri);  
5841                 return result;
5842         } else {
5843                 /* We don't support setting any of this other permission,
5844                  * times and all that voodoo */
5845                 return GNOME_VFS_ERROR_NOT_SUPPORTED;
5846         }
5847 }
5848
5849 static GnomeVFSResult
5850 do_monitor_add (GnomeVFSMethod *method,
5851                 GnomeVFSMethodHandle **method_handle_return,
5852                 GnomeVFSURI *uri,
5853                 GnomeVFSMonitorType monitor_type)
5854 {
5855         VFolderInfo *info;
5856         VFolderURI vuri;
5857         GnomeVFSResult result;
5858         Folder *folder;
5859         Entry *entry;
5860         GnomeVFSURI *file_uri;
5861         FileMonitorHandle *handle;
5862         gboolean is_directory_file;
5863
5864         VFOLDER_URI_PARSE (uri, &vuri);
5865
5866         info = get_vfolder_info (vuri.scheme, &result, NULL);
5867         if (info == NULL)
5868                 return result;
5869
5870         if (monitor_type == GNOME_VFS_MONITOR_DIRECTORY) {
5871                 G_LOCK (vfolder_lock);
5872
5873                 folder = resolve_folder (info, 
5874                                          vuri.path,
5875                                          FALSE /* ignore_basename */,
5876                                          &result,
5877                                          NULL);
5878
5879                 handle = g_new0 (FileMonitorHandle, 1);
5880                 handle->refcount = 2;
5881                 handle->uri = gnome_vfs_uri_dup (uri);
5882                 handle->dir_monitor = TRUE;
5883                 handle->handle = NULL;
5884                 handle->filename = NULL;
5885
5886                 if (folder == NULL) {
5887                         handle->exists = FALSE;
5888                         info->free_folder_monitors = 
5889                                 g_slist_prepend (info->free_folder_monitors,
5890                                                  handle);
5891                 } else {
5892                         handle->exists = TRUE;
5893                         ((Entry *)folder)->monitors = 
5894                                 g_slist_prepend (((Entry *)folder)->monitors,
5895                                                  handle);
5896                 }
5897
5898                 info->folder_monitors = 
5899                         g_slist_prepend (info->folder_monitors, handle);
5900
5901                 G_UNLOCK (vfolder_lock);
5902
5903                 *method_handle_return = (GnomeVFSMethodHandle *) handle;
5904
5905                 return GNOME_VFS_OK;
5906         } else {
5907                 /* These can't be very nice FILE names */
5908                 if (vuri.file == NULL ||
5909                     vuri.ends_in_slash)
5910                         return GNOME_VFS_ERROR_INVALID_URI;
5911
5912                 G_LOCK (vfolder_lock);
5913                 file_uri = desktop_uri_to_file_uri (info,
5914                                                     &vuri,
5915                                                     &entry,
5916                                                     &is_directory_file,
5917                                                     NULL /* the_folder */,
5918                                                     FALSE,
5919                                                     &result,
5920                                                     NULL);
5921
5922                 handle = g_new0 (FileMonitorHandle, 1);
5923                 handle->refcount = 2;
5924                 handle->uri = gnome_vfs_uri_dup (uri);
5925                 handle->dir_monitor = FALSE;
5926                 handle->handle = NULL;
5927                 handle->filename = g_strdup (vuri.file);
5928                 handle->is_directory_file = is_directory_file;
5929
5930                 info->file_monitors = 
5931                         g_slist_prepend (info->file_monitors, handle);
5932
5933
5934                 if (file_uri == NULL) {
5935                         handle->exists = FALSE;
5936                         info->free_file_monitors = 
5937                                 g_slist_prepend (info->free_file_monitors,
5938                                                  handle);
5939                 } else {
5940                         char *uri_string = gnome_vfs_uri_to_string (file_uri, 0);
5941                         handle->exists = TRUE;
5942                         gnome_vfs_monitor_add (&(handle->handle),
5943                                                uri_string,
5944                                                GNOME_VFS_MONITOR_FILE,
5945                                                file_monitor,
5946                                                handle);
5947                         g_free (uri_string);
5948                         
5949                         entry->monitors = g_slist_prepend (entry->monitors,
5950                                                            handle);
5951                         gnome_vfs_uri_unref (file_uri);
5952                 }
5953
5954                 *method_handle_return = (GnomeVFSMethodHandle *) handle;
5955
5956                 G_UNLOCK (vfolder_lock);
5957
5958                 return GNOME_VFS_OK;
5959         }
5960 }
5961
5962 static GnomeVFSResult
5963 do_monitor_cancel (GnomeVFSMethod *method,
5964                    GnomeVFSMethodHandle *method_handle)
5965 {
5966         FileMonitorHandle *handle;
5967         VFolderInfo *info;
5968         VFolderURI vuri;
5969         GnomeVFSResult result;
5970         Folder *folder;
5971         GSList *li;
5972
5973         handle = (FileMonitorHandle *)method_handle;
5974
5975         /* FIXME: is this correct? */
5976         if (method_handle == NULL)
5977                 return GNOME_VFS_OK;
5978
5979         VFOLDER_URI_PARSE (handle->uri, &vuri);
5980
5981         info = get_vfolder_info (vuri.scheme, &result, NULL);
5982         if (info == NULL)
5983                 return result;
5984
5985         if (handle->dir_monitor) {
5986                 G_LOCK (vfolder_lock);
5987
5988                 folder = resolve_folder (info, 
5989                                          vuri.path,
5990                                          FALSE /* ignore_basename */,
5991                                          &result,
5992                                          NULL);
5993
5994                 for (li = info->folder_monitors; li != NULL; li = li->next) {
5995                         FileMonitorHandle *h = li->data;
5996                         if (h != handle)
5997                                 continue;
5998                         info->folder_monitors = g_slist_delete_link
5999                                 (info->folder_monitors, li);
6000                         file_monitor_handle_unref_unlocked (h);
6001                         break;
6002                 }
6003
6004                 if (folder == NULL) {
6005                         for (li = info->free_folder_monitors;
6006                              li != NULL;
6007                              li = li->next) {
6008                                 FileMonitorHandle *h = li->data;
6009                                 if (h != handle)
6010                                         continue;
6011                                 info->free_folder_monitors = g_slist_delete_link
6012                                         (info->free_folder_monitors, li);
6013                                 file_monitor_handle_unref_unlocked (h);
6014                                 break;
6015                         }
6016                 } else {
6017                         for (li = ((Entry *)folder)->monitors;
6018                              li != NULL;
6019                              li = li->next) {
6020                                 FileMonitorHandle *h = li->data;
6021                                 if (h != handle)
6022                                         continue;
6023                                 ((Entry *)folder)->monitors =
6024                                         g_slist_delete_link
6025                                         (((Entry *)folder)->monitors, li);
6026                                 file_monitor_handle_unref_unlocked (h);
6027                                 break;
6028                         }
6029                 }
6030
6031                 G_UNLOCK (vfolder_lock);
6032
6033                 return GNOME_VFS_OK;
6034         } else {
6035                 G_LOCK (vfolder_lock);
6036
6037                 for (li = info->file_monitors; li != NULL; li = li->next) {
6038                         FileMonitorHandle *h = li->data;
6039                         if (h != handle)
6040                                 continue;
6041                         info->file_monitors = g_slist_delete_link
6042                                 (info->file_monitors, li);
6043                         file_monitor_handle_unref_unlocked (h);
6044                         break;
6045                 }
6046
6047                 for (li = info->free_file_monitors;
6048                      li != NULL;
6049                      li = li->next) {
6050                         FileMonitorHandle *h = li->data;
6051                         if (h != handle)
6052                                 continue;
6053                         info->free_file_monitors = g_slist_delete_link
6054                                 (info->free_file_monitors, li);
6055                         file_monitor_handle_unref_unlocked (h);
6056                         break;
6057                 }
6058
6059                 for (li = info->entries; li != NULL; li = li->next) {
6060                         Entry *e = li->data;
6061                         GSList *link = g_slist_find (e->monitors, handle);
6062
6063                         if (link == NULL)
6064                                 continue;
6065                         link->data = NULL;
6066                         e->monitors = g_slist_delete_link (e->monitors, link);
6067
6068                         file_monitor_handle_unref_unlocked (handle);
6069                         break;
6070                 }
6071
6072                 G_UNLOCK (vfolder_lock);
6073
6074                 /* Note: last unref of our monitor will cancel the
6075                  * underlying handle */
6076
6077                 return GNOME_VFS_OK;
6078         }
6079 }
6080
6081 \f
6082 /* gnome-vfs bureaucracy */
6083
6084 static GnomeVFSMethod method = {
6085         sizeof (GnomeVFSMethod),
6086         do_open,
6087         do_create,
6088         do_close,
6089         do_read,
6090         do_write,
6091         do_seek,
6092         do_tell,
6093         do_truncate_handle,
6094         do_open_directory,
6095         do_close_directory,
6096         do_read_directory,
6097         do_get_file_info,
6098         do_get_file_info_from_handle,
6099         do_is_local,
6100         do_make_directory,
6101         do_remove_directory,
6102         do_move,
6103         do_unlink,
6104         do_check_same_fs,
6105         do_set_file_info,
6106         do_truncate,
6107         NULL /* find_directory */,
6108         NULL /* create_symbolic_link */,
6109         do_monitor_add,
6110         do_monitor_cancel
6111 };
6112
6113 GnomeVFSMethod *
6114 vfs_module_init (const char *method_name, 
6115                  const char *args)
6116 {
6117         parent_method = gnome_vfs_method_get ("file");
6118
6119         if (parent_method == NULL) {
6120                 g_error ("Could not find 'file' method for gnome-vfs");
6121                 return NULL;
6122         }
6123
6124         return &method;
6125 }
6126
6127 void
6128 vfs_module_shutdown (GnomeVFSMethod *method)
6129 {
6130         if (infos == NULL)
6131                 return;
6132
6133         g_hash_table_destroy (infos);
6134         infos = NULL;
6135 }