[Pkg-owncloud-commits] [owncloud-client] 87/171: Propagation: Fix dir <-> file changes propagating to server #4302

Sandro Knauß hefee-guest at moszumanska.debian.org
Wed Feb 17 09:36:52 UTC 2016


This is an automated email from the git hooks/post-receive script.

hefee-guest pushed a commit to annotated tag upstream/2.1.1+dfsg
in repository owncloud-client.

commit abf5a5ad1e4eadbeb24a999aeeab39ed4357eef9
Author: Christian Kamm <mail at ckamm.de>
Date:   Wed Jan 6 10:01:22 2016 +0100

    Propagation: Fix dir <-> file changes propagating to server #4302
    
    * Ensure every time a file becomes a directory or the other way around
      the item is flagged as INSTRUCTION_TYPE_CHANGE.
    * Delete the badly-typed entity if necessary in the propagation jobs.
---
 csync/src/csync.h                    |  5 ++-
 csync/src/csync_reconcile.c          |  8 +++-
 csync/src/csync_update.c             |  5 +++
 csync/src/csync_util.c               |  1 +
 src/gui/folder.cpp                   |  1 +
 src/gui/folderstatusmodel.cpp        |  1 +
 src/gui/syncrunfilelog.cpp           |  3 ++
 src/libsync/owncloudpropagator.cpp   | 71 +++++++++++++++++++++---------------
 src/libsync/progressdispatcher.cpp   |  2 +
 src/libsync/progressdispatcher.h     |  3 +-
 src/libsync/propagatedownload.cpp    | 41 +++++++++++++++++++++
 src/libsync/propagatedownload.h      | 16 +++++++-
 src/libsync/propagateremotemkdir.cpp | 27 +++++++++++++-
 src/libsync/propagateremotemkdir.h   | 13 ++++++-
 src/libsync/propagateupload.cpp      | 39 ++++++++++++++++++--
 src/libsync/propagateupload.h        | 16 +++++++-
 src/libsync/propagatorjobs.cpp       | 35 ------------------
 src/libsync/propagatorjobs.h         | 20 ----------
 src/libsync/syncengine.cpp           | 14 ++++---
 19 files changed, 221 insertions(+), 100 deletions(-)

diff --git a/csync/src/csync.h b/csync/src/csync.h
index 06a4497..49cad89 100644
--- a/csync/src/csync.h
+++ b/csync/src/csync.h
@@ -134,7 +134,10 @@ enum csync_instructions_e {
   CSYNC_INSTRUCTION_IGNORE     = 0x00000020,  /* The file is ignored (UPDATE|RECONCILE) */
   CSYNC_INSTRUCTION_SYNC       = 0x00000040,  /* The file need to be pushed to the other remote (RECONCILE) */
   CSYNC_INSTRUCTION_STAT_ERROR = 0x00000080,
-  CSYNC_INSTRUCTION_ERROR      = 0x00000100
+  CSYNC_INSTRUCTION_ERROR      = 0x00000100,
+  CSYNC_INSTRUCTION_TYPE_CHANGE = 0x0000200,  /* Like NEW, but deletes the old entity first (RECONCILE)
+                                                 Used when the type of something changes from directory to file
+                                                 or back. */
 };
 
 enum csync_ftw_type_e {
diff --git a/csync/src/csync_reconcile.c b/csync/src/csync_reconcile.c
index 8fb295f..ab25b1c 100644
--- a/csync/src/csync_reconcile.c
+++ b/csync/src/csync_reconcile.c
@@ -283,7 +283,13 @@ static int _csync_merge_algorithm_visitor(void *obj, void *data) {
                 break;
                 /* file on the other replica has not been modified */
             case CSYNC_INSTRUCTION_NONE:
-                cur->instruction = CSYNC_INSTRUCTION_SYNC;
+                if (cur->type != other->type) {
+                    // If the type of the entity changed, it's like NEW, but
+                    // needs to delete the other entity first.
+                    cur->instruction = CSYNC_INSTRUCTION_TYPE_CHANGE;
+                } else {
+                    cur->instruction = CSYNC_INSTRUCTION_SYNC;
+                }
                 break;
             case CSYNC_INSTRUCTION_IGNORE:
                 cur->instruction = CSYNC_INSTRUCTION_IGNORE;
diff --git a/csync/src/csync_update.c b/csync/src/csync_update.c
index d166714..bd0a4c7 100644
--- a/csync/src/csync_update.c
+++ b/csync/src/csync_update.c
@@ -301,6 +301,11 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
                     goto out;
                 }
             }
