1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
2
3 This program is free software; you can redistribute it and/or
4 modify it under the terms of the GNU General Public License as
5 published by the Free Software Foundation; either version 2 of the
6 License, or (at your option) any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 General Public License for more details.
12
13 You should have received a copy of the GNU General Public
14 License along with this program; if not, write to the
15 Free Software Foundation, Inc., 51 Franklin Street - Suite 500,
16 Boston, MA 02110-1335, USA.
17
18 */
19
20 #include "nemo-action.h"
21 #include <eel/eel-string.h>
22 #include <eel/eel-vfs-extensions.h>
23 #include <glib/gi18n.h>
24 #include <gdk/gdk.h>
25 #include "nemo-file-utilities.h"
26 #include "nemo-program-choosing.h"
27
28 #define DEBUG_FLAG NEMO_DEBUG_ACTIONS
29 #include <libnemo-private/nemo-debug.h>
30
31 #if (!GLIB_CHECK_VERSION(2,50,0))
32 #define g_drive_is_removable g_drive_is_media_removable
33 #endif
34
35 G_DEFINE_TYPE (NemoAction, nemo_action,
36 GTK_TYPE_ACTION);
37
38 static void nemo_action_get_property (GObject *object,
39 guint param_id,
40 GValue *value,
41 GParamSpec *pspec);
42 static void nemo_action_set_property (GObject *object,
43 guint param_id,
44 const GValue *value,
45 GParamSpec *pspec);
46 static void nemo_action_constructed (GObject *object);
47 static void nemo_action_finalize (GObject *gobject);
48
49 static SelectionType nemo_action_get_selection_type (NemoAction *action);
50 static void nemo_action_set_extensions (NemoAction *action, gchar **extensions);
51 static gchar **nemo_action_get_extension_list (NemoAction *action);
52 static void nemo_action_set_mimetypes (NemoAction *action, gchar **mimetypes);
53 static gchar **nemo_action_get_mimetypes_list (NemoAction *action);
54 static void nemo_action_set_key_file_path (NemoAction *action, const gchar *path);
55 static void nemo_action_set_exec (NemoAction *action, const gchar *exec);
56 static void nemo_action_set_parent_dir (NemoAction *action, const gchar *parent_dir);
57 static void nemo_action_set_separator (NemoAction *action, const gchar *separator);
58 static void nemo_action_set_conditions (NemoAction *action, gchar **conditions);
59 static gchar **nemo_action_get_conditions (NemoAction *action);
60 static void nemo_action_set_orig_label (NemoAction *action, const gchar *orig_label);
61 static void nemo_action_set_orig_tt (NemoAction *action, const gchar *orig_tt);
62
63 static gchar *find_token_type (const gchar *str, TokenType *token_type);
64
65 static gpointer parent_class;
66
67 enum
68 {
69 PROP_0,
70 PROP_KEY_FILE_PATH,
71 PROP_SELECTION_TYPE,
72 PROP_EXTENSIONS,
73 PROP_MIMES,
74 PROP_EXEC,
75 PROP_PARENT_DIR,
76 PROP_USE_PARENT_DIR,
77 PROP_ORIG_LABEL,
78 PROP_ORIG_TT,
79 PROP_SEPARATOR,
80 PROP_QUOTE_TYPE,
81 PROP_ESCAPE_SPACE,
82 PROP_RUN_IN_TERMINAL,
83 PROP_CONDITIONS
84 };
85
86 enum {
87 CONDITION_CHANGED,
88 LAST_SIGNAL
89 };
90
91 static guint signals[LAST_SIGNAL] = { 0 };
92
93 typedef struct {
94 NemoAction *action;
95 gchar *name;
96 guint watch_id;
97 gboolean exists;
98 } DBusCondition;
99
100 typedef struct {
101 NemoAction *action;
102 GSettings *settings;
103 gchar *condition_string;
104 gchar *key;
105 guint handler_id;
106 } GSettingsCondition;
107
108 static void
dbus_condition_free(gpointer data)109 dbus_condition_free (gpointer data)
110 {
111 DBusCondition *cond = (DBusCondition *) data;
112 g_free (cond->name);
113 g_bus_unwatch_name (cond->watch_id);
114
115 g_free (cond);
116 }
117
118 static void
gsettings_condition_free(gpointer data)119 gsettings_condition_free (gpointer data)
120 {
121 GSettingsCondition *cond = (GSettingsCondition *) data;
122
123 g_signal_handler_disconnect (cond->settings, cond->handler_id);
124
125 g_object_unref (cond->settings);
126 g_free (cond->key);
127 g_free (cond->condition_string);
128
129 g_free (cond);
130 }
131
132 static void
nemo_action_init(NemoAction * action)133 nemo_action_init (NemoAction *action)
134 {
135 action->key_file_path = NULL;
136 action->selection_type = SELECTION_SINGLE;
137 action->extensions = NULL;
138 action->mimetypes = NULL;
139 action->exec = NULL;
140 action->parent_dir = NULL;
141 action->use_parent_dir = FALSE;
142 action->orig_label = NULL;
143 action->orig_tt = NULL;
144 action->quote_type = QUOTE_TYPE_NONE;
145 action->separator = NULL;
146 action->conditions = NULL;
147 action->dbus = NULL;
148 action->dbus_satisfied = TRUE;
149 action->dbus_recalc_timeout_id = 0;
150 action->gsettings = NULL;
151 action->gsettings_satisfied = TRUE;
152 action->gsettings_recalc_timeout_id = 0;
153 action->escape_underscores = FALSE;
154 action->escape_space = FALSE;
155 action->show_in_blank_desktop = FALSE;
156 action->run_in_terminal = FALSE;
157
158 action->constructing = TRUE;
159 }
160
161 static void
nemo_action_class_init(NemoActionClass * klass)162 nemo_action_class_init (NemoActionClass *klass)
163 {
164 GObjectClass *object_class = G_OBJECT_CLASS(klass);
165 parent_class = g_type_class_peek_parent (klass);
166 object_class->finalize = nemo_action_finalize;
167 object_class->set_property = nemo_action_set_property;
168 object_class->get_property = nemo_action_get_property;
169 object_class->constructed = nemo_action_constructed;
170
171 g_object_class_install_property (object_class,
172 PROP_KEY_FILE_PATH,
173 g_param_spec_string ("key-file-path",
174 "Key File Path",
175 "The key file path associated with this action",
176 NULL,
177 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)
178 );
179
180 g_object_class_install_property (object_class,
181 PROP_SELECTION_TYPE,
182 g_param_spec_int ("selection-type",
183 "Selection Type",
184 "The action selection type",
185 0,
186 SELECTION_NONE,
187 SELECTION_SINGLE,
188 G_PARAM_READWRITE)
189 );
190
191 g_object_class_install_property (object_class,
192 PROP_EXTENSIONS,
193 g_param_spec_pointer ("extensions",
194 "Extensions",
195 "String array of file extensions",
196 G_PARAM_READWRITE)
197 );
198
199 g_object_class_install_property (object_class,
200 PROP_MIMES,
201 g_param_spec_pointer ("mimetypes",
202 "Mimetypes",
203 "String array of file mimetypes",
204 G_PARAM_READWRITE)
205 );
206
207 g_object_class_install_property (object_class,
208 PROP_EXEC,
209 g_param_spec_string ("exec",
210 "Executable String",
211 "The command line to run",
212 NULL,
213 G_PARAM_READWRITE)
214 );
215
216 g_object_class_install_property (object_class,
217 PROP_PARENT_DIR,
218 g_param_spec_string ("parent-dir",
219 "Parent directory",
220 "The directory the action file resides in",
221 NULL,
222 G_PARAM_READWRITE)
223 );
224 g_object_class_install_property (object_class,
225 PROP_USE_PARENT_DIR,
226 g_param_spec_boolean ("use-parent-dir",
227 "Use Parent Directory",
228 "Execute using the full action path",
229 FALSE,
230 G_PARAM_READWRITE)
231 );
232 g_object_class_install_property (object_class,
233 PROP_ORIG_LABEL,
234 g_param_spec_string ("orig-label",
235 "Original label string",
236 "The starting label - with token",
237 NULL,
238 G_PARAM_READWRITE)
239 );
240 g_object_class_install_property (object_class,
241 PROP_ORIG_TT,
242 g_param_spec_string ("orig-tooltip",
243 "Original tooltip string",
244 "The starting tooltip - with token",
245 NULL,
246 G_PARAM_READWRITE)
247 );
248
249 g_object_class_install_property (object_class,
250 PROP_SEPARATOR,
251 g_param_spec_string ("separator",
252 "Separator to insert between files in the exec line",
253 "Separator to use between files, like comma, space, etc",
254 NULL,
255 G_PARAM_READWRITE)
256 );
257
258 g_object_class_install_property (object_class,
259 PROP_QUOTE_TYPE,
260 g_param_spec_int ("quote-type",
261 "Type of quotes to use to enclose individual file names",
262 "Type of quotes to use to enclose individual file names - none, single or double",
263 QUOTE_TYPE_SINGLE,
264 QUOTE_TYPE_NONE,
265 QUOTE_TYPE_SINGLE,
266 G_PARAM_READWRITE)
267 );
268
269 g_object_class_install_property (object_class,
270 PROP_CONDITIONS,
271 g_param_spec_pointer ("conditions",
272 "Special show conditions",
273 "Special conditions, like a bool gsettings key, or 'desktop'",
274 G_PARAM_READWRITE)
275 );
276 g_object_class_install_property (object_class,
277 PROP_ESCAPE_SPACE,
278 g_param_spec_boolean ("escape-space",
279 "Escape spaces in file paths",
280 "Escape spaces in file paths",
281 FALSE,
282 G_PARAM_READWRITE)
283 );
284 g_object_class_install_property (object_class,
285 PROP_RUN_IN_TERMINAL,
286 g_param_spec_boolean ("run-in-terminal",
287 "Run command in a terminal",
288 "Run command in a terminal",
289 FALSE,
290 G_PARAM_READWRITE)
291 );
292
293 signals[CONDITION_CHANGED] = g_signal_new ("condition-changed",
294 G_TYPE_FROM_CLASS (object_class),
295 G_SIGNAL_RUN_LAST,
296 0, NULL, NULL,
297 g_cclosure_marshal_VOID__VOID,
298 G_TYPE_NONE, 0);
299 }
300
301 static gboolean
recalc_dbus_conditions(NemoAction * action)302 recalc_dbus_conditions (NemoAction *action)
303 {
304 GList *l;
305 DBusCondition *c;
306 gboolean pass, old_satisfied;
307
308 DEBUG ("Recalculating dbus conditions for %s", action->key_file_path);
309
310 pass = TRUE;
311
312 for (l = action->dbus; l != NULL; l = l->next) {
313 c = (DBusCondition *) l->data;
314
315 DEBUG ("Checking dbus name for an owner: '%s' - evaluated to %s",
316 c->name,
317 c->exists ? "TRUE" : "FALSE");
318
319 if (!c->exists) {
320 pass = FALSE;
321 break;
322 }
323 }
324
325 old_satisfied = action->dbus_satisfied;
326 action->dbus_satisfied = pass;
327
328 DEBUG ("DBus satisfied: %s",
329 pass ? "TRUE" : "FALSE");
330
331 if (pass != old_satisfied) {
332 g_signal_emit (action, signals[CONDITION_CHANGED], 0);
333 }
334
335 action->dbus_recalc_timeout_id = 0;
336 return FALSE;
337 }
338
339 static void
queue_recalc_dbus_conditions(NemoAction * action)340 queue_recalc_dbus_conditions (NemoAction *action)
341 {
342 if (action->constructing) {
343 return;
344 }
345
346 if (action->dbus_recalc_timeout_id != 0) {
347 g_source_remove (action->dbus_recalc_timeout_id);
348 action->dbus_recalc_timeout_id = 0;
349 }
350 action->dbus_recalc_timeout_id = g_idle_add ((GSourceFunc) recalc_dbus_conditions,
351 action);
352 }
353
354 static void
on_dbus_appeared(GDBusConnection * connection,const gchar * name,const gchar * name_owner,gpointer user_data)355 on_dbus_appeared (GDBusConnection *connection,
356 const gchar *name,
357 const gchar *name_owner,
358 gpointer user_data)
359 {
360 DBusCondition *cond = user_data;
361
362 cond->exists = TRUE;
363 queue_recalc_dbus_conditions (cond->action);
364 }
365
366 static void
on_dbus_disappeared(GDBusConnection * connection,const gchar * name,gpointer user_data)367 on_dbus_disappeared (GDBusConnection *connection,
368 const gchar *name,
369 gpointer user_data)
370 {
371 DBusCondition *cond = user_data;
372
373 cond->exists = FALSE;
374 queue_recalc_dbus_conditions (cond->action);
375 }
376
377 static void
setup_dbus_condition(NemoAction * action,const gchar * condition)378 setup_dbus_condition (NemoAction *action, const gchar *condition)
379 {
380 gchar **split = g_strsplit (condition, " ", 2);
381
382 if (g_strv_length (split) != 2) {
383 g_strfreev (split);
384 return;
385 }
386
387 if (g_strcmp0 (split[0], "dbus") != 0) {
388 g_strfreev (split);
389 return;
390 }
391
392 DBusCondition *cond = g_new0 (DBusCondition, 1);
393
394 cond->name = g_strdup (split[1]);
395 cond->exists = FALSE;
396 cond->action = action;
397 action->dbus = g_list_append (action->dbus, cond);
398 cond->watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
399 cond->name,
400 0,
401 on_dbus_appeared,
402 on_dbus_disappeared,
403 cond,
404 NULL);
405
406 g_strfreev (split);
407 }
408
409 #define EQUALS "eq"
410 #define NOT_EQUALS "ne"
411 #define LESS_THAN "lt"
412 #define GREATER_THAN "gt"
413
414 enum
415 {
416 GSETTINGS_SCHEMA_INDEX = 1,
417 GSETTINGS_KEY_INDEX = 2,
418 GSETTINGS_TYPE_INDEX = 3,
419 GSETTINGS_OP_INDEX = 4,
420 GSETTINGS_VAL_INDEX = 5,
421 };
422
423 static gboolean
operator_is_valid(const gchar * op_string)424 operator_is_valid (const gchar *op_string)
425 {
426 return (g_strcmp0 (op_string, EQUALS) == 0 ||
427 g_strcmp0 (op_string, NOT_EQUALS) == 0 ||
428 g_strcmp0 (op_string, LESS_THAN) == 0 ||
429 g_strcmp0 (op_string, GREATER_THAN) == 0);
430 }
431
432 static gboolean
try_vector(const gchar * op,gint vector)433 try_vector (const gchar *op, gint vector)
434 {
435 if (g_strcmp0 (op, EQUALS) == 0) {
436 return (vector == 0);
437 } else if (g_strcmp0 (op, NOT_EQUALS) == 0) {
438 return (vector != 0);
439 } else if (g_strcmp0 (op, LESS_THAN) == 0) {
440 return (vector < 0);
441 } else if (g_strcmp0 (op, GREATER_THAN) == 0) {
442 return (vector > 0);
443 }
444 return FALSE;
445 }
446
447 static gboolean
recalc_gsettings_conditions(NemoAction * action)448 recalc_gsettings_conditions (NemoAction *action)
449 {
450 GList *l;
451 gboolean pass, old_satisfied;
452
453 DEBUG ("Recalculating gsettings conditions for %s", action->key_file_path);
454
455 pass = TRUE;
456
457 for (l = action->gsettings; l != NULL; l = l->next) {
458 GSettingsCondition *cond;
459 const GVariantType *target_type, *setting_type;
460 gchar **split;
461 gint len;
462 gboolean iter_pass = FALSE;
463
464 cond = (GSettingsCondition *) l->data;
465
466 split = g_strsplit (cond->condition_string, " ", 6);
467 len = g_strv_length (split);
468
469 if (len == 3) {
470 GVariant *setting_var;
471
472 target_type = G_VARIANT_TYPE_BOOLEAN;
473
474 setting_var = g_settings_get_value (cond->settings, cond->key);
475 setting_type = g_variant_get_type (setting_var);
476
477 if (g_variant_type_equal (setting_type, target_type)) {
478 iter_pass = g_variant_get_boolean (setting_var);
479 }
480
481 g_variant_unref (setting_var);
482 } else {
483 GVariant *setting_var;
484
485 target_type = G_VARIANT_TYPE (split[GSETTINGS_TYPE_INDEX]);
486
487 setting_var = g_settings_get_value (cond->settings, cond->key);
488 setting_type = g_variant_get_type (setting_var);
489
490 if (g_variant_type_equal (setting_type, target_type)) {
491 GVariant *target_var;
492
493 target_var = g_variant_parse (target_type,
494 split[GSETTINGS_VAL_INDEX],
495 NULL, NULL, NULL);
496
497 if (target_var != NULL) {
498 gint vector = g_variant_compare (setting_var, target_var);
499
500 iter_pass = try_vector (split[GSETTINGS_OP_INDEX], vector);
501
502 g_variant_unref (target_var);
503 } else {
504 g_warning ("Nemo Action: gsettings value could not be parsed into a valid GVariant");
505 }
506 }
507
508 g_variant_unref (setting_var);
509 }
510
511 g_strfreev (split);
512
513 DEBUG ("Checking gsettings condition: '%s' - evaluated to %s",
514 cond->condition_string,
515 iter_pass ? "TRUE" : "FALSE");
516
517 /* This should just break here, except the catch with GSettings changed signal handler,
518 * where the value must be retrieved once with a handler connected before the changed signal
519 * will begin being emitted. So, we should go thru all conditions so during setup none
520 * of the signals are skipped. */
521 if (!iter_pass) {
522 pass = FALSE;
523 }
524 }
525
526 DEBUG ("GSettings satisfied: %s",
527 pass ? "TRUE" : "FALSE");
528
529 old_satisfied = action->gsettings_satisfied;
530 action->gsettings_satisfied = pass;
531
532 if (pass != old_satisfied) {
533 g_signal_emit (action, signals[CONDITION_CHANGED], 0);
534 }
535
536 action->gsettings_recalc_timeout_id = 0;
537 return FALSE;
538 }
539
540 static void
queue_recalc_gsettings_conditions(NemoAction * action)541 queue_recalc_gsettings_conditions (NemoAction *action)
542 {
543 if (action->constructing) {
544 return;
545 }
546
547 if (action->gsettings_recalc_timeout_id != 0) {
548 g_source_remove (action->gsettings_recalc_timeout_id);
549 action->gsettings_recalc_timeout_id = 0;
550 }
551
552 action->gsettings_recalc_timeout_id = g_idle_add ((GSourceFunc) recalc_gsettings_conditions,
553 action);
554 }
555
556 static void
setup_gsettings_condition(NemoAction * action,const gchar * condition)557 setup_gsettings_condition (NemoAction *action,
558 const gchar *condition)
559 {
560 GSettingsSchemaSource *schema_source;
561 GSettingsSchema *schema;
562 gchar **split;
563 gint len;
564
565 split = g_strsplit (condition, " ", 6);
566 len = g_strv_length (split);
567
568 if (len != 6 &&
569 len != 3) {
570 g_strfreev (split);
571 return;
572 }
573
574 if (g_strcmp0 (split[0], "gsettings") != 0) {
575 g_strfreev (split);
576 return;
577 }
578
579 if (len == 6 &&
580 (!g_variant_type_string_is_valid (split[GSETTINGS_TYPE_INDEX]) ||
581 !operator_is_valid (split[GSETTINGS_OP_INDEX]))) {
582 g_warning ("Nemo Action: Either gsettings variant type (%s) or operator (%s) is invalid.",
583 split[GSETTINGS_TYPE_INDEX], split[GSETTINGS_OP_INDEX]);
584 g_strfreev (split);
585 return;
586 }
587
588 schema_source = g_settings_schema_source_get_default();
589 schema = g_settings_schema_source_lookup (schema_source, split[GSETTINGS_SCHEMA_INDEX], TRUE);
590
591 if (schema) {
592 GSettings *settings;
593 gchar **keys;
594 gint i;
595
596 settings = g_settings_new (split[GSETTINGS_SCHEMA_INDEX]);
597 keys = g_settings_list_keys (settings);
598
599 for (i = 0; i < g_strv_length (keys); i++) {
600 if (g_strcmp0 (keys[i], split[GSETTINGS_KEY_INDEX]) == 0) {
601 GSettingsCondition *cond;
602 gchar *signal_string;
603
604 cond = g_new0 (GSettingsCondition, 1);
605
606 cond->action = action;
607 cond->condition_string = g_strdup (condition);
608 cond->settings = g_object_ref (settings);
609 cond->key = g_strdup (keys[i]);
610
611 signal_string = g_strdup_printf ("changed::%s", cond->key);
612
613 cond->handler_id = g_signal_connect_swapped (settings,
614 signal_string,
615 G_CALLBACK (queue_recalc_gsettings_conditions),
616 action);
617
618 action->gsettings = g_list_prepend (action->gsettings, cond);
619
620 g_free (signal_string);
621
622 break;
623 }
624 }
625
626 g_object_unref (settings);
627 g_strfreev (keys);
628 g_settings_schema_unref (schema);
629 }
630
631 g_strfreev (split);
632 }
633
634 static void
strip_custom_modifier(const gchar * raw,gboolean * custom,gchar ** out)635 strip_custom_modifier (const gchar *raw, gboolean *custom, gchar **out)
636 {
637 if (g_str_has_prefix (raw, "<") && g_str_has_suffix (raw, ">")) {
638 gchar **split = g_strsplit_set (raw, "<>", 3);
639 *out = g_strdup (split[1]);
640 *custom = TRUE;
641 g_strfreev (split);
642 } else {
643 *out = g_strdup (raw);
644 *custom = FALSE;
645 }
646 }
647
648 void
nemo_action_constructed(GObject * object)649 nemo_action_constructed (GObject *object)
650 {
651 G_OBJECT_CLASS (parent_class)->constructed (object);
652
653 NemoAction *action = NEMO_ACTION (object);
654
655 GKeyFile *key_file = g_key_file_new();
656
657 g_key_file_load_from_file (key_file, action->key_file_path, G_KEY_FILE_NONE, NULL);
658
659 gchar *orig_label = g_key_file_get_locale_string (key_file,
660 ACTION_FILE_GROUP,
661 KEY_NAME,
662 NULL,
663 NULL);
664
665 gchar *orig_tt = g_key_file_get_locale_string (key_file,
666 ACTION_FILE_GROUP,
667 KEY_COMMENT,
668 NULL,
669 NULL);
670
671 gchar *icon_name = g_key_file_get_string (key_file,
672 ACTION_FILE_GROUP,
673 KEY_ICON_NAME,
674 NULL);
675
676 gchar *stock_id = g_key_file_get_string (key_file,
677 ACTION_FILE_GROUP,
678 KEY_STOCK_ID,
679 NULL);
680
681
682 gchar *exec_raw = g_key_file_get_string (key_file,
683 ACTION_FILE_GROUP,
684 KEY_EXEC,
685 NULL);
686
687 gchar *selection_string_raw = g_key_file_get_string (key_file,
688 ACTION_FILE_GROUP,
689 KEY_SELECTION,
690 NULL);
691
692 gchar *selection_string = g_ascii_strdown (selection_string_raw, -1);
693
694 g_free (selection_string_raw);
695
696 gchar *separator = g_key_file_get_string (key_file,
697 ACTION_FILE_GROUP,
698 KEY_SEPARATOR,
699 NULL);
700
701 gchar *quote_type_string = g_key_file_get_string (key_file,
702 ACTION_FILE_GROUP,
703 KEY_QUOTE_TYPE,
704 NULL);
705
706 QuoteType quote_type = QUOTE_TYPE_NONE;
707
708 if (quote_type_string != NULL) {
709 if (g_strcmp0 (quote_type_string, "single") == 0)
710 quote_type = QUOTE_TYPE_SINGLE;
711 else if (g_strcmp0 (quote_type_string, "double") == 0)
712 quote_type = QUOTE_TYPE_DOUBLE;
713 else if (g_strcmp0 (quote_type_string, "backtick") == 0)
714 quote_type = QUOTE_TYPE_BACKTICK;
715 }
716
717 SelectionType type;
718
719 if (g_strcmp0 (selection_string, SELECTION_SINGLE_KEY) == 0)
720 type = SELECTION_SINGLE;
721 else if (g_strcmp0 (selection_string, SELECTION_MULTIPLE_KEY) == 0)
722 type = SELECTION_MULTIPLE;
723 else if (g_strcmp0 (selection_string, SELECTION_ANY_KEY) == 0)
724 type = SELECTION_ANY;
725 else if (g_strcmp0 (selection_string, SELECTION_NONE_KEY) == 0)
726 type = SELECTION_NONE;
727 else if (g_strcmp0 (selection_string, SELECTION_NOT_NONE_KEY) == 0)
728 type = SELECTION_NOT_NONE;
729 else {
730 gint val = (int) g_ascii_strtoll (selection_string, NULL, 10);
731 type = val > 0 ? val : SELECTION_SINGLE;
732 }
733
734 g_free (selection_string);
735
736 gsize count;
737
738 gchar **ext = g_key_file_get_string_list (key_file,
739 ACTION_FILE_GROUP,
740 KEY_EXTENSIONS,
741 &count,
742 NULL);
743
744 gsize mime_count;
745
746 gchar **mimes = g_key_file_get_string_list (key_file,
747 ACTION_FILE_GROUP,
748 KEY_MIME_TYPES,
749 &mime_count,
750 NULL);
751
752 gsize condition_count;
753
754 gchar **conditions = g_key_file_get_string_list (key_file,
755 ACTION_FILE_GROUP,
756 KEY_CONDITIONS,
757 &condition_count,
758 NULL);
759
760 gboolean escape_space;
761
762 escape_space = g_key_file_get_boolean (key_file,
763 ACTION_FILE_GROUP,
764 KEY_WHITESPACE,
765 NULL);
766
767 gboolean run_in_terminal;
768
769 run_in_terminal = g_key_file_get_boolean (key_file,
770 ACTION_FILE_GROUP,
771 KEY_TERMINAL,
772 NULL);
773
774 gboolean is_desktop = FALSE;
775
776 if (conditions && condition_count > 0) {
777 guint j;
778 gchar *condition;
779 for (j = 0; j < condition_count; j++) {
780 condition = conditions[j];
781 if (g_str_has_prefix (condition, "dbus")) {
782 setup_dbus_condition (action, condition);
783 }
784 else
785 if (g_str_has_prefix (condition, "gsettings")) {
786 setup_gsettings_condition (action, condition);
787 }
788 else
789 if (g_str_has_prefix (condition, "exec")) {
790 /* handled in nemo_action_get_visibility */
791 }
792 else
793 if (g_strcmp0 (condition, "desktop") == 0) {
794 is_desktop = TRUE;
795 }
796 else
797 if (g_strcmp0 (condition, "removable") == 0) {
798 /* this is handled in nemo_action_get_visibility() */
799 }
800 else {
801 g_warning ("Ignoring invalid condition: %s."
802 " See sample action at /usr/share/nemo/actions/sample.nemo_action", condition);
803 }
804 }
805 }
806
807 gchar *exec = NULL;
808 gboolean use_parent_dir = FALSE;
809
810 strip_custom_modifier (exec_raw, &use_parent_dir, &exec);
811 g_free (exec_raw);
812
813 TokenType token_type;
814
815 action->show_in_blank_desktop = is_desktop &&
816 type == SELECTION_NONE &&
817 find_token_type (exec, &token_type) == NULL;
818
819 GFile *file = g_file_new_for_path (action->key_file_path);
820 GFile *parent = g_file_get_parent (file);
821
822 gchar *parent_dir = g_file_get_path (parent);
823
824 g_object_unref (file);
825 g_object_unref (parent);
826
827 g_object_set (action,
828 "label", orig_label,
829 "tooltip", orig_tt,
830 "icon-name", icon_name,
831 "stock-id", stock_id,
832 "exec", exec,
833 "selection-type", type,
834 "extensions", ext,
835 "mimetypes", mimes,
836 "parent-dir", parent_dir,
837 "use-parent-dir", use_parent_dir,
838 "orig-label", orig_label,
839 "orig-tooltip", orig_tt,
840 "quote-type", quote_type,
841 "separator", separator,
842 "conditions", conditions,
843 "escape-space", escape_space,
844 "run-in-terminal", run_in_terminal,
845 NULL);
846
847 action->constructing = FALSE;
848
849 DEBUG ("Initial action gsettings and dbus update (%s)", action->key_file_path);
850 queue_recalc_dbus_conditions (action);
851 queue_recalc_gsettings_conditions (action);
852 DEBUG ("Initial action gsettings and dbus complete (%s)", action->key_file_path);
853
854 g_free (orig_label);
855 g_free (orig_tt);
856 g_free (icon_name);
857 g_free (stock_id);
858 g_free (exec);
859 g_free (parent_dir);
860 g_free (quote_type_string);
861 g_free (separator);
862 g_strfreev (ext);
863 g_strfreev (mimes);
864 g_strfreev (conditions);
865 g_key_file_free (key_file);
866 }
867
868 NemoAction *
nemo_action_new(const gchar * name,const gchar * path)869 nemo_action_new (const gchar *name,
870 const gchar *path)
871 {
872 GKeyFile *key_file = g_key_file_new();
873
874 g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, NULL);
875
876 if (!g_key_file_has_group (key_file, ACTION_FILE_GROUP)) {
877 g_key_file_free (key_file);
878 return NULL;
879 }
880
881 if (g_key_file_has_key (key_file, ACTION_FILE_GROUP, KEY_ACTIVE, NULL)) {
882 if (!g_key_file_get_boolean (key_file, ACTION_FILE_GROUP, KEY_ACTIVE, NULL)) {
883 g_key_file_free (key_file);
884 return NULL;
885 }
886 }
887
888 gchar *orig_label = g_key_file_get_locale_string (key_file,
889 ACTION_FILE_GROUP,
890 KEY_NAME,
891 NULL,
892 NULL);
893
894 gchar *exec_raw = g_key_file_get_string (key_file,
895 ACTION_FILE_GROUP,
896 KEY_EXEC,
897 NULL);
898
899 gchar **ext = g_key_file_get_string_list (key_file,
900 ACTION_FILE_GROUP,
901 KEY_EXTENSIONS,
902 NULL,
903 NULL);
904
905 gchar **mimes = g_key_file_get_string_list (key_file,
906 ACTION_FILE_GROUP,
907 KEY_MIME_TYPES,
908 NULL,
909 NULL);
910
911 gchar **deps = g_key_file_get_string_list (key_file,
912 ACTION_FILE_GROUP,
913 KEY_DEPENDENCIES,
914 NULL,
915 NULL);
916
917 gchar *selection_string = g_key_file_get_string (key_file,
918 ACTION_FILE_GROUP,
919 KEY_SELECTION,
920 NULL);
921
922 gboolean finish = TRUE;
923
924 if (deps != NULL) {
925 guint i = 0;
926 for (i = 0; i < g_strv_length (deps); i++) {
927 if (g_path_is_absolute (deps[i])) {
928 if (!g_file_test (deps[i], G_FILE_TEST_EXISTS)) {
929 finish = FALSE;
930 DEBUG ("Missing action dependency: %s", deps[i]);
931 }
932 } else {
933 gchar *p = g_find_program_in_path (deps[i]);
934 if (p == NULL) {
935 finish = FALSE;
936 DEBUG ("Missing action dependency: %s", deps[i]);
937 g_free (p);
938 break;
939 }
940 g_free (p);
941 }
942 }
943 }
944
945 if (orig_label == NULL || exec_raw == NULL || (ext == NULL && mimes == NULL) || selection_string == NULL) {
946 g_warning ("An action definition requires, at minimum, "
947 "a Label field, an Exec field, a Selection field, and an either an Extensions or Mimetypes field.\n"
948 "Check the %s file for missing fields.", path);
949 finish = FALSE;
950 }
951
952 g_free (orig_label);
953 g_free (exec_raw);
954 g_free (selection_string);
955 g_strfreev (ext);
956 g_strfreev (mimes);
957 g_strfreev (deps);
958 g_key_file_free (key_file);
959
960 return finish ? g_object_new (NEMO_TYPE_ACTION,
961 "name", name,
962 "key-file-path", path,
963 NULL): NULL;
964 }
965
966 static void
nemo_action_finalize(GObject * object)967 nemo_action_finalize (GObject *object)
968 {
969 NemoAction *action = NEMO_ACTION (object);
970
971 g_free (action->key_file_path);
972 g_strfreev (action->extensions);
973 g_strfreev (action->mimetypes);
974 g_strfreev (action->conditions);
975 g_free (action->exec);
976 g_free (action->parent_dir);
977 g_free (action->orig_label);
978 g_free (action->orig_tt);
979 g_free (action->separator);
980
981 if (action->dbus) {
982 g_list_free_full (action->dbus, (GDestroyNotify) dbus_condition_free);
983 action->dbus = NULL;
984 }
985
986 if (action->gsettings) {
987 g_list_free_full (action->gsettings, (GDestroyNotify) gsettings_condition_free);
988 action->gsettings = NULL;
989 }
990
991 if (action->dbus_recalc_timeout_id != 0) {
992 g_source_remove (action->dbus_recalc_timeout_id);
993 action->dbus_recalc_timeout_id = 0;
994 }
995
996 if (action->gsettings_recalc_timeout_id != 0) {
997 g_source_remove (action->gsettings_recalc_timeout_id);
998 action->gsettings_recalc_timeout_id = 0;
999 }
1000
1001 G_OBJECT_CLASS (parent_class)->finalize (object);
1002 }
1003
1004
1005 static void
nemo_action_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1006 nemo_action_set_property (GObject *object,
1007 guint prop_id,
1008 const GValue *value,
1009 GParamSpec *pspec)
1010 {
1011 NemoAction *action;
1012
1013 action = NEMO_ACTION (object);
1014
1015 switch (prop_id)
1016 {
1017 case PROP_KEY_FILE_PATH:
1018 nemo_action_set_key_file_path (action, g_value_get_string (value));
1019 break;
1020 case PROP_SELECTION_TYPE:
1021 action->selection_type = g_value_get_int (value);
1022 break;
1023 case PROP_EXTENSIONS:
1024 nemo_action_set_extensions (action, g_value_get_pointer (value));
1025 break;
1026 case PROP_MIMES:
1027 nemo_action_set_mimetypes (action, g_value_get_pointer (value));
1028 break;
1029 case PROP_EXEC:
1030 nemo_action_set_exec (action, g_value_get_string (value));
1031 break;
1032 case PROP_PARENT_DIR:
1033 nemo_action_set_parent_dir (action, g_value_get_string (value));
1034 break;
1035 case PROP_USE_PARENT_DIR:
1036 action->use_parent_dir = g_value_get_boolean (value);
1037 break;
1038 case PROP_ORIG_LABEL:
1039 nemo_action_set_orig_label (action, g_value_get_string (value));
1040 break;
1041 case PROP_ORIG_TT:
1042 nemo_action_set_orig_tt (action, g_value_get_string (value));
1043 break;
1044 case PROP_QUOTE_TYPE:
1045 action->quote_type = g_value_get_int (value);
1046 break;
1047 case PROP_SEPARATOR:
1048 nemo_action_set_separator (action, g_value_get_string (value));
1049 break;
1050 case PROP_CONDITIONS:
1051 nemo_action_set_conditions (action, g_value_get_pointer (value));
1052 break;
1053 case PROP_ESCAPE_SPACE:
1054 action->escape_space = g_value_get_boolean (value);
1055 break;
1056 case PROP_RUN_IN_TERMINAL:
1057 action->run_in_terminal = g_value_get_boolean (value);
1058 break;
1059 default:
1060 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1061 break;
1062 }
1063 }
1064
1065 static void
nemo_action_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1066 nemo_action_get_property (GObject *object,
1067 guint prop_id,
1068 GValue *value,
1069 GParamSpec *pspec)
1070 {
1071 NemoAction *action;
1072
1073 action = NEMO_ACTION (object);
1074
1075 switch (prop_id)
1076 {
1077 case PROP_KEY_FILE_PATH:
1078 g_value_set_string (value, action->key_file_path);
1079 break;
1080 case PROP_SELECTION_TYPE:
1081 g_value_set_int (value, action->selection_type);
1082 break;
1083 case PROP_EXTENSIONS:
1084 g_value_set_pointer (value, action->extensions);
1085 break;
1086 case PROP_MIMES:
1087 g_value_set_pointer (value, action->mimetypes);
1088 break;
1089 case PROP_EXEC:
1090 g_value_set_string (value, action->exec);
1091 break;
1092 case PROP_PARENT_DIR:
1093 g_value_set_string (value, action->parent_dir);
1094 break;
1095 case PROP_USE_PARENT_DIR:
1096 g_value_set_boolean (value, action->use_parent_dir);
1097 break;
1098 case PROP_ORIG_LABEL:
1099 g_value_set_string (value, action->orig_label);
1100 break;
1101 case PROP_ORIG_TT:
1102 g_value_set_string (value, action->orig_tt);
1103 break;
1104 case PROP_QUOTE_TYPE:
1105 g_value_set_int (value, action->quote_type);
1106 break;
1107 case PROP_SEPARATOR:
1108 g_value_set_string (value, action->separator);
1109 break;
1110 case PROP_CONDITIONS:
1111 g_value_set_pointer (value, action->conditions);
1112 break;
1113 case PROP_ESCAPE_SPACE:
1114 g_value_set_boolean (value, action->escape_space);
1115 break;
1116 case PROP_RUN_IN_TERMINAL:
1117 g_value_set_boolean (value, action->run_in_terminal);
1118 break;
1119 default:
1120 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1121 break;
1122 }
1123 }
1124
1125 static gchar *
find_token_type(const gchar * str,TokenType * token_type)1126 find_token_type (const gchar *str, TokenType *token_type)
1127 {
1128 gchar *ptr = NULL;
1129 *token_type = TOKEN_NONE;
1130
1131 ptr = g_strstr_len (str, -1, "%");
1132
1133 if (ptr != NULL) {
1134 if (g_str_has_prefix (ptr, TOKEN_EXEC_FILE_LIST)) {
1135 *token_type = TOKEN_PATH_LIST;
1136 return ptr;
1137 }
1138 if (g_str_has_prefix (ptr, TOKEN_EXEC_URI_LIST)) {
1139 *token_type = TOKEN_URI_LIST;
1140 return ptr;
1141 }
1142 if (g_str_has_prefix (ptr, TOKEN_EXEC_PARENT)) {
1143 *token_type = TOKEN_PARENT_PATH;
1144 return ptr;
1145 }
1146 if (g_str_has_prefix (ptr, TOKEN_EXEC_FILE_NAME)) {
1147 *token_type = TOKEN_FILE_DISPLAY_NAME;
1148 return ptr;
1149 }
1150 if (g_str_has_prefix (ptr, TOKEN_EXEC_PARENT_NAME)) {
1151 *token_type = TOKEN_PARENT_DISPLAY_NAME;
1152 return ptr;
1153 }
1154 if (g_str_has_prefix (ptr, TOKEN_LABEL_FILE_NAME)) {
1155 *token_type = TOKEN_FILE_DISPLAY_NAME;
1156 return ptr;
1157 }
1158 if (g_str_has_prefix (ptr, TOKEN_EXEC_DEVICE)) {
1159 *token_type = TOKEN_DEVICE;
1160 return ptr;
1161 }
1162 if (g_str_has_prefix (ptr, TOKEN_EXEC_FILE_NO_EXT)) {
1163 *token_type = TOKEN_FILE_DISPLAY_NAME_NO_EXT;
1164 return ptr;
1165 }
1166 if (g_str_has_prefix (ptr, TOKEN_EXEC_LITERAL_PERCENT)) {
1167 *token_type = TOKEN_LITERAL_PERCENT;
1168 return ptr;
1169 }
1170 }
1171
1172 return NULL;
1173 }
1174
1175 static gchar *
get_path(NemoAction * action,NemoFile * file)1176 get_path (NemoAction *action, NemoFile *file)
1177 {
1178 gchar *ret, *quote_escaped, *orig;
1179
1180 orig = nemo_file_get_path (file);
1181
1182 if (action->quote_type != QUOTE_TYPE_DOUBLE && action->quote_type != QUOTE_TYPE_SINGLE)
1183 quote_escaped = eel_str_escape_quotes (orig);
1184 else
1185 quote_escaped = orig;
1186
1187 if (action->escape_space) {
1188 ret = eel_str_escape_spaces (quote_escaped);
1189 } else {
1190 ret = g_strdup (quote_escaped);
1191 }
1192
1193 g_free (orig);
1194
1195 if (quote_escaped != orig)
1196 g_free (quote_escaped);
1197
1198 return ret;
1199 }
1200
1201 static GString *
score_append(NemoAction * action,GString * str,const gchar * c)1202 score_append (NemoAction *action, GString *str, const gchar *c)
1203 {
1204 if (action->escape_underscores) {
1205 gchar *escaped = eel_str_double_underscores (c);
1206 str = g_string_append (str, escaped);
1207 g_free (escaped);
1208 return str;
1209 } else {
1210 return g_string_append (str, c);
1211 }
1212 }
1213
1214 static GString *
insert_separator(NemoAction * action,GString * str)1215 insert_separator (NemoAction *action, GString *str)
1216 {
1217 if (action->separator == NULL)
1218 str = g_string_append (str, " ");
1219 else
1220 str = score_append (action, str, action->separator);
1221
1222 return str;
1223 }
1224
1225 static GString *
insert_quote(NemoAction * action,GString * str)1226 insert_quote (NemoAction *action, GString *str)
1227 {
1228 switch (action->quote_type) {
1229 case QUOTE_TYPE_SINGLE:
1230 str = g_string_append (str, "'");
1231 break;
1232 case QUOTE_TYPE_DOUBLE:
1233 str = g_string_append (str, "\"");
1234 break;
1235 case QUOTE_TYPE_BACKTICK:
1236 str = g_string_append (str, "`");
1237 break;
1238 case QUOTE_TYPE_NONE:
1239 break;
1240 default:
1241 break;
1242 }
1243
1244 return str;
1245 }
1246
1247 static gchar *
get_device_path(NemoAction * action,NemoFile * file)1248 get_device_path (NemoAction *action, NemoFile *file)
1249 {
1250 GMount *mount = nemo_file_get_mount (file);
1251 GVolume *volume = g_mount_get_volume (mount);
1252 gchar *ret = NULL;
1253
1254 if (action->escape_space) {
1255 gchar *id = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
1256 ret = eel_str_escape_spaces (id);
1257 g_free (id);
1258 } else {
1259 ret = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
1260 }
1261
1262 g_object_unref (mount);
1263 g_object_unref (volume);
1264
1265 return ret;
1266 }
1267
1268 static gchar *
get_insertion_string(NemoAction * action,TokenType token_type,GList * selection,NemoFile * parent)1269 get_insertion_string (NemoAction *action, TokenType token_type, GList *selection, NemoFile *parent)
1270 {
1271 GList *l;
1272
1273 GString *str = g_string_new("");
1274 gboolean first = TRUE;
1275
1276 switch (token_type) {
1277 case TOKEN_LITERAL_PERCENT:
1278 str = g_string_append(str, "%");
1279 break;
1280 case TOKEN_PATH_LIST:
1281 if (g_list_length (selection) > 0) {
1282 for (l = selection; l != NULL; l = l->next) {
1283 if (!first)
1284 str = insert_separator (action, str);
1285 str = insert_quote (action, str);
1286 gchar *path = get_path (action, NEMO_FILE (l->data));
1287 if (path)
1288 str = score_append (action, str, path);
1289 g_free (path);
1290 str = insert_quote (action, str);
1291 first = FALSE;
1292 }
1293 } else {
1294 goto default_parent_path;
1295 }
1296 break;
1297 case TOKEN_URI_LIST:
1298 if (g_list_length (selection) > 0) {
1299 for (l = selection; l != NULL; l = l->next) {
1300 if (!first)
1301 str = insert_separator (action, str);
1302 str = insert_quote (action, str);
1303 gchar *uri = nemo_file_get_uri (NEMO_FILE (l->data));
1304 str = score_append (action, str, uri);
1305 g_free (uri);
1306 str = insert_quote (action, str);
1307 first = FALSE;
1308 }
1309 } else {
1310 goto default_parent_path;
1311 }
1312 break;
1313 case TOKEN_PARENT_PATH:
1314 ;
1315 default_parent_path:
1316 ;
1317 gchar *path = get_path (action, parent);
1318 if (path == NULL) {
1319 gchar *name = nemo_file_get_display_name (parent);
1320 if (g_strcmp0 (name, "x-nemo-desktop") == 0)
1321 path = nemo_get_desktop_directory ();
1322 else
1323 path = g_strdup ("");
1324 g_free (name);
1325 }
1326 str = insert_quote (action, str);
1327 str = score_append (action, str, path);
1328 str = insert_quote (action, str);
1329 g_free (path);
1330 break;
1331 case TOKEN_FILE_DISPLAY_NAME:
1332 if (g_list_length (selection) > 0) {
1333 gchar *file_display_name = nemo_file_get_display_name (NEMO_FILE (selection->data));
1334 str = score_append (action, str, file_display_name);
1335 g_free (file_display_name);
1336 } else {
1337 goto default_parent_display_name;
1338 }
1339 break;
1340 case TOKEN_PARENT_DISPLAY_NAME:
1341 ;
1342 default_parent_display_name:
1343 ;
1344 gchar *parent_display_name;
1345 gchar *real_display_name = nemo_file_get_display_name (parent);
1346 if (g_strcmp0 (real_display_name, "x-nemo-desktop") == 0)
1347 parent_display_name = g_strdup_printf (_("Desktop"));
1348 else
1349 parent_display_name = nemo_file_get_display_name (parent);
1350 g_free (real_display_name);
1351 str = insert_quote (action, str);
1352 str = score_append (action, str, parent_display_name);
1353 str = insert_quote (action, str);
1354 g_free (parent_display_name);
1355 break;
1356 case TOKEN_DEVICE:
1357 if (g_list_length (selection) > 0) {
1358 for (l = selection; l != NULL; l = l->next) {
1359 if (!first)
1360 str = insert_separator (action, str);
1361 str = insert_quote (action, str);
1362 gchar *dev = get_device_path (action, NEMO_FILE (l->data));
1363 if (dev)
1364 str = score_append (action, str, dev);
1365 g_free (dev);
1366 str = insert_quote (action, str);
1367 first = FALSE;
1368 }
1369 } else {
1370 goto default_parent_path;
1371 }
1372 break;
1373 case TOKEN_FILE_DISPLAY_NAME_NO_EXT:
1374 if (g_list_length (selection) > 0) {
1375 gchar *file_display_name = nemo_file_get_display_name (NEMO_FILE (selection->data));
1376 str = score_append (action, str, eel_filename_strip_extension (file_display_name));
1377 g_free (file_display_name);
1378 } else {
1379 goto default_parent_path;
1380 }
1381 break;
1382 case TOKEN_NONE:
1383 default:
1384 break;
1385 }
1386
1387 gchar *ret = str->str;
1388
1389 g_string_free (str, FALSE);
1390
1391 return ret;
1392 }
1393
1394 static GString *
expand_action_string(NemoAction * action,GList * selection,NemoFile * parent,GString * str)1395 expand_action_string (NemoAction *action, GList *selection, NemoFile *parent, GString *str)
1396 {
1397 gchar *ptr;
1398 TokenType token_type;
1399
1400 ptr = find_token_type (str->str, &token_type);
1401
1402 while (ptr != NULL) {
1403 gint shift = ptr - str->str;
1404
1405 gchar *insertion = get_insertion_string (action, token_type, selection, parent);
1406 str = g_string_erase (str, shift, 2);
1407 str = g_string_insert (str, shift, insertion);
1408
1409 token_type = TOKEN_NONE;
1410
1411 /* The string may have expanded, and since we modify-in-place using GString, make sure
1412 * our continuation begins just beyond what we inserted, not the original match position.
1413 * Otherwise we may get confused by uri escape codes that happen to match *our* replacement
1414 * tokens (%U, %F, etc...).
1415 *
1416 * See: https://github.com/linuxmint/nemo/issues/1956
1417 */
1418 ptr = find_token_type (str->str + shift + strlen(insertion), &token_type);
1419 g_free (insertion);
1420 }
1421
1422 return str;
1423 }
1424
1425 void
nemo_action_activate(NemoAction * action,GList * selection,NemoFile * parent)1426 nemo_action_activate (NemoAction *action, GList *selection, NemoFile *parent)
1427 {
1428 GError *error;
1429 GString *exec = g_string_new (action->exec);
1430
1431 error = NULL;
1432
1433 action->escape_underscores = FALSE;
1434
1435 exec = expand_action_string (action, selection, parent, exec);
1436
1437 if (action->use_parent_dir) {
1438 exec = g_string_prepend (exec, G_DIR_SEPARATOR_S);
1439 exec = g_string_prepend (exec, action->parent_dir);
1440 }
1441
1442 DEBUG ("Action Spawning: %s", exec->str);
1443
1444 if (action->run_in_terminal) {
1445 gint argcp;
1446 gchar **argvp;
1447
1448 if (g_shell_parse_argv (exec->str, &argcp, &argvp, &error)) {
1449 nemo_launch_application_from_command_array (gdk_screen_get_default (),
1450 argvp[0],
1451 TRUE,
1452 (const char * const *)(argvp + sizeof (gchar)));
1453 g_strfreev (argvp);
1454 } else {
1455 DEBUG ("Could not parse arguments terminal launch. Possibly turn off Quotes and remove any from your Exec line: %s\n",
1456 error->message);
1457 g_error_free (error);
1458 }
1459 } else {
1460 if (!g_spawn_command_line_async (exec->str, &error)) {
1461 DEBUG ("Error spawning action: %s\n",
1462 error->message);
1463 g_error_free (error);
1464 }
1465 }
1466
1467 g_string_free (exec, TRUE);
1468 }
1469
1470 static SelectionType
nemo_action_get_selection_type(NemoAction * action)1471 nemo_action_get_selection_type (NemoAction *action)
1472 {
1473 return action->selection_type;
1474 }
1475
1476 static void
nemo_action_set_extensions(NemoAction * action,gchar ** extensions)1477 nemo_action_set_extensions (NemoAction *action, gchar **extensions)
1478 {
1479 gchar **tmp;
1480
1481 tmp = action->extensions;
1482 action->extensions = g_strdupv (extensions);
1483 g_strfreev (tmp);
1484 }
1485
1486 static gchar **
nemo_action_get_extension_list(NemoAction * action)1487 nemo_action_get_extension_list (NemoAction *action)
1488 {
1489 return action->extensions;
1490 }
1491
1492 static void
nemo_action_set_mimetypes(NemoAction * action,gchar ** mimetypes)1493 nemo_action_set_mimetypes (NemoAction *action, gchar **mimetypes)
1494 {
1495 gchar **tmp;
1496
1497 tmp = action->mimetypes;
1498 action->mimetypes = g_strdupv (mimetypes);
1499 g_strfreev (tmp);
1500 }
1501
1502 static gchar **
nemo_action_get_mimetypes_list(NemoAction * action)1503 nemo_action_get_mimetypes_list (NemoAction *action)
1504 {
1505 return action->mimetypes;
1506 }
1507
1508 static void
nemo_action_set_key_file_path(NemoAction * action,const gchar * path)1509 nemo_action_set_key_file_path (NemoAction *action, const gchar *path)
1510 {
1511 gchar *tmp;
1512 tmp = action->key_file_path;
1513 action->key_file_path = g_strdup (path);
1514 g_free (tmp);
1515 }
1516
1517 static void
nemo_action_set_exec(NemoAction * action,const gchar * exec)1518 nemo_action_set_exec (NemoAction *action, const gchar *exec)
1519 {
1520 gchar *tmp;
1521
1522 tmp = action->exec;
1523 action->exec = g_strdup (exec);
1524 g_free (tmp);
1525 }
1526
1527 static void
nemo_action_set_parent_dir(NemoAction * action,const gchar * parent_dir)1528 nemo_action_set_parent_dir (NemoAction *action, const gchar *parent_dir)
1529 {
1530 gchar *tmp;
1531
1532 tmp = action->parent_dir;
1533 action->parent_dir = g_strdup (parent_dir);
1534 g_free (tmp);
1535 }
1536
1537 static void
nemo_action_set_separator(NemoAction * action,const gchar * separator)1538 nemo_action_set_separator (NemoAction *action, const gchar *separator)
1539 {
1540 gchar *tmp;
1541
1542 tmp = action->separator;
1543 action->separator = g_strdup (separator);
1544 g_free (tmp);
1545 }
1546
1547 static void
nemo_action_set_conditions(NemoAction * action,gchar ** conditions)1548 nemo_action_set_conditions (NemoAction *action, gchar **conditions)
1549 {
1550 gchar **tmp;
1551
1552 tmp = action->conditions;
1553 action->conditions = g_strdupv (conditions);
1554 g_strfreev (tmp);
1555 }
1556
1557 static gchar **
nemo_action_get_conditions(NemoAction * action)1558 nemo_action_get_conditions (NemoAction *action)
1559 {
1560 return action->conditions;
1561 }
1562
1563 static void
nemo_action_set_orig_label(NemoAction * action,const gchar * orig_label)1564 nemo_action_set_orig_label (NemoAction *action, const gchar *orig_label)
1565 {
1566 gchar *tmp;
1567
1568 tmp = action->orig_label;
1569 action->orig_label = g_strdup (orig_label);
1570 g_free (tmp);
1571 }
1572
1573 static void
nemo_action_set_orig_tt(NemoAction * action,const gchar * orig_tt)1574 nemo_action_set_orig_tt (NemoAction *action, const gchar *orig_tt)
1575 {
1576 gchar *tmp;
1577
1578 tmp = action->orig_tt;
1579 action->orig_tt = g_strdup (orig_tt);
1580 g_free (tmp);
1581 }
1582
1583 const gchar *
nemo_action_get_orig_label(NemoAction * action)1584 nemo_action_get_orig_label (NemoAction *action)
1585 {
1586 return action->orig_label;
1587 }
1588
1589 const gchar *
nemo_action_get_orig_tt(NemoAction * action)1590 nemo_action_get_orig_tt (NemoAction *action)
1591 {
1592 return action->orig_tt;
1593 }
1594
1595
1596 gchar *
nemo_action_get_label(NemoAction * action,GList * selection,NemoFile * parent)1597 nemo_action_get_label (NemoAction *action, GList *selection, NemoFile *parent)
1598 {
1599 const gchar *orig_label = nemo_action_get_orig_label (action);
1600
1601 if (orig_label == NULL)
1602 return NULL;
1603
1604 action->escape_underscores = TRUE;
1605
1606 GString *str = g_string_new (orig_label);
1607
1608 str = expand_action_string (action, selection, parent, str);
1609
1610 DEBUG ("Action Label: %s", str->str);
1611
1612 gchar *ret = str->str;
1613 g_string_free (str, FALSE);
1614 return ret;
1615 }
1616
1617 gchar *
nemo_action_get_tt(NemoAction * action,GList * selection,NemoFile * parent)1618 nemo_action_get_tt (NemoAction *action, GList *selection, NemoFile *parent)
1619 {
1620 const gchar *orig_tt = nemo_action_get_orig_tt (action);
1621
1622 if (orig_tt == NULL)
1623 return NULL;
1624
1625 action->escape_underscores = FALSE;
1626
1627 GString *str = g_string_new (orig_tt);
1628
1629 str = expand_action_string (action, selection, parent, str);
1630
1631 DEBUG ("Action Tooltip: %s", str->str);
1632
1633 gchar *ret = str->str;
1634 g_string_free (str, FALSE);
1635 return ret;
1636 }
1637
1638 static gboolean
get_dbus_satisfied(NemoAction * action)1639 get_dbus_satisfied (NemoAction *action)
1640 {
1641 return action->dbus_satisfied;
1642 }
1643
1644 static gboolean
get_gsettings_satisfied(NemoAction * action)1645 get_gsettings_satisfied (NemoAction *action)
1646 {
1647 return action->gsettings_satisfied;
1648 }
1649
1650 static gboolean
check_exec_condition(NemoAction * action,const gchar * condition,GList * selection,NemoFile * parent)1651 check_exec_condition (NemoAction *action,
1652 const gchar *condition,
1653 GList *selection,
1654 NemoFile *parent)
1655 {
1656 GString *exec;
1657 GError *error;
1658 gint return_code;
1659 gchar *exec_str;
1660 gchar **split;
1661 gboolean use_parent_dir;
1662
1663 split = g_strsplit (condition, " ", 2);
1664
1665 if (g_strv_length (split) != 2) {
1666 g_strfreev (split);
1667 return FALSE;
1668 }
1669
1670 if (g_strcmp0 (split[0], "exec") != 0) {
1671 g_strfreev (split);
1672 return FALSE;
1673 }
1674
1675 strip_custom_modifier (split[1], &use_parent_dir, &exec_str);
1676
1677 g_strfreev (split);
1678
1679 exec = g_string_new (exec_str);
1680
1681 g_free (exec_str);
1682
1683 error = NULL;
1684
1685 action->escape_underscores = FALSE;
1686
1687 exec = expand_action_string (action, selection, parent, exec);
1688
1689 if (use_parent_dir) {
1690 exec = g_string_prepend (exec, G_DIR_SEPARATOR_S);
1691 exec = g_string_prepend (exec, action->parent_dir);
1692 }
1693
1694 DEBUG ("Checking exec condition: %s", exec->str);
1695
1696 if (!g_spawn_command_line_sync (exec->str,
1697 NULL,
1698 NULL,
1699 &return_code,
1700 &error)) {
1701 DEBUG ("Error spawning exec condition: %s\n",
1702 error->message);
1703 g_error_free (error);
1704 }
1705
1706 DEBUG ("Action checking exec condition '%s' returned: %d", exec->str, return_code);
1707
1708 g_string_free (exec, TRUE);
1709
1710 return (return_code == 0);
1711 }
1712
1713 static gboolean
get_is_dir(NemoFile * file)1714 get_is_dir (NemoFile *file)
1715 {
1716 gboolean ret = FALSE;
1717
1718 GFile *f = nemo_file_get_location (file);
1719
1720 if (g_file_is_native (f)) {
1721 gchar *path;
1722
1723 path = g_file_get_path (f);
1724 ret = g_file_test (path, G_FILE_TEST_IS_DIR);
1725 g_free (path);
1726 } else {
1727 ret = nemo_file_is_directory (file);
1728 }
1729
1730 g_object_unref (f);
1731
1732 return ret;
1733 }
1734
1735 gboolean
nemo_action_get_visibility(NemoAction * action,GList * selection,NemoFile * parent,gboolean for_places)1736 nemo_action_get_visibility (NemoAction *action,
1737 GList *selection,
1738 NemoFile *parent,
1739 gboolean for_places)
1740 {
1741 // Check DBUS
1742 if (!get_dbus_satisfied (action))
1743 return FALSE;
1744
1745 if (!get_gsettings_satisfied (action))
1746 return FALSE;
1747
1748 // Check selection
1749 gboolean selection_type_show = FALSE;
1750 SelectionType selection_type = nemo_action_get_selection_type (action);
1751
1752 guint selected_count = g_list_length (selection);
1753
1754 switch (selection_type) {
1755 case SELECTION_SINGLE:
1756 selection_type_show = selected_count == 1;
1757 break;
1758 case SELECTION_MULTIPLE:
1759 selection_type_show = selected_count > 1;
1760 break;
1761 case SELECTION_NOT_NONE:
1762 selection_type_show = selected_count > 0;
1763 break;
1764 case SELECTION_NONE:
1765 selection_type_show = selected_count == 0;
1766 break;
1767 case SELECTION_ANY:
1768 selection_type_show = TRUE;
1769 break;
1770 default:
1771 selection_type_show = selected_count == selection_type;
1772 break;
1773 }
1774
1775 if (!selection_type_show)
1776 return FALSE;
1777
1778 // Check extensions and mimetypes
1779 gboolean extension_type_show = TRUE;
1780 gchar **extensions = nemo_action_get_extension_list (action);
1781 gchar **mimetypes = nemo_action_get_mimetypes_list (action);
1782
1783 guint ext_count = extensions != NULL ? g_strv_length (extensions) : 0;
1784 guint mime_count = mimetypes != NULL ? g_strv_length (mimetypes) : 0;
1785
1786 if (ext_count == 1 && g_strcmp0 (extensions[0], "any") == 0) {
1787 extension_type_show = TRUE;
1788 }
1789 else {
1790 gboolean found_match = TRUE;
1791 GList *iter;
1792 for (iter = selection; iter != NULL && found_match; iter = iter->next) {
1793 found_match = FALSE;
1794 gboolean is_dir;
1795 gchar *raw_fn = nemo_file_get_name (NEMO_FILE (iter->data));
1796 gchar *filename = g_ascii_strdown (raw_fn, -1);
1797 g_free (raw_fn);
1798 guint i;
1799
1800 is_dir = get_is_dir (iter->data);
1801
1802 if (ext_count > 0) {
1803 for (i = 0; i < ext_count; i++) {
1804 if (g_strcmp0 (extensions[i], "dir") == 0) {
1805 if (is_dir) {
1806 found_match = TRUE;
1807 break;
1808 }
1809 } else if (g_strcmp0 (extensions[i], "none") == 0) {
1810 if (g_strrstr (filename, ".") == NULL) {
1811 found_match = TRUE;
1812 break;
1813 }
1814 } else if (g_strcmp0 (extensions[i], "nodirs") == 0) {
1815 if (!is_dir) {
1816 found_match = TRUE;
1817 break;
1818 }
1819 } else {
1820 gchar *str = g_ascii_strdown (extensions[i], -1);
1821 if (g_str_has_suffix (filename, str)) {
1822 found_match = TRUE;
1823 }
1824
1825 g_free (str);
1826
1827 if (found_match) {
1828 break;
1829 }
1830 }
1831 }
1832 }
1833
1834 g_free (filename);
1835
1836 if (mime_count > 0) {
1837 for (i = 0; i < mime_count; i++) {
1838 if (nemo_file_is_mime_type (NEMO_FILE (iter->data), mimetypes[i])) {
1839 found_match = TRUE;
1840 break;
1841 }
1842 }
1843 }
1844
1845 if (nemo_file_is_mime_type (NEMO_FILE (iter->data), "application/x-nemo-link")) {
1846 found_match = FALSE;
1847 }
1848 }
1849 extension_type_show = found_match;
1850 }
1851 if (!extension_type_show)
1852 return FALSE;
1853
1854 // Check conditions
1855 gboolean condition_type_show = TRUE;
1856 gchar **conditions = nemo_action_get_conditions (action);
1857 guint condition_count = conditions != NULL ? g_strv_length (conditions) : 0;
1858
1859 if (condition_count > 0) {
1860 guint j;
1861 gchar *condition;
1862 for (j = 0; j < condition_count; j++) {
1863 condition = conditions[j];
1864 if (g_strcmp0 (condition, "desktop") == 0) {
1865 gchar *name = nemo_file_get_display_name (parent);
1866 if (g_strcmp0 (name, "x-nemo-desktop") != 0)
1867 condition_type_show = FALSE;
1868 g_free (name);
1869 } else if (g_strcmp0 (condition, "removable") == 0) {
1870 gboolean is_removable = FALSE;
1871 if (g_list_length (selection) > 0) {
1872 NemoFile *file;
1873 GMount *mount = NULL;
1874
1875 file = NEMO_FILE (selection->data);
1876
1877 mount = nemo_file_get_mount (file);
1878
1879 /* find_enclosing_mount can block, so only bother when activated
1880 * from the places sidebar (which is strictly done on-demand),
1881 * so we don't drag down any view loads. */
1882 if (!mount && for_places) {
1883 GFile *f;
1884
1885 f = nemo_file_get_location (file);
1886
1887 if (g_file_is_native (f)) {
1888 mount = g_file_find_enclosing_mount (f, NULL, NULL);
1889 nemo_file_set_mount (file, mount);
1890 }
1891
1892 g_object_unref (f);
1893 }
1894
1895 if (mount) {
1896 GDrive *drive;
1897
1898 drive = g_mount_get_drive (mount);
1899
1900 if (drive) {
1901 if (g_drive_is_removable (drive)) {
1902 is_removable = TRUE;
1903 }
1904
1905 g_object_unref (drive);
1906 }
1907 }
1908 }
1909 condition_type_show = is_removable;
1910 } else if (g_str_has_prefix (condition, "exec")) {
1911 condition_type_show = check_exec_condition (action,
1912 condition,
1913 selection,
1914 parent);
1915 }
1916
1917 if (!condition_type_show)
1918 break;
1919 }
1920 }
1921
1922 if (!condition_type_show)
1923 return FALSE;
1924
1925 return TRUE;
1926 }
1927