RemotePermissions: Store in a class rather than in a QByteArray to save memory
authorOlivier Goffart <ogoffart@woboq.com>
Tue, 19 Sep 2017 08:53:51 +0000 (10:53 +0200)
committerRoeland Jago Douma <roeland@famdouma.nl>
Thu, 5 Oct 2017 20:01:35 +0000 (22:01 +0200)
Create a specific type that parses the permissions so we can store
it in a short rather than in a QByteArray

Note: in RemotePermissions::toString, we make sure the string is not
empty by adding a space, this was already existing before commit
e8f7adc7cacd4f55e26b2dd14464654e82204307 where it was removed by mistake.

18 files changed:
src/common/common.cmake
src/common/remotepermissions.cpp [new file with mode: 0644]
src/common/remotepermissions.h [new file with mode: 0644]
src/common/syncjournaldb.cpp
src/common/syncjournalfilerecord.h
src/csync/csync.h
src/csync/csync_private.h
src/csync/csync_statedb.cpp
src/csync/csync_update.cpp
src/gui/owncloudgui.cpp
src/libsync/discoveryphase.cpp
src/libsync/discoveryphase.h
src/libsync/propagatedownload.cpp
src/libsync/syncengine.cpp
src/libsync/syncengine.h
src/libsync/syncfileitem.h
src/libsync/syncfilestatustracker.cpp
test/testsyncjournaldb.cpp

index e448868c60b3c2423029843354b159d3add8e045..9d7898e8a4ed61439a40bcf6f1ed1a0e3462a105 100644 (file)
@@ -8,4 +8,5 @@ set(common_SOURCES
     ${CMAKE_CURRENT_LIST_DIR}/syncjournaldb.cpp
     ${CMAKE_CURRENT_LIST_DIR}/syncjournalfilerecord.cpp
     ${CMAKE_CURRENT_LIST_DIR}/utility.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/remotepermissions.cpp
 )
diff --git a/src/common/remotepermissions.cpp b/src/common/remotepermissions.cpp
new file mode 100644 (file)
index 0000000..ce39460
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) by Olivier Goffart <ogoffart@woboq.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "remotepermissions.h"
+#include <cstring>
+
+namespace OCC {
+
+static const char letters[] = " WDNVCKRSMm";
+
+
+template <typename Char>
+void RemotePermissions::fromArray(const Char *p)
+{
+    _value = p ? notNullMask : 0;
+    if (!p)
+        return;
+    while (*p) {
+        if (auto res = std::strchr(letters, static_cast<char>(*p)))
+            _value |= (1 << (res - letters));
+        ++p;
+    }
+}
+
+RemotePermissions::RemotePermissions(const char *p)
+{
+    fromArray(p);
+}
+
+RemotePermissions::RemotePermissions(const QString &s)
+{
+    fromArray(s.isEmpty() ? nullptr : s.utf16());
+}
+
+QByteArray RemotePermissions::toString() const
+{
+    QByteArray result;
+    if (isNull())
+        return result;
+    result.reserve(PermissionsCount);
+    for (uint i = 1; i <= PermissionsCount; ++i) {
+        if (_value & (1 << i))
+            result.append(letters[i]);
+    }
+    if (result.isEmpty()) {
+        // Make sure it is not empty so we can differentiate null and empty permissions
+        result.append(' ');
+    }
+    return result;
+}
+
+} // namespace OCC
diff --git a/src/common/remotepermissions.h b/src/common/remotepermissions.h
new file mode 100644 (file)
index 0000000..2b34dcb
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) by Olivier Goffart <ogoffart@woboq.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#pragma once
+
+#include <QString>
+#include <QMetaType>
+#include "ocsynclib.h"
+
+namespace OCC {
+
+/**
+ * Class that store in a memory efficient way the remote permission
+ */
+class OCSYNC_EXPORT RemotePermissions
+{
+private:
+    // The first bit tells if the value is set or not
+    // The remaining bits correspond to know if the value is set
+    quint16 _value = 0;
+    static constexpr int notNullMask = 0x1;
+
+    template <typename Char> // can be 'char' or 'ushort' if conversion from QString
+    void fromArray(const Char *p);
+
+public:
+    enum Permissions {
+        CanWrite = 1,             // W
+        CanDelete = 2,            // D
+        CanRename = 3,            // N
+        CanMove = 4,              // V
+        CanAddFile = 5,           // C
+        CanAddSubDirectories = 6, // K
+        CanReshare = 7,           // R
+        // Note: on the server, this means SharedWithMe, but in discoveryphase.cpp we also set
+        // this permission when the server reports the any "share-types"
+        IsShared = 8,             // S
+        IsMounted = 9,            // M
+        IsMountedSub = 10,        // m (internal: set if the parent dir has IsMounted)
+
+        // Note: when adding support for more permissions, we need to invalid the cache in the database.
+        // (by setting forceRemoteDiscovery in SyncJournalDb::checkConnect)
+        PermissionsCount = IsMountedSub
+    };
+    RemotePermissions() = default;
+    explicit RemotePermissions(const char *);
+    explicit RemotePermissions(const QString &);
+
+    QByteArray toString() const;
+    bool hasPermission(Permissions p) const
+    {
+        return _value & (1 << static_cast<int>(p));
+    }
+    void setPermission(Permissions p)
+    {
+        _value |= (1 << static_cast<int>(p)) | notNullMask;
+    }
+    void unsetPermission(Permissions p)
+    {
+        _value &= ~(1 << static_cast<int>(p));
+    }
+
+    bool isNull() const { return !(_value & notNullMask); }
+    friend bool operator==(RemotePermissions a, RemotePermissions b)
+    {
+        return a._value == b._value;
+    }
+    friend bool operator!=(RemotePermissions a, RemotePermissions b)
+    {
+        return !(a == b);
+    }
+};
+
+
+} // namespace OCC
+
+Q_DECLARE_METATYPE(OCC::RemotePermissions)
index 65254fd72ce90c0d7c4f8971caac783cb2767c5c..9e5052b5a95defbc04e84166008126b5c36c1e92 100644 (file)
@@ -894,7 +894,7 @@ bool SyncJournalDb::setFileRecord(const SyncJournalFileRecord &_record)
 
     qCInfo(lcDb) << "Updating file record for path:" << record._path << "inode:" << record._inode
                  << "modtime:" << record._modtime << "type:" << record._type
