ownCloudFolder -> Folder

Fixes #769
This commit is contained in:
Daniel Molkentin 2013-07-22 20:27:42 +02:00
parent 38db0eddab
commit 56e5627b6b
11 changed files with 642 additions and 923 deletions

View file

@ -67,7 +67,6 @@ set(libsync_SRCS
mirall/networklocation.cpp
mirall/mirallconfigfile.cpp
mirall/credentialstore.cpp
mirall/owncloudfolder.cpp
mirall/csyncthread.cpp
mirall/fileutils.cpp
mirall/theme.cpp
@ -82,7 +81,6 @@ set(libsync_HEADERS
mirall/folderman.h
mirall/folder.h
mirall/folderwatcher.h
mirall/owncloudfolder.h
mirall/csyncthread.h
mirall/theme.h
mirall/owncloudtheme.h

View file

@ -128,11 +128,10 @@ void AccountSettings::slotFolderWizardAccepted()
QString alias = folderWizard->field(QLatin1String("alias")).toString();
QString sourceFolder = folderWizard->field(QLatin1String("sourceFolder")).toString();
QString targetPath = folderWizard->field(QLatin1String("OCFolderLineEdit")).toString();
QString backend = QLatin1String("owncloud");
if (!FolderMan::ensureJournalGone( sourceFolder ))
return;
folderMan->addFolderDefinition( backend, alias, sourceFolder, targetPath, false );
folderMan->addFolderDefinition(alias, sourceFolder, targetPath );
Folder *f = folderMan->setupFolderFromConfigFile( alias );
slotAddFolder( f );
folderMan->setSyncEnabled(true);

View file

@ -23,7 +23,7 @@
#include "mirall/folderwatcher.h"
#include "mirall/folderwizard.h"
#include "mirall/networklocation.h"
#include "mirall/owncloudfolder.h"
#include "mirall/folder.h"
#include "mirall/owncloudsetupwizard.h"
#include "mirall/owncloudinfo.h"
#include "mirall/sslerrordialog.h"
@ -332,8 +332,6 @@ void Application::setupContextMenu()
if( folder ) {
// if there is singleFolder mode, a generic open action is displayed.
QAction *action = new QAction( tr("Open local folder '%1'").arg(_theme->appNameGUI()), this);
action->setIcon( _theme->trayFolderIcon( folder->backend()) );
connect( action, SIGNAL(triggered()),_folderOpenActionMapper,SLOT(map()));
_folderOpenActionMapper->setMapping( action, folder->alias() );

View file

@ -1,5 +1,7 @@
/*
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
* Copyright (C) by Daniel Molkentin <danimo@owncloud.com>
* 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
@ -17,26 +19,55 @@
#include "mirall/folderwatcher.h"
#include "mirall/mirallconfigfile.h"
#include "mirall/syncresult.h"
#include "mirall/logger.h"
#include "mirall/owncloudinfo.h"
#include "mirall/credentialstore.h"
#include "mirall/utility.h"
#include <QDebug>
#include <QTimer>
#include <QUrl>
#include <QFileSystemWatcher>
#include <QDir>
#include <QMessageBox>
#include <QPushButton>
namespace Mirall {
void csyncLogCatcher(CSYNC *ctx,
int verbosity,
const char *function,
const char *buffer,
void *userdata)
{
Logger::instance()->csyncLog( QString::fromUtf8(buffer) );
}
static QString replaceScheme(const QString &urlStr)
{
QUrl url( urlStr );
if( url.scheme() == QLatin1String("http") ) {
url.setScheme( QLatin1String("owncloud") );
} else {
// connect SSL!
url.setScheme( QLatin1String("ownclouds") );
}
return url.toString();
}
Folder::Folder(const QString &alias, const QString &path, const QString& secondPath, QObject *parent)
: QObject(parent),
_errorCount(0),
_path(path),
_secondPath(secondPath),
_pollTimer(new QTimer(this)),
_alias(alias),
_onlyOnlineEnabled(false),
_onlyThisLANEnabled(false),
_online(false),
_enabled(true)
: QObject(parent)
, _path(path)
, _secondPath(secondPath)
, _pollTimer(new QTimer(this))
, _alias(alias)
, _enabled(true)
, _thread(0)
, _csync(0)
, _csyncError(false)
, _csyncUnavail(false)
, _csync_ctx(0)
{
qsrand(QTime::currentTime().msec());
MirallConfigFile cfgFile;
@ -62,21 +93,55 @@ Folder::Folder(const QString &alias, const QString &path, const QString& secondP
QObject::connect(this, SIGNAL(syncFinished(const SyncResult &)),
SLOT(slotSyncFinished(const SyncResult &)));
#if QT_VERSION >= 0x040700
_online = _networkMgr.isOnline();
QObject::connect(&_networkMgr, SIGNAL(onlineStateChanged(bool)), SLOT(slotOnlineChanged(bool)));
#else
_online = true;
#endif
_syncResult.setStatus( SyncResult::NotYetStarted );
ServerActionNotifier *notifier = new ServerActionNotifier(this);
connect(notifier, SIGNAL(guiLog(QString,QString)), Logger::instance(), SIGNAL(optionalGuiLog(QString,QString)));
connect(this, SIGNAL(syncFinished(SyncResult)), notifier, SLOT(slotSyncFinished(SyncResult)));
// check if the local path exists
checkLocalPath();
}
bool Folder::init()
{
QString url = replaceScheme(ownCloudInfo::instance()->webdavUrl() + secondPath());
QString localpath = path();
if( csync_create( &_csync_ctx, localpath.toUtf8().data(), url.toUtf8().data() ) < 0 ) {
qDebug() << "Unable to create csync-context!";
slotCSyncError(tr("Unable to create csync-context"));
_csync_ctx = 0;
} else {
csync_set_log_callback( _csync_ctx, csyncLogCatcher );
csync_set_log_verbosity(_csync_ctx, 11);
MirallConfigFile cfgFile;
csync_set_config_dir( _csync_ctx, cfgFile.configPath().toUtf8() );
csync_enable_conflictcopys(_csync_ctx);
setIgnoredFiles();
csync_set_auth_callback( _csync_ctx, getauth );
if( csync_init( _csync_ctx ) < 0 ) {
qDebug() << "Could not initialize csync!" << csync_get_error(_csync_ctx) << csync_get_error_string(_csync_ctx);
slotCSyncError(CSyncThread::csyncErrorToString(csync_get_error(_csync_ctx), csync_get_error_string(_csync_ctx)));
csync_destroy(_csync_ctx);
_csync_ctx = 0;
}
}
return _csync_ctx;
}
Folder::~Folder()
{
if( _thread ) {
_thread->quit();
csync_request_abort(_csync_ctx);
_thread->wait();
}
delete _csync;
// Destroy csync here.
csync_destroy(_csync_ctx);
}
void Folder::checkLocalPath()
@ -129,6 +194,11 @@ QString Folder::path() const
return p;
}
bool Folder::isBusy() const
{
return ( _thread && _thread->isRunning() );
}
QString Folder::secondPath() const
{
return _secondPath;
@ -163,26 +233,6 @@ void Folder::setSyncEnabled( bool doit )
}
}
bool Folder::onlyOnlineEnabled() const
{
return _onlyOnlineEnabled;
}
void Folder::setOnlyOnlineEnabled(bool enabled)
{
_onlyOnlineEnabled = enabled;
}
bool Folder::onlyThisLANEnabled() const
{
return _onlyThisLANEnabled;
}
void Folder::setOnlyThisLANEnabled(bool enabled)
{
_onlyThisLANEnabled = enabled;
}
int Folder::pollInterval() const
{
return _pollTimer->interval();
@ -198,30 +248,6 @@ void Folder::setPollInterval(int milliseconds)
_pollTimer->setInterval( milliseconds );
}
int Folder::errorCount()
{
return _errorCount;
}
void Folder::resetErrorCount()
{
_errorCount = 0;
}
void Folder::incrementErrorCount()
{
// if the error count gets higher than three, the interval timer
// of the watcher is doubled.
_errorCount++;
if( _errorCount > 1 ) {
int interval = _watcher->eventInterval();
int newInt = 2*interval;
qDebug() << "Set new watcher interval to " << newInt;
_watcher->setEventInterval( newInt );
_errorCount = 0;
}
}
SyncResult Folder::syncResult() const
{
return _syncResult;
@ -233,10 +259,6 @@ void Folder::evaluateSync(const QStringList &pathList)
qDebug() << "*" << alias() << "sync skipped, disabled!";
return;
}
if (!_online && onlyOnlineEnabled()) {
qDebug() << "*" << alias() << "sync skipped, not online";
return;
}
// stop the poll timer here. Its started again in the slot of
// sync finished.
@ -255,12 +277,6 @@ void Folder::slotPollTimerTimeout()
evaluateSync(QStringList());
}
void Folder::slotOnlineChanged(bool online)
{
qDebug() << "* " << alias() << "is" << (online ? "now online" : "no longer online");
_online = online;
}
void Folder::slotChanged(const QStringList &pathList)
{
qDebug() << "** Changed was notified on " << pathList;
@ -293,10 +309,18 @@ void Folder::slotSyncFinished(const SyncResult &result)
void Folder::slotLocalPathChanged( const QString& dir )
{
QDir notifiedDir(dir);
QDir localPath(_path );
QDir localPath( path() );
if( notifiedDir == localPath ) {
if( notifiedDir.absolutePath() == localPath.absolutePath() ) {
if( !localPath.exists() ) {
qDebug() << "XXXXXXX The sync folder root was removed!!";
if( _thread && _thread->isRunning() ) {
qDebug() << "CSync currently running, set wipe flag!!";
} else {
qDebug() << "CSync not running, wipe it now!!";
wipe();
}
qDebug() << "ALARM: The local path was DELETED!";
}
}
@ -312,20 +336,419 @@ QString Folder::configFile()
return _configFile;
}
void Folder::setBackend( const QString& b )
void Folder::slotThreadTreeWalkResult(const SyncFileItemVector& items)
{
_backend = b;
_syncResult.setSyncFileItemVector(items);
}
QString Folder::backend() const
void Folder::slotTerminateSync()
{
return _backend;
qDebug() << "folder " << alias() << " Terminating!";
MirallConfigFile cfg;
QString configDir = cfg.configPath();
qDebug() << "csync's Config Dir: " << configDir;
if( _thread && _csync ) {
csync_request_abort(_csync_ctx);
_thread->quit();
_thread->wait();
_csync->deleteLater();
delete _thread;
_csync = 0;
_thread = 0;
csync_resume(_csync_ctx);
}
if( ! configDir.isEmpty() ) {
QFile file( configDir + QLatin1String("/lock"));
if( file.exists() ) {
qDebug() << "After termination, lock file exists and gets removed.";
file.remove();
}
}
_errors.append( tr("The CSync thread terminated.") );
_csyncError = true;
qDebug() << "-> CSync Terminated!";
slotCSyncFinished();
}
// This removes the csync File database if the sync folder definition is removed
// permanentely. This is needed to provide a clean startup again in case another
// local folder is synced to the same ownCloud.
// See http://bugs.owncloud.org/thebuggenie/owncloud/issues/oc-788
void Folder::wipe()
{
QString stateDbFile = path()+QLatin1String(".csync_journal.db");
QFile file(stateDbFile);
if( file.exists() ) {
if( !file.remove()) {
qDebug() << "WRN: Failed to remove existing csync StateDB " << stateDbFile;
} else {
qDebug() << "wipe: Removed csync StateDB " << stateDbFile;
}
} else {
qDebug() << "WRN: statedb is empty, can not remove.";
}
// Check if the tmp database file also exists
QString ctmpName = path() + QLatin1String(".csync_journal.db.ctmp");
QFile ctmpFile( ctmpName );
if( ctmpFile.exists() ) {
ctmpFile.remove();
}
}
void Folder::setIgnoredFiles()
{
MirallConfigFile cfgFile;
csync_clear_exclude_list( _csync_ctx );
QString excludeList = cfgFile.excludeFile( MirallConfigFile::SystemScope );
if( !excludeList.isEmpty() ) {
qDebug() << "==== added system ignore list to csync:" << excludeList.toUtf8();
csync_add_exclude_list( _csync_ctx, excludeList.toUtf8() );
}
excludeList = cfgFile.excludeFile( MirallConfigFile::UserScope );
if( !excludeList.isEmpty() ) {
qDebug() << "==== added user defined ignore list to csync:" << excludeList.toUtf8();
csync_add_exclude_list( _csync_ctx, excludeList.toUtf8() );
}
}
void Folder::setProxy()
{
if( _csync_ctx ) {
/* Store proxy */
QUrl proxyUrl(ownCloudInfo::instance()->webdavUrl());
QList<QNetworkProxy> proxies = QNetworkProxyFactory::proxyForQuery(proxyUrl);
// We set at least one in Application
Q_ASSERT(proxies.count() > 0);
QNetworkProxy proxy = proxies.first();
if (proxy.type() == QNetworkProxy::NoProxy) {
qDebug() << "Passing NO proxy to csync for" << proxyUrl;
} else {
qDebug() << "Passing" << proxy.hostName() << "of proxy type " << proxy.type()
<< " to csync for" << proxyUrl;
}
int proxyPort = proxy.port();
csync_set_module_property(_csync_ctx, "proxy_type", (char*) proxyTypeToCStr(proxy.type()) );
csync_set_module_property(_csync_ctx, "proxy_host", proxy.hostName().toUtf8().data() );
csync_set_module_property(_csync_ctx, "proxy_port", &proxyPort );
csync_set_module_property(_csync_ctx, "proxy_user", proxy.user().toUtf8().data() );
csync_set_module_property(_csync_ctx, "proxy_pwd" , proxy.password().toUtf8().data() );
}
}
const char* Folder::proxyTypeToCStr(QNetworkProxy::ProxyType type)
{
switch (type) {
case QNetworkProxy::NoProxy:
return "NoProxy";
case QNetworkProxy::DefaultProxy:
return "DefaultProxy";
case QNetworkProxy::Socks5Proxy:
return "Socks5Proxy";
case QNetworkProxy::HttpProxy:
return "HttpProxy";
case QNetworkProxy::HttpCachingProxy:
return "HttpCachingProxy";
case QNetworkProxy::FtpCachingProxy:
return "FtpCachingProxy";
default:
return "NoProxy";
}
}
int Folder::getauth(const char *prompt,
char *buf,
size_t len,
int echo,
int verify,
void *userdata
)
{
int re = 0;
QMutex mutex;
QString qPrompt = QString::fromLatin1( prompt ).trimmed();
QString user = CredentialStore::instance()->user();
QString pwd = CredentialStore::instance()->password();
if( qPrompt == QLatin1String("Enter your username:") ) {
// qDebug() << "OOO Username requested!";
QMutexLocker locker( &mutex );
qstrncpy( buf, user.toUtf8().constData(), len );
} else if( qPrompt == QLatin1String("Enter your password:") ) {
QMutexLocker locker( &mutex );
// qDebug() << "OOO Password requested!";
qstrncpy( buf, pwd.toUtf8().constData(), len );
} else {
if( qPrompt.startsWith( QLatin1String("There are problems with the SSL certificate:"))) {
// SSL is requested. If the program came here, the SSL check was done by mirall
// It needs to be checked if the chain is still equal to the one which
// was verified by the user.
QRegExp regexp("fingerprint: ([\\w\\d:]+)");
bool certOk = false;
int pos = 0;
// This is the set of certificates which QNAM accepted, so we should accept
// them as well
QList<QSslCertificate> certs = ownCloudInfo::instance()->certificateChain();
while (!certOk && (pos = regexp.indexIn(qPrompt, 1+pos)) != -1) {
QString neon_fingerprint = regexp.cap(1);
foreach( const QSslCertificate& c, certs ) {
QString verified_shasum = Utility::formatFingerprint(c.digest(QCryptographicHash::Sha1).toHex());
qDebug() << "SSL Fingerprint from neon: " << neon_fingerprint << " compared to verified: " << verified_shasum;
if( verified_shasum == neon_fingerprint ) {
certOk = true;
break;
}
}
}
// certOk = false; DEBUG setting, keep disabled!
if( !certOk ) { // Problem!
qstrcpy( buf, "no" );
re = -1;
} else {
qstrcpy( buf, "yes" ); // Certificate is fine!
}
} else {
qDebug() << "Unknown prompt: <" << prompt << ">";
re = -1;
}
}
return re;
}
void Folder::startSync(const QStringList &pathList)
{
Q_UNUSED(pathList)
if (!_csync_ctx) {
// no _csync_ctx yet, initialize it.
init();
if (!_csync_ctx) {
qDebug() << Q_FUNC_INFO << "init failed.";
// the error should already be set
QMetaObject::invokeMethod(this, "slotCSyncFinished", Qt::QueuedConnection);
return;
}
}
if (_thread && _thread->isRunning()) {
qCritical() << "* ERROR csync is still running and new sync requested.";
return;
}
if (_thread)
_thread->quit();
delete _csync;
delete _thread;
_errors.clear();
_csyncError = false;
_csyncUnavail = false;
_syncResult.clearErrors();
_syncResult.setStatus( SyncResult::SyncPrepare );
emit syncStateChange();
qDebug() << "*** Start syncing";
_thread = new QThread(this);
_thread->setPriority(QThread::LowPriority);
setIgnoredFiles();
_csync = new CSyncThread( _csync_ctx );
_csync->setLastAuthCookies(ownCloudInfo::instance()->getLastAuthCookies());
_csync->moveToThread(_thread);
qRegisterMetaType<SyncFileItemVector>("SyncFileItemVector");
qRegisterMetaType<SyncFileItem::Direction>("SyncFileItem::Direction");
connect( _csync, SIGNAL(treeWalkResult(const SyncFileItemVector&)),
this, SLOT(slotThreadTreeWalkResult(const SyncFileItemVector&)), Qt::QueuedConnection);
connect(_csync, SIGNAL(started()), SLOT(slotCSyncStarted()), Qt::QueuedConnection);
connect(_csync, SIGNAL(finished()), SLOT(slotCSyncFinished()), Qt::QueuedConnection);
connect(_csync, SIGNAL(csyncError(QString)), SLOT(slotCSyncError(QString)), Qt::QueuedConnection);
connect(_csync, SIGNAL(csyncUnavailable()), SLOT(slotCsyncUnavailable()), Qt::QueuedConnection);
//blocking connection so the message box happens in this thread, but block the csync thread.
connect(_csync, SIGNAL(aboutToRemoveAllFiles(SyncFileItem::Direction,bool*)),
SLOT(slotAboutToRemoveAllFiles(SyncFileItem::Direction,bool*)), Qt::BlockingQueuedConnection);
connect(_csync, SIGNAL(fileTransmissionProgress(Progress::Kind, QString,long,long)),
SLOT(slotFileTransmissionProgress(Progress::Kind, QString,long,long)));
connect(_csync, SIGNAL(overallTransmissionProgress(QString, int, int, long long, long long)),
SLOT(slotOverallTransmissionProgress(QString, int, int, long long, long long)));
_thread->start();
QMetaObject::invokeMethod(_csync, "startSync", Qt::QueuedConnection);
emit syncStarted();
}
void Folder::slotCSyncError(const QString& err)
{
_errors.append( err );
_csyncError = true;
}
void Folder::slotCSyncStarted()
{
qDebug() << " * csync thread started";
_syncResult.setStatus(SyncResult::SyncRunning);
emit syncStateChange();
}
void Folder::slotCsyncUnavailable()
{
_csyncUnavail = true;
}
void Folder::slotCSyncFinished()
{
qDebug() << "-> CSync Finished slot with error " << _csyncError;
if (_csyncError) {
_syncResult.setStatus(SyncResult::Error);
qDebug() << " ** error Strings: " << _errors;
_syncResult.setErrorStrings( _errors );
qDebug() << " * owncloud csync thread finished with error";
} else if (_csyncUnavail) {
_syncResult.setStatus(SyncResult::Unavailable);
} else {
_syncResult.setStatus(SyncResult::Success);
}
if( _thread && _thread->isRunning() ) {
_thread->quit();
}
ownCloudInfo::instance()->getQuotaRequest("/");
emit syncFinished( _syncResult );
}
void Folder::slotFileTransmissionProgress(Progress::Kind kind, const QString& file ,long p1, long p2)
{
if( kind == Progress::StartDownload ) {
_progressKind = Progress::Download;
}
if( kind == Progress::StartUpload ) {
_progressKind = Progress::Upload;
}
// qDebug() << "Upload Progress: " << file << p1 << p2;
ProgressDispatcher::instance()->setFolderProgress( _progressKind, alias(), file, p1, p2 );
if( kind == Progress::EndDownload || kind == Progress::EndUpload ) {
_progressKind = Progress::Inactive;
}
}
void Folder::slotOverallTransmissionProgress( const QString& fileName, int fileNo, int fileCnt,
long long o1, long long o2)
{
ProgressDispatcher::instance()->setOverallProgress( alias(), fileName, fileNo, fileCnt, qlonglong(o1), qlonglong(o2));
}
ServerActionNotifier::ServerActionNotifier(QObject *parent)
: QObject(parent)
{
}
void ServerActionNotifier::slotSyncFinished(const SyncResult &result)
{
SyncFileItemVector items = result.syncFileItemVector();
if (items.count() == 0)
return;
int newItems = 0;
int removedItems = 0;
int updatedItems = 0;
SyncFileItem firstItemNew;
SyncFileItem firstItemDeleted;
SyncFileItem firstItemUpdated;
foreach (const SyncFileItem &item, items) {
if (item._dir == SyncFileItem::Down) {
switch (item._instruction) {
case CSYNC_INSTRUCTION_NEW:
newItems++;
if (firstItemNew.isEmpty())
firstItemNew = item;
break;
case CSYNC_INSTRUCTION_REMOVE:
removedItems++;
if (firstItemDeleted.isEmpty())
firstItemDeleted = item;
break;
case CSYNC_INSTRUCTION_UPDATED:
updatedItems++;
if (firstItemUpdated.isEmpty())
firstItemUpdated = item;
break;
default:
// nothing.
break;
}
}
}
if (newItems > 0) {
QString file = QDir::toNativeSeparators(firstItemNew._file);
if (newItems == 1)
emit guiLog(tr("New file available"), tr("'%1' has been synced to this machine.").arg(file));
else
emit guiLog(tr("New files available"), tr("'%1' and %n other file(s) have been synced to this machine.",
"", newItems-1).arg(file));
}
if (removedItems > 0) {
QString file = QDir::toNativeSeparators(firstItemDeleted._file);
if (removedItems == 1)
emit guiLog(tr("File removed"), tr("'%1' has been removed.").arg(file));
else
emit guiLog(tr("Files removed"), tr("'%1' and %n other file(s) have been removed.",
"", removedItems-1).arg(file));
}
if (updatedItems > 0) {
QString file = QDir::toNativeSeparators(firstItemUpdated._file);
if (updatedItems == 1)
emit guiLog(tr("File updated"), tr("'%1' has been updated.").arg(file));
else
emit guiLog(tr("Files updated"), tr("'%1' and %n other file(s) have been updated.",
"", updatedItems-1).arg(file));
}
}
void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction direction, bool *cancel)
{
QString msg = direction == SyncFileItem::Down ?
tr("This sync would remove all the files in the local sync folder '%1'.\n"
"If you or your administrator have reset your account on the server, choose "
"\"Keep files\". If you want your data to be removed, choose \"Remove all files\".") :
tr("This sync would remove all the files in the sync folder '%1'.\n"
"This might be because the folder was silently reconfigured, or that all "
"the file were manually removed.\n"
"Are you sure you want to perform this operation?");
QMessageBox msgBox(QMessageBox::Warning, tr("Remove All Files?"),
msg.arg(alias()));
msgBox.addButton(tr("Remove all files"), QMessageBox::DestructiveRole);
QPushButton* keepBtn = msgBox.addButton(tr("Keep files"), QMessageBox::ActionRole);
if (msgBox.exec() == -1) {
*cancel = true;
return;
}
*cancel = msgBox.clickedButton() == keepBtn;
if (*cancel) {
wipe();
}
}
} // namespace Mirall

