main.cpp 12.4 KB
Newer Older
1
/*
2
 * Copyright (C) 2014-2017 CZ.NIC
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of portions of this program with the
 * OpenSSL library under certain conditions as described in each
 * individual source file, and distribute linked combinations including
 * the two.
 */

#include <QApplication>
25
#include <QCommandLineParser>
26
#include <QQmlApplicationEngine>
Martin Straka's avatar
Martin Straka committed
27
#include <QQuickStyle>
28 29 30 31 32 33 34 35
#include <QSettings>
#include <QTranslator>
#include <QtQuick>
#include <QtSvg>
#include <QtXml>

#include "src/accounts.h"
#include "src/common.h"
36 37
#include "src/dialogues/qml_dialogue_helper.h"
#include "src/dialogues/qml_input_dialogue.h"
38
#include "src/dialogues/dialogues.h"
39
#include "src/files.h"
Karel Slaný's avatar
Karel Slaný committed
40
#include "src/locker.h"
41 42 43
#include "src/net/isds_wrapper.h"
#include "src/models/accountmodel.h"
#include "src/models/filemodel.h"
44
#include "src/models/list_sort_filter_proxy_model.h"
45
#include "src/models/messagemodel.h"
46 47 48
#if defined(Q_OS_ANDROID)
#include "src/os_android.h"
#endif /* defined(Q_OS_ANDROID) */
49
#include "src/qml_interaction/interaction_zfo_file.h"
50 51 52 53 54 55 56
#include "src/settings.h"
#include "src/sqlite/db_tables.h"
#include "src/sqlite/account_db.h"
#include "src/sqlite/message_db_container.h"
#include "src/sqlite/file_db_container.h"
#include "src/setwrapper.h"

57
/* iOS app_delegate - for interaction with iOS action Open in... */
58 59 60 61
#if defined Q_OS_IOS
#include "ios/src/qt_app_delegate.h"
#endif

62
/* namespace for QML registered objects */
63
const char *uri = "cz.nic.mobileDatovka"; /* Pages and components. */
64

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
/*!
 * @brief Used when registering types.
 */
struct QmlTypeEntry {
	const char *typeName;
	int major;
	int minor;
};

#define QML_PAGE_LOC "qrc:/qml/pages"
/*!
 * @brief NULL-terminated list of pages.
 */
static
const struct QmlTypeEntry qmlPages[] = {
Karel Slaný's avatar
Karel Slaný committed
80 81 82
	{ "PageAboutApp", 1, 0 },
	{ "PageAccountDetail", 1, 0 },
	{ "PageAccountList", 1, 0 },
83
	{ "PageChangePassword", 1, 0 },
Karel Slaný's avatar
Karel Slaný committed
84 85 86 87 88 89 90
	{ "PageMenuAccount", 1, 0 },
	{ "PageMenuDatovkaSettings", 1, 0 },
	{ "PageMenuMessage", 1, 0 },
	{ "PageMenuMessageDetail", 1, 0 },
	{ "PageMenuMessageList", 1, 0 },
	{ "PageMessageDetail", 1, 0 },
	{ "PageMessageList", 1, 0 },
91
	{ "PageMessageSearch", 1, 0 },
Karel Slaný's avatar
Karel Slaný committed
92 93 94 95 96
	{ "PageSettingsAccount", 1, 0 },
	{ "PageSettingsGeneral", 1, 0 },
	{ "PageSettingsPin", 1, 0 },
	{ "PageSettingsStorage", 1, 0 },
	{ "PageSettingsSync", 1, 0 },
97
	{ NULL, 0, 0 }
98 99
};

100 101 102 103 104 105
#define QML_COMPONENT_LOC "qrc:/qml/components"
/*!
 * @brief NULL-terminated list of components.
 */
static
const struct QmlTypeEntry qmlComponents[] = {
106
	{ "InputLineMenu", 1, 0 },
107
	{ "MessageList", 1, 0 },
108
	{ "OverlaidImage", 1, 0 },
Martin Straka's avatar
Martin Straka committed
109
	{ "PageHeader", 1, 0 },
110
	{ "ScrollContent", 1, 0 },
111 112 113 114
	{ "SpinBoxZeroMax", 1, 0 },
	{ NULL, 0, 0 }
};

