Merge pull request #1 from owncloud/master

Merge with owncloud:master
This commit is contained in:
Tom Swartz 2013-11-25 12:29:22 -08:00
commit 738f026c41
70 changed files with 10707 additions and 11140 deletions

View file

@ -351,18 +351,13 @@ SectionEnd
SectionGroup "Shortcuts"
!ifdef OPTION_SECTION_SC_START_MENU
${MementoSection} "Start Menu Program Group" SEC_START_MENU
${MementoSection} "Start Menu Program Shortcut" SEC_START_MENU
SectionIn 1 2 3
SetDetailsPrint textonly
DetailPrint "Adding shortcuts for the ${APPLICATION_NAME} program group to the Start Menu."
DetailPrint "Adding shortcut for ${APPLICATION_NAME} to the Start Menu."
SetDetailsPrint listonly
SetShellVarContext all
RMDir /r "$SMPROGRAMS\${APPLICATION_NAME}"
CreateDirectory "$SMPROGRAMS\${APPLICATION_NAME}"
;CreateShortCut "$SMPROGRAMS\${APPLICATION_NAME}\LICENSE.lnk" "$INSTDIR\LICENSE.txt"
CreateShortCut "$SMPROGRAMS\${APPLICATION_NAME}\${APPLICATION_NAME}.lnk" "$INSTDIR\${APPLICATION_EXECUTABLE}"
;CreateShortCut "$SMPROGRAMS\${APPLICATION_NAME}\Release notes.lnk" "$INSTDIR\NOTES.txt"
CreateShortCut "$SMPROGRAMS\${APPLICATION_NAME}\Uninstall.lnk" "$INSTDIR\uninstall.exe"
CreateShortCut "$SMPROGRAMS\${APPLICATION_NAME}.lnk" "$INSTDIR\${APPLICATION_EXECUTABLE}"
SetShellVarContext current
${MementoSectionEnd}
!endif
@ -395,7 +390,7 @@ ${MementoSectionDone}
;--------------------------------
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_APPLICATION} "${APPLICATION_NAME} essentials."
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_START_MENU} "${APPLICATION_NAME} program group."
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_START_MENU} "${APPLICATION_NAME} shortcut."
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_DESKTOP} "Desktop shortcut for ${APPLICATION_NAME}."
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_QUICK_LAUNCH} "Quick Launch shortcut for ${APPLICATION_NAME}."
!insertmacro MUI_FUNCTION_DESCRIPTION_END
@ -503,7 +498,7 @@ Section Uninstall
;Start menu shortcuts.
!ifdef OPTION_SECTION_SC_START_MENU
SetShellVarContext all
RMDir /r "$SMPROGRAMS\${APPLICATION_NAME}"
Delete "$SMPROGRAMS\${APPLICATION_NAME}.lnk"
SetShellVarContext current
!endif

View file

@ -70,9 +70,10 @@ Next, install the cross-compiler packages and the cross-compiled dependencies::
zypper install cmake make mingw32-cross-binutils mingw32-cross-cpp mingw32-cross-gcc \
mingw32-cross-gcc-c++ mingw32-cross-pkg-config mingw32-filesystem \
mingw32-headers mingw32-runtime site-config \
mingw32-libsqlite-devel mingw32-dlfcn-devel mingw32-libssh2-devel \
kdewin-png2ico mingw32-libqt4 mingw32-libqt4-devel mingw32-libgcrypt \
mingw32-headers mingw32-runtime site-config mingw32-libqt4-sql
mingw32-libqt4-sql-sqlite mingw32-libsqlite-devel \
mingw32-dlfcn-devel mingw32-libssh2-devel kdewin-png2ico \
mingw32-libqt4 mingw32-libqt4-devel mingw32-libgcrypt \
mingw32-libgnutls mingw32-libneon-openssl mingw32-libneon-devel \
mingw32-libbeecrypt mingw32-libopenssl mingw32-openssl \
mingw32-libpng-devel mingw32-libsqlite mingw32-qtkeychain \

View file

@ -343,7 +343,6 @@ else()
install(FILES ${qt_I18N} DESTINATION ${QM_DIR})
file(GLOB qtkeychain_I18N ${QT_TRANSLATIONS_DIR}/qtkeychain*.qm)
install(FILES ${qtkeychain_I18N} DESTINATION ${QM_DIR})
list(APPEND dirs "/usr/local/lib")
endif()
@ -365,7 +364,7 @@ install(TARGETS ${APPLICATION_EXECUTABLE}
# currently it needs to be done because the code right above needs to be executed no matter
# if building a bundle or not and the install_qt4_executable needs to be called afterwards
if(BUILD_OWNCLOUD_OSX_BUNDLE)
install_qt_executable(${OWNCLOUD_OSX_BUNDLE} "qsqlite" "" ${dirs})
install_qt_executable(${OWNCLOUD_OSX_BUNDLE} "qsqlite")
endif()
find_program(KRAZY2_EXECUTABLE krazy2)

View file

@ -19,29 +19,34 @@
#include <csync.h>
class QNetworkAccessManager;
class QNetworkReply;
namespace Mirall
{
class Account;
class AbstractCredentials : public QObject
{
Q_OBJECT
Q_OBJECT
public:
// No need for virtual destructor - QObject already has one.
virtual void syncContextPreInit(CSYNC* ctx) = 0;
virtual void syncContextPreStart(CSYNC* ctx) = 0;
virtual bool changed(AbstractCredentials* credentials) const = 0;
virtual QString authType() const = 0;
virtual QString user() const = 0;
virtual QNetworkAccessManager* getQNAM() const = 0;
virtual bool ready() const = 0;
virtual void fetch(Account *account) = 0;
virtual void persist(Account *account) = 0;
// No need for virtual destructor - QObject already has one.
virtual void syncContextPreInit(CSYNC* ctx) = 0;
virtual void syncContextPreStart(CSYNC* ctx) = 0;
virtual bool changed(AbstractCredentials* credentials) const = 0;
virtual QString authType() const = 0;
virtual QString user() const = 0;
virtual QNetworkAccessManager* getQNAM() const = 0;
virtual bool ready() const = 0;
virtual void fetch(Account *account) = 0;
virtual bool stillValid(QNetworkReply *reply) = 0;
virtual bool fetchFromUser(Account *account) = 0;
virtual void persist(Account *account) = 0;
/** Invalidates auth token, or password for basic auth */
virtual void invalidateToken(Account *account) = 0;
Q_SIGNALS:
void fetched();
void fetched();
};
} // ns Mirall

View file

@ -50,6 +50,18 @@ bool DummyCredentials::ready() const
return true;
}
bool DummyCredentials::stillValid(QNetworkReply *reply)
{
Q_UNUSED(reply)
return true;
}
bool DummyCredentials::fetchFromUser(Account *account)
{
Q_UNUSED(account)
return false;
}
void DummyCredentials::fetch(Account*)
{
Q_EMIT(fetched());

View file

@ -21,18 +21,21 @@ namespace Mirall
class DummyCredentials : public AbstractCredentials
{
Q_OBJECT
Q_OBJECT
public:
void syncContextPreInit(CSYNC* ctx);
void syncContextPreStart(CSYNC* ctx);
bool changed(AbstractCredentials* credentials) const;
QString authType() const;
QString user() const;
QNetworkAccessManager* getQNAM() const;
bool ready() const;
void fetch(Account*);
void persist(Account*);
void syncContextPreInit(CSYNC* ctx);
void syncContextPreStart(CSYNC* ctx);
bool changed(AbstractCredentials* credentials) const;
QString authType() const;
QString user() const;
QNetworkAccessManager* getQNAM() const;
bool ready() const;
bool stillValid(QNetworkReply *reply);
bool fetchFromUser(Account *account);
void fetch(Account*);
void persist(Account*);
void invalidateToken(Account *) {}
};
} // ns Mirall

View file

@ -17,6 +17,7 @@
#include <QDebug>
#include <QNetworkReply>
#include <QSettings>
#include <QInputDialog>
#include <qtkeychain/keychain.h>
@ -29,6 +30,8 @@
using namespace QKeychain;
Q_DECLARE_METATYPE(Mirall::Account*)
namespace Mirall
{
@ -83,6 +86,7 @@ protected:
QByteArray credHash = QByteArray(_cred->user().toUtf8()+":"+_cred->password().toUtf8()).toBase64();
QNetworkRequest req(request);
req.setRawHeader(QByteArray("Authorization"), QByteArray("Basic ") + credHash);
//qDebug() << "Request for " << req.url() << "with authorization" << QByteArray::fromBase64(credHash);
return MirallAccessManager::createRequest(op, req, outgoingData);
}
private:
@ -180,9 +184,28 @@ void HttpCredentials::fetch(Account *account)
job->setInsecureFallback(true);
job->setKey(keychainKey(account->url().toString(), _user));
connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotReadJobDone(QKeychain::Job*)));
job->setProperty("account", QVariant::fromValue(account));
job->start();
}
}
bool HttpCredentials::stillValid(QNetworkReply *reply)
{
return ((reply->error() != QNetworkReply::AuthenticationRequiredError)
// returned if user or password is incorrect
&& (reply->error() != QNetworkReply::OperationCanceledError));
}
bool HttpCredentials::fetchFromUser(Account *account)
{
bool ok = false;
QString password = queryPassword(&ok);
if (ok) {
_password = password;
_ready = true;
persist(account);
}
return ok;
}
void HttpCredentials::slotReadJobDone(QKeychain::Job *job)
{
@ -196,9 +219,43 @@ void HttpCredentials::slotReadJobDone(QKeychain::Job *job)
Q_EMIT fetched();
break;
default:
if (!_user.isEmpty()) {
bool ok;
QString pwd = queryPassword(&ok);
if (ok) {
_password = pwd;
_ready = true;
persist(qvariant_cast<Account*>(readJob->property("account")));
Q_EMIT fetched();
break;
}
}
qDebug() << "Error while reading password" << job->errorString();
}
}
QString HttpCredentials::queryPassword(bool *ok)
{
qDebug() << AccountManager::instance()->account()->state();
if (ok) {
QString str = QInputDialog::getText(0, tr("Enter Password"),
tr("Please enter %1 password for user '%2':")
.arg(Theme::instance()->appNameGUI(), _user),
QLineEdit::Password, QString(), ok);
qDebug() << AccountManager::instance()->account()->state();
return str;
} else {
return QString();
}
}
void HttpCredentials::invalidateToken(Account *account)
{
_password = QString();
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
job->setKey(keychainKey(account->url().toString(), _user));
job->start();
_ready = false;
}
void HttpCredentials::persist(Account *account)

View file

@ -24,7 +24,7 @@ class QNetworkReply;
class QAuthenticator;
namespace QKeychain {
class Job;
class Job;
}
namespace Mirall
@ -32,33 +32,37 @@ namespace Mirall
class HttpCredentials : public AbstractCredentials
{
Q_OBJECT
Q_OBJECT
public:
HttpCredentials();
HttpCredentials(const QString& user, const QString& password);
HttpCredentials();
HttpCredentials(const QString& user, const QString& password);
void syncContextPreInit(CSYNC* ctx);
void syncContextPreStart(CSYNC* ctx);
bool changed(AbstractCredentials* credentials) const;
QString authType() const;
QNetworkAccessManager* getQNAM() const;
bool ready() const;
void fetch(Account *account);
void persist(Account *account);
QString user() const;
QString password() const;
void syncContextPreInit(CSYNC* ctx);
void syncContextPreStart(CSYNC* ctx);
bool changed(AbstractCredentials* credentials) const;
QString authType() const;
QNetworkAccessManager* getQNAM() const;
bool ready() const;
void fetch(Account *account);
bool stillValid(QNetworkReply *reply);
bool fetchFromUser(Account *account);
void persist(Account *account);
QString user() const;
QString password() const;
QString queryPassword(bool *ok);
void invalidateToken(Account *account);
private Q_SLOTS:
void slotAuthentication(QNetworkReply*, QAuthenticator*);
void slotReadJobDone(QKeychain::Job*);
void slotWriteJobDone(QKeychain::Job*);
void slotAuthentication(QNetworkReply*, QAuthenticator*);
void slotReadJobDone(QKeychain::Job*);
void slotWriteJobDone(QKeychain::Job*);
private:
static QString keychainKey(const QString &url, const QString &user);
QString _user;
QString _password;
bool _ready;
static QString keychainKey(const QString &url, const QString &user);
QString _user;
QString _password;
bool _ready;
};
} // ns Mirall

