VirtualBox

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

Last change on this file since 71586 was 69238, checked in by vboxsync, 7 years ago

Main: scm updates

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