/***************************************************************************
                        synchronizer.cpp  -  description
                             -------------------
    copyright            : (C) 2003 + by Csaba Karai
    e-mail               : krusader@users.sourceforge.net
    web site             : http://krusader.sourceforge.net
 ---------------------------------------------------------------------------
  Description
 ***************************************************************************

  A

     db   dD d8888b. db    db .d8888.  .d8b.  d8888b. d88888b d8888b.
     88 ,8P' 88  `8D 88    88 88'  YP d8' `8b 88  `8D 88'     88  `8D
     88,8P   88oobY' 88    88 `8bo.   88ooo88 88   88 88ooooo 88oobY'
     88`8b   88`8b   88    88   `Y8b. 88~~~88 88   88 88~~~~~ 88`8b
     88 `88. 88 `88. 88b  d88 db   8D 88   88 88  .8D 88.     88 `88.
     YP   YD 88   YD ~Y8888P' `8888Y' YP   YP Y8888D' Y88888P 88   YD

                                                     S o u r c e    F i l e

 ***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "synchronizer.h"
#include "synchronizerdirlist.h"
#include "../krusader.h"
#include "../krservices.h"
#include "../VFS/vfs.h"
#include "../VFS/krquery.h"
#include "config.h"
#include <kurl.h>
#include <tdemessagebox.h>
#include <tdelocale.h>
#include <tqapplication.h>
#include <tqregexp.h>
#include <tqdir.h>
#include <tqtimer.h>
#include <tdeio/job.h>
#include <kdialogbase.h>
#include <tdeio/observer.h>
#include <tdeio/renamedlg.h>
#include <tdeio/skipdlg.h>
#include <unistd.h>
#include <tqeventloop.h>
#include <tqpushbutton.h>
#include <tqdatetime.h>
#include <kprocess.h>
#include <kdialogbase.h>
#include <kprogress.h>
#include <tqlayout.h>
#include <kurlcompletion.h>

#include <sys/types.h>
#include <sys/time.h>
#include <utime.h>
#include <pwd.h>
#include <grp.h>
#include <tqlabel.h>



#if defined( HAVE_POSIX_ACL )
#include <sys/acl.h>
#ifdef HAVE_NON_POSIX_ACL_EXTENSIONS
#include <acl/libacl.h>
#endif
#endif


#define  DISPLAY_UPDATE_PERIOD        2

Synchronizer::Synchronizer() : displayUpdateCount( 0 ), markEquals( true ), 
        markDiffers ( true ), markCopyToLeft( true ), markCopyToRight( true ), markDeletable( true ),
        stack(), jobMap(), receivedMap(), parentWidget( 0 )
{
  resultList.setAutoDelete( true );
  stack.setAutoDelete( true );
}

void Synchronizer::reset()
{
  displayUpdateCount = 0;
  markEquals = markDiffers = markCopyToLeft = markCopyToRight = markDeletable = true;
  stopped = false;
  recurseSubDirs = followSymLinks = ignoreDate = asymmetric = cmpByContent = ignoreCase = autoScroll = false;
  markEquals = markDiffers = markCopyToLeft = markCopyToRight = markDeletable = markDuplicates = markSingles = false;
  leftCopyEnabled = rightCopyEnabled = deleteEnabled = overWrite = autoSkip = paused = false;
  leftCopyNr = rightCopyNr = deleteNr = 0;
  leftCopySize = rightCopySize = deleteSize = 0;
  comparedDirs = fileCount = 0;
  leftBaseDir = rightBaseDir = TQString();
  resultList.clear();
  temporaryList.clear();
  stack.clear();
}

int Synchronizer::compare( TQString leftURL, TQString rightURL, KRQuery *query, bool subDirs,
                            bool symLinks, bool igDate, bool asymm, bool cmpByCnt, bool igCase, 
                            bool autoSc, TQStringList &selFiles, int equThres, int timeOffs, int parThreads, bool hiddenFiles )
{
  resultList.clear();
  temporaryList.clear();

  recurseSubDirs = subDirs;
  followSymLinks = symLinks;
  ignoreDate     = igDate;
  asymmetric     = asymm;
  cmpByContent   = cmpByCnt;
  autoScroll     = autoSc;
  ignoreCase     = igCase;
  selectedFiles  = selFiles;
  equalsThreshold= equThres;
  timeOffset     = timeOffs;
  parallelThreads= parThreads;
  ignoreHidden   = hiddenFiles;

  stopped = false;

  this->query = query;

  leftURL = KURLCompletion::replacedPath( leftURL, true, true );
  rightURL = KURLCompletion::replacedPath( rightURL, true, true );

  if( !leftURL.endsWith("/" ))  leftURL+="/";
  if( !rightURL.endsWith("/" )) rightURL+="/";

  excludedPaths = query->dontSearchInDirs().toStringList();
  for( unsigned i = 0; i != excludedPaths.count(); i++ )
    if( excludedPaths[ i ].endsWith( "/" ) )
      excludedPaths[ i ].truncate( excludedPaths[ i ].length() - 1 );

  comparedDirs = fileCount = 0;

  stack.append( new CompareTask( 0, leftBaseDir = leftURL, rightBaseDir = rightURL, "", "", ignoreHidden ) );
  compareLoop();

  SynchronizerFileItem *item = temporaryList.first();
  while( item )
  {
    if( item->isTemporary() )
      delete item;
    item = temporaryList.next();
  }
  temporaryList.clear();


  if( !autoScroll )
    refresh( true );

  emit statusInfo( i18n( "Number of files: %1" ).arg( fileCount ) );
  return fileCount;
}

void Synchronizer::compareLoop() {
  while( !stopped && !stack.isEmpty() ) {
    for( int thread=0; thread < (int)stack.count() && thread < parallelThreads; thread++ ) {
      SynchronizerTask * entry = stack.at( thread );

      if( entry->state() == ST_STATE_NEW )
        entry->start( parentWidget );

      if( entry->inherits("CompareTask") ) {
        if( entry->state() == ST_STATE_READY ) {
          CompareTask *ctentry = (CompareTask *) entry;
          if( ctentry->isDuplicate() )
            compareDirectory( ctentry->parent(), ctentry->leftDirList(), ctentry->rightDirList(),
                              ctentry->leftDir(), ctentry->rightDir() );
          else
            addSingleDirectory( ctentry->parent(), ctentry->dirList(), ctentry->dir(),
                                ctentry->isLeft() );
        }
        if( entry->state() == ST_STATE_READY || entry->state() == ST_STATE_ERROR )
          comparedDirs++;
      }
      switch( entry->state() ) {
      case ST_STATE_STATUS:
        emit statusInfo( entry->status() );
        break;
      case ST_STATE_READY:
      case ST_STATE_ERROR:
        emit statusInfo( i18n( "Number of compared directories: %1" ).arg( comparedDirs ) );
        stack.removeRef( entry );
        continue;
      default:
        break;
      }
    }
    if( !stack.isEmpty() )
      tqApp->processEvents();
  }
  stack.clear();
}

void Synchronizer::compareDirectory( SynchronizerFileItem *parent, SynchronizerDirList * left_directory,
                                     SynchronizerDirList * right_directory, const TQString &leftDir,
                                     const TQString &rightDir )
{
  const TQString &leftURL = left_directory->url();
  const TQString &rightURL = right_directory->url();
  vfile * left_file;
  vfile * right_file;

  TQString file_name;
  bool checkIfSelected = false;

  if( leftDir.isEmpty() && rightDir.isEmpty() && selectedFiles.count() )
    checkIfSelected = true;

  /* walking through in the left directory */
  for( left_file=left_directory->first(); left_file != 0 && !stopped ;
       left_file=left_directory->next() )
  {
    if ( isDir( left_file ) )
      continue;

    file_name =  left_file->vfile_getName();

    if( checkIfSelected && !selectedFiles.contains( file_name ) )
      continue;

    if( !query->match( left_file ) )
      continue;

    if( (right_file = right_directory->search( file_name, ignoreCase )) == 0 )
      addLeftOnlyItem( parent, file_name, leftDir, left_file->vfile_getSize(), left_file->vfile_getTime_t(),
                       readLink( left_file ), left_file->vfile_getOwner(), left_file->vfile_getGroup(),
                       left_file->vfile_getMode(), left_file->vfile_getACL() );
    else
    {
      if( isDir( right_file ) )
        continue;

      addDuplicateItem( parent, file_name, right_file->vfile_getName(), leftDir, rightDir, left_file->vfile_getSize(), right_file->vfile_getSize(),
                        left_file->vfile_getTime_t(), right_file->vfile_getTime_t(), readLink( left_file ),
                        readLink( right_file ), left_file->vfile_getOwner(), right_file->vfile_getOwner(), 
                        left_file->vfile_getGroup(), right_file->vfile_getGroup(),
                        left_file->vfile_getMode(), right_file->vfile_getMode(),
                        left_file->vfile_getACL(), right_file->vfile_getACL() );
    }
  }

  /* walking through in the right directory */
  for( right_file=right_directory->first(); right_file != 0 && !stopped ;
       right_file=right_directory->next() )
  {
    if( isDir( right_file ) )
      continue;

    file_name =  right_file->vfile_getName();

    if( checkIfSelected && !selectedFiles.contains( file_name ) )
      continue;

    if( !query->match( right_file ) )
      continue;

    if( left_directory->search( file_name, ignoreCase ) == 0 )
      addRightOnlyItem( parent, file_name, rightDir, right_file->vfile_getSize(), right_file->vfile_getTime_t(),
                        readLink( right_file ), right_file->vfile_getOwner(), right_file->vfile_getGroup(),
                        right_file->vfile_getMode(), right_file->vfile_getACL() );
  }

  /* walking through the subdirectories */
  if( recurseSubDirs )
  {
    for( left_file=left_directory->first(); left_file != 0 && !stopped ;
         left_file=left_directory->next() )
    {
      if ( left_file->vfile_isDir() && ( followSymLinks || !left_file->vfile_isSymLink()) )
      {
        TQString left_file_name =  left_file->vfile_getName();
    
        if( checkIfSelected && !selectedFiles.contains( left_file_name ) )
          continue;

        if( excludedPaths.contains( leftDir.isEmpty() ? left_file_name : leftDir+"/"+left_file_name ) )
          continue;

        if( !query->matchDirName( left_file_name ) )
          continue;

        if( (right_file = right_directory->search( left_file_name, ignoreCase )) == 0 ) 
        {
          SynchronizerFileItem *me = addLeftOnlyItem( parent, left_file_name, leftDir, 0, 
                                                      left_file->vfile_getTime_t(), readLink( left_file ),
                                                      left_file->vfile_getOwner(), left_file->vfile_getGroup(),
                                                      left_file->vfile_getMode(), left_file->vfile_getACL(),
                                                      true, !query->match( left_file ) );
          stack.append( new CompareTask( me, leftURL+left_file_name+"/", 
                              leftDir.isEmpty() ? left_file_name : leftDir+"/"+left_file_name, true, ignoreHidden ) );
        } else {
          TQString right_file_name =  right_file->vfile_getName();
          SynchronizerFileItem *me = addDuplicateItem( parent, left_file_name, right_file_name, 
                                                       leftDir, rightDir, 0, 0, 
                                                       left_file->vfile_getTime_t(), right_file->vfile_getTime_t(),
                                                       readLink( left_file ), readLink( right_file ), 
                                                       left_file->vfile_getOwner(), right_file->vfile_getOwner(),
                                                       left_file->vfile_getGroup(), right_file->vfile_getGroup(),
                                                       left_file->vfile_getMode(), right_file->vfile_getMode(),
                                                       left_file->vfile_getACL(), right_file->vfile_getACL(),
                                                       true, !query->match( left_file ) );
          stack.append( new CompareTask( me, leftURL+left_file_name+"/", rightURL+right_file_name+"/",
                            leftDir.isEmpty() ? left_file_name : leftDir+"/"+left_file_name,
                            rightDir.isEmpty() ? right_file_name : rightDir+"/"+right_file_name, ignoreHidden ) );
        }
      }
    }

    /* walking through the the right side subdirectories */
    for( right_file=right_directory->first(); right_file != 0 && !stopped ;
         right_file=right_directory->next() )
    {
      if ( right_file->vfile_isDir() && (followSymLinks || !right_file->vfile_isSymLink()) )
      {
        file_name =  right_file->vfile_getName();

        if( checkIfSelected && !selectedFiles.contains( file_name ) )
          continue;
          
        if( excludedPaths.contains( rightDir.isEmpty() ? file_name : rightDir+"/"+file_name ) )
          continue;

        if( !query->matchDirName( file_name ) )
          continue;

        if( left_directory->search( file_name, ignoreCase ) == 0 )
        {
          SynchronizerFileItem *me = addRightOnlyItem( parent, file_name, rightDir, 0, 
                                                      right_file->vfile_getTime_t(), readLink( right_file ),
                                                      right_file->vfile_getOwner(), right_file->vfile_getGroup(),
                                                      right_file->vfile_getMode(), right_file->vfile_getACL(),
                                                      true, !query->match( right_file ) );
          stack.append( new CompareTask( me, rightURL+file_name+"/", 
                              rightDir.isEmpty() ? file_name : rightDir+"/"+file_name, false, ignoreHidden ) );
        }
      }
    }
  }
}

