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-02-15 12:30:37 +04:00
|
|
|
#include <QDebug>
|
|
|
|
#include <QDir>
|
|
|
|
#include <QMutexLocker>
|
|
|
|
#include <QThread>
|
|
|
|
#include <QStringList>
|
|
|
|
#include <QTextStream>
|
2012-02-28 19:49:13 +04:00
|
|
|
#include <QTime>
|
2012-02-15 12:30:37 +04:00
|
|
|
|
|
|
|
namespace Mirall {
|
|
|
|
|
2012-03-13 20:37:43 +04:00
|
|
|
/* static variables to hold the credentials */
|
|
|
|
QString CSyncThread::_user;
|
|
|
|
QString CSyncThread::_passwd;
|
2012-04-30 10:56:56 +04:00
|
|
|
QString CSyncThread::_csyncConfigDir; // to be able to remove the lock file.
|
|
|
|
|
2012-03-22 19:22:08 +04:00
|
|
|
QMutex CSyncThread::_mutex;
|
2012-03-21 21:03:49 +04:00
|
|
|
|
|
|
|
int CSyncThread::checkPermissions( TREE_WALK_FILE* file, void *data )
|
2012-03-22 19:22:08 +04:00
|
|
|
{
|
2012-03-21 21:03:49 +04:00
|
|
|
WalkStats *wStats = static_cast<WalkStats*>(data);
|
|
|
|
|
|
|
|
if( !wStats ) {
|
|
|
|
qDebug() << "WalkStats is zero - must not be!";
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
wStats->seenFiles++;
|
|
|
|
|
|
|
|
switch(file->instruction) {
|
|
|
|
case CSYNC_INSTRUCTION_NONE:
|
|
|
|
|
|
|
|
break;
|
|
|
|
case CSYNC_INSTRUCTION_EVAL:
|
|
|
|
wStats->eval++;
|
|
|
|
break;
|
|
|
|
case CSYNC_INSTRUCTION_REMOVE:
|
|
|
|
wStats->removed++;
|
|
|
|
break;
|
|
|
|
case CSYNC_INSTRUCTION_RENAME:
|
|
|
|
wStats->renamed++;
|
|
|
|
break;
|
|
|
|
case CSYNC_INSTRUCTION_NEW:
|
|
|
|
wStats->newFiles++;
|
|
|
|
break;
|
|
|
|
case CSYNC_INSTRUCTION_CONFLICT:
|
|
|
|
wStats->conflicts++;
|
|
|
|
break;
|
|
|
|
case CSYNC_INSTRUCTION_IGNORE:
|
|
|
|
wStats->ignores++;
|
|
|
|
break;
|
|
|
|
case CSYNC_INSTRUCTION_SYNC:
|
|
|
|
wStats->sync++;
|
|
|
|
break;
|
|
|
|
case CSYNC_INSTRUCTION_STAT_ERROR:
|
|
|
|
case CSYNC_INSTRUCTION_ERROR:
|
2012-03-22 19:22:08 +04:00
|
|
|
/* instructions for the propagator */
|
2012-03-21 21:03:49 +04:00
|
|
|
case CSYNC_INSTRUCTION_DELETED:
|
|
|
|
case CSYNC_INSTRUCTION_UPDATED:
|
|
|
|
wStats->error++;
|
2012-03-22 19:22:08 +04:00
|
|
|
wStats->errorType = WALK_ERROR_INSTRUCTIONS;
|
2012-03-21 21:03:49 +04:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
wStats->error++;
|
2012-03-22 19:22:08 +04:00
|
|
|
wStats->errorType = WALK_ERROR_WALK;
|
2012-03-21 21:03:49 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-03-22 19:22:08 +04:00
|
|
|
if( file ) {
|
|
|
|
QString source(wStats->sourcePath);
|
|
|
|
source.append(file->path);
|
|
|
|
QFileInfo fi(source);
|
|
|
|
|
|
|
|
if( fi.isDir()) { // File type directory.
|
2012-03-26 16:38:48 +04:00
|
|
|
if( !(fi.isWritable() && fi.isExecutable()) ) {
|
2012-03-22 19:22:08 +04:00
|
|
|
wStats->errorType = WALK_ERROR_DIR_PERMS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-26 16:38:48 +04:00
|
|
|
// qDebug() << wStats->seenFiles << ". Path: " << file->path << ": uid= " << file->uid << " - type: " << file->type;
|
2012-03-22 19:22:08 +04:00
|
|
|
if( wStats->errorType != WALK_ERROR_NONE ) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2012-03-21 21:03:49 +04:00
|
|
|
|
2012-02-28 19:49:13 +04:00
|
|
|
CSyncThread::CSyncThread(const QString &source, const QString &target, bool localCheckOnly)
|
2012-03-13 20:37:43 +04:00
|
|
|
|
2012-02-15 12:30:37 +04:00
|
|
|
: _source(source)
|
|
|
|
, _target(target)
|
2012-02-28 19:49:13 +04:00
|
|
|
, _localCheckOnly( localCheckOnly )
|
2012-03-21 21:03:49 +04:00
|
|
|
|
2012-02-15 12:30:37 +04:00
|
|
|
{
|
2012-03-22 19:22:08 +04:00
|
|
|
_mutex.lock();
|
|
|
|
if( ! _source.endsWith('/')) _source.append('/');
|
|
|
|
_mutex.unlock();
|
2012-02-15 12:30:37 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
CSyncThread::~CSyncThread()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSyncThread::run()
|
|
|
|
{
|
|
|
|
CSYNC *csync;
|
2012-03-22 19:22:08 +04:00
|
|
|
|
2012-03-21 21:03:49 +04:00
|
|
|
WalkStats *wStats = new WalkStats;
|
2012-03-22 19:22:08 +04:00
|
|
|
QTime walkTime;
|
|
|
|
|
|
|
|
wStats->sourcePath = 0;
|
|
|
|
wStats->errorType = 0;
|
|
|
|
wStats->eval = 0;
|
|
|
|
wStats->removed = 0;
|
|
|
|
wStats->renamed = 0;
|
|
|
|
wStats->newFiles = 0;
|
|
|
|
wStats->ignores = 0;
|
|
|
|
wStats->sync = 0;
|
|
|
|
wStats->seenFiles = 0;
|
|
|
|
wStats->conflicts = 0;
|
|
|
|
wStats->error = 0;
|
2012-02-15 12:30:37 +04:00
|
|
|
|
2012-03-21 21:03:49 +04:00
|
|
|
_mutex.lock();
|
2012-03-22 19:22:08 +04:00
|
|
|
if( csync_create(&csync,
|
2012-02-15 12:30:37 +04:00
|
|
|
_source.toLocal8Bit().data(),
|
2012-03-22 19:22:08 +04:00
|
|
|
_target.toLocal8Bit().data()) < 0 ) {
|
|
|
|
emit csyncError( tr("CSync create failed.") );
|
|
|
|
}
|
|
|
|
// FIXME: Check if we really need this stringcopy!
|
|
|
|
wStats->sourcePath = qstrdup( _source.toLocal8Bit().constData() );
|
2012-04-30 10:56:56 +04:00
|
|
|
_csyncConfigDir = QString::fromLocal8Bit( csync_get_config_dir( csync ));
|
2012-03-21 21:03:49 +04:00
|
|
|
_mutex.unlock();
|
|
|
|
|
2012-02-28 19:49:13 +04:00
|
|
|
qDebug() << "## CSync Thread local only: " << _localCheckOnly;
|
2012-03-13 20:37:43 +04:00
|
|
|
csync_set_auth_callback( csync, getauth );
|
2012-02-15 12:30:37 +04:00
|
|
|
csync_enable_conflictcopys(csync);
|
|
|
|
|
2012-03-28 14:23:34 +04:00
|
|
|
|
2012-03-28 13:26:51 +04:00
|
|
|
MirallConfigFile cfg;
|
2012-03-28 14:23:34 +04:00
|
|
|
QString excludeList = cfg.excludeFile();
|
|
|
|
|
|
|
|
if( !excludeList.isEmpty() ) {
|
2012-03-28 13:26:51 +04:00
|
|
|
qDebug() << "==== added CSync exclude List: " << excludeList.toAscii();
|
|
|
|
csync_add_exclude_list( csync, excludeList.toAscii() );
|
|
|
|
}
|
|
|
|
|
2012-02-28 19:49:13 +04:00
|
|
|
QTime t;
|
|
|
|
t.start();
|
|
|
|
|
2012-03-21 21:03:49 +04:00
|
|
|
_mutex.lock();
|
2012-02-28 19:49:13 +04:00
|
|
|
if( _localCheckOnly ) {
|
2012-03-22 19:22:08 +04:00
|
|
|
csync_set_local_only( csync, true );
|
2012-02-28 19:49:13 +04:00
|
|
|
}
|
2012-03-21 21:03:49 +04:00
|
|
|
_mutex.unlock();
|
2012-02-15 12:30:37 +04:00
|
|
|
|
2012-03-22 19:22:08 +04:00
|
|
|
if( csync_init(csync) < 0 ) {
|
2012-04-17 14:34:19 +04:00
|
|
|
CSYNC_ERROR_CODE err = csync_get_error( csync );
|
2012-03-30 17:34:56 +04:00
|
|
|
QString errStr;
|
|
|
|
|
|
|
|
switch( err ) {
|
|
|
|
case CSYNC_ERR_LOCK:
|
|
|
|
errStr = tr("CSync failed to create a lock file.");
|
|
|
|
break;
|
|
|
|
case CSYNC_ERR_STATEDB_LOAD:
|
|
|
|
errStr = tr("CSync failed to load the state db.");
|
|
|
|
break;
|
|
|
|
case CSYNC_ERR_TIMESKEW:
|
|
|
|
errStr = tr("The system time between the local machine and the server differs "
|
2012-05-21 18:48:49 +04:00
|
|
|
"too much. Please use a time synchronization service (ntp) on both machines.");
|
2012-03-30 17:34:56 +04:00
|
|
|
break;
|
|
|
|
case CSYNC_ERR_FILESYSTEM:
|
|
|
|
errStr = tr("CSync could not detect the filesystem type.");
|
|
|
|
break;
|
|
|
|
case CSYNC_ERR_TREE:
|
|
|
|
errStr = tr("CSync got an error while processing internal trees.");
|
|
|
|
break;
|
2012-04-15 18:49:11 +04:00
|
|
|
case CSYNC_ERR_ACCESS_FAILED:
|
|
|
|
errStr = tr("<p>The target directory %1 does not exist.</p><p>Please create it and try again.</p>").arg(_target);
|
|
|
|
break;
|
2012-04-19 19:34:52 +04:00
|
|
|
case CSYNC_ERR_MODULE:
|
|
|
|
errStr = tr("<p>The ownCloud plugin for csync could not be loaded.<br/>Please verify the installation!</p>");
|
|
|
|
break;
|
2012-04-15 18:49:11 +04:00
|
|
|
case CSYNC_ERR_LOCAL_CREATE:
|
|
|
|
case CSYNC_ERR_LOCAL_STAT:
|
|
|
|
errStr = tr("The local filesystem can not be written. Please check permissions.");
|
|
|
|
break;
|
|
|
|
case CSYNC_ERR_REMOTE_CREATE:
|
|
|
|
case CSYNC_ERR_REMOTE_STAT:
|
|
|
|
errStr = tr("A remote file can not be written. Please check the remote access.");
|
|
|
|
break;
|
2012-03-30 17:34:56 +04:00
|
|
|
default:
|
|
|
|
errStr = tr("An internal error number %1 happend.").arg( (int) err );
|
|
|
|
}
|
|
|
|
qDebug() << " #### ERROR String emitted: " << errStr;
|
|
|
|
emit csyncError(errStr);
|
2012-02-15 12:30:37 +04:00
|
|
|
goto cleanup;
|
2012-03-22 19:22:08 +04:00
|
|
|
}
|
2012-02-15 12:30:37 +04:00
|
|
|
|
2012-02-28 19:49:13 +04:00
|
|
|
qDebug() << "############################################################### >>";
|
2012-03-22 19:22:08 +04:00
|
|
|
if( csync_update(csync) < 0 ) {
|
|
|
|
emit csyncError(tr("CSync Update failed."));
|
2012-02-15 12:30:37 +04:00
|
|
|
goto cleanup;
|
2012-03-22 19:22:08 +04:00
|
|
|
}
|
|
|
|
qDebug() << "<<###############################################################";
|
2012-02-15 12:30:37 +04:00
|
|
|
|
2012-03-21 21:03:49 +04:00
|
|
|
csync_set_userdata(csync, wStats);
|
2012-03-28 13:26:51 +04:00
|
|
|
|
2012-03-22 19:22:08 +04:00
|
|
|
walkTime.start();
|
|
|
|
if( csync_walk_local_tree(csync, &checkPermissions, 0) < 0 ) {
|
|
|
|
qDebug() << "Error in treewalk.";
|
|
|
|
if( wStats->errorType == WALK_ERROR_DIR_PERMS ) {
|
|
|
|
emit csyncError(tr("The local filesystem has directories which are write protected.\n"
|
|
|
|
"That prevents ownCloud from successful syncing.\n"
|
|
|
|
"Please make sure that all directories are writeable."));
|
|
|
|
} else if( wStats->errorType == WALK_ERROR_WALK ) {
|
|
|
|
emit csyncError(tr("CSync encountered an error while examining the file system.\n"
|
|
|
|
"Syncing is not possible."));
|
|
|
|
} else if( wStats->errorType == WALK_ERROR_INSTRUCTIONS ) {
|
|
|
|
emit csyncError(tr("CSync update generated a strange instruction.\n"
|
|
|
|
"Please write a bug report."));
|
|
|
|
}
|
|
|
|
emit csyncError(tr("Local filesystem problems. Better disable Syncing and check."));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
qDebug() << " ..... Local walk finished: " << walkTime.elapsed();
|
2012-02-15 12:30:37 +04:00
|
|
|
|
2012-03-22 19:22:08 +04:00
|
|
|
// emit the treewalk results. Do not touch the wStats after this.
|
2012-03-21 21:03:49 +04:00
|
|
|
emit treeWalkResult(wStats);
|
2012-02-28 19:49:13 +04:00
|
|
|
|
2012-03-21 21:03:49 +04:00
|
|
|
_mutex.lock();
|
2012-02-28 19:49:13 +04:00
|
|
|
if( _localCheckOnly ) {
|
2012-03-21 21:03:49 +04:00
|
|
|
_mutex.unlock();
|
2012-02-28 19:49:13 +04:00
|
|
|
// we have to go out here as its local check only.
|
|
|
|
goto cleanup;
|
|
|
|
} else {
|
2012-03-21 21:03:49 +04:00
|
|
|
_mutex.unlock();
|
|
|
|
// check if we can write all over.
|
|
|
|
|
2012-03-22 19:22:08 +04:00
|
|
|
if( csync_reconcile(csync) < 0 ) {
|
|
|
|
emit csyncError(tr("CSync reconcile failed."));
|
2012-02-28 19:49:13 +04:00
|
|
|
goto cleanup;
|
2012-03-22 19:22:08 +04:00
|
|
|
}
|
|
|
|
if( csync_propagate(csync) < 0 ) {
|
|
|
|
emit csyncError(tr("CSync propagate failed."));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2012-02-28 19:49:13 +04:00
|
|
|
}
|
2012-02-15 12:30:37 +04:00
|
|
|
cleanup:
|
|
|
|
csync_destroy(csync);
|
2012-03-22 19:22:08 +04:00
|
|
|
/*
|
|
|
|
* Attention: do not delete the wStat memory here. it is deleted in the
|
|
|
|
* slot catching the signel treeWalkResult because this thread can faster
|
|
|
|
* die than the slot has read out the data.
|
|
|
|
*/
|
2012-02-28 19:49:13 +04:00
|
|
|
qDebug() << "CSync run took " << t.elapsed() << " Milliseconds";
|
|
|
|
}
|
|
|
|
|
2012-03-13 20:37:43 +04:00
|
|
|
|
|
|
|
void CSyncThread::setUserPwd( const QString& user, const QString& passwd )
|
|
|
|
{
|
2012-03-22 19:22:08 +04:00
|
|
|
_mutex.lock();
|
2012-03-13 20:37:43 +04:00
|
|
|
_user = user;
|
|
|
|
_passwd = passwd;
|
2012-03-22 19:22:08 +04:00
|
|
|
_mutex.unlock();
|
2012-03-13 20:37:43 +04:00
|
|
|
}
|
|
|
|
|
2012-04-30 10:56:56 +04:00
|
|
|
QString CSyncThread::csyncConfigDir()
|
|
|
|
{
|
|
|
|
return _csyncConfigDir;
|
|
|
|
}
|
|
|
|
|
2012-03-13 20:37:43 +04:00
|
|
|
int CSyncThread::getauth(const char *prompt,
|
|
|
|
char *buf,
|
|
|
|
size_t len,
|
|
|
|
int echo,
|
|
|
|
int verify,
|
|
|
|
void *userdata
|
|
|
|
)
|
|
|
|
{
|
|
|
|
QString qPrompt = QString::fromLocal8Bit( prompt ).trimmed();
|
2012-03-22 19:22:08 +04:00
|
|
|
_mutex.lock();
|
2012-03-13 20:37:43 +04:00
|
|
|
|
|
|
|
if( qPrompt == QString::fromLocal8Bit("Enter your username:") ) {
|
2012-03-26 16:38:48 +04:00
|
|
|
// qDebug() << "OOO Username requested!";
|
2012-03-13 20:37:43 +04:00
|
|
|
strncpy( buf, _user.toLocal8Bit().constData(), len );
|
|
|
|
} else if( qPrompt == QString::fromLocal8Bit("Enter your password:") ) {
|
2012-03-26 16:38:48 +04:00
|
|
|
// qDebug() << "OOO Password requested!";
|
2012-03-13 20:37:43 +04:00
|
|
|
strncpy( buf, _passwd.toLocal8Bit().constData(), len );
|
|
|
|
} else {
|
2012-04-12 13:37:48 +04:00
|
|
|
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
|
|
|
|
// the answer is simply yes here.
|
|
|
|
strncpy( buf, "yes", 3 );
|
|
|
|
} else {
|
|
|
|
qDebug() << "Unknown prompt: <" << prompt << ">";
|
|
|
|
}
|
2012-03-13 20:37:43 +04:00
|
|
|
}
|
2012-03-22 19:22:08 +04:00
|
|
|
_mutex.unlock();
|
2012-03-13 20:37:43 +04:00
|
|
|
}
|
|
|
|
|
2012-02-15 12:30:37 +04:00
|
|
|
}
|