summaryrefslogtreecommitdiffstats
path: root/src/config.cpp
blob: 15d2087f0bc670370d4ad02f0923d29db7ee31cd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#include "config.h"
#include "globals.h"

#include <QFileInfo>
#include <QCommandLineOption>
#include <QCommandLineParser>
#include <QSettings>
#include <QDebug>
#include <QList>

static QCommandLineParser parser;
static QSettings* configFile = nullptr;
static QList<ConfigOption*> allOptions;

static inline QStringList combine(const QString &a, const QString &b)
{
    QStringList ret;
    if (!a.isEmpty()) {
        ret << a;
    }
    if (!b.isEmpty()) {
        ret << b;
    }
    return ret;
}

struct ConfigOption
{
    const QCommandLineOption cmdLine;
    const QString iniKey;
    const QString defaultValue;
    const bool withArgument;
    ConfigOption(const QString optShort, const QString optLong, const QString iniKey, const QString valueName, const QString defaultValue, const QString description)
        : cmdLine(combine(optLong, optShort), description, valueName, defaultValue), iniKey(iniKey), defaultValue(defaultValue), withArgument(!valueName.isEmpty())
    {
        if (!cmdLine.names().isEmpty()) {
            parser.addOption(cmdLine);
        }
        allOptions.append(this);
    }
};

const ConfigOption* const Config::ALLOW_SCREENSAVER_DISABLE = new ConfigOption("", "allow-screensaver-disable", "allow-screensaver-disable", "", "", "Show checkbox to disable the screen saver for this session");
const ConfigOption* const Config::ALLOW_VM_EDIT = new ConfigOption("", "allow-vm-edit", "allow-vm-edit", "", "", "Show edit checkbox for VMs/lectures where meta data allows editing");
const ConfigOption* const Config::AUTOQUIT = new ConfigOption("", "autoquit", "autoquit", "seconds", "120", "Time after which vmchooser will quit if no user interaction is detected");
const ConfigOption* const Config::BASEDIR = new ConfigOption("b", "base", "base", "path", "/mnt/vmstore", "Base directory where vm meta data is relative to");
const ConfigOption* const Config::CONFIG = new ConfigOption("c", "config", "", "path", CONFIG_FILE_GLOBAL, "Path to global config file");
const ConfigOption* const Config::DEBUG = new ConfigOption("D", "debug", "debug", "", "", "Enable debug mode");
const ConfigOption* const Config::DEFAULT_SESSION = new ConfigOption("d", "default", "", "uuid", "", "Preselect session with given uuid or name");
const ConfigOption* const Config::EXAM_MODE = new ConfigOption("", "exam-mode", "exam-mode", "", "", "Turn on exam mode: Limits options, forces PVS");
const ConfigOption* const Config::FULLSCREEN = new ConfigOption("F", "fullscreen", "fullscreen", "", "", "Show vmchooser in fullscreen mode");
const ConfigOption* const Config::INSECURE = new ConfigOption("k", "insecure", "insecure", "", "", "When using HTTPS, don't check server's certificate");
const ConfigOption* const Config::LOCATIONS = new ConfigOption("l", "locations", "locations", "ids..", "", "Space separated list of location ids, for list request and location-mode");
const ConfigOption* const Config::PVS = new ConfigOption("p", "pvs", "pvs", "", "", "Show 'join PVS' checkbox");
const ConfigOption* const Config::PVS_CHECKED = new ConfigOption("", "pvs-checked", "pvs-checked", "", "", "PVS checkbox is selected by default");
const ConfigOption* const Config::RUNSCRIPT = new ConfigOption("S", "runscript", "runscript", "path", RUN_VIRT_PATH, "Path to run-virt script");
const ConfigOption* const Config::WINDOW_SIZE = new ConfigOption("s", "size", "size", "WxH", "640x480", "Size of window if not using fullscreen");
const ConfigOption* const Config::DEFAULT_TAB = new ConfigOption("T", "tab", "tab", "tabno", "2", "Default tab to show, first tab being 0");
const ConfigOption* const Config::THEME = new ConfigOption("t", "theme", "theme", "name", "", "Name of theme to load");
const ConfigOption* const Config::URL_BASE = new ConfigOption("u", "url", "url", "url", "", "Base URL path to fetch resources from");
const ConfigOption* const Config::URL_LIST = new ConfigOption("", "url-list", "url-list", "url", "", "Use this URL for the VM list instead of <urlbase>/list");
const ConfigOption* const Config::URL_NEWS = new ConfigOption("", "url-news", "url-news", "url", "", "Use this URL for the news panel instead of <urlbase>/news");
const ConfigOption* const Config::URL_HELP = new ConfigOption("", "url-help", "url-help", "url", "", "Use this URL for the help panel instead of <urlbase>/help");
const ConfigOption* const Config::XSESSION_PATH = new ConfigOption("x", "xpath", "xpath", "path", VMCHOOSER_X_SESSIONS_PATH, "Path to xsession files");
const ConfigOption* const Config::LOCATION_MODE = new ConfigOption("", "location-mode", "location-mode", "mode", "BUMP", "Whether to IGNORE locations, BUMP matching entries, or EXCLUSIVE-ly show only those matching the currently configured location");
const ConfigOption* const Config::TEMPLATE_MODE = new ConfigOption("", "template-mode", "template-mode", "mode", "BUMP", "Whether to BUMP entries marked as template, or IGNORE the flag");
const ConfigOption* const Config::AUTOSTART_UUID = new ConfigOption("", "start-uuid", "start-uuid", "uuid", "", "Immediately launch session with given uuid/name");
const ConfigOption* const Config::NO_VTX = new ConfigOption("", "no-vtx", "no-vtx", "", "0", "Fade all VM sessions that would require VTX/SVM CPU capabilities");
const ConfigOption* const Config::DUMP_CONFIG = new ConfigOption("", "dump-config", "", "", "", "Dump effective configuration (config file plus command line options) to stdout");


