VirtualBox

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

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

Main: Squeeze the validation kit and additions onto the aux image when installing windows vista+. Implemented setting up vboxtxs.

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