[68162] | 1 | /* $Id: UnattendedScript.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
[76167] | 3 | * Classes for reading/parsing/saving scripts for unattended installation.
|
---|
[68162] | 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[98103] | 7 | * Copyright (C) 2006-2023 Oracle and/or its affiliates.
|
---|
[68162] | 8 | *
|
---|
[96407] | 9 | * This file is part of VirtualBox base platform packages, as
|
---|
| 10 | * available from https://www.virtualbox.org.
|
---|
| 11 | *
|
---|
| 12 | * This program is free software; you can redistribute it and/or
|
---|
| 13 | * modify it under the terms of the GNU General Public License
|
---|
| 14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
| 15 | * License.
|
---|
| 16 | *
|
---|
| 17 | * This program is distributed in the hope that it will be useful, but
|
---|
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
| 20 | * General Public License for more details.
|
---|
| 21 | *
|
---|
| 22 | * You should have received a copy of the GNU General Public License
|
---|
| 23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
| 24 | *
|
---|
| 25 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
[68162] | 26 | */
|
---|
| 27 |
|
---|
[69238] | 28 |
|
---|
[68162] | 29 | /*********************************************************************************************************************************
|
---|
| 30 | * Header Files *
|
---|
| 31 | *********************************************************************************************************************************/
|
---|
| 32 | #define LOG_GROUP LOG_GROUP_MAIN_UNATTENDED
|
---|
| 33 | #include "LoggingNew.h"
|
---|
| 34 | #include "VirtualBoxBase.h"
|
---|
| 35 | #include "AutoCaller.h"
|
---|
| 36 | #include <VBox/com/ErrorInfo.h>
|
---|
| 37 |
|
---|
| 38 | #include "UnattendedScript.h"
|
---|
| 39 | #include "UnattendedImpl.h"
|
---|
| 40 |
|
---|
[93190] | 41 | #include <iprt/err.h>
|
---|
[68162] | 42 |
|
---|
| 43 | #include <iprt/ctype.h>
|
---|
| 44 | #include <iprt/file.h>
|
---|
[73826] | 45 | #include <iprt/vfs.h>
|
---|
[68162] | 46 | #include <iprt/getopt.h>
|
---|
| 47 | #include <iprt/path.h>
|
---|
| 48 |
|
---|
| 49 | using namespace std;
|
---|
| 50 |
|
---|
[73716] | 51 | #ifdef VBOX_WITH_UNATTENDED
|
---|
| 52 |
|
---|
[76168] | 53 |
|
---|
[76167] | 54 | /*********************************************************************************************************************************
|
---|
[93190] | 55 | * Defined Constants And Macros *
|
---|
| 56 | *********************************************************************************************************************************/
|
---|
| 57 | static const char g_szPrefix[] = "@@VBOX_";
|
---|
| 58 | static const char g_szPrefixInsert[] = "@@VBOX_INSERT";
|
---|
| 59 | static const char g_szPrefixInsertXxx[] = "@@VBOX_INSERT_";
|
---|
| 60 | static const char g_szPrefixInsertExpr[] = "@@VBOX_INSERT[";
|
---|
| 61 | static const char g_szPrefixCond[] = "@@VBOX_COND";
|
---|
| 62 | static const char g_szPrefixCondXxx[] = "@@VBOX_COND_";
|
---|
| 63 | static const char g_szPrefixCondExpr[] = "@@VBOX_COND[";
|
---|
| 64 | static const char g_szPrefixCondElse[] = "@@VBOX_COND_ELSE@@";
|
---|
| 65 | static const char g_szPrefixCondEnd[] = "@@VBOX_COND_END@@";
|
---|
| 66 | static const char g_szPrefixSplitter[] = "@@VBOX_SPLITTER";
|
---|
| 67 |
|
---|
| 68 |
|
---|
| 69 | /*********************************************************************************************************************************
|
---|
[76167] | 70 | * UnattendedScriptTemplate Implementation *
|
---|
| 71 | *********************************************************************************************************************************/
|
---|
[68162] | 72 |
|
---|
| 73 | UnattendedScriptTemplate::UnattendedScriptTemplate(Unattended *pUnattended, const char *pszDefaultTemplateFilename,
|
---|
| 74 | const char *pszDefaultFilename)
|
---|
| 75 | : BaseTextScript(pUnattended, pszDefaultTemplateFilename, pszDefaultFilename), mpUnattended(pUnattended)
|
---|
| 76 | {
|
---|
| 77 | }
|
---|
| 78 |
|
---|
| 79 | HRESULT UnattendedScriptTemplate::saveToString(Utf8Str &rStrDst)
|
---|
| 80 | {
|
---|
[93190] | 81 | RTEXPREVAL hEvaluator = NIL_RTEXPREVAL;
|
---|
| 82 | int vrc = RTExprEvalCreate(&hEvaluator, 0, "unattended", this, UnattendedScriptTemplate::queryVariableForExpr);
|
---|
| 83 | AssertRCReturn(vrc, mpSetError->setErrorVrc(vrc));
|
---|
[68162] | 84 |
|
---|
| 85 | struct
|
---|
| 86 | {
|
---|
| 87 | bool fSavedOutputting;
|
---|
| 88 | } aConds[8];
|
---|
| 89 | unsigned cConds = 0;
|
---|
| 90 | bool fOutputting = true;
|
---|
| 91 | HRESULT hrc = E_FAIL;
|
---|
| 92 | size_t offTemplate = 0;
|
---|
| 93 | size_t cchTemplate = mStrScriptFullContent.length();
|
---|
| 94 | rStrDst.setNull();
|
---|
| 95 | for (;;)
|
---|
| 96 | {
|
---|
| 97 | /*
|
---|
| 98 | * Find the next placeholder and add any text before it to the output.
|
---|
| 99 | */
|
---|
[93190] | 100 | size_t offPlaceholder = mStrScriptFullContent.find(g_szPrefix, offTemplate);
|
---|
[68162] | 101 | size_t cchToCopy = offPlaceholder != RTCString::npos ? offPlaceholder - offTemplate : cchTemplate - offTemplate;
|
---|
| 102 | if (cchToCopy > 0)
|
---|
| 103 | {
|
---|
| 104 | if (fOutputting)
|
---|
| 105 | {
|
---|
| 106 | try
|
---|
| 107 | {
|
---|
[86659] | 108 | rStrDst.append(mStrScriptFullContent, offTemplate , cchToCopy);
|
---|
[68162] | 109 | }
|
---|
[73505] | 110 | catch (std::bad_alloc &)
|
---|
[68162] | 111 | {
|
---|
| 112 | hrc = E_OUTOFMEMORY;
|
---|
| 113 | break;
|
---|
| 114 | }
|
---|
| 115 | }
|
---|
| 116 | offTemplate += cchToCopy;
|
---|
| 117 | }
|
---|
| 118 |
|
---|
| 119 | /*
|
---|
| 120 | * Process placeholder.
|
---|
| 121 | */
|
---|
| 122 | if (offPlaceholder != RTCString::npos)
|
---|
| 123 | {
|
---|
| 124 | /*
|
---|
| 125 | * First we must find the end of the placeholder string.
|
---|
| 126 | */
|
---|
[93190] | 127 | size_t const cchMaxPlaceholder = RT_MIN(cchTemplate - offPlaceholder, _1K);
|
---|
| 128 | const char *pszPlaceholder = mStrScriptFullContent.c_str() + offPlaceholder;
|
---|
| 129 | size_t cchPlaceholder = sizeof(g_szPrefix) - 1;
|
---|
| 130 | char ch;
|
---|
[93192] | 131 | while ( cchPlaceholder < cchMaxPlaceholder
|
---|
[68162] | 132 | && (ch = pszPlaceholder[cchPlaceholder]) != '\0'
|
---|
[93190] | 133 | && (RT_C_IS_PRINT(ch) || RT_C_IS_SPACE(ch))
|
---|
| 134 | && ch != '@')
|
---|
[68162] | 135 | cchPlaceholder++;
|
---|
| 136 |
|
---|
| 137 | if ( offPlaceholder + cchPlaceholder < cchTemplate
|
---|
| 138 | && pszPlaceholder[cchPlaceholder] == '@')
|
---|
| 139 | {
|
---|
| 140 | cchPlaceholder++;
|
---|
| 141 | if ( offPlaceholder + cchPlaceholder < cchTemplate
|
---|
| 142 | && pszPlaceholder[cchPlaceholder] == '@')
|
---|
| 143 | cchPlaceholder++;
|
---|
| 144 | }
|
---|
| 145 |
|
---|
| 146 | if ( pszPlaceholder[cchPlaceholder - 1] != '@'
|
---|
| 147 | || pszPlaceholder[cchPlaceholder - 2] != '@'
|
---|
[93190] | 148 | || ( strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixInsert)) != 0
|
---|
| 149 | && strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixCond)) != 0
|
---|
| 150 | && strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixSplitter)) != 0 ) )
|
---|
[68162] | 151 | {
|
---|
[93190] | 152 | hrc = mpSetError->setError(E_FAIL, tr("Malformed or too long template placeholder '%.*s'"),
|
---|
[68162] | 153 | cchPlaceholder, pszPlaceholder);
|
---|
| 154 | break;
|
---|
| 155 | }
|
---|
| 156 |
|
---|
| 157 | offTemplate += cchPlaceholder;
|
---|
| 158 |
|
---|
| 159 | /*
|
---|
| 160 | * @@VBOX_INSERT_XXX@@:
|
---|
| 161 | */
|
---|
[93190] | 162 | if (strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixInsertXxx)) == 0)
|
---|
[68162] | 163 | {
|
---|
| 164 | /*
|
---|
| 165 | * Get the placeholder value and add it to the output.
|
---|
| 166 | */
|
---|
| 167 | RTCString strValue;
|
---|
| 168 | hrc = getReplacement(pszPlaceholder, cchPlaceholder, fOutputting, strValue);
|
---|
| 169 | if (SUCCEEDED(hrc))
|
---|
| 170 | {
|
---|
| 171 | if (fOutputting)
|
---|
| 172 | {
|
---|
| 173 | try
|
---|
| 174 | {
|
---|
| 175 | rStrDst.append(strValue);
|
---|
| 176 | }
|
---|
[73505] | 177 | catch (std::bad_alloc &)
|
---|
[68162] | 178 | {
|
---|
| 179 | hrc = E_OUTOFMEMORY;
|
---|
| 180 | break;
|
---|
| 181 | }
|
---|
| 182 | }
|
---|
| 183 | }
|
---|
| 184 | else
|
---|
| 185 | break;
|
---|
| 186 | }
|
---|
| 187 | /*
|
---|
[93190] | 188 | * @@VBOX_INSERT[expr]@@:
|
---|
| 189 | * @@VBOX_INSERT[expr]SH@@:
|
---|
| 190 | * @@VBOX_INSERT[expr]ELEMENT@@:
|
---|
| 191 | * @@VBOX_INSERT[expr]ATTRIB_DQ@@:
|
---|
| 192 | */
|
---|
| 193 | else if (strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixInsertExpr)) == 0)
|
---|
| 194 | {
|
---|
| 195 | /*
|
---|
| 196 | * Get the placeholder value and add it to the output.
|
---|
| 197 | */
|
---|
| 198 | char *pszValue = NULL;
|
---|
| 199 | hrc = getReplacementForExpr(hEvaluator, pszPlaceholder, cchPlaceholder, fOutputting, &pszValue);
|
---|
| 200 | if (SUCCEEDED(hrc))
|
---|
| 201 | {
|
---|
| 202 | if (fOutputting && pszValue)
|
---|
| 203 | {
|
---|
| 204 | try
|
---|
| 205 | {
|
---|
| 206 | rStrDst.append(pszValue);
|
---|
| 207 | }
|
---|
| 208 | catch (std::bad_alloc &)
|
---|
| 209 | {
|
---|
| 210 | hrc = E_OUTOFMEMORY;
|
---|
| 211 | break;
|
---|
| 212 | }
|
---|
| 213 | }
|
---|
| 214 | RTStrFree(pszValue);
|
---|
| 215 | }
|
---|
| 216 | else
|
---|
| 217 | break;
|
---|
| 218 | }
|
---|
| 219 | /*
|
---|
[68162] | 220 | * @@VBOX_COND_END@@: Pop one item of the conditional stack.
|
---|
| 221 | */
|
---|
[93190] | 222 | else if (strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixCondEnd)) == 0)
|
---|
[68162] | 223 | {
|
---|
| 224 | if (cConds > 0)
|
---|
| 225 | {
|
---|
| 226 | cConds--;
|
---|
| 227 | fOutputting = aConds[cConds].fSavedOutputting;
|
---|
| 228 | }
|
---|
| 229 | else
|
---|
| 230 | {
|
---|
| 231 | hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
|
---|
[90828] | 232 | tr("%s without @@VBOX_COND_XXX@@ at offset %zu (%#zx)"),
|
---|
[93190] | 233 | g_szPrefixCondEnd, offPlaceholder, offPlaceholder);
|
---|
[68162] | 234 | break;
|
---|
| 235 | }
|
---|
| 236 | }
|
---|
| 237 | /*
|
---|
[93108] | 238 | * @@VBOX_COND_ELSE@@: Flip the output setting of the current condition.
|
---|
| 239 | */
|
---|
[93190] | 240 | else if (strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixCondElse)) == 0)
|
---|
[93108] | 241 | {
|
---|
| 242 | if (cConds > 0)
|
---|
| 243 | fOutputting = !fOutputting;
|
---|
| 244 | else
|
---|
| 245 | {
|
---|
| 246 | hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
|
---|
| 247 | tr("%s without @@VBOX_COND_XXX@@ at offset %zu (%#zx)"),
|
---|
[93190] | 248 | g_szPrefixCondElse, offPlaceholder, offPlaceholder);
|
---|
[93108] | 249 | break;
|
---|
| 250 | }
|
---|
| 251 | }
|
---|
| 252 | /*
|
---|
[68162] | 253 | * @@VBOX_COND_XXX@@: Push the previous outputting state and combine it with the
|
---|
| 254 | * one from the condition.
|
---|
| 255 | */
|
---|
[93190] | 256 | else if (strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixCondXxx)) == 0)
|
---|
[68162] | 257 | {
|
---|
| 258 | if (cConds + 1 < RT_ELEMENTS(aConds))
|
---|
| 259 | {
|
---|
| 260 | aConds[cConds].fSavedOutputting = fOutputting;
|
---|
| 261 | bool fNewOutputting = fOutputting;
|
---|
| 262 | hrc = getConditional(pszPlaceholder, cchPlaceholder, &fNewOutputting);
|
---|
| 263 | if (SUCCEEDED(hrc))
|
---|
| 264 | fOutputting = fOutputting && fNewOutputting;
|
---|
| 265 | else
|
---|
| 266 | break;
|
---|
| 267 | cConds++;
|
---|
| 268 | }
|
---|
| 269 | else
|
---|
| 270 | {
|
---|
| 271 | hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
|
---|
[90828] | 272 | tr("Too deep conditional nesting at offset %zu (%#zx)"),
|
---|
[68162] | 273 | offPlaceholder, offPlaceholder);
|
---|
| 274 | break;
|
---|
| 275 | }
|
---|
| 276 | }
|
---|
[93085] | 277 | /*
|
---|
[93190] | 278 | * @@VBOX_COND[expr]@@: Push the previous outputting state and combine it with the
|
---|
| 279 | * one from the condition.
|
---|
| 280 | */
|
---|
| 281 | else if (strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixCondExpr)) == 0)
|
---|
| 282 | {
|
---|
| 283 | if (cConds + 1 < RT_ELEMENTS(aConds))
|
---|
| 284 | {
|
---|
| 285 | aConds[cConds].fSavedOutputting = fOutputting;
|
---|
| 286 | bool fNewOutputting = fOutputting;
|
---|
| 287 | hrc = resolveConditionalExpr(hEvaluator, pszPlaceholder, cchPlaceholder, &fNewOutputting);
|
---|
| 288 | if (SUCCEEDED(hrc))
|
---|
| 289 | fOutputting = fOutputting && fNewOutputting;
|
---|
| 290 | else
|
---|
| 291 | break;
|
---|
| 292 | cConds++;
|
---|
| 293 | }
|
---|
| 294 | else
|
---|
| 295 | {
|
---|
| 296 | hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
|
---|
| 297 | tr("Too deep conditional nesting at offset %zu (%#zx)"),
|
---|
| 298 | offPlaceholder, offPlaceholder);
|
---|
| 299 | break;
|
---|
| 300 | }
|
---|
| 301 | }
|
---|
| 302 | /*
|
---|
[93085] | 303 | * @@VBOX_SPLITTER_START/END[filename]@@: Ignored in this pass.
|
---|
| 304 | */
|
---|
| 305 | else
|
---|
| 306 | {
|
---|
[93190] | 307 | Assert(strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixSplitter)) == 0);
|
---|
[93085] | 308 | if (fOutputting)
|
---|
| 309 | {
|
---|
| 310 | try
|
---|
| 311 | {
|
---|
| 312 | rStrDst.append(pszPlaceholder, cchPlaceholder);
|
---|
| 313 | }
|
---|
| 314 | catch (std::bad_alloc &)
|
---|
| 315 | {
|
---|
| 316 | hrc = E_OUTOFMEMORY;
|
---|
| 317 | break;
|
---|
| 318 | }
|
---|
| 319 | }
|
---|
| 320 | }
|
---|
[68162] | 321 | }
|
---|
| 322 |
|
---|
| 323 | /*
|
---|
| 324 | * Done?
|
---|
| 325 | */
|
---|
| 326 | if (offTemplate >= cchTemplate)
|
---|
| 327 | {
|
---|
| 328 | if (cConds == 0)
|
---|
[93816] | 329 | {
|
---|
| 330 | RTExprEvalRelease(hEvaluator);
|
---|
[68162] | 331 | return S_OK;
|
---|
[93816] | 332 | }
|
---|
[68162] | 333 | if (cConds == 1)
|
---|
[90828] | 334 | hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, tr("Missing @@VBOX_COND_END@@"));
|
---|
[68162] | 335 | else
|
---|
[90828] | 336 | hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, tr("Missing %u @@VBOX_COND_END@@"), cConds);
|
---|
[68162] | 337 | break;
|
---|
| 338 | }
|
---|
| 339 | }
|
---|
| 340 |
|
---|
| 341 | /* failed */
|
---|
| 342 | rStrDst.setNull();
|
---|
[93190] | 343 | RTExprEvalRelease(hEvaluator);
|
---|
[68162] | 344 | return hrc;
|
---|
| 345 | }
|
---|
| 346 |
|
---|
| 347 | HRESULT UnattendedScriptTemplate::getReplacement(const char *pachPlaceholder, size_t cchPlaceholder,
|
---|
| 348 | bool fOutputting, RTCString &rValue)
|
---|
| 349 | {
|
---|
| 350 | /*
|
---|
| 351 | * Check for an escaping suffix. Drop the '@@'.
|
---|
| 352 | */
|
---|
[93190] | 353 | kEvalEscaping_T enmEscaping;
|
---|
[68162] | 354 | #define PLACEHOLDER_ENDS_WITH(a_szSuffix) \
|
---|
| 355 | ( cchPlaceholder > sizeof(a_szSuffix) - 1U \
|
---|
| 356 | && memcmp(&pachPlaceholder[cchPlaceholder - sizeof(a_szSuffix) + 1U], a_szSuffix, sizeof(a_szSuffix) - 1U) == 0)
|
---|
| 357 | if (PLACEHOLDER_ENDS_WITH("_SH@@"))
|
---|
| 358 | {
|
---|
| 359 | cchPlaceholder -= 3 + 2;
|
---|
| 360 | enmEscaping = kValueEscaping_Bourne;
|
---|
| 361 | }
|
---|
| 362 | else if (PLACEHOLDER_ENDS_WITH("_ELEMENT@@"))
|
---|
| 363 | {
|
---|
| 364 | cchPlaceholder -= 8 + 2;
|
---|
| 365 | enmEscaping = kValueEscaping_XML_Element;
|
---|
| 366 | }
|
---|
| 367 | else if (PLACEHOLDER_ENDS_WITH("_ATTRIB_DQ@@"))
|
---|
| 368 | {
|
---|
| 369 | cchPlaceholder -= 10 + 2;
|
---|
| 370 | enmEscaping = kValueEscaping_XML_Attribute_Double_Quotes;
|
---|
| 371 | }
|
---|
| 372 | else
|
---|
| 373 | {
|
---|
| 374 | Assert(PLACEHOLDER_ENDS_WITH("@@"));
|
---|
| 375 | cchPlaceholder -= 2;
|
---|
| 376 | enmEscaping = kValueEscaping_None;
|
---|
| 377 | }
|
---|
[93190] | 378 | #undef PLACEHOLDER_ENDS_WITH
|
---|
[68162] | 379 |
|
---|
| 380 | /*
|
---|
| 381 | * Resolve and escape the value.
|
---|
| 382 | */
|
---|
| 383 | HRESULT hrc;
|
---|
| 384 | try
|
---|
| 385 | {
|
---|
[93190] | 386 | Utf8Str strTmp;
|
---|
| 387 | const char *pszReadOnlyValue = NULL;
|
---|
| 388 | int vrc = queryVariable(pachPlaceholder + sizeof(g_szPrefixInsertXxx) - 1,
|
---|
| 389 | cchPlaceholder - sizeof(g_szPrefixInsertXxx) + 1,
|
---|
| 390 | strTmp, fOutputting ? &pszReadOnlyValue : NULL);
|
---|
| 391 | if (RT_SUCCESS(vrc))
|
---|
[68162] | 392 | {
|
---|
[93190] | 393 | if (fOutputting)
|
---|
[68162] | 394 | {
|
---|
[93190] | 395 | Assert(pszReadOnlyValue != NULL);
|
---|
| 396 | switch (enmEscaping)
|
---|
[68162] | 397 | {
|
---|
[93190] | 398 | case kValueEscaping_None:
|
---|
| 399 | rValue = pszReadOnlyValue;
|
---|
| 400 | return S_OK;
|
---|
| 401 |
|
---|
| 402 | case kValueEscaping_Bourne:
|
---|
| 403 | case kValueEscaping_XML_Element:
|
---|
| 404 | case kValueEscaping_XML_Attribute_Double_Quotes:
|
---|
[68162] | 405 | {
|
---|
[93190] | 406 | switch (enmEscaping)
|
---|
[68162] | 407 | {
|
---|
[93190] | 408 | case kValueEscaping_Bourne:
|
---|
[68162] | 409 | {
|
---|
[93190] | 410 | const char * const papszArgs[2] = { pszReadOnlyValue, NULL };
|
---|
| 411 | char *pszEscaped = NULL;
|
---|
| 412 | vrc = RTGetOptArgvToString(&pszEscaped, papszArgs, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
|
---|
| 413 | if (RT_SUCCESS(vrc))
|
---|
[68162] | 414 | {
|
---|
[93190] | 415 | try
|
---|
| 416 | {
|
---|
| 417 | rValue = pszEscaped;
|
---|
| 418 | RTStrFree(pszEscaped);
|
---|
| 419 | return S_OK;
|
---|
| 420 | }
|
---|
| 421 | catch (std::bad_alloc &)
|
---|
| 422 | {
|
---|
| 423 | hrc = E_OUTOFMEMORY;
|
---|
| 424 | }
|
---|
[68162] | 425 | RTStrFree(pszEscaped);
|
---|
| 426 | }
|
---|
[93190] | 427 | else
|
---|
| 428 | hrc = mpSetError->setErrorVrc(vrc);
|
---|
| 429 | break;
|
---|
[68162] | 430 | }
|
---|
| 431 |
|
---|
[93190] | 432 | case kValueEscaping_XML_Element:
|
---|
| 433 | rValue.printf("%RMes", pszReadOnlyValue);
|
---|
| 434 | return S_OK;
|
---|
[68162] | 435 |
|
---|
[93190] | 436 | case kValueEscaping_XML_Attribute_Double_Quotes:
|
---|
| 437 | {
|
---|
| 438 | RTCString strTmp2;
|
---|
| 439 | strTmp2.printf("%RMas", pszReadOnlyValue);
|
---|
| 440 | rValue = RTCString(strTmp2, 1, strTmp2.length() - 2);
|
---|
| 441 | return S_OK;
|
---|
| 442 | }
|
---|
| 443 |
|
---|
| 444 | default:
|
---|
| 445 | hrc = E_FAIL;
|
---|
| 446 | break;
|
---|
[68162] | 447 | }
|
---|
[93190] | 448 | break;
|
---|
| 449 | }
|
---|
[68162] | 450 |
|
---|
[93190] | 451 | default:
|
---|
| 452 | AssertFailedStmt(hrc = E_FAIL);
|
---|
| 453 | break;
|
---|
[68162] | 454 | }
|
---|
| 455 | }
|
---|
[93190] | 456 | else
|
---|
| 457 | hrc = S_OK;
|
---|
[68162] | 458 | }
|
---|
[93190] | 459 | else
|
---|
| 460 | hrc = E_FAIL;
|
---|
[68162] | 461 | }
|
---|
[73505] | 462 | catch (std::bad_alloc &)
|
---|
[68162] | 463 | {
|
---|
| 464 | hrc = E_OUTOFMEMORY;
|
---|
| 465 | }
|
---|
| 466 | rValue.setNull();
|
---|
| 467 | return hrc;
|
---|
| 468 | }
|
---|
| 469 |
|
---|
[93190] | 470 | HRESULT UnattendedScriptTemplate::getReplacementForExpr(RTEXPREVAL hEvaluator, const char *pachPlaceholder, size_t cchPlaceholder,
|
---|
| 471 | bool fOutputting, char **ppszValue) RT_NOEXCEPT
|
---|
[68162] | 472 | {
|
---|
[93190] | 473 | /*
|
---|
| 474 | * Process the tail of the placeholder to figure out the escaping rules.
|
---|
| 475 | *
|
---|
| 476 | * @@VBOX_INSERT[expr]@@:
|
---|
| 477 | * @@VBOX_INSERT[expr]SH@@:
|
---|
| 478 | * @@VBOX_INSERT[expr]ELEMENT@@:
|
---|
| 479 | * @@VBOX_INSERT[expr]ATTRIB_DQ@@:
|
---|
| 480 | */
|
---|
| 481 | kEvalEscaping_T enmEscaping;
|
---|
| 482 | #define PLACEHOLDER_ENDS_WITH(a_szSuffix) \
|
---|
| 483 | ( cchPlaceholder > sizeof(a_szSuffix) - 1U \
|
---|
| 484 | && memcmp(&pachPlaceholder[cchPlaceholder - sizeof(a_szSuffix) + 1U], a_szSuffix, sizeof(a_szSuffix) - 1U) == 0)
|
---|
| 485 | if (PLACEHOLDER_ENDS_WITH("]SH@@"))
|
---|
| 486 | {
|
---|
| 487 | cchPlaceholder -= sizeof("]SH@@") - 1;
|
---|
| 488 | enmEscaping = kValueEscaping_Bourne;
|
---|
| 489 | }
|
---|
| 490 | else if (PLACEHOLDER_ENDS_WITH("]ELEMENT@@"))
|
---|
| 491 | {
|
---|
| 492 | cchPlaceholder -= sizeof("]ELEMENT@@") - 1;
|
---|
| 493 | enmEscaping = kValueEscaping_XML_Element;
|
---|
| 494 | }
|
---|
| 495 | else if (PLACEHOLDER_ENDS_WITH("]ATTRIB_DQ@@"))
|
---|
| 496 | {
|
---|
| 497 | cchPlaceholder -= sizeof("]ATTRIB_DQ@@") - 1;
|
---|
| 498 | enmEscaping = kValueEscaping_XML_Attribute_Double_Quotes;
|
---|
| 499 | }
|
---|
| 500 | else if (PLACEHOLDER_ENDS_WITH("]@@"))
|
---|
| 501 | {
|
---|
| 502 | cchPlaceholder -= sizeof("]@@") - 1;
|
---|
| 503 | enmEscaping = kValueEscaping_None;
|
---|
| 504 | }
|
---|
| 505 | else
|
---|
| 506 | return mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, tr("Malformed @@VBOX_INSERT[expr]@@: Missing ']' (%.*s)"),
|
---|
| 507 | cchPlaceholder, pachPlaceholder);
|
---|
| 508 | #undef PLACEHOLDER_ENDS_WITH
|
---|
[68162] | 509 |
|
---|
[93190] | 510 | /* The placeholder prefix length. The expression is from cchPrefix to cchPlaceholder. */
|
---|
| 511 | size_t const cchPrefix = sizeof(g_szPrefixInsertExpr) - 1;
|
---|
| 512 | Assert(pachPlaceholder[cchPrefix - 1] == '[');
|
---|
| 513 |
|
---|
| 514 | /*
|
---|
| 515 | * Evaluate the expression. We do this regardless of fOutput for now.
|
---|
| 516 | */
|
---|
| 517 | RTERRINFOSTATIC ErrInfo;
|
---|
| 518 | char *pszValue = NULL;
|
---|
| 519 | int vrc = RTExprEvalToString(hEvaluator, &pachPlaceholder[cchPrefix], cchPlaceholder - cchPrefix, &pszValue,
|
---|
| 520 | RTErrInfoInitStatic(&ErrInfo));
|
---|
| 521 | LogFlowFunc(("RTExprEvalToString(%.*s) -> %Rrc pszValue=%s\n",
|
---|
| 522 | cchPlaceholder - cchPrefix, &pachPlaceholder[cchPrefix], vrc, pszValue));
|
---|
| 523 | if (RT_SUCCESS(vrc))
|
---|
[86274] | 524 | {
|
---|
[93190] | 525 | if (fOutputting)
|
---|
[86274] | 526 | {
|
---|
[93190] | 527 | switch (enmEscaping)
|
---|
| 528 | {
|
---|
| 529 | case kValueEscaping_None:
|
---|
| 530 | *ppszValue = pszValue;
|
---|
| 531 | pszValue = NULL;
|
---|
| 532 | break;
|
---|
| 533 |
|
---|
| 534 | case kValueEscaping_Bourne:
|
---|
| 535 | {
|
---|
| 536 | const char * const papszArgs[2] = { pszValue, NULL };
|
---|
| 537 | vrc = RTGetOptArgvToString(ppszValue, papszArgs, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
|
---|
| 538 | break;
|
---|
| 539 | }
|
---|
| 540 |
|
---|
| 541 | case kValueEscaping_XML_Element:
|
---|
| 542 | vrc = RTStrAPrintf(ppszValue, "%RMes", pszValue);
|
---|
| 543 | break;
|
---|
| 544 |
|
---|
| 545 | case kValueEscaping_XML_Attribute_Double_Quotes:
|
---|
| 546 | vrc = RTStrAPrintf(ppszValue, "%RMas", pszValue);
|
---|
| 547 | if (RT_SUCCESS(vrc))
|
---|
| 548 | {
|
---|
| 549 | /* drop the quotes */
|
---|
| 550 | char *pszRet = *ppszValue;
|
---|
| 551 | size_t const cchRet = strlen(pszRet) - 2;
|
---|
| 552 | memmove(pszRet, &pszRet[1], cchRet);
|
---|
| 553 | pszRet[cchRet] = '\0';
|
---|
| 554 | }
|
---|
| 555 | break;
|
---|
| 556 |
|
---|
| 557 | default:
|
---|
| 558 | AssertFailedStmt(vrc = VERR_IPE_NOT_REACHED_DEFAULT_CASE);
|
---|
| 559 | break;
|
---|
| 560 | }
|
---|
| 561 | RTStrFree(pszValue);
|
---|
| 562 | if (RT_FAILURE(vrc))
|
---|
| 563 | return mpSetError->setErrorVrc(vrc);
|
---|
[86274] | 564 | }
|
---|
[93190] | 565 | else
|
---|
| 566 | {
|
---|
| 567 | *ppszValue = NULL;
|
---|
| 568 | RTStrFree(pszValue);
|
---|
| 569 | }
|
---|
[86274] | 570 | }
|
---|
[93190] | 571 | else
|
---|
| 572 | return mpSetError->setErrorBoth(E_FAIL, vrc, tr("Expression evaluation error for '%.*s': %#RTeic"),
|
---|
| 573 | cchPlaceholder, pachPlaceholder, &ErrInfo.Core);
|
---|
| 574 | return S_OK;
|
---|
| 575 | }
|
---|
| 576 |
|
---|
| 577 | HRESULT UnattendedScriptTemplate::resolveConditionalExpr(RTEXPREVAL hEvaluator, const char *pachPlaceholder,
|
---|
| 578 | size_t cchPlaceholder, bool *pfOutputting) RT_NOEXCEPT
|
---|
| 579 | {
|
---|
| 580 | /*
|
---|
| 581 | * Check the placeholder tail: @@VBOX_COND[expr]@@
|
---|
| 582 | */
|
---|
| 583 | static const char s_szTail[] = "]@@";
|
---|
| 584 | if (memcmp(&pachPlaceholder[cchPlaceholder - sizeof(s_szTail) + 1], RT_STR_TUPLE(s_szTail)) != 0)
|
---|
| 585 | return mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, tr("Malformed @@VBOX_COND[expr]@@: Missing ']' (%.*s)"),
|
---|
| 586 | cchPlaceholder, pachPlaceholder);
|
---|
| 587 | Assert(pachPlaceholder[sizeof(g_szPrefixCondExpr) - 2 ] == '[');
|
---|
| 588 |
|
---|
| 589 | /*
|
---|
| 590 | * Evaluate the expression.
|
---|
| 591 | */
|
---|
| 592 | RTERRINFOSTATIC ErrInfo;
|
---|
| 593 | const char * const pchExpr = &pachPlaceholder[sizeof(g_szPrefixCondExpr) - 1];
|
---|
| 594 | size_t const cchExpr = cchPlaceholder - sizeof(g_szPrefixCondExpr) + 1 - sizeof(s_szTail) + 1;
|
---|
| 595 | int vrc = RTExprEvalToBool(hEvaluator, pchExpr, cchExpr, pfOutputting, RTErrInfoInitStatic(&ErrInfo));
|
---|
| 596 | LogFlowFunc(("RTExprEvalToBool(%.*s) -> %Rrc *pfOutputting=%s\n", cchExpr, pchExpr, vrc, *pfOutputting));
|
---|
| 597 | if (RT_SUCCESS(vrc))
|
---|
| 598 | return S_OK;
|
---|
| 599 | return mpSetError->setErrorBoth(E_FAIL, vrc, tr("Expression evaluation error for '%.*s': %#RTeic"),
|
---|
| 600 | cchPlaceholder, pachPlaceholder, &ErrInfo.Core);
|
---|
| 601 | }
|
---|
| 602 |
|
---|
| 603 | /*static */ DECLCALLBACK(int)
|
---|
[93191] | 604 | UnattendedScriptTemplate::queryVariableForExpr(const char *pchName, size_t cchName, void *pvUser, char **ppszValue) RT_NOEXCEPT
|
---|
[93190] | 605 | {
|
---|
| 606 | UnattendedScriptTemplate *pThis = (UnattendedScriptTemplate *)pvUser;
|
---|
| 607 | int vrc;
|
---|
| 608 | try
|
---|
[68162] | 609 | {
|
---|
[93190] | 610 | const char *pszReadOnlyValue = NULL;
|
---|
| 611 | Utf8Str strTmp;
|
---|
| 612 | vrc = pThis->queryVariable(pchName, cchName, strTmp, ppszValue ? &pszReadOnlyValue : NULL);
|
---|
| 613 | if (ppszValue)
|
---|
| 614 | {
|
---|
| 615 | if (RT_SUCCESS(vrc))
|
---|
| 616 | vrc = RTStrDupEx(ppszValue, pszReadOnlyValue);
|
---|
| 617 | else
|
---|
| 618 | *ppszValue = NULL;
|
---|
| 619 | }
|
---|
| 620 | }
|
---|
| 621 | catch (std::bad_alloc &)
|
---|
| 622 | {
|
---|
| 623 | vrc = VERR_NO_MEMORY;
|
---|
| 624 | *ppszValue = NULL;
|
---|
| 625 | }
|
---|
| 626 | return vrc;
|
---|
| 627 | }
|
---|
| 628 |
|
---|
| 629 | int UnattendedScriptTemplate::queryVariable(const char *pchName, size_t cchName, Utf8Str &rstrTmp, const char **ppszValue)
|
---|
| 630 | {
|
---|
| 631 | #define IS_MATCH(a_szMatch) \
|
---|
| 632 | (cchName == sizeof(a_szMatch) - 1U && memcmp(pchName, a_szMatch, sizeof(a_szMatch) - 1U) == 0)
|
---|
| 633 |
|
---|
| 634 | const char *pszValue;
|
---|
| 635 |
|
---|
| 636 | /*
|
---|
| 637 | * Variables
|
---|
| 638 | */
|
---|
| 639 | if (IS_MATCH("USER_LOGIN"))
|
---|
| 640 | pszValue = mpUnattended->i_getUser().c_str();
|
---|
| 641 | else if (IS_MATCH("USER_PASSWORD"))
|
---|
| 642 | pszValue = mpUnattended->i_getPassword().c_str();
|
---|
| 643 | else if (IS_MATCH("ROOT_PASSWORD"))
|
---|
| 644 | pszValue = mpUnattended->i_getPassword().c_str();
|
---|
| 645 | else if (IS_MATCH("USER_FULL_NAME"))
|
---|
| 646 | pszValue = mpUnattended->i_getFullUserName().c_str();
|
---|
| 647 | else if (IS_MATCH("PRODUCT_KEY"))
|
---|
| 648 | pszValue = mpUnattended->i_getProductKey().c_str();
|
---|
| 649 | else if (IS_MATCH("POST_INSTALL_COMMAND"))
|
---|
| 650 | pszValue = mpUnattended->i_getPostInstallCommand().c_str();
|
---|
| 651 | else if (IS_MATCH("AUXILIARY_INSTALL_DIR"))
|
---|
| 652 | pszValue = mpUnattended->i_getAuxiliaryInstallDir().c_str();
|
---|
| 653 | else if (IS_MATCH("IMAGE_INDEX"))
|
---|
| 654 | pszValue = rstrTmp.printf("%u", mpUnattended->i_getImageIndex()).c_str();
|
---|
| 655 | else if (IS_MATCH("OS_ARCH"))
|
---|
| 656 | pszValue = mpUnattended->i_isGuestOs64Bit() ? "amd64" : "x86";
|
---|
| 657 | else if (IS_MATCH("OS_ARCH2"))
|
---|
| 658 | pszValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "x86";
|
---|
| 659 | else if (IS_MATCH("OS_ARCH3"))
|
---|
| 660 | pszValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i386";
|
---|
| 661 | else if (IS_MATCH("OS_ARCH4"))
|
---|
| 662 | pszValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i486";
|
---|
| 663 | else if (IS_MATCH("OS_ARCH6"))
|
---|
| 664 | pszValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i686";
|
---|
| 665 | else if (IS_MATCH("GUEST_OS_VERSION"))
|
---|
| 666 | pszValue = mpUnattended->i_getDetectedOSVersion().c_str();
|
---|
| 667 | else if (IS_MATCH("GUEST_OS_MAJOR_VERSION"))
|
---|
| 668 | {
|
---|
| 669 | Utf8Str const &rstrOsVer = mpUnattended->i_getDetectedOSVersion();
|
---|
| 670 | size_t offDot = rstrOsVer.find('.');
|
---|
| 671 | if (offDot > 0 && offDot != Utf8Str::npos)
|
---|
| 672 | pszValue = rstrTmp.assign(rstrOsVer, 0, offDot).c_str(); /* caller catches std::bad_alloc */
|
---|
| 673 | else if (!ppszValue)
|
---|
| 674 | return VERR_NOT_FOUND;
|
---|
| 675 | else
|
---|
| 676 | {
|
---|
| 677 | mpSetError->setErrorBoth(E_FAIL, VERR_NO_DATA, tr("Unknown guest OS major version '%s'"), rstrOsVer.c_str());
|
---|
| 678 | return VERR_NO_DATA;
|
---|
| 679 | }
|
---|
| 680 | }
|
---|
| 681 | else if (IS_MATCH("TIME_ZONE_UX"))
|
---|
| 682 | pszValue = mpUnattended->i_getTimeZoneInfo()
|
---|
| 683 | ? mpUnattended->i_getTimeZoneInfo()->pszUnixName : mpUnattended->i_getTimeZone().c_str();
|
---|
| 684 | else if (IS_MATCH("TIME_ZONE_WIN_NAME"))
|
---|
| 685 | {
|
---|
[68162] | 686 | PCRTTIMEZONEINFO pInfo = mpUnattended->i_getTimeZoneInfo();
|
---|
| 687 | if (pInfo)
|
---|
[93190] | 688 | pszValue = pInfo->pszWindowsName ? pInfo->pszWindowsName : "GMT";
|
---|
[68162] | 689 | else
|
---|
[93190] | 690 | pszValue = mpUnattended->i_getTimeZone().c_str();
|
---|
[68162] | 691 | }
|
---|
[93190] | 692 | else if (IS_MATCH("TIME_ZONE_WIN_INDEX"))
|
---|
[68162] | 693 | {
|
---|
| 694 | PCRTTIMEZONEINFO pInfo = mpUnattended->i_getTimeZoneInfo();
|
---|
| 695 | if (pInfo)
|
---|
[93190] | 696 | pszValue = rstrTmp.printf("%u", pInfo->idxWindows ? pInfo->idxWindows : 85 /*GMT*/).c_str();
|
---|
[68162] | 697 | else
|
---|
[93190] | 698 | pszValue = mpUnattended->i_getTimeZone().c_str();
|
---|
[68162] | 699 | }
|
---|
[93190] | 700 | else if (IS_MATCH("LOCALE"))
|
---|
| 701 | pszValue = mpUnattended->i_getLocale().c_str();
|
---|
| 702 | else if (IS_MATCH("DASH_LOCALE"))
|
---|
[68222] | 703 | {
|
---|
[93190] | 704 | Assert(mpUnattended->i_getLocale()[2] == '_');
|
---|
| 705 | pszValue = rstrTmp.assign(mpUnattended->i_getLocale()).replace(2, 1, "-").c_str();
|
---|
[68222] | 706 | }
|
---|
[93190] | 707 | else if (IS_MATCH("LANGUAGE"))
|
---|
| 708 | pszValue = mpUnattended->i_getLanguage().c_str();
|
---|
| 709 | else if (IS_MATCH("COUNTRY"))
|
---|
| 710 | pszValue = mpUnattended->i_getCountry().c_str();
|
---|
| 711 | else if (IS_MATCH("HOSTNAME_FQDN"))
|
---|
| 712 | pszValue = mpUnattended->i_getHostname().c_str();
|
---|
| 713 | else if (IS_MATCH("HOSTNAME_WITHOUT_DOMAIN"))
|
---|
| 714 | pszValue = rstrTmp.assign(mpUnattended->i_getHostname(), 0, mpUnattended->i_getHostname().find(".")).c_str();
|
---|
| 715 | else if (IS_MATCH("HOSTNAME_WITHOUT_DOMAIN_MAX_15"))
|
---|
| 716 | pszValue = rstrTmp.assign(mpUnattended->i_getHostname(), 0, RT_MIN(mpUnattended->i_getHostname().find("."), 15)).c_str();
|
---|
| 717 | else if (IS_MATCH("HOSTNAME_DOMAIN"))
|
---|
| 718 | pszValue = rstrTmp.assign(mpUnattended->i_getHostname(), mpUnattended->i_getHostname().find(".") + 1).c_str();
|
---|
| 719 | else if (IS_MATCH("PROXY"))
|
---|
| 720 | pszValue = mpUnattended->i_getProxy().c_str();
|
---|
| 721 | /*
|
---|
| 722 | * Indicator variables.
|
---|
| 723 | */
|
---|
| 724 | else if (IS_MATCH("IS_INSTALLING_ADDITIONS"))
|
---|
| 725 | pszValue = mpUnattended->i_getInstallGuestAdditions() ? "1" : "0";
|
---|
| 726 | else if (IS_MATCH("IS_USER_LOGIN_ADMINISTRATOR"))
|
---|
| 727 | pszValue = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) == 0 ? "1" : "0";
|
---|
| 728 | else if (IS_MATCH("IS_INSTALLING_TEST_EXEC_SERVICE"))
|
---|
| 729 | pszValue = mpUnattended->i_getInstallTestExecService() ? "1" : "0";
|
---|
| 730 | else if (IS_MATCH("HAS_POST_INSTALL_COMMAND"))
|
---|
| 731 | pszValue = mpUnattended->i_getPostInstallCommand().isNotEmpty() ? "1" : "0";
|
---|
| 732 | else if (IS_MATCH("HAS_PRODUCT_KEY"))
|
---|
| 733 | pszValue = mpUnattended->i_getProductKey().isNotEmpty() ? "1" : "0";
|
---|
| 734 | else if (IS_MATCH("IS_MINIMAL_INSTALLATION"))
|
---|
| 735 | pszValue = mpUnattended->i_isMinimalInstallation() ? "1" : "0";
|
---|
| 736 | else if (IS_MATCH("IS_FIRMWARE_UEFI"))
|
---|
| 737 | pszValue = mpUnattended->i_isFirmwareEFI() ? "1" : "0";
|
---|
| 738 | else if (IS_MATCH("IS_RTC_USING_UTC"))
|
---|
| 739 | pszValue = mpUnattended->i_isRtcUsingUtc() ? "1" : "0";
|
---|
| 740 | else if (IS_MATCH("HAS_PROXY"))
|
---|
| 741 | pszValue = mpUnattended->i_getProxy().isNotEmpty() ? "1" : "0";
|
---|
| 742 | /*
|
---|
| 743 | * Unknown variable.
|
---|
| 744 | */
|
---|
| 745 | else if (!ppszValue)
|
---|
| 746 | return VERR_NOT_FOUND;
|
---|
[68162] | 747 | else
|
---|
| 748 | {
|
---|
[93190] | 749 | mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, tr("Unknown variable '%.*s'"), cchName, pchName);
|
---|
| 750 | return VERR_NO_DATA;
|
---|
[68162] | 751 | }
|
---|
[93190] | 752 | if (ppszValue)
|
---|
| 753 | *ppszValue = pszValue;
|
---|
| 754 | return VINF_SUCCESS;
|
---|
[68162] | 755 | }
|
---|
| 756 |
|
---|
| 757 | HRESULT UnattendedScriptTemplate::getConditional(const char *pachPlaceholder, size_t cchPlaceholder, bool *pfOutputting)
|
---|
| 758 | {
|
---|
| 759 | #define IS_PLACEHOLDER_MATCH(a_szMatch) \
|
---|
| 760 | ( cchPlaceholder == sizeof("@@VBOX_COND_" a_szMatch "@@") - 1U \
|
---|
| 761 | && memcmp(pachPlaceholder, "@@VBOX_COND_" a_szMatch "@@", sizeof("@@VBOX_COND_" a_szMatch "@@") - 1U) == 0)
|
---|
| 762 |
|
---|
[84564] | 763 | /* Install Guest Additions: */
|
---|
[68162] | 764 | if (IS_PLACEHOLDER_MATCH("IS_INSTALLING_ADDITIONS"))
|
---|
| 765 | *pfOutputting = mpUnattended->i_getInstallGuestAdditions();
|
---|
| 766 | else if (IS_PLACEHOLDER_MATCH("IS_NOT_INSTALLING_ADDITIONS"))
|
---|
| 767 | *pfOutputting = !mpUnattended->i_getInstallGuestAdditions();
|
---|
| 768 | /* User == Administrator: */
|
---|
| 769 | else if (IS_PLACEHOLDER_MATCH("IS_USER_LOGIN_ADMINISTRATOR"))
|
---|
| 770 | *pfOutputting = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) == 0;
|
---|
| 771 | else if (IS_PLACEHOLDER_MATCH("IS_USER_LOGIN_NOT_ADMINISTRATOR"))
|
---|
| 772 | *pfOutputting = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) != 0;
|
---|
| 773 | /* Install TXS: */
|
---|
| 774 | else if (IS_PLACEHOLDER_MATCH("IS_INSTALLING_TEST_EXEC_SERVICE"))
|
---|
| 775 | *pfOutputting = mpUnattended->i_getInstallTestExecService();
|
---|
| 776 | else if (IS_PLACEHOLDER_MATCH("IS_NOT_INSTALLING_TEST_EXEC_SERVICE"))
|
---|
| 777 | *pfOutputting = !mpUnattended->i_getInstallTestExecService();
|
---|
| 778 | /* Post install command: */
|
---|
| 779 | else if (IS_PLACEHOLDER_MATCH("HAS_POST_INSTALL_COMMAND"))
|
---|
| 780 | *pfOutputting = mpUnattended->i_getPostInstallCommand().isNotEmpty();
|
---|
| 781 | else if (IS_PLACEHOLDER_MATCH("HAS_NO_POST_INSTALL_COMMAND"))
|
---|
[91502] | 782 | *pfOutputting = mpUnattended->i_getPostInstallCommand().isEmpty();
|
---|
[68251] | 783 | /* Product key: */
|
---|
| 784 | else if (IS_PLACEHOLDER_MATCH("HAS_PRODUCT_KEY"))
|
---|
| 785 | *pfOutputting = mpUnattended->i_getProductKey().isNotEmpty();
|
---|
| 786 | else if (IS_PLACEHOLDER_MATCH("HAS_NO_PRODUCT_KEY"))
|
---|
[91502] | 787 | *pfOutputting = mpUnattended->i_getProductKey().isEmpty();
|
---|
[68162] | 788 | /* Minimal installation: */
|
---|
| 789 | else if (IS_PLACEHOLDER_MATCH("IS_MINIMAL_INSTALLATION"))
|
---|
| 790 | *pfOutputting = mpUnattended->i_isMinimalInstallation();
|
---|
| 791 | else if (IS_PLACEHOLDER_MATCH("IS_NOT_MINIMAL_INSTALLATION"))
|
---|
| 792 | *pfOutputting = !mpUnattended->i_isMinimalInstallation();
|
---|
[91502] | 793 | /* Is firmware UEFI: */
|
---|
| 794 | else if (IS_PLACEHOLDER_MATCH("IS_FIRMWARE_UEFI"))
|
---|
| 795 | *pfOutputting = mpUnattended->i_isFirmwareEFI();
|
---|
| 796 | else if (IS_PLACEHOLDER_MATCH("IS_NOT_FIRMWARE_UEFI"))
|
---|
| 797 | *pfOutputting = !mpUnattended->i_isFirmwareEFI();
|
---|
[68164] | 798 | /* Is RTC using UTC (i.e. set to UTC time on startup): */
|
---|
| 799 | else if (IS_PLACEHOLDER_MATCH("IS_RTC_USING_UTC"))
|
---|
| 800 | *pfOutputting = mpUnattended->i_isRtcUsingUtc();
|
---|
| 801 | else if (IS_PLACEHOLDER_MATCH("IS_NOT_RTC_USING_UTC"))
|
---|
| 802 | *pfOutputting = !mpUnattended->i_isRtcUsingUtc();
|
---|
[84645] | 803 | else if (IS_PLACEHOLDER_MATCH("HAS_PROXY"))
|
---|
| 804 | *pfOutputting = mpUnattended->i_getProxy().isNotEmpty();
|
---|
[94533] | 805 | else if (IS_PLACEHOLDER_MATCH("AVOID_UPDATES_OVER_NETWORK"))
|
---|
| 806 | *pfOutputting = mpUnattended->i_getAvoidUpdatesOverNetwork();
|
---|
[68162] | 807 | else
|
---|
[90828] | 808 | return mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, tr("Unknown conditional placeholder '%.*s'"),
|
---|
[68162] | 809 | cchPlaceholder, pachPlaceholder);
|
---|
| 810 | return S_OK;
|
---|
| 811 | #undef IS_PLACEHOLDER_MATCH
|
---|
| 812 | }
|
---|
| 813 |
|
---|
[73716] | 814 | #endif /* VBOX_WITH_UNATTENDED */
|
---|
[76168] | 815 | #if 0 /* Keeping this a reference */
|
---|
[68162] | 816 |
|
---|
[73716] | 817 |
|
---|
[76167] | 818 | /*********************************************************************************************************************************
|
---|
| 819 | * UnattendedSUSEXMLScript Implementation *
|
---|
| 820 | *********************************************************************************************************************************/
|
---|
[68162] | 821 |
|
---|
| 822 | HRESULT UnattendedSUSEXMLScript::parse()
|
---|
| 823 | {
|
---|
| 824 | HRESULT hrc = UnattendedXMLScript::parse();
|
---|
| 825 | if (SUCCEEDED(hrc))
|
---|
| 826 | {
|
---|
| 827 | /*
|
---|
| 828 | * Check that we've got the right root element type.
|
---|
| 829 | */
|
---|
| 830 | const xml::ElementNode *pelmRoot = mDoc.getRootElement();
|
---|
| 831 | if ( pelmRoot
|
---|
| 832 | && strcmp(pelmRoot->getName(), "profile") == 0)
|
---|
| 833 | {
|
---|
| 834 | /*
|
---|
| 835 | * Work thought the sections.
|
---|
| 836 | */
|
---|
| 837 | try
|
---|
| 838 | {
|
---|
| 839 | LoopThruSections(pelmRoot);
|
---|
| 840 | hrc = S_OK;
|
---|
| 841 | }
|
---|
[73505] | 842 | catch (std::bad_alloc &)
|
---|
[68162] | 843 | {
|
---|
| 844 | hrc = E_OUTOFMEMORY;
|
---|
| 845 | }
|
---|
| 846 | }
|
---|
| 847 | else if (pelmRoot)
|
---|
[90828] | 848 | hrc = mpSetError->setError(E_FAIL, tr("XML document root element is '%s' instead of 'profile'"),
|
---|
[68162] | 849 | pelmRoot->getName());
|
---|
| 850 | else
|
---|
[90828] | 851 | hrc = mpSetError->setError(E_FAIL, tr("Missing XML root element"));
|
---|
[68162] | 852 | }
|
---|
| 853 | return hrc;
|
---|
| 854 | }
|
---|
| 855 |
|
---|
| 856 | HRESULT UnattendedSUSEXMLScript::setFieldInElement(xml::ElementNode *pElement, const DataId enmDataId, const Utf8Str &rStrValue)
|
---|
| 857 | {
|
---|
| 858 | /*
|
---|
| 859 | * Don't set empty values.
|
---|
| 860 | */
|
---|
| 861 | if (rStrValue.isEmpty())
|
---|
| 862 | {
|
---|
| 863 | Utf8Str strProbableValue;
|
---|
| 864 | try
|
---|
| 865 | {
|
---|
| 866 | strProbableValue = createProbableValue(enmDataId, pElement);
|
---|
| 867 | }
|
---|
[73505] | 868 | catch (std::bad_alloc &)
|
---|
[68162] | 869 | {
|
---|
| 870 | return E_OUTOFMEMORY;
|
---|
| 871 | }
|
---|
| 872 | return UnattendedXMLScript::setFieldInElement(pElement, enmDataId, strProbableValue);
|
---|
| 873 | }
|
---|
| 874 | return UnattendedXMLScript::setFieldInElement(pElement, enmDataId, rStrValue);
|
---|
| 875 | }
|
---|
| 876 |
|
---|
| 877 | HRESULT UnattendedSUSEXMLScript::LoopThruSections(const xml::ElementNode *pelmRoot)
|
---|
| 878 | {
|
---|
| 879 | xml::NodesLoop loopChildren(*pelmRoot);
|
---|
| 880 | const xml::ElementNode *pelmOuterLoop;
|
---|
| 881 | while ((pelmOuterLoop = loopChildren.forAllNodes()) != NULL)
|
---|
| 882 | {
|
---|
| 883 | const char *pcszElemName = pelmOuterLoop->getName();
|
---|
| 884 | if (!strcmp(pcszElemName, "users"))
|
---|
| 885 | {
|
---|
| 886 | xml::NodesLoop loopUsers(*pelmOuterLoop);
|
---|
| 887 | const xml::ElementNode *pelmUser;
|
---|
| 888 | while ((pelmUser = loopUsers.forAllNodes()) != NULL)
|
---|
| 889 | {
|
---|
| 890 | HRESULT hrc = HandleUserAccountsSection(pelmUser);
|
---|
| 891 | if (FAILED(hrc))
|
---|
| 892 | return hrc;
|
---|
| 893 | }
|
---|
| 894 | }
|
---|
| 895 | }
|
---|
| 896 | return S_OK;
|
---|
| 897 | }
|
---|
| 898 |
|
---|
| 899 | HRESULT UnattendedSUSEXMLScript::HandleUserAccountsSection(const xml::ElementNode *pelmSection)
|
---|
| 900 | {
|
---|
| 901 | xml::NodesLoop loopUser(*pelmSection);
|
---|
| 902 |
|
---|
| 903 | const xml::ElementNode *pelmCur;
|
---|
| 904 | while ((pelmCur = loopUser.forAllNodes()) != NULL)
|
---|
| 905 | {
|
---|
| 906 | const char *pszValue = pelmCur->getValue();
|
---|
| 907 | #ifdef LOG_ENABLED
|
---|
| 908 | if (!RTStrCmp(pelmCur->getName(), "uid"))
|
---|
| 909 | LogRelFunc(("UnattendedSUSEXMLScript::HandleUserAccountsSection profile/users/%s/%s = %s\n",
|
---|
| 910 | pelmSection->getName(), pelmCur->getName(), pszValue));
|
---|
| 911 | #endif
|
---|
| 912 |
|
---|
| 913 | if (!RTStrCmp(pszValue, "$homedir"))
|
---|
| 914 | mNodesForCorrectionMap.insert(make_pair(USERHOMEDIR_ID, pelmCur));
|
---|
| 915 |
|
---|
| 916 | if (!RTStrCmp(pszValue, "$user"))
|
---|
| 917 | mNodesForCorrectionMap.insert(make_pair(USERNAME_ID, pelmCur));
|
---|
| 918 |
|
---|
| 919 | if (!RTStrCmp(pszValue, "$password"))
|
---|
| 920 | mNodesForCorrectionMap.insert(make_pair(USERPASSWORD_ID, pelmCur));
|
---|
| 921 | }
|
---|
| 922 | return S_OK;
|
---|
| 923 | }
|
---|
| 924 |
|
---|
| 925 | Utf8Str UnattendedSUSEXMLScript::createProbableValue(const DataId enmDataId, const xml::ElementNode *pCurElem)
|
---|
| 926 | {
|
---|
| 927 | const xml::ElementNode *pElem = pCurElem;
|
---|
| 928 |
|
---|
| 929 | switch (enmDataId)
|
---|
| 930 | {
|
---|
| 931 | case USERHOMEDIR_ID:
|
---|
| 932 | // if ((pElem = pElem->findChildElement("home")))
|
---|
| 933 | // {
|
---|
| 934 | return createProbableUserHomeDir(pElem);
|
---|
| 935 | // }
|
---|
| 936 | break;
|
---|
| 937 | default:
|
---|
| 938 | break;
|
---|
| 939 | }
|
---|
| 940 |
|
---|
| 941 | return Utf8Str::Empty;
|
---|
| 942 | }
|
---|
| 943 |
|
---|
| 944 | Utf8Str UnattendedSUSEXMLScript::createProbableUserHomeDir(const xml::ElementNode *pCurElem)
|
---|
| 945 | {
|
---|
| 946 | Utf8Str strCalcValue;
|
---|
| 947 | const xml::ElementNode *pElem = pCurElem->findNextSibilingElement("username");
|
---|
| 948 | if (pElem)
|
---|
| 949 | {
|
---|
| 950 | const char *pszValue = pElem->getValue();
|
---|
| 951 | strCalcValue = "/home/";
|
---|
| 952 | strCalcValue.append(pszValue);
|
---|
| 953 | }
|
---|
| 954 |
|
---|
| 955 | return strCalcValue;
|
---|
| 956 | }
|
---|
| 957 | #endif /* just for reference */
|
---|