mirror of
https://github.com/nextcloud/desktop.git
synced 2024-10-23 21:05:44 +03:00
Merge pull request #4615 from owncloud/socketApiRefactor
Socket API refactor
This commit is contained in:
commit
cdbc25ede8
26 changed files with 459 additions and 732 deletions
|
@ -18,8 +18,6 @@
|
|||
|
||||
namespace OCC {
|
||||
|
||||
typedef QSharedPointer<AccountState> AccountStatePtr;
|
||||
|
||||
/**
|
||||
@brief The AccountManager class
|
||||
@ingroup gui
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
|
||||
#include <QDebug>
|
||||
#include <QDesktopServices>
|
||||
#include <QDir>
|
||||
#include <QListWidgetItem>
|
||||
#include <QMessageBox>
|
||||
#include <QAction>
|
||||
|
|
|
@ -29,11 +29,13 @@ namespace OCC {
|
|||
class AccountState;
|
||||
class Account;
|
||||
|
||||
typedef QExplicitlySharedDataPointer<AccountState> AccountStatePtr;
|
||||
|
||||
/**
|
||||
* @brief Extra info about an ownCloud server account.
|
||||
* @ingroup gui
|
||||
*/
|
||||
class AccountState : public QObject {
|
||||
class AccountState : public QObject, public QSharedData {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum State {
|
||||
|
@ -148,6 +150,6 @@ private:
|
|||
}
|
||||
|
||||
Q_DECLARE_METATYPE(OCC::AccountState*)
|
||||
Q_DECLARE_METATYPE(QSharedPointer<OCC::AccountState>)
|
||||
Q_DECLARE_METATYPE(OCC::AccountStatePtr)
|
||||
|
||||
#endif //ACCOUNTINFO_H
|
||||
|
|
|
@ -56,9 +56,10 @@ static void csyncLogCatcher(int /*verbosity*/,
|
|||
|
||||
|
||||
Folder::Folder(const FolderDefinition& definition,
|
||||
AccountState* accountState,
|
||||
QObject* parent)
|
||||
: QObject(parent)
|
||||
, _accountState(0)
|
||||
, _accountState(accountState)
|
||||
, _definition(definition)
|
||||
, _csyncError(false)
|
||||
, _csyncUnavail(false)
|
||||
|
@ -70,6 +71,9 @@ Folder::Folder(const FolderDefinition& definition,
|
|||
, _consecutiveFollowUpSyncs(0)
|
||||
, _journal(definition.localPath)
|
||||
{
|
||||
qRegisterMetaType<SyncFileItemVector>("SyncFileItemVector");
|
||||
qRegisterMetaType<SyncFileItem::Direction>("SyncFileItem::Direction");
|
||||
|
||||
qsrand(QTime::currentTime().msec());
|
||||
_timeSinceLastSyncStart.start();
|
||||
_timeSinceLastSyncDone.start();
|
||||
|
@ -84,24 +88,37 @@ Folder::Folder(const FolderDefinition& definition,
|
|||
checkLocalPath();
|
||||
|
||||
_syncResult.setFolder(_definition.alias);
|
||||
|
||||
_engine.reset(new SyncEngine(_accountState->account(), path(), remoteUrl(), remotePath(), &_journal));
|
||||
// pass the setting if hidden files are to be ignored, will be read in csync_update
|
||||
_engine->setIgnoreHiddenFiles(_definition.ignoreHiddenFiles);
|
||||
|
||||
if (!setIgnoredFiles())
|
||||
qWarning("Could not read system exclude file");
|
||||
|
||||
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(started()), SLOT(slotSyncStarted()), Qt::QueuedConnection);
|
||||
connect(_engine.data(), SIGNAL(finished(bool)), SLOT(slotSyncFinished(bool)), Qt::QueuedConnection);
|
||||
connect(_engine.data(), SIGNAL(csyncError(QString)), SLOT(slotSyncError(QString)), Qt::QueuedConnection);
|
||||
connect(_engine.data(), SIGNAL(csyncUnavailable()), SLOT(slotCsyncUnavailable()), Qt::QueuedConnection);
|
||||
|
||||
//direct connection so the message box is blocking the sync.
|
||||
connect(_engine.data(), SIGNAL(aboutToRemoveAllFiles(SyncFileItem::Direction,bool*)),
|
||||
SLOT(slotAboutToRemoveAllFiles(SyncFileItem::Direction,bool*)));
|
||||
connect(_engine.data(), SIGNAL(aboutToRestoreBackup(bool*)),
|
||||
SLOT(slotAboutToRestoreBackup(bool*)));
|
||||
connect(_engine.data(), SIGNAL(folderDiscovered(bool,QString)), this, SLOT(slotFolderDiscovered(bool,QString)));
|
||||
connect(_engine.data(), SIGNAL(transmissionProgress(ProgressInfo)), this, SLOT(slotTransmissionProgress(ProgressInfo)));
|
||||
connect(_engine.data(), SIGNAL(itemCompleted(const SyncFileItem &, const PropagatorJob &)),
|
||||
this, SLOT(slotItemCompleted(const SyncFileItem &, const PropagatorJob &)));
|
||||
connect(_engine.data(), SIGNAL(newBigFolder(QString)), this, SLOT(slotNewBigFolderDiscovered(QString)));
|
||||
}
|
||||
|
||||
Folder::~Folder()
|
||||
{
|
||||
if( _engine ) {
|
||||
_engine->abort();
|
||||
_engine.reset(0);
|
||||
}
|
||||
}
|
||||
|
||||
void Folder::setAccountState( AccountState *account )
|
||||
{
|
||||
_accountState = account;
|
||||
}
|
||||
|
||||
AccountState* Folder::accountState() const
|
||||
{
|
||||
return _accountState;
|
||||
}
|
||||
|
||||
void Folder::checkLocalPath()
|
||||
|
@ -192,7 +209,7 @@ QString Folder::cleanPath()
|
|||
|
||||
bool Folder::isBusy() const
|
||||
{
|
||||
return !_engine.isNull();
|
||||
return _engine->isSyncRunning();
|
||||
}
|
||||
|
||||
QString Folder::remotePath() const
|
||||
|
@ -202,9 +219,6 @@ QString Folder::remotePath() const
|
|||
|
||||
QUrl Folder::remoteUrl() const
|
||||
{
|
||||
if (!_accountState) {
|
||||
return QUrl("http://deleted-account");
|
||||
}
|
||||
return Account::concatUrlPath(_accountState->account()->davUrl(), remotePath());
|
||||
}
|
||||
|
||||
|
@ -256,11 +270,6 @@ void Folder::slotRunEtagJob()
|
|||
{
|
||||
qDebug() << "* Trying to check" << remoteUrl().toString() << "for changes via ETag check. (time since last sync:" << (_timeSinceLastSyncDone.elapsed() / 1000) << "s)";
|
||||
|
||||
if (!_accountState) {
|
||||
qDebug() << "Can't run EtagJob, account is deleted";
|
||||
return;
|
||||
}
|
||||
|
||||
AccountPtr account = _accountState->account();
|
||||
|
||||
if (!_requestEtagJob.isNull()) {
|
||||
|
@ -328,9 +337,7 @@ void Folder::etagRetreived(const QString& etag)
|
|||
emit scheduleToSync(this);
|
||||
}
|
||||
|
||||
if( _accountState ) {
|
||||
_accountState->tagLastSuccessfullETagRequest();
|
||||
}
|
||||
_accountState->tagLastSuccessfullETagRequest();
|
||||
}
|
||||
|
||||
void Folder::etagRetreivedFromSyncEngine(const QString& etag)
|
||||
|
@ -359,7 +366,7 @@ void Folder::bubbleUpSyncResult()
|
|||
|
||||
SyncRunFileLog syncFileLog;
|
||||
|
||||
syncFileLog.start(path(), _engine ? _engine->stopWatch() : Utility::StopWatch() );
|
||||
syncFileLog.start(path(), _engine->isSyncRunning() ? _engine->stopWatch() : Utility::StopWatch() );
|
||||
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
|
@ -434,32 +441,32 @@ void Folder::bubbleUpSyncResult()
|
|||
_syncResult.setWarnCount(ignoredItems);
|
||||
|
||||
if( firstItemNew ) {
|
||||
createGuiLog( firstItemNew->_file, SyncFileStatus::STATUS_NEW, newItems );
|
||||
createGuiLog( firstItemNew->_file, LogStatusNew, newItems );
|
||||
}
|
||||
if( firstItemDeleted ) {
|
||||
createGuiLog( firstItemDeleted->_file, SyncFileStatus::STATUS_REMOVE, removedItems );
|
||||
createGuiLog( firstItemDeleted->_file, LogStatusRemove, removedItems );
|
||||
}
|
||||
if( firstItemUpdated ) {
|
||||
createGuiLog( firstItemUpdated->_file, SyncFileStatus::STATUS_UPDATED, updatedItems );
|
||||
createGuiLog( firstItemUpdated->_file, LogStatusUpdated, updatedItems );
|
||||
}
|
||||
|
||||
if( firstItemRenamed ) {
|
||||
SyncFileStatus status(SyncFileStatus::STATUS_RENAME);
|
||||
LogStatus status(LogStatusRename);
|
||||
// if the path changes it's rather a move
|
||||
QDir renTarget = QFileInfo(firstItemRenamed->_renameTarget).dir();
|
||||
QDir renSource = QFileInfo(firstItemRenamed->_file).dir();
|
||||
if(renTarget != renSource) {
|
||||
status.set(SyncFileStatus::STATUS_MOVE);
|
||||
status = LogStatusMove;
|
||||
}
|
||||
createGuiLog( firstItemRenamed->_originalFile, status, renamedItems, firstItemRenamed->_renameTarget );
|
||||
}
|
||||
|
||||
createGuiLog( firstItemError->_file, SyncFileStatus::STATUS_ERROR, errorItems );
|
||||
createGuiLog( firstItemError->_file, LogStatusError, errorItems );
|
||||
|
||||
qDebug() << "OO folder slotSyncFinished: result: " << int(_syncResult.status());
|
||||
}
|
||||
|
||||
void Folder::createGuiLog( const QString& filename, SyncFileStatus status, int count,
|
||||
void Folder::createGuiLog( const QString& filename, LogStatus status, int count,
|
||||
const QString& renameTarget )
|
||||
{
|
||||
if(count > 0) {
|
||||
|
@ -468,53 +475,49 @@ void Folder::createGuiLog( const QString& filename, SyncFileStatus status, int c
|
|||
QString file = QDir::toNativeSeparators(filename);
|
||||
QString text;
|
||||
|
||||
// not all possible values of status are evaluated here because the others
|
||||
// are not used in the calling function. Please check there.
|
||||
switch (status.tag()) {
|
||||
case SyncFileStatus::STATUS_REMOVE:
|
||||
switch (status) {
|
||||
case LogStatusRemove:
|
||||
if( count > 1 ) {
|
||||
text = tr("%1 and %2 other files have been removed.", "%1 names a file.").arg(file).arg(count-1);
|
||||
} else {
|
||||
text = tr("%1 has been removed.", "%1 names a file.").arg(file);
|
||||
}
|
||||
break;
|
||||
case SyncFileStatus::STATUS_NEW:
|
||||
case LogStatusNew:
|
||||
if( count > 1 ) {
|
||||
text = tr("%1 and %2 other files have been downloaded.", "%1 names a file.").arg(file).arg(count-1);
|
||||
} else {
|
||||
text = tr("%1 has been downloaded.", "%1 names a file.").arg(file);
|
||||
}
|
||||
break;
|
||||
case SyncFileStatus::STATUS_UPDATED:
|
||||
case LogStatusUpdated:
|
||||
if( count > 1 ) {
|
||||
text = tr("%1 and %2 other files have been updated.").arg(file).arg(count-1);
|
||||
} else {
|
||||
text = tr("%1 has been updated.", "%1 names a file.").arg(file);
|
||||
}
|
||||
break;
|
||||
case SyncFileStatus::STATUS_RENAME:
|
||||
case LogStatusRename:
|
||||
if( count > 1 ) {
|
||||
text = tr("%1 has been renamed to %2 and %3 other files have been renamed.").arg(file).arg(renameTarget).arg(count-1);
|
||||
} else {
|
||||
text = tr("%1 has been renamed to %2.", "%1 and %2 name files.").arg(file).arg(renameTarget);
|
||||
}
|
||||
break;
|
||||
case SyncFileStatus::STATUS_MOVE:
|
||||
case LogStatusMove:
|
||||
if( count > 1 ) {
|
||||
text = tr("%1 has been moved to %2 and %3 other files have been moved.").arg(file).arg(renameTarget).arg(count-1);
|
||||
} else {
|
||||
text = tr("%1 has been moved to %2.").arg(file).arg(renameTarget);
|
||||
}
|
||||
break;
|
||||
case SyncFileStatus::STATUS_ERROR:
|
||||
case LogStatusError:
|
||||
if( count > 1 ) {
|
||||
text = tr("%1 and %2 other files could not be synced due to errors. See the log for details.", "%1 names a file.").arg(file).arg(count-1);
|
||||
} else {
|
||||
text = tr("%1 could not be synced due to an error. See the log for details.").arg(file);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if( !text.isEmpty() ) {
|
||||
|
@ -557,7 +560,7 @@ void Folder::slotWatchedPathChanged(const QString& path)
|
|||
{
|
||||
// When no sync is running or it's in the prepare phase, we can
|
||||
// always schedule a new sync.
|
||||
if (! _engine || _syncResult.status() == SyncResult::SyncPrepare) {
|
||||
if (! _engine->isSyncRunning() || _syncResult.status() == SyncResult::SyncPrepare) {
|
||||
emit scheduleToSync(this);
|
||||
return;
|
||||
}
|
||||
|
@ -585,91 +588,14 @@ 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)
|
||||
{
|
||||
// empty the tainted list since the status generation code will use the _syncedItems
|
||||
// (which imply the folder) to generate the syncing state icon now.
|
||||
_stateTaintedFolders.clear();
|
||||
|
||||
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.isNull()) {
|
||||
if (_engine->estimateState(fn, t, s)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(!fn.endsWith(QLatin1Char('/'))) {
|
||||
fn.append(QLatin1Char('/'));
|
||||
}
|
||||
if (Utility::doesSetContainPrefix(_stateTaintedFolders, fn)) {
|
||||
qDebug() << Q_FUNC_INFO << "Folder is tainted, EVAL!" << fn;
|
||||
s->set(SyncFileStatus::STATUS_EVAL);
|
||||
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.isNull()) {
|
||||
if (_engine->estimateState(fn, t, s)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Folder::saveToSettings() const
|
||||
{
|
||||
if (!_accountState) {
|
||||
qDebug() << "Can't save folder to settings, account is deleted";
|
||||
return;
|
||||
}
|
||||
|
||||
auto settings = _accountState->settings();
|
||||
settings->beginGroup(QLatin1String("Folders"));
|
||||
FolderDefinition::save(*settings, _definition);
|
||||
|
@ -680,11 +606,6 @@ void Folder::saveToSettings() const
|
|||
|
||||
void Folder::removeFromSettings() const
|
||||
{
|
||||
if (!_accountState) {
|
||||
qDebug() << "Can't remove folder from settings, account is deleted";
|
||||
return;
|
||||
}
|
||||
|
||||
auto settings = _accountState->settings();
|
||||
settings->beginGroup(QLatin1String("Folders"));
|
||||
settings->remove(_definition.alias);
|
||||
|
@ -692,56 +613,19 @@ void Folder::removeFromSettings() const
|
|||
|
||||
bool Folder::isFileExcludedAbsolute(const QString& fullPath) const
|
||||
{
|
||||
if (!fullPath.startsWith(path())) {
|
||||
// Mark paths we're not responsible for as excluded...
|
||||
return true;
|
||||
}
|
||||
|
||||
QString myFullPath = fullPath;
|
||||
if (myFullPath.endsWith(QLatin1Char('/'))) {
|
||||
myFullPath.chop(1);
|
||||
}
|
||||
|
||||
QString relativePath = myFullPath.mid(path().size());
|
||||
auto excl = ExcludedFiles::instance().isExcluded(myFullPath, relativePath, _definition.ignoreHiddenFiles);
|
||||
return excl != CSYNC_NOT_EXCLUDED;
|
||||
return _engine->excludedFiles().isExcluded(fullPath, path(), _definition.ignoreHiddenFiles);
|
||||
}
|
||||
|
||||
bool Folder::isFileExcludedRelative(const QString& relativePath) const
|
||||
{
|
||||
return isFileExcludedAbsolute(path() + relativePath);
|
||||
return _engine->excludedFiles().isExcluded(path() + relativePath, path(), _definition.ignoreHiddenFiles);
|
||||
}
|
||||
|
||||
void Folder::watcherSlot(QString fn)
|
||||
{
|
||||
// FIXME: On OS X we could not do this "if" since on OS X the file watcher ignores events for ourselves
|
||||
// however to have the same behaviour atm on all platforms, we don't do it
|
||||
if (!_engine.isNull()) {
|
||||
qDebug() << Q_FUNC_INFO << "Sync running, IGNORE event for " << fn;
|
||||
return;
|
||||
}
|
||||
QFileInfo fi(fn);
|
||||
if (fi.isFile()) {
|
||||
fn = fi.path(); // depending on OS, file watcher might be for dir or file
|
||||
}
|
||||
// Make it a relative path depending on the folder
|
||||
QString relativePath = fn.remove(0, path().length());
|
||||
if( !relativePath.endsWith(QLatin1Char('/'))) {
|
||||
relativePath.append(QLatin1Char('/'));
|
||||
}
|
||||
qDebug() << Q_FUNC_INFO << fi.canonicalFilePath() << fn << relativePath;
|
||||
_stateTaintedFolders.insert(relativePath);
|
||||
|
||||
// Notify the SocketAPI?
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Folder::slotTerminateSync()
|
||||
{
|
||||
qDebug() << "folder " << alias() << " Terminating!";
|
||||
|
||||
if( _engine ) {
|
||||
if( _engine->isSyncRunning() ) {
|
||||
_engine->abort();
|
||||
|
||||
// Do not display an error message, user knows his own actions.
|
||||
|
@ -814,11 +698,6 @@ bool Folder::proxyDirty()
|
|||
|
||||
void Folder::startSync(const QStringList &pathList)
|
||||
{
|
||||
if (!_accountState) {
|
||||
qDebug() << "Can't startSync, account is deleted";
|
||||
return;
|
||||
}
|
||||
|
||||
Q_UNUSED(pathList)
|
||||
if (proxyDirty()) {
|
||||
setProxyDirty(false);
|
||||
|
@ -843,10 +722,6 @@ void Folder::startSync(const QStringList &pathList)
|
|||
qDebug() << "*** Start syncing " << remoteUrl().toString() << " - client version"
|
||||
<< qPrintable(Theme::instance()->version());
|
||||
|
||||
_engine.reset(new SyncEngine( _accountState->account(), path(), remoteUrl(), remotePath(), &_journal));
|
||||
// pass the setting if hidden files are to be ignored, will be read in csync_update
|
||||
_engine->setIgnoreHiddenFiles(_definition.ignoreHiddenFiles);
|
||||
|
||||
if (!setIgnoredFiles())
|
||||
{
|
||||
slotSyncError(tr("Could not read system exclude file"));
|
||||
|
@ -854,32 +729,6 @@ void Folder::startSync(const QStringList &pathList)
|
|||
return;
|
||||
}
|
||||
|
||||
qRegisterMetaType<SyncFileItemVector>("SyncFileItemVector");
|
||||
qRegisterMetaType<SyncFileItem::Direction>("SyncFileItem::Direction");
|
||||
|
||||
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);
|
||||
connect(_engine.data(), SIGNAL(csyncError(QString)), SLOT(slotSyncError(QString)), Qt::QueuedConnection);
|
||||
connect(_engine.data(), SIGNAL(csyncUnavailable()), SLOT(slotCsyncUnavailable()), Qt::QueuedConnection);
|
||||
|
||||
//direct connection so the message box is blocking the sync.
|
||||
connect(_engine.data(), SIGNAL(aboutToRemoveAllFiles(SyncFileItem::Direction,bool*)),
|
||||
SLOT(slotAboutToRemoveAllFiles(SyncFileItem::Direction,bool*)));
|
||||
connect(_engine.data(), SIGNAL(aboutToRestoreBackup(bool*)),
|
||||
SLOT(slotAboutToRestoreBackup(bool*)));
|
||||
connect(_engine.data(), SIGNAL(folderDiscovered(bool,QString)), this, SLOT(slotFolderDiscovered(bool,QString)));
|
||||
connect(_engine.data(), SIGNAL(transmissionProgress(ProgressInfo)), this, SLOT(slotTransmissionProgress(ProgressInfo)));
|
||||
connect(_engine.data(), SIGNAL(itemCompleted(const SyncFileItem &, const PropagatorJob &)),
|
||||
this, SLOT(slotItemCompleted(const SyncFileItem &, const PropagatorJob &)));
|
||||
connect(_engine.data(), SIGNAL(syncItemDiscovered(const SyncFileItem &)), this, SLOT(slotSyncItemDiscovered(const SyncFileItem &)));
|
||||
connect(_engine.data(), SIGNAL(newBigFolder(QString)), this, SLOT(slotNewBigFolderDiscovered(QString)));
|
||||
|
||||
setDirtyNetworkLimits();
|
||||
|
||||
ConfigFile cfgFile;
|
||||
|
@ -896,27 +745,24 @@ void Folder::startSync(const QStringList &pathList)
|
|||
|
||||
void Folder::setDirtyNetworkLimits()
|
||||
{
|
||||
if (_engine) {
|
||||
|
||||
ConfigFile cfg;
|
||||
int downloadLimit = -75; // 75%
|
||||
int useDownLimit = cfg.useDownloadLimit();
|
||||
if (useDownLimit >= 1) {
|
||||
downloadLimit = cfg.downloadLimit() * 1000;
|
||||
} else if (useDownLimit == 0) {
|
||||
downloadLimit = 0;
|
||||
}
|
||||
|
||||
int uploadLimit = -75; // 75%
|
||||
int useUpLimit = cfg.useUploadLimit();
|
||||
if ( useUpLimit >= 1) {
|
||||
uploadLimit = cfg.uploadLimit() * 1000;
|
||||
} else if (useUpLimit == 0) {
|
||||
uploadLimit = 0;
|
||||
}
|
||||
|
||||
_engine->setNetworkLimits(uploadLimit, downloadLimit);
|
||||
ConfigFile cfg;
|
||||
int downloadLimit = -75; // 75%
|
||||
int useDownLimit = cfg.useDownloadLimit();
|
||||
if (useDownLimit >= 1) {
|
||||
downloadLimit = cfg.downloadLimit() * 1000;
|
||||
} else if (useDownLimit == 0) {
|
||||
downloadLimit = 0;
|
||||
}
|
||||
|
||||
int uploadLimit = -75; // 75%
|
||||
int useUpLimit = cfg.useUploadLimit();
|
||||
if ( useUpLimit >= 1) {
|
||||
uploadLimit = cfg.uploadLimit() * 1000;
|
||||
} else if (useUpLimit == 0) {
|
||||
uploadLimit = 0;
|
||||
}
|
||||
|
||||
_engine->setNetworkLimits(uploadLimit, downloadLimit);
|
||||
}
|
||||
|
||||
|
||||
|
@ -955,20 +801,11 @@ void Folder::slotSyncFinished(bool success)
|
|||
}
|
||||
bubbleUpSyncResult();
|
||||
|
||||
bool anotherSyncNeeded = false;
|
||||
if (_engine) {
|
||||
anotherSyncNeeded = _engine->isAnotherSyncNeeded();
|
||||
_engine.reset(0);
|
||||
}
|
||||
bool anotherSyncNeeded = _engine->isAnotherSyncNeeded();
|
||||
// _watcher->setEventsEnabledDelayed(2000);
|
||||
|
||||
|
||||
|
||||
// This is for sync state calculation
|
||||
_stateLastSyncItemsWithError = _stateLastSyncItemsWithErrorNew;
|
||||
_stateLastSyncItemsWithErrorNew.clear();
|
||||
_stateTaintedFolders.clear(); // heuristic: assume the sync had been done, new file watches needed to taint dirs
|
||||
|
||||
if (_csyncError) {
|
||||
_syncResult.setStatus(SyncResult::Error);
|
||||
qDebug() << " ** error Strings: " << _errors;
|
||||
|
@ -1064,10 +901,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);
|
||||
|
@ -1075,11 +908,6 @@ void Folder::slotItemCompleted(const SyncFileItem &item, const PropagatorJob& jo
|
|||
emit ProgressDispatcher::instance()->itemCompleted(alias(), item, job);
|
||||
}
|
||||
|
||||
void Folder::slotSyncItemDiscovered(const SyncFileItem & item)
|
||||
{
|
||||
emit ProgressDispatcher::instance()->syncItemDiscovered(alias(), item);
|
||||
}
|
||||
|
||||
void Folder::slotNewBigFolderDiscovered(const QString &newF)
|
||||
{
|
||||
auto newFolder = newF;
|
||||
|
|
|
@ -21,21 +21,13 @@
|
|||
#include "progressdispatcher.h"
|
||||
#include "syncjournaldb.h"
|
||||
#include "clientproxy.h"
|
||||
#include "syncfilestatus.h"
|
||||
#include "networkjobs.h"
|
||||
|
||||
#include <csync.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QHash>
|
||||
#include <QSet>
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
#include <qelapsedtimer.h>
|
||||
|
||||
class QThread;
|
||||
class QSettings;
|
||||
|
||||
|
@ -87,7 +79,7 @@ class Folder : public QObject
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Folder(const FolderDefinition& definition, QObject* parent = 0L);
|
||||
Folder(const FolderDefinition& definition, AccountState* accountState, QObject* parent = 0L);
|
||||
|
||||
~Folder();
|
||||
|
||||
|
@ -97,8 +89,7 @@ public:
|
|||
/**
|
||||
* The account the folder is configured on.
|
||||
*/
|
||||
void setAccountState( AccountState *account );
|
||||
AccountState* accountState() const;
|
||||
AccountState* accountState() const { return _accountState.data(); }
|
||||
|
||||
/**
|
||||
* alias or nickname
|
||||
|
@ -182,8 +173,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(); }
|
||||
|
@ -257,18 +247,15 @@ private slots:
|
|||
void slotFolderDiscovered(bool local, QString folderName);
|
||||
void slotTransmissionProgress(const ProgressInfo& pi);
|
||||
void slotItemCompleted(const SyncFileItem&, const PropagatorJob&);
|
||||
void slotSyncItemDiscovered(const SyncFileItem & item);
|
||||
|
||||
void slotRunEtagJob();
|
||||
void etagRetreived(const QString &);
|
||||
void etagRetreivedFromSyncEngine(const QString &);
|
||||
|
||||
void slotAboutToPropagate(SyncFileItemVector& );
|
||||
void slotThreadTreeWalkResult(const SyncFileItemVector& ); // after sync is done
|
||||
|
||||
void slotEmitFinishedDelayed();
|
||||
|
||||
void watcherSlot(QString);
|
||||
void slotNewBigFolderDiscovered(const QString &);
|
||||
|
||||
private:
|
||||
|
@ -278,10 +265,19 @@ private:
|
|||
|
||||
void checkLocalPath();
|
||||
|
||||
void createGuiLog(const QString& filename, SyncFileStatus status, int count,
|
||||
enum LogStatus {
|
||||
LogStatusRemove,
|
||||
LogStatusRename,
|
||||
LogStatusMove,
|
||||
LogStatusNew,
|
||||
LogStatusError,
|
||||
LogStatusUpdated
|
||||
};
|
||||
|
||||
void createGuiLog(const QString& filename, LogStatus status, int count,
|
||||
const QString& renameTarget = QString::null );
|
||||
|
||||
QPointer<AccountState> _accountState;
|
||||
AccountStatePtr _accountState;
|
||||
FolderDefinition _definition;
|
||||
|
||||
SyncResult _syncResult;
|
||||
|
@ -306,15 +302,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;
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
#endif
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QPointer>
|
||||
#include <QtCore>
|
||||
#include <QMutableSetIterator>
|
||||
#include <QSet>
|
||||
|
@ -50,7 +49,7 @@ FolderMan::FolderMan(QObject *parent) :
|
|||
Q_ASSERT(!_instance);
|
||||
_instance = this;
|
||||
|
||||
_socketApi = new SocketApi(this);
|
||||
_socketApi.reset(new SocketApi);
|
||||
|
||||
ConfigFile cfg;
|
||||
int polltime = cfg.remotePollInterval();
|
||||
|
@ -89,9 +88,7 @@ void FolderMan::unloadFolder( Folder *f )
|
|||
return;
|
||||
}
|
||||
|
||||
if( _socketApi ) {
|
||||
_socketApi->slotUnregisterPath(f->alias());
|
||||
}
|
||||
_socketApi->slotUnregisterPath(f->alias());
|
||||
|
||||
if( _folderWatchers.contains(f->alias())) {
|
||||
_folderWatchers.remove(f->alias());
|
||||
|
@ -108,6 +105,8 @@ void FolderMan::unloadFolder( Folder *f )
|
|||
this, SLOT(slotForwardFolderSyncStateChange()));
|
||||
disconnect(f, SIGNAL(syncPausedChanged(Folder*,bool)),
|
||||
this, SLOT(slotFolderSyncPaused(Folder*,bool)));
|
||||
disconnect(&f->syncEngine().syncFileStatusTracker(), SIGNAL(fileStatusChanged(const QString &, SyncFileStatus)),
|
||||
_socketApi.data(), SLOT(slotFileStatusChanged(const QString &, SyncFileStatus)));
|
||||
}
|
||||
|
||||
int FolderMan::unloadAndDeleteAllFolders()
|
||||
|
@ -147,15 +146,10 @@ void FolderMan::registerFolderMonitor( Folder *folder )
|
|||
// is lost this way, but we do not need it for the current implementation.
|
||||
connect(fw, SIGNAL(pathChanged(QString)), folder, SLOT(slotWatchedPathChanged(QString)));
|
||||
_folderWatchers.insert(folder->alias(), fw);
|
||||
|
||||
// This is at the moment only for the behaviour of the SocketApi.
|
||||
connect(fw, SIGNAL(pathChanged(QString)), folder, SLOT(watcherSlot(QString)));
|
||||
}
|
||||
|
||||
// register the folder with the socket API
|
||||
if( _socketApi ) {
|
||||
_socketApi->slotRegisterPath(folder->alias());
|
||||
}
|
||||
_socketApi->slotRegisterPath(folder->alias());
|
||||
}
|
||||
|
||||
void FolderMan::addMonitorPath( const QString& alias, const QString& path )
|
||||
|
@ -206,9 +200,8 @@ int FolderMan::setupFolders()
|
|||
foreach (const auto& folderAlias, settings->childGroups()) {
|
||||
FolderDefinition folderDefinition;
|
||||
if (FolderDefinition::load(*settings, folderAlias, &folderDefinition)) {
|
||||
Folder* f = addFolderInternal(folderDefinition);
|
||||
Folder* f = addFolderInternal(folderDefinition, account.data());
|
||||
if (f) {
|
||||
f->setAccountState( account.data() );
|
||||
slotScheduleSync(f);
|
||||
emit folderSyncStateChange(f);
|
||||
}
|
||||
|
@ -307,7 +300,7 @@ QString FolderMan::escapeAlias( const QString& alias )
|
|||
|
||||
SocketApi *FolderMan::socketApi()
|
||||
{
|
||||
return this->_socketApi;
|
||||
return this->_socketApi.data();
|
||||
}
|
||||
|
||||
QString FolderMan::unescapeAlias( const QString& alias )
|
||||
|
@ -395,10 +388,8 @@ Folder* FolderMan::setupFolderFromOldConfigFile(const QString &file, AccountStat
|
|||
folderDefinition.paused = paused;
|
||||
folderDefinition.ignoreHiddenFiles = ignoreHiddenFiles();
|
||||
|
||||
folder = addFolderInternal(folderDefinition);
|
||||
folder = addFolderInternal(folderDefinition, accountState);
|
||||
if (folder) {
|
||||
folder->setAccountState(accountState);
|
||||
|
||||
QStringList blackList = settings.value( QLatin1String("blackList")).toStringList();
|
||||
if (!blackList.empty()) {
|
||||
//migrate settings
|
||||
|
@ -478,21 +469,17 @@ void FolderMan::slotScheduleSync( Folder *f )
|
|||
}
|
||||
auto alias = f->alias();
|
||||
|
||||
if( _socketApi ) {
|
||||
// We want the SocketAPI to already now update so that it can show the EVAL icon
|
||||
// for files/folders. Only do this when not syncing, else we might get a lot
|
||||
// of those notifications.
|
||||
_socketApi->slotUpdateFolderView(f);
|
||||
}
|
||||
// We want the SocketAPI to already now update so that it can show the EVAL icon
|
||||
// for files/folders. Only do this when not syncing, else we might get a lot
|
||||
// of those notifications.
|
||||
_socketApi->slotUpdateFolderView(f);
|
||||
|
||||
qDebug() << "Schedule folder " << alias << " to sync!";
|
||||
|
||||
if( ! _scheduleQueue.contains(f) ) {
|
||||
if( !f->canSync() ) {
|
||||
qDebug() << "Folder is not ready to sync, not scheduled!";
|
||||
if( _socketApi ) {
|
||||
_socketApi->slotUpdateFolderView(f);
|
||||
}
|
||||
_socketApi->slotUpdateFolderView(f);
|
||||
return;
|
||||
}
|
||||
f->prepareToSync();
|
||||
|
@ -786,9 +773,8 @@ Folder* FolderMan::addFolder(AccountState* accountState, const FolderDefinition&
|
|||
return 0;
|
||||
}
|
||||
|
||||
auto folder = addFolderInternal(folderDefinition);
|
||||
if(folder && accountState) {
|
||||
folder->setAccountState(accountState);
|
||||
auto folder = addFolderInternal(folderDefinition, accountState);
|
||||
if(folder) {
|
||||
folder->saveToSettings();
|
||||
emit folderSyncStateChange(folder);
|
||||
emit folderListChanged(_folderMap);
|
||||
|
@ -796,9 +782,9 @@ Folder* FolderMan::addFolder(AccountState* accountState, const FolderDefinition&
|
|||
return folder;
|
||||
}
|
||||
|
||||
Folder* FolderMan::addFolderInternal(const FolderDefinition& folderDefinition)
|
||||
Folder* FolderMan::addFolderInternal(const FolderDefinition& folderDefinition, AccountState* accountState)
|
||||
{
|
||||
auto folder = new Folder(folderDefinition, this );
|
||||
auto folder = new Folder(folderDefinition, accountState, this );
|
||||
|
||||
qDebug() << "Adding folder to Folder Map " << folder;
|
||||
_folderMap[folder->alias()] = folder;
|
||||
|
@ -812,6 +798,8 @@ Folder* FolderMan::addFolderInternal(const FolderDefinition& folderDefinition)
|
|||
connect(folder, SIGNAL(syncFinished(SyncResult)), SLOT(slotFolderSyncFinished(SyncResult)));
|
||||
connect(folder, SIGNAL(syncStateChange()), SLOT(slotForwardFolderSyncStateChange()));
|
||||
connect(folder, SIGNAL(syncPausedChanged(Folder*,bool)), SLOT(slotFolderSyncPaused(Folder*,bool)));
|
||||
connect(&folder->syncEngine().syncFileStatusTracker(), SIGNAL(fileStatusChanged(const QString &, SyncFileStatus)),
|
||||
_socketApi.data(), SLOT(slotFileStatusChanged(const QString &, SyncFileStatus)));
|
||||
|
||||
registerFolderMonitor(folder);
|
||||
return folder;
|
||||
|
@ -935,15 +923,13 @@ bool FolderMan::startFromScratch( const QString& localFolder )
|
|||
}
|
||||
// Disconnect the socket api from the database to avoid that locking of the
|
||||
// db file does not allow to move this dir.
|
||||
if( _socketApi ) {
|
||||
Folder *f = folderForPath(localFolder);
|
||||
if(f) {
|
||||
if( localFolder.startsWith(f->path()) ) {
|
||||
_socketApi->slotUnregisterPath(f->alias());
|
||||
}
|
||||
f->journalDb()->close();
|
||||
f->slotTerminateSync(); // Normally it should not be running, but viel hilft viel
|
||||
Folder *f = folderForPath(localFolder);
|
||||
if(f) {
|
||||
if( localFolder.startsWith(f->path()) ) {
|
||||
_socketApi->slotUnregisterPath(f->alias());
|
||||
}
|
||||
f->journalDb()->close();
|
||||
f->slotTerminateSync(); // Normally it should not be running, but viel hilft viel
|
||||
}
|
||||
|
||||
// Make a backup of the folder/file.
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <QObject>
|
||||
#include <QQueue>
|
||||
#include <QList>
|
||||
#include <QPointer>
|
||||
|
||||
#include "folder.h"
|
||||
#include "folderwatcher.h"
|
||||
|
@ -202,7 +201,7 @@ private:
|
|||
/** Adds a new folder, does not add it to the account settings and
|
||||
* does not set an account on the new folder.
|
||||
*/
|
||||
Folder* addFolderInternal(const FolderDefinition& folderDefinition);
|
||||
Folder* addFolderInternal(const FolderDefinition& folderDefinition, AccountState* accountState);
|
||||
|
||||
/* unloads a folder object, does not delete it */
|
||||
void unloadFolder( Folder * );
|
||||
|
@ -228,7 +227,7 @@ private:
|
|||
QPointer<RequestEtagJob> _currentEtagJob; // alias of Folder running the current RequestEtagJob
|
||||
|
||||
QMap<QString, FolderWatcher*> _folderWatchers;
|
||||
QPointer<SocketApi> _socketApi;
|
||||
QScopedPointer<SocketApi> _socketApi;
|
||||
|
||||
/** The aliases of folders that shall be synced. */
|
||||
QQueue<Folder*> _scheduleQueue;
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "creds/abstractcredentials.h"
|
||||
|
||||
#include <QDesktopServices>
|
||||
#include <QDir>
|
||||
#include <QMessageBox>
|
||||
#include <QSignalMapper>
|
||||
|
||||
|
@ -350,7 +351,7 @@ void ownCloudGui::addAccountContextMenu(AccountStatePtr accountState, QMenu *men
|
|||
bool onePaused = false;
|
||||
bool allPaused = true;
|
||||
foreach (Folder* folder, folderMan->map()) {
|
||||
if (folder->accountState() != accountState) {
|
||||
if (folder->accountState() != accountState.data()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,6 @@ class ShareDialog;
|
|||
class Application;
|
||||
class LogBrowser;
|
||||
class AccountState;
|
||||
typedef QSharedPointer<AccountState> AccountStatePtr;
|
||||
|
||||
/**
|
||||
* @brief The ownCloudGui class
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "selectivesyncdialog.h"
|
||||
#include "folder.h"
|
||||
#include "account.h"
|
||||
#include "excludedfiles.h"
|
||||
#include "networkjobs.h"
|
||||
#include "theme.h"
|
||||
#include "folderman.h"
|
||||
|
@ -176,21 +177,11 @@ void SelectiveSyncTreeView::slotUpdateDirectories(QStringList list)
|
|||
pathToRemove.append('/');
|
||||
|
||||
// Check for excludes.
|
||||
//
|
||||
// We would like to use Folder::isFileExcluded, but the folder doesn't
|
||||
// exist yet. So we just create one temporarily...
|
||||
FolderDefinition def;
|
||||
def.localPath = pathToRemove;
|
||||
def.ignoreHiddenFiles = FolderMan::instance()->ignoreHiddenFiles();
|
||||
Folder f(def);
|
||||
QMutableListIterator<QString> it(list);
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
QString path = it.value();
|
||||
path.remove(pathToRemove);
|
||||
if (f.isFileExcludedRelative(path)) {
|
||||
if (ExcludedFiles::instance().isExcluded(it.value(), pathToRemove, FolderMan::instance()->ignoreHiddenFiles()))
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// Since / cannot be in the blacklist, expand it to the actual
|
||||
|
|
|
@ -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"
|
||||
|
@ -113,10 +114,6 @@ SocketApi::SocketApi(QObject* parent)
|
|||
|
||||
// folder watcher
|
||||
connect(FolderMan::instance(), SIGNAL(folderSyncStateChange(Folder*)), this, SLOT(slotUpdateFolderView(Folder*)));
|
||||
connect(ProgressDispatcher::instance(), SIGNAL(itemCompleted(QString, const SyncFileItem &, const PropagatorJob &)),
|
||||
SLOT(slotItemCompleted(QString, const SyncFileItem &)));
|
||||
connect(ProgressDispatcher::instance(), SIGNAL(syncItemDiscovered(QString, const SyncFileItem &)),
|
||||
this, SLOT(slotSyncItemDiscovered(QString, const SyncFileItem &)));
|
||||
}
|
||||
|
||||
SocketApi::~SocketApi()
|
||||
|
@ -195,24 +192,8 @@ void SocketApi::slotRegisterPath( const QString& alias )
|
|||
void SocketApi::slotUnregisterPath( const QString& alias )
|
||||
{
|
||||
Folder *f = FolderMan::instance()->folder(alias);
|
||||
if (f) {
|
||||
if (f)
|
||||
broadcastMessage(QLatin1String("UNREGISTER_PATH"), f->path(), QString::null, true );
|
||||
|
||||
if( _dbQueries.contains(f)) {
|
||||
auto h = _dbQueries[f];
|
||||
if( h ) {
|
||||
h->finish();
|
||||
}
|
||||
_dbQueries.remove(f);
|
||||
}
|
||||
if( _openDbs.contains(f) ) {
|
||||
auto db = _openDbs[f];
|
||||
if( db ) {
|
||||
db->close();
|
||||
}
|
||||
_openDbs.remove(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SocketApi::slotUpdateFolderView(Folder *f)
|
||||
|
@ -231,7 +212,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 {
|
||||
|
@ -240,48 +221,11 @@ void SocketApi::slotUpdateFolderView(Folder *f)
|
|||
}
|
||||
}
|
||||
|
||||
void SocketApi::slotItemCompleted(const QString &folder, const SyncFileItem &item)
|
||||
void SocketApi::slotFileStatusChanged(const QString& systemFileName, SyncFileStatus fileStatus)
|
||||
{
|
||||
if (_listeners.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Folder *f = FolderMan::instance()->folder(folder);
|
||||
if (!f) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto status = this->fileStatus(f, item.destination());
|
||||
const QString path = f->path() + item.destination();
|
||||
broadcastMessage(QLatin1String("STATUS"), path, status.toSocketAPIString());
|
||||
broadcastMessage(QLatin1String("STATUS"), systemFileName, fileStatus.toSocketAPIString());
|
||||
}
|
||||
|
||||
void SocketApi::slotSyncItemDiscovered(const QString &folder, const SyncFileItem &item)
|
||||
{
|
||||
if (_listeners.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Folder *f = FolderMan::instance()->folder(folder);
|
||||
if (!f) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString path = f->path() + item.destination();
|
||||
|
||||
// the trailing slash for directories must be appended as the filenames coming in
|
||||
// from the plugins have that too. Otherwise the matching entry item is not found
|
||||
// in the plugin.
|
||||
if( item._type == SyncFileItem::Type::Directory ) {
|
||||
path += QLatin1Char('/');
|
||||
}
|
||||
|
||||
const QString command = QLatin1String("SYNC");
|
||||
broadcastMessage(QLatin1String("STATUS"), path, command);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SocketApi::sendMessage(QIODevice *socket, const QString& message, bool doWait)
|
||||
{
|
||||
DEBUG << "Sending message: " << message;
|
||||
|
@ -346,7 +290,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();
|
||||
}
|
||||
|
@ -383,11 +327,10 @@ 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 &&
|
||||
fileStatus.tag() != SyncFileStatus::STATUS_UPDATED) {
|
||||
if (fileStatus.tag() != SyncFileStatus::StatusUpToDate) {
|
||||
const QString message = QLatin1String("SHARE:NOTSYNCED:")+QDir::toNativeSeparators(localFile);
|
||||
sendMessage(socket, message);
|
||||
return;
|
||||
|
@ -402,7 +345,7 @@ void SocketApi::command_SHARE(const QString& localFile, QIODevice* socket)
|
|||
return;
|
||||
}
|
||||
|
||||
SyncJournalFileRecord rec = dbFileRecord_capi(shareFolder, localFileClean);
|
||||
SyncJournalFileRecord rec = shareFolder->journalDb()->getFileRecord(localFileClean);
|
||||
|
||||
bool allowReshare = true; // lets assume the good
|
||||
if( rec.isValid() ) {
|
||||
|
@ -439,11 +382,10 @@ 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 &&
|
||||
fileStatus.tag() != SyncFileStatus::STATUS_UPDATED) {
|
||||
if (fileStatus.tag() != SyncFileStatus::StatusUpToDate) {
|
||||
const QString message = QLatin1String("SHARE_STATUS:NOTSYNCED:")+QDir::toNativeSeparators(localFile);
|
||||
sendMessage(socket, message);
|
||||
return;
|
||||
|
@ -494,227 +436,4 @@ QString SocketApi::buildRegisterPathMessage(const QString& path)
|
|||
return message;
|
||||
}
|
||||
|
||||
SqlQuery* SocketApi::getSqlQuery( Folder *folder )
|
||||
{
|
||||
if( !folder ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( _dbQueries.contains(folder) ) {
|
||||
return _dbQueries[folder].data();
|
||||
}
|
||||
|
||||
/* No valid sql query object yet for this folder */
|
||||
int rc;
|
||||
const QString sql("SELECT inode, modtime, type, md5, fileid, remotePerm FROM "
|
||||
"metadata WHERE phash=?1");
|
||||
QString dbFileName = folder->journalDb()->databaseFilePath();
|
||||
|
||||
QFileInfo fi(dbFileName);
|
||||
if( fi.exists() ) {
|
||||
auto db = QSharedPointer<SqlDatabase>::create();
|
||||
|
||||
if( db && db->openReadOnly(dbFileName) ) {
|
||||
_openDbs.insert(folder, db);
|
||||
|
||||
QSharedPointer<SqlQuery> query(new SqlQuery(*db));
|
||||
rc = query->prepare(sql);
|
||||
|
||||
if( rc != SQLITE_OK ) {
|
||||
qDebug() << "Unable to prepare the query statement:" << rc;
|
||||
return 0; // do not insert into hash
|
||||
}
|
||||
_dbQueries.insert( folder, query);
|
||||
return query.data();
|
||||
} else {
|
||||
qDebug() << "Unable to open db" << dbFileName;
|
||||
}
|
||||
} else {
|
||||
qDebug() << Q_FUNC_INFO << "Journal to query does not yet exist.";
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SyncJournalFileRecord SocketApi::dbFileRecord_capi( Folder *folder, QString fileName )
|
||||
{
|
||||
if( !(folder && folder->journalDb()) ) {
|
||||
return SyncJournalFileRecord();
|
||||
}
|
||||
|
||||
if( fileName.startsWith( folder->path() )) {
|
||||
fileName.remove(0, folder->path().length());
|
||||
}
|
||||
|
||||
// remove trailing slash
|
||||
if( fileName.endsWith( QLatin1Char('/') ) ) {
|
||||
fileName.truncate(fileName.length()-1);
|
||||
}
|
||||
SqlQuery *query = getSqlQuery(folder);
|
||||
SyncJournalFileRecord rec;
|
||||
|
||||
if( query ) {
|
||||
qlonglong phash = SyncJournalDb::getPHash( fileName );
|
||||
query->bindValue(1, phash);
|
||||
// int column_count = sqlite3_column_count(stmt);
|
||||
|
||||
if (query->next()) {
|
||||
rec._path = fileName;
|
||||
rec._inode = query->int64Value(0);
|
||||
rec._modtime = Utility::qDateTimeFromTime_t( query->int64Value(1));
|
||||
rec._type = query->intValue(2);
|
||||
rec._etag = query->baValue(3);
|
||||
rec._fileId = query->baValue(4);
|
||||
rec._remotePerm = query->baValue(5);
|
||||
}
|
||||
query->reset_and_clear_bindings();
|
||||
}
|
||||
return rec;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = dbFileRecord_capi(folder, 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()) {
|
||||
if (rec._remotePerm.isNull()) {
|
||||
// probably owncloud 6, that does not have permissions flag yet.
|
||||
QString url = folder->remoteUrl().toString() + fileName;
|
||||
if (url.contains( folder->accountState()->account()->davPath() + QLatin1String("Shared/") )) {
|
||||
status.setSharedWithMe(true);
|
||||
}
|
||||
} else if (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 = dbFileRecord_capi(folder, 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 = dbFileRecord_capi(folder, 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
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#define SOCKETAPI_H
|
||||
|
||||
#include "syncfileitem.h"
|
||||
#include "syncjournalfilerecord.h"
|
||||
#include "syncfilestatus.h"
|
||||
#include "ownsql.h"
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
|
@ -45,7 +45,7 @@ class SocketApi : public QObject
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SocketApi(QObject* parent);
|
||||
explicit SocketApi(QObject* parent = 0);
|
||||
virtual ~SocketApi();
|
||||
|
||||
public slots:
|
||||
|
@ -61,14 +61,9 @@ private slots:
|
|||
void slotNewConnection();
|
||||
void onLostConnection();
|
||||
void slotReadSocket();
|
||||
void slotItemCompleted(const QString &, const SyncFileItem &);
|
||||
void slotSyncItemDiscovered(const QString &, const SyncFileItem &);
|
||||
void slotFileStatusChanged(const QString& systemFileName, SyncFileStatus fileStatus);
|
||||
|
||||
private:
|
||||
SyncFileStatus fileStatus(Folder *folder, const QString& systemFileName);
|
||||
SyncJournalFileRecord dbFileRecord_capi( Folder *folder, QString fileName );
|
||||
SqlQuery *getSqlQuery( Folder *folder );
|
||||
|
||||
void sendMessage(QIODevice* socket, const QString& message, bool doWait = false);
|
||||
void broadcastMessage(const QString& verb, const QString &path, const QString &status = QString::null, bool doWait = false);
|
||||
|
||||
|
@ -84,8 +79,6 @@ private:
|
|||
|
||||
QList<QIODevice*> _listeners;
|
||||
SocketApiServer _localServer;
|
||||
QHash<Folder*, QSharedPointer<SqlQuery>> _dbQueries;
|
||||
QHash<Folder*, QSharedPointer<SqlDatabase>> _openDbs;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ set(libsync_SRCS
|
|||
propagateremotemkdir.cpp
|
||||
syncengine.cpp
|
||||
syncfilestatus.cpp
|
||||
syncfilestatustracker.cpp
|
||||
syncjournaldb.cpp
|
||||
syncjournalfilerecord.cpp
|
||||
syncresult.cpp
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace OCC {
|
|||
class Account;
|
||||
typedef QSharedPointer<Account> AccountPtr;
|
||||
class AccountState;
|
||||
typedef QSharedPointer<AccountState> AccountStatePtr;
|
||||
typedef QExplicitlySharedDataPointer<AccountState> AccountStatePtr;
|
||||
|
||||
} // namespace OCC
|
||||
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
#include "excludedfiles.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QReadLocker>
|
||||
#include <QWriteLocker>
|
||||
|
||||
extern "C" {
|
||||
#include "std/c_string.h"
|
||||
|
@ -44,13 +42,11 @@ ExcludedFiles& ExcludedFiles::instance()
|
|||
|
||||
void ExcludedFiles::addExcludeFilePath(const QString& path)
|
||||
{
|
||||
QWriteLocker locker(&_mutex);
|
||||
_excludeFiles.append(path);
|
||||
}
|
||||
|
||||
bool ExcludedFiles::reloadExcludes()
|
||||
{
|
||||
QWriteLocker locker(&_mutex);
|
||||
c_strlist_destroy(*_excludesPtr);
|
||||
*_excludesPtr = NULL;
|
||||
|
||||
|
@ -62,16 +58,20 @@ bool ExcludedFiles::reloadExcludes()
|
|||
return success;
|
||||
}
|
||||
|
||||
CSYNC_EXCLUDE_TYPE ExcludedFiles::isExcluded(
|
||||
const QString& fullPath,
|
||||
const QString& relativePath,
|
||||
bool ExcludedFiles::isExcluded(
|
||||
const QString& filePath,
|
||||
const QString& basePath,
|
||||
bool excludeHidden) const
|
||||
{
|
||||
QFileInfo fi(fullPath);
|
||||
if (!filePath.startsWith(basePath)) {
|
||||
// Mark paths we're not responsible for as excluded...
|
||||
return true;
|
||||
}
|
||||
|
||||
QFileInfo fi(filePath);
|
||||
if( excludeHidden ) {
|
||||
if( fi.isHidden() || fi.fileName().startsWith(QLatin1Char('.')) ) {
|
||||
return CSYNC_FILE_EXCLUDE_HIDDEN;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,6 +79,11 @@ CSYNC_EXCLUDE_TYPE ExcludedFiles::isExcluded(
|
|||
if (fi.isDir()) {
|
||||
type = CSYNC_FTW_TYPE_DIR;
|
||||
}
|
||||
QReadLocker lock(&_mutex);
|
||||
return csync_excluded_no_ctx(*_excludesPtr, relativePath.toUtf8(), type);
|
||||
|
||||
QString relativePath = filePath.mid(basePath.size());
|
||||
if (relativePath.endsWith(QLatin1Char('/'))) {
|
||||
relativePath.chop(1);
|
||||
}
|
||||
|
||||
return csync_excluded_no_ctx(*_excludesPtr, relativePath.toUtf8(), type) != CSYNC_NOT_EXCLUDED;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include "owncloudlib.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QReadWriteLock>
|
||||
#include <QStringList>
|
||||
|
||||
extern "C" {
|
||||
|
@ -49,14 +48,12 @@ public:
|
|||
/**
|
||||
* Checks whether a file or directory should be excluded.
|
||||
*
|
||||
* @param fullPath the absolute path to the file
|
||||
* @param relativePath path relative to the folder
|
||||
*
|
||||
* For directories, the paths must not contain a trailing /.
|
||||
* @param filePath the absolute path to the file
|
||||
* @param basePath folder path from which to apply exclude rules
|
||||
*/
|
||||
CSYNC_EXCLUDE_TYPE isExcluded(
|
||||
const QString& fullPath,
|
||||
const QString& relativePath,
|
||||
bool isExcluded(
|
||||
const QString& filePath,
|
||||
const QString& basePath,
|
||||
bool excludeHidden) const;
|
||||
|
||||
public slots:
|
||||
|
@ -70,7 +67,6 @@ private:
|
|||
// but the pointer can be in a csync_context so that it can itself also query the list.
|
||||
c_strlist_t** _excludesPtr;
|
||||
QStringList _excludeFiles;
|
||||
mutable QReadWriteLock _mutex;
|
||||
};
|
||||
|
||||
} // namespace OCC
|
||||
|
|
|
@ -239,8 +239,6 @@ signals:
|
|||
const SyncFileItem & item,
|
||||
const PropagatorJob & job);
|
||||
|
||||
void syncItemDiscovered(const QString &folder, const SyncFileItem & item);
|
||||
|
||||
protected:
|
||||
void setProgressInfo(const QString& folder, const ProgressInfo& progress);
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ extern "C" const char *csync_instruction_str(enum csync_instructions_e instr);
|
|||
|
||||
namespace OCC {
|
||||
|
||||
bool SyncEngine::_syncRunning = false;
|
||||
bool SyncEngine::s_anySyncRunning = false;
|
||||
|
||||
qint64 SyncEngine::minimumFileAgeForUpload = 2000;
|
||||
|
||||
|
@ -60,6 +60,7 @@ SyncEngine::SyncEngine(AccountPtr account, const QString& localPath,
|
|||
const QUrl& remoteURL, const QString& remotePath, OCC::SyncJournalDb* journal)
|
||||
: _account(account)
|
||||
, _needsUpdate(false)
|
||||
, _syncRunning(false)
|
||||
, _localPath(localPath)
|
||||
, _remoteUrl(remoteURL)
|
||||
, _remotePath(remotePath)
|
||||
|
@ -92,13 +93,14 @@ 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");
|
||||
_thread.start();
|
||||
}
|
||||
|
||||
SyncEngine::~SyncEngine()
|
||||
{
|
||||
abort();
|
||||
_excludedFiles.reset();
|
||||
csync_destroy(_csync_ctx);
|
||||
_thread.quit();
|
||||
|
@ -662,10 +664,11 @@ void SyncEngine::startSync()
|
|||
}
|
||||
}
|
||||
|
||||
Q_ASSERT(!s_anySyncRunning);
|
||||
Q_ASSERT(!_syncRunning);
|
||||
s_anySyncRunning = true;
|
||||
_syncRunning = true;
|
||||
|
||||
Q_ASSERT(_csync_ctx);
|
||||
_anotherSyncNeeded = false;
|
||||
|
||||
if (!QDir(_localPath).exists()) {
|
||||
// No _tr, it should only occur in non-mirall
|
||||
|
@ -741,6 +744,7 @@ void SyncEngine::startSync()
|
|||
|
||||
qDebug() << "#### Discovery start #################################################### >>";
|
||||
|
||||
_thread.start();
|
||||
_discoveryMainThread = new DiscoveryMainThread(account());
|
||||
_discoveryMainThread->setParent(this);
|
||||
connect(this, SIGNAL(finished(bool)), _discoveryMainThread, SLOT(deleteLater()));
|
||||
|
@ -1000,6 +1004,7 @@ void SyncEngine::finalize(bool success)
|
|||
qDebug() << "CSync run took " << _stopWatch.addLapTime(QLatin1String("Sync Finished"));
|
||||
_stopWatch.stop();
|
||||
|
||||
s_anySyncRunning = false;
|
||||
_syncRunning = false;
|
||||
emit finished(success);
|
||||
|
||||
|
@ -1301,33 +1306,14 @@ void SyncEngine::restoreOldFiles()
|
|||
}
|
||||
}
|
||||
|
||||
bool SyncEngine::estimateState(QString fn, csync_ftw_type_e t, SyncFileStatus* s)
|
||||
SyncFileItem* SyncEngine::findSyncItem(const QString &fileName) const
|
||||
{
|
||||
Q_UNUSED(t);
|
||||
QString pat(fn);
|
||||
if( t == CSYNC_FTW_TYPE_DIR && ! fn.endsWith(QLatin1Char('/'))) {
|
||||
pat.append(QLatin1Char('/'));
|
||||
}
|
||||
|
||||
Q_FOREACH(const SyncFileItemPtr &item, _syncedItems) {
|
||||
//qDebug() << Q_FUNC_INFO << fn << item->_status << item->_file << fn.startsWith(item->_file) << item->_file.startsWith(fn);
|
||||
|
||||
if (item->_file.startsWith(pat) ||
|
||||
item->_file == fn || item->_renameTarget == fn /* the same directory or file */) {
|
||||
if (item->_status == SyncFileItem::NormalError
|
||||
|| item->_status == SyncFileItem::FatalError)
|
||||
s->set(SyncFileStatus::STATUS_ERROR);
|
||||
else if (item->_status == SyncFileItem::FileIgnored)
|
||||
s->set(SyncFileStatus::STATUS_IGNORE);
|
||||
else if (item->_status == SyncFileItem::Success)
|
||||
s->set(SyncFileStatus::STATUS_UPDATED);
|
||||
else
|
||||
s->set(SyncFileStatus::STATUS_EVAL);
|
||||
qDebug() << Q_FUNC_INFO << "Setting" << fn << "to" << s->toSocketAPIString();
|
||||
return true;
|
||||
}
|
||||
// Directories will appear in this list as well, and will get their status set once all children have been propagated
|
||||
if ((item->_file == fileName || item->_renameTarget == fileName))
|
||||
return item.data();
|
||||
}
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
qint64 SyncEngine::timeSinceFileTouched(const QString& fn) const
|
||||
|
|
|
@ -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"
|
||||
|
@ -69,19 +69,23 @@ public:
|
|||
/* Abort the sync. Called from the main thread */
|
||||
void abort();
|
||||
|
||||
bool isSyncRunning() const { return _syncRunning; }
|
||||
|
||||
/* Set the maximum size a folder can have without asking for confirmation
|
||||
* -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; }
|
||||
|
||||
bool estimateState(QString fn, csync_ftw_type_e t, SyncFileStatus* s);
|
||||
SyncFileItem* findSyncItem(const QString &fileName) const;
|
||||
|
||||
/** Get the ms since a file was touched, or -1 if it wasn't.
|
||||
*
|
||||
|
@ -91,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
|
||||
|
@ -168,7 +172,7 @@ private:
|
|||
// cleanup and emit the finished signal
|
||||
void finalize(bool success);
|
||||
|
||||
static bool _syncRunning; //true when one sync is running somewhere (for debugging)
|
||||
static bool s_anySyncRunning; //true when one sync is running somewhere (for debugging)
|
||||
|
||||
// Must only be acessed during update and reconcile
|
||||
QMap<QString, SyncFileItemPtr> _syncItemMap;
|
||||
|
@ -180,6 +184,7 @@ private:
|
|||
AccountPtr _account;
|
||||
CSYNC *_csync_ctx;
|
||||
bool _needsUpdate;
|
||||
bool _syncRunning;
|
||||
QString _localPath;
|
||||
QUrl _remoteUrl;
|
||||
QString _remotePath;
|
||||
|
@ -208,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
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
namespace OCC {
|
||||
SyncFileStatus::SyncFileStatus()
|
||||
:_tag(STATUS_NONE), _sharedWithMe(false)
|
||||
:_tag(StatusNone), _sharedWithMe(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -53,30 +53,22 @@ QString SyncFileStatus::toSocketAPIString() const
|
|||
|
||||
switch(_tag)
|
||||
{
|
||||
case STATUS_NONE:
|
||||
case StatusNone:
|
||||
statusString = QLatin1String("NONE");
|
||||
break;
|
||||
case STATUS_EVAL:
|
||||
case StatusSync:
|
||||
statusString = QLatin1String("SYNC");
|
||||
break;
|
||||
case STATUS_NEW:
|
||||
statusString = QLatin1String("NEW");
|
||||
break;
|
||||
case STATUS_IGNORE:
|
||||
case StatusWarning:
|
||||
// The protocol says IGNORE, but all implementations show a yellow warning sign.
|
||||
statusString = QLatin1String("IGNORE");
|
||||
break;
|
||||
case STATUS_UPTODATE:
|
||||
case STATUS_UPDATED:
|
||||
case StatusUpToDate:
|
||||
statusString = QLatin1String("OK");
|
||||
break;
|
||||
case STATUS_STAT_ERROR:
|
||||
case STATUS_ERROR:
|
||||
case StatusError:
|
||||
statusString = QLatin1String("ERROR");
|
||||
break;
|
||||
default:
|
||||
qWarning() << "This status should not be here:" << _tag;
|
||||
Q_ASSERT(false);
|
||||
statusString = QLatin1String("NONE");
|
||||
}
|
||||
if(_sharedWithMe) {
|
||||
statusString += QLatin1String("+SWM");
|
||||
|
|
|
@ -28,18 +28,11 @@ class OWNCLOUDSYNC_EXPORT SyncFileStatus
|
|||
{
|
||||
public:
|
||||
enum SyncFileStatusTag {
|
||||
STATUS_NONE,
|
||||
STATUS_EVAL,
|
||||
STATUS_REMOVE,
|
||||
STATUS_RENAME,
|
||||
STATUS_MOVE,
|
||||
STATUS_NEW,
|
||||
STATUS_CONFLICT,
|
||||
STATUS_IGNORE,
|
||||
STATUS_UPTODATE,
|
||||
STATUS_STAT_ERROR,
|
||||
STATUS_ERROR,
|
||||
STATUS_UPDATED
|
||||
StatusNone,
|
||||
StatusSync,
|
||||
StatusWarning,
|
||||
StatusUpToDate,
|
||||
StatusError,
|
||||
};
|
||||
|
||||
SyncFileStatus();
|
||||
|
|
204
src/libsync/syncfilestatustracker.cpp
Normal file
204
src/libsync/syncfilestatustracker.cpp
Normal file
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||
* Copyright (C) by Jocelyn Turcotte <jturcotte@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; 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 "syncengine.h"
|
||||
#include "syncjournaldb.h"
|
||||
#include "syncjournalfilerecord.h"
|
||||
|
||||
namespace OCC {
|
||||
|
||||
static SyncFileStatus::SyncFileStatusTag lookupProblem(const QString &pathToMatch, const std::map<QString, SyncFileStatus::SyncFileStatusTag> &problemMap)
|
||||
{
|
||||
auto lower = problemMap.lower_bound(pathToMatch);
|
||||
for (auto it = lower; it != problemMap.cend(); ++it) {
|
||||
const QString &problemPath = it->first;
|
||||
SyncFileStatus::SyncFileStatusTag severity = it->second;
|
||||
// qDebug() << Q_FUNC_INFO << pathToMatch << severity << problemPath;
|
||||
if (problemPath == pathToMatch) {
|
||||
return severity;
|
||||
} else if (severity == SyncFileStatus::StatusError && problemPath.startsWith(pathToMatch) && problemPath.at(pathToMatch.size()) == '/') {
|
||||
Q_ASSERT(!pathToMatch.endsWith('/'));
|
||||
return SyncFileStatus::StatusWarning;
|
||||
} else if (!problemPath.startsWith(pathToMatch)) {
|
||||
// Starting at lower_bound we get the first path that is not smaller,
|
||||
// since: "/a/" < "/a/aa" < "/a/aa/aaa" < "/a/ab/aba"
|
||||
// If problemMap keys are ["/a/aa/aaa", "/a/ab/aba"] and pathToMatch == "/a/aa",
|
||||
// lower_bound(pathToMatch) will point to "/a/aa/aaa", and the moment that
|
||||
// problemPath.startsWith(pathToMatch) == false, we know that we've looked
|
||||
// at everything that interest us.
|
||||
break;
|
||||
}
|
||||
}
|
||||
return SyncFileStatus::StatusNone;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 inline bool showErrorInSocketApi(const SyncFileItem& item)
|
||||
{
|
||||
const auto status = item._status;
|
||||
return status == SyncFileItem::NormalError
|
||||
|| status == SyncFileItem::FatalError
|
||||
|| item._hasBlacklistEntry;
|
||||
}
|
||||
|
||||
static inline bool showWarningInSocketApi(const SyncFileItem& item)
|
||||
{
|
||||
const auto status = item._status;
|
||||
return status == SyncFileItem::FileIgnored
|
||||
|| status == SyncFileItem::Conflict
|
||||
|| status == SyncFileItem::Restoration;
|
||||
}
|
||||
|
||||
SyncFileStatusTracker::SyncFileStatusTracker(SyncEngine *syncEngine)
|
||||
: _syncEngine(syncEngine)
|
||||
{
|
||||
connect(syncEngine, SIGNAL(aboutToPropagate(SyncFileItemVector&)),
|
||||
this, SLOT(slotAboutToPropagate(SyncFileItemVector&)));
|
||||
connect(syncEngine, SIGNAL(itemCompleted(const SyncFileItem&, const PropagatorJob&)),
|
||||
this, SLOT(slotItemCompleted(const SyncFileItem&)));
|
||||
}
|
||||
|
||||
|
||||
SyncFileStatus SyncFileStatusTracker::fileStatus(const QString& systemFileName)
|
||||
{
|
||||
QString fileName = systemFileName.normalized(QString::NormalizationForm_C);
|
||||
if( fileName.endsWith(QLatin1Char('/')) ) {
|
||||
fileName.truncate(fileName.length()-1);
|
||||
qDebug() << "Removed trailing slash: " << fileName;
|
||||
}
|
||||
|
||||
// The SyncEngine won't notify us at all for CSYNC_FILE_SILENTLY_EXCLUDED
|
||||
// and CSYNC_FILE_EXCLUDE_AND_REMOVE excludes. Even though it's possible
|
||||
// that the status of CSYNC_FILE_EXCLUDE_LIST excludes will change if the user
|
||||
// update the exclude list at runtime and doing it statically here removes
|
||||
// our ability to notify changes through the fileStatusChanged signal,
|
||||
// it's an acceptable compromize to treat all exclude types the same.
|
||||
if( _syncEngine->excludedFiles().isExcluded(_syncEngine->localPath() + fileName, _syncEngine->localPath(), _syncEngine->ignoreHiddenFiles()) )
|
||||
return SyncFileStatus(SyncFileStatus::StatusWarning);
|
||||
|
||||
SyncFileItem* item = _syncEngine->findSyncItem(fileName);
|
||||
if (item)
|
||||
return fileStatus(*item);
|
||||
|
||||
// If we're not currently syncing that file, look it up in the database to know if it's shared
|
||||
SyncJournalFileRecord rec = _syncEngine->journal()->getFileRecord(fileName);
|
||||
if (rec.isValid())
|
||||
return fileStatus(rec.toSyncFileItem());
|
||||
|
||||
// Must be a new file, wait for the filesystem watcher to trigger a sync
|
||||
return SyncFileStatus();
|
||||
}
|
||||
|
||||
void SyncFileStatusTracker::slotAboutToPropagate(SyncFileItemVector& items)
|
||||
{
|
||||
std::map<QString, SyncFileStatus::SyncFileStatusTag> oldProblems;
|
||||
std::swap(_syncProblems, oldProblems);
|
||||
|
||||
foreach (const SyncFileItemPtr &item, items) {
|
||||
// qDebug() << Q_FUNC_INFO << "Investigating" << item->destination() << item->_status;
|
||||
|
||||
if (showErrorInSocketApi(*item))
|
||||
_syncProblems[item->_file] = SyncFileStatus::StatusError;
|
||||
else if (showWarningInSocketApi(*item))
|
||||
_syncProblems[item->_file] = SyncFileStatus::StatusWarning;
|
||||
|
||||
emit fileStatusChanged(getSystemDestination(*item), fileStatus(*item));
|
||||
}
|
||||
|
||||
// Make sure to push any status that might have been resolved indirectly since the last sync
|
||||
// (like an error file being deleted from disk)
|
||||
for (auto it = _syncProblems.begin(); it != _syncProblems.end(); ++it)
|
||||
oldProblems.erase(it->first);
|
||||
for (auto it = oldProblems.begin(); it != oldProblems.end(); ++it) {
|
||||
const QString &path = it->first;
|
||||
SyncFileStatus::SyncFileStatusTag severity = it->second;
|
||||
if (severity == SyncFileStatus::StatusError)
|
||||
invalidateParentPaths(path);
|
||||
emit fileStatusChanged(_syncEngine->localPath() + path, fileStatus(path));
|
||||
}
|
||||
}
|
||||
|
||||
void SyncFileStatusTracker::slotItemCompleted(const SyncFileItem &item)
|
||||
{
|
||||
// qDebug() << Q_FUNC_INFO << item.destination() << item._status;
|
||||
|
||||
if (showErrorInSocketApi(item)) {
|
||||
_syncProblems[item._file] = SyncFileStatus::StatusError;
|
||||
invalidateParentPaths(item.destination());
|
||||
} else if (showWarningInSocketApi(item)) {
|
||||
_syncProblems[item._file] = SyncFileStatus::StatusWarning;
|
||||
} else {
|
||||
// There is currently no situation where an error status set during discovery/update is fixed by propagation.
|
||||
Q_ASSERT(_syncProblems.find(item._file) == _syncProblems.end());
|
||||
}
|
||||
|
||||
emit fileStatusChanged(getSystemDestination(item), fileStatus(item));
|
||||
}
|
||||
|
||||
SyncFileStatus SyncFileStatusTracker::fileStatus(const SyncFileItem& item)
|
||||
{
|
||||
// Hack to know if the item was taken from the sync engine (Sync), or from the database (UpToDate)
|
||||
bool waitingForPropagation = item._direction != SyncFileItem::None && item._status == SyncFileItem::NoStatus;
|
||||
|
||||
SyncFileStatus status(SyncFileStatus::StatusUpToDate);
|
||||
if (waitingForPropagation) {
|
||||
status.set(SyncFileStatus::StatusSync);
|
||||
} else if (showErrorInSocketApi(item)) {
|
||||
status.set(SyncFileStatus::StatusError);
|
||||
} else if (showWarningInSocketApi(item)) {
|
||||
status.set(SyncFileStatus::StatusWarning);
|
||||
} else {
|
||||
// After a sync finished, we need to show the users issues from that last sync like the activity list does.
|
||||
// Also used for parent directories showing a warning for an error child.
|
||||
SyncFileStatus::SyncFileStatusTag problemStatus = lookupProblem(item.destination(), _syncProblems);
|
||||
if (problemStatus != SyncFileStatus::StatusNone)
|
||||
status.set(problemStatus);
|
||||
}
|
||||
|
||||
if (item._remotePerm.contains("S"))
|
||||
status.setSharedWithMe(true);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void SyncFileStatusTracker::invalidateParentPaths(const QString& path)
|
||||
{
|
||||
QStringList splitPath = path.split('/', QString::SkipEmptyParts);
|
||||
for (int i = 0; i < splitPath.size(); ++i) {
|
||||
QString parentPath = splitPath.mid(0, i).join('/');
|
||||
emit fileStatusChanged(_syncEngine->localPath() + parentPath, fileStatus(parentPath));
|
||||
}
|
||||
}
|
||||
|
||||
QString SyncFileStatusTracker::getSystemDestination(const SyncFileItem& item)
|
||||
{
|
||||
QString systemFileName = _syncEngine->localPath() + item.destination();
|
||||
// the trailing slash for directories must be appended as the filenames coming in
|
||||
// from the plugins have that too. Otherwise the matching entry item is not found
|
||||
// in the plugin.
|
||||
if( item._type == SyncFileItem::Type::Directory ) {
|
||||
systemFileName += QLatin1Char('/');
|
||||
}
|
||||
return systemFileName;
|
||||
}
|
||||
|
||||
}
|
57
src/libsync/syncfilestatustracker.h
Normal file
57
src/libsync/syncfilestatustracker.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||
* Copyright (C) by Jocelyn Turcotte <jturcotte@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; 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 <map>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
class SyncEngine;
|
||||
|
||||
/**
|
||||
* @brief Takes care of tracking the status of individual files as they
|
||||
* go through the SyncEngine, to be reported as overlay icons in the shell.
|
||||
* @ingroup libsync
|
||||
*/
|
||||
class OWNCLOUDSYNC_EXPORT SyncFileStatusTracker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SyncFileStatusTracker(SyncEngine* syncEngine);
|
||||
SyncFileStatus fileStatus(const QString& systemFileName);
|
||||
|
||||
signals:
|
||||
void fileStatusChanged(const QString& systemFileName, SyncFileStatus fileStatus);
|
||||
|
||||
private slots:
|
||||
void slotAboutToPropagate(SyncFileItemVector& items);
|
||||
void slotItemCompleted(const SyncFileItem& item);
|
||||
|
||||
private:
|
||||
SyncFileStatus fileStatus(const SyncFileItem& item);
|
||||
void invalidateParentPaths(const QString& path);
|
||||
QString getSystemDestination(const SyncFileItem& syncEnginePath);
|
||||
|
||||
SyncEngine* _syncEngine;
|
||||
std::map<QString, SyncFileStatus::SyncFileStatusTag> _syncProblems;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -247,18 +247,6 @@ QString Utility::toCSyncScheme(const QString &urlStr)
|
|||
return url.toString();
|
||||
}
|
||||
|
||||
bool Utility::doesSetContainPrefix(const QSet<QString> &l, const QString &p) {
|
||||
|
||||
Q_FOREACH (const QString &setPath, l) {
|
||||
//qDebug() << Q_FUNC_INFO << p << setPath << setPath.startsWith(p);
|
||||
if (setPath.startsWith(p)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
//qDebug() << "-> NOOOOO!!!" << p << l.count();
|
||||
return false;
|
||||
}
|
||||
|
||||
QString Utility::escape(const QString &in)
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
||||
|
|
|
@ -40,11 +40,6 @@ namespace Utility
|
|||
OWNCLOUDSYNC_EXPORT void setLaunchOnStartup(const QString &appName, const QString& guiName, bool launch);
|
||||
OWNCLOUDSYNC_EXPORT qint64 freeDiskSpace(const QString &path);
|
||||
OWNCLOUDSYNC_EXPORT QString toCSyncScheme(const QString &urlStr);
|
||||
/** Like QLocale::toString(double, 'f', prec), but drops trailing zeros after the decimal point */
|
||||
|
||||
OWNCLOUDSYNC_EXPORT bool doesSetContainPrefix(const QSet<QString> &l, const QString &p);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief compactFormatDouble - formats a double value human readable.
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "utility.h"
|
||||
#include "folderman.h"
|
||||
#include "account.h"
|
||||
#include "accountstate.h"
|
||||
|
||||
using namespace OCC;
|
||||
|
@ -52,10 +53,11 @@ private slots:
|
|||
f.write("hello");
|
||||
}
|
||||
|
||||
AccountStatePtr newAccountState(new AccountState(Account::create()));
|
||||
FolderMan *folderman = FolderMan::instance();
|
||||
QCOMPARE(folderman, &_fm);
|
||||
QVERIFY(folderman->addFolder(0, folderDefinition(dir.path() + "/sub/ownCloud1")));
|
||||
QVERIFY(folderman->addFolder(0, folderDefinition(dir.path() + "/ownCloud2")));
|
||||
QVERIFY(folderman->addFolder(newAccountState.data(), folderDefinition(dir.path() + "/sub/ownCloud1")));
|
||||
QVERIFY(folderman->addFolder(newAccountState.data(), folderDefinition(dir.path() + "/ownCloud2")));
|
||||
|
||||
|
||||
// those should be allowed
|
||||
|
|
Loading…
Reference in a new issue