/***************************************************************************
    smb4kpreviewer  -  This class queries a remote share for a preview
                             -------------------
    begin                : Mo Mai 28 2007
    copyright            : (C) 2007-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 includes
#include <QApplication>
#include <QDesktopWidget>

// KDE includes
#include <kapplication.h>
#include <kdebug.h>
#include <kshell.h>

// application specific includes
#include <smb4kpreviewer.h>
#include <smb4kpreviewitem.h>
#include <smb4kdefs.h>
#include <smb4kglobal.h>
#include <smb4kauthinfo.h>
#include <smb4ksambaoptionshandler.h>
#include <smb4kcoremessage.h>
#include <smb4kshare.h>
#include <smb4khomesshareshandler.h>
#include <smb4kwalletmanager.h>

using namespace Smb4KGlobal;


Smb4KPreviewer::Smb4KPreviewer( QObject *parent ) : QObject( parent )
{
  m_proc = new KProcess( this );

  m_item = NULL;
  m_working = false;
  m_aborted = false;
  m_process_error = (QProcess::ProcessError)(-1);

  connect( m_proc,  SIGNAL( finished( int, QProcess::ExitStatus ) ),
           this,    SLOT( slotProcessFinished( int, QProcess::ExitStatus ) )  );

  connect( m_proc,  SIGNAL( error( QProcess::ProcessError ) ),
           this,    SLOT( slotProcessError( QProcess::ProcessError ) ) );
}


Smb4KPreviewer::~Smb4KPreviewer()
{
  // Do not delete m_item here, because it is owned
  // by a different object.
}


bool Smb4KPreviewer::preview( Smb4KPreviewItem *item )
{
   // If there is no item, stop right here.
  if ( !item )
  {
    return false;
  }

  if ( QString::compare( item->shareItem()->name(), "homes" ) == 0 )
  {
    QWidget *parent = 0;

    if ( kapp )
    {
      if ( kapp->activeWindow() )
      {
        parent = kapp->activeWindow();
      }
      else
      {
        parent = kapp->desktop();
      }
    }
    else
    {
      // Do nothing
    }

    bool success = Smb4KHomesSharesHandler::self()->specifyUser( item->shareItem(), parent );

    if ( !success )
    {
      return success;
    }
    else
    {
      // Do nothing
    }
  }

  startTimer( TIMER_INTERVAL );

  m_queue.enqueue( item );

  return true;
}


void Smb4KPreviewer::abort()
{
  while ( !m_queue.isEmpty() )
  {
    (void) m_queue.dequeue();
  }

  if ( m_proc->state() == KProcess::Running )
  {
    m_proc->kill();
  }

  m_aborted = true;
}


void Smb4KPreviewer::timerEvent( QTimerEvent *e )
{
  if ( !m_queue.isEmpty() )
  {
    if ( !m_working )
    {
      if ( !m_queue.isEmpty() )
      {
        m_working = true;

        m_aborted = false;

        emit state( PREVIEWER_START );

        m_item = m_queue.dequeue();

        // Assemble the command.
        //
        // Here are some things to remember:
        // (a) Do not convert the path to local 8 bit. It won't work with umlauts or other
        //     special characters.
        // (b) Do not pass the path unquoted, or you'll get a NT_STATUS_OBJECT_NAME_NOT_FOUND
        //     error message in the case the path is empty.
        QString command;
        command.append( "smbclient" );
        command.append( " "+KShell::quoteArg( m_item->shareItem()->unc() ) );
        command.append( " -W "+KShell::quoteArg( m_item->shareItem()->workgroup() ) );
        command.append( " -D "+KShell::quoteArg( m_item->path() ) );
        command.append( " -c "+KShell::quoteArg( "ls" ) );

        if ( !m_item->shareItem()->hostIP().isEmpty() )
        {
          command.append( QString( " -I %1 " ).arg( m_item->shareItem()->hostIP() ) );
        }
        else
        {
          // Do nothing
        }

        command.append( Smb4KSambaOptionsHandler::self()->smbclientOptions( *m_item->shareItem() ) );

        Smb4KAuthInfo authInfo( m_item->shareItem() );
        Smb4KWalletManager::self()->readAuthInfo( &authInfo );

        if ( !authInfo.login().isEmpty() )
        {
          command.append( QString( " -U %1" ).arg( KShell::quoteArg( authInfo.login() ) ) );

          if ( !authInfo.password().isEmpty() )
          {
            m_proc->setEnv( "PASSWD", authInfo.password() );
          }
        }
        else
        {
          command.append( " -U guest%" );
        }

        m_proc->setShellCommand( command );
        m_proc->setOutputChannelMode( KProcess::SeparateChannels );

        QApplication::setOverrideCursor( Qt::WaitCursor );

        m_proc->start();
      }
    }
    else
    {
      // Do nothing
    }
  }
  else
  {
    killTimer( e->timerId() );
  }
}


/////////////////////////////////////////////////////////////////////////////
// SLOT IMPLEMENTATIONS
/////////////////////////////////////////////////////////////////////////////


void Smb4KPreviewer::slotProcessFinished( int /*exitCode*/, QProcess::ExitStatus exitStatus )
{
  if ( exitStatus == QProcess::NormalExit )
  {
    // Read from stderr and decide what to do:
    QString stderr_output = QString::fromLocal8Bit( m_proc->readAllStandardError(), -1 ).trimmed();

    if ( !stderr_output.isEmpty() )
    {
      QStringList errors = stderr_output.split( "\n", QString::SkipEmptyParts );

      if ( errors.size() == 1 && (errors.first().trimmed().startsWith( "Domain" ) ||
           errors.first().trimmed().startsWith( "OS" )) )
      {
        // Ignore this "error" message and proceed with the code below. We
        // have to do this, because - unfortunately - smbclient reports the
        // domain name, the operating system and the server to stderr.
      }
      else
      {
        if ( stderr_output.contains( "NT_STATUS_ACCESS_DENIED", Qt::CaseSensitive ) ||
             stderr_output.contains( "NT_STATUS_LOGON_FAILURE", Qt::CaseSensitive ) )
        {
          Smb4KAuthInfo authInfo( m_item->shareItem() );

          if ( Smb4KWalletManager::self()->showPasswordDialog( &authInfo ) )
          {
            // Now we have a password. Retry.
            // NOTE: Since the item is appended to the queue, there might
            // be the case, that another preview is generated before the
            // retry is executed. I think, we can live with that.
            preview( m_item );
          }
          else
          {
            // The user cancelled the askpass dialog. We won't show an
            // error dialog here, but will only clear the contents of
            // the preview item and emit the failed() signal.
            m_item->clearContents();

            emit failed();
          }
        }
        else
        {
          // OK, error out. We cannot recover from it:
          Smb4KCoreMessage::error( ERROR_GETTING_PREVIEW, QString(), stderr_output );

          m_item->clearContents();

          emit failed();
        }

        m_proc->clearProgram();

        QApplication::restoreOverrideCursor();

        m_working = false;

        emit state( PREVIEWER_STOP );

        return;
      }
    }

    // No error occurred. Process the output from stdout:
    QStringList list = QString::fromLocal8Bit( m_proc->readAllStandardOutput(), -1 )
                       .split( "\n", QString::SkipEmptyParts );

    for ( int i = 0; i < list.size(); ++i )
    {
      if ( list.at( i ).contains( "blocks of size" ) )
      {
        continue;
      }
      else
      {
        QString tmp = list.at( i ).trimmed().section( " ", 0, -9 ).trimmed();

        QString item = tmp.section( "  ", 0, -2 ).trimmed();

        if ( !item.isEmpty() && tmp.section( "  ", -1, -1 ).contains( "D" ) )
        {
          // We have a directory here.
          if ( item.startsWith( "." ) &&
               (QString::compare( item.trimmed(), "." ) != 0 &&
                QString::compare( item.trimmed(), ".." ) != 0) )
          {
            m_item->addContents( ContentsItem( Smb4KPreviewItem::HiddenDirectory, item ) );
          }
          else
          {
            m_item->addContents( ContentsItem( Smb4KPreviewItem::Directory, item ) );
          }

          continue;
        }
        else if ( item.isEmpty() || !tmp.section( "  ", -1, -1 ).contains( "D" ) )
        {
          // We have a file
          if ( item.isEmpty() )
          {
            if ( tmp.startsWith( "." ) )
            {
              m_item->addContents( ContentsItem( Smb4KPreviewItem::HiddenFile, tmp ) );
            }
            else
            {
              m_item->addContents( ContentsItem( Smb4KPreviewItem::File, tmp ) );
            }
          }
          else
          {
            if ( item.startsWith( "." ) )
            {
              m_item->addContents( ContentsItem( Smb4KPreviewItem::HiddenFile, item ) );
            }
            else
            {
              m_item->addContents( ContentsItem( Smb4KPreviewItem::File, item ) );
            }
          }

          continue;
        }
        else
        {
          continue;
        }
      }
    }

    emit result( m_item );
  }
  else
  {
    // Something went wrong. Throw an error if the problem was not
    // caused by using the abort() function.
    if ( !m_aborted )
    {
      if ( m_process_error != -1 )
      {
        Smb4KCoreMessage::processError( ERROR_PROCESS_ERROR, m_process_error );
      }
      else
      {
        Smb4KCoreMessage::processError( ERROR_PROCESS_EXIT, m_process_error );
      }
    }
    else
    {
      // Do nothing
    }
  }

  m_proc->clearProgram();

  QApplication::restoreOverrideCursor();

  m_process_error = (QProcess::ProcessError)(-1);
  m_working = false;

  emit state( PREVIEWER_STOP );
}


void Smb4KPreviewer::slotProcessError( QProcess::ProcessError errorCode )
{
  m_process_error = errorCode;
}

#include "smb4kpreviewer.moc"
