Conflicts: Upload them files if env variable says so
authorChristian Kamm <mail@ckamm.de>
Mon, 18 Sep 2017 08:23:20 +0000 (10:23 +0200)
committerRoeland Jago Douma <roeland@famdouma.nl>
Thu, 5 Oct 2017 20:01:34 +0000 (22:01 +0200)
Set OWNCLOUD_UPLOAD_CONFLICT_FILES=1 to trigger this behavior.

Note that this is experimental and unsupported. The real feature is
likely to end up in 2.5.

Uploading conflict files is simply done by removing the pattern from
csync_exclude. The rest here deals with making the conflict notification
ui approximately work.

There are still some concerns about where an uploaded conflict file
appears in the sync protocol and issues list (it should be in both, but
is only in one of them currently!).

See #4557.

src/common/utility.cpp
src/common/utility.h
src/csync/csync_exclude.cpp
src/csync/csync_update.cpp
src/libsync/syncengine.cpp

index b3b7df19e5e6ef8c7fc4820924c2783b38cbf9ae..53aaf2b9dab0b003500513e5ba4613d37ee4290b 100644 (file)
@@ -545,4 +545,41 @@ QUrl Utility::concatUrlPath(const QUrl &url, const QString &concatPath,
     return tmpUrl;
 }
 
+bool Utility::isConflictFile(const char *name)
+{
+    auto bname = strrchr(name, '/');
+    if (bname) {
+        bname += 1;
+    } else {
+        bname = name;
+    }
+
+    if (strstr(bname, "_conflict-"))
+        return true;
+
+    if (shouldUploadConflictFiles()) {
+        // For uploads, we want to consider files with any kind of username tag
+        // as conflict files. (pattern *_conflict_*-)
+        auto startOfMarker = strstr(bname, "_conflict_");
+        if (startOfMarker && strchr(startOfMarker, '-'))
+            return true;
+    } else {
+        // Old behavior: optionally, files with the specific string in the env variable
+        // appended are also considered conflict files.
+        static auto conflictFileUsername = qgetenv("CSYNC_CONFLICT_FILE_USERNAME");
+        static auto usernameConflictId = QByteArray("_conflict_" + conflictFileUsername + "-");
+        if (!conflictFileUsername.isEmpty() && strstr(bname, usernameConflictId.constData())) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+bool Utility::shouldUploadConflictFiles()
+{
+    static bool uploadConflictFiles = qgetenv("OWNCLOUD_UPLOAD_CONFLICT_FILES").toInt() != 0;
+    return uploadConflictFiles;
+}
+
 } // namespace OCC
index d32a7bb3f6fce8da8212892acde9b21cd522bdc4..9fc0a72d73ce14ff1a167606b136fde6873b5527 100644 (file)
@@ -175,6 +175,18 @@ namespace Utility {
     /**  Returns a new settings pre-set in a specific group.  The Settings will be created
          with the given parent. If no parent is specified, the caller must destroy the settings */
     OCSYNC_EXPORT std::unique_ptr<QSettings> settingsWithGroup(const QString &group, QObject *parent = 0);
+
+    /** Returns whether a file name indicates a conflict file
+     *
+     * See FileSystem::makeConflictFileName.
+     */
+    OCSYNC_EXPORT bool isConflictFile(const char *name);
+
+    /** Returns whether conflict files should be uploaded.
+     *
+     * Experimental! Real feature planned for 2.5.
+     */
+    OCSYNC_EXPORT bool shouldUploadConflictFiles();
 }
 /** @} */ // \addtogroup
 
index 7a177b840574c47980e754f95f4879dc4022e1df..dbc54f2bb71e2219874d7c3b080830d2e12d06d7 100644 (file)
@@ -37,6 +37,8 @@
 #include "csync_exclude.h"
 #include "csync_misc.h"
 
+#include "common/utility.h"
+
 #ifdef _WIN32
 #include <io.h>
 #else
@@ -218,7 +220,6 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
     size_t i = 0;
     const char *bname = NULL;
     size_t blen = 0;
-    char *conflict = NULL;
     int rc = -1;
     CSYNC_EXCLUDE_TYPE match = CSYNC_NOT_EXCLUDED;
     CSYNC_EXCLUDE_TYPE type  = CSYNC_NOT_EXCLUDED;
@@ -308,25 +309,11 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
         goto out;
     }
 
