Index: trunk/include/VBox/xml.h
===================================================================
--- trunk/include/VBox/xml.h	(revision 17416)
+++ trunk/include/VBox/xml.h	(revision 17417)
@@ -168,4 +168,11 @@
 };
 
+class VBOXXML_CLASS ENodeIsNotElement : public LogicError
+{
+public:
+    ENodeIsNotElement(const char *aMsg = NULL) : LogicError(aMsg) {}
+    ENodeIsNotElement(RT_SRC_POS_DECL) : LogicError(RT_SRC_POS_ARGS) {}
+};
+
 // Runtime errors
 //////////////////////////////////////////////////////////////////////////////
@@ -420,7 +427,8 @@
 };
 
-/*
+/**
  * Node:
- *  an XML node, which represents either an element or an attribute.
+ *  an XML node, which represents either an element or text content
+ *  or an attribute.
  *
  *  For elements, getName() returns the element name, and getValue()
@@ -435,4 +443,5 @@
  *  --  xml::Document::createRootElement()
  *  --  xml::Node::createChild()
+ *  --  xml::Node::addContent()
  *  --  xml::Node::setAttribute()
  */
@@ -467,4 +476,5 @@
 
     Node* createChild(const char *pcszElementName);
+    Node* addContent(const char *pcszContent);
     Node* setAttribute(const char *pcszName, const char *pcszValue);
 
Index: trunk/src/VBox/Main/ApplianceImpl.cpp
===================================================================
--- trunk/src/VBox/Main/ApplianceImpl.cpp	(revision 17416)
+++ trunk/src/VBox/Main/ApplianceImpl.cpp	(revision 17417)
@@ -38,5 +38,8 @@
 using namespace std;
 
-// defines
+////////////////////////////////////////////////////////////////////////////////
+//
+// hardware definitions
+//
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -112,18 +115,4 @@
 struct VirtualSystem;
 
-// opaque private instance data of Appliance class
-struct Appliance::Data
-{
-    Utf8Str                 strPath;            // file name last given to either read() or write()
-
-    DiskImagesMap           mapDisks;           // map of DiskImage structs, sorted by DiskImage.strDiskId
-
-    NetworksMap             mapNetworks;        // map of Network structs, sorted by Network.strNetworkName
-
-    list<VirtualSystem>     llVirtualSystems;
-
-    list< ComObjPtr<VirtualSystemDescription> > virtualSystemDescriptions;
-};
-
 typedef map<uint32_t, VirtualHardwareItem> HardwareItemsMap;
 