TQString Synchronizer::getTaskTypeName( TaskType taskType )
{
  static TQString names[] = {"=","!=","<-","->","DEL","?","?","?","?","?"};

  return names[taskType];
}

SynchronizerFileItem * Synchronizer::addItem( SynchronizerFileItem *parent, const TQString &leftFile, 
                                  const TQString &rightFile, const TQString &leftDir,
                                  const TQString &rightDir, bool existsLeft, bool existsRight,
                                  TDEIO::filesize_t leftSize, TDEIO::filesize_t rightSize,
                                  time_t leftDate, time_t rightDate, const TQString &leftLink,
                                  const TQString &rightLink, const TQString &leftOwner,
                                  const TQString &rightOwner, const TQString &leftGroup,
                                  const TQString &rightGroup, mode_t leftMode, mode_t rightMode, 
                                  const TQString &leftACL, const TQString &rightACL, TaskType tsk, 
                                  bool isDir, bool isTemp )
{
  bool marked = autoScroll ? !isTemp && isMarked( tsk, existsLeft && existsRight ) : false;
  SynchronizerFileItem *item = new SynchronizerFileItem( leftFile, rightFile, leftDir, rightDir, marked,
    existsLeft, existsRight, leftSize, rightSize, leftDate, rightDate, leftLink, rightLink,
    leftOwner, rightOwner, leftGroup, rightGroup, leftMode, rightMode, leftACL, rightACL, tsk, isDir,
    isTemp, parent );

  if( !isTemp )
  {
    while( parent && parent->isTemporary() )
       setPermanent( parent );

    bool doRefresh = false;

    if( marked )
    {
      fileCount++;
      if( autoScroll && markParentDirectories( item ) )
        doRefresh = true;
    }

    resultList.append( item );
    emit comparedFileData( item );

    if( doRefresh )
      refresh( true );

    if( marked && (displayUpdateCount++ % DISPLAY_UPDATE_PERIOD == (DISPLAY_UPDATE_PERIOD-1) ) )
      tqApp->processEvents();
  }
  else
    temporaryList.append( item );

  return item;
}

