VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/UnattendedScript.cpp@ 93121

Last change on this file since 93121 was 93121, checked in by vboxsync, 3 years ago

Main/UnattendedScript: Added two todo that needs looking at. bugref:9781

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.8 KB
Line 
1/* $Id: UnattendedScript.cpp 93121 2022-01-04 02:58:46Z vboxsync $ */
2/** @file
3 * Classes for reading/parsing/saving scripts for unattended installation.
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_MAIN_UNATTENDED
23#include "LoggingNew.h"
24#include "VirtualBoxBase.h"
25#include "AutoCaller.h"
26#include <VBox/com/ErrorInfo.h>
27
28#include "UnattendedScript.h"
29#include "UnattendedImpl.h"
30
31#include <iprt/errcore.h>
32
33#include <iprt/ctype.h>
34#include <iprt/file.h>
35#include <iprt/vfs.h>
36#include <iprt/getopt.h>
37#include <iprt/path.h>
38
39using namespace std;
40
41#ifdef VBOX_WITH_UNATTENDED
42
43
44/*********************************************************************************************************************************
45* UnattendedScriptTemplate Implementation *
46*********************************************************************************************************************************/
47
48UnattendedScriptTemplate::UnattendedScriptTemplate(Unattended *pUnattended, const char *pszDefaultTemplateFilename,
49 const char *pszDefaultFilename)
50 : BaseTextScript(pUnattended, pszDefaultTemplateFilename, pszDefaultFilename), mpUnattended(pUnattended)
51{
52}
53
54
55HRESULT UnattendedScriptTemplate::saveToString(Utf8Str &rStrDst)
56{
57 static const char s_szPrefix[] = "@@VBOX_";
58 static const char s_szPrefixInsert[] = "@@VBOX_INSERT_";
59 static const char s_szPrefixCond[] = "@@VBOX_COND_";
60 static const char s_szPrefixCondElse[] = "@@VBOX_COND_ELSE@@";
61 static const char s_szPrefixCondEnd[] = "@@VBOX_COND_END@@";
62 static const char s_szPrefixSplitter[] = "@@VBOX_SPLITTER";
63
64 struct
65 {
66 bool fSavedOutputting;
67 } aConds[8];
68 unsigned cConds = 0;
69 bool fOutputting = true;
70 HRESULT hrc = E_FAIL;
71 size_t offTemplate = 0;
72 size_t cchTemplate = mStrScriptFullContent.length();
73 rStrDst.setNull();
74 for (;;)
75 {
76 /*
77 * Find the next placeholder and add any text before it to the output.
78 */
79 size_t offPlaceholder = mStrScriptFullContent.find(s_szPrefix, offTemplate);
80 size_t cchToCopy = offPlaceholder != RTCString::npos ? offPlaceholder - offTemplate : cchTemplate - offTemplate;
81 if (cchToCopy > 0)
82 {
83 if (fOutputting)
84 {
85 try
86 {
87 rStrDst.append(mStrScriptFullContent, offTemplate , cchToCopy);
88 }
89 catch (std::bad_alloc &)
90 {
91 hrc = E_OUTOFMEMORY;
92 break;
93 }
94 }
95 offTemplate += cchToCopy;
96 }
97
98 /*
99 * Process placeholder.
100 */
101 if (offPlaceholder != RTCString::npos)
102 {
103 /*
104 * First we must find the end of the placeholder string.
105 */
106 const char *pszPlaceholder = mStrScriptFullContent.c_str() + offPlaceholder;
107 size_t cchPlaceholder = sizeof(s_szPrefix) - 1;
108 char ch;
109 while ( offPlaceholder + cchPlaceholder < cchTemplate
110 && (ch = pszPlaceholder[cchPlaceholder]) != '\0'
111 && ( ch == '_'
112 || ch == '['
113 || ch == ']'
114 || ch == '.'
115 || ch == '>'
116 || ch == '<'
117 || RT_C_IS_UPPER(ch)
118 || RT_C_IS_DIGIT(ch)) )
119 cchPlaceholder++;
120
121 if ( offPlaceholder + cchPlaceholder < cchTemplate
122 && pszPlaceholder[cchPlaceholder] == '@')
123 {
124 cchPlaceholder++;
125 if ( offPlaceholder + cchPlaceholder < cchTemplate
126 && pszPlaceholder[cchPlaceholder] == '@')
127 cchPlaceholder++;
128 }
129
130 if ( pszPlaceholder[cchPlaceholder - 1] != '@'
131 || pszPlaceholder[cchPlaceholder - 2] != '@'
132 || ( strncmp(pszPlaceholder, s_szPrefixInsert, sizeof(s_szPrefixInsert) - 1) != 0
133 && strncmp(pszPlaceholder, s_szPrefixCond, sizeof(s_szPrefixCond) - 1) != 0
134 && strncmp(pszPlaceholder, s_szPrefixSplitter, sizeof(s_szPrefixSplitter) - 1) != 0 ) )
135 {
136 hrc = mpSetError->setError(E_FAIL, tr("Malformed template placeholder '%.*s'"),
137 cchPlaceholder, pszPlaceholder);
138 break;
139 }
140
141 offTemplate += cchPlaceholder;
142
143 /*
144 * @@VBOX_INSERT_XXX@@:
145 */
146 if ( strncmp(pszPlaceholder, s_szPrefixInsert, sizeof(s_szPrefixInsert) - 1) == 0 )
147 {
148 /*
149 * Get the placeholder value and add it to the output.
150 */
151 RTCString strValue;
152 hrc = getReplacement(pszPlaceholder, cchPlaceholder, fOutputting, strValue);
153 if (SUCCEEDED(hrc))
154 {
155 if (fOutputting)
156 {
157 try
158 {
159 rStrDst.append(strValue);
160 }
161 catch (std::bad_alloc &)
162 {
163 hrc = E_OUTOFMEMORY;
164 break;
165 }
166 }
167 }
168 else
169 break;
170 }
171 /*
172 * @@VBOX_COND_END@@: Pop one item of the conditional stack.
173 */
174 else if ( strncmp(pszPlaceholder, s_szPrefixCondEnd, sizeof(s_szPrefixCondEnd) - 1U) == 0 )
175 {
176 if (cConds > 0)
177 {
178 cConds--;
179 fOutputting = aConds[cConds].fSavedOutputting;
180 }
181 else
182 {
183 hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
184 tr("%s without @@VBOX_COND_XXX@@ at offset %zu (%#zx)"),
185 s_szPrefixCondEnd, offPlaceholder, offPlaceholder);
186 break;
187 }
188 }
189 /*
190 * @@VBOX_COND_ELSE@@: Flip the output setting of the current condition.
191 */
192 else if ( strncmp(pszPlaceholder, s_szPrefixCondElse, sizeof(s_szPrefixCondElse) - 1U) == 0 )
193 {
194 if (cConds > 0)
195 fOutputting = !fOutputting;
196 else
197 {
198 hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
199 tr("%s without @@VBOX_COND_XXX@@ at offset %zu (%#zx)"),
200 s_szPrefixCondElse, offPlaceholder, offPlaceholder);
201 break;
202 }
203 }
204 /*
205 * @@VBOX_COND_XXX@@: Push the previous outputting state and combine it with the
206 * one from the condition.
207 */
208 else if (strncmp(pszPlaceholder, s_szPrefixSplitter, sizeof(s_szPrefixSplitter) - 1) != 0)
209 {
210 Assert(strncmp(pszPlaceholder, s_szPrefixCond, sizeof(s_szPrefixCond) - 1) == 0);
211 if (cConds + 1 < RT_ELEMENTS(aConds))
212 {
213 aConds[cConds].fSavedOutputting = fOutputting;
214 bool fNewOutputting = fOutputting;
215 hrc = getConditional(pszPlaceholder, cchPlaceholder, &fNewOutputting);
216 if (SUCCEEDED(hrc))
217 fOutputting = fOutputting && fNewOutputting;
218 else
219 break;
220 cConds++;
221 }
222 else
223 {
224 hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
225 tr("Too deep conditional nesting at offset %zu (%#zx)"),
226 offPlaceholder, offPlaceholder);
227 break;
228 }
229 }
230 /*
231 * @@VBOX_SPLITTER_START/END[filename]@@: Ignored in this pass.
232 */
233 else
234 {
235 if (fOutputting)
236 {
237 try
238 {
239 rStrDst.append(pszPlaceholder, cchPlaceholder);
240 }
241 catch (std::bad_alloc &)
242 {
243 hrc = E_OUTOFMEMORY;
244 break;
245 }
246 }
247 }
248 }
249
250 /*
251 * Done?
252 */
253 if (offTemplate >= cchTemplate)
254 {
255 if (cConds == 0)
256 return S_OK;
257 if (cConds == 1)
258 hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, tr("Missing @@VBOX_COND_END@@"));
259 else
260 hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, tr("Missing %u @@VBOX_COND_END@@"), cConds);
261 break;
262 }
263 }
264
265 /* failed */
266 rStrDst.setNull();
267 return hrc;
268}
269
270HRESULT UnattendedScriptTemplate::getReplacement(const char *pachPlaceholder, size_t cchPlaceholder,
271 bool fOutputting, RTCString &rValue)
272{
273 /*
274 * Check for an escaping suffix. Drop the '@@'.
275 */
276 size_t const cchFullPlaceholder = cchPlaceholder;
277 enum
278 {
279 kValueEscaping_None,
280 kValueEscaping_Bourne,
281 kValueEscaping_XML_Element,
282 kValueEscaping_XML_Attribute_Double_Quotes
283 } enmEscaping;
284
285#define PLACEHOLDER_ENDS_WITH(a_szSuffix) \
286 ( cchPlaceholder > sizeof(a_szSuffix) - 1U \
287 && memcmp(&pachPlaceholder[cchPlaceholder - sizeof(a_szSuffix) + 1U], a_szSuffix, sizeof(a_szSuffix) - 1U) == 0)
288 if (PLACEHOLDER_ENDS_WITH("_SH@@"))
289 {
290 cchPlaceholder -= 3 + 2;
291 enmEscaping = kValueEscaping_Bourne;
292 }
293 else if (PLACEHOLDER_ENDS_WITH("_ELEMENT@@"))
294 {
295 cchPlaceholder -= 8 + 2;
296 enmEscaping = kValueEscaping_XML_Element;
297 }
298 else if (PLACEHOLDER_ENDS_WITH("_ATTRIB_DQ@@"))
299 {
300 cchPlaceholder -= 10 + 2;
301 enmEscaping = kValueEscaping_XML_Attribute_Double_Quotes;
302 }
303 else
304 {
305 Assert(PLACEHOLDER_ENDS_WITH("@@"));
306 cchPlaceholder -= 2;
307 enmEscaping = kValueEscaping_None;
308 }
309
310 /*
311 * Resolve and escape the value.
312 */
313 HRESULT hrc;
314 try
315 {
316 switch (enmEscaping)
317 {
318 case kValueEscaping_None:
319 hrc = getUnescapedReplacement(pachPlaceholder, cchPlaceholder, cchFullPlaceholder, fOutputting, rValue);
320 if (SUCCEEDED(hrc))
321 return hrc;
322 break;
323
324 case kValueEscaping_Bourne:
325 case kValueEscaping_XML_Element:
326 case kValueEscaping_XML_Attribute_Double_Quotes:
327 {
328 RTCString strUnescaped;
329 hrc = getUnescapedReplacement(pachPlaceholder, cchPlaceholder, cchFullPlaceholder, fOutputting, strUnescaped);
330 if (SUCCEEDED(hrc))
331 {
332 switch (enmEscaping)
333 {
334 case kValueEscaping_Bourne:
335 {
336 const char * const papszArgs[2] = { strUnescaped.c_str(), NULL };
337 char *pszEscaped = NULL;
338 int vrc = RTGetOptArgvToString(&pszEscaped, papszArgs, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
339 if (RT_SUCCESS(vrc))
340 {
341 try
342 {
343 rValue = pszEscaped;
344 RTStrFree(pszEscaped);
345 return S_OK;
346 }
347 catch (std::bad_alloc &)
348 {
349 hrc = E_OUTOFMEMORY;
350 }
351 RTStrFree(pszEscaped);
352 }
353 break;
354 }
355
356 case kValueEscaping_XML_Element:
357 rValue.printf("%RMes", strUnescaped.c_str());
358 return S_OK;
359
360 case kValueEscaping_XML_Attribute_Double_Quotes:
361 {
362 RTCString strTmp;
363 strTmp.printf("%RMas", strUnescaped.c_str());
364 rValue = RTCString(strTmp, 1, strTmp.length() - 2);
365 return S_OK;
366 }
367
368 default:
369 hrc = E_FAIL;
370 break;
371 }
372 }
373 break;
374 }
375
376 default:
377 AssertFailedStmt(hrc = E_FAIL);
378 break;
379 }
380 }
381 catch (std::bad_alloc &)
382 {
383 hrc = E_OUTOFMEMORY;
384 }
385 rValue.setNull();
386 return hrc;
387}
388
389HRESULT UnattendedScriptTemplate::getUnescapedReplacement(const char *pachPlaceholder, size_t cchPlaceholder,
390 size_t cchFullPlaceholder, bool fOutputting, RTCString &rValue)
391{
392 RT_NOREF(fOutputting);
393#define IS_PLACEHOLDER_MATCH(a_szMatch) \
394 ( cchPlaceholder == sizeof("@@VBOX_INSERT_" a_szMatch) - 1U \
395 && memcmp(pachPlaceholder, "@@VBOX_INSERT_" a_szMatch, sizeof("@@VBOX_INSERT_" a_szMatch) - 1U) == 0)
396
397 if (IS_PLACEHOLDER_MATCH("USER_LOGIN"))
398 rValue = mpUnattended->i_getUser();
399 else if (IS_PLACEHOLDER_MATCH("USER_PASSWORD"))
400 rValue = mpUnattended->i_getPassword();
401 else if (IS_PLACEHOLDER_MATCH("ROOT_PASSWORD"))
402 rValue = mpUnattended->i_getPassword();
403 else if (IS_PLACEHOLDER_MATCH("USER_FULL_NAME"))
404 rValue = mpUnattended->i_getFullUserName();
405 else if (IS_PLACEHOLDER_MATCH("PRODUCT_KEY"))
406 rValue = mpUnattended->i_getProductKey();
407 else if (IS_PLACEHOLDER_MATCH("POST_INSTALL_COMMAND"))
408 rValue = mpUnattended->i_getPostInstallCommand();
409 else if (IS_PLACEHOLDER_MATCH("AUXILIARY_INSTALL_DIR"))
410 rValue = mpUnattended->i_getAuxiliaryInstallDir();
411 else if (IS_PLACEHOLDER_MATCH("IMAGE_INDEX"))
412 rValue.printf("%u", mpUnattended->i_getImageIndex());
413 else if (IS_PLACEHOLDER_MATCH("OS_ARCH"))
414 rValue = mpUnattended->i_isGuestOs64Bit() ? "amd64" : "x86";
415 else if (IS_PLACEHOLDER_MATCH("OS_ARCH2"))
416 rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "x86";
417 else if (IS_PLACEHOLDER_MATCH("OS_ARCH3"))
418 rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i386";
419 else if (IS_PLACEHOLDER_MATCH("OS_ARCH4"))
420 rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i486";
421 else if (IS_PLACEHOLDER_MATCH("OS_ARCH6"))
422 rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i686";
423 else if (IS_PLACEHOLDER_MATCH("GUEST_OS_VERSION"))
424 rValue = mpUnattended->i_getDetectedOSVersion();
425 else if (IS_PLACEHOLDER_MATCH("GUEST_OS_MAJOR_VERSION"))
426 {
427 Utf8Str strOsVer(mpUnattended->i_getDetectedOSVersion());
428 RTCList<RTCString> partList = strOsVer.split(".");
429 if (partList.size() < 1)
430 {
431 rValue.setNull();
432 return mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, tr("Unknown guest OS major version '%s'"),
433 partList.at(0).c_str());
434 }
435 rValue = partList.at(0);
436 }
437 else if (IS_PLACEHOLDER_MATCH("TIME_ZONE_UX"))
438 rValue = mpUnattended->i_getTimeZoneInfo()
439 ? mpUnattended->i_getTimeZoneInfo()->pszUnixName : mpUnattended->i_getTimeZone();
440 else if (IS_PLACEHOLDER_MATCH("TIME_ZONE_WIN_NAME"))
441 {
442 PCRTTIMEZONEINFO pInfo = mpUnattended->i_getTimeZoneInfo();
443 if (pInfo)
444 rValue = pInfo->pszWindowsName ? pInfo->pszWindowsName : "GMT";
445 else
446 rValue = mpUnattended->i_getTimeZone();
447 }
448 else if (IS_PLACEHOLDER_MATCH("TIME_ZONE_WIN_INDEX"))
449 {
450 PCRTTIMEZONEINFO pInfo = mpUnattended->i_getTimeZoneInfo();
451 if (pInfo)
452 rValue.printf("%u", pInfo->idxWindows ? pInfo->idxWindows : 85 /*GMT*/);
453 else
454 rValue = mpUnattended->i_getTimeZone();
455 }
456 else if (IS_PLACEHOLDER_MATCH("LOCALE"))
457 rValue = mpUnattended->i_getLocale();
458 else if (IS_PLACEHOLDER_MATCH("DASH_LOCALE"))
459 {
460 rValue = mpUnattended->i_getLocale();
461 Assert(rValue[2] == '_');
462 rValue.replace(2, 1, "-");
463 }
464 else if (IS_PLACEHOLDER_MATCH("LANGUAGE"))
465 rValue = mpUnattended->i_getLanguage();
466 else if (IS_PLACEHOLDER_MATCH("COUNTRY"))
467 rValue = mpUnattended->i_getCountry();
468 else if (IS_PLACEHOLDER_MATCH("HOSTNAME_FQDN"))
469 rValue = mpUnattended->i_getHostname();
470 else if (IS_PLACEHOLDER_MATCH("HOSTNAME_WITHOUT_DOMAIN"))
471 rValue.assign(mpUnattended->i_getHostname(), 0, mpUnattended->i_getHostname().find("."));
472 else if (IS_PLACEHOLDER_MATCH("HOSTNAME_WITHOUT_DOMAIN_MAX_15"))
473 rValue.assign(mpUnattended->i_getHostname(), 0, RT_MIN(mpUnattended->i_getHostname().find("."), 15));
474 else if (IS_PLACEHOLDER_MATCH("HOSTNAME_DOMAIN"))
475 rValue.assign(mpUnattended->i_getHostname(), mpUnattended->i_getHostname().find(".") + 1);
476 else if (IS_PLACEHOLDER_MATCH("PROXY"))
477 rValue = mpUnattended->i_getProxy();
478 else
479 {
480 rValue.setNull();
481 return mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, tr("Unknown template placeholder '%.*s'"),
482 cchFullPlaceholder, pachPlaceholder);
483 }
484 return S_OK;
485#undef IS_PLACEHOLDER_MATCH
486}
487
488HRESULT UnattendedScriptTemplate::getConditional(const char *pachPlaceholder, size_t cchPlaceholder, bool *pfOutputting)
489{
490#define IS_PLACEHOLDER_MATCH(a_szMatch) \
491 ( cchPlaceholder == sizeof("@@VBOX_COND_" a_szMatch "@@") - 1U \
492 && memcmp(pachPlaceholder, "@@VBOX_COND_" a_szMatch "@@", sizeof("@@VBOX_COND_" a_szMatch "@@") - 1U) == 0)
493#define IS_PLACEHOLDER_PARTIALLY_MATCH(a_szMatch) \
494 (memcmp(pachPlaceholder, "@@VBOX_COND_" a_szMatch, sizeof("@@VBOX_COND_" a_szMatch) - 1U) == 0)
495
496 /* Install Guest Additions: */
497 if (IS_PLACEHOLDER_MATCH("IS_INSTALLING_ADDITIONS"))
498 *pfOutputting = mpUnattended->i_getInstallGuestAdditions();
499 else if (IS_PLACEHOLDER_MATCH("IS_NOT_INSTALLING_ADDITIONS"))
500 *pfOutputting = !mpUnattended->i_getInstallGuestAdditions();
501 /* User == Administrator: */
502 else if (IS_PLACEHOLDER_MATCH("IS_USER_LOGIN_ADMINISTRATOR"))
503 *pfOutputting = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) == 0;
504 else if (IS_PLACEHOLDER_MATCH("IS_USER_LOGIN_NOT_ADMINISTRATOR"))
505 *pfOutputting = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) != 0;
506 /* Install TXS: */
507 else if (IS_PLACEHOLDER_MATCH("IS_INSTALLING_TEST_EXEC_SERVICE"))
508 *pfOutputting = mpUnattended->i_getInstallTestExecService();
509 else if (IS_PLACEHOLDER_MATCH("IS_NOT_INSTALLING_TEST_EXEC_SERVICE"))
510 *pfOutputting = !mpUnattended->i_getInstallTestExecService();
511 /* Post install command: */
512 else if (IS_PLACEHOLDER_MATCH("HAS_POST_INSTALL_COMMAND"))
513 *pfOutputting = mpUnattended->i_getPostInstallCommand().isNotEmpty();
514 else if (IS_PLACEHOLDER_MATCH("HAS_NO_POST_INSTALL_COMMAND"))
515 *pfOutputting = mpUnattended->i_getPostInstallCommand().isEmpty();
516 /* Product key: */
517 else if (IS_PLACEHOLDER_MATCH("HAS_PRODUCT_KEY"))
518 *pfOutputting = mpUnattended->i_getProductKey().isNotEmpty();
519 else if (IS_PLACEHOLDER_MATCH("HAS_NO_PRODUCT_KEY"))
520 *pfOutputting = mpUnattended->i_getProductKey().isEmpty();
521 /* Minimal installation: */
522 else if (IS_PLACEHOLDER_MATCH("IS_MINIMAL_INSTALLATION"))
523 *pfOutputting = mpUnattended->i_isMinimalInstallation();
524 else if (IS_PLACEHOLDER_MATCH("IS_NOT_MINIMAL_INSTALLATION"))
525 *pfOutputting = !mpUnattended->i_isMinimalInstallation();
526 /* Is firmware UEFI: */
527 else if (IS_PLACEHOLDER_MATCH("IS_FIRMWARE_UEFI"))
528 *pfOutputting = mpUnattended->i_isFirmwareEFI();
529 else if (IS_PLACEHOLDER_MATCH("IS_NOT_FIRMWARE_UEFI"))
530 *pfOutputting = !mpUnattended->i_isFirmwareEFI();
531 /* Is RTC using UTC (i.e. set to UTC time on startup): */
532 else if (IS_PLACEHOLDER_MATCH("IS_RTC_USING_UTC"))
533 *pfOutputting = mpUnattended->i_isRtcUsingUtc();
534 else if (IS_PLACEHOLDER_MATCH("IS_NOT_RTC_USING_UTC"))
535 *pfOutputting = !mpUnattended->i_isRtcUsingUtc();
536 else if (IS_PLACEHOLDER_MATCH("HAS_PROXY"))
537 *pfOutputting = mpUnattended->i_getProxy().isNotEmpty();
538 else if (IS_PLACEHOLDER_PARTIALLY_MATCH("GUEST_VERSION"))
539 {
540 //parse the placeholder and extract the OS version from there
541 RTCString strPlaceHolder(pachPlaceholder); /** @todo r=bird: What's the meaning of duplicating the rest of the script here
542 * when you could just add cchPlaceholder to the parameter list and limit it to
543 * what is actually needed. OTOH it's really not needed to make copies here,
544 * validating the "[" can be done in the partial match above and "]@@" by using
545 * cchPlaceholder, what you wnat to get at is inbetween and does not need
546 * two copies (only one for RTStrVersionCompare). */
547 size_t startPos = sizeof("@@VBOX_COND_GUEST_VERSION") - 1;//-1 is for '\n'
548 size_t endPos = strPlaceHolder.find("@@", startPos + 2);
549 //next part should look like [>8.0.0] for example where:
550 // - "[,]" is just the brackets to wrap up the condition;
551 // - ">" is "greater". Also possible comparison is "<";
552 // - 8.0.0 is required guest OS version.
553 //The end of placeholder is "@@" like for others.
554
555 /** @todo r=bird: What kind of syntax checking is this? Ignore any kind of
556 * mistyped stuff and let the user figure out what he did wrong
557 * without clues? Lazy. */
558 if ( strPlaceHolder[endPos] == '@'
559 && strPlaceHolder[endPos+1] == '@' )
560 {
561 if ( strPlaceHolder[startPos++] == '[' && strPlaceHolder[--endPos] == ']' )
562 {
563 char chComp = strPlaceHolder[startPos++];
564 RTCString strRequiredOSVersion = strPlaceHolder.substr(startPos, endPos - startPos);
565 RTCString strDetectedOSVersion = mpUnattended->i_getDetectedOSVersion();
566 int res = RTStrVersionCompare(strDetectedOSVersion.c_str(), strRequiredOSVersion.c_str());
567 if ( res >= 0 && chComp == '>' )
568 *pfOutputting = true;
569 else if ( res < 0 && chComp == '<' )
570 *pfOutputting = true;
571 else
572 *pfOutputting = false;
573 }
574 }
575 else
576 *pfOutputting = false;//initially is set to "false"
577 }
578 else
579 return mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, tr("Unknown conditional placeholder '%.*s'"),
580 cchPlaceholder, pachPlaceholder);
581 return S_OK;
582#undef IS_PLACEHOLDER_MATCH
583}
584
585#endif /* VBOX_WITH_UNATTENDED */
586#if 0 /* Keeping this a reference */
587
588
589/*********************************************************************************************************************************
590* UnattendedSUSEXMLScript Implementation *
591*********************************************************************************************************************************/
592
593HRESULT UnattendedSUSEXMLScript::parse()
594{
595 HRESULT hrc = UnattendedXMLScript::parse();
596 if (SUCCEEDED(hrc))
597 {
598 /*
599 * Check that we've got the right root element type.
600 */
601 const xml::ElementNode *pelmRoot = mDoc.getRootElement();
602 if ( pelmRoot
603 && strcmp(pelmRoot->getName(), "profile") == 0)
604 {
605 /*
606 * Work thought the sections.
607 */
608 try
609 {
610 LoopThruSections(pelmRoot);
611 hrc = S_OK;
612 }
613 catch (std::bad_alloc &)
614 {
615 hrc = E_OUTOFMEMORY;
616 }
617 }
618 else if (pelmRoot)
619 hrc = mpSetError->setError(E_FAIL, tr("XML document root element is '%s' instead of 'profile'"),
620 pelmRoot->getName());
621 else
622 hrc = mpSetError->setError(E_FAIL, tr("Missing XML root element"));
623 }
624 return hrc;
625}
626
627HRESULT UnattendedSUSEXMLScript::setFieldInElement(xml::ElementNode *pElement, const DataId enmDataId, const Utf8Str &rStrValue)
628{
629 /*
630 * Don't set empty values.
631 */
632 if (rStrValue.isEmpty())
633 {
634 Utf8Str strProbableValue;
635 try
636 {
637 strProbableValue = createProbableValue(enmDataId, pElement);
638 }
639 catch (std::bad_alloc &)
640 {
641 return E_OUTOFMEMORY;
642 }
643 return UnattendedXMLScript::setFieldInElement(pElement, enmDataId, strProbableValue);
644 }
645 return UnattendedXMLScript::setFieldInElement(pElement, enmDataId, rStrValue);
646}
647
648HRESULT UnattendedSUSEXMLScript::LoopThruSections(const xml::ElementNode *pelmRoot)
649{
650 xml::NodesLoop loopChildren(*pelmRoot);
651 const xml::ElementNode *pelmOuterLoop;
652 while ((pelmOuterLoop = loopChildren.forAllNodes()) != NULL)
653 {
654 const char *pcszElemName = pelmOuterLoop->getName();
655 if (!strcmp(pcszElemName, "users"))
656 {
657 xml::NodesLoop loopUsers(*pelmOuterLoop);
658 const xml::ElementNode *pelmUser;
659 while ((pelmUser = loopUsers.forAllNodes()) != NULL)
660 {
661 HRESULT hrc = HandleUserAccountsSection(pelmUser);
662 if (FAILED(hrc))
663 return hrc;
664 }
665 }
666 }
667 return S_OK;
668}
669
670HRESULT UnattendedSUSEXMLScript::HandleUserAccountsSection(const xml::ElementNode *pelmSection)
671{
672 xml::NodesLoop loopUser(*pelmSection);
673
674 const xml::ElementNode *pelmCur;
675 while ((pelmCur = loopUser.forAllNodes()) != NULL)
676 {
677 const char *pszValue = pelmCur->getValue();
678#ifdef LOG_ENABLED
679 if (!RTStrCmp(pelmCur->getName(), "uid"))
680 LogRelFunc(("UnattendedSUSEXMLScript::HandleUserAccountsSection profile/users/%s/%s = %s\n",
681 pelmSection->getName(), pelmCur->getName(), pszValue));
682#endif
683
684 if (!RTStrCmp(pszValue, "$homedir"))
685 mNodesForCorrectionMap.insert(make_pair(USERHOMEDIR_ID, pelmCur));
686
687 if (!RTStrCmp(pszValue, "$user"))
688 mNodesForCorrectionMap.insert(make_pair(USERNAME_ID, pelmCur));
689
690 if (!RTStrCmp(pszValue, "$password"))
691 mNodesForCorrectionMap.insert(make_pair(USERPASSWORD_ID, pelmCur));
692 }
693 return S_OK;
694}
695
696Utf8Str UnattendedSUSEXMLScript::createProbableValue(const DataId enmDataId, const xml::ElementNode *pCurElem)
697{
698 const xml::ElementNode *pElem = pCurElem;
699
700 switch (enmDataId)
701 {
702 case USERHOMEDIR_ID:
703// if ((pElem = pElem->findChildElement("home")))
704// {
705 return createProbableUserHomeDir(pElem);
706// }
707 break;
708 default:
709 break;
710 }
711
712 return Utf8Str::Empty;
713}
714
715Utf8Str UnattendedSUSEXMLScript::createProbableUserHomeDir(const xml::ElementNode *pCurElem)
716{
717 Utf8Str strCalcValue;
718 const xml::ElementNode *pElem = pCurElem->findNextSibilingElement("username");
719 if (pElem)
720 {
721 const char *pszValue = pElem->getValue();
722 strCalcValue = "/home/";
723 strCalcValue.append(pszValue);
724 }
725
726 return strCalcValue;
727}
728#endif /* just for reference */
729
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette