2012-02-15 12:30:37 +04:00
|
|
|
/*
|
|
|
|
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
|
|
|
|
* 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; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
|
|
* for more details.
|
|
|
|
*/
|
|
|
|
|
2014-07-11 02:31:24 +04:00
|
|
|
#include "syncengine.h"
|
|
|
|
#include "account.h"
|
2013-05-03 21:11:00 +04:00
|
|
|
#include "owncloudpropagator.h"
|
2013-10-03 22:00:58 +04:00
|
|
|
#include "syncjournaldb.h"
|
|
|
|
#include "syncjournalfilerecord.h"
|
2014-08-11 19:47:16 +04:00
|
|
|
#include "discoveryphase.h"
|
2013-07-30 13:19:22 +04:00
|
|
|
#include "creds/abstractcredentials.h"
|
2014-10-22 12:20:38 +04:00
|
|
|
#include "syncfilestatus.h"
|
2014-09-19 12:58:52 +04:00
|
|
|
#include "csync_private.h"
|
2015-10-29 16:10:11 +03:00
|
|
|
#include "filesystem.h"
|
2012-05-21 18:48:49 +04:00
|
|
|
|
2012-12-06 19:24:53 +04:00
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
#include <windows.h>
|
|
|
|
#else
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
|
2014-10-09 13:03:07 +04:00
|
|
|
#include <climits>
|
2013-04-04 19:14:38 +04:00
|
|
|
#include <assert.h>
|
|
|
|
|
2015-06-15 18:38:12 +03:00
|
|
|
#include <QCoreApplication>
|
2012-02-15 12:30:37 +04:00
|
|
|
#include <QDebug>
|
2013-01-14 15:13:29 +04:00
|
|
|
#include <QSslSocket>
|
2012-02-15 12:30:37 +04:00
|
|
|
#include <QDir>
|
|
|
|
#include <QMutexLocker>
|
|
|
|
#include <QThread>
|
|
|
|
#include <QStringList>
|
|
|
|
#include <QTextStream>
|
2012-02-28 19:49:13 +04:00
|
|
|
#include <QTime>
|
2012-12-11 15:49:48 +04:00
|
|
|
#include <QUrl>
|
2012-12-13 22:52:07 +04:00
|
|
|
#include <QSslCertificate>
|
2014-07-22 20:07:02 +04:00
|
|
|
#include <QProcess>
|
2014-08-15 17:00:10 +04:00
|
|
|
#include <QElapsedTimer>
|
2015-01-05 15:54:31 +03:00
|
|
|
#include <qtextcodec.h>
|
2012-08-26 13:47:45 +04:00
|
|
|
|
2015-03-12 12:26:44 +03:00
|
|
|
extern "C" const char *csync_instruction_str(enum csync_instructions_e instr);
|
2015-03-03 12:05:38 +03:00
|
|
|
|
2014-11-10 00:34:07 +03:00
|
|
|
namespace OCC {
|
2012-02-15 12:30:37 +04:00
|
|
|
|
2014-05-29 13:34:25 +04:00
|
|
|
bool SyncEngine::_syncRunning = false;
|
2012-03-21 21:03:49 +04:00
|
|
|
|
2015-11-20 17:14:22 +03:00
|
|
|
qint64 SyncEngine::minimumFileAgeForUpload = 2000;
|
|
|
|
|
2014-12-18 14:09:48 +03:00
|
|
|
SyncEngine::SyncEngine(AccountPtr account, CSYNC *ctx, const QString& localPath,
|
|
|
|
const QString& remoteURL, const QString& remotePath, OCC::SyncJournalDb* journal)
|
|
|
|
: _account(account)
|
|
|
|
, _csync_ctx(ctx)
|
2014-06-20 15:25:39 +04:00
|
|
|
, _needsUpdate(false)
|
|
|
|
, _localPath(localPath)
|
|
|
|
, _remoteUrl(remoteURL)
|
|
|
|
, _remotePath(remotePath)
|
|
|
|
, _journal(journal)
|
2015-01-30 15:36:20 +03:00
|
|
|
, _progressInfo(new ProgressInfo)
|
2014-07-15 13:22:16 +04:00
|
|
|
, _hasNoneFiles(false)
|
|
|
|
, _hasRemoveFile(false)
|
2014-06-20 15:25:39 +04:00
|
|
|
, _uploadLimit(0)
|
2014-06-23 14:48:34 +04:00
|
|
|
, _downloadLimit(0)
|
2015-07-27 10:54:20 +03:00
|
|
|
, _newBigFolderSizeLimit(-1)
|
2015-11-23 13:53:06 +03:00
|
|
|
, _checksum_hook(journal)
|
2014-09-10 19:25:13 +04:00
|
|
|
, _anotherSyncNeeded(false)
|
2012-02-15 12:30:37 +04:00
|
|
|
{
|
2013-05-16 15:54:22 +04:00
|
|
|
qRegisterMetaType<SyncFileItem>("SyncFileItem");
|
2013-10-28 13:47:10 +04:00
|
|
|
qRegisterMetaType<SyncFileItem::Status>("SyncFileItem::Status");
|
2014-02-05 23:18:03 +04:00
|
|
|
|
2015-06-06 11:49:24 +03:00
|
|
|
_thread.setObjectName("SyncEngine_Thread");
|
2014-02-05 23:18:03 +04:00
|
|
|
_thread.start();
|
2012-02-15 12:30:37 +04:00
|
|
|
}
|
|
|
|
|
2014-03-17 14:34:51 +04:00
|
|
|
SyncEngine::~SyncEngine()
|
2012-02-15 12:30:37 +04:00
|
|
|
{
|
2014-02-05 23:18:03 +04:00
|
|
|
_thread.quit();
|
|
|
|
_thread.wait();
|
2012-02-15 12:30:37 +04:00
|
|
|
}
|
|
|
|
|
2013-06-19 20:17:32 +04:00
|
|
|
//Convert an error code from csync to a user readable string.
|
|
|
|
// Keep that function thread safe as it can be called from the sync thread or the main thread
|
2014-03-17 14:34:51 +04:00
|
|
|
QString SyncEngine::csyncErrorToString(CSYNC_STATUS err)
|
2012-12-20 19:45:56 +04:00
|
|
|
{
|
|
|
|
QString errStr;
|
|
|
|
|
|
|
|
switch( err ) {
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_OK:
|
2012-12-20 19:45:56 +04:00
|
|
|
errStr = tr("Success.");
|
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_STATEDB_LOAD_ERROR:
|
2014-02-04 14:08:18 +04:00
|
|
|
errStr = tr("CSync failed to load or create the journal file. "
|
2015-09-07 09:51:22 +03:00
|
|
|
"Make sure you have read and write permissions in the local sync folder.");
|
2012-12-20 19:45:56 +04:00
|
|
|
break;
|
2015-01-15 13:35:16 +03:00
|
|
|
case CSYNC_STATUS_STATEDB_CORRUPTED:
|
|
|
|
errStr = tr("CSync failed to load the journal file. The journal file is corrupted.");
|
2013-08-19 18:15:20 +04:00
|
|
|
break;
|
|
|
|
case CSYNC_STATUS_NO_MODULE:
|
2015-06-15 18:38:12 +03:00
|
|
|
errStr = tr("<p>The %1 plugin for csync could not be loaded.<br/>Please verify the installation!</p>").arg(qApp->applicationName());
|
2012-12-20 19:45:56 +04:00
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_TREE_ERROR:
|
2012-12-20 19:45:56 +04:00
|
|
|
errStr = tr("CSync got an error while processing internal trees.");
|
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_MEMORY_ERROR:
|
2012-12-20 19:45:56 +04:00
|
|
|
errStr = tr("CSync failed to reserve memory.");
|
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_PARAM_ERROR:
|
2012-12-20 19:45:56 +04:00
|
|
|
errStr = tr("CSync fatal parameter error.");
|
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_UPDATE_ERROR:
|
2012-12-20 19:45:56 +04:00
|
|
|
errStr = tr("CSync processing step update failed.");
|
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_RECONCILE_ERROR:
|
2012-12-20 19:45:56 +04:00
|
|
|
errStr = tr("CSync processing step reconcile failed.");
|
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_PROXY_AUTH_ERROR:
|
|
|
|
errStr = tr("CSync could not authenticate at the proxy.");
|
|
|
|
break;
|
|
|
|
case CSYNC_STATUS_LOOKUP_ERROR:
|
2012-12-20 19:45:56 +04:00
|
|
|
errStr = tr("CSync failed to lookup proxy or server.");
|
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_SERVER_AUTH_ERROR:
|
2015-06-15 18:38:12 +03:00
|
|
|
errStr = tr("CSync failed to authenticate at the %1 server.").arg(qApp->applicationName());
|
2012-12-20 19:45:56 +04:00
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_CONNECT_ERROR:
|
2012-12-20 19:45:56 +04:00
|
|
|
errStr = tr("CSync failed to connect to the network.");
|
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_TIMEOUT:
|
2014-06-29 16:06:45 +04:00
|
|
|
errStr = tr("A network connection timeout happened.");
|
2012-12-20 19:45:56 +04:00
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_HTTP_ERROR:
|
2012-12-20 19:45:56 +04:00
|
|
|
errStr = tr("A HTTP transmission error happened.");
|
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_PERMISSION_DENIED:
|
2015-10-05 06:20:09 +03:00
|
|
|
errStr = tr("CSync failed due to unhandled permission denied.");
|
2012-12-20 19:45:56 +04:00
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_NOT_FOUND:
|
2015-02-06 00:00:13 +03:00
|
|
|
errStr = tr("CSync failed to access") + " "; // filename gets added.
|
2012-12-20 19:45:56 +04:00
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_FILE_EXISTS:
|
2015-09-07 09:51:22 +03:00
|
|
|
errStr = tr("CSync tried to create a folder that already exists.");
|
2012-12-20 19:45:56 +04:00
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_OUT_OF_SPACE:
|
2015-06-15 18:38:12 +03:00
|
|
|
errStr = tr("CSync: No space on %1 server available.").arg(qApp->applicationName());
|
2012-12-20 19:45:56 +04:00
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_UNSUCCESSFUL:
|
2012-12-20 19:45:56 +04:00
|
|
|
errStr = tr("CSync unspecified error.");
|
2013-10-02 21:41:17 +04:00
|
|
|
break;
|
|
|
|
case CSYNC_STATUS_ABORTED:
|
|
|
|
errStr = tr("Aborted by the user");
|
|
|
|
break;
|
2014-10-27 21:21:12 +03:00
|
|
|
case CSYNC_STATUS_SERVICE_UNAVAILABLE:
|
2015-02-25 10:57:39 +03:00
|
|
|
errStr = tr("The service is temporarily unavailable");
|
|
|
|
break;
|
|
|
|
case CSYNC_STATUS_STORAGE_UNAVAILABLE:
|
2015-09-07 09:51:22 +03:00
|
|
|
errStr = tr("The mounted folder is temporarily not available on the server");
|
2015-01-15 14:19:06 +03:00
|
|
|
break;
|
2015-10-12 18:06:49 +03:00
|
|
|
case CSYNC_STATUS_FORBIDDEN:
|
|
|
|
errStr = tr("Access is forbidden");
|
|
|
|
break;
|
2014-12-02 14:25:51 +03:00
|
|
|
case CSYNC_STATUS_OPENDIR_ERROR:
|
2015-09-07 09:51:22 +03:00
|
|
|
errStr = tr("An error occurred while opening a folder");
|
2014-12-02 14:25:51 +03:00
|
|
|
break;
|
2015-08-04 23:13:38 +03:00
|
|
|
case CSYNC_STATUS_READDIR_ERROR:
|
2015-09-07 09:51:22 +03:00
|
|
|
errStr = tr("Error while reading folder.");
|
2015-08-04 23:13:38 +03:00
|
|
|
break;
|
|
|
|
case CSYNC_STATUS_INVALID_CHARACTERS:
|
|
|
|
// Handled in callee
|
2012-12-20 19:45:56 +04:00
|
|
|
default:
|
2015-02-06 00:00:13 +03:00
|
|
|
errStr = tr("An internal error number %1 occurred.").arg( (int) err );
|
2012-12-20 19:45:56 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return errStr;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
bool SyncEngine::checkErrorBlacklisting( SyncFileItem &item )
|
2013-11-20 16:44:01 +04:00
|
|
|
{
|
|
|
|
if( !_journal ) {
|
|
|
|
qWarning() << "Journal is undefined!";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
SyncJournalErrorBlacklistRecord entry = _journal->errorBlacklistEntry(item._file);
|
|
|
|
item._hasBlacklistEntry = false;
|
2013-11-20 16:44:01 +04:00
|
|
|
|
2014-10-09 16:49:51 +04:00
|
|
|
if( !entry.isValid() ) {
|
|
|
|
return false;
|
|
|
|
}
|
2013-11-29 19:15:26 +04:00
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
item._hasBlacklistEntry = true;
|
2013-11-20 16:44:01 +04:00
|
|
|
|
2014-10-09 16:49:51 +04:00
|
|
|
// If duration has expired, it's not blacklisted anymore
|
|
|
|
time_t now = Utility::qDateTimeToTime_t(QDateTime::currentDateTime());
|
|
|
|
if( now > entry._lastTryTime + entry._ignoreDuration ) {
|
2015-04-15 16:19:11 +03:00
|
|
|
qDebug() << "blacklist entry for " << item._file << " has expired!";
|
2014-10-09 16:49:51 +04:00
|
|
|
return false;
|
|
|
|
}
|
2013-11-29 19:15:26 +04:00
|
|
|
|
2014-10-09 16:49:51 +04:00
|
|
|
// If the file has changed locally or on the server, the blacklist
|
|
|
|
// entry no longer applies
|
2015-04-15 16:19:11 +03:00
|
|
|
if( item._direction == SyncFileItem::Up ) { // check the modtime
|
|
|
|
if(item._modtime == 0 || entry._lastTryModtime == 0) {
|
2014-10-09 16:49:51 +04:00
|
|
|
return false;
|
2015-04-15 16:19:11 +03:00
|
|
|
} else if( item._modtime != entry._lastTryModtime ) {
|
|
|
|
qDebug() << item._file << " is blacklisted, but has changed mtime!";
|
2014-10-09 16:49:51 +04:00
|
|
|
return false;
|
|
|
|
}
|
2015-04-15 16:19:11 +03:00
|
|
|
} else if( item._direction == SyncFileItem::Down ) {
|
2014-10-09 16:49:51 +04:00
|
|
|
// download, check the etag.
|
2015-04-15 16:19:11 +03:00
|
|
|
if( item._etag.isEmpty() || entry._lastTryEtag.isEmpty() ) {
|
|
|
|
qDebug() << item._file << "one ETag is empty, no blacklisting";
|
2014-10-09 16:49:51 +04:00
|
|
|
return false;
|
2015-04-15 16:19:11 +03:00
|
|
|
} else if( item._etag != entry._lastTryEtag ) {
|
|
|
|
qDebug() << item._file << " is blacklisted, but has changed etag!";
|
2014-10-09 16:49:51 +04:00
|
|
|
return false;
|
2013-11-20 16:44:01 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-09 16:49:51 +04:00
|
|
|
qDebug() << "Item is on blacklist: " << entry._file
|
|
|
|
<< "retries:" << entry._retryCount
|
|
|
|
<< "for another" << (entry._lastTryTime + entry._ignoreDuration - now) << "s";
|
2015-04-15 16:19:11 +03:00
|
|
|
item._instruction = CSYNC_INSTRUCTION_ERROR;
|
|
|
|
item._status = SyncFileItem::FileIgnored;
|
|
|
|
item._errorString = tr("The item is not synced because of previous errors: %1").arg(entry._errorString);
|
2014-10-09 16:49:51 +04:00
|
|
|
|
|
|
|
return true;
|
2013-11-20 16:44:01 +04:00
|
|
|
}
|
|
|
|
|
2014-09-03 14:11:03 +04:00
|
|
|
void SyncEngine::deleteStaleDownloadInfos()
|
|
|
|
{
|
|
|
|
// Find all downloadinfo paths that we want to preserve.
|
|
|
|
QSet<QString> download_file_paths;
|
2015-04-15 16:19:11 +03:00
|
|
|
foreach(const SyncFileItemPtr &it, _syncedItems) {
|
|
|
|
if (it->_direction == SyncFileItem::Down
|
|
|
|
&& it->_type == SyncFileItem::File)
|
2014-09-03 14:11:03 +04:00
|
|
|
{
|
2015-04-15 16:19:11 +03:00
|
|
|
download_file_paths.insert(it->_file);
|
2014-09-03 14:11:03 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete from journal and from filesystem.
|
|
|
|
const QVector<SyncJournalDb::DownloadInfo> deleted_infos =
|
|
|
|
_journal->getAndDeleteStaleDownloadInfos(download_file_paths);
|
|
|
|
foreach (const SyncJournalDb::DownloadInfo & deleted_info, deleted_infos) {
|
|
|
|
const QString tmppath = _propagator->getFilePath(deleted_info._tmpfile);
|
|
|
|
qDebug() << "Deleting stale temporary file: " << tmppath;
|
2016-01-05 13:58:18 +03:00
|
|
|
FileSystem::remove(tmppath);
|
2014-09-03 14:11:03 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SyncEngine::deleteStaleUploadInfos()
|
|
|
|
{
|
|
|
|
// Find all blacklisted paths that we want to preserve.
|
|
|
|
QSet<QString> upload_file_paths;
|
2015-04-15 16:19:11 +03:00
|
|
|
foreach(const SyncFileItemPtr &it, _syncedItems) {
|
|
|
|
if (it->_direction == SyncFileItem::Up
|
|
|
|
&& it->_type == SyncFileItem::File)
|
2014-09-03 14:11:03 +04:00
|
|
|
{
|
2015-04-15 16:19:11 +03:00
|
|
|
upload_file_paths.insert(it->_file);
|
2014-09-03 14:11:03 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete from journal.
|
|
|
|
_journal->deleteStaleUploadInfos(upload_file_paths);
|
|
|
|
}
|
|
|
|
|
2015-01-16 12:17:19 +03:00
|
|
|
void SyncEngine::deleteStaleErrorBlacklistEntries()
|
2014-09-03 14:11:03 +04:00
|
|
|
{
|
|
|
|
// Find all blacklisted paths that we want to preserve.
|
|
|
|
QSet<QString> blacklist_file_paths;
|
2015-04-15 16:19:11 +03:00
|
|
|
foreach(const SyncFileItemPtr &it, _syncedItems) {
|
|
|
|
if (it->_hasBlacklistEntry)
|
|
|
|
blacklist_file_paths.insert(it->_file);
|
2014-09-03 14:11:03 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Delete from journal.
|
2015-01-16 12:17:19 +03:00
|
|
|
_journal->deleteStaleErrorBlacklistEntries(blacklist_file_paths);
|
2014-09-03 14:11:03 +04:00
|
|
|
}
|
|
|
|
|
2014-03-17 14:34:51 +04:00
|
|
|
int SyncEngine::treewalkLocal( TREE_WALK_FILE* file, void *data )
|
2013-01-15 23:41:52 +04:00
|
|
|
{
|
2014-03-17 14:34:51 +04:00
|
|
|
return static_cast<SyncEngine*>(data)->treewalkFile( file, false );
|
2013-01-15 23:41:52 +04:00
|
|
|
}
|
|
|
|
|
2014-03-17 14:34:51 +04:00
|
|
|
int SyncEngine::treewalkRemote( TREE_WALK_FILE* file, void *data )
|
2013-01-15 23:41:52 +04:00
|
|
|
{
|
2014-03-17 14:34:51 +04:00
|
|
|
return static_cast<SyncEngine*>(data)->treewalkFile( file, true );
|
2013-01-15 23:41:52 +04:00
|
|
|
}
|
|
|
|
|
2014-03-17 14:34:51 +04:00
|
|
|
int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
|
2013-01-15 23:41:52 +04:00
|
|
|
{
|
|
|
|
if( ! file ) return -1;
|
2014-08-07 12:14:14 +04:00
|
|
|
|
2015-01-05 15:54:31 +03:00
|
|
|
QTextCodec::ConverterState utf8State;
|
2015-10-21 17:01:44 +03:00
|
|
|
static QTextCodec *codec = QTextCodec::codecForName("UTF-8");
|
2015-01-05 15:54:31 +03:00
|
|
|
Q_ASSERT(codec);
|
|
|
|
QString fileUtf8 = codec->toUnicode(file->path, qstrlen(file->path), &utf8State);
|
2015-02-17 20:40:17 +03:00
|
|
|
QString renameTarget;
|
|
|
|
QString key = fileUtf8;
|
2015-01-05 15:54:31 +03:00
|
|
|
|
|
|
|
auto instruction = file->instruction;
|
2015-02-17 20:40:17 +03:00
|
|
|
if (utf8State.invalidChars > 0 || utf8State.remainingChars > 0) {
|
|
|
|
qWarning() << "File ignored because of invalid utf-8 sequence: " << file->path;
|
2015-01-05 15:54:31 +03:00
|
|
|
instruction = CSYNC_INSTRUCTION_IGNORE;
|
2015-02-17 20:40:17 +03:00
|
|
|
} else {
|
|
|
|
renameTarget = codec->toUnicode(file->rename_path, qstrlen(file->rename_path), &utf8State);
|
|
|
|
if (utf8State.invalidChars > 0 || utf8State.remainingChars > 0) {
|
|
|
|
qWarning() << "File ignored because of invalid utf-8 sequence in the rename_path: " << file->path << file->rename_path;
|
|
|
|
instruction = CSYNC_INSTRUCTION_IGNORE;
|
|
|
|
}
|
|
|
|
if (instruction == CSYNC_INSTRUCTION_RENAME) {
|
|
|
|
key = renameTarget;
|
|
|
|
}
|
2015-01-05 15:54:31 +03:00
|
|
|
}
|
2014-08-07 12:14:14 +04:00
|
|
|
|
2015-10-05 06:20:09 +03:00
|
|
|
// Gets a default-constructed SyncFileItemPtr or the one from the first walk (=local walk)
|
2015-04-15 16:19:11 +03:00
|
|
|
SyncFileItemPtr item = _syncItemMap.value(key);
|
|
|
|
if (!item)
|
|
|
|
item = SyncFileItemPtr(new SyncFileItem);
|
|
|
|
|
|
|
|
if (item->_file.isEmpty() || instruction == CSYNC_INSTRUCTION_RENAME) {
|
|
|
|
item->_file = fileUtf8;
|
2015-02-17 20:40:17 +03:00
|
|
|
}
|
2015-04-15 16:19:11 +03:00
|
|
|
item->_originalFile = item->_file;
|
2014-08-07 12:14:14 +04:00
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
if (item->_instruction == CSYNC_INSTRUCTION_NONE
|
|
|
|
|| (item->_instruction == CSYNC_INSTRUCTION_IGNORE && instruction != CSYNC_INSTRUCTION_NONE)) {
|
|
|
|
item->_instruction = instruction;
|
|
|
|
item->_modtime = file->modtime;
|
2014-08-07 12:14:14 +04:00
|
|
|
} else {
|
2015-01-05 15:54:31 +03:00
|
|
|
if (instruction != CSYNC_INSTRUCTION_NONE) {
|
2015-04-15 16:19:11 +03:00
|
|
|
qDebug() << "ERROR: Instruction" << item->_instruction << "vs" << instruction << "for" << fileUtf8;
|
2014-08-07 12:14:14 +04:00
|
|
|
Q_ASSERT(!"Instructions are both unequal NONE");
|
2014-10-27 17:52:17 +03:00
|
|
|
return -1;
|
2014-08-07 12:14:14 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-21 15:17:30 +03:00
|
|
|
if (file->file_id && file->file_id[0]) {
|
2015-04-15 16:19:11 +03:00
|
|
|
item->_fileId = file->file_id;
|
2014-08-07 12:14:14 +04:00
|
|
|
}
|
2014-06-03 13:50:13 +04:00
|
|
|
if (file->directDownloadUrl) {
|
2015-04-15 16:19:11 +03:00
|
|
|
item->_directDownloadUrl = QString::fromUtf8( file->directDownloadUrl );
|
2014-06-03 13:50:13 +04:00
|
|
|
}
|
|
|
|
if (file->directDownloadCookies) {
|
2015-04-15 16:19:11 +03:00
|
|
|
item->_directDownloadCookies = QString::fromUtf8( file->directDownloadCookies );
|
2014-06-03 13:50:13 +04:00
|
|
|
}
|
2014-06-23 17:05:48 +04:00
|
|
|
if (file->remotePerm && file->remotePerm[0]) {
|
2015-04-15 16:19:11 +03:00
|
|
|
item->_remotePerm = QByteArray(file->remotePerm);
|
2014-06-06 17:24:17 +04:00
|
|
|
}
|
2015-07-13 16:42:16 +03:00
|
|
|
|
2015-07-09 16:05:37 +03:00
|
|
|
item->_should_update_metadata = item->_should_update_metadata || file->should_update_metadata;
|
2013-09-04 18:33:06 +04:00
|
|
|
|
2015-07-13 16:42:16 +03:00
|
|
|
/* The flag "serverHasIgnoredFiles" is true if item in question is a directory
|
|
|
|
* that has children which are ignored in sync, either because the files are
|
2015-10-05 06:20:09 +03:00
|
|
|
* matched by an ignore pattern, or because they are hidden.
|
2015-07-13 16:42:16 +03:00
|
|
|
*
|
|
|
|
* Only the information about the server side ignored files is stored to the
|
|
|
|
* database and thus written to the item here. For the local repository its
|
|
|
|
* generated by the walk through the real file tree by discovery phase.
|
|
|
|
*
|
|
|
|
* It needs to go to the sync journal becasue the stat information about remote
|
|
|
|
* files are often read from database rather than being pulled from remote.
|
|
|
|
*/
|
|
|
|
if( remote ) {
|
|
|
|
item->_serverHasIgnoredFiles = (file->has_ignored_files > 0);
|
|
|
|
}
|
|
|
|
|
2015-11-23 15:44:49 +03:00
|
|
|
// Sometimes the discovery computes checksums for local files
|
|
|
|
if (!remote && file->checksum && file->checksumTypeId) {
|
|
|
|
item->_contentChecksum = QByteArray(file->checksum);
|
|
|
|
item->_contentChecksumType = _journal->getChecksumType(file->checksumTypeId);
|
|
|
|
}
|
|
|
|
|
2013-11-20 16:44:01 +04:00
|
|
|
// record the seen files to be able to clean the journal later
|
2015-04-15 16:19:11 +03:00
|
|
|
_seenFiles.insert(item->_file);
|
2015-02-19 17:00:37 +03:00
|
|
|
if (!renameTarget.isEmpty()) {
|
2015-10-05 06:20:09 +03:00
|
|
|
// Yes, this records both the rename renameTarget and the original so we keep both in case of a rename
|
2015-02-19 17:00:37 +03:00
|
|
|
_seenFiles.insert(renameTarget);
|
|
|
|
}
|
2013-11-11 14:11:45 +04:00
|
|
|
|
2014-06-23 17:05:48 +04:00
|
|
|
if (remote && file->remotePerm && file->remotePerm[0]) {
|
2015-04-15 16:19:11 +03:00
|
|
|
_remotePerms[item->_file] = file->remotePerm;
|
2014-06-23 15:35:34 +04:00
|
|
|
}
|
|
|
|
|
2013-12-04 15:19:38 +04:00
|
|
|
switch(file->error_status) {
|
|
|
|
case CSYNC_STATUS_OK:
|
|
|
|
break;
|
|
|
|
case CSYNC_STATUS_INDIVIDUAL_IS_SYMLINK:
|
2015-04-15 16:19:11 +03:00
|
|
|
item->_errorString = tr("Symbolic links are not supported in syncing.");
|
2013-12-04 15:19:38 +04:00
|
|
|
break;
|
|
|
|
case CSYNC_STATUS_INDIVIDUAL_IGNORE_LIST:
|
2015-04-15 16:19:11 +03:00
|
|
|
item->_errorString = tr("File is listed on the ignore list.");
|
2013-12-04 15:19:38 +04:00
|
|
|
break;
|
|
|
|
case CSYNC_STATUS_INDIVIDUAL_IS_INVALID_CHARS:
|
2015-07-15 11:38:33 +03:00
|
|
|
item->_errorString = tr("Filename contains invalid characters that can not be synced cross platform.");
|
2013-12-04 15:19:38 +04:00
|
|
|
break;
|
2015-02-27 13:25:03 +03:00
|
|
|
case CSYNC_STATUS_INDIVIDUAL_EXCLUDE_LONG_FILENAME:
|
2015-04-15 16:19:11 +03:00
|
|
|
item->_errorString = tr("Filename is too long.");
|
2015-02-27 13:25:03 +03:00
|
|
|
break;
|
2015-07-08 19:06:38 +03:00
|
|
|
case CSYNC_STATUS_INDIVIDUAL_EXCLUDE_HIDDEN:
|
|
|
|
item->_errorString = tr("File is ignored because it's hidden.");
|
|
|
|
break;
|
2014-04-28 19:25:18 +04:00
|
|
|
case CYSNC_STATUS_FILE_LOCKED_OR_OPEN:
|
2015-04-15 16:19:11 +03:00
|
|
|
item->_errorString = QLatin1String("File locked"); // don't translate, internal use!
|
2014-04-28 19:25:18 +04:00
|
|
|
break;
|
2015-07-17 17:48:33 +03:00
|
|
|
case CSYNC_STATUS_INDIVIDUAL_STAT_FAILED:
|
2015-08-11 12:34:40 +03:00
|
|
|
item->_errorString = tr("Stat failed.");
|
2015-07-17 17:48:33 +03:00
|
|
|
break;
|
2014-10-27 21:21:12 +03:00
|
|
|
case CSYNC_STATUS_SERVICE_UNAVAILABLE:
|
2015-04-15 16:19:11 +03:00
|
|
|
item->_errorString = QLatin1String("Server temporarily unavailable.");
|
2015-02-25 10:57:39 +03:00
|
|
|
break;
|
|
|
|
case CSYNC_STATUS_STORAGE_UNAVAILABLE:
|
2015-04-15 16:19:11 +03:00
|
|
|
item->_errorString = QLatin1String("Directory temporarily not available on server.");
|
|
|
|
item->_status = SyncFileItem::SoftError;
|
|
|
|
_temporarilyUnavailablePaths.insert(item->_file);
|
2014-10-27 21:21:12 +03:00
|
|
|
break;
|
2015-10-12 18:06:49 +03:00
|
|
|
case CSYNC_STATUS_FORBIDDEN:
|
|
|
|
item->_errorString = QLatin1String("Access forbidden.");
|
|
|
|
item->_status = SyncFileItem::SoftError;
|
|
|
|
_temporarilyUnavailablePaths.insert(item->_file);
|
|
|
|
break;
|
2015-09-09 15:12:13 +03:00
|
|
|
case CSYNC_STATUS_PERMISSION_DENIED:
|
|
|
|
item->_errorString = QLatin1String("Directory not accessible on client, permission denied.");
|
|
|
|
item->_status = SyncFileItem::SoftError;
|
|
|
|
break;
|
2013-12-04 15:19:38 +04:00
|
|
|
default:
|
|
|
|
Q_ASSERT("Non handled error-status");
|
|
|
|
/* No error string */
|
2013-09-02 19:25:07 +04:00
|
|
|
}
|
2014-12-06 14:27:50 +03:00
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
if (item->_instruction == CSYNC_INSTRUCTION_IGNORE && (utf8State.invalidChars > 0 || utf8State.remainingChars > 0)) {
|
|
|
|
item->_status = SyncFileItem::NormalError;
|
|
|
|
//item->_instruction = CSYNC_INSTRUCTION_ERROR;
|
|
|
|
item->_errorString = tr("Filename encoding is not valid");
|
2014-12-06 14:27:50 +03:00
|
|
|
}
|
2014-08-07 12:14:14 +04:00
|
|
|
|
2016-01-06 12:01:22 +03:00
|
|
|
bool isDirectory = file->type == CSYNC_FTW_TYPE_DIR;
|
2014-08-07 12:14:14 +04:00
|
|
|
|
2015-10-21 15:17:30 +03:00
|
|
|
if (file->etag && file->etag[0]) {
|
|
|
|
item->_etag = file->etag;
|
|
|
|
}
|
2015-04-15 16:19:11 +03:00
|
|
|
item->_size = file->size;
|
2014-05-06 14:55:54 +04:00
|
|
|
|
2016-01-20 15:44:30 +03:00
|
|
|
if (!item->_inode) {
|
2015-04-15 16:19:11 +03:00
|
|
|
item->_inode = file->inode;
|
2014-08-07 12:14:14 +04:00
|
|
|
}
|
|
|
|
|
2013-12-06 17:11:51 +04:00
|
|
|
switch( file->type ) {
|
|
|
|
case CSYNC_FTW_TYPE_DIR:
|
2015-04-15 16:19:11 +03:00
|
|
|
item->_type = SyncFileItem::Directory;
|
2013-12-06 17:11:51 +04:00
|
|
|
break;
|
|
|
|
case CSYNC_FTW_TYPE_FILE:
|
2015-04-15 16:19:11 +03:00
|
|
|
item->_type = SyncFileItem::File;
|
2013-12-06 17:11:51 +04:00
|
|
|
break;
|
|
|
|
case CSYNC_FTW_TYPE_SLINK:
|
2015-04-15 16:19:11 +03:00
|
|
|
item->_type = SyncFileItem::SoftLink;
|
2013-12-06 17:11:51 +04:00
|
|
|
break;
|
|
|
|
default:
|
2015-04-15 16:19:11 +03:00
|
|
|
item->_type = SyncFileItem::UnknownType;
|
2013-12-06 17:11:51 +04:00
|
|
|
}
|
2013-01-15 23:41:52 +04:00
|
|
|
|
2015-07-09 16:57:56 +03:00
|
|
|
SyncFileItem::Direction dir = SyncFileItem::None;
|
2013-01-15 23:41:52 +04:00
|
|
|
|
|
|
|
int re = 0;
|
|
|
|
switch(file->instruction) {
|
2015-11-23 13:53:06 +03:00
|
|
|
case CSYNC_INSTRUCTION_NONE: {
|
2016-01-06 12:01:22 +03:00
|
|
|
if (remote && item->_should_update_metadata && !isDirectory && item->_instruction == CSYNC_INSTRUCTION_NONE) {
|
2015-11-23 13:53:06 +03:00
|
|
|
// Update the database now already: New remote fileid or Etag or RemotePerm
|
2014-11-06 16:54:59 +03:00
|
|
|
// Or for files that were detected as "resolved conflict".
|
2015-11-23 13:53:06 +03:00
|
|
|
// Or a local inode/mtime change (see localMetadataUpdate below)
|
2015-10-29 16:10:11 +03:00
|
|
|
|
|
|
|
// In case of "resolved conflict": there should have been a conflict because they
|
|
|
|
// both were new, or both had their local mtime or remote etag modified, but the
|
|
|
|
// size and mtime is the same on the server. This typically happens when the
|
|
|
|
// database is removed. Nothing will be done for those files, but we still need
|
|
|
|
// to update the database.
|
|
|
|
|
|
|
|
// This metadata update *could* be a propagation job of its own, but since it's
|
|
|
|
// quick to do and we don't want to create a potentially large number of
|
|
|
|
// mini-jobs later on, we just update metadata right now.
|
|
|
|
|
|
|
|
QString filePath = _localPath + item->_file;
|
2014-11-06 16:54:59 +03:00
|
|
|
|
|
|
|
// Even if the mtime is different on the server, we always want to keep the mtime from
|
|
|
|
// the file system in the DB, this is to avoid spurious upload on the next sync
|
2015-04-15 16:19:11 +03:00
|
|
|
item->_modtime = file->other.modtime;
|
2015-10-08 10:54:43 +03:00
|
|
|
// same for the size
|
|
|
|
item->_size = file->other.size;
|
2014-11-06 16:54:59 +03:00
|
|
|
|
2015-10-29 16:10:11 +03:00
|
|
|
// If the 'W' remote permission changed, update the local filesystem
|
|
|
|
SyncJournalFileRecord prev = _journal->getFileRecord(item->_file);
|
|
|
|
if (prev._remotePerm.contains('W') != item->_remotePerm.contains('W')) {
|
|
|
|
const bool isReadOnly = !item->_remotePerm.contains('W');
|
2015-11-23 15:23:19 +03:00
|
|
|
FileSystem::setFileReadOnlyWeak(filePath, isReadOnly);
|
2015-10-29 16:10:11 +03:00
|
|
|
}
|
|
|
|
|
2015-11-10 17:05:00 +03:00
|
|
|
_journal->setFileRecordMetadata(SyncJournalFileRecord(*item, filePath));
|
2015-07-09 16:05:37 +03:00
|
|
|
item->_should_update_metadata = false;
|
2015-11-23 13:53:06 +03:00
|
|
|
|
|
|
|
// Technically we're done with this item. See localMetadataUpdate hack below.
|
|
|
|
_syncItemMap.remove(key);
|
|
|
|
}
|
|
|
|
// Any files that are instruction NONE?
|
2016-01-06 12:01:22 +03:00
|
|
|
if (!isDirectory && file->other.instruction == CSYNC_INSTRUCTION_NONE) {
|
2015-11-23 13:53:06 +03:00
|
|
|
_hasNoneFiles = true;
|
2014-04-18 20:27:27 +04:00
|
|
|
}
|
2015-11-23 13:53:06 +03:00
|
|
|
// We want to still update etags of directories, other NONE
|
|
|
|
// items can be ignored.
|
2016-01-06 12:01:22 +03:00
|
|
|
bool directoryEtagUpdate = isDirectory && file->should_update_metadata;
|
2015-11-23 13:53:06 +03:00
|
|
|
bool localMetadataUpdate = !remote && file->should_update_metadata;
|
|
|
|
if (!directoryEtagUpdate) {
|
|
|
|
if (localMetadataUpdate) {
|
|
|
|
// Hack, we want a local metadata update to happen, but only if the
|
|
|
|
// remote tree doesn't ask us to do some kind of propagation.
|
2016-01-06 12:01:22 +03:00
|
|
|
item->_isDirectory = isDirectory;
|
2015-11-23 13:53:06 +03:00
|
|
|
_syncItemMap.insert(key, item);
|
2015-02-17 18:41:44 +03:00
|
|
|
}
|
2016-05-20 17:58:44 +03:00
|
|
|
item->_isDirectory = isDirectory;
|
2016-03-14 20:09:16 +03:00
|
|
|
emit syncItemDiscovered(*item);
|
2013-10-03 22:00:58 +04:00
|
|
|
return re;
|
|
|
|
}
|
2013-01-15 23:41:52 +04:00
|
|
|
break;
|
2015-11-23 13:53:06 +03:00
|
|
|
}
|
2013-01-15 23:41:52 +04:00
|
|
|
case CSYNC_INSTRUCTION_RENAME:
|
|
|
|
dir = !remote ? SyncFileItem::Down : SyncFileItem::Up;
|
2015-04-15 16:19:11 +03:00
|
|
|
item->_renameTarget = renameTarget;
|
2016-01-06 12:01:22 +03:00
|
|
|
if (isDirectory)
|
2015-04-15 16:19:11 +03:00
|
|
|
_renamedFolders.insert(item->_file, item->_renameTarget);
|
2013-01-15 23:41:52 +04:00
|
|
|
break;
|
|
|
|
case CSYNC_INSTRUCTION_REMOVE:
|
2014-07-15 13:22:16 +04:00
|
|
|
_hasRemoveFile = true;
|
2013-01-15 23:41:52 +04:00
|
|
|
dir = !remote ? SyncFileItem::Down : SyncFileItem::Up;
|
2014-07-15 13:22:16 +04:00
|
|
|
break;
|
2013-01-15 23:41:52 +04:00
|
|
|
case CSYNC_INSTRUCTION_CONFLICT:
|
|
|
|
case CSYNC_INSTRUCTION_IGNORE:
|
|
|
|
case CSYNC_INSTRUCTION_ERROR:
|
|
|
|
dir = SyncFileItem::None;
|
|
|
|
break;
|
|
|
|
case CSYNC_INSTRUCTION_EVAL:
|
|
|
|
case CSYNC_INSTRUCTION_NEW:
|
2016-01-06 12:01:22 +03:00
|
|
|
case CSYNC_INSTRUCTION_TYPE_CHANGE:
|
2013-01-15 23:41:52 +04:00
|
|
|
case CSYNC_INSTRUCTION_SYNC:
|
|
|
|
case CSYNC_INSTRUCTION_STAT_ERROR:
|
|
|
|
default:
|
|
|
|
dir = remote ? SyncFileItem::Down : SyncFileItem::Up;
|
2014-07-15 13:22:16 +04:00
|
|
|
if (!remote && file->instruction == CSYNC_INSTRUCTION_SYNC) {
|
|
|
|
// An upload of an existing file means that the file was left unchanged on the server
|
2015-10-05 06:20:09 +03:00
|
|
|
// This counts as a NONE for detecting if all the files on the server were changed
|
2014-07-15 13:22:16 +04:00
|
|
|
_hasNoneFiles = true;
|
|
|
|
}
|
2013-01-15 23:41:52 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
item->_direction = dir;
|
2016-01-06 12:01:22 +03:00
|
|
|
item->_isDirectory = isDirectory;
|
2015-03-24 14:17:48 +03:00
|
|
|
if (instruction != CSYNC_INSTRUCTION_NONE) {
|
|
|
|
// check for blacklisting of this item.
|
|
|
|
// if the item is on blacklist, the instruction was set to ERROR
|
2015-04-15 16:19:11 +03:00
|
|
|
checkErrorBlacklisting( *item );
|
2015-03-24 14:17:48 +03:00
|
|
|
}
|
2013-11-29 19:15:26 +04:00
|
|
|
|
2015-01-30 15:36:20 +03:00
|
|
|
_progressInfo->adjustTotalsForFile(*item);
|
|
|
|
|
2014-03-14 21:29:23 +04:00
|
|
|
_needsUpdate = true;
|
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
item->log._etag = file->etag;
|
|
|
|
item->log._fileId = file->file_id;
|
|
|
|
item->log._instruction = file->instruction;
|
|
|
|
item->log._modtime = file->modtime;
|
|
|
|
item->log._size = file->size;
|
2014-04-03 18:56:36 +04:00
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
item->log._other_etag = file->other.etag;
|
|
|
|
item->log._other_fileId = file->other.file_id;
|
|
|
|
item->log._other_instruction = file->other.instruction;
|
|
|
|
item->log._other_modtime = file->other.modtime;
|
|
|
|
item->log._other_size = file->other.size;
|
2014-03-26 21:06:25 +04:00
|
|
|
|
2015-02-17 20:40:17 +03:00
|
|
|
_syncItemMap.insert(key, item);
|
2014-06-17 16:50:24 +04:00
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
emit syncItemDiscovered(*item);
|
2013-01-15 23:41:52 +04:00
|
|
|
return re;
|
|
|
|
}
|
|
|
|
|
2014-03-17 14:34:51 +04:00
|
|
|
void SyncEngine::handleSyncError(CSYNC *ctx, const char *state) {
|
2013-11-25 01:21:29 +04:00
|
|
|
CSYNC_STATUS err = csync_get_status( ctx );
|
2013-08-19 18:15:20 +04:00
|
|
|
const char *errMsg = csync_get_status_string( ctx );
|
2013-08-21 17:29:04 +04:00
|
|
|
QString errStr = csyncErrorToString(err);
|
2013-05-05 13:41:31 +04:00
|
|
|
if( errMsg ) {
|
2013-11-25 21:22:41 +04:00
|
|
|
if( !errStr.endsWith(" ")) {
|
|
|
|
errStr.append(" ");
|
|
|
|
}
|
2013-05-05 13:41:31 +04:00
|
|
|
errStr += QString::fromUtf8(errMsg);
|
|
|
|
}
|
2015-08-04 23:13:38 +03:00
|
|
|
// Special handling CSYNC_STATUS_INVALID_CHARACTERS
|
|
|
|
if (err == CSYNC_STATUS_INVALID_CHARACTERS) {
|
|
|
|
errStr = tr("Invalid characters, please rename \"%1\"").arg(errMsg);
|
|
|
|
}
|
2013-11-25 21:22:41 +04:00
|
|
|
|
|
|
|
// if there is csyncs url modifier in the error message, replace it.
|
|
|
|
if( errStr.contains("ownclouds://") ) errStr.replace("ownclouds://", "https://");
|
|
|
|
if( errStr.contains("owncloud://") ) errStr.replace("owncloud://", "http://");
|
|
|
|
|
2013-02-15 22:29:27 +04:00
|
|
|
qDebug() << " #### ERROR during "<< state << ": " << errStr;
|
2013-08-19 18:15:20 +04:00
|
|
|
|
2013-11-25 01:21:29 +04:00
|
|
|
if( CSYNC_STATUS_IS_EQUAL( err, CSYNC_STATUS_ABORTED) ) {
|
|
|
|
qDebug() << "Update phase was aborted by user!";
|
|
|
|
} else if( CSYNC_STATUS_IS_EQUAL( err, CSYNC_STATUS_SERVICE_UNAVAILABLE ) ||
|
2013-08-19 18:15:20 +04:00
|
|
|
CSYNC_STATUS_IS_EQUAL( err, CSYNC_STATUS_CONNECT_ERROR )) {
|
2013-02-15 22:29:27 +04:00
|
|
|
emit csyncUnavailable();
|
2013-08-19 18:15:20 +04:00
|
|
|
} else {
|
2013-02-15 22:29:27 +04:00
|
|
|
emit csyncError(errStr);
|
|
|
|
}
|
2015-10-29 18:43:30 +03:00
|
|
|
finalize(false);
|
2013-02-15 22:29:27 +04:00
|
|
|
}
|
2013-01-15 23:41:52 +04:00
|
|
|
|
2014-03-17 14:34:51 +04:00
|
|
|
void SyncEngine::startSync()
|
2012-02-15 12:30:37 +04:00
|
|
|
{
|
2014-07-28 14:12:52 +04:00
|
|
|
if (_journal->exists()) {
|
|
|
|
QVector< SyncJournalDb::PollInfo > pollInfos = _journal->getPollInfos();
|
|
|
|
if (!pollInfos.isEmpty()) {
|
|
|
|
qDebug() << "Finish Poll jobs before starting a sync";
|
2014-12-18 14:09:48 +03:00
|
|
|
CleanupPollsJob *job = new CleanupPollsJob(pollInfos, _account,
|
2014-07-28 14:12:52 +04:00
|
|
|
_journal, _localPath, this);
|
|
|
|
connect(job, SIGNAL(finished()), this, SLOT(startSync()));
|
2014-07-29 17:51:22 +04:00
|
|
|
connect(job, SIGNAL(aborted(QString)), this, SLOT(slotCleanPollsJobAborted(QString)));
|
2014-07-28 14:12:52 +04:00
|
|
|
job->start();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-29 13:34:25 +04:00
|
|
|
Q_ASSERT(!_syncRunning);
|
|
|
|
_syncRunning = true;
|
2013-04-15 17:55:54 +04:00
|
|
|
|
2014-06-20 13:36:25 +04:00
|
|
|
Q_ASSERT(_csync_ctx);
|
2013-04-04 19:14:38 +04:00
|
|
|
|
2014-08-28 13:47:40 +04:00
|
|
|
if (!QDir(_localPath).exists()) {
|
|
|
|
// No _tr, it should only occur in non-mirall
|
2015-09-07 09:51:22 +03:00
|
|
|
emit csyncError("Unable to find local sync folder.");
|
2015-10-29 18:43:30 +03:00
|
|
|
finalize(false);
|
2014-08-28 13:47:40 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-09-30 16:07:45 +03:00
|
|
|
// Check free size on disk first.
|
2015-10-01 12:39:09 +03:00
|
|
|
const qint64 minFree = criticalFreeSpaceLimit();
|
2015-09-30 16:07:45 +03:00
|
|
|
const qint64 freeBytes = Utility::freeDiskSpace(_localPath);
|
|
|
|
if (freeBytes >= 0) {
|
|
|
|
qDebug() << "There are" << freeBytes << "bytes available at" << _localPath
|
|
|
|
<< "and at least" << minFree << "are required";
|
|
|
|
if (freeBytes < minFree) {
|
2016-01-06 18:50:59 +03:00
|
|
|
emit csyncError(tr("Only %1 are available, need at least %2 to start",
|
|
|
|
"Placeholders are postfixed with file sizes using Utility::octetsToString()").arg(
|
2015-09-30 16:07:45 +03:00
|
|
|
Utility::octetsToString(freeBytes),
|
|
|
|
Utility::octetsToString(minFree)));
|
2015-10-29 18:43:30 +03:00
|
|
|
finalize(false);
|
2015-09-30 16:07:45 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
qDebug() << "Could not determine free space available at" << _localPath;
|
|
|
|
}
|
|
|
|
|
2013-05-07 19:47:29 +04:00
|
|
|
_syncedItems.clear();
|
2014-08-07 12:14:14 +04:00
|
|
|
_syncItemMap.clear();
|
2013-02-14 19:25:00 +04:00
|
|
|
_needsUpdate = false;
|
2014-01-20 19:29:26 +04:00
|
|
|
|
2014-02-06 17:52:56 +04:00
|
|
|
csync_resume(_csync_ctx);
|
2012-08-30 19:50:42 +04:00
|
|
|
|
2014-10-20 16:25:22 +04:00
|
|
|
int fileRecordCount = -1;
|
2013-10-04 16:44:57 +04:00
|
|
|
if (!_journal->exists()) {
|
2014-10-20 17:51:50 +04:00
|
|
|
qDebug() << "=====sync looks new (no DB exists)";
|
|
|
|
} else {
|
|
|
|
qDebug() << "=====sync with existing DB";
|
|
|
|
}
|
|
|
|
|
2014-11-20 15:46:44 +03:00
|
|
|
qDebug() << "=====Using Qt" << qVersion();
|
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
|
|
|
qDebug() << "=====Using SSL library version"
|
|
|
|
<< QSslSocket::sslLibraryVersionString().toUtf8().data();
|
|
|
|
#endif
|
2014-11-12 11:21:05 +03:00
|
|
|
|
2014-10-20 17:51:50 +04:00
|
|
|
fileRecordCount = _journal->getFileRecordCount(); // this creates the DB if it does not exist yet
|
|
|
|
|
|
|
|
if( fileRecordCount == -1 ) {
|
|
|
|
qDebug() << "No way to create a sync journal!";
|
|
|
|
emit csyncError(tr("Unable to initialize a sync journal."));
|
2015-10-29 18:43:30 +03:00
|
|
|
finalize(false);
|
2014-10-20 17:51:50 +04:00
|
|
|
return;
|
|
|
|
// database creation error!
|
|
|
|
}
|
2014-10-20 16:25:22 +04:00
|
|
|
|
2015-05-13 14:15:53 +03:00
|
|
|
_csync_ctx->read_remote_from_db = true;
|
2014-10-20 17:51:50 +04:00
|
|
|
|
2015-04-08 16:30:07 +03:00
|
|
|
// This tells csync to never read from the DB if it is empty
|
|
|
|
// thereby speeding up the initial discovery significantly.
|
|
|
|
_csync_ctx->db_is_empty = (fileRecordCount == 0);
|
|
|
|
|
2015-05-21 13:22:50 +03:00
|
|
|
auto selectiveSyncBlackList = _journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList);
|
|
|
|
bool usingSelectiveSync = (!selectiveSyncBlackList.isEmpty());
|
2014-10-20 17:51:50 +04:00
|
|
|
qDebug() << (usingSelectiveSync ? "====Using Selective Sync" : "====NOT Using Selective Sync");
|
2013-07-05 20:46:43 +04:00
|
|
|
|
2013-04-20 14:15:27 +04:00
|
|
|
csync_set_userdata(_csync_ctx, this);
|
2014-05-02 15:04:53 +04:00
|
|
|
|
2015-11-23 13:53:06 +03:00
|
|
|
// Set up checksumming hook
|
|
|
|
_csync_ctx->callbacks.checksum_hook = &CSyncChecksumHook::hook;
|
|
|
|
_csync_ctx->callbacks.checksum_userdata = &_checksum_hook;
|
|
|
|
|
2014-03-26 21:06:25 +04:00
|
|
|
_stopWatch.start();
|
2014-02-19 00:41:20 +04:00
|
|
|
|
2014-08-11 19:47:16 +04:00
|
|
|
qDebug() << "#### Discovery start #################################################### >>";
|
2013-09-24 17:56:03 +04:00
|
|
|
|
2014-12-02 14:25:51 +03:00
|
|
|
_discoveryMainThread = new DiscoveryMainThread(account());
|
|
|
|
_discoveryMainThread->setParent(this);
|
2015-10-30 15:21:34 +03:00
|
|
|
connect(this, SIGNAL(finished(bool)), _discoveryMainThread, SLOT(deleteLater()));
|
2015-10-16 12:52:27 +03:00
|
|
|
qDebug() << "=====Server" << account()->serverVersion()
|
|
|
|
<< QString("rootEtagChangesNotOnlySubFolderEtags=%1").arg(account()->rootEtagChangesNotOnlySubFolderEtags());
|
|
|
|
if (account()->rootEtagChangesNotOnlySubFolderEtags()) {
|
|
|
|
connect(_discoveryMainThread, SIGNAL(etag(QString)), this, SLOT(slotRootEtagReceived(QString)));
|
|
|
|
} else {
|
|
|
|
connect(_discoveryMainThread, SIGNAL(etagConcatenation(QString)), this, SLOT(slotRootEtagReceived(QString)));
|
|
|
|
}
|
2014-12-02 14:25:51 +03:00
|
|
|
|
|
|
|
DiscoveryJob *discoveryJob = new DiscoveryJob(_csync_ctx);
|
2015-05-21 13:22:50 +03:00
|
|
|
discoveryJob->_selectiveSyncBlackList = selectiveSyncBlackList;
|
2015-05-21 15:50:30 +03:00
|
|
|
discoveryJob->_selectiveSyncWhiteList =
|
|
|
|
_journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList);
|
2015-07-27 10:54:20 +03:00
|
|
|
discoveryJob->_newBigFolderSizeLimit = _newBigFolderSizeLimit;
|
2014-12-02 14:25:51 +03:00
|
|
|
discoveryJob->moveToThread(&_thread);
|
|
|
|
connect(discoveryJob, SIGNAL(finished(int)), this, SLOT(slotDiscoveryJobFinished(int)));
|
|
|
|
connect(discoveryJob, SIGNAL(folderDiscovered(bool,QString)),
|
2014-08-15 17:00:10 +04:00
|
|
|
this, SIGNAL(folderDiscovered(bool,QString)));
|
2014-12-02 14:25:51 +03:00
|
|
|
|
2015-07-27 10:54:20 +03:00
|
|
|
connect(discoveryJob, SIGNAL(newBigFolder(QString)),
|
|
|
|
this, SIGNAL(newBigFolder(QString)));
|
2015-05-21 15:50:30 +03:00
|
|
|
|
|
|
|
|
2014-12-02 14:25:51 +03:00
|
|
|
// This is used for the DiscoveryJob to be able to request the main thread/
|
|
|
|
// to read in directory contents.
|
|
|
|
qDebug() << Q_FUNC_INFO << _remotePath << _remoteUrl;
|
2015-01-20 20:49:49 +03:00
|
|
|
_discoveryMainThread->setupHooks( discoveryJob, _remotePath);
|
2014-12-02 14:25:51 +03:00
|
|
|
|
|
|
|
// Starts the update in a seperate thread
|
|
|
|
QMetaObject::invokeMethod(discoveryJob, "start", Qt::QueuedConnection);
|
2014-02-05 23:18:03 +04:00
|
|
|
}
|
|
|
|
|
2015-10-29 17:01:29 +03:00
|
|
|
void SyncEngine::slotRootEtagReceived(const QString &e) {
|
2015-01-23 17:30:00 +03:00
|
|
|
if (_remoteRootEtag.isEmpty()) {
|
2015-03-06 11:28:50 +03:00
|
|
|
qDebug() << Q_FUNC_INFO << e;
|
2015-01-23 17:30:00 +03:00
|
|
|
_remoteRootEtag = e;
|
|
|
|
emit rootEtag(_remoteRootEtag);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-11 19:47:16 +04:00
|
|
|
void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
|
2014-02-05 23:18:03 +04:00
|
|
|
{
|
2014-08-15 17:00:10 +04:00
|
|
|
// To clean the progress info
|
|
|
|
emit folderDiscovered(false, QString());
|
|
|
|
|
2014-08-11 19:47:16 +04:00
|
|
|
if (discoveryResult < 0 ) {
|
2013-04-20 14:15:27 +04:00
|
|
|
handleSyncError(_csync_ctx, "csync_update");
|
2013-02-15 22:29:27 +04:00
|
|
|
return;
|
2012-03-22 19:22:08 +04:00
|
|
|
}
|
2014-08-11 19:47:16 +04:00
|
|
|
qDebug() << "<<#### Discovery end #################################################### " << _stopWatch.addLapTime(QLatin1String("Discovery Finished"));
|
2012-02-15 12:30:37 +04:00
|
|
|
|
2014-10-20 18:50:55 +04:00
|
|
|
// Sanity check
|
|
|
|
if (!_journal->isConnected()) {
|
|
|
|
qDebug() << "Bailing out, DB failure";
|
|
|
|
emit csyncError(tr("Cannot open the sync journal"));
|
2015-10-29 18:43:30 +03:00
|
|
|
finalize(false);
|
2014-10-20 18:50:55 +04:00
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
// Commits a possibly existing (should not though) transaction and starts a new one for the propagate phase
|
2014-10-20 19:20:58 +04:00
|
|
|
_journal->commitIfNeededAndStartNewTransaction("Post discovery");
|
2014-10-20 18:50:55 +04:00
|
|
|
}
|
|
|
|
|
2013-04-20 14:15:27 +04:00
|
|
|
if( csync_reconcile(_csync_ctx) < 0 ) {
|
2013-07-29 16:28:19 +04:00
|
|
|
handleSyncError(_csync_ctx, "csync_reconcile");
|
2013-02-15 22:29:27 +04:00
|
|
|
return;
|
2012-03-22 19:22:08 +04:00
|
|
|
}
|
2014-06-07 13:49:46 +04:00
|
|
|
|
2015-04-02 16:18:19 +03:00
|
|
|
qDebug() << "<<#### Reconcile end #################################################### " << _stopWatch.addLapTime(QLatin1String("Reconcile Finished"));
|
2013-11-25 19:16:33 +04:00
|
|
|
|
2014-07-15 13:22:16 +04:00
|
|
|
_hasNoneFiles = false;
|
|
|
|
_hasRemoveFile = false;
|
2013-02-20 20:26:07 +04:00
|
|
|
bool walkOk = true;
|
2013-11-11 14:11:45 +04:00
|
|
|
_seenFiles.clear();
|
2015-04-08 11:50:08 +03:00
|
|
|
_temporarilyUnavailablePaths.clear();
|
2013-11-11 14:11:45 +04:00
|
|
|
|
2013-04-20 14:15:27 +04:00
|
|
|
if( csync_walk_local_tree(_csync_ctx, &treewalkLocal, 0) < 0 ) {
|
2013-02-15 22:29:27 +04:00
|
|
|
qDebug() << "Error in local treewalk.";
|
2013-02-20 20:26:07 +04:00
|
|
|
walkOk = false;
|
2013-01-15 23:41:52 +04:00
|
|
|
}
|
2013-04-20 14:15:27 +04:00
|
|
|
if( walkOk && csync_walk_remote_tree(_csync_ctx, &treewalkRemote, 0) < 0 ) {
|
2013-02-15 22:29:27 +04:00
|
|
|
qDebug() << "Error in remote treewalk.";
|
2013-01-15 23:41:52 +04:00
|
|
|
}
|
|
|
|
|
2014-09-19 12:58:52 +04:00
|
|
|
if (_csync_ctx->remote.root_perms) {
|
|
|
|
_remotePerms[QLatin1String("")] = _csync_ctx->remote.root_perms;
|
|
|
|
qDebug() << "Permissions of the root folder: " << _remotePerms[QLatin1String("")];
|
|
|
|
}
|
|
|
|
|
2015-03-02 17:08:21 +03:00
|
|
|
// Re-init the csync context to free memory
|
|
|
|
csync_commit(_csync_ctx);
|
|
|
|
|
2014-08-07 12:14:14 +04:00
|
|
|
// The map was used for merging trees, convert it to a list:
|
|
|
|
_syncedItems = _syncItemMap.values().toVector();
|
2015-03-02 17:08:21 +03:00
|
|
|
_syncItemMap.clear(); // free memory
|
2014-08-07 12:14:14 +04:00
|
|
|
|
2013-05-15 17:22:20 +04:00
|
|
|
// Adjust the paths for the renames.
|
|
|
|
for (SyncFileItemVector::iterator it = _syncedItems.begin();
|
|
|
|
it != _syncedItems.end(); ++it) {
|
2015-04-15 16:19:11 +03:00
|
|
|
(*it)->_file = adjustRenamedPath((*it)->_file);
|
2013-05-15 17:22:20 +04:00
|
|
|
}
|
|
|
|
|
2016-01-13 19:49:41 +03:00
|
|
|
// Check for invalid character in old server version
|
|
|
|
if (_account->serverVersionInt() < 0x080100) {
|
|
|
|
// Server version older than 8.1 don't support these character in filename.
|
|
|
|
static const QRegExp invalidCharRx("[\\\\:?*\"<>|]");
|
|
|
|
for (auto it = _syncedItems.begin(); it != _syncedItems.end(); ++it) {
|
|
|
|
if ((*it)->_direction == SyncFileItem::Up &&
|
|
|
|
(*it)->destination().contains(invalidCharRx)) {
|
|
|
|
(*it)->_errorString = tr("File name contains at least one invalid character");
|
|
|
|
(*it)->_instruction = CSYNC_INSTRUCTION_IGNORE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-27 15:34:15 +04:00
|
|
|
// Sort items per destination
|
|
|
|
std::sort(_syncedItems.begin(), _syncedItems.end());
|
|
|
|
|
2014-06-07 13:49:46 +04:00
|
|
|
// make sure everything is allowed
|
|
|
|
checkForPermission();
|
|
|
|
|
2014-03-14 21:43:23 +04:00
|
|
|
// To announce the beginning of the sync
|
2014-03-27 20:04:31 +04:00
|
|
|
emit aboutToPropagate(_syncedItems);
|
2015-01-30 15:36:20 +03:00
|
|
|
// it's important to do this before ProgressInfo::start(), to announce start of new sync
|
|
|
|
emit transmissionProgress(*_progressInfo);
|
|
|
|
_progressInfo->start();
|
2014-03-14 21:29:23 +04:00
|
|
|
|
2014-07-15 13:22:16 +04:00
|
|
|
if (!_hasNoneFiles && _hasRemoveFile) {
|
|
|
|
qDebug() << Q_FUNC_INFO << "All the files are going to be changed, asking the user";
|
2013-08-14 17:44:30 +04:00
|
|
|
bool cancel = false;
|
2015-04-15 16:19:11 +03:00
|
|
|
emit aboutToRemoveAllFiles(_syncedItems.first()->_direction, &cancel);
|
2013-06-08 17:40:45 +04:00
|
|
|
if (cancel) {
|
|
|
|
qDebug() << Q_FUNC_INFO << "Abort sync";
|
2015-10-29 18:43:30 +03:00
|
|
|
finalize(false);
|
2013-06-08 17:40:45 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-22 20:07:02 +04:00
|
|
|
// post update phase script: allow to tweak stuff by a custom script in debug mode.
|
|
|
|
if( !qgetenv("OWNCLOUD_POST_UPDATE_SCRIPT").isEmpty() ) {
|
2014-10-15 18:43:58 +04:00
|
|
|
#ifndef NDEBUG
|
2014-07-22 20:07:02 +04:00
|
|
|
QString script = qgetenv("OWNCLOUD_POST_UPDATE_SCRIPT");
|
|
|
|
|
|
|
|
qDebug() << "OOO => Post Update Script: " << script;
|
|
|
|
QProcess::execute(script.toUtf8());
|
2014-10-15 18:43:58 +04:00
|
|
|
#else
|
2015-02-06 12:20:10 +03:00
|
|
|
qWarning() << "**** Attention: POST_UPDATE_SCRIPT installed, but not executed because compiled with NDEBUG";
|
2014-07-22 20:07:02 +04:00
|
|
|
#endif
|
2014-10-15 18:43:58 +04:00
|
|
|
}
|
2014-08-14 13:28:34 +04:00
|
|
|
|
|
|
|
// do a database commit
|
|
|
|
_journal->commit("post treewalk");
|
|
|
|
|
2014-11-25 18:24:47 +03:00
|
|
|
_propagator = QSharedPointer<OwncloudPropagator>(
|
2015-10-20 17:58:32 +03:00
|
|
|
new OwncloudPropagator (_account, _localPath, _remoteUrl, _remotePath, _journal));
|
2015-08-11 14:45:02 +03:00
|
|
|
connect(_propagator.data(), SIGNAL(itemCompleted(const SyncFileItem &, const PropagatorJob &)),
|
|
|
|
this, SLOT(slotItemCompleted(const SyncFileItem &, const PropagatorJob &)));
|
2015-04-15 16:19:11 +03:00
|
|
|
connect(_propagator.data(), SIGNAL(progress(const SyncFileItem &,quint64)),
|
|
|
|
this, SLOT(slotProgress(const SyncFileItem &,quint64)));
|
2014-05-26 14:23:25 +04:00
|
|
|
connect(_propagator.data(), SIGNAL(finished()), this, SLOT(slotFinished()), Qt::QueuedConnection);
|
2013-05-16 15:54:22 +04:00
|
|
|
|
2014-06-07 13:49:41 +04:00
|
|
|
// apply the network limits to the propagator
|
|
|
|
setNetworkLimits(_uploadLimit, _downloadLimit);
|
2014-01-31 20:29:50 +04:00
|
|
|
|
2014-09-03 14:11:03 +04:00
|
|
|
deleteStaleDownloadInfos();
|
|
|
|
deleteStaleUploadInfos();
|
2015-01-16 12:17:19 +03:00
|
|
|
deleteStaleErrorBlacklistEntries();
|
2014-09-03 14:11:03 +04:00
|
|
|
_journal->commit("post stale entry removal");
|
|
|
|
|
2014-11-07 13:41:21 +03:00
|
|
|
// Emit the started signal only after the propagator has been set up.
|
|
|
|
if (_needsUpdate)
|
|
|
|
emit(started());
|
|
|
|
|
2014-01-31 20:29:50 +04:00
|
|
|
_propagator->start(_syncedItems);
|
2015-04-02 16:18:19 +03:00
|
|
|
|
|
|
|
qDebug() << "<<#### Post-Reconcile end #################################################### " << _stopWatch.addLapTime(QLatin1String("Post-Reconcile Finished"));
|
2014-01-31 20:29:50 +04:00
|
|
|
}
|
|
|
|
|
2014-07-29 17:51:22 +04:00
|
|
|
void SyncEngine::slotCleanPollsJobAborted(const QString &error)
|
|
|
|
{
|
|
|
|
csyncError(error);
|
2015-10-29 18:43:30 +03:00
|
|
|
finalize(false);
|
2014-07-29 17:51:22 +04:00
|
|
|
}
|
|
|
|
|
2014-06-07 13:49:41 +04:00
|
|
|
void SyncEngine::setNetworkLimits(int upload, int download)
|
2014-01-31 20:29:50 +04:00
|
|
|
{
|
2014-06-07 13:49:41 +04:00
|
|
|
_uploadLimit = upload;
|
|
|
|
_downloadLimit = download;
|
2014-01-31 20:29:50 +04:00
|
|
|
|
|
|
|
if( !_propagator ) return;
|
|
|
|
|
2014-06-07 13:49:41 +04:00
|
|
|
_propagator->_uploadLimit = upload;
|
|
|
|
_propagator->_downloadLimit = download;
|
2013-08-14 21:59:16 +04:00
|
|
|
|
2014-04-07 19:25:25 +04:00
|
|
|
int propDownloadLimit = _propagator->_downloadLimit
|
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
|
|
|
.load()
|
|
|
|
#endif
|
|
|
|
;
|
|
|
|
int propUploadLimit = _propagator->_uploadLimit
|
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
|
|
|
.load()
|
|
|
|
#endif
|
|
|
|
;
|
2014-04-07 18:48:14 +04:00
|
|
|
|
2014-04-15 18:15:33 +04:00
|
|
|
if( propDownloadLimit != 0 || propUploadLimit != 0 ) {
|
2014-04-07 18:48:14 +04:00
|
|
|
qDebug() << " N------N Network Limits (down/up) " << propDownloadLimit << propUploadLimit;
|
2014-04-07 17:10:44 +04:00
|
|
|
}
|
2013-05-16 15:54:22 +04:00
|
|
|
}
|
2013-05-07 19:47:29 +04:00
|
|
|
|
2015-08-11 14:45:02 +03:00
|
|
|
void SyncEngine::slotItemCompleted(const SyncFileItem &item, const PropagatorJob &job)
|
2013-05-16 15:54:22 +04:00
|
|
|
{
|
2015-03-12 12:26:44 +03:00
|
|
|
const char * instruction_str = csync_instruction_str(item._instruction);
|
|
|
|
qDebug() << Q_FUNC_INFO << item._file << instruction_str << item._status << item._errorString;
|
2013-10-04 17:55:10 +04:00
|
|
|
|
2015-01-30 15:36:20 +03:00
|
|
|
_progressInfo->setProgressComplete(item);
|
2014-03-14 16:03:16 +04:00
|
|
|
|
2013-10-28 13:47:10 +04:00
|
|
|
if (item._status == SyncFileItem::FatalError) {
|
2013-10-04 17:13:36 +04:00
|
|
|
emit csyncError(item._errorString);
|
|
|
|
}
|
2014-03-14 16:03:16 +04:00
|
|
|
|
2015-01-30 15:36:20 +03:00
|
|
|
emit transmissionProgress(*_progressInfo);
|
2015-08-11 14:45:02 +03:00
|
|
|
emit itemCompleted(item, job);
|
2013-05-16 15:54:22 +04:00
|
|
|
}
|
2013-05-06 18:59:11 +04:00
|
|
|
|
2014-03-17 14:34:51 +04:00
|
|
|
void SyncEngine::slotFinished()
|
2013-05-16 15:54:22 +04:00
|
|
|
{
|
2014-09-10 19:25:13 +04:00
|
|
|
_anotherSyncNeeded = _anotherSyncNeeded || _propagator->_anotherSyncNeeded;
|
|
|
|
|
2013-05-16 15:54:22 +04:00
|
|
|
// emit the treewalk results.
|
2015-04-08 11:50:08 +03:00
|
|
|
if( ! _journal->postSyncCleanup( _seenFiles, _temporarilyUnavailablePaths ) ) {
|
2013-11-11 14:11:45 +04:00
|
|
|
qDebug() << "Cleaning of synced ";
|
|
|
|
}
|
2014-06-17 18:29:38 +04:00
|
|
|
|
2013-11-21 14:13:58 +04:00
|
|
|
_journal->commit("All Finished.", false);
|
2013-10-03 22:00:58 +04:00
|
|
|
emit treeWalkResult(_syncedItems);
|
2015-10-29 18:43:30 +03:00
|
|
|
finalize(true); // FIXME: should it be true if there was errors?
|
2014-05-20 14:28:55 +04:00
|
|
|
}
|
2013-05-16 15:54:22 +04:00
|
|
|
|
2015-10-29 18:43:30 +03:00
|
|
|
void SyncEngine::finalize(bool success)
|
2014-05-20 14:28:55 +04:00
|
|
|
{
|
2014-06-18 17:04:55 +04:00
|
|
|
_thread.quit();
|
|
|
|
_thread.wait();
|
2015-03-02 17:08:21 +03:00
|
|
|
|
2013-05-16 15:54:22 +04:00
|
|
|
csync_commit(_csync_ctx);
|
|
|
|
|
2014-03-26 21:06:25 +04:00
|
|
|
qDebug() << "CSync run took " << _stopWatch.addLapTime(QLatin1String("Sync Finished"));
|
|
|
|
_stopWatch.stop();
|
|
|
|
|
2014-05-29 13:34:25 +04:00
|
|
|
_syncRunning = false;
|
2015-10-29 18:43:30 +03:00
|
|
|
emit finished(success);
|
2014-11-07 13:41:21 +03:00
|
|
|
|
|
|
|
// Delete the propagator only after emitting the signal.
|
2014-11-25 18:24:47 +03:00
|
|
|
_propagator.clear();
|
2012-02-28 19:49:13 +04:00
|
|
|
}
|
|
|
|
|
2014-03-17 14:34:51 +04:00
|
|
|
void SyncEngine::slotProgress(const SyncFileItem& item, quint64 current)
|
2014-01-07 18:42:21 +04:00
|
|
|
{
|
2015-01-30 15:36:20 +03:00
|
|
|
_progressInfo->setProgressItem(item, current);
|
|
|
|
emit transmissionProgress(*_progressInfo);
|
2014-01-07 18:42:21 +04:00
|
|
|
}
|
|
|
|
|
2013-11-26 15:22:28 +04:00
|
|
|
|
2013-05-15 17:22:20 +04:00
|
|
|
/* Given a path on the remote, give the path as it is when the rename is done */
|
2014-03-17 14:34:51 +04:00
|
|
|
QString SyncEngine::adjustRenamedPath(const QString& original)
|
2013-05-15 17:22:20 +04:00
|
|
|
{
|
|
|
|
int slashPos = original.size();
|
|
|
|
while ((slashPos = original.lastIndexOf('/' , slashPos - 1)) > 0) {
|
|
|
|
QHash< QString, QString >::const_iterator it = _renamedFolders.constFind(original.left(slashPos));
|
|
|
|
if (it != _renamedFolders.constEnd()) {
|
|
|
|
return *it + original.mid(slashPos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return original;
|
|
|
|
}
|
2013-10-02 21:41:17 +04:00
|
|
|
|
2014-10-09 18:43:56 +04:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* Make sure that we are allowed to do what we do by checking the permissions and the selective sync list
|
|
|
|
*
|
|
|
|
*/
|
2014-06-07 13:49:46 +04:00
|
|
|
void SyncEngine::checkForPermission()
|
|
|
|
{
|
2015-05-21 13:22:50 +03:00
|
|
|
auto selectiveSyncBlackList = _journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList);
|
|
|
|
std::sort(selectiveSyncBlackList.begin(), selectiveSyncBlackList.end());
|
|
|
|
|
2014-06-07 13:49:46 +04:00
|
|
|
for (SyncFileItemVector::iterator it = _syncedItems.begin(); it != _syncedItems.end(); ++it) {
|
2015-04-15 16:19:11 +03:00
|
|
|
if ((*it)->_direction != SyncFileItem::Up) {
|
2014-06-07 13:49:46 +04:00
|
|
|
// Currently we only check server-side permissions
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-10-09 18:43:56 +04:00
|
|
|
// Do not propagate anything in the server if it is in the selective sync blacklist
|
2015-04-15 16:19:11 +03:00
|
|
|
const QString path = (*it)->destination() + QLatin1Char('/');
|
2015-05-21 13:22:50 +03:00
|
|
|
if (std::binary_search(selectiveSyncBlackList.constBegin(), selectiveSyncBlackList.constEnd(),
|
2014-10-09 18:43:56 +04:00
|
|
|
path)) {
|
2015-04-15 16:19:11 +03:00
|
|
|
(*it)->_instruction = CSYNC_INSTRUCTION_IGNORE;
|
|
|
|
(*it)->_status = SyncFileItem::FileIgnored;
|
|
|
|
(*it)->_errorString = tr("Ignored because of the \"choose what to sync\" blacklist");
|
2014-10-09 18:43:56 +04:00
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
if ((*it)->_isDirectory) {
|
|
|
|
for (SyncFileItemVector::iterator it_next = it + 1; it_next != _syncedItems.end() && (*it_next)->_file.startsWith(path); ++it_next) {
|
2014-10-09 18:43:56 +04:00
|
|
|
it = it_next;
|
2015-04-15 16:19:11 +03:00
|
|
|
(*it)->_instruction = CSYNC_INSTRUCTION_IGNORE;
|
|
|
|
(*it)->_status = SyncFileItem::FileIgnored;
|
|
|
|
(*it)->_errorString = tr("Ignored because of the \"choose what to sync\" blacklist");
|
2014-10-09 18:43:56 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
switch((*it)->_instruction) {
|
2016-01-06 12:01:22 +03:00
|
|
|
case CSYNC_INSTRUCTION_TYPE_CHANGE:
|
2014-06-07 13:49:46 +04:00
|
|
|
case CSYNC_INSTRUCTION_NEW: {
|
2015-04-15 16:19:11 +03:00
|
|
|
int slashPos = (*it)->_file.lastIndexOf('/');
|
|
|
|
QString parentDir = slashPos <= 0 ? "" : (*it)->_file.mid(0, slashPos);
|
2014-06-07 13:49:46 +04:00
|
|
|
const QByteArray perms = getPermissions(parentDir);
|
|
|
|
if (perms.isNull()) {
|
|
|
|
// No permissions set
|
|
|
|
break;
|
2015-04-15 16:19:11 +03:00
|
|
|
} else if ((*it)->_isDirectory && !perms.contains("K")) {
|
|
|
|
qDebug() << "checkForPermission: ERROR" << (*it)->_file;
|
|
|
|
(*it)->_instruction = CSYNC_INSTRUCTION_ERROR;
|
|
|
|
(*it)->_status = SyncFileItem::NormalError;
|
2015-09-14 14:57:40 +03:00
|
|
|
(*it)->_errorString = tr("Not allowed because you don't have permission to add subfolders to that folder");
|
2014-06-07 13:49:46 +04:00
|
|
|
|
2016-04-04 11:41:12 +03:00
|
|
|
for (SyncFileItemVector::iterator it_next = it + 1; it_next != _syncedItems.end() && (*it_next)->destination().startsWith(path); ++it_next) {
|
2014-06-07 13:49:46 +04:00
|
|
|
it = it_next;
|
2016-04-04 11:41:12 +03:00
|
|
|
if ((*it)->_instruction == CSYNC_INSTRUCTION_RENAME) {
|
|
|
|
// The file was most likely moved in this directory.
|
|
|
|
// If the file was read only or could not be moved or removed, it should
|
|
|
|
// be restored. Do that in the next sync by not considering as a rename
|
|
|
|
// but delete and upload. It will then be restored if needed.
|
|
|
|
_journal->avoidRenamesOnNextSync((*it)->_file);
|
|
|
|
_anotherSyncNeeded = true;
|
|
|
|
qDebug() << "Moving of " << (*it)->_file << " canceled because no permission to add parent folder";
|
|
|
|
}
|
2015-04-15 16:19:11 +03:00
|
|
|
(*it)->_instruction = CSYNC_INSTRUCTION_ERROR;
|
|
|
|
(*it)->_status = SyncFileItem::NormalError;
|
2015-09-07 09:51:22 +03:00
|
|
|
(*it)->_errorString = tr("Not allowed because you don't have permission to add parent folder");
|
2014-06-07 13:49:46 +04:00
|
|
|
}
|
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
} else if (!(*it)->_isDirectory && !perms.contains("C")) {
|
|
|
|
qDebug() << "checkForPermission: ERROR" << (*it)->_file;
|
|
|
|
(*it)->_instruction = CSYNC_INSTRUCTION_ERROR;
|
|
|
|
(*it)->_status = SyncFileItem::NormalError;
|
2015-09-07 09:51:22 +03:00
|
|
|
(*it)->_errorString = tr("Not allowed because you don't have permission to add files in that folder");
|
2014-06-07 13:49:46 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CSYNC_INSTRUCTION_SYNC: {
|
2015-04-15 16:19:11 +03:00
|
|
|
const QByteArray perms = getPermissions((*it)->_file);
|
2014-06-07 13:49:46 +04:00
|
|
|
if (perms.isNull()) {
|
|
|
|
// No permissions set
|
|
|
|
break;
|
2015-04-15 16:19:11 +03:00
|
|
|
} if (!(*it)->_isDirectory && !perms.contains("W")) {
|
|
|
|
qDebug() << "checkForPermission: RESTORING" << (*it)->_file;
|
2015-07-09 16:05:37 +03:00
|
|
|
(*it)->_should_update_metadata = true;
|
2015-04-15 16:19:11 +03:00
|
|
|
(*it)->_instruction = CSYNC_INSTRUCTION_CONFLICT;
|
|
|
|
(*it)->_direction = SyncFileItem::Down;
|
|
|
|
(*it)->_isRestoration = true;
|
2014-06-18 18:15:14 +04:00
|
|
|
// take the things to write to the db from the "other" node (i.e: info from server)
|
2015-04-15 16:19:11 +03:00
|
|
|
(*it)->_modtime = (*it)->log._other_modtime;
|
|
|
|
(*it)->_size = (*it)->log._other_size;
|
|
|
|
(*it)->_fileId = (*it)->log._other_fileId;
|
|
|
|
(*it)->_etag = (*it)->log._other_etag;
|
|
|
|
(*it)->_errorString = tr("Not allowed to upload this file because it is read-only on the server, restoring");
|
2014-06-07 13:49:46 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CSYNC_INSTRUCTION_REMOVE: {
|
2015-04-15 16:19:11 +03:00
|
|
|
const QByteArray perms = getPermissions((*it)->_file);
|
2014-06-07 13:49:46 +04:00
|
|
|
if (perms.isNull()) {
|
|
|
|
// No permissions set
|
|
|
|
break;
|
2014-09-25 17:36:28 +04:00
|
|
|
}
|
|
|
|
if (!perms.contains("D")) {
|
2015-04-15 16:19:11 +03:00
|
|
|
qDebug() << "checkForPermission: RESTORING" << (*it)->_file;
|
2015-07-09 16:05:37 +03:00
|
|
|
(*it)->_should_update_metadata = true;
|
2015-04-15 16:19:11 +03:00
|
|
|
(*it)->_instruction = CSYNC_INSTRUCTION_NEW;
|
|
|
|
(*it)->_direction = SyncFileItem::Down;
|
|
|
|
(*it)->_isRestoration = true;
|
|
|
|
(*it)->_errorString = tr("Not allowed to remove, restoring");
|
|
|
|
|
|
|
|
if ((*it)->_isDirectory) {
|
2014-06-07 13:49:46 +04:00
|
|
|
// restore all sub items
|
|
|
|
for (SyncFileItemVector::iterator it_next = it + 1;
|
2015-04-15 16:19:11 +03:00
|
|
|
it_next != _syncedItems.end() && (*it_next)->_file.startsWith(path); ++it_next) {
|
2014-06-07 13:49:46 +04:00
|
|
|
it = it_next;
|
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
if ((*it)->_instruction != CSYNC_INSTRUCTION_REMOVE) {
|
2015-09-07 09:51:22 +03:00
|
|
|
qWarning() << "non-removed job within a removed folder"
|
2015-04-15 16:19:11 +03:00
|
|
|
<< (*it)->_file << (*it)->_instruction;
|
2014-06-07 13:49:46 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
qDebug() << "checkForPermission: RESTORING" << (*it)->_file;
|
2015-07-09 16:05:37 +03:00
|
|
|
(*it)->_should_update_metadata = true;
|
2014-06-07 13:49:46 +04:00
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
(*it)->_instruction = CSYNC_INSTRUCTION_NEW;
|
|
|
|
(*it)->_direction = SyncFileItem::Down;
|
|
|
|
(*it)->_isRestoration = true;
|
|
|
|
(*it)->_errorString = tr("Not allowed to remove, restoring");
|
2014-06-07 13:49:46 +04:00
|
|
|
}
|
|
|
|
}
|
2014-10-09 18:43:56 +04:00
|
|
|
} else if(perms.contains("S") && perms.contains("D")) {
|
2014-09-25 17:36:28 +04:00
|
|
|
// this is a top level shared dir which can be removed to unshare it,
|
|
|
|
// regardless if it is a read only share or not.
|
|
|
|
// To avoid that we try to restore files underneath this dir which have
|
|
|
|
// not delete permission we fast forward the iterator and leave the
|
|
|
|
// delete jobs intact. It is not physically tried to remove this files
|
|
|
|
// underneath, propagator sees that.
|
2015-04-15 16:19:11 +03:00
|
|
|
if( (*it)->_isDirectory ) {
|
2015-10-05 06:20:09 +03:00
|
|
|
// put a more descriptive message if a top level share dir really is removed.
|
2015-04-15 16:19:11 +03:00
|
|
|
if( it == _syncedItems.begin() || !(path.startsWith((*(it-1))->_file)) ) {
|
|
|
|
(*it)->_errorString = tr("Local files and share folder removed.");
|
2014-09-25 17:36:28 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
for (SyncFileItemVector::iterator it_next = it + 1;
|
2015-04-15 16:19:11 +03:00
|
|
|
it_next != _syncedItems.end() && (*it_next)->_file.startsWith(path); ++it_next) {
|
2014-09-25 17:36:28 +04:00
|
|
|
it = it_next;
|
|
|
|
}
|
|
|
|
}
|
2014-06-07 13:49:46 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case CSYNC_INSTRUCTION_RENAME: {
|
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
int slashPos = (*it)->_renameTarget.lastIndexOf('/');
|
|
|
|
const QString parentDir = slashPos <= 0 ? "" : (*it)->_renameTarget.mid(0, slashPos);
|
2014-06-07 13:49:46 +04:00
|
|
|
const QByteArray destPerms = getPermissions(parentDir);
|
2015-04-15 16:19:11 +03:00
|
|
|
const QByteArray filePerms = getPermissions((*it)->_file);
|
2014-06-07 13:49:46 +04:00
|
|
|
|
|
|
|
//true when it is just a rename in the same directory. (not a move)
|
2015-04-15 16:19:11 +03:00
|
|
|
bool isRename = (*it)->_file.startsWith(parentDir) && (*it)->_file.lastIndexOf('/') == slashPos;
|
2014-06-07 13:49:46 +04:00
|
|
|
|
|
|
|
|
|
|
|
// Check if we are allowed to move to the destination.
|
|
|
|
bool destinationOK = true;
|
|
|
|
if (isRename || destPerms.isNull()) {
|
|
|
|
// no need to check for the destination dir permission
|
|
|
|
destinationOK = true;
|
2015-04-15 16:19:11 +03:00
|
|
|
} else if ((*it)->_isDirectory && !destPerms.contains("K")) {
|
2014-06-07 13:49:46 +04:00
|
|
|
destinationOK = false;
|
2015-04-15 16:19:11 +03:00
|
|
|
} else if (!(*it)->_isDirectory && !destPerms.contains("C")) {
|
2014-06-07 13:49:46 +04:00
|
|
|
destinationOK = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if we are allowed to move from the source
|
|
|
|
bool sourceOK = true;
|
|
|
|
if (!filePerms.isNull()
|
|
|
|
&& ((isRename && !filePerms.contains("N"))
|
2014-08-07 16:27:27 +04:00
|
|
|
|| (!isRename && !filePerms.contains("V")))) {
|
2014-06-07 13:49:46 +04:00
|
|
|
|
|
|
|
// We are not allowed to move or rename this file
|
|
|
|
sourceOK = false;
|
|
|
|
|
|
|
|
if (filePerms.contains("D") && destinationOK) {
|
|
|
|
// but we are allowed to delete it
|
|
|
|
// TODO! simulate delete & upload
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-12 14:14:07 +03:00
|
|
|
#ifdef OWNCLOUD_RESTORE_RENAME /* We don't like the idea of renaming behind user's back, as the user may be working with the files */
|
|
|
|
if (!sourceOK && (!destinationOK || isRename)
|
|
|
|
// (not for directory because that's more complicated with the contents that needs to be adjusted)
|
|
|
|
&& !(*it)->_isDirectory) {
|
2014-06-07 13:49:46 +04:00
|
|
|
// Both the source and the destination won't allow move. Move back to the original
|
2015-04-15 16:19:11 +03:00
|
|
|
std::swap((*it)->_file, (*it)->_renameTarget);
|
|
|
|
(*it)->_direction = SyncFileItem::Down;
|
|
|
|
(*it)->_errorString = tr("Move not allowed, item restored");
|
|
|
|
(*it)->_isRestoration = true;
|
|
|
|
qDebug() << "checkForPermission: MOVING BACK" << (*it)->_file;
|
2016-05-12 14:14:07 +03:00
|
|
|
// in case something does wrong, we will not do it next time
|
|
|
|
_journal->avoidRenamesOnNextSync((*it)->_file);
|
2014-06-27 17:26:12 +04:00
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
if (!sourceOK || !destinationOK) {
|
2014-06-07 13:49:46 +04:00
|
|
|
// One of them is not possible, just throw an error
|
2015-04-15 16:19:11 +03:00
|
|
|
(*it)->_instruction = CSYNC_INSTRUCTION_ERROR;
|
|
|
|
(*it)->_status = SyncFileItem::NormalError;
|
2014-06-07 13:49:46 +04:00
|
|
|
const QString errorString = tr("Move not allowed because %1 is read-only").arg(
|
|
|
|
sourceOK ? tr("the destination") : tr("the source"));
|
2015-04-15 16:19:11 +03:00
|
|
|
(*it)->_errorString = errorString;
|
2014-06-07 13:49:46 +04:00
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
qDebug() << "checkForPermission: ERROR MOVING" << (*it)->_file << errorString;
|
2014-06-07 13:49:46 +04:00
|
|
|
|
2014-06-27 17:26:12 +04:00
|
|
|
// Avoid a rename on next sync:
|
|
|
|
// TODO: do the resolution now already so we don't need two sync
|
|
|
|
// At this point we would need to go back to the propagate phase on both remote to take
|
|
|
|
// the decision.
|
2015-04-15 16:19:11 +03:00
|
|
|
_journal->avoidRenamesOnNextSync((*it)->_file);
|
2014-09-10 19:25:13 +04:00
|
|
|
_anotherSyncNeeded = true;
|
2014-06-27 17:26:12 +04:00
|
|
|
|
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
if ((*it)->_isDirectory) {
|
2014-06-07 13:49:46 +04:00
|
|
|
for (SyncFileItemVector::iterator it_next = it + 1;
|
2015-04-15 16:19:11 +03:00
|
|
|
it_next != _syncedItems.end() && (*it_next)->destination().startsWith(path); ++it_next) {
|
2014-06-07 13:49:46 +04:00
|
|
|
it = it_next;
|
2015-04-15 16:19:11 +03:00
|
|
|
(*it)->_instruction = CSYNC_INSTRUCTION_ERROR;
|
|
|
|
(*it)->_status = SyncFileItem::NormalError;
|
|
|
|
(*it)->_errorString = errorString;
|
|
|
|
qDebug() << "checkForPermission: ERROR MOVING" << (*it)->_file;
|
2014-06-07 13:49:46 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-17 16:50:24 +04:00
|
|
|
QByteArray SyncEngine::getPermissions(const QString& file) const
|
2014-06-07 13:49:46 +04:00
|
|
|
{
|
|
|
|
static bool isTest = qgetenv("OWNCLOUD_TEST_PERMISSIONS").toInt();
|
|
|
|
if (isTest) {
|
|
|
|
QRegExp rx("_PERM_([^_]*)_[^/]*$");
|
|
|
|
if (rx.indexIn(file) != -1) {
|
|
|
|
return rx.cap(1).toLatin1();
|
|
|
|
}
|
|
|
|
}
|
2014-06-17 16:50:24 +04:00
|
|
|
return _remotePerms.value(file);
|
2014-06-07 13:49:46 +04:00
|
|
|
}
|
|
|
|
|
2014-10-13 19:23:42 +04:00
|
|
|
bool SyncEngine::estimateState(QString fn, csync_ftw_type_e t, SyncFileStatus* s)
|
|
|
|
{
|
2014-10-18 16:18:11 +04:00
|
|
|
Q_UNUSED(t);
|
2014-12-12 18:38:07 +03:00
|
|
|
QString pat(fn);
|
|
|
|
if( t == CSYNC_FTW_TYPE_DIR && ! fn.endsWith(QLatin1Char('/'))) {
|
|
|
|
pat.append(QLatin1Char('/'));
|
|
|
|
}
|
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
Q_FOREACH(const SyncFileItemPtr &item, _syncedItems) {
|
2016-01-05 17:03:23 +03:00
|
|
|
//qDebug() << Q_FUNC_INFO << fn << item->_status << item->_file << fn.startsWith(item->_file) << item->_file.startsWith(fn);
|
2014-12-12 18:38:07 +03:00
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
if (item->_file.startsWith(pat) ||
|
2016-01-05 17:03:23 +03:00
|
|
|
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)
|
2016-01-05 17:28:01 +03:00
|
|
|
s->set(SyncFileStatus::STATUS_UPDATED);
|
2016-01-05 17:03:23 +03:00
|
|
|
else
|
|
|
|
s->set(SyncFileStatus::STATUS_EVAL);
|
|
|
|
qDebug() << Q_FUNC_INFO << "Setting" << fn << "to" << s->toSocketAPIString();
|
2014-10-13 19:23:42 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2014-06-07 13:49:46 +04:00
|
|
|
|
2014-11-07 13:41:21 +03:00
|
|
|
qint64 SyncEngine::timeSinceFileTouched(const QString& fn) const
|
|
|
|
{
|
|
|
|
// This copy is essential for thread safety.
|
|
|
|
QSharedPointer<OwncloudPropagator> prop = _propagator;
|
|
|
|
if (prop) {
|
|
|
|
return prop->timeSinceFileTouched(fn);
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-12-18 17:39:20 +03:00
|
|
|
AccountPtr SyncEngine::account() const
|
|
|
|
{
|
|
|
|
return _account;
|
|
|
|
}
|
|
|
|
|
2014-03-17 14:34:51 +04:00
|
|
|
void SyncEngine::abort()
|
2013-10-02 21:41:17 +04:00
|
|
|
{
|
2015-03-05 18:50:33 +03:00
|
|
|
qDebug() << Q_FUNC_INFO << _discoveryMainThread;
|
2014-12-02 14:25:51 +03:00
|
|
|
// Aborts the discovery phase job
|
|
|
|
if (_discoveryMainThread) {
|
|
|
|
_discoveryMainThread->abort();
|
|
|
|
}
|
|
|
|
// Sets a flag for the update phase
|
2013-10-02 21:41:17 +04:00
|
|
|
csync_request_abort(_csync_ctx);
|
2014-12-02 14:25:51 +03:00
|
|
|
// For the propagator
|
|
|
|
if(_propagator) {
|
2014-02-06 17:52:56 +04:00
|
|
|
_propagator->abort();
|
2014-12-02 14:25:51 +03:00
|
|
|
}
|
2013-10-02 21:41:17 +04:00
|
|
|
}
|
|
|
|
|
2014-11-10 00:34:07 +03:00
|
|
|
} // namespace OCC
|