Include libs3 sources in the BlueSky tree.
[bluesky.git] / libs3-1.4 / src / bucket.c
diff --git a/libs3-1.4/src/bucket.c b/libs3-1.4/src/bucket.c
new file mode 100644 (file)
index 0000000..9dcc48c
--- /dev/null
@@ -0,0 +1,731 @@
+/** **************************************************************************
+ * bucket.c
+ * 
+ * Copyright 2008 Bryan Ischo <bryan@ischo.com>
+ * 
+ * This file is part of libs3.
+ * 
+ * libs3 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, version 3 of the License.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of this library and its programs with the
+ * OpenSSL library, and distribute linked combinations including the two.
+ *
+ * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License version 3
+ * along with libs3, in a file named COPYING.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ ************************************************************************** **/
+
+#include <string.h>
+#include <stdlib.h>
+#include "libs3.h"
+#include "request.h"
+#include "simplexml.h"
+
+// test bucket ---------------------------------------------------------------
+
+typedef struct TestBucketData
+{
+    SimpleXml simpleXml;
+
+    S3ResponsePropertiesCallback *responsePropertiesCallback;
+    S3ResponseCompleteCallback *responseCompleteCallback;
+    void *callbackData;
+
+    int locationConstraintReturnSize;
+    char *locationConstraintReturn;
+
+    string_buffer(locationConstraint, 256);
+} TestBucketData;
+
+
+static S3Status testBucketXmlCallback(const char *elementPath,
+                                      const char *data, int dataLen,
+                                      void *callbackData)
+{
+    TestBucketData *tbData = (TestBucketData *) callbackData;
+
+    int fit;
+
+    if (data && !strcmp(elementPath, "LocationConstraint")) {
+        string_buffer_append(tbData->locationConstraint, data, dataLen, fit);
+    }
+
+    return S3StatusOK;
+}
+
+
+static S3Status testBucketPropertiesCallback
+    (const S3ResponseProperties *responseProperties, void *callbackData)
+{
+    TestBucketData *tbData = (TestBucketData *) callbackData;
+    
+    return (*(tbData->responsePropertiesCallback))
+        (responseProperties, tbData->callbackData);
+}
+
+
+static S3Status testBucketDataCallback(int bufferSize, const char *buffer,
+                                       void *callbackData)
+{
+    TestBucketData *tbData = (TestBucketData *) callbackData;
+
+    return simplexml_add(&(tbData->simpleXml), buffer, bufferSize);
+}
+
+
+static void testBucketCompleteCallback(S3Status requestStatus, 
+                                       const S3ErrorDetails *s3ErrorDetails,
+                                       void *callbackData)
+{
+    TestBucketData *tbData = (TestBucketData *) callbackData;
+
+    // Copy the location constraint into the return buffer
+    snprintf(tbData->locationConstraintReturn, 
+             tbData->locationConstraintReturnSize, "%s", 
+             tbData->locationConstraint);
+
+    (*(tbData->responseCompleteCallback))
+        (requestStatus, s3ErrorDetails, tbData->callbackData);
+
+    simplexml_deinitialize(&(tbData->simpleXml));
+
+    free(tbData);
+}
+
+
+void S3_test_bucket(S3Protocol protocol, S3UriStyle uriStyle,
+                    const char *accessKeyId, const char *secretAccessKey,
+                    const char *bucketName, int locationConstraintReturnSize,
+                    char *locationConstraintReturn,
+                    S3RequestContext *requestContext,
+                    const S3ResponseHandler *handler, void *callbackData)
+{
+    // Create the callback data
+    TestBucketData *tbData = 
+        (TestBucketData *) malloc(sizeof(TestBucketData));
+    if (!tbData) {
+        (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData);
+        return;
+    }
+
+    simplexml_initialize(&(tbData->simpleXml), &testBucketXmlCallback, tbData);
+
+    tbData->responsePropertiesCallback = handler->propertiesCallback;
+    tbData->responseCompleteCallback = handler->completeCallback;
+    tbData->callbackData = callbackData;
+
+    tbData->locationConstraintReturnSize = locationConstraintReturnSize;
+    tbData->locationConstraintReturn = locationConstraintReturn;
+    string_buffer_initialize(tbData->locationConstraint);
+
+    // Set up the RequestParams
+    RequestParams params =
+    {
+        HttpRequestTypeGET,                           // httpRequestType
+        { bucketName,                                 // bucketName
+          protocol,                                   // protocol
+          uriStyle,                                   // uriStyle
+          accessKeyId,                                // accessKeyId
+          secretAccessKey },                          // secretAccessKey
+        0,                                            // key
+        0,                                            // queryParams
+        "location",                                   // subResource
+        0,                                            // copySourceBucketName
+        0,                                            // copySourceKey
+        0,                                            // getConditions
+        0,                                            // startByte
+        0,                                            // byteCount
+        0,                                            // putProperties
+        &testBucketPropertiesCallback,                // propertiesCallback
+        0,                                            // toS3Callback
+        0,                                            // toS3CallbackTotalSize
+        &testBucketDataCallback,                      // fromS3Callback
+        &testBucketCompleteCallback,                  // completeCallback
+        tbData                                        // callbackData
+    };
+
+    // Perform the request
+    request_perform(&params, requestContext);
+}
+
+
+// create bucket -------------------------------------------------------------
+
+typedef struct CreateBucketData
+{
+    S3ResponsePropertiesCallback *responsePropertiesCallback;
+    S3ResponseCompleteCallback *responseCompleteCallback;
+    void *callbackData;
+
+    char doc[1024];
+    int docLen, docBytesWritten;
+} CreateBucketData;                         
+                            
+
+static S3Status createBucketPropertiesCallback
+    (const S3ResponseProperties *responseProperties, void *callbackData)
+{
+    CreateBucketData *cbData = (CreateBucketData *) callbackData;
+    
+    return (*(cbData->responsePropertiesCallback))
+        (responseProperties, cbData->callbackData);
+}
+
+
+static int createBucketDataCallback(int bufferSize, char *buffer, 
+                                    void *callbackData)
+{
+    CreateBucketData *cbData = (CreateBucketData *) callbackData;
+
+    if (!cbData->docLen) {
+        return 0;
+    }
+
+    int remaining = (cbData->docLen - cbData->docBytesWritten);
+
+    int toCopy = bufferSize > remaining ? remaining : bufferSize;
+    
+    if (!toCopy) {
+        return 0;
+    }
+
+    memcpy(buffer, &(cbData->doc[cbData->docBytesWritten]), toCopy);
+
+    cbData->docBytesWritten += toCopy;
+
+    return toCopy;
+}
+
+
+static void createBucketCompleteCallback(S3Status requestStatus, 
+                                         const S3ErrorDetails *s3ErrorDetails,
+                                         void *callbackData)
+{
+    CreateBucketData *cbData = (CreateBucketData *) callbackData;
+
+    (*(cbData->responseCompleteCallback))
+        (requestStatus, s3ErrorDetails, cbData->callbackData);
+
+    free(cbData);
+}
+
+
+void S3_create_bucket(S3Protocol protocol, const char *accessKeyId,
+                      const char *secretAccessKey, const char *bucketName,
+                      S3CannedAcl cannedAcl, const char *locationConstraint,
+                      S3RequestContext *requestContext,
+                      const S3ResponseHandler *handler, void *callbackData)
+{
+    // Create the callback data
+    CreateBucketData *cbData = 
+        (CreateBucketData *) malloc(sizeof(CreateBucketData));
+    if (!cbData) {
+        (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData);
+        return;
+    }
+
+    cbData->responsePropertiesCallback = handler->propertiesCallback;
+    cbData->responseCompleteCallback = handler->completeCallback;
+    cbData->callbackData = callbackData;
+
+    if (locationConstraint) {
+        cbData->docLen =
+            snprintf(cbData->doc, sizeof(cbData->doc),
+                     "<CreateBucketConfiguration><LocationConstraint>"
+                     "%s</LocationConstraint></CreateBucketConfiguration>",
+                     locationConstraint);
+        cbData->docBytesWritten = 0;
+    }
+    else {
+        cbData->docLen = 0;
+    }
+    
+    // Set up S3PutProperties
+    S3PutProperties properties =
+    {
+        0,                                       // contentType
+        0,                                       // md5
+        0,                                       // cacheControl
+        0,                                       // contentDispositionFilename
+        0,                                       // contentEncoding
+        0,                                       // expires
+        cannedAcl,                               // cannedAcl
+        0,                                       // metaDataCount
+        0                                        // metaData
+    };
+    
+    // Set up the RequestParams
+    RequestParams params =
+    {
+        HttpRequestTypePUT,                           // httpRequestType
+        { bucketName,                                 // bucketName
+          protocol,                                   // protocol
+          S3UriStylePath,                             // uriStyle
+          accessKeyId,                                // accessKeyId
+          secretAccessKey },                          // secretAccessKey
+        0,                                            // key
+        0,                                            // queryParams
+        0,                                            // subResource
+        0,                                            // copySourceBucketName
+        0,                                            // copySourceKey
+        0,                                            // getConditions
+        0,                                            // startByte
+        0,                                            // byteCount
+        &properties,                                  // putProperties
+        &createBucketPropertiesCallback,              // propertiesCallback
+        &createBucketDataCallback,                    // toS3Callback
+        cbData->docLen,                               // toS3CallbackTotalSize
+        0,                                            // fromS3Callback
+        &createBucketCompleteCallback,                // completeCallback
+        cbData                                        // callbackData
+    };
+
+    // Perform the request
+    request_perform(&params, requestContext);
+}
+
+                           
+// delete bucket -------------------------------------------------------------
+
+typedef struct DeleteBucketData
+{
+    S3ResponsePropertiesCallback *responsePropertiesCallback;
+    S3ResponseCompleteCallback *responseCompleteCallback;
+    void *callbackData;
+} DeleteBucketData;
+
+
+static S3Status deleteBucketPropertiesCallback
+    (const S3ResponseProperties *responseProperties, void *callbackData)
+{
+    DeleteBucketData *dbData = (DeleteBucketData *) callbackData;
+    
+    return (*(dbData->responsePropertiesCallback))
+        (responseProperties, dbData->callbackData);
+}
+
+
+static void deleteBucketCompleteCallback(S3Status requestStatus, 
+                                         const S3ErrorDetails *s3ErrorDetails,
+                                         void *callbackData)
+{
+    DeleteBucketData *dbData = (DeleteBucketData *) callbackData;
+
+    (*(dbData->responseCompleteCallback))
+        (requestStatus, s3ErrorDetails, dbData->callbackData);
+
+    free(dbData);
+}
+
+
+void S3_delete_bucket(S3Protocol protocol, S3UriStyle uriStyle,
+                      const char *accessKeyId, const char *secretAccessKey,
+                      const char *bucketName,
+                      S3RequestContext *requestContext,
+                      const S3ResponseHandler *handler, void *callbackData)
+{
+    // Create the callback data
+    DeleteBucketData *dbData = 
+        (DeleteBucketData *) malloc(sizeof(DeleteBucketData));
+    if (!dbData) {
+        (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData);
+        return;
+    }
+
+    dbData->responsePropertiesCallback = handler->propertiesCallback;
+    dbData->responseCompleteCallback = handler->completeCallback;
+    dbData->callbackData = callbackData;
+
+    // Set up the RequestParams
+    RequestParams params =
+    {
+        HttpRequestTypeDELETE,                        // httpRequestType
+        { bucketName,                                 // bucketName
+          protocol,                                   // protocol
+          uriStyle,                                   // uriStyle
+          accessKeyId,                                // accessKeyId
+          secretAccessKey },                          // secretAccessKey
+        0,                                            // key
+        0,                                            // queryParams
+        0,                                            // subResource
+        0,                                            // copySourceBucketName
+        0,                                            // copySourceKey
+        0,                                            // getConditions
+        0,                                            // startByte
+        0,                                            // byteCount
+        0,                                            // putProperties
+        &deleteBucketPropertiesCallback,              // propertiesCallback
+        0,                                            // toS3Callback
+        0,                                            // toS3CallbackTotalSize
+        0,                                            // fromS3Callback
+        &deleteBucketCompleteCallback,                // completeCallback
+        dbData                                        // callbackData
+    };
+
+    // Perform the request
+    request_perform(&params, requestContext);
+}
+
+
+// list bucket ----------------------------------------------------------------
+
+typedef struct ListBucketContents
+{
+    string_buffer(key, 1024);
+    string_buffer(lastModified, 256);
+    string_buffer(eTag, 256);
+    string_buffer(size, 24);
+    string_buffer(ownerId, 256);
+    string_buffer(ownerDisplayName, 256);
+} ListBucketContents;
+
+
+static void initialize_list_bucket_contents(ListBucketContents *contents)
+{
+    string_buffer_initialize(contents->key);
+    string_buffer_initialize(contents->lastModified);
+    string_buffer_initialize(contents->eTag);
+    string_buffer_initialize(contents->size);
+    string_buffer_initialize(contents->ownerId);
+    string_buffer_initialize(contents->ownerDisplayName);
+}
+
+// We read up to 32 Contents at a time
+#define MAX_CONTENTS 32
+// We read up to 8 CommonPrefixes at a time
+#define MAX_COMMON_PREFIXES 8
+
+typedef struct ListBucketData
+{
+    SimpleXml simpleXml;
+
+    S3ResponsePropertiesCallback *responsePropertiesCallback;
+    S3ListBucketCallback *listBucketCallback;
+    S3ResponseCompleteCallback *responseCompleteCallback;
+    void *callbackData;
+
+    string_buffer(isTruncated, 64);
+    string_buffer(nextMarker, 1024);
+
+    int contentsCount;
+    ListBucketContents contents[MAX_CONTENTS];
+
+    int commonPrefixesCount;
+    char commonPrefixes[MAX_COMMON_PREFIXES][1024];
+    int commonPrefixLens[MAX_COMMON_PREFIXES];
+} ListBucketData;
+
+
+static void initialize_list_bucket_data(ListBucketData *lbData)
+{
+    lbData->contentsCount = 0;
+    initialize_list_bucket_contents(lbData->contents);
+    lbData->commonPrefixesCount = 0;
+    lbData->commonPrefixes[0][0] = 0;
+    lbData->commonPrefixLens[0] = 0;
+}
+
+
+static S3Status make_list_bucket_callback(ListBucketData *lbData)
+{
+    int i;
+
+    // Convert IsTruncated
+    int isTruncated = (!strcmp(lbData->isTruncated, "true") ||
+                       !strcmp(lbData->isTruncated, "1")) ? 1 : 0;
+
+    // Convert the contents
+    S3ListBucketContent contents[lbData->contentsCount];
+
+    int contentsCount = lbData->contentsCount;
+    for (i = 0; i < contentsCount; i++) {
+        S3ListBucketContent *contentDest = &(contents[i]);
+        ListBucketContents *contentSrc = &(lbData->contents[i]);
+        contentDest->key = contentSrc->key;
+        contentDest->lastModified = 
+            parseIso8601Time(contentSrc->lastModified);
+        contentDest->eTag = contentSrc->eTag;
+        contentDest->size = parseUnsignedInt(contentSrc->size);
+        contentDest->ownerId =
+            contentSrc->ownerId[0] ?contentSrc->ownerId : 0;
+        contentDest->ownerDisplayName = (contentSrc->ownerDisplayName[0] ?
+                                         contentSrc->ownerDisplayName : 0);
+    }
+
+    // Make the common prefixes array
+    int commonPrefixesCount = lbData->commonPrefixesCount;
+    char *commonPrefixes[commonPrefixesCount];
+    for (i = 0; i < commonPrefixesCount; i++) {
+        commonPrefixes[i] = lbData->commonPrefixes[i];
+    }
+
+    return (*(lbData->listBucketCallback))
+        (isTruncated, lbData->nextMarker,
+         contentsCount, contents, commonPrefixesCount, 
+         (const char **) commonPrefixes, lbData->callbackData);
+}
+
+
+static S3Status listBucketXmlCallback(const char *elementPath,
+                                      const char *data, int dataLen,
+                                      void *callbackData)
+{
+    ListBucketData *lbData = (ListBucketData *) callbackData;
+
+    int fit;
+
+    if (data) {
+        if (!strcmp(elementPath, "ListBucketResult/IsTruncated")) {
+            string_buffer_append(lbData->isTruncated, data, dataLen, fit);
+        }
+        else if (!strcmp(elementPath, "ListBucketResult/NextMarker")) {
+            string_buffer_append(lbData->nextMarker, data, dataLen, fit);
+        }
+        else if (!strcmp(elementPath, "ListBucketResult/Contents/Key")) {
+            ListBucketContents *contents = 
+                &(lbData->contents[lbData->contentsCount]);
+            string_buffer_append(contents->key, data, dataLen, fit);
+        }
+        else if (!strcmp(elementPath, 
+                         "ListBucketResult/Contents/LastModified")) {
+            ListBucketContents *contents = 
+                &(lbData->contents[lbData->contentsCount]);
+            string_buffer_append(contents->lastModified, data, dataLen, fit);
+        }
+        else if (!strcmp(elementPath, "ListBucketResult/Contents/ETag")) {
+            ListBucketContents *contents = 
+                &(lbData->contents[lbData->contentsCount]);
+            string_buffer_append(contents->eTag, data, dataLen, fit);
+        }
+        else if (!strcmp(elementPath, "ListBucketResult/Contents/Size")) {
+            ListBucketContents *contents = 
+                &(lbData->contents[lbData->contentsCount]);
+            string_buffer_append(contents->size, data, dataLen, fit);
+        }
+        else if (!strcmp(elementPath, "ListBucketResult/Contents/Owner/ID")) {
+            ListBucketContents *contents = 
+                &(lbData->contents[lbData->contentsCount]);
+            string_buffer_append(contents->ownerId, data, dataLen, fit);
+        }
+        else if (!strcmp(elementPath, 
+                         "ListBucketResult/Contents/Owner/DisplayName")) {
+            ListBucketContents *contents = 
+                &(lbData->contents[lbData->contentsCount]);
+            string_buffer_append
+                (contents->ownerDisplayName, data, dataLen, fit);
+        }
+        else if (!strcmp(elementPath, 
+                         "ListBucketResult/CommonPrefixes/Prefix")) {
+            int which = lbData->commonPrefixesCount;
+            lbData->commonPrefixLens[which] +=
+                snprintf(lbData->commonPrefixes[which],
+                         sizeof(lbData->commonPrefixes[which]) -
+                         lbData->commonPrefixLens[which] - 1,
+                         "%.*s", dataLen, data);
+            if (lbData->commonPrefixLens[which] >=
+                (int) sizeof(lbData->commonPrefixes[which])) {
+                return S3StatusXmlParseFailure;
+            }
+        }
+    }
+    else {
+        if (!strcmp(elementPath, "ListBucketResult/Contents")) {
+            // Finished a Contents
+            lbData->contentsCount++;
+            if (lbData->contentsCount == MAX_CONTENTS) {
+                // Make the callback
+                S3Status status = make_list_bucket_callback(lbData);
+                if (status != S3StatusOK) {
+                    return status;
+                }
+                initialize_list_bucket_data(lbData);
+            }
+            else {
+                // Initialize the next one
+                initialize_list_bucket_contents
+                    (&(lbData->contents[lbData->contentsCount]));
+            }
+        }
+        else if (!strcmp(elementPath,
+                         "ListBucketResult/CommonPrefixes/Prefix")) {
+            // Finished a Prefix
+            lbData->commonPrefixesCount++;
+            if (lbData->commonPrefixesCount == MAX_COMMON_PREFIXES) {
+                // Make the callback
+                S3Status status = make_list_bucket_callback(lbData);
+                if (status != S3StatusOK) {
+                    return status;
+                }
+                initialize_list_bucket_data(lbData);
+            }
+            else {
+                // Initialize the next one
+                lbData->commonPrefixes[lbData->commonPrefixesCount][0] = 0;
+                lbData->commonPrefixLens[lbData->commonPrefixesCount] = 0;
+            }
+        }
+    }
+
+    return S3StatusOK;
+}
+
+
+static S3Status listBucketPropertiesCallback
+    (const S3ResponseProperties *responseProperties, void *callbackData)
+{
+    ListBucketData *lbData = (ListBucketData *) callbackData;
+    
+    return (*(lbData->responsePropertiesCallback))
+        (responseProperties, lbData->callbackData);
+}
+
+
+static S3Status listBucketDataCallback(int bufferSize, const char *buffer, 
+                                       void *callbackData)
+{
+    ListBucketData *lbData = (ListBucketData *) callbackData;
+    
+    return simplexml_add(&(lbData->simpleXml), buffer, bufferSize);
+}
+
+
+static void listBucketCompleteCallback(S3Status requestStatus, 
+                                       const S3ErrorDetails *s3ErrorDetails,
+                                       void *callbackData)
+{
+    ListBucketData *lbData = (ListBucketData *) callbackData;
+
+    // Make the callback if there is anything
+    if (lbData->contentsCount || lbData->commonPrefixesCount) {
+        make_list_bucket_callback(lbData);
+    }
+
+    (*(lbData->responseCompleteCallback))
+        (requestStatus, s3ErrorDetails, lbData->callbackData);
+
+    simplexml_deinitialize(&(lbData->simpleXml));
+
+    free(lbData);
+}
+
+
+void S3_list_bucket(const S3BucketContext *bucketContext, const char *prefix,
+                    const char *marker, const char *delimiter, int maxkeys,
+                    S3RequestContext *requestContext,
+                    const S3ListBucketHandler *handler, void *callbackData)
+{
+    // Compose the query params
+    string_buffer(queryParams, 4096);
+    string_buffer_initialize(queryParams);
+    
+#define safe_append(name, value)                                        \
+    do {                                                                \
+        int fit;                                                        \
+        if (amp) {                                                      \
+            string_buffer_append(queryParams, "&", 1, fit);             \
+            if (!fit) {                                                 \
+                (*(handler->responseHandler.completeCallback))          \
+                    (S3StatusQueryParamsTooLong, 0, callbackData);      \
+                return;                                                 \
+            }                                                           \
+        }                                                               \
+        string_buffer_append(queryParams, name "=",                     \
+                             sizeof(name "=") - 1, fit);                \
+        if (!fit) {                                                     \
+            (*(handler->responseHandler.completeCallback))              \
+                (S3StatusQueryParamsTooLong, 0, callbackData);          \
+            return;                                                     \
+        }                                                               \
+        amp = 1;                                                        \
+        char encoded[3 * 1024];                                         \
+        if (!urlEncode(encoded, value, 1024)) {                         \
+            (*(handler->responseHandler.completeCallback))              \
+                (S3StatusQueryParamsTooLong, 0, callbackData);          \
+            return;                                                     \
+        }                                                               \
+        string_buffer_append(queryParams, encoded, strlen(encoded),     \
+                             fit);                                      \
+        if (!fit) {                                                     \
+            (*(handler->responseHandler.completeCallback))              \
+                (S3StatusQueryParamsTooLong, 0, callbackData);          \
+            return;                                                     \
+        }                                                               \
+    } while (0)
+
+
+    int amp = 0;
+    if (prefix) {
+        safe_append("prefix", prefix);
+    }
+    if (marker) {
+        safe_append("marker", marker);
+    }
+    if (delimiter) {
+        safe_append("delimiter", delimiter);
+    }
+    if (maxkeys) {
+        char maxKeysString[64];
+        snprintf(maxKeysString, sizeof(maxKeysString), "%d", maxkeys);
+        safe_append("max-keys", maxKeysString);
+    }
+
+    ListBucketData *lbData =
+        (ListBucketData *) malloc(sizeof(ListBucketData));
+
+    if (!lbData) {
+        (*(handler->responseHandler.completeCallback))
+            (S3StatusOutOfMemory, 0, callbackData);
+        return;
+    }
+
+    simplexml_initialize(&(lbData->simpleXml), &listBucketXmlCallback, lbData);
+    
+    lbData->responsePropertiesCallback = 
+        handler->responseHandler.propertiesCallback;
+    lbData->listBucketCallback = handler->listBucketCallback;
+    lbData->responseCompleteCallback = 
+        handler->responseHandler.completeCallback;
+    lbData->callbackData = callbackData;
+
+    string_buffer_initialize(lbData->isTruncated);
+    string_buffer_initialize(lbData->nextMarker);
+    initialize_list_bucket_data(lbData);
+
+    // Set up the RequestParams
+    RequestParams params =
+    {
+        HttpRequestTypeGET,                           // httpRequestType
+        { bucketContext->bucketName,                  // bucketName
+          bucketContext->protocol,                    // protocol
+          bucketContext->uriStyle,                    // uriStyle
+          bucketContext->accessKeyId,                 // accessKeyId
+          bucketContext->secretAccessKey },           // secretAccessKey
+        0,                                            // key
+        queryParams[0] ? queryParams : 0,             // queryParams
+        0,                                            // subResource
+        0,                                            // copySourceBucketName
+        0,                                            // copySourceKey
+        0,                                            // getConditions
+        0,                                            // startByte
+        0,                                            // byteCount
+        0,                                            // putProperties
+        &listBucketPropertiesCallback,                // propertiesCallback
+        0,                                            // toS3Callback
+        0,                                            // toS3CallbackTotalSize
+        &listBucketDataCallback,                      // fromS3Callback
+        &listBucketCompleteCallback,                  // completeCallback
+        lbData                                        // callbackData
+    };
+
+    // Perform the request
+    request_perform(&params, requestContext);
+}