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