main.cpp 14.2 KB
Newer Older
1
/*
2
 * Copyright (C) 2014-2018 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
#include <QSettings>
#include <QTranslator>
#include <QtQuick>
#include <QtSvg>
#include <QtXml>

#include "src/accounts.h"
35 36
#include "src/dialogues/qml_dialogue_helper.h"
#include "src/dialogues/qml_input_dialogue.h"
37
#include "src/dialogues/dialogues.h"
38
#include "src/files.h"
39
#include "src/io/filesystem.h"
Karel Slaný's avatar
Karel Slaný committed
40
#include "src/locker.h"
41
#include "src/log/log.h"
42 43
#include "src/net/isds_wrapper.h"
#include "src/models/accountmodel.h"
44
#include "src/models/databoxmodel.h"
45
#include "src/models/filemodel.h"
46
#include "src/models/list_sort_filter_proxy_model.h"
47
#include "src/models/messagemodel.h"
48 49 50
#if defined(Q_OS_ANDROID)
#include "src/os_android.h"
#endif /* defined(Q_OS_ANDROID) */
51
#include "src/qml_interaction/attachment_data.h"
52
#include "src/qml_interaction/interaction_filesystem.h"
53
#include "src/qml_interaction/interaction_zfo_file.h"
54
#include "src/qml_interaction/message_envelope.h"
55
#include "src/qml_interaction/string_manipulation.h"
56
#include "src/settings.h"
Martin Straka's avatar
Martin Straka committed
57
#include "src/setwrapper.h"
58 59 60
#include "src/sqlite/db_tables.h"
#include "src/sqlite/account_db.h"
#include "src/sqlite/file_db_container.h"
Martin Straka's avatar
Martin Straka committed
61 62
#include "src/sqlite/message_db_container.h"
#include "src/sqlite/zfo_db.h"
63
#include "src/zfo.h"
64

65
/* iOS app_delegate - for interaction with iOS action Open in... */
66 67 68 69
#if defined Q_OS_IOS
#include "ios/src/qt_app_delegate.h"
#endif

70 71 72
/* ACCOUNT DB filename */
#define ACNT_DB_NAME "accounts.db"

73 74 75 76 77
/* APPLICATION IDENTIFICATION */
#define APP_ORG_NAME "CZ.NIC, z.s.p.o."
#define APP_ORG_DOMAIN "cz.nic"
#define DEFAULT_FONT_FAMILY "System"

78
/* namespace for QML registered objects */
79
const char *uri = "cz.nic.mobileDatovka"; /* Pages and components. */
80

81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
/*!
 * @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[] = {
96 97 98
	{ "PageAboutApp", 1, 0 },
	{ "PageAccountDetail", 1, 0 },
	{ "PageAccountList", 1, 0 },
99
	{ "PageChangePassword", 1, 0 },
Martin Straka's avatar
Martin Straka committed
100
	{ "PageContactList", 1, 0 },
101
	{ "PageDataboxDetail", 1, 0 },
102
	{ "PageDataboxSearch", 1, 0 },
103 104 105 106 107 108 109
	{ "PageMenuAccount", 1, 0 },
	{ "PageMenuDatovkaSettings", 1, 0 },
	{ "PageMenuMessage", 1, 0 },
	{ "PageMenuMessageDetail", 1, 0 },
	{ "PageMenuMessageList", 1, 0 },
	{ "PageMessageDetail", 1, 0 },
	{ "PageMessageList", 1, 0 },
110
	{ "PageMessageSearch", 1, 0 },
111
	{ "PageSendMessage", 1, 0 },
112 113 114 115 116
	{ "PageSettingsAccount", 1, 0 },
	{ "PageSettingsGeneral", 1, 0 },
	{ "PageSettingsPin", 1, 0 },
	{ "PageSettingsStorage", 1, 0 },
	{ "PageSettingsSync", 1, 0 },
117
	{ NULL, 0, 0 }
118 119
};

120 121 122 123 124 125
#define QML_COMPONENT_LOC "qrc:/qml/components"
/*!
 * @brief NULL-terminated list of components.
 */
