Index: /trunk/src/VBox/Main/ApplianceImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/ApplianceImpl.cpp	(revision 20080)
+++ /trunk/src/VBox/Main/ApplianceImpl.cpp	(revision 20081)
@@ -21,11 +21,15 @@
  */
 
-#include <VBox/param.h>
 #include <iprt/stream.h>
 #include <iprt/path.h>
 #include <iprt/dir.h>
 #include <iprt/file.h>
+#include <iprt/s3.h>
+
+#include <VBox/param.h>
+#include <VBox/version.h>
 
 #include "ApplianceImpl.h"
+#include "VFSExplorerImpl.h"
 #include "VirtualBoxImpl.h"
 #include "GuestOSTypeImpl.h"
@@ -460,5 +464,4 @@
  * @return
  */
-
 HRESULT Appliance::init(VirtualBox *aVirtualBox)
 {
@@ -2501,33 +2504,115 @@
 struct Appliance::TaskWriteOVF
 {
-    TaskWriteOVF(Appliance *aThat, Progress *aProgress)
-        : pAppliance(aThat),
-          enFormat(unspecified),
-          progress(aProgress),
+    enum OVFFormat
+    {
+        unspecified,
+        OVF_0_9,
+        OVF_1_0
+    };
+    enum TaskType
+    {
+        Write
+    };
+
+    TaskWriteOVF(OVFFormat aFormat, Appliance *aThat)
+        : taskType(Write),
+          storageType(VFSType_File),
+          enFormat(aFormat),
+          pAppliance(aThat),
           rc(S_OK)
     {}
     ~TaskWriteOVF() {}
 
-    HRESULT startThread();
-
+    int startThread();
+    static int uploadProgress(unsigned uPercent, void *pvUser);
+
+    TaskType taskType;
+    VFSType_T storageType;
+    Utf8Str filepath;
+    Utf8Str hostname;
+    Utf8Str username;
+    Utf8Str password;
+    OVFFormat enFormat;
     Appliance *pAppliance;
-    enum { unspecified, OVF_0_9, OVF_1_0 }
-        enFormat;
-
     ComObjPtr<Progress> progress;
     HRESULT rc;
 };
 
-HRESULT Appliance::TaskWriteOVF::startThread()
+int Appliance::TaskWriteOVF::startThread()
 {
     int vrc = RTThreadCreate(NULL, Appliance::taskThreadWriteOVF, this,
                              0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
                              "Appliance::Task");
+
     ComAssertMsgRCRet(vrc,
-                      ("Could not create taskThreadExportOVF (%Rrc)\n", vrc), E_FAIL);
+                      ("Could not create taskThreadWriteOVF (%Rrc)\n", vrc), E_FAIL);
 
     return S_OK;
 }
 
