From c712b7c46cf6656a511d6662278781ae1c4e4abd Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Tue, 11 Nov 2014 16:09:01 +0100 Subject: [PATCH] Parallel MOVE by porting the job to qnam --- src/libsync/CMakeLists.txt | 1 + src/libsync/networkjobs.h | 1 + src/libsync/owncloudpropagator.cpp | 3 +- src/libsync/propagateremotemove.cpp | 145 ++++++++++++++++++++++++++++ src/libsync/propagateremotemove.h | 51 ++++++++++ src/libsync/propagatorjobs.cpp | 48 --------- src/libsync/propagatorjobs.h | 7 -- 7 files changed, 200 insertions(+), 56 deletions(-) create mode 100644 src/libsync/propagateremotemove.cpp create mode 100644 src/libsync/propagateremotemove.h diff --git a/src/libsync/CMakeLists.txt b/src/libsync/CMakeLists.txt index 0a1a58084e..50a7c4886e 100644 --- a/src/libsync/CMakeLists.txt +++ b/src/libsync/CMakeLists.txt @@ -52,6 +52,7 @@ set(libsync_SRCS propagatedownload.cpp propagateupload.cpp propagateremotedelete.cpp + propagateremotemove.cpp quotainfo.cpp syncengine.cpp syncfilestatus.cpp diff --git a/src/libsync/networkjobs.h b/src/libsync/networkjobs.h index 8ed368f2a9..77e00cd25a 100644 --- a/src/libsync/networkjobs.h +++ b/src/libsync/networkjobs.h @@ -95,6 +95,7 @@ protected: quint64 _duration; bool _timedout; // set to true when the timeout slot is recieved +public: // Timeout workarounds (Because of PHP session locking) static bool preOc7WasDetected; diff --git a/src/libsync/owncloudpropagator.cpp b/src/libsync/owncloudpropagator.cpp index 45d8d22e0e..b053ce8023 100644 --- a/src/libsync/owncloudpropagator.cpp +++ b/src/libsync/owncloudpropagator.cpp @@ -19,6 +19,7 @@ #include "propagatedownload.h" #include "propagateupload.h" #include "propagateremotedelete.h" +#include "propagateremotemove.h" #include "propagatorjobs.h" #include "propagator_legacy.h" #include "mirallconfigfile.h" @@ -225,7 +226,7 @@ PropagateItemJob* OwncloudPropagator::createJob(const SyncFileItem& item) { } case CSYNC_INSTRUCTION_RENAME: if (item._direction == SyncFileItem::Up) { - return new PropagateRemoteRename(this, item); + return new PropagateRemoteMove(this, item); } else { return new PropagateLocalRename(this, item); } diff --git a/src/libsync/propagateremotemove.cpp b/src/libsync/propagateremotemove.cpp new file mode 100644 index 0000000000..f092dc1218 --- /dev/null +++ b/src/libsync/propagateremotemove.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (C) by Olivier Goffart + * + * 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. + */ + +#include "propagateremotemove.h" +#include "owncloudpropagator_p.h" +#include "account.h" +#include "syncjournalfilerecord.h" +#include + +namespace Mirall { + +MoveJob::MoveJob(Account* account, const QString& path, + const QString &destination, QObject* parent) + : AbstractNetworkJob(account, path, parent), _destination(destination) +{ } + + +void MoveJob::start() +{ + QNetworkRequest req; + req.setRawHeader("Destination", _destination.toUtf8()); + setReply(davRequest("MOVE", path(), req)); + setupConnections(reply()); + + if( reply()->error() != QNetworkReply::NoError ) { + qWarning() << Q_FUNC_INFO << " Network error: " << reply()->errorString(); + } + AbstractNetworkJob::start(); +} + + +QString MoveJob::errorString() +{ + return _timedout ? tr("Connection timed out") : reply()->errorString(); +} + +bool MoveJob::finished() +{ + emit finishedSignal(); + return true; +} + +void PropagateRemoteMove::start() +{ + if (_propagator->_abortRequested.fetchAndAddRelaxed(0)) + return; + + qDebug() << Q_FUNC_INFO << _item._file << _item._renameTarget; + + if (_item._file == _item._renameTarget) { + // The parents has been renamed already so there is nothing more to do. + finalize(); + return; + } else if (AbstractNetworkJob::preOc7WasDetected && _item._file == QLatin1String("Shared") ) { + // Check if it is the toplevel Shared folder and do not propagate it. + if( QFile::rename( _propagator->_localDir + _item._renameTarget, _propagator->_localDir + QLatin1String("Shared")) ) { + done(SyncFileItem::NormalError, tr("This folder must not be renamed. It is renamed back to its original name.")); + } else { + done(SyncFileItem::NormalError, tr("This folder must not be renamed. Please name it back to Shared.")); + } + return; + } else { + _job = new MoveJob(AccountManager::instance()->account(), + _propagator->_remoteFolder + _item._file, + _propagator->_remoteDir + _item._renameTarget, + this); + connect(_job, SIGNAL(finishedSignal()), this, SLOT(slotMoveJobFinished())); + _propagator->_activeJobs++; + _job->start(); + emitReady(); + } +} + +void PropagateRemoteMove::abort() +{ + if (_job && _job->reply()) + _job->reply()->abort(); +} + +void PropagateRemoteMove::slotMoveJobFinished() +{ + _propagator->_activeJobs--; + + Q_ASSERT(_job); + + qDebug() << Q_FUNC_INFO << _job->reply()->request().url() << "FINISHED WITH STATUS" + << _job->reply()->error() + << (_job->reply()->error() == QNetworkReply::NoError ? QLatin1String("") : _job->reply()->errorString()); + + QNetworkReply::NetworkError err = _job->reply()->error(); + _item._httpErrorCode = _job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (err != QNetworkReply::NoError) { + + if( checkForProblemsWithShared(_item._httpErrorCode, + tr("The file was renamed but is part of a read only share. The original file was restored."))) { + return; + } + + SyncFileItem::Status status = classifyError(err, _item._httpErrorCode); + done(status, _job->errorString()); + return; + } + + _item._requestDuration = _job->duration(); + _item._responseTimeStamp = _job->responseTimestamp(); + + if (_item._httpErrorCode != 201 ) { + // Normaly we expect "201 Created" + // If it is not the case, it might be because of a proxy or gateway intercepting the request, so we must + // throw an error. + done(SyncFileItem::NormalError, tr("Wrong HTTP code returned by server. Expected 201, but recieved \"%1 %2\".") + .arg(_item._httpErrorCode).arg(_job->reply()->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString())); + return; + } + + finalize(); + +} + +void PropagateRemoteMove::finalize() +{ + _propagator->_journal->deleteFileRecord(_item._originalFile); + SyncJournalFileRecord record(_item, _propagator->_localDir + _item._renameTarget); + record._path = _item._renameTarget; + + _propagator->_journal->setFileRecord(record); + _propagator->_journal->commit("Remote Rename"); + done(SyncFileItem::Success); +} + + +} + diff --git a/src/libsync/propagateremotemove.h b/src/libsync/propagateremotemove.h new file mode 100644 index 0000000000..97cf5c0f05 --- /dev/null +++ b/src/libsync/propagateremotemove.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) by Olivier Goffart + * + * 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. + */ +#pragma once + +#include "owncloudpropagator.h" +#include "networkjobs.h" + +namespace Mirall { + +class MoveJob : public AbstractNetworkJob { + Q_OBJECT + const QString _destination; +public: + explicit MoveJob(Account* account, const QString& path, const QString &destination, QObject* parent = 0); + + void start() Q_DECL_OVERRIDE; + bool finished() Q_DECL_OVERRIDE; + + QString errorString(); + bool timedOut() { return _timedout; } + +signals: + void finishedSignal(); +}; + + +class PropagateRemoteMove : public PropagateItemJob { + Q_OBJECT + QPointer _job; +public: + PropagateRemoteMove (OwncloudPropagator* propagator,const SyncFileItem& item) + : PropagateItemJob(propagator, item) {} + void start() Q_DECL_OVERRIDE; + void abort() Q_DECL_OVERRIDE; +private slots: + void slotMoveJobFinished(); + void finalize(); +}; + +} \ No newline at end of file diff --git a/src/libsync/propagatorjobs.cpp b/src/libsync/propagatorjobs.cpp index 3c980166ec..0edf76792c 100644 --- a/src/libsync/propagatorjobs.cpp +++ b/src/libsync/propagatorjobs.cpp @@ -263,54 +263,6 @@ void PropagateLocalRename::start() done(SyncFileItem::Success); } -void PropagateRemoteRename::start() -{ - if (_propagator->_abortRequested.fetchAndAddRelaxed(0)) - return; - - if (_item._file == _item._renameTarget) { - // The parents has been renamed already so there is nothing more to do. - } else if (_item._file == QLatin1String("Shared") ) { - // Check if it is the toplevel Shared folder and do not propagate it. - if( QFile::rename( _propagator->_localDir + _item._renameTarget, _propagator->_localDir + QLatin1String("Shared")) ) { - done(SyncFileItem::NormalError, tr("This folder must not be renamed. It is renamed back to its original name.")); - } else { - done(SyncFileItem::NormalError, tr("This folder must not be renamed. Please name it back to Shared.")); - } - return; - } else { - emit progress(_item, 0); - - QScopedPointer uri1(ne_path_escape((_propagator->_remoteDir + _item._file).toUtf8())); - QScopedPointer uri2(ne_path_escape((_propagator->_remoteDir + _item._renameTarget).toUtf8())); - qDebug() << "MOVE on Server: " << uri1.data() << "->" << uri2.data(); - - int rc = ne_move(_propagator->_session, 1, uri1.data(), uri2.data()); - - QString errorString = QString::fromUtf8(ne_get_error(_propagator->_session)); - int httpStatusCode = errorString.mid(0, errorString.indexOf(QChar(' '))).toInt(); - if( checkForProblemsWithShared(httpStatusCode, - tr("The file was renamed but is part of a read only share. The original file was restored."))) { - return; - } - - if (updateErrorFromSession(rc)) { - return; - } - } - // Wed, 15 Nov 1995 06:25:24 GMT - QDateTime dt = QDateTime::currentDateTimeUtc(); - _item._responseTimeStamp = dt.toString("hh:mm:ss"); - - _propagator->_journal->deleteFileRecord(_item._originalFile); - SyncJournalFileRecord record(_item, _propagator->_localDir + _item._renameTarget); - record._path = _item._renameTarget; - - _propagator->_journal->setFileRecord(record); - _propagator->_journal->commit("Remote Rename"); - done(SyncFileItem::Success); -} - bool PropagateNeonJob::updateErrorFromSession(int neon_code, ne_request* req, int ignoreHttpCode) { if( neon_code != NE_OK ) { diff --git a/src/libsync/propagatorjobs.h b/src/libsync/propagatorjobs.h index ee6550c3d0..a6534e010b 100644 --- a/src/libsync/propagatorjobs.h +++ b/src/libsync/propagatorjobs.h @@ -99,13 +99,6 @@ public: PropagateLocalRename (OwncloudPropagator* propagator,const SyncFileItem& item) : PropagateItemJob(propagator, item) {} void start() Q_DECL_OVERRIDE; }; -class PropagateRemoteRename : public PropagateNeonJob { - Q_OBJECT -public: - PropagateRemoteRename (OwncloudPropagator* propagator,const SyncFileItem& item) : PropagateNeonJob(propagator, item) {} - void start() Q_DECL_OVERRIDE; -}; - // To support older owncloud in the class UpdateMTimeAndETagJob : public PropagateNeonJob{