diff options
Diffstat (limited to 'smb4k/core/smb4kmounter.cpp')
-rw-r--r-- | smb4k/core/smb4kmounter.cpp | 1718 |
1 files changed, 1718 insertions, 0 deletions
diff --git a/smb4k/core/smb4kmounter.cpp b/smb4k/core/smb4kmounter.cpp new file mode 100644 index 0000000..45ccc9c --- /dev/null +++ b/smb4k/core/smb4kmounter.cpp @@ -0,0 +1,1718 @@ +/*************************************************************************** + smb4kmounter.cpp - The core class that mounts the shares. + ------------------- + begin : Die Jun 10 2003 + copyright : (C) 2003-2007 by Alexander Reinholdt + email : dustpuppy@users.berlios.de + ***************************************************************************/ + +/*************************************************************************** + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301 USA * + ***************************************************************************/ + +// Qt includes +#include <qapplication.h> +#include <qdir.h> +#include <qtextstream.h> +#include <qfile.h> +#include <qtextstream.h> + +// KDE includes +#include <kapplication.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kdebug.h> + +// system includes +#if !defined(__FreeBSD__) && !defined(__solaris__) && !defined(USE_SOLARIS) +#include <sys/statfs.h> +#elif defined(__solaris__) || defined(USE_SOLARIS) +#include <sys/statvfs.h> +#elif defined(__FreeBSD__) +#include <sys/param.h> +#include <sys/mount.h> +#endif +#include <sys/types.h> +#include <pwd.h> +#include <unistd.h> +#include <errno.h> +#include <dirent.h> + +#ifdef __FreeBSD__ +#include <sys/param.h> +#include <sys/ucred.h> +#include <sys/mount.h> +#include <qfileinfo.h> +#endif + +// Application specific includes +#include "smb4kmounter.h" +#include "smb4kmounter_p.h" +#include "smb4kauthinfo.h" +#include "smb4ksambaoptionsinfo.h" +#include "smb4kerror.h" +#include "smb4kglobal.h" +#include "smb4ksambaoptionshandler.h" +#include "smb4kpasswordhandler.h" +#include "smb4kshare.h" +#include "smb4ksettings.h" + +using namespace Smb4KGlobal; + + +Smb4KMounter::Smb4KMounter( QObject *parent, const char *name ) : QObject( parent, name ) +{ + m_priv = new Smb4KMounterPrivate; + + m_proc = new KProcess( this, "MounterProcess" ); + m_proc->setUseShell( true ); + + m_working = false; + + m_queue.setAutoDelete( true ); + + connect( m_proc, SIGNAL( processExited( KProcess * ) ), + this, SLOT( slotProcessExited( KProcess * ) ) ); + + connect( m_proc, SIGNAL( receivedStdout( KProcess *, char *, int ) ), + this, SLOT( slotReceivedStdout( KProcess *, char *, int ) ) ); + + connect( m_proc, SIGNAL( receivedStderr( KProcess *, char *, int ) ), + this, SLOT( slotReceivedStderr( KProcess *, char *, int ) ) ); + + connect( kapp, SIGNAL( shutDown() ), + this, SLOT( slotShutdown() ) ); +} + + +Smb4KMounter::~Smb4KMounter() +{ + abort(); + + for ( QValueList<Smb4KShare *>::Iterator it = m_mounted_shares.begin(); it != m_mounted_shares.end(); ++it ) + { + delete *it; + } + + m_mounted_shares.clear(); + + delete m_priv; +} + + +void Smb4KMounter::init() +{ + m_queue.enqueue( new QString( QString( "%1:" ).arg( Import ) ) ); + m_queue.enqueue( new QString( QString( "%1:" ).arg( Remount ) ) ); + + startTimer( TIMER_INTERVAL ); +} + + +/*************************************************************************** + Aborts any action of the mounter. +***************************************************************************/ + + +void Smb4KMounter::abort() +{ + m_queue.clear(); + + if ( m_proc->isRunning() ) + { + if ( Smb4KSettings::alwaysUseSuperUser() ) + { + QString suid_program; + + switch( Smb4KSettings::superUserProgram() ) + { + case Smb4KSettings::EnumSuperUserProgram::Sudo: + { + suid_program = Smb4KSettings::sudo(); + + break; + } + case Smb4KSettings::EnumSuperUserProgram::Super: + { + suid_program = Smb4KSettings::super(); + + break; + } + default: + { + // FIXME: Throw an error? + return; + } + } + + KProcess proc; + proc.setUseShell( true ); + proc << QString( "%1 smb4k_kill %2" ).arg( suid_program ).arg( m_proc->pid() ); + proc.start( KProcess::DontCare, KProcess::NoCommunication ); + } + else + { + m_proc->kill(); + } + } +} + + +/*************************************************************************** + Mounts recently used shares. +***************************************************************************/ + + +void Smb4KMounter::remount() +{ + if ( Smb4KSettings::remountShares() ) + { + const QValueList<Smb4KSambaOptionsInfo *> *list = &(optionsHandler()->customOptionsList()); + + for ( QValueList<Smb4KSambaOptionsInfo *>::ConstIterator it = list->begin(); + it != list->end(); ++it ) + { + if ( (*it)->remount() ) + { + QValueList<Smb4KShare> list = findShareByName( (*it)->itemName() ); + + bool mount = true; + + if ( !list.isEmpty() ) + { + for ( QValueList<Smb4KShare>::ConstIterator it = list.begin(); it != list.end(); ++it ) + { + if ( !(*it).isForeign() ) + { + mount = false; + + break; + } + else + { + continue; + } + } + } + + if ( mount ) + { +#ifndef __FreeBSD__ + mountShare( QString::null, (*it)->itemName().section( "/", 2, 2 ), QString::null, (*it)->itemName().section( "/", 3, 3 ) ); +#else + mountShare( QString::null, (*it)->itemName().section( "/", 2, 2 ).section( "@", 1, 1 ), QString::null, (*it)->itemName().section( "/", 3, 3 ) ); +#endif + } + + // If the share is to be remounted the next time, + // slotShutdown() will tell the options handler. + (*it)->setRemount( false ); + + continue; + } + else + { + continue; + } + } + + m_working = false; + emit state( MOUNTER_STOP ); + } + else + { + m_working = false; + emit state( MOUNTER_STOP ); + } +} + + +/*************************************************************************** + Imports all shares, that are mounted externally. +***************************************************************************/ + +void Smb4KMounter::import() +{ + QValueList<Smb4KShare *> shares; + +#ifndef __FreeBSD__ + + if ( m_proc_mounts.name().isEmpty() ) + { + m_proc_mounts.setName( "/proc/mounts" ); + } + + if ( !QFile::exists( m_proc_mounts.name() ) ) + { + if ( !m_proc_error ) + { + m_proc_error = true; + Smb4KError::error( ERROR_FILE_NOT_FOUND, m_proc_mounts.name() ); + } + else + { + // No need to do anything here + } + } + else + { + QStringList contents, list; + + // Read /proc/mounts: + + if ( m_proc_mounts.open( IO_ReadOnly ) ) + { + QTextStream ts( &m_proc_mounts ); + ts.setEncoding( QTextStream::Locale ); + + contents = QStringList::split( "\n", ts.read(), false ); + + m_proc_mounts.close(); + } + else + { + Smb4KError::error( ERROR_OPENING_FILE, m_proc_mounts.name() ); + + return; + } + + // Process the SMBFS and CIFS entries: + + list += contents.grep( " smbfs ", true ); + list += contents.grep( " cifs ", true ); + + if ( !list.isEmpty() ) + { + for ( QStringList::Iterator it = list.begin(); it != list.end(); it++ ) + { + Smb4KShare *new_share = NULL; + + if ( (*it).contains( " smbfs ", false ) != 0 ) + { + QString share_and_path = (*it).section( " smbfs ", 0, 0 ).stripWhiteSpace(); + QString name = share_and_path.section( " ", 0, 0 ).stripWhiteSpace().replace( "\\040", "\040" ); + QString path = share_and_path.section( " ", 1, 1 ).stripWhiteSpace(); + + if ( path.contains( "\\040" ) != 0 || path.contains( "\040" ) != 0 ) + { + name.replace( "_", "\040" ); + path.replace( "\\040", "\040" ); + } + + int uid = (*it).section( "uid=", 1, 1 ).section( ",", 0, 0 ).stripWhiteSpace().toInt(); + int gid = (*it).section( "gid=", 1, 1 ).section( ",", 0, 0 ).stripWhiteSpace().toInt(); + + new_share = new Smb4KShare( name, path, "smbfs", uid, gid ); + } + else if ( (*it).contains( " cifs ", false ) != 0 ) + { + QString share_and_path = (*it).section( " cifs ", 0, 0 ).stripWhiteSpace(); + QString name = share_and_path.section( " ", 0, 0 ).stripWhiteSpace().replace( "\\040", "\040" ); + QString path = share_and_path.section( " ", 1, 1 ).stripWhiteSpace(); + + if ( path.contains( "\\040" ) != 0 || path.contains( "\040" ) != 0 ) + { + name.replace( "_", "\040" ); + path.replace( "\\040", "\040" ); + } + + QString login = (*it).section( "username=", 1, 1 ).section( ",", 0, 0 ).stripWhiteSpace(); + + new_share = new Smb4KShare( name, path, "cifs", login ); + } + else + { + continue; + } + + if ( new_share ) + { + // If the a representative of the new share is already in the list of + // mounted shares, replace the new with the old one. + + Smb4KShare *existing_share = findShareByPath( new_share->path() ); + + if ( existing_share ) + { + delete new_share; + new_share = new Smb4KShare( *existing_share ); + } + + // Check if the share is broken and/or foreign. + + if ( (existing_share && !existing_share->isBroken()) || !existing_share ) + { + checkAccessibility( new_share ); + } + else + { + // Since new_share is a copy of existing_share, we do not need to do + // anything here. + } + + if ( !existing_share && QString::compare( new_share->filesystem(), "cifs" ) == 0 ) + { + bool foreign = true; + + if ( (!new_share->isBroken() && + (qstrncmp( new_share->canonicalPath(), + QDir( Smb4KSettings::mountPrefix() ).canonicalPath(), + QDir( Smb4KSettings::mountPrefix() ).canonicalPath().length() ) == 0 || + qstrncmp( new_share->canonicalPath(), + QDir::home().canonicalPath(), + QDir::home().canonicalPath().length() ) == 0)) || + (new_share->isBroken() && + (qstrncmp( new_share->path(), + QDir::homeDirPath(), + QDir::homeDirPath().length() ) == 0 || + qstrncmp( new_share->path(), + Smb4KSettings::mountPrefix(), + Smb4KSettings::mountPrefix().length() ) == 0)) ) + { + foreign = false; + } + + new_share->setForeign( foreign ); + } + + shares.append( new_share ); + } + } + } + } + +#else + + struct statfs *buf; + int count = getmntinfo( &buf, 0 ); + + if ( count == 0 ) + { + int err_code = errno; + + Smb4KError::error( ERROR_IMPORTING_SHARES, QString::null, strerror( err_code ) ); + + m_working = false; + return; + } + + for ( int i = 0; i < count; ++i ) + { + if ( !strcmp( buf[i].f_fstypename, "smbfs" ) ) + { + QString share_name( buf[i].f_mntfromname ); + QString path( buf[i].f_mntonname ); + QString fs( buf[i].f_fstypename ); + + QFileInfo info( QString( buf[i].f_mntonname )+"/." ); + + int uid = (int)info.ownerId(); + int gid = (int)info.groupId(); + + Smb4KShare *existing_share = findShareByPath( path ); + Smb4KShare *new_share = NULL; + + if ( existing_share ) + { + new_share = new Smb4KShare( *existing_share ); + } + else + { + new_share = new Smb4KShare( share_name, path, fs, uid, gid ); + } + + // Test if share is broken + if ( (existing_share && !existing_share->isBroken()) || !existing_share ) + { + checkAccessibility( new_share ); + } + else + { + // Since new_share is a copy of existing_share, we do not need to do + // anything here. + } + + shares.append( new_share ); + } + } + + // Apparently, under FreeBSD we do not need to delete + // the pointer (see manual page). + +#endif + + // Delete all entries of m_mounted_shares. + for ( QValueList<Smb4KShare *>::Iterator it = m_mounted_shares.begin(); it != m_mounted_shares.end(); ++it ) + { + delete *it; + } + + m_mounted_shares.clear(); + + m_mounted_shares = shares; + + emit updated(); + + m_working = false; +} + + +/*************************************************************************** + Mounts a share. (Public part) +***************************************************************************/ + +void Smb4KMounter::mountShare( const QString &workgroup, const QString &host, const QString &ip, const QString &share ) +{ + QString share_name = QString::null; + + if ( QString::compare( share, "homes" ) == 0 ) + { + share_name = specifyUser( host, kapp->mainWidget() ? kapp->mainWidget() : 0, "SpecifyUser" ); + } + else + { + share_name = share; + } + + if ( !share_name.stripWhiteSpace().isEmpty() ) + { + // Before doing anything else let's check that the + // share has not been mounted by the user already: + QValueList<Smb4KShare> list = findShareByName( QString( "//%1/%2" ).arg( host, share_name ) ); + + for ( QValueList<Smb4KShare>::ConstIterator it = list.begin(); it != list.end(); ++it ) + { + if ( !(*it).isForeign() ) + { + emit mountedShare( (*it).canonicalPath() ); + + return; + } + } + + m_queue.enqueue( new QString( QString( "%1:%2:%3:%4:%5" ).arg( Mount ) + .arg( workgroup, host ) + .arg( ip, share_name ) ) ); + } +} + + + +/*************************************************************************** + Mounts a share. (Private part) +***************************************************************************/ + +void Smb4KMounter::mount( const QString &workgroup, const QString &host, const QString &ip, const QString &share ) +{ + m_priv->setWorkgroup( workgroup ); + m_priv->setHost( host ); + m_priv->setShare( share ); + m_priv->setIP( ip ); + + // Create the mount point: + QDir *dir = new QDir( Smb4KSettings::mountPrefix() ); + + if ( !dir->exists() ) + { + if ( !dir->mkdir( dir->canonicalPath() ) ) + { + Smb4KError::error( ERROR_MKDIR_FAILED, dir->path() ); + m_working = false; + emit state( MOUNTER_STOP ); + + return; + } + } + + dir->setPath( dir->path() + "/" + + (Smb4KSettings::forceLowerCaseSubdirs() ? + m_priv->host().lower() : + m_priv->host()) ); + + if ( !dir->exists() ) + { + if ( !dir->mkdir( dir->canonicalPath() ) ) + { + Smb4KError::error( ERROR_MKDIR_FAILED, dir->path() ); + m_working = false; + emit state( MOUNTER_STOP ); + + return; + } + } + + dir->setPath( dir->path() + "/" + + (Smb4KSettings::forceLowerCaseSubdirs() ? + m_priv->share().lower() : + m_priv->share()) ); + + if ( !dir->exists() ) + { + if ( !dir->mkdir( dir->canonicalPath() ) ) + { + Smb4KError::error( ERROR_MKDIR_FAILED, dir->path() ); + m_working = false; + emit state( MOUNTER_STOP ); + + return; + } + } + + m_priv->setPath( QDir::cleanDirPath( dir->path() ) ); + + delete dir; + + // Now we are prepared to mount the share: + QString command, suid_program; + + switch ( Smb4KSettings::superUserProgram() ) + { + case Smb4KSettings::EnumSuperUserProgram::Sudo: + { + suid_program = "sudo"; + + break; + } + case Smb4KSettings::EnumSuperUserProgram::Super: + { + suid_program = "super"; + + break; + } + default: + { + return; + } + } + + Smb4KAuthInfo authInfo( m_priv->workgroup(), m_priv->host(), m_priv->share() ); + (void) passwordHandler()->readAuth( &authInfo ); + +#ifndef __FreeBSD__ + + // Let's see if the options handler knows the share: + Smb4KSambaOptionsInfo *info = optionsHandler()->findItem( QString( "//%1/%2" ).arg( m_priv->host(), m_priv->share() ), true ); + + // Determine the file system we have to use: + int filesystem; + + if ( info ) + { + filesystem = QString::compare( info->filesystem().lower(), "cifs" ) == 0 ? + Smb4KSettings::EnumFilesystem::CIFS : + Smb4KSettings::EnumFilesystem::SMBFS; + } + else + { + filesystem = Smb4KSettings::filesystem(); + } + + // Compile the mount command: + switch ( filesystem ) + { + case Smb4KSettings::EnumFilesystem::CIFS: + { + command.append( Smb4KSettings::alwaysUseSuperUser() ? // FIXME: Check if suid program is installed + QString( "%1 smb4k_mount -s -t cifs " ).arg( suid_program ) : + "smb4k_mount -n -t cifs " ); + + command.append( "-o " ); + + command.append( optionsHandler()->mountOptions( QString( "//%1/%2" ).arg( m_priv->host(), m_priv->share() ) ) ); + + command.append( !m_priv->workgroup().stripWhiteSpace().isEmpty() ? + QString( "domain='%1'," ).arg( m_priv->workgroup() ) : + "" ); + + command.append( !m_priv->ip().stripWhiteSpace().isEmpty() ? + QString( "ip=%1," ).arg( m_priv->ip() ) : + "" ); + + command.append( !authInfo.user().isEmpty() ? + QString( "user=%1" ).arg( authInfo.user() ) : + "guest" ); + + command.append( " -- " ); + + command.append( QString( "//'%1'/'%2' '%3'" ).arg( m_priv->host(), m_priv->share(), m_priv->path() ) ); + + m_priv->setCIFSLogin( !authInfo.user().isEmpty() ? + authInfo.user() : + "guest" ); + + m_priv->setFileSystem( "cifs" ); + + break; + } + case Smb4KSettings::EnumFilesystem::SMBFS: + { + command.append( Smb4KSettings::alwaysUseSuperUser() ? // FIXME: Check if suid program is installed + QString( "%1 smb4k_mount -s -t smbfs " ).arg( suid_program ) : + "smb4k_mount -n -t smbfs " ); + + command.append( "-o " ); + + command.append( optionsHandler()->mountOptions( QString( "//%1/%2" ).arg( m_priv->host(), m_priv->share() ) ) ); + + command.append( !m_priv->workgroup().stripWhiteSpace().isEmpty() ? + QString( "workgroup='%1'," ).arg( m_priv->workgroup() ) : + "" ); + + command.append( !m_priv->ip().stripWhiteSpace().isEmpty() ? + QString( "ip=%1," ).arg( m_priv->ip() ) : + "" ); + + command.append( !authInfo.user().isEmpty() ? + QString( "username=%1" ).arg( authInfo.user() ) : + "guest" ); + + command.append( " -- " ); + + command.append( QString( "//'%1'/'%2' '%3'" ).arg( m_priv->host(), m_priv->share(), m_priv->path() ) ); + + m_priv->setFileSystem( "smbfs" ); + + break; + } + default: + { + return; + } + } + + m_proc->setEnvironment( "PASSWD", !authInfo.password().isEmpty() ? authInfo.password() : "" ); + +#else + + Smb4KSambaOptionsInfo *info = optionsHandler()->findItem( "//"+m_priv->host()+"/"+m_priv->share() ); + + int port = info && info->port() != -1 ? + info->port() : + Smb4KSettings::remotePort(); + + command.append( Smb4KSettings::alwaysUseSuperUser() ? // FIXME: Check if suid program is installed + QString( "%1 smb4k_mount " ).arg( suid_program ) : + "smb4k_mount " ); + + command.append( optionsHandler()->mountOptions( QString( "//%1/%2" ).arg( m_priv->host(), m_priv->share() ) ) ); + + command.append( !m_priv->workgroup().stripWhiteSpace().isEmpty() ? + QString( " -W '%1'" ).arg( m_priv->workgroup() ) : + "" ); + + command.append( !m_priv->ip().stripWhiteSpace().isEmpty() ? + QString( " -I %1" ).arg( m_priv->ip() ) : + "" ); + + command.append( " -N" ); + + command.append( " -- " ); + + command.append( QString( "//%1@'%2':%3/'%4' '%5'" ).arg( !authInfo.user().isEmpty() ? authInfo.user() : "guest" ) + .arg( m_priv->host() ) + .arg( port ) + .arg( m_priv->share(), m_priv->path() ) ); + +#endif + + // Start the mount process: + *m_proc << command; + + startProcess( Mount ); +} + + +/**************************************************************************** + Unmount a share. (Public part) +****************************************************************************/ + +void Smb4KMounter::unmountShare( Smb4KShare *share, bool force, bool noMessage ) +{ + // Do *not* change share->canonicalPath(). It is necessary for the + // checks below to work. + m_queue.enqueue( new QString( QString( "%1:%2:%3:%4" ).arg( Unmount ) + .arg( share->canonicalPath() ) + .arg( force, noMessage ) ) ); +} + + +/*************************************************************************** + Unmount a share. (Private part) +***************************************************************************/ + +void Smb4KMounter::unmount( const QString &mountpoint, bool force, bool noMessage ) +{ + // First let's see if all requirements are fullfilled: + + if ( force ) + { + // Check that the user enabled the "Force Unmounting" ability: + if ( !Smb4KSettings::useForceUnmount() ) + { + Smb4KError::error( ERROR_FEATURE_NOT_ENABLED ); + m_working = false; + emit state( MOUNTER_STOP ); + + return; + } + } + + // Compose the unmount command: + if ( !mountpoint.stripWhiteSpace().isEmpty() ) + { + bool execute = false; + + QString path = mountpoint; + m_priv->setPath( path.replace( '\044', "\044" ) ); + + QString suid_program, command; + + if ( Smb4KSettings::useForceUnmount() || Smb4KSettings::alwaysUseSuperUser() ) + { + switch ( Smb4KSettings::superUserProgram() ) + { + case Smb4KSettings::EnumSuperUserProgram::Sudo: + { + suid_program = Smb4KSettings::sudo(); + + break; + } + case Smb4KSettings::EnumSuperUserProgram::Super: + { + suid_program = Smb4KSettings::super(); + + break; + } + default: + { + // FIXME: Throw an error? + return; + } + } + } + + Smb4KShare *share = findShareByPath( mountpoint ); + + if ( share ) + { + if ( !share->isForeign() ) + { + if ( force ) + { + if ( KMessageBox::questionYesNo( 0, i18n( "Do you really want to force the unmounting of this share?" ), QString::null, KStdGuiItem::yes(), KStdGuiItem::no(), "Dont Ask Forced", KMessageBox::Notify ) == KMessageBox::Yes ) + { +#ifdef __linux__ + command.append( QString( "%1 smb4k_umount -s -l " ).arg( suid_program ) ); +#else +#ifdef __FreeBSD__ + command.append( QString( "%1 smb4k_umount " ).arg( suid_program ) ); +#else + command.append( QString( "%1 smb4k_umount -s " ).arg( suid_program ) ); +#endif +#endif + execute = true; + } + else + { + m_working = false; + emit state( MOUNTER_STOP ); + + return; + } + } + else + { + if ( !Smb4KSettings::alwaysUseSuperUser() ) + { +#ifndef __FreeBSD__ + command.append( "smb4k_umount -n " ); +#else + command.append( "smb4k_umount " ); +#endif + } + else + { +#ifndef __FreeBSD__ + command.append( QString( "%1 smb4k_umount -s " ).arg( suid_program ) ); +#else + command.append( QString( "%1 smb4k_umount " ).arg( suid_program ) ); +#endif + } + } + } + else + { + if ( Smb4KSettings::unmountForeignShares() ) + { + if ( force ) + { + if ( KMessageBox::questionYesNo( 0, i18n( "Do you really want to force the unmounting of this share?" ), QString::null, KStdGuiItem::yes(), KStdGuiItem::no(), "Dont Ask Forced", KMessageBox::Notify ) == KMessageBox::Yes ) + { +#ifdef __linux__ + command.append( QString( "%1 smb4k_umount -s -l " ).arg( suid_program ) ); +#else +#ifdef __FreeBSD__ + command.append( QString( "%1 smb4k_umount " ).arg( suid_program ) ); +#else + command.append( QString( "%1 smb4k_umount -s " ).arg( suid_program ) ); +#endif +#endif + execute = true; + } + else + { + m_working = false; + emit state( MOUNTER_STOP ); + + return; + } + } + else + { + if ( !Smb4KSettings::alwaysUseSuperUser() ) + { +#ifndef __FreeBSD__ + command.append( "smb4k_umount -n " ); +#else + command.append( "smb4k_umount " ); +#endif + } + else + { +#ifndef __FreeBSD__ + command.append( QString( "%1 smb4k_umount -s " ).arg( suid_program ) ); +#else + command.append( QString( "%1 smb4k_umount " ).arg( suid_program ) ); +#endif + } + } + } + else + { + if ( !noMessage ) + { + Smb4KError::error( ERROR_UNMOUNTING_NOT_ALLOWED ); + } + + m_working = false; + emit state( MOUNTER_STOP ); + + return; + } + } + +#ifndef __FreeBSD__ + command.append( QString( "-t %1 " ).arg( share->filesystem() ) ); +#endif + command.append( QString( "'%1'" ).arg( m_priv->path() ) ); + + if ( force && !execute ) + { + return; + } + + emit aboutToUnmount( mountpoint ); + + *m_proc << command; + startProcess( Unmount ); + } + else + { + // FIXME: Throw an error? + return; + } + } + else + { + Smb4KError::error( ERROR_MOUNTPOINT_EMPTY ); + m_working = false; + emit state( MOUNTER_STOP ); + + return; + } +} + + +/*************************************************************************** + Unmounts all shares at once. (Public part) +***************************************************************************/ + +void Smb4KMounter::unmountAllShares() +{ + m_queue.enqueue( new QString( QString( "%1" ).arg( UnmountAll ) ) ); +} + + +/*************************************************************************** + Unmounts all shares at once. +***************************************************************************/ + +void Smb4KMounter::unmountAll() +{ + for ( QValueListIterator<Smb4KShare *> it = m_mounted_shares.begin(); it != m_mounted_shares.end(); ++it ) + { + unmountShare( *it, false, true ); + } + + m_working = false; +} + + +/*************************************************************************** + Starts any process. +***************************************************************************/ + +void Smb4KMounter::startProcess( int state ) +{ + m_buffer = QString::null; + m_state = state; + + if ( m_state != Import ) + { + QApplication::setOverrideCursor( waitCursor ); + } + + m_proc->start( KProcess::NotifyOnExit, KProcess::AllOutput ); +} + + +/*************************************************************************** + Ends any process. This functions tells the mounter what to do + afterwards. +***************************************************************************/ + +void Smb4KMounter::endProcess() +{ + switch ( m_state ) + { + case Mount: + processMount(); + break; + case Unmount: + processUnmount(); + break; + default: + break; + } + + m_state = Idle; + + m_priv->clearData(); + + QApplication::restoreOverrideCursor(); + m_proc->clearArguments(); + + m_working = false; + emit state( MOUNTER_STOP ); +} + + +/*************************************************************************** + Process mounts. +***************************************************************************/ + +void Smb4KMounter::processMount() +{ + Smb4KShare *share = NULL; + +#ifndef __FreeBSD__ + + if ( m_proc->normalExit() ) + { + if ( m_buffer.contains( "smb4k_mount:", true ) == 0 && + m_buffer.contains( "failed", true ) == 0 && + m_buffer.contains( "ERR", true ) == 0 && + m_buffer.contains( "/bin/sh:" ) == 0 && + m_buffer.contains( "mount:", true ) == 0 && + m_buffer.contains( "smbmnt" ) == 0 && + m_buffer.contains( m_priv->path() ) == 0 && + m_buffer.contains( "mount error" ) == 0 && + m_buffer.contains( "bad user name" ) == 0 && + m_buffer.contains( "bad group name" ) == 0 ) + { + QString name = QString( "//%1/%2" ).arg( m_priv->host() ).arg( m_priv->share() ); + + // Check file system +#if !defined(__solaris__) + struct statfs filesystem; +#else + struct statvfs filesystem; +#endif + +#if !defined(__solaris__) && !defined(__irix__) + if ( statfs( m_priv->path(), &filesystem ) == -1 ) +#elif defined(__irix__) + if ( statfs( m_priv->path(), &filesystem, sizeof( filesystem ), 0 ) == -1 ) +#else + if ( statvfs( m_priv->path(), &filesystem ) == -1 ) +#endif + { + // The query failed. Go with the file system already defined in m_priv. + if ( QString::compare( m_priv->filesystem(), "smbfs" ) == 0 ) + { + share = new Smb4KShare( name, m_priv->path(), m_priv->filesystem(), (int)getuid(), (int)getgid() ); + m_mounted_shares.append( share ); + } + else if ( QString::compare( m_priv->filesystem(), "cifs" ) == 0 ) + { + // The user name will be send if no login was specified. + QString cifs_login = !m_priv->cifsLogin().isEmpty() ? + m_priv->cifsLogin() : + getpwuid( getuid() )->pw_name; + + share = new Smb4KShare( name, m_priv->path(), m_priv->filesystem(), cifs_login, false ); + m_mounted_shares.append( share ); + } + } + else + { +#if !defined(__FreeBSD__) && !defined(__solaris__) && !defined(__irix__) + if ( (uint)filesystem.f_type == 0xFF534D42 /* CIFS */) + { + // The user name will be send if no login was specified. + QString cifs_login = !m_priv->cifsLogin().isEmpty() ? + m_priv->cifsLogin() : + getpwuid( getuid() )->pw_name; + + share = new Smb4KShare( name, m_priv->path(), "cifs", cifs_login, false ); + m_mounted_shares.append( share ); + } + else if ( (uint)filesystem.f_type == 0x517B /* SMBFS */) + { + share = new Smb4KShare( name, m_priv->path(), "smbfs", (int)getuid(), (int)getgid() ); + m_mounted_shares.append( share ); + } +#elif defined(__solaris__) + if ( (uint)filesystem.f_basetype == 0xFF534D42 /* CIFS */) + { + // The user name will be send if no login was specified. + QString cifs_login = !m_priv->cifsLogin().isEmpty() ? + m_priv->cifsLogin() : + getpwuid( getuid() )->pw_name; + + share = new Smb4KShare( name, m_priv->path(), "cifs", cifs_login, false ); + m_mounted_shares.append( share ); + } + else if ( (uint)filesystem.f_basetype == 0x517B /* SMBFS */) + { + share = new Smb4KShare( name, m_priv->path(), "smbfs", (int)getuid(), (int)getgid() ); + m_mounted_shares.append( share ); + } +#elif defined(__irix__) + if ( (uint)filesystem.f_fstyp == 0xFF534D42 /* CIFS */) + { + // The user name will be send if no login was specified. + QString cifs_login = !m_priv->cifsLogin().isEmpty() ? + m_priv->cifsLogin() : + getpwuid( getuid() )->pw_name; + + share = new Smb4KShare( name, m_priv->path(), "cifs", cifs_login, false ); + m_mounted_shares.append( share ); + } + else if ( (uint)filesystem.f_basetype == 0x517B && !strncmp( fs, "smbfs", strlen( fs )+1 ) ) + { + share = new Smb4KShare( name, m_priv->path(), "smbfs", (int)getuid(), (int)getgid() ); + m_mounted_shares.append( share ); + } +#endif + else + { + // Error... We don't create a share. + } + } + + if ( share ) + { + // Check that the share is accessible: + checkAccessibility( share ); + + emit mountedShare( m_priv->path() ); + } + } + else + { + if ( m_buffer.contains( "ERRbadpw" ) != 0 || + m_buffer.contains( "ERRnoaccess" ) != 0 || + m_buffer.contains( "mount error 13 = Permission denied" ) != 0 ) + { + int state = Smb4KPasswordHandler::None; + + if ( m_buffer.contains( "ERRbadpw" ) != 0 ) + { + state = Smb4KPasswordHandler::BadPassword; + } + else if ( m_buffer.contains( "ERRnoaccess" ) != 0 ) + { + state = Smb4KPasswordHandler::AccessDenied; + } + else if ( m_buffer.contains( "mount error 13 = Permission denied" ) != 0 ) + { + state = Smb4KPasswordHandler::PermDenied; + } + + // If the user supplied auth information, we will retry mounting. + if ( passwordHandler()->askpass( m_priv->workgroup(), m_priv->host(), m_priv->share(), state ) ) + { + mountShare( m_priv->workgroup(), m_priv->host(), m_priv->ip(), m_priv->share() ); + } + } + else if ( m_buffer.contains( "ERRnosuchshare" ) != 0 && m_priv->share().contains( "_" ) != 0 ) + { + QString share_string = static_cast<QString>( m_priv->share() ).replace( "_", " " ); + mountShare( m_priv->workgroup(), m_priv->host(), m_priv->ip(), share_string ); + } + else + { + QString name = QString( "//%1/%2" ).arg( m_priv->host() ).arg( m_priv->share() ); + + Smb4KError::error( ERROR_MOUNTING_SHARE, name, m_buffer ); + } + } + } + +#else + + if ( m_proc->normalExit() ) + { + if ( m_buffer.contains( "smb4k_mount:", true ) == 0 && + m_buffer.contains( "syserr =", true ) == 0 && + /* To make sure we catch all errors, also check for the following + strings. Maybe we can remove them?? */ + m_buffer.contains( "Authentication error", true ) == 0 && + m_buffer.contains( "Connection refused", true ) == 0 && + m_buffer.contains( "Operation not permitted", true ) == 0 ) + { + import(); // FIXME: *cough* What is this for??? + + Smb4KAuthInfo authInfo( m_priv->workgroup(), m_priv->host(), m_priv->share() ); + (void) passwordHandler()->readAuth( &authInfo ); + + QString name = QString( "//%1@%2/%3" ).arg( authInfo.user().upper(), m_priv->host().upper(), m_priv->share().upper() ); + + share = new Smb4KShare( name, m_priv->path(), m_priv->filesystem(), (int)getuid(), (int)getgid() ); + m_mounted_shares.append( share ); + + // Check that the share is accessible: + checkAccessibility( share ); + + emit mountedShare( m_priv->path() ); + } + else + { + if ( m_buffer.contains( "Authentication error" ) != 0 ) + { + // If the user supplied auth information, we will retry mounting. + if ( passwordHandler()->askpass( m_priv->workgroup(), m_priv->host(), m_priv->share(), Smb4KPasswordHandler::AuthError ) ) + { + mountShare( m_priv->workgroup(), m_priv->host(), m_priv->ip() , m_priv->share() ); + } + } + else + { + Smb4KAuthInfo authInfo( m_priv->workgroup(), m_priv->host(), m_priv->share() ); + (void) passwordHandler()->readAuth( &authInfo ); + + QString name = QString( "//%1@%2/%3" ).arg( authInfo.user().upper(), m_priv->host().upper(), m_priv->share().upper() ); + + Smb4KError::error( ERROR_MOUNTING_SHARE, name, m_buffer ); + } + } + } + +#endif + + emit updated(); +} + + +/*************************************************************************** + Process unmounts. +***************************************************************************/ + +void Smb4KMounter::processUnmount() +{ + // Get the share: + Smb4KShare *share = findShareByPath( m_priv->path() ); + + if ( m_proc->normalExit() ) + { + if ( m_buffer.isEmpty() ) + { + if ( qstrncmp( share->canonicalPath(), + QDir( Smb4KSettings::mountPrefix() ).canonicalPath().local8Bit(), + QDir( Smb4KSettings::mountPrefix() ).canonicalPath().local8Bit().length() ) == 0 ) + { + QDir dir( share->canonicalPath() ); + + if ( dir.rmdir( dir.canonicalPath(), true ) ) + { + dir.cdUp(); + dir.rmdir( dir.canonicalPath(), true ); + } + } + + m_mounted_shares.remove(share); + } + else + { + // If the user's computer is configured by a DHCP server, under + // rare circumstances it might occur that sudo reports an error, + // because it is not able to resolve the host. This error message + // will be removed, because it does not affect the unmounting: + if ( m_buffer.contains( "sudo: unable to resolve host", true ) != 0 ) + { + size_t hostnamelen = 255; + char *hostname = new char[hostnamelen]; + + if ( gethostname( hostname, hostnamelen ) == -1 ) + { + int error_number = errno; + Smb4KError::error( ERROR_GETTING_HOSTNAME, QString::null, strerror( error_number ) ); + } + else + { + QString str = QString( "sudo: unable to resolve host %1\n" ).arg( hostname ); + + m_buffer.remove( str, false /* case insensitive */ ); + + if ( !m_buffer.isEmpty() ) + { + Smb4KError::error( ERROR_UNMOUNTING_SHARE, share->name(), m_buffer ); + } + else + { + if ( qstrncmp( share->canonicalPath(), + QDir( Smb4KSettings::mountPrefix() ).canonicalPath().local8Bit(), + QDir( Smb4KSettings::mountPrefix() ).canonicalPath().local8Bit().length() ) == 0 ) + { + QDir dir( share->canonicalPath() ); + + if ( dir.rmdir( dir.canonicalPath(), true ) ) + { + dir.cdUp(); + dir.rmdir( dir.canonicalPath(), true ); + } + } + + m_mounted_shares.remove(share); + } + } + + delete [] hostname; + } + else + { + Smb4KError::error( ERROR_UNMOUNTING_SHARE, share->name(), m_buffer ); + } + } + } + + emit updated(); +} + + +/*************************************************************************** + Check if a share is already mounted +***************************************************************************/ + +bool Smb4KMounter::isMounted( const QString &name, bool userOnly ) +{ + QValueList<Smb4KShare> list = findShareByName( name ); + + bool mounted = false; + + if ( !list.isEmpty() && userOnly ) + { + for ( QValueList<Smb4KShare>::ConstIterator it = list.begin(); it != list.end(); ++it ) + { + if ( !(*it).isForeign() ) + { + mounted = true; + + break; + } + else + { + continue; + } + } + } + else + { + mounted = !list.isEmpty(); + } + + return mounted; +} + + +/*************************************************************************** + Find a share in the list with its path +***************************************************************************/ + +Smb4KShare* Smb4KMounter::findShareByPath( const QString &path ) +{ + if ( path.isEmpty() || m_mounted_shares.isEmpty() ) + { + return NULL; + } + + Smb4KShare *share = NULL; + + for ( QValueListIterator<Smb4KShare *> it = m_mounted_shares.begin(); it != m_mounted_shares.end(); ++it ) + { + if( QString::compare( path.upper(), QString::fromLocal8Bit( (*it)->path(), -1 ).upper() ) == 0 || + QString::compare( path.upper(), QString::fromLocal8Bit( (*it)->canonicalPath(), -1 ).upper() ) == 0 ) + { + share = *it; + + break; + } + } + + return share; +} + + +/*************************************************************************** + Find the list of mounts of a share +***************************************************************************/ + +QValueList<Smb4KShare> Smb4KMounter::findShareByName( const QString &name ) +{ + QValueList<Smb4KShare> list; + + if ( name.isEmpty() || m_mounted_shares.isEmpty() ) + { + return list; // is empty + } + + QString n = name; + + for ( QValueListIterator<Smb4KShare *> it = m_mounted_shares.begin(); it != m_mounted_shares.end(); ++it ) + { + if( QString::compare( (*it)->name().upper(), name.upper() ) == 0 || + QString::compare( (*it)->name().upper(), n.replace( " ", "_" ).upper() ) == 0 ) + { + list.append( *(*it) ); + + continue; + } + else + { + continue; + } + } + + return list; +} + + +/*************************************************************************** + Returns a list of mount points that belong to broken shares +***************************************************************************/ + +const QValueList<Smb4KShare *> Smb4KMounter::getBrokenShares() +{ + QValueList<Smb4KShare *> broken_shares; + + for ( QValueListIterator<Smb4KShare *> it = m_mounted_shares.begin(); it != m_mounted_shares.end(); ++it ) + { + if ( (*it)->isBroken() ) + { + broken_shares.append( *it ); + + continue; + } + else + { + continue; + } + } + + return broken_shares; +} + + +void Smb4KMounter::prepareForShutdown() +{ + slotShutdown(); +} + + +void Smb4KMounter::checkAccessibility( Smb4KShare *share ) +{ + if ( share ) + { + m_priv->thread.setMountpoint( share->path() ); + m_priv->thread.start(); + m_priv->thread.wait( THREAD_WAITING_TIME ); + m_priv->thread.terminate(); + m_priv->thread.wait(); + + share->setBroken( m_priv->thread.isBroken() ); + share->setTotalDiskSpace( m_priv->thread.totalDiskSpace() ); + share->setFreeDiskSpace( m_priv->thread.freeDiskSpace() ); + } + else + { + // FIXME: Should we throw an error here? + } +} + + +void Smb4KMounter::timerEvent( QTimerEvent * ) +{ + if ( !m_working && !m_queue.isEmpty() ) + { + // Tell the mounter, that it is busy. + m_working = true; + + QString *item = m_queue.dequeue(); + int todo = item->section( ":", 0, 0 ).toInt(); + + switch ( todo ) + { + case Remount: + { + remount(); + break; + } + case Import: + { + import(); + break; + } + case Mount: + { + emit state( MOUNTER_MOUNTING ); + mount( item->section( ":", 1, 1 ), item->section( ":", 2, 2 ), item->section( ":", 3, 3 ), item->section( ":", 4, 4 ) ); + break; + } + case Unmount: + { + emit state( MOUNTER_UNMOUNTING ); + unmount( item->section( ":", 1, 1 ), (bool)item->section( ":", 2, 2 ).toInt() /* force */, (bool)item->section( ":", 3, 3 ).toInt() /* noMessage */); + break; + } + case UnmountAll: + { + unmountAll(); + break; + } + default: + { + break; + } + } + + delete item; + } + + m_priv->timerTicks++; + + if ( m_priv->timerTicks * timerInterval() >= Smb4KSettings::checkInterval() /* msec */ && + (!m_working || m_queue.isEmpty()) ) + { + m_queue.enqueue( new QString( QString( "%1:" ).arg( Import ) ) ); + m_priv->timerTicks = 0; + } +} + + +///////////////////////////////////////////////////////////////////////////// +// SLOT IMPLEMENTATIONS +///////////////////////////////////////////////////////////////////////////// + + +void Smb4KMounter::slotProcessExited( KProcess * ) +{ + endProcess(); +} + + +void Smb4KMounter::slotReceivedStdout( KProcess *, char *buf, int len ) +{ + m_buffer.append( QString::fromLocal8Bit( buf, len ) ); +} + + +void Smb4KMounter::slotReceivedStderr( KProcess *, char *buf, int len ) +{ + m_buffer.append( QString::fromLocal8Bit( buf, len ) ); +} + + +void Smb4KMounter::slotShutdown() +{ + // Abort any action: + abort(); + + // Prepare for shutdown: + if ( Smb4KSettings::remountShares() && !m_mounted_shares.isEmpty() ) + { + for ( QValueList<Smb4KShare *>::ConstIterator it = m_mounted_shares.begin(); it != m_mounted_shares.end(); ++it ) + { + optionsHandler()->remount( *it, !(*it)->isForeign() ); + } + } + + optionsHandler()->sync(); + + QDir dir; + + dir.cd( Smb4KSettings::mountPrefix() ); + + QStringList dirs = dir.entryList( QDir::Dirs, QDir::DefaultSort ); + + QValueList<Smb4KShare *> broken_shares = getBrokenShares(); + + for ( QStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it ) + { + if ( QString::compare( *it, "." ) != 0 && QString::compare( *it, ".." ) != 0 ) + { + bool broken = false; + + for ( QValueListIterator<Smb4KShare *> bs = broken_shares.begin(); bs != broken_shares.end(); ++bs ) + { + if ( qstrncmp( (*bs)->path(), + Smb4KSettings::mountPrefix()+*it, + (Smb4KSettings::mountPrefix()+*it).length() ) == 0 || + qstrncmp( (*bs)->canonicalPath(), + Smb4KSettings::mountPrefix()+*it, + (Smb4KSettings::mountPrefix()+*it).length() ) == 0 ) + { + broken = true; + + break; + } + else + { + continue; + } + } + + if ( !broken ) + { + dir.cd( *it ); + + QStringList subdirs = dir.entryList( QDir::Dirs, QDir::DefaultSort ); + + for ( QStringList::ConstIterator i = subdirs.begin(); i != subdirs.end(); ++i ) + { + if ( QString::compare( *i, "." ) != 0 && QString::compare( *i, ".." ) != 0 ) + { + dir.rmdir( *i ); + } + } + + dir.cdUp(); + dir.rmdir( *it ); + } + } + } + + broken_shares.clear(); + + if ( Smb4KSettings::unmountSharesOnExit() ) + { + QString suid_program, command; + + switch( Smb4KSettings::superUserProgram() ) + { + case Smb4KSettings::EnumSuperUserProgram::Sudo: + { + suid_program = Smb4KSettings::sudo(); + + break; + } + case Smb4KSettings::EnumSuperUserProgram::Super: + { + suid_program = Smb4KSettings::super(); + + break; + } + default: + { + // FIXME: Throw an error? + return; + } + } + + KProcess proc; + proc.setUseShell( true ); + proc.detach(); + + for ( QValueListIterator<Smb4KShare *> it = m_mounted_shares.begin(); it != m_mounted_shares.end(); ++it ) + { + if ( !(*it)->isForeign() ) + { + if ( Smb4KSettings::alwaysUseSuperUser() ) + { +#ifndef __FreeBSD__ + command.append( QString( "%1 smb4k_umount -s -t %2 " ).arg( suid_program ).arg( (*it)->filesystem() ) ); +#else + command.append( QString( "%1 smb4k_umount " ).arg( suid_program ) ); +#endif + command.append( KProcess::quote( (*it)->path() ) ); + command.append( " ; " ); + } + else + { +#ifndef __FreeBSD__ + command.append( QString( "smb4k_umount -n -t %1 " ).arg( (*it)->filesystem() ) ); +#else + command.append( "smb4k_umount " ); +#endif + command.append( KProcess::quote( (*it)->path() ) ); + command.append( " ; " ); + } + + dir.setPath( (*it)->canonicalPath() ); + +#ifndef __FreeBSD__ + command.append( "rmdir --ignore-fail-on-non-empty " ); + command.append( KProcess::quote( dir.canonicalPath() ) ); + command.append( " ; " ); + command.append( "rmdir --ignore-fail-on-non-empty " ); + dir.cdUp(); + command.append( KProcess::quote( dir.canonicalPath() ) ); + command.append( " ; " ); +#else + command.append( "rmdir " ); + command.append( KProcess::quote( dir.canonicalPath() ) ); + command.append( " ; " ); + command.append( "rmdir " ); + dir.cdUp(); + command.append( KProcess::quote( dir.canonicalPath() ) ); + command.append( " ; " ); +#endif + } + else + { + continue; + } + } + + command.truncate( command.length() - 2 ); + + proc << command; + proc.start( KProcess::DontCare, KProcess::NoCommunication ); + } +} + + +#include "smb4kmounter.moc" |