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