summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/src-server/UnattendedScript.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 14:19:18 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 14:19:18 +0000
commit4035b1bfb1e5843a539a8b624d21952b756974d1 (patch)
treef1e9cd5bf548cbc57ff2fddfb2b4aa9ae95587e2 /src/VBox/Main/src-server/UnattendedScript.cpp
parentInitial commit. (diff)
downloadvirtualbox-4035b1bfb1e5843a539a8b624d21952b756974d1.tar.xz
virtualbox-4035b1bfb1e5843a539a8b624d21952b756974d1.zip
Adding upstream version 6.1.22-dfsg.upstream/6.1.22-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Main/src-server/UnattendedScript.cpp')
-rw-r--r--src/VBox/Main/src-server/UnattendedScript.cpp679
1 files changed, 679 insertions, 0 deletions
diff --git a/src/VBox/Main/src-server/UnattendedScript.cpp b/src/VBox/Main/src-server/UnattendedScript.cpp
new file mode 100644
index 00000000..eea89be8
--- /dev/null
+++ b/src/VBox/Main/src-server/UnattendedScript.cpp
@@ -0,0 +1,679 @@
+/* $Id: UnattendedScript.cpp $ */
+/** @file
+ * Classes for reading/parsing/saving scripts for unattended installation.
+ */
+
+/*
+ * Copyright (C) 2006-2020 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_MAIN_UNATTENDED
+#include "LoggingNew.h"
+#include "VirtualBoxBase.h"
+#include "AutoCaller.h"
+#include <VBox/com/ErrorInfo.h>
+
+#include "UnattendedScript.h"
+#include "UnattendedImpl.h"
+
+#include <iprt/errcore.h>
+
+#include <iprt/ctype.h>
+#include <iprt/file.h>
+#include <iprt/vfs.h>
+#include <iprt/getopt.h>
+#include <iprt/path.h>
+
+using namespace std;
+
+#ifdef VBOX_WITH_UNATTENDED
+
+
+/*********************************************************************************************************************************
+* UnattendedScriptTemplate Implementation *
+*********************************************************************************************************************************/
+
+UnattendedScriptTemplate::UnattendedScriptTemplate(Unattended *pUnattended, const char *pszDefaultTemplateFilename,
+ const char *pszDefaultFilename)
+ : BaseTextScript(pUnattended, pszDefaultTemplateFilename, pszDefaultFilename), mpUnattended(pUnattended)
+{
+}
+
+
+HRESULT UnattendedScriptTemplate::saveToString(Utf8Str &rStrDst)
+{
+ static const char s_szPrefix[] = "@@VBOX_";
+ static const char s_szPrefixInsert[] = "@@VBOX_INSERT_";
+ static const char s_szPrefixCond[] = "@@VBOX_COND_";
+ static const char s_szPrefixCondEnd[] = "@@VBOX_COND_END@@";
+
+ struct
+ {
+ bool fSavedOutputting;
+ } aConds[8];
+ unsigned cConds = 0;
+ bool fOutputting = true;
+ HRESULT hrc = E_FAIL;
+ size_t offTemplate = 0;
+ size_t cchTemplate = mStrScriptFullContent.length();
+ rStrDst.setNull();
+ for (;;)
+ {
+ /*
+ * Find the next placeholder and add any text before it to the output.
+ */
+ size_t offPlaceholder = mStrScriptFullContent.find(s_szPrefix, offTemplate);
+ size_t cchToCopy = offPlaceholder != RTCString::npos ? offPlaceholder - offTemplate : cchTemplate - offTemplate;
+ if (cchToCopy > 0)
+ {
+ if (fOutputting)
+ {
+ try
+ {
+ rStrDst.append(mStrScriptFullContent, offTemplate, cchToCopy);
+ }
+ catch (std::bad_alloc &)
+ {
+ hrc = E_OUTOFMEMORY;
+ break;
+ }
+ }
+ offTemplate += cchToCopy;
+ }
+
+ /*
+ * Process placeholder.
+ */
+ if (offPlaceholder != RTCString::npos)
+ {
+ /*
+ * First we must find the end of the placeholder string.
+ */
+ const char *pszPlaceholder = mStrScriptFullContent.c_str() + offPlaceholder;
+ size_t cchPlaceholder = sizeof(s_szPrefix) - 1;
+ char ch;
+ while ( offPlaceholder + cchPlaceholder < cchTemplate
+ && (ch = pszPlaceholder[cchPlaceholder]) != '\0'
+ && ( ch == '_'
+ || ch == '['
+ || ch == ']'
+ || ch == '.'
+ || ch == '>'
+ || ch == '<'
+ || RT_C_IS_UPPER(ch)
+ || RT_C_IS_DIGIT(ch)) )
+ cchPlaceholder++;
+
+ if ( offPlaceholder + cchPlaceholder < cchTemplate
+ && pszPlaceholder[cchPlaceholder] == '@')
+ {
+ cchPlaceholder++;
+ if ( offPlaceholder + cchPlaceholder < cchTemplate
+ && pszPlaceholder[cchPlaceholder] == '@')
+ cchPlaceholder++;
+ }
+
+ if ( pszPlaceholder[cchPlaceholder - 1] != '@'
+ || pszPlaceholder[cchPlaceholder - 2] != '@'
+ || ( strncmp(pszPlaceholder, s_szPrefixInsert, sizeof(s_szPrefixInsert) - 1) != 0
+ && strncmp(pszPlaceholder, s_szPrefixCond, sizeof(s_szPrefixCond) - 1) != 0) )
+ {
+ hrc = mpSetError->setError(E_FAIL, mpSetError->tr("Malformed template placeholder '%.*s'"),
+ cchPlaceholder, pszPlaceholder);
+ break;
+ }
+
+ offTemplate += cchPlaceholder;
+
+ /*
+ * @@VBOX_INSERT_XXX@@:
+ */
+ if (strncmp(pszPlaceholder, s_szPrefixInsert, sizeof(s_szPrefixInsert) - 1) == 0)
+ {
+ /*
+ * Get the placeholder value and add it to the output.
+ */
+ RTCString strValue;
+ hrc = getReplacement(pszPlaceholder, cchPlaceholder, fOutputting, strValue);
+ if (SUCCEEDED(hrc))
+ {
+ if (fOutputting)
+ {
+ try
+ {
+ rStrDst.append(strValue);
+ }
+ catch (std::bad_alloc &)
+ {
+ hrc = E_OUTOFMEMORY;
+ break;
+ }
+ }
+ }
+ else
+ break;
+ }
+ /*
+ * @@VBOX_COND_END@@: Pop one item of the conditional stack.
+ */
+ else if ( cchPlaceholder == sizeof(s_szPrefixCondEnd) - 1U
+ && strncmp(pszPlaceholder, s_szPrefixCondEnd, sizeof(s_szPrefixCondEnd) - 1U) == 0)
+ {
+ if (cConds > 0)
+ {
+ cConds--;
+ fOutputting = aConds[cConds].fSavedOutputting;
+ }
+ else
+ {
+ hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
+ mpSetError->tr("%s without @@VBOX_COND_XXX@@ at offset %zu (%#zx)"),
+ s_szPrefixCondEnd, offPlaceholder, offPlaceholder);
+ break;
+ }
+ }
+ /*
+ * @@VBOX_COND_XXX@@: Push the previous outputting state and combine it with the
+ * one from the condition.
+ */
+ else
+ {
+ Assert(strncmp(pszPlaceholder, s_szPrefixCond, sizeof(s_szPrefixCond) - 1) == 0);
+ if (cConds + 1 < RT_ELEMENTS(aConds))
+ {
+ aConds[cConds].fSavedOutputting = fOutputting;
+ bool fNewOutputting = fOutputting;
+ hrc = getConditional(pszPlaceholder, cchPlaceholder, &fNewOutputting);
+ if (SUCCEEDED(hrc))
+ fOutputting = fOutputting && fNewOutputting;
+ else
+ break;
+ cConds++;
+ }
+ else
+ {
+ hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
+ mpSetError->tr("Too deep conditional nesting at offset %zu (%#zx)"),
+ offPlaceholder, offPlaceholder);
+ break;
+ }
+ }
+ }
+
+ /*
+ * Done?
+ */
+ if (offTemplate >= cchTemplate)
+ {
+ if (cConds == 0)
+ return S_OK;
+ if (cConds == 1)
+ hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, mpSetError->tr("Missing @@VBOX_COND_END@@"));
+ else
+ hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, mpSetError->tr("Missing %u @@VBOX_COND_END@@"), cConds);
+ break;
+ }
+ }
+
+ /* failed */
+ rStrDst.setNull();
+ return hrc;
+}
+
+HRESULT UnattendedScriptTemplate::getReplacement(const char *pachPlaceholder, size_t cchPlaceholder,
+ bool fOutputting, RTCString &rValue)
+{
+ /*
+ * Check for an escaping suffix. Drop the '@@'.
+ */
+ size_t const cchFullPlaceholder = cchPlaceholder;
+ enum
+ {
+ kValueEscaping_None,
+ kValueEscaping_Bourne,
+ kValueEscaping_XML_Element,
+ kValueEscaping_XML_Attribute_Double_Quotes
+ } enmEscaping;
+
+#define PLACEHOLDER_ENDS_WITH(a_szSuffix) \
+ ( cchPlaceholder > sizeof(a_szSuffix) - 1U \
+ && memcmp(&pachPlaceholder[cchPlaceholder - sizeof(a_szSuffix) + 1U], a_szSuffix, sizeof(a_szSuffix) - 1U) == 0)
+ if (PLACEHOLDER_ENDS_WITH("_SH@@"))
+ {
+ cchPlaceholder -= 3 + 2;
+ enmEscaping = kValueEscaping_Bourne;
+ }
+ else if (PLACEHOLDER_ENDS_WITH("_ELEMENT@@"))
+ {
+ cchPlaceholder -= 8 + 2;
+ enmEscaping = kValueEscaping_XML_Element;
+ }
+ else if (PLACEHOLDER_ENDS_WITH("_ATTRIB_DQ@@"))
+ {
+ cchPlaceholder -= 10 + 2;
+ enmEscaping = kValueEscaping_XML_Attribute_Double_Quotes;
+ }
+ else
+ {
+ Assert(PLACEHOLDER_ENDS_WITH("@@"));
+ cchPlaceholder -= 2;
+ enmEscaping = kValueEscaping_None;
+ }
+
+ /*
+ * Resolve and escape the value.
+ */
+ HRESULT hrc;
+ try
+ {
+ switch (enmEscaping)
+ {
+ case kValueEscaping_None:
+ hrc = getUnescapedReplacement(pachPlaceholder, cchPlaceholder, cchFullPlaceholder, fOutputting, rValue);
+ if (SUCCEEDED(hrc))
+ return hrc;
+ break;
+
+ case kValueEscaping_Bourne:
+ case kValueEscaping_XML_Element:
+ case kValueEscaping_XML_Attribute_Double_Quotes:
+ {
+ RTCString strUnescaped;
+ hrc = getUnescapedReplacement(pachPlaceholder, cchPlaceholder, cchFullPlaceholder, fOutputting, strUnescaped);
+ if (SUCCEEDED(hrc))
+ {
+ switch (enmEscaping)
+ {
+ case kValueEscaping_Bourne:
+ {
+ const char * const papszArgs[2] = { strUnescaped.c_str(), NULL };
+ char *pszEscaped = NULL;
+ int vrc = RTGetOptArgvToString(&pszEscaped, papszArgs, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
+ if (RT_SUCCESS(vrc))
+ {
+ try
+ {
+ rValue = pszEscaped;
+ RTStrFree(pszEscaped);
+ return S_OK;
+ }
+ catch (std::bad_alloc &)
+ {
+ hrc = E_OUTOFMEMORY;
+ }
+ RTStrFree(pszEscaped);
+ }
+ break;
+ }
+
+ case kValueEscaping_XML_Element:
+ rValue.printf("%RMes", strUnescaped.c_str());
+ return S_OK;
+
+ case kValueEscaping_XML_Attribute_Double_Quotes:
+ {
+ RTCString strTmp;
+ strTmp.printf("%RMas", strUnescaped.c_str());
+ rValue = RTCString(strTmp, 1, strTmp.length() - 2);
+ return S_OK;
+ }
+
+ default:
+ hrc = E_FAIL;
+ break;
+ }
+ }
+ break;
+ }
+
+ default:
+ AssertFailedStmt(hrc = E_FAIL);
+ break;
+ }
+ }
+ catch (std::bad_alloc &)
+ {
+ hrc = E_OUTOFMEMORY;
+ }
+ rValue.setNull();
+ return hrc;
+}
+
+HRESULT UnattendedScriptTemplate::getUnescapedReplacement(const char *pachPlaceholder, size_t cchPlaceholder,
+ size_t cchFullPlaceholder, bool fOutputting, RTCString &rValue)
+{
+ RT_NOREF(fOutputting);
+#define IS_PLACEHOLDER_MATCH(a_szMatch) \
+ ( cchPlaceholder == sizeof("@@VBOX_INSERT_" a_szMatch) - 1U \
+ && memcmp(pachPlaceholder, "@@VBOX_INSERT_" a_szMatch, sizeof("@@VBOX_INSERT_" a_szMatch) - 1U) == 0)
+
+ if (IS_PLACEHOLDER_MATCH("USER_LOGIN"))
+ rValue = mpUnattended->i_getUser();
+ else if (IS_PLACEHOLDER_MATCH("USER_PASSWORD"))
+ rValue = mpUnattended->i_getPassword();
+ else if (IS_PLACEHOLDER_MATCH("ROOT_PASSWORD"))
+ rValue = mpUnattended->i_getPassword();
+ else if (IS_PLACEHOLDER_MATCH("USER_FULL_NAME"))
+ rValue = mpUnattended->i_getFullUserName();
+ else if (IS_PLACEHOLDER_MATCH("PRODUCT_KEY"))
+ rValue = mpUnattended->i_getProductKey();
+ else if (IS_PLACEHOLDER_MATCH("POST_INSTALL_COMMAND"))
+ rValue = mpUnattended->i_getPostInstallCommand();
+ else if (IS_PLACEHOLDER_MATCH("IMAGE_INDEX"))
+ rValue.printf("%u", mpUnattended->i_getImageIndex());
+ else if (IS_PLACEHOLDER_MATCH("OS_ARCH"))
+ rValue = mpUnattended->i_isGuestOs64Bit() ? "amd64" : "x86";
+ else if (IS_PLACEHOLDER_MATCH("OS_ARCH2"))
+ rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "x86";
+ else if (IS_PLACEHOLDER_MATCH("OS_ARCH3"))
+ rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i386";
+ else if (IS_PLACEHOLDER_MATCH("OS_ARCH4"))
+ rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i486";
+ else if (IS_PLACEHOLDER_MATCH("OS_ARCH6"))
+ rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i686";
+ else if (IS_PLACEHOLDER_MATCH("GUEST_OS_VERSION"))
+ rValue = mpUnattended->i_getDetectedOSVersion();
+ else if (IS_PLACEHOLDER_MATCH("GUEST_OS_MAJOR_VERSION"))
+ {
+ Utf8Str strOsVer(mpUnattended->i_getDetectedOSVersion());
+ RTCList<RTCString> partList = strOsVer.split(".");
+ if (partList.size() < 1)
+ {
+ rValue.setNull();
+ return mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, mpSetError->tr("Unknown guest OS major version '%s'"),
+ partList.at(0).c_str());
+ }
+ rValue = partList.at(0);
+ }
+ else if (IS_PLACEHOLDER_MATCH("TIME_ZONE_UX"))
+ rValue = mpUnattended->i_getTimeZoneInfo()
+ ? mpUnattended->i_getTimeZoneInfo()->pszUnixName : mpUnattended->i_getTimeZone();
+ else if (IS_PLACEHOLDER_MATCH("TIME_ZONE_WIN_NAME"))
+ {
+ PCRTTIMEZONEINFO pInfo = mpUnattended->i_getTimeZoneInfo();
+ if (pInfo)
+ rValue = pInfo->pszWindowsName ? pInfo->pszWindowsName : "GMT";
+ else
+ rValue = mpUnattended->i_getTimeZone();
+ }
+ else if (IS_PLACEHOLDER_MATCH("TIME_ZONE_WIN_INDEX"))
+ {
+ PCRTTIMEZONEINFO pInfo = mpUnattended->i_getTimeZoneInfo();
+ if (pInfo)
+ rValue.printf("%u", pInfo->idxWindows ? pInfo->idxWindows : 85 /*GMT*/);
+ else
+ rValue = mpUnattended->i_getTimeZone();
+ }
+ else if (IS_PLACEHOLDER_MATCH("LOCALE"))
+ rValue = mpUnattended->i_getLocale();
+ else if (IS_PLACEHOLDER_MATCH("DASH_LOCALE"))
+ {
+ rValue = mpUnattended->i_getLocale();
+ Assert(rValue[2] == '_');
+ rValue.replace(2, 1, "-");
+ }
+ else if (IS_PLACEHOLDER_MATCH("LANGUAGE"))
+ rValue = mpUnattended->i_getLanguage();
+ else if (IS_PLACEHOLDER_MATCH("COUNTRY"))
+ rValue = mpUnattended->i_getCountry();
+ else if (IS_PLACEHOLDER_MATCH("HOSTNAME_FQDN"))
+ rValue = mpUnattended->i_getHostname();
+ else if (IS_PLACEHOLDER_MATCH("HOSTNAME_WITHOUT_DOMAIN"))
+ rValue.assign(mpUnattended->i_getHostname(), 0, mpUnattended->i_getHostname().find("."));
+ else if (IS_PLACEHOLDER_MATCH("HOSTNAME_WITHOUT_DOMAIN_MAX_15"))
+ rValue.assign(mpUnattended->i_getHostname(), 0, RT_MIN(mpUnattended->i_getHostname().find("."), 15));
+ else if (IS_PLACEHOLDER_MATCH("HOSTNAME_DOMAIN"))
+ rValue.assign(mpUnattended->i_getHostname(), mpUnattended->i_getHostname().find(".") + 1);
+ else if (IS_PLACEHOLDER_MATCH("PROXY"))
+ rValue = mpUnattended->i_getProxy();
+ else
+ {
+ rValue.setNull();
+ return mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, mpSetError->tr("Unknown template placeholder '%.*s'"),
+ cchFullPlaceholder, pachPlaceholder);
+ }
+ return S_OK;
+#undef IS_PLACEHOLDER_MATCH
+}
+
+HRESULT UnattendedScriptTemplate::getConditional(const char *pachPlaceholder, size_t cchPlaceholder, bool *pfOutputting)
+{
+#define IS_PLACEHOLDER_MATCH(a_szMatch) \
+ ( cchPlaceholder == sizeof("@@VBOX_COND_" a_szMatch "@@") - 1U \
+ && memcmp(pachPlaceholder, "@@VBOX_COND_" a_szMatch "@@", sizeof("@@VBOX_COND_" a_szMatch "@@") - 1U) == 0)
+#define IS_PLACEHOLDER_PARTIALLY_MATCH(a_szMatch) \
+ (memcmp(pachPlaceholder, "@@VBOX_COND_" a_szMatch, sizeof("@@VBOX_COND_" a_szMatch) - 1U) == 0)
+
+ /* Install guest additions: */
+ if (IS_PLACEHOLDER_MATCH("IS_INSTALLING_ADDITIONS"))
+ *pfOutputting = mpUnattended->i_getInstallGuestAdditions();
+ else if (IS_PLACEHOLDER_MATCH("IS_NOT_INSTALLING_ADDITIONS"))
+ *pfOutputting = !mpUnattended->i_getInstallGuestAdditions();
+ /* User == Administrator: */
+ else if (IS_PLACEHOLDER_MATCH("IS_USER_LOGIN_ADMINISTRATOR"))
+ *pfOutputting = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) == 0;
+ else if (IS_PLACEHOLDER_MATCH("IS_USER_LOGIN_NOT_ADMINISTRATOR"))
+ *pfOutputting = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) != 0;
+ /* Install TXS: */
+ else if (IS_PLACEHOLDER_MATCH("IS_INSTALLING_TEST_EXEC_SERVICE"))
+ *pfOutputting = mpUnattended->i_getInstallTestExecService();
+ else if (IS_PLACEHOLDER_MATCH("IS_NOT_INSTALLING_TEST_EXEC_SERVICE"))
+ *pfOutputting = !mpUnattended->i_getInstallTestExecService();
+ /* Post install command: */
+ else if (IS_PLACEHOLDER_MATCH("HAS_POST_INSTALL_COMMAND"))
+ *pfOutputting = mpUnattended->i_getPostInstallCommand().isNotEmpty();
+ else if (IS_PLACEHOLDER_MATCH("HAS_NO_POST_INSTALL_COMMAND"))
+ *pfOutputting = !mpUnattended->i_getPostInstallCommand().isNotEmpty();
+ /* Product key: */
+ else if (IS_PLACEHOLDER_MATCH("HAS_PRODUCT_KEY"))
+ *pfOutputting = mpUnattended->i_getProductKey().isNotEmpty();
+ else if (IS_PLACEHOLDER_MATCH("HAS_NO_PRODUCT_KEY"))
+ *pfOutputting = !mpUnattended->i_getProductKey().isNotEmpty();
+ /* Minimal installation: */
+ else if (IS_PLACEHOLDER_MATCH("IS_MINIMAL_INSTALLATION"))
+ *pfOutputting = mpUnattended->i_isMinimalInstallation();
+ else if (IS_PLACEHOLDER_MATCH("IS_NOT_MINIMAL_INSTALLATION"))
+ *pfOutputting = !mpUnattended->i_isMinimalInstallation();
+ /* Is RTC using UTC (i.e. set to UTC time on startup): */
+ else if (IS_PLACEHOLDER_MATCH("IS_RTC_USING_UTC"))
+ *pfOutputting = mpUnattended->i_isRtcUsingUtc();
+ else if (IS_PLACEHOLDER_MATCH("IS_NOT_RTC_USING_UTC"))
+ *pfOutputting = !mpUnattended->i_isRtcUsingUtc();
+ else if (IS_PLACEHOLDER_MATCH("HAS_PROXY"))
+ *pfOutputting = mpUnattended->i_getProxy().isNotEmpty();
+ else if (IS_PLACEHOLDER_PARTIALLY_MATCH("GUEST_VERSION"))
+ {
+ //parse the placeholder and extract the OS version from there
+ RTCString strPlaceHolder(pachPlaceholder);
+ size_t startPos = sizeof("@@VBOX_COND_GUEST_VERSION") - 1;//-1 is for '\n'
+ size_t endPos = strPlaceHolder.find("@@", startPos + 2);
+ //next part should look like [>8.0.0] for example where:
+ // - "[,]" is just the brackets to wrap up the condition;
+ // - ">" is "greater". Also possible comparison is "<";
+ // - 8.0.0 is required guest OS version.
+ //The end of placeholder is "@@" like for others.
+
+ if ( strPlaceHolder[endPos] == '@'
+ && strPlaceHolder[endPos+1] == '@' )
+ {
+ if ( strPlaceHolder[startPos++] == '[' && strPlaceHolder[--endPos] == ']' )
+ {
+ char chComp = strPlaceHolder[startPos++];
+ RTCString strRequiredOSVersion = strPlaceHolder.substr(startPos, endPos - startPos);
+ RTCString strDetectedOSVersion = mpUnattended->i_getDetectedOSVersion();
+ int res = RTStrVersionCompare(strDetectedOSVersion.c_str(), strRequiredOSVersion.c_str());
+ if ( res >= 0 && chComp == '>' )
+ *pfOutputting = true;
+ else if ( res < 0 && chComp == '<' )
+ *pfOutputting = true;
+ else
+ *pfOutputting = false;
+ }
+ }
+ else
+ *pfOutputting = false;//initially is set to "false"
+ }
+ else
+ return mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, mpSetError->tr("Unknown conditional placeholder '%.*s'"),
+ cchPlaceholder, pachPlaceholder);
+ return S_OK;
+#undef IS_PLACEHOLDER_MATCH
+}
+
+#endif /* VBOX_WITH_UNATTENDED */
+#if 0 /* Keeping this a reference */
+
+
+/*********************************************************************************************************************************
+* UnattendedSUSEXMLScript Implementation *
+*********************************************************************************************************************************/
+
+HRESULT UnattendedSUSEXMLScript::parse()
+{
+ HRESULT hrc = UnattendedXMLScript::parse();
+ if (SUCCEEDED(hrc))
+ {
+ /*
+ * Check that we've got the right root element type.
+ */
+ const xml::ElementNode *pelmRoot = mDoc.getRootElement();
+ if ( pelmRoot
+ && strcmp(pelmRoot->getName(), "profile") == 0)
+ {
+ /*
+ * Work thought the sections.
+ */
+ try
+ {
+ LoopThruSections(pelmRoot);
+ hrc = S_OK;
+ }
+ catch (std::bad_alloc &)
+ {
+ hrc = E_OUTOFMEMORY;
+ }
+ }
+ else if (pelmRoot)
+ hrc = mpSetError->setError(E_FAIL, mpSetError->tr("XML document root element is '%s' instead of 'profile'"),
+ pelmRoot->getName());
+ else
+ hrc = mpSetError->setError(E_FAIL, mpSetError->tr("Missing XML root element"));
+ }
+ return hrc;
+}
+
+HRESULT UnattendedSUSEXMLScript::setFieldInElement(xml::ElementNode *pElement, const DataId enmDataId, const Utf8Str &rStrValue)
+{
+ /*
+ * Don't set empty values.
+ */
+ if (rStrValue.isEmpty())
+ {
+ Utf8Str strProbableValue;
+ try
+ {
+ strProbableValue = createProbableValue(enmDataId, pElement);
+ }
+ catch (std::bad_alloc &)
+ {
+ return E_OUTOFMEMORY;
+ }
+ return UnattendedXMLScript::setFieldInElement(pElement, enmDataId, strProbableValue);
+ }
+ return UnattendedXMLScript::setFieldInElement(pElement, enmDataId, rStrValue);
+}
+
+HRESULT UnattendedSUSEXMLScript::LoopThruSections(const xml::ElementNode *pelmRoot)
+{
+ xml::NodesLoop loopChildren(*pelmRoot);
+ const xml::ElementNode *pelmOuterLoop;
+ while ((pelmOuterLoop = loopChildren.forAllNodes()) != NULL)
+ {
+ const char *pcszElemName = pelmOuterLoop->getName();
+ if (!strcmp(pcszElemName, "users"))
+ {
+ xml::NodesLoop loopUsers(*pelmOuterLoop);
+ const xml::ElementNode *pelmUser;
+ while ((pelmUser = loopUsers.forAllNodes()) != NULL)
+ {
+ HRESULT hrc = HandleUserAccountsSection(pelmUser);
+ if (FAILED(hrc))
+ return hrc;
+ }
+ }
+ }
+ return S_OK;
+}
+
+HRESULT UnattendedSUSEXMLScript::HandleUserAccountsSection(const xml::ElementNode *pelmSection)
+{
+ xml::NodesLoop loopUser(*pelmSection);
+
+ const xml::ElementNode *pelmCur;
+ while ((pelmCur = loopUser.forAllNodes()) != NULL)
+ {
+ const char *pszValue = pelmCur->getValue();
+#ifdef LOG_ENABLED
+ if (!RTStrCmp(pelmCur->getName(), "uid"))
+ LogRelFunc(("UnattendedSUSEXMLScript::HandleUserAccountsSection profile/users/%s/%s = %s\n",
+ pelmSection->getName(), pelmCur->getName(), pszValue));
+#endif
+
+ if (!RTStrCmp(pszValue, "$homedir"))
+ mNodesForCorrectionMap.insert(make_pair(USERHOMEDIR_ID, pelmCur));
+
+ if (!RTStrCmp(pszValue, "$user"))
+ mNodesForCorrectionMap.insert(make_pair(USERNAME_ID, pelmCur));
+
+ if (!RTStrCmp(pszValue, "$password"))
+ mNodesForCorrectionMap.insert(make_pair(USERPASSWORD_ID, pelmCur));
+ }
+ return S_OK;
+}
+
+Utf8Str UnattendedSUSEXMLScript::createProbableValue(const DataId enmDataId, const xml::ElementNode *pCurElem)
+{
+ const xml::ElementNode *pElem = pCurElem;
+
+ switch (enmDataId)
+ {
+ case USERHOMEDIR_ID:
+// if ((pElem = pElem->findChildElement("home")))
+// {
+ return createProbableUserHomeDir(pElem);
+// }
+ break;
+ default:
+ break;
+ }
+
+ return Utf8Str::Empty;
+}
+
+Utf8Str UnattendedSUSEXMLScript::createProbableUserHomeDir(const xml::ElementNode *pCurElem)
+{
+ Utf8Str strCalcValue;
+ const xml::ElementNode *pElem = pCurElem->findNextSibilingElement("username");
+ if (pElem)
+ {
+ const char *pszValue = pElem->getValue();
+ strCalcValue = "/home/";
+ strCalcValue.append(pszValue);
+ }
+
+ return strCalcValue;
+}
+#endif /* just for reference */
+