View file

@ -13,14 +13,16 @@
#include <QEventLoop>
#include "mirall/account.h"
#include "creds/shibboleth/shibbolethrefresher.h"
#include "creds/shibbolethcredentials.h"
namespace Mirall
{
ShibbolethRefresher::ShibbolethRefresher(ShibbolethCredentials* creds, CSYNC* csync_ctx, QObject* parent)
ShibbolethRefresher::ShibbolethRefresher(Account *account, ShibbolethCredentials* creds, CSYNC* csync_ctx, QObject* parent)
: QObject(parent),
_account(account),
_creds(creds),
_csync_ctx(csync_ctx)
{}
@ -33,7 +35,8 @@ void ShibbolethRefresher::refresh()
this, SLOT(onInvalidatedAndFetched(QByteArray)));
connect(_creds, SIGNAL(invalidatedAndFetched(QByteArray)),
&loop, SLOT(quit()));
QMetaObject::invokeMethod(_creds, "invalidateAndFetch", Qt::QueuedConnection);
QMetaObject::invokeMethod(_creds, "invalidateAndFetch",Qt::QueuedConnection,
Q_ARG(Account*, _account));
loop.exec();
disconnect(_creds, SIGNAL(invalidatedAndFetched(QByteArray)),
&loop, SLOT(quit()));

View file

@ -23,6 +23,7 @@ class QByteArray;
namespace Mirall
{
class Account;
class ShibbolethCredentials;
class ShibbolethRefresher : public QObject
@ -30,7 +31,7 @@ class ShibbolethRefresher : public QObject
Q_OBJECT
public:
ShibbolethRefresher(ShibbolethCredentials* creds, CSYNC* csync_ctx, QObject* parent = 0);
ShibbolethRefresher(Account *account, ShibbolethCredentials* creds, CSYNC* csync_ctx, QObject* parent = 0);
void refresh();
@ -38,6 +39,7 @@ private Q_SLOTS:
void onInvalidatedAndFetched(const QByteArray& cookieData);
private:
Account* _account;
ShibbolethCredentials* _creds;
CSYNC* _csync_ctx;
};

View file

