swift
application.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2016 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
4 #include "core/application.h"
5 
6 #include <cstdio>
7 #include <cstdlib>
8 
9 #include <QCoreApplication>
10 #include <QDateTime>
11 #include <QEventLoop>
12 #include <QFileInfo>
13 #include <QHttpMultiPart>
14 #include <QNetworkReply>
15 #include <QNetworkRequest>
16 #include <QProcess>
17 #include <QRegularExpression>
18 #include <QSslSocket>
19 #include <QStandardPaths>
20 #include <QStringBuilder>
21 #include <QStringList>
22 #include <QSysInfo>
23 #include <QTemporaryDir>
24 #include <QThread>
25 #include <QTimer>
26 #include <QWriteLocker>
27 #include <Qt>
28 #include <QtGlobal>
29 
30 #include "config/buildconfig.h"
35 #include "core/cookiemanager.h"
36 #include "core/corefacade.h"
37 #include "core/inputmanager.h"
38 #include "core/registermetadata.h"
39 #include "core/setupreader.h"
40 #include "core/webdataservices.h"
41 #include "misc/applicationinfo.h"
42 #include "misc/crashhandler.h"
43 #include "misc/datacache.h"
44 #include "misc/dbusserver.h"
45 #include "misc/eventloop.h"
46 #include "misc/filelogger.h"
47 #include "misc/loghandler.h"
48 #include "misc/logmessage.h"
49 #include "misc/logpattern.h"
51 #include "misc/registermetadata.h"
52 #include "misc/settingscache.h"
53 #include "misc/stringutils.h"
54 #include "misc/swiftdirectories.h"
55 #include "misc/threadutils.h"
56 #include "misc/verify.h"
57 
58 using namespace swift::config;
59 using namespace swift::misc;
60 using namespace swift::misc::db;
61 using namespace swift::misc::network;
62 using namespace swift::misc::aviation;
63 using namespace swift::misc::simulation;
64 using namespace swift::misc::weather;
65 using namespace swift::core;
66 using namespace swift::core::context;
67 using namespace swift::core::vatsim;
68 using namespace swift::core::data;
69 using namespace swift::core::db;
70 
71 swift::core::CApplication *sApp = nullptr; // set by constructor
72 
74 static const QString &swiftDataRoot()
75 {
76  static const QString path =
78  return path;
79 }
80 
81 namespace swift::core
82 {
83  CApplication::CApplication(CApplicationInfo::Application application, bool init)
84  : CApplication(executable(), application, init)
85  {}
86 
87  CApplication::CApplication(const QString &applicationName, CApplicationInfo::Application application, bool init)
88  : CIdentifiable(this), m_accessManager(new QNetworkAccessManager(this)), m_applicationInfo(application),
89  m_applicationName(applicationName), m_coreFacadeConfig(CCoreFacadeConfig::NotUsed)
90  {
91  Q_ASSERT_X(!sApp, Q_FUNC_INFO, "already initialized");
92  Q_ASSERT_X(QCoreApplication::instance(), Q_FUNC_INFO, "no application object");
93 
95  QCoreApplication::setApplicationName(m_applicationName);
96  QCoreApplication::setApplicationVersion(CBuildConfig::getVersionString());
97  this->setObjectName(m_applicationName);
98  this->thread()->setObjectName(
99  m_applicationName); // normally no effect as thread already runs, but does not harm either
100 
101  // init skipped when called from CGuiApplication
102  if (init) { this->init(true); }
103  }
104 
105  void CApplication::init(bool withMetadata)
106  {
107  if (!sApp)
108  {
109  // notify when app goes down
112 
113  // metadata
114  if (withMetadata) { CApplication::registerMetadata(); }
115 
116  // unit test
117  if (this->getApplicationInfo().getApplication() == CApplicationInfo::UnitTest)
118  {
121  }
123  CApplication::getApplicationInfo().getApplication());
124  this->initParser();
125  this->initLogging();
126  this->tagApplicationDataDirectory();
127 
128  //
129  // cmd line arguments not yet parsed here
130  //
131 
132  // main app
133  sApp = this;
134 
135  this->initNetwork();
136 
137  // global setup
138  m_setupReader.reset(new CSetupReader(this));
139 
140  // check for updates
141  m_gitHubPackagesReader.reset(new CGitHubPackagesReader(this));
142  connect(m_gitHubPackagesReader.data(), &CGitHubPackagesReader::updateInfoAvailable, this,
145 
146  // startup done
151 
152  if (!this->getApplicationInfo().isUnitTest())
153  {
154  m_inputManager = new CInputManager(this);
155  m_inputManager->createDevices();
156  }
157 
158  connect(this, &QObject::destroyed, [cat = CLogCategoryList(this)] {
159  for (CWorkerBase *worker : CWorkerBase::allWorkers())
160  {
161  CLogMessage(cat).debug(u"Worker named '%1' still exists after application destroyed")
162  << worker->objectName();
163  }
164  });
165  }
166  }
167 
169  {
173  if (!apps.contains(myself)) { apps.push_back(myself); }
174  const bool ok = CFileUtils::writeStringToLockedFile(apps.toJsonString(),
175  CFileUtils::appendFilePaths(swiftDataRoot(), "apps.json"));
176  if (!ok)
177  {
178  CLogMessage(static_cast<CApplication *>(nullptr)).error(u"Failed to write to application list file");
179  }
180  return ok;
181  }
182 
184  {
188  if (!apps.contains(myself)) { return true; }
189  apps.remove(myself);
190  const bool ok = CFileUtils::writeStringToLockedFile(apps.toJsonString(),
191  CFileUtils::appendFilePaths(swiftDataRoot(), "apps.json"));
192  if (!ok)
193  {
194  CLogMessage(static_cast<CApplication *>(nullptr)).error(u"Failed to write to application list file");
195  }
196  return ok;
197  }
198 
200  {
201  Q_ASSERT_X(instance(), Q_FUNC_INFO, "missing application");
202  emit this->startUpCompleted(true);
204  return QCoreApplication::exec();
205  }
206 
207  void CApplication::restartApplication(const QStringList &newArguments, const QStringList &removeArguments)
208  {
211  const QStringList args = this->argumentsJoined(newArguments, removeArguments);
212  this->gracefulShutdown();
213  QProcess::startDetached(prg, args);
215  }
216 
218 
220  {
222  apps.convertFromJsonNoThrow(CFileUtils::readLockedFileToString(swiftDataRoot() + "apps.json"), {}, {});
223  apps.removeIf([](const CApplicationInfo &info) { return !info.getProcessInfo().exists(); });
224  return apps;
225  }
226 
228  {
230  return running.containsApplication(application);
231  }
232 
234  {
235  return getRunningApplications().containsBy([this](const CApplicationInfo &info) {
236  return info.getApplication() == getApplicationInfo().getApplication();
237  });
238  }
239 
241 
242  bool CApplication::isIncognito() const { return m_incognito; }
243 
244  void CApplication::setIncognito(bool incognito) { m_incognito = incognito; }
245 
247 
249  {
250  static const QString s(m_applicationName % u" " % CBuildConfig::getVersionString());
251  return s;
252  }
253 
255  {
256  static const QString s(m_applicationName % u" " % this->versionStringDetailed());
257  return s;
258  }
259 
261  {
262  if (m_shutdown) { return CGlobalSetup(); }
263  const CSetupReader *r = m_setupReader.data();
264  if (!r) { return CGlobalSetup(); }
265  return r->getSetup();
266  }
267 
269  {
270  if (m_shutdown) { return CUpdateInfo(); }
271  if (!m_gitHubPackagesReader) { return CUpdateInfo(); }
272  return m_gitHubPackagesReader->getUpdateInfo();
273  }
274 
276  {
277  if (m_shutdown) { return; }
278  if (!m_gitHubPackagesReader) { return; }
279  m_gitHubPackagesReader->readUpdateInfo();
280  }
281 
283  {
284  if (CBuildConfig::isLocalDeveloperDebugBuild()) { return CDistribution::localDeveloperBuild(); }
285  const CUpdateInfo u = this->getUpdateInfo();
286  return u.anticipateOwnDistribution();
287  }
288 
290  {
291  m_started = false; // reset
292 
293  Q_ASSERT_X(m_parsed, Q_FUNC_INFO, "Call this function after parsing");
294 
295  // parsing itself is done
296  CStatusMessageList msgs;
297  do {
298  // clear cache?
299  if (this->isSet(m_cmdClearCache))
300  {
301  const QStringList files(CApplication::clearCaches());
302  msgs.push_back(CLogMessage(this).debug() << "Cleared cache, " << files.size() << " files");
303  }
304 
305  // crashpad dump
306  if (this->isSet(m_cmdTestCrashpad))
307  {
308  msgs.push_back(CLogMessage(this).info(u"About to simulate crash"));
309  QTimer::singleShot(10 * 1000, [=] {
310  if (!sApp || sApp->isShuttingDown()) { return; }
311  this->simulateCrash();
312  });
313  }
314 
315  Q_ASSERT_X(m_setupReader && m_setupReader->isSetupAvailable(), Q_FUNC_INFO, "Setup not available");
316 
317  // start hookin
318  msgs.push_back(this->startHookIn());
319  if (msgs.isFailure()) { break; }
320 
321  // Settings if not already initialized
322  msgs.push_back(this->initLocalSettings());
323  if (msgs.isFailure()) { break; }
324  }
325  while (false);
326 
327  // terminate with failures, otherwise log messages
328  if (msgs.isFailure())
329  {
330  this->cmdLineErrorMessage(msgs);
331  return false;
332  }
333  else if (!msgs.isEmpty()) { CLogMessage::preformatted(msgs); }
334 
335  m_started = true;
336  return m_started;
337  }
338 
340  {
341  if (m_shutdown || !m_setupReader) { return false; }
342  return m_setupReader->isSetupAvailable();
343  }
344 
346  {
347  return (this->getGlobalSetup().isSwiftVersionMinimumMappingVersion());
348  }
349 
351  {
352  if (this->isShuttingDown()) { return false; } // service will not survive for long
353  return !m_webDataServices.isNull();
354  }
355 
357  {
358  // use hasWebDataServices() to test if services are available
359  // getting the assert means web services are accessed before the are initialized
360 
361  Q_ASSERT_X(m_webDataServices, Q_FUNC_INFO,
362  "Missing web data services, use hasWebDataServices to test if existing");
363  return m_webDataServices.data();
364  }
365 
367  {
368  if (this->isDeveloperFlagSet() && CBuildConfig::isLocalDeveloperDebugBuild())
369  {
370  static const QString s(CBuildConfig::getVersionStringPlatform() % u" [dev,DEVDBG]");
371  return s;
372  }
373  if (isDeveloperFlagSet())
374  {
375  static const QString s(CBuildConfig::getVersionStringPlatform() % u" [dev]");
376  return s;
377  }
378  if (CBuildConfig::isLocalDeveloperDebugBuild())
379  {
380  static const QString s(CBuildConfig::getVersionStringPlatform() % u" [DEVDBG]");
381  return s;
382  }
383  return CBuildConfig::getVersionStringPlatform();
384  }
385 
387  {
388  static const QString s(QStringLiteral("swift %1").arg(versionStringDetailed()));
389  return s;
390  }
391 
393  {
394  static const QByteArray a(swiftVersionString().toUtf8());
395  return a.constData();
396  }
397 
398  bool CApplication::initIsRunningInDeveloperEnvironment() const
399  {
400  //
401  // assumption: restricted distributions are development versions
402  //
403 
404  if (this->getApplicationInfo().isSampleOrUnitTest()) { return true; }
405  if (CBuildConfig::isLocalDeveloperDebugBuild()) { return true; }
406 
407  const CDistribution d(this->getOwnDistribution());
408  if (d.isRestricted() && this->isSet(m_cmdDevelopment)) { return true; }
409 
410  return false;
411  }
412 
413  CStatusMessage CApplication::initLocalSettings()
414  {
415  if (m_localSettingsLoaded) { return CStatusMessage(); }
416  m_localSettingsLoaded = true;
417 
418  // trigger loading and saving of settings in appropriate scenarios
419  if (m_coreFacadeConfig.getMode() != CCoreFacadeConfig::Remote)
420  {
421  // facade running here locally
423  if (msg.isFailure()) { return msg; }
424 
425  // Settings are distributed via DBus. So only one application is responsible for saving. `enableLocalSave()`
426  // means "this is the application responsible for saving". If swiftgui requests a setting to be saved, it is
427  // sent to swiftcore and saved by swiftcore.
429  }
430  return CStatusMessage();
431  }
432 
434 
435  void CApplication::saveSettingsOnShutdown(bool saveSettings) { m_saveSettingsOnShutdown = saveSettings; }
436 
438  {
441  }
442 
444  {
445  if (keys.isEmpty()) { return CStatusMessage(); }
446  return this->supportsContexts() ? this->getIContextApplication()->saveSettingsByKey(keys) :
448  }
449 
451  {
452  static const QTemporaryDir tempDir;
453  if (tempDir.isValid()) { return tempDir.path(); }
454  return QDir::tempPath();
455  }
456 
458  {
459  QString str = CBuildConfig::getVersionString() % u" " %
460  (CBuildConfig::isReleaseBuild() ? u"Release build" : u"Debug build") % separator %
461  u"Local dev.dbg.: " % boolToYesNo(CBuildConfig::isLocalDeveloperDebugBuild()) % separator %
462  u"dev.env.: " % boolToYesNo(this->isDeveloperFlagSet()) % separator % u"distribution: " %
463  this->getOwnDistribution().toQString(true) % separator % u"Windows NT: " %
464  boolToYesNo(CBuildConfig::isRunningOnWindowsNtPlatform()) % separator % u"Linux: " %
465  boolToYesNo(CBuildConfig::isRunningOnLinuxPlatform()) % " Unix: " %
466  boolToYesNo(CBuildConfig::isRunningOnUnixPlatform()) % separator % u"MacOS: " %
467  boolToYesNo(CBuildConfig::isRunningOnMacOSPlatform()) % separator % "Build Abi: " %
468  QSysInfo::buildAbi() % separator % u"Build CPU: " % QSysInfo::buildCpuArchitecture() % separator %
469  CBuildConfig::compiledWithInfo();
470 
471  if (this->supportsContexts()) { str += (separator % u"Supporting contexts"); }
472 
473  return str;
474  }
475 
477  int maxRedirects)
478  {
479  const CApplication::ProgressSlot progress;
480  return this->getFromNetwork(url, callback, progress, maxRedirects);
481  }
482 
484  const CApplication::ProgressSlot &progress, int maxRedirects)
485  {
486  return this->getFromNetwork(url.toNetworkRequest(), NoLogRequestId, callback, progress, maxRedirects);
487  }
488 
490  const CApplication::ProgressSlot &progress, int maxRedirects)
491  {
492  return this->getFromNetwork(url.toNetworkRequest(), logId, callback, progress, maxRedirects);
493  }
494 
496  const CApplication::CallbackSlot &callback, int maxRedirects)
497  {
498  const CApplication::ProgressSlot progress;
499  return this->getFromNetwork(request, callback, progress, maxRedirects);
500  }
501 
503  const CApplication::CallbackSlot &callback,
504  const CApplication::ProgressSlot &progress, int maxRedirects)
505  {
506  return this->getFromNetwork(request, NoLogRequestId, callback, progress, maxRedirects);
507  }
508 
510  const CApplication::CallbackSlot &callback,
511  const CApplication::ProgressSlot &progress, int maxRedirects)
512  {
513  return this->httpRequestImpl(request, logId, callback, progress, maxRedirects,
514  [](QNetworkAccessManager &qam, const QNetworkRequest &request) {
515  QNetworkReply *nr = qam.get(request);
516  return nr;
517  });
518  }
519 
521  const CApplication::CallbackSlot &callback, int maxRedirects)
522  {
523  const CApplication::ProgressSlot progress;
524  return this->httpRequestImpl(request, logId, callback, progress, maxRedirects,
525  [](QNetworkAccessManager &qam, const QNetworkRequest &request) {
526  QNetworkReply *nr = qam.deleteResource(request);
527  return nr;
528  });
529  }
530 
531  QNetworkReply *CApplication::postToNetwork(const QNetworkRequest &request, int logId, const QByteArray &data,
532  const CSlot<void(QNetworkReply *)> &callback)
533  {
534  return this->httpRequestImpl(request, logId, callback, NoRedirects,
535  [data](QNetworkAccessManager &qam, const QNetworkRequest &request) {
536  QNetworkReply *nr = qam.post(request, data);
537  return nr;
538  });
539  }
540 
542  const CSlot<void(QNetworkReply *)> &callback)
543  {
544  if (multiPart->thread() != m_accessManager->thread()) { multiPart->moveToThread(m_accessManager->thread()); }
545 
546  QPointer<CApplication> myself(this);
547  return httpRequestImpl(request, logId, callback, NoRedirects,
548  [=](QNetworkAccessManager &qam, const QNetworkRequest &request) {
549  QNetworkReply *nr = nullptr;
550  if (!myself) { return nr; }
551  if (!multiPart) { return nr; }
552  nr = qam.post(request, multiPart);
553  multiPart->setParent(nr);
554  return nr;
555  });
556  }
557 
558  QNetworkReply *CApplication::headerFromNetwork(const CUrl &url, const CallbackSlot &callback, int maxRedirects)
559  {
560  return headerFromNetwork(url.toNetworkRequest(), callback, maxRedirects);
561  }
562 
564  int maxRedirects)
565  {
566  return httpRequestImpl(
567  request, NoLogRequestId, callback, maxRedirects,
568  [](QNetworkAccessManager &qam, const QNetworkRequest &request) { return qam.head(request); });
569  }
570 
571  QNetworkReply *CApplication::downloadFromNetwork(const CUrl &url, const QString &saveAsFileName,
572  const CSlot<void(const CStatusMessage &)> &callback,
573  int maxRedirects)
574  {
575  // upfront checks
576  if (url.isEmpty()) { return nullptr; }
577  if (saveAsFileName.isEmpty()) { return nullptr; }
578  const QFileInfo fi(saveAsFileName);
579  if (!fi.dir().exists()) { return nullptr; }
580 
581  // function called with reply when done
582  CallbackSlot callbackSlot(this, [=](QNetworkReply *reply) {
584  CStatusMessage msg;
585  if (reply->error() != QNetworkReply::NoError)
586  {
587  msg = CStatusMessage(this, CStatusMessage::SeverityError, u"Download for '%1' failed: '%2'")
588  << url.getFullUrl() << nwReply->errorString();
589  }
590  else
591  {
592  const bool ok = CFileUtils::writeByteArrayToFile(reply->readAll(), saveAsFileName);
593  msg = ok ? CStatusMessage(this, CStatusMessage::SeverityInfo, u"Saved file '%1' downloaded from '%2'")
594  << saveAsFileName << url.getFullUrl() :
596  u"Saving file '%1' downloaded from '%2' failed")
597  << saveAsFileName << url.getFullUrl();
598  }
599  nwReply->close();
600  QTimer::singleShot(0, callback.object(), [=] {
601  if (!sApp || sApp->isShuttingDown()) { return; }
602  callback(msg);
603  });
604  });
605 
606  ProgressSlot progressSlot(this, [=](int, qint64, qint64, const QUrl &) {
607  // so far not implemented
608  });
609 
610  QNetworkReply *reply = this->getFromNetwork(url, callbackSlot, progressSlot, maxRedirects);
611  return reply;
612  }
613 
614  void CApplication::deleteAllCookies() { m_cookieManager->deleteAllCookies(); }
615 
616  void CApplication::exit(int retcode)
617  {
618  if (sApp) { instance()->gracefulShutdown(); }
619 
620  // when the event loop is not running, this does nothing
621  QCoreApplication::exit(retcode);
622  }
623 
624  QStringList CApplication::arguments() { return QCoreApplication::arguments(); }
625 
626  int CApplication::indexOfCommandLineOption(const QCommandLineOption &option, const QStringList &args)
627  {
628  const QStringList names = option.names();
629  if (names.isEmpty() || args.isEmpty()) { return -1; }
630  int i = -1;
631  for (const QString &arg : args)
632  {
633  i++;
634  QString a;
635  if (arg.startsWith("--")) { a = arg.mid(2); }
636  else if (arg.startsWith("-")) { a = arg.mid(1); }
637  else { continue; }
638 
639  if (names.contains(a, Qt::CaseInsensitive)) { return i; }
640  }
641  return -1;
642  }
643 
644  void CApplication::argumentsWithoutOption(const QCommandLineOption &option, QStringList &args)
645  {
646  const int index = indexOfCommandLineOption(option, args);
647  if (index < 0) { return; }
648 
649  // remove argument and its value
650  args.removeAt(index);
651  if (!option.valueName().isEmpty() && args.size() > index) { args.removeAt(index); }
652  }
653 
654  void CApplication::processEventsFor(int milliseconds)
655  {
656  // sApp check allows to use it in test cases without sApp
657  if (sApp && sApp->isShuttingDown()) { return; }
658  QEventLoop eventLoop;
659  QTimer::singleShot(milliseconds, &eventLoop, &QEventLoop::quit);
661  eventLoop.exec();
662  }
663 
664  CStatusMessageList CApplication::initContextsAndStartCoreFacade(const CCoreFacadeConfig &coreConfig)
665  {
666  Q_ASSERT_X(m_parsed, Q_FUNC_INFO, "Call this function after parsing");
667 
668  m_coreFacadeConfig = coreConfig;
669  const CStatusMessage msg = this->initLocalSettings();
670  if (msg.isFailure()) { return msg; }
671 
672  // now we can use settings
673  // if not yet initialized, init web data services
674  if (!m_webDataServices)
675  {
676  const CStatusMessageList msgs = this->initAndStartWebDataServices(
677  CWebReaderFlags::AllReaders, CDatabaseReaderConfigList::forPilotClient());
678  if (msgs.hasErrorMessages()) { return msgs; }
679  }
680  return this->startCoreFacade(); // will do nothing if setup is not yet loaded
681  }
682 
683  CStatusMessageList CApplication::startCoreFacadeWithoutContexts()
684  {
685  Q_ASSERT_X(m_parsed, Q_FUNC_INFO, "Call this function after parsing");
686 
687  m_coreFacadeConfig = CCoreFacadeConfig(CCoreFacadeConfig::NotUsed);
688  const CStatusMessage msg = this->initLocalSettings();
689  if (msg.isFailure()) { return msg; }
690 
691  return this->startCoreFacade(); // will do nothing if setup is not yet loaded
692  }
693 
694  CStatusMessageList CApplication::initAndStartWebDataServices(CWebReaderFlags::WebReader webReader,
695  const db::CDatabaseReaderConfigList &dbReaderConfig)
696  {
697  Q_ASSERT_X(m_webDataServices.isNull(), Q_FUNC_INFO, "Services already started");
698  SWIFT_VERIFY_X(QSslSocket::supportsSsl(), Q_FUNC_INFO, "No SSL");
699  if (!QSslSocket::supportsSsl()) { return CStatusMessage(this).error(u"No SSL supported, can`t be used"); }
700 
701  return this->startWebDataServices(webReader, dbReaderConfig);
702  }
703 
704  bool CApplication::isLocalContext() const
705  {
706  return this->getIContextApplication() && this->getIContextApplication()->isUsingImplementingObject();
707  }
708 
709  bool CApplication::isDBusContext() const
710  {
711  return this->getIContextApplication() && !this->getIContextApplication()->isUsingImplementingObject() &&
712  !this->getIContextApplication()->isEmptyObject();
713  }
714 
715  CStatusMessageList CApplication::startCoreFacade()
716  {
717  Q_ASSERT_X(m_parsed, Q_FUNC_INFO, "Call this function after parsing");
718 
719  if (!m_setupReader || !m_setupReader->isSetupAvailable())
720  {
721  return CStatusMessage(this).error(u"No setup reader or setup available");
722  }
723 
724  Q_ASSERT_X(m_coreFacade.isNull(), Q_FUNC_INFO, "Cannot alter facade");
725  Q_ASSERT_X(m_setupReader, Q_FUNC_INFO, "No facade without setup possible");
726  Q_ASSERT_X(m_webDataServices, Q_FUNC_INFO, "Need running web data services");
727 
728  const CStatusMessageList msgs(CStatusMessage(this).info(u"Will start core facade now"));
729  m_coreFacade.reset(new CCoreFacade(m_coreFacadeConfig));
730  emit this->coreFacadeStarted();
731  return msgs;
732  }
733 
734  CStatusMessageList CApplication::startWebDataServices(CWebReaderFlags::WebReader webReader,
735  const db::CDatabaseReaderConfigList &dbReaderConfig)
736  {
737  Q_ASSERT_X(m_parsed, Q_FUNC_INFO, "Call this function after parsing");
738 
739  if (!m_setupReader || !m_setupReader->isSetupAvailable())
740  {
741  return CStatusMessage(this).error(u"No setup reader or setup available");
742  }
743 
744  Q_ASSERT_X(m_setupReader, Q_FUNC_INFO, "No web data services without setup possible");
745  CStatusMessageList msgs;
746  if (!m_webDataServices)
747  {
748  msgs.push_back(CStatusMessage(this).info(u"Will start web data services now"));
749  m_webDataServices.reset(new CWebDataServices(webReader, dbReaderConfig, this));
750  Q_ASSERT_X(m_webDataServices, Q_FUNC_INFO, "Missing web services");
751 
752  emit this->webDataServicesStarted(true);
753  }
754  else { msgs.push_back(CStatusMessage(this).info(u"Web data services already running")); }
755 
756  return msgs;
757  }
758 
759  void CApplication::initLogging()
760  {
761  CLogHandler::instance()->install(); // make sure we have a log handler!
762 
763  // File logger
764  m_fileLogger.reset(new CFileLogger(this));
765  connect(CLogHandler::instance(), &CLogHandler::localMessageLogged, m_fileLogger.data(),
766  &CFileLogger::writeStatusMessageToFile);
767  connect(CLogHandler::instance(), &CLogHandler::remoteMessageLogged, m_fileLogger.data(),
768  &CFileLogger::writeStatusMessageToFile);
769  m_fileLogger->changeLogPattern(CLogPattern().withSeverityAtOrAbove(CStatusMessage::SeverityDebug));
770  }
771 
772  void CApplication::initParser()
773  {
774  m_parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
775  m_parser.setApplicationDescription(m_applicationName);
776  m_cmdHelp = m_parser.addHelpOption();
777  m_cmdVersion = m_parser.addVersionOption();
778  m_allOptions.append(m_cmdHelp);
779  m_allOptions.append(m_cmdVersion);
780 
781  // dev. system
782  m_cmdDevelopment = QCommandLineOption({ "dev", "development" },
783  QCoreApplication::translate("application", "Dev. system features?"));
784  this->addParserOption(m_cmdDevelopment);
785 
786  // Skip single application check
787  m_cmdSkipSingleApp = QCommandLineOption(
788  { "skipsa", "skipsingleapp" }, QCoreApplication::translate("application", "Skip the single app.test."));
789  this->addParserOption(m_cmdSkipSingleApp);
790 
791  // reset caches upfront
792  m_cmdClearCache = QCommandLineOption({ "ccache", "clearcache" },
793  QCoreApplication::translate("application", "Clear (reset) the caches."));
794  this->addParserOption(m_cmdClearCache);
795 
796  // test crashpad upload
797  m_cmdTestCrashpad = QCommandLineOption(
798  { "testcp", "testcrashpad" }, QCoreApplication::translate("application", "Trigger crashpad situation."));
799  this->addParserOption(m_cmdTestCrashpad);
800  }
801 
802  bool CApplication::isSet(const QCommandLineOption &option) const { return (m_parser.isSet(option)); }
803 
804  void CApplication::registerMetadata()
805  {
808  }
809 
810  QStringList CApplication::clearCaches()
811  {
812  const QStringList files(CDataCache::instance()->enumerateStore());
813  CDataCache::instance()->clearAllValues();
814  return files;
815  }
816 
817  void CApplication::gracefulShutdown()
818  {
819  if (m_shutdown) { return; }
820  if (m_shutdownInProgress) { return; }
821  m_shutdownInProgress = true;
822 
823  CLogMessage(this).info(u"Graceful shutdown of CApplication started");
824 
825  // info that we will shutdown
826  emit this->aboutToShutdown();
827 
828  // Release all input devices to not cause any accidental hotkey triggers anymore.
829  // This is also necessary to properly free platform specific instances at a defined point in time.
830  if (m_inputManager)
831  {
832  CLogMessage(this).info(u"Graceful shutdown of CApplication, released devices");
833  m_inputManager->releaseDevices();
834  }
835 
836  // save settings (but only when application was really alive)
837  if (m_parsed && m_saveSettingsOnShutdown)
838  {
839  const CStatusMessage m = this->supportsContexts() ? this->getIContextApplication()->saveSettings() :
840  CSettingsCache::instance()->saveToStore();
841  CLogMessage(this).preformatted(m);
842  }
843 
844  // from here on we really rip apart the application object
845  // and it should no longer be used
846 
847  if (this->supportsContexts(true))
848  {
849  CLogMessage(this).info(u"Graceful shutdown of CApplication, shutdown of contexts");
850 
851  // clean up facade
852  m_coreFacade->gracefulShutdown();
853  m_coreFacade.reset();
854  }
855 
856  if (m_webDataServices)
857  {
858  CLogMessage(this).info(u"Graceful shutdown of CApplication, shutdown of web services");
859 
860  m_webDataServices->gracefulShutdown();
861  m_webDataServices.reset();
862  }
863 
864  if (m_gitHubPackagesReader) { m_gitHubPackagesReader.reset(); }
865 
866  if (m_setupReader) { m_setupReader.reset(); }
867 
868  CLogMessage(this).info(u"Graceful shutdown of CApplication, shutdown of logger");
869  m_fileLogger->close();
870 
871  // clean up all in "deferred delete state"
872  qApp->sendPostedEvents(nullptr, QEvent::DeferredDelete);
873  sApp->processEventsFor(500);
874 
875  // completed
876  m_shutdown = true;
877  sApp = nullptr;
878 
879  disconnect(this);
880  }
881 
882  void CApplication::onStartUpCompleted()
883  {
884  // void
885  }
886 
887  void CApplication::initNetwork()
888  {
889  if (!m_accessManager) { m_accessManager = new QNetworkAccessManager(this); }
890 
891  m_cookieManager = new CCookieManager(this);
892  m_cookieManager->setParent(m_accessManager);
893  m_accessManager->setCookieJar(m_cookieManager);
894 
895  // Init network
896  Q_ASSERT_X(m_accessManager, Q_FUNC_INFO, "Need QAM");
897  }
898 
900 
901  const QString &CApplication::executable()
902  {
903  static const QString e(QFileInfo(QCoreApplication::applicationFilePath()).completeBaseName());
904  return e;
905  }
906 
907  const QStringList &CApplication::getLogCategories()
908  {
909  static const QStringList l({ "swift.application", "swift." % executable() });
910  return l;
911  }
912 
913  // ---------------------------------------------------------------------------------
914  // Parsing
915  // ---------------------------------------------------------------------------------
916 
917  bool CApplication::addParserOption(const QCommandLineOption &option)
918  {
919  m_allOptions.append(option);
920  return m_parser.addOption(option);
921  }
922 
923  bool CApplication::addParserOptions(const QList<QCommandLineOption> &options)
924  {
925  m_allOptions.append(options);
926  return m_parser.addOptions(options);
927  }
928 
929  void CApplication::addDBusAddressOption()
930  {
931  m_cmdDBusAddress = QCommandLineOption(
932  { "dbus", "dbusaddress" },
933  QCoreApplication::translate("application", "DBus address (session, system, P2P IP e.g. 192.168.23.5)"),
934  "dbusaddress");
935  this->addParserOption(m_cmdDBusAddress);
936  }
937 
938  void CApplication::addNetworkOptions() { this->addParserOptions(IContextNetwork::getCmdLineOptions()); }
939 
940  void CApplication::addAudioOptions() { this->addParserOptions(CContextAudioBase::getCmdLineOptions()); }
941 
942  QString CApplication::getCmdDBusAddressValue() const
943  {
944  if (!this->isParserOptionSet(m_cmdDBusAddress)) { return {}; }
945  const QString v(this->getParserValue(m_cmdDBusAddress));
946  const QString dBusAddress(CDBusServer::normalizeAddress(v));
947  return dBusAddress;
948  }
949 
950  bool CApplication::isParserOptionSet(const QString &option) const { return m_parser.isSet(option); }
951 
952  bool CApplication::skipSingleApplicationCheck() const { return this->isParserOptionSet(m_cmdSkipSingleApp); }
953 
954  bool CApplication::isParserOptionSet(const QCommandLineOption &option) const { return m_parser.isSet(option); }
955 
956  QString CApplication::getParserValue(const QString &option) const { return m_parser.value(option).trimmed(); }
957 
958  QString CApplication::getParserValue(const QCommandLineOption &option) const
959  {
960  return m_parser.value(option).trimmed();
961  }
962 
963  bool CApplication::parseCommandLineArgsAndLoadSetup()
964  {
965  if (!this->startupCheck()) return false;
966  if (!this->parseCommandLineArguments()) return false;
967  if (!this->loadSetupAndHandleErrors()) return false;
968  return true;
969  }
970 
971  bool CApplication::parseCommandLineArguments()
972  {
973  if (m_parsed) { return m_parsed; } // already done
974 
975  // we call parse because we also want to display a GUI error message when applicable
977  if (!m_parser.parse(args))
978  {
979  this->cmdLineErrorMessage("Parser error:", m_parser.errorText());
980  return false;
981  }
982 
983  if (m_alreadyRunning && !this->skipSingleApplicationCheck())
984  {
985  this->cmdLineErrorMessage("Program must only run once",
986  "You cannot run two or more instances side-by-side.");
987  return false;
988  }
989 
990  // help/version
991  if (m_parser.isSet(m_cmdHelp))
992  {
993  // Important: parser help will already stop application
994  this->cmdLineHelpMessage();
995  return false;
996  }
997  if (m_parser.isSet(m_cmdVersion))
998  {
999  // Important: version will already stop application
1000  this->cmdLineVersionMessage();
1001  return false;
1002  }
1003 
1004  // dev.
1005  m_devFlag = this->initIsRunningInDeveloperEnvironment();
1006 
1007  // Hookin, other parsing
1008  if (!this->parsingHookIn()) { return false; }
1009 
1010  // setup reader
1011  m_parsed = true;
1012  return true;
1013  }
1014 
1015  bool CApplication::startupCheck() const
1016  {
1017  const QStringList verifyErrors = CSwiftDirectories::verifyRuntimeDirectoriesAndFiles();
1018  if (!verifyErrors.isEmpty() && !m_applicationInfo.isUnitTest())
1019  {
1020  cmdLineErrorMessage("Missing runtime directories/files:", verifyErrors.join(", "));
1021  return false;
1022  }
1023  return true;
1024  }
1025 
1026  bool CApplication::loadSetupAndHandleErrors()
1027  {
1028  const CStatusMessageList msgs = loadSetup();
1029 
1030  if (msgs.isFailure()) { displaySetupLoadFailure(msgs); }
1031  return msgs.isSuccess();
1032  }
1033 
1034  void CApplication::displaySetupLoadFailure(swift::misc::CStatusMessageList)
1035  {
1036  // Ignore for CLI application
1037  // Already logged to console
1038  }
1039 
1040  void CApplication::cmdLineErrorMessage(const QString &text, const QString &informativeText) const
1041  {
1042  fputs(qPrintable(text + informativeText), stderr);
1043  }
1044 
1045  void CApplication::cmdLineErrorMessage(const CStatusMessageList &msgs) const
1046  {
1047  if (msgs.isEmpty()) { return; }
1048  if (!msgs.hasErrorMessages()) { return; }
1049  CApplication::cmdLineErrorMessage(msgs.toQString(true), "");
1050  }
1051 
1052  QPointer<ISimulator> CApplication::getISimulator() const
1053  {
1054  if (!this->hasSimulator()) { return nullptr; }
1055  return this->getCoreFacade()->getCContextSimulator()->simulator();
1056  }
1057 
1058  bool CApplication::hasSimulator() const
1059  {
1060  if (!this->getCoreFacade()) { return false; }
1061  if (!this->getCoreFacade()->getIContextSimulator()->isUsingImplementingObject()) { return false; }
1062  return (this->getCoreFacade()->getCContextSimulator()); // should always be true
1063  }
1064 
1065  void CApplication::cmdLineHelpMessage()
1066  {
1067  m_parser.showHelp(); // terminates
1068  Q_UNREACHABLE();
1069  }
1070 
1071  void CApplication::cmdLineVersionMessage()
1072  {
1073  m_parser.showVersion(); // terminates
1074  Q_UNREACHABLE();
1075  }
1076 
1077  QStringList CApplication::argumentsJoined(const QStringList &newArguments, const QStringList &removeArguments) const
1078  {
1079  QStringList joinedArguments = CApplication::arguments();
1080  QStringList newArgumentsChecked = newArguments;
1081 
1082  // remove the executable argument if it exists at position 0
1083  if (!joinedArguments.isEmpty() && !joinedArguments.at(0).startsWith("-"))
1084  {
1085  joinedArguments.removeFirst();
1086  } // was cmd line argument
1087  if (!newArgumentsChecked.isEmpty() && !newArgumentsChecked.at(0).startsWith("-"))
1088  {
1089  newArgumentsChecked.removeFirst();
1090  } // was cmd line argument
1091 
1092  // remove all values before checking options
1093  static const QRegularExpression regExp("^-");
1094  QStringList toBeRemoved(newArgumentsChecked.filter(regExp));
1095  toBeRemoved.append(removeArguments.filter(regExp));
1096  toBeRemoved.append("--installer");
1097  toBeRemoved.removeDuplicates();
1098 
1099  if (!joinedArguments.isEmpty() && !toBeRemoved.isEmpty())
1100  {
1101  // remove all options from removeArguments
1102  // consider alias names, that is why we check on option
1103  for (const QCommandLineOption &option : m_allOptions)
1104  {
1105  const int n = indexOfCommandLineOption(option, toBeRemoved);
1106  if (n >= 0) { argumentsWithoutOption(option, joinedArguments); }
1107  }
1108  }
1109 
1110  joinedArguments.append(newArgumentsChecked);
1111  return joinedArguments;
1112  }
1113 
1114  // ---------------------------------------------------------------------------------
1115  // Contexts
1116  // ---------------------------------------------------------------------------------
1117 
1118  shared_state::CDataLinkDBus *CApplication::getDataLinkDBus() { return getCoreFacade()->getDataLinkDBus(); }
1119 
1120  bool CApplication::supportsContexts(bool ignoreShutdownTest) const
1121  {
1122  if (!ignoreShutdownTest && m_shutdown) { return false; }
1123  if (m_coreFacade.isNull()) { return false; }
1124  if (!m_coreFacade->getIContextApplication()) { return false; }
1125  return (!m_coreFacade->getIContextApplication()->isEmptyObject());
1126  }
1127 
1128  const IContextNetwork *CApplication::getIContextNetwork() const
1129  {
1130  if (!supportsContexts()) { return nullptr; }
1131  return m_coreFacade->getIContextNetwork();
1132  }
1133 
1134  const IContextAudio *CApplication::getIContextAudio() const
1135  {
1136  if (!supportsContexts()) { return nullptr; }
1137  return m_coreFacade->getIContextAudio();
1138  }
1139 
1140  const CContextAudioBase *CApplication::getCContextAudioBase() const
1141  {
1142  if (!supportsContexts()) { return nullptr; }
1143  return m_coreFacade->getCContextAudioBase();
1144  }
1145 
1146  const IContextApplication *CApplication::getIContextApplication() const
1147  {
1148  if (!supportsContexts()) { return nullptr; }
1149  return m_coreFacade->getIContextApplication();
1150  }
1151 
1152  const IContextOwnAircraft *CApplication::getIContextOwnAircraft() const
1153  {
1154  if (!supportsContexts()) { return nullptr; }
1155  return m_coreFacade->getIContextOwnAircraft();
1156  }
1157 
1158  const IContextSimulator *CApplication::getIContextSimulator() const
1159  {
1160  if (!supportsContexts()) { return nullptr; }
1161  return m_coreFacade->getIContextSimulator();
1162  }
1163 
1164  IContextNetwork *CApplication::getIContextNetwork()
1165  {
1166  if (!supportsContexts()) { return nullptr; }
1167  return m_coreFacade->getIContextNetwork();
1168  }
1169 
1170  IContextAudio *CApplication::getIContextAudio()
1171  {
1172  if (!supportsContexts()) { return nullptr; }
1173  return m_coreFacade->getIContextAudio();
1174  }
1175 
1176  CContextAudioBase *CApplication::getCContextAudioBase()
1177  {
1178  if (!supportsContexts()) { return nullptr; }
1179  return m_coreFacade->getCContextAudioBase();
1180  }
1181 
1182  IContextApplication *CApplication::getIContextApplication()
1183  {
1184  if (!supportsContexts()) { return nullptr; }
1185  return m_coreFacade->getIContextApplication();
1186  }
1187 
1188  IContextOwnAircraft *CApplication::getIContextOwnAircraft()
1189  {
1190  if (!supportsContexts()) { return nullptr; }
1191  return m_coreFacade->getIContextOwnAircraft();
1192  }
1193 
1194  IContextSimulator *CApplication::getIContextSimulator()
1195  {
1196  if (!supportsContexts()) { return nullptr; }
1197  return m_coreFacade->getIContextSimulator();
1198  }
1199 
1200  void CApplication::onCoreFacadeStarted()
1201  {
1202  // void
1203  }
1204 
1205  // ---------------------------------------------------------------------------------
1206  // Setup
1207  // ---------------------------------------------------------------------------------
1208 
1209  bool CApplication::hasSetupReader() const { return !m_setupReader.isNull(); }
1210 
1211  CSetupReader *CApplication::getSetupReader() const { return m_setupReader.data(); }
1212 
1213  CStatusMessageList CApplication::loadSetup()
1214  {
1215  if (m_shutdown) { return CStatusMessage(this).warning(u"Shutting down, not reading"); }
1216  if (!m_setupReader) { return CStatusMessage(this).error(u"No reader for setup/version"); }
1217  Q_ASSERT_X(m_parsed, Q_FUNC_INFO, "Not yet parsed");
1218  const CStatusMessageList requestMsgs = m_setupReader->loadSetup();
1219  return requestMsgs;
1220  }
1221 
1222  CUrl CApplication::getVatsimMetarUrl() const
1223  {
1224  if (m_shutdown) { return {}; }
1225  if (m_webDataServices)
1226  {
1227  const CUrl url(m_webDataServices->getVatsimMetarUrl());
1228  if (!url.isEmpty()) { return url; }
1229  }
1230  if (m_setupReader) { return m_setupReader->getSetup().getVatsimMetarsUrl(); }
1231  return {};
1232  }
1233 
1234  CUrl CApplication::getVatsimDataFileUrl() const
1235  {
1236  if (m_shutdown) { return {}; }
1237  if (m_webDataServices)
1238  {
1239  const CUrl url(m_webDataServices->getVatsimDataFileUrl());
1240  if (!url.isEmpty()) { return url; }
1241  }
1242  if (m_setupReader) { return m_setupReader->getSetup().getVatsimDataFileUrl(); }
1243  return {};
1244  }
1245 
1246  CUrl CApplication::getVatsimServerFileUrl() const
1247  {
1248  if (m_shutdown || !m_setupReader) { return {}; }
1249 
1250  return m_setupReader->getSetup().getVatsimServerFileUrl();
1251  }
1252 
1253  CUrl CApplication::getVatsimFsdHttpUrl() const
1254  {
1255  if (m_shutdown || !m_setupReader) { return {}; }
1256 
1257  return m_setupReader->getSetup().getVatsimFsdHttpUrl();
1258  }
1259 
1260  void CApplication::onCrashDumpUploadEnabledChanged()
1261  {
1262  const bool enabled = CBuildConfig::isReleaseBuild() && m_crashDumpUploadEnabled.getThreadLocal();
1263  this->enableCrashDumpUpload(enabled);
1264  }
1265 
1266  void CApplication::simulateCrash() { CCrashHandler::instance()->simulateCrash(); }
1267 
1268  void CApplication::simulateAssert() { CCrashHandler::instance()->simulateAssert(); }
1269 
1270  void CApplication::enableCrashDumpUpload(bool enable) { CCrashHandler::instance()->setUploadsEnabled(enable); }
1271 
1272  bool CApplication::isSupportingCrashpad() const
1273  {
1274 #ifdef SWIFT_USE_CRASHPAD
1275  return true;
1276 #else
1277  return false;
1278 #endif
1279  }
1280 
1281  void CApplication::httpRequestImplInQAMThread(const QNetworkRequest &request, int logId,
1282  const CallbackSlot &callback, const ProgressSlot &progress,
1283  int maxRedirects, NetworkRequestOrPostFunction getPostOrDeleteRequest)
1284  {
1285  // run in QAM thread
1286  if (this->isShuttingDown()) { return; }
1287  QTimer::singleShot(0, m_accessManager, [=] {
1288  // should be now in QAM thread
1289  if (!sApp || sApp->isShuttingDown()) { return; }
1290  Q_ASSERT_X(CThreadUtils::isInThisThread(sApp->m_accessManager), Q_FUNC_INFO,
1291  "Wrong thread, must be QAM thread");
1292  this->httpRequestImpl(request, logId, callback, progress, maxRedirects, getPostOrDeleteRequest);
1293  });
1294  }
1295 
1296  QNetworkReply *CApplication::httpRequestImpl(const QNetworkRequest &request, int logId,
1297  const CApplication::CallbackSlot &callback, int maxRedirects,
1298  NetworkRequestOrPostFunction requestOrPostMethod)
1299  {
1300  ProgressSlot progress;
1301  return this->httpRequestImpl(request, logId, callback, progress, maxRedirects, requestOrPostMethod);
1302  }
1303 
1304  QNetworkReply *CApplication::httpRequestImpl(const QNetworkRequest &request, int logId,
1305  const CallbackSlot &callback, const ProgressSlot &progress,
1306  int maxRedirects, NetworkRequestOrPostFunction getPostOrDeleteRequest)
1307  {
1308  if (this->isShuttingDown()) { return nullptr; }
1309 
1310  QWriteLocker locker(&m_accessManagerLock);
1311  Q_ASSERT_X(m_accessManager->thread() == qApp->thread(), Q_FUNC_INFO,
1312  "Network manager supposed to be in main thread");
1313  if (!CThreadUtils::isInThisThread(m_accessManager))
1314  {
1315  this->httpRequestImplInQAMThread(request, logId, callback, progress, maxRedirects, getPostOrDeleteRequest);
1316  return nullptr; // not yet started, will be called again in QAM thread
1317  }
1318 
1319  Q_ASSERT_X(CThreadUtils::isInThisThread(m_accessManager), Q_FUNC_INFO, "Network manager thread mismatch");
1320  QNetworkRequest copiedRequest =
1321  CNetworkUtils::getSwiftNetworkRequest(request, this->getApplicationNameAndVersion());
1322 
1323  QNetworkReply *reply = getPostOrDeleteRequest(*m_accessManager, copiedRequest);
1325  reply->setProperty(CUrlLog::propertyNameId(), QVariant(logId));
1326  const QUrl url(reply->url());
1327  QString urlStr = url.toString();
1328 
1329  if (progress)
1330  {
1331  connect(reply, &QNetworkReply::downloadProgress, progress.object(),
1332  [=](qint64 current, qint64 max) { progress(logId, current, max, url); });
1333  }
1334 
1335  if (callback)
1336  {
1337  Q_ASSERT_X(callback.object(), Q_FUNC_INFO, "Need callback object (to determine thread)");
1338  connect(
1339  reply, &QNetworkReply::finished, callback.object(),
1340  [=] {
1341  // Called when finished!
1342  // QNetworkRequest::FollowRedirectsAttribute would allow auto redirect, but we use our approach as
1343  // it gives us better control \fixme: Check again on Qt 5.9: Added redirects policy to
1344  // QNetworkAccessManager (ManualRedirectsPolicy, NoLessSafeRedirectsPolicy,
1345  // SameOriginRedirectsPolicy, UserVerifiedRedirectsPolicy)
1346  const bool isRedirect = CNetworkUtils::isHttpStatusRedirect(reply);
1347  if (isRedirect && maxRedirects > 0)
1348  {
1349  const QUrl redirectUrl = CNetworkUtils::getHttpRedirectUrl(reply);
1350  if (!redirectUrl.isEmpty())
1351  {
1352  QNetworkRequest redirectRequest(redirectUrl);
1353  const int redirectsLeft = maxRedirects - 1;
1354  CLogMessage(sApp).info(u"Redirecting '%1' to '%2'") << urlStr << redirectUrl.toString();
1355  this->httpRequestImplInQAMThread(redirectRequest, logId, callback, progress, redirectsLeft,
1356  getPostOrDeleteRequest);
1357  return;
1358  }
1359  }
1360  // called when there are no more callbacks
1361  callback(reply);
1362  },
1363  Qt::QueuedConnection); // called in callback thread
1364  }
1365  return reply;
1366  }
1367 
1368  void CApplication::tagApplicationDataDirectory()
1369  {
1371  const QDir dir(d);
1372  if (!dir.exists() || !dir.isReadable()) { return; }
1373  const QString aiStr(this->getApplicationInfo().toJsonString());
1374  const QString filePath(CFileUtils::appendFilePaths(
1375  dir.path(), CApplicationInfo::fileName())); // will be overridden by next swift app
1376  CFileUtils::writeStringToFile(aiStr, filePath);
1377  }
1378 } // namespace swift::core
SWIFT_CORE_EXPORT swift::core::CApplication * sApp
Single instance of application object.
Definition: application.cpp:71
QStringList argumentsJoined(const QStringList &newArguments={}, const QStringList &removeArguments={}) const
Current parameters replaced by new arguments without the cmd line argument.
const QString & versionStringDetailed() const
String with beta, dev. and version.
QNetworkReply * getFromNetwork(const swift::misc::network::CUrl &url, const CallbackSlot &callback, int maxRedirects=DefaultMaxRedirects)
Request to get network reply.
QNetworkReply * downloadFromNetwork(const swift::misc::network::CUrl &url, const QString &saveAsFileName, const swift::misc::CSlot< void(const swift::misc::CStatusMessage &)> &callback, int maxRedirects=DefaultMaxRedirects)
Download file from network and store it as passed.
std::atomic_bool m_shutdown
Is being shutdown?
Definition: application.h:579
void restartApplication(const QStringList &newArguments={}, const QStringList &removeArguments={})
Stop and restart application.
bool m_parsed
Parsing accomplished?
Definition: application.h:576
data::CGlobalSetup getGlobalSetup() const
Global setup.
static constexpr int NoLogRequestId
network request without logging
Definition: application.h:413
bool isDeveloperFlagSet() const
Running with dev.flag?
Definition: application.h:173
swift::misc::db::CDistribution getOwnDistribution() const
Own distribution.
bool m_alreadyRunning
Application already running.
Definition: application.h:578
const char * swiftVersionChar()
swift info string
std::atomic_bool m_incognito
Incognito mode?
Definition: application.h:580
QCommandLineOption m_cmdClearCache
Clear cache.
Definition: application.h:573
const context::IContextApplication * getIContextApplication() const
Direct access to contexts if a CCoreFacade has been initialized.
virtual void onStartUpCompleted()
Startup completed.
static bool unregisterAsRunning()
Unregister from running.
bool isIncognito() const
Is incognito mode?
static void processEventsFor(int milliseconds)
Process all events for some time.
bool hasUnsavedSettings() const
Unsaved settings.
QNetworkReply * postToNetwork(const QNetworkRequest &request, int logId, const QByteArray &data, const CallbackSlot &callback)
Post to network.
virtual void onCoreFacadeStarted()
Called when facade/contexts have been started.
const QString & swiftVersionString() const
swift info string
void coreFacadeStarted()
Facade started.
void setIncognito(bool incognito)
Set incognito mode.
static QString getTemporaryDirectory()
Directory for temporary files.
bool hasWebDataServices() const
Web data services available?
virtual void cmdLineErrorMessage(const QString &text, const QString &informativeText) const
Display error message.
void init(bool withMetadata)
Init class, allows to init from swift::gui::CGuiApplication as well (pseudo virtual)
void updateInfoAvailable(bool success)
Update info available (cache, web load)
swift::misc::CStatusMessage saveSettingsByKey(const QStringList &keys)
Save all settings.
CApplication(swift::misc::CApplicationInfo::Application application, bool init=true)
Constructor.
Definition: application.cpp:83
static void exit(int retcode=EXIT_SUCCESS)
Exit application, perform graceful shutdown and exit.
QString getInfoString(const QString &separator) const
Comprehensive info.
bool isShuttingDown() const
Is application shutting down?
QNetworkReply * deleteResourceFromNetwork(const QNetworkRequest &request, int logId, const CallbackSlot &callback, int maxRedirects=DefaultMaxRedirects)
Request to delete a network resource from network, supporting swift::misc::network::CUrlLog.
static constexpr int NoRedirects
network request not allowing redirects
Definition: application.h:412
QCommandLineOption m_cmdDevelopment
Development flag.
Definition: application.h:572
bool isSet(const QCommandLineOption &option) const
Flag set or explicitly set to true.
std::atomic_bool m_shutdownInProgress
shutdown in progress?
Definition: application.h:581
static swift::misc::CApplicationInfoList getRunningApplications()
Information about all running apps (including this one only if exec() has already been called)
QStringList getUnsavedSettingsKeys() const
All unsaved settings.
const QString & getApplicationNameVersionDetailed() const
Version, name beta and dev info.
void toggleIncognito()
Toggle incognito mode.
virtual swift::misc::CStatusMessageList startHookIn()
Can be used to start special services.
Definition: application.h:546
static bool isApplicationRunning(swift::misc::CApplicationInfo::Application application)
Is application running?
bool m_started
Started with success?
Definition: application.h:577
swift::misc::db::CUpdateInfo getUpdateInfo() const
Update info.
bool isSetupAvailable() const
Setup already synchronized.
static QStringList clearCaches()
Clear the caches.
virtual bool start()
Start services, if not yet parsed call CApplication::parse.
bool supportsContexts(bool ignoreShutdownTest=false) const
Supports contexts.
void reloadUpdateInfo()
Reload update info.
CWebDataServices * getWebDataServices() const
Get the web data services.
const swift::misc::CApplicationInfo & getApplicationInfo() const
swift application running
Definition: application.h:138
int exec()
Finishes initialization and executes the event loop.
const QString & getApplicationNameAndVersion() const
Application name and version.
virtual void gracefulShutdown()
Graceful shutdown.
void saveSettingsOnShutdown(bool saveSettings)
Save settings on shutdown.
QCommandLineOption m_cmdTestCrashpad
Test a crasphpad upload.
Definition: application.h:574
static void registerMetadata()
Register metadata.
bool isAlreadyRunning() const
True if this swift application is already running (including different versions)
virtual bool hasMinimumMappingVersion() const
Minimum mapping version check.
void startUpCompleted(bool success)
Startup has been completed Will be triggered shortly before starting the event loop.
QNetworkReply * headerFromNetwork(const swift::misc::network::CUrl &url, const CallbackSlot &callback, int maxRedirects=NoRedirects)
Request to get network repy using HTTP's HEADER method.
static CApplication * instance()
Similar to.
static bool registerAsRunning()
Register as running.
Configuration object for the contexts.
ContextMode getMode() const
Mode.
@ Remote
context runs in a different process.
The class providing facades (the contexts) for all DBus relevant operations.
Definition: corefacade.h:57
Read available updates from GitHub Packages REST API.
void updateInfoAvailable(bool available)
Updates have been received from GitHub Packages.
Input manager handling hotkey function calls.
Definition: inputmanager.h:32
void createDevices()
Creates low level input devices. Once completed, hotkeys start to be processed.
Read the central URLs / locations of our data, setup and versions.
Definition: setupreader.h:36
data::CGlobalSetup getSetup() const
Current setup (reader URLs, DB location, crash server)
Definition: setupreader.cpp:79
Encapsulates reading data from web sources.
virtual misc::CStatusMessage saveSettingsByKey(const QStringList &keys)=0
Save core settings to disk.
virtual QStringList getUnsavedSettingsKeys() const =0
Get keys of all unsaved settings currently in core settings cache.
Audio context interface.
Definition: contextaudio.h:61
const IContextSimulator * getIContextSimulator() const
Context for simulator.
Definition: context.cpp:69
IContextNetwork * getIContextNetwork()
Context for network.
Definition: context.cpp:29
const IContextApplication * getIContextApplication() const
Context for application.
Definition: context.cpp:39
IContextAudio * getIContextAudio()
Context for network.
Definition: context.cpp:33
IContextOwnAircraft * getIContextOwnAircraft()
Context for own aircraft.
Definition: context.cpp:44
Global settings for readers, debug flags, etc.
Definition: globalsetup.h:31
Value object encapsulating a list of reader configs.
Description of a swift application.
Application getApplication() const
Get application.
static const QString & fileName()
File name of the application info file.
Application
Enumeration of application roles.
const CProcessInfo & getProcessInfo() const
Get process info.
void setApplicationDataDirectory(const QString &appDataDir)
Set application data dir.
List of swift application descriptions.
bool containsApplication(CApplicationInfo::Application application) const
List containing entry for CApplicationInfo::Application ?
QString toJsonString(QJsonDocument::JsonFormat format=QJsonDocument::Indented) const
Convenience function JSON as string.
CStatusMessage convertFromJsonNoThrow(const QJsonObject &json, const CLogCategoryList &categories, const QString &prefix)
Call convertFromJson, catch any CJsonException that is thrown and return it as CStatusMessage.
Class to write log messages to file.
Definition: filelogger.h:22
static QString readLockedFileToString(const QString &fileNameAndPath)
Read file into string, with a lock so two applications can't access at the same time.
Definition: fileutils.cpp:78
static bool writeStringToFile(const QString &content, const QString &fileNameAndPath)
Write string to text file.
Definition: fileutils.cpp:40
static bool writeByteArrayToFile(const QByteArray &data, const QString &fileNameAndPath)
Write byte array to file.
Definition: fileutils.cpp:51
static bool writeStringToLockedFile(const QString &content, const QString &fileNameAndPath)
Write string to file, with a lock so two applications can't access at the same time.
Definition: fileutils.cpp:61
static QString appendFilePaths(const QString &path1, const QString &path2)
Append file paths.
Definition: fileutils.cpp:95
Base class with a member CIdentifier to be inherited by a class which has an identity in the environm...
Definition: identifiable.h:24
A sequence of log categories.
Class for emitting a log message.
Definition: logmessage.h:27
static void preformatted(const CStatusMessage &statusMessage)
Sends a verbatim, preformatted message to the log.
Value class for matching log messages based on their categories.
Definition: logpattern.h:49
Derived & warning(const char16_t(&format)[N])
Set the severity to warning, providing a format string.
Derived & error(const char16_t(&format)[N])
Set the severity to error, providing a format string.
Derived & debug()
Set the severity to debug.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
bool containsBy(Predicate p) const
Return true if there is an element for which a given predicate returns true.
Definition: range.h:101
bool contains(const T &object) const
Return true if there is an element equal to given object. Uses the most efficient implementation avai...
Definition: range.h:109
int removeIf(Predicate p)
Remove elements for which a given predicate returns true.
Definition: sequence.h:446
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
int remove(const T &object)
Remove all elements equal to the given object, if it is contained.
Definition: sequence.h:435
bool isEmpty() const
Synonym for empty.
Definition: sequence.h:285
CStatusMessage loadFromStore()
Load settings from disk.
void enableLocalSave()
Connects signal CValueCache::valuesSaveRequested to a private slot that saves the values....
static CSettingsCache * instance()
Return the singleton instance.
CStatusMessage saveToStore(const QString &keyPrefix={})
Save settings to disk.
Callable wrapper for a member function with function signature F.
Definition: slot.h:62
Streamable status message, e.g.
constexpr static auto SeverityError
Status severities.
constexpr static auto SeverityInfo
Status severities.
bool isFailure() const
Operation considered unsuccessful.
Status messages, e.g. from Core -> GUI.
bool isFailure() const
Any message is marked as failure.
bool hasErrorMessages() const
Error messages.
bool isSuccess() const
All messages are marked as success.
static const QString & normalizedApplicationDataDirectory()
swift application data directory for one specific installation (a version)
QStringList getAllUnsavedKeys(const QString &keyPrefix={}) const
Return keys of all values which have been changed but not saved.
Base class for CWorker and CContinuousWorker.
Definition: worker.h:72
static const QSet< CWorkerBase * > & allWorkers()
All workers currently existing.
Definition: worker.h:163
Distributions for channel.
Definition: distribution.h:27
Update info, i.e. artifacts and distributions.
Definition: updateinfo.h:24
CDistribution anticipateOwnDistribution() const
Own distribution.
Definition: updateinfo.cpp:63
QString toQString(bool i18n=false) const
Cast as QString.
Definition: mixinstring.h:74
Value object encapsulating information of a location, kind of simplified CValueObject compliant versi...
Definition: url.h:27
bool isEmpty() const
Empty.
Definition: url.cpp:54
QNetworkRequest toNetworkRequest() const
To request.
Definition: url.cpp:113
QString getFullUrl(bool withQuery=true) const
Qualified name.
Definition: url.cpp:84
Core data traits (aka cached values) and classes.
Classes interacting with the swift database (aka "datastore").
Backend services of the swift project, like dealing with the network or the simulators.
Definition: actionbind.cpp:7
void registerMetadata()
Register all relevant metadata in swift::core.
Free functions in swift::misc.
SWIFT_MISC_EXPORT void setMockCacheRootDirectory(const QString &path)
Overwrite the default root directory for cache and settings, for testing purposes.
void registerMetadata()
Register all relevant metadata in Misc.
QString applicationName()
Get application name.
Definition: filelogger.cpp:24
const char * constData() const const
QStringList names() const const
QString valueName() const const
QString applicationFilePath()
void setApplicationName(const QString &application)
void setApplicationVersion(const QString &version)
QStringList arguments()
void exit(int returnCode)
QCoreApplication * instance()
QString translate(const char *context, const char *sourceText, const char *disambiguation, int n)
qint64 currentMSecsSinceEpoch()
bool exists() const const
QString tempPath()
int exec(QEventLoop::ProcessEventsFlags flags)
void quit()
QDir dir() const const
QByteArray readAll()
void append(QList< T > &&value)
QList< T >::const_reference at(qsizetype i) const const
bool isEmpty() const const
void removeAt(qsizetype i)
void removeFirst()
qsizetype size() const const
bool startsWith(QList< T >::parameter_type value) const const
QNetworkReply * deleteResource(const QNetworkRequest &request)
QNetworkReply * get(const QNetworkRequest &request)
QNetworkReply * head(const QNetworkRequest &request)
QNetworkReply * post(const QNetworkRequest &request, QHttpMultiPart *multiPart)
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
QNetworkReply::NetworkError error() const const
QUrl url() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void destroyed(QObject *obj)
bool moveToThread(QThread *targetThread)
void setObjectName(QAnyStringView name)
void setParent(QObject *parent)
bool setProperty(const char *name, QVariant &&value)
QThread * thread() const const
bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid)
bool supportsSsl()
QString writableLocation(QStandardPaths::StandardLocation type)
bool isEmpty() const const
QString mid(qsizetype position, qsizetype n) &&
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QString trimmed() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QStringList filter(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QString join(QChar separator) const const
qsizetype removeDuplicates()
QString buildAbi()
QString buildCpuArchitecture()
CaseInsensitive
QueuedConnection
bool isValid() const const
QString path() const const
QFuture< QtFuture::ArgsType< Signal >> connect(Sender *sender, Signal signal)
#define SWIFT_VERIFY_X(COND, WHERE, WHAT)
A weaker kind of assert.
Definition: verify.h:26