/***************************************************************************
    smb4kshare  -  Smb4K's container class for information about a share.
                             -------------------
    begin                : Mo Jan 28 2008
    copyright            : (C) 2008 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., 59 Temple Place, Suite 330, Boston,   *
 *   MA  02111-1307 USA                                                    *
 ***************************************************************************/

// Qt include
#include <QHostAddress>
#include <QAbstractSocket>
#include <QDir>

// KDE includes
#include <klocale.h>
#include <kdebug.h>

// system includes
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <math.h>

// application specific includes
#include <smb4kshare.h>

Smb4KShare::Smb4KShare( const QString &host, const QString &name )
#ifndef __FreeBSD__
: m_name( name ), m_host( host ), m_unc( QString() ), m_workgroup( QString() ),
  m_type( QString() ), m_comment( QString() ), m_host_ip( QString() ), m_path( QByteArray() ),
  m_inaccessible( false ), m_foreign( false ), m_filesystem( Unknown ), m_user( getuid() ),
  m_group( getgid() ), m_login( getpwuid( getuid() )->pw_name ), m_total( -1 ), m_free( -1 ),
  m_uid_set( false ), m_gid_set( false ), m_login_set( false ), m_is_mounted( false ),
  m_homes_share( false ), m_homes_users( QStringList() )
{
  m_unc = "//"+m_host+"/"+m_name;
#else
: m_name( name ), m_host( host ), m_unc( QString() ), m_workgroup( QString() ),
  m_type( QString() ), m_comment( QString() ), m_host_ip( QString() ), m_path( QByteArray() ),
  m_inaccessible( false ), m_foreign( false ), m_filesystem( Unknown ), m_user( getuid() ),
  m_group( getgid() ), m_login( "guest" ), m_total( -1 ), m_free( -1 ), m_uid_set( false ),
  m_gid_set( false ), m_login_set( false ), m_is_mounted( false ), m_homes_share( false ),
  m_homes_users( QStringList() )
{
  m_unc = "//"+m_login+"@"+m_host+"/"+m_name;
#endif

  m_homes_share = (QString::compare( m_name, "homes", Qt::CaseSensitive ) == 0);
}


Smb4KShare::Smb4KShare( const QString &unc )
#ifndef __FreeBSD__
: m_name( QString() ), m_host( QString() ), m_unc( unc ), m_workgroup( QString() ),
  m_type( QString() ), m_comment( QString() ), m_host_ip( QString() ), m_path( QByteArray() ),
  m_inaccessible( false ), m_foreign( false ), m_filesystem( Unknown ), m_user( getuid() ),
  m_group( getgid() ), m_login( getpwuid( getuid() )->pw_name ), m_total( -1 ), m_free( -1 ),
  m_uid_set( false ), m_gid_set( false ), m_login_set( false ), m_is_mounted( false ),
  m_homes_share( false ), m_homes_users( QStringList() )
#else
: m_name( QString() ), m_host( QString() ), m_unc( unc ), m_workgroup( QString() ),
  m_type( QString() ), m_comment( QString() ), m_host_ip( QString() ), m_path( QByteArray() ),
  m_inaccessible( false ), m_foreign( false ), m_filesystem( Unknown ), m_user( getuid() ),
  m_group( getgid() ), m_login( "guest" ), m_total( -1 ), m_free( -1 ), m_uid_set( false ),
  m_gid_set( false ), m_login_set( false ), m_is_mounted( false ), m_homes_share( false ),
  m_homes_users( QStringList() )
#endif
{
  // Extract host and share from the UNC
  if ( !unc.contains( "@" ) )
  {
    m_host  = m_unc.section( "/", 2, 2 ).trimmed();
  }
  else
  {
    m_host  = m_unc.section( "@", 1, 1 ).section( "/", 0, 0 ).trimmed();
#ifdef __FreeBSD__
    m_login = m_unc.section( "/", 2, 2 ).section( "@", 0, 0 ).trimmed();
#endif
  }

  m_name    = m_unc.section( "/", 3, 3 ).trimmed();

  m_homes_share = (QString::compare( m_name, "homes", Qt::CaseSensitive ) == 0);
}


Smb4KShare::Smb4KShare( const Smb4KShare &s )
#ifndef __FreeBSD__
: m_name( s.name() ), m_host( s.host() ), m_unc( s.unc() ), m_workgroup( s.workgroup() ),
  m_type( s.type() ), m_comment( s.comment() ), m_host_ip( s.hostIP() ), m_path( s.path() ),
  m_inaccessible( s.isInaccessible() ), m_foreign( s.isForeign() ), m_filesystem( s.fileSystem() ),
  m_user( s.uid() ), m_group( s.gid() ), m_login( s.cifsLogin() ), m_total( s.totalDiskSpace() ),
  m_free( s.freeDiskSpace() ), m_uid_set( s.uidIsSet() ), m_gid_set( s.gidIsSet() ),
  m_login_set( s.cifsLoginIsSet() ), m_is_mounted( s.isMounted() ), m_homes_share( s.isHomesShare() ),
  m_homes_users( s.homesUsers() )
#else
: m_name( s.name() ), m_host( s.host() ), m_unc( s.unc() ), m_workgroup( s.workgroup() ),
  m_type( s.type() ), m_comment( s.comment() ), m_host_ip( s.hostIP() ), m_path( s.path() ),
  m_inaccessible( s.isInaccessible() ), m_foreign( s.isForeign() ), m_filesystem( s.fileSystem() ),
  m_user( s.uid() ), m_group( s.gid() ), m_login( s.login() ), m_total( s.totalDiskSpace() ),
  m_free( s.freeDiskSpace() ), m_uid_set( s.uidIsSet() ), m_gid_set( s.gidIsSet() ),
  m_login_set( s.loginIsSet() ), m_is_mounted( s.isMounted() ), m_homes_share( s.isHomesShare() ),
  m_homes_users( s.homesUsers() )
#endif
{
}



Smb4KShare::Smb4KShare()
#ifndef __FreeBSD__
: m_name( QString() ), m_host( QString() ), m_unc( QString() ), m_workgroup( QString() ),
  m_type( QString() ), m_comment( QString() ), m_host_ip( QString() ), m_path( QByteArray() ),
  m_inaccessible( false ), m_foreign( false ), m_filesystem( Unknown ), m_user( getuid() ),
  m_group( getgid() ), m_login( getpwuid( getuid() )->pw_name ), m_total( -1 ), m_free( -1 ),
  m_uid_set( false ), m_gid_set( false ), m_login_set( false ), m_is_mounted( false ),
  m_homes_share( false ), m_homes_users( QStringList() )
#else
: m_name( QString() ), m_host( QString() ), m_unc( QString() ), m_workgroup( QString() ),
  m_type( QString() ), m_comment( QString() ), m_host_ip( QString() ), m_path( QByteArray() ),
  m_inaccessible( false ), m_foreign( false ), m_filesystem( Unknown ), m_user( getuid() ),
  m_group( getgid() ), m_login( "guest" ), m_total( -1 ), m_free( -1 ), m_uid_set( false ),
  m_gid_set( false ), m_login_set( false ), m_is_mounted( false ), m_homes_share( false ),
  m_homes_users( QStringList() )
#endif
{
}


Smb4KShare::~Smb4KShare()
{
}


void Smb4KShare::setName( const QString &name )
{
  m_name = name;

  // Set the UNC.
  if ( !m_host.isEmpty() && !m_name.isEmpty() )
  {
#ifndef __FreeBSD__
    m_unc = "//"+m_host+"/"+m_name;
#else
    m_unc = "//"+(!m_login.isEmpty() ? m_login : "guest")+"@"+m_host+"/"+m_name;
#endif
  }
  else
  {
    // Do nothing
  }

  // Check if we need to set m_homes_share (it is FALSE by default).
  if ( !m_homes_share )
  {
    m_homes_share = (QString::compare( m_name, "homes", Qt::CaseSensitive ) == 0);
  }
  else
  {
    // Do nothing
  }
}


void Smb4KShare::setHost( const QString &hostName )
{
  m_host = hostName;

  // Set the UNC.
  if ( !m_host.isEmpty() && !m_name.isEmpty() )
  {
#ifndef __FreeBSD__
    m_unc = "//"+m_host+"/"+m_name;
#else
    m_unc = "//"+(!m_login.isEmpty() ? m_login : "guest")+"@"+m_host+"/"+m_name;
#endif
  }
  else
  {
    // Do nothing
  }
}


void Smb4KShare::setUNC( const QString &unc )
{
  m_unc = unc;

  // Extract host and share from the UNC
  if ( !unc.contains( "@" ) )
  {
    m_host  = m_unc.section( "/", 2, 2 ).trimmed();
  }
  else
  {
    m_host  = m_unc.section( "@", 1, 1 ).section( "/", 0, 0 ).trimmed();
#ifdef __FreeBSD__
    m_login = m_unc.section( "/", 2, 2 ).section( "@", 0, 0 ).trimmed();
#endif
  }

  m_name    = m_unc.section( "/", 3, 3 ).trimmed();

  // Check if we need to set m_homes_share (it is FALSE by default).
  if ( !m_homes_share )
  {
    m_homes_share = (QString::compare( m_name, "homes", Qt::CaseSensitive ) == 0);
  }
  else
  {
    // Do nothing
  }
}


void Smb4KShare::setWorkgroup( const QString &workgroup )
{
  m_workgroup = workgroup;
}


void Smb4KShare::setType( const QString &type )
{
  m_type = type;
}


const QString Smb4KShare::translatedType() const
{
  if ( QString::compare( m_type, "Disk" ) == 0 )
  {
    return i18n( "Disk" );
  }
  else if ( QString::compare( m_type, "Print" ) == 0 ||
            QString::compare( m_type, "Printer" ) == 0 )
  {
    return i18n( "Printer" );
  }
  else
  {
    // Do nothing
  }

  return m_type;
}


void Smb4KShare::setComment( const QString &comment )
{
  m_comment = comment;
}


void Smb4KShare::setHostIP( const QString &ip )
{
  m_host_ip = ipIsValid( ip );
}


bool Smb4KShare::isHidden() const
{
  return m_name.trimmed().endsWith( "$" );
}


bool Smb4KShare::isPrinter() const
{
  return (QString::compare( m_type, "Print" ) == 0 ||
          QString::compare( m_type, "Printer" ) == 0);
}


bool Smb4KShare::isIPC() const
{
  return (QString::compare( m_name.trimmed(), "IPC$" ) == 0);
}


bool Smb4KShare::isADMIN() const
{
  return (QString::compare( m_name.trimmed(), "ADMIN$" ) == 0);
}


void Smb4KShare::setPath( const QString &mountpoint )
{
  m_path = mountpoint.toLocal8Bit();
}


const QByteArray Smb4KShare::canonicalPath() const
{
  return (m_inaccessible ? m_path : QDir( m_path ).canonicalPath().toLocal8Bit());
}


void Smb4KShare::setInaccessible( bool in )
{
  m_inaccessible = in;
}


void Smb4KShare::setForeign( bool foreign )
{
  m_foreign = foreign;
}


void Smb4KShare::setFileSystem( FileSystem filesystem )
{
  m_filesystem = filesystem;
}


const QString Smb4KShare::fileSystemString() const
{
  switch ( m_filesystem )
  {
    case CIFS:
    {
      return "cifs";
    }
    case SMBFS:
    {
      return "smbfs";
    }
    default:
    {
      break;
    }
  }

  return QString();
}


void Smb4KShare::setUID( uid_t uid )
{
  m_user = KUser( uid );
  m_uid_set = true;
}


void Smb4KShare::setGID( gid_t gid )
{
  m_group = KUserGroup( gid );
  m_gid_set = true;
}


#ifndef __FreeBSD__
void Smb4KShare::setCIFSLogin( const QString &login )
{
  m_login = login;
  m_login_set = true;
}
#else
void Smb4KShare::setLogin( const QString &login )
{
  m_login = login;
  m_unc = "//"+m_login+"@"+m_host+"/"+m_name;
  m_login_set = true;
}
#endif


void Smb4KShare::setIsMounted( bool mounted )
{
  if ( !isPrinter() )
  {
    m_is_mounted = mounted;
  }
  else
  {
    // Do nothing
  }
}


void Smb4KShare::setTotalDiskSpace( double total )
{
  m_total = total;
}


QString Smb4KShare::totalDiskSpaceString() const
{
  if ( m_total != -1 )
  {
    QString total, total_dim = QString();

    int exponent = 0;
    double tmp_factor = 0;
    int factor = 0;

    (void) frexp( m_total * 1024 /*bytes*/, &exponent );
    (void) modf( (exponent - 10) / 10, &tmp_factor );
    factor = tmp_factor;
    double tmp_total = m_total / pow( 1024, factor );
    total = QString( "%1" ).arg( tmp_total, 0, 'f', 1 );

    switch ( factor )
    {
      case 0:
      {
        total_dim = "KiB";

        break;
      }
      case 1:
      {
        total_dim = "MiB";

        break;
      }
      case 2:
      {
        total_dim = "GiB";

        break;
      }
      case 3:
      {
        total_dim = "TiB";

        break;
      }
      default:
      {
        break;
      }
    }

    return total+" "+total_dim;
  }
  else
  {
    // Do nothing
  }

  return QString();
}


void Smb4KShare::setFreeDiskSpace( double free )
{
  m_free = free;
}


QString Smb4KShare::freeDiskSpaceString() const
{
  if ( m_free != -1 )
  {
    QString free, free_dim = QString();

    int exponent = 0;
    double tmp_factor = 0;
    int factor = 0;

    (void) frexp( m_free * 1024 /*bytes*/, &exponent );
    (void) modf( (exponent - 10) / 10, &tmp_factor );
    factor = tmp_factor;
    double tmp_free = m_free / pow( 1024, factor );
    free = QString( "%1" ).arg( tmp_free, 0, 'f', 1 );

    switch ( factor )
    {
      case 0:
      {
        free_dim = "KiB";

        break;
      }
      case 1:
      {
        free_dim = "MiB";

        break;
      }
      case 2:
      {
        free_dim = "GiB";

        break;
      }
      case 3:
      {
        free_dim = "TiB";

        break;
      }
      default:
      {
        break;
      }
    }

    return free+" "+free_dim;
  }
  else
  {
    // Do nothing
  }

  return QString();
}


double Smb4KShare::usedDiskSpace() const
{
  double used;

  if ( m_free != -1 && m_total != -1 )
  {
    used = m_total - m_free;
  }
  else
  {
    used = -1;
  }

  return used;
}


QString Smb4KShare::usedDiskSpaceString() const
{
  if ( usedDiskSpace() != -1 )
  {
    QString used, used_dim = QString();

    int exponent = 0;
    double tmp_factor = 0;
    int factor = 0;
    double used_space = usedDiskSpace();

    (void) frexp( used_space * 1024 /*bytes*/, &exponent );
    (void) modf( (exponent - 10) / 10, &tmp_factor );
    factor = tmp_factor;
    double tmp_used = used_space / pow( 1024, factor );
    used = QString( "%1" ).arg( tmp_used, 0, 'f', 1 );

    switch ( factor )
    {
      case 0:
      {
        used_dim = "KiB";

        break;
      }
      case 1:
      {
        used_dim = "MiB";

        break;
      }
      case 2:
      {
        used_dim = "GiB";

        break;
      }
      case 3:
      {
        used_dim = "TiB";

        break;
      }
      default:
      {
        break;
      }
    }

    return used+" "+used_dim;
  }
  else
  {
    // Do nothing
  }

  return QString();
}


double Smb4KShare::diskUsage() const
{
  if ( m_total != -1 && m_free != -1 )
  {
    return (m_total - m_free) / m_total * 100;
  }
  else
  {
    // Do nothing
  }

  return -1;
}


QString Smb4KShare::diskUsageString() const
{
  if ( diskUsage() != -1 )
  {
    return QString( "%1 %" ).arg( diskUsage(), 0, 'f', 1 );
  }
  else
  {
    // Do nothing
  }

  return QString();
}


bool Smb4KShare::equals( const Smb4KShare &share, CheckFlags flag )
{
  switch ( flag )
  {
    case Full:
    {
      if ( QString::compare( m_name, share.name() ) == 0 &&
           QString::compare( m_host, share.host() ) == 0 &&
           QString::compare( m_unc, share.unc() ) == 0 &&
           QString::compare( m_workgroup, share.workgroup() ) == 0 &&
           QString::compare( m_type, share.type() ) == 0 &&
           QString::compare( m_comment, share.comment() ) == 0 &&
           QString::compare( m_host_ip, share.hostIP() ) == 0 &&
           QString::compare( m_path, share.path() ) == 0 &&
#ifndef __FreeBSD__
           QString::compare( m_login, share.cifsLogin() ) == 0 &&
#else
           QString::compare( m_login, share.login() ) == 0 &&
#endif
           m_inaccessible == share.isInaccessible() &&
           m_foreign == share.isForeign() &&
           m_filesystem == share.fileSystem() &&
           m_user.uid() == share.uid() &&
           m_group.gid() == share.gid() &&
           m_total == share.totalDiskSpace() &&
           m_free == share.freeDiskSpace() &&
           m_homes_users == share.homesUsers() )
      {
        return true;
      }
      else
      {
        // Do nothing
      }

      break;
    }
    case NetworkOnly:
    {
      if ( QString::compare( m_name, share.name() ) == 0 &&
           QString::compare( m_host, share.host() ) == 0 &&
           QString::compare( m_workgroup, share.workgroup() ) == 0 &&
           QString::compare( m_type, share.type() ) == 0 &&
           QString::compare( m_comment, share.comment() ) == 0 &&
           QString::compare( m_host_ip, share.hostIP() ) == 0 &&
           m_homes_users == share.homesUsers() )
      {
        return true;
      }
      else
      {
        // Do nothing
      }

      break;
    }
    case LocalOnly:
    {
      if ( QString::compare( m_unc, share.unc() ) == 0 &&
           QString::compare( m_path, share.path() ) == 0 &&
#ifndef __FreeBSD__
           QString::compare( m_login, share.cifsLogin() ) == 0 &&
#else
           QString::compare( m_login, share.login() ) == 0 &&
#endif
           m_inaccessible == share.isInaccessible() &&
           m_foreign == share.isForeign() &&
           m_filesystem == share.fileSystem() &&
           m_user.uid() == share.uid() &&
           m_group.gid() == share.gid() &&
           m_total == share.totalDiskSpace() &&
           m_free == share.freeDiskSpace() )
      {
        return true;
      }
      else
      {
        // Do nothing
      }

      break;
    }
    default:
    {
      break;
    }
  }

  return false;
}


bool Smb4KShare::isEmpty( CheckFlags flag ) const
{
  switch ( flag )
  {
    case Full:
    {
      if ( !m_name.isEmpty() )
      {
        return false;
      }

      if ( !m_host.isEmpty() )
      {
        return false;
      }

      if ( !m_unc.isEmpty() )
      {
        return false;
      }

      if ( !m_workgroup.isEmpty() )
      {
        return false;
      }

      if ( !m_type.isEmpty() )
      {
        return false;
      }

      if ( !m_comment.isEmpty() )
      {
        return false;
      }

      if ( !m_host_ip.isEmpty() )
      {
        return false;
      }

      if ( !m_path.isEmpty() )
      {
        return false;
      }

      if ( m_filesystem != Unknown )
      {
        return false;
      }

      if ( m_total != -1 )
      {
        return false;
      }

      if ( m_free != -1 )
      {
        return false;
      }

      if ( m_uid_set )
      {
        return false;
      }

      if ( m_gid_set )
      {
        return false;
      }

      if ( m_login_set )
      {
        return false;
      }

      break;
    }
    case NetworkOnly:
    {
      if ( !m_name.isEmpty() )
      {
        return false;
      }

      if ( !m_host.isEmpty() )
      {
        return false;
      }

      if ( !m_workgroup.isEmpty() )
      {
        return false;
      }

      if ( !m_type.isEmpty() )
      {
        return false;
      }

      if ( !m_comment.isEmpty() )
      {
        return false;
      }

      if ( !m_host_ip.isEmpty() )
      {
        return false;
      }

      break;
    }
    case LocalOnly:
    {
      if ( !m_unc.isEmpty() )
      {
        return false;
      }

      if ( !m_path.isEmpty() )
      {
        return false;
      }

      if ( m_filesystem != Unknown )
      {
        return false;
      }

      if ( m_total != -1 )
      {
        return false;
      }

      if ( m_free != -1 )
      {
        return false;
      }

      if ( m_uid_set )
      {
        return false;
      }

      if ( m_gid_set )
      {
        return false;
      }

      if ( m_login_set )
      {
        return false;
      }

      break;
    }
    default:
    {
      break;
    }
  }

  return true;
}


void Smb4KShare::setMountData( Smb4KShare *share )
{
  if ( share )
  {
    m_path = share->path();
    m_inaccessible = share->isInaccessible();
    m_foreign = share->isForeign();
    m_filesystem = share->fileSystem();
    m_user = KUser( share->uid() );
    m_group = KUserGroup( share->gid() );
#ifndef __FreeBSD__
    m_login = share->cifsLogin();
    m_login_set = share->cifsLoginIsSet();
#else
    m_login = share->login();
    m_login_set = share->loginIsSet();
#endif
    m_total = share->totalDiskSpace();
    m_free = share->freeDiskSpace();
    m_uid_set = share->uidIsSet();
    m_gid_set = share->gidIsSet();
    m_is_mounted = share->isMounted();
  }
  else
  {
    // Do nothing
  }
}


void Smb4KShare::resetMountData()
{
  m_path = QByteArray();
  m_inaccessible = false;
  m_foreign = false;
  m_filesystem = Unknown;
  m_user = KUser( getuid() );
  m_group = KUserGroup( getgid() );
#ifndef __FreeBSD__
  m_login = QString::fromLocal8Bit( getpwuid( getuid() )->pw_name );
#else
  m_login = "guest";
#endif
  m_total = -1;
  m_free = -1;
  m_uid_set = false;
  m_gid_set = false;
  m_login_set = false;
  m_is_mounted = false;
}


void Smb4KShare::setHomesUsers( const QStringList &users )
{
  if ( m_homes_share )
  {
    m_homes_users = users;
  }
  else
  {
    // Do nothing
  }
}


const QString &Smb4KShare::ipIsValid( const QString &ip )
{
  QHostAddress ip_address( ip );

  if ( ip_address.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol )
  {
    // The IP address is invalid.
    static_cast<QString>( ip ).clear();
  }

  return ip;
}