+/* static */
+int Appliance::TaskWriteOVF::uploadProgress(unsigned uPercent, void *pvUser)
+{
+    Appliance::TaskWriteOVF* pTask = *(Appliance::TaskWriteOVF**)pvUser;
+
+    if (pTask &&
+        !pTask->progress.isNull())
+    {
+        BOOL fCanceled;
+        pTask->progress->COMGETTER(Canceled)(&fCanceled);
+        if (fCanceled)
+            return -1;
+        pTask->progress->setCurrentOperationProgress(uPercent);
+    }
+    return VINF_SUCCESS;
+}
+
+STDMETHODIMP Appliance::CreateVFSExplorer(IN_BSTR aURI, IVFSExplorer **aExplorer)
+{
+    HRESULT rc = S_OK;
+
+    CheckComArgOutPointerValid(aExplorer);
+
+    AutoCaller autoCaller(this);
+    CheckComRCReturnRC(autoCaller.rc());
+
+    AutoReadLock(this);
+
+    Utf8Str uri(aURI);
+    /* Check which kind of export the user has requested */
+    VFSType_T type = VFSType_File;
+    Utf8Str strProtocol = "";
+    /* Check the URI for the target format */
+    if (uri.startsWith("SunCloud://", Utf8Str::CaseInsensitive)) /* Sun Cloud service */
+    {
+        type = VFSType_S3;
+        strProtocol = "SunCloud://";
+    }
+    else if (uri.startsWith("S3://", Utf8Str::CaseInsensitive)) /* S3 service */
+    {
+        type = VFSType_S3;
+        strProtocol = "S3://";
+    }
+    else if (uri.startsWith("webdav://", Utf8Str::CaseInsensitive)) /* webdav service */
+        throw E_NOTIMPL;
+
+    Utf8Str strFilepath;
+    Utf8Str strHostname;
+    Utf8Str strUsername;
+    Utf8Str strPassword;
+    parseURI(uri, strProtocol, strFilepath, strHostname, strUsername, strPassword);
+
+    ComObjPtr<VFSExplorer> explorer;
+    explorer.createObject();
+
+    rc = explorer->init(type, strFilepath, strHostname, strUsername, strPassword, mVirtualBox);
+
+    if (SUCCEEDED(rc))
+        /* Return explorer to the caller */
+        explorer.queryInterfaceTo(aExplorer);
+
+    return rc;
+}
+
 STDMETHODIMP Appliance::Write(IN_BSTR format, IN_BSTR path, IProgress **aProgress)
 {
@@ -2537,44 +2622,26 @@
 
     AutoCaller autoCaller(this);
-    if (FAILED(rc = autoCaller.rc())) return rc;
+    CheckComRCReturnRC(autoCaller.rc());
 
     AutoWriteLock(this);
 
     // see if we can handle this file; for now we insist it has an ".ovf" extension
-    m->strPath = path;
-    if (!m->strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
+    Utf8Str strPath = path;
+    if (!strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
         return setError(VBOX_E_FILE_ERROR,
                         tr("Appliance file must have .ovf extension"));
 
     ComObjPtr<Progress> progress;
-    try
-    {
-        Bstr progressDesc = BstrFmt(tr("Export appliance '%s'"),
-                                    m->strPath.raw());
-        rc = setUpProgress(progress, progressDesc);
-        if (FAILED(rc)) throw rc;
-
-        /* Initialize our worker task */
-        std::auto_ptr<TaskWriteOVF> task(new TaskWriteOVF(this, progress));
-        //AssertComRCThrowRC (task->autoCaller.rc());
-
-        Utf8Str strFormat(format);
-        if (strFormat == "ovf-0.9")
-            task->enFormat = TaskWriteOVF::OVF_0_9;
-        else if (strFormat == "ovf-1.0")
-            task->enFormat = TaskWriteOVF::OVF_1_0;
-        else
-            return setError(VBOX_E_FILE_ERROR,
-                            tr("Invalid format \"%s\" specified"), strFormat.c_str());
-
-        rc = task->startThread();
-        CheckComRCThrowRC(rc);
-
-        task.release();
-    }
-    catch (HRESULT aRC)
-    {
-        rc = aRC;
-    }
+    Utf8Str strFormat(format);
+    TaskWriteOVF::OVFFormat ovfF;
+    if (strFormat == "ovf-0.9")
+        ovfF = TaskWriteOVF::OVF_0_9;
+    else if (strFormat == "ovf-1.0")
+        ovfF = TaskWriteOVF::OVF_1_0;
+    else
+        return setError(VBOX_E_FILE_ERROR,
+                        tr("Invalid format \"%s\" specified"), strFormat.c_str());
+
+    rc = writeImpl(ovfF, strPath, progress);
 
     if (SUCCEEDED(rc))
@@ -2583,4 +2650,113 @@
 
     return rc;
+}
+
+void Appliance::parseURI(Utf8Str strUri, const Utf8Str &strProtocol, Utf8Str &strFilepath, Utf8Str &strHostname, Utf8Str &strUsername, Utf8Str &strPassword)
+{
+    /* Remove the protocol */
+    if (strUri.startsWith(strProtocol, Utf8Str::CaseInsensitive))
+        strUri = strUri.substr(strProtocol.length());
+    size_t uppos = strUri.find("@");
+    if (uppos != Utf8Str::npos)
+    {
+        strUsername = strUri.substr(0, uppos);
+        strUri = strUri.substr(uppos + 1);
+        size_t upos = strUsername.find(":");
+        if (upos != Utf8Str::npos)
+        {
+            strPassword = strUsername.substr(upos + 1);
+            strUsername = strUsername.substr(0, upos);
+        }
+    }
+    size_t hpos = strUri.find("/");
+    if (hpos != Utf8Str::npos)
+    {
+        strHostname = strUri.substr(0, hpos);
+        strUri = strUri.substr(hpos);
+    }
+    strFilepath = strUri;
+}
+
+HRESULT Appliance::writeImpl(int aFormat, Utf8Str aPath, ComObjPtr<Progress> &aProgress)
+{
+    HRESULT rc = S_OK;
+    try
+    {
+        m->strPath = aPath;
+
+        /* Initialize our worker task */
+        std::auto_ptr<TaskWriteOVF> task(new TaskWriteOVF((TaskWriteOVF::OVFFormat)aFormat, this));
+
+        /* Check which kind of export the user has requested */
+        Utf8Str strProtocol = "";
+        /* Check the URI for the target format */
+        if (m->strPath.startsWith("SunCloud://", Utf8Str::CaseInsensitive)) /* Sun Cloud service */
+        {
+            task->storageType = VFSType_S3;
+            strProtocol = "SunCloud://";
+        }
+        else if (m->strPath.startsWith("S3://", Utf8Str::CaseInsensitive)) /* S3 service */
+        {
+            task->storageType = VFSType_S3;
+            strProtocol = "S3://";
+        }
+        else if (m->strPath.startsWith("webdav://", Utf8Str::CaseInsensitive)) /* webdav service */
+            throw E_NOTIMPL;
+
+        parseURI(m->strPath, strProtocol, task->filepath, task->hostname, task->username, task->password);
+        Bstr progressDesc = BstrFmt(tr("Export appliance '%s'"),
+                                    task->filepath.c_str());
+
+        /* todo: This progress init stuff should be done a little bit more generic */
+        if (task->storageType == VFSType_S3)
+            rc = setUpProgressUpload(aProgress, progressDesc);
+        else
+            rc = setUpProgress(aProgress, progressDesc);
+        if (FAILED(rc)) throw rc;
+
+        task->progress = aProgress;
+
+        rc = task->startThread();
+        CheckComRCThrowRC(rc);
+
+        /* Don't destruct on success */
+        task.release();
+    }
+    catch (HRESULT aRC)
+    {
+        rc = aRC;
+    }
+
+    return rc;
+}
+
+DECLCALLBACK(int) Appliance::taskThreadWriteOVF(RTTHREAD /* aThread */, void *pvUser)
+{
+    std::auto_ptr<TaskWriteOVF> task(static_cast<TaskWriteOVF*>(pvUser));
+    AssertReturn(task.get(), VERR_GENERAL_FAILURE);
+
+    Appliance *pAppliance = task->pAppliance;
+
+    LogFlowFuncEnter();
+    LogFlowFunc(("Appliance %p\n", pAppliance));
+
+    HRESULT rc = S_OK;
+
+    switch(task->taskType)
+    {
+        case TaskWriteOVF::Write:
+        {
+            if (task->storageType == VFSType_File)
+                rc = pAppliance->writeFS(task.get());
+            else if (task->storageType == VFSType_S3)
+                rc = pAppliance->writeS3(task.get());
+            break;
+        }
+    }
+
+    LogFlowFunc(("rc=%Rhrc\n", rc));
+    LogFlowFuncLeave();
+
+    return VINF_SUCCESS;
 }
 
@@ -2591,22 +2767,15 @@
  */
 /* static */
-DECLCALLBACK(int) Appliance::taskThreadWriteOVF(RTTHREAD /* aThread */, void *pvUser)
-{
-    std::auto_ptr<TaskWriteOVF> task(static_cast<TaskWriteOVF*>(pvUser));
-    AssertReturn(task.get(), VERR_GENERAL_FAILURE);
-
-    Appliance *pAppliance = task->pAppliance;
-
+int Appliance::writeFS(TaskWriteOVF *pTask)
+{
     LogFlowFuncEnter();
-    LogFlowFunc(("Appliance %p\n", pAppliance));
-
-    AutoCaller autoCaller(pAppliance);
+    LogFlowFunc(("Appliance %p\n", this));
+
+    AutoCaller autoCaller(this);
     CheckComRCReturnRC(autoCaller.rc());
 
-    AutoWriteLock appLock(pAppliance);
+    AutoWriteLock appLock(this);
 
     HRESULT rc = S_OK;
-
-    ComPtr<IVirtualBox> pVirtualBox(pAppliance->mVirtualBox);
 
     try
@@ -2615,5 +2784,5 @@
         xml::ElementNode *pelmRoot = doc.createRootElement("Envelope");
 
-        pelmRoot->setAttribute("ovf:version", (task->enFormat == TaskWriteOVF::OVF_1_0) ? "1.0" : "0.9");
+        pelmRoot->setAttribute("ovf:version", (pTask->enFormat == TaskWriteOVF::OVF_1_0) ? "1.0" : "0.9");
         pelmRoot->setAttribute("xml:lang", "en-US");
 
@@ -2639,5 +2808,5 @@
             </DiskSection> */
         xml::ElementNode *pelmDiskSection;
-        if (task->enFormat == TaskWriteOVF::OVF_0_9)
+        if (pTask->enFormat == TaskWriteOVF::OVF_0_9)
         {
             // <Section xsi:type="ovf:DiskSection_Type">
@@ -2662,5 +2831,5 @@
             </NetworkSection> */
         xml::ElementNode *pelmNetworkSection;
-        if (task->enFormat == TaskWriteOVF::OVF_0_9)
+        if (pTask->enFormat == TaskWriteOVF::OVF_0_9)
         {
             // <Section xsi:type="ovf:NetworkSection_Type">
@@ -2684,7 +2853,7 @@
         // one machine, it seems
         xml::ElementNode *pelmToAddVirtualSystemsTo;
-        if (pAppliance->m->virtualSystemDescriptions.size() > 1)
+        if (m->virtualSystemDescriptions.size() > 1)
         {
-            if (task->enFormat == TaskWriteOVF::OVF_0_9)
+            if (pTask->enFormat == TaskWriteOVF::OVF_0_9)
                 throw setError(VBOX_E_FILE_ERROR,
                                tr("Cannot export more than one virtual system with OVF 0.9, use OVF 1.0"));
@@ -2698,6 +2867,6 @@
         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();
+        for (it = m->virtualSystemDescriptions.begin();
+             it != m->virtualSystemDescriptions.end();
              ++it)
         {
@@ -2705,5 +2874,5 @@
 
             xml::ElementNode *pelmVirtualSystem;
-            if (task->enFormat == TaskWriteOVF::OVF_0_9)
+            if (pTask->enFormat == TaskWriteOVF::OVF_0_9)
             {
                 // <Section xsi:type="ovf:NetworkSection_Type">
@@ -2749,5 +2918,5 @@
                 </Section> */
                 xml::ElementNode *pelmAnnotationSection;
-                if (task->enFormat == TaskWriteOVF::OVF_0_9)
+                if (pTask->enFormat == TaskWriteOVF::OVF_0_9)
                 {
                     // <Section ovf:required="false" xsi:type="ovf:ProductSection_Type">
@@ -2781,5 +2950,5 @@
                     </Section> */
                 xml::ElementNode *pelmAnnotationSection;
-                if (task->enFormat == TaskWriteOVF::OVF_0_9)
+                if (pTask->enFormat == TaskWriteOVF::OVF_0_9)
                 {
                     // <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type">
@@ -2804,5 +2973,5 @@
                    </EulaSection> */
                 xml::ElementNode *pelmEulaSection;
-                if (task->enFormat == TaskWriteOVF::OVF_0_9)
+                if (pTask->enFormat == TaskWriteOVF::OVF_0_9)
                 {
                     pelmEulaSection = pelmVirtualSystem->createChild("Section");
@@ -2826,5 +2995,5 @@
                 </OperatingSystemSection> */
             xml::ElementNode *pelmOperatingSystemSection;
-            if (task->enFormat == TaskWriteOVF::OVF_0_9)
+            if (pTask->enFormat == TaskWriteOVF::OVF_0_9)
             {
                 pelmOperatingSystemSection = pelmVirtualSystem->createChild("Section");
@@ -2842,5 +3011,5 @@
             // <VirtualHardwareSection ovf:id="hw1" ovf:transport="iso">
             xml::ElementNode *pelmVirtualHardwareSection;
-            if (task->enFormat == TaskWriteOVF::OVF_0_9)
+            if (pTask->enFormat == TaskWriteOVF::OVF_0_9)
             {
                 // <Section xsi:type="ovf:VirtualHardwareSection_Type">
@@ -2868,5 +3037,5 @@
             // <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
             const char *pcszHardware = "virtualbox-2.2";
-            if (task->enFormat == TaskWriteOVF::OVF_0_9)
+            if (pTask->enFormat == TaskWriteOVF::OVF_0_9)
                 // pretend to be vmware compatible then
                 pcszHardware = "vmx-6";
@@ -3226,5 +3395,5 @@
                         // <rasd:InstanceID>1</rasd:InstanceID>
                         xml::ElementNode *pelmInstanceID;
-                        if (task->enFormat == TaskWriteOVF::OVF_0_9)
+                        if (pTask->enFormat == TaskWriteOVF::OVF_0_9)
                             pelmInstanceID = pItem->createChild("rasd:InstanceId");
                         else
@@ -3302,5 +3471,5 @@
             const Utf8Str &strTargetFileNameOnly = pDiskEntry->strOvf;
             // target path needs to be composed from where the output OVF is
-            Utf8Str strTargetFilePath = stripFilename(pAppliance->m->strPath);
+            Utf8Str strTargetFilePath = stripFilename(m->strPath);
             strTargetFilePath.append("/");
             strTargetFilePath.append(strTargetFileNameOnly);
@@ -3312,5 +3481,5 @@
 
             Log(("Finding source disk \"%ls\"\n", bstrSrcFilePath.raw()));
-            rc = pVirtualBox->FindHardDisk(bstrSrcFilePath, pSourceDisk.asOutParam());
+            rc = mVirtualBox->FindHardDisk(bstrSrcFilePath, pSourceDisk.asOutParam());
             if (FAILED(rc)) throw rc;
 
@@ -3320,5 +3489,5 @@
             // create a new hard disk interface for the destination disk image
             Log(("Creating target disk \"%s\"\n", strTargetFilePath.raw()));
-            rc = pVirtualBox->CreateHardDisk(bstrSrcFormat, Bstr(strTargetFilePath), pTargetDisk.asOutParam());
+            rc = mVirtualBox->CreateHardDisk(bstrSrcFormat, Bstr(strTargetFilePath), pTargetDisk.asOutParam());
             if (FAILED(rc)) throw rc;
 
@@ -3332,10 +3501,10 @@
 
                 // advance to the next operation
-                if (!task->progress.isNull())
-                    task->progress->setNextOperation(BstrFmt(tr("Exporting virtual disk image '%s'"), strSrcFilePath.c_str()),
+                if (!pTask->progress.isNull())
+                    pTask->progress->setNextOperation(BstrFmt(tr("Exporting virtual disk image '%s'"), strSrcFilePath.c_str()),
                                                      pDiskEntry->ulSizeMB);     // operation's weight, as set up with the IProgress originally);
 
                 // now wait for the background disk operation to complete; this throws HRESULTs on error
-                pAppliance->waitForAsyncProgress(task->progress, pProgress2);
+                waitForAsyncProgress(pTask->progress, pProgress2);
             }
             catch (HRESULT rc3)
@@ -3344,5 +3513,5 @@
                 // it'll stick in the registry forever
                 pTargetDisk->Close();
-                throw rc3;
+                throw;
             }
 
@@ -3381,5 +3550,5 @@
         // now go write the XML
         xml::XmlFileWriter writer(doc);
-        writer.write(pAppliance->m->strPath.c_str());
+        writer.write(m->strPath.c_str());
     }
     catch(xml::Error &x)
@@ -3393,8 +3562,186 @@
     }
 
-    task->rc = rc;
-
-    if (!task->progress.isNull())
-        task->progress->notifyComplete(rc);
+    pTask->rc = rc;
+
+    if (!pTask->progress.isNull())
+        pTask->progress->notifyComplete(rc);
+
+    LogFlowFunc(("rc=%Rhrc\n", rc));
+    LogFlowFuncLeave();
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * Worker thread implementation for Upload() (ovf uploader).
+ * @param aThread
+ * @param pvUser
+ */
+/* static */
+int Appliance::writeS3(TaskWriteOVF *pTask)
+{
+    LogFlowFuncEnter();
+    LogFlowFunc(("Appliance %p\n", this));
+
+    AutoCaller autoCaller(this);
+    CheckComRCReturnRC(autoCaller.rc());
+
+    HRESULT rc = S_OK;
+
+    AutoWriteLock appLock(this);
+
+    /* Buckets are S3 specific. So parse the bucket out of the file path */
+    Utf8Str tmpPath = pTask->filepath;
+    if (!tmpPath.startsWith("/"))
+        return setError(E_INVALIDARG,
+                        tr("The path '%s' must start with /"), tmpPath.c_str());
+    Utf8Str bucket;
+    size_t bpos = tmpPath.find("/", 1);
+    if (bpos != Utf8Str::npos)
+    {
+        bucket = tmpPath.substr(1, bpos - 1); /* The bucket without any slashes */
+        tmpPath = tmpPath.substr(bpos); /* The rest of the file path */
+    }
+    /* If there is no bucket name provided reject the upload */
+    if (bucket.isEmpty())
+        return setError(E_INVALIDARG,
+                        tr("You doesn't provide a bucket name in the URI"), tmpPath.c_str());
+
+    int vrc = VINF_SUCCESS;
+    RTS3 hS3 = NULL;
+    char szOSTmpDir[RTPATH_MAX];
+    RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));
+    /* The template for the temporary directory created below */
+    char *pszTmpDir;
+    RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir);
+    list< pair<Utf8Str, ULONG> > filesList;
+
+    // todo:
+    // - getting the tmp directory (especially on win)
+    // - usable error codes
+    // - seems snapshot filenames are problematic {uuid}.vdi
+    try
+    {
+        /* We need a temporary directory which we can put the OVF file & all
+         * disk images in */
+        vrc = RTDirCreateTemp(pszTmpDir);
+        if (RT_FAILURE(rc))
+            throw setError(VBOX_E_FILE_ERROR,
+                           tr("Cannot create temporary directory '%s'"), pszTmpDir);
+
+        /* The temporary name of the target OVF file */
+        Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath));
+
+        /* Prepare the temporary writing of the OVF */
+        ComObjPtr<Progress> progress;
+        rc = writeImpl(pTask->enFormat, strTmpOvf.c_str(), progress);
+        if (FAILED(rc)) throw rc;
+
+        /* Unlock the appliance for the writing thread */
+        appLock.unlock();
+        /* Wait until the writing is done, but report the progress back to the
+           caller */
+        ComPtr<IProgress> progressInt(progress);
+        waitForAsyncProgress(pTask->progress, progressInt); /* Any errors will be thrown */
+
+        /* Again lock the appliance for the next steps */
+        appLock.lock();
+
+        vrc = RTPathExists(strTmpOvf.c_str()); /* Paranoid check */
+        if(RT_FAILURE(vrc))
+            throw setError(VBOX_E_FILE_ERROR,
+                           tr("Cannot find source file '%s'"), strTmpOvf.c_str());
+        /* Add the OVF file */
+        filesList.push_back(pair<Utf8Str, ULONG>(strTmpOvf, m->ulWeightPerOperation)); /* Use 1% of the total for the OVF file upload */
+
+        /* Now add every disks of every virtual system */
+        list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
+        for (it = m->virtualSystemDescriptions.begin();
+             it != m->virtualSystemDescriptions.end();
+             ++it)
+        {
+            ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
+            std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
+            std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
+            for (itH = avsdeHDs.begin();
+                 itH != avsdeHDs.end();
+                 ++itH)
+            {
+                const Utf8Str &strTargetFileNameOnly = (*itH)->strOvf;
+                /* Target path needs to be composed from where the output OVF is */
+                Utf8Str strTargetFilePath = stripFilename(m->strPath);
+                strTargetFilePath.append("/");
+                strTargetFilePath.append(strTargetFileNameOnly);
+                vrc = RTPathExists(strTargetFilePath.c_str()); /* Paranoid check */
+                if(RT_FAILURE(vrc))
+                    throw setError(VBOX_E_FILE_ERROR,
+                                   tr("Cannot find source file '%s'"), strTargetFilePath.c_str());
+                filesList.push_back(pair<Utf8Str, ULONG>(strTargetFilePath, (*itH)->ulSizeMB));
+            }
+        }
+        /* Next we have to upload the OVF & all disk images */
+        vrc = RTS3Create(&hS3, pTask->username.c_str(), pTask->password.c_str(), pTask->hostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);
+        if(RT_FAILURE(vrc))
+            throw setError(VBOX_E_IPRT_ERROR,
+                           tr("Cannot create S3 service handler"));
+        RTS3SetProgressCallback(hS3, pTask->uploadProgress, &pTask);
+
+        /* Upload all files */
+        for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
+        {
+            const pair<Utf8Str, ULONG> &s = (*it1);
+            char *pszFilename = RTPathFilename(s.first.c_str());
+            /* Advance to the next operation */
+            if (!pTask->progress.isNull())
+                pTask->progress->setNextOperation(BstrFmt(tr("Uploading file '%s'"), pszFilename), s.second);
+            vrc = RTS3PutKey(hS3, bucket.c_str(), pszFilename, s.first.c_str());
+            if (RT_FAILURE(vrc))
+            {
+                if(vrc == VERR_S3_CANCELED)
+                    break;
+                else if(vrc == VERR_S3_ACCESS_DENIED)
+                    throw setError(E_ACCESSDENIED,
+                                   tr("Cannot upload file '%s' to S3 storage server (Access denied)"), pszFilename);
+                else if(vrc == VERR_S3_NOT_FOUND)
+                    throw setError(VBOX_E_FILE_ERROR,
+                                   tr("Cannot upload file '%s' to S3 storage server (File not found)"), pszFilename);
+                else
+                    throw setError(VBOX_E_IPRT_ERROR,
+                                   tr("Cannot upload file '%s' to S3 storage server (%Rrc)"), pszFilename, vrc);
+            }
+        }
+
+    }
+    catch(HRESULT aRC)
+    {
+        rc = aRC;
+    }
+    /* Cleanup */
+    if (hS3)
+        RTS3Destroy(hS3);
+    /* Delete all files which where temporary created */
+    for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
+    {
+        const pair<Utf8Str, ULONG> &s = (*it1);
+        vrc = RTFileDelete(s.first.c_str());
+        if(RT_FAILURE(vrc))
+            rc = setError(VBOX_E_FILE_ERROR,
+                          tr("Cannot delete file '%s' (%Rrc)"), s.first.c_str(), vrc);
+    }
+    /* Delete the temporary directory */
+    if (RTPathExists(pszTmpDir))
+    {
+        vrc = RTDirRemove(pszTmpDir);
+        if(RT_FAILURE(vrc))
+            rc = setError(VBOX_E_FILE_ERROR,
+                          tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
+    }
+    if (pszTmpDir)
+        RTStrFree(pszTmpDir);
+
+    pTask->rc = rc;
+
+    if (!pTask->progress.isNull())
+        pTask->progress->notifyComplete(rc);
 
     LogFlowFunc(("rc=%Rhrc\n", rc));
@@ -3542,4 +3889,63 @@
                          bstrDescription, // CBSTR bstrFirstOperationDescription,
                          m->ulWeightPerOperation); // ULONG ulFirstOperationWeight,
+    return rc;
+}
+
+HRESULT Appliance::setUpProgressUpload(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription)
+{
+    HRESULT rc;
+
+    /* Create the progress object */
+    pProgress.createObject();
+
+    // weigh the disk images according to their sizes
+    uint32_t ulTotalMB = 0;
+    uint32_t cDisks = 0;
+    list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
+    for (it = m->virtualSystemDescriptions.begin();
+         it != m->virtualSystemDescriptions.end();
+         ++it)
+    {
+        ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
+        /* One for every hard disk of the Virtual System */
+        std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
+        std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
+        for (itH = avsdeHDs.begin();
+             itH != avsdeHDs.end();
+             ++itH)
+        {
+            const VirtualSystemDescriptionEntry *pHD = *itH;
+            ulTotalMB += pHD->ulSizeMB;
+            ++cDisks;
+        }
+    }
+
+    ULONG cOperations = 1 + 1 + cDisks;     // one op per disk plus 1 for the OVF & 1 plus to the temporary creation */
+
+    ULONG ulTotalOperationsWeight;
+    if (ulTotalMB)
+    {
+        m->ulWeightPerOperation = (ULONG)((double)ulTotalMB * 1  / 100);    // use 1% of the progress for OVF file upload (we didn't know the size at this point)
+        ulTotalOperationsWeight = ulTotalMB + m->ulWeightPerOperation;
+    }
+    else
+    {
+        // no disks to export:
+        ulTotalOperationsWeight = 1;
+        m->ulWeightPerOperation = 1;
+    }
+    ULONG ulOVFCreationWeight = ((double)ulTotalOperationsWeight * 50.0 / 100.0); /* Use 50% for the creation of the OVF & the disks */
+    ulTotalOperationsWeight += ulOVFCreationWeight;
+
+    Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightPerOperation = %d\n",
+         ulTotalMB, cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightPerOperation));
+
+    rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
+                         bstrDescription,
+                         TRUE /* aCancelable */,
+                         cOperations, // ULONG cOperations,
+                         ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight,
+                         bstrDescription, // CBSTR bstrFirstOperationDescription,
+                         ulOVFCreationWeight); // ULONG ulFirstOperationWeight,
     return rc;
 }
Index: /trunk/src/VBox/Main/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Main/Makefile.kmk	(revision 20080)
+++ /trunk/src/VBox/Main/Makefile.kmk	(revision 20081)
@@ -285,4 +285,5 @@
 	VirtualBoxImplExtra.cpp \
 	ApplianceImpl.cpp \
+	VFSExplorerImpl.cpp \
 	MachineImpl.cpp \
 	SnapshotImpl.cpp \
Index: /trunk/src/VBox/Main/VFSExplorerImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/VFSExplorerImpl.cpp	(revision 20081)
+++ /trunk/src/VBox/Main/VFSExplorerImpl.cpp	(revision 20081)
@@ -0,0 +1,674 @@
+/* $Id$ */
+/** @file
+ *
+ * IVFSExplorer COM class implementations.
+ */
+
+/*
+ * Copyright (C) 2009 Sun Microsystems, Inc.
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
+ * Clara, CA 95054 USA or visit http://www.sun.com if you need
+ * additional information or have any questions.
+ */
+
+#include <iprt/dir.h>
+#include <iprt/path.h>
+#include <iprt/file.h>
+#include <iprt/s3.h>
+
+#include <VBox/param.h>
+#include <VBox/version.h>
+
+#include "VFSExplorerImpl.h"
+#include "VirtualBoxImpl.h"
+#include "ProgressImpl.h"
+
+#include "Logging.h"
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// VFSExplorer definitions
+//
+////////////////////////////////////////////////////////////////////////////////
+
+/* opaque private instance data of VFSExplorer class */
+struct VFSExplorer::Data
+{
+    struct DirEntry
+    {
+        DirEntry(Utf8Str aName, VFSFileType_T aType)
+          : name(aName)
+          , type(aType) {}
+
+        Utf8Str name;
+        VFSFileType_T type;
+    };
+
+    VFSType_T storageType;
+    Utf8Str strUsername;
+    Utf8Str strPassword;
+    Utf8Str strHostname;
+    Utf8Str strPath;
+    Utf8Str strBucket;
+
+    std::list<DirEntry> entryList;
+};
+
+DEFINE_EMPTY_CTOR_DTOR(VFSExplorer)
+
+/**
+ * VFSExplorer COM initializer.
+ * @param
+ * @return
+ */
+HRESULT VFSExplorer::init(VFSType_T aType, Utf8Str aFilePath, Utf8Str aHostname, Utf8Str aUsername, Utf8Str aPassword, VirtualBox *aVirtualBox)
+{
+    /* Enclose the state transition NotReady->InInit->Ready */
+    AutoInitSpan autoInitSpan(this);
+    AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+    /* Weak reference to a VirtualBox object */
+    unconst(mVirtualBox) = aVirtualBox;
+
+    /* initialize data */
+    m = new Data;
+
+    m->storageType = aType;
+    m->strPath = aFilePath;
+    m->strHostname = aHostname;
+    m->strUsername = aUsername;
+    m->strPassword = aPassword;
+
+    if (m->storageType == VFSType_S3)
+    {
+        size_t bpos = aFilePath.find("/", 1);
+        if (bpos != Utf8Str::npos)
+        {
+            m->strBucket = aFilePath.substr(1, bpos - 1); /* The bucket without any slashes */
+            aFilePath = aFilePath.substr(bpos); /* The rest of the file path */
+        }
+    }
+
+    /* Confirm a successful initialization */
+    autoInitSpan.setSucceeded();
+
+    return S_OK;
+}
+
+/**
+ * VFSExplorer COM uninitializer.
+ * @return
+ */
+void VFSExplorer::uninit()
+{
+    delete m;
+    m = NULL;
+}
+
+/**
+ * Public method implementation.
+ * @param
+ * @return
+ */
+STDMETHODIMP VFSExplorer::COMGETTER(Path)(BSTR *aPath)
+{
+    if (!aPath)
+        return E_POINTER;
+
+    AutoCaller autoCaller(this);
+    CheckComRCReturnRC(autoCaller.rc());
+
+    AutoReadLock alock(this);
+
+    Bstr bstrPath(m->strPath);
+    bstrPath.cloneTo(aPath);
+
+    return S_OK;
+}
+
+STDMETHODIMP VFSExplorer::COMGETTER(Type)(VFSType_T *aType)
+{
+    if (!aType)
+        return E_POINTER;
+
+    AutoCaller autoCaller(this);
+    CheckComRCReturnRC(autoCaller.rc());
+
+    AutoReadLock alock(this);
+
+    *aType = m->storageType;
+
+    return S_OK;
+}
+
+struct VFSExplorer::TaskVFSExplorer
+{
+    enum TaskType
+    {
+        Update,
+        Delete
+    };
+
+    TaskVFSExplorer(TaskType aTaskType, VFSExplorer *aThat, Progress *aProgress)
+        : taskType(aTaskType),
+          pVFSExplorer(aThat),
+          progress(aProgress),
+          rc(S_OK)
+    {}
+    ~TaskVFSExplorer() {}
+
+    int startThread();
+    static int taskThread(RTTHREAD aThread, void *pvUser);
+    static int uploadProgress(unsigned uPercent, void *pvUser);
+
+    TaskType taskType;
+    VFSExplorer *pVFSExplorer;
+    ComObjPtr<Progress> progress;
+    HRESULT rc;
+
+    /* task data */
+    std::list<Utf8Str> filenames;
+};
+
+int VFSExplorer::TaskVFSExplorer::startThread()
+{
+    int vrc = RTThreadCreate(NULL, VFSExplorer::TaskVFSExplorer::taskThread, this,
+                             0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
+                             "Explorer::Task");
+
+    ComAssertMsgRCRet(vrc,
+                      ("Could not create taskThreadVFS (%Rrc)\n", vrc), E_FAIL);
+
+    return vrc;
+}
+
+/* static */
+DECLCALLBACK(int) VFSExplorer::TaskVFSExplorer::taskThread(RTTHREAD /* aThread */, void *pvUser)
+{
+    std::auto_ptr<TaskVFSExplorer> task(static_cast<TaskVFSExplorer*>(pvUser));
+    AssertReturn(task.get(), VERR_GENERAL_FAILURE);
+
+    VFSExplorer *pVFSExplorer = task->pVFSExplorer;
+
+    LogFlowFuncEnter();
+    LogFlowFunc(("VFSExplorer %p\n", pVFSExplorer));
+
+    HRESULT rc = S_OK;
+
+    switch(task->taskType)
+    {
+        case TaskVFSExplorer::Update:
+        {
+            if (pVFSExplorer->m->storageType == VFSType_File)
+                rc = pVFSExplorer->updateFS(task.get());
+            else if (pVFSExplorer->m->storageType == VFSType_S3)
+                rc = pVFSExplorer->updateS3(task.get());
+            break;
+        }
+        case TaskVFSExplorer::Delete:
+        {
+            if (pVFSExplorer->m->storageType == VFSType_File)
+                rc = pVFSExplorer->deleteFS(task.get());
+            else if (pVFSExplorer->m->storageType == VFSType_S3)
+                rc = pVFSExplorer->deleteS3(task.get());
+            break;
+        }
+    }
+
+    LogFlowFunc(("rc=%Rhrc\n", rc));
+    LogFlowFuncLeave();
+
+    return VINF_SUCCESS;
+}
+
+/* static */
+int VFSExplorer::TaskVFSExplorer::uploadProgress(unsigned uPercent, void *pvUser)
+{
+    VFSExplorer::TaskVFSExplorer* pTask = *(VFSExplorer::TaskVFSExplorer**)pvUser;
+
+    if (pTask &&
+        !pTask->progress.isNull())
+    {
+        BOOL fCanceled;
+        pTask->progress->COMGETTER(Canceled)(&fCanceled);
+        if (fCanceled)
+            return -1;
+        pTask->progress->setCurrentOperationProgress(uPercent);
+    }
+    return VINF_SUCCESS;
+}
+
+VFSFileType_T VFSExplorer::RTToVFSFileType(int aType) const
+{
+    VFSFileType_T t;
+    switch(aType)
+    {
+        case RTDIRENTRYTYPE_UNKNOWN: t = VFSFileType_Unknown; break;
+        case RTDIRENTRYTYPE_FIFO: t = VFSFileType_Fifo; break;
+        case RTDIRENTRYTYPE_DEV_CHAR: t = VFSFileType_DevChar; break;
+        case RTDIRENTRYTYPE_DIRECTORY: t = VFSFileType_Directory; break;
+        case RTDIRENTRYTYPE_DEV_BLOCK: t = VFSFileType_DevBlock; break;
+        case RTDIRENTRYTYPE_FILE: t = VFSFileType_File; break;
+        case RTDIRENTRYTYPE_SYMLINK: t = VFSFileType_SymLink; break;
+        case RTDIRENTRYTYPE_SOCKET: t = VFSFileType_Socket; break;
+        case RTDIRENTRYTYPE_WHITEOUT: t = VFSFileType_WhiteOut; break;
+    }
+    return t;
+}
+
+HRESULT VFSExplorer::updateFS(TaskVFSExplorer *aTask)
+{
+    LogFlowFuncEnter();
+
+    AutoCaller autoCaller(this);
+    CheckComRCReturnRC(autoCaller.rc());
+
+    AutoWriteLock appLock(this);
+
+    HRESULT rc = S_OK;
+
+    std::list<VFSExplorer::Data::DirEntry> fileList;
+    char *pszPath = NULL;
+    PRTDIR pDir = NULL;
+    try
+    {
+        pszPath = RTStrDup(m->strPath.c_str());
+        RTPathStripFilename(pszPath);
+        int vrc = RTDirOpen(&pDir, pszPath);
+        if (RT_FAILURE(vrc))
+            throw setError(VBOX_E_FILE_ERROR, tr ("Can't open directory '%s' (%Rrc)"), pszPath, vrc);
+
+        if(aTask->progress)
+            aTask->progress->setCurrentOperationProgress(33);
+        RTDIRENTRY entry;
+        while (RT_SUCCESS(vrc))
+        {
+            vrc = RTDirRead(pDir, &entry, NULL);
+            if (RT_SUCCESS(vrc))
+            {
+                Utf8Str name(entry.szName);
+                if (name != "." &&
+                    name != "..")
+                    fileList.push_back(VFSExplorer::Data::DirEntry(name, RTToVFSFileType(entry.enmType)));
+            }
+        }
+        if(aTask->progress)
+            aTask->progress->setCurrentOperationProgress(66);
+    }
+    catch(HRESULT aRC)
+    {
+        rc = aRC;
+    }
+
+    /* Clean up */
+    if (pszPath)
+        RTStrFree(pszPath);
+    if (pDir)
+        RTDirClose(pDir);
+
+    if(aTask->progress)
+        aTask->progress->setCurrentOperationProgress(99);
+
+    /* Assign the result on success (this clears the old list) */
+    if (rc == S_OK)
+        m->entryList.assign(fileList.begin(), fileList.end());
+
+    aTask->rc = rc;
+
+    if (!aTask->progress.isNull())
+        aTask->progress->notifyComplete(rc);
+
+    LogFlowFunc(("rc=%Rhrc\n", rc));
+    LogFlowFuncLeave();
+
+    return VINF_SUCCESS;
+}
+
+HRESULT VFSExplorer::deleteFS(TaskVFSExplorer *aTask)
+{
+    LogFlowFuncEnter();
+
+    AutoCaller autoCaller(this);
+    CheckComRCReturnRC(autoCaller.rc());
+
+    AutoWriteLock appLock(this);
+
+    HRESULT rc = S_OK;
+
+    float fPercentStep = 100.0 / aTask->filenames.size();
+    try
+    {
+        char szPath[RTPATH_MAX];
+        std::list<Utf8Str>::const_iterator it;
+        size_t i = 0;
+        for (it = aTask->filenames.begin();
+             it != aTask->filenames.end();
+             ++it, ++i)
+        {
+            memcpy(szPath, m->strPath.c_str(), strlen(m->strPath.c_str()) + 1);
+            RTPathStripFilename(szPath);
+            RTPathAppend(szPath, sizeof(szPath), (*it).c_str());
+            int vrc = RTFileDelete(szPath);
+            if (RT_FAILURE(vrc))
+                throw setError(VBOX_E_FILE_ERROR, tr ("Can't delete file '%s' (%Rrc)"), szPath, vrc);
+            if(aTask->progress)
+                aTask->progress->setCurrentOperationProgress(fPercentStep * i);
+        }
+    }
+    catch(HRESULT aRC)
+    {
+        rc = aRC;
+    }
+
+    aTask->rc = rc;
+
+    if (!aTask->progress.isNull())
+        aTask->progress->notifyComplete(rc);
+
+    LogFlowFunc(("rc=%Rhrc\n", rc));
+    LogFlowFuncLeave();
+
+    return VINF_SUCCESS;
+}
+
+HRESULT VFSExplorer::updateS3(TaskVFSExplorer *aTask)
+{
+    LogFlowFuncEnter();
+
+    AutoCaller autoCaller(this);
+    CheckComRCReturnRC(autoCaller.rc());
+
+    AutoWriteLock appLock(this);
+
+    HRESULT rc = S_OK;
+
+    RTS3 hS3 = NULL;
+    std::list<VFSExplorer::Data::DirEntry> fileList;
+    try
+    {
+        int vrc = RTS3Create(&hS3, m->strUsername.c_str(), m->strPassword.c_str(), m->strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);
+        if (RT_FAILURE(vrc))
+            throw setError(E_FAIL, tr ("Can't open S3 storage service (%Rrc)"), vrc);
+
+        RTS3SetProgressCallback(hS3, VFSExplorer::TaskVFSExplorer::uploadProgress, &aTask);
+        /* Do we need the list of buckets or keys? */
+        if (m->strBucket.isEmpty())
+        {
+            PCRTS3BUCKETENTRY pBuckets = NULL;
+            vrc = RTS3GetBuckets(hS3, &pBuckets);
+            if (RT_FAILURE(vrc))
+                throw setError(E_FAIL, tr ("Can't get buckets (%Rrc)"), vrc);
+
+            PCRTS3BUCKETENTRY pTmpBuckets = pBuckets;
+            while (pBuckets)
+            {
+                fileList.push_back(VFSExplorer::Data::DirEntry(pBuckets->pszName, VFSFileType_Directory));
+                pBuckets = pBuckets->pNext;
+            }
+            RTS3BucketsDestroy(pTmpBuckets);
+        }
+        else
+        {
+            PCRTS3KEYENTRY pKeys = NULL;
+            vrc = RTS3GetBucketKeys(hS3, m->strBucket, &pKeys);
+            if (RT_FAILURE(vrc))
+                throw setError(E_FAIL, tr ("Can't get keys for bucket (%Rrc)"), vrc);
+
+            PCRTS3KEYENTRY pTmpKeys = pKeys;
+            while (pKeys)
+            {
+                Utf8Str name(pKeys->pszName);
+                fileList.push_back(VFSExplorer::Data::DirEntry(pKeys->pszName, VFSFileType_File));
+                pKeys = pKeys->pNext;
+            }
+            RTS3KeysDestroy(pTmpKeys);
+        }
+    }
+    catch(HRESULT aRC)
+    {
+        rc = aRC;
+    }
+
+    if (hS3 != NULL)
+        RTS3Destroy(hS3);
+
+    /* Assign the result on success (this clears the old list) */
+    if (rc == S_OK)
+        m->entryList.assign(fileList.begin(), fileList.end());
+
+    aTask->rc = rc;
+
+    if (!aTask->progress.isNull())
+        aTask->progress->notifyComplete(rc);
+
+    LogFlowFunc(("rc=%Rhrc\n", rc));
+    LogFlowFuncLeave();
+
+    return VINF_SUCCESS;
+}
+
+HRESULT VFSExplorer::deleteS3(TaskVFSExplorer *aTask)
+{
+    LogFlowFuncEnter();
+
+    AutoCaller autoCaller(this);
+    CheckComRCReturnRC(autoCaller.rc());
+
+    AutoWriteLock appLock(this);
+
+    HRESULT rc = S_OK;
+
+    RTS3 hS3 = NULL;
+    float fPercentStep = 100.0 / aTask->filenames.size();
+    try
+    {
+        int vrc = RTS3Create(&hS3, m->strUsername.c_str(), m->strPassword.c_str(), m->strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);
+        if (RT_FAILURE(vrc))
+            throw setError(E_FAIL, tr ("Can't open S3 storage service (%Rrc)"), vrc);
+
+        RTS3SetProgressCallback(hS3, VFSExplorer::TaskVFSExplorer::uploadProgress, &aTask);
+
+        std::list<Utf8Str>::const_iterator it;
+        size_t i = 0;
+        for (it = aTask->filenames.begin();
+             it != aTask->filenames.end();
+             ++it, ++i)
+        {
+            vrc = RTS3DeleteKey(hS3, m->strBucket.c_str(), (*it).c_str());
+            if (RT_FAILURE(vrc))
+                throw setError(VBOX_E_FILE_ERROR, tr ("Can't delete file '%s' (%Rrc)"), (*it).c_str(), vrc);
+            if(aTask->progress)
+                aTask->progress->setCurrentOperationProgress(fPercentStep * i);
+        }
+    }
+    catch(HRESULT aRC)
+    {
+        rc = aRC;
+    }
+
+    aTask->rc = rc;
+
+    if (hS3 != NULL)
+        RTS3Destroy(hS3);
+
+    if (!aTask->progress.isNull())
+        aTask->progress->notifyComplete(rc);
+
+    LogFlowFunc(("rc=%Rhrc\n", rc));
+    LogFlowFuncLeave();
+
+    return VINF_SUCCESS;
+}
+
+STDMETHODIMP VFSExplorer::Update(IProgress **aProgress)
+{
+    CheckComArgOutPointerValid(aProgress);
+
+    AutoCaller autoCaller(this);
+    CheckComRCReturnRC(autoCaller.rc());
+
+    AutoReadLock(this);
+
+    HRESULT rc = S_OK;
+
+    ComObjPtr<Progress> progress;
+    try
+    {
+        Bstr progressDesc = BstrFmt(tr("Update directory info for '%s'"),
+                                    m->strPath.raw());
+        /* Create the progress object */
+        progress.createObject();
+
+        rc = progress->init(mVirtualBox, static_cast<IVFSExplorer*>(this),
+                            progressDesc,
+                            TRUE /* aCancelable */);
+        if (FAILED(rc)) throw rc;
+
+        /* Initialize our worker task */
+        std::auto_ptr<TaskVFSExplorer> task(new TaskVFSExplorer(TaskVFSExplorer::Update, this, progress));
+
+        rc = task->startThread();
+        if (FAILED(rc)) throw rc;
+
+        /* Don't destruct on success */
+        task.release();
+    }
+    catch (HRESULT aRC)
+    {
+        rc = aRC;
+    }
+
+    if (SUCCEEDED(rc))
+        /* Return progress to the caller */
+        progress.queryInterfaceTo(aProgress);
+
+    return rc;
+}
+
+STDMETHODIMP VFSExplorer::EntryList(ComSafeArrayOut(BSTR, aNames), ComSafeArrayOut(VFSFileType_T, aTypes))
+{
+    if (ComSafeArrayOutIsNull(aNames) ||
+        ComSafeArrayOutIsNull(aTypes))
+        return E_POINTER;
+
+    AutoCaller autoCaller(this);
+    CheckComRCReturnRC(autoCaller.rc());
+
+    AutoReadLock alock(this);
+
+    com::SafeArray<BSTR> sfaNames((ULONG)m->entryList.size());
+    com::SafeArray<ULONG> sfaTypes((VFSFileType_T)m->entryList.size());
+
+    std::list<VFSExplorer::Data::DirEntry>::const_iterator it;
+    size_t i = 0;
+    for (it = m->entryList.begin();
+         it != m->entryList.end();
+         ++it, ++i)
+    {
+        const VFSExplorer::Data::DirEntry &entry = (*it);
+        Bstr bstr(entry.name);
+        bstr.cloneTo(&sfaNames[i]);
+        sfaTypes[i] = entry.type;
+    }
+
+    sfaNames.detachTo(ComSafeArrayOutArg(aNames));
+    sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));
+
+    return S_OK;
+}
+
+STDMETHODIMP VFSExplorer::Exists(ComSafeArrayIn(IN_BSTR, aNames), ComSafeArrayOut(BSTR, aExists))
+{
+    CheckComArgSafeArrayNotNull(aNames);
+    CheckComArgSafeArrayNotNull(aExists);
+
+    AutoCaller autoCaller(this);
+    CheckComRCReturnRC(autoCaller.rc());
+
+    AutoReadLock alock(this);
+
+    com::SafeArray<IN_BSTR> sfaNames(ComSafeArrayInArg(aNames));
+    std::list<BSTR> listExists;
+
+    std::list<VFSExplorer::Data::DirEntry>::const_iterator it;
+    for (it = m->entryList.begin();
+         it != m->entryList.end();
+         ++it)
+    {
+        const VFSExplorer::Data::DirEntry &entry = (*it);
+        for (size_t a=0; a < sfaNames.size(); ++a)
+        {
+            if (entry.name == RTPathFilename(Utf8Str(sfaNames[a])))
+            {
+                BSTR name;
+                Bstr(sfaNames[a]).cloneTo(&name);
+                listExists.push_back(name);
+            }
+        }
+    }
+
+    com::SafeArray<BSTR> sfaExists(listExists);
+    sfaExists.detachTo(ComSafeArrayOutArg(aExists));
+
+    return S_OK;
+}
+
+STDMETHODIMP VFSExplorer::Remove(ComSafeArrayIn(IN_BSTR, aNames), IProgress **aProgress)
+{
+    CheckComArgSafeArrayNotNull(aNames);
+    CheckComArgOutPointerValid(aProgress);
+
+    AutoCaller autoCaller(this);
+    CheckComRCReturnRC(autoCaller.rc());
+
+    AutoReadLock(this);
+
+    HRESULT rc = S_OK;
+
+    com::SafeArray<IN_BSTR> sfaNames(ComSafeArrayInArg(aNames));
+
+    ComObjPtr<Progress> progress;
+    try
+    {
+        /* Create the progress object */
+        progress.createObject();
+
+        rc = progress->init(mVirtualBox, static_cast<IVFSExplorer*>(this),
+                            Bstr(tr("Delete files")),
+                            TRUE /* aCancelable */);
+        if (FAILED(rc)) throw rc;
+
+        /* Initialize our worker task */
+        std::auto_ptr<TaskVFSExplorer> task(new TaskVFSExplorer(TaskVFSExplorer::Delete, this, progress));
+
+        /* Add all filenames to delete as task data */
+        for (size_t a=0; a < sfaNames.size(); ++a)
+            task->filenames.push_back(Utf8Str(sfaNames[a]));
+
+        rc = task->startThread();
+        if (FAILED(rc)) throw rc;
+
+        /* Don't destruct on success */
+        task.release();
+    }
+    catch (HRESULT aRC)
+    {
+        rc = aRC;
+    }
+
+    if (SUCCEEDED(rc))
+        /* Return progress to the caller */
+        progress.queryInterfaceTo(aProgress);
+
+    return rc;
+}
+
Index: /trunk/src/VBox/Main/idl/VirtualBox.xidl
===================================================================
--- /trunk/src/VBox/Main/idl/VirtualBox.xidl	(revision 20080)
+++ /trunk/src/VBox/Main/idl/VirtualBox.xidl	(revision 20081)
@@ -2890,4 +2890,113 @@
     </method>
 
+  </interface>
+
+  <!--
+  // IVFSExplorer
+  /////////////////////////////////////////////////////////////////////////
+  -->
+
+  <enum
+    name="VFSType"
+    uuid="813999ba-b949-48a8-9230-aadc6285e2f2"
+  >
+    <desc>
+      Supported virtual file systems of VFSExplorer.
+    </desc>
+
+    <const name="File" value="1" />
+    <const name="Cloud" value="2" />
+    <const name="S3" value="3" />
+    <const name="WebDav" value="4" />
+  </enum>
+
+  <enum
+    name="VFSFileType"
+    uuid="714333cd-44e2-415f-a245-d378fa9b1242"
+  >
+    <desc>
+      File types known by VFSExplorer.
+    </desc>
+
+    <const name="Unknown" value="1" />
+    <const name="Fifo" value="2" />
+    <const name="DevChar" value="3" />
+    <const name="Directory" value="4" />
+    <const name="DevBlock" value="5" />
+    <const name="File" value="6" />
+    <const name="SymLink" value="7" />
+    <const name="Socket" value="8" />
+    <const name="WhiteOut" value="9" />
+  </enum>
+
+  <interface
+     name="IVFSExplorer" extends="$unknown"
+     uuid="fd7da337-80ef-4a5c-9122-918435e33003"
+     wsmap="managed"
+     >
+    <desc>
+      The VFSExplorer interface unify the access to different file system
+      types. This includes local file systems as well remote file systems like
+      the S3 one. For a list of supported types see <link to="VFSType" />.
+    </desc>
+
+    <attribute name="path" type="wstring" readonly="yes">
+      <desc>Return the current path in the virtual file system.</desc>
+    </attribute>
+
+    <attribute name="type" type="VFSType" readonly="yes">
+      <desc>Return the file system type which is currently in use.</desc>
+    </attribute>
+
+    <method name="update">
+      <desc>This method updates the internal list of files/directories from the
+      current directory level. Use <link to="entryList" /> to get the full list
+      after a call to this method.</desc>
+
+      <param name="aProgress" type="IProgress" dir="return">
+        <desc>Progress object to track the operation completion.</desc>
+      </param>
+    </method>
+
+    <method name="entryList">
+      <desc>Fetch the list of files/directories after a call to <link
+      to="update" />. The user is responcible for keeping this internal list up
+      do date.</desc>
+
+      <param name="aNames" type="wstring" safearray="yes" dir="out">
+        <desc>The list of names for the entries.</desc>
+      </param>
+
+      <param name="aTypes" type="unsigned long" safearray="yes" dir="out">
+        <desc>The list of types for the entries.</desc>
+      </param>
+    </method>
+
+    <method name="exists">
+        <desc>Check if the given file list exists in the current directory
+        level.</desc>
+
+      <param name="aNames" type="wstring" safearray="yes" dir="in">
+        <desc>The names to check.</desc>
+      </param>
+
+      <param name="aExists" type="wstring" safearray="yes" dir="return">
+        <desc>The names which exists.</desc>
+      </param>
+    </method>
+
+    <method name="remove">
+        <desc>Remove the given file names from the current directory
+        level.</desc>
+
+      <param name="aNames" type="wstring" safearray="yes" dir="in">
+        <desc>The names to remove.</desc>
+      </param>
+
+      <param name="aProgress" type="IProgress" dir="return">
+        <desc>Progress object to track the operation completion.</desc>
+      </param>
+    </method>
+    
   </interface>
 
@@ -3043,5 +3152,5 @@
   <interface
      name="IAppliance" extends="$unknown"
-     uuid="30bfa6b8-9eda-4b0a-b218-a86813248ccd"
+     uuid="07495095-d16c-4911-8964-5914341ced5d"
      wsmap="managed"
      >
@@ -3229,4 +3338,16 @@
     </method>
 
+    <method name="createVFSExplorer">
+      <desc>Returns a <link to="IVFSExplorer" /> object for the given URI.</desc>
+
+      <param name="aUri" type="wstring" dir="in">
+        <desc>The URI describing the file system to use.</desc>
+      </param>
+
+      <param name="aExplorer" type="IVFSExplorer" dir="return">
+        <desc></desc>
+      </param>
+    </method>
+
     <method name="write">
       <desc>
@@ -3253,5 +3374,5 @@
       </param>
       <param name="aProgress" type="IProgress" dir="return">
-          <desc></desc>
+        <desc>Progress object to track the operation completion.</desc>
       </param>
     </method>
Index: /trunk/src/VBox/Main/include/ApplianceImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/ApplianceImpl.h	(revision 20080)
+++ /trunk/src/VBox/Main/include/ApplianceImpl.h	(revision 20081)
@@ -75,8 +75,12 @@
 
     /* IAppliance methods */
+    /* Import methods */
     STDMETHOD(Read)(IN_BSTR path);
     STDMETHOD(Interpret)(void);
     STDMETHOD(ImportMachines)(IProgress **aProgress);
+    /* Export methods */
+    STDMETHOD(CreateVFSExplorer)(IN_BSTR aURI, IVFSExplorer **aExplorer);
     STDMETHOD(Write)(IN_BSTR format, IN_BSTR path, IProgress **aProgress);
+
     STDMETHOD(GetWarnings)(ComSafeArrayOut(BSTR, aWarnings));
 
@@ -99,12 +103,20 @@
     HRESULT searchUniqueDiskImageFilePath(Utf8Str& aName) const;
     HRESULT setUpProgress(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription);
+    HRESULT setUpProgressUpload(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription);
     void waitForAsyncProgress(ComObjPtr<Progress> &pProgressThis, ComPtr<IProgress> &pProgressAsync);
     void addWarning(const char* aWarning, ...);
 
+    void parseURI(Utf8Str strUri, const Utf8Str &strProtocol, Utf8Str &strFilepath, Utf8Str &strHostname, Utf8Str &strUsername, Utf8Str &strPassword);
+    HRESULT writeImpl(int aFormat, Utf8Str aPath, ComObjPtr<Progress> &aProgress);
+
+    char * mkTemp(char *pszTemplate);
     struct TaskImportMachines;  /* Worker thread for import */
     static DECLCALLBACK(int) taskThreadImportMachines(RTTHREAD thread, void *pvUser);
 
-    struct TaskWriteOVF;       /* Worker thread for export */
-    static DECLCALLBACK(int) taskThreadWriteOVF(RTTHREAD thread, void *pvUser);
+    struct TaskWriteOVF;        /* Worker threads for export */
+    static DECLCALLBACK(int) taskThreadWriteOVF(RTTHREAD aThread, void *pvUser);
+
+    int writeFS(TaskWriteOVF *pTask);
+    int writeS3(TaskWriteOVF *pTask);
 
     friend class Machine;
Index: /trunk/src/VBox/Main/include/VFSExplorerImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/VFSExplorerImpl.h	(revision 20081)
+++ /trunk/src/VBox/Main/include/VFSExplorerImpl.h	(revision 20081)
@@ -0,0 +1,102 @@
+/* $Id$ */
+
+/** @file
+ *
+ * VirtualBox COM class implementation
+ */
+
+/*
+ * Copyright (C) 2009 Sun Microsystems, Inc.
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
+ * Clara, CA 95054 USA or visit http://www.sun.com if you need
+ * additional information or have any questions.
+ */
+
+#ifndef ____H_VFSEXPLORERIMPL
+#define ____H_VFSEXPLORERIMPL
+
+#include "VirtualBoxBase.h"
+
+class VirtualBox;
+
+class ATL_NO_VTABLE VFSExplorer :
+    public VirtualBoxBaseNEXT,
+    public VirtualBoxSupportErrorInfoImpl <VFSExplorer, IVFSExplorer>,
+    public VirtualBoxSupportTranslation <VFSExplorer>,
+    VBOX_SCRIPTABLE_IMPL(IVFSExplorer)
+{
+    VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT (VFSExplorer)
+
+    DECLARE_NOT_AGGREGATABLE(VFSExplorer)
+
+    DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+    BEGIN_COM_MAP(VFSExplorer)
+        COM_INTERFACE_ENTRY(ISupportErrorInfo)
+        COM_INTERFACE_ENTRY(IVFSExplorer)
+        COM_INTERFACE_ENTRY(IDispatch)
+    END_COM_MAP()
+
+    NS_DECL_ISUPPORTS
+
+    DECLARE_EMPTY_CTOR_DTOR(VFSExplorer)
+
+    // public initializer/uninitializer for internal purposes only
+    HRESULT FinalConstruct() { return S_OK; }
+    void FinalRelease() { uninit(); }
+
+    HRESULT init(VFSType_T aType, Utf8Str aFilePath, Utf8Str aHostname, Utf8Str aUsername, Utf8Str aPassword, VirtualBox *aVirtualBox);
+    void uninit();
+
+    // for VirtualBoxSupportErrorInfoImpl
+    static const wchar_t *getComponentName() { return L"VFSExplorer"; }
+
+    /* IVFSExplorer properties */
+    STDMETHOD(COMGETTER(Path))(BSTR *aPath);
+    STDMETHOD(COMGETTER(Type))(VFSType_T *aType);
+
+    /* IVFSExplorer methods */
+    STDMETHOD(Update)(IProgress **aProgress);
+
+    STDMETHOD(EntryList)(ComSafeArrayOut(BSTR, aNames), ComSafeArrayOut(VFSFileType_T, aTypes));
+
+    STDMETHOD(Exists)(ComSafeArrayIn(IN_BSTR, aNames), ComSafeArrayOut(BSTR, aExists));
+
+    STDMETHOD(Remove)(ComSafeArrayIn(IN_BSTR, aNames), IProgress **aProgress);
+
+    /* todo: later
+    STDMETHOD(Cd)(IN_BSTR aDir, IProgress **aProgress);
+    STDMETHOD(CdUp)(IProgress **aProgress);
+
+    STDMETHOD(MkDir)(IN_BSTR aDir, IProgress **aProgress);
+    STDMETHOD(RmDir)(IN_BSTR aDir, IProgress **aProgress);
+    */
+
+private:
+    /* Private member vars */
+    const ComObjPtr<VirtualBox, ComWeakRef> mVirtualBox;
+
+    struct TaskVFSExplorer;  /* Worker thread helper */
+    struct Data;
+    Data *m;
+
+    /* Private member methods */
+    VFSFileType_T RTToVFSFileType(int aType) const;
+
+    HRESULT updateFS(TaskVFSExplorer *aTask);
+    HRESULT deleteFS(TaskVFSExplorer *aTask);
+    HRESULT updateS3(TaskVFSExplorer *aTask);
+    HRESULT deleteS3(TaskVFSExplorer *aTask);
+};
+
+#endif /* ____H_VFSEXPLORERIMPL */
+
Index: /trunk/src/VBox/Main/xpcom/server.cpp
===================================================================
--- /trunk/src/VBox/Main/xpcom/server.cpp	(revision 20080)
+++ /trunk/src/VBox/Main/xpcom/server.cpp	(revision 20081)
@@ -74,4 +74,5 @@
 #include <VirtualBoxImpl.h>
 #include <MachineImpl.h>
+#include <VFSExplorerImpl.h>
 #include <ApplianceImpl.h>
 #include <SnapshotImpl.h>
@@ -110,4 +111,7 @@
 NS_DECL_CLASSINFO(Machine)
 NS_IMPL_THREADSAFE_ISUPPORTS1_CI(Machine, IMachine)
+
+NS_DECL_CLASSINFO(VFSExplorer)
+NS_IMPL_THREADSAFE_ISUPPORTS1_CI(VFSExplorer, IVFSExplorer)
 
 NS_DECL_CLASSINFO(Appliance)
