Logo Search packages:      
Sourcecode: nautilus-cd-burner version File versions  Download package

nautilus-burn-drive.c

/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
 *
 * nautilus-burn-drive.c: easy to use cd burner software
 *
 * Copyright (C) 2002-2004 Red Hat, Inc.
 * Copyright (C) 2005 William Jon McCann <mccann@jhu.edu>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 * 
 * Authors: Alexander Larsson <alexl@redhat.com>
 *          Bastien Nocera <hadess@hadess.net>
 *          William Jon McCann <mccann@jhu.edu>
 */

#include "config.h"

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <math.h>

#ifdef __linux__
#include <linux/cdrom.h>
#include <scsi/scsi.h>
#include <scsi/sg.h>
#endif /* __linux__ */

#ifdef __FreeBSD__
#include <sys/cdio.h>
#include <sys/cdrio.h>
#include <camlib.h>
#endif /* __FreeBSD__ */

#ifdef HAVE_SYS_CDIO_H
#include <sys/cdio.h>
#endif

#include <glib.h>
#include <glib/gstdio.h>
#include <glib/gi18n.h>
#include <gdk/gdk.h>

#ifdef USE_HAL
#include <libhal.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#endif /* USE_HAL */

#include "nautilus-burn-drive.h"

/* For dvd_plus_rw_utils.cpp */
int    get_dvd_r_rw_profile (const char *name);
int    get_mmc_profile      (int fd);
gint64 get_disc_size_cd     (int fd);
int    get_disc_size_cdr    (int fd);
gint64 get_disc_size_dvd    (int fd);
gint64 get_disc_size_dvdr   (int fd, int mmc_profile);
int    get_read_write_speed (int fd, int *read_speed, int *write_speed);
int    get_disc_status      (int fd, int *empty, int *is_rewritable, int *is_blank);

#define ENABLE_NON_HAL_FALLBACK_CODE 1

#ifndef INVALID_HANDLE
#define INVALID_HANDLE (GINT_TO_POINTER(-1))
#endif

#define CD_ROM_SPEED 176

#define CDS_NO_INFO           0     /* if not implemented */
#define CDS_AUDIO       100
#define CDS_DATA_1            101
#define CDS_DATA_2            102
#define CDS_XA_2_1            103
#define CDS_XA_2_2            104
#define CDS_MIXED       105

static struct {
      const char *name;
      gboolean can_write_cdr;
      gboolean can_write_cdrw;
      gboolean can_write_dvdr;
      gboolean can_write_dvdram;
} recorder_whitelist [] = {};

typedef enum {
      NAUTILUS_BURN_DRIVE_PROTOCOL_IDE,
      NAUTILUS_BURN_DRIVE_PROTOCOL_SCSI,
} NautilusBurnDriveProtocolType;

#define NAUTILUS_BURN_DRIVE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NAUTILUS_BURN_TYPE_DRIVE, NautilusBurnDrivePrivate))

struct NautilusBurnDrivePrivate {
      NautilusBurnDriveProtocolType protocol;

      gboolean                      monitor_enabled;
      guint                         poll_id;

      int                          *write_speeds;

#ifdef USE_HAL
      LibHalContext                *ctx;
#endif

      char                         *udi;

      char                         *monitor_udi;
      NautilusBurnDriveType         monitor_media_type;
      gboolean                      monitor_tray_open;
};

/* Signals */
enum {
      MEDIA_ADDED,
      MEDIA_REMOVED,
      LAST_SIGNAL
};

enum {
        PROP_0,
        PROP_MONITOR_ENABLED
};

static int nautilus_burn_drive_table_signals [LAST_SIGNAL] = { 0 };

static GObjectClass *parent_class = NULL;

G_DEFINE_TYPE(NautilusBurnDrive, nautilus_burn_drive, G_TYPE_OBJECT);

/* fill in the write speeds as max_speed downto 1 if we
 * are using a detection method that doesn't report all
 * of the drive speeds
 */
static void
fill_write_speeds (NautilusBurnDrive *drive)
{
      int max_speed;
      int i;

      max_speed = drive->max_speed_write;
      drive->priv->write_speeds = g_new0 (int, max_speed + 1);

      for (i = 0; i < max_speed; i++) {
            drive->priv->write_speeds[i] = max_speed - i;
      }
}

#ifdef __FreeBSD__

#define get_ioctl_handle_fd(x) (((struct cam_device *)x)->fd)

static gpointer
open_ioctl_handle (const char *device)
{
      struct cam_device *cam;

      cam = cam_open_device (device, O_RDWR);

      return (cam ? (gpointer)cam : INVALID_HANDLE);
}

static void
close_ioctl_handle (gpointer handle)
{
      cam_close_device ((struct cam_device *)handle);
}

#else

#define get_ioctl_handle_fd(x) (GPOINTER_TO_INT(x))

static gpointer
open_ioctl_handle (const char *device)
{
      int fd;

      if ((fd = g_open (device, O_RDWR | O_EXCL | O_NONBLOCK, 0)) < 0
          && (fd = g_open (device, O_RDONLY | O_EXCL | O_NONBLOCK, 0)) < 0) {
            return INVALID_HANDLE;
      }

      return GINT_TO_POINTER (fd);
}

static void
close_ioctl_handle (gpointer handle)
{
      close (GPOINTER_TO_INT (handle));
}

#endif

#ifdef USE_HAL
static LibHalContext *
get_hal_context (void)
{
      static LibHalContext *ctx = NULL;
      DBusError             error;
      DBusConnection       *dbus_conn;

      if (ctx == NULL) {
            ctx = libhal_ctx_new ();
            if (ctx == NULL) {
                  g_warning ("Could not create a HAL context");
            } else { 
                  dbus_error_init (&error);
                  dbus_conn = dbus_bus_get (DBUS_BUS_SYSTEM, &error);

                  if (dbus_error_is_set (&error)) {
                        g_warning ("Could not connect to system bus: %s", error.message);
                        dbus_error_free (&error);
                        return NULL;
                  }

                  libhal_ctx_set_dbus_connection (ctx, dbus_conn);

                  if (! libhal_ctx_init (ctx, &error)) {
                        g_warning ("Could not initalize the HAL context: %s",
                                 error.message);

                        if (dbus_error_is_set (&error))
                              dbus_error_free (&error);

                        libhal_ctx_free (ctx);
                        ctx = NULL;
                  }
            }
      }

      return ctx;
}
#endif /* USE_HAL */

static int
get_device_max_write_speed (char *device)
{
      gpointer ioctl_handle;
      int      fd;
      int      max_speed;
      int      read_speed, write_speed;

      max_speed = -1;

      ioctl_handle = open_ioctl_handle (device);
      if (ioctl_handle == INVALID_HANDLE) {
            return -1;
      }

      fd = get_ioctl_handle_fd (ioctl_handle);
      get_read_write_speed (fd, &read_speed, &write_speed);
      close_ioctl_handle (ioctl_handle);
      max_speed = (int) floor (write_speed) / CD_ROM_SPEED;

      return max_speed;
}

/* Utility functions, be careful to have a match with what's use in the
 * different bits of code */
#if !defined (__linux__)

static int
get_device_max_read_speed (char *device)
{
      gpointer ioctl_handle;
      int      fd;
      int      max_speed;
      int      read_speed, write_speed;

      max_speed = -1;

      ioctl_handle = open_ioctl_handle (device);
      if (ioctl_handle == INVALID_HANDLE) {
            return -1;
      }

      fd = get_ioctl_handle_fd (ioctl_handle);
      get_read_write_speed (fd, &read_speed, &write_speed);
      close_ioctl_handle (ioctl_handle);
      max_speed = (int)floor (read_speed) / CD_ROM_SPEED;

      return max_speed;
}

static char *
cdrecord_get_stdout_for_id (char *id)
{
      int         max_speed, i;
      const char *argv [20]; /* Shouldn't need more than 20 arguments */
      char       *dev_str, *stdout_data;

      max_speed = -1;

      i = 0;
      argv [i++] = "cdrecord";
      argv [i++] = "-prcap";
      dev_str = g_strdup_printf ("dev=%s", id);
      argv [i++] = dev_str;
      argv [i++] = NULL;

      if (g_spawn_sync (NULL,
                    (char **)argv,
                    NULL,
                    G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL,
                    NULL, NULL,
                    &stdout_data,
                    NULL,
                    NULL,
                    NULL)) {
            g_free (dev_str);
            return stdout_data;
      }

      g_free (dev_str);
      return NULL;
}

static void
get_cd_properties (char                  *device,
               char                  *id,
               int                   *max_rd_speed,
               int                   *max_wr_speed,
               NautilusBurnDriveType *type)
{
      char *stdout_data, *drive_cap;

      *max_rd_speed = -1;
      *max_wr_speed = -1;
      *type = 0;

      *max_rd_speed = get_device_max_read_speed (device);
      *max_wr_speed = get_device_max_write_speed (device);

      stdout_data = cdrecord_get_stdout_for_id (id);
      if (stdout_data == NULL) {
            return;
      }
      drive_cap = strstr (stdout_data, "Does write DVD-RAM media");
      if (drive_cap != NULL) {
            *type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_RAM_RECORDER;
      }
      drive_cap = strstr (stdout_data, "Does read DVD-R media");
      if (drive_cap != NULL) {
            *type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_RW_RECORDER;
      }
      drive_cap = strstr (stdout_data, "Does read DVD-ROM media");
      if (drive_cap != NULL) {
            *type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_DRIVE;
      }
      drive_cap = strstr (stdout_data, "Does write CD-RW media");
      if (drive_cap != NULL) {
            *type |= NAUTILUS_BURN_DRIVE_TYPE_CDRW_RECORDER;
      }
      drive_cap = strstr (stdout_data, "Does write CD-R media");
      if (drive_cap != NULL) {
            *type |= NAUTILUS_BURN_DRIVE_TYPE_CD_RECORDER;
      }
      drive_cap = strstr (stdout_data, "Does read CD-R media");
      if (drive_cap != NULL) {
            *type |= NAUTILUS_BURN_DRIVE_TYPE_CD_DRIVE;
      }
      g_free (stdout_data);
}
#endif /* !__linux__ */

#if !(defined (__linux__)) || (defined (__linux__) && (defined (USE_HAL)))
static void
add_whitelist (NautilusBurnDrive *drive)
{
      guint i;

      for (i = 0; i < G_N_ELEMENTS (recorder_whitelist); i++) {
            if (!strcmp (drive->display_name, recorder_whitelist [i].name)) {
                  if (recorder_whitelist [i].can_write_cdr) {
                        drive->type |= NAUTILUS_BURN_DRIVE_TYPE_CD_RECORDER;
                  }
                  if (recorder_whitelist [i].can_write_cdrw) {
                        drive->type |= NAUTILUS_BURN_DRIVE_TYPE_CDRW_RECORDER;
                  }
                  if (recorder_whitelist [i].can_write_dvdr) {
                        drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_RW_RECORDER;
                  }
                  if (recorder_whitelist [i].can_write_dvdram) {
                        drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_RAM_RECORDER;
                  }
            }
      }
}
#endif /* !__linux__ */

/**
 * nautilus_burn_drive_get_max_speed_write:
 * @drive: #NautilusBurnDrive
 *
 * Get the maximum write speed that the drive is capable of
 *
 * Returns: The speed of the drive, in device units.
 *
 * Since: 2.14
 **/
int
nautilus_burn_drive_get_max_speed_write (NautilusBurnDrive *drive)
{
      int speed;

      g_return_val_if_fail (drive != NULL, -1);

      speed = drive->max_speed_write;

      return speed;
}

/**
 * nautilus_burn_drive_get_max_speed_read:
 * @drive: #NautilusBurnDrive
 *
 * Get the maximum read speed that the drive is capable of
 *
 * Returns: The speed of the drive, in device units.
 *
 * Since: 2.14
 **/
int
nautilus_burn_drive_get_max_speed_read (NautilusBurnDrive *drive)
{
      int speed;

      g_return_val_if_fail (drive != NULL, -1);

      speed = drive->max_speed_read;

      return speed;
}

/**
 * nautilus_burn_drive_get_max_write_speeds:
 * @drive: #NautilusBurnDrive
 *
 * Get the list of supported write speeds
 *
 * Returns: The write speeds of the drive, in device units, as a
 * zero-terminated array of integers.  The array is sorted in
 * descending order and has no duplicate entries.
 *
 * Since: 2.14
 **/
const int *
nautilus_burn_drive_get_write_speeds (NautilusBurnDrive *drive)
{
      const int *speeds;

      g_return_val_if_fail (drive != NULL, NULL);

      speeds = drive->priv->write_speeds;

      return speeds;
}

/**
 * nautilus_burn_drive_get_name_for_display:
 * @drive: #NautilusBurnDrive
 *
 * Get the name of the drive for use in a user interface
 *
 * Returns: name of the drive.  Must be freed with g_free().
 *
 * Since: 2.14
 **/
char *
nautilus_burn_drive_get_name_for_display (NautilusBurnDrive *drive)
{
      char *name;

      g_return_val_if_fail (drive != NULL, NULL);

      name = g_strdup (drive->display_name);

      return name;
}

/**
 * nautilus_burn_drive_can_rewrite:
 * @drive: #NautilusBurnDrive
 *
 * Report the whether the drive is capable of re-recording
 *
 * Returns: %TRUE if the drive can rewrite, otherwise return %FALSE.
 *
 * Since: 2.14
 **/
