1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 
3 /*
4  * Caja
5  *
6  * Copyright (C) 2008 Red Hat, Inc.
7  *
8  * Caja is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  * Author: David Zeuthen <davidz@redhat.com>
23  */
24 
25 #include <config.h>
26 #include <string.h>
27 #include <glib/gi18n.h>
28 #include <gio/gio.h>
29 #include <gtk/gtk.h>
30 #include <gdk/gdkx.h>
31 #include <gio/gdesktopappinfo.h>
32 #include <X11/XKBlib.h>
33 #include <gdk/gdkkeysyms.h>
34 #include <cairo-gobject.h>
35 
36 #include <eel/eel-glib-extensions.h>
37 #include <eel/eel-stock-dialogs.h>
38 
39 #include "caja-icon-info.h"
40 #include "caja-global-preferences.h"
41 #include "caja-file-operations.h"
42 #include "caja-autorun.h"
43 #include "caja-program-choosing.h"
44 #include "caja-open-with-dialog.h"
45 #include "caja-desktop-icon-file.h"
46 #include "caja-file-utilities.h"
47 
48 enum
49 {
50     AUTORUN_ASK,
51     AUTORUN_IGNORE,
52     AUTORUN_APP,
53     AUTORUN_OPEN_FOLDER,
54     AUTORUN_SEP,
55     AUTORUN_OTHER_APP,
56 };
57 enum
58 {
59     COLUMN_AUTORUN_SURFACE,
60     COLUMN_AUTORUN_NAME,
61     COLUMN_AUTORUN_APP_INFO,
62     COLUMN_AUTORUN_X_CONTENT_TYPE,
63     COLUMN_AUTORUN_ITEM_TYPE,
64 };
65 
66 static gboolean should_autorun_mount (GMount *mount);
67 
68 static void caja_autorun_rebuild_combo_box (GtkWidget *combo_box);
69 
70 void
caja_autorun_get_preferences(const char * x_content_type,gboolean * pref_start_app,gboolean * pref_ignore,gboolean * pref_open_folder)71 caja_autorun_get_preferences (const char *x_content_type,
72                               gboolean *pref_start_app,
73                               gboolean *pref_ignore,
74                               gboolean *pref_open_folder)
75 {
76     char **x_content_start_app;
77     char **x_content_ignore;
78     char **x_content_open_folder;
79 
80     g_return_if_fail (pref_start_app != NULL);
81     g_return_if_fail (pref_ignore != NULL);
82     g_return_if_fail (pref_open_folder != NULL);
83 
84     *pref_start_app = FALSE;
85     *pref_ignore = FALSE;
86     *pref_open_folder = FALSE;
87     x_content_start_app = g_settings_get_strv (caja_media_preferences, CAJA_PREFERENCES_MEDIA_AUTORUN_X_CONTENT_START_APP);
88     x_content_ignore = g_settings_get_strv (caja_media_preferences, CAJA_PREFERENCES_MEDIA_AUTORUN_X_CONTENT_IGNORE);
89     x_content_open_folder = g_settings_get_strv (caja_media_preferences, CAJA_PREFERENCES_MEDIA_AUTORUN_X_CONTENT_OPEN_FOLDER);
90     if (x_content_start_app != NULL)
91     {
92         *pref_start_app = eel_g_strv_find (x_content_start_app, x_content_type) != -1;
93     }
94     if (x_content_ignore != NULL)
95     {
96         *pref_ignore = eel_g_strv_find (x_content_ignore, x_content_type) != -1;
97     }
98     if (x_content_open_folder != NULL)
99     {
100         *pref_open_folder = eel_g_strv_find (x_content_open_folder, x_content_type) != -1;
101     }
102     g_strfreev (x_content_ignore);
103     g_strfreev (x_content_start_app);
104     g_strfreev (x_content_open_folder);
105 }
106 
107 static void
remove_elem_from_str_array(char ** v,const char * s)108 remove_elem_from_str_array (char **v, const char *s)
109 {
110     int n, m;
111 
112     if (v == NULL)
113     {
114         return;
115     }
116 
117     for (n = 0; v[n] != NULL; n++)
118     {
119         if (strcmp (v[n], s) == 0)
120         {
121             for (m = n + 1; v[m] != NULL; m++)
122             {
123                 v[m - 1] = v[m];
124             }
125             v[m - 1] = NULL;
126             n--;
127         }
128     }
129 }
130 
131 static char **
add_elem_to_str_array(char ** v,const char * s)132 add_elem_to_str_array (char **v, const char *s)
133 {
134     guint len;
135     char **r;
136 
137     len = v != NULL ? g_strv_length (v) : 0;
138     r = g_new0 (char *, len + 2);
139 
140     if (v)
141         memcpy (r, v, len * sizeof (char *));
142 
143     r[len] = g_strdup (s);
144     r[len+1] = NULL;
145     g_free (v);
146 
147     return r;
148 }
149 
150 
151 void
caja_autorun_set_preferences(const char * x_content_type,gboolean pref_start_app,gboolean pref_ignore,gboolean pref_open_folder)152 caja_autorun_set_preferences (const char *x_content_type,
153                               gboolean pref_start_app,
154                               gboolean pref_ignore,
155                               gboolean pref_open_folder)
156 {
157     char **x_content_start_app;
158     char **x_content_ignore;
159     char **x_content_open_folder;
160 
161     g_assert (x_content_type != NULL);
162 
163     x_content_start_app = g_settings_get_strv (caja_media_preferences, CAJA_PREFERENCES_MEDIA_AUTORUN_X_CONTENT_START_APP);
164     x_content_ignore = g_settings_get_strv (caja_media_preferences, CAJA_PREFERENCES_MEDIA_AUTORUN_X_CONTENT_IGNORE);
165     x_content_open_folder = g_settings_get_strv (caja_media_preferences, CAJA_PREFERENCES_MEDIA_AUTORUN_X_CONTENT_OPEN_FOLDER);
166 
167     remove_elem_from_str_array (x_content_start_app, x_content_type);
168     if (pref_start_app)
169     {
170         x_content_start_app = add_elem_to_str_array (x_content_start_app, x_content_type);
171     }
172     g_settings_set_strv (caja_media_preferences, CAJA_PREFERENCES_MEDIA_AUTORUN_X_CONTENT_START_APP, (const gchar * const*) x_content_start_app);
173 
174     remove_elem_from_str_array (x_content_ignore, x_content_type);
175     if (pref_ignore)
176     {
177         x_content_ignore = add_elem_to_str_array (x_content_ignore, x_content_type);
178     }
179     g_settings_set_strv (caja_media_preferences, CAJA_PREFERENCES_MEDIA_AUTORUN_X_CONTENT_IGNORE, (const gchar * const*) x_content_ignore);
180 
181     remove_elem_from_str_array (x_content_open_folder, x_content_type);
182     if (pref_open_folder)
183     {
184         x_content_open_folder = add_elem_to_str_array (x_content_open_folder, x_content_type);
185     }
186     g_settings_set_strv (caja_media_preferences, CAJA_PREFERENCES_MEDIA_AUTORUN_X_CONTENT_OPEN_FOLDER, (const gchar * const*) x_content_open_folder);
187 
188     g_strfreev (x_content_open_folder);
189     g_strfreev (x_content_ignore);
190     g_strfreev (x_content_start_app);
191 
192 }
193 
194 static gboolean
combo_box_separator_func(GtkTreeModel * model,GtkTreeIter * iter,gpointer data)195 combo_box_separator_func (GtkTreeModel *model,
196                           GtkTreeIter *iter,
197                           gpointer data)
198 {
199     char *str;
200 
201     gtk_tree_model_get (model, iter,
202                         1, &str,
203                         -1);
204     if (str != NULL)
205     {
206         g_free (str);
207         return FALSE;
208     }
209     return TRUE;
210 }
211 
212 typedef struct
213 {
214     guint changed_signal_id;
215     GtkWidget *combo_box;
216 
217     char *x_content_type;
218     gboolean include_ask;
219     gboolean include_open_with_other_app;
220 
221     gboolean update_settings;
222     CajaAutorunComboBoxChanged changed_cb;
223     gpointer user_data;
224 
225     gboolean other_application_selected;
226 } CajaAutorunComboBoxData;
227 
228 static void
caja_autorun_combobox_data_destroy(CajaAutorunComboBoxData * data)229 caja_autorun_combobox_data_destroy (CajaAutorunComboBoxData *data)
230 {
231     /* signal handler may be automatically disconnected by destroying the widget */
232     if (g_signal_handler_is_connected (G_OBJECT (data->combo_box), data->changed_signal_id))
233     {
234         g_signal_handler_disconnect (G_OBJECT (data->combo_box), data->changed_signal_id);
235     }
236     g_free (data->x_content_type);
237     g_free (data);
238 }
239 
240 static void
other_application_selected(CajaOpenWithDialog * dialog,GAppInfo * app_info,CajaAutorunComboBoxData * data)241 other_application_selected (CajaOpenWithDialog *dialog,
242                             GAppInfo *app_info,
243                             CajaAutorunComboBoxData *data)
244 {
245     if (data->changed_cb != NULL)
246     {
247         data->changed_cb (TRUE, FALSE, FALSE, app_info, data->user_data);
248     }
249     if (data->update_settings)
250     {
251         caja_autorun_set_preferences (data->x_content_type, TRUE, FALSE, FALSE);
252         g_app_info_set_as_default_for_type (app_info,
253                                             data->x_content_type,
254                                             NULL);
255         data->other_application_selected = TRUE;
256     }
257 
258     /* rebuild so we include and select the new application in the list */
259     caja_autorun_rebuild_combo_box (data->combo_box);
260 }
261 
262 static void
handle_dialog_closure(CajaAutorunComboBoxData * data)263 handle_dialog_closure (CajaAutorunComboBoxData *data)
264 {
265     if (!data->other_application_selected)
266     {
267         /* reset combo box so we don't linger on "Open with other Application..." */
268         caja_autorun_rebuild_combo_box (data->combo_box);
269     }
270 }
271 
272 static void
dialog_response_cb(GtkDialog * dialog,gint response,CajaAutorunComboBoxData * data)273 dialog_response_cb (GtkDialog *dialog,
274                     gint response,
275                     CajaAutorunComboBoxData *data)
276 {
277     handle_dialog_closure (data);
278 }
279 
280 static void
dialog_destroy_cb(GtkWidget * object,CajaAutorunComboBoxData * data)281 dialog_destroy_cb (GtkWidget *object,
282                    CajaAutorunComboBoxData *data)
283 {
284     handle_dialog_closure (data);
285 }
286 
287 static void
combo_box_changed(GtkComboBox * combo_box,CajaAutorunComboBoxData * data)288 combo_box_changed (GtkComboBox *combo_box,
289                    CajaAutorunComboBoxData *data)
290 {
291     GtkTreeIter iter;
292     GtkTreeModel *model;
293     GAppInfo *app_info;
294     char *x_content_type;
295     int type;
296 
297     model = NULL;
298     app_info = NULL;
299     x_content_type = NULL;
300 
301     if (!gtk_combo_box_get_active_iter (combo_box, &iter))
302     {
303         goto out;
304     }
305 
306     model = gtk_combo_box_get_model (combo_box);
307     if (model == NULL)
308     {
309         goto out;
310     }
311 
312     gtk_tree_model_get (model, &iter,
313                         COLUMN_AUTORUN_APP_INFO, &app_info,
314                         COLUMN_AUTORUN_X_CONTENT_TYPE, &x_content_type,
315                         COLUMN_AUTORUN_ITEM_TYPE, &type,
316                         -1);
317 
318     switch (type)
319     {
320     case AUTORUN_ASK:
321         if (data->changed_cb != NULL)
322         {
323             data->changed_cb (TRUE, FALSE, FALSE, NULL, data->user_data);
324         }
325         if (data->update_settings)
326         {
327             caja_autorun_set_preferences (x_content_type, FALSE, FALSE, FALSE);
328         }
329         break;
330     case AUTORUN_IGNORE:
331         if (data->changed_cb != NULL)
332         {
333             data->changed_cb (FALSE, TRUE, FALSE, NULL, data->user_data);
334         }
335         if (data->update_settings)
336         {
337             caja_autorun_set_preferences (x_content_type, FALSE, TRUE, FALSE);
338         }
339         break;
340     case AUTORUN_OPEN_FOLDER:
341         if (data->changed_cb != NULL)
342         {
343             data->changed_cb (FALSE, FALSE, TRUE, NULL, data->user_data);
344         }
345         if (data->update_settings)
346         {
347             caja_autorun_set_preferences (x_content_type, FALSE, FALSE, TRUE);
348         }
349         break;
350 
351     case AUTORUN_APP:
352         if (data->changed_cb != NULL)
353         {
354             /* TODO TODO?? */
355             data->changed_cb (TRUE, FALSE, FALSE, app_info, data->user_data);
356         }
357         if (data->update_settings)
358         {
359             caja_autorun_set_preferences (x_content_type, TRUE, FALSE, FALSE);
360             g_app_info_set_as_default_for_type (app_info,
361                                                 x_content_type,
362                                                 NULL);
363         }
364         break;
365 
366     case AUTORUN_OTHER_APP:
367     {
368         GtkWidget *dialog;
369 
370         data->other_application_selected = FALSE;
371 
372         dialog = caja_add_application_dialog_new (NULL, x_content_type);
373         gtk_window_set_transient_for (GTK_WINDOW (dialog),
374                                       GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (combo_box))));
375         g_signal_connect (dialog, "application_selected",
376                           G_CALLBACK (other_application_selected),
377                           data);
378         g_signal_connect (dialog, "response",
379                           G_CALLBACK (dialog_response_cb), data);
380         g_signal_connect (dialog, "destroy",
381                           G_CALLBACK (dialog_destroy_cb), data);
382         gtk_widget_show (GTK_WIDGET (dialog));
383 
384         break;
385     }
386 
387     }
388 
389 out:
390     if (app_info != NULL)
391     {
392         g_object_unref (app_info);
393     }
394     g_free (x_content_type);
395 }
396 
397 static void
caja_autorun_rebuild_combo_box(GtkWidget * combo_box)398 caja_autorun_rebuild_combo_box (GtkWidget *combo_box)
399 {
400     CajaAutorunComboBoxData *data;
401     char *x_content_type;
402 
403     data = g_object_get_data (G_OBJECT (combo_box), "caja_autorun_combobox_data");
404     if (data == NULL)
405     {
406         g_warning ("no 'caja_autorun_combobox_data' data!");
407         return;
408     }
409 
410     x_content_type = g_strdup (data->x_content_type);
411     caja_autorun_prepare_combo_box (combo_box,
412                                     x_content_type,
413                                     data->include_ask,
414                                     data->include_open_with_other_app,
415                                     data->update_settings,
416                                     data->changed_cb,
417                                     data->user_data);
418     g_free (x_content_type);
419 }
420 
421 /* TODO: we need some kind of way to remove user-defined associations,
422  * e.g. the result of "Open with other Application...".
423  *
424  * However, this is a bit hard as
425  * g_app_info_can_remove_supports_type() will always return TRUE
426  * because we now have [Removed Applications] in the file
427  * ~/.local/share/applications/mimeapps.list.
428  *
429  * We need the API outlined in
430  *
431  *  https://bugzilla.gnome.org/show_bug.cgi?id=545350
432  *
433  * to do this.
434  *
435  * Now, there's also the question about what the UI would look like
436  * given this API. Ideally we'd include a small button on the right
437  * side of the combo box that the user can press to delete an
438  * association, e.g.:
439  *
440  *  +-------------------------------------+
441  *  | Ask what to do                      |
442  *  | Do Nothing                          |
443  *  | Open Folder                         |
444  *  +-------------------------------------+
445  *  | Open Rhythmbox Music Player         |
446  *  | Open Audio CD Extractor             |
447  *  | Open Banshee Media Player           |
448  *  | Open Frobnicator App            [x] |
449  *  +-------------------------------------+
450  *  | Open with other Application...      |
451  *  +-------------------------------------+
452  *
453  * where "Frobnicator App" have been set up using "Open with other
454  * Application...". However this is not accessible (which is a
455  * GTK+ issue) but probably not a big deal.
456  *
457  * And we only want show these buttons (e.g. [x]) for associations with
458  * GAppInfo instances that are deletable.
459  */
460 
461 void
caja_autorun_prepare_combo_box(GtkWidget * combo_box,const char * x_content_type,gboolean include_ask,gboolean include_open_with_other_app,gboolean update_settings,CajaAutorunComboBoxChanged changed_cb,gpointer user_data)462 caja_autorun_prepare_combo_box (GtkWidget *combo_box,
463                                 const char *x_content_type,
464                                 gboolean include_ask,
465                                 gboolean include_open_with_other_app,
466                                 gboolean update_settings,
467                                 CajaAutorunComboBoxChanged changed_cb,
468                                 gpointer user_data)
469 {
470     GList *l;
471     GList *app_info_list;
472     GAppInfo *default_app_info;
473     GtkListStore *list_store;
474     GtkTreeIter iter;
475     cairo_surface_t *surface;
476     int icon_size, icon_scale;
477     int set_active;
478     int num_apps;
479     gboolean pref_ask;
480     gboolean pref_start_app;
481     gboolean pref_ignore;
482     gboolean pref_open_folder;
483     CajaAutorunComboBoxData *data;
484     GtkCellRenderer *renderer;
485     gboolean new_data;
486 
487     caja_autorun_get_preferences (x_content_type, &pref_start_app, &pref_ignore, &pref_open_folder);
488     pref_ask = !pref_start_app && !pref_ignore && !pref_open_folder;
489 
490     icon_size = caja_get_icon_size_for_stock_size (GTK_ICON_SIZE_MENU);
491     icon_scale = gtk_widget_get_scale_factor (combo_box);
492 
493     set_active = -1;
494     data = NULL;
495     new_data = TRUE;
496 
497     app_info_list = g_app_info_get_all_for_type (x_content_type);
498     default_app_info = g_app_info_get_default_for_type (x_content_type, FALSE);
499     num_apps = g_list_length (app_info_list);
500 
501     list_store = gtk_list_store_new (5,
502                                      CAIRO_GOBJECT_TYPE_SURFACE,
503                                      G_TYPE_STRING,
504                                      G_TYPE_APP_INFO,
505                                      G_TYPE_STRING,
506                                      G_TYPE_INT);
507 
508     /* no apps installed */
509     if (num_apps == 0)
510     {
511         gtk_list_store_append (list_store, &iter);
512         surface = gtk_icon_theme_load_surface (gtk_icon_theme_get_default (),
513                                                "dialog-error",
514                                                icon_size,
515                                                icon_scale,
516                                                NULL,
517                                                0,
518                                                NULL);
519 
520         /* TODO: integrate with PackageKit-mate to find applications */
521 
522         gtk_list_store_set (list_store, &iter,
523                             COLUMN_AUTORUN_SURFACE, surface,
524                             COLUMN_AUTORUN_NAME, _("No applications found"),
525                             COLUMN_AUTORUN_APP_INFO, NULL,
526                             COLUMN_AUTORUN_X_CONTENT_TYPE, x_content_type,
527                             COLUMN_AUTORUN_ITEM_TYPE, AUTORUN_ASK,
528                             -1);
529         cairo_surface_destroy (surface);
530     }
531     else
532     {
533         if (include_ask)
534         {
535             gtk_list_store_append (list_store, &iter);
536             surface = gtk_icon_theme_load_surface (gtk_icon_theme_get_default (),
537                                                    "dialog-question",
538                                                    icon_size,
539                                                    icon_scale,
540                                                    NULL,
541                                                    0,
542                                                    NULL);
543             gtk_list_store_set (list_store, &iter,
544                                 COLUMN_AUTORUN_SURFACE, surface,
545                                 COLUMN_AUTORUN_NAME, _("Ask what to do"),
546                                 COLUMN_AUTORUN_APP_INFO, NULL,
547                                 COLUMN_AUTORUN_X_CONTENT_TYPE, x_content_type,
548                                 COLUMN_AUTORUN_ITEM_TYPE, AUTORUN_ASK,
549                                 -1);
550             cairo_surface_destroy (surface);
551         }
552 
553         gtk_list_store_append (list_store, &iter);
554         surface = gtk_icon_theme_load_surface (gtk_icon_theme_get_default (),
555                                                "window-close",
556                                                icon_size,
557                                                icon_scale,
558                                                NULL,
559                                                0,
560                                                NULL);
561         gtk_list_store_set (list_store, &iter,
562                             COLUMN_AUTORUN_SURFACE, surface,
563                             COLUMN_AUTORUN_NAME, _("Do Nothing"),
564                             COLUMN_AUTORUN_APP_INFO, NULL,
565                             COLUMN_AUTORUN_X_CONTENT_TYPE, x_content_type,
566                             COLUMN_AUTORUN_ITEM_TYPE, AUTORUN_IGNORE,
567                             -1);
568         cairo_surface_destroy (surface);
569 
570         gtk_list_store_append (list_store, &iter);
571         surface = gtk_icon_theme_load_surface (gtk_icon_theme_get_default (),
572                                                "folder-open",
573                                                icon_size,
574                                                icon_scale,
575                                                NULL,
576                                                0,
577                                                NULL);
578         gtk_list_store_set (list_store, &iter,
579                             COLUMN_AUTORUN_SURFACE, surface,
580                             COLUMN_AUTORUN_NAME, _("Open Folder"),
581                             COLUMN_AUTORUN_APP_INFO, NULL,
582                             COLUMN_AUTORUN_X_CONTENT_TYPE, x_content_type,
583                             COLUMN_AUTORUN_ITEM_TYPE, AUTORUN_OPEN_FOLDER,
584                             -1);
585         cairo_surface_destroy (surface);
586 
587         gtk_list_store_append (list_store, &iter);
588         gtk_list_store_set (list_store, &iter,
589                             COLUMN_AUTORUN_SURFACE, NULL,
590                             COLUMN_AUTORUN_NAME, NULL,
591                             COLUMN_AUTORUN_APP_INFO, NULL,
592                             COLUMN_AUTORUN_X_CONTENT_TYPE, NULL,
593                             COLUMN_AUTORUN_ITEM_TYPE, AUTORUN_SEP,
594                             -1);
595 
596         int n;
597 
598         for (l = app_info_list, n = include_ask ? 4 : 3; l != NULL; l = l->next, n++)
599         {
600             GIcon *icon;
601             CajaIconInfo *icon_info;
602             char *open_string;
603             GAppInfo *app_info = l->data;
604 
605             /* we deliberately ignore should_show because some apps might want
606              * to install special handlers that should be hidden in the regular
607              * application launcher menus
608              */
609 
610             icon = g_app_info_get_icon (app_info);
611             icon_info = caja_icon_info_lookup (icon, icon_size, icon_scale);
612             surface = caja_icon_info_get_surface_at_size (icon_info, icon_size);
613             g_object_unref (icon_info);
614 
615             open_string = g_strdup_printf (_("Open %s"), g_app_info_get_display_name (app_info));
616 
617             gtk_list_store_append (list_store, &iter);
618             gtk_list_store_set (list_store, &iter,
619                                 COLUMN_AUTORUN_SURFACE, surface,
620                                 COLUMN_AUTORUN_NAME, open_string,
621                                 COLUMN_AUTORUN_APP_INFO, app_info,
622                                 COLUMN_AUTORUN_X_CONTENT_TYPE, x_content_type,
623                                 COLUMN_AUTORUN_ITEM_TYPE, AUTORUN_APP,
624                                 -1);
625             if (surface != NULL)
626             {
627                 cairo_surface_destroy (surface);
628             }
629             g_free (open_string);
630 
631             if (g_app_info_equal (app_info, default_app_info))
632             {
633                 set_active = n;
634             }
635         }
636     }
637 
638     if (include_open_with_other_app)
639     {
640         gtk_list_store_append (list_store, &iter);
641         gtk_list_store_set (list_store, &iter,
642                             COLUMN_AUTORUN_SURFACE, NULL,
643                             COLUMN_AUTORUN_NAME, NULL,
644                             COLUMN_AUTORUN_APP_INFO, NULL,
645                             COLUMN_AUTORUN_X_CONTENT_TYPE, NULL,
646                             COLUMN_AUTORUN_ITEM_TYPE, AUTORUN_SEP,
647                             -1);
648 
649         gtk_list_store_append (list_store, &iter);
650         surface = gtk_icon_theme_load_surface (gtk_icon_theme_get_default (),
651                                                "application-x-executable",
652                                                icon_size,
653                                                icon_scale,
654                                                NULL,
655                                                0,
656                                                NULL);
657         gtk_list_store_set (list_store, &iter,
658                             COLUMN_AUTORUN_SURFACE, surface,
659                             COLUMN_AUTORUN_NAME, _("Open with other Application..."),
660                             COLUMN_AUTORUN_APP_INFO, NULL,
661                             COLUMN_AUTORUN_X_CONTENT_TYPE, x_content_type,
662                             COLUMN_AUTORUN_ITEM_TYPE, AUTORUN_OTHER_APP,
663                             -1);
664         cairo_surface_destroy (surface);
665     }
666 
667     if (default_app_info != NULL)
668     {
669         g_object_unref (default_app_info);
670     }
671     g_list_free_full (app_info_list, g_object_unref);
672 
673     gtk_combo_box_set_model (GTK_COMBO_BOX (combo_box), GTK_TREE_MODEL (list_store));
674     g_object_unref (G_OBJECT (list_store));
675 
676     gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo_box));
677 
678     renderer = gtk_cell_renderer_pixbuf_new ();
679     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, FALSE);
680     gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer,
681                                     "surface", COLUMN_AUTORUN_SURFACE,
682                                     NULL);
683     renderer = gtk_cell_renderer_text_new ();
684     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, TRUE);
685     gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer,
686                                     "text", COLUMN_AUTORUN_NAME,
687                                     NULL);
688     gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo_box), combo_box_separator_func, NULL, NULL);
689 
690     if (num_apps == 0)
691     {
692         gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), 0);
693         gtk_widget_set_sensitive (combo_box, FALSE);
694     }
695     else
696     {
697         gtk_widget_set_sensitive (combo_box, TRUE);
698         if (pref_ask && include_ask)
699         {
700             gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), 0);
701         }
702         else if (pref_ignore)
703         {
704             gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), include_ask ? 1 : 0);
705         }
706         else if (pref_open_folder)
707         {
708             gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), include_ask ? 2 : 1);
709         }
710         else if (set_active != -1)
711         {
712             gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), set_active);
713         }
714         else
715         {
716             gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), include_ask ? 1 : 0);
717         }
718 
719         /* See if we have an old data around */
720         data = g_object_get_data (G_OBJECT (combo_box), "caja_autorun_combobox_data");
721         if (data)
722         {
723             new_data = FALSE;
724             g_free (data->x_content_type);
725         }
726         else
727         {
728             data = g_new0 (CajaAutorunComboBoxData, 1);
729         }
730 
731         data->x_content_type = g_strdup (x_content_type);
732         data->include_ask = include_ask;
733         data->include_open_with_other_app = include_open_with_other_app;
734         data->update_settings = update_settings;
735         data->changed_cb = changed_cb;
736         data->user_data = user_data;
737         data->combo_box = combo_box;
738         if (data->changed_signal_id == 0)
739         {
740             data->changed_signal_id = g_signal_connect (G_OBJECT (combo_box),
741                                       "changed",
742                                       G_CALLBACK (combo_box_changed),
743                                       data);
744         }
745     }
746 
747     if (new_data)
748     {
749         g_object_set_data_full (G_OBJECT (combo_box),
750                                 "caja_autorun_combobox_data",
751                                 data,
752                                 (GDestroyNotify) caja_autorun_combobox_data_destroy);
753     }
754 }
755 
756 static gboolean
is_shift_pressed(void)757 is_shift_pressed (void)
758 {
759     gboolean ret;
760     GdkDisplay *display;
761     XkbStateRec state;
762     Bool status;
763 
764     ret = FALSE;
765 
766     display = gdk_display_get_default ();
767     gdk_x11_display_error_trap_push (display);
768     status = XkbGetState (GDK_DISPLAY_XDISPLAY (display),
769                           XkbUseCoreKbd, &state);
770     gdk_x11_display_error_trap_pop_ignored (display);
771 
772     if (status == Success)
773     {
774         ret = state.mods & ShiftMask;
775     }
776 
777     return ret;
778 }
779 
780 enum
781 {
782     AUTORUN_DIALOG_RESPONSE_EJECT = 0
783 };
784 
785 typedef struct
786 {
787     GtkWidget *dialog;
788 
789     GMount *mount;
790     gboolean should_eject;
791 
792     gboolean selected_ignore;
793     gboolean selected_open_folder;
794     GAppInfo *selected_app;
795 
796     gboolean remember;
797 
798     char *x_content_type;
799 
800     CajaAutorunOpenWindow open_window_func;
801     gpointer user_data;
802 } AutorunDialogData;
803 
804 
805 void
caja_autorun_launch_for_mount(GMount * mount,GAppInfo * app_info)806 caja_autorun_launch_for_mount (GMount *mount, GAppInfo *app_info)
807 {
808     GFile *root;
809     CajaFile *file;
810     GList *files;
811 
812     root = g_mount_get_root (mount);
813     file = caja_file_get (root);
814     g_object_unref (root);
815     files = g_list_append (NULL, file);
816     caja_launch_application (app_info,
817                              files,
818                              NULL); /* TODO: what to set here? */
819     g_object_unref (file);
820     g_list_free (files);
821 }
822 
823 static void autorun_dialog_mount_unmounted (GMount *mount, AutorunDialogData *data);
824 
825 static void
autorun_dialog_destroy(AutorunDialogData * data)826 autorun_dialog_destroy (AutorunDialogData *data)
827 {
828     g_signal_handlers_disconnect_by_func (G_OBJECT (data->mount),
829                                           G_CALLBACK (autorun_dialog_mount_unmounted),
830                                           data);
831 
832     gtk_widget_destroy (GTK_WIDGET (data->dialog));
833     if (data->selected_app != NULL)
834     {
835         g_object_unref (data->selected_app);
836     }
837     g_object_unref (data->mount);
838     g_free (data->x_content_type);
839     g_free (data);
840 }
841 
842 static void
autorun_dialog_mount_unmounted(GMount * mount,AutorunDialogData * data)843 autorun_dialog_mount_unmounted (GMount *mount, AutorunDialogData *data)
844 {
845     /* remove the dialog if the media is unmounted */
846     autorun_dialog_destroy (data);
847 }
848 
849 static void
autorun_dialog_response(GtkDialog * dialog,gint response,AutorunDialogData * data)850 autorun_dialog_response (GtkDialog *dialog, gint response, AutorunDialogData *data)
851 {
852     switch (response)
853     {
854     case AUTORUN_DIALOG_RESPONSE_EJECT:
855         caja_file_operations_unmount_mount (GTK_WINDOW (dialog),
856                                             data->mount,
857                                             data->should_eject,
858                                             FALSE);
859         break;
860 
861     case GTK_RESPONSE_NONE:
862         /* window was closed */
863         break;
864     case GTK_RESPONSE_CANCEL:
865         break;
866     case GTK_RESPONSE_OK:
867         /* do the selected action */
868 
869         if (data->remember)
870         {
871             /* make sure we don't ask again */
872             caja_autorun_set_preferences (data->x_content_type, TRUE, data->selected_ignore, data->selected_open_folder);
873             if (!data->selected_ignore && !data->selected_open_folder && data->selected_app != NULL)
874             {
875                 g_app_info_set_as_default_for_type (data->selected_app,
876                                                     data->x_content_type,
877                                                     NULL);
878             }
879         }
880         else
881         {
882             /* make sure we do ask again */
883             caja_autorun_set_preferences (data->x_content_type, FALSE, FALSE, FALSE);
884         }
885 
886         if (!data->selected_ignore && !data->selected_open_folder && data->selected_app != NULL)
887         {
888             caja_autorun_launch_for_mount (data->mount, data->selected_app);
889         }
890         else if (!data->selected_ignore && data->selected_open_folder)
891         {
892             if (data->open_window_func != NULL)
893                 data->open_window_func (data->mount, data->user_data);
894         }
895         break;
896     }
897 
898     autorun_dialog_destroy (data);
899 }
900 
901 static void
autorun_combo_changed(gboolean selected_ask,gboolean selected_ignore,gboolean selected_open_folder,GAppInfo * selected_app,gpointer user_data)902 autorun_combo_changed (gboolean selected_ask,
903                        gboolean selected_ignore,
904                        gboolean selected_open_folder,
905                        GAppInfo *selected_app,
906                        gpointer user_data)
907 {
908     AutorunDialogData *data = user_data;
909 
910     if (data->selected_app != NULL)
911     {
912         g_object_unref (data->selected_app);
913     }
914     data->selected_app = selected_app != NULL ? g_object_ref (selected_app) : NULL;
915     data->selected_ignore = selected_ignore;
916     data->selected_open_folder = selected_open_folder;
917 }
918 
919 
920 static void
autorun_always_toggled(GtkToggleButton * togglebutton,AutorunDialogData * data)921 autorun_always_toggled (GtkToggleButton *togglebutton, AutorunDialogData *data)
922 {
923     data->remember = gtk_toggle_button_get_active (togglebutton);
924 }
925 
926 static gboolean
combo_box_enter_ok(GtkWidget * togglebutton,GdkEventKey * event,GtkDialog * dialog)927 combo_box_enter_ok (GtkWidget *togglebutton, GdkEventKey *event, GtkDialog *dialog)
928 {
929     if (event->keyval == GDK_KEY_KP_Enter || event->keyval == GDK_KEY_Return)
930     {
931         gtk_dialog_response (dialog, GTK_RESPONSE_OK);
932         return TRUE;
933     }
934     return FALSE;
935 }
936 
937 /* returns TRUE if a folder window should be opened */
938 static gboolean
do_autorun_for_content_type(GMount * mount,const char * x_content_type,CajaAutorunOpenWindow open_window_func,gpointer user_data)939 do_autorun_for_content_type (GMount *mount, const char *x_content_type, CajaAutorunOpenWindow open_window_func, gpointer user_data)
940 {
941     AutorunDialogData *data;
942     GtkWidget *dialog;
943     GtkWidget *hbox;
944     GtkWidget *vbox;
945     GtkWidget *label;
946     GtkWidget *combo_box;
947     GtkWidget *always_check_button;
948     GtkWidget *eject_button;
949     GtkWidget *image;
950     GtkWidget *action_area;
951     char *markup;
952     char *content_description;
953     char *mount_name;
954     GIcon *icon;
955     GdkPixbuf *pixbuf;
956     cairo_surface_t *surface;
957     CajaIconInfo *icon_info;
958     int icon_size, icon_scale;
959     gboolean user_forced_dialog;
960     gboolean pref_ask;
961     gboolean pref_start_app;
962     gboolean pref_ignore;
963     gboolean pref_open_folder;
964     char *media_greeting;
965     gboolean ret;
966 
967     ret = FALSE;
968     mount_name = NULL;
969 
970     if (g_content_type_is_a (x_content_type, "x-content/win32-software"))
971     {
972         /* don't pop up the dialog anyway if the content type says
973          * windows software.
974          */
975         goto out;
976     }
977 
978     user_forced_dialog = is_shift_pressed ();
979 
980     caja_autorun_get_preferences (x_content_type, &pref_start_app, &pref_ignore, &pref_open_folder);
981     pref_ask = !pref_start_app && !pref_ignore && !pref_open_folder;
982 
983     if (user_forced_dialog)
984     {
985         goto show_dialog;
986     }
987 
988     if (!pref_ask && !pref_ignore && !pref_open_folder)
989     {
990         GAppInfo *app_info;
991         app_info = g_app_info_get_default_for_type (x_content_type, FALSE);
992         if (app_info != NULL)
993         {
994             caja_autorun_launch_for_mount (mount, app_info);
995         }
996         goto out;
997     }
998 
999     if (pref_open_folder)
1000     {
1001         ret = TRUE;
1002         goto out;
1003     }
1004 
1005     if (pref_ignore)
1006     {
1007         goto out;
1008     }
1009 
1010 show_dialog:
1011 
1012     mount_name = g_mount_get_name (mount);
1013 
1014     dialog = gtk_dialog_new ();
1015 
1016     hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
1017     gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), hbox, TRUE, TRUE, 0);
1018     gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
1019 
1020     icon = g_mount_get_icon (mount);
1021     icon_size = caja_get_icon_size_for_stock_size (GTK_ICON_SIZE_DIALOG);
1022     icon_scale = gtk_widget_get_scale_factor (dialog);
1023     icon_info = caja_icon_info_lookup (icon, icon_size, icon_scale);
1024     pixbuf = caja_icon_info_get_pixbuf_at_size (icon_info, icon_size);
1025     surface = caja_icon_info_get_surface_at_size (icon_info, icon_size);
1026     g_object_unref (icon_info);
1027     g_object_unref (icon);
1028     image = gtk_image_new_from_surface (surface);
1029     gtk_widget_set_halign (image, GTK_ALIGN_CENTER);
1030     gtk_widget_set_valign (image, GTK_ALIGN_START);
1031     gtk_box_pack_start (GTK_BOX (hbox), image, TRUE, TRUE, 0);
1032     /* also use the icon on the dialog */
1033     gtk_window_set_title (GTK_WINDOW (dialog), mount_name);
1034     gtk_window_set_icon (GTK_WINDOW (dialog), pixbuf);
1035     gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
1036     g_object_unref (pixbuf);
1037     cairo_surface_destroy (surface);
1038 
1039     vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
1040     gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1041 
1042     label = gtk_label_new (NULL);
1043 
1044 
1045     /* Customize greeting for well-known x-content types */
1046     if (strcmp (x_content_type, "x-content/audio-cdda") == 0)
1047     {
1048         media_greeting = _("You have just inserted an Audio CD.");
1049     }
1050     else if (strcmp (x_content_type, "x-content/audio-dvd") == 0)
1051     {
1052         media_greeting = _("You have just inserted an Audio DVD.");
1053     }
1054     else if (strcmp (x_content_type, "x-content/video-dvd") == 0)
1055     {
1056         media_greeting = _("You have just inserted a Video DVD.");
1057     }
1058     else if (strcmp (x_content_type, "x-content/video-vcd") == 0)
1059     {
1060         media_greeting = _("You have just inserted a Video CD.");
1061     }
1062     else if (strcmp (x_content_type, "x-content/video-svcd") == 0)
1063     {
1064         media_greeting = _("You have just inserted a Super Video CD.");
1065     }
1066     else if (strcmp (x_content_type, "x-content/blank-cd") == 0)
1067     {
1068         media_greeting = _("You have just inserted a blank CD.");
1069     }
1070     else if (strcmp (x_content_type, "x-content/blank-dvd") == 0)
1071     {
1072         media_greeting = _("You have just inserted a blank DVD.");
1073     }
1074     else if (strcmp (x_content_type, "x-content/blank-bd") == 0)
1075     {
1076         media_greeting = _("You have just inserted a blank Blu-Ray disc.");
1077     }
1078     else if (strcmp (x_content_type, "x-content/blank-hddvd") == 0)
1079     {
1080         media_greeting = _("You have just inserted a blank HD DVD.");
1081     }
1082     else if (strcmp (x_content_type, "x-content/image-photocd") == 0)
1083     {
1084         media_greeting = _("You have just inserted a Photo CD.");
1085     }
1086     else if (strcmp (x_content_type, "x-content/image-picturecd") == 0)
1087     {
1088         media_greeting = _("You have just inserted a Picture CD.");
1089     }
1090     else if (strcmp (x_content_type, "x-content/image-dcf") == 0)
1091     {
1092         media_greeting = _("You have just inserted a medium with digital photos.");
1093     }
1094     else if (strcmp (x_content_type, "x-content/audio-player") == 0)
1095     {
1096         media_greeting = _("You have just inserted a digital audio player.");
1097     }
1098     else if (g_content_type_is_a (x_content_type, "x-content/software"))
1099     {
1100         media_greeting = _("You have just inserted a medium with software intended to be automatically started.");
1101     }
1102     else
1103     {
1104         /* fallback to generic greeting */
1105         media_greeting = _("You have just inserted a medium.");
1106     }
1107     markup = g_strdup_printf ("<big><b>%s %s</b></big>", media_greeting, _("Choose what application to launch."));
1108     gtk_label_set_markup (GTK_LABEL (label), markup);
1109     g_free (markup);
1110     gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1111     gtk_label_set_max_width_chars (GTK_LABEL (label), 50);
1112     gtk_label_set_xalign (GTK_LABEL (label), 0);
1113     gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
1114 
1115     label = gtk_label_new (NULL);
1116     content_description = g_content_type_get_description (x_content_type);
1117     markup = g_strdup_printf (_("Select how to open \"%s\" and whether to perform this action in the future for other media of type \"%s\"."), mount_name, content_description);
1118     g_free (content_description);
1119     gtk_label_set_markup (GTK_LABEL (label), markup);
1120     g_free (markup);
1121     gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1122     gtk_label_set_max_width_chars (GTK_LABEL (label), 50);
1123     gtk_label_set_xalign (GTK_LABEL (label), 0);
1124     gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
1125 
1126     data = g_new0 (AutorunDialogData, 1);
1127     data->dialog = dialog;
1128     data->mount = g_object_ref (mount);
1129     data->remember = !pref_ask;
1130     data->selected_ignore = pref_ignore;
1131     data->x_content_type = g_strdup (x_content_type);
1132     data->selected_app = g_app_info_get_default_for_type (x_content_type, FALSE);
1133     data->open_window_func = open_window_func;
1134     data->user_data = user_data;
1135 
1136     combo_box = gtk_combo_box_new ();
1137     caja_autorun_prepare_combo_box (combo_box, x_content_type, FALSE, TRUE, FALSE, autorun_combo_changed, data);
1138     g_signal_connect (G_OBJECT (combo_box),
1139                       "key-press-event",
1140                       G_CALLBACK (combo_box_enter_ok),
1141                       dialog);
1142 
1143     gtk_box_pack_start (GTK_BOX (vbox), combo_box, TRUE, TRUE, 0);
1144 
1145     always_check_button = gtk_check_button_new_with_mnemonic (_("_Always perform this action"));
1146     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (always_check_button), data->remember);
1147     g_signal_connect (G_OBJECT (always_check_button),
1148                       "toggled",
1149                       G_CALLBACK (autorun_always_toggled),
1150                       data);
1151     gtk_box_pack_start (GTK_BOX (vbox), always_check_button, TRUE, TRUE, 0);
1152 
1153     eel_dialog_add_button (GTK_DIALOG (dialog),
1154                            _("_Cancel"),
1155                            "process-stop",
1156                            GTK_RESPONSE_CANCEL);
1157 
1158     action_area = gtk_widget_get_parent (eel_dialog_add_button (GTK_DIALOG (dialog),
1159                                                                 _("_OK"),
1160                                                                 "gtk-ok",
1161                                                                 GTK_RESPONSE_OK));
1162 
1163     gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
1164 
1165     if (g_mount_can_eject (mount))
1166     {
1167         GtkWidget *eject_image;
1168         eject_button = gtk_button_new_with_mnemonic (_("_Eject"));
1169         surface = gtk_icon_theme_load_surface (gtk_icon_theme_get_default (),
1170                                                "media-eject",
1171                                                caja_get_icon_size_for_stock_size (GTK_ICON_SIZE_BUTTON),
1172                                                icon_scale,
1173                                                NULL,
1174                                                0,
1175                                                NULL);
1176         eject_image = gtk_image_new_from_surface (surface);
1177         cairo_surface_destroy (surface);
1178         gtk_button_set_image (GTK_BUTTON (eject_button), eject_image);
1179         data->should_eject = TRUE;
1180     }
1181     else
1182     {
1183         eject_button = gtk_button_new_with_mnemonic (_("_Unmount"));
1184         data->should_eject = FALSE;
1185     }
1186     gtk_dialog_add_action_widget (GTK_DIALOG (dialog), eject_button, AUTORUN_DIALOG_RESPONSE_EJECT);
1187     gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (action_area), eject_button, TRUE);
1188 
1189     /* show the dialog */
1190     gtk_widget_show_all (dialog);
1191 
1192     g_signal_connect (G_OBJECT (dialog),
1193                       "response",
1194                       G_CALLBACK (autorun_dialog_response),
1195                       data);
1196 
1197     g_signal_connect (G_OBJECT (data->mount),
1198                       "unmounted",
1199                       G_CALLBACK (autorun_dialog_mount_unmounted),
1200                       data);
1201 
1202 out:
1203     g_free (mount_name);
1204     return ret;
1205 }
1206 
1207 typedef struct
1208 {
1209     GMount *mount;
1210     CajaAutorunOpenWindow open_window_func;
1211     gpointer user_data;
1212 } AutorunData;
1213 
1214 static void
autorun_guessed_content_type_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)1215 autorun_guessed_content_type_callback (GObject *source_object,
1216                                        GAsyncResult *res,
1217                                        gpointer user_data)
1218 {
1219     GError *error;
1220     char **guessed_content_type;
1221     AutorunData *data = user_data;
1222     gboolean open_folder;
1223 
1224     open_folder = FALSE;
1225 
1226     error = NULL;
1227     guessed_content_type = g_mount_guess_content_type_finish (G_MOUNT (source_object), res, &error);
1228     g_object_set_data_full (source_object,
1229                             "caja-content-type-cache",
1230                             g_strdupv (guessed_content_type),
1231                             (GDestroyNotify)g_strfreev);
1232     if (error != NULL)
1233     {
1234         g_warning ("Unabled to guess content type for mount: %s", error->message);
1235         g_error_free (error);
1236     }
1237     else
1238     {
1239         if (guessed_content_type != NULL && g_strv_length (guessed_content_type) > 0)
1240         {
1241             int n;
1242             for (n = 0; guessed_content_type[n] != NULL; n++)
1243             {
1244                 if (do_autorun_for_content_type (data->mount, guessed_content_type[n],
1245                                                  data->open_window_func, data->user_data))
1246                 {
1247                     open_folder = TRUE;
1248                 }
1249             }
1250             g_strfreev (guessed_content_type);
1251         }
1252         else
1253         {
1254             if (g_settings_get_boolean (caja_media_preferences, CAJA_PREFERENCES_MEDIA_AUTOMOUNT_OPEN))
1255                 open_folder = TRUE;
1256         }
1257     }
1258 
1259     /* only open the folder once.. */
1260     if (open_folder && data->open_window_func != NULL)
1261     {
1262         data->open_window_func (data->mount, data->user_data);
1263     }
1264 
1265     g_object_unref (data->mount);
1266     g_free (data);
1267 }
1268 
1269 void
caja_autorun(GMount * mount,CajaAutorunOpenWindow open_window_func,gpointer user_data)1270 caja_autorun (GMount *mount, CajaAutorunOpenWindow open_window_func, gpointer user_data)
1271 {
1272     AutorunData *data;
1273 
1274     if (!should_autorun_mount (mount) ||
1275             g_settings_get_boolean (caja_media_preferences, CAJA_PREFERENCES_MEDIA_AUTORUN_NEVER))
1276     {
1277         return;
1278     }
1279 
1280     data = g_new0 (AutorunData, 1);
1281     data->mount = g_object_ref (mount);
1282     data->open_window_func = open_window_func;
1283     data->user_data = user_data;
1284 
1285     g_mount_guess_content_type (mount,
1286                                 FALSE,
1287                                 NULL,
1288                                 autorun_guessed_content_type_callback,
1289                                 data);
1290 }
1291 
1292 typedef struct
1293 {
1294     CajaAutorunGetContent callback;
1295     gpointer user_data;
1296 } GetContentTypesData;
1297 
1298 static void
get_types_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)1299 get_types_cb (GObject *source_object,
1300               GAsyncResult *res,
1301               gpointer user_data)
1302 {
1303     GetContentTypesData *data;
1304     char **types;
1305 
1306     data = user_data;
1307     types = g_mount_guess_content_type_finish (G_MOUNT (source_object), res, NULL);
1308 
1309     g_object_set_data_full (source_object,
1310                             "caja-content-type-cache",
1311                             g_strdupv (types),
1312                             (GDestroyNotify)g_strfreev);
1313 
1314     if (data->callback)
1315     {
1316         data->callback (types, data->user_data);
1317     }
1318     g_strfreev (types);
1319     g_free (data);
1320 }
1321 
1322 void
caja_autorun_get_x_content_types_for_mount_async(GMount * mount,CajaAutorunGetContent callback,GCancellable * cancellable,gpointer user_data)1323 caja_autorun_get_x_content_types_for_mount_async (GMount *mount,
1324         CajaAutorunGetContent callback,
1325         GCancellable *cancellable,
1326         gpointer user_data)
1327 {
1328     char **cached;
1329     GetContentTypesData *data;
1330 
1331     if (mount == NULL)
1332     {
1333         if (callback)
1334         {
1335             callback (NULL, user_data);
1336         }
1337         return;
1338     }
1339 
1340     cached = g_object_get_data (G_OBJECT (mount), "caja-content-type-cache");
1341     if (cached != NULL)
1342     {
1343         if (callback)
1344         {
1345             callback (cached, user_data);
1346         }
1347         return;
1348     }
1349 
1350     data = g_new (GetContentTypesData, 1);
1351     data->callback = callback;
1352     data->user_data = user_data;
1353 
1354     g_mount_guess_content_type (mount,
1355                                 FALSE,
1356                                 cancellable,
1357                                 get_types_cb,
1358                                 data);
1359 }
1360 
1361 
1362 char **
caja_autorun_get_cached_x_content_types_for_mount(GMount * mount)1363 caja_autorun_get_cached_x_content_types_for_mount (GMount      *mount)
1364 {
1365     char **cached;
1366 
1367     if (mount == NULL)
1368     {
1369         return NULL;
1370     }
1371 
1372     cached = g_object_get_data (G_OBJECT (mount), "caja-content-type-cache");
1373     if (cached != NULL)
1374     {
1375         return g_strdupv (cached);
1376     }
1377 
1378     return NULL;
1379 }
1380 
1381 static gboolean
remove_allow_volume(gpointer data)1382 remove_allow_volume (gpointer data)
1383 {
1384     GVolume *volume = data;
1385 
1386     g_object_set_data (G_OBJECT (volume), "caja-allow-autorun", NULL);
1387     return FALSE;
1388 }
1389 
1390 void
caja_allow_autorun_for_volume(GVolume * volume)1391 caja_allow_autorun_for_volume (GVolume *volume)
1392 {
1393     g_object_set_data (G_OBJECT (volume), "caja-allow-autorun", GINT_TO_POINTER (1));
1394 }
1395 
1396 #define INHIBIT_AUTORUN_SECONDS 10
1397 
1398 void
caja_allow_autorun_for_volume_finish(GVolume * volume)1399 caja_allow_autorun_for_volume_finish (GVolume *volume)
1400 {
1401     if (g_object_get_data (G_OBJECT (volume), "caja-allow-autorun") != NULL)
1402     {
1403         g_timeout_add_seconds_full (0,
1404                                     INHIBIT_AUTORUN_SECONDS,
1405                                     remove_allow_volume,
1406                                     g_object_ref (volume),
1407                                     g_object_unref);
1408     }
1409 }
1410 
1411 static gboolean
should_skip_native_mount_root(GFile * root)1412 should_skip_native_mount_root (GFile *root)
1413 {
1414     char *path;
1415     gboolean should_skip;
1416 
1417     /* skip any mounts in hidden directory hierarchies */
1418     path = g_file_get_path (root);
1419     should_skip = strstr (path, "/.") != NULL;
1420     g_free (path);
1421 
1422     return should_skip;
1423 }
1424 
1425 static gboolean
should_autorun_mount(GMount * mount)1426 should_autorun_mount (GMount *mount)
1427 {
1428     GFile *root;
1429     GVolume *enclosing_volume;
1430     gboolean ignore_autorun;
1431 
1432     ignore_autorun = TRUE;
1433     enclosing_volume = g_mount_get_volume (mount);
1434     if (enclosing_volume != NULL)
1435     {
1436         if (g_object_get_data (G_OBJECT (enclosing_volume), "caja-allow-autorun") != NULL)
1437         {
1438             ignore_autorun = FALSE;
1439             g_object_set_data (G_OBJECT (enclosing_volume), "caja-allow-autorun", NULL);
1440         }
1441     }
1442 
1443     if (ignore_autorun)
1444     {
1445         if (enclosing_volume != NULL)
1446         {
1447             g_object_unref (enclosing_volume);
1448         }
1449         return FALSE;
1450     }
1451 
1452     root = g_mount_get_root (mount);
1453 
1454     /* only do autorun on local files or files where g_volume_should_automount() returns TRUE */
1455     ignore_autorun = TRUE;
1456     if ((g_file_is_native (root) && !should_skip_native_mount_root (root)) ||
1457             (enclosing_volume != NULL && g_volume_should_automount (enclosing_volume)))
1458     {
1459         ignore_autorun = FALSE;
1460     }
1461     if (enclosing_volume != NULL)
1462     {
1463         g_object_unref (enclosing_volume);
1464     }
1465     g_object_unref (root);
1466 
1467     return !ignore_autorun;
1468 }
1469