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.
|
|
|
|
*/
|
|
|
|
|
2012-05-21 18:48:49 +04:00
|
|
|
#include "mirall/csyncthread.h"
|
|
|
|
#include "mirall/mirallconfigfile.h"
|
2012-09-27 19:22:08 +04:00
|
|
|
#include "mirall/theme.h"
|
2012-12-05 19:45:28 +04:00
|
|
|
#include "mirall/logger.h"
|
2013-01-21 16:29:17 +04:00
|
|
|
#include "mirall/owncloudinfo.h"
|
2013-05-03 21:11:00 +04:00
|
|
|
#include "owncloudpropagator.h"
|
2013-05-06 18:59:11 +04:00
|
|
|
#include "progressdatabase.h"
|
2013-07-30 13:19:22 +04:00
|
|
|
#include "creds/abstractcredentials.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
|
|
|
|
|
2013-04-04 19:14:38 +04:00
|
|
|
#include <assert.h>
|
|
|
|
|
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-08-26 13:47:45 +04:00
|
|
|
#include <QApplication>
|
2012-12-11 15:49:48 +04:00
|
|
|
#include <QUrl>
|
2012-12-13 22:52:07 +04:00
|
|
|
#include <QSslCertificate>
|
2012-08-26 13:47:45 +04:00
|
|
|
|
2012-02-15 12:30:37 +04:00
|
|
|
namespace Mirall {
|
|
|
|
|
2013-09-24 17:56:03 +04:00
|
|
|
void csyncLogCatcher(int /*verbosity*/,
|
|
|
|
const char */*function*/,
|
|
|
|
const char *buffer,
|
|
|
|
void */*userdata*/)
|
|
|
|
{
|
|
|
|
Logger::instance()->csyncLog( QString::fromUtf8(buffer) );
|
|
|
|
}
|
2012-08-02 12:17:15 +04:00
|
|
|
|
2013-09-24 17:56:03 +04:00
|
|
|
/* static variables to hold the credentials */
|
2012-03-22 19:22:08 +04:00
|
|
|
QMutex CSyncThread::_mutex;
|
2013-04-15 17:55:54 +04:00
|
|
|
QMutex CSyncThread::_syncMutex;
|
2012-03-21 21:03:49 +04:00
|
|
|
|
2013-05-03 21:11:00 +04:00
|
|
|
CSyncThread::CSyncThread(CSYNC *csync, const QString &localPath, const QString &remotePath)
|
2012-02-15 12:30:37 +04:00
|
|
|
{
|
2012-03-22 19:22:08 +04:00
|
|
|
_mutex.lock();
|
2013-05-03 21:11:00 +04:00
|
|
|
_localPath = localPath;
|
|
|
|
_remotePath = remotePath;
|
2013-04-20 14:15:27 +04:00
|
|
|
_csync_ctx = csync;
|
2012-03-22 19:22:08 +04:00
|
|
|
_mutex.unlock();
|
2013-05-16 15:54:22 +04:00
|
|
|
qRegisterMetaType<SyncFileItem>("SyncFileItem");
|
2013-08-21 17:29:04 +04:00
|
|
|
qRegisterMetaType<CSYNC_STATUS>("CSYNC_STATUS");
|
2012-02-15 12:30:37 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
CSyncThread::~CSyncThread()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
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
|
2013-08-21 17:29:04 +04:00
|
|
|
QString CSyncThread::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_NO_LOCK:
|
2012-12-20 19:45:56 +04:00
|
|
|
errStr = tr("CSync failed to create a lock file.");
|
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_STATEDB_LOAD_ERROR:
|
2012-12-20 19:45:56 +04:00
|
|
|
errStr = tr("CSync failed to load the state db.");
|
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_STATEDB_WRITE_ERROR:
|
|
|
|
errStr = tr("CSync failed to write the state db.");
|
|
|
|
break;
|
|
|
|
case CSYNC_STATUS_NO_MODULE:
|
2013-02-21 15:21:42 +04:00
|
|
|
errStr = tr("<p>The %1 plugin for csync could not be loaded.<br/>Please verify the installation!</p>").arg(Theme::instance()->appNameGUI());
|
2012-12-20 19:45:56 +04:00
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_TIMESKEW:
|
2012-12-20 19:45:56 +04:00
|
|
|
errStr = tr("The system time on this client is different than the system time on the server. "
|
|
|
|
"Please use a time synchronization service (NTP) on the server and client machines "
|
|
|
|
"so that the times remain the same.");
|
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_FILESYSTEM_UNKNOWN:
|
2012-12-20 19:45:56 +04:00
|
|
|
errStr = tr("CSync could not detect the filesystem type.");
|
|
|
|
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_PROPAGATE_ERROR:
|
2012-12-20 19:45:56 +04:00
|
|
|
errStr = tr("CSync processing step propagate failed.");
|
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_REMOTE_ACCESS_ERROR:
|
2013-04-20 14:15:27 +04:00
|
|
|
errStr = tr("<p>The target directory does not exist.</p><p>Please check the sync setup.</p>");
|
2012-12-20 19:45:56 +04:00
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_REMOTE_CREATE_ERROR:
|
|
|
|
case CSYNC_STATUS_REMOTE_STAT_ERROR:
|
2012-12-20 19:45:56 +04:00
|
|
|
errStr = tr("A remote file can not be written. Please check the remote access.");
|
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_LOCAL_CREATE_ERROR:
|
|
|
|
case CSYNC_STATUS_LOCAL_STAT_ERROR:
|
2012-12-20 19:45:56 +04:00
|
|
|
errStr = tr("The local filesystem can not be written. Please check permissions.");
|
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_PROXY_ERROR:
|
2012-12-20 19:45:56 +04:00
|
|
|
errStr = tr("CSync failed to connect through a proxy.");
|
|
|
|
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:
|
2013-02-21 15:21:42 +04:00
|
|
|
errStr = tr("CSync failed to authenticate at the %1 server.").arg(Theme::instance()->appNameGUI());
|
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:
|
2012-12-20 19:45:56 +04:00
|
|
|
errStr = tr("A network connection timeout happend.");
|
|
|
|
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:
|
2012-12-20 19:45:56 +04:00
|
|
|
errStr = tr("CSync failed due to not handled permission deniend.");
|
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_NOT_FOUND:
|
2012-12-20 19:45:56 +04:00
|
|
|
errStr = tr("CSync failed to find a specific file.");
|
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_FILE_EXISTS:
|
2012-12-20 19:45:56 +04:00
|
|
|
errStr = tr("CSync tried to create a directory that already exists.");
|
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_OUT_OF_SPACE:
|
2013-02-21 15:21:42 +04:00
|
|
|
errStr = tr("CSync: No space on %1 server available.").arg(Theme::instance()->appNameGUI());
|
2012-12-20 19:45:56 +04:00
|
|
|
break;
|
2013-08-19 18:15:20 +04:00
|
|
|
case CSYNC_STATUS_QUOTA_EXCEEDED:
|
|
|
|
errStr = tr("CSync: No space on %1 server available.").arg(Theme::instance()->appNameGUI());
|
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;
|
2012-12-20 19:45:56 +04:00
|
|
|
|
|
|
|
default:
|
|
|
|
errStr = tr("An internal error number %1 happend.").arg( (int) err );
|
|
|
|
}
|
|
|
|
|
|
|
|
return errStr;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-01-15 23:41:52 +04:00
|
|
|
int CSyncThread::treewalkLocal( TREE_WALK_FILE* file, void *data )
|
|
|
|
{
|
|
|
|
return static_cast<CSyncThread*>(data)->treewalkFile( file, false );
|
|
|
|
}
|
|
|
|
|
|
|
|
int CSyncThread::treewalkRemote( TREE_WALK_FILE* file, void *data )
|
|
|
|
{
|
|
|
|
return static_cast<CSyncThread*>(data)->treewalkFile( file, true );
|
|
|
|
}
|
|
|
|
|
|
|
|
int CSyncThread::walkFinalize(TREE_WALK_FILE* file, void *data )
|
|
|
|
{
|
2013-05-04 17:32:11 +04:00
|
|
|
return static_cast<CSyncThread*>(data)->treewalkFinalize( file);
|
2013-01-15 23:41:52 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int CSyncThread::treewalkFile( TREE_WALK_FILE *file, bool remote )
|
|
|
|
{
|
|
|
|
if( ! file ) return -1;
|
|
|
|
SyncFileItem item;
|
|
|
|
item._file = QString::fromUtf8( file->path );
|
2013-05-15 17:22:20 +04:00
|
|
|
item._originalFile = file->path;
|
2013-01-15 23:41:52 +04:00
|
|
|
item._instruction = file->instruction;
|
|
|
|
item._dir = SyncFileItem::None;
|
2013-09-04 18:33:06 +04:00
|
|
|
|
2013-09-02 19:25:07 +04:00
|
|
|
if(file->error_string) {
|
|
|
|
item._errorString = QString::fromUtf8(file->error_string);
|
|
|
|
}
|
2013-09-04 18:33:06 +04:00
|
|
|
|
2013-05-03 21:11:00 +04:00
|
|
|
item._isDirectory = file->type == CSYNC_FTW_TYPE_DIR;
|
|
|
|
item._modtime = file->modtime;
|
2013-05-06 18:59:11 +04:00
|
|
|
item._etag = file->md5;
|
2013-09-11 10:49:09 +04:00
|
|
|
item._size = file->size;
|
2013-01-15 23:41:52 +04:00
|
|
|
|
|
|
|
SyncFileItem::Direction dir;
|
|
|
|
|
|
|
|
int re = 0;
|
|
|
|
|
2013-06-08 17:40:45 +04:00
|
|
|
if (file->instruction != CSYNC_INSTRUCTION_IGNORE
|
|
|
|
&& file->instruction != CSYNC_INSTRUCTION_REMOVE) {
|
|
|
|
_hasFiles = true;
|
|
|
|
}
|
|
|
|
|
2013-02-14 19:25:00 +04:00
|
|
|
switch(file->instruction) {
|
|
|
|
case CSYNC_INSTRUCTION_NONE:
|
|
|
|
case CSYNC_INSTRUCTION_IGNORE:
|
|
|
|
break;
|
2013-09-11 10:42:29 +04:00
|
|
|
case CSYNC_INSTRUCTION_NEW:
|
|
|
|
case CSYNC_INSTRUCTION_SYNC:
|
|
|
|
case CSYNC_INSTRUCTION_CONFLICT:
|
|
|
|
_progressInfo.overall_file_count++;
|
|
|
|
_progressInfo.overall_transmission_size += file->size;
|
|
|
|
//fall trough
|
2013-02-14 19:25:00 +04:00
|
|
|
default:
|
2013-09-11 10:42:29 +04:00
|
|
|
_needsUpdate = true;
|
2013-02-14 19:25:00 +04:00
|
|
|
}
|
2013-01-15 23:41:52 +04:00
|
|
|
switch(file->instruction) {
|
|
|
|
case CSYNC_INSTRUCTION_NONE:
|
|
|
|
// No need to do anything.
|
|
|
|
return re;
|
|
|
|
break;
|
|
|
|
case CSYNC_INSTRUCTION_RENAME:
|
|
|
|
dir = !remote ? SyncFileItem::Down : SyncFileItem::Up;
|
|
|
|
item._renameTarget = QString::fromUtf8( file->rename_path );
|
2013-05-15 17:22:20 +04:00
|
|
|
if (item._isDirectory)
|
|
|
|
_renamedFolders.insert(item._file, item._renameTarget);
|
2013-01-15 23:41:52 +04:00
|
|
|
break;
|
|
|
|
case CSYNC_INSTRUCTION_REMOVE:
|
|
|
|
dir = !remote ? SyncFileItem::Down : SyncFileItem::Up;
|
|
|
|
break;
|
|
|
|
case CSYNC_INSTRUCTION_CONFLICT:
|
|
|
|
case CSYNC_INSTRUCTION_IGNORE:
|
|
|
|
case CSYNC_INSTRUCTION_ERROR:
|
|
|
|
dir = SyncFileItem::None;
|
|
|
|
break;
|
|
|
|
case CSYNC_INSTRUCTION_EVAL:
|
|
|
|
case CSYNC_INSTRUCTION_NEW:
|
|
|
|
case CSYNC_INSTRUCTION_SYNC:
|
|
|
|
case CSYNC_INSTRUCTION_STAT_ERROR:
|
|
|
|
case CSYNC_INSTRUCTION_DELETED:
|
|
|
|
case CSYNC_INSTRUCTION_UPDATED:
|
|
|
|
default:
|
|
|
|
dir = remote ? SyncFileItem::Down : SyncFileItem::Up;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-08-07 14:15:28 +04:00
|
|
|
switch( file->type ) {
|
|
|
|
case CSYNC_FTW_TYPE_DIR:
|
|
|
|
item._type = SyncFileItem::Directory;
|
|
|
|
break;
|
|
|
|
case CSYNC_FTW_TYPE_FILE:
|
|
|
|
item._type = SyncFileItem::File;
|
|
|
|
break;
|
|
|
|
case CSYNC_FTW_TYPE_SLINK:
|
|
|
|
item._type = SyncFileItem::SoftLink;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
item._type = SyncFileItem::UnknownType;
|
|
|
|
}
|
|
|
|
|
2013-01-15 23:41:52 +04:00
|
|
|
item._dir = dir;
|
|
|
|
_syncedItems.append(item);
|
|
|
|
|
|
|
|
return re;
|
|
|
|
}
|
|
|
|
|
2013-05-04 17:32:11 +04:00
|
|
|
int CSyncThread::treewalkFinalize(TREE_WALK_FILE* file)
|
2013-01-15 23:41:52 +04:00
|
|
|
{
|
2013-05-04 17:32:11 +04:00
|
|
|
if (file->instruction == CSYNC_INSTRUCTION_IGNORE)
|
2013-01-15 23:41:52 +04:00
|
|
|
return 0;
|
|
|
|
|
2013-05-04 17:32:11 +04:00
|
|
|
// Update the instruction and etag in the csync rb_tree so it is saved on the database
|
2013-05-15 17:22:20 +04:00
|
|
|
QHash<QByteArray, Action>::const_iterator action = _performedActions.constFind(file->path);
|
|
|
|
if (action != _performedActions.constEnd()) {
|
2013-05-04 17:32:11 +04:00
|
|
|
if (file->instruction != CSYNC_INSTRUCTION_NONE) {
|
|
|
|
// it is NONE if we are in the wrong tree (remote vs. local)
|
2013-01-15 23:41:52 +04:00
|
|
|
|
2013-08-14 21:59:16 +04:00
|
|
|
qDebug() << "UPDATING " << file->path << action->instruction;
|
2013-01-15 23:41:52 +04:00
|
|
|
|
2013-05-04 17:32:11 +04:00
|
|
|
file->instruction = action->instruction;
|
|
|
|
}
|
2013-02-15 22:29:27 +04:00
|
|
|
|
2013-05-03 21:11:00 +04:00
|
|
|
if (!action->etag.isNull()) {
|
2013-05-04 17:32:11 +04:00
|
|
|
// Update the etag even for INSTRUCTION_NONE (eg. renames)
|
2013-05-03 21:11:00 +04:00
|
|
|
file->md5 = action->etag.constData();
|
|
|
|
}
|
2013-02-15 22:29:27 +04:00
|
|
|
}
|
2013-01-15 23:41:52 +04:00
|
|
|
return 0;
|
|
|
|
}
|
2013-02-15 22:29:27 +04:00
|
|
|
|
|
|
|
void CSyncThread::handleSyncError(CSYNC *ctx, const char *state) {
|
2013-08-21 17:29:04 +04:00
|
|
|
CSYNC_STATUS err = CSYNC_STATUS(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 ) {
|
|
|
|
errStr += QLatin1String("<br/>");
|
|
|
|
errStr += QString::fromUtf8(errMsg);
|
|
|
|
}
|
2013-02-15 22:29:27 +04:00
|
|
|
qDebug() << " #### ERROR during "<< state << ": " << errStr;
|
2013-08-19 18:15:20 +04:00
|
|
|
|
|
|
|
if( CSYNC_STATUS_IS_EQUAL( err, CSYNC_STATUS_SERVICE_UNAVAILABLE ) ||
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2013-01-15 23:41:52 +04:00
|
|
|
|
2012-08-26 13:47:45 +04:00
|
|
|
void CSyncThread::startSync()
|
2012-02-15 12:30:37 +04:00
|
|
|
{
|
2013-04-15 17:55:54 +04:00
|
|
|
if (!_syncMutex.tryLock()) {
|
|
|
|
qDebug() << Q_FUNC_INFO << "WARNING: Another sync seems to be running. Not starting a new one.";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-04-20 14:15:27 +04:00
|
|
|
if( ! _csync_ctx ) {
|
|
|
|
qDebug() << "XXXXXXXXXXXXXXXX FAIL: do not have csync_ctx!";
|
|
|
|
}
|
2013-04-04 19:14:38 +04:00
|
|
|
qDebug() << Q_FUNC_INFO << "Sync started";
|
|
|
|
|
2012-08-26 13:47:45 +04:00
|
|
|
qDebug() << "starting to sync " << qApp->thread() << QThread::currentThread();
|
2013-05-07 19:47:29 +04:00
|
|
|
_syncedItems.clear();
|
2012-03-22 19:22:08 +04:00
|
|
|
|
2012-03-21 21:03:49 +04:00
|
|
|
_mutex.lock();
|
2013-02-14 19:25:00 +04:00
|
|
|
_needsUpdate = false;
|
2013-02-14 20:34:52 +04:00
|
|
|
_mutex.unlock();
|
2012-08-30 19:50:42 +04:00
|
|
|
|
2012-12-11 15:49:48 +04:00
|
|
|
|
2013-07-24 21:53:05 +04:00
|
|
|
// maybe move this somewhere else where it can influence a running sync?
|
|
|
|
MirallConfigFile cfg;
|
|
|
|
|
2013-07-05 20:46:43 +04:00
|
|
|
|
2013-06-12 14:13:09 +04:00
|
|
|
csync_set_module_property(_csync_ctx, "csync_context", _csync_ctx);
|
2013-04-20 14:15:27 +04:00
|
|
|
csync_set_userdata(_csync_ctx, this);
|
2013-07-29 16:28:19 +04:00
|
|
|
// TODO: This should be a part of this method, but we don't have
|
|
|
|
// any way to get "session_key" module property from csync. Had we
|
|
|
|
// have it, then we could keep this code and remove it from
|
|
|
|
// AbstractCredentials implementations.
|
2013-07-29 16:44:54 +04:00
|
|
|
cfg.getCredentials()->syncContextPreStart(_csync_ctx);
|
2013-07-29 16:28:19 +04:00
|
|
|
// if (_lastAuthCookies.length() > 0) {
|
2013-07-29 16:44:54 +04:00
|
|
|
// // Stuff cookies inside csync, then we can avoid the intermediate HTTP 401 reply
|
2013-07-29 16:28:19 +04:00
|
|
|
// // when https://github.com/owncloud/core/pull/4042 is merged.
|
|
|
|
// QString cookiesAsString;
|
|
|
|
// foreach(QNetworkCookie c, _lastAuthCookies) {
|
|
|
|
// cookiesAsString += c.name();
|
|
|
|
// cookiesAsString += '=';
|
|
|
|
// cookiesAsString += c.value();
|
|
|
|
// cookiesAsString += "; ";
|
|
|
|
// }
|
|
|
|
// csync_set_module_property(_csync_ctx, "session_key", cookiesAsString.to
|
|
|
|
// }
|
2013-07-17 18:27:18 +04:00
|
|
|
|
2013-04-20 14:15:27 +04:00
|
|
|
// csync_set_auth_callback( _csync_ctx, getauth );
|
2013-09-24 17:56:03 +04:00
|
|
|
csync_set_log_callback( csyncLogCatcher );
|
|
|
|
csync_set_log_level( 11 );
|
2013-07-29 16:28:19 +04:00
|
|
|
|
2013-05-16 15:54:22 +04:00
|
|
|
_syncTime.start();
|
|
|
|
|
|
|
|
QElapsedTimer updateTime;
|
|
|
|
updateTime.start();
|
2012-08-09 16:13:20 +04:00
|
|
|
qDebug() << "#### Update start #################################################### >>";
|
2013-09-24 17:56:03 +04:00
|
|
|
|
2013-04-20 14:15:27 +04:00
|
|
|
if( csync_update(_csync_ctx) < 0 ) {
|
|
|
|
handleSyncError(_csync_ctx, "csync_update");
|
2013-02-15 22:29:27 +04:00
|
|
|
return;
|
2012-03-22 19:22:08 +04:00
|
|
|
}
|
2013-05-16 15:54:22 +04:00
|
|
|
qDebug() << "<<#### Update end #################################################### " << updateTime.elapsed();
|
2012-02-15 12:30:37 +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
|
|
|
}
|
2013-01-15 23:41:52 +04:00
|
|
|
|
2013-08-14 21:59:16 +04:00
|
|
|
_progressInfo = Progress::Info();
|
|
|
|
|
2013-06-08 17:40:45 +04:00
|
|
|
_hasFiles = false;
|
2013-02-20 20:26:07 +04:00
|
|
|
bool walkOk = true;
|
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
|
|
|
}
|
|
|
|
|
2013-05-15 17:22:20 +04:00
|
|
|
// Adjust the paths for the renames.
|
|
|
|
for (SyncFileItemVector::iterator it = _syncedItems.begin();
|
|
|
|
it != _syncedItems.end(); ++it) {
|
|
|
|
it->_file = adjustRenamedPath(it->_file);
|
|
|
|
}
|
|
|
|
|
2013-05-03 21:11:00 +04:00
|
|
|
qSort(_syncedItems);
|
|
|
|
|
2013-06-08 17:40:45 +04:00
|
|
|
if (!_hasFiles && !_syncedItems.isEmpty()) {
|
|
|
|
qDebug() << Q_FUNC_INFO << "All the files are going to be removed, asking the user";
|
2013-08-14 17:44:30 +04:00
|
|
|
bool cancel = false;
|
2013-06-10 17:57:23 +04:00
|
|
|
emit aboutToRemoveAllFiles(_syncedItems.first()._dir, &cancel);
|
2013-06-08 17:40:45 +04:00
|
|
|
if (cancel) {
|
|
|
|
qDebug() << Q_FUNC_INFO << "Abort sync";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-14 19:25:00 +04:00
|
|
|
if (_needsUpdate)
|
|
|
|
emit(started());
|
|
|
|
|
2013-05-03 21:11:00 +04:00
|
|
|
ne_session_s *session = 0;
|
|
|
|
// that call to set property actually is a get which will return the session
|
|
|
|
csync_set_module_property(_csync_ctx, "get_dav_session", &session);
|
|
|
|
Q_ASSERT(session);
|
|
|
|
|
2013-05-16 15:54:22 +04:00
|
|
|
_progressDataBase.load(_localPath);
|
2013-10-02 21:41:17 +04:00
|
|
|
_propagator.reset(new OwncloudPropagator (session, _localPath, _remotePath,
|
|
|
|
&_progressDataBase, &_abortRequested));
|
2013-08-21 17:29:04 +04:00
|
|
|
connect(_propagator.data(), SIGNAL(completed(SyncFileItem, CSYNC_STATUS)),
|
|
|
|
this, SLOT(transferCompleted(SyncFileItem, CSYNC_STATUS)), Qt::QueuedConnection);
|
2013-08-14 21:59:16 +04:00
|
|
|
connect(_propagator.data(), SIGNAL(progress(Progress::Kind,QString,quint64,quint64)),
|
|
|
|
this, SLOT(slotProgress(Progress::Kind,QString,quint64,quint64)));
|
2013-05-16 15:54:22 +04:00
|
|
|
_iterator = 0;
|
|
|
|
|
2013-08-14 21:59:16 +04:00
|
|
|
int downloadLimit = 0;
|
|
|
|
if (cfg.useDownloadLimit()) {
|
|
|
|
downloadLimit = cfg.downloadLimit() * 1000;
|
|
|
|
}
|
|
|
|
_propagator->_downloadLimit = downloadLimit;
|
|
|
|
|
|
|
|
int uploadLimit = -75; // 75%
|
|
|
|
int useUpLimit = cfg.useUploadLimit();
|
|
|
|
if ( useUpLimit >= 1) {
|
|
|
|
uploadLimit = cfg.uploadLimit() * 1000;
|
|
|
|
} else if (useUpLimit == 0) {
|
|
|
|
uploadLimit = 0;
|
2012-02-28 19:49:13 +04:00
|
|
|
}
|
2013-08-14 21:59:16 +04:00
|
|
|
_propagator->_uploadLimit = uploadLimit;
|
|
|
|
|
|
|
|
slotProgress(Progress::StartSync, QString(), 0, 0);
|
2013-01-15 23:41:52 +04:00
|
|
|
|
2013-08-14 21:59:16 +04:00
|
|
|
startNextTransfer();
|
2013-05-16 15:54:22 +04:00
|
|
|
}
|
2013-05-07 19:47:29 +04:00
|
|
|
|
2013-08-21 17:29:04 +04:00
|
|
|
void CSyncThread::transferCompleted(const SyncFileItem &item, CSYNC_STATUS error)
|
2013-05-16 15:54:22 +04:00
|
|
|
{
|
|
|
|
Action a;
|
2013-05-16 16:50:36 +04:00
|
|
|
a.instruction = item._instruction;
|
2013-05-16 15:54:22 +04:00
|
|
|
|
|
|
|
// if the propagator had an error for a file, put the error string into the synced item
|
2013-08-21 17:29:04 +04:00
|
|
|
if( error != CSYNC_STATUS_OK
|
2013-05-16 15:54:22 +04:00
|
|
|
|| a.instruction == CSYNC_INSTRUCTION_ERROR) {
|
|
|
|
|
2013-05-16 16:50:36 +04:00
|
|
|
// Search for the item in the starting from _iterator because it should be a bit before it.
|
|
|
|
// This works because SyncFileItem::operator== only compare the file name;
|
2013-05-16 15:54:22 +04:00
|
|
|
int idx = _syncedItems.lastIndexOf(item, _iterator);
|
|
|
|
if (idx >= 0) {
|
|
|
|
_syncedItems[idx]._instruction = CSYNC_INSTRUCTION_ERROR;
|
2013-10-02 17:36:11 +04:00
|
|
|
_syncedItems[idx]._errorString = item._errorString.isEmpty() ? csyncErrorToString( error ) : item._errorString;
|
2013-05-16 16:50:36 +04:00
|
|
|
_syncedItems[idx]._httpCode = item._httpCode;
|
2013-05-16 15:54:22 +04:00
|
|
|
qDebug() << "File " << item._file << " propagator error " << _syncedItems[idx]._errorString
|
2013-05-16 16:50:36 +04:00
|
|
|
<< "(" << item._errorString << ")";
|
2013-05-05 13:41:31 +04:00
|
|
|
}
|
|
|
|
|
2013-05-04 17:32:11 +04:00
|
|
|
if (item._isDirectory && item._instruction == CSYNC_INSTRUCTION_REMOVE
|
2013-05-05 14:33:46 +04:00
|
|
|
&& a.instruction == CSYNC_INSTRUCTION_DELETED) {
|
2013-05-16 15:54:22 +04:00
|
|
|
_lastDeleted = item._file;
|
2013-02-20 20:26:07 +04:00
|
|
|
} else {
|
2013-05-16 15:54:22 +04:00
|
|
|
_lastDeleted.clear();
|
2013-05-03 21:11:00 +04:00
|
|
|
}
|
2013-05-16 15:54:22 +04:00
|
|
|
}
|
2013-05-03 21:11:00 +04:00
|
|
|
|
2013-05-16 16:50:36 +04:00
|
|
|
a.etag = item._etag;
|
2013-05-16 15:54:22 +04:00
|
|
|
_performedActions.insert(item._originalFile, a);
|
2013-05-04 17:32:11 +04:00
|
|
|
|
2013-05-16 15:54:22 +04:00
|
|
|
if (item._instruction == CSYNC_INSTRUCTION_RENAME) {
|
|
|
|
if (a.instruction == CSYNC_INSTRUCTION_DELETED) {
|
|
|
|
// we should update the etag on the destination as well
|
|
|
|
a.instruction = CSYNC_INSTRUCTION_NONE;
|
|
|
|
} else { // ERROR
|
|
|
|
a.instruction = CSYNC_INSTRUCTION_ERROR;
|
2013-02-20 20:26:07 +04:00
|
|
|
}
|
2013-05-16 15:54:22 +04:00
|
|
|
_performedActions.insert(item._renameTarget.toUtf8(), a);
|
2013-01-15 23:41:52 +04:00
|
|
|
}
|
2013-05-05 14:33:46 +04:00
|
|
|
|
2013-08-14 21:59:16 +04:00
|
|
|
if (!item._isDirectory && a.instruction == CSYNC_INSTRUCTION_UPDATED) {
|
|
|
|
slotProgress((item._dir != SyncFileItem::Up) ? Progress::EndDownload : Progress::EndUpload,
|
|
|
|
item._file, item._size, item._size);
|
|
|
|
_progressInfo.current_file_no++;
|
|
|
|
_progressInfo.overall_current_bytes += item._size;
|
2012-02-28 19:49:13 +04:00
|
|
|
}
|
2013-01-15 23:41:52 +04:00
|
|
|
|
2013-05-16 15:54:22 +04:00
|
|
|
startNextTransfer();
|
|
|
|
}
|
2013-05-06 18:59:11 +04:00
|
|
|
|
2013-05-16 15:54:22 +04:00
|
|
|
void CSyncThread::startNextTransfer()
|
|
|
|
{
|
|
|
|
while (_iterator < _syncedItems.size() && !_propagator->_hasFatalError) {
|
|
|
|
const SyncFileItem &item = _syncedItems.at(_iterator);
|
|
|
|
++_iterator;
|
|
|
|
if (!_lastDeleted.isEmpty() && item._file.startsWith(_lastDeleted)
|
|
|
|
&& item._instruction == CSYNC_INSTRUCTION_REMOVE) {
|
|
|
|
// If the item's name starts with the name of the previously deleted directory, we
|
|
|
|
// can assume this file was already destroyed by the previous recursive call.
|
|
|
|
Action a;
|
|
|
|
a.instruction = CSYNC_INSTRUCTION_DELETED;
|
|
|
|
_performedActions.insert(item._originalFile, a);
|
|
|
|
continue;
|
2013-02-20 20:26:07 +04:00
|
|
|
}
|
2013-05-16 15:54:22 +04:00
|
|
|
_propagator->_etag.clear(); // FIXME : set to the right one
|
2013-08-14 21:59:16 +04:00
|
|
|
|
|
|
|
if (item._instruction == CSYNC_INSTRUCTION_SYNC || item._instruction == CSYNC_INSTRUCTION_NEW
|
|
|
|
|| item._instruction == CSYNC_INSTRUCTION_CONFLICT) {
|
|
|
|
slotProgress((item._dir != SyncFileItem::Up) ? Progress::StartDownload : Progress::StartUpload,
|
|
|
|
item._file, 0, item._size);
|
|
|
|
}
|
|
|
|
|
2013-05-16 15:54:22 +04:00
|
|
|
_propagator->propagate(item);
|
|
|
|
return; //propagate is async.
|
|
|
|
}
|
|
|
|
|
|
|
|
// Everything is finished.
|
|
|
|
_progressDataBase.save(_localPath);
|
|
|
|
|
|
|
|
if( csync_walk_local_tree(_csync_ctx, &walkFinalize, 0) < 0 ||
|
|
|
|
csync_walk_remote_tree( _csync_ctx, &walkFinalize, 0 ) < 0 ) {
|
|
|
|
qDebug() << "Error in finalize treewalk.";
|
|
|
|
} else {
|
|
|
|
// emit the treewalk results.
|
|
|
|
emit treeWalkResult(_syncedItems);
|
2013-01-15 23:41:52 +04:00
|
|
|
}
|
2013-05-16 15:54:22 +04:00
|
|
|
|
|
|
|
csync_commit(_csync_ctx);
|
|
|
|
|
|
|
|
qDebug() << "CSync run took " << _syncTime.elapsed() << " Milliseconds";
|
2013-08-14 21:59:16 +04:00
|
|
|
slotProgress(Progress::EndSync,QString(), 0 , 0);
|
2013-05-16 15:54:22 +04:00
|
|
|
emit finished();
|
|
|
|
_propagator.reset(0);
|
|
|
|
_syncMutex.unlock();
|
2013-10-02 21:41:17 +04:00
|
|
|
thread()->quit();
|
2012-02-28 19:49:13 +04:00
|
|
|
}
|
|
|
|
|
2013-07-26 15:44:38 +04:00
|
|
|
Progress::Kind CSyncThread::csyncToProgressKind( enum csync_notify_type_e kind )
|
|
|
|
{
|
|
|
|
Progress::Kind pKind = Progress::Invalid;
|
|
|
|
|
|
|
|
switch(kind) {
|
|
|
|
case CSYNC_NOTIFY_INVALID:
|
|
|
|
pKind = Progress::Invalid;
|
|
|
|
break;
|
|
|
|
case CSYNC_NOTIFY_START_SYNC_SEQUENCE:
|
|
|
|
pKind = Progress::StartSync;
|
|
|
|
break;
|
|
|
|
case CSYNC_NOTIFY_START_DOWNLOAD:
|
|
|
|
pKind = Progress::StartDownload;
|
|
|
|
break;
|
|
|
|
case CSYNC_NOTIFY_START_UPLOAD:
|
|
|
|
pKind = Progress::StartUpload;
|
|
|
|
break;
|
|
|
|
case CSYNC_NOTIFY_PROGRESS:
|
|
|
|
pKind = Progress::Context;
|
|
|
|
break;
|
|
|
|
case CSYNC_NOTIFY_FINISHED_DOWNLOAD:
|
|
|
|
pKind = Progress::EndDownload;
|
|
|
|
break;
|
|
|
|
case CSYNC_NOTIFY_FINISHED_UPLOAD:
|
|
|
|
pKind = Progress::EndUpload;
|
|
|
|
break;
|
|
|
|
case CSYNC_NOTIFY_FINISHED_SYNC_SEQUENCE:
|
|
|
|
pKind = Progress::EndSync;
|
|
|
|
break;
|
2013-08-03 23:33:19 +04:00
|
|
|
case CSYNC_NOTIFY_START_DELETE:
|
|
|
|
pKind = Progress::StartDelete;
|
|
|
|
break;
|
|
|
|
case CSYNC_NOTIFY_END_DELETE:
|
|
|
|
pKind = Progress::EndDelete;
|
|
|
|
break;
|
2013-07-30 11:06:18 +04:00
|
|
|
case CSYNC_NOTIFY_ERROR:
|
|
|
|
pKind = Progress::Error;
|
|
|
|
break;
|
2013-07-26 15:44:38 +04:00
|
|
|
default:
|
|
|
|
pKind = Progress::Invalid;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return pKind;
|
|
|
|
}
|
|
|
|
|
2013-08-14 21:59:16 +04:00
|
|
|
void CSyncThread::slotProgress(Progress::Kind kind, const QString &file, quint64 curr, quint64 total)
|
2013-07-25 18:28:36 +04:00
|
|
|
{
|
2013-08-14 21:59:16 +04:00
|
|
|
Progress::Info pInfo = _progressInfo;
|
|
|
|
|
|
|
|
pInfo.kind = kind;
|
|
|
|
pInfo.current_file = file;
|
|
|
|
pInfo.file_size = total;
|
|
|
|
pInfo.current_file_bytes = curr;
|
|
|
|
|
|
|
|
pInfo.overall_current_bytes += curr;
|
2013-08-13 18:53:39 +04:00
|
|
|
pInfo.timestamp = QDateTime::currentDateTime();
|
2012-12-11 15:49:48 +04:00
|
|
|
|
2013-07-26 15:44:38 +04:00
|
|
|
// Connect to something in folder!
|
2013-08-14 21:59:16 +04:00
|
|
|
transmissionProgress( pInfo );
|
2013-07-19 15:05:30 +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 */
|
|
|
|
QString CSyncThread::adjustRenamedPath(const QString& original)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
|
|
|
void CSyncThread::abort()
|
|
|
|
{
|
|
|
|
csync_request_abort(_csync_ctx);
|
|
|
|
_abortRequested = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-29 16:28:19 +04:00
|
|
|
} // ns Mirall
|