1 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 /* vim:set et sts=4: */
3 /* bus - The Input Bus
4  * Copyright (C) 2015 Peng Huang <shawn.p.huang@gmail.com>
5  * Copyright (C) 2015-2020 Takao Fujiwara <takao.fujiwara1@gmail.com>
6  * Copyright (C) 2015-2020 Red Hat, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This 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  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
21  * USA
22  */
23 #include <gio/gio.h>
24 #include <glib/gstdio.h>
25 #include <string.h>
26 
27 #include "ibusinternal.h"
28 #include "ibusmarshalers.h"
29 #include "ibusregistry.h"
30 
31 #define IBUS_CACHE_MAGIC 0x49425553 /* "IBUS" */
32 #define IBUS_CACHE_VERSION 0x00010522
33 
34 enum {
35     CHANGED,
36     LAST_SIGNAL,
37 };
38 
39 static guint             _signals[LAST_SIGNAL] = { 0 };
40 
41 struct _IBusRegistryPrivate {
42     /* a list of IBusObservedPath objects. */
43     GList *observed_paths;
44 
45     /* a list of IBusComponent objects that are created from component XML
46      * files (or from the cache of them). */
47     GList *components;
48 
49     gboolean changed;
50 
51     /* a mapping from GFile to GFileMonitor. */
52     GHashTable *monitor_table;
53 
54     guint monitor_timeout_id;
55 };
56 
57 #define IBUS_REGISTRY_GET_PRIVATE(o)  \
58    ((IBusRegistryPrivate *)ibus_registry_get_instance_private (o))
59 
60 /* functions prototype */
61 static void     ibus_registry_destroy        (IBusRegistry           *registry);
62 static void     ibus_registry_remove_all     (IBusRegistry           *registry);
63 static gboolean ibus_registry_serialize      (IBusRegistry           *registry,
64                                               GVariantBuilder        *builder);
65 static gint     ibus_registry_deserialize    (IBusRegistry           *registry,
66                                               GVariant               *variant);
67 static gboolean ibus_registry_copy           (IBusRegistry           *dest,
68                                               const IBusRegistry     *src);
69 
G_DEFINE_TYPE_WITH_PRIVATE(IBusRegistry,ibus_registry,IBUS_TYPE_SERIALIZABLE)70 G_DEFINE_TYPE_WITH_PRIVATE (IBusRegistry, ibus_registry, IBUS_TYPE_SERIALIZABLE)
71 
72 static void
73 ibus_registry_class_init (IBusRegistryClass *class)
74 {
75     GObjectClass *gobject_class = G_OBJECT_CLASS (class);
76     IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (class);
77     IBusSerializableClass *serializable_class = IBUS_SERIALIZABLE_CLASS (class);
78 
79     ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_registry_destroy;
80 
81     serializable_class->serialize =
82         (IBusSerializableSerializeFunc) ibus_registry_serialize;
83     serializable_class->deserialize =
84         (IBusSerializableDeserializeFunc) ibus_registry_deserialize;
85     serializable_class->copy = (IBusSerializableCopyFunc) ibus_registry_copy;
86 
87     /* install signals */
88     /**
89      * IBusRegistry::changed:
90      * @registry: An #IBusRegistry.
91      *
92      * Emitted when any observed paths are changed.
93      * A method is not associated in this class. the "changed"
94      * signal would be handled in other classes.
95      *
96      * See also: ibus_registry_start_monitor_changes().
97      */
98     _signals[CHANGED] =
99         g_signal_new (I_("changed"),
100             G_TYPE_FROM_CLASS (gobject_class),
101             G_SIGNAL_RUN_LAST,
102             0,
103             NULL, NULL,
104             _ibus_marshal_VOID__VOID,
105             G_TYPE_NONE,
106             0);
107 }
108 
109 static void
ibus_registry_init(IBusRegistry * registry)110 ibus_registry_init (IBusRegistry *registry)
111 {
112     registry->priv = IBUS_REGISTRY_GET_PRIVATE (registry);
113 
114     registry->priv->observed_paths = NULL;
115     registry->priv->components = NULL;
116     registry->priv->changed = FALSE;
117     registry->priv->monitor_table =
118         g_hash_table_new_full (g_file_hash,
119                                (GEqualFunc) g_file_equal,
120                                (GDestroyNotify) g_object_unref,
121                                (GDestroyNotify) g_object_unref);
122 }
123 
124 static void
ibus_registry_destroy(IBusRegistry * registry)125 ibus_registry_destroy (IBusRegistry *registry)
126 {
127     ibus_registry_remove_all (registry);
128 
129     g_hash_table_destroy (registry->priv->monitor_table);
130     registry->priv->monitor_table = NULL;
131 
132     if (registry->priv->monitor_timeout_id > 0) {
133         g_source_remove (registry->priv->monitor_timeout_id);
134         registry->priv->monitor_timeout_id = 0;
135     }
136 
137     IBUS_OBJECT_CLASS (ibus_registry_parent_class)->
138             destroy (IBUS_OBJECT (registry));
139 }
140 
141 static gboolean
ibus_registry_serialize(IBusRegistry * registry,GVariantBuilder * builder)142 ibus_registry_serialize (IBusRegistry    *registry,
143                          GVariantBuilder *builder)
144 {
145     gboolean retval;
146 
147     retval = IBUS_SERIALIZABLE_CLASS (ibus_registry_parent_class)->
148         serialize ((IBusSerializable *)registry, builder);
149     g_return_val_if_fail (retval, FALSE);
150 
151     GList *p;
152     GVariantBuilder *array;
153 
154     array = g_variant_builder_new (G_VARIANT_TYPE ("av"));
155     for (p = registry->priv->observed_paths; p != NULL; p = p->next) {
156         IBusSerializable *serializable = (IBusSerializable *) p->data;
157         g_variant_builder_add (array,
158                                "v",
159                                ibus_serializable_serialize (serializable));
160     }
161     g_variant_builder_add (builder, "av", array);
162     g_variant_builder_unref (array);
163 
164     array = g_variant_builder_new (G_VARIANT_TYPE ("av"));
165     for (p = registry->priv->components; p != NULL; p = p->next) {
166         IBusSerializable *serializable = (IBusSerializable *) p->data;
167         g_variant_builder_add (array,
168                                "v",
169                                ibus_serializable_serialize (serializable));
170     }
171     g_variant_builder_add (builder, "av", array);
172     g_variant_builder_unref (array);
173 
174     return TRUE;
175 }
176 
177 static gint
ibus_registry_deserialize(IBusRegistry * registry,GVariant * variant)178 ibus_registry_deserialize (IBusRegistry *registry,
179                            GVariant     *variant)
180 {
181     GVariant *var;
182     GVariantIter *iter;
183     gint retval;
184 
185     retval = IBUS_SERIALIZABLE_CLASS (ibus_registry_parent_class)->
186         deserialize ((IBusSerializable *)registry, variant);
187     g_return_val_if_fail (retval, 0);
188 
189     g_variant_get_child (variant, retval++, "av", &iter);
190     while (g_variant_iter_loop (iter, "v", &var)) {
191         IBusSerializable *serializable = ibus_serializable_deserialize (var);
192         registry->priv->observed_paths =
193             g_list_append (registry->priv->observed_paths,
194                            IBUS_OBSERVED_PATH (serializable));
195     }
196     g_variant_iter_free (iter);
197 
198     g_variant_get_child (variant, retval++, "av", &iter);
199     while (g_variant_iter_loop (iter, "v", &var)) {
200         IBusSerializable *serializable = ibus_serializable_deserialize (var);
201         registry->priv->components =
202             g_list_append (registry->priv->components,
203                            IBUS_COMPONENT (serializable));
204     }
205     g_variant_iter_free (iter);
206 
207     return retval;
208 }
209 
210 static gboolean
ibus_registry_copy(IBusRegistry * dest,const IBusRegistry * src)211 ibus_registry_copy (IBusRegistry       *dest,
212                     const IBusRegistry *src)
213 {
214     gboolean retval;
215 
216     retval = IBUS_SERIALIZABLE_CLASS (ibus_registry_parent_class)->
217         copy ((IBusSerializable *)dest, (IBusSerializable *)src);
218     g_return_val_if_fail (retval, FALSE);
219 
220     dest->priv->components = g_list_copy (src->priv->components);
221     dest->priv->observed_paths = g_list_copy (src->priv->observed_paths);
222 
223     return TRUE;
224 }
225 
226 /**
227  * ibus_registry_remove_all:
228  *
229  * Remove the loaded registry.
230  */
231 static void
ibus_registry_remove_all(IBusRegistry * registry)232 ibus_registry_remove_all (IBusRegistry *registry)
233 {
234     g_assert (IBUS_IS_REGISTRY (registry));
235 
236     g_list_free_full (registry->priv->observed_paths, g_object_unref);
237     registry->priv->observed_paths = NULL;
238 
239     g_list_free_full (registry->priv->components, g_object_unref);
240     registry->priv->components = NULL;
241 }
242 
243 void
ibus_registry_load(IBusRegistry * registry)244 ibus_registry_load (IBusRegistry *registry)
245 {
246     const gchar *envstr;
247     GPtrArray *path;
248     gchar **d, **search_path;
249 
250     g_assert (IBUS_IS_REGISTRY (registry));
251 
252     path = g_ptr_array_new();
253 
254     envstr = g_getenv ("IBUS_COMPONENT_PATH");
255     if (envstr) {
256         gchar **dirs = g_strsplit (envstr, G_SEARCHPATH_SEPARATOR_S, 0);
257         for (d = dirs; *d != NULL; d++)
258             g_ptr_array_add (path, *d);
259         g_free (dirs);
260     } else {
261         gchar *dirname;
262 
263         dirname = g_build_filename (IBUS_DATA_DIR, "component", NULL);
264         g_ptr_array_add (path, dirname);
265 
266 #if 0
267         /* FIXME Should we support install some IME in user dir? */
268         dirname = g_build_filename (g_get_user_data_dir (),
269                                     "ibus", "component",
270                                     NULL);
271         g_ptr_array_add (path, dirname);
272 #endif
273     }
274 
275     g_ptr_array_add (path, NULL);
276     search_path = (gchar **) g_ptr_array_free (path, FALSE);
277     for (d = search_path; *d != NULL; d++) {
278         ibus_registry_load_in_dir (registry, *d);
279     }
280     g_strfreev (search_path);
281 }
282 
283 gboolean
ibus_registry_load_cache(IBusRegistry * registry,gboolean is_user)284 ibus_registry_load_cache (IBusRegistry *registry,
285                           gboolean      is_user)
286 {
287     gchar *filename;
288     gboolean retval;
289 
290     g_assert (IBUS_IS_REGISTRY (registry));
291 
292     if (is_user) {
293         filename = g_build_filename (g_get_user_cache_dir (),
294                                      "ibus", "bus", "registry", NULL);
295     } else {
296         filename = g_build_filename (IBUS_CACHE_DIR,
297                                      "bus", "registry", NULL);
298     }
299 
300     retval = ibus_registry_load_cache_file (registry, filename);
301     g_free (filename);
302 
303     return retval;
304 }
305 
306 gboolean
ibus_registry_load_cache_file(IBusRegistry * registry,const gchar * filename)307 ibus_registry_load_cache_file (IBusRegistry *registry,
308                                const gchar  *filename)
309 {
310     gchar *contents, *p;
311     gsize length;
312     GVariant *variant;
313     GError *error;
314 
315     g_assert (IBUS_IS_REGISTRY (registry));
316     g_assert (filename != NULL);
317 
318     if (!g_file_test (filename, G_FILE_TEST_EXISTS))
319         return FALSE;
320 
321     error = NULL;
322     if (!g_file_get_contents (filename, &contents, &length, &error)) {
323         g_warning ("cannot read %s: %s", filename, error->message);
324         g_error_free (error);
325         return FALSE;
326     }
327 
328     p = contents;
329 
330     /* read file header including magic and version */
331     if (length < 8) {
332         g_free (contents);
333         return FALSE;
334     }
335 
336     if (GUINT32_FROM_BE (*(guint32 *) p) != IBUS_CACHE_MAGIC) {
337         g_free (contents);
338         return FALSE;
339     }
340     p += 4;
341 
342     if (GUINT32_FROM_BE (*(guint32 *) p) != IBUS_CACHE_VERSION) {
343         g_free (contents);
344         return FALSE;
345     }
346     p += 4;
347 
348     /* read serialized IBusRegistry */
349     variant = g_variant_new_from_data (G_VARIANT_TYPE ("(sa{sv}avav)"),
350                                        p,
351                                        length - (p - contents),
352                                        FALSE,
353                                        (GDestroyNotify) g_free,
354                                        NULL);
355     if (variant == NULL) {
356         g_free (contents);
357         return FALSE;
358     }
359 
360     ibus_registry_deserialize (registry, variant);
361     g_variant_unref (variant);
362     g_free (contents);
363 
364     return TRUE;
365 }
366 
367 gboolean
ibus_registry_check_modification(IBusRegistry * registry)368 ibus_registry_check_modification (IBusRegistry *registry)
369 {
370     GList *p;
371 
372     g_assert (IBUS_IS_REGISTRY (registry));
373 
374     for (p = registry->priv->observed_paths; p != NULL; p = p->next) {
375         if (!IBUS_IS_OBSERVED_PATH (p->data)) {
376             g_warning ("The registry cache of observed_paths might be " \
377                        "broken and have to generate the cache again.");
378             g_list_free_full (registry->priv->observed_paths, g_object_unref);
379             registry->priv->observed_paths = NULL;
380             return TRUE;
381         }
382         if (ibus_observed_path_check_modification (
383                     (IBusObservedPath *) p->data))
384             return TRUE;
385     }
386 
387     for (p = registry->priv->components; p != NULL; p = p->next) {
388         if (!IBUS_IS_COMPONENT (p->data)) {
389             g_warning ("The registry cache of components might be " \
390                        "broken and have to generate the cache again.");
391             g_list_free_full (registry->priv->components, g_object_unref);
392             registry->priv->components = NULL;
393             return TRUE;
394         }
395         if (ibus_component_check_modification ((IBusComponent *) p->data))
396             return TRUE;
397     }
398 
399     return FALSE;
400 }
401 
402 gboolean
ibus_registry_save_cache(IBusRegistry * registry,gboolean is_user)403 ibus_registry_save_cache (IBusRegistry *registry,
404                           gboolean      is_user)
405 {
406     gchar *filename;
407     gboolean retval;
408 
409     g_assert (IBUS_IS_REGISTRY (registry));
410 
411     if (is_user) {
412         filename = g_build_filename (g_get_user_cache_dir (),
413                                      "ibus", "bus", "registry", NULL);
414     } else {
415         filename = g_build_filename (IBUS_CACHE_DIR,
416                                      "bus", "registry", NULL);
417     }
418 
419     retval = ibus_registry_save_cache_file (registry, filename);
420     g_free (filename);
421 
422     return retval;
423 }
424 
425 gboolean
ibus_registry_save_cache_file(IBusRegistry * registry,const gchar * filename)426 ibus_registry_save_cache_file (IBusRegistry *registry,
427                                const gchar  *filename)
428 {
429     gchar *cachedir;
430     GVariant *variant;
431     gchar *contents, *p;
432     gsize length;
433     gboolean retval;
434     guint32 intval;
435     GError *error;
436 
437     g_assert (IBUS_IS_REGISTRY (registry));
438     g_assert (filename != NULL);
439 
440     cachedir = g_path_get_dirname (filename);
441     g_mkdir_with_parents (cachedir, 0775);
442     g_free (cachedir);
443 
444     variant = ibus_serializable_serialize (IBUS_SERIALIZABLE (registry));
445     length = 8 + g_variant_get_size (variant);
446     p = contents = g_slice_alloc (length);
447 
448     /* write file header */
449     intval = GUINT32_TO_BE (IBUS_CACHE_MAGIC);
450     memcpy (p, (gchar *) &intval, 4);
451     p += 4;
452 
453     intval = GUINT32_TO_BE (IBUS_CACHE_VERSION);
454     memcpy (p, (gchar *) &intval, 4);
455     p += 4;
456 
457     /* write serialized IBusRegistry */
458     g_variant_store (variant, p);
459 
460     error = NULL;
461     retval = g_file_set_contents (filename, contents, length, &error);
462 
463     g_variant_unref (variant);
464     g_slice_free1 (length, contents);
465 
466     if (!retval) {
467         g_warning ("cannot write %s: %s", filename, error->message);
468         g_error_free (error);
469         return FALSE;
470     }
471 
472     if (g_str_has_prefix (filename, g_get_user_cache_dir ())) {
473         g_warn_if_fail (!g_chmod (filename, 0644));
474     }
475 
476     return TRUE;
477 }
478 
479 #define g_string_append_indent(string, indent)  \
480     {                                           \
481         gint i;                                 \
482         for (i = 0; i < (indent); i++) {        \
483             g_string_append (string, "    ");   \
484         }                                       \
485     }
486 
487 void
ibus_registry_output(IBusRegistry * registry,GString * output,int indent)488 ibus_registry_output (IBusRegistry *registry,
489                       GString      *output,
490                       int           indent)
491 {
492     GList *p;
493 
494     g_assert (IBUS_IS_REGISTRY (registry));
495     g_return_if_fail (output != NULL);
496 
497     g_string_append (output, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
498     g_string_append (output, "<ibus-registry>\n");
499 
500     if (registry->priv->observed_paths) {
501         g_string_append_indent (output, indent);
502         g_string_append (output, "<observed-paths>\n");
503         for (p = registry->priv->observed_paths; p != NULL; p = p->next) {
504             ibus_observed_path_output ((IBusObservedPath *) p->data,
505                                       output, indent * 2);
506         }
507         g_string_append_indent (output, indent);
508         g_string_append (output, "</observed-paths>\n");
509     }
510 
511     if (registry->priv->components) {
512         g_string_append_indent (output, indent);
513         g_string_append (output, "<components>\n");
514         for (p = registry->priv->components; p != NULL; p = p->next) {
515             ibus_component_output ((IBusComponent *) p->data,
516                                    output, indent * 2);
517         }
518         g_string_append_indent (output, indent);
519         g_string_append (output, "</components>\n");
520     }
521 
522     g_string_append (output, "</ibus-registry>\n");
523 }
524 
525 void
ibus_registry_load_in_dir(IBusRegistry * registry,const gchar * dirname)526 ibus_registry_load_in_dir (IBusRegistry *registry,
527                            const gchar  *dirname)
528 {
529     GError *error = NULL;
530     GDir *dir;
531     IBusObservedPath *observed_path = NULL;
532     const gchar *filename;
533 
534     g_assert (IBUS_IS_REGISTRY (registry));
535     g_assert (dirname);
536 
537     dir = g_dir_open (dirname, 0, &error);
538 
539     if (dir == NULL) {
540         g_warning ("Unable open directory %s : %s", dirname, error->message);
541         g_error_free (error);
542         return;
543     }
544 
545     observed_path = ibus_observed_path_new (dirname, TRUE);
546 
547     registry->priv->observed_paths =
548             g_list_append (registry->priv->observed_paths,
549                            observed_path);
550 
551     while ((filename = g_dir_read_name (dir)) != NULL) {
552         glong size;
553         gchar *path;
554         IBusComponent *component;
555 
556         size = g_utf8_strlen (filename, -1);
557         if (g_strcmp0 (MAX (filename, filename + size - 4), ".xml") != 0)
558             continue;
559 
560         path = g_build_filename (dirname, filename, NULL);
561         component = ibus_component_new_from_file (path);
562         if (component != NULL) {
563             g_object_ref_sink (component);
564             registry->priv->components =
565                 g_list_append (registry->priv->components, component);
566         }
567 
568         g_free (path);
569     }
570 
571     g_dir_close (dir);
572 }
573 
574 
575 IBusRegistry *
ibus_registry_new(void)576 ibus_registry_new (void)
577 {
578     IBusRegistry *registry;
579     registry = (IBusRegistry *) g_object_new (IBUS_TYPE_REGISTRY, NULL);
580     return registry;
581 }
582 
583 GList *
ibus_registry_get_components(IBusRegistry * registry)584 ibus_registry_get_components (IBusRegistry *registry)
585 {
586     g_assert (IBUS_IS_REGISTRY (registry));
587 
588     return g_list_copy (registry->priv->components);
589 }
590 
591 GList *
ibus_registry_get_observed_paths(IBusRegistry * registry)592 ibus_registry_get_observed_paths (IBusRegistry *registry)
593 {
594     g_assert (IBUS_IS_REGISTRY (registry));
595 
596     return g_list_copy (registry->priv->observed_paths);
597 }
598 
599 static gboolean
_monitor_timeout_cb(IBusRegistry * registry)600 _monitor_timeout_cb (IBusRegistry *registry)
601 {
602     g_hash_table_remove_all (registry->priv->monitor_table);
603     registry->priv->changed = TRUE;
604     g_signal_emit (registry, _signals[CHANGED], 0);
605     registry->priv->monitor_timeout_id = 0;
606     return FALSE;
607 }
608 
609 static void
_monitor_changed_cb(GFileMonitor * monitor,GFile * file,GFile * other_file,GFileMonitorEvent event_type,IBusRegistry * registry)610 _monitor_changed_cb (GFileMonitor     *monitor,
611                      GFile            *file,
612                      GFile            *other_file,
613                      GFileMonitorEvent event_type,
614                      IBusRegistry     *registry)
615 {
616     g_assert (IBUS_IS_REGISTRY (registry));
617 
618     if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
619         event_type != G_FILE_MONITOR_EVENT_DELETED &&
620         event_type != G_FILE_MONITOR_EVENT_CREATED &&
621         event_type != G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED)
622         return;
623 
624     /* Merge successive file changes into one, with a low priority
625        timeout handler. */
626     if (registry->priv->monitor_timeout_id > 0)
627         return;
628 
629     registry->priv->monitor_timeout_id =
630         g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
631                             5000,
632                             (GSourceFunc) _monitor_timeout_cb,
633                             g_object_ref (registry),
634                             (GDestroyNotify) g_object_unref);
635 }
636 
637 void
ibus_registry_start_monitor_changes(IBusRegistry * registry)638 ibus_registry_start_monitor_changes (IBusRegistry *registry)
639 {
640     GList *observed_paths, *p;
641 
642     g_assert (IBUS_IS_REGISTRY (registry));
643 
644     g_hash_table_remove_all (registry->priv->monitor_table);
645 
646     observed_paths = g_list_copy (registry->priv->observed_paths);
647     for (p = registry->priv->components; p != NULL; p = p->next) {
648         IBusComponent *component = (IBusComponent *) p->data;
649         GList *component_observed_paths =
650             ibus_component_get_observed_paths (component);
651         observed_paths = g_list_concat (observed_paths,
652                                         component_observed_paths);
653     }
654 
655     for (p = observed_paths; p != NULL; p = p->next) {
656         IBusObservedPath *path = (IBusObservedPath *) p->data;
657         GFile *file = g_file_new_for_path (path->path);
658         if (g_hash_table_lookup (registry->priv->monitor_table, file) == NULL) {
659             GFileMonitor *monitor;
660             GError *error;
661 
662             error = NULL;
663             monitor = g_file_monitor (file,
664                                       G_FILE_MONITOR_NONE,
665                                       NULL,
666                                       &error);
667 
668             if (monitor != NULL) {
669                 g_signal_connect (monitor, "changed",
670                                   G_CALLBACK (_monitor_changed_cb),
671                                   registry);
672 
673                 g_hash_table_replace (registry->priv->monitor_table,
674                                       g_object_ref (file),
675                                       monitor);
676             } else {
677                 g_warning ("Can't monitor directory %s: %s",
678                            path->path,
679                            error->message);
680                 g_error_free (error);
681             }
682         }
683         g_object_unref (file);
684     }
685     g_list_free (observed_paths);
686 }
687