-    /* Always ignore conflict files, not only via the exclude list */
-    rc = csync_fnmatch("*_conflict-*", bname, 0);
-    if (rc == 0) {
-        match = CSYNC_FILE_EXCLUDE_CONFLICT;
-        goto out;
-    }
-
-    if (getenv("CSYNC_CONFLICT_FILE_USERNAME")) {
-        rc = asprintf(&conflict, "*_conflict_%s-*", getenv("CSYNC_CONFLICT_FILE_USERNAME"));
-        if (rc < 0) {
-            goto out;
-        }
-        rc = csync_fnmatch(conflict, path, 0);
-        if (rc == 0) {
+    if (!OCC::Utility::shouldUploadConflictFiles()) {
+        if (OCC::Utility::isConflictFile(bname)) {
             match = CSYNC_FILE_EXCLUDE_CONFLICT;
-            SAFE_FREE(conflict);
             goto out;
         }
-        SAFE_FREE(conflict);
     }
 
     if( ! excludes ) {
index e90c9dd19d343d012953861d8e6237a3d0b2f427..71be03be89974eef4f6609f8381e7facd618774c 100644 (file)
@@ -46,6 +46,8 @@
 #include "csync_log.h"
 #include "csync_rename.h"
 
+#include "common/utility.h"
+
 // Needed for PRIu64 on MinGW in C++ mode.
 #define __STDC_FORMAT_MACROS
 #include <inttypes.h>
@@ -371,6 +373,15 @@ out:
       && fs->type != CSYNC_FTW_TYPE_DIR) {
     fs->child_modified = true;
   }
+
+  // If conflict files are uploaded, they won't be marked as IGNORE / CSYNC_FILE_EXCLUDE_CONFLICT
+  // but we still want them marked!
+  if (OCC::Utility::shouldUploadConflictFiles()) {
+      if (OCC::Utility::isConflictFile(fs->path.constData())) {
+          fs->error_status = CSYNC_STATUS_INDIVIDUAL_IS_CONFLICT_FILE;
+      }
+  }
+
   ctx->current_fs = fs.get();
 
   CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO, "file: %s, instruction: %s <<=", fs->path.constData(),
index 4b4836ad3cbe7be3942f8362faecb658b2c1f3c2..b133df1158d18ab98d3a9f40a2431d73d2e5272b 100644 (file)
@@ -479,7 +479,17 @@ int SyncEngine::treewalkFile(csync_file_stat_t *file, csync_file_stat_t *other,
         break;
     case CSYNC_STATUS_INDIVIDUAL_IS_CONFLICT_FILE:
         item->_status = SyncFileItem::Conflict;
-        item->_errorString = tr("Conflict: Server version downloaded, local copy renamed and not uploaded.");
+        if (Utility::shouldUploadConflictFiles()) {
+            // For uploaded conflict files, files with no action performed on them should
+            // be displayed: but we mustn't overwrite the instruction if something happens
+            // to the file!
+            if (remote && item->_instruction == CSYNC_INSTRUCTION_NONE) {
+                item->_errorString = tr("Unresolved conflict.");
+                item->_instruction = CSYNC_INSTRUCTION_IGNORE;
+            }
+        } else {
+            item->_errorString = tr("Conflict: Server version downloaded, local copy renamed and not uploaded.");
+        }
         break;
     case CYSNC_STATUS_FILE_LOCKED_OR_OPEN:
         item->_errorString = QLatin1String("File locked"); // don't translate, internal use!
@@ -549,6 +559,12 @@ int SyncEngine::treewalkFile(csync_file_stat_t *file, csync_file_stat_t *other,
         if (!isDirectory && (!other || other->instruction == CSYNC_INSTRUCTION_NONE)) {
             _hasNoneFiles = true;
         }
+        // Put none-instruction conflict files into the syncfileitem list
+        if (Utility::shouldUploadConflictFiles()
+            && file->error_status == CSYNC_STATUS_INDIVIDUAL_IS_CONFLICT_FILE
+            && item->_instruction == CSYNC_INSTRUCTION_IGNORE) {
+            break;
+        }
         // No syncing or update to be done.
         return re;
     }