View file

@ -1,5 +1,7 @@
/*
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
* Copyright (C) by Daniel Molkentin <danimo@owncloud.com>
* 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
@ -15,38 +17,74 @@
#ifndef MIRALL_FOLDER_H
#define MIRALL_FOLDER_H
#include "mirall/syncresult.h"
#include "mirall/progressdispatcher.h"
#include "mirall/csyncthread.h"
#include <QObject>
#include <QString>
#include <QStringList>
#include <QDir>
#include <QHash>
#include <QNetworkAccessManager>
#include <QNetworkProxy>
#include <QNetworkProxyFactory>
#include <QObject>
#include <QStringList>
#include <QThread>
#include <QTimer>
#if QT_VERSION >= 0x040700
#include <QNetworkConfigurationManager>
#endif
#include <QDebug>
#include "mirall/syncresult.h"
class QAction;
class QIcon;
class QFileSystemWatcher;
namespace Mirall {
class FolderWatcher;
typedef enum SyncFileStatus_s {
STATUS_NONE,
STATUS_EVAL,
STATUS_REMOVE,
STATUS_RENAME,
STATUS_NEW,
STATUS_CONFLICT,
STATUS_IGNORE,
STATUS_SYNC,
STATUS_STAT_ERROR,
STATUS_ERROR,
STATUS_UPDATED
} SyncFileStatus;
class ServerActionNotifier : public QObject
{
Q_OBJECT
public:
ServerActionNotifier(QObject *parent = 0);
public slots:
void slotSyncFinished(const SyncResult &result);
signals:
void guiLog(const QString&, const QString&);
void sendResults();
private:
};
class Folder : public QObject
{
Q_OBJECT
public:
protected:
friend class FolderMan;
Folder(const QString&, const QString&, const QString& , QObject*parent = 0L);
virtual ~Folder();
public:
~Folder();
typedef QHash<QString, Folder*> Map;
typedef QHashIterator<QString, Folder*> MapIterator;
/**
* Get status about a single file.
*/
SyncFileStatus fileStatus( const QString& );
/**
* alias or nickname
*/
@ -56,12 +94,16 @@ public:
* local folder path
*/
QString path() const;
virtual QString secondPath() const;
/**
* remote folder path
*/
QString secondPath() const;
/**
* local folder path with native separators
*/
QString nativePath() const;
/**
* switch sync on or off
* If the sync is switched off, the startSync method is not going to
@ -71,69 +113,17 @@ public:
bool syncEnabled() const;
/**
* Starts a sync operation
*
* If the list of changed files is known, it is passed.
*
* If the list of changed files is empty, the folder
* implementation should figure it by itself of
* perform a full scan of changes
*/
virtual void startSync(const QStringList &pathList) = 0;
/**
* True if the folder is busy and can't initiate
* a synchronization
*/
virtual bool isBusy() const = 0;
/**
* only sync when online in the network
*/
bool onlyOnlineEnabled() const;
/**
* @see onlyOnlineEnabled
*/
void setOnlyOnlineEnabled(bool enabled);
/**
* only sync when online in the same LAN
* as the one used during setup
*/
bool onlyThisLANEnabled() const;
/**
* @see onlyThisLANEnabled
*/
void setOnlyThisLANEnabled(bool enabled);
/**
* error counter, stop syncing after the counter reaches a certain
* number.
*/
int errorCount();
void resetErrorCount();
void incrementErrorCount();
virtual bool isBusy() const;
/**
* return the last sync result with error message and status
*/
SyncResult syncResult() const;
/**
* set the backend description string.
*/
void setBackend( const QString& );
/**
* get the backend description string.
*/
QString backend() const;
/**
* set the config file name.
*/
@ -145,7 +135,6 @@ public:
*/
virtual void wipe();
QIcon icon( int size ) const;
QTimer *_pollTimer;
signals:
@ -165,7 +154,7 @@ public slots:
/**
* terminate the current sync run
*/
virtual void slotTerminateSync() = 0;
void slotTerminateSync();
/**
* Sets minimum amounts of milliseconds that will separate
@ -173,7 +162,42 @@ public slots:
*/
void setPollInterval( int );
void slotAboutToRemoveAllFiles(SyncFileItem::Direction, bool*);
/**
* Starts a sync operation
*
* If the list of changed files is known, it is passed.
*/
void startSync(const QStringList &pathList = QStringList());
private slots:
void slotCSyncStarted();
void slotCSyncError(const QString& );
void slotCsyncUnavailable();
void slotCSyncFinished();
void slotFileTransmissionProgress(Progress::Kind, const QString&,long, long);
void slotOverallTransmissionProgress( const QString&, int, int, long long, long long);
void slotPollTimerTimeout();
/** called when the watcher detect a list of changed paths */
void slotSyncStarted();
/**
* Triggered by a file system watcher on the local sync dir
*/
void slotLocalPathChanged( const QString& );
void slotThreadTreeWalkResult(const SyncFileItemVector& );
protected:
bool init();
/**
* The minimum amounts of seconds to wait before
* doing a full sync to see if the remote changed
@ -181,27 +205,16 @@ protected:
int pollInterval() const;
void setSyncState(SyncResult::Status state);
FolderWatcher *_watcher;
int _errorCount;
SyncResult _syncResult;
protected slots:
void slotOnlineChanged(bool online);
void slotPollTimerTimeout();
/* called when the watcher detect a list of changed
paths */
void slotSyncStarted();
/**
* Triggered by a file system watcher on the local sync dir
*/
virtual void slotLocalPathChanged( const QString& );
private:
void setIgnoredFiles();
void setProxy();
static int getauth(const char *prompt,
char *buf,
size_t len,
int echo,
int verify,
void *userdata
);
const char* proxyTypeToCStr(QNetworkProxy::ProxyType type);
/**
* Starts a sync (calling startSync)
@ -209,24 +222,25 @@ private:
*/
void evaluateSync(const QStringList &pathList);
virtual void checkLocalPath();
void checkLocalPath();
QString _path;
QString _secondPath;
QString _alias;
bool _onlyOnlineEnabled;
bool _onlyThisLANEnabled;
QString _configFile;
QFileSystemWatcher *_pathWatcher;
#if QT_VERSION >= 0x040700
QNetworkConfigurationManager _networkMgr;
#endif
bool _online;
bool _enabled;
QString _backend;
FolderWatcher *_watcher;
SyncResult _syncResult;
QThread *_thread;
CSyncThread *_csync;
QStringList _errors;
bool _csyncError;
bool _csyncUnavail;
bool _wipeDb;
Progress::Kind _progressKind;
CSYNC *_csync_ctx;
};
}

