VirtualBox

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

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

Unattended: Added detectedOSLanguages and language attributes to deal with the windows installer's stupid UILanguage requirement. The specified language must be supported by the installation media, probably by way of lang.ini, which means we need to implement UDF support to really get this right. For now, looking for language specifier in the ISO name (ASSUMES filename unchanged since MSDN download).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.3 KB
Line 
1/* $Id: UnattendedScript.cpp 68239 2017-08-02 12:39:12Z 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.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* 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_DOMAIN"))
576 rValue.assign(mpUnattended->i_getHostname(), mpUnattended->i_getHostname().find(".") + 1);
577 else
578 {
579 rValue.setNull();
580 return mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, mpSetError->tr("Unknown template placeholder '%.*s'"),
581 cchFullPlaceholder, pachPlaceholder);
582 }
583 return S_OK;
584#undef IS_PLACEHOLDER_MATCH
585}
586
587HRESULT UnattendedScriptTemplate::getConditional(const char *pachPlaceholder, size_t cchPlaceholder, bool *pfOutputting)
588{
589#define IS_PLACEHOLDER_MATCH(a_szMatch) \
590 ( cchPlaceholder == sizeof("@@VBOX_COND_" a_szMatch "@@") - 1U \
591 && memcmp(pachPlaceholder, "@@VBOX_COND_" a_szMatch "@@", sizeof("@@VBOX_COND_" a_szMatch "@@") - 1U) == 0)
592
593 /* Install guest additions: */
594 if (IS_PLACEHOLDER_MATCH("IS_INSTALLING_ADDITIONS"))
595 *pfOutputting = mpUnattended->i_getInstallGuestAdditions();
596 else if (IS_PLACEHOLDER_MATCH("IS_NOT_INSTALLING_ADDITIONS"))
597 *pfOutputting = !mpUnattended->i_getInstallGuestAdditions();
598 /* User == Administrator: */
599 else if (IS_PLACEHOLDER_MATCH("IS_USER_LOGIN_ADMINISTRATOR"))
600 *pfOutputting = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) == 0;
601 else if (IS_PLACEHOLDER_MATCH("IS_USER_LOGIN_NOT_ADMINISTRATOR"))
602 *pfOutputting = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) != 0;
603 /* Install TXS: */
604 else if (IS_PLACEHOLDER_MATCH("IS_INSTALLING_TEST_EXEC_SERVICE"))
605 *pfOutputting = mpUnattended->i_getInstallTestExecService();
606 else if (IS_PLACEHOLDER_MATCH("IS_NOT_INSTALLING_TEST_EXEC_SERVICE"))
607 *pfOutputting = !mpUnattended->i_getInstallTestExecService();
608 /* Post install command: */
609 else if (IS_PLACEHOLDER_MATCH("HAS_POST_INSTALL_COMMAND"))
610 *pfOutputting = mpUnattended->i_getPostInstallCommand().isNotEmpty();
611 else if (IS_PLACEHOLDER_MATCH("HAS_NO_POST_INSTALL_COMMAND"))
612 *pfOutputting = !mpUnattended->i_getPostInstallCommand().isNotEmpty();
613 /* Minimal installation: */
614 else if (IS_PLACEHOLDER_MATCH("IS_MINIMAL_INSTALLATION"))
615 *pfOutputting = mpUnattended->i_isMinimalInstallation();
616 else if (IS_PLACEHOLDER_MATCH("IS_NOT_MINIMAL_INSTALLATION"))
617 *pfOutputting = !mpUnattended->i_isMinimalInstallation();
618 /* Is RTC using UTC (i.e. set to UTC time on startup): */
619 else if (IS_PLACEHOLDER_MATCH("IS_RTC_USING_UTC"))
620 *pfOutputting = mpUnattended->i_isRtcUsingUtc();
621 else if (IS_PLACEHOLDER_MATCH("IS_NOT_RTC_USING_UTC"))
622 *pfOutputting = !mpUnattended->i_isRtcUsingUtc();
623 else
624 return mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, mpSetError->tr("Unknown conditional placeholder '%.*s'"),
625 cchPlaceholder, pachPlaceholder);
626 return S_OK;
627#undef IS_PLACEHOLDER_MATCH
628}
629
630
631//////////////////////////////////////////////////////////////////////////////////////////////////////
632/*
633*
634*
635* Implementation GeneralTextScript functions
636*
637*/
638//////////////////////////////////////////////////////////////////////////////////////////////////////
639HRESULT GeneralTextScript::parse()
640{
641 AssertReturn(!mfDataParsed, mpSetError->setErrorBoth(E_FAIL, VERR_WRONG_ORDER, "parse called more than once"));
642
643 /*
644 * Split the raw context into an array of lines.
645 */
646 try
647 {
648 mScriptContentByLines = mStrScriptFullContent.split("\n");
649 }
650 catch (std::bad_alloc)
651 {
652 mScriptContentByLines.clear();
653 return E_OUTOFMEMORY;
654 }
655
656 mfDataParsed = true;
657 return S_OK;
658}
659
660HRESULT GeneralTextScript::saveToString(Utf8Str &rStrDst)
661{
662 AssertReturn(mfDataParsed, mpSetError->setErrorBoth(E_FAIL, VERR_WRONG_ORDER, "saveToString() called before parse()"));
663
664 /*
665 * Calc the required size first.
666 */
667 size_t const cLines = mScriptContentByLines.size();
668 size_t cbTotal = 1;
669 for (size_t iLine = 0; iLine < cLines; iLine++)
670 cbTotal = mScriptContentByLines[iLine].length() + 1;
671
672 /*
673 * Clear the output and try reserve sufficient space.
674 */
675 rStrDst.setNull();
676
677 int vrc = rStrDst.reserveNoThrow(cbTotal);
678 if (RT_FAILURE(vrc))
679 return E_OUTOFMEMORY;
680
681 /*
682 * Assemble the output.
683 */
684 for (size_t iLine = 0; iLine < cLines; iLine++)
685 {
686 try
687 {
688 rStrDst.append(mScriptContentByLines[iLine]);
689 rStrDst.append('\n');
690 }
691 catch (std::bad_alloc)
692 {
693 return E_OUTOFMEMORY;
694 }
695 }
696
697 return S_OK;
698}
699
700const RTCString &GeneralTextScript::getContentOfLine(size_t idxLine)
701{
702 if (idxLine < mScriptContentByLines.size())
703 return mScriptContentByLines[idxLine];
704 return Utf8Str::Empty;
705}
706
707
708HRESULT GeneralTextScript::setContentOfLine(size_t idxLine, const Utf8Str &rStrNewLine)
709{
710 AssertReturn(idxLine < mScriptContentByLines.size(),
711 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE, "attempting to set line %zu when there are only %zu lines",
712 idxLine, mScriptContentByLines.size()));
713 try
714 {
715 mScriptContentByLines[idxLine] = rStrNewLine;
716 }
717 catch (std::bad_alloc)
718 {
719 return E_OUTOFMEMORY;
720 }
721 return S_OK;
722}
723
724vector<size_t> GeneralTextScript::findTemplate(const Utf8Str &rStrNeedle,
725 RTCString::CaseSensitivity enmCase /*= RTCString::CaseSensitive*/)
726{
727 vector<size_t> vecHitLineNumbers;
728 size_t const cLines = mScriptContentByLines.size();
729 for (size_t iLine = 0; iLine < cLines; iLine++)
730 if (mScriptContentByLines[iLine].contains(rStrNeedle, enmCase))
731 vecHitLineNumbers.push_back(iLine);
732
733 return vecHitLineNumbers;
734}
735
736HRESULT GeneralTextScript::findAndReplace(size_t idxLine, const Utf8Str &rStrNeedle, const Utf8Str &rStrReplacement)
737{
738 AssertReturn(idxLine < mScriptContentByLines.size(),
739 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE,
740 "attempting search&replace in line %zu when there are only %zu lines",
741 idxLine, mScriptContentByLines.size()));
742
743 RTCString &rDstString = mScriptContentByLines[idxLine];
744 size_t const offNeedle = rDstString.find(&rStrNeedle);
745 if (offNeedle != RTCString::npos)
746 {
747 try
748 {
749 RTCString strBefore(rDstString, 0, offNeedle);
750 RTCString strAfter(rDstString, offNeedle + rStrNeedle.length());
751 rDstString = strBefore;
752 strBefore.setNull();
753 rDstString.append(rStrReplacement);
754 rDstString.append(strAfter);
755 }
756 catch (std::bad_alloc)
757 {
758 return E_OUTOFMEMORY;
759 }
760 }
761 return S_OK;
762}
763
764HRESULT GeneralTextScript::appendToLine(size_t idxLine, const Utf8Str &rStrToAppend)
765{
766 AssertReturn(idxLine < mScriptContentByLines.size(),
767 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE, "appending to line %zu when there are only %zu lines",
768 idxLine, mScriptContentByLines.size()));
769
770 try
771 {
772 mScriptContentByLines[idxLine].append(rStrToAppend);
773 }
774 catch (std::bad_alloc)
775 {
776 return E_OUTOFMEMORY;
777 }
778 return S_OK;
779}
780
781HRESULT GeneralTextScript::prependToLine(size_t idxLine, const Utf8Str &rStrToPrepend)
782{
783 AssertReturn(idxLine < mScriptContentByLines.size(),
784 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE, "prepending to line %zu when there are only %zu lines",
785 idxLine, mScriptContentByLines.size()));
786
787 RTCString &rDstString = mScriptContentByLines[idxLine];
788 try
789 {
790 RTCString strCopy;
791 rDstString.swap(strCopy);
792 rDstString.reserve(strCopy.length() + rStrToPrepend.length() + 1);
793 rDstString = rStrToPrepend;
794 rDstString.append(strCopy);
795 }
796 catch (std::bad_alloc)
797 {
798 return E_OUTOFMEMORY;
799 }
800 return S_OK;
801}
802
803#if 0 /* Keeping this a reference */
804//////////////////////////////////////////////////////////////////////////////////////////////////////
805/*
806*
807*
808* Implementation UnattendedSUSEXMLScript functions
809*
810*/
811/////////////////////////////////////////////////////////////////////////////////////////////////////
812HRESULT UnattendedSUSEXMLScript::parse()
813{
814 HRESULT hrc = UnattendedXMLScript::parse();
815 if (SUCCEEDED(hrc))
816 {
817 /*
818 * Check that we've got the right root element type.
819 */
820 const xml::ElementNode *pelmRoot = mDoc.getRootElement();
821 if ( pelmRoot
822 && strcmp(pelmRoot->getName(), "profile") == 0)
823 {
824 /*
825 * Work thought the sections.
826 */
827 try
828 {
829 LoopThruSections(pelmRoot);
830 hrc = S_OK;
831 }
832 catch (std::bad_alloc)
833 {
834 hrc = E_OUTOFMEMORY;
835 }
836 }
837 else if (pelmRoot)
838 hrc = mpSetError->setError(E_FAIL, mpSetError->tr("XML document root element is '%s' instead of 'profile'"),
839 pelmRoot->getName());
840 else
841 hrc = mpSetError->setError(E_FAIL, mpSetError->tr("Missing XML root element"));
842 }
843 return hrc;
844}
845
846HRESULT UnattendedSUSEXMLScript::setFieldInElement(xml::ElementNode *pElement, const DataId enmDataId, const Utf8Str &rStrValue)
847{
848 /*
849 * Don't set empty values.
850 */
851 if (rStrValue.isEmpty())
852 {
853 Utf8Str strProbableValue;
854 try
855 {
856 strProbableValue = createProbableValue(enmDataId, pElement);
857 }
858 catch (std::bad_alloc)
859 {
860 return E_OUTOFMEMORY;
861 }
862 return UnattendedXMLScript::setFieldInElement(pElement, enmDataId, strProbableValue);
863 }
864 return UnattendedXMLScript::setFieldInElement(pElement, enmDataId, rStrValue);
865}
866
867HRESULT UnattendedSUSEXMLScript::LoopThruSections(const xml::ElementNode *pelmRoot)
868{
869 xml::NodesLoop loopChildren(*pelmRoot);
870 const xml::ElementNode *pelmOuterLoop;
871 while ((pelmOuterLoop = loopChildren.forAllNodes()) != NULL)
872 {
873 const char *pcszElemName = pelmOuterLoop->getName();
874 if (!strcmp(pcszElemName, "users"))
875 {
876 xml::NodesLoop loopUsers(*pelmOuterLoop);
877 const xml::ElementNode *pelmUser;
878 while ((pelmUser = loopUsers.forAllNodes()) != NULL)
879 {
880 HRESULT hrc = HandleUserAccountsSection(pelmUser);
881 if (FAILED(hrc))
882 return hrc;
883 }
884 }
885 }
886 return S_OK;
887}
888
889HRESULT UnattendedSUSEXMLScript::HandleUserAccountsSection(const xml::ElementNode *pelmSection)
890{
891 xml::NodesLoop loopUser(*pelmSection);
892
893 const xml::ElementNode *pelmCur;
894 while ((pelmCur = loopUser.forAllNodes()) != NULL)
895 {
896 const char *pszValue = pelmCur->getValue();
897#ifdef LOG_ENABLED
898 if (!RTStrCmp(pelmCur->getName(), "uid"))
899 LogRelFunc(("UnattendedSUSEXMLScript::HandleUserAccountsSection profile/users/%s/%s = %s\n",
900 pelmSection->getName(), pelmCur->getName(), pszValue));
901#endif
902
903 if (!RTStrCmp(pszValue, "$homedir"))
904 mNodesForCorrectionMap.insert(make_pair(USERHOMEDIR_ID, pelmCur));
905
906 if (!RTStrCmp(pszValue, "$user"))
907 mNodesForCorrectionMap.insert(make_pair(USERNAME_ID, pelmCur));
908
909 if (!RTStrCmp(pszValue, "$password"))
910 mNodesForCorrectionMap.insert(make_pair(USERPASSWORD_ID, pelmCur));
911 }
912 return S_OK;
913}
914
915Utf8Str UnattendedSUSEXMLScript::createProbableValue(const DataId enmDataId, const xml::ElementNode *pCurElem)
916{
917 const xml::ElementNode *pElem = pCurElem;
918
919 switch (enmDataId)
920 {
921 case USERHOMEDIR_ID:
922// if ((pElem = pElem->findChildElement("home")))
923// {
924 return createProbableUserHomeDir(pElem);
925// }
926 break;
927 default:
928 break;
929 }
930
931 return Utf8Str::Empty;
932}
933
934Utf8Str UnattendedSUSEXMLScript::createProbableUserHomeDir(const xml::ElementNode *pCurElem)
935{
936 Utf8Str strCalcValue;
937 const xml::ElementNode *pElem = pCurElem->findNextSibilingElement("username");
938 if (pElem)
939 {
940 const char *pszValue = pElem->getValue();
941 strCalcValue = "/home/";
942 strCalcValue.append(pszValue);
943 }
944
945 return strCalcValue;
946}
947#endif /* just for reference */
948
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