PageRenderTime 61ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/src/mirall/application.cpp

https://github.com/morten-stickybeat/mirall
C++ | 915 lines | 708 code | 138 blank | 69 comment | 108 complexity | 1d170a87c51b82aae2f23b463653e081 MD5 | raw file
Possible License(s): GPL-2.0
  1. /*
  2. * Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  11. * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  12. * for more details.
  13. */
  14. #define LOG_TO_CALLBACK // FIXME: This should be in csync.
  15. #include <iostream>
  16. #include "mirall/application.h"
  17. #include "mirall/folder.h"
  18. #include "mirall/folderwatcher.h"
  19. #include "mirall/folderwizard.h"
  20. #include "mirall/networklocation.h"
  21. #include "mirall/unisonfolder.h"
  22. #include "mirall/owncloudfolder.h"
  23. #include "mirall/statusdialog.h"
  24. #include "mirall/owncloudsetupwizard.h"
  25. #include "mirall/owncloudinfo.h"
  26. #include "mirall/sslerrordialog.h"
  27. #include "mirall/theme.h"
  28. #include "mirall/mirallconfigfile.h"
  29. #include "mirall/updatedetector.h"
  30. #include "mirall/proxydialog.h"
  31. #include "mirall/version.h"
  32. #ifdef WITH_CSYNC
  33. #include "mirall/csyncfolder.h"
  34. #endif
  35. #include "mirall/inotify.h"
  36. #include <csync.h>
  37. #include <QtCore>
  38. #include <QtGui>
  39. #include <QHash>
  40. #include <QHashIterator>
  41. #include <QUrl>
  42. #include <QDesktopServices>
  43. #include <QTranslator>
  44. #include <QNetworkProxy>
  45. #include <QNetworkProxyFactory>
  46. namespace Mirall {
  47. // application logging handler.
  48. void mirallLogCatcher(QtMsgType type, const char *msg)
  49. {
  50. Q_UNUSED(type)
  51. Logger::instance()->mirallLog( QString::fromUtf8(msg) );
  52. }
  53. void csyncLogCatcher(const char *msg)
  54. {
  55. Logger::instance()->csyncLog( QString::fromUtf8(msg) );
  56. }
  57. // ----------------------------------------------------------------------------------
  58. Application::Application(int &argc, char **argv) :
  59. SharedTools::QtSingleApplication(argc, argv),
  60. _tray(0),
  61. _sslErrorDialog(0),
  62. #if QT_VERSION >= 0x040700
  63. _networkMgr(new QNetworkConfigurationManager(this)),
  64. #endif
  65. _contextMenu(0),
  66. _theme(Theme::instance()),
  67. _updateDetector(0),
  68. _helpOnly(false)
  69. {
  70. setApplicationName( _theme->appName() );
  71. setWindowIcon( _theme->applicationIcon() );
  72. if( arguments().contains(QLatin1String("--help"))) {
  73. showHelp();
  74. }
  75. setupLogBrowser();
  76. processEvents();
  77. QTranslator *qtTranslator = new QTranslator(this);
  78. qtTranslator->load(QLatin1String("qt_") + QLocale::system().name(),
  79. QLibraryInfo::location(QLibraryInfo::TranslationsPath));
  80. installTranslator(qtTranslator);
  81. QTranslator *mirallTranslator = new QTranslator(this);
  82. #ifdef Q_OS_LINUX
  83. // FIXME - proper path!
  84. mirallTranslator->load(QLatin1String("mirall_") + QLocale::system().name(), QLatin1String("/usr/share/mirall/i18n/"));
  85. #endif
  86. #ifdef Q_OS_MAC
  87. mirallTranslator->load(QLatin1String("mirall_") + QLocale::system().name(), applicationDirPath()+QLatin1String("/../translations") ); // path defaults to app dir.
  88. #endif
  89. #ifdef Q_OS_WIN32
  90. mirallTranslator->load(QLatin1String("mirall_") + QLocale::system().name(), applicationDirPath());
  91. #endif
  92. installTranslator(mirallTranslator);
  93. // create folder manager for sync folder management
  94. _folderMan = new FolderMan(this);
  95. connect( _folderMan, SIGNAL(folderSyncStateChange(QString)),
  96. this,SLOT(slotSyncStateChange(QString)));
  97. /* use a signal mapper to map the open requests to the alias names */
  98. _folderOpenActionMapper = new QSignalMapper(this);
  99. connect(_folderOpenActionMapper, SIGNAL(mapped(const QString &)),
  100. this, SLOT(slotFolderOpenAction(const QString &)));
  101. setQuitOnLastWindowClosed(false);
  102. _folderWizard = new FolderWizard;
  103. _owncloudSetupWizard = new OwncloudSetupWizard( _folderMan, _theme, this );
  104. connect( _owncloudSetupWizard, SIGNAL(ownCloudWizardDone(int)), SLOT(slotStartFolderSetup(int)));
  105. _statusDialog = new StatusDialog( _theme );
  106. connect( _statusDialog, SIGNAL(addASync()), this, SLOT(slotAddFolder()) );
  107. connect( _statusDialog, SIGNAL(removeFolderAlias( const QString&)),
  108. SLOT(slotRemoveFolder(const QString&)));
  109. connect( _statusDialog, SIGNAL(openLogBrowser()), this, SLOT(slotOpenLogBrowser()));
  110. connect( _statusDialog, SIGNAL(enableFolderAlias(QString,bool)),
  111. SLOT(slotEnableFolder(QString,bool)));
  112. connect( _statusDialog, SIGNAL(infoFolderAlias(const QString&)),
  113. SLOT(slotInfoFolder( const QString&)));
  114. connect( _statusDialog, SIGNAL(openFolderAlias(const QString&)),
  115. SLOT(slotFolderOpenAction(QString)));
  116. #if QT_VERSION >= 0x040700
  117. qDebug() << "* Network is" << (_networkMgr->isOnline() ? "online" : "offline");
  118. foreach (const QNetworkConfiguration& netCfg, _networkMgr->allConfigurations(QNetworkConfiguration::Active)) {
  119. //qDebug() << "Network:" << netCfg.identifier();
  120. }
  121. #endif
  122. setupActions();
  123. setupSystemTray();
  124. setupProxy();
  125. processEvents();
  126. QObject::connect( this, SIGNAL(messageReceived(QString)),
  127. this, SLOT(slotOpenStatus()) );
  128. QTimer::singleShot( 0, this, SLOT( slotStartFolderSetup() ));
  129. MirallConfigFile cfg;
  130. if( !cfg.ownCloudSkipUpdateCheck() ) {
  131. QTimer::singleShot( 3000, this, SLOT( slotStartUpdateDetector() ));
  132. }
  133. connect( ownCloudInfo::instance(), SIGNAL(sslFailed(QNetworkReply*, QList<QSslError>)),
  134. this,SLOT(slotSSLFailed(QNetworkReply*, QList<QSslError>)));
  135. qDebug() << "Network Location: " << NetworkLocation::currentLocation().encoded();
  136. }
  137. Application::~Application()
  138. {
  139. delete _tray; // needed, see ctor
  140. qDebug() << "* Mirall shutdown";
  141. }
  142. void Application::slotStartUpdateDetector()
  143. {
  144. _updateDetector = new UpdateDetector(this);
  145. _updateDetector->versionCheck(_theme);
  146. }
  147. void Application::slotStartFolderSetup( int result )
  148. {
  149. if( result == QDialog::Accepted ) {
  150. if( ownCloudInfo::instance()->isConfigured() ) {
  151. connect( ownCloudInfo::instance(),SIGNAL(ownCloudInfoFound(QString,QString,QString,QString)),
  152. SLOT(slotOwnCloudFound(QString,QString,QString,QString)));
  153. connect( ownCloudInfo::instance(),SIGNAL(noOwncloudFound(QNetworkReply*)),
  154. SLOT(slotNoOwnCloudFound(QNetworkReply*)));
  155. connect( ownCloudInfo::instance(),SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
  156. this,SLOT(slotAuthCheck(QString,QNetworkReply*)));
  157. ownCloudInfo::instance()->checkInstallation();
  158. } else {
  159. QMessageBox::warning(0, tr("No %1 Configuration").arg(_theme->appName()),
  160. tr("<p>No server connection has been configured for this %1 client.</p>"
  161. "<p>Please right click on the %1 system tray icon and select <i>Configure</i> "
  162. "to connect this client to an %1 server.</p>").arg(_theme->appName()));
  163. // It was evaluated to open the config dialog from here directly but decided
  164. // against because the user does not know why. The popup gives a better user
  165. // guidance, even if its a click more.
  166. }
  167. } else {
  168. qDebug() << "Setup Wizard was canceled. No reparsing of config.";
  169. }
  170. }
  171. void Application::slotOwnCloudFound( const QString& url, const QString& versionStr, const QString& version, const QString& edition)
  172. {
  173. qDebug() << "** Application: ownCloud found: " << url << " with version " << versionStr << "(" << version << ")";
  174. // now check the authentication
  175. MirallConfigFile cfgFile;
  176. cfgFile.setOwnCloudVersion( version );
  177. // disconnect from ownCloudInfo
  178. disconnect( ownCloudInfo::instance(),SIGNAL(ownCloudInfoFound(QString,QString,QString,QString)),
  179. this, SLOT(slotOwnCloudFound(QString,QString,QString,QString)));
  180. disconnect( ownCloudInfo::instance(),SIGNAL(noOwncloudFound(QNetworkReply*)),
  181. this, SLOT(slotNoOwnCloudFound(QNetworkReply*)));
  182. if( version.startsWith("4.0") ) {
  183. QMessageBox::warning(0, tr("%1 Server Mismatch").arg(_theme->appName()),
  184. tr("<p>The configured server for this client is too old.</p>"
  185. "<p>Please update to the latest %1 server and restart the client.</p>").arg(_theme->appName()));
  186. return;
  187. }
  188. QTimer::singleShot( 0, this, SLOT( slotCheckAuthentication() ));
  189. }
  190. void Application::slotNoOwnCloudFound( QNetworkReply* reply )
  191. {
  192. qDebug() << "** Application: NO ownCloud found!";
  193. QString msg;
  194. if( reply ) {
  195. QString url( reply->url().toString() );
  196. url.remove( QLatin1String("/status.php") );
  197. msg = tr("<p>The %1 at %2 could not be reached.</p>").arg(_theme->appName()).arg( url );
  198. msg += tr("<p>The detailed error message is<br/><tt>%1</tt></p>").arg( reply->errorString() );
  199. }
  200. msg += tr("<p>Please check your configuration by clicking on the tray icon.</p>");
  201. QMessageBox::warning(0, tr("%1 Connection Failed").arg(_theme->appName()), msg );
  202. _actionAddFolder->setEnabled( false );
  203. // Disconnect.
  204. disconnect( ownCloudInfo::instance(),SIGNAL(ownCloudInfoFound(QString,QString,QString,QString)),
  205. this, SLOT(slotOwnCloudFound(QString,QString,QString,QString)));
  206. disconnect( ownCloudInfo::instance(),SIGNAL(noOwncloudFound(QNetworkReply*)),
  207. this, SLOT(slotNoOwnCloudFound(QNetworkReply*)));
  208. disconnect( ownCloudInfo::instance(),SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
  209. this,SLOT(slotAuthCheck(QString,QNetworkReply*)));
  210. setupContextMenu();
  211. }
  212. void Application::slotCheckAuthentication()
  213. {
  214. qDebug() << "# checking for authentication settings.";
  215. ownCloudInfo::instance()->getRequest(QLatin1String("/"), true ); // this call needs to be authenticated.
  216. // simply GET the webdav root, will fail if credentials are wrong.
  217. // continue in slotAuthCheck here :-)
  218. }
  219. void Application::slotAuthCheck( const QString& ,QNetworkReply *reply )
  220. {
  221. if( reply->error() == QNetworkReply::AuthenticationRequiredError ) { // returned if the user is wrong.
  222. qDebug() << "******** Password is wrong!";
  223. QMessageBox::warning(0, tr("No %1 Connection").arg(_theme->appName()),
  224. tr("<p>Your %1 credentials are not correct.</p>"
  225. "<p>Please correct them by starting the configuration dialog from the tray!</p>")
  226. .arg(_theme->appName()));
  227. _actionAddFolder->setEnabled( false );
  228. } else if( reply->error() == QNetworkReply::OperationCanceledError ) {
  229. // the username was wrong and ownCloudInfo was closing the request after a couple of auth tries.
  230. qDebug() << "******** Username or password is wrong!";
  231. QMessageBox::warning(0, tr("No %1 Connection").arg(_theme->appName()),
  232. tr("<p>Either your user name or your password are not correct.</p>"
  233. "<p>Please correct it by starting the configuration dialog from the tray!</p>"));
  234. _actionAddFolder->setEnabled( false );
  235. } else {
  236. qDebug() << "######## Credentials are ok!";
  237. int cnt = _folderMan->setupFolders();
  238. if( cnt ) {
  239. _tray->setIcon(_theme->applicationIcon());
  240. _tray->show();
  241. processEvents();
  242. if( _tray )
  243. _tray->showMessage(tr("%1 Sync Started").arg(_theme->appName()),
  244. tr("Sync started for %1 configured sync folder(s).").arg(cnt));
  245. _statusDialog->setFolderList( _folderMan->map() );
  246. }
  247. _actionAddFolder->setEnabled( true );
  248. }
  249. // disconnect from ownCloud Info signals
  250. disconnect( ownCloudInfo::instance(),SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
  251. this,SLOT(slotAuthCheck(QString,QNetworkReply*)));
  252. setupContextMenu();
  253. }
  254. void Application::slotSSLFailed( QNetworkReply *reply, QList<QSslError> errors )
  255. {
  256. qDebug() << "SSL-Warnings happened for url " << reply->url().toString();
  257. if( ownCloudInfo::instance()->certsUntrusted() ) {
  258. // User decided once to untrust. Honor this decision.
  259. qDebug() << "Untrusted by user decision, returning.";
  260. return;
  261. }
  262. QString configHandle = ownCloudInfo::instance()->configHandle(reply);
  263. // make the ssl dialog aware of the custom config. It loads known certs.
  264. if( ! _sslErrorDialog ) {
  265. _sslErrorDialog = new SslErrorDialog;
  266. }
  267. _sslErrorDialog->setCustomConfigHandle( configHandle );
  268. if( _sslErrorDialog->setErrorList( errors ) ) {
  269. // all ssl certs are known and accepted. We can ignore the problems right away.
  270. qDebug() << "Certs are already known and trusted, Warnings are not valid.";
  271. reply->ignoreSslErrors();
  272. } else {
  273. if( _sslErrorDialog->exec() == QDialog::Accepted ) {
  274. if( _sslErrorDialog->trustConnection() ) {
  275. reply->ignoreSslErrors();
  276. } else {
  277. // User does not want to trust.
  278. ownCloudInfo::instance()->setCertsUntrusted(true);
  279. }
  280. } else {
  281. ownCloudInfo::instance()->setCertsUntrusted(true);
  282. }
  283. }
  284. }
  285. void Application::setupActions()
  286. {
  287. _actionOpenoC = new QAction(tr("Open %1 in browser...").arg(_theme->appName()), this);
  288. QObject::connect(_actionOpenoC, SIGNAL(triggered(bool)), SLOT(slotOpenOwnCloud()));
  289. _actionOpenStatus = new QAction(tr("Open status..."), this);
  290. QObject::connect(_actionOpenStatus, SIGNAL(triggered(bool)), SLOT(slotOpenStatus()));
  291. _actionAddFolder = new QAction(tr("Add folder..."), this);
  292. QObject::connect(_actionAddFolder, SIGNAL(triggered(bool)), SLOT(slotAddFolder()));
  293. _actionConfigure = new QAction(tr("Configure..."), this);
  294. QObject::connect(_actionConfigure, SIGNAL(triggered(bool)), SLOT(slotConfigure()));
  295. _actionConfigureProxy = new QAction(tr("Configure proxy..."), this);
  296. QObject::connect(_actionConfigureProxy, SIGNAL(triggered(bool)), SLOT(slotConfigureProxy()));
  297. _actionAbout = new QAction(tr("About..."), this);
  298. QObject::connect(_actionAbout, SIGNAL(triggered(bool)), SLOT(slotAbout()));
  299. _actionQuit = new QAction(tr("Quit"), this);
  300. QObject::connect(_actionQuit, SIGNAL(triggered(bool)), SLOT(quit()));
  301. }
  302. void Application::setupSystemTray()
  303. {
  304. // Setting a parent heres will crash on X11 since by the time qapp runs
  305. // its childrens dtors, the X11->screen variable queried for is gone -> crash
  306. _tray = new QSystemTrayIcon;
  307. _tray->setIcon( _theme->applicationIcon() ); // load the grey icon
  308. connect(_tray,SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
  309. SLOT(slotTrayClicked(QSystemTrayIcon::ActivationReason)));
  310. setupContextMenu();
  311. _tray->show();
  312. }
  313. void Application::setupContextMenu()
  314. {
  315. if( _contextMenu ) {
  316. _contextMenu->clear();
  317. } else {
  318. _contextMenu = new QMenu();
  319. }
  320. _contextMenu->setTitle(_theme->appName() );
  321. _contextMenu->addAction(_actionOpenStatus);
  322. _contextMenu->addAction(_actionOpenoC);
  323. _contextMenu->addSeparator();
  324. int folderCnt = _folderMan->map().size();
  325. // add open actions for all sync folders to the tray menu
  326. if( _theme->singleSyncFolder() ) {
  327. if( folderCnt == 0 ) {
  328. // if there is no folder configured yet, show the add action.
  329. _contextMenu->addAction(_actionAddFolder);
  330. } else {
  331. // there should be exactly one folder. No sync-folder add action will be shown.
  332. QStringList li = _folderMan->map().keys();
  333. if( li.size() == 1 ) {
  334. Folder *folder = _folderMan->map().value(li.first());
  335. if( folder ) {
  336. // if there is singleFolder mode, a generic open action is displayed.
  337. QAction *action = new QAction( tr("Open %1 folder").arg(_theme->appName()), this);
  338. action->setIcon( _theme->trayFolderIcon( folder->backend()) );
  339. connect( action, SIGNAL(triggered()),_folderOpenActionMapper,SLOT(map()));
  340. _folderOpenActionMapper->setMapping( action, folder->alias() );
  341. _contextMenu->addAction(action);
  342. }
  343. }
  344. }
  345. } else {
  346. // show a grouping with more than one folder.
  347. if ( folderCnt ) {
  348. _contextMenu->addAction(tr("Managed Folders:"))->setDisabled(true);
  349. }
  350. foreach (Folder *folder, _folderMan->map() ) {
  351. QAction *action = new QAction( folder->alias(), this );
  352. action->setIcon( _theme->trayFolderIcon( folder->backend()) );
  353. connect( action, SIGNAL(triggered()),_folderOpenActionMapper,SLOT(map()));
  354. _folderOpenActionMapper->setMapping( action, folder->alias() );
  355. _contextMenu->addAction(action);
  356. }
  357. _contextMenu->addAction(_actionAddFolder);
  358. }
  359. _contextMenu->addSeparator();
  360. _contextMenu->addAction(_actionConfigure);
  361. _contextMenu->addAction(_actionConfigureProxy);
  362. _contextMenu->addSeparator();
  363. _contextMenu->addAction(_actionAbout);
  364. _contextMenu->addSeparator();
  365. _contextMenu->addAction(_actionQuit);
  366. _tray->setContextMenu(_contextMenu);
  367. }
  368. void Application::setupLogBrowser()
  369. {
  370. // init the log browser.
  371. _logBrowser = new LogBrowser;
  372. qInstallMsgHandler( mirallLogCatcher );
  373. csync_set_log_callback( csyncLogCatcher );
  374. if( arguments().contains(QLatin1String("--logwindow"))
  375. || arguments().contains(QLatin1String("-l"))) {
  376. slotOpenLogBrowser();
  377. }
  378. // check for command line option for a log file.
  379. int lf = arguments().indexOf(QLatin1String("--logfile"));
  380. if( lf > -1 && lf+1 < arguments().count() ) {
  381. QString logfile = arguments().at( lf+1 );
  382. bool flush = false;
  383. if( arguments().contains(QLatin1String("--logflush"))) flush = true;
  384. qDebug() << "Logging into logfile: " << logfile << " with flush " << flush;
  385. _logBrowser->setLogFile( logfile, flush );
  386. }
  387. qDebug() << QString::fromLatin1( "################## %1 %2 %3 ").arg(_theme->appName())
  388. .arg( QLocale::system().name() )
  389. .arg(_theme->version());
  390. }
  391. void Application::setupProxy()
  392. {
  393. //
  394. Mirall::MirallConfigFile cfg;
  395. int proxy = cfg.proxyType();
  396. switch(proxy) {
  397. case QNetworkProxy::NoProxy: {
  398. QNetworkProxy::setApplicationProxy(QNetworkProxy::NoProxy);
  399. break;
  400. }
  401. case QNetworkProxy::DefaultProxy: {
  402. QNetworkProxyFactory::setUseSystemConfiguration(true);
  403. break;
  404. }
  405. case QNetworkProxy::Socks5Proxy: {
  406. proxy = QNetworkProxy::HttpProxy;
  407. cfg.setProxyType(proxy);
  408. // fall through
  409. }
  410. case QNetworkProxy::HttpProxy:{
  411. QNetworkProxy proxy;
  412. proxy.setType(QNetworkProxy::HttpProxy);
  413. proxy.setHostName(cfg.proxyHostName());
  414. proxy.setPort(cfg.proxyPort());
  415. proxy.setUser(cfg.proxyUser());
  416. proxy.setPassword(cfg.proxyPassword());
  417. QNetworkProxy::setApplicationProxy(proxy);
  418. break;
  419. }
  420. }
  421. }
  422. /*
  423. * open the folder with the given Alais
  424. */
  425. void Application::slotFolderOpenAction( const QString& alias )
  426. {
  427. Folder *f = _folderMan->folder(alias);
  428. qDebug() << "opening local url " << f->path();
  429. if( f ) {
  430. QUrl url(f->path(), QUrl::TolerantMode);
  431. url.setScheme( QLatin1String("file") );
  432. #ifdef Q_OS_WIN32
  433. // work around a bug in QDesktopServices on Win32, see i-net
  434. QString filePath = f->path();
  435. if (filePath.startsWith(QLatin1String("\\\\")) || filePath.startsWith(QLatin1String("//")))
  436. url.setUrl(QDir::toNativeSeparators(filePath));
  437. else
  438. url = QUrl::fromLocalFile(filePath);
  439. #endif
  440. QDesktopServices::openUrl(url);
  441. }
  442. }
  443. void Application::slotOpenOwnCloud()
  444. {
  445. MirallConfigFile cfgFile;
  446. QString url = cfgFile.ownCloudUrl();
  447. QDesktopServices::openUrl( url );
  448. }
  449. void Application::slotTrayClicked( QSystemTrayIcon::ActivationReason reason )
  450. {
  451. // A click on the tray icon should only open the status window on Win and
  452. // Linux, not on Mac. They want a menu entry.
  453. #if defined Q_WS_WIN || defined Q_WS_X11
  454. if( reason == QSystemTrayIcon::Trigger ) {
  455. slotOpenStatus();
  456. }
  457. #endif
  458. }
  459. void Application::slotAddFolder()
  460. {
  461. _folderMan->disableFoldersWithRestore();
  462. Folder::Map folderMap = _folderMan->map();
  463. _folderWizard->setFolderMap( &folderMap );
  464. _folderWizard->restart();
  465. if (_folderWizard->exec() == QDialog::Accepted) {
  466. qDebug() << "* Folder wizard completed";
  467. bool goodData = true;
  468. QString alias = _folderWizard->field(QLatin1String("alias")).toString();
  469. QString sourceFolder = _folderWizard->field(QLatin1String("sourceFolder")).toString();
  470. QString backend = QLatin1String("csync");
  471. QString targetPath;
  472. bool onlyThisLAN = false;
  473. bool onlyOnline = false;
  474. if (_folderWizard->field(QLatin1String("local?")).toBool()) {
  475. // setup a local csync folder
  476. targetPath = _folderWizard->field(QLatin1String("targetLocalFolder")).toString();
  477. } else if (_folderWizard->field(QLatin1String("remote?")).toBool()) {
  478. // setup a remote csync folder
  479. targetPath = _folderWizard->field(QLatin1String("targetURLFolder")).toString();
  480. onlyOnline = _folderWizard->field(QLatin1String("onlyOnline?")).toBool();
  481. onlyThisLAN = _folderWizard->field(QLatin1String("onlyThisLAN?")).toBool();
  482. } else if( _folderWizard->field(QLatin1String("OC?")).toBool()) {
  483. // setup a ownCloud folder
  484. backend = QLatin1String("owncloud");
  485. targetPath = _folderWizard->field(QLatin1String("targetOCFolder")).toString();
  486. } else {
  487. qWarning() << "* Folder not local and note remote?";
  488. goodData = false;
  489. }
  490. if( goodData ) {
  491. _folderMan->addFolderDefinition( backend, alias, sourceFolder, targetPath, onlyThisLAN );
  492. Folder *f = _folderMan->setupFolderFromConfigFile( alias );
  493. if( f ) {
  494. _statusDialog->slotAddFolder( f );
  495. _statusDialog->buttonsSetEnabled();
  496. setupContextMenu();
  497. }
  498. }
  499. } else {
  500. qDebug() << "* Folder wizard cancelled";
  501. }
  502. _folderMan->restoreEnabledFolders();
  503. }
  504. void Application::slotOpenStatus()
  505. {
  506. if( ! _statusDialog ) return;
  507. QWidget *raiseWidget = 0;
  508. // check if there is a mirall.cfg already.
  509. if( _owncloudSetupWizard->wizard()->isVisible() ) {
  510. raiseWidget = _owncloudSetupWizard->wizard();
  511. }
  512. // if no config file is there, start the configuration wizard.
  513. if( ! raiseWidget ) {
  514. MirallConfigFile cfgFile;
  515. if( !cfgFile.exists() ) {
  516. qDebug() << "No configured folders yet, start the Owncloud integration dialog.";
  517. _owncloudSetupWizard->startWizard();
  518. } else {
  519. qDebug() << "#============# Status dialog starting #=============#";
  520. raiseWidget = _statusDialog;
  521. _statusDialog->setFolderList( _folderMan->map() );
  522. }
  523. }
  524. // viel hilft viel ;-)
  525. if( raiseWidget ) {
  526. #if defined(Q_WS_WIN) || defined (Q_OS_MAC)
  527. Qt::WindowFlags eFlags = raiseWidget->windowFlags();
  528. eFlags |= Qt::WindowStaysOnTopHint;
  529. raiseWidget->setWindowFlags(eFlags);
  530. raiseWidget->show();
  531. eFlags &= ~Qt::WindowStaysOnTopHint;
  532. raiseWidget->setWindowFlags(eFlags);
  533. #endif
  534. raiseWidget->show();
  535. raiseWidget->raise();
  536. raiseWidget->activateWindow();
  537. }
  538. }
  539. void Application::slotOpenLogBrowser()
  540. {
  541. _logBrowser->show();
  542. _logBrowser->raise();
  543. }
  544. void Application::slotAbout()
  545. {
  546. QMessageBox::about(0, tr("About %1").arg(_theme->appName()),
  547. tr("%1 client, version %2\n\nCopyright 2012, the ownCloud developers\n\nLicensed under GPLv2\n\n"
  548. "The program is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN,"
  549. "MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.")
  550. .arg(_theme->appName())
  551. .arg(MIRALL_STRINGIFY(MIRALL_VERSION)));
  552. }
  553. /*
  554. * the folder is to be removed. The slot is called from a signal emitted by
  555. * the status dialog, which removes the folder from its list by itself.
  556. */
  557. void Application::slotRemoveFolder( const QString& alias )
  558. {
  559. int ret = QMessageBox::question( 0, tr("Confirm Folder Remove"),
  560. tr("Do you really want to remove upload folder <i>%1</i>?").arg(alias),
  561. QMessageBox::Yes|QMessageBox::No );
  562. if( ret == QMessageBox::No ) {
  563. return;
  564. }
  565. Folder *f = _folderMan->folder(alias);
  566. if( f && _overallStatusStrings.contains( f->alias() )) {
  567. _overallStatusStrings.remove( f->alias() );
  568. }
  569. _folderMan->slotRemoveFolder( alias );
  570. _statusDialog->slotRemoveSelectedFolder( );
  571. computeOverallSyncStatus();
  572. setupContextMenu();
  573. }
  574. void Application::slotInfoFolder( const QString& alias )
  575. {
  576. qDebug() << "details of folder with alias " << alias;
  577. SyncResult folderResult = _folderMan->syncResult( alias );
  578. bool enabled = true;
  579. Folder *f = _folderMan->folder( alias );
  580. if( f && ! f->syncEnabled() ) {
  581. enabled = false;
  582. }
  583. QString folderMessage;
  584. SyncResult::Status syncStatus = folderResult.status();
  585. switch( syncStatus ) {
  586. case SyncResult::Undefined:
  587. folderMessage = tr( "Undefined Folder State" );
  588. break;
  589. case SyncResult::NotYetStarted:
  590. folderMessage = tr( "The folder waits to start syncing." );
  591. break;
  592. case SyncResult::SyncRunning:
  593. folderMessage = tr("Sync is running.");
  594. break;
  595. case SyncResult::Success:
  596. folderMessage = tr("Last Sync was successful.");
  597. break;
  598. case SyncResult::Error:
  599. folderMessage = tr( "Syncing Error." );
  600. break;
  601. case SyncResult::SetupError:
  602. folderMessage = tr( "Setup Error." );
  603. break;
  604. default:
  605. folderMessage = tr( "Undefined Error State." );
  606. }
  607. folderMessage = QLatin1String("<b>") + folderMessage + QLatin1String("</b><br/>");
  608. QMessageBox infoBox( QMessageBox::Information, tr( "Folder information" ), alias, QMessageBox::Ok );
  609. QStringList li = folderResult.errorStrings();
  610. foreach( const QString& l, li ) {
  611. folderMessage += QString::fromLatin1("<p>%1</p>").arg( l );
  612. }
  613. infoBox.setText( folderMessage );
  614. // qDebug() << "informative text: " << infoBox.informativeText();
  615. if ( !folderResult.syncChanges().isEmpty() ) {
  616. QString details;
  617. QHash < QString, QStringList > changes = folderResult.syncChanges();
  618. QHash< QString, QStringList >::const_iterator change_it = changes.constBegin();
  619. for(; change_it != changes.constEnd(); ++change_it ) {
  620. QString changeType = tr( "Unknown" );
  621. if ( change_it.key() == QLatin1String("changed") ) {
  622. changeType = tr( "Changed files:\n" );
  623. } else if ( change_it.key() == QLatin1String("added") ) {
  624. changeType = tr( "Added files:\n" );
  625. } else if ( change_it.key() == QLatin1String("deleted") ) {
  626. changeType = tr( "New files in the server, or files deleted locally:\n");
  627. }
  628. QStringList files = change_it.value();
  629. QString fileList;
  630. foreach( const QString& file, files) {
  631. fileList += file + QLatin1Char('\n');
  632. }
  633. details += changeType + fileList;
  634. }
  635. infoBox.setDetailedText(details);
  636. qDebug() << "detailed text: " << infoBox.detailedText();
  637. }
  638. infoBox.exec();
  639. }
  640. void Application::slotEnableFolder(const QString& alias, const bool enable)
  641. {
  642. qDebug() << "Application: enable folder with alias " << alias;
  643. bool terminate = false;
  644. // this sets the folder status to disabled but does not interrupt it.
  645. Folder *f = _folderMan->folder( alias );
  646. if( f && !enable ) {
  647. // check if a sync is still running and if so, ask if we should terminate.
  648. if( f->isBusy() ) { // its still running
  649. int reply = QMessageBox::question( 0, tr("Sync Running"),
  650. tr("The syncing operation is running.<br/>Do you want to terminate it?"),
  651. QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes );
  652. if ( reply == QMessageBox::Yes )
  653. terminate = true;
  654. else
  655. return; // do nothing
  656. }
  657. }
  658. // message box can return at any time while the thread keeps running,
  659. // so better check again after the user has responded.
  660. if ( f->isBusy() && terminate )
  661. _folderMan->terminateSyncProcess( alias );
  662. _folderMan->slotEnableFolder( alias, enable );
  663. _statusDialog->slotUpdateFolderState( f );
  664. }
  665. void Application::slotConfigure()
  666. {
  667. _folderMan->disableFoldersWithRestore();
  668. _owncloudSetupWizard->startWizard();
  669. _folderMan->restoreEnabledFolders();
  670. }
  671. void Application::slotConfigureProxy()
  672. {
  673. ProxyDialog dlg;
  674. if (dlg.exec() == QDialog::Accepted)
  675. {
  676. setupProxy();
  677. }
  678. }
  679. void Application::slotSyncStateChange( const QString& alias )
  680. {
  681. SyncResult result = _folderMan->syncResult( alias );
  682. // do not promote LocalSyncState to the status dialog.
  683. if( !result.localRunOnly() ) {
  684. _statusDialog->slotUpdateFolderState( _folderMan->folder(alias) );
  685. }
  686. computeOverallSyncStatus();
  687. qDebug() << "Sync state changed for folder " << alias << ": " << result.statusString();
  688. }
  689. void Application::computeOverallSyncStatus()
  690. {
  691. // display the info of the least successful sync (eg. not just display the result of the latest sync
  692. SyncResult overallResult(SyncResult::Undefined );
  693. QString trayMessage;
  694. Folder::Map map = _folderMan->map();
  695. foreach ( Folder *syncedFolder, map.values() ) {
  696. QString folderMessage = _overallStatusStrings[syncedFolder->alias()];
  697. SyncResult folderResult = syncedFolder->syncResult();
  698. SyncResult::Status syncStatus = folderResult.status();
  699. if( ! folderResult.localRunOnly() ) { // skip local runs, use the last message.
  700. if( syncedFolder->syncEnabled() ) {
  701. switch( syncStatus ) {
  702. case SyncResult::Undefined:
  703. if ( overallResult.status() != SyncResult::Error ) {
  704. overallResult.setStatus(SyncResult::Error);
  705. }
  706. folderMessage = tr( "Undefined State." );
  707. break;
  708. case SyncResult::NotYetStarted:
  709. folderMessage = tr( "Waits to start syncing." );
  710. overallResult.setStatus( SyncResult::NotYetStarted );
  711. break;
  712. case SyncResult::SyncRunning:
  713. folderMessage = tr( "Sync is running." );
  714. overallResult.setStatus( SyncResult::SyncRunning );
  715. break;
  716. case SyncResult::Success:
  717. if( overallResult.status() == SyncResult::Undefined ) {
  718. folderMessage = tr( "Last Sync was successful." );
  719. overallResult.setStatus( SyncResult::Success );
  720. }
  721. break;
  722. case SyncResult::Error:
  723. overallResult.setStatus( SyncResult::Error );
  724. folderMessage = tr( "Syncing Error." );
  725. break;
  726. case SyncResult::SetupError:
  727. if ( overallResult.status() != SyncResult::Error ) {
  728. overallResult.setStatus( SyncResult::SetupError );
  729. }
  730. folderMessage = tr( "Setup Error." );
  731. break;
  732. default:
  733. folderMessage = tr( "Undefined Error State." );
  734. overallResult.setStatus( SyncResult::Error );
  735. }
  736. } else {
  737. // sync is disabled.
  738. folderMessage = tr( "Sync is paused." );
  739. }
  740. }
  741. qDebug() << "Folder in overallStatus Message: " << syncedFolder << " with name " << syncedFolder->alias();
  742. QString msg = QString::fromLatin1("Folder %1: %2").arg(syncedFolder->alias()).arg(folderMessage);
  743. if( msg != _overallStatusStrings[syncedFolder->alias()] ) {
  744. _overallStatusStrings[syncedFolder->alias()] = msg;
  745. }
  746. }
  747. // create the tray blob message, check if we have an defined state
  748. if( overallResult.status() != SyncResult::Undefined ) {
  749. QStringList allStatusStrings = _overallStatusStrings.values();
  750. if( ! allStatusStrings.isEmpty() )
  751. trayMessage = allStatusStrings.join(QLatin1String("\n"));
  752. else
  753. trayMessage = tr("No sync folders configured.");
  754. QIcon statusIcon = _theme->syncStateIcon( overallResult.status()); // size 48 before
  755. _tray->setIcon( statusIcon );
  756. _tray->setToolTip(trayMessage);
  757. }
  758. }
  759. void Application::showHelp()
  760. {
  761. std::cout << _theme->appName().toLatin1().constData() << " version " <<
  762. _theme->version().toLatin1().constData() << std::endl << std::endl;
  763. std::cout << "File synchronisation desktop utility." << std::endl << std::endl;
  764. std::cout << "Options:" << std::endl;
  765. std::cout << " --logwindow : open a window to show log output." << std::endl;
  766. std::cout << " --logfile <filename> : write log output to file <filename>." << std::endl;
  767. std::cout << " --flushlog : flush the log file after every write." << std::endl;
  768. std::cout << std::endl;
  769. if (_theme->appName() == QLatin1String("ownCloud"))
  770. std::cout << "For more information, see http://www.owncloud.org" << std::endl;
  771. _helpOnly = true;
  772. }
  773. bool Application::giveHelp()
  774. {
  775. return _helpOnly;
  776. }
  777. } // namespace Mirall