/*************************************************************************** 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 #include #include #include // KDE includes #include #include #include #include #include #include #include #include #include // application specific includes #include "smb4ksynchronizer.h" #include "smb4kdefs.h" #include "smb4kerror.h" #include "smb4tdeglobal.h" #include "smb4kshare.h" #include "smb4ksynchronizationinfo.h" #include "smb4ksettings.h" using namespace Smb4TDEGlobal; bool cancel = false; Smb4KSynchronizer::Smb4KSynchronizer( TQObject *parent, const char *name ) : TQObject( parent, name ) { m_proc = new TDEProcess( this, "SynchronizerProcess" ); m_proc->setUseShell( true ); m_working = false; connect( m_proc, TQ_SIGNAL( receivedStdout( TDEProcess *, char *, int ) ), this, TQ_SLOT( slotReceivedStdout( TDEProcess *, char *, int ) ) ); connect( m_proc, TQ_SIGNAL( processExited( TDEProcess* ) ), this, TQ_SLOT( slotProcessExited( TDEProcess * ) ) ); connect( m_proc, TQ_SIGNAL( receivedStderr( TDEProcess *, char *, int ) ), this, TQ_SLOT( slotReceivedStderr( TDEProcess *, char *, int ) ) ); connect( kapp, TQ_SIGNAL( shutDown() ), this, TQ_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( TDEProcess::quote( source ) ); command.append( " " ); command.append( TDEProcess::quote( destination ) ); *m_proc << command; // Use TDEProcess::OwnGroup instead of TDEProcess::NotifyOnExit here, because // this garantees that the process is indeed killed when using abort(). // See TDEProcess docs for further information. m_proc->start( TDEProcess::OwnGroup, TDEProcess::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; } ///////////////////////////////////////////////////////////////////////////// // SLOT IMPLEMENTATIONS ///////////////////////////////////////////////////////////////////////////// void Smb4KSynchronizer::abort() { cancel = true; if ( m_proc->isRunning() ) { m_proc->kill(); } } void Smb4KSynchronizer::slotProcessExited( TDEProcess * ) { m_proc->clearArguments(); m_working = false; emit finished(); emit state( SYNCHRONIZER_STOP ); } void Smb4KSynchronizer::slotReceivedStdout( TDEProcess *, 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( TDEProcess *, 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"