Index: /trunk/include/iprt/s3.h
===================================================================
--- /trunk/include/iprt/s3.h	(revision 20043)
+++ /trunk/include/iprt/s3.h	(revision 20043)
@@ -0,0 +1,274 @@
+/* $Id$ */
+/** @file
+ * IPRT - Simple Storage Service (S3) Communication API.
+ */
+
+/*
+ * 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.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * 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 ___iprt_s3_h
+#define ___iprt_s3_h
+
+#include <iprt/types.h>
+
+__BEGIN_DECLS
+
+/** @group grp_rt_s3    RTS3 - Simple Storage Service (S3) Communication API
+ * @ingroup grp_rt
+ * @{
+ */
+
+/** @todo the following three definitions may move the iprt/types.h later. */
+/** RTS3 interface handle. */
+typedef R3PTRTYPE(struct RTS3INTERNAL *)    RTS3;
+/** Pointer to a RTS3 interface handle. */
+typedef RTS3                               *PRTS3;
+/** Nil RTS3 interface handle. */
+#define NIL_RTS3                            ((RTS3)0)
+
+
+/**
+ * S3 progress callback.
+ *
+ * @returns Reserved, must be 0.
+ *
+ * @param   uPercent    The process completion percentage.
+ * @param   pvUser      The user parameter given to RTS3SetProgressCallback.
+ */
+typedef DECLCALLBACK(int) FNRTS3PROGRESS(unsigned uPercent, void *pvUser);
+/** Pointer to a S3 progress callback. */
+typedef FNRTS3PROGRESS *PFNRTS3PROGRESS;
+
+
+/** Pointer to an S3 bucket entry. */
+typedef struct RTS3BUCKETENTRY *PRTS3BUCKETENTRY;
+/** Pointer to a const S3 bucket entry. */
+typedef struct RTS3BUCKETENTRY const *PCRTS3BUCKETENTRY;
+/**
+ * RTS3 bucket entry.
+ *
+ * Represent a bucket of the S3 storage server. Bucket entries are chained as a
+ * doubly linked list using the pPrev & pNext member.
+ *
+ * @todo    Consider making the entire list const unless there are plans for
+ *          more APIs using this structure which requires the caller to create
+ *          or modify it.
+ */
+typedef struct RTS3BUCKETENTRY
+{
+    /** The previous element. */
+    PRTS3BUCKETENTRY       pPrev;
+    /** The next element. */
+    PRTS3BUCKETENTRY       pNext;
+
+    /** The name of the bucket. */
+    char const             *pszName;
+    /** The creation date of the bucket as string. */
+    char const             *pszCreationDate;
+} RTS3BUCKETENTRY;
+
+
+/** Pointer to an S3 key entry. */
+typedef struct RTS3KEYENTRY *PRTS3KEYENTRY;
+/** Pointer to a const S3 key entry. */
+typedef struct RTS3KEYENTRY const *PCRTS3KEYENTRY;
+/**
+ * RTS3 key entry.
+ *
+ * Represent a key of the S3 storage server. Key entries are chained as a doubly
+ * linked list using the pPrev & pNext member.
+ *
+ * @todo    Consider making the entire list const unless there are plans for
+ *          more APIs using this structure which requires the caller to create
+ *          or modify it.
+ */
+typedef struct RTS3KEYENTRY
+{
+    /** The previous element. */
+    PRTS3KEYENTRY          pPrev;
+    /** The next element. */
+    PRTS3KEYENTRY          pNext;
+
+    /** The name of the key. */
+    char const             *pszName;
+    /** The date this key was last modified as string. */
+    char const             *pszLastModified;
+    /** The size of the file behind this key in bytes. */
+    uint64_t                cbFile;
+} RTS3KEYENTRY;
+
+
+/**
+ * Creates a RTS3 interface handle.
+ *
+ * @returns IPRT status code.
+ *
+ * @param   phS3           Where to store the RTS3 handle.
+ * @param   pszAccessKey   The access key for the S3 storage server.
+ * @param   pszSecretKey   The secret access key for the S3 storage server.
+ * @param   pszBaseUrl     The base URL of the S3 storage server.
+ * @param   pszUserAgent   An optional user agent string used in the HTTP
+ *                         communication.
+ */
+RTR3DECL(int) RTS3Create(PRTS3 phS3, const char *pszAccessKey, const char *pszSecretKey, const char *pszBaseUrl, const char *pszUserAgent);
+
+/**
+ * Destroys a RTS3 interface handle.
+ *
+ * @returns IPRT status code.
+ *
+ * @param   hS3            Handle to the RTS3 interface.
+ */
+RTR3DECL(void) RTS3Destroy(RTS3 hS3);
+
+/**
+ * Sets an optional progress callback.
+ *
+ * This callback function will be called when the completion percentage of an S3
+ * operation changes.
+ *
+ * @returns IPRT status code.
+ *
+ * @param   hS3             Handle to the RTS3 interface.
+ * @param   pfnProgressCB   The pointer to the progress function.
+ * @param   pvUser          The pvUser arg of FNRTS3PROGRESS.
+ */
+RTR3DECL(void) RTS3SetProgressCallback(RTS3 hS3, PFNRTS3PROGRESS pfnProgressCB, void *pvUser);
+
+/**
+ * Gets a list of all available buckets on the S3 storage server.
+ *
+ * You have to delete ppBuckets after usage with RTS3BucketsDestroy.
+ *
+ * @returns VBox status code.
+ *
+ * @param   hS3             Handle to the RTS3 interface.
+ * @param   ppBuckets       Where to store the pointer to the head of the
+ *                          returned bucket list. Consider the entire list
+ *                          read-only.
+ */
+RTR3DECL(int) RTS3GetBuckets(RTS3 hS3, PCRTS3BUCKETENTRY *ppBuckets);
+
+/**
+ * Destroys the bucket list returned by RTS3GetBuckets.
+ *
+ * @returns IPRT status code.
+ *
+ * @param   pBuckets        Pointer to the first bucket entry.
+ */
+RTR3DECL(int) RTS3BucketsDestroy(PCRTS3BUCKETENTRY pBuckets);
+
+/**
+ * Creates a new bucket on the S3 storage server.
+ *
+ * This name have to be unique over all accounts on the S3 storage server.
+ *
+ * @returns IPRT status code.
+ *
+ * @param   hS3             Handle to the RTS3 interface.
+ * @param   pszBucketName   Name of the new bucket.
+ */
+RTR3DECL(int) RTS3CreateBucket(RTS3 hS3, const char *pszBucketName);
+
+/**
+ * Deletes a bucket on the S3 storage server.
+ *
+ * The bucket must be empty.
+ *
+ * @returns IPRT status code.
+ *
+ * @param   hS3             Handle to the RTS3 interface.
+ * @param   pszBucketName   Name of the bucket to delete.
+ */
+RTR3DECL(int) RTS3DeleteBucket(RTS3 hS3, const char *pszBucketName);
+
+/**
+ * Gets a list of all available keys in a bucket on the S3 storage server.
+ *
+ * You have to delete ppKeys after usage with RTS3KeysDestroy.
+ *
+ * @returns IPRT status code.
+ *
+ * @param   hS3             Handle to the RTS3 interface.
+ * @param   pszBucketName   Name of the bucket to delete.
+ * @param   ppKeys          Where to store the pointer to the head of the
+ *                          returned key list. Consider the entire list
+ *                          read-only.
+ */
+RTR3DECL(int) RTS3GetBucketKeys(RTS3 hS3, const char *pszBucketName, PCRTS3KEYENTRY *ppKeys);
+
+/**
+ * Delete the key list returned by RTS3GetBucketKeys.
+ *
+ * @returns IPRT status code.
+ *
+ * @param   pKeys           Pointer to the first key entry.
+ */
+RTR3DECL(int) RTS3KeysDestroy(PCRTS3KEYENTRY pKeys);
+
+/**
+ * Deletes a key in a bucket on the S3 storage server.
+ *
+ * @returns IPRT status code.
+ *
+ * @param   hS3             Handle to the RTS3 interface.
+ * @param   pszBucketName   Name of the bucket contains pszKeyName.
+ * @param   pszKeyName      Name of the key to delete.
+ */
+RTR3DECL(int) RTS3DeleteKey(RTS3 hS3, const char *pszBucketName, const char *pszKeyName);
+
+/**
+ * Downloads a key from a bucket into a file.
+ *
+ * The file must not exists.
+ *
+ * @returns IPRT status code.
+ *
+ * @param   hS3             Handle to the RTS3 interface.
+ * @param   pszBucketName   Name of the bucket that contains pszKeyName.
+ * @param   pszKeyName      Name of the key to download.
+ * @param   pszFilename     Name of the file to store the downloaded key as.
+ */
+RTR3DECL(int) RTS3GetKey(RTS3 hS3, const char *pszBucketName, const char *pszKeyName, const char *pszFilename);
+
+/**
+ * Uploads the content of a file into a key in the specified bucked.
+ *
+ * @returns IPRT status code.
+ *
+ * @param   hS3             Handle to the RTS3 interface.
+ * @param   pszBucketName   Name of the bucket where the new key should be
+ *                          created.
+ * @param   pszKeyName      Name of the new key.
+ * @param   pszFilename     Name of the file to upload the content of.
+ */
+RTR3DECL(int) RTS3PutKey(RTS3 hS3, const char *pszBucketName, const char *pszKeyName, const char *pszFileName);
+
+/** @} */
+
+__END_DECLS
+
+#endif
+
Index: /trunk/src/VBox/Runtime/common/misc/s3.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/misc/s3.cpp	(revision 20043)
+++ /trunk/src/VBox/Runtime/common/misc/s3.cpp	(revision 20043)
@@ -0,0 +1,979 @@
+/* $Id$ */
+/** @file
+ * S3 communication API.
+ */
+
+/*
+ * 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.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * 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.
+ */
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include "iprt/s3.h"
+
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/base64.h>
+#include <iprt/file.h>
+#include <iprt/stream.h>
+
+#include <curl/curl.h>
+#include <openssl/hmac.h>
+#include <libxml/parser.h>
+
+#include "internal/magics.h"
+
+typedef struct RTS3INTERNAL
+{
+    uint32_t u32Magic;
+    CURL *pCurl;
+    char *pszAccessKey;
+    char *pszSecretKey;
+    char *pszBaseUrl;
+    char *pszUserAgent;
+
+    PFNRTS3PROGRESS pfnProgressCallback;
+    void *pvUser;
+
+    long lLastResp;
+} RTS3INTERNAL;
+typedef RTS3INTERNAL* PRTS3INTERNAL;
+
+typedef struct RTS3TMPMEMCHUNK
+{
+    char *pszMem;
+    size_t cSize;
+} RTS3TMPMEMCHUNK;
+typedef RTS3TMPMEMCHUNK *PRTS3TMPMEMCHUNK;
+
+/*******************************************************************************
+*   Defined Constants And Macros                                               *
+*******************************************************************************/
+
+/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
+#define RTS3_VALID_RETURN_RC(hS3, rc) \
+    do { \
+        AssertPtrReturn((hS3), (rc)); \
+        AssertReturn((hS3)->u32Magic == RTS3_MAGIC, (rc)); \
+    } while (0)
+
+/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
+#define RTS3_VALID_RETURN(hS3) RTS3_VALID_RETURN_RC((hS3), VERR_INVALID_HANDLE)
+
+/** Validates a handle and returns (void) if not valid. */
+#define RTS3_VALID_RETURN_VOID(hS3) \
+    do { \
+        AssertPtrReturnVoid(hS3); \
+        AssertReturnVoid((hS3)->u32Magic == RTS3_MAGIC); \
+    } while (0)
+
+/*******************************************************************************
+*   Private RTS3 helper                                                        *
+*******************************************************************************/
+
+static char* rtS3Host(const char* pszBucket, const char* pszKey, const char* pszBaseUrl)
+{
+    char* pszUrl;
+    /* Host header entry */
+    if (pszBucket[0] == 0)
+        RTStrAPrintf(&pszUrl, "%s", pszBaseUrl);
+    else if (pszKey[0] == 0)
+        RTStrAPrintf(&pszUrl, "%s.%s", pszBucket, pszBaseUrl);
+    else
+        RTStrAPrintf(&pszUrl, "%s.%s/%s", pszBucket, pszBaseUrl, pszKey);
+    return pszUrl;
+}
+
+static char* rtS3HostHeader(const char* pszBucket, const char* pszBaseUrl)
+{
+    char* pszUrl;
+    /* Host header entry */
+    if (pszBucket[0] != 0)
+        RTStrAPrintf(&pszUrl, "Host: %s.%s", pszBucket, pszBaseUrl);
+    else
+        RTStrAPrintf(&pszUrl, "Host: %s", pszBaseUrl);
+    return pszUrl;
+}
+
+static char* rtS3DateHeader()
+{
+    /* Date header entry */
+    time_t tt = time(NULL);
+    char* pszDate = (char*)RTMemAlloc(128);
+    strftime(pszDate, 128, "Date: %a, %d %b %Y %H:%M:%S UTC", gmtime(&tt));
+
+    return pszDate;
+}
+
+static char* rtS3ParseHeaders(char** ppHeaders, size_t cHead)
+{
+    char pszEmpty[] = "";
+    char *pszRes = NULL;
+    char *pszDate = pszEmpty;
+    char *pszType = pszEmpty;
+    for(size_t i=0; i < cHead; ++i)
+    {
+        if(ppHeaders[i] != NULL)
+        {
+            if (RTStrStr(ppHeaders[i], "Date: ") == ppHeaders[i])
+            {
+                pszDate = &(ppHeaders[i][6]);
+            }
+            else if(RTStrStr(ppHeaders[i], "Content-Type: ") == ppHeaders[i])
+            {
+                pszType = &(ppHeaders[i][14]);
+//                char *pszTmp = RTStrDup (&(ppHeaders[i][14]));
+//                if (pszRes)
+//                {
+//                    char *pszTmp1 = pszRes;
+//                    RTStrAPrintf(&pszRes, "%s\n%s", pszRes, pszTmp);
+//                    RTStrFree(pszTmp);
+//                    RTStrFree(pszTmp1);
+//                }
+//                else
+//                    pszRes = pszTmp;
+            }
+        }
+    }
+    RTStrAPrintf(&pszRes, "\n%s\n%s", pszType, pszDate);
+    return pszRes;
+}
+
+static char* rtS3Canonicalize(const char* pszAction, const char* pszBucket, const char* pszKey, char** ppszHead, size_t cHead)
+{
+    char* pszRes;
+    /* Grep the necessary info out of the headers & put them in a string */
+    char* pszHead = rtS3ParseHeaders(ppszHead, cHead);
+    /* Create the string which will be used as signature */
+    RTStrAPrintf(&pszRes, "%s\n%s\n/",
+                 pszAction,
+                 pszHead);
+    RTStrFree(pszHead);
+    /* Add the bucket if the bucket isn't empty */
+    if (pszBucket[0] != 0)
+    {
+        char* pszTmp = pszRes;
+        RTStrAPrintf(&pszRes, "%s%s/", pszRes, pszBucket);
+        RTStrFree(pszTmp);
+    }
+    /* Add the key if the key isn't empty. */
+    if (pszKey[0] != 0)
+    {
+        char* pszTmp = pszRes;
+        RTStrAPrintf(&pszRes, "%s%s", pszRes, pszKey);
+        RTStrFree(pszTmp);
+    }
+
+    return pszRes;
+}
+
+static char* rtS3CreateSignature(PRTS3INTERNAL pS3Int, const char* pszAction, const char* pszBucket, const char* pszKey, char** ppszHead, size_t cHead)
+{
+    /* Create a string we can sign */
+    char* pszSig = rtS3Canonicalize(pszAction, pszBucket, pszKey, ppszHead, cHead);
+//    printf ("Sig %s\n", pszSig);
+    /* Sign the string by creating a SHA1 finger print */
+    char pszSigEnc[1024];
+    unsigned int cSigEnc = sizeof(pszSigEnc);
+    HMAC(EVP_sha1(), pS3Int->pszSecretKey, strlen(pS3Int->pszSecretKey),
+         (const unsigned char*)pszSig, strlen(pszSig),
+         (unsigned char*)pszSigEnc, &cSigEnc);
+    RTStrFree(pszSig);
+    /* Convert the signature to Base64 */
+    size_t cSigBase64Enc = RTBase64EncodedLength(cSigEnc) + 1; /* +1 for the 0 */
+    char *pszSigBase64Enc = (char*)RTMemAlloc(cSigBase64Enc);
+    size_t cRes;
+    RTBase64Encode(pszSigEnc, cSigEnc, pszSigBase64Enc, cSigBase64Enc, &cRes);
+
+    return pszSigBase64Enc;
+}
+
+static char* rtS3CreateAuthHeader(PRTS3INTERNAL pS3Int, const char* pszAction, const char* pszBucket, const char* pszKey, char** ppszHead, size_t cHead)
+{
+    char *pszAuth;
+    /* Create a signature out of the header & the bucket/key info */
+    char *pszSigBase64Enc = rtS3CreateSignature(pS3Int, pszAction, pszBucket, pszKey, ppszHead, cHead);
+    /* Create the authorization header entry */
+    RTStrAPrintf(&pszAuth, "Authorization: AWS %s:%s",
+                 pS3Int->pszAccessKey,
+                 pszSigBase64Enc);
+    RTStrFree(pszSigBase64Enc);
+    return pszAuth;
+}
+
+static int rtS3Perform(PRTS3INTERNAL pS3Int)
+{
+    int rc = VERR_INTERNAL_ERROR;
+    CURLcode code = curl_easy_perform(pS3Int->pCurl);
+    if (code == CURLE_OK)
+    {
+        curl_easy_getinfo(pS3Int->pCurl, CURLINFO_RESPONSE_CODE, &pS3Int->lLastResp);
+        switch (pS3Int->lLastResp)
+        {
+            case 200:
+            case 204: rc = VINF_SUCCESS; break; /* No content */
+            case 403: rc = VERR_S3_ACCESS_DENIED; break; /* Access denied */
+            case 404: rc = VERR_S3_NOT_FOUND; break; /* Site not found */
+        }
+    }else
+    {
+        switch(code)
+        {
+            case CURLE_URL_MALFORMAT:
+            case CURLE_COULDNT_RESOLVE_HOST:
+            case CURLE_REMOTE_FILE_NOT_FOUND: rc = VERR_S3_NOT_FOUND; break;
+            case CURLE_REMOTE_ACCESS_DENIED: rc = VERR_S3_ACCESS_DENIED; break;
+            case CURLE_ABORTED_BY_CALLBACK: rc = VERR_S3_CANCELED; break;
+            default: break;
+        }
+    }
+    return rc;
+}
+
+static size_t rtS3WriteNothingCallback(void *pvBuf, size_t cSize, size_t cBSize, void *pvUser)
+{
+    return cSize*cBSize;
+}
+
+static size_t rtS3WriteMemoryCallback(void *pvBuf, size_t cSize, size_t cBSize, void *pvUser)
+{
+    PRTS3TMPMEMCHUNK pTmpMem = (PRTS3TMPMEMCHUNK)pvUser;
+    size_t cRSize = cSize * cBSize;
+
+    pTmpMem->pszMem = (char*)RTMemRealloc(pTmpMem->pszMem, pTmpMem->cSize + cRSize + 1);
+    if (pTmpMem->pszMem)
+    {
+        memcpy(&(pTmpMem->pszMem[pTmpMem->cSize]), pvBuf, cRSize);
+        pTmpMem->cSize += cRSize;
+        pTmpMem->pszMem[pTmpMem->cSize] = 0;
+    }
+    return cRSize;
+}
+
+static size_t rtS3WriteFileCallback(void *pvBuf, size_t cSize, size_t cBSize, void *pvUser)
+{
+    size_t cWritten;
+    RTFileWrite(*(RTFILE*)pvUser, pvBuf, cSize * cBSize, &cWritten);
+    return cWritten;
+}
+
+static size_t rtS3ReadFileCallback(void *pvBuf, size_t cSize, size_t cBSize, void *pvUser)
+{
+  size_t cRead;
+  RTFileRead(*(RTFILE*)pvUser, pvBuf, cSize * cBSize, &cRead);
+
+  return cRead;
+}
+
+static int rtS3ProgressCallback(void *pvUser, double dDlTotal, double dDlNow, double dUlTotal, double dUlNow)
+{
+    if (pvUser)
+    {
+        PRTS3INTERNAL pS3Int = (PRTS3INTERNAL)pvUser;
+        if (pS3Int->pfnProgressCallback)
+        {
+            int rc = VINF_SUCCESS;
+            if (dDlTotal > 0)
+                rc = pS3Int->pfnProgressCallback((unsigned)(100.0/dDlTotal*dDlNow), pS3Int->pvUser);
+            else if (dUlTotal > 0)
+                rc = pS3Int->pfnProgressCallback((unsigned)(100.0/dUlTotal*dUlNow), pS3Int->pvUser);
+            if (rc != VINF_SUCCESS)
+                return -1;
+        }
+    }
+    return CURLE_OK;
+}
+
+static void rtS3ReinitCurl(PRTS3INTERNAL pS3Int)
+{
+    if (pS3Int &&
+        pS3Int->pCurl)
+    {
+        /* Reset the CURL object to an defined state */
+        curl_easy_reset(pS3Int->pCurl);
+        /* Make sure HTTP 1.1 is used */
+        curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+        /* We are cool we are a user agent now */
+        if (pS3Int->pszUserAgent)
+            curl_easy_setopt(pS3Int->pCurl, CURLOPT_USERAGENT, pS3Int->pszUserAgent);
+        /* Check if the user has a progress callback requested */
+        if (pS3Int->pfnProgressCallback)
+        {
+            /* Yes, we are willing to receive progress info */
+            curl_easy_setopt(pS3Int->pCurl, CURLOPT_NOPROGRESS, 0);
+            /* Callback for the progress info */
+            curl_easy_setopt(pS3Int->pCurl, CURLOPT_PROGRESSFUNCTION, rtS3ProgressCallback);
+            curl_easy_setopt(pS3Int->pCurl, CURLOPT_PROGRESSDATA, pS3Int);
+        }
+        /* Disable the internal cURL write function by providing one which does
+         * nothing */
+        curl_easy_setopt(pS3Int->pCurl, CURLOPT_WRITEFUNCTION, rtS3WriteNothingCallback);
+        /* Set this do get some verbose info what CURL is doing */
+//        curl_easy_setopt(pS3Int->pCurl, CURLOPT_VERBOSE, 1);
+    }
+}
+
+/*******************************************************************************
+*   Private XML helper                                                         *
+*******************************************************************************/
+
+static xmlNodePtr rtS3FindNode(xmlNodePtr pNode, const char* pszName)
+{
+    pNode = pNode->xmlChildrenNode;
+    while (pNode != NULL)
+    {
+        /* Check this level */
+        if ((!xmlStrcmp(pNode->name, (const xmlChar *)pszName)))
+            return pNode;
+        /* Recursively check the childs of this node */
+        xmlNodePtr pChildNode = rtS3FindNode(pNode, pszName);
+        if (pChildNode != NULL)
+            return pChildNode;
+        /* Next node*/
+        pNode = pNode->next;
+    }
+    return pNode;
+}
+
+static int rtS3ReadXmlFromMemory(PRTS3TMPMEMCHUNK pChunk, const char* pszRootElement, xmlDocPtr *ppDoc, xmlNodePtr *ppCur)
+{
+    *ppDoc = xmlReadMemory(pChunk->pszMem, pChunk->cSize, "", "ISO-8859-1", XML_PARSE_NOBLANKS);
+    if (*ppDoc == NULL)
+        return VERR_PARSE_ERROR;
+
+    *ppCur = xmlDocGetRootElement(*ppDoc);
+    if (*ppCur == NULL)
+    {
+        xmlFreeDoc(*ppDoc);
+        return VERR_PARSE_ERROR;
+    }
+    if (xmlStrcmp((*ppCur)->name, (const xmlChar *) pszRootElement))
+    {
+        xmlFreeDoc(*ppDoc);
+        return VERR_PARSE_ERROR;
+    }
+    return VINF_SUCCESS;
+}
+
+static void rtS3ExtractAllBuckets(xmlDocPtr pDoc, xmlNodePtr pNode, PCRTS3BUCKETENTRY *ppBuckets)
+{
+    pNode = rtS3FindNode(pNode, "Buckets");
+    if (pNode != NULL)
+    {
+        PRTS3BUCKETENTRY pPrevBucket = NULL;
+        xmlNodePtr pCurBucket = pNode->xmlChildrenNode;
+        while (pCurBucket != NULL)
+        {
+            if ((!xmlStrcmp(pCurBucket->name, (const xmlChar *)"Bucket")))
+            {
+                PRTS3BUCKETENTRY pBucket = (PRTS3BUCKETENTRY)RTMemAllocZ(sizeof(RTS3BUCKETENTRY));
+                pBucket->pPrev = pPrevBucket;
+                if (pPrevBucket)
+                    pPrevBucket->pNext = pBucket;
+                else
+                    (*ppBuckets) = pBucket;
+                pPrevBucket = pBucket;
+                xmlNodePtr pCurCont = pCurBucket->xmlChildrenNode;
+                while (pCurCont != NULL)
+                {
+                    if ((!xmlStrcmp(pCurCont->name, (const xmlChar *)"Name")))
+                    {
+                        xmlChar *pszKey = xmlNodeListGetString(pDoc, pCurCont->xmlChildrenNode, 1);
+                        pBucket->pszName = RTStrDup((const char*)pszKey);
+                        xmlFree(pszKey);
+                    }
+                    if ((!xmlStrcmp(pCurCont->name, (const xmlChar*)"CreationDate")))
+                    {
+                        xmlChar *pszKey = xmlNodeListGetString(pDoc, pCurCont->xmlChildrenNode, 1);
+                        pBucket->pszCreationDate = RTStrDup((const char*)pszKey);
+                        xmlFree(pszKey);
+                    }
+                    pCurCont = pCurCont->next;
+                }
+            }
+            pCurBucket = pCurBucket->next;
+        }
+    }
+}
+
+static void rtS3ExtractAllKeys(xmlDocPtr pDoc, xmlNodePtr pNode, PCRTS3KEYENTRY *ppKeys)
+{
+    if (pNode != NULL)
+    {
+        PRTS3KEYENTRY pPrevKey = NULL;
+        xmlNodePtr pCurKey = pNode->xmlChildrenNode;
+        while (pCurKey != NULL)
+        {
+            if ((!xmlStrcmp(pCurKey->name, (const xmlChar *)"Contents")))
+            {
+                PRTS3KEYENTRY pKey = (PRTS3KEYENTRY)RTMemAllocZ(sizeof(RTS3KEYENTRY));
+                pKey->pPrev = pPrevKey;
+                if (pPrevKey)
+                    pPrevKey->pNext = pKey;
+                else
+                    (*ppKeys) = pKey;
+                pPrevKey = pKey;
+                xmlNodePtr pCurCont = pCurKey->xmlChildrenNode;
+                while (pCurCont != NULL)
+                {
+                    if ((!xmlStrcmp(pCurCont->name, (const xmlChar *)"Key")))
+                    {
+                        xmlChar *pszKey = xmlNodeListGetString(pDoc, pCurCont->xmlChildrenNode, 1);
+                        pKey->pszName = RTStrDup((const char*)pszKey);
+                        xmlFree(pszKey);
+                    }
+                    if ((!xmlStrcmp(pCurCont->name, (const xmlChar*)"LastModified")))
+                    {
+                        xmlChar *pszKey = xmlNodeListGetString(pDoc, pCurCont->xmlChildrenNode, 1);
+                        pKey->pszLastModified = RTStrDup((const char*)pszKey);
+                        xmlFree(pszKey);
+                    }
+                    if ((!xmlStrcmp(pCurCont->name, (const xmlChar*)"Size")))
+                    {
+                        xmlChar *pszKey = xmlNodeListGetString(pDoc, pCurCont->xmlChildrenNode, 1);
+                        pKey->cbFile = RTStrToUInt64((const char*)pszKey);
+                        xmlFree(pszKey);
+                    }
+                    pCurCont = pCurCont->next;
+                }
+            }
+            pCurKey = pCurKey->next;
+        }
+    }
+}
+
+/*******************************************************************************
+*   Public RTS3 interface                                                      *
+*******************************************************************************/
+
+RTR3DECL(int) RTS3Create(PRTS3 ppS3, const char* pszAccessKey, const char* pszSecretKey, const char* pszBaseUrl, const char* pszUserAgent /* = NULL */)
+{
+    AssertPtrReturn(ppS3, VERR_INVALID_POINTER);
+
+    /* We need at least an URL to connect with */
+    if (pszBaseUrl == NULL ||
+        pszBaseUrl[0] == 0)
+        return VERR_INVALID_PARAMETER;
+
+    /* In windows, this will init the winsock stuff */
+    if (curl_global_init(CURL_GLOBAL_ALL) != 0)
+        return VERR_INTERNAL_ERROR;
+
+    CURL* pCurl = curl_easy_init();
+    if (!pCurl)
+        return VERR_INTERNAL_ERROR;
+
+    PRTS3INTERNAL pS3Int = (PRTS3INTERNAL)RTMemAllocZ(sizeof(RTS3INTERNAL));
+    if (pS3Int == NULL)
+        return VERR_NO_MEMORY;
+
+    pS3Int->u32Magic = RTS3_MAGIC;
+    pS3Int->pCurl = pCurl;
+    pS3Int->pszAccessKey = RTStrDup(pszAccessKey);
+    pS3Int->pszSecretKey = RTStrDup(pszSecretKey);
+    pS3Int->pszBaseUrl = RTStrDup(pszBaseUrl);
+    if (pszUserAgent)
+        pS3Int->pszUserAgent = RTStrDup(pszUserAgent);
+
+    *ppS3 = (RTS3)pS3Int;
+
+    return VINF_SUCCESS;
+}
+
+RTR3DECL(void) RTS3Destroy(RTS3 hS3)
+{
+    if (hS3 == NIL_RTS3)
+        return;
+
+    PRTS3INTERNAL pS3Int = hS3;
+    RTS3_VALID_RETURN_VOID(pS3Int);
+
+    curl_easy_cleanup(pS3Int->pCurl);
+
+    pS3Int->u32Magic = RTS3_MAGIC_DEAD;
+
+    if (pS3Int->pszUserAgent)
+        RTStrFree(pS3Int->pszUserAgent);
+    RTStrFree(pS3Int->pszBaseUrl);
+    RTStrFree(pS3Int->pszSecretKey);
+    RTStrFree(pS3Int->pszAccessKey);
+
+    RTMemFree(pS3Int);
+
+    curl_global_cleanup();
+}
+
+RTR3DECL(void) RTS3SetProgressCallback(RTS3 hS3, PFNRTS3PROGRESS pfnProgressCallback, void *pvUser /* = NULL */)
+{
+    PRTS3INTERNAL pS3Int = hS3;
+    RTS3_VALID_RETURN_VOID(pS3Int);
+
+    pS3Int->pfnProgressCallback = pfnProgressCallback;
+    pS3Int->pvUser = pvUser;
+}
+
+RTR3DECL(int) RTS3GetBuckets(RTS3 hS3, PCRTS3BUCKETENTRY *ppBuckets)
+{
+    PRTS3INTERNAL pS3Int = hS3;
+    RTS3_VALID_RETURN(pS3Int);
+
+    /* Properly initialize this */
+    *ppBuckets = NULL;
+
+    /* Reset the CURL object to an defined state */
+    rtS3ReinitCurl(pS3Int);
+    /* Create the CURL object to operate on */
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_URL, pS3Int->pszBaseUrl);
+
+    /* Create the three basic header entries */
+    char *ppszHead[3] =
+    {
+        rtS3HostHeader("", pS3Int->pszBaseUrl), /* Host entry */
+        rtS3DateHeader(),                       /* Date entry */
+        NULL                                    /* Authorization entry */
+    };
+    /* Create the authorization header entry */
+    ppszHead[RT_ELEMENTS(ppszHead)-1] = rtS3CreateAuthHeader(pS3Int, "GET", "", "", ppszHead, RT_ELEMENTS(ppszHead));
+
+    /* Add all headers to curl */
+    struct curl_slist* pHeaders = NULL; /* Init to NULL is important */
+    for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
+        pHeaders = curl_slist_append(pHeaders, ppszHead[i]);
+
+    /* Pass our list of custom made headers */
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTPHEADER, pHeaders);
+
+    RTS3TMPMEMCHUNK chunk = { NULL, 0 };
+    /* Set the callback which receive the content */
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_WRITEFUNCTION, rtS3WriteMemoryCallback);
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_WRITEDATA, (void *)&chunk);
+    /* Start the request */
+    int rc = rtS3Perform(pS3Int);
+
+    /* Regardless of the result, free all used resources first*/
+    curl_slist_free_all(pHeaders);
+    for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
+        RTStrFree(ppszHead[i]);
+
+    /* On success parse the result */
+    if (RT_SUCCESS(rc))
+    {
+        xmlDocPtr pDoc;
+        xmlNodePtr pCur;
+        /* Parse the xml memory for "ListAllMyBucketsResult" */
+        rc = rtS3ReadXmlFromMemory(&chunk, "ListAllMyBucketsResult", &pDoc, &pCur);
+        if (RT_SUCCESS(rc))
+        {
+            /* Now extract all buckets */
+            rtS3ExtractAllBuckets(pDoc, pCur, ppBuckets);
+            /* Free the xml stuff */
+            xmlFreeDoc(pDoc);
+        }
+    }
+    /* Free the temporary memory */
+    RTMemFree(chunk.pszMem);
+
+    return rc;
+}
+
+RTR3DECL(int) RTS3BucketsDestroy(PCRTS3BUCKETENTRY pBuckets)
+{
+    if (!pBuckets)
+        return VINF_SUCCESS;
+
+    while (pBuckets)
+    {
+        PCRTS3BUCKETENTRY pTemp = pBuckets;
+        RTStrFree((char*)pBuckets->pszName);
+        RTStrFree((char*)pBuckets->pszCreationDate);
+        pBuckets = pBuckets->pNext;
+        RTMemFree((PRTS3BUCKETENTRY )pTemp);
+    }
+    return VINF_SUCCESS;
+}
+
+RTR3DECL(int) RTS3CreateBucket(RTS3 hS3, const char* pszBucketName)
+{
+    PRTS3INTERNAL pS3Int = hS3;
+    RTS3_VALID_RETURN(pS3Int);
+
+    /* Reset the CURL object to an defined state */
+    rtS3ReinitCurl(pS3Int);
+
+    char* pszUrl = rtS3Host(pszBucketName, "", pS3Int->pszBaseUrl);
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_URL, pszUrl);
+    RTStrFree(pszUrl);
+
+    /* Create the basic header entries */
+    char *ppszHead[4] =
+    {
+        RTStrDup("Content-Length: 0"),                     /* Content length entry */
+        rtS3HostHeader(pszBucketName, pS3Int->pszBaseUrl), /* Host entry */
+        rtS3DateHeader(),                                  /* Date entry */
+        NULL                                               /* Authorization entry */
+    };
+    /* Create the authorization header entry */
+    ppszHead[RT_ELEMENTS(ppszHead)-1] = rtS3CreateAuthHeader(pS3Int, "PUT", pszBucketName, "", ppszHead, RT_ELEMENTS(ppszHead));
+
+    /* Add all headers to curl */
+    struct curl_slist* pHeaders = NULL; /* Init to NULL is important */
+    for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
+        pHeaders = curl_slist_append(pHeaders, ppszHead[i]);
+
+    /* Pass our list of custom made headers */
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTPHEADER, pHeaders);
+
+    /* Set CURL in upload mode */
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_PUT, 1);
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_UPLOAD, 1);
+
+    /* Set the size of the file we like to transfer */
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_INFILESIZE_LARGE, 0);
+
+    /* Start the request */
+    int rc = rtS3Perform(pS3Int);
+    if (RT_FAILURE(rc))
+    {
+        /* Handle special failures */
+        if (pS3Int->lLastResp == 409)
+            rc = VERR_S3_BUCKET_ALREADY_EXISTS;
+    }
+
+    /* Regardless of the result, free all used resources first*/
+    curl_slist_free_all(pHeaders);
+    for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
+        RTStrFree(ppszHead[i]);
+
+    return rc;
+}
+
+RTR3DECL(int) RTS3DeleteBucket(RTS3 hS3, const char* pszBucketName)
+{
+    PRTS3INTERNAL pS3Int = hS3;
+    RTS3_VALID_RETURN(pS3Int);
+
+    /* Reset the CURL object to an defined state */
+    rtS3ReinitCurl(pS3Int);
+
+    char* pszUrl = rtS3Host(pszBucketName, "", pS3Int->pszBaseUrl);
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_URL, pszUrl);
+    RTStrFree(pszUrl);
+
+    /* Create the three basic header entries */
+    char *ppszHead[3] =
+    {
+        rtS3HostHeader(pszBucketName, pS3Int->pszBaseUrl), /* Host entry */
+        rtS3DateHeader(),                                  /* Date entry */
+        NULL                                               /* Authorization entry */
+    };
+    /* Create the authorization header entry */
+    ppszHead[RT_ELEMENTS(ppszHead)-1] = rtS3CreateAuthHeader(pS3Int, "DELETE", pszBucketName, "", ppszHead, RT_ELEMENTS(ppszHead));
+
+    /* Add all headers to curl */
+    struct curl_slist* pHeaders = NULL; /* Init to NULL is important */
+    for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
+        pHeaders = curl_slist_append(pHeaders, ppszHead[i]);
+
+    /* Pass our list of custom made headers */
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTPHEADER, pHeaders);
+
+    /* Set CURL in delete mode */
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_CUSTOMREQUEST, "DELETE");
+
+    /* Start the request */
+    int rc = rtS3Perform(pS3Int);
+    if (RT_FAILURE(rc))
+    {
+        /* Handle special failures */
+        if (pS3Int->lLastResp == 409)
+            rc = VERR_S3_BUCKET_NOT_EMPTY;
+    }
+
+    /* Regardless of the result, free all used resources first*/
+    curl_slist_free_all(pHeaders);
+    for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
+        RTStrFree(ppszHead[i]);
+
+    return rc;
+}
+
+RTR3DECL(int) RTS3GetBucketKeys(RTS3 hS3, const char* pszBucketName, PCRTS3KEYENTRY *ppKeys)
+{
+    PRTS3INTERNAL pS3Int = hS3;
+    RTS3_VALID_RETURN(pS3Int);
+
+    *ppKeys = NULL;
+
+    /* Reset the CURL object to an defined state */
+    rtS3ReinitCurl(pS3Int);
+
+    char* pszUrl = rtS3Host(pszBucketName, "", pS3Int->pszBaseUrl);
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_URL, pszUrl);
+    RTStrFree(pszUrl);
+
+    /* Create the three basic header entries */
+    char *ppszHead[3] =
+    {
+        rtS3HostHeader(pszBucketName, pS3Int->pszBaseUrl), /* Host entry */
+        rtS3DateHeader(),                                  /* Date entry */
+        NULL                                               /* Authorization entry */
+    };
+    /* Create the authorization header entry */
+    ppszHead[RT_ELEMENTS(ppszHead)-1] = rtS3CreateAuthHeader(pS3Int, "GET", pszBucketName, "", ppszHead, RT_ELEMENTS(ppszHead));
+
+    /* Add all headers to curl */
+    struct curl_slist* pHeaders = NULL; /* Init to NULL is important */
+    for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
+        pHeaders = curl_slist_append(pHeaders, ppszHead[i]);
+
+    /* Pass our list of custom made headers */
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTPHEADER, pHeaders);
+
+    RTS3TMPMEMCHUNK chunk = { NULL, 0 };
+    /* Set the callback which recieve the content */
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_WRITEFUNCTION, rtS3WriteMemoryCallback);
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_WRITEDATA, (void *)&chunk);
+
+    /* Start the request */
+    int rc = rtS3Perform(pS3Int);
+
+    /* Regardless of the result, free all used resources first*/
+    curl_slist_free_all(pHeaders);
+    for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
+        RTStrFree(ppszHead[i]);
+
+    /* On success parse the result */
+    if (RT_SUCCESS(rc))
+    {
+        xmlDocPtr pDoc;
+        xmlNodePtr pCur;
+        /* Parse the xml memory for "ListBucketResult" */
+        rc = rtS3ReadXmlFromMemory(&chunk, "ListBucketResult", &pDoc, &pCur);
+        if (RT_SUCCESS(rc))
+        {
+            /* Now extract all buckets */
+            rtS3ExtractAllKeys(pDoc, pCur, ppKeys);
+            /* Free the xml stuff */
+            xmlFreeDoc(pDoc);
+        }
+    }
+    /* Free the tempory memory */
+    RTMemFree(chunk.pszMem);
+
+    return rc;
+}
+
+RTR3DECL(int) RTS3KeysDestroy(PCRTS3KEYENTRY pKeys)
+{
+    if (!pKeys)
+        return VINF_SUCCESS;
+
+    while (pKeys)
+    {
+        PCRTS3KEYENTRY pTemp = pKeys;
+        RTStrFree((char*)pKeys->pszName);
+        RTStrFree((char*)pKeys->pszLastModified);
+        pKeys = pKeys->pNext;
+        RTMemFree((PRTS3KEYENTRY)pTemp);
+    }
+    return VINF_SUCCESS;
+}
+
+RTR3DECL(int) RTS3DeleteKey(RTS3 hS3, const char* pszBucketName, const char* pszKeyName)
+{
+    PRTS3INTERNAL pS3Int = hS3;
+    RTS3_VALID_RETURN(pS3Int);
+
+    /* Reset the CURL object to an defined state */
+    rtS3ReinitCurl(pS3Int);
+
+    char* pszUrl = rtS3Host(pszBucketName, pszKeyName, pS3Int->pszBaseUrl);
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_URL, pszUrl);
+    RTStrFree(pszUrl);
+
+    /* Create the three basic header entries */
+    char *ppszHead[3] =
+    {
+        rtS3HostHeader(pszBucketName, pS3Int->pszBaseUrl), /* Host entry */
+        rtS3DateHeader(),                                  /* Date entry */
+        NULL                                               /* Authorization entry */
+    };
+    /* Create the authorization header entry */
+    ppszHead[RT_ELEMENTS(ppszHead)-1] = rtS3CreateAuthHeader(pS3Int, "DELETE", pszBucketName, pszKeyName, ppszHead, RT_ELEMENTS(ppszHead));
+
+    /* Add all headers to curl */
+    struct curl_slist* pHeaders = NULL; /* Init to NULL is important */
+    for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
+        pHeaders = curl_slist_append(pHeaders, ppszHead[i]);
+
+    /* Pass our list of custom made headers */
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTPHEADER, pHeaders);
+
+    /* Set CURL in delete mode */
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_CUSTOMREQUEST, "DELETE");
+
+    /* Start the request */
+    int rc = rtS3Perform(pS3Int);
+
+    /* Regardless of the result, free all used resources first*/
+    curl_slist_free_all(pHeaders);
+    for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
+        RTStrFree(ppszHead[i]);
+
+    return rc;
+}
+
+RTR3DECL(int) RTS3GetKey(RTS3 hS3, const char* pszBucketName, const char* pszKeyName, const char* pszFileName)
+{
+    PRTS3INTERNAL pS3Int = hS3;
+    RTS3_VALID_RETURN(pS3Int);
+
+    /* Reset the CURL object to an defined state */
+    rtS3ReinitCurl(pS3Int);
+
+    /* Open the file */
+    RTFILE hFile;
+    int rc = RTFileOpen(&hFile, pszFileName, RTFILE_O_CREATE | RTFILE_O_WRITE);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    char* pszUrl = rtS3Host(pszBucketName, pszKeyName, pS3Int->pszBaseUrl);
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_URL, pszUrl);
+    RTStrFree(pszUrl);
+
+    /* Create the three basic header entries */
+    char *ppszHead[3] =
+    {
+        rtS3HostHeader(pszBucketName, pS3Int->pszBaseUrl), /* Host entry */
+        rtS3DateHeader(),                                  /* Date entry */
+        NULL                                               /* Authorization entry */
+    };
+    /* Create the authorization header entry */
+    ppszHead[RT_ELEMENTS(ppszHead)-1] = rtS3CreateAuthHeader(pS3Int, "GET", pszBucketName, pszKeyName, ppszHead, RT_ELEMENTS(ppszHead));
+
+    /* Add all headers to curl */
+    struct curl_slist* pHeaders = NULL; /* Init to NULL is important */
+    for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
+        pHeaders = curl_slist_append(pHeaders, ppszHead[i]);
+
+    /* Pass our list of custom made headers */
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTPHEADER, pHeaders);
+
+    /* Set the callback which receive the content */
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_WRITEFUNCTION, rtS3WriteFileCallback);
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_WRITEDATA, &hFile);
+
+    /* Start the request */
+    rc = rtS3Perform(pS3Int);
+
+    /* Regardless of the result, free all used resources first*/
+    curl_slist_free_all(pHeaders);
+    for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
+        RTStrFree(ppszHead[i]);
+
+    /* Close the open file */
+    RTFileClose(hFile);
+
+    return rc;
+}
+
+RTR3DECL(int) RTS3PutKey(RTS3 hS3, const char* pszBucketName, const char* pszKeyName, const char* pszFileName)
+{
+    PRTS3INTERNAL pS3Int = hS3;
+    RTS3_VALID_RETURN(pS3Int);
+
+    /* Reset the CURL object to an defined state */
+    rtS3ReinitCurl(pS3Int);
+
+    /* Open the file */
+    RTFILE hFile;
+    int rc = RTFileOpen(&hFile, pszFileName, RTFILE_O_OPEN | RTFILE_O_READ);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    uint64_t cbFileSize;
+    rc = RTFileGetSize(hFile, &cbFileSize);
+    if (RT_FAILURE(rc))
+    {
+        RTFileClose(hFile);
+        return rc;
+    }
+
+    char* pszUrl = rtS3Host(pszBucketName, pszKeyName, pS3Int->pszBaseUrl);
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_URL, pszUrl);
+    RTStrFree(pszUrl);
+
+    char* pszContentLength;
+    RTStrAPrintf(&pszContentLength, "Content-Length: %lu", cbFileSize);
+    /* Create the three basic header entries */
+    char *ppszHead[5] =
+    {
+        /* todo: For now we use octet-stream for all types. Later we should try
+         * to set the right one (libmagic from the file packet could be a
+         * candidate for finding the right type). */
+        RTStrDup("Content-Type: octet-stream"),            /* Content type entry */
+        pszContentLength,                                  /* Content length entry */
+        rtS3HostHeader(pszBucketName, pS3Int->pszBaseUrl), /* Host entry */
+        rtS3DateHeader(),                                  /* Date entry */
+        NULL                                               /* Authorization entry */
+    };
+    /* Create the authorization header entry */
+    ppszHead[RT_ELEMENTS(ppszHead)-1] = rtS3CreateAuthHeader(pS3Int, "PUT", pszBucketName, pszKeyName, ppszHead, RT_ELEMENTS(ppszHead));
+
+    /* Add all headers to curl */
+    struct curl_slist* pHeaders = NULL; /* Init to NULL is important */
+    for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
+        pHeaders = curl_slist_append(pHeaders, ppszHead[i]);
+
+    /* Pass our list of custom made headers */
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTPHEADER, pHeaders);
+
+    /* Set CURL in upload mode */
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_PUT, 1);
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_UPLOAD, 1);
+
+    /* Set the size of the file we like to transfer */
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_INFILESIZE_LARGE, cbFileSize);
+
+    /* Set the callback which send the content */
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_READFUNCTION, rtS3ReadFileCallback);
+    curl_easy_setopt(pS3Int->pCurl, CURLOPT_READDATA, &hFile);
+
+    /* Start the request */
+    rc = rtS3Perform(pS3Int);
+
+    /* Regardless of the result, free all used resources first*/
+    curl_slist_free_all(pHeaders);
+    for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
+        RTStrFree(ppszHead[i]);
+
+    /* Close the open file */
+    RTFileClose(hFile);
+
+    return rc;
+}
+
Index: /trunk/src/VBox/Runtime/testcase/tstRTS3.cpp
===================================================================
--- /trunk/src/VBox/Runtime/testcase/tstRTS3.cpp	(revision 20043)
+++ /trunk/src/VBox/Runtime/testcase/tstRTS3.cpp	(revision 20043)
@@ -0,0 +1,237 @@
+/* $Id$ */
+/** @file
+ * IPRT Testcase - Simple Storage Service (S3) Communication API
+ */
+
+/*
+ * 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.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * 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.
+ */
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include <iprt/s3.h>
+#include <iprt/stream.h>
+#include <iprt/initterm.h>
+#include <iprt/err.h>
+#include <iprt/test.h>
+
+/* Manual configuration of this testcase */
+#define TSTS3_CREATEBUCKET
+#define TSTS3_PUTGETKEY
+//#define TSTS3_SHOWPROGRESS
+
+#ifdef TSTS3_CREATEBUCKET
+//# define TSTS3_CREATEBUCKET_BUCKETNAME "tstS3CreateBucket"
+# define TSTS3_CREATEBUCKET_BUCKETNAME "tt9"
+#endif /* TSTS3_CREATEBUCKET */
+
+#ifdef TSTS3_PUTGETKEY
+# define TSTS3_PUTGETKEY_BUCKETNAME "tstS3PutGetBucket"
+# define TSTS3_PUTGETKEY_KEYNAME "tstS3PutGetKey"
+# define TSTS3_PUTGETKEY_PUTFILE "tstS3"
+# define TSTS3_PUTGETKEY_GETFILE "tstS3_fetched"
+#endif /* TSTS3_PUTGETKEY */
+
+static int progress(unsigned uPercent, void *pvUser)
+{
+#ifdef TSTS3_SHOWPROGRESS
+    RTTestIPrintf(RTTESTLVL_ALWAYS, " Progress for %s - %d%% done.\n", (char*)pvUser, (int)uPercent);
+#endif /* TSTS3_SHOWPROGRESS */
+    return VINF_SUCCESS;
+}
+
+void fetchAllBuckets(RTS3 hS3)
+{
+    /* Fetch all available buckets */
+    RTTestIPrintf(RTTESTLVL_ALWAYS, " List all buckets...\n");
+    char pszTitle[] = "RTS3GetBuckets";
+    RTS3SetProgressCallback(hS3, progress, pszTitle);
+    PCRTS3BUCKETENTRY pBuckets = NULL;
+    int rc = RTS3GetBuckets(hS3, &pBuckets);
+    if (RT_SUCCESS(rc))
+    {
+        if (pBuckets)
+        {
+            PCRTS3BUCKETENTRY pTmpBuckets = pBuckets;
+            while (pBuckets)
+            {
+                RTTestIPrintf(RTTESTLVL_ALWAYS, "  > %s, %s\n", pBuckets->pszName, pBuckets->pszCreationDate);
+                pBuckets = pBuckets->pNext;
+            }
+            RTS3BucketsDestroy(pTmpBuckets);
+        }
+        else
+            RTTestIPrintf(RTTESTLVL_ALWAYS, "  > empty\n");
+    }
+    else
+        RTTestIFailed("RTS3GetBuckets -> %Rrc", rc);
+}
+
+void createBucket(RTS3 hS3, const char *pszBucketName)
+{
+    /* Create the bucket */
+    RTTestIPrintf(RTTESTLVL_ALWAYS, " Create bucket '%s'...\n", pszBucketName);
+    char pszTitle[] = "RTS3CreateBucket";
+    RTS3SetProgressCallback(hS3, progress, pszTitle);
+    int rc = RTS3CreateBucket(hS3, pszBucketName);
+    if (RT_FAILURE(rc))
+        RTTestIFailed("RTS3CreateBucket  -> %Rrc", rc);
+}
+
+void deleteBucket(RTS3 hS3, const char *pszBucketName)
+{
+    /* Delete the bucket */
+    RTTestIPrintf(RTTESTLVL_ALWAYS, " Delete bucket '%s'...\n", pszBucketName);
+    char pszTitle[] = "RTS3DeleteBucket";
+    RTS3SetProgressCallback(hS3, progress, pszTitle);
+    int rc = RTS3DeleteBucket(hS3, pszBucketName);
+    if (RT_FAILURE(rc))
+        RTTestIFailed("RTS3DeleteBucket -> %Rrc", rc);
+}
+
+void fetchAllKeys(RTS3 hS3, const char *pszBucketName)
+{
+    /* Fetch all available keys of a specific bucket */
+    RTTestIPrintf(RTTESTLVL_ALWAYS, " List all keys of bucket '%s'...\n", pszBucketName);
+    PCRTS3KEYENTRY pKeys = NULL;
+    char pszTitle[] = "RTS3GetBucketKeys";
+    RTS3SetProgressCallback(hS3, progress, pszTitle);
+    int rc = RTS3GetBucketKeys(hS3, pszBucketName, &pKeys);
+    if (RT_SUCCESS(rc))
+    {
+        if (pKeys)
+        {
+            PCRTS3KEYENTRY pTmpKeys = pKeys;
+            while (pKeys)
+            {
+                RTTestIPrintf(RTTESTLVL_ALWAYS, "  > %s, %s, %lu\n", pKeys->pszName, pKeys->pszLastModified, pKeys->cbFile);
+                pKeys = pKeys->pNext;
+            }
+            RTS3KeysDestroy(pTmpKeys);
+        }
+        else
+            RTTestIPrintf(RTTESTLVL_ALWAYS, "  > empty\n");
+    }
+    else
+        RTTestIFailed("RTS3GetBucketKeys -> %Rrc", rc);
+}
+
+void deleteKey(RTS3 hS3, const char *pszBucketName, const char *pszKeyName)
+{
+    /* Delete the key */
+    RTTestIPrintf(RTTESTLVL_ALWAYS, " Delete key '%s' in bucket '%s'...\n", pszKeyName, pszBucketName);
+    char pszTitle[] = "RTS3DeleteKey";
+    RTS3SetProgressCallback(hS3, progress, pszTitle);
+    int rc = RTS3DeleteKey(hS3, pszBucketName, pszKeyName);
+    if (RT_FAILURE(rc))
+        RTTestIFailed("RTS3DeleteKey -> %Rrc", rc);
+}
+
+void getKey(RTS3 hS3, const char *pszBucketName, const char *pszKeyName, const char *pszFilename)
+{
+    /* Fetch the content of a key */
+    RTTestIPrintf(RTTESTLVL_ALWAYS, " Get key '%s' from bucket '%s' into '%s' ...\n", pszKeyName, pszBucketName, pszFilename);
+    char pszTitle[] = "RTS3GetKey";
+    RTS3SetProgressCallback(hS3, progress, pszTitle);
+    int rc = RTS3GetKey(hS3, pszBucketName, pszKeyName, pszFilename);
+    if (RT_FAILURE(rc))
+        RTTestIFailed("RTS3GetKey -> %Rrc", rc);
+}
+
+void putKey(RTS3 hS3, const char *pszBucketName, const char *pszKeyName, const char *pszFilename)
+{
+    /* Fetch the content of a key */
+    RTTestIPrintf(RTTESTLVL_ALWAYS, " Put '%s' into key '%s' in bucket '%s' ...\n", pszFilename, pszKeyName, pszBucketName);
+    char pszTitle[] = "RTS3PutKey";
+    RTS3SetProgressCallback(hS3, progress, pszTitle);
+    int rc = RTS3PutKey(hS3, pszBucketName, pszKeyName, pszFilename);
+    if (RT_FAILURE(rc))
+        RTTestIFailed("RTS3PutKey -> %Rrc", rc);
+}
+
+int main(int argc, char **argv)
+{
+    /*
+     * If no args, display usage.
+     */
+    if (argc <= 2)
+    {
+        RTPrintf("Syntax: %s [Access Key] [Secret Key]\n", argv[0]);
+        return 1;
+    }
+
+    int rc = RTR3Init();
+    if (RT_FAILURE(rc))
+        return 1;
+
+    RTTEST hTest;
+    rc = RTTestCreate("tstRTS3", &hTest);
+    if (RT_FAILURE(rc))
+        return 1;
+    RTTestBanner(hTest);
+
+    RTTestSubF(hTest, "Create S3");
+    RTS3 hS3;
+    rc = RTS3Create(&hS3, argv[1], argv[2], "object.storage.network.com", "tstS3-agent/1.0");
+    if (RT_FAILURE(rc))
+    {
+        RTTestIFailed("RTS3Create -> %Rrc", rc);
+        return RTTestSummaryAndDestroy(hTest);
+    }
+
+    RTTestSub(hTest, "Fetch buckets");
+    fetchAllBuckets(hS3);
+    RTTestSub(hTest, "Fetch keys");
+    fetchAllKeys(hS3, "bla");
+
+#ifdef TSTS3_CREATEBUCKET
+    RTTestSub(hTest, "Create bucket");
+    createBucket(hS3, TSTS3_CREATEBUCKET_BUCKETNAME);
+    fetchAllBuckets(hS3);
+    deleteBucket(hS3, TSTS3_CREATEBUCKET_BUCKETNAME);
+    fetchAllBuckets(hS3);
+#endif /* TSTS3_CREATEBUCKET */
+
+
+#ifdef TSTS3_PUTGETKEY
+    RTTestSub(hTest, "Put key");
+    createBucket(hS3, TSTS3_PUTGETKEY_BUCKETNAME);
+    putKey(hS3, TSTS3_PUTGETKEY_BUCKETNAME, TSTS3_PUTGETKEY_KEYNAME, TSTS3_PUTGETKEY_PUTFILE);
+    fetchAllKeys(hS3, TSTS3_PUTGETKEY_BUCKETNAME);
+    getKey(hS3, TSTS3_PUTGETKEY_BUCKETNAME, TSTS3_PUTGETKEY_KEYNAME, TSTS3_PUTGETKEY_GETFILE);
+    deleteKey(hS3, TSTS3_PUTGETKEY_BUCKETNAME, TSTS3_PUTGETKEY_KEYNAME);
+    fetchAllKeys(hS3, TSTS3_PUTGETKEY_BUCKETNAME);
+    deleteBucket(hS3, TSTS3_PUTGETKEY_BUCKETNAME);
+#endif /* TSTS3_PUTGETKEY */
+
+    RTS3Destroy(hS3);
+
+    /*
+     * Summary
+     */
+    return RTTestSummaryAndDestroy(hTest);
+}
+
