1 /*
2  * Nautilus-Actions
3  * A Nautilus extension which offers configurable context menu actions.
4  *
5  * Copyright (C) 2005 The GNOME Foundation
6  * Copyright (C) 2006-2008 Frederic Ruaudel and others (see AUTHORS)
7  * Copyright (C) 2009-2014 Pierre Wieser and others (see AUTHORS)
8  *
9  * Nautilus-Actions is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 2 of
12  * the License, or (at your option) any later version.
13  *
14  * Nautilus-Actions is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Nautilus-Actions; see the file COPYING. If not, see
21  * <http://www.gnu.org/licenses/>.
22  *
23  * Authors:
24  *   Frederic Ruaudel <grumz@grumz.net>
25  *   Rodrigo Moya <rodrigo@gnome-db.org>
26  *   Pierre Wieser <pwieser@trychlos.org>
27  *   ... and many others (see AUTHORS)
28  */
29 
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33 
34 #include <gio/gio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <strings.h>
38 
39 #include <api/na-boxed.h>
40 #include <api/na-data-types.h>
41 #include <api/na-core-utils.h>
42 #include <api/na-timeout.h>
43 
44 #include "na-settings.h"
45 #include "na-marshal.h"
46 
47 #define NA_SETTINGS_TYPE                ( settings_get_type())
48 #define NA_SETTINGS( object )           ( G_TYPE_CHECK_INSTANCE_CAST( object, NA_SETTINGS_TYPE, NASettings ))
49 #define NA_SETTINGS_CLASS( klass )      ( G_TYPE_CHECK_CLASS_CAST( klass, NA_SETTINGS_TYPE, NASettingsClass ))
50 #define NA_IS_SETTINGS( object )        ( G_TYPE_CHECK_INSTANCE_TYPE( object, NA_SETTINGS_TYPE ))
51 #define NA_IS_SETTINGS_CLASS( klass )   ( G_TYPE_CHECK_CLASS_TYPE(( klass ), NA_SETTINGS_TYPE ))
52 #define NA_SETTINGS_GET_CLASS( object ) ( G_TYPE_INSTANCE_GET_CLASS(( object ), NA_SETTINGS_TYPE, NASettingsClass ))
53 
54 typedef struct _NASettingsPrivate       NASettingsPrivate;
55 
56 typedef struct {
57 	/*< private >*/
58 	GObject            parent;
59 	NASettingsPrivate *private;
60 }
61 	NASettings;
62 
63 typedef struct _NASettingsClassPrivate  NASettingsClassPrivate;
64 
65 typedef struct {
66 	/*< private >*/
67 	GObjectClass            parent;
68 	NASettingsClassPrivate *private;
69 }
70 	NASettingsClass;
71 
72 /* private class data
73  */
74 struct _NASettingsClassPrivate {
75 	void *empty;						/* so that gcc -pedantic is happy */
76 };
77 
78 /* The characteristics of a configuration file.
79  * We manage two configuration files:
80  * - the global configuration file handles mandatory preferences;
81  * - the user configuration file handles.. well, user preferences.
82  */
83 typedef struct {
84 	gchar        *fname;
85 	gboolean      mandatory;
86 	GKeyFile     *key_file;
87 	GFileMonitor *monitor;
88 	gulong        handler;
89 }
90 	KeyFile;
91 
92 /* Each consumer may register a callback function which will be triggered
93  * when a key is modified.
94  *
95  * The monitored key usually is the real key read in the file;
96  * as a special case, composite keys are defined:
97  * - NA_IPREFS_IO_PROVIDERS_READ_STATUS monitors the 'readable' key for all i/o providers
98  *
99  * Note that we actually monitor the _user_view_ of the configuration:
100  * e.g. if a key has a mandatory value in global conf, then the same
101  * key in user conf will just be ignored.
102  */
103 typedef struct {
104 	gchar    *monitored_key;
105 	GCallback callback;
106 	gpointer  user_data;
107 }
108 	Consumer;
109 
110 /* private instance data
111  */
112 struct _NASettingsPrivate {
113 	gboolean  dispose_has_run;
114 	KeyFile  *mandatory;
115 	KeyFile  *user;
116 	GList    *content;
117 	GList    *consumers;
118 	NATimeout timeout;
119 };
120 
121 #define GROUP_NACT						"nact"
122 #define GROUP_RUNTIME					"runtime"
123 
124 typedef struct {
125 	const gchar *key;
126 	const gchar *group;
127 	guint        type;
128 	const gchar *default_value;
129 }
130 	KeyDef;
131 
132 static const KeyDef st_def_keys[] = {
133 	{ NA_IPREFS_ADMIN_PREFERENCES_LOCKED,         GROUP_NACT,    NA_DATA_TYPE_BOOLEAN,     "false" },
134 	{ NA_IPREFS_ADMIN_IO_PROVIDERS_LOCKED,        GROUP_RUNTIME, NA_DATA_TYPE_BOOLEAN,     "false" },
135 	{ NA_IPREFS_ASSISTANT_ESC_CONFIRM,            GROUP_NACT,    NA_DATA_TYPE_BOOLEAN,     "true" },
136 	{ NA_IPREFS_ASSISTANT_ESC_QUIT,               GROUP_NACT,    NA_DATA_TYPE_BOOLEAN,     "true" },
137 	{ NA_IPREFS_CAPABILITY_ADD_CAPABILITY_WSP,    GROUP_NACT,    NA_DATA_TYPE_UINT_LIST,   "" },
138 	{ NA_IPREFS_COMMAND_CHOOSER_WSP,              GROUP_NACT,    NA_DATA_TYPE_UINT_LIST,   "" },
139 	{ NA_IPREFS_COMMAND_CHOOSER_URI,              GROUP_NACT,    NA_DATA_TYPE_STRING,      "file:///bin" },
140 	{ NA_IPREFS_COMMAND_LEGEND_WSP,               GROUP_NACT,    NA_DATA_TYPE_UINT_LIST,   "" },
141 	{ NA_IPREFS_CONFIRM_LOGOUT_WSP,               GROUP_NACT,    NA_DATA_TYPE_UINT_LIST,   "" },
142 	{ NA_IPREFS_DESKTOP_ENVIRONMENT,              GROUP_RUNTIME, NA_DATA_TYPE_STRING,      "" },
143 	{ NA_IPREFS_WORKING_DIR_WSP,                  GROUP_NACT,    NA_DATA_TYPE_UINT_LIST,   "" },
144 	{ NA_IPREFS_WORKING_DIR_URI,                  GROUP_NACT,    NA_DATA_TYPE_STRING,      "file:///" },
145 	{ NA_IPREFS_SHOW_IF_RUNNING_WSP,              GROUP_NACT,    NA_DATA_TYPE_UINT_LIST,   "" },
146 	{ NA_IPREFS_SHOW_IF_RUNNING_URI,              GROUP_NACT,    NA_DATA_TYPE_STRING,      "file:///bin" },
147 	{ NA_IPREFS_TRY_EXEC_WSP,                     GROUP_NACT,    NA_DATA_TYPE_UINT_LIST,   "" },
148 	{ NA_IPREFS_TRY_EXEC_URI,                     GROUP_NACT,    NA_DATA_TYPE_STRING,      "file:///bin" },
149 	{ NA_IPREFS_EXPORT_ASK_USER_WSP,              GROUP_NACT,    NA_DATA_TYPE_UINT_LIST,   "" },
150 	{ NA_IPREFS_EXPORT_ASK_USER_LAST_FORMAT,      GROUP_NACT,    NA_DATA_TYPE_STRING,      "Desktop1" },
151 	{ NA_IPREFS_EXPORT_ASK_USER_KEEP_LAST_CHOICE, GROUP_NACT,    NA_DATA_TYPE_BOOLEAN,     "false" },
152 	{ NA_IPREFS_EXPORT_ASSISTANT_WSP,             GROUP_NACT,    NA_DATA_TYPE_UINT_LIST,   "" },
153 	{ NA_IPREFS_EXPORT_ASSISTANT_URI,             GROUP_NACT,    NA_DATA_TYPE_STRING,      "file:///tmp" },
154 	{ NA_IPREFS_EXPORT_ASSISTANT_PANED,           GROUP_NACT,    NA_DATA_TYPE_UINT,        "200" },
155 	{ NA_IPREFS_EXPORT_PREFERRED_FORMAT,          GROUP_NACT,    NA_DATA_TYPE_STRING,      "Ask" },
156 	{ NA_IPREFS_FOLDER_CHOOSER_WSP,               GROUP_NACT,    NA_DATA_TYPE_UINT_LIST,   "" },
157 	{ NA_IPREFS_FOLDER_CHOOSER_URI,               GROUP_NACT,    NA_DATA_TYPE_STRING,      "file:///" },
158 	{ NA_IPREFS_IMPORT_ASK_USER_WSP,              GROUP_NACT,    NA_DATA_TYPE_UINT_LIST,   "" },
159 	{ NA_IPREFS_IMPORT_ASK_USER_LAST_MODE,        GROUP_NACT,    NA_DATA_TYPE_STRING,      "NoImport" },
160 	{ NA_IPREFS_IMPORT_ASSISTANT_WSP,             GROUP_NACT,    NA_DATA_TYPE_UINT_LIST,   "" },
161 	{ NA_IPREFS_IMPORT_ASSISTANT_URI,             GROUP_NACT,    NA_DATA_TYPE_STRING,      "file:///tmp" },
162 	{ NA_IPREFS_IMPORT_ASK_USER_KEEP_LAST_CHOICE, GROUP_NACT,    NA_DATA_TYPE_BOOLEAN,     "false" },
163 	{ NA_IPREFS_IMPORT_PREFERRED_MODE,            GROUP_NACT,    NA_DATA_TYPE_STRING,      "Ask" },
164 	{ NA_IPREFS_IO_PROVIDERS_WRITE_ORDER,         GROUP_NACT,    NA_DATA_TYPE_STRING_LIST, "" },
165 	{ NA_IPREFS_ICON_CHOOSER_URI,                 GROUP_NACT,    NA_DATA_TYPE_STRING,      "file:///" },
166 	{ NA_IPREFS_ICON_CHOOSER_PANED,               GROUP_NACT,    NA_DATA_TYPE_UINT,        "200" },
167 	{ NA_IPREFS_ICON_CHOOSER_WSP,                 GROUP_NACT,    NA_DATA_TYPE_UINT_LIST,   "" },
168 	{ NA_IPREFS_ITEMS_ADD_ABOUT_ITEM,             GROUP_RUNTIME, NA_DATA_TYPE_BOOLEAN,     "true" },
169 	{ NA_IPREFS_ITEMS_CREATE_ROOT_MENU,           GROUP_RUNTIME, NA_DATA_TYPE_BOOLEAN,     "true" },
170 	{ NA_IPREFS_ITEMS_LEVEL_ZERO_ORDER,           GROUP_RUNTIME, NA_DATA_TYPE_STRING_LIST, "" },
171 	{ NA_IPREFS_ITEMS_LIST_ORDER_MODE,            GROUP_RUNTIME, NA_DATA_TYPE_STRING,      "AscendingOrder" },
172 	{ NA_IPREFS_MAIN_PANED,                       GROUP_NACT,    NA_DATA_TYPE_UINT,        "200" },
173 	{ NA_IPREFS_MAIN_SAVE_AUTO,                   GROUP_NACT,    NA_DATA_TYPE_BOOLEAN,     "false" },
174 	{ NA_IPREFS_MAIN_SAVE_PERIOD,                 GROUP_NACT,    NA_DATA_TYPE_UINT,        "5" },
175 	{ NA_IPREFS_MAIN_TABS_POS,                    GROUP_NACT,    NA_DATA_TYPE_STRING,      "Top" },
176 	{ NA_IPREFS_MAIN_TOOLBAR_EDIT_DISPLAY,        GROUP_NACT,    NA_DATA_TYPE_BOOLEAN,     "true" },
177 	{ NA_IPREFS_MAIN_TOOLBAR_FILE_DISPLAY,        GROUP_NACT,    NA_DATA_TYPE_BOOLEAN,     "true" },
178 	{ NA_IPREFS_MAIN_TOOLBAR_HELP_DISPLAY,        GROUP_NACT,    NA_DATA_TYPE_BOOLEAN,     "true" },
179 	{ NA_IPREFS_MAIN_TOOLBAR_TOOLS_DISPLAY,       GROUP_NACT,    NA_DATA_TYPE_BOOLEAN,     "false" },
180 	{ NA_IPREFS_MAIN_WINDOW_WSP,                  GROUP_NACT,    NA_DATA_TYPE_UINT_LIST,   "" },
181 	{ NA_IPREFS_PREFERENCES_WSP,                  GROUP_NACT,    NA_DATA_TYPE_UINT_LIST,   "" },
182 	{ NA_IPREFS_PLUGIN_MENU_LOG,                  GROUP_RUNTIME, NA_DATA_TYPE_BOOLEAN,     "false" },
183 	{ NA_IPREFS_RELABEL_DUPLICATE_ACTION,         GROUP_NACT,    NA_DATA_TYPE_BOOLEAN,     "false" },
184 	{ NA_IPREFS_RELABEL_DUPLICATE_MENU,           GROUP_NACT,    NA_DATA_TYPE_BOOLEAN,     "false" },
185 	{ NA_IPREFS_RELABEL_DUPLICATE_PROFILE,        GROUP_NACT,    NA_DATA_TYPE_BOOLEAN,     "false" },
186 	{ NA_IPREFS_SCHEME_ADD_SCHEME_WSP,            GROUP_NACT,    NA_DATA_TYPE_UINT_LIST,   "" },
187 	{ NA_IPREFS_SCHEME_DEFAULT_LIST,              GROUP_NACT,    NA_DATA_TYPE_STRING_LIST, "" },
188 	{ NA_IPREFS_TERMINAL_PATTERN,                 GROUP_RUNTIME, NA_DATA_TYPE_STRING,      "" },
189 	{ NA_IPREFS_IO_PROVIDER_READABLE,             NA_IPREFS_IO_PROVIDER_GROUP, NA_DATA_TYPE_BOOLEAN, "true" },
190 	{ NA_IPREFS_IO_PROVIDER_WRITABLE,             NA_IPREFS_IO_PROVIDER_GROUP, NA_DATA_TYPE_BOOLEAN, "true" },
191 	{ 0 }
192 };
193 
194 /* The configuration content is handled as a GList of KeyValue structs.
195  * This list is loaded at initialization time, and then compared each
196  * time our file monitors signal us that a change has occured.
197  */
198 typedef struct {
199 	const KeyDef *def;
200 	const gchar  *group;
201 	gboolean      mandatory;
202 	NABoxed      *boxed;
203 }
204 	KeyValue;
205 
206 /* signals
207  */
208 enum {
209 	KEY_CHANGED,
210 	LAST_SIGNAL
211 };
212 
213 static GObjectClass *st_parent_class           = NULL;
214 static gint          st_burst_timeout          = 100;		/* burst timeout in msec */
215 static gint          st_signals[ LAST_SIGNAL ] = { 0 };
216 static NASettings   *st_settings               = NULL;
217 
218 static GType     settings_get_type( void );
219 static GType     register_type( void );
220 static void      class_init( NASettingsClass *klass );
221 static void      instance_init( GTypeInstance *instance, gpointer klass );
222 static void      instance_dispose( GObject *object );
223 static void      instance_finalize( GObject *object );
224 
225 static void      settings_new( void );
226 
227 static GList    *content_diff( GList *old, GList *new );
228 static GList    *content_load_keys( GList *content, KeyFile *keyfile );
229 static KeyDef   *get_key_def( const gchar *key );
230 static KeyFile  *key_file_new( const gchar *dir );
231 static void      on_keyfile_changed( GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type );
232 static void      on_keyfile_changed_timeout( void );
233 static void      on_key_changed_final_handler( NASettings *settings, gchar *group, gchar *key, NABoxed *new_value, gboolean mandatory );
234 static KeyValue *peek_key_value_from_content( GList *content, const gchar *group, const gchar *key );
235 static KeyValue *read_key_value( const gchar *group, const gchar *key, gboolean *found, gboolean *mandatory );
236 static KeyValue *read_key_value_from_key_file( KeyFile *keyfile, const gchar *group, const gchar *key, const KeyDef *key_def );
237 static void      release_consumer( Consumer *consumer );
238 static void      release_key_file( KeyFile *key_file );
239 static void      release_key_value( KeyValue *value );
240 static gboolean  set_key_value( const gchar *group, const gchar *key, const gchar *string );
241 static gboolean  write_user_key_file( void );
242 
243 static GType
settings_get_type(void)244 settings_get_type( void )
245 {
246 	static GType object_type = 0;
247 
248 	if( !object_type ){
249 		object_type = register_type();
250 	}
251 
252 	return( object_type );
253 }
254 
255 static GType
register_type(void)256 register_type( void )
257 {
258 	static const gchar *thisfn = "na_settings_register_type";
259 	GType type;
260 
261 	static GTypeInfo info = {
262 		sizeof( NASettingsClass ),
263 		( GBaseInitFunc ) NULL,
264 		( GBaseFinalizeFunc ) NULL,
265 		( GClassInitFunc ) class_init,
266 		NULL,
267 		NULL,
268 		sizeof( NASettings ),
269 		0,
270 		( GInstanceInitFunc ) instance_init
271 	};
272 
273 	g_debug( "%s", thisfn );
274 
275 	type = g_type_register_static( G_TYPE_OBJECT, "NASettings", &info, 0 );
276 
277 	return( type );
278 }
279 
280 static void
class_init(NASettingsClass * klass)281 class_init( NASettingsClass *klass )
282 {
283 	static const gchar *thisfn = "na_settings_class_init";
284 	GObjectClass *object_class;
285 
286 	g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
287 
288 	st_parent_class = g_type_class_peek_parent( klass );
289 
290 	object_class = G_OBJECT_CLASS( klass );
291 	object_class->dispose = instance_dispose;
292 	object_class->finalize = instance_finalize;
293 
294 	klass->private = g_new0( NASettingsClassPrivate, 1 );
295 
296 	/*
297 	 * NASettings::settings-key-changed:
298 	 *
299 	 * This signal is sent by NASettings when the value of a key is modified.
300 	 *
301 	 * Arguments are the group, the key, the new value as a NABoxed,
302 	 * and whether it is mandatory.
303 	 *
304 	 * Handler is of type:
305 	 * void ( *handler )( NASettings *settings,
306 	 * 						const gchar *group,
307 	 * 						const gchar *key,
308 	 * 						NABoxed *value,
309 	 * 						gboolean mandatory,
310 	 * 						gpointer user_data );
311 	 *
312 	 * The default class handler frees these datas.
313 	 */
314 	st_signals[ KEY_CHANGED ] = g_signal_new_class_handler(
315 				SETTINGS_SIGNAL_KEY_CHANGED,
316 				NA_SETTINGS_TYPE,
317 				G_SIGNAL_RUN_CLEANUP | G_SIGNAL_ACTION,
318 				G_CALLBACK( on_key_changed_final_handler ),
319 				NULL,								/* accumulator */
320 				NULL,								/* accumulator data */
321 				na_cclosure_marshal_VOID__STRING_STRING_POINTER_BOOLEAN,
322 				G_TYPE_NONE,
323 				4,
324 				G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN );
325 }
326 
327 static void
instance_init(GTypeInstance * instance,gpointer klass)328 instance_init( GTypeInstance *instance, gpointer klass )
329 {
330 	static const gchar *thisfn = "na_settings_instance_init";
331 	NASettings *self;
332 
333 	g_return_if_fail( NA_IS_SETTINGS( instance ));
334 
335 	g_debug( "%s: instance=%p (%s), klass=%p",
336 			thisfn, ( void * ) instance, G_OBJECT_TYPE_NAME( instance ), ( void * ) klass );
337 
338 	self = NA_SETTINGS( instance );
339 
340 	self->private = g_new0( NASettingsPrivate, 1 );
341 
342 	self->private->dispose_has_run = FALSE;
343 	self->private->mandatory = NULL;
344 	self->private->user = NULL;
345 	self->private->content = NULL;
346 	self->private->consumers = NULL;
347 
348 	self->private->timeout.timeout = st_burst_timeout;
349 	self->private->timeout.handler = ( NATimeoutFunc ) on_keyfile_changed_timeout;
350 	self->private->timeout.user_data = NULL;
351 	self->private->timeout.source_id = 0;
352 }
353 
354 static void
instance_dispose(GObject * object)355 instance_dispose( GObject *object )
356 {
357 	static const gchar *thisfn = "na_settings_instance_dispose";
358 	NASettings *self;
359 
360 	g_return_if_fail( NA_IS_SETTINGS( object ));
361 
362 	self = NA_SETTINGS( object );
363 
364 	if( !self->private->dispose_has_run ){
365 
366 		g_debug( "%s: object=%p (%s)", thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ));
367 
368 		self->private->dispose_has_run = TRUE;
369 
370 		release_key_file( self->private->mandatory );
371 		release_key_file( self->private->user );
372 
373 		if( G_OBJECT_CLASS( st_parent_class )->dispose ){
374 			G_OBJECT_CLASS( st_parent_class )->dispose( object );
375 		}
376 	}
377 }
378 
379 static void
instance_finalize(GObject * object)380 instance_finalize( GObject *object )
381 {
382 	static const gchar *thisfn = "na_settings_instance_finalize";
383 	NASettings *self;
384 
385 	g_return_if_fail( NA_IS_SETTINGS( object ));
386 
387 	g_debug( "%s: object=%p (%s)", thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ));
388 
389 	self = NA_SETTINGS( object );
390 
391 	g_list_foreach( self->private->content, ( GFunc ) release_key_value, NULL );
392 	g_list_free( self->private->content );
393 
394 	g_list_foreach( self->private->consumers, ( GFunc ) release_consumer, NULL );
395 	g_list_free( self->private->consumers );
396 
397 	g_free( self->private );
398 
399 	/* chain call to parent class */
400 	if( G_OBJECT_CLASS( st_parent_class )->finalize ){
401 		G_OBJECT_CLASS( st_parent_class )->finalize( object );
402 	}
403 }
404 
405 /**
406  * na_settings_new:
407  *
408  * Allocates a new #NASettings object which should be na_settings_free()
409  * by the caller.
410  */
411 static void
settings_new(void)412 settings_new( void )
413 {
414 	static const gchar *thisfn = "na_settings_new";
415 	gchar *dir;
416 	GList *content;
417 	const gchar * const *array;
418 	gchar **iter;
419 
420 	if( !st_settings ){
421 		content = NULL;
422 		st_settings = g_object_new( NA_SETTINGS_TYPE, NULL );
423 
424 		/* iterate through system config dirs until having found a
425 		 * config file */
426 		g_debug( "%s: reading mandatory configuration", thisfn );
427 		array = g_get_system_config_dirs();
428 		iter = ( gchar ** ) array;
429 		while( *iter ){
430 			if( st_settings->private->mandatory ){
431 				release_key_file( st_settings->private->mandatory );
432 			}
433 			g_debug( "iter=%s", *iter );
434 			dir = g_build_filename( *iter, PACKAGE, NULL );
435 			st_settings->private->mandatory = key_file_new( dir );
436 			g_free( dir );
437 			st_settings->private->mandatory->mandatory = TRUE;
438 			content = content_load_keys( NULL, st_settings->private->mandatory );
439 			if( content ){
440 				break;
441 			}
442 			iter++;
443 		}
444 
445 		g_debug( "%s: reading user configuration", thisfn );
446 		dir = g_build_filename( g_get_user_config_dir(), PACKAGE, NULL );
447 		g_mkdir_with_parents( dir, 0750 );
448 		st_settings->private->user = key_file_new( dir );
449 		g_free( dir );
450 		st_settings->private->mandatory->mandatory = FALSE;
451 		content = content_load_keys( content, st_settings->private->user );
452 
453 		st_settings->private->content = g_list_copy( content );
454 		g_list_free( content );
455 	}
456 }
457 
458 /**
459  * na_settings_free:
460  */
461 void
na_settings_free(void)462 na_settings_free( void )
463 {
464 	if( st_settings ){
465 		g_object_unref( st_settings );
466 		st_settings = NULL;
467 	}
468 }
469 
470 /**
471  * na_settings_register_key_callback:
472  * @key: the key to be monitored.
473  * @callback: the function to be called when the value of the key changes.
474  * @user_data: data to be passed to the @callback function.
475  *
476  * Registers a new consumer of the monitoring of the @key.
477  *
478  * Since: 3.1
479  */
480 void
na_settings_register_key_callback(const gchar * key,GCallback callback,gpointer user_data)481 na_settings_register_key_callback( const gchar *key, GCallback callback, gpointer user_data )
482 {
483 	static const gchar *thisfn = "na_settings_register_key_callback";
484 
485 	g_debug( "%s: key=%s, callback=%p, user_data=%p",
486 			thisfn, key, ( void * ) callback, ( void * ) user_data );
487 
488 	Consumer *consumer = g_new0( Consumer, 1 );
489 	consumer->monitored_key = g_strdup( key );
490 	consumer->callback = callback;
491 	consumer->user_data = user_data;
492 
493 	settings_new();
494 	st_settings->private->consumers = g_list_prepend( st_settings->private->consumers, consumer );
495 }
496 
497 /**
498  * na_settings_get_boolean:
499  * @key: the key whose value is to be returned.
500  * @found: if not %NULL, a pointer to a gboolean in which we will store
501  *  whether the searched @key has been found (%TRUE), or if the returned
502  *  value comes from default (%FALSE).
503  * @mandatory: if not %NULL, a pointer to a gboolean in which we will store
504  *  whether the returned value has been read from mandatory preferences
505  *  (%TRUE), or from the user preferences (%FALSE). When the @key has not
506  *  been found, @mandatory is set to %FALSE.
507  *
508  * This function should only be called for unambiguous keys; the resultat
509  * is otherwise undefined (and rather unpredictable).
510  *
511  * Returns: the value of the key, of its default value if not found.
512  *
513  * Since: 3.1
514  */
515 gboolean
na_settings_get_boolean(const gchar * key,gboolean * found,gboolean * mandatory)516 na_settings_get_boolean( const gchar *key, gboolean *found, gboolean *mandatory )
517 {
518 	return( na_settings_get_boolean_ex( NULL, key, found, mandatory ));
519 }
520 
521 /**
522  * na_settings_get_boolean_ex:
523  * @group: the group where the @key is to be searched for. May be %NULL.
524  * @key: the key whose value is to be returned.
525  * @found: if not %NULL, a pointer to a gboolean in which we will store
526  *  whether the searched @key has been found (%TRUE), or if the returned
527  *  value comes from default (%FALSE).
528  * @mandatory: if not %NULL, a pointer to a gboolean in which we will store
529  *  whether the returned value has been read from mandatory preferences
530  *  (%TRUE), or from the user preferences (%FALSE). When the @key has not
531  *  been found, @mandatory is set to %FALSE.
532  *
533  * Returns: the value of the key, of its default value if not found.
534  *
535  * Since: 3.1
536  */
537 gboolean
na_settings_get_boolean_ex(const gchar * group,const gchar * key,gboolean * found,gboolean * mandatory)538 na_settings_get_boolean_ex( const gchar *group, const gchar *key, gboolean *found, gboolean *mandatory )
539 {
540 	gboolean value;
541 	KeyValue *key_value;
542 	KeyDef *key_def;
543 
544 	value = FALSE;
545 	key_value = read_key_value( group, key, found, mandatory );
546 
547 	if( key_value ){
548 		value = na_boxed_get_boolean( key_value->boxed );
549 		release_key_value( key_value );
550 
551 	} else {
552 		key_def = get_key_def( key );
553 		if( key_def ){
554 			value = ( key_def->default_value ? ( strcasecmp( key_def->default_value, "true" ) == 0 || atoi( key_def->default_value ) != 0 ) : FALSE );
555 		}
556 	}
557 
558 	return( value );
559 }
560 
561 /**
562  * na_settings_get_string:
563  * @key: the key whose value is to be returned.
564  * @found: if not %NULL, a pointer to a gboolean in which we will store
565  *  whether the searched @key has been found (%TRUE), or if the returned
566  *  value comes from default (%FALSE).
567  * @mandatory: if not %NULL, a pointer to a gboolean in which we will store
568  *  whether the returned value has been read from mandatory preferences
569  *  (%TRUE), or from the user preferences (%FALSE). When the @key has not
570  *  been found, @mandatory is set to %FALSE.
571  *
572  * This function should only be called for unambiguous keys; the resultat
573  * is otherwise undefined (and rather unpredictable).
574  *
575  * Returns: the value of the key as a newly allocated string, which should
576  * be g_free() by the caller.
577  *
578  * Since: 3.1
579  */
580 gchar *
na_settings_get_string(const gchar * key,gboolean * found,gboolean * mandatory)581 na_settings_get_string( const gchar *key, gboolean *found, gboolean *mandatory )
582 {
583 	gchar *value;
584 	KeyValue *key_value;
585 	KeyDef *key_def;
586 
587 	value = NULL;
588 	key_value = read_key_value( NULL, key, found, mandatory );
589 
590 	if( key_value ){
591 		value = na_boxed_get_string( key_value->boxed );
592 		release_key_value( key_value );
593 
594 	} else {
595 		key_def = get_key_def( key );
596 		if( key_def && key_def->default_value ){
597 			value = g_strdup( key_def->default_value );
598 		}
599 	}
600 
601 	return( value );
602 }
603 
604 /**
605  * na_settings_get_string_list:
606  * @key: the key whose value is to be returned.
607  * @found: if not %NULL, a pointer to a gboolean in which we will store
608  *  whether the searched @key has been found (%TRUE), or if the returned
609  *  value comes from default (%FALSE).
610  * @mandatory: if not %NULL, a pointer to a gboolean in which we will store
611  *  whether the returned value has been read from mandatory preferences
612  *  (%TRUE), or from the user preferences (%FALSE). When the @key has not
613  *  been found, @mandatory is set to %FALSE.
614  *
615  * This function should only be called for unambiguous keys; the resultat
616  * is otherwise undefined (and rather unpredictable).
617  *
618  * Returns: the value of the key as a newly allocated list of strings.
619  * The returned list should be na_core_utils_slist_free() by the caller.
620  *
621  * Since: 3.1
622  */
623 GSList *
na_settings_get_string_list(const gchar * key,gboolean * found,gboolean * mandatory)624 na_settings_get_string_list( const gchar *key, gboolean *found, gboolean *mandatory )
625 {
626 	GSList *value;
627 	KeyValue *key_value;
628 	KeyDef *key_def;
629 
630 	value = NULL;
631 	key_value = read_key_value( NULL, key, found, mandatory );
632 
633 	if( key_value ){
634 		value = na_boxed_get_string_list( key_value->boxed );
635 		release_key_value( key_value );
636 
637 	} else {
638 		key_def = get_key_def( key );
639 		if( key_def && key_def->default_value && strlen( key_def->default_value )){
640 			value = g_slist_append( NULL, g_strdup( key_def->default_value ));
641 		}
642 	}
643 
644 	return( value );
645 }
646 
647 /**
648  * na_settings_get_uint:
649  * @key: the key whose value is to be returned.
650  * @found: if not %NULL, a pointer to a gboolean in which we will store
651  *  whether the searched @key has been found (%TRUE), or if the returned
652  *  value comes from default (%FALSE).
653  * @mandatory: if not %NULL, a pointer to a gboolean in which we will store
654  *  whether the returned value has been read from mandatory preferences
655  *  (%TRUE), or from the user preferences (%FALSE). When the @key has not
656  *  been found, @mandatory is set to %FALSE.
657  *
658  * This function should only be called for unambiguous keys; the resultat
659  * is otherwise undefined (and rather unpredictable).
660  *
661  * Returns: the value of the key.
662  *
663  * Since: 3.1
664  */
665 guint
na_settings_get_uint(const gchar * key,gboolean * found,gboolean * mandatory)666 na_settings_get_uint( const gchar *key, gboolean *found, gboolean *mandatory )
667 {
668 	guint value;
669 	KeyDef *key_def;
670 	KeyValue *key_value;
671 
672 	value = 0;
673 	key_value = read_key_value( NULL, key, found, mandatory );
674 
675 	if( key_value ){
676 		value = na_boxed_get_uint( key_value->boxed );
677 		release_key_value( key_value );
678 
679 	} else {
680 		key_def = get_key_def( key );
681 		if( key_def && key_def->default_value ){
682 			value = atoi( key_def->default_value );
683 		}
684 	}
685 
686 	return( value );
687 }
688 
689 /**
690  * na_settings_get_uint_list:
691  * @key: the key whose value is to be returned.
692  * @found: if not %NULL, a pointer to a gboolean in which we will store
693  *  whether the searched @key has been found (%TRUE), or if the returned
694  *  value comes from default (%FALSE).
695  * @mandatory: if not %NULL, a pointer to a gboolean in which we will store
696  *  whether the returned value has been read from mandatory preferences
697  *  (%TRUE), or from the user preferences (%FALSE). When the @key has not
698  *  been found, @mandatory is set to %FALSE.
699  *
700  * This function should only be called for unambiguous keys; the resultat
701  * is otherwise undefined (and rather unpredictable).
702  *
703  * Returns: the value of the key as a newly allocated list of uints.
704  * The returned list should be g_list_free() by the caller.
705  *
706  * Since: 3.1
707  */
708 GList *
na_settings_get_uint_list(const gchar * key,gboolean * found,gboolean * mandatory)709 na_settings_get_uint_list( const gchar *key, gboolean *found, gboolean *mandatory )
710 {
711 	GList *value;
712 	KeyDef *key_def;
713 	KeyValue *key_value;
714 
715 	value = NULL;
716 	key_value = read_key_value( NULL, key, found, mandatory );
717 
718 	if( key_value ){
719 		value = na_boxed_get_uint_list( key_value->boxed );
720 		release_key_value( key_value );
721 
722 	} else {
723 		key_def = get_key_def( key );
724 		if( key_def && key_def->default_value ){
725 			value = g_list_append( NULL, GUINT_TO_POINTER( atoi( key_def->default_value )));
726 		}
727 	}
728 
729 	return( value );
730 }
731 
732 /**
733  * na_settings_set_boolean:
734  * @key: the key whose value is to be returned.
735  * @value: the boolean to be written.
736  *
737  * This function writes @value as a user preference.
738  *
739  * This function should only be called for unambiguous keys; the resultat
740  * is otherwise undefined (and rather unpredictable).
741  *
742  * Returns: %TRUE is the writing has been successful, %FALSE else.
743  *
744  * Since: 3.1
745  */
746 gboolean
na_settings_set_boolean(const gchar * key,gboolean value)747 na_settings_set_boolean( const gchar *key, gboolean value )
748 {
749 	gchar *string;
750 	gboolean ok;
751 
752 	string = g_strdup_printf( "%s", value ? "true" : "false" );
753 	ok = set_key_value( NULL, key, string );
754 	g_free( string );
755 
756 	return( ok );
757 }
758 
759 /**
760  * na_settings_set_boolean_ex:
761  * @group: the group in the keyed file;
762  * @key: the key whose value is to be returned.
763  * @value: the boolean to be written.
764  *
765  * This function writes @value as a user preference.
766  *
767  * Returns: %TRUE is the writing has been successful, %FALSE else.
768  *
769  * Since: 3.1
770  */
771 gboolean
na_settings_set_boolean_ex(const gchar * group,const gchar * key,gboolean value)772 na_settings_set_boolean_ex( const gchar *group, const gchar *key, gboolean value )
773 {
774 	gchar *string;
775 	gboolean ok;
776 
777 	string = g_strdup_printf( "%s", value ? "true" : "false" );
778 	ok = set_key_value( group, key, string );
779 	g_free( string );
780 
781 	return( ok );
782 }
783 
784 /**
785  * na_settings_set_string:
786  * @key: the key whose value is to be returned.
787  * @value: the string to be written.
788  *
789  * This function writes @value as a user preference.
790  *
791  * This function should only be called for unambiguous keys; the resultat
792  * is otherwise undefined (and rather unpredictable).
793  *
794  * Returns: %TRUE is the writing has been successful, %FALSE else.
795  *
796  * Since: 3.1
797  */
798 gboolean
na_settings_set_string(const gchar * key,const gchar * value)799 na_settings_set_string( const gchar *key, const gchar *value )
800 {
801 	return( set_key_value( NULL, key, value ));
802 }
803 
804 /**
805  * na_settings_set_string_ex:
806  * @group: the group in the keyed file;
807  * @key: the key whose value is to be returned.
808  * @value: the string to be written.
809  *
810  * This function writes @value as a user preference.
811  *
812  * Returns: %TRUE is the writing has been successful, %FALSE else.
813  *
814  * Since: 3.2
815  */
816 gboolean
na_settings_set_string_ex(const gchar * group,const gchar * key,const gchar * value)817 na_settings_set_string_ex( const gchar *group, const gchar *key, const gchar *value )
818 {
819 	return( set_key_value( group, key, value ));
820 }
821 
822 /**
823  * na_settings_set_string_list:
824  * @key: the key whose value is to be returned.
825  * @value: the list of strings to be written.
826  *
827  * This function writes @value as a user preference.
828  *
829  * This function should only be called for unambiguous keys; the resultat
830  * is otherwise undefined (and rather unpredictable).
831  *
832  * Returns: %TRUE is the writing has been successful, %FALSE else.
833  *
834  * Since: 3.1
835  */
836 gboolean
na_settings_set_string_list(const gchar * key,const GSList * value)837 na_settings_set_string_list( const gchar *key, const GSList *value )
838 {
839 	GString *string;
840 	const GSList *it;
841 	gboolean ok;
842 
843 	string = g_string_new( "" );
844 	for( it = value ; it ; it = it->next ){
845 		g_string_append_printf( string, "%s;", ( const gchar * ) it->data );
846 	}
847 	ok = set_key_value( NULL, key, string->str );
848 	g_string_free( string, TRUE );
849 
850 	return( ok );
851 }
852 
853 /**
854  * na_settings_set_int_ex:
855  * @group: the group in the keyed file;
856  * @key: the key whose value is to be returned.
857  * @value: the unsigned integer to be written.
858  *
859  * This function writes @value as a user preference.
860  *
861  * Returns: %TRUE is the writing has been successful, %FALSE else.
862  *
863  * Since: 3.2
864  */
865 gboolean
na_settings_set_int_ex(const gchar * group,const gchar * key,int value)866 na_settings_set_int_ex( const gchar *group, const gchar *key, int value )
867 {
868 	gchar *string;
869 	gboolean ok;
870 
871 	string = g_strdup_printf( "%d", value );
872 	ok = set_key_value( group, key, string );
873 	g_free( string );
874 
875 	return( ok );
876 }
877 
878 /**
879  * na_settings_set_uint:
880  * @key: the key whose value is to be returned.
881  * @value: the unsigned integer to be written.
882  *
883  * This function writes @value as a user preference.
884  *
885  * This function should only be called for unambiguous keys; the resultat
886  * is otherwise undefined (and rather unpredictable).
887  *
888  * Returns: %TRUE is the writing has been successful, %FALSE else.
889  *
890  * Since: 3.1
891  */
892 gboolean
na_settings_set_uint(const gchar * key,guint value)893 na_settings_set_uint( const gchar *key, guint value )
894 {
895 	gchar *string;
896 	gboolean ok;
897 
898 	string = g_strdup_printf( "%u", value );
899 	ok = set_key_value( NULL, key, string );
900 	g_free( string );
901 
902 	return( ok );
903 }
904 
905 /**
906  * na_settings_set_uint_list:
907  * @key: the key whose value is to be returned.
908  * @value: the list of unsigned integers to be written.
909  *
910  * This function writes @value as a user preference.
911  *
912  * This function should only be called for unambiguous keys; the resultat
913  * is otherwise undefined (and rather unpredictable).
914  *
915  * Returns: %TRUE is the writing has been successful, %FALSE else.
916  *
917  * Since: 3.1
918  */
919 gboolean
na_settings_set_uint_list(const gchar * key,const GList * value)920 na_settings_set_uint_list( const gchar *key, const GList *value )
921 {
922 	GString *string;
923 	const GList *it;
924 	gboolean ok;
925 
926 	string = g_string_new( "" );
927 	for( it = value ; it ; it = it->next ){
928 		g_string_append_printf( string, "%u;", GPOINTER_TO_UINT( it->data ));
929 	}
930 	ok = set_key_value( NULL, key, string->str );
931 	g_string_free( string, TRUE );
932 
933 	return( ok );
934 }
935 
936 /**
937  * na_settings_get_groups:
938  *
939  * Returns: the list of groups in the configuration; this list should be
940  * na_core_utils_slist_free() by the caller.
941  *
942  * This function participates to a rather bad hack to obtain the list of
943  * known i/o providers from preferences. We do not care of returning unique
944  * or sorted group names.
945  *
946  * Since: 3.1
947  */
948 GSList *
na_settings_get_groups(void)949 na_settings_get_groups( void )
950 {
951 	GSList *groups;
952 	gchar **array;
953 
954 	groups = NULL;
955 	settings_new();
956 
957 	array = g_key_file_get_groups( st_settings->private->mandatory->key_file, NULL );
958 	if( array ){
959 		groups = na_core_utils_slist_from_array(( const gchar ** ) array );
960 		g_strfreev( array );
961 	}
962 
963 	array = g_key_file_get_groups( st_settings->private->user->key_file, NULL );
964 	if( array ){
965 		groups = g_slist_concat( groups, na_core_utils_slist_from_array(( const gchar ** ) array ));
966 		g_strfreev( array );
967 	}
968 
969 	return( groups );
970 }
971 
972 /*
973  * returns a list of modified KeyValue
974  * - order in the lists is not signifiant
975  * - the mandatory flag is not signifiant
976  * - a key is modified:
977  *   > if it appears in new
978  *   > if it disappears: the value is so reset to its default
979  *   > if the value has been modified
980  *
981  * we return here a new list, with newly allocated KeyValue structs
982  * which hold the new value of each modified key
983  */
984 static GList *
content_diff(GList * old,GList * new)985 content_diff( GList *old, GList *new )
986 {
987 	GList *diffs, *io, *in;
988 	KeyValue *kold, *knew, *kdiff;
989 	gboolean found;
990 
991 	diffs = NULL;
992 
993 	for( io = old ; io ; io = io->next ){
994 		kold = ( KeyValue * ) io->data;
995 		found = FALSE;
996 		for( in = new ; in && !found ; in = in->next ){
997 			knew = ( KeyValue * ) in->data;
998 			if( !strcmp( kold->group, knew->group ) && ( gpointer ) kold->def == ( gpointer ) knew->def ){
999 				found = TRUE;
1000 				if( !na_boxed_are_equal( kold->boxed, knew->boxed )){
1001 					/* a key has been modified */
1002 					kdiff = g_new0( KeyValue, 1 );
1003 					kdiff->group = g_strdup( knew->group );
1004 					kdiff->def = knew->def;
1005 					kdiff->mandatory = knew->mandatory;
1006 					kdiff->boxed = na_boxed_copy( knew->boxed );
1007 					diffs = g_list_prepend( diffs, kdiff );
1008 				}
1009 			}
1010 		}
1011 		if( !found ){
1012 			/* a key has disappeared */
1013 			kdiff = g_new0( KeyValue, 1 );
1014 			kdiff->group = g_strdup( kold->group );
1015 			kdiff->def = kold->def;
1016 			kdiff->mandatory = FALSE;
1017 			kdiff->boxed = na_boxed_new_from_string( kold->def->type, kold->def->default_value );
1018 			diffs = g_list_prepend( diffs, kdiff );
1019 		}
1020 	}
1021 
1022 	for( in = new ; in ; in = in->next ){
1023 		knew = ( KeyValue * ) in->data;
1024 		found = FALSE;
1025 		for( io = old ; io && !found ; io = io->next ){
1026 			kold = ( KeyValue * ) io->data;
1027 			if( !strcmp( kold->group, knew->group ) && ( gpointer ) kold->def == ( gpointer ) knew->def ){
1028 				found = TRUE;
1029 			}
1030 		}
1031 		if( !found ){
1032 			/* a key is new */
1033 			kdiff = g_new0( KeyValue, 1 );
1034 			kdiff->group = g_strdup( knew->group );
1035 			kdiff->def = knew->def;
1036 			kdiff->mandatory = knew->mandatory;
1037 			kdiff->boxed = na_boxed_copy( knew->boxed );
1038 			diffs = g_list_prepend( diffs, kdiff );
1039 		}
1040 	}
1041 
1042 	return( diffs );
1043 }
1044 
1045 /* add the content of a configuration files to those already loaded
1046  *
1047  * when the two configuration files have been read, then the content of
1048  * _the_ configuration has been loaded, while preserving the mandatory
1049  * keys
1050  */
1051 static GList *
content_load_keys(GList * content,KeyFile * keyfile)1052 content_load_keys( GList *content, KeyFile *keyfile )
1053 {
1054 	static const gchar *thisfn = "na_settings_content_load_keys";
1055 	GError *error;
1056 	gchar **groups, **ig;
1057 	gchar **keys, **ik;
1058 	KeyValue *key_value;
1059 	KeyDef *key_def;
1060 
1061 	error = NULL;
1062 	if( !g_key_file_load_from_file( keyfile->key_file, keyfile->fname, G_KEY_FILE_KEEP_COMMENTS, &error )){
1063 		if( error->code != G_FILE_ERROR_NOENT ){
1064 			g_warning( "%s: %s (%d) %s", thisfn, keyfile->fname, error->code, error->message );
1065 		} else {
1066 			g_debug( "%s: %s: file doesn't exist", thisfn, keyfile->fname );
1067 		}
1068 		g_error_free( error );
1069 		error = NULL;
1070 
1071 	} else {
1072 		groups = g_key_file_get_groups( keyfile->key_file, NULL );
1073 		ig = groups;
1074 		while( *ig ){
1075 			keys = g_key_file_get_keys( keyfile->key_file, *ig, NULL, NULL );
1076 			ik = keys;
1077 			while( *ik ){
1078 				key_def = get_key_def( *ik );
1079 				if( key_def ){
1080 					key_value = peek_key_value_from_content( content, *ig, *ik );
1081 					if( !key_value ){
1082 						key_value = read_key_value_from_key_file( keyfile, *ig, *ik, key_def );
1083 						if( key_value ){
1084 							key_value->mandatory = keyfile->mandatory;
1085 							content = g_list_prepend( content, key_value );
1086 						}
1087 					}
1088 				}
1089 				ik++;
1090 			}
1091 			g_strfreev( keys );
1092 			ig++;
1093 		}
1094 		g_strfreev( groups );
1095 	}
1096 
1097 	return( content );
1098 }
1099 
1100 static KeyDef *
get_key_def(const gchar * key)1101 get_key_def( const gchar *key )
1102 {
1103 	static const gchar *thisfn = "na_settings_get_key_def";
1104 	KeyDef *found = NULL;
1105 	KeyDef *idef;
1106 
1107 	idef = ( KeyDef * ) st_def_keys;
1108 	while( idef->key && !found ){
1109 		if( !strcmp( idef->key, key )){
1110 			found = idef;
1111 		}
1112 		idef++;
1113 	}
1114 	if( !found ){
1115 		g_warning( "%s: no KeyDef found for key=%s", thisfn, key );
1116 	}
1117 
1118 	return( found );
1119 }
1120 
1121 /*
1122  * called from na_settings_new
1123  * allocate and load the key files for global and user preferences
1124  */
1125 static KeyFile *
key_file_new(const gchar * dir)1126 key_file_new( const gchar *dir )
1127 {
1128 	static const gchar *thisfn = "na_settings_key_file_new";
1129 	KeyFile *keyfile;
1130 	GError *error;
1131 	GFile *file;
1132 
1133 	keyfile = g_new0( KeyFile, 1 );
1134 
1135 	keyfile->key_file = g_key_file_new();
1136 	keyfile->fname = g_strdup_printf( "%s/%s.conf", dir, PACKAGE );
1137 	na_core_utils_file_list_perms( keyfile->fname, thisfn );
1138 
1139 	error = NULL;
1140 	file = g_file_new_for_path( keyfile->fname );
1141 	keyfile->monitor = g_file_monitor_file( file, 0, NULL, &error );
1142 	if( error ){
1143 		g_warning( "%s: %s: %s", thisfn, keyfile->fname, error->message );
1144 		g_error_free( error );
1145 		error = NULL;
1146 	} else {
1147 		keyfile->handler = g_signal_connect( keyfile->monitor, "changed", ( GCallback ) on_keyfile_changed, NULL );
1148 	}
1149 	g_object_unref( file );
1150 
1151 	return( keyfile );
1152 }
1153 
1154 /*
1155  * one of the two monitored configuration files have changed on the disk
1156  * we do not try to identify which keys have actually change
1157  * instead we trigger each registered consumer for the 'global' event
1158  *
1159  * consumers which register for the 'global_conf' event are recorded
1160  * with a NULL key
1161  */
1162 static void
on_keyfile_changed(GFileMonitor * monitor,GFile * file,GFile * other_file,GFileMonitorEvent event_type)1163 on_keyfile_changed( GFileMonitor *monitor,
1164 		GFile *file, GFile *other_file, GFileMonitorEvent event_type )
1165 {
1166 	settings_new();
1167 	na_timeout_event( &st_settings->private->timeout );
1168 }
1169 
1170 static void
on_keyfile_changed_timeout(void)1171 on_keyfile_changed_timeout( void )
1172 {
1173 	static const gchar *thisfn = "na_settings_on_keyfile_changed_timeout";
1174 	GList *new_content;
1175 	GList *modifs;
1176 	GList *ic, *im;
1177 	const KeyValue *changed;
1178 	const Consumer *consumer;
1179 	gchar *group_prefix, *key;
1180 #ifdef NA_MAINTAINER_MODE
1181 	gchar *value;
1182 #endif
1183 
1184 	/* last individual notification is older that the st_burst_timeout
1185 	 * we may so suppose that the burst is terminated
1186 	 */
1187 	new_content = content_load_keys( NULL, st_settings->private->mandatory );
1188 	new_content = content_load_keys( new_content, st_settings->private->user );
1189 	modifs = content_diff( st_settings->private->content, new_content );
1190 
1191 #ifdef NA_MAINTAINER_MODE
1192 	g_debug( "%s: %d found update(s)", thisfn, g_list_length( modifs ));
1193 	for( im = modifs ; im ; im = im->next ){
1194 		changed = ( const KeyValue * ) im->data;
1195 		value = na_boxed_get_string( changed->boxed );
1196 		g_debug( "%s: group=%s, key=%s, value=%s", thisfn, changed->group, changed->def->key, value );
1197 		g_free( value );
1198 	}
1199 #endif
1200 
1201 	/* for each modification found,
1202 	 * - check if a consumer has registered for this key, and triggers callback if apply
1203 	 * - send a notification message
1204 	 */
1205 	for( im = modifs ; im ; im = im->next ){
1206 		changed = ( const KeyValue * ) im->data;
1207 
1208 		for( ic = st_settings->private->consumers ; ic ; ic = ic->next ){
1209 			consumer = ( const Consumer * ) ic->data;
1210 			group_prefix = NULL;
1211 
1212 			if( !strcmp( consumer->monitored_key, NA_IPREFS_IO_PROVIDERS_READ_STATUS )){
1213 				group_prefix = g_strdup_printf( "%s ", NA_IPREFS_IO_PROVIDER_GROUP );
1214 				key = NA_IPREFS_IO_PROVIDER_READABLE;
1215 			} else {
1216 				key = consumer->monitored_key;
1217 			}
1218 
1219 			if(( !group_prefix || g_str_has_prefix( changed->group, group_prefix )) && !strcmp( changed->def->key, key )){
1220 				( *( NASettingsKeyCallback ) consumer->callback )(
1221 						changed->group,
1222 						changed->def->key,
1223 						na_boxed_get_pointer( changed->boxed ),
1224 						changed->mandatory,
1225 						consumer->user_data );
1226 			}
1227 
1228 			g_free( group_prefix );
1229 		}
1230 
1231 		g_debug( "%s: sending signal for group=%s, key=%s", thisfn, changed->group, changed->def->key );
1232 		g_signal_emit_by_name( st_settings,
1233 				SETTINGS_SIGNAL_KEY_CHANGED,
1234 				changed->group, changed->def->key, changed->boxed, changed->mandatory );
1235 	}
1236 
1237 	g_debug( "%s: releasing content", thisfn );
1238 	g_list_foreach( st_settings->private->content, ( GFunc ) release_key_value, NULL );
1239 	g_list_free( st_settings->private->content );
1240 	st_settings->private->content = new_content;
1241 
1242 	g_debug( "%s: releasing modifs", thisfn );
1243 	g_list_foreach( modifs, ( GFunc ) release_key_value, NULL );
1244 	g_list_free( modifs );
1245 }
1246 
1247 static void
on_key_changed_final_handler(NASettings * settings,gchar * group,gchar * key,NABoxed * new_value,gboolean mandatory)1248 on_key_changed_final_handler( NASettings *settings, gchar *group, gchar *key, NABoxed *new_value, gboolean mandatory )
1249 {
1250 	g_debug( "na_settings_on_key_changed_final_handler: group=%s, key=%s", group, key );
1251 	na_boxed_dump( new_value );
1252 }
1253 
1254 static KeyValue *
peek_key_value_from_content(GList * content,const gchar * group,const gchar * key)1255 peek_key_value_from_content( GList *content, const gchar *group, const gchar *key )
1256 {
1257 	KeyValue *value, *found;
1258 	GList *ic;
1259 
1260 	found = NULL;
1261 	for( ic = content ; ic && !found ; ic = ic->next ){
1262 		value = ( KeyValue * ) ic->data;
1263 		if( !strcmp( value->group, group ) && !strcmp( value->def->key, key )){
1264 			found = value;
1265 		}
1266 	}
1267 
1268 	return( found );
1269 }
1270 
1271 /* group may be NULL
1272  */
1273 static KeyValue *
read_key_value(const gchar * group,const gchar * key,gboolean * found,gboolean * mandatory)1274 read_key_value( const gchar *group, const gchar *key, gboolean *found, gboolean *mandatory )
1275 {
1276 	static const gchar *thisfn = "na_settings_read_key_value";
1277 	KeyDef *key_def;
1278 	gboolean has_entry;
1279 	KeyValue *key_value;
1280 
1281 	key_value = NULL;
1282 	if( found ){
1283 		*found = FALSE;
1284 	}
1285 	if( mandatory ){
1286 		*mandatory = FALSE;
1287 	}
1288 
1289 	settings_new();
1290 	key_def = get_key_def( key );
1291 
1292 	if( key_def ){
1293 		has_entry = FALSE;
1294 		key_value = read_key_value_from_key_file( st_settings->private->mandatory, group ? group : key_def->group, key, key_def );
1295 		if( key_value ){
1296 			has_entry = TRUE;
1297 			if( found ){
1298 				*found = TRUE;
1299 			}
1300 			if( mandatory ){
1301 				*mandatory = TRUE;
1302 				g_debug( "%s: %s: key is mandatory", thisfn, key );
1303 			}
1304 		}
1305 		if( !has_entry ){
1306 			key_value = read_key_value_from_key_file( st_settings->private->user, group ? group : key_def->group, key, key_def );
1307 			if( key_value ){
1308 				has_entry = TRUE;
1309 				if( found ){
1310 					*found = TRUE;
1311 				}
1312 			}
1313 		}
1314 	}
1315 
1316 	return( key_value );
1317 }
1318 
1319 static KeyValue *
read_key_value_from_key_file(KeyFile * keyfile,const gchar * group,const gchar * key,const KeyDef * key_def)1320 read_key_value_from_key_file( KeyFile *keyfile, const gchar *group, const gchar *key, const KeyDef *key_def )
1321 {
1322 	static const gchar *thisfn = "na_settings_read_key_value_from_key_file";
1323 	KeyValue *value;
1324 	gchar *str;
1325 	GError *error;
1326 
1327 	value = NULL;
1328 	error = NULL;
1329 	str = NULL;
1330 
1331 	switch( key_def->type ){
1332 
1333 		case NA_DATA_TYPE_STRING:
1334 		case NA_DATA_TYPE_STRING_LIST:
1335 		case NA_DATA_TYPE_UINT:
1336 		case NA_DATA_TYPE_UINT_LIST:
1337 		case NA_DATA_TYPE_BOOLEAN:
1338 			str = g_key_file_get_string( keyfile->key_file, group, key, &error );
1339 			if( error ){
1340 				if( error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND && error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND ){
1341 					g_warning( "%s: %s", thisfn, error->message );
1342 				}
1343 				g_error_free( error );
1344 
1345 			/* key exists, but may be empty */
1346 			} else {
1347 				value = g_new0( KeyValue, 1 );
1348 				value->group = g_strdup( group );
1349 				value->def = key_def;
1350 				switch( key_def->type ){
1351 					case NA_DATA_TYPE_STRING:
1352 					case NA_DATA_TYPE_UINT:
1353 					case NA_DATA_TYPE_BOOLEAN:
1354 					case NA_DATA_TYPE_STRING_LIST:
1355 					case NA_DATA_TYPE_UINT_LIST:
1356 						value->boxed = na_boxed_new_from_string( key_def->type, str );
1357 						break;
1358 				}
1359 			}
1360 			break;
1361 
1362 		default:
1363 			g_warning( "%s: group=%s, key=%s - unmanaged boxed type: %d", thisfn, group, key, key_def->type );
1364 			return( NULL );
1365 	}
1366 
1367 	if( value ){
1368 		g_debug( "%s: group=%s, key=%s, value=%s, mandatory=%s",
1369 				thisfn, group, key, str, keyfile->mandatory ? "True":"False" );
1370 	}
1371 
1372 	g_free( str );
1373 
1374 	return( value );
1375 }
1376 
1377 /*
1378  * called from instance_finalize
1379  * release the list of registered consumers
1380  */
1381 static void
release_consumer(Consumer * consumer)1382 release_consumer( Consumer *consumer )
1383 {
1384 	g_free( consumer->monitored_key );
1385 	g_free( consumer );
1386 }
1387 
1388 /*
1389  * called from instance_dispose
1390  * release the opened and monitored GKeyFiles
1391  */
1392 static void
release_key_file(KeyFile * key_file)1393 release_key_file( KeyFile *key_file )
1394 {
1395 	g_key_file_free( key_file->key_file );
1396 	if( key_file->monitor ){
1397 		if( key_file->handler ){
1398 			g_signal_handler_disconnect( key_file->monitor, key_file->handler );
1399 		}
1400 		g_file_monitor_cancel( key_file->monitor );
1401 		g_object_unref( key_file->monitor );
1402 	}
1403 	g_free( key_file->fname );
1404 	g_free( key_file );
1405 }
1406 
1407 /*
1408  * called from instance_finalize
1409  * release a KeyValue struct
1410  */
1411 static void
release_key_value(KeyValue * value)1412 release_key_value( KeyValue *value )
1413 {
1414 	g_free(( gpointer ) value->group );
1415 	g_object_unref( value->boxed );
1416 	g_free( value );
1417 }
1418 
1419 static gboolean
set_key_value(const gchar * group,const gchar * key,const gchar * string)1420 set_key_value( const gchar *group, const gchar *key, const gchar *string )
1421 {
1422 	static const gchar *thisfn = "na_settings_set_key_value";
1423 	KeyDef *key_def;
1424 	const gchar *wgroup;
1425 	gboolean ok;
1426 	GError *error;
1427 
1428 	ok = FALSE;
1429 	settings_new();
1430 
1431 	wgroup = group;
1432 	if( !wgroup ){
1433 		key_def = get_key_def( key );
1434 		if( key_def ){
1435 			wgroup = key_def->group;
1436 		}
1437 	}
1438 	if( wgroup ){
1439 		ok = TRUE;
1440 
1441 		if( string ){
1442 			g_key_file_set_string( st_settings->private->user->key_file, wgroup, key, string );
1443 
1444 		} else {
1445 			error = NULL;
1446 			ok = g_key_file_remove_key( st_settings->private->user->key_file, wgroup, key, &error );
1447 			if( error ){
1448 				g_warning( "%s: g_key_file_remove_key: %s", thisfn, error->message );
1449 				g_error_free( error );
1450 			}
1451 		}
1452 
1453 		ok &= write_user_key_file();
1454 	}
1455 
1456 	return( ok );
1457 }
1458 
1459 static gboolean
write_user_key_file(void)1460 write_user_key_file( void )
1461 {
1462 	static const gchar *thisfn = "na_settings_write_user_key_file";
1463 	gchar *data;
1464 	GFile *file;
1465 	GFileOutputStream *stream;
1466 	GError *error;
1467 	gsize length;
1468 
1469 	error = NULL;
1470 	settings_new();
1471 	data = g_key_file_to_data( st_settings->private->user->key_file, &length, NULL );
1472 	file = g_file_new_for_path( st_settings->private->user->fname );
1473 
1474 	stream = g_file_replace( file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error );
1475 	if( error ){
1476 		g_warning( "%s: g_file_replace: %s", thisfn, error->message );
1477 		g_error_free( error );
1478 		if( stream ){
1479 			g_object_unref( stream );
1480 		}
1481 		g_object_unref( file );
1482 		g_free( data );
1483 		return( FALSE );
1484 	}
1485 
1486 	g_output_stream_write( G_OUTPUT_STREAM( stream ), data, length, NULL, &error );
1487 	if( error ){
1488 		g_warning( "%s: g_output_stream_write: %s", thisfn, error->message );
1489 		g_error_free( error );
1490 		g_object_unref( stream );
1491 		g_object_unref( file );
1492 		g_free( data );
1493 		return( FALSE );
1494 	}
1495 
1496 	g_output_stream_close( G_OUTPUT_STREAM( stream ), NULL, &error );
1497 	if( error ){
1498 		g_warning( "%s: g_output_stream_close: %s", thisfn, error->message );
1499 		g_error_free( error );
1500 		g_object_unref( stream );
1501 		g_object_unref( file );
1502 		g_free( data );
1503 		return( FALSE );
1504 	}
1505 
1506 	g_object_unref( stream );
1507 	g_object_unref( file );
1508 	g_free( data );
1509 
1510 	return( TRUE );
1511 }
1512