1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 
3 /* Caja
4 
5    Copyright (C) 2008 Red Hat, Inc.
6 
7    The Mate Library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Library General Public License as
9    published by the Free Software Foundation; either version 2 of the
10    License, or (at your option) any later version.
11 
12    The Mate Library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Library General Public License for more details.
16 
17    You should have received a copy of the GNU Library General Public
18    License along with the Mate Library; see the file COPYING.LIB.  If not,
19    write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20    Boston, MA 02110-1301, USA.
21 
22    Author: David Zeuthen <davidz@redhat.com>
23 */
24 
25 
26 #include <config.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <time.h>
30 #include <errno.h>
31 
32 #include <gtk/gtk.h>
33 #include <gio/gio.h>
34 #include <glib/gi18n.h>
35 
36 #include <libcaja-private/caja-module.h>
37 #include <libcaja-private/caja-icon-info.h>
38 
39 typedef struct
40 {
41     GtkWidget *dialog;
42     GMount *mount;
43 } AutorunSoftwareDialogData;
44 
45 static void autorun_software_dialog_mount_unmounted (GMount *mount, AutorunSoftwareDialogData *data);
46 
47 static void
autorun_software_dialog_destroy(AutorunSoftwareDialogData * data)48 autorun_software_dialog_destroy (AutorunSoftwareDialogData *data)
49 {
50     g_signal_handlers_disconnect_by_func (G_OBJECT (data->mount),
51                                           G_CALLBACK (autorun_software_dialog_mount_unmounted),
52                                           data);
53 
54     gtk_widget_destroy (GTK_WIDGET (data->dialog));
55     g_object_unref (data->mount);
56     g_free (data);
57 }
58 
59 static void
autorun_software_dialog_mount_unmounted(GMount * mount,AutorunSoftwareDialogData * data)60 autorun_software_dialog_mount_unmounted (GMount *mount, AutorunSoftwareDialogData *data)
61 {
62     autorun_software_dialog_destroy (data);
63 }
64 
65 static gboolean
_check_file(GFile * mount_root,const char * file_path,gboolean must_be_executable)66 _check_file (GFile *mount_root, const char *file_path, gboolean must_be_executable)
67 {
68     GFile *file;
69     GFileInfo *file_info;
70     gboolean ret;
71 
72     ret = FALSE;
73 
74     file = g_file_get_child (mount_root, file_path);
75     file_info = g_file_query_info (file,
76                                    G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE,
77                                    G_FILE_QUERY_INFO_NONE,
78                                    NULL,
79                                    NULL);
80     if (file_info != NULL)
81     {
82         if (must_be_executable)
83         {
84             if (g_file_info_get_attribute_boolean (file_info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE))
85             {
86                 ret = TRUE;
87             }
88         }
89         else
90         {
91             ret = TRUE;
92         }
93         g_object_unref (file_info);
94     }
95     g_object_unref (file);
96 
97     return ret;
98 }
99 
100 static void
autorun(GMount * mount)101 autorun (GMount *mount)
102 {
103     char *error_string;
104     GFile *root;
105     GFile *program_to_spawn;
106     char *path_to_spawn;
107     char *cwd_for_program;
108 
109     root = g_mount_get_root (mount);
110 
111     /* Careful here, according to
112      *
113      *  https://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
114      *
115      * the ordering does matter.
116      */
117 
118     program_to_spawn = NULL;
119     path_to_spawn = NULL;
120 
121     if (_check_file (root, ".autorun", TRUE))
122     {
123         program_to_spawn = g_file_get_child (root, ".autorun");
124     }
125     else if (_check_file (root, "autorun", TRUE))
126     {
127         program_to_spawn = g_file_get_child (root, "autorun");
128     }
129     else if (_check_file (root, "autorun.sh", TRUE))
130     {
131         program_to_spawn = g_file_get_child (root, "autorun.sh");
132     }
133     else if (_check_file (root, "autorun.exe", TRUE))
134     {
135         /* TODO */
136     }
137     else if (_check_file (root, "AUTORUN.EXE", TRUE))
138     {
139         /* TODO */
140     }
141     else if (_check_file (root, "autorun.inf", FALSE))
142     {
143         /* TODO */
144     }
145     else if (_check_file (root, "AUTORUN.INF", FALSE))
146     {
147         /* TODO */
148     }
149 
150     if (program_to_spawn != NULL)
151     {
152         path_to_spawn = g_file_get_path (program_to_spawn);
153     }
154 
155     cwd_for_program = g_file_get_path (root);
156 
157     error_string = NULL;
158     if (path_to_spawn != NULL && cwd_for_program != NULL)
159     {
160         if (chdir (cwd_for_program) == 0)
161         {
162             execl (path_to_spawn, path_to_spawn, NULL);
163             error_string = g_strdup_printf (_("Error starting autorun program: %s"), strerror (errno));
164             goto out;
165         }
166         error_string = g_strdup_printf (_("Error starting autorun program: %s"), strerror (errno));
167         goto out;
168     }
169     error_string = g_strdup_printf (_("Cannot find the autorun program"));
170 
171 out:
172     if (program_to_spawn != NULL)
173     {
174         g_object_unref (program_to_spawn);
175     }
176     if (root != NULL)
177     {
178         g_object_unref (root);
179     }
180     g_free (path_to_spawn);
181     g_free (cwd_for_program);
182 
183     if (error_string != NULL)
184     {
185         GtkWidget *dialog;
186         dialog = gtk_message_dialog_new_with_markup (NULL, /* TODO: parent window? */
187                  0,
188                  GTK_MESSAGE_ERROR,
189                  GTK_BUTTONS_OK,
190                  _("<big><b>Error autorunning software</b></big>"));
191         gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", error_string);
192         gtk_dialog_run (GTK_DIALOG (dialog));
193         gtk_widget_destroy (dialog);
194         g_free (error_string);
195     }
196 }
197 
198 static void
present_autorun_for_software_dialog(GMount * mount)199 present_autorun_for_software_dialog (GMount *mount)
200 {
201     GIcon *icon;
202     int icon_size;
203     CajaIconInfo *icon_info;
204     GdkPixbuf *pixbuf;
205     GtkWidget *image;
206     char *mount_name;
207     GtkWidget *dialog;
208     AutorunSoftwareDialogData *data;
209 
210     mount_name = g_mount_get_name (mount);
211 
212     dialog = gtk_message_dialog_new_with_markup (NULL, /* TODO: parent window? */
213              0,
214              GTK_MESSAGE_OTHER,
215              GTK_BUTTONS_CANCEL,
216              _("<big><b>This medium contains software intended to be automatically started. Would you like to run it?</b></big>"));
217     gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
218             _("The software will run directly from the medium \"%s\". "
219               "You should never run software that you don't trust.\n"
220               "\n"
221               "If in doubt, press Cancel."),
222             mount_name);
223 
224     /* TODO: in a star trek future add support for verifying
225      * software on media (e.g. if it has a certificate, check it
226      * etc.)
227      */
228 
229 
230     icon = g_mount_get_icon (mount);
231     icon_size = caja_get_icon_size_for_stock_size (GTK_ICON_SIZE_DIALOG);
232     icon_info = caja_icon_info_lookup (icon, icon_size,
233                                        gtk_widget_get_scale_factor (GTK_WIDGET (dialog)));
234     pixbuf = caja_icon_info_get_pixbuf_at_size (icon_info, icon_size);
235     image = gtk_image_new_from_pixbuf (pixbuf);
236     gtk_widget_set_halign (image, GTK_ALIGN_CENTER);
237     gtk_widget_set_valign (image, GTK_ALIGN_START);
238     gtk_message_dialog_set_image (GTK_MESSAGE_DIALOG (dialog), image);
239 
240     gtk_window_set_title (GTK_WINDOW (dialog), mount_name);
241     gtk_window_set_icon (GTK_WINDOW (dialog), pixbuf);
242 
243     data = g_new0 (AutorunSoftwareDialogData, 1);
244     data->dialog = dialog;
245     data->mount = g_object_ref (mount);
246 
247     g_signal_connect (G_OBJECT (mount),
248                       "unmounted",
249                       G_CALLBACK (autorun_software_dialog_mount_unmounted),
250                       data);
251 
252     gtk_dialog_add_button (GTK_DIALOG (dialog),
253                            _("_Run"),
254                            GTK_RESPONSE_OK);
255 
256     gtk_widget_show_all (dialog);
257 
258     if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
259     {
260         gtk_widget_destroy (dialog);
261         autorun (mount);
262     }
263 
264     g_object_unref (icon_info);
265     g_object_unref (pixbuf);
266     g_free (mount_name);
267 }
268 
269 int
main(int argc,char * argv[])270 main (int argc, char *argv[])
271 {
272     GVolumeMonitor *monitor;
273     GFile *file;
274     GMount *mount;
275 
276 #ifdef ENABLE_NLS
277     bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
278     bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
279     textdomain (GETTEXT_PACKAGE);
280 #endif /* ENABLE_NLS */
281 
282     gtk_init (&argc, &argv);
283 
284     if (argc != 2)
285     {
286         goto out;
287     }
288 
289     /* instantiate monitor so we get the "unmounted" signal properly */
290     monitor = g_volume_monitor_get ();
291     if (monitor == NULL)
292     {
293         goto out;
294     }
295 
296     file = g_file_new_for_commandline_arg (argv[1]);
297     if (file == NULL)
298     {
299         g_object_unref (monitor);
300         goto out;
301     }
302 
303     mount = g_file_find_enclosing_mount (file, NULL, NULL);
304     if (mount == NULL)
305     {
306         g_object_unref (file);
307         g_object_unref (monitor);
308         goto out;
309     }
310 
311     present_autorun_for_software_dialog (mount);
312     g_object_unref (file);
313     g_object_unref (monitor);
314     g_object_unref (mount);
315 
316 out:
317     return 0;
318 }
319