115 116 117 118 119 120
#define QML_DIALOGUES "qrc:/qml/dialogues"
/*!
 * @brief NULL-terminated list of components.
 */
static
const struct QmlTypeEntry qmlDialogues[] = {
Martin Straka's avatar
Martin Straka committed
121
	{ "InputDialogue", 1, 0},
122
	{ "MessageDialogue", 1, 0},
123 124 125 126
	{ "PasteInputDialogue", 1, 0 },
	{ NULL, 0, 0 }
};

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
/*!
 * @brief Initialises command line parser.
 *
 * @param[in,out] parser Command line parser object to be initialised.
 * @return 0 on success, -1 on failure
 */
static
int setupCmdLineParser(QCommandLineParser &parser)
{
	parser.setApplicationDescription(QObject::tr("Data box application"));
	parser.addHelpOption();
	parser.addVersionOption();

	parser.addPositionalArgument("[zfo-file]",
	    QObject::tr("ZFO file to be viewed."));

	return 0;
}

146 147 148 149 150 151 152 153 154 155
/*!
 * @brief Registers QML types.
 *
 * @param[in] uri Namespace for QML objects.
 * @param[in] location Location of the QML description file.
 * @Param[in] entries List of entries describing the types.
 */
static
void registerQmlTypes(const char *uri, const char *location,
    const struct QmlTypeEntry *entries)
156
{
157 158 159 160 161 162 163 164 165 166 167 168 169
	if ((uri == NULL) || (location == NULL)) {
		Q_ASSERT(0);
		return;
	}

	const struct QmlTypeEntry *entry = entries;
	while ((entry != NULL) && (entry->typeName != NULL)) {
		qmlRegisterType(
		    QUrl(QString("%1/%2.qml").arg(location).arg(entry->typeName)),
		    uri, entry->major, entry->minor, entry->typeName);
		++entry;
	}
}
170