@ -46,7 +46,8 @@ int shibboleth_redirect_callback(CSYNC* csync_ctx,
QMutex mutex;
QMutexLocker locker(&mutex);
ShibbolethCredentials* creds = qobject_cast<ShibbolethCredentials*>(AccountManager::instance()->account()->credentials());
Account *account = AccountManager::instance()->account();
ShibbolethCredentials* creds = qobject_cast<ShibbolethCredentials*>(account->credentials());
if (!creds) {
@ -54,7 +55,7 @@ int shibboleth_redirect_callback(CSYNC* csync_ctx,
return 1;
}
ShibbolethRefresher refresher(creds, csync_ctx);
ShibbolethRefresher refresher(account, creds, csync_ctx);
// blocks
refresher.refresh();
@ -191,6 +192,18 @@ void ShibbolethCredentials::fetch(Account *account)
}
}
bool ShibbolethCredentials::stillValid(QNetworkReply *reply)
{
Q_UNUSED(reply)
return true;
}
bool ShibbolethCredentials::fetchFromUser(Account *account)
{
Q_UNUSED(account)
return false;
}
void ShibbolethCredentials::persist(Account* /*account*/)
{
ShibbolethConfigFile cfg;
@ -198,6 +211,14 @@ void ShibbolethCredentials::persist(Account* /*account*/)
cfg.storeCookies(_otherCookies);
}
void ShibbolethCredentials::invalidateToken(Account *account)
{
Q_UNUSED(account)
_shibCookie.setValue("");
// ### access to ctx missing, but might not be required at all
//csync_set_module_property(ctx, "session_key", "");
}
void ShibbolethCredentials::disposeBrowser()
{
disconnect(_browser, SIGNAL(viewHidden()),
@ -226,7 +247,7 @@ void ShibbolethCredentials::slotBrowserHidden()
Q_EMIT fetched();
}
void ShibbolethCredentials::invalidateAndFetch()
void ShibbolethCredentials::invalidateAndFetch(Account* account)
{
_ready = false;
connect (this, SIGNAL(fetched()),
@ -234,7 +255,7 @@ void ShibbolethCredentials::invalidateAndFetch()
// small hack to support the ShibbolethRefresher hack
// we already rand fetch() with a valid account object,
// and hence know the url on refresh
fetch(0);
fetch(account);
}
void ShibbolethCredentials::onFetched()

View file

@ -42,12 +42,15 @@ public:
QNetworkAccessManager* getQNAM() const;
bool ready() const;
void fetch(Account *account);
bool stillValid(QNetworkReply *reply);
virtual bool fetchFromUser(Account *account);
void persist(Account *account);
void invalidateToken(Account *account);
QNetworkCookie cookie() const;
public Q_SLOTS:
void invalidateAndFetch();
void invalidateAndFetch(Account *account);
private Q_SLOTS:
void onShibbolethCookieReceived(const QNetworkCookie& cookie);

View file

@ -40,12 +40,10 @@ AccountManager *AccountManager::instance()
static QMutex mutex;
if (!_instance)
{
mutex.lock();
QMutexLocker lock(&mutex);
if (!_instance) {
_instance = new AccountManager;
}
mutex.unlock();
}
return _instance;
@ -65,7 +63,7 @@ Account::Account(AbstractSslErrorHandler *sslErrorHandler, QObject *parent)
, _am(0)
, _credentials(0)
, _treatSslErrorsAsFailure(false)
, _isOnline(false)
, _state(Account::Disconnected)
{
}
@ -285,17 +283,17 @@ void Account::setCredentialSetting(const QString &key, const QVariant &value)
}
}
bool Account::isOnline() const
int Account::state() const
{
return _isOnline;
return _state;
}
void Account::setOnline(bool online)
void Account::setState(int state)
{
if (_isOnline != online) {
emit onlineStateChanged(online);
if (_state != state) {
_state = state;
emit stateChanged(state);
}
_isOnline = online;
}
void Account::slotHandleErrors(QNetworkReply *reply , QList<QSslError> errors)

View file

@ -64,6 +64,11 @@ public:
class Account : public QObject {
Q_OBJECT
public:
enum State { Connected = 0, /// account is online
Disconnected = 1, /// no network connection
SignedOut = 2 /// Disconnected + credential token has been discarded
};
static QString davPath() { return "remote.php/webdav/"; }
Account(AbstractSslErrorHandler *sslErrorHandler = 0, QObject *parent = 0);
@ -133,10 +138,10 @@ public:
QVariant credentialSetting(const QString& key) const;
void setCredentialSetting(const QString& key, const QVariant &value);
bool isOnline() const;
void setOnline(bool online);
int state() const;
void setState(int state);
signals:
void onlineStateChanged(bool online);
void stateChanged(int state);
protected Q_SLOTS:
void slotHandleErrors(QNetworkReply*,QList<QSslError>);
@ -150,7 +155,7 @@ private:
QNetworkAccessManager *_am;
AbstractCredentials* _credentials;
bool _treatSslErrorsAsFailure;
bool _isOnline;
int _state;
static QString _configFileName;
};

View file

@ -104,8 +104,8 @@ AccountSettings::AccountSettings(QWidget *parent) :
ui->connectLabel->setText(tr("No account configured."));
ui->_buttonAdd->setEnabled(false);
if (_account) {
connect(_account, SIGNAL(onlineStateChanged(bool)), SLOT(slotOnlineStateChanged(bool)));
slotOnlineStateChanged(_account->isOnline());
connect(_account, SIGNAL(stateChanged(int)), SLOT(slotAccountStateChanged(int)));
slotAccountStateChanged(_account->state());
}
setFolderList(FolderMan::instance()->map());
@ -115,7 +115,16 @@ void AccountSettings::slotFolderActivated( const QModelIndex& indx )
{
bool state = indx.isValid();
ui->_buttonRemove->setEnabled( state );
bool haveFolders = ui->_folderList->model()->rowCount() > 0;
ui->_buttonRemove->setEnabled(state);
if( Theme::instance()->singleSyncFolder() ) {
// only one folder synced folder allowed.
ui->_buttonAdd->setVisible(!haveFolders);
} else {
ui->_buttonAdd->setVisible(true);
ui->_buttonAdd->setEnabled( state );
}
ui->_buttonEnable->setEnabled( state );
ui->_buttonInfo->setEnabled( state );
@ -168,7 +177,7 @@ void AccountSettings::slotFolderWizardAccepted()
folderMan->slotScheduleAllFolders();
emit folderChanged();
}
buttonsSetEnabled();
slotButtonsSetEnabled();
}
void AccountSettings::slotFolderWizardRejected()
@ -193,30 +202,18 @@ void AccountSettings::slotAddFolder( Folder *folder )
folderToModelItem( item, folder );
_model->appendRow( item );
// in order to update the enabled state of the "Sync now" button
connect(folder, SIGNAL(syncStateChange()), this, SLOT(buttonsSetEnabled()), Qt::UniqueConnection);
connect(folder, SIGNAL(syncStateChange()), this, SLOT(slotButtonsSetEnabled()), Qt::UniqueConnection);
}
void AccountSettings::buttonsSetEnabled()
void AccountSettings::slotButtonsSetEnabled()
{
bool haveFolders = ui->_folderList->model()->rowCount() > 0;
ui->_buttonRemove->setEnabled(false);
if( Theme::instance()->singleSyncFolder() ) {
// only one folder synced folder allowed.
ui->_buttonAdd->setVisible(!haveFolders);
} else {
ui->_buttonAdd->setVisible(true);
ui->_buttonAdd->setEnabled(true);
}
QModelIndex selected = ui->_folderList->currentIndex();
bool isSelected = selected.isValid();
ui->_buttonEnable->setEnabled(isSelected);
ui->_buttonRemove->setEnabled(isSelected);
ui->_buttonInfo->setEnabled(isSelected);
if (isSelected) {
slotFolderActivated(selected);
}
}
void AccountSettings::setGeneralErrors( const QStringList& errors )
@ -248,7 +245,11 @@ void AccountSettings::folderToModelItem( QStandardItem *item, Folder *f )
} // we keep the previous icon for the SyncPrepare state.
} else {
// kepp the previous icon for the prepare phase.
item->setData( theme->syncStateIcon( status ), FolderStatusDelegate::FolderStatusIconRole );
if( status == SyncResult::Problem) {
item->setData( theme->syncStateIcon( SyncResult::Success), FolderStatusDelegate::FolderStatusIconRole );
} else {
item->setData( theme->syncStateIcon( status ), FolderStatusDelegate::FolderStatusIconRole );
}
}
} else {
item->setData( theme->folderDisabledIcon( ), FolderStatusDelegate::FolderStatusIconRole ); // size 48 before
@ -379,7 +380,7 @@ void AccountSettings::setFolderList( const Folder::Map &folders )
if (idx.isValid()) {
ui->_folderList->setCurrentIndex(idx);
}
buttonsSetEnabled();
slotButtonsSetEnabled();
}
@ -672,7 +673,9 @@ void AccountSettings::slotSetProgress(const QString& folder, const Progress::Inf
case Progress::Download:
case Progress::Upload:
case Progress::Inactive:
case Progress::Error:
case Progress::SoftError:
case Progress::NormalError:
case Progress::FatalError:
break;
}
@ -771,13 +774,13 @@ void AccountSettings::slotIgnoreFilesEditor()
}
}
void AccountSettings::slotOnlineStateChanged(bool online)
void AccountSettings::slotAccountStateChanged(int state)
{
if (_account) {
QUrl safeUrl(_account->url());
safeUrl.setPassword(QString()); // Remove the password from the URL to avoid showing it in the UI
ui->_buttonAdd->setEnabled(online);
if (online) {
ui->_buttonAdd->setEnabled(state == Account::Connected);
if (state == Account::Connected) {
QString user;
if (AbstractCredentials *cred = _account->credentials()) {
user = cred->user();

View file

@ -49,7 +49,6 @@ public:
~AccountSettings();
void setFolderList( const Folder::Map& );
void buttonsSetEnabled();
signals:
void folderChanged();
@ -65,10 +64,11 @@ public slots:
void slotFolderOpenAction( const QString& );
void slotSetProgress(const QString&, const Progress::Info& progress);
void slotProgressProblem(const QString& folder, const Progress::SyncProblem& problem);
void slotButtonsSetEnabled();
void slotUpdateQuota( qint64,qint64 );
void slotIgnoreFilesEditor();
void slotOnlineStateChanged(bool online = true);
void slotAccountStateChanged(int state);
void setGeneralErrors( const QStringList& errors );

View file

@ -111,6 +111,7 @@ Application::Application(int &argc, char **argv) :
qRegisterMetaType<Progress::Kind>("Progress::Kind");
qRegisterMetaType<Progress::Info>("Progress::Info");
qRegisterMetaType<Progress::SyncProblem>("Progress::SyncProblem");
MirallConfigFile cfg;
_theme->setSystrayUseMonoIcons(cfg.monoIcons());
@ -124,6 +125,11 @@ Application::Application(int &argc, char **argv) :
_gui->slotToggleLogBrowser(); // _showLogWindow is set in parseOptions.
}
connect( _gui, SIGNAL(setupProxy()), SLOT(slotSetupProxy()));
if (account) {
connect(account, SIGNAL(stateChanged(int)), _gui, SLOT(slotAccountStateChanged()));
}
connect(AccountManager::instance(), SIGNAL(accountChanged(Account*,Account*)),
this, SLOT(slotAccountChanged(Account*,Account*)));
// startup procedure.
QTimer::singleShot( 0, this, SLOT( slotCheckConnection() ));
@ -135,6 +141,7 @@ Application::Application(int &argc, char **argv) :
connect (this, SIGNAL(aboutToQuit()), SLOT(slotCleanup()));
_socketApi = new SocketApi(this, cfg.configPathWithAppName().append(QLatin1String("socket")));
}
Application::~Application()
@ -142,6 +149,39 @@ Application::~Application()
// qDebug() << "* Mirall shutdown";
}
void Application::slotLogin()
{
Account *a = AccountManager::instance()->account();
if (a) {
FolderMan::instance()->setupFolders();
slotCheckConnection();
}
}
void Application::slotLogout()
{
Account *a = AccountManager::instance()->account();
if (a) {
// invalidate & forget token/password
a->credentials()->invalidateToken(a);
// terminate all syncs and unload folders
FolderMan *folderMan = FolderMan::instance();
folderMan->setSyncEnabled(false);
folderMan->terminateSyncProcess();
folderMan->unloadAllFolders();
a->setState(Account::SignedOut);
// show result
_gui->slotComputeOverallSyncStatus();
}
}
void Application::slotAccountChanged(Account *newAccount, Account *oldAccount)
{
disconnect(oldAccount, SIGNAL(stateChanged(int)), _gui, SLOT(slotOnlineStateChanged()));
connect(newAccount, SIGNAL(stateChanged(int)), _gui, SLOT(slotOnlineStateChanged()));
}
void Application::slotCleanup()
{
// explicitly close windows. This is somewhat of a hack to ensure

View file

@ -73,7 +73,11 @@ protected slots:
void slotSetupProxy();
void slotUseMonoIconsChanged( bool );
void slotCredentialsFetched();
void slotLogin();
void slotLogout();
void slotCleanup();
void slotAccountChanged(Account *newAccount, Account *oldAccount);
private:
void setHelp();
void runValidator();

View file

@ -82,6 +82,7 @@ void ConnectionValidator::checkConnection()
{
if( _account ) {
CheckServerJob *checkJob = new CheckServerJob(_account, false, this);
checkJob->setIgnoreCredentialFailure(true);
connect(checkJob, SIGNAL(instanceFound(QUrl,QVariantMap)), SLOT(slotStatusFound(QUrl,QVariantMap)));
connect(checkJob, SIGNAL(networkError(QNetworkReply*)), SLOT(slotNoStatusFound(QNetworkReply*)));
checkJob->start();
@ -113,7 +114,7 @@ void ConnectionValidator::slotStatusFound(const QUrl&url, const QVariantMap &inf
// status.php could not be loaded.
void ConnectionValidator::slotNoStatusFound(QNetworkReply *reply)
{
_account->setOnline(false);
_account->setState(false);
// ### TODO
_errors.append(tr("Unable to connect to %1").arg(_account->url().toString()));
@ -127,11 +128,12 @@ void ConnectionValidator::slotCheckAuthentication()
{
// simply GET the webdav root, will fail if credentials are wrong.
// continue in slotAuthCheck here :-)
PropfindJob *propFind = new PropfindJob(_account, "/", this);
propFind->setProperties(QList<QByteArray>() << "getlastmodified");
connect(propFind, SIGNAL(result(QVariantMap)), SLOT(slotAuthSuccess()));
connect(propFind, SIGNAL(networkError(QNetworkReply*)), SLOT(slotAuthFailed(QNetworkReply*)));
propFind->start();
PropfindJob *job = new PropfindJob(_account, "/", this);
job->setIgnoreCredentialFailure(true);
job->setProperties(QList<QByteArray>() << "getlastmodified");
connect(job, SIGNAL(result(QVariantMap)), SLOT(slotAuthSuccess()));
connect(job, SIGNAL(networkError(QNetworkReply*)), SLOT(slotAuthFailed(QNetworkReply*)));
job->start();
qDebug() << "# checking for authentication settings.";
}
@ -140,11 +142,19 @@ void ConnectionValidator::slotAuthFailed(QNetworkReply *reply)
Status stat = StatusNotFound;
if( reply->error() == QNetworkReply::AuthenticationRequiredError ||
reply->error() == QNetworkReply::OperationCanceledError ) { // returned if the user is wrong.
reply->error() == QNetworkReply::OperationCanceledError ) { // returned if the user/pwd is wrong.
qDebug() << reply->error() << reply->errorString();
qDebug() << "******** Password is wrong!";
_errors << tr("The provided credentials are not correct");
stat = CredentialsWrong;
_account->setOnline(false);
switch (_account->state()) {
case Account::SignedOut:
_account->setState(Account::SignedOut);
break;
default:
_account->setState(Account::Disconnected);
}
} else if( reply->error() != QNetworkReply::NoError ) {
_errors << reply->errorString();
}
@ -154,7 +164,7 @@ void ConnectionValidator::slotAuthFailed(QNetworkReply *reply)
void ConnectionValidator::slotAuthSuccess()
{
_account->setOnline(true);
_account->setState(Account::Connected);
emit connectionResult(Connected);
}

View file

@ -225,6 +225,8 @@ bool CSyncThread::checkBlacklisting( SyncFileItem *item )
qDebug() << "Item is on blacklist: " << entry._file << "retries:" << entry._retryCount;
item->_blacklistedInDb = true;
item->_instruction = CSYNC_INSTRUCTION_IGNORE;
item->_errorString = tr("The item is not synced because it is on the blacklist.");
slotProgressProblem( Progress::SoftError, *item );
}
}
@ -316,6 +318,8 @@ int CSyncThread::treewalkFile( TREE_WALK_FILE *file, bool remote )
case CSYNC_INSTRUCTION_CONFLICT:
case CSYNC_INSTRUCTION_IGNORE:
case CSYNC_INSTRUCTION_ERROR:
//
slotProgressProblem(Progress::SoftError, item );
dir = SyncFileItem::None;
break;
case CSYNC_INSTRUCTION_EVAL:
@ -349,7 +353,7 @@ int CSyncThread::treewalkFile( TREE_WALK_FILE *file, bool remote )
}
void CSyncThread::handleSyncError(CSYNC *ctx, const char *state) {
CSYNC_STATUS err = CSYNC_STATUS(csync_get_status( ctx ));
CSYNC_STATUS err = csync_get_status( ctx );
const char *errMsg = csync_get_status_string( ctx );
QString errStr = csyncErrorToString(err);
if( errMsg ) {
@ -357,7 +361,9 @@ void CSyncThread::handleSyncError(CSYNC *ctx, const char *state) {
}
qDebug() << " #### ERROR during "<< state << ": " << errStr;
if( CSYNC_STATUS_IS_EQUAL( err, CSYNC_STATUS_SERVICE_UNAVAILABLE ) ||
if( CSYNC_STATUS_IS_EQUAL( err, CSYNC_STATUS_ABORTED) ) {
qDebug() << "Update phase was aborted by user!";
} else if( CSYNC_STATUS_IS_EQUAL( err, CSYNC_STATUS_SERVICE_UNAVAILABLE ) ||
CSYNC_STATUS_IS_EQUAL( err, CSYNC_STATUS_CONNECT_ERROR )) {
emit csyncUnavailable();
} else {
@ -472,6 +478,8 @@ void CSyncThread::startSync()
return;
}
slotProgress(Progress::StartSync, SyncFileItem(), 0, 0);
_progressInfo = Progress::Info();
_hasFiles = false;
@ -516,8 +524,8 @@ void CSyncThread::startSync()
_journal, &_abortRequested));
connect(_propagator.data(), SIGNAL(completed(SyncFileItem)),
this, SLOT(transferCompleted(SyncFileItem)), Qt::QueuedConnection);
connect(_propagator.data(), SIGNAL(progress(Progress::Kind,QString,quint64,quint64)),
this, SLOT(slotProgress(Progress::Kind,QString,quint64,quint64)));
connect(_propagator.data(), SIGNAL(progress(Progress::Kind,SyncFileItem,quint64,quint64)),
this, SLOT(slotProgress(Progress::Kind,SyncFileItem,quint64,quint64)));
connect(_propagator.data(), SIGNAL(finished()), this, SLOT(slotFinished()));
int downloadLimit = 0;
@ -535,7 +543,6 @@ void CSyncThread::startSync()
}
_propagator->_uploadLimit = uploadLimit;
slotProgress(Progress::StartSync, QString(), 0, 0);
_propagator->start(_syncedItems);
}
@ -549,6 +556,9 @@ void CSyncThread::transferCompleted(const SyncFileItem &item)
_syncedItems[idx]._instruction = item._instruction;
_syncedItems[idx]._errorString = item._errorString;
_syncedItems[idx]._status = item._status;
} else {
qWarning() << Q_FUNC_INFO << "Could not find index in synced items!";
}
if (item._status == SyncFileItem::FatalError) {
@ -562,34 +572,59 @@ void CSyncThread::slotFinished()
if( ! _journal->postSyncCleanup( _seenFiles ) ) {
qDebug() << "Cleaning of synced ";
}
_journal->commit();
_journal->commit("All Finished.", false);
emit treeWalkResult(_syncedItems);
csync_commit(_csync_ctx);
qDebug() << "CSync run took " << _syncTime.elapsed() << " Milliseconds";
slotProgress(Progress::EndSync,QString(), 0 , 0);
slotProgress(Progress::EndSync,SyncFileItem(), 0 , 0);
emit finished();
_propagator.reset(0);
_syncMutex.unlock();
thread()->quit();
}
void CSyncThread::slotProgress(Progress::Kind kind, const QString &file, quint64 curr, quint64 total)
void CSyncThread::slotProgressProblem(Progress::Kind kind, const SyncFileItem& item)
{
Progress::SyncProblem problem;
problem.kind = kind;
problem.current_file = item._file;
problem.error_message = item._errorString;
problem.error_code = item._httpErrorCode;
problem.timestamp = QDateTime::currentDateTime();
// connected to something in folder.
emit transmissionProblem( problem );
}
void CSyncThread::slotProgress(Progress::Kind kind, const SyncFileItem& item, quint64 curr, quint64 total)
{
if( kind == Progress::StartSync ) {
QMutexLocker lock(&_mutex);
_currentFileNo = 0;
}
if( kind == Progress::StartDelete ||
kind == Progress::StartDownload ||
kind == Progress::StartRename ||
kind == Progress::StartUpload ) {
QMutexLocker lock(&_mutex);
_currentFileNo += 1;
}
Progress::Info pInfo = _progressInfo;
pInfo.kind = kind;
pInfo.current_file = file;
pInfo.current_file = item._file;
pInfo.file_size = total;
pInfo.current_file_bytes = curr;
pInfo.current_file_no = _currentFileNo;
pInfo.overall_current_bytes += curr;
pInfo.timestamp = QDateTime::currentDateTime();
// Connect to something in folder!
transmissionProgress( pInfo );
emit transmissionProgress( pInfo );
}
/* Given a path on the remote, give the path as it is when the rename is done */

View file

@ -68,6 +68,8 @@ signals:
void treeWalkResult(const SyncFileItemVector&);
void transmissionProgress( const Progress::Info& progress );
void transmissionProblem( const Progress::SyncProblem& problem );
void csyncStateDbFile( const QString& );
void wipeDb();
@ -79,7 +81,8 @@ signals:
private slots:
void transferCompleted(const SyncFileItem& item);
void slotFinished();
void slotProgress(Progress::Kind kind, const QString& file, quint64, quint64);
void slotProgress(Progress::Kind kind, const SyncFileItem &item, quint64, quint64);
void slotProgressProblem(Progress::Kind kind, const SyncFileItem& item);
private:
void handleSyncError(CSYNC *ctx, const char *state);
@ -112,6 +115,7 @@ private:
Progress::Info _progressInfo;
int _downloadLimit;
int _uploadLimit;
int _currentFileNo;
QAtomicInt _abortRequested;

View file

@ -299,7 +299,7 @@ void Folder::etagRetreived(const QString& etag)
void Folder::slotNetworkUnavailable()
{
AccountManager::instance()->account()->setOnline(false);
AccountManager::instance()->account()->setState(Account::Disconnected);
_syncResult.setStatus(SyncResult::Unavailable);
emit syncStateChange();
}
@ -452,8 +452,10 @@ void Folder::slotTerminateSync(bool block)
if( _thread && _csync ) {
_csync->abort();
_errors.append( tr("The CSync thread terminated.") );
_csyncError = true;
// Do not display an error message, user knows his own actions.
// _errors.append( tr("The CSync thread terminated.") );
// _csyncError = true;
if (!block) {
setSyncState(SyncResult::SyncAbortRequested);
return;
@ -625,6 +627,7 @@ void Folder::startSync(const QStringList &pathList)
connect(_csync, SIGNAL(aboutToRemoveAllFiles(SyncFileItem::Direction,bool*)),
SLOT(slotAboutToRemoveAllFiles(SyncFileItem::Direction,bool*)), Qt::BlockingQueuedConnection);
connect(_csync, SIGNAL(transmissionProgress(Progress::Info)), this, SLOT(slotTransmissionProgress(Progress::Info)));
connect(_csync, SIGNAL(transmissionProblem(Progress::SyncProblem)), this, SLOT(slotTransmissionProblem(Progress::SyncProblem)));
_thread->start();
_thread->setPriority(QThread::LowPriority);
@ -657,7 +660,7 @@ void Folder::slotCsyncUnavailable()
void Folder::slotCSyncFinished()
{
qDebug() << "-> CSync Finished slot with error " << _csyncError;
qDebug() << "-> CSync Finished slot with error " << _csyncError << "warn count" << _syncResult.warnCount();
_watcher->setEventsEnabledDelayed(2000);
_pollTimer.start();
_timeSinceLastSync.restart();
@ -685,6 +688,32 @@ void Folder::slotCSyncFinished()
emit syncFinished( _syncResult );
}
// the problem comes without a folder and the valid path set. Add that here
// and hand the result over to the progress dispatcher.
void Folder::slotTransmissionProblem( const Progress::SyncProblem& problem )
{
Progress::SyncProblem newProb = problem;
newProb.folder = alias();
if(newProb.current_file.startsWith(QLatin1String("ownclouds://")) ||
newProb.current_file.startsWith(QLatin1String("owncloud://")) ) {
// rip off the whole ownCloud URL.
newProb.current_file.remove(Utility::toCSyncScheme(remoteUrl().toString()));
}
QString localPath = path();
if( newProb.current_file.startsWith(localPath) ) {
// remove the local dir.
newProb.current_file = newProb.current_file.right( newProb.current_file.length() - localPath.length());
}
// Count all error conditions.
_syncResult.setWarnCount( _syncResult.warnCount()+1 );
ProgressDispatcher::instance()->setProgressProblem(alias(), newProb);
}
// the progress comes without a folder and the valid path set. Add that here
// and hand the result over to the progress dispatcher.
void Folder::slotTransmissionProgress(const Progress::Info& progress)
{
Progress::Info newInfo = progress;
@ -705,9 +734,6 @@ void Folder::slotTransmissionProgress(const Progress::Info& progress)
if( newInfo.kind == Progress::StartSync ) {
_syncResult.setWarnCount(0);
}
if( newInfo.kind == Progress::Error ) {
_syncResult.setWarnCount( _syncResult.warnCount()+1 );
}
ProgressDispatcher::instance()->setProgressInfo(alias(), newInfo);
}

View file

@ -174,6 +174,7 @@ private slots:
void slotCSyncFinished();
void slotTransmissionProgress(const Progress::Info& progress);
void slotTransmissionProblem( const Progress::SyncProblem& problem );
void slotPollTimerTimeout();
void etagRetreived(const QString &);

View file

@ -23,7 +23,7 @@
#include <QStringList>
#include <QStack>
#include <QTimer>
#include <QMutex>
#include <QDebug>
#include "json.h"
@ -32,11 +32,13 @@
#include "mirall/account.h"
#include "creds/credentialsfactory.h"
#include "creds/abstractcredentials.h"
namespace Mirall {
AbstractNetworkJob::AbstractNetworkJob(Account *account, const QString &path, QObject *parent)
: QObject(parent)
, _ignoreCredentialFailure(false)
, _reply(0)
, _account(account)
, _path(path)
@ -70,6 +72,11 @@ void AbstractNetworkJob::resetTimeout()
_timer->start(interval);
}
void AbstractNetworkJob::setIgnoreCredentialFailure(bool ignore)
{
_ignoreCredentialFailure = ignore;
}
void AbstractNetworkJob::setAccount(Account *account)
{
_account = account;
@ -80,14 +87,10 @@ void AbstractNetworkJob::setPath(const QString &path)
_path = path;
}
void AbstractNetworkJob::slotError(QNetworkReply::NetworkError error)
void AbstractNetworkJob::slotError(QNetworkReply::NetworkError)
{
if (error == QNetworkReply::ContentAccessDenied) {
// ### ask for password, retry job, needs refactoring to use start()
}
qDebug() << metaObject()->className() << "Error:" << _reply->errorString();
emit networkError(_reply);
deleteLater();
}
void AbstractNetworkJob::setupConnections(QNetworkReply *reply)
@ -128,10 +131,38 @@ QNetworkReply *AbstractNetworkJob::headRequest(const QUrl &url)
return _account->headRequest(url);
}
void AbstractNetworkJob::slotFinished()
{
static QMutex mutex;
AbstractCredentials *creds = _account->credentials();
if (creds->stillValid(_reply) || _ignoreCredentialFailure) {
finished();
} else {
// If other jobs that still were created from before
// the account was put offline by the code below,
// we do want them to fail silently while we
// query the user
if (mutex.tryLock()) {
Account *a = account();
bool fetched = creds->fetchFromUser(a);
if (fetched) {
a->setState(Account::Connected);
}
mutex.unlock();
}
}
deleteLater();
}
AbstractNetworkJob::~AbstractNetworkJob() {
_reply->deleteLater();
}
void AbstractNetworkJob::start()
{
qDebug() << "!!!" << metaObject()->className() << "created for" << account()->url() << "querying" << path();
}
/*********************************************************************************************/
RequestEtagJob::RequestEtagJob(Account *account, const QString &path, QObject *parent)
@ -167,9 +198,10 @@ void RequestEtagJob::start()
if( reply()->error() != QNetworkReply::NoError ) {
qDebug() << "getting etag: request network error: " << reply()->errorString();
}
AbstractNetworkJob::start();
}
void RequestEtagJob::slotFinished()
void RequestEtagJob::finished()
{
if (reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 207) {
// Parse DAV response
@ -188,7 +220,6 @@ void RequestEtagJob::slotFinished()
}
emit etagRetreived(etag);
}
deleteLater();
}
/*********************************************************************************************/
@ -204,12 +235,12 @@ void MkColJob::start()
QNetworkReply *reply = davRequest("MKCOL", path());
setReply(reply);
setupConnections(reply);
AbstractNetworkJob::start();
}
void MkColJob::slotFinished()
void MkColJob::finished()
{
emit finished(reply()->error());
deleteLater();
}
/*********************************************************************************************/
@ -236,9 +267,10 @@ void LsColJob::start()
buf->setParent(reply);
setReply(reply);
setupConnections(reply);
AbstractNetworkJob::start();
}
void LsColJob::slotFinished()
void LsColJob::finished()
{
if (reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 207) {
// Parse DAV response
@ -264,8 +296,6 @@ void LsColJob::slotFinished()
}
emit directoryListing(folders);
}
deleteLater();
}
/*********************************************************************************************/
@ -281,6 +311,7 @@ void CheckServerJob::start()
{
setReply(getRequest(path()));
setupConnections(reply());
AbstractNetworkJob::start();
}
void CheckServerJob::slotTimeout()
@ -305,7 +336,7 @@ bool CheckServerJob::installed(const QVariantMap &info)
return info.value(QLatin1String("installed")).toBool();
}
void CheckServerJob::slotFinished()
void CheckServerJob::finished()
{
account()->setCertificateChain(reply()->sslConfiguration().peerCertificateChain());
@ -343,7 +374,6 @@ void CheckServerJob::slotFinished()
} else {
qDebug() << "No proper answer on " << requestedUrl;
}
deleteLater();
}
/*********************************************************************************************/
@ -380,6 +410,7 @@ void PropfindJob::start()
setReply(davRequest("PROPFIND", path(), req, buf));
buf->setParent(reply());
setupConnections(reply());
AbstractNetworkJob::start();
}
void PropfindJob::setProperties(QList<QByteArray> properties)
@ -392,7 +423,7 @@ QList<QByteArray> PropfindJob::properties() const
return _properties;
}
void PropfindJob::slotFinished()
void PropfindJob::finished()
{
int http_result_code = reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
@ -426,8 +457,6 @@ void PropfindJob::slotFinished()
} else {
qDebug() << "Quota request *not* successful, http result code is " << http_result_code;
}
deleteLater();
}
/*********************************************************************************************/
@ -441,9 +470,10 @@ void EntityExistsJob::start()
{
setReply(headRequest(path()));
setupConnections(reply());
AbstractNetworkJob::start();
}
void EntityExistsJob::slotFinished()
void EntityExistsJob::finished()
{
emit exists(reply());
}
@ -473,9 +503,10 @@ void CheckQuotaJob::start()
setReply(davRequest("PROPFIND", path(), req, buf));
buf->setParent(reply());
setupConnections(reply());
AbstractNetworkJob::start();
}
void CheckQuotaJob::slotFinished()
void CheckQuotaJob::finished()
{
if (reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 207) {
// Parse DAV response
@ -498,7 +529,6 @@ void CheckQuotaJob::slotFinished()
qint64 total = quotaUsedBytes + quotaAvailableBytes;
emit quotaRetrieved(total, quotaUsedBytes);
}
deleteLater();
}
} // namespace Mirall

