/* * This file is part of smolbote. It's copyrighted by the contributors recorded * in the version control history of the file, available from its original * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote * * SPDX-License-Identifier: GPL-3.0 */ #include "browser.h" #include "configuration.h" #include "session.h" #include "util.h" #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #include #endif #include "config.h" #if defined(CONFIG_USEPLASMA) && !defined(PLASMA) #error "You have enabled Plasma integration, but Frameworks was not found." #endif #if defined(CONFIG_USEBREAKPAD) && !defined(BREAKPAD) #error "You have enabled Breakpad integration, but Breakpad was not found." #endif #ifdef CONFIG_USEBREAKPAD struct BreakpadContext { char *handler = nullptr; }; #ifdef Q_OS_LINUX #include #include // bool filter_callback (void*) // --> true: continue processing and write a minidump static bool dumpCallback(const google_breakpad::MinidumpDescriptor &descriptor, void *context, bool succeeded) { printf("Dump path: %s\n", descriptor.path()); auto *ctx = static_cast(context); if(ctx != nullptr) { if(ctx->handler != nullptr) { // fork and run 'handler master:commit path.dmp' pid_t pid = fork(); if(pid == 0) { char buffer[256]; snprintf(buffer, 256, "%s %s %s", ctx->handler, poi_Build, descriptor.path()); execlp("/bin/sh", "/bin/sh", "-c", buffer, (char *)nullptr); } } } return succeeded; } #endif // Q_OS_LINUX #endif // CONFIG_USEBREAKPAD int main(int argc, char **argv) { // a beautiful hack to be able to write to stdout on Windows #ifdef _WIN32 if(AttachConsole(ATTACH_PARENT_PROCESS)) { freopen("CONOUT$", "w", stdout); freopen("CONOUT$", "w", stderr); } #endif // Disable Chromium's crash handler so breakpad can capture crashes instead. // This has to be done before QtWebEngine gets initialized. const auto chromiumFlags = qgetenv("QTWEBENGINE_CHROMIUM_FLAGS"); if(!chromiumFlags.contains("disable-in-process-stack-traces")) { qputenv("QTWEBENGINE_CHROMIUM_FLAGS", chromiumFlags + " --disable-in-process-stack-traces"); } // create and load configuration std::unique_ptr config = std::make_unique(nullptr); #ifdef QT_DEBUG QObject::connect(config.get(), &Configuration::settingChanged, [](const std::string &path, const QString &value) { qDebug("!!! setting changed %s=[%s]", path.c_str(), qUtf8Printable(value)); }); #endif if(!config->parse(argc, argv)) { qWarning("Error parsing command line, check --help for usage."); return -1; } if(!config->parse(config->value("config").value())) { qWarning("Error parsing config file."); } // --version if(config->exists("version")) { auto ver = QVersionNumber::fromString(QLatin1String(poi_Version)); std::cout << "smolbote " << ver.toString().toStdString() << std::endl; return 0; } // --build if(config->exists("build")) { std::cout << poi_Version << std::endl; return 0; } QVector plugins; CommandHash_t pluginCommands; // Load plugins for(const QString &path : Util::files(config->value("plugins.path").value())) { QPluginLoader *loader = new QPluginLoader(path); const bool loaded = loader->load(); #ifdef QT_DEBUG qDebug("Loading plugin %s %s", qUtf8Printable(path), loaded ? "[ok]" : "[failed]"); #endif if(loaded) { plugins.append(loader); auto *plugin = qobject_cast(loader->instance()); pluginCommands.unite(plugin->commands()); } else { qDebug("%s", qUtf8Printable(loader->errorString())); delete loader; } } if(config->exists("help")) { std::cout << "smolbote " << poi_Version << ": yet another no-frills browser" << std::endl; std::cout << "Usage: " << argv[0] << " [options] [command/URL(s)]" << std::endl << std::endl; std::cout << "Command-line Options: " << std::endl << config->commandlineOptions() << std::endl; std::cout << "Commands: " << std::endl; for(auto it = pluginCommands.constBegin(); it != pluginCommands.constEnd(); ++it) { std::cout << it.key().toStdString() << std::endl; } std::cout << std::endl; std::cout << "Configuration Options: " << std::endl << config->configurationOptions() << std::endl; #ifdef Q_OS_LINUX std::cout << std::endl << "For more information refer to the manual page smolbote.7" << std::endl; #endif return 0; } // argc, argv, allowSecondary Browser app(argc, argv); // set this, otherwise the webview becomes black when using a stylesheet app.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); #ifdef CONFIG_USEBREAKPAD const std::string crashpath = config->value("browser.crash.path").value_or("/tmp"); assert(!crashpath.empty()); BreakpadContext ctx; google_breakpad::MinidumpDescriptor descriptor(crashpath.c_str()); const auto crashHandler = config->value("browser.crash.handler"); if(crashHandler) { const int length = crashHandler.value().length() + 1; ctx.handler = new char[length]; crashHandler.value().copy(ctx.handler, length - 1); ctx.handler[length - 1] = '\0'; } // minidump descriptor, filter callback, minidump callback, callback_context, install handler, server_fd google_breakpad::ExceptionHandler eh(descriptor, nullptr, dumpCallback, &ctx, true, -1); #ifdef QT_DEBUG qDebug("Installed breakpad exception handler (path=%s)", crashpath.c_str()); #endif #endif // CONFIG_USEBREAKPAD // translator if(config->exists("browser.locale")) { auto *translator = new QTranslator(&app); if(translator->load("qt_" + config->value("browser.locale").value(), QLibraryInfo::location(QLibraryInfo::TranslationsPath))) app.installTranslator(translator); else delete translator; } if(config->exists("browser.translation")) { auto *translator = new QTranslator(&app); if(translator->load(config->value("browser.translation").value())) app.installTranslator(translator); else delete translator; } // command line arguments bool isStandalone = config->exists("no-remote"); auto arguments = config->value>("args"); auto session = config->value("browser.session"); auto profile = config->value("profile.default"); app.setConfiguration(config); app.setup(plugins); QStringList urls; if(arguments) { for(const auto &u : arguments.value()) { if(pluginCommands.contains(QString::fromStdString(u))) { return pluginCommands.value(QString::fromStdString(u))(); } else { urls.append(QString::fromStdString(u)); } } } // if app is primary, create new sessions from received messages if(app.isPrimary() && !isStandalone) { QObject::connect(&app, &Browser::receivedMessage, &app, [&app](quint32 instanceId, QByteArray message) { Q_UNUSED(instanceId); auto doc = QJsonDocument::fromJson(message); app.createSession(doc.object()); }); } { QJsonObject sessionData; if(session) { QFile sessionJson(session.value()); if(sessionJson.open(QIODevice::ReadOnly | QIODevice::Text)) { sessionData = QJsonDocument::fromJson(sessionJson.readAll()).object(); sessionJson.close(); } } else { sessionData = Session::window(profile.value(), urls); } if(app.isPrimary() || isStandalone) { app.createSession(sessionData); } else { // app is secondary and not standalone return app.sendMessage(QJsonDocument(sessionData).toJson()); } } return app.exec(); }