gboolean
nautilus_burn_drive_can_rewrite (NautilusBurnDrive *drive)
{
      int      type;
      gboolean can_rewrite;

      g_return_val_if_fail (drive != NULL, FALSE);

      type = drive->type;

      can_rewrite = (type & NAUTILUS_BURN_DRIVE_TYPE_CDRW_RECORDER
                   || type & NAUTILUS_BURN_DRIVE_TYPE_DVD_RW_RECORDER
                   || type & NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_RW_RECORDER);

      return can_rewrite;
}

/**
 * nautilus_burn_drive_can_write:
 * @drive: #NautilusBurnDrive
 *
 * Report the whether the drive is capable of recording
 *
 * Returns: %TRUE if the drive is a recorder, otherwise return %FALSE.
 *
 * Since: 2.14
 **/
gboolean
nautilus_burn_drive_can_write (NautilusBurnDrive *drive)
{
      int      type;
      gboolean can_write;
      gboolean can_rewrite;

      g_return_val_if_fail (drive != NULL, FALSE);

      type = drive->type;

      /* Handle rewritables first */
      can_rewrite = nautilus_burn_drive_can_rewrite (drive);
      if (can_rewrite)
            return TRUE;

      /* now only handle non-rewritables */
      can_write = (type & NAUTILUS_BURN_DRIVE_TYPE_FILE
                 || type & NAUTILUS_BURN_DRIVE_TYPE_CD_RECORDER
                 || type & NAUTILUS_BURN_DRIVE_TYPE_DVD_RAM_RECORDER
                 || type & NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_R_RECORDER
                 || type & NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_R_DL_RECORDER);

      return can_write;
}

static void
add_dvd_plus (NautilusBurnDrive *drive)
{
      int caps;

      caps = get_dvd_r_rw_profile (drive->device);

      if (caps == -1) {
            return;
      }

      if (caps == 2) {
            drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_RW_RECORDER;
            drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_R_RECORDER;
      } else if (caps == 0) {
            drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_R_RECORDER;
      } else if (caps == 1) {
            drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_RW_RECORDER;
      }
}