QString Config::get(const ConfigOption* const option)
{
    if (option == nullptr)
        return QString();
    if (parser.isSet(option->cmdLine))
        return parser.value(option->cmdLine);
    if (configFile != nullptr && !option->iniKey.isEmpty())
        return configFile->value(option->iniKey, option->defaultValue).toString();
    // TODO: CONFIG_FILE_USER is currently not supported, but unused anyways
    return option->defaultValue;
}

bool Config::isSet(const ConfigOption* const option)
{
    if (option == nullptr)
        return false;
    if (parser.isSet(option->cmdLine))
        return true;
    if (configFile != nullptr && !option->iniKey.isEmpty() && configFile->contains(option->iniKey)) {
        // Option present in ini...
        if (option->withArgument) // ...and needs an argument -> set either way
            return true;
        // If it doesn't require an argument, special case
        QString val = configFile->value(option->iniKey).toString().toLower();
        if (!val.isEmpty() && val != QLatin1String("false") && val != QLatin1String("0") && val != QLatin1String("no") && val != QLatin1String("off"))
            return true; // Everything except empty string, "false", "0", "no" and "off" is considered as "is set"
        // Fall though otherwise
    }
    return false;
}

bool Config::init(const QCoreApplication& app, const ConfigOption* const configFileName)
{
    parser.addHelpOption();
    parser.addVersionOption();
    parser.process(app);
    if (configFileName != nullptr) {
        QString file(parser.value(configFileName->cmdLine));
        if (!QFileInfo(file).exists()) {
            qDebug() << "--config file" << file << "does not exist!";
            return false;
        }
        configFile = new QSettings(file, QSettings::IniFormat);
        configFile->setIniCodec("UTF-8");
        qDebug() << "Using config file" << file;
    }
    return true;
}

void Config::dump()
{
    QTextStream ts(stdout);
    for (ConfigOption *c : allOptions) {
        if (c->iniKey.isEmpty()) // Not configurable by config
            continue;
        QString val = get(c);
        if (c->withArgument) {
            if (c->defaultValue.isEmpty() && val.isEmpty()) // Defaults to empty, current value is empty, skip
                continue;
            ts << c->iniKey << "=" << val << "\n";
        } else {
            if (!isSet(c)) // Bool argument not set, default would be false anyways, skip
                continue;
            ts << c->iniKey << "=true\n";
        }
    }
}