Move the SocketApi business logic to a libsync SyncFileStatusTracker class

This will allow testing this code and avoid going through too many
layers to get notified and a file status changed.
This commit is contained in:
Jocelyn Turcotte 2016-03-17 12:26:44 +01:00
parent dac4bd8370
commit da7b9916e5
9 changed files with 296 additions and 234 deletions

View File

@ -34,6 +34,7 @@
#include <QDebug>
#include <QDesktopServices>
#include <QDir>
#include <QListWidgetItem>
#include <QMessageBox>
#include <QAction>

View File

@ -99,8 +99,6 @@ Folder::Folder(const FolderDefinition& definition,
connect(_engine.data(), SIGNAL(rootEtag(QString)), this, SLOT(etagRetreivedFromSyncEngine(QString)));
connect(_engine.data(), SIGNAL(treeWalkResult(const SyncFileItemVector&)),
this, SLOT(slotThreadTreeWalkResult(const SyncFileItemVector&)), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(aboutToPropagate(SyncFileItemVector&)),
this, SLOT(slotAboutToPropagate(SyncFileItemVector&)));
connect(_engine.data(), SIGNAL(started()), SLOT(slotSyncStarted()), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(finished(bool)), SLOT(slotSyncFinished(bool)), Qt::QueuedConnection);
@ -595,71 +593,11 @@ void Folder::slotWatchedPathChanged(const QString& path)
}
}
/**
* Whether this item should get an ERROR icon through the Socket API.
*
* The Socket API should only present serious, permanent errors to the user.
* In particular SoftErrors should just retain their 'needs to be synced'
* icon as the problem is most likely going to resolve itself quickly and
* automatically.
*/
static bool showErrorInSocketApi(const SyncFileItem& item)
{
const auto status = item._status;
return status == SyncFileItem::NormalError
|| status == SyncFileItem::FatalError;
}
static void addErroredSyncItemPathsToList(const SyncFileItemVector& items, QSet<QString>* set) {
foreach (const SyncFileItemPtr &item, items) {
if (showErrorInSocketApi(*item)) {
set->insert(item->_file);
}
}
}
void Folder::slotThreadTreeWalkResult(const SyncFileItemVector& items)
{
addErroredSyncItemPathsToList(items, &this->_stateLastSyncItemsWithErrorNew);
_syncResult.setSyncFileItemVector(items);
}
void Folder::slotAboutToPropagate(SyncFileItemVector& items)
{
addErroredSyncItemPathsToList(items, &this->_stateLastSyncItemsWithErrorNew);
}
bool Folder::estimateState(QString fn, csync_ftw_type_e t, SyncFileStatus* s)
{
if (t == CSYNC_FTW_TYPE_DIR) {
if (Utility::doesSetContainPrefix(_stateLastSyncItemsWithError, fn)) {
qDebug() << Q_FUNC_INFO << "Folder has error" << fn;
s->set(SyncFileStatus::STATUS_ERROR);
return true;
}
// If sync is running, check _syncedItems, possibly give it STATUS_EVAL (=syncing down)
if (_engine->isSyncRunning()) {
if (_engine->estimateState(fn, t, s)) {
return true;
}
}
return false;
} else if ( t== CSYNC_FTW_TYPE_FILE) {
// check if errorList has the directory/file
if (Utility::doesSetContainPrefix(_stateLastSyncItemsWithError, fn)) {
s->set(SyncFileStatus::STATUS_ERROR);
return true;
}
// If sync running: _syncedItems -> SyncingState
if (_engine->isSyncRunning()) {
if (_engine->estimateState(fn, t, s)) {
return true;
}
}
}
return false;
}
void Folder::saveToSettings() const
{
@ -873,10 +811,6 @@ void Folder::slotSyncFinished(bool success)
// This is for sync state calculation
_stateLastSyncItemsWithError = _stateLastSyncItemsWithErrorNew;
_stateLastSyncItemsWithErrorNew.clear();
if (_csyncError) {
_syncResult.setStatus(SyncResult::Error);
qDebug() << " ** error Strings: " << _errors;
@ -972,10 +906,6 @@ void Folder::slotTransmissionProgress(const ProgressInfo &pi)
// a item is completed: count the errors and forward to the ProgressDispatcher
void Folder::slotItemCompleted(const SyncFileItem &item, const PropagatorJob& job)
{
if (showErrorInSocketApi(item)) {
_stateLastSyncItemsWithErrorNew.insert(item._file);
}
if (Progress::isWarningKind(item._status)) {
// Count all error conditions.
_syncResult.setWarnCount(_syncResult.warnCount()+1);

View File

@ -26,16 +26,9 @@
#include <csync.h>
#include <QDir>
#include <QHash>
#include <QSet>
#include <QObject>
#include <QStringList>
#include <QDebug>
#include <QTimer>
#include <qelapsedtimer.h>
class QThread;
class QSettings;
@ -181,8 +174,7 @@ public:
// Used by the Socket API
SyncJournalDb *journalDb() { return &_journal; }
bool estimateState(QString fn, csync_ftw_type_e t, SyncFileStatus* s);
SyncEngine &syncEngine() { return *_engine; }
RequestEtagJob *etagJob() { return _requestEtagJob; }
qint64 msecSinceLastSync() const { return _timeSinceLastSyncDone.elapsed(); }
@ -262,7 +254,6 @@ private slots:
void etagRetreived(const QString &);
void etagRetreivedFromSyncEngine(const QString &);
void slotAboutToPropagate(SyncFileItemVector& );
void slotThreadTreeWalkResult(const SyncFileItemVector& ); // after sync is done
void slotEmitFinishedDelayed();
@ -304,15 +295,6 @@ private:
/// Reset when no follow-up is requested.
int _consecutiveFollowUpSyncs;
// SocketAPI: Cache files and folders that had errors so that they can
// get a red ERROR icon.
QSet<QString> _stateLastSyncItemsWithErrorNew; // gets moved to _stateLastSyncItemsWithError at end of sync
QSet<QString> _stateLastSyncItemsWithError;
// SocketAPI: A folder is tained if we got a file watcher notification
// for it. It's displayed as EVAL.
QSet<QString> _stateTaintedFolders;
SyncJournalDb _journal;
ClientProxy _clientProxy;

View File

@ -23,6 +23,7 @@
#include "utility.h"
#include "theme.h"
#include "syncjournalfilerecord.h"
#include "syncengine.h"
#include "syncfileitem.h"
#include "filesystem.h"
#include "version.h"
@ -215,7 +216,7 @@ void SocketApi::slotUpdateFolderView(Folder *f)
f->syncResult().status() == SyncResult::SetupError ) {
broadcastMessage(QLatin1String("STATUS"), f->path() ,
this->fileStatus(f, "").toSocketAPIString());
f->syncEngine().syncFileStatusTracker().fileStatus("").toSocketAPIString());
broadcastMessage(QLatin1String("UPDATE_VIEW"), f->path() );
} else {
@ -235,7 +236,7 @@ void SocketApi::slotItemCompleted(const QString &folder, const SyncFileItem &ite
return;
}
auto status = this->fileStatus(f, item.destination());
auto status = f->syncEngine().syncFileStatusTracker().fileStatus(item.destination());
const QString path = f->path() + item.destination();
broadcastMessage(QLatin1String("STATUS"), path, status.toSocketAPIString());
}
@ -330,7 +331,7 @@ void SocketApi::command_RETRIEVE_FILE_STATUS(const QString& argument, QIODevice*
statusString = QLatin1String("NOP");
} else {
const QString file = QDir::cleanPath(argument).mid(syncFolder->cleanPath().length()+1);
SyncFileStatus fileStatus = this->fileStatus(syncFolder, file);
SyncFileStatus fileStatus = syncFolder->syncEngine().syncFileStatusTracker().fileStatus(file);
statusString = fileStatus.toSocketAPIString();
}
@ -367,7 +368,7 @@ void SocketApi::command_SHARE(const QString& localFile, QIODevice* socket)
} else {
const QString localFileClean = QDir::cleanPath(localFile);
const QString file = localFileClean.mid(shareFolder->cleanPath().length()+1);
SyncFileStatus fileStatus = this->fileStatus(shareFolder, file);
SyncFileStatus fileStatus = shareFolder->syncEngine().syncFileStatusTracker().fileStatus(file);
// Verify the file is on the server (to our knowledge of course)
if (fileStatus.tag() != SyncFileStatus::STATUS_UPTODATE &&
@ -423,7 +424,7 @@ void SocketApi::command_SHARE_STATUS(const QString &localFile, QIODevice *socket
sendMessage(socket, message);
} else {
const QString file = QDir::cleanPath(localFile).mid(shareFolder->cleanPath().length()+1);
SyncFileStatus fileStatus = this->fileStatus(shareFolder, file);
SyncFileStatus fileStatus = shareFolder->syncEngine().syncFileStatusTracker().fileStatus(file);
// Verify the file is on the server (to our knowledge of course)
if (fileStatus.tag() != SyncFileStatus::STATUS_UPTODATE &&
@ -478,142 +479,4 @@ QString SocketApi::buildRegisterPathMessage(const QString& path)
return message;
}
/**
* Get status about a single file.
*/
SyncFileStatus SocketApi::fileStatus(Folder *folder, const QString& systemFileName)
{
QString file = folder->path();
QString fileName = systemFileName.normalized(QString::NormalizationForm_C);
QString fileNameSlash = fileName;
if(fileName != QLatin1String("/") && !fileName.isEmpty()) {
file += fileName;
}
if( fileName.endsWith(QLatin1Char('/')) ) {
fileName.truncate(fileName.length()-1);
qDebug() << "Removed trailing slash: " << fileName;
} else {
fileNameSlash += QLatin1Char('/');
}
const QFileInfo fi(file);
if( !FileSystem::fileExists(file, fi) ) {
qDebug() << "OO File " << file << " is not existing";
return SyncFileStatus(SyncFileStatus::STATUS_STAT_ERROR);
}
// file is ignored?
// Qt considers .lnk files symlinks on Windows so we need to work
// around that here.
if( fi.isSymLink()
#ifdef Q_OS_WIN
&& fi.suffix() != "lnk"
#endif
) {
return SyncFileStatus(SyncFileStatus::STATUS_IGNORE);
}
csync_ftw_type_e type = CSYNC_FTW_TYPE_FILE;
if( fi.isDir() ) {
type = CSYNC_FTW_TYPE_DIR;
}
// Is it excluded?
if( folder->isFileExcludedRelative(fileName) ) {
return SyncFileStatus(SyncFileStatus::STATUS_IGNORE);
}
// Error if it is in the selective sync blacklist
foreach(const auto &s, folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList)) {
if (fileNameSlash.startsWith(s)) {
return SyncFileStatus(SyncFileStatus::STATUS_ERROR);
}
}
SyncFileStatus status(SyncFileStatus::STATUS_NONE);
SyncJournalFileRecord rec = folder->journalDb()->getFileRecord(fileName);
if (folder->estimateState(fileName, type, &status)) {
qDebug() << "Folder estimated status for" << fileName << "to" << status.toSocketAPIString();
} else if (fileName == "") {
// sync folder itself
switch (folder->syncResult().status()) {
case SyncResult::Undefined:
case SyncResult::NotYetStarted:
case SyncResult::SyncPrepare:
case SyncResult::SyncRunning:
status.set(SyncFileStatus::STATUS_EVAL);
return status;
case SyncResult::Success:
case SyncResult::Problem:
status.set(SyncFileStatus::STATUS_UPTODATE);
return status;
case SyncResult::Error:
case SyncResult::SetupError:
case SyncResult::SyncAbortRequested:
status.set(SyncFileStatus::STATUS_ERROR);
return status;
case SyncResult::Paused:
status.set(SyncFileStatus::STATUS_IGNORE);
return status;
}
} else if (type == CSYNC_FTW_TYPE_DIR) {
if (rec.isValid()) {
status.set(SyncFileStatus::STATUS_UPTODATE);
} else {
qDebug() << "Could not determine state for folder" << fileName << "will set STATUS_NEW";
status.set(SyncFileStatus::STATUS_NEW);
}
} else if (type == CSYNC_FTW_TYPE_FILE) {
if (rec.isValid()) {
if( FileSystem::getModTime(fi.absoluteFilePath()) == Utility::qDateTimeToTime_t(rec._modtime) ) {
status.set(SyncFileStatus::STATUS_UPTODATE);
} else {
if (rec._remotePerm.isNull() || rec._remotePerm.contains("W") ) {
status.set(SyncFileStatus::STATUS_EVAL);
} else {
status.set(SyncFileStatus::STATUS_ERROR);
}
}
} else {
qDebug() << "Could not determine state for file" << fileName << "will set STATUS_NEW";
status.set(SyncFileStatus::STATUS_NEW);
}
}
if (rec.isValid() && rec._remotePerm.contains("S"))
status.setSharedWithMe(true);
if (status.tag() == SyncFileStatus::STATUS_NEW) {
// check the parent folder if it is shared and if it is allowed to create a file/dir within
QDir d( fi.path() );
auto parentPath = d.path();
auto dirRec = folder->journalDb()->getFileRecord(parentPath);
bool isDir = type == CSYNC_FTW_TYPE_DIR;
while( !d.isRoot() && !(d.exists() && dirRec.isValid()) ) {
d.cdUp(); // returns true if the dir exists.
parentPath = d.path();
// cut the folder path
dirRec = folder->journalDb()->getFileRecord(parentPath);
isDir = true;
}
if( dirRec.isValid() && !dirRec._remotePerm.isNull()) {
if( (isDir && !dirRec._remotePerm.contains("K"))
|| (!isDir && !dirRec._remotePerm.contains("C")) ) {
status.set(SyncFileStatus::STATUS_ERROR);
}
}
}
return status;
}
} // namespace OCC

View File

@ -56,6 +56,7 @@ set(libsync_SRCS
propagateremotemkdir.cpp
syncengine.cpp
syncfilestatus.cpp
syncfilestatustracker.cpp
syncjournaldb.cpp
syncjournalfilerecord.cpp
syncresult.cpp

View File

@ -93,6 +93,7 @@ SyncEngine::SyncEngine(AccountPtr account, const QString& localPath,
csync_create(&_csync_ctx, localPath.toUtf8().data(), url_string.toUtf8().data());
csync_init(_csync_ctx);
_excludedFiles.reset(new ExcludedFiles(&_csync_ctx->excludes));
_syncFileStatusTracker.reset(new SyncFileStatusTracker(this));
_thread.setObjectName("SyncEngine_Thread");
}

View File

@ -35,7 +35,7 @@
#include "syncfileitem.h"
#include "progressdispatcher.h"
#include "utility.h"
#include "syncfilestatus.h"
#include "syncfilestatustracker.h"
#include "accountfwd.h"
#include "discoveryphase.h"
#include "checksums.h"
@ -75,10 +75,12 @@ public:
* -1 means infinite
*/
void setNewBigFolderSizeLimit(qint64 limit) { _newBigFolderSizeLimit = limit; }
bool ignoreHiddenFiles() const { return _csync_ctx->ignore_hidden_files; }
void setIgnoreHiddenFiles(bool ignore) { _csync_ctx->ignore_hidden_files = ignore; }
ExcludedFiles &excludedFiles() { return *_excludedFiles; }
Utility::StopWatch &stopWatch() { return _stopWatch; }
SyncFileStatusTracker &syncFileStatusTracker() { return *_syncFileStatusTracker; }
/* Return true if we detected that another sync is needed to complete the sync */
bool isAnotherSyncNeeded() { return _anotherSyncNeeded; }
@ -93,7 +95,7 @@ public:
AccountPtr account() const;
SyncJournalDb *journal() const { return _journal; }
QString localPath() const { return _localPath; }
/**
* Minimum age, in milisecond, of a file that can be uploaded.
* Files more recent than that are not going to be uploaeded as they are considered
@ -211,6 +213,7 @@ private:
QScopedPointer<ProgressInfo> _progressInfo;
QScopedPointer<ExcludedFiles> _excludedFiles;
QScopedPointer<SyncFileStatusTracker> _syncFileStatusTracker;
Utility::StopWatch _stopWatch;
// maps the origin and the target of the folders that have been renamed

View File

@ -0,0 +1,230 @@
/*
* Copyright (C) by Klaas Freitag <freitag@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; version 2 of the License.
*
* 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 "syncfilestatustracker.h"
#include "filesystem.h"
#include "syncengine.h"
#include "syncjournaldb.h"
#include "syncjournalfilerecord.h"
#include "utility.h"
#include <QDir>
#include <QFileInfo>
namespace OCC {
/**
* Whether this item should get an ERROR icon through the Socket API.
*
* The Socket API should only present serious, permanent errors to the user.
* In particular SoftErrors should just retain their 'needs to be synced'
* icon as the problem is most likely going to resolve itself quickly and
* automatically.
*/
static bool showErrorInSocketApi(const SyncFileItem& item)
{
const auto status = item._status;
return status == SyncFileItem::NormalError
|| status == SyncFileItem::FatalError;
}
static void addErroredSyncItemPathsToList(const SyncFileItemVector& items, QSet<QString>* set) {
foreach (const SyncFileItemPtr &item, items) {
if (showErrorInSocketApi(*item)) {
set->insert(item->_file);
}
}
}
SyncFileStatusTracker::SyncFileStatusTracker(SyncEngine *syncEngine)
: _syncEngine(syncEngine)
{
connect(syncEngine, SIGNAL(treeWalkResult(const SyncFileItemVector&)),
this, SLOT(slotThreadTreeWalkResult(const SyncFileItemVector&)));
connect(syncEngine, SIGNAL(aboutToPropagate(SyncFileItemVector&)),
this, SLOT(slotAboutToPropagate(SyncFileItemVector&)));
connect(syncEngine, SIGNAL(finished(bool)), SLOT(slotSyncFinished()));
connect(syncEngine, SIGNAL(itemCompleted(const SyncFileItem &, const PropagatorJob &)),
this, SLOT(slotItemCompleted(const SyncFileItem &)));
}
bool SyncFileStatusTracker::estimateState(QString fn, csync_ftw_type_e t, SyncFileStatus* s)
{
if (t == CSYNC_FTW_TYPE_DIR) {
if (Utility::doesSetContainPrefix(_stateLastSyncItemsWithError, fn)) {
qDebug() << Q_FUNC_INFO << "Folder has error" << fn;
s->set(SyncFileStatus::STATUS_ERROR);
return true;
}
// If sync is running, check _syncedItems, possibly give it STATUS_EVAL (=syncing down)
if (_syncEngine->isSyncRunning()) {
if (_syncEngine->estimateState(fn, t, s)) {
return true;
}
}
return false;
} else if ( t== CSYNC_FTW_TYPE_FILE) {
// check if errorList has the directory/file
if (Utility::doesSetContainPrefix(_stateLastSyncItemsWithError, fn)) {
s->set(SyncFileStatus::STATUS_ERROR);
return true;
}
// If sync running: _syncedItems -> SyncingState
if (_syncEngine->isSyncRunning()) {
if (_syncEngine->estimateState(fn, t, s)) {
return true;
}
}
}
return false;
}
/**
* Get status about a single file.
*/
SyncFileStatus SyncFileStatusTracker::fileStatus(const QString& systemFileName)
{
QString file = _syncEngine->localPath();
QString fileName = systemFileName.normalized(QString::NormalizationForm_C);
QString fileNameSlash = fileName;
if(fileName != QLatin1String("/") && !fileName.isEmpty()) {
file += fileName;
}
if( fileName.endsWith(QLatin1Char('/')) ) {
fileName.truncate(fileName.length()-1);
qDebug() << "Removed trailing slash: " << fileName;
} else {
fileNameSlash += QLatin1Char('/');
}
const QFileInfo fi(file);
if( !FileSystem::fileExists(file, fi) ) {
qDebug() << "OO File " << file << " is not existing";
return SyncFileStatus(SyncFileStatus::STATUS_STAT_ERROR);
}
// file is ignored?
// Qt considers .lnk files symlinks on Windows so we need to work
// around that here.
if( fi.isSymLink()
#ifdef Q_OS_WIN
&& fi.suffix() != "lnk"
#endif
) {
return SyncFileStatus(SyncFileStatus::STATUS_IGNORE);
}
csync_ftw_type_e type = CSYNC_FTW_TYPE_FILE;
if( fi.isDir() ) {
type = CSYNC_FTW_TYPE_DIR;
}
// Is it excluded?
if( _syncEngine->excludedFiles().isExcluded(file, _syncEngine->localPath(), _syncEngine->ignoreHiddenFiles()) ) {
return SyncFileStatus(SyncFileStatus::STATUS_IGNORE);
}
// Error if it is in the selective sync blacklist
foreach(const auto &s, _syncEngine->journal()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList)) {
if (fileNameSlash.startsWith(s)) {
return SyncFileStatus(SyncFileStatus::STATUS_ERROR);
}
}
SyncFileStatus status(SyncFileStatus::STATUS_NONE);
SyncJournalFileRecord rec = _syncEngine->journal()->getFileRecord(fileName );
if (estimateState(fileName, type, &status)) {
qDebug() << "Folder estimated status for" << fileName << "to" << status.toSocketAPIString();
} else if (fileName == "") {
// sync folder itself
// FIXME: The new parent folder logic should take over this, treating the root the same as any folder.
} else if (type == CSYNC_FTW_TYPE_DIR) {
if (rec.isValid()) {
status.set(SyncFileStatus::STATUS_UPTODATE);
} else {
qDebug() << "Could not determine state for folder" << fileName << "will set STATUS_NEW";
status.set(SyncFileStatus::STATUS_NEW);
}
} else if (type == CSYNC_FTW_TYPE_FILE) {
if (rec.isValid()) {
if( FileSystem::getModTime(fi.absoluteFilePath()) == Utility::qDateTimeToTime_t(rec._modtime) ) {
status.set(SyncFileStatus::STATUS_UPTODATE);
} else {
if (rec._remotePerm.isNull() || rec._remotePerm.contains("W") ) {
status.set(SyncFileStatus::STATUS_EVAL);
} else {
status.set(SyncFileStatus::STATUS_ERROR);
}
}
} else {
qDebug() << "Could not determine state for file" << fileName << "will set STATUS_NEW";
status.set(SyncFileStatus::STATUS_NEW);
}
}
if (rec.isValid() && rec._remotePerm.contains("S"))
status.setSharedWithMe(true);
if (status.tag() == SyncFileStatus::STATUS_NEW) {
// check the parent folder if it is shared and if it is allowed to create a file/dir within
QDir d( fi.path() );
auto parentPath = d.path();
auto dirRec = _syncEngine->journal()->getFileRecord(parentPath);
bool isDir = type == CSYNC_FTW_TYPE_DIR;
while( !d.isRoot() && !(d.exists() && dirRec.isValid()) ) {
d.cdUp(); // returns true if the dir exists.
parentPath = d.path();
// cut the folder path
dirRec = _syncEngine->journal()->getFileRecord(parentPath);
isDir = true;
}
if( dirRec.isValid() && !dirRec._remotePerm.isNull()) {
if( (isDir && !dirRec._remotePerm.contains("K"))
|| (!isDir && !dirRec._remotePerm.contains("C")) ) {
status.set(SyncFileStatus::STATUS_ERROR);
}
}
}
return status;
}
void SyncFileStatusTracker::slotThreadTreeWalkResult(const SyncFileItemVector& items)
{
addErroredSyncItemPathsToList(items, &_stateLastSyncItemsWithErrorNew);
}
void SyncFileStatusTracker::slotAboutToPropagate(SyncFileItemVector& items)
{
addErroredSyncItemPathsToList(items, &_stateLastSyncItemsWithErrorNew);
}
void SyncFileStatusTracker::slotSyncFinished()
{
_stateLastSyncItemsWithError = _stateLastSyncItemsWithErrorNew;
_stateLastSyncItemsWithErrorNew.clear();
}
void SyncFileStatusTracker::slotItemCompleted(const SyncFileItem &item)
{
if (showErrorInSocketApi(item)) {
_stateLastSyncItemsWithErrorNew.insert(item._file);
}
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (C) by Klaas Freitag <freitag@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; version 2 of the License.
*
* 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.
*/
#ifndef SYNCFILESTATUSTRACKER_H
#define SYNCFILESTATUSTRACKER_H
#include "ownsql.h"
#include "syncfileitem.h"
#include "syncfilestatus.h"
#include <QSet>
#include <csync.h>
namespace OCC {
class SyncEngine;
class OWNCLOUDSYNC_EXPORT SyncFileStatusTracker : public QObject
{
public:
SyncFileStatusTracker(SyncEngine *syncEngine);
SyncFileStatus fileStatus(const QString& systemFileName);
private slots:
void slotThreadTreeWalkResult(const SyncFileItemVector& items);
void slotAboutToPropagate(SyncFileItemVector& items);
void slotSyncFinished();
void slotItemCompleted(const SyncFileItem &item);
private:
bool estimateState(QString fn, csync_ftw_type_e t, SyncFileStatus* s);
SyncEngine *_syncEngine;
// SocketAPI: Cache files and folders that had errors so that they can
// get a red ERROR icon.
QSet<QString> _stateLastSyncItemsWithErrorNew; // gets moved to _stateLastSyncItemsWithError at end of sync
QSet<QString> _stateLastSyncItemsWithError;
};
}
#endif