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 <glib.h>
35 #include <glib/gi18n.h>
36 #include <locale.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #include <api/na-core-utils.h>
41 #include <api/na-object-api.h>
42 #include <api/na-dbus.h>
43
44 #include <core/na-gconf-migration.h>
45 #include <core/na-pivot.h>
46 #include <core/na-selected-info.h>
47 #include <core/na-tokens.h>
48
49 #include "console-utils.h"
50 #include "nautilus-actions-run-bindings.h"
51
52 static gchar *id = "";
53 static gchar **targets_array = NULL;
54 static gboolean version = FALSE;
55
56 static GOptionEntry entries[] = {
57
58 { "id" , 'i', 0, G_OPTION_ARG_STRING , &id,
59 N_( "The internal identifier of the action to be launched" ), N_( "<STRING>" ) },
60 { "target" , 't', 0, G_OPTION_ARG_FILENAME_ARRAY, &targets_array,
61 N_( "A target, file or folder, for the action. More than one options may be specified" ), N_( "<URI>" ) },
62 { NULL }
63 };
64
65 static GOptionEntry misc_entries[] = {
66
67 { "version" , 'v', 0, G_OPTION_ARG_NONE , &version,
68 N_( "Output the version number" ), NULL },
69 { NULL }
70 };
71
72 static GOptionContext *init_options( void );
73 static NAObjectAction *get_action( const gchar *id );
74 static GList *targets_from_selection( void );
75 static GList *targets_from_commandline( void );
76 static GList *get_selection_from_strv( const gchar **strv, gboolean has_mimetype );
77 static NAObjectProfile *get_profile_for_targets( NAObjectAction *action, GList *targets );
78 static void execute_action( NAObjectAction *action, NAObjectProfile *profile, GList *targets );
79 static void dump_targets( GList *targets );
80 static void exit_with_usage( void );
81
82 int
main(int argc,char ** argv)83 main( int argc, char** argv )
84 {
85 static const gchar *thisfn = "nautilus_actions_run_main";
86 int status = EXIT_SUCCESS;
87 GOptionContext *context;
88 GError *error = NULL;
89 gchar *help;
90 gint errors;
91 NAObjectAction *action;
92 NAObjectProfile *profile;
93 GList *targets;
94
95 #if !GLIB_CHECK_VERSION( 2,36, 0 )
96 g_type_init();
97 #endif
98
99 setlocale( LC_ALL, "" );
100 console_init_log_handler();
101
102 /* pwi 2011-01-05
103 * run GConf migration tools before doing anything else
104 * above all before allocating a new NAPivot
105 */
106 na_gconf_migration_run();
107
108 context = init_options();
109
110 if( argc == 1 ){
111 g_set_prgname( argv[0] );
112 help = g_option_context_get_help( context, FALSE, NULL );
113 g_print( "\n%s", help );
114 g_free( help );
115 exit( status );
116 }
117
118 if( !g_option_context_parse( context, &argc, &argv, &error )){
119 g_printerr( _( "Syntax error: %s\n" ), error->message );
120 g_error_free (error);
121 exit_with_usage();
122 }
123
124 g_option_context_free( context );
125
126 if( version ){
127 na_core_utils_print_version();
128 exit( status );
129 }
130
131 errors = 0;
132
133 if( !id || !strlen( id )){
134 g_printerr( _( "Error: action id is mandatory.\n" ));
135 errors += 1;
136 }
137
138 action = get_action( id );
139 if( !action ){
140 errors += 1;
141 } else {
142 g_debug( "%s: action %s have been found, and is enabled and valid", thisfn, id );
143 }
144
145 if( errors ){
146 exit_with_usage();
147 }
148
149 if( targets_array ){
150 targets = targets_from_commandline();
151
152 } else {
153 targets = targets_from_selection();
154 }
155
156 dump_targets( targets );
157
158 if( g_list_length( targets ) == 0 ){
159 g_print( _( "No current selection. Nothing to do. Exiting.\n" ));
160 exit( status );
161 }
162
163 if( !na_icontext_is_candidate( NA_ICONTEXT( action ), ITEM_TARGET_ANY, targets )){
164 g_printerr( _( "Action %s is not a valid candidate. Exiting.\n" ), id );
165 exit( status );
166 }
167
168 profile = get_profile_for_targets( action, targets );
169 if( !profile ){
170 g_print( _( "No valid profile is candidate to execution. Exiting.\n" ));
171 exit( status );
172 }
173 g_debug( "%s: profile %p found", thisfn, ( void * ) profile );
174
175 execute_action( action, profile, targets );
176
177 na_selected_info_free_list( targets );
178 exit( status );
179 }
180
181 /*
182 * init options context
183 */
184 static GOptionContext *
init_options(void)185 init_options( void )
186 {
187 GOptionContext *context;
188 gchar* description;
189 GOptionGroup *misc_group;
190
191 context = g_option_context_new( _( "Execute an action on the specified target." ));
192 g_option_context_set_translation_domain( context, GETTEXT_PACKAGE );
193
194 #ifdef ENABLE_NLS
195 bindtextdomain( GETTEXT_PACKAGE, GNOMELOCALEDIR );
196 # ifdef HAVE_BIND_TEXTDOMAIN_CODESET
197 bind_textdomain_codeset( GETTEXT_PACKAGE, "UTF-8" );
198 # endif
199 textdomain( GETTEXT_PACKAGE );
200 g_option_context_add_main_entries( context, entries, GETTEXT_PACKAGE );
201 #else
202 g_option_context_add_main_entries( context, entries, NULL );
203 #endif
204
205 description = console_cmdline_get_description();
206 g_option_context_set_description( context, description );
207 g_free( description );
208
209 misc_group = g_option_group_new(
210 "misc", _( "Miscellaneous options" ), _( "Miscellaneous options" ), NULL, NULL );
211 g_option_group_add_entries( misc_group, misc_entries );
212 g_option_group_set_translation_domain( misc_group, GETTEXT_PACKAGE );
213 g_option_context_add_group( context, misc_group );
214
215 return( context );
216 }
217
218 /*
219 * search for the action in the repository
220 */
221 static NAObjectAction *
get_action(const gchar * id)222 get_action( const gchar *id )
223 {
224 NAPivot *pivot;
225 NAObjectAction *action;
226
227 action = NULL;
228
229 pivot = na_pivot_new();
230 na_pivot_set_loadable( pivot, !PIVOT_LOAD_DISABLED & !PIVOT_LOAD_INVALID );
231 na_pivot_load_items( pivot );
232
233 action = ( NAObjectAction * ) na_pivot_get_item( pivot, id );
234
235 if( !action ){
236 g_printerr( _( "Error: action '%s' doesn't exist.\n" ), id );
237
238 } else {
239 if( !na_object_is_enabled( action )){
240 g_printerr( _( "Error: action '%s' is disabled.\n" ), id );
241 g_object_unref( action );
242 action = NULL;
243 }
244 if( !na_object_is_valid( action )){
245 g_printerr( _( "Error: action '%s' is not valid.\n" ), id );
246 g_object_unref( action );
247 action = NULL;
248 }
249 }
250
251 return( action );
252 }
253
254 /*
255 * the DBus.Tracker.Properties1 interface returns a list of strings
256 * where each selected item brings up both its URI and its Nautilus
257 * mime type.
258 *
259 * We return to the caller a GList of NASelectedInfo objects
260 */
261 static GList *
targets_from_selection(void)262 targets_from_selection( void )
263 {
264 static const gchar *thisfn = "nautilus_actions_run_targets_from_selection";
265 GList *selection;
266 GError *error;
267 gchar **paths;
268
269 g_debug( "%s", thisfn );
270
271 selection = NULL;
272 error = NULL;
273 paths = NULL;
274
275 #ifdef HAVE_GDBUS
276 GDBusObjectManager *manager;
277 gchar *name_owner;
278 GDBusObject *object;
279 GDBusInterface *iface;
280
281 manager = na_tracker_object_manager_client_new_for_bus_sync(
282 G_BUS_TYPE_SESSION,
283 G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
284 NAUTILUS_ACTIONS_DBUS_SERVICE,
285 NAUTILUS_ACTIONS_DBUS_TRACKER_PATH,
286 NULL,
287 &error );
288
289 if( !manager ){
290 g_printerr( "%s: unable to allocate an ObjectManagerClient: %s\n", thisfn, error->message );
291 g_error_free( error );
292 return( NULL );
293 }
294
295 name_owner = g_dbus_object_manager_client_get_name_owner( G_DBUS_OBJECT_MANAGER_CLIENT( manager ));
296 g_debug( "%s: name_owner=%s", thisfn, name_owner );
297 g_free( name_owner );
298
299 object = g_dbus_object_manager_get_object( manager, NAUTILUS_ACTIONS_DBUS_TRACKER_PATH "/0" );
300 if( !object ){
301 g_printerr( "%s: unable to get object at %s path\n", thisfn, NAUTILUS_ACTIONS_DBUS_TRACKER_PATH "/0" );
302 g_object_unref( manager );
303 return( NULL );
304 }
305
306 iface = g_dbus_object_get_interface( object, NAUTILUS_ACTIONS_DBUS_TRACKER_IFACE );
307 if( !iface ){
308 g_printerr( "%s: unable to get %s interface\n", thisfn, NAUTILUS_ACTIONS_DBUS_TRACKER_IFACE );
309 g_object_unref( object );
310 g_object_unref( manager );
311 return( NULL );
312 }
313
314 /* note that @iface is really a GDBusProxy instance
315 * and additionally also a NATrackerProperties1 instance
316 */
317 na_tracker_properties1_call_get_selected_paths_sync(
318 NA_TRACKER_PROPERTIES1( iface ),
319 &paths,
320 NULL,
321 &error );
322
323 #else
324 # ifdef HAVE_DBUS_GLIB
325 DBusGConnection *connection;
326 DBusGProxy *proxy = NULL;
327
328 connection = dbus_g_bus_get( DBUS_BUS_SESSION, &error );
329 if( !connection ){
330 if( error ){
331 g_printerr( _( "Error: unable to get a connection to session DBus: %s" ), error->message );
332 g_error_free( error );
333 }
334 return( NULL );
335 }
336 g_debug( "%s: connection is ok", thisfn );
337
338 proxy = dbus_g_proxy_new_for_name( connection,
339 NAUTILUS_ACTIONS_DBUS_SERVICE,
340 NAUTILUS_ACTIONS_DBUS_TRACKER_PATH "/0",
341 NAUTILUS_ACTIONS_DBUS_TRACKER_IFACE );
342
343 if( !proxy ){
344 g_printerr( _( "Error: unable to get a proxy on %s service" ), NAUTILUS_ACTIONS_DBUS_SERVICE );
345 dbus_g_connection_unref( connection );
346 return( NULL );
347 }
348 g_debug( "%s: proxy is ok", thisfn );
349
350 if( !dbus_g_proxy_call( proxy, "GetSelectedPaths", &error,
351 G_TYPE_INVALID,
352 G_TYPE_STRV, &paths, G_TYPE_INVALID )){
353
354 g_printerr( _( "Error on GetSelectedPaths call: %s" ), error->message );
355 g_error_free( error );
356 /* TODO: unref proxy */
357 dbus_g_connection_unref( connection );
358 return( NULL );
359 }
360 g_debug( "%s: function call is ok", thisfn );
361
362 /* TODO: unref proxy */
363 dbus_g_connection_unref( connection );
364 # endif
365 #endif
366
367 selection = get_selection_from_strv(( const gchar ** ) paths, TRUE );
368
369 g_strfreev( paths );
370
371 return( selection );
372 }
373
374 /*
375 * get targets from command-line
376 *
377 * We return to the caller a GList of NASelectedInfo objects.
378 */
379 static GList *
targets_from_commandline(void)380 targets_from_commandline( void )
381 {
382 static const gchar *thisfn = "nautilus_actions_run_targets_from_commandline";
383 GList *targets;
384
385 g_debug( "%s", thisfn );
386
387 targets = get_selection_from_strv(( const gchar ** ) targets_array, FALSE );
388
389 return( targets );
390 }
391
392 static GList *
get_selection_from_strv(const gchar ** strv,gboolean has_mimetype)393 get_selection_from_strv( const gchar **strv, gboolean has_mimetype )
394 {
395 GList *list;
396 gchar **iter;
397 gchar *errmsg;
398
399 list = NULL;
400 iter = ( gchar ** ) strv;
401
402 while( *iter ){
403 const gchar *uri = ( const gchar * ) *iter;
404 const gchar *mimetype = NULL;
405 if( has_mimetype ){
406 iter++;
407 mimetype = ( const gchar * ) *iter;
408 }
409
410 errmsg = NULL;
411 NASelectedInfo *nsi = na_selected_info_create_for_uri( uri, mimetype, &errmsg );
412
413 if( errmsg ){
414 g_printerr( "%s\n", errmsg );
415 g_free( errmsg );
416 }
417
418 if( nsi ){
419 list = g_list_prepend( list, nsi );
420 }
421 iter++;
422 }
423
424 return( g_list_reverse( list ));
425 }
426
427 /*
428 * find a profile candidate to be executed for the given uris
429 */
430 static NAObjectProfile *
get_profile_for_targets(NAObjectAction * action,GList * targets)431 get_profile_for_targets( NAObjectAction *action, GList *targets )
432 {
433 /*static const gchar *thisfn = "nautilus_actions_run_get_profile_for_targets";*/
434 GList *profiles, *ip;
435 NAObjectProfile *candidate;
436
437 candidate = NULL;
438 profiles = na_object_get_items( action );
439
440 for( ip = profiles ; ip && !candidate ; ip = ip->next ){
441 if( na_icontext_is_candidate( NA_ICONTEXT( ip->data ), ITEM_TARGET_ANY, targets )){
442 candidate = NA_OBJECT_PROFILE( ip->data );
443 }
444 }
445
446 return( candidate );
447 }
448
449 static void
execute_action(NAObjectAction * action,NAObjectProfile * profile,GList * targets)450 execute_action( NAObjectAction *action, NAObjectProfile *profile, GList *targets )
451 {
452 /*static const gchar *thisfn = "nautilus_action_run_execute_action";*/
453 NATokens *tokens;
454
455 tokens = na_tokens_new_from_selection( targets );
456 na_tokens_execute_action( tokens, profile );
457 }
458
459 /*
460 *
461 */
462 static void
dump_targets(GList * targets)463 dump_targets( GList *targets )
464 {
465 GList *it;
466 gchar *uri, *mimetype;
467
468 for( it = targets ; it ; it = it->next ){
469 NASelectedInfo *nsi = NA_SELECTED_INFO( it->data );
470 uri = na_selected_info_get_uri( nsi );
471 mimetype = na_selected_info_get_mime_type( nsi );
472 g_print( "%s\t[%s]\n", uri, mimetype );
473 g_free( mimetype );
474 g_free( uri );
475 }
476 }
477
478 /*
479 * print a help message and exit with failure
480 */
481 static void
exit_with_usage(void)482 exit_with_usage( void )
483 {
484 g_printerr( _( "Try %s --help for usage.\n" ), g_get_prgname());
485 exit( EXIT_FAILURE );
486 }
487