171 172
int main(int argc, char *argv[])
{
Martin Straka's avatar
Martin Straka committed
173 174
	QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
	QQuickStyle::setStyle("Material");
175
	//qputenv("QT_QUICK_CONTROLS_STYLE", "material");
176

Karel Slaný's avatar
Karel Slaný committed
177
	/* First thing to create is the application object. */
178 179 180 181 182 183
	QApplication app(argc, argv);
	/*
	 * This object needs to be created in the same thread as the
	 * application.
	 */
	QmlDlgHelper dlgHelper;
184
	QmlDlgHelper::dlgEmitter = &dlgHelper;
185

186 187 188 189 190 191 192 193
	/* Perform check that configuration file can be accessed. */
	const QString settingsFileName(Settings::settingsPath());
	if (settingsFileName.isEmpty()) {
		return EXIT_FAILURE;
	}

	QCoreApplication::addLibraryPath("./");

194 195 196
	/* Initialise random number generator. */
	qsrand(QDateTime::currentDateTimeUtc().toTime_t());

197
	/* set application data, identification, etc. */
198 199 200 201 202
	app.setOrganizationName(APP_ORG_NAME);
	app.setOrganizationDomain(APP_ORG_DOMAIN);
	app.setApplicationName(APP_NAME);
	app.setApplicationVersion(VERSION);

203 204 205 206 207 208 209 210
	QCommandLineParser parser;
	if (0 != setupCmdLineParser(parser)) {
		return EXIT_FAILURE;
	}

	/* Process command-line arguments. */
	parser.process(app);

211 212
	QStringList cmdLineFileNames;
#if defined(Q_OS_ANDROID)
213
	cmdLineFileNames = IntentNotification::getIntentArguments();
214 215 216
#else
	cmdLineFileNames = parser.positionalArguments();
#endif /* defined(Q_OS_ANDROID) */
217

218 219 220 221
	/* load global settings */
	{
		QSettings settings(Settings::settingsPath(),
		    QSettings::IniFormat);
222
		globSet.loadFromSettings(settings);
223 224
	}

225 226 227 228 229 230
	/* set font family and font size from settings */
	QFont font;
	font.setFamily(DEFAULT_FONT_FAMILY);
	font.setPointSize(globSet.fontSize);
	app.setFont(font);

231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
	/* load datovka localization and qtbase localization */
	QTranslator datovkaTrans;
	QTranslator qtbaseTrans;
	QString lang = QLocale::system().name();
	if (globSet.language == "cs") {
		lang = "cs";
	} else if (globSet.language == "en") {
		lang = "en";
	}
	if (!datovkaTrans.load("datovka_" + lang, ":/locale/")) {
		qDebug() << "Could not load datovka localisation file...";
	}
	app.installTranslator(&datovkaTrans);
	if (!qtbaseTrans.load("qtbase_" + lang, ":/locale/")) {
		qDebug() << "Could not load qtbase localisation file...";
	}
	app.installTranslator(&qtbaseTrans);

	/* Init and use these class - we need register it to QML */
	Messages messages;
	Accounts accounts;
	Files files;
	IsdsWrapper isds;
	GlobalSettingsQmlWrapper settings;

	/* Connect slot for isds cxt delete when account was deleted or updated */
	QObject::connect(&accounts, SIGNAL(removeIsdsCtx(QString)),
	    &isds, SLOT(removeIsdsCtx(QString)));

260 261 262 263 264 265 266
	/* Register application state changes. */
	class Locker locker;
	QObject::connect(&app,
	    SIGNAL(applicationStateChanged(Qt::ApplicationState)),
	    &locker, SLOT(processNewState(Qt::ApplicationState)));
	app.installEventFilter(&locker);

267
	/* Create and init account model. */
268 269
	AccountListModel *accountModelPtr = new (std::nothrow) AccountListModel;
	if (Q_NULLPTR == accountModelPtr) {
270 271 272
		qCritical("Cannot create account model.");
		return EXIT_FAILURE;
	}
273
	accountModelPtr->setObjectName("mainAccountModel");
274 275 276 277 278

	/* get main handle of appliaction and QML */
	QQmlApplicationEngine engine;
	QQmlContext *ctx = engine.rootContext();

279 280 281
	/* Register application pages to QML */
	registerQmlTypes(uri, QML_PAGE_LOC, qmlPages);
	registerQmlTypes(uri, QML_COMPONENT_LOC, qmlComponents);
282
	registerQmlTypes(uri, QML_DIALOGUES, qmlDialogues);
283

284
	/* Register types into QML. */
285
	AccountListModel::declareQML();
286
	Dialogues::declareQML();
287
	FileListModel::declareQML();
288
	ListSortFilterProxyModel::declareQML();
289
	MessageListModel::declareQML();
290
	Messages::declareQML();
291
	MsgInfo::declareQML();
292

293 294
	InteractionZfoFile interactionZfoFile;

295 296 297 298 299 300
	/* Inicialize app delegate component for interaction with iOS
	 * Reaction on the iOS action "Open in..." */
#if defined Q_OS_IOS
	QtAppDelegateInitialize(&interactionZfoFile);
#endif

301 302 303 304 305 306
	/* register classes in QML */
	ctx->setContextProperty("isds", &isds);
	ctx->setContextProperty("messages", &messages);
	ctx->setContextProperty("accounts", &accounts);
	ctx->setContextProperty("files", &files);
	ctx->setContextProperty("settings", &settings);
307
	ctx->setContextProperty("locker", &locker);
308
	ctx->setContextProperty("interactionZfoFile", &interactionZfoFile);
309
	ctx->setContextProperty("dlgEmitter", QmlDlgHelper::dlgEmitter);
310 311

	/* register and set models in QML */
312
	ctx->setContextProperty(accountModelPtr->objectName(), accountModelPtr);
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338

	/* Localise description in tables. */
	accntinfTbl.reloadLocalisedDescription();
	userinfTbl.reloadLocalisedDescription();
	msgsTbl.reloadLocalisedDescription();
	flsTbl.reloadLocalisedDescription();
	evntsTbl.reloadLocalisedDescription();

	/* init and open account database */
	AccountDb globAccountDb("ACCOUNTS");
	globAccountDbPtr = &globAccountDb;

	/* true = account db store in disk, false = to memory */
	if (!globAccountDbPtr->openDb(ACNT_DB_NAME, true)) {
		qDebug() << "ERROR: account db not found!";
		return EXIT_FAILURE;
	}

	/* init message db container */
	MsgDbContainer globMessageDb("MESSAGES");
	globMessageDbsPtr = &globMessageDb;

	/* init file db container */
	FileDbContainer globFileDb("FILES");
	globFileDbsPtr = &globFileDb;

339
	/* Load accounts from settings to account model. */
340 341 342
	{
		QSettings settings(Settings::settingsPath(),
		    QSettings::IniFormat);
343
		accountModelPtr->loadAccountsFromSettings(settings);
344 345 346 347
		/*
		 * No need to load counters here as some messages are likely
		 * to be deleted.
		 */
348 349 350 351 352 353 354 355
	}

	/* load UI (QML) */
	engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));

	/* OpenSSL support test */
	if (QSslSocket::supportsSsl()) {
		/* set last update text to status bar */
356
		if (!globSet.lastUpdateStr().isEmpty() && globSet.pinCode.isEmpty()) {
357
			emit isds.statusBarTextChanged(
Karel Slaný's avatar
Karel Slaný committed
358
			    QObject::tr("Last synchronisation: %1").
359
			    arg(globSet.lastUpdateStr()), false, true);
360 361
		}
	} else {
362
		Dialogues::errorMessage(Dialogues::WARNING,
363
		    QObject::tr("Security problem"),
364 365
		    QObject::tr("OpenSSL support is required!"),
		    QObject::tr("The device does not support OpenSSL. "
366
		        "The application won't work correctly."));
367 368
	}

