diff options
Diffstat (limited to 'tdeioslave/media/mediamanager')
24 files changed, 6791 insertions, 0 deletions
diff --git a/tdeioslave/media/mediamanager/CMakeLists.txt b/tdeioslave/media/mediamanager/CMakeLists.txt new file mode 100644 index 000000000..5a73f3708 --- /dev/null +++ b/tdeioslave/media/mediamanager/CMakeLists.txt @@ -0,0 +1,65 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR}/tdeioslave/media/libmediacommon + ${CMAKE_SOURCE_DIR}/tdeioslave/media/libmediacommon + ${CMAKE_BINARY_DIR} + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} + ${HAL_INCLUDE_DIRS} + ${DBUS_TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} + ${DBUS_TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES mediamanager.desktop DESTINATION ${SERVICES_INSTALL_DIR}/kded ) + + +##### kded_mediamanager (module) ################ + +set( target kded_mediamanager ) + +if( WITH_HAL ) + set( ${target}_SRCS + mediamanager.cpp mediamanager.skel medialist.cpp + backendbase.cpp fstabbackend.cpp removablebackend.cpp + mediadirnotify.cpp mediadirnotify.skel + decryptdialog.ui dialog.cpp + halbackend.cpp linuxcdpolling.cpp tdehardwarebackend.cpp + ) + + tde_add_kpart( ${target} AUTOMOC + SOURCES ${${target}_SRCS} + LINK mediacommon-static tdeinit_kded-shared ${HAL_LIBRARIES} -lhal-storage ${DBUS_TQT_LIBRARIES} + DESTINATION ${PLUGIN_INSTALL_DIR} + ) +else( ) + set( ${target}_SRCS + mediamanager.cpp mediamanager.skel medialist.cpp + backendbase.cpp fstabbackend.cpp removablebackend.cpp + decryptdialog.ui dialog.cpp + mediadirnotify.cpp mediadirnotify.skel tdehardwarebackend.cpp + ) + + tde_add_kpart( ${target} AUTOMOC + SOURCES ${${target}_SRCS} + LINK mediacommon-static tdeinit_kded-shared + DESTINATION ${PLUGIN_INSTALL_DIR} + ) +endif( ) diff --git a/tdeioslave/media/mediamanager/Makefile.am b/tdeioslave/media/mediamanager/Makefile.am new file mode 100644 index 000000000..45289a9eb --- /dev/null +++ b/tdeioslave/media/mediamanager/Makefile.am @@ -0,0 +1,32 @@ +kde_module_LTLIBRARIES = kded_mediamanager.la + +if include_media_halbackend +HALBACKEND_INCS = $(HAL_INCS) $(DBUS_INCS) $(DBUSQT_INCS) +endif + +METASOURCES = AUTO +INCLUDES = -I$(srcdir)/../libmediacommon -I../libmediacommon $(HALBACKEND_INCS) $(all_includes) + +if include_media_halbackend +HALBACKEND_LIB = libhalbackend.la +libhalbackend_la_SOURCES = halbackend.cpp +libhalbackend_la_LDFLAGS = -avoid-version $(all_libraries) -no-undefined +libhalbackend_la_LIBADD = $(HAL_LIBS) $(DBUS_LIBS) $(DBUSQT_LIBS) +endif + +if include_media_linuxcdpolling +LINUXCDPOLLING_LIB = liblinuxcdpolling.la +liblinuxcdpolling_la_SOURCES = linuxcdpolling.cpp +liblinuxcdpolling_la_LDFLAGS = -avoid-version $(all_libraries) -no-undefined +endif + +noinst_LTLIBRARIES = $(LINUXCDPOLLING_LIB) $(HALBACKEND_LIB) + +kded_mediamanager_la_SOURCES = mediamanager.cpp mediamanager.skel medialist.cpp backendbase.cpp fstabbackend.cpp removablebackend.cpp mediadirnotify.cpp mediadirnotify.skel +kded_mediamanager_la_LDFLAGS = $(all_libraries) -module -avoid-version +kded_mediamanager_la_LIBADD = $(LIB_KSYCOCA) ../libmediacommon/libmediacommon.la $(HALBACKEND_LIB) $(LINUXCDPOLLING_LIB) + + +servicesdir = $(kde_servicesdir)/kded +services_DATA = mediamanager.desktop + diff --git a/tdeioslave/media/mediamanager/backendbase.cpp b/tdeioslave/media/mediamanager/backendbase.cpp new file mode 100644 index 000000000..157acfca5 --- /dev/null +++ b/tdeioslave/media/mediamanager/backendbase.cpp @@ -0,0 +1,26 @@ +/* This file is part of the KDE Project + Copyright (c) 2004 Kévin Ottens <ervin ipsquad net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "backendbase.h" + +BackendBase::BackendBase(MediaList &list) + : m_mediaList(list) +{ + +} + diff --git a/tdeioslave/media/mediamanager/backendbase.h b/tdeioslave/media/mediamanager/backendbase.h new file mode 100644 index 000000000..689522d1f --- /dev/null +++ b/tdeioslave/media/mediamanager/backendbase.h @@ -0,0 +1,35 @@ +/* This file is part of the KDE Project + Copyright (c) 2004 Kvin Ottens <ervin ipsquad net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _BACKENDBASE_H_ +#define _BACKENDBASE_H_ + +#include "medialist.h" + +class BackendBase +{ +protected: + BackendBase(MediaList &list); +public: + virtual ~BackendBase() { } + +protected: + MediaList &m_mediaList; +}; + +#endif diff --git a/tdeioslave/media/mediamanager/decryptdialog.ui b/tdeioslave/media/mediamanager/decryptdialog.ui new file mode 100644 index 000000000..939f0a36a --- /dev/null +++ b/tdeioslave/media/mediamanager/decryptdialog.ui @@ -0,0 +1,201 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>DecryptDialog</class> +<widget class="TQWidget"> + <property name="name"> + <cstring>DecryptDialog</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>207</width> + <height>172</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="caption"> + <string>Decrypting Storage Device</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout5</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLabel"> + <property name="name"> + <cstring>encryptedIcon</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>48</width> + <height>48</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>32</width> + <height>32</height> + </size> + </property> + <property name="scaledContents"> + <bool>true</bool> + </property> + <property name="alignment"> + <set>AlignTop</set> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer2_2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>31</width> + <height>41</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="TQLabel"> + <property name="name"> + <cstring>descLabel</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string><p><b>%1</b> is an encrypted storage device.</p> +<p>Please enter the password to decrypt the storage device.</p></string> + </property> + <property name="alignment"> + <set>WordBreak|AlignTop</set> + </property> + </widget> + </hbox> + </widget> + <widget class="TQLayoutWidget" row="1" column="0"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>&Password:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>passwordEdit</cstring> + </property> + </widget> + <widget class="TQLineEdit"> + <property name="name"> + <cstring>passwordEdit</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="echoMode"> + <enum>Password</enum> + </property> + </widget> + </hbox> + </widget> + <widget class="TQGroupBox" row="2" column="0"> + <property name="name"> + <cstring>errorBox</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Error</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLabel" row="0" column="0"> + <property name="name"> + <cstring>errorLabel</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string></string> + </property> + <property name="alignment"> + <set>WordBreak|AlignTop</set> + </property> + </widget> + </grid> + </widget> + </grid> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/tdeioslave/media/mediamanager/dialog.cpp b/tdeioslave/media/mediamanager/dialog.cpp new file mode 100644 index 000000000..40e99a84d --- /dev/null +++ b/tdeioslave/media/mediamanager/dialog.cpp @@ -0,0 +1,68 @@ +/* This file is part of the KDE project + * Copyright (C) 2007 Jan Klötzke <jan kloetzke at freenet de> + * + * Based on kryptomedia- Another KDE cryto media application. + * Copyright (C) 2006 Daniel Gollub <dgollub@suse.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "dialog.h" + +Dialog::Dialog(TQString url, TQString iconName) : + KDialogBase(NULL, "Dialog", true, "Decrypt Storage Device", (Cancel|User1), User1, false, KGuiItem(i18n("Decrypt"), "decrypted" )) +{ + decryptDialog = new DecryptDialog(this); + + decryptDialog->errorBox->hide(); + decryptDialog->descLabel->setText(decryptDialog->descLabel->text().arg(url)); + decryptDialog->descLabel->adjustSize(); + decryptDialog->adjustSize(); + + enableButton( User1, false ); + + TQPixmap pixmap = TDEGlobal::iconLoader()->loadIcon(iconName, KIcon::NoGroup, KIcon::SizeLarge); + decryptDialog->encryptedIcon->setPixmap( pixmap ); + + connect(decryptDialog->passwordEdit, TQT_SIGNAL (textChanged(const TQString &)), this, TQT_SLOT (slotPasswordChanged(const TQString &))); + + setMainWidget(decryptDialog); +} + +Dialog::~Dialog() +{ + delete decryptDialog; +} + +TQString Dialog::getPassword() +{ + return decryptDialog->passwordEdit->text(); +} + +void Dialog::slotDialogError(TQString errorMsg) +{ + kdDebug() << __func__ << "(" << errorMsg << " )" << endl; + + decryptDialog->errorLabel->setText(TQString("<b>%1</b>").arg(errorMsg)); + decryptDialog->errorBox->show(); +} + +void Dialog::slotPasswordChanged(const TQString &text) +{ + enableButton( User1, !text.isEmpty() ); +} + +#include "dialog.moc" diff --git a/tdeioslave/media/mediamanager/dialog.h b/tdeioslave/media/mediamanager/dialog.h new file mode 100644 index 000000000..dc1d552d8 --- /dev/null +++ b/tdeioslave/media/mediamanager/dialog.h @@ -0,0 +1,61 @@ +/* This file is part of the KDE project + * Copyright (C) 2007 Jan Klötzke <jan kloetzke at freenet de> + * + * Based on kryptomedia- Another KDE cryto media application. + * Copyright (C) 2006 Daniel Gollub <dgollub@suse.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef DIALOG_H_ +#define DIALOG_H_ + +#include <kmessagebox.h> +#include <klocale.h> +#include <tdeconfig.h> +#include <kdebug.h> +#include <kdialogbase.h> +#include <kiconloader.h> + +#include <tqlineedit.h> +#include <tqlabel.h> +#include <tqgroupbox.h> + +#include "decryptdialog.h" + +class KryptoMedia; + +class Dialog : public KDialogBase +{ + +Q_OBJECT + +public: + Dialog(TQString url, TQString iconName); + ~Dialog(); + + TQString getPassword(); + +public slots: + void slotDialogError(TQString errorMsg); + void slotPasswordChanged(const TQString &text); + +private: + DecryptDialog *decryptDialog; +}; + +#endif // DIALOG_H_ + diff --git a/tdeioslave/media/mediamanager/fstabbackend.cpp b/tdeioslave/media/mediamanager/fstabbackend.cpp new file mode 100644 index 000000000..0069a8e8e --- /dev/null +++ b/tdeioslave/media/mediamanager/fstabbackend.cpp @@ -0,0 +1,483 @@ +/* This file is part of the KDE Project + Copyright (c) 2004 Kévin Ottens <ervin ipsquad net> + Linux CD/DVD detection + Copyright (c) 2005 Bernhard Rosenkraenzer <bero arklinux org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "fstabbackend.h" + +#ifdef __linux__ +// For CD/DVD drive detection +#include <fcntl.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <stdint.h> +#define CDROM_GET_CAPABILITY 0x5331 +#define CDSL_CURRENT ((int) (~0U>>1)) +#define CDC_DVD_R 0x10000 /* drive can write DVD-R */ +#define CDC_DVD_RAM 0x20000 /* drive can write DVD-RAM */ +#define CDC_CD_R 0x2000 /* drive is a CD-R */ +#define CDC_CD_RW 0x4000 /* drive is a CD-RW */ +#define CDC_DVD 0x8000 /* drive is a DVD */ +#include <tqfile.h> +#endif + +#include <klocale.h> +#include <tdeio/job.h> +#include <tdeio/netaccess.h> +#include <kdebug.h> +#include <kdirwatch.h> +#include <kurl.h> +#include <kmountpoint.h> +#include <kstandarddirs.h> + +#ifdef _OS_SOLARIS_ +#define FSTAB "/etc/vfstab" +#define MTAB "/etc/mnttab" +#else +#define FSTAB "/etc/fstab" +#define MTAB "/etc/mtab" +#endif + + + +FstabBackend::FstabBackend(MediaList &list, bool networkSharesOnly) + : TQObject(), BackendBase(list), m_networkSharesOnly(networkSharesOnly) +{ + KDirWatch::self()->addFile(MTAB); + KDirWatch::self()->addFile(FSTAB); + + connect( KDirWatch::self(), TQT_SIGNAL( dirty(const TQString&) ), + this, TQT_SLOT( slotDirty(const TQString&) ) ); + + handleFstabChange(false); + handleMtabChange(false); + + KDirWatch::self()->startScan(); + +#ifdef Q_OS_FREEBSD + connect( &m_mtabTimer, TQT_SIGNAL( timeout() ), + this, TQT_SLOT( handleMtabChange() ) ); + m_mtabTimer.start(250); +#endif +} + +FstabBackend::~FstabBackend() +{ + TQStringList::iterator it = m_mtabIds.begin(); + TQStringList::iterator end = m_mtabIds.end(); + + for (; it!=end; ++it) + { + m_mediaList.removeMedium(*it, false); + } + + it = m_fstabIds.begin(); + end = m_fstabIds.end(); + + for (; it!=end; ++it) + { + m_mediaList.removeMedium(*it, false); + } + KDirWatch::self()->removeFile(FSTAB); + KDirWatch::self()->removeFile(MTAB); +} + +TQString FstabBackend::mount( const TQString &_udi ) +{ + const Medium* medium = m_mediaList.findById(_udi); + if (!medium) + return i18n("No such medium: %1").arg(_udi); + TDEIO::Job* job = TDEIO::mount( false, 0, medium->deviceNode(), medium->mountPoint()); + TDEIO::NetAccess::synchronousRun( job, 0 ); + return TQString::null; +} + +TQString FstabBackend::unmount( const TQString &_udi ) +{ + const Medium* medium = m_mediaList.findById(_udi); + if (!medium) + return i18n("No such medium: %1").arg(_udi); + TDEIO::Job* job = TDEIO::unmount( medium->mountPoint(), false); + TDEIO::NetAccess::synchronousRun( job, 0 ); + return TQString::null; +} + +void FstabBackend::slotDirty(const TQString &path) +{ + if (path==MTAB) + { + handleMtabChange(); + } + else if (path==FSTAB) + { + handleFstabChange(); + } +} + +bool inExclusionPattern(KMountPoint *mount, bool networkSharesOnly) +{ + if ( mount->mountType() == "swap" + || mount->mountType() == "tmpfs" + || mount->mountType() == "sysfs" + || mount->mountType() == "fdescfs" + || mount->mountType() == "kernfs" + || mount->mountType() == "usbfs" + || mount->mountType().contains( "proc" ) + || mount->mountType() == "unknown" + || mount->mountType() == "none" + || mount->mountType() == "sunrpc" + || mount->mountedFrom() == "none" + || mount->mountedFrom() == "tmpfs" + || mount->mountedFrom().find("shm") != -1 + || mount->mountPoint() == "/dev/swap" + || mount->mountPoint() == "/dev/pts" + || mount->mountPoint().find("/proc") == 0 + || mount->mountPoint().find("/sys") == 0 + + // We might want to display only network shares + // since HAL doesn't handle them + || ( networkSharesOnly + && mount->mountType().find( "smb" ) == -1 + && mount->mountType().find( "cifs" ) == -1 + && mount->mountType().find( "nfs" ) == -1 + ) + ) + { + return true; + } + else + { + return false; + } +} + + +void FstabBackend::handleMtabChange(bool allowNotification) +{ + TQStringList new_mtabIds; + KMountPoint::List mtab = KMountPoint::currentMountPoints(); + + KMountPoint::List::iterator it = mtab.begin(); + KMountPoint::List::iterator end = mtab.end(); + + for (; it!=end; ++it) + { + TQString dev = (*it)->mountedFrom(); + TQString mp = (*it)->mountPoint(); + TQString fs = (*it)->mountType(); + + if ( ::inExclusionPattern(*it, m_networkSharesOnly) ) continue; + + /* Did we know this already before ? If yes, then + nothing has changed, do not stat the mount point. Avoids + hang if network shares are stalling */ + TQString mtabEntry = dev + "*" + mp + "*" + fs; + if(m_mtabEntries.contains(mtabEntry)) { + new_mtabIds += m_mtabEntries[mtabEntry]; + continue; + } + + TQString id = generateId(dev, mp); + new_mtabIds+=id; + m_mtabEntries[mtabEntry] = id; + + if ( !m_mtabIds.contains(id) && m_fstabIds.contains(id) ) + { + TQString mime, icon, label; + guess(dev, mp, fs, true, mime, icon, label); + m_mediaList.changeMediumState(id, true, false, + mime, icon, label); + } +#if 0 + else if ( !m_mtabIds.contains(id) ) + { + TQString name = generateName(dev, fs); + + Medium *m = new Medium(id, name); + + m->mountableState(dev, mp, fs, true); + + TQString mime, icon, label; + guess(dev, mp, fs, true, mime, icon, label); + + m->setMimeType(mime); + m->setIconName(icon); + m->setLabel(label); + + m_mediaList.addMedium(m, notificationAllowed); + } +#endif + } + + TQStringList::iterator it2 = m_mtabIds.begin(); + TQStringList::iterator end2 = m_mtabIds.end(); + + for (; it2!=end2; ++it2) + { + if ( !new_mtabIds.contains(*it2) && m_fstabIds.contains(*it2) ) + { + const Medium *medium = m_mediaList.findById(*it2); + + TQString dev = medium->deviceNode(); + TQString mp = medium->mountPoint(); + TQString fs = medium->fsType(); + + + TQString mtabEntry = dev + "*" + mp + "*" + fs; + m_mtabEntries.remove(mtabEntry); + + TQString mime, icon, label; + guess(dev, mp, fs, false, mime, icon, label); + + m_mediaList.changeMediumState(*it2, false, false, + mime, icon, label); + } +#if 0 + else if ( !new_mtabIds.contains(*it2) ) + { + m_mediaList.removeMedium(*it2, allowNotification); + } +#endif + } + + m_mtabIds = new_mtabIds; +} + +void FstabBackend::handleFstabChange(bool allowNotification) +{ + TQStringList new_fstabIds; + KMountPoint::List fstab = KMountPoint::possibleMountPoints(); + + KMountPoint::List::iterator it = fstab.begin(); + KMountPoint::List::iterator end = fstab.end(); + + for (; it!=end; ++it) + { + TQString dev = (*it)->mountedFrom(); + TQString mp = (*it)->mountPoint(); + TQString fs = (*it)->mountType(); + + if ( ::inExclusionPattern(*it, m_networkSharesOnly) ) continue; + + TQString id = generateId(dev, mp); + new_fstabIds+=id; + + if ( !m_fstabIds.contains(id) ) + { + TQString name = generateName(dev, fs); + + Medium *m = new Medium(id, id, name); + + m->mountableState(dev, mp, fs, false); + + TQString mime, icon, label; + guess(dev, mp, fs, false, mime, icon, label); + + m->setMimeType(mime); + m->setIconName(icon); + m->setLabel(label); + + m_mediaList.addMedium(m, allowNotification); + } + } + + TQStringList::iterator it2 = m_fstabIds.begin(); + TQStringList::iterator end2 = m_fstabIds.end(); + + for (; it2!=end2; ++it2) + { + if ( !new_fstabIds.contains(*it2) ) + { + m_mediaList.removeMedium(*it2, allowNotification); + } + } + + m_fstabIds = new_fstabIds; +} + +TQString FstabBackend::generateId(const TQString &devNode, + const TQString &mountPoint) +{ + TQString d = KStandardDirs::realFilePath(devNode); + TQString m = KStandardDirs::realPath(mountPoint); + + return "/org/kde/mediamanager/fstab/" + +d.replace("/", "") + +m.replace("/", ""); +} + +TQString FstabBackend::generateName(const TQString &devNode, const TQString &fsType) +{ + KURL url( devNode ); + + if ( url.isValid() ) + { + return url.fileName(); + } + else // surely something nfs or samba based + { + return fsType; + } +} + +void FstabBackend::guess(const TQString &devNode, const TQString &mountPoint, + const TQString &fsType, bool mounted, + TQString &mimeType, TQString &iconName, TQString &label) +{ + enum { UNKNOWN, CD, CDWRITER, DVD, DVDWRITER } devType = UNKNOWN; +#ifdef __linux__ + // Guessing device types by mount point is not exactly accurate... + // Do something accurate first, and fall back if necessary. + int device=open(TQFile::encodeName(devNode), O_RDONLY|O_NONBLOCK); + if(device>=0) + { + bool isCd=false; + TQString devname=devNode.section('/', -1); + if(devname.startsWith("scd") || devname.startsWith("sr")) + { + // SCSI CD/DVD drive + isCd=true; + } + else if(devname.startsWith("hd")) + { + // IDE device -- we can't tell if this is a + // CD/DVD drive or harddisk by just looking at the + // filename + TQFile m(TQString("/proc/ide/") + devname + "/media"); + if(m.open(IO_ReadOnly)) + { + TQString buf; + m.readLine(buf, 1024); + if(buf.contains("cdrom")) + isCd=true; + m.close(); + } + } + if(isCd) + { + int drv=ioctl(device, CDROM_GET_CAPABILITY, CDSL_CURRENT); + if(drv>=0) + { + if((drv & CDC_DVD_R) || (drv & CDC_DVD_RAM)) + devType = DVDWRITER; + else if((drv & CDC_CD_R) || (drv & CDC_CD_RW)) + devType = CDWRITER; + else if(drv & CDC_DVD) + devType = DVD; + else + devType = CD; + } + } + close(device); + } +#endif + if ( devType == CDWRITER + || devNode.find("cdwriter")!=-1 || mountPoint.find("cdwriter")!=-1 + || devNode.find("cdrecorder")!=-1 || mountPoint.find("cdrecorder")!=-1 + || devNode.find("cdburner")!=-1 || mountPoint.find("cdburner")!=-1 + || devNode.find("cdrw")!=-1 || mountPoint.find("cdrw")!=-1 + || devNode.find("graveur")!=-1 + ) + { + mimeType = "media/cdwriter"; + label = i18n("CD Recorder"); + } + else if ( devType == DVD || devType == DVDWRITER + || devNode.find("dvd")!=-1 || mountPoint.find("dvd")!=-1 ) + { + mimeType = "media/dvd"; + label = i18n("DVD"); + } + else if ( devType == CD + || devNode.find("cdrom")!=-1 || mountPoint.find("cdrom")!=-1 + // LINUX SPECIFIC + || devNode.find("/dev/scd")!=-1 || devNode.find("/dev/sr")!=-1 + // FREEBSD SPECIFIC + || devNode.find("/acd")!=-1 || devNode.find("/scd")!=-1 + ) + { + mimeType = "media/cdrom"; + label = i18n("CD-ROM"); + } + else if ( devNode.find("fd")!=-1 || mountPoint.find("fd")!=-1 + || devNode.find("floppy")!=-1 || mountPoint.find("floppy")!=-1 ) + { + if ( devNode.find("360")!=-1 || devNode.find("1200")!=-1 ) + { + mimeType = "media/floppy5"; + } + else + { + mimeType = "media/floppy"; + } + label = i18n("Floppy"); + } + else if ( mountPoint.find("zip")!=-1 + // FREEBSD SPECIFIC + || devNode.find("/afd")!=-1 + ) + { + mimeType = "media/zip"; + label = i18n("Zip Disk"); + } + else if ( mountPoint.find("removable")!=-1 + || mountPoint.find("hotplug")!=-1 + || mountPoint.find("usb")!=-1 + || mountPoint.find("firewire")!=-1 + || mountPoint.find("ieee1394")!=-1 + || devNode.find("/usb/")!= -1 + ) + { + mimeType = "media/removable"; + label = i18n("Removable Device"); + } + else if ( fsType.find("nfs")!=-1 ) + { + mimeType = "media/nfs"; + label = i18n("Remote Share"); + } + else if ( fsType.find("smb")!=-1 || fsType.find("cifs")!=-1 + || devNode.find("//")!=-1 ) + { + mimeType = "media/smb"; + label = i18n("Remote Share"); + } + else + { + mimeType = "media/hdd"; + label = i18n("Hard Disk"); + } + + if ( mimeType=="media/nfs" || mimeType=="media/smb" ) + { + label+= " (" + devNode + ")"; + } + else + { + TQString tmp = devNode; + if ( tmp.startsWith("/dev/") ) + { + tmp = tmp.mid(5); + } + label+= " (" + tmp + ")"; + } + mimeType+= (mounted ? "_mounted" : "_unmounted"); + iconName = TQString::null; +} + +#include "fstabbackend.moc" diff --git a/tdeioslave/media/mediamanager/fstabbackend.h b/tdeioslave/media/mediamanager/fstabbackend.h new file mode 100644 index 000000000..e43ed48c0 --- /dev/null +++ b/tdeioslave/media/mediamanager/fstabbackend.h @@ -0,0 +1,68 @@ +/* This file is part of the KDE Project + Copyright (c) 2004 Kvin Ottens <ervin ipsquad net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _FSTABBACKEND_H_ +#define _FSTABBACKEND_H_ + +#include "backendbase.h" + +#include <tqobject.h> +#include <tqstringlist.h> +#include <tqmap.h> + +#ifdef Q_OS_FREEBSD +#include <tqtimer.h> +#endif + +class FstabBackend : public TQObject, public BackendBase +{ +Q_OBJECT + +public: + FstabBackend(MediaList &list, bool networkSharesOnly = false); + virtual ~FstabBackend(); + + static void guess(const TQString &devNode, const TQString &mountPoint, + const TQString &fsType, bool mounted, + TQString &mimeType, TQString &iconName, + TQString &label); + + TQString mount(const TQString &id); + TQString unmount(const TQString &id); + +private slots: + void slotDirty(const TQString &path); + void handleFstabChange(bool allowNotification = true); + void handleMtabChange(bool allowNotification = true); + +private: + static TQString generateId(const TQString &devNode, + const TQString &mountPoint); + static TQString generateName(const TQString &devNode, + const TQString &fsType); + + bool m_networkSharesOnly; + TQStringList m_mtabIds; + TQMap<TQString, TQString> m_mtabEntries; + TQStringList m_fstabIds; +#ifdef Q_OS_FREEBSD + TQTimer m_mtabTimer; +#endif +}; + +#endif diff --git a/tdeioslave/media/mediamanager/halbackend.cpp b/tdeioslave/media/mediamanager/halbackend.cpp new file mode 100644 index 000000000..aba2b0175 --- /dev/null +++ b/tdeioslave/media/mediamanager/halbackend.cpp @@ -0,0 +1,1858 @@ +/* This file is part of the KDE Project + Copyright (c) 2004-2005 Jérôme Lodewyck <jerome dot lodewyck at normalesup dot org> + Copyright (c) 2006 Valentine Sinitsyn <e_val@inbox.ru> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "halbackend.h" +#include "linuxcdpolling.h" + +#include <stdlib.h> +#include <locale.h> + +#include <kapplication.h> +#include <kmessagebox.h> +#include <tqeventloop.h> +#include <tqfile.h> +#include <klocale.h> +#include <kurl.h> +#include <kdebug.h> +#include <kprocess.h> +#include <tdeconfig.h> +#include <tqstylesheet.h> +#include <kmountpoint.h> +#include <kmessagebox.h> +#include <tdeio/job.h> +#include <kprotocolinfo.h> +#include <kstandarddirs.h> +#include <kprocess.h> + +#define MOUNT_SUFFIX ( \ + (medium->isMounted() ? TQString("_mounted") : TQString("_unmounted")) + \ + (medium->isEncrypted() ? (halClearVolume ? "_decrypted" : "_encrypted") : "" ) \ + ) +#define MOUNT_ICON_SUFFIX ( \ + (medium->isMounted() ? TQString("_mount") : TQString("_unmount")) + \ + (medium->isEncrypted() ? (halClearVolume ? "_decrypt" : "_encrypt") : "" ) \ + ) + +/* Static instance of this class, for static HAL callbacks */ +static HALBackend* s_HALBackend; + +/* A macro function to convert HAL string properties to TQString */ +TQString libhal_device_get_property_QString(LibHalContext *ctx, const char* udi, const char *key) +{ + char* _ppt_string; + TQString _ppt_QString; + _ppt_string = libhal_device_get_property_string(ctx, udi, key, NULL); + if ( _ppt_string ) + _ppt_QString = _ppt_string; + libhal_free_string(_ppt_string); + return _ppt_QString; +} + +/* Constructor */ +HALBackend::HALBackend(MediaList &list, TQObject* parent) + : TQObject() + , BackendBase(list) + , m_halContext(NULL) + , m_halStoragePolicy(NULL) + , m_parent(parent) +{ + s_HALBackend = this; +} + +/* Destructor */ +HALBackend::~HALBackend() +{ + /* Close HAL connection */ + if (m_halContext) + { + const TQPtrList<Medium> medlist = m_mediaList.list(); + TQPtrListIterator<Medium> it (medlist); + for ( const Medium *current_medium = it.current(); current_medium; current_medium = ++it) + { + if( !current_medium->id().startsWith( "/org/kde" )) + unmount(current_medium->id()); + } + + + /* Remove all the registered media first */ + int numDevices; + char** halDeviceList = libhal_get_all_devices( m_halContext, &numDevices, NULL ); + + if ( halDeviceList ) + { + for ( int i = 0; i < numDevices; i++ ) + { + m_mediaList.removeMedium( halDeviceList[i], false ); + } + } + + libhal_free_string_array( halDeviceList ); + + DBusError error; + dbus_error_init(&error); + libhal_ctx_shutdown(m_halContext, &error); + libhal_ctx_free(m_halContext); + } + + if (m_halStoragePolicy) + libhal_storage_policy_free(m_halStoragePolicy); +} + +/* Connect to the HAL */ +bool HALBackend::InitHal() +{ + kdDebug(1219) << "Context new" << endl; + m_halContext = libhal_ctx_new(); + if (!m_halContext) + { + kdDebug(1219) << "Failed to initialize HAL!" << endl; + return false; + } + + // Main loop integration + kdDebug(1219) << "Main loop integration" << endl; + DBusError error; + dbus_error_init(&error); + dbus_connection = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); + + if (!dbus_connection || dbus_error_is_set(&error)) { + dbus_error_free(&error); + libhal_ctx_free(m_halContext); + m_halContext = NULL; + return false; + } + + dbus_connection_set_exit_on_disconnect (dbus_connection, FALSE); + + MainLoopIntegration(dbus_connection); + libhal_ctx_set_dbus_connection(m_halContext, dbus_connection); + + // HAL callback functions + kdDebug(1219) << "Callback functions" << endl; + libhal_ctx_set_device_added(m_halContext, HALBackend::hal_device_added); + libhal_ctx_set_device_removed(m_halContext, HALBackend::hal_device_removed); + libhal_ctx_set_device_new_capability (m_halContext, NULL); + libhal_ctx_set_device_lost_capability (m_halContext, NULL); + libhal_ctx_set_device_property_modified (m_halContext, HALBackend::hal_device_property_modified); + libhal_ctx_set_device_condition(m_halContext, HALBackend::hal_device_condition); + + kdDebug(1219) << "Context Init" << endl; + if (!libhal_ctx_init(m_halContext, &error)) + { + if (dbus_error_is_set(&error)) + dbus_error_free(&error); + libhal_ctx_free(m_halContext); + m_halContext = NULL; + kdDebug(1219) << "Failed to init HAL context!" << endl; + return false; + } + + /** @todo customize watch policy */ + kdDebug(1219) << "Watch properties" << endl; + if (!libhal_device_property_watch_all(m_halContext, &error)) + { + kdDebug(1219) << "Failed to watch HAL properties!" << endl; + return false; + } + + /* libhal-storage initialization */ + kdDebug(1219) << "Storage Policy" << endl; + m_halStoragePolicy = libhal_storage_policy_new(); + /** @todo define libhal-storage icon policy */ + + /* List devices at startup */ + return ListDevices(); +} + +/* List devices (at startup)*/ +bool HALBackend::ListDevices() +{ + kdDebug(1219) << "ListDevices" << endl; + + int numDevices; + char** halDeviceList = libhal_get_all_devices(m_halContext, &numDevices, NULL); + + if (!halDeviceList) + return false; + + kdDebug(1219) << "HALBackend::ListDevices : " << numDevices << " devices found" << endl; + for (int i = 0; i < numDevices; i++) + AddDevice(halDeviceList[i], false); + + libhal_free_string_array( halDeviceList ); + + return true; +} + +/* Create a media instance for the HAL device "udi". + This functions checks whether the device is worth listing */ +void HALBackend::AddDevice(const char *udi, bool allowNotification) +{ + /* We don't deal with devices that do not expose their capabilities. + If we don't check this, we will get a lot of warning messages from libhal */ + if (!libhal_device_property_exists(m_halContext, udi, "info.capabilities", NULL)) + return; + + /* If the device is already listed, do not process. + This should not happen, but who knows... */ + /** @todo : refresh properties instead ? */ + if (m_mediaList.findById(udi)) + return; + + if (libhal_device_get_property_bool(m_halContext, "/org/freedesktop/Hal/devices/computer", "storage.disable_volume_handling", NULL)) + allowNotification=false; + + /* Add volume block devices */ + if (libhal_device_query_capability(m_halContext, udi, "volume", NULL)) + { + /* We only list volumes that... + * - are encrypted with LUKS or + * - have a filesystem or + * - have an audio track + */ + if ( ( libhal_device_get_property_QString(m_halContext, udi, "volume.fsusage") != "crypto" || + libhal_device_get_property_QString(m_halContext, udi, "volume.fstype") != "crypto_LUKS" + ) && + libhal_device_get_property_QString(m_halContext, udi, "volume.fsusage") != "filesystem" && + !libhal_device_get_property_bool(m_halContext, udi, "volume.disc.has_audio", NULL) && + !libhal_device_get_property_bool(m_halContext, udi, "volume.disc.is_blank", NULL) ) + return; + + /* Query drive udi */ + TQString driveUdi = libhal_device_get_property_QString(m_halContext, udi, "block.storage_device"); + if ( driveUdi.isNull() ) // no storage - no fun + return; + + // if the device is locked do not act upon it + if (libhal_device_get_property_bool(m_halContext, driveUdi.ascii(), "info.locked", NULL)) + allowNotification=false; + + // if the device is locked do not act upon it + if (libhal_device_get_property_bool(m_halContext, driveUdi.ascii(), "storage.partition_table_changed", NULL)) + allowNotification=false; + + /** @todo check exclusion list **/ + + /* Special handling for clear crypto volumes */ + LibHalVolume* halVolume = libhal_volume_from_udi(m_halContext, udi); + if (!halVolume) + return; + const char* backingVolumeUdi = libhal_volume_crypto_get_backing_volume_udi(halVolume); + if ( backingVolumeUdi != NULL ) + { + /* The crypto drive was unlocked and may now be mounted... */ + kdDebug(1219) << "HALBackend::AddDevice : ClearVolume appeared for " << backingVolumeUdi << endl; + ResetProperties(backingVolumeUdi, allowNotification); + libhal_volume_free(halVolume); + return; + } + libhal_volume_free(halVolume); + + /* Create medium */ + Medium* medium = new Medium(udi, udi, ""); + setVolumeProperties(medium); + + if ( isInFstab( medium ).isNull() ) + { + // if it's not mountable by user and not by HAL, don't show it at all + if ( ( libhal_device_get_property_QString(m_halContext, udi, "volume.fsusage") == "filesystem" && + !libhal_device_get_property_bool(m_halContext, udi, "volume.is_mounted", NULL ) ) && + ( libhal_device_get_property_bool(m_halContext, udi, "volume.ignore", NULL ) ) ) + { + delete medium; + return; + } + } + + // instert medium into list + m_mediaList.addMedium(medium, allowNotification); + + // finally check for automount + TQMap<TQString,TQString> options = MediaManagerUtils::splitOptions(mountoptions(udi)); + kdDebug() << "automount " << options["automount"] << endl; + if (options["automount"] == "true" && allowNotification ) { + TQString error = mount(medium); + if (!error.isEmpty()) + kdDebug() << "error " << error << endl; + } + + return; + } + + /* Floppy & zip drives */ + if (libhal_device_query_capability(m_halContext, udi, "storage", NULL)) + if ((libhal_device_get_property_QString(m_halContext, udi, "storage.drive_type") == "floppy") || + (libhal_device_get_property_QString(m_halContext, udi, "storage.drive_type") == "zip") || + (libhal_device_get_property_QString(m_halContext, udi, "storage.drive_type") == "jaz")) + { + if (! libhal_device_get_property_bool(m_halContext, udi, "storage.removable.media_available", NULL) ) + allowNotification = false; + /* Create medium */ + Medium* medium = new Medium(udi, udi, ""); + // if the storage has a volume, we ignore it + if ( setFloppyProperties(medium) ) + m_mediaList.addMedium(medium, allowNotification); + else + delete medium; + return; + } + + /* Camera handled by gphoto2*/ + if (libhal_device_query_capability(m_halContext, udi, "camera", NULL) && + ((libhal_device_get_property_QString(m_halContext, udi, "camera.access_method")=="ptp") || + + (libhal_device_property_exists(m_halContext, udi, "camera.libgphoto2.support", NULL) && + libhal_device_get_property_bool(m_halContext, udi, "camera.libgphoto2.support", NULL))) + ) + { + /* Create medium */ + Medium* medium = new Medium(udi, udi, ""); + setCameraProperties(medium); + m_mediaList.addMedium(medium, allowNotification); + return; + } +} + +void HALBackend::RemoveDevice(const char *udi) +{ + const Medium *medium = m_mediaList.findByClearUdi(udi); + if (medium) { + ResetProperties(medium->id().ascii()); + } else { + m_mediaList.removeMedium(udi, true); + } +} + +void HALBackend::ModifyDevice(const char *udi, const char* key) +{ + kdDebug(1219) << "HALBackend::ModifyDevice for '" << udi << "' on '" << key << "'\n"; + + const char* mediumUdi = findMediumUdiFromUdi(udi); + if (!mediumUdi) + return; + bool allowNotification = false; + if (strcmp(key, "storage.removable.media_available") == 0) + allowNotification = libhal_device_get_property_bool(m_halContext, udi, key, NULL); + ResetProperties(mediumUdi, allowNotification); +} + +void HALBackend::DeviceCondition(const char* udi, const char* condition) +{ + TQString conditionName = TQString(condition); + kdDebug(1219) << "Processing device condition " << conditionName << " for " << udi << endl; + + if (conditionName == "EjectPressed") { + const Medium* medium = m_mediaList.findById(udi); + if (!medium) { + /* the ejectpressed appears on the drive and we need to find the volume */ + const TQPtrList<Medium> medlist = m_mediaList.list(); + TQPtrListIterator<Medium> it (medlist); + for ( const Medium *current_medium = it.current(); current_medium; current_medium = ++it) + { + if( current_medium->id().startsWith( "/org/kde" )) + continue; + TQString driveUdi = libhal_device_get_property_QString(m_halContext, current_medium->id().latin1(), "block.storage_device"); + if (driveUdi == udi) + { + medium = current_medium; + break; + } + } + } + if (medium) { + TDEProcess p; + p << "kio_media_mounthelper" << "-e" << medium->name(); + p.start(TDEProcess::DontCare); + } + } + + const char* mediumUdi = findMediumUdiFromUdi(udi); + kdDebug() << "findMedumUdiFromUdi " << udi << " returned " << mediumUdi << endl; + if (!mediumUdi) + return; + + /* TODO: Warn the user that (s)he should unmount devices before unplugging */ + if (conditionName == "VolumeUnmountForced") + ResetProperties(mediumUdi); + + /* Reset properties after mounting */ + if (conditionName == "VolumeMount") + ResetProperties(mediumUdi); + + /* Reset properties after unmounting */ + if (conditionName == "VolumeUnmount") + ResetProperties(mediumUdi); + +} + +void HALBackend::MainLoopIntegration(DBusConnection *dbusConnection) +{ + m_dBusQtConnection = new DBusQt::Connection(m_parent); + m_dBusQtConnection->dbus_connection_setup_with_qt_main(dbusConnection); +} + +/****************************************** + ** Properties attribution ** + ******************************************/ + +/* Return the medium udi that should be updated when recieving a call for + device udi */ +const char* HALBackend::findMediumUdiFromUdi(const char* udi) +{ + /* Easy part : this Udi is already registered as a device */ + const Medium* medium = m_mediaList.findById(udi); + if (medium) + return medium->id().ascii(); + + /* Hard part : this is a volume whose drive is registered */ + if (libhal_device_property_exists(m_halContext, udi, "info.capabilities", NULL)) + if (libhal_device_query_capability(m_halContext, udi, "volume", NULL)) + { + /* check if this belongs to an encrypted volume */ + LibHalVolume* halVolume = libhal_volume_from_udi(m_halContext, udi); + if (!halVolume) return NULL; + const char* backingUdi = libhal_volume_crypto_get_backing_volume_udi(halVolume); + if (backingUdi != NULL) { + const char* result = findMediumUdiFromUdi(backingUdi); + libhal_volume_free(halVolume); + return result; + } + libhal_volume_free(halVolume); + + /* this is a volume whose drive is registered */ + TQString driveUdi = libhal_device_get_property_QString(m_halContext, udi, "block.storage_device"); + return findMediumUdiFromUdi(driveUdi.ascii()); + } + + return NULL; +} + +void HALBackend::ResetProperties(const char* mediumUdi, bool allowNotification) +{ + kdDebug(1219) << "HALBackend::setProperties" << endl; + if ( TQString::fromLatin1( mediumUdi ).startsWith( "/org/kde/" ) ) + { + const Medium *cmedium = m_mediaList.findById(mediumUdi); + if ( cmedium ) + { + Medium m( *cmedium ); + if ( setFstabProperties( &m ) ) { + kdDebug() << "setFstabProperties worked" << endl; + m_mediaList.changeMediumState(m, allowNotification); + } + return; + } + } + + Medium* m = new Medium(mediumUdi, mediumUdi, ""); + + if (libhal_device_query_capability(m_halContext, mediumUdi, "volume", NULL)) + setVolumeProperties(m); + if (libhal_device_query_capability(m_halContext, mediumUdi, "storage", NULL)) + setFloppyProperties(m); + if (libhal_device_query_capability(m_halContext, mediumUdi, "camera", NULL)) + setCameraProperties(m); + + m_mediaList.changeMediumState(*m, allowNotification); + + delete m; +} + +void HALBackend::setVolumeProperties(Medium* medium) +{ + kdDebug(1219) << "HALBackend::setVolumeProperties for " << medium->id() << endl; + + const char* udi = medium->id().ascii(); + /* Check if the device still exists */ + if (!libhal_device_exists(m_halContext, udi, NULL)) + return; + + /* Get device information from libhal-storage */ + LibHalVolume* halVolume = libhal_volume_from_udi(m_halContext, udi); + if (!halVolume) + return; + TQString driveUdi = libhal_volume_get_storage_device_udi(halVolume); + LibHalDrive* halDrive = 0; + if ( !driveUdi.isNull() ) + halDrive = libhal_drive_from_udi(m_halContext, driveUdi.ascii()); + if (!halDrive) { + // at times HAL sends an UnmountForced event before the device is removed + libhal_volume_free(halVolume); + return; + } + + medium->setName( + generateName(libhal_volume_get_device_file(halVolume)) ); + + LibHalVolume* halClearVolume = NULL; + if ( libhal_device_get_property_QString(m_halContext, udi, "volume.fsusage") == "crypto" ) + { + kdDebug(1219) << "HALBackend::setVolumeProperties : crypto volume" << endl; + + medium->setEncrypted(true); + char* clearUdi = libhal_volume_crypto_get_clear_volume_udi(m_halContext, halVolume); + TQString clearUdiString; + if (clearUdi != NULL) { + kdDebug(1219) << "HALBackend::setVolumeProperties : crypto clear volume avail - " << clearUdi << endl; + halClearVolume = libhal_volume_from_udi(m_halContext, clearUdi); + // ignore if halClearVolume is NULL -> just not decrypted in this case + clearUdiString = clearUdi; + libhal_free_string(clearUdi); + } + + if (halClearVolume) + medium->mountableState( + libhal_volume_get_device_file(halVolume), /* Device node */ + clearUdiString, + libhal_volume_get_mount_point(halClearVolume), /* Mount point */ + libhal_volume_get_fstype(halClearVolume), /* Filesystem type */ + libhal_volume_is_mounted(halClearVolume) ); /* Mounted ? */ + else + medium->mountableState( + libhal_volume_get_device_file(halVolume), /* Device node */ + TQString::null, + TQString::null, /* Mount point */ + TQString::null, /* Filesystem type */ + false ); /* Mounted ? */ + } + else + { + kdDebug(1219) << "HALBackend::setVolumeProperties : normal volume" << endl; + medium->mountableState( + libhal_volume_get_device_file(halVolume), /* Device node */ + libhal_volume_get_mount_point(halVolume), /* Mount point */ + libhal_volume_get_fstype(halVolume), /* Filesystem type */ + libhal_volume_is_mounted(halVolume) ); /* Mounted ? */ + } + + + char* name = libhal_volume_policy_compute_display_name(halDrive, halVolume, m_halStoragePolicy); + TQString volume_name = TQString::fromUtf8(name); + TQString media_name = volume_name; + medium->setLabel(media_name); + free(name); + + TQString mimeType; + if (libhal_volume_is_disc(halVolume)) + { + mimeType = "media/cdrom" + MOUNT_SUFFIX; + + LibHalVolumeDiscType discType = libhal_volume_get_disc_type(halVolume); + if ((discType == LIBHAL_VOLUME_DISC_TYPE_CDROM) || + (discType == LIBHAL_VOLUME_DISC_TYPE_CDR) || + (discType == LIBHAL_VOLUME_DISC_TYPE_CDRW)) + if (libhal_volume_disc_is_blank(halVolume)) + { + mimeType = "media/blankcd"; + medium->unmountableState(""); + } + else + mimeType = "media/cdwriter" + MOUNT_SUFFIX; + + if ((discType == LIBHAL_VOLUME_DISC_TYPE_DVDROM) || (discType == LIBHAL_VOLUME_DISC_TYPE_DVDRAM) || + (discType == LIBHAL_VOLUME_DISC_TYPE_DVDR) || (discType == LIBHAL_VOLUME_DISC_TYPE_DVDRW) || + (discType == LIBHAL_VOLUME_DISC_TYPE_DVDPLUSR) || (discType == LIBHAL_VOLUME_DISC_TYPE_DVDPLUSRW) ) + if (libhal_volume_disc_is_blank(halVolume)) + { + mimeType = "media/blankdvd"; + medium->unmountableState(""); + } + else + mimeType = "media/dvd" + MOUNT_SUFFIX; + + if (libhal_volume_disc_has_audio(halVolume) && !libhal_volume_disc_has_data(halVolume)) + { + mimeType = "media/audiocd"; + medium->unmountableState( "audiocd:/?device=" + TQString(libhal_volume_get_device_file(halVolume)) ); + } + + medium->setIconName(TQString::null); + + /* check if the disc id a vcd or a video dvd */ + DiscType type = LinuxCDPolling::identifyDiscType(libhal_volume_get_device_file(halVolume)); + switch (type) + { + case DiscType::VCD: + mimeType = "media/vcd"; + break; + case DiscType::SVCD: + mimeType = "media/svcd"; + break; + case DiscType::DVD: + mimeType = "media/dvdvideo"; + break; + } + } + else + { + mimeType = "media/hdd" + MOUNT_SUFFIX; + medium->setIconName(TQString::null); // reset icon + if (libhal_drive_is_hotpluggable(halDrive)) + { + mimeType = "media/removable" + MOUNT_SUFFIX; + medium->needMounting(); + switch (libhal_drive_get_type(halDrive)) { + case LIBHAL_DRIVE_TYPE_COMPACT_FLASH: + medium->setIconName("compact_flash" + MOUNT_ICON_SUFFIX); + break; + case LIBHAL_DRIVE_TYPE_MEMORY_STICK: + medium->setIconName("memory_stick" + MOUNT_ICON_SUFFIX); + break; + case LIBHAL_DRIVE_TYPE_SMART_MEDIA: + medium->setIconName("smart_media" + MOUNT_ICON_SUFFIX); + break; + case LIBHAL_DRIVE_TYPE_SD_MMC: + medium->setIconName("sd_mmc" + MOUNT_ICON_SUFFIX); + break; + case LIBHAL_DRIVE_TYPE_PORTABLE_AUDIO_PLAYER: + { + medium->setIconName("ipod" + MOUNT_ICON_SUFFIX); + + if (libhal_device_get_property_QString(m_halContext, driveUdi.latin1(), "info.product") == "iPod" && + KProtocolInfo::isKnownProtocol( TQString("ipod") ) ) + { + medium->unmountableState( "ipod:/" ); + medium->mountableState( libhal_volume_is_mounted(halVolume) ); + } + break; + } + case LIBHAL_DRIVE_TYPE_CAMERA: + { + mimeType = "media/camera" + MOUNT_SUFFIX; + const char *physdev = libhal_drive_get_physical_device_udi(halDrive); + // get model from camera + if (physdev && libhal_device_query_capability(m_halContext, physdev, "camera", NULL)) + { + if (libhal_device_property_exists(m_halContext, physdev, "usb_device.product", NULL)) + medium->setLabel(libhal_device_get_property_QString(m_halContext, physdev, "usb_device.product")); + else if (libhal_device_property_exists(m_halContext, physdev, "usb.product", NULL)) + medium->setLabel(libhal_device_get_property_QString(m_halContext, physdev, "usb.product")); + } + break; + } + case LIBHAL_DRIVE_TYPE_TAPE: + medium->setIconName(TQString::null); //FIXME need icon + break; + default: + medium->setIconName(TQString::null); + } + + if (medium->isMounted() && TQFile::exists(medium->mountPoint() + "/dcim")) + { + mimeType = "media/camera" + MOUNT_SUFFIX; + } + } + } + medium->setMimeType(mimeType); + + libhal_drive_free(halDrive); + libhal_volume_free(halVolume); +} + +bool HALBackend::setFstabProperties( Medium *medium ) +{ + TQString mp = isInFstab(medium); + + if (!mp.isNull() && !medium->id().startsWith( "/org/kde" ) ) + { + // now that we know it's in fstab, we have to find out if it's mounted + KMountPoint::List mtab = KMountPoint::currentMountPoints(); + + KMountPoint::List::iterator it = mtab.begin(); + KMountPoint::List::iterator end = mtab.end(); + + bool mounted = false; + + for (; it!=end; ++it) + { + if ((*it)->mountedFrom() == medium->deviceNode() && (*it)->mountPoint() == mp ) + { + mounted = true; + break; + } + } + + kdDebug() << mp << " " << mounted << " " << medium->deviceNode() << " " << endl; + TQString fstype = medium->fsType(); + if ( fstype.isNull() ) + fstype = "auto"; + + medium->mountableState( + medium->deviceNode(), + mp, /* Mount point */ + fstype, /* Filesystem type */ + mounted ); /* Mounted ? */ + + return true; + } + + return false; + +} + +// Handle floppies and zip drives +bool HALBackend::setFloppyProperties(Medium* medium) +{ + kdDebug(1219) << "HALBackend::setFloppyProperties for " << medium->id() << endl; + + const char* udi = medium->id().ascii(); + /* Check if the device still exists */ + if (!libhal_device_exists(m_halContext, udi, NULL)) + return false; + + LibHalDrive* halDrive = libhal_drive_from_udi(m_halContext, udi); + if (!halDrive) + return false; + + TQString drive_type = libhal_device_get_property_QString(m_halContext, udi, "storage.drive_type"); + + if (drive_type == "zip") { + int numVolumes; + char** volumes = libhal_drive_find_all_volumes(m_halContext, halDrive, &numVolumes); + libhal_free_string_array(volumes); + kdDebug(1219) << " found " << numVolumes << " volumes" << endl; + if (numVolumes) + { + libhal_drive_free(halDrive); + return false; + } + } + + medium->setName( generateName(libhal_drive_get_device_file(halDrive)) ); + medium->setLabel(i18n("Unknown Drive")); + + // HAL hates floppies - so we have to do it twice ;( + medium->mountableState(libhal_drive_get_device_file(halDrive), TQString::null, TQString::null, false); + setFloppyMountState(medium); + + if (drive_type == "floppy") + { + if (medium->isMounted()) // don't use _SUFFIX here as it accesses the volume + medium->setMimeType("media/floppy_mounted" ); + else + medium->setMimeType("media/floppy_unmounted"); + medium->setLabel(i18n("Floppy Drive")); + } + else if (drive_type == "zip") + { + if (medium->isMounted()) + medium->setMimeType("media/zip_mounted" ); + else + medium->setMimeType("media/zip_unmounted"); + medium->setLabel(i18n("Zip Drive")); + } + + /** @todo And mimtype for JAZ drives ? */ + + medium->setIconName(TQString::null); + + libhal_drive_free(halDrive); + + return true; +} + +void HALBackend::setFloppyMountState( Medium *medium ) +{ + if ( !medium->id().startsWith( "/org/kde" ) ) + { + KMountPoint::List mtab = KMountPoint::currentMountPoints(); + KMountPoint::List::iterator it = mtab.begin(); + KMountPoint::List::iterator end = mtab.end(); + + TQString fstype; + TQString mountpoint; + for (; it!=end; ++it) + { + if ((*it)->mountedFrom() == medium->deviceNode() ) + { + fstype = (*it)->mountType().isNull() ? (*it)->mountType() : "auto"; + mountpoint = (*it)->mountPoint(); + medium->mountableState( medium->deviceNode(), mountpoint, fstype, true ); + return; + } + } + } +} + +void HALBackend::setCameraProperties(Medium* medium) +{ + kdDebug(1219) << "HALBackend::setCameraProperties for " << medium->id() << endl; + + const char* udi = medium->id().ascii(); + /* Check if the device still exists */ + if (!libhal_device_exists(m_halContext, udi, NULL)) + return; + + /** @todo find name */ + medium->setName("camera"); + + TQString device = "camera:/"; + + char *cam = libhal_device_get_property_string(m_halContext, udi, "camera.libgphoto2.name", NULL); + DBusError error; + dbus_error_init(&error); + if (cam && + libhal_device_property_exists(m_halContext, udi, "usb.linux.device_number", NULL) && + libhal_device_property_exists(m_halContext, udi, "usb.bus_number", NULL)) + device.sprintf("camera://%s@[usb:%03d,%03d]/", cam, + libhal_device_get_property_int(m_halContext, udi, "usb.bus_number", &error), + libhal_device_get_property_int(m_halContext, udi, "usb.linux.device_number", &error)); + + libhal_free_string(cam); + + /** @todo find the rest of this URL */ + medium->unmountableState(device); + medium->setMimeType("media/gphoto2camera"); + medium->setIconName(TQString::null); + if (libhal_device_property_exists(m_halContext, udi, "usb_device.product", NULL)) + medium->setLabel(libhal_device_get_property_QString(m_halContext, udi, "usb_device.product")); + else if (libhal_device_property_exists(m_halContext, udi, "usb.product", NULL)) + medium->setLabel(libhal_device_get_property_QString(m_halContext, udi, "usb.product")); + else + medium->setLabel(i18n("Camera")); +} + +TQString HALBackend::generateName(const TQString &devNode) +{ + return KURL(devNode).fileName(); +} + +/****************************************** + ** HAL CALL-BACKS ** + ******************************************/ + +void HALBackend::hal_device_added(LibHalContext *ctx, const char *udi) +{ + kdDebug(1219) << "HALBackend::hal_device_added " << udi << endl; + Q_UNUSED(ctx); + s_HALBackend->AddDevice(udi); +} + +void HALBackend::hal_device_removed(LibHalContext *ctx, const char *udi) +{ + kdDebug(1219) << "HALBackend::hal_device_removed " << udi << endl; + Q_UNUSED(ctx); + s_HALBackend->RemoveDevice(udi); +} + +void HALBackend::hal_device_property_modified(LibHalContext *ctx, const char *udi, + const char *key, dbus_bool_t is_removed, dbus_bool_t is_added) +{ + kdDebug(1219) << "HALBackend::hal_property_modified " << udi << " -- " << key << endl; + Q_UNUSED(ctx); + Q_UNUSED(is_removed); + Q_UNUSED(is_added); + s_HALBackend->ModifyDevice(udi, key); +} + +void HALBackend::hal_device_condition(LibHalContext *ctx, const char *udi, + const char *condition_name, + const char* message + ) +{ + kdDebug(1219) << "HALBackend::hal_device_condition " << udi << " -- " << condition_name << endl; + Q_UNUSED(ctx); + Q_UNUSED(message); + s_HALBackend->DeviceCondition(udi, condition_name); +} + +TQStringList HALBackend::getHALmountoptions(TQString udi) +{ + const char* _ppt_string; + LibHalVolume* volume; + LibHalDrive* drive; + + TQString _ppt_TQString; + + volume = libhal_volume_from_udi( m_halContext, udi.latin1() ); + if( volume ) + drive = libhal_drive_from_udi( m_halContext, libhal_volume_get_storage_device_udi( volume ) ); + else + drive = libhal_drive_from_udi( m_halContext, udi.latin1() ); + + if( !drive ) + return TQString::null; + + if( volume ) + _ppt_string = libhal_volume_policy_get_mount_options ( drive, volume, NULL ); + else + _ppt_string = libhal_drive_policy_get_mount_options ( drive, NULL ); + + _ppt_TQString = TQString(_ppt_string ? _ppt_string : ""); + + return TQStringList::split(",",_ppt_TQString); +} + +TQStringList HALBackend::mountoptions(const TQString &name) +{ + const Medium* medium = m_mediaList.findById(name); + if (!medium) + return TQStringList(); // we don't know about that one + if (!isInFstab(medium).isNull()) + return TQStringList(); // not handled by HAL - fstab entry + + TQString volume_udi = name; + if (medium->isEncrypted()) { + // see if we have a clear volume + LibHalVolume* halVolume = libhal_volume_from_udi(m_halContext, medium->id().latin1()); + if (halVolume) { + char* clearUdi = libhal_volume_crypto_get_clear_volume_udi(m_halContext, halVolume); + if (clearUdi != NULL) { + volume_udi = clearUdi; + libhal_free_string(clearUdi); + } else { + // if not decrypted yet then no mountoptions + return TQStringList(); + } + libhal_volume_free(halVolume); + } else { + // strange... + return TQStringList(); + } + } + + TDEConfig config("mediamanagerrc"); + + bool use_defaults = true; + if (config.hasGroup(name)) + { + config.setGroup(name); + use_defaults = config.readBoolEntry("use_defaults", false); + } + + if (use_defaults) + config.setGroup("DefaultOptions"); + + char ** array = libhal_device_get_property_strlist(m_halContext, volume_udi.latin1(), "volume.mount.valid_options", NULL); + TQMap<TQString,bool> valids; + + for (int index = 0; array && array[index]; ++index) { + TQString t = array[index]; + if (t.endsWith("=")) + t = t.left(t.length() - 1); + valids[t] = true; + kdDebug() << "valid " << t << endl; + } + libhal_free_string_array(array); + TQStringList result; + TQString tmp; + + result << TQString("use_defaults=%1").arg(use_defaults ? "true" : "false"); + + TQString fstype = libhal_device_get_property_QString(m_halContext, volume_udi.latin1(), "volume.fstype"); + if (fstype.isNull()) + fstype = libhal_device_get_property_QString(m_halContext, volume_udi.latin1(), "volume.policy.mount_filesystem"); + + TQString drive_udi = libhal_device_get_property_QString(m_halContext, volume_udi.latin1(), "block.storage_device"); + + bool removable = false; + if ( !drive_udi.isNull() ) + removable = libhal_device_get_property_bool(m_halContext, drive_udi.latin1(), "storage.removable", NULL) + || libhal_device_get_property_bool(m_halContext, drive_udi.latin1(), "storage.hotpluggable", NULL); + + bool value; + if (use_defaults) + { + value = config.readBoolEntry("automount", false); + } + else + { + QString current_group = config.group(); + config.setGroup(drive_udi); + value = config.readBoolEntry("automount", false); + config.setGroup(current_group); + } + + if (libhal_device_get_property_bool(m_halContext, volume_udi.latin1(), "volume.disc.is_blank", NULL) + || libhal_device_get_property_bool(m_halContext, volume_udi.latin1(), "volume.disc.is_vcd", NULL) + || libhal_device_get_property_bool(m_halContext, volume_udi.latin1(), "volume.disc.is_svcd", NULL) + || libhal_device_get_property_bool(m_halContext, volume_udi.latin1(), "volume.disc.is_videodvd", NULL) + || libhal_device_get_property_bool(m_halContext, volume_udi.latin1(), "volume.disc.has_audio", NULL)) + value = false; + + result << TQString("automount=%1").arg(value ? "true" : "false"); + + if (valids.contains("ro")) + { + value = config.readBoolEntry("ro", false); + tmp = TQString("ro=%1").arg(value ? "true" : "false"); + if (fstype != "iso9660") // makes no sense + result << tmp; + } + + if (valids.contains("quiet")) + { + value = config.readBoolEntry("quiet", false); + tmp = TQString("quiet=%1").arg(value ? "true" : "false"); + if (fstype != "iso9660") // makes no sense + result << tmp; + } + + if (valids.contains("flush")) + { + value = config.readBoolEntry("flush", fstype.endsWith("fat")); + tmp = TQString("flush=%1").arg(value ? "true" : "false"); + result << tmp; + } + + if (valids.contains("uid")) + { + value = config.readBoolEntry("uid", true); + tmp = TQString("uid=%1").arg(value ? "true" : "false"); + result << tmp; + } + + if (valids.contains("utf8")) + { + value = config.readBoolEntry("utf8", true); + tmp = TQString("utf8=%1").arg(value ? "true" : "false"); + result << tmp; + } + + if (valids.contains("shortname")) + { + TQString svalue = config.readEntry("shortname", "lower").lower(); + if (svalue == "winnt") + result << "shortname=winnt"; + else if (svalue == "win95") + result << "shortname=win95"; + else if (svalue == "mixed") + result << "shortname=mixed"; + else + result << "shortname=lower"; + } + + // pass our locale to the ntfs-3g driver so it can translate local characters + if (valids.contains("locale") && fstype == "ntfs-3g") + { + // have to obtain LC_CTYPE as returned by the `locale` command + // check in the same order as `locale` does + char *cType; + if ( (cType = getenv("LC_ALL")) || (cType = getenv("LC_CTYPE")) || (cType = getenv("LANG")) ) { + result << TQString("locale=%1").arg(cType); + } + } + + if (valids.contains("sync")) + { + value = config.readBoolEntry("sync", ( valids.contains("flush") && !fstype.endsWith("fat") ) && removable); + tmp = TQString("sync=%1").arg(value ? "true" : "false"); + if (fstype != "iso9660") // makes no sense + result << tmp; + } + + if (valids.contains("noatime")) + { + value = config.readBoolEntry("atime", !fstype.endsWith("fat")); + tmp = TQString("atime=%1").arg(value ? "true" : "false"); + if (fstype != "iso9660") // makes no sense + result << tmp; + } + + TQString mount_point = libhal_device_get_property_QString(m_halContext, volume_udi.latin1(), "volume.mount_point"); + if (mount_point.isEmpty()) + mount_point = libhal_device_get_property_QString(m_halContext, volume_udi.latin1(), "volume.policy.desired_mount_point"); + + mount_point = config.readEntry("mountpoint", mount_point); + + if (!mount_point.startsWith("/")) + mount_point = "/media/" + mount_point; + + result << TQString("mountpoint=%1").arg(mount_point); + result << TQString("filesystem=%1").arg(fstype); + + if (valids.contains("data")) + { + TQString svalue = config.readEntry("journaling").lower(); + if (svalue == "ordered") + result << "journaling=ordered"; + else if (svalue == "writeback") + result << "journaling=writeback"; + else if (svalue == "data") + result << "journaling=data"; + else + result << "journaling=ordered"; + } + + return result; +} + +bool HALBackend::setMountoptions(const TQString &name, const TQStringList &options ) +{ + kdDebug() << "setMountoptions " << name << " " << options << endl; + + TDEConfig config("mediamanagerrc"); + config.setGroup(name); + + TQMap<TQString,TQString> valids = MediaManagerUtils::splitOptions(options); + + const char *names[] = { "use_defaults", "ro", "quiet", "atime", "uid", "utf8", "flush", "sync", 0 }; + for (int index = 0; names[index]; ++index) + if (valids.contains(names[index])) + config.writeEntry(names[index], valids[names[index]] == "true"); + + if (valids.contains("shortname")) + config.writeEntry("shortname", valids["shortname"]); + + if (valids.contains("journaling")) + config.writeEntry("journaling", valids["journaling"]); + + if (!mountoptions(name).contains(TQString("mountpoint=%1").arg(valids["mountpoint"]))) + config.writeEntry("mountpoint", valids["mountpoint"]); + + if (valids.contains("automount")) { + TQString drive_udi = libhal_device_get_property_QString(m_halContext, name.latin1(), "block.storage_device"); + config.setGroup(drive_udi); + config.writeEntry("automount", valids["automount"]); + } + + return true; +} + +TQString startKdeSudoProcess(const TQString& tdesudoPath, const TQString& command, + const TQString& dialogCaption, const TQString& dialogComment) +{ + TDEProcess tdesudoProcess; + + tdesudoProcess << tdesudoPath + << "-d" + << "--noignorebutton" + << "--caption" << dialogCaption + << "--comment" << dialogComment + << "-c" << command; + + // @todo handle tdesudo output + tdesudoProcess.start(TDEProcess::Block); + + return TQString(); +} + +TQString startKdeSuProcess(const TQString& tdesuPath, const TQString& command, + const TQString& dialogCaption) +{ + TDEProcess tdesuProcess; + + tdesuProcess << tdesuPath + << "-d" + << "--noignorebutton" + << "--caption" << dialogCaption + << "-c" << command; + + // @todo handle tdesu output + tdesuProcess.start(TDEProcess::Block); + + return TQString(); +} + +TQString startPrivilegedProcess(const TQString& command, const TQString& dialogCaption, const TQString& dialogComment) +{ + TQString error; + + TQString tdesudoPath = KStandardDirs::findExe("tdesudo"); + + if (!tdesudoPath.isEmpty()) + error = startKdeSudoProcess(tdesudoPath, command, dialogCaption, dialogComment); + else { + TQString tdesuPath = KStandardDirs::findExe("tdesu"); + + if (!tdesuPath.isEmpty()) + error = startKdeSuProcess(tdesuPath, command, dialogCaption); + } + + return error; +} + +TQString privilegedMount(const char* udi, const char* mountPoint, const char** options, int numberOfOptions) +{ + TQString error; + + kdDebug() << "run privileged mount for " << udi << endl; + + TQString dbusSendPath = KStandardDirs::findExe("dbus-send"); + + // @todo return error message + if (dbusSendPath.isEmpty()) + return TQString(); + + TQString mountOptions; + TQTextOStream optionsStream(&mountOptions); + for (int optionIndex = 0; optionIndex < numberOfOptions; optionIndex++) { + optionsStream << options[optionIndex]; + if (optionIndex < numberOfOptions - 1) + optionsStream << ","; + } + + TQString command; + TQTextOStream(&command) << dbusSendPath + << " --system --print-reply --dest=org.freedesktop.Hal " << udi + << " org.freedesktop.Hal.Device.Volume.Mount string:" << mountPoint + << " string: array:string:" << mountOptions; + + kdDebug() << "command: " << command << endl; + + error = startPrivilegedProcess(command, + i18n("Authenticate"), + i18n("<big><b>System policy prevents mounting internal media</b></big><br/>Authentication is required to perform this action. Please enter your password to verify.")); + + return error; +} + +TQString privilegedUnmount(const char* udi) +{ + TQString error; + + kdDebug() << "run privileged unmount for " << udi << endl; + + TQString dbusSendPath = KStandardDirs::findExe("dbus-send"); + + // @todo return error message + if (dbusSendPath.isEmpty()) + return TQString(); + + TQString command; + TQTextOStream(&command) << dbusSendPath + << " --system --print-reply --dest=org.freedesktop.Hal " << udi + << " org.freedesktop.Hal.Device.Volume.Unmount array:string:force"; + + kdDebug() << "command: " << command << endl; + + error = startPrivilegedProcess(command, + i18n("Authenticate"), + i18n("<big><b>System policy prevents unmounting media mounted by other users</b></big><br/>Authentication is required to perform this action. Please enter your password to verify.")); + + return error; +} + +static TQString mount_priv(const char *udi, const char *mount_point, const char **poptions, int noptions, + DBusConnection *dbus_connection) +{ + DBusMessage *dmesg, *reply; + DBusError error; + + const char *fstype = ""; + if (!(dmesg = dbus_message_new_method_call ("org.freedesktop.Hal", udi, + "org.freedesktop.Hal.Device.Volume", + "Mount"))) { + kdDebug() << "mount failed for " << udi << ": could not create dbus message\n"; + return i18n("Internal Error"); + } + + if (!dbus_message_append_args (dmesg, DBUS_TYPE_STRING, &mount_point, DBUS_TYPE_STRING, &fstype, + DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &poptions, noptions, + DBUS_TYPE_INVALID)) + { + kdDebug() << "mount failed for " << udi << ": could not append args to dbus message\n"; + dbus_message_unref (dmesg); + return i18n("Internal Error"); + } + + TQString qerror; + + dbus_error_init (&error); + if (!(reply = dbus_connection_send_with_reply_and_block (dbus_connection, dmesg, -1, &error))) + { + TQString qerror = error.message; + kdError() << "mount failed for " << udi << ": " << error.name << " - " << qerror << endl; + if ( !strcmp(error.name, "org.freedesktop.Hal.Device.Volume.UnknownFilesystemType")) + qerror = i18n("Invalid filesystem type"); + else if ( !strcmp(error.name, "org.freedesktop.Hal.Device.Volume.PermissionDenied")) + qerror = i18n("Permission denied<p>Please ensure that:<br>1. You have permission to access this device.<br>2. This device node is not listed in /etc/fstab.</p>"); + else if ( !strcmp(error.name, "org.freedesktop.Hal.Device.PermissionDeniedByPolicy")) + qerror = privilegedMount(udi, mount_point, poptions, noptions); + else if ( !strcmp(error.name, "org.freedesktop.Hal.Device.Volume.AlreadyMounted")) + qerror = i18n("Device is already mounted."); + else if ( !strcmp(error.name, "org.freedesktop.Hal.Device.Volume.InvalidMountpoint") && strlen(mount_point)) { + dbus_message_unref (dmesg); + dbus_error_free (&error); + return mount_priv(udi, "", poptions, noptions, dbus_connection); + } + dbus_message_unref (dmesg); + dbus_error_free (&error); + return qerror; + } + + kdDebug() << "mount queued for " << udi << endl; + + dbus_message_unref (dmesg); + dbus_message_unref (reply); + + return qerror; + +} + +TQString HALBackend::listUsingProcesses(const Medium* medium) +{ + TQString proclist, fullmsg; + TQString cmdline = TQString("/usr/bin/env fuser -vm %1 2>&1").arg(TDEProcess::quote(medium->mountPoint())); + FILE *fuser = popen(cmdline.latin1(), "r"); + + uint counter = 0; + if (fuser) { + proclist += "<pre>"; + TQTextIStream is(fuser); + TQString tmp; + while (!is.atEnd()) { + tmp = is.readLine(); + tmp = TQStyleSheet::escape(tmp) + "\n"; + + proclist += tmp; + if (counter++ > 10) + { + proclist += "..."; + break; + } + } + proclist += "</pre>"; + (void)pclose( fuser ); + } + if (counter) { + fullmsg = i18n("Moreover, programs still using the device " + "have been detected. They are listed below. You have to " + "close them or change their working directory before " + "attempting to unmount the device again."); + fullmsg += "<br>" + proclist; + return fullmsg; + } else { + return TQString::null; + } +} + +TQString HALBackend::killUsingProcesses(const Medium* medium) +{ + TQString proclist, fullmsg; + TQString cmdline = TQString("/usr/bin/env fuser -vmk %1 2>&1").arg(TDEProcess::quote(medium->mountPoint())); + FILE *fuser = popen(cmdline.latin1(), "r"); + + uint counter = 0; + if (fuser) { + proclist += "<pre>"; + TQTextIStream is(fuser); + TQString tmp; + while (!is.atEnd()) { + tmp = is.readLine(); + tmp = TQStyleSheet::escape(tmp) + "\n"; + + proclist += tmp; + if (counter++ > 10) + { + proclist += "..."; + break; + } + } + proclist += "</pre>"; + (void)pclose( fuser ); + } + if (counter) { + fullmsg = i18n("Programs that were still using the device " + "have been forcibly terminated. They are listed below."); + fullmsg += "<br>" + proclist; + return fullmsg; + } else { + return TQString::null; + } +} + +void HALBackend::slotResult(TDEIO::Job *job) +{ + kdDebug() << "slotResult " << mount_jobs[job] << endl; + + struct mount_job_data *data = mount_jobs[job]; + TQString& qerror = data->errorMessage; + const Medium* medium = data->medium; + + if (job->error() == TDEIO::ERR_COULD_NOT_UNMOUNT) { + TQString proclist(listUsingProcesses(medium)); + + qerror = "<qt>"; + qerror += "<p>" + i18n("Unfortunately, the device <b>%1</b> (%2) named <b>'%3'</b> and " + "currently mounted at <b>%4</b> could not be unmounted. ").arg( + "system:/media/" + medium->name(), + medium->deviceNode(), + medium->prettyLabel(), + medium->prettyBaseURL().pathOrURL()) + "</p>"; + qerror += "<p>" + i18n("The following error was returned by umount command:"); + qerror += "</p><pre>" + job->errorText() + "</pre>"; + + if (!proclist.isEmpty()) { + qerror += proclist; + } + qerror += "</qt>"; + } else if (job->error()) { + qerror = job->errorText(); + } + + ResetProperties( medium->id().latin1() ); + mount_jobs.remove(job); + + /* Job completed. Notify the caller */ + data->error = job->error(); + data->completed = true; + kapp->eventLoop()->exitLoop(); +} + +TQString HALBackend::isInFstab(const Medium *medium) +{ + KMountPoint::List fstab = KMountPoint::possibleMountPoints(KMountPoint::NeedMountOptions|KMountPoint::NeedRealDeviceName); + + KMountPoint::List::iterator it = fstab.begin(); + KMountPoint::List::iterator end = fstab.end(); + + for (; it!=end; ++it) + { + TQString reald = (*it)->realDeviceName(); + if ( reald.endsWith( "/" ) ) + reald = reald.left( reald.length() - 1 ); + kdDebug() << "isInFstab -" << medium->deviceNode() << "- -" << reald << "- -" << (*it)->mountedFrom() << "-" << endl; + if ((*it)->mountedFrom() == medium->deviceNode() || ( !medium->deviceNode().isEmpty() && reald == medium->deviceNode() ) ) + { + TQStringList opts = (*it)->mountOptions(); + if (opts.contains("user") || opts.contains("users")) + return (*it)->mountPoint(); + } + } + + return TQString::null; +} + +TQString HALBackend::mount(const Medium *medium) +{ + if (medium->isMounted()) + return TQString(); // that was easy + + TQString mountPoint = isInFstab(medium); + if (!mountPoint.isNull()) + { + struct mount_job_data data; + data.completed = false; + data.medium = medium; + + kdDebug() << "triggering user mount " << medium->deviceNode() << " " << mountPoint << " " << medium->id() << endl; + TDEIO::Job *job = TDEIO::mount( false, 0, medium->deviceNode(), mountPoint ); + connect(job, TQT_SIGNAL( result (TDEIO::Job *)), + TQT_SLOT( slotResult( TDEIO::Job *))); + mount_jobs[job] = &data; + // The caller expects the device to be mounted when the function + // completes. Thus block until the job completes. + while (!data.completed) { + kapp->eventLoop()->enterLoop(); + } + // Return the error message (if any) to the caller + return (data.error) ? data.errorMessage : TQString::null; + + } else if (medium->id().startsWith("/org/kde/") ) + return i18n("Permission denied"); + + TQStringList soptions; + + kdDebug() << "mounting " << medium->id() << "..." << endl; + + TQMap<TQString,TQString> valids = MediaManagerUtils::splitOptions(mountoptions(medium->id())); + if (valids["flush"] == "true") + soptions << "flush"; + + if ((valids["uid"] == "true") && (medium->fsType() != "ntfs")) + { + soptions << TQString("uid=%1").arg(getuid()); + } + + if (valids["ro"] == "true") + soptions << "ro"; + + if (valids["atime"] != "true") + soptions << "noatime"; + + if (valids["quiet"] == "true") + soptions << "quiet"; + + if (valids["utf8"] == "true") + soptions << "utf8"; + + if (valids["sync"] == "true") + soptions << "sync"; + + if (medium->fsType() == "ntfs") { + TQString fsLocale("locale="); + fsLocale += setlocale(LC_ALL, ""); + + soptions << fsLocale; + } + + TQString mount_point = valids["mountpoint"]; + if (mount_point.startsWith("/media/")) + mount_point = mount_point.mid(7); + + if (valids.contains("shortname")) + { + soptions << TQString("shortname=%1").arg(valids["shortname"]); + } + + if (valids.contains("locale")) + { + soptions << TQString("locale=%1").arg(valids["locale"]); + } + + if (valids.contains("journaling")) + { + TQString option = valids["journaling"]; + if (option == "data") + soptions << TQString("data=journal"); + else if (option == "writeback") + soptions << TQString("data=writeback"); + else + soptions << TQString("data=ordered"); + } + + TQStringList hal_mount_options = getHALmountoptions(medium->id()); + for (TQValueListIterator<TQString> it=hal_mount_options.begin();it!=hal_mount_options.end();it++) + { + soptions << *it; + kdDebug()<<"HALOption: "<<*it<<endl; + if ((*it).startsWith("iocharset=")) + { + soptions.remove("utf8"); + kdDebug()<<"\"iocharset=\" found. Removing \"utf8\" from options."<<endl; + } + } + + + const char **options = new const char*[soptions.size() + 1]; + uint noptions = 0; + for (TQStringList::ConstIterator it = soptions.begin(); it != soptions.end(); ++it, ++noptions) + { + options[noptions] = (*it).latin1(); + kdDebug()<<"Option: "<<*it<<endl; + } + options[noptions] = NULL; + + TQString qerror = i18n("Cannot mount encrypted drives!"); + + if (!medium->isEncrypted()) { + // normal volume + qerror = mount_priv(medium->id().latin1(), mount_point.utf8(), options, noptions, dbus_connection); + } else { + // see if we have a clear volume + LibHalVolume* halVolume = libhal_volume_from_udi(m_halContext, medium->id().latin1()); + if (halVolume) { + char* clearUdi = libhal_volume_crypto_get_clear_volume_udi(m_halContext, halVolume); + if (clearUdi != NULL) { + qerror = mount_priv(clearUdi, mount_point.utf8(), options, noptions, dbus_connection); + libhal_free_string(clearUdi); + } + libhal_volume_free(halVolume); + } + } + + if (!qerror.isEmpty()) { + kdError() << "mounting " << medium->id() << " returned " << qerror << endl; + return qerror; + } + + medium->setHalMounted(true); + ResetProperties(medium->id().latin1()); + + return TQString(); +} + +TQString HALBackend::mount(const TQString &_udi) +{ + const Medium* medium = m_mediaList.findById(_udi); + if (!medium) + return i18n("No such medium: %1").arg(_udi); + + return mount(medium); +} + +TQString HALBackend::unmount(const TQString &_udi) +{ + const Medium* medium = m_mediaList.findById(_udi); + if (!medium) + { // now we get fancy: if the udi is no volume, it _might_ be a device with only one + // volume on it (think CDs) - so we're so nice to the caller to unmount that volume + LibHalDrive* halDrive = libhal_drive_from_udi(m_halContext, _udi.latin1()); + if (halDrive) + { + int numVolumes; + char** volumes = libhal_drive_find_all_volumes(m_halContext, halDrive, &numVolumes); + if (numVolumes == 1) + medium = m_mediaList.findById( volumes[0] ); + } + } + + if ( !medium ) + return i18n("No such medium: %1").arg(_udi); + + if (!medium->isMounted()) + return TQString(); // that was easy + + TQString mountPoint = isInFstab(medium); + if (!mountPoint.isNull()) + { + struct mount_job_data data; + data.completed = false; + data.medium = medium; + + kdDebug() << "triggering user unmount " << medium->deviceNode() << " " << mountPoint << endl; + TDEIO::Job *job = TDEIO::unmount( medium->mountPoint(), false ); + connect(job, TQT_SIGNAL( result (TDEIO::Job *)), + TQT_SLOT( slotResult( TDEIO::Job *))); + mount_jobs[job] = &data; + // The caller expects the device to be unmounted when the function + // completes. Thus block until the job completes. + while (!data.completed) { + kapp->eventLoop()->enterLoop(); + } + // Return the error message (if any) to the caller + return (data.error) ? data.errorMessage : TQString::null; + } + + DBusMessage *dmesg, *reply; + DBusError error; + const char *options[2]; + TQString udi = TQString::null; + + if (!medium->isEncrypted()) { + // normal volume + udi = medium->id(); + } else { + // see if we have a clear volume + LibHalVolume* halVolume = libhal_volume_from_udi(m_halContext, medium->id().latin1()); + if (halVolume) { + char *clearUdi = libhal_volume_crypto_get_clear_volume_udi(m_halContext, halVolume); + udi = clearUdi; + libhal_free_string(clearUdi); + libhal_volume_free(halVolume); + } + } + if (udi.isNull()) { + kdDebug() << "unmount failed: no udi" << endl; + return i18n("Internal Error"); + } + + kdDebug() << "unmounting " << udi << "..." << endl; + + dbus_error_init(&error); + DBusConnection *dbus_connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error); + if (dbus_error_is_set(&error)) + { + dbus_error_free(&error); + return false; + } + + if (!(dmesg = dbus_message_new_method_call ("org.freedesktop.Hal", udi.latin1(), + "org.freedesktop.Hal.Device.Volume", + "Unmount"))) { + kdDebug() << "unmount failed for " << udi << ": could not create dbus message\n"; + return i18n("Internal Error"); + } + + options[0] = "force"; + options[1] = 0; + + if (!dbus_message_append_args (dmesg, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options, 0, + DBUS_TYPE_INVALID)) + { + kdDebug() << "unmount failed for " << udi << ": could not append args to dbus message\n"; + dbus_message_unref (dmesg); + return i18n("Internal Error"); + } + + char thisunmounthasfailed = 0; + dbus_error_init (&error); + if (!(reply = dbus_connection_send_with_reply_and_block (dbus_connection, dmesg, -1, &error))) + { + thisunmounthasfailed = 1; + TQString qerror, reason, origqerror; + + if (!strcmp(error.name, "org.freedesktop.Hal.Device.PermissionDeniedByPolicy")) { + qerror = privilegedUnmount(udi.latin1()); + + if (qerror.isEmpty()) { + dbus_message_unref(dmesg); + dbus_error_free(&error); + return TQString(); + } + + // @todo handle unmount error message + } + + kdDebug() << "unmount failed for " << udi << ": " << error.name << " " << error.message << endl; + qerror = "<qt>"; + qerror += "<p>" + i18n("Unfortunately, the device <b>%1</b> (%2) named <b>'%3'</b> and " + "currently mounted at <b>%4</b> could not be unmounted. ").arg( + "system:/media/" + medium->name(), + medium->deviceNode(), + medium->prettyLabel(), + medium->prettyBaseURL().pathOrURL()) + "</p>"; + qerror += "<p>" + i18n("Unmounting failed due to the following error:") + "</p>"; + if (!strcmp(error.name, "org.freedesktop.Hal.Device.Volume.Busy")) { + reason = i18n("Device is Busy:"); + thisunmounthasfailed = 2; + } else if (!strcmp(error.name, "org.freedesktop.Hal.Device.Volume.NotMounted")) { + // this is faking. The error is that the device wasn't mounted by hal (but by the system) + reason = i18n("Permission denied<p>Please ensure that:<br>1. You have permission to access this device.<br>2. This device was originally mounted using TDE.</p>"); + } else { + reason = error.message; + } + qerror += "<p><b>" + reason + "</b></p>"; + origqerror = qerror; + + // Include list of processes (if any) using the device in the error message + reason = listUsingProcesses(medium); + if (!reason.isEmpty()) { + qerror += reason; + if (thisunmounthasfailed == 2) { // Failed as BUSY + if (KMessageBox::warningYesNo(0, i18n("%1<p><b>Would you like to forcibly terminate these processes?</b><br><i>All unsaved data would be lost</i>").arg(qerror)) == KMessageBox::Yes) { + qerror = origqerror; + reason = killUsingProcesses(medium); + qerror = HALBackend::unmount(udi); + if (qerror.isNull()) { + thisunmounthasfailed = 0; + } + } + } + } + + if (thisunmounthasfailed != 0) { + dbus_message_unref (dmesg); + dbus_error_free (&error); + return qerror; + } + } + + kdDebug() << "unmount queued for " << udi << endl; + + dbus_message_unref (dmesg); + dbus_message_unref (reply); + + medium->setHalMounted(false); + ResetProperties(medium->id().latin1()); + + while (dbus_connection_dispatch(dbus_connection) == DBUS_DISPATCH_DATA_REMAINS) ; + + return TQString(); +} + +TQString HALBackend::decrypt(const TQString &_udi, const TQString &password) +{ + const Medium* medium = m_mediaList.findById(_udi); + if (!medium) + return i18n("No such medium: %1").arg(_udi); + + if (!medium->isEncrypted() || !medium->clearDeviceUdi().isNull()) + return TQString(); + + const char *udi = medium->id().latin1(); + DBusMessage *msg = NULL; + DBusMessage *reply = NULL; + DBusError error; + + kdDebug() << "Setting up " << udi << " for crypto\n" <<endl; + + msg = dbus_message_new_method_call ("org.freedesktop.Hal", udi, + "org.freedesktop.Hal.Device.Volume.Crypto", + "Setup"); + if (msg == NULL) { + kdDebug() << "decrypt failed for " << udi << ": could not create dbus message\n"; + return i18n("Internal Error"); + } + + TQCString pwdUtf8 = password.utf8(); + const char *pwd_utf8 = pwdUtf8; + if (!dbus_message_append_args (msg, DBUS_TYPE_STRING, &pwd_utf8, DBUS_TYPE_INVALID)) { + kdDebug() << "decrypt failed for " << udi << ": could not append args to dbus message\n"; + dbus_message_unref (msg); + return i18n("Internal Error"); + } + + dbus_error_init (&error); + if (!(reply = dbus_connection_send_with_reply_and_block (dbus_connection, msg, -1, &error)) || + dbus_error_is_set (&error)) + { + TQString qerror = i18n("Internal Error"); + kdDebug() << "decrypt failed for " << udi << ": " << error.name << " " << error.message << endl; + if (strcmp (error.name, "org.freedesktop.Hal.Device.Volume.Crypto.SetupPasswordError") == 0) { + qerror = i18n("Wrong password"); + } + dbus_error_free (&error); + dbus_message_unref (msg); + while (dbus_connection_dispatch(dbus_connection) == DBUS_DISPATCH_DATA_REMAINS) ; + return qerror; + } + + dbus_message_unref (msg); + dbus_message_unref (reply); + + while (dbus_connection_dispatch(dbus_connection) == DBUS_DISPATCH_DATA_REMAINS) ; + + return TQString(); +} + +TQString HALBackend::undecrypt(const TQString &_udi) +{ + const Medium* medium = m_mediaList.findById(_udi); + if (!medium) + return i18n("No such medium: %1").arg(_udi); + + if (!medium->isEncrypted() || medium->clearDeviceUdi().isNull()) + return TQString(); + + const char *udi = medium->id().latin1(); + DBusMessage *msg = NULL; + DBusMessage *reply = NULL; + DBusError error; + + kdDebug() << "Tear down " << udi << "\n" <<endl; + + msg = dbus_message_new_method_call ("org.freedesktop.Hal", udi, + "org.freedesktop.Hal.Device.Volume.Crypto", + "Teardown"); + if (msg == NULL) { + kdDebug() << "teardown failed for " << udi << ": could not create dbus message\n"; + return i18n("Internal Error"); + } + + if (!dbus_message_append_args (msg, DBUS_TYPE_INVALID)) { + kdDebug() << "teardown failed for " << udi << ": could not append args to dbus message\n"; + dbus_message_unref (msg); + return i18n("Internal Error"); + } + + dbus_error_init (&error); + if (!(reply = dbus_connection_send_with_reply_and_block (dbus_connection, msg, -1, &error)) || + dbus_error_is_set (&error)) + { + TQString qerror = i18n("Internal Error"); + kdDebug() << "teardown failed for " << udi << ": " << error.name << " " << error.message << endl; + dbus_error_free (&error); + dbus_message_unref (msg); + while (dbus_connection_dispatch(dbus_connection) == DBUS_DISPATCH_DATA_REMAINS) ; + return qerror; + } + + dbus_message_unref (msg); + dbus_message_unref (reply); + + ResetProperties(udi); + + while (dbus_connection_dispatch(dbus_connection) == DBUS_DISPATCH_DATA_REMAINS) ; + + return TQString(); +} + +#include "halbackend.moc" diff --git a/tdeioslave/media/mediamanager/halbackend.h b/tdeioslave/media/mediamanager/halbackend.h new file mode 100644 index 000000000..8c8bbfce6 --- /dev/null +++ b/tdeioslave/media/mediamanager/halbackend.h @@ -0,0 +1,232 @@ +/* This file is part of the KDE Project + Copyright (c) 2004-2005 Jérôme Lodewyck <jerome dot lodewyck at normalesup dot org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/** +* This is a media:/ backend for the freedesktop Hardware Abstraction Layer +* Usage : create an instance of HALBackend, then call InitHal(). A false +* result from the later function means that something went wrong and that +* the backend shall not be used. +* +* @author Jérôme Lodewyck <jerome dot lodewyck at normalesup dot org> +* @short media:/ backend for the HAL +*/ + +#ifndef _HALBACKEND_H_ +#define _HALBACKEND_H_ + +#include "backendbase.h" + +#include <tqobject.h> +#include <tqstringlist.h> +#include <tqstring.h> + +#include <config.h> + +/* We acknowledge the the dbus API is unstable */ +#define DBUS_API_SUBJECT_TO_CHANGE +/* DBus-Qt bindings */ +#include <dbus/connection.h> +/* HAL libraries */ +#include <libhal.h> +#include <libhal-storage.h> + +namespace TDEIO { + class Job; +} + +class HALBackend : public TQObject, public BackendBase +{ +Q_OBJECT + +public: + /** + * Constructor + */ + HALBackend(MediaList &list, TQObject* parent); + + /** + * Destructor + */ + ~HALBackend(); + + /** + * Perform HAL initialization. + * + * @return true if succeded. If not, rely on some other backend + */ + bool InitHal(); + + /** + * List all devices and append them to the media device list (called only once, at startup). + * + * @return true if succeded, false otherwise + */ + bool ListDevices(); + + TQStringList mountoptions(const TQString &id); + + bool setMountoptions(const TQString &id, const TQStringList &options); + + TQString mount(const TQString &id); + TQString mount(const Medium *medium); + TQString unmount(const TQString &id); + TQString decrypt(const TQString &id, const TQString &password); + TQString undecrypt(const TQString &id); + +private: + /** + * Append a device in the media list. This function will check if the device + * is worth listing. + * + * @param udi Universal Device Id + * @param allowNotification Indicates if this event will be notified to the user + */ + void AddDevice(const char* udi, bool allowNotification=true); + + /** + * Remove a device from the device list + * + * @param udi Universal Device Id + */ + void RemoveDevice(const char* udi); + + /** + * A device has changed, update it + * + * @param udi Universal Device Id + */ + void ModifyDevice(const char *udi, const char* key); + + /** + * HAL informed that a special action has occured + * (e.g. device unplugged without unmounting) + * + * @param udi Universal Device Id + */ + void DeviceCondition(const char *udi, const char *condition); + + /** + * Integrate the DBus connection within qt main loop + */ + void MainLoopIntegration(DBusConnection *dbusConnection); + +/* Set media properties */ +private: + /** + * Reset properties for the given medium + */ + void ResetProperties(const char* MediumUdi, bool allowNotification=false); + + /** + * Find the medium that is concerned with device udi + */ + const char* findMediumUdiFromUdi(const char* udi); + + void setVolumeProperties(Medium* medium); + bool setFloppyProperties(Medium* medium); + void setFloppyMountState( Medium* medium ); + bool setFstabProperties(Medium* medium); + void setCameraProperties(Medium* medium); + TQString generateName(const TQString &devNode); + static TQString isInFstab(const Medium *medium); + static TQString listUsingProcesses(const Medium *medium); + static TQString killUsingProcesses(const Medium *medium); + +private slots: + void slotResult(TDEIO::Job *job); + +/* Hal call-backs -- from gvm*/ +public: + /** Invoked when a device is added to the Global Device List. + * + * @param ctx LibHal context + * @param udi Universal Device Id + */ + static void hal_device_added(LibHalContext *ctx, const char *udi); + + /** Invoked when a device is removed from the Global Device List. + * + * @param ctx LibHal context + * @param udi Universal Device Id + */ + static void hal_device_removed(LibHalContext *ctx, const char *udi); + + /** Invoked when a property of a device in the Global Device List is + * changed, and we have we have subscribed to changes for that device. + * + * @param ctx LibHal context + * @param udi Univerisal Device Id + * @param key Key of property + */ + static void hal_device_property_modified(LibHalContext *ctx, const char *udi, const char *key, + dbus_bool_t is_removed, dbus_bool_t is_added); + + /** Type for callback when a non-continuos condition occurs on a device + * + * @param udi Univerisal Device Id + * @param condition_name Name of the condition + * @param message D-BUS message with variable parameters depending on condition + */ + static void hal_device_condition(LibHalContext *ctx, const char *udi, + const char *condition_name, + const char* message + ); + + TQStringList getHALmountoptions(TQString udi); +/* HAL and DBus structures */ +private: + /** + * The HAL context connecting the whole application to the HAL + */ + LibHalContext* m_halContext; + + /** + * libhal-storage HAL policy, e.g. for icon names + */ + LibHalStoragePolicy* m_halStoragePolicy; + + /** + * The DBus-Qt bindings connection for mainloop integration + */ + DBusQt::Connection* m_dBusQtConnection; + + /** + * Object for the kded module + */ + TQObject* m_parent; + + DBusConnection *dbus_connection; + + /** + * Data structure for fstab mount/unmount jobs + */ + struct mount_job_data { + // [in] Medium, which is being mounted/unmounted by the job + const Medium* medium; + // [in,out] Should be set to true when the job completes + bool completed; + // [out] TDEIO::Error if an error occured during operation. Otherwise, 0 + int error; + // [out] Error message to be displayed to the user + TQString errorMessage; + }; + + TQMap<TDEIO::Job *, struct mount_job_data*> mount_jobs; +}; + +#endif /* _HALBACKEND_H_ */ diff --git a/tdeioslave/media/mediamanager/linuxcdpolling.cpp b/tdeioslave/media/mediamanager/linuxcdpolling.cpp new file mode 100644 index 000000000..0fcda8d0f --- /dev/null +++ b/tdeioslave/media/mediamanager/linuxcdpolling.cpp @@ -0,0 +1,585 @@ +/* This file is part of the KDE Project + Copyright (c) 2003 Gav Wood <gav kde org> + Copyright (c) 2004 Kévin Ottens <ervin ipsquad net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/* Some code of this file comes from kdeautorun */ + +#include "linuxcdpolling.h" + +#include <tqthread.h> +#include <tqmutex.h> +#include <tqfile.h> + +#include <kdebug.h> + +#include "fstabbackend.h" + +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> + +// Never ever include directly a kernel header! +// #include <linux/cdrom.h> +// Instead we redefine the necessary (copied from the header) + +/* This struct is used by the CDROMREADTOCHDR ioctl */ +struct cdrom_tochdr +{ + unsigned char cdth_trk0; /* start track */ + unsigned char cdth_trk1; /* end track */ +}; + +#define CDROMREADTOCHDR 0x5305 /* Read TOC header + (struct cdrom_tochdr) */ +#define CDROM_DRIVE_STATUS 0x5326 /* Get tray position, etc. */ +#define CDROM_DISC_STATUS 0x5327 /* Get disc type, etc. */ + +/* drive status possibilities returned by CDROM_DRIVE_STATUS ioctl */ +#define CDS_NO_INFO 0 /* if not implemented */ +#define CDS_NO_DISC 1 +#define CDS_TRAY_OPEN 2 +#define CDS_DRIVE_NOT_READY 3 +#define CDS_DISC_OK 4 + +/* return values for the CDROM_DISC_STATUS ioctl */ +/* can also return CDS_NO_[INFO|DISC], from above */ +#define CDS_AUDIO 100 +#define CDS_DATA_1 101 +#define CDS_DATA_2 102 +#define CDS_XA_2_1 103 +#define CDS_XA_2_2 104 +#define CDS_MIXED 105 + +#define CDSL_CURRENT ((int) (~0U>>1)) + +// ------- + + + +DiscType::DiscType(Type type) + : m_type(type) +{ +} + +bool DiscType::isKnownDisc() const +{ + return m_type != None + && m_type != Unknown + && m_type != UnknownType + && m_type != Broken; +} + +bool DiscType::isDisc() const +{ + return m_type != None + && m_type != Unknown + && m_type != Broken; +} + +bool DiscType::isNotDisc() const +{ + return m_type == None; +} + +bool DiscType::isData() const +{ + return m_type == Data; +} + +DiscType::operator int() const +{ + return (int)m_type; +} + + +class PollingThread : public TQThread +{ +public: + PollingThread(const TQCString &devNode) : m_dev(devNode) + { + kdDebug(1219) << "PollingThread::PollingThread(" + << devNode << ")" << endl; + m_stop = false; + m_currentType = DiscType::None; + m_lastPollType = DiscType::None; + } + + + void stop() + { + TQMutexLocker locker(&m_mutex); + m_stop = true; + } + + bool hasChanged() + { + TQMutexLocker locker(&m_mutex); + + return m_currentType!=m_lastPollType; + } + + DiscType type() + { + TQMutexLocker locker(&m_mutex); + m_currentType = m_lastPollType; + return m_currentType; + } + +protected: + virtual void run() + { + kdDebug(1219) << "PollingThread(" << m_dev << ") start" << endl; + while (!m_stop && m_lastPollType!=DiscType::Broken) + { + m_mutex.lock(); + DiscType type = m_lastPollType; + m_mutex.unlock(); + + type = LinuxCDPolling::identifyDiscType(m_dev, type); + + m_mutex.lock(); + m_lastPollType = type; + m_mutex.unlock(); + + msleep(500); + } + kdDebug(1219) << "PollingThread(" << m_dev << ") stop" << endl; + } + +private: + TQMutex m_mutex; + bool m_stop; + const TQCString m_dev; + DiscType m_currentType; + DiscType m_lastPollType; +}; + + +LinuxCDPolling::LinuxCDPolling(MediaList &list) + : TQObject(), BackendBase(list) +{ + connect(&m_mediaList, TQT_SIGNAL(mediumAdded(const TQString &, + const TQString &, bool)), + this, TQT_SLOT(slotMediumAdded(const TQString &)) ); + + connect(&m_mediaList, TQT_SIGNAL(mediumRemoved(const TQString &, + const TQString &, bool)), + this, TQT_SLOT(slotMediumRemoved(const TQString &)) ); + + connect(&m_mediaList, TQT_SIGNAL(mediumStateChanged(const TQString &, + const TQString &, bool, bool)), + this, TQT_SLOT(slotMediumStateChanged(const TQString &)) ); + + connect(&m_timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotTimeout())); +} + +LinuxCDPolling::~LinuxCDPolling() +{ + TQMap<TQString, PollingThread*>::iterator it = m_threads.begin(); + TQMap<TQString, PollingThread*>::iterator end = m_threads.end(); + + for(; it!=end; ++it) + { + PollingThread *thread = it.data(); + thread->stop(); + thread->wait(); + delete thread; + } +} + +void LinuxCDPolling::slotMediumAdded(const TQString &id) +{ + kdDebug(1219) << "LinuxCDPolling::slotMediumAdded(" << id << ")" << endl; + + if (m_threads.contains(id)) return; + + const Medium *medium = m_mediaList.findById(id); + + TQString mime = medium->mimeType(); + kdDebug(1219) << "mime == " << mime << endl; + + if (mime.find("dvd")==-1 && mime.find("cd")==-1) return; + + if (!medium->isMounted()) + { + m_excludeNotification.append( id ); + + TQCString dev = TQFile::encodeName( medium->deviceNode() ).data(); + PollingThread *thread = new PollingThread(dev); + m_threads[id] = thread; + thread->start(); + m_timer.start(500); + } +} + +void LinuxCDPolling::slotMediumRemoved(const TQString &id) +{ + kdDebug(1219) << "LinuxCDPolling::slotMediumRemoved(" << id << ")" << endl; + + if (!m_threads.contains(id)) return; + + PollingThread *thread = m_threads[id]; + m_threads.remove(id); + thread->stop(); + thread->wait(); + delete thread; + + m_excludeNotification.remove(id); +} + +void LinuxCDPolling::slotMediumStateChanged(const TQString &id) +{ + kdDebug(1219) << "LinuxCDPolling::slotMediumStateChanged(" + << id << ")" << endl; + + const Medium *medium = m_mediaList.findById(id); + + TQString mime = medium->mimeType(); + kdDebug(1219) << "mime == " << mime << endl; + + if (mime.find("dvd")==-1 && mime.find("cd")==-1) return; + + if (!m_threads.contains(id) && !medium->isMounted()) + { + // It is just a mount state change, no need to notify + m_excludeNotification.append( id ); + + TQCString dev = TQFile::encodeName( medium->deviceNode() ).data(); + PollingThread *thread = new PollingThread(dev); + m_threads[id] = thread; + thread->start(); + m_timer.start(500); + } + else if (m_threads.contains(id) && medium->isMounted()) + { + PollingThread *thread = m_threads[id]; + m_threads.remove(id); + thread->stop(); + thread->wait(); + delete thread; + } +} + +void LinuxCDPolling::slotTimeout() +{ + //kdDebug(1219) << "LinuxCDPolling::slotTimeout()" << endl; + + if (m_threads.isEmpty()) + { + m_timer.stop(); + return; + } + + TQMap<TQString, PollingThread*>::iterator it = m_threads.begin(); + TQMap<TQString, PollingThread*>::iterator end = m_threads.end(); + + for(; it!=end; ++it) + { + TQString id = it.key(); + PollingThread *thread = it.data(); + + if (thread->hasChanged()) + { + DiscType type = thread->type(); + const Medium *medium = m_mediaList.findById(id); + applyType(type, medium); + } + } +} + +static TQString baseType(const Medium *medium) +{ + kdDebug(1219) << "baseType(" << medium->id() << ")" << endl; + + TQString devNode = medium->deviceNode(); + TQString mountPoint = medium->mountPoint(); + TQString fsType = medium->fsType(); + bool mounted = medium->isMounted(); + + TQString mimeType, iconName, label; + + FstabBackend::guess(devNode, mountPoint, fsType, mounted, + mimeType, iconName, label); + + if (devNode.find("dvd")!=-1) + { + kdDebug(1219) << "=> dvd" << endl; + return "dvd"; + } + else + { + kdDebug(1219) << "=> cd" << endl; + return "cd"; + } +} + +static void restoreEmptyState(MediaList &list, const Medium *medium, + bool allowNotification) +{ + kdDebug(1219) << "restoreEmptyState(" << medium->id() << ")" << endl; + + TQString id = medium->id(); + TQString devNode = medium->deviceNode(); + TQString mountPoint = medium->mountPoint(); + TQString fsType = medium->fsType(); + bool mounted = medium->isMounted(); + + TQString mimeType, iconName, label; + + FstabBackend::guess(devNode, mountPoint, fsType, mounted, + mimeType, iconName, label); + + list.changeMediumState(id, devNode, mountPoint, fsType, mounted, + allowNotification, mimeType, iconName, label); +} + + +void LinuxCDPolling::applyType(DiscType type, const Medium *medium) +{ + kdDebug(1219) << "LinuxCDPolling::applyType(" << type << ", " + << medium->id() << ")" << endl; + + TQString id = medium->id(); + TQString dev = medium->deviceNode(); + + bool notify = !m_excludeNotification.contains(id); + m_excludeNotification.remove(id); + + switch (type) + { + case DiscType::Data: + restoreEmptyState(m_mediaList, medium, notify); + break; + case DiscType::Audio: + case DiscType::Mixed: + m_mediaList.changeMediumState(id, "audiocd:/?device="+dev, + notify, "media/audiocd"); + break; + case DiscType::VCD: + m_mediaList.changeMediumState(id, false, notify, "media/vcd"); + break; + case DiscType::SVCD: + m_mediaList.changeMediumState(id, false, notify, "media/svcd"); + break; + case DiscType::DVD: + m_mediaList.changeMediumState(id, false, notify, "media/dvdvideo"); + break; + case DiscType::Blank: + if (baseType(medium)=="dvd") + { + m_mediaList.changeMediumState(id, false, + notify, "media/blankdvd"); + } + else + { + m_mediaList.changeMediumState(id, false, + notify, "media/blankcd"); + } + break; + case DiscType::None: + case DiscType::Unknown: + case DiscType::UnknownType: + restoreEmptyState(m_mediaList, medium, false); + break; + } +} + +DiscType LinuxCDPolling::identifyDiscType(const TQCString &devNode, + const DiscType ¤t) +{ + //kdDebug(1219) << "LinuxCDPolling::identifyDiscType(" + // << devNode << ")" << endl; + + int fd; + struct cdrom_tochdr th; + + // open the device + fd = open(devNode, O_RDONLY | O_NONBLOCK); + if (fd < 0) return DiscType::Broken; + + switch (ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT)) + { + case CDS_DISC_OK: + { + if (current.isDisc()) + { + close(fd); + return current; + } + + // see if we can read the disc's table of contents (TOC). + if (ioctl(fd, CDROMREADTOCHDR, &th)) + { + close(fd); + return DiscType::Blank; + } + + // read disc status info + int status = ioctl(fd, CDROM_DISC_STATUS, CDSL_CURRENT); + + // release the device + close(fd); + + switch (status) + { + case CDS_AUDIO: + return DiscType::Audio; + case CDS_DATA_1: + case CDS_DATA_2: + if (hasDirectory(devNode, "video_ts")) + { + return DiscType::DVD; + } + else if (hasDirectory(devNode, "vcd")) + { + return DiscType::VCD; + } + else if (hasDirectory(devNode, "svcd")) + { + return DiscType::SVCD; + } + else + { + return DiscType::Data; + } + case CDS_MIXED: + return DiscType::Mixed; + default: + return DiscType::UnknownType; + } + } + case CDS_NO_INFO: + close(fd); + return DiscType::Unknown; + default: + close(fd); + return DiscType::None; + } +} + +bool LinuxCDPolling::hasDirectory(const TQCString &devNode, const TQCString &dir) +{ + bool ret = false; // return value + int fd = 0; // file descriptor for drive + unsigned short bs; // the discs block size + unsigned short ts; // the path table size + unsigned int tl; // the path table location (in blocks) + unsigned char len_di = 0; // length of the directory name in current path table entry + unsigned int parent = 0; // the number of the parent directory's path table entry + char dirname[256]; // filename for the current path table entry + int pos = 0; // our position into the path table + int curr_record = 1; // the path table record we're on + TQCString fixed_directory = dir.upper(); // the uppercase version of the "directory" parameter + + // open the drive + fd = open(devNode, O_RDONLY | O_NONBLOCK); + if (fd == -1) return false; + + // read the block size + lseek(fd, 0x8080, SEEK_CUR); + if (read(fd, &bs, 2) != 2) + { + close(fd); + return false; + } + if (Q_BYTE_ORDER != Q_LITTLE_ENDIAN) + bs = ((bs << 8) & 0xFF00) | ((bs >> 8) & 0xFF); + + // read in size of path table + lseek(fd, 2, SEEK_CUR); + if (read(fd, &ts, 2) != 2) + { + close(fd); + return false; + } + if (Q_BYTE_ORDER != Q_LITTLE_ENDIAN) + ts = ((ts << 8) & 0xFF00) | ((ts >> 8) & 0xFF); + + // read in which block path table is in + lseek(fd, 6, SEEK_CUR); + if (read(fd, &tl, 4) != 4) + { + close(fd); + return false; + } + if (Q_BYTE_ORDER != Q_LITTLE_ENDIAN) + tl = ((tl << 24) & 0xFF000000) | ((tl << 8) & 0xFF0000) | + ((tl >> 8) & 0xFF00) | ((tl >> 24) & 0xFF); + + // seek to the path table + lseek(fd, bs * tl, SEEK_SET); + + // loop through the path table entries + while (pos < ts) + { + // get the length of the filename of the current entry + if (read(fd, &len_di, 1) != 1) + { + ret = false; + break; + } + + // get the record number of this entry's parent + // i'm pretty sure that the 1st entry is always the top directory + lseek(fd, 5, SEEK_CUR); + if (read(fd, &parent, 2) != 2) + { + ret = false; + break; + } + if (Q_BYTE_ORDER != Q_LITTLE_ENDIAN) + parent = ((parent << 8) & 0xFF00) | ((parent >> 8) & 0xFF); + + // read the name + if (read(fd, dirname, len_di) != len_di) + { + ret = false; + break; + } + dirname[len_di] = 0; + qstrcpy(dirname, TQCString(dirname).upper()); + + // if we found a folder that has the root as a parent, and the directory name matches + // then return success + if ((parent == 1) && (dirname == fixed_directory)) + { + ret = true; + break; + } + + // all path table entries are padded to be even, so if this is an odd-length table, seek a byte to fix it + if (len_di%2 == 1) + { + lseek(fd, 1, SEEK_CUR); + pos++; + } + + // update our position + pos += 8 + len_di; + curr_record++; + } + + close(fd); + return ret; +} + + +#include "linuxcdpolling.moc" diff --git a/tdeioslave/media/mediamanager/linuxcdpolling.h b/tdeioslave/media/mediamanager/linuxcdpolling.h new file mode 100644 index 000000000..3dec7e3ee --- /dev/null +++ b/tdeioslave/media/mediamanager/linuxcdpolling.h @@ -0,0 +1,86 @@ +/* This file is part of the KDE Project + Copyright (c) 2004 Kvin Ottens <ervin ipsquad net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _LINUXCDPOLLING_H_ +#define _LINUXCDPOLLING_H_ + +#include "backendbase.h" + +#include <tqobject.h> +#include <tqcstring.h> +#include <tqmap.h> +#include <tqtimer.h> + +class DiscType +{ +public: + enum Type { None, Unknown, Audio, Data, DVD, Mixed, + Blank, VCD, SVCD, UnknownType, Broken }; + + DiscType(Type type = Unknown); + + bool isKnownDisc() const; + bool isDisc() const; + bool isNotDisc() const; + bool isData() const; + + operator int() const; + +private: + Type m_type; +}; + +class PollingThread; + +class LinuxCDPolling : public TQObject, public BackendBase +{ +Q_OBJECT + +public: + + LinuxCDPolling(MediaList &list); + virtual ~LinuxCDPolling(); + + /** + * Find the disc type of the medium inserted in a drive + * (considered to be a cdrom or dvdrom) + * + * @param devNode the path to the device to test + * @param current the current known state of the drive + * @return the disc type + */ + static DiscType identifyDiscType(const TQCString &devNode, + const DiscType ¤t = DiscType::Unknown); + +private slots: + void slotMediumAdded(const TQString &id); + void slotMediumRemoved(const TQString &id); + void slotMediumStateChanged(const TQString &id); + void slotTimeout(); + +private: + void applyType(DiscType type, const Medium *medium); + + static bool hasDirectory(const TQCString &devNode, const TQCString &dir); + + TQMap<TQString, PollingThread*> m_threads; + TQStringList m_excludeNotification; + TQTimer m_timer; +}; + +#endif diff --git a/tdeioslave/media/mediamanager/mediadirnotify.cpp b/tdeioslave/media/mediamanager/mediadirnotify.cpp new file mode 100644 index 000000000..9e6658153 --- /dev/null +++ b/tdeioslave/media/mediamanager/mediadirnotify.cpp @@ -0,0 +1,124 @@ +/* This file is part of the KDE Project + Copyright (c) 2004 Kévin Ottens <ervin ipsquad net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "mediadirnotify.h" + +#include <kdebug.h> + +#include <kdirnotify_stub.h> + +#include "medium.h" + +MediaDirNotify::MediaDirNotify(const MediaList &list) + : m_mediaList(list) +{ + +} + +KURL::List MediaDirNotify::toMediaURL(const KURL &url) +{ + kdDebug(1219) << "MediaDirNotify::toMediaURL(" << url << ")" << endl; + + KURL::List result; + + const TQPtrList<Medium> list = m_mediaList.list(); + + TQPtrList<Medium>::const_iterator it = list.begin(); + TQPtrList<Medium>::const_iterator end = list.end(); + + for (; it!=end; ++it) + { + const Medium *m = *it; + KURL base = m->prettyBaseURL(); + + if ( base.isParentOf(url) ) + { + TQString path = KURL::relativePath(base.path(), + url.path()); + + KURL new_url("media:/"+m->name()+"/"+path ); + new_url.cleanPath(); + + result.append(new_url); + } + } + + kdDebug(1219) << result << endl; + return result; +} + +KURL::List MediaDirNotify::toMediaURLList(const KURL::List &list) +{ + KURL::List new_list; + + KURL::List::const_iterator it = list.begin(); + KURL::List::const_iterator end = list.end(); + + for (; it!=end; ++it) + { + KURL::List urls = toMediaURL(*it); + + if (!urls.isEmpty()) + { + new_list += urls; + } + } + + return new_list; +} + +ASYNC MediaDirNotify::FilesAdded(const KURL &directory) +{ + KURL::List new_urls = toMediaURL(directory); + + if (!new_urls.isEmpty()) + { + KDirNotify_stub notifier("*", "*"); + + KURL::List::const_iterator it = new_urls.begin(); + KURL::List::const_iterator end = new_urls.end(); + + for (; it!=end; ++it) + { + notifier.FilesAdded(*it); + } + } +} + +ASYNC MediaDirNotify::FilesRemoved(const KURL::List &fileList) +{ + KURL::List new_list = toMediaURLList(fileList); + + if (!new_list.isEmpty()) + { + KDirNotify_stub notifier("*", "*"); + notifier.FilesRemoved( new_list ); + } +} + +ASYNC MediaDirNotify::FilesChanged(const KURL::List &fileList) +{ + KURL::List new_list = toMediaURLList(fileList); + + if (!new_list.isEmpty()) + { + KDirNotify_stub notifier("*", "*"); + notifier.FilesChanged( new_list ); + } +} + diff --git a/tdeioslave/media/mediamanager/mediadirnotify.h b/tdeioslave/media/mediamanager/mediadirnotify.h new file mode 100644 index 000000000..beb1b8849 --- /dev/null +++ b/tdeioslave/media/mediamanager/mediadirnotify.h @@ -0,0 +1,47 @@ +/* This file is part of the KDE Project + Copyright (c) 2004 Kvin Ottens <ervin ipsquad net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _MEDIADIRNOTIFY_H_ +#define _MEDIADIRNOTIFY_H_ + +#include <kurl.h> +#include <kdirnotify.h> + +#include "medialist.h" + + +class MediaDirNotify : public KDirNotify +{ +K_DCOP + +public: + MediaDirNotify(const MediaList &list); + +k_dcop: + virtual ASYNC FilesAdded (const KURL &directory); + virtual ASYNC FilesRemoved (const KURL::List &fileList); + virtual ASYNC FilesChanged (const KURL::List &fileList); + +private: + KURL::List toMediaURL(const KURL &url); + KURL::List toMediaURLList(const KURL::List &list); + + const MediaList &m_mediaList; +}; + +#endif diff --git a/tdeioslave/media/mediamanager/medialist.cpp b/tdeioslave/media/mediamanager/medialist.cpp new file mode 100644 index 000000000..6ac7dc341 --- /dev/null +++ b/tdeioslave/media/mediamanager/medialist.cpp @@ -0,0 +1,303 @@ +/* This file is part of the KDE Project + Copyright (c) 2004 Kévin Ottens <ervin ipsquad net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "medialist.h" + +#include <kdebug.h> + +MediaList::MediaList() +{ + kdDebug(1219) << "MediaList::MediaList()" << endl; + + m_media.setAutoDelete(true); +} + +const TQPtrList<Medium> MediaList::list() const +{ + kdDebug(1219) << "MediaList::list()" << endl; + + return m_media; +} + +const Medium *MediaList::findById(const TQString &id) const +{ + kdDebug(1219) << "MediaList::findById(" << id << ")" << endl; + + if ( !m_idMap.contains(id) ) return 0L; + + return m_idMap[id]; +} + +const Medium *MediaList::findByName(const TQString &name) const +{ + kdDebug(1219) << "MediaList::findByName(" << name << ")" << endl; + + if ( !m_nameMap.contains(name) ) return 0L; + + return m_nameMap[name]; +} + +const Medium *MediaList::findByClearUdi(const TQString &name) +{ + kdDebug(1219) << "MediaList::findByClearUdi(" << name << ")" << endl; + + Medium *medium; + for (medium = m_media.first(); medium; medium = m_media.next()) { + if (medium->clearDeviceUdi() == name) return medium; + } + + return 0L; +} + + +TQString MediaList::addMedium(Medium *medium, bool allowNotification) +{ + kdDebug(1219) << "MediaList::addMedium(@" << medium->id() << ")" << endl; + + TQString id = medium->id(); + if ( m_idMap.contains(id) ) return TQString::null; + + m_media.append( medium ); + m_idMap[id] = medium; + + TQString name = medium->name(); + if ( !m_nameMap.contains(name) ) + { + m_nameMap[name] = medium; + + kdDebug(1219) << "MediaList emits mediumAdded(" << id << ", " + << name << ")" << endl; + emit mediumAdded(id, name, allowNotification); + + return name; + } + + TQString base_name = name+"_"; + int i = 1; + + while ( m_nameMap.contains(base_name+TQString::number(i)) ) + { + i++; + } + + name = base_name+TQString::number(i); + medium->setName(name); + m_nameMap[name] = medium; + + kdDebug(1219) << "MediaList emits mediumAdded(" << id << ", " + << name << ")" << endl; + emit mediumAdded(id, name, allowNotification); + return name; +} + +bool MediaList::removeMedium(const TQString &id, bool allowNotification) +{ + kdDebug(1219) << "MediaList::removeMedium(" << id << ")" << endl; + + if ( !m_idMap.contains(id) ) return false; + + Medium *medium = m_idMap[id]; + TQString name = medium->name(); + + m_idMap.remove(id); + m_nameMap.remove( medium->name() ); + m_media.remove( medium ); + + emit mediumRemoved(id, name, allowNotification); + return true; +} + +bool MediaList::changeMediumState(const Medium &medium, bool allowNotification) +{ + kdDebug(1219) << "MediaList::changeMediumState(const Medium &)" << endl; + + if ( !m_idMap.contains(medium.id()) ) return false; + + Medium *m = m_idMap[medium.id()]; + + if ( medium.isMountable() ) + { + TQString device_node = medium.deviceNode(); + TQString clear_device_udi = medium.clearDeviceUdi(); + TQString mount_point = medium.mountPoint(); + TQString fs_type = medium.fsType(); + bool mounted = medium.isMounted(); + + m->mountableState( device_node, clear_device_udi, mount_point, + fs_type, mounted ); + } + else + { + m->unmountableState( medium.baseURL() ); + } + + + if (!medium.mimeType().isEmpty()) + { + m->setMimeType( medium.mimeType() ); + } + + if (!medium.iconName().isEmpty()) + { + m->setIconName( medium.iconName() ); + } + + if (!medium.label().isEmpty()) + { + m->setLabel( medium.label() ); + } + + m->setHidden(medium.hidden()); + + emit mediumStateChanged(m->id(), m->name(), !m->needMounting(), allowNotification); + return true; +} + +bool MediaList::changeMediumState(const TQString &id, + const TQString &baseURL, + bool allowNotification, + const TQString &mimeType, + const TQString &iconName, + const TQString &label) +{ + kdDebug(1219) << "MediaList::changeMediumState(" << id << ", " + << baseURL << ", " << mimeType << ", " << iconName << ")" + << endl; + + if ( !m_idMap.contains(id) ) return false; + + Medium *medium = m_idMap[id]; + + medium->unmountableState( baseURL ); + + if (!mimeType.isEmpty()) + { + medium->setMimeType( mimeType ); + } + + if (!iconName.isEmpty()) + { + medium->setIconName( iconName ); + } + + if (!label.isEmpty()) + { + medium->setLabel( label ); + } + + emit mediumStateChanged(id, medium->name(), + !medium->needMounting(), + allowNotification); + return true; +} + +bool MediaList::changeMediumState(const TQString &id, + const TQString &deviceNode, + const TQString &mountPoint, + const TQString &fsType, bool mounted, + bool allowNotification, + const TQString &mimeType, + const TQString &iconName, + const TQString &label) +{ + kdDebug(1219) << "MediaList::changeMediumState(" << id << ", " + << deviceNode << ", " << mountPoint << ", " << fsType << ", " + << mounted << ", " << mimeType << ", " << iconName << ")" + << endl; + + if ( !m_idMap.contains(id) ) return false; + + Medium *medium = m_idMap[id]; + + medium->mountableState( deviceNode, mountPoint, fsType, mounted ); + + if (!mimeType.isEmpty()) + { + medium->setMimeType( mimeType ); + } + + if (!iconName.isEmpty()) + { + medium->setIconName( iconName ); + } + + if (!label.isEmpty()) + { + medium->setLabel( label ); + } + + emit mediumStateChanged(id, medium->name(), + !medium->needMounting(), + allowNotification); + return true; +} + +bool MediaList::changeMediumState(const TQString &id, bool mounted, + bool allowNotification, + const TQString &mimeType, + const TQString &iconName, + const TQString &label) +{ + kdDebug(1219) << "MediaList::changeMediumState(" << id << ", " + << mounted << ", " << mimeType << ", " << iconName << ")" + << endl; + + if ( !m_idMap.contains(id) ) return false; + + Medium *medium = m_idMap[id]; + + if ( !medium->mountableState( mounted ) ) return false; + + if (!mimeType.isEmpty()) + { + medium->setMimeType( mimeType ); + } + + if (!iconName.isEmpty()) + { + medium->setIconName( iconName ); + } + + if (!label.isEmpty()) + { + medium->setLabel( label ); + } + + emit mediumStateChanged(id, medium->name(), + !medium->needMounting(), + allowNotification); + return true; +} + +bool MediaList::setUserLabel(const TQString &name, const TQString &label) +{ + kdDebug(1219) << "MediaList::setUserLabel(" << name << ", " + << label << ")" << endl; + + if ( !m_nameMap.contains(name) ) return false; + + Medium *medium = m_nameMap[name]; + medium->setUserLabel(label); + + emit mediumStateChanged(medium->id(), name, + !medium->needMounting(), + false); + return true; +} + +#include "medialist.moc" diff --git a/tdeioslave/media/mediamanager/medialist.h b/tdeioslave/media/mediamanager/medialist.h new file mode 100644 index 000000000..59ddb6bd2 --- /dev/null +++ b/tdeioslave/media/mediamanager/medialist.h @@ -0,0 +1,80 @@ +/* This file is part of the KDE Project + Copyright (c) 2004 Kvin Ottens <ervin ipsquad net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _MEDIALIST_H_ +#define _MEDIALIST_H_ + +#include <tqobject.h> + +#include "medium.h" + +class MediaList : public QObject +{ +Q_OBJECT + +public: + MediaList(); + + // FIXME: should be <const Medium> or something similar... + const TQPtrList<Medium> list() const; + const Medium *findById(const TQString &id) const; + const Medium *findByName(const TQString &name) const; + const Medium *findByClearUdi(const TQString &name); + +public: + TQString addMedium(Medium *medium, bool allowNotification = true); + bool removeMedium(const TQString &id, bool allowNotification = true); + + bool changeMediumState(const Medium &medium, bool allowNotification); + bool changeMediumState(const TQString &id, + const TQString &baseURL, + bool allowNotification = true, + const TQString &mimeType = TQString::null, + const TQString &iconName = TQString::null, + const TQString &label = TQString::null); + bool changeMediumState(const TQString &id, + const TQString &deviceNode, + const TQString &mountPoint, + const TQString &fsType, bool mounted, + bool allowNotification = true, + const TQString &mimeType = TQString::null, + const TQString &iconName = TQString::null, + const TQString &label = TQString::null); + bool changeMediumState(const TQString &id, bool mounted, + bool allowNotification = true, + const TQString &mimeType = TQString::null, + const TQString &iconName = TQString::null, + const TQString &label = TQString::null); + + bool setUserLabel(const TQString &name, const TQString &label); + +signals: + void mediumAdded(const TQString &id, const TQString &name, + bool allowNotification); + void mediumRemoved(const TQString &id, const TQString &name, + bool allowNotification); + void mediumStateChanged(const TQString &id, const TQString &name, + bool mounted, bool allowNotification); + +private: + TQPtrList<Medium> m_media; + TQMap<TQString,Medium*> m_nameMap; + TQMap<TQString,Medium*> m_idMap; +}; + +#endif diff --git a/tdeioslave/media/mediamanager/mediamanager.cpp b/tdeioslave/media/mediamanager/mediamanager.cpp new file mode 100644 index 000000000..db2c12a71 --- /dev/null +++ b/tdeioslave/media/mediamanager/mediamanager.cpp @@ -0,0 +1,423 @@ +/* This file is part of the KDE Project + Copyright (c) 2004 Kévin Ottens <ervin ipsquad net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "mediamanager.h" + +#include <config.h> +#include <tqtimer.h> + +#include <kdebug.h> +#include <kglobal.h> +#include <klocale.h> + +#include <kdirnotify_stub.h> +#include <kstandarddirs.h> + +#include "mediamanagersettings.h" + +#include "fstabbackend.h" + +#ifdef COMPILE_TDEHARDWAREBACKEND +#include "tdehardwarebackend.h" +#endif // COMPILE_TDEHARDWAREBACKEND + +#ifdef COMPILE_HALBACKEND +#include "halbackend.h" +#endif //COMPILE_HALBACKEND + +#ifdef COMPILE_LINUXCDPOLLING +#include "linuxcdpolling.h" +#endif //COMPILE_LINUXCDPOLLING + + +MediaManager::MediaManager(const TQCString &obj) + : KDEDModule(obj), m_dirNotify(m_mediaList) +{ + connect( &m_mediaList, TQT_SIGNAL(mediumAdded(const TQString&, const TQString&, bool)), + TQT_SLOT(slotMediumAdded(const TQString&, const TQString&, bool)) ); + connect( &m_mediaList, TQT_SIGNAL(mediumRemoved(const TQString&, const TQString&, bool)), + TQT_SLOT(slotMediumRemoved(const TQString&, const TQString&, bool)) ); + connect( &m_mediaList, + TQT_SIGNAL(mediumStateChanged(const TQString&, const TQString&, bool, bool)), + TQT_SLOT(slotMediumChanged(const TQString&, const TQString&, bool, bool)) ); + + TQTimer::singleShot( 10, this, TQT_SLOT( loadBackends() ) ); +} + +MediaManager::~MediaManager() +{ + while ( !m_backends.isEmpty() ) + { + BackendBase *b = m_backends.first(); + m_backends.remove( b ); + delete b; + } +} + +void MediaManager::loadBackends() +{ + m_mediaList.blockSignals(true); + + while ( !m_backends.isEmpty() ) + { + BackendBase *b = m_backends.first(); + m_backends.remove( b ); + delete b; + } + + mp_removableBackend = 0L; + m_halbackend = 0L; + m_tdebackend = 0L; + m_fstabbackend = 0L; + +#ifdef COMPILE_HALBACKEND + if ( MediaManagerSettings::self()->halBackendEnabled() ) + { + m_mediaList.blockSignals(false); + m_halbackend = new HALBackend(m_mediaList, this); + if (m_halbackend->InitHal()) + { + m_backends.append( m_halbackend ); + m_fstabbackend = new FstabBackend(m_mediaList, true); + m_backends.append( m_fstabbackend ); + // No need to load something else... + m_mediaList.blockSignals(false); + return; + } + else + { + delete m_halbackend; + m_halbackend = 0; + m_mediaList.blockSignals(true); + } + } +#endif // COMPILE_HALBACKEND + +#ifdef COMPILE_TDEHARDWAREBACKEND + if ( MediaManagerSettings::self()->tdeHardwareBackendEnabled() ) + { + m_mediaList.blockSignals(false); + m_tdebackend = new TDEBackend(m_mediaList, this); + m_backends.append( m_tdebackend ); + m_fstabbackend = new FstabBackend(m_mediaList, true); + m_backends.append( m_fstabbackend ); + // No need to load something else... + m_mediaList.blockSignals(false); + return; + } +#endif // COMPILE_TDEHARDWAREBACKEND + + mp_removableBackend = new RemovableBackend(m_mediaList); + m_backends.append( mp_removableBackend ); + +#ifdef COMPILE_LINUXCDPOLLING + if ( MediaManagerSettings::self()->cdPollingEnabled() ) + { + m_backends.append( new LinuxCDPolling(m_mediaList) ); + } +#endif //COMPILE_LINUXCDPOLLING + + m_fstabbackend = new FstabBackend(m_mediaList); + m_backends.append( m_fstabbackend ); + m_mediaList.blockSignals(false); +} + + +TQStringList MediaManager::fullList() +{ + TQPtrList<Medium> list = m_mediaList.list(); + + TQStringList result; + + TQPtrList<Medium>::const_iterator it = list.begin(); + TQPtrList<Medium>::const_iterator end = list.end(); + for (; it!=end; ++it) + { + result+= (*it)->properties(); + result+= Medium::SEPARATOR; + } + + return result; +} + +TQStringList MediaManager::properties(const TQString &name) +{ + const Medium *m = m_mediaList.findByName(name); + + if (!m) + { + KURL u(name); + kdDebug() << "Media::prop " << name << " " << u.isValid() << endl; + if (u.isValid()) + { + if (u.protocol() == "system") + { + TQString path = u.path(); + if (path.startsWith("/media/")) + path = path.mid(strlen("/media/")); + m = m_mediaList.findByName(path); + kdDebug() << "findByName " << path << m << endl; + } + else if (u.protocol() == "media") + { + m = m_mediaList.findByName(u.filename()); + kdDebug() << "findByName " << u.filename() << m << endl; + } + else if (u.protocol() == "file") + { + // look for the mount point + TQPtrList<Medium> list = m_mediaList.list(); + TQPtrList<Medium>::const_iterator it = list.begin(); + TQPtrList<Medium>::const_iterator end = list.end(); + TQString path; + + for (; it!=end; ++it) + { + path = KStandardDirs::realFilePath(u.path()); + kdDebug() << "comparing " << (*it)->mountPoint() << " " << path << " " << (*it)->deviceNode() << endl; + if ((*it)->mountPoint() == path || (*it)->deviceNode() == path) { + m = *it; + break; + } + } + } + } + } + + if (m) { + return m->properties(); + } + else { + return TQStringList(); + } +} + +TQStringList MediaManager::mountoptions(const TQString &name) +{ +#ifdef COMPILE_HALBACKEND + if (!m_halbackend) + return TQStringList(); + return m_halbackend->mountoptions(name); +#else // COMPILE_HALBACKEND + #ifdef COMPILE_TDEHARDWAREBACKEND + if (!m_tdebackend) + return TQStringList(); + return m_tdebackend->mountoptions(name); + #else // COMPILE_TDEHARDWAREBACKEND + return TQStringList(); + #endif // COMPILE_TDEHARDWAREBACKEND +#endif // COMPILE_HALBACKEND +} + +bool MediaManager::setMountoptions(const TQString &name, const TQStringList &options) +{ +#ifdef COMPILE_HALBACKEND + if (!m_halbackend) + return false; + return m_halbackend->setMountoptions(name, options); +#else // COMPILE_HALBACKEND + #ifdef COMPILE_TDEHARDWAREBACKEND + if (!m_tdebackend) + return false; + return m_tdebackend->setMountoptions(name, options); + #else // COMPILE_TDEHARDWAREBACKEND + return false; + #endif // COMPILE_TDEHARDWAREBACKEND +#endif // COMPILE_HALBACKEND +} + +TQString MediaManager::mount(const TQString &name) +{ +#ifdef COMPILE_HALBACKEND + if (!m_halbackend) + return i18n("Feature only available with HAL"); + return m_halbackend->mount(name); +#else // COMPILE_HALBACKEND + #ifdef COMPILE_TDEHARDWAREBACKEND + if (!m_tdebackend) + return i18n("Feature only available with the TDE hardware backend"); + return m_tdebackend->mount(name); + #else // COMPILE_TDEHARDWAREBACKEND + if ( !m_fstabbackend ) // lying :) + return i18n("Feature only available with HAL or TDE hardware backend"); + return m_fstabbackend->mount( name ); + #endif // COMPILE_TDEHARDWAREBACKEND +#endif // COMPILE_HALBACKEND +} + +TQString MediaManager::unmount(const TQString &name) +{ +#ifdef COMPILE_HALBACKEND + if (!m_halbackend) + return i18n("Feature only available with HAL"); + return m_halbackend->unmount(name); +#else // COMPILE_HALBACKEND + #ifdef COMPILE_TDEHARDWAREBACKEND + if (!m_tdebackend) + return i18n("Feature only available with HAL or TDE hardware backend"); + return m_tdebackend->unmount(name); + #else // COMPILE_TDEHARDWAREBACKEND + if ( !m_fstabbackend ) // lying :) + return i18n("Feature only available with HAL or TDE hardware backend"); + return m_fstabbackend->unmount( name ); + #endif // COMPILE_TDEHARDWAREBACKEND +#endif // COMPILE_HALBACKEND +} + +TQString MediaManager::decrypt(const TQString &name, const TQString &password) +{ +#ifdef COMPILE_HALBACKEND + if (!m_halbackend) + return i18n("Feature only available with HAL"); + return m_halbackend->decrypt(name, password); +#else // COMPILE_HALBACKEND +// #ifdef COMPILE_TDEHARDWAREBACKEND +// if (!m_tdebackend) +// return i18n("Feature only available with HAL or TDE hardware backend"); +// return m_tdebackend->decrypt(name, password); +// #else // COMPILE_TDEHARDWAREBACKEND + return i18n("Feature only available with HAL"); +// #endif // COMPILE_TDEHARDWAREBACKEND +#endif // COMPILE_HALBACKEND +} + +TQString MediaManager::undecrypt(const TQString &name) +{ +#ifdef COMPILE_HALBACKEND + if (!m_halbackend) + return i18n("Feature only available with HAL"); + return m_halbackend->undecrypt(name); +#else // COMPILE_HALBACKEND +// #ifdef COMPILE_TDEHARDWAREBACKEND +// if (!m_tdebackend) +// return i18n("Feature only available with HAL or TDE hardware backend"); +// return m_tdebackend->undecrypt(name); +// #else // COMPILE_TDEHARDWAREBACKEND + return i18n("Feature only available with HAL"); +// #endif // COMPILE_TDEHARDWAREBACKEND +#endif // COMPILE_HALBACKEND +} + +TQString MediaManager::nameForLabel(const TQString &label) +{ + const TQPtrList<Medium> media = m_mediaList.list(); + + TQPtrList<Medium>::const_iterator it = media.begin(); + TQPtrList<Medium>::const_iterator end = media.end(); + for (; it!=end; ++it) + { + const Medium *m = *it; + + if (m->prettyLabel()==label) + { + return m->name(); + } + } + + return TQString::null; +} + +ASYNC MediaManager::setUserLabel(const TQString &name, const TQString &label) +{ + m_mediaList.setUserLabel(name, label); +} + +ASYNC MediaManager::reloadBackends() +{ + MediaManagerSettings::self()->readConfig(); + loadBackends(); +} + +bool MediaManager::removablePlug(const TQString &devNode, const TQString &label) +{ + if (mp_removableBackend) + { + return mp_removableBackend->plug(devNode, label); + } + return false; +} + +bool MediaManager::removableUnplug(const TQString &devNode) +{ + if (mp_removableBackend) + { + return mp_removableBackend->unplug(devNode); + } + return false; +} + +bool MediaManager::removableCamera(const TQString &devNode) +{ + if (mp_removableBackend) + { + return mp_removableBackend->camera(devNode); + } + return false; +} + + +void MediaManager::slotMediumAdded(const TQString &/*id*/, const TQString &name, + bool allowNotification) +{ + kdDebug(1219) << "MediaManager::slotMediumAdded: " << name << endl; + + KDirNotify_stub notifier("*", "*"); + notifier.FilesAdded( KURL("media:/") ); + + emit mediumAdded(name, allowNotification); + emit mediumAdded(name); +} + +void MediaManager::slotMediumRemoved(const TQString &/*id*/, const TQString &name, + bool allowNotification) +{ + kdDebug(1219) << "MediaManager::slotMediumRemoved: " << name << endl; + + KDirNotify_stub notifier("*", "*"); + notifier.FilesRemoved( KURL("media:/"+name) ); + + emit mediumRemoved(name, allowNotification); + emit mediumRemoved(name); +} + +void MediaManager::slotMediumChanged(const TQString &/*id*/, const TQString &name, + bool mounted, bool allowNotification) +{ + kdDebug(1219) << "MediaManager::slotMediumChanged: " << name << endl; + + KDirNotify_stub notifier("*", "*"); + if (!mounted) + { + notifier.FilesRemoved( KURL("media:/"+name) ); + } + notifier.FilesChanged( KURL("media:/"+name) ); + + emit mediumChanged(name, allowNotification); + emit mediumChanged(name); +} + + +extern "C" { + KDE_EXPORT KDEDModule *create_mediamanager(const TQCString &obj) + { + TDEGlobal::locale()->insertCatalogue("kio_media"); + return new MediaManager(obj); + } +} + +#include "mediamanager.moc" diff --git a/tdeioslave/media/mediamanager/mediamanager.desktop b/tdeioslave/media/mediamanager/mediamanager.desktop new file mode 100644 index 000000000..48261e428 --- /dev/null +++ b/tdeioslave/media/mediamanager/mediamanager.desktop @@ -0,0 +1,141 @@ +[Desktop Entry] +Type=Service +Name=KDED Media Manager +Name[af]=KDED Media Bestuurder +Name[ar]=مسيير الوسائط KDED +Name[az]=KDED Mediya İdarəcisi +Name[be]=Кіраўнік носьбітаў KDED +Name[bg]=Управление на устройствата KDED +Name[bn]=কে.ডি.ই.ডি. মিডিয়া ম্যানেজার +Name[bs]=KDED upravitelj medijima +Name[ca]=Gestor de suports KDED +Name[cs]=KDED správce médií +Name[csb]=Menedżer zôpisownëch mediów dlô KDED +Name[da]=KDED Mediehåndtering +Name[de]=Medienverwaltung +Name[el]=Διαχειριστής μέσων KDED +Name[eo]=KDatumportila administrilo +Name[es]=Gestor de dispositivos KDED +Name[et]=KDED andmekandjate haldur +Name[eu]=KDED media kudeatzailea +Name[fa]=مدیر رسانه KDED +Name[fi]=KDED-tallennusmedianhallinta +Name[fr]=Gestionnaire de média KDED +Name[fy]=KDEDED-mediabehearder +Name[ga]=Bainisteoir Meán KDED +Name[gl]=Xestor de Meios de KDED +Name[he]=מנהל המדיה של KDED +Name[hi]=केडीईडी मीडिया प्रबंधक +Name[hr]=KDED upravitelj medija +Name[hu]=KDED médiakezelő +Name[is]=KDED miðilstjóri +Name[it]=Gestore dei supporti KDED +Name[ja]=KDED メディアマネージャ +Name[ka]=მატარებლების მმართველი KDED +Name[kk]=KDED ауыстырмалы тасушыларды басқару +Name[km]=កម្មវិធីគ្រប់គ្រងព័ត៌មាន KDED +Name[ko]=KDE 창 관리자 +Name[lv]=KDED Datu nesēju menedžeris +Name[mk]=Менаџер на носачи KDED +Name[ms]=Pengurus Media KDED +Name[nb]=KDED mediebehandler +Name[nds]=KDED-Medienpleeg +Name[ne]=KDED मिडिया प्रबन्धक +Name[nl]=KDED-Mediabeheerder +Name[nn]=KDED Mediehandsamar +Name[pa]=KDED ਮਾਧਿਅਮ ਮੈਨੇਜਰ +Name[pl]=Menedżer nośników danych dla KDED +Name[pt]=Gestor de Dispositivos KDED +Name[pt_BR]=Gerenciador de Mídia +Name[ro]=Manager multimedia KDED +Name[ru]=Управление подключаемыми устройствами +Name[rw]=Mugenga Igihuza KDED +Name[se]=KDED-mediagieđahalli +Name[sk]=KDED správca médií +Name[sl]=Upravitelj medijev KDED +Name[sr]=Менаџер медијума, KDED +Name[sr@Latn]=Menadžer medijuma, KDED +Name[sv]=KDED-mediahanterare +Name[ta]=KDED மீடியா மேலாளர் +Name[te]=కెడిఈడి మాధ్యమ అభికర్త +Name[th]=เครื่องมือจัดการสื่อบันทึก KDED +Name[tr]=KDED Ortam Yöneticisi +Name[tt]=KDED Media İdäräçe +Name[uk]=Менеджер носіїв інформації для KDED +Name[uz]=KDED saqlash uskunalarni boshqaruvchi +Name[uz@cyrillic]=KDED сақлаш ускуналарни бошқарувчи +Name[vi]=Trình quản lí Ổ lưu trữ KDED +Name[wa]=Manaedjeu di fitchîs KDED +Name[zh_CN]=KDED 介质管理器 +Name[zh_TW]=KDED 媒體管理程式 +Comment=Keep track of media activities and allow to (un)mount (media:/) +Comment[af]=Hou tred van media aktiwiteite en laat die (ont)koppel van 'media:/' toe +Comment[ar]=يتتبع نشاطات الوسائط و يسمح بتكيبها/إزالة تركيبها (media:/) +Comment[be]=Вядзе інфармацыю аб медыяносьбітах і дазваляе прымацоўваць/адмацоўваць іх (media:/) +Comment[bg]=Наблюдение на устройствата и разрешаване на монтиране/демонтиране на (media:/) +Comment[bn]=কোথায় কী মিডিয়া ব্যবহার করা হচ্ছে খেয়াল রাখুন এবং (আন)মাউন্ট করুন (media:/) +Comment[bs]=Prati aktivnosti montiranja uređaja za smještaj podataka (media:/) +Comment[ca]=Fa el seguiment de les activitats dels suports i permet muntar i desmuntar (media:/) +Comment[cs]=Udržuje přehled o připojených zařízeních +Comment[csb]=Dozérô zdarzeniów sparłączonëch z mediama pòdôwków ë zezwôlô je (òd)mòntowac (media:/) +Comment[da]=Hold styr på medieaktiviteter og tillad at (af)montere (media:/) +Comment[de]=Überwacht Medien-Aktivität und ermöglicht das Einbinden/Lösen von Einbindungen (media:/) +Comment[el]=Έλεγχος ενεργειών των μέσων και δυνατότητα (από)προσάρτησης (media:/) +Comment[eo]=Sekvu spurojn de datumportilaj aktivoj kaj permesu (de/sur)meti (media:/) +Comment[es]=Monitoriza las actividades de los recursos y permite (des)montarlos (media:/) +Comment[et]=Hoiab silma peal andmekandjate aktiivsusel ja võimaldab neid ühendada/lahutada (media:/) +Comment[eu]=Montatzeak begiztatzen ditu (media:/) eta desmontatzen uzten du +Comment[fa]=حفظ رد فعالیتهای رسانه و اجازۀ سوار(پیاده) کردن (media:/) +Comment[fi]=Pidä kirjaa tallennustapahtumista ja salli tallennusvälineen liittäminen/irrotus (devices:/) +Comment[fr]=Gardez une trace des montages et permettre le (dé)montage (media:/) +Comment[fy]=Hâld de media-aktiviteiten by en stien ta om media oan- en ôf te keppelje (media:/) +Comment[gl]=Seguemento das actividades dos meios e permite-lle (des)montar (media:/) +Comment[he]=מנטר אחר פעילויות מדיה, ומאפשר לחבר או לנתק התקנים (media:/) +Comment[hi]=मीडिया क्रियाओं की जानकारी रखे तथा (अन)माउन्ट करने दे (मीडिया:/) +Comment[hr]=Praćenje aktivnosti medija i omogućavanje pristupanja i napuštanja +Comment[hu]=Az adathordozók követése, csatlakoztatása és leválasztása (media:/) +Comment[is]=Fylgjast með breytingum á tækjum og bjóða upp á (af)tengingu (media:/) +Comment[it]=Tiene traccia delle attività dei supporti e permette di montarli o smontarli (device:/) +Comment[ja]=メディアの活動を追跡し、(media:/) をマウントしたりアンマウントします +Comment[ka]=თვალყურს ადევნებს მედიის აქტიურობას და საშუალებას აძლევს (დე)მონტირების გაკეთებას (media:/) +Comment[kk]=Тасушылардың белсендігін байқап тіркеуге не тіркеуден шығаруға мүмкіндік береді: (un)mount (media:/) +Comment[km]=តាមដានសកម្មភាពព័ត៌មាន និងអនុញ្ញាតឲ្យរៀបចំ (មិនរៀបចំ) (media:/) +Comment[lt]=Stebi laikmenose vykdomus veiksmus ir leidžia (iš)montuoti (media:/) +Comment[lv]=Seko datu nesēju aktivitātēm un ļauj montēt/nomontēt tos (media:/) +Comment[mk]=Води сметка за активностите на носачите и дозволува (од)монтирање (media:/) +Comment[ms]=Ikuti perkembangan aktiviti media dan benarkan untuk (nyah)lekap (media:/) +Comment[mt]=Żomm kont ta' attivitajiet ta' mmuntar u ippermetti (un)mount (media:/) +Comment[nb]=Holder styr på monteringsaktiviteter og lar deg (av)montere (media:/) +Comment[nds]=Blifft bi all Medienaktiviteten op'n Stand un verlöövt dat In- un Afhangen (media:/) +Comment[ne]=मिडिया क्रियाकलापको मार्ग राख्नुहोस् र (अन)माउन्ट (media:/) गर्न अनुमति दिनुहोस् +Comment[nl]=Houdt de media-activiteiten bij en staat u toe om media aan- en af te koppelen (media:/) +Comment[nn]=Held styr på medieaktivitetar og lèt deg montera og avmontera (media:/) +Comment[pa]=ਮਾਊਟ ਕਾਰਵਾਈਆਂ ਦੀ ਜਾਣਕਾਰੀ ਰੱਖੋ ਅਤੇ ਅਨ-ਮਾਊਟ ਕਰਨ ਦਿਓ (ਜੰਤਰ:/) +Comment[pl]=Śledzi zdarzenia związane z nośnikami danych i pozwala je (od)montować (media:/) +Comment[pt]=Manter o registo das actividades de dispositivos e permitir a (des)montagem (media:/) +Comment[pt_BR]=Monitora as atividades de mídias e permite a (des)montagem (media:/) +Comment[ro]=Urmărește activitățile multimedia și permite (de)montarea media:/ +Comment[ru]=Автоматическое подключение устройств (media:/) +Comment[rw]=Kugumana inzira y'ibikorwa by'ibitangazamakuru no kwemerera gushyiramo(gukuramo) (ibitangazamakuru:/) +Comment[se]=Gozit mii dáhpáhuvvá median ja diktá du gálgat ja čadnat daid (media:/) +Comment[sk]=Sledovanie pripojenia/odpojenia medií (media:/) +Comment[sl]=Nadzoruj dejanja medija in dovilo priklop/odklop (media:/) +Comment[sr]=Прати активност медијума и омогућава (де)монтирање (media:/) +Comment[sr@Latn]=Prati aktivnost medijuma i omogućava (de)montiranje (media:/) +Comment[sv]=Håll reda på mediaaktiviteter och tillåt (av)montering (media:/) +Comment[ta]=இடைக்காலத்திற்குரிய நடவடிக்கைகளின் வைத்திரு. (சாதனம்:/)த்தை ஏற்ற(இறக்க)கவும் அனுமதி +Comment[th]=จะคอยติดตามกิจกรรมของสื่อบันทึก และอนุญาตให้เมานท์หรือยกเลิกการเมานท์ (media:/) +Comment[tr]=Ortam işlemlerini takip et ve bağlanma işlemlerine izin ver(media:/) +Comment[tt]=Cıhazlarnıñ totaşuın/ayırıluın sizüçe närsä (media:/) +Comment[uk]=Спостерігає за змінами серед носіїв інформації та дозволяє (роз)монтування (media:/) +Comment[vi]=Theo dõi các hoạt động của ổ lưu trữ và cho phép lắp đặt hay gỡ bỏ chúng ở thư mục "media:/" +Comment[wa]=Wåde li trace des activités media eyet permete di (dis)monter (media:/) +Comment[zh_CN]=跟踪介质活动并允许挂载或卸载(media:/) +Comment[zh_TW]=持續追蹤媒體活動並允許(解除)掛載 (media:/) +ServiceTypes=KDEDModule +X-TDE-ModuleType=Library +X-TDE-Library=mediamanager +X-TDE-FactoryName=mediamanager +X-TDE-Kded-autoload=true +X-TDE-Kded-load-on-demand=true +X-TDE-Kded-phase=1 diff --git a/tdeioslave/media/mediamanager/mediamanager.h b/tdeioslave/media/mediamanager/mediamanager.h new file mode 100644 index 000000000..256d11bf8 --- /dev/null +++ b/tdeioslave/media/mediamanager/mediamanager.h @@ -0,0 +1,94 @@ +/* This file is part of the KDE Project + Copyright (c) 2004 K�vin Ottens <ervin ipsquad net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _MEDIAMANAGER_H_ +#define _MEDIAMANAGER_H_ + +#include <kdedmodule.h> +#include <tqstring.h> +#include <tqstringlist.h> + +#include "medialist.h" +#include "backendbase.h" +#include "removablebackend.h" +#include "mediadirnotify.h" + +class HALBackend; +class TDEBackend; +class FstabBackend; + +class MediaManager : public KDEDModule +{ +Q_OBJECT +K_DCOP +public: + MediaManager(const TQCString &obj); + ~MediaManager(); + +k_dcop: + TQStringList fullList(); + TQStringList properties(const TQString &name); + TQStringList mountoptions(const TQString &name); + bool setMountoptions(const TQString &name, const TQStringList &options); + + TQString mount(const TQString &uid); + TQString unmount(const TQString &uid); + TQString decrypt(const TQString &uid, const TQString &password); + TQString undecrypt(const TQString &uid); + + TQString nameForLabel(const TQString &label); + ASYNC setUserLabel(const TQString &name, const TQString &label); + + ASYNC reloadBackends(); + + // Removable media handling (for people not having HAL) + bool removablePlug(const TQString &devNode, const TQString &label); + bool removableUnplug(const TQString &devNode); + bool removableCamera(const TQString &devNode); + +k_dcop_signals: + void mediumAdded(const TQString &name, bool allowNotification); + void mediumRemoved(const TQString &name, bool allowNotification); + void mediumChanged(const TQString &name, bool allowNotification); + + // For compatibility purpose, not needed for KDE4 + void mediumAdded(const TQString &name); + void mediumRemoved(const TQString &name); + void mediumChanged(const TQString &name); + +private slots: + void loadBackends(); + + void slotMediumAdded(const TQString &id, const TQString &name, + bool allowNotification); + void slotMediumRemoved(const TQString &id, const TQString &name, + bool allowNotification); + void slotMediumChanged(const TQString &id, const TQString &name, + bool mounted, bool allowNotification); + +private: + MediaList m_mediaList; + TQValueList<BackendBase*> m_backends; + RemovableBackend *mp_removableBackend; + HALBackend *m_halbackend; + TDEBackend *m_tdebackend; + MediaDirNotify m_dirNotify; + FstabBackend *m_fstabbackend; +}; + +#endif diff --git a/tdeioslave/media/mediamanager/removablebackend.cpp b/tdeioslave/media/mediamanager/removablebackend.cpp new file mode 100644 index 000000000..6b83ae1ea --- /dev/null +++ b/tdeioslave/media/mediamanager/removablebackend.cpp @@ -0,0 +1,180 @@ +/* This file is part of the KDE Project + Copyright (c) 2004 Kévin Ottens <ervin ipsquad net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "removablebackend.h" + +#include <klocale.h> +#include <kdirwatch.h> +#include <kurl.h> +#include <kmountpoint.h> +#include <kstandarddirs.h> + +#ifdef _OS_SOLARIS_ +#define MTAB "/etc/mnttab" +#else +#define MTAB "/etc/mtab" +#endif + + + +RemovableBackend::RemovableBackend(MediaList &list) + : TQObject(), BackendBase(list) +{ + KDirWatch::self()->addFile(MTAB); + + connect( KDirWatch::self(), TQT_SIGNAL( dirty(const TQString&) ), + this, TQT_SLOT( slotDirty(const TQString&) ) ); + KDirWatch::self()->startScan(); +} + +RemovableBackend::~RemovableBackend() +{ + TQStringList::iterator it = m_removableIds.begin(); + TQStringList::iterator end = m_removableIds.end(); + + for (; it!=end; ++it) + { + m_mediaList.removeMedium(*it, false); + } + + KDirWatch::self()->removeFile(MTAB); +} + +bool RemovableBackend::plug(const TQString &devNode, const TQString &label) +{ + TQString name = generateName(devNode); + TQString id = generateId(devNode); + + if (!m_removableIds.contains(id)) + { + Medium *medium = new Medium(id, id, name); + medium->mountableState(devNode, TQString::null, + TQString::null, false); + + TQStringList words = TQStringList::split(" ", label); + + TQStringList::iterator it = words.begin(); + TQStringList::iterator end = words.end(); + + TQString tmp = (*it).lower(); + tmp[0] = tmp[0].upper(); + TQString new_label = tmp; + + ++it; + for (; it!=end; ++it) + { + tmp = (*it).lower(); + tmp[0] = tmp[0].upper(); + new_label+= " "+tmp; + } + + medium->setLabel(new_label); + medium->setMimeType("media/removable_unmounted"); + + m_removableIds.append(id); + return !m_mediaList.addMedium(medium).isNull(); + } + return false; +} + +bool RemovableBackend::unplug(const TQString &devNode) +{ + TQString id = generateId(devNode); + if (m_removableIds.contains(id)) + { + m_removableIds.remove(id); + return m_mediaList.removeMedium(id); + } + return false; +} + +bool RemovableBackend::camera(const TQString &devNode) +{ + TQString id = generateId(devNode); + if (m_removableIds.contains(id)) + { + return m_mediaList.changeMediumState(id, + TQString("camera:/"), false, "media/gphoto2camera"); + } + return false; +} + +void RemovableBackend::slotDirty(const TQString &path) +{ + if (path==MTAB) + { + handleMtabChange(); + } +} + + +void RemovableBackend::handleMtabChange() +{ + TQStringList new_mtabIds; + KMountPoint::List mtab = KMountPoint::currentMountPoints(); + + KMountPoint::List::iterator it = mtab.begin(); + KMountPoint::List::iterator end = mtab.end(); + + for (; it!=end; ++it) + { + TQString dev = (*it)->mountedFrom(); + TQString mp = (*it)->mountPoint(); + TQString fs = (*it)->mountType(); + + TQString id = generateId(dev); + new_mtabIds+=id; + + if ( !m_mtabIds.contains(id) + && m_removableIds.contains(id) ) + { + m_mediaList.changeMediumState(id, dev, mp, fs, true, + false, "media/removable_mounted"); + } + } + + TQStringList::iterator it2 = m_mtabIds.begin(); + TQStringList::iterator end2 = m_mtabIds.end(); + + for (; it2!=end2; ++it2) + { + if ( !new_mtabIds.contains(*it2) + && m_removableIds.contains(*it2) ) + { + m_mediaList.changeMediumState(*it2, false, + false, "media/removable_unmounted"); + } + } + + m_mtabIds = new_mtabIds; +} + +TQString RemovableBackend::generateId(const TQString &devNode) +{ + TQString dev = KStandardDirs::realFilePath(devNode); + + return "/org/kde/mediamanager/removable/" + +dev.replace("/", ""); +} + +TQString RemovableBackend::generateName(const TQString &devNode) +{ + return KURL(devNode).fileName(); +} + +#include "removablebackend.moc" diff --git a/tdeioslave/media/mediamanager/removablebackend.h b/tdeioslave/media/mediamanager/removablebackend.h new file mode 100644 index 000000000..fa16a391b --- /dev/null +++ b/tdeioslave/media/mediamanager/removablebackend.h @@ -0,0 +1,52 @@ +/* This file is part of the KDE Project + Copyright (c) 2004 Kvin Ottens <ervin ipsquad net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _REMOVABLEBACKEND_H_ +#define _REMOVABLEBACKEND_H_ + +#include "backendbase.h" + +#include <tqobject.h> +#include <tqstringlist.h> + +class RemovableBackend : public TQObject, public BackendBase +{ +Q_OBJECT + +public: + RemovableBackend(MediaList &list); + virtual ~RemovableBackend(); + + bool plug(const TQString &devNode, const TQString &label); + bool unplug(const TQString &devNode); + bool camera(const TQString &devNode); + +private slots: + void slotDirty(const TQString &path); + +private: + void handleMtabChange(); + + static TQString generateId(const TQString &devNode); + static TQString generateName(const TQString &devNode); + + TQStringList m_removableIds; + TQStringList m_mtabIds; +}; + +#endif diff --git a/tdeioslave/media/mediamanager/tdehardwarebackend.cpp b/tdeioslave/media/mediamanager/tdehardwarebackend.cpp new file mode 100644 index 000000000..32ed746cf --- /dev/null +++ b/tdeioslave/media/mediamanager/tdehardwarebackend.cpp @@ -0,0 +1,1376 @@ +/* This file is part of the TDE Project + Copyright (c) 2012 Timothy Pearson <kb9vqf@pearsoncomputing.net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "tdehardwarebackend.h" + +#include <tqfile.h> +#include <tqfileinfo.h> +#include <tqeventloop.h> +#include <tqstylesheet.h> + +#include <kglobal.h> +#include <klocale.h> +#include <tdeconfig.h> +#include <tdeio/job.h> +#include <kprocess.h> +#include <kmimetype.h> +#include <kmountpoint.h> +#include <kmessagebox.h> +#include <kapplication.h> +#include <kprotocolinfo.h> + +#include "dialog.h" + +#define MOUNT_SUFFIX ( \ + (medium->isMounted() ? TQString("_mounted") : TQString("_unmounted")) + \ + (medium->isEncrypted() ? (sdevice->isDiskOfType(TDEDiskDeviceType::UnlockedCrypt) ? "_decrypted" : "_encrypted") : "" ) \ + ) +#define MOUNT_ICON_SUFFIX ( \ + (medium->isMounted() ? TQString("_mount") : TQString("_unmount")) + \ + (medium->isEncrypted() ? (sdevice->isDiskOfType(TDEDiskDeviceType::UnlockedCrypt) ? "_decrypt" : "_encrypt") : "" ) \ + ) + +#define CHECK_FOR_AND_EXECUTE_AUTOMOUNT(udi, medium, allowNotification) { \ + TQMap<TQString,TQString> options = MediaManagerUtils::splitOptions(mountoptions(udi)); \ + kdDebug(1219) << "automount " << options["automount"] << endl; \ + if (options["automount"] == "true" && allowNotification ) { \ + TQString error = mount(medium); \ + if (!error.isEmpty()) \ + kdDebug(1219) << "error " << error << endl; \ + } \ + } + +/* Constructor */ +TDEBackend::TDEBackend(MediaList &list, TQObject* parent) + : TQObject() + , BackendBase(list) + , m_decryptDialog(0) + , m_parent(parent) +{ + // Initialize the TDE device manager + TDEHardwareDevices *hwdevices = TDEGlobal::hardwareDevices(); + + // Connect device monitoring signals/slots + connect(hwdevices, TQT_SIGNAL(hardwareAdded(TDEGenericDevice*)), this, TQT_SLOT(AddDeviceHandler(TDEGenericDevice*))); + connect(hwdevices, TQT_SIGNAL(hardwareRemoved(TDEGenericDevice*)), this, TQT_SLOT(RemoveDeviceHandler(TDEGenericDevice*))); + connect(hwdevices, TQT_SIGNAL(hardwareUpdated(TDEGenericDevice*)), this, TQT_SLOT(ModifyDeviceHandler(TDEGenericDevice*))); + + // List devices at startup + ListDevices(); +} + +/* Destructor */ +TDEBackend::~TDEBackend() +{ + // Remove all media from the media list + TDEHardwareDevices *hwdevices = TDEGlobal::hardwareDevices(); + TDEGenericHardwareList hwlist = hwdevices->listAllPhysicalDevices(); + TDEGenericDevice *hwdevice; + for ( hwdevice = hwlist.first(); hwdevice; hwdevice = hwlist.next() ) { + if (hwdevice->type() == TDEGenericDeviceType::Disk) { + TDEStorageDevice* sdevice = static_cast<TDEStorageDevice*>(hwdevice); + RemoveDevice(sdevice); + } + } +} + +void TDEBackend::AddDeviceHandler(TDEGenericDevice *device) { + if (device->type() == TDEGenericDeviceType::Disk) { + TDEStorageDevice* sdevice = static_cast<TDEStorageDevice*>(device); + AddDevice(sdevice); + } +} + +void TDEBackend::RemoveDeviceHandler(TDEGenericDevice *device) { + if (device->type() == TDEGenericDeviceType::Disk) { + TDEStorageDevice* sdevice = static_cast<TDEStorageDevice*>(device); + RemoveDevice(sdevice); + } +} + +void TDEBackend::ModifyDeviceHandler(TDEGenericDevice *device) { + if (device->type() == TDEGenericDeviceType::Disk) { + TDEStorageDevice* sdevice = static_cast<TDEStorageDevice*>(device); + ModifyDevice(sdevice); + } +} + +// List devices (at startup) +bool TDEBackend::ListDevices() +{ + TDEHardwareDevices *hwdevices = TDEGlobal::hardwareDevices(); + TDEGenericHardwareList hwlist = hwdevices->listAllPhysicalDevices(); + TDEGenericDevice *hwdevice; + for ( hwdevice = hwlist.first(); hwdevice; hwdevice = hwlist.next() ) { + if (hwdevice->type() == TDEGenericDeviceType::Disk) { + TDEStorageDevice* sdevice = static_cast<TDEStorageDevice*>(hwdevice); + AddDevice(sdevice, false); + } + } + + return true; +} + +// Create a media instance for a new storage device +void TDEBackend::AddDevice(TDEStorageDevice * sdevice, bool allowNotification) +{ + kdDebug(1219) << "TDEBackend::AddDevice for " << sdevice->uniqueID() << endl; + + // If the device is already listed, do not process it + // This should not happen, but who knows... + /** @todo : refresh properties instead ? */ + if (m_mediaList.findById(sdevice->uniqueID())) { + kdDebug(1219) << "TDEBackend::AddDevice for " << sdevice->uniqueID() << " found existing entry in media list" << endl; + return; + } + + // Add volume block devices + if (sdevice->isDiskOfType(TDEDiskDeviceType::HDD)) { + /* We only list volumes that... + * - are encrypted with LUKS or + * - have a filesystem or + * - have an audio track + */ + if (!(sdevice->isDiskOfType(TDEDiskDeviceType::LUKS)) + && !(sdevice->checkDiskStatus(TDEDiskDeviceStatus::ContainsFilesystem)) + && !(sdevice->isDiskOfType(TDEDiskDeviceType::CDAudio)) + && !(sdevice->checkDiskStatus(TDEDiskDeviceStatus::Blank)) + ) { + // + } + else { + // Create medium + Medium* medium = new Medium(sdevice->uniqueID(), driveUDIFromDeviceUID(sdevice->uniqueID()), ""); + setVolumeProperties(medium); + + // Do not list the LUKS backend device if it has been unlocked elsewhere + if (sdevice->isDiskOfType(TDEDiskDeviceType::LUKS)) { + if (sdevice->holdingDevices().count() > 0) { + medium->setHidden(true); + } + else { + medium->setHidden(false); + } + } + + // Insert medium into list + m_mediaList.addMedium(medium, allowNotification); + + kdDebug(1219) << "TDEBackend::AddDevice inserted hard medium for " << sdevice->uniqueID() << endl; + + // Automount if enabled + CHECK_FOR_AND_EXECUTE_AUTOMOUNT(sdevice->uniqueID(), medium, allowNotification) + } + } + + // Add CD drives + if ((sdevice->isDiskOfType(TDEDiskDeviceType::CDROM)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::CDRW)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::DVDROM)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::DVDRAM)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::DVDRW)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::BDROM)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::BDRW)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::CDAudio)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::CDVideo)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::DVDVideo)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::BDVideo)) + ) { + + // Create medium + Medium* medium = new Medium(sdevice->uniqueID(), driveUDIFromDeviceUID(sdevice->uniqueID()), ""); + setVolumeProperties(medium); + + // Insert medium into list + m_mediaList.addMedium(medium, allowNotification); + + kdDebug(1219) << "TDEBackend::AddDevice inserted optical medium for " << sdevice->uniqueID() << endl; + + // Automount if enabled + CHECK_FOR_AND_EXECUTE_AUTOMOUNT(sdevice->uniqueID(), medium, allowNotification) + } + + // Floppy & zip drives + if ((sdevice->isDiskOfType(TDEDiskDeviceType::Floppy)) || + (sdevice->isDiskOfType(TDEDiskDeviceType::Zip)) || + (sdevice->isDiskOfType(TDEDiskDeviceType::Jaz)) + ) { + if ((sdevice->checkDiskStatus(TDEDiskDeviceStatus::Removable)) && (!(sdevice->checkDiskStatus(TDEDiskDeviceStatus::Inserted)))) { + allowNotification = false; + } + + /* We only list volumes that... + * - are encrypted with LUKS or + * - have a filesystem or + * - are a floppy disk + */ + if (!(sdevice->isDiskOfType(TDEDiskDeviceType::LUKS)) + && !(sdevice->checkDiskStatus(TDEDiskDeviceStatus::ContainsFilesystem)) + && !(sdevice->isDiskOfType(TDEDiskDeviceType::Floppy)) + && !(sdevice->checkDiskStatus(TDEDiskDeviceStatus::Blank)) + ) { + // + } + else { + // Create medium + Medium* medium = new Medium(sdevice->uniqueID(), driveUDIFromDeviceUID(sdevice->uniqueID()), ""); + + setFloppyProperties(medium); + + // Do not list the LUKS backend device if it has been unlocked elsewhere + if (sdevice->isDiskOfType(TDEDiskDeviceType::LUKS)) { + if (sdevice->holdingDevices().count() > 0) { + medium->setHidden(true); + } + else { + medium->setHidden(false); + } + } + + m_mediaList.addMedium(medium, allowNotification); + + kdDebug(1219) << "TDEBackend::AddDevice inserted floppy medium for " << sdevice->uniqueID() << endl; + + return; + } + } + + // PTP camera + if (sdevice->isDiskOfType(TDEDiskDeviceType::Camera)) { + // PTP cameras are handled by the "camera" tdeioslave + if (KProtocolInfo::isKnownProtocol( TQString("camera") ) ) + { + // Create medium + Medium* medium = new Medium(sdevice->uniqueID(), driveUDIFromDeviceUID(sdevice->uniqueID()), ""); + setCameraProperties(medium); + m_mediaList.addMedium(medium, allowNotification); + + kdDebug(1219) << "TDEBackend::AddDevice inserted camera medium for " << sdevice->uniqueID() << endl; + + return; + } + } +} + +void TDEBackend::RemoveDevice(TDEStorageDevice * sdevice) +{ + kdDebug(1219) << "TDEBackend::RemoveDevice for " << sdevice->uniqueID() << endl; + + if (!m_mediaList.findById(sdevice->uniqueID())) { + kdDebug(1219) << "TDEBackend::RemoveDevice for " << sdevice->uniqueID() << " existing entry in media list was not found" << endl; + return; + } + + m_mediaList.removeMedium(sdevice->uniqueID(), true); +} + +void TDEBackend::ModifyDevice(TDEStorageDevice * sdevice) +{ + kdDebug(1219) << "TDEBackend::ModifyDevice for " << sdevice->uniqueID() << endl; + + bool allowNotification = false; + ResetProperties(sdevice, allowNotification); +} + +void TDEBackend::ResetProperties(TDEStorageDevice * sdevice, bool allowNotification, bool overrideIgnoreList) +{ + kdDebug(1219) << "TDEBackend::ResetProperties for " << sdevice->uniqueID() << " allowNotification: " << allowNotification << " overrideIgnoreList: " << overrideIgnoreList << endl; + + if (!m_mediaList.findById(sdevice->uniqueID())) { + // This device is not currently in the device list, so add it and exit + kdDebug(1219) << "TDEBackend::ResetProperties for " << sdevice->uniqueID() << " existing entry in media list was not found" << endl; + AddDevice(sdevice); + return; + } + + // If we should ignore device change events for this device, do so + if (overrideIgnoreList == false) { + if (m_ignoreDeviceChangeEvents.contains(sdevice->uniqueID())) { + return; + } + } + + Medium* m = new Medium(sdevice->uniqueID(), driveUDIFromDeviceUID(sdevice->uniqueID()), ""); + + // Keep these conditions in sync with ::AddDevice above, OR ELSE!!! + // BEGIN + + if (!(sdevice->isDiskOfType(TDEDiskDeviceType::LUKS)) + && !(sdevice->checkDiskStatus(TDEDiskDeviceStatus::ContainsFilesystem)) + && !(sdevice->isDiskOfType(TDEDiskDeviceType::CDAudio)) + && !(sdevice->checkDiskStatus(TDEDiskDeviceStatus::Blank)) + ) { + } + else { + // Do not list the LUKS backend device if it has been unlocked elsewhere + if (sdevice->isDiskOfType(TDEDiskDeviceType::LUKS)) { + if (sdevice->holdingDevices().count() > 0) { + m->setHidden(true); + } + else { + m->setHidden(false); + } + } + setVolumeProperties(m); + } + + if ((sdevice->isDiskOfType(TDEDiskDeviceType::CDROM)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::CDRW)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::DVDROM)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::DVDRAM)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::DVDRW)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::BDROM)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::BDRW)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::CDAudio)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::CDVideo)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::DVDVideo)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::BDVideo)) + ) { + setVolumeProperties(m); + } + + // Floppy & zip drives + if ((sdevice->isDiskOfType(TDEDiskDeviceType::Floppy)) || + (sdevice->isDiskOfType(TDEDiskDeviceType::Zip)) || + (sdevice->isDiskOfType(TDEDiskDeviceType::Jaz)) + ) { + + if (!(sdevice->isDiskOfType(TDEDiskDeviceType::LUKS)) + && !(sdevice->checkDiskStatus(TDEDiskDeviceStatus::ContainsFilesystem)) + && !(sdevice->isDiskOfType(TDEDiskDeviceType::Floppy)) + && !(sdevice->checkDiskStatus(TDEDiskDeviceStatus::Blank)) + ) { + // + } + else { + // Do not list the LUKS backend device if it has been unlocked elsewhere + if (sdevice->isDiskOfType(TDEDiskDeviceType::LUKS)) { + if (sdevice->holdingDevices().count() > 0) { + m->setHidden(true); + } + else { + m->setHidden(false); + } + } + + setFloppyProperties(m); + } + } + + if (sdevice->isDiskOfType(TDEDiskDeviceType::Camera)) { + setCameraProperties(m); + } + + // END + + if ((sdevice->checkDiskStatus(TDEDiskDeviceStatus::Removable)) && (!(sdevice->checkDiskStatus(TDEDiskDeviceStatus::Inserted)))) { + kdDebug(1219) << "TDEBackend::ResetProperties for " << sdevice->uniqueID() << " device was removed from system" << endl; + RemoveDevice(sdevice); + return; + } + + m_mediaList.changeMediumState(*m, allowNotification); + + delete m; +} + +void TDEBackend::setVolumeProperties(Medium* medium) +{ + TDEHardwareDevices *hwdevices = TDEGlobal::hardwareDevices(); + + TDEStorageDevice * sdevice = hwdevices->findDiskByUID(medium->id()); + if (!sdevice) { + return; + } + + medium->setName(generateName(sdevice->deviceNode())); + if ((sdevice->isDiskOfType(TDEDiskDeviceType::LUKS)) || (sdevice->isDiskOfType(TDEDiskDeviceType::UnlockedCrypt))) { + medium->setEncrypted(true); + } + else { + medium->setEncrypted(false); + } + + // USAGE: mountableState(Device node, Mount point, Filesystem type, Mounted ?) + medium->mountableState(sdevice->deviceNode(), sdevice->mountPath(), sdevice->fileSystemName(), !sdevice->mountPath().isNull()); + + TQString diskLabel = sdevice->diskLabel(); + if (diskLabel.isNull()) { + diskLabel = i18n("%1 Removable Device").arg(sdevice->deviceFriendlySize()); + } + + TQString mimeType; + + if ((sdevice->isDiskOfType(TDEDiskDeviceType::CDROM)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::CDRW)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::DVDROM)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::DVDRAM)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::DVDRW)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::BDROM)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::BDRW)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::CDAudio)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::CDVideo)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::DVDVideo)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::BDVideo)) + ) { + // This device is a CD drive of some sort + + // Default + mimeType = "media/cdrom" + MOUNT_SUFFIX; + + if (sdevice->isDiskOfType(TDEDiskDeviceType::CDROM)) { + mimeType = "media/cdrom" + MOUNT_SUFFIX; + if (sdevice->checkDiskStatus(TDEDiskDeviceStatus::Blank)) { + mimeType = "media/blankcd"; + medium->unmountableState(""); + diskLabel = i18n("Blank CDROM"); + } + } + if (sdevice->isDiskOfType(TDEDiskDeviceType::CDRW)) { + mimeType = "media/cdwriter" + MOUNT_SUFFIX; + if (sdevice->checkDiskStatus(TDEDiskDeviceStatus::Blank)) { + mimeType = "media/blankcd"; + medium->unmountableState(""); + diskLabel = i18n("Blank CDRW"); + } + } + + if (sdevice->isDiskOfType(TDEDiskDeviceType::DVDROM)) { + mimeType = "media/dvd" + MOUNT_SUFFIX; + if (sdevice->checkDiskStatus(TDEDiskDeviceStatus::Blank)) { + mimeType = "media/blankdvd"; + medium->unmountableState(""); + diskLabel = i18n("Blank DVDROM"); + } + } + + if (sdevice->isDiskOfType(TDEDiskDeviceType::DVDRAM)) { + mimeType = "media/dvd" + MOUNT_SUFFIX; + if (sdevice->checkDiskStatus(TDEDiskDeviceStatus::Blank)) { + mimeType = "media/blankdvd"; + medium->unmountableState(""); + diskLabel = i18n("Blank DVDRAM"); + } + } + + if (sdevice->isDiskOfType(TDEDiskDeviceType::DVDRW)) { + mimeType = "media/dvd" + MOUNT_SUFFIX; + if (sdevice->checkDiskStatus(TDEDiskDeviceStatus::Blank)) { + mimeType = "media/blankdvd" + MOUNT_SUFFIX; + medium->unmountableState(""); + diskLabel = i18n("Blank DVDRW"); + } + } + + if (sdevice->isDiskOfType(TDEDiskDeviceType::BDROM)) { + mimeType = "media/bluray" + MOUNT_SUFFIX; + if (sdevice->checkDiskStatus(TDEDiskDeviceStatus::Blank)) { + mimeType = "media/blankbd" + MOUNT_SUFFIX; + medium->unmountableState(""); + diskLabel = i18n("Blank BDROM"); + } + } + + if (sdevice->isDiskOfType(TDEDiskDeviceType::BDRW)) { + mimeType = "media/bluray" + MOUNT_SUFFIX; + if (sdevice->checkDiskStatus(TDEDiskDeviceStatus::Blank)) { + mimeType = "media/blankbd" + MOUNT_SUFFIX; + medium->unmountableState(""); + diskLabel = i18n("Blank BDRW"); + } + } + + if (sdevice->isDiskOfType(TDEDiskDeviceType::CDAudio)) { + mimeType = "media/audiocd"; + medium->unmountableState("audiocd:/?device=" + sdevice->deviceNode()); + diskLabel = i18n("Audio CD"); + } + + if (sdevice->isDiskOfType(TDEDiskDeviceType::CDVideo)) { + mimeType = "media/vcd"; + } + if (sdevice->isDiskOfType(TDEDiskDeviceType::DVDVideo)) { + mimeType = "media/dvdvideo"; + } + if (sdevice->isDiskOfType(TDEDiskDeviceType::BDVideo)) { + mimeType = "media/bdvideo"; + } + + medium->setIconName(TQString::null); + } + else { + // This device is a hard or flash disk of some kind + + // Default + mimeType = "media/hdd" + MOUNT_SUFFIX; + + if (sdevice->isDiskOfType(TDEDiskDeviceType::USB)) { + mimeType = "media/removable" + MOUNT_SUFFIX; + medium->needMounting(); + + if (sdevice->isDiskOfType(TDEDiskDeviceType::CompactFlash)) { + medium->setIconName("compact_flash" + MOUNT_ICON_SUFFIX); + } + if (sdevice->isDiskOfType(TDEDiskDeviceType::MemoryStick)) { + medium->setIconName("memory_stick" + MOUNT_ICON_SUFFIX); + } + if (sdevice->isDiskOfType(TDEDiskDeviceType::SmartMedia)) { + medium->setIconName("smart_media" + MOUNT_ICON_SUFFIX); + } + if (sdevice->isDiskOfType(TDEDiskDeviceType::SDMMC)) { + medium->setIconName("sd_mmc" + MOUNT_ICON_SUFFIX); + } + if (sdevice->isDiskOfType(TDEDiskDeviceType::MediaDevice)) { + medium->setIconName("ipod" + MOUNT_ICON_SUFFIX); + + if (sdevice->vendorModel().upper().contains("IPOD") && KProtocolInfo::isKnownProtocol( TQString("ipod") ) ) + { + medium->unmountableState( "ipod:/" ); + medium->mountableState(!sdevice->mountPath().isNull()); + } + } + if (sdevice->isDiskOfType(TDEDiskDeviceType::Tape)) { + medium->setIconName("magnetic_tape" + MOUNT_ICON_SUFFIX); + } + if (medium->isMounted() && TQFile::exists(medium->mountPoint() + "/dcim")) + { + mimeType = "media/camera" + MOUNT_SUFFIX; + } + } + } + + if (!medium->needMounting()) { + if (sdevice->isDiskOfType(TDEDiskDeviceType::LUKS)) { + if (sdevice->checkDiskStatus(TDEDiskDeviceStatus::UsedByDevice)) { + // Encrypted base devices must be set to this mimetype or they won't open when the base device node is passed to the tdeioslave + mimeType = "media/removable_mounted"; + } + } + } + + medium->setLabel(diskLabel); + medium->setMimeType(mimeType); +} + +// Handle floppies and zip drives +bool TDEBackend::setFloppyProperties(Medium* medium) +{ + TDEHardwareDevices *hwdevices = TDEGlobal::hardwareDevices(); + + TDEStorageDevice * sdevice = hwdevices->findDiskByUID(medium->id()); + if (!sdevice) { + return false; + } + + medium->setName(generateName(sdevice->deviceNode())); + medium->setLabel(i18n("Unknown Drive")); + + // Certain disks have a lot in common with hard drives + // FIXME + // Any more? + if ((sdevice->isDiskOfType(TDEDiskDeviceType::Zip)) || (sdevice->isDiskOfType(TDEDiskDeviceType::Jaz))) { + medium->setName(generateName(sdevice->deviceNode())); + if ((sdevice->isDiskOfType(TDEDiskDeviceType::LUKS)) || (sdevice->isDiskOfType(TDEDiskDeviceType::UnlockedCrypt))) { + medium->setEncrypted(true); + } + else { + medium->setEncrypted(false); + } + + // USAGE: mountableState(Device node, Mount point, Filesystem type, Mounted ?) + medium->mountableState(sdevice->deviceNode(), sdevice->mountPath(), sdevice->fileSystemName(), !sdevice->mountPath().isNull()); + } + + if (sdevice->isDiskOfType(TDEDiskDeviceType::Floppy)) { + setFloppyMountState(medium); + + // We don't use the routine above as floppy disks are extremely slow (we don't want them accessed at all during media listing) + medium->mountableState(sdevice->deviceNode(), sdevice->mountPath(), sdevice->fileSystemName(), !sdevice->mountPath().isNull()); + + if (sdevice->mountPath().isNull()) { + medium->setMimeType("media/floppy_unmounted"); + } + else { + medium->setMimeType("media/floppy_mounted" ); + } + medium->setLabel(i18n("Floppy Drive")); + } + + if (sdevice->isDiskOfType(TDEDiskDeviceType::Zip)) { + if (sdevice->mountPath().isNull()) { + medium->setMimeType("media/zip_unmounted"); + } + else { + medium->setMimeType("media/zip_mounted" ); + } + + // Set label + TQString diskLabel = sdevice->diskLabel(); + if (diskLabel.isNull()) { + diskLabel = i18n("%1 Zip Disk").arg(sdevice->deviceFriendlySize()); + } + medium->setLabel(diskLabel); + } + + /** @todo Mimetype for JAZ drives ? */ + + medium->setIconName(TQString::null); + + return true; +} + +void TDEBackend::setCameraProperties(Medium* medium) +{ + TDEHardwareDevices *hwdevices = TDEGlobal::hardwareDevices(); + + TDEStorageDevice * sdevice = hwdevices->findDiskByUID(medium->id()); + if (!sdevice) { + return; + } + + TQString cameraName = sdevice->friendlyName(); + cameraName.replace("/", "_"); + medium->setName(cameraName); + + TQString device = "camera:/"; + + TQStringList devNodeList = TQStringList::split("/", sdevice->deviceNode(), TRUE); + TQString devNode0 = devNodeList[devNodeList.count()-2]; + TQString devNode1 = devNodeList[devNodeList.count()-1]; + + if ((devNode0 != "") && (devNode1 != "")) { + device.sprintf("camera://@[usb:%s,%s]/", devNode0.ascii(), devNode1.ascii()); + } + + medium->unmountableState(device); + medium->setMimeType("media/gphoto2camera"); + medium->setIconName(TQString::null); + + if (sdevice->friendlyName() != "") { + medium->setLabel(sdevice->friendlyName()); + } + else { + medium->setLabel(i18n("Camera")); + } +} + +void TDEBackend::setFloppyMountState( Medium *medium ) +{ + KMountPoint::List mtab = KMountPoint::currentMountPoints(); + KMountPoint::List::iterator it = mtab.begin(); + KMountPoint::List::iterator end = mtab.end(); + + TQString fstype; + TQString mountpoint; + for (; it!=end; ++it) { + if ((*it)->mountedFrom() == medium->deviceNode() ) { + fstype = (*it)->mountType().isNull() ? (*it)->mountType() : "auto"; + mountpoint = (*it)->mountPoint(); + medium->mountableState( medium->deviceNode(), mountpoint, fstype, true ); + return; + } + } +} + +TQStringList TDEBackend::mountoptions(const TQString &name) +{ + const Medium* medium = m_mediaList.findById(name); + if (!medium) { + return TQStringList(); // we know nothing about that device + } + if (!isInFstab(medium).isNull()) { + return TQStringList(); // device is listed in fstab, therefore is not handled by us + } + + if (medium->isEncrypted()) { + // if not decrypted yet then there are no mountoptions + return TQStringList(); + } + + TDEHardwareDevices *hwdevices = TDEGlobal::hardwareDevices(); + + TDEStorageDevice * sdevice = hwdevices->findDiskByUID(medium->id()); + if (!sdevice) { + return TQStringList(); // we can't get the information needed in order to process mount options + } + + TQStringList result; + + // Allow only those options which are valid for the given device + // pmount only allows a subset of options, and those are given in the map below + // FIXME + TQMap<TQString,bool> valids; + valids["ro"] = true; + //valids["quiet"] = false; + //valids["flush"] = false; + //valids["uid"] = false; + valids["utf8"] = true; + //valids["shortname"] = false; + valids["locale"] = true; + valids["sync"] = true; + valids["noatime"] = true; + //valids["data"] = false; + + TQString drive_udi = driveUDIFromDeviceUID(medium->id()); + + TDEConfig config("mediamanagerrc"); + + bool use_defaults = true; + if (config.hasGroup(drive_udi)) { + config.setGroup(drive_udi); + use_defaults = config.readBoolEntry("use_defaults", false); + } + if (use_defaults) { + config.setGroup("DefaultOptions"); + } + result << TQString("use_defaults=%1").arg(use_defaults ? "true" : "false"); + + bool removable = false; + if (!drive_udi.isNull()) { + removable = ((sdevice->checkDiskStatus(TDEDiskDeviceStatus::Removable)) || (sdevice->checkDiskStatus(TDEDiskDeviceStatus::Hotpluggable))); + } + + TQString tmp; + bool value; + if (use_defaults) { + value = config.readBoolEntry("automount", false); + } + else { + QString current_group = config.group(); + config.setGroup(drive_udi); + value = config.readBoolEntry("automount", false); + config.setGroup(current_group); + } + + if ((sdevice->checkDiskStatus(TDEDiskDeviceStatus::Blank)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::CDAudio)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::CDVideo)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::DVDVideo)) + || (sdevice->isDiskOfType(TDEDiskDeviceType::BDVideo)) + ) { + value = false; + } + + result << TQString("automount=%1").arg(value ? "true" : "false"); + + if (valids.contains("ro")) { + value = config.readBoolEntry("ro", false); + tmp = TQString("ro=%1").arg(value ? "true" : "false"); + if (sdevice->fileSystemName() != "iso9660") { + result << tmp; + } + } + + if (valids.contains("quiet")) { + value = config.readBoolEntry("quiet", false); + tmp = TQString("quiet=%1").arg(value ? "true" : "false"); + if (sdevice->fileSystemName() != "iso9660") { + result << tmp; + } + } + + if (valids.contains("flush")) { + value = config.readBoolEntry("flush", sdevice->fileSystemName().endsWith("fat")); + tmp = TQString("flush=%1").arg(value ? "true" : "false"); + result << tmp; + } + + if (valids.contains("uid")) { + value = config.readBoolEntry("uid", true); + tmp = TQString("uid=%1").arg(value ? "true" : "false"); + result << tmp; + } + + if (valids.contains("utf8")) { + value = config.readBoolEntry("utf8", true); + tmp = TQString("utf8=%1").arg(value ? "true" : "false"); + result << tmp; + } + + if (valids.contains("shortname")) { + TQString svalue = config.readEntry("shortname", "lower").lower(); + if (svalue == "winnt") { + result << "shortname=winnt"; + } + else if (svalue == "win95") { + result << "shortname=win95"; + } + else if (svalue == "mixed") { + result << "shortname=mixed"; + } + else { + result << "shortname=lower"; + } + } + + // pass our locale to the ntfs-3g driver so it can translate local characters + if (valids.contains("locale") && (sdevice->fileSystemName() == "ntfs-3g")) { + // have to obtain LC_CTYPE as returned by the `locale` command + // check in the same order as `locale` does + char *cType; + if ( (cType = getenv("LC_ALL")) || (cType = getenv("LC_CTYPE")) || (cType = getenv("LANG")) ) { + result << TQString("locale=%1").arg(cType); + } + } + + if (valids.contains("sync")) { + value = config.readBoolEntry("sync", ( valids.contains("flush") && !sdevice->fileSystemName().endsWith("fat") ) && removable); + tmp = TQString("sync=%1").arg(value ? "true" : "false"); + if (sdevice->fileSystemName() != "iso9660") { + result << tmp; + } + } + + if (valids.contains("noatime")) { + value = config.readBoolEntry("atime", !sdevice->fileSystemName().endsWith("fat")); + tmp = TQString("atime=%1").arg(value ? "true" : "false"); + if (sdevice->fileSystemName() != "iso9660") { + result << tmp; + } + } + + TQString mount_point; + mount_point = config.readEntry("mountpoint", TQString::null); + + if (!mount_point.startsWith("/")) { + mount_point = "/media/" + mount_point; + } + if (mount_point != "") { + result << TQString("mountpoint=%1").arg(mount_point); + } + + TQString file_system_name; + file_system_name = sdevice->fileSystemName(); + if (file_system_name != "") { + result << TQString("filesystem=%1").arg(file_system_name); + } + + if (valids.contains("data")) { + TQString svalue = config.readEntry("journaling").lower(); + if (svalue == "ordered") { + result << "journaling=ordered"; + } + else if (svalue == "writeback") { + result << "journaling=writeback"; + } + else if (svalue == "data") { + result << "journaling=data"; + } + else { + result << "journaling=ordered"; + } + } + + return result; +} + +bool TDEBackend::setMountoptions(const TQString &name, const TQStringList &options ) +{ + const Medium* medium = m_mediaList.findById(name); + if (!medium) { + return false; // we know nothing about that device + } + if (!isInFstab(medium).isNull()) { + return false; // device is listed in fstab, therefore is not handled by us + } + + TQString drive_udi = driveUDIFromDeviceUID(medium->id()); + + kdDebug(1219) << "setMountoptions " << name << " " << options << endl; + + TDEConfig config("mediamanagerrc"); + config.setGroup(drive_udi); + + TQMap<TQString,TQString> valids = MediaManagerUtils::splitOptions(options); + + const char *names[] = { "use_defaults", "ro", "quiet", "atime", "uid", "utf8", "flush", "sync", 0 }; + for (int index = 0; names[index]; ++index) { + if (valids.contains(names[index])) { + config.writeEntry(names[index], valids[names[index]] == "true"); + } + } + + if (valids.contains("shortname")) { + config.writeEntry("shortname", valids["shortname"]); + } + + if (valids.contains("journaling")) { + config.writeEntry("journaling", valids["journaling"]); + } + + if (!mountoptions(name).contains(TQString("mountpoint=%1").arg(valids["mountpoint"]))) { + config.writeEntry("mountpoint", valids["mountpoint"]); + } + + if (valids.contains("automount")) { + config.setGroup(drive_udi); + config.writeEntry("automount", valids["automount"]); + } + + return true; +} + +void TDEBackend::slotPasswordReady() { + m_decryptionPassword = m_decryptDialog->getPassword(); + m_decryptPasswordValid = true; +} + +void TDEBackend::slotPasswordCancel() { + m_decryptionPassword = TQString::null; + m_decryptPasswordValid = true; +} + +TQString TDEBackend::mount(const Medium *medium) +{ + if (medium->isMounted()) { + return TQString(); // that was easy + } + + TQString mountPoint = isInFstab(medium); + if (!mountPoint.isNull()) + { + struct mount_job_data data; + data.completed = false; + data.medium = medium; + + TDEIO::Job *job = TDEIO::mount( false, 0, medium->deviceNode(), mountPoint ); + connect(job, TQT_SIGNAL( result (TDEIO::Job *)), TQT_SLOT( slotResult( TDEIO::Job *))); + mount_jobs[job] = &data; + // The caller expects the device to be mounted when the function + // completes. Thus block until the job completes. + while (!data.completed) { + kapp->eventLoop()->enterLoop(); + } + // Return the error message (if any) to the caller + return (data.error) ? data.errorMessage : TQString::null; + + } + + TDEHardwareDevices *hwdevices = TDEGlobal::hardwareDevices(); + + TDEStorageDevice * sdevice = hwdevices->findDiskByUID(medium->id()); + if (!sdevice) { + return i18n("Internal error"); + } + + TQString optionString; + TQString diskLabel; + + TQMap<TQString,TQString> valids = MediaManagerUtils::splitOptions(mountoptions(medium->id())); + if (valids["ro"] == "true") { + optionString.append(" -r"); + } + + if (valids["atime"] != "true") { + optionString.append(" -A"); + } + + if (valids["utf8"] == "true") { + optionString.append(" -c utf8"); + } + + if (valids["sync"] == "true") { + optionString.append(" -s"); + } + + TQString mount_point = valids["mountpoint"]; + if (mount_point.startsWith("/media/")) { + diskLabel = mount_point.mid(7); + } + + if (valids.contains("filesystem")) { + optionString.append(TQString(" -t %1").arg(valids["filesystem"])); + } + + if (valids.contains("locale")) { + optionString.append(TQString(" -c %1").arg(valids["locale"])); + } + + if (diskLabel == "") { + // Try to use a pretty mount point if possible + TQStringList pieces = TQStringList::split("/", sdevice->deviceNode(), FALSE); + TQString node = pieces[pieces.count()-1]; + diskLabel = medium->label() + " (" + node + ")"; + diskLabel.replace("/", "_"); + } + + TQString qerror = i18n("Cannot mount encrypted drives!"); + + if (!medium->isEncrypted()) { + // normal volume + TQString mountMessages; + TQString mountedPath = sdevice->mountDevice(diskLabel, optionString, &mountMessages); + if (mountedPath.isNull()) { + qerror = i18n("<qt>Unable to mount this device.<p>Potential reasons include:<br>Improper device and/or user privilege level<br>Corrupt data on storage device"); + if (!mountMessages.isNull()) { + qerror.append(i18n("<p>Technical details:<br>").append(mountMessages)); + } + qerror.append("</qt>"); + } + else { + qerror = ""; + } + } + else { + TQString iconName = medium->iconName(); + if (iconName.isEmpty()) + { + TQString mime = medium->mimeType(); + iconName = KMimeType::mimeType(mime)->icon(mime, false); + } + + bool continue_trying_to_decrypt = true; + while (continue_trying_to_decrypt == true) { + m_decryptPasswordValid = false; + + m_decryptDialog = new Dialog(sdevice->deviceNode(), iconName); + m_decryptDialog->show(); + + connect(m_decryptDialog, TQT_SIGNAL (user1Clicked()), this, TQT_SLOT (slotPasswordReady())); + connect(m_decryptDialog, TQT_SIGNAL (cancelClicked()), this, TQT_SLOT (slotPasswordCancel())); + connect(this, TQT_SIGNAL (signalDecryptionPasswordError(TQString)), m_decryptDialog, TQT_SLOT (slotDialogError(TQString))); + + while (m_decryptPasswordValid == false) { + tqApp->processEvents(); + } + + m_decryptDialog->setEnabled(false); + tqApp->processEvents(); + + if (m_decryptionPassword.isNull()) { + delete m_decryptDialog; + return TQString("Decryption aborted"); + } + else { + // Just for some added fun, if udev emits a medium change event, which I then forward, with mounted==0, it stops the MediaProtocol::listDir method dead in its tracks, + // and therefore the media:/ tdeioslave won't refresh after the encrypted device mount + // Therefore, I need to ignore all change events on this device during the mount process and hope nothing bad happens as a result! + if (!m_ignoreDeviceChangeEvents.contains(sdevice->uniqueID())) { + m_ignoreDeviceChangeEvents.append(sdevice->uniqueID()); + } + + // mount encrypted volume with password + int mountRetcode; + TQString mountMessages; + TQString mountedPath = sdevice->mountEncryptedDevice(m_decryptionPassword, diskLabel, optionString, &mountMessages, &mountRetcode); + if (mountedPath.isNull()) { + if (mountRetcode == 0) { + // Mounting was successful + // Because the TDE hardware backend is event driven it might take a little while for the new unencrypted mapped device to show up + // Wait up to 30 seconds for it to appear... + for (int i=0;i<300;i++) { + mountedPath = sdevice->mountPath(); + if (!mountedPath.isNull()) { + break; + } + tqApp->processEvents(50); + usleep(50000); + } + } + } + if (mountedPath.isNull()) { + if (mountRetcode == 25600) { + // Probable LUKS failure + // Retry + m_decryptDialog->setEnabled(true); + continue_trying_to_decrypt = true; + } + else { + qerror = i18n("<qt>Unable to mount this device.<p>Potential reasons include:<br>Improper device and/or user privilege level<br>Corrupt data on storage device<br>Incorrect encryption password"); + if (!mountMessages.isNull()) { + qerror.append(i18n("<p>Technical details:<br>").append(mountMessages)); + } + qerror.append("</qt>"); + continue_trying_to_decrypt = false; + } + } + else { + qerror = ""; + continue_trying_to_decrypt = false; + } + + delete m_decryptDialog; + } + } + } + + if (!qerror.isEmpty()) { + return qerror; + } + + ResetProperties(sdevice, false, true); + + if (m_ignoreDeviceChangeEvents.contains(sdevice->uniqueID())) { + m_ignoreDeviceChangeEvents.remove(sdevice->uniqueID()); + } + + return TQString(); +} + +TQString TDEBackend::mount(const TQString &_udi) +{ + const Medium* medium = m_mediaList.findById(_udi); + if (!medium) + return i18n("No such medium: %1").arg(_udi); + + return mount(medium); +} + +TQString TDEBackend::unmount(const TQString &_udi) +{ + const Medium* medium = m_mediaList.findById(_udi); + + if ( !medium ) + return i18n("No such medium: %1").arg(_udi); + + if (!medium->isMounted()) + return TQString(); // that was easy + + TQString mountPoint = isInFstab(medium); + if (!mountPoint.isNull()) + { + struct mount_job_data data; + data.completed = false; + data.medium = medium; + + TDEIO::Job *job = TDEIO::unmount( medium->mountPoint(), false ); + connect(job, TQT_SIGNAL( result (TDEIO::Job *)), TQT_SLOT( slotResult( TDEIO::Job *))); + mount_jobs[job] = &data; + // The caller expects the device to be unmounted when the function + // completes. Thus block until the job completes. + while (!data.completed) { + kapp->eventLoop()->enterLoop(); + } + // Return the error message (if any) to the caller + return (data.error) ? data.errorMessage : TQString::null; + } + + TQString udi = TQString::null; + + TDEHardwareDevices *hwdevices = TDEGlobal::hardwareDevices(); + + TDEStorageDevice * sdevice = hwdevices->findDiskByUID(medium->id()); + if (!sdevice) { + return i18n("Internal error"); + } + + TQString qerror; + TQString origqerror; + + // Save these for later + TQString uid = sdevice->uniqueID(); + TQString node = sdevice->deviceNode(); + + TQString unmountMessages; + int unmountRetcode = 0; + if (!sdevice->unmountDevice(&unmountMessages, &unmountRetcode)) { + // Unmount failed! + qerror = "<qt>" + i18n("Unfortunately, the device <b>%1</b> (%2) named <b>'%3'</b> and currently mounted at <b>%4</b> could not be unmounted. ").arg("system:/media/" + medium->name(), medium->deviceNode(), medium->prettyLabel(), medium->prettyBaseURL().pathOrURL()); + if (!unmountMessages.isNull()) { + qerror.append(i18n("<p>Technical details:<br>").append(unmountMessages)); + } + qerror.append("</qt>"); + } + else { + qerror = ""; + } + + if (unmountRetcode == 1280) { + // Failed as BUSY + TQString processesUsingDev = listUsingProcesses(medium); + if (!processesUsingDev.isNull()) { + if (KMessageBox::warningYesNo(0, i18n("<qt>The device <b>%1</b> (%2) named <b>'%3'</b> and currently mounted at <b>%4</b> can not be unmounted at this time.<p>%5<p><b>Would you like to forcibly terminate these processes?</b><br><i>All unsaved data would be lost</i>").arg("system:/media/" + medium->name()).arg(medium->deviceNode()).arg(medium->prettyLabel()).arg(medium->prettyBaseURL().pathOrURL()).arg(processesUsingDev)) == KMessageBox::Yes) { + killUsingProcesses(medium); + if (!sdevice->unmountDevice(&unmountMessages, &unmountRetcode)) { + // Unmount failed! + qerror = "<qt>" + i18n("Unfortunately, the device <b>%1</b> (%2) named <b>'%3'</b> and currently mounted at <b>%4</b> could not be unmounted. ").arg("system:/media/" + medium->name(), medium->deviceNode(), medium->prettyLabel(), medium->prettyBaseURL().pathOrURL()); + if (!unmountMessages.isNull()) { + qerror.append(i18n("<p>Technical details:<br>").append(unmountMessages)); + } + qerror.append("</qt>"); + } + else { + qerror = ""; + } + } + } + } + + if (qerror != "") { + return qerror; + } + + // There is a possibility that the storage device was unceremoniously removed from the system immediately after it was unmounted + // There is no reliable way to know if this happened either! + // For now, see if the device node still exists + TQFileInfo checkDN(node); + if (!checkDN.exists()) { + m_mediaList.removeMedium(uid, true); + } + else { + TQString mountedPath = sdevice->mountPath(); + if (!mountedPath.isNull()) { + // Because the TDE hardware backend is event driven it might take a little while for the device to show up as unmounted + // Wait up to 30 seconds for the mount to disappear... + for (int i=0;i<300;i++) { + mountedPath = sdevice->mountPath(); + if (mountedPath.isNull()) { + break; + } + tqApp->processEvents(50); + usleep(50000); + } + } + + ResetProperties(sdevice, false); + } + + return TQString(); +} + +TQString TDEBackend::isInFstab(const Medium *medium) +{ + KMountPoint::List fstab = KMountPoint::possibleMountPoints(KMountPoint::NeedMountOptions|KMountPoint::NeedRealDeviceName); + + KMountPoint::List::iterator it = fstab.begin(); + KMountPoint::List::iterator end = fstab.end(); + + for (; it!=end; ++it) + { + TQString reald = (*it)->realDeviceName(); + if ( reald.endsWith( "/" ) ) { + reald = reald.left( reald.length() - 1 ); + } + if ((*it)->mountedFrom() == medium->deviceNode() || ( !medium->deviceNode().isEmpty() && reald == medium->deviceNode() ) ) + { + TQStringList opts = (*it)->mountOptions(); + if (opts.contains("user") || opts.contains("users")) { + return (*it)->mountPoint(); + } + } + } + + return TQString::null; +} + +TQString TDEBackend::listUsingProcesses(const Medium* medium) +{ + TQString proclist, fullmsg; + TQString cmdline = TQString("/usr/bin/env fuser -vm %1 2>&1").arg(TDEProcess::quote(medium->mountPoint())); + FILE *fuser = popen(cmdline.latin1(), "r"); + + uint counter = 0; + if (fuser) { + proclist += "<pre>"; + TQTextIStream is(fuser); + TQString tmp; + while (!is.atEnd()) { + tmp = is.readLine(); + tmp = TQStyleSheet::escape(tmp) + "\n"; + + proclist += tmp; + if (counter++ > 10) { + proclist += "..."; + break; + } + } + proclist += "</pre>"; + (void)pclose( fuser ); + } + if (counter) { + fullmsg = i18n("Programs still using the device " + "have been detected. They are listed below. You have to " + "close them or change their working directory before " + "attempting to unmount the device again."); + fullmsg += "<br>" + proclist; + return fullmsg; + } + else { + return TQString::null; + } +} + +TQString TDEBackend::killUsingProcesses(const Medium* medium) +{ + TQString proclist, fullmsg; + TQString cmdline = TQString("/usr/bin/env fuser -vmk %1 2>&1").arg(TDEProcess::quote(medium->mountPoint())); + FILE *fuser = popen(cmdline.latin1(), "r"); + + uint counter = 0; + if (fuser) { + proclist += "<pre>"; + TQTextIStream is(fuser); + TQString tmp; + while (!is.atEnd()) { + tmp = is.readLine(); + tmp = TQStyleSheet::escape(tmp) + "\n"; + + proclist += tmp; + if (counter++ > 10) { + proclist += "..."; + break; + } + } + proclist += "</pre>"; + (void)pclose( fuser ); + } + if (counter) { + fullmsg = i18n("Programs that were still using the device " + "have been forcibly terminated. They are listed below."); + fullmsg += "<br>" + proclist; + return fullmsg; + } + else { + return TQString::null; + } +} + +TQString TDEBackend::generateName(const TQString &devNode) +{ + return KURL(devNode).fileName(); +} + +TQString TDEBackend::driveUDIFromDeviceUID(TQString uuid) { + TDEHardwareDevices *hwdevices = TDEGlobal::hardwareDevices(); + + TDEStorageDevice * sdevice = hwdevices->findDiskByUID(uuid); + TQString ret; + if (sdevice) { + ret = sdevice->diskUUID(); + if (ret != "") { + ret = "volume_uuid_" + ret; + } + else { + ret = sdevice->deviceNode(); + if (ret != "") { + ret = "device_node_" + ret; + } + else { + ret = sdevice->uniqueID(); + } + } + } + if (ret == "") { + return TQString::null; + } + else { + return ret; + } +} + +#include "tdehardwarebackend.moc"
\ No newline at end of file diff --git a/tdeioslave/media/mediamanager/tdehardwarebackend.h b/tdeioslave/media/mediamanager/tdehardwarebackend.h new file mode 100644 index 000000000..9b5407d8f --- /dev/null +++ b/tdeioslave/media/mediamanager/tdehardwarebackend.h @@ -0,0 +1,171 @@ +/* This file is part of the TDE Project + Copyright (c) 2012 Timothy Pearson <kb9vqf@pearsoncomputing.net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/** +* This is a media:/ backend for the builtin TDE hardware library +* +* @author Timothy Pearson <kb9vqf@pearsoncomputing.net> +* @short media:/ backend for the TDE hardware library +*/ + +#ifndef _TDEBACKEND_H_ +#define _TDEBACKEND_H_ + +#include "backendbase.h" + +#include <tqobject.h> +#include <tqstringlist.h> +#include <tqstring.h> + +#include <config.h> + +#include <tdehardwaredevices.h> + +namespace TDEIO { + class Job; +} + +class Dialog; + +class TDEBackend : public TQObject, public BackendBase +{ +Q_OBJECT + +public: + /** + * Constructor + */ + TDEBackend(MediaList &list, TQObject* parent); + + /** + * Destructor + */ + ~TDEBackend(); + + /** + * List all devices and append them to the media device list (called only once, at startup). + * + * @return true if succeded, false otherwise + */ + bool ListDevices(); + + TQStringList mountoptions(const TQString &id); + + bool setMountoptions(const TQString &id, const TQStringList &options); + + TQString mount(const TQString &id); + TQString mount(const Medium *medium); + TQString unmount(const TQString &id); +// TQString decrypt(const TQString &id, const TQString &password); +// TQString undecrypt(const TQString &id); + +private: + /** + * Append a device in the media list. This function will check if the device + * is worth listing. + * + * @param sdevice A pointer to a TDEStorageDevice object + * @param allowNotification Indicates if this event will be notified to the user + */ + void AddDevice(TDEStorageDevice * sdevice, bool allowNotification=true); + + /** + * Remove a device from the device list + * + * @param sdevice A pointer to a TDEStorageDevice object + */ + void RemoveDevice(TDEStorageDevice * sdevice); + + /** + * A device has changed, update it + * + * @param sdevice A pointer to a TDEStorageDevice object + */ + void ModifyDevice(TDEStorageDevice * sdevice); + +private slots: + void AddDeviceHandler(TDEGenericDevice* device); + void RemoveDeviceHandler(TDEGenericDevice* device); + void ModifyDeviceHandler(TDEGenericDevice* device); + + void slotPasswordReady(); + void slotPasswordCancel(); + +signals: + void signalDecryptionPasswordError(TQString); + +/* Set media properties */ +private: + /** + * Reset properties for the given medium + * + * @param sdevice A pointer to a TDEStorageDevice objec + * @param allowNotification Indicates if this event will be notified to the user + * @param overrideIgnoreList If true, override event ignore requests for the current device node + */ + void ResetProperties(TDEStorageDevice * sdevice, bool allowNotification=false, bool overrideIgnoreList=false); + + /** + * Find the medium that is concerned with device udi + */ +// const char* findMediumUdiFromUdi(const char* udi); + + void setVolumeProperties(Medium* medium); + bool setFloppyProperties(Medium* medium); + void setFloppyMountState( Medium* medium ); +// bool setFstabProperties(Medium* medium); + void setCameraProperties(Medium* medium); + TQString generateName(const TQString &devNode); + static TQString isInFstab(const Medium *medium); + static TQString listUsingProcesses(const Medium *medium); + static TQString killUsingProcesses(const Medium *medium); + + TQString driveUDIFromDeviceUID(TQString uuid); + + // Decryption + Dialog* m_decryptDialog; + TQString m_decryptionPassword; + bool m_decryptPasswordValid; + +/* TDE structures */ +private: + /** + * Object for the kded module + */ + TQObject* m_parent; + + /** + * Data structure for fstab mount/unmount jobs + */ + struct mount_job_data { + // [in] Medium, which is being mounted/unmounted by the job + const Medium* medium; + // [in,out] Should be set to true when the job completes + bool completed; + // [out] TDEIO::Error if an error occured during operation. Otherwise, 0 + int error; + // [out] Error message to be displayed to the user + TQString errorMessage; + }; + + TQMap<TDEIO::Job *, struct mount_job_data*> mount_jobs; + + TQStringList m_ignoreDeviceChangeEvents; +}; + +#endif /* _TDEBACKEND_H_ */ |