static
const struct QmlTypeEntry qmlComponents[] = {
126
	{ "AccessibleImageButton", 1, 0 },
127
	{ "AccessibleMenu", 1, 0 },
128
	{ "AccessibleOverlaidImageButton", 1, 0 },
129
	{ "AccessibleText", 1, 0 },
130
	{ "AccessibleTextButton", 1, 0 },
131
	{ "DataboxList", 1, 0 },
132
	{ "InputLineMenu", 1, 0 },
133
	{ "MessageList", 1, 0 },
134
	{ "OverlaidImage", 1, 0 },
135
	{ "PageHeader", 1, 0 },
136
	{ "ProgressBar", 1, 0 },
137 138 139 140
	{ "SpinBoxZeroMax", 1, 0 },
	{ NULL, 0, 0 }
};

141 142 143 144 145 146
#define QML_DIALOGUES "qrc:/qml/dialogues"
/*!
 * @brief NULL-terminated list of components.
 */
static
const struct QmlTypeEntry qmlDialogues[] = {
147
	{ "FileDialogue", 1, 0 },
Martin Straka's avatar
Martin Straka committed
148
	{ "InputDialogue", 1, 0},
149
	{ "MessageDialogue", 1, 0},
150 151 152 153
	{ "PasteInputDialogue", 1, 0 },
	{ NULL, 0, 0 }
};

154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
/*!
 * @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;
}

173 174 175 176 177 178 179 180 181 182
/*!
 * @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)
183
{
184 185 186 187 188 189 190 191 192 193 194 195 196
	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;
	}
}
197

198 199
int main(int argc, char *argv[])
{
Martin Straka's avatar
Martin Straka committed
200 201
	QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
	QQuickStyle::setStyle("Material");
202
	//qputenv("QT_QUICK_CONTROLS_STYLE", "material");
203

Karel Slaný's avatar
Karel Slaný committed
204
	/* First thing to create is the application object. */
205 206 207 208 209 210
	QApplication app(argc, argv);
	/*
	 * This object needs to be created in the same thread as the
	 * application.
	 */
	QmlDlgHelper dlgHelper;
211
	QmlDlgHelper::dlgEmitter = &dlgHelper;
212

213 214 215 216 217 218 219 220
	/* Perform check that configuration file can be accessed. */
	const QString settingsFileName(Settings::settingsPath());
	if (settingsFileName.isEmpty()) {
		return EXIT_FAILURE;
	}

	QCoreApplication::addLibraryPath("./");

221 222 223
	/* Initialise random number generator. */
	qsrand(QDateTime::currentDateTimeUtc().toTime_t());

224
	/* set application data, identification, etc. */
225 226 227 228 229
	app.setOrganizationName(APP_ORG_NAME);
	app.setOrganizationDomain(APP_ORG_DOMAIN);
	app.setApplicationName(APP_NAME);
	app.setApplicationVersion(VERSION);

230 231 232 233 234 235 236 237
	QCommandLineParser parser;
	if (0 != setupCmdLineParser(parser)) {
		return EXIT_FAILURE;
	}

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

238 239
	QStringList cmdLineFileNames;
#if defined(Q_OS_ANDROID)
240
	cmdLineFileNames = IntentNotification::getIntentArguments();
241 242 243
#else
	cmdLineFileNames = parser.positionalArguments();
#endif /* defined(Q_OS_ANDROID) */
244

245 246 247 248
	/* load global settings */
	{
		QSettings settings(Settings::settingsPath(),
		    QSettings::IniFormat);
249
		globSet.loadFromSettings(settings);
250 251
	}

252 253 254 255 256 257
	/* set font family and font size from settings */
	QFont font;
	font.setFamily(DEFAULT_FONT_FAMILY);
	font.setPointSize(globSet.fontSize);
	app.setFont(font);

