Index: /trunk/src/VBox/Main/include/UnattendedImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/UnattendedImpl.h	(revision 68162)
+++ /trunk/src/VBox/Main/include/UnattendedImpl.h	(revision 68162)
@@ -0,0 +1,233 @@
+/* $Id$ */
+/** @file
+ * Unattended class header
+ */
+
+/*
+ * Copyright (C) 2006-2017 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef ____H_UNATTENDED
+#define ____H_UNATTENDED
+
+#include <VBox/ostypes.h>
+#include <iprt/time.h>
+#include "UnattendedWrap.h"
+
+/* Forward declarations. */
+class UnattendedInstaller;
+struct UnattendedInstallationDisk;
+struct ControllerSlot;
+
+
+/**
+ * Class implementing the IUnattended interface.
+ *
+ * This class is instantiated on the request by IMachine::getUnattended.
+ */
+class ATL_NO_VTABLE Unattended
+    : public UnattendedWrap
+{
+public:
+    DECLARE_EMPTY_CTOR_DTOR(Unattended)
+
+    HRESULT FinalConstruct();
+    void FinalRelease();
+
+    // public initializer/uninitializer for internal purposes only
+    HRESULT initUnattended(VirtualBox *aParent);
+    void uninit();
+
+    // public methods for internal purposes
+    Utf8Str const &i_getIsoPath() const;
+    Utf8Str const &i_getUser() const;
+    Utf8Str const &i_getPassword() const;
+    Utf8Str const &i_getFullUserName() const;
+    Utf8Str const &i_getProductKey() const;
+    Utf8Str const &i_getAdditionsIsoPath() const;
+    bool           i_getInstallGuestAdditions() const;
+    Utf8Str const &i_getValidationKitIsoPath() const;
+    bool           i_getInstallTestExecService() const;
+    Utf8Str const &i_getTimeZone() const;
+    PCRTTIMEZONEINFO i_getTimeZoneInfo() const;
+    Utf8Str const &i_getLocale() const;
+    Utf8Str const &i_getCountry() const;
+    bool           i_isMinimalInstallation() const;
+    Utf8Str const &i_getHostname() const;
+    Utf8Str const &i_getAuxiliaryBasePath() const;
+    ULONG          i_getImageIndex() const;
+    Utf8Str const &i_getScriptTemplatePath() const;
+    Utf8Str const &i_getPostInstallScriptTemplatePath() const;
+    Utf8Str const &i_getPostInstallCommand() const;
+    Utf8Str const &i_getExtraInstallKernelParameters() const;
+
+    bool           i_isGuestOs64Bit() const;
+    VBOXOSTYPE     i_getGuestOsType() const;
+
+
+private:
+    ComPtr<VirtualBox> const mParent;       /**< Strong reference to the parent object (VirtualBox/IMachine). */
+    ComPtr<Machine> mMachine;               /**< Strong reference to the machine object (Machine/IMachine). */
+    Guid            mMachineUuid;           /**< The machine UUID. */
+    RTNATIVETHREAD  mhThreadReconfigureVM;  /**< Set when reconfigureVM is running. */
+    Utf8Str         mStrGuestOsTypeId;      /**< Guest OS type ID (set by prepare). */
+    bool            mfGuestOs64Bit;         /**< 64-bit (true) or 32-bit guest OS (set by prepare). */
+    VBOXOSTYPE      meGuestOsType;          /**< The guest OS type (set by prepare). */
+    UnattendedInstaller *mpInstaller;       /**< The installer instance (set by prepare, deleted by done). */
+
+    /** @name Values of the IUnattended attributes.
+     * @{ */
+    Utf8Str         mStrUser;
+    Utf8Str         mStrPassword;
+    Utf8Str         mStrFullUserName;
+    Utf8Str         mStrProductKey;
+    Utf8Str         mStrIsoPath;
+    Utf8Str         mStrAdditionsIsoPath;
+    bool            mfInstallGuestAdditions;
+    bool            mfInstallTestExecService;
+    Utf8Str         mStrValidationKitIsoPath;
+    Utf8Str         mStrTimeZone;
+    PCRTTIMEZONEINFO mpTimeZoneInfo;
+    Utf8Str         mStrLocale;
+    Utf8Str         mStrCountry;
+    RTCList<RTCString, RTCString *> mPackageSelectionAdjustments;
+    Utf8Str         mStrHostname;
+    Utf8Str         mStrAuxiliaryBasePath;
+    bool            mfIsDefaultAuxiliaryBasePath;
+    ULONG           midxImage;
+    Utf8Str         mStrScriptTemplatePath;
+    Utf8Str         mStrPostInstallScriptTemplatePath;
+    Utf8Str         mStrPostInstallCommand;
+    Utf8Str         mStrExtraInstallKernelParameters;
+    Utf8Str         mStrDetectedOSTypeId;
+    Utf8Str         mStrDetectedOSVersion;
+    Utf8Str         mStrDetectedOSFlavor;
+    Utf8Str         mStrDetectedOSHints;
+    /** @} */
+
+    // wrapped IUnattended functions:
+
+    /**
+     * Checks what mStrIsoPath points to and sets the detectedOS* properties.
+     */
+    HRESULT detectIsoOS();
+
+    /**
+     * Prepare any data, environment, etc.
+     */
+    HRESULT prepare();
+
+    /**
+     * Prepare installation ISO/floppy.
+     */
+    HRESULT constructMedia();
+
+    /**
+     * Prepare a VM to run an unattended installation
+     */
+    HRESULT reconfigureVM();
+
+    /**
+     * Done with all media construction and VM configuration and stuff.
+     */
+    HRESULT done();
+
+    // wrapped IUnattended attributes:
+    HRESULT getIsoPath(com::Utf8Str &isoPath);
+    HRESULT setIsoPath(const com::Utf8Str &isoPath);
+    HRESULT getUser(com::Utf8Str &user);
+    HRESULT setUser(const com::Utf8Str &user);
+    HRESULT getPassword(com::Utf8Str &password);
+    HRESULT setPassword(const com::Utf8Str &password);
+    HRESULT getFullUserName(com::Utf8Str &user);
+    HRESULT setFullUserName(const com::Utf8Str &user);
+    HRESULT getProductKey(com::Utf8Str &productKey);
+    HRESULT setProductKey(const com::Utf8Str &productKey);
+    HRESULT getAdditionsIsoPath(com::Utf8Str &additionsIsoPath);
+    HRESULT setAdditionsIsoPath(const com::Utf8Str &additionsIsoPath);
+    HRESULT getInstallGuestAdditions(BOOL *installGuestAdditions);
+    HRESULT setInstallGuestAdditions(BOOL installGuestAdditions);
+    HRESULT getValidationKitIsoPath(com::Utf8Str &aValidationKitIsoPath);
+    HRESULT setValidationKitIsoPath(const com::Utf8Str &aValidationKitIsoPath);
+    HRESULT getInstallTestExecService(BOOL *aInstallTestExecService);
+    HRESULT setInstallTestExecService(BOOL aInstallTestExecService);
+    HRESULT getTimeZone(com::Utf8Str &aTimezone);
+    HRESULT setTimeZone(const com::Utf8Str &aTimezone);
+    HRESULT getLocale(com::Utf8Str &aLocale);
+    HRESULT setLocale(const com::Utf8Str &aLocale);
+    HRESULT getCountry(com::Utf8Str &aCountry);
+    HRESULT setCountry(const com::Utf8Str &aCountry);
+    HRESULT getProxy(com::Utf8Str &aProxy);
+    HRESULT setProxy(const com::Utf8Str &aProxy);
+    HRESULT getPackageSelectionAdjustments(com::Utf8Str &aPackageSelectionAdjustments);
+    HRESULT setPackageSelectionAdjustments(const com::Utf8Str &aPackageSelectionAdjustments);
+    HRESULT getHostname(com::Utf8Str &aHostname);
+    HRESULT setHostname(const com::Utf8Str &aHostname);
+    HRESULT getAuxiliaryBasePath(com::Utf8Str &aAuxiliaryBasePath);
+    HRESULT setAuxiliaryBasePath(const com::Utf8Str &aAuxiliaryBasePath);
+    HRESULT getImageIndex(ULONG *index);
+    HRESULT setImageIndex(ULONG index);
+    HRESULT getMachine(ComPtr<IMachine> &aMachine);
+    HRESULT setMachine(const ComPtr<IMachine> &aMachine);
+    HRESULT getScriptTemplatePath(com::Utf8Str &aScriptTemplatePath);
+    HRESULT setScriptTemplatePath(const com::Utf8Str &aScriptTemplatePath);
+    HRESULT getPostInstallScriptTemplatePath(com::Utf8Str &aPostInstallScriptTemplatePath);
+    HRESULT setPostInstallScriptTemplatePath(const com::Utf8Str &aPostInstallScriptTemplatePath);
+    HRESULT getPostInstallCommand(com::Utf8Str &aPostInstallCommand);
+    HRESULT setPostInstallCommand(const com::Utf8Str &aPostInstallCommand);
+    HRESULT getExtraInstallKernelParameters(com::Utf8Str &aExtraInstallKernelParameters);
+    HRESULT setExtraInstallKernelParameters(const com::Utf8Str &aExtraInstallKernelParameters);
+    HRESULT getDetectedOSTypeId(com::Utf8Str &aDetectedOSTypeId);
+    HRESULT getDetectedOSVersion(com::Utf8Str &aDetectedOSVersion);
+    HRESULT getDetectedOSFlavor(com::Utf8Str &aDetectedOSFlavor);
+    HRESULT getDetectedOSHints(com::Utf8Str &aDetectedOSHints);
+    //internal functions
+
+    /**
+     * Worker for reconfigureVM.
+     * The caller makes sure to close the session whatever happens.
+     */
+    HRESULT i_innerReconfigureVM(AutoMultiWriteLock2 &rAutoLock, StorageBus_T enmRecommendedStorageBus,
+                                 ComPtr<IMachine> const &rPtrSessionMachine);
+    HRESULT i_reconfigureFloppy(com::SafeIfaceArray<IStorageController> &rControllers,
+                                std::vector<UnattendedInstallationDisk> &rVecInstallatationDisks,
+                                ComPtr<IMachine> const &rPtrSessionMachine,
+                                AutoMultiWriteLock2 &rAutoLock);
+    HRESULT i_reconfigureIsos(com::SafeIfaceArray<IStorageController> &rControllers,
+                              std::vector<UnattendedInstallationDisk> &rVecInstallatationDisks,
+                              ComPtr<IMachine> const &rPtrSessionMachine,
+                              AutoMultiWriteLock2 &rAutoLock, StorageBus_T enmRecommendedStorageBus);
+
+    /**
+     * Adds all free slots on the controller to @a rOutput.
+     */
+    HRESULT i_findOrCreateNeededFreeSlots(const Utf8Str &rStrControllerName, StorageBus_T enmStorageBus,
+                                          ComPtr<IMachine> const &rPtrSessionMachine, uint32_t cSlotsNeeded,
+                                          std::list<ControllerSlot> &rDvdSlots);
+
+    /**
+     * Attach to VM a disk
+     */
+    HRESULT i_attachImage(UnattendedInstallationDisk const *pImage, ComPtr<IMachine> const &rPtrSessionMachine,
+                          AutoMultiWriteLock2 &rLock);
+
+    /*
+     * Wrapper functions
+     */
+
+    /**
+     * Check whether guest is 64bit platform or not
+     */
+    bool i_isGuestOSArchX64(Utf8Str const &rStrGuestOsTypeId);
+};
+
+#endif // !____H_UNATTENDED
+
Index: /trunk/src/VBox/Main/include/UnattendedInstaller.h
===================================================================
--- /trunk/src/VBox/Main/include/UnattendedInstaller.h	(revision 68162)
+++ /trunk/src/VBox/Main/include/UnattendedInstaller.h	(revision 68162)
@@ -0,0 +1,547 @@
+/* $Id$ */
+/** @file
+ * UnattendedInstaller class header
+ */
+
+/*
+ * Copyright (C) 2006-2017 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef ____H_UNATTENDEDINSTALLER
+#define ____H_UNATTENDEDINSTALLER
+
+#include "UnattendedScript.h"
+
+/* Forward declarations */
+class Unattended;
+class UnattendedInstaller;
+class BaseTextScript;
+
+
+/**
+ * The abstract UnattendedInstaller class declaration
+ *
+ * The class is intended to service a new VM that this VM will be able to
+ * execute an unattended installation
+ */
+class UnattendedInstaller : public RTCNonCopyable
+{
+/*data*/
+protected:
+    /** Main unattended installation script. */
+    UnattendedScriptTemplate    mMainScript;
+    /** Full path to the main template file (set by initInstaller). */
+    Utf8Str                     mStrMainScriptTemplate;
+
+    /** Post installation (shell) script. */
+    UnattendedScriptTemplate    mPostScript;
+    /** Full path to the post template file (set by initInstaller). */
+    Utf8Str                     mStrPostScriptTemplate;
+
+    /** Pointer to the parent object.
+     * We use this for setting errors and querying attributes. */
+    Unattended                 *mpParent;
+    /** The path of the extra ISO image we create (set by initInstaller).
+     * This is only valid when isAdditionsIsoNeeded() returns true. */
+    Utf8Str                     mStrAuxiliaryIsoFilePath;
+    /** The path of the extra floppy image we create (set by initInstaller)
+     * This is only valid when isAdditionsFloppyNeeded() returns true. */
+    Utf8Str                     mStrAuxiliaryFloppyFilePath;
+    /** The boot device. */
+    DeviceType_T const          meBootDevice;
+    /** Default extra install kernel parameters (set by constructor).
+     * This can be overridden by the extraInstallKernelParameters attribute of
+     * IUnattended. */
+    Utf8Str                     mStrDefaultExtraInstallKernelParameters;
+
+private:
+    UnattendedInstaller(); /* no default constructors */
+
+public:
+    /**
+     * Regular constructor.
+     *
+     * @param   pParent                     The parent object.  Used for setting
+     *                                      errors and querying attributes.
+     * @param   pszMainScriptTemplateName   The name of the template file (no path)
+     *                                      for the main unattended installer
+     *                                      script.
+     * @param   pszPostScriptTemplateName   The name of the template file (no path)
+     *                                      for the post installation script.
+     * @param   pszMainScriptFilename       The main unattended installer script
+     *                                      filename (on aux media).
+     * @param   pszPostScriptFilename       The post installation script filename
+     *                                      (on aux media).
+     * @param   enmBootDevice               The boot device type.
+     */
+    UnattendedInstaller(Unattended *pParent,
+                        const char *pszMainScriptTemplateName, const char *pszPostScriptTemplateName,
+                        const char *pszMainScriptFilename,     const char *pszPostScriptFilename,
+                        DeviceType_T enmBootDevice = DeviceType_DVD);
+    virtual ~UnattendedInstaller();
+
+    /**
+     * Instantiates the appropriate child class.
+     *
+     * @returns Pointer to the new instance, NULL if no appropriate installer.
+     * @param   enmOsType       The guest OS type value.
+     * @param   strGuestOsType  The guest OS type string
+     * @param   pParent         The parent object.  Used for setting errors and
+     *                          querying attributes.
+     * @throws  std::bad_alloc
+     */
+    static UnattendedInstaller *createInstance(VBOXOSTYPE enmOsType, const Utf8Str &strGuestOsType, Unattended *pParent);
+
+    /**
+     * Initialize the installer.
+     *
+     * @note This is called immediately after instantiation and the caller will
+     *       always destroy the unattended installer instance on failure, so it
+     *       is not necessary to keep track of whether this succeeded or not.
+     */
+    virtual HRESULT initInstaller();
+
+    /**
+     * Whether the VBox guest additions ISO is needed or not.
+     *
+     * The default implementation always returns false when a VISO is used, see
+     * UnattendedInstaller::addFilesToAuxVisoVectors.
+     */
+    virtual bool isAdditionsIsoNeeded() const;
+
+    /**
+     * Whether the VBox validation kit ISO is needed or not.
+     *
+     * The default implementation always returns false when a VISO is used, see
+     * UnattendedInstaller::addFilesToAuxVisoVectors.
+     */
+    virtual bool isValidationKitIsoNeeded() const;
+
+    /**
+     * Indicates whether an original installation ISO is needed or not.
+     */
+    virtual bool isOriginalIsoNeeded() const        { return true; }
+
+    /**
+     * Indicates whether a floppy image is needed or not.
+     */
+    virtual bool isAuxiliaryFloppyNeeded() const    { return false; }
+
+    /**
+     * Indicates whether an additional or replacement ISO image is needed or not.
+     */
+    virtual bool isAuxiliaryIsoNeeded() const       { return false; }
+
+    /**
+     * Indicates whether a the auxiliary ISO is a .viso-file rather than an
+     * .iso-file.
+     *
+     * Different worker methods are used depending on the return value.  A
+     * .viso-file is generally only used when the installation media needs to
+     * be remastered with small changes and additions.
+     */
+    virtual bool isAuxiliaryIsoIsVISO() const       { return false; }
+
+    /*
+     * Getters
+     */
+    DeviceType_T   getBootableDeviceType() const        { return meBootDevice; }
+    const Utf8Str &getTemplateFilePath() const          { return mStrMainScriptTemplate; }
+    const Utf8Str &getPostTemplateFilePath() const      { return mStrPostScriptTemplate; }
+    const Utf8Str &getAuxiliaryIsoFilePath() const      { return mStrAuxiliaryIsoFilePath; }
+    const Utf8Str &getAuxiliaryFloppyFilePath() const   { return mStrAuxiliaryFloppyFilePath; }
+    const Utf8Str &getDefaultExtraInstallKernelParameters() const { return mStrDefaultExtraInstallKernelParameters; }
+
+    /*
+     * Setters
+     */
+    void setTemplatePath(const Utf8Str& data); /**< @todo r=bird: This is confusing as heck. Dir for a while, then it's a file. Not a comment about it. Brilliant. */
+
+    /**
+     * Prepares the unattended scripts, does all but write them to the installation
+     * media.
+     */
+    HRESULT prepareUnattendedScripts();
+
+    /**
+     * Prepares the media - floppy image, ISO image.
+     *
+     * This method calls prepareAuxFloppyImage() and prepareAuxIsoImage(), child
+     * classes may override these methods or methods they call.
+     *
+     * @returns COM status code.
+     * @param   fOverwrite      Whether to overwrite media files or fail if they
+     *                          already exist.
+     */
+    HRESULT prepareMedia(bool fOverwrite = true);
+
+protected:
+    /**
+     * Prepares (creates) the auxiliary floppy image.
+     *
+     * This is called by the base class prepareMedia() when
+     * isAuxiliaryFloppyNeeded() is true. The base class implementation puts the
+     * edited unattended script onto it.
+     */
+    HRESULT prepareAuxFloppyImage(bool fOverwrite);
+
+    /**
+     * Creates and formats (FAT12) a floppy image, then opens a VFS for it.
+     *
+     * @returns COM status code.
+     * @param   pszFilename     The path to the image file.
+     * @param   fOverwrite      Whether to overwrite the file.
+     * @param   phVfs           Where to return a writable VFS handle to the newly
+     *                          created image.
+     */
+    HRESULT newAuxFloppyImage(const char *pszFilename, bool fOverwrite, PRTVFS phVfs);
+
+    /**
+     * Copies files to the auxiliary floppy image.
+     *
+     * The base class implementation copies the main and post scripts to the root of
+     * the floppy using the default script names.  Child classes may override this
+     * to add additional or different files.
+     *
+     * @returns COM status code.
+     * @param   hVfs            The floppy image VFS handle.
+     */
+    virtual HRESULT copyFilesToAuxFloppyImage(RTVFS hVfs);
+
+    /**
+     * Adds the given script to the root of the floppy image under the default
+     * script filename.
+     *
+     * @returns COM status code.
+     * @param   pEditor         The script to add.
+     * @param   hVfs            The VFS to add it to.
+     */
+    HRESULT addScriptToFloppyImage(BaseTextScript *pEditor, RTVFS hVfs);
+
+    /**
+     * Prepares (creates) the auxiliary ISO image.
+     *
+     * This is called by the base class prepareMedia() when isAuxiliaryIsoNeeded()
+     * is true.  The base class implementation puts the edited unattended script
+     * onto it.
+     */
+    virtual HRESULT prepareAuxIsoImage(bool fOverwrite);
+
+    /**
+     * Opens the installation ISO image.
+     *
+     * @returns COM status code.
+     * @param   phVfsIso        Where to return the VFS handle for the ISO.
+     * @param   fFlags          RTFSISO9660_F_XXX flags to pass to the
+     *                          RTFsIso9660VolOpen API.
+     */
+    virtual HRESULT openInstallIsoImage(PRTVFS phVfsIso, uint32_t fFlags = 0);
+
+    /**
+     * Creates and configures the ISO maker instance.
+     *
+     * This can be overridden to set configure options.
+     *
+     * @returns COM status code.
+     * @param   phIsoMaker      Where to return the ISO maker.
+     */
+    virtual HRESULT newAuxIsoImageMaker(PRTFSISOMAKER phIsoMaker);
+
+    /**
+     * Adds files to the auxiliary ISO image maker.
+     *
+     * The base class implementation copies just the mMainScript and mPostScript
+     * files to root directory using the default filenames.
+     *
+     * @returns COM status code.
+     * @param   hIsoMaker       The ISO maker handle.
+     * @param   hVfsOrgIso      The VFS handle to the original ISO in case files
+     *                          needs to be added from it.
+     */
+    virtual HRESULT addFilesToAuxIsoImageMaker(RTFSISOMAKER hIsoMaker, RTVFS hVfsOrgIso);
+
+    /**
+     * Adds the given script to the ISO maker.
+     *
+     * @returns COM status code.
+     * @param   pEditor         The script to add.
+     * @param   hIsoMaker       The ISO maker to add it to.
+     * @param   pszDstFilename  The file name (w/ path) to add it under.  If NULL,
+     *                          the default script filename is used to add it to the
+     *                          root.
+     */
+    HRESULT addScriptToIsoMaker(BaseTextScript *pEditor, RTFSISOMAKER hIsoMaker, const char *pszDstFilename = NULL);
+
+    /**
+     * Writes the ISO image to disk.
+     *
+     * @returns COM status code.
+     * @param   hIsoMaker       The ISO maker handle.
+     * @param   pszFilename     The filename.
+     * @param   fOverwrite      Whether to overwrite the destination file or not.
+     */
+    HRESULT finalizeAuxIsoImage(RTFSISOMAKER hIsoMaker, const char *pszFilename, bool fOverwrite);
+
+    /**
+     * Adds files to the .viso-file vectors.
+     *
+     * The base class implementation adds the script from mAlg, additions ISO
+     * content to '/vboxadditions', and validation kit ISO to '/vboxvalidationkit'.
+     *
+     * @returns COM status code.
+     * @param   rVecArgs        The ISO maker argument list that will be turned into
+     *                          a .viso-file.
+     * @param   rVecFiles       The list of files we've created.  This is for
+     *                          cleaning up at the end.
+     * @param   hVfsOrgIso      The VFS handle to the original ISO in case files
+     *                          needs to be added from it.
+     * @param   fOverwrite      Whether to overwrite files or not.
+     */
+    virtual HRESULT addFilesToAuxVisoVectors(RTCList<RTCString> &rVecArgs, RTCList<RTCString> &rVecFiles,
+                                             RTVFS hVfsOrgIso, bool fOverwrite);
+
+    /**
+     * Saves the given script to disk and adds it to the .viso-file vectors.
+     *
+     * @returns COM status code.
+     * @param   pEditor         The script to add.
+     * @param   rVecArgs        The ISO maker argument list that will be turned into
+     *                          a .viso-file.
+     * @param   rVecFiles       The list of files we've created.  This is for
+     *                          cleaning up at the end.
+     * @param   fOverwrite      Whether to overwrite files or not.
+     */
+    HRESULT addScriptToVisoVectors(BaseTextScript *pEditor, RTCList<RTCString> &rVecArgs,
+                                   RTCList<RTCString> &rVecFiles, bool fOverwrite);
+
+    /**
+     * Writes out the .viso-file to disk.
+     *
+     * @returns COM status code.
+     * @param   rVecArgs        The ISO maker argument list to write out.
+     * @param   pszFilename     The filename.
+     * @param   fOverwrite      Whether to overwrite the destination file or not.
+     */
+    HRESULT finalizeAuxVisoFile(RTCList<RTCString> const &rVecArgs, const char *pszFilename, bool fOverwrite);
+
+    /**
+     * Loads @a pszFilename from @a hVfsOrgIso into @a pEditor and parses it.
+     *
+     * @returns COM status code.
+     * @param   hVfsOrgIso      The handle to the original installation ISO.
+     * @param   pszFilename     The filename to open and load from the ISO.
+     * @param   pEditor         The editor instance to load the file into and
+     *                          do the parseing with.
+     */
+    HRESULT loadAndParseFileFromIso(RTVFS hVfsOrgIso, const char *pszFilename, AbstractScript *pEditor);
+};
+
+
+/**
+ * Windows installer, for versions up to xp 64 / w2k3.
+ */
+class UnattendedWindowsSifInstaller : public UnattendedInstaller
+{
+public:
+    UnattendedWindowsSifInstaller(Unattended *pParent)
+        : UnattendedInstaller(pParent,
+                              "win_nt5_unattended.sif", "win_postinstall.cmd",
+                              "WINNT.SIF",              "vboxpostinstall.cmd")
+    { Assert(isOriginalIsoNeeded()); Assert(isAuxiliaryFloppyNeeded());  Assert(!isAuxiliaryIsoNeeded()); }
+    ~UnattendedWindowsSifInstaller()        {}
+
+    bool isAuxiliaryFloppyNeeded() const    { return true; }
+};
+
+/**
+ * Windows installer, for versions starting with Vista.
+ */
+class UnattendedWindowsXmlInstaller : public UnattendedInstaller
+{
+public:
+    UnattendedWindowsXmlInstaller(Unattended *pParent)
+        : UnattendedInstaller(pParent,
+                              "win_nt6_unattended.xml", "win_postinstall.cmd",
+                              "autounattend.xml",       "vboxpostinstall.cmd")
+    { Assert(isOriginalIsoNeeded()); Assert(isAuxiliaryFloppyNeeded());  Assert(!isAuxiliaryIsoNeeded()); }
+    ~UnattendedWindowsXmlInstaller()      {}
+
+    bool isAuxiliaryFloppyNeeded() const    { return true; }
+};
+
+
+/**
+ * Base class for the unattended linux installers.
+ */
+class UnattendedLinuxInstaller : public UnattendedInstaller
+{
+protected:
+    /** Array of linux parameter patterns that should be removed by editIsoLinuxCfg.
+     * The patterns are proceed by RTStrSimplePatternNMatch. */
+    RTCList<RTCString, RTCString *> mArrStrRemoveInstallKernelParameters;
+
+public:
+    UnattendedLinuxInstaller(Unattended *pParent,
+                             const char *pszMainScriptTemplateName, const char *pszPostScriptTemplateName,
+                             const char *pszMainScriptFilename,     const char *pszPostScriptFilename = "vboxpostinstall.sh")
+        : UnattendedInstaller(pParent,
+                              pszMainScriptTemplateName, pszPostScriptTemplateName,
+                              pszMainScriptFilename,     pszPostScriptFilename) {}
+    ~UnattendedLinuxInstaller() {}
+
+    bool isAuxiliaryIsoNeeded() const       { return true; }
+    bool isAuxiliaryIsoIsVISO() const       { return true; }
+
+protected:
+    /**
+     * Performs basic edits on a isolinux.cfg file.
+     *
+     * @returns COM status code
+     * @param   pEditor         Editor with the isolinux.cfg file loaded and parsed.
+     */
+    virtual HRESULT editIsoLinuxCfg(GeneralTextScript *pEditor);
+};
+
+
+/**
+ * Debian installer.
+ *
+ * This will remaster the orignal ISO and therefore be producing a .viso-file.
+ */
+class UnattendedDebianInstaller : public UnattendedLinuxInstaller
+{
+public:
+    UnattendedDebianInstaller(Unattended *pParent,
+                              const char *pszMainScriptTemplateName = "debian_preseed.cfg",
+                              const char *pszPostScriptTemplateName = "debian_postinstall.sh",
+                              const char *pszMainScriptFilename     = "preseed.cfg")
+        : UnattendedLinuxInstaller(pParent, pszMainScriptTemplateName, pszPostScriptTemplateName, pszMainScriptFilename)
+    {
+        Assert(!isOriginalIsoNeeded()); Assert(isAuxiliaryIsoNeeded());
+        Assert(!isAuxiliaryFloppyNeeded()); Assert(isAuxiliaryIsoIsVISO());
+        mStrDefaultExtraInstallKernelParameters.setNull();
+        mStrDefaultExtraInstallKernelParameters += " auto=true";
+        mStrDefaultExtraInstallKernelParameters.append(" preseed/file=/cdrom/").append(pszMainScriptFilename);
+        mStrDefaultExtraInstallKernelParameters += " priority=critical";
+        mStrDefaultExtraInstallKernelParameters += " quiet";
+        mStrDefaultExtraInstallKernelParameters += " splash";
+        mStrDefaultExtraInstallKernelParameters += " noprompt";  /* no questions about things like CD/DVD ejections */
+        mStrDefaultExtraInstallKernelParameters += " noshell";   /* No shells on VT1-3 (debian, not ubuntu). */
+        mStrDefaultExtraInstallKernelParameters += " automatic-ubiquity";   // ubiquity
+        // the following can probably go into the preseed.cfg:
+        mStrDefaultExtraInstallKernelParameters.append(" debian-installer/locale=").append(pParent->i_getLocale());
+        mStrDefaultExtraInstallKernelParameters += " keyboard-configuration/layoutcode=us";
+        mStrDefaultExtraInstallKernelParameters += " languagechooser/language-name=English"; /** @todo fixme */
+        mStrDefaultExtraInstallKernelParameters.append(" localechooser/supported-locales=").append(pParent->i_getLocale()).append(".UTF-8");
+        mStrDefaultExtraInstallKernelParameters.append(" countrychooser/shortlist=").append(pParent->i_getCountry()); // ubiquity?
+        mStrDefaultExtraInstallKernelParameters += " --";
+    }
+    ~UnattendedDebianInstaller()            {}
+
+    bool isOriginalIsoNeeded() const        { return false; }
+
+protected:
+    HRESULT addFilesToAuxVisoVectors(RTCList<RTCString> &rVecArgs, RTCList<RTCString> &rVecFiles,
+                                     RTVFS hVfsOrgIso, bool fOverwrite);
+    HRESULT editDebianTxtCfg(GeneralTextScript *pEditor);
+
+};
+
+
+/**
+ * Ubuntu installer (same as debian, except for the template).
+ */
+class UnattendedUbuntuInstaller : public UnattendedDebianInstaller
+{
+public:
+    UnattendedUbuntuInstaller(Unattended *pParent)
+        : UnattendedDebianInstaller(pParent, "ubuntu_preseed.cfg")
+    { Assert(!isOriginalIsoNeeded()); Assert(isAuxiliaryIsoNeeded()); Assert(!isAuxiliaryFloppyNeeded()); Assert(isAuxiliaryIsoIsVISO()); }
+    ~UnattendedUbuntuInstaller() {}
+};
+
+
+/**
+ * RedHat 6/7 installer.
+ *
+ * This serves as a base for the kickstart based installers.
+ */
+class UnattendedRedHat67Installer : public UnattendedLinuxInstaller
+{
+public:
+    UnattendedRedHat67Installer(Unattended *pParent,
+                                const char *pszMainScriptTemplateName = "redhat67_ks.cfg",
+                                const char *pszPostScriptTemplateName = "redhat_postinstall.sh",
+                                const char *pszMainScriptFilename     = "ks.cfg")
+          : UnattendedLinuxInstaller(pParent, pszMainScriptTemplateName, pszPostScriptTemplateName, pszMainScriptFilename)
+    {
+        Assert(!isOriginalIsoNeeded()); Assert(isAuxiliaryIsoNeeded());
+        Assert(!isAuxiliaryFloppyNeeded()); Assert(isAuxiliaryIsoIsVISO());
+        mStrDefaultExtraInstallKernelParameters.assign(" ks=cdrom:/").append(pszMainScriptFilename).append(' ');
+        mArrStrRemoveInstallKernelParameters.append("rd.live.check"); /* Disables the checkisomd5 step. Required for VISO. */
+    }
+    ~UnattendedRedHat67Installer() {}
+
+    bool isAuxiliaryIsoIsVISO()             { return true; }
+    bool isOriginalIsoNeeded() const        { return false; }
+
+protected:
+    HRESULT addFilesToAuxVisoVectors(RTCList<RTCString> &rVecArgs, RTCList<RTCString> &rVecFiles,
+                                     RTVFS hVfsOrgIso, bool fOverwrite);
+};
+
+
+/**
+ * Fedora installer (same as RedHat 6/7, except for the template).
+ */
+class UnattendedFedoraInstaller : public UnattendedRedHat67Installer
+{
+public:
+    UnattendedFedoraInstaller(Unattended *pParent)
+        : UnattendedRedHat67Installer(pParent, "fedora_ks.cfg")
+    { Assert(!isOriginalIsoNeeded()); Assert(isAuxiliaryIsoNeeded()); Assert(!isAuxiliaryFloppyNeeded()); Assert(isAuxiliaryIsoIsVISO()); }
+    ~UnattendedFedoraInstaller() {}
+};
+
+
+/**
+ * Oracle Linux installer (same as RedHat 6/7, except for the template).
+ */
+class UnattendedOracleLinuxInstaller : public UnattendedRedHat67Installer
+{
+public:
+    UnattendedOracleLinuxInstaller(Unattended *pParent)
+        : UnattendedRedHat67Installer(pParent, "ol_ks.cfg")
+    { Assert(!isOriginalIsoNeeded()); Assert(isAuxiliaryIsoNeeded()); Assert(!isAuxiliaryFloppyNeeded()); Assert(isAuxiliaryIsoIsVISO()); }
+    ~UnattendedOracleLinuxInstaller() {}
+};
+
+
+#if 0 /* fixme */
+/**
+ * SUSE linux installer.
+ *
+ * @todo needs fixing.
+ */
+class UnattendedSuseInstaller : public UnattendedLinuxInstaller
+{
+public:
+    UnattendedSuseInstaller(BaseTextScript *pAlg, Unattended *pParent)
+        : UnattendedLinuxInstaller(pAlg, pParent, "suse_autoinstall.xml")
+    { Assert(isOriginalIsoNeeded()); Assert(isAuxiliaryIsoNeeded()); Assert(!isAuxiliaryFloppyNeeded()); Assert(!isAuxiliaryIsoIsVISO()); }
+    ~UnattendedSuseInstaller() {}
+
+    HRESULT setupScriptOnAuxiliaryCD(const Utf8Str &path);
+};
+#endif
+
+#endif // !____H_UNATTENDEDINSTALLER
+
Index: /trunk/src/VBox/Main/include/UnattendedScript.h
===================================================================
--- /trunk/src/VBox/Main/include/UnattendedScript.h	(revision 68162)
+++ /trunk/src/VBox/Main/include/UnattendedScript.h	(revision 68162)
@@ -0,0 +1,331 @@
+/* $Id$ */
+/** @file
+ * Implementeation of algorithms which read/parse/save scripts for unattended installation.
+ */
+
+/*
+ * Copyright (C) 2006-2017 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef ____H_UNATTENDEDSCRIPT
+#define ____H_UNATTENDEDSCRIPT
+
+#include "VirtualBoxBase.h"
+#include <iprt/cpp/utils.h>
+
+using namespace xml;
+
+class Unattended;
+
+
+/**
+ * Base class for all the script readers/editors.
+ *
+ * @todo get rid of this bugger
+ */
+class AbstractScript : public RTCNonCopyable
+{
+protected:
+    /** For setting errors.
+     * Yeah, class isn't entirely abstract now.  */
+    VirtualBoxBase *mpSetError;
+
+private: /* no default constructors for children. */
+    AbstractScript() {}
+
+public:
+    AbstractScript(VirtualBoxBase *pSetError) : mpSetError(pSetError) {}
+    virtual ~AbstractScript() {}
+
+    /**
+     * Read a script from a file
+     */
+    virtual HRESULT read(const Utf8Str &rStrFilename) = 0;
+
+    /**
+     * Read a script from a VFS file handle.
+     */
+    virtual HRESULT readFromHandle(RTVFSFILE hVfsFile, const char *pszFilename) = 0;
+
+    /**
+     * Parse the script
+     */
+    virtual HRESULT parse() = 0;
+
+    /**
+     * Save a script to a string.
+     *
+     * This is used by save() and later others to deloy the script.
+     */
+    virtual HRESULT saveToString(Utf8Str &rStrDst) = 0;
+
+    /**
+     * Save a script to a file.
+     * @param   rStrPath        Where to save the script.  This normally points to a
+     *                          file, but in a number of child use cases it's
+     *                          actually giving a directory to put the script in
+     *                          using the default deloyment filename.  One day we
+     *                          might make the caller do this path joining.
+     * @param   fOverwrite      Whether to overwrite the file or not.
+     */
+    virtual HRESULT save(const Utf8Str &rStrPath, bool fOverwrite) = 0;
+
+    /**
+     * Path where an actual script with user's data is located
+     */
+    virtual const Utf8Str &getActualScriptPath() const = 0;
+};
+
+/**
+ * Base class for text based script readers/editors.
+ *
+ * This deals with reading the file into a string data member, writing it back
+ * out to a file, and remember the filenames.
+ */
+class BaseTextScript : public AbstractScript
+{
+protected:
+    const char * const mpszDefaultTemplateFilename; /**< The default template filename.  Can be empty. */
+    const char * const mpszDefaultFilename;         /**< Filename to use when someone calls save() with a directory path.  Can be NULL. */
+    RTCString   mStrScriptFullContent;  /**< Raw text file content.  Produced by read() and typically only used by parse(). */
+    Utf8Str     mStrOriginalPath;       /**< Path where an original script is located (set by read()). */
+    Utf8Str     mStrSavedPath;          /**< Path where an saved script with user's data is located (set by save()). */
+
+public:
+    BaseTextScript(VirtualBoxBase *pSetError, const char *pszDefaultTemplateFilename, const char *pszDefaultFilename)
+        : AbstractScript(pSetError)
+        , mpszDefaultTemplateFilename(pszDefaultTemplateFilename)
+        , mpszDefaultFilename(pszDefaultFilename)
+    { }
+    virtual ~BaseTextScript() {}
+
+    HRESULT read(const Utf8Str &rStrFilename);
+    HRESULT readFromHandle(RTVFSFILE hVfsFile, const char *pszFilename);
+    HRESULT save(const Utf8Str &rStrFilename, bool fOverwrite);
+
+    /**
+     * Gets the default filename for this class of scripts (empty if none).
+     *
+     * @note Just the filename, no path.
+     */
+    const char *getDefaultFilename() const
+    {
+        return mpszDefaultFilename;
+    }
+
+    /**
+     * Gets the default template filename for this class of scripts (empty if none).
+     *
+     * @note Just the filename, no path.
+     */
+    const char *getDefaultTemplateFilename() const
+    {
+        return mpszDefaultTemplateFilename;
+    }
+
+    /**
+     * Path to the file we last saved the script as.
+     */
+    const Utf8Str &getActualScriptPath() const
+    {
+        return mStrSavedPath;
+    }
+
+    /**
+     * Path where an original script is located
+     */
+    const Utf8Str &getOriginalScriptPath() const
+    {
+        return mStrOriginalPath;
+    }
+};
+
+
+/**
+ * Generic unattended text script template editor.
+ *
+ * This just perform variable replacements, no other editing possible.
+ *
+ * Everything happens during saveToString, parse is a noop.
+ */
+class UnattendedScriptTemplate : public BaseTextScript
+{
+protected:
+    /** Where to get the replacement strings from. */
+    Unattended *mpUnattended;
+
+public:
+    UnattendedScriptTemplate(Unattended *pUnattended, const char *pszDefaultTemplateFilename, const char *pszDefaultFilename);
+    virtual ~UnattendedScriptTemplate()             {}
+
+    HRESULT parse()                                 { return S_OK; }
+    HRESULT saveToString(Utf8Str &rStrDst);
+
+protected:
+    /**
+     * Gets the replacement value for the given placeholder.
+     *
+     * @returns COM status code.
+     * @param   pachPlaceholder The placholder string.  Not zero terminated.
+     * @param   cchPlaceholder  The length of the placeholder.
+     * @param   fOutputting     Indicates whether we actually need the correct value
+     *                          or is just syntax checking excluded template parts.
+     * @param   rValue          Where to return the value.
+     */
+    HRESULT getReplacement(const char *pachPlaceholder, size_t cchPlaceholder, bool fOutputting, RTCString &rValue);
+
+    /**
+     * Overridable worker for getReplacement.
+     *
+     * @returns COM status code.
+     * @param   pachPlaceholder     The placholder string.  Not zero terminated.
+     * @param   cchPlaceholder      The length of the placeholder.
+     * @param   cchFullPlaceholder  The full placeholder length, including suffixes
+     *                              indicating how it should be escaped (for error
+     *                              messages).
+     * @param   fOutputting         Indicates whether we actually need the correct
+     *                              value or is just syntax checking excluded
+     *                              template parts.  Intended for voiding triggering
+     *                              sanity checks regarding which replacements
+     *                              should be used and not (e.g. no guest additions
+     *                              path when installing GAs aren't enabled).
+     * @param   rValue              Where to return the value.
+     * @throws  std::bad_alloc
+     */
+    virtual HRESULT getUnescapedReplacement(const char *pachPlaceholder, size_t cchPlaceholder,
+                                            size_t cchFullPlaceholder, bool fOutputting, RTCString &rValue);
+
+
+    /**
+     * Get the result of a conditional.
+     *
+     * @returns COM status code.
+     * @param   pachPlaceholder     The placholder string.  Not zero terminated.
+     * @param   cchPlaceholder      The length of the placeholder.
+     * @param   pfOutputting        Where to return the result of the conditional.
+     *                              This holds the current outputting state on input
+     *                              in case someone want to sanity check anything.
+     */
+    virtual HRESULT getConditional(const char *pachPlaceholder, size_t cchPlaceholder, bool *pfOutputting);
+};
+
+
+/**
+ * Generic line based text script editor.
+ *
+ * This is used for editing isolinux configuratin files among other things.
+ */
+class GeneralTextScript : public BaseTextScript
+{
+protected:
+    RTCList<RTCString>  mScriptContentByLines; /**< Content index by line. This contains the edited version. */
+    bool                mfDataParsed;          /**< Indicates whether the script has been parse() yet.  */
+
+public:
+    GeneralTextScript(VirtualBoxBase *pSetError, const char *pszDefaultTemplateFilename = NULL, const char *pszDefaultFilename = NULL)
+        : BaseTextScript(pSetError, pszDefaultTemplateFilename, pszDefaultFilename), mfDataParsed(false)
+    {}
+    virtual ~GeneralTextScript() {}
+
+    HRESULT parse();
+    HRESULT saveToString(Utf8Str &rStrDst);
+
+    //////////////////New functions//////////////////////////////
+
+    bool isDataParsed() const
+    {
+        return mfDataParsed;
+    }
+
+    /**
+     * Returns the actual size of script in lines
+     */
+    size_t getLineNumbersOfScript() const
+    {
+        return mScriptContentByLines.size();
+    }
+
+    /**
+     * Gets a read-only reference to the given line, returning Utf8Str::Empty if
+     * idxLine is out of range.
+     *
+     * @returns Line string reference or Utf8Str::Empty.
+     * @param   idxLine     The line number.
+     *
+     * @todo    RTCList doesn't allow this method to be const.
+     */
+    RTCString const &getContentOfLine(size_t idxLine);
+
+    /**
+     * Set new content of line
+     */
+    HRESULT setContentOfLine(size_t idxLine, const Utf8Str &newContent);
+
+    /**
+     * Find a substring in the script
+     * Returns a list with the found lines
+     * @throws std::bad_alloc
+     */
+    std::vector<size_t> findTemplate(const Utf8Str &rStrNeedle, RTCString::CaseSensitivity enmCase = RTCString::CaseSensitive);
+
+    /**
+     * In line @a idxLine replace the first occurence of @a rStrNeedle with
+     * @a rStrRelacement.
+     */
+    HRESULT findAndReplace(size_t idxLine, const Utf8Str &rStrNeedle, const Utf8Str &rStrReplacement);
+
+    /**
+     * Append a string into the end of the given line.
+     */
+    HRESULT appendToLine(size_t idxLine, const Utf8Str &rStrToAppend);
+
+    /**
+     * Prepend a string in the beginning of the given line.
+     */
+    HRESULT prependToLine(size_t idxLine, const Utf8Str &rStrToPrepend);
+
+    //////////////////New functions//////////////////////////////
+};
+
+
+#if 0 /* convert when we fix SUSE */
+/**
+ * SUSE unattended XML file editor.
+ */
+class UnattendedSUSEXMLScript : public UnattendedXMLScript
+{
+public:
+    UnattendedSUSEXMLScript(VirtualBoxBase *pSetError, const char *pszDefaultFilename = "autoinst.xml")
+        : UnattendedXMLScript(pSetError, pszDefaultFilename) {}
+    ~UnattendedSUSEXMLScript() {}
+
+    HRESULT parse();
+
+protected:
+    HRESULT setFieldInElement(xml::ElementNode *pElement, const DataId enmDataId, const Utf8Str &rStrValue);
+
+private:
+    //////////////////New functions//////////////////////////////
+    /** @throws std::bad_alloc */
+    HRESULT LoopThruSections(const xml::ElementNode *pelmRoot);
+    /** @throws std::bad_alloc */
+    HRESULT HandleUserAccountsSection(const xml::ElementNode *pelmSection);
+    /** @throws std::bad_alloc */
+    Utf8Str createProbableValue(const DataId enmDataId, const xml::ElementNode *pCurElem);
+    /** @throws std::bad_alloc */
+    Utf8Str createProbableUserHomeDir(const xml::ElementNode *pCurElem);
+    //////////////////New functions//////////////////////////////
+};
+#endif
+
+
+#endif // !____H_UNATTENDEDSCRIPT
+
Index: /trunk/src/VBox/Main/src-server/UnattendedImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/UnattendedImpl.cpp	(revision 68162)
+++ /trunk/src/VBox/Main/src-server/UnattendedImpl.cpp	(revision 68162)
@@ -0,0 +1,1711 @@
+/* $Id$ */
+/** @file
+ * Unattended class implementation
+ */
+
+/*
+ * Copyright (C) 2006-2017 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/*********************************************************************************************************************************
+*   Header Files                                                                                                                 *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_MAIN_UNATTENDED
+#include "LoggingNew.h"
+#include "VirtualBoxBase.h"
+#include "UnattendedImpl.h"
+#include "UnattendedInstaller.h"
+#include "UnattendedScript.h"
+#include "VirtualBoxImpl.h"
+#include "SystemPropertiesImpl.h"
+#include "MachineImpl.h"
+#include "Global.h"
+
+#include <VBox/err.h>
+#include <iprt/ctype.h>
+#include <iprt/file.h>
+#include <iprt/locale.h>
+#include <iprt/path.h>
+
+using namespace std;
+
+
+/*********************************************************************************************************************************
+*   Structures and Typedefs                                                                                                      *
+*********************************************************************************************************************************/
+/**
+ * Controller slot for a DVD drive.
+ *
+ * The slot can be free and needing a drive to be attached along with the ISO
+ * image, or it may already be there and only need mounting the ISO.  The
+ * ControllerSlot::fFree member indicates which it is.
+ */
+struct ControllerSlot
+{
+    StorageBus_T    enmBus;
+    Utf8Str         strControllerName;
+    ULONG           uPort;
+    ULONG           uDevice;
+    bool            fFree;
+
+    ControllerSlot(StorageBus_T a_enmBus, const Utf8Str &a_rName, ULONG a_uPort, ULONG a_uDevice, bool a_fFree)
+        : enmBus(a_enmBus), strControllerName(a_rName), uPort(a_uPort), uDevice(a_uDevice), fFree(a_fFree)
+    {}
+
+    bool operator<(const ControllerSlot &rThat) const
+    {
+        if (enmBus == rThat.enmBus)
+        {
+            if (strControllerName == rThat.strControllerName)
+            {
+                if (uPort == rThat.uPort)
+                    return uDevice < rThat.uDevice;
+                return uPort < rThat.uPort;
+            }
+            return strControllerName < rThat.strControllerName;
+        }
+
+        /*
+         * Bus comparsion in boot priority order.
+         */
+        /* IDE first. */
+        if (enmBus == StorageBus_IDE)
+            return true;
+        if (rThat.enmBus == StorageBus_IDE)
+            return false;
+        /* SATA next */
+        if (enmBus == StorageBus_SATA)
+            return true;
+        if (rThat.enmBus == StorageBus_SATA)
+            return false;
+        /* SCSI next */
+        if (enmBus == StorageBus_SCSI)
+            return true;
+        if (rThat.enmBus == StorageBus_SCSI)
+            return false;
+        /* numerical */
+        return (int)enmBus < (int)rThat.enmBus;
+    }
+
+    bool operator==(const ControllerSlot &rThat) const
+    {
+        return enmBus            == rThat.enmBus
+            && strControllerName == rThat.strControllerName
+            && uPort             == rThat.uPort
+            && uDevice           == rThat.uDevice;
+    }
+};
+
+/**
+ * Installation disk.
+ *
+ * Used when reconfiguring the VM.
+ */
+typedef struct UnattendedInstallationDisk
+{
+    StorageBus_T    enmBusType;         /**< @todo nobody is using this... */
+    Utf8Str         strControllerName;
+    DeviceType_T    enmDeviceType;
+    AccessMode_T    enmAccessType;
+    ULONG           uPort;
+    ULONG           uDevice;
+    bool            fMountOnly;
+    Utf8Str         strImagePath;
+
+    UnattendedInstallationDisk(StorageBus_T a_enmBusType, Utf8Str const &a_rBusName, DeviceType_T a_enmDeviceType,
+                               AccessMode_T a_enmAccessType, ULONG a_uPort, ULONG a_uDevice, bool a_fMountOnly,
+                               Utf8Str const &a_rImagePath)
+        : enmBusType(a_enmBusType), strControllerName(a_rBusName), enmDeviceType(a_enmDeviceType), enmAccessType(a_enmAccessType)
+        , uPort(a_uPort), uDevice(a_uDevice), fMountOnly(a_fMountOnly), strImagePath(a_rImagePath)
+    {
+        Assert(strControllerName.length() > 0);
+    }
+
+    UnattendedInstallationDisk(std::list<ControllerSlot>::const_iterator const &itDvdSlot, Utf8Str const &a_rImagePath)
+        : enmBusType(itDvdSlot->enmBus), strControllerName(itDvdSlot->strControllerName), enmDeviceType(DeviceType_DVD)
+        , enmAccessType(AccessMode_ReadOnly), uPort(itDvdSlot->uPort), uDevice(itDvdSlot->uDevice)
+        , fMountOnly(!itDvdSlot->fFree), strImagePath(a_rImagePath)
+    {
+        Assert(strControllerName.length() > 0);
+    }
+} UnattendedInstallationDisk;
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+*
+*
+*  Implementation Unattended functions
+*
+*/
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+
+Unattended::Unattended()
+    : mhThreadReconfigureVM(NIL_RTNATIVETHREAD), mfGuestOs64Bit(false), mpInstaller(NULL), mpTimeZoneInfo(NULL)
+    , mfIsDefaultAuxiliaryBasePath(true)
+{ }
+
+Unattended::~Unattended()
+{
+    if (mpInstaller)
+    {
+        delete mpInstaller;
+        mpInstaller = NULL;
+    }
+}
+
+HRESULT Unattended::FinalConstruct()
+{
+    return BaseFinalConstruct();
+}
+
+void Unattended::FinalRelease()
+{
+    uninit();
+
+    BaseFinalRelease();
+}
+
+void Unattended::uninit()
+{
+    /* Enclose the state transition Ready->InUninit->NotReady */
+    AutoUninitSpan autoUninitSpan(this);
+    if (autoUninitSpan.uninitDone())
+        return;
+
+    unconst(mParent) = NULL;
+    mMachine.setNull();
+}
+
+/**
+ * Initializes the unattended object.
+ *
+ * @param aParent  Pointer to the parent object.
+ */
+HRESULT Unattended::initUnattended(VirtualBox *aParent)
+{
+    LogFlowThisFunc(("aParent=%p\n", aParent));
+    ComAssertRet(aParent, E_INVALIDARG);
+
+    /* Enclose the state transition NotReady->InInit->Ready */
+    AutoInitSpan autoInitSpan(this);
+    AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+    unconst(mParent) = aParent;
+
+    /*
+     * Fill public attributes (IUnattended) with useful defaults.
+     */
+    try
+    {
+        mStrUser                    = "vboxuser";
+        mStrPassword                = "changeme";
+        mfInstallGuestAdditions     = false;
+        mfInstallTestExecService    = false;
+        midxImage                   = 1;
+
+        HRESULT hrc = mParent->i_getSystemProperties()->i_getDefaultAdditionsISO(mStrAdditionsIsoPath);
+        ComAssertComRCRet(hrc, hrc);
+    }
+    catch (std::bad_alloc)
+    {
+        return E_OUTOFMEMORY;
+    }
+
+    /*
+     * Confirm a successful initialization
+     */
+    autoInitSpan.setSucceeded();
+
+    return S_OK;
+}
+
+HRESULT Unattended::detectIsoOS()
+{
+    return E_NOTIMPL;
+}
+
+HRESULT Unattended::prepare()
+{
+    LogFlow(("Unattended::prepare: enter\n"));
+
+    /*
+     * Must have a machine.
+     */
+    ComPtr<Machine> ptrMachine;
+    Guid            MachineUuid;
+    {
+        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+        ptrMachine = mMachine;
+        if (ptrMachine.isNull())
+            return setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("No machine associated with this IUnatteded instance"));
+        MachineUuid = mMachineUuid;
+    }
+
+    /*
+     * Before we write lock ourselves, we must get stuff from Machine and
+     * VirtualBox because their locks have higher priorities than ours.
+     */
+    Utf8Str strGuestOsTypeId;
+    Utf8Str strMachineName;
+    Utf8Str strDefaultAuxBasePath;
+    HRESULT hrc;
+    try
+    {
+        Bstr bstrTmp;
+        hrc = ptrMachine->COMGETTER(OSTypeId)(bstrTmp.asOutParam());
+        if (SUCCEEDED(hrc))
+        {
+            strGuestOsTypeId = bstrTmp;
+            hrc = mMachine->COMGETTER(Name)(bstrTmp.asOutParam());
+            if (SUCCEEDED(hrc))
+                strMachineName = bstrTmp;
+        }
+        int vrc = ptrMachine->i_calculateFullPath(Utf8StrFmt("Unattended-%RTuuid-", MachineUuid.raw()), strDefaultAuxBasePath);
+        if (RT_FAILURE(vrc))
+            return setErrorBoth(E_FAIL, vrc);
+    }
+    catch (std::bad_alloc)
+    {
+        return E_OUTOFMEMORY;
+    }
+    bool const fIs64Bit = i_isGuestOSArchX64(strGuestOsTypeId);
+
+
+    /*
+     * Write lock this object.
+     */
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    /*
+     * Do some state checks.
+     */
+    if (mpInstaller != NULL)
+        return setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("The prepare method has been called (must call done to restart)"));
+    if ((Machine *)ptrMachine != (Machine *)mMachine)
+        return setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("The 'machine' while we were using it - please don't do that"));
+
+    /*
+     * Check if the specified ISOs and files exist.
+     */
+    if (!RTFileExists(mStrIsoPath.c_str()))
+        return setErrorBoth(E_FAIL, VERR_FILE_NOT_FOUND, tr("Could not locate the installation ISO file '%s'"),
+                            mStrIsoPath.c_str());
+    if (mfInstallGuestAdditions && !RTFileExists(mStrAdditionsIsoPath.c_str()))
+        return setErrorBoth(E_FAIL, VERR_FILE_NOT_FOUND, tr("Could not locate the guest additions ISO file '%s'"),
+                            mStrAdditionsIsoPath.c_str());
+    if (mfInstallTestExecService && !RTFileExists(mStrValidationKitIsoPath.c_str()))
+        return setErrorBoth(E_FAIL, VERR_FILE_NOT_FOUND, tr("Could not locate the validation kit ISO file '%s'"),
+                            mStrValidationKitIsoPath.c_str());
+    if (mStrScriptTemplatePath.isNotEmpty() && !RTFileExists(mStrScriptTemplatePath.c_str()))
+        return setErrorBoth(E_FAIL, VERR_FILE_NOT_FOUND, tr("Could not locate unattended installation script template '%s'"),
+                            mStrScriptTemplatePath.c_str());
+
+    /*
+     * Do some default property stuff and check other properties.
+     */
+    try
+    {
+        char szTmp[128];
+
+        if (mStrLocale.isEmpty())
+        {
+            int vrc = RTLocaleQueryNormalizedBaseLocaleName(szTmp, sizeof(szTmp));
+            if (   RT_SUCCESS(vrc)
+                && RTLOCALE_IS_LANGUAGE2_UNDERSCORE_COUNTRY2(szTmp))
+                mStrLocale.assign(szTmp, 5);
+            else
+                mStrLocale = "en_US";
+            Assert(RTLOCALE_IS_LANGUAGE2_UNDERSCORE_COUNTRY2(mStrLocale));
+        }
+
+        if (mStrCountry.isEmpty())
+        {
+            int vrc = RTLocaleQueryUserCountryCode(szTmp);
+            if (RT_SUCCESS(vrc))
+                mStrCountry = szTmp;
+            else if (   mStrLocale.isNotEmpty()
+                     && RTLOCALE_IS_LANGUAGE2_UNDERSCORE_COUNTRY2(mStrLocale))
+                mStrCountry.assign(mStrLocale, 3, 2);
+            else
+                mStrCountry = "US";
+        }
+
+        if (mStrTimeZone.isEmpty())
+        {
+            int vrc = RTTimeZoneGetCurrent(szTmp, sizeof(szTmp));
+            if (RT_SUCCESS(vrc))
+                mStrTimeZone = szTmp;
+            else
+                mStrTimeZone = "Etc/UTC";
+            Assert(mStrTimeZone.isNotEmpty());
+        }
+        mpTimeZoneInfo = RTTimeZoneGetInfoByUnixName(mStrTimeZone.c_str());
+        if (!mpTimeZoneInfo)
+            mpTimeZoneInfo = RTTimeZoneGetInfoByWindowsName(mStrTimeZone.c_str());
+        Assert(mpTimeZoneInfo || mStrTimeZone != "Etc/UTC");
+        if (!mpTimeZoneInfo)
+            LogRel(("Unattended::prepare: warning: Unknown time zone '%s'\n", mStrTimeZone.c_str()));
+
+        if (mStrHostname.isEmpty())
+        {
+            /* Mangle the VM name into a valid hostname. */
+            for (size_t i = 0; i < strMachineName.length(); i++)
+            {
+                char ch = strMachineName[i];
+                if (   (unsigned)ch < 127
+                    && RT_C_IS_ALNUM(ch))
+                    mStrHostname.append(ch);
+                else if (mStrHostname.isNotEmpty() && RT_C_IS_PUNCT(ch) && !mStrHostname.endsWith("-"))
+                    mStrHostname.append('-');
+            }
+            if (mStrHostname.length() == 0)
+                mStrHostname.printf("%RTuuid-vm", MachineUuid.raw());
+            else if (mStrHostname.length() < 3)
+                mStrHostname.append("-vm");
+            mStrHostname.append(".myguest.virtualbox.org");
+        }
+
+        if (mStrAuxiliaryBasePath.isEmpty())
+        {
+            mStrAuxiliaryBasePath = strDefaultAuxBasePath;
+            mfIsDefaultAuxiliaryBasePath = true;
+        }
+    }
+    catch (std::bad_alloc)
+    {
+        return E_OUTOFMEMORY;
+    }
+
+    /*
+     * Get the guest OS type info and instantiate the appropriate installer.
+     */
+    mStrGuestOsTypeId = strGuestOsTypeId;
+    mfGuestOs64Bit    = fIs64Bit;
+    uint32_t   const idxOSType = Global::getOSTypeIndexFromId(mStrGuestOsTypeId.c_str());
+    meGuestOsType     = idxOSType < Global::cOSTypes ? Global::sOSTypes[idxOSType].osType : VBOXOSTYPE_Unknown;
+
+    mpInstaller = UnattendedInstaller::createInstance(meGuestOsType, mStrGuestOsTypeId, this);
+    if (mpInstaller != NULL)
+    {
+        hrc = mpInstaller->initInstaller();
+        if (SUCCEEDED(hrc))
+        {
+            /*
+             * Do the script preps (just reads them).
+             */
+            hrc = mpInstaller->prepareUnattendedScripts();
+            if (SUCCEEDED(hrc))
+            {
+                LogFlow(("Unattended::prepare: returns S_OK\n"));
+                return S_OK;
+            }
+        }
+
+        /* Destroy the installer instance. */
+        delete mpInstaller;
+        mpInstaller = NULL;
+    }
+    else
+        hrc = setErrorBoth(E_FAIL, VERR_NOT_FOUND,
+                           tr("Unattended installation is not supported for guest type '%s'"), mStrGuestOsTypeId.c_str());
+    LogRelFlow(("Unattended::prepare: failed with %Rhrc\n", hrc));
+    return hrc;
+}
+
+HRESULT Unattended::constructMedia()
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    LogFlow(("===========================================================\n"));
+    LogFlow(("Call Unattended::constructMedia()\n"));
+
+    if (mpInstaller == NULL)
+        return setErrorBoth(E_FAIL, VERR_WRONG_ORDER, "prepare() not yet called");
+
+    return mpInstaller->prepareMedia();
+}
+
+HRESULT Unattended::reconfigureVM()
+{
+    LogFlow(("===========================================================\n"));
+    LogFlow(("Call Unattended::reconfigureVM()\n"));
+
+    /*
+     * Interrogate VirtualBox/IGuestOSType before we lock stuff and create ordering issues.
+     */
+    StorageBus_T enmRecommendedStorageBus = StorageBus_IDE;
+    {
+        Bstr bstrGuestOsTypeId;
+        {
+            AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+            bstrGuestOsTypeId = mStrGuestOsTypeId;
+        }
+        ComPtr<IGuestOSType> ptrGuestOSType;
+        HRESULT hrc = mParent->GetGuestOSType(bstrGuestOsTypeId.raw(), ptrGuestOSType.asOutParam());
+        if (SUCCEEDED(hrc))
+            hrc = ptrGuestOSType->COMGETTER(RecommendedDVDStorageBus)(&enmRecommendedStorageBus);
+        if (FAILED(hrc))
+            return hrc;
+    }
+
+    /*
+     * Take write lock (for lock order reasons, write lock our parent object too)
+     * then make sure we're the only caller of this method.
+     */
+    AutoMultiWriteLock2 alock(mMachine, this COMMA_LOCKVAL_SRC_POS);
+    HRESULT hrc;
+    if (mhThreadReconfigureVM == NIL_RTNATIVETHREAD)
+    {
+        RTNATIVETHREAD const hNativeSelf = RTThreadNativeSelf();
+        mhThreadReconfigureVM = hNativeSelf;
+
+        /*
+         * Create a new session, lock the machine and get the session machine object.
+         * Do the locking without pinning down the write locks, just to be on the safe side.
+         */
+        ComPtr<ISession> ptrSession;
+        try
+        {
+            hrc = ptrSession.createInprocObject(CLSID_Session);
+        }
+        catch (std::bad_alloc)
+        {
+            hrc = E_OUTOFMEMORY;
+        }
+        if (SUCCEEDED(hrc))
+        {
+            alock.release();
+            hrc = mMachine->LockMachine(ptrSession, LockType_Shared);
+            alock.acquire();
+            if (SUCCEEDED(hrc))
+            {
+                ComPtr<IMachine> ptrSessionMachine;
+                hrc = ptrSession->COMGETTER(Machine)(ptrSessionMachine.asOutParam());
+                if (SUCCEEDED(hrc))
+                {
+                    /*
+                     * Hand the session to the inner work and let it do it job.
+                     */
+                    try
+                    {
+                        hrc = i_innerReconfigureVM(alock, enmRecommendedStorageBus, ptrSessionMachine);
+                    }
+                    catch (...)
+                    {
+                        hrc = E_UNEXPECTED;
+                    }
+                }
+
+                /* Paranoia: release early in case we it a bump below.  */
+                Assert(mhThreadReconfigureVM == hNativeSelf);
+                mhThreadReconfigureVM = NIL_RTNATIVETHREAD;
+
+                /*
+                 * While unlocking the machine we'll have to drop the locks again.
+                 */
+                alock.release();
+
+                ptrSessionMachine.setNull();
+                HRESULT hrc2 = ptrSession->UnlockMachine();
+                AssertLogRelMsg(SUCCEEDED(hrc2), ("UnlockMachine -> %Rhrc\n", hrc2));
+
+                ptrSession.setNull();
+
+                alock.acquire();
+            }
+            else
+                mhThreadReconfigureVM = NIL_RTNATIVETHREAD;
+        }
+        else
+            mhThreadReconfigureVM = NIL_RTNATIVETHREAD;
+    }
+    else
+        hrc = setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("reconfigureVM running on other thread"));
+    return hrc;
+}
+
+
+HRESULT Unattended::i_innerReconfigureVM(AutoMultiWriteLock2 &rAutoLock, StorageBus_T enmRecommendedStorageBus,
+                                         ComPtr<IMachine> const &rPtrSessionMachine)
+{
+    if (mpInstaller == NULL)
+        return setErrorBoth(E_FAIL, VERR_WRONG_ORDER, "prepare() not yet called");
+
+    // Fetch all available storage controllers
+    com::SafeIfaceArray<IStorageController> arrayOfControllers;
+    HRESULT hrc = rPtrSessionMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(arrayOfControllers));
+    AssertComRCReturn(hrc, hrc);
+
+    /*
+     * Figure out where the images are to be mounted, adding controllers/ports as needed.
+     */
+    std::vector<UnattendedInstallationDisk> vecInstallationDisks;
+    if (mpInstaller->isAuxiliaryFloppyNeeded())
+    {
+        hrc = i_reconfigureFloppy(arrayOfControllers, vecInstallationDisks, rPtrSessionMachine, rAutoLock);
+        if (FAILED(hrc))
+            return hrc;
+    }
+
+    hrc = i_reconfigureIsos(arrayOfControllers, vecInstallationDisks, rPtrSessionMachine, rAutoLock, enmRecommendedStorageBus);
+    if (FAILED(hrc))
+        return hrc;
+
+    /*
+     * Mount the images.
+     */
+    for (size_t idxImage = 0; idxImage < vecInstallationDisks.size(); idxImage++)
+    {
+        UnattendedInstallationDisk const *pImage = &vecInstallationDisks.at(idxImage);
+        Assert(pImage->strImagePath.isNotEmpty());
+        hrc = i_attachImage(pImage, rPtrSessionMachine, rAutoLock);
+        if (FAILED(hrc))
+            return hrc;
+    }
+
+    /*
+     * Set the boot order.
+     */
+    Assert(   mpInstaller->getBootableDeviceType() == DeviceType_DVD
+           || mpInstaller->getBootableDeviceType() == DeviceType_Floppy);
+    hrc = rPtrSessionMachine->SetBootOrder(1, mpInstaller->getBootableDeviceType());
+    if (SUCCEEDED(hrc))
+        hrc = rPtrSessionMachine->SetBootOrder(2, DeviceType_HardDisk);
+    if (SUCCEEDED(hrc))
+        hrc = rPtrSessionMachine->SetBootOrder(3, mpInstaller->getBootableDeviceType() == DeviceType_DVD
+                                                  ? (DeviceType_T)DeviceType_Floppy : (DeviceType_T)DeviceType_DVD);
+    if (FAILED(hrc))
+        return hrc;
+
+    /*
+     * Essential step.
+     *
+     * HACK ALERT! We have to release the lock here or we'll get into trouble with
+     *             the VirtualBox lock (via i_saveHardware/NetworkAdaptger::i_hasDefaults/VirtualBox::i_findGuestOSType).
+     */
+    if (SUCCEEDED(hrc))
+    {
+        rAutoLock.release();
+        hrc = rPtrSessionMachine->SaveSettings();
+        rAutoLock.acquire();
+    }
+
+    return hrc;
+}
+
+/**
+ * Makes sure we've got a floppy drive attached to a floppy controller, adding
+ * the auxiliary floppy image to the installation disk vector.
+ *
+ * @returns COM status code.
+ * @param   rControllers            The existing controllers.
+ * @param   rVecInstallatationDisks The list of image to mount.
+ * @param   rPtrSessionMachine      The session machine smart pointer.
+ * @param   rAutoLock               The lock.
+ */
+HRESULT Unattended::i_reconfigureFloppy(com::SafeIfaceArray<IStorageController> &rControllers,
+                                        std::vector<UnattendedInstallationDisk> &rVecInstallatationDisks,
+                                        ComPtr<IMachine> const &rPtrSessionMachine,
+                                        AutoMultiWriteLock2 &rAutoLock)
+{
+    Assert(mpInstaller->isAuxiliaryFloppyNeeded());
+
+    /*
+     * Look for a floppy controller with a primary drive (A:) we can "insert"
+     * the auxiliary floppy image.  Add a controller and/or a drive if necessary.
+     */
+    bool    fFoundPort0Dev0 = false;
+    Bstr    bstrControllerName;
+    Utf8Str strControllerName;
+
+    for (size_t i = 0; i < rControllers.size(); ++i)
+    {
+        StorageBus_T enmStorageBus;
+        HRESULT hrc = rControllers[i]->COMGETTER(Bus)(&enmStorageBus);
+        AssertComRCReturn(hrc, hrc);
+        if (enmStorageBus == StorageBus_Floppy)
+        {
+
+            /*
+             * Found a floppy controller.
+             */
+            hrc = rControllers[i]->COMGETTER(Name)(bstrControllerName.asOutParam());
+            AssertComRCReturn(hrc, hrc);
+
+            /*
+             * Check the attchments to see if we've got a device 0 attached on port 0.
+             *
+             * While we're at it we eject flppies from all floppy drives we encounter,
+             * we don't want any confusion at boot or during installation.
+             */
+            com::SafeIfaceArray<IMediumAttachment> arrayOfMediumAttachments;
+            hrc = rPtrSessionMachine->GetMediumAttachmentsOfController(bstrControllerName.raw(),
+                                                                       ComSafeArrayAsOutParam(arrayOfMediumAttachments));
+            AssertComRCReturn(hrc, hrc);
+            strControllerName = bstrControllerName;
+            AssertLogRelReturn(strControllerName.isNotEmpty(), setErrorBoth(E_UNEXPECTED, VERR_INTERNAL_ERROR_2));
+
+            for (size_t j = 0; j < arrayOfMediumAttachments.size(); j++)
+            {
+                LONG iPort = -1;
+                hrc = arrayOfMediumAttachments[j]->COMGETTER(Port)(&iPort);
+                AssertComRCReturn(hrc, hrc);
+
+                LONG iDevice = -1;
+                hrc = arrayOfMediumAttachments[j]->COMGETTER(Device)(&iDevice);
+                AssertComRCReturn(hrc, hrc);
+
+                DeviceType_T enmType;
+                hrc = arrayOfMediumAttachments[j]->COMGETTER(Type)(&enmType);
+                AssertComRCReturn(hrc, hrc);
+
+                if (enmType == DeviceType_Floppy)
+                {
+                    ComPtr<IMedium> ptrMedium;
+                    hrc = arrayOfMediumAttachments[j]->COMGETTER(Medium)(ptrMedium.asOutParam());
+                    AssertComRCReturn(hrc, hrc);
+
+                    if (ptrMedium.isNotNull())
+                    {
+                        ptrMedium.setNull();
+                        rAutoLock.release();
+                        hrc = rPtrSessionMachine->UnmountMedium(bstrControllerName.raw(), iPort, iDevice, TRUE /*fForce*/);
+                        rAutoLock.acquire();
+                    }
+
+                    if (iPort == 0 && iDevice == 0)
+                        fFoundPort0Dev0 = true;
+                }
+                else if (iPort == 0 && iDevice == 0)
+                    return setError(E_FAIL,
+                                    tr("Found non-floppy device attached to port 0 device 0 on the floppy controller '%ls'"),
+                                    bstrControllerName.raw());
+            }
+        }
+    }
+
+    /*
+     * Add a floppy controller if we need to.
+     */
+    if (strControllerName.isEmpty())
+    {
+        bstrControllerName = strControllerName = "Floppy";
+        ComPtr<IStorageController> ptrControllerIgnored;
+        HRESULT hrc = rPtrSessionMachine->AddStorageController(bstrControllerName.raw(), StorageBus_Floppy,
+                                                               ptrControllerIgnored.asOutParam());
+        LogRelFunc(("Machine::addStorageController(Floppy) -> %Rhrc \n", hrc));
+        if (FAILED(hrc))
+            return hrc;
+    }
+
+    /*
+     * Adding a floppy drive (if needed) and mounting the auxiliary image is
+     * done later together with the ISOs.
+     */
+    rVecInstallatationDisks.push_back(UnattendedInstallationDisk(StorageBus_Floppy, strControllerName,
+                                                                 DeviceType_Floppy, AccessMode_ReadWrite,
+                                                                 0, 0,
+                                                                 fFoundPort0Dev0 /*fMountOnly*/,
+                                                                 mpInstaller->getAuxiliaryFloppyFilePath()));
+    return S_OK;
+}
+
+/**
+ * Reconfigures DVD drives of the VM to mount all the ISOs we need.
+ *
+ * This will umount all DVD media.
+ *
+ * @returns COM status code.
+ * @param   rControllers            The existing controllers.
+ * @param   rVecInstallatationDisks The list of image to mount.
+ * @param   rPtrSessionMachine      The session machine smart pointer.
+ * @param   rAutoLock               The lock.
+ * @param   enmRecommendedStorageBus The recommended storage bus type for adding
+ *                                   DVD drives on.
+ */
+HRESULT Unattended::i_reconfigureIsos(com::SafeIfaceArray<IStorageController> &rControllers,
+                                      std::vector<UnattendedInstallationDisk> &rVecInstallatationDisks,
+                                      ComPtr<IMachine> const &rPtrSessionMachine,
+                                      AutoMultiWriteLock2 &rAutoLock, StorageBus_T enmRecommendedStorageBus)
+{
+    /*
+     * Enumerate the attachements of every controller, looking for DVD drives,
+     * ASSUMEING all drives are bootable.
+     *
+     * Eject the medium from all the drives (don't want any confusion) and look
+     * for the recommended storage bus in case we need to add more drives.
+     */
+    HRESULT                    hrc;
+    std::list<ControllerSlot>  lstControllerDvdSlots;
+    Utf8Str                    strRecommendedControllerName; /* non-empty if recommended bus found. */
+    Utf8Str                    strControllerName;
+    Bstr                       bstrControllerName;
+    for (size_t i = 0; i < rControllers.size(); ++i)
+    {
+        hrc = rControllers[i]->COMGETTER(Name)(bstrControllerName.asOutParam());
+        AssertComRCReturn(hrc, hrc);
+        strControllerName = bstrControllerName;
+
+        /* Look for recommended storage bus. */
+        StorageBus_T enmStorageBus;
+        hrc = rControllers[i]->COMGETTER(Bus)(&enmStorageBus);
+        AssertComRCReturn(hrc, hrc);
+        if (enmStorageBus == enmRecommendedStorageBus)
+        {
+            strRecommendedControllerName = bstrControllerName;
+            AssertLogRelReturn(strControllerName.isNotEmpty(), setErrorBoth(E_UNEXPECTED, VERR_INTERNAL_ERROR_2));
+        }
+
+        /* Scan the controller attachments. */
+        com::SafeIfaceArray<IMediumAttachment> arrayOfMediumAttachments;
+        hrc = rPtrSessionMachine->GetMediumAttachmentsOfController(bstrControllerName.raw(),
+                                                                  ComSafeArrayAsOutParam(arrayOfMediumAttachments));
+        AssertComRCReturn(hrc, hrc);
+
+        for (size_t j = 0; j < arrayOfMediumAttachments.size(); j++)
+        {
+            DeviceType_T enmType;
+            hrc = arrayOfMediumAttachments[j]->COMGETTER(Type)(&enmType);
+            AssertComRCReturn(hrc, hrc);
+            if (enmType == DeviceType_DVD)
+            {
+                LONG iPort = -1;
+                hrc = arrayOfMediumAttachments[j]->COMGETTER(Port)(&iPort);
+                AssertComRCReturn(hrc, hrc);
+
+                LONG iDevice = -1;
+                hrc = arrayOfMediumAttachments[j]->COMGETTER(Device)(&iDevice);
+                AssertComRCReturn(hrc, hrc);
+
+                /* Remeber it. */
+                lstControllerDvdSlots.push_back(ControllerSlot(enmStorageBus, strControllerName, iPort, iDevice, false /*fFree*/));
+
+                /* Eject the medium, if any. */
+                ComPtr<IMedium> ptrMedium;
+                hrc = arrayOfMediumAttachments[j]->COMGETTER(Medium)(ptrMedium.asOutParam());
+                AssertComRCReturn(hrc, hrc);
+                if (ptrMedium.isNotNull())
+                {
+                    ptrMedium.setNull();
+
+                    rAutoLock.release();
+                    hrc = rPtrSessionMachine->UnmountMedium(bstrControllerName.raw(), iPort, iDevice, TRUE /*fForce*/);
+                    rAutoLock.acquire();
+                }
+            }
+        }
+    }
+
+    /*
+     * How many drives do we need? Add more if necessary.
+     */
+    ULONG cDvdDrivesNeeded = 0;
+    if (mpInstaller->isAuxiliaryIsoNeeded())
+        cDvdDrivesNeeded++;
+    if (mpInstaller->isOriginalIsoNeeded())
+        cDvdDrivesNeeded++;
+    if (mpInstaller->isAdditionsIsoNeeded())
+        cDvdDrivesNeeded++;
+    if (mpInstaller->isValidationKitIsoNeeded())
+        cDvdDrivesNeeded++;
+    Assert(cDvdDrivesNeeded > 0);
+    if (cDvdDrivesNeeded > lstControllerDvdSlots.size())
+    {
+        /* Do we need to add the recommended controller? */
+        if (strRecommendedControllerName.isEmpty())
+        {
+            switch (enmRecommendedStorageBus)
+            {
+                case StorageBus_IDE:    strRecommendedControllerName = "IDE";  break;
+                case StorageBus_SATA:   strRecommendedControllerName = "SATA"; break;
+                case StorageBus_SCSI:   strRecommendedControllerName = "SCSI"; break;
+                case StorageBus_SAS:    strRecommendedControllerName = "SAS";  break;
+                case StorageBus_USB:    strRecommendedControllerName = "USB";  break;
+                case StorageBus_PCIe:   strRecommendedControllerName = "PCIe"; break;
+                default:
+                    return setError(E_FAIL, tr("Support for recommended storage bus %d not implemented"),
+                                    (int)enmRecommendedStorageBus);
+            }
+            ComPtr<IStorageController> ptrControllerIgnored;
+            hrc = rPtrSessionMachine->AddStorageController(Bstr(strRecommendedControllerName).raw(), enmRecommendedStorageBus,
+                                                           ptrControllerIgnored.asOutParam());
+            LogRelFunc(("Machine::addStorageController(%s) -> %Rhrc \n", strRecommendedControllerName.c_str(), hrc));
+            if (FAILED(hrc))
+                return hrc;
+        }
+
+        /* Add free controller slots, maybe raising the port limit on the controller if we can. */
+        hrc = i_findOrCreateNeededFreeSlots(strRecommendedControllerName, enmRecommendedStorageBus, rPtrSessionMachine,
+                                            cDvdDrivesNeeded, lstControllerDvdSlots);
+        if (FAILED(hrc))
+            return hrc;
+        if (cDvdDrivesNeeded > lstControllerDvdSlots.size())
+        {
+            /* We could in many cases create another controller here, but it's not worth the effort. */
+            return setError(E_FAIL, tr("Not enough free slots on controller '%s' to add %u DVD drive(s)"),
+                            strRecommendedControllerName.c_str(), cDvdDrivesNeeded - lstControllerDvdSlots.size());
+        }
+        Assert(cDvdDrivesNeeded == lstControllerDvdSlots.size());
+    }
+
+    /*
+     * Sort the DVD slots in boot order.
+     */
+    lstControllerDvdSlots.sort();
+
+    /*
+     * Prepare ISO mounts.
+     *
+     * ASSUMES the auxiliary ISO is bootable if present.  ASSUMES the original
+     * ISO is bootable if no auxiliary ISO present.  These two assumptions is
+     * reflected in the order we grab DVD slots.
+     */
+    std::list<ControllerSlot>::const_iterator itDvdSlot = lstControllerDvdSlots.begin();
+    if (mpInstaller->isAuxiliaryIsoNeeded())
+    {
+        rVecInstallatationDisks.push_back(UnattendedInstallationDisk(itDvdSlot, mpInstaller->getAuxiliaryIsoFilePath()));
+        ++itDvdSlot;
+    }
+
+    if (mpInstaller->isOriginalIsoNeeded())
+    {
+        rVecInstallatationDisks.push_back(UnattendedInstallationDisk(itDvdSlot, i_getIsoPath()));
+        ++itDvdSlot;
+    }
+
+    if (mpInstaller->isAdditionsIsoNeeded())
+    {
+        rVecInstallatationDisks.push_back(UnattendedInstallationDisk(itDvdSlot, i_getAdditionsIsoPath()));
+        ++itDvdSlot;
+    }
+
+    if (mpInstaller->isValidationKitIsoNeeded())
+    {
+        rVecInstallatationDisks.push_back(UnattendedInstallationDisk(itDvdSlot, i_getValidationKitIsoPath()));
+        ++itDvdSlot;
+    }
+
+    return S_OK;
+}
+
+/**
+ * Used to find more free slots for DVD drives during VM reconfiguration.
+ *
+ * This may modify the @a portCount property of the given controller.
+ *
+ * @returns COM status code.
+ * @param   rStrControllerName      The name of the controller to find/create
+ *                                  free slots on.
+ * @param   enmStorageBus           The storage bus type.
+ * @param   rPtrSessionMachine      Reference to the session machine.
+ * @param   cSlotsNeeded            Total slots needed (including those we've
+ *                                  already found).
+ * @param   rDvdSlots               The slot collection for DVD drives to add
+ *                                  free slots to as we find/create them.
+ */
+HRESULT Unattended::i_findOrCreateNeededFreeSlots(const Utf8Str &rStrControllerName, StorageBus_T enmStorageBus,
+                                                  ComPtr<IMachine> const &rPtrSessionMachine, uint32_t cSlotsNeeded,
+                                                  std::list<ControllerSlot> &rDvdSlots)
+{
+    Assert(cSlotsNeeded > rDvdSlots.size());
+
+    /*
+     * Get controlleer stats.
+     */
+    ComPtr<IStorageController> pController;
+    HRESULT hrc = rPtrSessionMachine->GetStorageControllerByName(Bstr(rStrControllerName).raw(), pController.asOutParam());
+    AssertComRCReturn(hrc, hrc);
+
+    ULONG cMaxDevicesPerPort = 1;
+    hrc = pController->COMGETTER(MaxDevicesPerPortCount)(&cMaxDevicesPerPort);
+    AssertComRCReturn(hrc, hrc);
+    AssertLogRelReturn(cMaxDevicesPerPort > 0, E_UNEXPECTED);
+
+    ULONG cPorts = 0;
+    hrc = pController->COMGETTER(PortCount)(&cPorts);
+    AssertComRCReturn(hrc, hrc);
+
+    /*
+     * Get the attachment list and turn into an internal list for lookup speed.
+     */
+    com::SafeIfaceArray<IMediumAttachment> arrayOfMediumAttachments;
+    hrc = rPtrSessionMachine->GetMediumAttachmentsOfController(Bstr(rStrControllerName).raw(),
+                                                               ComSafeArrayAsOutParam(arrayOfMediumAttachments));
+    AssertComRCReturn(hrc, hrc);
+
+    std::vector<ControllerSlot> arrayOfUsedSlots;
+    for (size_t i = 0; i < arrayOfMediumAttachments.size(); i++)
+    {
+        LONG iPort = -1;
+        hrc = arrayOfMediumAttachments[i]->COMGETTER(Port)(&iPort);
+        AssertComRCReturn(hrc, hrc);
+
+        LONG iDevice = -1;
+        hrc = arrayOfMediumAttachments[i]->COMGETTER(Device)(&iDevice);
+        AssertComRCReturn(hrc, hrc);
+
+        arrayOfUsedSlots.push_back(ControllerSlot(enmStorageBus, Utf8Str::Empty, iPort, iDevice, false /*fFree*/));
+    }
+
+    /*
+     * Iterate thru all possible slots, adding those not found in arrayOfUsedSlots.
+     */
+    for (uint32_t iPort = 0; iPort < cPorts; iPort++)
+        for (uint32_t iDevice = 0; iDevice < cMaxDevicesPerPort; iDevice++)
+        {
+            bool fFound = false;
+            for (size_t i = 0; i < arrayOfUsedSlots.size(); i++)
+                if (   arrayOfUsedSlots[i].uPort   == iPort
+                    && arrayOfUsedSlots[i].uDevice == iDevice)
+                {
+                    fFound = true;
+                    break;
+                }
+            if (!fFound)
+            {
+                rDvdSlots.push_back(ControllerSlot(enmStorageBus, rStrControllerName, iPort, iDevice, true /*fFree*/));
+                if (rDvdSlots.size() >= cSlotsNeeded)
+                    return S_OK;
+            }
+        }
+
+    /*
+     * Okay we still need more ports.  See if increasing the number of controller
+     * ports would solve it.
+     */
+    ULONG cMaxPorts = 1;
+    hrc = pController->COMGETTER(MaxPortCount)(&cMaxPorts);
+    AssertComRCReturn(hrc, hrc);
+    if (cMaxPorts <= cPorts)
+        return S_OK;
+    size_t cNewPortsNeeded = (cSlotsNeeded - rDvdSlots.size() + cMaxDevicesPerPort - 1) / cMaxDevicesPerPort;
+    if (cPorts + cNewPortsNeeded > cMaxPorts)
+        return S_OK;
+
+    /*
+     * Raise the port count and add the free slots we've just created.
+     */
+    hrc = pController->COMSETTER(PortCount)(cPorts + (ULONG)cNewPortsNeeded);
+    AssertComRCReturn(hrc, hrc);
+    for (uint32_t iPort = cPorts; iPort < cPorts + cNewPortsNeeded; iPort++)
+        for (uint32_t iDevice = 0; iDevice < cMaxDevicesPerPort; iDevice++)
+        {
+            rDvdSlots.push_back(ControllerSlot(enmStorageBus, rStrControllerName, iPort, iDevice, true /*fFree*/));
+            if (rDvdSlots.size() >= cSlotsNeeded)
+                return S_OK;
+        }
+
+    /* We should not get here! */
+    AssertLogRelFailedReturn(E_UNEXPECTED);
+}
+
+HRESULT Unattended::done()
+{
+    LogFlow(("Unattended::done\n"));
+    if (mpInstaller)
+    {
+        LogRelFlow(("Unattended::done: Deleting installer object (%p)\n", mpInstaller));
+        delete mpInstaller;
+        mpInstaller = NULL;
+    }
+    return S_OK;
+}
+
+HRESULT Unattended::getIsoPath(com::Utf8Str &isoPath)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    isoPath = mStrIsoPath;
+    return S_OK;
+}
+
+HRESULT Unattended::setIsoPath(const com::Utf8Str &isoPath)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+    AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+    mStrIsoPath = isoPath;
+    return S_OK;
+}
+
+HRESULT Unattended::getUser(com::Utf8Str &user)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    user = mStrUser;
+    return S_OK;
+}
+
+
+HRESULT Unattended::setUser(const com::Utf8Str &user)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+    AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+    mStrUser = user;
+    return S_OK;
+}
+
+HRESULT Unattended::getPassword(com::Utf8Str &password)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    password = mStrPassword;
+    return S_OK;
+}
+
+HRESULT Unattended::setPassword(const com::Utf8Str &password)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+    AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+    mStrPassword = password;
+    return S_OK;
+}
+
+HRESULT Unattended::getFullUserName(com::Utf8Str &fullUserName)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    fullUserName = mStrFullUserName;
+    return S_OK;
+}
+
+HRESULT Unattended::setFullUserName(const com::Utf8Str &fullUserName)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+    AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+    mStrFullUserName = fullUserName;
+    return S_OK;
+}
+
+HRESULT Unattended::getProductKey(com::Utf8Str &productKey)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    productKey = mStrProductKey;
+    return S_OK;
+}
+
+HRESULT Unattended::setProductKey(const com::Utf8Str &productKey)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+    AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+    mStrProductKey = productKey;
+    return S_OK;
+}
+
+HRESULT Unattended::getAdditionsIsoPath(com::Utf8Str &additionsIsoPath)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    additionsIsoPath = mStrAdditionsIsoPath;
+    return S_OK;
+}
+
+HRESULT Unattended::setAdditionsIsoPath(const com::Utf8Str &additionsIsoPath)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+    AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+    mStrAdditionsIsoPath = additionsIsoPath;
+    return S_OK;
+}
+
+HRESULT Unattended::getInstallGuestAdditions(BOOL *installGuestAdditions)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    *installGuestAdditions = mfInstallGuestAdditions;
+    return S_OK;
+}
+
+HRESULT Unattended::setInstallGuestAdditions(BOOL installGuestAdditions)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+    AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+    mfInstallGuestAdditions = installGuestAdditions != FALSE;
+    return S_OK;
+}
+
+HRESULT Unattended::getValidationKitIsoPath(com::Utf8Str &aValidationKitIsoPath)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    aValidationKitIsoPath = mStrValidationKitIsoPath;
+    return S_OK;
+}
+
+HRESULT Unattended::setValidationKitIsoPath(const com::Utf8Str &aValidationKitIsoPath)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+    AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+    mStrValidationKitIsoPath = aValidationKitIsoPath;
+    return S_OK;
+}
+
+HRESULT Unattended::getInstallTestExecService(BOOL *aInstallTestExecService)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    *aInstallTestExecService = mfInstallTestExecService;
+    return S_OK;
+}
+
+HRESULT Unattended::setInstallTestExecService(BOOL aInstallTestExecService)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+    AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+    mfInstallTestExecService = aInstallTestExecService != FALSE;
+    return S_OK;
+}
+
+HRESULT Unattended::getTimeZone(com::Utf8Str &aTimeZone)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    aTimeZone = mStrTimeZone;
+    return S_OK;
+}
+
+HRESULT Unattended::setTimeZone(const com::Utf8Str &aTimezone)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+    AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+    mStrTimeZone = aTimezone;
+    return S_OK;
+}
+
+HRESULT Unattended::getLocale(com::Utf8Str &aLocale)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    aLocale = mStrLocale;
+    return S_OK;
+}
+
+HRESULT Unattended::setLocale(const com::Utf8Str &aLocale)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+    AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+    if (    aLocale.isEmpty() /* use default */
+        || (   aLocale.length() == 5
+            && RT_C_IS_LOWER(aLocale[0])
+            && RT_C_IS_LOWER(aLocale[1])
+            && aLocale[2] == '_'
+            && RT_C_IS_UPPER(aLocale[3])
+            && RT_C_IS_UPPER(aLocale[4])) )
+    {
+        mStrLocale = aLocale;
+        return S_OK;
+    }
+    return setError(E_INVALIDARG, tr("Expected two lower cased letters, an underscore, and two upper cased letters"));
+}
+
+HRESULT Unattended::getCountry(com::Utf8Str &aCountry)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    aCountry = mStrCountry;
+    return S_OK;
+}
+
+HRESULT Unattended::setCountry(const com::Utf8Str &aCountry)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+    AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+    if (   aCountry.isEmpty()
+        || (   aCountry.length() == 2
+            && RT_C_IS_UPPER(aCountry[0])
+            && RT_C_IS_UPPER(aCountry[1])) )
+    {
+        mStrCountry = aCountry;
+        return S_OK;
+    }
+    return setError(E_INVALIDARG, tr("Expected two upper cased letters"));
+}
+
+HRESULT Unattended::getProxy(com::Utf8Str &aProxy)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    aProxy = ""; /// @todo turn schema map into string or something.
+    return S_OK;
+}
+
+HRESULT Unattended::setProxy(const com::Utf8Str &aProxy)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+    AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+    if (aProxy.isEmpty())
+    {
+        /* set default proxy */
+    }
+    else if (aProxy.equalsIgnoreCase("none"))
+    {
+        /* clear proxy config */
+    }
+    else
+    {
+        /* Parse and set proxy config into a schema map or something along those lines. */
+        return E_NOTIMPL;
+    }
+    return S_OK;
+}
+
+HRESULT Unattended::getPackageSelectionAdjustments(com::Utf8Str &aPackageSelectionAdjustments)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    aPackageSelectionAdjustments = RTCString::join(mPackageSelectionAdjustments, ";");
+    return S_OK;
+}
+
+HRESULT Unattended::setPackageSelectionAdjustments(const com::Utf8Str &aPackageSelectionAdjustments)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+    AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+    if (aPackageSelectionAdjustments.isEmpty())
+        mPackageSelectionAdjustments.clear();
+    else
+    {
+        RTCList<RTCString, RTCString *> arrayStrSplit = aPackageSelectionAdjustments.split(";");
+        for (size_t i = 0; i < arrayStrSplit.size(); i++)
+        {
+            if (arrayStrSplit[i].equals("minimal"))
+            { /* okay */ }
+            else
+                return setError(E_INVALIDARG, tr("Unknown keyword: %s"), arrayStrSplit[i].c_str());
+        }
+        mPackageSelectionAdjustments = arrayStrSplit;
+    }
+    return S_OK;
+}
+
+HRESULT Unattended::getHostname(com::Utf8Str &aHostname)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    aHostname = mStrHostname;
+    return S_OK;
+}
+
+HRESULT Unattended::setHostname(const com::Utf8Str &aHostname)
+{
+    /*
+     * Validate input.
+     */
+    if (aHostname.length() > (aHostname.endsWith(".") ? 254U : 253U))
+        return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
+                            tr("Hostname '%s' is %zu bytes long, max is 253 (excluing trailing dot)"),
+                            aHostname.c_str(), aHostname.length());
+    size_t      cLabels  = 0;
+    const char *pszSrc   = aHostname.c_str();
+    for (;;)
+    {
+        size_t cchLabel = 1;
+        char ch = *pszSrc++;
+        if (RT_C_IS_ALNUM(ch))
+        {
+            cLabels++;
+            while ((ch = *pszSrc++) != '.' && ch != '\0')
+            {
+                if (RT_C_IS_ALNUM(ch) || ch == '-')
+                {
+                    if (cchLabel < 63)
+                        cchLabel++;
+                    else
+                        return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
+                                            tr("Invalid hostname '%s' - label %u is too long, max is 63."),
+                                            aHostname.c_str(), cLabels);
+                }
+                else
+                    return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
+                                        tr("Invalid hostname '%s' - illegal char '%c' at position %zu"),
+                                        aHostname.c_str(), ch, pszSrc - aHostname.c_str() - 1);
+            }
+            if (cLabels == 1 && cchLabel < 2)
+                return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
+                                    tr("Invalid hostname '%s' - the name part must be at least two characters long"),
+                                    aHostname.c_str());
+            if (ch == '\0')
+                break;
+        }
+        else if (ch != '\0')
+            return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
+                                tr("Invalid hostname '%s' - illegal lead char '%c' at position %zu"),
+                                aHostname.c_str(), ch, pszSrc - aHostname.c_str() - 1);
+        else
+            return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
+                                tr("Invalid hostname '%s' - trailing dot not permitted"), aHostname.c_str());
+    }
+    if (cLabels < 2)
+        return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
+                            tr("Incomplete hostname '%s' - must include both a name and a domain"), aHostname.c_str());
+
+    /*
+     * Make the change.
+     */
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+    AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+    mStrHostname = aHostname;
+    return S_OK;
+}
+
+HRESULT Unattended::getAuxiliaryBasePath(com::Utf8Str &aAuxiliaryBasePath)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    aAuxiliaryBasePath = mStrAuxiliaryBasePath;
+    return S_OK;
+}
+
+HRESULT Unattended::setAuxiliaryBasePath(const com::Utf8Str &aAuxiliaryBasePath)
+{
+    if (aAuxiliaryBasePath.isEmpty())
+        return setError(E_INVALIDARG, "Empty base path is not allowed");
+    if (!RTPathStartsWithRoot(aAuxiliaryBasePath.c_str()))
+        return setError(E_INVALIDARG, "Base path must be absolute");
+
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+    AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+    mStrAuxiliaryBasePath = aAuxiliaryBasePath;
+    mfIsDefaultAuxiliaryBasePath = mStrAuxiliaryBasePath.isEmpty();
+    return S_OK;
+}
+
+HRESULT Unattended::getImageIndex(ULONG *index)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    *index = midxImage;
+    return S_OK;
+}
+
+HRESULT Unattended::setImageIndex(ULONG index)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+    AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+    midxImage = index;
+    return S_OK;
+}
+
+HRESULT Unattended::getMachine(ComPtr<IMachine> &aMachine)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    return mMachine.queryInterfaceTo(aMachine.asOutParam());
+}
+
+HRESULT Unattended::setMachine(const ComPtr<IMachine> &aMachine)
+{
+    /*
+     * Lookup the VM so we can safely get the Machine instance.
+     * (Don't want to test how reliable XPCOM and COM are with finding
+     * the local object instance when a client passes a stub back.)
+     */
+    Bstr bstrUuidMachine;
+    HRESULT hrc = aMachine->COMGETTER(Id)(bstrUuidMachine.asOutParam());
+    if (SUCCEEDED(hrc))
+    {
+        Guid UuidMachine(bstrUuidMachine);
+        ComObjPtr<Machine> ptrMachine;
+        hrc = mParent->i_findMachine(UuidMachine, false /*fPermitInaccessible*/, true /*aSetError*/, &ptrMachine);
+        if (SUCCEEDED(hrc))
+        {
+            AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+            AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER,
+                                                           tr("Cannot change after prepare() has been called")));
+            mMachine     = ptrMachine;
+            mMachineUuid = UuidMachine;
+            if (mfIsDefaultAuxiliaryBasePath)
+                mStrAuxiliaryBasePath.setNull();
+            hrc = S_OK;
+        }
+    }
+    return hrc;
+}
+
+HRESULT Unattended::getScriptTemplatePath(com::Utf8Str &aScriptTemplatePath)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    if (   mStrScriptTemplatePath.isNotEmpty()
+        || mpInstaller == NULL)
+        aScriptTemplatePath = mStrScriptTemplatePath;
+    else
+        aScriptTemplatePath = mpInstaller->getTemplateFilePath();
+    return S_OK;
+}
+
+HRESULT Unattended::setScriptTemplatePath(const com::Utf8Str &aScriptTemplatePath)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+    AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+    mStrScriptTemplatePath = aScriptTemplatePath;
+    return S_OK;
+}
+
+HRESULT Unattended::getPostInstallScriptTemplatePath(com::Utf8Str &aPostInstallScriptTemplatePath)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    if (   mStrPostInstallScriptTemplatePath.isNotEmpty()
+        || mpInstaller == NULL)
+        aPostInstallScriptTemplatePath = mStrPostInstallScriptTemplatePath;
+    else
+        aPostInstallScriptTemplatePath = mpInstaller->getPostTemplateFilePath();
+    return S_OK;
+}
+
+HRESULT Unattended::setPostInstallScriptTemplatePath(const com::Utf8Str &aPostInstallScriptTemplatePath)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+    AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+    mStrPostInstallScriptTemplatePath = aPostInstallScriptTemplatePath;
+    return S_OK;
+}
+
+HRESULT Unattended::getPostInstallCommand(com::Utf8Str &aPostInstallCommand)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    aPostInstallCommand = mStrPostInstallCommand;
+    return S_OK;
+}
+
+HRESULT Unattended::setPostInstallCommand(const com::Utf8Str &aPostInstallCommand)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+    AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+    mStrPostInstallCommand = aPostInstallCommand;
+    return S_OK;
+}
+
+HRESULT Unattended::getExtraInstallKernelParameters(com::Utf8Str &aExtraInstallKernelParameters)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    if (   mStrExtraInstallKernelParameters.isNotEmpty()
+        || mpInstaller == NULL)
+        aExtraInstallKernelParameters = mStrExtraInstallKernelParameters;
+    else
+        aExtraInstallKernelParameters = mpInstaller->getDefaultExtraInstallKernelParameters();
+    return S_OK;
+}
+
+HRESULT Unattended::setExtraInstallKernelParameters(const com::Utf8Str &aExtraInstallKernelParameters)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+    AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+    mStrExtraInstallKernelParameters = aExtraInstallKernelParameters;
+    return S_OK;
+}
+
+HRESULT Unattended::getDetectedOSTypeId(com::Utf8Str &aDetectedOSTypeId)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    aDetectedOSTypeId = mStrDetectedOSTypeId;
+    return S_OK;
+}
+
+HRESULT Unattended::getDetectedOSVersion(com::Utf8Str &aDetectedOSVersion)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    aDetectedOSVersion = mStrDetectedOSVersion;
+    return S_OK;
+}
+
+HRESULT Unattended::getDetectedOSFlavor(com::Utf8Str &aDetectedOSFlavor)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    aDetectedOSFlavor = mStrDetectedOSFlavor;
+    return S_OK;
+}
+
+HRESULT Unattended::getDetectedOSHints(com::Utf8Str &aDetectedOSHints)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    aDetectedOSHints = mStrDetectedOSHints;
+    return S_OK;
+}
+
+/*
+ * Getters that the installer and script classes can use.
+ */
+Utf8Str const &Unattended::i_getIsoPath() const
+{
+    Assert(isReadLockedOnCurrentThread());
+    return mStrIsoPath;
+}
+
+Utf8Str const &Unattended::i_getUser() const
+{
+    Assert(isReadLockedOnCurrentThread());
+    return mStrUser;
+}
+
+Utf8Str const &Unattended::i_getPassword() const
+{
+    Assert(isReadLockedOnCurrentThread());
+    return mStrPassword;
+}
+
+Utf8Str const &Unattended::i_getFullUserName() const
+{
+    Assert(isReadLockedOnCurrentThread());
+    return mStrFullUserName.isNotEmpty() ? mStrFullUserName : mStrUser;
+}
+
+Utf8Str const &Unattended::i_getProductKey() const
+{
+    Assert(isReadLockedOnCurrentThread());
+    return mStrProductKey;
+}
+
+Utf8Str const &Unattended::i_getAdditionsIsoPath() const
+{
+    Assert(isReadLockedOnCurrentThread());
+    return mStrAdditionsIsoPath;
+}
+
+bool           Unattended::i_getInstallGuestAdditions() const
+{
+    Assert(isReadLockedOnCurrentThread());
+    return mfInstallGuestAdditions;
+}
+
+Utf8Str const &Unattended::i_getValidationKitIsoPath() const
+{
+    Assert(isReadLockedOnCurrentThread());
+    return mStrValidationKitIsoPath;
+}
+
+bool           Unattended::i_getInstallTestExecService() const
+{
+    Assert(isReadLockedOnCurrentThread());
+    return mfInstallTestExecService;
+}
+
+Utf8Str const &Unattended::i_getTimeZone() const
+{
+    Assert(isReadLockedOnCurrentThread());
+    return mStrTimeZone;
+}
+
+PCRTTIMEZONEINFO Unattended::i_getTimeZoneInfo() const
+{
+    Assert(isReadLockedOnCurrentThread());
+    return mpTimeZoneInfo;
+}
+
+Utf8Str const &Unattended::i_getLocale() const
+{
+    Assert(isReadLockedOnCurrentThread());
+    return mStrLocale;
+}
+
+Utf8Str const &Unattended::i_getCountry() const
+{
+    Assert(isReadLockedOnCurrentThread());
+    return mStrCountry;
+}
+
+bool Unattended::i_isMinimalInstallation() const
+{
+    size_t i = mPackageSelectionAdjustments.size();
+    while (i-- > 0)
+        if (mPackageSelectionAdjustments[i].equals("minimal"))
+            return true;
+    return false;
+}
+
+Utf8Str const &Unattended::i_getHostname() const
+{
+    Assert(isReadLockedOnCurrentThread());
+    return mStrHostname;
+}
+
+Utf8Str const &Unattended::i_getAuxiliaryBasePath() const
+{
+    Assert(isReadLockedOnCurrentThread());
+    return mStrAuxiliaryBasePath;
+}
+
+ULONG Unattended::i_getImageIndex() const
+{
+    Assert(isReadLockedOnCurrentThread());
+    return midxImage;
+}
+
+Utf8Str const &Unattended::i_getScriptTemplatePath() const
+{
+    Assert(isReadLockedOnCurrentThread());
+    return mStrScriptTemplatePath;
+}
+
+Utf8Str const &Unattended::i_getPostInstallScriptTemplatePath() const
+{
+    Assert(isReadLockedOnCurrentThread());
+    return mStrPostInstallScriptTemplatePath;
+}
+
+Utf8Str const &Unattended::i_getPostInstallCommand() const
+{
+    Assert(isReadLockedOnCurrentThread());
+    return mStrPostInstallCommand;
+}
+
+Utf8Str const &Unattended::i_getExtraInstallKernelParameters() const
+{
+    Assert(isReadLockedOnCurrentThread());
+    return mStrExtraInstallKernelParameters;
+}
+
+bool Unattended::i_isGuestOs64Bit() const
+{
+    Assert(isReadLockedOnCurrentThread());
+    return mfGuestOs64Bit;
+}
+
+VBOXOSTYPE Unattended::i_getGuestOsType() const
+{
+    Assert(isReadLockedOnCurrentThread());
+    return meGuestOsType;
+}
+
+HRESULT Unattended::i_attachImage(UnattendedInstallationDisk const *pImage, ComPtr<IMachine> const &rPtrSessionMachine,
+                                  AutoMultiWriteLock2 &rLock)
+{
+    /*
+     * Attach the disk image
+     * HACK ALERT! Temporarily release the Unattended lock.
+     */
+    rLock.release();
+
+    ComPtr<IMedium> ptrMedium;
+    HRESULT rc = mParent->OpenMedium(Bstr(pImage->strImagePath).raw(),
+                                     pImage->enmDeviceType,
+                                     pImage->enmAccessType,
+                                     true,
+                                     ptrMedium.asOutParam());
+    LogRelFlowFunc(("VirtualBox::openMedium -> %Rhrc\n", rc));
+    if (SUCCEEDED(rc))
+    {
+        if (pImage->fMountOnly)
+        {
+            // mount the opened disk image
+            rc = rPtrSessionMachine->MountMedium(Bstr(pImage->strControllerName).raw(), pImage->uPort,
+                                                 pImage->uDevice, ptrMedium, TRUE /*fForce*/);
+            LogRelFlowFunc(("Machine::MountMedium -> %Rhrc\n", rc));
+        }
+        else
+        {
+            //attach the opened disk image to the controller
+            rc = rPtrSessionMachine->AttachDevice(Bstr(pImage->strControllerName).raw(), pImage->uPort,
+                                                  pImage->uDevice, pImage->enmDeviceType, ptrMedium);
+            LogRelFlowFunc(("Machine::AttachDevice -> %Rhrc\n", rc));
+        }
+    }
+
+    rLock.acquire();
+    return rc;
+}
+
+bool Unattended::i_isGuestOSArchX64(Utf8Str const &rStrGuestOsTypeId)
+{
+    ComPtr<IGuestOSType> pGuestOSType;
+    HRESULT hrc = mParent->GetGuestOSType(Bstr(rStrGuestOsTypeId).raw(), pGuestOSType.asOutParam());
+    if (SUCCEEDED(hrc))
+    {
+        BOOL fIs64Bit = FALSE;
+        hrc = pGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit);
+        if (SUCCEEDED(hrc))
+            return fIs64Bit != FALSE;
+    }
+    return false;
+}
+
Index: /trunk/src/VBox/Main/src-server/UnattendedInstaller.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/UnattendedInstaller.cpp	(revision 68162)
+++ /trunk/src/VBox/Main/src-server/UnattendedInstaller.cpp	(revision 68162)
@@ -0,0 +1,1214 @@
+/* $Id$ */
+/** @file
+ * UnattendedInstaller class and it's descendants implementation
+ */
+
+/*
+ * Copyright (C) 2006-2017 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/*********************************************************************************************************************************
+*   Header Files                                                                                                                 *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_MAIN_UNATTENDED
+#include "LoggingNew.h"
+#include "VirtualBoxBase.h"
+#include "VirtualBoxErrorInfoImpl.h"
+#include "AutoCaller.h"
+#include <VBox/com/ErrorInfo.h>
+
+#include "MachineImpl.h"
+#include "UnattendedImpl.h"
+#include "UnattendedInstaller.h"
+#include "UnattendedScript.h"
+
+#include <VBox/err.h>
+#include <iprt/ctype.h>
+#include <iprt/fsisomaker.h>
+#include <iprt/fsvfs.h>
+#include <iprt/getopt.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+#include <iprt/vfs.h>
+#ifdef RT_OS_SOLARIS
+# undef ES /* Workaround for someone dragging the namespace pollutor sys/regset.h. Sigh. */
+#endif
+#include <iprt/formats/iso9660.h>
+#include <iprt/cpp/path.h>
+
+
+using namespace std;
+
+
+/* static */ UnattendedInstaller *UnattendedInstaller::createInstance(VBOXOSTYPE enmOsType, const Utf8Str &strGuestOsType,
+                                                                      Unattended *pParent)
+{
+    UnattendedInstaller *pUinstaller = NULL;
+
+    if (strGuestOsType.find("Windows") != RTCString::npos)
+    {
+        if (enmOsType >= VBOXOSTYPE_WinVista)
+            pUinstaller = new UnattendedWindowsXmlInstaller(pParent);
+        else
+            pUinstaller = new UnattendedWindowsSifInstaller(pParent);
+    }
+    else
+    {
+        if (enmOsType == VBOXOSTYPE_Debian || enmOsType == VBOXOSTYPE_Debian_x64)
+            pUinstaller = new UnattendedDebianInstaller(pParent);
+        else if (enmOsType >= VBOXOSTYPE_Ubuntu && enmOsType <= VBOXOSTYPE_Ubuntu_x64)
+            pUinstaller = new UnattendedUbuntuInstaller(pParent);
+        else if (enmOsType >= VBOXOSTYPE_RedHat && enmOsType <= VBOXOSTYPE_RedHat_x64)
+            pUinstaller = new UnattendedRedHat67Installer(pParent);
+        else if (enmOsType >= VBOXOSTYPE_FedoraCore && enmOsType <= VBOXOSTYPE_FedoraCore_x64)
+            pUinstaller = new UnattendedFedoraInstaller(pParent);
+        else if (enmOsType >= VBOXOSTYPE_Oracle && enmOsType <= VBOXOSTYPE_Oracle_x64)
+            pUinstaller = new UnattendedOracleLinuxInstaller(pParent);
+#if 0 /* doesn't work, so convert later. */
+        else if (enmOsType == VBOXOSTYPE_OpenSUSE || enmOsType == VBOXOSTYPE_OpenSUSE_x64)
+            pUinstaller = new UnattendedSuseInstaller(new UnattendedSUSEXMLScript(pParent), pParent);
+#endif
+    }
+    return pUinstaller;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+*
+*
+*  Implementation Unattended functions
+*
+*/
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*
+ *
+ * UnattendedInstaller public methods
+ *
+ */
+UnattendedInstaller::UnattendedInstaller(Unattended *pParent,
+                                         const char *pszMainScriptTemplateName, const char *pszPostScriptTemplateName,
+                                         const char *pszMainScriptFilename,     const char *pszPostScriptFilename,
+                                         DeviceType_T enmBootDevice  /*= DeviceType_DVD */)
+    : mMainScript(pParent, pszMainScriptTemplateName, pszMainScriptFilename)
+    , mPostScript(pParent, pszPostScriptTemplateName, pszPostScriptFilename)
+    , mpParent(pParent)
+    , meBootDevice(enmBootDevice)
+{
+    AssertPtr(pParent);
+    Assert(*pszMainScriptTemplateName);
+    Assert(*pszMainScriptFilename);
+    Assert(*pszPostScriptTemplateName);
+    Assert(*pszPostScriptFilename);
+    Assert(enmBootDevice == DeviceType_DVD || enmBootDevice == DeviceType_Floppy);
+}
+
+UnattendedInstaller::~UnattendedInstaller()
+{
+    mpParent = NULL;
+}
+
+HRESULT UnattendedInstaller::initInstaller()
+{
+    /*
+     * Calculate the full main script template location.
+     */
+    if (mpParent->i_getScriptTemplatePath().isNotEmpty())
+        mStrMainScriptTemplate = mpParent->i_getScriptTemplatePath();
+    else
+    {
+        int vrc = RTPathAppPrivateNoArchCxx(mStrMainScriptTemplate);
+        if (RT_SUCCESS(vrc))
+            vrc = RTPathAppendCxx(mStrMainScriptTemplate, "UnattendedTemplates");
+        if (RT_SUCCESS(vrc))
+            vrc = RTPathAppendCxx(mStrMainScriptTemplate, mMainScript.getDefaultTemplateFilename());
+        if (RT_FAILURE(vrc))
+            return mpParent->setErrorBoth(E_FAIL, vrc,
+                                          mpParent->tr("Failed to construct path to the unattended installer script templates (%Rrc)"),
+                                          vrc);
+    }
+
+    /*
+     * Calculate the full post script template location.
+     */
+    if (mpParent->i_getPostInstallScriptTemplatePath().isNotEmpty())
+        mStrPostScriptTemplate = mpParent->i_getPostInstallScriptTemplatePath();
+    else
+    {
+        int vrc = RTPathAppPrivateNoArchCxx(mStrPostScriptTemplate);
+        if (RT_SUCCESS(vrc))
+            vrc = RTPathAppendCxx(mStrPostScriptTemplate, "UnattendedTemplates");
+        if (RT_SUCCESS(vrc))
+            vrc = RTPathAppendCxx(mStrPostScriptTemplate, mPostScript.getDefaultTemplateFilename());
+        if (RT_FAILURE(vrc))
+            return mpParent->setErrorBoth(E_FAIL, vrc,
+                                          mpParent->tr("Failed to construct path to the unattended installer script templates (%Rrc)"),
+                                          vrc);
+    }
+
+    /*
+     * Construct paths we need.
+     */
+    if (isAuxiliaryFloppyNeeded())
+    {
+        mStrAuxiliaryFloppyFilePath = mpParent->i_getAuxiliaryBasePath();
+        mStrAuxiliaryFloppyFilePath.append("aux-floppy.img");
+    }
+    if (isAuxiliaryIsoNeeded())
+    {
+        mStrAuxiliaryIsoFilePath = mpParent->i_getAuxiliaryBasePath();
+        if (!isAuxiliaryIsoIsVISO())
+            mStrAuxiliaryIsoFilePath.append("aux-iso.iso");
+        else
+            mStrAuxiliaryIsoFilePath.append("aux-iso.viso");
+    }
+
+    /*
+     * Check that we've got the minimum of data available.
+     */
+    if (mpParent->i_getIsoPath().isEmpty())
+        return mpParent->setError(E_INVALIDARG, mpParent->tr("Cannot proceed with an empty installation ISO path"));
+    if (mpParent->i_getUser().isEmpty())
+        return mpParent->setError(E_INVALIDARG, mpParent->tr("Empty user name is not allowed"));
+    if (mpParent->i_getPassword().isEmpty())
+        return mpParent->setError(E_INVALIDARG, mpParent->tr("Empty password is not allowed"));
+
+    LogRelFunc(("UnattendedInstaller::savePassedData(): \n"));
+    return S_OK;
+}
+
+bool UnattendedInstaller::isAdditionsIsoNeeded() const
+{
+    /* In the VISO case, we'll add the additions to the VISO in a subdir. */
+    return !isAuxiliaryIsoIsVISO() && mpParent->i_getInstallGuestAdditions();
+}
+
+bool UnattendedInstaller::isValidationKitIsoNeeded() const
+{
+    /* In the VISO case, we'll add the validation kit to the VISO in a subdir. */
+    return !isAuxiliaryIsoIsVISO() && mpParent->i_getInstallTestExecService();
+}
+
+HRESULT UnattendedInstaller::prepareUnattendedScripts()
+{
+    LogFlow(("UnattendedInstaller::prepareUnattendedScripts()\n"));
+
+    /*
+     * The script template editor calls setError, so status codes just needs to
+     * be passed on to the caller.  Do the same for both scripts.
+     */
+    HRESULT hrc = mMainScript.read(getTemplateFilePath());
+    if (SUCCEEDED(hrc))
+    {
+        hrc = mMainScript.parse();
+        if (SUCCEEDED(hrc))
+        {
+            /* Ditto for the post script. */
+            hrc = mPostScript.read(getPostTemplateFilePath());
+            if (SUCCEEDED(hrc))
+            {
+                hrc = mPostScript.parse();
+                if (SUCCEEDED(hrc))
+                {
+                    LogFlow(("UnattendedInstaller::prepareUnattendedScripts: returns S_OK\n"));
+                    return S_OK;
+                }
+                LogFlow(("UnattendedInstaller::prepareUnattendedScripts: parse failed on post script (%Rhrc)\n", hrc));
+            }
+            else
+                LogFlow(("UnattendedInstaller::prepareUnattendedScripts: error reading post install script template file (%Rhrc)\n", hrc));
+        }
+        else
+            LogFlow(("UnattendedInstaller::prepareUnattendedScripts: parse failed (%Rhrc)\n", hrc));
+    }
+    else
+        LogFlow(("UnattendedInstaller::prepareUnattendedScripts: error reading installation script template file (%Rhrc)\n", hrc));
+    return hrc;
+}
+
+HRESULT UnattendedInstaller::prepareMedia(bool fOverwrite /*=true*/)
+{
+    LogRelFlow(("UnattendedInstaller::prepareMedia:\n"));
+    HRESULT hrc = S_OK;
+    if (isAuxiliaryFloppyNeeded())
+        hrc = prepareAuxFloppyImage(fOverwrite);
+    if (SUCCEEDED(hrc))
+    {
+        if (isAuxiliaryIsoNeeded())
+        {
+            hrc = prepareAuxIsoImage(fOverwrite);
+            if (FAILED(hrc))
+            {
+                LogRelFlow(("UnattendedInstaller::prepareMedia: prepareAuxIsoImage failed\n"));
+
+                /* Delete the floppy image if we created one.  */
+                if (isAuxiliaryFloppyNeeded())
+                    RTFileDelete(getAuxiliaryFloppyFilePath().c_str());
+            }
+        }
+    }
+    LogRelFlow(("UnattendedInstaller::prepareMedia: returns %Rrc\n", hrc));
+    return hrc;
+}
+
+/*
+ *
+ * UnattendedInstaller protected methods
+ *
+ */
+HRESULT UnattendedInstaller::prepareAuxFloppyImage(bool fOverwrite)
+{
+    Assert(isAuxiliaryFloppyNeeded());
+
+    /*
+     * Create the image and get a VFS to it.
+     */
+    RTVFS   hVfs;
+    HRESULT hrc = newAuxFloppyImage(getAuxiliaryFloppyFilePath().c_str(), fOverwrite, &hVfs);
+    if (SUCCEEDED(hrc))
+    {
+        /*
+         * Call overridable method to copies the files onto it.
+         */
+        hrc = copyFilesToAuxFloppyImage(hVfs);
+
+        /*
+         * Relase the VFS.  On failure, delete the floppy image so the operation can
+         * be repeated in non-overwrite mode and we don't leave any mess behind.
+         */
+        RTVfsRelease(hVfs);
+
+        if (FAILED(hrc))
+            RTFileDelete(getAuxiliaryFloppyFilePath().c_str());
+    }
+    return hrc;
+}
+
+HRESULT UnattendedInstaller::newAuxFloppyImage(const char *pszFilename, bool fOverwrite, PRTVFS phVfs)
+{
+    /*
+     * Open the image file.
+     */
+    HRESULT     hrc;
+    RTVFSFILE   hVfsFile;
+    uint64_t    fOpen = RTFILE_O_READWRITE | RTFILE_O_DENY_ALL | (0660 << RTFILE_O_CREATE_MODE_SHIFT);
+    if (fOverwrite)
+        fOpen |= RTFILE_O_CREATE_REPLACE;
+    else
+        fOpen |= RTFILE_O_OPEN;
+    int vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
+    if (RT_SUCCESS(vrc))
+    {
+        /*
+         * Format it.
+         */
+        vrc = RTFsFatVolFormat144(hVfsFile, false /*fQuick*/);
+        if (RT_SUCCESS(vrc))
+        {
+            /*
+             * Open the FAT VFS.
+             */
+            RTERRINFOSTATIC ErrInfo;
+            RTVFS           hVfs;
+            vrc = RTFsFatVolOpen(hVfsFile, false /*fReadOnly*/,  0 /*offBootSector*/, &hVfs, RTErrInfoInitStatic(&ErrInfo));
+            if (RT_SUCCESS(vrc))
+            {
+                *phVfs = hVfs;
+                RTVfsFileRelease(hVfsFile);
+                LogRelFlow(("UnattendedInstaller::newAuxFloppyImage: created, formatted and opened '%s'\n", pszFilename));
+                return S_OK;
+            }
+
+            if (RTErrInfoIsSet(&ErrInfo.Core))
+                hrc = mpParent->setErrorBoth(E_FAIL, vrc, mpParent->tr("Failed to open newly created floppy image '%s': %Rrc: %s"),
+                                             pszFilename, vrc, ErrInfo.Core.pszMsg);
+            else
+                hrc = mpParent->setErrorBoth(E_FAIL, vrc, mpParent->tr("Failed to open newly created floppy image '%s': %Rrc"),
+                                             pszFilename, vrc);
+        }
+        else
+            hrc = mpParent->setErrorBoth(E_FAIL, vrc, mpParent->tr("Failed to format floppy image '%s': %Rrc"), pszFilename, vrc);
+        RTVfsFileRelease(hVfsFile);
+        RTFileDelete(pszFilename);
+    }
+    else
+        hrc = mpParent->setErrorBoth(E_FAIL, vrc, mpParent->tr("Failed to create floppy image '%s': %Rrc"), pszFilename, vrc);
+    return hrc;
+}
+
+
+HRESULT UnattendedInstaller::copyFilesToAuxFloppyImage(RTVFS hVfs)
+{
+    HRESULT hrc = addScriptToFloppyImage(&mMainScript, hVfs);
+    if (SUCCEEDED(hrc))
+        hrc = addScriptToFloppyImage(&mPostScript, hVfs);
+    return hrc;
+}
+
+HRESULT UnattendedInstaller::addScriptToFloppyImage(BaseTextScript *pEditor, RTVFS hVfs)
+{
+    /*
+     * Open the destination file.
+     */
+    HRESULT   hrc;
+    RTVFSFILE hVfsFileDst;
+    int vrc = RTVfsFileOpen(hVfs, pEditor->getDefaultFilename(),
+                            RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_ALL
+                            | (0660 << RTFILE_O_CREATE_MODE_SHIFT),
+                            &hVfsFileDst);
+    if (RT_SUCCESS(vrc))
+    {
+        /*
+         * Save the content to a string.
+         */
+        Utf8Str strScript;
+        hrc = pEditor->saveToString(strScript);
+        if (SUCCEEDED(hrc))
+        {
+            /*
+             * Write the string.
+             */
+            vrc = RTVfsFileWrite(hVfsFileDst, strScript.c_str(), strScript.length(), NULL);
+            if (RT_SUCCESS(vrc))
+                hrc = S_OK; /* done */
+            else
+                hrc = mpParent->setErrorBoth(E_FAIL, vrc,
+                                             mpParent->tr("Error writing %zu bytes to '%s' in floppy image '%s': %Rrc"),
+                                             strScript.length(), pEditor->getDefaultFilename(),
+                                             getAuxiliaryFloppyFilePath().c_str());
+        }
+        RTVfsFileRelease(hVfsFileDst);
+    }
+    else
+        hrc = mpParent->setErrorBoth(E_FAIL, vrc,
+                                     mpParent->tr("Error creating '%s' in floppy image '%s': %Rrc"),
+                                     pEditor->getDefaultFilename(), getAuxiliaryFloppyFilePath().c_str());
+    return hrc;
+
+}
+
+HRESULT UnattendedInstaller::prepareAuxIsoImage(bool fOverwrite)
+{
+    /*
+     * Open the original installation ISO.
+     */
+    RTVFS   hVfsOrgIso;
+    HRESULT hrc = openInstallIsoImage(&hVfsOrgIso);
+    if (SUCCEEDED(hrc))
+    {
+        /*
+         * The next steps depends on the kind of image we're making.
+         */
+        if (!isAuxiliaryIsoIsVISO())
+        {
+            RTFSISOMAKER hIsoMaker;
+            hrc = newAuxIsoImageMaker(&hIsoMaker);
+            if (SUCCEEDED(hrc))
+            {
+                hrc = addFilesToAuxIsoImageMaker(hIsoMaker, hVfsOrgIso);
+                if (SUCCEEDED(hrc))
+                    hrc = finalizeAuxIsoImage(hIsoMaker, getAuxiliaryIsoFilePath().c_str(), fOverwrite);
+                RTFsIsoMakerRelease(hIsoMaker);
+            }
+        }
+        else
+        {
+            RTCList<RTCString> vecFiles(0);
+            RTCList<RTCString> vecArgs(0);
+            try
+            {
+                vecArgs.append() = "--iprt-iso-maker-file-marker-bourne-sh";
+                RTUUID Uuid;
+                int vrc = RTUuidCreate(&Uuid); AssertRC(vrc);
+                char szTmp[RTUUID_STR_LENGTH + 1];
+                vrc = RTUuidToStr(&Uuid, szTmp, sizeof(szTmp)); AssertRC(vrc);
+                vecArgs.append() = szTmp;
+                vecArgs.append() = "--file-mode=0444";
+                vecArgs.append() = "--dir-mode=0555";
+            }
+            catch (std::bad_alloc)
+            {
+                hrc = E_OUTOFMEMORY;
+            }
+            if (SUCCEEDED(hrc))
+            {
+                hrc = addFilesToAuxVisoVectors(vecArgs, vecFiles, hVfsOrgIso, fOverwrite);
+                if (SUCCEEDED(hrc))
+                    hrc = finalizeAuxVisoFile(vecArgs, getAuxiliaryIsoFilePath().c_str(), fOverwrite);
+
+                if (FAILED(hrc))
+                    for (size_t i = 0; i < vecFiles.size(); i++)
+                        RTFileDelete(vecFiles[i].c_str());
+            }
+        }
+        RTVfsRelease(hVfsOrgIso);
+    }
+    return hrc;
+}
+
+HRESULT UnattendedInstaller::openInstallIsoImage(PRTVFS phVfsIso, uint32_t fFlags /*= 0*/)
+{
+    /* Open the file. */
+    const char *pszIsoPath = mpParent->i_getIsoPath().c_str();
+    RTVFSFILE hOrgIsoFile;
+    int vrc = RTVfsFileOpenNormal(pszIsoPath, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hOrgIsoFile);
+    if (RT_FAILURE(vrc))
+        return mpParent->setErrorBoth(E_FAIL, vrc, mpParent->tr("Failed to open ISO image '%s' (%Rrc)"), pszIsoPath, vrc);
+
+    /* Pass the file to the ISO file system interpreter. */
+    RTERRINFOSTATIC ErrInfo;
+    vrc = RTFsIso9660VolOpen(hOrgIsoFile, fFlags, phVfsIso, RTErrInfoInitStatic(&ErrInfo));
+    RTVfsFileRelease(hOrgIsoFile);
+    if (RT_SUCCESS(vrc))
+        return S_OK;
+    if (RTErrInfoIsSet(&ErrInfo.Core))
+        return mpParent->setErrorBoth(E_FAIL, vrc, mpParent->tr("ISO reader fail to open '%s' (%Rrc): %s"),
+                                      pszIsoPath, vrc, ErrInfo.Core.pszMsg);
+    return mpParent->setErrorBoth(E_FAIL, vrc, mpParent->tr("ISO reader fail to open '%s' (%Rrc)"), pszIsoPath, vrc);
+}
+
+HRESULT UnattendedInstaller::newAuxIsoImageMaker(PRTFSISOMAKER phIsoMaker)
+{
+    int vrc = RTFsIsoMakerCreate(phIsoMaker);
+    if (RT_SUCCESS(vrc))
+        return vrc;
+    return mpParent->setErrorBoth(E_FAIL, vrc, mpParent->tr("RTFsIsoMakerCreate failed (%Rrc)"), vrc);
+}
+
+HRESULT UnattendedInstaller::addFilesToAuxIsoImageMaker(RTFSISOMAKER hIsoMaker, RTVFS hVfsOrgIso)
+{
+    RT_NOREF(hVfsOrgIso);
+
+    /*
+     * Add the two scripts to the image with default names.
+     */
+    HRESULT hrc = addScriptToIsoMaker(&mMainScript, hIsoMaker);
+    if (SUCCEEDED(hrc))
+        hrc = addScriptToIsoMaker(&mPostScript, hIsoMaker);
+    return hrc;
+}
+
+HRESULT UnattendedInstaller::addScriptToIsoMaker(BaseTextScript *pEditor, RTFSISOMAKER hIsoMaker,
+                                                 const char *pszDstFilename /*= NULL*/)
+{
+    /*
+     * Calc default destination filename if desired.
+     */
+    RTCString strDstNameBuf;
+    if (!pszDstFilename)
+    {
+        try
+        {
+            strDstNameBuf = RTPATH_SLASH_STR;
+            strDstNameBuf.append(pEditor->getDefaultTemplateFilename());
+            pszDstFilename = strDstNameBuf.c_str();
+        }
+        catch (std::bad_alloc)
+        {
+            return E_OUTOFMEMORY;
+        }
+    }
+
+    /*
+     * Create a memory file for the script.
+     */
+    Utf8Str strScript;
+    HRESULT hrc = pEditor->saveToString(strScript);
+    if (SUCCEEDED(hrc))
+    {
+        RTVFSFILE hVfsScriptFile;
+        size_t    cchScript = strScript.length();
+        int vrc = RTVfsFileFromBuffer(RTFILE_O_READ, strScript.c_str(), strScript.length(), &hVfsScriptFile);
+        strScript.setNull();
+        if (RT_SUCCESS(vrc))
+        {
+            /*
+             * Add it to the ISO.
+             */
+            vrc = RTFsIsoMakerAddFileWithVfsFile(hIsoMaker, pszDstFilename, hVfsScriptFile, NULL);
+            RTVfsFileRelease(hVfsScriptFile);
+            if (RT_SUCCESS(vrc))
+                hrc = S_OK;
+            else
+                hrc = mpParent->setErrorBoth(E_FAIL, vrc,
+                                             mpParent->tr("RTFsIsoMakerAddFileWithVfsFile failed on the script '%s' (%Rrc)"),
+                                             pszDstFilename, vrc);
+        }
+        else
+            hrc = mpParent->setErrorBoth(E_FAIL, vrc,
+                                         mpParent->tr("RTVfsFileFromBuffer failed on the %zu byte script '%s' (%Rrc)"),
+                                         cchScript, pszDstFilename, vrc);
+    }
+    return hrc;
+}
+
+HRESULT UnattendedInstaller::finalizeAuxIsoImage(RTFSISOMAKER hIsoMaker, const char *pszFilename, bool fOverwrite)
+{
+    /*
+     * Finalize the image.
+     */
+    int vrc = RTFsIsoMakerFinalize(hIsoMaker);
+    if (RT_FAILURE(vrc))
+        return mpParent->setErrorBoth(E_FAIL, vrc, mpParent->tr("RTFsIsoMakerFinalize failed (%Rrc)"), vrc);
+
+    /*
+     * Open the destination file.
+     */
+    uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_ALL;
+    if (fOverwrite)
+        fOpen |= RTFILE_O_CREATE_REPLACE;
+    else
+        fOpen |= RTFILE_O_CREATE;
+    RTVFSFILE hVfsDstFile;
+    vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsDstFile);
+    if (RT_FAILURE(vrc))
+    {
+        if (vrc == VERR_ALREADY_EXISTS)
+            return mpParent->setErrorBoth(E_FAIL, vrc, mpParent->tr("The auxiliary ISO image file '%s' already exists"),
+                                          pszFilename);
+        return mpParent->setErrorBoth(E_FAIL, vrc, mpParent->tr("Failed to open the auxiliary ISO image file '%s' for writing (%Rrc)"),
+                                      pszFilename, vrc);
+    }
+
+    /*
+     * Get the source file from the image maker.
+     */
+    HRESULT   hrc;
+    RTVFSFILE hVfsSrcFile;
+    vrc = RTFsIsoMakerCreateVfsOutputFile(hIsoMaker, &hVfsSrcFile);
+    if (RT_SUCCESS(vrc))
+    {
+        RTVFSIOSTREAM hVfsSrcIso = RTVfsFileToIoStream(hVfsSrcFile);
+        RTVFSIOSTREAM hVfsDstIso = RTVfsFileToIoStream(hVfsDstFile);
+        if (   hVfsSrcIso != NIL_RTVFSIOSTREAM
+            && hVfsDstIso != NIL_RTVFSIOSTREAM)
+        {
+            vrc = RTVfsUtilPumpIoStreams(hVfsSrcIso, hVfsDstIso, 0 /*cbBufHint*/);
+            if (RT_SUCCESS(vrc))
+                hrc = S_OK;
+            else
+                hrc = mpParent->setErrorBoth(E_FAIL, vrc, mpParent->tr("Error writing auxiliary ISO image '%s' (%Rrc)"),
+                                             pszFilename, vrc);
+        }
+        else
+            hrc = mpParent->setErrorBoth(E_FAIL, VERR_INTERNAL_ERROR_2,
+                                         mpParent->tr("Internal Error: Failed to case VFS file to VFS I/O stream"));
+        RTVfsIoStrmRelease(hVfsSrcIso);
+        RTVfsIoStrmRelease(hVfsDstIso);
+    }
+    else
+        hrc = mpParent->setErrorBoth(E_FAIL, vrc, mpParent->tr("RTFsIsoMakerCreateVfsOutputFile failed (%Rrc)"), vrc);
+    RTVfsFileRelease(hVfsSrcFile);
+    RTVfsFileRelease(hVfsDstFile);
+    if (FAILED(hrc))
+        RTFileDelete(pszFilename);
+    return hrc;
+}
+
+HRESULT UnattendedInstaller::addFilesToAuxVisoVectors(RTCList<RTCString> &rVecArgs, RTCList<RTCString> &rVecFiles,
+                                                      RTVFS hVfsOrgIso, bool fOverwrite)
+{
+    RT_NOREF(hVfsOrgIso);
+
+    /*
+     * Save and add the scripts.
+     */
+    HRESULT hrc = addScriptToVisoVectors(&mMainScript, rVecArgs, rVecFiles, fOverwrite);
+    if (SUCCEEDED(hrc))
+        hrc = addScriptToVisoVectors(&mPostScript, rVecArgs, rVecFiles, fOverwrite);
+    if (SUCCEEDED(hrc))
+    {
+        try
+        {
+            /*
+             * If we've got additions ISO, add its content to a /vboxadditions dir.
+             */
+            if (mpParent->i_getInstallGuestAdditions())
+            {
+                rVecArgs.append().append("--push-iso=").append(mpParent->i_getAdditionsIsoPath());
+                rVecArgs.append() = "/vboxadditions=/";
+                rVecArgs.append() = "--pop";
+            }
+
+            /*
+             * If we've got additions ISO, add its content to a /vboxadditions dir.
+             */
+            if (mpParent->i_getInstallTestExecService())
+            {
+                rVecArgs.append().append("--push-iso=").append(mpParent->i_getValidationKitIsoPath());
+                rVecArgs.append() = "/vboxvalidationkit=/";
+                rVecArgs.append() = "--pop";
+            }
+        }
+        catch (std::bad_alloc)
+        {
+            hrc = E_OUTOFMEMORY;
+        }
+    }
+    return hrc;
+}
+
+HRESULT UnattendedInstaller::addScriptToVisoVectors(BaseTextScript *pEditor, RTCList<RTCString> &rVecArgs,
+                                                    RTCList<RTCString> &rVecFiles, bool fOverwrite)
+{
+    /*
+     * Calc the aux script file name.
+     */
+    RTCString strScriptName;
+    try
+    {
+        strScriptName = mpParent->i_getAuxiliaryBasePath();
+        strScriptName.append(pEditor->getDefaultFilename());
+    }
+    catch (std::bad_alloc)
+    {
+        return E_OUTOFMEMORY;
+    }
+
+    /*
+     * Save it.
+     */
+    HRESULT hrc = pEditor->save(strScriptName.c_str(), fOverwrite);
+    if (SUCCEEDED(hrc))
+    {
+        /*
+         * Add it to the vectors.
+         */
+        try
+        {
+            rVecArgs.append().append('/').append(pEditor->getDefaultFilename()).append('=').append(strScriptName);
+            rVecFiles.append(strScriptName);
+        }
+        catch (std::bad_alloc)
+        {
+            RTFileDelete(strScriptName.c_str());
+            hrc = E_OUTOFMEMORY;
+        }
+    }
+    return hrc;
+}
+
+HRESULT UnattendedInstaller::finalizeAuxVisoFile(RTCList<RTCString> const &rVecArgs, const char *pszFilename, bool fOverwrite)
+{
+    /*
+     * Create a C-style argument vector and turn that into a command line string.
+     */
+    size_t const cArgs     = rVecArgs.size();
+    const char **papszArgs = (const char **)RTMemTmpAlloc((cArgs + 1) * sizeof(const char *));
+    if (!papszArgs)
+        return E_OUTOFMEMORY;
+    for (size_t i = 0; i < cArgs; i++)
+        papszArgs[i] = rVecArgs[i].c_str();
+    papszArgs[cArgs] = NULL;
+
+    char *pszCmdLine;
+    int vrc = RTGetOptArgvToString(&pszCmdLine, papszArgs, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
+    RTMemTmpFree(papszArgs);
+    if (RT_FAILURE(vrc))
+        return mpParent->setErrorBoth(E_FAIL, vrc, mpParent->tr("RTGetOptArgvToString failed (%Rrc)"), vrc);
+
+    /*
+     * Open the file.
+     */
+    HRESULT  hrc;
+    uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ;
+    if (fOverwrite)
+        fOpen |= RTFILE_O_CREATE_REPLACE;
+    else
+        fOpen |= RTFILE_O_CREATE;
+    RTFILE hFile;
+    vrc = RTFileOpen(&hFile, pszFilename, fOpen);
+    if (RT_SUCCESS(vrc))
+    {
+        vrc = RTFileWrite(hFile, pszCmdLine, strlen(pszCmdLine), NULL);
+        if (RT_SUCCESS(vrc))
+            vrc = RTFileClose(hFile);
+        else
+            RTFileClose(hFile);
+        if (RT_SUCCESS(vrc))
+            hrc = S_OK;
+        else
+            hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, mpParent->tr("Error writing '%s' (%Rrc)"), pszFilename, vrc);
+    }
+    else
+        hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, mpParent->tr("Failed to create '%s' (%Rrc)"), pszFilename, vrc);
+
+    RTStrFree(pszCmdLine);
+    return hrc;
+}
+
+HRESULT UnattendedInstaller::loadAndParseFileFromIso(RTVFS hVfsOrgIso, const char *pszFilename, AbstractScript *pEditor)
+{
+    HRESULT   hrc;
+    RTVFSFILE hVfsFile;
+    int vrc = RTVfsFileOpen(hVfsOrgIso, pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, &hVfsFile);
+    if (RT_SUCCESS(vrc))
+    {
+        hrc = pEditor->readFromHandle(hVfsFile, pszFilename);
+        RTVfsFileRelease(hVfsFile);
+        if (SUCCEEDED(hrc))
+            hrc = pEditor->parse();
+    }
+    else
+        hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, mpParent->tr("Failed to open '%s' on the ISO '%s' (%Rrc)"),
+                                     pszFilename, mpParent->i_getIsoPath().c_str(), vrc);
+    return hrc;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+*
+*
+*  Implementation UnattendedLinuxInstaller functions
+*
+*/
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT UnattendedLinuxInstaller::editIsoLinuxCfg(GeneralTextScript *pEditor)
+{
+    try
+    {
+        /* Set timeouts to 10 seconds. */
+        std::vector<size_t> vecLineNumbers = pEditor->findTemplate("timeout", RTCString::CaseInsensitive);
+        for (size_t i = 0; i < vecLineNumbers.size(); ++i)
+            if (pEditor->getContentOfLine(vecLineNumbers[i]).startsWithWord("timeout", RTCString::CaseInsensitive))
+            {
+                HRESULT hrc = pEditor->setContentOfLine(vecLineNumbers.at(i), "timeout 10");
+                if (FAILED(hrc))
+                    return hrc;
+            }
+
+        /* Comment out 'display <filename>' directives that's used for displaying files at boot time. */
+        vecLineNumbers = pEditor->findTemplate("display", RTCString::CaseInsensitive);
+        for (size_t i = 0; i < vecLineNumbers.size(); ++i)
+            if (pEditor->getContentOfLine(vecLineNumbers[i]).startsWithWord("display", RTCString::CaseInsensitive))
+            {
+                HRESULT hrc = pEditor->prependToLine(vecLineNumbers.at(i), "#");
+                if (FAILED(hrc))
+                    return hrc;
+            }
+
+        /* Modify kernel parameters. */
+        vecLineNumbers = pEditor->findTemplate("append", RTCString::CaseInsensitive);
+        if (vecLineNumbers.size() > 0)
+        {
+            Utf8Str const &rStrAppend = mpParent->i_getExtraInstallKernelParameters().isNotEmpty()
+                                      ? mpParent->i_getExtraInstallKernelParameters()
+                                      : mStrDefaultExtraInstallKernelParameters;
+
+            for (size_t i = 0; i < vecLineNumbers.size(); ++i)
+                if (pEditor->getContentOfLine(vecLineNumbers[i]).startsWithWord("append", RTCString::CaseInsensitive))
+                {
+                    Utf8Str strLine = pEditor->getContentOfLine(vecLineNumbers[i]);
+
+                    /* Do removals. */
+                    if (mArrStrRemoveInstallKernelParameters.size() > 0)
+                    {
+                        size_t offStart = strLine.find("append") + 5;
+                        while (offStart < strLine.length() && !RT_C_IS_SPACE(strLine[offStart]))
+                            offStart++;
+                        while (offStart < strLine.length() && RT_C_IS_SPACE(strLine[offStart]))
+                            offStart++;
+                        if (offStart < strLine.length())
+                        {
+                            for (size_t iRemove = 0; iRemove < mArrStrRemoveInstallKernelParameters.size(); iRemove++)
+                            {
+                                RTCString const &rStrRemove = mArrStrRemoveInstallKernelParameters[iRemove];
+                                for (size_t off = offStart; off < strLine.length(); )
+                                {
+                                    Assert(!RT_C_IS_SPACE(strLine[off]));
+
+                                    /* Find the end of word. */
+                                    size_t offEnd = off + 1;
+                                    while (offEnd < strLine.length() && !RT_C_IS_SPACE(strLine[offEnd]))
+                                        offEnd++;
+
+                                    /* Check if it matches. */
+                                    if (RTStrSimplePatternNMatch(rStrRemove.c_str(), rStrRemove.length(),
+                                                                 strLine.c_str() + off, offEnd - off))
+                                    {
+                                        while (off > 0 && RT_C_IS_SPACE(strLine[off - 1]))
+                                            off--;
+                                        strLine.erase(off, offEnd - off);
+                                    }
+
+                                    /* Advance to the next word. */
+                                    off = offEnd;
+                                    while (off < strLine.length() && RT_C_IS_SPACE(strLine[off]))
+                                        off++;
+                                }
+                            }
+                        }
+                    }
+
+                    /* Do the appending. */
+                    if (rStrAppend.isNotEmpty())
+                    {
+                        if (!rStrAppend.startsWith(" ") && !strLine.endsWith(" "))
+                            strLine.append(' ');
+                        strLine.append(rStrAppend);
+                    }
+
+                    /* Update line. */
+                    HRESULT hrc = pEditor->setContentOfLine(vecLineNumbers.at(i), strLine);
+                    if (FAILED(hrc))
+                        return hrc;
+                }
+        }
+    }
+    catch (std::bad_alloc)
+    {
+        return E_OUTOFMEMORY;
+    }
+    return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+*
+*
+*  Implementation UnattendedDebianInstaller functions
+*
+*/
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+
+HRESULT UnattendedDebianInstaller::addFilesToAuxVisoVectors(RTCList<RTCString> &rVecArgs, RTCList<RTCString> &rVecFiles,
+                                                            RTVFS hVfsOrgIso, bool fOverwrite)
+{
+    /*
+     * VISO bits and filenames.
+     */
+    RTCString strIsoLinuxCfg;
+    RTCString strTxtCfg;
+    try
+    {
+        /* Remaster ISO. */
+        rVecArgs.append() = "--no-file-mode";
+        rVecArgs.append() = "--no-dir-mode";
+
+        rVecArgs.append() = "--import-iso";
+        rVecArgs.append(mpParent->i_getIsoPath());
+
+        rVecArgs.append() = "--file-mode=0444";
+        rVecArgs.append() = "--dir-mode=0555";
+
+        /* Remove the two isolinux configure files we'll be replacing. */
+        rVecArgs.append() = "isolinux/isolinux.cfg=:must-remove:";
+        rVecArgs.append() = "isolinux/txt.cfg=:must-remove:";
+
+        /* Add the replacement files. */
+        strIsoLinuxCfg = mpParent->i_getAuxiliaryBasePath();
+        strIsoLinuxCfg.append("isolinux-isolinux.cfg");
+        rVecArgs.append().append("isolinux/isolinux.cfg=").append(strIsoLinuxCfg);
+
+        strTxtCfg = mpParent->i_getAuxiliaryBasePath();
+        strTxtCfg.append("isolinux-txt.cfg");
+        rVecArgs.append().append("isolinux/txt.cfg=").append(strTxtCfg);
+    }
+    catch (std::bad_alloc)
+    {
+        return E_OUTOFMEMORY;
+    }
+
+    /*
+     * Edit the isolinux.cfg file.
+     */
+    {
+        GeneralTextScript Editor(mpParent);
+        HRESULT hrc = loadAndParseFileFromIso(hVfsOrgIso, "/isolinux/isolinux.cfg", &Editor);
+        if (SUCCEEDED(hrc))
+            hrc = editIsoLinuxCfg(&Editor);
+        if (SUCCEEDED(hrc))
+        {
+            hrc = Editor.save(strIsoLinuxCfg, fOverwrite);
+            if (SUCCEEDED(hrc))
+            {
+                try
+                {
+                    rVecFiles.append(strIsoLinuxCfg);
+                }
+                catch (std::bad_alloc)
+                {
+                    RTFileDelete(strIsoLinuxCfg.c_str());
+                    hrc = E_OUTOFMEMORY;
+                }
+            }
+        }
+        if (FAILED(hrc))
+            return hrc;
+    }
+
+    /*
+     * Edit the txt.cfg file.
+     */
+    {
+        GeneralTextScript Editor(mpParent);
+        HRESULT hrc = loadAndParseFileFromIso(hVfsOrgIso, "/isolinux/txt.cfg", &Editor);
+        if (SUCCEEDED(hrc))
+            hrc = editDebianTxtCfg(&Editor);
+        if (SUCCEEDED(hrc))
+        {
+            hrc = Editor.save(strTxtCfg, fOverwrite);
+            if (SUCCEEDED(hrc))
+            {
+                try
+                {
+                    rVecFiles.append(strTxtCfg);
+                }
+                catch (std::bad_alloc)
+                {
+                    RTFileDelete(strTxtCfg.c_str());
+                    hrc = E_OUTOFMEMORY;
+                }
+            }
+        }
+        if (FAILED(hrc))
+            return hrc;
+    }
+
+    /*
+     * Call parent to add the preseed file from mAlg.
+     */
+    return UnattendedLinuxInstaller::addFilesToAuxVisoVectors(rVecArgs, rVecFiles, hVfsOrgIso, fOverwrite);
+}
+
+HRESULT UnattendedDebianInstaller::editDebianTxtCfg(GeneralTextScript *pEditor)
+{
+    try
+    {
+        /** @todo r=bird: Add some comments saying wtf you're actually up to here.
+         *        Repeating what's clear from function calls and boasting the
+         *        inteligence of the code isn't helpful. */
+        //find all lines with "label" inside
+        std::vector<size_t> vecLineNumbers = pEditor->findTemplate("label", RTCString::CaseInsensitive);
+        for (size_t i = 0; i < vecLineNumbers.size(); ++i)
+        {
+            RTCString const &rContent = pEditor->getContentOfLine(vecLineNumbers[i]);
+
+            // ASSUME: suppose general string looks like "label install", two words separated by " ".
+            RTCList<RTCString> vecPartsOfcontent = rContent.split(" ");
+            if (vecPartsOfcontent.size() > 1 && vecPartsOfcontent[1].contains("install")) /** @todo r=bird: case insensitive? */
+            {
+                std::vector<size_t> vecDefaultLineNumbers = pEditor->findTemplate("default", RTCString::CaseInsensitive);
+                //handle the lines more intelligently
+                for (size_t j = 0; j < vecDefaultLineNumbers.size(); ++j)
+                {
+                    Utf8Str strNewContent("default ");
+                    strNewContent.append(vecPartsOfcontent[1]);
+                    HRESULT hrc = pEditor->setContentOfLine(vecDefaultLineNumbers[j], strNewContent);
+                    if (FAILED(hrc))
+                        return hrc;
+                }
+            }
+        }
+    }
+    catch (std::bad_alloc)
+    {
+        return E_OUTOFMEMORY;
+    }
+    return UnattendedLinuxInstaller::editIsoLinuxCfg(pEditor);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+*
+*
+*  Implementation UnattendedRedHat67Installer functions
+*
+*/
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT UnattendedRedHat67Installer::addFilesToAuxVisoVectors(RTCList<RTCString> &rVecArgs, RTCList<RTCString> &rVecFiles,
+                                                              RTVFS hVfsOrgIso, bool fOverwrite)
+{
+    Utf8Str strIsoLinuxCfg;
+    try
+    {
+#if 1
+        /* Remaster ISO. */
+        rVecArgs.append() = "--no-file-mode";
+        rVecArgs.append() = "--no-dir-mode";
+
+        rVecArgs.append() = "--import-iso";
+        rVecArgs.append(mpParent->i_getIsoPath());
+
+        rVecArgs.append() = "--file-mode=0444";
+        rVecArgs.append() = "--dir-mode=0555";
+
+        /* We replace isolinux.cfg with our edited version (see further down). */
+        rVecArgs.append() = "isolinux/isolinux.cfg=:must-remove:";
+        strIsoLinuxCfg = mpParent->i_getAuxiliaryBasePath();
+        strIsoLinuxCfg.append("isolinux-isolinux.cfg");
+        rVecArgs.append().append("isolinux/isolinux.cfg=").append(strIsoLinuxCfg);
+
+#else
+        /** @todo Maybe we should just remaster the ISO for redhat derivatives too?
+         *        One less CDROM to mount. */
+        /* Name the ISO. */
+        rVecArgs.append() = "--volume-id=VBox Unattended Boot";
+
+        /* Copy the isolinux directory from the original install ISO. */
+        rVecArgs.append().append("--push-iso=").append(mpParent->i_getIsoPath());
+        rVecArgs.append() = "/isolinux=/isolinux";
+        rVecArgs.append() = "--pop";
+
+        /* We replace isolinux.cfg with our edited version (see further down). */
+        rVecArgs.append() = "/isolinux/isolinux.cfg=:must-remove:";
+
+        strIsoLinuxCfg = mpParent->i_getAuxiliaryBasePath();
+        strIsoLinuxCfg.append("isolinux-isolinux.cfg");
+        rVecArgs.append().append("/isolinux/isolinux.cfg=").append(strIsoLinuxCfg);
+
+        /* Configure booting /isolinux/isolinux.bin. */
+        rVecArgs.append() = "--eltorito-boot";
+        rVecArgs.append() = "/isolinux/isolinux.bin";
+        rVecArgs.append() = "--no-emulation-boot";
+        rVecArgs.append() = "--boot-info-table";
+        rVecArgs.append() = "--boot-load-seg=0x07c0";
+        rVecArgs.append() = "--boot-load-size=4";
+
+        /* Make the boot catalog visible in the file system. */
+        rVecArgs.append() = "--boot-catalog=/isolinux/vboxboot.cat";
+#endif
+    }
+    catch (std::bad_alloc)
+    {
+        return E_OUTOFMEMORY;
+    }
+
+    /*
+     * Edit isolinux.cfg and save it.
+     */
+    {
+        GeneralTextScript Editor(mpParent);
+        HRESULT hrc = loadAndParseFileFromIso(hVfsOrgIso, "/isolinux/isolinux.cfg", &Editor);
+        if (SUCCEEDED(hrc))
+            hrc = editIsoLinuxCfg(&Editor);
+        if (SUCCEEDED(hrc))
+        {
+            hrc = Editor.save(strIsoLinuxCfg, fOverwrite);
+            if (SUCCEEDED(hrc))
+            {
+                try
+                {
+                    rVecFiles.append(strIsoLinuxCfg);
+                }
+                catch (std::bad_alloc)
+                {
+                    RTFileDelete(strIsoLinuxCfg.c_str());
+                    hrc = E_OUTOFMEMORY;
+                }
+            }
+        }
+        if (FAILED(hrc))
+            return hrc;
+    }
+
+    /*
+     * Call parent to add the ks.cfg file from mAlg.
+     */
+    return UnattendedLinuxInstaller::addFilesToAuxVisoVectors(rVecArgs, rVecFiles, hVfsOrgIso, fOverwrite);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+*
+*
+*  Implementation UnattendedSuseInstaller functions
+*
+*/
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+#if 0 /* doesn't work, so convert later */
+/*
+ *
+ * UnattendedSuseInstaller protected methods
+ *
+*/
+HRESULT UnattendedSuseInstaller::setUserData()
+{
+    HRESULT rc = S_OK;
+    //here base class function must be called first
+    //because user home directory is set after user name
+    rc = UnattendedInstaller::setUserData();
+
+    rc = mAlg->setField(USERHOMEDIR_ID, "");
+    if (FAILED(rc))
+        return rc;
+
+    return rc;
+}
+
+/*
+ *
+ * UnattendedSuseInstaller private methods
+ *
+*/
+
+HRESULT UnattendedSuseInstaller::iv_initialPhase()
+{
+    Assert(isAuxiliaryIsoNeeded());
+    if (mParent->i_isGuestOs64Bit())
+        mFilesAndDirsToExtractFromIso.append("boot/x86_64/loader/ ");
+    else
+        mFilesAndDirsToExtractFromIso.append("boot/i386/loader/ ");
+    return extractOriginalIso(mFilesAndDirsToExtractFromIso);
+}
+
+
+HRESULT UnattendedSuseInstaller::setupScriptOnAuxiliaryCD(const Utf8Str &path)
+{
+    HRESULT rc = S_OK;
+
+    GeneralTextScript isoSuseCfgScript(mpParent);
+    rc = isoSuseCfgScript.read(path);
+    rc = isoSuseCfgScript.parse();
+    //fix linux core bootable parameters: add path to the preseed script
+
+    std::vector<size_t> listOfLines = isoSuseCfgScript.findTemplate("append");
+    for(unsigned int i=0; i<listOfLines.size(); ++i)
+    {
+        isoSuseCfgScript.appendToLine(listOfLines.at(i),
+                                      " auto=true priority=critical autoyast=default instmode=cd quiet splash noprompt noshell --");
+    }
+
+    //find all lines with "label" inside
+    listOfLines = isoSuseCfgScript.findTemplate("label");
+    for(unsigned int i=0; i<listOfLines.size(); ++i)
+    {
+        Utf8Str content = isoSuseCfgScript.getContentOfLine(listOfLines.at(i));
+
+        //suppose general string looks like "label linux", two words separated by " ".
+        RTCList<RTCString> partsOfcontent = content.split(" ");
+
+        if (partsOfcontent.at(1).contains("linux"))
+        {
+            std::vector<size_t> listOfDefault = isoSuseCfgScript.findTemplate("default");
+            //handle the lines more intelligently
+            for(unsigned int j=0; j<listOfDefault.size(); ++j)
+            {
+                Utf8Str newContent("default ");
+                newContent.append(partsOfcontent.at(1));
+                isoSuseCfgScript.setContentOfLine(listOfDefault.at(j), newContent);
+            }
+        }
+    }
+
+    rc = isoSuseCfgScript.save(path, true);
+
+    LogRelFunc(("UnattendedSuseInstaller::setupScriptsOnAuxiliaryCD(): The file %s has been changed\n", path.c_str()));
+
+    return rc;
+}
+#endif
+
Index: /trunk/src/VBox/Main/src-server/UnattendedScript.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/UnattendedScript.cpp	(revision 68162)
+++ /trunk/src/VBox/Main/src-server/UnattendedScript.cpp	(revision 68162)
@@ -0,0 +1,935 @@
+/* $Id$ */
+/** @file
+ * Implementeation of algorithms which read/parse/save scripts for unattended installation.
+ */
+
+/*
+ * Copyright (C) 2006-2017 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/*********************************************************************************************************************************
+*   Header Files                                                                                                                 *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_MAIN_UNATTENDED
+#include "LoggingNew.h"
+#include "VirtualBoxBase.h"
+#include "AutoCaller.h"
+#include <VBox/com/ErrorInfo.h>
+
+#include "MachineImpl.h"
+#include "UnattendedScript.h"
+#include "UnattendedImpl.h"
+
+#include <VBox/err.h>
+
+#include <iprt/ctype.h>
+#include <iprt/file.h>
+#include <iprt/getopt.h>
+#include <iprt/path.h>
+
+using namespace std;
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+*
+*
+*  Implementation BaseTextScript functions
+*
+*/
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT BaseTextScript::read(const Utf8Str &rStrFilename)
+{
+    /*
+     * Open the file for reading and figure it's size.  Capping the size
+     * at 16MB so we don't exaust the heap on bad input.
+     */
+    HRESULT   hrc;
+    RTVFSFILE hVfsFile;
+    int vrc = RTVfsFileOpenNormal(rStrFilename.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, &hVfsFile);
+    if (RT_SUCCESS(vrc))
+    {
+        hrc = readFromHandle(hVfsFile, rStrFilename.c_str());
+        RTVfsFileRelease(hVfsFile);
+    }
+    else
+        hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("Failed to open '%s' (%Rrc)"), rStrFilename.c_str(), vrc);
+    return hrc;
+}
+
+HRESULT BaseTextScript::readFromHandle(RTVFSFILE hVfsFile, const char *pszFilename)
+{
+    /*
+     * Open the file for reading and figure it's size.  Capping the size
+     * at 16MB so we don't exaust the heap on bad input.
+     */
+    HRESULT  hrc;
+    uint64_t cbFile = 0;
+    int vrc = RTVfsFileGetSize(hVfsFile, &cbFile);
+    if (   RT_SUCCESS(vrc)
+        && cbFile < _16M)
+    {
+        /*
+         * Exploint the jolt() feature of RTCString and read the content directly into
+         * its storage buffer.
+         */
+        vrc = mStrScriptFullContent.reserveNoThrow((size_t)cbFile + 1);
+        if (RT_SUCCESS(vrc))
+        {
+            char *pszDst = mStrScriptFullContent.mutableRaw();
+            vrc = RTVfsFileReadAt(hVfsFile, 0 /*off*/, pszDst, (size_t)cbFile, NULL);
+            pszDst[(size_t)cbFile] = '\0';
+            if (RT_SUCCESS(vrc))
+            {
+                /*
+                 * We must validate the encoding or we'll be subject to potential security trouble.
+                 * If this turns out to be problematic, we will need to implement codeset
+                 * conversion coping mechanisms.
+                 */
+                vrc = RTStrValidateEncodingEx(pszDst, (size_t)cbFile + 1,
+                                              RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED | RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
+                if (RT_SUCCESS(vrc))
+                {
+                    mStrScriptFullContent.jolt();
+                    return S_OK;
+                }
+
+                hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("'%s' isn't valid UTF-8: %Rrc"), pszFilename, vrc);
+            }
+            else
+                hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("Error reading '%s': %Rrc"), pszFilename, vrc);
+            mStrScriptFullContent.setNull();
+        }
+        else
+            hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("Failed to allocate memory (%'RU64 bytes) for '%s'"),
+                                          cbFile, pszFilename);
+    }
+    else if (RT_SUCCESS(vrc))
+        hrc = mpSetError->setErrorVrc(VERR_FILE_TOO_BIG,
+                                      mpSetError->tr("'%s' is too big (max 16MB): %'RU64"), pszFilename, cbFile);
+    else
+        hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("RTVfsFileGetSize failed (%Rrc)"), vrc);
+    return hrc;
+}
+
+HRESULT BaseTextScript::save(const Utf8Str &rStrFilename, bool fOverwrite)
+{
+    /*
+     * We may have to append the default filename to the
+     */
+    const char *pszFilename = rStrFilename.c_str();
+    Utf8Str     strWithDefaultFilename;
+    if (   getDefaultFilename() != NULL
+        && *getDefaultFilename() != '\0'
+        && RTDirExists(rStrFilename.c_str()) )
+    {
+        try
+        {
+            strWithDefaultFilename = rStrFilename;
+            strWithDefaultFilename.append(RTPATH_SLASH);
+            strWithDefaultFilename.append(getDefaultFilename());
+        }
+        catch (std::bad_alloc)
+        {
+            return E_OUTOFMEMORY;
+        }
+        pszFilename = strWithDefaultFilename.c_str();
+    }
+
+    /*
+     * Save the filename for later use.
+     */
+    try
+    {
+        mStrSavedPath = pszFilename;
+    }
+    catch (std::bad_alloc)
+    {
+        return E_OUTOFMEMORY;
+    }
+
+    /*
+     * Use the saveToString method to produce the content.
+     */
+    Utf8Str strDst;
+    HRESULT hrc = saveToString(strDst);
+    if (SUCCEEDED(hrc))
+    {
+        /*
+         * Write the content.
+         */
+        RTFILE   hFile;
+        uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_ALL;
+        if (fOverwrite)
+            fOpen |= RTFILE_O_CREATE_REPLACE;
+        else
+            fOpen |= RTFILE_O_CREATE;
+        int vrc = RTFileOpen(&hFile, pszFilename, fOpen);
+        if (RT_SUCCESS(vrc))
+        {
+            vrc = RTFileWrite(hFile, strDst.c_str(), strDst.length(), NULL);
+            if (RT_SUCCESS(vrc))
+            {
+                vrc = RTFileClose(hFile);
+                if (RT_SUCCESS(vrc))
+                {
+                    LogRelFlow(("GeneralTextScript::save(): saved %zu bytes to '%s'\n", strDst.length(), pszFilename));
+                    return S_OK;
+                }
+            }
+            RTFileClose(hFile);
+            RTFileDelete(pszFilename);
+            hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("Error writing to '%s' (%Rrc)"), pszFilename, vrc);
+        }
+        else
+            hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("Error creating/replacing '%s' (%Rrc)"), pszFilename, vrc);
+    }
+    return hrc;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+*
+*
+*  Implementation UnattendedScriptTemplate methods
+*
+*/
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+
+UnattendedScriptTemplate::UnattendedScriptTemplate(Unattended *pUnattended, const char *pszDefaultTemplateFilename,
+                                                   const char *pszDefaultFilename)
+    : BaseTextScript(pUnattended, pszDefaultTemplateFilename, pszDefaultFilename), mpUnattended(pUnattended)
+{
+}
+
+
+HRESULT UnattendedScriptTemplate::saveToString(Utf8Str &rStrDst)
+{
+    static const char s_szPrefix[]         = "@@VBOX_";
+    static const char s_szPrefixInsert[]   = "@@VBOX_INSERT_";
+    static const char s_szPrefixCond[]     = "@@VBOX_COND_";
+    static const char s_szPrefixCondEnd[]  = "@@VBOX_COND_END@@";
+
+    struct
+    {
+        bool    fSavedOutputting;
+    }           aConds[8];
+    unsigned    cConds      = 0;
+    bool        fOutputting = true;
+    HRESULT     hrc         = E_FAIL;
+    size_t      offTemplate = 0;
+    size_t      cchTemplate = mStrScriptFullContent.length();
+    rStrDst.setNull();
+    for (;;)
+    {
+        /*
+         * Find the next placeholder and add any text before it to the output.
+         */
+        size_t offPlaceholder = mStrScriptFullContent.find(s_szPrefix, offTemplate);
+        size_t cchToCopy = offPlaceholder != RTCString::npos ? offPlaceholder - offTemplate : cchTemplate - offTemplate;
+        if (cchToCopy > 0)
+        {
+            if (fOutputting)
+            {
+                try
+                {
+                    rStrDst.append(mStrScriptFullContent, offTemplate, cchToCopy);
+                }
+                catch (std::bad_alloc)
+                {
+                    hrc = E_OUTOFMEMORY;
+                    break;
+                }
+            }
+            offTemplate += cchToCopy;
+        }
+
+        /*
+         * Process placeholder.
+         */
+        if (offPlaceholder != RTCString::npos)
+        {
+            /*
+             * First we must find the end of the placeholder string.
+             */
+            const char *pszPlaceholder = mStrScriptFullContent.c_str() + offPlaceholder;
+            size_t      cchPlaceholder = sizeof(s_szPrefix) - 1;
+            char        ch;
+            while (   offPlaceholder + cchPlaceholder < cchTemplate
+                   && (ch = pszPlaceholder[cchPlaceholder]) != '\0'
+                   && (   ch == '_'
+                       || RT_C_IS_UPPER(ch)
+                       || RT_C_IS_DIGIT(ch)) )
+                cchPlaceholder++;
+
+            if (   offPlaceholder + cchPlaceholder < cchTemplate
+                && pszPlaceholder[cchPlaceholder] == '@')
+            {
+                cchPlaceholder++;
+                if (   offPlaceholder + cchPlaceholder < cchTemplate
+                    && pszPlaceholder[cchPlaceholder] == '@')
+                    cchPlaceholder++;
+            }
+
+            if (   pszPlaceholder[cchPlaceholder - 1] != '@'
+                || pszPlaceholder[cchPlaceholder - 2] != '@'
+                || (   strncmp(pszPlaceholder, s_szPrefixInsert, sizeof(s_szPrefixInsert) - 1) != 0
+                    && strncmp(pszPlaceholder, s_szPrefixCond,   sizeof(s_szPrefixCond)   - 1) != 0) )
+            {
+                hrc = mpSetError->setError(E_FAIL, mpSetError->tr("Malformed template placeholder '%.*s'"),
+                                           cchPlaceholder, pszPlaceholder);
+                break;
+            }
+
+            offTemplate += cchPlaceholder;
+
+            /*
+             * @@VBOX_INSERT_XXX@@:
+             */
+            if (strncmp(pszPlaceholder, s_szPrefixInsert, sizeof(s_szPrefixInsert) - 1) == 0)
+            {
+                /*
+                 * Get the placeholder value and add it to the output.
+                 */
+                RTCString strValue;
+                hrc = getReplacement(pszPlaceholder, cchPlaceholder, fOutputting, strValue);
+                if (SUCCEEDED(hrc))
+                {
+                    if (fOutputting)
+                    {
+                        try
+                        {
+                            rStrDst.append(strValue);
+                        }
+                        catch (std::bad_alloc)
+                        {
+                            hrc = E_OUTOFMEMORY;
+                            break;
+                        }
+                    }
+                }
+                else
+                    break;
+            }
+            /*
+             * @@VBOX_COND_END@@: Pop one item of the conditional stack.
+             */
+            else if (   cchPlaceholder == sizeof(s_szPrefixCondEnd) - 1U
+                     && strncmp(pszPlaceholder, s_szPrefixCondEnd, sizeof(s_szPrefixCondEnd) - 1U) == 0)
+            {
+                if (cConds > 0)
+                {
+                    cConds--;
+                    fOutputting = aConds[cConds].fSavedOutputting;
+                }
+                else
+                {
+                    hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
+                                                   mpSetError->tr("%s without @@VBOX_COND_XXX@@ at offset %zu (%#zx)"),
+                                                   s_szPrefixCondEnd, offPlaceholder, offPlaceholder);
+                    break;
+                }
+            }
+            /*
+             * @@VBOX_COND_XXX@@: Push the previous outputting state and combine it with the
+             *                    one from the condition.
+             */
+            else
+            {
+                Assert(strncmp(pszPlaceholder, s_szPrefixCond, sizeof(s_szPrefixCond) - 1) == 0);
+                if (cConds + 1 < RT_ELEMENTS(aConds))
+                {
+                    aConds[cConds].fSavedOutputting = fOutputting;
+                    bool fNewOutputting = fOutputting;
+                    hrc = getConditional(pszPlaceholder, cchPlaceholder, &fNewOutputting);
+                    if (SUCCEEDED(hrc))
+                        fOutputting = fOutputting && fNewOutputting;
+                    else
+                        break;
+                    cConds++;
+                }
+                else
+                {
+                    hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
+                                                   mpSetError->tr("Too deep conditional nesting at offset %zu (%#zx)"),
+                                                   offPlaceholder, offPlaceholder);
+                    break;
+                }
+            }
+        }
+
+        /*
+         * Done?
+         */
+        if (offTemplate >= cchTemplate)
+        {
+            if (cConds == 0)
+                return S_OK;
+            if (cConds == 1)
+                hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, mpSetError->tr("Missing @@VBOX_COND_END@@"));
+            else
+                hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, mpSetError->tr("Missing %u @@VBOX_COND_END@@"), cConds);
+            break;
+        }
+    }
+
+    /* failed */
+    rStrDst.setNull();
+    return hrc;
+}
+
+HRESULT UnattendedScriptTemplate::getReplacement(const char *pachPlaceholder, size_t cchPlaceholder,
+                                                 bool fOutputting, RTCString &rValue)
+{
+    /*
+     * Check for an escaping suffix.  Drop the '@@'.
+     */
+    size_t const cchFullPlaceholder = cchPlaceholder;
+    enum
+    {
+        kValueEscaping_None,
+        kValueEscaping_Bourne,
+        kValueEscaping_XML_Element,
+        kValueEscaping_XML_Attribute_Double_Quotes
+    } enmEscaping;
+
+#define PLACEHOLDER_ENDS_WITH(a_szSuffix) \
+        (   cchPlaceholder > sizeof(a_szSuffix) - 1U \
+         && memcmp(&pachPlaceholder[cchPlaceholder - sizeof(a_szSuffix) + 1U], a_szSuffix, sizeof(a_szSuffix) - 1U) == 0)
+    if (PLACEHOLDER_ENDS_WITH("_SH@@"))
+    {
+        cchPlaceholder -= 3 + 2;
+        enmEscaping = kValueEscaping_Bourne;
+    }
+    else if (PLACEHOLDER_ENDS_WITH("_ELEMENT@@"))
+    {
+        cchPlaceholder -= 8 + 2;
+        enmEscaping = kValueEscaping_XML_Element;
+    }
+    else if (PLACEHOLDER_ENDS_WITH("_ATTRIB_DQ@@"))
+    {
+        cchPlaceholder -= 10 + 2;
+        enmEscaping = kValueEscaping_XML_Attribute_Double_Quotes;
+    }
+    else
+    {
+        Assert(PLACEHOLDER_ENDS_WITH("@@"));
+        cchPlaceholder -= 2;
+        enmEscaping = kValueEscaping_None;
+    }
+
+    /*
+     * Resolve and escape the value.
+     */
+    HRESULT hrc;
+    try
+    {
+        switch (enmEscaping)
+        {
+            case kValueEscaping_None:
+                hrc = getUnescapedReplacement(pachPlaceholder, cchPlaceholder, cchFullPlaceholder, fOutputting, rValue);
+                if (SUCCEEDED(hrc))
+                    return hrc;
+                break;
+
+            case kValueEscaping_Bourne:
+            case kValueEscaping_XML_Element:
+            case kValueEscaping_XML_Attribute_Double_Quotes:
+            {
+                RTCString strUnescaped;
+                hrc = getUnescapedReplacement(pachPlaceholder, cchPlaceholder, cchFullPlaceholder, fOutputting, strUnescaped);
+                if (SUCCEEDED(hrc))
+                {
+                    switch (enmEscaping)
+                    {
+                        case kValueEscaping_Bourne:
+                        {
+                            const char * const papszArgs[2] = { strUnescaped.c_str(), NULL };
+                            char              *pszEscaped   = NULL;
+                            int vrc = RTGetOptArgvToString(&pszEscaped, papszArgs, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
+                            if (RT_SUCCESS(vrc))
+                            {
+                                try
+                                {
+                                    rValue = pszEscaped;
+                                    RTStrFree(pszEscaped);
+                                    return S_OK;
+                                }
+                                catch (std::bad_alloc)
+                                {
+                                    hrc = E_OUTOFMEMORY;
+                                }
+                                RTStrFree(pszEscaped);
+                            }
+                            break;
+                        }
+
+                        case kValueEscaping_XML_Element:
+                            rValue.printf("%RMes", strUnescaped.c_str());
+                            return S_OK;
+
+                        case kValueEscaping_XML_Attribute_Double_Quotes:
+                        {
+                            RTCString strTmp;
+                            strTmp.printf("%RMas", strUnescaped.c_str());
+                            rValue = RTCString(strTmp, 1, strTmp.length() - 2);
+                            return S_OK;
+                        }
+
+                        default:
+                            hrc = E_FAIL;
+                            break;
+                    }
+                }
+                break;
+            }
+
+            default:
+                AssertFailedStmt(hrc = E_FAIL);
+                break;
+        }
+    }
+    catch (std::bad_alloc)
+    {
+        hrc = E_OUTOFMEMORY;
+    }
+    rValue.setNull();
+    return hrc;
+}
+
+HRESULT UnattendedScriptTemplate::getUnescapedReplacement(const char *pachPlaceholder, size_t cchPlaceholder,
+                                                          size_t cchFullPlaceholder, bool fOutputting, RTCString &rValue)
+{
+    RT_NOREF(fOutputting);
+#define IS_PLACEHOLDER_MATCH(a_szMatch) \
+        (   cchPlaceholder == sizeof("@@VBOX_INSERT_" a_szMatch) - 1U \
+         && memcmp(pachPlaceholder, "@@VBOX_INSERT_" a_szMatch, sizeof("@@VBOX_INSERT_" a_szMatch) - 1U) == 0)
+
+    if (IS_PLACEHOLDER_MATCH("USER_LOGIN"))
+        rValue = mpUnattended->i_getUser();
+    else if (IS_PLACEHOLDER_MATCH("USER_PASSWORD"))
+        rValue = mpUnattended->i_getPassword();
+    else if (IS_PLACEHOLDER_MATCH("ROOT_PASSWORD"))
+        rValue = mpUnattended->i_getPassword();
+    else if (IS_PLACEHOLDER_MATCH("USER_FULL_NAME"))
+        rValue = mpUnattended->i_getFullUserName();
+    else if (IS_PLACEHOLDER_MATCH("PRODUCT_KEY"))
+        rValue = mpUnattended->i_getProductKey();
+    else if (IS_PLACEHOLDER_MATCH("POST_INSTALL_COMMAND"))
+        rValue = mpUnattended->i_getPostInstallCommand();
+    else if (IS_PLACEHOLDER_MATCH("IMAGE_INDEX"))
+        rValue.printf("%u", mpUnattended->i_getImageIndex());
+    else if (IS_PLACEHOLDER_MATCH("OS_ARCH"))
+        rValue = mpUnattended->i_isGuestOs64Bit() ? "amd64" : "x86";
+    else if (IS_PLACEHOLDER_MATCH("OS_ARCH2"))
+        rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "x86";
+    else if (IS_PLACEHOLDER_MATCH("OS_ARCH3"))
+        rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i386";
+    else if (IS_PLACEHOLDER_MATCH("OS_ARCH4"))
+        rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i486";
+    else if (IS_PLACEHOLDER_MATCH("OS_ARCH6"))
+        rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i686";
+    else if (IS_PLACEHOLDER_MATCH("TIME_ZONE_UX"))
+        rValue = mpUnattended->i_getTimeZoneInfo()
+               ? mpUnattended->i_getTimeZoneInfo()->pszUnixName : mpUnattended->i_getTimeZone();
+    else if (IS_PLACEHOLDER_MATCH("TIME_ZONE_WIN_NAME"))
+    {
+        PCRTTIMEZONEINFO pInfo = mpUnattended->i_getTimeZoneInfo();
+        if (pInfo)
+            rValue = pInfo->pszWindowsName ? pInfo->pszWindowsName : "GMT";
+        else
+            rValue = mpUnattended->i_getTimeZone();
+    }
+    else if (IS_PLACEHOLDER_MATCH("TIME_ZONE_WIN_INDEX"))
+    {
+        PCRTTIMEZONEINFO pInfo = mpUnattended->i_getTimeZoneInfo();
+        if (pInfo)
+            rValue.printf("%u", pInfo->idxWindows ? pInfo->idxWindows : 85 /*GMT*/);
+        else
+            rValue = mpUnattended->i_getTimeZone();
+    }
+    else if (IS_PLACEHOLDER_MATCH("LOCALE"))
+        rValue = mpUnattended->i_getLocale();
+    else if (IS_PLACEHOLDER_MATCH("COUNTRY"))
+        rValue = mpUnattended->i_getCountry();
+    else if (IS_PLACEHOLDER_MATCH("HOSTNAME_FQDN"))
+        rValue = mpUnattended->i_getHostname();
+    else if (IS_PLACEHOLDER_MATCH("HOSTNAME_WITHOUT_DOMAIN"))
+        rValue.assign(mpUnattended->i_getHostname(), 0, mpUnattended->i_getHostname().find("."));
+    else if (IS_PLACEHOLDER_MATCH("HOSTNAME_DOMAIN"))
+        rValue.assign(mpUnattended->i_getHostname(), mpUnattended->i_getHostname().find(".") + 1);
+    else
+    {
+        rValue.setNull();
+        return mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, mpSetError->tr("Unknown template placeholder '%.*s'"),
+                                        cchFullPlaceholder, pachPlaceholder);
+    }
+    return S_OK;
+#undef IS_PLACEHOLDER_MATCH
+}
+
+HRESULT UnattendedScriptTemplate::getConditional(const char *pachPlaceholder, size_t cchPlaceholder, bool *pfOutputting)
+{
+#define IS_PLACEHOLDER_MATCH(a_szMatch) \
+        (   cchPlaceholder == sizeof("@@VBOX_COND_" a_szMatch "@@") - 1U \
+         && memcmp(pachPlaceholder, "@@VBOX_COND_" a_szMatch "@@", sizeof("@@VBOX_COND_" a_szMatch "@@") - 1U) == 0)
+
+    /* Install guest additions: */
+    if (IS_PLACEHOLDER_MATCH("IS_INSTALLING_ADDITIONS"))
+        *pfOutputting = mpUnattended->i_getInstallGuestAdditions();
+    else if (IS_PLACEHOLDER_MATCH("IS_NOT_INSTALLING_ADDITIONS"))
+        *pfOutputting = !mpUnattended->i_getInstallGuestAdditions();
+    /* User == Administrator: */
+    else if (IS_PLACEHOLDER_MATCH("IS_USER_LOGIN_ADMINISTRATOR"))
+        *pfOutputting = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) == 0;
+    else if (IS_PLACEHOLDER_MATCH("IS_USER_LOGIN_NOT_ADMINISTRATOR"))
+        *pfOutputting = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) != 0;
+    /* Install TXS: */
+    else if (IS_PLACEHOLDER_MATCH("IS_INSTALLING_TEST_EXEC_SERVICE"))
+        *pfOutputting = mpUnattended->i_getInstallTestExecService();
+    else if (IS_PLACEHOLDER_MATCH("IS_NOT_INSTALLING_TEST_EXEC_SERVICE"))
+        *pfOutputting = !mpUnattended->i_getInstallTestExecService();
+    /* Post install command: */
+    else if (IS_PLACEHOLDER_MATCH("HAS_POST_INSTALL_COMMAND"))
+        *pfOutputting = mpUnattended->i_getPostInstallCommand().isNotEmpty();
+    else if (IS_PLACEHOLDER_MATCH("HAS_NO_POST_INSTALL_COMMAND"))
+        *pfOutputting = !mpUnattended->i_getPostInstallCommand().isNotEmpty();
+    /* Minimal installation: */
+    else if (IS_PLACEHOLDER_MATCH("IS_MINIMAL_INSTALLATION"))
+        *pfOutputting = mpUnattended->i_isMinimalInstallation();
+    else if (IS_PLACEHOLDER_MATCH("IS_NOT_MINIMAL_INSTALLATION"))
+        *pfOutputting = !mpUnattended->i_isMinimalInstallation();
+    else
+        return mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, mpSetError->tr("Unknown conditional placeholder '%.*s'"),
+                                        cchPlaceholder, pachPlaceholder);
+    return S_OK;
+#undef IS_PLACEHOLDER_MATCH
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+*
+*
+*  Implementation GeneralTextScript functions
+*
+*/
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT GeneralTextScript::parse()
+{
+    AssertReturn(!mfDataParsed, mpSetError->setErrorBoth(E_FAIL, VERR_WRONG_ORDER, "parse called more than once"));
+
+    /*
+     * Split the raw context into an array of lines.
+     */
+    try
+    {
+        mScriptContentByLines = mStrScriptFullContent.split("\n");
+    }
+    catch (std::bad_alloc)
+    {
+        mScriptContentByLines.clear();
+        return E_OUTOFMEMORY;
+    }
+
+    mfDataParsed = true;
+    return S_OK;
+}
+
+HRESULT GeneralTextScript::saveToString(Utf8Str &rStrDst)
+{
+    AssertReturn(mfDataParsed, mpSetError->setErrorBoth(E_FAIL, VERR_WRONG_ORDER, "saveToString() called before parse()"));
+
+    /*
+     * Calc the required size first.
+     */
+    size_t const cLines = mScriptContentByLines.size();
+    size_t       cbTotal = 1;
+    for (size_t iLine = 0; iLine < cLines; iLine++)
+        cbTotal = mScriptContentByLines[iLine].length() + 1;
+
+    /*
+     * Clear the output and try reserve sufficient space.
+     */
+    rStrDst.setNull();
+
+    int vrc = rStrDst.reserveNoThrow(cbTotal);
+    if (RT_FAILURE(vrc))
+        return E_OUTOFMEMORY;
+
+    /*
+     * Assemble the output.
+     */
+    for (size_t iLine = 0; iLine < cLines; iLine++)
+    {
+        try
+        {
+            rStrDst.append(mScriptContentByLines[iLine]);
+            rStrDst.append('\n');
+        }
+        catch (std::bad_alloc)
+        {
+            return E_OUTOFMEMORY;
+        }
+    }
+
+    return S_OK;
+}
+
+const RTCString &GeneralTextScript::getContentOfLine(size_t idxLine)
+{
+    if (idxLine < mScriptContentByLines.size())
+        return mScriptContentByLines[idxLine];
+    return Utf8Str::Empty;
+}
+
+
+HRESULT GeneralTextScript::setContentOfLine(size_t idxLine, const Utf8Str &rStrNewLine)
+{
+    AssertReturn(idxLine < mScriptContentByLines.size(),
+                 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE, "attempting to set line %zu when there are only %zu lines",
+                                          idxLine, mScriptContentByLines.size()));
+    try
+    {
+        mScriptContentByLines[idxLine] = rStrNewLine;
+    }
+    catch (std::bad_alloc)
+    {
+        return E_OUTOFMEMORY;
+    }
+    return S_OK;
+}
+
+vector<size_t> GeneralTextScript::findTemplate(const Utf8Str &rStrNeedle,
+                                               RTCString::CaseSensitivity enmCase /*= RTCString::CaseSensitive*/)
+{
+    vector<size_t> vecHitLineNumbers;
+    size_t const   cLines = mScriptContentByLines.size();
+    for (size_t iLine = 0; iLine < cLines; iLine++)
+        if (mScriptContentByLines[iLine].contains(rStrNeedle, enmCase))
+            vecHitLineNumbers.push_back(iLine);
+
+    return vecHitLineNumbers;
+}
+
+HRESULT GeneralTextScript::findAndReplace(size_t idxLine, const Utf8Str &rStrNeedle, const Utf8Str &rStrReplacement)
+{
+    AssertReturn(idxLine < mScriptContentByLines.size(),
+                 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE,
+                                          "attempting search&replace in line %zu when there are only %zu lines",
+                                          idxLine, mScriptContentByLines.size()));
+
+    RTCString &rDstString = mScriptContentByLines[idxLine];
+    size_t const offNeedle = rDstString.find(&rStrNeedle);
+    if (offNeedle != RTCString::npos)
+    {
+        try
+        {
+            RTCString strBefore(rDstString, 0, offNeedle);
+            RTCString strAfter(rDstString, offNeedle + rStrNeedle.length());
+            rDstString = strBefore;
+            strBefore.setNull();
+            rDstString.append(rStrReplacement);
+            rDstString.append(strAfter);
+        }
+        catch (std::bad_alloc)
+        {
+            return E_OUTOFMEMORY;
+        }
+    }
+    return S_OK;
+}
+
+HRESULT GeneralTextScript::appendToLine(size_t idxLine, const Utf8Str &rStrToAppend)
+{
+    AssertReturn(idxLine < mScriptContentByLines.size(),
+                 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE, "appending to line %zu when there are only %zu lines",
+                                          idxLine, mScriptContentByLines.size()));
+
+    try
+    {
+        mScriptContentByLines[idxLine].append(rStrToAppend);
+    }
+    catch (std::bad_alloc)
+    {
+        return E_OUTOFMEMORY;
+    }
+    return S_OK;
+}
+
+HRESULT GeneralTextScript::prependToLine(size_t idxLine, const Utf8Str &rStrToPrepend)
+{
+    AssertReturn(idxLine < mScriptContentByLines.size(),
+                 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE, "prepending to line %zu when there are only %zu lines",
+                                          idxLine, mScriptContentByLines.size()));
+
+    RTCString &rDstString = mScriptContentByLines[idxLine];
+    try
+    {
+        RTCString strCopy;
+        rDstString.swap(strCopy);
+        rDstString.reserve(strCopy.length() + rStrToPrepend.length() + 1);
+        rDstString = rStrToPrepend;
+        rDstString.append(strCopy);
+    }
+    catch (std::bad_alloc)
+    {
+        return E_OUTOFMEMORY;
+    }
+    return S_OK;
+}
+
+#if 0 /* Keeping this a reference */
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+*
+*
+*  Implementation UnattendedSUSEXMLScript functions
+*
+*/
+/////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT UnattendedSUSEXMLScript::parse()
+{
+    HRESULT hrc = UnattendedXMLScript::parse();
+    if (SUCCEEDED(hrc))
+    {
+        /*
+         * Check that we've got the right root element type.
+         */
+        const xml::ElementNode *pelmRoot = mDoc.getRootElement();
+        if (   pelmRoot
+            && strcmp(pelmRoot->getName(), "profile") == 0)
+        {
+            /*
+             * Work thought the sections.
+             */
+            try
+            {
+                LoopThruSections(pelmRoot);
+                hrc = S_OK;
+            }
+            catch (std::bad_alloc)
+            {
+                hrc = E_OUTOFMEMORY;
+            }
+        }
+        else if (pelmRoot)
+            hrc = mpSetError->setError(E_FAIL, mpSetError->tr("XML document root element is '%s' instead of 'profile'"),
+                                       pelmRoot->getName());
+        else
+            hrc = mpSetError->setError(E_FAIL, mpSetError->tr("Missing XML root element"));
+    }
+    return hrc;
+}
+
+HRESULT UnattendedSUSEXMLScript::setFieldInElement(xml::ElementNode *pElement, const DataId enmDataId, const Utf8Str &rStrValue)
+{
+    /*
+     * Don't set empty values.
+     */
+    if (rStrValue.isEmpty())
+    {
+        Utf8Str strProbableValue;
+        try
+        {
+            strProbableValue = createProbableValue(enmDataId, pElement);
+        }
+        catch (std::bad_alloc)
+        {
+            return E_OUTOFMEMORY;
+        }
+        return UnattendedXMLScript::setFieldInElement(pElement, enmDataId, strProbableValue);
+    }
+    return UnattendedXMLScript::setFieldInElement(pElement, enmDataId, rStrValue);
+}
+
+HRESULT UnattendedSUSEXMLScript::LoopThruSections(const xml::ElementNode *pelmRoot)
+{
+    xml::NodesLoop loopChildren(*pelmRoot);
+    const xml::ElementNode *pelmOuterLoop;
+    while ((pelmOuterLoop = loopChildren.forAllNodes()) != NULL)
+    {
+        const char *pcszElemName = pelmOuterLoop->getName();
+        if (!strcmp(pcszElemName, "users"))
+        {
+            xml::NodesLoop loopUsers(*pelmOuterLoop);
+            const xml::ElementNode *pelmUser;
+            while ((pelmUser = loopUsers.forAllNodes()) != NULL)
+            {
+                HRESULT hrc = HandleUserAccountsSection(pelmUser);
+                if (FAILED(hrc))
+                    return hrc;
+            }
+        }
+    }
+    return S_OK;
+}
+
+HRESULT UnattendedSUSEXMLScript::HandleUserAccountsSection(const xml::ElementNode *pelmSection)
+{
+    xml::NodesLoop loopUser(*pelmSection);
+
+    const xml::ElementNode *pelmCur;
+    while ((pelmCur = loopUser.forAllNodes()) != NULL)
+    {
+        const char *pszValue = pelmCur->getValue();
+#ifdef LOG_ENABLED
+        if (!RTStrCmp(pelmCur->getName(), "uid"))
+            LogRelFunc(("UnattendedSUSEXMLScript::HandleUserAccountsSection profile/users/%s/%s = %s\n",
+                        pelmSection->getName(), pelmCur->getName(), pszValue));
+#endif
+
+        if (!RTStrCmp(pszValue, "$homedir"))
+            mNodesForCorrectionMap.insert(make_pair(USERHOMEDIR_ID, pelmCur));
+
+        if (!RTStrCmp(pszValue, "$user"))
+            mNodesForCorrectionMap.insert(make_pair(USERNAME_ID, pelmCur));
+
+        if (!RTStrCmp(pszValue, "$password"))
+            mNodesForCorrectionMap.insert(make_pair(USERPASSWORD_ID, pelmCur));
+    }
+    return S_OK;
+}
+
+Utf8Str UnattendedSUSEXMLScript::createProbableValue(const DataId enmDataId, const xml::ElementNode *pCurElem)
+{
+    const xml::ElementNode *pElem = pCurElem;
+
+    switch (enmDataId)
+    {
+        case USERHOMEDIR_ID:
+//          if ((pElem = pElem->findChildElement("home")))
+//          {
+                return createProbableUserHomeDir(pElem);
+//          }
+            break;
+        default:
+            break;
+    }
+
+    return Utf8Str::Empty;
+}
+
+Utf8Str UnattendedSUSEXMLScript::createProbableUserHomeDir(const xml::ElementNode *pCurElem)
+{
+    Utf8Str strCalcValue;
+    const xml::ElementNode *pElem = pCurElem->findNextSibilingElement("username");
+    if (pElem)
+    {
+        const char *pszValue = pElem->getValue();
+        strCalcValue = "/home/";
+        strCalcValue.append(pszValue);
+    }
+
+    return strCalcValue;
+}
+#endif /* just for reference */
+
