1 /*
2  * \copyright Copyright (c) 2014-2021 Governikus GmbH & Co. KG, Germany
3  */
4 
5 #include "Bootstrap.h"
6 
7 #include "BuildHelper.h"
8 #include "controller/AppController.h"
9 #include "CommandLineParser.h"
10 #include "Env.h"
11 #include "LogHandler.h"
12 #include "SignalHandler.h"
13 
14 #include <openssl/crypto.h>
15 
16 #include <QLoggingCategory>
17 #include <QProcess>
18 #include <QScopedPointer>
19 #include <QSslSocket>
20 #include <QThread>
21 
22 #include "config.h"
23 
24 using namespace governikus;
25 
26 Q_DECLARE_LOGGING_CATEGORY(init)
27 
28 
29 #if defined(INTEGRATED_SDK)
30 	#ifdef Q_OS_ANDROID
31 		#include <QAndroidService>
32 using QAPP = QAndroidService;
33 
34 	#else
35 		#include <QCoreApplication>
36 using QAPP = QCoreApplication;
37 	#endif
38 
39 #elif defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_WINRT)
40 	#ifdef Q_OS_ANDROID
41 		#include <QAndroidService>
42 		#include <QtAndroid>
43 	#endif
44 
45 	#include <QGuiApplication>
46 using QAPP = QGuiApplication;
47 #else
48 	#include <QApplication>
49 using QAPP = QApplication;
50 #endif
51 
52 
printInfo()53 static inline void printInfo()
54 {
55 	qCDebug(init) << "Logging to" << *Env::getSingleton<LogHandler>();
56 
57 	qCInfo(init) << "##################################################";
58 	const auto& info = BuildHelper::getInformationHeader();
59 	for (const auto& entry : info)
60 	{
61 		qCInfo(init).noquote().nospace() << "### " << entry.first << ": " << entry.second;
62 	}
63 	qCInfo(init) << "##################################################";
64 
65 	#if OPENSSL_VERSION_NUMBER < 0x10100000L
66 		#define OpenSSL_version SSLeay_version
67 		#define OPENSSL_VERSION SSLEAY_VERSION
68 	#endif
69 
70 	if (QSslSocket::sslLibraryVersionString() != QLatin1String(OpenSSL_version(OPENSSL_VERSION)))
71 	{
72 		qCWarning(init) << "Linked OpenSSL Version differs:" << OpenSSL_version(OPENSSL_VERSION);
73 	}
74 
75 	const auto libPathes = QCoreApplication::libraryPaths();
76 	for (const auto& path : libPathes)
77 	{
78 		qCDebug(init) << "Library path:" << path;
79 	}
80 }
81 
82 
initQt(int & argc,char ** argv)83 static inline QCoreApplication* initQt(int& argc, char** argv)
84 {
85 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
86 	QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton);
87 	QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
88 #endif
89 
90 	QCoreApplication::setOrganizationName(QStringLiteral(VENDOR));
91 	QCoreApplication::setOrganizationDomain(QStringLiteral(VENDOR_DOMAIN));
92 	QCoreApplication::setApplicationName(QStringLiteral(PRODUCT));
93 	QCoreApplication::setApplicationVersion(QStringLiteral(VERSION));
94 
95 #ifndef INTEGRATED_SDK
96 	QGuiApplication::setQuitOnLastWindowClosed(false);
97 #endif
98 
99 #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && !defined(INTEGRATED_SDK)
100 	QGuiApplication::setDesktopSettingsAware(false);
101 #endif
102 
103 #if defined(Q_OS_ANDROID) && !defined(INTEGRATED_SDK)
104 	if (QtAndroid::androidService().isValid())
105 	{
106 		return new QAndroidService(argc, argv);
107 	}
108 #endif
109 
110 	return new QAPP(argc, argv);
111 }
112 
113 
exec(const QScopedPointer<QCoreApplication> & pApp)114 static inline int exec(const QScopedPointer<QCoreApplication>& pApp)
115 {
116 #if defined(Q_OS_ANDROID) && !defined(INTEGRATED_SDK)
117 	if (QLatin1String(pApp->metaObject()->className()) == QLatin1String("QAndroidService"))
118 	{
119 		return QAndroidService::exec();
120 	}
121 #else
122 	Q_UNUSED(pApp)
123 #endif
124 	return QAPP::exec();
125 }
126 
127 
128 #if defined(Q_OS_WIN) || defined(Q_OS_MACOS) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
restartApp(const QString & pApplicationFilePath,QStringList pArgumentList,int pArgc)129 static void restartApp(const QString& pApplicationFilePath, QStringList pArgumentList, int pArgc)
130 {
131 	if (pArgumentList.size() == pArgc)
132 	{
133 		pArgumentList.removeFirst();
134 	}
135 
136 	pArgumentList << QStringLiteral("--show");
137 
138 	qCInfo(init) << "Attempting to start new process:" << pApplicationFilePath << ", args:" << pArgumentList;
139 	if (qint64 pid = -1; QProcess::startDetached(pApplicationFilePath, pArgumentList, QString(), &pid))
140 	{
141 		qCInfo(init) << "New process successfully launched, PID:" << pid;
142 	}
143 	else
144 	{
145 		qCCritical(init) << "Could not launch new process.";
146 	}
147 }
148 
149 
150 #endif
151 
initApp(int & argc,char ** argv)152 int governikus::initApp(int& argc, char** argv)
153 {
154 	const QScopedPointer<QCoreApplication> app(initQt(argc, argv));
155 	QThread::currentThread()->setObjectName(QStringLiteral("MainThread"));
156 
157 	CommandLineParser::getInstance().parse();
158 	Env::getSingleton<LogHandler>()->init();
159 	Env::getSingleton<SignalHandler>()->init();
160 	printInfo();
161 
162 	AppController controller;
163 	if (!controller.start())
164 	{
165 		qCCritical(init) << "Cannot start application controller, exit application";
166 		return EXIT_FAILURE;
167 	}
168 
169 	Env::getSingleton<SignalHandler>()->setController(controller);
170 	if (Env::getSingleton<SignalHandler>()->shouldQuit())
171 	{
172 		return EXIT_SUCCESS;
173 	}
174 
175 	const int returnCode = exec(app);
176 
177 #if defined(Q_OS_WIN) || defined(Q_OS_MACOS) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
178 	if (controller.shouldApplicationRestart())
179 	{
180 		restartApp(QCoreApplication::applicationFilePath(), QCoreApplication::arguments(), argc);
181 	}
182 #endif
183 
184 	qCDebug(init) << "Leaving application... bye bye!";
185 	return returnCode;
186 }
187