File indexing completed on 2024-04-21 15:40:26

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 <QNetworkProxyFactory>
0007 #include <QQmlApplicationEngine>
0008 #include <QQmlContext>
0009 #include <QQmlNetworkAccessManagerFactory>
0010 #include <QQuickStyle>
0011 #include <QQuickWindow>
0012 
0013 #ifdef Q_OS_ANDROID
0014 #include <QGuiApplication>
0015 #else
0016 #include <QApplication>
0017 #endif
0018 
0019 #include <KAboutData>
0020 #ifdef HAVE_KDBUSADDONS
0021 #include <KDBusService>
0022 #endif
0023 #ifdef HAVE_WINDOWSYSTEM
0024 #include <KWindowSystem>
0025 #endif
0026 #include <KLocalizedContext>
0027 #include <KLocalizedString>
0028 
0029 #include "neochat-version.h"
0030 
0031 #include <Quotient/keyverificationsession.h>
0032 #include <Quotient/accountregistry.h>
0033 #include <Quotient/networkaccessmanager.h>
0034 #include <Quotient/room.h>
0035 #include <Quotient/util.h>
0036 
0037 #include "actionshandler.h"
0038 #include "blurhashimageprovider.h"
0039 #include "chatdocumenthandler.h"
0040 #include "clipboard.h"
0041 #include "controller.h"
0042 #include "delegatesizehelper.h"
0043 #include "filetypesingleton.h"
0044 #include "linkpreviewer.h"
0045 #include "locationhelper.h"
0046 #include "logger.h"
0047 #include "login.h"
0048 #include "matriximageprovider.h"
0049 #include "models/accountemoticonmodel.h"
0050 #include "models/collapsestateproxymodel.h"
0051 #include "models/customemojimodel.h"
0052 #include "models/devicesmodel.h"
0053 #include "models/devicesproxymodel.h"
0054 #include "models/emojimodel.h"
0055 #include "models/emoticonfiltermodel.h"
0056 #include "models/imagepacksmodel.h"
0057 #include "models/livelocationsmodel.h"
0058 #include "models/locationsmodel.h"
0059 #include "models/mediamessagefiltermodel.h"
0060 #include "models/messageeventmodel.h"
0061 #include "models/messagefiltermodel.h"
0062 #include "models/publicroomlistmodel.h"
0063 #include "models/pushrulemodel.h"
0064 #include "models/reactionmodel.h"
0065 #include "models/roomlistmodel.h"
0066 #include "models/searchmodel.h"
0067 #include "models/serverlistmodel.h"
0068 #include "models/sortfilterroomlistmodel.h"
0069 #include "models/sortfilterspacelistmodel.h"
0070 #include "models/statefiltermodel.h"
0071 #include "models/stickermodel.h"
0072 #include "models/userdirectorylistmodel.h"
0073 #include "models/userfiltermodel.h"
0074 #include "models/userlistmodel.h"
0075 #include "models/webshortcutmodel.h"
0076 #include "neochatconfig.h"
0077 #include "neochatroom.h"
0078 #include "neochatuser.h"
0079 #include "notificationsmanager.h"
0080 #include "pollhandler.h"
0081 #include "roommanager.h"
0082 #include "spacehierarchycache.h"
0083 #include "urlhelper.h"
0084 #include "windowcontroller.h"
0085 
0086 #ifdef HAVE_COLORSCHEME
0087 #include "colorschemer.h"
0088 #endif
0089 #include "models/completionmodel.h"
0090 #include "models/statemodel.h"
0091 #include "neochatuser.h"
0092 
0093 #ifdef HAVE_RUNNER
0094 #include "runner.h"
0095 #include <QDBusConnection>
0096 #endif
0097 
0098 #ifdef Q_OS_WINDOWS
0099 #include <Windows.h>
0100 #endif
0101 
0102 using namespace Quotient;
0103 
0104 class NetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory
0105 {
0106     QNetworkAccessManager *create(QObject *) override
0107     {
0108         return NetworkAccessManager::instance();
0109     }
0110 };
0111 
0112 static QWindow *windowFromEngine(QQmlApplicationEngine *engine)
0113 {
0114     const auto rootObjects = engine->rootObjects();
0115     auto *window = qobject_cast<QQuickWindow *>(rootObjects.first());
0116     Q_ASSERT(window);
0117     return window;
0118 }
0119 
0120 #ifdef Q_OS_ANDROID
0121 Q_DECL_EXPORT
0122 #endif
0123 int main(int argc, char *argv[])
0124 {
0125 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0126     QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
0127 #endif
0128 
0129     QNetworkProxyFactory::setUseSystemConfiguration(true);
0130 
0131 #ifdef Q_OS_ANDROID
0132     QGuiApplication app(argc, argv);
0133     QQuickStyle::setStyle(QStringLiteral("org.kde.breeze"));
0134 #else
0135     QIcon::setFallbackThemeName("breeze");
0136     QApplication app(argc, argv);
0137     // Default to org.kde.desktop style unless the user forces another style
0138     if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) {
0139         QQuickStyle::setStyle(QStringLiteral("org.kde.desktop"));
0140     }
0141 #endif
0142 
0143 #ifdef Q_OS_WINDOWS
0144     if (AttachConsole(ATTACH_PARENT_PROCESS)) {
0145         freopen("CONOUT$", "w", stdout);
0146         freopen("CONOUT$", "w", stderr);
0147     }
0148 
0149     QApplication::setStyle(QStringLiteral("breeze"));
0150     QFont font(QStringLiteral("Segoe UI Emoji"));
0151     font.setPointSize(10);
0152     font.setHintingPreference(QFont::PreferNoHinting);
0153     app.setFont(font);
0154 #endif
0155     KLocalizedString::setApplicationDomain("neochat");
0156 
0157     QGuiApplication::setOrganizationName("KDE");
0158 
0159     KAboutData about(QStringLiteral("neochat"),
0160                      i18n("NeoChat"),
0161                      QStringLiteral(NEOCHAT_VERSION_STRING),
0162                      i18n("Matrix client"),
0163                      KAboutLicense::GPL_V3,
0164                      i18n("© 2018-2020 Black Hat, 2020-2023 KDE Community"));
0165     about.addAuthor(i18n("Carl Schwan"), i18n("Maintainer"), QStringLiteral("carl@carlschwan.eu"), QStringLiteral("https://carlschwan.eu"));
0166     about.addAuthor(i18n("Tobias Fella"), i18n("Maintainer"), QStringLiteral("tobias.fella@kde.org"), QStringLiteral("https://tobiasfella.de"));
0167     about.addAuthor(i18n("James Graham"), i18n("Maintainer"), QStringLiteral("james.h.graham@protonmail.com"));
0168     about.addCredit(i18n("Black Hat"), i18n("Original author of Spectral"), QStringLiteral("bhat@encom.eu.org"));
0169     about.addCredit(i18n("Alexey Rusakov"), i18n("Maintainer of Quotient"), QStringLiteral("Kitsune-Ral@users.sf.net"));
0170     about.setTranslator(i18nc("NAME OF TRANSLATORS", "Your names"), i18nc("EMAIL OF TRANSLATORS", "Your emails"));
0171     about.setOrganizationDomain("kde.org");
0172 
0173     about.addComponent(QStringLiteral("libQuotient"),
0174                        i18n("A Qt5 library to write cross-platform clients for Matrix"),
0175                        i18nc("<version number> (built against <possibly different version number>)",
0176                              "%1 (built against %2)",
0177                              Quotient::versionString(),
0178                              QStringLiteral(Quotient_VERSION_STRING)),
0179                        QStringLiteral("https://github.com/quotient-im/libquotient"),
0180                        KAboutLicense::LGPL_V2_1);
0181 
0182     KAboutData::setApplicationData(about);
0183     QGuiApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("org.kde.neochat")));
0184 
0185     initLogging();
0186 
0187 #if Quotient_VERSION_MINOR == 8
0188 #ifdef Quotient_E2EE_ENABLED
0189     Connection::setEncryptionDefault(true);
0190 #endif
0191 #endif
0192 
0193 #ifdef NEOCHAT_FLATPAK
0194     // Copy over the included FontConfig configuration to the
0195     // app's config dir:
0196     QFile::copy("/app/etc/fonts/conf.d/99-noto-mono-color-emoji.conf", "/var/config/fontconfig/conf.d/99-noto-mono-color-emoji.conf");
0197 #endif
0198 
0199     Clipboard clipboard;
0200     auto config = NeoChatConfig::self();
0201     FileTypeSingleton fileTypeSingleton;
0202 
0203     Login *login = new Login();
0204     UrlHelper urlHelper;
0205 
0206 #ifdef HAVE_COLORSCHEME
0207     ColorSchemer colorScheme;
0208     qmlRegisterSingletonInstance<ColorSchemer>("org.kde.neochat", 1, 0, "ColorSchemer", &colorScheme);
0209     if (!config->colorScheme().isEmpty()) {
0210         colorScheme.apply(config->colorScheme());
0211     }
0212 #endif
0213 
0214     qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Controller", &Controller::instance());
0215     qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "NotificationsManager", &NotificationsManager::instance());
0216     qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Clipboard", &clipboard);
0217     qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Config", config);
0218     qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "RoomManager", &RoomManager::instance());
0219     qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "FileType", &fileTypeSingleton);
0220     qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "LoginHelper", login);
0221     qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "UrlHelper", &urlHelper);
0222     qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "EmojiModel", &EmojiModel::instance());
0223     qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "AccountRegistry", &Controller::instance().accounts());
0224     qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "SpaceHierarchyCache", &SpaceHierarchyCache::instance());
0225     qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "CustomEmojiModel", &CustomEmojiModel::instance());
0226     qmlRegisterType<ActionsHandler>("org.kde.neochat", 1, 0, "ActionsHandler");
0227     qmlRegisterType<ChatDocumentHandler>("org.kde.neochat", 1, 0, "ChatDocumentHandler");
0228     qmlRegisterType<RoomListModel>("org.kde.neochat", 1, 0, "RoomListModel");
0229     qmlRegisterType<KWebShortcutModel>("org.kde.neochat", 1, 0, "WebShortcutModel");
0230     qmlRegisterType<UserListModel>("org.kde.neochat", 1, 0, "UserListModel");
0231     qmlRegisterType<MessageEventModel>("org.kde.neochat", 1, 0, "MessageEventModel");
0232     qmlRegisterType<ReactionModel>("org.kde.neochat", 1, 0, "ReactionModel");
0233     qmlRegisterType<CollapseStateProxyModel>("org.kde.neochat", 1, 0, "CollapseStateProxyModel");
0234     qmlRegisterType<MediaMessageFilterModel>("org.kde.neochat", 1, 0, "MediaMessageFilterModel");
0235     qmlRegisterType<MessageFilterModel>("org.kde.neochat", 1, 0, "MessageFilterModel");
0236     qmlRegisterType<UserFilterModel>("org.kde.neochat", 1, 0, "UserFilterModel");
0237     qmlRegisterType<PublicRoomListModel>("org.kde.neochat", 1, 0, "PublicRoomListModel");
0238     qmlRegisterType<UserDirectoryListModel>("org.kde.neochat", 1, 0, "UserDirectoryListModel");
0239     qmlRegisterType<ServerListModel>("org.kde.neochat", 1, 0, "ServerListModel");
0240     qmlRegisterType<SortFilterRoomListModel>("org.kde.neochat", 1, 0, "SortFilterRoomListModel");
0241     qmlRegisterType<SortFilterSpaceListModel>("org.kde.neochat", 1, 0, "SortFilterSpaceListModel");
0242     qmlRegisterType<DevicesModel>("org.kde.neochat", 1, 0, "DevicesModel");
0243     qmlRegisterType<DevicesProxyModel>("org.kde.neochat", 1, 0, "DevicesProxyModel");
0244     qmlRegisterType<LinkPreviewer>("org.kde.neochat", 1, 0, "LinkPreviewer");
0245     qmlRegisterType<CompletionModel>("org.kde.neochat", 1, 0, "CompletionModel");
0246     qmlRegisterType<StateModel>("org.kde.neochat", 1, 0, "StateModel");
0247     qmlRegisterType<StateFilterModel>("org.kde.neochat", 1, 0, "StateFilterModel");
0248     qmlRegisterType<SearchModel>("org.kde.neochat", 1, 0, "SearchModel");
0249     qmlRegisterType<LiveLocationsModel>("org.kde.neochat", 1, 0, "LiveLocationsModel");
0250     qmlRegisterType<LocationsModel>("org.kde.neochat", 1, 0, "LocationsModel");
0251     qmlRegisterType<PollHandler>("org.kde.neochat", 1, 0, "PollHandler");
0252     qmlRegisterType<PushRuleModel>("org.kde.neochat", 1, 0, "PushRuleModel");
0253     qmlRegisterType<StickerModel>("org.kde.neochat", 1, 0, "StickerModel");
0254     qmlRegisterType<ImagePacksModel>("org.kde.neochat", 1, 0, "ImagePacksModel");
0255     qmlRegisterType<AccountEmoticonModel>("org.kde.neochat", 1, 0, "AccountEmoticonModel");
0256     qmlRegisterType<EmoticonFilterModel>("org.kde.neochat", 1, 0, "EmoticonFilterModel");
0257     qmlRegisterType<DelegateSizeHelper>("org.kde.neochat", 1, 0, "DelegateSizeHelper");
0258     qmlRegisterUncreatableType<RoomMessageEvent>("org.kde.neochat", 1, 0, "RoomMessageEvent", "ENUM");
0259     qmlRegisterUncreatableType<PushNotificationKind>("org.kde.neochat", 1, 0, "PushNotificationKind", "ENUM");
0260     qmlRegisterUncreatableType<PushNotificationSection>("org.kde.neochat", 1, 0, "PushNotificationSection", "ENUM");
0261     qmlRegisterUncreatableType<PushNotificationState>("org.kde.neochat", 1, 0, "PushNotificationState", "ENUM");
0262     qmlRegisterUncreatableType<PushNotificationAction>("org.kde.neochat", 1, 0, "PushNotificationAction", "ENUM");
0263     qmlRegisterUncreatableType<NeoChatRoomType>("org.kde.neochat", 1, 0, "NeoChatRoomType", "ENUM");
0264     qmlRegisterUncreatableType<NeoChatUser>("org.kde.neochat", 1, 0, "NeoChatUser", {});
0265     qmlRegisterUncreatableType<NeoChatRoom>("org.kde.neochat", 1, 0, "NeoChatRoom", {});
0266 
0267     qRegisterMetaType<User *>("User*");
0268     qRegisterMetaType<User *>("const User*");
0269     qRegisterMetaType<User *>("const Quotient::User*");
0270     qRegisterMetaType<Room *>("Room*");
0271     qRegisterMetaType<Connection *>("Connection*");
0272     qRegisterMetaType<MessageEventType>("MessageEventType");
0273     qRegisterMetaType<NeoChatRoom *>("NeoChatRoom*");
0274     qRegisterMetaType<NeoChatUser *>("NeoChatUser*");
0275     qRegisterMetaType<GetRoomEventsJob *>("GetRoomEventsJob*");
0276     qRegisterMetaType<QMimeType>("QMimeType");
0277 #ifdef Quotient_E2EE_ENABLED
0278     qRegisterMetaType<KeyVerificationSession *>("KeyVerificationSession*");
0279     qmlRegisterUncreatableType<KeyVerificationSession>("org.kde.neochat", 1, 0, "KeyVerificationSession", {});
0280     qRegisterMetaType<QVector<EmojiEntry>>("QVector<EmojiEntry>");
0281 #endif
0282     qmlRegisterSingletonType("org.kde.neochat", 1, 0, "About", [](QQmlEngine *engine, QJSEngine *) -> QJSValue {
0283         return engine->toScriptValue(KAboutData::applicationData());
0284     });
0285     qmlRegisterSingletonType(QUrl("qrc:/OsmLocationPlugin.qml"), "org.kde.neochat", 1, 0, "OsmLocationPlugin");
0286     qmlRegisterSingletonType("org.kde.neochat", 1, 0, "LocationHelper", [](QQmlEngine *engine, QJSEngine *) -> QJSValue {
0287         return engine->toScriptValue(LocationHelper());
0288     });
0289 
0290 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0291     qRegisterMetaTypeStreamOperators<Emoji>();
0292 #endif
0293 
0294     QQmlApplicationEngine engine;
0295 
0296 #ifdef HAVE_KDBUSADDONS
0297     KDBusService service(KDBusService::Unique);
0298     service.connect(&service,
0299                     &KDBusService::activateRequested,
0300                     &RoomManager::instance(),
0301                     [&engine](const QStringList &arguments, const QString &workingDirectory) {
0302                         Q_UNUSED(workingDirectory);
0303 
0304                         QWindow *window = windowFromEngine(&engine);
0305                         KWindowSystem::updateStartupId(window);
0306 
0307                         WindowController::instance().showAndRaiseWindow(QString());
0308 
0309                         // Open matrix uri
0310                         if (arguments.isEmpty()) {
0311                             return;
0312                         }
0313                         auto args = arguments;
0314                         args.removeFirst();
0315                         for (const auto &arg : args) {
0316                             RoomManager::instance().openResource(arg);
0317                         }
0318                     });
0319 #endif
0320 
0321     engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
0322     QObject::connect(&engine, &QQmlApplicationEngine::quit, &app, &QCoreApplication::quit);
0323     engine.setNetworkAccessManagerFactory(new NetworkAccessManagerFactory());
0324 
0325     QCommandLineParser parser;
0326     parser.setApplicationDescription(i18n("Client for the matrix communication protocol"));
0327     parser.addPositionalArgument(QStringLiteral("urls"), i18n("Supports matrix: url scheme"));
0328 
0329     about.setupCommandLine(&parser);
0330     parser.process(app);
0331     about.processCommandLine(&parser);
0332 
0333     engine.addImageProvider(QLatin1String("mxc"), new MatrixImageProvider);
0334     engine.addImageProvider(QLatin1String("blurhash"), new BlurhashImageProvider);
0335 
0336     engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
0337     if (engine.rootObjects().isEmpty()) {
0338         return -1;
0339     }
0340 
0341     if (parser.positionalArguments().length() > 0) {
0342         RoomManager::instance().setUrlArgument(parser.positionalArguments()[0]);
0343     }
0344 
0345 #ifdef HAVE_RUNNER
0346     Runner runner;
0347     QDBusConnection::sessionBus().registerObject("/RoomRunner", &runner, QDBusConnection::ExportScriptableContents);
0348 #endif
0349 
0350     QWindow *window = windowFromEngine(&engine);
0351 
0352     WindowController::instance().setWindow(window);
0353     WindowController::instance().restoreGeometry();
0354 
0355     return app.exec();
0356 }