VirtualBox

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

Last change on this file since 68243 was 68243, checked in by vboxsync, 8 years ago

Unattended: Set hostname (computer name) for windows vista+ installs.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.4 KB
Line 
1/* $Id: UnattendedScript.cpp 68243 2017-08-02 13:20:54Z vboxsync $ */
2/** @file
3 * Implementeation of algorithms which read/parse/save scripts for unattended installation.
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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* Header Files *
20*********************************************************************************************************************************/
21#define LOG_GROUP LOG_GROUP_MAIN_UNATTENDED
22#include "LoggingNew.h"
23#include "VirtualBoxBase.h"
24#include "AutoCaller.h"
25#include <VBox/com/ErrorInfo.h>
26
27#include "MachineImpl.h"
28#include "UnattendedScript.h"
29#include "UnattendedImpl.h"
30
31#include <VBox/err.h>
32
33#include <iprt/ctype.h>
34#include <iprt/file.h>
35#include <iprt/getopt.h>
36#include <iprt/path.h>
37
38using namespace std;
39
40
41//////////////////////////////////////////////////////////////////////////////////////////////////////
42/*
43*
44*
45* Implementation BaseTextScript functions
46*
47*/
48//////////////////////////////////////////////////////////////////////////////////////////////////////
49HRESULT BaseTextScript::read(const Utf8Str &rStrFilename)
50{
51 /*
52 * Open the file for reading and figure it's size. Capping the size
53 * at 16MB so we don't exaust the heap on bad input.
54 */
55 HRESULT hrc;
56 RTVFSFILE hVfsFile;
57 int vrc = RTVfsFileOpenNormal(rStrFilename.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, &hVfsFile);
58 if (RT_SUCCESS(vrc))
59 {
60 hrc = readFromHandle(hVfsFile, rStrFilename.c_str());
61 RTVfsFileRelease(hVfsFile);
62 }
63 else
64 hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("Failed to open '%s' (%Rrc)"), rStrFilename.c_str(), vrc);
65 return hrc;
66}
67
68HRESULT BaseTextScript::readFromHandle(RTVFSFILE hVfsFile, const char *pszFilename)
69{
70 /*
71 * Open the file for reading and figure it's size. Capping the size
72 * at 16MB so we don't exaust the heap on bad input.
73 */
74 HRESULT hrc;
75 uint64_t cbFile = 0;
76 int vrc = RTVfsFileGetSize(hVfsFile, &cbFile);
77 if ( RT_SUCCESS(vrc)
78 && cbFile < _16M)
79 {
80 /*
81 * Exploint the jolt() feature of RTCString and read the content directly into
82 * its storage buffer.
83 */
84 vrc = mStrScriptFullContent.reserveNoThrow((size_t)cbFile + 1);
85 if (RT_SUCCESS(vrc))
86 {
87 char *pszDst = mStrScriptFullContent.mutableRaw();
88 vrc = RTVfsFileReadAt(hVfsFile, 0 /*off*/, pszDst, (size_t)cbFile, NULL);
89 pszDst[(size_t)cbFile] = '\0';
90 if (RT_SUCCESS(vrc))
91 {
92 /*
93 * We must validate the encoding or we'll be subject to potential security trouble.
94 * If this turns out to be problematic, we will need to implement codeset
95 * conversion coping mechanisms.
96 */
97 vrc = RTStrValidateEncodingEx(pszDst, (size_t)cbFile + 1,
98 RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED | RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
99 if (RT_SUCCESS(vrc))
100 {
101 mStrScriptFullContent.jolt();
102 return S_OK;
103 }
104
105 hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("'%s' isn't valid UTF-8: %Rrc"), pszFilename, vrc);
106 }
107 else
108 hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("Error reading '%s': %Rrc"), pszFilename, vrc);
109 mStrScriptFullContent.setNull();
110 }
111 else
112 hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("Failed to allocate memory (%'RU64 bytes) for '%s'"),
113 cbFile, pszFilename);
114 }
115 else if (RT_SUCCESS(vrc))
116 hrc = mpSetError->setErrorVrc(VERR_FILE_TOO_BIG,
117 mpSetError->tr("'%s' is too big (max 16MB): %'RU64"), pszFilename, cbFile);
118 else
119 hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("RTVfsFileGetSize failed (%Rrc)"), vrc);
120 return hrc;
121}
122
123HRESULT BaseTextScript::save(const Utf8Str &rStrFilename, bool fOverwrite)
124{
125 /*
126 * We may have to append the default filename to the
127 */
128 const char *pszFilename = rStrFilename.c_str();
129 Utf8Str strWithDefaultFilename;
130 if ( getDefaultFilename() != NULL
131 && *getDefaultFilename() != '\0'
132 && RTDirExists(rStrFilename.c_str()) )
133 {
134 try
135 {
136 strWithDefaultFilename = rStrFilename;
137 strWithDefaultFilename.append(RTPATH_SLASH);
138 strWithDefaultFilename.append(getDefaultFilename());
139 }
140 catch (std::bad_alloc)
141 {
142 return E_OUTOFMEMORY;
143 }
144 pszFilename = strWithDefaultFilename.c_str();
145 }
146
147 /*
148 * Save the filename for later use.
149 */
150 try
151 {
152 mStrSavedPath = pszFilename;
153 }
154 catch (std::bad_alloc)
155 {
156 return E_OUTOFMEMORY;
157 }
158
159 /*
160 * Use the saveToString method to produce the content.
161 */
162 Utf8Str strDst;
163 HRESULT hrc = saveToString(strDst);
164 if (SUCCEEDED(hrc))
165 {
166 /*
167 * Write the content.
168 */
169 RTFILE hFile;
170 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_ALL;
171 if (fOverwrite)
172 fOpen |= RTFILE_O_CREATE_REPLACE;
173 else
174 fOpen |= RTFILE_O_CREATE;
175 int vrc = RTFileOpen(&hFile, pszFilename, fOpen);
176 if (RT_SUCCESS(vrc))
177 {
178 vrc = RTFileWrite(hFile, strDst.c_str(), strDst.length(), NULL);
179 if (RT_SUCCESS(vrc))
180 {
181 vrc = RTFileClose(hFile);
182 if (RT_SUCCESS(vrc))
183 {
184 LogRelFlow(("GeneralTextScript::save(): saved %zu bytes to '%s'\n", strDst.length(), pszFilename));
185 return S_OK;
186 }
187 }
188 RTFileClose(hFile);
189 RTFileDelete(pszFilename);
190 hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("Error writing to '%s' (%Rrc)"), pszFilename, vrc);
191 }
192 else
193 hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("Error creating/replacing '%s' (%Rrc)"), pszFilename, vrc);
194 }
195 return hrc;
196}
197
198//////////////////////////////////////////////////////////////////////////////////////////////////////
199/*
200*
201*
202* Implementation UnattendedScriptTemplate methods
203*
204*/
205//////////////////////////////////////////////////////////////////////////////////////////////////////
206
207UnattendedScriptTemplate::UnattendedScriptTemplate(Unattended *pUnattended, const char *pszDefaultTemplateFilename,
208 const char *pszDefaultFilename)
209 : BaseTextScript(pUnattended, pszDefaultTemplateFilename, pszDefaultFilename), mpUnattended(pUnattended)
210{
211}
212
213
214HRESULT UnattendedScriptTemplate::saveToString(Utf8Str &rStrDst)
215{
216 static const char s_szPrefix[] = "@@VBOX_";
217 static const char s_szPrefixInsert[] = "@@VBOX_INSERT_";
218 static const char s_szPrefixCond[] = "@@VBOX_COND_";
219 static const char s_szPrefixCondEnd[] = "@@VBOX_COND_END@@";
220
221 struct
222 {
223 bool fSavedOutputting;
224 } aConds[8];
225 unsigned cConds = 0;
226 bool fOutputting = true;
227 HRESULT hrc = E_FAIL;
228 size_t offTemplate = 0;
229 size_t cchTemplate = mStrScriptFullContent.length();
230 rStrDst.setNull();
231 for (;;)
232 {
233 /*
234 * Find the next placeholder and add any text before it to the output.
235 */
236 size_t offPlaceholder = mStrScriptFullContent.find(s_szPrefix, offTemplate);
237 size_t cchToCopy = offPlaceholder != RTCString::npos ? offPlaceholder - offTemplate : cchTemplate - offTemplate;
238 if (cchToCopy > 0)
239 {
240 if (fOutputting)
241 {
242 try
243 {
244 rStrDst.append(mStrScriptFullContent, offTemplate, cchToCopy);
245 }
246 catch (std::bad_alloc)
247 {
248 hrc = E_OUTOFMEMORY;
249 break;
250 }
251 }
252 offTemplate += cchToCopy;
253 }
254
255 /*
256 * Process placeholder.
257 */
258 if (offPlaceholder != RTCString::npos)
259 {
260 /*
261 * First we must find the end of the placeholder string.
262 */
263 const char *pszPlaceholder = mStrScriptFullContent.c_str() + offPlaceholder;
264 size_t cchPlaceholder = sizeof(s_szPrefix) - 1;
265 char ch;
266 while ( offPlaceholder + cchPlaceholder < cchTemplate
267 && (ch = pszPlaceholder[cchPlaceholder]) != '\0'
268 && ( ch == '_'
269 || RT_C_IS_UPPER(ch)
270 || RT_C_IS_DIGIT(ch)) )
271 cchPlaceholder++;
272
273 if ( offPlaceholder + cchPlaceholder < cchTemplate
274 && pszPlaceholder[cchPlaceholder] == '@')
275 {
276 cchPlaceholder++;
277 if ( offPlaceholder + cchPlaceholder < cchTemplate
278 && pszPlaceholder[cchPlaceholder] == '@')
279 cchPlaceholder++;
280 }
281
282 if ( pszPlaceholder[cchPlaceholder - 1] != '@'
283 || pszPlaceholder[cchPlaceholder - 2] != '@'
284 || ( strncmp(pszPlaceholder, s_szPrefixInsert, sizeof(s_szPrefixInsert) - 1) != 0
285 && strncmp(pszPlaceholder, s_szPrefixCond, sizeof(s_szPrefixCond) - 1) != 0) )
286 {
287 hrc = mpSetError->setError(E_FAIL, mpSetError->tr("Malformed template placeholder '%.*s'"),
288 cchPlaceholder, pszPlaceholder);
289 break;
290 }
291
292 offTemplate += cchPlaceholder;
293
294 /*
295 * @@VBOX_INSERT_XXX@@:
296 */
297 if (strncmp(pszPlaceholder, s_szPrefixInsert, sizeof(s_szPrefixInsert) - 1) == 0)
298 {
299 /*
300 * Get the placeholder value and add it to the output.
301 */
302 RTCString strValue;
303 hrc = getReplacement(pszPlaceholder, cchPlaceholder, fOutputting, strValue);
304 if (SUCCEEDED(hrc))
305 {
306 if (fOutputting)
307 {
308 try
309 {
310 rStrDst.append(strValue);
311 }
312 catch (std::bad_alloc)
313 {
314 hrc = E_OUTOFMEMORY;
315 break;
316 }
317 }
318 }
319 else
320 break;
321 }
322 /*
323 * @@VBOX_COND_END@@: Pop one item of the conditional stack.
324 */
325 else if ( cchPlaceholder == sizeof(s_szPrefixCondEnd) - 1U
326 && strncmp(pszPlaceholder, s_szPrefixCondEnd, sizeof(s_szPrefixCondEnd) - 1U) == 0)
327 {
328 if (cConds > 0)
329 {
330 cConds--;
331 fOutputting = aConds[cConds].fSavedOutputting;
332 }
333 else
334 {
335 hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
336 mpSetError->tr("%s without @@VBOX_COND_XXX@@ at offset %zu (%#zx)"),
337 s_szPrefixCondEnd, offPlaceholder, offPlaceholder);
338 break;
339 }
340 }
341 /*
342 * @@VBOX_COND_XXX@@: Push the previous outputting state and combine it with the
343 * one from the condition.
344 */
345 else
346 {
347 Assert(strncmp(pszPlaceholder, s_szPrefixCond, sizeof(s_szPrefixCond) - 1) == 0);
348 if (cConds + 1 < RT_ELEMENTS(aConds))
349 {
350 aConds[cConds].fSavedOutputting = fOutputting;
351 bool fNewOutputting = fOutputting;
352 hrc = getConditional(pszPlaceholder, cchPlaceholder, &fNewOutputting);
353 if (SUCCEEDED(hrc))
354 fOutputting = fOutputting && fNewOutputting;
355 else
356 break;
357 cConds++;
358 }
359 else
360 {
361 hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
362 mpSetError->tr("Too deep conditional nesting at offset %zu (%#zx)"),
363 offPlaceholder, offPlaceholder);
364 break;
365 }
366 }
367 }
368
369 /*
370 * Done?
371 */
372 if (offTemplate >= cchTemplate)
373 {
374 if (cConds == 0)
375 return S_OK;
376 if (cConds == 1)
377 hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, mpSetError->tr("Missing @@VBOX_COND_END@@"));
378 else
379 hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, mpSetError->tr("Missing %u @@VBOX_COND_END@@"), cConds);
380 break;
381 }
382 }
383
384 /* failed */
385 rStrDst.setNull();
386 return hrc;
387}
388
389HRESULT UnattendedScriptTemplate::getReplacement(const char *pachPlaceholder, size_t cchPlaceholder,
390 bool fOutputting, RTCString &rValue)
391{
392 /*
393 * Check for an escaping suffix. Drop the '@@'.
394 */
395 size_t const cchFullPlaceholder = cchPlaceholder;
396 enum
397 {
398 kValueEscaping_None,
399 kValueEscaping_Bourne,
400 kValueEscaping_XML_Element,
401 kValueEscaping_XML_Attribute_Double_Quotes
402 } enmEscaping;
403
404#define PLACEHOLDER_ENDS_WITH(a_szSuffix) \
405 ( cchPlaceholder > sizeof(a_szSuffix) - 1U \
406 && memcmp(&pachPlaceholder[cchPlaceholder - sizeof(a_szSuffix) + 1U], a_szSuffix, sizeof(a_szSuffix) - 1U) == 0)
407 if (PLACEHOLDER_ENDS_WITH("_SH@@"))
408 {
409 cchPlaceholder -= 3 + 2;
410 enmEscaping = kValueEscaping_Bourne;
411 }
412 else if (PLACEHOLDER_ENDS_WITH("_ELEMENT@@"))
413 {
414 cchPlaceholder -= 8 + 2;
415 enmEscaping = kValueEscaping_XML_Element;
416 }
417 else if (PLACEHOLDER_ENDS_WITH("_ATTRIB_DQ@@"))
418 {
419 cchPlaceholder -= 10 + 2;
420 enmEscaping = kValueEscaping_XML_Attribute_Double_Quotes;
421 }
422 else
423 {
424 Assert(PLACEHOLDER_ENDS_WITH("@@"));
425 cchPlaceholder -= 2;
426 enmEscaping = kValueEscaping_None;
427 }
428
429 /*
430 * Resolve and escape the value.
431 */
432 HRESULT hrc;
433 try
434 {
435 switch (enmEscaping)
436 {
437 case kValueEscaping_None:
438 hrc = getUnescapedReplacement(pachPlaceholder, cchPlaceholder, cchFullPlaceholder, fOutputting, rValue);
439 if (SUCCEEDED(hrc))
440 return hrc;
441 break;
442
443 case kValueEscaping_Bourne:
444 case kValueEscaping_XML_Element:
445 case kValueEscaping_XML_Attribute_Double_Quotes:
446 {
447 RTCString strUnescaped;
448 hrc = getUnescapedReplacement(pachPlaceholder, cchPlaceholder, cchFullPlaceholder, fOutputting, strUnescaped);
449 if (SUCCEEDED(hrc))
450 {
451 switch (enmEscaping)
452 {
453 case kValueEscaping_Bourne:
454 {
455 const char * const papszArgs[2] = { strUnescaped.c_str(), NULL };
456 char *pszEscaped = NULL;
457 int vrc = RTGetOptArgvToString(&pszEscaped, papszArgs, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
458 if (RT_SUCCESS(vrc))
459 {
460 try
461 {
462 rValue = pszEscaped;
463 RTStrFree(pszEscaped);
464 return S_OK;
465 }
466 catch (std::bad_alloc)
467 {
468 hrc = E_OUTOFMEMORY;
469 }
470 RTStrFree(pszEscaped);
471 }
472 break;
473 }
474
475 case kValueEscaping_XML_Element:
476 rValue.printf("%RMes", strUnescaped.c_str());
477 return S_OK;
478
479 case kValueEscaping_XML_Attribute_Double_Quotes:
480 {
481 RTCString strTmp;
482 strTmp.printf("%RMas", strUnescaped.c_str());
483 rValue = RTCString(strTmp, 1, strTmp.length() - 2);
484 return S_OK;
485 }
486
487 default:
488 hrc = E_FAIL;
489 break;
490 }
491 }
492 break;
493 }
494
495 default:
496 AssertFailedStmt(hrc = E_FAIL);
497 break;
498 }
499 }
500 catch (std::bad_alloc)
501 {
502 hrc = E_OUTOFMEMORY;
503 }
504 rValue.setNull();
505 return hrc;
506}
507
508HRESULT UnattendedScriptTemplate::getUnescapedReplacement(const char *pachPlaceholder, size_t cchPlaceholder,
509 size_t cchFullPlaceholder, bool fOutputting, RTCString &rValue)
510{
511 RT_NOREF(fOutputting);
512#define IS_PLACEHOLDER_MATCH(a_szMatch) \
513 ( cchPlaceholder == sizeof("@@VBOX_INSERT_" a_szMatch) - 1U \
514 && memcmp(pachPlaceholder, "@@VBOX_INSERT_" a_szMatch, sizeof("@@VBOX_INSERT_" a_szMatch) - 1U) == 0)
515
516 if (IS_PLACEHOLDER_MATCH("USER_LOGIN"))
517 rValue = mpUnattended->i_getUser();
518 else if (IS_PLACEHOLDER_MATCH("USER_PASSWORD"))
519 rValue = mpUnattended->i_getPassword();
520 else if (IS_PLACEHOLDER_MATCH("ROOT_PASSWORD"))
521 rValue = mpUnattended->i_getPassword();
522 else if (IS_PLACEHOLDER_MATCH("USER_FULL_NAME"))
523 rValue = mpUnattended->i_getFullUserName();
524 else if (IS_PLACEHOLDER_MATCH("PRODUCT_KEY"))
525 rValue = mpUnattended->i_getProductKey();
526 else if (IS_PLACEHOLDER_MATCH("POST_INSTALL_COMMAND"))
527 rValue = mpUnattended->i_getPostInstallCommand();
528 else if (IS_PLACEHOLDER_MATCH("IMAGE_INDEX"))
529 rValue.printf("%u", mpUnattended->i_getImageIndex());
530 else if (IS_PLACEHOLDER_MATCH("OS_ARCH"))
531 rValue = mpUnattended->i_isGuestOs64Bit() ? "amd64" : "x86";
532 else if (IS_PLACEHOLDER_MATCH("OS_ARCH2"))
533 rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "x86";
534 else if (IS_PLACEHOLDER_MATCH("OS_ARCH3"))
535 rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i386";
536 else if (IS_PLACEHOLDER_MATCH("OS_ARCH4"))
537 rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i486";
538 else if (IS_PLACEHOLDER_MATCH("OS_ARCH6"))
539 rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i686";
540 else if (IS_PLACEHOLDER_MATCH("TIME_ZONE_UX"))
541 rValue = mpUnattended->i_getTimeZoneInfo()
542 ? mpUnattended->i_getTimeZoneInfo()->pszUnixName : mpUnattended->i_getTimeZone();
543 else if (IS_PLACEHOLDER_MATCH("TIME_ZONE_WIN_NAME"))
544 {
545 PCRTTIMEZONEINFO pInfo = mpUnattended->i_getTimeZoneInfo();
546 if (pInfo)
547 rValue = pInfo->pszWindowsName ? pInfo->pszWindowsName : "GMT";
548 else
549 rValue = mpUnattended->i_getTimeZone();
550 }
551 else if (IS_PLACEHOLDER_MATCH("TIME_ZONE_WIN_INDEX"))
552 {
553 PCRTTIMEZONEINFO pInfo = mpUnattended->i_getTimeZoneInfo();
554 if (pInfo)
555 rValue.printf("%u", pInfo->idxWindows ? pInfo->idxWindows : 85 /*GMT*/);
556 else
557 rValue = mpUnattended->i_getTimeZone();
558 }
559 else if (IS_PLACEHOLDER_MATCH("LOCALE"))
560 rValue = mpUnattended->i_getLocale();
561 else if (IS_PLACEHOLDER_MATCH("DASH_LOCALE"))
562 {
563 rValue = mpUnattended->i_getLocale();
564 Assert(rValue[2] == '_');
565 rValue.replace(2, 1, "-");
566 }
567 else if (IS_PLACEHOLDER_MATCH("LANGUAGE"))
568 rValue = mpUnattended->i_getLanguage();
569 else if (IS_PLACEHOLDER_MATCH("COUNTRY"))
570 rValue = mpUnattended->i_getCountry();
571 else if (IS_PLACEHOLDER_MATCH("HOSTNAME_FQDN"))
572 rValue = mpUnattended->i_getHostname();
573 else if (IS_PLACEHOLDER_MATCH("HOSTNAME_WITHOUT_DOMAIN"))
574 rValue.assign(mpUnattended->i_getHostname(), 0, mpUnattended->i_getHostname().find("."));
575 else if (IS_PLACEHOLDER_MATCH("HOSTNAME_WITHOUT_DOMAIN_MAX_15"))
576 rValue.assign(mpUnattended->i_getHostname(), 0, RT_MIN(mpUnattended->i_getHostname().find("."), 15));
577 else if (IS_PLACEHOLDER_MATCH("HOSTNAME_DOMAIN"))
578 rValue.assign(mpUnattended->i_getHostname(), mpUnattended->i_getHostname().find(".") + 1);
579 else
580 {
581 rValue.setNull();
582 return mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, mpSetError->tr("Unknown template placeholder '%.*s'"),
583 cchFullPlaceholder, pachPlaceholder);
584 }
585 return S_OK;
586#undef IS_PLACEHOLDER_MATCH
587}
588
589HRESULT UnattendedScriptTemplate::getConditional(const char *pachPlaceholder, size_t cchPlaceholder, bool *pfOutputting)
590{
591#define IS_PLACEHOLDER_MATCH(a_szMatch) \
592 ( cchPlaceholder == sizeof("@@VBOX_COND_" a_szMatch "@@") - 1U \
593 && memcmp(pachPlaceholder, "@@VBOX_COND_" a_szMatch "@@", sizeof("@@VBOX_COND_" a_szMatch "@@") - 1U) == 0)
594
595 /* Install guest additions: */
596 if (IS_PLACEHOLDER_MATCH("IS_INSTALLING_ADDITIONS"))
597 *pfOutputting = mpUnattended->i_getInstallGuestAdditions();
598 else if (IS_PLACEHOLDER_MATCH("IS_NOT_INSTALLING_ADDITIONS"))
599 *pfOutputting = !mpUnattended->i_getInstallGuestAdditions();
600 /* User == Administrator: */
601 else if (IS_PLACEHOLDER_MATCH("IS_USER_LOGIN_ADMINISTRATOR"))
602 *pfOutputting = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) == 0;
603 else if (IS_PLACEHOLDER_MATCH("IS_USER_LOGIN_NOT_ADMINISTRATOR"))
604 *pfOutputting = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) != 0;
605 /* Install TXS: */
606 else if (IS_PLACEHOLDER_MATCH("IS_INSTALLING_TEST_EXEC_SERVICE"))
607 *pfOutputting = mpUnattended->i_getInstallTestExecService();
608 else if (IS_PLACEHOLDER_MATCH("IS_NOT_INSTALLING_TEST_EXEC_SERVICE"))
609 *pfOutputting = !mpUnattended->i_getInstallTestExecService();
610 /* Post install command: */
611 else if (IS_PLACEHOLDER_MATCH("HAS_POST_INSTALL_COMMAND"))
612 *pfOutputting = mpUnattended->i_getPostInstallCommand().isNotEmpty();
613 else if (IS_PLACEHOLDER_MATCH("HAS_NO_POST_INSTALL_COMMAND"))
614 *pfOutputting = !mpUnattended->i_getPostInstallCommand().isNotEmpty();
615 /* Minimal installation: */
616 else if (IS_PLACEHOLDER_MATCH("IS_MINIMAL_INSTALLATION"))
617 *pfOutputting = mpUnattended->i_isMinimalInstallation();
618 else if (IS_PLACEHOLDER_MATCH("IS_NOT_MINIMAL_INSTALLATION"))
619 *pfOutputting = !mpUnattended->i_isMinimalInstallation();
620 /* Is RTC using UTC (i.e. set to UTC time on startup): */
621 else if (IS_PLACEHOLDER_MATCH("IS_RTC_USING_UTC"))
622 *pfOutputting = mpUnattended->i_isRtcUsingUtc();
623 else if (IS_PLACEHOLDER_MATCH("IS_NOT_RTC_USING_UTC"))
624 *pfOutputting = !mpUnattended->i_isRtcUsingUtc();
625 else
626 return mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, mpSetError->tr("Unknown conditional placeholder '%.*s'"),
627 cchPlaceholder, pachPlaceholder);
628 return S_OK;
629#undef IS_PLACEHOLDER_MATCH
630}
631
632
633//////////////////////////////////////////////////////////////////////////////////////////////////////
634/*
635*
636*
637* Implementation GeneralTextScript functions
638*
639*/
640//////////////////////////////////////////////////////////////////////////////////////////////////////
641HRESULT GeneralTextScript::parse()
642{
643 AssertReturn(!mfDataParsed, mpSetError->setErrorBoth(E_FAIL, VERR_WRONG_ORDER, "parse called more than once"));
644
645 /*
646 * Split the raw context into an array of lines.
647 */
648 try
649 {
650 mScriptContentByLines = mStrScriptFullContent.split("\n");
651 }
652 catch (std::bad_alloc)
653 {
654 mScriptContentByLines.clear();
655 return E_OUTOFMEMORY;
656 }
657
658 mfDataParsed = true;
659 return S_OK;
660}
661
662HRESULT GeneralTextScript::saveToString(Utf8Str &rStrDst)
663{
664 AssertReturn(mfDataParsed, mpSetError->setErrorBoth(E_FAIL, VERR_WRONG_ORDER, "saveToString() called before parse()"));
665
666 /*
667 * Calc the required size first.
668 */
669 size_t const cLines = mScriptContentByLines.size();
670 size_t cbTotal = 1;
671 for (size_t iLine = 0; iLine < cLines; iLine++)
672 cbTotal = mScriptContentByLines[iLine].length() + 1;
673
674 /*
675 * Clear the output and try reserve sufficient space.
676 */
677 rStrDst.setNull();
678
679 int vrc = rStrDst.reserveNoThrow(cbTotal);
680 if (RT_FAILURE(vrc))
681 return E_OUTOFMEMORY;
682
683 /*
684 * Assemble the output.
685 */
686 for (size_t iLine = 0; iLine < cLines; iLine++)
687 {
688 try
689 {
690 rStrDst.append(mScriptContentByLines[iLine]);
691 rStrDst.append('\n');
692 }
693 catch (std::bad_alloc)
694 {
695 return E_OUTOFMEMORY;
696 }
697 }
698
699 return S_OK;
700}
701
702const RTCString &GeneralTextScript::getContentOfLine(size_t idxLine)
703{
704 if (idxLine < mScriptContentByLines.size())
705 return mScriptContentByLines[idxLine];
706 return Utf8Str::Empty;
707}
708
709
710HRESULT GeneralTextScript::setContentOfLine(size_t idxLine, const Utf8Str &rStrNewLine)
711{
712 AssertReturn(idxLine < mScriptContentByLines.size(),
713 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE, "attempting to set line %zu when there are only %zu lines",
714 idxLine, mScriptContentByLines.size()));
715 try
716 {
717 mScriptContentByLines[idxLine] = rStrNewLine;
718 }
719 catch (std::bad_alloc)
720 {
721 return E_OUTOFMEMORY;
722 }
723 return S_OK;
724}
725
726vector<size_t> GeneralTextScript::findTemplate(const Utf8Str &rStrNeedle,
727 RTCString::CaseSensitivity enmCase /*= RTCString::CaseSensitive*/)
728{
729 vector<size_t> vecHitLineNumbers;
730 size_t const cLines = mScriptContentByLines.size();
731 for (size_t iLine = 0; iLine < cLines; iLine++)
732 if (mScriptContentByLines[iLine].contains(rStrNeedle, enmCase))
733 vecHitLineNumbers.push_back(iLine);
734
735 return vecHitLineNumbers;
736}
737
738HRESULT GeneralTextScript::findAndReplace(size_t idxLine, const Utf8Str &rStrNeedle, const Utf8Str &rStrReplacement)
739{
740 AssertReturn(idxLine < mScriptContentByLines.size(),
741 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE,
742 "attempting search&replace in line %zu when there are only %zu lines",
743 idxLine, mScriptContentByLines.size()));
744
745 RTCString &rDstString = mScriptContentByLines[idxLine];
746 size_t const offNeedle = rDstString.find(&rStrNeedle);
747 if (offNeedle != RTCString::npos)
748 {
749 try
750 {
751 RTCString strBefore(rDstString, 0, offNeedle);
752 RTCString strAfter(rDstString, offNeedle + rStrNeedle.length());
753 rDstString = strBefore;
754 strBefore.setNull();
755 rDstString.append(rStrReplacement);
756 rDstString.append(strAfter);
757 }
758 catch (std::bad_alloc)
759 {
760 return E_OUTOFMEMORY;
761 }
762 }
763 return S_OK;
764}
765
766HRESULT GeneralTextScript::appendToLine(size_t idxLine, const Utf8Str &rStrToAppend)
767{
768 AssertReturn(idxLine < mScriptContentByLines.size(),
769 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE, "appending to line %zu when there are only %zu lines",
770 idxLine, mScriptContentByLines.size()));
771
772 try
773 {
774 mScriptContentByLines[idxLine].append(rStrToAppend);
775 }
776 catch (std::bad_alloc)
777 {
778 return E_OUTOFMEMORY;
779 }
780 return S_OK;
781}
782
783HRESULT GeneralTextScript::prependToLine(size_t idxLine, const Utf8Str &rStrToPrepend)
784{
785 AssertReturn(idxLine < mScriptContentByLines.size(),
786 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE, "prepending to line %zu when there are only %zu lines",
787 idxLine, mScriptContentByLines.size()));
788
789 RTCString &rDstString = mScriptContentByLines[idxLine];
790 try
791 {
792 RTCString strCopy;
793 rDstString.swap(strCopy);
794 rDstString.reserve(strCopy.length() + rStrToPrepend.length() + 1);
795 rDstString = rStrToPrepend;
796 rDstString.append(strCopy);
797 }
798 catch (std::bad_alloc)
799 {
800 return E_OUTOFMEMORY;
801 }
802 return S_OK;
803}
804
805#if 0 /* Keeping this a reference */
806//////////////////////////////////////////////////////////////////////////////////////////////////////
807/*
808*
809*
810* Implementation UnattendedSUSEXMLScript functions
811*
812*/
813/////////////////////////////////////////////////////////////////////////////////////////////////////
814HRESULT UnattendedSUSEXMLScript::parse()
815{
816 HRESULT hrc = UnattendedXMLScript::parse();
817 if (SUCCEEDED(hrc))
818 {
819 /*
820 * Check that we've got the right root element type.
821 */
822 const xml::ElementNode *pelmRoot = mDoc.getRootElement();
823 if ( pelmRoot
824 && strcmp(pelmRoot->getName(), "profile") == 0)
825 {
826 /*
827 * Work thought the sections.
828 */
829 try
830 {
831 LoopThruSections(pelmRoot);
832 hrc = S_OK;
833 }
834 catch (std::bad_alloc)
835 {
836 hrc = E_OUTOFMEMORY;
837 }
838 }
839 else if (pelmRoot)
840 hrc = mpSetError->setError(E_FAIL, mpSetError->tr("XML document root element is '%s' instead of 'profile'"),
841 pelmRoot->getName());
842 else
843 hrc = mpSetError->setError(E_FAIL, mpSetError->tr("Missing XML root element"));
844 }
845 return hrc;
846}
847
848HRESULT UnattendedSUSEXMLScript::setFieldInElement(xml::ElementNode *pElement, const DataId enmDataId, const Utf8Str &rStrValue)
849{
850 /*
851 * Don't set empty values.
852 */
853 if (rStrValue.isEmpty())
854 {
855 Utf8Str strProbableValue;
856 try
857 {
858 strProbableValue = createProbableValue(enmDataId, pElement);
859 }
860 catch (std::bad_alloc)
861 {
862 return E_OUTOFMEMORY;
863 }
864 return UnattendedXMLScript::setFieldInElement(pElement, enmDataId, strProbableValue);
865 }
866 return UnattendedXMLScript::setFieldInElement(pElement, enmDataId, rStrValue);
867}
868
869HRESULT UnattendedSUSEXMLScript::LoopThruSections(const xml::ElementNode *pelmRoot)
870{
871 xml::NodesLoop loopChildren(*pelmRoot);
872 const xml::ElementNode *pelmOuterLoop;
873 while ((pelmOuterLoop = loopChildren.forAllNodes()) != NULL)
874 {
875 const char *pcszElemName = pelmOuterLoop->getName();
876 if (!strcmp(pcszElemName, "users"))
877 {
878 xml::NodesLoop loopUsers(*pelmOuterLoop);
879 const xml::ElementNode *pelmUser;
880 while ((pelmUser = loopUsers.forAllNodes()) != NULL)
881 {
882 HRESULT hrc = HandleUserAccountsSection(pelmUser);
883 if (FAILED(hrc))
884 return hrc;
885 }
886 }
887 }
888 return S_OK;
889}
890
891HRESULT UnattendedSUSEXMLScript::HandleUserAccountsSection(const xml::ElementNode *pelmSection)
892{
893 xml::NodesLoop loopUser(*pelmSection);
894
895 const xml::ElementNode *pelmCur;
896 while ((pelmCur = loopUser.forAllNodes()) != NULL)
897 {
898 const char *pszValue = pelmCur->getValue();
899#ifdef LOG_ENABLED
900 if (!RTStrCmp(pelmCur->getName(), "uid"))
901 LogRelFunc(("UnattendedSUSEXMLScript::HandleUserAccountsSection profile/users/%s/%s = %s\n",
902 pelmSection->getName(), pelmCur->getName(), pszValue));
903#endif
904
905 if (!RTStrCmp(pszValue, "$homedir"))
906 mNodesForCorrectionMap.insert(make_pair(USERHOMEDIR_ID, pelmCur));
907
908 if (!RTStrCmp(pszValue, "$user"))
909 mNodesForCorrectionMap.insert(make_pair(USERNAME_ID, pelmCur));
910
911 if (!RTStrCmp(pszValue, "$password"))
912 mNodesForCorrectionMap.insert(make_pair(USERPASSWORD_ID, pelmCur));
913 }
914 return S_OK;
915}
916
917Utf8Str UnattendedSUSEXMLScript::createProbableValue(const DataId enmDataId, const xml::ElementNode *pCurElem)
918{
919 const xml::ElementNode *pElem = pCurElem;
920
921 switch (enmDataId)
922 {
923 case USERHOMEDIR_ID:
924// if ((pElem = pElem->findChildElement("home")))
925// {
926 return createProbableUserHomeDir(pElem);
927// }
928 break;
929 default:
930 break;
931 }
932
933 return Utf8Str::Empty;
934}
935
936Utf8Str UnattendedSUSEXMLScript::createProbableUserHomeDir(const xml::ElementNode *pCurElem)
937{
938 Utf8Str strCalcValue;
939 const xml::ElementNode *pElem = pCurElem->findNextSibilingElement("username");
940 if (pElem)
941 {
942 const char *pszValue = pElem->getValue();
943 strCalcValue = "/home/";
944 strCalcValue.append(pszValue);
945 }
946
947 return strCalcValue;
948}
949#endif /* just for reference */
950
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