1 /* Totem Disc Content Detection
2 * Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
3 * Copyright (C) 2004-2007 Bastien Nocera <hadess@hadess.net>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This 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 this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301 USA.
19 *
20 * The Totem project hereby grant permission for non-gpl compatible GStreamer
21 * plugins to be used and distributed together with GStreamer and Totem. This
22 * permission are above and beyond the permissions granted by the GPL license
23 * Totem is covered by.
24 *
25 * Monday 7th February 2005: Christian Schaller: Add exception clause.
26 * See license_change file for details.
27 *
28 */
29
30 /**
31 * SECTION:totem-disc
32 * @short_description: disc utility functions
33 * @stability: Stable
34 * @include: totem-disc.h
35 *
36 * This file has various different disc utility functions for getting
37 * the media types and labels of discs.
38 *
39 * The functions in this file refer to MRLs, which are a special form
40 * of URIs used by xine to refer to things such as DVDs. An example of
41 * an MRL would be <literal>dvd:///dev/scd0</literal>, which is not a
42 * valid URI as far as, for example, GIO is concerned.
43 *
44 * The rest of the totem-pl-parser API exclusively uses URIs.
45 **/
46
47 #include "config.h"
48
49 #include <fcntl.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <errno.h>
53 #include <string.h>
54
55 #include <sys/stat.h>
56
57 #include <glib.h>
58 #include <glib/gi18n.h>
59 #include <gio/gio.h>
60
61 #ifdef HAVE_LIBARCHIVE
62 #include <archive.h>
63 #include <archive_entry.h>
64 #endif /* HAVE_ARCHIVE */
65
66 #include "totem-disc.h"
67 #include "totem-pl-parser.h"
68
69 typedef struct _CdCache {
70 /* device node and mountpoint */
71 char *device, *mountpoint;
72 GVolume *volume;
73
74 char **content_types;
75
76 GFile *iso_file;
77
78 /* Whether we have a medium */
79 guint has_medium : 1;
80 /* if we're checking a media, or a dir */
81 guint is_media : 1;
82
83 /* indicates if we mounted this mountpoint ourselves or if it
84 * was already mounted. */
85 guint self_mounted : 1;
86 guint mounted : 1;
87
88 /* Whether it's a local ISO file */
89 guint is_iso : 1;
90 } CdCache;
91
92 typedef struct _CdCacheCallbackData {
93 CdCache *cache;
94 gboolean called;
95 gboolean result;
96 GError *error;
97 } CdCacheCallbackData;
98
99 static void cd_cache_free (CdCache *cache);
100
101 static char *
totem_resolve_symlink(const char * device,GError ** error)102 totem_resolve_symlink (const char *device, GError **error)
103 {
104 char *dir, *_link;
105 char *f;
106 char *f1;
107
108 f = g_strdup (device);
109 while (g_file_test (f, G_FILE_TEST_IS_SYMLINK)) {
110 _link = g_file_read_link (f, error);
111 if (_link == NULL) {
112 g_free (f);
113 return NULL;
114 }
115
116 dir = g_path_get_dirname (f);
117 f1 = g_build_filename (dir, _link, NULL);
118 g_free (dir);
119 g_free (f);
120 f = f1;
121 }
122
123 if (f != NULL) {
124 GFile *file;
125
126 file = g_file_new_for_path (f);
127 f1 = g_file_get_path (file);
128 g_object_unref (file);
129 g_free (f);
130 f = f1;
131 }
132 return f;
133 }
134
135 static gboolean
cd_cache_get_best_mount_for_drive(GDrive * drive,char ** mountpoint,GVolume ** volume)136 cd_cache_get_best_mount_for_drive (GDrive *drive,
137 char **mountpoint,
138 GVolume **volume)
139 {
140 GList *list, *l;
141 int rank;
142
143 rank = 0;
144 list = g_drive_get_volumes (drive);
145 for (l = list; l != NULL; l = l->next) {
146 GVolume *v;
147 GMount *m;
148 GFile *file;
149 int new_rank;
150
151 v = l->data;
152
153 m = g_volume_get_mount (v);
154 if (m == NULL)
155 continue;
156
157 file = g_mount_get_root (m);
158 if (g_file_has_uri_scheme (file, "cdda"))
159 new_rank = 100;
160 else
161 new_rank = 50;
162
163 if (new_rank > rank) {
164 if (*mountpoint)
165 g_free (*mountpoint);
166 if (*volume)
167 g_object_unref (volume);
168
169 *volume = g_object_ref (v);
170 *mountpoint = g_file_get_path (file);
171 rank = new_rank;
172 }
173
174 g_object_unref (file);
175 g_object_unref (m);
176 }
177
178 g_list_foreach (list, (GFunc) g_object_unref, NULL);
179 g_list_free (list);
180
181 return rank > 0;
182 }
183
184 static gboolean
cd_cache_get_dev_from_volumes(GVolumeMonitor * mon,const char * device,char ** mountpoint,GVolume ** volume)185 cd_cache_get_dev_from_volumes (GVolumeMonitor *mon, const char *device,
186 char **mountpoint, GVolume **volume)
187 {
188 GList *list, *l;
189 gboolean found;
190
191 found = FALSE;
192 list = g_volume_monitor_get_connected_drives (mon);
193 for (l = list; l != NULL; l = l->next) {
194 GDrive *drive;
195 char *ddev, *resolved;
196
197 drive = l->data;
198 ddev = g_drive_get_identifier (drive, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
199 if (ddev == NULL)
200 continue;
201 resolved = totem_resolve_symlink (ddev, NULL);
202 g_free (ddev);
203 if (resolved == NULL)
204 continue;
205
206 if (strcmp (resolved, device) == 0) {
207 found = cd_cache_get_best_mount_for_drive (drive, mountpoint, volume);
208 }
209
210 g_free (resolved);
211 if (found != FALSE)
212 break;
213 }
214
215 g_list_foreach (list, (GFunc) g_object_unref, NULL);
216 g_list_free (list);
217
218 if (found != FALSE)
219 return found;
220
221 /* Not in the drives? Look in the volumes themselves */
222 found = FALSE;
223 list = g_volume_monitor_get_volumes (mon);
224 for (l = list; l != NULL; l = l->next) {
225 GVolume *vol;
226 char *ddev, *resolved;
227
228 vol = l->data;
229 ddev = g_volume_get_identifier (vol, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
230 if (ddev == NULL)
231 continue;
232 resolved = totem_resolve_symlink (ddev, NULL);
233 g_free (ddev);
234 if (resolved == NULL)
235 continue;
236
237 if (strcmp (resolved, device) == 0) {
238 found = TRUE;
239 *volume = g_object_ref (vol);
240 }
241
242 g_free (resolved);
243 if (found != FALSE)
244 break;
245 }
246
247 g_list_foreach (list, (GFunc) g_object_unref, NULL);
248 g_list_free (list);
249
250 return found;
251 }
252
253 static gboolean
cd_cache_has_content_type(CdCache * cache,const char * content_type)254 cd_cache_has_content_type (CdCache *cache, const char *content_type)
255 {
256 guint i;
257
258 if (cache->content_types == NULL) {
259 return FALSE;
260 }
261
262 for (i = 0; cache->content_types[i] != NULL; i++) {
263 if (g_str_equal (cache->content_types[i], content_type) != FALSE)
264 return TRUE;
265 }
266 return FALSE;
267 }
268
269 static gboolean
cd_cache_check_archive(CdCache * cache,const char * filename,GError ** error)270 cd_cache_check_archive (CdCache *cache,
271 const char *filename,
272 GError **error)
273 {
274 #ifndef HAVE_LIBARCHIVE
275 g_set_error (error, TOTEM_PL_PARSER_ERROR, TOTEM_PL_PARSER_ERROR_MOUNT_FAILED,
276 _("Failed to mount %s."), filename);
277 return FALSE;
278 #else
279 struct archive *a;
280 struct archive_entry *entry;
281 const char * content_types[] = { NULL, NULL };
282 int r;
283
284 a = archive_read_new();
285 archive_read_support_format_iso9660(a);
286 r = archive_read_open_filename(a, filename, 10240);
287 if (r != ARCHIVE_OK) {
288 g_set_error (error, TOTEM_PL_PARSER_ERROR, TOTEM_PL_PARSER_ERROR_MOUNT_FAILED,
289 _("Failed to mount %s."), filename);
290 return FALSE;
291 }
292 while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
293 const char *name;
294
295 name = archive_entry_pathname (entry);
296 if (g_ascii_strcasecmp (name, "VIDEO_TS/VIDEO_TS.IFO") == 0) {
297 content_types[0] = "x-content/video-dvd";
298 cache->content_types = g_strdupv ((gchar**) content_types);
299 break;
300 } else if (g_ascii_strcasecmp (name, "mpegav/AVSEQ01.DAT") == 0) {
301 content_types[0] = "x-content/video-vcd";
302 cache->content_types = g_strdupv ((gchar**) content_types);
303 break;
304 } else if (g_ascii_strcasecmp (name, "MPEG2/AVSEQ01.MPG") == 0) {
305 content_types[0] = "x-content/video-svcd";
306 cache->content_types = g_strdupv ((gchar**) content_types);
307 break;
308 } else if (g_ascii_strcasecmp (name, "BDAV") == 0 ||
309 g_ascii_strcasecmp (name, "BDMV") == 0) {
310 content_types[0] = "x-content/video-bluray";
311 cache->content_types = g_strdupv ((gchar**) content_types);
312 break;
313 }
314 archive_read_data_skip(a);
315 }
316 archive_read_free(a);
317 return TRUE;
318 #endif
319 }
320
321 static char *
unescape_archive_name(const char * orig_uri)322 unescape_archive_name (const char *orig_uri)
323 {
324 char *uri;
325 guint len;
326 char *escape1, *escape2;
327
328 uri = g_strdup (orig_uri);
329
330 /* Remove trailing slash */
331 len = strlen (uri);
332 if (uri[len - 1] == '/')
333 uri[len - 1] = '\0';
334
335 /* Unescape the path */
336 escape1 = g_uri_unescape_string (uri + strlen ("archive://"), NULL);
337 escape2 = g_uri_unescape_string (escape1, NULL);
338 g_free (escape1);
339 g_free (uri);
340
341 return escape2;
342 }
343
344 static CdCache *
cd_cache_new(const char * dev,GError ** error)345 cd_cache_new (const char *dev,
346 GError **error)
347 {
348 CdCache *cache;
349 char *mountpoint = NULL, *device, *local;
350 GVolumeMonitor *mon;
351 GVolume *volume = NULL;
352 GFile *file;
353 gboolean found, self_mounted;
354
355 if (dev[0] == '/') {
356 local = g_strdup (dev);
357 file = g_file_new_for_path (dev);
358 } else {
359 if (g_str_has_prefix (dev, "archive://")) {
360 char *orig_uri;
361 orig_uri = unescape_archive_name (dev);
362 file = g_file_new_for_uri (orig_uri);
363 g_free (orig_uri);
364 local = g_file_get_path (file);
365 } else {
366 file = g_file_new_for_commandline_arg (dev);
367 local = g_file_get_path (file);
368 }
369 }
370
371 if (local == NULL) {
372 /* No error, just no cache */
373 g_object_unref (file);
374 return NULL;
375 }
376
377 self_mounted = FALSE;
378
379 if (g_file_test (local, G_FILE_TEST_IS_DIR) != FALSE) {
380 cache = g_new0 (CdCache, 1);
381 cache->mountpoint = local;
382 cache->is_media = FALSE;
383 cache->content_types = g_content_type_guess_for_tree (file);
384 g_object_unref (file);
385
386 return cache;
387 } else if (g_file_test (local, G_FILE_TEST_IS_REGULAR)) {
388 cache = g_new0 (CdCache, 1);
389 cache->is_iso = TRUE;
390 cache->is_media = FALSE;
391
392 g_object_unref (file);
393
394 if (cd_cache_check_archive (cache, local, error) == FALSE) {
395 cd_cache_free (cache);
396 return FALSE;
397 }
398
399 cache->device = local;
400 cache->self_mounted = FALSE;
401 cache->mounted = FALSE;
402
403 return cache;
404 }
405
406 g_object_unref (file);
407
408 /* We have a local device
409 * retrieve mountpoint and volume from gio volumes */
410 device = totem_resolve_symlink (local, error);
411 g_free (local);
412 if (!device)
413 return NULL;
414 mon = g_volume_monitor_get ();
415 found = cd_cache_get_dev_from_volumes (mon, device, &mountpoint, &volume);
416 if (!found) {
417 g_set_error (error, TOTEM_PL_PARSER_ERROR, TOTEM_PL_PARSER_ERROR_NO_DISC,
418 _("No media in drive for device “%s”."),
419 device);
420 g_free (device);
421 return NULL;
422 }
423
424 cache = g_new0 (CdCache, 1);
425 cache->device = device;
426 cache->mountpoint = mountpoint;
427 cache->self_mounted = self_mounted;
428 cache->volume = volume;
429 cache->is_media = TRUE;
430
431 {
432 GMount *mount;
433
434 mount = g_volume_get_mount (volume);
435 if (mount) {
436 cache->content_types = g_mount_guess_content_type_sync (mount, FALSE, NULL, NULL);
437 g_object_unref (mount);
438 }
439 }
440
441 return cache;
442 }
443
444 static gboolean
cd_cache_has_medium(CdCache * cache)445 cd_cache_has_medium (CdCache *cache)
446 {
447 GDrive *drive;
448 gboolean retval;
449
450 if (cache->volume == NULL)
451 return FALSE;
452
453 drive = g_volume_get_drive (cache->volume);
454 if (drive == NULL) {
455 /* No drive, so not something that can have a medium */
456 return TRUE;
457 }
458 retval = g_drive_has_media (drive);
459 g_object_unref (drive);
460
461 return retval;
462 }
463
464 static gboolean
cd_cache_open_device(CdCache * cache,GError ** error)465 cd_cache_open_device (CdCache *cache,
466 GError **error)
467 {
468 /* not a medium? */
469 if (cache->is_media == FALSE || cache->has_medium != FALSE) {
470 return TRUE;
471 }
472
473 if (cd_cache_has_medium (cache) == FALSE) {
474 g_set_error (error, TOTEM_PL_PARSER_ERROR, TOTEM_PL_PARSER_ERROR_NO_DISC,
475 _("Please check that a disc is present in the drive."));
476 return FALSE;
477 }
478 cache->has_medium = TRUE;
479
480 return TRUE;
481 }
482
483 static void
cd_cache_mount_callback(GObject * source_object,GAsyncResult * res,CdCacheCallbackData * data)484 cd_cache_mount_callback (GObject *source_object,
485 GAsyncResult *res,
486 CdCacheCallbackData *data)
487 {
488 data->result = g_volume_mount_finish (data->cache->volume, res, &data->error);
489 data->called = TRUE;
490 }
491
492 static gboolean
cd_cache_open_mountpoint(CdCache * cache,GError ** error)493 cd_cache_open_mountpoint (CdCache *cache,
494 GError **error)
495 {
496 GMount *mount;
497 GFile *root;
498
499 /* already opened? */
500 if (cache->mounted || cache->is_media == FALSE || cache->is_iso)
501 return TRUE;
502
503 /* check for mounting - assume we'll mount ourselves */
504 if (cache->volume == NULL)
505 return TRUE;
506
507 mount = g_volume_get_mount (cache->volume);
508 cache->self_mounted = (mount == NULL);
509
510 /* mount if we have to */
511 if (cache->self_mounted) {
512 CdCacheCallbackData data;
513
514 memset (&data, 0, sizeof(data));
515 data.cache = cache;
516
517 /* mount - wait for callback */
518 g_volume_mount (cache->volume,
519 G_MOUNT_MOUNT_NONE,
520 NULL,
521 NULL,
522 (GAsyncReadyCallback) cd_cache_mount_callback,
523 &data);
524 /* FIXME wait until it's done, any better way? */
525 while (!data.called) g_main_context_iteration (NULL, TRUE);
526
527 if (!data.result) {
528 if (data.error) {
529 g_propagate_error (error, data.error);
530 } else {
531 g_set_error (error, TOTEM_PL_PARSER_ERROR, TOTEM_PL_PARSER_ERROR_MOUNT_FAILED,
532 _("Failed to mount %s."), cache->device);
533 }
534 return FALSE;
535 } else {
536 cache->mounted = TRUE;
537 mount = g_volume_get_mount (cache->volume);
538 }
539 }
540
541 if (!cache->mountpoint) {
542 root = g_mount_get_root (mount);
543 cache->mountpoint = g_file_get_path (root);
544 g_object_unref (root);
545 }
546
547 return TRUE;
548 }
549
550 static void
cd_cache_unmount_callback(GObject * source_object,GAsyncResult * res,CdCacheCallbackData * data)551 cd_cache_unmount_callback (GObject *source_object,
552 GAsyncResult *res,
553 CdCacheCallbackData *data)
554 {
555 data->result = g_mount_unmount_with_operation_finish (G_MOUNT (source_object),
556 res, NULL);
557 data->called = TRUE;
558 }
559
560 static void
cd_cache_free(CdCache * cache)561 cd_cache_free (CdCache *cache)
562 {
563 GMount *mount;
564
565 g_strfreev (cache->content_types);
566
567 if (cache->iso_file && cache->self_mounted) {
568 mount = g_file_find_enclosing_mount (cache->iso_file,
569 NULL, NULL);
570 if (mount) {
571 CdCacheCallbackData data;
572
573 memset (&data, 0, sizeof(data));
574
575 g_mount_unmount_with_operation (mount,
576 G_MOUNT_UNMOUNT_NONE,
577 NULL,
578 NULL,
579 (GAsyncReadyCallback) cd_cache_unmount_callback,
580 &data);
581
582 while (!data.called) g_main_context_iteration (NULL, TRUE);
583 g_object_unref (mount);
584 }
585 g_object_unref (cache->iso_file);
586 }
587
588 /* free mem */
589 if (cache->volume)
590 g_object_unref (cache->volume);
591 g_free (cache->mountpoint);
592 g_free (cache->device);
593 g_free (cache);
594 }
595
596 static TotemDiscMediaType
cd_cache_disc_is_cdda(CdCache * cache,GError ** error)597 cd_cache_disc_is_cdda (CdCache *cache,
598 GError **error)
599 {
600 /* We can't have audio CDs on disc, yet */
601 if (cache->is_media == FALSE)
602 return MEDIA_TYPE_DATA;
603 if (!cd_cache_open_device (cache, error))
604 return MEDIA_TYPE_ERROR;
605 if (cd_cache_has_content_type (cache, "x-content/audio-cdda") != FALSE)
606 return MEDIA_TYPE_CDDA;
607
608 return MEDIA_TYPE_DATA;
609 }
610
611 static TotemDiscMediaType
cd_cache_disc_is_vcd(CdCache * cache,GError ** error)612 cd_cache_disc_is_vcd (CdCache *cache,
613 GError **error)
614 {
615 /* open disc and open mount */
616 if (!cd_cache_open_device (cache, error))
617 return MEDIA_TYPE_ERROR;
618 if (!cd_cache_open_mountpoint (cache, error))
619 return MEDIA_TYPE_ERROR;
620
621 if (cd_cache_has_content_type (cache, "x-content/video-vcd") != FALSE)
622 return MEDIA_TYPE_VCD;
623 if (cd_cache_has_content_type (cache, "x-content/video-svcd") != FALSE)
624 return MEDIA_TYPE_VCD;
625
626 return MEDIA_TYPE_DATA;
627 }
628
629 static TotemDiscMediaType
cd_cache_disc_is_dvd(CdCache * cache,GError ** error)630 cd_cache_disc_is_dvd (CdCache *cache,
631 GError **error)
632 {
633 /* open disc, check capabilities and open mount */
634 if (!cd_cache_open_device (cache, error))
635 return MEDIA_TYPE_ERROR;
636 if (!cd_cache_open_mountpoint (cache, error))
637 return MEDIA_TYPE_ERROR;
638
639 if (cd_cache_has_content_type (cache, "x-content/video-dvd") != FALSE)
640 return MEDIA_TYPE_DVD;
641
642 return MEDIA_TYPE_DATA;
643 }
644
645 static TotemDiscMediaType
cd_cache_disc_is_bd(CdCache * cache,GError ** error)646 cd_cache_disc_is_bd (CdCache *cache,
647 GError **error)
648 {
649 /* open disc, check capabilities and open mount */
650 if (!cd_cache_open_device (cache, error))
651 return MEDIA_TYPE_ERROR;
652 if (!cd_cache_open_mountpoint (cache, error))
653 return MEDIA_TYPE_ERROR;
654
655 if (cd_cache_has_content_type (cache, "x-content/video-bluray") != FALSE)
656 return MEDIA_TYPE_BD;
657
658 return MEDIA_TYPE_DATA;
659 }
660
661 /**
662 * totem_cd_mrl_from_type:
663 * @scheme: a scheme (e.g. "dvd")
664 * @dir: a directory URI
665 *
666 * Builds an MRL using the scheme @scheme and the given URI @dir,
667 * taking the filename from the URI if it's a <filename>file://</filename> and just
668 * using the whole URI otherwise.
669 *
670 * Return value: a newly-allocated string containing the MRL
671 **/
672 char *
totem_cd_mrl_from_type(const char * scheme,const char * dir)673 totem_cd_mrl_from_type (const char *scheme, const char *dir)
674 {
675 char *retval;
676
677 if (g_str_has_prefix (dir, "file://") != FALSE) {
678 char *local;
679 local = g_filename_from_uri (dir, NULL, NULL);
680 retval = g_strdup_printf ("%s://%s", scheme, local);
681 g_free (local);
682 } else {
683 retval = g_strdup_printf ("%s://%s", scheme, dir);
684 }
685 return retval;
686 }
687
688 static char *
totem_cd_dir_get_parent(const char * dir)689 totem_cd_dir_get_parent (const char *dir)
690 {
691 GFile *file, *parent_file;
692 char *parent;
693
694 file = g_file_new_for_path (dir);
695 parent_file = g_file_get_parent (file);
696 g_object_unref (file);
697
698 parent = g_file_get_path (parent_file);
699 g_object_unref (parent_file);
700
701 return parent;
702 }
703
704 /**
705 * totem_cd_detect_type_from_dir:
706 * @dir: a directory URI
707 * @mrl: (out) (transfer full) (allow-none): return location for the disc's MRL, or %NULL
708 * @error: return location for a #GError, or %NULL
709 *
710 * Detects the disc's type, given its mount directory URI. If
711 * a string pointer is passed to @mrl, it will return the disc's
712 * MRL as from totem_cd_mrl_from_type().
713 *
714 * Note that this function does synchronous I/O.
715 *
716 * If no disc is present in the drive, a #TOTEM_PL_PARSER_ERROR_NO_DISC
717 * error will be returned. On unknown mounting errors, a
718 * #TOTEM_PL_PARSER_ERROR_MOUNT_FAILED error will be returned. On other
719 * I/O errors, or if resolution of symlinked mount paths failed, a code from
720 * #GIOErrorEnum will be returned.
721 *
722 * Return value: #TotemDiscMediaType corresponding to the disc's type, or #MEDIA_TYPE_ERROR on failure
723 **/
724 TotemDiscMediaType
totem_cd_detect_type_from_dir(const char * dir,char ** mrl,GError ** error)725 totem_cd_detect_type_from_dir (const char *dir, char **mrl, GError **error)
726 {
727 CdCache *cache;
728 TotemDiscMediaType type;
729
730 g_return_val_if_fail (dir != NULL, MEDIA_TYPE_ERROR);
731
732 if (!(cache = cd_cache_new (dir, error)))
733 return MEDIA_TYPE_ERROR;
734 if ((type = cd_cache_disc_is_vcd (cache, error)) == MEDIA_TYPE_DATA &&
735 (type = cd_cache_disc_is_dvd (cache, error)) == MEDIA_TYPE_DATA &&
736 (type = cd_cache_disc_is_bd (cache, error)) == MEDIA_TYPE_DATA) {
737 /* is it the directory itself? */
738 char *parent;
739
740 cd_cache_free (cache);
741 parent = totem_cd_dir_get_parent (dir);
742 if (!parent)
743 return type;
744
745 cache = cd_cache_new (parent, error);
746 g_free (parent);
747 if (!cache)
748 return MEDIA_TYPE_ERROR;
749 if ((type = cd_cache_disc_is_vcd (cache, error)) == MEDIA_TYPE_DATA &&
750 (type = cd_cache_disc_is_dvd (cache, error)) == MEDIA_TYPE_DATA &&
751 (type = cd_cache_disc_is_bd (cache, error)) == MEDIA_TYPE_DATA) {
752 /* crap, nothing found */
753 cd_cache_free (cache);
754 return type;
755 }
756 }
757
758 if (mrl == NULL) {
759 cd_cache_free (cache);
760 return type;
761 }
762
763 if (type == MEDIA_TYPE_DVD) {
764 *mrl = totem_cd_mrl_from_type ("dvd", cache->mountpoint ? cache->mountpoint : cache->device);
765 } else if (type == MEDIA_TYPE_VCD) {
766 *mrl = totem_cd_mrl_from_type ("vcd", cache->mountpoint);
767 } else if (type == MEDIA_TYPE_BD) {
768 *mrl = totem_cd_mrl_from_type ("bluray", cache->mountpoint);
769 }
770
771 cd_cache_free (cache);
772
773 return type;
774 }
775
776 /**
777 * totem_cd_detect_type_with_url:
778 * @device: a device node path
779 * @mrl: (out) (transfer full) (allow-none): return location for the disc's MRL, or %NULL
780 * @error: return location for a #GError, or %NULL
781 *
782 * Detects the disc's type, given its device node path. If
783 * a string pointer is passed to @mrl, it will return the disc's
784 * MRL as from totem_cd_mrl_from_type().
785 *
786 * Note that this function does synchronous I/O.
787 *
788 * Possible error codes are as per totem_cd_detect_type_from_dir().
789 *
790 * Return value: #TotemDiscMediaType corresponding to the disc's type, or #MEDIA_TYPE_ERROR on failure
791 **/
792 TotemDiscMediaType
totem_cd_detect_type_with_url(const char * device,char ** mrl,GError ** error)793 totem_cd_detect_type_with_url (const char *device,
794 char **mrl,
795 GError **error)
796 {
797 CdCache *cache;
798 TotemDiscMediaType type;
799
800 if (mrl != NULL)
801 *mrl = NULL;
802
803 if (!(cache = cd_cache_new (device, error)))
804 return MEDIA_TYPE_ERROR;
805
806 type = cd_cache_disc_is_cdda (cache, error);
807 if (type == MEDIA_TYPE_ERROR && *error != NULL) {
808 cd_cache_free (cache);
809 return type;
810 }
811
812 if ((type == MEDIA_TYPE_DATA || type == MEDIA_TYPE_ERROR) &&
813 (type = cd_cache_disc_is_vcd (cache, error)) == MEDIA_TYPE_DATA &&
814 (type = cd_cache_disc_is_dvd (cache, error)) == MEDIA_TYPE_DATA &&
815 (type = cd_cache_disc_is_bd (cache, error)) == MEDIA_TYPE_DATA) {
816 /* crap, nothing found */
817 }
818
819 if (mrl == NULL) {
820 cd_cache_free (cache);
821 return type;
822 }
823
824 switch (type) {
825 case MEDIA_TYPE_DVD:
826 {
827 const char *str;
828
829 if (!cache->is_iso)
830 str = cache->mountpoint ? cache->mountpoint : device;
831 else
832 str = cache->device;
833 *mrl = totem_cd_mrl_from_type ("dvd", str);
834 }
835 break;
836 case MEDIA_TYPE_VCD:
837 {
838 const char *str;
839
840 if (!cache->is_iso)
841 str = cache->mountpoint ? cache->mountpoint : device;
842 else
843 str = cache->device;
844 *mrl = totem_cd_mrl_from_type ("vcd", str);
845 }
846 break;
847 case MEDIA_TYPE_CDDA:
848 {
849 const char *dev;
850
851 dev = cache->device ? cache->device : device;
852 if (g_str_has_prefix (dev, "/dev/") != FALSE)
853 *mrl = totem_cd_mrl_from_type ("cdda", dev + 5);
854 else
855 *mrl = totem_cd_mrl_from_type ("cdda", dev);
856 }
857 break;
858 case MEDIA_TYPE_BD:
859 {
860 const char *str;
861
862 if (!cache->is_iso)
863 str = cache->mountpoint ? cache->mountpoint : device;
864 else
865 str = cache->device;
866 *mrl = totem_cd_mrl_from_type ("bluray", str);
867 }
868 break;
869 case MEDIA_TYPE_DATA:
870 if (cache->is_iso) {
871 type = MEDIA_TYPE_ERROR;
872 /* No error, it's just not usable */
873 } else {
874 *mrl = g_filename_to_uri (cache->mountpoint, NULL, NULL);
875 if (*mrl == NULL)
876 *mrl = g_strdup (cache->mountpoint);
877 }
878 break;
879 case MEDIA_TYPE_ERROR:
880 case MEDIA_TYPE_DVB:
881 default:
882 break;
883 }
884
885 cd_cache_free (cache);
886
887 return type;
888 }
889
890 /**
891 * totem_cd_detect_type:
892 * @device: a device node path
893 * @error: return location for a #GError, or %NULL
894 *
895 * Detects the disc's type, given its device node path.
896 *
897 * Possible error codes are as per totem_cd_detect_type_with_url().
898 *
899 * Return value: #TotemDiscMediaType corresponding to the disc's type, or #MEDIA_TYPE_ERROR on failure
900 **/
901 TotemDiscMediaType
totem_cd_detect_type(const char * device,GError ** error)902 totem_cd_detect_type (const char *device,
903 GError **error)
904 {
905 return totem_cd_detect_type_with_url (device, NULL, error);
906 }
907
908 /**
909 * totem_cd_has_medium:
910 * @device: a device node path
911 *
912 * Returns whether the disc has a physical medium.
913 *
914 * Return value: %TRUE if the disc physically exists
915 **/
916 gboolean
totem_cd_has_medium(const char * device)917 totem_cd_has_medium (const char *device)
918 {
919 CdCache *cache;
920 gboolean retval = TRUE;
921
922 if (!(cache = cd_cache_new (device, NULL)))
923 return TRUE;
924
925 retval = cd_cache_has_medium (cache);
926 cd_cache_free (cache);
927
928 return retval;
929 }
930
931 /**
932 * totem_cd_get_human_readable_name:
933 * @type: a #TotemDiscMediaType
934 *
935 * Returns the human-readable name for the given
936 * #TotemDiscMediaType.
937 *
938 * Return value: the disc media type's readable name, which must not be freed, or %NULL for unhandled media types
939 **/
940 const char *
totem_cd_get_human_readable_name(TotemDiscMediaType type)941 totem_cd_get_human_readable_name (TotemDiscMediaType type)
942 {
943 switch (type)
944 {
945 case MEDIA_TYPE_CDDA:
946 return N_("Audio CD");
947 case MEDIA_TYPE_VCD:
948 return N_("Video CD");
949 case MEDIA_TYPE_DVD:
950 return N_("DVD");
951 case MEDIA_TYPE_DVB:
952 return N_("Digital Television");
953 case MEDIA_TYPE_BD:
954 return N_("Blu-ray");
955 case MEDIA_TYPE_ERROR:
956 case MEDIA_TYPE_DATA:
957 default:
958 g_assert_not_reached ();
959 }
960
961 return NULL;
962 }
963
964 GQuark
totem_disc_media_type_quark(void)965 totem_disc_media_type_quark (void)
966 {
967 static GQuark quark = 0;
968 if (!quark)
969 quark = g_quark_from_static_string ("totem_disc_media_type");
970
971 return quark;
972 }
973
974 /*
975 * vim: sw=2 ts=8 cindent noai bs=2
976 */
977