summaryrefslogtreecommitdiffstats
path: root/smb4k/core/smb4ksynchronizer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'smb4k/core/smb4ksynchronizer.cpp')
-rw-r--r--smb4k/core/smb4ksynchronizer.cpp503
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"