mirror of
https://github.com/nextcloud/desktop.git
synced 2025-10-26 11:17:43 +00:00
Merge pull request #3669 from nextcloud/bugfix/variousFixesV2
Bugfix/various fixes v2
This commit is contained in:
commit
138323b79d
2
src/3rdparty/libcrashreporter-qt
vendored
2
src/3rdparty/libcrashreporter-qt
vendored
@ -1 +1 @@
|
||||
Subproject commit 34b4665bd7ae5a86115f366b3ec4935d7ecb769f
|
||||
Subproject commit 5423c0ef54f99cea2628c2cb3f48e39e215f9961
|
||||
@ -505,6 +505,9 @@ restart_sync:
|
||||
selectiveSyncFixup(&db, selectiveSyncList);
|
||||
}
|
||||
|
||||
SyncOptions opt;
|
||||
opt.fillFromEnvironmentVariables();
|
||||
opt.verifyChunkSizes();
|
||||
SyncEngine engine(account, options.source_dir, folder, &db);
|
||||
engine.setIgnoreHiddenFiles(options.ignoreHiddenFiles);
|
||||
engine.setNetworkLimits(options.uplimit, options.downlimit);
|
||||
|
||||
@ -144,6 +144,9 @@ QByteArray makeChecksumHeader(const QByteArray &checksumType, const QByteArray &
|
||||
|
||||
QByteArray findBestChecksum(const QByteArray &_checksums)
|
||||
{
|
||||
if (_checksums.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
const auto checksums = QString::fromUtf8(_checksums);
|
||||
int i = 0;
|
||||
// The order of the searches here defines the preference ordering.
|
||||
@ -162,7 +165,7 @@ QByteArray findBestChecksum(const QByteArray &_checksums)
|
||||
return _checksums.mid(i, end - i);
|
||||
}
|
||||
qCWarning(lcChecksums) << "Failed to parse" << _checksums;
|
||||
return QByteArray();
|
||||
return {};
|
||||
}
|
||||
|
||||
bool parseChecksumHeader(const QByteArray &header, QByteArray *type, QByteArray *checksum)
|
||||
|
||||
@ -5,6 +5,7 @@ set(common_SOURCES
|
||||
${CMAKE_CURRENT_LIST_DIR}/checksums.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/filesystembase.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/ownsql.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/preparedsqlquerymanager.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/syncjournaldb.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/syncjournalfilerecord.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/utility.cpp
|
||||
|
||||
@ -490,28 +490,4 @@ void SqlQuery::reset_and_clear_bindings()
|
||||
}
|
||||
}
|
||||
|
||||
PreparedSqlQueryRAII::PreparedSqlQueryRAII(SqlQuery *query)
|
||||
: _query(query)
|
||||
{
|
||||
Q_ASSERT(!sqlite3_stmt_busy(_query->_stmt));
|
||||
}
|
||||
|
||||
PreparedSqlQueryRAII::PreparedSqlQueryRAII(SqlQuery *query, const QByteArray &sql, SqlDatabase &db)
|
||||
: _query(query)
|
||||
{
|
||||
Q_ASSERT(!sqlite3_stmt_busy(_query->_stmt));
|
||||
ENFORCE(!query->_sqldb || &db == query->_sqldb)
|
||||
query->_sqldb = &db;
|
||||
query->_db = db.sqliteDb();
|
||||
if (!query->_stmt) {
|
||||
_ok = query->prepare(sql) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
PreparedSqlQueryRAII::~PreparedSqlQueryRAII()
|
||||
{
|
||||
_query->reset_and_clear_bindings();
|
||||
}
|
||||
|
||||
|
||||
} // namespace OCC
|
||||
|
||||
@ -168,42 +168,9 @@ private:
|
||||
QByteArray _sql;
|
||||
|
||||
friend class SqlDatabase;
|
||||
friend class PreparedSqlQueryRAII;
|
||||
friend class PreparedSqlQueryManager;
|
||||
};
|
||||
|
||||
class OCSYNC_EXPORT PreparedSqlQueryRAII
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Simple Guard which allow reuse of prepared querys.
|
||||
* The queries are reset in the destructor to prevent wal locks
|
||||
*/
|
||||
PreparedSqlQueryRAII(SqlQuery *query);
|
||||
/**
|
||||
* Prepare the SqlQuery if it was not prepared yet.
|
||||
*/
|
||||
PreparedSqlQueryRAII(SqlQuery *query, const QByteArray &sql, SqlDatabase &db);
|
||||
~PreparedSqlQueryRAII();
|
||||
|
||||
explicit operator bool() const { return _ok; }
|
||||
|
||||
SqlQuery *operator->() const
|
||||
{
|
||||
Q_ASSERT(_ok);
|
||||
return _query;
|
||||
}
|
||||
|
||||
SqlQuery &operator*() const &
|
||||
{
|
||||
Q_ASSERT(_ok);
|
||||
return *_query;
|
||||
}
|
||||
|
||||
private:
|
||||
SqlQuery *const _query;
|
||||
bool _ok = true;
|
||||
Q_DISABLE_COPY(PreparedSqlQueryRAII);
|
||||
};
|
||||
} // namespace OCC
|
||||
|
||||
#endif // OWNSQL_H
|
||||
|
||||
56
src/common/preparedsqlquerymanager.cpp
Normal file
56
src/common/preparedsqlquerymanager.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) by Hannah von Reth <hannah.vonreth@owncloud.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 "preparedsqlquerymanager.h"
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
using namespace OCC;
|
||||
|
||||
PreparedSqlQuery::PreparedSqlQuery(SqlQuery *query, bool ok)
|
||||
: _query(query)
|
||||
, _ok(ok)
|
||||
{
|
||||
}
|
||||
|
||||
PreparedSqlQuery::~PreparedSqlQuery()
|
||||
{
|
||||
_query->reset_and_clear_bindings();
|
||||
}
|
||||
|
||||
const PreparedSqlQuery PreparedSqlQueryManager::get(PreparedSqlQueryManager::Key key)
|
||||
{
|
||||
auto &query = _queries[key];
|
||||
ENFORCE(query._stmt)
|
||||
Q_ASSERT(!sqlite3_stmt_busy(query._stmt));
|
||||
return { &query };
|
||||
}
|
||||
|
||||
const PreparedSqlQuery PreparedSqlQueryManager::get(PreparedSqlQueryManager::Key key, const QByteArray &sql, SqlDatabase &db)
|
||||
{
|
||||
auto &query = _queries[key];
|
||||
Q_ASSERT(!sqlite3_stmt_busy(query._stmt));
|
||||
ENFORCE(!query._sqldb || &db == query._sqldb)
|
||||
if (!query._stmt) {
|
||||
query._sqldb = &db;
|
||||
query._db = db.sqliteDb();
|
||||
return { &query, query.prepare(sql) == 0 };
|
||||
}
|
||||
return { &query };
|
||||
}
|
||||
119
src/common/preparedsqlquerymanager.h
Normal file
119
src/common/preparedsqlquerymanager.h
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (C) by Hannah von Reth <hannah.vonreth@owncloud.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 "ocsynclib.h"
|
||||
#include "ownsql.h"
|
||||
#include "common/asserts.h"
|
||||
|
||||
namespace OCC {
|
||||
|
||||
class OCSYNC_EXPORT PreparedSqlQuery
|
||||
{
|
||||
public:
|
||||
~PreparedSqlQuery();
|
||||
|
||||
explicit operator bool() const { return _ok; }
|
||||
|
||||
SqlQuery *operator->() const
|
||||
{
|
||||
Q_ASSERT(_ok);
|
||||
return _query;
|
||||
}
|
||||
|
||||
SqlQuery &operator*() const &
|
||||
{
|
||||
Q_ASSERT(_ok);
|
||||
return *_query;
|
||||
}
|
||||
|
||||
private:
|
||||
PreparedSqlQuery(SqlQuery *query, bool ok = true);
|
||||
|
||||
SqlQuery *_query;
|
||||
bool _ok;
|
||||
|
||||
friend class PreparedSqlQueryManager;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Manage PreparedSqlQuery
|
||||
*/
|
||||
class OCSYNC_EXPORT PreparedSqlQueryManager
|
||||
{
|
||||
public:
|
||||
enum Key {
|
||||
GetFileRecordQuery,
|
||||
GetFileRecordQueryByMangledName,
|
||||
GetFileRecordQueryByInode,
|
||||
GetFileRecordQueryByFileId,
|
||||
GetFilesBelowPathQuery,
|
||||
GetAllFilesQuery,
|
||||
ListFilesInPathQuery,
|
||||
SetFileRecordQuery,
|
||||
SetFileRecordChecksumQuery,
|
||||
SetFileRecordLocalMetadataQuery,
|
||||
GetDownloadInfoQuery,
|
||||
SetDownloadInfoQuery,
|
||||
DeleteDownloadInfoQuery,
|
||||
GetUploadInfoQuery,
|
||||
SetUploadInfoQuery,
|
||||
DeleteUploadInfoQuery,
|
||||
DeleteFileRecordPhash,
|
||||
DeleteFileRecordRecursively,
|
||||
GetErrorBlacklistQuery,
|
||||
SetErrorBlacklistQuery,
|
||||
GetSelectiveSyncListQuery,
|
||||
GetChecksumTypeIdQuery,
|
||||
GetChecksumTypeQuery,
|
||||
InsertChecksumTypeQuery,
|
||||
GetDataFingerprintQuery,
|
||||
SetDataFingerprintQuery1,
|
||||
SetDataFingerprintQuery2,
|
||||
SetKeyValueStoreQuery,
|
||||
GetKeyValueStoreQuery,
|
||||
DeleteKeyValueStoreQuery,
|
||||
GetConflictRecordQuery,
|
||||
SetConflictRecordQuery,
|
||||
DeleteConflictRecordQuery,
|
||||
GetRawPinStateQuery,
|
||||
GetEffectivePinStateQuery,
|
||||
GetSubPinsQuery,
|
||||
CountDehydratedFilesQuery,
|
||||
SetPinStateQuery,
|
||||
WipePinStateQuery,
|
||||
|
||||
PreparedQueryCount
|
||||
};
|
||||
PreparedSqlQueryManager() = default;
|
||||
/**
|
||||
* The queries are reset in the destructor to prevent wal locks
|
||||
*/
|
||||
const PreparedSqlQuery get(Key key);
|
||||
/**
|
||||
* Prepare the SqlQuery if it was not prepared yet.
|
||||
*/
|
||||
const PreparedSqlQuery get(Key key, const QByteArray &sql, SqlDatabase &db);
|
||||
|
||||
private:
|
||||
SqlQuery _queries[PreparedQueryCount];
|
||||
Q_DISABLE_COPY(PreparedSqlQueryManager);
|
||||
};
|
||||
|
||||
}
|
||||
@ -104,6 +104,7 @@ public:
|
||||
ASSERT(!_isError);
|
||||
return _result;
|
||||
}
|
||||
|
||||
T operator*() &&
|
||||
{
|
||||
ASSERT(!_isError);
|
||||
@ -116,6 +117,12 @@ public:
|
||||
return &_result;
|
||||
}
|
||||
|
||||
const T &get() const
|
||||
{
|
||||
ASSERT(!_isError)
|
||||
return _result;
|
||||
}
|
||||
|
||||
const Error &error() const &
|
||||
{
|
||||
ASSERT(_isError);
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
#include "filesystembase.h"
|
||||
#include "common/asserts.h"
|
||||
#include "common/checksums.h"
|
||||
#include "common/preparedsqlquerymanager.h"
|
||||
|
||||
#include "common/c_jhash.h"
|
||||
|
||||
@ -586,16 +587,15 @@ bool SyncJournalDb::checkConnect()
|
||||
if (forceRemoteDiscovery) {
|
||||
forceRemoteDiscoveryNextSyncLocked();
|
||||
}
|
||||
|
||||
const PreparedSqlQueryRAII deleteDownloadInfo(&_deleteDownloadInfoQuery, QByteArrayLiteral("DELETE FROM downloadinfo WHERE path=?1"), _db);
|
||||
const auto deleteDownloadInfo = _queryManager.get(PreparedSqlQueryManager::DeleteDownloadInfoQuery, QByteArrayLiteral("DELETE FROM downloadinfo WHERE path=?1"), _db);
|
||||
if (!deleteDownloadInfo) {
|
||||
return sqlFail(QStringLiteral("prepare _deleteDownloadInfoQuery"), _deleteDownloadInfoQuery);
|
||||
return sqlFail(QStringLiteral("prepare _deleteDownloadInfoQuery"), *deleteDownloadInfo);
|
||||
}
|
||||
|
||||
|
||||
const PreparedSqlQueryRAII deleteUploadInfoQuery(&_deleteUploadInfoQuery, QByteArrayLiteral("DELETE FROM uploadinfo WHERE path=?1"), _db);
|
||||
const auto deleteUploadInfoQuery = _queryManager.get(PreparedSqlQueryManager::DeleteUploadInfoQuery, QByteArrayLiteral("DELETE FROM uploadinfo WHERE path=?1"), _db);
|
||||
if (!deleteUploadInfoQuery) {
|
||||
return sqlFail(QStringLiteral("prepare _deleteUploadInfoQuery"), _deleteUploadInfoQuery);
|
||||
return sqlFail(QStringLiteral("prepare _deleteUploadInfoQuery"), *deleteUploadInfoQuery);
|
||||
}
|
||||
|
||||
QByteArray sql("SELECT lastTryEtag, lastTryModtime, retrycount, errorstring, lastTryTime, ignoreDuration, renameTarget, errorCategory, requestId "
|
||||
@ -605,7 +605,7 @@ bool SyncJournalDb::checkConnect()
|
||||
// case insensitively
|
||||
sql += " COLLATE NOCASE";
|
||||
}
|
||||
const PreparedSqlQueryRAII getErrorBlacklistQuery(&_getErrorBlacklistQuery, sql, _db);
|
||||
const auto getErrorBlacklistQuery = _queryManager.get(PreparedSqlQueryManager::GetErrorBlacklistQuery, sql, _db);
|
||||
if (!getErrorBlacklistQuery) {
|
||||
return sqlFail(QStringLiteral("prepare _getErrorBlacklistQuery"), *getErrorBlacklistQuery);
|
||||
}
|
||||
@ -892,7 +892,7 @@ QVector<QByteArray> SyncJournalDb::tableColumns(const QByteArray &table)
|
||||
|
||||
qint64 SyncJournalDb::getPHash(const QByteArray &file)
|
||||
{
|
||||
int64_t h = 0;
|
||||
qint64 h = 0;
|
||||
int len = file.length();
|
||||
|
||||
h = c_jhash64((uint8_t *)file.data(), len, 0);
|
||||
@ -922,7 +922,7 @@ Result<void, QString> SyncJournalDb::setFileRecord(const SyncJournalFileRecord &
|
||||
<< "fileSize:" << record._fileSize << "checksum:" << record._checksumHeader
|
||||
<< "e2eMangledName:" << record.e2eMangledName() << "isE2eEncrypted:" << record._isE2eEncrypted;
|
||||
|
||||
qlonglong phash = getPHash(record._path);
|
||||
const qint64 phash = getPHash(record._path);
|
||||
if (checkConnect()) {
|
||||
int plen = record._path.length();
|
||||
|
||||
@ -937,10 +937,10 @@ Result<void, QString> SyncJournalDb::setFileRecord(const SyncJournalFileRecord &
|
||||
parseChecksumHeader(record._checksumHeader, &checksumType, &checksum);
|
||||
int contentChecksumTypeId = mapChecksumType(checksumType);
|
||||
|
||||
const PreparedSqlQueryRAII query(&_setFileRecordQuery, QByteArrayLiteral("INSERT OR REPLACE INTO metadata "
|
||||
"(phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize, ignoredChildrenRemote, contentChecksum, contentChecksumTypeId, e2eMangledName, isE2eEncrypted) "
|
||||
"VALUES (?1 , ?2, ?3 , ?4 , ?5 , ?6 , ?7, ?8 , ?9 , ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18);"),
|
||||
_db);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::SetFileRecordQuery, QByteArrayLiteral("INSERT OR REPLACE INTO metadata "
|
||||
"(phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize, ignoredChildrenRemote, contentChecksum, contentChecksumTypeId, e2eMangledName, isE2eEncrypted) "
|
||||
"VALUES (?1 , ?2, ?3 , ?4 , ?5 , ?6 , ?7, ?8 , ?9 , ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18);"),
|
||||
_db);
|
||||
if (!query) {
|
||||
return query->error();
|
||||
}
|
||||
@ -985,7 +985,7 @@ void SyncJournalDb::keyValueStoreSet(const QString &key, QVariant value)
|
||||
return;
|
||||
}
|
||||
|
||||
const PreparedSqlQueryRAII query(&_setKeyValueStoreQuery, QByteArrayLiteral("INSERT OR REPLACE INTO key_value_store (key, value) VALUES(?1, ?2);"), _db);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::SetKeyValueStoreQuery, QByteArrayLiteral("INSERT OR REPLACE INTO key_value_store (key, value) VALUES(?1, ?2);"), _db);
|
||||
if (!query) {
|
||||
return;
|
||||
}
|
||||
@ -1002,7 +1002,7 @@ qint64 SyncJournalDb::keyValueStoreGetInt(const QString &key, qint64 defaultValu
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
const PreparedSqlQueryRAII query(&_getKeyValueStoreQuery, QByteArrayLiteral("SELECT value FROM key_value_store WHERE key = ?1;"), _db);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::GetKeyValueStoreQuery, QByteArrayLiteral("SELECT value FROM key_value_store WHERE key = ?1;"), _db);
|
||||
if (!query) {
|
||||
return defaultValue;
|
||||
}
|
||||
@ -1024,7 +1024,7 @@ QVariant SyncJournalDb::keyValueStoreGet(const QString &key, QVariant defaultVal
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
const PreparedSqlQueryRAII query(&_getKeyValueStoreQuery, QByteArrayLiteral("SELECT value FROM key_value_store WHERE key = ?1;"), _db);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::GetKeyValueStoreQuery, QByteArrayLiteral("SELECT value FROM key_value_store WHERE key = ?1;"), _db);
|
||||
if (!query) {
|
||||
return defaultValue;
|
||||
}
|
||||
@ -1041,7 +1041,7 @@ QVariant SyncJournalDb::keyValueStoreGet(const QString &key, QVariant defaultVal
|
||||
|
||||
void SyncJournalDb::keyValueStoreDelete(const QString &key)
|
||||
{
|
||||
const PreparedSqlQueryRAII query(&_deleteKeyValueStoreQuery, QByteArrayLiteral("DELETE FROM key_value_store WHERE key=?1;"), _db);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::DeleteKeyValueStoreQuery, QByteArrayLiteral("DELETE FROM key_value_store WHERE key=?1;"), _db);
|
||||
if (!query) {
|
||||
qCWarning(lcDb) << "Failed to initOrReset _deleteKeyValueStoreQuery";
|
||||
Q_ASSERT(false);
|
||||
@ -1063,12 +1063,12 @@ bool SyncJournalDb::deleteFileRecord(const QString &filename, bool recursively)
|
||||
// always delete the actual file.
|
||||
|
||||
{
|
||||
const PreparedSqlQueryRAII query(&_deleteFileRecordPhash, QByteArrayLiteral("DELETE FROM metadata WHERE phash=?1"), _db);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::DeleteFileRecordPhash, QByteArrayLiteral("DELETE FROM metadata WHERE phash=?1"), _db);
|
||||
if (!query) {
|
||||
return false;
|
||||
}
|
||||
|
||||
qlonglong phash = getPHash(filename.toUtf8());
|
||||
const qint64 phash = getPHash(filename.toUtf8());
|
||||
query->bindValue(1, phash);
|
||||
|
||||
if (!query->exec()) {
|
||||
@ -1077,7 +1077,7 @@ bool SyncJournalDb::deleteFileRecord(const QString &filename, bool recursively)
|
||||
}
|
||||
|
||||
if (recursively) {
|
||||
const PreparedSqlQueryRAII query(&_deleteFileRecordRecursively, QByteArrayLiteral("DELETE FROM metadata WHERE " IS_PREFIX_PATH_OF("?1", "path")), _db);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::DeleteFileRecordRecursively, QByteArrayLiteral("DELETE FROM metadata WHERE " IS_PREFIX_PATH_OF("?1", "path")), _db);
|
||||
if (!query)
|
||||
return false;
|
||||
query->bindValue(1, filename);
|
||||
@ -1109,7 +1109,7 @@ bool SyncJournalDb::getFileRecord(const QByteArray &filename, SyncJournalFileRec
|
||||
return false;
|
||||
|
||||
if (!filename.isEmpty()) {
|
||||
const PreparedSqlQueryRAII query(&_getFileRecordQuery, QByteArrayLiteral(GET_FILE_RECORD_QUERY " WHERE phash=?1"), _db);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::GetFileRecordQuery, QByteArrayLiteral(GET_FILE_RECORD_QUERY " WHERE phash=?1"), _db);
|
||||
if (!query) {
|
||||
return false;
|
||||
}
|
||||
@ -1153,7 +1153,7 @@ bool SyncJournalDb::getFileRecordByE2eMangledName(const QString &mangledName, Sy
|
||||
}
|
||||
|
||||
if (!mangledName.isEmpty()) {
|
||||
const PreparedSqlQueryRAII query(&_getFileRecordQueryByMangledName, QByteArrayLiteral(GET_FILE_RECORD_QUERY " WHERE e2eMangledName=?1"), _db);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::GetFileRecordQueryByMangledName, QByteArrayLiteral(GET_FILE_RECORD_QUERY " WHERE e2eMangledName=?1"), _db);
|
||||
if (!query) {
|
||||
return false;
|
||||
}
|
||||
@ -1193,7 +1193,7 @@ bool SyncJournalDb::getFileRecordByInode(quint64 inode, SyncJournalFileRecord *r
|
||||
|
||||
if (!checkConnect())
|
||||
return false;
|
||||
const PreparedSqlQueryRAII query(&_getFileRecordQueryByInode, QByteArrayLiteral(GET_FILE_RECORD_QUERY " WHERE inode=?1"), _db);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::GetFileRecordQueryByInode, QByteArrayLiteral(GET_FILE_RECORD_QUERY " WHERE inode=?1"), _db);
|
||||
if (!query)
|
||||
return false;
|
||||
|
||||
@ -1221,7 +1221,7 @@ bool SyncJournalDb::getFileRecordsByFileId(const QByteArray &fileId, const std::
|
||||
if (!checkConnect())
|
||||
return false;
|
||||
|
||||
const PreparedSqlQueryRAII query(&_getFileRecordQueryByFileId, QByteArrayLiteral(GET_FILE_RECORD_QUERY " WHERE fileid=?1"), _db);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::GetFileRecordQueryByFileId, QByteArrayLiteral(GET_FILE_RECORD_QUERY " WHERE fileid=?1"), _db);
|
||||
if (!query) {
|
||||
return false;
|
||||
}
|
||||
@ -1281,7 +1281,7 @@ bool SyncJournalDb::getFilesBelowPath(const QByteArray &path, const std::functio
|
||||
// and find nothing. So, unfortunately, we have to use a different query for
|
||||
// retrieving the whole tree.
|
||||
|
||||
const PreparedSqlQueryRAII query(&_getAllFilesQuery, QByteArrayLiteral(GET_FILE_RECORD_QUERY " ORDER BY path||'/' ASC"), _db);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::GetAllFilesQuery, QByteArrayLiteral(GET_FILE_RECORD_QUERY " ORDER BY path||'/' ASC"), _db);
|
||||
if (!query) {
|
||||
return false;
|
||||
}
|
||||
@ -1289,15 +1289,15 @@ bool SyncJournalDb::getFilesBelowPath(const QByteArray &path, const std::functio
|
||||
} else {
|
||||
// This query is used to skip discovery and fill the tree from the
|
||||
// database instead
|
||||
const PreparedSqlQueryRAII query(&_getFilesBelowPathQuery, QByteArrayLiteral(GET_FILE_RECORD_QUERY " WHERE " IS_PREFIX_PATH_OF("?1", "path")
|
||||
" OR " IS_PREFIX_PATH_OF("?1", "e2eMangledName")
|
||||
// We want to ensure that the contents of a directory are sorted
|
||||
// directly behind the directory itself. Without this ORDER BY
|
||||
// an ordering like foo, foo-2, foo/file would be returned.
|
||||
// With the trailing /, we get foo-2, foo, foo/file. This property
|
||||
// is used in fill_tree_from_db().
|
||||
" ORDER BY path||'/' ASC"),
|
||||
_db);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::GetFilesBelowPathQuery, QByteArrayLiteral(GET_FILE_RECORD_QUERY " WHERE " IS_PREFIX_PATH_OF("?1", "path")
|
||||
" OR " IS_PREFIX_PATH_OF("?1", "e2eMangledName")
|
||||
// We want to ensure that the contents of a directory are sorted
|
||||
// directly behind the directory itself. Without this ORDER BY
|
||||
// an ordering like foo, foo-2, foo/file would be returned.
|
||||
// With the trailing /, we get foo-2, foo, foo/file. This property
|
||||
// is used in fill_tree_from_db().
|
||||
" ORDER BY path||'/' ASC"),
|
||||
_db);
|
||||
if (!query) {
|
||||
return false;
|
||||
}
|
||||
@ -1317,7 +1317,7 @@ bool SyncJournalDb::listFilesInPath(const QByteArray& path,
|
||||
if (!checkConnect())
|
||||
return false;
|
||||
|
||||
const PreparedSqlQueryRAII query(&_listFilesInPathQuery, QByteArrayLiteral(GET_FILE_RECORD_QUERY " WHERE parent_hash(path) = ?1 ORDER BY path||'/' ASC"), _db);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::ListFilesInPathQuery, QByteArrayLiteral(GET_FILE_RECORD_QUERY " WHERE parent_hash(path) = ?1 ORDER BY path||'/' ASC"), _db);
|
||||
if (!query) {
|
||||
return false;
|
||||
}
|
||||
@ -1372,7 +1372,7 @@ bool SyncJournalDb::updateFileRecordChecksum(const QString &filename,
|
||||
|
||||
qCInfo(lcDb) << "Updating file checksum" << filename << contentChecksum << contentChecksumType;
|
||||
|
||||
qlonglong phash = getPHash(filename.toUtf8());
|
||||
const qint64 phash = getPHash(filename.toUtf8());
|
||||
if (!checkConnect()) {
|
||||
qCWarning(lcDb) << "Failed to connect database.";
|
||||
return false;
|
||||
@ -1380,9 +1380,9 @@ bool SyncJournalDb::updateFileRecordChecksum(const QString &filename,
|
||||
|
||||
int checksumTypeId = mapChecksumType(contentChecksumType);
|
||||
|
||||
const PreparedSqlQueryRAII query(&_setFileRecordChecksumQuery, QByteArrayLiteral("UPDATE metadata"
|
||||
" SET contentChecksum = ?2, contentChecksumTypeId = ?3"
|
||||
" WHERE phash == ?1;"),
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::SetFileRecordChecksumQuery, QByteArrayLiteral("UPDATE metadata"
|
||||
" SET contentChecksum = ?2, contentChecksumTypeId = ?3"
|
||||
" WHERE phash == ?1;"),
|
||||
_db);
|
||||
if (!query) {
|
||||
return false;
|
||||
@ -1401,15 +1401,15 @@ bool SyncJournalDb::updateLocalMetadata(const QString &filename,
|
||||
|
||||
qCInfo(lcDb) << "Updating local metadata for:" << filename << modtime << size << inode;
|
||||
|
||||
qlonglong phash = getPHash(filename.toUtf8());
|
||||
const qint64 phash = getPHash(filename.toUtf8());
|
||||
if (!checkConnect()) {
|
||||
qCWarning(lcDb) << "Failed to connect database.";
|
||||
return false;
|
||||
}
|
||||
|
||||
const PreparedSqlQueryRAII query(&_setFileRecordLocalMetadataQuery, QByteArrayLiteral("UPDATE metadata"
|
||||
" SET inode=?2, modtime=?3, filesize=?4"
|
||||
" WHERE phash == ?1;"),
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::SetFileRecordLocalMetadataQuery, QByteArrayLiteral("UPDATE metadata"
|
||||
" SET inode=?2, modtime=?3, filesize=?4"
|
||||
" WHERE phash == ?1;"),
|
||||
_db);
|
||||
if (!query) {
|
||||
return false;
|
||||
@ -1428,8 +1428,8 @@ Optional<SyncJournalDb::HasHydratedDehydrated> SyncJournalDb::hasHydratedOrDehyd
|
||||
if (!checkConnect())
|
||||
return {};
|
||||
|
||||
const PreparedSqlQueryRAII query(&_countDehydratedFilesQuery, QByteArrayLiteral("SELECT DISTINCT type FROM metadata"
|
||||
" WHERE (" IS_PREFIX_PATH_OR_EQUAL("?1", "path") " OR ?1 == '');"),
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::CountDehydratedFilesQuery, QByteArrayLiteral("SELECT DISTINCT type FROM metadata"
|
||||
" WHERE (" IS_PREFIX_PATH_OR_EQUAL("?1", "path") " OR ?1 == '');"),
|
||||
_db);
|
||||
if (!query) {
|
||||
return {};
|
||||
@ -1490,7 +1490,7 @@ SyncJournalDb::DownloadInfo SyncJournalDb::getDownloadInfo(const QString &file)
|
||||
DownloadInfo res;
|
||||
|
||||
if (checkConnect()) {
|
||||
const PreparedSqlQueryRAII query(&_getDownloadInfoQuery, QByteArrayLiteral("SELECT tmpfile, etag, errorcount FROM downloadinfo WHERE path=?1"), _db);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::GetDownloadInfoQuery, QByteArrayLiteral("SELECT tmpfile, etag, errorcount FROM downloadinfo WHERE path=?1"), _db);
|
||||
if (!query) {
|
||||
return res;
|
||||
}
|
||||
@ -1518,9 +1518,9 @@ void SyncJournalDb::setDownloadInfo(const QString &file, const SyncJournalDb::Do
|
||||
|
||||
|
||||
if (i._valid) {
|
||||
const PreparedSqlQueryRAII query(&_setDownloadInfoQuery, QByteArrayLiteral("INSERT OR REPLACE INTO downloadinfo "
|
||||
"(path, tmpfile, etag, errorcount) "
|
||||
"VALUES ( ?1 , ?2, ?3, ?4 )"),
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::SetDownloadInfoQuery, QByteArrayLiteral("INSERT OR REPLACE INTO downloadinfo "
|
||||
"(path, tmpfile, etag, errorcount) "
|
||||
"VALUES ( ?1 , ?2, ?3, ?4 )"),
|
||||
_db);
|
||||
if (!query) {
|
||||
return;
|
||||
@ -1531,7 +1531,7 @@ void SyncJournalDb::setDownloadInfo(const QString &file, const SyncJournalDb::Do
|
||||
query->bindValue(4, i._errorCount);
|
||||
query->exec();
|
||||
} else {
|
||||
const PreparedSqlQueryRAII query(&_deleteDownloadInfoQuery);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::DeleteDownloadInfoQuery);
|
||||
query->bindValue(1, file);
|
||||
query->exec();
|
||||
}
|
||||
@ -1568,7 +1568,7 @@ QVector<SyncJournalDb::DownloadInfo> SyncJournalDb::getAndDeleteStaleDownloadInf
|
||||
}
|
||||
|
||||
{
|
||||
const PreparedSqlQueryRAII query(&_deleteDownloadInfoQuery);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::DeleteDownloadInfoQuery);
|
||||
if (!deleteBatch(*query, superfluousPaths, QStringLiteral("downloadinfo"))) {
|
||||
return empty_result;
|
||||
}
|
||||
@ -1602,8 +1602,8 @@ SyncJournalDb::UploadInfo SyncJournalDb::getUploadInfo(const QString &file)
|
||||
UploadInfo res;
|
||||
|
||||
if (checkConnect()) {
|
||||
const PreparedSqlQueryRAII query(&_getUploadInfoQuery, QByteArrayLiteral("SELECT chunk, transferid, errorcount, size, modtime, contentChecksum FROM "
|
||||
"uploadinfo WHERE path=?1"),
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::GetUploadInfoQuery, QByteArrayLiteral("SELECT chunk, transferid, errorcount, size, modtime, contentChecksum FROM "
|
||||
"uploadinfo WHERE path=?1"),
|
||||
_db);
|
||||
if (!query) {
|
||||
return res;
|
||||
@ -1637,9 +1637,9 @@ void SyncJournalDb::setUploadInfo(const QString &file, const SyncJournalDb::Uplo
|
||||
}
|
||||
|
||||
if (i._valid) {
|
||||
const PreparedSqlQueryRAII query(&_setUploadInfoQuery, QByteArrayLiteral("INSERT OR REPLACE INTO uploadinfo "
|
||||
"(path, chunk, transferid, errorcount, size, modtime, contentChecksum) "
|
||||
"VALUES ( ?1 , ?2, ?3 , ?4 , ?5, ?6 , ?7 )"),
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::SetUploadInfoQuery, QByteArrayLiteral("INSERT OR REPLACE INTO uploadinfo "
|
||||
"(path, chunk, transferid, errorcount, size, modtime, contentChecksum) "
|
||||
"VALUES ( ?1 , ?2, ?3 , ?4 , ?5, ?6 , ?7 )"),
|
||||
_db);
|
||||
if (!query) {
|
||||
return;
|
||||
@ -1657,7 +1657,7 @@ void SyncJournalDb::setUploadInfo(const QString &file, const SyncJournalDb::Uplo
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const PreparedSqlQueryRAII query(&_deleteUploadInfoQuery);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::DeleteUploadInfoQuery);
|
||||
query->bindValue(1, file);
|
||||
|
||||
if (!query->exec()) {
|
||||
@ -1692,7 +1692,7 @@ QVector<uint> SyncJournalDb::deleteStaleUploadInfos(const QSet<QString> &keep)
|
||||
}
|
||||
}
|
||||
|
||||
const PreparedSqlQueryRAII deleteUploadInfoQuery(&_deleteUploadInfoQuery);
|
||||
const auto deleteUploadInfoQuery = _queryManager.get(PreparedSqlQueryManager::DeleteUploadInfoQuery);
|
||||
deleteBatch(*deleteUploadInfoQuery, superfluousPaths, QStringLiteral("uploadinfo"));
|
||||
return ids;
|
||||
}
|
||||
@ -1706,7 +1706,7 @@ SyncJournalErrorBlacklistRecord SyncJournalDb::errorBlacklistEntry(const QString
|
||||
return entry;
|
||||
|
||||
if (checkConnect()) {
|
||||
const PreparedSqlQueryRAII query(&_getErrorBlacklistQuery);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::GetErrorBlacklistQuery);
|
||||
query->bindValue(1, file);
|
||||
if (query->exec()) {
|
||||
if (query->next().hasData) {
|
||||
@ -1847,9 +1847,9 @@ void SyncJournalDb::setErrorBlacklistEntry(const SyncJournalErrorBlacklistRecord
|
||||
return;
|
||||
}
|
||||
|
||||
const PreparedSqlQueryRAII query(&_setErrorBlacklistQuery, QByteArrayLiteral("INSERT OR REPLACE INTO blacklist "
|
||||
"(path, lastTryEtag, lastTryModtime, retrycount, errorstring, lastTryTime, ignoreDuration, renameTarget, errorCategory, requestId) "
|
||||
"VALUES ( ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)"),
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::SetErrorBlacklistQuery, QByteArrayLiteral("INSERT OR REPLACE INTO blacklist "
|
||||
"(path, lastTryEtag, lastTryModtime, retrycount, errorstring, lastTryTime, ignoreDuration, renameTarget, errorCategory, requestId) "
|
||||
"VALUES ( ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)"),
|
||||
_db);
|
||||
if (!query) {
|
||||
return;
|
||||
@ -1927,7 +1927,7 @@ QStringList SyncJournalDb::getSelectiveSyncList(SyncJournalDb::SelectiveSyncList
|
||||
return result;
|
||||
}
|
||||
|
||||
const PreparedSqlQueryRAII query(&_getSelectiveSyncListQuery, QByteArrayLiteral("SELECT path FROM selectivesync WHERE type=?1"), _db);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::GetSelectiveSyncListQuery, QByteArrayLiteral("SELECT path FROM selectivesync WHERE type=?1"), _db);
|
||||
if (!query) {
|
||||
*ok = false;
|
||||
return result;
|
||||
@ -2064,7 +2064,7 @@ QByteArray SyncJournalDb::getChecksumType(int checksumTypeId)
|
||||
}
|
||||
|
||||
// Retrieve the id
|
||||
const PreparedSqlQueryRAII query(&_getChecksumTypeQuery, QByteArrayLiteral("SELECT name FROM checksumtype WHERE id=?1"), _db);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::GetChecksumTypeQuery, QByteArrayLiteral("SELECT name FROM checksumtype WHERE id=?1"), _db);
|
||||
if (!query) {
|
||||
return {};
|
||||
}
|
||||
@ -2092,7 +2092,7 @@ int SyncJournalDb::mapChecksumType(const QByteArray &checksumType)
|
||||
|
||||
// Ensure the checksum type is in the db
|
||||
{
|
||||
const PreparedSqlQueryRAII query(&_insertChecksumTypeQuery, QByteArrayLiteral("INSERT OR IGNORE INTO checksumtype (name) VALUES (?1)"), _db);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::InsertChecksumTypeQuery, QByteArrayLiteral("INSERT OR IGNORE INTO checksumtype (name) VALUES (?1)"), _db);
|
||||
if (!query) {
|
||||
return 0;
|
||||
}
|
||||
@ -2104,7 +2104,7 @@ int SyncJournalDb::mapChecksumType(const QByteArray &checksumType)
|
||||
|
||||
// Retrieve the id
|
||||
{
|
||||
const PreparedSqlQueryRAII query(&_getChecksumTypeIdQuery, QByteArrayLiteral("SELECT id FROM checksumtype WHERE name=?1"), _db);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::GetChecksumTypeIdQuery, QByteArrayLiteral("SELECT id FROM checksumtype WHERE name=?1"), _db);
|
||||
if (!query) {
|
||||
return 0;
|
||||
}
|
||||
@ -2130,7 +2130,7 @@ QByteArray SyncJournalDb::dataFingerprint()
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
const PreparedSqlQueryRAII query(&_getDataFingerprintQuery, QByteArrayLiteral("SELECT fingerprint FROM datafingerprint"), _db);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::GetDataFingerprintQuery, QByteArrayLiteral("SELECT fingerprint FROM datafingerprint"), _db);
|
||||
if (!query) {
|
||||
return QByteArray();
|
||||
}
|
||||
@ -2152,16 +2152,16 @@ void SyncJournalDb::setDataFingerprint(const QByteArray &dataFingerprint)
|
||||
return;
|
||||
}
|
||||
|
||||
const PreparedSqlQueryRAII setDataFingerprintQuery1(&_setDataFingerprintQuery1, QByteArrayLiteral("DELETE FROM datafingerprint;"), _db);
|
||||
const PreparedSqlQueryRAII setDataFingerprintQuery2(&_setDataFingerprintQuery2, QByteArrayLiteral("INSERT INTO datafingerprint (fingerprint) VALUES (?1);"), _db);
|
||||
const auto setDataFingerprintQuery1 = _queryManager.get(PreparedSqlQueryManager::SetDataFingerprintQuery1, QByteArrayLiteral("DELETE FROM datafingerprint;"), _db);
|
||||
const auto setDataFingerprintQuery2 = _queryManager.get(PreparedSqlQueryManager::SetDataFingerprintQuery2, QByteArrayLiteral("INSERT INTO datafingerprint (fingerprint) VALUES (?1);"), _db);
|
||||
if (!setDataFingerprintQuery1 || !setDataFingerprintQuery2) {
|
||||
return;
|
||||
}
|
||||
|
||||
_setDataFingerprintQuery1.exec();
|
||||
setDataFingerprintQuery1->exec();
|
||||
|
||||
_setDataFingerprintQuery2.bindValue(1, dataFingerprint);
|
||||
_setDataFingerprintQuery2.exec();
|
||||
setDataFingerprintQuery2->bindValue(1, dataFingerprint);
|
||||
setDataFingerprintQuery2->exec();
|
||||
}
|
||||
|
||||
void SyncJournalDb::setConflictRecord(const ConflictRecord &record)
|
||||
@ -2170,10 +2170,10 @@ void SyncJournalDb::setConflictRecord(const ConflictRecord &record)
|
||||
if (!checkConnect())
|
||||
return;
|
||||
|
||||
const PreparedSqlQueryRAII query(&_setConflictRecordQuery, QByteArrayLiteral("INSERT OR REPLACE INTO conflicts "
|
||||
"(path, baseFileId, baseModtime, baseEtag, basePath) "
|
||||
"VALUES (?1, ?2, ?3, ?4, ?5);"),
|
||||
_db);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::SetConflictRecordQuery, QByteArrayLiteral("INSERT OR REPLACE INTO conflicts "
|
||||
"(path, baseFileId, baseModtime, baseEtag, basePath) "
|
||||
"VALUES (?1, ?2, ?3, ?4, ?5);"),
|
||||
_db);
|
||||
ASSERT(query)
|
||||
query->bindValue(1, record.path);
|
||||
query->bindValue(2, record.baseFileId);
|
||||
@ -2191,7 +2191,7 @@ ConflictRecord SyncJournalDb::conflictRecord(const QByteArray &path)
|
||||
if (!checkConnect()) {
|
||||
return entry;
|
||||
}
|
||||
const PreparedSqlQueryRAII query(&_getConflictRecordQuery, QByteArrayLiteral("SELECT baseFileId, baseModtime, baseEtag, basePath FROM conflicts WHERE path=?1;"), _db);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::GetConflictRecordQuery, QByteArrayLiteral("SELECT baseFileId, baseModtime, baseEtag, basePath FROM conflicts WHERE path=?1;"), _db);
|
||||
ASSERT(query)
|
||||
query->bindValue(1, path);
|
||||
ASSERT(query->exec())
|
||||
@ -2212,7 +2212,7 @@ void SyncJournalDb::deleteConflictRecord(const QByteArray &path)
|
||||
if (!checkConnect())
|
||||
return;
|
||||
|
||||
const PreparedSqlQueryRAII query(&_deleteConflictRecordQuery, QByteArrayLiteral("DELETE FROM conflicts WHERE path=?1;"), _db);
|
||||
const auto query = _queryManager.get(PreparedSqlQueryManager::DeleteConflictRecordQuery, QByteArrayLiteral("DELETE FROM conflicts WHERE path=?1;"), _db);
|
||||
ASSERT(query)
|
||||
query->bindValue(1, path);
|
||||
ASSERT(query->exec())
|
||||
@ -2288,7 +2288,7 @@ Optional<PinState> SyncJournalDb::PinStateInterface::rawForPath(const QByteArray
|
||||
if (!_db->checkConnect())
|
||||
return {};
|
||||
|
||||
const PreparedSqlQueryRAII query(&_db->_getRawPinStateQuery, QByteArrayLiteral("SELECT pinState FROM flags WHERE path == ?1;"), _db->_db);
|
||||
const auto query = _db->_queryManager.get(PreparedSqlQueryManager::GetRawPinStateQuery, QByteArrayLiteral("SELECT pinState FROM flags WHERE path == ?1;"), _db->_db);
|
||||
ASSERT(query)
|
||||
query->bindValue(1, path);
|
||||
query->exec();
|
||||
@ -2309,14 +2309,13 @@ Optional<PinState> SyncJournalDb::PinStateInterface::effectiveForPath(const QByt
|
||||
if (!_db->checkConnect())
|
||||
return {};
|
||||
|
||||
const PreparedSqlQueryRAII query(&_db->_getEffectivePinStateQuery, QByteArrayLiteral(
|
||||
"SELECT pinState FROM flags WHERE"
|
||||
// explicitly allow "" to represent the root path
|
||||
// (it'd be great if paths started with a / and "/" could be the root)
|
||||
" (" IS_PREFIX_PATH_OR_EQUAL("path", "?1") " OR path == '')"
|
||||
" AND pinState is not null AND pinState != 0"
|
||||
" ORDER BY length(path) DESC LIMIT 1;"),
|
||||
_db->_db);
|
||||
const auto query = _db->_queryManager.get(PreparedSqlQueryManager::GetEffectivePinStateQuery, QByteArrayLiteral("SELECT pinState FROM flags WHERE"
|
||||
// explicitly allow "" to represent the root path
|
||||
// (it'd be great if paths started with a / and "/" could be the root)
|
||||
" (" IS_PREFIX_PATH_OR_EQUAL("path", "?1") " OR path == '')"
|
||||
" AND pinState is not null AND pinState != 0"
|
||||
" ORDER BY length(path) DESC LIMIT 1;"),
|
||||
_db->_db);
|
||||
ASSERT(query)
|
||||
query->bindValue(1, path);
|
||||
query->exec();
|
||||
@ -2344,10 +2343,10 @@ Optional<PinState> SyncJournalDb::PinStateInterface::effectiveForPathRecursive(c
|
||||
return {};
|
||||
|
||||
// Find all the non-inherited pin states below the item
|
||||
const PreparedSqlQueryRAII query(&_db->_getSubPinsQuery, QByteArrayLiteral("SELECT DISTINCT pinState FROM flags WHERE"
|
||||
" (" IS_PREFIX_PATH_OF("?1", "path") " OR ?1 == '')"
|
||||
" AND pinState is not null and pinState != 0;"),
|
||||
_db->_db);
|
||||
const auto query = _db->_queryManager.get(PreparedSqlQueryManager::GetSubPinsQuery, QByteArrayLiteral("SELECT DISTINCT pinState FROM flags WHERE"
|
||||
" (" IS_PREFIX_PATH_OF("?1", "path") " OR ?1 == '')"
|
||||
" AND pinState is not null and pinState != 0;"),
|
||||
_db->_db);
|
||||
ASSERT(query)
|
||||
query->bindValue(1, path);
|
||||
query->exec();
|
||||
@ -2373,13 +2372,13 @@ void SyncJournalDb::PinStateInterface::setForPath(const QByteArray &path, PinSta
|
||||
if (!_db->checkConnect())
|
||||
return;
|
||||
|
||||
const PreparedSqlQueryRAII query(&_db->_setPinStateQuery, QByteArrayLiteral(
|
||||
// If we had sqlite >=3.24.0 everywhere this could be an upsert,
|
||||
// making further flags columns easy
|
||||
//"INSERT INTO flags(path, pinState) VALUES(?1, ?2)"
|
||||
//" ON CONFLICT(path) DO UPDATE SET pinState=?2;"),
|
||||
// Simple version that doesn't work nicely with multiple columns:
|
||||
"INSERT OR REPLACE INTO flags(path, pinState) VALUES(?1, ?2);"),
|
||||
const auto query = _db->_queryManager.get(PreparedSqlQueryManager::SetPinStateQuery, QByteArrayLiteral(
|
||||
// If we had sqlite >=3.24.0 everywhere this could be an upsert,
|
||||
// making further flags columns easy
|
||||
//"INSERT INTO flags(path, pinState) VALUES(?1, ?2)"
|
||||
//" ON CONFLICT(path) DO UPDATE SET pinState=?2;"),
|
||||
// Simple version that doesn't work nicely with multiple columns:
|
||||
"INSERT OR REPLACE INTO flags(path, pinState) VALUES(?1, ?2);"),
|
||||
_db->_db);
|
||||
ASSERT(query)
|
||||
query->bindValue(1, path);
|
||||
@ -2393,10 +2392,10 @@ void SyncJournalDb::PinStateInterface::wipeForPathAndBelow(const QByteArray &pat
|
||||
if (!_db->checkConnect())
|
||||
return;
|
||||
|
||||
const PreparedSqlQueryRAII query(&_db->_wipePinStateQuery, QByteArrayLiteral("DELETE FROM flags WHERE "
|
||||
// Allow "" to delete everything
|
||||
" (" IS_PREFIX_PATH_OR_EQUAL("?1", "path") " OR ?1 == '');"),
|
||||
_db->_db);
|
||||
const auto query = _db->_queryManager.get(PreparedSqlQueryManager::WipePinStateQuery, QByteArrayLiteral("DELETE FROM flags WHERE "
|
||||
// Allow "" to delete everything
|
||||
" (" IS_PREFIX_PATH_OR_EQUAL("?1", "path") " OR ?1 == '');"),
|
||||
_db->_db);
|
||||
ASSERT(query)
|
||||
query->bindValue(1, path);
|
||||
query->exec();
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
|
||||
#include "common/utility.h"
|
||||
#include "common/ownsql.h"
|
||||
#include "common/preparedsqlquerymanager.h"
|
||||
#include "common/syncjournalfilerecord.h"
|
||||
#include "common/result.h"
|
||||
#include "common/pinstate.h"
|
||||
@ -397,46 +398,6 @@ private:
|
||||
int _transaction;
|
||||
bool _metadataTableIsEmpty;
|
||||
|
||||
SqlQuery _getFileRecordQuery;
|
||||
SqlQuery _getFileRecordQueryByMangledName;
|
||||
SqlQuery _getFileRecordQueryByInode;
|
||||
SqlQuery _getFileRecordQueryByFileId;
|
||||
SqlQuery _getFilesBelowPathQuery;
|
||||
SqlQuery _getAllFilesQuery;
|
||||
SqlQuery _listFilesInPathQuery;
|
||||
SqlQuery _setFileRecordQuery;
|
||||
SqlQuery _setFileRecordChecksumQuery;
|
||||
SqlQuery _setFileRecordLocalMetadataQuery;
|
||||
SqlQuery _getDownloadInfoQuery;
|
||||
SqlQuery _setDownloadInfoQuery;
|
||||
SqlQuery _deleteDownloadInfoQuery;
|
||||
SqlQuery _getUploadInfoQuery;
|
||||
SqlQuery _setUploadInfoQuery;
|
||||
SqlQuery _deleteUploadInfoQuery;
|
||||
SqlQuery _deleteFileRecordPhash;
|
||||
SqlQuery _deleteFileRecordRecursively;
|
||||
SqlQuery _getErrorBlacklistQuery;
|
||||
SqlQuery _setErrorBlacklistQuery;
|
||||
SqlQuery _getSelectiveSyncListQuery;
|
||||
SqlQuery _getChecksumTypeIdQuery;
|
||||
SqlQuery _getChecksumTypeQuery;
|
||||
SqlQuery _insertChecksumTypeQuery;
|
||||
SqlQuery _getDataFingerprintQuery;
|
||||
SqlQuery _setDataFingerprintQuery1;
|
||||
SqlQuery _setDataFingerprintQuery2;
|
||||
SqlQuery _setKeyValueStoreQuery;
|
||||
SqlQuery _getKeyValueStoreQuery;
|
||||
SqlQuery _deleteKeyValueStoreQuery;
|
||||
SqlQuery _getConflictRecordQuery;
|
||||
SqlQuery _setConflictRecordQuery;
|
||||
SqlQuery _deleteConflictRecordQuery;
|
||||
SqlQuery _getRawPinStateQuery;
|
||||
SqlQuery _getEffectivePinStateQuery;
|
||||
SqlQuery _getSubPinsQuery;
|
||||
SqlQuery _countDehydratedFilesQuery;
|
||||
SqlQuery _setPinStateQuery;
|
||||
SqlQuery _wipePinStateQuery;
|
||||
|
||||
/* Storing etags to these folders, or their parent folders, is filtered out.
|
||||
*
|
||||
* When schedulePathForRemoteDiscovery() is called some etags to _invalid_ in the
|
||||
@ -458,6 +419,8 @@ private:
|
||||
* variable, for specific filesystems, or when WAL fails in a particular way.
|
||||
*/
|
||||
QByteArray _journalMode;
|
||||
|
||||
PreparedSqlQueryManager _queryManager;
|
||||
};
|
||||
|
||||
bool OCSYNC_EXPORT
|
||||
|
||||
@ -18,7 +18,9 @@
|
||||
#include <libcrashreporter-gui/CrashReporter.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
#include <QFileInfo>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
@ -52,6 +54,14 @@ int main(int argc, char *argv[])
|
||||
reporter.setWindowTitle(CRASHREPORTER_PRODUCT_NAME);
|
||||
reporter.setText("<html><head/><body><p><span style=\" font-weight:600;\">Sorry!</span> " CRASHREPORTER_PRODUCT_NAME " crashed. Please tell us about it! " CRASHREPORTER_PRODUCT_NAME " has created an error report for you that can help improve the stability in the future. You can now send this report directly to the " CRASHREPORTER_PRODUCT_NAME " developers.</p></body></html>");
|
||||
|
||||
const QFileInfo crashLog(QDir::tempPath() + QStringLiteral("/" CRASHREPORTER_PRODUCT_NAME "-crash.log"));
|
||||
if (crashLog.exists()) {
|
||||
QFile inFile(crashLog.filePath());
|
||||
if (inFile.open(QFile::ReadOnly)) {
|
||||
reporter.setComment(inFile.readAll());
|
||||
}
|
||||
}
|
||||
|
||||
reporter.setReportData("BuildID", CRASHREPORTER_BUILD_ID);
|
||||
reporter.setReportData("ProductName", CRASHREPORTER_PRODUCT_NAME);
|
||||
reporter.setReportData("Version", CRASHREPORTER_VERSION_STRING);
|
||||
|
||||
@ -199,13 +199,13 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(const QString &path, bool exclu
|
||||
}
|
||||
#endif
|
||||
|
||||
/* We create a Desktop.ini on Windows for the sidebar icon, make sure we don't sync it. */
|
||||
if (blen == 11 && path == bname) {
|
||||
if (bname.compare(QLatin1String("Desktop.ini"), Qt::CaseInsensitive) == 0) {
|
||||
return CSYNC_FILE_SILENTLY_EXCLUDED;
|
||||
}
|
||||
/* Do not sync desktop.ini files anywhere in the tree. */
|
||||
const auto desktopIniFile = QStringLiteral("desktop.ini");
|
||||
if (blen == static_cast<size_t>(desktopIniFile.length()) && bname.compare(desktopIniFile, Qt::CaseInsensitive) == 0) {
|
||||
return CSYNC_FILE_SILENTLY_EXCLUDED;
|
||||
}
|
||||
|
||||
|
||||
if (excludeConflictFiles && OCC::Utility::isConflictFile(path)) {
|
||||
return CSYNC_FILE_EXCLUDE_CONFLICT;
|
||||
}
|
||||
|
||||
@ -34,6 +34,7 @@
|
||||
#include "csync.h"
|
||||
#include "vio/csync_vio_local.h"
|
||||
#include "common/filesystembase.h"
|
||||
#include "common/utility.h"
|
||||
|
||||
#include <QtCore/QLoggingCategory>
|
||||
|
||||
@ -52,8 +53,6 @@ struct csync_vio_handle_t {
|
||||
QString path; // Always ends with '\'
|
||||
};
|
||||
|
||||
static int _csync_vio_local_stat_mb(const QString &path, csync_file_stat_t *buf);
|
||||
|
||||
csync_vio_handle_t *csync_vio_local_opendir(const QString &name) {
|
||||
|
||||
QScopedPointer<csync_vio_handle_t> handle(new csync_vio_handle_t{});
|
||||
@ -175,12 +174,9 @@ std::unique_ptr<csync_file_stat_t> csync_vio_local_readdir(csync_vio_handle_t *h
|
||||
file_stat->size = (handle->ffd.nFileSizeHigh * ((int64_t)(MAXDWORD)+1)) + handle->ffd.nFileSizeLow;
|
||||
file_stat->modtime = FileTimeToUnixTime(&handle->ffd.ftLastWriteTime, &rem);
|
||||
|
||||
QString fullPath;
|
||||
fullPath.reserve(handle->path.size() + std::wcslen(handle->ffd.cFileName));
|
||||
fullPath += handle->path; // path always ends with '\', by construction
|
||||
fullPath += QString::fromWCharArray(handle->ffd.cFileName);
|
||||
// path always ends with '\', by construction
|
||||
|
||||
if (_csync_vio_local_stat_mb(fullPath, file_stat.get()) < 0) {
|
||||
if (csync_vio_local_stat(handle->path + QString::fromWCharArray(handle->ffd.cFileName), file_stat.get()) < 0) {
|
||||
// Will get excluded by _csync_detect_update.
|
||||
file_stat->type = ItemTypeSkip;
|
||||
}
|
||||
@ -188,14 +184,7 @@ std::unique_ptr<csync_file_stat_t> csync_vio_local_readdir(csync_vio_handle_t *h
|
||||
return file_stat;
|
||||
}
|
||||
|
||||
|
||||
int csync_vio_local_stat(const QString &uri, csync_file_stat_t *buf)
|
||||
{
|
||||
int rc = _csync_vio_local_stat_mb(uri, buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int _csync_vio_local_stat_mb(const QString &path, csync_file_stat_t *buf)
|
||||
{
|
||||
/* Almost nothing to do since csync_vio_local_readdir already filled up most of the information
|
||||
But we still need to fetch the file ID.
|
||||
@ -206,21 +195,19 @@ static int _csync_vio_local_stat_mb(const QString &path, csync_file_stat_t *buf)
|
||||
BY_HANDLE_FILE_INFORMATION fileInfo;
|
||||
ULARGE_INTEGER FileIndex;
|
||||
|
||||
const auto longPath = OCC::FileSystem::longWinPath(path);
|
||||
|
||||
h = CreateFileW(longPath.toStdWString().data(), 0, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
|
||||
nullptr, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
|
||||
nullptr );
|
||||
h = CreateFileW(reinterpret_cast<const wchar_t *>(OCC::FileSystem::longWinPath(uri).utf16()), 0, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
|
||||
NULL, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
|
||||
NULL);
|
||||
if( h == INVALID_HANDLE_VALUE ) {
|
||||
qCCritical(lcCSyncVIOLocal) << "CreateFileW failed on" << longPath;
|
||||
errno = GetLastError();
|
||||
qCCritical(lcCSyncVIOLocal) << "CreateFileW failed on" << uri << OCC::Utility::formatWinError(errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!GetFileInformationByHandle( h, &fileInfo ) ) {
|
||||
qCCritical(lcCSyncVIOLocal) << "GetFileInformationByHandle failed on" << longPath;
|
||||
errno = GetLastError();
|
||||
qCCritical(lcCSyncVIOLocal) << "GetFileInformationByHandle failed on" << uri << OCC::Utility::formatWinError(errno);
|
||||
CloseHandle(h);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -87,7 +87,6 @@ set(client_SRCS
|
||||
sharemanager.cpp
|
||||
shareusergroupwidget.cpp
|
||||
sharee.cpp
|
||||
socketapi.cpp
|
||||
sslbutton.cpp
|
||||
sslerrordialog.cpp
|
||||
syncrunfilelog.cpp
|
||||
@ -338,6 +337,8 @@ target_link_libraries(nextcloudCore
|
||||
Qt5::QuickControls2
|
||||
)
|
||||
|
||||
add_subdirectory(socketapi)
|
||||
|
||||
if(Qt5WebEngine_FOUND AND Qt5WebEngineWidgets_FOUND)
|
||||
target_link_libraries(nextcloudCore PUBLIC Qt5::WebEngineWidgets)
|
||||
endif()
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
#include "folderman.h"
|
||||
#include "logger.h"
|
||||
#include "configfile.h"
|
||||
#include "socketapi.h"
|
||||
#include "socketapi/socketapi.h"
|
||||
#include "sslerrordialog.h"
|
||||
#include "theme.h"
|
||||
#include "clientproxy.h"
|
||||
@ -524,7 +524,12 @@ void Application::setupLogging()
|
||||
|
||||
logger->enterNextLogFile();
|
||||
|
||||
qCInfo(lcApplication) << QString::fromLatin1("################## %1 locale:[%2] ui_lang:[%3] version:[%4] os:[%5]").arg(_theme->appName()).arg(QLocale::system().name()).arg(property("ui_lang").toString()).arg(_theme->version()).arg(Utility::platformName());
|
||||
qCInfo(lcApplication) << "##################" << _theme->appName()
|
||||
<< "locale:" << QLocale::system().name()
|
||||
<< "ui_lang:" << property("ui_lang")
|
||||
<< "version:" << _theme->version()
|
||||
<< "os:" << Utility::platformName();
|
||||
qCInfo(lcApplication) << "Arguments:" << qApp->arguments();
|
||||
}
|
||||
|
||||
void Application::slotUseMonoIconsChanged(bool)
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
#include "clientproxy.h"
|
||||
#include "syncengine.h"
|
||||
#include "syncrunfilelog.h"
|
||||
#include "socketapi.h"
|
||||
#include "socketapi/socketapi.h"
|
||||
#include "theme.h"
|
||||
#include "filesystem.h"
|
||||
#include "localdiscoverytracker.h"
|
||||
@ -353,7 +353,7 @@ void Folder::slotRunEtagJob()
|
||||
// The _requestEtagJob is auto deleting itself on finish. Our guard pointer _requestEtagJob will then be null.
|
||||
}
|
||||
|
||||
void Folder::etagRetrieved(const QString &etag, const QDateTime &tp)
|
||||
void Folder::etagRetrieved(const QByteArray &etag, const QDateTime &tp)
|
||||
{
|
||||
// re-enable sync if it was disabled because network was down
|
||||
FolderMan::instance()->setSyncEnabled(true);
|
||||
@ -367,7 +367,7 @@ void Folder::etagRetrieved(const QString &etag, const QDateTime &tp)
|
||||
_accountState->tagLastSuccessfullETagRequest(tp);
|
||||
}
|
||||
|
||||
void Folder::etagRetrievedFromSyncEngine(const QString &etag, const QDateTime &time)
|
||||
void Folder::etagRetrievedFromSyncEngine(const QByteArray &etag, const QDateTime &time)
|
||||
{
|
||||
qCInfo(lcFolder) << "Root etag from during sync:" << etag;
|
||||
accountState()->tagLastSuccessfullETagRequest(time);
|
||||
@ -874,42 +874,15 @@ void Folder::setSyncOptions()
|
||||
opt._confirmExternalStorage = cfgFile.confirmExternalStorage();
|
||||
opt._moveFilesToTrash = cfgFile.moveToTrash();
|
||||
opt._vfs = _vfs;
|
||||
opt._parallelNetworkJobs = _accountState->account()->isHttp2Supported() ? 20 : 6;
|
||||
|
||||
QByteArray chunkSizeEnv = qgetenv("OWNCLOUD_CHUNK_SIZE");
|
||||
if (!chunkSizeEnv.isEmpty()) {
|
||||
opt._initialChunkSize = chunkSizeEnv.toUInt();
|
||||
} else {
|
||||
opt._initialChunkSize = cfgFile.chunkSize();
|
||||
}
|
||||
QByteArray minChunkSizeEnv = qgetenv("OWNCLOUD_MIN_CHUNK_SIZE");
|
||||
if (!minChunkSizeEnv.isEmpty()) {
|
||||
opt._minChunkSize = minChunkSizeEnv.toUInt();
|
||||
} else {
|
||||
opt._minChunkSize = cfgFile.minChunkSize();
|
||||
}
|
||||
QByteArray maxChunkSizeEnv = qgetenv("OWNCLOUD_MAX_CHUNK_SIZE");
|
||||
if (!maxChunkSizeEnv.isEmpty()) {
|
||||
opt._maxChunkSize = maxChunkSizeEnv.toUInt();
|
||||
} else {
|
||||
opt._maxChunkSize = cfgFile.maxChunkSize();
|
||||
}
|
||||
opt._initialChunkSize = cfgFile.chunkSize();
|
||||
opt._minChunkSize = cfgFile.minChunkSize();
|
||||
opt._maxChunkSize = cfgFile.maxChunkSize();
|
||||
opt._targetChunkUploadDuration = cfgFile.targetChunkUploadDuration();
|
||||
|
||||
int maxParallel = qgetenv("OWNCLOUD_MAX_PARALLEL").toUInt();
|
||||
opt._parallelNetworkJobs = maxParallel ? maxParallel : _accountState->account()->isHttp2Supported() ? 20 : 6;
|
||||
|
||||
// Previously min/max chunk size values didn't exist, so users might
|
||||
// have setups where the chunk size exceeds the new min/max default
|
||||
// values. To cope with this, adjust min/max to always include the
|
||||
// initial chunk size value.
|
||||
opt._minChunkSize = qMin(opt._minChunkSize, opt._initialChunkSize);
|
||||
opt._maxChunkSize = qMax(opt._maxChunkSize, opt._initialChunkSize);
|
||||
|
||||
QByteArray targetChunkUploadDurationEnv = qgetenv("OWNCLOUD_TARGET_CHUNK_UPLOAD_DURATION");
|
||||
if (!targetChunkUploadDurationEnv.isEmpty()) {
|
||||
opt._targetChunkUploadDuration = std::chrono::milliseconds(targetChunkUploadDurationEnv.toUInt());
|
||||
} else {
|
||||
opt._targetChunkUploadDuration = cfgFile.targetChunkUploadDuration();
|
||||
}
|
||||
opt.fillFromEnvironmentVariables();
|
||||
opt.verifyChunkSizes();
|
||||
|
||||
_engine->setSyncOptions(opt);
|
||||
}
|
||||
|
||||
@ -376,8 +376,8 @@ private slots:
|
||||
void slotItemCompleted(const SyncFileItemPtr &);
|
||||
|
||||
void slotRunEtagJob();
|
||||
void etagRetrieved(const QString &, const QDateTime &tp);
|
||||
void etagRetrievedFromSyncEngine(const QString &, const QDateTime &time);
|
||||
void etagRetrieved(const QByteArray &, const QDateTime &tp);
|
||||
void etagRetrievedFromSyncEngine(const QByteArray &, const QDateTime &time);
|
||||
|
||||
void slotEmitFinishedDelayed();
|
||||
|
||||
@ -447,7 +447,7 @@ private:
|
||||
SyncResult _syncResult;
|
||||
QScopedPointer<SyncEngine> _engine;
|
||||
QPointer<RequestEtagJob> _requestEtagJob;
|
||||
QString _lastEtag;
|
||||
QByteArray _lastEtag;
|
||||
QElapsedTimer _timeSinceLastSyncDone;
|
||||
QElapsedTimer _timeSinceLastSyncStart;
|
||||
QElapsedTimer _timeSinceLastFullLocalDiscovery;
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
#include "folder.h"
|
||||
#include "syncresult.h"
|
||||
#include "theme.h"
|
||||
#include "socketapi.h"
|
||||
#include "socketapi/socketapi.h"
|
||||
#include "account.h"
|
||||
#include "accountstate.h"
|
||||
#include "accountmanager.h"
|
||||
|
||||
@ -201,21 +201,19 @@ void FolderWizardRemotePath::slotCreateRemoteFolder(const QString &folder)
|
||||
|
||||
auto *job = new MkColJob(_account, fullPath, this);
|
||||
/* check the owncloud configuration file and query the ownCloud */
|
||||
connect(job, static_cast<void (MkColJob::*)(QNetworkReply::NetworkError)>(&MkColJob::finished),
|
||||
connect(job, &MkColJob::finishedWithoutError,
|
||||
this, &FolderWizardRemotePath::slotCreateRemoteFolderFinished);
|
||||
connect(job, &AbstractNetworkJob::networkError, this, &FolderWizardRemotePath::slotHandleMkdirNetworkError);
|
||||
job->start();
|
||||
}
|
||||
|
||||
void FolderWizardRemotePath::slotCreateRemoteFolderFinished(QNetworkReply::NetworkError error)
|
||||
void FolderWizardRemotePath::slotCreateRemoteFolderFinished()
|
||||
{
|
||||
if (error == QNetworkReply::NoError) {
|
||||
qCDebug(lcWizard) << "webdav mkdir request finished";
|
||||
showWarn(tr("Folder was successfully created on %1.").arg(Theme::instance()->appNameGUI()));
|
||||
slotRefreshFolders();
|
||||
_ui.folderEntry->setText(static_cast<MkColJob *>(sender())->path());
|
||||
slotLsColFolderEntry();
|
||||
}
|
||||
qCDebug(lcWizard) << "webdav mkdir request finished";
|
||||
showWarn(tr("Folder was successfully created on %1.").arg(Theme::instance()->appNameGUI()));
|
||||
slotRefreshFolders();
|
||||
_ui.folderEntry->setText(static_cast<MkColJob *>(sender())->path());
|
||||
slotLsColFolderEntry();
|
||||
}
|
||||
|
||||
void FolderWizardRemotePath::slotHandleMkdirNetworkError(QNetworkReply *reply)
|
||||
|
||||
@ -92,7 +92,7 @@ protected slots:
|
||||
void showWarn(const QString & = QString()) const;
|
||||
void slotAddRemoteFolder();
|
||||
void slotCreateRemoteFolder(const QString &);
|
||||
void slotCreateRemoteFolderFinished(QNetworkReply::NetworkError error);
|
||||
void slotCreateRemoteFolderFinished();
|
||||
void slotHandleMkdirNetworkError(QNetworkReply *);
|
||||
void slotHandleLsColNetworkError(QNetworkReply *);
|
||||
void slotUpdateDirectories(const QStringList &);
|
||||
|
||||
@ -536,26 +536,28 @@ void OwncloudSetupWizard::createRemoteFolder()
|
||||
_ocWizard->appendToConfigurationLog(tr("creating folder on Nextcloud: %1").arg(_remoteFolder));
|
||||
|
||||
auto *job = new MkColJob(_ocWizard->account(), _remoteFolder, this);
|
||||
connect(job, SIGNAL(finished(QNetworkReply::NetworkError)), SLOT(slotCreateRemoteFolderFinished(QNetworkReply::NetworkError)));
|
||||
connect(job, &MkColJob::finishedWithError, this, &OwncloudSetupWizard::slotCreateRemoteFolderFinished);
|
||||
connect(job, &MkColJob::finishedWithoutError, this, [this] {
|
||||
_ocWizard->appendToConfigurationLog(tr("Remote folder %1 created successfully.").arg(_remoteFolder));
|
||||
finalizeSetup(true);
|
||||
});
|
||||
job->start();
|
||||
}
|
||||
|
||||
void OwncloudSetupWizard::slotCreateRemoteFolderFinished(QNetworkReply::NetworkError error)
|
||||
void OwncloudSetupWizard::slotCreateRemoteFolderFinished(QNetworkReply *reply)
|
||||
{
|
||||
auto error = reply->error();
|
||||
qCDebug(lcWizard) << "** webdav mkdir request finished " << error;
|
||||
// disconnect(ownCloudInfo::instance(), SIGNAL(webdavColCreated(QNetworkReply::NetworkError)),
|
||||
// this, SLOT(slotCreateRemoteFolderFinished(QNetworkReply::NetworkError)));
|
||||
|
||||
bool success = true;
|
||||
|
||||
if (error == QNetworkReply::NoError) {
|
||||
_ocWizard->appendToConfigurationLog(tr("Remote folder %1 created successfully.").arg(_remoteFolder));
|
||||
} else if (error == 202) {
|
||||
if (error == 202) {
|
||||
_ocWizard->appendToConfigurationLog(tr("The remote folder %1 already exists. Connecting it for syncing.").arg(_remoteFolder));
|
||||
} else if (error > 202 && error < 300) {
|
||||
_ocWizard->displayError(tr("The folder creation resulted in HTTP error code %1").arg((int)error), false);
|
||||
_ocWizard->displayError(tr("The folder creation resulted in HTTP error code %1").arg(static_cast<int>(error)), false);
|
||||
|
||||
_ocWizard->appendToConfigurationLog(tr("The folder creation resulted in HTTP error code %1").arg((int)error));
|
||||
_ocWizard->appendToConfigurationLog(tr("The folder creation resulted in HTTP error code %1").arg(static_cast<int>(error)));
|
||||
} else if (error == QNetworkReply::OperationCanceledError) {
|
||||
_ocWizard->displayError(tr("The remote folder creation failed because the provided credentials "
|
||||
"are wrong!"
|
||||
|
||||
@ -65,7 +65,7 @@ private slots:
|
||||
|
||||
void slotCreateLocalAndRemoteFolders(const QString &, const QString &);
|
||||
void slotRemoteFolderExists(QNetworkReply *);
|
||||
void slotCreateRemoteFolderFinished(QNetworkReply::NetworkError);
|
||||
void slotCreateRemoteFolderFinished(QNetworkReply *reply);
|
||||
void slotAssistantFinished(int);
|
||||
void slotSkipFolderConfiguration();
|
||||
|
||||
|
||||
8
src/gui/socketapi/CMakeLists.txt
Normal file
8
src/gui/socketapi/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
||||
target_sources(nextcloudCore PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/socketapi.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/socketuploadjob.cpp
|
||||
)
|
||||
|
||||
if( APPLE )
|
||||
target_sources(nextcloudCore PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/socketapisocket_mac.mm)
|
||||
endif()
|
||||
@ -16,9 +16,11 @@
|
||||
|
||||
#include "socketapi.h"
|
||||
#include "socketapi_p.h"
|
||||
#include "socketapi/socketuploadjob.h"
|
||||
|
||||
#include "conflictdialog.h"
|
||||
#include "conflictsolver.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "configfile.h"
|
||||
#include "folderman.h"
|
||||
@ -32,6 +34,7 @@
|
||||
#include "account.h"
|
||||
#include "accountstate.h"
|
||||
#include "account.h"
|
||||
#include "accountmanager.h"
|
||||
#include "capabilities.h"
|
||||
#include "common/asserts.h"
|
||||
#include "guiutility.h"
|
||||
@ -57,6 +60,7 @@
|
||||
|
||||
|
||||
#include <QAction>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QWidget>
|
||||
@ -78,12 +82,25 @@
|
||||
#define MIRALL_SOCKET_API_VERSION "1.1"
|
||||
|
||||
namespace {
|
||||
|
||||
const QLatin1Char RecordSeparator()
|
||||
{
|
||||
return QLatin1Char('\x1e');
|
||||
}
|
||||
|
||||
QStringList split(const QString &data)
|
||||
{
|
||||
// TODO: string ref?
|
||||
return data.split(RecordSeparator());
|
||||
}
|
||||
|
||||
#if GUI_TESTING
|
||||
|
||||
using namespace OCC;
|
||||
|
||||
QList<QObject*> allObjects(const QList<QWidget*> &widgets) {
|
||||
QList<QObject*> objects;
|
||||
QList<QObject *> allObjects(const QList<QWidget *> &widgets)
|
||||
{
|
||||
QList<QObject *> objects;
|
||||
std::copy(widgets.constBegin(), widgets.constEnd(), std::back_inserter(objects));
|
||||
|
||||
objects << qApp;
|
||||
@ -91,11 +108,11 @@ QList<QObject*> allObjects(const QList<QWidget*> &widgets) {
|
||||
return objects;
|
||||
}
|
||||
|
||||
QObject *findWidget(const QString &queryString, const QList<QWidget*> &widgets = QApplication::allWidgets())
|
||||
QObject *findWidget(const QString &queryString, const QList<QWidget *> &widgets = QApplication::allWidgets())
|
||||
{
|
||||
auto objects = allObjects(widgets);
|
||||
|
||||
QList<QObject*>::const_iterator foundWidget;
|
||||
QList<QObject *>::const_iterator foundWidget;
|
||||
|
||||
if (queryString.contains('>')) {
|
||||
qCDebug(lcSocketApi) << "queryString contains >";
|
||||
@ -107,33 +124,34 @@ QObject *findWidget(const QString &queryString, const QList<QWidget*> &widgets =
|
||||
qCDebug(lcSocketApi) << "Find parent: " << parentQueryString;
|
||||
auto parent = findWidget(parentQueryString);
|
||||
|
||||
if(!parent) {
|
||||
if (!parent) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto childQueryString = subQueries[1].trimmed();
|
||||
auto child = findWidget(childQueryString, parent->findChildren<QWidget*>());
|
||||
auto child = findWidget(childQueryString, parent->findChildren<QWidget *>());
|
||||
qCDebug(lcSocketApi) << "found child: " << !!child;
|
||||
return child;
|
||||
|
||||
} else if(queryString.startsWith('#')) {
|
||||
} else if (queryString.startsWith('#')) {
|
||||
auto objectName = queryString.mid(1);
|
||||
qCDebug(lcSocketApi) << "find objectName: " << objectName;
|
||||
foundWidget = std::find_if(objects.constBegin(), objects.constEnd(), [&](QObject *widget) {
|
||||
return widget->objectName() == objectName;
|
||||
});
|
||||
} else {
|
||||
QList<QObject*> matches;
|
||||
std::copy_if(objects.constBegin(), objects.constEnd(), std::back_inserter(matches), [&](QObject* widget) {
|
||||
QList<QObject *> matches;
|
||||
std::copy_if(objects.constBegin(), objects.constEnd(), std::back_inserter(matches), [&](QObject *widget) {
|
||||
return widget->inherits(queryString.toLatin1());
|
||||
});
|
||||
|
||||
std::for_each(matches.constBegin(), matches.constEnd(), [](QObject* w) {
|
||||
if(!w) return;
|
||||
std::for_each(matches.constBegin(), matches.constEnd(), [](QObject *w) {
|
||||
if (!w)
|
||||
return;
|
||||
qCDebug(lcSocketApi) << "WIDGET: " << w->objectName() << w->metaObject()->className();
|
||||
});
|
||||
|
||||
if(matches.empty()) {
|
||||
if (matches.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
return matches[0];
|
||||
@ -200,16 +218,6 @@ void SocketListener::sendMessage(const QString &message, bool doWait) const
|
||||
}
|
||||
}
|
||||
|
||||
struct ListenerHasSocketPred
|
||||
{
|
||||
QIODevice *socket;
|
||||
ListenerHasSocketPred(QIODevice *socket)
|
||||
: socket(socket)
|
||||
{
|
||||
}
|
||||
bool operator()(const SocketListener &listener) const { return listener.socket == socket; }
|
||||
};
|
||||
|
||||
SocketApi::SocketApi(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
@ -217,6 +225,7 @@ SocketApi::SocketApi(QObject *parent)
|
||||
|
||||
qRegisterMetaType<SocketListener *>("SocketListener*");
|
||||
qRegisterMetaType<QSharedPointer<SocketApiJob>>("QSharedPointer<SocketApiJob>");
|
||||
qRegisterMetaType<QSharedPointer<SocketApiJobV2>>("QSharedPointer<SocketApiJobV2>");
|
||||
|
||||
if (Utility::isWindows()) {
|
||||
socketPath = QLatin1String(R"(\\.\pipe\)")
|
||||
@ -236,21 +245,20 @@ SocketApi::SocketApi(QObject *parent)
|
||||
CFURLRef url = (CFURLRef)CFAutorelease((CFURLRef)CFBundleCopyBundleURL(CFBundleGetMainBundle()));
|
||||
QString bundlePath = QUrl::fromCFURL(url).path();
|
||||
|
||||
auto _system = [](const QString &cmd, const QStringList &args){
|
||||
auto _system = [](const QString &cmd, const QStringList &args) {
|
||||
QProcess process;
|
||||
process.setProcessChannelMode(QProcess::MergedChannels);
|
||||
process.start(cmd, args);
|
||||
if (!process.waitForFinished())
|
||||
{
|
||||
if (!process.waitForFinished()) {
|
||||
qCWarning(lcSocketApi) << "Failed to load shell extension:" << cmd << args.join(" ") << process.errorString();
|
||||
} else {
|
||||
qCInfo(lcSocketApi) << (process.exitCode() != 0 ? "Failed to load" : "Loaded") << "shell extension:" << cmd << args.join(" ") << process.readAll();
|
||||
qCInfo(lcSocketApi) << (process.exitCode() != 0 ? "Failed to load" : "Loaded") << "shell extension:" << cmd << args.join(" ") << process.readAll();
|
||||
}
|
||||
};
|
||||
// Add it again. This was needed for Mojave to trigger a load.
|
||||
_system(QStringLiteral("pluginkit"), {QStringLiteral("-a"),QStringLiteral("%1Contents/PlugIns/FinderSyncExt.appex/").arg(bundlePath)});
|
||||
_system(QStringLiteral("pluginkit"), { QStringLiteral("-a"), QStringLiteral("%1Contents/PlugIns/FinderSyncExt.appex/").arg(bundlePath) });
|
||||
// Tell Finder to use the Extension (checking it from System Preferences -> Extensions)
|
||||
_system(QStringLiteral("pluginkit"), {QStringLiteral("-e"), QStringLiteral("use"), QStringLiteral("-i"), QStringLiteral(APPLICATION_REV_DOMAIN ".FinderSyncExt")});
|
||||
_system(QStringLiteral("pluginkit"), { QStringLiteral("-e"), QStringLiteral("use"), QStringLiteral("-i"), QStringLiteral(APPLICATION_REV_DOMAIN ".FinderSyncExt") });
|
||||
|
||||
#endif
|
||||
} else if (Utility::isLinux() || Utility::isBSD()) {
|
||||
@ -288,7 +296,7 @@ SocketApi::~SocketApi()
|
||||
qCDebug(lcSocketApi) << "dtor";
|
||||
_localServer.close();
|
||||
// All remaining sockets will be destroyed with _localServer, their parent
|
||||
ASSERT(_listeners.isEmpty() || _listeners.first().socket->parent() == &_localServer);
|
||||
ASSERT(_listeners.isEmpty() || _listeners.first()->socket->parent() == &_localServer)
|
||||
_listeners.clear();
|
||||
}
|
||||
|
||||
@ -307,14 +315,13 @@ void SocketApi::slotNewConnection()
|
||||
connect(socket, &QObject::destroyed, this, &SocketApi::slotSocketDestroyed);
|
||||
ASSERT(socket->readAll().isEmpty());
|
||||
|
||||
_listeners.append(SocketListener(socket));
|
||||
SocketListener &listener = _listeners.last();
|
||||
|
||||
foreach (Folder *f, FolderMan::instance()->map()) {
|
||||
auto listener = QSharedPointer<SocketListener>::create(socket);
|
||||
_listeners.insert(socket, listener);
|
||||
for (Folder *f : FolderMan::instance()->map()) {
|
||||
if (f->canSync()) {
|
||||
QString message = buildRegisterPathMessage(removeTrailingSlash(f->path()));
|
||||
qCInfo(lcSocketApi) << "Trying to send SocketAPI Register Path Message -->" << message << "to" << listener.socket;
|
||||
listener.sendMessage(message);
|
||||
qCInfo(lcSocketApi) << "Trying to send SocketAPI Register Path Message -->" << message << "to" << listener->socket;
|
||||
listener->sendMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -326,13 +333,13 @@ void SocketApi::onLostConnection()
|
||||
|
||||
auto socket = qobject_cast<QIODevice *>(sender());
|
||||
ASSERT(socket);
|
||||
_listeners.erase(std::remove_if(_listeners.begin(), _listeners.end(), ListenerHasSocketPred(socket)), _listeners.end());
|
||||
_listeners.remove(socket);
|
||||
}
|
||||
|
||||
void SocketApi::slotSocketDestroyed(QObject *obj)
|
||||
{
|
||||
auto *socket = static_cast<QIODevice *>(obj);
|
||||
_listeners.erase(std::remove_if(_listeners.begin(), _listeners.end(), ListenerHasSocketPred(socket)), _listeners.end());
|
||||
_listeners.remove(socket);
|
||||
}
|
||||
|
||||
void SocketApi::slotReadSocket()
|
||||
@ -346,36 +353,38 @@ void SocketApi::slotReadSocket()
|
||||
// the readyRead() signals are received - in that case there won't be a
|
||||
// valid listener. We execute the handler anyway, but it will work with
|
||||
// a SocketListener that doesn't send any messages.
|
||||
static auto noListener = SocketListener(nullptr);
|
||||
SocketListener *listener = &noListener;
|
||||
auto listenerIt = std::find_if(_listeners.begin(), _listeners.end(), ListenerHasSocketPred(socket));
|
||||
if (listenerIt != _listeners.end()) {
|
||||
listener = &*listenerIt;
|
||||
}
|
||||
|
||||
static auto invalidListener = QSharedPointer<SocketListener>::create(nullptr);
|
||||
const auto listener = _listeners.value(socket, invalidListener);
|
||||
while (socket->canReadLine()) {
|
||||
// Make sure to normalize the input from the socket to
|
||||
// make sure that the path will match, especially on OS X.
|
||||
QString line = QString::fromUtf8(socket->readLine()).normalized(QString::NormalizationForm_C);
|
||||
line.chop(1); // remove the '\n'
|
||||
const QString line = QString::fromUtf8(socket->readLine().trimmed()).normalized(QString::NormalizationForm_C);
|
||||
qCInfo(lcSocketApi) << "Received SocketAPI message <--" << line << "from" << socket;
|
||||
QByteArray command = line.split(":").value(0).toLatin1();
|
||||
const int argPos = line.indexOf(QLatin1Char(':'));
|
||||
const QByteArray command = line.midRef(0, argPos).toUtf8().toUpper();
|
||||
const int indexOfMethod = [&] {
|
||||
QByteArray functionWithArguments = QByteArrayLiteral("command_");
|
||||
if (command.startsWith("ASYNC_")) {
|
||||
functionWithArguments += command + QByteArrayLiteral("(QSharedPointer<SocketApiJob>)");
|
||||
} else if (command.startsWith("V2/")) {
|
||||
functionWithArguments += QByteArrayLiteral("V2_") + command.mid(3) + QByteArrayLiteral("(QSharedPointer<SocketApiJobV2>)");
|
||||
} else {
|
||||
functionWithArguments += command + QByteArrayLiteral("(QString,SocketListener*)");
|
||||
}
|
||||
Q_ASSERT(staticQtMetaObject.normalizedSignature(functionWithArguments) == functionWithArguments);
|
||||
const auto out = staticMetaObject.indexOfMethod(functionWithArguments);
|
||||
if (out == -1) {
|
||||
listener->sendError(QStringLiteral("Function %1 not found").arg(QString::fromUtf8(functionWithArguments)));
|
||||
}
|
||||
ASSERT(out != -1)
|
||||
return out;
|
||||
}();
|
||||
|
||||
QByteArray functionWithArguments = "command_" + command;
|
||||
const auto argument = argPos != -1 ? line.midRef(argPos + 1) : QStringRef();
|
||||
if (command.startsWith("ASYNC_")) {
|
||||
functionWithArguments += "(QSharedPointer<SocketApiJob>)";
|
||||
} else {
|
||||
functionWithArguments += "(QString,SocketListener*)";
|
||||
}
|
||||
|
||||
int indexOfMethod = staticMetaObject.indexOfMethod(functionWithArguments);
|
||||
|
||||
QString argument = line.remove(0, command.length() + 1);
|
||||
if (command.startsWith("ASYNC_")) {
|
||||
|
||||
auto arguments = argument.split('|');
|
||||
if (arguments.size() != 2) {
|
||||
listener->sendMessage(QLatin1String("argument count is wrong"));
|
||||
listener->sendError(QStringLiteral("argument count is wrong"));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -384,25 +393,41 @@ void SocketApi::slotReadSocket()
|
||||
auto jobId = arguments[0];
|
||||
|
||||
auto socketApiJob = QSharedPointer<SocketApiJob>(
|
||||
new SocketApiJob(jobId, listener, json), &QObject::deleteLater);
|
||||
new SocketApiJob(jobId.toString(), listener, json), &QObject::deleteLater);
|
||||
if (indexOfMethod != -1) {
|
||||
staticMetaObject.method(indexOfMethod)
|
||||
.invoke(this, Qt::QueuedConnection,
|
||||
Q_ARG(QSharedPointer<SocketApiJob>, socketApiJob));
|
||||
Q_ARG(QSharedPointer<SocketApiJob>, socketApiJob));
|
||||
} else {
|
||||
qCWarning(lcSocketApi) << "The command is not supported by this version of the client:" << command
|
||||
<< "with argument:" << argument;
|
||||
socketApiJob->reject("command not found");
|
||||
<< "with argument:" << argument;
|
||||
socketApiJob->reject(QStringLiteral("command not found"));
|
||||
}
|
||||
} else if (command.startsWith("V2/")) {
|
||||
QJsonParseError error;
|
||||
const auto json = QJsonDocument::fromJson(argument.toUtf8(), &error).object();
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qCWarning(lcSocketApi()) << "Invalid json" << argument.toString() << error.errorString();
|
||||
listener->sendError(error.errorString());
|
||||
return;
|
||||
}
|
||||
auto socketApiJob = QSharedPointer<SocketApiJobV2>::create(listener, command, json);
|
||||
if (indexOfMethod != -1) {
|
||||
staticMetaObject.method(indexOfMethod)
|
||||
.invoke(this, Qt::QueuedConnection,
|
||||
Q_ARG(QSharedPointer<SocketApiJobV2>, socketApiJob));
|
||||
} else {
|
||||
qCWarning(lcSocketApi) << "The command is not supported by this version of the client:" << command
|
||||
<< "with argument:" << argument;
|
||||
socketApiJob->failure(QStringLiteral("command not found"));
|
||||
}
|
||||
} else {
|
||||
if (indexOfMethod != -1) {
|
||||
// to ensure that listener is still valid we need to call it with Qt::DirectConnection
|
||||
ASSERT(thread() == QThread::currentThread())
|
||||
staticMetaObject.method(indexOfMethod)
|
||||
.invoke(this, Qt::DirectConnection, Q_ARG(QString, argument),
|
||||
Q_ARG(SocketListener *, listener));
|
||||
} else {
|
||||
qCWarning(lcSocketApi) << "The command is not supported by this version of the client:" << command << "with argument:" << argument;
|
||||
.invoke(this, Qt::DirectConnection, Q_ARG(QString, argument.toString()),
|
||||
Q_ARG(SocketListener *, listener.data()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -416,10 +441,10 @@ void SocketApi::slotRegisterPath(const QString &alias)
|
||||
|
||||
Folder *f = FolderMan::instance()->folder(alias);
|
||||
if (f) {
|
||||
QString message = buildRegisterPathMessage(removeTrailingSlash(f->path()));
|
||||
foreach (auto &listener, _listeners) {
|
||||
qCInfo(lcSocketApi) << "Trying to send SocketAPI Register Path Message -->" << message << "to" << listener.socket;
|
||||
listener.sendMessage(message);
|
||||
const QString message = buildRegisterPathMessage(removeTrailingSlash(f->path()));
|
||||
for (const auto &listener : qAsConst(_listeners)) {
|
||||
qCInfo(lcSocketApi) << "Trying to send SocketAPI Register Path Message -->" << message << "to" << listener->socket;
|
||||
listener->sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
@ -464,8 +489,8 @@ void SocketApi::slotUpdateFolderView(Folder *f)
|
||||
|
||||
void SocketApi::broadcastMessage(const QString &msg, bool doWait)
|
||||
{
|
||||
foreach (auto &listener, _listeners) {
|
||||
listener.sendMessage(msg, doWait);
|
||||
for (const auto &listener : qAsConst(_listeners)) {
|
||||
listener->sendMessage(msg, doWait);
|
||||
}
|
||||
}
|
||||
|
||||
@ -515,8 +540,8 @@ void SocketApi::broadcastStatusPushMessage(const QString &systemPath, SyncFileSt
|
||||
QString msg = buildMessage(QLatin1String("STATUS"), systemPath, fileStatus.toSocketAPIString());
|
||||
Q_ASSERT(!systemPath.endsWith('/'));
|
||||
uint directoryHash = qHash(systemPath.left(systemPath.lastIndexOf('/')));
|
||||
foreach (auto &listener, _listeners) {
|
||||
listener.sendMessageIfDirectoryMonitored(msg, directoryHash);
|
||||
for (const auto &listener : qAsConst(_listeners)) {
|
||||
listener->sendMessageIfDirectoryMonitored(msg, directoryHash);
|
||||
}
|
||||
}
|
||||
|
||||
@ -612,20 +637,20 @@ class GetOrCreatePublicLinkShare : public QObject
|
||||
Q_OBJECT
|
||||
public:
|
||||
GetOrCreatePublicLinkShare(const AccountPtr &account, const QString &localFile,
|
||||
std::function<void(const QString &link)> targetFun, QObject *parent)
|
||||
QObject *parent)
|
||||
: QObject(parent)
|
||||
, _account(account)
|
||||
, _shareManager(account)
|
||||
, _localFile(localFile)
|
||||
, _targetFun(targetFun)
|
||||
{
|
||||
connect(&_shareManager, &ShareManager::sharesFetched,
|
||||
this, &GetOrCreatePublicLinkShare::sharesFetched);
|
||||
connect(&_shareManager, &ShareManager::linkShareCreated,
|
||||
this, &GetOrCreatePublicLinkShare::linkShareCreated);
|
||||
connect(&_shareManager, &ShareManager::linkShareRequiresPassword,
|
||||
this, &GetOrCreatePublicLinkShare::linkShareRequiresPassword);
|
||||
connect(&_shareManager, &ShareManager::serverError,
|
||||
this, &GetOrCreatePublicLinkShare::serverError);
|
||||
connect(&_shareManager, &ShareManager::linkShareRequiresPassword,
|
||||
this, &GetOrCreatePublicLinkShare::passwordRequired);
|
||||
}
|
||||
|
||||
void run()
|
||||
@ -638,6 +663,7 @@ private slots:
|
||||
void sharesFetched(const QList<QSharedPointer<Share>> &shares)
|
||||
{
|
||||
auto shareName = SocketApi::tr("Context menu share");
|
||||
|
||||
// If there already is a context menu share, reuse it
|
||||
for (const auto &share : shares) {
|
||||
const auto linkShare = qSharedPointerDynamicCast<LinkShare>(share);
|
||||
@ -679,6 +705,13 @@ private slots:
|
||||
_shareManager.createLinkShare(_localFile, QString(), password);
|
||||
}
|
||||
|
||||
void linkShareRequiresPassword(const QString &message)
|
||||
{
|
||||
qCInfo(lcPublicLink) << "Could not create link share:" << message;
|
||||
emit error(message);
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void serverError(int code, const QString &message)
|
||||
{
|
||||
qCWarning(lcPublicLink) << "Share fetch/create error" << code << message;
|
||||
@ -688,19 +721,24 @@ private slots:
|
||||
tr("Could not retrieve or create the public link share. Error:\n\n%1").arg(message),
|
||||
QMessageBox::Ok,
|
||||
QMessageBox::NoButton);
|
||||
emit error(message);
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
signals:
|
||||
void done(const QString &link);
|
||||
void error(const QString &message);
|
||||
|
||||
private:
|
||||
void success(const QString &link)
|
||||
{
|
||||
_targetFun(link);
|
||||
emit done(link);
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
AccountPtr _account;
|
||||
ShareManager _shareManager;
|
||||
QString _localFile;
|
||||
std::function<void(const QString &url)> _targetFun;
|
||||
};
|
||||
|
||||
#else
|
||||
@ -728,7 +766,11 @@ void SocketApi::command_COPY_PUBLIC_LINK(const QString &localFile, SocketListene
|
||||
return;
|
||||
|
||||
AccountPtr account = fileData.folder->accountState()->account();
|
||||
auto job = new GetOrCreatePublicLinkShare(account, fileData.serverRelativePath, [](const QString &url) { copyUrlToClipboard(url); }, this);
|
||||
auto job = new GetOrCreatePublicLinkShare(account, fileData.serverRelativePath, this);
|
||||
connect(job, &GetOrCreatePublicLinkShare::done, this,
|
||||
[](const QString &url) { copyUrlToClipboard(url); });
|
||||
connect(job, &GetOrCreatePublicLinkShare::error, this,
|
||||
[=]() { emit shareCommandReceived(fileData.serverRelativePath, fileData.localPath, ShareDialogStartPage::PublicLinks); });
|
||||
job->run();
|
||||
}
|
||||
|
||||
@ -788,7 +830,7 @@ void SocketApi::command_OPEN_PRIVATE_LINK(const QString &localFile, SocketListen
|
||||
|
||||
void SocketApi::command_MAKE_AVAILABLE_LOCALLY(const QString &filesArg, SocketListener *)
|
||||
{
|
||||
QStringList files = filesArg.split(QLatin1Char('\x1e')); // Record Separator
|
||||
const QStringList files = split(filesArg);
|
||||
|
||||
for (const auto &file : files) {
|
||||
auto data = FileData::get(file);
|
||||
@ -809,7 +851,7 @@ void SocketApi::command_MAKE_AVAILABLE_LOCALLY(const QString &filesArg, SocketLi
|
||||
/* Go over all the files and replace them by a virtual file */
|
||||
void SocketApi::command_MAKE_ONLINE_ONLY(const QString &filesArg, SocketListener *)
|
||||
{
|
||||
QStringList files = filesArg.split(QLatin1Char('\x1e')); // Record Separator
|
||||
const QStringList files = split(filesArg);
|
||||
|
||||
for (const auto &file : files) {
|
||||
auto data = FileData::get(file);
|
||||
@ -903,6 +945,22 @@ void SocketApi::command_MOVE_ITEM(const QString &localFile, SocketListener *)
|
||||
solver.setRemoteVersionFilename(target);
|
||||
}
|
||||
|
||||
void SocketApi::command_V2_LIST_ACCOUNTS(const QSharedPointer<SocketApiJobV2> &job) const
|
||||
{
|
||||
QJsonArray out;
|
||||
for (auto acc : AccountManager::instance()->accounts()) {
|
||||
// TODO: Use uuid once https://github.com/owncloud/client/pull/8397 is merged
|
||||
out << QJsonObject({ { "name", acc->account()->displayName() }, { "id", acc->account()->id() } });
|
||||
}
|
||||
job->success({ { "accounts", out } });
|
||||
}
|
||||
|
||||
void SocketApi::command_V2_UPLOAD_FILES_FROM(const QSharedPointer<SocketApiJobV2> &job) const
|
||||
{
|
||||
auto uploadJob = new SocketUploadJob(job);
|
||||
uploadJob->start();
|
||||
}
|
||||
|
||||
void SocketApi::emailPrivateLink(const QString &link)
|
||||
{
|
||||
Utility::openEmailComposer(
|
||||
@ -948,8 +1006,7 @@ void SocketApi::sendSharingContextMenuOptions(const FileData &fileData, SocketLi
|
||||
// If sharing is globally disabled, do not show any sharing entries.
|
||||
// If there is no permission to share for this file, add a disabled entry saying so
|
||||
if (isOnTheServer && !record._remotePerm.isNull() && !record._remotePerm.hasPermission(RemotePermissions::CanReshare)) {
|
||||
listener->sendMessage(QLatin1String("MENU_ITEM:DISABLED:d:") + (!record.isDirectory()
|
||||
? tr("Resharing this file is not allowed") : tr("Resharing this folder is not allowed")));
|
||||
listener->sendMessage(QLatin1String("MENU_ITEM:DISABLED:d:") + (!record.isDirectory() ? tr("Resharing this file is not allowed") : tr("Resharing this folder is not allowed")));
|
||||
} else {
|
||||
listener->sendMessage(QLatin1String("MENU_ITEM:SHARE") + flagString + tr("Share options"));
|
||||
|
||||
@ -1031,7 +1088,7 @@ SocketApi::FileData SocketApi::FileData::parentFolder() const
|
||||
void SocketApi::command_GET_MENU_ITEMS(const QString &argument, OCC::SocketListener *listener)
|
||||
{
|
||||
listener->sendMessage(QString("GET_MENU_ITEMS:BEGIN"));
|
||||
QStringList files = argument.split(QLatin1Char('\x1e')); // Record Separator
|
||||
const QStringList files = split(argument);
|
||||
|
||||
// Find the common sync folder.
|
||||
// syncFolder will be null if files are in different folders.
|
||||
@ -1149,13 +1206,13 @@ void SocketApi::command_GET_MENU_ITEMS(const QString &argument, OCC::SocketListe
|
||||
// TODO: Should be a submenu, should use icons
|
||||
auto makePinContextMenu = [&](bool makeAvailableLocally, bool freeSpace) {
|
||||
listener->sendMessage(QLatin1String("MENU_ITEM:CURRENT_PIN:d:")
|
||||
+ Utility::vfsCurrentAvailabilityText(*combined));
|
||||
+ Utility::vfsCurrentAvailabilityText(*combined));
|
||||
listener->sendMessage(QLatin1String("MENU_ITEM:MAKE_AVAILABLE_LOCALLY:")
|
||||
+ (makeAvailableLocally ? QLatin1String(":") : QLatin1String("d:"))
|
||||
+ Utility::vfsPinActionText());
|
||||
+ (makeAvailableLocally ? QLatin1String(":") : QLatin1String("d:"))
|
||||
+ Utility::vfsPinActionText());
|
||||
listener->sendMessage(QLatin1String("MENU_ITEM:MAKE_ONLINE_ONLY:")
|
||||
+ (freeSpace ? QLatin1String(":") : QLatin1String("d:"))
|
||||
+ Utility::vfsFreeSpaceActionText());
|
||||
+ (freeSpace ? QLatin1String(":") : QLatin1String("d:"))
|
||||
+ Utility::vfsFreeSpaceActionText());
|
||||
};
|
||||
|
||||
if (combined) {
|
||||
@ -1241,20 +1298,20 @@ void SocketApi::command_ASYNC_GET_WIDGET_PROPERTY(const QSharedPointer<SocketApi
|
||||
|
||||
auto segments = propertyName.split('.');
|
||||
|
||||
QObject* currentObject = widget;
|
||||
QObject *currentObject = widget;
|
||||
QString value;
|
||||
for(int i = 0;i<segments.count(); i++) {
|
||||
for (int i = 0; i < segments.count(); i++) {
|
||||
auto segment = segments.at(i);
|
||||
auto var = currentObject->property(segment.toUtf8().constData());
|
||||
|
||||
if(var.canConvert<QString>()) {
|
||||
if (var.canConvert<QString>()) {
|
||||
var.convert(QMetaType::QString);
|
||||
value = var.value<QString>();
|
||||
break;
|
||||
}
|
||||
|
||||
auto tmpObject = var.value<QObject*>();
|
||||
if(tmpObject) {
|
||||
auto tmpObject = var.value<QObject *>();
|
||||
if (tmpObject) {
|
||||
currentObject = tmpObject;
|
||||
} else {
|
||||
QString message = QString(QLatin1String("Widget not found: 3: %1")).arg(widgetName);
|
||||
@ -1277,7 +1334,7 @@ void SocketApi::command_ASYNC_SET_WIDGET_PROPERTY(const QSharedPointer<SocketApi
|
||||
return;
|
||||
}
|
||||
widget->setProperty(arguments["property"].toString().toUtf8().constData(),
|
||||
arguments["value"]);
|
||||
arguments["value"]);
|
||||
|
||||
job->resolve();
|
||||
}
|
||||
@ -1345,20 +1402,20 @@ void SocketApi::command_ASYNC_ASSERT_ICON_IS_EQUAL(const QSharedPointer<SocketAp
|
||||
|
||||
auto segments = propertyName.split('.');
|
||||
|
||||
QObject* currentObject = widget;
|
||||
QObject *currentObject = widget;
|
||||
QIcon value;
|
||||
for(int i = 0;i<segments.count(); i++) {
|
||||
for (int i = 0; i < segments.count(); i++) {
|
||||
auto segment = segments.at(i);
|
||||
auto var = currentObject->property(segment.toUtf8().constData());
|
||||
|
||||
if(var.canConvert<QIcon>()) {
|
||||
if (var.canConvert<QIcon>()) {
|
||||
var.convert(QMetaType::QIcon);
|
||||
value = var.value<QIcon>();
|
||||
break;
|
||||
}
|
||||
|
||||
auto tmpObject = var.value<QObject*>();
|
||||
if(tmpObject) {
|
||||
auto tmpObject = var.value<QObject *>();
|
||||
if (tmpObject) {
|
||||
currentObject = tmpObject;
|
||||
} else {
|
||||
job->reject(QString(QLatin1String("Icon not found: %1")).arg(propertyName));
|
||||
@ -1366,12 +1423,11 @@ void SocketApi::command_ASYNC_ASSERT_ICON_IS_EQUAL(const QSharedPointer<SocketAp
|
||||
}
|
||||
|
||||
auto iconName = job->arguments()[QLatin1String("iconName")].toString();
|
||||
if (value.name() == iconName) {
|
||||
if (value.name() == iconName) {
|
||||
job->resolve();
|
||||
} else {
|
||||
job->reject("iconName " + iconName + " does not match: " + value.name());
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1383,6 +1439,46 @@ QString SocketApi::buildRegisterPathMessage(const QString &path)
|
||||
return message;
|
||||
}
|
||||
|
||||
void SocketApiJob::resolve(const QString &response)
|
||||
{
|
||||
_socketListener->sendMessage(QStringLiteral("RESOLVE|") + _jobId + QLatin1Char('|') + response);
|
||||
}
|
||||
|
||||
void SocketApiJob::resolve(const QJsonObject &response)
|
||||
{
|
||||
resolve(QJsonDocument { response }.toJson());
|
||||
}
|
||||
|
||||
void SocketApiJob::reject(const QString &response)
|
||||
{
|
||||
_socketListener->sendMessage(QStringLiteral("REJECT|") + _jobId + QLatin1Char('|') + response);
|
||||
}
|
||||
|
||||
SocketApiJobV2::SocketApiJobV2(const QSharedPointer<SocketListener> &socketListener, const QByteArray &command, const QJsonObject &arguments)
|
||||
: _socketListener(socketListener)
|
||||
, _command(command)
|
||||
, _jobId(arguments[QStringLiteral("id")].toString())
|
||||
, _arguments(arguments[QStringLiteral("arguments")].toObject())
|
||||
{
|
||||
ASSERT(!_jobId.isEmpty())
|
||||
}
|
||||
|
||||
void SocketApiJobV2::success(const QJsonObject &response) const
|
||||
{
|
||||
doFinish(response);
|
||||
}
|
||||
|
||||
void SocketApiJobV2::failure(const QString &error) const
|
||||
{
|
||||
doFinish({ { QStringLiteral("error"), error } });
|
||||
}
|
||||
|
||||
void SocketApiJobV2::doFinish(const QJsonObject &obj) const
|
||||
{
|
||||
_socketListener->sendMessage(_command + QStringLiteral("_RESULT:") + QJsonDocument({ { QStringLiteral("id"), _jobId }, { QStringLiteral("arguments"), obj } }).toJson(QJsonDocument::Compact));
|
||||
Q_EMIT finished();
|
||||
}
|
||||
|
||||
} // namespace OCC
|
||||
|
||||
#include "socketapi.moc"
|
||||
@ -40,6 +40,7 @@ class Folder;
|
||||
class SocketListener;
|
||||
class DirectEditor;
|
||||
class SocketApiJob;
|
||||
class SocketApiJobV2;
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(lcSocketApi)
|
||||
|
||||
@ -129,6 +130,10 @@ private:
|
||||
Q_INVOKABLE void command_OPEN(const QString &localFile, SocketListener *listener);
|
||||
#endif
|
||||
|
||||
// External sync
|
||||
Q_INVOKABLE void command_V2_LIST_ACCOUNTS(const QSharedPointer<SocketApiJobV2> &job) const;
|
||||
Q_INVOKABLE void command_V2_UPLOAD_FILES_FROM(const QSharedPointer<SocketApiJobV2> &job) const;
|
||||
|
||||
// Fetch the private link and call targetFun
|
||||
void fetchPrivateLinkUrlHelper(const QString &localFile, const std::function<void(const QString &url)> &targetFun);
|
||||
|
||||
@ -164,7 +169,7 @@ private:
|
||||
QString buildRegisterPathMessage(const QString &path);
|
||||
|
||||
QSet<QString> _registeredAliases;
|
||||
QList<SocketListener> _listeners;
|
||||
QMap<QIODevice *, QSharedPointer<SocketListener>> _listeners;
|
||||
SocketApiServer _localServer;
|
||||
};
|
||||
}
|
||||
@ -62,12 +62,20 @@ class SocketListener
|
||||
public:
|
||||
QPointer<QIODevice> socket;
|
||||
|
||||
explicit SocketListener(QIODevice *socket)
|
||||
: socket(socket)
|
||||
explicit SocketListener(QIODevice *_socket)
|
||||
: socket(_socket)
|
||||
{
|
||||
}
|
||||
|
||||
void sendMessage(const QString &message, bool doWait = false) const;
|
||||
void sendWarning(const QString &message, bool doWait = false) const
|
||||
{
|
||||
sendMessage(QStringLiteral("WARNING:") + message, doWait);
|
||||
}
|
||||
void sendError(const QString &message, bool doWait = false) const
|
||||
{
|
||||
sendMessage(QStringLiteral("ERROR:") + message, doWait);
|
||||
}
|
||||
|
||||
void sendMessageIfDirectoryMonitored(const QString &message, uint systemDirectoryHash) const
|
||||
{
|
||||
@ -109,30 +117,48 @@ class SocketApiJob : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SocketApiJob(const QString &jobId, SocketListener *socketListener, const QJsonObject &arguments)
|
||||
explicit SocketApiJob(const QString &jobId, const QSharedPointer<SocketListener> &socketListener, const QJsonObject &arguments)
|
||||
: _jobId(jobId)
|
||||
, _socketListener(socketListener)
|
||||
, _arguments(arguments)
|
||||
{
|
||||
}
|
||||
|
||||
void resolve(const QString &response = QString())
|
||||
{
|
||||
_socketListener->sendMessage(QLatin1String("RESOLVE|") + _jobId + '|' + response);
|
||||
}
|
||||
void resolve(const QString &response = QString());
|
||||
|
||||
void resolve(const QJsonObject &response) { resolve(QJsonDocument{ response }.toJson()); }
|
||||
void resolve(const QJsonObject &response);
|
||||
|
||||
const QJsonObject &arguments() { return _arguments; }
|
||||
|
||||
void reject(const QString &response)
|
||||
{
|
||||
_socketListener->sendMessage(QLatin1String("REJECT|") + _jobId + '|' + response);
|
||||
}
|
||||
void reject(const QString &response);
|
||||
|
||||
protected:
|
||||
QString _jobId;
|
||||
QSharedPointer<SocketListener> _socketListener;
|
||||
QJsonObject _arguments;
|
||||
};
|
||||
|
||||
class SocketApiJobV2 : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SocketApiJobV2(const QSharedPointer<SocketListener> &socketListener, const QByteArray &command, const QJsonObject &arguments);
|
||||
|
||||
void success(const QJsonObject &response) const;
|
||||
void failure(const QString &error) const;
|
||||
|
||||
const QJsonObject &arguments() const { return _arguments; }
|
||||
QByteArray command() const { return _command; }
|
||||
|
||||
Q_SIGNALS:
|
||||
void finished() const;
|
||||
|
||||
private:
|
||||
void doFinish(const QJsonObject &obj) const;
|
||||
|
||||
QSharedPointer<SocketListener> _socketListener;
|
||||
const QByteArray _command;
|
||||
QString _jobId;
|
||||
SocketListener *_socketListener;
|
||||
QJsonObject _arguments;
|
||||
};
|
||||
}
|
||||
@ -16,7 +16,7 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@protocol ChannelProtocol <NSObject>
|
||||
- (void)sendMessage:(NSData*)msg;
|
||||
- (void)sendMessage:(NSData *)msg;
|
||||
@end
|
||||
|
||||
@protocol RemoteEndProtocol <NSObject, ChannelProtocol>
|
||||
@ -31,7 +31,7 @@
|
||||
@interface Server : NSObject
|
||||
@property SocketApiServerPrivate *wrapper;
|
||||
- (instancetype)initWithWrapper:(SocketApiServerPrivate *)wrapper;
|
||||
- (void)registerClient:(NSDistantObject <RemoteEndProtocol> *)remoteEnd;
|
||||
- (void)registerClient:(NSDistantObject<RemoteEndProtocol> *)remoteEnd;
|
||||
@end
|
||||
|
||||
class SocketApiSocketPrivate
|
||||
@ -39,13 +39,13 @@ class SocketApiSocketPrivate
|
||||
public:
|
||||
SocketApiSocket *q_ptr;
|
||||
|
||||
SocketApiSocketPrivate(NSDistantObject <ChannelProtocol> *remoteEnd);
|
||||
SocketApiSocketPrivate(NSDistantObject<ChannelProtocol> *remoteEnd);
|
||||
~SocketApiSocketPrivate();
|
||||
|
||||
// release remoteEnd
|
||||
void disconnectRemote();
|
||||
|
||||
NSDistantObject <ChannelProtocol> *remoteEnd;
|
||||
NSDistantObject<ChannelProtocol> *remoteEnd;
|
||||
LocalEnd *localEnd;
|
||||
QByteArray inBuffer;
|
||||
bool isRemoteDisconnected = false;
|
||||
@ -59,7 +59,7 @@ public:
|
||||
SocketApiServerPrivate();
|
||||
~SocketApiServerPrivate();
|
||||
|
||||
QList<SocketApiSocket*> pendingConnections;
|
||||
QList<SocketApiSocket *> pendingConnections;
|
||||
NSConnection *connection;
|
||||
Server *server;
|
||||
};
|
||||
@ -73,7 +73,7 @@ public:
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)sendMessage:(NSData*)msg
|
||||
- (void)sendMessage:(NSData *)msg
|
||||
{
|
||||
if (_wrapper) {
|
||||
_wrapper->inBuffer += QByteArray::fromRawNSData(msg);
|
||||
@ -81,7 +81,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
- (void)connectionDidDie:(NSNotification*)notification
|
||||
- (void)connectionDidDie:(NSNotification *)notification
|
||||
{
|
||||
#pragma unused(notification)
|
||||
if (_wrapper) {
|
||||
@ -99,7 +99,7 @@ public:
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)registerClient:(NSDistantObject <RemoteEndProtocol> *)remoteEnd
|
||||
- (void)registerClient:(NSDistantObject<RemoteEndProtocol> *)remoteEnd
|
||||
{
|
||||
// This saves a few mach messages that would otherwise be needed to query the interface
|
||||
[remoteEnd setProtocolForProxy:@protocol(RemoteEndProtocol)];
|
||||
@ -150,7 +150,7 @@ qint64 SocketApiSocket::writeData(const char *data, qint64 len)
|
||||
// Since FinderSync already runs in a separate process, blocking isn't too critical.
|
||||
[d->remoteEnd sendMessage:[NSData dataWithBytesNoCopy:const_cast<char *>(data) length:len freeWhenDone:NO]];
|
||||
return len;
|
||||
} @catch(NSException* e) {
|
||||
} @catch (NSException *e) {
|
||||
// connectionDidDie can be notified too late, also interpret any sending exception as a disconnection.
|
||||
d->disconnectRemote();
|
||||
emit disconnected();
|
||||
@ -170,16 +170,16 @@ bool SocketApiSocket::canReadLine() const
|
||||
return d->inBuffer.indexOf('\n', int(pos())) != -1 || QIODevice::canReadLine();
|
||||
}
|
||||
|
||||
SocketApiSocketPrivate::SocketApiSocketPrivate(NSDistantObject <ChannelProtocol> *remoteEnd)
|
||||
SocketApiSocketPrivate::SocketApiSocketPrivate(NSDistantObject<ChannelProtocol> *remoteEnd)
|
||||
: remoteEnd(remoteEnd)
|
||||
, localEnd([[LocalEnd alloc] initWithWrapper:this])
|
||||
{
|
||||
[remoteEnd retain];
|
||||
// (Ab)use our objective-c object just to catch the notification
|
||||
[[NSNotificationCenter defaultCenter] addObserver:localEnd
|
||||
selector:@selector(connectionDidDie:)
|
||||
name:NSConnectionDidDieNotification
|
||||
object:[remoteEnd connectionForProxy]];
|
||||
selector:@selector(connectionDidDie:)
|
||||
name:NSConnectionDidDieNotification
|
||||
object:[remoteEnd connectionForProxy]];
|
||||
}
|
||||
|
||||
SocketApiSocketPrivate::~SocketApiSocketPrivate()
|
||||
92
src/gui/socketapi/socketuploadjob.cpp
Normal file
92
src/gui/socketapi/socketuploadjob.cpp
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) by Hannah von Reth <hannah.vonreth@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 "socketuploadjob.h"
|
||||
#include "socketapi_p.h"
|
||||
|
||||
#include "accountmanager.h"
|
||||
#include "common/syncjournaldb.h"
|
||||
#include "syncengine.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QJsonArray>
|
||||
#include <QRegularExpression>
|
||||
|
||||
using namespace OCC;
|
||||
|
||||
SocketUploadJob::SocketUploadJob(const QSharedPointer<SocketApiJobV2> &job)
|
||||
: _apiJob(job)
|
||||
{
|
||||
connect(job.data(), &SocketApiJobV2::finished, this, &SocketUploadJob::deleteLater);
|
||||
|
||||
_localPath = _apiJob->arguments()[QLatin1String("localPath")].toString();
|
||||
_remotePath = _apiJob->arguments()[QLatin1String("remotePath")].toString();
|
||||
if (!_remotePath.startsWith(QLatin1Char('/'))) {
|
||||
_remotePath = QLatin1Char('/') + _remotePath;
|
||||
}
|
||||
|
||||
_pattern = job->arguments()[QLatin1String("pattern")].toString();
|
||||
// TODO: use uuid
|
||||
const auto accname = job->arguments()[QLatin1String("account")][QLatin1String("name")].toString();
|
||||
auto account = AccountManager::instance()->account(accname);
|
||||
|
||||
if (!QFileInfo(_localPath).isAbsolute()) {
|
||||
job->failure(QStringLiteral("Local path must be a an absolute path"));
|
||||
return;
|
||||
}
|
||||
if (!_tmp.open()) {
|
||||
job->failure(QStringLiteral("Failed to create temporary database"));
|
||||
return;
|
||||
}
|
||||
|
||||
_db = new SyncJournalDb(_tmp.fileName(), this);
|
||||
_engine = new SyncEngine(account->account(), _localPath.endsWith(QLatin1Char('/')) ? _localPath : _localPath + QLatin1Char('/'), _remotePath, _db);
|
||||
_engine->setParent(_db);
|
||||
|
||||
connect(_engine, &OCC::SyncEngine::itemCompleted, this, [this](const OCC::SyncFileItemPtr item) {
|
||||
_syncedFiles.append(item->_file);
|
||||
});
|
||||
|
||||
connect(_engine, &OCC::SyncEngine::finished, this, [this](bool ok) {
|
||||
if (ok) {
|
||||
_apiJob->success({ { "localPath", _localPath }, { "syncedFiles", QJsonArray::fromStringList(_syncedFiles) } });
|
||||
}
|
||||
});
|
||||
connect(_engine, &OCC::SyncEngine::syncError, this, [this](const QString &error, ErrorCategory) {
|
||||
_apiJob->failure(error);
|
||||
});
|
||||
}
|
||||
|
||||
void SocketUploadJob::start()
|
||||
{
|
||||
auto opt = _engine->syncOptions();
|
||||
opt.setFilePattern(_pattern);
|
||||
if (!opt.fileRegex().isValid()) {
|
||||
_apiJob->failure(opt.fileRegex().errorString());
|
||||
return;
|
||||
}
|
||||
_engine->setSyncOptions(opt);
|
||||
|
||||
// create the dir, fail if it already exists
|
||||
auto mkdir = new OCC::MkColJob(_engine->account(), _remotePath);
|
||||
connect(mkdir, &OCC::MkColJob::finishedWithoutError, _engine, &OCC::SyncEngine::startSync);
|
||||
connect(mkdir, &OCC::MkColJob::finishedWithError, this, [this](QNetworkReply *reply) {
|
||||
if (reply->error() == 202) {
|
||||
_apiJob->failure(QStringLiteral("Destination %1 already exists").arg(_remotePath));
|
||||
} else {
|
||||
_apiJob->failure(reply->errorString());
|
||||
}
|
||||
});
|
||||
mkdir->start();
|
||||
}
|
||||
44
src/gui/socketapi/socketuploadjob.h
Normal file
44
src/gui/socketapi/socketuploadjob.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) by Hannah von Reth <hannah.vonreth@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 <QObject>
|
||||
#include <QTemporaryFile>
|
||||
|
||||
#include "socketapi.h"
|
||||
#include "account.h"
|
||||
|
||||
namespace OCC {
|
||||
|
||||
class SyncJournalDb;
|
||||
class SyncEngine;
|
||||
|
||||
class SocketUploadJob : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SocketUploadJob(const QSharedPointer<SocketApiJobV2> &job);
|
||||
void start();
|
||||
|
||||
private:
|
||||
QSharedPointer<SocketApiJobV2> _apiJob;
|
||||
QString _localPath;
|
||||
QString _remotePath;
|
||||
QString _pattern;
|
||||
QTemporaryFile _tmp;
|
||||
SyncJournalDb *_db;
|
||||
SyncEngine *_engine;
|
||||
QStringList _syncedFiles;
|
||||
};
|
||||
}
|
||||
@ -51,6 +51,7 @@ set(libsync_SRCS
|
||||
syncfilestatustracker.cpp
|
||||
localdiscoverytracker.cpp
|
||||
syncresult.cpp
|
||||
syncoptions.cpp
|
||||
theme.cpp
|
||||
clientsideencryption.cpp
|
||||
clientsideencryptionjobs.cpp
|
||||
|
||||
@ -149,11 +149,15 @@ void AbstractNetworkJob::adoptRequest(QNetworkReply *reply)
|
||||
|
||||
QUrl AbstractNetworkJob::makeAccountUrl(const QString &relativePath) const
|
||||
{
|
||||
// ensure we always used the remote folder
|
||||
ASSERT(relativePath.startsWith(QLatin1Char('/')))
|
||||
return Utility::concatUrlPath(_account->url(), relativePath);
|
||||
}
|
||||
|
||||
QUrl AbstractNetworkJob::makeDavUrl(const QString &relativePath) const
|
||||
{
|
||||
// ensure we always used the remote folder
|
||||
ASSERT(relativePath.startsWith(QLatin1Char('/')))
|
||||
return Utility::concatUrlPath(_account->davUrl(), relativePath);
|
||||
}
|
||||
|
||||
|
||||
@ -51,9 +51,7 @@ AccessManager::AccessManager(QObject *parent)
|
||||
|
||||
QByteArray AccessManager::generateRequestId()
|
||||
{
|
||||
// Use a UUID with the starting and ending curly brace removed.
|
||||
auto uuid = QUuid::createUuid().toByteArray();
|
||||
return uuid.mid(1, uuid.size() - 2);
|
||||
return QUuid::createUuid().toByteArray(QUuid::WithoutBraces);
|
||||
}
|
||||
|
||||
QNetworkReply *AccessManager::createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData)
|
||||
|
||||
@ -67,8 +67,6 @@ void ProcessDirectoryJob::process()
|
||||
{
|
||||
ASSERT(_localQueryDone && _serverQueryDone);
|
||||
|
||||
QString localDir;
|
||||
|
||||
// Build lookup tables for local, remote and db entries.
|
||||
// For suffix-virtual files, the key will normally be the base file name
|
||||
// without the suffix.
|
||||
@ -528,7 +526,7 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(
|
||||
}
|
||||
return ParentNotChanged;
|
||||
}();
|
||||
|
||||
|
||||
processFileAnalyzeLocalInfo(item, path, localEntry, serverEntry, dbEntry, serverQueryMode);
|
||||
return;
|
||||
}
|
||||
@ -545,15 +543,14 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(
|
||||
item->_modtime = serverEntry.modtime;
|
||||
item->_size = serverEntry.size;
|
||||
|
||||
auto postProcessServerNew = [=] () {
|
||||
auto tmp_path = path;
|
||||
auto postProcessServerNew = [=]() mutable {
|
||||
if (item->isDirectory()) {
|
||||
_pendingAsyncJobs++;
|
||||
_discoveryData->checkSelectiveSyncNewFolder(tmp_path._server, serverEntry.remotePerm,
|
||||
_discoveryData->checkSelectiveSyncNewFolder(path._server, serverEntry.remotePerm,
|
||||
[=](bool result) {
|
||||
--_pendingAsyncJobs;
|
||||
if (!result) {
|
||||
processFileAnalyzeLocalInfo(item, tmp_path, localEntry, serverEntry, dbEntry, _queryServer);
|
||||
processFileAnalyzeLocalInfo(item, path, localEntry, serverEntry, dbEntry, _queryServer);
|
||||
}
|
||||
QTimer::singleShot(0, _discoveryData, &DiscoveryPhase::scheduleMoreJobs);
|
||||
});
|
||||
@ -568,7 +565,7 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(
|
||||
&& !FileSystem::isExcludeFile(item->_file)) {
|
||||
item->_type = ItemTypeVirtualFile;
|
||||
if (isVfsWithSuffix())
|
||||
addVirtualFileSuffix(tmp_path._original);
|
||||
addVirtualFileSuffix(path._original);
|
||||
}
|
||||
|
||||
if (opts._vfs->mode() != Vfs::Off && !item->_encryptedFileName.isEmpty()) {
|
||||
@ -579,7 +576,7 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(
|
||||
// another scenario - we are syncing a file which is on disk but not in the database (database was removed or file was not written there yet)
|
||||
item->_size = serverEntry.size - Constants::e2EeTagSize;
|
||||
}
|
||||
processFileAnalyzeLocalInfo(item, tmp_path, localEntry, serverEntry, dbEntry, _queryServer);
|
||||
processFileAnalyzeLocalInfo(item, path, localEntry, serverEntry, dbEntry, _queryServer);
|
||||
};
|
||||
|
||||
// Potential NEW/NEW conflict is handled in AnalyzeLocal
|
||||
@ -697,9 +694,8 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(
|
||||
} else {
|
||||
// we need to make a request to the server to know that the original file is deleted on the server
|
||||
_pendingAsyncJobs++;
|
||||
auto job = new RequestEtagJob(_discoveryData->_account, originalPath, this);
|
||||
connect(job, &RequestEtagJob::finishedWithResult, this, [=](const HttpResult<QString> &etag) {
|
||||
auto tmp_path = path;
|
||||
auto job = new RequestEtagJob(_discoveryData->_account, _discoveryData->_remoteFolder + originalPath, this);
|
||||
connect(job, &RequestEtagJob::finishedWithResult, this, [=](const HttpResult<QByteArray> &etag) mutable {
|
||||
_pendingAsyncJobs--;
|
||||
QTimer::singleShot(0, _discoveryData, &DiscoveryPhase::scheduleMoreJobs);
|
||||
if (etag || etag.error().code != 404 ||
|
||||
@ -715,8 +711,8 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(
|
||||
// In case the deleted item was discovered in parallel
|
||||
_discoveryData->findAndCancelDeletedJob(originalPath);
|
||||
|
||||
postProcessRename(tmp_path);
|
||||
processFileFinalize(item, tmp_path, item->isDirectory(), item->_instruction == CSYNC_INSTRUCTION_RENAME ? NormalQuery : ParentDontExist, _queryServer);
|
||||
postProcessRename(path);
|
||||
processFileFinalize(item, path, item->isDirectory(), item->_instruction == CSYNC_INSTRUCTION_RENAME ? NormalQuery : ParentDontExist, _queryServer);
|
||||
});
|
||||
job->start();
|
||||
done = true; // Ideally, if the origin still exist on the server, we should continue searching... but that'd be difficult
|
||||
@ -1162,8 +1158,8 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo(
|
||||
if (base.isVirtualFile() && isVfsWithSuffix())
|
||||
chopVirtualFileSuffix(serverOriginalPath);
|
||||
auto job = new RequestEtagJob(_discoveryData->_account, serverOriginalPath, this);
|
||||
connect(job, &RequestEtagJob::finishedWithResult, this, [=](const HttpResult<QString> &etag) mutable {
|
||||
if (!etag || (*etag != base._etag && !item->isDirectory()) || _discoveryData->isRenamed(originalPath)) {
|
||||
connect(job, &RequestEtagJob::finishedWithResult, this, [=](const HttpResult<QByteArray> &etag) mutable {
|
||||
if (!etag || (etag.get() != base._etag && !item->isDirectory()) || _discoveryData->isRenamed(originalPath)) {
|
||||
qCInfo(lcDisco) << "Can't rename because the etag has changed or the directory is gone" << originalPath;
|
||||
// Can't be a rename, leave it as a new.
|
||||
postProcessLocalNew();
|
||||
@ -1171,7 +1167,7 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo(
|
||||
// In case the deleted item was discovered in parallel
|
||||
_discoveryData->findAndCancelDeletedJob(originalPath);
|
||||
processRename(path);
|
||||
recurseQueryServer = *etag == base._etag ? ParentNotChanged : NormalQuery;
|
||||
recurseQueryServer = etag.get() == base._etag ? ParentNotChanged : NormalQuery;
|
||||
}
|
||||
processFileFinalize(item, path, item->isDirectory(), NormalQuery, recurseQueryServer);
|
||||
_pendingAsyncJobs--;
|
||||
|
||||
@ -293,6 +293,6 @@ private:
|
||||
signals:
|
||||
void finished();
|
||||
// The root etag of this directory was fetched
|
||||
void etag(const QString &, const QDateTime &time);
|
||||
void etag(const QByteArray &, const QDateTime &time);
|
||||
};
|
||||
}
|
||||
|
||||
@ -486,7 +486,7 @@ void DiscoverySingleDirectoryJob::directoryListingIteratedSlot(const QString &fi
|
||||
//This works in concerto with the RequestEtagJob and the Folder object to check if the remote folder changed.
|
||||
if (map.contains("getetag")) {
|
||||
if (_firstEtag.isEmpty()) {
|
||||
_firstEtag = parseEtag(map.value("getetag").toUtf8()); // for directory itself
|
||||
_firstEtag = parseEtag(map.value(QStringLiteral("getetag")).toUtf8()); // for directory itself
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,7 +127,7 @@ public:
|
||||
// This is not actually a network job, it is just a job
|
||||
signals:
|
||||
void firstDirectoryPermissions(RemotePermissions);
|
||||
void etag(const QString &, const QDateTime &time);
|
||||
void etag(const QByteArray &, const QDateTime &time);
|
||||
void finished(const HttpResult<QVector<RemoteInfo>> &result);
|
||||
|
||||
private slots:
|
||||
@ -141,7 +141,7 @@ private slots:
|
||||
private:
|
||||
QVector<RemoteInfo> _results;
|
||||
QString _subPath;
|
||||
QString _firstEtag;
|
||||
QByteArray _firstEtag;
|
||||
QByteArray _fileId;
|
||||
AccountPtr _account;
|
||||
// The first result is for the directory itself and need to be ignored.
|
||||
|
||||
@ -63,9 +63,9 @@ time_t FileSystem::getModTime(const QString &filename)
|
||||
&& (stat.modtime != 0)) {
|
||||
result = stat.modtime;
|
||||
} else {
|
||||
qCWarning(lcFileSystem) << "Could not get modification time for" << filename
|
||||
<< "with csync, using QFileInfo";
|
||||
result = Utility::qDateTimeToTime_t(QFileInfo(filename).lastModified());
|
||||
qCWarning(lcFileSystem) << "Could not get modification time for" << filename
|
||||
<< "with csync, using QFileInfo:" << result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -115,7 +115,7 @@ static qint64 getSizeWithCsync(const QString &filename)
|
||||
if (csync_vio_local_stat(filename, &stat) != -1) {
|
||||
result = stat.size;
|
||||
} else {
|
||||
qCWarning(lcFileSystem) << "Could not get size for" << filename << "with csync";
|
||||
qCWarning(lcFileSystem) << "Could not get size for" << filename << "with csync" << Utility::formatWinError(errno);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -32,35 +32,10 @@
|
||||
#include <io.h> // for stdout
|
||||
#endif
|
||||
|
||||
namespace OCC {
|
||||
|
||||
QtMessageHandler s_originalMessageHandler = nullptr;
|
||||
|
||||
static void mirallLogCatcher(QtMsgType type, const QMessageLogContext &ctx, const QString &message)
|
||||
{
|
||||
auto logger = Logger::instance();
|
||||
if (type == QtDebugMsg && !logger->logDebug()) {
|
||||
if (s_originalMessageHandler) {
|
||||
s_originalMessageHandler(type, ctx, message);
|
||||
}
|
||||
} else if (!logger->isNoop()) {
|
||||
logger->doLog(qFormatLogMessage(type, ctx, message));
|
||||
}
|
||||
if(type == QtCriticalMsg || type == QtFatalMsg) {
|
||||
std::cerr << qPrintable(qFormatLogMessage(type, ctx, message)) << std::endl;
|
||||
}
|
||||
|
||||
if(type == QtFatalMsg) {
|
||||
if (!logger->isNoop()) {
|
||||
logger->close();
|
||||
}
|
||||
#if defined(Q_OS_WIN)
|
||||
// Make application terminate in a way that can be caught by the crash reporter
|
||||
Utility::crash();
|
||||
#endif
|
||||
}
|
||||
namespace {
|
||||
constexpr int CrashLogSize = 20;
|
||||
}
|
||||
|
||||
namespace OCC {
|
||||
|
||||
Logger *Logger::instance()
|
||||
{
|
||||
@ -71,11 +46,12 @@ Logger *Logger::instance()
|
||||
Logger::Logger(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
qSetMessagePattern("%{time yyyy-MM-dd hh:mm:ss:zzz} [ %{type} %{category} ]%{if-debug}\t[ %{function} ]%{endif}:\t%{message}");
|
||||
qSetMessagePattern(QStringLiteral("%{time yyyy-MM-dd hh:mm:ss:zzz} [ %{type} %{category} ]%{if-debug}\t[ %{function} ]%{endif}:\t%{message}"));
|
||||
_crashLog.resize(CrashLogSize);
|
||||
#ifndef NO_MSG_HANDLER
|
||||
s_originalMessageHandler = qInstallMessageHandler(mirallLogCatcher);
|
||||
#else
|
||||
Q_UNUSED(mirallLogCatcher)
|
||||
qInstallMessageHandler([](QtMsgType type, const QMessageLogContext &ctx, const QString &message) {
|
||||
Logger::instance()->doLog(type, ctx, message);
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -102,51 +78,38 @@ void Logger::postGuiMessage(const QString &title, const QString &message)
|
||||
emit guiMessage(title, message);
|
||||
}
|
||||
|
||||
void Logger::log(Log log)
|
||||
{
|
||||
QString msg;
|
||||
if (_showTime) {
|
||||
msg = log.timeStamp.toString(QLatin1String("MM-dd hh:mm:ss:zzz")) + QLatin1Char(' ');
|
||||
}
|
||||
|
||||
msg += log.message;
|
||||
// _logs.append(log);
|
||||
// std::cout << qPrintable(log.message) << std::endl;
|
||||
|
||||
doLog(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if doLog does nothing and need not to be called
|
||||
*/
|
||||
bool Logger::isNoop() const
|
||||
{
|
||||
QMutexLocker lock(&_mutex);
|
||||
return !_logstream;
|
||||
}
|
||||
|
||||
bool Logger::isLoggingToFile() const
|
||||
{
|
||||
QMutexLocker lock(&_mutex);
|
||||
return _logstream;
|
||||
}
|
||||
|
||||
void Logger::doLog(const QString &msg)
|
||||
void Logger::doLog(QtMsgType type, const QMessageLogContext &ctx, const QString &message)
|
||||
{
|
||||
const QString msg = qFormatLogMessage(type, ctx, message);
|
||||
{
|
||||
QMutexLocker lock(&_mutex);
|
||||
_crashLogIndex = (_crashLogIndex + 1) % CrashLogSize;
|
||||
_crashLog[_crashLogIndex] = msg;
|
||||
if (_logstream) {
|
||||
(*_logstream) << msg << endl;
|
||||
if (_doFileFlush)
|
||||
_logstream->flush();
|
||||
}
|
||||
if (type == QtFatalMsg) {
|
||||
close();
|
||||
#if defined(Q_OS_WIN)
|
||||
// Make application terminate in a way that can be caught by the crash reporter
|
||||
Utility::crash();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
emit logWindowLog(msg);
|
||||
}
|
||||
|
||||
void Logger::close()
|
||||
{
|
||||
QMutexLocker lock(&_mutex);
|
||||
dumpCrashLog();
|
||||
if (_logstream)
|
||||
{
|
||||
_logstream->flush();
|
||||
@ -155,15 +118,6 @@ void Logger::close()
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::mirallLog(const QString &message)
|
||||
{
|
||||
Log log_;
|
||||
log_.timeStamp = QDateTime::currentDateTimeUtc();
|
||||
log_.message = message;
|
||||
|
||||
Logger::instance()->log(log_);
|
||||
}
|
||||
|
||||
QString Logger::logFile() const
|
||||
{
|
||||
return _logFile.fileName();
|
||||
@ -264,7 +218,24 @@ void Logger::disableTemporaryFolderLogDir()
|
||||
void Logger::setLogRules(const QSet<QString> &rules)
|
||||
{
|
||||
_logRules = rules;
|
||||
QLoggingCategory::setFilterRules(rules.toList().join(QLatin1Char('\n')));
|
||||
QString tmp;
|
||||
QTextStream out(&tmp);
|
||||
for (const auto &p : rules) {
|
||||
out << p << QLatin1Char('\n');
|
||||
}
|
||||
qDebug() << tmp;
|
||||
QLoggingCategory::setFilterRules(tmp);
|
||||
}
|
||||
|
||||
void Logger::dumpCrashLog()
|
||||
{
|
||||
QFile logFile(QDir::tempPath() + QStringLiteral("/" APPLICATION_NAME "-crash.log"));
|
||||
if (logFile.open(QFile::WriteOnly)) {
|
||||
QTextStream out(&logFile);
|
||||
for (int i = 1; i <= CrashLogSize; ++i) {
|
||||
out << _crashLog[(_crashLogIndex + i) % CrashLogSize] << QLatin1Char('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool compressLog(const QString &originalName, const QString &targetName)
|
||||
|
||||
@ -27,12 +27,6 @@
|
||||
|
||||
namespace OCC {
|
||||
|
||||
struct Log
|
||||
{
|
||||
QDateTime timeStamp;
|
||||
QString message;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The Logger class
|
||||
* @ingroup libsync
|
||||
@ -41,16 +35,9 @@ class OWNCLOUDSYNC_EXPORT Logger : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
bool isNoop() const;
|
||||
bool isLoggingToFile() const;
|
||||
|
||||
void log(Log log);
|
||||
void doLog(const QString &log);
|
||||
void close();
|
||||
|
||||
static void mirallLog(const QString &message);
|
||||
|
||||
const QList<Log> &logs() const { return _logs; }
|
||||
void doLog(QtMsgType type, const QMessageLogContext &ctx, const QString &message);
|
||||
|
||||
static Logger *instance();
|
||||
|
||||
@ -107,9 +94,11 @@ public slots:
|
||||
|
||||
private:
|
||||
Logger(QObject *parent = nullptr);
|
||||
~Logger();
|
||||
QList<Log> _logs;
|
||||
bool _showTime = true;
|
||||
~Logger() override;
|
||||
|
||||
void close();
|
||||
void dumpCrashLog();
|
||||
|
||||
QFile _logFile;
|
||||
bool _doFileFlush = false;
|
||||
int _logExpire = 0;
|
||||
@ -119,6 +108,8 @@ private:
|
||||
QString _logDirectory;
|
||||
bool _temporaryFolderLogDir = false;
|
||||
QSet<QString> _logRules;
|
||||
QVector<QString> _crashLog;
|
||||
int _crashLogIndex = 0;
|
||||
};
|
||||
|
||||
} // namespace OCC
|
||||
|
||||
@ -113,8 +113,8 @@ bool RequestEtagJob::finished()
|
||||
if (httpCode == 207) {
|
||||
// Parse DAV response
|
||||
QXmlStreamReader reader(reply());
|
||||
reader.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("d", "DAV:"));
|
||||
QString etag;
|
||||
reader.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration(QStringLiteral("d"), QStringLiteral("DAV:")));
|
||||
QByteArray etag;
|
||||
while (!reader.atEnd()) {
|
||||
QXmlStreamReader::TokenType type = reader.readNext();
|
||||
if (type == QXmlStreamReader::StartElement && reader.namespaceUri() == QLatin1String("DAV:")) {
|
||||
@ -123,9 +123,9 @@ bool RequestEtagJob::finished()
|
||||
auto etagText = reader.readElementText();
|
||||
auto parsedTag = parseEtag(etagText.toUtf8());
|
||||
if (!parsedTag.isEmpty()) {
|
||||
etag += QString::fromUtf8(parsedTag);
|
||||
etag += parsedTag;
|
||||
} else {
|
||||
etag += etagText;
|
||||
etag += etagText.toUtf8();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -182,7 +182,11 @@ bool MkColJob::finished()
|
||||
qCInfo(lcMkColJob) << "MKCOL of" << reply()->request().url() << "FINISHED WITH STATUS"
|
||||
<< replyStatusString();
|
||||
|
||||
emit finished(reply()->error());
|
||||
if (reply()->error() != QNetworkReply::NoError) {
|
||||
Q_EMIT finishedWithError(reply());
|
||||
} else {
|
||||
Q_EMIT finishedWithoutError();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -273,9 +273,10 @@ public:
|
||||
void start() override;
|
||||
|
||||
signals:
|
||||
void finished(QNetworkReply::NetworkError);
|
||||
void finishedWithError(QNetworkReply *reply);
|
||||
void finishedWithoutError();
|
||||
|
||||
private slots:
|
||||
private:
|
||||
bool finished() override;
|
||||
};
|
||||
|
||||
@ -348,8 +349,8 @@ public:
|
||||
void start() override;
|
||||
|
||||
signals:
|
||||
void etagRetrieved(const QString &etag, const QDateTime &time);
|
||||
void finishedWithResult(const HttpResult<QString> &etag);
|
||||
void etagRetrieved(const QByteArray &etag, const QDateTime &time);
|
||||
void finishedWithResult(const HttpResult<QByteArray> &etag);
|
||||
|
||||
private slots:
|
||||
bool finished() override;
|
||||
@ -420,10 +421,10 @@ signals:
|
||||
* @param statusCode - the OCS status code: 100 (!) for success
|
||||
*/
|
||||
void etagResponseHeaderReceived(const QByteArray &value, int statusCode);
|
||||
|
||||
|
||||
/**
|
||||
* @brief desktopNotificationStatusReceived - signal to report if notifications are allowed
|
||||
* @param status - set desktop notifications allowed status
|
||||
* @param status - set desktop notifications allowed status
|
||||
*/
|
||||
void allowDesktopNotificationsChanged(bool isAllowed);
|
||||
|
||||
|
||||
@ -40,6 +40,7 @@
|
||||
#include <QTimer>
|
||||
#include <QObject>
|
||||
#include <QTimerEvent>
|
||||
#include <QRegularExpression>
|
||||
#include <qmath.h>
|
||||
|
||||
namespace OCC {
|
||||
@ -387,7 +388,7 @@ qint64 OwncloudPropagator::smallFileSize()
|
||||
return smallFileSize;
|
||||
}
|
||||
|
||||
void OwncloudPropagator::start(const SyncFileItemVector &items)
|
||||
void OwncloudPropagator::start(SyncFileItemVector &&items)
|
||||
{
|
||||
Q_ASSERT(std::is_sorted(items.begin(), items.end()));
|
||||
|
||||
@ -396,6 +397,26 @@ void OwncloudPropagator::start(const SyncFileItemVector &items)
|
||||
* In order to do that we loop over the items. (which are sorted by destination)
|
||||
* When we enter a directory, we can create the directory job and push it on the stack. */
|
||||
|
||||
const auto regex = syncOptions().fileRegex();
|
||||
if (regex.isValid()) {
|
||||
QSet<QStringRef> names;
|
||||
for (auto &i : items) {
|
||||
if (regex.match(i->_file).hasMatch()) {
|
||||
int index = -1;
|
||||
QStringRef ref;
|
||||
do {
|
||||
ref = i->_file.midRef(0, index);
|
||||
names.insert(ref);
|
||||
index = ref.lastIndexOf(QLatin1Char('/'));
|
||||
} while (index > 0);
|
||||
}
|
||||
}
|
||||
items.erase(std::remove_if(items.begin(), items.end(), [&names](auto i) {
|
||||
return !names.contains(QStringRef { &i->_file });
|
||||
}),
|
||||
items.end());
|
||||
}
|
||||
|
||||
_rootJob.reset(new PropagateRootDirectory(this));
|
||||
QStack<QPair<QString /* directory name */, PropagateDirectory * /* job */>> directories;
|
||||
directories.push(qMakePair(QString(), _rootJob.data()));
|
||||
@ -535,7 +556,6 @@ bool OwncloudPropagator::localFileNameClash(const QString &relFile)
|
||||
#ifdef Q_OS_MAC
|
||||
const QFileInfo fileInfo(file);
|
||||
if (!fileInfo.exists()) {
|
||||
qCWarning(lcPropagator) << "No valid fileinfo";
|
||||
return false;
|
||||
} else {
|
||||
// Need to normalize to composited form because of QTBUG-39622/QTBUG-55896
|
||||
|
||||
@ -421,7 +421,7 @@ public:
|
||||
|
||||
~OwncloudPropagator();
|
||||
|
||||
void start(const SyncFileItemVector &_syncedItems);
|
||||
void start(SyncFileItemVector &&_syncedItems);
|
||||
|
||||
const SyncOptions &syncOptions() const;
|
||||
void setSyncOptions(const SyncOptions &syncOptions);
|
||||
|
||||
@ -61,8 +61,7 @@ void PropagateRemoteMkdir::start()
|
||||
_job = new DeleteJob(propagator()->account(),
|
||||
propagator()->fullRemotePath(_item->_file),
|
||||
this);
|
||||
connect(static_cast<DeleteJob*>(_job.data()), &DeleteJob::finishedSignal,
|
||||
this, &PropagateRemoteMkdir::slotMkdir);
|
||||
connect(qobject_cast<DeleteJob *>(_job), &DeleteJob::finishedSignal, this, &PropagateRemoteMkdir::slotMkdir);
|
||||
_job->start();
|
||||
}
|
||||
|
||||
@ -76,7 +75,8 @@ void PropagateRemoteMkdir::slotStartMkcolJob()
|
||||
_job = new MkColJob(propagator()->account(),
|
||||
propagator()->fullRemotePath(_item->_file),
|
||||
this);
|
||||
connect(_job, SIGNAL(finished(QNetworkReply::NetworkError)), this, SLOT(slotMkcolJobFinished()));
|
||||
connect(qobject_cast<MkColJob *>(_job), &MkColJob::finishedWithError, this, &PropagateRemoteMkdir::slotMkcolJobFinished);
|
||||
connect(qobject_cast<MkColJob *>(_job), &MkColJob::finishedWithoutError, this, &PropagateRemoteMkdir::slotMkcolJobFinished);
|
||||
_job->start();
|
||||
}
|
||||
|
||||
@ -95,8 +95,8 @@ void PropagateRemoteMkdir::slotStartEncryptedMkcolJob(const QString &path, const
|
||||
propagator()->fullRemotePath(filename),
|
||||
{{"e2e-token", _uploadEncryptedHelper->folderToken() }},
|
||||
this);
|
||||
connect(job, qOverload<QNetworkReply::NetworkError>(&MkColJob::finished),
|
||||
this, &PropagateRemoteMkdir::slotMkcolJobFinished);
|
||||
connect(job, &MkColJob::finishedWithError, this, &PropagateRemoteMkdir::slotMkcolJobFinished);
|
||||
connect(job, &MkColJob::finishedWithoutError, this, &PropagateRemoteMkdir::slotMkcolJobFinished);
|
||||
_job = job;
|
||||
_job->start();
|
||||
}
|
||||
@ -137,36 +137,34 @@ void PropagateRemoteMkdir::finalizeMkColJob(QNetworkReply::NetworkError err, con
|
||||
return;
|
||||
}
|
||||
|
||||
if (_item->_fileId.isEmpty()) {
|
||||
// Owncloud 7.0.0 and before did not have a header with the file id.
|
||||
// (https://github.com/owncloud/core/issues/9000)
|
||||
// So we must get the file id using a PROPFIND
|
||||
// This is required so that we can detect moves even if the folder is renamed on the server
|
||||
// while files are still uploading
|
||||
propagator()->_activeJobList.append(this);
|
||||
auto propfindJob = new PropfindJob(propagator()->account(), jobPath, this);
|
||||
propfindJob->setProperties(QList<QByteArray>() << "http://owncloud.org/ns:id");
|
||||
QObject::connect(propfindJob, &PropfindJob::result, this, &PropagateRemoteMkdir::propfindResult);
|
||||
QObject::connect(propfindJob, &PropfindJob::finishedWithError, this, &PropagateRemoteMkdir::propfindError);
|
||||
propfindJob->start();
|
||||
_job = propfindJob;
|
||||
return;
|
||||
}
|
||||
propagator()->_activeJobList.append(this);
|
||||
auto propfindJob = new PropfindJob(_job->account(), _job->path(), this);
|
||||
propfindJob->setProperties({"http://owncloud.org/ns:permissions"});
|
||||
connect(propfindJob, &PropfindJob::result, this, [this, jobPath](const QVariantMap &result){
|
||||
propagator()->_activeJobList.removeOne(this);
|
||||
_item->_remotePerm = RemotePermissions::fromServerString(result.value(QStringLiteral("permissions")).toString());
|
||||
|
||||
if (!_uploadEncryptedHelper && !_item->_isEncrypted) {
|
||||
success();
|
||||
} else {
|
||||
// We still need to mark that folder encrypted in case we were uploading it as encrypted one
|
||||
// Another scenario, is we are creating a new folder because of move operation on an encrypted folder that works via remove + re-upload
|
||||
propagator()->_activeJobList.append(this);
|
||||
if (!_uploadEncryptedHelper && !_item->_isEncrypted) {
|
||||
success();
|
||||
} else {
|
||||
// We still need to mark that folder encrypted in case we were uploading it as encrypted one
|
||||
// Another scenario, is we are creating a new folder because of move operation on an encrypted folder that works via remove + re-upload
|
||||
propagator()->_activeJobList.append(this);
|
||||
|
||||
// We're expecting directory path in /Foo/Bar convention...
|
||||
Q_ASSERT(jobPath.startsWith('/') && !jobPath.endsWith('/'));
|
||||
// But encryption job expect it in Foo/Bar/ convention
|
||||
auto job = new OCC::EncryptFolderJob(propagator()->account(), propagator()->_journal, jobPath.mid(1), _item->_fileId, this);
|
||||
connect(job, &OCC::EncryptFolderJob::finished, this, &PropagateRemoteMkdir::slotEncryptFolderFinished);
|
||||
job->start();
|
||||
}
|
||||
// We're expecting directory path in /Foo/Bar convention...
|
||||
Q_ASSERT(jobPath.startsWith('/') && !jobPath.endsWith('/'));
|
||||
// But encryption job expect it in Foo/Bar/ convention
|
||||
auto job = new OCC::EncryptFolderJob(propagator()->account(), propagator()->_journal, jobPath.mid(1), _item->_fileId, this);
|
||||
connect(job, &OCC::EncryptFolderJob::finished, this, &PropagateRemoteMkdir::slotEncryptFolderFinished);
|
||||
job->start();
|
||||
}
|
||||
});
|
||||
connect(propfindJob, &PropfindJob::finishedWithError, this, [this]{
|
||||
// ignore the PROPFIND error
|
||||
propagator()->_activeJobList.removeOne(this);
|
||||
done(SyncFileItem::NormalError);
|
||||
});
|
||||
propfindJob->start();
|
||||
}
|
||||
|
||||
void PropagateRemoteMkdir::slotMkdir()
|
||||
@ -235,22 +233,6 @@ void PropagateRemoteMkdir::slotEncryptFolderFinished()
|
||||
success();
|
||||
}
|
||||
|
||||
void PropagateRemoteMkdir::propfindResult(const QVariantMap &result)
|
||||
{
|
||||
propagator()->_activeJobList.removeOne(this);
|
||||
if (result.contains("id")) {
|
||||
_item->_fileId = result["id"].toByteArray();
|
||||
}
|
||||
success();
|
||||
}
|
||||
|
||||
void PropagateRemoteMkdir::propfindError()
|
||||
{
|
||||
// ignore the PROPFIND error
|
||||
propagator()->_activeJobList.removeOne(this);
|
||||
done(SyncFileItem::Success);
|
||||
}
|
||||
|
||||
void PropagateRemoteMkdir::success()
|
||||
{
|
||||
// Never save the etag on first mkdir.
|
||||
|
||||
@ -54,8 +54,6 @@ private slots:
|
||||
void slotStartEncryptedMkcolJob(const QString &path, const QString &filename, quint64 size);
|
||||
void slotMkcolJobFinished();
|
||||
void slotEncryptFolderFinished();
|
||||
void propfindResult(const QVariantMap &);
|
||||
void propfindError();
|
||||
void success();
|
||||
|
||||
private:
|
||||
|
||||
@ -418,7 +418,7 @@ private slots:
|
||||
void slotPropfindFinishedWithError();
|
||||
void slotPropfindIterate(const QString &name, const QMap<QString, QString> &properties);
|
||||
void slotDeleteJobFinished();
|
||||
void slotMkColFinished(QNetworkReply::NetworkError);
|
||||
void slotMkColFinished();
|
||||
void slotPutFinished();
|
||||
void slotMoveJobFinished();
|
||||
void slotUploadProgress(qint64, qint64);
|
||||
|
||||
@ -249,13 +249,15 @@ void PropagateUploadFileNG::startNewUpload()
|
||||
headers["OC-Total-Length"] = QByteArray::number(_fileToUpload._size);
|
||||
auto job = new MkColJob(propagator()->account(), chunkUrl(), headers, this);
|
||||
|
||||
connect(job, SIGNAL(finished(QNetworkReply::NetworkError)),
|
||||
this, SLOT(slotMkColFinished(QNetworkReply::NetworkError)));
|
||||
connect(job, &MkColJob::finishedWithError,
|
||||
this, &PropagateUploadFileNG::slotMkColFinished);
|
||||
connect(job, &MkColJob::finishedWithoutError,
|
||||
this, &PropagateUploadFileNG::slotMkColFinished);
|
||||
connect(job, &QObject::destroyed, this, &PropagateUploadFileCommon::slotJobDestroyed);
|
||||
job->start();
|
||||
}
|
||||
|
||||
void PropagateUploadFileNG::slotMkColFinished(QNetworkReply::NetworkError)
|
||||
void PropagateUploadFileNG::slotMkColFinished()
|
||||
{
|
||||
propagator()->_activeJobList.removeOne(this);
|
||||
auto job = qobject_cast<MkColJob *>(sender());
|
||||
|
||||
@ -434,7 +434,7 @@ void SyncEngine::startSync()
|
||||
}
|
||||
|
||||
if (s_anySyncRunning || _syncRunning) {
|
||||
ASSERT(false);
|
||||
ASSERT(false)
|
||||
return;
|
||||
}
|
||||
|
||||
@ -452,7 +452,7 @@ void SyncEngine::startSync()
|
||||
if (!QDir(_localPath).exists()) {
|
||||
_anotherSyncNeeded = DelayedFollowUp;
|
||||
// No _tr, it should only occur in non-mirall
|
||||
syncError("Unable to find local sync folder.");
|
||||
Q_EMIT syncError(QStringLiteral("Unable to find local sync folder."));
|
||||
finalize(false);
|
||||
return;
|
||||
}
|
||||
@ -465,11 +465,11 @@ void SyncEngine::startSync()
|
||||
qCWarning(lcEngine()) << "Too little space available at" << _localPath << ". Have"
|
||||
<< freeBytes << "bytes and require at least" << minFree << "bytes";
|
||||
_anotherSyncNeeded = DelayedFollowUp;
|
||||
syncError(tr("Only %1 are available, need at least %2 to start",
|
||||
Q_EMIT syncError(tr("Only %1 are available, need at least %2 to start",
|
||||
"Placeholders are postfixed with file sizes using Utility::octetsToString()")
|
||||
.arg(
|
||||
Utility::octetsToString(freeBytes),
|
||||
Utility::octetsToString(minFree)));
|
||||
.arg(
|
||||
Utility::octetsToString(freeBytes),
|
||||
Utility::octetsToString(minFree)));
|
||||
finalize(false);
|
||||
return;
|
||||
} else {
|
||||
@ -498,7 +498,7 @@ void SyncEngine::startSync()
|
||||
// This creates the DB if it does not exist yet.
|
||||
if (!_journal->open()) {
|
||||
qCWarning(lcEngine) << "No way to create a sync journal!";
|
||||
syncError(tr("Unable to open or create the local sync database. Make sure you have write access in the sync folder."));
|
||||
Q_EMIT syncError(tr("Unable to open or create the local sync database. Make sure you have write access in the sync folder."));
|
||||
finalize(false);
|
||||
return;
|
||||
// database creation error!
|
||||
@ -514,7 +514,7 @@ void SyncEngine::startSync()
|
||||
_lastLocalDiscoveryStyle = _localDiscoveryStyle;
|
||||
|
||||
if (_syncOptions._vfs->mode() == Vfs::WithSuffix && _syncOptions._vfs->fileSuffix().isEmpty()) {
|
||||
syncError(tr("Using virtual files with suffix, but suffix is not set"));
|
||||
Q_EMIT syncError(tr("Using virtual files with suffix, but suffix is not set"));
|
||||
finalize(false);
|
||||
return;
|
||||
}
|
||||
@ -526,7 +526,7 @@ void SyncEngine::startSync()
|
||||
qCInfo(lcEngine) << (usingSelectiveSync ? "Using Selective Sync" : "NOT Using Selective Sync");
|
||||
} else {
|
||||
qCWarning(lcEngine) << "Could not retrieve selective sync list from DB";
|
||||
syncError(tr("Unable to read the blacklist from the local database"));
|
||||
Q_EMIT syncError(tr("Unable to read the blacklist from the local database"));
|
||||
finalize(false);
|
||||
return;
|
||||
}
|
||||
@ -557,7 +557,7 @@ void SyncEngine::startSync()
|
||||
_discoveryPhase->setSelectiveSyncWhiteList(_journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList, &ok));
|
||||
if (!ok) {
|
||||
qCWarning(lcEngine) << "Unable to read selective sync list, aborting.";
|
||||
syncError(tr("Unable to read from the sync journal."));
|
||||
Q_EMIT syncError(tr("Unable to read from the sync journal."));
|
||||
finalize(false);
|
||||
return;
|
||||
}
|
||||
@ -581,7 +581,7 @@ void SyncEngine::startSync()
|
||||
connect(_discoveryPhase.data(), &DiscoveryPhase::itemDiscovered, this, &SyncEngine::slotItemDiscovered);
|
||||
connect(_discoveryPhase.data(), &DiscoveryPhase::newBigFolder, this, &SyncEngine::newBigFolder);
|
||||
connect(_discoveryPhase.data(), &DiscoveryPhase::fatalError, this, [this](const QString &errorString) {
|
||||
syncError(errorString);
|
||||
Q_EMIT syncError(errorString);
|
||||
finalize(false);
|
||||
});
|
||||
connect(_discoveryPhase.data(), &DiscoveryPhase::finished, this, &SyncEngine::slotDiscoveryFinished);
|
||||
@ -614,7 +614,7 @@ void SyncEngine::slotFolderDiscovered(bool local, const QString &folder)
|
||||
emit transmissionProgress(*_progressInfo);
|
||||
}
|
||||
|
||||
void SyncEngine::slotRootEtagReceived(const QString &e, const QDateTime &time)
|
||||
void SyncEngine::slotRootEtagReceived(const QByteArray &e, const QDateTime &time)
|
||||
{
|
||||
if (_remoteRootEtag.isEmpty()) {
|
||||
qCDebug(lcEngine) << "Root etag:" << e;
|
||||
@ -640,7 +640,7 @@ void SyncEngine::slotDiscoveryFinished()
|
||||
// Sanity check
|
||||
if (!_journal->open()) {
|
||||
qCWarning(lcEngine) << "Bailing out, DB failure";
|
||||
syncError(tr("Cannot open the sync journal"));
|
||||
Q_EMIT syncError(tr("Cannot open the sync journal"));
|
||||
finalize(false);
|
||||
return;
|
||||
} else {
|
||||
@ -723,10 +723,9 @@ void SyncEngine::slotDiscoveryFinished()
|
||||
|
||||
// Emit the started signal only after the propagator has been set up.
|
||||
if (_needsUpdate)
|
||||
emit(started());
|
||||
Q_EMIT started();
|
||||
|
||||
_propagator->start(_syncItems);
|
||||
_syncItems.clear();
|
||||
_propagator->start(std::move(_syncItems));
|
||||
|
||||
qCInfo(lcEngine) << "#### Post-Reconcile end #################################################### " << _stopWatch.addLapTime(QStringLiteral("Post-Reconcile Finished")) << "ms";
|
||||
};
|
||||
@ -1078,7 +1077,7 @@ void SyncEngine::abort()
|
||||
disconnect(_discoveryPhase.data(), nullptr, this, nullptr);
|
||||
_discoveryPhase.take()->deleteLater();
|
||||
|
||||
syncError(tr("Aborted"));
|
||||
Q_EMIT syncError(tr("Aborted"));
|
||||
finalize(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,7 +138,7 @@ public:
|
||||
|
||||
signals:
|
||||
// During update, before reconcile
|
||||
void rootEtag(const QString &, const QDateTime &);
|
||||
void rootEtag(const QByteArray &, const QDateTime &);
|
||||
|
||||
// after the above signals. with the items that actually need propagating
|
||||
void aboutToPropagate(SyncFileItemVector &);
|
||||
@ -174,7 +174,7 @@ signals:
|
||||
|
||||
private slots:
|
||||
void slotFolderDiscovered(bool local, const QString &folder);
|
||||
void slotRootEtagReceived(const QString &, const QDateTime &time);
|
||||
void slotRootEtagReceived(const QByteArray &, const QDateTime &time);
|
||||
|
||||
/** When the discovery phase discovers an item */
|
||||
void slotItemDiscovered(const SyncFileItemPtr &item);
|
||||
@ -234,7 +234,7 @@ private:
|
||||
bool _syncRunning;
|
||||
QString _localPath;
|
||||
QString _remotePath;
|
||||
QString _remoteRootEtag;
|
||||
QByteArray _remoteRootEtag;
|
||||
SyncJournalDb *_journal;
|
||||
QScopedPointer<DiscoveryPhase> _discoveryPhase;
|
||||
QSharedPointer<OwncloudPropagator> _propagator;
|
||||
|
||||
73
src/libsync/syncoptions.cpp
Normal file
73
src/libsync/syncoptions.cpp
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) by Olivier Goffart <ogoffart@woboq.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 "syncoptions.h"
|
||||
#include "common/utility.h"
|
||||
|
||||
#include <QRegularExpression>
|
||||
|
||||
using namespace OCC;
|
||||
|
||||
SyncOptions::SyncOptions()
|
||||
: _vfs(new VfsOff)
|
||||
{
|
||||
}
|
||||
|
||||
SyncOptions::~SyncOptions() = default;
|
||||
|
||||
void SyncOptions::fillFromEnvironmentVariables()
|
||||
{
|
||||
QByteArray chunkSizeEnv = qgetenv("OWNCLOUD_CHUNK_SIZE");
|
||||
if (!chunkSizeEnv.isEmpty())
|
||||
_initialChunkSize = chunkSizeEnv.toUInt();
|
||||
|
||||
QByteArray minChunkSizeEnv = qgetenv("OWNCLOUD_MIN_CHUNK_SIZE");
|
||||
if (!minChunkSizeEnv.isEmpty())
|
||||
_minChunkSize = minChunkSizeEnv.toUInt();
|
||||
|
||||
QByteArray maxChunkSizeEnv = qgetenv("OWNCLOUD_MAX_CHUNK_SIZE");
|
||||
if (!maxChunkSizeEnv.isEmpty())
|
||||
_maxChunkSize = maxChunkSizeEnv.toUInt();
|
||||
|
||||
QByteArray targetChunkUploadDurationEnv = qgetenv("OWNCLOUD_TARGET_CHUNK_UPLOAD_DURATION");
|
||||
if (!targetChunkUploadDurationEnv.isEmpty())
|
||||
_targetChunkUploadDuration = std::chrono::milliseconds(targetChunkUploadDurationEnv.toUInt());
|
||||
|
||||
int maxParallel = qgetenv("OWNCLOUD_MAX_PARALLEL").toInt();
|
||||
if (maxParallel > 0)
|
||||
_parallelNetworkJobs = maxParallel;
|
||||
}
|
||||
|
||||
void SyncOptions::verifyChunkSizes()
|
||||
{
|
||||
_minChunkSize = qMin(_minChunkSize, _initialChunkSize);
|
||||
_maxChunkSize = qMax(_maxChunkSize, _initialChunkSize);
|
||||
}
|
||||
|
||||
QRegularExpression SyncOptions::fileRegex() const
|
||||
{
|
||||
return _fileRegex;
|
||||
}
|
||||
|
||||
void SyncOptions::setFilePattern(const QString &pattern)
|
||||
{
|
||||
// full match or a path ending with this pattern
|
||||
setPathPattern(QStringLiteral("(^|/|\\\\)") + pattern + QLatin1Char('$'));
|
||||
}
|
||||
|
||||
void SyncOptions::setPathPattern(const QString &pattern)
|
||||
{
|
||||
_fileRegex.setPatternOptions(Utility::fsCasePreserving() ? QRegularExpression::CaseInsensitiveOption : QRegularExpression::NoPatternOption);
|
||||
_fileRegex.setPattern(pattern);
|
||||
}
|
||||
@ -15,21 +15,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "owncloudlib.h"
|
||||
#include <QString>
|
||||
#include <QSharedPointer>
|
||||
#include <chrono>
|
||||
#include "common/vfs.h"
|
||||
|
||||
#include <QRegularExpression>
|
||||
#include <QSharedPointer>
|
||||
#include <QString>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
|
||||
namespace OCC {
|
||||
|
||||
/**
|
||||
* Value class containing the options given to the sync engine
|
||||
*/
|
||||
struct OWNCLOUDSYNC_EXPORT SyncOptions
|
||||
class OWNCLOUDSYNC_EXPORT SyncOptions
|
||||
{
|
||||
SyncOptions()
|
||||
: _vfs(new VfsOff)
|
||||
{}
|
||||
public:
|
||||
SyncOptions();
|
||||
~SyncOptions();
|
||||
|
||||
/** Maximum size (in Bytes) a folder can have without asking for confirmation.
|
||||
* -1 means infinite */
|
||||
@ -67,7 +71,45 @@ struct OWNCLOUDSYNC_EXPORT SyncOptions
|
||||
|
||||
/** The maximum number of active jobs in parallel */
|
||||
int _parallelNetworkJobs = 6;
|
||||
|
||||
/** Reads settings from env vars where available.
|
||||
*
|
||||
* Currently reads _initialChunkSize, _minChunkSize, _maxChunkSize,
|
||||
* _targetChunkUploadDuration, _parallelNetworkJobs.
|
||||
*/
|
||||
void fillFromEnvironmentVariables();
|
||||
|
||||
/** Ensure min <= initial <= max
|
||||
*
|
||||
* Previously min/max chunk size values didn't exist, so users might
|
||||
* have setups where the chunk size exceeds the new min/max default
|
||||
* values. To cope with this, adjust min/max to always include the
|
||||
* initial chunk size value.
|
||||
*/
|
||||
void verifyChunkSizes();
|
||||
|
||||
|
||||
/** A regular expression to match file names
|
||||
* If no pattern is provided the default is an invalid regular expression.
|
||||
*/
|
||||
QRegularExpression fileRegex() const;
|
||||
|
||||
/**
|
||||
* A pattern like *.txt, matching only file names
|
||||
*/
|
||||
void setFilePattern(const QString &pattern);
|
||||
|
||||
/**
|
||||
* A pattern like /own.*\/.*txt matching the full path
|
||||
*/
|
||||
void setPathPattern(const QString &pattern);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Only sync files that mathc the expression
|
||||
* Invalid pattern by default.
|
||||
*/
|
||||
QRegularExpression _fileRegex = QRegularExpression(QStringLiteral("("));
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -468,6 +468,8 @@ QString Theme::about() const
|
||||
|
||||
devString += tr("<p><small>Using virtual files plugin: %1</small></p>")
|
||||
.arg(Vfs::modeToString(bestAvailableVfsMode()));
|
||||
devString += tr("<br>%1")
|
||||
.arg(QSysInfo::productType() % QLatin1Char('-') % QSysInfo::kernelVersion());
|
||||
|
||||
return devString;
|
||||
}
|
||||
|
||||
@ -68,12 +68,12 @@ if (WIN32)
|
||||
${CMAKE_BINARY_DIR}/src/libsync/vfs/cfapi
|
||||
)
|
||||
|
||||
nextcloud_add_test(LongWinPath)
|
||||
nextcloud_add_test(SyncCfApi)
|
||||
elseif(LINUX) # elseif(LINUX OR APPLE)
|
||||
nextcloud_add_test(SyncXAttr)
|
||||
endif()
|
||||
|
||||
nextcloud_add_test(LongPath)
|
||||
nextcloud_add_benchmark(LargeSync)
|
||||
|
||||
nextcloud_add_test(Account)
|
||||
|
||||
@ -243,6 +243,15 @@ QString FileInfo::path() const
|
||||
return (parentPath.isEmpty() ? QString() : (parentPath + QLatin1Char('/'))) + name;
|
||||
}
|
||||
|
||||
QString FileInfo::absolutePath() const
|
||||
{
|
||||
if (parentPath.endsWith(QLatin1Char('/'))) {
|
||||
return parentPath + name;
|
||||
} else {
|
||||
return parentPath + QLatin1Char('/') + name;
|
||||
}
|
||||
}
|
||||
|
||||
void FileInfo::fixupParentPathRecursively()
|
||||
{
|
||||
auto p = path();
|
||||
@ -273,7 +282,7 @@ FakePropfindReply::FakePropfindReply(FileInfo &remoteRootFileInfo, QNetworkAcces
|
||||
QMetaObject::invokeMethod(this, "respond404", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
QString prefix = request.url().path().left(request.url().path().size() - fileName.size());
|
||||
const QString prefix = request.url().path().left(request.url().path().size() - fileName.size());
|
||||
|
||||
// Don't care about the request and just return a full propfind
|
||||
const QString davUri { QStringLiteral("DAV:") };
|
||||
@ -288,11 +297,12 @@ FakePropfindReply::FakePropfindReply(FileInfo &remoteRootFileInfo, QNetworkAcces
|
||||
auto writeFileResponse = [&](const FileInfo &fileInfo) {
|
||||
xml.writeStartElement(davUri, QStringLiteral("response"));
|
||||
|
||||
QString url = prefix + QString::fromUtf8(QUrl::toPercentEncoding(fileInfo.path(), "/"));
|
||||
auto url = QString::fromUtf8(QUrl::toPercentEncoding(fileInfo.absolutePath(), "/"));
|
||||
if (!url.endsWith(QChar('/'))) {
|
||||
url.append(QChar('/'));
|
||||
}
|
||||
xml.writeTextElement(davUri, QStringLiteral("href"), url);
|
||||
const auto href = OCC::Utility::concatUrlPath(prefix, url).path();
|
||||
xml.writeTextElement(davUri, QStringLiteral("href"), href);
|
||||
xml.writeStartElement(davUri, QStringLiteral("propstat"));
|
||||
xml.writeStartElement(davUri, QStringLiteral("prop"));
|
||||
|
||||
@ -488,9 +498,11 @@ FakeGetReply::FakeGetReply(FileInfo &remoteRootFileInfo, QNetworkAccessManager::
|
||||
QString fileName = getFilePathFromUrl(request.url());
|
||||
Q_ASSERT(!fileName.isEmpty());
|
||||
fileInfo = remoteRootFileInfo.find(fileName);
|
||||
if (!fileInfo)
|
||||
qWarning() << "Could not find file" << fileName << "on the remote";
|
||||
QMetaObject::invokeMethod(this, "respond", Qt::QueuedConnection);
|
||||
if (!fileInfo) {
|
||||
qDebug() << "meh;";
|
||||
}
|
||||
Q_ASSERT_X(fileInfo, Q_FUNC_INFO, "Could not find file on the remote");
|
||||
QMetaObject::invokeMethod(this, &FakeGetReply::respond, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void FakeGetReply::respond()
|
||||
@ -740,7 +752,7 @@ FakeErrorReply::FakeErrorReply(QNetworkAccessManager::Operation op, const QNetwo
|
||||
open(QIODevice::ReadOnly);
|
||||
setAttribute(QNetworkRequest::HttpStatusCodeAttribute, httpErrorCode);
|
||||
setError(InternalServerError, QStringLiteral("Internal Server Fake Error"));
|
||||
QMetaObject::invokeMethod(this, "respond", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(this, &FakeErrorReply::respond, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void FakeErrorReply::respond()
|
||||
|
||||
@ -143,6 +143,7 @@ public:
|
||||
}
|
||||
|
||||
QString path() const;
|
||||
QString absolutePath() const;
|
||||
|
||||
void fixupParentPathRecursively();
|
||||
|
||||
|
||||
@ -322,7 +322,7 @@ private slots:
|
||||
QCOMPARE(check_file_traversal("subdir/.sync_5bdd60bdfcfa.db"), CSYNC_FILE_SILENTLY_EXCLUDED);
|
||||
|
||||
/* Other builtin excludes */
|
||||
QCOMPARE(check_file_traversal("foo/Desktop.ini"), CSYNC_NOT_EXCLUDED);
|
||||
QCOMPARE(check_file_traversal("foo/Desktop.ini"), CSYNC_FILE_SILENTLY_EXCLUDED);
|
||||
QCOMPARE(check_file_traversal("Desktop.ini"), CSYNC_FILE_SILENTLY_EXCLUDED);
|
||||
|
||||
/* pattern ]*.directory - ignore and remove */
|
||||
|
||||
@ -17,7 +17,8 @@ using namespace OCC;
|
||||
// pass combination of FILE_SHARE_READ, FILE_SHARE_WRITE, FILE_SHARE_DELETE
|
||||
HANDLE makeHandle(const QString &file, int shareMode)
|
||||
{
|
||||
const wchar_t *wuri = reinterpret_cast<const wchar_t *>(file.utf16());
|
||||
const auto fName = FileSystem::longWinPath(file);
|
||||
const wchar_t *wuri = reinterpret_cast<const wchar_t *>(fName.utf16());
|
||||
auto handle = CreateFileW(
|
||||
wuri,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
@ -39,6 +40,7 @@ class TestLockedFiles : public QObject
|
||||
private slots:
|
||||
void testBasicLockFileWatcher()
|
||||
{
|
||||
QTemporaryDir tmp;
|
||||
int count = 0;
|
||||
QString file;
|
||||
|
||||
@ -46,12 +48,16 @@ private slots:
|
||||
watcher.setCheckInterval(std::chrono::milliseconds(50));
|
||||
connect(&watcher, &LockWatcher::fileUnlocked, &watcher, [&](const QString &f) { ++count; file = f; });
|
||||
|
||||
QString tmpFile;
|
||||
const QString tmpFile = tmp.path() + QString::fromUtf8("/alonglonglonglong/blonglonglonglong/clonglonglonglong/dlonglonglonglong/"
|
||||
"elonglonglonglong/flonglonglonglong/glonglonglonglong/hlonglonglonglong/ilonglonglonglong/"
|
||||
"jlonglonglonglong/klonglonglonglong/llonglonglonglong/mlonglonglonglong/nlonglonglonglong/"
|
||||
"olonglonglonglong/file🐷.txt");
|
||||
{
|
||||
QTemporaryFile tmp;
|
||||
tmp.setAutoRemove(false);
|
||||
tmp.open();
|
||||
tmpFile = tmp.fileName();
|
||||
// use a long file path to ensure we handle that correctly
|
||||
QVERIFY(QFileInfo(tmpFile).dir().mkpath("."));
|
||||
QFile tmp(tmpFile);
|
||||
QVERIFY(tmp.open(QFile::WriteOnly));
|
||||
QVERIFY(tmp.write("ownCLoud"));
|
||||
}
|
||||
QVERIFY(QFile::exists(tmpFile));
|
||||
|
||||
@ -91,7 +97,7 @@ private slots:
|
||||
QCOMPARE(file, tmpFile);
|
||||
QVERIFY(!watcher.contains(tmpFile));
|
||||
#endif
|
||||
QFile::remove(tmpFile);
|
||||
QVERIFY(tmp.remove());
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
@ -18,7 +18,10 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include "common/filesystembase.h"
|
||||
#include "csync/csync.h"
|
||||
#include "csync/vio/csync_vio_local.h"
|
||||
|
||||
#include <QTemporaryFile>
|
||||
#include <QTest>
|
||||
|
||||
|
||||
@ -27,6 +30,7 @@ class TestLongWindowsPath : public QObject
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
#ifdef Q_OS_WIN
|
||||
void check_long_win_path()
|
||||
{
|
||||
{
|
||||
@ -81,7 +85,58 @@ private Q_SLOTS:
|
||||
// printf( "YYYYYYYYYYYY %ld\n", strlen(new_long));
|
||||
QCOMPARE(new_long.length(), 286);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void testLongPathStat_data()
|
||||
{
|
||||
QTest::addColumn<QString>("name");
|
||||
|
||||
QTest::newRow("long") << QStringLiteral("/alonglonglonglong/blonglonglonglong/clonglonglonglong/dlonglonglonglong/"
|
||||
"elonglonglonglong/flonglonglonglong/glonglonglonglong/hlonglonglonglong/ilonglonglonglong/"
|
||||
"jlonglonglonglong/klonglonglonglong/llonglonglonglong/mlonglonglonglong/nlonglonglonglong/"
|
||||
"olonglonglonglong/file.txt");
|
||||
QTest::newRow("long emoji") << QString::fromUtf8("/alonglonglonglong/blonglonglonglong/clonglonglonglong/dlonglonglonglong/"
|
||||
"elonglonglonglong/flonglonglonglong/glonglonglonglong/hlonglonglonglong/ilonglonglonglong/"
|
||||
"jlonglonglonglong/klonglonglonglong/llonglonglonglong/mlonglonglonglong/nlonglonglonglong/"
|
||||
"olonglonglonglong/file🐷.txt");
|
||||
QTest::newRow("long russian") << QString::fromUtf8("/alonglonglonglong/blonglonglonglong/clonglonglonglong/dlonglonglonglong/"
|
||||
"elonglonglonglong/flonglonglonglong/glonglonglonglong/hlonglonglonglong/ilonglonglonglong/"
|
||||
"jlonglonglonglong/klonglonglonglong/llonglonglonglong/mlonglonglonglong/nlonglonglonglong/"
|
||||
"olonglonglonglong/собственное.txt");
|
||||
QTest::newRow("long arabic") << QString::fromUtf8("/alonglonglonglong/blonglonglonglong/clonglonglonglong/dlonglonglonglong/"
|
||||
"elonglonglonglong/flonglonglonglong/glonglonglonglong/hlonglonglonglong/ilonglonglonglong/"
|
||||
"jlonglonglonglong/klonglonglonglong/llonglonglonglong/mlonglonglonglong/nlonglonglonglong/"
|
||||
"olonglonglonglong/السحاب.txt");
|
||||
QTest::newRow("long chinese") << QString::fromUtf8("/alonglonglonglong/blonglonglonglong/clonglonglonglong/dlonglonglonglong/"
|
||||
"elonglonglonglong/flonglonglonglong/glonglonglonglong/hlonglonglonglong/ilonglonglonglong/"
|
||||
"jlonglonglonglong/klonglonglonglong/llonglonglonglong/mlonglonglonglong/nlonglonglonglong/"
|
||||
"olonglonglonglong/自己的云.txt");
|
||||
}
|
||||
|
||||
void testLongPathStat()
|
||||
{
|
||||
QTemporaryDir tmp;
|
||||
QFETCH(QString, name);
|
||||
const QFileInfo longPath(tmp.path() + name);
|
||||
|
||||
const auto data = QByteArrayLiteral("hello");
|
||||
qDebug() << longPath;
|
||||
QVERIFY(longPath.dir().mkpath("."));
|
||||
|
||||
QFile file(longPath.filePath());
|
||||
QVERIFY(file.open(QFile::WriteOnly));
|
||||
QVERIFY(file.write(data.constData()) == data.size());
|
||||
file.close();
|
||||
|
||||
csync_file_stat_t buf;
|
||||
QVERIFY(csync_vio_local_stat(longPath.filePath(), &buf) != -1);
|
||||
QVERIFY(buf.size == data.size());
|
||||
QVERIFY(buf.size == longPath.size());
|
||||
|
||||
QVERIFY(tmp.remove());
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(TestLongWindowsPath)
|
||||
#include "testlongwinpath.moc"
|
||||
#include "testlongpath.moc"
|
||||
@ -136,8 +136,6 @@ private slots:
|
||||
q2.prepare("SELECT * FROM addresses");
|
||||
SqlQuery q3("SELECT * FROM addresses", _db);
|
||||
SqlQuery q4;
|
||||
SqlQuery q5;
|
||||
PreparedSqlQueryRAII testQuery(&q5, "SELECT * FROM addresses", _db);
|
||||
db.reset();
|
||||
}
|
||||
|
||||
|
||||
@ -336,7 +336,7 @@ private slots:
|
||||
});
|
||||
|
||||
// For directly editing the remote checksum
|
||||
auto &remoteInfo = dynamic_cast<FileInfo &>(fakeFolder.remoteModifier());
|
||||
auto &remoteInfo = fakeFolder.remoteModifier();
|
||||
|
||||
// Base mtime with no ms content (filesystem is seconds only)
|
||||
auto mtime = QDateTime::currentDateTimeUtc().addDays(-4);
|
||||
|
||||
@ -117,7 +117,7 @@ private slots:
|
||||
{
|
||||
SyncJournalFileRecord record;
|
||||
record._path = "foo-nochecksum";
|
||||
record._remotePerm = RemotePermissions();
|
||||
record._remotePerm = RemotePermissions::fromDbValue("RW");
|
||||
record._modtime = Utility::qDateTimeToTime_t(QDateTime::currentDateTimeUtc());
|
||||
|
||||
QVERIFY(_db.setFileRecord(record));
|
||||
@ -214,6 +214,7 @@ private slots:
|
||||
record._path = path;
|
||||
record._type = type;
|
||||
record._etag = initialEtag;
|
||||
record._remotePerm = RemotePermissions::fromDbValue("RW");
|
||||
_db.setFileRecord(record);
|
||||
};
|
||||
auto getEtag = [&](const QByteArray &path) {
|
||||
@ -275,6 +276,7 @@ private slots:
|
||||
auto makeEntry = [&](const QByteArray &path) {
|
||||
SyncJournalFileRecord record;
|
||||
record._path = path;
|
||||
record._remotePerm = RemotePermissions::fromDbValue("RW");
|
||||
_db.setFileRecord(record);
|
||||
};
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user