1 #include <config.h>
2 #include <glib/gi18n-lib.h>
3 
4 #include "favorite-vfs-file.h"
5 #include "favorite-vfs-file-enumerator.h"
6 #include "favorite-vfs-file-monitor.h"
7 
8 #define DEBUG_FLAG XAPP_DEBUG_FAVORITE_VFS
9 #include "xapp-debug.h"
10 
11 #define FAVORITES_SCHEMA "org.x.apps.favorites"
12 #define FAVORITE_DCONF_METADATA_KEY "root-metadata"
13 
14 static GSettings *settings = NULL;
15 
16 typedef struct
17 {
18     gchar *uri; // favorites://foo
19 
20     XAppFavoriteInfo *info;
21 } FavoriteVfsFilePrivate;
22 
23 struct _FavoriteVfsFile
24 {
25     GObject parent_instance;
26     FavoriteVfsFilePrivate *priv;
27 };
28 
29 GList       *_xapp_favorites_get_display_names      (XAppFavorites *favorites);
30 static void  favorite_vfs_file_gfile_iface_init (GFileIface *iface);
31 
32 gchar *
path_to_fav_uri(const gchar * path)33 path_to_fav_uri (const gchar *path)
34 {
35     g_return_val_if_fail (path != NULL, NULL);
36 
37     return g_strconcat (ROOT_URI, path, NULL);
38 }
39 
40 gchar *
fav_uri_to_display_name(const gchar * uri)41 fav_uri_to_display_name (const gchar *uri)
42 {
43     g_return_val_if_fail (uri != NULL, NULL);
44     g_return_val_if_fail (g_str_has_prefix (uri, ROOT_URI), NULL);
45 
46     const gchar *ptr;
47 
48     ptr = uri + strlen (ROOT_URI);
49 
50     if (ptr[0] == '/')
51     {
52         ptr++;
53     }
54 
55     return g_strdup (ptr);
56 }
57 
58 G_DEFINE_TYPE_EXTENDED (FavoriteVfsFile,
59                         favorite_vfs_file,
60                         G_TYPE_OBJECT,
61                         0,
62                         G_ADD_PRIVATE (FavoriteVfsFile)
63                         G_IMPLEMENT_INTERFACE (G_TYPE_FILE, favorite_vfs_file_gfile_iface_init))
64 
65 static gboolean
is_root_file(FavoriteVfsFile * file)66 is_root_file (FavoriteVfsFile *file)
67 {
68     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (file);
69 
70     return g_strcmp0 (priv->uri, ROOT_URI) == 0;
71 }
72 
73 static GFile *
file_dup(GFile * file)74 file_dup (GFile *file)
75 {
76     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
77 
78     return favorite_vfs_file_new_for_uri (priv->uri);
79 }
80 
81 static guint
file_hash(GFile * file)82 file_hash (GFile *file)
83 {
84     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
85 
86     return g_str_hash (priv->uri);
87 }
88 
89 static gboolean
file_equal(GFile * file1,GFile * file2)90 file_equal (GFile *file1,
91             GFile *file2)
92 {
93     FavoriteVfsFilePrivate *priv1 = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file1));
94     FavoriteVfsFilePrivate *priv2 = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file2));
95 
96     return g_strcmp0 (priv1->uri, priv2->uri) == 0;
97 }
98 
99 static gboolean
file_is_native(GFile * file)100 file_is_native (GFile *file)
101 {
102     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
103 
104     if (priv->info != NULL && priv->info->uri != NULL)
105     {
106         GFile *real_file;
107         gboolean is_really_native;
108 
109         real_file = g_file_new_for_uri (priv->info->uri);
110         is_really_native = g_file_is_native (real_file);
111 
112         g_object_unref (real_file);
113         return is_really_native;
114     }
115 
116     return FALSE;
117 }
118 
119 static gboolean
file_has_uri_scheme(GFile * file,const gchar * uri_scheme)120 file_has_uri_scheme (GFile      *file,
121                      const gchar *uri_scheme)
122 {
123     return g_strcmp0 (uri_scheme, URI_SCHEME) == 0;
124 }
125 
126 static gchar *
file_get_uri_scheme(GFile * file)127 file_get_uri_scheme (GFile *file)
128 {
129     return g_strdup (URI_SCHEME);
130 }
131 
132 static gchar *
file_get_basename(GFile * file)133 file_get_basename (GFile *file)
134 {
135     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
136 
137     if (priv->info == NULL)
138     {
139         return g_strdup ("/");
140     }
141 
142     return g_strdup (priv->info->display_name);
143 }
144 
145 static gchar *
file_get_path(GFile * file)146 file_get_path (GFile *file)
147 {
148     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
149 
150     if (file_is_native (file))
151     {
152         GFile *real_file;
153         gchar *ret;
154 
155         real_file = g_file_new_for_uri (priv->info->uri);
156 
157         // file can't be native without an info, so we don't need to check for null here
158         ret = g_file_get_path (real_file);
159         g_object_unref (real_file);
160 
161         return ret;
162     }
163 
164     // GtkFileChooser checks for a path (and not null) before allowing a shortcut to
165     // be added using gtk_file_chooser_add_shortcut_folder_uri(). Even though this / doesn't
166     // make much sense for favorites:/// it allows it to be added to the file chooser without
167     // being forced to override its 'local-only' property.
168     if (is_root_file (FAVORITE_VFS_FILE (file)))
169     {
170         return g_strdup ("/");
171     }
172 
173     return NULL;
174 }
175 
176 static gchar *
file_get_uri(GFile * file)177 file_get_uri (GFile *file)
178 {
179     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
180 
181     return g_strdup (priv->uri);
182 }
183 
184 static gchar *
file_get_parse_name(GFile * file)185 file_get_parse_name (GFile *file)
186 {
187     return file_get_uri (file);
188 }
189 
190 static GFile *
file_get_parent(GFile * file)191 file_get_parent (GFile *file)
192 {
193     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
194 
195     // We're only ever one level deep.
196     if (priv->info != NULL)
197     {
198         return g_file_new_for_uri (ROOT_URI);
199     }
200 
201     return NULL;
202 }
203 
204 static gboolean
file_prefix_matches(GFile * parent,GFile * descendant)205 file_prefix_matches (GFile *parent,
206                      GFile *descendant)
207 {
208     g_autofree gchar *puri = NULL;
209     g_autofree gchar *duri = NULL;
210     gchar *ptr = NULL;
211 
212     puri = g_file_get_uri (parent);
213     duri = g_file_get_uri (descendant);
214 
215     ptr = g_strstr_len (puri, -1, duri);
216 
217     if ((ptr == puri) && ptr[strlen (duri) + 1] == '/')
218     {
219         return TRUE;
220     }
221 
222     return FALSE;
223 }
224 
225 static gchar *
file_get_relative_path(GFile * parent,GFile * descendant)226 file_get_relative_path (GFile *parent,
227                         GFile *descendant)
228 {
229     g_autofree gchar *puri = NULL;
230     g_autofree gchar *duri = NULL;
231     g_autofree gchar *rpath = NULL;
232     gchar *ptr = NULL;
233 
234     puri = g_file_get_uri (parent);
235     duri = g_file_get_uri (descendant);
236 
237     ptr = g_strstr_len (puri, -1, duri);
238 
239     if ((ptr == puri) && ptr[strlen (duri) + 1] == '/')
240     {
241         rpath = g_strdup (puri + strlen (duri) + 1);
242 
243         return rpath;
244     }
245 
246     return NULL;
247 }
248 
249 static GFile *
file_resolve_relative_path(GFile * file,const gchar * relative_path)250 file_resolve_relative_path (GFile       *file,
251                             const gchar *relative_path)
252 {
253     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
254     GFile *relative_file;
255     gchar *uri;
256 
257     if (g_path_is_absolute (relative_path))
258     {
259         return g_file_new_for_path (relative_path);
260     }
261 
262     if (priv->info != NULL && priv->info->uri != NULL)
263     {
264         GFile *real_file;
265         real_file = g_file_new_for_uri (priv->info->uri);
266 
267         relative_file = g_file_resolve_relative_path (real_file,
268                                                       relative_path);
269 
270         g_object_unref (real_file);
271         return relative_file;
272     }
273 
274     if (g_strcmp0 (relative_path, ".") == 0)
275     {
276         relative_file = file_dup (file);
277 
278         return relative_file;
279     }
280 
281     uri = path_to_fav_uri (relative_path);
282 
283     relative_file = g_file_new_for_uri (uri);
284     g_free (uri);
285 
286     return relative_file;
287 }
288 
289 static GFile *
file_get_child_for_display_name(GFile * file,const char * display_name,GError ** error)290 file_get_child_for_display_name (GFile       *file,
291                                  const char  *display_name,
292                                  GError     **error)
293 {
294     return g_file_get_child (file, display_name);
295 }
296 
297 static GFile *
file_set_display_name(GFile * file,const gchar * display_name,GCancellable * cancellable,GError ** error)298 file_set_display_name (GFile         *file,
299                        const gchar   *display_name,
300                        GCancellable  *cancellable,
301                        GError       **error)
302 {
303     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
304 
305     if (priv->info != NULL && priv->info->uri != NULL)
306     {
307         GFile *real_file;
308         GFile *ret;
309 
310         real_file = g_file_new_for_uri (priv->info->uri);
311         ret = g_file_set_display_name (real_file,
312                                        display_name,
313                                        cancellable,
314                                        error);
315         g_object_unref (real_file);
316 
317         return ret;
318     }
319 
320     g_set_error_literal (error, G_IO_ERROR,
321                          G_IO_ERROR_NOT_SUPPORTED,
322                          "Can't rename file");
323 
324     return NULL;
325 }
326 
327 static GFileEnumerator *
file_enumerate_children(GFile * file,const char * attributes,GFileQueryInfoFlags flags,GCancellable * cancellable,GError ** error)328 file_enumerate_children(GFile               *file,
329                         const char          *attributes,
330                         GFileQueryInfoFlags  flags,
331                         GCancellable        *cancellable,
332                         GError             **error)
333 {
334     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
335     GFileEnumerator *enumerator;
336 
337     if (priv->info != NULL && priv->info->uri != NULL)
338     {
339         GFile *real_file;
340         real_file = g_file_new_for_uri (priv->info->uri);
341 
342         enumerator = g_file_enumerate_children (real_file,
343                                                 attributes,
344                                                 flags,
345                                                 cancellable,
346                                                 error);
347 
348         g_object_unref (real_file);
349         return enumerator;
350     }
351 
352     GList *uris;
353 
354     uris = _xapp_favorites_get_display_names (xapp_favorites_get_default ());
355     enumerator = favorite_vfs_file_enumerator_new (file,
356                                                    attributes,
357                                                    flags,
358                                                    uris);
359 
360     g_list_free (uris);
361 
362     return enumerator;
363 }
364 
365 static GFileInfo *
file_query_info(GFile * file,const char * attributes,GFileQueryInfoFlags flags,GCancellable * cancellable,GError ** error)366 file_query_info (GFile               *file,
367                  const char          *attributes,
368                  GFileQueryInfoFlags  flags,
369                  GCancellable        *cancellable,
370                  GError             **error)
371 {
372     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
373     GFileInfo *info;
374     GIcon *icon;
375 
376     info = NULL;
377 
378     if (priv->info != NULL)
379     {
380         if (!priv->info->uri)
381         {
382             g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "File not found");
383             return NULL;
384         }
385 
386         GFile *real_file = g_file_new_for_uri (priv->info->uri);
387 
388         info = g_file_query_info (real_file, attributes, flags, cancellable, error);
389 
390         if (info != NULL)
391         {
392             gchar *local_path;
393 
394             g_file_info_set_display_name (info, priv->info->display_name);
395             g_file_info_set_name (info, priv->info->display_name);
396             g_file_info_set_is_symlink (info, TRUE);
397 
398             local_path = g_file_get_path (real_file);
399 
400             if (local_path != NULL)
401             {
402                 g_file_info_set_symlink_target (info, local_path);
403                 g_free (local_path);
404             }
405             else
406             {
407                 g_file_info_set_symlink_target (info, priv->info->uri);
408             }
409 
410             // Recent sets this also. If it's set, this uri is used to display the "location"
411             // for the file (the directory in which real file resides).
412             g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, priv->info->uri);
413 
414             g_file_info_set_attribute_string (info, FAVORITE_AVAILABLE_METADATA_KEY, META_TRUE);
415         }
416         else
417         {
418             // This file is still in our favorites list but doesn't exist (currently).
419             g_clear_error (error);
420             gchar *content_type;
421 
422             info = g_file_info_new ();
423 
424             g_file_info_set_display_name (info, priv->info->display_name);
425             g_file_info_set_name (info, priv->info->display_name);
426             g_file_info_set_file_type (info, G_FILE_TYPE_SYMBOLIC_LINK);
427             g_file_info_set_is_symlink (info, TRUE);
428             g_file_info_set_symlink_target (info, priv->info->uri);
429             g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, priv->info->uri);
430 
431             /* Prevent showing a 'thumbnailing' icon */
432             g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED, TRUE);
433 
434             /* This will keep the sort position the same for missing or unmounted files */
435             g_file_info_set_attribute_string (info, FAVORITE_METADATA_KEY, META_TRUE);
436             g_file_info_set_attribute_string (info, FAVORITE_AVAILABLE_METADATA_KEY, META_FALSE);
437 
438             content_type = g_content_type_from_mime_type (priv->info->cached_mimetype);
439 
440             icon = g_content_type_get_icon (content_type);
441             g_file_info_set_icon (info, icon);
442             g_object_unref (icon);
443 
444             icon = g_content_type_get_symbolic_icon (content_type);
445             g_file_info_set_symbolic_icon (info, icon);
446             g_object_unref (icon);
447 
448             g_free (content_type);
449         }
450 
451         g_object_unref (real_file);
452 
453         return info;
454     }
455 
456     if (is_root_file (FAVORITE_VFS_FILE (file)))
457     {
458         GFileAttributeMatcher *matcher = g_file_attribute_matcher_new (attributes);
459 
460         info = g_file_info_new ();
461 
462         if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_STANDARD_NAME))
463             g_file_info_set_name (info, "/");
464 
465         if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME))
466             g_file_info_set_display_name (info, _("Favorites"));
467 
468         if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_STANDARD_TYPE))
469             g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
470 
471         if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_STANDARD_ICON))
472         {
473             icon = g_themed_icon_new ("xapp-user-favorites");
474             g_file_info_set_icon (info, icon);
475 
476             g_object_unref (icon);
477         }
478 
479         if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_STANDARD_SYMBOLIC_ICON))
480         {
481             icon = g_themed_icon_new ("xapp-user-favorites-symbolic");
482             g_file_info_set_symbolic_icon (info, icon);
483             g_object_unref (icon);
484         }
485 
486         if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_GVFS_BACKEND))
487             g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_GVFS_BACKEND, "favorites");
488 
489         if (g_file_attribute_matcher_matches (matcher, FAVORITE_AVAILABLE_METADATA_KEY))
490             g_file_info_set_attribute_string (info, FAVORITE_AVAILABLE_METADATA_KEY, META_TRUE);
491 
492         if (g_file_attribute_matcher_enumerate_namespace (matcher, "metadata"))
493         {
494             gchar **entries = g_settings_get_strv (settings, FAVORITE_DCONF_METADATA_KEY);
495 
496             if (entries != NULL)
497             {
498                 gint i;
499 
500                 for (i = 0; entries[i] != NULL; i++)
501                 {
502                     gchar **t_n_v;
503 
504                     t_n_v = g_strsplit (entries[i], "==", 3);
505 
506                     if (g_strv_length (t_n_v) == 3)
507                     {
508                         if (g_strcmp0 (t_n_v[0], "string") == 0)
509                         {
510                             g_file_info_set_attribute_string (info, t_n_v[1], t_n_v[2]);
511                         }
512                         else
513                         if (g_strcmp0 (t_n_v[0], "strv") == 0)
514                         {
515                             gchar **members = g_strsplit (t_n_v[2], "|", -1);
516 
517                             g_file_info_set_attribute_stringv (info, t_n_v[1], members);
518 
519                             g_strfreev (members);
520                         }
521                     }
522 
523                     g_strfreev (t_n_v);
524                 }
525             }
526 
527             g_strfreev (entries);
528         }
529 
530         g_file_attribute_matcher_unref (matcher);
531     }
532     else
533     {
534         g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Can't retrieve info for favorite file");
535     }
536 
537     return info;
538 }
539 
540 GFileInfo *
file_query_filesystem_info(GFile * file,const char * attributes,GCancellable * cancellable,GError ** error)541 file_query_filesystem_info (GFile         *file,
542                             const char    *attributes,
543                             GCancellable  *cancellable,
544                             GError       **error)
545 {
546     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
547     GFileInfo *info;
548     GFileAttributeMatcher *matcher;
549 
550     matcher = g_file_attribute_matcher_new (attributes);
551 
552     if (priv->info != NULL && priv->info->uri != NULL)
553     {
554         GFileInfo *info;
555         GFile *real_file;
556         real_file = g_file_new_for_uri (priv->info->uri);
557 
558         info = g_file_query_filesystem_info (real_file,
559                                              attributes,
560                                              cancellable,
561                                              error);
562 
563         if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY))
564         {
565             g_file_info_set_attribute_boolean (info,
566                                                G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, TRUE);
567         }
568 
569         g_object_unref (real_file);
570         return info;
571     }
572 
573     info = g_file_info_new ();
574 
575     if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE))
576     {
577         g_file_info_set_attribute_string (info,
578                                           G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "favorites");
579     }
580 
581     if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY))
582     {
583         g_file_info_set_attribute_boolean (info,
584                                            G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, TRUE);
585     }
586 
587     if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW))
588     {
589         g_file_info_set_attribute_uint32 (info,
590                                           G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW,
591                                           G_FILESYSTEM_PREVIEW_TYPE_IF_LOCAL);
592     }
593 
594     g_file_attribute_matcher_unref (matcher);
595 
596     return info;
597 }
598 
599 GMount *
file_find_enclosing_mount(GFile * file,GCancellable * cancellable,GError ** error)600 file_find_enclosing_mount (GFile         *file,
601                            GCancellable  *cancellable,
602                            GError       **error)
603 {
604     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
605 
606     if (priv->info != NULL && priv->info->uri != NULL)
607     {
608         GMount *mount;
609         GFile *real_file;
610         real_file = g_file_new_for_uri (priv->info->uri);
611 
612         mount = g_file_find_enclosing_mount (real_file,
613                                              cancellable,
614                                              error);
615 
616         g_object_unref (real_file);
617         return mount;
618     }
619 
620     g_set_error_literal (error, G_IO_ERROR,
621                          G_IO_ERROR_NOT_SUPPORTED,
622                          "Can't find favorite file enclosing mount");
623 
624     return NULL;
625 }
626 
627 GFileAttributeInfoList *
file_query_settable_attributes(GFile * file,GCancellable * cancellable,GError ** error)628 file_query_settable_attributes (GFile         *file,
629                                 GCancellable  *cancellable,
630                                 GError       **error)
631 {
632     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
633 
634     if (priv->info != NULL && priv->info->uri != NULL)
635     {
636         GFileAttributeInfoList *list;
637         GFile *real_file;
638         real_file = g_file_new_for_uri (priv->info->uri);
639 
640         list = g_file_query_settable_attributes (real_file,
641                                                  cancellable,
642                                                  error);
643 
644         g_object_unref (real_file);
645         return list;
646     }
647 
648     return g_file_attribute_info_list_new ();
649 }
650 
651 GFileAttributeInfoList *
file_query_writable_namespaces(GFile * file,GCancellable * cancellable,GError ** error)652 file_query_writable_namespaces (GFile         *file,
653                                 GCancellable  *cancellable,
654                                 GError       **error)
655 {
656     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
657     GFileAttributeInfoList *list;
658 
659     if (priv->info != NULL && priv->info->uri != NULL)
660     {
661         GFile *real_file;
662         real_file = g_file_new_for_uri (priv->info->uri);
663 
664         list = g_file_query_writable_namespaces (real_file,
665                                                  cancellable,
666                                                  error);
667 
668         g_object_unref (real_file);
669         return list;
670     }
671 
672     list = g_file_attribute_info_list_new ();
673 
674     g_file_attribute_info_list_add (list,
675                                     "metadata",
676                                     G_FILE_ATTRIBUTE_TYPE_STRING,
677                                     G_FILE_ATTRIBUTE_INFO_NONE);
678 
679     return list;
680 }
681 
682 static void
remove_root_metadata(const gchar * attr_name)683 remove_root_metadata (const gchar *attr_name)
684 {
685     GPtrArray *new_array;
686     gchar **old_metadata, **new_metadata;
687     gint i;
688 
689     old_metadata = g_settings_get_strv (settings, FAVORITE_DCONF_METADATA_KEY);
690 
691     if (old_metadata == NULL)
692     {
693         return;
694     }
695 
696     new_array = g_ptr_array_new ();
697 
698     for (i = 0; old_metadata[i] != NULL; i++)
699     {
700         gchar **t_n_v;
701 
702         t_n_v = g_strsplit (old_metadata[i], "==", 3);
703 
704         if (g_strcmp0 (t_n_v[1], attr_name) != 0)
705         {
706             g_ptr_array_add (new_array, g_strdup (old_metadata[i]));
707         }
708 
709         g_strfreev (t_n_v);
710     }
711 
712     g_ptr_array_add (new_array, NULL);
713     g_strfreev (old_metadata);
714 
715     new_metadata = (gchar **) g_ptr_array_free (new_array, FALSE);
716 
717     g_settings_set_strv (settings, FAVORITE_DCONF_METADATA_KEY, (const gchar * const *) new_metadata);
718     g_strfreev (new_metadata);
719 }
720 
721 static void
set_or_update_root_metadata(const gchar * attr_name,const gpointer value,GFileAttributeType type)722 set_or_update_root_metadata (const gchar        *attr_name,
723                              const gpointer      value,
724                              GFileAttributeType  type)
725 {
726     GPtrArray *new_array;
727     gchar **old_metadata, **new_metadata;
728     gint i;
729     gchar *entry;
730     gboolean exists;
731 
732     old_metadata = g_settings_get_strv (settings, FAVORITE_DCONF_METADATA_KEY);
733 
734     if (old_metadata == NULL)
735     {
736         return;
737     }
738 
739     switch (type)
740     {
741         case G_FILE_ATTRIBUTE_TYPE_STRING:
742         {
743             entry = g_strdup_printf ("string==%s==%s", attr_name, (gchar *) value);
744             break;
745         }
746         case G_FILE_ATTRIBUTE_TYPE_STRINGV:
747         {
748             gchar *val_strv = g_strjoinv ("|", (gchar **) value);
749             entry = g_strdup_printf ("strv==%s==%s", attr_name, val_strv);
750             g_free (val_strv);
751             break;
752         }
753         default:
754             g_warn_if_reached ();
755             g_strfreev (old_metadata);
756             return;
757     }
758 
759     exists = FALSE;
760     new_array = g_ptr_array_new ();
761 
762     for (i = 0; old_metadata[i] != NULL; i++)
763     {
764         gchar **t_n_v;
765 
766         t_n_v = g_strsplit (old_metadata[i], "==", 3);
767 
768         if (g_strcmp0 (t_n_v[1], attr_name) == 0)
769         {
770             g_ptr_array_add (new_array, entry);
771             exists = TRUE;
772         }
773         else
774         {
775             g_ptr_array_add (new_array, g_strdup (old_metadata[i]));
776         }
777 
778         g_strfreev (t_n_v);
779     }
780 
781     if (!exists)
782     {
783         g_ptr_array_add (new_array, entry);
784     }
785 
786     g_ptr_array_add (new_array, NULL);
787     g_strfreev (old_metadata);
788 
789     new_metadata = (gchar **) g_ptr_array_free (new_array, FALSE);
790 
791     g_settings_set_strv (settings, FAVORITE_DCONF_METADATA_KEY, (const gchar * const *) new_metadata);
792     g_strfreev (new_metadata);
793 }
794 
795 gboolean
file_set_attribute(GFile * file,const gchar * attribute,GFileAttributeType type,gpointer value_p,GFileQueryInfoFlags flags,GCancellable * cancellable,GError ** error)796 file_set_attribute (GFile                *file,
797                     const gchar          *attribute,
798                     GFileAttributeType    type,
799                     gpointer              value_p,
800                     GFileQueryInfoFlags   flags,
801                     GCancellable         *cancellable,
802                     GError              **error)
803 {
804     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
805     gboolean ret;
806 
807     if (priv->info != NULL && priv->info->uri != NULL)
808     {
809         GFile *real_file;
810         gboolean ret;
811         real_file = g_file_new_for_uri (priv->info->uri);
812 
813         ret = g_file_set_attribute (real_file,
814                                     attribute,
815                                     type,
816                                     value_p,
817                                     flags,
818                                     cancellable,
819                                     error);
820 
821         g_object_unref (real_file);
822         return ret;
823     }
824 
825     ret = FALSE;
826 
827     if (!is_root_file (FAVORITE_VFS_FILE (file)))
828     {
829         g_set_error (error, G_IO_ERROR,
830                      G_IO_ERROR_NOT_SUPPORTED,
831                      "Can't set attributes for %s - only the root (favorites:///) is supported.", priv->uri);
832     }
833     else
834     {
835         if (g_str_has_prefix (attribute, "metadata"))
836         {
837             if (type == G_FILE_ATTRIBUTE_TYPE_INVALID || value_p == NULL || ((char *) value_p)[0] == '\0')
838             {
839                 // unset metadata
840                 remove_root_metadata (attribute);
841                 ret = TRUE;
842             }
843             else
844             {
845                 if (type == G_FILE_ATTRIBUTE_TYPE_STRING || type == G_FILE_ATTRIBUTE_TYPE_STRINGV)
846                 {
847                     set_or_update_root_metadata (attribute, (gchar *) value_p, type);
848                     ret = TRUE;
849                 }
850                 else
851                 {
852                     g_set_error (error, G_IO_ERROR,
853                                  G_IO_ERROR_NOT_SUPPORTED,
854                                  "Can't set attribute '%s' for favorites:/// file "
855                                  "(only string-type metadata are allowed).", attribute);
856                 }
857             }
858         }
859         else
860         {
861             g_set_error (error, G_IO_ERROR,
862                          G_IO_ERROR_NOT_SUPPORTED,
863                          "Can't set attribute '%s' for favorites:/// file "
864                          "(only 'metadata' namespace is allowed).", attribute);
865         }
866     }
867 
868     return ret;
869 }
870 
871 static gboolean
file_set_attributes_from_info(GFile * file,GFileInfo * info,GFileQueryInfoFlags flags,GCancellable * cancellable,GError ** error)872 file_set_attributes_from_info (GFile                *file,
873                                GFileInfo            *info,
874                                GFileQueryInfoFlags   flags,
875                                GCancellable         *cancellable,
876                                GError              **error)
877 {
878     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
879     gboolean res;
880 
881     if (priv->info != NULL && priv->info->uri != NULL)
882     {
883         GFile *real_file;
884         gboolean ret = FALSE;
885         real_file = g_file_new_for_uri (priv->info->uri);
886 
887         ret = g_file_set_attributes_from_info (real_file, info, flags, cancellable, error);
888 
889         g_object_unref (real_file);
890         return ret;
891     }
892 
893     res = TRUE;
894 
895     if (g_file_info_has_namespace (info, "metadata"))
896     {
897         GFileAttributeType type;
898         gchar **attributes;
899         gpointer value_p;
900         gint i;
901 
902         attributes = g_file_info_list_attributes (info, "metadata");
903 
904         for (i = 0; attributes[i] != NULL; i++)
905         {
906             if (g_file_info_get_attribute_data (info, attributes[i], &type, &value_p, NULL))
907             {
908                 if (!file_set_attribute (file,
909                                          attributes[i],
910                                          type,
911                                          value_p,
912                                          flags,
913                                          cancellable,
914                                          error))
915                 {
916                     g_file_info_set_attribute_status (info, attributes[i], G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING);
917                     error = NULL; // from gvfs gdaemonvfs.c - ignore subsequent errors iterating thru attribute list.
918                     res = FALSE;
919                 }
920                 else
921                 {
922                     g_file_info_set_attribute_status (info, attributes[i], G_FILE_ATTRIBUTE_STATUS_SET);
923                 }
924             }
925         }
926 
927         g_strfreev (attributes);
928     }
929 
930     return res;
931 }
932 
933 static GFileInputStream *
file_read_fn(GFile * file,GCancellable * cancellable,GError ** error)934 file_read_fn (GFile         *file,
935               GCancellable  *cancellable,
936               GError       **error)
937 {
938     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
939 
940     if (priv->info != NULL && priv->info->uri != NULL)
941     {
942         GFile *real_file = g_file_new_for_uri (priv->info->uri);
943 
944         GFileInputStream *stream;
945 
946         stream = g_file_read (real_file, cancellable, error);
947 
948         g_object_unref (real_file);
949         return stream;
950     }
951 
952     g_set_error_literal (error, G_IO_ERROR,
953                          G_IO_ERROR_NOT_SUPPORTED,
954                         _("Operation not supported"));
955 
956     return NULL;
957 }
958 
959 GFileOutputStream *
file_append_to(GFile * file,GFileCreateFlags flags,GCancellable * cancellable,GError ** error)960 file_append_to (GFile             *file,
961                 GFileCreateFlags   flags,
962                 GCancellable      *cancellable,
963                 GError           **error)
964 {
965     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
966 
967     if (priv->info != NULL && priv->info->uri != NULL)
968     {
969         GFile *real_file = g_file_new_for_uri (priv->info->uri);
970 
971         GFileOutputStream *stream;
972 
973         stream = g_file_append_to (real_file,
974                                    flags,
975                                    cancellable,
976                                    error);
977 
978         g_object_unref (real_file);
979         return stream;
980     }
981 
982     g_set_error_literal (error, G_IO_ERROR,
983                          G_IO_ERROR_NOT_SUPPORTED,
984                         _("Operation not supported"));
985     return NULL;
986 }
987 
988 static GFileOutputStream *
file_replace(GFile * file,const char * etag,gboolean make_backup,GFileCreateFlags flags,GCancellable * cancellable,GError ** error)989 file_replace (GFile             *file,
990               const char        *etag,
991               gboolean           make_backup,
992               GFileCreateFlags   flags,
993               GCancellable      *cancellable,
994               GError           **error)
995 {
996     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
997 
998     if (priv->info != NULL && priv->info->uri != NULL)
999     {
1000         GFile *real_file = g_file_new_for_uri (priv->info->uri);
1001 
1002         GFileOutputStream *stream;
1003 
1004         stream = g_file_replace (real_file,
1005                                  etag,
1006                                  make_backup,
1007                                  flags,
1008                                  cancellable,
1009                                  error);
1010 
1011         g_object_unref (real_file);
1012         return stream;
1013     }
1014 
1015     g_set_error_literal (error, G_IO_ERROR,
1016                          G_IO_ERROR_NOT_SUPPORTED,
1017                         _("Operation not supported"));
1018 
1019     return NULL;
1020 }
1021 
1022 static GFileIOStream *
file_open_readwrite(GFile * file,GCancellable * cancellable,GError ** error)1023 file_open_readwrite (GFile                      *file,
1024                      GCancellable               *cancellable,
1025                      GError                    **error)
1026 {
1027     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
1028 
1029     if (priv->info != NULL && priv->info->uri != NULL)
1030     {
1031         GFileIOStream *res;
1032         GFile *real_file = g_file_new_for_uri (priv->info->uri);
1033 
1034         res = g_file_open_readwrite (real_file,
1035                                      cancellable,
1036                                      error);
1037 
1038         g_object_unref (real_file);
1039         return res;
1040     }
1041 
1042     g_set_error_literal (error, G_IO_ERROR,
1043                          G_IO_ERROR_NOT_SUPPORTED,
1044                         _("Operation not supported"));
1045 
1046     return NULL;
1047 }
1048 
1049 static GFileIOStream *
file_replace_readwrite(GFile * file,const char * etag,gboolean make_backup,GFileCreateFlags flags,GCancellable * cancellable,GError ** error)1050 file_replace_readwrite (GFile                      *file,
1051                         const char                 *etag,
1052                         gboolean                    make_backup,
1053                         GFileCreateFlags            flags,
1054                         GCancellable               *cancellable,
1055                         GError                    **error)
1056 {
1057     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
1058 
1059     if (priv->info != NULL && priv->info->uri != NULL)
1060     {
1061         GFileIOStream *res;
1062         GFile *real_file = g_file_new_for_uri (priv->info->uri);
1063 
1064         res = g_file_replace_readwrite (real_file,
1065                                         etag,
1066                                         make_backup,
1067                                         flags,
1068                                         cancellable,
1069                                         error);
1070 
1071         g_object_unref (real_file);
1072         return res;
1073     }
1074 
1075     g_set_error_literal (error, G_IO_ERROR,
1076                          G_IO_ERROR_NOT_SUPPORTED,
1077                         _("Operation not supported"));
1078 
1079     return NULL;
1080 }
1081 
1082 static gboolean
file_delete(GFile * file,GCancellable * cancellable,GError ** error)1083 file_delete (GFile         *file,
1084              GCancellable  *cancellable,
1085              GError       **error)
1086 {
1087     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
1088 
1089     if (!is_root_file (FAVORITE_VFS_FILE (file)))
1090     {
1091         if (priv->info != NULL && priv->info->uri != NULL)
1092         {
1093             xapp_favorites_remove (xapp_favorites_get_default (), priv->info->uri);
1094         }
1095         else
1096         {
1097             xapp_favorites_remove (xapp_favorites_get_default (), priv->uri);
1098         }
1099 
1100         return TRUE;
1101     }
1102 
1103     g_set_error_literal (error, G_IO_ERROR,
1104                          G_IO_ERROR_NOT_SUPPORTED,
1105                         _("Operation not supported"));
1106 
1107     return FALSE;
1108 }
1109 
1110 static gboolean
file_trash(GFile * file,GCancellable * cancellable,GError ** error)1111 file_trash (GFile         *file,
1112             GCancellable  *cancellable,
1113             GError       **error)
1114 {
1115     return file_delete (file, cancellable, error);
1116 }
1117 
1118 gboolean
file_copy(GFile * source,GFile * destination,GFileCopyFlags flags,GCancellable * cancellable,GFileProgressCallback progress_callback,gpointer progress_callback_data,GError ** error)1119 file_copy (GFile                  *source,
1120            GFile                  *destination,
1121            GFileCopyFlags          flags,
1122            GCancellable           *cancellable,
1123            GFileProgressCallback   progress_callback,
1124            gpointer                progress_callback_data,
1125            GError                **error)
1126 {
1127     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (source));
1128 
1129     if (priv->info != NULL && priv->info->uri != NULL)
1130     {
1131         gboolean res;
1132         GFile *real_file = g_file_new_for_uri (priv->info->uri);
1133 
1134         res = g_file_copy (real_file,
1135                            destination,
1136                            flags,
1137                            cancellable,
1138                            progress_callback,
1139                            progress_callback_data,
1140                            error);
1141 
1142         g_object_unref (real_file);
1143         return res;
1144     }
1145 
1146     g_set_error_literal (error, G_IO_ERROR,
1147                          G_IO_ERROR_NOT_SUPPORTED,
1148                         _("Operation not supported"));
1149 
1150     return FALSE;
1151 }
1152 
1153 static GFileMonitor *
file_monitor_dir(GFile * file,GFileMonitorFlags flags,GCancellable * cancellable,GError ** error)1154 file_monitor_dir (GFile              *file,
1155                   GFileMonitorFlags   flags,
1156                   GCancellable       *cancellable,
1157                   GError            **error)
1158 {
1159     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
1160 
1161     if (priv->info != NULL && priv->info->uri != NULL)
1162     {
1163         GFile *real_file;
1164         GFileMonitor *monitor;
1165 
1166         real_file = g_file_new_for_uri (priv->info->uri);
1167         monitor = g_file_monitor_directory (real_file,
1168                                             flags,
1169                                             cancellable,
1170                                             error);
1171         g_object_unref (real_file);
1172 
1173         return monitor;
1174     }
1175     else
1176     if (is_root_file (FAVORITE_VFS_FILE (file)))
1177     {
1178         return favorite_vfs_file_monitor_new ();
1179     }
1180 
1181     g_set_error_literal (error, G_IO_ERROR,
1182                          G_IO_ERROR_NOT_SUPPORTED,
1183                         _("Operation not supported"));
1184 
1185     return NULL;
1186 }
1187 
1188 static GFileMonitor *
file_monitor_file(GFile * file,GFileMonitorFlags flags,GCancellable * cancellable,GError ** error)1189 file_monitor_file (GFile              *file,
1190                    GFileMonitorFlags   flags,
1191                    GCancellable       *cancellable,
1192                    GError            **error)
1193 {
1194     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
1195 
1196     if (priv->info != NULL && priv->info->uri != NULL)
1197     {
1198         GFile *real_file;
1199         GFileMonitor *monitor;
1200 
1201         real_file = g_file_new_for_uri (priv->info->uri);
1202         monitor = g_file_monitor_file (real_file,
1203                                        flags,
1204                                        cancellable,
1205                                        error);
1206         g_object_unref (real_file);
1207 
1208         return monitor;
1209     }
1210 
1211     g_set_error_literal (error, G_IO_ERROR,
1212                          G_IO_ERROR_NOT_SUPPORTED,
1213                         _("Operation not supported"));
1214 
1215     return NULL;
1216 }
1217 
1218 gboolean
file_measure_disk_usage(GFile * file,GFileMeasureFlags flags,GCancellable * cancellable,GFileMeasureProgressCallback progress_callback,gpointer progress_data,guint64 * disk_usage,guint64 * num_dirs,guint64 * num_files,GError ** error)1219 file_measure_disk_usage (GFile                         *file,
1220                          GFileMeasureFlags              flags,
1221                          GCancellable                  *cancellable,
1222                          GFileMeasureProgressCallback   progress_callback,
1223                          gpointer                       progress_data,
1224                          guint64                       *disk_usage,
1225                          guint64                       *num_dirs,
1226                          guint64                       *num_files,
1227                          GError                       **error)
1228 {
1229     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
1230 
1231     if (priv->info != NULL && priv->info->uri != NULL)
1232     {
1233         gboolean res;
1234         GFile *real_file = g_file_new_for_uri (priv->info->uri);
1235 
1236         res = g_file_measure_disk_usage (real_file,
1237                                          flags,
1238                                          cancellable,
1239                                          progress_callback,
1240                                          progress_data,
1241                                          disk_usage,
1242                                          num_dirs,
1243                                          num_files,
1244                                          error);
1245 
1246         g_object_unref (real_file);
1247         return res;
1248     }
1249 
1250     g_set_error_literal (error, G_IO_ERROR,
1251                          G_IO_ERROR_NOT_SUPPORTED,
1252                         _("Operation not supported"));
1253 
1254     return FALSE;
1255 }
1256 
favorite_vfs_file_gfile_iface_init(GFileIface * iface)1257 static void favorite_vfs_file_gfile_iface_init (GFileIface *iface)
1258 {
1259     iface->dup = file_dup;
1260     iface->hash = file_hash;
1261     iface->equal = file_equal;
1262     iface->is_native = file_is_native;
1263     iface->has_uri_scheme = file_has_uri_scheme;
1264     iface->get_uri_scheme = file_get_uri_scheme;
1265     iface->get_basename = file_get_basename;
1266     iface->get_path = file_get_path;
1267     iface->get_uri = file_get_uri;
1268     iface->get_parse_name = file_get_parse_name;
1269     iface->get_parent = file_get_parent;
1270     iface->prefix_matches = file_prefix_matches;
1271     iface->get_relative_path = file_get_relative_path;
1272     iface->resolve_relative_path = file_resolve_relative_path;
1273     iface->get_child_for_display_name = file_get_child_for_display_name;
1274     iface->set_display_name = file_set_display_name;
1275     iface->enumerate_children = file_enumerate_children;
1276     iface->query_info = file_query_info;
1277     iface->query_filesystem_info = file_query_filesystem_info;
1278     iface->find_enclosing_mount = file_find_enclosing_mount;
1279     iface->query_settable_attributes = file_query_settable_attributes;
1280     iface->query_writable_namespaces = file_query_writable_namespaces;
1281     iface->set_attribute = file_set_attribute;
1282     iface->set_attributes_from_info = file_set_attributes_from_info;
1283     iface->read_fn = file_read_fn;
1284     iface->append_to = file_append_to;
1285     // iface->create = file_create; ### Don't support
1286     iface->replace = file_replace;
1287     iface->open_readwrite = file_open_readwrite;
1288     // iface->create_readwrite = file_create_readwrite; ### Don't support
1289     iface->replace_readwrite = file_replace_readwrite;
1290     iface->delete_file = file_delete;
1291     iface->trash = file_trash;
1292     // iface->make_directory = file_make_directory; ### Don't support
1293     // iface->make_symbolic_link = file_make_symbolic_link; ### Don't support
1294     iface->copy = file_copy;
1295     iface->monitor_dir = file_monitor_dir;
1296     iface->monitor_file = file_monitor_file;
1297     iface->measure_disk_usage = file_measure_disk_usage;
1298     iface->supports_thread_contexts = TRUE;
1299 }
1300 
favorite_vfs_file_dispose(GObject * object)1301 static void favorite_vfs_file_dispose (GObject *object)
1302 {
1303     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (object));
1304 
1305     if (priv->info != NULL)
1306     {
1307         xapp_favorite_info_free (priv->info);
1308         priv->info = NULL;
1309     }
1310 
1311     G_OBJECT_CLASS (favorite_vfs_file_parent_class)->dispose (object);
1312 }
1313 
favorite_vfs_file_finalize(GObject * object)1314 static void favorite_vfs_file_finalize (GObject *object)
1315 {
1316     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (object));
1317 
1318     g_clear_pointer (&priv->uri, g_free);
1319 
1320     G_OBJECT_CLASS (favorite_vfs_file_parent_class)->finalize (object);
1321 }
1322 
1323 static void
ensure_metadata_store(FavoriteVfsFile * file)1324 ensure_metadata_store (FavoriteVfsFile *file)
1325 {
1326     if (is_root_file (file))
1327     {
1328         if (settings == NULL)
1329         {
1330             settings = g_settings_new (FAVORITES_SCHEMA);
1331             g_object_add_weak_pointer (G_OBJECT (settings), (gpointer) &settings);
1332         }
1333         else
1334         {
1335             g_object_ref (settings);
1336         }
1337     }
1338 }
1339 
favorite_vfs_file_class_init(FavoriteVfsFileClass * klass)1340 static void favorite_vfs_file_class_init (FavoriteVfsFileClass *klass)
1341 {
1342     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1343 
1344     gobject_class->dispose = favorite_vfs_file_dispose;
1345     gobject_class->finalize = favorite_vfs_file_finalize;
1346 }
1347 
favorite_vfs_file_init(FavoriteVfsFile * self)1348 static void favorite_vfs_file_init (FavoriteVfsFile *self)
1349 {
1350 }
1351 
_favorite_vfs_file_new_for_info(XAppFavoriteInfo * info)1352 GFile *_favorite_vfs_file_new_for_info (XAppFavoriteInfo *info)
1353 {
1354     FavoriteVfsFile *new_file;
1355 
1356     new_file = g_object_new (FAVORITE_TYPE_VFS_FILE, NULL);
1357 
1358     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (new_file));
1359 
1360     priv->uri = path_to_fav_uri (info->display_name);
1361     priv->info = xapp_favorite_info_copy (info);
1362     ensure_metadata_store (new_file);
1363 
1364     return G_FILE (new_file);
1365 }
1366 
favorite_vfs_file_get_real_uri(GFile * file)1367 gchar *favorite_vfs_file_get_real_uri (GFile *file)
1368 {
1369     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
1370 
1371     if (priv->info != NULL && priv->info->uri != NULL)
1372     {
1373         return g_strdup (priv->info->uri);
1374     }
1375 
1376     return NULL;
1377 }
1378 
favorite_vfs_file_new_for_uri(const char * uri)1379 GFile *favorite_vfs_file_new_for_uri (const char *uri)
1380 {
1381     FavoriteVfsFile *new_file;
1382     g_autofree gchar *basename = NULL;
1383 
1384     new_file = g_object_new (FAVORITE_TYPE_VFS_FILE, NULL);
1385 
1386     DEBUG ("FavoriteVfsFile new for uri: %s", uri);
1387 
1388     FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (new_file));
1389 
1390     priv->uri = g_strdup (uri);
1391     ensure_metadata_store (new_file);
1392 
1393     if (g_strcmp0 (uri, ROOT_URI) == 0)
1394     {
1395         priv->info = NULL;
1396     }
1397     else
1398     {
1399         gchar *display_name;
1400 
1401         display_name = fav_uri_to_display_name (uri);
1402         XAppFavoriteInfo *info = xapp_favorites_find_by_display_name (xapp_favorites_get_default (),
1403                                                                       display_name);
1404 
1405         if (info != NULL)
1406         {
1407             priv->info = xapp_favorite_info_copy (info);
1408         }
1409         else
1410         {
1411             info = g_slice_new0 (XAppFavoriteInfo);
1412             info->uri = g_strdup (NULL);
1413             info->display_name = g_strdup (display_name);
1414             info->cached_mimetype = NULL;
1415 
1416             priv->info = info;
1417         }
1418 
1419         g_free (display_name);
1420     }
1421 
1422     return G_FILE (new_file);
1423 }
1424 
favorite_vfs_file_new(void)1425 GFile *favorite_vfs_file_new (void)
1426 {
1427     return favorite_vfs_file_new_for_uri (ROOT_URI);
1428 }
1429 
1430 static GFile *
favorite_vfs_lookup(GVfs * vfs,const char * identifier,gpointer user_data)1431 favorite_vfs_lookup (GVfs       *vfs,
1432                      const char *identifier,
1433                      gpointer    user_data)
1434 {
1435     if (g_str_has_prefix (identifier, ROOT_URI))
1436     {
1437         return favorite_vfs_file_new_for_uri (identifier);
1438     }
1439 
1440     return NULL;
1441 }
1442 
1443 void
init_favorite_vfs(void)1444 init_favorite_vfs (void)
1445 {
1446     static gsize once_init_value = 0;
1447 
1448     if (g_once_init_enter (&once_init_value))
1449     {
1450         GVfs *vfs;
1451         vfs = g_vfs_get_default ();
1452 
1453         g_vfs_register_uri_scheme (vfs, "favorites",
1454                                    favorite_vfs_lookup, NULL, NULL,
1455                                    favorite_vfs_lookup, NULL, NULL);
1456 
1457         g_once_init_leave (&once_init_value, 1);
1458     }
1459 }
1460