You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
krusader/krusader/Synchronizer/synchronizer.cpp

1494 lines
48 KiB

/***************************************************************************
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 KDE_IS_VERSION(3,5,0) && 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 KDE_IS_VERSION(3,5,0) && 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 KDE_IS_VERSION(3,5,0) && 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"