void Synchronizer::compareContentResult( SynchronizerFileItem * item, bool res ) {
  item->compareContentResult( res );
  bool marked = autoScroll ? isMarked( item->task(), item->existsInLeft() && item->existsInRight() ) : false;
  item->setMarked( marked );
  if( marked ) {
    markParentDirectories( item );
    fileCount++;
    emit markChanged( item, true );
  }
}

void Synchronizer::setPermanent( SynchronizerFileItem *item )
{
  if( item->parent() && item->parent()->isTemporary() )
    setPermanent( item->parent() );

  item->setPermanent();
  resultList.append( item );
  emit comparedFileData( item );
}

SynchronizerFileItem * Synchronizer::addLeftOnlyItem( SynchronizerFileItem *parent,
                                    const TQString &file_name, const TQString &dir, TDEIO::filesize_t size, 
                                    time_t date, const TQString &link, const TQString &owner,
                                    const TQString &group, mode_t mode, const TQString &acl, bool isDir, 
                                    bool isTemp )
{
  return addItem( parent, file_name, file_name, dir, dir, true, false, size, 0, date, 0, link, TQString(),
                  owner, TQString(), group, TQString(), mode, (mode_t)-1, acl, TQString(),
                  asymmetric ? TT_DELETE : TT_COPY_TO_RIGHT, isDir, isTemp );
}

SynchronizerFileItem * Synchronizer::addRightOnlyItem( SynchronizerFileItem *parent,
                                    const TQString &file_name, const TQString &dir, TDEIO::filesize_t size,
                                    time_t date, const TQString &link, const TQString &owner,
                                    const TQString &group, mode_t mode, const TQString &acl, bool isDir,
                                    bool isTemp )
{
  return addItem( parent, file_name, file_name, dir, dir, false, true, 0, size, 0, date, TQString(), link,
                  TQString(), owner, TQString(), group, (mode_t)-1, mode, TQString(), acl,
                  TT_COPY_TO_LEFT, isDir, isTemp );
}

SynchronizerFileItem * Synchronizer::addDuplicateItem( SynchronizerFileItem *parent,
                                     const TQString &leftName, const TQString &rightName,
                                     const TQString &leftDir, const TQString &rightDir,
                                     TDEIO::filesize_t leftSize, TDEIO::filesize_t rightSize, time_t leftDate, time_t rightDate,
                                     const TQString &leftLink, const TQString &rightLink,
                                     const TQString &leftOwner, const TQString &rightOwner,
                                     const TQString &leftGroup, const TQString &rightGroup,
                                     mode_t leftMode, mode_t rightMode,
                                     const TQString &leftACL, const TQString &rightACL,
                                     bool isDir, bool isTemp )
{
  TaskType        task;

  int checkedRightDate = rightDate - timeOffset;
  int uncertain = 0;

  do
  {
    if( isDir )
    {
      task = TT_EQUALS;
      break;
    }
    if( leftSize == rightSize )
    {
      if( !leftLink.isNull() || !rightLink.isNull() ) {
        if( leftLink == rightLink ) {
          task = TT_EQUALS;
          break;
        }
      } else if( cmpByContent )
        uncertain = TT_UNKNOWN;
      else {
        if( ignoreDate || leftDate == checkedRightDate ) {
          task = TT_EQUALS;
          break;
        }
        time_t diff = ( leftDate > checkedRightDate ) ? leftDate - checkedRightDate : checkedRightDate - leftDate;
        if( diff <= equalsThreshold ) {
          task = TT_EQUALS;
          break;
        }
      }
    }

    if( asymmetric )
      task = TT_COPY_TO_LEFT;
    else if( ignoreDate )
      task = TT_DIFFERS;
    else if( leftDate > checkedRightDate )
      task = TT_COPY_TO_RIGHT;
    else if( leftDate < checkedRightDate )
      task = TT_COPY_TO_LEFT;
    else
      task = TT_DIFFERS;

  }while( false );

  SynchronizerFileItem * item = addItem( parent, leftName, rightName, leftDir, rightDir, true, true,
                                         leftSize, rightSize, leftDate, rightDate, leftLink, rightLink,
                                         leftOwner, rightOwner, leftGroup, rightGroup,
                                         leftMode, rightMode, leftACL, rightACL,
                                         (TaskType)(task + uncertain), isDir, isTemp );

  if( uncertain == TT_UNKNOWN ) {
    KURL leftURL  = vfs::fromPathOrURL( leftDir.isEmpty() ? leftBaseDir + leftName : leftBaseDir + leftDir + "/" + leftName );
    KURL rightURL = vfs::fromPathOrURL( rightDir.isEmpty() ? rightBaseDir + rightName : rightBaseDir + rightDir + "/" + rightName );
    stack.append( new CompareContentTask( this, item, leftURL, rightURL, leftSize ) );
  }

  return item;
}

