File indexing completed on 2024-04-21 04:59:27

0001 // SPDX-FileCopyrightText: 2018-2019 Black Hat <bhat@encom.eu.org>
0002 // SPDX-License-Identifier: GPL-3.0-only
0003 
0004 #include <QCommandLineParser>
0005 #include <QIcon>
0006 #include <QNetworkDiskCache>
0007 #include <QNetworkProxyFactory>
0008 #include <QObject>
0009 #include <QQmlApplicationEngine>
0010 #include <QQmlContext>
0011 #include <QQmlNetworkAccessManagerFactory>
0012 #include <QQuickStyle>
0013 #include <QQuickWindow>
0014 
0015 #ifdef Q_OS_ANDROID
0016 #include <QGuiApplication>
0017 #else
0018 #include <QApplication>
0019 #endif
0020 
0021 #ifdef HAVE_WEBVIEW
0022 #include <QtWebView>
0023 #endif
0024 
0025 #include <KAboutData>
0026 #ifdef HAVE_KDBUSADDONS
0027 #include <KDBusService>
0028 #endif
0029 #ifdef HAVE_WINDOWSYSTEM
0030 #include <KWindowSystem>
0031 #endif
0032 #include <KLocalizedContext>
0033 #include <KLocalizedString>
0034 
0035 #include "neochat-version.h"
0036 
0037 #include <Quotient/networkaccessmanager.h>
0038 
0039 #include "blurhashimageprovider.h"
0040 #include "colorschemer.h"
0041 #include "controller.h"
0042 #include "logger.h"
0043 #include "matriximageprovider.h"
0044 #include "neochatconfig.h"
0045 #include "roommanager.h"
0046 #include "windowcontroller.h"
0047 
0048 #ifdef HAVE_RUNNER
0049 #include "runner.h"
0050 #include <QDBusConnection>
0051 #endif
0052 
0053 #ifdef Q_OS_WINDOWS
0054 #include <Windows.h>
0055 #endif
0056 
0057 using namespace Quotient;
0058 
0059 void qml_register_types_org_kde_neochat();
0060 
0061 class NetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory
0062 {
0063     QNetworkAccessManager *create(QObject *) override
0064     {
0065         auto nam = NetworkAccessManager::instance();
0066         nam->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
0067 
0068         nam->enableStrictTransportSecurityStore(true, QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/hsts/"));
0069         nam->setStrictTransportSecurityEnabled(true);
0070 
0071         auto namDiskCache = new QNetworkDiskCache(nam);
0072         namDiskCache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/nam/"));
0073         nam->setCache(namDiskCache);
0074 
0075         return nam;
0076     }
0077 };
0078 
0079 static QWindow *windowFromEngine(QQmlApplicationEngine *engine)
0080 {
0081     const auto rootObjects = engine->rootObjects();
0082     auto *window = qobject_cast<QQuickWindow *>(rootObjects.first());
0083     Q_ASSERT(window);
0084     return window;
0085 }
0086 
0087 #ifdef Q_OS_ANDROID
0088 Q_DECL_EXPORT
0089 #endif
0090 int main(int argc, char *argv[])
0091 {
0092     QNetworkProxyFactory::setUseSystemConfiguration(true);
0093 
0094 #ifdef HAVE_WEBVIEW
0095     QtWebView::initialize();
0096     QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
0097     QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi);
0098 #endif
0099 
0100 #ifdef Q_OS_ANDROID
0101     QGuiApplication app(argc, argv);
0102     QQuickStyle::setStyle(QStringLiteral("org.kde.breeze"));
0103 #else
0104     QIcon::setFallbackThemeName("breeze"_ls);
0105     QApplication app(argc, argv);
0106     // Default to org.kde.desktop style unless the user forces another style
0107     if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) {
0108         QQuickStyle::setStyle(QStringLiteral("org.kde.desktop"));
0109     }
0110 #endif
0111 
0112 #ifdef Q_OS_WINDOWS
0113     if (AttachConsole(ATTACH_PARENT_PROCESS)) {
0114         freopen("CONOUT$", "w", stdout);
0115         freopen("CONOUT$", "w", stderr);
0116     }
0117 
0118     QApplication::setStyle(QStringLiteral("breeze"));
0119     QFont font(QStringLiteral("Segoe UI Emoji"));
0120     font.setPointSize(10);
0121     font.setHintingPreference(QFont::PreferNoHinting);
0122     app.setFont(font);
0123 #endif
0124     KLocalizedString::setApplicationDomain(QByteArrayLiteral("neochat"));
0125 
0126     QGuiApplication::setOrganizationName("KDE"_ls);
0127 
0128     KAboutData about(QStringLiteral("neochat"),
0129                      i18n("NeoChat"),
0130                      QStringLiteral(NEOCHAT_VERSION_STRING),
0131                      i18n("Matrix client"),
0132                      KAboutLicense::GPL_V3,
0133                      i18n("© 2018-2020 Black Hat, 2020-2024 KDE Community"));
0134     about.addAuthor(i18n("Carl Schwan"),
0135                     i18n("Maintainer"),
0136                     QStringLiteral("carl@carlschwan.eu"),
0137                     QStringLiteral("https://carlschwan.eu"),
0138                     QStringLiteral("https://carlschwan.eu/avatar.png"));
0139     about.addAuthor(i18n("Tobias Fella"), i18n("Maintainer"), QStringLiteral("tobias.fella@kde.org"), QStringLiteral("https://tobiasfella.de"));
0140     about.addAuthor(i18n("James Graham"), i18n("Maintainer"), QStringLiteral("james.h.graham@protonmail.com"));
0141     about.addCredit(i18n("Black Hat"), i18n("Original author of Spectral"), QStringLiteral("bhat@encom.eu.org"));
0142     about.addCredit(i18n("Alexey Rusakov"), i18n("Maintainer of Quotient"), QStringLiteral("Kitsune-Ral@users.sf.net"));
0143     about.setTranslator(i18nc("NAME OF TRANSLATORS", "Your names"), i18nc("EMAIL OF TRANSLATORS", "Your emails"));
0144     about.setOrganizationDomain("kde.org");
0145 
0146     about.addComponent(QStringLiteral("libQuotient"),
0147                        i18n("A Qt library to write cross-platform clients for Matrix"),
0148                        i18nc("<version number> (built against <possibly different version number>)",
0149                              "%1 (built against %2)",
0150                              Quotient::versionString(),
0151                              QStringLiteral(Quotient_VERSION_STRING)),
0152                        QStringLiteral("https://github.com/quotient-im/libquotient"),
0153                        KAboutLicense::LGPL_V2_1);
0154 
0155     KAboutData::setApplicationData(about);
0156     QGuiApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("org.kde.neochat")));
0157 
0158     initLogging();
0159 
0160     Connection::setEncryptionDefault(true);
0161 
0162 #ifdef NEOCHAT_FLATPAK
0163     // Copy over the included FontConfig configuration to the
0164     // app's config dir:
0165     QFile::copy(QStringLiteral("/app/etc/fonts/conf.d/99-noto-mono-color-emoji.conf"),
0166                 QStringLiteral("/var/config/fontconfig/conf.d/99-noto-mono-color-emoji.conf"));
0167 #endif
0168 
0169     ColorSchemer colorScheme;
0170     if (!NeoChatConfig::self()->colorScheme().isEmpty()) {
0171         colorScheme.apply(NeoChatConfig::self()->colorScheme());
0172     }
0173 
0174     QCommandLineParser parser;
0175     parser.setApplicationDescription(i18n("Client for the matrix communication protocol"));
0176     parser.addPositionalArgument(QStringLiteral("urls"), i18n("Supports matrix: url scheme"));
0177     parser.addOption(QCommandLineOption("ignore-ssl-errors"_ls, i18n("Ignore all SSL Errors, e.g., unsigned certificates.")));
0178 
0179     QCommandLineOption testOption("test"_ls, i18n("Only used for autotests"));
0180     testOption.setFlags(QCommandLineOption::HiddenFromHelp);
0181     parser.addOption(testOption);
0182 
0183 #ifdef HAVE_KUNIFIEDPUSH
0184     QCommandLineOption dbusActivatedOption(QStringLiteral("dbus-activated"), i18n("Internal usage only."));
0185     dbusActivatedOption.setFlags(QCommandLineOption::Flag::HiddenFromHelp);
0186     parser.addOption(dbusActivatedOption);
0187 #endif
0188 
0189     about.setupCommandLine(&parser);
0190     parser.process(app);
0191     about.processCommandLine(&parser);
0192     Controller::setTestMode(parser.isSet("test"_ls));
0193 
0194 #ifdef HAVE_KUNIFIEDPUSH
0195     if (parser.isSet(dbusActivatedOption)) {
0196         // We want to be replaceable by the main client
0197         KDBusService service(KDBusService::Replace);
0198 
0199         Controller::listenForNotifications();
0200         return QCoreApplication::exec();
0201     }
0202 #endif
0203 
0204 #ifdef HAVE_KDBUSADDONS
0205     KDBusService service(KDBusService::Unique);
0206 #endif
0207 
0208     qml_register_types_org_kde_neochat();
0209     qmlRegisterSingletonInstance("org.kde.neochat.config", 1, 0, "Config", NeoChatConfig::self());
0210     qmlRegisterSingletonInstance("org.kde.neochat.accounts", 1, 0, "AccountRegistry", &Controller::instance().accounts());
0211 
0212     qmlRegisterUncreatableType<KeyVerificationSession>("com.github.quotient_im.libquotient", 1, 0, "KeyVerificationSession", {});
0213 
0214     QQmlApplicationEngine engine;
0215 
0216 #ifdef HAVE_KDBUSADDONS
0217     service.connect(&service,
0218                     &KDBusService::activateRequested,
0219                     &RoomManager::instance(),
0220                     [&engine](const QStringList &arguments, const QString &workingDirectory) {
0221                         Q_UNUSED(workingDirectory);
0222 
0223                         QWindow *window = windowFromEngine(&engine);
0224                         KWindowSystem::updateStartupId(window);
0225 
0226                         WindowController::instance().showAndRaiseWindow(QString());
0227 
0228                         // Open matrix uri
0229                         if (arguments.isEmpty()) {
0230                             return;
0231                         }
0232                         auto args = arguments;
0233                         args.removeFirst();
0234                         for (const auto &arg : args) {
0235                             RoomManager::instance().resolveResource(arg);
0236                         }
0237                     });
0238 #endif
0239 
0240     engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
0241     QObject::connect(&engine, &QQmlApplicationEngine::quit, &app, &QCoreApplication::quit);
0242     engine.setNetworkAccessManagerFactory(new NetworkAccessManagerFactory());
0243 
0244     if (parser.isSet("ignore-ssl-errors"_ls)) {
0245         QObject::connect(NetworkAccessManager::instance(), &QNetworkAccessManager::sslErrors, NetworkAccessManager::instance(), [](QNetworkReply *reply) {
0246             reply->ignoreSslErrors();
0247         });
0248     }
0249 
0250     engine.addImageProvider(QLatin1String("mxc"), MatrixImageProvider::create(&engine, &engine));
0251     engine.addImageProvider(QLatin1String("blurhash"), new BlurhashImageProvider);
0252 
0253     engine.load(QUrl(QStringLiteral("qrc:/org/kde/neochat/qml/main.qml")));
0254     if (engine.rootObjects().isEmpty()) {
0255         return -1;
0256     }
0257 
0258     if (!parser.positionalArguments().isEmpty()) {
0259         RoomManager::instance().setUrlArgument(parser.positionalArguments()[0]);
0260     }
0261 
0262 #ifdef HAVE_RUNNER
0263     auto runner = Runner::create(&engine, &engine);
0264     QDBusConnection::sessionBus().registerObject("/RoomRunner"_ls, runner, QDBusConnection::ExportScriptableContents);
0265 #endif
0266 
0267     QWindow *window = windowFromEngine(&engine);
0268 
0269     WindowController::instance().setWindow(window);
0270     WindowController::instance().restoreGeometry();
0271 
0272     return app.exec();
0273 }