[Pkg-owncloud-commits] [owncloud-client] 45/333: Move the jobs around between files
Sandro Knauß
hefee-guest at moszumanska.debian.org
Thu Apr 17 23:16:32 UTC 2014
This is an automated email from the git hooks/post-receive script.
hefee-guest pushed a commit to branch master
in repository owncloud-client.
commit c32beb957d8af0a29eef77390d1bf903d5d50a06
Author: Olivier Goffart <ogoffart at woboq.com>
Date: Tue Feb 18 11:52:38 2014 +0100
Move the jobs around between files
Renamed owncloudpropagator_qnam to propagator_qnam
Move the jobs in propagatorjobs.cpp
Move the neon jobs that have a qnam equivalent into propagator_legacy.cpp and
rename the jobs accordingly
---
src/CMakeLists.txt | 8 +-
src/mirall/owncloudpropagator.cpp | 957 +--------------------
src/mirall/owncloudpropagator_p.h | 161 ----
...wncloudpropagator.cpp => propagator_legacy.cpp} | 610 +++----------
src/mirall/propagator_legacy.h | 82 ++
...loudpropagator_qnam.cpp => propagator_qnam.cpp} | 2 +-
...owncloudpropagator_qnam.h => propagator_qnam.h} | 4 +-
src/mirall/propagatorjobs.cpp | 241 ++++++
.../{owncloudpropagator_p.h => propagatorjobs.h} | 82 --
9 files changed, 447 insertions(+), 1700 deletions(-)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b5f15d7..554b8f1 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -70,7 +70,9 @@ set(libsync_SRCS
mirall/mirallconfigfile.cpp
mirall/csyncthread.cpp
mirall/owncloudpropagator.cpp
- mirall/owncloudpropagator_qnam.cpp
+ mirall/propagatorjobs.cpp
+ mirall/propagator_qnam.cpp
+ mirall/propagator_legacy.cpp
mirall/syncjournalfilerecord.cpp
mirall/syncjournaldb.cpp
mirall/theme.cpp
@@ -106,7 +108,9 @@ set(libsync_HEADERS
mirall/csyncthread.h
mirall/owncloudpropagator.h
mirall/owncloudpropagator_p.h
- mirall/owncloudpropagator_qnam.h
+ mirall/propagator_qnam.h
+ mirall/propagator_legacy.h
+ mirall/propagatorjobs.h
mirall/syncjournaldb.h
mirall/theme.h
mirall/owncloudtheme.h
diff --git a/src/mirall/owncloudpropagator.cpp b/src/mirall/owncloudpropagator.cpp
index 7113305..22b837b 100644
--- a/src/mirall/owncloudpropagator.cpp
+++ b/src/mirall/owncloudpropagator.cpp
@@ -13,47 +13,20 @@
* for more details.
*/
-#include "owncloudpropagator_p.h"
+#include "owncloudpropagator.h"
#include "syncjournaldb.h"
#include "syncjournalfilerecord.h"
-#include "utility.h"
-#include "owncloudpropagator_qnam.h"
-#include <httpbf.h>
-#include <qfile.h>
-#include <qdir.h>
-#include <qdiriterator.h>
-#include <qtemporaryfile.h>
-#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
-#include <qabstractfileengine.h>
-#else
-#include <qsavefile.h>
-#endif
-#include <QDebug>
-#include <QDateTime>
-#include <qstack.h>
-#include <QCoreApplication>
+#include "propagator_qnam.h"
+#include "propagatorjobs.h"
+#include "propagator_legacy.h"
-#include <neon/ne_basic.h>
-#include <neon/ne_socket.h>
-#include <neon/ne_session.h>
-#include <neon/ne_props.h>
-#include <neon/ne_auth.h>
-#include <neon/ne_dates.h>
-#include <neon/ne_compress.h>
-#include <neon/ne_redirect.h>
+#include <QStack>
-#ifdef Q_OS_WIN
-#include <windef.h>
-#include <winbase.h>
-#endif
-
-#include <time.h>
+namespace Mirall {
/* The maximum number of active job in parallel */
static const int maximumActiveJob = 6;
-namespace Mirall {
-
void PropagateItemJob::done(SyncFileItem::Status status, const QString &errorString)
{
_item._errorString = errorString;
@@ -100,924 +73,6 @@ void PropagateItemJob::done(SyncFileItem::Status status, const QString &errorStr
}
-/**
- * For delete or remove, check that we are not removing from a shared directory.
- * If we are, try to restore the file
- *
- * Return true if the problem is handled.
- */
-bool PropagateNeonJob::checkForProblemsWithShared()
-{
- QString errorString = QString::fromUtf8(ne_get_error(_propagator->_session));
- int httpStatusCode = errorString.mid(0, errorString.indexOf(QChar(' '))).toInt();
-
- if( httpStatusCode == 403 && _propagator->isInSharedDirectory(_item._file )) {
- if( _item._type != SyncFileItem::Directory ) {
- // the file was removed locally from a read only Shared sync
- // the file is gone locally and it should be recovered.
- SyncFileItem downloadItem(_item);
- downloadItem._instruction = CSYNC_INSTRUCTION_SYNC;
- downloadItem._dir = SyncFileItem::Down;
- _restoreJob.reset(new PropagateDownloadFile(_propagator, downloadItem));
- } else {
- // Directories are harder to recover.
- // But just re-create the directory, next sync will be able to recover the files
- SyncFileItem mkdirItem(_item);
- mkdirItem._instruction = CSYNC_INSTRUCTION_SYNC;
- mkdirItem._dir = SyncFileItem::Down;
- _restoreJob.reset(new PropagateLocalMkdir(_propagator, mkdirItem));
- // Also remove the inodes and fileid from the db so no further renames are tried for
- // this item.
- _propagator->_journal->avoidRenamesOnNextSync(_item._file);
- }
- connect(_restoreJob.data(), SIGNAL(completed(SyncFileItem)),
- this, SLOT(slotRestoreJobCompleted(SyncFileItem)));
- QMetaObject::invokeMethod(_restoreJob.data(), "start");
- return true;
- }
- return false;
-}
-
-void PropagateNeonJob::slotRestoreJobCompleted(const SyncFileItem& item )
-{
- if( item._status == SyncFileItem::Success ) {
- done( SyncFileItem::SoftError, tr("The file was removed from a read only share. The file has been restored."));
- } else {
- done( item._status, tr("A file was removed from a read only share, but restoring failed: %1").arg(item._errorString) );
- }
-}
-
-
-// compare two files with given filename and return true if they have the same content
-bool fileEquals(const QString &fn1, const QString &fn2) {
- QFile f1(fn1);
- QFile f2(fn2);
- if (!f1.open(QIODevice::ReadOnly) || !f2.open(QIODevice::ReadOnly)) {
- qDebug() << "fileEquals: Failed to open " << fn1 << "or" << fn2;
- return false;
- }
-
- if (f1.size() != f2.size()) {
- return false;
- }
-
- const int BufferSize = 16 * 1024;
- char buffer1[BufferSize];
- char buffer2[BufferSize];
- do {
- int r = f1.read(buffer1, BufferSize);
- if (f2.read(buffer2, BufferSize) != r) {
- // this should normaly not happen: the file are supposed to have the same size.
- return false;
- }
- if (r <= 0) {
- return true;
- }
- if (memcmp(buffer1, buffer2, r) != 0) {
- return false;
- }
- } while (true);
- return false;
-}
-
-// Code copied from Qt5's QDir::removeRecursively
-static bool removeRecursively(const QString &path)
-{
- bool success = true;
- QDirIterator di(path, QDir::AllEntries | QDir::Hidden | QDir::System | QDir::NoDotAndDotDot);
- while (di.hasNext()) {
- di.next();
- const QFileInfo& fi = di.fileInfo();
- bool ok;
- if (fi.isDir() && !fi.isSymLink())
- ok = removeRecursively(di.filePath()); // recursive
- else
- ok = QFile::remove(di.filePath());
- if (!ok)
- success = false;
- }
- if (success)
- success = QDir().rmdir(path);
- return success;
-}
-
-void PropagateLocalRemove::start()
-{
- if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
- return;
-
- QString filename = _propagator->_localDir + _item._file;
- if (_item._isDirectory) {
- if (QDir(filename).exists() && !removeRecursively(filename)) {
- done(SyncFileItem::NormalError, tr("Could not remove directory %1").arg(filename));
- return;
- }
- } else {
- QFile file(filename);
- if (file.exists() && !file.remove()) {
- done(SyncFileItem::NormalError, file.errorString());
- return;
- }
- }
- emit progress(Progress::StartDelete, _item, 0, _item._size);
- _propagator->_journal->deleteFileRecord(_item._originalFile, _item._isDirectory);
- _propagator->_journal->commit("Local remove");
- done(SyncFileItem::Success);
- emit progress(Progress::EndDelete, _item, _item._size, _item._size);
-}
-
-void PropagateLocalMkdir::start()
-{
- if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
- return;
-
- QDir d;
- if (!d.mkpath(_propagator->_localDir + _item._file)) {
- done(SyncFileItem::NormalError, tr("could not create directory %1").arg(_propagator->_localDir + _item._file));
- return;
- }
- done(SyncFileItem::Success);
-}
-
-void PropagateRemoteRemove::start()
-{
- if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
- return;
-
- QScopedPointer<char, QScopedPointerPodDeleter> uri(
- ne_path_escape((_propagator->_remoteDir + _item._file).toUtf8()));
- emit progress(Progress::StartDelete, _item, 0, _item._size);
- qDebug() << "** DELETE " << uri.data();
- int rc = ne_delete(_propagator->_session, uri.data());
-
- if( checkForProblemsWithShared() ) {
- return;
- }
-
- /* Ignore the error 404, it means it is already deleted */
- if (updateErrorFromSession(rc, 0, 404)) {
- return;
- }
-
- _propagator->_journal->deleteFileRecord(_item._originalFile, _item._isDirectory);
- _propagator->_journal->commit("Remote Remove");
- done(SyncFileItem::Success);
- emit progress(Progress::EndDelete, _item, _item._size, _item._size);
-}
-
-void PropagateRemoteMkdir::start()
-{
- if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
- return;
-
- QScopedPointer<char, QScopedPointerPodDeleter> uri(
- ne_path_escape((_propagator->_remoteDir + _item._file).toUtf8()));
-
- int rc = ne_mkcol(_propagator->_session, uri.data());
-
- /* Special for mkcol: it returns 405 if the directory already exists.
- * Ignore that error */
- if( updateErrorFromSession( rc , 0, 405 ) ) {
- return;
- }
- done(SyncFileItem::Success);
-}
-
-void PropagateUploadFile::start()
-{
- if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
- return;
-
- QFile file(_propagator->_localDir + _item._file);
- if (!file.open(QIODevice::ReadOnly)) {
- done(SyncFileItem::NormalError, file.errorString());
- return;
- }
- QScopedPointer<char, QScopedPointerPodDeleter> uri(
- ne_path_escape((_propagator->_remoteDir + _item._file).toUtf8()));
-
- int attempts = 0;
-
- /*
- * do ten tries to upload the file chunked. Check the file size and mtime
- * before submitting a chunk and after having submitted the last one.
- * If the file has changed, retry.
- */
- qDebug() << "** PUT request to" << uri.data();
- const SyncJournalDb::UploadInfo progressInfo = _propagator->_journal->getUploadInfo(_item._file);
-
- do {
- Hbf_State state = HBF_SUCCESS;
- QScopedPointer<hbf_transfer_t, ScopedPointerHelpers> trans(hbf_init_transfer(uri.data()));
- trans->user_data = this;
- hbf_set_log_callback(trans.data(), _log_callback);
- hbf_set_abort_callback(trans.data(), _user_want_abort);
- trans.data()->chunk_finished_cb = chunk_finished_cb;
- Q_ASSERT(trans);
-
- state = hbf_splitlist(trans.data(), file.handle());
-
- // If the source file has changed during upload, it is detected and the
- // variable _previousFileSize is set accordingly. The propagator waits a
- // couple of seconds and retries.
- if(_previousFileSize > 0) {
- qDebug() << "File size changed underway: " << trans->stat_size - _previousFileSize;
- // Report the change of the overall transmission size to the propagator
- _propagator->overallTransmissionSizeChanged(qint64(trans->stat_size - _previousFileSize));
- // update the item's values to the current from trans. hbf_splitlist does a stat
- _item._size = trans->stat_size;
- _item._modtime = trans->modtime;
-
- }
- emit progress(Progress::StartUpload, _item, 0, trans->stat_size);
-
- if (progressInfo._valid) {
- if (Utility::qDateTimeToTime_t(progressInfo._modtime) == _item._modtime) {
- trans->start_id = progressInfo._chunk;
- trans->transfer_id = progressInfo._transferid;
- }
- }
-
- ne_set_notifier(_propagator->_session, notify_status_cb, this);
- _lastTime.restart();
- _lastProgress = 0;
- _chunked_done = 0;
- _chunked_total_size = _item._size;
-
- if( state == HBF_SUCCESS ) {
- QByteArray previousEtag;
- if (!_item._etag.isEmpty() && _item._etag != "empty_etag") {
- // We add quotes because the owncloud server always add quotes around the etag, and
- // csync_owncloud.c's owncloud_file_id always strip the quotes.
- previousEtag = '"' + _item._etag + '"';
- trans->previous_etag = previousEtag.data();
- }
- _chunked_total_size = trans->stat_size;
- qDebug() << "About to upload " << _item._file << " (" << previousEtag << _item._size << " bytes )";
- /* Transfer all the chunks through the HTTP session using PUT. */
- state = hbf_transfer( _propagator->_session, trans.data(), "PUT" );
- }
-
- // the file id should only be empty for new files up- or downloaded
- QString fid = QString::fromUtf8( hbf_transfer_file_id( trans.data() ));
- if( !fid.isEmpty() ) {
- if( !_item._fileId.isEmpty() && _item._fileId != fid ) {
- qDebug() << "WARN: File ID changed!" << _item._fileId << fid;
- }
- _item._fileId = fid;
- }
-
- /* Handle errors. */
- if ( state != HBF_SUCCESS ) {
-
- /* If the source file changed during submission, lets try again */
- if( state == HBF_SOURCE_FILE_CHANGE ) {
- if( attempts++ < 5 ) { /* FIXME: How often do we want to try? */
- qDebug("SOURCE file has changed during upload, retry #%d in %d seconds!", attempts, 2*attempts);
- Utility::sleep(2*attempts);
- if( _previousFileSize == 0 ) {
- _previousFileSize = _item._size;
- } else {
- _previousFileSize = trans->stat_size;
- }
- continue;
- }
-
- const QString errMsg = tr("Local file changed during sync, syncing once it arrived completely");
- done( SyncFileItem::SoftError, errMsg );
- } else if( state == HBF_USER_ABORTED ) {
- const QString errMsg = tr("Sync was aborted by user.");
- done( SyncFileItem::SoftError, errMsg );
- } else {
- // Other HBF error conditions.
- // FIXME: find out the error class.
- _item._httpErrorCode = hbf_fail_http_code(trans.data());
- done(SyncFileItem::NormalError, hbf_error_string(trans.data(), state));
- }
- return;
- }
-
- ne_set_notifier(_propagator->_session, 0, 0);
-
- if( trans->modtime_accepted ) {
- _item._etag = parseEtag(hbf_transfer_etag( trans.data() ));
- } else {
- if (!updateMTimeAndETag(uri.data(), _item._modtime))
- return;
- }
-
- _propagator->_journal->setFileRecord(SyncJournalFileRecord(_item, _propagator->_localDir + _item._file));
- // Remove from the progress database:
- _propagator->_journal->setUploadInfo(_item._file, SyncJournalDb::UploadInfo());
- _propagator->_journal->commit("upload file start");
-
- if (hbf_validate_source_file(trans.data()) == HBF_SOURCE_FILE_CHANGE) {
- /* Did the source file changed since the upload ?
- This is different from the previous check because the previous check happens between
- chunks while this one happens when the whole file has been uploaded.
-
- The new etag is already stored in the database in the previous lines so in case of
- crash, we won't have a conflict but we will properly do a new upload
- */
-
- if( attempts++ < 5 ) { /* FIXME: How often do we want to try? */
- qDebug("SOURCE file has changed after upload, retry #%d in %d seconds!", attempts, 2*attempts);
- Utility::sleep(2*attempts);
- continue;
- }
-
- // Still the file change error, but we tried a couple of times.
- // Ignore this file for now.
- // Lets remove the file from the server (at least if it is new) as it is different
- // from our file here.
- if( _item._instruction == CSYNC_INSTRUCTION_NEW ) {
- QScopedPointer<char, QScopedPointerPodDeleter> uri(
- ne_path_escape((_propagator->_remoteDir + _item._file).toUtf8()));
-
- int rc = ne_delete(_propagator->_session, uri.data());
- qDebug() << "Remove the invalid file from server:" << rc;
- }
-
- const QString errMsg = tr("Local file changed during sync, syncing once it arrived completely");
- done( SyncFileItem::SoftError, errMsg );
- return;
- }
-
-
-
- emit progress(Progress::EndUpload, _item, _item._size, _item._size);
- done(SyncFileItem::Success);
- return;
-
- } while( true );
-}
-
-void PropagateUploadFile::chunk_finished_cb(hbf_transfer_s *trans, int chunk, void* userdata)
-{
- PropagateUploadFile *that = static_cast<PropagateUploadFile *>(userdata);
- Q_ASSERT(that);
- that->_chunked_done += trans->block_arr[chunk]->size;
- if (trans->block_cnt > 1) {
- SyncJournalDb::UploadInfo pi;
- pi._valid = true;
- pi._chunk = chunk + 1; // next chunk to start with
- pi._transferid = trans->transfer_id;
- pi._modtime = Utility::qDateTimeFromTime_t(trans->modtime);
- that->_propagator->_journal->setUploadInfo(that->_item._file, pi);
- that->_propagator->_journal->commit("Upload info");
- }
-}
-
-void PropagateUploadFile::notify_status_cb(void* userdata, ne_session_status status,
- const ne_session_status_info* info)
-{
- PropagateUploadFile* that = reinterpret_cast<PropagateUploadFile*>(userdata);
-
- if (status == ne_status_sending && info->sr.total > 0) {
- emit that->progress(Progress::Context, that->_item,
- that->_chunked_done + info->sr.progress,
- that->_chunked_total_size ? that->_chunked_total_size : info->sr.total );
-
- that->limitBandwidth(that->_chunked_done + info->sr.progress, that->_propagator->_uploadLimit.fetchAndAddAcquire(0));
- }
-}
-
-
-
-static QString parseFileId(ne_request *req) {
- QString fileId;
-
- const char *header = ne_get_response_header(req, "OC-FileId");
- if( header ) {
- fileId = QString::fromUtf8(header);
- }
- return fileId;
-}
-
-bool PropagateNeonJob::updateMTimeAndETag(const char* uri, time_t mtime)
-{
- QByteArray modtime = QByteArray::number(qlonglong(mtime));
- ne_propname pname;
- pname.nspace = "DAV:";
- pname.name = "lastmodified";
- ne_proppatch_operation ops[2];
- ops[0].name = &pname;
- ops[0].type = ne_propset;
- ops[0].value = modtime.constData();
- ops[1].name = NULL;
-
- int rc = ne_proppatch( _propagator->_session, uri, ops );
- Q_UNUSED(rc);
- /* FIXME: error handling
- bool error = updateErrorFromSession( rc );
- if( error ) {
- // FIXME: We could not set the mtime. Error or not?
- qDebug() << "PROP-Patching of modified date failed.";
- }*/
-
- // get the etag
- QScopedPointer<ne_request, ScopedPointerHelpers> req(ne_request_create(_propagator->_session, "HEAD", uri));
- int neon_stat = ne_request_dispatch(req.data());
- if (updateErrorFromSession(neon_stat, req.data())) {
- return false;
- } else {
- _item._etag = parseEtag(ne_get_response_header(req.data(), "etag"));
- QString fid = parseFileId(req.data());
- if( _item._fileId.isEmpty() ) {
- _item._fileId = fid;
- qDebug() << "FileID was empty, set it to " << _item._fileId;
- } else {
- if( !fid.isEmpty() && fid != _item._fileId ) {
- qDebug() << "WARN: FileID seems to have changed: "<< fid << _item._fileId;
- } else {
- qDebug() << "FileID is " << _item._fileId;
- }
- }
- return true;
- }
-}
-
-void PropagateNeonJob::limitBandwidth(qint64 progress, qint64 bandwidth_limit)
-{
- if (bandwidth_limit > 0) {
- int64_t diff = _lastTime.nsecsElapsed() / 1000;
- int64_t len = progress - _lastProgress;
- if (len > 0 && diff > 0 && (1000000 * len / diff) > bandwidth_limit) {
- int64_t wait_time = (1000000 * len / bandwidth_limit) - diff;
- if (wait_time > 0) {
- //qDebug() << "Limiting bandwidth to " << bandwidth_limit << "KB/s by waiting " << wait_time << " µs; ";
- Mirall::Utility::usleep(wait_time);
- }
- }
- _lastProgress = progress;
- _lastTime.start();
- } else if (bandwidth_limit < 0 && bandwidth_limit > -100) {
- int64_t diff = _lastTime.nsecsElapsed() / 1000;
- if (diff > 0) {
- // -bandwidth_limit is the % of bandwidth
- int64_t wait_time = -diff * (1 + 100.0 / bandwidth_limit);
- if (wait_time > 0) {
- Mirall::Utility::usleep(wait_time);
-
- }
- }
- _lastTime.start();
- }
-}
-
-int PropagateDownloadFile::content_reader(void *userdata, const char *buf, size_t len)
-{
- PropagateDownloadFile *that = static_cast<PropagateDownloadFile *>(userdata);
- size_t written = 0;
-
- if (that->_propagator->_abortRequested.fetchAndAddRelaxed(0)) {
- ne_set_error(that->_propagator->_session, "%s", tr("Sync was aborted by user.").toUtf8().data());
- return NE_ERROR;
- }
-
- if(buf) {
- written = that->_file->write(buf, len);
- if( len != written || that->_file->error() != QFile::NoError) {
- qDebug() << "WRN: content_reader wrote wrong num of bytes:" << len << "," << written;
- return NE_ERROR;
- }
- return NE_OK;
- }
-
- return NE_ERROR;
-}
-
-/*
- * This hook is called after the response is here from the server, but before
- * the response body is parsed. It decides if the response is compressed and
- * if it is it installs the compression reader accordingly.
- * If the response is not compressed, the normal response body reader is installed.
- */
-void PropagateDownloadFile::install_content_reader( ne_request *req, void *userdata, const ne_status *status )
-{
- PropagateDownloadFile *that = static_cast<PropagateDownloadFile *>(userdata);
-
- Q_UNUSED(status);
-
- if( !that ) {
- qDebug("Error: install_content_reader called without valid write context!");
- return;
- }
-
- if( ne_get_status(req)->klass != 2 ) {
- qDebug() << "Request class != 2, aborting.";
- ne_add_response_body_reader( req, do_not_accept,
- do_not_download_content_reader,
- (void*) that );
- return;
- }
-
- QByteArray reason_phrase = ne_get_status(req)->reason_phrase;
- if(reason_phrase == QByteArray("Connection established")) {
- ne_add_response_body_reader( req, ne_accept_2xx,
- content_reader,
- (void*) that );
- return;
- }
-
- QByteArray etag = parseEtag(ne_get_response_header(req, "etag"));
- if(etag.isEmpty())
- etag = parseEtag(ne_get_response_header(req, "ETag"));
-
- if (etag.isEmpty()) {
- qDebug() << Q_FUNC_INFO << "No E-Tag reply by server, considering it invalid" << ne_get_response_header(req, "etag");
- that->errorString = tr("No E-Tag received from server, check Proxy/Gateway");
- ne_set_error(that->_propagator->_session, "%s", that->errorString.toUtf8().data());
- ne_add_response_body_reader( req, do_not_accept,
- do_not_download_content_reader,
- (void*) that );
- return;
- } else if (!that->_expectedEtagForResume.isEmpty() && that->_expectedEtagForResume != etag) {
- qDebug() << Q_FUNC_INFO << "We received a different E-Tag for resuming!"
- << QString::fromLatin1(that->_expectedEtagForResume.data()) << "vs"
- << QString::fromLatin1(etag.data());
- that->errorString = tr("We received a different E-Tag for resuming. Retrying next time.");
- ne_set_error(that->_propagator->_session, "%s", that->errorString.toUtf8().data());
- ne_add_response_body_reader( req, do_not_accept,
- do_not_download_content_reader,
- (void*) that );
- return;
- }
-
-
- const char *enc = ne_get_response_header( req, "Content-Encoding" );
- qDebug("Content encoding ist <%s> with status %d", enc ? enc : "empty",
- status ? status->code : -1 );
-
- if( enc == QLatin1String("gzip") ) {
- that->_decompress.reset(ne_decompress_reader( req, ne_accept_2xx,
- content_reader, /* reader callback */
- that )); /* userdata */
- } else {
- ne_add_response_body_reader( req, ne_accept_2xx,
- content_reader,
- (void*) that );
- }
-}
-
-void PropagateDownloadFile::notify_status_cb(void* userdata, ne_session_status status,
- const ne_session_status_info* info)
-{
- PropagateDownloadFile* that = reinterpret_cast<PropagateDownloadFile*>(userdata);
- if (status == ne_status_recving && info->sr.total > 0) {
- emit that->progress(Progress::Context, that->_item, info->sr.progress, info->sr.total );
-
- that->limitBandwidth(info->sr.progress, that->_propagator->_downloadLimit.fetchAndAddAcquire(0));
- }
-}
-
-void PropagateDownloadFile::start()
-{
- if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
- return;
-
- emit progress(Progress::StartDownload, _item, 0, _item._size);
-
- QString tmpFileName;
- const SyncJournalDb::DownloadInfo progressInfo = _propagator->_journal->getDownloadInfo(_item._file);
- if (progressInfo._valid) {
- // if the etag has changed meanwhile, remove the already downloaded part.
- if (progressInfo._etag != _item._etag) {
- QFile::remove(_propagator->_localDir + progressInfo._tmpfile);
- _propagator->_journal->setDownloadInfo(_item._file, SyncJournalDb::DownloadInfo());
- } else {
- tmpFileName = progressInfo._tmpfile;
- _expectedEtagForResume = progressInfo._etag;
- }
-
- }
-
- if (tmpFileName.isEmpty()) {
- tmpFileName = _item._file;
- //add a dot at the begining of the filename to hide the file.
- int slashPos = tmpFileName.lastIndexOf('/');
- tmpFileName.insert(slashPos+1, '.');
- //add the suffix
- tmpFileName += ".~" + QString::number(uint(qrand()), 16);
- }
-
- QFile tmpFile(_propagator->_localDir + tmpFileName);
- _file = &tmpFile;
- if (!tmpFile.open(QIODevice::Append | QIODevice::Unbuffered)) {
- done(SyncFileItem::NormalError, tmpFile.errorString());
- return;
- }
-
- csync_win32_set_file_hidden(tmpFile.fileName().toUtf8().constData(), true);
-
- {
- SyncJournalDb::DownloadInfo pi;
- pi._etag = _item._etag;
- pi._tmpfile = tmpFileName;
- pi._valid = true;
- _propagator->_journal->setDownloadInfo(_item._file, pi);
- _propagator->_journal->commit("download file start");
- }
-
- /* actually do the request */
- int retry = 0;
-
- QScopedPointer<char, QScopedPointerPodDeleter> uri(
- ne_path_escape((_propagator->_remoteDir + _item._file).toUtf8()));
-
- do {
- QScopedPointer<ne_request, ScopedPointerHelpers> req(ne_request_create(_propagator->_session, "GET", uri.data()));
-
- /* Allow compressed content by setting the header */
- ne_add_request_header( req.data(), "Accept-Encoding", "gzip" );
-
- if (tmpFile.size() > 0) {
- quint64 done = tmpFile.size();
- if (done == _item._size) {
- qDebug() << "File is already complete, no need to download";
- break;
- }
- QByteArray rangeRequest = "bytes=" + QByteArray::number(done) +'-';
- ne_add_request_header(req.data(), "Range", rangeRequest.constData());
- ne_add_request_header(req.data(), "Accept-Ranges", "bytes");
- qDebug() << "Retry with range " << rangeRequest;
- }
-
- /* hook called before the content is parsed to set the correct reader,
- * either the compressed- or uncompressed reader.
- */
- ne_hook_post_headers( _propagator->_session, install_content_reader, this);
- ne_set_notifier(_propagator->_session, notify_status_cb, this);
- _lastProgress = 0;
- _lastTime.start();
-
- int neon_stat = ne_request_dispatch(req.data());
-
- _decompress.reset(); // Destroy the decompress after the request has been dispatched.
-
- /* delete the hook again, otherwise they get chained as they are with the session */
- ne_unhook_post_headers( _propagator->_session, install_content_reader, this );
- ne_set_notifier(_propagator->_session, 0, 0);
-
- if (neon_stat == NE_TIMEOUT && (++retry) < 3) {
- continue;
- }
-
- // This one is set by install_content_reader if e.g. there is no E-Tag
- if (!errorString.isEmpty()) {
- // don't keep the temporary file as the file downloaded so far is invalid
- tmpFile.close();
- tmpFile.remove();
- _propagator->_journal->setDownloadInfo(_item._file, SyncJournalDb::DownloadInfo());
- done(SyncFileItem::SoftError, errorString);
- return;
- }
-
- // This one is set by neon
- if( updateErrorFromSession(neon_stat, req.data() ) ) {
- qDebug("Error GET: Neon: %d", neon_stat);
- if (tmpFile.size() == 0) {
- // don't keep the temporary file if it is empty.
- tmpFile.close();
- tmpFile.remove();
- _propagator->_journal->setDownloadInfo(_item._file, SyncJournalDb::DownloadInfo());
- }
- return;
- }
- _item._etag = parseEtag(ne_get_response_header(req.data(), "etag"));
- break;
- } while (1);
-
- tmpFile.close();
- tmpFile.flush();
- QString fn = _propagator->_localDir + _item._file;
-
-
- bool isConflict = _item._instruction == CSYNC_INSTRUCTION_CONFLICT
- && !fileEquals(fn, tmpFile.fileName()); // compare the files to see if there was an actual conflict.
- //In case of conflict, make a backup of the old file
- if (isConflict) {
- QFile f(fn);
- QString conflictFileName(fn);
- // Add _conflict-XXXX before the extention.
- int dotLocation = conflictFileName.lastIndexOf('.');
- // If no extention, add it at the end (take care of cases like foo/.hidden or foo.bar/file)
- if (dotLocation <= conflictFileName.lastIndexOf('/') + 1) {
- dotLocation = conflictFileName.size();
- }
- QString timeString = Utility::qDateTimeFromTime_t(_item._modtime).toString("yyyyMMdd-hhmmss");
- conflictFileName.insert(dotLocation, "_conflict-" + timeString);
- if (!f.rename(conflictFileName)) {
- //If the rename fails, don't replace it.
- done(SyncFileItem::NormalError, f.errorString());
- return;
- }
- }
-
- QFileInfo existingFile(fn);
- if(existingFile.exists() && existingFile.permissions() != tmpFile.permissions()) {
- tmpFile.setPermissions(existingFile.permissions());
- }
-
- csync_win32_set_file_hidden(tmpFile.fileName().toUtf8().constData(), false);
-
-#ifndef Q_OS_WIN
- bool success;
-#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
- success = tmpFile.fileEngine()->rename(fn);
- // qDebug() << "Renaming " << tmpFile.fileName() << " to " << fn;
-#else
- // We want a rename that also overwite. QFile::rename does not overwite.
- // Qt 5.1 has QSaveFile::renameOverwrite we cold use.
- // ### FIXME
- QFile::remove(fn);
- success = tmpFile.rename(fn);
-#endif
- // unixoids
- if (!success) {
- qDebug() << "FAIL: renaming temp file to final failed: " << tmpFile.errorString();
- done(SyncFileItem::NormalError, tmpFile.errorString());
- return;
- }
-#else //Q_OS_WIN
- BOOL ok;
- ok = MoveFileEx((wchar_t*)tmpFile.fileName().utf16(),
- (wchar_t*)QString(_propagator->_localDir + _item._file).utf16(),
- MOVEFILE_REPLACE_EXISTING+MOVEFILE_COPY_ALLOWED+MOVEFILE_WRITE_THROUGH);
- if (!ok) {
- wchar_t *string = 0;
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
- NULL, ::GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPWSTR)&string, 0, NULL);
-
- done(SyncFileItem::NormalError, QString::fromWCharArray(string));
- LocalFree((HLOCAL)string);
- return;
- }
-#endif
- struct timeval times[2];
- times[0].tv_sec = times[1].tv_sec = _item._modtime;
- times[0].tv_usec = times[1].tv_usec = 0;
- c_utimes(fn.toUtf8().data(), times);
-
- _propagator->_journal->setFileRecord(SyncJournalFileRecord(_item, fn));
- _propagator->_journal->setDownloadInfo(_item._file, SyncJournalDb::DownloadInfo());
- _propagator->_journal->commit("download file start2");
- emit progress(Progress::EndDownload, _item, _item._size, _item._size);
- done(isConflict ? SyncFileItem::Conflict : SyncFileItem::Success);
-}
-
-
-void PropagateLocalRename::start()
-{
- if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
- return;
-
- // if the file is a file underneath a moved dir, the _item.file is equal
- // to _item.renameTarget and the file is not moved as a result.
- if (_item._file != _item._renameTarget) {
- emit progress(Progress::StartRename, _item, 0, _item._size);
- qDebug() << "MOVE " << _propagator->_localDir + _item._file << " => " << _propagator->_localDir + _item._renameTarget;
- QFile::rename(_propagator->_localDir + _item._file, _propagator->_localDir + _item._renameTarget);
- emit progress(Progress::EndRename, _item, _item._size, _item._size);
- }
-
- _item._instruction = CSYNC_INSTRUCTION_DELETED;
- _propagator->_journal->deleteFileRecord(_item._originalFile);
-
- // store the rename file name in the item.
- _item._file = _item._renameTarget;
-
- SyncJournalFileRecord record(_item, _propagator->_localDir + _item._renameTarget);
- record._path = _item._renameTarget;
-
- if (!_item._isDirectory) { // Directory are saved at the end
- _propagator->_journal->setFileRecord(record);
- }
- _propagator->_journal->commit("localRename");
-
-
- done(SyncFileItem::Success);
-}
-
-void PropagateRemoteRename::start()
-{
- if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
- return;
-
- if (_item._file == _item._renameTarget) {
- if (!_item._isDirectory) {
- // The parents has been renamed already so there is nothing more to do.
- // But we still need to fetch the new ETAG
- // FIXME maybe do a recusrsive propfind after having moved the parent.
- // Note: we also update the mtime because the server do not keep the mtime when moving files
- QScopedPointer<char, QScopedPointerPodDeleter> uri2(
- ne_path_escape((_propagator->_remoteDir + _item._renameTarget).toUtf8()));
- if (!updateMTimeAndETag(uri2.data(), _item._modtime))
- return;
- }
- } 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(Progress::StartRename, _item, 0, _item._size);
-
- QScopedPointer<char, QScopedPointerPodDeleter> uri1(ne_path_escape((_propagator->_remoteDir + _item._file).toUtf8()));
- QScopedPointer<char, QScopedPointerPodDeleter> 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());
-
- if( checkForProblemsWithShared()) {
- return;
- }
-
- if (updateErrorFromSession(rc)) {
- return;
- }
-
- if (!updateMTimeAndETag(uri2.data(), _item._modtime))
- return;
- emit progress(Progress::EndRename, _item, _item._size, _item._size);
- }
-
- _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 ) {
- qDebug("Neon error code was %d", neon_code);
- }
-
- QString errorString;
- int httpStatusCode = 0;
-
- switch(neon_code) {
- case NE_OK: /* Success, but still the possiblity of problems */
- if( req ) {
- const ne_status *status = ne_get_status(req);
-
- if (status) {
- if ( status->klass == 2 || status->code == ignoreHttpCode) {
- // Everything is ok, no error.
- return false;
- }
- errorString = QString::fromUtf8( status->reason_phrase );
- httpStatusCode = status->code;
- _item._httpErrorCode = httpStatusCode;
- }
- } else {
- errorString = QString::fromUtf8(ne_get_error(_propagator->_session));
- httpStatusCode = errorString.mid(0, errorString.indexOf(QChar(' '))).toInt();
- _item._httpErrorCode = httpStatusCode;
- if ((httpStatusCode >= 200 && httpStatusCode < 300)
- || (httpStatusCode != 0 && httpStatusCode == ignoreHttpCode)) {
- // No error
- return false;
- }
- }
- // FIXME: classify the error
- done (SyncFileItem::NormalError, errorString);
- return true;
- case NE_ERROR: /* Generic error; use ne_get_error(session) for message */
- errorString = QString::fromUtf8(ne_get_error(_propagator->_session));
- // Check if we don't need to ignore that error.
- httpStatusCode = errorString.mid(0, errorString.indexOf(QChar(' '))).toInt();
- _item._httpErrorCode = httpStatusCode;
- qDebug() << Q_FUNC_INFO << "NE_ERROR" << errorString << httpStatusCode << ignoreHttpCode;
- if (ignoreHttpCode && httpStatusCode == ignoreHttpCode)
- return false;
-
- done(SyncFileItem::NormalError, errorString);
- return true;
- case NE_LOOKUP: /* Server or proxy hostname lookup failed */
- case NE_AUTH: /* User authentication failed on server */
- case NE_PROXYAUTH: /* User authentication failed on proxy */
- case NE_CONNECT: /* Could not connect to server */
- case NE_TIMEOUT: /* Connection timed out */
- done(SyncFileItem::FatalError, QString::fromUtf8(ne_get_error(_propagator->_session)));
- return true;
- case NE_FAILED: /* The precondition failed */
- case NE_RETRY: /* Retry request (ne_end_request ONLY) */
- case NE_REDIRECT: /* See ne_redirect.h */
- default:
- done(SyncFileItem::SoftError, QString::fromUtf8(ne_get_error(_propagator->_session)));
- return true;
- }
- return false;
-}
PropagateItemJob* OwncloudPropagator::createJob(const SyncFileItem& item) {
switch(item._instruction) {
diff --git a/src/mirall/owncloudpropagator_p.h b/src/mirall/owncloudpropagator_p.h
index 6085f16..dd0fda8 100644
--- a/src/mirall/owncloudpropagator_p.h
+++ b/src/mirall/owncloudpropagator_p.h
@@ -15,12 +15,6 @@
#pragma once
-#include "owncloudpropagator.h"
-#include <httpbf.h>
-#include <neon/ne_compress.h>
-#include <QFile>
-#include <qdebug.h>
-
// We use some internals of csync:
extern "C" int c_utimes(const char *, const struct timeval *);
extern "C" void csync_win32_set_file_hidden( const char *file, bool h );
@@ -31,161 +25,6 @@ namespace Mirall {
/** compare two files with given filename and return true if they have the same content */
bool fileEquals(const QString &fn1, const QString &fn2);
-/* Helper for QScopedPointer<>, to be used as the deleter.
- * QScopePointer will call the right overload of cleanup for the pointer it holds
- */
-struct ScopedPointerHelpers {
- static inline void cleanup(hbf_transfer_t *pointer) { if (pointer) hbf_free_transfer(pointer); }
- static inline void cleanup(ne_request *pointer) { if (pointer) ne_request_destroy(pointer); }
- static inline void cleanup(ne_decompress *pointer) { if (pointer) ne_decompress_destroy(pointer); }
-// static inline void cleanup(ne_propfind_handler *pointer) { if (pointer) ne_propfind_destroy(pointer); }
-};
-
-
-/*
- * Abstract class for neon job. Lives in the neon thread
- */
-class PropagateNeonJob : public PropagateItemJob {
- Q_OBJECT
-protected:
-
- /* Issue a PROPPATCH and PROPFIND to update the mtime, and fetch the etag
- * Return true in case of success, and false if the PROPFIND failed and the
- * error has been reported
- */
- bool updateMTimeAndETag(const char *uri, time_t);
-
- /* fetch the error code and string from the session
- in case of error, calls done with the error and returns true.
-
- If the HTTP error code is ignoreHTTPError, the error is ignored
- */
- bool updateErrorFromSession(int neon_code = 0, ne_request *req = 0, int ignoreHTTPError = 0);
-
- /*
- * to be called by the progress callback and will wait the amount of time needed.
- */
- void limitBandwidth(qint64 progress, qint64 limit);
-
- bool checkForProblemsWithShared();
-
- QElapsedTimer _lastTime;
- qint64 _lastProgress;
- int _httpStatusCode;
-
-protected slots:
- void slotRestoreJobCompleted(const SyncFileItem& );
-
-private:
- QScopedPointer<PropagateItemJob> _restoreJob;
-
-public:
- PropagateNeonJob(OwncloudPropagator* propagator, const SyncFileItem &item)
- : PropagateItemJob(propagator, item), _lastProgress(0), _httpStatusCode(0) {
- moveToThread(propagator->_neonThread);
- }
-
-};
-
-
-
-class PropagateLocalRemove : public PropagateItemJob {
- Q_OBJECT
-public:
- PropagateLocalRemove (OwncloudPropagator* propagator,const SyncFileItem& item) : PropagateItemJob(propagator, item) {}
- void start();
-};
-class PropagateLocalMkdir : public PropagateItemJob {
- Q_OBJECT
-public:
- PropagateLocalMkdir (OwncloudPropagator* propagator,const SyncFileItem& item) : PropagateItemJob(propagator, item) {}
- void start();
-};
-class PropagateRemoteRemove : public PropagateNeonJob {
- Q_OBJECT
-public:
- PropagateRemoteRemove (OwncloudPropagator* propagator,const SyncFileItem& item) : PropagateNeonJob(propagator, item) {}
- void start();
-};
-class PropagateRemoteMkdir : public PropagateNeonJob {
- Q_OBJECT
-public:
- PropagateRemoteMkdir (OwncloudPropagator* propagator,const SyncFileItem& item) : PropagateNeonJob(propagator, item) {}
- void start();
-};
-class PropagateLocalRename : public PropagateItemJob {
- Q_OBJECT
-public:
- PropagateLocalRename (OwncloudPropagator* propagator,const SyncFileItem& item) : PropagateItemJob(propagator, item) {}
- void start();
-};
-class PropagateRemoteRename : public PropagateNeonJob {
- Q_OBJECT
-public:
- PropagateRemoteRename (OwncloudPropagator* propagator,const SyncFileItem& item) : PropagateNeonJob(propagator, item) {}
- void start();
-};
-
-class PropagateUploadFile: public PropagateNeonJob {
- Q_OBJECT
-public:
- explicit PropagateUploadFile(OwncloudPropagator* propagator,const SyncFileItem& item)
- : PropagateNeonJob(propagator, item), _previousFileSize(0) {}
- void start();
-private:
- // Log callback for httpbf
- static void _log_callback(const char *func, const char *text, void*)
- {
- qDebug() << " " << func << text;
- }
-
- // abort callback for httpbf
- static int _user_want_abort(void *userData)
- {
- return static_cast<PropagateUploadFile *>(userData)->_propagator->_abortRequested.fetchAndAddRelaxed(0);
- }
-
- // callback from httpbf when a chunk is finished
- static void chunk_finished_cb(hbf_transfer_s *trans, int chunk, void* userdata);
- static void notify_status_cb(void* userdata, ne_session_status status,
- const ne_session_status_info* info);
-
- qint64 _chunked_done; // amount of bytes already sent with the previous chunks
- qint64 _chunked_total_size; // total size of the whole file
- qint64 _previousFileSize; // In case the file size has changed during upload, this is the previous one.
-};
-
-class PropagateDownloadFile: public PropagateNeonJob {
- Q_OBJECT
-public:
- explicit PropagateDownloadFile(OwncloudPropagator* propagator,const SyncFileItem& item)
- : PropagateNeonJob(propagator, item), _file(0) {}
- void start();
-private:
- QFile *_file;
- QScopedPointer<ne_decompress, ScopedPointerHelpers> _decompress;
- QString errorString;
- QByteArray _expectedEtagForResume;
-
- static int do_not_accept (void *userdata, ne_request *req, const ne_status *st)
- {
- Q_UNUSED(userdata); Q_UNUSED(req); Q_UNUSED(st);
- return 0; // ignore this response
- }
-
- static int do_not_download_content_reader(void *userdata, const char *buf, size_t len)
- {
- Q_UNUSED(userdata); Q_UNUSED(buf); Q_UNUSED(len);
- return NE_ERROR;
- }
-
- // neon hooks:
- static int content_reader(void *userdata, const char *buf, size_t len);
- static void install_content_reader( ne_request *req, void *userdata, const ne_status *status );
- static void notify_status_cb(void* userdata, ne_session_status status,
- const ne_session_status_info* info);
-};
-
inline QByteArray parseEtag(const char *header) {
if (!header)
return QByteArray();
diff --git a/src/mirall/owncloudpropagator.cpp b/src/mirall/propagator_legacy.cpp
similarity index 56%
copy from src/mirall/owncloudpropagator.cpp
copy to src/mirall/propagator_legacy.cpp
index 7113305..da3ff15 100644
--- a/src/mirall/owncloudpropagator.cpp
+++ b/src/mirall/propagator_legacy.cpp
@@ -13,11 +13,12 @@
* for more details.
*/
+#include "propagator_legacy.h"
#include "owncloudpropagator_p.h"
+
+#include "utility.h"
#include "syncjournaldb.h"
#include "syncjournalfilerecord.h"
-#include "utility.h"
-#include "owncloudpropagator_qnam.h"
#include <httpbf.h>
#include <qfile.h>
#include <qdir.h>
@@ -49,241 +50,11 @@
#include <time.h>
-/* The maximum number of active job in parallel */
-static const int maximumActiveJob = 6;
namespace Mirall {
-void PropagateItemJob::done(SyncFileItem::Status status, const QString &errorString)
-{
- _item._errorString = errorString;
- _item._status = status;
-
- // Blacklisting
- int retries = 0;
-
- if( _item._httpErrorCode == 403 ||_item._httpErrorCode == 413 || _item._httpErrorCode == 415 ) {
- qDebug() << "Fatal Error condition" << _item._httpErrorCode << ", forbid retry!";
- retries = -1;
- } else {
- retries = 3; // FIXME: good number of allowed retries?
- }
- SyncJournalBlacklistRecord record(_item, retries);;
-
- switch( status ) {
- case SyncFileItem::SoftError:
- // do not blacklist in case of soft error.
- emit progress( Progress::SoftError, _item, 0, 0 );
- break;
- case SyncFileItem::FatalError:
- case SyncFileItem::NormalError:
- _propagator->_journal->updateBlacklistEntry( record );
- if( status == SyncFileItem::NormalError ) {
- emit progress( Progress::NormalError, _item, 0, 0 );
- }
- break;
- case SyncFileItem::Success:
- if( _item._blacklistedInDb ) {
- // wipe blacklist entry.
- _propagator->_journal->wipeBlacklistEntry(_item._file);
- }
- break;
- case SyncFileItem::Conflict:
- case SyncFileItem::FileIgnored:
- case SyncFileItem::NoStatus:
- // nothing
- break;
- }
-
- emit completed(_item);
- emit finished(status);
-}
-
-/**
- * For delete or remove, check that we are not removing from a shared directory.
- * If we are, try to restore the file
- *
- * Return true if the problem is handled.
- */
-bool PropagateNeonJob::checkForProblemsWithShared()
-{
- QString errorString = QString::fromUtf8(ne_get_error(_propagator->_session));
- int httpStatusCode = errorString.mid(0, errorString.indexOf(QChar(' '))).toInt();
-
- if( httpStatusCode == 403 && _propagator->isInSharedDirectory(_item._file )) {
- if( _item._type != SyncFileItem::Directory ) {
- // the file was removed locally from a read only Shared sync
- // the file is gone locally and it should be recovered.
- SyncFileItem downloadItem(_item);
- downloadItem._instruction = CSYNC_INSTRUCTION_SYNC;
- downloadItem._dir = SyncFileItem::Down;
- _restoreJob.reset(new PropagateDownloadFile(_propagator, downloadItem));
- } else {
- // Directories are harder to recover.
- // But just re-create the directory, next sync will be able to recover the files
- SyncFileItem mkdirItem(_item);
- mkdirItem._instruction = CSYNC_INSTRUCTION_SYNC;
- mkdirItem._dir = SyncFileItem::Down;
- _restoreJob.reset(new PropagateLocalMkdir(_propagator, mkdirItem));
- // Also remove the inodes and fileid from the db so no further renames are tried for
- // this item.
- _propagator->_journal->avoidRenamesOnNextSync(_item._file);
- }
- connect(_restoreJob.data(), SIGNAL(completed(SyncFileItem)),
- this, SLOT(slotRestoreJobCompleted(SyncFileItem)));
- QMetaObject::invokeMethod(_restoreJob.data(), "start");
- return true;
- }
- return false;
-}
-
-void PropagateNeonJob::slotRestoreJobCompleted(const SyncFileItem& item )
-{
- if( item._status == SyncFileItem::Success ) {
- done( SyncFileItem::SoftError, tr("The file was removed from a read only share. The file has been restored."));
- } else {
- done( item._status, tr("A file was removed from a read only share, but restoring failed: %1").arg(item._errorString) );
- }
-}
-
-
-// compare two files with given filename and return true if they have the same content
-bool fileEquals(const QString &fn1, const QString &fn2) {
- QFile f1(fn1);
- QFile f2(fn2);
- if (!f1.open(QIODevice::ReadOnly) || !f2.open(QIODevice::ReadOnly)) {
- qDebug() << "fileEquals: Failed to open " << fn1 << "or" << fn2;
- return false;
- }
-
- if (f1.size() != f2.size()) {
- return false;
- }
-
- const int BufferSize = 16 * 1024;
- char buffer1[BufferSize];
- char buffer2[BufferSize];
- do {
- int r = f1.read(buffer1, BufferSize);
- if (f2.read(buffer2, BufferSize) != r) {
- // this should normaly not happen: the file are supposed to have the same size.
- return false;
- }
- if (r <= 0) {
- return true;
- }
- if (memcmp(buffer1, buffer2, r) != 0) {
- return false;
- }
- } while (true);
- return false;
-}
-
-// Code copied from Qt5's QDir::removeRecursively
-static bool removeRecursively(const QString &path)
-{
- bool success = true;
- QDirIterator di(path, QDir::AllEntries | QDir::Hidden | QDir::System | QDir::NoDotAndDotDot);
- while (di.hasNext()) {
- di.next();
- const QFileInfo& fi = di.fileInfo();
- bool ok;
- if (fi.isDir() && !fi.isSymLink())
- ok = removeRecursively(di.filePath()); // recursive
- else
- ok = QFile::remove(di.filePath());
- if (!ok)
- success = false;
- }
- if (success)
- success = QDir().rmdir(path);
- return success;
-}
-
-void PropagateLocalRemove::start()
-{
- if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
- return;
-
- QString filename = _propagator->_localDir + _item._file;
- if (_item._isDirectory) {
- if (QDir(filename).exists() && !removeRecursively(filename)) {
- done(SyncFileItem::NormalError, tr("Could not remove directory %1").arg(filename));
- return;
- }
- } else {
- QFile file(filename);
- if (file.exists() && !file.remove()) {
- done(SyncFileItem::NormalError, file.errorString());
- return;
- }
- }
- emit progress(Progress::StartDelete, _item, 0, _item._size);
- _propagator->_journal->deleteFileRecord(_item._originalFile, _item._isDirectory);
- _propagator->_journal->commit("Local remove");
- done(SyncFileItem::Success);
- emit progress(Progress::EndDelete, _item, _item._size, _item._size);
-}
-
-void PropagateLocalMkdir::start()
-{
- if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
- return;
-
- QDir d;
- if (!d.mkpath(_propagator->_localDir + _item._file)) {
- done(SyncFileItem::NormalError, tr("could not create directory %1").arg(_propagator->_localDir + _item._file));
- return;
- }
- done(SyncFileItem::Success);
-}
-
-void PropagateRemoteRemove::start()
-{
- if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
- return;
-
- QScopedPointer<char, QScopedPointerPodDeleter> uri(
- ne_path_escape((_propagator->_remoteDir + _item._file).toUtf8()));
- emit progress(Progress::StartDelete, _item, 0, _item._size);
- qDebug() << "** DELETE " << uri.data();
- int rc = ne_delete(_propagator->_session, uri.data());
-
- if( checkForProblemsWithShared() ) {
- return;
- }
-
- /* Ignore the error 404, it means it is already deleted */
- if (updateErrorFromSession(rc, 0, 404)) {
- return;
- }
-
- _propagator->_journal->deleteFileRecord(_item._originalFile, _item._isDirectory);
- _propagator->_journal->commit("Remote Remove");
- done(SyncFileItem::Success);
- emit progress(Progress::EndDelete, _item, _item._size, _item._size);
-}
-
-void PropagateRemoteMkdir::start()
-{
- if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
- return;
-
- QScopedPointer<char, QScopedPointerPodDeleter> uri(
- ne_path_escape((_propagator->_remoteDir + _item._file).toUtf8()));
-
- int rc = ne_mkcol(_propagator->_session, uri.data());
-
- /* Special for mkcol: it returns 405 if the directory already exists.
- * Ignore that error */
- if( updateErrorFromSession( rc , 0, 405 ) ) {
- return;
- }
- done(SyncFileItem::Success);
-}
-
-void PropagateUploadFile::start()
+void PropagateUploadFileLegacy::start()
{
if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
return;
@@ -413,11 +184,11 @@ void PropagateUploadFile::start()
if (hbf_validate_source_file(trans.data()) == HBF_SOURCE_FILE_CHANGE) {
/* Did the source file changed since the upload ?
- This is different from the previous check because the previous check happens between
- chunks while this one happens when the whole file has been uploaded.
-
- The new etag is already stored in the database in the previous lines so in case of
- crash, we won't have a conflict but we will properly do a new upload
+ * This is different from the previous check because the previous check happens between
+ * chunks while this one happens when the whole file has been uploaded.
+ *
+ * The new etag is already stored in the database in the previous lines so in case of
+ * crash, we won't have a conflict but we will properly do a new upload
*/
if( attempts++ < 5 ) { /* FIXME: How often do we want to try? */
@@ -452,34 +223,34 @@ void PropagateUploadFile::start()
} while( true );
}
-void PropagateUploadFile::chunk_finished_cb(hbf_transfer_s *trans, int chunk, void* userdata)
+void PropagateUploadFileLegacy::chunk_finished_cb(hbf_transfer_s *trans, int chunk, void* userdata)
{
- PropagateUploadFile *that = static_cast<PropagateUploadFile *>(userdata);
- Q_ASSERT(that);
- that->_chunked_done += trans->block_arr[chunk]->size;
- if (trans->block_cnt > 1) {
- SyncJournalDb::UploadInfo pi;
- pi._valid = true;
- pi._chunk = chunk + 1; // next chunk to start with
- pi._transferid = trans->transfer_id;
- pi._modtime = Utility::qDateTimeFromTime_t(trans->modtime);
- that->_propagator->_journal->setUploadInfo(that->_item._file, pi);
- that->_propagator->_journal->commit("Upload info");
- }
+ PropagateUploadFileLegacy *that = static_cast<PropagateUploadFileLegacy *>(userdata);
+ Q_ASSERT(that);
+ that->_chunked_done += trans->block_arr[chunk]->size;
+ if (trans->block_cnt > 1) {
+ SyncJournalDb::UploadInfo pi;
+ pi._valid = true;
+ pi._chunk = chunk + 1; // next chunk to start with
+ pi._transferid = trans->transfer_id;
+ pi._modtime = Utility::qDateTimeFromTime_t(trans->modtime);
+ that->_propagator->_journal->setUploadInfo(that->_item._file, pi);
+ that->_propagator->_journal->commit("Upload info");
+ }
}
-void PropagateUploadFile::notify_status_cb(void* userdata, ne_session_status status,
- const ne_session_status_info* info)
+void PropagateUploadFileLegacy::notify_status_cb(void* userdata, ne_session_status status,
+ const ne_session_status_info* info)
{
- PropagateUploadFile* that = reinterpret_cast<PropagateUploadFile*>(userdata);
+ PropagateUploadFileLegacy* that = reinterpret_cast<PropagateUploadFileLegacy*>(userdata);
- if (status == ne_status_sending && info->sr.total > 0) {
- emit that->progress(Progress::Context, that->_item,
- that->_chunked_done + info->sr.progress,
- that->_chunked_total_size ? that->_chunked_total_size : info->sr.total );
+ if (status == ne_status_sending && info->sr.total > 0) {
+ emit that->progress(Progress::Context, that->_item,
+ that->_chunked_done + info->sr.progress,
+ that->_chunked_total_size ? that->_chunked_total_size : info->sr.total );
- that->limitBandwidth(that->_chunked_done + info->sr.progress, that->_propagator->_uploadLimit.fetchAndAddAcquire(0));
- }
+ that->limitBandwidth(that->_chunked_done + info->sr.progress, that->_propagator->_uploadLimit.fetchAndAddAcquire(0));
+ }
}
@@ -509,11 +280,11 @@ bool PropagateNeonJob::updateMTimeAndETag(const char* uri, time_t mtime)
int rc = ne_proppatch( _propagator->_session, uri, ops );
Q_UNUSED(rc);
/* FIXME: error handling
- bool error = updateErrorFromSession( rc );
- if( error ) {
- // FIXME: We could not set the mtime. Error or not?
- qDebug() << "PROP-Patching of modified date failed.";
- }*/
+ * bool error = updateErrorFromSession( rc );
+ * if( error ) {
+ * // FIXME: We could not set the mtime. Error or not?
+ * qDebug() << "PROP-Patching of modified date failed.";
+}*/
// get the etag
QScopedPointer<ne_request, ScopedPointerHelpers> req(ne_request_create(_propagator->_session, "HEAD", uri));
@@ -565,9 +336,9 @@ void PropagateNeonJob::limitBandwidth(qint64 progress, qint64 bandwidth_limit)
}
}
-int PropagateDownloadFile::content_reader(void *userdata, const char *buf, size_t len)
+int PropagateDownloadFileLegacy::content_reader(void *userdata, const char *buf, size_t len)
{
- PropagateDownloadFile *that = static_cast<PropagateDownloadFile *>(userdata);
+ PropagateDownloadFileLegacy *that = static_cast<PropagateDownloadFileLegacy *>(userdata);
size_t written = 0;
if (that->_propagator->_abortRequested.fetchAndAddRelaxed(0)) {
@@ -593,9 +364,9 @@ int PropagateDownloadFile::content_reader(void *userdata, const char *buf, size_
* if it is it installs the compression reader accordingly.
* If the response is not compressed, the normal response body reader is installed.
*/
-void PropagateDownloadFile::install_content_reader( ne_request *req, void *userdata, const ne_status *status )
+void PropagateDownloadFileLegacy::install_content_reader( ne_request *req, void *userdata, const ne_status *status )
{
- PropagateDownloadFile *that = static_cast<PropagateDownloadFile *>(userdata);
+ PropagateDownloadFileLegacy *that = static_cast<PropagateDownloadFileLegacy *>(userdata);
Q_UNUSED(status);
@@ -607,16 +378,16 @@ void PropagateDownloadFile::install_content_reader( ne_request *req, void *userd
if( ne_get_status(req)->klass != 2 ) {
qDebug() << "Request class != 2, aborting.";
ne_add_response_body_reader( req, do_not_accept,
- do_not_download_content_reader,
- (void*) that );
+ do_not_download_content_reader,
+ (void*) that );
return;
}
QByteArray reason_phrase = ne_get_status(req)->reason_phrase;
if(reason_phrase == QByteArray("Connection established")) {
ne_add_response_body_reader( req, ne_accept_2xx,
- content_reader,
- (void*) that );
+ content_reader,
+ (void*) that );
return;
}
@@ -629,41 +400,41 @@ void PropagateDownloadFile::install_content_reader( ne_request *req, void *userd
that->errorString = tr("No E-Tag received from server, check Proxy/Gateway");
ne_set_error(that->_propagator->_session, "%s", that->errorString.toUtf8().data());
ne_add_response_body_reader( req, do_not_accept,
- do_not_download_content_reader,
- (void*) that );
+ do_not_download_content_reader,
+ (void*) that );
return;
} else if (!that->_expectedEtagForResume.isEmpty() && that->_expectedEtagForResume != etag) {
qDebug() << Q_FUNC_INFO << "We received a different E-Tag for resuming!"
- << QString::fromLatin1(that->_expectedEtagForResume.data()) << "vs"
- << QString::fromLatin1(etag.data());
+ << QString::fromLatin1(that->_expectedEtagForResume.data()) << "vs"
+ << QString::fromLatin1(etag.data());
that->errorString = tr("We received a different E-Tag for resuming. Retrying next time.");
ne_set_error(that->_propagator->_session, "%s", that->errorString.toUtf8().data());
ne_add_response_body_reader( req, do_not_accept,
- do_not_download_content_reader,
- (void*) that );
+ do_not_download_content_reader,
+ (void*) that );
return;
}
const char *enc = ne_get_response_header( req, "Content-Encoding" );
qDebug("Content encoding ist <%s> with status %d", enc ? enc : "empty",
- status ? status->code : -1 );
+ status ? status->code : -1 );
if( enc == QLatin1String("gzip") ) {
that->_decompress.reset(ne_decompress_reader( req, ne_accept_2xx,
- content_reader, /* reader callback */
- that )); /* userdata */
+ content_reader, /* reader callback */
+ that )); /* userdata */
} else {
ne_add_response_body_reader( req, ne_accept_2xx,
- content_reader,
- (void*) that );
+ content_reader,
+ (void*) that );
}
}
-void PropagateDownloadFile::notify_status_cb(void* userdata, ne_session_status status,
- const ne_session_status_info* info)
+void PropagateDownloadFileLegacy::notify_status_cb(void* userdata, ne_session_status status,
+ const ne_session_status_info* info)
{
- PropagateDownloadFile* that = reinterpret_cast<PropagateDownloadFile*>(userdata);
+ PropagateDownloadFileLegacy* that = reinterpret_cast<PropagateDownloadFileLegacy*>(userdata);
if (status == ne_status_recving && info->sr.total > 0) {
emit that->progress(Progress::Context, that->_item, info->sr.progress, info->sr.total );
@@ -671,7 +442,7 @@ void PropagateDownloadFile::notify_status_cb(void* userdata, ne_session_status s
}
}
-void PropagateDownloadFile::start()
+void PropagateDownloadFileLegacy::start()
{
if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
return;
@@ -794,7 +565,7 @@ void PropagateDownloadFile::start()
bool isConflict = _item._instruction == CSYNC_INSTRUCTION_CONFLICT
- && !fileEquals(fn, tmpFile.fileName()); // compare the files to see if there was an actual conflict.
+ && !fileEquals(fn, tmpFile.fileName()); // compare the files to see if there was an actual conflict.
//In case of conflict, make a backup of the old file
if (isConflict) {
QFile f(fn);
@@ -821,25 +592,25 @@ void PropagateDownloadFile::start()
csync_win32_set_file_hidden(tmpFile.fileName().toUtf8().constData(), false);
-#ifndef Q_OS_WIN
+ #ifndef Q_OS_WIN
bool success;
-#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
+ #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
success = tmpFile.fileEngine()->rename(fn);
// qDebug() << "Renaming " << tmpFile.fileName() << " to " << fn;
-#else
+ #else
// We want a rename that also overwite. QFile::rename does not overwite.
// Qt 5.1 has QSaveFile::renameOverwrite we cold use.
// ### FIXME
QFile::remove(fn);
success = tmpFile.rename(fn);
-#endif
+ #endif
// unixoids
if (!success) {
qDebug() << "FAIL: renaming temp file to final failed: " << tmpFile.errorString();
done(SyncFileItem::NormalError, tmpFile.errorString());
return;
}
-#else //Q_OS_WIN
+ #else //Q_OS_WIN
BOOL ok;
ok = MoveFileEx((wchar_t*)tmpFile.fileName().utf16(),
(wchar_t*)QString(_propagator->_localDir + _item._file).utf16(),
@@ -854,7 +625,7 @@ void PropagateDownloadFile::start()
LocalFree((HLOCAL)string);
return;
}
-#endif
+ #endif
struct timeval times[2];
times[0].tv_sec = times[1].tv_sec = _item._modtime;
times[0].tv_usec = times[1].tv_usec = 0;
@@ -965,223 +736,58 @@ bool PropagateNeonJob::updateErrorFromSession(int neon_code, ne_request* req, in
int httpStatusCode = 0;
switch(neon_code) {
- case NE_OK: /* Success, but still the possiblity of problems */
- if( req ) {
- const ne_status *status = ne_get_status(req);
-
- if (status) {
- if ( status->klass == 2 || status->code == ignoreHttpCode) {
- // Everything is ok, no error.
- return false;
+ case NE_OK: /* Success, but still the possiblity of problems */
+ if( req ) {
+ const ne_status *status = ne_get_status(req);
+
+ if (status) {
+ if ( status->klass == 2 || status->code == ignoreHttpCode) {
+ // Everything is ok, no error.
+ return false;
+ }
+ errorString = QString::fromUtf8( status->reason_phrase );
+ httpStatusCode = status->code;
+ _item._httpErrorCode = httpStatusCode;
}
- errorString = QString::fromUtf8( status->reason_phrase );
- httpStatusCode = status->code;
+ } else {
+ errorString = QString::fromUtf8(ne_get_error(_propagator->_session));
+ httpStatusCode = errorString.mid(0, errorString.indexOf(QChar(' '))).toInt();
_item._httpErrorCode = httpStatusCode;
+ if ((httpStatusCode >= 200 && httpStatusCode < 300)
+ || (httpStatusCode != 0 && httpStatusCode == ignoreHttpCode)) {
+ // No error
+ return false;
+ }
}
- } else {
+ // FIXME: classify the error
+ done (SyncFileItem::NormalError, errorString);
+ return true;
+ case NE_ERROR: /* Generic error; use ne_get_error(session) for message */
errorString = QString::fromUtf8(ne_get_error(_propagator->_session));
+ // Check if we don't need to ignore that error.
httpStatusCode = errorString.mid(0, errorString.indexOf(QChar(' '))).toInt();
_item._httpErrorCode = httpStatusCode;
- if ((httpStatusCode >= 200 && httpStatusCode < 300)
- || (httpStatusCode != 0 && httpStatusCode == ignoreHttpCode)) {
- // No error
+ qDebug() << Q_FUNC_INFO << "NE_ERROR" << errorString << httpStatusCode << ignoreHttpCode;
+ if (ignoreHttpCode && httpStatusCode == ignoreHttpCode)
return false;
- }
- }
- // FIXME: classify the error
- done (SyncFileItem::NormalError, errorString);
- return true;
- case NE_ERROR: /* Generic error; use ne_get_error(session) for message */
- errorString = QString::fromUtf8(ne_get_error(_propagator->_session));
- // Check if we don't need to ignore that error.
- httpStatusCode = errorString.mid(0, errorString.indexOf(QChar(' '))).toInt();
- _item._httpErrorCode = httpStatusCode;
- qDebug() << Q_FUNC_INFO << "NE_ERROR" << errorString << httpStatusCode << ignoreHttpCode;
- if (ignoreHttpCode && httpStatusCode == ignoreHttpCode)
- return false;
-
- done(SyncFileItem::NormalError, errorString);
- return true;
- case NE_LOOKUP: /* Server or proxy hostname lookup failed */
- case NE_AUTH: /* User authentication failed on server */
- case NE_PROXYAUTH: /* User authentication failed on proxy */
- case NE_CONNECT: /* Could not connect to server */
- case NE_TIMEOUT: /* Connection timed out */
- done(SyncFileItem::FatalError, QString::fromUtf8(ne_get_error(_propagator->_session)));
- return true;
- case NE_FAILED: /* The precondition failed */
- case NE_RETRY: /* Retry request (ne_end_request ONLY) */
- case NE_REDIRECT: /* See ne_redirect.h */
- default:
- done(SyncFileItem::SoftError, QString::fromUtf8(ne_get_error(_propagator->_session)));
- return true;
- }
- return false;
-}
-PropagateItemJob* OwncloudPropagator::createJob(const SyncFileItem& item) {
- switch(item._instruction) {
- case CSYNC_INSTRUCTION_REMOVE:
- if (item._dir == SyncFileItem::Down) return new PropagateLocalRemove(this, item);
- else return new PropagateRemoteRemove(this, item);
- case CSYNC_INSTRUCTION_NEW:
- if (item._isDirectory) {
- if (item._dir == SyncFileItem::Down) return new PropagateLocalMkdir(this, item);
- else return new PropagateRemoteMkdir(this, item);
- } //fall trough
- case CSYNC_INSTRUCTION_SYNC:
- case CSYNC_INSTRUCTION_CONFLICT:
- if (item._isDirectory) {
- // Should we set the mtime?
- return 0;
- }
- if (item._dir != SyncFileItem::Up) return new PropagateDownloadFileQNAM(this, item);
- else return new PropagateUploadFileQNAM(this, item);
- case CSYNC_INSTRUCTION_RENAME:
- if (item._dir == SyncFileItem::Up) {
- return new PropagateRemoteRename(this, item);
- } else {
- return new PropagateLocalRename(this, item);
- }
- case CSYNC_INSTRUCTION_IGNORE:
- return new PropagateIgnoreJob(this, item);
+ done(SyncFileItem::NormalError, errorString);
+ return true;
+ case NE_LOOKUP: /* Server or proxy hostname lookup failed */
+ case NE_AUTH: /* User authentication failed on server */
+ case NE_PROXYAUTH: /* User authentication failed on proxy */
+ case NE_CONNECT: /* Could not connect to server */
+ case NE_TIMEOUT: /* Connection timed out */
+ done(SyncFileItem::FatalError, QString::fromUtf8(ne_get_error(_propagator->_session)));
+ return true;
+ case NE_FAILED: /* The precondition failed */
+ case NE_RETRY: /* Retry request (ne_end_request ONLY) */
+ case NE_REDIRECT: /* See ne_redirect.h */
default:
- return 0;
- }
- return 0;
-}
-
-void OwncloudPropagator::start(const SyncFileItemVector& _syncedItems)
-{
- /* This builds all the job needed for the propagation.
- * Each directories is a PropagateDirectory job, which contains the files in it.
- * In order to do that we sort the items by destination. and loop over it. When we enter a
- * directory, we can create the directory job and push it on the stack. */
- SyncFileItemVector items = _syncedItems;
- std::sort(items.begin(), items.end());
- _rootJob.reset(new PropagateDirectory(this));
- QStack<QPair<QString /* directory name */, PropagateDirectory* /* job */> > directories;
- directories.push(qMakePair(QString(), _rootJob.data()));
- QVector<PropagatorJob*> directoriesToRemove;
- QString removedDirectory;
- foreach(const SyncFileItem &item, items) {
- if (item._instruction == CSYNC_INSTRUCTION_REMOVE
- && !removedDirectory.isEmpty() && item._file.startsWith(removedDirectory)) {
- //already taken care of. (by the removal of the parent directory)
- continue;
- }
-
- while (!item._file.startsWith(directories.top().first)) {
- directories.pop();
- }
-
- if (item._isDirectory) {
- PropagateDirectory *dir = new PropagateDirectory(this, item);
- dir->_firstJob.reset(createJob(item));
- if (item._instruction == CSYNC_INSTRUCTION_REMOVE) {
- //We do the removal of directories at the end
- directoriesToRemove.append(dir);
- removedDirectory = item._file + "/";
- } else {
- directories.top().second->append(dir);
- }
- directories.push(qMakePair(item._file + "/" , dir));
- } else if (PropagateItemJob* current = createJob(item)) {
- directories.top().second->append(current);
- }
- }
-
- foreach(PropagatorJob* it, directoriesToRemove) {
- _rootJob->append(it);
- }
-
- connect(_rootJob.data(), SIGNAL(completed(SyncFileItem)), this, SIGNAL(completed(SyncFileItem)));
- connect(_rootJob.data(), SIGNAL(progress(Progress::Kind,SyncFileItem,quint64,quint64)), this,
- SIGNAL(progress(Progress::Kind,SyncFileItem,quint64,quint64)));
- connect(_rootJob.data(), SIGNAL(finished(SyncFileItem::Status)), this, SIGNAL(finished()));
-
- QMetaObject::invokeMethod(_rootJob.data(), "start");
-}
-
-void OwncloudPropagator::overallTransmissionSizeChanged(qint64 change)
-{
- emit progressChanged(change);
-}
-
-bool OwncloudPropagator::isInSharedDirectory(const QString& file)
-{
- bool re = false;
- if( _remoteDir.contains("remote.php/webdav/Shared") ) {
- // The Shared directory is synced as its own sync connection
- re = true;
- } else {
- if( file.startsWith("Shared/") ) {
- // The whole ownCloud is synced and Shared is always a top dir
- re = true;
- }
- }
- return re;
-}
-
-void PropagateDirectory::start()
-{
- _current = -1;
- _hasError = SyncFileItem::NoStatus;
- if (!_firstJob) {
- slotSubJobReady();
- } else {
- startJob(_firstJob.data());
- }
-}
-
-void PropagateDirectory::slotSubJobFinished(SyncFileItem::Status status)
-{
- if (status == SyncFileItem::FatalError) {
- abort();
- emit finished(status);
- return;
- } else if (status == SyncFileItem::NormalError || status == SyncFileItem::SoftError) {
- _hasError = status;
- }
- _runningNow--;
- slotSubJobReady();
-}
-
-void PropagateDirectory::slotSubJobReady()
-{
- qDebug() << Q_FUNC_INFO << _runningNow << _propagator->_activeJobs;
-
- if (_runningNow && _current == -1)
- return; // Ignore the case when the _fistJob is ready and not yet finished
- if (_runningNow && _current >= 0 && _current < _subJobs.count()) {
- // there is a job running and the current one is not ready yet, we can't start new job
- qDebug() << _subJobs[_current]->_readySent << maximumActiveJob << _subJobs[_current];
- if (!_subJobs[_current]->_readySent || _propagator->_activeJobs >= maximumActiveJob)
- return;
- }
-
- _current++;
- if (_current < _subJobs.size() && !_propagator->_abortRequested.fetchAndAddRelaxed(0)) {
- PropagatorJob *next = _subJobs.at(_current);
- startJob(next);
- return;
- }
- // We finished to processing all the jobs
- emitReady();
- if (!_runningNow) {
- if (!_item.isEmpty() && _hasError == SyncFileItem::NoStatus) {
- if( !_item._renameTarget.isEmpty() ) {
- _item._file = _item._renameTarget;
- }
-
- if (_item._should_update_etag && _item._instruction != CSYNC_INSTRUCTION_REMOVE) {
- SyncJournalFileRecord record(_item, _propagator->_localDir + _item._file);
- _propagator->_journal->setFileRecord(record);
- }
- }
- emit finished(_hasError == SyncFileItem::NoStatus ? SyncFileItem::Success : _hasError);
+ done(SyncFileItem::SoftError, QString::fromUtf8(ne_get_error(_propagator->_session)));
+ return true;
}
+ return false;
}
-
}
diff --git a/src/mirall/propagator_legacy.h b/src/mirall/propagator_legacy.h
new file mode 100644
index 0000000..813b8c5
--- /dev/null
+++ b/src/mirall/propagator_legacy.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) by Olivier Goffart <ogoffart at owncloud.com>
+ * Copyright (C) by Klaas Freitag <freitag at 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.
+ */
+
+#pragma once
+
+#include "propagatorjobs.h"
+
+namespace Mirall {
+
+class PropagateUploadFileLegacy: public PropagateNeonJob {
+ Q_OBJECT
+public:
+ explicit PropagateUploadFileLegacy(OwncloudPropagator* propagator,const SyncFileItem& item)
+ : PropagateNeonJob(propagator, item), _previousFileSize(0) {}
+ void start();
+private:
+ // Log callback for httpbf
+ static void _log_callback(const char *func, const char *text, void*)
+ {
+ qDebug() << " " << func << text;
+ }
+
+ // abort callback for httpbf
+ static int _user_want_abort(void *userData)
+ {
+ return static_cast<PropagateUploadFileLegacy *>(userData)->_propagator->_abortRequested.fetchAndAddRelaxed(0);
+ }
+
+ // callback from httpbf when a chunk is finished
+ static void chunk_finished_cb(hbf_transfer_s *trans, int chunk, void* userdata);
+ static void notify_status_cb(void* userdata, ne_session_status status,
+ const ne_session_status_info* info);
+
+ qint64 _chunked_done; // amount of bytes already sent with the previous chunks
+ qint64 _chunked_total_size; // total size of the whole file
+ qint64 _previousFileSize; // In case the file size has changed during upload, this is the previous one.
+};
+
+class PropagateDownloadFileLegacy: public PropagateNeonJob {
+ Q_OBJECT
+public:
+ explicit PropagateDownloadFileLegacy(OwncloudPropagator* propagator,const SyncFileItem& item)
+ : PropagateNeonJob(propagator, item), _file(0) {}
+ void start();
+private:
+ QFile *_file;
+ QScopedPointer<ne_decompress, ScopedPointerHelpers> _decompress;
+ QString errorString;
+ QByteArray _expectedEtagForResume;
+
+ static int do_not_accept (void *userdata, ne_request *req, const ne_status *st)
+ {
+ Q_UNUSED(userdata); Q_UNUSED(req); Q_UNUSED(st);
+ return 0; // ignore this response
+ }
+
+ static int do_not_download_content_reader(void *userdata, const char *buf, size_t len)
+ {
+ Q_UNUSED(userdata); Q_UNUSED(buf); Q_UNUSED(len);
+ return NE_ERROR;
+ }
+
+ // neon hooks:
+ static int content_reader(void *userdata, const char *buf, size_t len);
+ static void install_content_reader( ne_request *req, void *userdata, const ne_status *status );
+ static void notify_status_cb(void* userdata, ne_session_status status,
+ const ne_session_status_info* info);
+};
+
+}
diff --git a/src/mirall/owncloudpropagator_qnam.cpp b/src/mirall/propagator_qnam.cpp
similarity index 99%
rename from src/mirall/owncloudpropagator_qnam.cpp
rename to src/mirall/propagator_qnam.cpp
index 08d35b0..ee7a776 100644
--- a/src/mirall/owncloudpropagator_qnam.cpp
+++ b/src/mirall/propagator_qnam.cpp
@@ -12,7 +12,7 @@
* for more details.
*/
-#include "owncloudpropagator_qnam.h"
+#include "propagator_qnam.h"
#include "networkjobs.h"
#include "account.h"
#include "syncjournaldb.h"
diff --git a/src/mirall/owncloudpropagator_qnam.h b/src/mirall/propagator_qnam.h
similarity index 98%
rename from src/mirall/owncloudpropagator_qnam.h
rename to src/mirall/propagator_qnam.h
index 303f2aa..75e2b0b 100644
--- a/src/mirall/owncloudpropagator_qnam.h
+++ b/src/mirall/propagator_qnam.h
@@ -14,11 +14,13 @@
*/
#pragma once
-#include <QBuffer>
+#include "owncloudpropagator.h"
#include "owncloudpropagator_p.h"
#include "networkjobs.h"
+#include <QBuffer>
+#include <QFile>
namespace Mirall {
diff --git a/src/mirall/propagatorjobs.cpp b/src/mirall/propagatorjobs.cpp
new file mode 100644
index 0000000..2c5d4b3
--- /dev/null
+++ b/src/mirall/propagatorjobs.cpp
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) by Olivier Goffart <ogoffart at owncloud.com>
+ * Copyright (C) by Klaas Freitag <freitag at 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 "propagatorjobs.h"
+#include "owncloudpropagator_p.h"
+#include "propagator_legacy.h"
+
+#include "utility.h"
+#include "syncjournaldb.h"
+#include "syncjournalfilerecord.h"
+#include <httpbf.h>
+#include <qfile.h>
+#include <qdir.h>
+#include <qdiriterator.h>
+#include <qtemporaryfile.h>
+#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
+#include <qabstractfileengine.h>
+#else
+#include <qsavefile.h>
+#endif
+#include <QDebug>
+#include <QDateTime>
+#include <qstack.h>
+#include <QCoreApplication>
+
+#include <neon/ne_basic.h>
+#include <neon/ne_socket.h>
+#include <neon/ne_session.h>
+#include <neon/ne_props.h>
+#include <neon/ne_auth.h>
+#include <neon/ne_dates.h>
+#include <neon/ne_compress.h>
+#include <neon/ne_redirect.h>
+
+#ifdef Q_OS_WIN
+#include <windef.h>
+#include <winbase.h>
+#endif
+
+#include <time.h>
+
+
+namespace Mirall {
+
+/**
+ * For delete or remove, check that we are not removing from a shared directory.
+ * If we are, try to restore the file
+ *
+ * Return true if the problem is handled.
+ */
+bool PropagateNeonJob::checkForProblemsWithShared()
+{
+ QString errorString = QString::fromUtf8(ne_get_error(_propagator->_session));
+ int httpStatusCode = errorString.mid(0, errorString.indexOf(QChar(' '))).toInt();
+
+ if( httpStatusCode == 403 && _propagator->isInSharedDirectory(_item._file )) {
+ if( _item._type != SyncFileItem::Directory ) {
+ // the file was removed locally from a read only Shared sync
+ // the file is gone locally and it should be recovered.
+ SyncFileItem downloadItem(_item);
+ downloadItem._instruction = CSYNC_INSTRUCTION_SYNC;
+ downloadItem._dir = SyncFileItem::Down;
+ _restoreJob.reset(new PropagateDownloadFileLegacy(_propagator, downloadItem));
+ } else {
+ // Directories are harder to recover.
+ // But just re-create the directory, next sync will be able to recover the files
+ SyncFileItem mkdirItem(_item);
+ mkdirItem._instruction = CSYNC_INSTRUCTION_SYNC;
+ mkdirItem._dir = SyncFileItem::Down;
+ _restoreJob.reset(new PropagateLocalMkdir(_propagator, mkdirItem));
+ // Also remove the inodes and fileid from the db so no further renames are tried for
+ // this item.
+ _propagator->_journal->avoidRenamesOnNextSync(_item._file);
+ }
+ connect(_restoreJob.data(), SIGNAL(completed(SyncFileItem)),
+ this, SLOT(slotRestoreJobCompleted(SyncFileItem)));
+ QMetaObject::invokeMethod(_restoreJob.data(), "start");
+ return true;
+ }
+ return false;
+}
+
+void PropagateNeonJob::slotRestoreJobCompleted(const SyncFileItem& item )
+{
+ if( item._status == SyncFileItem::Success ) {
+ done( SyncFileItem::SoftError, tr("The file was removed from a read only share. The file has been restored."));
+ } else {
+ done( item._status, tr("A file was removed from a read only share, but restoring failed: %1").arg(item._errorString) );
+ }
+}
+
+
+// compare two files with given filename and return true if they have the same content
+bool fileEquals(const QString &fn1, const QString &fn2) {
+ QFile f1(fn1);
+ QFile f2(fn2);
+ if (!f1.open(QIODevice::ReadOnly) || !f2.open(QIODevice::ReadOnly)) {
+ qDebug() << "fileEquals: Failed to open " << fn1 << "or" << fn2;
+ return false;
+ }
+
+ if (f1.size() != f2.size()) {
+ return false;
+ }
+
+ const int BufferSize = 16 * 1024;
+ char buffer1[BufferSize];
+ char buffer2[BufferSize];
+ do {
+ int r = f1.read(buffer1, BufferSize);
+ if (f2.read(buffer2, BufferSize) != r) {
+ // this should normaly not happen: the file are supposed to have the same size.
+ return false;
+ }
+ if (r <= 0) {
+ return true;
+ }
+ if (memcmp(buffer1, buffer2, r) != 0) {
+ return false;
+ }
+ } while (true);
+ return false;
+}
+
+// Code copied from Qt5's QDir::removeRecursively
+static bool removeRecursively(const QString &path)
+{
+ bool success = true;
+ QDirIterator di(path, QDir::AllEntries | QDir::Hidden | QDir::System | QDir::NoDotAndDotDot);
+ while (di.hasNext()) {
+ di.next();
+ const QFileInfo& fi = di.fileInfo();
+ bool ok;
+ if (fi.isDir() && !fi.isSymLink())
+ ok = removeRecursively(di.filePath()); // recursive
+ else
+ ok = QFile::remove(di.filePath());
+ if (!ok)
+ success = false;
+ }
+ if (success)
+ success = QDir().rmdir(path);
+ return success;
+}
+
+void PropagateLocalRemove::start()
+{
+ if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
+ return;
+
+ QString filename = _propagator->_localDir + _item._file;
+ if (_item._isDirectory) {
+ if (QDir(filename).exists() && !removeRecursively(filename)) {
+ done(SyncFileItem::NormalError, tr("Could not remove directory %1").arg(filename));
+ return;
+ }
+ } else {
+ QFile file(filename);
+ if (file.exists() && !file.remove()) {
+ done(SyncFileItem::NormalError, file.errorString());
+ return;
+ }
+ }
+ emit progress(Progress::StartDelete, _item, 0, _item._size);
+ _propagator->_journal->deleteFileRecord(_item._originalFile, _item._isDirectory);
+ _propagator->_journal->commit("Local remove");
+ done(SyncFileItem::Success);
+ emit progress(Progress::EndDelete, _item, _item._size, _item._size);
+}
+
+void PropagateLocalMkdir::start()
+{
+ if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
+ return;
+
+ QDir d;
+ if (!d.mkpath(_propagator->_localDir + _item._file)) {
+ done(SyncFileItem::NormalError, tr("could not create directory %1").arg(_propagator->_localDir + _item._file));
+ return;
+ }
+ done(SyncFileItem::Success);
+}
+
+void PropagateRemoteRemove::start()
+{
+ if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
+ return;
+
+ QScopedPointer<char, QScopedPointerPodDeleter> uri(
+ ne_path_escape((_propagator->_remoteDir + _item._file).toUtf8()));
+ emit progress(Progress::StartDelete, _item, 0, _item._size);
+ qDebug() << "** DELETE " << uri.data();
+ int rc = ne_delete(_propagator->_session, uri.data());
+
+ if( checkForProblemsWithShared() ) {
+ return;
+ }
+
+ /* Ignore the error 404, it means it is already deleted */
+ if (updateErrorFromSession(rc, 0, 404)) {
+ return;
+ }
+
+ _propagator->_journal->deleteFileRecord(_item._originalFile, _item._isDirectory);
+ _propagator->_journal->commit("Remote Remove");
+ done(SyncFileItem::Success);
+ emit progress(Progress::EndDelete, _item, _item._size, _item._size);
+}
+
+void PropagateRemoteMkdir::start()
+{
+ if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
+ return;
+
+ QScopedPointer<char, QScopedPointerPodDeleter> uri(
+ ne_path_escape((_propagator->_remoteDir + _item._file).toUtf8()));
+
+ int rc = ne_mkcol(_propagator->_session, uri.data());
+
+ /* Special for mkcol: it returns 405 if the directory already exists.
+ * Ignore that error */
+ if( updateErrorFromSession( rc , 0, 405 ) ) {
+ return;
+ }
+ done(SyncFileItem::Success);
+}
+
+
+}
diff --git a/src/mirall/owncloudpropagator_p.h b/src/mirall/propagatorjobs.h
similarity index 57%
copy from src/mirall/owncloudpropagator_p.h
copy to src/mirall/propagatorjobs.h
index 6085f16..3be7aa4 100644
--- a/src/mirall/owncloudpropagator_p.h
+++ b/src/mirall/propagatorjobs.h
@@ -21,16 +21,8 @@
#include <QFile>
#include <qdebug.h>
-// We use some internals of csync:
-extern "C" int c_utimes(const char *, const struct timeval *);
-extern "C" void csync_win32_set_file_hidden( const char *file, bool h );
-
-
namespace Mirall {
-/** compare two files with given filename and return true if they have the same content */
-bool fileEquals(const QString &fn1, const QString &fn2);
-
/* Helper for QScopedPointer<>, to be used as the deleter.
* QScopePointer will call the right overload of cleanup for the pointer it holds
*/
@@ -41,7 +33,6 @@ struct ScopedPointerHelpers {
// static inline void cleanup(ne_propfind_handler *pointer) { if (pointer) ne_propfind_destroy(pointer); }
};
-
/*
* Abstract class for neon job. Lives in the neon thread
*/
@@ -87,8 +78,6 @@ public:
};
-
-
class PropagateLocalRemove : public PropagateItemJob {
Q_OBJECT
public:
@@ -126,76 +115,5 @@ public:
void start();
};
-class PropagateUploadFile: public PropagateNeonJob {
- Q_OBJECT
-public:
- explicit PropagateUploadFile(OwncloudPropagator* propagator,const SyncFileItem& item)
- : PropagateNeonJob(propagator, item), _previousFileSize(0) {}
- void start();
-private:
- // Log callback for httpbf
- static void _log_callback(const char *func, const char *text, void*)
- {
- qDebug() << " " << func << text;
- }
-
- // abort callback for httpbf
- static int _user_want_abort(void *userData)
- {
- return static_cast<PropagateUploadFile *>(userData)->_propagator->_abortRequested.fetchAndAddRelaxed(0);
- }
-
- // callback from httpbf when a chunk is finished
- static void chunk_finished_cb(hbf_transfer_s *trans, int chunk, void* userdata);
- static void notify_status_cb(void* userdata, ne_session_status status,
- const ne_session_status_info* info);
-
- qint64 _chunked_done; // amount of bytes already sent with the previous chunks
- qint64 _chunked_total_size; // total size of the whole file
- qint64 _previousFileSize; // In case the file size has changed during upload, this is the previous one.
-};
-
-class PropagateDownloadFile: public PropagateNeonJob {
- Q_OBJECT
-public:
- explicit PropagateDownloadFile(OwncloudPropagator* propagator,const SyncFileItem& item)
- : PropagateNeonJob(propagator, item), _file(0) {}
- void start();
-private:
- QFile *_file;
- QScopedPointer<ne_decompress, ScopedPointerHelpers> _decompress;
- QString errorString;
- QByteArray _expectedEtagForResume;
-
- static int do_not_accept (void *userdata, ne_request *req, const ne_status *st)
- {
- Q_UNUSED(userdata); Q_UNUSED(req); Q_UNUSED(st);
- return 0; // ignore this response
- }
-
- static int do_not_download_content_reader(void *userdata, const char *buf, size_t len)
- {
- Q_UNUSED(userdata); Q_UNUSED(buf); Q_UNUSED(len);
- return NE_ERROR;
- }
-
- // neon hooks:
- static int content_reader(void *userdata, const char *buf, size_t len);
- static void install_content_reader( ne_request *req, void *userdata, const ne_status *status );
- static void notify_status_cb(void* userdata, ne_session_status status,
- const ne_session_status_info* info);
-};
-
-inline QByteArray parseEtag(const char *header) {
- if (!header)
- return QByteArray();
- QByteArray arr = header;
- arr.replace("-gzip", ""); // https://github.com/owncloud/mirall/issues/1195
- if(arr.length() >= 2 && arr.startsWith('"') && arr.endsWith('"')) {
- arr = arr.mid(1, arr.length() - 2);
- }
- return arr;
-}
-
}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-owncloud/owncloud-client.git
More information about the Pkg-owncloud-commits
mailing list