/***************************************************************************
    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 <tdeapplication.h>
#include <tdelocale.h>
#include <tdemessagebox.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 "smb4tdeglobal.h"
#include "smb4ksambaoptionshandler.h"
#include "smb4kpasswordhandler.h"
#include "smb4kshare.h"
#include "smb4ksettings.h"

using namespace Smb4TDEGlobal;


Smb4KMounter::Smb4KMounter( TQObject *parent, const char *name ) : TQObject( parent, name )
{
  m_priv = new Smb4KMounterPrivate;

  m_proc = new TDEProcess( this, "MounterProcess" );
  m_proc->setUseShell( true );

  m_working = false;

  m_queue.setAutoDelete( true );

  connect( m_proc,  TQT_SIGNAL( processExited( TDEProcess * ) ),
           this,    TQT_SLOT( slotProcessExited( TDEProcess * ) ) );

  connect( m_proc,  TQT_SIGNAL( receivedStdout( TDEProcess *, char *, int ) ),
           this,    TQT_SLOT( slotReceivedStdout( TDEProcess *, char *, int ) ) );

  connect( m_proc,  TQT_SIGNAL( receivedStderr( TDEProcess *, char *, int ) ),
           this,    TQT_SLOT( slotReceivedStderr( TDEProcess *, 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;
        }
      }

      TDEProcess proc;
      proc.setUseShell( true );
      proc << TQString( "%1 smb4k_kill %2" ).arg( suid_program ).arg( m_proc->pid() );
      proc.start( TDEProcess::DontCare, TDEProcess::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( TDEProcess::NotifyOnExit, TDEProcess::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( TDEProcess * )
{
  endProcess();
}


void Smb4KMounter::slotReceivedStdout( TDEProcess *, char *buf, int len )
{
  m_buffer.append( TQString::fromLocal8Bit( buf, len ) );
}


void Smb4KMounter::slotReceivedStderr( TDEProcess *, 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;
      }
    }

    TDEProcess 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( TDEProcess::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( TDEProcess::quote( (*it)->path() ) );
          command.append( " ; " );
        }

        dir.setPath( (*it)->canonicalPath() );

#ifndef __FreeBSD__
        command.append( "rmdir --ignore-fail-on-non-empty " );
        command.append( TDEProcess::quote( dir.canonicalPath() ) );
        command.append( " ; " );
        command.append( "rmdir --ignore-fail-on-non-empty " );
        dir.cdUp();
        command.append( TDEProcess::quote( dir.canonicalPath() ) );
        command.append( " ; " );
#else
        command.append( "rmdir " );
        command.append( TDEProcess::quote( dir.canonicalPath() ) );
        command.append( " ; " );
        command.append( "rmdir " );
        dir.cdUp();
        command.append( TDEProcess::quote( dir.canonicalPath() ) );
        command.append( " ; " );
#endif
      }
      else
      {
        continue;
      }
    }

    command.truncate( command.length() - 2 );

    proc << command;
    proc.start( TDEProcess::DontCare, TDEProcess::NoCommunication );
  }
}


#include "smb4kmounter.moc"