void Synchronizer::addSingleDirectory( SynchronizerFileItem *parent, SynchronizerDirList *directory,
                                       const TQString &dirName, bool isLeft )
{
  const TQString &url = directory->url();
  vfile               * file;
  TQString file_name;

  /* walking through the directory files */
  for( file=directory->first(); file != 0 && !stopped; file = directory->next() )
  {
    if ( isDir( file ) )
      continue;

    file_name =  file->vfile_getName();

    if( !query->match( file ) )
      continue;

    if( isLeft )
      addLeftOnlyItem( parent, file_name, dirName, file->vfile_getSize(), file->vfile_getTime_t(), readLink( file ),
                       file->vfile_getOwner(), file->vfile_getGroup(), file->vfile_getMode(), file->vfile_getACL() );
    else
      addRightOnlyItem( parent, file_name, dirName, file->vfile_getSize(), file->vfile_getTime_t(), readLink( file ),
                        file->vfile_getOwner(), file->vfile_getGroup(), file->vfile_getMode(), file->vfile_getACL() );
  }

  /* walking through the subdirectories */
  for( file=directory->first(); file != 0 && !stopped; file=directory->next() )
  {
    if ( file->vfile_isDir() && (followSymLinks || !file->vfile_isSymLink())  )
    {
        file_name =  file->vfile_getName();

        if( excludedPaths.contains( dirName.isEmpty() ? file_name : dirName+"/"+file_name ) )
          continue;

        if( !query->matchDirName( file_name ) )
          continue;

        SynchronizerFileItem *me;

        if( isLeft )
          me = addLeftOnlyItem( parent, file_name, dirName, 0, file->vfile_getTime_t(), readLink( file ),
                                file->vfile_getOwner(), file->vfile_getGroup(), file->vfile_getMode(),
                                file->vfile_getACL(), true, !query->match( file ) );
        else
          me = addRightOnlyItem( parent, file_name, dirName, 0, file->vfile_getTime_t(), readLink( file ),
                                 file->vfile_getOwner(), file->vfile_getGroup(), file->vfile_getMode(),
                                 file->vfile_getACL(), true, !query->match( file ) );
        stack.append( new CompareTask( me, url+file_name+"/", 
                            dirName.isEmpty() ? file_name : dirName+"/"+file_name, isLeft, ignoreHidden ) );
    }
  }
}

void Synchronizer::setMarkFlags( bool left, bool equal, bool differs, bool right, bool dup, bool sing,
                                 bool del )
{
  markEquals      = equal;
  markDiffers     = differs;
  markCopyToLeft  = left;
  markCopyToRight = right;
  markDeletable   = del;
  markDuplicates  = dup;
  markSingles     = sing;
}

bool Synchronizer::isMarked( TaskType task, bool isDuplicate )
{
  if( (isDuplicate && !markDuplicates) || (!isDuplicate && !markSingles) )
    return false;

  switch( task )
  {
  case TT_EQUALS:
    return markEquals;
  case TT_DIFFERS:
    return markDiffers;
  case TT_COPY_TO_LEFT:
    return markCopyToLeft;
  case TT_COPY_TO_RIGHT:
    return markCopyToRight;
  case TT_DELETE:
    return markDeletable;
  default:
    return false;
  }
}

bool Synchronizer::markParentDirectories( SynchronizerFileItem *item )
{
  if( item->parent() == 0 || item->parent()->isMarked() )
    return false;

  markParentDirectories( item->parent() );

  item->parent()->setMarked( true );

  fileCount++;
  emit markChanged( item->parent(), false );
  return true;
}

int Synchronizer::refresh(bool nostatus)
{
  fileCount = 0;

  SynchronizerFileItem *item = resultList.first();

  while( item )
  {
    bool marked = isMarked( item->task(), item->existsInLeft() && item->existsInRight() );
    item->setMarked( marked );

    if( marked )
    {
      markParentDirectories( item );
      fileCount++;
    }

    item = resultList.next();
  }

  item = resultList.first();
  while( item )
  {
    emit markChanged( item, false );
    item = resultList.next();
  }

  if( !nostatus )
    emit statusInfo( i18n( "Number of files: %1" ).arg( fileCount ) );

  return fileCount;
}

void Synchronizer::operate( SynchronizerFileItem *item,
                            void (*executeOperation)(SynchronizerFileItem *) )
{
  executeOperation( item );

  if( item->isDir() )
  {
    TQString leftDirName = ( item->leftDirectory() == "" ) ?
                        item->leftName() : item->leftDirectory() + "/" + item->leftName() ;
    TQString rightDirName = ( item->rightDirectory() == "" ) ?
                        item->rightName() : item->rightDirectory() + "/" + item->rightName() ;

    item = resultList.first();
    while( item )
    {
      if( item->leftDirectory() == leftDirName || item->leftDirectory().startsWith( leftDirName + "/" ) ||
          item->rightDirectory() == rightDirName || item->rightDirectory().startsWith( rightDirName + "/" ) )
        executeOperation( item );

      item = resultList.next();
    }
  }
}

void Synchronizer::excludeOperation( SynchronizerFileItem *item )
{
  item->setTask( TT_DIFFERS );
}

void Synchronizer::exclude( SynchronizerFileItem *item )
{
  if( !item->parent() || item->parent()->task() != TT_DELETE )
    operate( item, excludeOperation );  /* exclude only if the parent task is not DEL */
}

void Synchronizer::restoreOperation( SynchronizerFileItem *item )
{
  item->restoreOriginalTask();
}

void Synchronizer::restore( SynchronizerFileItem *item )
{
  operate( item, restoreOperation );

  while( ( item = item->parent() ) != 0 ) /* in case of restore, the parent directories */
  {                                          /* must be changed for being consistent */
    if( item->task() != TT_DIFFERS )
      break;

    if( item->originalTask() == TT_DELETE ) /* if the parent original task is delete */
      break;                                    /* don't touch it */

    item->restoreOriginalTask();          /* restore */
  }
}

