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.moved-menu-files
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: 8; c-basic-offset: 8 -*- */
2
3 /* vfolder-desktop-method.c
4
5    Copyright (C) 2001 Red Hat, Inc.
6    Copyright (C) 2001 The Dark Prince
7
8    The Gnome Library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Library General Public License as
10    published by the Free Software Foundation; either version 2 of the
11    License, or (at your option) any later version.
12
13    The Gnome Library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Library General Public License for more details.
17
18    You should have received a copy of the GNU Library General Public
19    License along with the Gnome Library; see the file COPYING.LIB.  If not,
20    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21    Boston, MA 02111-1307, USA.
22 */
23
24 /* URI scheme for reading the "applications:" vfolder and other
25  * vfolder schemes.  Lots of code stolen from the original desktop
26  * reading URI scheme.
27  */
28
29 #ifdef HAVE_CONFIG_H
30 # include <config.h>
31 #endif
32
33 /* Debugging foo: */
34 /*#define D(x) x */
35 #define D(x) ;
36
37 #include <glib.h>
38 #include <sys/types.h>
39 #include <dirent.h>
40 #include <errno.h>
41 #include <string.h>
42 #include <time.h>
43 #include <sys/stat.h>
44 #include <unistd.h>
45 #include <fcntl.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48
49 #include <libxml/parser.h>
50 #include <libxml/tree.h>
51 #include <libxml/xmlmemory.h>
52
53 #include <libgnomevfs/gnome-vfs-mime.h>
54
55 #include <libgnomevfs/gnome-vfs-module.h>
56 #include <libgnomevfs/gnome-vfs-method.h>
57 #include <libgnomevfs/gnome-vfs-utils.h>
58 #include <libgnomevfs/gnome-vfs-ops.h>
59 #include <libgnomevfs/gnome-vfs-module-shared.h>
60 #include <libgnomevfs/gnome-vfs-monitor-private.h>
61
62 #define DOT_GNOME ".gnome2"
63
64 typedef struct _VFolderInfo VFolderInfo;
65 typedef struct _Query Query;
66 typedef struct _QueryKeyword QueryKeyword;
67 typedef struct _QueryFilename QueryFilename;
68 typedef struct _Entry Entry;
69 typedef struct _Folder Folder;
70 typedef struct _EntryFile EntryFile;
71 typedef struct _Keyword Keyword;
72 typedef struct _FileMonitorHandle FileMonitorHandle;
73 typedef struct _StatLoc StatLoc;
74 typedef struct _VFolderURI VFolderURI;
75
76 /* TODO before 2.0: */
77 /* FIXME: also check/monitor desktop_dirs like we do the vfolder
78  * file and the item dirs */
79 /* FIXME: check if thread locks are not completely on crack which
80  * is likely given my experience with threads */
81 /* FIXME: use filename locking, currently we are full of races if
82  * multiple processes write to this filesystem */
83 /* FIXME: implement monitors */
84
85 /* TODO for later (star trek future): */
86 /* FIXME: Maybe when chaining to file:, we should call the gnome-vfs wrapper
87  * functions, instead of the file: methods directly.  */
88 /* FIXME: related to the above: we should support things being on non
89  * file: filesystems.  Such as having the vfolder info file on http
90  * somewhere or some such nonsense :) */
91
92 static GnomeVFSMethod *parent_method = NULL;
93
94 static GHashTable *infos = NULL;
95
96 /* Note: I have no clue about how to write thread safe code and this
97  * is my first attempt, so it's probably wrong
98  * -George */
99 G_LOCK_DEFINE_STATIC (vfolder_lock);
100
101 /* Note: all keywords are quarks */
102 /* Note: basenames are unique */
103
104 #define UNSUPPORTED_INFO_FIELDS (GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS | \
105                                  GNOME_VFS_FILE_INFO_FIELDS_DEVICE | \
106                                  GNOME_VFS_FILE_INFO_FIELDS_INODE | \
107                                  GNOME_VFS_FILE_INFO_FIELDS_LINK_COUNT | \
108                                  GNOME_VFS_FILE_INFO_FIELDS_ATIME)
109
110
111 enum {
112         QUERY_OR,
113         QUERY_AND,
114         QUERY_KEYWORD,
115         QUERY_FILENAME
116 };
117
118 struct _Query {
119         int type;
120         gboolean not;
121         GSList *queries;
122 };
123
124 struct _QueryKeyword {
125         int type;
126         gboolean not;
127         GQuark keyword;
128 };
129
130 struct _QueryFilename {
131         int type;
132         gboolean not;
133         char *filename;
134 };
135
136 enum {
137         ENTRY_FILE,
138         ENTRY_FOLDER
139 };
140
141 struct _Entry {
142         int type;
143         int refcount;
144         int alloc; /* not really useful for folders,
145                       but oh well, whatever.  It's the number
146                       of times this is queried in some directory,
147                       used for the Unallocated query type */
148         char *name;
149
150         GSList *monitors;
151 };
152
153 struct _EntryFile {
154         Entry entry;
155
156         char *filename;
157         gboolean per_user;
158         GSList *keywords;
159
160         gboolean implicit_keywords; /* the keywords were added by us */
161 };
162
163 struct _Folder {
164         Entry entry;
165
166         Folder *parent;
167
168         char *desktop_file; /* the .directory file */
169
170         Query *query;
171
172         /* The following is for per file
173          * access */
174         /* excluded by filename */
175         GHashTable *excludes;
176         /* included by filename */
177         GSList *includes;
178         GHashTable *includes_ht;
179
180         GSList *subfolders;
181
182         /* Some flags */
183         gboolean read_only;
184         gboolean dont_show_if_empty;
185         gboolean only_unallocated; /* include only unallocated items */
186
187         /* lazily done, will run query only when it
188          * needs to */
189         gboolean up_to_date;
190         gboolean sorted;
191         GSList *entries;
192 };
193
194 struct _StatLoc {
195         time_t ctime;
196         time_t last_stat;
197         gboolean trigger_next; /* if true, next check will fail */
198         char name[1]; /* the structure will be long enough to
199                          fit name */
200 };
201
202 struct _VFolderInfo {
203         char *scheme;
204
205         char *filename;
206         char *user_filename;
207         time_t user_filename_last_write;
208         char *desktop_dir; /* directory with .directorys */
209         char *user_desktop_dir; /* directory with .directorys */
210         gboolean user_file_active; /* if using user_filename and
211                                       not filename */
212
213         GSList *item_dirs;
214         char *user_item_dir; /* dir where user changes to
215                                 items are stored */
216
217         /* old style dirs to merge in */
218         GSList *merge_dirs;
219
220         /* if entries are valid, else
221          * they need to be (re)read */
222         gboolean entries_valid;
223
224         GSList *entries;
225
226         /* entry hash by basename */
227         GHashTable *entries_ht;
228
229         /* The root folder */
230         Folder *root;
231
232         /* The unallocated folders, the folder which only
233          * include unallocated items */
234         GSList *unallocated_folders;
235
236         /* some flags */
237         gboolean read_only;
238
239         gboolean dirty;
240
241         int inhibit_write;
242
243         /* change monitoring stuff */
244         GnomeVFSMonitorHandle *filename_monitor;
245         GnomeVFSMonitorHandle *user_filename_monitor;
246
247         /* stat locations (in case we aren't monitoring) */
248         StatLoc *filename_statloc;
249         StatLoc *user_filename_statloc;
250
251         /* for .directory dirs */
252         /* FIXME: */GnomeVFSMonitorHandle *desktop_dir_monitor;
253         /* FIXME: */GnomeVFSMonitorHandle *user_desktop_dir_monitor;
254
255         /* stat locations (in case we aren't monitoring) */
256         /* FIXME: */StatLoc *desktop_dir_statloc;
257         /* FIXME: */StatLoc *user_desktop_dir_statloc;
258
259         /* FIXME: */GSList *file_monitors; /* FileMonitorHandle */
260         /* FIXME: */GSList *free_file_monitors; /* FileMonitorHandle */
261         GSList *folder_monitors; /* FileMonitorHandle */
262         GSList *free_folder_monitors; /* FileMonitorHandle */
263
264         GSList *item_dir_monitors; /* GnomeVFSMonitorHandle */
265
266         /* item dirs to stat */
267         GSList *stat_dirs;
268
269         /* ctime for folders */
270         time_t modification_time;
271
272         guint reread_queue;
273 };
274
275 struct _FileMonitorHandle {
276         int refcount;
277         gboolean exists;
278         gboolean dir_monitor; /* TRUE if dir, FALSE if file */
279         GnomeVFSURI *uri;
280         GnomeVFSMonitorHandle *handle; /* A real handle if we're monitoring
281                                           an actual file here, or NULL */
282         char *filename; /* Just the basename, used in the free_file_list */
283         gboolean is_directory_file;
284 };
285
286 struct _VFolderURI {
287         const gchar *scheme;
288         gboolean     is_all_scheme;
289         gboolean     ends_in_slash;
290         gchar       *path;
291         gchar       *file;
292         GnomeVFSURI *uri;
293 };
294
295
296 static Entry *  entry_ref                       (Entry *entry);
297 static Entry *  entry_ref_alloc                 (Entry *entry);
298 static void     entry_unref                     (Entry *entry);
299 static void     entry_unref_dealloc             (Entry *entry);
300 static void     query_destroy                   (Query *query);
301 static void     ensure_folder                   (VFolderInfo *info,
302                                                  Folder *folder,
303                                                  gboolean subfolders,
304                                                  Folder *except,
305                                                  gboolean ignore_unallocated);
306 static void     ensure_folder_unlocked          (VFolderInfo *info,
307                                                  Folder *folder,
308                                                  gboolean subfolders,
309                                                  Folder *except,
310                                                  gboolean ignore_unallocated);
311 /* An EVIL function for quick reading of .desktop files,
312  * only reads in one or two keys, but that's ALL we need */
313 static void     readitem_entry                  (const char *filename,
314                                                  const char *key1,
315                                                  char **result1,
316                                                  const char *key2,
317                                                  char **result2);
318 static gboolean vfolder_info_reload             (VFolderInfo *info,
319                                                  GnomeVFSResult *result,
320                                                  GnomeVFSContext *context,
321                                                  gboolean force_read_items);
322 static gboolean vfolder_info_reload_unlocked    (VFolderInfo *info,
323                                                  GnomeVFSResult *result,
324                                                  GnomeVFSContext *context,
325                                                  gboolean force_read_items);
326 static void     invalidate_folder_subfolders    (Folder   *folder,
327                                                  gboolean  lock_taken);
328 static Folder * resolve_folder                  (VFolderInfo *info,
329                                                  const char *path,
330                                                  gboolean ignore_basename,
331                                                  GnomeVFSResult *result,
332                                                  GnomeVFSContext *context);
333 static gboolean vfolder_info_read_items         (VFolderInfo *info,
334                                                  GnomeVFSResult *result,
335                                                  GnomeVFSContext *context);
336
337 /* assumes vuri->path already set */
338 static gboolean
339 vfolder_uri_parse_internal (GnomeVFSURI *uri, VFolderURI *vuri)
340 {
341         vuri->scheme = (gchar *) gnome_vfs_uri_get_scheme (uri);
342
343         vuri->ends_in_slash = FALSE;
344
345         if (strncmp (vuri->scheme, "all-", strlen ("all-")) == 0) {
346                 vuri->scheme += strlen ("all-");
347                 vuri->is_all_scheme = TRUE;
348         } else
349                 vuri->is_all_scheme = FALSE;
350
351         if (vuri->path != NULL) {
352                 int last_slash = strlen (vuri->path) - 1;
353                 char *first;
354
355                 /* Note: This handling of paths is somewhat evil, may need a
356                  * bit of a rework */
357
358                 /* kill leading slashes, that is make sure there is
359                  * only one */
360                 for (first = vuri->path; *first == '/'; first++)
361                         ;
362                 if (first != vuri->path) {
363                         first--;
364                         vuri->path = first;
365                 }
366
367                 /* kill trailing slashes (leave first if all slashes) */
368                 while (last_slash > 0 && vuri->path [last_slash] == '/') {
369                         vuri->path [last_slash--] = '\0';
370                         vuri->ends_in_slash = TRUE;
371                 }
372
373                 /* get basename start */
374                 while (last_slash >= 0 && vuri->path [last_slash] != '/')
375                         last_slash--;
376
377                 if (last_slash > -1)
378                         vuri->file = vuri->path + last_slash + 1;
379                 else
380                         vuri->file = vuri->path;
381
382                 if (vuri->file[0] == '\0' &&
383                     strcmp (vuri->path, "/") == 0) {
384                         vuri->file = NULL;
385                 }
386         } else {
387                 vuri->ends_in_slash = TRUE;
388                 vuri->path = "/";
389                 vuri->file = NULL;
390         }
391
392         vuri->uri = uri;
393
394         return TRUE;
395 }
396
397 #define VFOLDER_URI_PARSE(_uri, _vuri) {                                    \
398         gchar *path;                                                        \
399         path = gnome_vfs_unescape_string ((_uri)->text, G_DIR_SEPARATOR_S); \
400         if (path != NULL) {                                                 \
401                 (_vuri)->path = g_alloca (strlen (path) + 1);               \
402                 strcpy ((_vuri)->path, path);                               \
403                 g_free (path);                                              \
404         } else {                                                            \
405                 (_vuri)->path = NULL;                                       \
406         }                                                                   \
407         vfolder_uri_parse_internal ((_uri), (_vuri));                       \
408 }
409
410 static FileMonitorHandle *
411 file_monitor_handle_ref_unlocked (FileMonitorHandle *h)
412 {
413         h->refcount ++;
414         return h;
415 }
416
417 static void
418 file_monitor_handle_unref_unlocked (FileMonitorHandle *h)
419 {
420         h->refcount --;
421         if (h->refcount == 0) {
422                 gnome_vfs_uri_unref (h->uri);
423                 h->uri = NULL;
424
425                 g_free (h->filename);
426                 h->filename = NULL;
427
428                 if (h->handle != NULL) {
429                         gnome_vfs_monitor_cancel (h->handle);
430                         h->handle = NULL;
431                 }
432         }
433 }
434
435 /* This includes the .directory files */
436 static void
437 emit_monitor (Folder *folder, int type)
438 {
439         GSList *li;
440         for (li = ((Entry *)folder)->monitors;
441              li != NULL;
442              li = li->next) {
443                 FileMonitorHandle *handle = li->data;
444                 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *) handle,
445                                             handle->uri, type);
446         }
447 }
448
449 static void
450 emit_file_deleted_monitor (VFolderInfo *info, Entry *entry, Folder *folder)
451 {
452         GSList *li;
453         for (li = entry->monitors;
454              li != NULL;
455              li = li->next) {
456                 VFolderURI vuri;
457                 Folder *f;
458                 GnomeVFSResult result;
459                 FileMonitorHandle *handle = li->data;
460
461                 /* Evil! EVIL URI PARSING. this will eat a lot of
462                  * stack if we have lots of monitors */
463
464                 VFOLDER_URI_PARSE (handle->uri, &vuri);
465
466                 f = resolve_folder (info, 
467                                     vuri.path,
468                                     TRUE /* ignore_basename */,
469                                     &result,
470                                     NULL);
471
472                 if (f == folder)
473                         gnome_vfs_monitor_callback
474                                 ((GnomeVFSMethodHandle *) handle,
475                                  handle->uri,
476                                  GNOME_VFS_MONITOR_EVENT_DELETED);
477         }
478 }
479
480 static void
481 emit_and_delete_monitor (VFolderInfo *info, Folder *folder)
482 {
483         GSList *li;
484         for (li = ((Entry *)folder)->monitors;
485              li != NULL;
486              li = li->next) {
487                 FileMonitorHandle *handle = li->data;
488                 li->data = NULL;
489
490                 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *) handle,
491                                             handle->uri,
492                                             GNOME_VFS_MONITOR_EVENT_DELETED);
493
494                 if (handle->dir_monitor)
495                         info->free_folder_monitors = 
496                                 g_slist_prepend (info->free_folder_monitors,
497                                                  handle);
498                 else
499                         info->free_file_monitors = 
500                                 g_slist_prepend (info->free_file_monitors,
501                                                  handle);
502         }
503         g_slist_free (((Entry *)folder)->monitors);
504         ((Entry *)folder)->monitors = NULL;
505 }
506
507 static gboolean
508 check_ext (const char *name, const char *ext_check)
509 {
510         const char *ext;
511
512         ext = strrchr (name, '.');
513         if (ext == NULL ||
514             strcmp (ext, ext_check) != 0)
515                 return FALSE;
516         else
517                 return TRUE;
518 }
519
520 static StatLoc *
521 bake_statloc (const char *name,
522               time_t curtime)
523 {
524         struct stat s;
525         StatLoc *sl = NULL;
526         if (stat (name, &s) != 0) {
527                 if (errno == ENOENT) {
528                         sl = g_malloc0 (sizeof (StatLoc) +
529                                         strlen (name) + 1);
530                         sl->last_stat = curtime;
531                         sl->ctime = 0;
532                         sl->trigger_next = FALSE;
533                         strcpy (sl->name, name);
534                 }
535                 return sl;
536         }
537
538         sl = g_malloc0 (sizeof (StatLoc) +
539                         strlen (name) + 1);
540         sl->last_stat = curtime;
541         sl->ctime = s.st_ctime;
542         sl->trigger_next = FALSE;
543         strcpy (sl->name, name);
544
545         return sl;
546 }
547
548 /* returns FALSE if we must reread */
549 static gboolean
550 check_statloc (StatLoc *sl,
551                time_t curtime)
552 {
553         struct stat s;
554
555         if (sl->trigger_next) {
556                 sl->trigger_next = FALSE;
557                 return FALSE;
558         }
559
560         /* don't stat more then once every 3 seconds */
561         if (curtime <= sl->last_stat + 3)
562                 return TRUE;
563
564         sl->last_stat = curtime;
565
566         if (stat (sl->name, &s) != 0) {
567                 if (errno == ENOENT &&
568                     sl->ctime == 0)
569                         return TRUE;
570                 else
571                         return FALSE;
572         }
573
574         if (sl->ctime == s.st_ctime)
575                 return TRUE;
576         else
577                 return FALSE;
578 }
579
580 static gboolean
581 ensure_dir (const char *dirname, gboolean ignore_basename)
582 {
583         char *parsed, *p;
584
585         if (dirname == NULL)
586                 return FALSE;
587
588         if (ignore_basename)
589                 parsed = g_path_get_dirname (dirname);
590         else
591                 parsed = g_strdup (dirname);
592
593         if (g_file_test (parsed, G_FILE_TEST_IS_DIR)) {
594                 g_free (parsed);
595                 return TRUE;
596         }
597
598         p = strchr (parsed, '/');
599         if (p == parsed)
600                 p = strchr (p+1, '/');
601
602         while (p != NULL) {
603                 *p = '\0';
604                 if (mkdir (parsed, 0700) != 0 &&
605                     errno != EEXIST) {
606                         g_free (parsed);
607                         return FALSE;
608                 }
609                 *p = '/';
610                 p = strchr (p+1, '/');
611         }
612
613         if (mkdir (parsed, 0700) != 0 &&
614             errno != EEXIST) {
615                 g_free (parsed);
616                 return FALSE;
617         }
618
619         g_free (parsed);
620         return TRUE;
621 }
622
623 /* check for any directory name other then root */
624 static gboolean
625 any_subdir (const char *dirname)
626 {
627         const char *p;
628         if (dirname == NULL)
629                 return FALSE;
630
631         for (p = dirname; *p != '\0'; p++) {
632                 if (*p != '/') {
633                         return TRUE;
634                 }
635         }
636         return FALSE;
637 }
638
639 static void
640 destroy_entry_file (EntryFile *efile)
641 {
642         if (efile == NULL)
643                 return;
644
645         g_free (efile->filename);
646         efile->filename = NULL;
647
648         g_slist_free (efile->keywords);
649         efile->keywords = NULL;
650
651         g_free (efile);
652 }
653
654 static void
655 destroy_folder (Folder *folder)
656 {
657         GSList *list;
658
659         if (folder == NULL)
660                 return;
661
662         if (folder->parent != NULL) {
663                 folder->parent->subfolders =
664                         g_slist_remove (folder->parent->subfolders, folder);
665                 folder->parent->up_to_date = FALSE;
666                 folder->parent = NULL;
667         }
668
669         g_free (folder->desktop_file);
670         folder->desktop_file = NULL;
671
672         query_destroy (folder->query);
673         folder->query = NULL;
674
675         if (folder->excludes != NULL) {
676                 g_hash_table_destroy (folder->excludes);
677                 folder->excludes = NULL;
678         }
679
680         g_slist_foreach (folder->includes, (GFunc)g_free, NULL);
681         g_slist_free (folder->includes);
682         folder->includes = NULL;
683         if (folder->includes_ht != NULL) {
684                 g_hash_table_destroy (folder->includes_ht);
685                 folder->includes_ht = NULL;
686         }
687
688         list = folder->subfolders;
689         folder->subfolders = NULL;
690         g_slist_foreach (list, (GFunc)entry_unref, NULL);
691         g_slist_free (list);
692
693         list = folder->entries;
694         folder->entries = NULL;
695         g_slist_foreach (list, (GFunc)entry_unref, NULL);
696         g_slist_free (list);
697
698         g_free (folder);
699 }
700
701 static Entry *
702 entry_ref (Entry *entry)
703 {
704         if (entry != NULL)
705                 entry->refcount++;
706         return entry;
707 }
708
709 static Entry *
710 entry_ref_alloc (Entry *entry)
711 {
712         entry_ref (entry);
713
714         if (entry != NULL)
715                 entry->alloc++;
716
717         return entry;
718 }
719
720 static void
721 entry_unref (Entry *entry)
722 {
723         if (entry == NULL)
724                 return;
725
726         entry->refcount--;
727
728         if (entry->refcount == 0) {
729                 g_free (entry->name);
730                 entry->name = NULL;
731
732                 g_slist_foreach (entry->monitors,
733                                  (GFunc)file_monitor_handle_unref_unlocked,
734                                  NULL);
735                 g_slist_free (entry->monitors);
736                 entry->monitors = NULL;
737
738                 if (entry->type == ENTRY_FILE)
739                         destroy_entry_file ((EntryFile *)entry);
740                 else /* ENTRY_FOLDER */
741                         destroy_folder ((Folder *)entry);
742         }
743 }
744
745 static void
746 entry_unref_dealloc (Entry *entry)
747 {
748         if (entry != NULL) {
749                 entry->alloc --;
750                 entry_unref (entry);
751         }
752 }
753
754 /* Handles ONLY files, not dirs */
755 /* Also allocates the entries as well as refs them */
756 static GSList *
757 alloc_entries_from_files (VFolderInfo *info, GSList *filenames)
758 {
759         GSList *li;
760         GSList *files;
761
762         files = NULL;
763         for (li = filenames; li != NULL; li = li->next) {
764                 char *filename = li->data;
765                 GSList *entry_list = g_hash_table_lookup (info->entries_ht, filename);
766                 if (entry_list != NULL)
767                         files = g_slist_prepend (files,
768                                                  entry_ref_alloc (entry_list->data));
769         }
770
771         return files;
772 }
773
774 static gboolean
775 matches_query (VFolderInfo *info,
776                Folder *folder,
777                EntryFile *efile,
778                Query *query)
779 {
780         GSList *li;
781
782         if (query == NULL)
783                 return TRUE;
784
785 #define INVERT_IF_NEEDED(val) (query->not ? !(val) : (val))
786         switch (query->type) {
787         case QUERY_OR:
788                 for (li = query->queries; li != NULL; li = li->next) {
789                         Query *subquery = li->data;
790                         if (matches_query (info, folder, efile, subquery))
791                                 return INVERT_IF_NEEDED (TRUE);
792                 }
793                 return INVERT_IF_NEEDED (FALSE);
794         case QUERY_AND:
795                 for (li = query->queries; li != NULL; li = li->next) {
796                         Query *subquery = li->data;
797                         if ( ! matches_query (info, folder, efile, subquery))
798                                 return INVERT_IF_NEEDED (FALSE);
799                 }
800                 return INVERT_IF_NEEDED (TRUE);
801         case QUERY_KEYWORD:
802                 {
803                         QueryKeyword *qkeyword = (QueryKeyword *)query;
804                         for (li = efile->keywords; li != NULL; li = li->next) {
805                                 GQuark keyword = GPOINTER_TO_INT (li->data);
806                                 if (keyword == qkeyword->keyword)
807                                         return INVERT_IF_NEEDED (TRUE);
808                         }
809                         return INVERT_IF_NEEDED (FALSE);
810                 }
811         case QUERY_FILENAME:
812                 {
813                         QueryFilename *qfilename = (QueryFilename *)query;
814                         if (strcmp (qfilename->filename, ((Entry *)efile)->name) == 0) {
815                                 return INVERT_IF_NEEDED (TRUE);
816                         } else {
817                                 return INVERT_IF_NEEDED (FALSE);
818                         }
819                 }
820         }
821 #undef INVERT_IF_NEEDED
822         g_assert_not_reached ();
823         /* huh? */
824         return FALSE;
825 }
826
827 static void
828 dump_unallocated_folders (Folder *folder)
829 {
830         GSList *li;
831         for (li = folder->subfolders; li != NULL; li = li->next)
832                 dump_unallocated_folders (li->data);
833
834         if (folder->only_unallocated &&
835             folder->entries != NULL) {
836                 g_slist_foreach (folder->entries,
837                                  (GFunc)entry_unref_dealloc, NULL);
838                 g_slist_free (folder->entries);
839                 folder->entries = NULL;
840         }
841 }
842
843 /* Run query, allocs and refs the entries */
844 static void
845 append_query (VFolderInfo *info, Folder *folder)
846 {
847         GSList *li;
848
849         if (folder->query == NULL &&
850             ! folder->only_unallocated)
851                 return;
852
853         if (folder->only_unallocated) {
854                 /* dump all folders that use unallocated
855                  * items only.  This sucks if you keep
856                  * reading one and then another such
857                  * folder, but oh well, life sucks for
858                  * you then, but at least you get
859                  * consistent results */
860                 dump_unallocated_folders (info->root);
861
862                 /* ensure all other folders, so that
863                  * after this we know which ones are
864                  * unallocated */
865                 ensure_folder_unlocked (info,
866                                         info->root,
867                                         TRUE /* subfolders */,
868                                         folder /* except */,
869                                         /* avoid infinite loops */
870                                         TRUE /* ignore_unallocated */);
871         }
872
873         for (li = info->entries; li != NULL; li = li->next) {
874                 Entry *entry = li->data;
875                 
876                 if (/* if not file */
877                     entry->type != ENTRY_FILE ||
878                     /* if already included */
879                     (folder->includes_ht != NULL &&
880                      g_hash_table_lookup (folder->includes_ht,
881                                           entry->name) != NULL))
882                         continue;
883
884                 if (folder->only_unallocated &&
885                     entry->alloc != 0)
886                         continue;
887
888                 if (matches_query (info, folder, (EntryFile *)entry,
889                                    folder->query))
890                         folder->entries = g_slist_prepend
891                                 (folder->entries, entry_ref_alloc (entry));
892         }
893 }
894
895 /* get entries in folder */
896 /* FIXME: support cancellation here */
897 static void
898 ensure_folder_unlocked (VFolderInfo *info,
899                         Folder *folder,
900                         gboolean subfolders,
901                         Folder *except,
902                         gboolean ignore_unallocated)
903 {
904         if (subfolders) {
905                 GSList *li;
906                 for (li = folder->subfolders; li != NULL; li = li->next)
907                         ensure_folder_unlocked (info, li->data, subfolders,
908                                                 except, ignore_unallocated);
909         }
910
911         if (except == folder)
912                 return;
913
914         if (ignore_unallocated &&
915             folder->only_unallocated)
916                 return;
917
918         if (folder->up_to_date)
919                 return;
920
921         if (folder->entries != NULL) {
922                 g_slist_foreach (folder->entries,
923                                  (GFunc)entry_unref_dealloc, NULL);
924                 g_slist_free (folder->entries);
925                 folder->entries = NULL;
926         }
927
928         /* Include includes */
929         folder->entries = alloc_entries_from_files (info, folder->includes);
930
931         /* Run query */
932         append_query (info, folder);
933
934         /* We were prepending all this time */
935         folder->entries = g_slist_reverse (folder->entries);
936
937         /* Include subfolders */
938         /* we always whack them onto the beginning */
939         if (folder->subfolders != NULL) {
940                 GSList *subfolders = g_slist_copy (folder->subfolders);
941                 g_slist_foreach (subfolders, (GFunc)entry_ref_alloc, NULL);
942                 folder->entries = g_slist_concat (subfolders, folder->entries);
943         }
944
945         /* Exclude excludes */
946         if (folder->excludes != NULL) {
947                 GSList *li;
948                 GSList *entries = folder->entries;
949                 folder->entries = NULL;
950                 for (li = entries; li != NULL; li = li->next) {
951                         Entry *entry = li->data;
952                         if (g_hash_table_lookup (folder->excludes, entry->name) == NULL)
953                                 folder->entries = g_slist_prepend (folder->entries, entry);
954                         else
955                                 entry_unref_dealloc (entry);
956
957                 }
958                 g_slist_free (entries);
959
960                 /* to preserve the Folders then everything else order */
961                 folder->entries = g_slist_reverse (folder->entries);
962         }
963
964         folder->up_to_date = TRUE;
965         /* not yet sorted */
966         folder->sorted = FALSE;
967 }
968
969 static void
970 ensure_folder (VFolderInfo *info,
971                Folder *folder,
972                gboolean subfolders,
973                Folder *except,
974                gboolean ignore_unallocated)
975 {
976         G_LOCK (vfolder_lock);
977         ensure_folder_unlocked (info, folder, subfolders, except, ignore_unallocated);
978         G_UNLOCK (vfolder_lock);
979 }
980
981 static char *
982 get_directory_file_unlocked (VFolderInfo *info, Folder *folder)
983 {
984         char *filename;
985
986         /* FIXME: cache dir_files */
987         
988         if (folder->desktop_file == NULL)
989                 return NULL;
990
991         if (folder->desktop_file[0] == G_DIR_SEPARATOR)
992                 return g_strdup (folder->desktop_file);
993
994         /* Now try the user directory */
995         if (info->user_desktop_dir != NULL) {
996                 filename = g_build_filename (info->user_desktop_dir,
997                                              folder->desktop_file,
998                                              NULL);
999                 if (access (filename, F_OK) == 0) {
1000                         return filename;
1001                 }
1002
1003                 g_free (filename);
1004         }
1005
1006         filename = g_build_filename (info->desktop_dir, folder->desktop_file, NULL);
1007         if (access (filename, F_OK) == 0) {
1008                 return filename;
1009         }
1010         g_free (filename);
1011
1012         return NULL;
1013 }
1014
1015 static char *
1016 get_directory_file (VFolderInfo *info, Folder *folder)
1017 {
1018         char *ret;
1019
1020         G_LOCK (vfolder_lock);
1021         ret = get_directory_file_unlocked (info, folder);
1022         G_UNLOCK (vfolder_lock);
1023
1024         return ret;
1025 }
1026
1027 static GSList *
1028 get_sort_order (VFolderInfo *info, Folder *folder)
1029 {
1030         GSList *list;
1031         char **parsed;
1032         char *order;
1033         int i;
1034         char *filename;
1035
1036         filename = get_directory_file_unlocked (info, folder);
1037         if (filename == NULL)
1038                 return NULL;
1039
1040         order = NULL;
1041         readitem_entry (filename,
1042                         "SortOrder",
1043                         &order,
1044                         NULL,
1045                         NULL);
1046         g_free (filename);
1047
1048         if (order == NULL)
1049                 return NULL;
1050
1051         parsed = g_strsplit (order, ":", -1);
1052
1053         g_free (order);
1054
1055         list = NULL;
1056         for (i = 0; parsed[i] != NULL; i++) {
1057                 char *word = parsed[i];
1058                 /* steal */
1059                 parsed[i] = NULL;
1060                 /* ignore empty */
1061                 if (word[0] == '\0') {
1062                         g_free (word);
1063                         continue;
1064                 }
1065                 list = g_slist_prepend (list, word);
1066         }
1067         /* we've stolen all strings from it */
1068         g_free (parsed);
1069
1070         return g_slist_reverse (list);
1071 }
1072
1073 /* get entries in folder */
1074 static void
1075 ensure_folder_sort (VFolderInfo *info, Folder *folder)
1076 {
1077         GSList *li, *sort_order;
1078         GSList *entries;
1079         GHashTable *entry_hash;
1080
1081         ensure_folder (info, folder,
1082                        FALSE /* subfolders */,
1083                        NULL /* except */,
1084                        FALSE /* ignore_unallocated */);
1085         if (folder->sorted)
1086                 return;
1087
1088         G_LOCK (vfolder_lock);
1089
1090         sort_order = get_sort_order (info, folder);
1091         if (sort_order == NULL) {
1092                 folder->sorted = TRUE;
1093                 G_UNLOCK (vfolder_lock);
1094                 return;
1095         }
1096
1097         entries = folder->entries;
1098         folder->entries = NULL;
1099
1100         entry_hash = g_hash_table_new (g_str_hash, g_str_equal);
1101         for (li = entries; li != NULL; li = li->next) {
1102                 Entry *entry = li->data;
1103                 g_hash_table_insert (entry_hash, entry->name, li);
1104         }
1105
1106         for (li = sort_order; li != NULL; li = li->next) {
1107                 char *word = li->data;
1108                 GSList *entry_list;
1109                 Entry *entry;
1110
1111                 /* we kill the words here */
1112                 li->data = NULL;
1113
1114                 entry_list = g_hash_table_lookup (entry_hash, word);
1115                 g_free (word);
1116
1117                 if (entry_list == NULL)
1118                         continue;
1119
1120                 entry = entry_list->data;
1121
1122                 entries = g_slist_delete_link (entries, entry_list);
1123
1124                 folder->entries = g_slist_prepend (folder->entries,
1125                                                    entry);
1126         }
1127
1128         /* put on those that weren't mentioned in the sort */
1129         for (li = entries; li != NULL; li = li->next) {
1130                 Entry *entry = li->data;
1131
1132                 folder->entries = g_slist_prepend (folder->entries,
1133                                                    entry);
1134         }
1135
1136         g_hash_table_destroy (entry_hash);
1137         g_slist_free (entries);
1138         g_slist_free (sort_order);
1139
1140         folder->sorted = TRUE;
1141
1142         G_UNLOCK (vfolder_lock);
1143 }
1144
1145 static EntryFile *
1146 file_new (const char *name)
1147 {
1148         EntryFile *efile = g_new0 (EntryFile, 1);
1149
1150         efile->entry.type = ENTRY_FILE;
1151         efile->entry.name = g_strdup (name);
1152         efile->entry.refcount = 1;
1153
1154         return efile;
1155 }
1156
1157 static Folder *
1158 folder_new (const char *name)
1159 {
1160         Folder *folder = g_new0 (Folder, 1);
1161
1162         folder->entry.type = ENTRY_FOLDER;
1163         folder->entry.name = g_strdup (name);
1164         folder->entry.refcount = 1;
1165
1166         return folder;
1167 }
1168
1169 static Query *
1170 query_new (int type)
1171 {
1172         Query *query;
1173
1174         if (type == QUERY_KEYWORD)
1175                 query = (Query *)g_new0 (QueryKeyword, 1);
1176         else if (type == QUERY_FILENAME)
1177                 query = (Query *)g_new0 (QueryFilename, 1);
1178         else
1179                 query = g_new0 (Query, 1);
1180
1181         query->type = type;
1182
1183         return query;
1184 }
1185
1186 static void
1187 query_destroy (Query *query)
1188 {
1189         if (query == NULL)
1190                 return;
1191
1192         if (query->type == QUERY_FILENAME) {
1193                 QueryFilename *qfile = (QueryFilename *)query;
1194                 g_free (qfile->filename);
1195                 qfile->filename = NULL;
1196         } else if (query->type == QUERY_OR ||
1197                    query->type == QUERY_AND) {
1198                 g_slist_foreach (query->queries, (GFunc)query_destroy, NULL);
1199                 g_slist_free (query->queries);
1200                 query->queries = NULL;
1201         }
1202
1203         g_free (query);
1204 }
1205
1206 static void
1207 add_folder_monitor_unlocked (VFolderInfo *info,
1208                              FileMonitorHandle *handle)
1209 {
1210         VFolderURI vuri;
1211         GnomeVFSResult result;
1212         Folder *folder;
1213
1214         VFOLDER_URI_PARSE (handle->uri, &vuri);
1215
1216         file_monitor_handle_ref_unlocked (handle);
1217
1218         info->folder_monitors = 
1219                 g_slist_prepend (info->folder_monitors, handle);
1220
1221         folder = resolve_folder (info, 
1222                                  vuri.path,
1223                                  FALSE /* ignore_basename */,
1224                                  &result,
1225                                  NULL);
1226
1227         if (folder == NULL) {
1228                 file_monitor_handle_ref_unlocked (handle);
1229
1230                 info->free_folder_monitors = 
1231                         g_slist_prepend (info->free_folder_monitors, handle);
1232
1233                 if (handle->exists) {
1234                         handle->exists = FALSE;
1235                         gnome_vfs_monitor_callback
1236                                 ((GnomeVFSMethodHandle *)handle,
1237                                  handle->uri, 
1238                                  GNOME_VFS_MONITOR_EVENT_DELETED);
1239                 }
1240         } else {
1241                 file_monitor_handle_ref_unlocked (handle);
1242
1243                 ((Entry *)folder)->monitors = 
1244                         g_slist_prepend (((Entry *)folder)->monitors, handle);
1245
1246                 if ( ! handle->exists) {
1247                         handle->exists = TRUE;
1248                         gnome_vfs_monitor_callback
1249                                 ((GnomeVFSMethodHandle *)handle,
1250                                  handle->uri, 
1251                                  GNOME_VFS_MONITOR_EVENT_CREATED);
1252                 }
1253         }
1254
1255 }
1256
1257 static inline void
1258 invalidate_folder_T (Folder *folder)
1259 {
1260         folder->up_to_date = FALSE;
1261
1262         invalidate_folder_subfolders (folder, TRUE);
1263 }
1264
1265 static inline void
1266 invalidate_folder (Folder *folder)
1267 {
1268         G_LOCK (vfolder_lock);
1269         folder->up_to_date = FALSE;
1270         G_UNLOCK (vfolder_lock);
1271
1272         invalidate_folder_subfolders (folder, FALSE);
1273 }
1274
1275 static void
1276 invalidate_folder_subfolders (Folder   *folder,
1277                               gboolean  lock_taken)
1278 {
1279         GSList *li;
1280
1281         for (li = folder->subfolders; li != NULL; li = li->next) {
1282                 Folder *subfolder = li->data;
1283
1284                 if (!lock_taken)
1285                         invalidate_folder (subfolder);
1286                 else
1287                         invalidate_folder_T (subfolder);
1288         }
1289
1290         emit_monitor (folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
1291 }
1292
1293 /* FIXME: this is UGLY!, we need to figure out when the file
1294  * got finished changing! */
1295 static gboolean
1296 reread_timeout (gpointer data)
1297 {
1298         VFolderInfo *info = data;
1299         gboolean force_read_items = info->file_monitors != NULL;
1300         vfolder_info_reload (info, NULL, NULL, force_read_items);
1301         return FALSE;
1302 }
1303
1304 static void
1305 queue_reread_in (VFolderInfo *info, int msec)
1306 {
1307         G_LOCK (vfolder_lock);
1308         if (info->reread_queue != 0)
1309                 g_source_remove (info->reread_queue);
1310         info->reread_queue = g_timeout_add (msec, reread_timeout, info);
1311         G_UNLOCK (vfolder_lock);
1312 }
1313
1314 static void
1315 vfolder_desktop_dir_monitor (GnomeVFSMonitorHandle *handle,
1316                              const gchar *monitor_uri,
1317                              const gchar *info_uri,
1318                              GnomeVFSMonitorEventType event_type,
1319                              gpointer user_data)
1320 {
1321         /* FIXME: implement */
1322 }
1323
1324 static void
1325 vfolder_user_desktop_dir_monitor (GnomeVFSMonitorHandle *handle,
1326                                   const gchar *monitor_uri,
1327                                   const gchar *info_uri,
1328                                   GnomeVFSMonitorEventType event_type,
1329                                   gpointer user_data)
1330 {
1331         /* FIXME: implement */
1332 }
1333
1334 static void
1335 vfolder_filename_monitor (GnomeVFSMonitorHandle *handle,
1336                           const gchar *monitor_uri,
1337                           const gchar *info_uri,
1338                           GnomeVFSMonitorEventType event_type,
1339                           gpointer user_data)
1340 {
1341         VFolderInfo *info = user_data;
1342
1343         if ((event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
1344              event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) &&
1345             ! info->user_file_active) {
1346                 queue_reread_in (info, 200);
1347         } else if (event_type == GNOME_VFS_MONITOR_EVENT_DELETED &&
1348                    ! info->user_file_active) {
1349                 /* FIXME: is this correct?  I mean now
1350                  * there probably isn't ANY vfolder file, so we
1351                  * init to default values really.  I have no clue what's
1352                  * right here */
1353                 vfolder_info_reload (info, NULL, NULL,
1354                                      TRUE /* force read items */);
1355         }
1356 }
1357
1358 static void
1359 vfolder_user_filename_monitor (GnomeVFSMonitorHandle *handle,
1360                                const gchar *monitor_uri,
1361                                const gchar *info_uri,
1362                                GnomeVFSMonitorEventType event_type,
1363                                gpointer user_data)
1364 {
1365         VFolderInfo *info = user_data;
1366
1367         if ((event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
1368              event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) &&
1369             info->user_file_active) {
1370                 struct stat s;
1371
1372                 /* see if this was really our own change */
1373                 if (info->user_filename_last_write == time (NULL))
1374                         return;
1375                 /* anal retentive */
1376                 if (stat (info->user_filename, &s) == 0 &&
1377                     info->user_filename_last_write == s.st_ctime)
1378                         return;
1379
1380                 queue_reread_in (info, 200);
1381         } else if ((event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
1382                     event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) &&
1383                     ! info->user_file_active) {
1384                 queue_reread_in (info, 200);
1385         } else if (event_type == GNOME_VFS_MONITOR_EVENT_DELETED &&
1386                    info->user_file_active) {
1387                 gboolean force_read_items = info->file_monitors != NULL;
1388                 vfolder_info_reload (info, NULL, NULL, force_read_items);
1389         }
1390 }
1391
1392 static void
1393 item_dir_monitor (GnomeVFSMonitorHandle *handle,
1394                   const gchar *monitor_uri,
1395                   const gchar *info_uri,
1396                   GnomeVFSMonitorEventType event_type,
1397                   gpointer user_data)
1398 {
1399         VFolderInfo *info = user_data;
1400
1401         if (event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
1402             event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) {
1403                 /* first invalidate all folders */
1404                 invalidate_folder (info->root);
1405                 /* second invalidate all entries */
1406                 info->entries_valid = FALSE;
1407
1408                 if (info->file_monitors != NULL) {
1409                         GnomeVFSResult result;
1410                         GSList *li;
1411
1412                         /* Whack all monitors here! */
1413                         for (li = info->file_monitors;
1414                              li != NULL;
1415                              li = li->next) {
1416                                 FileMonitorHandle *h = li->data;
1417                                 if (h->handle != NULL)
1418                                         gnome_vfs_monitor_cancel (h->handle);
1419                                 h->handle = NULL;
1420                         }
1421
1422                         if (vfolder_info_read_items (info, &result, NULL)) {
1423                                 info->entries_valid = TRUE;
1424                         }
1425                 }
1426         }
1427 }
1428
1429 static gboolean
1430 setup_dir_monitor (VFolderInfo *info, const char *dir, gboolean subdirs,
1431                    GnomeVFSResult *result,
1432                    GnomeVFSContext *context)
1433 {
1434         GnomeVFSMonitorHandle *handle;
1435         DIR *dh;
1436         struct dirent *de;
1437         char *uri;
1438
1439         uri = gnome_vfs_get_uri_from_local_path (dir);
1440
1441         if (gnome_vfs_monitor_add (&handle,
1442                                    uri,
1443                                    GNOME_VFS_MONITOR_DIRECTORY,
1444                                    item_dir_monitor,
1445                                    info) != GNOME_VFS_OK) {
1446                 StatLoc *sl = bake_statloc (dir, time (NULL));
1447                 if (sl != NULL)
1448                         info->stat_dirs = g_slist_prepend (info->stat_dirs, sl);
1449                 g_free (uri);
1450                 return TRUE;
1451         }
1452         g_free (uri);
1453
1454         if (gnome_vfs_context_check_cancellation (context)) {
1455                 gnome_vfs_monitor_cancel (handle);
1456                 *result = GNOME_VFS_ERROR_CANCELLED;
1457                 return FALSE;
1458         }
1459
1460         info->item_dir_monitors =
1461                 g_slist_prepend (info->item_dir_monitors, handle);
1462
1463         if ( ! subdirs)
1464                 return TRUE;
1465
1466         dh = opendir (dir);
1467         if (dh == NULL)
1468                 return TRUE;
1469
1470         while ((de = readdir (dh)) != NULL) {
1471                 char *full_path;
1472
1473                 if (gnome_vfs_context_check_cancellation (context)) {
1474                         *result = GNOME_VFS_ERROR_CANCELLED;
1475                         closedir (dh);
1476                         return FALSE;
1477                 }
1478
1479                 if (de->d_name[0] == '.')
1480                         continue;
1481
1482                 full_path = g_build_filename (dir, de->d_name, NULL);
1483                 if (g_file_test (full_path, G_FILE_TEST_IS_DIR)) {
1484                         if ( ! setup_dir_monitor (info, full_path,
1485                                                   TRUE /* subdirs */,
1486                                                   result, context)) {
1487                                 closedir (dh);
1488                                 return FALSE;
1489                         }
1490                 }
1491                 g_free (full_path);
1492         }
1493
1494         closedir (dh);
1495
1496         return TRUE;
1497 }
1498
1499 static gboolean
1500 monitor_setup (VFolderInfo *info,
1501                gboolean setup_filenames,
1502                gboolean setup_itemdirs,
1503                gboolean setup_desktop_dirs,
1504                GnomeVFSResult *result,
1505                GnomeVFSContext *context)
1506 {
1507         char *uri;
1508         GSList *li;
1509
1510         if (setup_filenames) {
1511                 uri = gnome_vfs_get_uri_from_local_path
1512                         (info->filename);
1513
1514                 if (gnome_vfs_monitor_add (&info->filename_monitor,
1515                                            uri,
1516                                            GNOME_VFS_MONITOR_FILE,
1517                                            vfolder_filename_monitor,
1518                                            info) != GNOME_VFS_OK) {
1519                         info->filename_monitor = NULL;
1520                         info->filename_statloc = bake_statloc (info->filename,
1521                                                                time (NULL));
1522                 }
1523                 g_free (uri);
1524         }
1525         if (setup_filenames &&
1526             info->user_filename != NULL) {
1527                 uri = gnome_vfs_get_uri_from_local_path
1528                         (info->user_filename);
1529                 if (gnome_vfs_monitor_add (&info->user_filename_monitor,
1530                                            uri,
1531                                            GNOME_VFS_MONITOR_FILE,
1532                                            vfolder_user_filename_monitor,
1533                                            info) != GNOME_VFS_OK) {
1534                         info->user_filename_monitor = NULL;
1535                         info->user_filename_statloc =
1536                                 bake_statloc (info->user_filename,
1537                                               time (NULL));
1538                 }
1539
1540                 g_free (uri);
1541         }
1542
1543         if (gnome_vfs_context_check_cancellation (context)) {
1544                 *result = GNOME_VFS_ERROR_CANCELLED;
1545                 return FALSE;
1546         }
1547
1548         if (setup_itemdirs) {
1549                 for (li = info->item_dirs; li != NULL; li = li->next) {
1550                         const char *dir = li->data;
1551                         if ( ! setup_dir_monitor (info, dir,
1552                                                   FALSE /* subdirs */,
1553                                                   result, context))
1554                                 return FALSE;
1555                 }
1556                 if (info->user_item_dir != NULL) {
1557                         if ( ! setup_dir_monitor (info, info->user_item_dir,
1558                                                   FALSE /* subdirs */,
1559                                                   result, context))
1560                                 return FALSE;
1561                 }
1562                 for (li = info->merge_dirs; li != NULL; li = li->next) {
1563                         const char *dir = li->data;
1564                         if ( ! setup_dir_monitor (info, dir,
1565                                                   TRUE /* subdirs */,
1566                                                   result, context))
1567                                 return FALSE;
1568                 }
1569         }
1570
1571         if (setup_desktop_dirs) {
1572                 uri = gnome_vfs_get_uri_from_local_path
1573                         (info->desktop_dir);
1574
1575                 if (gnome_vfs_monitor_add (&info->desktop_dir_monitor,
1576                                            uri,
1577                                            GNOME_VFS_MONITOR_FILE,
1578                                            vfolder_desktop_dir_monitor,
1579                                            info) != GNOME_VFS_OK) {
1580                         info->desktop_dir_monitor = NULL;
1581                         info->desktop_dir_statloc =
1582                                 bake_statloc (info->desktop_dir,
1583                                               time (NULL));
1584                 }
1585                 g_free (uri);
1586         }
1587         if (setup_desktop_dirs &&
1588             info->user_desktop_dir != NULL) {
1589                 uri = gnome_vfs_get_uri_from_local_path
1590                         (info->user_desktop_dir);
1591                 if (gnome_vfs_monitor_add (&info->user_desktop_dir_monitor,
1592                                            uri,
1593                                            GNOME_VFS_MONITOR_DIRECTORY,
1594                                            vfolder_user_desktop_dir_monitor,
1595                                            info) != GNOME_VFS_OK) {
1596                         info->user_desktop_dir_monitor = NULL;
1597                         info->user_desktop_dir_statloc =
1598                                 bake_statloc (info->user_desktop_dir,
1599                                               time (NULL));
1600                 }
1601
1602                 g_free (uri);
1603         }
1604
1605         return TRUE;
1606 }
1607
1608 static void
1609 vfolder_info_init (VFolderInfo *info, const char *scheme)
1610 {
1611         const char *path;
1612         GSList *list;
1613
1614         info->scheme = g_strdup (scheme);
1615
1616         info->filename = g_strconcat (SYSCONFDIR,
1617                                       "/gnome-vfs-2.0/vfolders/",
1618                                       scheme, ".vfolder-info",
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                 return info;
3359         }
3360
3361         if (gnome_vfs_context_check_cancellation (context)) {
3362                 *result = GNOME_VFS_ERROR_CANCELLED;
3363                 return NULL;
3364         }
3365
3366         if (infos == NULL)
3367                 infos = g_hash_table_new_full
3368                         (g_str_hash, g_str_equal,
3369                          (GDestroyNotify)g_free,
3370                          (GDestroyNotify)vfolder_info_destroy);
3371
3372         info = g_new0 (VFolderInfo, 1);
3373         vfolder_info_init (info, scheme);
3374
3375         if (gnome_vfs_context_check_cancellation (context)) {
3376                 vfolder_info_destroy (info);
3377                 *result = GNOME_VFS_ERROR_CANCELLED;
3378                 return NULL;
3379         }
3380
3381         if ( ! vfolder_info_read_info (info, result, context)) {
3382                 vfolder_info_destroy (info);
3383                 return NULL;
3384         }
3385
3386         if ( ! monitor_setup (info,
3387                               TRUE /* setup_filenames */,
3388                               TRUE /* setup_itemdirs */,
3389                               TRUE /* setup_desktop_dirs */,
3390                               result, context)) {
3391                 vfolder_info_destroy (info);
3392                 return NULL;
3393         }
3394
3395         g_hash_table_insert (infos, g_strdup (scheme), info);
3396
3397         if ( ! vfolder_info_read_items (info, result, context)) {
3398                 info->entries_valid = FALSE;
3399                 return NULL;
3400         }
3401         info->entries_valid = TRUE;
3402
3403         return info;
3404 }
3405
3406 static VFolderInfo *
3407 get_vfolder_info (const char *scheme,
3408                   GnomeVFSResult *result,
3409                   GnomeVFSContext *context)
3410 {
3411         VFolderInfo *info;
3412         G_LOCK (vfolder_lock);
3413         info = get_vfolder_info_unlocked (scheme, result, context);
3414         G_UNLOCK (vfolder_lock);
3415         return info;
3416 }
3417
3418
3419 static char *
3420 keywords_to_string (GSList *keywords)
3421 {
3422         GSList *li;
3423         GString *str = g_string_new (NULL);
3424
3425         for (li = keywords; li != NULL; li = li->next) {
3426                 GQuark word = GPOINTER_TO_INT (li->data);
3427                 g_string_append (str, g_quark_to_string (word));
3428                 g_string_append_c (str, ';');
3429         }
3430
3431         return g_string_free (str, FALSE);
3432 }
3433
3434 /* copy file and add keywords line */
3435 static gboolean
3436 copy_file_with_keywords (const char *from, const char *to, GSList *keywords)
3437 {
3438         FILE *fp;
3439         FILE *wfp;
3440         int wfd;
3441         char buf[BUFSIZ];
3442         char *keyword_string;
3443
3444         if ( ! ensure_dir (to,
3445                            TRUE /* ignore_basename */))
3446                 return FALSE;
3447
3448         wfd = open (to, O_CREAT | O_WRONLY | O_TRUNC, 0600);
3449         if (wfd < 0) {
3450                 return FALSE;
3451         }
3452
3453         keyword_string = keywords_to_string (keywords);
3454
3455         wfp = fdopen (wfd, "w");
3456
3457         fp = fopen (from, "r");
3458         if (fp != NULL) {
3459                 gboolean wrote_keywords = FALSE;
3460                 while (fgets (buf, sizeof (buf), fp) != NULL) {
3461                         fprintf (wfp, "%s", buf);
3462                         if ( ! wrote_keywords &&
3463                             (strncmp (buf, "[Desktop Entry]",
3464                                       strlen ("[Desktop Entry]")) == 0 ||
3465                              strncmp (buf, "[KDE Desktop Entry]",
3466                                       strlen ("[KDE Desktop Entry]")) == 0)) {
3467                                 fprintf (wfp, "Categories=%s\n",
3468                                          keyword_string);
3469                                 wrote_keywords = TRUE;
3470                         }
3471                 }
3472
3473                 fclose (fp);
3474         } else {
3475                 fprintf (wfp, "[Desktop Entry]\nCategories=%s\n",
3476                          keyword_string);
3477         }
3478
3479         /* FIXME: does this close wfd???? */
3480         fclose (wfp);
3481
3482         close (wfd);
3483
3484         g_free (keyword_string);
3485
3486         return TRUE;
3487 }
3488
3489 static gboolean
3490 copy_file (const char *from, const char *to)
3491 {
3492         int fd;
3493         int wfd;
3494
3495         if ( ! ensure_dir (to,
3496                            TRUE /* ignore_basename */))
3497                 return FALSE;
3498
3499         wfd = open (to, O_CREAT | O_WRONLY | O_TRUNC, 0600);
3500         if (wfd < 0) {
3501                 return FALSE;
3502         }
3503
3504         fd = open (from, O_RDONLY);
3505         if (fd >= 0) {
3506                 char buf[1024];
3507                 ssize_t n;
3508
3509                 while ((n = read (fd, buf, sizeof(buf))) > 0) {
3510                         write (wfd, buf, n);
3511                 }
3512
3513                 close (fd);
3514         }
3515
3516         close (wfd);
3517
3518         return TRUE;
3519 }
3520
3521 static gboolean
3522 make_file_private (VFolderInfo *info, EntryFile *efile)
3523 {
3524         char *newfname;
3525         Entry *entry = (Entry *)efile;
3526
3527         if (efile->per_user)
3528                 return TRUE;
3529
3530         /* this file already exists so whack its monitors */
3531         if (efile->filename != NULL) {
3532                 GSList *li;
3533
3534                 for (li = entry->monitors; li != NULL; li = li->next) {
3535                         FileMonitorHandle *h = li->data;
3536                         if (h->handle != NULL)
3537                                 gnome_vfs_monitor_cancel (h->handle);
3538                         h->handle = NULL;
3539                 }
3540         }
3541
3542         newfname = g_build_filename (g_get_home_dir (),
3543                                      DOT_GNOME,
3544                                      "vfolders",
3545                                      info->scheme,
3546                                      efile->entry.name,
3547                                      NULL);
3548
3549         if (efile->implicit_keywords) {
3550                 if (efile->filename != NULL &&
3551                     ! copy_file_with_keywords (efile->filename,
3552                                                newfname,
3553                                                efile->keywords)) {
3554                         /* FIXME: what to do with monitors here, they
3555                          * have already been whacked, a corner case
3556                          * not handled! */
3557                         g_free (newfname);
3558                         return FALSE;
3559                 }
3560         } else {
3561                 if (efile->filename != NULL &&
3562                     ! copy_file (efile->filename, newfname)) {
3563                         /* FIXME: what to do with monitors here, they
3564                          * have already been whacked, a corner case
3565                          * not handled! */
3566                         g_free (newfname);
3567                         return FALSE;
3568                 }
3569         }
3570
3571         /* we didn't copy but ensure path anyway */
3572         if (efile->filename == NULL &&
3573             ! ensure_dir (newfname,
3574                           TRUE /* ignore_basename */)) {
3575                 g_free (newfname);
3576                 return FALSE;
3577         }
3578
3579         /* this file already exists so re-add monitors at the new location */
3580         if (efile->filename != NULL) {
3581                 GSList *li;
3582                 char *uri = gnome_vfs_get_uri_from_local_path (newfname);
3583
3584                 for (li = entry->monitors; li != NULL; li = li->next) {
3585                         FileMonitorHandle *h = li->data;
3586
3587                         gnome_vfs_monitor_add (&(h->handle),
3588                                                uri,
3589                                                GNOME_VFS_MONITOR_FILE,
3590                                                file_monitor,
3591                                                h);
3592                 }
3593
3594                 g_free (uri);
3595         }
3596
3597         g_free (efile->filename);
3598         efile->filename = newfname;
3599         efile->per_user = TRUE;
3600
3601         return TRUE;
3602 }
3603
3604 static void
3605 try_free_file_monitors_create_dirfile_unlocked (VFolderInfo *info,
3606                                                 Folder *folder)
3607 {
3608         GSList *li, *list;
3609
3610         list = g_slist_copy (info->free_file_monitors);
3611
3612         for (li = list; li != NULL; li = li->next) {
3613                 FileMonitorHandle *handle = li->data;
3614                 Folder *f;
3615                 VFolderURI vuri;
3616                 GnomeVFSResult result;
3617
3618                 if ( ! handle->is_directory_file)
3619                         continue;
3620
3621                 /* Evil! EVIL URI PARSING. this will eat a lot of stack if we
3622                  * have lots of free monitors */
3623
3624                 VFOLDER_URI_PARSE (handle->uri, &vuri);
3625
3626                 f = resolve_folder (info, 
3627                                     vuri.path,
3628                                     TRUE /* ignore_basename */,
3629                                     &result,
3630                                     NULL);
3631
3632                 if (folder != f)
3633                         continue;
3634
3635                 info->free_file_monitors =
3636                         g_slist_remove (info->free_file_monitors, handle);
3637                 ((Entry *)folder)->monitors =
3638                         g_slist_prepend (((Entry *)folder)->monitors, handle);
3639
3640                 handle->exists = TRUE;
3641                 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *)handle,
3642                                             handle->uri, 
3643                                             GNOME_VFS_MONITOR_EVENT_CREATED);
3644         }
3645
3646         g_slist_free (list);
3647 }
3648
3649 static void
3650 make_new_dirfile (VFolderInfo *info, Folder *folder)
3651 {
3652         char *name = g_strdup (folder->entry.name);
3653         char *fname;
3654         char *p;
3655         int i;
3656         int fd;
3657
3658         for (p = name; *p != '\0'; p++) {
3659                 if ( ! ( (*p >= 'a' && *p <= 'z') ||
3660                          (*p >= 'A' && *p <= 'Z') ||
3661                          (*p >= '0' && *p <= '9') ||
3662                          *p == '_')) {
3663                         *p = '_';
3664                 }
3665         }
3666
3667         i = 0;
3668         fname = NULL;
3669         do {
3670                 char *fullname;
3671
3672                 g_free (fname);
3673
3674                 if (i > 0) {
3675                         fname = g_strdup_printf ("%s-%d.directory", name, i);
3676                 } else {
3677                         fname = g_strdup_printf ("%s.directory", name);
3678                 }
3679
3680                 fullname = g_build_filename
3681                         (info->user_desktop_dir, fname, NULL);
3682                 fd = open (fullname, O_CREAT | O_WRONLY | O_EXCL, 0600);
3683                 g_free (fullname);
3684         } while (fd < 0);
3685
3686         close (fd);
3687
3688         folder->desktop_file = fname;
3689         info->dirty = TRUE;
3690
3691         try_free_file_monitors_create_dirfile_unlocked (info, folder);
3692 }
3693
3694 static gboolean
3695 make_dirfile_private (VFolderInfo *info, Folder *folder)
3696 {
3697         char *fname;
3698         char *desktop_file;
3699         GSList *li;
3700         char *uri;
3701         gboolean ret;
3702
3703         if (info->user_desktop_dir == NULL)
3704                 return FALSE;
3705
3706         if ( ! ensure_dir (info->user_desktop_dir,
3707                            FALSE /* ignore_basename */))
3708                 return FALSE;
3709
3710
3711         if (folder->desktop_file == NULL) {
3712                 make_new_dirfile (info, folder);
3713                 return TRUE;
3714         }
3715
3716         /* FIXME: this is broken!  What if the desktop file exists
3717          * in the local but there is a different (but with a same name)
3718          * .directory in the system. */
3719         fname = g_build_filename (info->user_desktop_dir,
3720                                   folder->desktop_file,
3721                                   NULL);
3722
3723         if (access (fname, F_OK) == 0) {
3724                 g_free (fname);
3725                 return TRUE;
3726         }
3727
3728         desktop_file = get_directory_file (info, folder);
3729
3730         if (desktop_file == NULL) {
3731                 int fd = open (fname, O_CREAT | O_EXCL | O_WRONLY, 0600);
3732                 g_free (fname);
3733                 if (fd >= 0) {
3734                         close (fd);
3735                         return TRUE;
3736                 }
3737                 return FALSE;
3738         }
3739
3740         for (li = ((Entry *)folder)->monitors; li != NULL; li = li->next) {
3741                 FileMonitorHandle *h = li->data;
3742                 if (h->is_directory_file) {
3743                         if (h->handle != NULL)
3744                                 gnome_vfs_monitor_cancel (h->handle);
3745                         h->handle = NULL;
3746                 }
3747         }
3748
3749         ret = TRUE;
3750
3751         if ( ! copy_file (desktop_file, fname)) {
3752                 ret = FALSE;
3753                 g_free (fname);
3754                 fname = desktop_file;
3755                 desktop_file = NULL;
3756         }
3757
3758         uri = gnome_vfs_get_uri_from_local_path (fname);
3759
3760         for (li = ((Entry *)folder)->monitors; li != NULL; li = li->next) {
3761                 FileMonitorHandle *h = li->data;
3762
3763                 if (h->is_directory_file) {
3764                         gnome_vfs_monitor_add (&(h->handle),
3765                                                uri,
3766                                                GNOME_VFS_MONITOR_FILE,
3767                                                file_monitor,
3768                                                h);
3769                 }
3770         }
3771
3772         g_free (uri);
3773
3774         g_free (desktop_file);
3775         g_free (fname);
3776
3777         return ret;
3778 }
3779
3780 static Folder *
3781 resolve_folder (VFolderInfo *info,
3782                 const char *path,
3783                 gboolean ignore_basename,
3784                 GnomeVFSResult *result,
3785                 GnomeVFSContext *context)
3786 {
3787         char **ppath;
3788         int i;
3789         Folder *folder = info->root;
3790
3791         ppath = g_strsplit (path, "/", -1);
3792
3793         if (ppath == NULL ||
3794             ppath[0] == NULL) {
3795                 g_strfreev (ppath);
3796                 *result = GNOME_VFS_ERROR_INVALID_URI;
3797                 return NULL;
3798         }
3799
3800         for (i = 0; ppath [i] != NULL; i++) {
3801                 const char *segment = ppath[i];
3802
3803                 if (*segment == '\0')
3804                         continue;
3805
3806                 if (ignore_basename && ppath [i + 1] == NULL)
3807                         break;
3808                 else {
3809                         folder = (Folder *) find_entry (folder->subfolders, 
3810                                                         segment);
3811                         if (folder == NULL)
3812                                 break;
3813                 }
3814         }
3815         g_strfreev (ppath);
3816
3817         if (gnome_vfs_context_check_cancellation (context)) {
3818                 *result = GNOME_VFS_ERROR_CANCELLED;
3819                 return NULL;
3820         }
3821
3822         if (folder == NULL)
3823                 *result = GNOME_VFS_ERROR_NOT_FOUND;
3824
3825         return folder;
3826 }
3827
3828 static Entry *
3829 resolve_path (VFolderInfo *info,
3830               const char *path,
3831               const char *basename,
3832               Folder **return_folder,
3833               GnomeVFSResult *result,
3834               GnomeVFSContext *context)
3835 {
3836         Entry *entry;
3837         Folder *folder;
3838
3839         if (strcmp (path, "/") == 0)
3840                 return (Entry *)info->root;
3841
3842         folder = resolve_folder (info, path,
3843                                  TRUE /* ignore_basename */,
3844                                  result, context);
3845
3846         if (return_folder != NULL)
3847                 *return_folder = folder;
3848
3849         if (folder == NULL) {
3850                 return NULL;
3851         }
3852
3853         /* Make sure we have the entries here */
3854         ensure_folder_unlocked (info, folder,
3855                                 FALSE /* subfolders */,
3856                                 NULL /* except */,
3857                                 FALSE /* ignore_unallocated */);
3858
3859         entry = find_entry (folder->entries, basename);
3860
3861         if (entry == NULL)
3862                 *result = GNOME_VFS_ERROR_NOT_FOUND;
3863
3864         return entry;
3865 }
3866
3867 static Entry *
3868 get_entry_unlocked (VFolderURI *vuri,
3869                     Folder **parent,
3870                     gboolean *is_directory_file,
3871                     GnomeVFSResult *result,
3872                     GnomeVFSContext *context)
3873 {
3874         VFolderInfo *info;
3875         Entry *entry;
3876
3877         if (is_directory_file != NULL)
3878                 *is_directory_file = FALSE;
3879         if (parent != NULL)
3880                 *parent = NULL;
3881
3882         info = get_vfolder_info_unlocked (vuri->scheme, result, context);
3883         if (info == NULL)
3884                 return NULL;
3885
3886         if (gnome_vfs_context_check_cancellation (context)) {
3887                 *result = GNOME_VFS_ERROR_CANCELLED;
3888                 return NULL;
3889         }
3890
3891         if (vuri->is_all_scheme) {
3892                 GSList *efile_list;
3893
3894                 if (vuri->file == NULL) {
3895                         entry = resolve_path (info, 
3896                                               vuri->path, 
3897                                               vuri->file, 
3898                                               parent, 
3899                                               result, 
3900                                               context);
3901                         return entry;
3902                 }
3903
3904                 efile_list = g_hash_table_lookup (info->entries_ht, vuri->file);
3905
3906                 if (efile_list == NULL) {
3907                         *result = GNOME_VFS_ERROR_NOT_FOUND;
3908                         return NULL;
3909                 } else {
3910                         return efile_list->data;
3911                 }
3912         }
3913
3914         if (vuri->file != NULL && 
3915             check_ext (vuri->file, ".directory") == TRUE) {
3916                 Folder *folder;
3917
3918                 folder = resolve_folder (info, vuri->path,
3919                                          TRUE /* ignore_basename */,
3920                                          result, context);
3921                 if (folder == NULL) {
3922                         return NULL;
3923                 }
3924
3925                 if (is_directory_file != NULL)
3926                         *is_directory_file = TRUE;
3927
3928                 if (parent != NULL)
3929                         *parent = folder;
3930
3931                 return (Entry *)folder;
3932         } else {
3933                 entry = resolve_path (info, vuri->path, vuri->file, parent, 
3934                                       result, context);
3935                 return entry;
3936         }
3937 }
3938
3939 static Entry *
3940 get_entry (VFolderURI *vuri,
3941            Folder **parent,
3942            gboolean *is_directory_file,
3943            GnomeVFSResult *result,
3944            GnomeVFSContext *context)
3945 {
3946         Entry *entry;
3947
3948         G_LOCK (vfolder_lock);
3949         entry = get_entry_unlocked (vuri, 
3950                                     parent, 
3951                                     is_directory_file, 
3952                                     result, context);
3953         G_UNLOCK (vfolder_lock);
3954
3955         return entry;
3956 }
3957
3958 /* only works for files and only those that exist */
3959 /* unlocked function */
3960 static GnomeVFSURI *
3961 desktop_uri_to_file_uri (VFolderInfo *info,
3962                          VFolderURI *desktop_vuri,
3963                          Entry **the_entry,
3964                          gboolean *the_is_directory_file,
3965                          Folder **the_folder,
3966                          gboolean privatize,
3967                          GnomeVFSResult *result,
3968                          GnomeVFSContext *context)
3969 {
3970         gboolean is_directory_file;
3971         GnomeVFSURI *ret_uri;
3972         Folder *folder = NULL;
3973         Entry *entry;
3974
3975         entry = get_entry_unlocked (desktop_vuri,
3976                                     &folder,
3977                                     &is_directory_file,
3978                                     result,
3979                                     context);
3980         if (entry == NULL)
3981                 return NULL;
3982
3983         if (gnome_vfs_context_check_cancellation (context)) {
3984                 *result = GNOME_VFS_ERROR_CANCELLED;
3985                 return NULL;
3986         }
3987
3988         if (the_folder != NULL)
3989                 *the_folder = folder;
3990
3991         if (the_entry != NULL)
3992                 *the_entry = entry;
3993         if (the_is_directory_file != NULL)
3994                 *the_is_directory_file = is_directory_file;
3995
3996         if (is_directory_file &&
3997             entry->type == ENTRY_FOLDER) {
3998                 char *desktop_file;
3999
4000                 folder = (Folder *)entry;
4001
4002                 if (the_folder != NULL)
4003                         *the_folder = folder;
4004
4005                 /* we'll be doing something write like */
4006                 if (folder->read_only &&
4007                     privatize) {
4008                         *result = GNOME_VFS_ERROR_READ_ONLY;
4009                         return NULL;
4010                 }
4011
4012                 if (privatize) {
4013                         char *fname;
4014
4015                         if (gnome_vfs_context_check_cancellation (context)) {
4016                                 *result = GNOME_VFS_ERROR_CANCELLED;
4017                                 return NULL;
4018                         }
4019
4020                         if ( ! make_dirfile_private (info, folder)) {
4021                                 *result = GNOME_VFS_ERROR_GENERIC;
4022                                 return NULL;
4023                         }
4024                         fname = g_build_filename (g_get_home_dir (),
4025                                                   folder->desktop_file,
4026                                                   NULL);
4027                         ret_uri = gnome_vfs_uri_new (fname);
4028                         g_free (fname);
4029                         return ret_uri;
4030                 }
4031
4032                 desktop_file = get_directory_file_unlocked (info, folder);
4033                 if (desktop_file != NULL) {
4034                         char *s = gnome_vfs_get_uri_from_local_path
4035                                 (desktop_file);
4036
4037                         g_free (desktop_file);
4038
4039                         ret_uri = gnome_vfs_uri_new (s);
4040                         g_free (s);
4041
4042                         return ret_uri;
4043                 } else {
4044                         *result = GNOME_VFS_ERROR_NOT_FOUND;
4045                         return NULL;
4046                 }
4047         } else if (entry->type == ENTRY_FILE) {
4048                 EntryFile *efile = (EntryFile *)entry;
4049                 char *s;
4050
4051                 /* we'll be doing something write like */
4052                 if (folder != NULL &&
4053                     folder->read_only &&
4054                     privatize) {
4055                         *result = GNOME_VFS_ERROR_READ_ONLY;
4056                         return NULL;
4057                 }
4058
4059                 if (gnome_vfs_context_check_cancellation (context)) {
4060                         *result = GNOME_VFS_ERROR_CANCELLED;
4061                         return NULL;
4062                 }
4063
4064                 if (privatize &&
4065                     ! make_file_private (info, efile)) {
4066                         *result = GNOME_VFS_ERROR_GENERIC;
4067                         return NULL;
4068                 }
4069
4070                 s = gnome_vfs_get_uri_from_local_path (efile->filename);
4071                 ret_uri = gnome_vfs_uri_new (s);
4072                 g_free (s);
4073
4074                 return ret_uri;
4075         } else {
4076                 if (the_folder != NULL)
4077                         *the_folder = (Folder *)entry;
4078                 *result = GNOME_VFS_ERROR_IS_DIRECTORY;
4079                 return NULL;
4080         }
4081 }
4082
4083 static void
4084 remove_file (Folder *folder, const char *basename)
4085 {
4086         GSList *li;
4087         char *s;
4088
4089         if (folder->includes_ht != NULL) {
4090                 li = g_hash_table_lookup (folder->includes_ht, basename);
4091                 if (li != NULL) {
4092                         char *name = li->data;
4093                         folder->includes = g_slist_delete_link
4094                                 (folder->includes, li);
4095                         g_hash_table_remove (folder->includes_ht, basename);
4096                         g_free (name);
4097                 }
4098         }
4099
4100         if (folder->excludes == NULL) {
4101                 folder->excludes = g_hash_table_new_full
4102                         (g_str_hash, g_str_equal,
4103                          (GDestroyNotify)g_free,
4104                          NULL);
4105         }
4106         s = g_strdup (basename);
4107         g_hash_table_replace (folder->excludes, s, s);
4108 }
4109
4110 static void
4111 add_file (Folder *folder, const char *basename)
4112 {
4113         GSList *li = NULL;
4114
4115         if (folder->includes_ht != NULL) {
4116                 li = g_hash_table_lookup (folder->includes_ht, basename);
4117         }
4118
4119         /* if not found */
4120         if (li == NULL) {
4121                 char *str = g_strdup (basename);
4122                 folder->includes =
4123                         g_slist_prepend (folder->includes, str);
4124                 if (folder->includes_ht == NULL) {
4125                         folder->includes_ht =
4126                                 g_hash_table_new_full (g_str_hash,
4127                                                        g_str_equal,
4128                                                        NULL,
4129                                                        NULL);
4130                 }
4131                 g_hash_table_replace (folder->includes_ht,
4132                                       str, folder->includes);
4133         }
4134         if (folder->excludes != NULL)
4135                 g_hash_table_remove (folder->excludes, basename);
4136 }
4137
4138 typedef struct _FileHandle FileHandle;
4139 struct _FileHandle {
4140         VFolderInfo *info;
4141         GnomeVFSMethodHandle *handle;
4142         Entry *entry;
4143         gboolean write;
4144         gboolean is_directory_file;
4145 };
4146
4147 static void
4148 make_handle (GnomeVFSMethodHandle **method_handle,
4149              GnomeVFSMethodHandle *file_handle,
4150              VFolderInfo *info,
4151              Entry *entry,
4152              gboolean is_directory_file,
4153              gboolean write)
4154 {
4155         if (file_handle != NULL) {
4156                 FileHandle *handle = g_new0 (FileHandle, 1);
4157
4158                 handle->info = info;
4159                 handle->handle = file_handle;
4160                 handle->entry = entry_ref (entry);
4161                 handle->is_directory_file = is_directory_file;
4162                 handle->write = write;
4163
4164                 *method_handle = (GnomeVFSMethodHandle *) handle;
4165         } else {
4166                 *method_handle = NULL;
4167         }
4168 }
4169
4170 static void
4171 whack_handle (FileHandle *handle)
4172 {
4173         entry_unref (handle->entry);
4174         handle->entry = NULL;
4175
4176         handle->handle = NULL;
4177         handle->info = NULL;
4178
4179         g_free (handle);
4180 }
4181
4182 static GnomeVFSResult
4183 do_open (GnomeVFSMethod *method,
4184          GnomeVFSMethodHandle **method_handle,
4185          GnomeVFSURI *uri,
4186          GnomeVFSOpenMode mode,
4187          GnomeVFSContext *context)
4188 {
4189         GnomeVFSURI *file_uri;
4190         GnomeVFSResult result = GNOME_VFS_OK;
4191         VFolderInfo *info;
4192         Entry *entry;
4193         gboolean is_directory_file;
4194         GnomeVFSMethodHandle *file_handle = NULL;
4195         VFolderURI vuri;
4196
4197         VFOLDER_URI_PARSE (uri, &vuri);
4198
4199         /* These can't be very nice FILE names */
4200         if (vuri.file == NULL ||
4201             vuri.ends_in_slash)
4202                 return GNOME_VFS_ERROR_INVALID_URI;
4203
4204         info = get_vfolder_info (vuri.scheme, &result, context);
4205         if (info == NULL)
4206                 return result;
4207
4208         if (mode & GNOME_VFS_OPEN_WRITE && 
4209             (info->read_only || vuri.is_all_scheme))
4210                 return GNOME_VFS_ERROR_READ_ONLY;
4211
4212         G_LOCK (vfolder_lock);
4213         file_uri = desktop_uri_to_file_uri (info,
4214                                             &vuri,
4215                                             &entry,
4216                                             &is_directory_file,
4217                                             NULL /* the_folder */,
4218                                             mode & GNOME_VFS_OPEN_WRITE,
4219                                             &result,
4220                                             context);
4221
4222         if (file_uri == NULL) {
4223                 G_UNLOCK (vfolder_lock);
4224                 return result;
4225         }
4226
4227         result = (* parent_method->open) (parent_method,
4228                                           &file_handle,
4229                                           file_uri,
4230                                           mode,
4231                                           context);
4232
4233         if (result == GNOME_VFS_ERROR_CANCELLED) {
4234                 G_UNLOCK (vfolder_lock);
4235                 gnome_vfs_uri_unref (file_uri);
4236                 return result;
4237         }
4238
4239         make_handle (method_handle,
4240                      file_handle,
4241                      info,
4242                      entry,
4243                      is_directory_file,
4244                      mode & GNOME_VFS_OPEN_WRITE);
4245
4246         gnome_vfs_uri_unref (file_uri);
4247
4248         if (info->dirty) {
4249                 vfolder_info_write_user (info);
4250         }
4251
4252         G_UNLOCK (vfolder_lock);
4253
4254         return result;
4255 }
4256
4257 static void
4258 remove_from_all_except (Folder *root,
4259                         const char *name,
4260                         Folder *except)
4261 {
4262         GSList *li;
4263
4264         if (root != except) {
4265                 remove_file (root, name);
4266                 if (root->up_to_date) {
4267                         for (li = root->entries; li != NULL; li = li->next) {
4268                                 Entry *entry = li->data;
4269                                 if (strcmp (name, entry->name) == 0) {
4270                                         root->entries = 
4271                                                 g_slist_delete_link
4272                                                    (root->entries, li);
4273                                         break;
4274                                 }
4275                         }
4276                 }
4277         }
4278
4279         for (li = root->subfolders; li != NULL; li = li->next) {
4280                 Folder *subfolder = li->data;
4281
4282                 remove_from_all_except (subfolder, name, except);
4283         }
4284 }
4285
4286 static GnomeVFSResult
4287 do_create (GnomeVFSMethod *method,
4288            GnomeVFSMethodHandle **method_handle,
4289            GnomeVFSURI *uri,
4290            GnomeVFSOpenMode mode,
4291            gboolean exclusive,
4292            guint perm,
4293            GnomeVFSContext *context)
4294 {
4295         GnomeVFSResult result = GNOME_VFS_OK;
4296         GnomeVFSMethodHandle *file_handle;
4297         GnomeVFSURI *file_uri;
4298         VFolderURI vuri;
4299         VFolderInfo *info;
4300         Folder *parent;
4301         Entry *entry;
4302         EntryFile *efile;
4303         char *s;
4304         GSList *li;
4305
4306         VFOLDER_URI_PARSE (uri, &vuri);
4307
4308         /* These can't be very nice FILE names */
4309         if (vuri.file == NULL ||
4310             vuri.ends_in_slash)
4311                 return GNOME_VFS_ERROR_INVALID_URI;
4312         
4313         if ( ! check_ext (vuri.file, ".desktop") &&
4314              ! strcmp (vuri.file, ".directory") == 0) {
4315                 return GNOME_VFS_ERROR_INVALID_URI;
4316         }
4317
4318         /* all scheme is read only */
4319         if (vuri.is_all_scheme)
4320                 return GNOME_VFS_ERROR_READ_ONLY;
4321
4322         info = get_vfolder_info (vuri.scheme, &result, context);
4323         if (info == NULL)
4324                 return result;
4325
4326         if (info->user_filename == NULL ||
4327             info->read_only)
4328                 return GNOME_VFS_ERROR_READ_ONLY;
4329
4330         parent = resolve_folder (info, vuri.path,
4331                                  TRUE /* ignore_basename */,
4332                                  &result, context);
4333         if (parent == NULL)
4334                 return result;
4335
4336         if (parent->read_only)
4337                 return GNOME_VFS_ERROR_READ_ONLY;
4338
4339         if (strcmp (vuri.file, ".directory") == 0) {
4340                 char *fname;
4341
4342                 G_LOCK (vfolder_lock);
4343
4344                 if (exclusive) {
4345                         char *desktop_file;
4346                         desktop_file = get_directory_file_unlocked (info, parent);
4347                         if (desktop_file != NULL) {
4348                                 g_free (desktop_file);
4349                                 G_UNLOCK (vfolder_lock);
4350                                 return GNOME_VFS_ERROR_FILE_EXISTS;
4351                         }
4352                 }
4353
4354                 if ( ! make_dirfile_private (info, parent)) {
4355                         G_UNLOCK (vfolder_lock);
4356                         return GNOME_VFS_ERROR_GENERIC;
4357                 }
4358                 fname = g_build_filename (g_get_home_dir (),
4359                                           parent->desktop_file,
4360                                           NULL);
4361                 s = gnome_vfs_get_uri_from_local_path (fname);
4362                 file_uri = gnome_vfs_uri_new (s);
4363                 g_free (fname);
4364                 g_free (s);
4365
4366                 if (file_uri == NULL) {
4367                         G_UNLOCK (vfolder_lock);
4368                         return GNOME_VFS_ERROR_GENERIC;
4369                 }
4370
4371                 result = (* parent_method->create) (parent_method,
4372                                                     &file_handle,
4373                                                     file_uri,
4374                                                     mode,
4375                                                     exclusive,
4376                                                     perm,
4377                                                     context);
4378                 gnome_vfs_uri_unref (file_uri);
4379
4380                 make_handle (method_handle,
4381                              file_handle,
4382                              info,
4383                              (Entry *)parent,
4384                              TRUE /* is_directory_file */,
4385                              TRUE /* write */);
4386
4387                 if (info->dirty)
4388                         vfolder_info_write_user (info);
4389
4390                 G_UNLOCK (vfolder_lock);
4391
4392                 return result;
4393         }
4394
4395         ensure_folder (info, parent,
4396                        FALSE /* subfolders */,
4397                        NULL /* except */,
4398                        FALSE /* ignore_unallocated */);
4399
4400         entry = find_entry (parent->entries, vuri.file);
4401
4402         if (entry != NULL &&
4403             entry->type == ENTRY_FOLDER)
4404                 return GNOME_VFS_ERROR_IS_DIRECTORY;
4405
4406         efile = (EntryFile *)entry;
4407
4408         if (efile != NULL) {
4409                 if (exclusive)
4410                         return GNOME_VFS_ERROR_FILE_EXISTS;
4411
4412                 G_LOCK (vfolder_lock);
4413                 if ( ! make_file_private (info, efile)) {
4414                         G_UNLOCK (vfolder_lock);
4415                         return GNOME_VFS_ERROR_GENERIC;
4416                 }
4417
4418                 s = gnome_vfs_get_uri_from_local_path (efile->filename);
4419                 file_uri = gnome_vfs_uri_new (s);
4420                 g_free (s);
4421
4422                 if (file_uri == NULL) {
4423                         G_UNLOCK (vfolder_lock);
4424                         return GNOME_VFS_ERROR_GENERIC;
4425                 }
4426
4427                 result = (* parent_method->create) (parent_method,
4428                                                     &file_handle,
4429                                                     file_uri,
4430                                                     mode,
4431                                                     exclusive,
4432                                                     perm,
4433                                                     context);
4434                 gnome_vfs_uri_unref (file_uri);
4435
4436                 make_handle (method_handle,
4437                              file_handle,
4438                              info,
4439                              (Entry *)efile,
4440                              FALSE /* is_directory_file */,
4441                              TRUE /* write */);
4442
4443                 G_UNLOCK (vfolder_lock);
4444
4445                 return result;
4446         }
4447
4448         G_LOCK (vfolder_lock);
4449         
4450         li = g_hash_table_lookup (info->entries_ht, vuri.file);
4451
4452         if (exclusive && li != NULL) {
4453                 G_UNLOCK (vfolder_lock);
4454                 return GNOME_VFS_ERROR_FILE_EXISTS;
4455         }
4456
4457         if (li == NULL) {
4458                 efile = file_new (vuri.file);
4459                 vfolder_info_insert_entry (info, efile);
4460                 entry_unref ((Entry *)efile);
4461         } else {
4462                 efile = li->data;
4463         }
4464
4465         /* this will make a private name for this */
4466         if ( ! make_file_private (info, efile)) {
4467                 G_UNLOCK (vfolder_lock);
4468                 return GNOME_VFS_ERROR_GENERIC;
4469         }
4470
4471         add_file (parent, vuri.file);
4472         parent->sorted = FALSE;
4473
4474         if (parent->up_to_date)
4475                 parent->entries = g_slist_prepend (parent->entries, efile);
4476
4477         /* if we created a brand new name, then we exclude it
4478          * from everywhere else to ensure overall sanity */
4479         if (li == NULL)
4480                 remove_from_all_except (info->root, vuri.file, parent);
4481
4482         s = gnome_vfs_get_uri_from_local_path (efile->filename);
4483         file_uri = gnome_vfs_uri_new (s);
4484         g_free (s);
4485
4486         result = (* parent_method->create) (parent_method,
4487                                             &file_handle,
4488                                             file_uri,
4489                                             mode,
4490                                             exclusive,
4491                                             perm,
4492                                             context);
4493         gnome_vfs_uri_unref (file_uri);
4494
4495         make_handle (method_handle,
4496                      file_handle,
4497                      info,
4498                      (Entry *)efile,
4499                      FALSE /* is_directory_file */,
4500                      TRUE /* write */);
4501
4502         vfolder_info_write_user (info);
4503
4504         G_UNLOCK (vfolder_lock);
4505
4506         return result;
4507 }
4508
4509 static GnomeVFSResult
4510 do_close (GnomeVFSMethod *method,
4511           GnomeVFSMethodHandle *method_handle,
4512           GnomeVFSContext *context)
4513 {
4514         GnomeVFSResult result;
4515         FileHandle *handle = (FileHandle *)method_handle;
4516         if (method_handle == (GnomeVFSMethodHandle *)method)
4517                 return GNOME_VFS_OK;
4518
4519         G_LOCK (vfolder_lock);
4520         
4521         result = (* parent_method->close) (parent_method,
4522                                            handle->handle,
4523                                            context);
4524         handle->handle = NULL;
4525
4526         /* we reread the Categories keyword */
4527         if (handle->write &&
4528             handle->entry != NULL &&
4529             handle->entry->type == ENTRY_FILE) {
4530                 EntryFile *efile = (EntryFile *)handle->entry;
4531                 char *categories;
4532                 readitem_entry (efile->filename,
4533                                 "Categories",
4534                                 &categories,
4535                                 NULL,
4536                                 NULL);
4537                 set_keywords (efile, categories);
4538                 g_free (categories);
4539                 /* FIXME: what about OnlyShowIn */
4540
4541                 /* FIXME: check if the keywords changed, if not, do
4542                  * nothing */
4543
4544                 /* Perhaps a bit drastic */
4545                 /* also this emits the CHANGED monitor signal */
4546                 invalidate_folder_T (handle->info->root);
4547
4548                 /* the file changed monitor will happen by itself
4549                  * as the underlying file is changed */
4550         } else if (handle->write &&
4551                    handle->entry != NULL &&
4552                    handle->entry->type == ENTRY_FOLDER &&
4553                    handle->is_directory_file) {
4554                 /* if we're monitoring this directory, emit the CHANGED
4555                  * monitor thing, it will also emit a changed on
4556                  * the file itself.  It is better to emit changed
4557                  * just in case. */
4558                 emit_monitor ((Folder *)(handle->entry),
4559                               GNOME_VFS_MONITOR_EVENT_CHANGED);
4560         }
4561
4562         whack_handle (handle);
4563
4564         G_UNLOCK (vfolder_lock);
4565
4566         return result;
4567 }
4568
4569 static void
4570 fill_buffer (gpointer buffer,
4571              GnomeVFSFileSize num_bytes,
4572              GnomeVFSFileSize *bytes_read)
4573 {
4574         char *buf = buffer;
4575         GnomeVFSFileSize i;
4576         for (i = 0; i < num_bytes; i++) {
4577                 if (rand () % 32 == 0 ||
4578                     i == num_bytes-1)
4579                         buf[i] = '\n';
4580                 else
4581                         buf[i] = ((rand()>>4) % 94) + 32;
4582         }
4583         if (bytes_read != 0)
4584                 *bytes_read = i;
4585 }
4586
4587 static GnomeVFSResult
4588 do_read (GnomeVFSMethod *method,
4589          GnomeVFSMethodHandle *method_handle,
4590          gpointer buffer,
4591          GnomeVFSFileSize num_bytes,
4592          GnomeVFSFileSize *bytes_read,
4593          GnomeVFSContext *context)
4594 {
4595         GnomeVFSResult result;
4596         FileHandle *handle = (FileHandle *)method_handle;
4597
4598         if (method_handle == (GnomeVFSMethodHandle *)method) {
4599                 if ((rand () >> 4) & 0x3) {
4600                         fill_buffer (buffer, num_bytes, bytes_read);
4601                         return GNOME_VFS_OK;
4602                 } else {
4603                         return GNOME_VFS_ERROR_EOF;
4604                 }
4605         }
4606         
4607         result = (* parent_method->read) (parent_method,
4608                                           handle->handle,
4609                                           buffer, num_bytes,
4610                                           bytes_read,
4611                                           context);
4612
4613         return result;
4614 }
4615
4616 static GnomeVFSResult
4617 do_write (GnomeVFSMethod *method,
4618           GnomeVFSMethodHandle *method_handle,
4619           gconstpointer buffer,
4620           GnomeVFSFileSize num_bytes,
4621           GnomeVFSFileSize *bytes_written,
4622           GnomeVFSContext *context)
4623 {
4624         GnomeVFSResult result;
4625         FileHandle *handle = (FileHandle *)method_handle;
4626
4627         if (method_handle == (GnomeVFSMethodHandle *)method)
4628                 return GNOME_VFS_OK;
4629
4630         result = (* parent_method->write) (parent_method,
4631                                            handle->handle,
4632                                            buffer, num_bytes,
4633                                            bytes_written,
4634                                            context);
4635
4636         return result;
4637 }
4638
4639
4640 static GnomeVFSResult
4641 do_seek (GnomeVFSMethod *method,
4642          GnomeVFSMethodHandle *method_handle,
4643          GnomeVFSSeekPosition whence,
4644          GnomeVFSFileOffset offset,
4645          GnomeVFSContext *context)
4646 {
4647         GnomeVFSResult result;
4648         FileHandle *handle = (FileHandle *)method_handle;
4649
4650         if (method_handle == (GnomeVFSMethodHandle *)method)
4651                 return GNOME_VFS_OK;
4652         
4653         result = (* parent_method->seek) (parent_method,
4654                                           handle->handle,
4655                                           whence, offset,
4656                                           context);
4657
4658         return result;
4659 }
4660
4661 static GnomeVFSResult
4662 do_tell (GnomeVFSMethod *method,
4663          GnomeVFSMethodHandle *method_handle,
4664          GnomeVFSFileOffset *offset_return)
4665 {
4666         GnomeVFSResult result;
4667         FileHandle *handle = (FileHandle *)method_handle;
4668         
4669         result = (* parent_method->tell) (parent_method,
4670                                           handle->handle,
4671                                           offset_return);
4672
4673         return result;
4674 }
4675
4676
4677 static GnomeVFSResult
4678 do_truncate_handle (GnomeVFSMethod *method,
4679                     GnomeVFSMethodHandle *method_handle,
4680                     GnomeVFSFileSize where,
4681                     GnomeVFSContext *context)
4682 {
4683         GnomeVFSResult result;
4684         FileHandle *handle = (FileHandle *)method_handle;
4685
4686         if (method_handle == (GnomeVFSMethodHandle *)method)
4687                 return GNOME_VFS_OK;
4688         
4689         result = (* parent_method->truncate_handle) (parent_method,
4690                                                      handle->handle,
4691                                                      where,
4692                                                      context);
4693
4694         return result;
4695 }
4696
4697 static GnomeVFSResult
4698 do_truncate (GnomeVFSMethod *method,
4699              GnomeVFSURI *uri,
4700              GnomeVFSFileSize where,
4701              GnomeVFSContext *context)
4702 {
4703         GnomeVFSURI *file_uri;
4704         GnomeVFSResult result = GNOME_VFS_OK;
4705         VFolderInfo *info;
4706         Entry *entry;
4707         VFolderURI vuri;
4708
4709         VFOLDER_URI_PARSE (uri, &vuri);
4710
4711         /* These can't be very nice FILE names */
4712         if (vuri.file == NULL ||
4713             vuri.ends_in_slash)
4714                 return GNOME_VFS_ERROR_INVALID_URI;
4715
4716         if (vuri.is_all_scheme)
4717                 return GNOME_VFS_ERROR_READ_ONLY;
4718
4719         info = get_vfolder_info (vuri.scheme, &result, context);
4720         if (info == NULL)
4721                 return result;
4722
4723         if (info->read_only)
4724                 return GNOME_VFS_ERROR_READ_ONLY;
4725
4726         G_LOCK (vfolder_lock);
4727         file_uri = desktop_uri_to_file_uri (info,
4728                                             &vuri,
4729                                             &entry,
4730                                             NULL /* the_is_directory_file */,
4731                                             NULL /* the_folder */,
4732                                             TRUE /* privatize */,
4733                                             &result,
4734                                             context);
4735         G_UNLOCK (vfolder_lock);
4736
4737         if (file_uri == NULL)
4738                 return result;
4739
4740         result = (* parent_method->truncate) (parent_method,
4741                                               file_uri,
4742                                               where,
4743                                               context);
4744
4745         gnome_vfs_uri_unref (file_uri);
4746
4747         if (info->dirty) {
4748                 G_LOCK (vfolder_lock);
4749                 vfolder_info_write_user (info);
4750                 G_UNLOCK (vfolder_lock);
4751         }
4752
4753         if (entry->type == ENTRY_FILE) {
4754                 EntryFile *efile = (EntryFile *)entry;
4755
4756                 G_LOCK (vfolder_lock);
4757                 g_slist_free (efile->keywords);
4758                 efile->keywords = NULL;
4759                 G_UNLOCK (vfolder_lock);
4760         }
4761
4762         /* Perhaps a bit drastic, but oh well */
4763         invalidate_folder (info->root);
4764
4765         return result;
4766 }
4767
4768 typedef struct _DirHandle DirHandle;
4769 struct _DirHandle {
4770         VFolderInfo *info;
4771         Folder *folder;
4772
4773         GnomeVFSFileInfoOptions options;
4774
4775         /* List of Entries */
4776         GSList *list;
4777         GSList *current;
4778 };
4779
4780 static GnomeVFSResult
4781 do_open_directory (GnomeVFSMethod *method,
4782                    GnomeVFSMethodHandle **method_handle,
4783                    GnomeVFSURI *uri,
4784                    GnomeVFSFileInfoOptions options,
4785                    GnomeVFSContext *context)
4786 {
4787         GnomeVFSResult result = GNOME_VFS_OK;
4788         VFolderURI vuri;
4789         DirHandle *dh;
4790         Folder *folder;
4791         VFolderInfo *info;
4792         char *desktop_file;
4793
4794         VFOLDER_URI_PARSE (uri, &vuri);
4795
4796         info = get_vfolder_info (vuri.scheme, &result, context);
4797         if (info == NULL)
4798                 return result;
4799
4800         /* In the all- scheme just list all filenames */
4801         if (vuri.is_all_scheme) {
4802                 if (any_subdir (vuri.path))
4803                         return GNOME_VFS_ERROR_NOT_FOUND;
4804
4805                 dh = g_new0 (DirHandle, 1);
4806                 dh->info = info;
4807                 dh->options = options;
4808                 dh->folder = NULL;
4809
4810                 G_LOCK (vfolder_lock);
4811                 dh->list = g_slist_copy (info->entries);
4812                 g_slist_foreach (dh->list, (GFunc)entry_ref, NULL);
4813                 dh->current = dh->list;
4814                 G_UNLOCK (vfolder_lock);
4815
4816                 *method_handle = (GnomeVFSMethodHandle*) dh;
4817                 return GNOME_VFS_OK;
4818         }
4819
4820         folder = resolve_folder (info, vuri.path,
4821                                  FALSE /* ignore_basename */,
4822                                  &result, context);
4823         if (folder == NULL)
4824                 return result;
4825
4826         /* Make sure we have the entries and sorted here */
4827         ensure_folder_sort (info, folder);
4828
4829         dh = g_new0 (DirHandle, 1);
4830         dh->info = info;
4831         dh->options = options;
4832
4833         G_LOCK (vfolder_lock);
4834         dh->folder = (Folder *)entry_ref ((Entry *)folder);
4835         dh->list = g_slist_copy (folder->entries);
4836         g_slist_foreach (folder->entries, (GFunc)entry_ref, NULL);
4837         G_UNLOCK (vfolder_lock);
4838
4839         desktop_file = get_directory_file (info, folder);
4840         if (desktop_file != NULL) {
4841                 EntryFile *efile = file_new (".directory");
4842                 dh->list = g_slist_prepend (dh->list, efile);
4843                 g_free (desktop_file);
4844         }
4845
4846         dh->current = dh->list;
4847
4848         *method_handle = (GnomeVFSMethodHandle*) dh;
4849
4850         return GNOME_VFS_OK;
4851 }
4852
4853 static GnomeVFSResult
4854 do_close_directory (GnomeVFSMethod *method,
4855                     GnomeVFSMethodHandle *method_handle,
4856                     GnomeVFSContext *context)
4857 {
4858         DirHandle *dh;
4859
4860         dh = (DirHandle*) method_handle;
4861
4862         G_LOCK (vfolder_lock);
4863
4864         g_slist_foreach (dh->list, (GFunc)entry_unref, NULL);
4865         g_slist_free (dh->list);
4866         dh->list = NULL;
4867
4868         dh->current = NULL;
4869
4870         if (dh->folder != NULL)
4871                 entry_unref ((Entry *)dh->folder);
4872         dh->folder = NULL;
4873
4874         dh->info = NULL;
4875
4876         g_free (dh);
4877
4878         G_UNLOCK (vfolder_lock);
4879
4880         return GNOME_VFS_OK;
4881 }
4882
4883 static GnomeVFSResult
4884 do_read_directory (GnomeVFSMethod *method,
4885                    GnomeVFSMethodHandle *method_handle,
4886                    GnomeVFSFileInfo *file_info,
4887                    GnomeVFSContext *context)
4888 {
4889         DirHandle *dh;
4890         Entry *entry;
4891         GnomeVFSFileInfoOptions options;
4892
4893         dh = (DirHandle*) method_handle;
4894
4895 read_directory_again:
4896
4897         if (dh->current == NULL) {
4898                 return GNOME_VFS_ERROR_EOF;
4899         }
4900
4901         entry = dh->current->data;
4902         dh->current = dh->current->next;
4903
4904         options = dh->options;
4905
4906         if (entry->type == ENTRY_FILE &&
4907             ((EntryFile *)entry)->filename != NULL) {
4908                 EntryFile *efile = (EntryFile *)entry;
4909                 char *furi = gnome_vfs_get_uri_from_local_path (efile->filename);
4910                 GnomeVFSURI *uri = gnome_vfs_uri_new (furi);
4911
4912                 /* we always get mime-type by forcing it below */
4913                 if (options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
4914                         options &= ~GNOME_VFS_FILE_INFO_GET_MIME_TYPE;
4915
4916                 file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
4917
4918                 /* Get the file info for this */
4919                 (* parent_method->get_file_info) (parent_method,
4920                                                   uri,
4921                                                   file_info,
4922                                                   options,
4923                                                   context);
4924
4925                 /* we ignore errors from this since the file_info just
4926                  * won't be filled completely if there's an error, that's all */
4927
4928                 g_free (file_info->mime_type);
4929                 file_info->mime_type = g_strdup ("application/x-gnome-app-info");
4930                 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
4931
4932                 /* Now we wipe those fields we don't support */
4933                 file_info->valid_fields &= ~(UNSUPPORTED_INFO_FIELDS);
4934
4935                 gnome_vfs_uri_unref (uri);
4936                 g_free (furi);
4937         } else if (entry->type == ENTRY_FILE) {
4938                 file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
4939
4940                 file_info->name = g_strdup (entry->name);
4941                 GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
4942
4943                 file_info->type = GNOME_VFS_FILE_TYPE_REGULAR;
4944                 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
4945
4946                 /* FIXME: Is this correct? isn't there an xdg mime type? */
4947                 file_info->mime_type = g_strdup ("application/x-gnome-app-info");
4948                 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
4949
4950                 /* FIXME: get some ctime/mtime */
4951         } else /* ENTRY_FOLDER */ {
4952                 Folder *folder = (Folder *)entry;
4953
4954                 /* Skip empty folders if they have
4955                  * the flag set */
4956                 if (folder->dont_show_if_empty) {
4957                         /* Make sure we have the entries */
4958                         ensure_folder (dh->info, folder,
4959                                        FALSE /* subfolders */,
4960                                        NULL /* except */,
4961                                        FALSE /* ignore_unallocated */);
4962
4963                         if (folder->entries == NULL) {
4964                                 /* start this function over on the
4965                                  * next item */
4966                                 goto read_directory_again;
4967                         }
4968                 }
4969
4970                 file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
4971
4972                 file_info->name = g_strdup (entry->name);
4973                 GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
4974
4975                 file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
4976                 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
4977
4978                 file_info->mime_type = g_strdup ("x-directory/vfolder-desktop");
4979                 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
4980
4981                 file_info->ctime = dh->info->modification_time;
4982                 file_info->mtime = dh->info->modification_time;
4983                 file_info->valid_fields |= (GNOME_VFS_FILE_INFO_FIELDS_CTIME |
4984                                             GNOME_VFS_FILE_INFO_FIELDS_MTIME);
4985         }
4986
4987         return GNOME_VFS_OK;
4988 }
4989
4990 static GnomeVFSResult
4991 do_get_file_info (GnomeVFSMethod *method,
4992                   GnomeVFSURI *uri,
4993                   GnomeVFSFileInfo *file_info,
4994                   GnomeVFSFileInfoOptions options,
4995                   GnomeVFSContext *context)
4996 {
4997         GnomeVFSURI *file_uri;
4998         GnomeVFSResult result = GNOME_VFS_OK;
4999         Folder *folder;
5000         VFolderInfo *info;
5001         VFolderURI vuri;
5002
5003         VFOLDER_URI_PARSE (uri, &vuri);
5004
5005         info = get_vfolder_info (vuri.scheme, &result, context);
5006         if (info == NULL)
5007                 return result;
5008
5009         G_LOCK (vfolder_lock);
5010         file_uri = desktop_uri_to_file_uri (info,
5011                                             &vuri,
5012                                             NULL /* the_entry */,
5013                                             NULL /* the_is_directory_file */,
5014                                             &folder,
5015                                             FALSE /* privatize */,
5016                                             &result,
5017                                             context);
5018         G_UNLOCK (vfolder_lock);
5019
5020         if (file_uri == NULL &&
5021             result != GNOME_VFS_ERROR_IS_DIRECTORY)
5022                 return result;
5023
5024         if (file_uri != NULL) {
5025                 /* we always get mime-type by forcing it below */
5026                 if (options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
5027                         options &= ~GNOME_VFS_FILE_INFO_GET_MIME_TYPE;
5028
5029                 result = (* parent_method->get_file_info) (parent_method,
5030                                                            file_uri,
5031                                                            file_info,
5032                                                            options,
5033                                                            context);
5034
5035                 g_free (file_info->mime_type);
5036                 file_info->mime_type = g_strdup ("application/x-gnome-app-info");
5037                 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
5038
5039                 /* Now we wipe those fields we don't support */
5040                 file_info->valid_fields &= ~(UNSUPPORTED_INFO_FIELDS);
5041
5042                 gnome_vfs_uri_unref (file_uri);
5043
5044                 return result;
5045         } else if (folder != NULL) {
5046                 file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
5047
5048                 file_info->name = g_strdup (folder->entry.name);
5049                 GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
5050
5051                 file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
5052                 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
5053
5054                 file_info->mime_type = g_strdup ("x-directory/vfolder-desktop");
5055                 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
5056
5057                 file_info->ctime = info->modification_time;
5058                 file_info->mtime = info->modification_time;
5059                 file_info->valid_fields |= (GNOME_VFS_FILE_INFO_FIELDS_CTIME |
5060                                             GNOME_VFS_FILE_INFO_FIELDS_MTIME);
5061
5062                 return GNOME_VFS_OK;
5063         } else {
5064                 return GNOME_VFS_ERROR_NOT_FOUND;
5065         }
5066 }
5067
5068 static GnomeVFSResult
5069 do_get_file_info_from_handle (GnomeVFSMethod *method,
5070                               GnomeVFSMethodHandle *method_handle,
5071                               GnomeVFSFileInfo *file_info,
5072                               GnomeVFSFileInfoOptions options,
5073                               GnomeVFSContext *context)
5074 {
5075         GnomeVFSResult result;
5076         FileHandle *handle = (FileHandle *)method_handle;
5077
5078         if (method_handle == (GnomeVFSMethodHandle *)method) {
5079                 g_free (file_info->mime_type);
5080                 file_info->mime_type = g_strdup ("text/plain");
5081                 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
5082                 return GNOME_VFS_OK;
5083         }
5084
5085         /* we always get mime-type by forcing it below */
5086         if (options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
5087                 options &= ~GNOME_VFS_FILE_INFO_GET_MIME_TYPE;
5088
5089         result = (* parent_method->get_file_info_from_handle) (parent_method,
5090                                                                handle->handle,
5091                                                                file_info,
5092                                                                options,
5093                                                                context);
5094
5095         /* any file is of the .desktop type */
5096         g_free (file_info->mime_type);
5097         file_info->mime_type = g_strdup ("application/x-gnome-app-info");
5098         file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
5099
5100         /* Now we wipe those fields we don't support */
5101         file_info->valid_fields &= ~(UNSUPPORTED_INFO_FIELDS);
5102
5103         return result;
5104 }
5105
5106
5107 static gboolean
5108 do_is_local (GnomeVFSMethod *method,
5109              const GnomeVFSURI *uri)
5110 {
5111         return TRUE;
5112 }
5113
5114 static void
5115 try_free_folder_monitors_create_unlocked (VFolderInfo *info,
5116                                           Folder *folder)
5117 {
5118         GSList *li, *list;
5119
5120         list = g_slist_copy (info->free_folder_monitors);
5121
5122         for (li = list; li != NULL; li = li->next) {
5123                 FileMonitorHandle *handle = li->data;
5124                 Folder *f;
5125                 VFolderURI vuri;
5126                 GnomeVFSResult result;
5127
5128                 /* Evil! EVIL URI PARSING. this will eat a lot of stack if we
5129                  * have lots of free monitors */
5130
5131                 VFOLDER_URI_PARSE (handle->uri, &vuri);
5132
5133                 f = resolve_folder (info, 
5134                                          vuri.path,
5135                                          FALSE /* ignore_basename */,
5136                                          &result,
5137                                          NULL);
5138
5139                 if (folder != f)
5140                         continue;
5141
5142                 info->free_folder_monitors =
5143                         g_slist_remove (info->free_folder_monitors, handle);
5144                 ((Entry *)folder)->monitors =
5145                         g_slist_prepend (((Entry *)folder)->monitors, handle);
5146
5147                 handle->exists = TRUE;
5148                 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *)handle,
5149                                             handle->uri, 
5150                                             GNOME_VFS_MONITOR_EVENT_CREATED);
5151         }
5152 }
5153
5154
5155 static GnomeVFSResult
5156 do_make_directory (GnomeVFSMethod *method,
5157                    GnomeVFSURI *uri,
5158                    guint perm,
5159                    GnomeVFSContext *context)
5160 {
5161         GnomeVFSResult result = GNOME_VFS_OK;
5162         VFolderInfo *info;
5163         Folder *parent, *folder;
5164         VFolderURI vuri;
5165
5166         VFOLDER_URI_PARSE (uri, &vuri);
5167
5168         if (vuri.is_all_scheme)
5169                 return GNOME_VFS_ERROR_READ_ONLY;
5170
5171         info = get_vfolder_info (vuri.scheme, &result, context);
5172         if (info == NULL)
5173                 return result;
5174
5175         if (info->user_filename == NULL ||
5176             info->read_only)
5177                 return GNOME_VFS_ERROR_READ_ONLY;
5178
5179         parent = resolve_folder (info, vuri.path,
5180                                  TRUE /* ignore_basename */,
5181                                  &result, context);
5182         if (parent == NULL)
5183                 return result;
5184         else if (parent->read_only)
5185                 return GNOME_VFS_ERROR_READ_ONLY;
5186
5187         G_LOCK (vfolder_lock);
5188
5189         folder = (Folder *)find_entry (parent->subfolders,
5190                                        vuri.file);
5191         if (folder != NULL) {
5192                 G_UNLOCK (vfolder_lock);
5193                 return GNOME_VFS_ERROR_FILE_EXISTS;
5194         }
5195
5196         folder = folder_new (vuri.file);
5197         parent->subfolders = g_slist_append (parent->subfolders, folder);
5198         folder->parent = parent;
5199         parent->up_to_date = FALSE;
5200
5201         try_free_folder_monitors_create_unlocked (info, folder);
5202
5203         /* parent changed */
5204         emit_monitor (parent, GNOME_VFS_MONITOR_EVENT_CHANGED);
5205
5206         vfolder_info_write_user (info);
5207         G_UNLOCK (vfolder_lock);
5208
5209         return GNOME_VFS_OK;
5210 }
5211
5212 static GnomeVFSResult
5213 do_remove_directory (GnomeVFSMethod *method,
5214                      GnomeVFSURI *uri,
5215                      GnomeVFSContext *context)
5216 {
5217         GnomeVFSResult result = GNOME_VFS_OK;
5218         Folder *folder;
5219         VFolderInfo *info;
5220         VFolderURI vuri;
5221
5222         VFOLDER_URI_PARSE (uri, &vuri);
5223
5224         if (vuri.is_all_scheme)
5225                 return GNOME_VFS_ERROR_READ_ONLY;
5226
5227         info = get_vfolder_info (vuri.scheme, &result, context);
5228         if (info == NULL)
5229                 return result;
5230
5231         if (info->user_filename == NULL ||
5232             info->read_only)
5233                 return GNOME_VFS_ERROR_READ_ONLY;
5234
5235         G_LOCK (vfolder_lock);
5236
5237         folder = resolve_folder (info, vuri.path,
5238                                  FALSE /* ignore_basename */,
5239                                  &result, context);
5240         if (folder == NULL) {
5241                 G_UNLOCK (vfolder_lock);
5242                 return result;
5243         }
5244
5245         if (folder->read_only ||
5246             (folder->parent != NULL &&
5247              folder->parent->read_only)) {
5248                 G_UNLOCK (vfolder_lock);
5249                 return GNOME_VFS_ERROR_READ_ONLY;
5250         }
5251
5252         /* don't make removing directories easy */
5253         if (folder->desktop_file != NULL) {
5254                 G_UNLOCK (vfolder_lock);
5255                 return GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY;
5256         }
5257
5258         /* Make sure we have the entries */
5259         ensure_folder_unlocked (info, folder,
5260                                 FALSE /* subfolders */,
5261                                 NULL /* except */,
5262                                 FALSE /* ignore_unallocated */);
5263
5264         /* don't make removing directories easy */
5265         if (folder->entries != NULL) {
5266                 G_UNLOCK (vfolder_lock);
5267                 return GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY;
5268         }
5269
5270         emit_and_delete_monitor (info, folder);
5271
5272         if (folder->only_unallocated) {
5273                 GSList *li = g_slist_find (info->unallocated_folders,
5274                                            folder);
5275                 if (li != NULL) {
5276                         info->unallocated_folders = g_slist_delete_link
5277                                 (info->unallocated_folders, li);
5278                         entry_unref ((Entry *)folder);
5279                 }
5280         }
5281
5282         if (folder == info->root) {
5283                 info->root = NULL;
5284                 entry_unref ((Entry *)folder);
5285                 info->root = folder_new ("Root");
5286         } else {
5287                 Folder *parent = folder->parent;
5288
5289                 g_assert (parent != NULL);
5290
5291                 parent->subfolders =
5292                         g_slist_remove (parent->subfolders, folder);
5293
5294                 parent->up_to_date = FALSE;
5295
5296                 entry_unref ((Entry *)folder);
5297
5298                 /* parent changed */
5299                 emit_monitor (parent, GNOME_VFS_MONITOR_EVENT_CHANGED);
5300         }
5301
5302         vfolder_info_write_user (info);
5303
5304         G_UNLOCK (vfolder_lock);
5305
5306         return GNOME_VFS_OK;
5307 }
5308
5309 /* a fairly evil function that does the whole move bit by copy and
5310  * remove */
5311 static GnomeVFSResult
5312 long_move (GnomeVFSMethod *method,
5313            VFolderURI *old_vuri,
5314            VFolderURI *new_vuri,
5315            gboolean force_replace,
5316            GnomeVFSContext *context)
5317 {
5318         GnomeVFSResult result;
5319         GnomeVFSMethodHandle *handle;
5320         GnomeVFSURI *file_uri;
5321         const char *path;
5322         int fd;
5323         char buf[BUFSIZ];
5324         int bytes;
5325         VFolderInfo *info;
5326
5327         info = get_vfolder_info (old_vuri->scheme, &result, context);
5328         if (info == NULL)
5329                 return result;
5330
5331         G_LOCK (vfolder_lock);
5332         file_uri = desktop_uri_to_file_uri (info,
5333                                             old_vuri,
5334                                             NULL /* the_entry */,
5335                                             NULL /* the_is_directory_file */,
5336                                             NULL /* the_folder */,
5337                                             FALSE /* privatize */,
5338                                             &result,
5339                                             context);
5340         G_UNLOCK (vfolder_lock);
5341
5342         if (file_uri == NULL)
5343                 return result;
5344
5345         path = gnome_vfs_uri_get_path (file_uri);
5346         if (path == NULL) {
5347                 gnome_vfs_uri_unref (file_uri);
5348                 return GNOME_VFS_ERROR_INVALID_URI;
5349         }
5350
5351         fd = open (path, O_RDONLY);
5352         if (fd < 0) {
5353                 gnome_vfs_uri_unref (file_uri);
5354                 return gnome_vfs_result_from_errno ();
5355         }
5356
5357         gnome_vfs_uri_unref (file_uri);
5358
5359         info->inhibit_write++;
5360
5361         result = method->create (method,
5362                                  &handle,
5363                                  new_vuri->uri,
5364                                  GNOME_VFS_OPEN_WRITE,
5365                                  force_replace /* exclusive */,
5366                                  0600 /* perm */,
5367                                  context);
5368         if (result != GNOME_VFS_OK) {
5369                 close (fd);
5370                 info->inhibit_write--;
5371                 return result;
5372         }
5373
5374         while ((bytes = read (fd, buf, BUFSIZ)) > 0) {
5375                 GnomeVFSFileSize bytes_written = 0;
5376                 result = method->write (method,
5377                                         handle,
5378                                         buf,
5379                                         bytes,
5380                                         &bytes_written,
5381                                         context);
5382                 if (result == GNOME_VFS_OK &&
5383                     bytes_written != bytes)
5384                         result = GNOME_VFS_ERROR_NO_SPACE;
5385                 if (result != GNOME_VFS_OK) {
5386                         close (fd);
5387                         method->close (method, handle, context);
5388                         /* FIXME: is this completely correct ? */
5389                         method->unlink (method,
5390                                         new_vuri->uri,
5391                                         context);
5392                         G_LOCK (vfolder_lock);
5393                         info->inhibit_write--;
5394                         vfolder_info_write_user (info);
5395                         G_UNLOCK (vfolder_lock);
5396                         return result;
5397                 }
5398         }
5399
5400         close (fd);
5401
5402         result = method->close (method, handle, context);
5403         if (result != GNOME_VFS_OK) {
5404                 G_LOCK (vfolder_lock);
5405                 info->inhibit_write--;
5406                 vfolder_info_write_user (info);
5407                 G_UNLOCK (vfolder_lock);
5408                 return result;
5409         }
5410
5411         result = method->unlink (method, old_vuri->uri, context);
5412
5413         G_LOCK (vfolder_lock);
5414         info->inhibit_write--;
5415         vfolder_info_write_user (info);
5416         G_UNLOCK (vfolder_lock);
5417
5418         return result;
5419 }
5420
5421 static GnomeVFSResult
5422 move_directory_file (VFolderInfo *info,
5423                      Folder *old_folder,
5424                      Folder *new_folder)
5425 {
5426         if (old_folder->desktop_file == NULL)
5427                 return GNOME_VFS_ERROR_NOT_FOUND;
5428
5429         /* "move" the desktop file */
5430         g_free (new_folder->desktop_file);
5431         new_folder->desktop_file = old_folder->desktop_file;
5432         old_folder->desktop_file = NULL;
5433
5434         /* is this too drastic, it will requery the folder? */
5435         new_folder->up_to_date = FALSE;
5436         old_folder->up_to_date = FALSE;
5437
5438         emit_monitor (new_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5439         emit_monitor (old_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5440
5441         vfolder_info_write_user (info);
5442
5443         return GNOME_VFS_OK;
5444 }
5445
5446 static gboolean
5447 is_sub (Folder *master, Folder *sub)
5448 {
5449         GSList *li;
5450
5451         for (li = master->subfolders; li != NULL; li = li->next) {
5452                 Folder *subfolder = li->data;
5453
5454                 if (subfolder == sub ||
5455                     is_sub (subfolder, sub))
5456                         return TRUE;
5457         }
5458
5459         return FALSE;
5460 }
5461
5462 static GnomeVFSResult
5463 move_folder (VFolderInfo *info,
5464              Folder *old_folder, Entry *old_entry,
5465              Folder *new_folder, Entry *new_entry)
5466 {
5467         Folder *source = (Folder *)old_entry;
5468         Folder *target;
5469
5470         if (new_entry != NULL &&
5471             new_entry->type != ENTRY_FOLDER)
5472                 return GNOME_VFS_ERROR_NOT_A_DIRECTORY;
5473         
5474         if (new_entry != NULL) {
5475                 target = (Folder *)new_entry;
5476         } else {
5477                 target = new_folder;
5478         }
5479
5480         /* move to where we are, yay, we're done :) */
5481         if (source->parent == target)
5482                 return GNOME_VFS_OK;
5483
5484         if (source == target ||
5485             is_sub (source, target))
5486                 return GNOME_VFS_ERROR_LOOP;
5487
5488         /* this will never happen, but we're paranoid */
5489         if (source->parent == NULL)
5490                 return GNOME_VFS_ERROR_LOOP;
5491
5492         source->parent->subfolders = g_slist_remove (source->parent->subfolders,
5493                                                      source);
5494         target->subfolders = g_slist_append (target->subfolders,
5495                                              source);
5496
5497         source->parent = target;
5498
5499         source->up_to_date = FALSE;
5500         target->up_to_date = FALSE;
5501
5502         emit_monitor (source, GNOME_VFS_MONITOR_EVENT_CHANGED);
5503         emit_monitor (target, GNOME_VFS_MONITOR_EVENT_CHANGED);
5504
5505         vfolder_info_write_user (info);
5506
5507         return GNOME_VFS_OK;
5508 }
5509
5510 static GnomeVFSResult
5511 do_move (GnomeVFSMethod *method,
5512          GnomeVFSURI *old_uri,
5513          GnomeVFSURI *new_uri,
5514          gboolean force_replace,
5515          GnomeVFSContext *context)
5516 {
5517         GnomeVFSResult result = GNOME_VFS_OK;
5518         VFolderInfo *info;
5519         Folder *old_folder, *new_folder;
5520         Entry *old_entry, *new_entry;
5521         gboolean old_is_directory_file, new_is_directory_file;
5522         VFolderURI old_vuri, new_vuri;
5523
5524         VFOLDER_URI_PARSE (old_uri, &old_vuri);
5525         VFOLDER_URI_PARSE (new_uri, &new_vuri);
5526
5527         if (old_vuri.file == NULL)
5528                 return GNOME_VFS_ERROR_INVALID_URI;
5529
5530         if (old_vuri.is_all_scheme)
5531                 return GNOME_VFS_ERROR_READ_ONLY;
5532
5533         if (strcmp (old_vuri.scheme, new_vuri.scheme) != 0)
5534                 return GNOME_VFS_ERROR_NOT_SAME_FILE_SYSTEM;
5535
5536         info = get_vfolder_info (old_vuri.scheme, &result, context);
5537         if (info == NULL)
5538                 return result;
5539
5540         if (info->read_only)
5541                 return GNOME_VFS_ERROR_READ_ONLY;
5542
5543         old_entry = get_entry (&old_vuri,
5544                                &old_folder,
5545                                &old_is_directory_file,
5546                                &result,
5547                                context);
5548         if (old_entry == NULL)
5549                 return result;
5550
5551         if (old_folder != NULL && old_folder->read_only)
5552                 return GNOME_VFS_ERROR_READ_ONLY;
5553
5554         new_entry = get_entry (&new_vuri,
5555                                &new_folder,
5556                                &new_is_directory_file,
5557                                &result,
5558                                context);
5559         if (new_entry == NULL && new_folder == NULL)
5560                 return result;
5561
5562         if (new_folder != NULL && new_folder->read_only)
5563                 return GNOME_VFS_ERROR_READ_ONLY;
5564
5565         if (new_is_directory_file != old_is_directory_file) {
5566                 /* this will do another set of lookups
5567                  * perhaps this can be done in a nicer way,
5568                  * but is this the common case? I don't think so */
5569                 return long_move (method, &old_vuri, &new_vuri,
5570                                   force_replace, context);
5571         }
5572         
5573         if (new_is_directory_file) {
5574                 g_assert (old_entry != NULL);
5575                 g_assert (new_entry != NULL);
5576                 G_LOCK (vfolder_lock);
5577                 result = move_directory_file (info,
5578                                               (Folder *)old_entry,
5579                                               (Folder *)new_entry);
5580                 G_UNLOCK (vfolder_lock);
5581                 return result;
5582         }
5583
5584         if (old_entry->type == ENTRY_FOLDER) {
5585                 G_LOCK (vfolder_lock);
5586                 result = move_folder (info,
5587                                       old_folder, old_entry,
5588                                       new_folder, new_entry);
5589                 G_UNLOCK (vfolder_lock);
5590                 return result;
5591         }
5592
5593         /* move into self, just whack the old one */
5594         if (old_entry == new_entry) {
5595                 /* same folder */
5596                 if (new_folder == old_folder)
5597                         return GNOME_VFS_OK;
5598
5599                 if ( ! force_replace)
5600                         return GNOME_VFS_ERROR_FILE_EXISTS;
5601
5602                 G_LOCK (vfolder_lock);
5603
5604                 remove_file (old_folder, old_vuri.file);
5605
5606                 old_folder->entries = g_slist_remove (old_folder->entries, 
5607                                                       old_entry);
5608                 entry_unref (old_entry);
5609
5610                 emit_monitor (old_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5611
5612                 vfolder_info_write_user (info);
5613
5614                 G_UNLOCK (vfolder_lock);
5615
5616                 return GNOME_VFS_OK;
5617         }
5618
5619         /* this is a simple move */
5620         if (new_entry == NULL ||
5621             new_entry->type == ENTRY_FOLDER) {
5622                 if (new_entry != NULL) {
5623                         new_folder = (Folder *)new_entry;
5624                 } else {
5625                         /* a file and a totally different one */
5626                         if (strcmp (new_vuri.file, old_entry->name) != 0) {
5627                                 /* yay, a long move */
5628                                 /* this will do another set of lookups
5629                                  * perhaps this can be done in a nicer way,
5630                                  * but is this the common case? I don't think
5631                                  * so */
5632                                 return long_move (method, &old_vuri, &new_vuri,
5633                                                   force_replace, context);
5634                         }
5635                 }
5636
5637                 /* same folder */
5638                 if (new_folder == old_folder)
5639                         return GNOME_VFS_OK;
5640
5641                 G_LOCK (vfolder_lock);
5642
5643                 remove_file (old_folder, old_entry->name);
5644                 add_file (new_folder, old_entry->name);
5645
5646                 new_folder->entries = g_slist_prepend (new_folder->entries, 
5647                                                        old_entry);
5648                 entry_ref (old_entry);
5649                 new_folder->sorted = FALSE;
5650
5651                 old_folder->entries = g_slist_remove (old_folder->entries, 
5652                                                       old_entry);
5653                 entry_unref (old_entry);
5654
5655                 emit_monitor (new_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5656                 emit_monitor (old_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5657
5658                 vfolder_info_write_user (info);
5659
5660                 G_UNLOCK (vfolder_lock);
5661
5662                 return GNOME_VFS_OK;
5663         }
5664
5665         /* do we EVER get here? */
5666
5667         /* this will do another set of lookups
5668          * perhaps this can be done in a nicer way,
5669          * but is this the common case? I don't think so */
5670         return long_move (method, &old_vuri, &new_vuri,
5671                           force_replace, context);
5672 }
5673
5674 static GnomeVFSResult
5675 do_unlink (GnomeVFSMethod *method,
5676            GnomeVFSURI *uri,
5677            GnomeVFSContext *context)
5678 {
5679         GnomeVFSResult result = GNOME_VFS_OK;
5680         Entry *entry;
5681         Folder *the_folder;
5682         gboolean is_directory_file;
5683         VFolderInfo *info;
5684         VFolderURI vuri;
5685         GSList *li;
5686
5687         VFOLDER_URI_PARSE (uri, &vuri);
5688
5689         if (vuri.file == NULL)
5690                 return GNOME_VFS_ERROR_INVALID_URI;
5691         
5692         if (vuri.is_all_scheme == TRUE)
5693                 return GNOME_VFS_ERROR_READ_ONLY;
5694
5695         info = get_vfolder_info (vuri.scheme, &result, context);
5696         if (info == NULL)
5697                 return result;
5698         else if (info->read_only)
5699                 return GNOME_VFS_ERROR_READ_ONLY;
5700
5701         entry = get_entry (&vuri,
5702                            &the_folder,
5703                            &is_directory_file,
5704                            &result, context);
5705         if (entry == NULL) 
5706                 return result;
5707         else if (the_folder != NULL &&
5708                  the_folder->read_only)
5709                 return GNOME_VFS_ERROR_READ_ONLY;
5710
5711         if (entry->type == ENTRY_FOLDER &&
5712             is_directory_file) {
5713                 Folder *folder = (Folder *)entry;
5714
5715                 if (folder->desktop_file == NULL)
5716                         return GNOME_VFS_ERROR_NOT_FOUND;
5717
5718                 G_LOCK (vfolder_lock);
5719
5720                 g_free (folder->desktop_file);
5721                 folder->desktop_file = NULL;
5722
5723                 emit_monitor (folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5724
5725                 vfolder_info_write_user (info);
5726
5727                 G_UNLOCK (vfolder_lock);
5728
5729                 return GNOME_VFS_OK;
5730         } else if (entry->type == ENTRY_FOLDER) {
5731                 return GNOME_VFS_ERROR_IS_DIRECTORY;
5732         } else if (the_folder == NULL) {
5733                 return GNOME_VFS_ERROR_NOT_FOUND;
5734         }
5735
5736         G_LOCK (vfolder_lock);
5737
5738         the_folder->entries = g_slist_remove (the_folder->entries,
5739                                               entry);
5740         entry_unref (entry);
5741
5742         remove_file (the_folder, vuri.file);
5743
5744         emit_monitor (the_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5745
5746         /* evil, we must remove this from the unallocated folders as well
5747          * so that it magically doesn't appear there.  But it's not so simple.
5748          * We only want to remove it if it isn't in that folder already. */
5749         for (li = info->unallocated_folders;
5750              li != NULL;
5751              li = li->next) {
5752                 Folder *folder = li->data;
5753                 GSList *l;
5754
5755                 /* This is actually really evil since ensuring 
5756                  * an unallocated folder clears all other unallocated
5757                  * folders in it's wake.  I'm not sure it's worth
5758                  * optimizing however */
5759                 ensure_folder_unlocked (info, folder,
5760                                         FALSE /* subfolders */,
5761                                         NULL /* except */,
5762                                         FALSE /* ignore_unallocated */);
5763                 l = g_slist_find (folder->entries, entry);
5764                 if (l == NULL) {
5765                         remove_file (folder, vuri.file);
5766                 }
5767         }
5768
5769         emit_file_deleted_monitor (info, entry, the_folder);
5770
5771         /* FIXME: if this was a user file and this is the only
5772          * reference to it, unlink it. */
5773
5774         vfolder_info_write_user (info);
5775
5776         G_UNLOCK (vfolder_lock);
5777
5778         return GNOME_VFS_OK;
5779 }
5780
5781 static GnomeVFSResult
5782 do_check_same_fs (GnomeVFSMethod *method,
5783                   GnomeVFSURI *source_uri,
5784                   GnomeVFSURI *target_uri,
5785                   gboolean *same_fs_return,
5786                   GnomeVFSContext *context)
5787 {
5788         VFolderURI source_vuri, target_vuri;
5789
5790         *same_fs_return = FALSE;
5791
5792         VFOLDER_URI_PARSE (source_uri, &source_vuri);
5793         VFOLDER_URI_PARSE (target_uri, &target_vuri);
5794
5795         if (strcmp (source_vuri.scheme, target_vuri.scheme) != 0 ||
5796             source_vuri.is_all_scheme != target_vuri.is_all_scheme)
5797                 *same_fs_return = FALSE;
5798         else
5799                 *same_fs_return = TRUE;
5800
5801         return GNOME_VFS_OK;
5802 }
5803
5804 static GnomeVFSResult
5805 do_set_file_info (GnomeVFSMethod *method,
5806                   GnomeVFSURI *uri,
5807                   const GnomeVFSFileInfo *info,
5808                   GnomeVFSSetFileInfoMask mask,
5809                   GnomeVFSContext *context)
5810 {
5811         VFolderURI vuri;
5812
5813         VFOLDER_URI_PARSE (uri, &vuri);
5814
5815         if (vuri.file == NULL)
5816                 return GNOME_VFS_ERROR_INVALID_URI;
5817
5818         if (mask & GNOME_VFS_SET_FILE_INFO_NAME) {
5819                 GnomeVFSResult result = GNOME_VFS_OK;
5820                 char *dirname = gnome_vfs_uri_extract_dirname (uri);
5821                 GnomeVFSURI *new_uri = gnome_vfs_uri_dup (uri);
5822
5823                 G_LOCK (vfolder_lock);
5824                 g_free (new_uri->text);
5825                 new_uri->text = g_build_path ("/", dirname, info->name, NULL);
5826                 G_UNLOCK (vfolder_lock);
5827
5828                 result = do_move (method,
5829                                   uri,
5830                                   new_uri,
5831                                   FALSE /* force_replace */,
5832                                   context);
5833
5834                 g_free (dirname);
5835                 gnome_vfs_uri_unref (new_uri);  
5836                 return result;
5837         } else {
5838                 /* We don't support setting any of this other permission,
5839                  * times and all that voodoo */
5840                 return GNOME_VFS_ERROR_NOT_SUPPORTED;
5841         }
5842 }
5843
5844 static GnomeVFSResult
5845 do_monitor_add (GnomeVFSMethod *method,
5846                 GnomeVFSMethodHandle **method_handle_return,
5847                 GnomeVFSURI *uri,
5848                 GnomeVFSMonitorType monitor_type)
5849 {
5850         VFolderInfo *info;
5851         VFolderURI vuri;
5852         GnomeVFSResult result;
5853         Folder *folder;
5854         Entry *entry;
5855         GnomeVFSURI *file_uri;
5856         FileMonitorHandle *handle;
5857         gboolean is_directory_file;
5858
5859         VFOLDER_URI_PARSE (uri, &vuri);
5860
5861         info = get_vfolder_info (vuri.scheme, &result, NULL);
5862         if (info == NULL)
5863                 return result;
5864
5865         if (monitor_type == GNOME_VFS_MONITOR_DIRECTORY) {
5866                 G_LOCK (vfolder_lock);
5867
5868                 folder = resolve_folder (info, 
5869                                          vuri.path,
5870                                          FALSE /* ignore_basename */,
5871                                          &result,
5872                                          NULL);
5873
5874                 handle = g_new0 (FileMonitorHandle, 1);
5875                 handle->refcount = 2;
5876                 handle->uri = gnome_vfs_uri_dup (uri);
5877                 handle->dir_monitor = TRUE;
5878                 handle->handle = NULL;
5879                 handle->filename = NULL;
5880
5881                 if (folder == NULL) {
5882                         handle->exists = FALSE;
5883                         info->free_folder_monitors = 
5884                                 g_slist_prepend (info->free_folder_monitors,
5885                                                  handle);
5886                 } else {
5887                         handle->exists = TRUE;
5888                         ((Entry *)folder)->monitors = 
5889                                 g_slist_prepend (((Entry *)folder)->monitors,
5890                                                  handle);
5891                 }
5892
5893                 info->folder_monitors = 
5894                         g_slist_prepend (info->folder_monitors, handle);
5895
5896                 G_UNLOCK (vfolder_lock);
5897
5898                 *method_handle_return = (GnomeVFSMethodHandle *) handle;
5899
5900                 return GNOME_VFS_OK;
5901         } else {
5902                 /* These can't be very nice FILE names */
5903                 if (vuri.file == NULL ||
5904                     vuri.ends_in_slash)
5905                         return GNOME_VFS_ERROR_INVALID_URI;
5906
5907                 G_LOCK (vfolder_lock);
5908                 file_uri = desktop_uri_to_file_uri (info,
5909                                                     &vuri,
5910                                                     &entry,
5911                                                     &is_directory_file,
5912                                                     NULL /* the_folder */,
5913                                                     FALSE,
5914                                                     &result,
5915                                                     NULL);
5916
5917                 handle = g_new0 (FileMonitorHandle, 1);
5918                 handle->refcount = 2;
5919                 handle->uri = gnome_vfs_uri_dup (uri);
5920                 handle->dir_monitor = FALSE;
5921                 handle->handle = NULL;
5922                 handle->filename = g_strdup (vuri.file);
5923                 handle->is_directory_file = is_directory_file;
5924
5925                 info->file_monitors = 
5926                         g_slist_prepend (info->file_monitors, handle);
5927
5928
5929                 if (file_uri == NULL) {
5930                         handle->exists = FALSE;
5931                         info->free_file_monitors = 
5932                                 g_slist_prepend (info->free_file_monitors,
5933                                                  handle);
5934                 } else {
5935                         char *uri_string = gnome_vfs_uri_to_string (file_uri, 0);
5936                         handle->exists = TRUE;
5937                         gnome_vfs_monitor_add (&(handle->handle),
5938                                                uri_string,
5939                                                GNOME_VFS_MONITOR_FILE,
5940                                                file_monitor,
5941                                                handle);
5942                         g_free (uri_string);
5943                         
5944                         entry->monitors = g_slist_prepend (entry->monitors,
5945                                                            handle);
5946                         gnome_vfs_uri_unref (file_uri);
5947                 }
5948
5949                 *method_handle_return = (GnomeVFSMethodHandle *) handle;
5950
5951                 G_UNLOCK (vfolder_lock);
5952
5953                 return GNOME_VFS_OK;
5954         }
5955 }
5956
5957 static GnomeVFSResult
5958 do_monitor_cancel (GnomeVFSMethod *method,
5959                    GnomeVFSMethodHandle *method_handle)
5960 {
5961         FileMonitorHandle *handle;
5962         VFolderInfo *info;
5963         VFolderURI vuri;
5964         GnomeVFSResult result;
5965         Folder *folder;
5966         GSList *li;
5967
5968         handle = (FileMonitorHandle *)method_handle;
5969
5970         /* FIXME: is this correct? */
5971         if (method_handle == NULL)
5972                 return GNOME_VFS_OK;
5973
5974         VFOLDER_URI_PARSE (handle->uri, &vuri);
5975
5976         info = get_vfolder_info (vuri.scheme, &result, NULL);
5977         if (info == NULL)
5978                 return result;
5979
5980         if (handle->dir_monitor) {
5981                 G_LOCK (vfolder_lock);
5982
5983                 folder = resolve_folder (info, 
5984                                          vuri.path,
5985                                          FALSE /* ignore_basename */,
5986                                          &result,
5987                                          NULL);
5988
5989                 for (li = info->folder_monitors; li != NULL; li = li->next) {
5990                         FileMonitorHandle *h = li->data;
5991                         if (h != handle)
5992                                 continue;
5993                         info->folder_monitors = g_slist_delete_link
5994                                 (info->folder_monitors, li);
5995                         file_monitor_handle_unref_unlocked (h);
5996                         break;
5997                 }
5998
5999                 if (folder == NULL) {
6000                         for (li = info->free_folder_monitors;
6001                              li != NULL;
6002                              li = li->next) {
6003                                 FileMonitorHandle *h = li->data;
6004                                 if (h != handle)
6005                                         continue;
6006                                 info->free_folder_monitors = g_slist_delete_link
6007                                         (info->free_folder_monitors, li);
6008                                 file_monitor_handle_unref_unlocked (h);
6009                                 break;
6010                         }
6011                 } else {
6012                         for (li = ((Entry *)folder)->monitors;
6013                              li != NULL;
6014                              li = li->next) {
6015                                 FileMonitorHandle *h = li->data;
6016                                 if (h != handle)
6017                                         continue;
6018                                 ((Entry *)folder)->monitors =
6019                                         g_slist_delete_link
6020                                         (((Entry *)folder)->monitors, li);
6021                                 file_monitor_handle_unref_unlocked (h);
6022                                 break;
6023                         }
6024                 }
6025
6026                 G_UNLOCK (vfolder_lock);
6027
6028                 return GNOME_VFS_OK;
6029         } else {
6030                 G_LOCK (vfolder_lock);
6031
6032                 for (li = info->file_monitors; li != NULL; li = li->next) {
6033                         FileMonitorHandle *h = li->data;
6034                         if (h != handle)
6035                                 continue;
6036                         info->file_monitors = g_slist_delete_link
6037                                 (info->file_monitors, li);
6038                         file_monitor_handle_unref_unlocked (h);
6039                         break;
6040                 }
6041
6042                 for (li = info->free_file_monitors;
6043                      li != NULL;
6044                      li = li->next) {
6045                         FileMonitorHandle *h = li->data;
6046                         if (h != handle)
6047                                 continue;
6048                         info->free_file_monitors = g_slist_delete_link
6049                                 (info->free_file_monitors, li);
6050                         file_monitor_handle_unref_unlocked (h);
6051                         break;
6052                 }
6053
6054                 for (li = info->entries; li != NULL; li = li->next) {
6055                         Entry *e = li->data;
6056                         GSList *link = g_slist_find (e->monitors, handle);
6057
6058                         if (link == NULL)
6059                                 continue;
6060                         link->data = NULL;
6061                         e->monitors = g_slist_delete_link (e->monitors, link);
6062
6063                         file_monitor_handle_unref_unlocked (handle);
6064                         break;
6065                 }
6066
6067                 G_UNLOCK (vfolder_lock);
6068
6069                 /* Note: last unref of our monitor will cancel the
6070                  * underlying handle */
6071
6072                 return GNOME_VFS_OK;
6073         }
6074 }
6075
6076 \f
6077 /* gnome-vfs bureaucracy */
6078
6079 static GnomeVFSMethod method = {
6080         sizeof (GnomeVFSMethod),
6081         do_open,
6082         do_create,
6083         do_close,
6084         do_read,
6085         do_write,
6086         do_seek,
6087         do_tell,
6088         do_truncate_handle,
6089         do_open_directory,
6090         do_close_directory,
6091         do_read_directory,
6092         do_get_file_info,
6093         do_get_file_info_from_handle,
6094         do_is_local,
6095         do_make_directory,
6096         do_remove_directory,
6097         do_move,
6098         do_unlink,
6099         do_check_same_fs,
6100         do_set_file_info,
6101         do_truncate,
6102         NULL /* find_directory */,
6103         NULL /* create_symbolic_link */,
6104         do_monitor_add,
6105         do_monitor_cancel
6106 };
6107
6108 GnomeVFSMethod *
6109 vfs_module_init (const char *method_name, 
6110                  const char *args)
6111 {
6112         parent_method = gnome_vfs_method_get ("file");
6113
6114         if (parent_method == NULL) {
6115                 g_error ("Could not find 'file' method for gnome-vfs");
6116                 return NULL;
6117         }
6118
6119         return &method;
6120 }
6121
6122 void
6123 vfs_module_shutdown (GnomeVFSMethod *method)
6124 {
6125         if (infos == NULL)
6126                 return;
6127
6128         g_hash_table_destroy (infos);
6129         infos = NULL;
6130 }