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

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

// application specific includes
#include "smb4kpreviewer.h"
#include "smb4kpreviewitem.h"
#include "smb4kdefs.h"
#include "smb4tdeglobal.h"
#include "smb4kpasswordhandler.h"
#include "smb4kauthinfo.h"
#include "smb4ksambaoptionshandler.h"
#include "smb4kerror.h"

using namespace Smb4TDEGlobal;


Smb4KPreviewer::Smb4KPreviewer( TQObject *parent, const char *name )
: TQObject( parent, name )
{
  m_item = NULL;

  m_buffer = TQString();

  m_working = false;

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

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

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

  connect( m_proc, TQT_SIGNAL( receivedStderr( TDEProcess *, char *, int ) ),
           this,   TQT_SLOT( slotReceivedStderr( TDEProcess *, char *, int ) ) );
}


Smb4KPreviewer::~Smb4KPreviewer()
{
  // Do not delete m_item here, because it belongs to an
  // outside class.
}


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

  if ( TQString::compare( item->share(), "homes" ) == 0 )
  {
    TQString share_name = specifyUser( item->host(), kapp->mainWidget() ? kapp->mainWidget() : 0, "SpecifyUser" );

    if ( !share_name.isEmpty() )
    {
      // The Smb4KPreviewItem::setShare() function will take care
      // that no share name is overwritten, that is *not* named
      // 'homes'.
      item->setShare( share_name );
    }
    else
    {
      return false;
    }
  }

  m_timer_id = startTimer( TIMER_INTERVAL );

  m_queue.enqueue( item );

  return true;
}


void Smb4KPreviewer::abort()
{
  m_queue.clear();

  if ( m_proc->isRunning() )
  {
    m_proc->kill();
  }
}


void Smb4KPreviewer::timerEvent( TQTimerEvent * )
{
  if ( m_working )
  {
    return;
  }

  // Declare the previewer working:
  emit state( PREVIEWER_START );

  m_working = true;

  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.
  TQString command;
  command.append( TQString( "smbclient //%1/%2 " ).arg( TDEProcess::quote( m_item->host() ), TDEProcess::quote( m_item->share() ) ) );
  command.append( TQString( " -d1 -W %1 -D %2 " ).arg( TDEProcess::quote( m_item->workgroup() ), TDEProcess::quote( m_item->path() ) ) );
  command.append( " -c \"ls\" " );

  if ( !m_item->ip().isEmpty() )
  {
    command.append( TQString( " -I %1 " ).arg( m_item->ip() ) );
  }

  command.append( optionsHandler()->smbclientOptions( "//"+m_item->host()+"/"+m_item->share() ) );

  Smb4KAuthInfo *auth = passwordHandler()->readAuth( new Smb4KAuthInfo( m_item->workgroup(), m_item->host(), m_item->share() ) );

  if ( !auth->user().isEmpty() )
  {
    command.append( TQString( " -U %1" ).arg( TDEProcess::quote( auth->user() ) ) );

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

  delete auth;

  *m_proc << command;

  TQApplication::setOverrideCursor( waitCursor );

  m_proc->start( TDEProcess::NotifyOnExit, TDEProcess::AllOutput );
}


/////////////////////////////////////////////////////////////////////////////
// TQT_SLOT IMPLEMENTATIONS
/////////////////////////////////////////////////////////////////////////////

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


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


void Smb4KPreviewer::slotProcessExited( TDEProcess * )
{
  // Disconnect the timer:
  if ( m_queue.isEmpty() )
  {
    killTimer( m_timer_id );
  }

  m_proc->clearArguments();

  TQStringList list = TQStringList::split( "\n", m_buffer, false );

  m_buffer = TQString();

  // Check whether an error occurred:
  if ( list.grep( "NT_STATUS" ).count() != 0 )
  {
    // Something went wrong. Let's check if this "only" an
    // authentication issue or if we have to error out:

    TQString error_code = list.grep( "NT_STATUS" ).first().stripWhiteSpace().section( " ", 0, 0 );

    // The error output of smbclient is a little bit inconsistent:
    if ( error_code.contains( "NT_STATUS" ) == 0 )
    {
      error_code = list.grep( "NT_STATUS" ).first().stripWhiteSpace().section( " ", -1, -1 );
    }

    // Authentication issue?
    if ( TQString::compare( error_code, "NT_STATUS_ACCESS_DENIED" ) == 0 ||
         TQString::compare( error_code, "NT_STATUS_LOGON_FAILURE" ) == 0 )
    {
      int state = Smb4KPasswordHandler::None;

      if ( TQString::compare( error_code, "NT_STATUS_ACCESS_DENIED" ) == 0 )
      {
        state = Smb4KPasswordHandler::AccessDenied;
      }
      else if ( TQString::compare( error_code, "NT_STATUS_LOGON_FAILURE" ) == 0 )
      {
        state = Smb4KPasswordHandler::LogonFailure;
      }

      if ( passwordHandler()->askpass( m_item->workgroup(), m_item->host(),
                                       m_item->share(), state,
                                       kapp->mainWidget() ? kapp->mainWidget() : 0,
                                       "AskPass" ) )
      {
        // 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:
      Smb4KError::error( ERROR_GETTING_PREVIEW, TQString(), m_buffer );

      m_item->clearContents();

      emit failed();
    }
  }
  else if ( list.grep( "Connection to" ).count() != 0 ||
            (list.grep( "Error returning browse list:" ).count() != 0 &&
            list.grep( "NT_STATUS" ).count() == 0) )
  {
    // These are errors that we cannot work around. Error out.
    Smb4KError::error( ERROR_GETTING_PREVIEW, TQString(), m_buffer );

    m_item->clearContents();

    emit failed();
  }
  else
  {
    for ( TQStringList::Iterator it = list.begin(); it != list.end(); ++it )
    {
      if ( (*it).stripWhiteSpace().startsWith( "Domain" ) ||
          (*it).stripWhiteSpace().startsWith( "OS" ) ||
          (*it).stripWhiteSpace().startsWith( "Anonymous" ) )
      {
        continue;
      }
      else if ( (*it).contains( "blocks of size" ) != 0 )
      {
        continue;
      }
      else
      {
        TQString tmp = (*it).stripWhiteSpace().section( " ", 0, -9 ).stripWhiteSpace();

        TQString item = tmp.section( "  ", 0, -2 ).stripWhiteSpace();

        if ( !item.isEmpty() && tmp.section( "  ", -1, -1 ).contains( "D" ) != 0 )
        {
          // We have a directory here.
          if ( item.startsWith( "." ) &&
               (TQString::compare( item.stripWhiteSpace(), "." ) != 0 &&
               TQString::compare( item.stripWhiteSpace(), ".." ) != 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" ) == 0 )
        {
          // 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 );

  TQApplication::restoreOverrideCursor();

  m_working = false;

  emit state( PREVIEWER_STOP );
}

#include "smb4kpreviewer.moc"
