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