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 <string.h>
35 
36 #include <api/na-data-def.h>
37 #include <api/na-data-types.h>
38 #include <api/na-ifactory-provider.h>
39 #include <api/na-iio-provider.h>
40 #include <api/na-object-api.h>
41 #include <api/na-core-utils.h>
42 #include <api/na-gconf-utils.h>
43 
44 #include "nagp-gconf-provider.h"
45 #include "nagp-keys.h"
46 #include "nagp-reader.h"
47 
48 typedef struct {
49 	gchar        *path;
50 	GSList       *entries;
51 	NAObjectItem *parent;
52 }
53 	ReaderData;
54 
55 static NAObjectItem *read_item( NagpGConfProvider *provider, const gchar *path, GSList **messages );
56 
57 static void          read_start_profile_attach_profile( const NAIFactoryProvider *provider, NAObjectProfile *profile, ReaderData *data, GSList **messages );
58 
59 static gboolean      read_done_item_is_writable( const NAIFactoryProvider *provider, NAObjectItem *item, ReaderData *data, GSList **messages );
60 static void          read_done_action_read_profiles( const NAIFactoryProvider *provider, NAObjectAction *action, ReaderData *data, GSList **messages );
61 static void          read_done_action_load_profile( const NAIFactoryProvider *provider, ReaderData *data, const gchar *path, GSList **messages );
62 
63 static NADataBoxed  *get_boxed_from_path( const NagpGConfProvider *provider, const gchar *path, ReaderData *reader_data, const NADataDef *def );
64 static gboolean      is_key_writable( NagpGConfProvider *gconf, const gchar *key );
65 
66 /*
67  * nagp_iio_provider_read_items:
68  *
69  * Note that whatever be the version of the read action, it will be
70  * stored as a #NAObjectAction and its set of #NAObjectProfile of the same,
71  * latest, version of these classes.
72  */
73 GList *
nagp_iio_provider_read_items(const NAIIOProvider * provider,GSList ** messages)74 nagp_iio_provider_read_items( const NAIIOProvider *provider, GSList **messages )
75 {
76 	static const gchar *thisfn = "nagp_reader_nagp_iio_provider_read_items";
77 	NagpGConfProvider *self;
78 	GList *items_list = NULL;
79 	GSList *listpath, *ip;
80 	NAObjectItem *item;
81 
82 	g_debug( "%s: provider=%p, messages=%p", thisfn, ( void * ) provider, ( void * ) messages );
83 
84 	g_return_val_if_fail( NA_IS_IIO_PROVIDER( provider ), NULL );
85 	g_return_val_if_fail( NAGP_IS_GCONF_PROVIDER( provider ), NULL );
86 	self = NAGP_GCONF_PROVIDER( provider );
87 
88 	if( !self->private->dispose_has_run ){
89 
90 		listpath = na_gconf_utils_get_subdirs( self->private->gconf, NAGP_CONFIGURATIONS_PATH );
91 
92 		for( ip = listpath ; ip ; ip = ip->next ){
93 
94 			item = read_item( self, ( const gchar * ) ip->data, messages );
95 			if( item ){
96 				items_list = g_list_prepend( items_list, item );
97 				na_object_dump( item );
98 			}
99 		}
100 
101 		na_gconf_utils_free_subdirs( listpath );
102 	}
103 
104 	g_debug( "%s: count=%d", thisfn, g_list_length( items_list ));
105 	return( items_list );
106 }
107 
108 /*
109  * path is here the full path to an item
110  */
111 static NAObjectItem *
read_item(NagpGConfProvider * provider,const gchar * path,GSList ** messages)112 read_item( NagpGConfProvider *provider, const gchar *path, GSList **messages )
113 {
114 	static const gchar *thisfn = "nagp_reader_read_item";
115 	NAObjectItem *item;
116 	gchar *full_path;
117 	gchar *type;
118 	gchar *id;
119 	ReaderData *data;
120 
121 	g_debug( "%s: provider=%p, path=%s", thisfn, ( void * ) provider, path );
122 	g_return_val_if_fail( NAGP_IS_GCONF_PROVIDER( provider ), NULL );
123 	g_return_val_if_fail( NA_IS_IIO_PROVIDER( provider ), NULL );
124 	g_return_val_if_fail( !provider->private->dispose_has_run, NULL );
125 
126 	full_path = gconf_concat_dir_and_key( path, NAGP_ENTRY_TYPE );
127 	type = na_gconf_utils_read_string( provider->private->gconf, full_path, TRUE, NAGP_VALUE_TYPE_ACTION );
128 	g_free( full_path );
129 	item = NULL;
130 
131 	/* an item may have 'Action' or 'Menu' type; defaults to Action
132 	 */
133 	if( !type || !strlen( type ) || !strcmp( type, NAGP_VALUE_TYPE_ACTION )){
134 		item = NA_OBJECT_ITEM( na_object_action_new());
135 
136 	} else if( !strcmp( type, NAGP_VALUE_TYPE_MENU )){
137 		item = NA_OBJECT_ITEM( na_object_menu_new());
138 
139 	} else {
140 		g_warning( "%s: unknown type '%s' at %s", thisfn, type, path );
141 	}
142 
143 	g_free( type );
144 
145 	if( item ){
146 		id = g_path_get_basename( path );
147 		na_object_set_id( item, id );
148 		g_free( id );
149 
150 		data = g_new0( ReaderData, 1 );
151 		data->path = ( gchar * ) path;
152 		data->entries = na_gconf_utils_get_entries( provider->private->gconf, path );
153 		na_gconf_utils_dump_entries( data->entries );
154 
155 		na_ifactory_provider_read_item(
156 				NA_IFACTORY_PROVIDER( provider ),
157 				data,
158 				NA_IFACTORY_OBJECT( item ),
159 				messages );
160 
161 		na_gconf_utils_free_entries( data->entries );
162 		g_free( data );
163 	}
164 
165 	return( item );
166 }
167 
168 void
nagp_reader_read_start(const NAIFactoryProvider * provider,void * reader_data,const NAIFactoryObject * object,GSList ** messages)169 nagp_reader_read_start( const NAIFactoryProvider *provider, void *reader_data, const NAIFactoryObject *object, GSList **messages  )
170 {
171 	static const gchar *thisfn = "nagp_reader_read_start";
172 
173 	g_return_if_fail( NA_IS_IFACTORY_PROVIDER( provider ));
174 	g_return_if_fail( NAGP_IS_GCONF_PROVIDER( provider ));
175 	g_return_if_fail( NA_IS_IFACTORY_OBJECT( object ));
176 
177 	if( !NAGP_GCONF_PROVIDER( provider )->private->dispose_has_run ){
178 
179 		g_debug( "%s: provider=%p (%s), reader_data=%p, object=%p (%s), messages=%p",
180 				thisfn,
181 				( void * ) provider, G_OBJECT_TYPE_NAME( provider ),
182 				( void * ) reader_data,
183 				( void * ) object, G_OBJECT_TYPE_NAME( object ),
184 				( void * ) messages );
185 
186 		if( NA_IS_OBJECT_PROFILE( object )){
187 			read_start_profile_attach_profile( provider, NA_OBJECT_PROFILE( object ), ( ReaderData * ) reader_data, messages );
188 		}
189 	}
190 }
191 
192 static void
read_start_profile_attach_profile(const NAIFactoryProvider * provider,NAObjectProfile * profile,ReaderData * data,GSList ** messages)193 read_start_profile_attach_profile( const NAIFactoryProvider *provider, NAObjectProfile *profile, ReaderData *data, GSList **messages )
194 {
195 	na_object_attach_profile( data->parent, profile );
196 }
197 
198 NADataBoxed *
nagp_reader_read_data(const NAIFactoryProvider * provider,void * reader_data,const NAIFactoryObject * object,const NADataDef * def,GSList ** messages)199 nagp_reader_read_data( const NAIFactoryProvider *provider, void *reader_data, const NAIFactoryObject *object, const NADataDef *def, GSList **messages )
200 {
201 	static const gchar *thisfn = "nagp_reader_read_data";
202 	NADataBoxed *boxed;
203 
204 	g_return_val_if_fail( NA_IS_IFACTORY_PROVIDER( provider ), NULL );
205 	g_return_val_if_fail( NA_IS_IFACTORY_OBJECT( object ), NULL );
206 
207 	/*g_debug( "%s: reader_data=%p, object=%p (%s), data=%s",
208 			thisfn,
209 			( void * ) reader_data,
210 			( void * ) object, G_OBJECT_TYPE_NAME( object ),
211 			def->name );*/
212 
213 	if( !def->gconf_entry || !strlen( def->gconf_entry )){
214 		g_warning( "%s: GConf entry is not set for NADataDef %s", thisfn, def->name );
215 		return( NULL );
216 	}
217 
218 	boxed = get_boxed_from_path(
219 			NAGP_GCONF_PROVIDER( provider ), (( ReaderData * ) reader_data )->path, reader_data, def );
220 
221 	return( boxed );
222 }
223 
224 void
nagp_reader_read_done(const NAIFactoryProvider * provider,void * reader_data,const NAIFactoryObject * object,GSList ** messages)225 nagp_reader_read_done( const NAIFactoryProvider *provider, void *reader_data, const NAIFactoryObject *object, GSList **messages  )
226 {
227 	static const gchar *thisfn = "nagp_reader_read_done";
228 	gboolean writable;
229 
230 	g_return_if_fail( NA_IS_IFACTORY_PROVIDER( provider ));
231 	g_return_if_fail( NAGP_IS_GCONF_PROVIDER( provider ));
232 	g_return_if_fail( NA_IS_IFACTORY_OBJECT( object ));
233 
234 	if( !NAGP_GCONF_PROVIDER( provider )->private->dispose_has_run ){
235 
236 		g_debug( "%s: provider=%p (%s), reader_data=%p, object=%p (%s), messages=%p",
237 				thisfn,
238 				( void * ) provider, G_OBJECT_TYPE_NAME( provider ),
239 				( void * ) reader_data,
240 				( void * ) object, G_OBJECT_TYPE_NAME( object ),
241 				( void * ) messages );
242 
243 		if( NA_IS_OBJECT_ITEM( object )){
244 			writable = read_done_item_is_writable( provider, NA_OBJECT_ITEM( object ), ( ReaderData * ) reader_data, messages );
245 			na_object_set_readonly( object, !writable );
246 		}
247 
248 		if( NA_IS_OBJECT_ACTION( object )){
249 			read_done_action_read_profiles( provider, NA_OBJECT_ACTION( object ), ( ReaderData * ) reader_data, messages );
250 		}
251 
252 		g_debug( "%s: quitting for %s at %p", thisfn, G_OBJECT_TYPE_NAME( object ), ( void * ) object );
253 	}
254 }
255 
256 static gboolean
read_done_item_is_writable(const NAIFactoryProvider * provider,NAObjectItem * item,ReaderData * data,GSList ** messages)257 read_done_item_is_writable( const NAIFactoryProvider *provider, NAObjectItem *item, ReaderData *data, GSList **messages )
258 {
259 	GSList *ie;
260 	gboolean writable;
261 	GConfEntry *gconf_entry;
262 	const gchar *key;
263 
264 	/* check for writability of this item
265 	 * item is writable if and only if all entries are themselves writable
266 	 */
267 	writable = TRUE;
268 	for( ie = data->entries ; ie && writable ; ie = ie->next ){
269 		gconf_entry = ( GConfEntry * ) ie->data;
270 		key = gconf_entry_get_key( gconf_entry );
271 		writable = is_key_writable( NAGP_GCONF_PROVIDER( provider ), key );
272 	}
273 
274 	g_debug( "nagp_reader_read_done_item: writable=%s", writable ? "True":"False" );
275 	return( writable );
276 }
277 
278 static void
read_done_action_read_profiles(const NAIFactoryProvider * provider,NAObjectAction * action,ReaderData * data,GSList ** messages)279 read_done_action_read_profiles( const NAIFactoryProvider *provider, NAObjectAction *action, ReaderData *data, GSList **messages )
280 {
281 	static const gchar *thisfn = "nagp_reader_read_done_action_read_profiles";
282 	GSList *order;
283 	GSList *list_profiles;
284 	GSList *ip;
285 	gchar *profile_id;
286 	gchar *profile_path;
287 	NAObjectId *found;
288 	NAObjectProfile *profile;
289 
290 	data->parent = NA_OBJECT_ITEM( action );
291 	order = na_object_get_items_slist( action );
292 	list_profiles = na_gconf_utils_get_subdirs( NAGP_GCONF_PROVIDER( provider )->private->gconf, data->path );
293 
294 	/* read profiles in the specified order
295 	 * as a protection against bugs in NACT, we check that profile has not
296 	 * already been loaded
297 	 */
298 	for( ip = order ; ip ; ip = ip->next ){
299 		profile_id = ( gchar * ) ip->data;
300 		found = na_object_get_item( action, profile_id );
301 		if( !found ){
302 			g_debug( "nagp_reader_read_done_action: loading profile=%s", profile_id );
303 			profile_path = gconf_concat_dir_and_key( data->path, profile_id );
304 			read_done_action_load_profile( provider, data, profile_path, messages );
305 			g_free( profile_path );
306 		}
307 	}
308 
309 	/* append other profiles
310 	 * this is mandatory for pre-2.29 actions which introduced order of profiles
311 	 */
312 	for( ip = list_profiles ; ip ; ip = ip->next ){
313 		profile_id = g_path_get_basename(( const gchar * ) ip->data );
314 		found = na_object_get_item( action, profile_id );
315 		if( !found ){
316 			g_debug( "nagp_reader_read_done_action: loading profile=%s", profile_id );
317 			read_done_action_load_profile( provider, data, ( const gchar * ) ip->data, messages );
318 		}
319 		g_free( profile_id );
320 	}
321 
322 	/* make sure we have at least one profile
323 	 */
324 	if( !na_object_get_items_count( action )){
325 		g_warning( "%s: no profile found in GConf backend", thisfn );
326 		profile = na_object_profile_new_with_defaults();
327 		na_object_attach_profile( action, profile );
328 	}
329 }
330 
331 static void
read_done_action_load_profile(const NAIFactoryProvider * provider,ReaderData * data,const gchar * path,GSList ** messages)332 read_done_action_load_profile( const NAIFactoryProvider *provider, ReaderData *data, const gchar *path, GSList **messages )
333 {
334 	gchar *id;
335 	ReaderData *profile_data;
336 
337 	NAObjectProfile *profile = na_object_profile_new();
338 
339 	id = g_path_get_basename( path );
340 	na_object_set_id( profile, id );
341 	g_free( id );
342 
343 	profile_data = g_new0( ReaderData, 1 );
344 	profile_data->parent = data->parent;
345 	profile_data->path = ( gchar * ) path;
346 	profile_data->entries = na_gconf_utils_get_entries( NAGP_GCONF_PROVIDER( provider )->private->gconf, path );
347 
348 	na_ifactory_provider_read_item(
349 			NA_IFACTORY_PROVIDER( provider ),
350 			profile_data,
351 			NA_IFACTORY_OBJECT( profile ),
352 			messages );
353 
354 	na_gconf_utils_free_entries( profile_data->entries );
355 	g_free( profile_data );
356 }
357 
358 static NADataBoxed *
get_boxed_from_path(const NagpGConfProvider * provider,const gchar * path,ReaderData * reader_data,const NADataDef * def)359 get_boxed_from_path( const NagpGConfProvider *provider, const gchar *path, ReaderData *reader_data, const NADataDef *def )
360 {
361 	static const gchar *thisfn = "nagp_reader_get_boxed_from_path";
362 	NADataBoxed *boxed;
363 	gboolean have_entry;
364 	gchar *str_value;
365 	gboolean bool_value;
366 	GSList *slist_value;
367 	gint int_value;
368 
369 	boxed = NULL;
370 	have_entry = na_gconf_utils_has_entry( reader_data->entries, def->gconf_entry );
371 	g_debug( "%s: entry=%s, have_entry=%s", thisfn, def->gconf_entry, have_entry ? "True":"False" );
372 
373 	if( have_entry ){
374 		gchar *entry_path = gconf_concat_dir_and_key( path, def->gconf_entry );
375 		boxed = na_data_boxed_new( def );
376 
377 		switch( def->type ){
378 
379 			case NA_DATA_TYPE_STRING:
380 			case NA_DATA_TYPE_LOCALE_STRING:
381 				str_value = na_gconf_utils_read_string( provider->private->gconf, entry_path, TRUE, NULL );
382 				na_boxed_set_from_string( NA_BOXED( boxed ), str_value );
383 				g_free( str_value );
384 				break;
385 
386 			case NA_DATA_TYPE_BOOLEAN:
387 				bool_value = na_gconf_utils_read_bool( provider->private->gconf, entry_path, TRUE, FALSE );
388 				na_boxed_set_from_void( NA_BOXED( boxed ), GUINT_TO_POINTER( bool_value ));
389 				break;
390 
391 			case NA_DATA_TYPE_STRING_LIST:
392 				slist_value = na_gconf_utils_read_string_list( provider->private->gconf, entry_path );
393 				na_boxed_set_from_void( NA_BOXED( boxed ), slist_value );
394 				na_core_utils_slist_free( slist_value );
395 				break;
396 
397 			case NA_DATA_TYPE_UINT:
398 				int_value = na_gconf_utils_read_int( provider->private->gconf, entry_path, TRUE, 0 );
399 				na_boxed_set_from_void( NA_BOXED( boxed ), GUINT_TO_POINTER( int_value ));
400 				break;
401 
402 			default:
403 				g_warning( "%s: unknown type=%u for %s", thisfn, def->type, def->name );
404 				g_free( boxed );
405 				boxed = NULL;
406 		}
407 
408 		g_free( entry_path );
409 	}
410 
411 	return( boxed );
412 }
413 
414 /*
415  * key must be an existing entry (not a dir) to get a relevant return
416  * value ; else we get FALSE
417  */
418 static gboolean
is_key_writable(NagpGConfProvider * gconf,const gchar * key)419 is_key_writable( NagpGConfProvider *gconf, const gchar *key )
420 {
421 	static const gchar *thisfn = "nagp_read_is_key_writable";
422 	GError *error = NULL;
423 	gboolean is_writable;
424 
425 	is_writable = gconf_client_key_is_writable( gconf->private->gconf, key, &error );
426 	if( error ){
427 		g_warning( "%s: gconf_client_key_is_writable: %s", thisfn, error->message );
428 		g_error_free( error );
429 		error = NULL;
430 		is_writable = FALSE;
431 	}
432 
433 	return( is_writable );
434 }
435