/***************************************************************************
    smb4ksambaoptionshandler  -  This class handles the Samba options.
                             -------------------
    begin                : So Mai 14 2006
    copyright            : (C) 2006-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 <tqfile.h>
#include <tqtextstream.h>
#include <tqdir.h>

// KDE includes
#include <kstandarddirs.h>
#include <kprocess.h>
#include <kdebug.h>
#include <tdelocale.h>

// system specific includes
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>

// application specific includes
#include "smb4ksambaoptionshandler.h"
#include "smb4kdefs.h"
#include "smb4kerror.h"
#include "smb4tdeglobal.h"
#include "smb4ksambaoptionsinfo.h"
#include "smb4kshare.h"
#include "smb4ksettings.h"

using namespace Smb4TDEGlobal;


Smb4KSambaOptionsHandler::Smb4KSambaOptionsHandler( TQObject *parent, const char *name )
: TQObject( parent, name )
{
  // We need the directory.
  TDEStandardDirs *stddir = new TDEStandardDirs();
  TQString dir = locateLocal( "data", "smb4k", TDEGlobal::instance() );

  if ( !stddir->exists( dir ) )
  {
    stddir->makeDir( dir );
  }

  delete stddir;

  m_wins_server = TQString();
}


Smb4KSambaOptionsHandler::~Smb4KSambaOptionsHandler()
{
  for ( TQValueList<Smb4KSambaOptionsInfo *>::Iterator it = m_list.begin();
        it != m_list.end(); ++it )
  {
    delete *it;
  }

  m_list.clear();
}


const TQValueList<Smb4KSambaOptionsInfo *> &Smb4KSambaOptionsHandler::customOptionsList()
{
  if ( m_list.isEmpty() )
  {
    read_options();
  }

  return m_list;
}


void Smb4KSambaOptionsHandler::read_options()
{
  // Clear the list before filling it (again)
  if ( !m_list.isEmpty() )
  {
    for ( TQValueList<Smb4KSambaOptionsInfo *>::Iterator it = m_list.begin();
          it != m_list.end(); ++it )
    {
      delete *it;
    }

    m_list.clear();
  }

  TQFile file( locateLocal( "data", "smb4k/custom_options", TDEGlobal::instance() ) );

  TQStringList contents;

  if ( file.open( IO_ReadOnly ) )
  {
    TQTextStream ts( &file );
    ts.setEncoding( TQTextStream::Locale );

    contents = TQStringList::split( '\n', ts.read(), true );

    file.close();
  }
  else
  {
    if ( file.exists() )
    {
      Smb4KError::error( ERROR_READING_FILE, file.name() );
    }

    return;
  }

  if ( !contents.isEmpty() )
  {
    for ( TQStringList::ConstIterator it = contents.begin(); it != contents.end(); ++it )
    {
      if ( (*it).startsWith( "[" ) )
      {
        Smb4KSambaOptionsInfo *info = new Smb4KSambaOptionsInfo( (*it).section( "[", 1, -1 ).section( "]", -2, 0 ) );

        for ( TQStringList::ConstIterator i = ++it; i != contents.end(); ++i )
        {
          if ( (*i).startsWith( "remount=" ) )
          {
            bool remount = TQString::compare( (*i).section( "=", 1, 1 ).stripWhiteSpace(), "true" ) == 0 ? true : false;

            info->setRemount( remount );

            continue;
          }
          else if ( (*i).startsWith( "port=" ) )
          {
            int port = (*i).section( "=", 1, 1 ).stripWhiteSpace().toInt();

            info->setPort( port );

            continue;
          }
#ifndef __FreeBSD__
          else if ( (*i).startsWith( "filesystem=" ) )
          {
            info->setFilesystem( (*i).section( "=", 1, 1 ).stripWhiteSpace() );

            continue;
          }
          else if ( (*i).startsWith( "read-write=" ) )   // Deprecated since version 0.9.0
          {
            info->setWriteAccess( TQString::compare( (*i).section( "=", 1, 1 ).stripWhiteSpace(), "true" ) == 0 );

            continue;
          }
          else if ( (*i).startsWith( "write access=" ) )
          {
            info->setWriteAccess( TQString::compare( (*i).section( "=", 1, 1 ).stripWhiteSpace(), "true" ) == 0 );

            continue;
          }
#endif
          else if ( (*i).startsWith( "protocol=" ) )
          {
            info->setProtocol( (*i).section( "=", 1, 1 ).stripWhiteSpace() );

            continue;
          }
          else if ( (*i).startsWith( "kerberos=" ) )
          {
            info->setKerberos( TQString::compare( (*i).section( "=", 1, 1 ).stripWhiteSpace(), "true" ) == 0 );

            continue;
          }
          else if ( (*i).startsWith( "uid=" ) )
          {
            info->setUID( (*i).section( "=", 1, 1 ).stripWhiteSpace() );

            continue;
          }
          else if ( (*i).startsWith( "gid=" ) )
          {
            info->setGID( (*i).section( "=", 1, 1 ).stripWhiteSpace() );

            continue;
          }
          else if ( (*i).isEmpty() || (*i).stripWhiteSpace().startsWith( "[" ) )
          {
            it = i;

            break;
          }
          else
          {
            continue;
          }
        }

        m_list.append( info );
      }
      else
      {
        continue;
      }
    }
  }
}


void Smb4KSambaOptionsHandler::write_options()
{
  TQString protocol_hint;

  // Determine the protocol hint specified by the user:
  switch ( Smb4KSettings::protocolHint() )
  {
    case Smb4KSettings::EnumProtocolHint::Automatic:
    {
      // In this case the user leaves it to the net
      // command to determine the right protocol.
      protocol_hint = TQString();

      break;
    }
    case Smb4KSettings::EnumProtocolHint::RPC:
    {
      protocol_hint = "rpc";

      break;
    }
    case Smb4KSettings::EnumProtocolHint::RAP:
    {
      protocol_hint = "rap";

      break;
    }
    case Smb4KSettings::EnumProtocolHint::ADS:
    {
      protocol_hint = "ads";

      break;
    }
    default:
    {
      protocol_hint = TQString();

      break;
    }
  }

#ifndef __FreeBSD__

  TQString default_filesystem;

  switch( Smb4KSettings::filesystem() )
  {
    case Smb4KSettings::EnumFilesystem::CIFS:
    {
      default_filesystem = "cifs";

      break;
    }
    case Smb4KSettings::EnumFilesystem::SMBFS:
    {
      default_filesystem = "smbfs";

      break;
    }
    default:
    {
      // FIXME: Set default_filesystem to "cifs"?
      break;
    }
  }

  bool write_access = true;

  switch( Smb4KSettings::writeAccess() )
  {
    case Smb4KSettings::EnumWriteAccess::ReadWrite:
    {
      write_access = true;

      break;
    }
    case Smb4KSettings::EnumWriteAccess::ReadOnly:
    {
      write_access = false;

      break;
    }
    default:
    {
      break;
    }
  }

#endif

  TQFile file( locateLocal( "data", "smb4k/custom_options", TDEGlobal::instance() ) );

  if ( !m_list.isEmpty() )
  {
    if ( file.open( IO_WriteOnly ) )
    {
      TQTextStream ts( &file );
      ts.setEncoding( TQTextStream::Locale );

      for ( TQValueList<Smb4KSambaOptionsInfo *>::ConstIterator it = m_list.begin(); it != m_list.end(); ++it )
      {
        switch ( (*it)->type() )
        {
          case Smb4KSambaOptionsInfo::Host:
          {
            // Check if we need to write anything:
            if ( (*it)->port() != -1 ||
                 !(*it)->protocol().stripWhiteSpace().isEmpty() ||
                 ((*it)->kerberos() != Smb4KSettings::useKerberos()) )
            {
              ts << "[" << (*it)->itemName() << "]" << endl;

              ts << "port="     << ((*it)->port() != -1 ? (*it)->port() :
                                   Smb4KSettings::remotePort()) << endl;

              ts << "kerberos=" << ((*it)->kerberos() ? "true" : "false") << endl;

              ts << "protocol=" << (!(*it)->protocol().stripWhiteSpace().isEmpty() ?
                                   (*it)->protocol() : protocol_hint) << endl;
            }
            else
            {
              // Do nothing
            }

            break;
          }
          case Smb4KSambaOptionsInfo::Share:
          {
            if ( (*it)->port() != -1 ||
                 (*it)->remount() ||
#ifndef __FreeBSD__
                 (*it)->kerberos() != Smb4KSettings::useKerberos() ||
                 !(*it)->filesystem().isEmpty() ||
                 (*it)->writeAccess() != write_access ||
#endif
                 !(*it)->uid().isEmpty() ||
                 !(*it)->gid().isEmpty() )
            {
              ts << "[" << (*it)->itemName() << "]" << endl;

              ts << "port="         << ((*it)->port() != -1 ? (*it)->port() :
                                       Smb4KSettings::remotePort()) << endl;

              ts << "remount="      << ((*it)->remount() ? "true" : "false") << endl;
#ifndef __FreeBSD__
              // FreeBSD does not know Kerberos for mounting:
              ts << "kerberos="     << ((*it)->kerberos() ? "true" : "false") << endl;

              ts << "filesystem="   << (!(*it)->filesystem().stripWhiteSpace().isEmpty() ?
                                       (*it)->filesystem() : default_filesystem) << endl;

              ts << "write access=" << ((*it)->writeAccess() ? "true" : "false") << endl;
#endif
              ts << "uid=" << (!(*it)->uid().stripWhiteSpace().isEmpty() ?
                              (*it)->uid() : Smb4KSettings::userID()) << endl;

              ts << "gid=" << (!(*it)->gid().stripWhiteSpace().isEmpty() ?
                              (*it)->gid() : Smb4KSettings::groupID()) << endl;
            }
            else
            {
              // Do nothing
            }

            break;
          }
          default:
          {
            break;
          }
        }

        ts << endl;
      }

      file.close();
    }
  }
  else
  {
    file.remove();
  }
}


void Smb4KSambaOptionsHandler::remount( Smb4KShare *share, bool yes )
{
  if ( share )
  {
    Smb4KSambaOptionsInfo *info = NULL;

    if ( (info = find_item( share->name() )) )
    {
      info->setRemount( yes );
    }
    else if ( !info && yes )
    {
      info = new Smb4KSambaOptionsInfo( share );
      info->setRemount( yes );

      m_list.append( info );
    }
  }
}


void Smb4KSambaOptionsHandler::sync()
{
  write_options();
}


Smb4KSambaOptionsInfo *Smb4KSambaOptionsHandler::find_item( const TQString &item, bool exactMatch )
{
  // If the list is empty, we'll read the file.
  if ( m_list.isEmpty() )
  {
    read_options();
  }

  TQString host = item.section( "/", 2, 2 ).stripWhiteSpace();

  Smb4KSambaOptionsInfo *info = NULL;

  if ( !item.stripWhiteSpace().isEmpty() )
  {
    for ( TQValueList<Smb4KSambaOptionsInfo *>::ConstIterator it = m_list.begin();
          it != m_list.end(); ++it )
    {
      if ( TQString::compare( item.lower(), (*it)->itemName().lower() ) == 0 )
      {
        info = *it;

        break;
      }
      else if ( TQString::compare( host.lower(), (*it)->itemName().lower() ) == 0 )
      {
        if ( !info && !exactMatch )
        {
          info = *it;
        }

        continue;
      }
      else
      {
        continue;
      }
    }
  }

  return info;
}


const TQString Smb4KSambaOptionsHandler::smbclientOptions( const TQString &share )
{
  // Get the global Samba options
  (void) globalSambaOptions();

  Smb4KSambaOptionsInfo *info = find_item( share );
  TQString args = TQString();

  // Get the strings that are needed to put the
  // argument list together:
  TQString resolve_order =  (!Smb4KSettings::nameResolveOrder().isEmpty() &&
                           TQString::compare( Smb4KSettings::nameResolveOrder(),
                           m_samba_options["name resolve order"] ) != 0) ?
                           Smb4KSettings::nameResolveOrder() :
                           TQString();

  TQString netbios_name =   (!Smb4KSettings::netBIOSName().isEmpty() &&
                           TQString::compare( Smb4KSettings::netBIOSName(),
                           m_samba_options["netbios name"] ) != 0) ?
                           Smb4KSettings::netBIOSName() :
                           TQString();

  TQString netbios_scope =  (!Smb4KSettings::netBIOSScope().isEmpty() &&
                           TQString::compare( Smb4KSettings::netBIOSScope(),
                           m_samba_options["netbios scope"] ) != 0) ?
                           Smb4KSettings::netBIOSScope() :
                           TQString();

  TQString socket_options = (!Smb4KSettings::socketOptions().isEmpty() &&
                           TQString::compare( Smb4KSettings::socketOptions(),
                           m_samba_options["socket options"] ) != 0) ?
                           Smb4KSettings::socketOptions() :
                           TQString();

  bool kerberos =          info ?
                           info->kerberos() :
                           Smb4KSettings::useKerberos();

  int port =               info && info->port() != -1 ?
                           info->port() :
                           Smb4KSettings::remotePort();

  // Options that are not customizable:
  args.append( !resolve_order.isEmpty() ?
               TQString( " -R '%1'" ).arg( resolve_order ) :
               TQString() );

  args.append( !netbios_name.isEmpty() ?
               TQString( " -n '%1'" ).arg( netbios_name ) :
               TQString() );

  args.append( !netbios_scope.isEmpty() ?
               TQString( " -i '%1'" ).arg( netbios_scope ) :
               TQString() );

  args.append( !socket_options.isEmpty() ?
               TQString( " -O '%1'" ).arg( socket_options ) :
               TQString() );

  args.append( Smb4KSettings::bufferSize() != 65520 ?
               TQString( " -b %1" ).arg( Smb4KSettings::bufferSize() ) :
               TQString() );

  args.append( Smb4KSettings::machineAccount() ? " -P" : TQString() );

  switch ( Smb4KSettings::signingState() )
  {
    case Smb4KSettings::EnumSigningState::None:
    {
      // The user does not want this setting
      // to be used.
      break;
    }
    case Smb4KSettings::EnumSigningState::On:
    {
      args.append( " -S on" );

      break;
    }
    case Smb4KSettings::EnumSigningState::Off:
    {
      args.append( " -S off" );

      break;
    }
    case Smb4KSettings::EnumSigningState::Required:
    {
      args.append( " -S required" );

      break;
    }
    default:
    {
      break;
    }
  }

  args.append( kerberos ? " -k" : TQString() );

  args.append( TQString( " -p %1" ).arg( port ) );

  return args;
}


const TQString Smb4KSambaOptionsHandler::nmblookupOptions( bool with_broadcast )
{
  // Get the global Samba options
  (void) globalSambaOptions();

  TQString args = TQString();

  TQString netbios_name =   (!Smb4KSettings::netBIOSName().isEmpty() &&
                           TQString::compare( Smb4KSettings::netBIOSName(),
                           m_samba_options["netbios name"] ) != 0) ?
                           Smb4KSettings::netBIOSName() :
                           TQString();

  TQString netbios_scope =  (!Smb4KSettings::netBIOSScope().isEmpty() &&
                           TQString::compare( Smb4KSettings::netBIOSScope(),
                           m_samba_options["netbios scope"] ) != 0) ?
                           Smb4KSettings::netBIOSScope() :
                           TQString();

  TQString socket_options = (!Smb4KSettings::socketOptions().isEmpty() &&
                           TQString::compare( Smb4KSettings::socketOptions(),
                           m_samba_options["socket options"] ) != 0) ?
                           Smb4KSettings::socketOptions() :
                           TQString();

  TQString domain =         (!Smb4KSettings::domainName().isEmpty() &&
                           TQString::compare( Smb4KSettings::domainName(),
                           m_samba_options["workgroup"] ) != 0) ?
                           Smb4KSettings::domainName() :
                           TQString();

  args.append( !netbios_name.isEmpty() ?
               TQString( " -n '%1'" ).arg( netbios_name ) :
               TQString() );

  args.append( !netbios_scope.isEmpty() ?
               TQString( " -i '%1'" ).arg( netbios_scope ) :
               TQString() );

  args.append( !socket_options.isEmpty() ?
               TQString( " -O '%1'" ).arg( socket_options ) :
               TQString() );

  args.append( !domain.isEmpty() ?
               TQString( " -W '%1'" ).arg( domain ) :
               TQString() );

  args.append( (!Smb4KSettings::broadcastAddress().isEmpty() &&
               with_broadcast) ?
               TQString( " -B %1" ).arg( Smb4KSettings::broadcastAddress() ) :
               TQString() );

  args.append( Smb4KSettings::usePort137() ?
               " -r" :
               TQString() );

  return args;
}


const TQString Smb4KSambaOptionsHandler::netOptions( int command, const TQString &networkItem, const TQString &protocol )
{
  TQString args = TQString();

  Smb4KSambaOptionsInfo *info = find_item( networkItem );

  TQString protocol_hint;

  // Determine the protocol hint specified by the user:
  switch ( Smb4KSettings::protocolHint() )
  {
    case Smb4KSettings::EnumProtocolHint::Automatic:
    {
      // In this case the user leaves it to the net
      // command to determine the right protocol.
      protocol_hint = TQString();

      break;
    }
    case Smb4KSettings::EnumProtocolHint::RPC:
    {
      protocol_hint = "rpc";

      break;
    }
    case Smb4KSettings::EnumProtocolHint::RAP:
    {
      protocol_hint = "rap";

      break;
    }
    case Smb4KSettings::EnumProtocolHint::ADS:
    {
      protocol_hint = "ads";

      break;
    }
    default:
    {
      protocol_hint = TQString();

      break;
    }
  }

  TQString default_protocol = (info && !info->protocol().isEmpty()) ?
                             info->protocol() :
                             protocol_hint;

  TQString netbios_name =     (!Smb4KSettings::netBIOSName().isEmpty() &&
                             TQString::compare( Smb4KSettings::netBIOSName(),
                             m_samba_options["netbios name"] ) != 0) ?
                             Smb4KSettings::netBIOSName() :
                             TQString();

  TQString domain =           (!Smb4KSettings::domainName().isEmpty() &&
                             TQString::compare( Smb4KSettings::domainName(),
                             m_samba_options["workgroup"] ) != 0) ?
                             Smb4KSettings::domainName() :
                             TQString();

  int port =                 (info && info->port() != -1) ?
                             info->port() :
                             Smb4KSettings::remotePort();

  // Add command specific arguments:
  switch ( command )
  {
    case Share:
    {
      // We can only use the RAP or RPC protocol here.
      if ( !protocol.stripWhiteSpace().isEmpty() )
      {
        // Protocol can only be defined by us developers,
        // and we should know what we are doing. So, no
        // checks for the right protocol here:
        args.append( TQString( " %1" ).arg( protocol ) );
      }
      else
      {
        args.append( TQString( " %1" ).arg( TQString::compare( default_protocol, "ads" ) != 0 ?
                                           default_protocol :
                                           TQString() /* FIXME: Is that the best way how to do it? */) );
      }

      args.append( " share -l" );

      break;
    }
    case ServerDomain:
    {
      // NOTE: Since version 3.0.25, the command 'net rap server domain ...'
      // will through an error. We have to use 'net rap server ...'. This is
      // also compatible with earlier version.

      // This only works with the rap protocol:
      args.append( " rap server" );

      break;
    }
    case LookupHost:
    {
      // Check that the server name is present:
      if ( networkItem.stripWhiteSpace().isEmpty() )
      {
        Smb4KError::error( ERROR_NET_COMMAND, args.stripWhiteSpace() );

        return args; // still empty
      }

      // This lookup command takes no protocol:
      args.append( TQString( " lookup host %1" ).arg( networkItem ) );

      break;
    }
    case LookupMaster:
    {
      // Check that the domain name is present:
      if ( networkItem.stripWhiteSpace().isEmpty() )
      {
        Smb4KError::error( ERROR_NET_COMMAND, args.stripWhiteSpace() );

        return args; // still empty
      }

      // This lookup command takes no protocol:
      args.append( TQString( " lookup master '%1'" ).arg( networkItem ) );

      break;
    }
    case Domain:
    {
      // This only works with the rap protocol:
      args.append( " rap domain" );

      break;
    }
    default:
    {
      // Bypass the rest and return an
      // empty string:
      return args;
    }
  }

  args.append( !domain.isEmpty() ?
               TQString( " -W '%1'" ).arg( domain ) :
               TQString() );

  args.append( !netbios_name.isEmpty() ?
               TQString( " -n '%1'" ).arg( netbios_name ) :
               TQString() );

  args.append( Smb4KSettings::machineAccount() ?
               " -P" :
               TQString() );

  args.append( TQString( " -p %1" ).arg( port ) );

  return args;
}


