/* hw_volume_manager.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2009-2023 Ralf Hoffmann.
 * You can contact me at: ralf@boomerangsworld.de
 *   or http://www.boomerangsworld.de/worker
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "hw_volume_manager.hh"
#include "hw_volume.hh"

#ifdef HAVE_HAL_DBUS
#include "hal/libhal-storage.h"
#endif

#include <iostream>
#include <fstream>
#include <sstream>
#include <iterator>
#include <vector>
#include <set>
#include <algorithm>
#include <cctype>

#include "nwc_fsentry.hh"
#include "aguix/util.h"
#include "flagreplacer.hh"
#include "execlass.h"
#include "worker_locale.h"
#include "mounttable_entry.hh"

HWVolumeManager::HWVolumeManager( const std::string &fstab_location,
                                  const std::string &mtab_location ) :
#ifdef HAVE_HAL_DBUS
    m_dbus_conn( NULL ),
    m_hal_ctx( NULL ),
#elif defined(HAVE_DBUS)
    m_dbus_conn( NULL ),
#endif
    m_udisks_available( false ),
    m_udisks2_available( false ),
    m_fstab_table( fstab_location ),
    m_mtab_table( mtab_location ),
    m_mount_command( HW_VM_MOUNT_CMD ),
    m_unmount_command( HW_VM_UNMOUNT_CMD ),
    m_partition_file( HW_VM_PARTS ),
    m_eject_command( HW_VM_EJECT_CMD ),
    m_closetray_command( HW_VM_CLOSETRAY_CMD ),
    m_preferred_udisks_version( 1 )
{
    HALconnectionInit();
}

HWVolumeManager::~HWVolumeManager()
{
    HALconnectionFini();
}

typedef std::pair< mode_t, dev_t > w_osdevice_t;

static w_osdevice_t getOSDevice( const std::string &dev )
{
    NWC::FSEntry fe( dev );

    if ( ! fe.entryExists() ) {
        return std::make_pair( 0, 0 );
    }

    if ( fe.isLink() ) {
        return std::make_pair( fe.stat_dest_mode(),
                               fe.stat_dest_rdev() );
    } else {
        return std::make_pair( fe.stat_mode(),
                               fe.stat_rdev() );
    }
}

std::list<HWVolume> HWVolumeManager::getVolumes()
{
    std::list<HWVolume> l;
#ifdef HAVE_HAL_DBUS
    DBusError error;
    int i;
    int num_volumes;
    char **volumes;
    const char *udi;
#endif

    m_fstab_table.update();
    m_mtab_table.update();
    m_avail_parts = getAvailablePartitions();
    std::set< w_osdevice_t > managed;

    std::list< MountTableEntry > fstab_entries = m_fstab_table.getEntries();
    for ( std::list< MountTableEntry >::const_iterator it1 = fstab_entries.begin();
          it1 != fstab_entries.end();
          it1++ ) {
        if ( isDevice( it1->getDevice() ) ) {
            //TODO check for duplicate entries?
            HWVolume v;
            v.setDevice( it1->getDevice() );
            //TODO check whether supposed mount point is absolute and exists?
            v.setSupposedMountPoint( it1->getMountPoint() );

            update( v );

            managed.insert( getOSDevice( v.getDevice() ) );
            l.push_back( v );
        }
    }

#ifdef HAVE_HAL_DBUS
    if ( HALreconnect() == 0 ) {

        dbus_error_init( &error );
        volumes = libhal_find_device_by_capability( m_hal_ctx, "volume",
                                                    &num_volumes, &error );
        if ( volumes != NULL ) {
            for (i = 0; i < num_volumes; i++) {
                udi = volumes[i];

                bool use_volume = false;

                // first check whether we should ignore the volume
                try {
                    if ( ! getBoolProperty( udi, "volume.ignore" ) ) {
                        use_volume = true;
                    }
                } catch ( int ) {
                }

                // now check whether the volume is actually a mountable filesystem
                if ( use_volume ) {
                    try {
                        if ( getStringProperty( udi, "volume.fsusage" ) != "filesystem" ) {
                            use_volume = false;
                        }
                    } catch ( int ) {
                    }
                }

                if ( use_volume ) {
                    HWVolume v( udi );

                    update( v );

                    if ( managed.count( getOSDevice( v.getDevice() ) ) < 1 ) {
                        managed.insert( getOSDevice( v.getDevice() ) );
                
                        l.push_back( v );
                    }
                }
            }
            libhal_free_string_array( volumes );
        } else {
            if ( dbus_error_is_set( &error ) ) {
                fprintf( stderr, "HAL Find Volumes Error: %s\n", error.message );
                dbus_error_free( &error );
            }
        }
    }
#elif defined(HAVE_DBUS)
    if ( HALreconnect() == 0 && m_udisks_available )  {
        DBusMessage *message, *reply;
        DBusMessageIter reply_iter, sub_iter;
        DBusError dbus_error;

        message = dbus_message_new_method_call( "org.freedesktop.UDisks",
                                                "/org/freedesktop/UDisks",
                                                "org.freedesktop.UDisks",
                                                "EnumerateDevices" );

        if ( message != NULL ) {

            dbus_error_init( &dbus_error );

            reply = dbus_connection_send_with_reply_and_block( m_dbus_conn,
                                                               message,
                                                               -1,
                                                               &dbus_error );

            dbus_message_unref( message );

            if ( dbus_error_is_set( &dbus_error ) ) {
                fprintf( stderr, "Couldn't enumerate udisks devices: %s\n", dbus_error.message );
                dbus_error_free( &dbus_error );
            } else if ( reply == NULL ) {
                fprintf( stderr, "Couldn't enumerate udisks devices: %s\n", dbus_error.message );
            } else {

                dbus_message_iter_init( reply, &reply_iter );

                if ( dbus_message_iter_get_arg_type( &reply_iter ) ==
                     DBUS_TYPE_ARRAY ) {

                    dbus_message_iter_recurse( &reply_iter,
                                               &sub_iter );

                    while ( dbus_message_iter_get_arg_type( &sub_iter ) ==
                            DBUS_TYPE_OBJECT_PATH ) {
                        const char *udi = NULL;

                        dbus_message_iter_get_basic( &sub_iter, &udi );

                        if ( udi ) {
                            bool use_volume = false;

                            // first check whether we should ignore the volume
                            try {
                                if ( ! getBoolProperty( udi, "DeviceIsSystemInternal" ) ) {
                                    use_volume = true;
                                }
                            } catch ( int ) {
                            }

                            // now check whether the volume is actually a mountable filesystem
                            if ( use_volume ) {
                                try {
                                    if ( getStringProperty( udi, "IdUsage" ) != "filesystem" ) {
                                        use_volume = false;
                                    }
                                } catch ( int ) {
                                }

                                // no filesystem but ejectable?
                                if ( ! use_volume ) {
                                    try {
                                        if ( getBoolProperty( udi, "DriveIsMediaEjectable" ) ) {
                                            use_volume = true;
                                        }
                                    } catch ( int ) {
                                    }
                                }
                            }

                            if ( use_volume ) {
                                HWVolume v( udi );

                                update( v );

                                if ( managed.count( getOSDevice( v.getDevice() ) ) < 1 ) {
                                    managed.insert( getOSDevice( v.getDevice() ) );
                
                                    l.push_back( v );
                                }
                            }
                        }

                        dbus_message_iter_next( &sub_iter );
                    }
                }

                dbus_message_unref( reply );
            }
        }
    } else if ( HALreconnect() == 0 && m_udisks2_available)  {
        DBusMessage *message, *reply;
        DBusMessageIter reply_iter, sub_iter;
        DBusError dbus_error;

        message = dbus_message_new_method_call( "org.freedesktop.UDisks2",
                                                "/org/freedesktop/UDisks2",
                                                "org.freedesktop.DBus.ObjectManager",
                                                "GetManagedObjects" );

        if ( message != NULL ) {
            dbus_error_init( &dbus_error );

            reply = dbus_connection_send_with_reply_and_block( m_dbus_conn,
                                                               message,
                                                               -1,
                                                               &dbus_error );

            dbus_message_unref( message );

            if ( dbus_error_is_set( &dbus_error ) ) {
                fprintf( stderr, "Couldn't enumerate udisks devices: %s\n", dbus_error.message );
                dbus_error_free( &dbus_error );
            } else if ( reply == NULL ) {
                fprintf( stderr, "Couldn't enumerate udisks devices: %s\n", dbus_error.message );
            } else {
                dbus_message_iter_init( reply, &reply_iter );

                if ( dbus_message_iter_get_arg_type( &reply_iter ) ==
                     DBUS_TYPE_ARRAY ) {
                    dbus_message_iter_recurse( &reply_iter,
                                               &sub_iter );

                    while ( dbus_message_iter_get_arg_type( &sub_iter ) ==
                            DBUS_TYPE_DICT_ENTRY ) {
                        DBusMessageIter dict_iter;

                        dbus_message_iter_recurse( &sub_iter, &dict_iter );
                        
                        if ( dbus_message_iter_get_arg_type( &dict_iter ) == DBUS_TYPE_OBJECT_PATH ) {
                            const char *udi = NULL;

                            dbus_message_iter_get_basic( &dict_iter, &udi );

                            if ( udi ) {
                                bool use_volume = false;

                                std::string drive;
                                try {
                                    drive = getStringPropertyUDisks2( udi,
                                                                      "org.freedesktop.UDisks2.Block",
                                                                      "Drive" );
                                } catch ( int ) {
                                }

                                // first check whether we should ignore the volume
                                try {
                                    if ( ! getBoolPropertyUDisks2( udi,
                                                                   "org.freedesktop.UDisks2.Block",
                                                                   "HintSystem" ) ) {
                                        use_volume = true;
                                    }
                                } catch ( int ) {
                                }

                                // now check whether the volume is actually a mountable filesystem
                                if ( use_volume ) {
                                    try {
                                        if ( getStringPropertyUDisks2( udi,
                                                                       "org.freedesktop.UDisks2.Block",
                                                                       "IdUsage" ) != "filesystem" ) {
                                            use_volume = false;
                                        }
                                    } catch ( int ) {
                                    }

                                    // no filesystem but ejectable?
                                    if ( ! use_volume && ! drive.empty() ) {
                                        try {
                                            if ( getBoolPropertyUDisks2( drive,
                                                                         "org.freedesktop.UDisks2.Drive",
                                                                         "Ejectable" ) &&
                                                 ! getBoolPropertyUDisks2( udi,
                                                                           "org.freedesktop.UDisks2.Block",
                                                                           "HintPartitionable" ) ) {
                                                use_volume = true;
                                            }
                                        } catch ( int ) {
                                        }
                                    }
                                }

                                if ( use_volume ) {
                                    HWVolume v( udi );

                                    update( v );

                                    if ( managed.count( getOSDevice( v.getDevice() ) ) < 1 ) {
                                        managed.insert( getOSDevice( v.getDevice() ) );
                
                                        l.push_back( v );
                                    }
                                }
                            }
                        }

                        dbus_message_iter_next( &sub_iter );
                    }
                }

                dbus_message_unref( reply );
            }
        }
    }
#endif

#if !defined(HAVE_HAL_DBUS) and !defined(HAVE_DBUS)
    for ( std::list< std::string >::iterator it1 = m_avail_parts.begin();
          it1 != m_avail_parts.end();
          it1++ ) {
        if ( isDevice( *it1 ) &&
             ! m_fstab_table.containsDevice( *it1 ) &&
             managed.count( getOSDevice( *it1 ) ) < 1 ) {
            HWVolume v;
            v.setDevice( *it1 );
            v.setIsAvailable( true );

            l.push_back( v );
        }
    }
#endif

    return l;
}

int HWVolumeManager::mount( HWVolume &vol, std::string &error_return )
{
    if ( vol.isMounted() == true ) return 0;

    if ( ! vol.getUDI().empty() && vol.isInFStab() == false ) {
        return dbusMount( vol, error_return );
    } else {
        return cmdMount( vol, error_return );
    }
}

int HWVolumeManager::umount( HWVolume &vol, std::string &error_return )
{
    if ( vol.isMounted() == false ) return 0;

    if ( ! vol.getUDI().empty() && vol.isInFStab() == false ) {
        return dbusUnmount( vol, error_return );
    } else {
        return cmdUnmount( vol, error_return );
    }
}

int HWVolumeManager::eject( HWVolume &vol, std::string &error_return )
{
    if ( ! vol.getUDI().empty() ) {
        return dbusEject( vol, error_return );
    } else {
        return cmdEject( vol, error_return );
    }
}

int HWVolumeManager::closeTray( HWVolume &vol, std::string &error_return )
{
    if ( ! vol.getUDI().empty() ) {
        return dbusCloseTray( vol, error_return );
    } else {
        return cmdCloseTray( vol, error_return );
    }
}

void HWVolumeManager::update( HWVolume &vol )
{
    if ( ! vol.getUDI().empty() ) {
        bool mounted = false;
        
        if ( ! m_udisks_available &&
             ! m_udisks2_available ) {
            return;
        }

        try {
#ifdef HAVE_HAL_DBUS
            mounted = getBoolProperty( vol.getUDI(),
                                       "volume.is_mounted" );
#elif defined(HAVE_DBUS)
            if ( m_udisks_available ) {
                mounted = getBoolProperty( vol.getUDI(),
                                           "DeviceIsMounted" );
            } else {
                std::list< std::string > mountpoints = getByteArrayListPropertyUDisks2( vol.getUDI(),
                                                                                        "org.freedesktop.UDisks2.Filesystem",
                                                                                        "MountPoints" );
                if ( ! mountpoints.empty() ) {
                    mounted = true;
                }
            }
#endif
        } catch ( int ) {
        }

        if ( mounted ) {
            vol.setIsMounted( true );

            std::string mountpoint;

            try  {
#ifdef HAVE_HAL_DBUS
                mountpoint = getStringProperty( vol.getUDI().c_str(),
                                                "volume.mount_point" );
#elif defined(HAVE_DBUS)
                if ( m_udisks_available ) {
                    std::list< std::string > mountpoints = getStrlistProperty( vol.getUDI().c_str(),
                                                                               "DeviceMountPaths" );
                    if ( ! mountpoints.empty() ) {
                        mountpoint = *mountpoints.begin();
                    }
                } else {
                    std::list< std::string > mountpoints = getByteArrayListPropertyUDisks2( vol.getUDI(),
                                                                                            "org.freedesktop.UDisks2.Filesystem",
                                                                                            "MountPoints" );
                    if ( ! mountpoints.empty() ) {
                        mountpoint = *mountpoints.begin();
                    }
                }
#endif
            } catch ( int ) {
            }

            if ( ! mountpoint.empty() ) {
                vol.setMountPoint( mountpoint );
            }
        } else {
            vol.setIsMounted( false );
            vol.setMountPoint( "" );
        }
        
        try {
            std::string label;
            
#ifdef HAVE_HAL_DBUS
            label = getStringProperty( vol.getUDI().c_str(), "volume.label" );
#elif defined(HAVE_DBUS)
            if ( m_udisks_available ) {
                label = getStringProperty( vol.getUDI().c_str(),
                                           "IdLabel" );
            } else {
                label = getStringPropertyUDisks2( vol.getUDI(),
                                                  "org.freedesktop.UDisks2.Block",
                                                  "IdLabel" );
                if ( label.empty() ) {
                    label = getStringPropertyUDisks2( vol.getUDI(),
                                                      "org.freedesktop.UDisks2.Block",
                                                      "IdUUID" );
                }
            }
#endif
            
            if ( ! label.empty() ) {
                vol.setLabel( label );
            }
        } catch ( int ) {
        }

        try {
            loff_t size = 0;
            
#ifdef HAVE_HAL_DBUS
            size = getUint64Property( vol.getUDI().c_str(), "volume.size" );
#elif defined(HAVE_DBUS)
            if ( m_udisks_available ) {
                size = getUint64Property( vol.getUDI().c_str(), "DeviceSize" );
            } else {
                size = getUint64PropertyUDisks2( vol.getUDI(),
                                                 "org.freedesktop.UDisks2.Block",
                                                 "Size" );
            }
#endif
            
            if ( size != 0 ) {
                vol.setSize( size );
            }
        } catch ( int ) {
        }

        std::string device;
        try {
#ifdef HAVE_HAL_DBUS
            device = getStringProperty( vol.getUDI().c_str(),
                                        "block.device" );
#elif defined(HAVE_DBUS)
            if ( m_udisks_available ) {
                device = getStringProperty( vol.getUDI().c_str(),
                                            "DeviceFile" );
            } else {
                device = getByteArrayPropertyUDisks2( vol.getUDI(),
                                                      "org.freedesktop.UDisks2.Block",
                                                      "Device" );
            }
#endif
        } catch ( int ) {
        }

        if ( ! device.empty() ) {
            vol.setDevice( device );
            vol.setIsAvailable( true );
        } else {
            vol.setDevice( "" );
            vol.setIsAvailable( false );
        }

        try {
#ifdef HAVE_HAL_DBUS
#elif defined(HAVE_DBUS)
            if ( m_udisks_available ) {
                bool avail = getBoolProperty( vol.getUDI(),
                                              "DeviceIsMediaAvailable" );
                vol.setIsAvailable( avail );
            } else if ( m_udisks2_available ) {
                std::string drive;

                drive = getStringPropertyUDisks2( vol.getUDI(),
                                                  "org.freedesktop.UDisks2.Block",
                                                  "Drive" );

                if ( ! drive.empty() ) {
                    bool avail = getBoolPropertyUDisks2( drive,
                                                         "org.freedesktop.UDisks2.Drive",
                                                         "MediaAvailable" );

                    vol.setIsAvailable( avail );
                }
            }
#endif
        } catch ( int ) {
        }

        if ( m_fstab_table.containsDevice( vol.getDevice() ) ) {
            vol.setIsInFStab( true );
        } else {
            vol.setIsInFStab( false );
        }
    } else {
        MountTableEntry mte;
        if ( m_fstab_table.containsDevice( vol.getDevice() ) ) {
            vol.setIsInFStab( true );
        } else {
            vol.setIsInFStab( false );
        }
        if ( m_mtab_table.findDevice( vol.getDevice(), mte ) ) {
            vol.setIsMounted( true );
            vol.setIsAvailable( true );
            vol.setMountPoint( mte.getMountPoint() );
        } else {
            vol.setIsMounted( false );
            vol.setMountPoint( "" );
            if ( isAvailablePartition( vol.getDevice(), m_avail_parts ) ) {
                vol.setIsAvailable( true );
            } else {
                vol.setIsAvailable( false );
            }
        }
    }
}

static std::vector< std::string > split( const std::string &str )
{
    std::vector< std::string > l;
    std::string s1;
    std::string::size_type p = 0;

    while ( p < str.length() ) {
        if ( std::isspace( str[p] ) ) {
            if ( ! s1.empty() ) {
                l.push_back( s1 );
                s1 = "";
            }
        } else {
            s1 += str[p];
        }
        p++;
    }
    if ( ! s1.empty() ) {
        l.push_back( s1 );
    }
    return l;
}

std::list< std::string > HWVolumeManager::getAvailablePartitions()
{
    std::list< std::string > l;

    std::ifstream ifile( m_partition_file.c_str() );
    std::string line;
    
    if ( ifile.is_open() ) {
        std::string key;
        int line_nr = 0;
        while ( std::getline( ifile, line ) ) {
            if ( line_nr > 1 && ! line.empty() ) {
                std::vector< std::string > sp = split( line );
                if ( sp.size() == 4 ) {
                    if ( atoi( sp[2].c_str() ) > 10 &&
                         sp[3].length() > 0 ) {
                        char c = sp[3][sp[3].length()-1];
                        if ( c >= '0' && c <= '9' ) {
                            l.push_back( std::string( "/dev/" ) + sp[3] );
                        }
                    }
                }
            }
            line_nr++;
        }
    }

    return l;
}

bool HWVolumeManager::isAvailablePartition( const std::string &dev,
                                            const std::list< std::string > &l )
{
    std::list< std::string >::const_iterator it1;

    for ( it1 = l.begin();
          it1 != l.end();
          it1++ ) {
        if ( isSameDevice( dev, *it1 ) ) return true;
    }
    return false;
}

bool HWVolumeManager::isSameDevice( const std::string &dev1,
                                    const std::string &dev2 )
{
    if ( dev1 == dev2 ) return true;

    w_osdevice_t d1, d2;

    d1 = getOSDevice( dev1 );
    d2 = getOSDevice( dev2 );
    
    if ( d1.first == 0 ||
         d1.second == 0 ) return false;
    if ( d2.first == 0 ||
         d2.second == 0 ) return false;

    if ( d1 != d2 ) return false;

    return true;
}

bool HWVolumeManager::isDevice( const std::string &dev )
{
    NWC::FSEntry fe( dev );

    if ( ! fe.entryExists() ) return false;

    if ( fe.isLink() ) {
        if ( S_ISCHR( fe.stat_dest_mode() ) ||
             S_ISBLK( fe.stat_dest_mode() ) ) return true;
    } else {
        if ( S_ISCHR( fe.stat_mode() ) ||
             S_ISBLK( fe.stat_mode() ) ) return true;
    }

    return false;
}

std::list<HWVolume> HWVolumeManager::getNewVolumes( bool store_new )
{
    std::list<HWVolume> l = getVolumes();
    std::list<HWVolume> newl;
    std::set< std::string > new_known;
    
    for ( std::list<HWVolume>::iterator it1 = l.begin();
          it1 != l.end();
          it1++ ) {
        if ( it1->isAvailable() == true ) {
            if ( m_known_devices.count( it1->getDevice() ) == 0 ) {
                newl.push_back( *it1 );
            }
            new_known.insert( it1->getDevice() );
        }
    }
    if ( store_new ) {
        m_known_devices = new_known;
    }
    return newl;
}

std::list<HWVolume> HWVolumeManager::filterUniqueDevices( const std::list<HWVolume> &l ) const
{
    std::set< w_osdevice_t > managed;
    std::list<HWVolume> newl;

    for ( std::list<HWVolume>::const_iterator it1 = l.begin();
          it1 != l.end();
          it1++ ) {
        if ( managed.count( getOSDevice( it1->getDevice() ) ) < 1 ) {
            newl.push_back( *it1 );
            managed.insert( getOSDevice( it1->getDevice() ) );
        }
    }
    return newl;
}

bool HWVolumeManager::getBoolProperty( const std::string &udi,
                                       const std::string &property )
{
#ifdef HAVE_HAL_DBUS
    if ( ! m_hal_ctx ) throw 1;

    if ( ! libhal_device_property_exists( m_hal_ctx, udi.c_str(),
                                          property.c_str(),
                                          NULL ) ) {
        throw 2;
    }

    return libhal_device_get_property_bool( m_hal_ctx, udi.c_str(),
                                            property.c_str(), NULL );
#elif defined(HAVE_DBUS)
    DBusMessage *message = NULL, *reply;
    DBusMessageIter iter, reply_iter, sub_iter;
    DBusError dbus_error;

    message = dbus_message_new_method_call( "org.freedesktop.UDisks",
                                            udi.c_str(),
                                            "org.freedesktop.DBus.Properties",
                                            "Get" );

    if ( message == NULL ) {
        throw 1;
    }

    dbus_message_iter_init_append( message, &iter );

    const char *arg1 = "org.freedesktop.UDisks.Device";
    const char *arg2 = property.c_str();

    dbus_message_iter_append_basic( &iter,
                                    DBUS_TYPE_STRING,
                                    &arg1 );
    dbus_message_iter_append_basic( &iter,
                                    DBUS_TYPE_STRING,
                                    &arg2 );

    dbus_error_init( &dbus_error );

    reply = dbus_connection_send_with_reply_and_block( m_dbus_conn,
                                                       message,
                                                       -1,
                                                       &dbus_error );

    dbus_message_unref( message );

    if ( dbus_error_is_set( &dbus_error ) ) {
        fprintf( stderr, "get bool property error: %s\n", dbus_error.message );
        dbus_error_free( &dbus_error );

        throw 2;
    }

    if ( reply == NULL ) {
        throw 3;
    }

    dbus_message_iter_init( reply, &reply_iter );

    dbus_bool_t v = false;

    if ( dbus_message_iter_get_arg_type( &reply_iter ) ==
         DBUS_TYPE_VARIANT ) {

        dbus_message_iter_recurse( &reply_iter,
                                   &sub_iter );

        if ( dbus_message_iter_get_arg_type( &sub_iter ) ==
             DBUS_TYPE_BOOLEAN ) {

            dbus_message_iter_get_basic( &sub_iter, &v );
        } else {
            dbus_message_unref( reply );
            throw 4;
        }
    } else {
        dbus_message_unref( reply );
        throw 4;
    }

    dbus_message_unref( reply );

    return v;
#else
    throw -1;
#endif
}

bool HWVolumeManager::getBoolPropertyUDisks2( const std::string &path,
                                              const std::string &interface,
                                              const std::string &property )
{
#if defined(HAVE_DBUS)
    DBusMessage *message = NULL, *reply;
    DBusMessageIter iter, reply_iter, sub_iter;
    DBusError dbus_error;

    if ( ! m_udisks2_available ) {
        throw 1;
    }
    
    message = dbus_message_new_method_call( "org.freedesktop.UDisks2",
                                            path.c_str(),
                                            "org.freedesktop.DBus.Properties",
                                            "Get" );

    if ( message == NULL ) {
        throw 1;
    }

    dbus_message_iter_init_append( message, &iter );

    const char *arg1 = interface.c_str();
    const char *arg2 = property.c_str();

    dbus_message_iter_append_basic( &iter,
                                    DBUS_TYPE_STRING,
                                    &arg1 );
    dbus_message_iter_append_basic( &iter,
                                    DBUS_TYPE_STRING,
                                    &arg2 );

    dbus_error_init( &dbus_error );

    reply = dbus_connection_send_with_reply_and_block( m_dbus_conn,
                                                       message,
                                                       -1,
                                                       &dbus_error );

    dbus_message_unref( message );

    if ( dbus_error_is_set( &dbus_error ) ) {
        // be silent since the interface might not even exists
        //fprintf( stderr, "get bool property error: %s\n", dbus_error.message );
        dbus_error_free( &dbus_error );

        throw 2;
    }

    if ( reply == NULL ) {
        throw 3;
    }

    dbus_message_iter_init( reply, &reply_iter );

    dbus_bool_t v = false;

    if ( dbus_message_iter_get_arg_type( &reply_iter ) ==
         DBUS_TYPE_VARIANT ) {

        dbus_message_iter_recurse( &reply_iter,
                                   &sub_iter );

        if ( dbus_message_iter_get_arg_type( &sub_iter ) ==
             DBUS_TYPE_BOOLEAN ) {

            dbus_message_iter_get_basic( &sub_iter, &v );
        } else {
            dbus_message_unref( reply );
            throw 4;
        }
    } else {
        dbus_message_unref( reply );
        throw 4;
    }

    dbus_message_unref( reply );

    return v;
#else
    throw -1;
#endif
}

std::string HWVolumeManager::getStringProperty( const std::string &udi,
                                                const std::string &property )
{
#ifdef HAVE_HAL_DBUS
    if ( ! m_hal_ctx ) throw 1;

    if ( ! libhal_device_property_exists( m_hal_ctx, udi.c_str(),
                                          property.c_str(),
                                          NULL ) ) {
        throw 2;
    }

    char *s1 = libhal_device_get_property_string( m_hal_ctx, udi.c_str(),
                                                  property.c_str(), NULL );
    if ( ! s1 ) throw 3;

    std::string s2( s1 );
    libhal_free_string( s1 );
    return s2;
#elif defined(HAVE_DBUS)
    DBusMessage *message, *reply;
    DBusMessageIter iter, reply_iter, sub_iter;
    DBusError dbus_error;

    message = dbus_message_new_method_call( "org.freedesktop.UDisks",
                                            udi.c_str(),
                                            "org.freedesktop.DBus.Properties",
                                            "Get" );

    if ( message == NULL ) {
        throw 1;
    }

    dbus_message_iter_init_append( message, &iter );

    const char *arg1 = "org.freedesktop.UDisks.Device";
    const char *arg2 = property.c_str();

    dbus_message_iter_append_basic( &iter,
                                    DBUS_TYPE_STRING,
                                    &arg1 );
    dbus_message_iter_append_basic( &iter,
                                    DBUS_TYPE_STRING,
                                    &arg2 );

    dbus_error_init( &dbus_error );

    reply = dbus_connection_send_with_reply_and_block( m_dbus_conn,
                                                       message,
                                                       -1,
                                                       &dbus_error );

    dbus_message_unref( message );

    if ( dbus_error_is_set( &dbus_error ) ) {
        //fprintf( stderr, "get string property error: %s\n", dbus_error.message );
        dbus_error_free( &dbus_error );

        throw 2;
    }

    if ( reply == NULL ) {
        throw 3;
    }

    dbus_message_iter_init( reply, &reply_iter );

    std::string res1;

    if ( dbus_message_iter_get_arg_type( &reply_iter ) ==
         DBUS_TYPE_VARIANT ) {

        dbus_message_iter_recurse( &reply_iter,
                                   &sub_iter );

        if ( dbus_message_iter_get_arg_type( &sub_iter ) ==
             DBUS_TYPE_STRING ) {

            const char *str1 = NULL;

            dbus_message_iter_get_basic( &sub_iter, &str1 );

            if ( str1 ) {
                res1 = str1;
            }
        } else {
            dbus_message_unref( reply );
            throw 4;
        }
    } else {
        dbus_message_unref( reply );
        throw 4;
    }

    dbus_message_unref( reply );

    return res1;
#else
    throw -1;
#endif
}

std::string HWVolumeManager::getStringPropertyUDisks2( const std::string &udi,
                                                       const std::string &interface,
                                                       const std::string &property )
{
#if defined(HAVE_DBUS)
    DBusMessage *message, *reply;
    DBusMessageIter iter, reply_iter, sub_iter;
    DBusError dbus_error;

    if ( ! m_udisks2_available ) {
        throw 1;
    }
    
    message = dbus_message_new_method_call( "org.freedesktop.UDisks2",
                                            udi.c_str(),
                                            "org.freedesktop.DBus.Properties",
                                            "Get" );

    if ( message == NULL ) {
        throw 1;
    }

    dbus_message_iter_init_append( message, &iter );

    const char *arg1 = interface.c_str();
    const char *arg2 = property.c_str();

    dbus_message_iter_append_basic( &iter,
                                    DBUS_TYPE_STRING,
                                    &arg1 );
    dbus_message_iter_append_basic( &iter,
                                    DBUS_TYPE_STRING,
                                    &arg2 );

    dbus_error_init( &dbus_error );

    reply = dbus_connection_send_with_reply_and_block( m_dbus_conn,
                                                       message,
                                                       -1,
                                                       &dbus_error );

    dbus_message_unref( message );

    if ( dbus_error_is_set( &dbus_error ) ) {
        // be silent since the interface might not even exists
        //fprintf( stderr, "get bool property error: %s\n", dbus_error.message );
        dbus_error_free( &dbus_error );

        throw 2;
    }

    if ( reply == NULL ) {
        throw 3;
    }

    dbus_message_iter_init( reply, &reply_iter );

    std::string res1;

    if ( dbus_message_iter_get_arg_type( &reply_iter ) ==
         DBUS_TYPE_VARIANT ) {

        dbus_message_iter_recurse( &reply_iter,
                                   &sub_iter );

        if ( dbus_message_iter_get_arg_type( &sub_iter ) ==
             DBUS_TYPE_STRING ||
             dbus_message_iter_get_arg_type( &sub_iter ) ==
             DBUS_TYPE_OBJECT_PATH ) {

            DBusBasicValue val;

            dbus_message_iter_get_basic( &sub_iter, &val );

            if ( val.str ) {
                res1 = val.str;
            }
        } else {
            dbus_message_unref( reply );
            throw 4;
        }
    } else {
        dbus_message_unref( reply );
        throw 4;
    }

    dbus_message_unref( reply );

    return res1;
#else
    throw -1;
#endif
}

std::string HWVolumeManager::getByteArrayPropertyUDisks2( const std::string &udi,
                                                          const std::string &interface,
                                                          const std::string &property )
{
#if defined(HAVE_DBUS)
    DBusMessage *message, *reply;
    DBusMessageIter iter, reply_iter, sub_iter;
    DBusError dbus_error;

    if ( ! m_udisks2_available ) {
        throw 1;
    }
    
    message = dbus_message_new_method_call( "org.freedesktop.UDisks2",
                                            udi.c_str(),
                                            "org.freedesktop.DBus.Properties",
                                            "Get" );

    if ( message == NULL ) {
        throw 1;
    }

    dbus_message_iter_init_append( message, &iter );

    const char *arg1 = interface.c_str();
    const char *arg2 = property.c_str();

    dbus_message_iter_append_basic( &iter,
                                    DBUS_TYPE_STRING,
                                    &arg1 );
    dbus_message_iter_append_basic( &iter,
                                    DBUS_TYPE_STRING,
                                    &arg2 );

    dbus_error_init( &dbus_error );

    reply = dbus_connection_send_with_reply_and_block( m_dbus_conn,
                                                       message,
                                                       -1,
                                                       &dbus_error );

    dbus_message_unref( message );

    if ( dbus_error_is_set( &dbus_error ) ) {
        // be silent since the interface might not even exists
        //fprintf( stderr, "get bool property error: %s\n", dbus_error.message );
        dbus_error_free( &dbus_error );

        throw 2;
    }

    if ( reply == NULL ) {
        throw 3;
    }

    dbus_message_iter_init( reply, &reply_iter );

    std::string res1;

    if ( dbus_message_iter_get_arg_type( &reply_iter ) ==
         DBUS_TYPE_VARIANT ) {

        dbus_message_iter_recurse( &reply_iter,
                                   &sub_iter );

        if ( dbus_message_iter_get_arg_type( &sub_iter ) ==
             DBUS_TYPE_ARRAY ) {

            DBusMessageIter array_iter;

            dbus_message_iter_recurse( &sub_iter,
                                       &array_iter );

            while ( dbus_message_iter_get_arg_type( &array_iter ) ==
                    DBUS_TYPE_BYTE ) {
                DBusBasicValue val;

                dbus_message_iter_get_basic( &array_iter, &val );

                if ( val.byt != 0 ) {
                    res1 += val.byt;
                } else {
                    break;
                }

                dbus_message_iter_next( &array_iter );
            }
        } else {
            dbus_message_unref( reply );
            throw 4;
        }
    } else {
        dbus_message_unref( reply );
        throw 4;
    }

    dbus_message_unref( reply );

    return res1;
#else
    throw -1;
#endif
}

loff_t HWVolumeManager::getUint64Property( const std::string &udi,
                                           const std::string &property )
{
#ifdef HAVE_HAL_DBUS
    if ( ! m_hal_ctx ) throw 1;

    if ( ! libhal_device_property_exists( m_hal_ctx, udi.c_str(),
                                          property.c_str(),
                                          NULL ) ) {
        throw 2;
    }

    dbus_uint64_t val1 = 0;
    val1 = libhal_device_get_property_uint64( m_hal_ctx, udi.c_str(),
                                              property.c_str(), NULL );
    //TODO some error handling would be good
    return (loff_t)val1;
#elif defined(HAVE_DBUS)
    DBusMessage *message, *reply;
    DBusMessageIter iter, reply_iter, sub_iter;
    DBusError dbus_error;

    message = dbus_message_new_method_call( "org.freedesktop.UDisks",
                                            udi.c_str(),
                                            "org.freedesktop.DBus.Properties",
                                            "Get" );

    if ( message == NULL ) {
        throw 1;
    }

    dbus_message_iter_init_append( message, &iter );

    const char *arg1 = "org.freedesktop.UDisks.Device";
    const char *arg2 = property.c_str();

    dbus_message_iter_append_basic( &iter,
                                    DBUS_TYPE_STRING,
                                    &arg1 );
    dbus_message_iter_append_basic( &iter,
                                    DBUS_TYPE_STRING,
                                    &arg2 );

    dbus_error_init( &dbus_error );

    reply = dbus_connection_send_with_reply_and_block( m_dbus_conn,
                                                       message,
                                                       -1,
                                                       &dbus_error );

    dbus_message_unref( message );

    if ( dbus_error_is_set( &dbus_error ) ) {
        fprintf( stderr, "get uint64 property error: %s\n", dbus_error.message );
        dbus_error_free( &dbus_error );

        throw 2;
    }

    if ( reply == NULL ) {
        throw 3;
    }

    dbus_message_iter_init( reply, &reply_iter );

    loff_t res1 = 0;

    if ( dbus_message_iter_get_arg_type( &reply_iter ) ==
         DBUS_TYPE_VARIANT ) {

        dbus_message_iter_recurse( &reply_iter,
                                   &sub_iter );

        if ( dbus_message_iter_get_arg_type( &sub_iter ) ==
             DBUS_TYPE_UINT64 ) {

            dbus_uint64_t v;

            dbus_message_iter_get_basic( &sub_iter, &v );

            res1 = v;
        } else {
            dbus_message_unref( reply );
            throw 4;
        }
    } else {
        dbus_message_unref( reply );
        throw 4;
    }

    dbus_message_unref( reply );

    return res1;
#else
    throw -1;
#endif
}

loff_t HWVolumeManager::getUint64PropertyUDisks2( const std::string &udi,
                                                  const std::string &interface,
                                                  const std::string &property )
{
#if defined(HAVE_DBUS)
    DBusMessage *message, *reply;
    DBusMessageIter iter, reply_iter, sub_iter;
    DBusError dbus_error;

    if ( ! m_udisks2_available ) {
        throw 1;
    }
    
    message = dbus_message_new_method_call( "org.freedesktop.UDisks2",
                                            udi.c_str(),
                                            "org.freedesktop.DBus.Properties",
                                            "Get" );

    if ( message == NULL ) {
        throw 1;
    }

    dbus_message_iter_init_append( message, &iter );

    const char *arg1 = interface.c_str();
    const char *arg2 = property.c_str();

    dbus_message_iter_append_basic( &iter,
                                    DBUS_TYPE_STRING,
                                    &arg1 );
    dbus_message_iter_append_basic( &iter,
                                    DBUS_TYPE_STRING,
                                    &arg2 );

    dbus_error_init( &dbus_error );

    reply = dbus_connection_send_with_reply_and_block( m_dbus_conn,
                                                       message,
                                                       -1,
                                                       &dbus_error );

    dbus_message_unref( message );

    if ( dbus_error_is_set( &dbus_error ) ) {
        //fprintf( stderr, "get bool property error: %s\n", dbus_error.message );
        dbus_error_free( &dbus_error );

        throw 2;
    }

    if ( reply == NULL ) {
        throw 3;
    }

    dbus_message_iter_init( reply, &reply_iter );

    loff_t res1 = 0;

    if ( dbus_message_iter_get_arg_type( &reply_iter ) ==
         DBUS_TYPE_VARIANT ) {

        dbus_message_iter_recurse( &reply_iter,
                                   &sub_iter );

        if ( dbus_message_iter_get_arg_type( &sub_iter ) ==
             DBUS_TYPE_UINT64 ) {

            dbus_uint64_t v;

            dbus_message_iter_get_basic( &sub_iter, &v );

            res1 = v;
        } else {
            dbus_message_unref( reply );
            throw 4;
        }
    } else {
        dbus_message_unref( reply );
        throw 4;
    }

    dbus_message_unref( reply );

    return res1;
#else
    throw -1;
#endif
}

std::list< std::string > HWVolumeManager::getStrlistProperty( const std::string &udi,
                                                              const std::string &property )
{
#ifdef HAVE_HAL_DBUS
    if ( ! m_hal_ctx ) throw 1;

    if ( ! libhal_device_property_exists( m_hal_ctx, udi.c_str(),
                                          property.c_str(),
                                          NULL ) ) {
        throw 2;
    }

    char **l1 = libhal_device_get_property_strlist( m_hal_ctx, udi.c_str(),
                                                   property.c_str(), NULL );
    if ( ! l1 ) throw 3;

    std::list< std::string > l2;
    for ( int i = 0; l1[i] != NULL; i++ ) {
        l2.push_back( l1[i] );
    }

    libhal_free_string_array( l1 );

    return l2;
#elif defined(HAVE_DBUS)
    DBusMessage *message, *reply;
    DBusMessageIter iter, reply_iter, sub_iter, sub_sub_iter;
    DBusError dbus_error;

    message = dbus_message_new_method_call( "org.freedesktop.UDisks",
                                            udi.c_str(),
                                            "org.freedesktop.DBus.Properties",
                                            "Get" );

    if ( message == NULL ) {
        throw 1;
    }

    dbus_message_iter_init_append( message, &iter );

    const char *arg1 = "org.freedesktop.UDisks.Device";
    const char *arg2 = property.c_str();

    dbus_message_iter_append_basic( &iter,
                                    DBUS_TYPE_STRING,
                                    &arg1 );
    dbus_message_iter_append_basic( &iter,
                                    DBUS_TYPE_STRING,
                                    &arg2 );

    dbus_error_init( &dbus_error );

    reply = dbus_connection_send_with_reply_and_block( m_dbus_conn,
                                                       message,
                                                       -1,
                                                       &dbus_error );

    dbus_message_unref( message );

    if ( dbus_error_is_set( &dbus_error ) ) {
        fprintf( stderr, "get strlist property error: %s\n", dbus_error.message );
        dbus_error_free( &dbus_error );

        throw 2;
    }

    if ( reply == NULL ) {
        throw 3;
    }

    dbus_message_iter_init( reply, &reply_iter );

    std::list< std::string > res;

    if ( dbus_message_iter_get_arg_type( &reply_iter ) ==
         DBUS_TYPE_VARIANT ) {

        dbus_message_iter_recurse( &reply_iter,
                                   &sub_iter );


        if ( dbus_message_iter_get_arg_type( &sub_iter ) ==
             DBUS_TYPE_ARRAY ) {

            dbus_message_iter_recurse( &sub_iter,
                                       &sub_sub_iter );

            while ( dbus_message_iter_get_arg_type( &sub_sub_iter ) ==
                    DBUS_TYPE_STRING ) {
                const char *str1 = NULL;

                dbus_message_iter_get_basic( &sub_sub_iter, &str1 );

                if ( str1 ) {
                    res.push_back( str1 );
                }

                dbus_message_iter_next( &sub_sub_iter );
            }
        } else {
            dbus_message_unref( reply );
            throw 4;
        }
    } else {
        dbus_message_unref( reply );
        throw 4;
    }

    dbus_message_unref( reply );

    return res;
#else
    throw -1;
#endif
}

std::list< std::string > HWVolumeManager::getByteArrayListPropertyUDisks2( const std::string &udi,
                                                                           const std::string &interface,
                                                                           const std::string &property )
{
#if defined(HAVE_DBUS)
    DBusMessage *message, *reply;
    DBusMessageIter iter, reply_iter, sub_iter, sub_sub_iter;
    DBusError dbus_error;

    if ( ! m_udisks2_available ) {
        throw 1;
    }
    
    message = dbus_message_new_method_call( "org.freedesktop.UDisks2",
                                            udi.c_str(),
                                            "org.freedesktop.DBus.Properties",
                                            "Get" );

    if ( message == NULL ) {
        throw 1;
    }

    dbus_message_iter_init_append( message, &iter );

    const char *arg1 = interface.c_str();
    const char *arg2 = property.c_str();

    dbus_message_iter_append_basic( &iter,
                                    DBUS_TYPE_STRING,
                                    &arg1 );
    dbus_message_iter_append_basic( &iter,
                                    DBUS_TYPE_STRING,
                                    &arg2 );

    dbus_error_init( &dbus_error );

    reply = dbus_connection_send_with_reply_and_block( m_dbus_conn,
                                                       message,
                                                       -1,
                                                       &dbus_error );

    dbus_message_unref( message );

    if ( dbus_error_is_set( &dbus_error ) ) {
        //fprintf( stderr, "get bool property error: %s\n", dbus_error.message );
        dbus_error_free( &dbus_error );

        throw 2;
    }

    if ( reply == NULL ) {
        throw 3;
    }

    dbus_message_iter_init( reply, &reply_iter );

    std::list< std::string > res;

    if ( dbus_message_iter_get_arg_type( &reply_iter ) ==
         DBUS_TYPE_VARIANT ) {

        dbus_message_iter_recurse( &reply_iter,
                                   &sub_iter );


        if ( dbus_message_iter_get_arg_type( &sub_iter ) ==
             DBUS_TYPE_ARRAY ) {

            dbus_message_iter_recurse( &sub_iter,
                                       &sub_sub_iter );

            while ( dbus_message_iter_get_arg_type( &sub_sub_iter ) ==
                    DBUS_TYPE_ARRAY ) {
                std::string str1;
                DBusMessageIter array_iter;

                dbus_message_iter_recurse( &sub_sub_iter,
                                           &array_iter );

                while ( dbus_message_iter_get_arg_type( &array_iter ) ==
                        DBUS_TYPE_BYTE ) {
                    DBusBasicValue val;

                    dbus_message_iter_get_basic( &array_iter, &val );

                    if ( val.byt != 0 ) {
                        str1 += val.byt;
                    } else {
                        break;
                    }

                    dbus_message_iter_next( &array_iter );
                }
                
                res.push_back( str1 );

                dbus_message_iter_next( &sub_sub_iter );
            }
        } else {
            dbus_message_unref( reply );
            throw 4;
        }
    } else {
        dbus_message_unref( reply );
        throw 4;
    }

    dbus_message_unref( reply );

    return res;
#else
    throw -1;
#endif
}

void HWVolumeManager::setMountCommand( const std::string &cmd )
{
    if ( ! cmd.empty() ) {
        m_mount_command = cmd;
    } else {
        m_mount_command = HW_VM_MOUNT_CMD;
    }
}

void HWVolumeManager::setUnmountCommand( const std::string &cmd )
{
    if ( ! cmd.empty() ) {
        m_unmount_command = cmd;
    } else {
        m_unmount_command = HW_VM_UNMOUNT_CMD;
    }
}

void HWVolumeManager::setEjectCommand( const std::string &cmd )
{
    if ( ! cmd.empty() ) {
        m_eject_command = cmd;
    } else {
        m_eject_command = HW_VM_EJECT_CMD;
    }
}

void HWVolumeManager::setCloseTrayCommand( const std::string &cmd )
{
    if ( ! cmd.empty() ) {
        m_closetray_command = cmd;
    } else {
        m_closetray_command = HW_VM_CLOSETRAY_CMD;
    }
}

int HWVolumeManager::dbusMount( HWVolume &vol, std::string &error_return )
{
    if ( vol.isMounted() == true ) {
        error_return = catalog.getLocale( 889 );
        return 1;
    }
    if ( vol.getUDI().empty() || vol.isInFStab() ) {
        error_return = catalog.getLocale( 890 );
        return 1;
    }

#ifdef HAVE_HAL_DBUS
    DBusMessage *message = NULL;
    DBusMessage *reply = NULL;
    const char *mp = "";
    const char *fs = "";
    DBusError error;
#endif
    bool mount_failed = true;

#ifdef HAVE_HAL_DBUS
    if ( HALreconnect() == 0 ) {

        dbus_error_init( &error );

        // check whether there is a mount method
        std::list< std::string > method_list;

        try {
            method_list = getStrlistProperty( vol.getUDI().c_str(), "org.freedesktop.Hal.Device.Volume.method_names" );
        } catch ( int ) {
        }

        if ( std::find( method_list.begin(),
                        method_list.end(),
                        "Mount" ) != method_list.end() ) {

            try {
                message = dbus_message_new_method_call( "org.freedesktop.Hal", 
                                                        vol.getUDI().c_str(),
                                                        "org.freedesktop.Hal.Device.Volume",
                                                        "Mount" );
                if ( ! message ) {
                    //TODO translate
                    error_return = "Mount failed: Unable to create DBus message";
                    throw 1;
                }

                std::list< std::string > valid_options;
                try {
                    valid_options = getStrlistProperty( vol.getUDI().c_str(),
                                                        "volume.mount.valid_options" );
                } catch ( int ) {
                }

                const char *options[] = { NULL };
                int num_options = 0;
                std::string uid_opt;

                if ( std::find( valid_options.begin(),
                                valid_options.end(),
                                "uid=" ) != valid_options.end() ) {
                    uid_opt = AGUIXUtils::formatStringToString( "uid=%d", (int)getuid() );
                    
                    options[0] = uid_opt.c_str();
                    num_options = 1;
                }

                const char **options_p = options;
            
                if ( ! dbus_message_append_args( message, 
                                                 DBUS_TYPE_STRING, &mp,
                                                 DBUS_TYPE_STRING, &fs,
                                                 DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options_p, num_options,
                                                 DBUS_TYPE_INVALID ) ) {
                    //TODO translate?
                    error_return = "Mount failed: Unable to add arguments to DBus message";
                    throw 2;
                }
            
                reply = dbus_connection_send_with_reply_and_block( m_dbus_conn,
                                                                   message,
                                                                   -1,
                                                                   &error );
                if ( ! reply ) {
                    throw 3;
                }

                if ( dbus_error_is_set( &error ) ) {
                    throw 4;
                }
            
#if 0
                int hal_retcode;

                // I do not do this test anymore, on some systems its INT32
                // on other UINT32
                // beside, other tools (gnome-mount) do not check it either
                if ( ! dbus_message_get_args( reply, 
                                              &error,
                                              DBUS_TYPE_UINT32, &hal_retcode,
                                              DBUS_TYPE_INVALID ) ) {
                    throw 4;
                }
            
                if ( hal_retcode != 0 ) {
                    throw 5;
                }
#endif
                mount_failed = false;
            } catch ( int e ) {
                if ( dbus_error_is_set( &error ) ) {
                    error_return = AGUIXUtils::formatStringToString( catalog.getLocale( 891 ),
                                                                     error.name,
                                                                     error.message );
                    dbus_error_free( &error );
                }
            }

            if ( message ) dbus_message_unref( message );
            if ( reply ) dbus_message_unref( reply );
        } else {
            // no mount method
            //TODO translate?
            error_return = "Mount failed: No Mount method";
        }
    }
#elif defined(HAVE_DBUS)
    if ( ! m_udisks_available &&
         ! m_udisks2_available ) {
        return 1;
    }

    if ( HALreconnect() == 0 ) {
        if ( m_udisks_available ) {
            DBusMessage *message, *reply;
            DBusError dbus_error;
            const char *fs = "";
            const char *options[] = { NULL };
            int num_options = 0;

            message = dbus_message_new_method_call( "org.freedesktop.UDisks",
                                                    vol.getUDI().c_str(),
                                                    "org.freedesktop.UDisks.Device",
                                                    "FilesystemMount" );

            if ( message == NULL ) {
                error_return = "Mount failed: Unable to create DBus message";
            } else {

                const char **options_p = options;
            
                if ( ! dbus_message_append_args( message, 
                                                 DBUS_TYPE_STRING, &fs,
                                                 DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options_p, num_options,
                                                 DBUS_TYPE_INVALID ) ) {
                    //TODO translate?
                    error_return = "Mount failed: Unable to add arguments to DBus message";
                    dbus_message_unref( message );
                } else {

                    dbus_error_init( &dbus_error );

                    reply = dbus_connection_send_with_reply_and_block( m_dbus_conn,
                                                                       message,
                                                                       -1,
                                                                       &dbus_error );

                    dbus_message_unref( message );

                    if ( dbus_error_is_set( &dbus_error ) ) {
                        error_return = AGUIXUtils::formatStringToString( catalog.getLocale( 905 ),
                                                                         dbus_error.message );
                        dbus_error_free( &dbus_error );
                    } else {
                        mount_failed = false;

                        if ( reply ) {
                            dbus_message_unref( reply );
                        }
                    }
                }
            }
        } else {
            DBusMessage *message, *reply;
            DBusError dbus_error;

            message = dbus_message_new_method_call( "org.freedesktop.UDisks2",
                                                    vol.getUDI().c_str(),
                                                    "org.freedesktop.UDisks2.Filesystem",
                                                    "Mount" );

            if ( message == NULL ) {
                error_return = "Mount failed: Unable to create DBus message";
            } else {
                DBusMessageIter iter1, dict;

                dbus_message_iter_init_append( message, &iter1 );

                dbus_message_iter_open_container( &iter1,
                                                  DBUS_TYPE_ARRAY,
                                                  DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
                                                  DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
                                                  DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
                                                  &dict );
                
                dbus_message_iter_close_container(&iter1, &dict);

                dbus_error_init( &dbus_error );

                reply = dbus_connection_send_with_reply_and_block( m_dbus_conn,
                                                                   message,
                                                                   -1,
                                                                   &dbus_error );

                dbus_message_unref( message );

                if ( dbus_error_is_set( &dbus_error ) ) {
                    error_return = AGUIXUtils::formatStringToString( catalog.getLocale( 905 ),
                                                                     dbus_error.message );
                    dbus_error_free( &dbus_error );
                } else {
                    mount_failed = false;

                    if ( reply ) {
                        dbus_message_unref( reply );
                    }
                }
            }
        }
    }
#endif
    if ( mount_failed ) return 1;

    update( vol );

    return 0;
}

int HWVolumeManager::cmdMount( HWVolume &vol, std::string &error_return )
{
    if ( vol.isMounted() == true ) {
        error_return = catalog.getLocale( 889 );
        return 1;
    }
    if ( ! vol.getUDI().empty() && vol.isInFStab() == false ) {
        error_return = catalog.getLocale( 892 );
        return 1;
    }

    auto fl = std::make_shared< FlagReplacer::FlagProducerMap >();

    if ( ! vol.getSupposedMountPoint().empty() ) {
        fl->registerFlag( "f", vol.getSupposedMountPoint() );
    } else {
        fl->registerFlag( "f", vol.getDevice() );
    }
    fl->registerFlag( "d", vol.getDevice() );

    FlagReplacer rpl( fl );
  
    std::string exestr = rpl.replaceFlags( m_mount_command );

    ExeClass ec;

    ec.addCommand( "%s", exestr.c_str() );
    int ret = ec.getReturnCode();
    if ( ret != 0 ) {
        std::string error;
        //TODO do not use a hard-coded limit
        ec.readErrorOutput( error, 1024 );

        error_return = error.substr( 0, error.find( '\n' ) );
        return 1;
    }

    update( vol );

    return 0;
}

int HWVolumeManager::dbusUnmount( HWVolume &vol, std::string &error_return )
{
    if ( vol.isMounted() == false ) {
        error_return = catalog.getLocale( 893 );
        return 1;
    }
    if ( vol.getUDI().empty() || vol.isInFStab() ) {
        error_return = catalog.getLocale( 894 );
        return 1;
    }

#ifdef HAVE_HAL_DBUS
    DBusMessage *message = NULL;
    DBusMessage *reply = NULL;
    DBusError error;
#endif
    bool unmount_failed = true;
    
#ifdef HAVE_HAL_DBUS
    if ( HALreconnect() == 0 ) {

        dbus_error_init( &error );

        // check whether there is a mount method
        std::list< std::string > method_list;

        try {
            method_list = getStrlistProperty( vol.getUDI().c_str(), "org.freedesktop.Hal.Device.Volume.method_names" );
        } catch ( int ) {
        }

        if ( std::find( method_list.begin(),
                        method_list.end(),
                        "Unmount" ) != method_list.end() ) {

            try {
                message = dbus_message_new_method_call( "org.freedesktop.Hal", 
                                                        vol.getUDI().c_str(),
                                                        "org.freedesktop.Hal.Device.Volume",
                                                        "Unmount" );
                if ( ! message ) {
                    //TODO translate?
                    error_return = "Unmount failed: Unable to create DBus message";
                    throw 1;
                }

                const char *options[] = { NULL };
                int num_options = 0;
                const char **options_p = options;
            
                if ( ! dbus_message_append_args( message, 
                                                 DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options_p, num_options,
                                                 DBUS_TYPE_INVALID ) ) {
                    //TODO translate?
                    error_return = "Unmount failed: Unable to add arguments to DBus message";
                    throw 2;
                }
            
                reply = dbus_connection_send_with_reply_and_block( m_dbus_conn,
                                                                   message,
                                                                   -1,
                                                                   &error );
                if ( ! reply ) {
                    throw 3;
                }
            
                if ( dbus_error_is_set( &error ) ) {
                    throw 4;
                }

#if 0
                int hal_retcode;

                // see comment in dbusMount
                if ( ! dbus_message_get_args( reply, 
                                              &error,
                                              DBUS_TYPE_UINT32, &hal_retcode,
                                              DBUS_TYPE_INVALID ) ) {
                    throw 4;
                }
            
                if ( hal_retcode != 0 ) {
                    throw 5;
                }
#endif
                unmount_failed = false;
            } catch ( int e ) {
                if ( dbus_error_is_set( &error ) ) {
                    error_return = AGUIXUtils::formatStringToString( catalog.getLocale( 895 ),
                                                                     error.name,
                                                                     error.message );
                    dbus_error_free( &error );
                }
            }

            if ( message ) dbus_message_unref( message );
            if ( reply ) dbus_message_unref( reply );
        } else {
            // no unmount method
            //TODO translate?
            error_return = "Unmount failed: No Unmount method";
        }
    }
#elif defined(HAVE_DBUS)
    if ( ! m_udisks_available &&
         ! m_udisks2_available ) {
        return 1;
    }

    if ( HALreconnect() == 0 ) {
        if ( m_udisks_available ) {
            DBusMessage *message, *reply;
            DBusError dbus_error;
            const char *options[] = { NULL };
            int num_options = 0;

            message = dbus_message_new_method_call( "org.freedesktop.UDisks",
                                                    vol.getUDI().c_str(),
                                                    "org.freedesktop.UDisks.Device",
                                                    "FilesystemUnmount" );

            if ( message == NULL ) {
                error_return = "Unmount failed: Unable to create DBus message";
            } else {

                const char **options_p = options;
            
                if ( ! dbus_message_append_args( message, 
                                                 DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options_p, num_options,
                                                 DBUS_TYPE_INVALID ) ) {
                    //TODO translate?
                    error_return = "Unmount failed: Unable to add arguments to DBus message";
                    dbus_message_unref( message );
                } else {

                    dbus_error_init( &dbus_error );

                    reply = dbus_connection_send_with_reply_and_block( m_dbus_conn,
                                                                       message,
                                                                       DBUS_TIMEOUT_INFINITE,
                                                                       &dbus_error );

                    dbus_message_unref( message );

                    if ( dbus_error_is_set( &dbus_error ) ) {
                        error_return = AGUIXUtils::formatStringToString( catalog.getLocale( 971 ),
                                                                         dbus_error.message );
                        dbus_error_free( &dbus_error );
                    } else {
                        unmount_failed = false;

                        if ( reply ) {
                            dbus_message_unref( reply );
                        }
                    }
                }
            }
        } else {
            DBusMessage *message, *reply;
            DBusError dbus_error;

            message = dbus_message_new_method_call( "org.freedesktop.UDisks2",
                                                    vol.getUDI().c_str(),
                                                    "org.freedesktop.UDisks2.Filesystem",
                                                    "Unmount" );

            if ( message == NULL ) {
                error_return = "Unmount failed: Unable to create DBus message";
            } else {
                DBusMessageIter iter1, dict;

                dbus_message_iter_init_append( message, &iter1 );

                dbus_message_iter_open_container( &iter1,
                                                  DBUS_TYPE_ARRAY,
                                                  DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
                                                  DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
                                                  DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
                                                  &dict );
                
                dbus_message_iter_close_container(&iter1, &dict);

                dbus_error_init( &dbus_error );

                reply = dbus_connection_send_with_reply_and_block( m_dbus_conn,
                                                                   message,
                                                                   DBUS_TIMEOUT_INFINITE,
                                                                   &dbus_error );

                dbus_message_unref( message );

                if ( dbus_error_is_set( &dbus_error ) ) {
                    error_return = AGUIXUtils::formatStringToString( catalog.getLocale( 971 ),
                                                                     dbus_error.message );
                    dbus_error_free( &dbus_error );
                } else {
                    unmount_failed = false;

                    if ( reply ) {
                        dbus_message_unref( reply );
                    }
                }
            }
        }
    }
#endif
    if ( unmount_failed ) return 1;

    update( vol );

    return 0;
}

int HWVolumeManager::cmdUnmount( HWVolume &vol, std::string &error_return )
{
    if ( vol.isMounted() == false ) {
        error_return = catalog.getLocale( 893 );
        return 1;
    }
    if ( ! vol.getUDI().empty() && vol.isInFStab() == false ) {
        error_return = catalog.getLocale( 896 );
        return 1;
    }

    auto fl = std::make_shared< FlagReplacer::FlagProducerMap >();

    if ( ! vol.getSupposedMountPoint().empty() ) {
        fl->registerFlag( "f", vol.getSupposedMountPoint() );
    } else {
        fl->registerFlag( "f", vol.getDevice() );
    }
    fl->registerFlag( "d", vol.getDevice() );

    FlagReplacer rpl( fl );
  
    std::string exestr = rpl.replaceFlags( m_unmount_command );

    ExeClass ec;

    ec.addCommand( "%s", exestr.c_str() );
    int ret = ec.getReturnCode();
    if ( ret != 0 ) {
        std::string error;
        //TODO do not use a hard-coded limit
        ec.readErrorOutput( error, 1024 );

        error_return = error.substr( 0, error.find( '\n' ) );
        return 1;
    }

    update( vol );

    return 0;
}

int HWVolumeManager::dbusEject( HWVolume &vol, std::string &error_return )
{
    if ( vol.getUDI().empty() ) {
        error_return = catalog.getLocale( 923 );
        return 1;
    }

#ifdef HAVE_HAL_DBUS
    DBusMessage *message = NULL;
    DBusMessage *reply = NULL;
    DBusError error;
#endif
    bool eject_failed = true;

#ifdef HAVE_HAL_DBUS
    if ( HALreconnect() == 0 ) {

        dbus_error_init( &error );

        // check whether there is a eject method
        std::list< std::string > method_list;

        try {
            method_list = getStrlistProperty( vol.getUDI().c_str(), "org.freedesktop.Hal.Device.Storage.method_names" );
        } catch ( int ) {
        }

        if ( std::find( method_list.begin(),
                        method_list.end(),
                        "Eject" ) != method_list.end() ) {

            try {
                message = dbus_message_new_method_call( "org.freedesktop.Hal", 
                                                        vol.getUDI().c_str(),
                                                        "org.freedesktop.Hal.Device.Storage",
                                                        "Eject" );
                if ( ! message ) {
                    //TODO translate
                    error_return = "Eject failed: Unable to create DBus message";
                    throw 1;
                }

                const char *options[] = { NULL };
                int num_options = 0;
                const char **options_p = options;
            
                if ( ! dbus_message_append_args( message, 
                                                 DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options_p, num_options,
                                                 DBUS_TYPE_INVALID ) ) {
                    //TODO translate?
                    error_return = "Eject failed: Unable to add arguments to DBus message";
                    throw 2;
                }
            
                reply = dbus_connection_send_with_reply_and_block( m_dbus_conn,
                                                                   message,
                                                                   -1,
                                                                   &error );
                if ( ! reply ) {
                    throw 3;
                }

                if ( dbus_error_is_set( &error ) ) {
                    throw 4;
                }
            
#if 0
                int hal_retcode;

                // I do not do this test anymore, on some systems its INT32
                // on other UINT32
                // beside, other tools (gnome-mount) do not check it either
                if ( ! dbus_message_get_args( reply, 
                                              &error,
                                              DBUS_TYPE_UINT32, &hal_retcode,
                                              DBUS_TYPE_INVALID ) ) {
                    throw 4;
                }
            
                if ( hal_retcode != 0 ) {
                    throw 5;
                }
#endif
                eject_failed = false;
            } catch ( int e ) {
                if ( dbus_error_is_set( &error ) ) {
                    error_return = AGUIXUtils::formatStringToString( catalog.getLocale( 925 ),
                                                                     error.name,
                                                                     error.message );
                    dbus_error_free( &error );
                }
            }

            if ( message ) dbus_message_unref( message );
            if ( reply ) dbus_message_unref( reply );
        } else {
            // no eject method
            //TODO translate?
            error_return = "Eject failed: No Eject method";
        }
    }
#elif defined(HAVE_DBUS)
    if ( ! m_udisks_available &&
         ! m_udisks2_available ) {
        return 1;
    }

    if ( HALreconnect() == 0 ) {
        if ( m_udisks_available ) {
            DBusMessage *message, *reply;
            DBusError dbus_error;
            const char *options[] = { NULL };
            int num_options = 0;

            message = dbus_message_new_method_call( "org.freedesktop.UDisks",
                                                    vol.getUDI().c_str(),
                                                    "org.freedesktop.UDisks.Device",
                                                    "DriveEject" );

            if ( message == NULL ) {
                error_return = "Eject failed: Unable to create DBus message";
            } else {

                const char **options_p = options;
            
                if ( ! dbus_message_append_args( message, 
                                                 DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options_p, num_options,
                                                 DBUS_TYPE_INVALID ) ) {
                    //TODO translate?
                    error_return = "Eject failed: Unable to add arguments to DBus message";
                    dbus_message_unref( message );
                } else {

                    dbus_error_init( &dbus_error );

                    reply = dbus_connection_send_with_reply_and_block( m_dbus_conn,
                                                                       message,
                                                                       -1,
                                                                       &dbus_error );

                    dbus_message_unref( message );

                    if ( dbus_error_is_set( &dbus_error ) ) {
                        error_return = AGUIXUtils::formatStringToString( catalog.getLocale( 972 ),
                                                                         dbus_error.message );
                        dbus_error_free( &dbus_error );
                    } else {
                        eject_failed = false;

                        if ( reply ) {
                            dbus_message_unref( reply );
                        }
                    }
                }
            }
        } else {
            DBusMessage *message, *reply;
            DBusError dbus_error;

            std::string drive;
            try {
                drive = getStringPropertyUDisks2( vol.getUDI(),
                                                  "org.freedesktop.UDisks2.Block",
                                                  "Drive" );
            } catch ( int ) {
            }

            if ( ! drive.empty() ) {
                message = dbus_message_new_method_call( "org.freedesktop.UDisks2",
                                                        drive.c_str(),
                                                        "org.freedesktop.UDisks2.Drive",
                                                        "Eject" );

                if ( message == NULL ) {
                    error_return = "Eject failed: Unable to create DBus message";
                } else {
                    DBusMessageIter iter1, dict;

                    dbus_message_iter_init_append( message, &iter1 );

                    dbus_message_iter_open_container( &iter1,
                                                      DBUS_TYPE_ARRAY,
                                                      DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
                                                      DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
                                                      DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
                                                      &dict );
                
                    dbus_message_iter_close_container(&iter1, &dict);

                    dbus_error_init( &dbus_error );

                    reply = dbus_connection_send_with_reply_and_block( m_dbus_conn,
                                                                       message,
                                                                       -1,
                                                                       &dbus_error );

                    dbus_message_unref( message );

                    if ( dbus_error_is_set( &dbus_error ) ) {
                        error_return = AGUIXUtils::formatStringToString( catalog.getLocale( 972 ),
                                                                         dbus_error.message );
                        dbus_error_free( &dbus_error );
                    } else {
                        eject_failed = false;

                        if ( reply ) {
                            dbus_message_unref( reply );
                        }
                    }
                }
            }
        }
    }
#endif
    if ( eject_failed ) return 1;

    return 0;
}

int HWVolumeManager::cmdEject( HWVolume &vol, std::string &error_return )
{
    auto fl = std::make_shared< FlagReplacer::FlagProducerMap >();

    fl->registerFlag( "f", vol.getDevice() );
    fl->registerFlag( "d", vol.getDevice() );

    FlagReplacer rpl( fl );
  
    std::string exestr = rpl.replaceFlags( m_eject_command );

    ExeClass ec;

    ec.addCommand( "%s", exestr.c_str() );
    int ret = ec.getReturnCode();
    if ( ret != 0 ) {
        std::string error;
        //TODO do not use a hard-coded limit
        ec.readErrorOutput( error, 1024 );

        error_return = error.substr( 0, error.find( '\n' ) );
        return 1;
    }
    return 0;
}

int HWVolumeManager::dbusCloseTray( HWVolume &vol, std::string &error_return )
{
    if ( vol.getUDI().empty() ) {
        error_return = catalog.getLocale( 924 );
        return 1;
    }

#ifdef HAVE_HAL_DBUS
    DBusMessage *message = NULL;
    DBusMessage *reply = NULL;
    DBusError error;
#endif
    bool closetray_failed = true;
    
#ifdef HAVE_HAL_DBUS
    if ( HALreconnect() == 0 ) {

        dbus_error_init( &error );

        // check whether there is a closetray method
        std::list< std::string > method_list;

        try {
            method_list = getStrlistProperty( vol.getUDI().c_str(), "org.freedesktop.Hal.Device.Storage.method_names" );
        } catch ( int ) {
        }

        if ( std::find( method_list.begin(),
                        method_list.end(),
                        "CloseTray" ) != method_list.end() ) {

            try {
                message = dbus_message_new_method_call( "org.freedesktop.Hal", 
                                                        vol.getUDI().c_str(),
                                                        "org.freedesktop.Hal.Device.Storage",
                                                        "CloseTray" );
                if ( ! message ) {
                    //TODO translate?
                    error_return = "CloseTray failed: Unable to create DBus message";
                    throw 1;
                }

                const char *options[] = { NULL };
                int num_options = 0;
                const char **options_p = options;
            
                if ( ! dbus_message_append_args( message, 
                                                 DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options_p, num_options,
                                                 DBUS_TYPE_INVALID ) ) {
                    //TODO translate?
                    error_return = "CloseTray failed: Unable to add arguments to DBus message";
                    throw 2;
                }
            
                reply = dbus_connection_send_with_reply_and_block( m_dbus_conn,
                                                                   message,
                                                                   -1,
                                                                   &error );
                if ( ! reply ) {
                    throw 3;
                }
            
                if ( dbus_error_is_set( &error ) ) {
                    throw 4;
                }

#if 0
                int hal_retcode;

                // see comment in dbusMount
                if ( ! dbus_message_get_args( reply, 
                                              &error,
                                              DBUS_TYPE_UINT32, &hal_retcode,
                                              DBUS_TYPE_INVALID ) ) {
                    throw 4;
                }
            
                if ( hal_retcode != 0 ) {
                    throw 5;
                }
#endif
                closetray_failed = false;
            } catch ( int e ) {
                if ( dbus_error_is_set( &error ) ) {
                    error_return = AGUIXUtils::formatStringToString( catalog.getLocale( 926 ),
                                                                     error.name,
                                                                     error.message );
                    dbus_error_free( &error );
                }
            }

            if ( message ) dbus_message_unref( message );
            if ( reply ) dbus_message_unref( reply );
        } else {
            // no closetray method
            //TODO translate?
            error_return = "CloseTray failed: No CloseTray method";
        }
    }
#elif defined(HAVE_DBUS)
    //TODO implement once udisks supports closing the tray
    error_return = "CloseTray failed: Not supported by udisks";
#endif
    if ( closetray_failed ) return 1;

    return 0;
}

int HWVolumeManager::cmdCloseTray( HWVolume &vol, std::string &error_return )
{
    auto fl = std::make_shared< FlagReplacer::FlagProducerMap >();

    fl->registerFlag( "f", vol.getDevice() );
    fl->registerFlag( "d", vol.getDevice() );

    FlagReplacer rpl( fl );
  
    std::string exestr = rpl.replaceFlags( m_closetray_command );

    ExeClass ec;

    ec.addCommand( "%s", exestr.c_str() );
    int ret = ec.getReturnCode();
    if ( ret != 0 ) {
        std::string error;
        //TODO do not use a hard-coded limit
        ec.readErrorOutput( error, 1024 );

        error_return = error.substr( 0, error.find( '\n' ) );
        return 1;
    }
    return 0;
}

void HWVolumeManager::setPartitionFile( const std::string &file )
{
    if ( ! file.empty() ) {
        m_partition_file = file;
    } else {
        m_partition_file = HW_VM_PARTS;
    }
}

void HWVolumeManager::setFStabFile( const std::string &file )
{
    if ( ! file.empty() ) {
        m_fstab_table = FStabMountTable( file );
    } else {
        m_fstab_table = FStabMountTable( HW_VM_FSTAB );
    }
}

void HWVolumeManager::setMtabFile( const std::string &file )
{
    if ( ! file.empty() ) {
        m_mtab_table = MtabMountTable( file );
    } else {
        m_mtab_table = MtabMountTable( HW_VM_MTAB );
    }
}

int HWVolumeManager::HALconnectionInit()
{
    if ( m_initial_connect_tried &&
         m_dbus_unavailable ) {
        return 1;
    }

#ifdef HAVE_HAL_DBUS
    bool init_failed = false;
    DBusError error;

    if ( HALisConnected() ) return 0;

    dbus_error_init( &error );

    m_hal_ctx = libhal_ctx_new();
    if ( m_hal_ctx != NULL ) {
        
        m_dbus_conn = dbus_bus_get( DBUS_BUS_SYSTEM, &error );
        
        if ( dbus_error_is_set( &error ) ) {
            fprintf( stderr, "DBUS: error %s\n", error.message );
            dbus_error_free( &error );
            init_failed = true;
        } else {
            libhal_ctx_set_dbus_connection( m_hal_ctx,
                                            m_dbus_conn );

            if ( ! libhal_ctx_init( m_hal_ctx, &error ) ) {
		if ( dbus_error_is_set( &error ) ) {
                    fprintf( stderr, "libhal_ctx_init: %s: %s\n", error.name, error.message );
                    dbus_error_free( &error );
		} else {
                    fprintf( stderr, "libhal_ctx_init failed\n" );
		}
                init_failed = true;
            }
        }
    } else {
        init_failed = true;
    }

    if ( init_failed == true ) {
        if ( m_hal_ctx != NULL ) {
            libhal_ctx_free( m_hal_ctx );
            m_hal_ctx = NULL;
        }
        if ( m_dbus_conn != NULL ) {
            dbus_connection_unref( m_dbus_conn );
            m_dbus_conn = NULL;
        }
    }

    if ( init_failed ) {
        if ( ! m_initial_connect_tried ) {
            m_dbus_unavailable = true;
        }
        m_initial_connect_tried = true;
    }

    return ( init_failed == true ) ? 1 : 0;
#elif defined(HAVE_DBUS)
    bool init_failed = false;
    DBusError dbus_error;

    if ( HALisConnected() ) return 0;

    dbus_error_init( &dbus_error );

    m_udisks_available = false;

    m_dbus_conn = dbus_bus_get( DBUS_BUS_SYSTEM, &dbus_error );

    if ( dbus_error_is_set( &dbus_error ) ) {
        fprintf( stderr, "DBUS: error %s\n", dbus_error.message );
        dbus_error_free( &dbus_error );
        init_failed = true;
    }else if ( m_dbus_conn == NULL ){
        fprintf( stderr, "Couldn't connect to system bus\n" );
        init_failed = true;
    } else {
        try {
            if ( getStringProperty( "/org/freedesktop/UDisks",
                                    "DaemonVersion" ) != "" ) {
                m_udisks_available = true;
            }
        } catch ( int ) {
        }

        try {
            m_udisks2_available = true; // because getStringPropertyUDisks2 will check this flag

            if ( getStringPropertyUDisks2( "/org/freedesktop/UDisks2/Manager",
                                           "org.freedesktop.UDisks2.Manager",
                                           "Version" ) != "" ) {
                m_udisks2_available = true;
            } else {
                m_udisks2_available = false;
            }
        } catch ( int ) {
            m_udisks2_available = false;
        }

        if ( m_preferred_udisks_version == 2 &&
             m_udisks_available &&
             m_udisks2_available ) {
            m_udisks_available = false;
        }
    }

    if ( init_failed ) {
        if ( ! m_initial_connect_tried ) {
            m_dbus_unavailable = true;
        }
        m_initial_connect_tried = true;
    }

    return ( init_failed == true ) ? 1 : 0;
#else
    return 0;
#endif
}

int HWVolumeManager::HALconnectionFini()
{
#ifdef HAVE_HAL_DBUS
    DBusError error;

    dbus_error_init( &error );

    if ( m_hal_ctx != NULL ) {
        if ( ! libhal_ctx_shutdown( m_hal_ctx, &error ) ) {
            if ( dbus_error_is_set( &error ) ) {
                fprintf( stderr, "libhal_ctx_shutdown: %s: %s\n", error.name, error.message );
                dbus_error_free( &error );
            } else {
                fprintf( stderr, "libhal_ctx_shutdown failed.\n" );
            }
        }
        
        libhal_ctx_free( m_hal_ctx );
    }
    if ( m_dbus_conn != NULL ) {
        dbus_connection_unref( m_dbus_conn );
    }
    return 0;
#elif defined(HAVE_DBUS)
    if ( m_dbus_conn != NULL ) {
        dbus_connection_unref( m_dbus_conn );
        m_dbus_conn = NULL;
    }
    return 0;
#else
    return 0;
#endif
}

bool HWVolumeManager::HALisConnected()
{
#ifdef HAVE_HAL_DBUS
    if ( m_dbus_conn != NULL ) {
        if ( dbus_connection_get_is_connected( m_dbus_conn ) ) {
            return true;
        }
    }
    return false;
#elif defined(HAVE_DBUS)
    if ( m_dbus_conn != NULL ) {
        if ( dbus_connection_get_is_connected( m_dbus_conn ) ) {
            return true;
        }
    }
    return false;
#else
    return false;
#endif
}

int HWVolumeManager::HALreconnect()
{
#ifdef HAVE_HAL_DBUS
    if ( HALisConnected() ) return 0;

    if ( HALconnectionFini() != 0 ) return 1;

    if ( HALconnectionInit() != 0 ) return 1;

    return 0;
#elif defined(HAVE_DBUS)
    if ( HALisConnected() ) return 0;

    if ( HALconnectionFini() != 0 ) return 1;

    if ( HALconnectionInit() != 0 ) return 1;

    return 0;
#else
    return 0;
#endif
}

std::list<HWVolume> HWVolumeManager::getEjectableDevices()
{
    std::list<HWVolume> l;
#ifdef HAVE_HAL_DBUS
    DBusError error;
    int i;
    int num_devices;
    char **devices;
    const char *udi;
#endif

    m_fstab_table.update();
    m_mtab_table.update();
    m_avail_parts = getAvailablePartitions();
    std::set< w_osdevice_t > managed;

#ifndef HAVE_HAL_DBUS
    std::list< MountTableEntry > fstab_entries = m_fstab_table.getEntries();
    for ( std::list< MountTableEntry >::const_iterator it1 = fstab_entries.begin();
          it1 != fstab_entries.end();
          it1++ ) {
        if ( isDevice( it1->getDevice() ) ) {
            //TODO check for duplicate entries?
            HWVolume v;
            v.setDevice( it1->getDevice() );

            update( v );

            managed.insert( getOSDevice( v.getDevice() ) );
            l.push_back( v );
        }
    }
#endif

#ifdef HAVE_HAL_DBUS
    if ( HALreconnect() == 0 ) {

        dbus_error_init( &error );
        devices = libhal_find_device_by_capability( m_hal_ctx, "storage",
                                                    &num_devices, &error );
        if ( devices != NULL ) {
            for (i = 0; i < num_devices; i++) {
                udi = devices[i];

		// check whether there is a closetray method
		std::list< std::string > method_list;
		
		try {
		    method_list = getStrlistProperty( udi, "org.freedesktop.Hal.Device.Storage.method_names" );
		} catch ( int ) {
		}

		if ( std::find( method_list.begin(),
				method_list.end(),
				"Eject" ) != method_list.end() ) {

		    HWVolume v( udi );
		    
		    update( v );
		    
		    if ( managed.count( getOSDevice( v.getDevice() ) ) < 1 ) {
			managed.insert( getOSDevice( v.getDevice() ) );
			
			l.push_back( v );
		    }
		}
            }
            libhal_free_string_array( devices );
        } else {
            if ( dbus_error_is_set( &error ) ) {
                fprintf( stderr, "HAL Find Devices Error: %s\n", error.message );
                dbus_error_free( &error );
            }
        }
        dbus_error_init( &error );
    }
#elif defined(HAVE_DBUS)
    if ( HALreconnect() == 0 && m_udisks_available)  {
        DBusMessage *message, *reply;
        DBusMessageIter reply_iter, sub_iter;
        DBusError dbus_error;

        message = dbus_message_new_method_call( "org.freedesktop.UDisks",
                                                "/org/freedesktop/UDisks",
                                                "org.freedesktop.UDisks",
                                                "EnumerateDevices" );

        if ( message == NULL ) {
        } else {

            dbus_error_init( &dbus_error );

            reply = dbus_connection_send_with_reply_and_block( m_dbus_conn,
                                                               message,
                                                               -1,
                                                               &dbus_error );

            dbus_message_unref( message );

            if ( dbus_error_is_set( &dbus_error ) ) {
                fprintf( stderr, "Couldn't enumerate udisks devices: %s\n", dbus_error.message );
                dbus_error_free( &dbus_error );
            } else if ( reply == NULL ) {
                fprintf( stderr, "Couldn't enumerate udisks devices: %s\n", dbus_error.message );
            } else {

                dbus_message_iter_init( reply, &reply_iter );

                if ( dbus_message_iter_get_arg_type( &reply_iter ) ==
                     DBUS_TYPE_ARRAY ) {

                    dbus_message_iter_recurse( &reply_iter,
                                               &sub_iter );

                    while ( dbus_message_iter_get_arg_type( &sub_iter ) ==
                            DBUS_TYPE_OBJECT_PATH ) {
                        const char *udi = NULL;

                        dbus_message_iter_get_basic( &sub_iter, &udi );

                        if ( udi ) {
                            bool use_volume = false;

                            try {
                                if ( getBoolProperty( udi, "DriveIsMediaEjectable" ) ) {
                                    use_volume = true;
                                }
                            } catch ( int ) {
                            }

                            // first check whether we should ignore the volume
                            try {
                                if ( getBoolProperty( udi, "DeviceIsSystemInternal" ) ) {
                                    use_volume = false;
                                }
                            } catch ( int ) {
                            }

                            if ( use_volume ) {
                                HWVolume v( udi );

                                update( v );

                                if ( managed.count( getOSDevice( v.getDevice() ) ) < 1 ) {
                                    managed.insert( getOSDevice( v.getDevice() ) );
                
                                    l.push_back( v );
                                }
                            }
                        }

                        dbus_message_iter_next( &sub_iter );
                    }
                }

                dbus_message_unref( reply );
            }
        }
    } else if ( HALreconnect() == 0 && m_udisks2_available)  {
        DBusMessage *message, *reply;
        DBusMessageIter reply_iter, sub_iter;
        DBusError dbus_error;

        message = dbus_message_new_method_call( "org.freedesktop.UDisks2",
                                                "/org/freedesktop/UDisks2",
                                                "org.freedesktop.DBus.ObjectManager",
                                                "GetManagedObjects" );

        if ( message != NULL ) {
            dbus_error_init( &dbus_error );

            reply = dbus_connection_send_with_reply_and_block( m_dbus_conn,
                                                               message,
                                                               -1,
                                                               &dbus_error );

            dbus_message_unref( message );

            if ( dbus_error_is_set( &dbus_error ) ) {
                fprintf( stderr, "Couldn't enumerate udisks devices: %s\n", dbus_error.message );
                dbus_error_free( &dbus_error );
            } else if ( reply == NULL ) {
                fprintf( stderr, "Couldn't enumerate udisks devices: %s\n", dbus_error.message );
            } else {
                dbus_message_iter_init( reply, &reply_iter );

                if ( dbus_message_iter_get_arg_type( &reply_iter ) ==
                     DBUS_TYPE_ARRAY ) {
                    dbus_message_iter_recurse( &reply_iter,
                                               &sub_iter );

                    while ( dbus_message_iter_get_arg_type( &sub_iter ) ==
                            DBUS_TYPE_DICT_ENTRY ) {
                        DBusMessageIter dict_iter;

                        dbus_message_iter_recurse( &sub_iter, &dict_iter );
                        
                        if ( dbus_message_iter_get_arg_type( &dict_iter ) == DBUS_TYPE_OBJECT_PATH ) {
                            const char *udi = NULL;

                            dbus_message_iter_get_basic( &dict_iter, &udi );

                            if ( udi ) {
                                bool use_volume = false;

                                std::string drive;
                                try {
                                    drive = getStringPropertyUDisks2( udi,
                                                                      "org.freedesktop.UDisks2.Block",
                                                                      "Drive" );
                                } catch ( int ) {
                                }

                                // no filesystem but ejectable?
                                if ( ! drive.empty() ) {
                                    try {
                                        if ( getBoolPropertyUDisks2( drive,
                                                                     "org.freedesktop.UDisks2.Drive",
                                                                     "Ejectable" ) &&
                                             ! getBoolPropertyUDisks2( udi,
                                                                       "org.freedesktop.UDisks2.Block",
                                                                       "HintPartitionable" ) ) {
                                            use_volume = true;
                                        }
                                    } catch ( int ) {
                                    }
                                }

                                // first check whether we should ignore the volume
                                try {
                                    if ( getBoolPropertyUDisks2( udi,
                                                                 "org.freedesktop.UDisks2.Block",
                                                                 "HintSystem" ) ) {
                                        use_volume = false;
                                    }
                                } catch ( int ) {
                                }

                                if ( use_volume ) {
                                    HWVolume v( udi );

                                    update( v );

                                    if ( managed.count( getOSDevice( v.getDevice() ) ) < 1 ) {
                                        managed.insert( getOSDevice( v.getDevice() ) );
                
                                        l.push_back( v );
                                    }
                                }
                            }
                        }

                        dbus_message_iter_next( &sub_iter );
                    }
                }

                dbus_message_unref( reply );
            }
        }
    }
#endif

    return l;
}

std::string HWVolumeManager::getDescription( const HWVolume &vol )
{
    if ( vol.getUDI().empty() ) {
        return "";
    }

    std::string descr;

#ifdef HAVE_HAL_DBUS
    DBusError error;

    if ( HALreconnect() == 0 ) {

        dbus_error_init( &error );

        std::string vendor, product;

        try {
            vendor = getStringProperty( vol.getUDI().c_str(), "info.vendor" );
        } catch ( int ) {
        }
        try {
            product = getStringProperty( vol.getUDI().c_str(), "info.product" );
        } catch ( int ) {
        }

        if ( ! vendor.empty() ) {
            descr = vendor;
            descr += " ";
        }
        if ( ! product.empty() ) {
            descr += product;
        }

        if ( dbus_error_is_set( &error ) ) {
            fprintf( stderr, "HAL Error: %s\n", error.message );
            dbus_error_free( &error );
        }
    }
#elif defined(HAVE_DBUS)
    if ( HALreconnect() == 0 ) {

        std::string vendor, product;

        if ( m_udisks_available ) {
            try {
                vendor = getStringProperty( vol.getUDI().c_str(), "DriveVendor" );
            } catch ( int ) {
            }
            try {
                product = getStringProperty( vol.getUDI().c_str(), "DriveModel" );
            } catch ( int ) {
            }
        } else if ( m_udisks2_available ) {
            std::string drive;
            try {
                drive = getStringPropertyUDisks2( vol.getUDI(),
                                                  "org.freedesktop.UDisks2.Block",
                                                  "Drive" );
            } catch ( int ) {
            }


            if ( ! drive.empty() ) {
                try {
                    vendor = getStringPropertyUDisks2( drive, "org.freedesktop.UDisks2.Drive", "Vendor" );
                } catch ( int ) {
                }
                try {
                    product = getStringPropertyUDisks2( drive, "org.freedesktop.UDisks2.Drive", "Model" );
                } catch ( int ) {
                }
            }
        }

        if ( ! vendor.empty() ) {
            descr = vendor;
            descr += " ";
        }
        if ( ! product.empty() ) {
            descr += product;
        }
    }
#endif
    return descr;
}

bool HWVolumeManager::getUDisksAvailable() const
{
    return m_udisks_available;
}

bool HWVolumeManager::getUDisks2Available() const
{
    return m_udisks2_available;
}

bool HWVolumeManager::setPreferredUDisksVersion( int version )
{
    if ( version != 1 && version != 2 ) return false;

    if ( version == m_preferred_udisks_version ) return true;

    m_preferred_udisks_version = version;

    HALconnectionFini();
    HALconnectionInit();

    return true;
}

int HWVolumeManager::retrySystemConnection()
{

    if ( HALisConnected() ) return 0;

    m_dbus_unavailable = false;
    m_initial_connect_tried = false;

    return HALreconnect();
}

bool HWVolumeManager::systemConnectionFailed() const
{
    if ( m_dbus_unavailable ) return true;

    return false;
}
