VirtualBox

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

Last change on this file since 73739 was 73716, checked in by vboxsync, 6 years ago

Main/CloudProviderManager+CloudProvider+CloudProfile: Introduce CloudProfile as separate interface, and do a big cleanup. Adding synchronization and incomplete support for moving to an extension pack. Updated VBoxManage to list providers and touched up the GUI code slightly to deal with the changed interfaces.

  • 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 73716 2018-08-16 15:58:57Z 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#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