View file

@ -37,7 +37,7 @@ public:
explicit AbstractNetworkJob(Account *account, const QString &path, QObject* parent = 0);
virtual ~AbstractNetworkJob();
virtual void start() = 0;
virtual void start();
void setAccount(Account *account);
Account* account() const { return _account; }
@ -50,6 +50,9 @@ public:
void setTimeout(qint64 msec);
void resetTimeout();
void setIgnoreCredentialFailure(bool ignore);
bool ignoreCredentialFailure() const { return _ignoreCredentialFailure; }
signals:
void networkError(QNetworkReply *reply);
protected:
@ -66,13 +69,15 @@ protected:
QNetworkReply* headRequest(const QUrl &url);
int maxRedirects() const { return 10; }
virtual void finished() = 0;
private slots:
virtual void slotFinished() = 0;
void slotFinished();
void slotError(QNetworkReply::NetworkError);
virtual void slotTimeout() {}
private:
bool _ignoreCredentialFailure;
QNetworkReply *_reply;
Account *_account;
QString _path;
@ -92,7 +97,7 @@ signals:
void exists(QNetworkReply*);
private slots:
virtual void slotFinished();
virtual void finished();
};
/**
@ -108,7 +113,7 @@ signals:
void directoryListing(const QStringList &items);
private slots:
virtual void slotFinished();
virtual void finished();
};
/**
@ -126,7 +131,7 @@ signals:
void result(const QVariantMap &values);
private slots:
virtual void slotFinished();
virtual void finished();
private:
QList<QByteArray> _properties;
@ -145,11 +150,11 @@ signals:
void finished(QNetworkReply::NetworkError);
private slots:
virtual void slotFinished();
virtual void finished();
};
/**
* @brief The CheckOwncloudJob class
* @brief The CheckServerJob class
*/
class CheckServerJob : public AbstractNetworkJob {
Q_OBJECT
@ -166,7 +171,7 @@ signals:
void timeout(const QUrl&url);
private slots:
virtual void slotFinished();
virtual void finished();
virtual void slotTimeout();
private:
@ -188,7 +193,7 @@ signals:
void etagRetreived(const QString &etag);
private slots:
virtual void slotFinished();
virtual void finished();
};
/**
@ -204,7 +209,7 @@ signals:
void quotaRetrieved(qint64 totalBytes, qint64 availableBytes);
private slots:
virtual void slotFinished();
virtual void finished();
};
} // namespace Mirall

View file

@ -23,6 +23,7 @@
#include "mirall/logger.h"
#include "mirall/logbrowser.h"
#include "mirall/account.h"
#include "creds/abstractcredentials.h"
#include <QDesktopServices>
#include <QMessageBox>
@ -140,6 +141,11 @@ void ownCloudGui::slotOpenPath(const QString &path)
Utility::showInFileManager(path);
}
void ownCloudGui::slotAccountStateChanged()
{
setupContextMenu();
}
void ownCloudGui::startupConnected( bool connected, const QStringList& fails )
{
FolderMan *folderMan = FolderMan::instance();
@ -161,6 +167,13 @@ void ownCloudGui::startupConnected( bool connected, const QStringList& fails )
void ownCloudGui::slotComputeOverallSyncStatus()
{
if (Account *a = AccountManager::instance()->account()) {
if (a->state() == Account::SignedOut) {
_tray->setIcon(Theme::instance()->syncStateIcon( SyncResult::Unavailable, true));
_tray->setToolTip(tr("Please sign in"));
return;
}
}
// display the info of the least successful sync (eg. not just display the result of the latest sync
QString trayMessage;
FolderMan *folderMan = FolderMan::instance();
@ -208,17 +221,23 @@ void ownCloudGui::setupContextMenu()
{
FolderMan *folderMan = FolderMan::instance();
bool isConfigured = (AccountManager::instance()->account() != 0);
_actionOpenoC->setEnabled(isConfigured);
Account *a = AccountManager::instance()->account();
if( _contextMenu ) {
bool isConfigured = (a != 0);
_actionOpenoC->setEnabled(isConfigured);
bool isConnected = false;
if (isConfigured) {
isConnected = (a->state() == Account::Connected);
}
if ( _contextMenu ) {
_contextMenu->clear();
_recentActionsMenu->clear();
_recentActionsMenu->addAction(tr("None."));
_recentActionsMenu->addAction(_actionRecent);
} else {
_contextMenu = new QMenu();
_recentActionsMenu = _contextMenu->addMenu(tr("Recent Changes"));
_contextMenu = new QMenu(_contextMenu);
_recentActionsMenu = new QMenu(tr("Recent Changes"));
// this must be called only once after creating the context menu, or
// it will trigger a bug in Ubuntu's SNI bridge patch (11.10, 12.04).
_tray->setContextMenu(_contextMenu);
@ -255,19 +274,25 @@ void ownCloudGui::setupContextMenu()
_contextMenu->addAction(action);
}
}
_contextMenu->addSeparator();
_contextMenu->addSeparator();
_contextMenu->addAction(_actionQuota);
_contextMenu->addSeparator();
_contextMenu->addAction(_actionStatus);
_contextMenu->addMenu(_recentActionsMenu);
_contextMenu->addSeparator();
if (isConfigured && isConnected) {
_contextMenu->addAction(_actionQuota);
_contextMenu->addSeparator();
_contextMenu->addAction(_actionStatus);
_contextMenu->addMenu(_recentActionsMenu);
_contextMenu->addSeparator();
}
_contextMenu->addAction(_actionSettings);
if (!Theme::instance()->helpUrl().isEmpty()) {
_contextMenu->addAction(_actionHelp);
}
_contextMenu->addSeparator();
if (isConfigured && isConnected) {
_contextMenu->addAction(_actionLogout);
} else {
_contextMenu->addAction(_actionLogin);
}
_contextMenu->addAction(_actionQuit);
// Populate once at start
@ -334,6 +359,11 @@ void ownCloudGui::setupActions()
_actionQuit = new QAction(tr("Quit %1").arg(Theme::instance()->appNameGUI()), this);
QObject::connect(_actionQuit, SIGNAL(triggered(bool)), _app, SLOT(quit()));
_actionLogin = new QAction(tr("Sign in..."), this);
connect(_actionLogin, SIGNAL(triggered()), _app, SLOT(slotLogin()));
_actionLogout = new QAction(tr("Sign out"), this);
connect(_actionLogout, SIGNAL(triggered()), _app, SLOT(slotLogout()));
_quotaInfo = new QuotaInfo(this);
connect(_quotaInfo, SIGNAL(quotaUpdated(qint64,qint64)), SLOT(slotRefreshQuotaDisplay(qint64,qint64)));
}
@ -451,6 +481,7 @@ void ownCloudGui::slotSettings()
_settingsDialog->setGeneralErrors( _startupFails );
Utility::raiseDialog(_settingsDialog.data());
_settingsDialog->slotRefreshResultList();
}
// open sync protocol widget

View file

@ -67,6 +67,7 @@ public slots:
void slotOpenOwnCloud();
void slotHelp();
void slotOpenPath(const QString& path);
void slotAccountStateChanged();
private slots:
void slotDisplayIdle();
@ -81,6 +82,9 @@ private:
QMenu *_contextMenu;
QMenu *_recentActionsMenu;
QAction *_actionLogin;
QAction *_actionLogout;
QAction *_actionOpenoC;
QAction *_actionSettings;
QAction *_actionQuota;

View file

@ -176,7 +176,7 @@ void PropagateLocalRemove::start()
}
}
_propagator->_journal->deleteFileRecord(_item._originalFile);
_propagator->_journal->commit();
_propagator->_journal->commit("Local remove");
done(SyncFileItem::Success);
}
@ -205,7 +205,7 @@ void PropagateRemoteRemove::start()
return;
}
_propagator->_journal->deleteFileRecord(_item._originalFile, _item._isDirectory);
_propagator->_journal->commit();
_propagator->_journal->commit("Remote Remove");
done(SyncFileItem::Success);
}
@ -257,7 +257,7 @@ private:
pi._transferid = trans->transfer_id;
pi._modtime = QDateTime::fromTime_t(trans->modtime);
that->_propagator->_journal->setUploadInfo(that->_item._file, pi);
that->_propagator->_journal->commit();
that->_propagator->_journal->commit("Upload info");
}
}
@ -267,7 +267,7 @@ private:
PropagateUploadFile* that = reinterpret_cast<PropagateUploadFile*>(userdata);
if (status == ne_status_sending && info->sr.total > 0) {
emit that->progress(Progress::Context, that->_item._file ,
emit that->progress(Progress::Context, that->_item,
that->_chunked_done + info->sr.progress,
that->_chunked_total_size ? that->_chunked_total_size : info->sr.total );
@ -281,7 +281,7 @@ private:
void PropagateUploadFile::start()
{
emit progress(Progress::StartUpload, _item._file, 0, _item._size);
emit progress(Progress::StartUpload, _item, 0, _item._size);
QFile file(_propagator->_localDir + _item._file);
if (!file.open(QIODevice::ReadOnly)) {
@ -299,6 +299,8 @@ void PropagateUploadFile::start()
* If the file has changed, retry.
*/
qDebug() << "** PUT request to" << uri.data();
const SyncJournalDb::UploadInfo progressInfo = _propagator->_journal->getUploadInfo(_item._file);
do {
Hbf_State state = HBF_SUCCESS;
QScopedPointer<hbf_transfer_t, ScopedPointerHelpers> trans(hbf_init_transfer(uri.data()));
@ -309,7 +311,6 @@ void PropagateUploadFile::start()
Q_ASSERT(trans);
state = hbf_splitlist(trans.data(), file.handle());
const SyncJournalDb::UploadInfo progressInfo = _propagator->_journal->getUploadInfo(_item._file);
if (progressInfo._valid) {
if (progressInfo._modtime.toTime_t() == _item._modtime) {
trans->start_id = progressInfo._chunk;
@ -342,8 +343,6 @@ void PropagateUploadFile::start()
if( !fid.isEmpty() ) {
if( !_item._fileId.isEmpty() && _item._fileId != fid ) {
qDebug() << "WARN: File ID changed!" << _item._fileId << fid;
} else {
qDebug() << "FileID is" << fid;
}
_item._fileId = fid;
}
@ -353,15 +352,29 @@ void PropagateUploadFile::start()
/* If the source file changed during submission, lets try again */
if( state == HBF_SOURCE_FILE_CHANGE ) {
if( attempts++ < 30 ) { /* FIXME: How often do we want to try? */
qDebug("SOURCE file has changed during upload, retry #%d in two seconds!", attempts);
sleep(2);
continue;
}
if( attempts++ < 5 ) { /* FIXME: How often do we want to try? */
qDebug("SOURCE file has changed during upload, retry #%d in %d seconds!", attempts, 2*attempts);
sleep(2*attempts);
continue;
}
const QString errMsg = tr("Local file changed during sync, syncing once it arrived completely");
done( SyncFileItem::SoftError, errMsg );
_item._errorString = errMsg;
emit progressProblem( Progress::SoftError, _item );
return;
} else if( state == HBF_USER_ABORTED ) {
const QString errMsg = tr("Sync was aborted by user.");
done( SyncFileItem::SoftError, errMsg);
_item._errorString = errMsg;
emit progressProblem( Progress::SoftError, _item );
} else {
// Other HBF error conditions.
// FIXME: find out the error class.
_item._httpErrorCode = hbf_fail_http_code(trans.data());
done(SyncFileItem::NormalError, hbf_error_string(trans.data(), state));
emit progressProblem(Progress::NormalError, _item);
}
// FIXME: find out the error class.
_item._httpErrorCode = hbf_fail_http_code(trans.data());
done(SyncFileItem::NormalError, hbf_error_string(trans.data(), state));
return;
}
@ -369,6 +382,10 @@ void PropagateUploadFile::start()
if( trans->modtime_accepted ) {
_item._etag = QByteArray(hbf_transfer_etag( trans.data() ));
if (_item._etag.endsWith("-gzip")) {
// https://github.com/owncloud/mirall/issues/1195
_item._etag.chop(5);
}
} else {
updateMTimeAndETag(uri.data(), _item._modtime);
}
@ -376,8 +393,43 @@ void PropagateUploadFile::start()
_propagator->_journal->setFileRecord(SyncJournalFileRecord(_item, _propagator->_localDir + _item._file));
// Remove from the progress database:
_propagator->_journal->setUploadInfo(_item._file, SyncJournalDb::UploadInfo());
_propagator->_journal->commit();
emit progress(Progress::EndUpload, _item._file, 0, _item._size);
_propagator->_journal->commit("upload file start");
if (hbf_validate_source_file(trans.data()) == HBF_SOURCE_FILE_CHANGE) {
/* Did the source file changed since the upload ?
This is different from the previous check because the previous check happens between
chunks while this one happens when the whole file has been uploaded.
The new etag is already stored in the database in the previous lines so in case of
crash, we won't have a conflict but we will properly do a new upload
*/
if( attempts++ < 5 ) { /* FIXME: How often do we want to try? */
qDebug("SOURCE file has changed after upload, retry #%d in %d seconds!", attempts, 2*attempts);
sleep(2*attempts);
continue;
}
// Still the file change error, but we tried a couple of times.
// Ignore this file for now.
// Lets remove the file from the server (at least if it is new) as it is different
// from our file here.
if( _item._instruction == CSYNC_INSTRUCTION_NEW ) {
QScopedPointer<char, QScopedPointerPodDeleter> uri(
ne_path_escape((_propagator->_remoteDir + _item._file).toUtf8()));
int rc = ne_delete(_propagator->_session, uri.data());
qDebug() << "Remove the invalid file from server:" << rc;
}
const QString errMsg = tr("Local file changed during sync, syncing once it arrived completely");
done( SyncFileItem::SoftError, errMsg );
return;
}
emit progress(Progress::EndUpload, _item, 0, _item._size);
done(SyncFileItem::Success);
return;
@ -386,11 +438,17 @@ void PropagateUploadFile::start()
static QByteArray parseEtag(ne_request *req) {
const char *header = ne_get_response_header(req, "etag");
QByteArray arr;
if(header && header [0] == '"' && header[ strlen(header)-1] == '"') {
return QByteArray(header + 1, strlen(header)-2);
arr = QByteArray(header + 1, strlen(header)-2);
} else {
return header;
arr = header;
}
if (arr.endsWith("-gzip")) {
// https://github.com/owncloud/mirall/issues/1195
arr.chop(5);
}
return arr;
}
static QString parseFileId(ne_request *req) {
@ -482,9 +540,10 @@ public:
void start();
private:
QIODevice *_file;
QFile *_file;
QScopedPointer<ne_decompress, ScopedPointerHelpers> _decompress;
QString errorString;
QByteArray _expectedEtagForResume;
static int content_reader(void *userdata, const char *buf, size_t len)
{
@ -498,8 +557,9 @@ private:
if(buf) {
written = that->_file->write(buf, len);
if( len != written ) {
if( len != written || that->_file->error() != QFile::NoError) {
qDebug() << "WRN: content_reader wrote wrong num of bytes:" << len << "," << written;
return NE_ERROR;
}
return NE_OK;
}
@ -540,17 +600,28 @@ private:
return;
}
const char *etag = ne_get_response_header( req, "ETag" );
if (!etag) {
qDebug() << Q_FUNC_INFO << "No E-Tag reply by server, considering it invalid";
QByteArray etag = parseEtag(req);
if (etag.isEmpty()) {
qDebug() << Q_FUNC_INFO << "No E-Tag reply by server, considering it invalid" << ne_get_response_header(req, "etag");
that->errorString = QLatin1String("No E-Tag received from server, check Proxy/Gateway");
ne_set_error(that->_propagator->_session, "No E-Tag received from server, check Proxy/Gateway");
ne_add_response_body_reader( req, do_not_accept,
do_not_download_content_reader,
(void*) that );
return;
} else if (!that->_expectedEtagForResume.isEmpty() && that->_expectedEtagForResume != etag) {
qDebug() << Q_FUNC_INFO << "We received a different E-Tag for resuming!"
<< QString::fromLatin1(that->_expectedEtagForResume.data()) << "vs"
<< QString::fromLatin1(etag.data());
that->errorString = QLatin1String("We received a different E-Tag for resuming. Retrying next time.");
ne_set_error(that->_propagator->_session, "We received a different E-Tag for resuming. Retrying next time.");
ne_add_response_body_reader( req, do_not_accept,
do_not_download_content_reader,
(void*) that );
return;
}
const char *enc = ne_get_response_header( req, "Content-Encoding" );
qDebug("Content encoding ist <%s> with status %d", enc ? enc : "empty",
status ? status->code : -1 );
@ -571,7 +642,7 @@ private:
{
PropagateDownloadFile* that = reinterpret_cast<PropagateDownloadFile*>(userdata);
if (status == ne_status_recving && info->sr.total > 0) {
emit that->progress(Progress::Context, that->_item._file, info->sr.progress, info->sr.total );
emit that->progress(Progress::Context, that->_item, info->sr.progress, info->sr.total );
that->limitBandwidth(info->sr.progress, that->_propagator->_downloadLimit);
}
}
@ -579,7 +650,7 @@ private:
void PropagateDownloadFile::start()
{
emit progress(Progress::StartDownload, _item._file, 0, _item._size);
emit progress(Progress::StartDownload, _item, 0, _item._size);
QString tmpFileName;
const SyncJournalDb::DownloadInfo progressInfo = _propagator->_journal->getDownloadInfo(_item._file);
@ -590,6 +661,7 @@ void PropagateDownloadFile::start()
_propagator->_journal->setDownloadInfo(_item._file, SyncJournalDb::DownloadInfo());
} else {
tmpFileName = progressInfo._tmpfile;
_expectedEtagForResume = progressInfo._etag;
}
}
@ -605,7 +677,7 @@ void PropagateDownloadFile::start()
QFile tmpFile(_propagator->_localDir + tmpFileName);
_file = &tmpFile;
if (!tmpFile.open(QIODevice::Append)) {
if (!tmpFile.open(QIODevice::Append | QIODevice::Unbuffered)) {
done(SyncFileItem::NormalError, tmpFile.errorString());
return;
}
@ -618,7 +690,7 @@ void PropagateDownloadFile::start()
pi._tmpfile = tmpFileName;
pi._valid = true;
_propagator->_journal->setDownloadInfo(_item._file, pi);
_propagator->_journal->commit();
_propagator->_journal->commit("download file start");
}
/* actually do the request */
@ -752,8 +824,8 @@ void PropagateDownloadFile::start()
_propagator->_journal->setFileRecord(SyncJournalFileRecord(_item, fn));
_propagator->_journal->setDownloadInfo(_item._file, SyncJournalDb::DownloadInfo());
_propagator->_journal->commit();
emit progress(Progress::EndDownload, _item._file, 0, _item._size);
_propagator->_journal->commit("download file start2");
emit progress(Progress::EndDownload, _item, 0, _item._size);
done(isConflict ? SyncFileItem::Conflict : SyncFileItem::Success);
}
@ -761,7 +833,7 @@ DECLARE_JOB(PropagateLocalRename)
void PropagateLocalRename::start()
{
emit progress(Progress::StartRename, _item._file, 0, _item._size);
emit progress(Progress::StartRename, _item, 0, _item._size);
if (_item._file != _item._renameTarget) {
qDebug() << "MOVE " << _propagator->_localDir + _item._file << " => " << _propagator->_localDir + _item._renameTarget;
QFile::rename(_propagator->_localDir + _item._file, _propagator->_localDir + _item._renameTarget);
@ -777,9 +849,9 @@ void PropagateLocalRename::start()
record._path = _item._renameTarget;
_propagator->_journal->setFileRecord(record);
_propagator->_journal->commit();
_propagator->_journal->commit("localRename");
emit progress(Progress::EndRename, _item._file, 0, _item._size);
emit progress(Progress::EndRename, _item, 0, _item._size);
done(SyncFileItem::Success);
}
@ -807,7 +879,7 @@ void PropagateRemoteRename::start()
}
return;
} else {
emit progress(Progress::StartRename, _item._file, 0, _item._size);
emit progress(Progress::StartRename, _item, 0, _item._size);
QScopedPointer<char, QScopedPointerPodDeleter> uri1(ne_path_escape((_propagator->_remoteDir + _item._file).toUtf8()));
QScopedPointer<char, QScopedPointerPodDeleter> uri2(ne_path_escape((_propagator->_remoteDir + _item._renameTarget).toUtf8()));
@ -819,7 +891,7 @@ void PropagateRemoteRename::start()
}
updateMTimeAndETag(uri2.data(), _item._modtime);
emit progress(Progress::EndRename, _item._file, 0, _item._size);
emit progress(Progress::EndRename, _item, 0, _item._size);
}
@ -828,7 +900,7 @@ void PropagateRemoteRename::start()
record._path = _item._renameTarget;
_propagator->_journal->setFileRecord(record);
_propagator->_journal->commit();
_propagator->_journal->commit("Remote Rename");
done(SyncFileItem::Success);
}
@ -873,7 +945,8 @@ bool PropagateItemJob::updateErrorFromSession(int neon_code, ne_request* req, in
// Check if we don't need to ignore that error.
httpStatusCode = errorString.mid(0, errorString.indexOf(QChar(' '))).toInt();
_item._httpErrorCode = httpStatusCode;
if (httpStatusCode == ignoreHttpCode)
qDebug() << Q_FUNC_INFO << "NE_ERROR" << errorString << httpStatusCode << ignoreHttpCode;
if (ignoreHttpCode && httpStatusCode == ignoreHttpCode)
return false;
done(SyncFileItem::NormalError, errorString);
@ -972,8 +1045,10 @@ void OwncloudPropagator::start(const SyncFileItemVector& _syncedItems)
}
connect(_rootJob.data(), SIGNAL(completed(SyncFileItem)), this, SIGNAL(completed(SyncFileItem)));
connect(_rootJob.data(), SIGNAL(progress(Progress::Kind,QString,quint64,quint64)), this, SIGNAL(progress(Progress::Kind,QString,quint64,quint64)));
connect(_rootJob.data(), SIGNAL(progress(Progress::Kind,SyncFileItem,quint64,quint64)), this,
SIGNAL(progress(Progress::Kind,SyncFileItem,quint64,quint64)));
connect(_rootJob.data(), SIGNAL(finished(SyncFileItem::Status)), this, SIGNAL(finished()));
_rootJob->start();
}

