diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/config.cpp | 2 | ||||
-rw-r--r-- | src/config.h | 2 | ||||
-rw-r--r-- | src/dialog.cpp | 163 | ||||
-rw-r--r-- | src/dialog.h | 4 | ||||
-rw-r--r-- | src/images.qrc | 3 | ||||
-rw-r--r-- | src/img/docker.png | bin | 0 -> 5165 bytes | |||
-rw-r--r-- | src/img/qemu.png | bin | 2854 -> 0 bytes | |||
-rw-r--r-- | src/img/qemukvm.png | bin | 0 -> 4059 bytes | |||
-rw-r--r-- | src/main.cpp | 2 | ||||
-rw-r--r-- | src/session.h | 12 | ||||
-rw-r--r-- | src/ui/dialog.ui | 37 | ||||
-rw-r--r-- | src/userldapdata.cpp | 17 | ||||
-rw-r--r-- | src/vsession.cpp | 30 | ||||
-rw-r--r-- | src/vsession.h | 14 | ||||
-rw-r--r-- | src/xsession.cpp | 5 |
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 Binary files differnew file mode 100644 index 0000000..0dc6379 --- /dev/null +++ b/src/img/docker.png diff --git a/src/img/qemu.png b/src/img/qemu.png Binary files differdeleted file mode 100644 index fadac31..0000000 --- a/src/img/qemu.png +++ /dev/null diff --git a/src/img/qemukvm.png b/src/img/qemukvm.png Binary files differnew file mode 100644 index 0000000..03d3489 --- /dev/null +++ b/src/img/qemukvm.png 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"><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } +hr { height: 1px; border-width: 0; } +li.unchecked::marker { content: "\2610"; } +li.checked::marker { content: "\2612"; } </style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans';">Loading...</span></p></body></html></string> </property> @@ -168,8 +171,11 @@ p, li { white-space: pre-wrap; } </property> <property name="html"> <string notr="true"><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } +hr { height: 1px; border-width: 0; } +li.unchecked::marker { content: "\2610"; } +li.checked::marker { content: "\2612"; } </style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans';">Loading...</span></p></body></html></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"><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } +hr { height: 1px; border-width: 0; } +li.unchecked::marker { content: "\2610"; } +li.checked::marker { content: "\2612"; } </style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans';">Click on an item on the left side for more information</span></p></body></html></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 { |