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