-                 << "etag:" << record._etag << "fileId:" << record._fileId << "remotePerm:" << record._remotePerm
+                 << "etag:" << record._etag << "fileId:" << record._fileId << "remotePerm:" << record._remotePerm.toString()
                  << "fileSize:" << record._fileSize << "checksum:" << record._checksumHeader;
 
     qlonglong phash = getPHash(record._path);
@@ -908,9 +908,7 @@ bool SyncJournalDb::setFileRecord(const SyncJournalFileRecord &_record)
         QString fileId(record._fileId);
         if (fileId.isEmpty())
             fileId = "";
-        QString remotePerm(record._remotePerm);
-        if (remotePerm.isEmpty())
-            remotePerm = QString(); // have NULL in DB (vs empty)
+        QByteArray remotePerm = record._remotePerm.toString();
         QByteArray checksumType, checksum;
         parseChecksumHeader(record._checksumHeader, &checksumType, &checksum);
         int contentChecksumTypeId = mapChecksumType(checksumType);
@@ -1001,7 +999,7 @@ SyncJournalFileRecord SyncJournalDb::getFileRecord(const QString &filename)
             rec._type = _getFileRecordQuery->intValue(6);
             rec._etag = _getFileRecordQuery->baValue(7);
             rec._fileId = _getFileRecordQuery->baValue(8);
-            rec._remotePerm = _getFileRecordQuery->baValue(9);
+            rec._remotePerm = RemotePermissions(_getFileRecordQuery->baValue(9).constData());
             rec._fileSize = _getFileRecordQuery->int64Value(10);
             rec._serverHasIgnoredFiles = (_getFileRecordQuery->intValue(11) > 0);
             rec._checksumHeader = _getFileRecordQuery->baValue(12);
index ba1c7ced0097329ada758d9b7206f8c8cfd82ff7..c6008713e3cafa94013e2d790a33fd9700038cd0 100644 (file)
@@ -23,6 +23,7 @@
 #include <QDateTime>
 
 #include "ocsynclib.h"
+#include "remotepermissions.h"
 
 namespace OCC {
 
@@ -57,7 +58,7 @@ public:
     QByteArray _etag;
     QByteArray _fileId;
     qint64 _fileSize;
-    QByteArray _remotePerm;
+    RemotePermissions _remotePerm;
     bool _serverHasIgnoredFiles;
     QByteArray _checksumHeader;
 };
index 9eaba2ab4d1d080ea1c1df83e34cdeb11a0116d5..0e7cf12c56dcabc0a7bc36a5f2e646e7741a793d 100644 (file)
@@ -41,6 +41,7 @@
 #include <config_csync.h>
 #include <memory>
 #include <QByteArray>
+#include "common/remotepermissions.h"
 
 #if defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && !defined(Q_CC_CLANG) && (__GNUC__ * 100 + __GNUC_MINOR__ < 408)
 // openSuse 12.3 didn't like enum bitfields.
