#include "config.h"
#include <QtXml>
#include <QDir>
#include <QApplication>
#include <QProcess>
#include <QDate>
#include <QThread>
#include <QStringList>
#include <QIcon>
#include <QMessageBox>
#include <QHostInfo> // available since Qt 4.7
#include <unistd.h> // for getuid()
#include <sys/types.h> // for getuid(), getpwuid()
#include <pwd.h> // for getpwuid()
#include "globals.h"
#include "vsession.h"
#include "sessionsiconholder.h"
#include "userldapdata.h"
static QProcess _process;
bool VSession::init(const QDomElement& xml) {
QDomElement settingsNode = this->doc_.createElement("settings");
this->doc_.appendChild(settingsNode);
eintrag_ = this->doc_.importNode(xml, true).toElement();
return !settingsNode.appendChild(eintrag_).isNull() && !settingsNode.isNull();
}
void VSession::addNodeWithAttribute(const QString& nodeName,
const QString& value,
const QString& attribute,
bool replace) {
QDomElement node =
eintrag_.firstChildElement(nodeName);
if (replace == false || node.isNull()) {
// create a new node
node = this->doc_.createElement(nodeName);
eintrag_.appendChild(node);
}
node.setAttribute(attribute, value);
}
QIcon VSession::icon() const {
QString icon(getAttribute("icon"));
SessionsIconHolder *iconHolder = SessionsIconHolder::get();
if (icon.startsWith("http://") || icon.startsWith("https://")) {
// try to load icon from url
QIcon url_icon(iconHolder->getIcon(QUrl(icon)));
if (!url_icon.isNull()) {
return url_icon;
}
}
if (!icon.isEmpty()) {
QIcon res_icon(iconHolder->getIcon(icon));
if (!res_icon.isNull()) {
return res_icon;
}
}
// Everything failed, try to guess the OS
QString os(getAttribute("os", "param").toLower());
if (!os.isEmpty()) {
QIcon osi = iconHolder->getIcon(os);
if (!osi.isNull())
return osi;
// These match vmware guestOS keywords mostly, extend for vbox...
if (os == "dos")
return iconHolder->getIcon("dos");
if (os.startsWith("windows7"))
return iconHolder->getIcon("win7");
if (os.startsWith("win31"))
return iconHolder->getIcon("win311");
if (os.startsWith("windows8"))
return iconHolder->getIcon("win8");
if (os.startsWith("win2000"))
return iconHolder->getIcon("win2000");
if (os.startsWith("winxp"))
return iconHolder->getIcon("winxp");
if (os.startsWith("debian"))
return iconHolder->getIcon("debian");
if (os.startsWith("ubuntu"))
return iconHolder->getIcon("ubuntu");
if (os.startsWith("win"))
return iconHolder->getIcon("windows");
if (os.contains("linux"))
return iconHolder->getIcon("linux");
}
// TODO: Maybe parse title of entry for an OS guess?
// Fallback to generic virtualizer icon
if (imgtype() == VMWARE)
return iconHolder->getIcon("vmware");
if (imgtype() == VBOX)
return iconHolder->getIcon("virtualbox");
return QIcon();
}
QString VSession::toXml() const {
QDomDocument doc(doc_);
QDomNode xmlNode = doc.createProcessingInstruction(
"xml", "version=\"1.0\" encoding=\"UTF-8\"");
doc.insertBefore(xmlNode, doc.firstChild());
QString image(this->getAttribute("image_name"));
QDomElement path = doc.firstChildElement("settings")
.firstChildElement("eintrag")
.appendChild(doc.createElement("image_path"))
.toElement();
if (QFileInfo(image).isRelative()) {
// make path to image absolute
path.setAttribute("param", Config::get(Config::BASEDIR) + "/" + image);
} else {
path.setAttribute("param", image);
}
return doc.toString();
}
QString VSession::getAttribute(const QString &nodeName,
const QString &attribute) const {
QDomNode n = eintrag_.firstChildElement(nodeName);
return eintrag_.firstChildElement(nodeName).attribute(attribute);
}
QList<QString> VSession::keywords() const {
return this->keywords_;
}
void VSession::readKeywords() {
QDomNode keywordsNode = eintrag_.namedItem("keywords");
for (QDomElement el(keywordsNode.firstChildElement("keyword"));
!el.isNull();
el = el.nextSiblingElement("keyword")) {
this->keywords_.append(el.text());
}
}
bool VSession::containsKeywords(const QList<QString>& keywords) const {
for (int j = 0; j < keywords.length(); ++j) {
if (!this->shortDescription().contains(keywords[j], Qt::CaseInsensitive)
&& !this->description().contains(keywords[j], Qt::CaseInsensitive)
&& !this->getAttribute("creator", "param").contains(keywords[j], Qt::CaseInsensitive)) {
bool match = false;
for (int i = 0; i < this->keywords().length(); ++i) {
if (this->keywords()[i].contains(keywords[j], Qt::CaseInsensitive)) {
match = true;
break;
}
}
if (!match)
return false;
}
}
return true;
}
QString VSession::getNodeText(const QString& nodeName) const {
return this->doc_.namedItem(nodeName).toText().data();
}
ImgType VSession::imgtype() const {
QString s(getAttribute("virtualmachine"));
if (s.compare("vmware") == 0) {
return VMWARE;
} else if (s.compare("virtualbox") == 0 || s.compare("vbox") == 0) {
return VBOX;
} else {
return OTHER;
}
}
bool VSession::isActive() const {
QString value(getAttribute("active"));
// Is disabled completely
if (value.compare("false") == 0) {
if (g_debugMode) qDebug() << "'" << shortDescription() << "' not active. Reason: active == false";
return false;
}
// Check for date range
if (value.count("/") == 1) {
// try to interpret value as date range
// [YYYY-MM-DD]/[YYYY-MM-DD]
// eg. "1970-01-01/1971-01-01" from Jan 1st 1970 till Jan 1st 1971
// "/1971-01-01" till Jan 1st 1971
// "1970-01-01/" from Jan 1st 1970
// "/" allways
// note: invalid dates are treated as empty dates
QStringList list(value.split("/"));
QString from(list.value(0));
QString till(list.value(1));
QDate fromDate(QDate::fromString(from, Qt::ISODate));
QDate tillDate(QDate::fromString(till, Qt::ISODate));
QDate today(QDate::currentDate());
if (fromDate.isValid() && fromDate > today) {
// fromDate is in the future
if (g_debugMode) qDebug() << "'" << shortDescription() << "' not active. Reason: fromDate is in the future";
return false;
}
if (tillDate.isValid() && tillDate < today) {
// tillDate is in the past
if (g_debugMode) qDebug() << "'" << shortDescription() << "' not active. Reason: tillDate is in the past";
return false;
}
}
// Filter by LDAP data
if (!UserLdapData::isEmpty()) {
QDomElement el(eintrag_.namedItem("filters").firstChildElement("filter"));
if (!el.isNull()) {
for (; !el.isNull(); el = el.nextSiblingElement("filter")) {
if (el.attribute("type") != "LDAP")
continue;
if (UserLdapData::isAllowed(el.firstChildElement("key").text(), el.firstChildElement("value").text()))
return true;
}
return false;
}
}
return true;
}
bool VSession::isLocked() const {
// default to false
return getAttribute("locked").compare("true") == 0;
}
bool VSession::isValid() const {
return !getAttribute("image_name").isEmpty();
}
int VSession::priority() const {
int prio = getAttribute("priority").toInt();
if (g_templateHandling == TEMPLATES_BUMP && isTemplate()) {
prio -= 500;
}
if (g_forLocationHandling != LOCATION_IGNORE && isForLocation()) {
prio -= 1000;
}
return prio;
}
bool VSession::run() const {
if (_process.state() != QProcess::NotRunning) {
qDebug() << "Cannot start vsession while old one is still running";
return false;
}
if (g_debugMode) {
qDebug() << "Sarting session " << this->getAttribute("short_description", "param") << " ...";
}
if (g_noVtx && is64Bit()) {
QMessageBox::warning(nullptr, QObject::trUtf8("Warning"),
QObject::trUtf8("The selected session is based on a 64 bit operating system,"
" but this computer doesn't seem to support this (VT-x/AMD-V not"
" supported by CPU, or disabled in BIOS). You will probably get an"
" error message while the virtualizer is initializing."));
}
QString command = getAttribute("command");
if (!command.isEmpty()) {
return QProcess::startDetached(command);
}
// write xml to temporary file
QTemporaryFile tmpfile(QDir::tempPath() + "/vmchooser-XXXXXX.xml");
if (!tmpfile.open() ||
tmpfile.write(this->toXml().toUtf8()) == -1) {
qDebug() << "Error writing xml to file" << tmpfile.fileName();
return false;
}
// Docs say we should call fileName() before closing to prevent deletion
QString tmpFileName = tmpfile.fileName();
tmpfile.setAutoRemove(false);
tmpfile.close();
QObject::connect(&_process, SIGNAL(finished(int, QProcess::ExitStatus)), QApplication::instance(), SLOT(quit()));
_process.start(Config::get(Config::RUNSCRIPT), QStringList(tmpFileName));
_process.waitForStarted(10);
if (_process.state() == QProcess::Starting || _process.state() == QProcess::Running)
return true;
else
return false;
}
int VSession::type() const {
return Session::VSESSION;
}
QList<Session*> VSession::readXmlFile(const QString& filepath) {
QList<Session*> sessionList;
QDomDocument doc;
QFile file(filepath);
QFile backup_file(TEMP_PATH_XML_LIST);
if (!file.open(QIODevice::ReadOnly)) {
if (g_debugMode) {
qDebug() << "Cannot read file: " << file.fileName();
}
return sessionList;
}
if (!doc.setContent(&file)) {
if (g_debugMode) {
qDebug() << "XML file not valid: " << file.fileName();
}
file.close();
// try to use backup file
if (!backup_file.open(QIODevice::ReadOnly)) {
if (g_debugMode) {
qDebug() << "Cannot read backup file " << TEMP_PATH_XML_LIST << " either";
}
return sessionList;
}
if (!doc.setContent(&backup_file)) {
if (g_debugMode) {
qDebug() << "XML file not valid: " << backup_file.fileName();
}
backup_file.close();
return sessionList;
}
if (g_debugMode) {
qDebug() << "Used backup file " << TEMP_PATH_XML_LIST;
}
backup_file.close();
} else {
file.close();
// file is valid --> create backup file
QFile::remove(TEMP_PATH_XML_LIST);
QFile::rename(filepath, TEMP_PATH_XML_LIST);
if (!QFile::setPermissions(TEMP_PATH_XML_LIST, QFile::ReadUser | QFile::ReadGroup | QFile::ReadOther | QFile::WriteUser | QFile::WriteGroup | QFile::WriteOther)) {
if (g_debugMode) {
qDebug() << "Could not change permissions of file: " << TEMP_PATH_NEWS;
}
}
}
UserLdapData::init();
QDomElement settingsNode = doc.firstChildElement("settings");
for (QDomElement el(settingsNode.firstChildElement("eintrag"));
!el.isNull();
el = el.nextSiblingElement("eintrag")) {
VSession* e = new VSession;
if (e->init(el) && e->isActive()) {
e->readKeywords();
sessionList.append(e);
} else {
delete e;
}
}
return sessionList;
}
bool VSession::is64Bit() const {
ImgType type = imgtype();
return (type == VMWARE && getAttribute("os").endsWith("-64"))
|| (type == VBOX && getAttribute("os").endsWith("_64"));
// TODO: qemu-kvm, ...
}
QVariant VSession::foregroundRole() const {
if (!g_noVtx || !is64Bit())
return Session::foregroundRole();
return QColor(180, 180, 180);
}
bool VSession::operator<(const Session& other) const {
int p0 = this->priority();
int p1 = other.priority();
if (p0 < p1) return true;
if (p0 == p1) {
QString d0 = this->shortDescription();
QString d1 = other.shortDescription();
return d0.localeAwareCompare(d1) < 0;
}
return false;
}