You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
smb4k/smb4k/core/smb4kmounter.cpp

1719 lines
48 KiB

/***************************************************************************
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 *
***************************************************************************/
// TQt includes
#include <tqapplication.h>
#include <tqdir.h>
#include <tqtextstream.h>
#include <tqfile.h>
#include <tqtextstream.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 <tqfileinfo.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( TQObject *parent, const char *name ) : TQObject( 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, TQT_SIGNAL( processExited( KProcess * ) ),
this, TQT_SLOT( slotProcessExited( KProcess * ) ) );
connect( m_proc, TQT_SIGNAL( receivedStdout( KProcess *, char *, int ) ),
this, TQT_SLOT( slotReceivedStdout( KProcess *, char *, int ) ) );
connect( m_proc, TQT_SIGNAL( receivedStderr( KProcess *, char *, int ) ),
this, TQT_SLOT( slotReceivedStderr( KProcess *, char *, int ) ) );
connect( kapp, TQT_SIGNAL( shutDown() ),
this, TQT_SLOT( slotShutdown() ) );
}
Smb4KMounter::~Smb4KMounter()
{
abort();
for ( TQValueList<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 TQString( TQString( "%1:" ).arg( Import ) ) );
m_queue.enqueue( new TQString( TQString( "%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() )
{
TQString 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 << TQString( "%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 TQValueList<Smb4KSambaOptionsInfo *> *list = &(optionsHandler()->customOptionsList());
for ( TQValueList<Smb4KSambaOptionsInfo *>::ConstIterator it = list->begin();
it != list->end(); ++it )
{
if ( (*it)->remount() )
{
TQValueList<Smb4KShare> list = findShareByName( (*it)->itemName() );
bool mount = true;
if ( !list.isEmpty() )
{
for ( TQValueList<Smb4KShare>::ConstIterator it = list.begin(); it != list.end(); ++it )
{
if ( !(*it).isForeign() )
{
mount = false;
break;
}
else
{
continue;
}
}
}
if ( mount )
{
#ifndef __FreeBSD__
mountShare( TQString(), (*it)->itemName().section( "/", 2, 2 ), TQString(), (*it)->itemName().section( "/", 3, 3 ) );
#else
mountShare( TQString(), (*it)->itemName().section( "/", 2, 2 ).section( "@", 1, 1 ), TQString(), (*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()
{
TQValueList<Smb4KShare *> shares;
#ifndef __FreeBSD__
if ( m_proc_mounts.name().isEmpty() )
{
m_proc_mounts.setName( "/proc/mounts" );
}
if ( !TQFile::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
{
TQStringList contents, list;
// Read /proc/mounts:
if ( m_proc_mounts.open( IO_ReadOnly ) )
{
TQTextStream ts( &m_proc_mounts );
ts.setEncoding( TQTextStream::Locale );
contents = TQStringList::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 ( TQStringList::Iterator it = list.begin(); it != list.end(); it++ )
{
Smb4KShare *new_share = NULL;
if ( (*it).contains( " smbfs ", false ) != 0 )
{
TQString share_and_path = (*it).section( " smbfs ", 0, 0 ).stripWhiteSpace();
TQString name = share_and_path.section( " ", 0, 0 ).stripWhiteSpace().replace( "\\040", "\040" );
TQString 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 )
{
TQString share_and_path = (*it).section( " cifs ", 0, 0 ).stripWhiteSpace();
TQString name = share_and_path.section( " ", 0, 0 ).stripWhiteSpace().replace( "\\040", "\040" );
TQString path = share_and_path.section( " ", 1, 1 ).stripWhiteSpace();
if ( path.contains( "\\040" ) != 0 || path.contains( "\040" ) != 0 )
{
name.replace( "_", "\040" );
path.replace( "\\040", "\040" );
}
TQString 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 && TQString::compare( new_share->filesystem(), "cifs" ) == 0 )
{
bool foreign = true;
if ( (!new_share->isBroken() &&
(tqstrncmp( new_share->canonicalPath(),
TQDir( Smb4KSettings::mountPrefix() ).canonicalPath(),
TQDir( Smb4KSettings::mountPrefix() ).canonicalPath().length() ) == 0 ||
tqstrncmp( new_share->canonicalPath(),
TQDir::home().canonicalPath(),
TQDir::home().canonicalPath().length() ) == 0)) ||
(new_share->isBroken() &&
(tqstrncmp( new_share->path(),
TQDir::homeDirPath(),
TQDir::homeDirPath().length() ) == 0 ||
tqstrncmp( 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, TQString(), strerror( err_code ) );
m_working = false;
return;
}
for ( int i = 0; i < count; ++i )
{
if ( !strcmp( buf[i].f_fstypename, "smbfs" ) )
{
TQString share_name( buf[i].f_mntfromname );
TQString path( buf[i].f_mntonname );
TQString fs( buf[i].f_fstypename );
TQFileInfo info( TQString( 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 ( TQValueList<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 TQString &workgroup, const TQString &host, const TQString &ip, const TQString &share )
{
TQString share_name = TQString();
if ( TQString::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:
TQValueList<Smb4KShare> list = findShareByName( TQString( "//%1/%2" ).arg( host, share_name ) );
for ( TQValueList<Smb4KShare>::ConstIterator it = list.begin(); it != list.end(); ++it )
{
if ( !(*it).isForeign() )
{
emit mountedShare( (*it).canonicalPath() );
return;
}
}
m_queue.enqueue( new TQString( TQString( "%1:%2:%3:%4:%5" ).arg( Mount )
.arg( workgroup, host )
.arg( ip, share_name ) ) );
}
}
/***************************************************************************
Mounts a share. (Private part)
***************************************************************************/
void Smb4KMounter::mount( const TQString &workgroup, const TQString &host, const TQString &ip, const TQString &share )
{
m_priv->setWorkgroup( workgroup );
m_priv->setHost( host );
m_priv->setShare( share );
m_priv->setIP( ip );
// Create the mount point:
TQDir *dir = new TQDir( 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( TQDir::cleanDirPath( dir->path() ) );
delete dir;
// Now we are prepared to mount the share:
TQString 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( TQString( "//%1/%2" ).arg( m_priv->host(), m_priv->share() ), true );
// Determine the file system we have to use:
int filesystem;
if ( info )
{
filesystem = TQString::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
TQString( "%1 smb4k_mount -s -t cifs " ).arg( suid_program ) :
"smb4k_mount -n -t cifs " );
command.append( "-o " );
command.append( optionsHandler()->mountOptions( TQString( "//%1/%2" ).arg( m_priv->host(), m_priv->share() ) ) );
command.append( !m_priv->workgroup().stripWhiteSpace().isEmpty() ?
TQString( "domain='%1'," ).arg( m_priv->workgroup() ) :
"" );
command.append( !m_priv->ip().stripWhiteSpace().isEmpty() ?
TQString( "ip=%1," ).arg( m_priv->ip() ) :
"" );
command.append( !authInfo.user().isEmpty() ?
TQString( "user=%1" ).arg( authInfo.user().data() ) :
"guest" );
command.append( " -- " );
command.append( TQString( "//'%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
TQString( "%1 smb4k_mount -s -t smbfs " ).arg( suid_program ) :
"smb4k_mount -n -t smbfs " );
command.append( "-o " );
command.append( optionsHandler()->mountOptions( TQString( "//%1/%2" ).arg( m_priv->host(), m_priv->share() ) ) );
command.append( !m_priv->workgroup().stripWhiteSpace().isEmpty() ?
TQString( "workgroup='%1'," ).arg( m_priv->workgroup() ) :
"" );
command.append( !m_priv->ip().stripWhiteSpace().isEmpty() ?
TQString( "ip=%1," ).arg( m_priv->ip() ) :
"" );
command.append( !authInfo.user().isEmpty() ?
TQString( "username=%1" ).arg( authInfo.user().data() ) :
"guest" );
command.append( " -- " );
command.append( TQString( "//'%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
TQString( "%1 smb4k_mount " ).arg( suid_program ) :
"smb4k_mount " );
command.append( optionsHandler()->mountOptions( TQString( "//%1/%2" ).arg( m_priv->host(), m_priv->share() ) ) );
command.append( !m_priv->workgroup().stripWhiteSpace().isEmpty() ?
TQString( " -W '%1'" ).arg( m_priv->workgroup() ) :
"" );
command.append( !m_priv->ip().stripWhiteSpace().isEmpty() ?
TQString( " -I %1" ).arg( m_priv->ip() ) :
"" );
command.append( " -N" );
command.append( " -- " );
command.append( TQString( "//%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 TQString( TQString( "%1:%2:%3:%4" ).arg( Unmount )
.arg( share->canonicalPath().data() )
.arg( force, noMessage ) ) );
}
/***************************************************************************
Unmount a share. (Private part)
***************************************************************************/
void Smb4KMounter::unmount( const TQString &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;
TQString path = mountpoint;
m_priv->setPath( path.replace( '\044', "\044" ) );
TQString 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?" ), TQString(), KStdGuiItem::yes(), KStdGuiItem::no(), "Dont Ask Forced", KMessageBox::Notify ) == KMessageBox::Yes )
{
#ifdef __linux__
command.append( TQString( "%1 smb4k_umount -s -l " ).arg( suid_program ) );
#else
#ifdef __FreeBSD__
command.append( TQString( "%1 smb4k_umount " ).arg( suid_program ) );
#else
command.append( TQString( "%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( TQString( "%1 smb4k_umount -s " ).arg( suid_program ) );
#else
command.append( TQString( "%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?" ), TQString(), KStdGuiItem::yes(), KStdGuiItem::no(), "Dont Ask Forced", KMessageBox::Notify ) == KMessageBox::Yes )
{
#ifdef __linux__
command.append( TQString( "%1 smb4k_umount -s -l " ).arg( suid_program ) );
#else
#ifdef __FreeBSD__
command.append( TQString( "%1 smb4k_umount " ).arg( suid_program ) );
#else
command.append( TQString( "%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( TQString( "%1 smb4k_umount -s " ).arg( suid_program ) );
#else
command.append( TQString( "%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( TQString( "-t %1 " ).arg( share->filesystem() ) );
#endif
command.append( TQString( "'%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 TQString( TQString( "%1" ).arg( UnmountAll ) ) );
}
/***************************************************************************
Unmounts all shares at once.
***************************************************************************/
void Smb4KMounter::unmountAll()
{
for ( TQValueListIterator<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 = TQString();
m_state = state;
if ( m_state != Import )
{
TQApplication::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();
TQApplication::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 )
{
TQString name = TQString( "//%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 ( TQString::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 ( TQString::compare( m_priv->filesystem(), "cifs" ) == 0 )
{
// The user name will be send if no login was specified.
TQString 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.
TQString 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.
TQString 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.
TQString 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 )
{
TQString share_string = static_cast<TQString>( m_priv->share() ).replace( "_", " " );
mountShare( m_priv->workgroup(), m_priv->host(), m_priv->ip(), share_string );
}
else
{
TQString name = TQString( "//%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 );
TQString name = TQString( "//%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 );
TQString name = TQString( "//%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 ( tqstrncmp( share->canonicalPath(),
TQDir( Smb4KSettings::mountPrefix() ).canonicalPath().local8Bit(),
TQDir( Smb4KSettings::mountPrefix() ).canonicalPath().local8Bit().length() ) == 0 )
{
TQDir 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, TQString(), strerror( error_number ) );
}
else
{
TQString str = TQString( "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 ( tqstrncmp( share->canonicalPath(),
TQDir( Smb4KSettings::mountPrefix() ).canonicalPath().local8Bit(),
TQDir( Smb4KSettings::mountPrefix() ).canonicalPath().local8Bit().length() ) == 0 )
{
TQDir 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 TQString &name, bool userOnly )
{
TQValueList<Smb4KShare> list = findShareByName( name );
bool mounted = false;
if ( !list.isEmpty() && userOnly )
{
for ( TQValueList<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 TQString &path )
{
if ( path.isEmpty() || m_mounted_shares.isEmpty() )
{
return NULL;
}
Smb4KShare *share = NULL;
for ( TQValueListIterator<Smb4KShare *> it = m_mounted_shares.begin(); it != m_mounted_shares.end(); ++it )
{
if( TQString::compare( path.upper(), TQString(TQString::fromLocal8Bit( (*it)->path(), -1 )).upper() ) == 0 ||
TQString::compare( path.upper(), TQString(TQString::fromLocal8Bit( (*it)->canonicalPath(), -1 )).upper() ) == 0 )
{
share = *it;
break;
}
}
return share;
}
/***************************************************************************
Find the list of mounts of a share
***************************************************************************/
TQValueList<Smb4KShare> Smb4KMounter::findShareByName( const TQString &name )
{
TQValueList<Smb4KShare> list;
if ( name.isEmpty() || m_mounted_shares.isEmpty() )
{
return list; // is empty
}
TQString n = name;
for ( TQValueListIterator<Smb4KShare *> it = m_mounted_shares.begin(); it != m_mounted_shares.end(); ++it )
{
if( TQString::compare( (*it)->name().upper(), name.upper() ) == 0 ||
TQString::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 TQValueList<Smb4KShare *> Smb4KMounter::getBrokenShares()
{
TQValueList<Smb4KShare *> broken_shares;
for ( TQValueListIterator<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( TQTimerEvent * )
{
if ( !m_working && !m_queue.isEmpty() )
{
// Tell the mounter, that it is busy.
m_working = true;
TQString *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 TQString( TQString( "%1:" ).arg( Import ) ) );
m_priv->timerTicks = 0;
}
}
/////////////////////////////////////////////////////////////////////////////
// TQT_SLOT IMPLEMENTATIONS
/////////////////////////////////////////////////////////////////////////////
void Smb4KMounter::slotProcessExited( KProcess * )
{
endProcess();
}
void Smb4KMounter::slotReceivedStdout( KProcess *, char *buf, int len )
{
m_buffer.append( TQString::fromLocal8Bit( buf, len ) );
}
void Smb4KMounter::slotReceivedStderr( KProcess *, char *buf, int len )
{
m_buffer.append( TQString::fromLocal8Bit( buf, len ) );
}
void Smb4KMounter::slotShutdown()
{
// Abort any action:
abort();
// Prepare for shutdown:
if ( Smb4KSettings::remountShares() && !m_mounted_shares.isEmpty() )
{
for ( TQValueList<Smb4KShare *>::ConstIterator it = m_mounted_shares.begin(); it != m_mounted_shares.end(); ++it )
{
optionsHandler()->remount( *it, !(*it)->isForeign() );
}
}
optionsHandler()->sync();
TQDir dir;
dir.cd( Smb4KSettings::mountPrefix() );
TQStringList dirs = dir.entryList( TQDir::Dirs, TQDir::DefaultSort );
TQValueList<Smb4KShare *> broken_shares = getBrokenShares();
for ( TQStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it )
{
if ( TQString::compare( *it, "." ) != 0 && TQString::compare( *it, ".." ) != 0 )
{
bool broken = false;
for ( TQValueListIterator<Smb4KShare *> bs = broken_shares.begin(); bs != broken_shares.end(); ++bs )
{
if ( tqstrncmp( (*bs)->path(),
Smb4KSettings::mountPrefix()+*it,
(Smb4KSettings::mountPrefix()+*it).length() ) == 0 ||
tqstrncmp( (*bs)->canonicalPath(),
Smb4KSettings::mountPrefix()+*it,
(Smb4KSettings::mountPrefix()+*it).length() ) == 0 )
{
broken = true;
break;
}
else
{
continue;
}
}
if ( !broken )
{
dir.cd( *it );
TQStringList subdirs = dir.entryList( TQDir::Dirs, TQDir::DefaultSort );
for ( TQStringList::ConstIterator i = subdirs.begin(); i != subdirs.end(); ++i )
{
if ( TQString::compare( *i, "." ) != 0 && TQString::compare( *i, ".." ) != 0 )
{
dir.rmdir( *i );
}
}
dir.cdUp();
dir.rmdir( *it );
}
}
}
broken_shares.clear();
if ( Smb4KSettings::unmountSharesOnExit() )
{
TQString 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 ( TQValueListIterator<Smb4KShare *> it = m_mounted_shares.begin(); it != m_mounted_shares.end(); ++it )
{
if ( !(*it)->isForeign() )
{
if ( Smb4KSettings::alwaysUseSuperUser() )
{
#ifndef __FreeBSD__
command.append( TQString( "%1 smb4k_umount -s -t %2 " ).arg( suid_program ).arg( (*it)->filesystem() ) );
#else
command.append( TQString( "%1 smb4k_umount " ).arg( suid_program ) );
#endif
command.append( KProcess::quote( (*it)->path() ) );
command.append( " ; " );
}
else
{
#ifndef __FreeBSD__
command.append( TQString( "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"