@@ -163,6 +164,8 @@ struct OCSYNC_EXPORT csync_file_stat_s {
   time_t modtime;
   int64_t size;
   uint64_t inode;
+
+  OCC::RemotePermissions remotePerm;
   enum csync_ftw_type_e type BITFIELD(4);
   bool child_modified BITFIELD(1);
   bool has_ignored_files BITFIELD(1); // Specify that a directory, or child directory contains ignored files.
@@ -174,7 +177,6 @@ struct OCSYNC_EXPORT csync_file_stat_s {
   QByteArray file_id;
   QByteArray directDownloadUrl;
   QByteArray directDownloadCookies;
-  QByteArray remotePerm;
   QByteArray original_path; // only set if locale conversion fails
 
   // In the local tree, this can hold a checksum and its type if it is
index 53383db89c67c98d5c401daa42c45543aaf9182e..9167a53a4e14777040c389f8f632dca511350b12 100644 (file)
@@ -86,7 +86,7 @@ struct OCSYNC_EXPORT csync_s {
 
       /* hooks for checking the white list (uses the update_callback_userdata) */
       int (*checkSelectiveSyncBlackListHook)(void*, const QByteArray &) = nullptr;
-      int (*checkSelectiveSyncNewFolderHook)(void*, const QByteArray &/* path */, const QByteArray &/* remotePerm */) = nullptr;
+      int (*checkSelectiveSyncNewFolderHook)(void *, const QByteArray & /* path */, OCC::RemotePermissions) = nullptr;
 
 
       csync_vio_opendir_hook remote_opendir_hook = nullptr;
@@ -126,7 +126,7 @@ struct OCSYNC_EXPORT csync_s {
   struct {
     FileMap files;
     bool read_from_db = false;
-    QByteArray root_perms; /* Permission of the root folder. (Since the root folder is not in the db tree, we need to keep a separate entry.) */
+    OCC::RemotePermissions root_perms; /* Permission of the root folder. (Since the root folder is not in the db tree, we need to keep a separate entry.) */
   } remote;
 
   /* replica we are currently walking */
index 89ac83fd76d1fc429668014f9388d053a263ad2c..b489b6789c42aa218b8e7d5048c71cfbdcd624d1 100644 (file)
@@ -264,7 +264,10 @@ static int _csync_file_stat_from_metadata_table( std::unique_ptr<csync_file_stat
         st->type = static_cast<enum csync_ftw_type_e>(sqlite3_column_int(stmt, 3));
         st->etag = (char*)sqlite3_column_text(stmt, 4);
         st->file_id = (char*)sqlite3_column_text(stmt, 5);
-        st->remotePerm = (char*)sqlite3_column_text(stmt, 6);
+        const char *permStr = (char *)sqlite3_column_text(stmt, 6);
+        // If permStr is empty, construct a null RemotePermissions.  We make sure that non-null
+        // permissions are never empty in RemotePermissions.toString()
+        st->remotePerm = permStr && *permStr ? OCC::RemotePermissions(permStr) : OCC::RemotePermissions();
         st->size = sqlite3_column_int64(stmt, 7);
         st->has_ignored_files = sqlite3_column_int(stmt, 8);
         st->checksumHeader = (char *)sqlite3_column_text(stmt, 9);
index 71be03be89974eef4f6609f8381e7facd618774c..28bf5cd26334405e6e00c856de5906bc4c637f30 100644 (file)
@@ -183,10 +183,10 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
       /* we have an update! */
       CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO, "Database entry found, compare: %" PRId64 " <-> %" PRId64
                                           ", etag: %s <-> %s, inode: %" PRId64 " <-> %" PRId64
-                                          ", size: %" PRId64 " <-> %" PRId64 ", perms: %s <-> %s, ignore: %d",
+                                          ", size: %" PRId64 " <-> %" PRId64 ", perms: %x <-> %x, ignore: %d",
                 ((int64_t) fs->modtime), ((int64_t) tmp->modtime),
                 fs->etag.constData(), tmp->etag.constData(), (uint64_t) fs->inode, (uint64_t) tmp->inode,
-                (uint64_t) fs->size, (uint64_t) tmp->size, fs->remotePerm.constData(), tmp->remotePerm.constData(), tmp->has_ignored_files );
+                (uint64_t) fs->size, (uint64_t) tmp->size, *reinterpret_cast<short*>(&fs->remotePerm), *reinterpret_cast<short*>(&tmp->remotePerm), tmp->has_ignored_files );
       if (ctx->current == REMOTE_REPLICA && fs->etag != tmp->etag) {
           fs->instruction = CSYNC_INSTRUCTION_EVAL;
 
index 0d2fcffd189253e55cc50176720979dec4e59958..6c57442ea547ba10fb0bf127d3dd0ce223a69d7a 100644 (file)
@@ -1007,7 +1007,7 @@ void ownCloudGui::slotShowShareDialog(const QString &sharePath, const QString &l
     bool resharingAllowed = true; // lets assume the good
     if (fileRecord.isValid()) {
         // check the permission: Is resharing allowed?
-        if (!fileRecord._remotePerm.contains('R')) {
+        if (!fileRecord._remotePerm.isNull() && !fileRecord._remotePerm.hasPermission(RemotePermissions::CanReshare)) {
             resharingAllowed = false;
         }
     }
index 23ac664c6a521bf7e48dee574b4b646100be5033..e5283f1310516d7883dd25b7b19e5746dde27bc1 100644 (file)
@@ -87,10 +87,11 @@ int DiscoveryJob::isInSelectiveSyncBlackListCallback(void *data, const QByteArra
     return static_cast<DiscoveryJob *>(data)->isInSelectiveSyncBlackList(path);
 }
 
-bool DiscoveryJob::checkSelectiveSyncNewFolder(const QString &path, const QByteArray &remotePerm)
+bool DiscoveryJob::checkSelectiveSyncNewFolder(const QString &path, RemotePermissions remotePerm)
 {
-    if (_syncOptions._confirmExternalStorage && remotePerm.contains('M')) {
-        // 'M' in the permission means external storage.
+    if (_syncOptions._confirmExternalStorage
+        && remotePerm.hasPermission(RemotePermissions::IsMounted)) {
+        // external storage.
 
         /* Note: DiscoverySingleDirectoryJob::directoryListingIteratedSlot make sure that only the
          * root of a mounted storage has 'M', all sub entries have 'm' */
@@ -144,7 +145,7 @@ bool DiscoveryJob::checkSelectiveSyncNewFolder(const QString &path, const QByteA
     }
 }
 
-int DiscoveryJob::checkSelectiveSyncNewFolderCallback(void *data, const QByteArray &path, const QByteArray &remotePerm)
+int DiscoveryJob::checkSelectiveSyncNewFolderCallback(void *data, const QByteArray &path, RemotePermissions remotePerm)
 {
     return static_cast<DiscoveryJob *>(data)->checkSelectiveSyncNewFolder(QString::fromUtf8(path), remotePerm);
 }
@@ -350,20 +351,19 @@ static std::unique_ptr<csync_file_stat_t> propertyMapToFileStat(const QMap<QStri
         } else if (property == "dDC") {
             file_stat->directDownloadCookies = value.toUtf8();
         } else if (property == "permissions") {
-            file_stat->remotePerm = value.toUtf8();
+            file_stat->remotePerm = RemotePermissions(value);
         } else if (property == "checksums") {
             file_stat->checksumHeader = findBestChecksum(value.toUtf8());
         } else if (property == "share-types" && !value.isEmpty()) {
-            // Since QMap is sorted, "share-types" is always "permissions".
-            if (file_stat->remotePerm.isEmpty()) {
+            // Since QMap is sorted, "share-types" is always after "permissions".
+            if (file_stat->remotePerm.isNull()) {
                 qWarning() << "Server returned a share type, but no permissions?";
             } else {
                 // S means shared with me.
                 // But for our purpose, we want to know if the file is shared. It does not matter
                 // if we are the owner or not.
-                // Piggy back on the persmission field 'S'
-                if (!file_stat->remotePerm.contains('S'))
-                    file_stat->remotePerm.append('S');
+                // Piggy back on the persmission field
+                file_stat->remotePerm.setPermission(RemotePermissions::IsShared);
             }
         }
     }
@@ -376,9 +376,9 @@ void DiscoverySingleDirectoryJob::directoryListingIteratedSlot(QString file, con
         // The first entry is for the folder itself, we should process it differently.
         _ignoredFirst = true;
         if (map.contains("permissions")) {
-            auto perm = map.value("permissions");
+            RemotePermissions perm(map.value("permissions"));
             emit firstDirectoryPermissions(perm);
-            _isExternalStorage = perm.contains(QLatin1Char('M'));
+            _isExternalStorage = perm.hasPermission(RemotePermissions::IsMounted);
         }
         if (map.contains("data-fingerprint")) {
             _dataFingerprint = map.value("data-fingerprint").toUtf8();
@@ -401,11 +401,12 @@ void DiscoverySingleDirectoryJob::directoryListingIteratedSlot(QString file, con
         if (file_stat->etag.isEmpty()) {
             qCCritical(lcDiscovery) << "etag of" << file_stat->path << "is" << file_stat->etag << "This must not happen.";
         }
-        if (_isExternalStorage) {
+        if (_isExternalStorage && file_stat->remotePerm.hasPermission(RemotePermissions::IsMounted)) {
             /* All the entries in a external storage have 'M' in their permission. However, for all
                purposes in the desktop client, we only need to know about the mount points.
                So replace the 'M' by a 'm' for every sub entries in an external storage */
-            file_stat->remotePerm.replace('M', 'm');
+            file_stat->remotePerm.unsetPermission(RemotePermissions::IsMounted);
+            file_stat->remotePerm.setPermission(RemotePermissions::IsMountedSub);
         }
 
         QStringRef fileRef(&file);
@@ -557,12 +558,12 @@ void DiscoveryMainThread::singleDirectoryJobFinishedWithErrorSlot(int csyncErrno
     _discoveryJob->_vioMutex.unlock();
 }
 
-void DiscoveryMainThread::singleDirectoryJobFirstDirectoryPermissionsSlot(const QString &p)
+void DiscoveryMainThread::singleDirectoryJobFirstDirectoryPermissionsSlot(RemotePermissions p)
 {
     // Should be thread safe since the sync thread is blocked
-    if (_discoveryJob->_csync_ctx->remote.root_perms.isEmpty()) {
-        qCDebug(lcDiscovery) << "Permissions for root dir:" << p;
-        _discoveryJob->_csync_ctx->remote.root_perms = p.toUtf8();
+    if (_discoveryJob->_csync_ctx->remote.root_perms.isNull()) {
+        qCDebug(lcDiscovery) << "Permissions for root dir:" << p.toString();
+        _discoveryJob->_csync_ctx->remote.root_perms = p;
     }
 }
 
index c3d44405b1ef7e38ae9e238658e656d99c2d9e5a..eea33ad52c9832c52b43f23d389860899a5fb487 100644 (file)
@@ -113,7 +113,7 @@ public:
 
     // This is not actually a network job, it is just a job
 signals:
-    void firstDirectoryPermissions(const QString &);
+    void firstDirectoryPermissions(RemotePermissions);
     void etagConcatenation(const QString &);
     void etag(const QString &);
     void finishedWithResult();
@@ -178,7 +178,7 @@ public slots:
     // From Job:
     void singleDirectoryJobResultSlot();
     void singleDirectoryJobFinishedWithErrorSlot(int csyncErrnoCode, const QString &msg);
-    void singleDirectoryJobFirstDirectoryPermissionsSlot(const QString &);
+    void singleDirectoryJobFirstDirectoryPermissionsSlot(RemotePermissions);
 
     void slotGetSizeFinishedWithError();
     void slotGetSizeResult(const QVariantMap &);
@@ -212,8 +212,8 @@ class DiscoveryJob : public QObject
      */
     bool isInSelectiveSyncBlackList(const QByteArray &path) const;
     static int isInSelectiveSyncBlackListCallback(void *, const QByteArray &);
-    bool checkSelectiveSyncNewFolder(const QString &path, const QByteArray &remotePerm);
-    static int checkSelectiveSyncNewFolderCallback(void *data, const QByteArray &path, const QByteArray &remotePerm);
+    bool checkSelectiveSyncNewFolder(const QString &path, RemotePermissions rp);
+    static int checkSelectiveSyncNewFolderCallback(void *data, const QByteArray &path, RemotePermissions rm);
 
     // Just for progress
     static void update_job_update_callback(bool local,
index 812c01316c0fe295489a0007b66918809f3ea16a..4bb4495c905a6ff394093f3020180a704f2d5c74 100644 (file)
@@ -823,13 +823,7 @@ void PropagateDownloadFile::downloadFinished()
     }
 
     // Apply the remote permissions
-    // Older server versions sometimes provide empty remote permissions
-    // see #4450 - don't adjust the write permissions there.
-    const int serverVersionGoodRemotePerm = Account::makeServerVersion(7, 0, 0);
-    if (propagator()->account()->serverVersionInt() >= serverVersionGoodRemotePerm) {
-        FileSystem::setFileReadOnlyWeak(_tmpFile.fileName(),
-            !_item->_remotePerm.contains('W'));
-    }
+    FileSystem::setFileReadOnlyWeak(_tmpFile.fileName(), !_item->_remotePerm.isNull() && !_item->_remotePerm.hasPermission(RemotePermissions::CanWrite));
 
     QString error;
     emit propagator()->touchedFile(fn);
@@ -882,7 +876,7 @@ void PropagateDownloadFile::updateMetadata(bool isConflict)
     done(isConflict ? SyncFileItem::Conflict : SyncFileItem::Success);
 
     // handle the special recall file
-    if (!_item->_remotePerm.contains("S")
+    if (!_item->_remotePerm.hasPermission(RemotePermissions::IsShared)
         && (_item->_file == QLatin1String(".sys.admin#recall#")
                || _item->_file.endsWith("/.sys.admin#recall#"))) {
         handleRecallFile(fn, propagator()->_localDir, *propagator()->_journal);
index b4b10d4052f14d60cc9d81e214b9f909e2e7a6ed..22a8068b06ad585ed553a8ad09dce851d3d71137 100644 (file)
@@ -411,7 +411,7 @@ int SyncEngine::treewalkFile(csync_file_stat_t *file, csync_file_stat_t *other,
     if (!file->directDownloadCookies.isEmpty()) {
         item->_directDownloadCookies = QString::fromUtf8(file->directDownloadCookies);
     }
-    if (!file->remotePerm.isEmpty()) {
+    if (!file->remotePerm.isNull()) {
         item->_remotePerm = file->remotePerm;
     }
 
@@ -601,8 +601,8 @@ int SyncEngine::treewalkFile(csync_file_stat_t *file, csync_file_stat_t *other,
 
                 // If the 'W' remote permission changed, update the local filesystem
                 SyncJournalFileRecord prev = _journal->getFileRecord(item->_file);
-                if (prev.isValid() && prev._remotePerm.contains('W') != item->_remotePerm.contains('W')) {
-                    const bool isReadOnly = !item->_remotePerm.contains('W');
+                if (prev.isValid() && prev._remotePerm.hasPermission(RemotePermissions::CanWrite) != item->_remotePerm.hasPermission(RemotePermissions::CanWrite)) {
+                    const bool isReadOnly = !item->_remotePerm.isNull() && !item->_remotePerm.hasPermission(RemotePermissions::CanWrite);
                     FileSystem::setFileReadOnlyWeak(filePath, isReadOnly);
                 }
 
@@ -946,7 +946,7 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
         qCWarning(lcEngine) << "Error in remote treewalk.";
     }
 
-    qCInfo(lcEngine) << "Permissions of the root folder: " << _csync_ctx->remote.root_perms;
+    qCInfo(lcEngine) << "Permissions of the root folder: " << _csync_ctx->remote.root_perms.toString();
 
     // The map was used for merging trees, convert it to a list:
     SyncFileItemVector syncItems = _syncItemMap.values().toVector();
@@ -1251,11 +1251,11 @@ void SyncEngine::checkForPermission(SyncFileItemVector &syncItems)
         case CSYNC_INSTRUCTION_NEW: {
             int slashPos = (*it)->_file.lastIndexOf('/');
             QString parentDir = slashPos <= 0 ? "" : (*it)->_file.mid(0, slashPos);
-            const QByteArray perms = getPermissions(parentDir);
+            const auto perms = getPermissions(parentDir);
             if (perms.isNull()) {
                 // No permissions set
                 break;
-            } else if ((*it)->isDirectory() && !perms.contains("K")) {
+            } else if ((*it)->isDirectory() && !perms.hasPermission(RemotePermissions::CanAddSubDirectories)) {
                 qCWarning(lcEngine) << "checkForPermission: ERROR" << (*it)->_file;
                 (*it)->_instruction = CSYNC_INSTRUCTION_ERROR;
                 (*it)->_status = SyncFileItem::NormalError;
@@ -1277,7 +1277,7 @@ void SyncEngine::checkForPermission(SyncFileItemVector &syncItems)
                     (*it)->_errorString = tr("Not allowed because you don't have permission to add parent folder");
                 }
 
-            } else if (!(*it)->isDirectory() && !perms.contains("C")) {
+            } else if (!(*it)->isDirectory() && !perms.hasPermission(RemotePermissions::CanAddFile)) {
                 qCWarning(lcEngine) << "checkForPermission: ERROR" << (*it)->_file;
                 (*it)->_instruction = CSYNC_INSTRUCTION_ERROR;
                 (*it)->_status = SyncFileItem::NormalError;
@@ -1286,12 +1286,12 @@ void SyncEngine::checkForPermission(SyncFileItemVector &syncItems)
             break;
         }
         case CSYNC_INSTRUCTION_SYNC: {
-            const QByteArray perms = getPermissions((*it)->_file);
+            const auto perms = getPermissions((*it)->_file);
             if (perms.isNull()) {
                 // No permissions set
                 break;
             }
-            if (!perms.contains("W")) {
+            if (!perms.hasPermission(RemotePermissions::CanWrite)) {
                 qCWarning(lcEngine) << "checkForPermission: RESTORING" << (*it)->_file;
                 (*it)->_instruction = CSYNC_INSTRUCTION_CONFLICT;
                 (*it)->_direction = SyncFileItem::Down;
@@ -1312,12 +1312,12 @@ void SyncEngine::checkForPermission(SyncFileItemVector &syncItems)
             break;
         }
         case CSYNC_INSTRUCTION_REMOVE: {
-            const QByteArray perms = getPermissions((*it)->_file);
+            const auto perms = getPermissions((*it)->_file);
             if (perms.isNull()) {
                 // No permissions set
                 break;
             }
-            if (!perms.contains("D")) {
+            if (!perms.hasPermission(RemotePermissions::CanDelete)) {
                 qCWarning(lcEngine) << "checkForPermission: RESTORING" << (*it)->_file;
                 (*it)->_instruction = CSYNC_INSTRUCTION_NEW;
                 (*it)->_direction = SyncFileItem::Down;
@@ -1344,7 +1344,8 @@ void SyncEngine::checkForPermission(SyncFileItemVector &syncItems)
                         (*it)->_errorString = tr("Not allowed to remove, restoring");
                     }
                 }
-            } else if (perms.contains("S") && perms.contains("D")) {
+            } else if (perms.hasPermission(RemotePermissions::IsShared)
+                && perms.hasPermission(RemotePermissions::CanDelete)) {
                 // this is a top level shared dir which can be removed to unshare it,
                 // regardless if it is a read only share or not.
                 // To avoid that we try to restore files underneath this dir which have
@@ -1369,8 +1370,8 @@ void SyncEngine::checkForPermission(SyncFileItemVector &syncItems)
         case CSYNC_INSTRUCTION_RENAME: {
             int slashPos = (*it)->_renameTarget.lastIndexOf('/');
             const QString parentDir = slashPos <= 0 ? "" : (*it)->_renameTarget.mid(0, slashPos);
-            const QByteArray destPerms = getPermissions(parentDir);
-            const QByteArray filePerms = getPermissions((*it)->_file);
+            const auto destPerms = getPermissions(parentDir);
+            const auto filePerms = getPermissions((*it)->_file);
 
             //true when it is just a rename in the same directory. (not a move)
             bool isRename = (*it)->_file.startsWith(parentDir) && (*it)->_file.lastIndexOf('/') == slashPos;
@@ -1381,21 +1382,21 @@ void SyncEngine::checkForPermission(SyncFileItemVector &syncItems)
             if (isRename || destPerms.isNull()) {
                 // no need to check for the destination dir permission
                 destinationOK = true;
-            } else if ((*it)->isDirectory() && !destPerms.contains("K")) {
+            } else if ((*it)->isDirectory() && !destPerms.hasPermission(RemotePermissions::CanAddSubDirectories)) {
                 destinationOK = false;
-            } else if (!(*it)->isDirectory() && !destPerms.contains("C")) {
+            } else if (!(*it)->isDirectory() && !destPerms.hasPermission(RemotePermissions::CanAddFile)) {
                 destinationOK = false;
             }
 
             // check if we are allowed to move from the source
             bool sourceOK = true;
             if (!filePerms.isNull()
-                && ((isRename && !filePerms.contains("N"))
-                       || (!isRename && !filePerms.contains("V")))) {
+                && ((isRename && !filePerms.hasPermission(RemotePermissions::CanRename))
+                       || (!isRename && !filePerms.hasPermission(RemotePermissions::CanMove)))) {
                 // We are not allowed to move or rename this file
                 sourceOK = false;
 
-                if (filePerms.contains("D") && destinationOK) {
+                if (filePerms.hasPermission(RemotePermissions::CanDelete) && destinationOK) {
                     // but we are allowed to delete it
                     // TODO!  simulate delete & upload
                 }
@@ -1451,13 +1452,13 @@ void SyncEngine::checkForPermission(SyncFileItemVector &syncItems)
     }
 }
 
-QByteArray SyncEngine::getPermissions(const QString &file) const
+RemotePermissions SyncEngine::getPermissions(const QString &file) const
 {
     static bool isTest = qgetenv("OWNCLOUD_TEST_PERMISSIONS").toInt();
     if (isTest) {
         QRegExp rx("_PERM_([^_]*)_[^/]*$");
         if (rx.indexIn(file) != -1) {
-            return rx.cap(1).toLatin1();
+            return RemotePermissions(rx.cap(1));
         }
     }
 
@@ -1471,7 +1472,7 @@ QByteArray SyncEngine::getPermissions(const QString &file) const
     if (it != _csync_ctx->remote.files.end()) {
         return it->second->remotePerm;
     }
-    return QByteArray();
+    return RemotePermissions();
 }
 
 void SyncEngine::restoreOldFiles(SyncFileItemVector &syncItems)
index 7f96afef92d326943da86d8e9eb4578d5ca3f9e3..44c8ca7a276539422a81616e76085e019fbcc0a2 100644 (file)
@@ -235,7 +235,7 @@ private:
      * to recover
      */
     void checkForPermission(SyncFileItemVector &syncItems);
-    QByteArray getPermissions(const QString &file) const;
+    RemotePermissions getPermissions(const QString &file) const;
 
     /**
      * Instead of downloading files from the server, upload the files to the server
index ab9526a93a559b8784463305fe608bbc61f547fa..dd364ae437ee8073b38df3b8c65988e0d18c6801 100644 (file)
@@ -215,6 +215,7 @@ public:
     Status _status BITFIELD(4);
     bool _isRestoration BITFIELD(1); // The original operation was forbidden, and this is a restoration
     quint16 _httpErrorCode;
+    RemotePermissions _remotePerm;
     QString _errorString; // Contains a string only in case of error
     QByteArray _responseTimeStamp;
     quint32 _affectedItems; // the number of affected items by the operation on this item.
@@ -228,7 +229,6 @@ public:
     quint64 _size;
     quint64 _inode;
     QByteArray _fileId;
-    QByteArray _remotePerm;
 
     // This is the value for the 'new' side, matching with _size and _modtime.
     //
index 4db5b72fcc243adb375d1d529d7913b42e79e368..855abff7a38b6c9b490fdc8c5810c73387abfe88 100644 (file)
@@ -147,7 +147,7 @@ SyncFileStatus SyncFileStatusTracker::fileStatus(const QString &relativePath)
     // First look it up in the database to know if it's shared
     SyncJournalFileRecord rec = _syncEngine->journal()->getFileRecord(relativePath);
     if (rec.isValid()) {
-        return resolveSyncAndErrorStatus(relativePath, rec._remotePerm.contains("S") ? Shared : NotShared);
+        return resolveSyncAndErrorStatus(relativePath, rec._remotePerm.hasPermission(RemotePermissions::IsShared) ? Shared : NotShared);
     }
 
     // Must be a new file not yet in the database, check if it's syncing or has an error.
@@ -226,7 +226,7 @@ void SyncFileStatusTracker::slotAboutToPropagate(SyncFileItemVector &items)
             _syncProblems[item->_file] = SyncFileStatus::StatusWarning;
         }
 
-        SharedFlag sharedFlag = item->_remotePerm.contains("S") ? Shared : NotShared;
+        SharedFlag sharedFlag = item->_remotePerm.hasPermission(RemotePermissions::IsShared) ? Shared : NotShared;
         if (item->_instruction != CSYNC_INSTRUCTION_NONE
             && item->_instruction != CSYNC_INSTRUCTION_UPDATE_METADATA
             && item->_instruction != CSYNC_INSTRUCTION_IGNORE
@@ -272,7 +272,7 @@ void SyncFileStatusTracker::slotItemCompleted(const SyncFileItemPtr &item)
         _syncProblems.erase(item->_file);
     }
 
-    SharedFlag sharedFlag = item->_remotePerm.contains("S") ? Shared : NotShared;
+    SharedFlag sharedFlag = item->_remotePerm.hasPermission(RemotePermissions::IsShared) ? Shared : NotShared;
     if (item->_instruction != CSYNC_INSTRUCTION_NONE
         && item->_instruction != CSYNC_INSTRUCTION_UPDATE_METADATA
         && item->_instruction != CSYNC_INSTRUCTION_IGNORE
index e3ea07cd95bf72f9d2913a907c2b6896b578217d..c7d8b85c3c2ca031940687bc1cf4eb1572a2348b 100644 (file)
@@ -54,7 +54,7 @@ private slots:
         record._type = 5;
         record._etag = "789789";
         record._fileId = "abcd";
-        record._remotePerm = "744";
+        record._remotePerm = RemotePermissions("RW");
         record._fileSize = 213089055;
         record._checksumHeader = "MD5:mychecksum";
         QVERIFY(_db.setFileRecord(record));
@@ -74,7 +74,7 @@ private slots:
         record._type = 7;
         record._etag = "789FFF";
         record._fileId = "efg";
-        record._remotePerm = "777";
+        record._remotePerm = RemotePermissions("NV");
         record._fileSize = 289055;
         _db.setFileRecordMetadata(record);
         storedRecord = _db.getFileRecord("foo");
@@ -91,7 +91,7 @@ private slots:
         {
             SyncJournalFileRecord record;
             record._path = "foo-checksum";
-            record._remotePerm = "744";
+            record._remotePerm = RemotePermissions("RW");
             record._checksumHeader = "MD5:mychecksum";
             record._modtime = QDateTime::currentDateTimeUtc();
             QVERIFY(_db.setFileRecord(record));
@@ -111,7 +111,7 @@ private slots:
         {
             SyncJournalFileRecord record;
             record._path = "foo-nochecksum";
-            record._remotePerm = "744";
+            record._remotePerm = RemotePermissions("RWN");
            record._modtime = QDateTime::currentDateTimeUtc();
 
             QVERIFY(_db.setFileRecord(record));