void Synchronizer::reverseDirectionOperation( SynchronizerFileItem *item )
{
  if( item->existsInRight() && item->existsInLeft() )
  {
    if( item->task() == TT_COPY_TO_LEFT )
      item->setTask( TT_COPY_TO_RIGHT );
    else if( item->task() == TT_COPY_TO_RIGHT )
      item->setTask( TT_COPY_TO_LEFT );
  }
}

void Synchronizer::reverseDirection( SynchronizerFileItem *item )
{
  operate( item, reverseDirectionOperation );
}

void Synchronizer::deleteLeftOperation( SynchronizerFileItem *item )
{
  if( !item->existsInRight() && item->existsInLeft() )
    item->setTask( TT_DELETE );
}

void Synchronizer::deleteLeft( SynchronizerFileItem *item )
{
  operate( item, deleteLeftOperation );
}

void Synchronizer::copyToLeftOperation( SynchronizerFileItem *item )
{
  if( item->existsInRight() )
  {
    if( !item->isDir() )
      item->setTask( TT_COPY_TO_LEFT );
    else
    {
      if( item->existsInLeft() && item->existsInRight() )
        item->setTask( TT_EQUALS );
      else if( !item->existsInLeft() && item->existsInRight() )
        item->setTask( TT_COPY_TO_LEFT );
    }
  }
}

void Synchronizer::copyToLeft( SynchronizerFileItem *item )
{
  operate( item, copyToLeftOperation );

  while( ( item = item->parent() ) != 0 )
  {
    if( item->task() != TT_DIFFERS )
      break;

    if( item->existsInLeft() && item->existsInRight() )
      item->setTask( TT_EQUALS );
    else if( !item->existsInLeft() && item->existsInRight() )
      item->setTask( TT_COPY_TO_LEFT );
  }
}

void Synchronizer::copyToRightOperation( SynchronizerFileItem *item )
{
  if( item->existsInLeft() )
  {
    if( !item->isDir() )
      item->setTask( TT_COPY_TO_RIGHT );
    else
    {
      if( item->existsInLeft() && item->existsInRight() )
        item->setTask( TT_EQUALS );
      else if( item->existsInLeft() && !item->existsInRight() )
        item->setTask( TT_COPY_TO_RIGHT );
    }
  }
}

void Synchronizer::copyToRight( SynchronizerFileItem *item )
{
  operate( item, copyToRightOperation );

  while( ( item = item->parent() ) != 0 )
  {
    if( item->task() != TT_DIFFERS && item->task() != TT_DELETE )
      break;

    if( item->existsInLeft() && item->existsInRight() )
      item->setTask( TT_EQUALS );
    else if( item->existsInLeft() && !item->existsInRight() )
      item->setTask( TT_COPY_TO_RIGHT );
  }
}

bool Synchronizer::totalSizes( int * leftCopyNr, TDEIO::filesize_t *leftCopySize, int * rightCopyNr,
                               TDEIO::filesize_t *rightCopySize, int *deleteNr, TDEIO::filesize_t *deletableSize )
{
  bool hasAnythingToDo = false;

  *leftCopySize = *rightCopySize = *deletableSize = 0;
  *leftCopyNr = *rightCopyNr = *deleteNr = 0;

  SynchronizerFileItem *item = resultList.first();

  while( item )
  {
    if( item->isMarked() )
    {
      switch( item->task() )
      {
      case TT_COPY_TO_LEFT:
        *leftCopySize += item->rightSize();
        (*leftCopyNr)++;
        hasAnythingToDo = true;
        break;
      case TT_COPY_TO_RIGHT:
        *rightCopySize += item->leftSize();
        (*rightCopyNr)++;
        hasAnythingToDo = true;
        break;
      case TT_DELETE:
        *deletableSize += item->leftSize();
        (*deleteNr)++;
        hasAnythingToDo = true;
        break;
      default:
        break;
      }
    }
    item = resultList.next();
  }

  return hasAnythingToDo;
}

void Synchronizer::swapSides()
{
  TQString leftTmp = leftBaseDir;
  leftBaseDir = rightBaseDir;
  rightBaseDir = leftTmp;

  SynchronizerFileItem *item = resultList.first();

  while( item )
  {
    item->swap( asymmetric );
    item = resultList.next();
  }
}

void Synchronizer::setScrolling( bool scroll )
{
  if( autoScroll = scroll )
  {
    int oldFileCount = fileCount;
    refresh( true );
    fileCount = oldFileCount;
  }
}

void Synchronizer::synchronize( TQWidget *syncWdg, bool leftCopyEnabled, bool rightCopyEnabled,
                                bool deleteEnabled, bool overWrite, int parThreads )
{
    this->leftCopyEnabled   = leftCopyEnabled;
    this->rightCopyEnabled  = rightCopyEnabled;
    this->deleteEnabled     = deleteEnabled;
    this->overWrite         = overWrite;
    this->parallelThreads   = parThreads;
    this->syncDlgWidget     = syncWdg;

    autoSkip = paused = disableNewTasks = false;

    leftCopyNr = rightCopyNr = deleteNr = 0;
    leftCopySize = rightCopySize = deleteSize = 0;

    inTaskFinished = 0;
    lastTask = 0;

    jobMap.clear();
    receivedMap.clear();

    resultList.first();
    synchronizeLoop();
}

void Synchronizer::synchronizeLoop() {
  if( disableNewTasks ) {
    if( resultList.current() == 0 && jobMap.count() == 0 )
      emit synchronizationFinished();
    return;
  }

  while( (int)jobMap.count() < parallelThreads ) {
    SynchronizerFileItem *task = getNextTask();
    if( task == 0 ) {
      if( jobMap.count() == 0 )
        emit synchronizationFinished();
      return;
    }
    executeTask( task );
    if( disableNewTasks )
      break;
  }
}

SynchronizerFileItem * Synchronizer::getNextTask() {
  TaskType task;
  SynchronizerFileItem * currentTask = resultList.current();

  do {
    if( currentTask == 0 )
      return 0;

    if( currentTask->isMarked() )
    {
      task = currentTask->task();

      if( leftCopyEnabled && task == TT_COPY_TO_LEFT )
        break;
      else if( rightCopyEnabled && task == TT_COPY_TO_RIGHT )
        break;
      else if( deleteEnabled && task == TT_DELETE )
        break;
    }

    currentTask = resultList.next();
  }while( true );

  resultList.next();
  return lastTask = currentTask;
}