@@ -198,4 +187,36 @@
     }
 };
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Appliance data definition
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// opaque private instance data of Appliance class
+struct Appliance::Data
+{
+    Utf8Str                 strPath;            // file name last given to either read() or write()
+
+    DiskImagesMap           mapDisks;           // map of DiskImage structs, sorted by DiskImage.strDiskId
+
+    NetworksMap             mapNetworks;        // map of Network structs, sorted by Network.strNetworkName
+
+    list<VirtualSystem>     llVirtualSystems;   // list of virtual systems, created by and valid after read()
+
+    list< ComObjPtr<VirtualSystemDescription> > virtualSystemDescriptions; //
+};
+
+struct VirtualSystemDescription::Data
+{
+    list<VirtualSystemDescriptionEntry> llDescriptions;
+    list<Utf8Str> llWarnings;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Threads
+//
+////////////////////////////////////////////////////////////////////////////////
 
 struct Appliance::TaskImportMachines
@@ -2455,6 +2476,186 @@
     {
         xml::Document doc;
-        doc.createRootElement("Envelope");
-
+        xml::Node *pelmRoot = doc.createRootElement("Envelope");
+
+        pelmRoot->setAttribute("ovf:version", "1.0");
+        pelmRoot->setAttribute("xml:lang", "en-US");
+        pelmRoot->setAttribute("xmlns", "http://schemas.dmtf.org/ovf/envelope/1");
+        pelmRoot->setAttribute("xmlns:ovf", "http://schemas.dmtf.org/ovf/envelope/1");
+        pelmRoot->setAttribute("xmlns:ovfstr", "http://schema.dmtf.org/ovf/strings/1");
+        pelmRoot->setAttribute("xmlns:rasd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData");
+        pelmRoot->setAttribute("xmlns:vssd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData");
+        pelmRoot->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
+        pelmRoot->setAttribute("xsi:schemaLocation", "http://schemas.dmtf.org/ovf/envelope/1 ../ovf-envelope.xsd");
+
+
+        // <Envelope>/<References>
+        xml::Node *pelmReferences = pelmRoot->createChild("References");
+                // @ŧodo
+
+        /* <Envelope>/<DiskSection>:
+            <DiskSection>
+                <Info>List of the virtual disks used in the package</Info>
+                <Disk ovf:capacity="4294967296" ovf:diskId="lamp" ovf:format="http://www.vmware.com/specifications/vmdk.html#compressed" ovf:populatedSize="1924967692"/>
+            </DiskSection> */
+        xml::Node *pelmDiskSection = pelmRoot->createChild("DiskSection");
+        xml::Node *pelmDiskSectionInfo = pelmDiskSection->createChild("Info");
+        pelmDiskSectionInfo->addContent("List of the virtual disks used in the package");
+        // @todo for each disk:
+        // xml::Node *pelmDisk = pelmDiskSection->createChild("Disk");
+
+        /* <Envelope>/<NetworkSection>:
+            <NetworkSection>
+                <Info>Logical networks used in the package</Info>
+                <Network ovf:name="VM Network">
+                    <Description>The network that the LAMP Service will be available on</Description>
+                </Network>
+            </NetworkSection> */
+        xml::Node *pelmNetworkSection = pelmRoot->createChild("NetworkSection");
+        xml::Node *pelmNetworkSectionInfo = pelmNetworkSection->createChild("Info");
+        pelmNetworkSectionInfo->addContent("Logical networks used in the package");
+        // @todo for each network:
+        // xml::Node *pelmNetwork = pelmNetworkSection->createChild("Network");
+
+        // and here come the virtual systems:
+        xml::Node *pelmVirtualSystemCollection = pelmRoot->createChild("VirtualSystemCollection");
+        xml::Node *pattrVirtualSystemCollectionId = pelmVirtualSystemCollection->setAttribute("ovf:id", "ExportedVirtualBoxMachines");      // whatever
+
+        list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
+        /* Iterate through all virtual systems of that appliance */
+        for (it = pAppliance->m->virtualSystemDescriptions.begin();
+             it != pAppliance->m->virtualSystemDescriptions.end();
+             ++it)
+        {
+            ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
+
+            xml::Node *pelmVirtualSystem = pelmVirtualSystemCollection->createChild("VirtualSystem");
+            xml::Node *pelmVirtualSystemInfo = pelmVirtualSystem->createChild("Info");      // @todo put in description here after implementing an entry for it
+
+            std::list<VirtualSystemDescriptionEntry*> llName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
+
+            std::list<VirtualSystemDescriptionEntry*> llOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
+            /*  <OperatingSystemSection ovf:id="82">
+                    <Info>Guest Operating System</Info>
+                    <Description>Linux 2.6.x</Description>
+                </OperatingSystemSection> */
+            xml::Node *pelmOperatingSystemSection = pelmVirtualSystem->createChild("OperatingSystemSection");
+            pelmOperatingSystemSection->setAttribute("ovf:id", "82");
+                    // @todo convert vbox OS type into OVF ID
+            pelmOperatingSystemSection->createChild("Info")->addContent("blah");        // @ŧodo
+            pelmOperatingSystemSection->createChild("Description")->addContent("blah");        // @ŧodo
+
+            // <VirtualHardwareSection ovf:id="hw1" ovf:transport="iso">
+            xml::Node *pelmVirtualHardwareSection = pelmVirtualSystem->createChild("VirtualHardwareSection");
+
+            /*  <System>
+                    <vssd:Description>Description of the virtual hardware section.</vssd:Description>
+                    <vssd:ElementName>vmware</vssd:ElementName>
+                    <vssd:InstanceID>1</vssd:InstanceID>
+                    <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
+                    <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
+                </System> */
+            xml::Node *pelmSystem = pelmVirtualHardwareSection->createChild("System");
+
+            // <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
+            xml::Node *pelmVirtualSystemType = pelmSystem->createChild("VirtualSystemType");
+            pelmVirtualSystemType->addContent("virtualbox-2.2");            // instead of vmx-7?
+
+            uint32_t ulInstanceID = 1;
+
+            list<VirtualSystemDescriptionEntry>::const_iterator itD;
+            for (itD = vsdescThis->m->llDescriptions.begin();
+                 itD != vsdescThis->m->llDescriptions.end();
+                 ++itD)
+            {
+                const VirtualSystemDescriptionEntry &desc = *itD;
+
+                OVFResourceType_T type = 0;     // if this becomes != 0 then we do stuff
+                Utf8Str strDescription;         // must also be set then
+                int32_t lVirtualQuantity = -1;
+                uint64_t uTemp;
+
+                switch (desc.type)
+                {
+                    case VirtualSystemDescriptionType_CPU:
+                        /*  <Item>
+                                <rasd:Caption>1 virtual CPU</rasd:Caption>
+                                <rasd:Description>Number of virtual CPUs</rasd:Description>
+                                <rasd:ElementName>virtual CPU</rasd:ElementName>
+                                <rasd:InstanceID>1</rasd:InstanceID>
+                                <rasd:ResourceType>3</rasd:ResourceType>
+                                <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
+                            </Item> */
+                        strDescription = "Number of virtual CPUs";
+                        type = OVFResourceType_Processor; // 3
+                        lVirtualQuantity = 1;
+                    break;
+
+                    case VirtualSystemDescriptionType_Memory:
+                        /*  <Item>
+                                <rasd:AllocationUnits>MegaBytes</rasd:AllocationUnits>
+                                <rasd:Caption>256 MB of memory</rasd:Caption>
+                                <rasd:Description>Memory Size</rasd:Description>
+                                <rasd:ElementName>Memory</rasd:ElementName>
+                                <rasd:InstanceID>2</rasd:InstanceID>
+                                <rasd:ResourceType>4</rasd:ResourceType>
+                                <rasd:VirtualQuantity>256</rasd:VirtualQuantity>
+                            </Item> */
+                        strDescription = "Memory Size";
+                        type = OVFResourceType_Memory; // 4
+                        desc.strVbox.toInt(uTemp);
+                        lVirtualQuantity = uTemp / _1M;
+                    break;
+
+                    case VirtualSystemDescriptionType_HardDiskControllerIDE:
+                    break;
+                    case VirtualSystemDescriptionType_HardDiskControllerSATA:
+                    break;
+                    case VirtualSystemDescriptionType_HardDiskControllerSCSI:
+                    break;
+
+                    case VirtualSystemDescriptionType_HardDiskImage:
+                    break;
+
+                    case VirtualSystemDescriptionType_Floppy:
+                    break;
+
+                    case VirtualSystemDescriptionType_CDROM:
+                    break;
+
+                    case VirtualSystemDescriptionType_LogicalNetwork:
+                    break;
+
+                    case VirtualSystemDescriptionType_NetworkAdapter:
+                    break;
+
+                    case VirtualSystemDescriptionType_USBController:
+                    break;
+
+                    case VirtualSystemDescriptionType_SoundCard:
+                    break;
+                }
+
+                if (type)
+                {
+                    xml::Node *pItem;
+                    pItem = pelmVirtualHardwareSection->createChild("Item");
+
+                    pItem->createChild("rasd:Description")->addContent(strDescription.c_str());
+
+                    // <rasd:InstanceID>1</rasd:InstanceID>
+                    pItem->createChild("rasd:InstanceID")->addContent(Utf8StrFmt("%d", ulInstanceID).c_str());
+
+                    // <rasd:ResourceType>3</rasd:ResourceType>
+                    pItem->createChild("rasd:ResourceType")->addContent(Utf8StrFmt("%d", type).c_str());
+
+                    // <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
+                    if (lVirtualQuantity != -1)
+                        pItem->createChild("rasd:VirtualQuantity")->addContent(Utf8StrFmt("%d", lVirtualQuantity).c_str());
+
+                }
+            }
+        }
+
+        // now go write the XML
         xml::XmlFileWriter writer(doc);
         writer.write(pAppliance->m->strPath.c_str());
@@ -2481,5 +2682,4 @@
 }
 
-#
 ////////////////////////////////////////////////////////////////////////////////
 //
@@ -2490,10 +2690,4 @@
 DEFINE_EMPTY_CTOR_DTOR(VirtualSystemDescription)
 struct shutup3 {};
-
-struct VirtualSystemDescription::Data
-{
-    list<VirtualSystemDescriptionEntry> descriptions;
-    list<Utf8Str> warnings;
-};
 
 /**
@@ -2546,5 +2740,5 @@
     AutoReadLock alock(this);
 
-    *aCount = (ULONG)m->descriptions.size();
+    *aCount = (ULONG)m->llDescriptions.size();
 
     return S_OK;
@@ -2573,5 +2767,5 @@
     AutoReadLock alock(this);
 
-    ULONG c = (ULONG)m->descriptions.size();
+    ULONG c = (ULONG)m->llDescriptions.size();
     com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);
     com::SafeArray<BSTR> sfaRefs(c);
@@ -2582,6 +2776,6 @@
     list<VirtualSystemDescriptionEntry>::const_iterator it;
     size_t i = 0;
-    for (it = m->descriptions.begin();
-         it != m->descriptions.end();
+    for (it = m->llDescriptions.begin();
+         it != m->llDescriptions.end();
          ++it, ++i)
     {
@@ -2631,6 +2825,6 @@
     com::SafeArray<IN_BSTR> aExtraConfigValues(ComSafeArrayInArg(argExtraConfigValues));
 
-    if (    (aVboxValues.size() != m->descriptions.size())
-         || (aExtraConfigValues.size() != m->descriptions.size())
+    if (    (aVboxValues.size() != m->llDescriptions.size())
+         || (aExtraConfigValues.size() != m->llDescriptions.size())
        )
         return E_INVALIDARG;
@@ -2638,6 +2832,6 @@
     list<VirtualSystemDescriptionEntry>::iterator it;
     size_t i = 0;
-    for (it = m->descriptions.begin();
-         it != m->descriptions.end();
+    for (it = m->llDescriptions.begin();
+         it != m->llDescriptions.end();
          ++it, ++i)
     {
@@ -2670,10 +2864,10 @@
     AutoReadLock alock(this);
 
-    com::SafeArray<BSTR> sfaWarnings(m->warnings.size());
+    com::SafeArray<BSTR> sfaWarnings(m->llWarnings.size());
 
     list<Utf8Str>::const_iterator it;
     size_t i = 0;
-    for (it = m->warnings.begin();
-         it != m->warnings.end();
+    for (it = m->llWarnings.begin();
+         it != m->llWarnings.end();
          ++it, ++i)
     {
@@ -2702,5 +2896,5 @@
 {
     VirtualSystemDescriptionEntry vsde;
-    vsde.ulIndex = (uint32_t)m->descriptions.size();      // each entry gets an index so the client side can reference them
+    vsde.ulIndex = (uint32_t)m->llDescriptions.size();      // each entry gets an index so the client side can reference them
     vsde.type = aType;
     vsde.strRef = strRef;
@@ -2709,5 +2903,5 @@
     vsde.strExtraConfig = strExtraConfig;
 
-    m->descriptions.push_back(vsde);
+    m->llDescriptions.push_back(vsde);
 }
 
@@ -2718,5 +2912,5 @@
     Utf8StrFmtVA str(aWarning, args);
     va_end(args);
-    m->warnings.push_back(str);
+    m->llWarnings.push_back(str);
 }
 
@@ -2732,6 +2926,6 @@
 
     list<VirtualSystemDescriptionEntry>::iterator it;
-    for (it = m->descriptions.begin();
-         it != m->descriptions.end();
+    for (it = m->llDescriptions.begin();
+         it != m->llDescriptions.end();
          ++it)
     {
@@ -2754,6 +2948,6 @@
     Utf8Str strRef = Utf8StrFmt("%RI32", id);
     list<VirtualSystemDescriptionEntry>::const_iterator it;
-    for (it = m->descriptions.begin();
-         it != m->descriptions.end();
+    for (it = m->llDescriptions.begin();
+         it != m->llDescriptions.end();
          ++it)
     {
@@ -2877,5 +3071,5 @@
         /* Memory */
         Utf8Str strMemory = Utf8StrFmt("%RI32", (uint64_t)ulMemSizeMB * _1M);
-        pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
+        pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
                            "",
                            strMemory,
Index: trunk/src/VBox/Main/xml/xml.cpp
===================================================================
--- trunk/src/VBox/Main/xml/xml.cpp	(revision 17416)
+++ trunk/src/VBox/Main/xml/xml.cpp	(revision 17417)
@@ -411,9 +411,10 @@
 struct Node::Data
 {
-    xmlNode     *plibNode;          // != NULL if this is an element
-    xmlAttr     *plibAttr;          // != NULL if this is an attribute
+    xmlNode     *plibNode;          // != NULL if this is an element or content node
+    xmlAttr     *plibAttr;          // != NULL if this is an attribute node
 
     Node        *pParent;           // NULL only for the root element
-    const char  *pcszName;          // points either into plibNode or plibAttr
+    const char  *pcszName;          // element or attribute name, points either into plibNode or plibAttr;
+                                    // NULL if this is a content node
 
     struct compare_const_char
@@ -748,4 +749,111 @@
     return false;
 }
+
+/**
+ * Creates a new child element node and appends it to the list
+ * of children in "this".
+ *
+ * @param pcszElementName
+ * @return
+ */
+Node* Node::createChild(const char *pcszElementName)
+{
+    // we must be an element, not an attribute
+    if (!m->plibNode)
+        throw ENodeIsNotElement(RT_SRC_POS);
+
+    // libxml side: create new node
+    xmlNode *plibNode;
+    if (!(plibNode = xmlNewNode(NULL,        // namespace
+                                (const xmlChar*)pcszElementName)))
+        throw ENoMemory();
+    xmlAddChild(m->plibNode, plibNode);
+
+    // now wrap this in C++
+    Node *p = new Node;
+    boost::shared_ptr<Node> pNew(p);
+    pNew->m->plibNode = plibNode;
+    pNew->m->pcszName = (const char*)plibNode->name;
+
+    m->children.push_back(pNew);
+
+    return p;
+}
+
+
+/**
+ * Creates a content node and appends it to the list of children
+ * in "this".
+ *
+ * @param pcszElementName
+ * @return
+ */
+Node* Node::addContent(const char *pcszContent)
+{
+    // we must be an element, not an attribute
+    if (!m->plibNode)
+        throw ENodeIsNotElement(RT_SRC_POS);
+
+    // libxml side: create new node
+    xmlNode *plibNode;
+    if (!(plibNode = xmlNewText((const xmlChar*)pcszContent)))
+        throw ENoMemory();
+    xmlAddChild(m->plibNode, plibNode);
+
+    // now wrap this in C++
+    Node *p = new Node;
+    boost::shared_ptr<Node> pNew(p);
+    pNew->m->plibNode = plibNode;
+    pNew->m->pcszName = NULL;
+
+    m->children.push_back(pNew);
+
+}
+
+/**
+ * Sets the given attribute. Assumes that "this" is an element node,
+ * otherwise ENodeIsNotElement is thrown.
+ *
+ * If an attribute with the given name exists, it is overwritten,
+ * otherwise a new attribute is created. Returns the attribute node
+ * that was either created or changed.
+ *
+ * @param pcszName
+ * @param pcszValue
+ * @return
+ */
+Node* Node::setAttribute(const char *pcszName, const char *pcszValue)
+{
+    // we must be an element, not an attribute
+    if (!m->plibNode)
+        throw ENodeIsNotElement(RT_SRC_POS);
+
+    Data::AttributesMap::const_iterator it;
+
+    it = m->attribs.find(pcszName);
+    if (it == m->attribs.end())
+    {
+        // libxml side: xmlNewProp creates an attribute
+        xmlAttr *plibAttr = xmlNewProp(m->plibNode, (xmlChar*)pcszName, (xmlChar*)pcszValue);
+        const char *pcszAttribName = (const char*)plibAttr->name;
+
+        // C++ side: create an attribute node around it
+        boost::shared_ptr<Node> pNew(new Node);
+        pNew->m->plibAttr = plibAttr;
+        pNew->m->pcszName = (const char*)plibAttr->name;
+        pNew->m->pParent = this;
+        // store
+        m->attribs[pcszAttribName] = pNew;
+    }
+    else
+    {
+        // @todo
+        throw LogicError("Attribute exists");
+    }
+
+    return NULL;
+
+}
+
 
 /*
@@ -899,15 +1007,19 @@
 Node* Document::createRootElement(const char *pcszRootElementName)
 {
-    if (m->pRootElement)
+    if (m->pRootElement || m->plibDocument)
         throw EDocumentNotEmpty(RT_SRC_POS);
 
+    // libxml side: create document, create root node
     m->plibDocument = xmlNewDoc((const xmlChar*)"1.0");
-    if (!(m->pRootElement = new Node()))
+    xmlNode *plibRootNode;
+    if (!(plibRootNode = xmlNewNode(NULL,        // namespace
+                                    (const xmlChar*)pcszRootElementName)))
         throw ENoMemory();
-    Node::Data *pNodeData = m->pRootElement->m;
-    if (!(pNodeData->plibNode = xmlNewNode(NULL,        // namespace
-                                           (const xmlChar*)pcszRootElementName)))
-        throw ENoMemory();
-    pNodeData->pcszName = (const char*)pNodeData->plibNode->name;
+    xmlDocSetRootElement(m->plibDocument, plibRootNode);
+
+    // now wrap this in C++
+    m->pRootElement = new Node();
+    m->pRootElement->m->plibNode = plibRootNode;
+    m->pRootElement->m->pcszName = (const char*)plibRootNode->name;
 
     return m->pRootElement;