369
	/* Deletion of messages from db is disabled when equal to 0. */
370 371 372 373
	if (globSet.msgLifeTimeInDays > 0) {
		messages.deleteExpiredMessagesFromDbs(globSet.msgLifeTimeInDays);
	}

374
	/* Deletion of files from db is disabled when equal to 0. */
375 376 377 378
	if (globSet.fileLifeTimeInDays > 0) {
		files.deleteExpiredFilesFromDbs(globSet.fileLifeTimeInDays);
	}

379 380 381 382 383
	/* Inactivity locking is disabled when equal to 0. */
	if (globSet.pinInactTimeoutInSecs > 0) {
		locker.setInactivityInterval(globSet.pinInactTimeoutInSecs);
	}

384
	/* Load counters. */
385
	Accounts::loadModelCounters(accountModelPtr);
386

387 388 389 390 391 392 393
	/*
	 * Show PIN screen if needed. Encoded PIN is checked because it hasn't
	 * been decoded yet.
	 */
	if (!globSet.pinCode.isEmpty()) {
		emit locker.lockApp();
	}
394

395
	/*
396
	 * Open files passed via command line.
397 398
	 */
	if (!cmdLineFileNames.isEmpty()) {
399 400 401
		foreach (const QString &filePath, cmdLineFileNames) {
			interactionZfoFile.openZfoFile(filePath);
		}
402
	}
403
#if defined(Q_OS_ANDROID)
404
	IntentNotification intentNotification(interactionZfoFile);
405 406
	QObject::connect(&app, SIGNAL(applicationStateChanged(Qt::ApplicationState)),
	    &intentNotification, SLOT(scanIntentsForFiles(Qt::ApplicationState)));
407
#endif /* defined(Q_OS_ANDROID) */
408

409 410
	QmlInputDialogue::searchPersistentDialogues(&engine);

411 412 413 414 415 416
	/* Run app main event loop */
	int ret = app.exec();

	/* Close all OTP connections if exist */
	isds.closeAllOtpConnections();

417 418 419 420 421 422 423 424 425 426
	/*
	 * Store the configuration only when PIN has been recovered or is not
	 * used.
	 */
	if (!globSet._pinVal.isEmpty() ||
	    globSet.pinAlg.isEmpty() || globSet.pinSalt.isEmpty() || globSet.pinCode.isEmpty()) {
		/*
		 * The PIN was set/recovered or
		 * incomplete data to check/recover the PIN were supplied.
		 */
427
		GlobalSettingsQmlWrapper::saveAllSettings(accountModelPtr);
428
	}
429

430 431
	delete accountModelPtr;
	accountModelPtr = Q_NULLPTR;
432 433 434

	return ret;
}