View file

@ -14,7 +14,7 @@
#include "mirall/folderman.h"
#include "mirall/mirallconfigfile.h"
#include "mirall/owncloudfolder.h"
#include "mirall/folder.h"
#include "mirall/syncresult.h"
#include "mirall/inotify.h"
#include "mirall/theme.h"
@ -252,39 +252,28 @@ Folder* FolderMan::setupFolderFromConfigFile(const QString &file) {
// QString connection = settings.value( QLatin1String("connection") ).toString();
QString alias = unescapeAlias( escapedAlias );
if (!backend.isEmpty()) {
if( backend == QLatin1String("owncloud") ) {
// cut off the leading slash, oCUrl always has a trailing.
if( targetPath.startsWith(QLatin1Char('/')) ) {
targetPath.remove(0,1);
}
folder = new ownCloudFolder( alias, path, targetPath, this );
folder->setConfigFile(file);
} else {
qWarning() << "unknown backend" << backend;
return 0;
}
if (backend.isEmpty() || backend != QLatin1String("owncloud")) {
qWarning() << "obsolete configuration of type" << backend;
return 0;
}
if( folder ) {
folder->setBackend( backend );
// folder->setOnlyOnlineEnabled(settings.value("folder/onlyOnline", false).toBool());
folder->setOnlyThisLANEnabled(settings.value(QLatin1String("folder/onlyThisLAN"), false).toBool());
_folderMap[alias] = folder;
qDebug() << "Adding folder to Folder Map " << folder;
/* Use a signal mapper to connect the signals to the alias */
connect(folder, SIGNAL(scheduleToSync(const QString&)), SLOT(slotScheduleSync(const QString&)));
connect(folder, SIGNAL(syncStateChange()), _folderChangeSignalMapper, SLOT(map()));
connect(folder, SIGNAL(syncStarted()), SLOT(slotFolderSyncStarted()));
connect(folder, SIGNAL(syncFinished(SyncResult)), SLOT(slotFolderSyncFinished(SyncResult)));
_folderChangeSignalMapper->setMapping( folder, folder->alias() );
// cut off the leading slash, oCUrl always has a trailing.
if( targetPath.startsWith(QLatin1Char('/')) ) {
targetPath.remove(0,1);
}
folder = new Folder( alias, path, targetPath, this );
folder->setConfigFile(file);
qDebug() << "Adding folder to Folder Map " << folder;
_folderMap[alias] = folder;
/* Use a signal mapper to connect the signals to the alias */
connect(folder, SIGNAL(scheduleToSync(const QString&)), SLOT(slotScheduleSync(const QString&)));
connect(folder, SIGNAL(syncStateChange()), _folderChangeSignalMapper, SLOT(map()));
connect(folder, SIGNAL(syncStarted()), SLOT(slotFolderSyncStarted()));
connect(folder, SIGNAL(syncFinished(SyncResult)), SLOT(slotFolderSyncFinished(SyncResult)));
_folderChangeSignalMapper->setMapping( folder, folder->alias() );
return folder;
}
@ -426,15 +415,11 @@ void FolderMan::slotFolderSyncFinished( const SyncResult& )
/**
* Add a folder definition to the config
* Params:
* QString backend
* QString alias
* QString sourceFolder on local machine
* QString targetPath on remote
* bool onlyThisLAN, currently unused.
*/
void FolderMan::addFolderDefinition( const QString& backend, const QString& alias,
const QString& sourceFolder, const QString& targetPath,
bool onlyThisLAN )
void FolderMan::addFolderDefinition(const QString& alias, const QString& sourceFolder, const QString& targetPath )
{
QString escapedAlias = escapeAlias(alias);
// Create a settings file named after the alias
@ -442,9 +427,9 @@ void FolderMan::addFolderDefinition( const QString& backend, const QString& alia
settings.setValue(QString::fromLatin1("%1/localPath").arg(escapedAlias), sourceFolder );
settings.setValue(QString::fromLatin1("%1/targetPath").arg(escapedAlias), targetPath );
settings.setValue(QString::fromLatin1("%1/backend").arg(escapedAlias), backend );
// for compat reasons
settings.setValue(QString::fromLatin1("%1/backend").arg(escapedAlias), "owncloud" );
settings.setValue(QString::fromLatin1("%1/connection").arg(escapedAlias), Theme::instance()->appName());
settings.setValue(QString::fromLatin1("%1/onlyThisLAN").arg(escapedAlias), onlyThisLAN );
settings.sync();
}

View file

@ -44,13 +44,11 @@ public:
/**
* Add a folder definition to the config
* Params:
* QString backend
* QString alias
* QString sourceFolder on local machine
* QString targetPath on remote
* bool onlyThisLAN, currently unused.
*/
void addFolderDefinition( const QString&, const QString&, const QString&, const QString&, bool );
void addFolderDefinition(const QString&, const QString&, const QString& );
/**
* return the folder by alias or NULL if no folder with the alias exists.

View file

@ -37,8 +37,8 @@
#elif defined(Q_OS_MAC)
#include "mirall/folderwatcher_mac.h"
#elif defined(USE_INOTIFY)
#include "mirall/folderwatcher_inotify.h"
#endif
#include "mirall/folderwatcher_inotify.h"
namespace Mirall {

View file

@ -1,572 +0,0 @@
/*
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
* Copyright (C) by Klaas Freitag <freitag@owncloud.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "mirall/owncloudfolder.h"
#include "mirall/mirallconfigfile.h"
#include "mirall/owncloudinfo.h"
#include "mirall/credentialstore.h"
#include "mirall/logger.h"
#include "mirall/utility.h"
#include "mirall/progressdispatcher.h"
#include <csync.h>
#include <QDebug>
#include <QDir>
#include <QUrl>
#include <QMutexLocker>
#include <QThread>
#include <QStringList>
#include <QTextStream>
#include <QTimer>
#include <QNetworkProxy>
#include <QNetworkAccessManager>
#include <QNetworkProxyFactory>
#include <QMessageBox>
#include <QPushButton>
namespace Mirall {
void csyncLogCatcher(CSYNC *ctx,
int verbosity,
const char *function,
const char *buffer,
void *userdata)
{
Logger::instance()->csyncLog( QString::fromUtf8(buffer) );
}
static QString replaceScheme(const QString &urlStr)
{
QUrl url( urlStr );
if( url.scheme() == QLatin1String("http") ) {
url.setScheme( QLatin1String("owncloud") );
} else {
// connect SSL!
url.setScheme( QLatin1String("ownclouds") );
}
return url.toString();
}
ownCloudFolder::ownCloudFolder(const QString &alias,
const QString &mpath,
const QString &secondPath,
QObject *parent)
: Folder(alias, mpath, secondPath, parent)
, _thread(0)
, _csync(0)
, _csyncError(false)
, _csyncUnavail(false)
, _csync_ctx(0)
{
ServerActionNotifier *notifier = new ServerActionNotifier(this);
connect(notifier, SIGNAL(guiLog(QString,QString)), Logger::instance(), SIGNAL(optionalGuiLog(QString,QString)));
connect(this, SIGNAL(syncFinished(SyncResult)), notifier, SLOT(slotSyncFinished(SyncResult)));
qDebug() << "****** ownCloud folder using watcher *******";
// The folder interval is set in the folder parent class.
}
bool ownCloudFolder::init()
{
QString url = replaceScheme(ownCloudInfo::instance()->webdavUrl() + secondPath());
QString localpath = path();
if( csync_create( &_csync_ctx, localpath.toUtf8().data(), url.toUtf8().data() ) < 0 ) {
qDebug() << "Unable to create csync-context!";
slotCSyncError(tr("Unable to create csync-context"));
_csync_ctx = 0;
} else {
csync_set_log_callback( _csync_ctx, csyncLogCatcher );
csync_set_log_verbosity(_csync_ctx, 11);
MirallConfigFile cfgFile;
csync_set_config_dir( _csync_ctx, cfgFile.configPath().toUtf8() );
csync_enable_conflictcopys(_csync_ctx);
setIgnoredFiles();
csync_set_auth_callback( _csync_ctx, getauth );
if( csync_init( _csync_ctx ) < 0 ) {
qDebug() << "Could not initialize csync!" << csync_get_error(_csync_ctx) << csync_get_error_string(_csync_ctx);
slotCSyncError(CSyncThread::csyncErrorToString(csync_get_error(_csync_ctx), csync_get_error_string(_csync_ctx)));
csync_destroy(_csync_ctx);
_csync_ctx = 0;
}
}
return _csync_ctx;
}
ownCloudFolder::~ownCloudFolder()
{
if( _thread ) {
_thread->quit();
csync_request_abort(_csync_ctx);
_thread->wait();
}
delete _csync;
// Destroy csync here.
csync_destroy(_csync_ctx);
}
void ownCloudFolder::setIgnoredFiles()
{
MirallConfigFile cfgFile;
csync_clear_exclude_list( _csync_ctx );
QString excludeList = cfgFile.excludeFile( MirallConfigFile::SystemScope );
if( !excludeList.isEmpty() ) {
qDebug() << "==== added system ignore list to csync:" << excludeList.toUtf8();
csync_add_exclude_list( _csync_ctx, excludeList.toUtf8() );
}
excludeList = cfgFile.excludeFile( MirallConfigFile::UserScope );
if( !excludeList.isEmpty() ) {
qDebug() << "==== added user defined ignore list to csync:" << excludeList.toUtf8();
csync_add_exclude_list( _csync_ctx, excludeList.toUtf8() );
}
}
void ownCloudFolder::setProxy()
{
if( _csync_ctx ) {
/* Store proxy */
QUrl proxyUrl(ownCloudInfo::instance()->webdavUrl());
QList<QNetworkProxy> proxies = QNetworkProxyFactory::proxyForQuery(proxyUrl);
// We set at least one in Application
Q_ASSERT(proxies.count() > 0);
QNetworkProxy proxy = proxies.first();
if (proxy.type() == QNetworkProxy::NoProxy) {
qDebug() << "Passing NO proxy to csync for" << proxyUrl;
} else {
qDebug() << "Passing" << proxy.hostName() << "of proxy type " << proxy.type()
<< " to csync for" << proxyUrl;
}
int proxyPort = proxy.port();
csync_set_module_property(_csync_ctx, "proxy_type", (char*) proxyTypeToCStr(proxy.type()) );
csync_set_module_property(_csync_ctx, "proxy_host", proxy.hostName().toUtf8().data() );
csync_set_module_property(_csync_ctx, "proxy_port", &proxyPort );
csync_set_module_property(_csync_ctx, "proxy_user", proxy.user().toUtf8().data() );
csync_set_module_property(_csync_ctx, "proxy_pwd" , proxy.password().toUtf8().data() );
}
}
const char* ownCloudFolder::proxyTypeToCStr(QNetworkProxy::ProxyType type)
{
switch (type) {
case QNetworkProxy::NoProxy:
return "NoProxy";
case QNetworkProxy::DefaultProxy:
return "DefaultProxy";
case QNetworkProxy::Socks5Proxy:
return "Socks5Proxy";
case QNetworkProxy::HttpProxy:
return "HttpProxy";
case QNetworkProxy::HttpCachingProxy:
return "HttpCachingProxy";
case QNetworkProxy::FtpCachingProxy:
return "FtpCachingProxy";
default:
return "NoProxy";
}
}
int ownCloudFolder::getauth(const char *prompt,
char *buf,
size_t len,
int echo,
int verify,
void *userdata
)
{
int re = 0;
QMutex mutex;
QString qPrompt = QString::fromLatin1( prompt ).trimmed();
QString user = CredentialStore::instance()->user();
QString pwd = CredentialStore::instance()->password();
if( qPrompt == QLatin1String("Enter your username:") ) {
// qDebug() << "OOO Username requested!";
QMutexLocker locker( &mutex );
qstrncpy( buf, user.toUtf8().constData(), len );
} else if( qPrompt == QLatin1String("Enter your password:") ) {
QMutexLocker locker( &mutex );
// qDebug() << "OOO Password requested!";
qstrncpy( buf, pwd.toUtf8().constData(), len );
} else {
if( qPrompt.startsWith( QLatin1String("There are problems with the SSL certificate:"))) {
// SSL is requested. If the program came here, the SSL check was done by mirall
// It needs to be checked if the chain is still equal to the one which
// was verified by the user.
QRegExp regexp("fingerprint: ([\\w\\d:]+)");
bool certOk = false;
int pos = 0;
// This is the set of certificates which QNAM accepted, so we should accept
// them as well
QList<QSslCertificate> certs = ownCloudInfo::instance()->certificateChain();
while (!certOk && (pos = regexp.indexIn(qPrompt, 1+pos)) != -1) {
QString neon_fingerprint = regexp.cap(1);
foreach( const QSslCertificate& c, certs ) {
QString verified_shasum = Utility::formatFingerprint(c.digest(QCryptographicHash::Sha1).toHex());
qDebug() << "SSL Fingerprint from neon: " << neon_fingerprint << " compared to verified: " << verified_shasum;
if( verified_shasum == neon_fingerprint ) {
certOk = true;
break;
}
}
}
// certOk = false; DEBUG setting, keep disabled!
if( !certOk ) { // Problem!
qstrcpy( buf, "no" );
re = -1;
} else {
qstrcpy( buf, "yes" ); // Certificate is fine!
}
} else {
qDebug() << "Unknown prompt: <" << prompt << ">";
re = -1;
}
}
return re;
}
bool ownCloudFolder::isBusy() const
{
return ( _thread && _thread->isRunning() );
}
QString ownCloudFolder::secondPath() const
{
return Folder::secondPath();
}
void ownCloudFolder::startSync()
{
startSync( QStringList() );
}
void ownCloudFolder::startSync(const QStringList &pathList)
{
if (!_csync_ctx) {
// no _csync_ctx yet, initialize it.
init();
if (!_csync_ctx) {
qDebug() << Q_FUNC_INFO << "init failed.";
// the error should already be set
QMetaObject::invokeMethod(this, "slotCSyncFinished", Qt::QueuedConnection);
return;
}
}
if (_thread && _thread->isRunning()) {
qCritical() << "* ERROR csync is still running and new sync requested.";
return;
}
if (_thread)
_thread->quit();
delete _csync;
delete _thread;
_errors.clear();
_csyncError = false;
_csyncUnavail = false;
_syncResult.clearErrors();
_syncResult.setStatus( SyncResult::SyncPrepare );
emit syncStateChange();
qDebug() << "*** Start syncing";
_thread = new QThread(this);
_thread->setPriority(QThread::LowPriority);
setIgnoredFiles();
_csync = new CSyncThread( _csync_ctx );
_csync->setLastAuthCookies(ownCloudInfo::instance()->getLastAuthCookies());
_csync->moveToThread(_thread);
qRegisterMetaType<SyncFileItemVector>("SyncFileItemVector");
qRegisterMetaType<SyncFileItem::Direction>("SyncFileItem::Direction");
connect( _csync, SIGNAL(treeWalkResult(const SyncFileItemVector&)),
this, SLOT(slotThreadTreeWalkResult(const SyncFileItemVector&)), Qt::QueuedConnection);
connect(_csync, SIGNAL(started()), SLOT(slotCSyncStarted()), Qt::QueuedConnection);
connect(_csync, SIGNAL(finished()), SLOT(slotCSyncFinished()), Qt::QueuedConnection);
connect(_csync, SIGNAL(csyncError(QString)), SLOT(slotCSyncError(QString)), Qt::QueuedConnection);
connect(_csync, SIGNAL(csyncUnavailable()), SLOT(slotCsyncUnavailable()), Qt::QueuedConnection);
//blocking connection so the message box happens in this thread, but block the csync thread.
connect(_csync, SIGNAL(aboutToRemoveAllFiles(SyncFileItem::Direction,bool*)),
SLOT(slotAboutToRemoveAllFiles(SyncFileItem::Direction,bool*)), Qt::BlockingQueuedConnection);
connect(_csync, SIGNAL(fileTransmissionProgress(Progress::Kind, QString,long,long)),
SLOT(slotFileTransmissionProgress(Progress::Kind, QString,long,long)));
connect(_csync, SIGNAL(overallTransmissionProgress(QString, int, int, long long, long long)),
SLOT(slotOverallTransmissionProgress(QString, int, int, long long, long long)));
_thread->start();
QMetaObject::invokeMethod(_csync, "startSync", Qt::QueuedConnection);
emit syncStarted();
}
void ownCloudFolder::slotCSyncStarted()
{
qDebug() << " * csync thread started";
_syncResult.setStatus(SyncResult::SyncRunning);
emit syncStateChange();
}
void ownCloudFolder::slotCSyncError(const QString& err)
{
_errors.append( err );
_csyncError = true;
}
void ownCloudFolder::slotCsyncUnavailable()
{
_csyncUnavail = true;
}
void ownCloudFolder::slotCSyncFinished()
{
qDebug() << "-> CSync Finished slot with error " << _csyncError;
if (_csyncError) {
_syncResult.setStatus(SyncResult::Error);
qDebug() << " ** error Strings: " << _errors;
_syncResult.setErrorStrings( _errors );
qDebug() << " * owncloud csync thread finished with error";
} else if (_csyncUnavail) {
_syncResult.setStatus(SyncResult::Unavailable);
} else {
_syncResult.setStatus(SyncResult::Success);
}
if( _thread && _thread->isRunning() ) {
_thread->quit();
}
ownCloudInfo::instance()->getQuotaRequest("/");
emit syncFinished( _syncResult );
}
void ownCloudFolder::slotThreadTreeWalkResult(const SyncFileItemVector& items)
{
_syncResult.setSyncFileItemVector(items);
}
void ownCloudFolder::slotTerminateSync()
{
qDebug() << "folder " << alias() << " Terminating!";
MirallConfigFile cfg;
QString configDir = cfg.configPath();
qDebug() << "csync's Config Dir: " << configDir;
if( _thread && _csync ) {
csync_request_abort(_csync_ctx);
_thread->quit();
_thread->wait();
_csync->deleteLater();
delete _thread;
_csync = 0;
_thread = 0;
csync_resume(_csync_ctx);
}
if( ! configDir.isEmpty() ) {
QFile file( configDir + QLatin1String("/lock"));
if( file.exists() ) {
qDebug() << "After termination, lock file exists and gets removed.";
file.remove();
}
}
_errors.append( tr("The CSync thread terminated.") );
_csyncError = true;
qDebug() << "-> CSync Terminated!";
slotCSyncFinished();
}
void ownCloudFolder::slotLocalPathChanged( const QString& dir )
{
QDir notifiedDir(dir);
QDir localPath( path() );
if( notifiedDir.absolutePath() == localPath.absolutePath() ) {
if( !localPath.exists() ) {
qDebug() << "XXXXXXX The sync folder root was removed!!";
if( _thread && _thread->isRunning() ) {
qDebug() << "CSync currently running, set wipe flag!!";
} else {
qDebug() << "CSync not running, wipe it now!!";
wipe();
}
qDebug() << "ALARM: The local path was DELETED!";
}
}
}
void ownCloudFolder::slotFileTransmissionProgress(Progress::Kind kind, const QString& file ,long p1, long p2)
{
if( kind == Progress::StartDownload ) {
_progressKind = Progress::Download;
}
if( kind == Progress::StartUpload ) {
_progressKind = Progress::Upload;
}
// qDebug() << "Upload Progress: " << file << p1 << p2;
ProgressDispatcher::instance()->setFolderProgress( _progressKind, alias(), file, p1, p2 );
if( kind == Progress::EndDownload || kind == Progress::EndUpload ) {
_progressKind = Progress::Inactive;
}
}
void ownCloudFolder::slotOverallTransmissionProgress( const QString& fileName, int fileNo, int fileCnt,
long long o1, long long o2)
{
ProgressDispatcher::instance()->setOverallProgress( alias(), fileName, fileNo, fileCnt, qlonglong(o1), qlonglong(o2));
}
// This removes the csync File database if the sync folder definition is removed
// permanentely. This is needed to provide a clean startup again in case another
// local folder is synced to the same ownCloud.
// See http://bugs.owncloud.org/thebuggenie/owncloud/issues/oc-788
void ownCloudFolder::wipe()
{
QString stateDbFile = path()+QLatin1String(".csync_journal.db");
QFile file(stateDbFile);
if( file.exists() ) {
if( !file.remove()) {
qDebug() << "WRN: Failed to remove existing csync StateDB " << stateDbFile;
} else {
qDebug() << "wipe: Removed csync StateDB " << stateDbFile;
}
} else {
qDebug() << "WRN: statedb is empty, can not remove.";
}
// Check if the tmp database file also exists
QString ctmpName = path() + QLatin1String(".csync_journal.db.ctmp");
QFile ctmpFile( ctmpName );
if( ctmpFile.exists() ) {
ctmpFile.remove();
}
}
ServerActionNotifier::ServerActionNotifier(QObject *parent)
: QObject(parent)
{
}
void ServerActionNotifier::slotSyncFinished(const SyncResult &result)
{
SyncFileItemVector items = result.syncFileItemVector();
if (items.count() == 0)
return;
int newItems = 0;
int removedItems = 0;
int updatedItems = 0;
SyncFileItem firstItemNew;
SyncFileItem firstItemDeleted;
SyncFileItem firstItemUpdated;
foreach (const SyncFileItem &item, items) {
if (item._dir == SyncFileItem::Down) {
switch (item._instruction) {
case CSYNC_INSTRUCTION_NEW:
newItems++;
if (firstItemNew.isEmpty())
firstItemNew = item;
break;
case CSYNC_INSTRUCTION_REMOVE:
removedItems++;
if (firstItemDeleted.isEmpty())
firstItemDeleted = item;
break;
case CSYNC_INSTRUCTION_UPDATED:
updatedItems++;
if (firstItemUpdated.isEmpty())
firstItemUpdated = item;
break;
default:
// nothing.
break;
}
}
}
if (newItems > 0) {
QString file = QDir::toNativeSeparators(firstItemNew._file);
if (newItems == 1)
emit guiLog(tr("New file available"), tr("'%1' has been synced to this machine.").arg(file));
else
emit guiLog(tr("New files available"), tr("'%1' and %n other file(s) have been synced to this machine.",
"", newItems-1).arg(file));
}
if (removedItems > 0) {
QString file = QDir::toNativeSeparators(firstItemDeleted._file);
if (removedItems == 1)
emit guiLog(tr("File removed"), tr("'%1' has been removed.").arg(file));
else
emit guiLog(tr("Files removed"), tr("'%1' and %n other file(s) have been removed.",
"", removedItems-1).arg(file));
}
if (updatedItems > 0) {
QString file = QDir::toNativeSeparators(firstItemUpdated._file);
if (updatedItems == 1)
emit guiLog(tr("File updated"), tr("'%1' has been updated.").arg(file));
else
emit guiLog(tr("Files updated"), tr("'%1' and %n other file(s) have been updated.",
"", updatedItems-1).arg(file));
}
}
void ownCloudFolder::slotAboutToRemoveAllFiles(SyncFileItem::Direction direction, bool *cancel)
{
QString msg = direction == SyncFileItem::Down ?
tr("This sync would remove all the files in the local sync folder '%1'.\n"
"If you or your administrator have reset your account on the server, choose "
"\"Keep files\". If you want your data to be removed, choose \"Remove all files\".") :
tr("This sync would remove all the files in the sync folder '%1'.\n"
"This might be because the folder was silently reconfigured, or that all "
"the file were manually removed.\n"
"Are you sure you want to perform this operation?");
QMessageBox msgBox(QMessageBox::Warning, tr("Remove All Files?"),
msg.arg(alias()));
msgBox.addButton(tr("Remove all files"), QMessageBox::DestructiveRole);
QPushButton* keepBtn = msgBox.addButton(tr("Keep files"), QMessageBox::ActionRole);
if (msgBox.exec() == -1) {
*cancel = true;
return;
}
*cancel = msgBox.clickedButton() == keepBtn;
if (*cancel) {
wipe();
}
}
} // ns