258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
	/* 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;
282
	StringManipulation strManipulation;
283
	Zfo zfo;
284 285 286 287 288

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

289 290 291 292 293 294 295
	/* Register application state changes. */
	class Locker locker;
	QObject::connect(&app,
	    SIGNAL(applicationStateChanged(Qt::ApplicationState)),
	    &locker, SLOT(processNewState(Qt::ApplicationState)));
	app.installEventFilter(&locker);

296
	/* Create and init account model. */
297 298
	AccountListModel *accountModelPtr = new (std::nothrow) AccountListModel;
	if (Q_NULLPTR == accountModelPtr) {
299 300 301
		qCritical("Cannot create account model.");
		return EXIT_FAILURE;
	}
302
	accountModelPtr->setObjectName("mainAccountModel");
303 304 305 306 307

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

308 309 310
	/* Register application pages to QML */
	registerQmlTypes(uri, QML_PAGE_LOC, qmlPages);
	registerQmlTypes(uri, QML_COMPONENT_LOC, qmlComponents);
311
	registerQmlTypes(uri, QML_DIALOGUES, qmlDialogues);
312

313
	/* Register types into QML. */
314
	AccountListModel::declareQML();
315
	AttachmentData::declareQML();
316
	DataboxListModel::declareQML();
317
	DataboxModelEntry::declareQML();
318
	Dialogues::declareQML();
319
	FileListModel::declareQML();
Martin Straka's avatar
Martin Straka committed
320
	Files::declareQML();
321
	InteractionFilesystem::declareQML();
322
	ListSortFilterProxyModel::declareQML();
323
	MessageListModel::declareQML();
324
	Messages::declareQML();
325
	MsgEnvelope::declareQML();
326
	MsgInfo::declareQML();
327

328 329
	InteractionZfoFile interactionZfoFile;

330 331 332 333 334 335
	/* Inicialize app delegate component for interaction with iOS
	 * Reaction on the iOS action "Open in..." */
#if defined Q_OS_IOS
	QtAppDelegateInitialize(&interactionZfoFile);
#endif

336 337 338 339 340 341
	/* register classes in QML */
	ctx->setContextProperty("isds", &isds);
	ctx->setContextProperty("messages", &messages);
	ctx->setContextProperty("accounts", &accounts);
	ctx->setContextProperty("files", &files);
	ctx->setContextProperty("settings", &settings);
342
	ctx->setContextProperty("strManipulation", &strManipulation);
343
	ctx->setContextProperty("locker", &locker);
344
	ctx->setContextProperty("interactionZfoFile", &interactionZfoFile);
345
	ctx->setContextProperty("dlgEmitter", QmlDlgHelper::dlgEmitter);
346
	ctx->setContextProperty("zfo", &zfo);
347 348

	/* register and set models in QML */
349
	ctx->setContextProperty(accountModelPtr->objectName(), accountModelPtr);
350 351 352 353 354 355 356

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

	/* init and open account database */
360
	AccountDb globAccountDb("ACCOUNTS", false);
361 362
	globAccountDbPtr = &globAccountDb;

363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
	{
		QString dirName(
		    existingWritableLocation(QStandardPaths::AppDataLocation));
		if (dirName.isEmpty()) {
			logErrorNL("%s",
			    "Cannot determine application data location.");
			Q_ASSERT(0);
			return EXIT_FAILURE;
		}

		QString dbPath(dirName + QDir::separator() + ACNT_DB_NAME);
		if (!globAccountDbPtr->openDb(dbPath, SQLiteDb::CREATE_MISSING)) {
			logErrorNL("%s", "Account database not found!");
			return EXIT_FAILURE;
		}
378 379 380 381 382 383 384 385 386 387
	}

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

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

Martin Straka's avatar
Martin Straka committed
388
	/* init zfo db database */
389
	ZfoDb globZfoDb("ZFOS", false);
Martin Straka's avatar
Martin Straka committed
390 391
	globZfoDbPtr = &globZfoDb;

392
	/* Load accounts from settings to account model. */