void Synchronizer::executeTask( SynchronizerFileItem * task )
{
  TQString leftDirName = task->leftDirectory();
  if( !leftDirName.isEmpty() )
    leftDirName += "/";
  TQString rightDirName = task->rightDirectory();
  if( !rightDirName.isEmpty() )
    rightDirName += "/";

  KURL leftURL  = vfs::fromPathOrURL( leftBaseDir + leftDirName + task->leftName() );
  KURL rightURL = vfs::fromPathOrURL( rightBaseDir + rightDirName + task->rightName() );

  switch( task->task() )
  {
  case TT_COPY_TO_LEFT:
    if( task->isDir() )
    {
      TDEIO::SimpleJob *job = TDEIO::mkdir( leftURL );
      connect(job,TQ_SIGNAL(result(TDEIO::Job*)),this,TQ_SLOT(slotTaskFinished(TDEIO::Job*)));
      jobMap[ job ] = task;
      disableNewTasks = true;
    }
    else
    {
      KURL destURL( leftURL );
      if( !task->destination().isNull() )
        destURL = vfs::fromPathOrURL( task->destination() );

      if( task->rightLink().isNull() ) {
        TDEIO::FileCopyJob *job = TDEIO::file_copy(rightURL, destURL, -1,
                                  overWrite || task->overWrite(), false, false );
        connect(job,TQ_SIGNAL(processedSize (TDEIO::Job *, TDEIO::filesize_t )), this,
                    TQ_SLOT  (slotProcessedSize (TDEIO::Job *, TDEIO::filesize_t )));
        connect(job,TQ_SIGNAL(result(TDEIO::Job*)),this,TQ_SLOT(slotTaskFinished(TDEIO::Job*)));
        jobMap[ job ] = task;
      } else {
        TDEIO::SimpleJob *job = TDEIO::symlink( task->rightLink(), destURL,
                                            overWrite || task->overWrite(), false );
        connect(job,TQ_SIGNAL(result(TDEIO::Job*)),this,TQ_SLOT(slotTaskFinished(TDEIO::Job*)));
        jobMap[ job ] = task;
      }
    }
    break;
  case TT_COPY_TO_RIGHT:
    if( task->isDir() )
    {
      TDEIO::SimpleJob *job = TDEIO::mkdir( rightURL );
      connect(job,TQ_SIGNAL(result(TDEIO::Job*)),this,TQ_SLOT(slotTaskFinished(TDEIO::Job*)));
      jobMap[ job ] = task;
      disableNewTasks = true;
    }
    else
    {
      KURL destURL( rightURL );
      if( !task->destination().isNull() )
        destURL = vfs::fromPathOrURL( task->destination() );

      if( task->leftLink().isNull() ) {
        TDEIO::FileCopyJob *job = TDEIO::file_copy(leftURL, destURL, -1,
                                  overWrite || task->overWrite(), false, false );
        connect(job,TQ_SIGNAL(processedSize (TDEIO::Job *, TDEIO::filesize_t )), this,
                    TQ_SLOT  (slotProcessedSize (TDEIO::Job *, TDEIO::filesize_t )));
        connect(job,TQ_SIGNAL(result(TDEIO::Job*)),this,TQ_SLOT(slotTaskFinished(TDEIO::Job*)));
        jobMap[ job ] = task;
      } else {
        TDEIO::SimpleJob *job = TDEIO::symlink( task->leftLink(), destURL,
                                            overWrite || task->overWrite(), false );
        connect(job,TQ_SIGNAL(result(TDEIO::Job*)),this,TQ_SLOT(slotTaskFinished(TDEIO::Job*)));
        jobMap[ job ] = task;
      }
    }
    break;
  case TT_DELETE:
    {
      TDEIO::DeleteJob *job = TDEIO::del( leftURL, false );
      connect(job,TQ_SIGNAL(result(TDEIO::Job*)),this,TQ_SLOT(slotTaskFinished(TDEIO::Job*)));
      jobMap[ job ] = task;
    }
    break;
  default:
    break;
  }
}

