: PropagateItemJob(propagator, item)
, _deleteExisting(false)
, _uploadEncryptedHelper(nullptr)
- , _isEncryptedRootFolder(_item->_isEncrypted && _item->_encryptedFileName.isEmpty())
{
const auto path = _item->_file;
const auto slashPosition = path.lastIndexOf('/');
propagator()->fullRemotePath(filename),
{{"e2e-token", _uploadEncryptedHelper->_folderToken }},
this);
- connect(job, qOverload<QNetworkReply::NetworkError>(&MkColJob::finished),
- _uploadEncryptedHelper, &PropagateUploadEncrypted::unlockFolder);
connect(job, qOverload<QNetworkReply::NetworkError>(&MkColJob::finished),
this, &PropagateRemoteMkdir::slotMkcolJobFinished);
_job = job;
_deleteExisting = enabled;
}
+void PropagateRemoteMkdir::finalizeMkColJob(QNetworkReply::NetworkError err, const QString &jobHttpReasonPhraseString, const QString &jobPath)
+{
+ if (_item->_httpErrorCode == 405) {
+ // This happens when the directory already exists. Nothing to do.
+ } else if (err != QNetworkReply::NoError) {
+ SyncFileItem::Status status = classifyError(err, _item->_httpErrorCode,
+ &propagator()->_anotherSyncNeeded);
+ done(status, _item->_errorString);
+ return;
+ } else 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(jobHttpReasonPhraseString));
+ return;
+ }
+
+ if (_item->_fileId.isEmpty()) {
+ // Owncloud 7.0.0 and before did not have a header with the file id.
+ // (https://github.com/owncloud/core/issues/9000)
+ // So we must get the file id using a PROPFIND
+ // This is required so that we can detect moves even if the folder is renamed on the server
+ // while files are still uploading
+ propagator()->_activeJobList.append(this);
+ auto propfindJob = new PropfindJob(propagator()->account(), jobPath, this);
+ propfindJob->setProperties(QList<QByteArray>() << "http://owncloud.org/ns:id");
+ QObject::connect(propfindJob, &PropfindJob::result, this, &PropagateRemoteMkdir::propfindResult);
+ QObject::connect(propfindJob, &PropfindJob::finishedWithError, this, &PropagateRemoteMkdir::propfindError);
+ propfindJob->start();
+ _job = propfindJob;
+ return;
+ }
+
+ if (!_uploadEncryptedHelper && !_item->_isEncrypted) {
+ success();
+ } else {
+ // We still need to mark that folder encrypted
+ propagator()->_activeJobList.append(this);
+
+ // We're expecting directory path in /Foo/Bar convention...
+ Q_ASSERT(jobPath.startsWith('/') && !jobPath.endsWith('/'));
+ // But encryption job expect it in Foo/Bar/ convention
+ auto job = new OCC::EncryptFolderJob(propagator()->account(), propagator()->_journal, jobPath.mid(1), _item->_fileId, this);
+ connect(job, &OCC::EncryptFolderJob::finished, this, &PropagateRemoteMkdir::slotEncryptFolderFinished);
+ job->start();
+ }
+}
+
void PropagateRemoteMkdir::slotMkdir()
{
const auto path = _item->_file;
_item->_responseTimeStamp = _job->responseTimestamp();
_item->_requestId = _job->requestId();
- if (_item->_httpErrorCode == 405) {
- // This happens when the directory already exists. Nothing to do.
- } else if (err != QNetworkReply::NoError) {
- SyncFileItem::Status status = classifyError(err, _item->_httpErrorCode,
- &propagator()->_anotherSyncNeeded);
- done(status, _job->errorString());
- return;
- } else 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;
- }
-
_item->_fileId = _job->reply()->rawHeader("OC-FileId");
- if (_item->_fileId.isEmpty()) {
- // Owncloud 7.0.0 and before did not have a header with the file id.
- // (https://github.com/owncloud/core/issues/9000)
- // So we must get the file id using a PROPFIND
- // This is required so that we can detect moves even if the folder is renamed on the server
- // while files are still uploading
- propagator()->_activeJobList.append(this);
- auto propfindJob = new PropfindJob(_job->account(), _job->path(), this);
- propfindJob->setProperties(QList<QByteArray>() << "http://owncloud.org/ns:id");
- QObject::connect(propfindJob, &PropfindJob::result, this, &PropagateRemoteMkdir::propfindResult);
- QObject::connect(propfindJob, &PropfindJob::finishedWithError, this, &PropagateRemoteMkdir::propfindError);
- propfindJob->start();
- _job = propfindJob;
- return;
- }
+ _item->_errorString = _job->errorString();
- if (!_uploadEncryptedHelper && !_isEncryptedRootFolder) {
- success();
- } else {
- // We still need to mark that folder encrypted
- propagator()->_activeJobList.append(this);
+ const auto jobHttpReasonPhraseString = _job->reply()->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
- // We're expecting directory path in /Foo/Bar convention...
- Q_ASSERT(_job->path().startsWith('/') && !_job->path().endsWith('/'));
- // But encryption job expect it in Foo/Bar/ convention
- const auto path = _isEncryptedRootFolder ? _job->path() : _job->path().mid(1);
+ const auto jobPath = _job->path();
- auto job = new OCC::EncryptFolderJob(propagator()->account(), propagator()->_journal, path, _item->_fileId, this);
- connect(job, &OCC::EncryptFolderJob::finished, this, &PropagateRemoteMkdir::slotEncryptFolderFinished);
- job->start();
+ if (_uploadEncryptedHelper && !_uploadEncryptedHelper->_folderId.isEmpty()) {
+ connect(_uploadEncryptedHelper, &PropagateUploadEncrypted::folderUnlocked, this, [this, err, jobHttpReasonPhraseString, jobPath]() {
+ finalizeMkColJob(err, jobHttpReasonPhraseString, jobPath);
+ });
+ _uploadEncryptedHelper->unlockFolder();
+ } else {
+ finalizeMkColJob(err, jobHttpReasonPhraseString, jobPath);
}
}
void success();
private:
- bool _isEncryptedRootFolder = false;
+ void finalizeMkColJob(QNetworkReply::NetworkError err, const QString &jobHttpReasonPhraseString, const QString &jobPath);
+
+private:
+ QPair<SyncFileItem::Status, QString> _status = { SyncFileItem::NoStatus, QString() };
};
}
if (origin == _item->_renameTarget) {
// The parent has been renamed already so there is nothing more to do.
-
- if (!_item->_encryptedFileName.isEmpty()) {
- const auto path = _item->_file;
- const auto slashPosition = path.lastIndexOf('/');
- const auto parentPath = slashPosition >= 0 ? path.left(slashPosition) : QString();
-
- SyncJournalFileRecord parentRec;
- bool ok = propagator()->_journal->getFileRecord(parentPath, &parentRec);
- if (!ok) {
- done(SyncFileItem::NormalError);
- return;
- }
-
- // We should be encrypted as well since our parent is
- const auto remoteParentPath = parentRec._e2eMangledName.isEmpty() ? parentPath : parentRec._e2eMangledName;
-
- const auto lastSlashPosition = _item->_encryptedFileName.lastIndexOf('/');
- const auto encryptedName = slashPosition >= 0 ? _item->_encryptedFileName.mid(lastSlashPosition + 1) : QString();
-
- if (!encryptedName.isEmpty()) {
- _item->_encryptedFileName = remoteParentPath + "/" + encryptedName;
- }
-
- }
-
finalize();
return;
}
_uploadEncryptedHelper = new PropagateUploadEncrypted(propagator(), remoteParentPath, _item, this);
connect(_uploadEncryptedHelper, &PropagateUploadEncrypted::finalized,
this, &PropagateUploadFileCommon::setupEncryptedFile);
- connect(_uploadEncryptedHelper, &PropagateUploadEncrypted::error,
- []{ qCDebug(lcPropagateUpload) << "Error setting up encryption."; });
+ connect(_uploadEncryptedHelper, &PropagateUploadEncrypted::error, [this]{
+ qCDebug(lcPropagateUpload) << "Error setting up encryption.";
+ done(SyncFileItem::FatalError, tr("Failed to upload encrypted file."));
+ });
_uploadEncryptedHelper->start();
}
const QString originalFilePath = propagator()->fullLocalPath(_item->_file);
if (!FileSystem::fileExists(fullFilePath)) {
- if (_uploadingEncrypted) {
- _uploadEncryptedHelper->unlockFolder();
- }
- done(SyncFileItem::SoftError, tr("File Removed (start upload) %1").arg(fullFilePath));
- return;
+ return slotOnErrorStartFolderUnlock(SyncFileItem::SoftError, tr("File Removed (start upload) %1").arg(fullFilePath));
}
time_t prevModtime = _item->_modtime; // the _item value was set in PropagateUploadFile::start()
// but a potential checksum calculation could have taken some time during which the file could
_item->_modtime = FileSystem::getModTime(originalFilePath);
if (prevModtime != _item->_modtime) {
propagator()->_anotherSyncNeeded = true;
- if (_uploadingEncrypted) {
- _uploadEncryptedHelper->unlockFolder();
- }
qDebug() << "prevModtime" << prevModtime << "Curr" << _item->_modtime;
- done(SyncFileItem::SoftError, tr("Local file changed during syncing. It will be resumed."));
- return;
+ return slotOnErrorStartFolderUnlock(SyncFileItem::SoftError, tr("Local file changed during syncing. It will be resumed."));
}
_fileToUpload._size = FileSystem::getSize(fullFilePath);
// or not yet fully copied to the destination.
if (fileIsStillChanging(*_item)) {
propagator()->_anotherSyncNeeded = true;
- if (_uploadingEncrypted) {
- _uploadEncryptedHelper->unlockFolder();
- }
- done(SyncFileItem::SoftError, tr("Local file changed during sync."));
- return;
+ return slotOnErrorStartFolderUnlock(SyncFileItem::SoftError, tr("Local file changed during sync."));
}
doStartUpload();
}
+void PropagateUploadFileCommon::slotFolderUnlocked(const QByteArray &folderId, int httpReturnCode)
+{
+ qDebug() << "Failed to unlock encrypted folder" << folderId;
+ if (_status.first == SyncFileItem::NoStatus && httpReturnCode != 200) {
+ done(SyncFileItem::FatalError, tr("Failed to unlock encrypted folder."));
+ } else {
+ done(_status.first, _status.second);
+ }
+}
+
+void PropagateUploadFileCommon::slotOnErrorStartFolderUnlock(SyncFileItem::Status status, const QString &errorString)
+{
+ if (_uploadingEncrypted) {
+ _status = { status, errorString };
+ connect(_uploadEncryptedHelper, &PropagateUploadEncrypted::folderUnlocked, this, &PropagateUploadFileCommon::slotFolderUnlocked);
+ _uploadEncryptedHelper->unlockFolder();
+ } else {
+ done(status, errorString);
+ }
+}
+
UploadDevice::UploadDevice(const QString &fileName, qint64 start, qint64 size, BandwidthManager *bwm)
: _file(fileName)
, _start(start)
propagator()->_journal->commit("upload file start");
if (_uploadingEncrypted) {
- _uploadEncryptedHelper->unlockFolder();
+ _status = { SyncFileItem::Success, QString() };
+ connect(_uploadEncryptedHelper, &PropagateUploadEncrypted::folderUnlocked, this, &PropagateUploadFileCommon::slotFolderUnlocked);
+ _uploadEncryptedHelper->unlockFolder();
+ } else {
+ done(SyncFileItem::Success);
}
- done(SyncFileItem::Success);
}
void PropagateUploadFileCommon::abortNetworkJobs(
void slotComputeTransmissionChecksum(const QByteArray &contentChecksumType, const QByteArray &contentChecksum);
// transmission checksum computed, prepare the upload
void slotStartUpload(const QByteArray &transmissionChecksumType, const QByteArray &transmissionChecksum);
+ // invoked when encrypted folder lock has been released
+ void slotFolderUnlocked(const QByteArray &folderId, int httpReturnCode);
+ // invoked on internal error to unlock a folder and faile
+ void slotOnErrorStartFolderUnlock(SyncFileItem::Status status, const QString &errorString);
public:
virtual void doStartUpload() = 0;
private:
PropagateUploadEncrypted *_uploadEncryptedHelper;
bool _uploadingEncrypted;
+ QPair<SyncFileItem::Status, QString> _status = { SyncFileItem::NoStatus, QString() };
};
/**
if (!encryptionResult) {
qCDebug(lcPropagateUploadEncrypted()) << "There was an error encrypting the file, aborting upload.";
- unlockFolder();
+ unlockFolder(true);
return;
}
{
qCDebug(lcPropagateUploadEncrypted) << "Update metadata error for folder" << fileId << "with error" << httpErrorResponse;
qCDebug(lcPropagateUploadEncrypted()) << "Unlocking the folder.";
- unlockFolder();
+ unlockFolder(true);
}
void PropagateUploadEncrypted::slotFolderLockedError(const QByteArray& fileId, int httpErrorCode)
qCDebug(lcPropagateUploadEncrypted) << "Error retrieving the Id of the encrypted folder.";
}
-void PropagateUploadEncrypted::unlockFolder()
+void PropagateUploadEncrypted::unlockFolder(bool uploadFailed)
{
+ {
+ QMutexLocker locker(&_isUnlockRunningMutex);
+
+ ASSERT(!_isUnlockRunning);
+
+ if (_isUnlockRunning) {
+ qWarning() << "Double-call to unlockFolder.";
+ return;
+ }
+
+ _isUnlockRunning = true;
+ }
+
qDebug() << "Calling Unlock";
auto *unlockJob = new UnlockEncryptFolderApiJob(_propagator->account(),
_folderId, _folderToken, this);
- connect(unlockJob, &UnlockEncryptFolderApiJob::success, []{ qDebug() << "Successfully Unlocked"; });
- connect(unlockJob, &UnlockEncryptFolderApiJob::error, []{ qDebug() << "Unlock Error"; });
+ connect(unlockJob, &UnlockEncryptFolderApiJob::success, [this, uploadFailed](const QByteArray &folderId) {
+ qDebug() << "Successfully Unlocked";
+ _folderToken = "";
+ _folderId = "";
+
+ emit folderUnlocked(folderId, 200);
+
+ if (uploadFailed) {
+ emit error();
+ }
+
+ QMutexLocker locker(&_isUnlockRunningMutex);
+ _isUnlockRunning = false;
+ });
+ connect(unlockJob, &UnlockEncryptFolderApiJob::error, [this, uploadFailed](const QByteArray &folderId, int httpStatus) {
+ qDebug() << "Unlock Error";
+
+ emit folderUnlocked(folderId, httpStatus);
+
+ if (uploadFailed) {
+ emit error();
+ }
+
+ QMutexLocker locker(&_isUnlockRunningMutex);
+ _isUnlockRunning = false;
+ });
unlockJob->start();
}
void start();
/* unlocks the current folder that holds this file */
- void unlockFolder();
+ void unlockFolder(bool uploadFailed = false);
// Used by propagateupload
QByteArray _folderToken;
QByteArray _folderId;
// Emmited after the file is encrypted and everythign is setup.
void finalized(const QString& path, const QString& filename, quint64 size);
void error();
+ void folderUnlocked(const QByteArray &folderId, int httpStatus);
private:
OwncloudPropagator *_propagator;
FolderMetadata *_metadata;
EncryptedFile _encryptedFile;
QString _completeFileName;
+
+ bool _isUnlockRunning = false; // protection against multiple calls to unlock the same folder
+ QMutex _isUnlockRunningMutex; // corresponding mutex for _isUnlockRunning
};