+
+            // Preserve the EVAL flag later on if the type has changed.
+            if (tmp->type != fs->type)
+                st->child_modified = 1;
+
             st->instruction = CSYNC_INSTRUCTION_EVAL;
             goto out;
         }
diff --git a/csync/src/csync_util.c b/csync/src/csync_util.c
index d9e27b7..b1746b1 100644
--- a/csync/src/csync_util.c
+++ b/csync/src/csync_util.c
@@ -55,6 +55,7 @@ static const _instr_code_struct _instr[] =
   { "INSTRUCTION_SYNC", CSYNC_INSTRUCTION_SYNC },
   { "INSTRUCTION_STAT_ERR", CSYNC_INSTRUCTION_STAT_ERROR },
   { "INSTRUCTION_ERROR", CSYNC_INSTRUCTION_ERROR },
+  { "INSTRUCTION_TYPE_CHANGE", CSYNC_INSTRUCTION_TYPE_CHANGE },
   { NULL, CSYNC_INSTRUCTION_ERROR }
 };
 
diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp
index 0483021..75648c3 100644
--- a/src/gui/folder.cpp
+++ b/src/gui/folder.cpp
@@ -434,6 +434,7 @@ void Folder::bubbleUpSyncResult()
             if (!item->hasErrorStatus() && item->_direction == SyncFileItem::Down) {
                 switch (item->_instruction) {
                 case CSYNC_INSTRUCTION_NEW:
+                case CSYNC_INSTRUCTION_TYPE_CHANGE:
                     newItems++;
                     if (!firstItemNew)
                         firstItemNew = item;
diff --git a/src/gui/folderstatusmodel.cpp b/src/gui/folderstatusmodel.cpp
index fc3fc81..d45800d 100644
--- a/src/gui/folderstatusmodel.cpp
+++ b/src/gui/folderstatusmodel.cpp
@@ -980,6 +980,7 @@ void FolderStatusModel::slotFolderSyncStateChange(Folder *f)
     if (state == SyncResult::Success) {
         foreach (const SyncFileItemPtr &i, f->syncResult().syncFileItemVector()) {
             if (i->_isDirectory && (i->_instruction == CSYNC_INSTRUCTION_NEW
+                    || i->_instruction == CSYNC_INSTRUCTION_TYPE_CHANGE
                     || i->_instruction == CSYNC_INSTRUCTION_REMOVE
                     || i->_instruction == CSYNC_INSTRUCTION_RENAME)) {
                 // There is a new or a removed folder. reset all data
diff --git a/src/gui/syncrunfilelog.cpp b/src/gui/syncrunfilelog.cpp
index 607ca56..988d15d 100644
--- a/src/gui/syncrunfilelog.cpp
+++ b/src/gui/syncrunfilelog.cpp
@@ -78,6 +78,9 @@ QString SyncRunFileLog::instructionToStr( csync_instructions_e inst )
     case CSYNC_INSTRUCTION_ERROR:
         re = "INST_ERROR";
         break;
+    case CSYNC_INSTRUCTION_TYPE_CHANGE:
+        re = "INST_TYPE_CHANGE";
+        break;
     }
 
     return re;
diff --git a/src/libsync/owncloudpropagator.cpp b/src/libsync/owncloudpropagator.cpp
index 7e82e8c..db54bea 100644
--- a/src/libsync/owncloudpropagator.cpp
+++ b/src/libsync/owncloudpropagator.cpp
@@ -175,7 +175,8 @@ bool PropagateItemJob::checkForProblemsWithShared(int httpStatusCode, const QStr
     if( httpStatusCode == 403 && _propagator->isInSharedDirectory(_item->_file )) {
         if( !_item->_isDirectory ) {
             SyncFileItemPtr downloadItem(new SyncFileItem(*_item));
-            if (downloadItem->_instruction == CSYNC_INSTRUCTION_NEW) {
+            if (downloadItem->_instruction == CSYNC_INSTRUCTION_NEW
+                    || downloadItem->_instruction == CSYNC_INSTRUCTION_TYPE_CHANGE) {
                 // don't try to recover pushing new files
                 return false;
             } else if (downloadItem->_instruction == CSYNC_INSTRUCTION_SYNC) {
@@ -234,32 +235,39 @@ void PropagateItemJob::slotRestoreJobCompleted(const SyncFileItem& item )
 // ================================================================================
 
 PropagateItemJob* OwncloudPropagator::createJob(const SyncFileItemPtr &item) {
+    bool deleteExisting = item->_instruction == CSYNC_INSTRUCTION_TYPE_CHANGE;
     switch(item->_instruction) {
         case CSYNC_INSTRUCTION_REMOVE:
             if (item->_direction == SyncFileItem::Down) return new PropagateLocalRemove(this, item);
             else return new PropagateRemoteDelete(this, item);
         case CSYNC_INSTRUCTION_NEW:
+        case CSYNC_INSTRUCTION_TYPE_CHANGE:
             if (item->_isDirectory) {
-                if (item->_direction == SyncFileItem::Down) return new PropagateLocalMkdir(this, item);
-                else return new PropagateRemoteMkdir(this, item);
+                if (item->_direction == SyncFileItem::Down) {
+                    auto job = new PropagateLocalMkdir(this, item);
+                    job->setDeleteExistingFile(deleteExisting);
+                    return job;
+                } else {
+                    auto job = new PropagateRemoteMkdir(this, item);
+                    job->setDeleteExisting(deleteExisting);
+                    return job;
+                }
             }   //fall through
         case CSYNC_INSTRUCTION_SYNC:
         case CSYNC_INSTRUCTION_CONFLICT:
             if (item->_isDirectory) {
-                // Did a file turn into a directory?
-                if (QFileInfo(getFilePath(item->_file)).isFile()) {
-                    auto job = new PropagateLocalMkdir(this, item);
-                    job->setDeleteExistingFile(true);
-                    return job;
-                }
                 // Should we set the mtime?
                 return 0;
             }
             {
                 if (item->_direction != SyncFileItem::Up) {
-                    return new PropagateDownloadFileQNAM(this, item);
+                    auto job = new PropagateDownloadFileQNAM(this, item);
+                    job->setDeleteExistingFolder(deleteExisting);
+                    return job;
                 } else {
-                    return new PropagateUploadFileQNAM(this, item);
+                    auto job = new PropagateUploadFileQNAM(this, item);
+                    job->setDeleteExisting(deleteExisting);
+                    return job;
                 }
             }
         case CSYNC_INSTRUCTION_RENAME:
@@ -320,7 +328,9 @@ void OwncloudPropagator::start(const SyncFileItemVector& items)
                     delDirJob->increaseAffectedCount();
                 }
                 continue;
-            } else if (item->_instruction == CSYNC_INSTRUCTION_NEW && item->_isDirectory) {
+            } else if (item->_isDirectory
+                       && (item->_instruction == CSYNC_INSTRUCTION_NEW
+                           || item->_instruction == CSYNC_INSTRUCTION_TYPE_CHANGE)) {
                 // create a new directory within a deleted directory? That can happen if the directory
                 // etag was not fetched properly on the previous sync because the sync was aborted
                 // while uploading this directory (which is now removed).  We can ignore it.
@@ -345,6 +355,22 @@ void OwncloudPropagator::start(const SyncFileItemVector& items)
         if (item->_isDirectory) {
             PropagateDirectory *dir = new PropagateDirectory(this, item);
             dir->_firstJob.reset(createJob(item));
+
+            if (item->_instruction == CSYNC_INSTRUCTION_TYPE_CHANGE
+                    && item->_direction == SyncFileItem::Up) {
+                // Skip all potential uploads to the new folder.
+                // Processing them now leads to problems with permissions:
+                // checkForPermissions() has already run and used the permissions
+                // of the file we're about to delete to decide whether uploading
+                // to the new dir is ok...
+                foreach(const SyncFileItemPtr &item2, items) {
+                    if (item2->destination().startsWith(item->destination() + "/")) {
+                        item2->_instruction = CSYNC_INSTRUCTION_NONE;
+                        _anotherSyncNeeded = true;
+                    }
+                }
+            }
+
             if (item->_instruction == CSYNC_INSTRUCTION_REMOVE) {
                 // We do the removal of directories at the end, because there might be moves from
                 // these directories that will happen later.
@@ -364,23 +390,10 @@ void OwncloudPropagator::start(const SyncFileItemVector& items)
             }
             directories.push(qMakePair(item->destination() + "/" , dir));
         } else if (PropagateItemJob* current = createJob(item)) {
-            // If the target of a job is currently a directory, we need to remove it!
-            // This can happen when what used to be a directory changed to a file on the
-            // server an a PropagateLocalRename or PropageDownload job wants to run.
-            if (item->_direction == SyncFileItem::Down
-                    && QFileInfo(getFilePath(item->_file)).isDir()) {
-                // The DirectoryConflict job *must* run before the file propagation job
-                // and we also need to make sure other jobs that deal with the files
-                // in the directory (like removes or moves, in particular of other
-                // directories!) run first.
-                // Making it a directory job ensures that moves run first and that the
-                // (potential) directory rename happens before the file propagation.
-                // Prepending all jobs to directoriesToRemove ensures that removals of
-                // subdirectories happen before the directory is renamed.
-                PropagateDirectory *dir = new PropagateDirectory(this, item);
-                dir->_firstJob.reset(new PropagateLocalDirectoryConflict(this, item));
-                dir->append(current);
-                directoriesToRemove.prepend(dir);
+            if (item->_instruction == CSYNC_INSTRUCTION_TYPE_CHANGE) {
+                // will delete directories, so defer execution
+                directoriesToRemove.prepend(current);
+                removedDirectory = item->_file + "/";
             } else {
                 directories.top().second->append(current);
             }
diff --git a/src/libsync/progressdispatcher.cpp b/src/libsync/progressdispatcher.cpp
index 4caf818..8f03228 100644
--- a/src/libsync/progressdispatcher.cpp
+++ b/src/libsync/progressdispatcher.cpp
@@ -27,6 +27,7 @@ QString Progress::asResultString( const SyncFileItem& item)
     switch(item._instruction) {
     case CSYNC_INSTRUCTION_SYNC:
     case CSYNC_INSTRUCTION_NEW:
+    case CSYNC_INSTRUCTION_TYPE_CHANGE:
         if (item._direction != SyncFileItem::Up) {
             return QCoreApplication::translate( "progress", "Downloaded");
         } else {
@@ -59,6 +60,7 @@ QString Progress::asActionString( const SyncFileItem &item )
     case CSYNC_INSTRUCTION_CONFLICT:
     case CSYNC_INSTRUCTION_SYNC:
     case CSYNC_INSTRUCTION_NEW:
+    case CSYNC_INSTRUCTION_TYPE_CHANGE:
         if (item._direction != SyncFileItem::Up)
             return QCoreApplication::translate( "progress", "downloading");
         else
diff --git a/src/libsync/progressdispatcher.h b/src/libsync/progressdispatcher.h
index 0efcee7..a89e0fd 100644
--- a/src/libsync/progressdispatcher.h
+++ b/src/libsync/progressdispatcher.h
@@ -79,7 +79,8 @@ public:
         return ! item._isDirectory && (
                item._instruction == CSYNC_INSTRUCTION_CONFLICT
             || item._instruction == CSYNC_INSTRUCTION_SYNC
-            || item._instruction == CSYNC_INSTRUCTION_NEW);
+            || item._instruction == CSYNC_INSTRUCTION_NEW
+            || item._instruction == CSYNC_INSTRUCTION_TYPE_CHANGE);
     }
 
     /**
diff --git a/src/libsync/propagatedownload.cpp b/src/libsync/propagatedownload.cpp
index f19dce9..2b6e014 100644
--- a/src/libsync/propagatedownload.cpp
+++ b/src/libsync/propagatedownload.cpp
@@ -310,6 +310,15 @@ void PropagateDownloadFileQNAM::start()
 
     qDebug() << Q_FUNC_INFO << _item->_file << _propagator->_activeJobs;
 
+    if (_deleteExisting) {
+        deleteExistingFolder();
+
+        // check for error with deletion
+        if (_state == Finished) {
+            return;
+        }
+    }
+
     // do a klaas' case clash check.
     if( _propagator->localFileNameClash(_item->_file) ) {
         done( SyncFileItem::NormalError, tr("File %1 can not be downloaded because of a local file name clash!")
@@ -415,6 +424,11 @@ qint64 PropagateDownloadFileQNAM::committedDiskSpace() const
     return 0;
 }
 
+void PropagateDownloadFileQNAM::setDeleteExistingFolder(bool enabled)
+{
+    _deleteExisting = enabled;
+}
+
 const char owncloudCustomSoftErrorStringC[] = "owncloud-custom-soft-error-string";
 void PropagateDownloadFileQNAM::slotGetFinished()
 {
@@ -551,6 +565,33 @@ void PropagateDownloadFileQNAM::slotChecksumFail( const QString& errMsg )
     done(SyncFileItem::SoftError, errMsg ); // tr("The file downloaded with a broken checksum, will be redownloaded."));
 }
 
+void PropagateDownloadFileQNAM::deleteExistingFolder()
+{
+    QString existingDir = _propagator->getFilePath(_item->_file);
+    if (!QFileInfo(existingDir).isDir()) {
+        return;
+    }
+
+    // Delete the directory if it is empty!
+    QDir dir(existingDir);
+    if (dir.entryList(QDir::NoDotAndDotDot|QDir::AllEntries).count() == 0) {
+        if (dir.rmdir(existingDir)) {
+            return;
+        }
+        // on error, just try to move it away...
+    }
+
+    QString conflictDir = FileSystem::makeConflictFileName(
+            existingDir, Utility::qDateTimeFromTime_t(_item->_modtime));
+
+    _propagator->addTouchedFile(existingDir);
+    _propagator->addTouchedFile(conflictDir);
+    QString renameError;
+    if (!FileSystem::rename(existingDir, conflictDir, &renameError)) {
+        done(SyncFileItem::NormalError, renameError);
+    }
+}
+
 namespace { // Anonymous namespace for the recall feature
 static QString makeRecallFileName(const QString &fn)
 {
diff --git a/src/libsync/propagatedownload.h b/src/libsync/propagatedownload.h
index 32e05db..8ba11f1 100644
--- a/src/libsync/propagatedownload.h
+++ b/src/libsync/propagatedownload.h
@@ -110,10 +110,21 @@ class PropagateDownloadFileQNAM : public PropagateItemJob {
     Q_OBJECT
 public:
     PropagateDownloadFileQNAM(OwncloudPropagator* propagator,const SyncFileItemPtr& item)
-        : PropagateItemJob(propagator, item), _resumeStart(0), _downloadProgress(0) {}
+        : PropagateItemJob(propagator, item), _resumeStart(0), _downloadProgress(0), _deleteExisting(false) {}
     void start() Q_DECL_OVERRIDE;
     qint64 committedDiskSpace() const Q_DECL_OVERRIDE;
 
+    /**
+     * Whether an existing folder with the same name may be deleted before
+     * the download.
+     *
+     * If it's a non-empty folder, it'll be renamed to a conflict-style name
+     * to preserve any non-synced content that may be inside.
+     *
+     * Default: false.
+     */
+    void setDeleteExistingFolder(bool enabled);
+
 private slots:
     void slotGetFinished();
     void abort() Q_DECL_OVERRIDE;
@@ -122,10 +133,13 @@ private slots:
     void slotChecksumFail( const QString& errMsg );
 
 private:
+    void deleteExistingFolder();
+
     quint64 _resumeStart;
     qint64 _downloadProgress;
     QPointer<GETFileJob> _job;
     QFile _tmpFile;
+    bool _deleteExisting;
 };
 
 }
diff --git a/src/libsync/propagateremotemkdir.cpp b/src/libsync/propagateremotemkdir.cpp
index 8498491..0c91bb2 100644
--- a/src/libsync/propagateremotemkdir.cpp
+++ b/src/libsync/propagateremotemkdir.cpp
@@ -16,6 +16,7 @@
 #include "owncloudpropagator_p.h"
 #include "account.h"
 #include "syncjournalfilerecord.h"
+#include "propagateremotedelete.h"
 #include <QFile>
 
 namespace OCC {
@@ -27,11 +28,30 @@ void PropagateRemoteMkdir::start()
 
     qDebug() << Q_FUNC_INFO << _item->_file;
 
+    _propagator->_activeJobs++;
+
+    if (!_deleteExisting) {
+        return slotStartMkcolJob();
+    }
+
+    _job = new DeleteJob(_propagator->account(),
+                         _propagator->_remoteFolder + _item->_file,
+                         this);
+    connect(_job, SIGNAL(finishedSignal()), SLOT(slotStartMkcolJob()));
+    _job->start();
+}
+
+void PropagateRemoteMkdir::slotStartMkcolJob()
+{
+    if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
+        return;
+
+    qDebug() << Q_FUNC_INFO << _item->_file;
+
     _job = new MkColJob(_propagator->account(),
                         _propagator->_remoteFolder + _item->_file,
                         this);
     connect(_job, SIGNAL(finished(QNetworkReply::NetworkError)), this, SLOT(slotMkcolJobFinished()));
-    _propagator->_activeJobs++;
     _job->start();
 }
 
@@ -41,6 +61,11 @@ void PropagateRemoteMkdir::abort()
         _job->reply()->abort();
 }
 
+void PropagateRemoteMkdir::setDeleteExisting(bool enabled)
+{
+    _deleteExisting = enabled;
+}
+
 void PropagateRemoteMkdir::slotMkcolJobFinished()
 {
     _propagator->_activeJobs--;
diff --git a/src/libsync/propagateremotemkdir.h b/src/libsync/propagateremotemkdir.h
index 9dcbb73..4df6bb5 100644
--- a/src/libsync/propagateremotemkdir.h
+++ b/src/libsync/propagateremotemkdir.h
@@ -25,13 +25,24 @@ namespace OCC {
 class PropagateRemoteMkdir : public PropagateItemJob {
     Q_OBJECT
     QPointer<AbstractNetworkJob> _job;
+    bool _deleteExisting;
     friend class PropagateDirectory; // So it can access the _item;
 public:
     PropagateRemoteMkdir (OwncloudPropagator* propagator,const SyncFileItemPtr& item)
-        : PropagateItemJob(propagator, item) {}
+        : PropagateItemJob(propagator, item), _deleteExisting(false) {}
     void start() Q_DECL_OVERRIDE;
     void abort() Q_DECL_OVERRIDE;
+
+    /**
+     * Whether an existing entity with the same name may be deleted before
+     * creating the directory.
+     *
+     * Default: false.
+     */
+    void setDeleteExisting(bool enabled);
+
 private slots:
+    void slotStartMkcolJob();
     void slotMkcolJobFinished();
     void propfindResult(const QVariantMap &);
     void propfindError();
diff --git a/src/libsync/propagateupload.cpp b/src/libsync/propagateupload.cpp
index 2d5e40e..e468f87 100644
--- a/src/libsync/propagateupload.cpp
+++ b/src/libsync/propagateupload.cpp
@@ -24,6 +24,7 @@
 #include "propagatorjobs.h"
 #include "checksums.h"
 #include "syncengine.h"
+#include "propagateremotedelete.h"
 
 #include <json.h>
 #include <QNetworkAccessManager>
@@ -208,6 +209,29 @@ void PropagateUploadFileQNAM::start()
         }
     }
 
+    _propagator->_activeJobs++;
+
+    if (!_deleteExisting) {
+        return slotComputeContentChecksum();
+    }
+
+    auto job = new DeleteJob(_propagator->account(),
+                             _propagator->_remoteFolder + _item->_file,
+                             this);
+    _jobs.append(job);
+    connect(job, SIGNAL(finishedSignal()), SLOT(slotComputeContentChecksum()));
+    connect(job, SIGNAL(destroyed(QObject*)), SLOT(slotJobDestroyed(QObject*)));
+    job->start();
+}
+
+void PropagateUploadFileQNAM::slotComputeContentChecksum()
+{
+    if (_propagator->_abortRequested.fetchAndAddRelaxed(0)) {
+        return;
+    }
+
+    _propagator->_activeJobs--; // from start
+
     const QString filePath = _propagator->getFilePath(_item->_file);
 
     // remember the modtime before checksumming to be able to detect a file
@@ -239,6 +263,11 @@ void PropagateUploadFileQNAM::start()
     computeChecksum->start(filePath);
 }
 
+void PropagateUploadFileQNAM::setDeleteExisting(bool enabled)
+{
+    _deleteExisting = enabled;
+}
+
 void PropagateUploadFileQNAM::slotComputeTransmissionChecksum(const QByteArray& contentChecksumType, const QByteArray& contentChecksum)
 {
     _item->_contentChecksum = contentChecksum;
@@ -491,8 +520,10 @@ void PropagateUploadFileQNAM::startNextChunk()
         headers["OC-Tag"] = ".sys.admin#recall#";
     }
 
-    if (!_item->_etag.isEmpty() && _item->_etag != "empty_etag" &&
-            _item->_instruction != CSYNC_INSTRUCTION_NEW  // On new files never send a If-Match
+    if (!_item->_etag.isEmpty() && _item->_etag != "empty_etag"
+            && _item->_instruction != CSYNC_INSTRUCTION_NEW  // On new files never send a If-Match
+            && _item->_instruction != CSYNC_INSTRUCTION_TYPE_CHANGE
+            && !_deleteExisting
             ) {
         // We add quotes because the owncloud server always adds quotes around the etag, and
         //  csync_owncloud.c's owncloud_file_id always strips the quotes.
@@ -710,7 +741,9 @@ void PropagateUploadFileQNAM::slotPutFinished()
         auto currentChunk = job->_chunk;
         foreach (auto *job, _jobs) {
             // Take the minimum finished one
-            currentChunk = qMin(currentChunk, job->_chunk - 1);
+            if (auto putJob = qobject_cast<PUTFileJob*>(job)) {
+                currentChunk = qMin(currentChunk, putJob->_chunk - 1);
+            }
         }
         pi._chunk = (currentChunk + _startChunk + 1) % _chunkCount ; // next chunk to start with
         pi._transferid = _transferId;
diff --git a/src/libsync/propagateupload.h b/src/libsync/propagateupload.h
index 1b4bc74..e438357 100644
--- a/src/libsync/propagateupload.h
+++ b/src/libsync/propagateupload.h
@@ -177,7 +177,7 @@ private:
     int _chunkCount; /// Total number of chunks for this file
     int _transferId; /// transfer id (part of the url)
     QElapsedTimer _duration;
-    QVector<PUTFileJob*> _jobs; /// network jobs that are currently in transit
+    QVector<AbstractNetworkJob*> _jobs; /// network jobs that are currently in transit
     bool _finished; // Tells that all the jobs have been finished
 
     // measure the performance of checksum calc and upload
@@ -186,10 +186,21 @@ private:
     QByteArray _transmissionChecksum;
     QByteArray _transmissionChecksumType;
 
+    bool _deleteExisting;
+
 public:
     PropagateUploadFileQNAM(OwncloudPropagator* propagator,const SyncFileItemPtr& item)
-        : PropagateItemJob(propagator, item), _startChunk(0), _currentChunk(0), _chunkCount(0), _transferId(0), _finished(false) {}
+        : PropagateItemJob(propagator, item), _startChunk(0), _currentChunk(0), _chunkCount(0), _transferId(0), _finished(false), _deleteExisting(false) {}
     void start() Q_DECL_OVERRIDE;
+
+    /**
+     * Whether an existing entity with the same name may be deleted before
+     * the upload.
+     *
+     * Default: false.
+     */
+    void setDeleteExisting(bool enabled);
+
 private slots:
     void slotPutFinished();
     void slotPollFinished();
@@ -200,6 +211,7 @@ private slots:
     void slotJobDestroyed(QObject *job);
     void slotStartUpload(const QByteArray& transmissionChecksumType, const QByteArray& transmissionChecksum);
     void slotComputeTransmissionChecksum(const QByteArray& contentChecksumType, const QByteArray& contentChecksum);
+    void slotComputeContentChecksum();
 
 private:
     void startPollJob(const QString& path);
diff --git a/src/libsync/propagatorjobs.cpp b/src/libsync/propagatorjobs.cpp
index 1e431bd..a133b67 100644
--- a/src/libsync/propagatorjobs.cpp
+++ b/src/libsync/propagatorjobs.cpp
@@ -244,39 +244,4 @@ void PropagateLocalRename::start()
     done(SyncFileItem::Success);
 }
 
-void PropagateLocalDirectoryConflict::start()
-{
-    if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
-        return;
-
-    QString existingDir = _propagator->getFilePath(_item->_file);
-    if (!QFileInfo(existingDir).isDir()) {
-        done(SyncFileItem::Success);
-        return;
-    }
-
-    // Delete the directory if it is empty!
-    QDir dir(existingDir);
-    if (dir.entryList(QDir::NoDotAndDotDot|QDir::AllEntries).count() == 0) {
-        if (dir.rmdir(existingDir)) {
-            done(SyncFileItem::Success);
-            return;
-        }
-        // on error, just try to move it away...
-    }
-
-    QString conflictDir = FileSystem::makeConflictFileName(
-            existingDir, Utility::qDateTimeFromTime_t(_item->_modtime));
-
-    _propagator->addTouchedFile(existingDir);
-    _propagator->addTouchedFile(conflictDir);
-    QString renameError;
-    if (!FileSystem::rename(existingDir, conflictDir, &renameError)) {
-        done(SyncFileItem::NormalError, renameError);
-        return;
-    }
-
-    done(SyncFileItem::Success);
-}
-
 }
diff --git a/src/libsync/propagatorjobs.h b/src/libsync/propagatorjobs.h
index 5a81d30..c3898db 100644
--- a/src/libsync/propagatorjobs.h
+++ b/src/libsync/propagatorjobs.h
@@ -82,24 +82,4 @@ public:
     JobParallelism parallelism() Q_DECL_OVERRIDE { return WaitForFinishedInParentDirectory; }
 };
 
-/**
- * Moves away a local directory when it should become a file.
- *
- * Example: Locally there's directory foo/ with three files in it,
- *   one of them ignored, while the server has a file foo.
- *   In this case, foo/ fill be moved to foo-conflict.../ and the
- *   file will be downloaded to foo.
- *
- * If the directory is empty, it will be removed instead.
- *
- * @ingroup libsync
- */
-class PropagateLocalDirectoryConflict : public PropagateItemJob {
-    Q_OBJECT
-public:
-    PropagateLocalDirectoryConflict (OwncloudPropagator* propagator, const SyncFileItemPtr& item) : PropagateItemJob(propagator, item) {}
-    void start() Q_DECL_OVERRIDE;
-    JobParallelism parallelism() Q_DECL_OVERRIDE { return WaitForFinishedInParentDirectory; }
-};
-
 }
diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp
index 38aff9f..6be3e93 100644
--- a/src/libsync/syncengine.cpp
+++ b/src/libsync/syncengine.cpp
@@ -444,7 +444,7 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
         item->_errorString = tr("Filename encoding is not valid");
     }
 
-    item->_isDirectory = file->type == CSYNC_FTW_TYPE_DIR;
+    bool isDirectory = file->type == CSYNC_FTW_TYPE_DIR;
 
     if (file->etag && file->etag[0]) {
         item->_etag = file->etag;
@@ -474,7 +474,7 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
     int re = 0;
     switch(file->instruction) {
     case CSYNC_INSTRUCTION_NONE: {
-        if (remote && item->_should_update_metadata && !item->_isDirectory && item->_instruction == CSYNC_INSTRUCTION_NONE) {
+        if (remote && item->_should_update_metadata && !isDirectory && item->_instruction == CSYNC_INSTRUCTION_NONE) {
             // Update the database now already:  New remote fileid or Etag or RemotePerm
             // Or for files that were detected as "resolved conflict".
             // Or a local inode/mtime change (see localMetadataUpdate below)
@@ -509,17 +509,18 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
             _syncItemMap.remove(key);
         }
         // Any files that are instruction NONE?
-        if (!item->_isDirectory && file->other.instruction == CSYNC_INSTRUCTION_NONE) {
+        if (!isDirectory && file->other.instruction == CSYNC_INSTRUCTION_NONE) {
             _hasNoneFiles = true;
         }
         // We want to still update etags of directories, other NONE
         // items can be ignored.
-        bool directoryEtagUpdate = item->_isDirectory && file->should_update_metadata;
+        bool directoryEtagUpdate = isDirectory && file->should_update_metadata;
         bool localMetadataUpdate = !remote && file->should_update_metadata;
         if (!directoryEtagUpdate) {
             if (localMetadataUpdate) {
                 // Hack, we want a local metadata update to happen, but only if the
                 // remote tree doesn't ask us to do some kind of propagation.
+                item->_isDirectory = isDirectory;
                 _syncItemMap.insert(key, item);
             }
             return re;
@@ -529,7 +530,7 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
     case CSYNC_INSTRUCTION_RENAME:
         dir = !remote ? SyncFileItem::Down : SyncFileItem::Up;
         item->_renameTarget = renameTarget;
-        if (item->_isDirectory)
+        if (isDirectory)
             _renamedFolders.insert(item->_file, item->_renameTarget);
         break;
     case CSYNC_INSTRUCTION_REMOVE:
@@ -543,6 +544,7 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
         break;
     case CSYNC_INSTRUCTION_EVAL:
     case CSYNC_INSTRUCTION_NEW:
+    case CSYNC_INSTRUCTION_TYPE_CHANGE:
     case CSYNC_INSTRUCTION_SYNC:
     case CSYNC_INSTRUCTION_STAT_ERROR:
     default:
@@ -556,6 +558,7 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
     }
 
     item->_direction = dir;
+    item->_isDirectory = isDirectory;
     if (instruction != CSYNC_INSTRUCTION_NONE) {
         // check for blacklisting of this item.
         // if the item is on blacklist, the instruction was set to ERROR
@@ -1010,6 +1013,7 @@ void SyncEngine::checkForPermission()
         }
 
         switch((*it)->_instruction) {
+            case CSYNC_INSTRUCTION_TYPE_CHANGE:
             case CSYNC_INSTRUCTION_NEW: {
                 int slashPos = (*it)->_file.lastIndexOf('/');
                 QString parentDir = slashPos <= 0 ? "" : (*it)->_file.mid(0, slashPos);

-- 
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