void Synchronizer::slotTaskFinished(TDEIO::Job *job )
{
  inTaskFinished++;

  SynchronizerFileItem * item = jobMap[ job ];
  jobMap.remove( job );

  TDEIO::filesize_t receivedSize = 0;

  if( receivedMap.contains( job ) ) {
    receivedSize = receivedMap[ job ];
    receivedMap.remove( job );
  }

  if( disableNewTasks && item == lastTask )
    disableNewTasks = false; // the blocker task finished

  TQString leftDirName     = item->leftDirectory().isEmpty() ? "" : item->leftDirectory() + "/";
  TQString rightDirName     = item->rightDirectory().isEmpty() ? "" : item->rightDirectory() + "/";
  KURL leftURL  = vfs::fromPathOrURL( leftBaseDir + leftDirName + item->leftName() );
  KURL rightURL = vfs::fromPathOrURL( rightBaseDir + rightDirName + item->rightName() );

  do {
    if( !job->error() )
    {
      switch( item->task() )
      {
      case TT_COPY_TO_LEFT:
        if( leftURL.isLocalFile() )
        {
          struct utimbuf timestamp;

          timestamp.actime = time( 0 );
          timestamp.modtime = item->rightDate() - timeOffset;

          utime( (const char *)( leftURL.path( -1 ).local8Bit() ), &timestamp );

          uid_t newOwnerID = (uid_t)-1; // chown(2) : -1 means no change
          if ( !item->rightOwner().isEmpty() )
          {
            struct passwd* pw = getpwnam(TQFile::encodeName(item->rightOwner()));
            if ( pw != 0L )
              newOwnerID = pw->pw_uid;
          }
          gid_t newGroupID = (gid_t)-1; // chown(2) : -1 means no change
          if ( !item->rightGroup().isEmpty() )
          {
            struct group* g = getgrnam(TQFile::encodeName(item->rightGroup()));
            if ( g != 0L )
              newGroupID = g->gr_gid;
          }
          chown( (const char *)( leftURL.path( -1 ).local8Bit() ), newOwnerID, (gid_t)-1 );
          chown( (const char *)( leftURL.path( -1 ).local8Bit() ), (uid_t)-1, newGroupID );

          chmod( (const char *)( leftURL.path( -1 ).local8Bit() ), item->rightMode() & 07777 );

#if defined( HAVE_POSIX_ACL )
          if( !item->rightACL().isNull() )
          {
            acl_t acl = acl_from_text( item->rightACL().latin1() );
            if( acl && !acl_valid( acl ) )
              acl_set_file( leftURL.path( -1 ).local8Bit(), ACL_TYPE_ACCESS, acl );
            if( acl )
              acl_free( acl );
          }
#endif
        }
        break;
      case TT_COPY_TO_RIGHT:
        if( rightURL.isLocalFile() )
        {
          struct utimbuf timestamp;

          timestamp.actime = time( 0 );
          timestamp.modtime = item->leftDate() + timeOffset;

          utime( (const char *)( rightURL.path( -1 ).local8Bit() ), &timestamp );

          uid_t newOwnerID = (uid_t)-1; // chown(2) : -1 means no change
          if ( !item->leftOwner().isEmpty() )
          {
            struct passwd* pw = getpwnam(TQFile::encodeName(item->leftOwner()));
            if ( pw != 0L )
              newOwnerID = pw->pw_uid;
          }
          gid_t newGroupID = (gid_t)-1; // chown(2) : -1 means no change
          if ( !item->leftGroup().isEmpty() )
          {
            struct group* g = getgrnam(TQFile::encodeName(item->leftGroup()));
            if ( g != 0L )
              newGroupID = g->gr_gid;
          }
          chown( (const char *)( rightURL.path( -1 ).local8Bit() ), newOwnerID, (uid_t)-1 );
          chown( (const char *)( rightURL.path( -1 ).local8Bit() ), (uid_t)-1, newGroupID );

          chmod( (const char *)( rightURL.path( -1 ).local8Bit() ), item->leftMode() & 07777 );

#if defined( HAVE_POSIX_ACL )
          if( !item->leftACL().isNull() )
          {
            acl_t acl = acl_from_text( item->leftACL().latin1() );
            if( acl && !acl_valid( acl ) )
              acl_set_file( rightURL.path( -1 ).local8Bit(), ACL_TYPE_ACCESS, acl );
            if( acl )
              acl_free( acl );
          }
#endif
        }
        break;
      default:
        break;
      }
    }
    else
    {
      if( job->error() == TDEIO::ERR_FILE_ALREADY_EXIST && item->task() != TT_DELETE )
      {
        TDEIO::RenameDlg_Result result;
        TQString newDest;

        if( autoSkip )
          break;

        if ( item->task() == TT_COPY_TO_LEFT )
        {
          TQWidget *mainWidget = tqApp->mainWidget(); // WORKAROUND, don't give focus to the main widget
          tqApp->setMainWidget( syncDlgWidget );

          result = Observer::self()->open_RenameDlg ( job, i18n("File Already Exists"),
            vfs::pathOrURL( rightURL ), vfs::pathOrURL( leftURL ),
            (TDEIO::RenameDlg_Mode)( TDEIO::M_OVERWRITE | TDEIO::M_SKIP | TDEIO::M_MULTI ), newDest,
            item->rightSize(), item->leftSize(), (time_t)-1, (time_t)-1,
            item->rightDate(), item->leftDate());

          tqApp->setMainWidget( mainWidget );
        }
        else
        {
          TQWidget *mainWidget = tqApp->mainWidget(); // WORKAROUND, don't give focus to the main widget
          tqApp->setMainWidget( syncDlgWidget );

          result = Observer::self()->open_RenameDlg ( job, i18n("File Already Exists"),
            vfs::pathOrURL( leftURL ), vfs::pathOrURL( rightURL ),
            (TDEIO::RenameDlg_Mode)( TDEIO::M_OVERWRITE | TDEIO::M_SKIP | TDEIO::M_MULTI ), newDest,
            item->leftSize(), item->rightSize(), (time_t)-1, (time_t)-1,
            item->leftDate(), item->rightDate());

          tqApp->setMainWidget( mainWidget );
        }

        switch ( result )
        {
        case TDEIO::R_RENAME:
          item->setDestination( newDest );
          executeTask( item );
          inTaskFinished--;
          return;
        case TDEIO::R_OVERWRITE:
          item->setOverWrite();
          executeTask( item );
          inTaskFinished--;
          return;
        case TDEIO::R_OVERWRITE_ALL:
          overWrite = true;
          executeTask( item );
          inTaskFinished--;
          return;
        case TDEIO::R_AUTO_SKIP:
          autoSkip = true;
        case TDEIO::R_SKIP:
        default:
          break;
        }
        break;
      }

      if( job->error() != TDEIO::ERR_DOES_NOT_EXIST || item->task() != TT_DELETE )
      {
        if( autoSkip )
          break;

        TQString error;

        switch( item->task() )
        {
        case TT_COPY_TO_LEFT:
          error = i18n("Error at copying file %1 to %2!")
                       .arg( vfs::pathOrURL( rightURL ) )
                       .arg( vfs::pathOrURL( leftURL ) );
          break;
        case TT_COPY_TO_RIGHT:
          error = i18n("Error at copying file %1 to %2!")
                       .arg( vfs::pathOrURL( leftURL ) )
                       .arg( vfs::pathOrURL( rightURL ) );
          break;
        case TT_DELETE:
          error = i18n("Error at deleting file %1!").arg( vfs::pathOrURL( leftURL ) );
          break;
        default:
          break;
        }

        TQWidget *mainWidget = tqApp->mainWidget(); // WORKAROUND, don't give focus to the main widget
        tqApp->setMainWidget( syncDlgWidget );

        TDEIO::SkipDlg_Result result = Observer::self()->open_SkipDlg( job, true, error );

        tqApp->setMainWidget( mainWidget );

        switch( result )
        {
        case TDEIO::S_CANCEL:
          executeTask( item );  /* simply retry */
          inTaskFinished--;
          return;
        case TDEIO::S_AUTO_SKIP:
          autoSkip = true;
        default:
          break;
        }
      }
    }
  }while( false );

  switch( item->task() )
  {
  case TT_COPY_TO_LEFT:
    leftCopyNr++;
    leftCopySize += item->rightSize() - receivedSize;
    break;
  case TT_COPY_TO_RIGHT:
    rightCopyNr++;
    rightCopySize += item->leftSize() - receivedSize;
    break;
  case TT_DELETE:
    deleteNr++;
    deleteSize += item->leftSize() - receivedSize;
    break;
  default:
    break;
  }

  emit processedSizes( leftCopyNr, leftCopySize, rightCopyNr, rightCopySize, deleteNr, deleteSize );

  if( --inTaskFinished == 0 ) {
    if( paused )
      emit pauseAccepted();
    else
      synchronizeLoop();
  }
}

