/* * Copyright (C) 2014-2018 CZ.NIC * * 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 . * * 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 #include #include #include #include #include #include #include #include #include "src/accounts.h" #include "src/datovka_shared/io/records_management_db.h" #include "src/datovka_shared/localisation/localisation.h" #include "src/dialogues/qml_dialogue_helper.h" #include "src/dialogues/qml_input_dialogue.h" #include "src/dialogues/dialogues.h" #include "src/files.h" #include "src/global.h" #include "src/io/filesystem.h" #include "src/locker.h" #include "src/log/log.h" #include "src/net/isds_wrapper.h" #include "src/models/accountmodel.h" #include "src/models/databoxmodel.h" #include "src/models/filemodel.h" #include "src/models/list_sort_filter_proxy_model.h" #include "src/models/messagemodel.h" #if defined(Q_OS_ANDROID) #include "src/os_android.h" #endif /* defined(Q_OS_ANDROID) */ #include "src/qml_interaction/attachment_data.h" #include "src/qml_interaction/image_provider.h" #include "src/qml_interaction/interaction_filesystem.h" #include "src/qml_interaction/interaction_zfo_file.h" #include "src/qml_interaction/message_envelope.h" #include "src/qml_interaction/string_manipulation.h" #include "src/records_management/models/upload_hierarchy_list_model.h" #include "src/records_management/models/upload_hierarchy_qml_proxy_model.h" #include "src/records_management.h" #include "src/settings.h" #include "src/setwrapper.h" #include "src/sqlite/db_tables.h" #include "src/sqlite/account_db.h" #include "src/sqlite/file_db_container.h" #include "src/sqlite/message_db_container.h" #include "src/sqlite/zfo_db.h" #include "src/worker/emitter.h" #include "src/worker/pool.h" #include "src/zfo.h" /* iOS app_delegate - for interaction with iOS action Open in... */ #if defined Q_OS_IOS #include "ios/src/qt_app_delegate.h" #endif /* ACCOUNT DB filename */ #define ACNT_DB_NAME "accounts.db" /* APPLICATION IDENTIFICATION */ #define APP_ORG_NAME "CZ.NIC, z.s.p.o." #define APP_ORG_DOMAIN "cz.nic" #define DEFAULT_FONT_FAMILY "System" /* namespace for QML registered objects */ const char *uri = "cz.nic.mobileDatovka"; /* Pages and components. */ /*! * @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[] = { { "PageAboutApp", 1, 0 }, { "PageAccountDetail", 1, 0 }, { "PageAccountList", 1, 0 }, { "PageChangePassword", 1, 0 }, { "PageContactList", 1, 0 }, { "PageDataboxDetail", 1, 0 }, { "PageDataboxSearch", 1, 0 }, { "PageImportMessage", 1, 0 }, { "PageMenuAccount", 1, 0 }, { "PageMenuDatovkaSettings", 1, 0 }, { "PageMenuMessage", 1, 0 }, { "PageMenuMessageDetail", 1, 0 }, { "PageMenuMessageList", 1, 0 }, { "PageMessageDetail", 1, 0 }, { "PageMessageList", 1, 0 }, { "PageMessageSearch", 1, 0 }, { "PageRecordsManagementUpload", 1, 0 }, { "PageSendMessage", 1, 0 }, { "PageSettingsAccount", 1, 0 }, { "PageSettingsGeneral", 1, 0 }, { "PageSettingsPin", 1, 0 }, { "PageSettingsRecordsManagement", 1, 0 }, { "PageSettingsStorage", 1, 0 }, { "PageSettingsSync", 1, 0 }, { NULL, 0, 0 } }; #define QML_COMPONENT_LOC "qrc:/qml/components" /*! * @brief NULL-terminated list of components. */ static const struct QmlTypeEntry qmlComponents[] = { { "AccessibleButton", 1, 0 }, { "AccessibleButtonWithImage", 1, 0 }, { "AccessibleComboBox", 1, 0 }, { "AccessibleImageButton", 1, 0 }, { "AccessibleMenu", 1, 0 }, { "AccessibleOverlaidImageButton", 1, 0 }, { "AccessibleSpinBox", 1, 0 }, { "AccessibleSpinBoxZeroMax", 1, 0 }, { "AccessibleSwitch", 1, 0 }, { "AccessibleTabButton", 1, 0 }, { "AccessibleText", 1, 0 }, { "AccessibleTextButton", 1, 0 }, { "AccessibleTextField", 1, 0 }, { "DataboxList", 1, 0 }, { "FilterBar", 1, 0 }, { "InputLineMenu", 1, 0 }, { "MessageList", 1, 0 }, { "OverlaidImage", 1, 0 }, { "PageHeader", 1, 0 }, { "ProgressBar", 1, 0 }, { "ScrollableListView", 1, 0 }, { NULL, 0, 0 } }; #define QML_DIALOGUES "qrc:/qml/dialogues" /*! * @brief NULL-terminated list of components. */ static const struct QmlTypeEntry qmlDialogues[] = { { "FileDialogue", 1, 0 }, { "InputDialogue", 1, 0}, { "MessageDialogue", 1, 0}, { "PasteInputDialogue", 1, 0 }, { NULL, 0, 0 } }; /*! * @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; } /*! * @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) { 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; } } int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QQuickStyle::setStyle("Material"); //qputenv("QT_QUICK_CONTROLS_STYLE", "material"); /* First thing to create is the application object. */ QApplication app(argc, argv); /* * This object needs to be created in the same thread as the * application. */ QmlDlgHelper dlgHelper; QmlDlgHelper::dlgEmitter = &dlgHelper; /* Perform check that configuration file can be accessed. */ const QString settingsFileName(Settings::settingsPath()); if (settingsFileName.isEmpty()) { return EXIT_FAILURE; } QCoreApplication::addLibraryPath("./"); /* Initialise random number generator. */ qsrand(QDateTime::currentDateTimeUtc().toTime_t()); /* set application data, identification, etc. */ app.setOrganizationName(APP_ORG_NAME); app.setOrganizationDomain(APP_ORG_DOMAIN); app.setApplicationName(APP_NAME); app.setApplicationVersion(VERSION); QCommandLineParser parser; if (0 != setupCmdLineParser(parser)) { return EXIT_FAILURE; } /* Process command-line arguments. */ parser.process(app); /* Create globally accessible objects. */ { GlobInstcs::msgProcEmitterPtr = new (std::nothrow) MessageProcessingEmitter; if (GlobInstcs::msgProcEmitterPtr == Q_NULLPTR) { qCritical("Cannot create status message emitter."); return EXIT_FAILURE; } /* * Only one worker thread currently. * TODO -- To be able to run multiple threads in the pool * a locking mechanism over isds context structures must * be implemented. Also, per-context queueing * ought to be implemented to avoid unnecessary waiting. */ GlobInstcs::workPoolPtr = new (std::nothrow) WorkerPool(1); if (GlobInstcs::workPoolPtr == Q_NULLPTR) { qCritical("Cannot create worker pool."); return EXIT_FAILURE; } GlobInstcs::setPtr = new (std::nothrow) Settings; if (GlobInstcs::setPtr == Q_NULLPTR) { qCritical("cannot create settings."); return EXIT_FAILURE; } } QStringList cmdLineFileNames; #if defined(Q_OS_ANDROID) cmdLineFileNames = IntentNotification::getIntentArguments(); #else cmdLineFileNames = parser.positionalArguments(); #endif /* defined(Q_OS_ANDROID) */ /* load global settings */ { QSettings settings(Settings::settingsPath(), QSettings::IniFormat); GlobInstcs::setPtr->loadFromSettings(settings); } /* set font family and font size from settings */ QFont font; font.setFamily(DEFAULT_FONT_FAMILY); font.setPointSize(GlobInstcs::setPtr->fontSize); app.setFont(font); /* load datovka localization and qtbase localization */ QTranslator datovkaTrans; QTranslator qtbaseTrans; QString lang( Localisation::shortLangName(GlobInstcs::setPtr->language)); Localisation::setProgramLocale(GlobInstcs::setPtr->language); 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); /* Start worker threads. */ GlobInstcs::workPoolPtr->start(); logInfoNL("%s", "Worker pool started."); /* Init and use these class - we need register it to QML */ Messages messages; Accounts accounts; Files files; IsdsWrapper isds; GlobalSettingsQmlWrapper settings; RecordsManagement recordsManagement; StringManipulation strManipulation; Zfo zfo; /* Connect slot for isds cxt delete when account was deleted or updated */ QObject::connect(&accounts, SIGNAL(removeIsdsCtx(QString)), &isds, SLOT(removeIsdsCtx(QString))); /* Register application state changes. */ class Locker locker; QObject::connect(&app, SIGNAL(applicationStateChanged(Qt::ApplicationState)), &locker, SLOT(processNewState(Qt::ApplicationState))); app.installEventFilter(&locker); /* Create and init account model. */ AccountListModel *accountModelPtr = new (std::nothrow) AccountListModel; if (Q_NULLPTR == accountModelPtr) { qCritical("Cannot create account model."); return EXIT_FAILURE; } accountModelPtr->setObjectName("mainAccountModel"); /* get main handle of appliaction and QML */ QQmlApplicationEngine engine; QQmlContext *ctx = engine.rootContext(); globImgProvPtr = new (std::nothrow) ImageProvider; if (Q_NULLPTR == globImgProvPtr) { qCritical("Cannot create image provider."); return EXIT_FAILURE; } engine.addImageProvider(IMAGE_PROVIDER_ID, globImgProvPtr); /* Register application pages to QML */ registerQmlTypes(uri, QML_PAGE_LOC, qmlPages); registerQmlTypes(uri, QML_COMPONENT_LOC, qmlComponents); registerQmlTypes(uri, QML_DIALOGUES, qmlDialogues); /* Register types into QML. */ AccountListModel::declareQML(); AttachmentData::declareQML(); DataboxListModel::declareQML(); DataboxModelEntry::declareQML(); Dialogues::declareQML(); FileListModel::declareQML(); Files::declareQML(); InteractionFilesystem::declareQML(); ListSortFilterProxyModel::declareQML(); MessageListModel::declareQML(); Messages::declareQML(); MsgEnvelope::declareQML(); MsgInfo::declareQML(); UploadHierarchyListModel::declareQML(); UploadHierarchyQmlProxyModel::declareQML(); InteractionZfoFile interactionZfoFile; /* Inicialize app delegate component for interaction with iOS * Reaction on the iOS action "Open in..." */ #if defined Q_OS_IOS QtAppDelegateInitialize(&interactionZfoFile); #endif /* register classes in QML */ ctx->setContextProperty("isds", &isds); ctx->setContextProperty("messages", &messages); ctx->setContextProperty("accounts", &accounts); ctx->setContextProperty("files", &files); ctx->setContextProperty("settings", &settings); ctx->setContextProperty("strManipulation", &strManipulation); ctx->setContextProperty("locker", &locker); ctx->setContextProperty("interactionZfoFile", &interactionZfoFile); ctx->setContextProperty("dlgEmitter", QmlDlgHelper::dlgEmitter); ctx->setContextProperty("zfo", &zfo); ctx->setContextProperty("recordsManagement", &recordsManagement); /* register and set models in QML */ ctx->setContextProperty(accountModelPtr->objectName(), accountModelPtr); /* Localise description in tables. */ accntinfTbl.reloadLocalisedDescription(); userinfTbl.reloadLocalisedDescription(); msgsTbl.reloadLocalisedDescription(); flsTbl.reloadLocalisedDescription(); evntsTbl.reloadLocalisedDescription(); msgZfoTbl.reloadLocalisedDescription(); /* Init and open account database. */ AccountDb accountDb("ACCOUNTS", false); MsgDbContainer messageDbs("MESSAGES"); FileDbContainer fileDbs("FILES"); ZfoDb zfoDb("ZFOS", false); { GlobInstcs::accountDbPtr = &accountDb; GlobInstcs::messageDbsPtr = &messageDbs; GlobInstcs::fileDbsPtr = &fileDbs; GlobInstcs::zfoDbPtr = &zfoDb; GlobInstcs::recMgmtDbPtr = new (std::nothrow) RecordsManagementDb("recordsManagementDb", false); if (GlobInstcs::recMgmtDbPtr == Q_NULLPTR) { logErrorNL("%s", "Cannot allocate records management db."); return EXIT_FAILURE; } } 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 (!GlobInstcs::accountDbPtr->openDb(dbPath, SQLiteDb::CREATE_MISSING)) { logErrorNL("%s", "Account database not found!"); return EXIT_FAILURE; } /* Open records management database. */ QString rmDbPath(dirName + QDir::separator() + RECORDS_MANAGEMENT_DB_FILE); if (!GlobInstcs::recMgmtDbPtr->openDb( rmDbPath, SQLiteDb::CREATE_MISSING)) { logErrorNL("Error opening records management db '%s'.", rmDbPath.toUtf8().constData()); return EXIT_FAILURE; } /* Load accounts from settings to account model. */ { QSettings settings(Settings::settingsPath(), QSettings::IniFormat); accountModelPtr->loadAccountsFromSettings(settings); /* * No need to load counters here as some messages are likely * to be deleted. */ } /* * Open ZFO database, the second parameter means: true = zfo db will * store on disk, false = only in memory */ if (!GlobInstcs::zfoDbPtr->openDb( ZFO_DB_NAME, (GlobInstcs::setPtr->zfoDbSizeMBs > 0))) { qDebug() << "ERROR: zfo db not found!"; } /* load UI (QML) */ engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml"))); /* OpenSSL support test */ if (QSslSocket::supportsSsl()) { /* set last update text to status bar */ if (!GlobInstcs::setPtr->lastUpdateStr().isEmpty() && GlobInstcs::setPtr->pinCode.isEmpty()) { emit isds.statusBarTextChanged( QObject::tr("Last synchronisation: %1"). arg(GlobInstcs::setPtr->lastUpdateStr()), false, true); } } else { Dialogues::errorMessage(Dialogues::WARNING, QObject::tr("Security problem"), QObject::tr("OpenSSL support is required!"), QObject::tr("The device does not support OpenSSL. " "The application won't work correctly.")); } /* Deletion of messages from db is disabled when equal to 0. */ if (GlobInstcs::setPtr->msgLifeTimeInDays > 0) { messages.deleteExpiredMessagesFromDbs( GlobInstcs::setPtr->msgLifeTimeInDays); } /* Deletion of files from db is disabled when equal to 0. */ if (GlobInstcs::setPtr->fileLifeTimeInDays > 0) { files.deleteExpiredFilesFromDbs( GlobInstcs::setPtr->fileLifeTimeInDays); } /* Inactivity locking is disabled when equal to 0. */ if (GlobInstcs::setPtr->pinInactTimeoutInSecs > 0) { locker.setInactivityInterval( GlobInstcs::setPtr->pinInactTimeoutInSecs); } /* Load counters. */ Accounts::loadModelCounters(accountModelPtr); /* * Show PIN screen if needed. Encoded PIN is checked because it hasn't * been decoded yet. */ if (!GlobInstcs::setPtr->pinCode.isEmpty()) { emit locker.lockApp(); } /* * Open files passed via command line. */ if (!cmdLineFileNames.isEmpty()) { foreach (const QString &filePath, cmdLineFileNames) { interactionZfoFile.openZfoFile(filePath); } } #if defined(Q_OS_ANDROID) IntentNotification intentNotification(interactionZfoFile); QObject::connect(&app, SIGNAL(applicationStateChanged(Qt::ApplicationState)), &intentNotification, SLOT(scanIntentsForFiles(Qt::ApplicationState))); #endif /* defined(Q_OS_ANDROID) */ QmlInputDialogue::searchPersistentDialogues(&engine); /* Run app main event loop */ int ret = app.exec(); /* Wait until all threads finished. */ logInfoNL("%s", "Waiting for pending worker threads."); GlobInstcs::workPoolPtr->wait(); GlobInstcs::workPoolPtr->stop(); logInfoNL("%s", "All worker threads finished"); /* Close all OTP connections if exist */ isds.closeAllOtpConnections(); /* * Store the configuration only when PIN has been recovered or is not * used. */ if (!GlobInstcs::setPtr->_pinVal.isEmpty() || !GlobInstcs::setPtr->pinConfigured()) { /* * The PIN was set/recovered or * incomplete data to check/recover the PIN were supplied. */ GlobalSettingsQmlWrapper::saveAllSettings(accountModelPtr); } delete accountModelPtr; accountModelPtr = Q_NULLPTR; /* Destroy globally accessible objects. */ { delete GlobInstcs::recMgmtDbPtr; GlobInstcs::recMgmtDbPtr = Q_NULLPTR; GlobInstcs::zfoDbPtr = Q_NULLPTR; GlobInstcs::fileDbsPtr = Q_NULLPTR; GlobInstcs::messageDbsPtr = Q_NULLPTR; GlobInstcs::accountDbPtr = Q_NULLPTR; delete GlobInstcs::setPtr; GlobInstcs::setPtr = Q_NULLPTR; delete GlobInstcs::workPoolPtr; GlobInstcs::workPoolPtr = Q_NULLPTR; delete GlobInstcs::msgProcEmitterPtr; GlobInstcs::msgProcEmitterPtr = Q_NULLPTR; } return ret; }