View file

@ -1,124 +0,0 @@
/*
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
*
* 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.
*/
#ifndef MIRALL_ownCloudFolder_H
#define MIRALL_ownCloudFolder_H
#include <QMutex>
#include <QThread>
#include <QStringList>
#include "mirall/folder.h"
#include "mirall/csyncthread.h"
#include "mirall/progressdispatcher.h"
class QProcess;
class QTimer;
namespace Mirall {
enum SyncFileStatus_s {
STATUS_NONE,
STATUS_EVAL,
STATUS_REMOVE,
STATUS_RENAME,
STATUS_NEW,
STATUS_CONFLICT,
STATUS_IGNORE,
STATUS_SYNC,
STATUS_STAT_ERROR,
STATUS_ERROR,
STATUS_UPDATED
};
typedef SyncFileStatus_s SyncFileStatus;
class ServerActionNotifier : public QObject
{
Q_OBJECT
public:
ServerActionNotifier(QObject *parent = 0);
public slots:
void slotSyncFinished(const SyncResult &result);
signals:
void guiLog(const QString&, const QString&);
void sendResults();
private:
};
class ownCloudFolder : public Folder
{
Q_OBJECT
public:
ownCloudFolder(const QString &alias,
const QString &path,
const QString &secondPath, QObject *parent = 0L);
virtual ~ownCloudFolder();
QString secondPath() const;
virtual bool isBusy() const;
virtual void startSync(const QStringList &pathList);
virtual void wipe();
/* get status about a singel file. */
SyncFileStatus fileStatus( const QString& );
public slots:
void startSync();
void slotTerminateSync();
void slotAboutToRemoveAllFiles(SyncFileItem::Direction, bool*);
protected slots:
void slotLocalPathChanged( const QString& );
void slotThreadTreeWalkResult(const SyncFileItemVector& );
private slots:
void slotCSyncStarted();
void slotCSyncError(const QString& );
void slotCsyncUnavailable();
void slotCSyncFinished();
void slotFileTransmissionProgress(Progress::Kind, const QString&,long, long);
void slotOverallTransmissionProgress( const QString&, int, int, long long, long long);
private:
void setIgnoredFiles();
void setProxy();
static int getauth(const char *prompt,
char *buf,
size_t len,
int echo,
int verify,
void *userdata
);
const char* proxyTypeToCStr(QNetworkProxy::ProxyType type);
bool init();
QString _secondPath;
QThread *_thread;
CSyncThread *_csync;
QStringList _errors;
bool _csyncError;
bool _csyncUnavail;
bool _wipeDb;
SyncFileItemVector _items;
Progress::Kind _progressKind;
CSYNC *_csync_ctx;
};
}
#endif

View file

@ -168,8 +168,8 @@ void OwncloudSetupWizard::slotAssistantFinished( int result )
// Now write the resulting folder definition if folder names are set.
if( acceptCfg && urlHasChanged ) {
folderMan->removeAllFolderDefinitions();
folderMan->addFolderDefinition( QLatin1String("owncloud"), Theme::instance()->appName(),
localFolder, _remoteFolder, false );
folderMan->addFolderDefinition(Theme::instance()->appName(),
localFolder, _remoteFolder );
_ocWizard->appendToConfigurationLog(tr("<font color=\"green\"><b>Local sync folder %1 successfully created!</b></font>").arg(localFolder));
} else {
// url is unchanged. Only the password was changed.