Include libs3 sources in the BlueSky tree.
[bluesky.git] / libs3-1.4 / src / service_access_logging.c
diff --git a/libs3-1.4/src/service_access_logging.c b/libs3-1.4/src/service_access_logging.c
new file mode 100644 (file)
index 0000000..cbed2c1
--- /dev/null
@@ -0,0 +1,556 @@
+/** **************************************************************************
+ * server_access_logging.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 <stdlib.h>
+#include <string.h>
+#include "libs3.h"
+#include "request.h"
+
+
+// get server access logging---------------------------------------------------
+
+typedef struct ConvertBlsData
+{
+    char *targetBucketReturn;
+    int targetBucketReturnLen;
+    char *targetPrefixReturn;
+    int targetPrefixReturnLen;
+    int *aclGrantCountReturn;
+    S3AclGrant *aclGrants;
+
+    string_buffer(emailAddress, S3_MAX_GRANTEE_EMAIL_ADDRESS_SIZE);
+    string_buffer(userId, S3_MAX_GRANTEE_USER_ID_SIZE);
+    string_buffer(userDisplayName, S3_MAX_GRANTEE_DISPLAY_NAME_SIZE);
+    string_buffer(groupUri, 128);
+    string_buffer(permission, 32);
+} ConvertBlsData;
+
+
+static S3Status convertBlsXmlCallback(const char *elementPath,
+                                      const char *data, int dataLen,
+                                      void *callbackData)
+{
+    ConvertBlsData *caData = (ConvertBlsData *) callbackData;
+
+    int fit;
+
+    if (data) {
+        if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/"
+                    "TargetBucket")) {
+            caData->targetBucketReturnLen += 
+                snprintf(&(caData->targetBucketReturn
+                           [caData->targetBucketReturnLen]),
+                         255 - caData->targetBucketReturnLen - 1, 
+                         "%.*s", dataLen, data);
+            if (caData->targetBucketReturnLen >= 255) {
+                return S3StatusTargetBucketTooLong;
+            }
+        }
+        else if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/"
+                    "TargetPrefix")) {
+            caData->targetPrefixReturnLen += 
+                snprintf(&(caData->targetPrefixReturn
+                           [caData->targetPrefixReturnLen]),
+                         255 - caData->targetPrefixReturnLen - 1, 
+                         "%.*s", dataLen, data);
+            if (caData->targetPrefixReturnLen >= 255) {
+                return S3StatusTargetPrefixTooLong;
+            }
+        }
+        else if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/"
+                         "TargetGrants/Grant/Grantee/EmailAddress")) {
+            // AmazonCustomerByEmail
+            string_buffer_append(caData->emailAddress, data, dataLen, fit);
+            if (!fit) {
+                return S3StatusEmailAddressTooLong;
+            }
+        }
+        else if (!strcmp(elementPath,
+                         "AccessControlPolicy/AccessControlList/Grant/"
+                         "Grantee/ID")) {
+            // CanonicalUser
+            string_buffer_append(caData->userId, data, dataLen, fit);
+            if (!fit) {
+                return S3StatusUserIdTooLong;
+            }
+        }
+        else if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/"
+                         "TargetGrants/Grant/Grantee/DisplayName")) {
+            // CanonicalUser
+            string_buffer_append(caData->userDisplayName, data, dataLen, fit);
+            if (!fit) {
+                return S3StatusUserDisplayNameTooLong;
+            }
+        }
+        else if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/"
+                         "TargetGrants/Grant/Grantee/URI")) {
+            // Group
+            string_buffer_append(caData->groupUri, data, dataLen, fit);
+            if (!fit) {
+                return S3StatusGroupUriTooLong;
+            }
+        }
+        else if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/"
+                         "TargetGrants/Grant/Permission")) {
+            // Permission
+            string_buffer_append(caData->permission, data, dataLen, fit);
+            if (!fit) {
+                return S3StatusPermissionTooLong;
+            }
+        }
+    }
+    else {
+        if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/"
+                    "TargetGrants/Grant")) {
+            // A grant has just been completed; so add the next S3AclGrant
+            // based on the values read
+            if (*(caData->aclGrantCountReturn) == S3_MAX_ACL_GRANT_COUNT) {
+                return S3StatusTooManyGrants;
+            }
+
+            S3AclGrant *grant = &(caData->aclGrants
+                                  [*(caData->aclGrantCountReturn)]);
+
+            if (caData->emailAddress[0]) {
+                grant->granteeType = S3GranteeTypeAmazonCustomerByEmail;
+                strcpy(grant->grantee.amazonCustomerByEmail.emailAddress,
+                       caData->emailAddress);
+            }
+            else if (caData->userId[0] && caData->userDisplayName[0]) {
+                grant->granteeType = S3GranteeTypeCanonicalUser;
+                strcpy(grant->grantee.canonicalUser.id, caData->userId);
+                strcpy(grant->grantee.canonicalUser.displayName, 
+                       caData->userDisplayName);
+            }
+            else if (caData->groupUri[0]) {
+                if (!strcmp(caData->groupUri,
+                            "http://acs.amazonaws.com/groups/global/"
+                            "AuthenticatedUsers")) {
+                    grant->granteeType = S3GranteeTypeAllAwsUsers;
+                }
+                else if (!strcmp(caData->groupUri,
+                                 "http://acs.amazonaws.com/groups/global/"
+                                 "AllUsers")) {
+                    grant->granteeType = S3GranteeTypeAllUsers;
+                }
+                else {
+                    return S3StatusBadGrantee;
+                }
+            }
+            else {
+                return S3StatusBadGrantee;
+            }
+
+            if (!strcmp(caData->permission, "READ")) {
+                grant->permission = S3PermissionRead;
+            }
+            else if (!strcmp(caData->permission, "WRITE")) {
+                grant->permission = S3PermissionWrite;
+            }
+            else if (!strcmp(caData->permission, "READ_ACP")) {
+                grant->permission = S3PermissionReadACP;
+            }
+            else if (!strcmp(caData->permission, "WRITE_ACP")) {
+                grant->permission = S3PermissionWriteACP;
+            }
+            else if (!strcmp(caData->permission, "FULL_CONTROL")) {
+                grant->permission = S3PermissionFullControl;
+            }
+            else {
+                return S3StatusBadPermission;
+            }
+
+            (*(caData->aclGrantCountReturn))++;
+
+            string_buffer_initialize(caData->emailAddress);
+            string_buffer_initialize(caData->userId);
+            string_buffer_initialize(caData->userDisplayName);
+            string_buffer_initialize(caData->groupUri);
+            string_buffer_initialize(caData->permission);
+        }
+    }
+
+    return S3StatusOK;
+}
+
+
+static S3Status convert_bls(char *blsXml, char *targetBucketReturn,
+                            char *targetPrefixReturn, int *aclGrantCountReturn,
+                            S3AclGrant *aclGrants)
+{
+    ConvertBlsData data;
+
+    data.targetBucketReturn = targetBucketReturn;
+    data.targetBucketReturn[0] = 0;
+    data.targetBucketReturnLen = 0;
+    data.targetPrefixReturn = targetPrefixReturn;
+    data.targetPrefixReturn[0] = 0;
+    data.targetPrefixReturnLen = 0;
+    data.aclGrantCountReturn = aclGrantCountReturn;
+    data.aclGrants = aclGrants;
+    *aclGrantCountReturn = 0;
+    string_buffer_initialize(data.emailAddress);
+    string_buffer_initialize(data.userId);
+    string_buffer_initialize(data.userDisplayName);
+    string_buffer_initialize(data.groupUri);
+    string_buffer_initialize(data.permission);
+
+    // Use a simplexml parser
+    SimpleXml simpleXml;
+    simplexml_initialize(&simpleXml, &convertBlsXmlCallback, &data);
+
+    S3Status status = simplexml_add(&simpleXml, blsXml, strlen(blsXml));
+
+    simplexml_deinitialize(&simpleXml);
+                                          
+    return status;
+}
+
+
+// Use a rather arbitrary max size for the document of 64K
+#define BLS_XML_DOC_MAXSIZE (64 * 1024)
+
+
+typedef struct GetBlsData
+{
+    SimpleXml simpleXml;
+
+    S3ResponsePropertiesCallback *responsePropertiesCallback;
+    S3ResponseCompleteCallback *responseCompleteCallback;
+    void *callbackData;
+
+    char *targetBucketReturn;
+    char *targetPrefixReturn;
+    int *aclGrantCountReturn;
+    S3AclGrant *aclGrants;
+    string_buffer(blsXmlDocument, BLS_XML_DOC_MAXSIZE);
+} GetBlsData;
+
+
+static S3Status getBlsPropertiesCallback
+    (const S3ResponseProperties *responseProperties, void *callbackData)
+{
+    GetBlsData *gsData = (GetBlsData *) callbackData;
+    
+    return (*(gsData->responsePropertiesCallback))
+        (responseProperties, gsData->callbackData);
+}
+
+
+static S3Status getBlsDataCallback(int bufferSize, const char *buffer,
+                                   void *callbackData)
+{
+    GetBlsData *gsData = (GetBlsData *) callbackData;
+
+    int fit;
+
+    string_buffer_append(gsData->blsXmlDocument, buffer, bufferSize, fit);
+    
+    return fit ? S3StatusOK : S3StatusXmlDocumentTooLarge;
+}
+
+
+static void getBlsCompleteCallback(S3Status requestStatus, 
+                                   const S3ErrorDetails *s3ErrorDetails,
+                                   void *callbackData)
+{
+    GetBlsData *gsData = (GetBlsData *) callbackData;
+
+    if (requestStatus == S3StatusOK) {
+        // Parse the document
+        requestStatus = convert_bls
+            (gsData->blsXmlDocument, gsData->targetBucketReturn,
+             gsData->targetPrefixReturn, gsData->aclGrantCountReturn, 
+             gsData->aclGrants);
+    }
+
+    (*(gsData->responseCompleteCallback))
+        (requestStatus, s3ErrorDetails, gsData->callbackData);
+
+    free(gsData);
+}
+
+
+void S3_get_server_access_logging(const S3BucketContext *bucketContext,
+                                  char *targetBucketReturn,
+                                  char *targetPrefixReturn,
+                                  int *aclGrantCountReturn, 
+                                  S3AclGrant *aclGrants,
+                                  S3RequestContext *requestContext,
+                                  const S3ResponseHandler *handler,
+                                  void *callbackData)
+{
+    // Create the callback data
+    GetBlsData *gsData = (GetBlsData *) malloc(sizeof(GetBlsData));
+    if (!gsData) {
+        (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData);
+        return;
+    }
+
+    gsData->responsePropertiesCallback = handler->propertiesCallback;
+    gsData->responseCompleteCallback = handler->completeCallback;
+    gsData->callbackData = callbackData;
+
+    gsData->targetBucketReturn = targetBucketReturn;
+    gsData->targetPrefixReturn = targetPrefixReturn;
+    gsData->aclGrantCountReturn = aclGrantCountReturn;
+    gsData->aclGrants = aclGrants;
+    string_buffer_initialize(gsData->blsXmlDocument);
+    *aclGrantCountReturn = 0;
+
+    // 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
+        0,                                            // queryParams
+        "logging",                                    // subResource
+        0,                                            // copySourceBucketName
+        0,                                            // copySourceKey
+        0,                                            // getConditions
+        0,                                            // startByte
+        0,                                            // byteCount
+        0,                                            // putProperties
+        &getBlsPropertiesCallback,                    // propertiesCallback
+        0,                                            // toS3Callback
+        0,                                            // toS3CallbackTotalSize
+        &getBlsDataCallback,                          // fromS3Callback
+        &getBlsCompleteCallback,                      // completeCallback
+        gsData                                        // callbackData
+    };
+
+    // Perform the request
+    request_perform(&params, requestContext);
+}
+
+
+
+// set server access logging---------------------------------------------------
+
+static S3Status generateSalXmlDocument(const char *targetBucket,
+                                       const char *targetPrefix,
+                                       int aclGrantCount, 
+                                       const S3AclGrant *aclGrants,
+                                       int *xmlDocumentLenReturn,
+                                       char *xmlDocument,
+                                       int xmlDocumentBufferSize)
+{
+    *xmlDocumentLenReturn = 0;
+
+#define append(fmt, ...)                                        \
+    do {                                                        \
+        *xmlDocumentLenReturn += snprintf                       \
+            (&(xmlDocument[*xmlDocumentLenReturn]),             \
+             xmlDocumentBufferSize - *xmlDocumentLenReturn - 1, \
+             fmt, __VA_ARGS__);                                 \
+        if (*xmlDocumentLenReturn >= xmlDocumentBufferSize) {   \
+            return S3StatusXmlDocumentTooLarge;                 \
+        } \
+    } while (0)
+
+    append("%s", "<BucketLoggingStatus "
+           "xmlns=\"http://doc.s3.amazonaws.com/2006-03-01\">");
+
+    if (targetBucket && targetBucket[0]) {
+        append("<LoggingEnabled><TargetBucket>%s</TargetBucket>", targetBucket);
+        append("<TargetPrefix>%s</TargetPrefix>", 
+               targetPrefix ? targetPrefix : "");
+
+        if (aclGrantCount) {
+            append("%s", "<TargetGrants>");
+            int i;
+            for (i = 0; i < aclGrantCount; i++) {
+                append("%s", "<Grant><Grantee "
+                       "xmlns:xsi=\"http://www.w3.org/2001/"
+                       "XMLSchema-instance\" xsi:type=\"");
+                const S3AclGrant *grant = &(aclGrants[i]);
+                switch (grant->granteeType) {
+                case S3GranteeTypeAmazonCustomerByEmail:
+                    append("AmazonCustomerByEmail\"><EmailAddress>%s"
+                           "</EmailAddress>",
+                           grant->grantee.amazonCustomerByEmail.emailAddress);
+                    break;
+                case S3GranteeTypeCanonicalUser:
+                    append("CanonicalUser\"><ID>%s</ID><DisplayName>%s"
+                           "</DisplayName>",
+                           grant->grantee.canonicalUser.id, 
+                           grant->grantee.canonicalUser.displayName);
+                    break;
+                default: // case S3GranteeTypeAllAwsUsers/S3GranteeTypeAllUsers:
+                    append("Group\"><URI>http://acs.amazonaws.com/groups/"
+                           "global/%s</URI>",
+                           (grant->granteeType == S3GranteeTypeAllAwsUsers) ?
+                           "AuthenticatedUsers" : "AllUsers");
+                    break;
+                }
+                append("</Grantee><Permission>%s</Permission></Grant>",
+                       ((grant->permission == S3PermissionRead) ? "READ" :
+                        (grant->permission == S3PermissionWrite) ? "WRITE" :
+                        (grant->permission == 
+                         S3PermissionReadACP) ? "READ_ACP" :
+                        (grant->permission == 
+                         S3PermissionWriteACP) ? "WRITE_ACP" : "FULL_CONTROL"));
+            }
+            append("%s", "</TargetGrants>");
+        }
+        append("%s", "</LoggingEnabled>");
+    }
+
+    append("%s", "</BucketLoggingStatus>");
+
+    return S3StatusOK;
+}
+
+
+typedef struct SetSalData
+{
+    S3ResponsePropertiesCallback *responsePropertiesCallback;
+    S3ResponseCompleteCallback *responseCompleteCallback;
+    void *callbackData;
+
+    int salXmlDocumentLen;
+    char salXmlDocument[BLS_XML_DOC_MAXSIZE];
+    int salXmlDocumentBytesWritten;
+
+} SetSalData;
+
+
+static S3Status setSalPropertiesCallback
+    (const S3ResponseProperties *responseProperties, void *callbackData)
+{
+    SetSalData *paData = (SetSalData *) callbackData;
+    
+    return (*(paData->responsePropertiesCallback))
+        (responseProperties, paData->callbackData);
+}
+
+
+static int setSalDataCallback(int bufferSize, char *buffer, void *callbackData)
+{
+    SetSalData *paData = (SetSalData *) callbackData;
+
+    int remaining = (paData->salXmlDocumentLen - 
+                     paData->salXmlDocumentBytesWritten);
+
+    int toCopy = bufferSize > remaining ? remaining : bufferSize;
+    
+    if (!toCopy) {
+        return 0;
+    }
+
+    memcpy(buffer, &(paData->salXmlDocument
+                     [paData->salXmlDocumentBytesWritten]), toCopy);
+
+    paData->salXmlDocumentBytesWritten += toCopy;
+
+    return toCopy;
+}
+
+
+static void setSalCompleteCallback(S3Status requestStatus, 
+                                   const S3ErrorDetails *s3ErrorDetails,
+                                   void *callbackData)
+{
+    SetSalData *paData = (SetSalData *) callbackData;
+
+    (*(paData->responseCompleteCallback))
+        (requestStatus, s3ErrorDetails, paData->callbackData);
+
+    free(paData);
+}
+
+
+void S3_set_server_access_logging(const S3BucketContext *bucketContext,
+                                  const char *targetBucket, 
+                                  const char *targetPrefix, int aclGrantCount, 
+                                  const S3AclGrant *aclGrants, 
+                                  S3RequestContext *requestContext,
+                                  const S3ResponseHandler *handler,
+                                  void *callbackData)
+{
+    if (aclGrantCount > S3_MAX_ACL_GRANT_COUNT) {
+        (*(handler->completeCallback))
+            (S3StatusTooManyGrants, 0, callbackData);
+        return;
+    }
+
+    SetSalData *data = (SetSalData *) malloc(sizeof(SetSalData));
+    if (!data) {
+        (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData);
+        return;
+    }
+    
+    // Convert aclGrants to XML document
+    S3Status status = generateSalXmlDocument
+        (targetBucket, targetPrefix, aclGrantCount, aclGrants,
+         &(data->salXmlDocumentLen), data->salXmlDocument, 
+         sizeof(data->salXmlDocument));
+    if (status != S3StatusOK) {
+        free(data);
+        (*(handler->completeCallback))(status, 0, callbackData);
+        return;
+    }
+
+    data->responsePropertiesCallback = handler->propertiesCallback;
+    data->responseCompleteCallback = handler->completeCallback;
+    data->callbackData = callbackData;
+
+    data->salXmlDocumentBytesWritten = 0;
+
+    // Set up the RequestParams
+    RequestParams params =
+    {
+        HttpRequestTypePUT,                           // httpRequestType
+        { bucketContext->bucketName,                  // bucketName
+          bucketContext->protocol,                    // protocol
+          bucketContext->uriStyle,                    // uriStyle
+          bucketContext->accessKeyId,                 // accessKeyId
+          bucketContext->secretAccessKey },           // secretAccessKey
+        0,                                            // key
+        0,                                            // queryParams
+        "logging",                                    // subResource
+        0,                                            // copySourceBucketName
+        0,                                            // copySourceKey
+        0,                                            // getConditions
+        0,                                            // startByte
+        0,                                            // byteCount
+        0,                                            // putProperties
+        &setSalPropertiesCallback,                    // propertiesCallback
+        &setSalDataCallback,                          // toS3Callback
+        data->salXmlDocumentLen,                      // toS3CallbackTotalSize
+        0,                                            // fromS3Callback
+        &setSalCompleteCallback,                      // completeCallback
+        data                                          // callbackData
+    };
+
+    // Perform the request
+    request_perform(&params, requestContext);
+}