VirtualBox

Changeset 22173 in vbox for trunk/src/VBox/Main/xml


Ignore:
Timestamp:
Aug 11, 2009 3:38:59 PM (16 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
50951
Message:

Main: the big XML settings rework. Move XML reading/writing out of interface implementation code into separate layer so it can handle individual settings versions in the future.

Location:
trunk/src/VBox/Main/xml
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/xml/Settings.cpp

    r21878 r22173  
    1919 */
    2020
    21 #include <iprt/err.h>
    22 #include <iprt/file.h>
    23 #include <iprt/lock.h>
    24 
    25 #include <libxml/tree.h>
    26 #include <libxml/parser.h>
    27 #include <libxml/globals.h>
    28 #include <libxml/xmlIO.h>
    29 #include <libxml/xmlsave.h>
    30 #include <libxml/uri.h>
    31 
    32 #include <libxml/xmlschemas.h>
    33 
    34 #include <libxslt/xsltInternals.h>
    35 #include <libxslt/transform.h>
    36 #include <libxslt/xsltutils.h>
    37 
    38 #include <list>
    39 
    40 // #include <string.h>
    41 
     21#include "VBox/com/string.h"
    4222#include "VBox/settings.h"
    43 
    44 #include "Logging.h"
    45 
    46 namespace settings
    47 {
    48 
    49 // Helpers
    50 ////////////////////////////////////////////////////////////////////////////////
    51 
    52 inline int sFromHex (char aChar)
    53 {
    54     if (aChar >= '0' && aChar <= '9')
    55         return aChar - '0';
    56     if (aChar >= 'A' && aChar <= 'F')
    57         return aChar - 'A' + 0xA;
    58     if (aChar >= 'a' && aChar <= 'f')
    59         return aChar - 'a' + 0xA;
    60 
    61     throw ENoConversion(com::Utf8StrFmt("'%c' (0x%02X) is not hex", aChar, aChar));
    62 }
    63 
    64 inline char sToHex (int aDigit)
    65 {
    66     return (aDigit < 0xA) ? aDigit + '0' : aDigit - 0xA + 'A';
    67 }
    68 
    69 static char *duplicate_chars (const char *that)
    70 {
    71     char *result = NULL;
    72     if (that != NULL)
    73     {
    74         size_t len = strlen (that) + 1;
    75         result = new char [len];
    76         if (result != NULL)
    77             memcpy (result, that, len);
    78     }
    79     return result;
    80 }
    81 
    82 //////////////////////////////////////////////////////////////////////////////
    83 // string -> type conversions
    84 //////////////////////////////////////////////////////////////////////////////
    85 
    86 uint64_t FromStringInteger (const char *aValue, bool aSigned,
    87                             int aBits, uint64_t aMin, uint64_t aMax)
    88 {
    89     if (aValue == NULL)
    90         throw ENoValue();
    91 
    92     switch (aBits)
    93     {
    94         case 8:
    95         case 16:
    96         case 32:
    97         case 64:
     23#include <iprt/xml_cpp.h>
     24#include <iprt/stream.h>
     25#include <iprt/ctype.h>
     26
     27#include "VirtualBoxXMLUtil.h"
     28
     29using namespace com;
     30using namespace settings;
     31
     32/**
     33 * Opaque data structore for ConfigFileBase (only declared
     34 * in header, defined only here).
     35 */
     36
     37struct ConfigFileBase::Data
     38{
     39    Data()
     40        : pParser(NULL),
     41          pDoc(NULL),
     42          pelmRoot(NULL)
     43    {}
     44
     45    ~Data()
     46    {
     47        cleanup();
     48    }
     49
     50    iprt::MiniString        strFilename;
     51    bool                    fFileExists;
     52
     53    xml::XmlFileParser      *pParser;
     54    xml::Document           *pDoc;
     55    xml::ElementNode        *pelmRoot;
     56    com::Utf8Str            strSettingsVersionFull;     // e.g. "1.7-linux"
     57    SettingsVersion_T       sv;                         // e.g. SETTINGS_VERSION_1_7
     58
     59    void cleanup()
     60    {
     61        if (pDoc)
     62        {
     63            delete pDoc;
     64            pDoc = NULL;
     65            pelmRoot = NULL;
     66        }
     67
     68        if (pParser)
     69        {
     70            delete pParser;
     71            pParser = NULL;
     72        }
     73    }
     74};
     75
     76/**
     77 * Private exception class (not in the header file) that makes
     78 * throwing xml::LogicError instances easier. That class is public
     79 * and should be caught by client code.
     80 */
     81class settings::ConfigFileError : public xml::LogicError
     82{
     83public:
     84    ConfigFileError(const ConfigFileBase *file, const char *pcszFormat, ...)
     85        : xml::LogicError()
     86    {
     87        va_list args;
     88        va_start(args, pcszFormat);
     89        Utf8StrFmtVA what(pcszFormat, args);
     90        va_end(args);
     91
     92        Utf8StrFmt str("Error reading %s. %s", file->m->strFilename.c_str(), what.c_str());
     93
     94        setWhat(str.c_str());
     95    }
     96};
     97
     98/**
     99 * Constructor. Allocates the XML internals.
     100 * @param strFilename
     101 */
     102ConfigFileBase::ConfigFileBase(const com::Utf8Str *pstrFilename)
     103    : m(new Data)
     104{
     105    m->fFileExists = false;
     106
     107    if (pstrFilename)
     108    {
     109        m->strFilename = *pstrFilename;
     110
     111        m->pParser = new xml::XmlFileParser;
     112        m->pDoc = new xml::Document;
     113        m->pParser->read(*pstrFilename,
     114                        *m->pDoc);
     115
     116        m->fFileExists = true;
     117
     118        m->pelmRoot = m->pDoc->getRootElement();
     119        if (!m->pelmRoot || !m->pelmRoot->nameEquals("VirtualBox"))
     120            throw ConfigFileError(this, N_("Root element in VirtualBox settings files must be \"VirtualBox\"."));
     121
     122        if (!(m->pelmRoot->getAttributeValue("version", m->strSettingsVersionFull)))
     123            throw ConfigFileError(this, N_("Required VirtualBox/version attribute is missing"));
     124
     125        m->sv = SettingsVersion_Null;
     126        if (m->strSettingsVersionFull.length() > 3)
     127        {
     128            const char *pcsz = m->strSettingsVersionFull.c_str();
     129            if (    (pcsz[0] == '1')
     130                && (pcsz[1] == '.')
     131                && (pcsz[3] == '-')
     132            )
     133            {
     134                if (pcsz[2] == '7')
     135                    m->sv = SettingsVersion_v1_7;
     136                else if (pcsz[2] == '8')
     137                    m->sv = SettingsVersion_v1_8;
     138            }
     139        }
     140
     141        if (m->sv == SettingsVersion_Null)
     142            throw ConfigFileError(this, N_("Cannot handle settings version '%s'"), m->strSettingsVersionFull.c_str());
     143    }
     144    else
     145    {
     146        m->strSettingsVersionFull = VBOX_XML_VERSION_FULL;
     147        m->sv = SettingsVersion_v1_8;
     148    }
     149}
     150
     151/**
     152 * Clean up.
     153 */
     154ConfigFileBase::~ConfigFileBase()
     155{
     156    if (m)
     157    {
     158        delete m;
     159        m = NULL;
     160    }
     161}
     162
     163/**
     164 * Helper function that parses a UUID in string form into
     165 * a com::Guid item. Since that uses an IPRT function which
     166 * does not accept "{}" characters around the UUID string,
     167 * we handle that here. Throws on errors.
     168 * @param guid
     169 * @param strUUID
     170 */
     171void ConfigFileBase::parseUUID(Guid &guid,
     172                               const Utf8Str &strUUID) const
     173{
     174    // {5f102a55-a51b-48e3-b45a-b28d33469488}
     175    // 01234567890123456789012345678901234567
     176    //           1         2         3
     177    if (    (strUUID[0] == '{')
     178         && (strUUID[37] == '}')
     179       )
     180        guid = strUUID.substr(1, 36).c_str();
     181    else
     182        guid = strUUID.c_str();
     183
     184    if (guid.isEmpty())
     185        throw ConfigFileError(this, N_("UUID \"%s\" has invalid format"), strUUID.c_str());
     186}
     187
     188/**
     189 * Parses the given string in str and attempts to treat it as an ISO
     190 * date/time stamp to put into timestamp. Throws on errors.
     191 * @param timestamp
     192 * @param str
     193 */
     194void ConfigFileBase::parseTimestamp(RTTIMESPEC &timestamp,
     195                                    const com::Utf8Str &str) const
     196{
     197    const char *pcsz = str.c_str();
     198        //  yyyy-mm-ddThh:mm:ss
     199        // "2009-07-10T11:54:03Z"
     200        //  01234567890123456789
     201        //            1
     202    if (str.length() > 19)
     203    {
     204        // timezone must either be unspecified or 'Z' for UTC
     205        if (    (pcsz[19])
     206             && (pcsz[19] != 'Z')
     207           )
     208            throw ConfigFileError(this, N_("Cannot handle ISO timestamp '%s': is not UTC date"), str.c_str());
     209
     210        int32_t yyyy;
     211        uint32_t mm, dd, hh, min, secs;
     212        if (    (pcsz[4] == '-')
     213             && (pcsz[7] == '-')
     214             && (pcsz[10] == 'T')
     215             && (pcsz[13] == ':')
     216             && (pcsz[16] == ':')
     217           )
     218        {
     219            int rc;
     220            if (    (RT_SUCCESS(rc = RTStrToInt32Ex(pcsz, NULL, 0, &yyyy)))
     221                        // could theoretically be negative but let's assume that nobody
     222                        // created virtual machines before the Christian era
     223                 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 5, NULL, 0, &mm)))
     224                 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 8, NULL, 0, &dd)))
     225                 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 11, NULL, 0, &hh)))
     226                 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 14, NULL, 0, &min)))
     227                 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 17, NULL, 0, &secs)))
     228               )
     229            {
     230                RTTIME time = { yyyy,
     231                                (uint8_t)mm,
     232                                0,
     233                                0,
     234                                (uint8_t)dd,
     235                                (uint8_t)hh,
     236                                (uint8_t)min,
     237                                (uint8_t)secs,
     238                                0,
     239                                RTTIME_FLAGS_TYPE_UTC };
     240                if (RTTimeNormalize(&time))
     241                    if (RTTimeImplode(&timestamp, &time))
     242                        return;
     243            }
     244
     245            throw ConfigFileError(this, N_("Cannot parse ISO timestamp '%s': runtime error, %Rra"), str.c_str(), rc);
     246        }
     247
     248        throw ConfigFileError(this, N_("Cannot parse ISO timestamp '%s': invalid format"), str.c_str());
     249    }
     250}
     251
     252/**
     253 * Helper to create a string for a RTTIMESPEC for writing out ISO timestamps.
     254 * @param stamp
     255 * @return
     256 */
     257com::Utf8Str ConfigFileBase::makeString(const RTTIMESPEC &stamp)
     258{
     259    RTTIME time;
     260    if (!RTTimeExplode(&time, &stamp))
     261        throw ConfigFileError(this, N_("Timespec %lld ms is invalid"), RTTimeSpecGetMilli(&stamp));
     262
     263    return Utf8StrFmt("%04ld-%02hd-%02hdT%02hd:%02hd:%02hdZ",
     264                      time.i32Year,
     265                      (uint16_t)time.u8Month,
     266                      (uint16_t)time.u8MonthDay,
     267                      (uint16_t)time.u8Hour,
     268                      (uint16_t)time.u8Minute,
     269                      (uint16_t)time.u8Second);
     270}
     271
     272com::Utf8Str ConfigFileBase::makeString(const Guid &guid)
     273{
     274    Utf8Str str("{");
     275    str.append(guid.toString());
     276    str.append("}");
     277    return str;
     278}
     279
     280/**
     281 * Helper method to read in an ExtraData subtree and stores its contents
     282 * in the given map of extradata items. Used for both main and machine
     283 * extradata (MainConfigFile and MachineConfigFile).
     284 * @param elmExtraData
     285 * @param map
     286 */
     287void ConfigFileBase::readExtraData(const xml::ElementNode &elmExtraData,
     288                                   ExtraDataItemsMap &map)
     289{
     290    xml::NodesLoop nlLevel4(elmExtraData);
     291    const xml::ElementNode *pelmExtraDataItem;
     292    while ((pelmExtraDataItem = nlLevel4.forAllNodes()))
     293    {
     294        if (pelmExtraDataItem->nameEquals("ExtraDataItem"))
     295        {
     296            // <ExtraDataItem name="GUI/LastWindowPostion" value="97,88,981,858"/>
     297            Utf8Str strName, strValue;
     298            if (    ((pelmExtraDataItem->getAttributeValue("name", strName)))
     299                 && ((pelmExtraDataItem->getAttributeValue("value", strValue)))
     300               )
     301                map[strName] = strValue;
     302            else
     303                throw ConfigFileError(this, N_("ExtraDataItem element must have name and value attributes"));
     304        }
     305        else
     306            throw ConfigFileError(this, N_("Invalid element %s in ExtraData section"), pelmExtraDataItem->getName());
     307    }
     308}
     309
     310/**
     311 * Reads <USBDeviceFilter> entries from under the given elmDeviceFilters node and
     312 * stores them in the given linklist. This is in ConfigFileBase because it's used
     313 * from both MainConfigFile (for host filters) and MachineConfigFile (for machine
     314 * filters).
     315 * @param elmDeviceFilters
     316 * @param ll
     317 */
     318void ConfigFileBase::readUSBDeviceFilters(const xml::ElementNode &elmDeviceFilters,
     319                                          USBDeviceFiltersList &ll)
     320{
     321    xml::NodesLoop nl1(elmDeviceFilters, "DeviceFilter");
     322    const xml::ElementNode *pelmLevel4Child;
     323    while ((pelmLevel4Child = nl1.forAllNodes()))
     324    {
     325        USBDeviceFilter flt;
     326        flt.action = USBDeviceFilterAction_Ignore;
     327        Utf8Str strAction;
     328        if (    (pelmLevel4Child->getAttributeValue("name", flt.strName))
     329             && (pelmLevel4Child->getAttributeValue("active", flt.fActive))
     330           )
     331        {
     332            pelmLevel4Child->getAttributeValue("vendorId", flt.strVendorId);
     333            pelmLevel4Child->getAttributeValue("productId", flt.strProductId);
     334            pelmLevel4Child->getAttributeValue("revision", flt.strRevision);
     335            pelmLevel4Child->getAttributeValue("manufacturer", flt.strManufacturer);
     336            pelmLevel4Child->getAttributeValue("product", flt.strProduct);
     337            pelmLevel4Child->getAttributeValue("serialNumber", flt.strSerialNumber);
     338            pelmLevel4Child->getAttributeValue("port", flt.strPort);
     339
     340            // the next 2 are irrelevant for host USB objects
     341            pelmLevel4Child->getAttributeValue("remote", flt.strRemote);
     342            pelmLevel4Child->getAttributeValue("maskedInterfaces", flt.ulMaskedInterfaces);
     343
     344            // action is only used with host USB objects
     345            if (pelmLevel4Child->getAttributeValue("action", strAction))
     346            {
     347                if (strAction == "Ignore")
     348                    flt.action = USBDeviceFilterAction_Ignore;
     349                else if (strAction == "Hold")
     350                    flt.action = USBDeviceFilterAction_Hold;
     351                else
     352                    throw ConfigFileError(this, N_("Invalid value %s in DeviceFilter/action attribute"), strAction.c_str());
     353            }
     354
     355            ll.push_back(flt);
     356        }
     357    }
     358}
     359
     360/**
     361 * Creates a new stub xml::Document in the m->pDoc member with the
     362 * root "VirtualBox" element set up. This is used by both
     363 * MainConfigFile and MachineConfigFile when writing out their XML.
     364 */
     365void ConfigFileBase::createStubDocument()
     366{
     367    Assert(m->pDoc == NULL);
     368    m->pDoc = new xml::Document;
     369
     370    m->pelmRoot = m->pDoc->createRootElement("VirtualBox");
     371    m->pelmRoot->setAttribute("xmlns", VBOX_XML_NAMESPACE);
     372
     373    const char *pcszVersion;
     374    switch (m->sv)
     375    {
     376        case SettingsVersion_v1_7:
     377            pcszVersion = "1.7";
     378        break;
     379        case SettingsVersion_v1_8:
     380            pcszVersion = "1.8";
     381        break;
     382    }
     383    m->pelmRoot->setAttribute("version", Utf8StrFmt("%s-%s",
     384                                                    pcszVersion,
     385                                                    VBOX_XML_PLATFORM));       // e.g. "linux"
     386}
     387
     388/**
     389 *
     390 * @param elmParent
     391 * @param me
     392 */
     393void ConfigFileBase::writeExtraData(xml::ElementNode &elmParent,
     394                                    const ExtraDataItemsMap &me)
     395{
     396    if (me.size())
     397    {
     398        xml::ElementNode *pelmExtraData = elmParent.createChild("ExtraData");
     399        for (ExtraDataItemsMap::const_iterator it = me.begin();
     400             it != me.end();
     401             ++it)
     402        {
     403            const Utf8Str &strName = it->first;
     404            const Utf8Str &strValue = it->second;
     405            xml::ElementNode *pelmThis = pelmExtraData->createChild("ExtraDataItem");
     406            pelmThis->setAttribute("name", strName);
     407            pelmThis->setAttribute("value", strValue);
     408        }
     409    }
     410}
     411
     412/**
     413 * Creates <DeviceFilter> nodes under the given parent element according to
     414 * the contents of the given USBDeviceFiltersList. If fHostMode is true,
     415 * this means that we're supposed to write filters for the IHost interface
     416 * (respect "action", omit "strRemote" and "ulMaskedInterfaces" in
     417 * struct USBDeviceFilter).
     418 * @param elmParent
     419 * @param ll
     420 * @param fHostMode
     421 */
     422void ConfigFileBase::writeUSBDeviceFilters(xml::ElementNode &elmParent,
     423                                           const USBDeviceFiltersList &ll,
     424                                           bool fHostMode)
     425{
     426    for (USBDeviceFiltersList::const_iterator it = ll.begin();
     427         it != ll.end();
     428         ++it)
     429    {
     430        const USBDeviceFilter &flt = *it;
     431        xml::ElementNode *pelmFilter = elmParent.createChild("DeviceFilter");
     432        pelmFilter->setAttribute("name", flt.strName);
     433        pelmFilter->setAttribute("active", flt.fActive);
     434        if (flt.strVendorId.length())
     435            pelmFilter->setAttribute("vendorId", flt.strVendorId);
     436        if (flt.strProductId.length())
     437            pelmFilter->setAttribute("productId", flt.strProductId);
     438        if (flt.strRevision.length())
     439            pelmFilter->setAttribute("revision", flt.strRevision);
     440        if (flt.strManufacturer.length())
     441            pelmFilter->setAttribute("manufacturer", flt.strManufacturer);
     442        if (flt.strProduct.length())
     443            pelmFilter->setAttribute("product", flt.strProduct);
     444        if (flt.strSerialNumber.length())
     445            pelmFilter->setAttribute("serialNumber", flt.strSerialNumber);
     446        if (flt.strPort.length())
     447            pelmFilter->setAttribute("port", flt.strPort);
     448
     449        if (fHostMode)
     450        {
     451            const char *pcsz =
     452                (flt.action == USBDeviceFilterAction_Ignore) ? "Ignore"
     453                : /*(flt.action == USBDeviceFilterAction_Hold) ?*/ "Hold";
     454            pelmFilter->setAttribute("action", pcsz);
     455        }
     456        else
     457        {
     458            if (flt.strRemote.length())
     459                pelmFilter->setAttribute("remote", flt.strRemote);
     460            if (flt.ulMaskedInterfaces)
     461                pelmFilter->setAttribute("maskedInterfaces", flt.ulMaskedInterfaces);
     462        }
     463    }
     464}
     465
     466/**
     467 * Cleans up memory allocated by the internal XML parser. To be called by
     468 * descendant classes when they're done analyzing the DOM tree to discard it.
     469 */
     470void ConfigFileBase::clearDocument()
     471{
     472    m->cleanup();
     473}
     474
     475/**
     476 * Returns true only if the underlying config file exists on disk;
     477 * either because the file has been loaded from disk, or it's been written
     478 * to disk, or both.
     479 * @return
     480 */
     481bool ConfigFileBase::fileExists()
     482{
     483    return m->fFileExists;
     484}
     485
     486/**
     487 * Reads one <MachineEntry> from the main VirtualBox.xml file.
     488 * @param elmMachineRegistry
     489 */
     490void MainConfigFile::readMachineRegistry(const xml::ElementNode &elmMachineRegistry)
     491{
     492    // <MachineEntry uuid="{ xxx }" src="   xxx "/>
     493    xml::NodesLoop nl1(elmMachineRegistry);
     494    const xml::ElementNode *pelmChild1;
     495    while ((pelmChild1 = nl1.forAllNodes()))
     496    {
     497        if (pelmChild1->nameEquals("MachineEntry"))
     498        {
     499            MachineRegistryEntry mre;
     500            Utf8Str strUUID;
     501            if (    ((pelmChild1->getAttributeValue("uuid", strUUID)))
     502                 && ((pelmChild1->getAttributeValue("src", mre.strSettingsFile)))
     503               )
     504            {
     505                parseUUID(mre.uuid, strUUID);
     506                llMachines.push_back(mre);
     507            }
     508            else
     509                throw ConfigFileError(this, N_("MachineEntry element must have uuid and src attributes"));
     510        }
     511        else
     512            throw ConfigFileError(this, N_("Invalid element %s in MachineRegistry section"), pelmChild1->getName());
     513    }
     514}
     515
     516/**
     517 * Reads a media registry entry from the main VirtualBox.xml file.
     518 * @param t
     519 * @param elmMedium
     520 * @param llMedia
     521 */
     522void MainConfigFile::readMedium(MediaType t,
     523                                const xml::ElementNode &elmMedium,  // MediaRegistry/HardDisks or a single HardDisk node if recursing
     524                                MediaList &llMedia)
     525{
     526    // <HardDisk uuid="{5471ecdb-1ddb-4012-a801-6d98e226868b}" location="/mnt/innotek-unix/vdis/Windows XP.vdi" format="VDI" type="Normal">
     527    settings::Medium med;
     528    Utf8Str strUUID;
     529    if (    (elmMedium.getAttributeValue("uuid", strUUID))
     530         && (elmMedium.getAttributeValue("location", med.strLocation))
     531       )
     532    {
     533        parseUUID(med.uuid, strUUID);
     534        elmMedium.getAttributeValue("Description", med.strDescription);       // optional
     535
     536        if (t == HardDisk)
     537        {
     538            if (!(elmMedium.getAttributeValue("format", med.strFormat)))
     539                throw ConfigFileError(this, N_("HardDisk element must have format attribute"));
     540
     541            if (!(elmMedium.getAttributeValue("autoReset", med.fAutoReset)))
     542                med.fAutoReset = false;
     543
     544            Utf8Str strType;
     545            if ((elmMedium.getAttributeValue("type", strType)))
     546            {
     547                if (strType == "Normal")
     548                    med.hdType = HardDiskType_Normal;
     549                else if (strType == "Immutable")
     550                    med.hdType = HardDiskType_Immutable;
     551                else if (strType == "Writethrough")
     552                    med.hdType = HardDiskType_Writethrough;
     553                else
     554                    throw ConfigFileError(this, N_("HardDisk/type attribute must be one of Normal, Immutable or Writethrough"));
     555            }
     556        }
     557
     558        // recurse to handle children
     559        xml::NodesLoop nl2(elmMedium);
     560        const xml::ElementNode *pelmHDChild;
     561        while ((pelmHDChild = nl2.forAllNodes()))
     562        {
     563            if (    t == HardDisk
     564                 && (pelmHDChild->nameEquals("HardDisk"))
     565               )
     566                // recurse with this element and push the child onto our current children list
     567                readMedium(t,
     568                           *pelmHDChild,
     569                           med.llChildren);
     570            else if (pelmHDChild->nameEquals("Property"))
     571            {
     572                Utf8Str strPropName, strPropValue;
     573                if (    (pelmHDChild->getAttributeValue("name", strPropName))
     574                     && (pelmHDChild->getAttributeValue("value", strPropValue))
     575                   )
     576                    med.properties[strPropName] = strPropValue;
     577                else
     578                    throw ConfigFileError(this, N_("HardDisk/Property element must have name and value attributes"));
     579            }
     580        }
     581
     582        llMedia.push_back(med);
     583    }
     584    else
     585        throw ConfigFileError(this, N_("%s element must have uuid and location attributes"), elmMedium.getName());
     586}
     587
     588/**
     589 * Reads in the entire <MediaRegistry> chunk.
     590 * @param elmMediaRegistry
     591 */
     592void MainConfigFile::readMediaRegistry(const xml::ElementNode &elmMediaRegistry)
     593{
     594    // <MachineEntry uuid="{ xxx }" src="   xxx "/>
     595    xml::NodesLoop nl1(elmMediaRegistry);
     596    const xml::ElementNode *pelmChild1;
     597    while ((pelmChild1 = nl1.forAllNodes()))
     598    {
     599        MediaType t = Error;
     600        if (pelmChild1->nameEquals("HardDisks"))
     601            t = HardDisk;
     602        else if (pelmChild1->nameEquals("DVDImages"))
     603            t = DVDImage;
     604        else if (pelmChild1->nameEquals("FloppyImages"))
     605            t = FloppyImage;
     606        else
     607            throw ConfigFileError(this, N_("Invalid element %s in MediaRegistry section"), pelmChild1->getName());
     608
     609        xml::NodesLoop nl1(*pelmChild1);
     610        const xml::ElementNode *pelmMedium;
     611        while ((pelmMedium = nl1.forAllNodes()))
     612        {
     613            if (    t == HardDisk
     614                 && (pelmMedium->nameEquals("HardDisk"))
     615               )
     616                readMedium(t,
     617                           *pelmMedium,
     618                           llHardDisks);      // list to append hard disk data to: the root list
     619            else if (    t == DVDImage
     620                      && (pelmMedium->nameEquals("Image"))
     621                    )
     622                readMedium(t,
     623                           *pelmMedium,
     624                           llDvdImages);      // list to append dvd images to: the root list
     625            else if (    t == FloppyImage
     626                      && (pelmMedium->nameEquals("Image"))
     627                    )
     628                readMedium(t,
     629                           *pelmMedium,
     630                           llFloppyImages);      // list to append floppy images to: the root list
     631        }
     632    }
     633}
     634
     635/**
     636 * Reads in the <DHCPServers> chunk.
     637 * @param elmDHCPServers
     638 */
     639void MainConfigFile::readDHCPServers(const xml::ElementNode &elmDHCPServers)
     640{
     641    xml::NodesLoop nl1(elmDHCPServers);
     642    const xml::ElementNode *pelmServer;
     643    while ((pelmServer = nl1.forAllNodes()))
     644    {
     645        if (pelmServer->nameEquals("DHCPServer"))
     646        {
     647            DHCPServer srv;
     648            if (    (pelmServer->getAttributeValue("networkName", srv.strNetworkName))
     649                 && (pelmServer->getAttributeValue("IPAddress", srv.strIPAddress))
     650                 && (pelmServer->getAttributeValue("networkMask", srv.strIPNetworkMask))
     651                 && (pelmServer->getAttributeValue("lowerIP", srv.strIPLower))
     652                 && (pelmServer->getAttributeValue("upperIP", srv.strIPUpper))
     653                 && (pelmServer->getAttributeValue("enabled", srv.fEnabled))
     654               )
     655                llDhcpServers.push_back(srv);
     656            else
     657                throw ConfigFileError(this, N_("DHCPServer element must have networkName, IPAddress, networkMask, lowerIP, upperIP and enabled attributes"));
     658        }
     659        else
     660            throw ConfigFileError(this, N_("Invalid element %s in DHCPServers section"), pelmServer->getName());
     661    }
     662}
     663
     664/**
     665 * Constructor.
     666 *
     667 * If pstrFilename is != NULL, this reads the given settings file into the member
     668 * variables and various substructures and lists. Otherwise, the member variables
     669 * are initialized with default values.
     670 *
     671 * Throws variants of xml::Error for I/O, XML and logical content errors, which
     672 * the caller should catch; if this constructor does not throw, then the file has
     673 * been successfully read.
     674 *
     675 * @param strFilename
     676 */
     677MainConfigFile::MainConfigFile(const Utf8Str *pstrFilename)
     678    : ConfigFileBase(pstrFilename)
     679{
     680    if (pstrFilename)
     681    {
     682        // the ConfigFileBase constructor has loaded the XML file, so now
     683        // we need only analyze what is in there
     684        xml::NodesLoop nlRootChildren(*m->pelmRoot);
     685        const xml::ElementNode *pelmRootChild;
     686        while ((pelmRootChild = nlRootChildren.forAllNodes()))
     687        {
     688            if (pelmRootChild->nameEquals("Global"))
     689            {
     690                xml::NodesLoop nlGlobalChildren(*pelmRootChild);
     691                const xml::ElementNode *pelmGlobalChild;
     692                while ((pelmGlobalChild = nlGlobalChildren.forAllNodes()))
     693                {
     694                    if (pelmGlobalChild->nameEquals("SystemProperties"))
     695                    {
     696                        pelmGlobalChild->getAttributeValue("defaultMachineFolder", systemProperties.strDefaultMachineFolder);
     697                        pelmGlobalChild->getAttributeValue("defaultHardDiskFolder", systemProperties.strDefaultHardDiskFolder);
     698                        pelmGlobalChild->getAttributeValue("defaultHardDiskFormat", systemProperties.strDefaultHardDiskFormat);
     699                        pelmGlobalChild->getAttributeValue("remoteDisplayAuthLibrary", systemProperties.strRemoteDisplayAuthLibrary);
     700                        pelmGlobalChild->getAttributeValue("webServiceAuthLibrary", systemProperties.strWebServiceAuthLibrary);
     701                        pelmGlobalChild->getAttributeValue("LogHistoryCount", systemProperties.ulLogHistoryCount);
     702                    }
     703                    else if (pelmGlobalChild->nameEquals("ExtraData"))
     704                        readExtraData(*pelmGlobalChild, mapExtraDataItems);
     705                    else if (pelmGlobalChild->nameEquals("MachineRegistry"))
     706                        readMachineRegistry(*pelmGlobalChild);
     707                    else if (pelmGlobalChild->nameEquals("MediaRegistry"))
     708                        readMediaRegistry(*pelmGlobalChild);
     709                    else if (pelmGlobalChild->nameEquals("NetserviceRegistry"))
     710                    {
     711                        xml::NodesLoop nlLevel4(*pelmGlobalChild);
     712                        const xml::ElementNode *pelmLevel4Child;
     713                        while ((pelmLevel4Child = nlLevel4.forAllNodes()))
     714                        {
     715                            if (pelmLevel4Child->nameEquals("DHCPServers"))
     716                                readDHCPServers(*pelmLevel4Child);
     717                            else
     718                                throw ConfigFileError(this, N_("Invalid element %s in NetserviceRegistry section"), pelmLevel4Child->getName());
     719                        }
     720                    }
     721                    else if (pelmGlobalChild->nameEquals("USBDeviceFilters"))
     722                        readUSBDeviceFilters(*pelmGlobalChild, host.llUSBDeviceFilters);
     723                    else
     724                        throw ConfigFileError(this, N_("Invalid element %s in Global section"), pelmGlobalChild->getName());
     725                }
     726            } // end if (pelmRootChild->nameEquals("Global"))
     727        }
     728
     729        clearDocument();
     730    }
     731}
     732
     733/**
     734 * Writes out a single <HardDisk> element for the given Medium structure
     735 * and recurses to write the child hard disks underneath. Called from
     736 * MainConfigFile::write().
     737 * @param elmMedium
     738 * @param m
     739 * @param level
     740 */
     741void MainConfigFile::writeHardDisk(xml::ElementNode &elmMedium,
     742                                   const Medium &m,
     743                                   uint32_t level)          // 0 for "root" call, incremented with each recursion
     744{
     745    xml::ElementNode *pelmHardDisk = elmMedium.createChild("HardDisk");
     746    pelmHardDisk->setAttribute("uuid", makeString(m.uuid));
     747    pelmHardDisk->setAttribute("location", m.strLocation);
     748    pelmHardDisk->setAttribute("format", m.strFormat);
     749    if (m.fAutoReset)
     750        pelmHardDisk->setAttribute("autoReset", m.fAutoReset);
     751    if (m.strDescription.length())
     752        pelmHardDisk->setAttribute("Description", m.strDescription);
     753
     754    for (PropertiesMap::const_iterator it = m.properties.begin();
     755         it != m.properties.end();
     756         ++it)
     757    {
     758        xml::ElementNode *pelmProp = pelmHardDisk->createChild("Property");
     759        pelmProp->setAttribute("name", it->first);
     760        pelmProp->setAttribute("value", it->second);
     761    }
     762
     763    // only for base hard disks, save the type
     764    if (level == 0)
     765    {
     766        const char *pcszType =
     767            m.hdType == HardDiskType_Normal ? "Normal" :
     768            m.hdType == HardDiskType_Immutable ? "Immutable" :
     769            /*m.hdType == HardDiskType_Writethrough ?*/ "Writethrough";
     770        pelmHardDisk->setAttribute("type", pcszType);
     771    }
     772
     773    for (MediaList::const_iterator it = m.llChildren.begin();
     774         it != m.llChildren.end();
     775         ++it)
     776    {
     777        // recurse for children
     778        writeHardDisk(*pelmHardDisk, // parent
     779                      *it,           // settings::Medium
     780                      ++level);      // recursion level
     781    }
     782}
     783
     784/**
     785 * Called from the IMachine interface to write out a machine config file. This
     786 * builds an XML DOM tree and writes it out to disk.
     787 */
     788void MainConfigFile::write()
     789{
     790    createStubDocument();
     791
     792    xml::ElementNode *pelmGlobal = m->pelmRoot->createChild("Global");
     793
     794    writeExtraData(*pelmGlobal, mapExtraDataItems);
     795
     796    xml::ElementNode *pelmMachineRegistry = pelmGlobal->createChild("MachineRegistry");
     797    for (MachinesRegistry::const_iterator it = llMachines.begin();
     798         it != llMachines.end();
     799         ++it)
     800    {
     801        // <MachineEntry uuid="{5f102a55-a51b-48e3-b45a-b28d33469488}" src="/mnt/innotek-unix/vbox-machines/Windows 5.1 XP 1 (Office 2003)/Windows 5.1 XP 1 (Office 2003).xml"/>
     802        const MachineRegistryEntry &mre = *it;
     803        xml::ElementNode *pelmMachineEntry = pelmMachineRegistry->createChild("MachineEntry");
     804        pelmMachineEntry->setAttribute("uuid", makeString(mre.uuid));
     805        pelmMachineEntry->setAttribute("src", mre.strSettingsFile);
     806    }
     807
     808    xml::ElementNode *pelmMediaRegistry = pelmGlobal->createChild("MediaRegistry");
     809
     810    xml::ElementNode *pelmHardDisks = pelmMediaRegistry->createChild("HardDisks");
     811    for (MediaList::const_iterator it = llHardDisks.begin();
     812         it != llHardDisks.end();
     813         ++it)
     814    {
     815        writeHardDisk(*pelmHardDisks, *it, 0);
     816    }
     817
     818    xml::ElementNode *pelmDVDImages = pelmMediaRegistry->createChild("DVDImages");
     819    for (MediaList::const_iterator it = llDvdImages.begin();
     820         it != llDvdImages.end();
     821         ++it)
     822    {
     823        const Medium &m = *it;
     824        xml::ElementNode *pelmMedium = pelmDVDImages->createChild("Image");
     825        pelmMedium->setAttribute("uuid", makeString(m.uuid));
     826        pelmMedium->setAttribute("location", m.strLocation);
     827        if (m.strDescription.length())
     828            pelmMedium->setAttribute("Description", m.strDescription);
     829    }
     830
     831    xml::ElementNode *pelmFloppyImages = pelmMediaRegistry->createChild("FloppyImages");
     832    for (MediaList::const_iterator it = llFloppyImages.begin();
     833         it != llFloppyImages.end();
     834         ++it)
     835    {
     836        const Medium &m = *it;
     837        xml::ElementNode *pelmMedium = pelmFloppyImages->createChild("Image");
     838        pelmMedium->setAttribute("uuid", makeString(m.uuid));
     839        pelmMedium->setAttribute("location", m.strLocation);
     840        if (m.strDescription.length())
     841            pelmMedium->setAttribute("Description", m.strDescription);
     842    }
     843
     844    xml::ElementNode *pelmNetserviceRegistry = pelmGlobal->createChild("NetserviceRegistry");
     845    xml::ElementNode *pelmDHCPServers = pelmNetserviceRegistry->createChild("DHCPServers");
     846    for (DHCPServersList::const_iterator it = llDhcpServers.begin();
     847         it != llDhcpServers.end();
     848         ++it)
     849    {
     850        const DHCPServer &d = *it;
     851        xml::ElementNode *pelmThis = pelmDHCPServers->createChild("DHCPServer");
     852        pelmThis->setAttribute("networkName", d.strNetworkName);
     853        pelmThis->setAttribute("IPAddress", d.strIPAddress);
     854        pelmThis->setAttribute("networkMask", d.strIPNetworkMask);
     855        pelmThis->setAttribute("lowerIP", d.strIPLower);
     856        pelmThis->setAttribute("upperIP", d.strIPUpper);
     857        pelmThis->setAttribute("enabled", (d.fEnabled) ? 1 : 0);        // too bad we chose 1 vs. 0 here
     858    }
     859
     860    xml::ElementNode *pelmSysProps = pelmGlobal->createChild("SystemProperties");
     861    if (systemProperties.strDefaultMachineFolder.length())
     862        pelmSysProps->setAttribute("defaultMachineFolder", systemProperties.strDefaultMachineFolder);
     863    if (systemProperties.strDefaultHardDiskFolder.length())
     864        pelmSysProps->setAttribute("defaultHardDiskFolder", systemProperties.strDefaultHardDiskFolder);
     865    if (systemProperties.strDefaultHardDiskFormat.length())
     866        pelmSysProps->setAttribute("defaultHardDiskFormat", systemProperties.strDefaultHardDiskFormat);
     867    if (systemProperties.strRemoteDisplayAuthLibrary.length())
     868        pelmSysProps->setAttribute("remoteDisplayAuthLibrary", systemProperties.strRemoteDisplayAuthLibrary);
     869    if (systemProperties.strWebServiceAuthLibrary.length())
     870        pelmSysProps->setAttribute("webServiceAuthLibrary", systemProperties.strWebServiceAuthLibrary);
     871    pelmSysProps->setAttribute("LogHistoryCount", systemProperties.ulLogHistoryCount);
     872
     873    writeUSBDeviceFilters(*pelmGlobal->createChild("USBDeviceFilters"),
     874                          host.llUSBDeviceFilters,
     875                          true);               // fHostMode
     876
     877    // now go write the XML
     878    xml::XmlFileWriter writer(*m->pDoc);
     879    writer.write(m->strFilename.c_str());
     880
     881    m->fFileExists = true;
     882
     883    clearDocument();
     884}
     885
     886/**
     887 * @param elmNetwork
     888 * @param ll
     889 */
     890void MachineConfigFile::readNetworkAdapters(const xml::ElementNode &elmNetwork,
     891                                            NetworkAdaptersList &ll)
     892{
     893    xml::NodesLoop nl1(elmNetwork, "Adapter");
     894    const xml::ElementNode *pelmAdapter;
     895    while ((pelmAdapter = nl1.forAllNodes()))
     896    {
     897        NetworkAdapter nic;
     898
     899        if (!pelmAdapter->getAttributeValue("slot", nic.ulSlot))
     900            throw ConfigFileError(this, N_("Required Adapter/slot attribute is missing"));
     901
     902        Utf8Str strTemp;
     903        if (pelmAdapter->getAttributeValue("type", strTemp))
     904        {
     905            if (strTemp == "Am79C970A")
     906                nic.type = NetworkAdapterType_Am79C970A;
     907            else if (strTemp == "Am79C973")
     908                nic.type = NetworkAdapterType_Am79C973;
     909            else if (strTemp == "82540EM")
     910                nic.type = NetworkAdapterType_I82540EM;
     911            else if (strTemp == "82543GC")
     912                nic.type = NetworkAdapterType_I82543GC;
     913            else if (strTemp == "82545EM")
     914                nic.type = NetworkAdapterType_I82545EM;
     915            else
     916                throw ConfigFileError(this, N_("Invalid value %s in Adapter/type attribute"), strTemp.c_str());
     917        }
     918
     919        pelmAdapter->getAttributeValue("enabled", nic.fEnabled);
     920        pelmAdapter->getAttributeValue("MACAddress", nic.strMACAddress);
     921        pelmAdapter->getAttributeValue("cable", nic.fCableConnected);
     922        pelmAdapter->getAttributeValue("speed", nic.ulLineSpeed);
     923        pelmAdapter->getAttributeValue("trace", nic.fTraceEnabled);
     924        pelmAdapter->getAttributeValue("tracefile", nic.strTraceFile);
     925
     926        const xml::ElementNode *pelmAdapterChild;
     927        if ((pelmAdapterChild = pelmAdapter->findChildElement("NAT")))
     928        {
     929            nic.mode = NetworkAttachmentType_NAT;
     930            pelmAdapterChild->getAttributeValue("name", nic.strName);    // optional network name
     931        }
     932        else if (    ((pelmAdapterChild = pelmAdapter->findChildElement("HostInterface")))
     933                  || ((pelmAdapterChild = pelmAdapter->findChildElement("BridgedInterface")))
     934                )
     935        {
     936            nic.mode = NetworkAttachmentType_Bridged;
     937            pelmAdapterChild->getAttributeValue("name", nic.strName);    // optional host interface name
     938        }
     939        else if ((pelmAdapterChild = pelmAdapter->findChildElement("InternalNetwork")))
     940        {
     941            nic.mode = NetworkAttachmentType_Internal;
     942            if (!pelmAdapterChild->getAttributeValue("name", nic.strName))    // required network name
     943                throw ConfigFileError(this, N_("Required 'name' element is missing under 'InternalNetwork' element"));
     944        }
     945        else if ((pelmAdapterChild = pelmAdapter->findChildElement("HostOnlyInterface")))
     946        {
     947            nic.mode = NetworkAttachmentType_HostOnly;
     948            if (!pelmAdapterChild->getAttributeValue("name", nic.strName))    // required network name
     949                throw ConfigFileError(this, N_("Required 'name' element is missing under 'HostOnlyInterface' element"));
     950        }
     951        // else: default is NetworkAttachmentType_Null
     952
     953        ll.push_back(nic);
     954    }
     955}
     956
     957/**
     958 * Called from MachineConfigFile::readHardware() to read serial port information.
     959 * @param elmUART
     960 * @param ll
     961 */
     962void MachineConfigFile::readSerialPorts(const xml::ElementNode &elmUART,
     963                                        SerialPortsList &ll)
     964{
     965    xml::NodesLoop nl1(elmUART, "Port");
     966    const xml::ElementNode *pelmPort;
     967    while ((pelmPort = nl1.forAllNodes()))
     968    {
     969        SerialPort port;
     970        if (!pelmPort->getAttributeValue("slot", port.ulSlot))
     971            throw ConfigFileError(this, N_("Required UART/Port/slot attribute is missing"));
     972
     973        // slot must be unique
     974        for (SerialPortsList::const_iterator it = ll.begin();
     975             it != ll.end();
     976             ++it)
     977            if ((*it).ulSlot == port.ulSlot)
     978                throw ConfigFileError(this, N_("UART/Port/slot attribute value %d is used twice, must be unique"), port.ulSlot);
     979
     980        if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
     981            throw ConfigFileError(this, N_("Required UART/Port/enabled attribute is missing"));
     982        if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
     983            throw ConfigFileError(this, N_("Required UART/Port/IOBase attribute is missing"));
     984        if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
     985            throw ConfigFileError(this, N_("Required UART/Port/IRQ attribute is missing"));
     986
     987        Utf8Str strPortMode;
     988        if (!pelmPort->getAttributeValue("hostMode", strPortMode))
     989            throw ConfigFileError(this, N_("Required UART/Port/hostMode attribute is missing"));
     990        if (strPortMode == "RawFile")
     991            port.portMode = PortMode_RawFile;
     992        else if (strPortMode == "HostPipe")
     993            port.portMode = PortMode_HostPipe;
     994        else if (strPortMode == "HostDevice")
     995            port.portMode = PortMode_HostDevice;
     996        else if (strPortMode == "Disconnected")
     997            port.portMode = PortMode_Disconnected;
     998        else
     999            throw ConfigFileError(this, N_("Invalid value %s in UART/Port/hostMode attribute"), strPortMode.c_str());
     1000
     1001        pelmPort->getAttributeValue("path", port.strPath);
     1002        pelmPort->getAttributeValue("server", port.fServer);
     1003
     1004        ll.push_back(port);
     1005    }
     1006}
     1007
     1008/**
     1009 * Called from MachineConfigFile::readHardware() to read parallel port information.
     1010 * @param elmLPT
     1011 * @param ll
     1012 */
     1013void MachineConfigFile::readParallelPorts(const xml::ElementNode &elmLPT,
     1014                                          ParallelPortsList &ll)
     1015{
     1016    xml::NodesLoop nl1(elmLPT, "Port");
     1017    const xml::ElementNode *pelmPort;
     1018    while ((pelmPort = nl1.forAllNodes()))
     1019    {
     1020        ParallelPort port;
     1021        if (!pelmPort->getAttributeValue("slot", port.ulSlot))
     1022            throw ConfigFileError(this, N_("Required LPT/Port/slot attribute is missing"));
     1023
     1024        // slot must be unique
     1025        for (ParallelPortsList::const_iterator it = ll.begin();
     1026             it != ll.end();
     1027             ++it)
     1028            if ((*it).ulSlot == port.ulSlot)
     1029                throw ConfigFileError(this, N_("LPT/Port/slot attribute value %d is used twice, must be unique"), port.ulSlot);
     1030
     1031        if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
     1032            throw ConfigFileError(this, N_("Required LPT/Port/enabled attribute is missing"));
     1033        if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
     1034            throw ConfigFileError(this, N_("Required LPT/Port/IOBase attribute is missing"));
     1035        if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
     1036            throw ConfigFileError(this, N_("Required LPT/Port/IRQ attribute is missing"));
     1037
     1038        pelmPort->getAttributeValue("path", port.strPath);
     1039
     1040        ll.push_back(port);
     1041    }
     1042}
     1043
     1044/**
     1045 * Called from MachineConfigFile::readHardware() to read guest property information.
     1046 * @param elmGuestProperties
     1047 * @param hw
     1048 */
     1049void MachineConfigFile::readGuestProperties(const xml::ElementNode &elmGuestProperties,
     1050                                            Hardware &hw)
     1051{
     1052    xml::NodesLoop nl1(elmGuestProperties, "GuestProperty");
     1053    const xml::ElementNode *pelmProp;
     1054    while ((pelmProp = nl1.forAllNodes()))
     1055    {
     1056        GuestProperty prop;
     1057        pelmProp->getAttributeValue("name", prop.strName);
     1058        pelmProp->getAttributeValue("value", prop.strValue);
     1059
     1060        pelmProp->getAttributeValue("timestamp", prop.timestamp);
     1061        pelmProp->getAttributeValue("flags", prop.strFlags);
     1062        hw.llGuestProperties.push_back(prop);
     1063    }
     1064
     1065    elmGuestProperties.getAttributeValue("notificationPatterns", hw.strNotificationPatterns);
     1066}
     1067
     1068/**
     1069 * Reads in a <Hardware> block and stores it in the given structure. Used
     1070 * both directly from readMachine and from readSnapshot, since snapshots
     1071 * have their own hardware sections.
     1072 * @param elmHardware
     1073 * @param hw
     1074 */
     1075void MachineConfigFile::readHardware(const xml::ElementNode &elmHardware,
     1076                                     Hardware &hw)
     1077{
     1078    elmHardware.getAttributeValue("version", hw.strVersion);
     1079            // defaults to 2 and is only written if != 2
     1080
     1081    xml::NodesLoop nl1(elmHardware);
     1082    const xml::ElementNode *pelmHwChild;
     1083    while ((pelmHwChild = nl1.forAllNodes()))
     1084    {
     1085        if (pelmHwChild->nameEquals("CPU"))
     1086        {
     1087            pelmHwChild->getAttributeValue("count", hw.cCPUs);
     1088
     1089            const xml::ElementNode *pelmCPUChild;
     1090            if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtEx")))
     1091                pelmCPUChild->getAttributeValue("enabled", hw.fHardwareVirt);
     1092            if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExNestedPaging")))
     1093                pelmCPUChild->getAttributeValue("enabled", hw.fNestedPaging);
     1094            if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExVPID")))
     1095                pelmCPUChild->getAttributeValue("enabled", hw.fVPID);
     1096            if ((pelmCPUChild = pelmHwChild->findChildElement("PAE")))
     1097                pelmCPUChild->getAttributeValue("enabled", hw.fPAE);
     1098        }
     1099        else if (pelmHwChild->nameEquals("Memory"))
     1100            pelmHwChild->getAttributeValue("RAMSize", hw.ulMemorySizeMB);
     1101        else if (pelmHwChild->nameEquals("Boot"))
     1102        {
     1103            xml::NodesLoop nl2(*pelmHwChild, "Order");
     1104            const xml::ElementNode *pelmOrder;
     1105            while ((pelmOrder = nl2.forAllNodes()))
     1106            {
     1107                uint32_t ulPos;
     1108                Utf8Str strDevice;
     1109                if (!pelmOrder->getAttributeValue("position", ulPos))
     1110                    throw ConfigFileError(this, N_("'Order' element lacks 'position' attribute"));
     1111                if (!pelmOrder->getAttributeValue("device", strDevice))
     1112                    throw ConfigFileError(this, N_("'Order' element lacks 'device' attribute"));
     1113                if (hw.mapBootOrder.find(ulPos) != hw.mapBootOrder.end())
     1114                    throw ConfigFileError(this, N_("Value %d of 'position' attribute in 'Order' element is not unique"), ulPos);
     1115
     1116                DeviceType_T type;
     1117                if (strDevice == "None")
     1118                    type = DeviceType_Null;
     1119                else if (strDevice == "Floppy")
     1120                    type = DeviceType_Floppy;
     1121                else if (strDevice == "DVD")
     1122                    type = DeviceType_DVD;
     1123                else if (strDevice == "HardDisk")
     1124                    type = DeviceType_HardDisk;
     1125                else if (strDevice == "Network")
     1126                    type = DeviceType_Network;
     1127                else
     1128                    throw ConfigFileError(this, N_("Invalid value %s in Boot/Order/device attribute"), strDevice.c_str());
     1129                hw.mapBootOrder[ulPos] = type;
     1130            }
     1131        }
     1132        else if (pelmHwChild->nameEquals("Display"))
     1133        {
     1134            pelmHwChild->getAttributeValue("VRAMSize", hw.ulVRAMSizeMB);
     1135            pelmHwChild->getAttributeValue("monitorCount", hw.cMonitors);
     1136            pelmHwChild->getAttributeValue("accelerate3D", hw.fAccelerate3D);
     1137            pelmHwChild->getAttributeValue("accelerate2DVideo", hw.fAccelerate2DVideo);
     1138        }
     1139        else if (pelmHwChild->nameEquals("RemoteDisplay"))
     1140        {
     1141            pelmHwChild->getAttributeValue("enabled", hw.vrdpSettings.fEnabled);
     1142            pelmHwChild->getAttributeValue("port", hw.vrdpSettings.ulPort);
     1143            pelmHwChild->getAttributeValue("netAddress", hw.vrdpSettings.strNetAddress);
     1144
     1145            Utf8Str strAuthType;
     1146            if (pelmHwChild->getAttributeValue("authType", strAuthType))
     1147            {
     1148                if (strAuthType == "Null")
     1149                    hw.vrdpSettings.authType = VRDPAuthType_Null;
     1150                else if (strAuthType == "Guest")
     1151                    hw.vrdpSettings.authType = VRDPAuthType_Guest;
     1152                else if (strAuthType == "External")
     1153                    hw.vrdpSettings.authType = VRDPAuthType_External;
     1154                else
     1155                    throw ConfigFileError(this, N_("Invalid value %s in RemoteDisplay/authType attribute"), strAuthType.c_str());
     1156            }
     1157
     1158            pelmHwChild->getAttributeValue("authTimeout", hw.vrdpSettings.ulAuthTimeout);
     1159            pelmHwChild->getAttributeValue("allowMultiConnection", hw.vrdpSettings.fAllowMultiConnection);
     1160            pelmHwChild->getAttributeValue("reuseSingleConnection", hw.vrdpSettings.fReuseSingleConnection);
     1161        }
     1162        else if (pelmHwChild->nameEquals("BIOS"))
     1163        {
     1164            const xml::ElementNode *pelmBIOSChild;
     1165            if ((pelmBIOSChild = pelmHwChild->findChildElement("ACPI")))
     1166                pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fACPIEnabled);
     1167            if ((pelmBIOSChild = pelmHwChild->findChildElement("IOAPIC")))
     1168                pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fIOAPICEnabled);
     1169            if ((pelmBIOSChild = pelmHwChild->findChildElement("Logo")))
     1170            {
     1171                pelmBIOSChild->getAttributeValue("fadeIn", hw.biosSettings.fLogoFadeIn);
     1172                pelmBIOSChild->getAttributeValue("fadeOut", hw.biosSettings.fLogoFadeOut);
     1173                pelmBIOSChild->getAttributeValue("displayTime", hw.biosSettings.ulLogoDisplayTime);
     1174                pelmBIOSChild->getAttributeValue("imagePath", hw.biosSettings.strLogoImagePath);
     1175            }
     1176            if ((pelmBIOSChild = pelmHwChild->findChildElement("BootMenu")))
     1177            {
     1178                Utf8Str strBootMenuMode;
     1179                if (pelmBIOSChild->getAttributeValue("mode", strBootMenuMode))
     1180                {
     1181                    if (strBootMenuMode == "Disabled")
     1182                        hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_Disabled;
     1183                    else if (strBootMenuMode == "MenuOnly")
     1184                        hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MenuOnly;
     1185                    else if (strBootMenuMode == "MessageAndMenu")
     1186                        hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MessageAndMenu;
     1187                    else
     1188                        throw ConfigFileError(this, N_("Invalid value %s in BootMenu/mode attribute"), strBootMenuMode.c_str());
     1189                }
     1190            }
     1191            if ((pelmBIOSChild = pelmHwChild->findChildElement("PXEDebug")))
     1192                pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fPXEDebugEnabled);
     1193            if ((pelmBIOSChild = pelmHwChild->findChildElement("TimeOffset")))
     1194                pelmBIOSChild->getAttributeValue("value", hw.biosSettings.llTimeOffset);
     1195        }
     1196        else if (pelmHwChild->nameEquals("DVDDrive"))
     1197        {
     1198            const xml::ElementNode *pDriveChild;
     1199            pelmHwChild->getAttributeValue("passthrough", hw.dvdDrive.fPassThrough);
     1200            Utf8Str strTmp;
     1201            if (    ((pDriveChild = pelmHwChild->findChildElement("Image")))
     1202                 && (pDriveChild->getAttributeValue("uuid", strTmp))
     1203               )
     1204                parseUUID(hw.dvdDrive.uuid, strTmp);
     1205            else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
     1206                pDriveChild->getAttributeValue("src", hw.dvdDrive.strHostDriveSrc);
     1207        }
     1208        else if (pelmHwChild->nameEquals("FloppyDrive"))
     1209        {
     1210            const xml::ElementNode *pDriveChild;
     1211            pelmHwChild->getAttributeValue("enabled", hw.floppyDrive.fEnabled);
     1212            Utf8Str strTmp;
     1213            if (    ((pDriveChild = pelmHwChild->findChildElement("Image")))
     1214                 && (pDriveChild->getAttributeValue("uuid", strTmp))
     1215               )
     1216                parseUUID(hw.floppyDrive.uuid, strTmp);
     1217            else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
     1218                pDriveChild->getAttributeValue("src", hw.floppyDrive.strHostDriveSrc);
     1219        }
     1220        else if (pelmHwChild->nameEquals("USBController"))
     1221        {
     1222            pelmHwChild->getAttributeValue("enabled", hw.usbController.fEnabled);
     1223            pelmHwChild->getAttributeValue("enabledEhci", hw.usbController.fEnabledEHCI);
     1224
     1225            readUSBDeviceFilters(*pelmHwChild,
     1226                                 hw.usbController.llDeviceFilters);
     1227        }
     1228        else if (pelmHwChild->nameEquals("Network"))
     1229            readNetworkAdapters(*pelmHwChild, hw.llNetworkAdapters);
     1230        else if (pelmHwChild->nameEquals("UART"))
     1231            readSerialPorts(*pelmHwChild, hw.llSerialPorts);
     1232        else if (pelmHwChild->nameEquals("LPT"))
     1233            readParallelPorts(*pelmHwChild, hw.llParallelPorts);
     1234        else if (pelmHwChild->nameEquals("AudioAdapter"))
     1235        {
     1236            pelmHwChild->getAttributeValue("enabled", hw.audioAdapter.fEnabled);
     1237
     1238            Utf8Str strTemp;
     1239            if (pelmHwChild->getAttributeValue("controller", strTemp))
     1240            {
     1241                if (strTemp == "SB16")
     1242                    hw.audioAdapter.controllerType = AudioControllerType_SB16;
     1243                else if (strTemp == "AC97")
     1244                    hw.audioAdapter.controllerType = AudioControllerType_AC97;
     1245                else
     1246                    throw ConfigFileError(this, N_("Invalid value %s in AudioAdapter/controller attribute"), strTemp.c_str());
     1247            }
     1248            if (pelmHwChild->getAttributeValue("driver", strTemp))
     1249            {
     1250                if (strTemp == "Null")
     1251                    hw.audioAdapter.driverType = AudioDriverType_Null;
     1252                else if (strTemp == "WinMM")
     1253                    hw.audioAdapter.driverType = AudioDriverType_WinMM;
     1254                else if (strTemp == "DirectSound")
     1255                    hw.audioAdapter.driverType = AudioDriverType_DirectSound;
     1256                else if (strTemp == "SolAudio")
     1257                    hw.audioAdapter.driverType = AudioDriverType_SolAudio;
     1258                else if (strTemp == "ALSA")
     1259                    hw.audioAdapter.driverType = AudioDriverType_ALSA;
     1260                else if (strTemp == "Pulse")
     1261                    hw.audioAdapter.driverType = AudioDriverType_Pulse;
     1262                else if (strTemp == "OSS")
     1263                    hw.audioAdapter.driverType = AudioDriverType_OSS;
     1264                else if (strTemp == "CoreAudio")
     1265                    hw.audioAdapter.driverType = AudioDriverType_CoreAudio;
     1266                else if (strTemp == "MMPM")
     1267                    hw.audioAdapter.driverType = AudioDriverType_MMPM;
     1268                else
     1269                    throw ConfigFileError(this, N_("Invalid value %s in AudioAdapter/driver attribute"), strTemp.c_str());
     1270            }
     1271        }
     1272        else if (pelmHwChild->nameEquals("SharedFolders"))
     1273        {
     1274            xml::NodesLoop nl2(*pelmHwChild, "SharedFolder");
     1275            const xml::ElementNode *pelmFolder;
     1276            while ((pelmFolder = nl2.forAllNodes()))
     1277            {
     1278                SharedFolder sf;
     1279                pelmFolder->getAttributeValue("name", sf.strName);
     1280                pelmFolder->getAttributeValue("hostPath", sf.strHostPath);
     1281                pelmFolder->getAttributeValue("writable", sf.fWritable);
     1282                hw.llSharedFolders.push_back(sf);
     1283            }
     1284        }
     1285        else if (pelmHwChild->nameEquals("Clipboard"))
     1286        {
     1287            Utf8Str strTemp;
     1288            if (pelmHwChild->getAttributeValue("mode", strTemp))
     1289            {
     1290                if (strTemp == "Disabled")
     1291                    hw.clipboardMode = ClipboardMode_Disabled;
     1292                else if (strTemp == "HostToGuest")
     1293                    hw.clipboardMode = ClipboardMode_HostToGuest;
     1294                else if (strTemp == "GuestToHost")
     1295                    hw.clipboardMode = ClipboardMode_GuestToHost;
     1296                else if (strTemp == "Bidirectional")
     1297                    hw.clipboardMode = ClipboardMode_Bidirectional;
     1298                else
     1299                    throw ConfigFileError(this, N_("Invalid value %s in Clipbord/mode attribute"), strTemp.c_str());
     1300            }
     1301        }
     1302        else if (pelmHwChild->nameEquals("Guest"))
     1303        {
     1304            pelmHwChild->getAttributeValue("memoryBalloonSize", hw.ulMemoryBalloonSize);
     1305            pelmHwChild->getAttributeValue("statisticsUpdateInterval", hw.ulStatisticsUpdateInterval);
     1306        }
     1307        else if (pelmHwChild->nameEquals("GuestProperties"))
     1308            readGuestProperties(*pelmHwChild, hw);
     1309        else
     1310            throw ConfigFileError(this, N_("Invalid element %s in Hardware section"), pelmHwChild->getName());
     1311    }
     1312
     1313    if (hw.ulMemorySizeMB == (uint32_t)-1)
     1314        throw ConfigFileError(this, N_("Required Memory/RAMSize element/attribute is missing"));
     1315}
     1316
     1317/**
     1318 * Reads in a <StorageControllers> block and stores it in the given Storage structure.
     1319 * Used both directly from readMachine and from readSnapshot, since snapshots
     1320 * have their own storage controllers sections.
     1321 *
     1322 * @param elmStorageControllers
     1323 */
     1324void MachineConfigFile::readStorageControllers(const xml::ElementNode &elmStorageControllers,
     1325                                               Storage &strg)
     1326{
     1327    xml::NodesLoop nlStorageControllers(elmStorageControllers, "StorageController");
     1328    const xml::ElementNode *pelmController;
     1329    while ((pelmController = nlStorageControllers.forAllNodes()))
     1330    {
     1331        StorageController sctl;
     1332
     1333        if (!pelmController->getAttributeValue("name", sctl.strName))
     1334            throw ConfigFileError(this, N_("Required StorageController/name attribute is missing"));
     1335        Utf8Str strType;
     1336        if (!pelmController->getAttributeValue("type", strType))
     1337            throw ConfigFileError(this, N_("Required StorageController/type attribute is missing"));
     1338
     1339        if (strType == "AHCI")
     1340        {
     1341            sctl.storageBus = StorageBus_SATA;
     1342            sctl.controllerType = StorageControllerType_IntelAhci;
     1343        }
     1344        else if (strType == "LsiLogic")
     1345        {
     1346            sctl.storageBus = StorageBus_SCSI;
     1347            sctl.controllerType = StorageControllerType_LsiLogic;
     1348        }
     1349        else if (strType == "BusLogic")
     1350        {
     1351            sctl.storageBus = StorageBus_SCSI;
     1352            sctl.controllerType = StorageControllerType_BusLogic;
     1353        }
     1354        else if (strType == "PIIX3")
     1355        {
     1356            sctl.storageBus = StorageBus_IDE;
     1357            sctl.controllerType = StorageControllerType_PIIX3;
     1358        }
     1359        else if (strType == "PIIX4")
     1360        {
     1361            sctl.storageBus = StorageBus_IDE;
     1362            sctl.controllerType = StorageControllerType_PIIX4;
     1363        }
     1364        else if (strType == "ICH6")
     1365        {
     1366            sctl.storageBus = StorageBus_IDE;
     1367            sctl.controllerType = StorageControllerType_ICH6;
     1368        }
     1369        else
     1370            throw ConfigFileError(this, N_("Invalid value %s for StorageController/type attribute"), strType.c_str());
     1371
     1372        pelmController->getAttributeValue("PortCount", sctl.ulPortCount);
     1373
     1374        pelmController->getAttributeValue("IDE0MasterEmulationPort", sctl.lIDE0MasterEmulationPort);
     1375        pelmController->getAttributeValue("IDE0SlaveEmulationPort", sctl.lIDE0SlaveEmulationPort);
     1376        pelmController->getAttributeValue("IDE1MasterEmulationPort", sctl.lIDE1MasterEmulationPort);
     1377        pelmController->getAttributeValue("IDE1SlaveEmulationPort", sctl.lIDE1SlaveEmulationPort);
     1378
     1379        xml::NodesLoop nlAttached(*pelmController, "AttachedDevice");
     1380        const xml::ElementNode *pelmAttached;
     1381        while ((pelmAttached = nlAttached.forAllNodes()))
     1382        {
     1383            AttachedDevice att;
     1384
     1385            Utf8Str strTemp;
     1386            pelmAttached->getAttributeValue("type", strTemp);
     1387            if (strTemp != "HardDisk")
     1388                throw ConfigFileError(this, N_("AttachedDevice element must have 'HardDisk' type"));
     1389
     1390            const xml::ElementNode *pelmImage;
     1391            if (!(pelmImage = pelmAttached->findChildElement("Image")))
     1392                throw ConfigFileError(this, N_("Required AttachedDevice/Image element is missing"));
     1393
     1394            if (!pelmImage->getAttributeValue("uuid", strTemp))
     1395                throw ConfigFileError(this, N_("Required AttachedDevice/Image/uuid attributeis missing"));
     1396            parseUUID(att.uuid, strTemp);
     1397
     1398            if (!pelmAttached->getAttributeValue("port", att.lPort))
     1399                throw ConfigFileError(this, N_("Required AttachedDevice/port attribute is missing"));
     1400            if (!pelmAttached->getAttributeValue("device", att.lDevice))
     1401                throw ConfigFileError(this, N_("Required AttachedDevice/device attribute is missing"));
     1402
     1403            sctl.llAttachedDevices.push_back(att);
     1404        }
     1405
     1406        strg.llStorageControllers.push_back(sctl);
     1407    }
     1408}
     1409
     1410/**
     1411 * Called initially for the <Snapshot> element under <Machine>, if present,
     1412 * to store the snapshot's data into the given Snapshot structure (which is
     1413 * then the one in the Machine struct). This might then recurse if
     1414 * a <Snapshots> (plural) element is found in the snapshot, which should
     1415 * contain a list of child snapshots; such lists are maintained in the
     1416 * Snapshot structure.
     1417 *
     1418 * @param elmSnapshot
     1419 * @param snap
     1420 */
     1421void MachineConfigFile::readSnapshot(const xml::ElementNode &elmSnapshot,
     1422                                     Snapshot &snap)
     1423{
     1424    Utf8Str strTemp;
     1425
     1426    if (!elmSnapshot.getAttributeValue("uuid", strTemp))
     1427        throw ConfigFileError(this, N_("Required Snapshot/uuid attribute is missing"));
     1428    parseUUID(snap.uuid, strTemp);
     1429
     1430    if (!elmSnapshot.getAttributeValue("name", snap.strName))
     1431        throw ConfigFileError(this, N_("Required Snapshot/name attribute is missing"));
     1432
     1433    elmSnapshot.getAttributeValue("Description", snap.strDescription);
     1434
     1435    if (!elmSnapshot.getAttributeValue("timeStamp", strTemp))
     1436        throw ConfigFileError(this, N_("Required Snapshot/timeStamp attribute is missing"));
     1437    parseTimestamp(snap.timestamp, strTemp);
     1438
     1439    elmSnapshot.getAttributeValue("stateFile", snap.strStateFile);      // online snapshots only
     1440
     1441    xml::NodesLoop nlSnapshotChildren(elmSnapshot);
     1442    const xml::ElementNode *pelmSnapshotChild;
     1443    while ((pelmSnapshotChild = nlSnapshotChildren.forAllNodes()))
     1444    {
     1445        if (pelmSnapshotChild->nameEquals("Description"))
     1446            snap.strDescription = pelmSnapshotChild->getValue();
     1447        else if (pelmSnapshotChild->nameEquals("Hardware"))
     1448            readHardware(*pelmSnapshotChild, snap.hardware);
     1449        else if (pelmSnapshotChild->nameEquals("StorageControllers"))
     1450            readStorageControllers(*pelmSnapshotChild, snap.storage);
     1451        else if (pelmSnapshotChild->nameEquals("Snapshots"))
     1452        {
     1453            xml::NodesLoop nlChildSnapshots(*pelmSnapshotChild);
     1454            const xml::ElementNode *pelmChildSnapshot;
     1455            while ((pelmChildSnapshot = nlChildSnapshots.forAllNodes()))
     1456            {
     1457                if (pelmChildSnapshot->nameEquals("Snapshot"))
     1458                {
     1459                    Snapshot child;
     1460                    readSnapshot(*pelmChildSnapshot, child);
     1461                    snap.llChildSnapshots.push_back(child);
     1462                }
     1463                else
     1464                    throw ConfigFileError(this, N_("Invalid element %s under Snapshots element"), pelmChildSnapshot->getName());
     1465            }
     1466        }
     1467        else
     1468            throw ConfigFileError(this, N_("Invalid element %s under Snapshot element"), pelmSnapshotChild->getName());
     1469    }
     1470}
     1471
     1472/**
     1473 * Called from the constructor to actually read in the Machine element
     1474 * of a machine config file.
     1475 * @param elmMachine
     1476 */
     1477void MachineConfigFile::readMachine(const xml::ElementNode &elmMachine)
     1478{
     1479    Utf8Str strUUID;
     1480    if (    (elmMachine.getAttributeValue("uuid", strUUID))
     1481         && (elmMachine.getAttributeValue("name", strName))
     1482       )
     1483    {
     1484        parseUUID(uuid, strUUID);
     1485
     1486        if (!elmMachine.getAttributeValue("nameSync", fNameSync))
     1487            fNameSync = true;
     1488
     1489        Utf8Str str;
     1490        elmMachine.getAttributeValue("Description", strDescription);
     1491        elmMachine.getAttributeValue("OSType", strOsType);
     1492        elmMachine.getAttributeValue("stateFile", strStateFile);
     1493        if (elmMachine.getAttributeValue("currentSnapshot", str))
     1494            parseUUID(uuidCurrentSnapshot, str);
     1495        elmMachine.getAttributeValue("snapshotFolder", strSnapshotFolder);
     1496        if (!elmMachine.getAttributeValue("currentStateModified", fCurrentStateModified))
     1497            fCurrentStateModified = true;
     1498        if (elmMachine.getAttributeValue("lastStateChange", str))
     1499            parseTimestamp(timeLastStateChange, str);
     1500            // constructor has called RTTimeNow(&timeLastStateChange) before
     1501
     1502        xml::NodesLoop nlRootChildren(elmMachine);
     1503        const xml::ElementNode *pelmMachineChild;
     1504        while ((pelmMachineChild = nlRootChildren.forAllNodes()))
     1505        {
     1506            if (pelmMachineChild->nameEquals("ExtraData"))
     1507                readExtraData(*pelmMachineChild,
     1508                              mapExtraDataItems);
     1509            else if (pelmMachineChild->nameEquals("Hardware"))
     1510                readHardware(*pelmMachineChild, hardwareMachine);
     1511            else if (pelmMachineChild->nameEquals("StorageControllers"))
     1512                readStorageControllers(*pelmMachineChild, storageMachine);
     1513            else if (pelmMachineChild->nameEquals("Snapshot"))
     1514            {
     1515                Snapshot snap;
     1516                // this will recurse into child snapshots, if necessary
     1517                readSnapshot(*pelmMachineChild, snap);
     1518                llFirstSnapshot.push_back(snap);
     1519            }
     1520            else if (pelmMachineChild->nameEquals("Description"))
     1521                strDescription = pelmMachineChild->getValue();
     1522            else
     1523                throw ConfigFileError(this, N_("Invalid element %s under Machine element"), pelmMachineChild->getName());
     1524        }
     1525    }
     1526    else
     1527        throw ConfigFileError(this, N_("Machine element must have uuid and name attributes"));
     1528}
     1529
     1530/**
     1531 * Constructor.
     1532 *
     1533 * If pstrFilename is != NULL, this reads the given settings file into the member
     1534 * variables and various substructures and lists. Otherwise, the member variables
     1535 * are initialized with default values.
     1536 *
     1537 * Throws variants of xml::Error for I/O, XML and logical content errors, which
     1538 * the caller should catch; if this constructor does not throw, then the file has
     1539 * been successfully read and parsed.
     1540 *
     1541 * @param strFilename
     1542 */
     1543MachineConfigFile::MachineConfigFile(const Utf8Str *pstrFilename)
     1544    : ConfigFileBase(pstrFilename),
     1545      fNameSync(true),
     1546      fCurrentStateModified(true),
     1547      fAborted(false)
     1548{
     1549    RTTimeNow(&timeLastStateChange);
     1550
     1551    if (pstrFilename)
     1552    {
     1553        // the ConfigFileBase constructor has loaded the XML file, so now
     1554        // we need only analyze what is in there
     1555
     1556        xml::NodesLoop nlRootChildren(*m->pelmRoot);
     1557        const xml::ElementNode *pelmRootChild;
     1558        while ((pelmRootChild = nlRootChildren.forAllNodes()))
     1559        {
     1560            if (pelmRootChild->nameEquals("Machine"))
     1561                readMachine(*pelmRootChild);
     1562            else
     1563                throw ConfigFileError(this, N_("Invalid element %s under root element"), pelmRootChild->getName());
     1564        }
     1565
     1566        clearDocument();
     1567    }
     1568}
     1569
     1570/**
     1571 * Creates a <Hardware> node under elmParent and then writes out the XML
     1572 * keys under that. Called for both the <Machine> node and for snapshots.
     1573 * @param elmParent
     1574 * @param st
     1575 */
     1576void MachineConfigFile::writeHardware(xml::ElementNode &elmParent,
     1577                                      const Hardware &hw)
     1578{
     1579    xml::ElementNode *pelmHardware = elmParent.createChild("Hardware");
     1580
     1581    if (hw.strVersion != "2")
     1582        pelmHardware->setAttribute("version", hw.strVersion);
     1583
     1584    xml::ElementNode *pelmCPU = pelmHardware->createChild("CPU");
     1585    pelmCPU->createChild("HardwareVirtEx")->setAttribute("enabled", hw.fHardwareVirt);
     1586    if (hw.fNestedPaging)
     1587        pelmCPU->createChild("HardwareVirtExNestedPaging")->setAttribute("enabled", hw.fNestedPaging);
     1588    if (hw.fVPID)
     1589        pelmCPU->createChild("HardwareVirtExVPID")->setAttribute("enabled", hw.fVPID);
     1590    if (hw.fPAE)
     1591        pelmCPU->createChild("PAE")->setAttribute("enabled", hw.fPAE);
     1592    pelmCPU->setAttribute("count", hw.cCPUs);
     1593
     1594    xml::ElementNode *pelmMemory = pelmHardware->createChild("Memory");
     1595    pelmMemory->setAttribute("RAMSize", hw.ulMemorySizeMB);
     1596
     1597    xml::ElementNode *pelmBoot = pelmHardware->createChild("Boot");
     1598    for (BootOrderMap::const_iterator it = hw.mapBootOrder.begin();
     1599         it != hw.mapBootOrder.end();
     1600         ++it)
     1601    {
     1602        uint32_t i = it->first;
     1603        DeviceType_T type = it->second;
     1604        const char *pcszDevice;
     1605
     1606        switch (type)
     1607        {
     1608            case DeviceType_Floppy:     pcszDevice = "Floppy"; break;
     1609            case DeviceType_DVD:        pcszDevice = "DVD"; break;
     1610            case DeviceType_HardDisk:   pcszDevice = "HardDisk"; break;
     1611            case DeviceType_Network:    pcszDevice = "Network"; break;
     1612            default: /*case DeviceType_Null:*/      pcszDevice = "None"; break;
     1613        }
     1614
     1615        xml::ElementNode *pelmOrder = pelmBoot->createChild("Order");
     1616        pelmOrder->setAttribute("position", i);
     1617        pelmOrder->setAttribute("device", pcszDevice);
     1618    }
     1619
     1620    xml::ElementNode *pelmDisplay = pelmHardware->createChild("Display");
     1621    pelmDisplay->setAttribute("VRAMSize", hw.ulVRAMSizeMB);
     1622    pelmDisplay->setAttribute("monitorCount", hw.cMonitors);
     1623    pelmDisplay->setAttribute("accelerate3D", hw.fAccelerate3D);
     1624
     1625    if (m->sv >= SettingsVersion_v1_8)
     1626        pelmDisplay->setAttribute("accelerate2DVideo", hw.fAccelerate2DVideo);
     1627
     1628    xml::ElementNode *pelmVRDP = pelmHardware->createChild("RemoteDisplay");
     1629    pelmVRDP->setAttribute("enabled", hw.vrdpSettings.fEnabled);
     1630    pelmVRDP->setAttribute("port", hw.vrdpSettings.ulPort);
     1631    if (hw.vrdpSettings.strNetAddress.length())
     1632        pelmVRDP->setAttribute("netAddress", hw.vrdpSettings.strNetAddress);
     1633    const char *pcszAuthType;
     1634    switch (hw.vrdpSettings.authType)
     1635    {
     1636        case VRDPAuthType_Guest:    pcszAuthType = "Guest";    break;
     1637        case VRDPAuthType_External: pcszAuthType = "External"; break;
     1638        default: /*case VRDPAuthType_Null:*/ pcszAuthType = "Null"; break;
     1639    }
     1640    pelmVRDP->setAttribute("authType", pcszAuthType);
     1641
     1642    if (hw.vrdpSettings.ulAuthTimeout != 0)
     1643        pelmVRDP->setAttribute("authTimeout", hw.vrdpSettings.ulAuthTimeout);
     1644    if (hw.vrdpSettings.fAllowMultiConnection)
     1645        pelmVRDP->setAttribute("allowMultiConnection", hw.vrdpSettings.fAllowMultiConnection);
     1646    if (hw.vrdpSettings.fReuseSingleConnection)
     1647        pelmVRDP->setAttribute("reuseSingleConnection", hw.vrdpSettings.fReuseSingleConnection);
     1648
     1649    xml::ElementNode *pelmBIOS = pelmHardware->createChild("BIOS");
     1650    pelmBIOS->createChild("ACPI")->setAttribute("enabled", hw.biosSettings.fACPIEnabled);
     1651    pelmBIOS->createChild("IOAPIC")->setAttribute("enabled", hw.biosSettings.fIOAPICEnabled);
     1652
     1653    xml::ElementNode *pelmLogo = pelmBIOS->createChild("Logo");
     1654    pelmLogo->setAttribute("fadeIn", hw.biosSettings.fLogoFadeIn);
     1655    pelmLogo->setAttribute("fadeOut", hw.biosSettings.fLogoFadeOut);
     1656    pelmLogo->setAttribute("displayTime", hw.biosSettings.ulLogoDisplayTime);
     1657    if (hw.biosSettings.strLogoImagePath.length())
     1658        pelmLogo->setAttribute("imagePath", hw.biosSettings.strLogoImagePath);
     1659
     1660    const char *pcszBootMenu;
     1661    switch (hw.biosSettings.biosBootMenuMode)
     1662    {
     1663        case BIOSBootMenuMode_Disabled: pcszBootMenu = "Disabled"; break;
     1664        case BIOSBootMenuMode_MenuOnly: pcszBootMenu = "MenuOnly"; break;
     1665        default: /*BIOSBootMenuMode_MessageAndMenu*/ pcszBootMenu = "MessageAndMenu"; break;
     1666    }
     1667    pelmBIOS->createChild("BootMenu")->setAttribute("mode", pcszBootMenu);
     1668    pelmBIOS->createChild("TimeOffset")->setAttribute("value", hw.biosSettings.llTimeOffset);
     1669    pelmBIOS->createChild("PXEDebug")->setAttribute("enabled", hw.biosSettings.fPXEDebugEnabled);
     1670
     1671    xml::ElementNode *pelmDVD = pelmHardware->createChild("DVDDrive");
     1672    pelmDVD->setAttribute("passthrough", hw.dvdDrive.fPassThrough);
     1673    if (!hw.dvdDrive.uuid.isEmpty())
     1674        pelmDVD->createChild("Image")->setAttribute("uuid", makeString(hw.dvdDrive.uuid));
     1675    else if (hw.dvdDrive.strHostDriveSrc.length())
     1676        pelmDVD->createChild("HostDrive")->setAttribute("src", hw.dvdDrive.strHostDriveSrc);
     1677
     1678    xml::ElementNode *pelmFloppy = pelmHardware->createChild("FloppyDrive");
     1679    pelmFloppy->setAttribute("enabled", hw.floppyDrive.fEnabled);
     1680    if (!hw.floppyDrive.uuid.isEmpty())
     1681        pelmFloppy->createChild("Image")->setAttribute("uuid", makeString(hw.floppyDrive.uuid));
     1682    else if (hw.floppyDrive.strHostDriveSrc.length())
     1683        pelmFloppy->createChild("HostDrive")->setAttribute("src", hw.floppyDrive.strHostDriveSrc);
     1684
     1685    xml::ElementNode *pelmUSB = pelmHardware->createChild("USBController");
     1686    pelmUSB->setAttribute("enabled", hw.usbController.fEnabled);
     1687    pelmUSB->setAttribute("enabledEhci", hw.usbController.fEnabledEHCI);
     1688
     1689    writeUSBDeviceFilters(*pelmUSB,
     1690                          hw.usbController.llDeviceFilters,
     1691                          false);               // fHostMode
     1692
     1693    xml::ElementNode *pelmNetwork = pelmHardware->createChild("Network");
     1694    for (NetworkAdaptersList::const_iterator it = hw.llNetworkAdapters.begin();
     1695         it != hw.llNetworkAdapters.end();
     1696         ++it)
     1697    {
     1698        const NetworkAdapter &nic = *it;
     1699
     1700        xml::ElementNode *pelmAdapter = pelmNetwork->createChild("Adapter");
     1701        pelmAdapter->setAttribute("slot", nic.ulSlot);
     1702        pelmAdapter->setAttribute("enabled", nic.fEnabled);
     1703        pelmAdapter->setAttribute("MACAddress", nic.strMACAddress);
     1704        pelmAdapter->setAttribute("cable", nic.fCableConnected);
     1705        pelmAdapter->setAttribute("speed", nic.ulLineSpeed);
     1706        if (nic.fTraceEnabled)
     1707        {
     1708            pelmAdapter->setAttribute("trace", nic.fTraceEnabled);
     1709            pelmAdapter->setAttribute("tracefile", nic.strTraceFile);
     1710        }
     1711
     1712        const char *pcszType;
     1713        switch (nic.type)
     1714        {
     1715            case NetworkAdapterType_Am79C973:   pcszType = "Am79C973"; break;
     1716            case NetworkAdapterType_I82540EM:   pcszType = "82540EM"; break;
     1717            case NetworkAdapterType_I82543GC:   pcszType = "82543GC"; break;
     1718            case NetworkAdapterType_I82545EM:   pcszType = "82545EM"; break;
     1719            default: /*case NetworkAdapterType_Am79C970A:*/  pcszType = "Am79C970A"; break;
     1720        }
     1721        pelmAdapter->setAttribute("type", pcszType);
     1722
     1723        xml::ElementNode *pelmNAT;
     1724        switch (nic.mode)
     1725        {
     1726            case NetworkAttachmentType_NAT:
     1727                pelmNAT = pelmAdapter->createChild("NAT");
     1728                if (nic.strName.length())
     1729                    pelmNAT->setAttribute("network", nic.strName);
    981730            break;
    99         default:
    100             throw xml::ENotImplemented (RT_SRC_POS);
    101     }
    102 
    103     if (aSigned)
    104     {
    105         int64_t result;
    106         int vrc = RTStrToInt64Full (aValue, 0, &result);
    107         if (RT_SUCCESS(vrc))
    108         {
    109             if (result >= (int64_t) aMin && result <= (int64_t) aMax)
    110                 return (uint64_t) result;
    111         }
    112     }
    113     else
    114     {
    115         uint64_t result;
    116         int vrc = RTStrToUInt64Full (aValue, 0, &result);
    117         if (RT_SUCCESS(vrc))
    118         {
    119             if (result >= aMin && result <= aMax)
    120                 return result;
    121         }
    122     }
    123 
    124     throw ENoConversion(com::Utf8StrFmt("'%s' is not integer", aValue));
    125 }
    126 
    127 template<> bool FromString <bool> (const char *aValue)
    128 {
    129     if (aValue == NULL)
    130         throw ENoValue();
    131 
    132     if (strcmp (aValue, "true") == 0 ||
    133         strcmp (aValue, "1") == 0)
    134         /* This contradicts the XML Schema's interpretation of boolean: */
    135         //strcmp (aValue, "yes") == 0 ||
    136         //strcmp (aValue, "on") == 0)
    137         return true;
    138     else if (strcmp (aValue, "false") == 0 ||
    139              strcmp (aValue, "0") == 0)
    140             /* This contradicts the XML Schema's interpretation of boolean: */
    141             //strcmp (aValue, "no") == 0 ||
    142             //strcmp (aValue, "off") == 0)
    143         return false;
    144 
    145     throw ENoConversion(com::Utf8StrFmt("'%s' is not bool", aValue));
    146 }
    147 
    148 template<> RTTIMESPEC FromString <RTTIMESPEC> (const char *aValue)
    149 {
    150     if (aValue == NULL)
    151         throw ENoValue();
    152 
    153     /* Parse ISO date (xsd:dateTime). The format is:
    154      * '-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?
    155      * where zzzzzz is: (('+' | '-') hh ':' mm) | 'Z' */
    156     uint32_t yyyy = 0;
    157     uint16_t mm = 0, dd = 0, hh = 0, mi = 0, ss = 0;
    158     char buf [256];
    159     if (strlen (aValue) > RT_ELEMENTS (buf) - 1 ||
    160         sscanf (aValue, "%d-%hu-%huT%hu:%hu:%hu%s",
    161                 &yyyy, &mm, &dd, &hh, &mi, &ss, buf) == 7)
    162     {
    163         /* currently, we accept only the UTC timezone ('Z'),
    164          * ignoring fractional seconds, if present */
    165         if (buf [0] == 'Z' ||
    166             (buf [0] == '.' && buf [strlen (buf) - 1] == 'Z'))
    167         {
    168             RTTIME time = { yyyy, (uint8_t) mm, 0, 0, (uint8_t) dd,
    169                             (uint8_t) hh, (uint8_t) mi, (uint8_t) ss, 0,
    170                             RTTIME_FLAGS_TYPE_UTC };
    171             if (RTTimeNormalize (&time))
    172             {
    173                 RTTIMESPEC timeSpec;
    174                 if (RTTimeImplode (&timeSpec, &time))
    175                     return timeSpec;
    176             }
    177         }
    178         else
    179             throw ENoConversion(com::Utf8StrFmt("'%s' is not UTC date", aValue));
    180     }
    181 
    182     throw ENoConversion(com::Utf8StrFmt("'%s' is not ISO date", aValue));
    183 }
    184 
    185 stdx::char_auto_ptr FromString (const char *aValue, size_t *aLen)
    186 {
    187     if (aValue == NULL)
    188         throw ENoValue();
    189 
    190     /* each two chars produce one byte */
    191     size_t len = strlen (aValue) / 2;
    192 
    193     /* therefore, the original length must be even */
    194     if (len % 2 != 0)
    195         throw ENoConversion(com::Utf8StrFmt("'%.*s' is not binary data",
    196                                             aLen, aValue));
    197 
    198     stdx::char_auto_ptr result (new char [len]);
    199 
    200     const char *src = aValue;
    201     char *dst = result.get();
    202 
    203     for (size_t i = 0; i < len; ++ i, ++ dst)
    204     {
    205         *dst = sFromHex (*src ++) << 4;
    206         *dst |= sFromHex (*src ++);
    207     }
    208 
    209     if (aLen != NULL)
    210         *aLen = len;
    211 
    212     return result;
    213 }
    214 
    215 //////////////////////////////////////////////////////////////////////////////
    216 // type -> string conversions
    217 //////////////////////////////////////////////////////////////////////////////
    218 
    219 stdx::char_auto_ptr ToStringInteger (uint64_t aValue, unsigned int aBase,
    220                                      bool aSigned, int aBits)
    221 {
    222     unsigned int flags = RTSTR_F_SPECIAL;
    223     if (aSigned)
    224         flags |= RTSTR_F_VALSIGNED;
    225 
    226     /* maximum is binary representation + terminator */
    227     size_t len = aBits + 1;
    228 
    229     switch (aBits)
    230     {
    231         case 8:
    232             flags |= RTSTR_F_8BIT;
     1731
     1732            case NetworkAttachmentType_Bridged:
     1733                pelmAdapter->createChild("BridgedInterface")->setAttribute("name", nic.strName);
    2331734            break;
    234         case 16:
    235             flags |= RTSTR_F_16BIT;
     1735
     1736            case NetworkAttachmentType_Internal:
     1737                pelmAdapter->createChild("InternalNetwork")->setAttribute("name", nic.strName);
    2361738            break;
    237         case 32:
    238             flags |= RTSTR_F_32BIT;
     1739
     1740            case NetworkAttachmentType_HostOnly:
     1741                pelmAdapter->createChild("HostOnlyInterface")->setAttribute("name", nic.strName);
    2391742            break;
    240         case 64:
    241             flags |= RTSTR_F_64BIT;
     1743
     1744            default: /*case NetworkAttachmentType_Null:*/
    2421745            break;
    243         default:
    244             throw xml::ENotImplemented (RT_SRC_POS);
    245     }
    246 
    247     stdx::char_auto_ptr result (new char [len]);
    248     if (aBase == 0)
    249         aBase = 10;
    250     int vrc = RTStrFormatNumber (result.get(), aValue, aBase, 0, 0, flags);
    251     if (RT_SUCCESS(vrc))
    252         return result;
    253 
    254     throw xml::EIPRTFailure (vrc);
    255 }
    256 
    257 template<> stdx::char_auto_ptr ToString <bool> (const bool &aValue,
    258                                                 unsigned int aExtra /* = 0 */)
    259 {
    260     /* Convert to the canonical form according to XML Schema */
    261     stdx::char_auto_ptr result (duplicate_chars (aValue ? "true" : "false"));
    262     return result;
    263 }
    264 
    265 template<> stdx::char_auto_ptr ToString <RTTIMESPEC> (const RTTIMESPEC &aValue,
    266                                                       unsigned int aExtra /* = 0 */)
    267 {
    268     RTTIME time;
    269     if (!RTTimeExplode (&time, &aValue))
    270         throw ENoConversion(com::Utf8StrFmt("timespec %lld ms is invalid",
    271                                             RTTimeSpecGetMilli (&aValue)));
    272 
    273     /* Store ISO date (xsd:dateTime). The format is:
    274      * '-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?
    275      * where zzzzzz is: (('+' | '-') hh ':' mm) | 'Z' */
    276     char buf [256];
    277     RTStrPrintf (buf, sizeof (buf),
    278                 "%04ld-%02hd-%02hdT%02hd:%02hd:%02hdZ",
    279                 time.i32Year, (uint16_t) time.u8Month, (uint16_t) time.u8MonthDay,
    280                 (uint16_t) time.u8Hour, (uint16_t) time.u8Minute, (uint16_t) time.u8Second);
    281 
    282     stdx::char_auto_ptr result (duplicate_chars (buf));
    283     return result;
    284 }
    285 
    286 stdx::char_auto_ptr ToString (const char *aData, size_t aLen)
    287 {
    288     /* each byte will produce two hex digits and there will be a null
    289      * terminator */
    290     stdx::char_auto_ptr result (new char [aLen * 2 + 1]);
    291 
    292     const char *src = aData;
    293     char *dst =  result.get();
    294 
    295     for (size_t i = 0; i < aLen; ++ i, ++ src)
    296     {
    297         *dst++ = sToHex ((*src) >> 4);
    298         *dst++ = sToHex ((*src) & 0xF);
    299     }
    300 
    301     *dst = '\0';
    302 
    303     return result;
    304 }
    305 
    306 //////////////////////////////////////////////////////////////////////////////
    307 // XmlKeyBackend Class
    308 //////////////////////////////////////////////////////////////////////////////
    309 
    310 class XmlKeyBackend : public Key::Backend
    311 {
    312 public:
    313 
    314     XmlKeyBackend (xmlNodePtr aNode);
    315     ~XmlKeyBackend();
    316 
    317     const char *name() const;
    318     void setName (const char *aName);
    319     const char *value (const char *aName) const;
    320     void setValue (const char *aName, const char *aValue);
    321 
    322     Key::List keys (const char *aName = NULL) const;
    323     Key findKey (const char *aName) const;
    324 
    325     Key appendKey (const char *aName);
    326     void zap();
    327 
    328     void *position() const { return mNode; }
    329 
    330 private:
    331 
    332     xmlNodePtr mNode;
    333 
    334     xmlChar *mNodeText;
    335 
    336     DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (XmlKeyBackend);
    337 
    338     friend class XmlTreeBackend;
    339 };
    340 
    341 XmlKeyBackend::XmlKeyBackend (xmlNodePtr aNode)
    342     : mNode (aNode), mNodeText (NULL)
    343 {
    344     AssertReturnVoid (mNode);
    345     AssertReturnVoid (mNode->type == XML_ELEMENT_NODE);
    346 }
    347 
    348 XmlKeyBackend::~XmlKeyBackend()
    349 {
    350     xmlFree (mNodeText);
    351 }
    352 
    353 const char *XmlKeyBackend::name() const
    354 {
    355     return mNode ? (char *) mNode->name : NULL;
    356 }
    357 
    358 void XmlKeyBackend::setName (const char *aName)
    359 {
    360     throw xml::ENotImplemented (RT_SRC_POS);
    361 }
    362 
    363 const char *XmlKeyBackend::value (const char *aName) const
    364 {
    365     if (!mNode)
    366         return NULL;
    367 
    368     if (aName == NULL)
    369     {
    370         /* @todo xmlNodeListGetString (,,1) returns NULL for things like
    371          * <Foo></Foo> and may want to return "" in this case to distinguish
    372          * from <Foo/> (where NULL is pretty much expected). */
    373         if (!mNodeText)
    374             unconst(mNodeText) =
    375                 xmlNodeListGetString (mNode->doc, mNode->children, 0);
    376         return (char *) mNodeText;
    377     }
    378 
    379     xmlAttrPtr attr = xmlHasProp (mNode, (const xmlChar *) aName);
    380     if (!attr)
    381         return NULL;
    382 
    383     if (attr->type == XML_ATTRIBUTE_NODE)
    384     {
    385         /* @todo for now, we only understand the most common case: only 1 text
    386          * node comprises the attribute's contents. Otherwise we'd need to
    387          * return a newly allocated string buffer to the caller that
    388          * concatenates all text nodes and obey him to free it or provide our
    389          * own internal map of attribute=value pairs and return const pointers
    390          * to values from this map. */
    391         if (attr->children != NULL &&
    392             attr->children->next == NULL &&
    393             (attr->children->type == XML_TEXT_NODE ||
    394              attr->children->type == XML_CDATA_SECTION_NODE))
    395             return (char *) attr->children->content;
    396     }
    397     else if (attr->type == XML_ATTRIBUTE_DECL)
    398     {
    399         return (char *) ((xmlAttributePtr) attr)->defaultValue;
    400     }
    401 
    402     return NULL;
    403 }
    404 
    405 void XmlKeyBackend::setValue (const char *aName, const char *aValue)
    406 {
    407     if (!mNode)
    408         return;
    409 
    410     if (aName == NULL)
    411     {
    412         xmlChar *value = (xmlChar *) aValue;
    413         if (value != NULL)
    414         {
    415             value = xmlEncodeSpecialChars (mNode->doc, value);
    416             if (value == NULL)
    417                 throw std::bad_alloc();
    418         }
    419 
    420         xmlNodeSetContent (mNode, value);
    421 
    422         if (value != (xmlChar *) aValue)
    423             xmlFree (value);
    424 
    425         /* outdate the node text holder */
    426         if (mNodeText != NULL)
    427         {
    428             xmlFree (mNodeText);
    429             mNodeText = NULL;
    430         }
    431 
    432         return;
    433     }
    434 
    435     if (aValue == NULL)
    436     {
    437         /* remove the attribute if it exists */
    438         xmlAttrPtr attr = xmlHasProp (mNode, (const xmlChar *) aName);
    439         if (attr != NULL)
    440         {
    441             int rc = xmlRemoveProp (attr);
    442             if (rc != 0)
    443                 throw xml::EInvalidArg (RT_SRC_POS);
    444         }
    445         return;
    446     }
    447 
    448     xmlAttrPtr attr = xmlSetProp (mNode, (const xmlChar *) aName,
    449                                   (const xmlChar *) aValue);
    450     if (attr == NULL)
    451         throw std::bad_alloc();
    452 }
    453 
    454 Key::List XmlKeyBackend::keys (const char *aName /* = NULL */) const
    455 {
    456     Key::List list;
    457 
    458     if (!mNode)
    459         return list;
    460 
    461     for (xmlNodePtr node = mNode->children; node; node = node->next)
    462     {
    463         if (node->type == XML_ELEMENT_NODE)
    464         {
    465             if (aName == NULL ||
    466                 strcmp (aName, (char *) node->name) == 0)
    467                 list.push_back (Key (new XmlKeyBackend (node)));
    468         }
    469     }
    470 
    471     return list;
    472 }
    473 
    474 Key XmlKeyBackend::findKey (const char *aName) const
    475 {
    476     Key key;
    477 
    478     if (!mNode)
    479         return key;
    480 
    481     for (xmlNodePtr node = mNode->children; node; node = node->next)
    482     {
    483         if (node->type == XML_ELEMENT_NODE)
    484         {
    485             if (aName == NULL ||
    486                 strcmp (aName, (char *) node->name) == 0)
    487             {
    488                 key = Key (new XmlKeyBackend (node));
    489                 break;
    490             }
    491         }
    492     }
    493 
    494     return key;
    495 }
    496 
    497 Key XmlKeyBackend::appendKey (const char *aName)
    498 {
    499     if (!mNode)
    500         return Key();
    501 
    502     xmlNodePtr node = xmlNewChild (mNode, NULL, (const xmlChar *) aName, NULL);
    503     if (node == NULL)
    504         throw std::bad_alloc();
    505 
    506     return Key (new XmlKeyBackend (node));
    507 }
    508 
    509 void XmlKeyBackend::zap()
    510 {
    511     if (!mNode)
    512         return;
    513 
    514     xmlUnlinkNode (mNode);
    515     xmlFreeNode (mNode);
    516     mNode = NULL;
    517 }
    518 
    519 //////////////////////////////////////////////////////////////////////////////
    520 // XmlTreeBackend Class
    521 //////////////////////////////////////////////////////////////////////////////
    522 
    523 struct XmlTreeBackend::Data
    524 {
    525     Data() : ctxt (NULL), doc (NULL)
    526            , inputResolver (NULL)
    527            , autoConverter (NULL), oldVersion (NULL) {}
    528 
    529     xmlParserCtxtPtr ctxt;
    530     xmlDocPtr doc;
    531 
    532     Key root;
    533 
    534     InputResolver *inputResolver;
    535 
    536     AutoConverter *autoConverter;
    537     char *oldVersion;
    538 
    539     std::auto_ptr <stdx::exception_trap_base> trappedErr;
    540 
    541     /**
    542      * This is to avoid throwing exceptions while in libxml2 code and
    543      * redirect them to our level instead. Also used to perform clean up
    544      * by deleting the I/O stream instance and self when requested.
    545      */
    546     struct IOCtxt
    547     {
    548         IOCtxt (xml::Stream *aStream, std::auto_ptr <stdx::exception_trap_base> &aErr)
    549             : stream (aStream), deleteStreamOnClose (false)
    550             , err (aErr) {}
    551 
    552         template <typename T>
    553         void setErr (const T& aErr) { err.reset (new stdx::exception_trap <T> (aErr)); }
    554 
    555         void resetErr() { err.reset(); }
    556 
    557         xml::Stream *stream;
    558         bool deleteStreamOnClose;
    559 
    560         std::auto_ptr <stdx::exception_trap_base> &err;
    561     };
    562 
    563     struct InputCtxt : public IOCtxt
    564     {
    565         InputCtxt (xml::Input *aInput, std::auto_ptr <stdx::exception_trap_base> &aErr)
    566             : IOCtxt (aInput, aErr), input (aInput) {}
    567 
    568         xml::Input *input;
    569     };
    570 
    571     struct OutputCtxt : public IOCtxt
    572     {
    573         OutputCtxt (xml::Output *aOutput, std::auto_ptr <stdx::exception_trap_base> &aErr)
    574             : IOCtxt (aOutput, aErr), output (aOutput) {}
    575 
    576         xml::Output *output;
    577     };
    578 };
    579 
    580 XmlTreeBackend::XmlTreeBackend()
    581     : m (new Data())
    582 {
    583     /* create a parser context */
    584     m->ctxt = xmlNewParserCtxt();
    585     if (m->ctxt == NULL)
    586         throw std::bad_alloc();
    587 }
    588 
    589 XmlTreeBackend::~XmlTreeBackend()
    590 {
    591     reset();
    592 
    593     xmlFreeParserCtxt (m->ctxt);
    594     m->ctxt = NULL;
    595 }
    596 
    597 void XmlTreeBackend::setInputResolver (InputResolver &aResolver)
    598 {
    599     m->inputResolver = &aResolver;
    600 }
    601 
    602 void XmlTreeBackend::resetInputResolver()
    603 {
    604     m->inputResolver = NULL;
    605 }
    606 
    607 void XmlTreeBackend::setAutoConverter (AutoConverter &aConverter)
    608 {
    609     m->autoConverter = &aConverter;
    610 }
    611 
    612 void XmlTreeBackend::resetAutoConverter()
    613 {
    614     m->autoConverter = NULL;
    615 }
    616 
    617 const char *XmlTreeBackend::oldVersion() const
    618 {
    619     return m->oldVersion;
    620 }
    621 
    622 extern "C" xmlGenericErrorFunc xsltGenericError;
    623 extern "C" void *xsltGenericErrorContext;
    624 
    625 void XmlTreeBackend::rawRead (xml::Input &aInput, const char *aSchema /* = NULL */,
    626                               int aFlags /* = 0 */)
    627 {
    628     /* Reset error variables used to memorize exceptions while inside the
    629      * libxml2 code. */
    630     m->trappedErr.reset();
    631 
    632     /* We use the global lock for the whole duration of this method to serialize
    633      * access to thread-unsafe xmlGetExternalEntityLoader() and some other
    634      * calls. It means that only one thread is able to parse an XML stream at a
    635      * time but another choice would be to patch libxml2/libxslt which is
    636      * unwanted now for several reasons. Search for "thread-safe" to find all
    637      * unsafe cases. */
    638     xml::GlobalLock global;
    639     global.setExternalEntityLoader(ExternalEntityLoader);
    640 
    641     sThat = this;
    642     xmlDocPtr doc = NULL;
    643 
    644     try
    645     {
    646         /* Note: when parsing we use XML_PARSE_NOBLANKS to instruct libxml2 to
    647          * remove text nodes that contain only blanks. This is important because
    648          * otherwise xmlSaveDoc() won't be able to do proper indentation on
    649          * output. */
    650         /* parse the stream */
    651         /* NOTE: new InputCtxt instance will be deleted when the stream is closed by
    652          * the libxml2 API (e.g. when calling xmlFreeParserCtxt()) */
    653         doc = xmlCtxtReadIO (m->ctxt,
    654                              ReadCallback, CloseCallback,
    655                              new Data::InputCtxt (&aInput, m->trappedErr),
    656                              aInput.uri(), NULL,
    657                              XML_PARSE_NOBLANKS);
    658         if (doc == NULL)
    659         {
    660             /* look if there was a forwared exception from the lower level */
    661             if (m->trappedErr.get() != NULL)
    662                 m->trappedErr->rethrow();
    663 
    664             throw xml::XmlError(xmlCtxtGetLastError (m->ctxt));
    665         }
    666 
    667         char *oldVersion = NULL;
    668 
    669         /* perform automatic document transformation if necessary */
    670         if (m->autoConverter != NULL &&
    671             m->autoConverter->
    672                 needsConversion (Key (new XmlKeyBackend (xmlDocGetRootElement (doc))),
    673                                  &oldVersion))
    674         {
    675             xmlDocPtr xsltDoc = NULL;
    676             xsltStylesheetPtr xslt = NULL;
    677             char *errorStr = NULL;
    678 
    679             xmlGenericErrorFunc oldXsltGenericError = xsltGenericError;
    680             void *oldXsltGenericErrorContext = xsltGenericErrorContext;
    681 
    682             try
    683             {
    684                 /* parse the XSLT template */
    685                 {
    686                     xml::Input *xsltInput =
    687                         m->inputResolver->resolveEntity
    688                             (m->autoConverter->templateUri(), NULL);
    689                     /* NOTE: new InputCtxt instance will be deleted when the
    690                      * stream is closed by the libxml2 API */
    691                     xsltDoc = xmlCtxtReadIO (m->ctxt,
    692                                              ReadCallback, CloseCallback,
    693                                              new Data::InputCtxt (xsltInput, m->trappedErr),
    694                                              m->autoConverter->templateUri(),
    695                                              NULL, 0);
    696                     delete xsltInput;
    697                 }
    698 
    699                 if (xsltDoc == NULL)
    700                 {
    701                     /* look if there was a forwared exception from the lower level */
    702                     if (m->trappedErr.get() != NULL)
    703                         m->trappedErr->rethrow();
    704 
    705                     throw xml::XmlError(xmlCtxtGetLastError (m->ctxt));
    706                 }
    707 
    708                 /* setup stylesheet compilation and transformation error
    709                  * reporting. Note that we could create a new transform context
    710                  * for doing xsltApplyStylesheetUser and use
    711                  * xsltSetTransformErrorFunc() on it to set a dedicated error
    712                  * handler but as long as we already do several non-thread-safe
    713                  * hacks, this is not really important. */
    714 
    715                 xsltGenericError = ValidityErrorCallback;
    716                 xsltGenericErrorContext = &errorStr;
    717 
    718                 xslt = xsltParseStylesheetDoc (xsltDoc);
    719                 if (xslt == NULL)
    720                 {
    721                     if (errorStr != NULL)
    722                         throw xml::LogicError (errorStr);
    723                     /* errorStr is freed in catch(...) below */
    724 
    725                     throw xml::LogicError (RT_SRC_POS);
    726                 }
    727 
    728                 /* repeat transformations until autoConverter is satisfied */
    729                 do
    730                 {
    731                     xmlDocPtr newDoc = xsltApplyStylesheet (xslt, doc, NULL);
    732                     if (newDoc == NULL && errorStr == NULL)
    733                         throw xml::LogicError (RT_SRC_POS);
    734 
    735                     if (errorStr != NULL)
    736                     {
    737                         xmlFreeDoc (newDoc);
    738                         throw xml::RuntimeError(errorStr);
    739                         /* errorStr is freed in catch(...) below */
    740                     }
    741 
    742                     /* replace the old document on success */
    743                     xmlFreeDoc (doc);
    744                     doc = newDoc;
    745                 }
    746                 while (m->autoConverter->
    747                        needsConversion (Key (new XmlKeyBackend (xmlDocGetRootElement (doc))),
    748                                         NULL));
    749 
    750                 RTStrFree (errorStr);
    751 
    752                 /* NOTE: xsltFreeStylesheet() also fress the document
    753                  * passed to xsltParseStylesheetDoc(). */
    754                 xsltFreeStylesheet (xslt);
    755 
    756                 /* restore the previous generic error func */
    757                 xsltGenericError = oldXsltGenericError;
    758                 xsltGenericErrorContext = oldXsltGenericErrorContext;
    759             }
    760             catch (...)
    761             {
    762                 RTStrFree (errorStr);
    763 
    764                 /* NOTE: xsltFreeStylesheet() also fress the document
    765                  * passed to xsltParseStylesheetDoc(). */
    766                 if (xslt != NULL)
    767                     xsltFreeStylesheet (xslt);
    768                 else if (xsltDoc != NULL)
    769                     xmlFreeDoc (xsltDoc);
    770 
    771                 /* restore the previous generic error func */
    772                 xsltGenericError = oldXsltGenericError;
    773                 xsltGenericErrorContext = oldXsltGenericErrorContext;
    774 
    775                 RTStrFree (oldVersion);
    776 
    777                 throw;
    778             }
    779         }
    780 
    781         /* validate the document */
    782         if (aSchema != NULL)
    783         {
    784             xmlSchemaParserCtxtPtr schemaCtxt = NULL;
    785             xmlSchemaPtr schema = NULL;
    786             xmlSchemaValidCtxtPtr validCtxt = NULL;
    787             char *errorStr = NULL;
    788 
    789             try
    790             {
    791                 bool valid = false;
    792 
    793                 schemaCtxt = xmlSchemaNewParserCtxt (aSchema);
    794                 if (schemaCtxt == NULL)
    795                     throw xml::LogicError (RT_SRC_POS);
    796 
    797                 /* set our error handlers */
    798                 xmlSchemaSetParserErrors (schemaCtxt, ValidityErrorCallback,
    799                                           ValidityWarningCallback, &errorStr);
    800                 xmlSchemaSetParserStructuredErrors (schemaCtxt,
    801                                                     StructuredErrorCallback,
    802                                                     &errorStr);
    803                 /* load schema */
    804                 schema = xmlSchemaParse (schemaCtxt);
    805                 if (schema != NULL)
    806                 {
    807                     validCtxt = xmlSchemaNewValidCtxt (schema);
    808                     if (validCtxt == NULL)
    809                         throw xml::LogicError (RT_SRC_POS);
    810 
    811                     /* instruct to create default attribute's values in the document */
    812                     if (aFlags & Read_AddDefaults)
    813                         xmlSchemaSetValidOptions (validCtxt, XML_SCHEMA_VAL_VC_I_CREATE);
    814 
    815                     /* set our error handlers */
    816                     xmlSchemaSetValidErrors (validCtxt, ValidityErrorCallback,
    817                                              ValidityWarningCallback, &errorStr);
    818 
    819                     /* finally, validate */
    820                     valid = xmlSchemaValidateDoc (validCtxt, doc) == 0;
    821                 }
    822 
    823                 if (!valid)
    824                 {
    825                     /* look if there was a forwared exception from the lower level */
    826                     if (m->trappedErr.get() != NULL)
    827                         m->trappedErr->rethrow();
    828 
    829                     if (errorStr == NULL)
    830                         throw xml::LogicError (RT_SRC_POS);
    831 
    832                     throw xml::RuntimeError(errorStr);
    833                     /* errorStr is freed in catch(...) below */
    834                 }
    835 
    836                 RTStrFree (errorStr);
    837 
    838                 xmlSchemaFreeValidCtxt (validCtxt);
    839                 xmlSchemaFree (schema);
    840                 xmlSchemaFreeParserCtxt (schemaCtxt);
    841             }
    842             catch (...)
    843             {
    844                 RTStrFree (errorStr);
    845 
    846                 if (validCtxt)
    847                     xmlSchemaFreeValidCtxt (validCtxt);
    848                 if (schema)
    849                     xmlSchemaFree (schema);
    850                 if (schemaCtxt)
    851                     xmlSchemaFreeParserCtxt (schemaCtxt);
    852 
    853                 RTStrFree (oldVersion);
    854 
    855                 throw;
    856             }
    857         }
    858 
    859         /* reset the previous tree on success */
    860         reset();
    861 
    862         m->doc = doc;
    863         /* assign the root key */
    864         m->root = Key (new XmlKeyBackend (xmlDocGetRootElement (m->doc)));
    865 
    866         /* memorize the old version string also used as a flag that
    867          * the conversion has been performed (transfers ownership) */
    868         m->oldVersion = oldVersion;
    869 
    870         sThat = NULL;
    871     }
    872     catch (...)
    873     {
    874         if (doc != NULL)
    875             xmlFreeDoc (doc);
    876 
    877         sThat = NULL;
    878 
    879         throw;
    880     }
    881 }
    882 
    883 void XmlTreeBackend::rawWrite (xml::Output &aOutput)
    884 {
    885     /* reset error variables used to memorize exceptions while inside the
    886      * libxml2 code */
    887     m->trappedErr.reset();
    888 
    889     /* set up an input stream for parsing the document. This will be deleted
    890      * when the stream is closed by the libxml2 API (e.g. when calling
    891      * xmlFreeParserCtxt()). */
    892     Data::OutputCtxt *outputCtxt =
    893         new Data::OutputCtxt (&aOutput, m->trappedErr);
    894 
    895     /* serialize to the stream */
    896 
    897     xmlIndentTreeOutput = 1;
    898     xmlTreeIndentString = "  ";
    899     xmlSaveNoEmptyTags = 0;
    900 
    901     xmlSaveCtxtPtr saveCtxt = xmlSaveToIO (WriteCallback, CloseCallback,
    902                                            outputCtxt, NULL,
    903                                            XML_SAVE_FORMAT);
    904     if (saveCtxt == NULL)
    905         throw xml::LogicError (RT_SRC_POS);
    906 
    907     long rc = xmlSaveDoc (saveCtxt, m->doc);
    908     if (rc == -1)
    909     {
    910         /* look if there was a forwared exception from the lower level */
    911         if (m->trappedErr.get() != NULL)
    912             m->trappedErr->rethrow();
    913 
    914         /* there must be an exception from the Output implementation,
    915          * otherwise the save operation must always succeed. */
    916         throw xml::LogicError (RT_SRC_POS);
    917     }
    918 
    919     xmlSaveClose (saveCtxt);
    920 }
    921 
    922 void XmlTreeBackend::reset()
    923 {
    924     RTStrFree (m->oldVersion);
    925     m->oldVersion = NULL;
    926 
    927     if (m->doc)
    928     {
    929         /* reset the root key's node */
    930         GetKeyBackend (m->root)->mNode = NULL;
    931         /* free the document*/
    932         xmlFreeDoc (m->doc);
    933         m->doc = NULL;
    934     }
    935 }
    936 
    937 Key &XmlTreeBackend::rootKey() const
    938 {
    939     return m->root;
    940 }
    941 
    942 /* static */
    943 int XmlTreeBackend::ReadCallback (void *aCtxt, char *aBuf, int aLen)
    944 {
    945     AssertReturn(aCtxt != NULL, 0);
    946 
    947     Data::InputCtxt *ctxt = static_cast <Data::InputCtxt *> (aCtxt);
    948 
    949     /* To prevent throwing exceptions while inside libxml2 code, we catch
    950      * them and forward to our level using a couple of variables. */
    951     try
    952     {
    953         return ctxt->input->read (aBuf, aLen);
    954     }
    955     catch (const xml::EIPRTFailure &err) { ctxt->setErr (err); }
    956     catch (const xml::Error &err) { ctxt->setErr (err); }
    957     catch (const std::exception &err) { ctxt->setErr (err); }
    958     catch (...) { ctxt->setErr (xml::LogicError (RT_SRC_POS)); }
    959 
    960     return -1 /* failure */;
    961 }
    962 
    963 /* static */
    964 int XmlTreeBackend::WriteCallback (void *aCtxt, const char *aBuf, int aLen)
    965 {
    966     AssertReturn(aCtxt != NULL, 0);
    967 
    968     Data::OutputCtxt *ctxt = static_cast <Data::OutputCtxt *> (aCtxt);
    969 
    970     /* To prevent throwing exceptions while inside libxml2 code, we catch
    971      * them and forward to our level using a couple of variables. */
    972     try
    973     {
    974         return ctxt->output->write (aBuf, aLen);
    975     }
    976     catch (const xml::EIPRTFailure &err) { ctxt->setErr (err); }
    977     catch (const xml::Error &err) { ctxt->setErr (err); }
    978     catch (const std::exception &err) { ctxt->setErr (err); }
    979     catch (...) { ctxt->setErr (xml::LogicError (RT_SRC_POS)); }
    980 
    981     return -1 /* failure */;
    982 }
    983 
    984 /* static */
    985 int XmlTreeBackend::CloseCallback (void *aCtxt)
    986 {
    987     AssertReturn(aCtxt != NULL, 0);
    988 
    989     Data::IOCtxt *ctxt = static_cast <Data::IOCtxt *> (aCtxt);
    990 
    991     /* To prevent throwing exceptions while inside libxml2 code, we catch
    992      * them and forward to our level using a couple of variables. */
    993     try
    994     {
    995         /// @todo there is no explicit close semantics in Stream yet
    996 #if 0
    997         ctxt->stream->close();
    998 #endif
    999 
    1000         /* perform cleanup when necessary */
    1001         if (ctxt->deleteStreamOnClose)
    1002             delete ctxt->stream;
    1003 
    1004         delete ctxt;
    1005 
    1006         return 0 /* success */;
    1007     }
    1008     catch (const xml::EIPRTFailure &err) { ctxt->setErr (err); }
    1009     catch (const xml::Error &err) { ctxt->setErr (err); }
    1010     catch (const std::exception &err) { ctxt->setErr (err); }
    1011     catch (...) { ctxt->setErr (xml::LogicError (RT_SRC_POS)); }
    1012 
    1013     return -1 /* failure */;
    1014 }
    1015 
    1016 /* static */
    1017 void XmlTreeBackend::ValidityErrorCallback (void *aCtxt, const char *aMsg, ...)
    1018 {
    1019     AssertReturnVoid (aCtxt != NULL);
    1020     AssertReturnVoid (aMsg != NULL);
    1021 
    1022     char * &str = *(char * *) aCtxt;
    1023 
    1024     char *newMsg = NULL;
    1025     {
    1026         va_list args;
    1027         va_start (args, aMsg);
    1028         RTStrAPrintfV (&newMsg, aMsg, args);
    1029         va_end (args);
    1030     }
    1031 
    1032     AssertReturnVoid (newMsg != NULL);
    1033 
    1034     /* strip spaces, trailing EOLs and dot-like char */
    1035     size_t newMsgLen = strlen (newMsg);
    1036     while (newMsgLen && strchr (" \n.?!", newMsg [newMsgLen - 1]))
    1037         -- newMsgLen;
    1038 
    1039     /* anything left? */
    1040     if (newMsgLen > 0)
    1041     {
    1042         if (str == NULL)
    1043         {
    1044             str = newMsg;
    1045             newMsg [newMsgLen] = '\0';
    1046         }
    1047         else
    1048         {
    1049             /* append to the existing string */
    1050             size_t strLen = strlen (str);
    1051             char *newStr = (char *) RTMemRealloc (str, strLen + 2 + newMsgLen + 1);
    1052             AssertReturnVoid (newStr != NULL);
    1053 
    1054             memcpy (newStr + strLen, ".\n", 2);
    1055             memcpy (newStr + strLen + 2, newMsg, newMsgLen);
    1056             newStr [strLen + 2 + newMsgLen] = '\0';
    1057             str = newStr;
    1058             RTStrFree (newMsg);
    1059         }
    1060     }
    1061 }
    1062 
    1063 /* static */
    1064 void XmlTreeBackend::ValidityWarningCallback (void *aCtxt, const char *aMsg, ...)
    1065 {
    1066     NOREF (aCtxt);
    1067     NOREF (aMsg);
    1068 }
    1069 
    1070 /* static */
    1071 void XmlTreeBackend::StructuredErrorCallback (void *aCtxt, xmlErrorPtr aErr)
    1072 {
    1073     AssertReturnVoid (aCtxt != NULL);
    1074     AssertReturnVoid (aErr != NULL);
    1075 
    1076     char * &str = *(char * *) aCtxt;
    1077 
    1078     char *newMsg = xml::XmlError::Format (aErr);
    1079     AssertReturnVoid (newMsg != NULL);
    1080 
    1081     if (str == NULL)
    1082         str = newMsg;
    1083     else
    1084     {
    1085         /* append to the existing string */
    1086         size_t newMsgLen = strlen (newMsg);
    1087         size_t strLen = strlen (str);
    1088         char *newStr = (char *) RTMemRealloc (str, strLen + newMsgLen + 2);
    1089         AssertReturnVoid (newStr != NULL);
    1090 
    1091         memcpy (newStr + strLen, ".\n", 2);
    1092         memcpy (newStr + strLen + 2, newMsg, newMsgLen);
    1093         str = newStr;
    1094         RTStrFree (newMsg);
    1095     }
    1096 }
    1097 
    1098 /* static */
    1099 XmlTreeBackend *XmlTreeBackend::sThat = NULL;
    1100 
    1101 /* static */
    1102 xmlParserInputPtr XmlTreeBackend::ExternalEntityLoader (const char *aURI,
    1103                                                         const char *aID,
    1104                                                         xmlParserCtxtPtr aCtxt)
    1105 {
    1106     AssertReturn(sThat != NULL, NULL);
    1107 
    1108     if (sThat->m->inputResolver == NULL)
    1109         return xml::GlobalLock::callDefaultLoader(aURI, aID, aCtxt);
    1110 
    1111     /* To prevent throwing exceptions while inside libxml2 code, we catch
    1112      * them and forward to our level using a couple of variables. */
    1113     try
    1114     {
    1115         xml::Input *input = sThat->m->inputResolver->resolveEntity (aURI, aID);
    1116         if (input == NULL)
    1117             return NULL;
    1118 
    1119         Data::InputCtxt *ctxt = new Data::InputCtxt (input, sThat->m->trappedErr);
    1120         ctxt->deleteStreamOnClose = true;
    1121 
    1122         /* create an input buffer with custom hooks */
    1123         xmlParserInputBufferPtr bufPtr =
    1124             xmlParserInputBufferCreateIO (ReadCallback, CloseCallback,
    1125                                           ctxt, XML_CHAR_ENCODING_NONE);
    1126         if (bufPtr)
    1127         {
    1128             /* create an input stream */
    1129             xmlParserInputPtr inputPtr =
    1130                 xmlNewIOInputStream (aCtxt, bufPtr, XML_CHAR_ENCODING_NONE);
    1131 
    1132             if (inputPtr != NULL)
    1133             {
    1134                 /* pass over the URI to the stream struct (it's NULL by
    1135                  * default) */
    1136                 inputPtr->filename =
    1137                     (char *) xmlCanonicPath ((const xmlChar *) input->uri());
    1138                 return inputPtr;
    1139             }
    1140         }
    1141 
    1142         /* either of libxml calls failed */
    1143 
    1144         if (bufPtr)
    1145             xmlFreeParserInputBuffer (bufPtr);
    1146 
    1147         delete input;
    1148         delete ctxt;
    1149 
    1150         throw std::bad_alloc();
    1151     }
    1152     catch (const xml::EIPRTFailure &err) { sThat->m->trappedErr.reset (stdx::new_exception_trap (err)); }
    1153     catch (const xml::Error &err) { sThat->m->trappedErr.reset (stdx::new_exception_trap (err)); }
    1154     catch (const std::exception &err) { sThat->m->trappedErr.reset (stdx::new_exception_trap (err)); }
    1155     catch (...) { sThat->m->trappedErr.reset (stdx::new_exception_trap (xml::LogicError (RT_SRC_POS))); }
    1156 
    1157     return NULL;
    1158 }
    1159 
    1160 
    1161 } /* namespace settings */
    1162 
     1746        }
     1747    }
     1748
     1749    xml::ElementNode *pelmPorts = pelmHardware->createChild("UART");
     1750    for (SerialPortsList::const_iterator it = hw.llSerialPorts.begin();
     1751         it != hw.llSerialPorts.end();
     1752         ++it)
     1753    {
     1754        const SerialPort &port = *it;
     1755        xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
     1756        pelmPort->setAttribute("slot", port.ulSlot);
     1757        pelmPort->setAttribute("enabled", port.fEnabled);
     1758        pelmPort->setAttributeHex("IOBase", port.ulIOBase);
     1759        pelmPort->setAttribute("IRQ", port.ulIRQ);
     1760
     1761        const char *pcszHostMode;
     1762        switch (port.portMode)
     1763        {
     1764            case PortMode_HostPipe: pcszHostMode = "HostPipe"; break;
     1765            case PortMode_HostDevice: pcszHostMode = "HostDevice"; break;
     1766            case PortMode_RawFile: pcszHostMode = "RawFile"; break;
     1767            default: /*case PortMode_Disconnected:*/ pcszHostMode = "Disconnected"; break;
     1768        }
     1769        pelmPort->setAttribute("hostMode", pcszHostMode);
     1770    }
     1771
     1772    pelmPorts = pelmHardware->createChild("LPT");
     1773    for (ParallelPortsList::const_iterator it = hw.llParallelPorts.begin();
     1774         it != hw.llParallelPorts.end();
     1775         ++it)
     1776    {
     1777        const ParallelPort &port = *it;
     1778        xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
     1779        pelmPort->setAttribute("slot", port.ulSlot);
     1780        pelmPort->setAttribute("enabled", port.fEnabled);
     1781        pelmPort->setAttributeHex("IOBase", port.ulIOBase);
     1782        pelmPort->setAttribute("IRQ", port.ulIRQ);
     1783        if (port.strPath.length())
     1784            pelmPort->setAttribute("path", port.strPath);
     1785    }
     1786
     1787    xml::ElementNode *pelmAudio = pelmHardware->createChild("AudioAdapter");
     1788    pelmAudio->setAttribute("controller", (hw.audioAdapter.controllerType == AudioControllerType_SB16) ? "SB16" : "AC97");
     1789
     1790    const char *pcszDriver;
     1791    switch (hw.audioAdapter.driverType)
     1792    {
     1793        case AudioDriverType_WinMM: pcszDriver = "WinMM"; break;
     1794        case AudioDriverType_DirectSound: pcszDriver = "DirectSound"; break;
     1795        case AudioDriverType_SolAudio: pcszDriver = "SolAudio"; break;
     1796        case AudioDriverType_ALSA: pcszDriver = "ALSA"; break;
     1797        case AudioDriverType_Pulse: pcszDriver = "Pulse"; break;
     1798        case AudioDriverType_OSS: pcszDriver = "OSS"; break;
     1799        case AudioDriverType_CoreAudio: pcszDriver = "CoreAudio"; break;
     1800        case AudioDriverType_MMPM: pcszDriver = "MMPM"; break;
     1801        default: /*case AudioDriverType_Null:*/ pcszDriver = "Null"; break;
     1802    }
     1803    pelmAudio->setAttribute("driver", pcszDriver);
     1804
     1805    pelmAudio->setAttribute("enabled", hw.audioAdapter.fEnabled);
     1806
     1807    xml::ElementNode *pelmSharedFolders = pelmHardware->createChild("SharedFolders");
     1808    for (SharedFoldersList::const_iterator it = hw.llSharedFolders.begin();
     1809         it != hw.llSharedFolders.end();
     1810         ++it)
     1811    {
     1812        const SharedFolder &sf = *it;
     1813        xml::ElementNode *pelmThis = pelmSharedFolders->createChild("SharedFolder");
     1814        pelmThis->setAttribute("name", sf.strName);
     1815        pelmThis->setAttribute("hostPath", sf.strHostPath);
     1816        pelmThis->setAttribute("writable", sf.fWritable);
     1817    }
     1818
     1819    xml::ElementNode *pelmClip = pelmHardware->createChild("Clipboard");
     1820    const char *pcszClip;
     1821    switch (hw.clipboardMode)
     1822    {
     1823        case ClipboardMode_Disabled: pcszClip = "Disabled"; break;
     1824        case ClipboardMode_HostToGuest: pcszClip = "HostToGuest"; break;
     1825        case ClipboardMode_GuestToHost: pcszClip = "GuestToHost"; break;
     1826        default: /*case ClipboardMode_Bidirectional:*/ pcszClip = "Bidirectional"; break;
     1827    }
     1828    pelmClip->setAttribute("mode", pcszClip);
     1829
     1830    xml::ElementNode *pelmGuest = pelmHardware->createChild("Guest");
     1831    pelmGuest->setAttribute("memoryBalloonSize", hw.ulMemoryBalloonSize);
     1832    pelmGuest->setAttribute("statisticsUpdateInterval", hw.ulStatisticsUpdateInterval);
     1833
     1834    xml::ElementNode *pelmGuestProps = pelmHardware->createChild("GuestProperties");
     1835    for (GuestPropertiesList::const_iterator it = hw.llGuestProperties.begin();
     1836         it != hw.llGuestProperties.end();
     1837         ++it)
     1838    {
     1839        const GuestProperty &prop = *it;
     1840        xml::ElementNode *pelmProp = pelmGuestProps->createChild("GuestProperty");
     1841        pelmProp->setAttribute("name", prop.strName);
     1842        pelmProp->setAttribute("value", prop.strValue);
     1843        pelmProp->setAttribute("timestamp", prop.timestamp);
     1844        pelmProp->setAttribute("flags", prop.strFlags);
     1845    }
     1846
     1847    if (hw.strNotificationPatterns.length())
     1848        pelmGuestProps->setAttribute("notificationPatterns", hw.strNotificationPatterns);
     1849}
     1850
     1851/**
     1852 * Creates a <StorageControllers> node under elmParent and then writes out the XML
     1853 * keys under that. Called for both the <Machine> node and for snapshots.
     1854 * @param elmParent
     1855 * @param st
     1856 */
     1857void MachineConfigFile::writeStorageControllers(xml::ElementNode &elmParent,
     1858                                                const Storage &st)
     1859{
     1860    xml::ElementNode *pelmStorageControllers = elmParent.createChild("StorageControllers");
     1861
     1862    for (StorageControllersList::const_iterator it = st.llStorageControllers.begin();
     1863         it != st.llStorageControllers.end();
     1864         ++it)
     1865    {
     1866        const StorageController &sc = *it;
     1867
     1868        xml::ElementNode *pelmController = pelmStorageControllers->createChild("StorageController");
     1869        pelmController->setAttribute("name", sc.strName);
     1870
     1871        const char *pcszType;
     1872        switch (sc.controllerType)
     1873        {
     1874            case StorageControllerType_IntelAhci: pcszType = "AHCI"; break;
     1875            case StorageControllerType_LsiLogic: pcszType = "LsiLogic"; break;
     1876            case StorageControllerType_BusLogic: pcszType = "BusLogic"; break;
     1877            case StorageControllerType_PIIX4: pcszType = "PIIX4"; break;
     1878            case StorageControllerType_ICH6: pcszType = "ICH6"; break;
     1879            default: /*case StorageControllerType_PIIX3:*/ pcszType = "PIIX3"; break;
     1880        }
     1881        pelmController->setAttribute("type", pcszType);
     1882
     1883        pelmController->setAttribute("PortCount", sc.ulPortCount);
     1884
     1885        if (sc.controllerType == StorageControllerType_IntelAhci)
     1886        {
     1887            pelmController->setAttribute("IDE0MasterEmulationPort", sc.lIDE0MasterEmulationPort);
     1888            pelmController->setAttribute("IDE0SlaveEmulationPort", sc.lIDE0SlaveEmulationPort);
     1889            pelmController->setAttribute("IDE1MasterEmulationPort", sc.lIDE1MasterEmulationPort);
     1890            pelmController->setAttribute("IDE1SlaveEmulationPort", sc.lIDE1SlaveEmulationPort);
     1891        }
     1892
     1893        for (AttachedDevicesList::const_iterator it2 = sc.llAttachedDevices.begin();
     1894             it2 != sc.llAttachedDevices.end();
     1895             ++it2)
     1896        {
     1897            const AttachedDevice &dev = *it2;
     1898
     1899            xml::ElementNode *pelmDevice = pelmController->createChild("AttachedDevice");
     1900            pelmDevice->setAttribute("type", "HardDisk");
     1901
     1902            pelmDevice->setAttribute("port", dev.lPort);
     1903            pelmDevice->setAttribute("device", dev.lDevice);
     1904            pelmDevice->createChild("Image")->setAttribute("uuid", makeString(dev.uuid));
     1905        }
     1906    }
     1907}
     1908
     1909/**
     1910 * Writes a single snapshot into the DOM tree. Initially this gets called from MachineConfigFile::write()
     1911 * for the root snapshot of a machine, if present; elmParent then points to the <Snapshots> node under the
     1912 * <Machine> node to which <Snapshot> must be added. This may then recurse for child snapshots.
     1913 * @param elmParent
     1914 * @param snap
     1915 */
     1916void MachineConfigFile::writeSnapshot(xml::ElementNode &elmParent,
     1917                                      const Snapshot &snap)
     1918{
     1919    xml::ElementNode *pelmSnapshot = elmParent.createChild("Snapshot");
     1920
     1921    pelmSnapshot->setAttribute("uuid", makeString(snap.uuid));
     1922    pelmSnapshot->setAttribute("name", snap.strName);
     1923    pelmSnapshot->setAttribute("Description", snap.strDescription);
     1924
     1925    pelmSnapshot->setAttribute("timeStamp", makeString(snap.timestamp));
     1926
     1927    if (snap.strStateFile.length())
     1928        pelmSnapshot->setAttribute("stateFile", snap.strStateFile);
     1929
     1930    writeHardware(*pelmSnapshot, snap.hardware);
     1931    writeStorageControllers(*pelmSnapshot, snap.storage);
     1932
     1933    if (snap.llChildSnapshots.size())
     1934    {
     1935        xml::ElementNode *pelmChildren = pelmSnapshot->createChild("Snapshots");
     1936        for (SnapshotsList::const_iterator it = snap.llChildSnapshots.begin();
     1937             it != snap.llChildSnapshots.end();
     1938             ++it)
     1939        {
     1940            const Snapshot &child = *it;
     1941            writeSnapshot(*pelmChildren, child);
     1942        }
     1943    }
     1944}
     1945
     1946/**
     1947 * Called from Main code to write a machine config file to disk. This builds a DOM tree from
     1948 * the member variables and then writes the XML file; it throws xml::Error instances on errors,
     1949 * in particular if the file cannot be written.
     1950 */
     1951void MachineConfigFile::write(const com::Utf8Str &strFilename)
     1952{
     1953    m->strFilename = strFilename;
     1954
     1955    createStubDocument();
     1956
     1957    xml::ElementNode *pelmMachine = m->pelmRoot->createChild("Machine");
     1958
     1959    pelmMachine->setAttribute("uuid", makeString(uuid));
     1960    pelmMachine->setAttribute("name", strName);
     1961    if (!fNameSync)
     1962        pelmMachine->setAttribute("nameSync", fNameSync);
     1963    if (strDescription.length())
     1964        pelmMachine->createChild("Description")->addContent(strDescription);
     1965    pelmMachine->setAttribute("OSType", strOsType);
     1966    if (strStateFile.length())
     1967        pelmMachine->setAttribute("stateFile", strStateFile);
     1968    if (!uuidCurrentSnapshot.isEmpty())
     1969        pelmMachine->setAttribute("currentSnapshot", makeString(uuidCurrentSnapshot));
     1970    if (strSnapshotFolder.length())
     1971        pelmMachine->setAttribute("snapshotFolder", strSnapshotFolder);
     1972    if (!fCurrentStateModified)
     1973        pelmMachine->setAttribute("currentStateModified", fCurrentStateModified);
     1974    pelmMachine->setAttribute("lastStateChange", makeString(timeLastStateChange));
     1975    if (fAborted)
     1976        pelmMachine->setAttribute("aborted", fAborted);
     1977
     1978    writeExtraData(*pelmMachine, mapExtraDataItems);
     1979
     1980    if (llFirstSnapshot.size())
     1981        writeSnapshot(*pelmMachine, llFirstSnapshot.front());
     1982
     1983    writeHardware(*pelmMachine, hardwareMachine);
     1984
     1985    writeStorageControllers(*pelmMachine, storageMachine);
     1986
     1987    // now go write the XML
     1988    xml::XmlFileWriter writer(*m->pDoc);
     1989    writer.write(m->strFilename.c_str());
     1990
     1991    m->fFileExists = true;
     1992
     1993    clearDocument();
     1994}
     1995
  • trunk/src/VBox/Main/xml/ovfreader.cpp

    r21701 r22173  
    3838    xml::XmlFileParser parser;
    3939    xml::Document doc;
    40     parser.read(m_strPath.raw(),
     40    parser.read(m_strPath,
    4141                doc);
    4242
    4343    const xml::ElementNode *pRootElem = doc.getRootElement();
    44     if (strcmp(pRootElem->getName(), "Envelope"))
     44    if (pRootElem && strcmp(pRootElem->getName(), "Envelope"))
    4545        throw OVFLogicError(N_("Root element in OVF file must be \"Envelope\"."));
    4646
Note: See TracChangeset for help on using the changeset viewer.

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