393 394 395
	{
		QSettings settings(Settings::settingsPath(),
		    QSettings::IniFormat);
396
		accountModelPtr->loadAccountsFromSettings(settings);
397 398 399 400
		/*
		 * No need to load counters here as some messages are likely
		 * to be deleted.
		 */
401 402
	}

403 404 405 406
	/*
	 * Open ZFO database, the second parameter means: true = zfo db will
	 * store in disk, false = only in memory
	 */
407
	if (!globZfoDbPtr->openDb(ZFO_DB_NAME, (globSet.zfoDbSizeMBs > 0))) {
Martin Straka's avatar
Martin Straka committed
408 409 410
		qDebug() << "ERROR: zfo db not found!";
	}

411 412 413 414 415 416
	/* load UI (QML) */
	engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));

	/* OpenSSL support test */
	if (QSslSocket::supportsSsl()) {
		/* set last update text to status bar */
417
		if (!globSet.lastUpdateStr().isEmpty() && globSet.pinCode.isEmpty()) {
418
			emit isds.statusBarTextChanged(
Karel Slaný's avatar
Karel Slaný committed
419
			    QObject::tr("Last synchronisation: %1").
420
			    arg(globSet.lastUpdateStr()), false, true);
421 422
		}
	} else {
423
		Dialogues::errorMessage(Dialogues::WARNING,
424
		    QObject::tr("Security problem"),
425 426
		    QObject::tr("OpenSSL support is required!"),
		    QObject::tr("The device does not support OpenSSL. "
427
		        "The application won't work correctly."));
428 429
	}

430
	/* Deletion of messages from db is disabled when equal to 0. */
431 432 433 434
	if (globSet.msgLifeTimeInDays > 0) {
		messages.deleteExpiredMessagesFromDbs(globSet.msgLifeTimeInDays);
	}

435
	/* Deletion of files from db is disabled when equal to 0. */
436 437 438 439
	if (globSet.fileLifeTimeInDays > 0) {
		files.deleteExpiredFilesFromDbs(globSet.fileLifeTimeInDays);
	}

440 441 442 443 444
	/* Inactivity locking is disabled when equal to 0. */
	if (globSet.pinInactTimeoutInSecs > 0) {
		locker.setInactivityInterval(globSet.pinInactTimeoutInSecs);
	}

445
	/* Load counters. */
446
	Accounts::loadModelCounters(accountModelPtr);
447

448 449 450 451 452 453 454
	/*
	 * Show PIN screen if needed. Encoded PIN is checked because it hasn't
	 * been decoded yet.
	 */
	if (!globSet.pinCode.isEmpty()) {
		emit locker.lockApp();
	}
455

456
	/*
457
	 * Open files passed via command line.
458 459
	 */
	if (!cmdLineFileNames.isEmpty()) {
460 461 462
		foreach (const QString &filePath, cmdLineFileNames) {
			interactionZfoFile.openZfoFile(filePath);
		}
463
	}
464
#if defined(Q_OS_ANDROID)
465
	IntentNotification intentNotification(interactionZfoFile);
466 467
	QObject::connect(&app, SIGNAL(applicationStateChanged(Qt::ApplicationState)),
	    &intentNotification, SLOT(scanIntentsForFiles(Qt::ApplicationState)));
468
#endif /* defined(Q_OS_ANDROID) */
469

470 471
	QmlInputDialogue::searchPersistentDialogues(&engine);

472 473 474 475 476 477
	/* Run app main event loop */
	int ret = app.exec();

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

478 479 480 481
	/*
	 * Store the configuration only when PIN has been recovered or is not
	 * used.
	 */
482
	if (!globSet._pinVal.isEmpty() || !globSet.pinConfigured()) {
483 484 485 486
		/*
		 * The PIN was set/recovered or
		 * incomplete data to check/recover the PIN were supplied.
		 */
487
		GlobalSettingsQmlWrapper::saveAllSettings(accountModelPtr);
488
	}
489

490 491
	delete accountModelPtr;
	accountModelPtr = Q_NULLPTR;
492 493 494

	return ret;
}