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