/* * Copyright (C) by Duncan Mac-Vicar P. * Copyright (C) by Klaas Freitag * Copyright (C) by Daniel Molkentin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #include #include "config.h" #include "mirall/account.h" #include "mirall/application.h" #include "mirall/connectionvalidator.h" #include "mirall/folder.h" #include "mirall/folderman.h" #include "mirall/logger.h" #include "mirall/mirallconfigfile.h" #include "mirall/socketapi.h" #include "mirall/sslerrordialog.h" #include "mirall/theme.h" #include "mirall/utility.h" #include "mirall/clientproxy.h" #include "updater/updater.h" #include "creds/abstractcredentials.h" #include "config.h" #if defined(Q_OS_WIN) #include #endif #include #include #include namespace Mirall { namespace { static const char optionsC[] = "Options:\n" " -h --help : show this help screen.\n" " --logwindow : open a window to show log output.\n" " --logfile : write log output to file .\n" " --logdir : write each sync log output in a new file\n" " in directory .\n" " --logexpire : removes logs older than hours.\n" " (to be used with --logdir)\n" " --logflush : flush the log file after every write.\n" " --confdir : Use the given configuration directory.\n" ; QString applicationTrPath() { #ifdef Q_OS_LINUX return QString::fromLatin1(DATADIR"/"APPLICATION_EXECUTABLE"/i18n/"); #endif #ifdef Q_OS_MAC return QApplication::applicationDirPath()+QLatin1String("/../Resources/Translations"); // path defaults to app dir. #endif #ifdef Q_OS_WIN return QApplication::applicationDirPath(); #endif } } // ---------------------------------------------------------------------------------- Application::Application(int &argc, char **argv) : SharedTools::QtSingleApplication(argc, argv), _gui(0), _theme(Theme::instance()), _helpOnly(false), _startupNetworkError(false), _showLogWindow(false), _logExpire(0), _logFlush(false), _userTriggeredConnect(false) { // TODO: Can't set this without breaking current config pathes // setOrganizationName(QLatin1String(APPLICATION_VENDOR)); setOrganizationDomain(QLatin1String(APPLICATION_REV_DOMAIN)); setApplicationName( _theme->appNameGUI() ); setWindowIcon( _theme->applicationIcon() ); parseOptions(arguments()); //no need to waste time; if ( _helpOnly ) return; initialize(); if (isRunning()) return; setupLogging(); setupTranslations(); connect( this, SIGNAL(messageReceived(QString)), SLOT(slotParseOptions(QString))); Account *account = Account::restore(); if (account) { account->setSslErrorHandler(new SslDialogErrorHandler); AccountManager::instance()->setAccount(account); } FolderMan::instance()->setSyncEnabled(false); setQuitOnLastWindowClosed(false); qRegisterMetaType("Progress::Kind"); qRegisterMetaType("Progress::Info"); qRegisterMetaType("Progress::SyncProblem"); MirallConfigFile cfg; _theme->setSystrayUseMonoIcons(cfg.monoIcons()); connect (_theme, SIGNAL(systrayUseMonoIconsChanged(bool)), SLOT(slotUseMonoIconsChanged(bool))); FolderMan::instance()->setupFolders(); _proxy.setupQtProxyFromConfig(); // folders have to be defined first, than we set up the Qt proxy. _gui = new ownCloudGui(this); if( _showLogWindow ) { _gui->slotToggleLogBrowser(); // _showLogWindow is set in parseOptions. } if (account) { slotAccountChanged(account); } connect(AccountManager::instance(), SIGNAL(accountChanged(Account*,Account*)), this, SLOT(slotAccountChanged(Account*,Account*))); // startup procedure. QTimer::singleShot( 0, this, SLOT( slotCheckConnection() )); if( cfg.skipUpdateCheck() ) { qDebug() << Q_FUNC_INFO << "Skipping update check"; } else { QTimer::singleShot( 3000, this, SLOT( slotStartUpdateDetector() )); } connect (this, SIGNAL(aboutToQuit()), SLOT(slotCleanup())); _socketApi = new SocketApi(this, cfg.configPathWithAppName().append(QLatin1String("socket"))); } Application::~Application() { // qDebug() << "* Mirall shutdown"; } void Application::slotLogin() { Account *a = AccountManager::instance()->account(); if (a) { FolderMan::instance()->setupFolders(); _userTriggeredConnect = true; 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) { if (oldAccount) { disconnect(oldAccount, SIGNAL(stateChanged(int)), _gui, SLOT(slotAccountStateChanged())); disconnect(oldAccount, SIGNAL(stateChanged(int)), this, SLOT(slotToggleFolderman(int))); connect(oldAccount->quotaInfo(), SIGNAL(quotaUpdated(qint64,qint64)), _gui, SLOT(slotRefreshQuotaDisplay(qint64,qint64))); } connect(newAccount, SIGNAL(stateChanged(int)), _gui, SLOT(slotAccountStateChanged())); connect(newAccount, SIGNAL(stateChanged(int)), this, SLOT(slotToggleFolderman(int))); connect(newAccount->quotaInfo(), SIGNAL(quotaUpdated(qint64,qint64)), _gui, SLOT(slotRefreshQuotaDisplay(qint64,qint64))); } void Application::slotCleanup() { // explicitly close windows. This is somewhat of a hack to ensure // that saving the geometries happens ASAP during a OS shutdown Account *account = AccountManager::instance()->account(); if (account) { account->save(); } _gui->slotShutdown(); _gui->deleteLater(); } void Application::slotStartUpdateDetector() { Updater *updater = Updater::instance(); updater->backgroundCheckForUpdate(); } void Application::slotCheckConnection() { Account *account = AccountManager::instance()->account(); if( account ) { if (account->state() == Account::InvalidCredidential || account->state() == Account::SignedOut) { //Do not try to connect if we are logged out if (!_userTriggeredConnect) return; } AbstractCredentials* credentials(account->credentials()); if (! credentials->ready()) { connect( credentials, SIGNAL(fetched()), this, SLOT(slotCredentialsFetched()), Qt::UniqueConnection); credentials->fetch(account); } else { slotCredentialsFetched(); } } else { // let gui open the setup wizard _gui->slotOpenSettingsDialog( true ); } } void Application::slotCredentialsFetched() { Account *account = AccountManager::instance()->account(); Q_ASSERT(account); disconnect(account->credentials(), SIGNAL(fetched()), this, SLOT(slotCredentialsFetched())); if (!account->credentials()->ready()) { // User canceled the connection or did not give a password account->setState(Account::SignedOut); return; } if (account->state() == Account::InvalidCredidential) { // Then we ask again for the credidentials if they are wrong again account->setState(Account::Disconnected); } if (_conValidator) _conValidator->deleteLater(); _conValidator = new ConnectionValidator(account); connect( _conValidator, SIGNAL(connectionResult(ConnectionValidator::Status)), this, SLOT(slotConnectionValidatorResult(ConnectionValidator::Status)) ); _conValidator->checkConnection(); } void Application::slotToggleFolderman(int state) { FolderMan* folderMan = FolderMan::instance(); switch (state) { case Account::Connected: folderMan->setSyncEnabled(true); folderMan->slotScheduleAllFolders(); break; case Account::Disconnected: case Account::SignedOut: case Account::InvalidCredidential: folderMan->setSyncEnabled(false); folderMan->terminateSyncProcess(); break; } } void Application::slotConnectionValidatorResult(ConnectionValidator::Status status) { qDebug() << "Connection Validator Result: " << _conValidator->statusString(status); QStringList startupFails; if( status == ConnectionValidator::Connected ) { FolderMan *folderMan = FolderMan::instance(); qDebug() << "######## Connection and Credentials are ok!"; folderMan->setSyncEnabled(true); // queue up the sync for all folders. folderMan->slotScheduleAllFolders(); if(!_connectionMsgBox.isNull()) { _connectionMsgBox->close(); } } else { // if we have problems here, it's unlikely that syncing will work. FolderMan::instance()->setSyncEnabled(false); startupFails = _conValidator->errors(); _startupNetworkError = _conValidator->networkError(); if (_userTriggeredConnect) { if(_connectionMsgBox.isNull()) { _connectionMsgBox = new QMessageBox(QMessageBox::Warning, tr("Connection failed"), _conValidator->errors().join(". ").append('.'), QMessageBox::Ok, 0); _connectionMsgBox->setAttribute(Qt::WA_DeleteOnClose); _connectionMsgBox->open(); _userTriggeredConnect = false; } } QTimer::singleShot(30*1000, this, SLOT(slotCheckConnection())); } _gui->startupConnected( (status == ConnectionValidator::Connected), startupFails); _conValidator->deleteLater(); } void Application::slotownCloudWizardDone( int res ) { FolderMan *folderMan = FolderMan::instance(); if( res == QDialog::Accepted ) { int cnt = folderMan->setupFolders(); qDebug() << "Set up " << cnt << " folders."; // We have some sort of configuration. Enable autostart Utility::setLaunchOnStartup(_theme->appName(), _theme->appNameGUI(), true); // FIXME! // _statusDialog->setFolderList( folderMan->map() ); } folderMan->setSyncEnabled( true ); if( res == QDialog::Accepted ) { slotCheckConnection(); } } void Application::setupLogging() { // might be called from second instance Logger::instance()->setLogFile(_logFile); Logger::instance()->setLogDir(_logDir); Logger::instance()->setLogExpire(_logExpire); Logger::instance()->setLogFlush(_logFlush); Logger::instance()->enterNextLogFile(); qDebug() << QString::fromLatin1( "################## %1 %2 (%3) %4").arg(_theme->appName()) .arg( QLocale::system().name() ) .arg(property("ui_lang").toString()) .arg(_theme->version()); } void Application::slotUseMonoIconsChanged(bool) { _gui->slotComputeOverallSyncStatus(); } void Application::slotParseOptions(const QString &opts) { QStringList options = opts.split(QLatin1Char('|')); parseOptions(options); setupLogging(); } void Application::parseOptions(const QStringList &options) { QStringListIterator it(options); // skip file name; if (it.hasNext()) it.next(); //parse options; if help or bad option exit while (it.hasNext()) { QString option = it.next(); if (option == QLatin1String("--help") || option == QLatin1String("-h")) { setHelp(); break; } else if (option == QLatin1String("--logwindow") || option == QLatin1String("-l")) { _showLogWindow = true; } else if (option == QLatin1String("--logfile")) { if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) { _logFile = it.next(); } else { setHelp(); } } else if (option == QLatin1String("--logdir")) { if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) { _logDir = it.next(); } else { setHelp(); } } else if (option == QLatin1String("--logexpire")) { if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) { _logExpire = it.next().toInt(); } else { setHelp(); } } else if (option == QLatin1String("--logflush")) { _logFlush = true; } else if (option == QLatin1String("--confdir")) { if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) { QString confDir = it.next(); MirallConfigFile::setConfDir( confDir ); } else { showHelp(); } } else { setHelp(); break; } } } // Helpers for displaying messages. Note that there is no console on Windows. #ifdef Q_OS_WIN // Format as
 HTML
static inline void toHtml(QString &t)
{
    t.replace(QLatin1Char('&'), QLatin1String("&"));
    t.replace(QLatin1Char('<'), QLatin1String("<"));
    t.replace(QLatin1Char('>'), QLatin1String(">"));
    t.insert(0, QLatin1String("
"));
    t.append(QLatin1String("
")); } static void displayHelpText(QString t) // No console on Windows. { toHtml(t); QMessageBox::information(0, Theme::instance()->appNameGUI(), t); } #else static void displayHelpText(const QString &t) { std::cout << qPrintable(t); } #endif void Application::showHelp() { setHelp(); QString helpText; QTextStream stream(&helpText); stream << _theme->appName().toLatin1().constData() << QLatin1String(" version ") << _theme->version().toLatin1().constData() << endl; stream << QLatin1String("File synchronisation desktop utility.") << endl << endl << QLatin1String(optionsC); if (_theme->appName() == QLatin1String("ownCloud")) stream << endl << "For more information, see http://www.owncloud.org" << endl << endl; displayHelpText(helpText); } void Application::setHelp() { _helpOnly = true; } #if defined(Q_OS_WIN) && QT_VERSION < QT_VERSION_CHECK(5,0,0) bool Application::winEventFilter(MSG *pMsg, long *result) { if (pMsg->message == WM_POWERBROADCAST) { switch(pMsg->wParam) { case PBT_APMPOWERSTATUSCHANGE: qDebug() << "WM_POWERBROADCAST: Power state changed"; break; case PBT_APMSUSPEND: qDebug() << "WM_POWERBROADCAST: Entering low power state"; break; case PBT_APMRESUMEAUTOMATIC: qDebug() << "WM_POWERBROADCAST: Resuming from low power state"; break; default: break; } return true; } return SharedTools::QtSingleApplication::winEventFilter(pMsg, result); } #endif QString substLang(const QString &lang) { // Map the more apropriate script codes // to country codes as used by Qt and // transifex translation conventions. // Simplified Chinese if (lang == QLatin1String("zh_Hans")) return QLatin1String("zh_CN"); // Traditional Chinese if (lang == QLatin1String("zh_Hant")) return QLatin1String("zh_TW"); return lang; } void Application::setupTranslations() { QStringList uiLanguages; // uiLanguages crashes on Windows with 4.8.0 release builds #if (QT_VERSION >= 0x040801) || (QT_VERSION >= 0x040800 && !defined(Q_OS_WIN)) uiLanguages = QLocale::system().uiLanguages(); #else // older versions need to fall back to the systems locale uiLanguages << QLocale::system().name(); #endif QString enforcedLocale = Theme::instance()->enforcedLocale(); if (!enforcedLocale.isEmpty()) uiLanguages.prepend(enforcedLocale); QTranslator *translator = new QTranslator(this); QTranslator *qtTranslator = new QTranslator(this); QTranslator *qtkeychainTranslator = new QTranslator(this); foreach(QString lang, uiLanguages) { lang.replace(QLatin1Char('-'), QLatin1Char('_')); // work around QTBUG-25973 lang = substLang(lang); const QString trPath = applicationTrPath(); const QString trFile = QLatin1String("mirall_") + lang; if (translator->load(trFile, trPath) || lang.startsWith(QLatin1String("en"))) { // Permissive approach: Qt and keychain translations // may be missing, but Qt translations must be there in order // for us to accept the language. Otherwise, we try with the next. // "en" is an exeption as it is the default language and may not // have a translation file provided. qDebug() << Q_FUNC_INFO << "Using" << lang << "translation"; setProperty("ui_lang", lang); const QString qtTrPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath); const QString qtTrFile = QLatin1String("qt_") + lang; if (qtTranslator->load(qtTrFile, qtTrPath)) { qtTranslator->load(qtTrFile, trPath); } const QString qtkeychainFile = QLatin1String("qt_") + lang; if (!qtkeychainTranslator->load(qtkeychainFile, qtTrPath)) { qtkeychainTranslator->load(qtkeychainFile, trPath); } if (!translator->isEmpty()) installTranslator(translator); if (!qtTranslator->isEmpty()) installTranslator(qtTranslator); if (!qtkeychainTranslator->isEmpty()) installTranslator(qtkeychainTranslator); break; } if (property("ui_lang").isNull()) setProperty("ui_lang", "C"); } } bool Application::giveHelp() { return _helpOnly; } } // namespace Mirall