View file

@ -43,7 +43,8 @@ public slots:
signals:
void finished(SyncFileItem::Status);
void completed(const SyncFileItem &);
void progress(Progress::Kind, const QString &filename, quint64 bytes, quint64 total);
void progress(Progress::Kind, const SyncFileItem& item, quint64 bytes, quint64 total);
void progressProblem( Progress::Kind, const SyncFileItem& );
};
/*
@ -84,7 +85,7 @@ private slots:
void startJob(PropagatorJob *next) {
connect(next, SIGNAL(finished(SyncFileItem::Status)), this, SLOT(proceedNext(SyncFileItem::Status)), Qt::QueuedConnection);
connect(next, SIGNAL(completed(SyncFileItem)), this, SIGNAL(completed(SyncFileItem)));
connect(next, SIGNAL(progress(Progress::Kind,QString,quint64,quint64)), this, SIGNAL(progress(Progress::Kind,QString,quint64,quint64)));
connect(next, SIGNAL(progress(Progress::Kind,SyncFileItem,quint64,quint64)), this, SIGNAL(progress(Progress::Kind,SyncFileItem,quint64,quint64)));
next->start();
}
@ -170,8 +171,10 @@ public:
signals:
void completed(const SyncFileItem &);
void progress(Progress::Kind, const QString &filename, quint64 bytes, quint64 total);
void progress(Progress::Kind kind, const SyncFileItem&, quint64 bytes, quint64 total);
void progressProblem( Progress::Kind, const SyncFileItem& );
void finished();
};
}

View file

@ -125,6 +125,7 @@ void OwncloudSetupWizard::slotDetermineAuthType(const QString &urlString)
Account *account = _ocWizard->account();
account->setUrl(url);
CheckServerJob *job = new CheckServerJob(_ocWizard->account(), false, this);
job->setIgnoreCredentialFailure(true);
connect(job, SIGNAL(instanceFound(QUrl,QVariantMap)), SLOT(slotOwnCloudFoundAuth(QUrl,QVariantMap)));
connect(job, SIGNAL(networkError(QNetworkReply*)), SLOT(slotNoOwnCloudFoundAuth(QNetworkReply*)));
connect(job, SIGNAL(timeout(const QUrl&)), SLOT(slotNoOwnCloudFoundAuthTimeout(const QUrl&)));
@ -149,6 +150,7 @@ void OwncloudSetupWizard::slotOwnCloudFoundAuth(const QUrl& url, const QVariantM
}
DetermineAuthTypeJob *job = new DetermineAuthTypeJob(_ocWizard->account(), this);
job->setIgnoreCredentialFailure(true);
connect(job, SIGNAL(authType(WizardCommon::AuthType)),
_ocWizard, SLOT(setAuthType(WizardCommon::AuthType)));
job->start();
@ -184,6 +186,7 @@ void OwncloudSetupWizard::slotConnectToOCUrl( const QString& url )
void OwncloudSetupWizard::testOwnCloudConnect()
{
ValidateDavAuthJob *job = new ValidateDavAuthJob(_ocWizard->account(), this);
job->setIgnoreCredentialFailure(true);
connect(job, SIGNAL(authResult(QNetworkReply*)), SLOT(slotConnectionCheck(QNetworkReply*)));
job->start();
}
@ -430,9 +433,10 @@ void DetermineAuthTypeJob::start()
QNetworkReply *reply = getRequest(Account::davPath());
setReply(reply);
setupConnections(reply);
AbstractNetworkJob::start();
}
void DetermineAuthTypeJob::slotFinished()
void DetermineAuthTypeJob::finished()
{
QUrl redirection = reply()->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
qDebug() << redirection.toString();
@ -458,7 +462,6 @@ void DetermineAuthTypeJob::slotFinished()
emit authType(WizardCommon::HttpCreds);
}
}
deleteLater();
}
ValidateDavAuthJob::ValidateDavAuthJob(Account *account, QObject *parent)
@ -471,12 +474,12 @@ void ValidateDavAuthJob::start()
QNetworkReply *reply = getRequest(Account::davPath());
setReply(reply);
setupConnections(reply);
AbstractNetworkJob::start();
}
void ValidateDavAuthJob::slotFinished()
void ValidateDavAuthJob::finished()
{
emit authResult(reply());
deleteLater();
}
} // ns Mirall

View file

@ -39,7 +39,7 @@ public:
signals:
void authResult(QNetworkReply*);
private slots:
void slotFinished();
void finished();
};
class DetermineAuthTypeJob : public AbstractNetworkJob {
@ -50,7 +50,7 @@ public:
signals:
void authType(WizardCommon::AuthType);
private slots:
void slotFinished();
void finished();
private:
int _redirects;
};

View file

@ -124,6 +124,15 @@ QString Progress::asActionString( Kind kind )
return re;
}
bool Progress::isErrorKind( Kind kind )
{
bool re = false;
if( kind == SoftError || kind == NormalError || kind == FatalError ) {
re = true;
}
return re;
}
ProgressDispatcher* ProgressDispatcher::instance() {
if (!_instance) {
_instance = new ProgressDispatcher();
@ -159,59 +168,57 @@ QList<Progress::SyncProblem> ProgressDispatcher::recentProblems(int count)
return _recentProblems;
}
void ProgressDispatcher::setProgressProblem(const QString& folder, const Progress::SyncProblem &problem)
{
Q_ASSERT( Progress::isErrorKind(problem.kind));
_recentProblems.prepend( problem );
if( _recentProblems.size() > _QueueSize ) {
_recentProblems.removeLast();
}
emit progressSyncProblem( folder, problem );
}
void ProgressDispatcher::setProgressInfo(const QString& folder, const Progress::Info& progress)
{
if( folder.isEmpty() ) {
return;
}
Progress::Info newProgress = progress;
Progress::Info newProgress(progress);
if( newProgress.kind == Progress::Error ) {
Progress::SyncProblem err;
err.folder = folder;
err.current_file = newProgress.current_file;
// its really
err.error_message = QString::fromLocal8Bit( (const char*)newProgress.file_size );
err.error_code = newProgress.current_file_bytes;
err.timestamp = QDateTime::currentDateTime();
Q_ASSERT( !Progress::isErrorKind(progress.kind));
_recentProblems.prepend( err );
if( _recentProblems.size() > _QueueSize ) {
_recentProblems.removeLast();
}
emit progressSyncProblem( folder, err );
} else {
if( newProgress.kind == Progress::StartSync ) {
_recentProblems.clear();
_timer.start();
}
if( newProgress.kind == Progress::EndSync ) {
newProgress.overall_current_bytes = newProgress.overall_transmission_size;
newProgress.current_file_no = newProgress.overall_file_count;
_currentAction.remove(newProgress.folder);
qint64 msecs = _timer.elapsed();
qDebug()<< "[PROGRESS] progressed " << newProgress.overall_transmission_size
<< " bytes in " << newProgress.overall_file_count << " files"
<< " in msec " << msecs;
}
if( newProgress.kind == Progress::EndDownload ||
newProgress.kind == Progress::EndUpload ||
newProgress.kind == Progress::EndDelete ||
newProgress.kind == Progress::EndRename ) {
_recentChanges.prepend(newProgress);
if( _recentChanges.size() > _QueueSize ) {
_recentChanges.removeLast();
}
}
// store the last real action to help clients that start during
// the Context-phase of an upload or download.
if( newProgress.kind != Progress::Context ) {
_currentAction[folder] = newProgress.kind;
}
emit progressInfo( folder, newProgress );
if( newProgress.kind == Progress::StartSync ) {
_recentProblems.clear();
_timer.start();
}
if( newProgress.kind == Progress::EndSync ) {
newProgress.overall_current_bytes = newProgress.overall_transmission_size;
newProgress.current_file_no = newProgress.overall_file_count;
_currentAction.remove(newProgress.folder);
qint64 msecs = _timer.elapsed();
qDebug()<< "[PROGRESS] progressed " << newProgress.overall_transmission_size
<< " bytes in " << newProgress.overall_file_count << " files"
<< " in msec " << msecs;
}
if( newProgress.kind == Progress::EndDownload ||
newProgress.kind == Progress::EndUpload ||
newProgress.kind == Progress::EndDelete ||
newProgress.kind == Progress::EndRename ) {
_recentChanges.prepend(newProgress);
if( _recentChanges.size() > _QueueSize ) {
_recentChanges.removeLast();
}
}
// store the last real action to help clients that start during
// the Context-phase of an upload or download.
if( newProgress.kind != Progress::Context ) {
_currentAction[folder] = newProgress.kind;
}
emit progressInfo( folder, newProgress );
}
Progress::Kind ProgressDispatcher::currentFolderContext( const QString& folder )

View file

@ -44,7 +44,9 @@ namespace Progress
EndDelete,
StartRename,
EndRename,
Error
SoftError,
NormalError,
FatalError
};
struct Info {
@ -67,15 +69,20 @@ namespace Progress
};
struct SyncProblem {
Kind kind;
QString folder;
QString current_file;
QString error_message;
int error_code;
QDateTime timestamp;
SyncProblem() : kind(Invalid), error_code(0) {}
};
QString asActionString( Kind );
QString asResultString( Kind );
bool isErrorKind( Kind );
}
/**
@ -100,6 +107,7 @@ public:
QList<Progress::SyncProblem> recentProblems(int count);
Progress::Kind currentFolderContext( const QString& folder );
signals:
/**
@brief Signals the progress of data transmission.
@ -113,7 +121,8 @@ signals:
void progressSyncProblem( const QString& folder, const Progress::SyncProblem& problem );
protected:
void setProgressInfo(const QString &folder, const Progress::Info& progress);
void setProgressInfo(const QString& folder, const Progress::Info& progress);
void setProgressProblem( const QString& folder, const Progress::SyncProblem& problem);
private:
ProgressDispatcher(QObject* parent = 0);

View file

@ -111,7 +111,8 @@ void ProtocolWidget::setSyncResult( const SyncResult& result )
// handle ignored files here.
if( item._status == SyncFileItem::FileIgnored
|| item._status == SyncFileItem::Conflict ) {
|| item._status == SyncFileItem::Conflict
|| item._status == SyncFileItem::SoftError ) {
QStringList columns;
QString timeStr = timeString(dt);
QString longTimeStr = timeString(dt, QLocale::LongFormat);
@ -120,7 +121,10 @@ void ProtocolWidget::setSyncResult( const SyncResult& result )
columns << item._file;
columns << folder;
if( item._status == SyncFileItem::FileIgnored ) {
if( item._type == SyncFileItem::SoftLink ) {
if( item._blacklistedInDb ) {
errMsg = tr("Blacklisted");
tooltip = tr("The file is blacklisted because of previous error conditions.");
}else if( item._type == SyncFileItem::SoftLink ) {
errMsg = tr("Soft Link ignored");
tooltip = tr("Softlinks break the semantics of synchronization.\nPlease do not "
"use them in synced directories");
@ -149,6 +153,8 @@ void ProtocolWidget::setSyncResult( const SyncResult& result )
"created a so called conflict. The local change is copied to the conflict\n"
"file while the file from the server side is available under the original\n"
"name");
} else if( item._status == SyncFileItem::SoftError ) {
errMsg = item._errorString;
} else {
Q_ASSERT(!"unhandled instruction.");
}
@ -181,7 +187,6 @@ void ProtocolWidget::setupList()
haveSyncResult = true;
}
if( haveSyncResult ) {
setSyncResult(lastResult);
}
@ -285,7 +290,7 @@ void ProtocolWidget::slotProgressProblem( const QString& folder, const Progress:
columns << timeStr;
columns << problem.current_file;
columns << folder;
QString errMsg = tr("Problem: %1").arg(problem.error_message);
QString errMsg = problem.error_message;
#if 0
if( problem.error_code == 507 ) {
errMsg = tr("No more storage space available on server.");
@ -297,7 +302,11 @@ void ProtocolWidget::slotProgressProblem( const QString& folder, const Progress:
item->setData(0, ErrorIndicatorRole, QVariant(true) );
// Maybe we should not set the error icon for all problems but distinguish
// by error_code. A quota problem is considered an error, others might not??
item->setIcon(0, Theme::instance()->syncStateIcon(SyncResult::Error, true));
if( problem.kind == Progress::SoftError ) {
item->setIcon(0, Theme::instance()->syncStateIcon(SyncResult::Problem, true));
} else {
item->setIcon(0, Theme::instance()->syncStateIcon(SyncResult::Error, true));
}
item->setToolTip(0, longTimeStr);
_ui->_treeWidget->insertTopLevelItem(0, item);
Q_UNUSED(item);

View file

@ -17,6 +17,7 @@
#include "creds/abstractcredentials.h"
#include <QTimer>
#include <QDebug>
namespace Mirall {
@ -28,6 +29,7 @@ static const int initialTimeT = 1*1000;
QuotaInfo::QuotaInfo(QObject *parent)
: QObject(parent)
, _account(AccountManager::instance()->account())
, _lastQuotaTotalBytes(0)
, _lastQuotaUsedBytes(0)
, _refreshTimer(new QTimer(this))
@ -41,8 +43,18 @@ QuotaInfo::QuotaInfo(QObject *parent)
void QuotaInfo::slotAccountChanged(Account *newAccount, Account *oldAccount)
{
Q_UNUSED(oldAccount);
_account = newAccount;
disconnect(oldAccount, SIGNAL(stateChanged(int)), this, SLOT(slotAccountStateChanged(int)));
connect(newAccount, SIGNAL(stateChanged(int)), this, SLOT(slotAccountStateChanged(int)));
}
void QuotaInfo::slotAccountStateChanged(int state)
{
if (state == Account::Connected) {
slotCheckQuota();
} else {
_refreshTimer->stop();
}
}
void QuotaInfo::slotCheckQuota()

View file

@ -34,6 +34,7 @@ public Q_SLOTS:
private Q_SLOTS:
void slotAccountChanged(Account *newAccount, Account *oldAccount);
void slotAccountStateChanged(int state);
Q_SIGNALS:
void quotaUpdated(qint64 total, qint64 used);

View file

@ -57,8 +57,8 @@ SettingsDialog::SettingsDialog(ownCloudGui *gui, QWidget *parent) :
QListWidgetItem *protocol= new QListWidgetItem(protocolIcon, tr("Status"), _ui->labelWidget);
protocol->setSizeHint(QSize(0, 32));
_ui->labelWidget->addItem(protocol);
ProtocolWidget *protocolWidget = new ProtocolWidget;
_protocolIdx = _ui->stack->addWidget(protocolWidget);
_protocolWidget = new ProtocolWidget;
_protocolIdx = _ui->stack->addWidget(_protocolWidget);
QIcon generalIcon(QLatin1String(":/mirall/resources/settings.png"));
QListWidgetItem *general = new QListWidgetItem(generalIcon, tr("General"), _ui->labelWidget);
@ -179,4 +179,9 @@ void SettingsDialog::accept() {
QDialog::accept();
}
void SettingsDialog::slotRefreshResultList() {
if( _protocolWidget ) {
_protocolWidget->setupList();
}
}
} // namespace Mirall

View file

@ -28,6 +28,7 @@ namespace Ui {
class SettingsDialog;
}
class AccountSettings;
class ProtocolWidget;
class Application;
class FolderMan;
class ownCloudGui;
@ -46,6 +47,7 @@ public:
public slots:
void slotSyncStateChange(const QString& alias);
void slotShowProtocol();
void slotRefreshResultList();
protected:
void reject();
@ -55,6 +57,7 @@ private:
Ui::SettingsDialog *_ui;
AccountSettings *_accountSettings;
QListWidgetItem *_accountItem;
ProtocolWidget *_protocolWidget;
int _protocolIdx;
};

View file

@ -24,12 +24,11 @@
#include "syncjournalfilerecord.h"
#define QSQLITE "QSQLITE"
#define SYNCJOURNALDB_CONNECTION_NAME "SyncJournalDbConnection"
namespace Mirall {
SyncJournalDb::SyncJournalDb(const QString& path, QObject *parent) :
QObject(parent)
QObject(parent), _transaction(0)
{
_dbFile = path;
@ -47,6 +46,42 @@ bool SyncJournalDb::exists()
return (!_dbFile.isEmpty() && QFile::exists(_dbFile));
}
void SyncJournalDb::startTransaction()
{
if( _transaction == 0 ) {
if( !_db.transaction() ) {
qDebug() << "ERROR commiting to the database: " << _db.lastError().text();
return;
}
_transaction = 1;
// qDebug() << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Transaction start!";
} else {
qDebug() << "Database Transaction is running, do not starting another one!";
}
}
void SyncJournalDb::commitTransaction()
{
if( _transaction == 1 ) {
if( ! _db.commit() ) {
qDebug() << "ERROR commiting to the database: " << _db.lastError().text();
return;
}
_transaction = 0;
// qDebug() << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Transaction END!";
} else {
qDebug() << "No database Transaction to commit";
}
}
bool SyncJournalDb::sqlFail( const QString& log, const QSqlQuery& query )
{
commitTransaction();
qWarning() << "Error" << log << query.lastError().text();
return false;
}
bool SyncJournalDb::checkConnect()
{
if( _db.isOpen() ) {
@ -69,8 +104,7 @@ bool SyncJournalDb::checkConnect()
}
// Add the connection
if (!QSqlDatabase::connectionNames().contains(SYNCJOURNALDB_CONNECTION_NAME))
_db = QSqlDatabase::addDatabase( QSQLITE, SYNCJOURNALDB_CONNECTION_NAME );
_db = QSqlDatabase::addDatabase( QSQLITE, _dbFile);
// Open the file
_db.setDatabaseName(_dbFile);
@ -86,18 +120,15 @@ bool SyncJournalDb::checkConnect()
QSqlQuery pragma1(_db);
pragma1.prepare("PRAGMA synchronous = 1;");
if (!pragma1.exec()) {
qWarning() << "Error setting pragma: " << pragma1.lastError().text();
return false;
return sqlFail("Set PRAGMA synchronous", pragma1);
}
pragma1.prepare("PRAGMA case_sensitive_like = ON;");
if (!pragma1.exec()) {
qWarning() << "Error setting pragma: " << pragma1.lastError().text();
return false;
return sqlFail("Set PRAGMA case_sensitivity", pragma1);
}
/* Because insert are so slow, e do everything in a transaction, and one need to call commit */
_db.transaction();
startTransaction();
QSqlQuery createQuery(_db);
createQuery.prepare("CREATE TABLE IF NOT EXISTS metadata("
@ -115,8 +146,7 @@ bool SyncJournalDb::checkConnect()
");");
if (!createQuery.exec()) {
qWarning() << "Error creating table metadata : " << createQuery.lastError().text();
return false;
return sqlFail("Create table metadata", createQuery);
}
createQuery.prepare("CREATE TABLE IF NOT EXISTS downloadinfo("
@ -128,8 +158,7 @@ bool SyncJournalDb::checkConnect()
");");
if (!createQuery.exec()) {
qWarning() << "Error creating table downloadinfo : " << createQuery.lastError().text();
return false;
return sqlFail("Create table downloadinfo", createQuery);
}
createQuery.prepare("CREATE TABLE IF NOT EXISTS uploadinfo("
@ -143,8 +172,7 @@ bool SyncJournalDb::checkConnect()
");");
if (!createQuery.exec()) {
qWarning() << "Error creating table downloadinfo : " << createQuery.lastError().text();
return false;
return sqlFail("Create table uploadinfo", createQuery);
}
// create the blacklist table.
@ -152,16 +180,17 @@ bool SyncJournalDb::checkConnect()
"path VARCHAR(4096),"
"lastTryEtag VARCHAR[32],"
"lastTryModtime INTEGER[8],"
"retrycount INTEGER default 0,"
"retrycount INTEGER,"
"errorstring VARCHAR[4096],"
"PRIMARY KEY(path)"
");");
if (!createQuery.exec()) {
qWarning() << "Error creating table blacklist: " << createQuery.lastError().text();
return false;
return sqlFail("Create table blacklist", createQuery);
}
commitInternal("checkConnect");
bool rc = updateDatabaseStructure();
if( rc ) {
_getFileRecordQuery.reset(new QSqlQuery(_db));
@ -215,6 +244,8 @@ void SyncJournalDb::close()
{
QMutexLocker locker(&_mutex);
commitTransaction();
_getFileRecordQuery.reset(0);
_setFileRecordQuery.reset(0);
_getDownloadInfoQuery.reset(0);
@ -226,7 +257,10 @@ void SyncJournalDb::close()
_deleteFileRecordPhash.reset(0);
_deleteFileRecordRecursively.reset(0);
_blacklistQuery.reset(0);
_db.close();
_db = QSqlDatabase(); // avoid the warning QSqlDatabasePrivate::removeDatabase: connection [...] still in use
QSqlDatabase::removeDatabase(_dbFile);
}
@ -236,13 +270,19 @@ bool SyncJournalDb::updateDatabaseStructure()
bool re = true;
// check if the file_id column is there and create it if not
if( !checkConnect() ) {
return false;
}
if( columns.indexOf(QLatin1String("fileid")) == -1 ) {
QSqlQuery query(_db);
query.prepare("ALTER TABLE metadata ADD COLUMN fileid VARCHAR(128);");
re = query.exec();
query.prepare("CREATE INDEX metadata_file_id ON metadata(fileid);");
re = re && query.exec();
commitInternal("update database structure");
}
return re;
@ -253,20 +293,21 @@ QStringList SyncJournalDb::tableColumns( const QString& table )
QStringList columns;
if( !table.isEmpty() ) {
QString q = QString("PRAGMA table_info(%1);").arg(table);
QSqlQuery query(_db);
query.prepare(q);
if( checkConnect() ) {
QString q = QString("PRAGMA table_info(%1);").arg(table);
QSqlQuery query(_db);
query.prepare(q);
if(!query.exec()) {
QString err = query.lastError().text();
qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err;;
return columns;
}
if(!query.exec()) {
QString err = query.lastError().text();
qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err;;
return columns;
}
while( query.next() ) {
columns.append( query.value(1).toString() );
while( query.next() ) {
columns.append( query.value(1).toString() );
}
}
query.finish();
}
qDebug() << "Columns in the current journal: " << columns;
@ -418,8 +459,9 @@ bool SyncJournalDb::postSyncCleanup(const QHash<QString, QString> &items )
{
QMutexLocker locker(&_mutex);
if( !checkConnect() )
if( !checkConnect() ) {
return false;
}
QSqlQuery query(_db);
query.prepare("SELECT phash, path FROM metadata order by path");
@ -458,8 +500,9 @@ int SyncJournalDb::getFileRecordCount()
{
QMutexLocker locker(&_mutex);
if( !checkConnect() )
if( !checkConnect() ) {
return -1;
}
QSqlQuery query(_db);
query.prepare("SELECT COUNT(*) FROM metadata");
@ -509,8 +552,9 @@ void SyncJournalDb::setDownloadInfo(const QString& file, const SyncJournalDb::Do
{
QMutexLocker locker(&_mutex);
if( !checkConnect() )
if( !checkConnect() ) {
return;
}
if (i._valid) {
_setDownloadInfoQuery->bindValue(0, file);
@ -572,8 +616,9 @@ void SyncJournalDb::setUploadInfo(const QString& file, const SyncJournalDb::Uplo
{
QMutexLocker locker(&_mutex);
if( !checkConnect() )
if( !checkConnect() ) {
return;
}
if (i._valid) {
_setUploadInfoQuery->bindValue(0, file);
@ -635,30 +680,35 @@ SyncJournalBlacklistRecord SyncJournalDb::blacklistEntry( const QString& file )
void SyncJournalDb::wipeBlacklistEntry( const QString& file )
{
QMutexLocker locker(&_mutex);
if( checkConnect() ) {
QSqlQuery query(_db);
QSqlQuery query;
query.prepare("DELETE FROM blacklist WHERE path=:path");
query.bindValue(":path", file);
if( ! query.exec() ) {
qDebug() << "Deletion of blacklist item failed.";
query.prepare("DELETE FROM blacklist WHERE path=:path");
query.bindValue(":path", file);
if( ! query.exec() ) {
qDebug() << "Deletion of blacklist item failed.";
}
}
}
void SyncJournalDb::updateBlacklistEntry( const SyncJournalBlacklistRecord& item )
{
QMutexLocker locker(&_mutex);
QSqlQuery query;
QSqlQuery query(_db);
if( !checkConnect() ) {
return;
}
query.prepare("SELECT retrycount FROM blacklist WHERE path=:path");
query.bindValue(":path", item._file);
if( !query.exec() ) {
qDebug() << "SQL exec blacklistitem failed.";
qDebug() << "SQL exec blacklistitem failed:" << query.lastError().text();
return;
}
QSqlQuery iQuery;
QSqlQuery iQuery(_db);
if( query.next() ) {
int retries = query.value(0).toInt();
retries--;
@ -685,20 +735,29 @@ void SyncJournalDb::updateBlacklistEntry( const SyncJournalBlacklistRecord& item
if( !iQuery.exec() ) {
qDebug() << "SQL exec blacklistitem insert/update failed: "<< iQuery.lastError().text();
}
}
void SyncJournalDb::commit()
void SyncJournalDb::commit(const QString& context, bool startTrans)
{
QMutexLocker locker(&_mutex);
if (!_db.commit()) {
qDebug() << "ERROR commiting to the database: " << _db.lastError().text();
QMutexLocker lock(&_mutex);
commitInternal(context, startTrans);
}
void SyncJournalDb::commitInternal(const QString& context, bool startTrans )
{
qDebug() << "Transaction Start " << context;
commitTransaction();
if( startTrans ) {
startTransaction();
}
_db.transaction();
}
SyncJournalDb::~SyncJournalDb()
{
_db.commit();
close();
}

View file

@ -36,7 +36,6 @@ public:
bool deleteFileRecord( const QString& filename, bool recursively = false );
int getFileRecordCount();
bool exists();
QStringList tableColumns( const QString& table );
void updateBlacklistEntry( const SyncJournalBlacklistRecord& item );
void wipeBlacklistEntry(const QString& file);
@ -68,9 +67,11 @@ public:
/* Because sqlite transactions is really slow, we encapsulate everything in big transactions
* Commit will actually commit the transaction and create a new one.
*/
void commit();
void commit(const QString &context, bool startTrans = true);
void close();
signals:
public slots:
@ -78,11 +79,17 @@ public slots:
private:
qint64 getPHash(const QString& ) const;
bool updateDatabaseStructure();
bool sqlFail(const QString& log, const QSqlQuery &query );
void commitInternal(const QString &context, bool startTrans = true);
void startTransaction();
void commitTransaction();
QStringList tableColumns( const QString& table );
bool checkConnect();
QSqlDatabase _db;
QString _dbFile;
QMutex _mutex; // Public functions are protected with the mutex.
int _transaction;
QScopedPointer<QSqlQuery> _getFileRecordQuery;
QScopedPointer<QSqlQuery> _setFileRecordQuery;
QScopedPointer<QSqlQuery> _getDownloadInfoQuery;

View file

@ -13,6 +13,7 @@
*/
#include "systray.h"
#include "mirall/theme.h"
#ifdef USE_FDO_NOTIFICATIONS
#include <QDBusConnection>
@ -43,4 +44,9 @@ void Systray::showMessage(const QString & title, const QString & message, Messag
}
}
void Systray::setToolTip(const QString &tip)
{
QSystemTrayIcon::setToolTip(tr("%1: %2").arg(Theme::instance()->appNameGUI(), tip));
}
} // namespace Mirall

View file

@ -26,7 +26,7 @@ class Systray : public QSystemTrayIcon
Q_OBJECT
public:
void showMessage(const QString & title, const QString & message, MessageIcon icon = Information, int millisecondsTimeoutHint = 10000);
void setToolTip(const QString &tip);
};
} // namespace Mirall

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff