1 /* totem-uri.c
2 
3    Copyright (C) 2004 Bastien Nocera
4 
5    The Gnome Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9 
10    The Gnome Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14 
15    You should have received a copy of the GNU Library General Public
16    License along with the Gnome Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18    Boston, MA 02110-1301  USA.
19 
20    Author: Bastien Nocera <hadess@hadess.net>
21  */
22 
23 #include "config.h"
24 
25 #include <glib.h>
26 #include <glib/gi18n.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 #include <gio/gio.h>
32 
33 #define WANT_MIME_TYPES 1
34 #define WANT_AUDIO_MIME_TYPES 1
35 #define WANT_VIDEO_MIME_TYPES 1
36 #include "totem-mime-types.h"
37 #include "totem-uri.h"
38 #include "totem-private.h"
39 
40 static GtkFileFilter *filter_all = NULL;
41 static GtkFileFilter *filter_subs = NULL;
42 static GtkFileFilter *filter_video = NULL;
43 
44 gboolean
totem_playing_dvd(const char * uri)45 totem_playing_dvd (const char *uri)
46 {
47 	if (uri == NULL)
48 		return FALSE;
49 
50 	return g_str_has_prefix (uri, "dvd:/");
51 }
52 
53 static void
totem_ensure_dir(const char * path)54 totem_ensure_dir (const char *path)
55 {
56 	if (g_file_test (path, G_FILE_TEST_IS_DIR) != FALSE)
57 		return;
58 
59 	g_mkdir_with_parents (path, 0700);
60 }
61 
62 const char *
totem_dot_dir(void)63 totem_dot_dir (void)
64 {
65 	static char *totem_dir = NULL;
66 
67 	if (totem_dir != NULL) {
68 		totem_ensure_dir (totem_dir);
69 		return totem_dir;
70 	}
71 
72 	totem_dir = g_build_filename (g_get_user_config_dir (),
73 				      "totem",
74 				      NULL);
75 
76 	totem_ensure_dir (totem_dir);
77 
78 	return (const char *)totem_dir;
79 }
80 
81 const char *
totem_data_dot_dir(void)82 totem_data_dot_dir (void)
83 {
84 	static char *totem_dir = NULL;
85 
86 	if (totem_dir != NULL) {
87 		totem_ensure_dir (totem_dir);
88 		return totem_dir;
89 	}
90 
91 	totem_dir = g_build_filename (g_get_user_data_dir (),
92 				      "totem",
93 				      NULL);
94 
95 	totem_ensure_dir (totem_dir);
96 
97 	return (const char *)totem_dir;
98 }
99 
100 char *
totem_pictures_dir(void)101 totem_pictures_dir (void)
102 {
103 	return g_strdup (g_get_user_special_dir (G_USER_DIRECTORY_PICTURES));
104 }
105 
106 static GMount *
totem_get_mount_for_uri(const char * path)107 totem_get_mount_for_uri (const char *path)
108 {
109 	GMount *mount;
110 	GFile *file;
111 
112 	file = g_file_new_for_path (path);
113 	mount = g_file_find_enclosing_mount (file, NULL, NULL);
114 	g_object_unref (file);
115 
116 	if (mount == NULL)
117 		return NULL;
118 
119 	/* FIXME: We used to explicitly check whether it was a CD/DVD */
120 	if (g_mount_can_eject (mount) == FALSE) {
121 		g_object_unref (mount);
122 		return NULL;
123 	}
124 
125 	return mount;
126 }
127 
128 static GMount *
totem_get_mount_for_dvd(const char * uri)129 totem_get_mount_for_dvd (const char *uri)
130 {
131 	GMount *mount;
132 	char *path;
133 
134 	mount = NULL;
135 	path = g_strdup (uri + strlen ("dvd://"));
136 
137 	/* If it's a device, we need to find the volume that corresponds to it,
138 	 * and then the mount for the volume */
139 	if (g_str_has_prefix (path, "/dev/")) {
140 		GVolumeMonitor *volume_monitor;
141 		GList *volumes, *l;
142 
143 		volume_monitor = g_volume_monitor_get ();
144 		volumes = g_volume_monitor_get_volumes (volume_monitor);
145 		g_object_unref (volume_monitor);
146 
147 		for (l = volumes; l != NULL; l = l->next) {
148 			char *id;
149 
150 			id = g_volume_get_identifier (l->data, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
151 			if (g_strcmp0 (id, path) == 0) {
152 				g_free (id);
153 				mount = g_volume_get_mount (l->data);
154 				break;
155 			}
156 			g_free (id);
157 		}
158 		g_list_free_full (volumes, (GDestroyNotify) g_object_unref);
159 	} else {
160 		mount = totem_get_mount_for_uri (path);
161 		g_free (path);
162 	}
163 	/* We have a path to the file itself */
164 	return mount;
165 }
166 
167 static char *
totem_get_mountpoint_for_vcd(const char * uri)168 totem_get_mountpoint_for_vcd (const char *uri)
169 {
170 	return NULL;
171 }
172 
173 GMount *
totem_get_mount_for_media(const char * uri)174 totem_get_mount_for_media (const char *uri)
175 {
176 	GMount *ret;
177 	char *mount_path;
178 
179 	if (uri == NULL)
180 		return NULL;
181 
182 	mount_path = NULL;
183 
184 	if (g_str_has_prefix (uri, "dvd://") != FALSE)
185 		return totem_get_mount_for_dvd (uri);
186 	else if (g_str_has_prefix (uri, "vcd:") != FALSE)
187 		mount_path = totem_get_mountpoint_for_vcd (uri);
188 	else if (g_str_has_prefix (uri, "file:") != FALSE)
189 		mount_path = g_filename_from_uri (uri, NULL, NULL);
190 
191 	if (mount_path == NULL)
192 		return NULL;
193 
194 	ret = totem_get_mount_for_uri (mount_path);
195 	g_free (mount_path);
196 
197 	return ret;
198 }
199 
200 gboolean
totem_is_special_mrl(const char * uri)201 totem_is_special_mrl (const char *uri)
202 {
203 	GMount *mount;
204 
205 	if (uri == NULL || g_str_has_prefix (uri, "file:") != FALSE)
206 		return FALSE;
207 	if (g_str_has_prefix (uri, "dvb:") != FALSE)
208 		return TRUE;
209 
210 	mount = totem_get_mount_for_media (uri);
211 	if (mount != NULL)
212 		g_object_unref (mount);
213 
214 	return (mount != NULL);
215 }
216 
217 gboolean
totem_is_block_device(const char * uri)218 totem_is_block_device (const char *uri)
219 {
220 	struct stat buf;
221 	char *local;
222 
223 	if (uri == NULL)
224 		return FALSE;
225 
226 	if (g_str_has_prefix (uri, "file:") == FALSE)
227 		return FALSE;
228 	local = g_filename_from_uri (uri, NULL, NULL);
229 	if (local == NULL)
230 		return FALSE;
231 	if (stat (local, &buf) != 0) {
232 		g_free (local);
233 		return FALSE;
234 	}
235 	g_free (local);
236 
237 	return (S_ISBLK (buf.st_mode));
238 }
239 
240 char *
totem_create_full_path(const char * path)241 totem_create_full_path (const char *path)
242 {
243 	GFile *file;
244 	char *retval;
245 
246 	g_return_val_if_fail (path != NULL, NULL);
247 
248 	if (strstr (path, "://") != NULL)
249 		return NULL;
250 	if (totem_is_special_mrl (path) != FALSE)
251 		return NULL;
252 
253 	file = g_file_new_for_commandline_arg (path);
254 	retval = g_file_get_uri (file);
255 	g_object_unref (file);
256 
257 	return retval;
258 }
259 
260 static void
totem_object_on_unmount(GVolumeMonitor * volume_monitor,GMount * mount,Totem * totem)261 totem_object_on_unmount (GVolumeMonitor *volume_monitor,
262 			 GMount *mount,
263 			 Totem *totem)
264 {
265 	totem_playlist_clear_with_g_mount (totem->playlist, mount);
266 }
267 
268 void
totem_setup_file_monitoring(Totem * totem)269 totem_setup_file_monitoring (Totem *totem)
270 {
271 	totem->monitor = g_volume_monitor_get ();
272 
273 	g_signal_connect (G_OBJECT (totem->monitor),
274 			  "mount-pre-unmount",
275 			  G_CALLBACK (totem_object_on_unmount),
276 			  totem);
277 	g_signal_connect (G_OBJECT (totem->monitor),
278 			  "mount-removed",
279 			  G_CALLBACK (totem_object_on_unmount),
280 			  totem);
281 }
282 
283 /* List from xine-lib's demux_sputext.c.
284  * Keep in sync with the list in totem_setup_file_filters() in this file.
285  * Don't add .txt extensions, as there are too many false positives. */
286 static const char subtitle_ext[][4] = {
287 	"sub",
288 	"srt",
289 	"vtt",
290 	"smi",
291 	"ssa",
292 	"ass",
293 	"mpl",
294 	"asc"
295 };
296 
297 gboolean
totem_uri_is_subtitle(const char * uri)298 totem_uri_is_subtitle (const char *uri)
299 {
300 	guint len, i;
301 
302 	len = strlen (uri);
303 	if (len < 4 || uri[len - 4] != '.')
304 		return FALSE;
305 	for (i = 0; i < G_N_ELEMENTS (subtitle_ext); i++) {
306 		if (g_str_has_suffix (uri, subtitle_ext[i]) != FALSE)
307 			return TRUE;
308 	}
309 	return FALSE;
310 }
311 
312 char *
totem_uri_escape_for_display(const char * uri)313 totem_uri_escape_for_display (const char *uri)
314 {
315 	GFile *file;
316 	char *disp;
317 
318 	file = g_file_new_for_uri (uri);
319 	disp = g_file_get_parse_name (file);
320 	g_object_unref (file);
321 
322 	return disp;
323 }
324 
325 void
totem_setup_file_filters(void)326 totem_setup_file_filters (void)
327 {
328 	guint i;
329 
330 	filter_all = gtk_file_filter_new ();
331 	gtk_file_filter_set_name (filter_all, _("All files"));
332 	gtk_file_filter_add_pattern (filter_all, "*");
333 	g_object_ref_sink (filter_all);
334 
335 	/* Video files */
336 	filter_video = gtk_file_filter_new ();
337 	gtk_file_filter_set_name (filter_video, _("Video files"));
338 	for (i = 0; video_mime_types[i] != NULL; i++) {
339 		gtk_file_filter_add_mime_type (filter_video, video_mime_types[i]);
340 	}
341 	/* Add the special Disc-as-files formats */
342 	gtk_file_filter_add_mime_type (filter_video, "application/x-cd-image");
343 	gtk_file_filter_add_mime_type (filter_video, "application/x-cue");
344 	g_object_ref_sink (filter_video);
345 
346 	/* Subtitles files. Keep in sync with subtitle_ext in this file. */
347 	filter_subs = gtk_file_filter_new ();
348 	gtk_file_filter_set_name (filter_subs, _("Subtitle files"));
349 	gtk_file_filter_add_mime_type (filter_subs, "application/x-subrip"); /* *.srt */
350 	gtk_file_filter_add_mime_type (filter_subs, "text/plain"); /* *.asc, *.txt */
351 	gtk_file_filter_add_mime_type (filter_subs, "text/x-mpl2"); /* *.mpl */
352 	gtk_file_filter_add_mime_type (filter_subs, "text/vtt"); /* *.vtt */
353 	gtk_file_filter_add_mime_type (filter_subs, "application/x-sami"); /* *.smi, *.sami */
354 	gtk_file_filter_add_mime_type (filter_subs, "text/x-microdvd"); /* *.sub */
355 	gtk_file_filter_add_mime_type (filter_subs, "text/x-mpsub"); /* *.sub */
356 	gtk_file_filter_add_mime_type (filter_subs, "text/x-ssa"); /* *.ssa, *.ass */
357 	gtk_file_filter_add_mime_type (filter_subs, "text/x-subviewer"); /* *.sub */
358 	g_object_ref_sink (filter_subs);
359 }
360 
361 void
totem_destroy_file_filters(void)362 totem_destroy_file_filters (void)
363 {
364 	if (filter_all != NULL) {
365 		g_object_unref (filter_all);
366 		filter_all = NULL;
367 		g_object_unref (filter_video);
368 		g_object_unref (filter_subs);
369 	}
370 }
371 
372 static const GUserDirectory dir_types[] = {
373 	G_USER_DIRECTORY_VIDEOS,
374 	G_USER_DIRECTORY_MUSIC
375 };
376 
377 static void
totem_add_default_dirs(GtkFileChooser * dialog)378 totem_add_default_dirs (GtkFileChooser *dialog)
379 {
380 	guint i;
381 	for (i = 0; i < G_N_ELEMENTS (dir_types); i++) {
382 		const char *dir;
383 
384 		dir = g_get_user_special_dir (dir_types[i]);
385 		if (dir == NULL)
386 			continue;
387 		gtk_file_chooser_add_shortcut_folder (dialog, dir, NULL);
388 	}
389 }
390 
391 char *
totem_add_subtitle(GtkWindow * parent,const char * uri)392 totem_add_subtitle (GtkWindow *parent, const char *uri)
393 {
394 	GtkWidget *fs;
395 	GSettings *settings;
396 	char *new_path;
397 	char *subtitle = NULL;
398 	gboolean folder_set;
399 
400 	fs = gtk_file_chooser_dialog_new (_("Select Text Subtitles"),
401 					  parent,
402 					  GTK_FILE_CHOOSER_ACTION_OPEN,
403 					  _("_Cancel"), GTK_RESPONSE_CANCEL,
404 					  _("_Open"), GTK_RESPONSE_ACCEPT,
405 					  NULL);
406 	gtk_dialog_set_default_response (GTK_DIALOG (fs), GTK_RESPONSE_ACCEPT);
407 	gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (fs), FALSE);
408 	gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (fs), filter_subs);
409 
410 	settings = g_settings_new (TOTEM_GSETTINGS_SCHEMA);
411 	folder_set = FALSE;
412 
413 	/* Add the subtitles cache dir as a shortcut */
414 	new_path = g_build_filename (g_get_user_cache_dir (),
415 				     "totem",
416 				     "subtitles",
417 				     NULL);
418 	gtk_file_chooser_add_shortcut_folder_uri (GTK_FILE_CHOOSER (fs), new_path, NULL);
419 	g_free (new_path);
420 
421 	/* Add the last open path as a shortcut */
422 	new_path = g_settings_get_string (settings, "open-uri");
423 	if (*new_path != '\0')
424 		gtk_file_chooser_add_shortcut_folder_uri (GTK_FILE_CHOOSER (fs), new_path, NULL);
425 	g_free (new_path);
426 
427 	/* Try to set the passed path as the current folder */
428 	if (uri != NULL) {
429 		folder_set = gtk_file_chooser_set_current_folder_uri
430 			(GTK_FILE_CHOOSER (fs), uri);
431 		gtk_file_chooser_add_shortcut_folder_uri (GTK_FILE_CHOOSER (fs), uri, NULL);
432 	}
433 
434 	/* And set it as home if it fails */
435 	if (folder_set == FALSE) {
436 		gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (fs),
437 						     g_get_home_dir ());
438 	}
439 	totem_add_default_dirs (GTK_FILE_CHOOSER (fs));
440 
441 	if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT) {
442 		subtitle = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (fs));
443 	}
444 
445 	gtk_widget_destroy (fs);
446 	g_object_unref (settings);
447 
448 	return subtitle;
449 }
450 
451 GSList *
totem_add_files(GtkWindow * parent,const char * path)452 totem_add_files (GtkWindow *parent, const char *path)
453 {
454 	GtkWidget *fs;
455 	int response;
456 	GSList *filenames;
457 	char *mrl, *new_path;
458 	GSettings *settings;
459 	gboolean set_folder;
460 
461 	fs = gtk_file_chooser_dialog_new (_("Add Videos"),
462 					  parent,
463 					  GTK_FILE_CHOOSER_ACTION_OPEN,
464 					  _("_Cancel"), GTK_RESPONSE_CANCEL,
465 					  _("_Add"), GTK_RESPONSE_ACCEPT,
466 					  NULL);
467 	gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (fs), filter_video);
468 	gtk_dialog_set_default_response (GTK_DIALOG (fs), GTK_RESPONSE_ACCEPT);
469 	gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (fs), TRUE);
470 	gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (fs), FALSE);
471 
472 	settings = g_settings_new (TOTEM_GSETTINGS_SCHEMA);
473 	set_folder = TRUE;
474 	if (path != NULL) {
475 		set_folder = gtk_file_chooser_set_current_folder_uri
476 			(GTK_FILE_CHOOSER (fs), path);
477 	} else {
478 		new_path = g_settings_get_string (settings, "open-uri");
479 		if (*new_path != '\0') {
480 			set_folder = gtk_file_chooser_set_current_folder_uri
481 				(GTK_FILE_CHOOSER (fs), new_path);
482 		}
483 		g_free (new_path);
484 	}
485 
486 	/* We didn't manage to change the directory */
487 	if (set_folder == FALSE) {
488 		gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (fs),
489 						     g_get_home_dir ());
490 	}
491 	totem_add_default_dirs (GTK_FILE_CHOOSER (fs));
492 
493 	response = gtk_dialog_run (GTK_DIALOG (fs));
494 
495 	filenames = NULL;
496 	if (response == GTK_RESPONSE_ACCEPT)
497 		filenames = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (fs));
498 
499 	if (filenames == NULL) {
500 		gtk_widget_destroy (fs);
501 		g_object_unref (settings);
502 		return NULL;
503 	}
504 	gtk_widget_destroy (fs);
505 
506 	mrl = filenames->data;
507 	if (mrl != NULL) {
508 		new_path = g_path_get_dirname (mrl);
509 		g_settings_set_string (settings, "open-uri", new_path);
510 		g_free (new_path);
511 	}
512 
513 	g_object_unref (settings);
514 
515 	return filenames;
516 }
517