summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/config.cpp2
-rw-r--r--src/config.h2
-rw-r--r--src/dialog.cpp163
-rw-r--r--src/dialog.h4
-rw-r--r--src/images.qrc3
-rw-r--r--src/img/docker.pngbin0 -> 5165 bytes
-rw-r--r--src/img/qemu.pngbin2854 -> 0 bytes
-rw-r--r--src/img/qemukvm.pngbin0 -> 4059 bytes
-rw-r--r--src/main.cpp2
-rw-r--r--src/session.h12
-rw-r--r--src/ui/dialog.ui37
-rw-r--r--src/userldapdata.cpp17
-rw-r--r--src/vsession.cpp30
-rw-r--r--src/vsession.h14
-rw-r--r--src/xsession.cpp5
15 files changed, 216 insertions, 75 deletions
diff --git a/src/config.cpp b/src/config.cpp
index 0d4a72d..16fdf56 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -56,6 +56,7 @@ const ConfigOption* const Config::PVS_CHECKED = new ConfigOption("", "pvs-checke
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::FORCE_DEFAULT_TAB = new ConfigOption("", "force-tab", "force-tab", "", "", "Force the default tab to be shown, even if saved session is available");
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");
@@ -67,6 +68,7 @@ const ConfigOption* const Config::TEMPLATE_MODE = new ConfigOption("", "template
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");
+const ConfigOption* const Config::COW_TOKEN = new ConfigOption("", "cow-token", "cow-token", "token", "", "Use given token for edit mode");
QString Config::get(const ConfigOption* const option)
diff --git a/src/config.h b/src/config.h
index 011e82a..24a613d 100644
--- a/src/config.h
+++ b/src/config.h
@@ -24,6 +24,7 @@ public:
static const ConfigOption* const RUNSCRIPT;
static const ConfigOption* const WINDOW_SIZE;
static const ConfigOption* const DEFAULT_TAB;
+ static const ConfigOption* const FORCE_DEFAULT_TAB;
static const ConfigOption* const THEME;
static const ConfigOption* const URL_BASE;
static const ConfigOption* const URL_LIST;
@@ -35,6 +36,7 @@ public:
static const ConfigOption* const AUTOSTART_UUID;
static const ConfigOption* const NO_VTX;
static const ConfigOption* const DUMP_CONFIG;
+ static const ConfigOption* const COW_TOKEN;
static bool init(const QCoreApplication& app, const ConfigOption* const configFile);
static QString get(const ConfigOption* const option);
diff --git a/src/dialog.cpp b/src/dialog.cpp
index bdeab16..9dfff00 100644
--- a/src/dialog.cpp
+++ b/src/dialog.cpp
@@ -12,9 +12,9 @@
#include <QTemporaryFile>
#include <QUrlQuery>
-#include "unistd.h"
-#include "stdio.h"
-#include "sys/wait.h"
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/wait.h>
#include "ui_dialog.h"
#include "sessiontreeitem.h"
@@ -95,8 +95,10 @@ Dialog::Dialog(QWidget *parent)
this->onTabButtonChanged(TAB_NATIVE);
}
- ui->chkAdminMode->setVisible(Config::isSet(Config::ALLOW_VM_EDIT));
- ui->chkAdminMode->setEnabled(false);
+ ui->chkAdminEdit->setVisible(Config::isSet(Config::ALLOW_VM_EDIT));
+ ui->chkAdminEdit->setEnabled(false);
+ ui->chkAdminCopy->setVisible(Config::isSet(Config::ALLOW_VM_EDIT));
+ ui->chkAdminCopy->setEnabled(false);
ui->btnScreenSetup->setVisible(isProcessRunning("beamergui"));
if (QApplication::screens().size() > 1) {
@@ -110,6 +112,16 @@ Dialog::Dialog(QWidget *parent)
QObject::connect(SessionsIconHolder::get(), &SessionsIconHolder::iconDownloaded,
this, &Dialog::iconDownloaded);
+ QObject::connect(ui->chkAdminCopy, &QCheckBox::toggled, [this](bool checked) {
+ if (checked) {
+ ui->chkAdminEdit->setChecked(false);
+ }
+ });
+ QObject::connect(ui->chkAdminEdit, &QCheckBox::toggled, [this](bool checked) {
+ if (checked) {
+ ui->chkAdminCopy->setChecked(false);
+ }
+ });
}
Dialog::~Dialog() {
@@ -143,6 +155,21 @@ void Dialog::on_treeView_doubleClicked(const QModelIndex& index)
if (!s->prepareRun())
return;
+ bool adminMode = false;
+
+ if (anyEditModeRequested() && s->canEdit()) {
+ auto mode = s->editModeTypes();
+ adminMode = true;
+ if (ui->chkAdminEdit->isChecked() && ui->chkAdminEdit->isEnabled()) {
+ setenv("VMCHOOSER_ADMIN_TYPE", "EDIT", 1);
+ } else {
+ setenv("VMCHOOSER_ADMIN_TYPE", "COPY", 1);
+ }
+ if (Config::isSet(Config::COW_TOKEN)) {
+ setenv("VMCHOOSER_ADMIN_TOKEN", Config::get(Config::COW_TOKEN).toLocal8Bit().constData(), 1);
+ }
+ }
+
WindowManager::stopOwnInstance(true);
// These two are up here in case run-virt cares...
@@ -153,7 +180,12 @@ void Dialog::on_treeView_doubleClicked(const QModelIndex& index)
setenv("PVS_AUTO_CONNECT", "FALSE", 1);
}
}
- if (ui->chkAdminMode->isEnabled() && ui->chkAdminMode->isChecked()) {
+
+ if (anyEditModeRequested() && Config::isSet(Config::ALLOW_VM_EDIT)) {
+ adminMode = true;
+ }
+
+ if (adminMode) {
setenv("VMCHOOSER_ADMIN_MODE", "TRUE", 1);
} else {
setenv("VMCHOOSER_ADMIN_MODE", "FALSE", 1);
@@ -170,38 +202,40 @@ void Dialog::on_treeView_doubleClicked(const QModelIndex& index)
UserConfig::setNewsHelpOpen(!ui->helpBox->isHidden());
UserConfig::sync();
- QProcess *process = nullptr;
+ qint64 scriptPid = 0;
if (QFile::exists(SESSION_START_SCRIPT)) {
// Companion script
- process = new QProcess;
- QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
- env.insert("SESSION_NAME", s->shortDescription());
- env.insert("SESSION_UUID", s->uuid());
- env.insert("SESSION_CMD", s->execCommand());
+ setenv("SESSION_NAME", s->shortDescription().toLocal8Bit().constData(), 1);
+ setenv("SESSION_UUID", s->uuid().toLocal8Bit().constData(), 1);
+ setenv("SESSION_CMD", s->execCommand().toLocal8Bit().constData(), 1);
if (s->type() == Session::VSESSION) {
- env.insert("SESSION_TYPE", "VSESSION");
+ setenv("SESSION_TYPE", "VSESSION", 1);
} else if (s->type() == Session::XSESSION) {
- env.insert("SESSION_TYPE", "XSESSION");
+ setenv("SESSION_TYPE", "XSESSION", 1);
+ }
+ if (!QProcess::startDetached(SESSION_START_SCRIPT, QStringList(), QStringLiteral("/"), &scriptPid)) {
+ scriptPid = 0;
}
- process->setProcessEnvironment(env);
- process->start(SESSION_START_SCRIPT);
- process->closeReadChannel(QProcess::StandardError);
- process->closeReadChannel(QProcess::StandardOutput);
- process->closeWriteChannel();
}
// Run session
s->run();
// Should not return on success
- if (process != nullptr) {
- process->terminate();
+ if (scriptPid > 0) {
+ ::kill(pid_t(scriptPid), SIGTERM);
}
QMessageBox::critical(
- this, trUtf8("vmchooser"),
- trUtf8("Vmchooser failed to run the selected session!"));
+ this, tr("vmchooser"),
+ tr("Vmchooser failed to run the selected session!"));
QApplication::instance()->quit();
}
+bool Dialog::anyEditModeRequested() const
+{
+ return (ui->chkAdminEdit->isEnabled() && ui->chkAdminEdit->isChecked())
+ || (ui->chkAdminCopy->isEnabled() && ui->chkAdminCopy->isChecked());
+}
+
void Dialog::on_treeView_expanded(const QModelIndex& index) {
if (activeTab_ != TAB_ALL_VMS)
return;
@@ -293,10 +327,11 @@ bool Dialog::selectSession(const QString& name, int preferredTab) {
QModelIndex root(ui->treeView->rootIndex());
int bestTab = -1;
+ int matchLevel = 0;
+ int boost;
QModelIndex bestIndex;
for (int tab = TAB_COUNT - 1; tab >= 0; --tab) {
- if (bestTab != -1 && preferredTab != tab) // We already have a potential match, only keep going if this is the desired tab
- continue;
+ boost = (tab == preferredTab);
for (int i = 0; i < model_[tab]->rowCount(root); ++i) {
QModelIndex section(model_[tab]->index(i, 0, root));
if (!section.isValid())
@@ -313,8 +348,19 @@ bool Dialog::selectSession(const QString& name, int preferredTab) {
if ((!s->uuid().isEmpty() && s->uuid() == name) || s->shortDescription() == name) {
bestTab = tab;
bestIndex = index;
+ matchLevel = 5 + boost;
break; // Break inner, keep checking other tabs
}
+ if (matchLevel < 3 + boost && s->shortDescription().startsWith(name)) {
+ bestTab = tab;
+ bestIndex = index;
+ matchLevel = 3 + boost;
+ }
+ if (matchLevel < 1 + boost && name.length() > 3 && s->shortDescription().contains(name)) {
+ bestTab = tab;
+ bestIndex = index;
+ matchLevel = 1 + boost;
+ }
}
}
}
@@ -341,8 +387,16 @@ void Dialog::selectPreviousSession() {
return;
}
int lastTab = UserConfig::getLastTab();
+
+ if (Config::isSet(Config::FORCE_DEFAULT_TAB)) {
+ // Force specific tab preselect
+ lastTab = Config::get(Config::DEFAULT_TAB).toInt();
+ }
+
+ // Command line default session takes precedence
QString lastSession = Config::get(Config::DEFAULT_SESSION);
- if (lastSession.isEmpty()) {
+ if (lastSession.isEmpty() && !Config::isSet(Config::FORCE_DEFAULT_TAB)) {
+ // If no cmdline, use user's last session if we don't force a tab via cmdline
auto list = UserConfig::getLastSessions();
if (!list.isEmpty()) {
lastSession = list.back();
@@ -361,7 +415,7 @@ void Dialog::selectPreviousSession() {
if (lastTab >= 0 && lastTab < TAB_COUNT) {
qDebug() << "Trying to select last tab " << lastTab;
this->onTabButtonChanged(lastTab);
- } else {
+ } else if (Config::isSet(Config::DEFAULT_TAB)) {
int defaultTab = Config::get(Config::DEFAULT_TAB).toInt();
qDebug() << "Selected default tab " << defaultTab;
// Select default tab
@@ -430,7 +484,7 @@ void Dialog::onCenterTimer() {
if (autoQuit_ == 0) {
QCoreApplication::instance()->exit(0);
} else if (autoQuit_ < 60) {
- ui->lblAutoQuit->setText(trUtf8("Auto logout in %1").arg(autoQuit_));
+ ui->lblAutoQuit->setText(tr("Auto logout in %1").arg(autoQuit_));
ui->lblAutoQuit->show();
} else if (!ui->lblAutoQuit->isHidden()) {
ui->lblAutoQuit->hide();
@@ -441,26 +495,42 @@ void Dialog::onCenterTimer() {
/**
* Download lecture list, news and help
*/
-void Dialog::downloadData(const QString& locationIds) {
+void Dialog::downloadData() {
QUrl listUrl(Config::isSet(Config::URL_LIST)
? Config::get(Config::URL_LIST)
: Config::get(Config::URL_BASE).append("/list"));
QUrlQuery listQuery(listUrl);
+ bool cache = true;
+ const QString& locationIds = Config::get(Config::LOCATIONS);
+ const QString& cowToken = Config::get(Config::COW_TOKEN);
if (!locationIds.isEmpty()) {
listQuery.addQueryItem("locations", locationIds);
}
+ if (!cowToken.isEmpty()) {
+ // Don't cache lecture list containing edit mode annotations
+ listQuery.addQueryItem("cow-user", cowToken);
+ cache = false;
+ }
if (Config::isSet(Config::EXAM_MODE)) {
listQuery.addQueryItem("exams", "exam-mode");
}
listUrl.setQuery(listQuery);
//
// Download lecture XML
- FileDownloader::download(listUrl, [this](QNetworkReply::NetworkError err, const QByteArray& data) {
+ FileDownloader::download(listUrl, [cache, this](QNetworkReply::NetworkError err, const QByteArray& data) {
QList<Session*> sessions;
- QDomDocument doc = toDomDocument(QStringLiteral("lecture list"), data, TEMP_PATH_XML_LIST, QStringLiteral("settings"));
+ QDomDocument doc = toDomDocument(QStringLiteral("lecture list"), data, cache ? TEMP_PATH_XML_LIST : QString(),
+ QStringLiteral("settings"));
sessions = VSession::loadFromXmlDocument(doc);
+ QString errMsg = doc.firstChildElement(QStringLiteral("settings"))
+ .firstChildElement(QStringLiteral("error")).text();
+
+ if (!errMsg.isEmpty()) {
+ ui->filterEdit->setText(errMsg);
+ }
+
this->removeStatusString(STR_LOADING);
if (sessions.isEmpty()) {
if (err == QNetworkReply::NoError) {
@@ -473,13 +543,14 @@ void Dialog::downloadData(const QString& locationIds) {
this->addItems(sessions, TAB_ALL_VMS);
bool showEdit = false; // Only show edit button if at least one lecture is editable
for (QList<Session*>::const_iterator it = sessions.begin(); it != sessions.end(); ++it) {
- if (reinterpret_cast<VSession*>(*it)->canEdit()) {
+ if ((**it).canEdit()) {
showEdit = true;
break;
}
}
if (showEdit) {
- ui->chkAdminMode->setVisible(true);
+ ui->chkAdminEdit->setVisible(true);
+ ui->chkAdminCopy->setVisible(true);
}
}
@@ -543,11 +614,16 @@ void Dialog::mousePressEvent(QMouseEvent * event) {
userInteracted_ = true;
}
+void Dialog::setAdminChecks(int mode)
+{
+ ui->chkAdminEdit->setEnabled((mode & Session::EDIT_TYPE_EDIT) != 0);
+ ui->chkAdminCopy->setEnabled((mode & Session::EDIT_TYPE_COPY) != 0);
+}
+
void Dialog::treeView_selectionChanged(const QModelIndex& current, const QModelIndex&) {
- SessionTreeItem* item =
- static_cast<SessionTreeItem*>(current.internalPointer());
+ SessionTreeItem* item = static_cast<SessionTreeItem*>(current.internalPointer());
if (item == nullptr) {
- ui->chkAdminMode->setEnabled(false);
+ setAdminChecks(0);
return;
}
@@ -555,7 +631,7 @@ void Dialog::treeView_selectionChanged(const QModelIndex& current, const QModelI
if (!s) {
qDebug() << "invalid selection";
// no valid session has been selected, do nothing
- ui->chkAdminMode->setEnabled(false);
+ setAdminChecks(0);
return;
}
@@ -572,8 +648,12 @@ void Dialog::treeView_selectionChanged(const QModelIndex& current, const QModelI
ui->label_platform->setText(vs->getAttribute("virtualizer_name", "param"));
ui->label_platform->setToolTip(vs->getAttribute("virtualizer_name", "param"));
- // TODO: This is a bug? vs->canEdit() seems completely pointless right now...
- ui->chkAdminMode->setEnabled(vs->canEdit() || Config::isSet(Config::ALLOW_VM_EDIT));
+ // If ALLOW_EDIT is false, we still might have editable sessions if the list meta data says so
+ if (Config::isSet(Config::ALLOW_VM_EDIT)) {
+ setAdminChecks(255);
+ } else {
+ setAdminChecks(s->editModeTypes());
+ }
if (vs->keywords().length() > 0) {
description = "\n\nKeywords: ";
@@ -587,6 +667,7 @@ void Dialog::treeView_selectionChanged(const QModelIndex& current, const QModelI
ui->label_os->setText(QCoreApplication::instance()->translate("Dialog", "Native Linux"));
ui->label_platform->setText("Linux");
ui->label_platform->setToolTip("");
+ setAdminChecks(0);
}
ui->label_name->setText(s->shortDescription());
ui->label_name->setToolTip(s->shortDescription());
@@ -861,6 +942,10 @@ static QDomDocument toDomDocument(const QString& what, const QByteArray& data, c
} else {
qDebug() << "No content downloaded for" << what;
}
+ // No backup file given, just return what we got
+ if (backupFile.isEmpty())
+ return doc;
+
QFile backup(backupFile);
if (doc.isNull() || !doc.hasChildNodes()) {
if (backup.open(QFile::ReadOnly)) {
diff --git a/src/dialog.h b/src/dialog.h
index d9c5d6b..651833f 100644
--- a/src/dialog.h
+++ b/src/dialog.h
@@ -40,7 +40,7 @@ class Dialog : public QDialog {
void selectPreviousSession();
void setTheme();
void startSession(const QString& name);
- void downloadData(const QString& locationIds);
+ void downloadData();
static QDialog* getInstance();
protected: // Overrides
@@ -69,6 +69,8 @@ class Dialog : public QDialog {
void selectFirstElement();
void checkAutostart();
+ bool anyEditModeRequested() const;
+ void setAdminChecks(int mode);
private slots:
void on_pushButtonStart_clicked();
diff --git a/src/images.qrc b/src/images.qrc
index dda48f1..e12f639 100644
--- a/src/images.qrc
+++ b/src/images.qrc
@@ -23,7 +23,7 @@
<file alias="virtualbox">img/virtualbox.png</file>
<file alias="unknown">img/unknown.png</file>
<file alias="dosbox">img/dosbox.png</file>
- <file alias="qemu">img/qemu.png</file>
+ <file alias="qemukvm">img/qemukvm.png</file>
<file alias="mess">img/mess.png</file>
<file alias="beos">img/beos.png</file>
<file alias="os2">img/os2.png</file>
@@ -43,5 +43,6 @@
<file alias="dos">img/msdos.png</file>
<file alias="vm-mix">img/vm-mix.png</file>
<file alias="i3">img/i3.png</file>
+ <file alias="docker">img/docker.png</file>
</qresource>
</RCC>
diff --git a/src/img/docker.png b/src/img/docker.png
new file mode 100644
index 0000000..0dc6379
--- /dev/null
+++ b/src/img/docker.png
Binary files differ
diff --git a/src/img/qemu.png b/src/img/qemu.png
deleted file mode 100644
index fadac31..0000000
--- a/src/img/qemu.png
+++ /dev/null
Binary files differ
diff --git a/src/img/qemukvm.png b/src/img/qemukvm.png
new file mode 100644
index 0000000..03d3489
--- /dev/null
+++ b/src/img/qemukvm.png
Binary files differ
diff --git a/src/main.cpp b/src/main.cpp
index df7309c..e1a3499 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -111,7 +111,7 @@ int main(int argc, char *argv[]) {
}
}
- w.downloadData(Config::get(Config::LOCATIONS));
+ w.downloadData();
w.setTheme();
w.setWindowFlag(Qt::FramelessWindowHint, true);
w.resize(width, height);
diff --git a/src/session.h b/src/session.h
index c3f758c..e926af8 100644
--- a/src/session.h
+++ b/src/session.h
@@ -17,6 +17,12 @@ enum SectionType {
class Session {
public:
+
+ enum EditType {
+ EDIT_TYPE_EDIT = 1,
+ EDIT_TYPE_COPY = 2,
+ };
+
virtual ~Session() {}
virtual bool isActive() const = 0;
@@ -28,8 +34,8 @@ class Session {
if (!error.isEmpty()) {
if (showError) {
QMessageBox::critical(
- nullptr, QObject::trUtf8("vmchooser"),
- QObject::trUtf8("Cannot start selected session:") + QStringLiteral("\n") + error);
+ nullptr, QObject::tr("vmchooser"),
+ QObject::tr("Cannot start selected session:") + QStringLiteral("\n") + error);
}
return false;
}
@@ -46,6 +52,8 @@ class Session {
virtual int type() const = 0;
virtual SectionType section() const = 0;
virtual bool needsVtx() const { return false; }
+ virtual bool canEdit() const { return false; }
+ virtual int editModeTypes() const { return 0; }
virtual QVariant foregroundRole() const { return QVariant(); }
virtual bool operator<(const Session& s) const = 0;
diff --git a/src/ui/dialog.ui b/src/ui/dialog.ui
index b6d7822..0e195ea 100644
--- a/src/ui/dialog.ui
+++ b/src/ui/dialog.ui
@@ -63,7 +63,7 @@ margin-bottom:0px;}</string>
<string/>
</property>
<property name="pixmap">
- <pixmap resource="../images.qrc">:/title_l</pixmap>
+ <pixmap>:/title_l</pixmap>
</property>
</widget>
</item>
@@ -94,7 +94,7 @@ margin-bottom:0px;}</string>
<string/>
</property>
<property name="pixmap">
- <pixmap resource="../images.qrc">:/title_r</pixmap>
+ <pixmap>:/title_r</pixmap>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@@ -133,8 +133,11 @@ margin-bottom:0px;}</string>
</property>
<property name="html">
<string notr="true">&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
-&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;meta charset=&quot;utf-8&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
+hr { height: 1px; border-width: 0; }
+li.unchecked::marker { content: &quot;\2610&quot;; }
+li.checked::marker { content: &quot;\2612&quot;; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Sans';&quot;&gt;Loading...&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
@@ -168,8 +171,11 @@ p, li { white-space: pre-wrap; }
</property>
<property name="html">
<string notr="true">&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
-&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;meta charset=&quot;utf-8&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
+hr { height: 1px; border-width: 0; }
+li.unchecked::marker { content: &quot;\2610&quot;; }
+li.checked::marker { content: &quot;\2612&quot;; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Sans';&quot;&gt;Loading...&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
@@ -213,7 +219,7 @@ p, li { white-space: pre-wrap; }
<string>Local</string>
</property>
<property name="icon">
- <iconset resource="../images.qrc">
+ <iconset>
<normaloff>:/linux</normaloff>:/linux</iconset>
</property>
<property name="iconSize">
@@ -255,7 +261,7 @@ p, li { white-space: pre-wrap; }
<string>All Classes</string>
</property>
<property name="icon">
- <iconset resource="../images.qrc">
+ <iconset>
<normaloff>:/vm-mix</normaloff>:/vm-mix</iconset>
</property>
<property name="iconSize">
@@ -499,8 +505,11 @@ border:1px solid #999;
</property>
<property name="html">
<string notr="true">&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
-&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;meta charset=&quot;utf-8&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
+hr { height: 1px; border-width: 0; }
+li.unchecked::marker { content: &quot;\2610&quot;; }
+li.checked::marker { content: &quot;\2612&quot;; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Sans';&quot;&gt;Click on an item on the left side for more information&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
@@ -539,13 +548,20 @@ p, li { white-space: pre-wrap; }
<item>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
- <widget class="QCheckBox" name="chkAdminMode">
+ <widget class="QCheckBox" name="chkAdminEdit">
<property name="text">
<string>Edit VM</string>
</property>
</widget>
</item>
<item>
+ <widget class="QCheckBox" name="chkAdminCopy">
+ <property name="text">
+ <string>Copy VM</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QCheckBox" name="chkNoScreenSaver">
<property name="text">
<string>Disable ScreenSaver and Standby</string>
@@ -619,7 +635,6 @@ p, li { white-space: pre-wrap; }
<property name="font">
<font>
<pointsize>9</pointsize>
- <weight>50</weight>
<bold>false</bold>
</font>
</property>
@@ -670,8 +685,6 @@ p, li { white-space: pre-wrap; }
<header>src/vmtree.h</header>
</customwidget>
</customwidgets>
- <resources>
- <include location="../images.qrc"/>
- </resources>
+ <resources/>
<connections/>
</ui>
diff --git a/src/userldapdata.cpp b/src/userldapdata.cpp
index 4382800..d4f966f 100644
--- a/src/userldapdata.cpp
+++ b/src/userldapdata.cpp
@@ -4,6 +4,7 @@
#include <QFile>
#include <QDebug>
#include <QDir>
+#include <QRegularExpression>
#define INCBREAK if (++i >= len) break
@@ -39,6 +40,7 @@ bool init(QString inputFile)
keyEnd = i;
INCBREAK;
if (p[i] == ':') {
+ // Double ":", means base64 encoded data
INCBREAK;
b64 = true;
}
@@ -53,6 +55,7 @@ bool init(QString inputFile)
while (dataLen > 0 && p[dataStart + dataLen - 1] == '\n') {
dataLen--;
}
+ // Put "lowercase(key):value" into set
QString value = QString::fromUtf8(p, keyEnd).toLower() + ":";
if (b64) {
value += QString::fromUtf8(QByteArray::fromBase64(QByteArray(p + dataStart, dataLen)));
@@ -71,8 +74,18 @@ bool isEmpty()
bool isAllowed(const QString& attribute, const QString& value)
{
- const QString keyLow(attribute.toLower() + ":" + value);
- return _entries.contains(keyLow);
+ if (value.indexOf(QLatin1Char('*')) == -1 && value.indexOf(QLatin1Char('?')) == -1) {
+ const QString keyLow(attribute.toLower() + QLatin1String(":") + value);
+ return _entries.contains(keyLow);
+ }
+ // Wildcard matching
+ QRegularExpression re(QLatin1String("^") + QRegularExpression::escape(attribute.toLower()) + QLatin1String(":")
+ + QRegularExpression::wildcardToRegularExpression(value));
+ for (auto str : _entries) {
+ if (re.match(str).hasMatch())
+ return true;
+ }
+ return false;
}
}
diff --git a/src/vsession.cpp b/src/vsession.cpp
index b61c759..a3334c8 100644
--- a/src/vsession.cpp
+++ b/src/vsession.cpp
@@ -104,27 +104,33 @@ QIcon VSession::icon() const {
return res_icon;
}
// Everything failed, try to guess the OS
- QString os(this->os().toLower());
- os = os.replace(cleanNameRegex, QString());
- if (!os.isEmpty()) {
+ QString os1(this->os().toLower());
+ QString os2(this->osDisplayName().toLower());
+ os1 = os1.replace(cleanNameRegex, QString());
+ os2 = os2.replace(cleanNameRegex, QString());
+ if (!os1.isEmpty() || !os2.isEmpty()) {
// Now try known good OS names via .startsWith()
for (const QString& str : directOsNames) {
- if (os.startsWith(str))
+ if (os1.startsWith(str) || os2.startsWith(str))
return iconHolder->getIcon(str);
}
// Fuzzy matching via regex
for (const IconMap& map : iconMapping) {
- if (map.expr.match(os).hasMatch())
+ if (map.expr.match(os1).hasMatch() || map.expr.match(os2).hasMatch())
return iconHolder->getIcon(map.icon);
}
// Running out of ideas...
- if (os.startsWith(QStringLiteral("win")))
+ if (os2.startsWith(QStringLiteral("win")))
return iconHolder->getIcon(QStringLiteral("windows"));
- if (os.contains(QStringLiteral("linux")))
+ if (os2.contains(QStringLiteral("linux")))
return iconHolder->getIcon(QStringLiteral("linux"));
}
// Fallback to generic virtualizer icon (if found)
- return iconHolder->getIcon(virtualizer());
+ QIcon ret = iconHolder->getIcon(virtualizer());
+ if (ret.isNull()) {
+ ret = QIcon(QPixmap(64, 64));
+ }
+ return ret;
}
QString VSession::toXml() const {
@@ -219,10 +225,10 @@ bool VSession::isLocked() const {
QString VSession::checkCanRunInternal() const {
if (getAttribute(QStringLiteral("image_name")).isEmpty())
- return QObject::trUtf8("XML error: image_name is empty");
+ return QObject::tr("XML error: image_name is empty");
const Virtualizer* virt = Virtualizer::get(virtualizer());
if (!virt->isAvailable)
- return QObject::trUtf8("Virtualizer '%1' is not enabled.").arg(virt->id);
+ return QObject::tr("Virtualizer '%1' is not enabled.").arg(virt->id);
// Seems OK
return QString();
}
@@ -247,8 +253,8 @@ bool VSession::prepareRun() const {
tmpfile.write(this->toXml().toUtf8()) == -1) {
qDebug() << "Error writing xml to file" << tmpfile.fileName();
QMessageBox::critical(
- nullptr, QObject::trUtf8("vmchooser"),
- QObject::trUtf8("Error writing temporary XML file for run-virt"));
+ nullptr, QObject::tr("vmchooser"),
+ QObject::tr("Error writing temporary XML file for run-virt"));
return false;
}
if (!tmpFileName.isEmpty()) {
diff --git a/src/vsession.h b/src/vsession.h
index e571805..2641008 100644
--- a/src/vsession.h
+++ b/src/vsession.h
@@ -25,8 +25,12 @@ class VSession : public Session {
return getAttribute(QStringLiteral("for_location")).toInt() != 0;
}
- bool canEdit() const {
- return getAttribute(QStringLiteral("allow_edit")).toInt() != 0;
+ bool canEdit() const override {
+ return editModeTypes() != 0;
+ }
+
+ int editModeTypes() const override {
+ return getAttribute(QStringLiteral("allow_edit")).toInt();
}
QString virtualizer() const {
@@ -47,6 +51,10 @@ class VSession : public Session {
return getAttribute(QStringLiteral("os"));
}
+ QString osDisplayName() const {
+ return getAttribute(QStringLiteral("os_name"));
+ }
+
QString uuid() const {
return getAttribute(QStringLiteral("uuid"));
}
@@ -88,7 +96,7 @@ class VSession : public Session {
static QList<Session*> loadFromXmlDocument(const QDomDocument& doc);
protected:
- virtual QString checkCanRunInternal() const;
+ QString checkCanRunInternal() const;
private:
diff --git a/src/xsession.cpp b/src/xsession.cpp
index 5db3b11..dcc9d22 100644
--- a/src/xsession.cpp
+++ b/src/xsession.cpp
@@ -128,7 +128,7 @@ QString XSession::checkCanRunInternal() const {
if (fi.isFile() && fi.isExecutable())
return QString();
// Not found
- return QObject::trUtf8("Binary %1 not found.").arg(exe);
+ return QObject::tr("Binary %1 not found.").arg(exe);
}
bool XSession::isLocked() const {
@@ -175,8 +175,9 @@ void XSession::run() const {
command += exec_;
qDebug() << "Running via /bin/sh:" << command;
QByteArray cmdbin = command.toUtf8();
- char *argv[4] = { "/bin/sh", "-c", cmdbin.data(), nullptr };
+ char * const argv[4] = { strdup("/bin/sh"), strdup("-c"), cmdbin.data(), nullptr };
execv("/bin/sh", argv);
+ // Caller will show error on failure to exec
}
int XSession::type() const {