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.
smb4k/smb4k/core/smb4ksynchronizer.cpp

504 lines
15 KiB

/***************************************************************************
smb4ksynchronizer - This is the synchronizer of Smb4K.
-------------------
begin : Mo Jul 4 2005
copyright : (C) 2005-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 <tqlayout.h>
#include <tqdir.h>
#include <tqlabel.h>
#include <tqregexp.h>
// KDE includes
#include <kmessagebox.h>
#include <kdebug.h>
#include <kdialogbase.h>
#include <klineedit.h>
#include <klocale.h>
#include <kprogress.h>
#include <kurlrequester.h>
#include <kguiitem.h>
#include <kapplication.h>
// application specific includes
#include "smb4ksynchronizer.h"
#include "smb4kdefs.h"
#include "smb4kerror.h"
#include "smb4kglobal.h"
#include "smb4kshare.h"
#include "smb4ksynchronizationinfo.h"
#include "smb4ksettings.h"
using namespace Smb4KGlobal;
bool cancel = false;
Smb4KSynchronizer::Smb4KSynchronizer( TQObject *parent, const char *name )
: TQObject( parent, name )
{
m_proc = new KProcess( this, "SynchronizerProcess" );
m_proc->setUseShell( true );
m_working = false;
connect( m_proc, TQT_SIGNAL( receivedStdout( KProcess *, char *, int ) ),
this, TQT_SLOT( slotReceivedStdout( KProcess *, char *, int ) ) );
connect( m_proc, TQT_SIGNAL( processExited( KProcess* ) ),
this, TQT_SLOT( slotProcessExited( KProcess * ) ) );
connect( m_proc, TQT_SIGNAL( receivedStderr( KProcess *, char *, int ) ),
this, TQT_SLOT( slotReceivedStderr( KProcess *, char *, int ) ) );
connect( kapp, TQT_SIGNAL( shutDown() ),
this, TQT_SLOT( slotShutdown() ) );
}
Smb4KSynchronizer::~Smb4KSynchronizer()
{
}
/****************************************************************************
Synchronizes a share with a local copy or vice versa.
****************************************************************************/
void Smb4KSynchronizer::synchronize( const TQString &source, const TQString &destination )
{
if ( Smb4KSettings::rsync().isEmpty() )
{
Smb4KError::error( ERROR_COMMAND_NOT_FOUND, "rsync" );
return;
}
// FIXME: Do not stop here but buffer the requests and
// process them if the previous process finished.
if ( isRunning() )
{
return;
}
m_working = true;
emit state( SYNCHRONIZER_START );
emit start();
// We will only define the stuff we urgently need
// here. The options that actually influence rsync's
// behavior will be retrieved by readRsyncOptions().
TQString command = "rsync --progress ";
command.append( readRsyncOptions() );
command.append( " " );
command.append( KProcess::quote( source ) );
command.append( " " );
command.append( KProcess::quote( destination ) );
*m_proc << command;
// Use KProcess::OwnGroup instead of KProcess::NotifyOnExit here, because
// this garantees that the process is indeed killed when using abort().
// See KProcess docs for further information.
m_proc->start( KProcess::OwnGroup, KProcess::AllOutput );
}
/****************************************************************************
Reads the options that should be used with rsync
****************************************************************************/
const TQString Smb4KSynchronizer::readRsyncOptions()
{
TQString options;
if ( Smb4KSettings::archiveMode() )
{
options.append( " --archive" );
}
else
{
options.append( Smb4KSettings::recurseIntoDirectories() ?
" --recursive" :
"" );
options.append( Smb4KSettings::preserveSymlinks() ?
" --links" :
"" );
options.append( Smb4KSettings::preservePermissions() ?
" --perms" :
"" );
options.append( Smb4KSettings::preserveTimes() ?
" --times" :
"" );
options.append( Smb4KSettings::preserveGroup() ?
" --group" :
"" );
options.append( Smb4KSettings::preserveOwner() ?
" --owner" :
"" );
options.append( Smb4KSettings::preserveDevicesAndSpecials() ?
" --devices --specials" : // alias: -D
"" );
}
options.append( Smb4KSettings::relativePathNames() ?
" --relative" :
"" );
options.append( Smb4KSettings::omitDirectoryTimes() ?
" --omit-dir-times" :
"" );
options.append( Smb4KSettings::noImpliedDirectories() ?
" --no-implied-dirs" :
"" );
options.append( Smb4KSettings::updateTarget() ?
" --update" :
"" );
options.append( Smb4KSettings::updateInPlace() ?
" --inplace" :
"" );
options.append( Smb4KSettings::transferDirectories() ?
" --dirs" :
"" );
options.append( Smb4KSettings::transformSymlinks() ?
" --copy-links" :
"" );
options.append( Smb4KSettings::transformUnsafeSymlinks() ?
" --copy-unsafe-links" :
"" );
options.append( Smb4KSettings::ignoreUnsafeSymlinks() ?
" --safe-links" :
"" );
options.append( Smb4KSettings::preserveHardLinks() ?
" --hard-links" :
"" );
options.append( Smb4KSettings::keepDirectorySymlinks() ?
" --keep-dirlinks" :
"" );
options.append( Smb4KSettings::deleteExtraneous() ?
" --delete" :
"" );
options.append( Smb4KSettings::removeSourceFiles() ?
" --remove-source-files" :
"" );
options.append( Smb4KSettings::deleteBefore() ?
" --delete-before" :
"" );
options.append( Smb4KSettings::deleteDuring() ?
" --delete-during" :
"" );
options.append( Smb4KSettings::deleteAfter() ?
" --delete-after" :
"" );
options.append( Smb4KSettings::deleteExcluded() ?
" --delete-excluded" :
"" );
options.append( Smb4KSettings::ignoreErrors() ?
" --ignore-errors" :
"" );
options.append( Smb4KSettings::forceDirectoryDeletion() ?
" --force" :
"" );
options.append( Smb4KSettings::copyFilesWhole() ?
" --whole-file" :
"" );
options.append( Smb4KSettings::efficientSparseFileHandling() ?
" --sparse" :
"" );
options.append( Smb4KSettings::oneFileSystem() ?
" --one-file-system" :
"" );
options.append( Smb4KSettings::updateExisting() ?
" --existing" :
"" );
options.append( Smb4KSettings::ignoreExisting() ?
" --ignore-existing" :
"" );
options.append( Smb4KSettings::delayUpdates() ?
" --delay-updates" :
"" );
options.append( Smb4KSettings::compressData() ?
" --compress" :
"" );
if ( Smb4KSettings::makeBackups() )
{
options.append( " --backup" );
options.append( Smb4KSettings::useBackupDirectory() ?
" --backup-dir="+Smb4KSettings::backupDirectory() :
"" );
options.append( Smb4KSettings::useBackupSuffix() ?
" --suffix="+Smb4KSettings::backupSuffix() :
"" );
}
options.append( Smb4KSettings::useMaximumDelete() ?
" --max-delete="+TQString( "%1" ).arg( Smb4KSettings::maximumDeleteValue() ) :
"" );
options.append( Smb4KSettings::useChecksum() ?
" --checksum" :
"" );
options.append( Smb4KSettings::useBlockSize() ?
" --block-size="+TQString( "%1" ).arg( Smb4KSettings::blockSize() ) :
"" );
options.append( Smb4KSettings::useChecksumSeed() ?
" --checksum-seed="+TQString( "%1" ).arg( Smb4KSettings::checksumSeed() ) :
"" );
if ( !Smb4KSettings::customFilteringRules().isEmpty() )
{
options.append( " "+Smb4KSettings::customFilteringRules() );
}
options.append( Smb4KSettings::useMinimalTransferSize() ?
" --min-size="+TQString( "%1" ).arg( Smb4KSettings::minimalTransferSize() )+"K" :
"" );
options.append( Smb4KSettings::useMaximalTransferSize() ?
" --max-size="+TQString( "%1" ).arg( Smb4KSettings::maximalTransferSize() )+"K" :
"" );
if ( Smb4KSettings::keepPartial() )
{
options.append( " --partial" );
options.append( Smb4KSettings::usePartialDirectory() ?
" --partial-dir="+Smb4KSettings::partialDirectory() :
"" );
}
options.append( Smb4KSettings::useCVSExclude() ?
" --cvs-exclude" :
"" );
options.append( Smb4KSettings::useFFilterRule() ?
" -F" :
"" );
options.append( Smb4KSettings::useFFFilterRule() ?
" -F -F" :
"" );
options.append( Smb4KSettings::useExcludePattern() ?
" --exclude="+Smb4KSettings::excludePattern() :
"" );
options.append( Smb4KSettings::useExcludeFrom() ?
" --exclude-from="+Smb4KSettings::excludeFrom() :
"" );
options.append( Smb4KSettings::useIncludePattern() ?
" --include="+Smb4KSettings::includePattern() :
"" );
options.append( Smb4KSettings::useIncludeFrom() ?
" --include-from="+Smb4KSettings::includeFrom() :
"" );
return options;
}
/////////////////////////////////////////////////////////////////////////////
// TQT_SLOT IMPLEMENTATIONS
/////////////////////////////////////////////////////////////////////////////
void Smb4KSynchronizer::abort()
{
cancel = true;
if ( m_proc->isRunning() )
{
m_proc->kill();
}
}
void Smb4KSynchronizer::slotProcessExited( KProcess * )
{
m_proc->clearArguments();
m_working = false;
emit finished();
emit state( SYNCHRONIZER_STOP );
}
void Smb4KSynchronizer::slotReceivedStdout( KProcess *, char *buf, int len )
{
m_buffer = TQString::fromLocal8Bit( buf, len );
Smb4KSynchronizationInfo sync_info;
TQString partial, total, files, rate;
if ( m_buffer[0].isSpace() && m_buffer.contains( "/s ", true ) > 0 )
{
partial = m_buffer.section( "%", 0, 0 ).section( " ", -1, -1 ).stripWhiteSpace();
if ( !partial.isEmpty() )
{
sync_info.setIndividualProgress( partial.toInt() );
}
if ( m_buffer.contains( "to-check=" ) > 0 )
{
// Newer versions of rsync:
TQString tmp = m_buffer.section( "to-check=", 1, 1 ).section( ")", 0, 0 ).stripWhiteSpace();
if ( !tmp.isEmpty() )
{
double tmp_total = tmp.section( "/", 1, 1 ).stripWhiteSpace().toInt();
double tmp_done = tmp.section( "/", 0, 0 ).stripWhiteSpace().toInt();
double tmp_percent = ((tmp_total-tmp_done)/tmp_total)*100;
total = TQString( "%1" ).arg( tmp_percent ).section( ".", 0, 0 ).stripWhiteSpace();
}
}
else
{
// Older versions of rsync:
total = m_buffer.section( " (", 1, 1 ).section( ",", 1, 1 ).section( "%", 0, 0 ).section( ".", 0, 0 ).stripWhiteSpace();
}
if ( !total.isEmpty() )
{
sync_info.setTotalProgress( total.toInt() );
}
if ( m_buffer.contains( "xfer#" ) > 0 )
{
// Newer versions of rsync:
files = m_buffer.section( "xfer#", 1, 1 ).section( ",", 0, 0 ).stripWhiteSpace();
}
else
{
// Older versions of rsync:
files = m_buffer.section( " (", 1, 1 ).section( ",", 0, 0 ).stripWhiteSpace();
}
if ( !files.isEmpty() )
{
sync_info.setProcessedFileNumber( files.toInt() );
sync_info.setTotalFileNumber( m_total_files.toInt() );
}
rate = m_buffer.section( "/s ", 0, 0 ).section( " ", -1, -1 ).stripWhiteSpace();
if ( !rate.isEmpty() )
{
rate.append( "/s" );
rate.insert( rate.length() - 4, " " );
sync_info.setTransferRate( rate );
}
m_buffer = TQString();
}
else if ( !m_buffer[0].isSpace() && m_buffer.endsWith( "\n" ) && m_buffer.contains( "/s ", true ) == 0 )
{
sync_info.setText( m_buffer.stripWhiteSpace() );
if ( m_buffer.contains( "files to consider" ) != 0 )
{
m_total_files = m_buffer.section( " files to consider", 0, 0 ).section( " ", -1, -1 ).stripWhiteSpace();
sync_info.setTotalFileNumber( m_total_files.toInt() );
}
m_buffer = TQString();
}
emit progress( sync_info );
}
void Smb4KSynchronizer::slotReceivedStderr( KProcess *, char *buf, int len )
{
TQString error_message = TQString::fromLocal8Bit( buf, len );
// At least under Debian unstable (20051216), rsync emits an error
// when you kill the process (using SIGTERM). Pressing the cancel
// button will exactly do this. Thus, we need to exclude this occasion
// from here.
if ( !cancel && error_message.contains( "rsync error:", true ) != 0 )
{
// Cancel the whole synchronization in case an error occurred.
// If we don't do it, the user might be flooded with error messages
// especially if you try to synchronize a local copy with a remote
// share that is mounted read-only.
abort();
Smb4KError::error( ERROR_SYNCHRONIZING, TQString(), error_message );
}
else
{
cancel = false;
}
}
void Smb4KSynchronizer::slotShutdown()
{
abort();
}
#include "smb4ksynchronizer.moc"