static gboolean
drive_door_is_open (int fd)
{
      if (fd < 0) {
            return FALSE;
      }

#ifdef __linux__
      {
            int status;

            status = ioctl (fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
            if (status < 0) {
                  return FALSE;
            }

            return status == CDS_TRAY_OPEN;
      }
#else
      return FALSE;
#endif
}

/**
 * nautilus_burn_drive_door_is_open:
 * @drive: #NautilusBurnDrive
 *
 * Report the whether the drive door or tray is open.
 *
 * Returns: %TRUE if the drive door is open, otherwise return %FALSE.
 *
 * Since: 2.12
 **/
gboolean
nautilus_burn_drive_door_is_open (NautilusBurnDrive *drive)
{
      gpointer ioctl_handle;
      int      fd;
      gboolean ret;

      g_return_val_if_fail (drive != NULL, FALSE);

      ioctl_handle = open_ioctl_handle (drive->device);
      if (ioctl_handle == INVALID_HANDLE)
            return FALSE;

      fd = get_ioctl_handle_fd (ioctl_handle);

      ret = drive_door_is_open (fd);

      close_ioctl_handle (ioctl_handle);

      return ret;
}

/* adapted from gnome-vfs */
static int
get_disc_type (const char *dev_path)
{
      int                   fd = 0;
      int                   type;
#if defined (HAVE_SYS_MNTTAB_H)   /* Solaris */
      GString              *new_dev_path;
      struct cdrom_tocentry entry;
      struct cdrom_tochdr   header;

      /* For ioctl call to work dev_path has to be /vol/dev/rdsk.
       * There has to be a nicer way to do this.
       */
      new_dev_path = g_string_new (dev_path);
      new_dev_path = g_string_insert_c (new_dev_path, 9, 'r');
      fd = open (new_dev_path->str, O_RDONLY | O_NONBLOCK);
      g_string_free (new_dev_path, TRUE);

      if (fd < 0) {
            return -1;
      }

      if (ioctl (fd, CDROMREADTOCHDR, &header) < 0) {
            return -1;
      }

      type = CDS_DATA_1;
      
      for (entry.cdte_track = 1;
           entry.cdte_track <= header.cdth_trk1;
           entry.cdte_track++) {
            entry.cdte_format = CDROM_LBA;
            if (ioctl (fd, CDROMREADTOCENTRY, &entry) == 0) {
                  if (!(entry.cdte_ctrl & CDROM_DATA_TRACK)) {
                        type = CDS_AUDIO;
                        break;
                  }
            }
      }

      return type;
#elif defined(HAVE_SYS_MNTCTL_H)
      return CDS_NO_INFO;
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
      struct ioc_toc_header            header;
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
      struct ioc_read_toc_single_entry entry;
#else
      struct ioc_read_toc_entry        entries;
      struct cd_toc_entry              entry;
      int                              i;
#endif
      int                              type;
#ifndef CDROM_DATA_TRACK
#define CDROM_DATA_TRACK 4
#endif

      fd = open (dev_path, O_RDONLY|O_NONBLOCK);
      if (fd < 0) {
            return -1;
      }

      if (ioctl (fd, CDIOREADTOCHEADER, &header) < 0) {
            close (fd);
            return -1;
      }

      type = CDS_DATA_1;
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
      for (entry.track = header.starting_track;
            entry.track <= header.ending_track;
            entry.track++) {
            entry.address_format = CD_LBA_FORMAT;
            if (ioctl (fd, CDIOREADTOCENTRY, &entry) == 0) {
                  if (!(entry.entry.control & CDROM_DATA_TRACK)) {
                        type = CDS_AUDIO;
                        break;
                  }
            }
      }

#else /* defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)*/
      entries.data_len = sizeof (entry);
      entries.data = &entry;
      for (i = header.starting_track; i <= header.ending_track; i++) {
            entries.starting_track = i;
            entries.address_format = CD_LBA_FORMAT;
            if (ioctl (fd, CDIOREADTOCENTRYS, &entries) == 0) {
                  if (!(entry.control & CDROM_DATA_TRACK)) {
                        type = CDS_AUDIO;
                        break;
                  }
            }
      }

#endif /* defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)*/
      close (fd);
      return type;
#elif defined(__linux__)
      fd = open (dev_path, O_RDONLY|O_NONBLOCK);
      if (fd  < 0) {
            return -1;
      }
      if (ioctl (fd, CDROM_DRIVE_STATUS, CDSL_CURRENT) != CDS_DISC_OK) {
            close (fd);
            fd = -1;
            return -1;
      }

      type = ioctl (fd, CDROM_DISC_STATUS, CDSL_CURRENT);
      close (fd);

      return type;
#else /* defined(__linux__) */
      return CDS_NO_INFO;
#endif /* defined(__linux__) */
}

static void
disc_type_get_info (int       type,
                gboolean *is_blank,
                gboolean *has_data,
                gboolean *has_audio)
{
      gboolean _has_audio = FALSE;
      gboolean _has_data  = FALSE;
      gboolean _is_blank  = FALSE;

      switch (type) {
      case CDS_AUDIO:         /* audio CD */
            _has_audio = TRUE;
            break;
      case CDS_MIXED:         /* mixed mode CD */
            _has_audio = TRUE;
            _has_data  = TRUE;
            break;
      case CDS_DATA_1:  /* data CD */
      case CDS_DATA_2:
      case CDS_XA_2_1:
      case CDS_XA_2_2:
            _has_data  = TRUE;
            break;
      case CDS_NO_INFO: /* blank or invalid CD */
            _is_blank  = TRUE;
            break;
            
      default:          /* should never see this */
            break;
      }

      if (is_blank)
            *is_blank = _is_blank;
      if (has_data)
            *has_data = _has_data;
      if (has_audio)
            *has_audio = _has_audio;
}
      
static NautilusBurnMediaType
nautilus_burn_drive_get_media_type_from_path_full (const char *device,
                                       gboolean   *is_rewritable,
                                       gboolean   *is_blank,
                                       gboolean   *has_data,
                                       gboolean   *has_audio)
{
      gpointer ioctl_handle;
      int      fd;
      int      mmc_profile;
      int      disc_type;

      g_return_val_if_fail (device != NULL, NAUTILUS_BURN_MEDIA_TYPE_ERROR);

      if (is_rewritable) *is_rewritable = FALSE;
      if (is_blank) *is_blank = FALSE;
      if (has_data) *has_data = FALSE;
      if (has_audio) *has_audio = FALSE;

      ioctl_handle = open_ioctl_handle (device);
      if (ioctl_handle == INVALID_HANDLE) {
            if (errno == EBUSY) {
                  return NAUTILUS_BURN_MEDIA_TYPE_BUSY;
            }
            g_warning ("Could not open %s: %s", device, g_strerror (errno));
            return NAUTILUS_BURN_MEDIA_TYPE_ERROR;
      }

      fd = get_ioctl_handle_fd (ioctl_handle);
      mmc_profile = get_mmc_profile (fd);

      /* Couldn't get the data about the media */
      if (mmc_profile < 0) {
            gboolean door_is_open;

            door_is_open = drive_door_is_open (fd);

            if (door_is_open != FALSE) {
                  close_ioctl_handle (ioctl_handle);
                  return NAUTILUS_BURN_MEDIA_TYPE_ERROR;
            } else {
                  int blank, rewrite, empty;

                  if (get_disc_status (fd, &empty, &rewrite, &blank) == 0) {

                        if (is_rewritable)
                              *is_rewritable = rewrite;
                        
                        if (is_blank)
                              *is_blank = blank;
                  }
                  close_ioctl_handle (ioctl_handle);

                  disc_type = get_disc_type (device);
                  disc_type_get_info (disc_type, is_blank, has_data, has_audio);

                  if (empty)
                        return NAUTILUS_BURN_MEDIA_TYPE_ERROR;
                  else
                        return NAUTILUS_BURN_MEDIA_TYPE_UNKNOWN;
            }
      }

      close_ioctl_handle (ioctl_handle);

      if (is_blank)
            *is_blank = mmc_profile & 0x10000;

      disc_type = get_disc_type (device);
      disc_type_get_info (disc_type, is_blank, has_data, has_audio);

      switch (mmc_profile & 0xFFFF) {
      case -1:
            g_assert_not_reached ();
      case 0:           /* No Media */
            return NAUTILUS_BURN_MEDIA_TYPE_ERROR;
      case 0x8:   /* Commercial CDs and Audio CD      */
            return NAUTILUS_BURN_MEDIA_TYPE_CD;
      case 0x9:   /* CD-R                         */
            return NAUTILUS_BURN_MEDIA_TYPE_CDR;
      case 0xa:   /* CD-RW                */
            if (is_rewritable)
                  *is_rewritable = TRUE;
            return NAUTILUS_BURN_MEDIA_TYPE_CDRW;
      case 0x10:  /* Commercial DVDs            */
            return NAUTILUS_BURN_MEDIA_TYPE_DVD;
      case 0x11:      /* DVD-R                        */
            return NAUTILUS_BURN_MEDIA_TYPE_DVDR;
      case 0x13:      /* DVD-RW Restricted Overwrite  */
      case 0x14:      /* DVD-RW Sequential            */
            if (is_rewritable)
                  *is_rewritable = TRUE;
            return NAUTILUS_BURN_MEDIA_TYPE_DVDRW;
      case 0x1B:      /* DVD+R                        */
            return NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_R;
      case 0x2B:      /* DVD+R Double Layer           */
            return NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_R_DL;
      case 0x1A:      /* DVD+RW                       */
            if (is_rewritable)
                  *is_rewritable = TRUE;
            return NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_RW;
      case 0x12:      /* DVD-RAM                      */
            return NAUTILUS_BURN_MEDIA_TYPE_DVD_RAM;
      default:
            return NAUTILUS_BURN_MEDIA_TYPE_UNKNOWN;
      }
}

/**
 * nautilus_burn_drive_get_media_type_from_path:
 * @device: Filename of drive device (eg. "/dev/hdb")
 *
 * Determine the type of the media in the drive at device @device.
 *
 * Return value: The #NautilusBurnMediaType of the media in the drive or the
 * following special values:
 *
 *    %NAUTILUS_BURN_MEDIA_TYPE_ERROR   if the type can not be determined
 *    %NAUTILUS_BURN_MEDIA_TYPE_UNKNOWN if the type can not be determined
 *    %NAUTILUS_BURN_MEDIA_TYPE_BUSY    if the device is busy
 **/
NautilusBurnMediaType
nautilus_burn_drive_get_media_type_from_path (const char *device)
{
      g_return_val_if_fail (device != NULL, NAUTILUS_BURN_MEDIA_TYPE_ERROR);

      return nautilus_burn_drive_get_media_type_from_path_full (device, NULL, NULL, NULL, NULL);
}

/**
 * nautilus_burn_drive_get_media_type:
 * @drive: #NautilusBurnDrive
 *
 * Get the type of the specified drive.
 *
 * Return value: See nautilus_burn_drive_get_media_type_from_path() for details.
 **/
NautilusBurnMediaType
nautilus_burn_drive_get_media_type (NautilusBurnDrive *drive)
{
      g_return_val_if_fail (drive != NULL, NAUTILUS_BURN_MEDIA_TYPE_ERROR);

      return nautilus_burn_drive_get_media_type_full (drive, NULL, NULL, NULL, NULL);
}

#ifdef USE_HAL

static char *
hal_drive_get_first_child_udi (NautilusBurnDrive *drive)
{
      LibHalContext        *ctx;
      char                **device_names;
      int                   num_devices;
      DBusError             error;
      char                 *udi;

      udi = NULL;

      if (drive->priv->udi == NULL) {
            return NULL;
      }

      ctx = drive->priv->ctx;

      if (ctx == NULL) {
            return NULL;
      }

      num_devices = -1;
      dbus_error_init (&error);
      device_names = libhal_manager_find_device_string_match (ctx, 
                                                "info.parent",
                                                drive->priv->udi,
                                                &num_devices,
                                                &error);

      if (dbus_error_is_set (&error)) {
            g_warning ("%s\n", error.message);
            dbus_error_free (&error);

            goto done;
      }

      if (num_devices <= 0) {
            /*g_warning ("No HAL devices found for UDI '%s'", drive->priv->udi);*/

            goto done;
      }

      udi = g_strdup (device_names [0]);

 done:
      libhal_free_string_array (device_names);

      return udi;
}

static NautilusBurnMediaType
nautilus_burn_drive_hal_get_media_type_full (NautilusBurnDrive *drive,
                                   gboolean          *is_rewritable,
                                   gboolean          *is_blank,
                                   gboolean          *has_data,
                                   gboolean          *has_audio)
{
      LibHalContext        *ctx;
      NautilusBurnMediaType type;
      char                 *hal_type;
      char                 *child_udi;

      if (is_rewritable) *is_rewritable = FALSE;
      if (is_blank) *is_blank = FALSE;
      if (has_data) *has_data = FALSE;
      if (has_audio) *has_audio = FALSE;

      if (drive->priv->udi == NULL) {
            goto fallback;
      }

      ctx = drive->priv->ctx;

      child_udi = hal_drive_get_first_child_udi (drive);
      if (child_udi == NULL) {
            goto fallback;
      }

      if (is_rewritable)
            *is_rewritable = libhal_device_get_property_bool (ctx, 
                                                  child_udi,
                                                  "volume.disc.is_rewritable",
                                                  NULL);

      if (is_blank)
            *is_blank = libhal_device_get_property_bool (ctx, 
                                               child_udi,
                                               "volume.disc.is_blank",
                                               NULL);

      if (has_data)
            *has_data = libhal_device_get_property_bool (ctx, 
                                               child_udi,
                                               "volume.disc.has_data",
                                               NULL);

      if (has_audio)
            *has_audio = libhal_device_get_property_bool (ctx, 
                                                child_udi,
                                                "volume.disc.has_audio",
                                                NULL);

      type = NAUTILUS_BURN_MEDIA_TYPE_BUSY;
      hal_type = libhal_device_get_property_string (ctx, 
                                          child_udi,
                                          "volume.disc.type",
                                          NULL);

      if (hal_type == NULL || strcmp (hal_type, "unknown") == 0) {
            type = NAUTILUS_BURN_MEDIA_TYPE_UNKNOWN;
      } else if (strcmp (hal_type, "cd_rom") == 0) {
            type = NAUTILUS_BURN_MEDIA_TYPE_CD;
      } else if (strcmp (hal_type, "cd_r") == 0) {
            type = NAUTILUS_BURN_MEDIA_TYPE_CDR;
      } else if (strcmp (hal_type, "cd_rw") == 0) {
            type = NAUTILUS_BURN_MEDIA_TYPE_CDRW;
      } else if (strcmp (hal_type, "dvd_rom") == 0) {
            type = NAUTILUS_BURN_MEDIA_TYPE_DVD;
      } else if (strcmp (hal_type, "dvd_r") == 0) {
            type = NAUTILUS_BURN_MEDIA_TYPE_DVDR;
      } else if (strcmp (hal_type, "dvd_ram") == 0) {
            type = NAUTILUS_BURN_MEDIA_TYPE_DVD_RAM;
      } else if (strcmp (hal_type, "dvd_rw") == 0) {
            type = NAUTILUS_BURN_MEDIA_TYPE_DVDRW;
      } else if (strcmp (hal_type, "dvd_plus_rw") == 0) {
            type = NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_RW;
      } else if (strcmp (hal_type, "dvd_plus_r") == 0) {
            type = NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_R;
      } else if (strcmp (hal_type, "dvd_plus_r_dl") == 0) {
            type = NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_R_DL;
      } else {
            type = NAUTILUS_BURN_MEDIA_TYPE_UNKNOWN;
      }
                        
      if (hal_type != NULL) {
            libhal_free_string (hal_type);
      }

      g_free (child_udi);

      /* if the type has been determined using HAL, return it.
         Otherwise, fall through and try the non-HAL approach. */
      if (type != NAUTILUS_BURN_MEDIA_TYPE_UNKNOWN
          && type != NAUTILUS_BURN_MEDIA_TYPE_ERROR) {
            return type;
      }

 fallback:
#ifdef ENABLE_NON_HAL_FALLBACK_CODE
      return nautilus_burn_drive_get_media_type_from_path_full (drive->device,
                                                  is_rewritable,
                                                  is_blank,
                                                  has_data,
                                                  has_audio);
#else
      return NAUTILUS_BURN_MEDIA_TYPE_ERROR;
#endif
}
#endif

/**
 * nautilus_burn_drive_get_media_type_full:
 * @drive: #NautilusBurnDrive
 * @is_rewritable: set to TRUE if media is rewritable
 * @is_blank: set to TRUE if media is blank
 * @has_data: set to TRUE if media has data
 * @has_audio: set to TRUE if media has audio
 *
 * Get the type of the specified drive.
 *
 * Return value: See nautilus_burn_drive_get_media_type_from_path() for details.
 **/
NautilusBurnMediaType
nautilus_burn_drive_get_media_type_full (NautilusBurnDrive *drive,
                               gboolean          *is_rewritable,
                               gboolean          *is_blank,
                               gboolean          *has_data,
                               gboolean          *has_audio)
{
      g_return_val_if_fail (drive != NULL, NAUTILUS_BURN_MEDIA_TYPE_ERROR);

#ifdef USE_HAL
      return nautilus_burn_drive_hal_get_media_type_full (drive,
                                              is_rewritable,
                                              is_blank,
                                              has_data,
                                              has_audio);
#else
      return nautilus_burn_drive_get_media_type_from_path_full (drive->device,
                                                  is_rewritable,
                                                  is_blank,
                                                  has_data,
                                                  has_audio);
#endif
}

/**
 * nautilus_burn_drive_get_media_size_from_path:
 * @device: Filename of drive device (eg. "/dev/hdb")
 *
 * Determine the capacity of the media in the specified device.
 *
 * Return value: The capacity of the media in bytes or the
 * following special values:
 *
 *    %NAUTILUS_BURN_MEDIA_SIZE_UNKNOWN if the type can not be determined
 *    %NAUTILUS_BURN_MEDIA_SIZE_NA      if the device type is not recognized
 *    %NAUTILUS_BURN_MEDIA_SIZE_BUSY    if the device is busy
 **/
gint64
nautilus_burn_drive_get_media_size_from_path (const char *device)
{
      gpointer ioctl_handle;
      int      fd;
      int      secs;
      int      mmc_profile;
      gint64   size;

      g_return_val_if_fail (device != NULL, NAUTILUS_BURN_MEDIA_SIZE_UNKNOWN);

      secs = 0;

      ioctl_handle = open_ioctl_handle (device);
      if (ioctl_handle == INVALID_HANDLE) {
            if (errno == EBUSY) {
                  return NAUTILUS_BURN_MEDIA_SIZE_BUSY;
            }
            return NAUTILUS_BURN_MEDIA_SIZE_UNKNOWN;
      }

      fd = get_ioctl_handle_fd (ioctl_handle);
      mmc_profile = get_mmc_profile (fd);

      /* See nautilus_burn_drive_get_media_type_from_path for details */
      switch (mmc_profile & 0xFFFF) {
      case 0x8:
            size = get_disc_size_cd (fd);
            break;
      case 0x9:
      case 0xa:
            secs = get_disc_size_cdr (fd);
            if (secs > 0)
                  size = (1 + secs * 7 / 48) * 1024 * 1024;
            else
                  size = 0;
            break;
      case 0x10:
            size = get_disc_size_dvd (fd);
            break;
      case 0x11:
      case 0x13:
      case 0x14:
      case 0x1B:
      case 0x2B:
      case 0x1A:
      case 0x12:
            size = get_disc_size_dvdr (fd, mmc_profile);
            break;
      default:
            size = NAUTILUS_BURN_MEDIA_SIZE_NA;
      }

      close_ioctl_handle (ioctl_handle);

      return size;
}

#ifdef ENABLE_NON_HAL_FALLBACK_CODE
static gint64
try_media_size_from_type (NautilusBurnDrive *drive)
{
      gint64                size = 0;
      NautilusBurnMediaType type;
      int                   fd;
      int                   secs;
      gpointer              ioctl_handle;

      type = nautilus_burn_drive_get_media_type (drive);

      ioctl_handle = open_ioctl_handle (drive->device);
      if (ioctl_handle == INVALID_HANDLE) {
            if (errno == EBUSY) {
                  return NAUTILUS_BURN_MEDIA_SIZE_BUSY;
            }
            return NAUTILUS_BURN_MEDIA_SIZE_UNKNOWN;
      }

      fd = get_ioctl_handle_fd (ioctl_handle);

      /* The CD types should be able to detect the media sizes
         the DVD types will likely fail since if we are here
         they probably aren't MMC capable */
      switch (type) {
      case NAUTILUS_BURN_MEDIA_TYPE_BUSY:
      case NAUTILUS_BURN_MEDIA_TYPE_ERROR:
      case NAUTILUS_BURN_MEDIA_TYPE_UNKNOWN:
            size = NAUTILUS_BURN_MEDIA_SIZE_NA;
            break;
      case NAUTILUS_BURN_MEDIA_TYPE_CD:
            size = get_disc_size_cd (fd);
            break;
      case NAUTILUS_BURN_MEDIA_TYPE_CDR:
      case NAUTILUS_BURN_MEDIA_TYPE_CDRW:
            secs = get_disc_size_cdr (fd);
            if (secs > 0)
                  size = (1 + secs * 7 / 48) * 1024 * 1024;
            else
                  size = 0;
            break;
      case NAUTILUS_BURN_MEDIA_TYPE_DVD:
            size = get_disc_size_dvd (fd);
            break;
      case NAUTILUS_BURN_MEDIA_TYPE_DVDR:
            size = get_disc_size_dvdr (fd, 0x11);
            break;
      case NAUTILUS_BURN_MEDIA_TYPE_DVDRW:
            size = get_disc_size_dvdr (fd, 0x13);
            break;
      case NAUTILUS_BURN_MEDIA_TYPE_DVD_RAM:
            size = get_disc_size_dvdr (fd, 0x12);
            break;
      case NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_R:
            size = get_disc_size_dvdr (fd, 0x1B);
            break;
      case NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_RW:
            size = get_disc_size_dvdr (fd, 0x1A);
            break;
      case NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_R_DL:
            size = get_disc_size_dvdr (fd, 0x2B);
            break;
      default:
            break;
      }

      close_ioctl_handle (ioctl_handle);

      return size;
}
#endif

#ifdef USE_HAL
static gint64
nautilus_burn_drive_hal_get_media_size (NautilusBurnDrive *drive)
{
      gint64         size;
      guint64        res;
      LibHalContext *ctx;
      char          *child_udi;
      DBusError      error;

      size = -1;

      if (drive->priv->udi == NULL) {
            goto done;
      }
            
      ctx = drive->priv->ctx;

      if (ctx == NULL) {
            goto done;
      }

      child_udi = hal_drive_get_first_child_udi (drive);
      if (child_udi == NULL) {
            goto done;
      }

      dbus_error_init (&error);
      res = libhal_device_get_property_uint64 (ctx, 
                                     child_udi,
                                     "volume.disc.capacity",
                                     &error);

      g_free (child_udi);

      if (dbus_error_is_set (&error)) {
            g_warning ("%s\n", error.message);
            dbus_error_free (&error);
                        
            goto done;
      }

      size = (gint64) res;

 done:
      return size;
}
#endif

/**
 * nautilus_burn_drive_get_media_size:
 * @drive: #NautilusBurnDrive
 *
 * Determine the capacity of the media in the specified drive.
 *
 * Return value: The capacity of the media in bytes or the
 * following special values:
 *
 *    %NAUTILUS_BURN_MEDIA_SIZE_UNKNOWN if the type can not be determined
 *    %NAUTILUS_BURN_MEDIA_SIZE_NA      if the device type is not recognized
 *    %NAUTILUS_BURN_MEDIA_SIZE_BUSY    if the device is busy
 **/
gint64
nautilus_burn_drive_get_media_size (NautilusBurnDrive *drive)
{
      gint64 size;

      g_return_val_if_fail (drive != NULL, NAUTILUS_BURN_MEDIA_SIZE_UNKNOWN);

#ifdef USE_HAL
      size = nautilus_burn_drive_hal_get_media_size (drive);

#ifdef ENABLE_NON_HAL_FALLBACK_CODE
      if (size < 0) {
            size = nautilus_burn_drive_get_media_size_from_path (drive->device);
            if (size < 0) {
                  size = try_media_size_from_type (drive);
            }
      }
#endif

#else
      size = nautilus_burn_drive_get_media_size_from_path (drive->device);
#endif

      return size;
}

#ifdef USE_HAL
static char *
nautilus_burn_drive_hal_get_media_label (NautilusBurnDrive *drive)
{
      char          *label;
      LibHalContext *ctx;
      char          *child_udi;
      DBusError      error;

      g_return_val_if_fail (drive != NULL, NULL);

      label = NULL;

      if (drive->priv->udi == NULL) {
            goto done;
      }
            
      ctx = drive->priv->ctx;

      if (ctx == NULL) {
            goto done;
      }

      child_udi = hal_drive_get_first_child_udi (drive);
      if (child_udi == NULL) {
            goto done;
      }

      dbus_error_init (&error);
      label = libhal_device_get_property_string (ctx, 
                                       child_udi,
                                       "volume.label",
                                       &error);
      if (dbus_error_is_set (&error)) {
            g_warning ("%s\n", error.message);
            dbus_error_free (&error);
      }

      g_free (child_udi);
 done:
      return label;
}
#endif

/**
 * nautilus_burn_drive_get_media_label:
 * @drive: #NautilusBurnDrive
 *
 * Determine the label of the media in the specified drive.
 *
 * Return value: The label of the media.
 *
 * Since: 2.14
 **/
char *
nautilus_burn_drive_get_media_label (NautilusBurnDrive *drive)
{
      char *label;

      g_return_val_if_fail (drive != NULL, NULL);

      label = NULL;
#ifdef USE_HAL
      label = nautilus_burn_drive_hal_get_media_label (drive);
#else
      g_warning ("nautilus_burn_drive_get_media_label: not implemented without HAL");
#endif

      return label;
}

typedef struct {
      gboolean    timeout;
      gboolean    unmount_ok;
      guint       timeout_tag;
      GMainLoop  *loop;
      GPtrArray  *argv;
} UnmountData;

static void
free_unmount_data (UnmountData *unmount_data)
{
      g_ptr_array_foreach (unmount_data->argv, (GFunc)g_free, NULL);
      g_ptr_array_free (unmount_data->argv, TRUE);

      g_free (unmount_data);
}

static gboolean
unmount_done (gpointer data)
{
      UnmountData *unmount_data;
      unmount_data = data;
      
      if (unmount_data->timeout_tag != 0) {
            g_source_remove (unmount_data->timeout_tag);
      }

      if (unmount_data->loop != NULL &&
          g_main_loop_is_running (unmount_data->loop)) {
            g_main_loop_quit (unmount_data->loop);
      }
      
      if (unmount_data->timeout) {
            /* We timed out, so unmount_data wasn't freed
               at mainloop exit. */
            free_unmount_data (unmount_data);
      }
      
      return FALSE;
}

static gboolean
unmount_timeout (gpointer data)
{
      UnmountData *unmount_data;
      unmount_data = data;

      /* We're sure, the callback hasn't been run, so just say
         we were interrupted and return from the mainloop */
      
      unmount_data->unmount_ok = FALSE;
      unmount_data->timeout_tag = 0;
      unmount_data->timeout = TRUE;
      
      if (g_main_loop_is_running (unmount_data->loop)) {
            g_main_loop_quit (unmount_data->loop);
      }
      
      return FALSE;
}

#ifndef USE_GNOME_MOUNT
static const char *umount_known_locations [] = {
      "/sbin/umount",
      "/bin/umount",
      "/usr/sbin/umount",
      "/usr/bin/umount",
      NULL
};
#endif

/* Returns the full command */
static GPtrArray *
create_command (const char *device)
{
      GPtrArray *argv;
      char      *str;

      argv = g_ptr_array_new ();

#ifdef USE_GNOME_MOUNT
      str = g_strdup (BINDIR "/gnome-mount");
      g_ptr_array_add (argv, str);
      str = g_strdup_printf ("--device=%s", device);
      g_ptr_array_add (argv, str);
      str = g_strdup ("--unmount");
      g_ptr_array_add (argv, str);
      str = g_strdup ("--no-ui");
      g_ptr_array_add (argv, str);
#else
      {
            int   i;

            str = NULL;
            for (i = 0; umount_known_locations [i]; i++){
                  if (g_file_test (umount_known_locations [i], G_FILE_TEST_EXISTS)) {
                        str = g_strdup (umount_known_locations [i]);
                        break;
                  }
            }

            /* no path then try just command */
            if (str == NULL) {
                  str = g_strdup ("umount");
            }

            g_ptr_array_add (argv, str);
            str = g_strdup_printf ("%s", device);
            g_ptr_array_add (argv, str);
      }
#endif

      g_ptr_array_add (argv, NULL);

      return argv;
}

static void *
unmount_thread_start (void *arg)
{
      GError      *error;
      UnmountData *data;
      gint         exit_status;
      char        *envp [] = {
            "LC_ALL=C",
            NULL
      };

      data = arg;

      data->unmount_ok = TRUE;

      error = NULL;
      if (g_spawn_sync (NULL,
                    (char **)data->argv->pdata,
                    envp,
                    G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
                    NULL, NULL,
                    NULL,
                    NULL,
                    &exit_status,
                    &error)) {
            if (exit_status == 0) {
                  data->unmount_ok = TRUE;
            } else {
                  data->unmount_ok = FALSE;
            }

            /* Delay a bit to make sure unmount finishes */
            sleep (2);
      } else {
            /* spawn failure */
            if (error) {
                  g_warning ("Unable to unmount: %s", error->message);
                  g_error_free (error);
            }
            data->unmount_ok = FALSE;
      }

      g_idle_add (unmount_done, data);    
      
      g_thread_exit (NULL);   
      
      return NULL;
}

/**
 * nautilus_burn_drive_unmount:
 * @drive: #NautilusBurnDrive
 *
 * Unmount the media in a #NautilusBurnDrive.
 *
 * Return value: %TRUE if the media was sucessfully unmounted, %FALSE otherwise.
 *
 * Since: 2.10
 **/
gboolean
nautilus_burn_drive_unmount (NautilusBurnDrive *drive)
{
      UnmountData *data;
      gboolean     unmount_ok;

      g_return_val_if_fail (drive != NULL, FALSE);
      
      if (drive->device == NULL)
            return FALSE;
      
      unmount_ok = FALSE;

      data = g_new0 (UnmountData, 1);
      data->loop = g_main_loop_new (NULL, FALSE);

      data->timeout_tag = g_timeout_add (5 * 1000,
                                 unmount_timeout,
                                 data);
      data->argv = create_command (drive->device);

      g_thread_create (unmount_thread_start, data, FALSE, NULL);
      
      GDK_THREADS_LEAVE ();
      g_main_loop_run (data->loop);
      GDK_THREADS_ENTER ();
      
      g_main_loop_unref (data->loop);
      data->loop = NULL;

      unmount_ok = data->unmount_ok;

      if (!data->timeout) {
            /* Don't free data if mount operation still running. */
            free_unmount_data (data);
      }
      
      return unmount_ok;
}

#ifdef USE_HAL
static gboolean
nautilus_burn_drive_hal_is_mounted (NautilusBurnDrive *drive)
{
      gboolean       is_mounted;
      LibHalContext *ctx;
      char          *child_udi;
      DBusError      error;

      g_return_val_if_fail (drive != NULL, FALSE);

      is_mounted = FALSE;

      if (drive->priv->udi == NULL) {
            goto done;
      }
            
      ctx = drive->priv->ctx;

      if (ctx == NULL) {
            goto done;
      }

      child_udi = hal_drive_get_first_child_udi (drive);
      if (child_udi == NULL) {
            goto done;
      }

      dbus_error_init (&error);
      is_mounted = libhal_device_get_property_bool (ctx, 
                                          child_udi,
                                          "volume.is_mounted",
                                          &error);
      if (dbus_error_is_set (&error)) {
            g_warning ("%s\n", error.message);
            dbus_error_free (&error);
      }

      g_free (child_udi);
 done:
      return is_mounted;
}
#else
static gboolean
nautilus_burn_drive_nohal_is_mounted (NautilusBurnDrive *drive)
{
      gboolean              is_mounted;
      gint64                media_size;
      NautilusBurnMediaType type;

      media_size = nautilus_burn_drive_get_media_size (drive);
      type = nautilus_burn_drive_get_media_type (drive);

      is_mounted = (type == NAUTILUS_BURN_MEDIA_TYPE_BUSY)
            || (media_size == NAUTILUS_BURN_MEDIA_SIZE_BUSY);

      return is_mounted;
}
#endif

/**
 * nautilus_burn_drive_is_mounted:
 * @drive: #NautilusBurnDrive
 *
 * Determine if media in the specified drive is mounted.
 *
 * Return value: %TRUE if the media is mounted, %FALSE otherwise.
 *
 * Since: 2.14
 **/
gboolean
nautilus_burn_drive_is_mounted (NautilusBurnDrive *drive)
{
      gboolean is_mounted;

      g_return_val_if_fail (drive != NULL, FALSE);

      is_mounted = FALSE;
#ifdef USE_HAL
      is_mounted = nautilus_burn_drive_hal_is_mounted (drive);
#else
      is_mounted = nautilus_burn_drive_nohal_is_mounted (drive);
#endif

      return is_mounted;
}

/**
 * nautilus_burn_drive_eject:
 * @drive: #NautilusBurnDrive
 *
 * Eject media from a #NautilusBurnDrive.
 *
 * Return value: %TRUE if the media was sucessfully ejected, %FALSE otherwise.
 *
 * Since: 2.12
 **/
gboolean
nautilus_burn_drive_eject (NautilusBurnDrive *drive)
{
      char    *cmd;
      gboolean res;

      g_return_val_if_fail (drive != NULL, FALSE);

      if (drive->device == NULL) {
            return FALSE;
      }
      
#ifdef USE_GNOME_MOUNT
      cmd = g_strdup_printf ("gnome-mount --eject --no-ui --device=%s", drive->device);
#else
      cmd = g_strdup_printf ("eject %s", drive->device);
#endif

      res = g_spawn_command_line_sync (cmd, NULL, NULL, NULL, NULL);
      g_free (cmd);

      /* delay a bit to make sure eject finishes */
      sleep (2);

      return res;
}

#ifdef USE_HAL

static gboolean
hal_udi_is_our_drive (LibHalContext     *ctx,
                      const char        *udi,
                  NautilusBurnDrive *drive)
{
      char    *device = NULL;
      gboolean res    = FALSE;

      if (drive == NULL || drive->device == NULL )
            return res;

      if (! libhal_device_property_exists (ctx, udi, "block.device", NULL))
            return res;

      device = libhal_device_get_property_string (ctx, udi, "block.device", NULL);
      if (device && strcmp (device, drive->device) == 0) {
            res = TRUE;
      }

      if (device)
            libhal_free_string (device);

      return res;
}

static void
hal_device_added (LibHalContext *ctx,
                  const char    *udi)
{
      NautilusBurnDrive *drive;

      drive = libhal_ctx_get_user_data (ctx);

      g_return_if_fail (drive != NULL);
      g_return_if_fail (udi != NULL);

      if (hal_udi_is_our_drive (ctx, udi, drive)) {
            g_free (drive->priv->monitor_udi);
            drive->priv->monitor_udi = g_strdup (udi);

            g_signal_emit (drive, nautilus_burn_drive_table_signals [MEDIA_ADDED], 0);
      }
}

static void
hal_device_removed (LibHalContext *ctx,
                    const char    *udi)
{
      NautilusBurnDrive *drive;

      drive = libhal_ctx_get_user_data (ctx);

      g_return_if_fail (drive != NULL);
      g_return_if_fail (udi != NULL);

      if (drive->priv->monitor_udi
          && strcmp (drive->priv->monitor_udi, udi) == 0) {
            g_free (drive->priv->monitor_udi);
            drive->priv->monitor_udi = NULL;

            g_signal_emit (drive, nautilus_burn_drive_table_signals [MEDIA_REMOVED], 0);
      }
}

static dbus_bool_t 
hal_mainloop_integration (LibHalContext *ctx,
                    DBusError     *error)
{
      DBusConnection *dbus_connection;

      dbus_connection = dbus_bus_get (DBUS_BUS_SYSTEM, error);

      if (dbus_error_is_set (error))
            return FALSE;
      
      dbus_connection_setup_with_g_main (dbus_connection, NULL);

      libhal_ctx_set_dbus_connection (ctx, dbus_connection);
      
      return TRUE;
}

static void
drive_hal_shutdown (NautilusBurnDrive *drive)
{
      DBusError error;

      dbus_error_init (&error);
      if (! libhal_ctx_shutdown (drive->priv->ctx, &error)) {
            g_warning ("hal_shutdown failed: %s\n", error.message);
            dbus_error_free (&error);
            return;
      }

      if (! libhal_ctx_free (drive->priv->ctx)) {
            g_warning ("hal_shutdown failed - unable to free hal context\n");
      }
}

static void
drive_hal_init (NautilusBurnDrive *drive)
{
      LibHalContext *ctx;
      DBusError      error;
      
      if (! (ctx = libhal_ctx_new ())) {
            g_warning ("failed to initialize HAL!");
            return;
      }
      
      dbus_error_init (&error);
      if (! hal_mainloop_integration (ctx, &error)) {
            g_warning ("hal_initialize failed: %s", error.message);
            dbus_error_free (&error);
            return;
      }
      
      if (! libhal_ctx_init (ctx, &error)) {
            g_warning ("hal_initialize failed: %s", error.message);
            dbus_error_free (&error);
            libhal_ctx_free (ctx);
            return;
      }

      drive->priv->ctx = ctx;
}

static int *
hal_parse_write_speeds (char **strlist)
{
      char *end;
      int  *write_speeds;
      int   fields = 1;
      int   i;

      if (strlist == NULL)
            return NULL;

      fields = libhal_string_array_length (strlist);

      if (fields == 0)
            return NULL;

      write_speeds = g_new0 (int, fields + 1);

      for (i = 0; i < fields; i++) {
            write_speeds[i] = strtol (strlist[i], &end, 10);

            if (write_speeds[i] < 0
                || write_speeds[i] > 65535
                || *end != '\0') {

                  g_free (write_speeds);
                  return NULL;
            }

            write_speeds[i] /= CD_ROM_SPEED;
      }

      return write_speeds;
}

#define LIBHAL_PROP_EXTRACT_BEGIN if (FALSE)
#define LIBHAL_PROP_EXTRACT_END ;
#define LIBHAL_PROP_EXTRACT_INT(_property_, _where_) else if (strcmp (key, _property_) == 0 && type == LIBHAL_PROPERTY_TYPE_INT32) _where_ = libhal_psi_get_int (&it)
#define LIBHAL_PROP_EXTRACT_INT_DIV(_property_, _where_, _div_) else if (strcmp (key, _property_) == 0 && type == LIBHAL_PROPERTY_TYPE_INT32) _where_ = libhal_psi_get_int (&it) / _div_
#define LIBHAL_PROP_EXTRACT_STRING(_property_, _where_) else if (strcmp (key, _property_) == 0 && type == LIBHAL_PROPERTY_TYPE_STRING) _where_ = (libhal_psi_get_string (&it) != NULL && strlen (libhal_psi_get_string (&it)) > 0) ? strdup (libhal_psi_get_string (&it)) : NULL
#define LIBHAL_PROP_EXTRACT_BOOL(_property_, _where_) else if (strcmp (key, _property_) == 0 && type == LIBHAL_PROPERTY_TYPE_BOOLEAN) _where_ = libhal_psi_get_bool (&it)
#define LIBHAL_PROP_EXTRACT_BOOL_BITFIELD(_property_, _where_, _field_) else if (strcmp (key, _property_) == 0 && type == LIBHAL_PROPERTY_TYPE_BOOLEAN) _where_ |= libhal_psi_get_bool (&it) ? _field_ : 0
#define LIBHAL_PROP_EXTRACT_STRLIST(_property_, _where_) else if (strcmp (key, _property_) == 0 && type == LIBHAL_PROPERTY_TYPE_STRLIST) _where_ = libhal_psi_get_strlist (&it)

static NautilusBurnDrive *
hal_drive_from_udi (LibHalContext *ctx,
                const char    *udi)
{
      LibHalPropertySet        *pset;
      LibHalPropertySetIterator it;
      DBusError                 error;
      NautilusBurnDrive        *drive;
      char                    **write_speeds = NULL;

      LIBHAL_CHECK_LIBHALCONTEXT (ctx, FALSE);

      dbus_error_init (&error);

      if ((pset = libhal_device_get_all_properties (ctx, udi, &error)) == NULL) {
            if (dbus_error_is_set (&error)) {
                  g_warning ("Could not get all properties: %s", error.message);
                  dbus_error_free (&error);
            }

            return NULL;
      }

      drive = nautilus_burn_drive_new ();
      drive->type = NAUTILUS_BURN_DRIVE_TYPE_CD_DRIVE;

      for (libhal_psi_init (&it, pset); libhal_psi_has_more (&it); libhal_psi_next (&it)) {
            int   type;
            char *key;

            type = libhal_psi_get_type (&it);
            key = libhal_psi_get_key (&it);

            LIBHAL_PROP_EXTRACT_BEGIN;

            LIBHAL_PROP_EXTRACT_STRING  ("block.device",               drive->device);
            LIBHAL_PROP_EXTRACT_STRING  ("storage.model",              drive->display_name);

            LIBHAL_PROP_EXTRACT_INT_DIV ("storage.cdrom.read_speed",   drive->max_speed_read,  CD_ROM_SPEED);
            LIBHAL_PROP_EXTRACT_INT_DIV ("storage.cdrom.write_speed",  drive->max_speed_write, CD_ROM_SPEED);
            LIBHAL_PROP_EXTRACT_STRLIST ("storage.cdrom.write_speeds", write_speeds);

            LIBHAL_PROP_EXTRACT_BOOL_BITFIELD ("storage.cdrom.cdr",        drive->type, NAUTILUS_BURN_DRIVE_TYPE_CD_RECORDER);
            LIBHAL_PROP_EXTRACT_BOOL_BITFIELD ("storage.cdrom.cdrw",       drive->type, NAUTILUS_BURN_DRIVE_TYPE_CDRW_RECORDER);
            LIBHAL_PROP_EXTRACT_BOOL_BITFIELD ("storage.cdrom.dvd",        drive->type, NAUTILUS_BURN_DRIVE_TYPE_DVD_DRIVE);
            LIBHAL_PROP_EXTRACT_BOOL_BITFIELD ("storage.cdrom.dvdplusr",   drive->type, NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_R_RECORDER);
            LIBHAL_PROP_EXTRACT_BOOL_BITFIELD ("storage.cdrom.dvdplusrw",  drive->type, NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_RW_RECORDER);
            LIBHAL_PROP_EXTRACT_BOOL_BITFIELD ("storage.cdrom.dvdplusrdl", drive->type, NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_R_DL_RECORDER);

            LIBHAL_PROP_EXTRACT_BOOL_BITFIELD ("storage.cdrom.dvdr",       drive->type, NAUTILUS_BURN_DRIVE_TYPE_DVD_RW_RECORDER);
            LIBHAL_PROP_EXTRACT_BOOL_BITFIELD ("storage.cdrom.dvdrw",      drive->type, NAUTILUS_BURN_DRIVE_TYPE_DVD_RW_RECORDER);
            LIBHAL_PROP_EXTRACT_BOOL_BITFIELD ("storage.cdrom.dvdram",     drive->type, NAUTILUS_BURN_DRIVE_TYPE_DVD_RAM_RECORDER);

            LIBHAL_PROP_EXTRACT_END;
      }
      drive->priv->udi = g_strdup (udi);
      drive->cdrecord_id = g_strdup (drive->device);
      drive->priv->write_speeds = hal_parse_write_speeds (write_speeds);
      /* we do not own write_speeds so do not free it. */
      if (drive->priv->write_speeds == NULL) {
            fill_write_speeds (drive);
      }

      if (! drive->display_name) {
            drive->display_name = g_strdup_printf ("Unnamed Drive (%s)", drive->device);
      }

      libhal_free_property_set (pset);

      return drive;
}

static GList *
hal_scan (gboolean recorder_only)
{
      GList         *drives = NULL;
      int            i;
      int            num_devices;
      LibHalContext *ctx;
      char         **device_names;

      ctx = get_hal_context ();

      if (ctx == NULL) {
            return NULL;
      }

      device_names = libhal_find_device_by_capability (ctx,
                                             "storage.cdrom", 
                                           &num_devices,
                                           NULL);

      if (device_names == NULL)
            return NULL;

      for (i = 0; i < num_devices; i++) {
            NautilusBurnDrive *drive;

            drive = hal_drive_from_udi (ctx, device_names [i]);

            add_whitelist (drive);

            if (recorder_only && !(drive->type & NAUTILUS_BURN_DRIVE_TYPE_CD_RECORDER)) {
                  nautilus_burn_drive_unref (drive);
            } else {
                  drives = g_list_prepend (drives, drive);
            }
      }

      libhal_free_string_array (device_names);

      drives = g_list_reverse (drives);

      return drives;
}
#endif /* USE_HAL */

#if defined (__linux__)

static char **
read_lines (char *filename)
{
      char      *contents;
      gsize      len;
      char      *p, *n;
      GPtrArray *array;
      
      if (g_file_get_contents (filename,
                         &contents,
                         &len, NULL)) {
            
            array = g_ptr_array_new ();
            
            p = contents;
            while ((n = memchr (p, '\n', len - (p - contents))) != NULL) {
                  *n = 0;
                  g_ptr_array_add (array, g_strdup (p));
                  p = n + 1;
            }
            if ((gsize)(p - contents) < len) {
                  g_ptr_array_add (array, g_strndup (p, len - (p - contents)));
            }

            g_ptr_array_add (array, NULL);
            
            g_free (contents);
            return (char **)g_ptr_array_free (array, FALSE);
      }
      return NULL;
}

struct scsi_unit {
      char *vendor;
      char *model;
      char *rev;
      int   bus;
      int   id;
      int   lun;
      int   type;
};

struct drive_unit {
      NautilusBurnDriveProtocolType protocol;
      char                         *device;
      char                         *display_name;
      int                           speed;
      gboolean                      can_write_cdr;
      gboolean                      can_write_cdrw;
      gboolean                      can_write_dvdr;
      gboolean                      can_write_dvdram;
      gboolean                      can_read_dvd;
};

static char *drive_get_name (struct drive_unit *drive, struct scsi_unit *scsi_units, int n_scsi_units);

static void
linux_add_whitelist (struct drive_unit *drive_s,
                 struct scsi_unit  *scsi_units,
                 int                n_scsi_units)
{
      guint i;

      for (i = 0; i < G_N_ELEMENTS (recorder_whitelist); i++) {
            if (drive_s->display_name == NULL) {
                  continue;
            }

            if (!strcmp (drive_s->display_name, recorder_whitelist [i].name)) {
                  drive_s->can_write_cdr =
                        recorder_whitelist [i].can_write_cdr;
                  drive_s->can_write_cdrw =
                        recorder_whitelist [i].can_write_cdrw;
                  drive_s->can_write_dvdr =
                        recorder_whitelist [i].can_write_dvdr;
                  drive_s->can_write_dvdram =
                        recorder_whitelist [i].can_write_dvdram;
            }
      }
}

static void
get_scsi_units (char            **device_str,
            char            **devices,
            struct scsi_unit *scsi_units)
{
      char vendor [9], model [17], rev [5];
      int  host_no, access_count, queue_depth, device_busy, online, channel;
      int  scsi_id, scsi_lun, scsi_type;
      int  i, j;

      j = 0;

      for (i = 0; device_str [i] != NULL && devices[i] != NULL; i++) {
            if (strcmp (device_str [i], "<no active device>") == 0) {
                  continue;
            }
            if (sscanf (devices [i], "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d",
                      &host_no,
                      &channel, &scsi_id, &scsi_lun, &scsi_type, &access_count, &queue_depth,
                      &device_busy, &online) != 9) {
            
                  g_warning ("Couldn't match line in /proc/scsi/sg/devices\n");
                  continue;
            }
            if (scsi_type == 5) { /* TYPE_ROM (include/scsi/scsi.h) */
                  if (sscanf (device_str [i], "%8c\t%16c\t%4c", 
                            vendor, model, rev) != 3) {
                        g_warning ("Couldn't match line /proc/scsi/sg/device_strs\n");
                        continue;
                  }
                  vendor [8] = '\0'; model [16] = '\0'; rev [4] = '\0';

                  scsi_units [j].vendor = g_strdup (g_strstrip (vendor));
                  scsi_units [j].model = g_strdup (g_strstrip (model));
                  scsi_units [j].rev = g_strdup (g_strstrip (rev));
                  scsi_units [j].bus = host_no;
                  scsi_units [j].id = scsi_id;
                  scsi_units [j].lun = scsi_lun; 
                  scsi_units [j].type = scsi_type;
                  
                  j++;
            }
      }
}

static int
count_strings (char *p)
{
      int n_strings;

      n_strings = 0;
      while (*p != 0) {
            n_strings++;
            while (*p != '\t' && *p != 0) {
                  p++;
            }
            if (*p == '\t') {
                  p++;
            }
      }
      return n_strings;
}

static int
get_cd_scsi_id (const char *dev,
            int        *bus,
            int        *id,
            int        *lun)
{
      int   fd;
      char *devfile;
      struct {
            long mux4;
            long hostUniqueId;
      } m_idlun;
      
      devfile = g_strdup_printf ("/dev/%s", dev);
      fd = g_open (devfile, O_RDWR | O_NONBLOCK, 0);
      if (fd < 0)
            fd = g_open (devfile, O_RDONLY | O_NONBLOCK, 0);

      g_free (devfile);

      /* Avoid problems with Valgrind */
      memset (&m_idlun, 0, sizeof (m_idlun));
      *bus = *id = *lun = -1;

      if (fd < 0) {
            g_warning ("Failed to open cd device %s\n", dev);
            return 0;
      }

      if (ioctl (fd, SCSI_IOCTL_GET_BUS_NUMBER, bus) < 0 || *bus < 0) {
            g_warning ("Failed to get scsi bus nr\n");
            close (fd);
            return 0;
      }

      if (ioctl (fd, SCSI_IOCTL_GET_IDLUN, &m_idlun) < 0) {
            g_warning ("Failed to get scsi id and lun\n");
            close(fd);
            return 0;
      }
      *id = m_idlun.mux4 & 0xFF;
      *lun = (m_idlun.mux4 >> 8)  & 0xFF;

      close(fd);
      return 1;
}

static struct scsi_unit *
lookup_scsi_unit (int               bus,
              int               id,
              int               lun,
              struct scsi_unit *scsi_units,
              int               n_scsi_units)
{
      int i;

      for (i = 0; i < n_scsi_units; i++) {
            if (scsi_units [i].bus == bus &&
                scsi_units [i].id == id &&
                scsi_units [i].lun == lun) {
                  return &scsi_units [i];
            }
      }
      return NULL;
}

static char *
get_scsi_cd_name (int               bus,
              int               id,
              int               lun,
              const char       *dev,
              struct scsi_unit *scsi_units,
              int               n_scsi_units)
{
      struct scsi_unit *scsi_unit;

      scsi_unit = lookup_scsi_unit (bus, id, lun, scsi_units, n_scsi_units);
      if (scsi_unit == NULL) {
            return g_strdup_printf (_("Unnamed SCSI Drive (%s)"), dev);
      }

      return g_strdup_printf ("%s - %s",
                        scsi_unit->vendor,
                        scsi_unit->model);
}

static char *
drive_get_name (struct drive_unit *drive,
            struct scsi_unit  *scsi_units,
            int                n_scsi_units)
{
      char *filename, *line, *retval;
      char  stdname [4], devfsname [15];
      int   bus, id, lun, i;

      g_return_val_if_fail (drive != NULL, FALSE);

      /* clean up the string again if we have devfs */
      i = sscanf (drive->device, "%4s %14s", stdname, devfsname);
      if (i < 1) { /* should never happen */
            g_warning ("drive_get_name: drive->device string broken!");
            return NULL;
      }
      if (i == 2) {
            g_free (drive->device);
            drive->device = g_strdup (devfsname);
      }
      stdname [3] = '\0'; devfsname [14] = '\0'; /* just in case */
      
      if (drive->protocol == NAUTILUS_BURN_DRIVE_PROTOCOL_SCSI) {
            get_cd_scsi_id (drive->device, &bus, &id, &lun);
            retval = get_scsi_cd_name (bus, id, lun, drive->device, scsi_units,
                                 n_scsi_units);
      } else {
            filename = g_strdup_printf ("/proc/ide/%s/model", stdname);
            if (!g_file_get_contents (filename, &line, NULL, NULL) ||
                line == NULL) {
                  g_free (filename);
                  return NULL;
            }
            g_free (filename);

            i = strlen (line);
            if (line [i-1] != '\n') {
                  retval = g_strdup (line);
            } else {
                  retval = g_strndup (line, i - 1);
            }

            g_free (line);
      }

      return retval;
}

static GList *
add_linux_cd_recorder (GList             *drives,
                   gboolean           recorder_only,
                   struct drive_unit *drive_s,
                   struct scsi_unit  *scsi_units,
                   int                n_scsi_units)
{
      int                bus, id, lun;
      NautilusBurnDrive *drive;

      drive = nautilus_burn_drive_new ();

      drive->type = NAUTILUS_BURN_DRIVE_TYPE_CD_DRIVE;
      drive->display_name = g_strdup (drive_s->display_name);

      if (drive_s->protocol == NAUTILUS_BURN_DRIVE_PROTOCOL_SCSI) {
            drive->priv->protocol = NAUTILUS_BURN_DRIVE_PROTOCOL_SCSI;
            if (!get_cd_scsi_id (drive_s->device, &bus, &id, &lun)) {
                  g_free (drive->display_name);
                  nautilus_burn_drive_unref (drive);
                  return drives;
            }
            drive->cdrecord_id = g_strdup_printf ("%d,%d,%d",
                                          bus, id, lun);
      } else {
            drive->priv->protocol = NAUTILUS_BURN_DRIVE_PROTOCOL_IDE;
            /* kernel >=2.5 can write cd w/o ide-scsi */
            drive->cdrecord_id = g_strdup_printf ("/dev/%s",
                                          drive_s->device);
      }

      if (recorder_only) {
            drive->max_speed_write = get_device_max_write_speed
                  (drive->device);
            if (drive->max_speed_write == -1) {
                  drive->max_speed_write = drive_s->speed;
            }
      } else {
            /* Have a wild guess, the drive should actually correct us */
            drive->max_speed_write = drive_s->speed;
      }
      fill_write_speeds (drive);

      drive->device = g_strdup_printf ("/dev/%s", drive_s->device);
      drive->max_speed_read = drive_s->speed;
      if (drive_s->can_write_dvdr) {
            drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_RW_RECORDER;
      }

      if (drive_s->can_write_dvdram) {
            drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_RAM_RECORDER;
      }

      if (drive_s->can_write_cdr) {
            drive->type |= NAUTILUS_BURN_DRIVE_TYPE_CD_RECORDER;
      }
      if (drive_s->can_write_cdrw) {
            drive->type |= NAUTILUS_BURN_DRIVE_TYPE_CDRW_RECORDER;
      }
      if (drive_s->can_read_dvd) {
            drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_DRIVE;
            add_dvd_plus (drive);
      }

      return g_list_append (drives, drive);
}

static GList *
add_linux_cd_drive (GList             *drives,
                struct drive_unit *drive_s,
                struct scsi_unit  *scsi_units,
                int                n_scsi_units)
{
      NautilusBurnDrive *drive;

      drive = nautilus_burn_drive_new ();
      drive->type = NAUTILUS_BURN_DRIVE_TYPE_CD_DRIVE;
      drive->cdrecord_id = NULL;
      drive->display_name = g_strdup (drive_s->display_name);
      drive->device = g_strdup_printf ("/dev/%s", drive_s->device);
      drive->max_speed_write = 0; /* Can't write */
      fill_write_speeds (drive);
      drive->max_speed_read = drive_s->speed;
      if (drive_s->can_read_dvd) {
            drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_DRIVE;
      }

      return g_list_append (drives, drive);
}

static char *
get_cd_device_file (const char *str)
{
      char *devname;
      
      if (str [0] == 's') {
            devname = g_strdup_printf ("/dev/scd%c", str [2]);
            if (g_file_test (devname, G_FILE_TEST_EXISTS)) {
                  g_free (devname);
                  return g_strdup_printf ("scd%c", str [2]);
            }
            g_free (devname);
      }
      return      g_strdup (str);
}

static GList *
linux_scan (gboolean recorder_only)
{
      char             **device_str, **devices;
      char             **drive_info;
      struct scsi_unit  *scsi_units;
      struct drive_unit *drives;
      char              *p, *t;
      int                n_drives, maj, min, i, j;
      int                n_scsi_units;
      int                fd;
      FILE              *file;
      GList             *drives_list;
      gboolean           have_devfs;

      /* devfs creates and populates the /dev/cdroms directory when its mounted
       * the 'old style names' are matched with devfs names below.
       * The cdroms.device string gets cleaned up again in drive_get_name()
       * we need the oldstyle name to get device->display_name for ide.
       */
      have_devfs = FALSE;
      if (g_file_test ("/dev/.devfsd", G_FILE_TEST_EXISTS)) {
            have_devfs = TRUE;
      }
      
      drive_info = read_lines ("/proc/sys/dev/cdrom/info");
      if (drive_info == NULL || drive_info [0] == NULL || drive_info [1] == NULL) {
            g_warning ("Couldn't read /proc/sys/dev/cdrom/info");
            return NULL;
      }
      if (!g_str_has_prefix (drive_info [2], "drive name:\t")) {
            return NULL;
      }
      p = drive_info [2] + strlen ("drive name:\t");
      while (*p == '\t') {
            p++;
      }
      n_drives = count_strings (p);
      drives = g_new0 (struct drive_unit, n_drives);

      for (j = 0; j < n_drives; j++) {
            t = strchr (p, '\t');
            if (t != NULL) {
                  *t = 0;
            }
            drives [j].device = get_cd_device_file (p);
            /* Assume its an IDE device for now */
            drives [j].protocol = NAUTILUS_BURN_DRIVE_PROTOCOL_IDE;
            if (t != NULL) {
                  p = t + 1;
            }
      }

      /* we only have to check the first char, since only ide or scsi   
       * devices are listed in /proc/sys/dev/cdrom/info. It will always
       * be 'h' or 's'
       */
      n_scsi_units = 0;
      for (i = 0; i < n_drives; i++) {
            if (drives [i].device[0] == 's') {
                  drives [i].protocol = NAUTILUS_BURN_DRIVE_PROTOCOL_SCSI;
                  n_scsi_units++;
            }
      }

      if (n_scsi_units > 0) {
            /* open /dev/sg0 to force loading of the sg module if not loaded yet */
            fd = g_open ("/dev/sg0", O_RDWR, 0);
            if (fd >= 0) {
                  close (fd);
            }
            
            devices = read_lines ("/proc/scsi/sg/devices");
            device_str = read_lines ("/proc/scsi/sg/device_strs");
            if (device_str == NULL) {
                  g_warning ("Can't read /proc/scsi/sg/device_strs");
                  g_strfreev (devices);
                  return NULL;
            }

            scsi_units = g_new0 (struct scsi_unit, n_scsi_units);
            get_scsi_units (device_str, devices, scsi_units);

            g_strfreev (device_str);
            g_strfreev (devices);
      } else {
            scsi_units = NULL;
      }

      for (i = 3; drive_info [i] != NULL; i++) {
            if (g_str_has_prefix (drive_info [i], "Can write CD-R:")) {
                  p = drive_info [i] + strlen ("Can write CD-R:");
                  while (*p == '\t') {
                        p++;
                  }
                  for (j = 0; j < n_drives; j++) {
                        drives [j].can_write_cdr = *p++ == '1';

                        /* Skip tab */
                        p++;
                  }
            }
            if (g_str_has_prefix (drive_info [i], "Can write CD-RW:")) {
                  p = drive_info [i] + strlen ("Can write CD-RW:");
                  while (*p == '\t') {
                        p++;
                  }
                  for (j = 0; j < n_drives; j++) {
                        drives [j].can_write_cdrw = *p++ == '1';

                        /* Skip tab */
                        p++;
                  }
            }
            if (g_str_has_prefix (drive_info [i], "Can write DVD-R:")) {
                  p = drive_info [i] + strlen ("Can write DVD-R:");
                  while (*p == '\t') {
                        p++;
                  }
                  for (j = 0; j < n_drives; j++) {
                        drives [j].can_write_dvdr = *p++ == '1';

                        /* Skip tab */
                        p++;
                  }
            }
            if (g_str_has_prefix (drive_info [i], "Can write DVD-RAM:")) {
                  p = drive_info [i] + strlen ("Can write DVD-RAM:");
                  while (*p == '\t') {
                        p++;
                  }
                  for (j = 0; j < n_drives; j++) {
                        drives [j].can_write_dvdram = *p++ == '1';

                        /* Skip tab */
                        p++;
                  }
            }
            if (g_str_has_prefix (drive_info [i], "Can read DVD:")) {
                  p = drive_info [i] + strlen ("Can read DVD:");
                  while (*p == '\t') {
                        p++;
                  }
                  for (j = 0; j < n_drives; j++) {
                        drives [j].can_read_dvd = *p++ == '1';

                        /* Skip tab */
                        p++;
                  }
            }
            if (g_str_has_prefix (drive_info [i], "drive speed:")) {
                  p = drive_info [i] + strlen ("drive speed:");
                  while (*p == '\t') {
                        p++;
                  }
                  for (j = 0; j < n_drives; j++) {
                        drives [j].speed = atoi (p);

                        /* Skip tab */
                        p++;
                  }
            }
      }
      g_strfreev (drive_info);

      /* get kernel major.minor version */
      file = fopen("/proc/sys/kernel/osrelease", "r");
      if (file == NULL || fscanf(file, "%d.%d", &maj, &min) != 2) {
            g_warning("Could not get kernel version.");
            maj = min = 0;
      }
      fclose(file);

      drives_list = NULL;
      for (i = n_drives - 1, j = 0; i >= 0; i--, j++) {
            if (have_devfs) {
                  char *s;
                  s = g_strdup_printf("%s cdroms/cdrom%d",
                                  drives [i].device,  j);
                  g_free (drives [i].device);
                  drives [i].device = s;
            }
            drives [i].display_name = drive_get_name (&drives [i],
                                            scsi_units, n_scsi_units);
            linux_add_whitelist (&drives [i], scsi_units, n_scsi_units);

            if ((drives [i].can_write_cdr ||
                 drives [i].can_write_cdrw ||
                 drives [i].can_write_dvdr ||
                 drives [i].can_write_dvdram) &&
                (drives [i].protocol == NAUTILUS_BURN_DRIVE_PROTOCOL_SCSI ||
                 (maj > 2) || (maj == 2 && min >= 5))) {
                  drives_list = add_linux_cd_recorder (drives_list,
                                               recorder_only, &drives [i],
                                               scsi_units, n_scsi_units);
            } else if (!recorder_only) {
                  drives_list = add_linux_cd_drive (drives_list,
                                            &drives [i], scsi_units, n_scsi_units);
            }
      }

      for (i = n_drives - 1; i >= 0; i--) {
            g_free (drives [i].display_name);
            g_free (drives [i].device);
      }
      g_free (drives);

      for (i = n_scsi_units - 1; i >= 0; i--) {
            g_free (scsi_units [i].vendor);
            g_free (scsi_units [i].model);
            g_free (scsi_units [i].rev);
      }
      g_free (scsi_units);

      return drives_list;
}

#elif defined (__FreeBSD__)

static GList *
freebsd_scan (gboolean recorder_only)
{
      GList      *drives_list = NULL;
      const char *dev_type = "cd";
      int         speed = 16; /* XXX Hardcode the write speed for now. */
      int         i = 0;
      int         cnode = 1; /* Use the CD device's 'c' node. */

      while (1) {
            NautilusBurnDrive *drive;
            char              *cam_path;
            struct cam_device *cam_dev;

            cam_path = g_strdup_printf ("/dev/%s%dc", dev_type, i);

            if (!g_file_test (cam_path, G_FILE_TEST_EXISTS)) {
                  g_free (cam_path);
                  cam_path = g_strdup_printf ("/dev/%s%d", dev_type, i);
                  cnode = 0;
                  if (!g_file_test (cam_path, G_FILE_TEST_EXISTS)) {
                        g_free (cam_path);
                        break;
                  }
            }

            if ((cam_dev = cam_open_spec_device (dev_type, i, O_RDWR, NULL)) == NULL) {
                  i++;
                  g_free (cam_path);
                  continue;
            }

            drive = nautilus_burn_drive_new ();
            drive->display_name = g_strdup_printf ("%s %s", cam_dev->inq_data.vendor, cam_dev->inq_data.revision);
            drive->device = g_strdup (cam_path);
            drive->cdrecord_id = g_strdup_printf ("%d,%d,%d", cam_dev->path_id, cam_dev->target_id, cam_dev->target_lun);
            /* Attempt to get more specific information from
             * this drive by using cdrecord.
             */
            get_cd_properties (drive->device, drive->cdrecord_id,
                           &(drive->max_speed_read),
                           &(drive->max_speed_write),
                           &(drive->type));
            if (drive->type & NAUTILUS_BURN_DRIVE_TYPE_CD_RECORDER
                || drive->type & NAUTILUS_BURN_DRIVE_TYPE_CDRW_RECORDER
                || drive->type & NAUTILUS_BURN_DRIVE_TYPE_DVD_RAM_RECORDER
                || drive->type & NAUTILUS_BURN_DRIVE_TYPE_DVD_RW_RECORDER
                || !recorder_only) {

                  if (drive->max_speed_read == -1) {
                        drive->max_speed_read = speed;
                  }
                  if (drive->max_speed_write == -1) {
                        drive->max_speed_write = speed;
                  }
                  fill_write_speeds (drive);

                  if (drive->type & NAUTILUS_BURN_DRIVE_TYPE_DVD_DRIVE) {
                        add_dvd_plus (drive);
                  }

                  drives_list = g_list_append (drives_list, drive);
            } else {
                  nautilus_burn_drive_unref (drive);
            }

            g_free (cam_path);
            free (cam_dev);

            i++;
      }

      return drives_list;
}

#else

static char *
cdrecord_scan_get_stdout (void)
{
      int         max_speed, i;
      const char *argv [20]; /* Shouldn't need more than 20 arguments */
      char       *stdout_data;

      max_speed = -1;

      i = 0;
      argv [i++] = "cdrecord";
      argv [i++] = "-scanbus";
      argv [i++] = NULL;

      if (g_spawn_sync (NULL,
                    (char **)argv,
                    NULL,
                    G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL,
                    NULL, NULL,
                    &stdout_data,
                    NULL,
                    NULL,
                    NULL)) {
            return stdout_data;
      }

      return NULL;
}

#define DEFAULT_SPEED 2

static GList *
cdrecord_scan (gboolean recorder_only)
{
      GList             *drives_list;
      NautilusBurnDrive *drive;
      char              *stdout_data, **lines, vendor [9], model [17];
      int                i, bus, id, lun, index;

      drives_list = NULL;

      stdout_data = cdrecord_scan_get_stdout ();
      if (stdout_data == NULL) {
            return drives_list;
      }

      lines = g_strsplit (stdout_data, "\n", 0);
      g_free (stdout_data);

      for (i = 0; lines [i] != NULL; i++) {
            if (sscanf (lines [i], "\t%d,%d,%d\t  %d) '%8c' '%16c'",
                      &bus, &id, &lun, &index,
                      vendor, model) != 6) {
                  continue;
            }

            vendor [8] = '\0'; model [16] = '\0';

            drive = nautilus_burn_drive_new ();
            drive->display_name = g_strdup_printf ("%s - %s",
                                           g_strstrip (vendor), g_strstrip (model));
            drive->cdrecord_id = g_strdup_printf ("%d,%d,%d", bus, id, lun);
            /* FIXME we don't have any way to guess the real device
             * from the info we get from CDRecord */
            drive->device = g_strdup_printf ("/dev/pg%d", index);
            get_cd_properties (drive->device, drive->cdrecord_id,
                           &(drive->max_speed_read),
                           &(drive->max_speed_write),
                           &(drive->type));
            add_whitelist (drive);
            if (drive->type & NAUTILUS_BURN_DRIVE_TYPE_CD_RECORDER
                || drive->type & NAUTILUS_BURN_DRIVE_TYPE_CDRW_RECORDER
                || drive->type & NAUTILUS_BURN_DRIVE_TYPE_DVD_RAM_RECORDER
                || drive->type & NAUTILUS_BURN_DRIVE_TYPE_DVD_RW_RECORDER
                || !recorder_only) {

                  if (drive->max_speed_read == -1) {
                        drive->max_speed_read = DEFAULT_SPEED;
                  }
                  if (drive->max_speed_write == -1) {
                        drive->max_speed_write = DEFAULT_SPEED;
                  }
                  fill_write_speeds (drive);

                  if (drive->type & NAUTILUS_BURN_DRIVE_TYPE_DVD_DRIVE) {
                        add_dvd_plus (drive);
                  }

                  drives_list = g_list_append (drives_list, drive);
            } else {
                  nautilus_burn_drive_unref (drive);
            }
      }

      g_strfreev (lines);

      return drives_list;
}

#endif

/**
 * nautilus_burn_drive_get_file_image:
 *
 * Create a new %NAUTILUS_BURN_DRIVE_TYPE_FILE #NautilusBurnDrive.
 *
 * Return value: A new drive.
 **/
NautilusBurnDrive *
nautilus_burn_drive_get_file_image (void)
{
      NautilusBurnDrive *drive;

      drive = nautilus_burn_drive_new ();
      drive->display_name = g_strdup (_("File image"));
      drive->max_speed_read = 0;
      drive->max_speed_write = 0;
      fill_write_speeds (drive);
      drive->type = NAUTILUS_BURN_DRIVE_TYPE_FILE;

      return drive;
}

/* This is used for testing different configurations */
#if 0
static GList*
test_cdroms (void)
{
      GList   *list = NULL;
      CDDrive *drive = g_new0 (CDDrive, 1);

      drive->type = CDDRIVE_TYPE_CD_DRIVE | CDDRIVE_TYPE_DVD_DRIVE;
      drive->display_name = g_strdup ("HL-DT-STDVD-ROM");
      drive->device = g_strdup ("/dev/hdc");
      list = g_list_append (list, drive);
      drive = g_new0 (CDDrive, 1);
      drive->type = CDDRIVE_TYPE_CD_DRIVE | CDDRIVE_TYPE_DVD_DRIVE | CDDRIVE_TYPE_CD_RECORDER;
      drive->display_name = g_strdup ("_NEC DVD_RW ND-2500A");
      drive->device = g_strdup ("/dev/hdd");
      list = g_list_append (list, drive);
      return list;
}
#endif

/**
 * nautilus_burn_drive_get_list:
 * @recorder_only: Include only devices capable of recording
 * @add_image: Include a file image device
 *
 * Find media devices on the system.
 *
 * Return value: List of media drives available.
 **/
GList *
nautilus_burn_drive_get_list (gboolean recorder_only,
                        gboolean add_image)
{
      GList *drives = NULL;

#ifdef USE_HAL
      drives = hal_scan (recorder_only);
#endif

      if (drives == NULL) {
#if defined (__linux__)
            drives = linux_scan (recorder_only);
#elif defined (__FreeBSD__)
            drives = freebsd_scan (recorder_only);
#else
            drives = cdrecord_scan (recorder_only);
#endif
      }

      if (add_image) {
            NautilusBurnDrive *drive;
            drive = nautilus_burn_drive_get_file_image ();
            drives = g_list_append (drives, drive);
      }

      return drives;
}

/**
 * nautilus_burn_drive_unref:
 * @drive: #NautilusBurnDrive
 *
 * Decrement the refcount of @drive.
 *
 * Since: 2.14
 **/
void
nautilus_burn_drive_unref (NautilusBurnDrive *drive)
{
      if (drive == NULL) {
            return;
      }
      
      g_object_unref (drive);
}

/**
 * nautilus_burn_drive_ref:
 * @drive: #NautilusBurnDrive
 *
 * Increment the refcount of @drive.
 *
 * Return value: #NautilusBurnDrive
 *
 * Since: 2.14
 **/
NautilusBurnDrive *
nautilus_burn_drive_ref (NautilusBurnDrive *drive)
{
      if (drive == NULL) {
            return NULL;
      }
      
      g_object_ref (drive);

      return drive;
}

/**
 * nautilus_burn_drive_free:
 * @drive: #NautilusBurnDrive to be freed
 *
 * Free @drive.
 *
 * Deprecated: Use nautilus_burn_drive_unref() instead
 **/
void
nautilus_burn_drive_free (NautilusBurnDrive *drive)
{
      g_warning ("nautilus_burn_drive_free is deprecated please use nautilus_burn_drive_unref() instead");
      nautilus_burn_drive_unref (drive);
}

/**
 * nautilus_burn_drive_lock:
 * @drive: Pointer to a #NautilusBurnDrive
 * @reason:
 * @reason_for_failure:
 *
 * Lock a #NautilusBurnDrive
 *
 * Return value: %TRUE if the drive was sucessfully locked, %FALSE otherwise.
 *
 * Since: 2.8
 **/
gboolean
nautilus_burn_drive_lock (NautilusBurnDrive *drive,
                    const char        *reason,
                    char             **reason_for_failure) 
{
      gboolean res;

      if (reason_for_failure != NULL)
            *reason_for_failure = NULL;

      res = TRUE;
#ifdef USE_HAL
      if (drive->priv->udi != NULL) {
            LibHalContext *ctx;
            char          *dbus_reason;
            DBusError      error;
            
            dbus_error_init (&error);
            ctx = drive->priv->ctx;

            if (ctx != NULL) {
                  res = libhal_device_lock (ctx, 
                                      drive->priv->udi,
                                      reason,
                                      &dbus_reason,
                                      &error);

                  if (dbus_error_is_set (&error))
                        dbus_error_free (&error);
                        
                  if (dbus_reason != NULL && 
                      reason_for_failure != NULL)
                        *reason_for_failure = g_strdup (dbus_reason);
                  if (dbus_reason != NULL)
                        dbus_free (dbus_reason);
            }
      }
#endif
      return res;
}

/**
 * nautilus_burn_drive_unlock:
 * @drive: Pointer to a #NautilusBurnDrive
 *
 * Unlock a #NautilusBurnDrive
 *
 * Return value: %TRUE if the drive was sucessfully unlocked, %FALSE otherwise.
 *
 * Since: 2.8
 **/
gboolean 
nautilus_burn_drive_unlock (NautilusBurnDrive *drive)
{
      gboolean res;

      res = TRUE;
#ifdef USE_HAL
      if (drive->priv->udi != NULL) {
            LibHalContext *ctx;
            DBusError      error;

            dbus_error_init (&error);
            ctx = drive->priv->ctx;

            if (ctx != NULL) {
                  res = libhal_device_unlock (ctx, 
                                        drive->priv->udi,
                                        &error);

                  if (dbus_error_is_set (&error))
                        dbus_error_free (&error);
            }
      }
#endif
      return res;
}

static void
nautilus_burn_drive_finalize (GObject *object)
{
      NautilusBurnDrive *drive = NAUTILUS_BURN_DRIVE (object);

      g_return_if_fail (object != NULL);

#ifdef USE_HAL
      drive_hal_shutdown (drive);
#endif

      if (drive->priv->poll_id > 0)
            g_source_remove (drive->priv->poll_id);

      g_free (drive->priv->udi);
      g_free (drive->priv->monitor_udi);

      g_free (drive->priv->write_speeds);
      g_free (drive->display_name);
      g_free (drive->cdrecord_id);
      g_free (drive->device);

      if (G_OBJECT_CLASS (parent_class)->finalize != NULL) {
            (* G_OBJECT_CLASS (parent_class)->finalize) (object);
      }
}

static void
nautilus_burn_drive_init (NautilusBurnDrive *drive)
{
      drive->priv = NAUTILUS_BURN_DRIVE_GET_PRIVATE (drive);

      drive->type            = 0;
      drive->display_name    = NULL;
      drive->max_speed_write = 0;
      drive->max_speed_read  = 0;
      drive->priv->write_speeds = NULL;
      drive->cdrecord_id     = NULL;
      drive->device          = NULL;

#ifdef USE_HAL
      drive_hal_init (drive);
#endif
}

/**
 * nautilus_burn_drive_new:
 *
 * Create a new #NautilusBurnDrive.
 *
 * Return value: The new drive.
 *
 * Since: 2.8
 **/
NautilusBurnDrive *
nautilus_burn_drive_new (void)
{
      return g_object_new (NAUTILUS_BURN_TYPE_DRIVE, NULL);
}

/**
 * nautilus_burn_drive_new_from_path:
 * @device: Filename of drive device (eg. "/dev/hdb")
 *
 * Create a new #NautilusBurnDrive.
 *
 * Return value: The new drive.
 *
 * Since: 2.12
 **/
NautilusBurnDrive *
nautilus_burn_drive_new_from_path (const char *device)
{
      GList             *drives, *l;
      NautilusBurnDrive *drive;

        drives = nautilus_burn_drive_get_list (FALSE, FALSE);

      drive = NULL;

        for (l = drives; l != NULL; l = l->next) {
            NautilusBurnDrive *d = l->data;
            if (g_str_equal (device, d->device)) {
                  drive = nautilus_burn_drive_ref (d);
            }
      }

      g_list_foreach (drives, (GFunc)nautilus_burn_drive_unref, NULL);
      g_list_free (drives);

      return drive;
}

/**
 * nautilus_burn_drive_copy:
 * @drive: Pointer to the #NautilusBurnDrive to be copied
 *
 * Return vale: Copy of specified #NautilusBurnDrive.
 *
 * Since: 2.8
 *
 * Deprecated: Use nautilus_burn_drive_ref() instead
 *
 **/
NautilusBurnDrive *
nautilus_burn_drive_copy (NautilusBurnDrive *drive)
{
      g_warning ("nautilus_burn_drive_copy is deprecated please use nautilus_burn_drive_ref() instead");

      return nautilus_burn_drive_ref (drive);
}

/**
 * nautilus_burn_drive_equal:
 * @a: First #NautilusBurnDrive struct to compare
 * @b: Second #NautilusBurnDrive struct to compare
 *
 * Compare the two cd drives, return %TRUE if they match exactly
 * the same drive.
 *
 * Returns: %TRUE if the two #NautilusBurnDrives are equal, otherwise return %FALSE.
 *
 * Since: 2.8
 **/
gboolean
nautilus_burn_drive_equal (NautilusBurnDrive *a,
                     NautilusBurnDrive *b)
{
      if (!a || !b)
            return FALSE;

      if ((a->type & NAUTILUS_BURN_DRIVE_TYPE_FILE)
          && (b->type & NAUTILUS_BURN_DRIVE_TYPE_FILE))
            return TRUE;

      if (!a->device || !b->device)
            return FALSE;

      return strcmp (a->device, b->device) == 0;
}

static void
add_desc (GString    *string,
        const char *addition)
{
      if (strcmp (string->str, "") == 0) {
            g_string_append_printf (string, "%s", addition);
      } else {
            g_string_append_printf (string, ", %s", addition);
      }
}

#define NCBD_ADD_TYPE_DESC(_t_, _desc_) if (type & _t_) add_desc (string, _desc_)

/**
 * nautilus_burn_drive_get_supported_media_string:
 * @drive: A #NautilusBurnDrive
 * @writable_only: Set to %TRUE if only writable media should be displayed
 *
 * Get a string description of the supported media types.  The
 * returned string should be freed when no longer needed.
 *
 * Returns: a string description of the supported media types
 *
 * Since: 2.14
 **/
char *
nautilus_burn_drive_get_supported_media_string (NautilusBurnDrive *drive,
                                    gboolean           writable_only)
{
      GString *string;
      int      type;

      type = drive->type;

      string = g_string_new (NULL);

      NCBD_ADD_TYPE_DESC (NAUTILUS_BURN_DRIVE_TYPE_FILE, "File");

      NCBD_ADD_TYPE_DESC (NAUTILUS_BURN_DRIVE_TYPE_CD_RECORDER, "CD-R");
      NCBD_ADD_TYPE_DESC (NAUTILUS_BURN_DRIVE_TYPE_CDRW_RECORDER, "CD-RW");
      NCBD_ADD_TYPE_DESC (NAUTILUS_BURN_DRIVE_TYPE_DVD_RAM_RECORDER, "DVD-RAM");
      NCBD_ADD_TYPE_DESC (NAUTILUS_BURN_DRIVE_TYPE_DVD_RW_RECORDER, "DVD-RW");
      NCBD_ADD_TYPE_DESC (NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_R_RECORDER, "DVD+R");
      NCBD_ADD_TYPE_DESC (NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_R_DL_RECORDER, "DVD+R DL");
      NCBD_ADD_TYPE_DESC (NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_RW_RECORDER, "DVD+RW");

      if (! writable_only) {
            NCBD_ADD_TYPE_DESC (NAUTILUS_BURN_DRIVE_TYPE_CD_DRIVE, "CD");
            NCBD_ADD_TYPE_DESC (NAUTILUS_BURN_DRIVE_TYPE_DVD_DRIVE, "DVD");
      }

      return g_string_free (string, FALSE);
}

/**
 * nautilus_burn_drive_media_type_get_string:
 * @type: A #NautilusBurnMediaType
 *
 * Get a string description of the specified media type.
 *
 * Returns: a string description for the media type.
 *
 * Since: 2.12
 **/
const char *
nautilus_burn_drive_media_type_get_string (NautilusBurnMediaType type)
{
      switch (type) {
      case NAUTILUS_BURN_MEDIA_TYPE_BUSY:
            return _("Could not determine media type because CD drive is busy");
      case NAUTILUS_BURN_MEDIA_TYPE_ERROR:
            return _("Couldn't open media");
      case NAUTILUS_BURN_MEDIA_TYPE_UNKNOWN:
            return _("Unknown Media");
      case NAUTILUS_BURN_MEDIA_TYPE_CD:
            return _("Commercial CD or Audio CD");
      case NAUTILUS_BURN_MEDIA_TYPE_CDR:
            return _("CD-R");
      case NAUTILUS_BURN_MEDIA_TYPE_CDRW:
            return _("CD-RW");
      case NAUTILUS_BURN_MEDIA_TYPE_DVD:
            return _("DVD");
      case NAUTILUS_BURN_MEDIA_TYPE_DVDR:
            return _("DVD-R, or DVD-RAM");
      case NAUTILUS_BURN_MEDIA_TYPE_DVDRW:
            return _("DVD-RW");
      case NAUTILUS_BURN_MEDIA_TYPE_DVD_RAM:
            return _("DVD-RAM");
      case NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_R:
            return _("DVD+R");
      case NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_RW:
            return _("DVD+RW");
      case NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_R_DL:
            return _("DVD+R DL");
      default:
            break;
      }

      return _("Broken media type");
}

/**
 * nautilus_burn_drive_media_type_is_writable
 * @type: A #NautilusBurnMediaType
 * @is_blank: if the media type is blank or not
 *
 * Determine if a media type is writable
 *
 * Returns: %TRUE if the media type can be written to, otherwise return %FALSE.
 *
 * Since: 2.14
 **/
gboolean
nautilus_burn_drive_media_type_is_writable (NautilusBurnMediaType type,
                                  gboolean              is_blank)
{
      gboolean can_write;

      can_write = FALSE;

      switch (type) {
      case NAUTILUS_BURN_MEDIA_TYPE_BUSY:
      case NAUTILUS_BURN_MEDIA_TYPE_ERROR:
      case NAUTILUS_BURN_MEDIA_TYPE_UNKNOWN:
      case NAUTILUS_BURN_MEDIA_TYPE_CD:
      case NAUTILUS_BURN_MEDIA_TYPE_DVD:
      case NAUTILUS_BURN_MEDIA_TYPE_DVD_RAM:
            can_write = FALSE;
            break;
      case NAUTILUS_BURN_MEDIA_TYPE_CDR:
      case NAUTILUS_BURN_MEDIA_TYPE_DVDR:
      case NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_R:
      case NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_R_DL:
            if (is_blank) {
                  can_write = TRUE;
            } else {
                  can_write = FALSE;
            }
            break;
      case NAUTILUS_BURN_MEDIA_TYPE_CDRW:
      case NAUTILUS_BURN_MEDIA_TYPE_DVDRW:
      case NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_RW:
            can_write = TRUE;
            break;
      default:
            g_warning ("Unknown media type: %d", type);
            can_write = FALSE;
            break;
      }

      return can_write;
}

#ifdef USE_HAL
static void
set_monitor_enabled_hal (NautilusBurnDrive *drive,
                   gboolean           enabled)
{

      g_free (drive->priv->monitor_udi);
      drive->priv->monitor_udi = NULL;

      if (enabled) {
            char *child_udi;

            libhal_ctx_set_user_data (drive->priv->ctx, drive);
            libhal_ctx_set_device_added (drive->priv->ctx, hal_device_added);
            libhal_ctx_set_device_removed (drive->priv->ctx, hal_device_removed);

            child_udi = hal_drive_get_first_child_udi (drive);
            if (child_udi != NULL) {
                  drive->priv->monitor_udi = child_udi;
            }
      } else {
            libhal_ctx_set_user_data (drive->priv->ctx, NULL);
            libhal_ctx_set_device_added (drive->priv->ctx, NULL);
            libhal_ctx_set_device_removed (drive->priv->ctx, NULL);
      }

      /* FIXME: check for devices already in the drive and emit signals for them? */
}
#else

static gboolean
poll_tray_opened (NautilusBurnDrive *drive)
{
      gboolean is_open;

      is_open = nautilus_burn_drive_door_is_open (drive);

      /* check for no change */
      if (is_open == drive->priv->monitor_tray_open)
            return TRUE;

      if (is_open) {
            /* Tray is now open */

            /* see if we lost media */
            if (drive->priv->monitor_media_type != NAUTILUS_BURN_MEDIA_TYPE_ERROR) {
                  drive->priv->monitor_media_type = NAUTILUS_BURN_MEDIA_TYPE_ERROR;
                  g_signal_emit (drive, nautilus_burn_drive_table_signals [MEDIA_REMOVED], 0);
            }
      } else {
            NautilusBurnDriveType type;

            /* Tray is now closed */

            /* see if there is now media in the drive */
            type = nautilus_burn_drive_get_media_type (drive);

            if (type != NAUTILUS_BURN_MEDIA_TYPE_ERROR) {
                  drive->priv->monitor_media_type = type;
                  g_signal_emit (drive, nautilus_burn_drive_table_signals [MEDIA_ADDED], 0);
            }
      }

      drive->priv->monitor_tray_open = is_open;

      return TRUE;
}

static void
set_monitor_enabled_poll (NautilusBurnDrive *drive,
                    gboolean           enabled)
{
      if (drive->priv->poll_id > 0)
            g_source_remove (drive->priv->poll_id);

      if (enabled) {
            drive->priv->monitor_media_type = NAUTILUS_BURN_MEDIA_TYPE_ERROR;
            drive->priv->monitor_tray_open = FALSE;
            drive->priv->poll_id = g_timeout_add (2000, (GSourceFunc)poll_tray_opened, drive);
      } else {
            drive->priv->monitor_media_type = NAUTILUS_BURN_MEDIA_TYPE_ERROR;
            drive->priv->monitor_tray_open = FALSE;
            drive->priv->poll_id = 0;
      }
}

#endif /* USE_HAL */

static void
nautilus_burn_drive_set_monitor_enabled (NautilusBurnDrive *drive,
                               gboolean           enabled)
{
      g_return_if_fail (drive != NULL);

        if (drive->priv->monitor_enabled != enabled) {
            drive->priv->monitor_enabled = enabled;

#ifdef USE_HAL
            set_monitor_enabled_hal (drive, enabled);
#else
            set_monitor_enabled_poll (drive, enabled);
#endif
      }
}

static void
nautilus_burn_drive_set_property (GObject      *object,
                          guint                 prop_id,
                          const GValue         *value,
                          GParamSpec           *pspec)
{
      NautilusBurnDrive *self;

      self = NAUTILUS_BURN_DRIVE (object);

      switch (prop_id) {
      case PROP_MONITOR_ENABLED:
            nautilus_burn_drive_set_monitor_enabled (self, g_value_get_boolean (value));
            break;
      default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
            break;
      }
}

static void
nautilus_burn_drive_get_property (GObject      *object,
                          guint                 prop_id,
                          GValue         *value,
                          GParamSpec           *pspec)
{
      NautilusBurnDrive *self;

      self = NAUTILUS_BURN_DRIVE (object);

      switch (prop_id) {
      case PROP_MONITOR_ENABLED:
            g_value_set_boolean (value, self->priv->monitor_enabled);
            break;
      default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
            break;
      }
}

static void
nautilus_burn_drive_class_init (NautilusBurnDriveClass *klass)
{
      GObjectClass *object_class;

      object_class = (GObjectClass *) klass;

      parent_class = g_type_class_peek_parent (klass);

      object_class->finalize = nautilus_burn_drive_finalize;
      object_class->get_property = nautilus_burn_drive_get_property;
      object_class->set_property = nautilus_burn_drive_set_property;

      g_type_class_add_private (klass, sizeof (NautilusBurnDrivePrivate));

      /* Properties */
        g_object_class_install_property (object_class,
                                         PROP_MONITOR_ENABLED,
                                         g_param_spec_boolean ("enable-monitor",
                                                               NULL,
                                                               NULL,
                                                               FALSE,
                                                               G_PARAM_READWRITE));

      /* Signals */
      nautilus_burn_drive_table_signals [MEDIA_ADDED] =
            g_signal_new ("media-added",
                        G_TYPE_FROM_CLASS (klass),
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (NautilusBurnDriveClass,
                                     media_added),
                        NULL, NULL,
                        g_cclosure_marshal_VOID__VOID,
                        G_TYPE_NONE, 0);
      nautilus_burn_drive_table_signals [MEDIA_REMOVED] =
            g_signal_new ("media-removed",
                        G_TYPE_FROM_CLASS (klass),
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (NautilusBurnDriveClass,
                                     media_removed),
                        NULL, NULL,
                        g_cclosure_marshal_VOID__VOID,
                        G_TYPE_NONE, 0);
}

Generated by  Doxygen 1.6.0   Back to index