diff options
Diffstat (limited to 'smb4k/core/smb4ksynchronizer.cpp')
-rw-r--r-- | smb4k/core/smb4ksynchronizer.cpp | 503 |
1 files changed, 503 insertions, 0 deletions
diff --git a/smb4k/core/smb4ksynchronizer.cpp b/smb4k/core/smb4ksynchronizer.cpp new file mode 100644 index 0000000..21ac788 --- /dev/null +++ b/smb4k/core/smb4ksynchronizer.cpp @@ -0,0 +1,503 @@ +/*************************************************************************** + 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 * + ***************************************************************************/ + +// Qt includes +#include <qlayout.h> +#include <qdir.h> +#include <qlabel.h> +#include <qregexp.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( QObject *parent, const char *name ) +: QObject( parent, name ) +{ + m_proc = new KProcess( this, "SynchronizerProcess" ); + + m_proc->setUseShell( true ); + + m_working = false; + + connect( m_proc, SIGNAL( receivedStdout( KProcess *, char *, int ) ), + this, SLOT( slotReceivedStdout( KProcess *, char *, int ) ) ); + + connect( m_proc, SIGNAL( processExited( KProcess* ) ), + this, SLOT( slotProcessExited( KProcess * ) ) ); + + connect( m_proc, SIGNAL( receivedStderr( KProcess *, char *, int ) ), + this, SLOT( slotReceivedStderr( KProcess *, char *, int ) ) ); + + connect( kapp, SIGNAL( shutDown() ), + this, SLOT( slotShutdown() ) ); +} + + +Smb4KSynchronizer::~Smb4KSynchronizer() +{ +} + + +/**************************************************************************** + Synchronizes a share with a local copy or vice versa. +****************************************************************************/ + +void Smb4KSynchronizer::synchronize( const QString &source, const QString &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(). + QString 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 QString Smb4KSynchronizer::readRsyncOptions() +{ + QString 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="+QString( "%1" ).arg( Smb4KSettings::maximumDeleteValue() ) : + "" ); + + options.append( Smb4KSettings::useChecksum() ? + " --checksum" : + "" ); + + options.append( Smb4KSettings::useBlockSize() ? + " --block-size="+QString( "%1" ).arg( Smb4KSettings::blockSize() ) : + "" ); + + options.append( Smb4KSettings::useChecksumSeed() ? + " --checksum-seed="+QString( "%1" ).arg( Smb4KSettings::checksumSeed() ) : + "" ); + + if ( !Smb4KSettings::customFilteringRules().isEmpty() ) + { + options.append( " "+Smb4KSettings::customFilteringRules() ); + } + + options.append( Smb4KSettings::useMinimalTransferSize() ? + " --min-size="+QString( "%1" ).arg( Smb4KSettings::minimalTransferSize() )+"K" : + "" ); + + options.append( Smb4KSettings::useMaximalTransferSize() ? + " --max-size="+QString( "%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; +} + + +///////////////////////////////////////////////////////////////////////////// +// 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 = QString::fromLocal8Bit( buf, len ); + + Smb4KSynchronizationInfo sync_info; + + QString 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: + QString 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 = QString( "%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 = QString::null; + } + 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 = QString::null; + } + + emit progress( sync_info ); +} + + + +void Smb4KSynchronizer::slotReceivedStderr( KProcess *, char *buf, int len ) +{ + QString error_message = QString::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, QString::null, error_message ); + } + else + { + cancel = false; + } +} + + +void Smb4KSynchronizer::slotShutdown() +{ + abort(); +} + +#include "smb4ksynchronizer.moc" |