proton-bridge/internal/frontend/bridge-gui/bridge-gui/CommandLine.cpp

150 lines
6.7 KiB
C++

// Copyright (c) 2024 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge 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.
//
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#include "Pch.h"
#include "CommandLine.h"
#include "Settings.h"
#include <bridgepp/CLI/CLIUtils.h>
#include <bridgepp/SessionID/SessionID.h>
using namespace bridgepp;
namespace {
QString const hyphenatedLauncherFlag = "--launcher"; ///< launcher flag parameter used for bridge.
QString const hyphenatedWindowFlag = "--no-window"; ///< The no-window command-line flag.
QString const hyphenatedSoftwareRendererFlag = "--software-renderer"; ///< The 'software-renderer' command-line flag. enable software rendering for a single execution
QString const hyphenatedSetSoftwareRendererFlag = "--set-software-renderer"; ///< The 'set-software-renderer' command-line flag. Software rendering will be used for all subsequent executions of the application.
QString const hyphenatedSetHardwareRendererFlag = "--set-hardware-renderer"; ///< The 'set-hardware-renderer' command-line flag. Hardware rendering will be used for all subsequent executions of the application.
QString const sessionIDFlag = "session-id";
QString const hyphenatedSessionIDFlag = "--" + sessionIDFlag;
//****************************************************************************************************************************************************
/// \brief Parse the log level from the command-line arguments.
///
/// \param[in] args The command-line arguments.
/// \return The log level. if not specified on the command-line, the default log level is returned.
//****************************************************************************************************************************************************
Log::Level parseLogLevel(QStringList const &args) {
QStringList levelStr = parseGoCLIStringArgument(args, {"l", "log-level"});
if (levelStr.isEmpty()) {
return Log::defaultLevel;
}
Log::Level level = Log::defaultLevel;
Log::stringToLevel(levelStr.back(), level);
return level;
}
//****************************************************************************************************************************************************
/// \brief Return the most recent sessionID parsed in command-line arguments
///
/// \param[in] args The command-line arguments.
/// \return The most recent sessionID in the list. If the list is empty, a new sessionID is created.
//****************************************************************************************************************************************************
QString mostRecentSessionID(QStringList const& args) {
QStringList const sessionIDs = parseGoCLIStringArgument(args, {sessionIDFlag});
if (sessionIDs.isEmpty()) {
return newSessionID();
}
return std::ranges::max(sessionIDs, [](QString const &lhs, QString const &rhs) -> bool {
return sessionIDToDateTime(lhs) < sessionIDToDateTime(rhs);
});
}
} // anonymous namespace
//****************************************************************************************************************************************************
/// \param[in] argv list of arguments passed to the application, including the exe name/path at index 0.
/// \return The parsed options.
//****************************************************************************************************************************************************
CommandLineOptions parseCommandLine(QStringList const &argv) {
CommandLineOptions options;
bool launcherFlagFound = false;
options.launcher = argv[0];
// for unknown reasons, on Windows QCoreApplication::arguments() frequently returns an empty list, which is incorrect, so we rebuild the argument
// list from the original argc and argv values.
for (int i = 1; i < argv.count(); i++) {
QString const &arg = argv[i];
// we can't use QCommandLineParser here since it will fail on unknown options.
// we skip session-id for now we'll process it later, with a special treatment for duplicates
if (arg == hyphenatedSessionIDFlag) {
i++; // we skip the next param, which if the flag's value.
continue;
}
if (arg.startsWith(hyphenatedSessionIDFlag + "=")) {
continue;
}
// Arguments may contain some bridge flags.
if (arg == hyphenatedSoftwareRendererFlag) {
options.bridgeGuiArgs.append(arg);
options.useSoftwareRenderer = true;
}
if (arg == hyphenatedSetSoftwareRendererFlag) {
app().settings().setUseSoftwareRenderer(true);
continue; // setting is permanent. no need to keep/pass it to bridge for restart.
}
if (arg == hyphenatedSetHardwareRendererFlag) {
app().settings().setUseSoftwareRenderer(false);
continue; // setting is permanent. no need to keep/pass it to bridge for restart.
}
if (arg == hyphenatedWindowFlag) {
options.noWindow = true;
}
if (arg == hyphenatedLauncherFlag) {
options.bridgeArgs.append(arg);
options.launcher = argv[++i];
options.bridgeArgs.append(options.launcher);
launcherFlagFound = true;
}
#ifdef QT_DEBUG
else if (arg == "--attach" || arg == "-a") {
// we don't keep the attach mode within the args since we don't need it for Bridge.
options.attach = true;
options.bridgeGuiArgs.append(arg);
}
#endif
else {
options.bridgeArgs.append(arg);
options.bridgeGuiArgs.append(arg);
}
}
if (!launcherFlagFound) {
// add bridge-gui as launcher
options.bridgeArgs.append(hyphenatedLauncherFlag);
options.bridgeArgs.append(options.launcher);
}
QStringList args;
if (!argv.isEmpty()) {
args = argv.last(argv.count() - 1);
}
options.logLevel = parseLogLevel(args);
QString const sessionID = mostRecentSessionID(args);
options.bridgeArgs.append(hyphenatedSessionIDFlag);
options.bridgeArgs.append(sessionID);
app().setSessionID(sessionID);
return options;
}