VirtualBox

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

Last change on this file since 74897 was 73826, checked in by vboxsync, 6 years ago

Main/UnattendedScript: clean up includes, it never needed MachineImpl.h

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

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