nextcloud-desktop/src/libsync/propagateremotemove.cpp
Olivier Goffart a1dc4069c9 libsync: Don't store the remote URI in the csync or in the SyncEngine
We are going to change the webdav path depending on the capabilities.
But the SyncEngine and csync might have been created before the capabilities
are retrieved.

The main raison why we gave the path to the sync engine was to pass it to csync.
But the thing is that csync don't need anymore this url as everything is done by the
discovery classes in libsync that use the network jobs that use the account for the urls.
So csync do not need the remote URI.

shortenFilename in folderstatusmodel.cpp was useless because the string is the
_file of a SyncFileItem which is the relative file name, that name never
starts with owncloud://.

All the csync test creates the folder because csync use to check if the folder
exists. But we don't need to do that anymore
2016-11-21 08:09:11 +01:00

230 lines
8.3 KiB
C++

/*
* Copyright (C) by Olivier Goffart <ogoffart@owncloud.com>
*
* 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 "propagatorjobs.h"
#include "owncloudpropagator_p.h"
#include "account.h"
#include "syncjournalfilerecord.h"
#include "filesystem.h"
#include <QFile>
#include <QStringList>
#include <QDir>
namespace OCC {
MoveJob::MoveJob(AccountPtr account, const QString& path,
const QString &destination, QObject* parent)
: AbstractNetworkJob(account, path, parent), _destination(destination)
{ }
MoveJob::MoveJob(AccountPtr account, const QUrl& url, const QString &destination,
QMap<QByteArray, QByteArray> extraHeaders, QObject* parent)
: AbstractNetworkJob(account, QString(), parent), _destination(destination), _url(url)
, _extraHeaders(extraHeaders)
{ }
void MoveJob::start()
{
QNetworkRequest req;
req.setRawHeader("Destination", QUrl::toPercentEncoding(_destination, "/"));
for(auto it = _extraHeaders.constBegin(); it != _extraHeaders.constEnd(); ++it) {
req.setRawHeader(it.key(), it.value());
}
setReply(_url.isValid() ? davRequest("MOVE", _url, req) : davRequest("MOVE", path(), req));
setupConnections(reply());
if( reply()->error() != QNetworkReply::NoError ) {
qWarning() << Q_FUNC_INFO << " Network error: " << reply()->errorString();
}
AbstractNetworkJob::start();
}
QString MoveJob::errorString()
{
if (_timedout) {
return tr("Connection timed out");
} else if (reply()->hasRawHeader("OC-ErrorString")) {
return reply()->rawHeader("OC-ErrorString");
} else {
return 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;
QString targetFile(_propagator->getFilePath(_item->_renameTarget));
if (_item->_file == _item->_renameTarget) {
// The parent has been renamed already so there is nothing more to do.
finalize();
return;
}
if (_item->_file == QLatin1String("Shared") ) {
// Before owncloud 7, there was no permissions system. At the time all the shared files were
// in a directory called "Shared" and were not supposed to be moved, otherwise bad things happened
QString versionString = _propagator->account()->serverVersion();
if (versionString.contains('.') && versionString.split('.')[0].toInt() < 7) {
QString originalFile(_propagator->getFilePath(QLatin1String("Shared")));
emit _propagator->touchedFile(originalFile);
emit _propagator->touchedFile(targetFile);
QString renameError;
if( FileSystem::rename(targetFile, originalFile, &renameError) ) {
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;
}
}
QString destination = QDir::cleanPath(_propagator->account()->url().path() + QLatin1Char('/')
+ _propagator->account()->davPath() + _propagator->_remoteFolder + _item->_renameTarget);
_job = new MoveJob(_propagator->account(),
_propagator->_remoteFolder + _item->_file,
destination, this);
connect(_job, SIGNAL(finishedSignal()), this, SLOT(slotMoveJobFinished()));
_propagator->_activeJobList.append(this);
_job->start();
}
void PropagateRemoteMove::abort()
{
if (_job && _job->reply())
_job->reply()->abort();
}
void PropagateRemoteMove::slotMoveJobFinished()
{
_propagator->_activeJobList.removeOne(this);
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,
&_propagator->_anotherSyncNeeded);
done(status, _job->errorString());
return;
}
_item->_requestDuration = _job->duration();
_item->_responseTimeStamp = _job->responseTimestamp();
if (_item->_httpErrorCode != 201 ) {
// Normally 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 received \"%1 %2\".")
.arg(_item->_httpErrorCode).arg(_job->reply()->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString()));
return;
}
finalize();
}
void PropagateRemoteMove::finalize()
{
SyncJournalFileRecord oldRecord =
_propagator->_journal->getFileRecord(_item->_originalFile);
// if reading from db failed still continue hoping that deleteFileRecord
// reopens the db successfully.
// The db is only queried to transfer the content checksum from the old
// to the new record. It is not a problem to skip it here.
_propagator->_journal->deleteFileRecord(_item->_originalFile);
SyncJournalFileRecord record(*_item, _propagator->getFilePath(_item->_renameTarget));
record._path = _item->_renameTarget;
if (oldRecord.isValid()) {
record._contentChecksum = oldRecord._contentChecksum;
record._contentChecksumType = oldRecord._contentChecksumType;
if (record._fileSize != oldRecord._fileSize) {
qDebug() << "Warning: file sizes differ on server vs csync_journal: " << record._fileSize << oldRecord._fileSize;
record._fileSize = oldRecord._fileSize; // server might have claimed different size, we take the old one from the DB
}
}
if (!_propagator->_journal->setFileRecord(record)) {
done(SyncFileItem::FatalError, tr("Error writing metadata to the database"));
return;
}
if (_item->_isDirectory) {
if (!adjustSelectiveSync(_propagator->_journal, _item->_file, _item->_renameTarget)) {
done(SyncFileItem::FatalError, tr("Error writing metadata to the database"));
return;
}
}
_propagator->_journal->commit("Remote Rename");
done(SyncFileItem::Success);
}
bool PropagateRemoteMove::adjustSelectiveSync(SyncJournalDb *journal, const QString &from_, const QString &to_)
{
bool ok;
// We only care about preserving the blacklist. The white list should anyway be empty.
// And the undecided list will be repopulated on the next sync, if there is anything too big.
QStringList list = journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, &ok);
if (!ok)
return false;
bool changed = false;
Q_ASSERT(!from_.endsWith(QLatin1String("/")));
Q_ASSERT(!to_.endsWith(QLatin1String("/")));
QString from = from_ + QLatin1String("/");
QString to = to_ + QLatin1String("/");
for (auto it = list.begin(); it != list.end(); ++it) {
if (it->startsWith(from)) {
*it = it->replace(0, from.size(), to);
changed = true;
}
}
if (changed) {
journal->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, list);
}
return true;
}
}