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