1 /* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* GConf
3  * Copyright (C) 1999, 2000, 2000 Red Hat Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #include <config.h>
22 #include <stdio.h>
23 #include <string.h>
24 
25 #include "gconf-client.h"
26 #include "gconf/gconf-internals.h"
27 
28 #include "gconfmarshal.h"
29 #include "gconfmarshal.c"
30 
31 static gboolean do_trace = FALSE;
32 
33 static void
trace(const char * format,...)34 trace (const char *format, ...)
35 {
36   va_list args;
37   gchar *str;
38 
39   if (!do_trace)
40     return;
41 
42   g_return_if_fail (format != NULL);
43 
44   va_start (args, format);
45   str = g_strdup_vprintf (format, args);
46   va_end (args);
47 
48   g_message ("%s", str);
49 
50   g_free (str);
51 }
52 
53 /*
54  * Error handler override
55  */
56 
57 static GConfClientErrorHandlerFunc global_error_handler = NULL;
58 
59 /**
60  * gconf_client_set_global_default_error_handler: (skip)
61  * @func: pointer to the function to be called for error handling.
62  *
63  * Set @func as the default error handler for the #GConfClient. This handler would be called
64  * for all #GConfClient internal errors.
65  */
66 void
gconf_client_set_global_default_error_handler(GConfClientErrorHandlerFunc func)67 gconf_client_set_global_default_error_handler(GConfClientErrorHandlerFunc func)
68 {
69   global_error_handler = func;
70 }
71 
72 /*
73  * Dir object (for list of directories we're watching)
74  */
75 
76 typedef struct _Dir Dir;
77 
78 struct _Dir {
79   gchar* name;
80   guint notify_id;
81   /* number of times this dir has been added */
82   guint add_count;
83 };
84 
85 static Dir* dir_new(const gchar* name, guint notify_id);
86 static void dir_destroy(Dir* d);
87 
88 /*
89  * Listener object
90  */
91 
92 typedef struct _Listener Listener;
93 
94 struct _Listener {
95   GConfClientNotifyFunc func;
96   gpointer data;
97   GFreeFunc destroy_notify;
98 };
99 
100 static Listener* listener_new(GConfClientNotifyFunc func,
101                               GFreeFunc destroy_notify,
102                               gpointer data);
103 
104 static void listener_destroy(Listener* l);
105 
106 /*
107  * GConfClient proper
108  */
109 
110 #define PUSH_USE_ENGINE(client) do { if ((client)->engine) gconf_engine_push_owner_usage ((client)->engine, client); } while (0)
111 #define POP_USE_ENGINE(client) do { if ((client)->engine) gconf_engine_pop_owner_usage ((client)->engine, client); } while (0)
112 
113 enum {
114   VALUE_CHANGED,
115   UNRETURNED_ERROR,
116   ERROR,
117   LAST_SIGNAL
118 };
119 
120 static void         register_client   (GConfClient *client);
121 static GConfClient *lookup_client     (GConfEngine *engine);
122 static void         unregister_client (GConfClient *client);
123 
124 static void gconf_client_class_init (GConfClientClass *klass);
125 static void gconf_client_init       (GConfClient      *client);
126 static void gconf_client_real_unreturned_error (GConfClient* client, GError* error);
127 static void gconf_client_real_error            (GConfClient* client, GError* error);
128 static void gconf_client_finalize              (GObject* object);
129 
130 static gboolean gconf_client_cache          (GConfClient *client,
131                                              gboolean     take_ownership,
132                                              GConfEntry  *entry,
133                                              gboolean    preserve_schema_name);
134 
135 static gboolean gconf_client_lookup         (GConfClient *client,
136                                              const char  *key,
137                                              GConfEntry **entryp);
138 
139 static void gconf_client_real_remove_dir    (GConfClient* client,
140                                              Dir* d,
141                                              GError** err);
142 
143 static void gconf_client_queue_notify       (GConfClient *client,
144                                              const char  *key);
145 static void gconf_client_flush_notifies     (GConfClient *client);
146 static void gconf_client_unqueue_notifies   (GConfClient *client);
147 static void notify_one_entry (GConfClient *client, GConfEntry  *entry);
148 
149 static GConfEntry* get (GConfClient  *client,
150                         const gchar  *key,
151                         gboolean      use_default,
152                         GError      **error);
153 
154 
155 static gpointer parent_class = NULL;
156 static guint client_signals[LAST_SIGNAL] = { 0 };
157 
158 GType
gconf_client_get_type(void)159 gconf_client_get_type (void)
160 {
161   static GType client_type = 0;
162 
163   if (!client_type)
164     {
165       static const GTypeInfo client_info =
166       {
167         sizeof (GConfClientClass),
168         (GBaseInitFunc) NULL,
169         (GBaseFinalizeFunc) NULL,
170         (GClassInitFunc) gconf_client_class_init,
171         NULL,           /* class_finalize */
172         NULL,           /* class_data */
173         sizeof (GConfClient),
174         0,              /* n_preallocs */
175         (GInstanceInitFunc) gconf_client_init
176       };
177 
178       client_type = g_type_register_static (G_TYPE_OBJECT,
179                                             "GConfClient",
180                                             &client_info,
181                                             0);
182     }
183 
184   return client_type;
185 }
186 
187 static void
gconf_client_class_init(GConfClientClass * class)188 gconf_client_class_init (GConfClientClass *class)
189 {
190   GObjectClass *object_class;
191 
192   object_class = (GObjectClass*) class;
193 
194   parent_class = g_type_class_peek_parent (class);
195 
196   client_signals[VALUE_CHANGED] =
197     g_signal_new ("value_changed",
198                   G_TYPE_FROM_CLASS (object_class),
199                   G_SIGNAL_RUN_LAST,
200                   G_STRUCT_OFFSET (GConfClientClass, value_changed),
201                   NULL, NULL,
202                   gconf_marshal_VOID__STRING_POINTER,
203                   G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_POINTER);
204 
205   client_signals[UNRETURNED_ERROR] =
206     g_signal_new ("unreturned_error",
207                   G_TYPE_FROM_CLASS (object_class),
208                   G_SIGNAL_RUN_LAST,
209                   G_STRUCT_OFFSET (GConfClientClass, unreturned_error),
210                   NULL, NULL,
211                   gconf_marshal_VOID__POINTER,
212                   G_TYPE_NONE, 1, G_TYPE_POINTER);
213 
214   client_signals[ERROR] =
215     g_signal_new ("error",
216                   G_TYPE_FROM_CLASS (object_class),
217                   G_SIGNAL_RUN_LAST,
218                   G_STRUCT_OFFSET (GConfClientClass, error),
219                   NULL, NULL,
220                   gconf_marshal_VOID__POINTER,
221                   G_TYPE_NONE, 1, G_TYPE_POINTER);
222 
223   class->value_changed    = NULL;
224   class->unreturned_error = gconf_client_real_unreturned_error;
225   class->error            = gconf_client_real_error;
226 
227   object_class->finalize  = gconf_client_finalize;
228 
229   if (g_getenv ("GCONF_DEBUG_TRACE_CLIENT") != NULL)
230     do_trace = TRUE;
231 }
232 
233 static void
gconf_client_init(GConfClient * client)234 gconf_client_init (GConfClient *client)
235 {
236   client->engine = NULL;
237   client->error_mode = GCONF_CLIENT_HANDLE_UNRETURNED;
238   client->dir_hash = g_hash_table_new (g_str_hash, g_str_equal);
239   client->cache_hash = g_hash_table_new (g_str_hash, g_str_equal);
240   client->cache_dirs = g_hash_table_new_full (g_str_hash, g_str_equal,
241 					      g_free, NULL);
242   client->cache_recursive_dirs = g_hash_table_new_full (g_str_hash, g_str_equal,
243                                                         g_free, NULL);
244   /* We create the listeners only if they're actually used */
245   client->listeners = NULL;
246   client->notify_list = NULL;
247   client->notify_handler = 0;
248 }
249 
250 static gboolean
destroy_dir_foreach_remove(gpointer key,gpointer value,gpointer user_data)251 destroy_dir_foreach_remove(gpointer key, gpointer value, gpointer user_data)
252 {
253   GConfClient *client = user_data;
254   Dir* d = value;
255 
256   /* remove notify for this dir */
257 
258   if (d->notify_id != 0)
259     {
260       trace ("REMOTED: Removing notify ID %u from engine", d->notify_id);
261       PUSH_USE_ENGINE (client);
262 	  gconf_engine_notify_remove (client->engine, d->notify_id);
263       POP_USE_ENGINE (client);
264     }
265 
266   d->notify_id = 0;
267 
268   dir_destroy(value);
269 
270   return TRUE;
271 }
272 
273 static void
set_engine(GConfClient * client,GConfEngine * engine)274 set_engine (GConfClient *client,
275             GConfEngine *engine)
276 {
277   if (engine == client->engine)
278     return;
279 
280   if (engine)
281     {
282       gconf_engine_ref (engine);
283 
284       gconf_engine_set_owner (engine, client);
285     }
286 
287   if (client->engine)
288     {
289       gconf_engine_set_owner (client->engine, NULL);
290 
291       gconf_engine_unref (client->engine);
292     }
293 
294   client->engine = engine;
295 }
296 
297 static void
gconf_client_finalize(GObject * object)298 gconf_client_finalize (GObject* object)
299 {
300   GConfClient* client = GCONF_CLIENT(object);
301 
302   gconf_client_unqueue_notifies (client);
303 
304   g_hash_table_foreach_remove (client->dir_hash,
305                                destroy_dir_foreach_remove, client);
306 
307   gconf_client_clear_cache (client);
308 
309   if (client->listeners != NULL)
310     {
311       gconf_listeners_free (client->listeners);
312       client->listeners = NULL;
313     }
314 
315   g_hash_table_destroy (client->dir_hash);
316   client->dir_hash = NULL;
317 
318   g_hash_table_destroy (client->cache_hash);
319   client->cache_hash = NULL;
320 
321   g_hash_table_destroy (client->cache_recursive_dirs);
322   client->cache_recursive_dirs = NULL;
323 
324   g_hash_table_destroy (client->cache_dirs);
325   client->cache_dirs = NULL;
326 
327   unregister_client (client);
328 
329   set_engine (client, NULL);
330 
331   if (G_OBJECT_CLASS (parent_class)->finalize)
332     (* G_OBJECT_CLASS (parent_class)->finalize) (object);
333 }
334 
335 /*
336  * Default error handlers
337  */
338 
339 static void
gconf_client_real_unreturned_error(GConfClient * client,GError * error)340 gconf_client_real_unreturned_error (GConfClient* client, GError* error)
341 {
342   trace ("Unreturned error '%s'", error->message);
343   if (client->error_mode == GCONF_CLIENT_HANDLE_UNRETURNED)
344     {
345       if (global_error_handler != NULL)
346         {
347           (*global_error_handler) (client, error);
348         }
349       else
350         {
351           /* We silently ignore this, since it probably isn't
352            * really an error per se
353            */
354           if (error->code == GCONF_ERROR_OVERRIDDEN ||
355               error->code == GCONF_ERROR_NO_WRITABLE_DATABASE)
356             return;
357 
358           g_printerr (_("GConf Error: %s\n"),
359                       error->message);
360         }
361     }
362 }
363 
364 static void
gconf_client_real_error(GConfClient * client,GError * error)365 gconf_client_real_error            (GConfClient* client, GError* error)
366 {
367   trace ("Error '%s'", error->message);
368   if (client->error_mode == GCONF_CLIENT_HANDLE_ALL)
369     {
370       if (global_error_handler != NULL)
371         {
372           (*global_error_handler) (client, error);
373         }
374       else
375         {
376           g_printerr (_("GConf Error: %s\n"),
377                       error->message);
378         }
379     }
380 }
381 
382 /* Emit the proper signals for the error, and fill in err */
383 static gboolean
handle_error(GConfClient * client,GError * error,GError ** err)384 handle_error(GConfClient* client, GError* error, GError** err)
385 {
386   if (error != NULL)
387     {
388       gconf_client_error(client, error);
389 
390       if (err == NULL)
391         {
392           gconf_client_unreturned_error(client, error);
393 
394           g_error_free(error);
395         }
396       else
397         *err = error;
398 
399       return TRUE;
400     }
401   else
402     return FALSE;
403 }
404 
405 static void
notify_from_server_callback(GConfEngine * conf,guint cnxn_id,GConfEntry * entry,gpointer user_data)406 notify_from_server_callback (GConfEngine* conf, guint cnxn_id,
407                              GConfEntry *entry,
408                              gpointer user_data)
409 {
410   GConfClient* client = user_data;
411   gboolean changed;
412 
413   g_return_if_fail (client != NULL);
414   g_return_if_fail (GCONF_IS_CLIENT(client));
415   g_return_if_fail (client->engine == conf);
416 
417   trace ("Received notify of change to '%s' from server",
418          entry->key);
419 
420   /* First do the caching, so that state is sane for the
421    * listeners or functions connected to value_changed.
422    * We know this key is under a directory in our dir list.
423    */
424   changed = gconf_client_cache (client, FALSE, entry, TRUE);
425 
426   if (!changed)
427     return; /* don't do the notify */
428 
429   gconf_client_queue_notify (client, entry->key);
430 }
431 
432 /*
433  * Public API
434  */
435 
436 
437 /**
438  * gconf_client_get_default:
439  *
440  * Creates a new #GConfClient using the default #GConfEngine. Normally this is the
441  * engine you want. If someone else is already using the default
442  * #GConfClient, this function returns the same one they're using, but
443  * with the reference count incremented. So you have to unref either way.
444  *
445  * It's important to call g_type_init() before using this GObject, to initialize the type system.
446  *
447  * Return value: (transfer full): a new #GConfClient. g_object_unref() when you're done.
448  */
449 GConfClient*
gconf_client_get_default(void)450 gconf_client_get_default (void)
451 {
452   GConfClient *client;
453   GConfEngine *engine;
454 
455   g_return_val_if_fail(gconf_is_initialized(), NULL);
456 
457   engine = gconf_engine_get_default ();
458 
459   client = lookup_client (engine);
460   if (client)
461     {
462       g_assert (client->engine == engine);
463       g_object_ref (G_OBJECT (client));
464       gconf_engine_unref (engine);
465       return client;
466     }
467   else
468     {
469       client = g_object_new (gconf_client_get_type (), NULL);
470       g_object_ref (G_OBJECT (client));
471       set_engine (client, engine);
472       register_client (client);
473     }
474 
475   return client;
476 }
477 
478 /**
479  * gconf_client_get_for_engine:
480  * @engine: the #GConfEngine to use.
481  *
482  * Creates a new #GConfClient with a specific #GConfEngine. Only specialized
483  * configuration-related programs should need to call this function. The
484  * returned #GConfClient should be unref'd when you're done with g_object_unref().
485  * Remember to avoid using the #GConfEngine directly once you have a #GConfClient
486  * wrapper.
487  *
488  * Return value: (transfer full): a new #GConfClient.
489  */
490 GConfClient*
gconf_client_get_for_engine(GConfEngine * engine)491 gconf_client_get_for_engine (GConfEngine* engine)
492 {
493   GConfClient *client;
494 
495   g_return_val_if_fail(gconf_is_initialized(), NULL);
496 
497   client = lookup_client (engine);
498   if (client)
499     {
500       g_assert (client->engine == engine);
501       g_object_ref (G_OBJECT (client));
502       return client;
503     }
504   else
505     {
506       client = g_object_new (gconf_client_get_type (), NULL);
507 
508       set_engine (client, engine);
509 
510       register_client (client);
511     }
512 
513   return client;
514 }
515 
516 typedef struct {
517   GConfClient *client;
518   Dir *lower_dir;
519   const char *dirname;
520 } OverlapData;
521 
522 static void
foreach_setup_overlap(gpointer key,gpointer value,gpointer user_data)523 foreach_setup_overlap(gpointer key, gpointer value, gpointer user_data)
524 {
525   GConfClient *client;
526   Dir *dir = value;
527   OverlapData * od = user_data;
528 
529   client = od->client;
530 
531   /* if we have found the first (well there is only one anyway) directory
532    * that includes us that has a notify handler
533    */
534 #ifdef GCONF_ENABLE_DEBUG
535   if (dir->notify_id != 0 &&
536       gconf_key_is_below(dir->name, od->dirname))
537     {
538       g_assert(od->lower_dir == NULL);
539       od->lower_dir = dir;
540     }
541 #else
542   if (od->lower_dir == NULL &&
543       dir->notify_id != 0 &&
544       gconf_key_is_below(dir->name, od->dirname))
545       od->lower_dir = dir;
546 #endif
547   /* if we have found a directory that we include and it has
548    * a notify_id, remove the notify handler now
549    * FIXME: this is a race, from now on we can miss notifies, it is
550    * not an incredible amount of time so this is not a showstopper */
551   else if (dir->notify_id != 0 &&
552            gconf_key_is_below (od->dirname, dir->name))
553     {
554       PUSH_USE_ENGINE (client);
555       gconf_engine_notify_remove (client->engine, dir->notify_id);
556       POP_USE_ENGINE (client);
557       dir->notify_id = 0;
558     }
559 }
560 
561 static Dir *
setup_overlaps(GConfClient * client,const gchar * dirname)562 setup_overlaps(GConfClient* client, const gchar* dirname)
563 {
564   OverlapData od;
565 
566   od.client = client;
567   od.lower_dir = NULL;
568   od.dirname = dirname;
569 
570   g_hash_table_foreach(client->dir_hash, foreach_setup_overlap, &od);
571 
572   return od.lower_dir;
573 }
574 
575 void
gconf_client_add_dir(GConfClient * client,const gchar * dirname,GConfClientPreloadType preload,GError ** err)576 gconf_client_add_dir     (GConfClient* client,
577                           const gchar* dirname,
578                           GConfClientPreloadType preload,
579                           GError** err)
580 {
581   Dir* d;
582   guint notify_id = 0;
583   GError* error = NULL;
584 
585   g_return_if_fail (gconf_valid_key (dirname, NULL));
586 
587   trace ("Adding directory '%s'", dirname);
588 
589   d = g_hash_table_lookup (client->dir_hash, dirname);
590 
591   if (d == NULL)
592     {
593       Dir *overlap_dir;
594 
595       overlap_dir = setup_overlaps (client, dirname);
596 
597       /* only if there is no directory that includes us
598        * already add a notify
599        */
600       if (overlap_dir == NULL)
601         {
602           trace ("REMOTE: Adding notify to engine at '%s'",
603                  dirname);
604           PUSH_USE_ENGINE (client);
605           notify_id = gconf_engine_notify_add (client->engine,
606                                                dirname,
607                                                notify_from_server_callback,
608                                                client,
609                                                &error);
610           POP_USE_ENGINE (client);
611 
612           /* We got a notify ID or we got an error, not both */
613           g_return_if_fail ( (notify_id != 0 && error == NULL) ||
614                              (notify_id == 0 && error != NULL) );
615 
616 
617           if (handle_error (client, error, err))
618             return;
619 
620           g_assert (error == NULL);
621         }
622       else
623         {
624           notify_id = 0;
625         }
626 
627       d = dir_new (dirname, notify_id);
628 
629       g_hash_table_insert (client->dir_hash, d->name, d);
630 
631       gconf_client_preload (client, dirname, preload, &error);
632 
633       handle_error (client, error, err);
634     }
635 
636   g_assert (d != NULL);
637 
638   d->add_count += 1;
639 }
640 
641 typedef struct {
642   GConfClient *client;
643   GError *error;
644 } AddNotifiesData;
645 
646 static void
foreach_add_notifies(gpointer key,gpointer value,gpointer user_data)647 foreach_add_notifies(gpointer key, gpointer value, gpointer user_data)
648 {
649   AddNotifiesData *ad = user_data;
650   GConfClient *client;
651   Dir *dir = value;
652 
653   client = ad->client;
654 
655   if (ad->error != NULL)
656     return;
657 
658   if (dir->notify_id == 0)
659     {
660       Dir *overlap_dir;
661       overlap_dir = setup_overlaps(client, dir->name);
662 
663       /* only if there is no directory that includes us
664        * already add a notify */
665       if (overlap_dir == NULL)
666         {
667           trace ("REMOTE: Adding notify to engine at '%s'",
668                  dir->name);
669           PUSH_USE_ENGINE (client);
670           dir->notify_id = gconf_engine_notify_add(client->engine,
671                                                    dir->name,
672                                                    notify_from_server_callback,
673                                                    client,
674                                                    &ad->error);
675           POP_USE_ENGINE (client);
676 
677           /* We got a notify ID or we got an error, not both */
678           g_return_if_fail( (dir->notify_id != 0 && ad->error == NULL) ||
679                             (dir->notify_id == 0 && ad->error != NULL) );
680 
681           /* if error is returned, then we'll just ignore
682            * things until the end */
683         }
684     }
685 }
686 
687 static gboolean
clear_dir_cache_foreach(char * key,GConfEntry * entry,char * dir)688 clear_dir_cache_foreach (char* key, GConfEntry* entry, char *dir)
689 {
690   if (gconf_key_is_below (dir, key))
691     {
692       gconf_entry_free (entry);
693       return TRUE;
694     }
695   else
696     return FALSE;
697 }
698 
699 static gboolean
clear_cache_dirs_foreach(char * key,gpointer value,char * dir)700 clear_cache_dirs_foreach (char *key, gpointer value, char *dir)
701 {
702   if (strcmp (dir, key) == 0 ||
703       gconf_key_is_below (dir, key))
704     {
705       trace ("'%s' no longer fully cached", dir);
706       return TRUE;
707     }
708 
709   return FALSE;
710 }
711 
712 static void
gconf_client_real_remove_dir(GConfClient * client,Dir * d,GError ** err)713 gconf_client_real_remove_dir    (GConfClient* client,
714                                  Dir* d,
715                                  GError** err)
716 {
717   AddNotifiesData ad;
718 
719   g_return_if_fail(d != NULL);
720   g_return_if_fail(d->add_count == 0);
721 
722   g_hash_table_remove(client->dir_hash, d->name);
723 
724   /* remove notify for this dir */
725 
726   if (d->notify_id != 0)
727     {
728       trace ("REMOTE: Removing notify from engine at '%s'", d->name);
729       PUSH_USE_ENGINE (client);
730       gconf_engine_notify_remove (client->engine, d->notify_id);
731       POP_USE_ENGINE (client);
732       d->notify_id = 0;
733     }
734 
735   g_hash_table_foreach_remove (client->cache_hash,
736                                (GHRFunc)clear_dir_cache_foreach,
737                                d->name);
738   g_hash_table_foreach_remove (client->cache_dirs,
739                                (GHRFunc)clear_cache_dirs_foreach,
740                                d->name);
741   dir_destroy(d);
742 
743   ad.client = client;
744   ad.error = NULL;
745 
746   g_hash_table_foreach(client->dir_hash, foreach_add_notifies, &ad);
747 
748   handle_error(client, ad.error, err);
749 }
750 
751 void
gconf_client_remove_dir(GConfClient * client,const gchar * dirname,GError ** err)752 gconf_client_remove_dir  (GConfClient* client,
753                           const gchar* dirname,
754                           GError** err)
755 {
756   Dir* found = NULL;
757 
758   trace ("Removing directory '%s'", dirname);
759 
760   found = g_hash_table_lookup (client->dir_hash,
761                                dirname);
762 
763   if (found != NULL)
764     {
765       g_return_if_fail(found->add_count > 0);
766 
767       found->add_count -= 1;
768 
769       if (found->add_count == 0)
770         gconf_client_real_remove_dir (client, found, err);
771     }
772 #ifndef G_DISABLE_CHECKS
773   else
774     g_warning("Directory `%s' was not being monitored by GConfClient %p",
775               dirname, client);
776 #endif
777 }
778 
779 /**
780  * gconf_client_notify_add:
781  * @client:
782  * @namespace_section
783  * @func: (scope notified) (closure user_data) (destroy destroy_notify):
784  * @user_data:
785  * @destroy_notify:
786  * @err:
787  */
788 guint
gconf_client_notify_add(GConfClient * client,const gchar * namespace_section,GConfClientNotifyFunc func,gpointer user_data,GFreeFunc destroy_notify,GError ** err)789 gconf_client_notify_add (GConfClient* client,
790                          const gchar* namespace_section,
791                          GConfClientNotifyFunc func,
792                          gpointer user_data,
793                          GFreeFunc destroy_notify,
794                          GError** err)
795 {
796   guint cnxn_id = 0;
797 
798   g_return_val_if_fail(client != NULL, 0);
799   g_return_val_if_fail(GCONF_IS_CLIENT(client), 0);
800 
801   if (client->listeners == NULL)
802     client->listeners = gconf_listeners_new();
803 
804   cnxn_id = gconf_listeners_add (client->listeners,
805                                  namespace_section,
806                                  listener_new (func, destroy_notify, user_data),
807                                  (GFreeFunc)listener_destroy);
808 
809   return cnxn_id;
810 }
811 
812 void
gconf_client_notify_remove(GConfClient * client,guint cnxn)813 gconf_client_notify_remove  (GConfClient* client,
814                              guint cnxn)
815 {
816   g_return_if_fail(client != NULL);
817   g_return_if_fail(GCONF_IS_CLIENT(client));
818   g_return_if_fail(client->listeners != NULL);
819 
820   gconf_listeners_remove(client->listeners, cnxn);
821 
822   if (gconf_listeners_count(client->listeners) == 0)
823     {
824       gconf_listeners_free(client->listeners);
825       client->listeners = NULL;
826     }
827 }
828 
829 void
gconf_client_notify(GConfClient * client,const char * key)830 gconf_client_notify (GConfClient* client, const char* key)
831 {
832   GConfEntry *entry;
833 
834   g_return_if_fail (client != NULL);
835   g_return_if_fail (GCONF_IS_CLIENT(client));
836   g_return_if_fail (key != NULL);
837 
838   entry = gconf_client_get_entry (client, key, NULL, TRUE, NULL);
839   if (entry != NULL)
840     {
841       notify_one_entry (client, entry);
842       gconf_entry_unref (entry);
843     }
844 }
845 
846 void
gconf_client_set_error_handling(GConfClient * client,GConfClientErrorHandlingMode mode)847 gconf_client_set_error_handling(GConfClient* client,
848                                 GConfClientErrorHandlingMode mode)
849 {
850   g_return_if_fail(client != NULL);
851   g_return_if_fail(GCONF_IS_CLIENT(client));
852 
853   client->error_mode = mode;
854 }
855 
856 static gboolean
clear_cache_foreach(char * key,GConfEntry * entry,GConfClient * client)857 clear_cache_foreach (char* key, GConfEntry* entry, GConfClient* client)
858 {
859   gconf_entry_free (entry);
860 
861   return TRUE;
862 }
863 
864 void
gconf_client_clear_cache(GConfClient * client)865 gconf_client_clear_cache(GConfClient* client)
866 {
867   g_return_if_fail(client != NULL);
868   g_return_if_fail(GCONF_IS_CLIENT(client));
869 
870   trace ("Clearing cache");
871 
872   g_hash_table_foreach_remove (client->cache_hash, (GHRFunc)clear_cache_foreach,
873                                client);
874 
875   g_hash_table_remove_all (client->cache_dirs);
876 }
877 
878 static void
879 cache_pairs_in_dir(GConfClient* client, const gchar* path, gboolean recursive);
880 
881 static void
recurse_subdir_list(GConfClient * client,GSList * subdirs)882 recurse_subdir_list(GConfClient* client, GSList* subdirs)
883 {
884   GSList* tmp;
885 
886   tmp = subdirs;
887 
888   while (tmp != NULL)
889     {
890       gchar* s = tmp->data;
891 
892       cache_pairs_in_dir(client, s, TRUE);
893 
894       trace ("REMOTE: All dirs at '%s'", s);
895       PUSH_USE_ENGINE (client);
896       recurse_subdir_list(client,
897                           gconf_engine_all_dirs (client->engine, s, NULL));
898       POP_USE_ENGINE (client);
899 
900       g_free(s);
901 
902       tmp = g_slist_next(tmp);
903     }
904 
905   g_slist_free(subdirs);
906 }
907 
908 static gboolean
key_being_monitored(GConfClient * client,const char * key)909 key_being_monitored (GConfClient *client,
910                      const char  *key)
911 {
912   gboolean retval = FALSE;
913   char* parent = g_strdup (key);
914   char* end;
915 
916   end = parent + strlen (parent);
917 
918   while (end)
919     {
920       if (end == parent)
921         *(end + 1) = '\0'; /* special-case "/" root dir */
922       else
923         *end = '\0'; /* chop '/' off of dir */
924 
925       if (g_hash_table_lookup (client->dir_hash, parent) != NULL)
926         {
927           retval = TRUE;
928           break;
929         }
930 
931       if (end != parent)
932         end = strrchr (parent, '/');
933       else
934         end = NULL;
935     }
936 
937   g_free (parent);
938 
939   return retval;
940 }
941 
942 static void
cache_entry_list_destructively(GConfClient * client,GSList * entries)943 cache_entry_list_destructively (GConfClient *client,
944                                 GSList      *entries)
945 {
946   GSList *tmp;
947 
948   tmp = entries;
949 
950   while (tmp != NULL)
951     {
952       GConfEntry* entry = tmp->data;
953 
954       gconf_client_cache (client, TRUE, entry, FALSE);
955 
956       tmp = g_slist_next (tmp);
957     }
958 
959   g_slist_free (entries);
960 }
961 
962 static void
cache_pairs_in_dir(GConfClient * client,const gchar * dir,gboolean recursive)963 cache_pairs_in_dir(GConfClient* client, const gchar* dir, gboolean recursive)
964 {
965   GSList* pairs;
966   GError* error = NULL;
967 
968   trace ("REMOTE: Caching values in '%s'", dir);
969 
970   PUSH_USE_ENGINE (client);
971   pairs = gconf_engine_all_entries(client->engine, dir, &error);
972   POP_USE_ENGINE (client);
973 
974   if (error != NULL)
975     {
976       g_printerr (_("GConf warning: failure listing pairs in `%s': %s"),
977                   dir, error->message);
978       g_error_free(error);
979       error = NULL;
980     }
981 
982   cache_entry_list_destructively (client, pairs);
983   trace ("Mark '%s' as fully cached", dir);
984   g_hash_table_insert (client->cache_dirs, g_strdup (dir), GINT_TO_POINTER (1));
985 
986   if (recursive)
987     g_hash_table_insert (client->cache_recursive_dirs, g_strdup (dir), GINT_TO_POINTER (1));
988 }
989 
990 void
gconf_client_preload(GConfClient * client,const gchar * dirname,GConfClientPreloadType type,GError ** err)991 gconf_client_preload    (GConfClient* client,
992                          const gchar* dirname,
993                          GConfClientPreloadType type,
994                          GError** err)
995 {
996 
997   g_return_if_fail(client != NULL);
998   g_return_if_fail(GCONF_IS_CLIENT(client));
999   g_return_if_fail(dirname != NULL);
1000 
1001 #ifdef GCONF_ENABLE_DEBUG
1002   if (!key_being_monitored (client, dirname))
1003     {
1004       g_warning("Can only preload directories you've added with gconf_client_add_dir() (tried to preload '%s')",
1005                 dirname);
1006       return;
1007     }
1008 #endif
1009 
1010   switch (type)
1011     {
1012     case GCONF_CLIENT_PRELOAD_NONE:
1013       /* nothing */
1014       break;
1015 
1016     case GCONF_CLIENT_PRELOAD_ONELEVEL:
1017       {
1018         trace ("Onelevel preload of '%s'", dirname);
1019 
1020         cache_pairs_in_dir (client, dirname, FALSE);
1021       }
1022       break;
1023 
1024     case GCONF_CLIENT_PRELOAD_RECURSIVE:
1025       {
1026         GSList* subdirs;
1027 
1028         trace ("Recursive preload of '%s'", dirname);
1029 
1030 	trace ("REMOTE: All dirs at '%s'", dirname);
1031         PUSH_USE_ENGINE (client);
1032         subdirs = gconf_engine_all_dirs(client->engine, dirname, NULL);
1033         POP_USE_ENGINE (client);
1034 
1035         cache_pairs_in_dir(client, dirname, TRUE);
1036 
1037         recurse_subdir_list(client, subdirs);
1038       }
1039       break;
1040 
1041     default:
1042       g_assert_not_reached();
1043       break;
1044     }
1045 }
1046 
1047 #ifdef HAVE_DBUS
1048 
1049 /*
1050  * Add functions for manipulating the internal cache of values - under the
1051  * D-BUS environment we update the internal cache when changes happen to
1052  * ensure a consistent state
1053  */
1054 static gboolean
remove_key_from_cache_foreach(const gchar * cached_key,GConfEntry * entry,const gchar * key)1055 remove_key_from_cache_foreach (const gchar *cached_key,
1056                                GConfEntry  *entry,
1057                                const gchar *key)
1058 {
1059   if (strcmp (cached_key, key) == 0)
1060     {
1061       gconf_entry_free (entry);
1062       return TRUE;
1063     }
1064 
1065   return FALSE;
1066 }
1067 
1068 static gboolean
remove_key_from_cache_recursively_foreach(const gchar * cached_key,GConfEntry * entry,const gchar * key)1069 remove_key_from_cache_recursively_foreach (const gchar *cached_key,
1070                                            GConfEntry  *entry,
1071                                            const gchar *key)
1072 {
1073   if (gconf_key_is_below (cached_key, key) == 0 || strcmp (cached_key, key) == 0)
1074     {
1075       /* Not sure why a null entry would be in the cache, but it happens
1076        * sometimes, workaround for now to avoid crashing.
1077        */
1078       if (entry)
1079         gconf_entry_free (entry);
1080       return TRUE;
1081     }
1082 
1083   return FALSE;
1084 }
1085 
1086 static gboolean
remove_dir_from_cache_foreach(char * cached_dir,gpointer value,char * dir)1087 remove_dir_from_cache_foreach (char     *cached_dir,
1088                                gpointer  value,
1089                                char     *dir)
1090 {
1091   if (strcmp (dir, cached_dir) == 0)
1092     {
1093       trace ("'%s' no longer fully cached", dir);
1094       return TRUE;
1095     }
1096 
1097   return FALSE;
1098 }
1099 
1100 /* The dbus version cleans the cache after modifying a value. So we will
1101  * remove current dir (where the key is in) from the cache_dirs
1102  * when removing the key from the cache_hash.
1103  * This is a workaround. It will degrade the performance of querying negative keys in the same dir.
1104  */
remove_dir_from_cache(GConfClient * client,const gchar * key)1105 static void remove_dir_from_cache (GConfClient *client,
1106                                    const gchar *key)
1107 {
1108   char *dir, *last_slash;
1109   dir = g_strdup (key);
1110   last_slash = strrchr (dir, '/');
1111   g_assert (last_slash != NULL);
1112   *last_slash = 0;
1113   trace ("Remove dir '%s' from cache since one of keys is changed", dir);
1114   g_hash_table_foreach_remove (client->cache_dirs,
1115                                (GHRFunc) remove_dir_from_cache_foreach,
1116                                dir);
1117   g_free (dir);
1118 }
1119 
1120 static void
remove_key_from_cache(GConfClient * client,const gchar * key)1121 remove_key_from_cache (GConfClient *client,
1122                        const gchar *key)
1123 {
1124   g_hash_table_foreach_remove (client->cache_hash,
1125                                (GHRFunc) remove_key_from_cache_foreach,
1126                                (char *) key);
1127   remove_dir_from_cache (client, key);
1128 }
1129 
1130 static void
remove_key_from_cache_recursively(GConfClient * client,const gchar * key)1131 remove_key_from_cache_recursively (GConfClient *client,
1132                                    const gchar *key)
1133 {
1134   g_hash_table_foreach_remove (client->cache_hash,
1135                                (GHRFunc) remove_key_from_cache_recursively_foreach,
1136                                (char *) key);
1137   remove_dir_from_cache (client, key);
1138 }
1139 
1140 static gboolean
cache_key_value(GConfClient * client,const gchar * key,const GConfValue * val)1141 cache_key_value (GConfClient      *client,
1142                  const gchar      *key,
1143                  const GConfValue *val)
1144 {
1145   GConfEntry *entry;
1146 
1147   entry = gconf_entry_new (key, val);
1148   return gconf_client_cache (client, TRUE, entry, TRUE);
1149 }
1150 
1151 static void
cache_key_value_and_notify(GConfClient * client,const gchar * key,GConfValue * val,gboolean free_value)1152 cache_key_value_and_notify (GConfClient *client,
1153                             const gchar *key,
1154                             GConfValue  *val,
1155                             gboolean     free_value)
1156 {
1157   if (cache_key_value (client, key, val))
1158     {
1159       if (key_being_monitored (client, key))
1160         gconf_client_queue_notify (client, key);
1161     }
1162 
1163   if (free_value)
1164     gconf_value_free (val);
1165 }
1166 
1167 #endif
1168 
1169 /*
1170  * Basic key-manipulation facilities
1171  */
1172 
1173 void
gconf_client_set(GConfClient * client,const gchar * key,const GConfValue * val,GError ** err)1174 gconf_client_set             (GConfClient* client,
1175                               const gchar* key,
1176                               const GConfValue* val,
1177                               GError** err)
1178 {
1179   GError* error = NULL;
1180 
1181   trace ("REMOTE: Setting value of '%s'", key);
1182   PUSH_USE_ENGINE (client);
1183   gconf_engine_set (client->engine, key, val, &error);
1184   POP_USE_ENGINE (client);
1185 
1186 #ifdef HAVE_DBUS
1187   if (!error)
1188     cache_key_value_and_notify (client, key, (GConfValue *) val, FALSE);
1189 #endif
1190 
1191   handle_error(client, error, err);
1192 }
1193 
1194 gboolean
gconf_client_unset(GConfClient * client,const gchar * key,GError ** err)1195 gconf_client_unset          (GConfClient* client,
1196                              const gchar* key, GError** err)
1197 {
1198   GError* error = NULL;
1199 
1200   trace ("REMOTE: Unsetting '%s'", key);
1201   PUSH_USE_ENGINE (client);
1202   gconf_engine_unset(client->engine, key, &error);
1203   POP_USE_ENGINE (client);
1204 
1205 #ifdef HAVE_DBUS
1206   if (!error)
1207     remove_key_from_cache (client, key);
1208 #endif
1209 
1210   handle_error(client, error, err);
1211 
1212   if (error != NULL)
1213     return FALSE;
1214   else
1215     return TRUE;
1216 }
1217 
1218 gboolean
gconf_client_recursive_unset(GConfClient * client,const char * key,GConfUnsetFlags flags,GError ** err)1219 gconf_client_recursive_unset (GConfClient *client,
1220                               const char     *key,
1221                               GConfUnsetFlags flags,
1222                               GError        **err)
1223 {
1224   GError* error = NULL;
1225 
1226   trace ("REMOTE: Recursive unsetting '%s'", key);
1227 
1228   PUSH_USE_ENGINE (client);
1229   gconf_engine_recursive_unset(client->engine, key, flags, &error);
1230   POP_USE_ENGINE (client);
1231 
1232 #ifdef HAVE_DBUS
1233   if (!error)
1234     remove_key_from_cache_recursively (client, key);
1235 #endif
1236 
1237   handle_error(client, error, err);
1238 
1239   if (error != NULL)
1240     return FALSE;
1241   else
1242     return TRUE;
1243 }
1244 
1245 static GSList*
copy_entry_list(GSList * list)1246 copy_entry_list (GSList *list)
1247 {
1248   GSList *copy;
1249   GSList *tmp;
1250 
1251   copy = NULL;
1252   tmp = list;
1253   while (tmp != NULL)
1254     {
1255       copy = g_slist_prepend (copy,
1256                               gconf_entry_copy (tmp->data));
1257       tmp = tmp->next;
1258     }
1259 
1260   copy = g_slist_reverse (copy);
1261 
1262   return copy;
1263 }
1264 
1265 /**
1266  * gconf_client_all_entries:
1267  * @client: a #GConfClient.
1268  * @dir: directory to list.
1269  * @err: the return location for an allocated #GError, or <symbol>NULL</symbol> to ignore errors.
1270  *
1271  * Lists the key-value pairs in @dir. Does not list subdirectories; for
1272  * that use gconf_client_all_dirs(). The returned list contains #GConfEntry
1273  * objects. A #GConfEntry contains an <emphasis>absolute</emphasis> key
1274  * and a value. The list is not recursive, it contains only the immediate
1275  * children of @dir.  To free the returned list, gconf_entry_free()
1276  * each list element, then g_slist_free() the list itself.
1277  * Just like gconf_engine_all_entries (), but uses #GConfClient caching and error-handling features.
1278  *
1279  * Return value: (element-type GConfEntry) (transfer full): List of #GConfEntry.
1280  */
1281 GSList*
gconf_client_all_entries(GConfClient * client,const gchar * dir,GError ** err)1282 gconf_client_all_entries    (GConfClient* client,
1283                              const gchar* dir,
1284                              GError** err)
1285 {
1286   GError *error = NULL;
1287   GSList *retval;
1288   int dirlen;
1289 
1290   if (g_hash_table_lookup (client->cache_dirs, dir))
1291     {
1292       GHashTableIter iter;
1293       gpointer key, value;
1294 
1295       trace ("CACHED: Getting all values in '%s'", dir);
1296 
1297       dirlen = strlen (dir);
1298       retval = NULL;
1299       g_hash_table_iter_init (&iter, client->cache_hash);
1300       while (g_hash_table_iter_next (&iter, &key, &value))
1301         {
1302           const gchar *id = key;
1303           GConfEntry *entry = value;
1304           if (g_str_has_prefix (id, dir) &&
1305               id + dirlen == strrchr (id, '/'))
1306             retval = g_slist_prepend (retval, gconf_entry_copy (entry));
1307         }
1308 
1309       return retval;
1310     }
1311 
1312   trace ("REMOTE: Getting all values in '%s'", dir);
1313 
1314   PUSH_USE_ENGINE (client);
1315   retval = gconf_engine_all_entries (client->engine, dir, &error);
1316   POP_USE_ENGINE (client);
1317 
1318   handle_error (client, error, err);
1319 
1320   if (error != NULL)
1321     return NULL;
1322 
1323   if (key_being_monitored (client, dir))
1324     {
1325       cache_entry_list_destructively (client, copy_entry_list (retval));
1326       trace ("Mark '%s' as fully cached", dir);
1327       g_hash_table_insert (client->cache_dirs, g_strdup (dir), GINT_TO_POINTER (1));
1328     }
1329 
1330   return retval;
1331 }
1332 
1333 /**
1334  * gconf_client_all_dirs:
1335  * @client: a #GConfClient.
1336  * @dir: directory to get subdirectories from.
1337  * @err: the return location for an allocated #GError, or <symbol>NULL</symbol> to ignore errors.
1338  *
1339  * Lists the subdirectories in @dir. The returned list contains
1340  * allocated strings. Each string is the absolute path of a
1341  * subdirectory. You should g_free() each string in the list, then
1342  * g_slist_free() the list itself.  Just like gconf_engine_all_dirs(),
1343  * but uses #GConfClient caching and error-handling features.
1344  *
1345  * Return value: (element-type utf8) (transfer full): List of allocated subdirectory names.
1346  */
1347 GSList*
gconf_client_all_dirs(GConfClient * client,const gchar * dir,GError ** err)1348 gconf_client_all_dirs       (GConfClient* client,
1349                              const gchar* dir, GError** err)
1350 {
1351   GError* error = NULL;
1352   GSList* retval;
1353 
1354   trace ("REMOTE: Getting all dirs in '%s'", dir);
1355 
1356   PUSH_USE_ENGINE (client);
1357   retval = gconf_engine_all_dirs(client->engine, dir, &error);
1358   POP_USE_ENGINE (client);
1359 
1360   handle_error(client, error, err);
1361 
1362   return retval;
1363 }
1364 
1365 void
gconf_client_suggest_sync(GConfClient * client,GError ** err)1366 gconf_client_suggest_sync   (GConfClient* client,
1367                              GError** err)
1368 {
1369   GError* error = NULL;
1370 
1371   trace ("REMOTE: Suggesting sync");
1372 
1373   PUSH_USE_ENGINE (client);
1374   gconf_engine_suggest_sync(client->engine, &error);
1375   POP_USE_ENGINE (client);
1376 
1377   handle_error(client, error, err);
1378 }
1379 
1380 gboolean
gconf_client_dir_exists(GConfClient * client,const gchar * dir,GError ** err)1381 gconf_client_dir_exists(GConfClient* client,
1382                         const gchar* dir, GError** err)
1383 {
1384   GError* error = NULL;
1385   gboolean retval;
1386 
1387   trace ("REMOTE: Checking whether directory '%s' exists...", dir);
1388 
1389   PUSH_USE_ENGINE (client);
1390   retval = gconf_engine_dir_exists (client->engine, dir, &error);
1391   POP_USE_ENGINE (client);
1392 
1393   handle_error (client, error, err);
1394 
1395   if (retval)
1396     trace ("'%s' exists", dir);
1397   else
1398     trace ("'%s' doesn't exist", dir);
1399 
1400   return retval;
1401 }
1402 
1403 gboolean
gconf_client_key_is_writable(GConfClient * client,const gchar * key,GError ** err)1404 gconf_client_key_is_writable (GConfClient* client,
1405                               const gchar* key,
1406                               GError**     err)
1407 {
1408   GError* error = NULL;
1409   GConfEntry *entry = NULL;
1410   gboolean is_writable;
1411 
1412   g_return_val_if_fail (key != NULL, FALSE);
1413   g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
1414 
1415   if (gconf_client_lookup (client, key, &entry))
1416     {
1417       if (!entry)
1418         return FALSE;
1419 
1420       trace ("CACHED: Checking whether key '%s' is writable", key);
1421       return gconf_entry_get_is_writable (entry);
1422     }
1423 
1424   trace ("REMOTE: Checking whether key '%s' is writable", key);
1425 
1426   entry = get (client, key, TRUE, &error);
1427 
1428   if (entry == NULL && error != NULL)
1429     handle_error (client, error, err);
1430   else
1431     g_assert (error == NULL);
1432 
1433   if (entry == NULL)
1434     is_writable = FALSE;
1435   else
1436     is_writable = gconf_entry_get_is_writable (entry);
1437 
1438   if (entry)
1439     gconf_entry_free (entry);
1440 
1441   return is_writable;
1442 }
1443 
1444 static gboolean
check_type(const gchar * key,GConfValue * val,GConfValueType t,GError ** err)1445 check_type(const gchar* key, GConfValue* val, GConfValueType t, GError** err)
1446 {
1447   if (val->type != t)
1448     {
1449       gconf_set_error(err, GCONF_ERROR_TYPE_MISMATCH,
1450                       _("Expected `%s' got `%s' for key %s"),
1451                       gconf_value_type_to_string(t),
1452                       gconf_value_type_to_string(val->type),
1453                       key);
1454       return FALSE;
1455     }
1456   else
1457     return TRUE;
1458 }
1459 
1460 static GConfEntry*
get(GConfClient * client,const gchar * key,gboolean use_default,GError ** error)1461 get (GConfClient *client,
1462      const gchar *key,
1463      gboolean     use_default,
1464      GError     **error)
1465 {
1466   GConfEntry *entry = NULL;
1467 
1468   g_return_val_if_fail (client != NULL, NULL);
1469   g_return_val_if_fail (GCONF_IS_CLIENT(client), NULL);
1470   g_return_val_if_fail (error != NULL, NULL);
1471   g_return_val_if_fail (*error == NULL, NULL);
1472 
1473   /* Check our client-side cache */
1474   if (gconf_client_lookup (client, key, &entry))
1475 
1476     {
1477       trace ("CACHED: Query for '%s'", key);
1478 
1479       if (entry == NULL)
1480         return NULL;
1481 
1482       if (gconf_entry_get_is_default (entry) && !use_default)
1483         return NULL;
1484       else
1485         return gconf_entry_copy (entry);
1486     }
1487 
1488   g_assert (entry == NULL); /* if it was in the cache we should have returned */
1489 
1490   /* Check the GConfEngine */
1491   trace ("REMOTE: Query for '%s'", key);
1492   PUSH_USE_ENGINE (client);
1493   entry = gconf_engine_get_entry (client->engine, key,
1494                                   gconf_current_locale(),
1495                                   TRUE /* always use default here */,
1496                                   error);
1497   POP_USE_ENGINE (client);
1498 
1499   if (*error != NULL)
1500     {
1501       g_return_val_if_fail (entry == NULL, NULL);
1502       return NULL;
1503     }
1504   else
1505     {
1506       g_assert (entry != NULL); /* gconf_engine_get_entry shouldn't return NULL ever */
1507 
1508       /* Cache this value, if it's in our directory list. */
1509       if (key_being_monitored (client, key))
1510         {
1511           /* cache a copy of val */
1512           gconf_client_cache (client, FALSE, entry, FALSE);
1513         }
1514 
1515       /* We don't own the entry, we're returning this copy belonging
1516        * to the caller
1517        */
1518       if (gconf_entry_get_is_default (entry) && !use_default)
1519         {
1520           gconf_entry_free (entry);
1521           return NULL;
1522         }
1523       else
1524         return entry;
1525     }
1526 }
1527 
1528 static GConfValue*
gconf_client_get_full(GConfClient * client,const gchar * key,const gchar * locale,gboolean use_schema_default,GError ** err)1529 gconf_client_get_full        (GConfClient* client,
1530                               const gchar* key, const gchar* locale,
1531                               gboolean use_schema_default,
1532                               GError** err)
1533 {
1534   GError* error = NULL;
1535   GConfEntry *entry;
1536   GConfValue *retval;
1537 
1538   g_return_val_if_fail (err == NULL || *err == NULL, NULL);
1539 
1540   if (locale != NULL)
1541     g_warning ("haven't implemented getting a specific locale in GConfClient");
1542 
1543   entry = get (client, key, use_schema_default,
1544                &error);
1545 
1546   if (entry == NULL && error != NULL)
1547     handle_error(client, error, err);
1548   else
1549     g_assert (error == NULL);
1550 
1551   retval = NULL;
1552 
1553   if (entry && gconf_entry_get_value (entry))
1554     retval = gconf_value_copy (gconf_entry_get_value (entry));
1555 
1556   if (entry != NULL)
1557     gconf_entry_free (entry);
1558 
1559   return retval;
1560 }
1561 
1562 GConfEntry*
gconf_client_get_entry(GConfClient * client,const gchar * key,const gchar * locale,gboolean use_schema_default,GError ** err)1563 gconf_client_get_entry (GConfClient* client,
1564                         const gchar* key,
1565                         const gchar* locale,
1566                         gboolean use_schema_default,
1567                         GError** err)
1568 {
1569   GError* error = NULL;
1570   GConfEntry *entry;
1571 
1572   g_return_val_if_fail(err == NULL || *err == NULL, NULL);
1573 
1574   if (locale != NULL)
1575     g_warning("haven't implemented getting a specific locale in GConfClient");
1576 
1577   entry = get (client, key, use_schema_default,
1578                &error);
1579 
1580   if (entry == NULL && error != NULL)
1581     handle_error (client, error, err);
1582   else
1583     g_assert (error == NULL);
1584 
1585   return entry;
1586 }
1587 
1588 GConfValue*
gconf_client_get(GConfClient * client,const gchar * key,GError ** err)1589 gconf_client_get             (GConfClient* client,
1590                               const gchar* key,
1591                               GError** err)
1592 {
1593   g_return_val_if_fail (GCONF_IS_CLIENT (client), NULL);
1594   g_return_val_if_fail (key != NULL, NULL);
1595   return gconf_client_get_full (client, key, NULL, TRUE, err);
1596 }
1597 
1598 GConfValue*
gconf_client_get_without_default(GConfClient * client,const gchar * key,GError ** err)1599 gconf_client_get_without_default  (GConfClient* client,
1600                                    const gchar* key,
1601                                    GError** err)
1602 {
1603   g_return_val_if_fail (GCONF_IS_CLIENT (client), NULL);
1604   g_return_val_if_fail (key != NULL, NULL);
1605   return gconf_client_get_full (client, key, NULL, FALSE, err);
1606 }
1607 
1608 GConfValue*
gconf_client_get_default_from_schema(GConfClient * client,const gchar * key,GError ** err)1609 gconf_client_get_default_from_schema (GConfClient* client,
1610                                       const gchar* key,
1611                                       GError** err)
1612 {
1613   GError* error = NULL;
1614   GConfEntry *entry = NULL;
1615   GConfValue *val = NULL;
1616 
1617   g_return_val_if_fail (err == NULL || *err == NULL, NULL);
1618   g_return_val_if_fail (client != NULL, NULL);
1619   g_return_val_if_fail (GCONF_IS_CLIENT(client), NULL);
1620   g_return_val_if_fail (key != NULL, NULL);
1621 
1622   /* Check our client-side cache to see if the default is the same as
1623    * the regular value (FIXME put a default_value field in the
1624    * cache and store both, lose the is_default flag)
1625    */
1626   if (gconf_client_lookup (client, key, &entry))
1627     {
1628       if (!entry)
1629         return NULL;
1630 
1631       if (gconf_entry_get_is_default (entry))
1632         {
1633 	  trace ("CACHED: Getting schema default for '%s'", key);
1634 
1635           return gconf_entry_get_value (entry) ?
1636             gconf_value_copy (gconf_entry_get_value (entry)) :
1637             NULL;
1638         }
1639     }
1640 
1641   /* Check the GConfEngine */
1642   trace ("REMOTE: Getting schema default for '%s'", key);
1643   PUSH_USE_ENGINE (client);
1644   val = gconf_engine_get_default_from_schema (client->engine, key,
1645                                               &error);
1646   POP_USE_ENGINE (client);
1647 
1648   if (error != NULL)
1649     {
1650       g_assert (val == NULL);
1651       handle_error (client, error, err);
1652       return NULL;
1653     }
1654   else
1655     {
1656       /* FIXME eventually we'll cache the value
1657        * by adding a field to the cache
1658        */
1659       return val;
1660     }
1661 }
1662 
1663 gdouble
gconf_client_get_float(GConfClient * client,const gchar * key,GError ** err)1664 gconf_client_get_float (GConfClient* client, const gchar* key,
1665                         GError** err)
1666 {
1667   static const gdouble def = 0.0;
1668   GError* error = NULL;
1669   GConfValue *val;
1670 
1671   g_return_val_if_fail (err == NULL || *err == NULL, 0.0);
1672 
1673   val = gconf_client_get (client, key, &error);
1674 
1675   if (val != NULL)
1676     {
1677       gdouble retval = def;
1678 
1679       g_assert(error == NULL);
1680 
1681       if (check_type (key, val, GCONF_VALUE_FLOAT, &error))
1682         retval = gconf_value_get_float (val);
1683       else
1684         handle_error (client, error, err);
1685 
1686       gconf_value_free (val);
1687 
1688       return retval;
1689     }
1690   else
1691     {
1692       if (error != NULL)
1693         handle_error (client, error, err);
1694       return def;
1695     }
1696 }
1697 
1698 gint
gconf_client_get_int(GConfClient * client,const gchar * key,GError ** err)1699 gconf_client_get_int   (GConfClient* client, const gchar* key,
1700                         GError** err)
1701 {
1702   static const gint def = 0;
1703   GError* error = NULL;
1704   GConfValue* val;
1705 
1706   g_return_val_if_fail (err == NULL || *err == NULL, 0);
1707 
1708   val = gconf_client_get (client, key, &error);
1709 
1710   if (val != NULL)
1711     {
1712       gint retval = def;
1713 
1714       g_assert(error == NULL);
1715 
1716       if (check_type (key, val, GCONF_VALUE_INT, &error))
1717         retval = gconf_value_get_int(val);
1718       else
1719         handle_error (client, error, err);
1720 
1721       gconf_value_free (val);
1722 
1723       return retval;
1724     }
1725   else
1726     {
1727       if (error != NULL)
1728         handle_error (client, error, err);
1729       return def;
1730     }
1731 }
1732 
1733 gchar*
gconf_client_get_string(GConfClient * client,const gchar * key,GError ** err)1734 gconf_client_get_string(GConfClient* client, const gchar* key,
1735                         GError** err)
1736 {
1737   GError* error = NULL;
1738   GConfValue* val;
1739 
1740   g_return_val_if_fail (err == NULL || *err == NULL, NULL);
1741 
1742   val = gconf_client_get (client, key, &error);
1743 
1744   if (val != NULL)
1745     {
1746       gchar* retval = NULL;
1747 
1748       g_assert(error == NULL);
1749 
1750       if (check_type (key, val, GCONF_VALUE_STRING, &error))
1751         retval = gconf_value_steal_string (val);
1752       else
1753         handle_error (client, error, err);
1754 
1755       gconf_value_free (val);
1756 
1757       return retval;
1758     }
1759   else
1760     {
1761       if (error != NULL)
1762         handle_error (client, error, err);
1763       return NULL;
1764     }
1765 }
1766 
1767 
1768 gboolean
gconf_client_get_bool(GConfClient * client,const gchar * key,GError ** err)1769 gconf_client_get_bool  (GConfClient* client, const gchar* key,
1770                         GError** err)
1771 {
1772   static const gboolean def = FALSE;
1773   GError* error = NULL;
1774   GConfValue* val;
1775 
1776   g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
1777 
1778   val = gconf_client_get (client, key, &error);
1779 
1780   if (val != NULL)
1781     {
1782       gboolean retval = def;
1783 
1784       g_assert (error == NULL);
1785 
1786       if (check_type (key, val, GCONF_VALUE_BOOL, &error))
1787         retval = gconf_value_get_bool (val);
1788       else
1789         handle_error (client, error, err);
1790 
1791       gconf_value_free (val);
1792 
1793       return retval;
1794     }
1795   else
1796     {
1797       if (error != NULL)
1798         handle_error (client, error, err);
1799       return def;
1800     }
1801 }
1802 
1803 GConfSchema*
gconf_client_get_schema(GConfClient * client,const gchar * key,GError ** err)1804 gconf_client_get_schema  (GConfClient* client,
1805                           const gchar* key, GError** err)
1806 {
1807   GError* error = NULL;
1808   GConfValue* val;
1809 
1810   g_return_val_if_fail (err == NULL || *err == NULL, NULL);
1811 
1812   val = gconf_client_get (client, key, &error);
1813 
1814   if (val != NULL)
1815     {
1816       GConfSchema* retval = NULL;
1817 
1818       g_assert(error == NULL);
1819 
1820       if (check_type (key, val, GCONF_VALUE_SCHEMA, &error))
1821         retval = gconf_value_steal_schema (val);
1822       else
1823         handle_error (client, error, err);
1824 
1825       gconf_value_free (val);
1826 
1827       return retval;
1828     }
1829   else
1830     {
1831       if (error != NULL)
1832         handle_error (client, error, err);
1833       return NULL;
1834     }
1835 }
1836 
1837 /**
1838  * gconf_client_get_list: (skip)
1839  * @client: a #GConfClient.
1840  * @key: key you want the value of.
1841  * @list_type: type of each list element.
1842  * @err: the return location for an allocated #GError, or <symbol>NULL</symbol> to ignore errors.
1843  *
1844  * Requests the list (%GCONF_VALUE_LIST) stored at @key.  Automatically
1845  * performs type-checking, so if a non-list is stored at @key, or the
1846  * list does not contain elements of type @list_type, an error is
1847  * returned. If no value is set or an error occurs, <symbol>NULL</symbol>
1848  * is returned. Note that <symbol>NULL</symbol> is also the empty list,
1849  * so if you need to distinguish the empty list from an unset value, you
1850  * must use gconf_client_get () to obtain a raw #GConfValue.
1851  *
1852  * <emphasis>Remember that GConf lists can only store primitive types:
1853  * %GCONF_VALUE_FLOAT, %GCONF_VALUE_INT, %GCONF_VALUE_BOOL,
1854  * %GCONF_VALUE_STRING, %GCONF_VALUE_SCHEMA.</emphasis> Also remember
1855  * that lists must be uniform, you may not mix types in the same list.
1856  *
1857  * The type of the list elements depends on @list_type. A #GConfValue
1858  * with type %GCONF_VALUE_LIST normally stores a list of more #GConfValue
1859  * objects. gconf_client_get_list() automatically converts to primitive C
1860  * types. Thus, the list-&gt;data fields in the returned list
1861  * contain:
1862  *
1863  * <informaltable pgwide="1" frame="none">
1864  * <tgroup cols="2"><colspec colwidth="2*"/><colspec colwidth="8*"/>
1865  * <tbody>
1866  *
1867  * <row>
1868  * <entry>%GCONF_VALUE_INT</entry>
1869  * <entry>The integer itself, converted with GINT_TO_POINTER()</entry>
1870  * </row>
1871  *
1872  * <row>
1873  * <entry>%GCONF_VALUE_BOOL</entry>
1874  * <entry>The bool itself, converted with GINT_TO_POINTER()</entry>
1875  * </row>
1876  *
1877  * <row>
1878  * <entry>%GCONF_VALUE_FLOAT</entry>
1879  * <entry>A pointer to #gdouble, which should be freed with g_free()</entry>
1880  * </row>
1881  *
1882  * <row>
1883  * <entry>%GCONF_VALUE_STRING</entry>
1884  * <entry>A pointer to #gchar, which should be freed with g_free()</entry>
1885  * </row>
1886  *
1887  * <row>
1888  * <entry>%GCONF_VALUE_SCHEMA</entry>
1889  * <entry>A pointer to #GConfSchema, which should be freed with gconf_schema_free()</entry>
1890  * </row>
1891  *
1892  * </tbody></tgroup></informaltable>
1893  *
1894  * In the %GCONF_VALUE_FLOAT and %GCONF_VALUE_STRING cases, you must
1895  * g_free() each list element. In the %GCONF_VALUE_SCHEMA case you must
1896  * gconf_schema_free() each element. In all cases you must free the
1897  * list itself with g_slist_free().
1898  *
1899  * Just like gconf_engine_get_list (), but uses #GConfClient caching and error-handling features.
1900  *
1901 * Return value: an allocated list, with elements as described above.
1902 */
1903 GSList*
gconf_client_get_list(GConfClient * client,const gchar * key,GConfValueType list_type,GError ** err)1904 gconf_client_get_list    (GConfClient* client, const gchar* key,
1905                           GConfValueType list_type, GError** err)
1906 {
1907   GError* error = NULL;
1908   GConfValue* val;
1909 
1910   g_return_val_if_fail (err == NULL || *err == NULL, NULL);
1911 
1912   val = gconf_client_get (client, key, &error);
1913 
1914   if (val != NULL)
1915     {
1916       GSList* retval;
1917 
1918       g_assert (error == NULL);
1919 
1920       /* This function checks the type and destroys "val" */
1921       retval = gconf_value_list_to_primitive_list_destructive (val, list_type, &error);
1922 
1923       if (error != NULL)
1924         {
1925           g_assert (retval == NULL);
1926           handle_error (client, error, err);
1927           return NULL;
1928         }
1929       else
1930         return retval;
1931     }
1932   else
1933     {
1934       if (error != NULL)
1935         handle_error (client, error, err);
1936       return NULL;
1937     }
1938 }
1939 
1940 gboolean
gconf_client_get_pair(GConfClient * client,const gchar * key,GConfValueType car_type,GConfValueType cdr_type,gpointer car_retloc,gpointer cdr_retloc,GError ** err)1941 gconf_client_get_pair    (GConfClient* client, const gchar* key,
1942                           GConfValueType car_type, GConfValueType cdr_type,
1943                           gpointer car_retloc, gpointer cdr_retloc,
1944                           GError** err)
1945 {
1946   GError* error = NULL;
1947   GConfValue* val;
1948 
1949   g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
1950 
1951   val = gconf_client_get (client, key, &error);
1952 
1953   if (val != NULL)
1954     {
1955       g_assert(error == NULL);
1956 
1957       /* This function checks the type and destroys "val" */
1958       if (gconf_value_pair_to_primitive_pair_destructive (val, car_type, cdr_type,
1959                                                           car_retloc, cdr_retloc,
1960                                                           &error))
1961         {
1962           g_assert (error == NULL);
1963           return TRUE;
1964         }
1965       else
1966         {
1967           g_assert (error != NULL);
1968           handle_error (client, error, err);
1969           return FALSE;
1970         }
1971     }
1972   else
1973     {
1974       if (error != NULL)
1975         {
1976           handle_error (client, error, err);
1977           return FALSE;
1978         }
1979       else
1980         return TRUE;
1981     }
1982 }
1983 
1984 #ifdef HAVE_DBUS
1985 #define MAKE_VALUE(v, TYPE, type, val) GConfValue *v = gconf_value_new (GCONF_VALUE_##TYPE); gconf_value_set_##type (v, val)
1986 #endif
1987 
1988 /*
1989  * For the set functions, we just set normally, and wait for the
1990  * notification to come back from the server before we update
1991  * our cache. This may be the wrong thing; maybe we should
1992  * update immediately?
1993  * Problem with delayed update: user calls set() then get(),
1994  *  results in weirdness
1995  * Problem with with regular update: get() before the notify
1996  *  is out of sync with the listening parts of the application
1997  *
1998  */
1999 
2000 gboolean
gconf_client_set_float(GConfClient * client,const gchar * key,gdouble val,GError ** err)2001 gconf_client_set_float   (GConfClient* client, const gchar* key,
2002                           gdouble val, GError** err)
2003 {
2004   GError* error = NULL;
2005   gboolean result;
2006 
2007   g_return_val_if_fail(client != NULL, FALSE);
2008   g_return_val_if_fail(GCONF_IS_CLIENT(client), FALSE);
2009   g_return_val_if_fail(key != NULL, FALSE);
2010 
2011   trace ("REMOTE: Setting float '%s'", key);
2012   PUSH_USE_ENGINE (client);
2013   result = gconf_engine_set_float (client->engine, key, val, &error);
2014   POP_USE_ENGINE (client);
2015 
2016 #ifdef HAVE_DBUS
2017   if (result)
2018     {
2019       MAKE_VALUE (v, FLOAT, float, val);
2020       cache_key_value_and_notify (client, key, v, TRUE);
2021     }
2022 #endif
2023 
2024   if (result)
2025     return TRUE;
2026   else
2027     {
2028       handle_error(client, error, err);
2029       return FALSE;
2030     }
2031 }
2032 
2033 gboolean
gconf_client_set_int(GConfClient * client,const gchar * key,gint val,GError ** err)2034 gconf_client_set_int     (GConfClient* client, const gchar* key,
2035                           gint val, GError** err)
2036 {
2037   GError* error = NULL;
2038   gboolean result;
2039 
2040   g_return_val_if_fail(client != NULL, FALSE);
2041   g_return_val_if_fail(GCONF_IS_CLIENT(client), FALSE);
2042   g_return_val_if_fail(key != NULL, FALSE);
2043 
2044   trace ("REMOTE: Setting int '%s'", key);
2045   PUSH_USE_ENGINE (client);
2046   result = gconf_engine_set_int (client->engine, key, val, &error);
2047   POP_USE_ENGINE (client);
2048 
2049 #ifdef HAVE_DBUS
2050   if (result)
2051     {
2052       MAKE_VALUE (v, INT, int, val);
2053       cache_key_value_and_notify (client, key, v, TRUE);
2054     }
2055 #endif
2056 
2057   if (result)
2058     return TRUE;
2059   else
2060     {
2061       handle_error(client, error, err);
2062       return FALSE;
2063     }
2064 }
2065 
2066 gboolean
gconf_client_set_string(GConfClient * client,const gchar * key,const gchar * val,GError ** err)2067 gconf_client_set_string  (GConfClient* client, const gchar* key,
2068                           const gchar* val, GError** err)
2069 {
2070   GError* error = NULL;
2071   gboolean result;
2072 
2073   g_return_val_if_fail(client != NULL, FALSE);
2074   g_return_val_if_fail(GCONF_IS_CLIENT(client), FALSE);
2075   g_return_val_if_fail(key != NULL, FALSE);
2076   g_return_val_if_fail(val != NULL, FALSE);
2077 
2078   trace ("REMOTE: Setting string '%s'", key);
2079   PUSH_USE_ENGINE (client);
2080   result = gconf_engine_set_string(client->engine, key, val, &error);
2081   POP_USE_ENGINE (client);
2082 
2083 #ifdef HAVE_DBUS
2084   if (result)
2085     {
2086       MAKE_VALUE (v, STRING, string, val);
2087       cache_key_value_and_notify (client, key, v, TRUE);
2088     }
2089 #endif
2090 
2091   if (result)
2092     return TRUE;
2093   else
2094     {
2095       handle_error(client, error, err);
2096       return FALSE;
2097     }
2098 }
2099 
2100 gboolean
gconf_client_set_bool(GConfClient * client,const gchar * key,gboolean val,GError ** err)2101 gconf_client_set_bool    (GConfClient* client, const gchar* key,
2102                           gboolean val, GError** err)
2103 {
2104   GError* error = NULL;
2105   gboolean result;
2106 
2107   g_return_val_if_fail(client != NULL, FALSE);
2108   g_return_val_if_fail(GCONF_IS_CLIENT(client), FALSE);
2109   g_return_val_if_fail(key != NULL, FALSE);
2110 
2111   trace ("REMOTE: Setting bool '%s'", key);
2112   PUSH_USE_ENGINE (client);
2113   result = gconf_engine_set_bool (client->engine, key, val, &error);
2114   POP_USE_ENGINE (client);
2115 
2116 #ifdef HAVE_DBUS
2117   if (result)
2118     {
2119       MAKE_VALUE (v, BOOL, bool, val);
2120       cache_key_value_and_notify (client, key, v, TRUE);
2121     }
2122 #endif
2123 
2124   if (result)
2125     return TRUE;
2126   else
2127     {
2128       handle_error(client, error, err);
2129       return FALSE;
2130     }
2131 }
2132 
2133 gboolean
gconf_client_set_schema(GConfClient * client,const gchar * key,const GConfSchema * val,GError ** err)2134 gconf_client_set_schema  (GConfClient* client, const gchar* key,
2135                           const GConfSchema* val, GError** err)
2136 {
2137   GError* error = NULL;
2138   gboolean result;
2139 
2140   g_return_val_if_fail(client != NULL, FALSE);
2141   g_return_val_if_fail(GCONF_IS_CLIENT(client), FALSE);
2142   g_return_val_if_fail(key != NULL, FALSE);
2143   g_return_val_if_fail(val != NULL, FALSE);
2144 
2145   trace ("REMOTE: Setting schema '%s'", key);
2146   PUSH_USE_ENGINE (client);
2147   result = gconf_engine_set_schema(client->engine, key, val, &error);
2148   POP_USE_ENGINE (client);
2149 
2150 #ifdef HAVE_DBUS
2151   if (result)
2152     {
2153       MAKE_VALUE (v, SCHEMA, schema, val);
2154       cache_key_value_and_notify (client, key, v, TRUE);
2155     }
2156 #endif
2157 
2158   if (result)
2159     return TRUE;
2160   else
2161     {
2162       handle_error(client, error, err);
2163       return FALSE;
2164     }
2165 }
2166 
2167 gboolean
gconf_client_set_list(GConfClient * client,const gchar * key,GConfValueType list_type,GSList * list,GError ** err)2168 gconf_client_set_list    (GConfClient* client, const gchar* key,
2169                           GConfValueType list_type,
2170                           GSList* list,
2171                           GError** err)
2172 {
2173   GError* error = NULL;
2174   gboolean result;
2175 
2176   g_return_val_if_fail(client != NULL, FALSE);
2177   g_return_val_if_fail(GCONF_IS_CLIENT(client), FALSE);
2178   g_return_val_if_fail(key != NULL, FALSE);
2179 
2180   trace ("REMOTE: Setting list '%s'", key);
2181   PUSH_USE_ENGINE (client);
2182   result = gconf_engine_set_list(client->engine, key, list_type, list, &error);
2183   POP_USE_ENGINE (client);
2184 
2185 #ifdef HAVE_DBUS
2186   if (result)
2187     {
2188       GConfValue *v = gconf_value_list_from_primitive_list (list_type, list, err);
2189       cache_key_value_and_notify (client, key, v, TRUE);
2190     }
2191 #endif
2192 
2193   if (result)
2194     return TRUE;
2195   else
2196     {
2197       handle_error(client, error, err);
2198       return FALSE;
2199     }
2200 }
2201 
2202 gboolean
gconf_client_set_pair(GConfClient * client,const gchar * key,GConfValueType car_type,GConfValueType cdr_type,gconstpointer address_of_car,gconstpointer address_of_cdr,GError ** err)2203 gconf_client_set_pair    (GConfClient* client, const gchar* key,
2204                           GConfValueType car_type, GConfValueType cdr_type,
2205                           gconstpointer address_of_car,
2206                           gconstpointer address_of_cdr,
2207                           GError** err)
2208 {
2209   GError* error = NULL;
2210   gboolean result;
2211 
2212   g_return_val_if_fail(client != NULL, FALSE);
2213   g_return_val_if_fail(GCONF_IS_CLIENT(client), FALSE);
2214   g_return_val_if_fail(key != NULL, FALSE);
2215 
2216   trace ("REMOTE: Setting pair '%s'", key);
2217   PUSH_USE_ENGINE (client);
2218   result = gconf_engine_set_pair (client->engine, key, car_type, cdr_type,
2219                                   address_of_car, address_of_cdr, &error);
2220   POP_USE_ENGINE (client);
2221 
2222 #ifdef HAVE_DBUS
2223   if (result)
2224     {
2225       GConfValue *v = gconf_value_pair_from_primitive_pair (car_type, cdr_type, address_of_car, address_of_cdr, &error);
2226       if (!v)
2227         {
2228           handle_error (client, error, err);
2229           return FALSE;
2230         }
2231 
2232       cache_key_value_and_notify (client, key, v, TRUE);
2233     }
2234 #endif
2235 
2236   if (result)
2237     return TRUE;
2238   else
2239     {
2240       handle_error(client, error, err);
2241       return FALSE;
2242     }
2243 }
2244 
2245 
2246 /*
2247  * Functions to emit signals
2248  */
2249 
2250 void
gconf_client_error(GConfClient * client,GError * error)2251 gconf_client_error                  (GConfClient* client, GError* error)
2252 {
2253   g_return_if_fail(client != NULL);
2254   g_return_if_fail(GCONF_IS_CLIENT(client));
2255 
2256   g_signal_emit (G_OBJECT(client), client_signals[ERROR], 0,
2257                  error);
2258 }
2259 
2260 void
gconf_client_unreturned_error(GConfClient * client,GError * error)2261 gconf_client_unreturned_error       (GConfClient* client, GError* error)
2262 {
2263   g_return_if_fail(client != NULL);
2264   g_return_if_fail(GCONF_IS_CLIENT(client));
2265 
2266   g_signal_emit (G_OBJECT(client), client_signals[UNRETURNED_ERROR], 0,
2267                  error);
2268 }
2269 
2270 void
gconf_client_value_changed(GConfClient * client,const gchar * key,GConfValue * value)2271 gconf_client_value_changed          (GConfClient* client,
2272                                      const gchar* key,
2273                                      GConfValue* value)
2274 {
2275   g_return_if_fail(client != NULL);
2276   g_return_if_fail(GCONF_IS_CLIENT(client));
2277   g_return_if_fail(key != NULL);
2278 
2279   g_signal_emit(G_OBJECT(client), client_signals[VALUE_CHANGED], 0,
2280                 key, value);
2281 }
2282 
2283 /*
2284  * Internal utility
2285  */
2286 
2287 static gboolean
gconf_client_cache(GConfClient * client,gboolean take_ownership,GConfEntry * new_entry,gboolean preserve_schema_name)2288 gconf_client_cache (GConfClient *client,
2289                     gboolean     take_ownership,
2290                     GConfEntry  *new_entry,
2291                     gboolean     preserve_schema_name)
2292 {
2293   gpointer oldkey = NULL, oldval = NULL;
2294 
2295   if (g_hash_table_lookup_extended (client->cache_hash, new_entry->key, &oldkey, &oldval))
2296     {
2297       /* Already have a value, update it */
2298       GConfEntry *entry = oldval;
2299       gboolean changed;
2300 
2301       g_assert (entry != NULL);
2302 
2303       changed = ! gconf_entry_equal (entry, new_entry);
2304 
2305       if (changed)
2306         {
2307           trace ("Updating value of '%s' in the cache",
2308                  new_entry->key);
2309 
2310           if (!take_ownership)
2311             new_entry = gconf_entry_copy (new_entry);
2312 
2313           if (preserve_schema_name)
2314             gconf_entry_set_schema_name (new_entry,
2315                                          gconf_entry_get_schema_name (entry));
2316 
2317           g_hash_table_replace (client->cache_hash,
2318                                 new_entry->key,
2319                                 new_entry);
2320 
2321           /* oldkey is inside entry */
2322           gconf_entry_free (entry);
2323         }
2324       else
2325         {
2326           trace ("Value of '%s' hasn't actually changed, would have updated in cache if it had",
2327                  new_entry->key);
2328 
2329           if (take_ownership)
2330             gconf_entry_free (new_entry);
2331         }
2332 
2333       return changed;
2334     }
2335   else
2336     {
2337       /* Create a new entry */
2338       if (!take_ownership)
2339         new_entry = gconf_entry_copy (new_entry);
2340 
2341       g_hash_table_insert (client->cache_hash, new_entry->key, new_entry);
2342       trace ("Added value of '%s' to the cache",
2343              new_entry->key);
2344 
2345       return TRUE; /* changed */
2346     }
2347 }
2348 
2349 static gboolean
gconf_client_lookup(GConfClient * client,const char * key,GConfEntry ** entryp)2350 gconf_client_lookup (GConfClient *client,
2351                      const char  *key,
2352                      GConfEntry **entryp)
2353 {
2354   GConfEntry *entry;
2355 
2356   g_return_val_if_fail (entryp != NULL, FALSE);
2357   g_return_val_if_fail (*entryp == NULL, FALSE);
2358 
2359   entry = g_hash_table_lookup (client->cache_hash, key);
2360 
2361   *entryp = entry;
2362 
2363   if (!entry)
2364   {
2365     char *dir, *last_slash;
2366 
2367     dir = g_strdup (key);
2368     last_slash = strrchr (dir, '/');
2369     g_assert (last_slash != NULL);
2370     *last_slash = 0;
2371 
2372     if (g_hash_table_lookup (client->cache_dirs, dir))
2373       {
2374         g_free (dir);
2375         trace ("Negative cache hit on %s", key);
2376         return TRUE;
2377       }
2378     else
2379       {
2380         gboolean not_cached = FALSE;
2381         while(not_cached || (!g_hash_table_lookup (client->cache_recursive_dirs, dir)))
2382           {
2383             last_slash = strrchr (dir, '/');
2384             if (last_slash == NULL)
2385               break;
2386             else
2387               *last_slash = 0;
2388             if (g_hash_table_lookup (client->cache_recursive_dirs, dir))
2389               {
2390                 g_free (dir);
2391                 trace ("Non-existing dir for %s", key);
2392                 return TRUE;
2393               }
2394             not_cached = TRUE;
2395           }
2396       }
2397     g_free (dir);
2398   }
2399 
2400   return entry != NULL;
2401 }
2402 
2403 /*
2404  * Dir
2405  */
2406 
2407 static Dir*
dir_new(const gchar * name,guint notify_id)2408 dir_new(const gchar* name, guint notify_id)
2409 {
2410   Dir* d;
2411 
2412   d = g_new(Dir, 1);
2413 
2414   d->name = g_strdup(name);
2415   d->notify_id = notify_id;
2416   d->add_count = 0;
2417 
2418   return d;
2419 }
2420 
2421 static void
dir_destroy(Dir * d)2422 dir_destroy(Dir* d)
2423 {
2424   g_return_if_fail(d != NULL);
2425   g_return_if_fail(d->notify_id == 0);
2426 
2427   g_free(d->name);
2428   g_free(d);
2429 }
2430 
2431 /*
2432  * Listener
2433  */
2434 
2435 static Listener*
listener_new(GConfClientNotifyFunc func,GFreeFunc destroy_notify,gpointer data)2436 listener_new(GConfClientNotifyFunc func,
2437              GFreeFunc destroy_notify,
2438              gpointer data)
2439 {
2440   Listener* l;
2441 
2442   l = g_new(Listener, 1);
2443 
2444   l->func = func;
2445   l->data = data;
2446   l->destroy_notify = destroy_notify;
2447 
2448   return l;
2449 }
2450 
2451 static void
listener_destroy(Listener * l)2452 listener_destroy(Listener* l)
2453 {
2454   g_return_if_fail(l != NULL);
2455 
2456   if (l->destroy_notify)
2457     (* l->destroy_notify) (l->data);
2458 
2459   g_free(l);
2460 }
2461 
2462 /*
2463  * Change sets
2464  */
2465 
2466 
2467 struct CommitData {
2468   GConfClient* client;
2469   GError* error;
2470   GSList* remove_list;
2471   gboolean remove_committed;
2472 };
2473 
2474 static void
commit_foreach(GConfChangeSet * cs,const gchar * key,GConfValue * value,gpointer user_data)2475 commit_foreach (GConfChangeSet* cs,
2476                 const gchar* key,
2477                 GConfValue* value,
2478                 gpointer user_data)
2479 {
2480   struct CommitData* cd = user_data;
2481 
2482   g_assert(cd != NULL);
2483 
2484   if (cd->error != NULL)
2485     return;
2486 
2487   if (value)
2488     gconf_client_set   (cd->client, key, value, &cd->error);
2489   else
2490     gconf_client_unset (cd->client, key, &cd->error);
2491 
2492   if (cd->error == NULL && cd->remove_committed)
2493     {
2494       /* Bad bad bad; we keep the key reference, knowing that it's
2495          valid until we modify the change set, to avoid string copies.  */
2496       cd->remove_list = g_slist_prepend(cd->remove_list, (gchar*)key);
2497     }
2498 }
2499 
2500 gboolean
gconf_client_commit_change_set(GConfClient * client,GConfChangeSet * cs,gboolean remove_committed,GError ** err)2501 gconf_client_commit_change_set   (GConfClient* client,
2502                                   GConfChangeSet* cs,
2503                                   gboolean remove_committed,
2504                                   GError** err)
2505 {
2506   struct CommitData cd;
2507   GSList* tmp;
2508 
2509   g_return_val_if_fail(client != NULL, FALSE);
2510   g_return_val_if_fail(GCONF_IS_CLIENT(client), FALSE);
2511   g_return_val_if_fail(cs != NULL, FALSE);
2512   g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
2513 
2514   cd.client = client;
2515   cd.error = NULL;
2516   cd.remove_list = NULL;
2517   cd.remove_committed = remove_committed;
2518 
2519   /* Because the commit could have lots of side
2520      effects, this makes it safer */
2521   gconf_change_set_ref(cs);
2522   g_object_ref(G_OBJECT(client));
2523 
2524   gconf_change_set_foreach(cs, commit_foreach, &cd);
2525 
2526   tmp = cd.remove_list;
2527   while (tmp != NULL)
2528     {
2529       const gchar* key = tmp->data;
2530 
2531       gconf_change_set_remove(cs, key);
2532 
2533       /* key is now invalid due to our little evil trick */
2534 
2535       tmp = g_slist_next(tmp);
2536     }
2537 
2538   g_slist_free(cd.remove_list);
2539 
2540   gconf_change_set_unref(cs);
2541   g_object_unref(G_OBJECT(client));
2542 
2543   if (cd.error != NULL)
2544     {
2545       if (err != NULL)
2546         *err = cd.error;
2547       else
2548         g_error_free(cd.error);
2549 
2550       return FALSE;
2551     }
2552   else
2553     {
2554       g_assert((!remove_committed) ||
2555                (gconf_change_set_size(cs) == 0));
2556 
2557       return TRUE;
2558     }
2559 }
2560 
2561 struct RevertData {
2562   GConfClient* client;
2563   GError* error;
2564   GConfChangeSet* revert_set;
2565 };
2566 
2567 static void
revert_foreach(GConfChangeSet * cs,const gchar * key,GConfValue * value,gpointer user_data)2568 revert_foreach (GConfChangeSet* cs,
2569                 const gchar* key,
2570                 GConfValue* value,
2571                 gpointer user_data)
2572 {
2573   struct RevertData* rd = user_data;
2574   GConfValue* old_value;
2575   GError* error = NULL;
2576 
2577   g_assert(rd != NULL);
2578 
2579   if (rd->error != NULL)
2580     return;
2581 
2582   old_value = gconf_client_get_without_default(rd->client, key, &error);
2583 
2584   if (error != NULL)
2585     {
2586       /* FIXME */
2587       g_warning("error creating revert set: %s", error->message);
2588       g_error_free(error);
2589       error = NULL;
2590     }
2591 
2592   if (old_value == NULL &&
2593       value == NULL)
2594     return; /* this commit will have no effect. */
2595 
2596   if (old_value == NULL)
2597     gconf_change_set_unset(rd->revert_set, key);
2598   else
2599     gconf_change_set_set_nocopy(rd->revert_set, key, old_value);
2600 }
2601 
2602 
2603 GConfChangeSet*
gconf_client_reverse_change_set(GConfClient * client,GConfChangeSet * cs,GError ** err)2604 gconf_client_reverse_change_set  (GConfClient* client,
2605                                          GConfChangeSet* cs,
2606                                          GError** err)
2607 {
2608   struct RevertData rd;
2609 
2610   rd.error = NULL;
2611   rd.client = client;
2612   rd.revert_set = gconf_change_set_new();
2613 
2614   /* we're emitting signals and such, avoid
2615      nasty side effects with these.
2616   */
2617   g_object_ref(G_OBJECT(rd.client));
2618   gconf_change_set_ref(cs);
2619 
2620   gconf_change_set_foreach(cs, revert_foreach, &rd);
2621 
2622   if (rd.error != NULL)
2623     {
2624       if (err != NULL)
2625         *err = rd.error;
2626       else
2627         g_error_free(rd.error);
2628     }
2629 
2630   g_object_unref(G_OBJECT(rd.client));
2631   gconf_change_set_unref(cs);
2632 
2633   return rd.revert_set;
2634 }
2635 
2636 
2637 GConfChangeSet*
gconf_client_change_set_from_currentv(GConfClient * client,const gchar ** keys,GError ** err)2638 gconf_client_change_set_from_currentv (GConfClient* client,
2639                                               const gchar** keys,
2640                                               GError** err)
2641 {
2642   GConfValue* old_value;
2643   GConfChangeSet* new_set;
2644   const gchar** keyp;
2645 
2646   g_return_val_if_fail(err == NULL || *err == NULL, NULL);
2647 
2648   new_set = gconf_change_set_new();
2649 
2650   keyp = keys;
2651 
2652   while (*keyp != NULL)
2653     {
2654       GError* error = NULL;
2655       const gchar* key = *keyp;
2656 
2657       old_value = gconf_client_get_without_default(client, key, &error);
2658 
2659       if (error != NULL)
2660         {
2661           /* FIXME */
2662           g_warning("error creating change set from current keys: %s", error->message);
2663           g_error_free(error);
2664           error = NULL;
2665         }
2666 
2667       if (old_value == NULL)
2668         gconf_change_set_unset(new_set, key);
2669       else
2670         gconf_change_set_set_nocopy(new_set, key, old_value);
2671 
2672       ++keyp;
2673     }
2674 
2675   return new_set;
2676 }
2677 
2678 GConfChangeSet*
gconf_client_change_set_from_current(GConfClient * client,GError ** err,const gchar * first_key,...)2679 gconf_client_change_set_from_current (GConfClient* client,
2680                                              GError** err,
2681                                              const gchar* first_key,
2682                                              ...)
2683 {
2684   GSList* keys = NULL;
2685   va_list args;
2686   const gchar* arg;
2687   const gchar** vec;
2688   GConfChangeSet* retval;
2689   GSList* tmp;
2690   guint i;
2691 
2692   g_return_val_if_fail(err == NULL || *err == NULL, NULL);
2693 
2694   va_start (args, first_key);
2695 
2696   arg = first_key;
2697 
2698   while (arg != NULL)
2699     {
2700       keys = g_slist_prepend(keys, (/*non-const*/gchar*)arg);
2701 
2702       arg = va_arg (args, const gchar*);
2703     }
2704 
2705   va_end (args);
2706 
2707   vec = g_new0(const gchar*, g_slist_length(keys) + 1);
2708 
2709   i = 0;
2710   tmp = keys;
2711 
2712   while (tmp != NULL)
2713     {
2714       vec[i] = tmp->data;
2715 
2716       ++i;
2717       tmp = g_slist_next(tmp);
2718     }
2719 
2720   g_slist_free(keys);
2721 
2722   retval = gconf_client_change_set_from_currentv(client, vec, err);
2723 
2724   g_free(vec);
2725 
2726   return retval;
2727 }
2728 
2729 static GHashTable * clients = NULL;
2730 
2731 static void
register_client(GConfClient * client)2732 register_client (GConfClient *client)
2733 {
2734   if (clients == NULL)
2735     clients = g_hash_table_new (NULL, NULL);
2736 
2737   g_hash_table_insert (clients, client->engine, client);
2738 }
2739 
2740 static GConfClient *
lookup_client(GConfEngine * engine)2741 lookup_client (GConfEngine *engine)
2742 {
2743   if (clients == NULL)
2744     return NULL;
2745   else
2746     return g_hash_table_lookup (clients, engine);
2747 }
2748 
2749 static void
unregister_client(GConfClient * client)2750 unregister_client (GConfClient *client)
2751 {
2752   g_return_if_fail (clients != NULL);
2753 
2754   g_hash_table_remove (clients, client->engine);
2755 }
2756 
2757 
2758 /*
2759  * Notification
2760  */
2761 
2762 static gboolean
notify_idle_callback(gpointer data)2763 notify_idle_callback (gpointer data)
2764 {
2765   GConfClient *client = data;
2766 
2767   client->notify_handler = 0; /* avoid g_source_remove */
2768 
2769   gconf_client_flush_notifies (client);
2770 
2771   /* remove handler */
2772   return FALSE;
2773 }
2774 
2775 static void
gconf_client_queue_notify(GConfClient * client,const char * key)2776 gconf_client_queue_notify (GConfClient *client,
2777                            const char  *key)
2778 {
2779   trace ("Queing notify on '%s', %d pending already", key,
2780          client->pending_notify_count);
2781 
2782   if (client->notify_handler == 0)
2783     client->notify_handler = g_idle_add (notify_idle_callback, client);
2784 
2785   client->notify_list = g_slist_prepend (client->notify_list, g_strdup (key));
2786   client->pending_notify_count += 1;
2787 }
2788 
2789 struct ClientAndEntry {
2790   GConfClient* client;
2791   GConfEntry* entry;
2792 };
2793 
2794 static void
notify_listeners_callback(GConfListeners * listeners,const gchar * key,guint cnxn_id,gpointer listener_data,gpointer user_data)2795 notify_listeners_callback(GConfListeners* listeners,
2796                           const gchar* key,
2797                           guint cnxn_id,
2798                           gpointer listener_data,
2799                           gpointer user_data)
2800 {
2801   Listener* l = listener_data;
2802   struct ClientAndEntry* cae = user_data;
2803 
2804   g_return_if_fail (cae != NULL);
2805   g_return_if_fail (cae->client != NULL);
2806   g_return_if_fail (GCONF_IS_CLIENT (cae->client));
2807   g_return_if_fail (l != NULL);
2808   g_return_if_fail (l->func != NULL);
2809 
2810   (*l->func) (cae->client, cnxn_id, cae->entry, l->data);
2811 }
2812 
2813 static void
notify_one_entry(GConfClient * client,GConfEntry * entry)2814 notify_one_entry (GConfClient *client,
2815                   GConfEntry  *entry)
2816 {
2817   g_object_ref (G_OBJECT (client));
2818   gconf_entry_ref (entry);
2819 
2820   /* Emit the value_changed signal before notifying specific listeners;
2821    * I'm not sure there's a reason this matters though
2822    */
2823   gconf_client_value_changed (client,
2824                               entry->key,
2825                               gconf_entry_get_value (entry));
2826 
2827   /* Now notify our listeners, if any */
2828   if (client->listeners != NULL)
2829     {
2830       struct ClientAndEntry cae;
2831 
2832       cae.client = client;
2833       cae.entry = entry;
2834 
2835       gconf_listeners_notify (client->listeners,
2836                               entry->key,
2837                               notify_listeners_callback,
2838                               &cae);
2839     }
2840 
2841   gconf_entry_unref (entry);
2842   g_object_unref (G_OBJECT (client));
2843 }
2844 
2845 static void
gconf_client_flush_notifies(GConfClient * client)2846 gconf_client_flush_notifies (GConfClient *client)
2847 {
2848   GSList *tmp;
2849   GSList *to_notify;
2850   GConfEntry *last_entry;
2851 
2852   trace ("Flushing notify queue");
2853 
2854   /* Adopt notify list and clear it, to avoid reentrancy concerns.
2855    * Sort it to compress duplicates, and keep people from relying on
2856    * the notify order.
2857    */
2858   to_notify = g_slist_sort (client->notify_list, (GCompareFunc) strcmp);
2859   client->notify_list = NULL;
2860   client->pending_notify_count = 0;
2861 
2862   gconf_client_unqueue_notifies (client);
2863 
2864   last_entry = NULL;
2865   tmp = to_notify;
2866   while (tmp != NULL)
2867     {
2868       GConfEntry *entry = NULL;
2869 
2870       if (gconf_client_lookup (client, tmp->data, &entry) && entry != NULL)
2871         {
2872           if (entry != last_entry)
2873             {
2874               trace ("Doing notification for '%s'", entry->key);
2875               notify_one_entry (client, entry);
2876               last_entry = entry;
2877             }
2878           else
2879             {
2880               trace ("Ignoring duplicate notify for '%s'", entry->key);
2881             }
2882         }
2883       else
2884         {
2885 #ifdef HAVE_DBUS
2886           /* The dbus version cleans the cache after modifying a value so a get
2887            * directly after a set doesn't return a stale value. That means we
2888            * have to check if the key is supposed to be monitored here, we can't
2889            * just rely on it being in the cache.
2890            */
2891           if (key_being_monitored (client, tmp->data))
2892             {
2893               trace ("Key %s was in notify queue but not in cache, but is being monitored",
2894                      tmp->data);
2895 
2896               entry = gconf_client_get_entry (client, tmp->data, NULL, TRUE, NULL);
2897               if (entry != NULL)
2898                 {
2899                   notify_one_entry (client, entry);
2900                   gconf_entry_unref (entry);
2901                   last_entry = NULL;
2902                 }
2903             }
2904           else
2905             {
2906               trace ("Key '%s' was in notify queue but not in cache; we must have stopped monitoring it; not notifying",
2907                      tmp->data);
2908             }
2909 #else
2910               trace ("Key '%s' was in notify queue but not in cache; we must have stopped monitoring it; not notifying",
2911                      tmp->data);
2912 #endif
2913         }
2914 
2915       tmp = tmp->next;
2916     }
2917 
2918   g_slist_foreach (to_notify, (GFunc) g_free, NULL);
2919   g_slist_free (to_notify);
2920 }
2921 
2922 static void
gconf_client_unqueue_notifies(GConfClient * client)2923 gconf_client_unqueue_notifies (GConfClient *client)
2924 {
2925   if (client->notify_handler != 0)
2926     {
2927       g_source_remove (client->notify_handler);
2928       client->notify_handler = 0;
2929     }
2930 
2931   if (client->notify_list != NULL)
2932     {
2933       g_slist_foreach (client->notify_list, (GFunc) g_free, NULL);
2934       g_slist_free (client->notify_list);
2935       client->notify_list = NULL;
2936       client->pending_notify_count = 0;
2937     }
2938 }
2939