void Synchronizer::slotProcessedSize( TDEIO::Job * job , TDEIO::filesize_t size)
{
  TDEIO::filesize_t dl = 0, dr = 0, dd = 0;
  SynchronizerFileItem * item = jobMap[ job ];

  TDEIO::filesize_t lastProcessedSize = 0;
  if( receivedMap.contains( job ) )
    lastProcessedSize = receivedMap[ job ];

  receivedMap[ job ] = size;

  switch( item->task() )
  {
  case TT_COPY_TO_LEFT:
    dl = size - lastProcessedSize;
    break;
  case TT_COPY_TO_RIGHT:
    dr = size - lastProcessedSize;
    break;
  case TT_DELETE:
    dd = size - lastProcessedSize;
    break;
  default:
    break;
  }

  emit processedSizes( leftCopyNr, leftCopySize+=dl, rightCopyNr, rightCopySize+=dr, deleteNr, deleteSize+=dd );
}

void Synchronizer::pause()
{
  paused = true;
}

void Synchronizer::resume()
{
  paused = false;
  synchronizeLoop();
}

TQString Synchronizer::leftBaseDirectory()
{
  return leftBaseDir;
}

TQString Synchronizer::rightBaseDirectory()
{
  return rightBaseDir;
}

class KgetProgressDialog : public KDialogBase
{
public:
  KgetProgressDialog( TQWidget *parent=0, const char *name=0, const TQString &caption=TQString(),
                    const TQString &text=TQString(), bool modal=false) : KDialogBase( KDialogBase::Plain,
                    caption, KDialogBase::User1 | KDialogBase::Cancel, KDialogBase::Cancel, parent, name, modal )
  {
    showButton(KDialogBase::Close, false);

    TQFrame* mainWidget = plainPage();
    TQVBoxLayout* layout = new TQVBoxLayout(mainWidget, 10);

    TQLabel *mLabel = new TQLabel(text, mainWidget);
    layout->addWidget(mLabel);

    mProgressBar = new KProgress(mainWidget);
    layout->addWidget(mProgressBar);

    setButtonText( KDialogBase::User1, i18n( "Pause" ) );

    mCancelled = mPaused = false;
  }

  KProgress *progressBar() { return mProgressBar; }

  void slotUser1()
  {
    if( ( mPaused = !mPaused ) == false )
      setButtonText( KDialogBase::User1, i18n( "Pause" ) );
    else
      setButtonText( KDialogBase::User1, i18n( "Resume" ) );
  }

  void slotCancel()
  {
    mCancelled = true;

    KDialogBase::slotCancel();
  }

  bool wasCancelled()      { return mCancelled; }
  bool isPaused()          { return mPaused; }

private:
  KProgress *mProgressBar;
  bool       mCancelled;
  bool       mPaused;
};


void Synchronizer::synchronizeWithKGet()
{
  bool isLeftLocal = vfs::fromPathOrURL( leftBaseDirectory() ).isLocalFile();
  KgetProgressDialog *progDlg = 0;
  int  processedCount = 0, totalCount = 0;

  SynchronizerFileItem *item = resultList.first();
  for(; item; item = resultList.next() )
    if( item->isMarked() )
      totalCount++;

  item = resultList.first();
  while( item )
  {
    if( item->isMarked() )
    {
      KURL downloadURL, destURL;
      TQString leftDirName     = item->leftDirectory().isEmpty() ? "" : item->leftDirectory() + "/";
      TQString rightDirName    = item->rightDirectory().isEmpty() ? "" : item->rightDirectory() + "/";
      TQString destDir;

      if( progDlg == 0 )
      {
        progDlg = new KgetProgressDialog( krApp, "Synchronizer Progress Dlg", i18n("Krusader::Synchronizer"),
                                          i18n( "Feeding the URLs to Kget" ), true );
        progDlg->progressBar()->setTotalSteps( totalCount );
        progDlg->show();
        tqApp->processEvents();
      }

      if( item->task() == TT_COPY_TO_RIGHT && !isLeftLocal )
      {
        downloadURL = vfs::fromPathOrURL( leftBaseDirectory() + leftDirName + item->leftName() );
        destDir     = rightBaseDirectory() + rightDirName;
        destURL     = vfs::fromPathOrURL( destDir + item->rightName() );

        if( item->isDir() )
          destDir += item->leftName();
      }
      if( item->task() == TT_COPY_TO_LEFT && isLeftLocal )
      {
        downloadURL = vfs::fromPathOrURL( rightBaseDirectory() + rightDirName + item->rightName() );
        destDir     = leftBaseDirectory() + leftDirName;
        destURL     = vfs::fromPathOrURL( destDir + item->leftName() );

        if( item->isDir() )
          destDir += item->rightName();
      }

      // creating the directory system
      for( int i=0; i >= 0 ; i= destDir.find('/',i+1) )
        if( !TQDir( destDir.left(i) ).exists() )
          TQDir().mkdir( destDir.left(i) );

      if( !item->isDir() && !downloadURL.isEmpty() )
      {
        if( TQFile( destURL.path() ).exists() )
          TQFile( destURL.path() ).remove();

        TQString source = downloadURL.prettyURL();
        if( source.contains( '@' ) >= 2 ) /* is this an ftp proxy URL? */
        {
          int lastAt = source.findRev( '@' );
          TQString startString = source.left( lastAt );
          TQString endString = source.mid( lastAt );
          startString.replace( "@", "%40" );
          source = startString+endString;
        }

        TDEProcess p;

        p << KrServices::fullPathName( "kget" ) << source << destURL.path();
        if (!p.start(TDEProcess::Block))
          KMessageBox::error(parentWidget,i18n("Error executing ")+KrServices::fullPathName( "kget" )+" !");
        else
          p.detach();
      }

      progDlg->progressBar()->setProgress( ++processedCount );

      do
      {
        tqApp->processEvents();

        if( progDlg->wasCancelled() )
          break;

        if( progDlg->isPaused() )
          usleep( 100000 );

      }while( progDlg->isPaused() );

      if( progDlg->wasCancelled() )
        break;
    }
    item = resultList.next();
  }

  if( progDlg )
    delete progDlg;
}

bool Synchronizer::isDir( const vfile * file ) {
  if( followSymLinks ) {
    return file->vfile_isDir();
  }
  else {
    return file->vfile_isDir() && !file->vfile_isSymLink();
  }
}

TQString Synchronizer::readLink( const vfile * file ) {
  if( file->vfile_isSymLink() )
    return file->vfile_getSymDest();
  else
    return TQString();
}

#include "synchronizer.moc"