const TQString Smb4KSambaOptionsHandler::mountOptions( const TQString &share )
{
  Smb4KSambaOptionsInfo *info = find_item( share );
  TQString args;

  // Read the global Samba options from smb.conf:
  (void) globalSambaOptions();

  // Get the strings needed to put the argument list together:
  TQString uid =            (info && !info->uid().isEmpty()) ?
                           info->uid() :
                           Smb4KSettings::userID();

  TQString gid =            (info && !info->gid().isEmpty()) ?
                           info->gid() :
                           Smb4KSettings::groupID();

  TQString charset, codepage;

  switch ( Smb4KSettings::clientCharset() )
  {
    case Smb4KSettings::EnumClientCharset::default_charset:
    {
      charset = m_samba_options["unix charset"].lower();  // maybe empty

      break;
    }
    case Smb4KSettings::EnumClientCharset::iso8859_1:
    {
      charset = "iso8859-1";

      break;
    }
    case Smb4KSettings::EnumClientCharset::iso8859_2:
    {
      charset = "iso8859-2";

      break;
    }
    case Smb4KSettings::EnumClientCharset::iso8859_3:
    {
      charset = "iso8859-3";

      break;
    }
    case Smb4KSettings::EnumClientCharset::iso8859_4:
    {
      charset = "iso8859-4";

      break;
    }
    case Smb4KSettings::EnumClientCharset::iso8859_5:
    {
      charset = "iso8859-5";

      break;
    }
    case Smb4KSettings::EnumClientCharset::iso8859_6:
    {
      charset = "iso8859-6";

      break;
    }
    case Smb4KSettings::EnumClientCharset::iso8859_7:
    {
      charset = "iso8859-7";

      break;
    }
    case Smb4KSettings::EnumClientCharset::iso8859_8:
    {
      charset = "iso8859-8";

      break;
    }
    case Smb4KSettings::EnumClientCharset::iso8859_9:
    {
      charset = "iso8859-9";

      break;
    }
    case Smb4KSettings::EnumClientCharset::iso8859_13:
    {
      charset = "iso8859-13";

      break;
    }
    case Smb4KSettings::EnumClientCharset::iso8859_14:
    {
      charset = "iso8859-14";

      break;
    }
    case Smb4KSettings::EnumClientCharset::iso8859_15:
    {
      charset = "iso8859-15";

      break;
    }
    case Smb4KSettings::EnumClientCharset::utf8:
    {
      charset = "utf8";

      break;
    }
    case Smb4KSettings::EnumClientCharset::koi8_r:
    {
      charset = "koi8-r";

      break;
    }
    case Smb4KSettings::EnumClientCharset::koi8_u:
    {
      charset = "koi8-u";

      break;
    }
    case Smb4KSettings::EnumClientCharset::koi8_ru:
    {
      charset = "koi8-ru";

      break;
    }
    case Smb4KSettings::EnumClientCharset::cp1251:
    {
      charset = "cp1251";

      break;
    }
    case Smb4KSettings::EnumClientCharset::gb2312:
    {
      charset = "gb2312";

      break;
    }
    case Smb4KSettings::EnumClientCharset::big5:
    {
      charset = "big5";

      break;
    }
    case Smb4KSettings::EnumClientCharset::euc_jp:
    {
      charset = "euc-jp";

      break;
    }
    case Smb4KSettings::EnumClientCharset::euc_kr:
    {
      charset = "euc-kr";

      break;
    }
    case Smb4KSettings::EnumClientCharset::tis_620:
    {
      charset = "tis-620";

      break;
    }
    default:
    {
      break;
    }
  }

  switch ( Smb4KSettings::serverCodepage() )
  {
    case Smb4KSettings::EnumServerCodepage::default_codepage:
    {
      codepage = m_samba_options["dos charset"].lower();  // maybe empty

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp437:
    {
      codepage = "cp437";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp720:
    {
      codepage = "cp720";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp737:
    {
      codepage = "cp737";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp775:
    {
      codepage = "cp775";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp850:
    {
      codepage = "cp850";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp852:
    {
      codepage = "cp852";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp855:
    {
      codepage = "cp855";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp857:
    {
      codepage = "cp857";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp858:
    {
      codepage = "cp858";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp860:
    {
      codepage = "cp860";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp861:
    {
      codepage = "cp861";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp862:
    {
      codepage = "cp862";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp863:
    {
      codepage = "cp863";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp864:
    {
      codepage = "cp864";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp865:
    {
      codepage = "cp865";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp866:
    {
      codepage = "cp866";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp869:
    {
      codepage = "cp869";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp874:
    {
      codepage = "cp874";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp932:
    {
      codepage = "cp932";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp936:
    {
      codepage = "cp936";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp949:
    {
      codepage = "cp949";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp950:
    {
      codepage = "cp950";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp1250:
    {
      codepage = "cp1250";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp1251:
    {
      codepage = "cp1251";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp1252:
    {
      codepage = "cp1252";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp1253:
    {
      codepage = "cp1253";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp1254:
    {
      codepage = "cp1254";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp1255:
    {
      codepage = "cp1255";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp1256:
    {
      codepage = "cp1256";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp1257:
    {
      codepage = "cp1257";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::cp1258:
    {
      codepage = "cp1258";

      break;
    }
    case Smb4KSettings::EnumServerCodepage::unicode:
    {
      codepage = "unicode";

      break;
    }
    default:
    {
      break;
    }
  }

#ifndef __FreeBSD__

  TQString netbios_name =   !Smb4KSettings::netBIOSName().isEmpty() ?
                           Smb4KSettings::netBIOSName() :
                           m_samba_options["netbios name"];

  TQString socket_options = (!Smb4KSettings::socketOptions().isEmpty() &&
                           TQString::compare( Smb4KSettings::socketOptions(),
                           m_samba_options["socket options"] ) != 0) ?
                           Smb4KSettings::socketOptions() :
                           TQString();

  TQString netbios_scope =  (!Smb4KSettings::netBIOSScope().isEmpty() &&
                           TQString::compare( Smb4KSettings::netBIOSScope(),
                           m_samba_options["netbios scope"] ) != 0) ?
                           Smb4KSettings::netBIOSScope() :
                           TQString();

  int port =               info && info->port() != -1 ?
                           info->port() :
                           Smb4KSettings::remotePort();

  bool kerberos =          info ?
                           info->kerberos() :
                           Smb4KSettings::useKerberos();

  bool read_write =        info ?
                           info->writeAccess() :
                           (Smb4KSettings::writeAccess() == Smb4KSettings::EnumWriteAccess::ReadWrite);

  // Compile the arguments list:
  args.append( !netbios_name.isEmpty() ?
               TQString( "netbiosname='%1'," ).arg( netbios_name ) :
               TQString() );

  args.append( !uid.isEmpty() ?
               TQString( "uid=%1," ).arg( uid ) :
               TQString() );

  args.append( !gid.isEmpty() ?
               TQString( "gid=%1," ).arg( gid ) :
               TQString() );

  args.append( TQString( "port=%1," ).arg( port ) );

  args.append( !charset.isEmpty() ?
               TQString( "iocharset=%1," ).arg( charset ) :
               TQString() );

  args.append( read_write ? "rw," : "ro," );

  switch ( Smb4KSettings::filesystem() )
  {
    case Smb4KSettings::EnumFilesystem::CIFS:
    {
      args.append( !Smb4KSettings::fileMask().isEmpty() ?
                   TQString( "file_mode=%1," ).arg( Smb4KSettings::fileMask() ) :
                   TQString() );

      args.append( !Smb4KSettings::directoryMask().isEmpty() ?
                   TQString( "dir_mode=%1," ).arg( Smb4KSettings::directoryMask() ) :
                   TQString() );

      args.append( Smb4KSettings::permissionChecks() ?
                   "perm," :
                   "noperm," );

      args.append( Smb4KSettings::clientControlsIDs() ?
                   "setuids," :
                   "nosetuids," );

      args.append( Smb4KSettings::serverInodeNumbers() ?
                   "serverino," :
                   "noserverino," );

      args.append( Smb4KSettings::inodeDataCaching() ?
                   "directio," :
                   TQString() );        // FIXME: Does 'nodirectio' exist?

      args.append( Smb4KSettings::translateReservedChars() ?
                   "mapchars," :
                   "nomapchars," );

      args.append( Smb4KSettings::noLocking() ?
                   "nolock," :
                   TQString() );        // FIXME: Does 'lock' exist?

      args.append( !Smb4KSettings::customCIFSOptions().isEmpty() ?
                   Smb4KSettings::customCIFSOptions() :
                   TQString() );

      break;
    }
    case Smb4KSettings::EnumFilesystem::SMBFS:
    {
      args.append( !socket_options.isEmpty() ?
                   TQString( "sockopt='%1'," ).arg( socket_options ) :
                   TQString() );

      args.append( !netbios_scope.isEmpty() ?
                   TQString( "scope=%1," ).arg( netbios_scope ) :
                   TQString() );

      args.append( !codepage.isEmpty() ?
                   TQString( "codepage=%1," ).arg( codepage ) :
                   TQString() );

      args.append( !Smb4KSettings::fileMask().isEmpty() ?
                   TQString( "fmask=%1," ).arg( Smb4KSettings::fileMask() ) :
                   TQString() );

      args.append( !Smb4KSettings::directoryMask().isEmpty() ?
                   TQString( "dmask=%1," ).arg( Smb4KSettings::directoryMask() ) :
                   TQString() );

      args.append( kerberos ? "krb," : TQString() );

      args.append( Smb4KSettings::cachingTime() != 1000 ?
                   TQString( "ttl=%1," ).arg( Smb4KSettings::cachingTime() ) :
                   TQString() );

      args.append( Smb4KSettings::unicodeSupport() ?
                   "unicode," :
                   TQString() );

      args.append( Smb4KSettings::largeFileSystemSupport() ?
                   "lfs," :
                   TQString() );

      break;
    }
    default:
    {
      break;
    }
  }

#else

  // Compile the arguments list:
  args.append( !uid.isEmpty() ?
               TQString( " -u %1" ).arg( uid ) :
               TQString() );

  args.append( !gid.isEmpty() ?
               TQString( " -g %1" ).arg( gid ) :
               TQString() );

  args.append( !charset.isEmpty() && !codepage.isEmpty() ?
               TQString( " -E %1:%2" ).arg( charset, codepage ) :
               TQString() );

  args.append( !Smb4KSettings::fileMask().isEmpty() ?
               TQString( " -f %1" ).arg( Smb4KSettings::fileMask() ) :
               TQString() );

  args.append( !Smb4KSettings::directoryMask().isEmpty() ?
               TQString( " -d %1" ).arg( Smb4KSettings::directoryMask() ) :
               TQString() );

  // NOTE: Under FreeBSD the port must be managed by the mounter.

  // FIXME: If the manual page was of more use, we could probably implement
  // more of the arguments that are available for mount_smbfs.

#endif

  return args;
}


void Smb4KSambaOptionsHandler::read_smb_conf()
{
  // Clear the options list before reading.
  m_samba_options.clear();

  TQStringList paths;
  paths << "/etc";
  paths << "/etc/samba";
  paths << "/usr/local/etc";
  paths << "/usr/local/etc/samba";

  TQFile f( "smb.conf" );

  TQStringList contents;

  // Locate the file and read its contents:
  for ( TQStringList::Iterator it = paths.begin(); it != paths.end(); ++it )
  {
    TQDir::setCurrent( *it );

    if ( f.exists() )
    {
      if ( f.open( IO_ReadOnly ) )
      {
        TQTextStream ts( &f );
        ts.setEncoding( TQTextStream::Locale );

        contents = TQStringList::split( '\n', ts.read(), false );
      }

      f.close();

      break;
    }
    else
    {
      continue;
    }
  }

  // Process the file contents.
  for ( TQStringList::Iterator it = contents.erase( contents.begin(), ++(contents.find( "[global]" )) ); it != contents.end(); ++it )
  {
    if ( (*it).stripWhiteSpace().startsWith( "#" ) || (*it).stripWhiteSpace().startsWith( ";" ) )
    {
      *it = TQString();
    }
    else if ( (*it).stripWhiteSpace().startsWith( "include" ) )
    {
      // Put the contents of the included at this position.
      TQString file = (*it).section( "=", 1, 1 ).stripWhiteSpace();
      *it = TQString();
      f.setName( file );

      TQStringList include;

      if ( f.exists() )
      {
        if ( f.open( IO_ReadOnly ) )
        {
          TQTextStream ts( &f );
          ts.setEncoding( TQTextStream::Locale );

          include = TQStringList::split( '\n', ts.read(), false );
        }

        f.close();
      }

      for ( TQStringList::Iterator i = include.begin(); i != include.end(); ++i )
      {
        if ( !(*i).stripWhiteSpace().isEmpty() )
        {
          contents.insert( it, *i );

          continue;
        }
        else
        {
          continue;
        }
      }

      continue;
    }
    else if ( (*it).startsWith( "[" ) )
    {
      contents.erase( it, contents.end() );

      break;
    }
    else
    {
      continue;
    }
  }

  contents.remove( TQString() );

  // Write all options into the map:
  for ( TQStringList::ConstIterator it = contents.begin(); it != contents.end(); ++it )
  {
    TQString key = (*it).section( "=", 0, 0 ).stripWhiteSpace().lower();
    m_samba_options[key] = TQString( (*it).section( "=", 1, 1 ).stripWhiteSpace().upper() );
  }

  // Post-processing. Some values should be entered with their defaults, if they are
  // not already present.
  if ( !m_samba_options.contains( "netbios name" ) )
  {
    size_t hostnamelen = 255;
    char *hostname = new char[hostnamelen];

    if ( gethostname( hostname, hostnamelen ) == -1 )
    {
      int error = errno;
      Smb4KError::error( ERROR_GETTING_HOSTNAME, TQString(), strerror( error ) );
    }
    else
    {
      m_samba_options["netbios name"] = ( TQString( "%1" ).arg( hostname ) ).upper();
    }

    delete [] hostname;
  }
}


const TQMap<TQString,TQString> &Smb4KSambaOptionsHandler::globalSambaOptions()
{
  if ( m_samba_options.isEmpty() )
  {
    read_smb_conf();
  }

  return m_samba_options;
}


const TQString &Smb4KSambaOptionsHandler::winsServer()
{
  if ( m_wins_server.isEmpty() )
  {
    (void) globalSambaOptions();

    if ( !m_samba_options["wins server"].isEmpty() )
    {
      m_wins_server = m_samba_options["wins server"];
    }
    else if ( !m_samba_options["wins support"].isEmpty() &&
              (TQString::compare( m_samba_options["wins support"].lower(), "yes" ) == 0 ||
              TQString::compare( m_samba_options["wins support"].lower(), "true" ) == 0) )
    {
      m_wins_server = "127.0.0.1";
    }
  }

  return m_wins_server;
}


void Smb4KSambaOptionsHandler::addItem( Smb4KSambaOptionsInfo *info, bool s )
{
  Smb4KSambaOptionsInfo *item = find_item( info->itemName() );

  if ( item && TQString::compare( item->itemName().lower(), info->itemName().lower() ) == 0 )
  {
    item->setPort( info->port() );
#ifndef __FreeBSD__
    item->setFilesystem( info->filesystem() );
    item->setWriteAccess( info->writeAccess() );
#endif
    item->setRemount( info->remount() );
    item->setProtocol( info->protocol() );
    item->setKerberos( info->kerberos() );
    item->setUID( info->uid() );
    item->setGID( info->gid() );

    delete info;
  }
  else
  {
    m_list.append( info );
  }

  if ( s )
  {
    sync();
  }
}


void Smb4KSambaOptionsHandler::removeItem( const TQString &name, bool s )
{
  Smb4KSambaOptionsInfo *item = find_item( name );

  if ( item && TQString::compare( item->itemName().lower(), name.lower() ) == 0 )
  {
    m_list.remove( item );
    delete item;
  }

  if ( s )
  {
    sync();
  }
}

#include "smb4ksambaoptionshandler.moc"
