diff options
author | Bryan Ischo <bryan@ischo.com> | 2008-12-30 12:02:46 +0000 |
---|---|---|
committer | Bryan Ischo <bryan@ischo.com> | 2008-12-30 12:02:46 +0000 |
commit | 37d90995389c2aed1d37b7b3aa6215759e14b94a (patch) | |
tree | 45a0121b98f6843da1edd61579049825988592cb /src | |
parent | ff546d9907178b66d844772840f763e34eca3bf8 (diff) | |
download | ceph-libs3-37d90995389c2aed1d37b7b3aa6215759e14b94a.tar.gz |
* tabify source, to fix the broken .emacs configuration I have been using
since forever (finally realized it was broken and fixed it today!)
Diffstat (limited to 'src')
-rw-r--r-- | src/acl.c | 482 | ||||
-rw-r--r-- | src/bucket.c | 1074 | ||||
-rw-r--r-- | src/error_parser.c | 362 | ||||
-rw-r--r-- | src/general.c | 804 | ||||
-rw-r--r-- | src/mingw_functions.c | 114 | ||||
-rw-r--r-- | src/mingw_s3_functions.c | 6 | ||||
-rw-r--r-- | src/object.c | 482 | ||||
-rw-r--r-- | src/request.c | 2278 | ||||
-rw-r--r-- | src/request_context.c | 260 | ||||
-rw-r--r-- | src/response_headers_handler.c | 322 | ||||
-rw-r--r-- | src/s3.c | 4642 | ||||
-rw-r--r-- | src/service.c | 240 | ||||
-rw-r--r-- | src/service_access_logging.c | 862 | ||||
-rw-r--r-- | src/simplexml.c | 218 | ||||
-rw-r--r-- | src/testsimplexml.c | 76 | ||||
-rw-r--r-- | src/util.c | 790 |
16 files changed, 6506 insertions, 6506 deletions
@@ -37,313 +37,313 @@ typedef struct GetAclData { - SimpleXml simpleXml; + SimpleXml simpleXml; - S3ResponsePropertiesCallback *responsePropertiesCallback; - S3ResponseCompleteCallback *responseCompleteCallback; - void *callbackData; + S3ResponsePropertiesCallback *responsePropertiesCallback; + S3ResponseCompleteCallback *responseCompleteCallback; + void *callbackData; - int *aclGrantCountReturn; - S3AclGrant *aclGrants; - char *ownerId; - char *ownerDisplayName; - string_buffer(aclXmlDocument, ACL_XML_DOC_MAXSIZE); + int *aclGrantCountReturn; + S3AclGrant *aclGrants; + char *ownerId; + char *ownerDisplayName; + string_buffer(aclXmlDocument, ACL_XML_DOC_MAXSIZE); } GetAclData; static S3Status getAclPropertiesCallback - (const S3ResponseProperties *responseProperties, void *callbackData) + (const S3ResponseProperties *responseProperties, void *callbackData) { - GetAclData *gaData = (GetAclData *) callbackData; - - return (*(gaData->responsePropertiesCallback)) - (responseProperties, gaData->callbackData); + GetAclData *gaData = (GetAclData *) callbackData; + + return (*(gaData->responsePropertiesCallback)) + (responseProperties, gaData->callbackData); } static S3Status getAclDataCallback(int bufferSize, const char *buffer, - void *callbackData) + void *callbackData) { - GetAclData *gaData = (GetAclData *) callbackData; + GetAclData *gaData = (GetAclData *) callbackData; - int fit; + int fit; - string_buffer_append(gaData->aclXmlDocument, buffer, bufferSize, fit); - - return fit ? S3StatusOK : S3StatusXmlDocumentTooLarge; + string_buffer_append(gaData->aclXmlDocument, buffer, bufferSize, fit); + + return fit ? S3StatusOK : S3StatusXmlDocumentTooLarge; } static void getAclCompleteCallback(S3Status requestStatus, - const S3ErrorDetails *s3ErrorDetails, - void *callbackData) + const S3ErrorDetails *s3ErrorDetails, + void *callbackData) { - GetAclData *gaData = (GetAclData *) callbackData; + GetAclData *gaData = (GetAclData *) callbackData; - if (requestStatus == S3StatusOK) { - // Parse the document - requestStatus = S3_convert_acl - (gaData->aclXmlDocument, gaData->ownerId, gaData->ownerDisplayName, - gaData->aclGrantCountReturn, gaData->aclGrants); - } + if (requestStatus == S3StatusOK) { + // Parse the document + requestStatus = S3_convert_acl + (gaData->aclXmlDocument, gaData->ownerId, gaData->ownerDisplayName, + gaData->aclGrantCountReturn, gaData->aclGrants); + } - (*(gaData->responseCompleteCallback)) - (requestStatus, s3ErrorDetails, gaData->callbackData); + (*(gaData->responseCompleteCallback)) + (requestStatus, s3ErrorDetails, gaData->callbackData); - free(gaData); + free(gaData); } void S3_get_acl(const S3BucketContext *bucketContext, const char *key, - char *ownerId, char *ownerDisplayName, - int *aclGrantCountReturn, S3AclGrant *aclGrants, - S3RequestContext *requestContext, - const S3ResponseHandler *handler, void *callbackData) + char *ownerId, char *ownerDisplayName, + int *aclGrantCountReturn, S3AclGrant *aclGrants, + S3RequestContext *requestContext, + const S3ResponseHandler *handler, void *callbackData) { - // Create the callback data - GetAclData *gaData = (GetAclData *) malloc(sizeof(GetAclData)); - if (!gaData) { - (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData); - return; - } - - gaData->responsePropertiesCallback = handler->propertiesCallback; - gaData->responseCompleteCallback = handler->completeCallback; - gaData->callbackData = callbackData; - - gaData->aclGrantCountReturn = aclGrantCountReturn; - gaData->aclGrants = aclGrants; - gaData->ownerId = ownerId; - gaData->ownerDisplayName = ownerDisplayName; - string_buffer_initialize(gaData->aclXmlDocument); - *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 - key, // key - 0, // queryParams - "acl", // subResource - 0, // copySourceBucketName - 0, // copySourceKey - 0, // getConditions - 0, // startByte - 0, // byteCount - 0, // putProperties - &getAclPropertiesCallback, // propertiesCallback - 0, // toS3Callback - 0, // toS3CallbackTotalSize - &getAclDataCallback, // fromS3Callback - &getAclCompleteCallback, // completeCallback - gaData // callbackData - }; - - // Perform the request - request_perform(¶ms, requestContext); + // Create the callback data + GetAclData *gaData = (GetAclData *) malloc(sizeof(GetAclData)); + if (!gaData) { + (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData); + return; + } + + gaData->responsePropertiesCallback = handler->propertiesCallback; + gaData->responseCompleteCallback = handler->completeCallback; + gaData->callbackData = callbackData; + + gaData->aclGrantCountReturn = aclGrantCountReturn; + gaData->aclGrants = aclGrants; + gaData->ownerId = ownerId; + gaData->ownerDisplayName = ownerDisplayName; + string_buffer_initialize(gaData->aclXmlDocument); + *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 + key, // key + 0, // queryParams + "acl", // subResource + 0, // copySourceBucketName + 0, // copySourceKey + 0, // getConditions + 0, // startByte + 0, // byteCount + 0, // putProperties + &getAclPropertiesCallback, // propertiesCallback + 0, // toS3Callback + 0, // toS3CallbackTotalSize + &getAclDataCallback, // fromS3Callback + &getAclCompleteCallback, // completeCallback + gaData // callbackData + }; + + // Perform the request + request_perform(¶ms, requestContext); } // set acl ------------------------------------------------------------------- static S3Status generateAclXmlDocument(const char *ownerId, - const char *ownerDisplayName, - int aclGrantCount, - const S3AclGrant *aclGrants, - int *xmlDocumentLenReturn, - char *xmlDocument, - int xmlDocumentBufferSize) + const char *ownerDisplayName, + 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("<AccessControlPolicy><Owner><ID>%s</ID><DisplayName>%s" - "</DisplayName></Owner><AccessControlList>", ownerId, - ownerDisplayName); - - 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: - const char *grantee; - switch (grant->granteeType) { - case S3GranteeTypeAllAwsUsers: - grantee = "http://acs.amazonaws.com/groups/global/" - "AuthenticatedUsers"; - break; - case S3GranteeTypeAllUsers: - grantee = "http://acs.amazonaws.com/groups/global/" - "AllUsers"; - break; - default: - grantee = "http://acs.amazonaws.com/groups/s3/" - "LogDelivery"; - break; - } - append("Group\"><URI>%s</URI>", grantee); - } - 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", "</AccessControlList></AccessControlPolicy>"); - - return S3StatusOK; + *xmlDocumentLenReturn = 0; + +#define append(fmt, ...) \ + do { \ + *xmlDocumentLenReturn += snprintf \ + (&(xmlDocument[*xmlDocumentLenReturn]), \ + xmlDocumentBufferSize - *xmlDocumentLenReturn - 1, \ + fmt, __VA_ARGS__); \ + if (*xmlDocumentLenReturn >= xmlDocumentBufferSize) { \ + return S3StatusXmlDocumentTooLarge; \ + } \ + } while (0) + + append("<AccessControlPolicy><Owner><ID>%s</ID><DisplayName>%s" + "</DisplayName></Owner><AccessControlList>", ownerId, + ownerDisplayName); + + 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: + const char *grantee; + switch (grant->granteeType) { + case S3GranteeTypeAllAwsUsers: + grantee = "http://acs.amazonaws.com/groups/global/" + "AuthenticatedUsers"; + break; + case S3GranteeTypeAllUsers: + grantee = "http://acs.amazonaws.com/groups/global/" + "AllUsers"; + break; + default: + grantee = "http://acs.amazonaws.com/groups/s3/" + "LogDelivery"; + break; + } + append("Group\"><URI>%s</URI>", grantee); + } + 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", "</AccessControlList></AccessControlPolicy>"); + + return S3StatusOK; } typedef struct SetAclData { - S3ResponsePropertiesCallback *responsePropertiesCallback; - S3ResponseCompleteCallback *responseCompleteCallback; - void *callbackData; + S3ResponsePropertiesCallback *responsePropertiesCallback; + S3ResponseCompleteCallback *responseCompleteCallback; + void *callbackData; - int aclXmlDocumentLen; - char aclXmlDocument[ACL_XML_DOC_MAXSIZE]; - int aclXmlDocumentBytesWritten; + int aclXmlDocumentLen; + char aclXmlDocument[ACL_XML_DOC_MAXSIZE]; + int aclXmlDocumentBytesWritten; } SetAclData; static S3Status setAclPropertiesCallback - (const S3ResponseProperties *responseProperties, void *callbackData) + (const S3ResponseProperties *responseProperties, void *callbackData) { - SetAclData *paData = (SetAclData *) callbackData; - - return (*(paData->responsePropertiesCallback)) - (responseProperties, paData->callbackData); + SetAclData *paData = (SetAclData *) callbackData; + + return (*(paData->responsePropertiesCallback)) + (responseProperties, paData->callbackData); } static int setAclDataCallback(int bufferSize, char *buffer, void *callbackData) { - SetAclData *paData = (SetAclData *) callbackData; + SetAclData *paData = (SetAclData *) callbackData; - int remaining = (paData->aclXmlDocumentLen - - paData->aclXmlDocumentBytesWritten); + int remaining = (paData->aclXmlDocumentLen - + paData->aclXmlDocumentBytesWritten); - int toCopy = bufferSize > remaining ? remaining : bufferSize; - - if (!toCopy) { - return 0; - } + int toCopy = bufferSize > remaining ? remaining : bufferSize; + + if (!toCopy) { + return 0; + } - memcpy(buffer, &(paData->aclXmlDocument - [paData->aclXmlDocumentBytesWritten]), toCopy); + memcpy(buffer, &(paData->aclXmlDocument + [paData->aclXmlDocumentBytesWritten]), toCopy); - paData->aclXmlDocumentBytesWritten += toCopy; + paData->aclXmlDocumentBytesWritten += toCopy; - return toCopy; + return toCopy; } static void setAclCompleteCallback(S3Status requestStatus, - const S3ErrorDetails *s3ErrorDetails, - void *callbackData) + const S3ErrorDetails *s3ErrorDetails, + void *callbackData) { - SetAclData *paData = (SetAclData *) callbackData; + SetAclData *paData = (SetAclData *) callbackData; - (*(paData->responseCompleteCallback)) - (requestStatus, s3ErrorDetails, paData->callbackData); + (*(paData->responseCompleteCallback)) + (requestStatus, s3ErrorDetails, paData->callbackData); - free(paData); + free(paData); } void S3_set_acl(const S3BucketContext *bucketContext, const char *key, - const char *ownerId, const char *ownerDisplayName, - int aclGrantCount, const S3AclGrant *aclGrants, - S3RequestContext *requestContext, - const S3ResponseHandler *handler, void *callbackData) + const char *ownerId, const char *ownerDisplayName, + 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; - } - - SetAclData *data = (SetAclData *) malloc(sizeof(SetAclData)); - if (!data) { - (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData); - return; - } - - // Convert aclGrants to XML document - S3Status status = generateAclXmlDocument - (ownerId, ownerDisplayName, aclGrantCount, aclGrants, - &(data->aclXmlDocumentLen), data->aclXmlDocument, - sizeof(data->aclXmlDocument)); - if (status != S3StatusOK) { - free(data); - (*(handler->completeCallback))(status, 0, callbackData); - return; - } - - data->responsePropertiesCallback = handler->propertiesCallback; - data->responseCompleteCallback = handler->completeCallback; - data->callbackData = callbackData; - - data->aclXmlDocumentBytesWritten = 0; - - // Set up the RequestParams - RequestParams params = - { - HttpRequestTypePUT, // httpRequestType - { bucketContext->bucketName, // bucketName - bucketContext->protocol, // protocol - bucketContext->uriStyle, // uriStyle - bucketContext->accessKeyId, // accessKeyId - bucketContext->secretAccessKey }, // secretAccessKey - key, // key - 0, // queryParams - "acl", // subResource - 0, // copySourceBucketName - 0, // copySourceKey - 0, // getConditions - 0, // startByte - 0, // byteCount - 0, // putProperties - &setAclPropertiesCallback, // propertiesCallback - &setAclDataCallback, // toS3Callback - data->aclXmlDocumentLen, // toS3CallbackTotalSize - 0, // fromS3Callback - &setAclCompleteCallback, // completeCallback - data // callbackData - }; - - // Perform the request - request_perform(¶ms, requestContext); + if (aclGrantCount > S3_MAX_ACL_GRANT_COUNT) { + (*(handler->completeCallback)) + (S3StatusTooManyGrants, 0, callbackData); + return; + } + + SetAclData *data = (SetAclData *) malloc(sizeof(SetAclData)); + if (!data) { + (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData); + return; + } + + // Convert aclGrants to XML document + S3Status status = generateAclXmlDocument + (ownerId, ownerDisplayName, aclGrantCount, aclGrants, + &(data->aclXmlDocumentLen), data->aclXmlDocument, + sizeof(data->aclXmlDocument)); + if (status != S3StatusOK) { + free(data); + (*(handler->completeCallback))(status, 0, callbackData); + return; + } + + data->responsePropertiesCallback = handler->propertiesCallback; + data->responseCompleteCallback = handler->completeCallback; + data->callbackData = callbackData; + + data->aclXmlDocumentBytesWritten = 0; + + // Set up the RequestParams + RequestParams params = + { + HttpRequestTypePUT, // httpRequestType + { bucketContext->bucketName, // bucketName + bucketContext->protocol, // protocol + bucketContext->uriStyle, // uriStyle + bucketContext->accessKeyId, // accessKeyId + bucketContext->secretAccessKey }, // secretAccessKey + key, // key + 0, // queryParams + "acl", // subResource + 0, // copySourceBucketName + 0, // copySourceKey + 0, // getConditions + 0, // startByte + 0, // byteCount + 0, // putProperties + &setAclPropertiesCallback, // propertiesCallback + &setAclDataCallback, // toS3Callback + data->aclXmlDocumentLen, // toS3CallbackTotalSize + 0, // fromS3Callback + &setAclCompleteCallback, // completeCallback + data // callbackData + }; + + // Perform the request + request_perform(¶ms, requestContext); } diff --git a/src/bucket.c b/src/bucket.c index 9dcc48c..379b8ba 100644 --- a/src/bucket.c +++ b/src/bucket.c @@ -34,127 +34,127 @@ typedef struct TestBucketData { - SimpleXml simpleXml; + SimpleXml simpleXml; - S3ResponsePropertiesCallback *responsePropertiesCallback; - S3ResponseCompleteCallback *responseCompleteCallback; - void *callbackData; + S3ResponsePropertiesCallback *responsePropertiesCallback; + S3ResponseCompleteCallback *responseCompleteCallback; + void *callbackData; - int locationConstraintReturnSize; - char *locationConstraintReturn; + int locationConstraintReturnSize; + char *locationConstraintReturn; - string_buffer(locationConstraint, 256); + string_buffer(locationConstraint, 256); } TestBucketData; static S3Status testBucketXmlCallback(const char *elementPath, - const char *data, int dataLen, - void *callbackData) + const char *data, int dataLen, + void *callbackData) { - TestBucketData *tbData = (TestBucketData *) callbackData; + TestBucketData *tbData = (TestBucketData *) callbackData; - int fit; + int fit; - if (data && !strcmp(elementPath, "LocationConstraint")) { - string_buffer_append(tbData->locationConstraint, data, dataLen, fit); - } + if (data && !strcmp(elementPath, "LocationConstraint")) { + string_buffer_append(tbData->locationConstraint, data, dataLen, fit); + } - return S3StatusOK; + return S3StatusOK; } static S3Status testBucketPropertiesCallback - (const S3ResponseProperties *responseProperties, void *callbackData) + (const S3ResponseProperties *responseProperties, void *callbackData) { - TestBucketData *tbData = (TestBucketData *) callbackData; - - return (*(tbData->responsePropertiesCallback)) - (responseProperties, tbData->callbackData); + TestBucketData *tbData = (TestBucketData *) callbackData; + + return (*(tbData->responsePropertiesCallback)) + (responseProperties, tbData->callbackData); } static S3Status testBucketDataCallback(int bufferSize, const char *buffer, - void *callbackData) + void *callbackData) { - TestBucketData *tbData = (TestBucketData *) callbackData; + TestBucketData *tbData = (TestBucketData *) callbackData; - return simplexml_add(&(tbData->simpleXml), buffer, bufferSize); + return simplexml_add(&(tbData->simpleXml), buffer, bufferSize); } static void testBucketCompleteCallback(S3Status requestStatus, - const S3ErrorDetails *s3ErrorDetails, - void *callbackData) + const S3ErrorDetails *s3ErrorDetails, + void *callbackData) { - TestBucketData *tbData = (TestBucketData *) callbackData; + TestBucketData *tbData = (TestBucketData *) callbackData; - // Copy the location constraint into the return buffer - snprintf(tbData->locationConstraintReturn, - tbData->locationConstraintReturnSize, "%s", - tbData->locationConstraint); + // Copy the location constraint into the return buffer + snprintf(tbData->locationConstraintReturn, + tbData->locationConstraintReturnSize, "%s", + tbData->locationConstraint); - (*(tbData->responseCompleteCallback)) - (requestStatus, s3ErrorDetails, tbData->callbackData); + (*(tbData->responseCompleteCallback)) + (requestStatus, s3ErrorDetails, tbData->callbackData); - simplexml_deinitialize(&(tbData->simpleXml)); + simplexml_deinitialize(&(tbData->simpleXml)); - free(tbData); + 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) + 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(¶ms, requestContext); + // 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(¶ms, requestContext); } @@ -162,217 +162,217 @@ void S3_test_bucket(S3Protocol protocol, S3UriStyle uriStyle, typedef struct CreateBucketData { - S3ResponsePropertiesCallback *responsePropertiesCallback; - S3ResponseCompleteCallback *responseCompleteCallback; - void *callbackData; + S3ResponsePropertiesCallback *responsePropertiesCallback; + S3ResponseCompleteCallback *responseCompleteCallback; + void *callbackData; - char doc[1024]; - int docLen, docBytesWritten; -} CreateBucketData; - + char doc[1024]; + int docLen, docBytesWritten; +} CreateBucketData; + static S3Status createBucketPropertiesCallback - (const S3ResponseProperties *responseProperties, void *callbackData) + (const S3ResponseProperties *responseProperties, void *callbackData) { - CreateBucketData *cbData = (CreateBucketData *) callbackData; - - return (*(cbData->responsePropertiesCallback)) - (responseProperties, cbData->callbackData); + CreateBucketData *cbData = (CreateBucketData *) callbackData; + + return (*(cbData->responsePropertiesCallback)) + (responseProperties, cbData->callbackData); } static int createBucketDataCallback(int bufferSize, char *buffer, - void *callbackData) + void *callbackData) { - CreateBucketData *cbData = (CreateBucketData *) callbackData; + CreateBucketData *cbData = (CreateBucketData *) callbackData; - if (!cbData->docLen) { - return 0; - } + if (!cbData->docLen) { + return 0; + } - int remaining = (cbData->docLen - cbData->docBytesWritten); + int remaining = (cbData->docLen - cbData->docBytesWritten); - int toCopy = bufferSize > remaining ? remaining : bufferSize; - - if (!toCopy) { - return 0; - } + int toCopy = bufferSize > remaining ? remaining : bufferSize; + + if (!toCopy) { + return 0; + } - memcpy(buffer, &(cbData->doc[cbData->docBytesWritten]), toCopy); + memcpy(buffer, &(cbData->doc[cbData->docBytesWritten]), toCopy); - cbData->docBytesWritten += toCopy; + cbData->docBytesWritten += toCopy; - return toCopy; + return toCopy; } static void createBucketCompleteCallback(S3Status requestStatus, - const S3ErrorDetails *s3ErrorDetails, - void *callbackData) + const S3ErrorDetails *s3ErrorDetails, + void *callbackData) { - CreateBucketData *cbData = (CreateBucketData *) callbackData; + CreateBucketData *cbData = (CreateBucketData *) callbackData; - (*(cbData->responseCompleteCallback)) - (requestStatus, s3ErrorDetails, cbData->callbackData); + (*(cbData->responseCompleteCallback)) + (requestStatus, s3ErrorDetails, cbData->callbackData); - free(cbData); + 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) + 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(¶ms, requestContext); + // 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(¶ms, requestContext); } - + // delete bucket ------------------------------------------------------------- typedef struct DeleteBucketData { - S3ResponsePropertiesCallback *responsePropertiesCallback; - S3ResponseCompleteCallback *responseCompleteCallback; - void *callbackData; + S3ResponsePropertiesCallback *responsePropertiesCallback; + S3ResponseCompleteCallback *responseCompleteCallback; + void *callbackData; } DeleteBucketData; static S3Status deleteBucketPropertiesCallback - (const S3ResponseProperties *responseProperties, void *callbackData) + (const S3ResponseProperties *responseProperties, void *callbackData) { - DeleteBucketData *dbData = (DeleteBucketData *) callbackData; - - return (*(dbData->responsePropertiesCallback)) - (responseProperties, dbData->callbackData); + DeleteBucketData *dbData = (DeleteBucketData *) callbackData; + + return (*(dbData->responsePropertiesCallback)) + (responseProperties, dbData->callbackData); } static void deleteBucketCompleteCallback(S3Status requestStatus, - const S3ErrorDetails *s3ErrorDetails, - void *callbackData) + const S3ErrorDetails *s3ErrorDetails, + void *callbackData) { - DeleteBucketData *dbData = (DeleteBucketData *) callbackData; + DeleteBucketData *dbData = (DeleteBucketData *) callbackData; - (*(dbData->responseCompleteCallback)) - (requestStatus, s3ErrorDetails, dbData->callbackData); + (*(dbData->responseCompleteCallback)) + (requestStatus, s3ErrorDetails, dbData->callbackData); - free(dbData); + 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) + 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(¶ms, requestContext); + // 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(¶ms, requestContext); } @@ -380,23 +380,23 @@ void S3_delete_bucket(S3Protocol protocol, S3UriStyle uriStyle, 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); + 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); + 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 @@ -406,326 +406,326 @@ static void initialize_list_bucket_contents(ListBucketContents *contents) typedef struct ListBucketData { - SimpleXml simpleXml; + SimpleXml simpleXml; - S3ResponsePropertiesCallback *responsePropertiesCallback; - S3ListBucketCallback *listBucketCallback; - S3ResponseCompleteCallback *responseCompleteCallback; - void *callbackData; + S3ResponsePropertiesCallback *responsePropertiesCallback; + S3ListBucketCallback *listBucketCallback; + S3ResponseCompleteCallback *responseCompleteCallback; + void *callbackData; - string_buffer(isTruncated, 64); - string_buffer(nextMarker, 1024); + string_buffer(isTruncated, 64); + string_buffer(nextMarker, 1024); - int contentsCount; - ListBucketContents contents[MAX_CONTENTS]; + int contentsCount; + ListBucketContents contents[MAX_CONTENTS]; - int commonPrefixesCount; - char commonPrefixes[MAX_COMMON_PREFIXES][1024]; - int commonPrefixLens[MAX_COMMON_PREFIXES]; + 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; + 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); + 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) + 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; + 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) + (const S3ResponseProperties *responseProperties, void *callbackData) { - ListBucketData *lbData = (ListBucketData *) callbackData; - - return (*(lbData->responsePropertiesCallback)) - (responseProperties, lbData->callbackData); + ListBucketData *lbData = (ListBucketData *) callbackData; + + return (*(lbData->responsePropertiesCallback)) + (responseProperties, lbData->callbackData); } static S3Status listBucketDataCallback(int bufferSize, const char *buffer, - void *callbackData) + void *callbackData) { - ListBucketData *lbData = (ListBucketData *) callbackData; - - return simplexml_add(&(lbData->simpleXml), buffer, bufferSize); + ListBucketData *lbData = (ListBucketData *) callbackData; + + return simplexml_add(&(lbData->simpleXml), buffer, bufferSize); } static void listBucketCompleteCallback(S3Status requestStatus, - const S3ErrorDetails *s3ErrorDetails, - void *callbackData) + const S3ErrorDetails *s3ErrorDetails, + void *callbackData) { - ListBucketData *lbData = (ListBucketData *) callbackData; + ListBucketData *lbData = (ListBucketData *) callbackData; - // Make the callback if there is anything - if (lbData->contentsCount || lbData->commonPrefixesCount) { - make_list_bucket_callback(lbData); - } + // Make the callback if there is anything + if (lbData->contentsCount || lbData->commonPrefixesCount) { + make_list_bucket_callback(lbData); + } - (*(lbData->responseCompleteCallback)) - (requestStatus, s3ErrorDetails, lbData->callbackData); + (*(lbData->responseCompleteCallback)) + (requestStatus, s3ErrorDetails, lbData->callbackData); - simplexml_deinitialize(&(lbData->simpleXml)); + simplexml_deinitialize(&(lbData->simpleXml)); - free(lbData); + 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) + 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(¶ms, requestContext); + // 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(¶ms, requestContext); } diff --git a/src/error_parser.c b/src/error_parser.c index baa206e..929f4ce 100644 --- a/src/error_parser.c +++ b/src/error_parser.c @@ -29,211 +29,211 @@ static S3Status errorXmlCallback(const char *elementPath, const char *data, - int dataLen, void *callbackData) + int dataLen, void *callbackData) { - // We ignore end of element callbacks because we don't care about them - if (!data) { - return S3StatusOK; - } - - ErrorParser *errorParser = (ErrorParser *) callbackData; - - int fit; - - if (!strcmp(elementPath, "Error")) { - // Ignore, this is the Error element itself, we only care about subs - } - else if (!strcmp(elementPath, "Error/Code")) { - string_buffer_append(errorParser->code, data, dataLen, fit); - } - else if (!strcmp(elementPath, "Error/Message")) { - string_buffer_append(errorParser->message, data, dataLen, fit); - errorParser->s3ErrorDetails.message = errorParser->message; - } - else if (!strcmp(elementPath, "Error/Resource")) { - string_buffer_append(errorParser->resource, data, dataLen, fit); - errorParser->s3ErrorDetails.resource = errorParser->resource; - } - else if (!strcmp(elementPath, "Error/FurtherDetails")) { - string_buffer_append(errorParser->furtherDetails, data, dataLen, fit); - errorParser->s3ErrorDetails.furtherDetails = - errorParser->furtherDetails; - } - else { - if (strncmp(elementPath, "Error/", sizeof("Error/") - 1)) { - // If for some weird reason it's not within the Error element, - // ignore it - return S3StatusOK; - } - // It's an unknown error element. See if it matches the most - // recent error element. - const char *elementName = &(elementPath[sizeof("Error/") - 1]); - if (errorParser->s3ErrorDetails.extraDetailsCount && - !strcmp(elementName, errorParser->s3ErrorDetails.extraDetails - [errorParser->s3ErrorDetails.extraDetailsCount - 1].name)) { - // Append the value - string_multibuffer_append(errorParser->extraDetailsNamesValues, - data, dataLen, fit); - // If it didn't fit, remove this extra - if (!fit) { - errorParser->s3ErrorDetails.extraDetailsCount--; - } - return S3StatusOK; - } - // OK, must add another unknown error element, if it will fit. - if (errorParser->s3ErrorDetails.extraDetailsCount == - sizeof(errorParser->extraDetails)) { - // Won't fit. Ignore this one. - return S3StatusOK; - } - // Copy in the name and value - char *name = string_multibuffer_current - (errorParser->extraDetailsNamesValues); - int nameLen = strlen(elementName); - string_multibuffer_add(errorParser->extraDetailsNamesValues, - elementName, nameLen, fit); - if (!fit) { - // Name didn't fit; ignore this one. - return S3StatusOK; - } - char *value = string_multibuffer_current - (errorParser->extraDetailsNamesValues); - string_multibuffer_add(errorParser->extraDetailsNamesValues, - data, dataLen, fit); - if (!fit) { - // Value didn't fit; ignore this one. - return S3StatusOK; - } - S3NameValue *nv = - &(errorParser->extraDetails - [errorParser->s3ErrorDetails.extraDetailsCount++]); - nv->name = name; - nv->value = value; - } - - return S3StatusOK; + // We ignore end of element callbacks because we don't care about them + if (!data) { + return S3StatusOK; + } + + ErrorParser *errorParser = (ErrorParser *) callbackData; + + int fit; + + if (!strcmp(elementPath, "Error")) { + // Ignore, this is the Error element itself, we only care about subs + } + else if (!strcmp(elementPath, "Error/Code")) { + string_buffer_append(errorParser->code, data, dataLen, fit); + } + else if (!strcmp(elementPath, "Error/Message")) { + string_buffer_append(errorParser->message, data, dataLen, fit); + errorParser->s3ErrorDetails.message = errorParser->message; + } + else if (!strcmp(elementPath, "Error/Resource")) { + string_buffer_append(errorParser->resource, data, dataLen, fit); + errorParser->s3ErrorDetails.resource = errorParser->resource; + } + else if (!strcmp(elementPath, "Error/FurtherDetails")) { + string_buffer_append(errorParser->furtherDetails, data, dataLen, fit); + errorParser->s3ErrorDetails.furtherDetails = + errorParser->furtherDetails; + } + else { + if (strncmp(elementPath, "Error/", sizeof("Error/") - 1)) { + // If for some weird reason it's not within the Error element, + // ignore it + return S3StatusOK; + } + // It's an unknown error element. See if it matches the most + // recent error element. + const char *elementName = &(elementPath[sizeof("Error/") - 1]); + if (errorParser->s3ErrorDetails.extraDetailsCount && + !strcmp(elementName, errorParser->s3ErrorDetails.extraDetails + [errorParser->s3ErrorDetails.extraDetailsCount - 1].name)) { + // Append the value + string_multibuffer_append(errorParser->extraDetailsNamesValues, + data, dataLen, fit); + // If it didn't fit, remove this extra + if (!fit) { + errorParser->s3ErrorDetails.extraDetailsCount--; + } + return S3StatusOK; + } + // OK, must add another unknown error element, if it will fit. + if (errorParser->s3ErrorDetails.extraDetailsCount == + sizeof(errorParser->extraDetails)) { + // Won't fit. Ignore this one. + return S3StatusOK; + } + // Copy in the name and value + char *name = string_multibuffer_current + (errorParser->extraDetailsNamesValues); + int nameLen = strlen(elementName); + string_multibuffer_add(errorParser->extraDetailsNamesValues, + elementName, nameLen, fit); + if (!fit) { + // Name didn't fit; ignore this one. + return S3StatusOK; + } + char *value = string_multibuffer_current + (errorParser->extraDetailsNamesValues); + string_multibuffer_add(errorParser->extraDetailsNamesValues, + data, dataLen, fit); + if (!fit) { + // Value didn't fit; ignore this one. + return S3StatusOK; + } + S3NameValue *nv = + &(errorParser->extraDetails + [errorParser->s3ErrorDetails.extraDetailsCount++]); + nv->name = name; + nv->value = value; + } + + return S3StatusOK; } void error_parser_initialize(ErrorParser *errorParser) { - errorParser->s3ErrorDetails.message = 0; - errorParser->s3ErrorDetails.resource = 0; - errorParser->s3ErrorDetails.furtherDetails = 0; - errorParser->s3ErrorDetails.extraDetailsCount = 0; - errorParser->s3ErrorDetails.extraDetails = errorParser->extraDetails; - errorParser->errorXmlParserInitialized = 0; - string_buffer_initialize(errorParser->code); - string_buffer_initialize(errorParser->message); - string_buffer_initialize(errorParser->resource); - string_buffer_initialize(errorParser->furtherDetails); - string_multibuffer_initialize(errorParser->extraDetailsNamesValues); + errorParser->s3ErrorDetails.message = 0; + errorParser->s3ErrorDetails.resource = 0; + errorParser->s3ErrorDetails.furtherDetails = 0; + errorParser->s3ErrorDetails.extraDetailsCount = 0; + errorParser->s3ErrorDetails.extraDetails = errorParser->extraDetails; + errorParser->errorXmlParserInitialized = 0; + string_buffer_initialize(errorParser->code); + string_buffer_initialize(errorParser->message); + string_buffer_initialize(errorParser->resource); + string_buffer_initialize(errorParser->furtherDetails); + string_multibuffer_initialize(errorParser->extraDetailsNamesValues); } S3Status error_parser_add(ErrorParser *errorParser, char *buffer, - int bufferSize) + int bufferSize) { - if (!errorParser->errorXmlParserInitialized) { - simplexml_initialize(&(errorParser->errorXmlParser), &errorXmlCallback, - errorParser); - errorParser->errorXmlParserInitialized = 1; - } + if (!errorParser->errorXmlParserInitialized) { + simplexml_initialize(&(errorParser->errorXmlParser), &errorXmlCallback, + errorParser); + errorParser->errorXmlParserInitialized = 1; + } - return simplexml_add(&(errorParser->errorXmlParser), buffer, bufferSize); + return simplexml_add(&(errorParser->errorXmlParser), buffer, bufferSize); } void error_parser_convert_status(ErrorParser *errorParser, S3Status *status) { - // Convert the error status string into a code - if (!errorParser->codeLen) { - return; - } - -#define HANDLE_CODE(name) \ - do { \ - if (!strcmp(errorParser->code, #name)) { \ - *status = S3StatusError##name; \ - goto code_set; \ - } \ - } while (0) - - HANDLE_CODE(AccessDenied); - HANDLE_CODE(AccountProblem); - HANDLE_CODE(AmbiguousGrantByEmailAddress); - HANDLE_CODE(BadDigest); - HANDLE_CODE(BucketAlreadyExists); - HANDLE_CODE(BucketAlreadyOwnedByYou); - HANDLE_CODE(BucketNotEmpty); - HANDLE_CODE(CredentialsNotSupported); - HANDLE_CODE(CrossLocationLoggingProhibited); - HANDLE_CODE(EntityTooSmall); - HANDLE_CODE(EntityTooLarge); - HANDLE_CODE(ExpiredToken); - HANDLE_CODE(IncompleteBody); - HANDLE_CODE(IncorrectNumberOfFilesInPostRequest); - HANDLE_CODE(InlineDataTooLarge); - HANDLE_CODE(InternalError); - HANDLE_CODE(InvalidAccessKeyId); - HANDLE_CODE(InvalidAddressingHeader); - HANDLE_CODE(InvalidArgument); - HANDLE_CODE(InvalidBucketName); - HANDLE_CODE(InvalidDigest); - HANDLE_CODE(InvalidLocationConstraint); - HANDLE_CODE(InvalidPayer); - HANDLE_CODE(InvalidPolicyDocument); - HANDLE_CODE(InvalidRange); - HANDLE_CODE(InvalidSecurity); - HANDLE_CODE(InvalidSOAPRequest); - HANDLE_CODE(InvalidStorageClass); - HANDLE_CODE(InvalidTargetBucketForLogging); - HANDLE_CODE(InvalidToken); - HANDLE_CODE(InvalidURI); - HANDLE_CODE(KeyTooLong); - HANDLE_CODE(MalformedACLError); - HANDLE_CODE(MalformedXML); - HANDLE_CODE(MaxMessageLengthExceeded); - HANDLE_CODE(MaxPostPreDataLengthExceededError); - HANDLE_CODE(MetadataTooLarge); - HANDLE_CODE(MethodNotAllowed); - HANDLE_CODE(MissingAttachment); - HANDLE_CODE(MissingContentLength); - HANDLE_CODE(MissingSecurityElement); - HANDLE_CODE(MissingSecurityHeader); - HANDLE_CODE(NoLoggingStatusForKey); - HANDLE_CODE(NoSuchBucket); - HANDLE_CODE(NoSuchKey); - HANDLE_CODE(NotImplemented); - HANDLE_CODE(NotSignedUp); - HANDLE_CODE(OperationAborted); - HANDLE_CODE(PermanentRedirect); - HANDLE_CODE(PreconditionFailed); - HANDLE_CODE(Redirect); - HANDLE_CODE(RequestIsNotMultiPartContent); - HANDLE_CODE(RequestTimeout); - HANDLE_CODE(RequestTimeTooSkewed); - HANDLE_CODE(RequestTorrentOfBucketError); - HANDLE_CODE(SignatureDoesNotMatch); - HANDLE_CODE(SlowDown); - HANDLE_CODE(TemporaryRedirect); - HANDLE_CODE(TokenRefreshRequired); - HANDLE_CODE(TooManyBuckets); - HANDLE_CODE(UnexpectedContent); - HANDLE_CODE(UnresolvableGrantByEmailAddress); - HANDLE_CODE(UserKeyMustBeSpecified); - *status = S3StatusErrorUnknown; + // Convert the error status string into a code + if (!errorParser->codeLen) { + return; + } + +#define HANDLE_CODE(name) \ + do { \ + if (!strcmp(errorParser->code, #name)) { \ + *status = S3StatusError##name; \ + goto code_set; \ + } \ + } while (0) + + HANDLE_CODE(AccessDenied); + HANDLE_CODE(AccountProblem); + HANDLE_CODE(AmbiguousGrantByEmailAddress); + HANDLE_CODE(BadDigest); + HANDLE_CODE(BucketAlreadyExists); + HANDLE_CODE(BucketAlreadyOwnedByYou); + HANDLE_CODE(BucketNotEmpty); + HANDLE_CODE(CredentialsNotSupported); + HANDLE_CODE(CrossLocationLoggingProhibited); + HANDLE_CODE(EntityTooSmall); + HANDLE_CODE(EntityTooLarge); + HANDLE_CODE(ExpiredToken); + HANDLE_CODE(IncompleteBody); + HANDLE_CODE(IncorrectNumberOfFilesInPostRequest); + HANDLE_CODE(InlineDataTooLarge); + HANDLE_CODE(InternalError); + HANDLE_CODE(InvalidAccessKeyId); + HANDLE_CODE(InvalidAddressingHeader); + HANDLE_CODE(InvalidArgument); + HANDLE_CODE(InvalidBucketName); + HANDLE_CODE(InvalidDigest); + HANDLE_CODE(InvalidLocationConstraint); + HANDLE_CODE(InvalidPayer); + HANDLE_CODE(InvalidPolicyDocument); + HANDLE_CODE(InvalidRange); + HANDLE_CODE(InvalidSecurity); + HANDLE_CODE(InvalidSOAPRequest); + HANDLE_CODE(InvalidStorageClass); + HANDLE_CODE(InvalidTargetBucketForLogging); + HANDLE_CODE(InvalidToken); + HANDLE_CODE(InvalidURI); + HANDLE_CODE(KeyTooLong); + HANDLE_CODE(MalformedACLError); + HANDLE_CODE(MalformedXML); + HANDLE_CODE(MaxMessageLengthExceeded); + HANDLE_CODE(MaxPostPreDataLengthExceededError); + HANDLE_CODE(MetadataTooLarge); + HANDLE_CODE(MethodNotAllowed); + HANDLE_CODE(MissingAttachment); + HANDLE_CODE(MissingContentLength); + HANDLE_CODE(MissingSecurityElement); + HANDLE_CODE(MissingSecurityHeader); + HANDLE_CODE(NoLoggingStatusForKey); + HANDLE_CODE(NoSuchBucket); + HANDLE_CODE(NoSuchKey); + HANDLE_CODE(NotImplemented); + HANDLE_CODE(NotSignedUp); + HANDLE_CODE(OperationAborted); + HANDLE_CODE(PermanentRedirect); + HANDLE_CODE(PreconditionFailed); + HANDLE_CODE(Redirect); + HANDLE_CODE(RequestIsNotMultiPartContent); + HANDLE_CODE(RequestTimeout); + HANDLE_CODE(RequestTimeTooSkewed); + HANDLE_CODE(RequestTorrentOfBucketError); + HANDLE_CODE(SignatureDoesNotMatch); + HANDLE_CODE(SlowDown); + HANDLE_CODE(TemporaryRedirect); + HANDLE_CODE(TokenRefreshRequired); + HANDLE_CODE(TooManyBuckets); + HANDLE_CODE(UnexpectedContent); + HANDLE_CODE(UnresolvableGrantByEmailAddress); + HANDLE_CODE(UserKeyMustBeSpecified); + *status = S3StatusErrorUnknown; code_set: - return; + return; } // Always call this void error_parser_deinitialize(ErrorParser *errorParser) { - if (errorParser->errorXmlParserInitialized) { - simplexml_deinitialize(&(errorParser->errorXmlParser)); - } + if (errorParser->errorXmlParserInitialized) { + simplexml_deinitialize(&(errorParser->errorXmlParser)); + } } diff --git a/src/general.c b/src/general.c index 861c289..1e23812 100644 --- a/src/general.c +++ b/src/general.c @@ -34,442 +34,442 @@ static int initializeCountG = 0; S3Status S3_initialize(const char *userAgentInfo, int flags) { - if (initializeCountG++) { - return S3StatusOK; - } + if (initializeCountG++) { + return S3StatusOK; + } - return request_api_initialize(userAgentInfo, flags); + return request_api_initialize(userAgentInfo, flags); } void S3_deinitialize() { - if (--initializeCountG) { - return; - } + if (--initializeCountG) { + return; + } - request_api_deinitialize(); + request_api_deinitialize(); } const char *S3_get_status_name(S3Status status) { - switch (status) { -#define handlecase(s) \ - case S3Status##s: \ - return #s - - handlecase(OK); - handlecase(InternalError); - handlecase(OutOfMemory); - handlecase(Interrupted); - handlecase(InvalidBucketNameTooLong); - handlecase(InvalidBucketNameFirstCharacter); - handlecase(InvalidBucketNameCharacter); - handlecase(InvalidBucketNameCharacterSequence); - handlecase(InvalidBucketNameTooShort); - handlecase(InvalidBucketNameDotQuadNotation); - handlecase(QueryParamsTooLong); - handlecase(FailedToInitializeRequest); - handlecase(MetaDataHeadersTooLong); - handlecase(BadMetaData); - handlecase(BadContentType); - handlecase(ContentTypeTooLong); - handlecase(BadMD5); - handlecase(MD5TooLong); - handlecase(BadCacheControl); - handlecase(CacheControlTooLong); - handlecase(BadContentDispositionFilename); - handlecase(ContentDispositionFilenameTooLong); - handlecase(BadContentEncoding); - handlecase(ContentEncodingTooLong); - handlecase(BadIfMatchETag); - handlecase(IfMatchETagTooLong); - handlecase(BadIfNotMatchETag); - handlecase(IfNotMatchETagTooLong); - handlecase(HeadersTooLong); - handlecase(KeyTooLong); - handlecase(UriTooLong); - handlecase(XmlParseFailure); - handlecase(EmailAddressTooLong); - handlecase(UserIdTooLong); - handlecase(UserDisplayNameTooLong); - handlecase(GroupUriTooLong); - handlecase(PermissionTooLong); - handlecase(TargetBucketTooLong); - handlecase(TargetPrefixTooLong); - handlecase(TooManyGrants); - handlecase(BadGrantee); - handlecase(BadPermission); - handlecase(XmlDocumentTooLarge); - handlecase(NameLookupError); - handlecase(FailedToConnect); - handlecase(ServerFailedVerification); - handlecase(ConnectionFailed); - handlecase(AbortedByCallback); - handlecase(ErrorAccessDenied); - handlecase(ErrorAccountProblem); - handlecase(ErrorAmbiguousGrantByEmailAddress); - handlecase(ErrorBadDigest); - handlecase(ErrorBucketAlreadyExists); - handlecase(ErrorBucketAlreadyOwnedByYou); - handlecase(ErrorBucketNotEmpty); - handlecase(ErrorCredentialsNotSupported); - handlecase(ErrorCrossLocationLoggingProhibited); - handlecase(ErrorEntityTooSmall); - handlecase(ErrorEntityTooLarge); - handlecase(ErrorExpiredToken); - handlecase(ErrorIncompleteBody); - handlecase(ErrorIncorrectNumberOfFilesInPostRequest); - handlecase(ErrorInlineDataTooLarge); - handlecase(ErrorInternalError); - handlecase(ErrorInvalidAccessKeyId); - handlecase(ErrorInvalidAddressingHeader); - handlecase(ErrorInvalidArgument); - handlecase(ErrorInvalidBucketName); - handlecase(ErrorInvalidDigest); - handlecase(ErrorInvalidLocationConstraint); - handlecase(ErrorInvalidPayer); - handlecase(ErrorInvalidPolicyDocument); - handlecase(ErrorInvalidRange); - handlecase(ErrorInvalidSecurity); - handlecase(ErrorInvalidSOAPRequest); - handlecase(ErrorInvalidStorageClass); - handlecase(ErrorInvalidTargetBucketForLogging); - handlecase(ErrorInvalidToken); - handlecase(ErrorInvalidURI); - handlecase(ErrorKeyTooLong); - handlecase(ErrorMalformedACLError); - handlecase(ErrorMalformedXML); - handlecase(ErrorMaxMessageLengthExceeded); - handlecase(ErrorMaxPostPreDataLengthExceededError); - handlecase(ErrorMetadataTooLarge); - handlecase(ErrorMethodNotAllowed); - handlecase(ErrorMissingAttachment); - handlecase(ErrorMissingContentLength); - handlecase(ErrorMissingSecurityElement); - handlecase(ErrorMissingSecurityHeader); - handlecase(ErrorNoLoggingStatusForKey); - handlecase(ErrorNoSuchBucket); - handlecase(ErrorNoSuchKey); - handlecase(ErrorNotImplemented); - handlecase(ErrorNotSignedUp); - handlecase(ErrorOperationAborted); - handlecase(ErrorPermanentRedirect); - handlecase(ErrorPreconditionFailed); - handlecase(ErrorRedirect); - handlecase(ErrorRequestIsNotMultiPartContent); - handlecase(ErrorRequestTimeout); - handlecase(ErrorRequestTimeTooSkewed); - handlecase(ErrorRequestTorrentOfBucketError); - handlecase(ErrorSignatureDoesNotMatch); - handlecase(ErrorSlowDown); - handlecase(ErrorTemporaryRedirect); - handlecase(ErrorTokenRefreshRequired); - handlecase(ErrorTooManyBuckets); - handlecase(ErrorUnexpectedContent); - handlecase(ErrorUnresolvableGrantByEmailAddress); - handlecase(ErrorUserKeyMustBeSpecified); - handlecase(ErrorUnknown); - handlecase(HttpErrorMovedTemporarily); - handlecase(HttpErrorBadRequest); - handlecase(HttpErrorForbidden); - handlecase(HttpErrorNotFound); - handlecase(HttpErrorConflict); - handlecase(HttpErrorUnknown); - } - - return "Unknown"; + switch (status) { +#define handlecase(s) \ + case S3Status##s: \ + return #s + + handlecase(OK); + handlecase(InternalError); + handlecase(OutOfMemory); + handlecase(Interrupted); + handlecase(InvalidBucketNameTooLong); + handlecase(InvalidBucketNameFirstCharacter); + handlecase(InvalidBucketNameCharacter); + handlecase(InvalidBucketNameCharacterSequence); + handlecase(InvalidBucketNameTooShort); + handlecase(InvalidBucketNameDotQuadNotation); + handlecase(QueryParamsTooLong); + handlecase(FailedToInitializeRequest); + handlecase(MetaDataHeadersTooLong); + handlecase(BadMetaData); + handlecase(BadContentType); + handlecase(ContentTypeTooLong); + handlecase(BadMD5); + handlecase(MD5TooLong); + handlecase(BadCacheControl); + handlecase(CacheControlTooLong); + handlecase(BadContentDispositionFilename); + handlecase(ContentDispositionFilenameTooLong); + handlecase(BadContentEncoding); + handlecase(ContentEncodingTooLong); + handlecase(BadIfMatchETag); + handlecase(IfMatchETagTooLong); + handlecase(BadIfNotMatchETag); + handlecase(IfNotMatchETagTooLong); + handlecase(HeadersTooLong); + handlecase(KeyTooLong); + handlecase(UriTooLong); + handlecase(XmlParseFailure); + handlecase(EmailAddressTooLong); + handlecase(UserIdTooLong); + handlecase(UserDisplayNameTooLong); + handlecase(GroupUriTooLong); + handlecase(PermissionTooLong); + handlecase(TargetBucketTooLong); + handlecase(TargetPrefixTooLong); + handlecase(TooManyGrants); + handlecase(BadGrantee); + handlecase(BadPermission); + handlecase(XmlDocumentTooLarge); + handlecase(NameLookupError); + handlecase(FailedToConnect); + handlecase(ServerFailedVerification); + handlecase(ConnectionFailed); + handlecase(AbortedByCallback); + handlecase(ErrorAccessDenied); + handlecase(ErrorAccountProblem); + handlecase(ErrorAmbiguousGrantByEmailAddress); + handlecase(ErrorBadDigest); + handlecase(ErrorBucketAlreadyExists); + handlecase(ErrorBucketAlreadyOwnedByYou); + handlecase(ErrorBucketNotEmpty); + handlecase(ErrorCredentialsNotSupported); + handlecase(ErrorCrossLocationLoggingProhibited); + handlecase(ErrorEntityTooSmall); + handlecase(ErrorEntityTooLarge); + handlecase(ErrorExpiredToken); + handlecase(ErrorIncompleteBody); + handlecase(ErrorIncorrectNumberOfFilesInPostRequest); + handlecase(ErrorInlineDataTooLarge); + handlecase(ErrorInternalError); + handlecase(ErrorInvalidAccessKeyId); + handlecase(ErrorInvalidAddressingHeader); + handlecase(ErrorInvalidArgument); + handlecase(ErrorInvalidBucketName); + handlecase(ErrorInvalidDigest); + handlecase(ErrorInvalidLocationConstraint); + handlecase(ErrorInvalidPayer); + handlecase(ErrorInvalidPolicyDocument); + handlecase(ErrorInvalidRange); + handlecase(ErrorInvalidSecurity); + handlecase(ErrorInvalidSOAPRequest); + handlecase(ErrorInvalidStorageClass); + handlecase(ErrorInvalidTargetBucketForLogging); + handlecase(ErrorInvalidToken); + handlecase(ErrorInvalidURI); + handlecase(ErrorKeyTooLong); + handlecase(ErrorMalformedACLError); + handlecase(ErrorMalformedXML); + handlecase(ErrorMaxMessageLengthExceeded); + handlecase(ErrorMaxPostPreDataLengthExceededError); + handlecase(ErrorMetadataTooLarge); + handlecase(ErrorMethodNotAllowed); + handlecase(ErrorMissingAttachment); + handlecase(ErrorMissingContentLength); + handlecase(ErrorMissingSecurityElement); + handlecase(ErrorMissingSecurityHeader); + handlecase(ErrorNoLoggingStatusForKey); + handlecase(ErrorNoSuchBucket); + handlecase(ErrorNoSuchKey); + handlecase(ErrorNotImplemented); + handlecase(ErrorNotSignedUp); + handlecase(ErrorOperationAborted); + handlecase(ErrorPermanentRedirect); + handlecase(ErrorPreconditionFailed); + handlecase(ErrorRedirect); + handlecase(ErrorRequestIsNotMultiPartContent); + handlecase(ErrorRequestTimeout); + handlecase(ErrorRequestTimeTooSkewed); + handlecase(ErrorRequestTorrentOfBucketError); + handlecase(ErrorSignatureDoesNotMatch); + handlecase(ErrorSlowDown); + handlecase(ErrorTemporaryRedirect); + handlecase(ErrorTokenRefreshRequired); + handlecase(ErrorTooManyBuckets); + handlecase(ErrorUnexpectedContent); + handlecase(ErrorUnresolvableGrantByEmailAddress); + handlecase(ErrorUserKeyMustBeSpecified); + handlecase(ErrorUnknown); + handlecase(HttpErrorMovedTemporarily); + handlecase(HttpErrorBadRequest); + handlecase(HttpErrorForbidden); + handlecase(HttpErrorNotFound); + handlecase(HttpErrorConflict); + handlecase(HttpErrorUnknown); + } + + return "Unknown"; } S3Status S3_validate_bucket_name(const char *bucketName, S3UriStyle uriStyle) { - int virtualHostStyle = (uriStyle == S3UriStyleVirtualHost); - int len = 0, maxlen = virtualHostStyle ? 63 : 255; - const char *b = bucketName; - - int hasDot = 0; - int hasNonDigit = 0; - - while (*b) { - if (len == maxlen) { - return S3StatusInvalidBucketNameTooLong; - } - else if (isalpha(*b)) { - len++, b++; - hasNonDigit = 1; - } - else if (isdigit(*b)) { - len++, b++; - } - else if (len == 0) { - return S3StatusInvalidBucketNameFirstCharacter; - } - else if (*b == '_') { - /* Virtual host style bucket names cannot have underscores */ - if (virtualHostStyle) { - return S3StatusInvalidBucketNameCharacter; - } - len++, b++; - hasNonDigit = 1; - } - else if (*b == '-') { - /* Virtual host style bucket names cannot have .- */ - if (virtualHostStyle && (b > bucketName) && (*(b - 1) == '.')) { - return S3StatusInvalidBucketNameCharacterSequence; - } - len++, b++; - hasNonDigit = 1; - } - else if (*b == '.') { - /* Virtual host style bucket names cannot have -. */ - if (virtualHostStyle && (b > bucketName) && (*(b - 1) == '-')) { - return S3StatusInvalidBucketNameCharacterSequence; - } - len++, b++; - hasDot = 1; - } - else { - return S3StatusInvalidBucketNameCharacter; - } - } - - if (len < 3) { - return S3StatusInvalidBucketNameTooShort; - } - - /* It's not clear from Amazon's documentation exactly what 'IP address - style' means. In its strictest sense, it could mean 'could be a valid - IP address', which would mean that 255.255.255.255 would be invalid, - wherase 256.256.256.256 would be valid. Or it could mean 'has 4 sets - of digits separated by dots'. Who knows. Let's just be really - conservative here: if it has any dots, and no non-digit characters, - then we reject it */ - if (hasDot && !hasNonDigit) { - return S3StatusInvalidBucketNameDotQuadNotation; - } - - return S3StatusOK; + int virtualHostStyle = (uriStyle == S3UriStyleVirtualHost); + int len = 0, maxlen = virtualHostStyle ? 63 : 255; + const char *b = bucketName; + + int hasDot = 0; + int hasNonDigit = 0; + + while (*b) { + if (len == maxlen) { + return S3StatusInvalidBucketNameTooLong; + } + else if (isalpha(*b)) { + len++, b++; + hasNonDigit = 1; + } + else if (isdigit(*b)) { + len++, b++; + } + else if (len == 0) { + return S3StatusInvalidBucketNameFirstCharacter; + } + else if (*b == '_') { + /* Virtual host style bucket names cannot have underscores */ + if (virtualHostStyle) { + return S3StatusInvalidBucketNameCharacter; + } + len++, b++; + hasNonDigit = 1; + } + else if (*b == '-') { + /* Virtual host style bucket names cannot have .- */ + if (virtualHostStyle && (b > bucketName) && (*(b - 1) == '.')) { + return S3StatusInvalidBucketNameCharacterSequence; + } + len++, b++; + hasNonDigit = 1; + } + else if (*b == '.') { + /* Virtual host style bucket names cannot have -. */ + if (virtualHostStyle && (b > bucketName) && (*(b - 1) == '-')) { + return S3StatusInvalidBucketNameCharacterSequence; + } + len++, b++; + hasDot = 1; + } + else { + return S3StatusInvalidBucketNameCharacter; + } + } + + if (len < 3) { + return S3StatusInvalidBucketNameTooShort; + } + + /* It's not clear from Amazon's documentation exactly what 'IP address + style' means. In its strictest sense, it could mean 'could be a valid + IP address', which would mean that 255.255.255.255 would be invalid, + wherase 256.256.256.256 would be valid. Or it could mean 'has 4 sets + of digits separated by dots'. Who knows. Let's just be really + conservative here: if it has any dots, and no non-digit characters, + then we reject it */ + if (hasDot && !hasNonDigit) { + return S3StatusInvalidBucketNameDotQuadNotation; + } + + return S3StatusOK; } typedef struct ConvertAclData { - char *ownerId; - int ownerIdLen; - char *ownerDisplayName; - int ownerDisplayNameLen; - 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); + char *ownerId; + int ownerIdLen; + char *ownerDisplayName; + int ownerDisplayNameLen; + 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); } ConvertAclData; static S3Status convertAclXmlCallback(const char *elementPath, - const char *data, int dataLen, - void *callbackData) + const char *data, int dataLen, + void *callbackData) { - ConvertAclData *caData = (ConvertAclData *) callbackData; - - int fit; - - if (data) { - if (!strcmp(elementPath, "AccessControlPolicy/Owner/ID")) { - caData->ownerIdLen += - snprintf(&(caData->ownerId[caData->ownerIdLen]), - S3_MAX_GRANTEE_USER_ID_SIZE - caData->ownerIdLen - 1, - "%.*s", dataLen, data); - if (caData->ownerIdLen >= S3_MAX_GRANTEE_USER_ID_SIZE) { - return S3StatusUserIdTooLong; - } - } - else if (!strcmp(elementPath, "AccessControlPolicy/Owner/" - "DisplayName")) { - caData->ownerDisplayNameLen += - snprintf(&(caData->ownerDisplayName - [caData->ownerDisplayNameLen]), - S3_MAX_GRANTEE_DISPLAY_NAME_SIZE - - caData->ownerDisplayNameLen - 1, - "%.*s", dataLen, data); - if (caData->ownerDisplayNameLen >= - S3_MAX_GRANTEE_DISPLAY_NAME_SIZE) { - return S3StatusUserDisplayNameTooLong; - } - } - else if (!strcmp(elementPath, - "AccessControlPolicy/AccessControlList/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, - "AccessControlPolicy/AccessControlList/Grant/" - "Grantee/DisplayName")) { - // CanonicalUser - string_buffer_append(caData->userDisplayName, data, dataLen, fit); - if (!fit) { - return S3StatusUserDisplayNameTooLong; - } - } - else if (!strcmp(elementPath, - "AccessControlPolicy/AccessControlList/Grant/" - "Grantee/URI")) { - // Group - string_buffer_append(caData->groupUri, data, dataLen, fit); - if (!fit) { - return S3StatusGroupUriTooLong; - } - } - else if (!strcmp(elementPath, - "AccessControlPolicy/AccessControlList/Grant/" - "Permission")) { - // Permission - string_buffer_append(caData->permission, data, dataLen, fit); - if (!fit) { - return S3StatusPermissionTooLong; - } - } - } - else { - if (!strcmp(elementPath, "AccessControlPolicy/AccessControlList/" - "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 if (!strcmp(caData->groupUri, - "http://acs.amazonaws.com/groups/s3/" - "LogDelivery")) { - grant->granteeType = S3GranteeTypeLogDelivery; - } - 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; + ConvertAclData *caData = (ConvertAclData *) callbackData; + + int fit; + + if (data) { + if (!strcmp(elementPath, "AccessControlPolicy/Owner/ID")) { + caData->ownerIdLen += + snprintf(&(caData->ownerId[caData->ownerIdLen]), + S3_MAX_GRANTEE_USER_ID_SIZE - caData->ownerIdLen - 1, + "%.*s", dataLen, data); + if (caData->ownerIdLen >= S3_MAX_GRANTEE_USER_ID_SIZE) { + return S3StatusUserIdTooLong; + } + } + else if (!strcmp(elementPath, "AccessControlPolicy/Owner/" + "DisplayName")) { + caData->ownerDisplayNameLen += + snprintf(&(caData->ownerDisplayName + [caData->ownerDisplayNameLen]), + S3_MAX_GRANTEE_DISPLAY_NAME_SIZE - + caData->ownerDisplayNameLen - 1, + "%.*s", dataLen, data); + if (caData->ownerDisplayNameLen >= + S3_MAX_GRANTEE_DISPLAY_NAME_SIZE) { + return S3StatusUserDisplayNameTooLong; + } + } + else if (!strcmp(elementPath, + "AccessControlPolicy/AccessControlList/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, + "AccessControlPolicy/AccessControlList/Grant/" + "Grantee/DisplayName")) { + // CanonicalUser + string_buffer_append(caData->userDisplayName, data, dataLen, fit); + if (!fit) { + return S3StatusUserDisplayNameTooLong; + } + } + else if (!strcmp(elementPath, + "AccessControlPolicy/AccessControlList/Grant/" + "Grantee/URI")) { + // Group + string_buffer_append(caData->groupUri, data, dataLen, fit); + if (!fit) { + return S3StatusGroupUriTooLong; + } + } + else if (!strcmp(elementPath, + "AccessControlPolicy/AccessControlList/Grant/" + "Permission")) { + // Permission + string_buffer_append(caData->permission, data, dataLen, fit); + if (!fit) { + return S3StatusPermissionTooLong; + } + } + } + else { + if (!strcmp(elementPath, "AccessControlPolicy/AccessControlList/" + "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 if (!strcmp(caData->groupUri, + "http://acs.amazonaws.com/groups/s3/" + "LogDelivery")) { + grant->granteeType = S3GranteeTypeLogDelivery; + } + 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; } S3Status S3_convert_acl(char *aclXml, char *ownerId, char *ownerDisplayName, - int *aclGrantCountReturn, S3AclGrant *aclGrants) + int *aclGrantCountReturn, S3AclGrant *aclGrants) { - ConvertAclData data; - - data.ownerId = ownerId; - data.ownerIdLen = 0; - data.ownerId[0] = 0; - data.ownerDisplayName = ownerDisplayName; - data.ownerDisplayNameLen = 0; - data.ownerDisplayName[0] = 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, &convertAclXmlCallback, &data); - - S3Status status = simplexml_add(&simpleXml, aclXml, strlen(aclXml)); - - simplexml_deinitialize(&simpleXml); - - return status; + ConvertAclData data; + + data.ownerId = ownerId; + data.ownerIdLen = 0; + data.ownerId[0] = 0; + data.ownerDisplayName = ownerDisplayName; + data.ownerDisplayNameLen = 0; + data.ownerDisplayName[0] = 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, &convertAclXmlCallback, &data); + + S3Status status = simplexml_add(&simpleXml, aclXml, strlen(aclXml)); + + simplexml_deinitialize(&simpleXml); + + return status; } int S3_status_is_retryable(S3Status status) { - switch (status) { - case S3StatusNameLookupError: - case S3StatusFailedToConnect: - case S3StatusConnectionFailed: - case S3StatusErrorInternalError: - case S3StatusErrorOperationAborted: - case S3StatusErrorRequestTimeout: - return 1; - default: - return 0; - } + switch (status) { + case S3StatusNameLookupError: + case S3StatusFailedToConnect: + case S3StatusConnectionFailed: + case S3StatusErrorInternalError: + case S3StatusErrorOperationAborted: + case S3StatusErrorRequestTimeout: + return 1; + default: + return 0; + } } diff --git a/src/mingw_functions.c b/src/mingw_functions.c index 0e2b7b2..fb3ec4d 100644 --- a/src/mingw_functions.c +++ b/src/mingw_functions.c @@ -29,91 +29,91 @@ unsigned long pthread_self() { - return (unsigned long) GetCurrentThreadId(); + return (unsigned long) GetCurrentThreadId(); } int pthread_mutex_init(pthread_mutex_t *mutex, void *v) { - (void) v; + (void) v; - InitializeCriticalSection(&(mutex->criticalSection)); + InitializeCriticalSection(&(mutex->criticalSection)); - return 0; + return 0; } int pthread_mutex_lock(pthread_mutex_t *mutex) { - EnterCriticalSection(&(mutex->criticalSection)); + EnterCriticalSection(&(mutex->criticalSection)); - return 0; + return 0; } int pthread_mutex_unlock(pthread_mutex_t *mutex) { - LeaveCriticalSection(&(mutex->criticalSection)); + LeaveCriticalSection(&(mutex->criticalSection)); - return 0; + return 0; } int pthread_mutex_destroy(pthread_mutex_t *mutex) { - DeleteCriticalSection(&(mutex->criticalSection)); + DeleteCriticalSection(&(mutex->criticalSection)); - return 0; + return 0; } int uname(struct utsname *u) { - OSVERSIONINFO info; - info.dwOSVersionInfoSize = sizeof(info); - - if (!GetVersionEx(&info)) { - return -1; - } - - u->machine = ""; - - switch (info.dwMajorVersion) { - case 4: - switch (info.dwMinorVersion) { - case 0: - u->sysname = "Microsoft Windows NT 4.0"; - break; - case 10: - u->sysname = "Microsoft Windows 98"; - break; - case 90: - u->sysname = "Microsoft Windows Me"; - break; - default: - return -1; - } - break; - - case 5: - switch (info.dwMinorVersion) { - case 0: - u->sysname = "Microsoft Windows 2000"; - break; - case 1: - u->sysname = "Microsoft Windows XP"; - break; - case 2: - u->sysname = "Microsoft Server 2003"; - break; - default: - return -1; - } - break; - - default: - return -1; - } - - return 0; + OSVERSIONINFO info; + info.dwOSVersionInfoSize = sizeof(info); + + if (!GetVersionEx(&info)) { + return -1; + } + + u->machine = ""; + + switch (info.dwMajorVersion) { + case 4: + switch (info.dwMinorVersion) { + case 0: + u->sysname = "Microsoft Windows NT 4.0"; + break; + case 10: + u->sysname = "Microsoft Windows 98"; + break; + case 90: + u->sysname = "Microsoft Windows Me"; + break; + default: + return -1; + } + break; + + case 5: + switch (info.dwMinorVersion) { + case 0: + u->sysname = "Microsoft Windows 2000"; + break; + case 1: + u->sysname = "Microsoft Windows XP"; + break; + case 2: + u->sysname = "Microsoft Server 2003"; + break; + default: + return -1; + } + break; + + default: + return -1; + } + + return 0; } diff --git a/src/mingw_s3_functions.c b/src/mingw_s3_functions.c index 142569d..b3f5c49 100644 --- a/src/mingw_s3_functions.c +++ b/src/mingw_s3_functions.c @@ -26,12 +26,12 @@ int setenv(const char *a, const char *b, int c) { - (void) c; + (void) c; - return SetEnvironmentVariable(a, b); + return SetEnvironmentVariable(a, b); } int unsetenv(const char *a) { - return SetEnvironmentVariable(a, 0); + return SetEnvironmentVariable(a, 0); } diff --git a/src/object.c b/src/object.c index 4c8fd1c..21946b1 100644 --- a/src/object.c +++ b/src/object.c @@ -33,39 +33,39 @@ // put object ---------------------------------------------------------------- void S3_put_object(const S3BucketContext *bucketContext, const char *key, - uint64_t contentLength, - const S3PutProperties *putProperties, - S3RequestContext *requestContext, - const S3PutObjectHandler *handler, void *callbackData) + uint64_t contentLength, + const S3PutProperties *putProperties, + S3RequestContext *requestContext, + const S3PutObjectHandler *handler, void *callbackData) { - // Set up the RequestParams - RequestParams params = - { - HttpRequestTypePUT, // httpRequestType - { bucketContext->bucketName, // bucketName - bucketContext->protocol, // protocol - bucketContext->uriStyle, // uriStyle - bucketContext->accessKeyId, // accessKeyId - bucketContext->secretAccessKey }, // secretAccessKey - key, // key - 0, // queryParams - 0, // subResource - 0, // copySourceBucketName - 0, // copySourceKey - 0, // getConditions - 0, // startByte - 0, // byteCount - putProperties, // putProperties - handler->responseHandler.propertiesCallback, // propertiesCallback - handler->putObjectDataCallback, // toS3Callback - contentLength, // toS3CallbackTotalSize - 0, // fromS3Callback - handler->responseHandler.completeCallback, // completeCallback - callbackData // callbackData - }; - - // Perform the request - request_perform(¶ms, requestContext); + // Set up the RequestParams + RequestParams params = + { + HttpRequestTypePUT, // httpRequestType + { bucketContext->bucketName, // bucketName + bucketContext->protocol, // protocol + bucketContext->uriStyle, // uriStyle + bucketContext->accessKeyId, // accessKeyId + bucketContext->secretAccessKey }, // secretAccessKey + key, // key + 0, // queryParams + 0, // subResource + 0, // copySourceBucketName + 0, // copySourceKey + 0, // getConditions + 0, // startByte + 0, // byteCount + putProperties, // putProperties + handler->responseHandler.propertiesCallback, // propertiesCallback + handler->putObjectDataCallback, // toS3Callback + contentLength, // toS3CallbackTotalSize + 0, // fromS3Callback + handler->responseHandler.completeCallback, // completeCallback + callbackData // callbackData + }; + + // Perform the request + request_perform(¶ms, requestContext); } @@ -74,264 +74,264 @@ void S3_put_object(const S3BucketContext *bucketContext, const char *key, typedef struct CopyObjectData { - SimpleXml simpleXml; - - S3ResponsePropertiesCallback *responsePropertiesCallback; - S3ResponseCompleteCallback *responseCompleteCallback; - void *callbackData; - - int64_t *lastModifiedReturn; - int eTagReturnSize; - char *eTagReturn; - int eTagReturnLen; - - string_buffer(lastModified, 256); + SimpleXml simpleXml; + + S3ResponsePropertiesCallback *responsePropertiesCallback; + S3ResponseCompleteCallback *responseCompleteCallback; + void *callbackData; + + int64_t *lastModifiedReturn; + int eTagReturnSize; + char *eTagReturn; + int eTagReturnLen; + + string_buffer(lastModified, 256); } CopyObjectData; static S3Status copyObjectXmlCallback(const char *elementPath, - const char *data, int dataLen, - void *callbackData) + const char *data, int dataLen, + void *callbackData) { - CopyObjectData *coData = (CopyObjectData *) callbackData; - - int fit; - - if (data) { - if (!strcmp(elementPath, "CopyObjectResult/LastModified")) { - string_buffer_append(coData->lastModified, data, dataLen, fit); - } - else if (!strcmp(elementPath, "CopyObjectResult/ETag")) { - if (coData->eTagReturnSize && coData->eTagReturn) { - coData->eTagReturnLen += - snprintf(&(coData->eTagReturn[coData->eTagReturnLen]), - coData->eTagReturnSize - - coData->eTagReturnLen - 1, - "%.*s", dataLen, data); - if (coData->eTagReturnLen >= coData->eTagReturnSize) { - return S3StatusXmlParseFailure; - } - } - } - } - - return S3StatusOK; + CopyObjectData *coData = (CopyObjectData *) callbackData; + + int fit; + + if (data) { + if (!strcmp(elementPath, "CopyObjectResult/LastModified")) { + string_buffer_append(coData->lastModified, data, dataLen, fit); + } + else if (!strcmp(elementPath, "CopyObjectResult/ETag")) { + if (coData->eTagReturnSize && coData->eTagReturn) { + coData->eTagReturnLen += + snprintf(&(coData->eTagReturn[coData->eTagReturnLen]), + coData->eTagReturnSize - + coData->eTagReturnLen - 1, + "%.*s", dataLen, data); + if (coData->eTagReturnLen >= coData->eTagReturnSize) { + return S3StatusXmlParseFailure; + } + } + } + } + + return S3StatusOK; } static S3Status copyObjectPropertiesCallback - (const S3ResponseProperties *responseProperties, void *callbackData) + (const S3ResponseProperties *responseProperties, void *callbackData) { - CopyObjectData *coData = (CopyObjectData *) callbackData; - - return (*(coData->responsePropertiesCallback)) - (responseProperties, coData->callbackData); + CopyObjectData *coData = (CopyObjectData *) callbackData; + + return (*(coData->responsePropertiesCallback)) + (responseProperties, coData->callbackData); } static S3Status copyObjectDataCallback(int bufferSize, const char *buffer, - void *callbackData) + void *callbackData) { - CopyObjectData *coData = (CopyObjectData *) callbackData; + CopyObjectData *coData = (CopyObjectData *) callbackData; - return simplexml_add(&(coData->simpleXml), buffer, bufferSize); + return simplexml_add(&(coData->simpleXml), buffer, bufferSize); } static void copyObjectCompleteCallback(S3Status requestStatus, - const S3ErrorDetails *s3ErrorDetails, - void *callbackData) + const S3ErrorDetails *s3ErrorDetails, + void *callbackData) { - CopyObjectData *coData = (CopyObjectData *) callbackData; + CopyObjectData *coData = (CopyObjectData *) callbackData; - if (coData->lastModifiedReturn) { - time_t lastModified = -1; - if (coData->lastModifiedLen) { - lastModified = parseIso8601Time(coData->lastModified); - } + if (coData->lastModifiedReturn) { + time_t lastModified = -1; + if (coData->lastModifiedLen) { + lastModified = parseIso8601Time(coData->lastModified); + } - *(coData->lastModifiedReturn) = lastModified; - } + *(coData->lastModifiedReturn) = lastModified; + } - (*(coData->responseCompleteCallback)) - (requestStatus, s3ErrorDetails, coData->callbackData); + (*(coData->responseCompleteCallback)) + (requestStatus, s3ErrorDetails, coData->callbackData); - simplexml_deinitialize(&(coData->simpleXml)); + simplexml_deinitialize(&(coData->simpleXml)); - free(coData); + free(coData); } void S3_copy_object(const S3BucketContext *bucketContext, const char *key, - const char *destinationBucket, const char *destinationKey, - const S3PutProperties *putProperties, - int64_t *lastModifiedReturn, int eTagReturnSize, - char *eTagReturn, S3RequestContext *requestContext, - const S3ResponseHandler *handler, void *callbackData) + const char *destinationBucket, const char *destinationKey, + const S3PutProperties *putProperties, + int64_t *lastModifiedReturn, int eTagReturnSize, + char *eTagReturn, S3RequestContext *requestContext, + const S3ResponseHandler *handler, void *callbackData) { - // Create the callback data - CopyObjectData *data = - (CopyObjectData *) malloc(sizeof(CopyObjectData)); - if (!data) { - (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData); - return; - } - - simplexml_initialize(&(data->simpleXml), ©ObjectXmlCallback, data); - - data->responsePropertiesCallback = handler->propertiesCallback; - data->responseCompleteCallback = handler->completeCallback; - data->callbackData = callbackData; - - data->lastModifiedReturn = lastModifiedReturn; - data->eTagReturnSize = eTagReturnSize; - data->eTagReturn = eTagReturn; - if (data->eTagReturnSize && data->eTagReturn) { - data->eTagReturn[0] = 0; - } - data->eTagReturnLen = 0; - string_buffer_initialize(data->lastModified); - - // Set up the RequestParams - RequestParams params = - { - HttpRequestTypeCOPY, // httpRequestType - { destinationBucket ? destinationBucket : - bucketContext->bucketName, // bucketName - bucketContext->protocol, // protocol - bucketContext->uriStyle, // uriStyle - bucketContext->accessKeyId, // accessKeyId - bucketContext->secretAccessKey }, // secretAccessKey - destinationKey ? destinationKey : key, // key - 0, // queryParams - 0, // subResource - bucketContext->bucketName, // copySourceBucketName - key, // copySourceKey - 0, // getConditions - 0, // startByte - 0, // byteCount - putProperties, // putProperties - ©ObjectPropertiesCallback, // propertiesCallback - 0, // toS3Callback - 0, // toS3CallbackTotalSize - ©ObjectDataCallback, // fromS3Callback - ©ObjectCompleteCallback, // completeCallback - data // callbackData - }; - - // Perform the request - request_perform(¶ms, requestContext); + // Create the callback data + CopyObjectData *data = + (CopyObjectData *) malloc(sizeof(CopyObjectData)); + if (!data) { + (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData); + return; + } + + simplexml_initialize(&(data->simpleXml), ©ObjectXmlCallback, data); + + data->responsePropertiesCallback = handler->propertiesCallback; + data->responseCompleteCallback = handler->completeCallback; + data->callbackData = callbackData; + + data->lastModifiedReturn = lastModifiedReturn; + data->eTagReturnSize = eTagReturnSize; + data->eTagReturn = eTagReturn; + if (data->eTagReturnSize && data->eTagReturn) { + data->eTagReturn[0] = 0; + } + data->eTagReturnLen = 0; + string_buffer_initialize(data->lastModified); + + // Set up the RequestParams + RequestParams params = + { + HttpRequestTypeCOPY, // httpRequestType + { destinationBucket ? destinationBucket : + bucketContext->bucketName, // bucketName + bucketContext->protocol, // protocol + bucketContext->uriStyle, // uriStyle + bucketContext->accessKeyId, // accessKeyId + bucketContext->secretAccessKey }, // secretAccessKey + destinationKey ? destinationKey : key, // key + 0, // queryParams + 0, // subResource + bucketContext->bucketName, // copySourceBucketName + key, // copySourceKey + 0, // getConditions + 0, // startByte + 0, // byteCount + putProperties, // putProperties + ©ObjectPropertiesCallback, // propertiesCallback + 0, // toS3Callback + 0, // toS3CallbackTotalSize + ©ObjectDataCallback, // fromS3Callback + ©ObjectCompleteCallback, // completeCallback + data // callbackData + }; + + // Perform the request + request_perform(¶ms, requestContext); } // get object ---------------------------------------------------------------- void S3_get_object(const S3BucketContext *bucketContext, const char *key, - const S3GetConditions *getConditions, - uint64_t startByte, uint64_t byteCount, - S3RequestContext *requestContext, - const S3GetObjectHandler *handler, void *callbackData) + const S3GetConditions *getConditions, + uint64_t startByte, uint64_t byteCount, + S3RequestContext *requestContext, + const S3GetObjectHandler *handler, void *callbackData) { - // Set up the RequestParams - RequestParams params = - { - HttpRequestTypeGET, // httpRequestType - { bucketContext->bucketName, // bucketName - bucketContext->protocol, // protocol - bucketContext->uriStyle, // uriStyle - bucketContext->accessKeyId, // accessKeyId - bucketContext->secretAccessKey }, // secretAccessKey - key, // key - 0, // queryParams - 0, // subResource - 0, // copySourceBucketName - 0, // copySourceKey - getConditions, // getConditions - startByte, // startByte - byteCount, // byteCount - 0, // putProperties - handler->responseHandler.propertiesCallback, // propertiesCallback - 0, // toS3Callback - 0, // toS3CallbackTotalSize - handler->getObjectDataCallback, // fromS3Callback - handler->responseHandler.completeCallback, // completeCallback - callbackData // callbackData - }; - - // Perform the request - request_perform(¶ms, requestContext); + // Set up the RequestParams + RequestParams params = + { + HttpRequestTypeGET, // httpRequestType + { bucketContext->bucketName, // bucketName + bucketContext->protocol, // protocol + bucketContext->uriStyle, // uriStyle + bucketContext->accessKeyId, // accessKeyId + bucketContext->secretAccessKey }, // secretAccessKey + key, // key + 0, // queryParams + 0, // subResource + 0, // copySourceBucketName + 0, // copySourceKey + getConditions, // getConditions + startByte, // startByte + byteCount, // byteCount + 0, // putProperties + handler->responseHandler.propertiesCallback, // propertiesCallback + 0, // toS3Callback + 0, // toS3CallbackTotalSize + handler->getObjectDataCallback, // fromS3Callback + handler->responseHandler.completeCallback, // completeCallback + callbackData // callbackData + }; + + // Perform the request + request_perform(¶ms, requestContext); } // head object --------------------------------------------------------------- void S3_head_object(const S3BucketContext *bucketContext, const char *key, - S3RequestContext *requestContext, - const S3ResponseHandler *handler, void *callbackData) + S3RequestContext *requestContext, + const S3ResponseHandler *handler, void *callbackData) { - // Set up the RequestParams - RequestParams params = - { - HttpRequestTypeHEAD, // httpRequestType - { bucketContext->bucketName, // bucketName - bucketContext->protocol, // protocol - bucketContext->uriStyle, // uriStyle - bucketContext->accessKeyId, // accessKeyId - bucketContext->secretAccessKey }, // secretAccessKey - key, // key - 0, // queryParams - 0, // subResource - 0, // copySourceBucketName - 0, // copySourceKey - 0, // getConditions - 0, // startByte - 0, // byteCount - 0, // putProperties - handler->propertiesCallback, // propertiesCallback - 0, // toS3Callback - 0, // toS3CallbackTotalSize - 0, // fromS3Callback - handler->completeCallback, // completeCallback - callbackData // callbackData - }; - - // Perform the request - request_perform(¶ms, requestContext); + // Set up the RequestParams + RequestParams params = + { + HttpRequestTypeHEAD, // httpRequestType + { bucketContext->bucketName, // bucketName + bucketContext->protocol, // protocol + bucketContext->uriStyle, // uriStyle + bucketContext->accessKeyId, // accessKeyId + bucketContext->secretAccessKey }, // secretAccessKey + key, // key + 0, // queryParams + 0, // subResource + 0, // copySourceBucketName + 0, // copySourceKey + 0, // getConditions + 0, // startByte + 0, // byteCount + 0, // putProperties + handler->propertiesCallback, // propertiesCallback + 0, // toS3Callback + 0, // toS3CallbackTotalSize + 0, // fromS3Callback + handler->completeCallback, // completeCallback + callbackData // callbackData + }; + + // Perform the request + request_perform(¶ms, requestContext); } - + // delete object -------------------------------------------------------------- void S3_delete_object(const S3BucketContext *bucketContext, const char *key, - S3RequestContext *requestContext, - const S3ResponseHandler *handler, void *callbackData) + S3RequestContext *requestContext, + const S3ResponseHandler *handler, void *callbackData) { - // Set up the RequestParams - RequestParams params = - { - HttpRequestTypeDELETE, // httpRequestType - { bucketContext->bucketName, // bucketName - bucketContext->protocol, // protocol - bucketContext->uriStyle, // uriStyle - bucketContext->accessKeyId, // accessKeyId - bucketContext->secretAccessKey }, // secretAccessKey - key, // key - 0, // queryParams - 0, // subResource - 0, // copySourceBucketName - 0, // copySourceKey - 0, // getConditions - 0, // startByte - 0, // byteCount - 0, // putProperties - handler->propertiesCallback, // propertiesCallback - 0, // toS3Callback - 0, // toS3CallbackTotalSize - 0, // fromS3Callback - handler->completeCallback, // completeCallback - callbackData // callbackData - }; - - // Perform the request - request_perform(¶ms, requestContext); + // Set up the RequestParams + RequestParams params = + { + HttpRequestTypeDELETE, // httpRequestType + { bucketContext->bucketName, // bucketName + bucketContext->protocol, // protocol + bucketContext->uriStyle, // uriStyle + bucketContext->accessKeyId, // accessKeyId + bucketContext->secretAccessKey }, // secretAccessKey + key, // key + 0, // queryParams + 0, // subResource + 0, // copySourceBucketName + 0, // copySourceKey + 0, // getConditions + 0, // startByte + 0, // byteCount + 0, // putProperties + handler->propertiesCallback, // propertiesCallback + 0, // toS3Callback + 0, // toS3CallbackTotalSize + 0, // fromS3Callback + handler->completeCallback, // completeCallback + callbackData // callbackData + }; + + // Perform the request + request_perform(¶ms, requestContext); } diff --git a/src/request.c b/src/request.c index 05bf8fb..94a36bd 100644 --- a/src/request.c +++ b/src/request.c @@ -49,60 +49,60 @@ static int requestStackCountG; typedef struct RequestComputedValues { - // All x-amz- headers, in normalized form (i.e. NAME: VALUE, no other ws) - char *amzHeaders[S3_MAX_METADATA_COUNT + 2]; // + 2 for acl and date + // All x-amz- headers, in normalized form (i.e. NAME: VALUE, no other ws) + char *amzHeaders[S3_MAX_METADATA_COUNT + 2]; // + 2 for acl and date - // The number of x-amz- headers - int amzHeadersCount; + // The number of x-amz- headers + int amzHeadersCount; - // Storage for amzHeaders (the +256 is for x-amz-acl and x-amz-date) - char amzHeadersRaw[COMPACTED_METADATA_BUFFER_SIZE + 256 + 1]; + // Storage for amzHeaders (the +256 is for x-amz-acl and x-amz-date) + char amzHeadersRaw[COMPACTED_METADATA_BUFFER_SIZE + 256 + 1]; - // Canonicalized x-amz- headers - string_multibuffer(canonicalizedAmzHeaders, - COMPACTED_METADATA_BUFFER_SIZE + 256 + 1); + // Canonicalized x-amz- headers + string_multibuffer(canonicalizedAmzHeaders, + COMPACTED_METADATA_BUFFER_SIZE + 256 + 1); - // URL-Encoded key - char urlEncodedKey[MAX_URLENCODED_KEY_SIZE + 1]; + // URL-Encoded key + char urlEncodedKey[MAX_URLENCODED_KEY_SIZE + 1]; - // Canonicalized resource - char canonicalizedResource[MAX_CANONICALIZED_RESOURCE_SIZE + 1]; + // Canonicalized resource + char canonicalizedResource[MAX_CANONICALIZED_RESOURCE_SIZE + 1]; - // Cache-Control header (or empty) - char cacheControlHeader[128]; + // Cache-Control header (or empty) + char cacheControlHeader[128]; - // Content-Type header (or empty) - char contentTypeHeader[128]; + // Content-Type header (or empty) + char contentTypeHeader[128]; - // Content-MD5 header (or empty) - char md5Header[128]; + // Content-MD5 header (or empty) + char md5Header[128]; - // Content-Disposition header (or empty) - char contentDispositionHeader[128]; + // Content-Disposition header (or empty) + char contentDispositionHeader[128]; - // Content-Encoding header (or empty) - char contentEncodingHeader[128]; + // Content-Encoding header (or empty) + char contentEncodingHeader[128]; - // Expires header (or empty) - char expiresHeader[128]; + // Expires header (or empty) + char expiresHeader[128]; - // If-Modified-Since header - char ifModifiedSinceHeader[128]; + // If-Modified-Since header + char ifModifiedSinceHeader[128]; - // If-Unmodified-Since header - char ifUnmodifiedSinceHeader[128]; + // If-Unmodified-Since header + char ifUnmodifiedSinceHeader[128]; - // If-Match header - char ifMatchHeader[128]; + // If-Match header + char ifMatchHeader[128]; - // If-None-Match header - char ifNoneMatchHeader[128]; + // If-None-Match header + char ifNoneMatchHeader[128]; - // Range header - char rangeHeader[128]; + // Range header + char rangeHeader[128]; - // Authorization header - char authorizationHeader[128]; + // Authorization header + char authorizationHeader[128]; } RequestComputedValues; @@ -112,126 +112,126 @@ typedef struct RequestComputedValues // zero on failure. static void request_headers_done(Request *request) { - if (request->propertiesCallbackMade) { - return; - } - - request->propertiesCallbackMade = 1; - - // Get the http response code - long httpResponseCode; - request->httpResponseCode = 0; - if (curl_easy_getinfo(request->curl, CURLINFO_RESPONSE_CODE, - &httpResponseCode) != CURLE_OK) { - // Not able to get the HTTP response code - error - request->status = S3StatusInternalError; - return; - } - else { - request->httpResponseCode = httpResponseCode; - } - - response_headers_handler_done(&(request->responseHeadersHandler), - request->curl); - - // Only make the callback if it was a successful request; otherwise we're - // returning information about the error response itself - if (request->propertiesCallback && - (request->httpResponseCode >= 200) && - (request->httpResponseCode <= 299)) { - request->status = (*(request->propertiesCallback)) - (&(request->responseHeadersHandler.responseProperties), - request->callbackData); - } + if (request->propertiesCallbackMade) { + return; + } + + request->propertiesCallbackMade = 1; + + // Get the http response code + long httpResponseCode; + request->httpResponseCode = 0; + if (curl_easy_getinfo(request->curl, CURLINFO_RESPONSE_CODE, + &httpResponseCode) != CURLE_OK) { + // Not able to get the HTTP response code - error + request->status = S3StatusInternalError; + return; + } + else { + request->httpResponseCode = httpResponseCode; + } + + response_headers_handler_done(&(request->responseHeadersHandler), + request->curl); + + // Only make the callback if it was a successful request; otherwise we're + // returning information about the error response itself + if (request->propertiesCallback && + (request->httpResponseCode >= 200) && + (request->httpResponseCode <= 299)) { + request->status = (*(request->propertiesCallback)) + (&(request->responseHeadersHandler.responseProperties), + request->callbackData); + } } static size_t curl_header_func(void *ptr, size_t size, size_t nmemb, - void *data) + void *data) { - Request *request = (Request *) data; + Request *request = (Request *) data; - int len = size * nmemb; + int len = size * nmemb; - response_headers_handler_add - (&(request->responseHeadersHandler), (char *) ptr, len); + response_headers_handler_add + (&(request->responseHeadersHandler), (char *) ptr, len); - return len; + return len; } static size_t curl_read_func(void *ptr, size_t size, size_t nmemb, void *data) { - Request *request = (Request *) data; - - int len = size * nmemb; - - request_headers_done(request); - - if (request->status != S3StatusOK) { - return CURL_READFUNC_ABORT; - } - - // If there is no data callback, or the data callback has already returned - // contentLength bytes, return 0; - if (!request->toS3Callback || !request->toS3CallbackBytesRemaining) { - return 0; - } - - // Don't tell the callback that we are willing to accept more data than we - // really are - if (len > request->toS3CallbackBytesRemaining) { - len = request->toS3CallbackBytesRemaining; - } - - // Otherwise, make the data callback - int ret = (*(request->toS3Callback)) - (len, (char *) ptr, request->callbackData); - if (ret < 0) { - request->status = S3StatusAbortedByCallback; - return CURL_READFUNC_ABORT; - } - else { - if (ret > request->toS3CallbackBytesRemaining) { - ret = request->toS3CallbackBytesRemaining; - } - request->toS3CallbackBytesRemaining -= ret; - return ret; - } + Request *request = (Request *) data; + + int len = size * nmemb; + + request_headers_done(request); + + if (request->status != S3StatusOK) { + return CURL_READFUNC_ABORT; + } + + // If there is no data callback, or the data callback has already returned + // contentLength bytes, return 0; + if (!request->toS3Callback || !request->toS3CallbackBytesRemaining) { + return 0; + } + + // Don't tell the callback that we are willing to accept more data than we + // really are + if (len > request->toS3CallbackBytesRemaining) { + len = request->toS3CallbackBytesRemaining; + } + + // Otherwise, make the data callback + int ret = (*(request->toS3Callback)) + (len, (char *) ptr, request->callbackData); + if (ret < 0) { + request->status = S3StatusAbortedByCallback; + return CURL_READFUNC_ABORT; + } + else { + if (ret > request->toS3CallbackBytesRemaining) { + ret = request->toS3CallbackBytesRemaining; + } + request->toS3CallbackBytesRemaining -= ret; + return ret; + } } static size_t curl_write_func(void *ptr, size_t size, size_t nmemb, - void *data) + void *data) { - Request *request = (Request *) data; - - int len = size * nmemb; - - request_headers_done(request); - - if (request->status != S3StatusOK) { - return 0; - } - - // On HTTP error, we expect to parse an HTTP error response - if ((request->httpResponseCode < 200) || - (request->httpResponseCode > 299)) { - request->status = error_parser_add - (&(request->errorParser), (char *) ptr, len); - } - // If there was a callback registered, make it - else if (request->fromS3Callback) { - request->status = (*(request->fromS3Callback)) - (len, (char *) ptr, request->callbackData); - } - // Else, consider this an error - S3 has sent back data when it was not - // expected - else { - request->status = S3StatusInternalError; - } - - return ((request->status == S3StatusOK) ? len : 0); + Request *request = (Request *) data; + + int len = size * nmemb; + + request_headers_done(request); + + if (request->status != S3StatusOK) { + return 0; + } + + // On HTTP error, we expect to parse an HTTP error response + if ((request->httpResponseCode < 200) || + (request->httpResponseCode > 299)) { + request->status = error_parser_add + (&(request->errorParser), (char *) ptr, len); + } + // If there was a callback registered, make it + else if (request->fromS3Callback) { + request->status = (*(request->fromS3Callback)) + (len, (char *) ptr, request->callbackData); + } + // Else, consider this an error - S3 has sent back data when it was not + // expected + else { + request->status = S3StatusInternalError; + } + + return ((request->status == S3StatusOK) ? len : 0); } @@ -240,279 +240,279 @@ static size_t curl_write_func(void *ptr, size_t size, size_t nmemb, // them such that they all look exactly like this: // x-amz-meta-${NAME}: ${VALUE} // It also adds the x-amz-acl, x-amz-copy-source, and x-amz-metadata-directive -// headers if necessary, and always adds the x-amz-date header. It copies the +// headers if necessary, and always adds the x-amz-date header. It copies the // raw string values into params->amzHeadersRaw, and creates an array of // string pointers representing these headers in params->amzHeaders (and also // sets params->amzHeadersCount to be the count of the total number of x-amz- // headers thus created). static S3Status compose_amz_headers(const RequestParams *params, - RequestComputedValues *values) + RequestComputedValues *values) { - const S3PutProperties *properties = params->putProperties; - - values->amzHeadersCount = 0; - values->amzHeadersRaw[0] = 0; - int len = 0; - - // Append a header to amzHeaders, trimming whitespace from the end. - // Does NOT trim whitespace from the beginning. -#define headers_append(isNewHeader, format, ...) \ - do { \ - if (isNewHeader) { \ - values->amzHeaders[values->amzHeadersCount++] = \ - &(values->amzHeadersRaw[len]); \ - } \ - len += snprintf(&(values->amzHeadersRaw[len]), \ - sizeof(values->amzHeadersRaw) - len, \ - format, __VA_ARGS__); \ - if (len >= (int) sizeof(values->amzHeadersRaw)) { \ - return S3StatusMetaDataHeadersTooLong; \ - } \ - while ((len > 0) && (values->amzHeadersRaw[len - 1] == ' ')) { \ - len--; \ - } \ - values->amzHeadersRaw[len++] = 0; \ - } while (0) - -#define header_name_tolower_copy(str, l) \ - do { \ - values->amzHeaders[values->amzHeadersCount++] = \ - &(values->amzHeadersRaw[len]); \ - if ((len + l) >= (int) sizeof(values->amzHeadersRaw)) { \ - return S3StatusMetaDataHeadersTooLong; \ - } \ - int todo = l; \ - while (todo--) { \ - if ((*(str) >= 'A') && (*(str) <= 'Z')) { \ - values->amzHeadersRaw[len++] = 'a' + (*(str) - 'A'); \ - } \ - else { \ - values->amzHeadersRaw[len++] = *(str); \ - } \ - (str)++; \ - } \ - } while (0) - - // Check and copy in the x-amz-meta headers - if (properties) { - int i; - for (i = 0; i < properties->metaDataCount; i++) { - const S3NameValue *property = &(properties->metaData[i]); - char headerName[S3_MAX_METADATA_SIZE - sizeof(": v")]; - int l = snprintf(headerName, sizeof(headerName), - S3_METADATA_HEADER_NAME_PREFIX "%s", - property->name); - char *hn = headerName; - header_name_tolower_copy(hn, l); - // Copy in the value - headers_append(0, ": %s", property->value); - } - - // Add the x-amz-acl header, if necessary - const char *cannedAclString; - switch (params->putProperties->cannedAcl) { - case S3CannedAclPrivate: - cannedAclString = 0; - break; - case S3CannedAclPublicRead: - cannedAclString = "public-read"; - break; - case S3CannedAclPublicReadWrite: - cannedAclString = "public-read-write"; - break; - default: // S3CannedAclAuthenticatedRead - cannedAclString = "authenticated-read"; - break; - } - if (cannedAclString) { - headers_append(1, "x-amz-acl: %s", cannedAclString); - } - } - - // Add the x-amz-date header - time_t now = time(NULL); - char date[64]; - strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now)); - headers_append(1, "x-amz-date: %s", date); - - if (params->httpRequestType == HttpRequestTypeCOPY) { - // Add the x-amz-copy-source header - if (params->copySourceBucketName && params->copySourceBucketName[0] && - params->copySourceKey && params->copySourceKey[0]) { - headers_append(1, "x-amz-copy-source: /%s/%s", - params->copySourceBucketName, - params->copySourceKey); - } - // And the x-amz-metadata-directive header - if (params->putProperties) { - headers_append(1, "%s", "x-amz-metadata-directive: REPLACE"); - } - } - - return S3StatusOK; + const S3PutProperties *properties = params->putProperties; + + values->amzHeadersCount = 0; + values->amzHeadersRaw[0] = 0; + int len = 0; + + // Append a header to amzHeaders, trimming whitespace from the end. + // Does NOT trim whitespace from the beginning. +#define headers_append(isNewHeader, format, ...) \ + do { \ + if (isNewHeader) { \ + values->amzHeaders[values->amzHeadersCount++] = \ + &(values->amzHeadersRaw[len]); \ + } \ + len += snprintf(&(values->amzHeadersRaw[len]), \ + sizeof(values->amzHeadersRaw) - len, \ + format, __VA_ARGS__); \ + if (len >= (int) sizeof(values->amzHeadersRaw)) { \ + return S3StatusMetaDataHeadersTooLong; \ + } \ + while ((len > 0) && (values->amzHeadersRaw[len - 1] == ' ')) { \ + len--; \ + } \ + values->amzHeadersRaw[len++] = 0; \ + } while (0) + +#define header_name_tolower_copy(str, l) \ + do { \ + values->amzHeaders[values->amzHeadersCount++] = \ + &(values->amzHeadersRaw[len]); \ + if ((len + l) >= (int) sizeof(values->amzHeadersRaw)) { \ + return S3StatusMetaDataHeadersTooLong; \ + } \ + int todo = l; \ + while (todo--) { \ + if ((*(str) >= 'A') && (*(str) <= 'Z')) { \ + values->amzHeadersRaw[len++] = 'a' + (*(str) - 'A'); \ + } \ + else { \ + values->amzHeadersRaw[len++] = *(str); \ + } \ + (str)++; \ + } \ + } while (0) + + // Check and copy in the x-amz-meta headers + if (properties) { + int i; + for (i = 0; i < properties->metaDataCount; i++) { + const S3NameValue *property = &(properties->metaData[i]); + char headerName[S3_MAX_METADATA_SIZE - sizeof(": v")]; + int l = snprintf(headerName, sizeof(headerName), + S3_METADATA_HEADER_NAME_PREFIX "%s", + property->name); + char *hn = headerName; + header_name_tolower_copy(hn, l); + // Copy in the value + headers_append(0, ": %s", property->value); + } + + // Add the x-amz-acl header, if necessary + const char *cannedAclString; + switch (params->putProperties->cannedAcl) { + case S3CannedAclPrivate: + cannedAclString = 0; + break; + case S3CannedAclPublicRead: + cannedAclString = "public-read"; + break; + case S3CannedAclPublicReadWrite: + cannedAclString = "public-read-write"; + break; + default: // S3CannedAclAuthenticatedRead + cannedAclString = "authenticated-read"; + break; + } + if (cannedAclString) { + headers_append(1, "x-amz-acl: %s", cannedAclString); + } + } + + // Add the x-amz-date header + time_t now = time(NULL); + char date[64]; + strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now)); + headers_append(1, "x-amz-date: %s", date); + + if (params->httpRequestType == HttpRequestTypeCOPY) { + // Add the x-amz-copy-source header + if (params->copySourceBucketName && params->copySourceBucketName[0] && + params->copySourceKey && params->copySourceKey[0]) { + headers_append(1, "x-amz-copy-source: /%s/%s", + params->copySourceBucketName, + params->copySourceKey); + } + // And the x-amz-metadata-directive header + if (params->putProperties) { + headers_append(1, "%s", "x-amz-metadata-directive: REPLACE"); + } + } + + return S3StatusOK; } // Composes the other headers static S3Status compose_standard_headers(const RequestParams *params, - RequestComputedValues *values) + RequestComputedValues *values) { -#define do_put_header(fmt, sourceField, destField, badError, tooLongError) \ - do { \ - if (params->putProperties && \ - params->putProperties-> sourceField && \ - params->putProperties-> sourceField[0]) { \ - /* Skip whitespace at beginning of val */ \ - const char *val = params->putProperties-> sourceField; \ - while (*val && isblank(*val)) { \ - val++; \ - } \ - if (!*val) { \ - return badError; \ - } \ - /* Compose header, make sure it all fit */ \ - int len = snprintf(values-> destField, \ - sizeof(values-> destField), fmt, val); \ - if (len >= (int) sizeof(values-> destField)) { \ - return tooLongError; \ - } \ - /* Now remove the whitespace at the end */ \ - while (isblank(values-> destField[len])) { \ - len--; \ - } \ - values-> destField[len] = 0; \ - } \ - else { \ - values-> destField[0] = 0; \ - } \ - } while (0) - -#define do_get_header(fmt, sourceField, destField, badError, tooLongError) \ - do { \ - if (params->getConditions && \ - params->getConditions-> sourceField && \ - params->getConditions-> sourceField[0]) { \ - /* Skip whitespace at beginning of val */ \ - const char *val = params->getConditions-> sourceField; \ - while (*val && isblank(*val)) { \ - val++; \ - } \ - if (!*val) { \ - return badError; \ - } \ - /* Compose header, make sure it all fit */ \ - int len = snprintf(values-> destField, \ - sizeof(values-> destField), fmt, val); \ - if (len >= (int) sizeof(values-> destField)) { \ - return tooLongError; \ - } \ - /* Now remove the whitespace at the end */ \ - while (isblank(values-> destField[len])) { \ - len--; \ - } \ - values-> destField[len] = 0; \ - } \ - else { \ - values-> destField[0] = 0; \ - } \ - } while (0) - - // Cache-Control - do_put_header("Cache-Control: %s", cacheControl, cacheControlHeader, - S3StatusBadCacheControl, S3StatusCacheControlTooLong); - - // ContentType - do_put_header("Content-Type: %s", contentType, contentTypeHeader, - S3StatusBadContentType, S3StatusContentTypeTooLong); - - // MD5 - do_put_header("Content-MD5: %s", md5, md5Header, S3StatusBadMD5, - S3StatusMD5TooLong); - - // Content-Disposition - do_put_header("Content-Disposition: attachment; filename=\"%s\"", - contentDispositionFilename, contentDispositionHeader, - S3StatusBadContentDispositionFilename, - S3StatusContentDispositionFilenameTooLong); - - // ContentEncoding - do_put_header("Content-Encoding: %s", contentEncoding, - contentEncodingHeader, S3StatusBadContentEncoding, - S3StatusContentEncodingTooLong); - - // Expires - if (params->putProperties && (params->putProperties->expires >= 0)) { - time_t t = (time_t) params->putProperties->expires; - strftime(values->expiresHeader, sizeof(values->expiresHeader), - "Expires: %a, %d %b %Y %H:%M:%S UTC", gmtime(&t)); - } - else { - values->expiresHeader[0] = 0; - } - - // If-Modified-Since - if (params->getConditions && - (params->getConditions->ifModifiedSince >= 0)) { - time_t t = (time_t) params->getConditions->ifModifiedSince; - strftime(values->ifModifiedSinceHeader, - sizeof(values->ifModifiedSinceHeader), - "If-Modified-Since: %a, %d %b %Y %H:%M:%S UTC", gmtime(&t)); - } - else { - values->ifModifiedSinceHeader[0] = 0; - } - - // If-Unmodified-Since header - if (params->getConditions && - (params->getConditions->ifNotModifiedSince >= 0)) { - time_t t = (time_t) params->getConditions->ifNotModifiedSince; - strftime(values->ifUnmodifiedSinceHeader, - sizeof(values->ifUnmodifiedSinceHeader), - "If-Unmodified-Since: %a, %d %b %Y %H:%M:%S UTC", gmtime(&t)); - } - else { - values->ifUnmodifiedSinceHeader[0] = 0; - } - - // If-Match header - do_get_header("If-Match: %s", ifMatchETag, ifMatchHeader, - S3StatusBadIfMatchETag, S3StatusIfMatchETagTooLong); - - // If-None-Match header - do_get_header("If-None-Match: %s", ifNotMatchETag, ifNoneMatchHeader, - S3StatusBadIfNotMatchETag, - S3StatusIfNotMatchETagTooLong); - - // Range header - if (params->startByte || params->byteCount) { - if (params->byteCount) { - snprintf(values->rangeHeader, sizeof(values->rangeHeader), - "Range: bytes=%llu-%llu", - (unsigned long long) params->startByte, - (unsigned long long) (params->startByte + - params->byteCount - 1)); - } - else { - snprintf(values->rangeHeader, sizeof(values->rangeHeader), - "Range: bytes=%llu-", - (unsigned long long) params->startByte); - } - } - else { - values->rangeHeader[0] = 0; - } - - return S3StatusOK; +#define do_put_header(fmt, sourceField, destField, badError, tooLongError) \ + do { \ + if (params->putProperties && \ + params->putProperties-> sourceField && \ + params->putProperties-> sourceField[0]) { \ + /* Skip whitespace at beginning of val */ \ + const char *val = params->putProperties-> sourceField; \ + while (*val && isblank(*val)) { \ + val++; \ + } \ + if (!*val) { \ + return badError; \ + } \ + /* Compose header, make sure it all fit */ \ + int len = snprintf(values-> destField, \ + sizeof(values-> destField), fmt, val); \ + if (len >= (int) sizeof(values-> destField)) { \ + return tooLongError; \ + } \ + /* Now remove the whitespace at the end */ \ + while (isblank(values-> destField[len])) { \ + len--; \ + } \ + values-> destField[len] = 0; \ + } \ + else { \ + values-> destField[0] = 0; \ + } \ + } while (0) + +#define do_get_header(fmt, sourceField, destField, badError, tooLongError) \ + do { \ + if (params->getConditions && \ + params->getConditions-> sourceField && \ + params->getConditions-> sourceField[0]) { \ + /* Skip whitespace at beginning of val */ \ + const char *val = params->getConditions-> sourceField; \ + while (*val && isblank(*val)) { \ + val++; \ + } \ + if (!*val) { \ + return badError; \ + } \ + /* Compose header, make sure it all fit */ \ + int len = snprintf(values-> destField, \ + sizeof(values-> destField), fmt, val); \ + if (len >= (int) sizeof(values-> destField)) { \ + return tooLongError; \ + } \ + /* Now remove the whitespace at the end */ \ + while (isblank(values-> destField[len])) { \ + len--; \ + } \ + values-> destField[len] = 0; \ + } \ + else { \ + values-> destField[0] = 0; \ + } \ + } while (0) + + // Cache-Control + do_put_header("Cache-Control: %s", cacheControl, cacheControlHeader, + S3StatusBadCacheControl, S3StatusCacheControlTooLong); + + // ContentType + do_put_header("Content-Type: %s", contentType, contentTypeHeader, + S3StatusBadContentType, S3StatusContentTypeTooLong); + + // MD5 + do_put_header("Content-MD5: %s", md5, md5Header, S3StatusBadMD5, + S3StatusMD5TooLong); + + // Content-Disposition + do_put_header("Content-Disposition: attachment; filename=\"%s\"", + contentDispositionFilename, contentDispositionHeader, + S3StatusBadContentDispositionFilename, + S3StatusContentDispositionFilenameTooLong); + + // ContentEncoding + do_put_header("Content-Encoding: %s", contentEncoding, + contentEncodingHeader, S3StatusBadContentEncoding, + S3StatusContentEncodingTooLong); + + // Expires + if (params->putProperties && (params->putProperties->expires >= 0)) { + time_t t = (time_t) params->putProperties->expires; + strftime(values->expiresHeader, sizeof(values->expiresHeader), + "Expires: %a, %d %b %Y %H:%M:%S UTC", gmtime(&t)); + } + else { + values->expiresHeader[0] = 0; + } + + // If-Modified-Since + if (params->getConditions && + (params->getConditions->ifModifiedSince >= 0)) { + time_t t = (time_t) params->getConditions->ifModifiedSince; + strftime(values->ifModifiedSinceHeader, + sizeof(values->ifModifiedSinceHeader), + "If-Modified-Since: %a, %d %b %Y %H:%M:%S UTC", gmtime(&t)); + } + else { + values->ifModifiedSinceHeader[0] = 0; + } + + // If-Unmodified-Since header + if (params->getConditions && + (params->getConditions->ifNotModifiedSince >= 0)) { + time_t t = (time_t) params->getConditions->ifNotModifiedSince; + strftime(values->ifUnmodifiedSinceHeader, + sizeof(values->ifUnmodifiedSinceHeader), + "If-Unmodified-Since: %a, %d %b %Y %H:%M:%S UTC", gmtime(&t)); + } + else { + values->ifUnmodifiedSinceHeader[0] = 0; + } + + // If-Match header + do_get_header("If-Match: %s", ifMatchETag, ifMatchHeader, + S3StatusBadIfMatchETag, S3StatusIfMatchETagTooLong); + + // If-None-Match header + do_get_header("If-None-Match: %s", ifNotMatchETag, ifNoneMatchHeader, + S3StatusBadIfNotMatchETag, + S3StatusIfNotMatchETagTooLong); + + // Range header + if (params->startByte || params->byteCount) { + if (params->byteCount) { + snprintf(values->rangeHeader, sizeof(values->rangeHeader), + "Range: bytes=%llu-%llu", + (unsigned long long) params->startByte, + (unsigned long long) (params->startByte + + params->byteCount - 1)); + } + else { + snprintf(values->rangeHeader, sizeof(values->rangeHeader), + "Range: bytes=%llu-", + (unsigned long long) params->startByte); + } + } + else { + values->rangeHeader[0] = 0; + } + + return S3StatusOK; } // URL encodes the params->key value into params->urlEncodedKey static S3Status encode_key(const RequestParams *params, - RequestComputedValues *values) + RequestComputedValues *values) { - return (urlEncode(values->urlEncodedKey, params->key, S3_MAX_KEY_SIZE) ? - S3StatusOK : S3StatusUriTooLong); + return (urlEncode(values->urlEncodedKey, params->key, S3_MAX_KEY_SIZE) ? + S3StatusOK : S3StatusUriTooLong); } @@ -521,857 +521,857 @@ static S3Status encode_key(const RequestParams *params, // before header2 alphabetically, false if not static int headerle(const char *header1, const char *header2) { - while (1) { - if (*header1 == ':') { - return (*header2 == ':'); - } - else if (*header2 == ':') { - return 0; - } - else if (*header2 < *header1) { - return 0; - } - else if (*header2 > *header1) { - return 1; - } - header1++, header2++; - } + while (1) { + if (*header1 == ':') { + return (*header2 == ':'); + } + else if (*header2 == ':') { + return 0; + } + else if (*header2 < *header1) { + return 0; + } + else if (*header2 > *header1) { + return 1; + } + header1++, header2++; + } } -// Replace this with merge sort eventually, it's the best stable sort. But +// Replace this with merge sort eventually, it's the best stable sort. But // since typically the number of elements being sorted is small, it doesn't // matter that much which sort is used, and gnome sort is the world's simplest -// stable sort. Added a slight twist to the standard gnome_sort - don't go -// forward +1, go forward to the last highest index considered. This saves +// stable sort. Added a slight twist to the standard gnome_sort - don't go +// forward +1, go forward to the last highest index considered. This saves // all the string comparisons that would be done "going forward", and thus // only does the necessary string comparisons to move values back into their // sorted position. static void header_gnome_sort(const char **headers, int size) { - int i = 0, last_highest = 0; - - while (i < size) { - if ((i == 0) || headerle(headers[i - 1], headers[i])) { - i = ++last_highest; - } - else { - const char *tmp = headers[i]; - headers[i] = headers[i - 1]; - headers[--i] = tmp; - } - } + int i = 0, last_highest = 0; + + while (i < size) { + if ((i == 0) || headerle(headers[i - 1], headers[i])) { + i = ++last_highest; + } + else { + const char *tmp = headers[i]; + headers[i] = headers[i - 1]; + headers[--i] = tmp; + } + } } // Canonicalizes the x-amz- headers into the canonicalizedAmzHeaders buffer static void canonicalize_amz_headers(RequestComputedValues *values) { - // Make a copy of the headers that will be sorted - const char *sortedHeaders[S3_MAX_METADATA_COUNT]; - - memcpy(sortedHeaders, values->amzHeaders, - (values->amzHeadersCount * sizeof(sortedHeaders[0]))); - - // Now sort these - header_gnome_sort(sortedHeaders, values->amzHeadersCount); - - // Now copy this sorted list into the buffer, all the while: - // - folding repeated headers into single lines, and - // - folding multiple lines - // - removing the space after the colon - int lastHeaderLen = 0, i; - char *buffer = values->canonicalizedAmzHeaders; - for (i = 0; i < values->amzHeadersCount; i++) { - const char *header = sortedHeaders[i]; - const char *c = header; - // If the header names are the same, append the next value - if ((i > 0) && - !strncmp(header, sortedHeaders[i - 1], lastHeaderLen)) { - // Replacing the previous newline with a comma - *(buffer - 1) = ','; - // Skip the header name and space - c += (lastHeaderLen + 1); - } - // Else this is a new header - else { - // Copy in everything up to the space in the ": " - while (*c != ' ') { - *buffer++ = *c++; - } - // Save the header len since it's a new header - lastHeaderLen = c - header; - // Skip the space - c++; - } - // Now copy in the value, folding the lines - while (*c) { - // If c points to a \r\n[whitespace] sequence, then fold - // this newline out - if ((*c == '\r') && (*(c + 1) == '\n') && isblank(*(c + 2))) { - c += 3; - while (isblank(*c)) { - c++; - } - // Also, what has most recently been copied into buffer amy - // have been whitespace, and since we're folding whitespace - // out around this newline sequence, back buffer up over - // any whitespace it contains - while (isblank(*(buffer - 1))) { - buffer--; - } - continue; - } - *buffer++ = *c++; - } - // Finally, add the newline - *buffer++ = '\n'; - } - - // Terminate the buffer - *buffer = 0; + // Make a copy of the headers that will be sorted + const char *sortedHeaders[S3_MAX_METADATA_COUNT]; + + memcpy(sortedHeaders, values->amzHeaders, + (values->amzHeadersCount * sizeof(sortedHeaders[0]))); + + // Now sort these + header_gnome_sort(sortedHeaders, values->amzHeadersCount); + + // Now copy this sorted list into the buffer, all the while: + // - folding repeated headers into single lines, and + // - folding multiple lines + // - removing the space after the colon + int lastHeaderLen = 0, i; + char *buffer = values->canonicalizedAmzHeaders; + for (i = 0; i < values->amzHeadersCount; i++) { + const char *header = sortedHeaders[i]; + const char *c = header; + // If the header names are the same, append the next value + if ((i > 0) && + !strncmp(header, sortedHeaders[i - 1], lastHeaderLen)) { + // Replacing the previous newline with a comma + *(buffer - 1) = ','; + // Skip the header name and space + c += (lastHeaderLen + 1); + } + // Else this is a new header + else { + // Copy in everything up to the space in the ": " + while (*c != ' ') { + *buffer++ = *c++; + } + // Save the header len since it's a new header + lastHeaderLen = c - header; + // Skip the space + c++; + } + // Now copy in the value, folding the lines + while (*c) { + // If c points to a \r\n[whitespace] sequence, then fold + // this newline out + if ((*c == '\r') && (*(c + 1) == '\n') && isblank(*(c + 2))) { + c += 3; + while (isblank(*c)) { + c++; + } + // Also, what has most recently been copied into buffer amy + // have been whitespace, and since we're folding whitespace + // out around this newline sequence, back buffer up over + // any whitespace it contains + while (isblank(*(buffer - 1))) { + buffer--; + } + continue; + } + *buffer++ = *c++; + } + // Finally, add the newline + *buffer++ = '\n'; + } + + // Terminate the buffer + *buffer = 0; } // Canonicalizes the resource into params->canonicalizedResource static void canonicalize_resource(const char *bucketName, - const char *subResource, - const char *urlEncodedKey, - char *buffer) + const char *subResource, + const char *urlEncodedKey, + char *buffer) { - int len = 0; + int len = 0; - *buffer = 0; + *buffer = 0; #define append(str) len += sprintf(&(buffer[len]), "%s", str) - if (bucketName && bucketName[0]) { - buffer[len++] = '/'; - append(bucketName); - } + if (bucketName && bucketName[0]) { + buffer[len++] = '/'; + append(bucketName); + } - append("/"); + append("/"); - if (urlEncodedKey && urlEncodedKey[0]) { - append(urlEncodedKey); - } + if (urlEncodedKey && urlEncodedKey[0]) { + append(urlEncodedKey); + } - if (subResource && subResource[0]) { - append("?"); - append(subResource); - } + if (subResource && subResource[0]) { + append("?"); + append(subResource); + } } // Convert an HttpRequestType to an HTTP Verb string static const char *http_request_type_to_verb(HttpRequestType requestType) { - switch (requestType) { - case HttpRequestTypeGET: - return "GET"; - case HttpRequestTypeHEAD: - return "HEAD"; - case HttpRequestTypePUT: - case HttpRequestTypeCOPY: - return "PUT"; - default: // HttpRequestTypeDELETE - return "DELETE"; - } + switch (requestType) { + case HttpRequestTypeGET: + return "GET"; + case HttpRequestTypeHEAD: + return "HEAD"; + case HttpRequestTypePUT: + case HttpRequestTypeCOPY: + return "PUT"; + default: // HttpRequestTypeDELETE + return "DELETE"; + } } // Composes the Authorization header for the request static S3Status compose_auth_header(const RequestParams *params, - RequestComputedValues *values) + RequestComputedValues *values) { - // We allow for: - // 17 bytes for HTTP-Verb + \n - // 129 bytes for Content-MD5 + \n - // 129 bytes for Content-Type + \n - // 1 byte for empty Date + \n - // CanonicalizedAmzHeaders & CanonicalizedResource - char signbuf[17 + 129 + 129 + 1 + - (sizeof(values->canonicalizedAmzHeaders) - 1) + - (sizeof(values->canonicalizedResource) - 1) + 1]; - int len = 0; - -#define signbuf_append(format, ...) \ - len += snprintf(&(signbuf[len]), sizeof(signbuf) - len, \ - format, __VA_ARGS__) - - signbuf_append - ("%s\n", http_request_type_to_verb(params->httpRequestType)); - - // For MD5 and Content-Type, use the value in the actual header, because - // it's already been trimmed - signbuf_append("%s\n", values->md5Header[0] ? - &(values->md5Header[sizeof("Content-MD5: ") - 1]) : ""); - - signbuf_append - ("%s\n", values->contentTypeHeader[0] ? - &(values->contentTypeHeader[sizeof("Content-Type: ") - 1]) : ""); - - signbuf_append("%s", "\n"); // Date - we always use x-amz-date - - signbuf_append("%s", values->canonicalizedAmzHeaders); - - signbuf_append("%s", values->canonicalizedResource); - - // Generate an HMAC-SHA-1 of the signbuf - unsigned char hmac[20]; - - HMAC_SHA1(hmac, (unsigned char *) params->bucketContext.secretAccessKey, - strlen(params->bucketContext.secretAccessKey), - (unsigned char *) signbuf, len); - - // Now base-64 encode the results - char b64[((20 + 1) * 4) / 3]; - int b64Len = base64Encode(hmac, 20, b64); - - snprintf(values->authorizationHeader, sizeof(values->authorizationHeader), - "Authorization: AWS %s:%.*s", params->bucketContext.accessKeyId, - b64Len, b64); - - return S3StatusOK; + // We allow for: + // 17 bytes for HTTP-Verb + \n + // 129 bytes for Content-MD5 + \n + // 129 bytes for Content-Type + \n + // 1 byte for empty Date + \n + // CanonicalizedAmzHeaders & CanonicalizedResource + char signbuf[17 + 129 + 129 + 1 + + (sizeof(values->canonicalizedAmzHeaders) - 1) + + (sizeof(values->canonicalizedResource) - 1) + 1]; + int len = 0; + +#define signbuf_append(format, ...) \ + len += snprintf(&(signbuf[len]), sizeof(signbuf) - len, \ + format, __VA_ARGS__) + + signbuf_append + ("%s\n", http_request_type_to_verb(params->httpRequestType)); + + // For MD5 and Content-Type, use the value in the actual header, because + // it's already been trimmed + signbuf_append("%s\n", values->md5Header[0] ? + &(values->md5Header[sizeof("Content-MD5: ") - 1]) : ""); + + signbuf_append + ("%s\n", values->contentTypeHeader[0] ? + &(values->contentTypeHeader[sizeof("Content-Type: ") - 1]) : ""); + + signbuf_append("%s", "\n"); // Date - we always use x-amz-date + + signbuf_append("%s", values->canonicalizedAmzHeaders); + + signbuf_append("%s", values->canonicalizedResource); + + // Generate an HMAC-SHA-1 of the signbuf + unsigned char hmac[20]; + + HMAC_SHA1(hmac, (unsigned char *) params->bucketContext.secretAccessKey, + strlen(params->bucketContext.secretAccessKey), + (unsigned char *) signbuf, len); + + // Now base-64 encode the results + char b64[((20 + 1) * 4) / 3]; + int b64Len = base64Encode(hmac, 20, b64); + + snprintf(values->authorizationHeader, sizeof(values->authorizationHeader), + "Authorization: AWS %s:%.*s", params->bucketContext.accessKeyId, + b64Len, b64); + + return S3StatusOK; } // Compose the URI to use for the request given the request parameters static S3Status compose_uri(char *buffer, int bufferSize, - const S3BucketContext *bucketContext, - const char *urlEncodedKey, - const char *subResource, const char *queryParams) + const S3BucketContext *bucketContext, + const char *urlEncodedKey, + const char *subResource, const char *queryParams) { - int len = 0; - -#define uri_append(fmt, ...) \ - do { \ - len += snprintf(&(buffer[len]), bufferSize - len, fmt, __VA_ARGS__); \ - if (len >= bufferSize) { \ - return S3StatusUriTooLong; \ - } \ - } while (0) - - uri_append("http%s://", - (bucketContext->protocol == S3ProtocolHTTP) ? "" : "s"); - - if (bucketContext->bucketName && - bucketContext->bucketName[0]) { - if (bucketContext->uriStyle == S3UriStyleVirtualHost) { - uri_append("%s.s3.amazonaws.com", bucketContext->bucketName); - } - else { - uri_append("s3.amazonaws.com/%s", bucketContext->bucketName); - } - } - else { - uri_append("%s", "s3.amazonaws.com"); - } - - uri_append("%s", "/"); - - uri_append("%s", urlEncodedKey); - - if (subResource && subResource[0]) { - uri_append("?%s", subResource); - } - - if (queryParams) { - uri_append("%s%s", (subResource && subResource[0]) ? "&" : "?", - queryParams); - } - - return S3StatusOK; + int len = 0; + +#define uri_append(fmt, ...) \ + do { \ + len += snprintf(&(buffer[len]), bufferSize - len, fmt, __VA_ARGS__); \ + if (len >= bufferSize) { \ + return S3StatusUriTooLong; \ + } \ + } while (0) + + uri_append("http%s://", + (bucketContext->protocol == S3ProtocolHTTP) ? "" : "s"); + + if (bucketContext->bucketName && + bucketContext->bucketName[0]) { + if (bucketContext->uriStyle == S3UriStyleVirtualHost) { + uri_append("%s.s3.amazonaws.com", bucketContext->bucketName); + } + else { + uri_append("s3.amazonaws.com/%s", bucketContext->bucketName); + } + } + else { + uri_append("%s", "s3.amazonaws.com"); + } + + uri_append("%s", "/"); + + uri_append("%s", urlEncodedKey); + + if (subResource && subResource[0]) { + uri_append("?%s", subResource); + } + + if (queryParams) { + uri_append("%s%s", (subResource && subResource[0]) ? "&" : "?", + queryParams); + } + + return S3StatusOK; } // Sets up the curl handle given the completely computed RequestParams static S3Status setup_curl(Request *request, - const RequestParams *params, - const RequestComputedValues *values) + const RequestParams *params, + const RequestComputedValues *values) { - CURLcode status; - -#define curl_easy_setopt_safe(opt, val) \ - if ((status = curl_easy_setopt \ - (request->curl, opt, val)) != CURLE_OK) { \ - return S3StatusFailedToInitializeRequest; \ - } - - // Debugging only - // curl_easy_setopt_safe(CURLOPT_VERBOSE, 1); - - // Set private data to request for the benefit of S3RequestContext - curl_easy_setopt_safe(CURLOPT_PRIVATE, request); - - // Set header callback and data - curl_easy_setopt_safe(CURLOPT_HEADERDATA, request); - curl_easy_setopt_safe(CURLOPT_HEADERFUNCTION, &curl_header_func); - - // Set read callback, data, and readSize - curl_easy_setopt_safe(CURLOPT_READFUNCTION, &curl_read_func); - curl_easy_setopt_safe(CURLOPT_READDATA, request); - - // Set write callback and data - curl_easy_setopt_safe(CURLOPT_WRITEFUNCTION, &curl_write_func); - curl_easy_setopt_safe(CURLOPT_WRITEDATA, request); - - // Ask curl to parse the Last-Modified header. This is easier than - // parsing it ourselves. - curl_easy_setopt_safe(CURLOPT_FILETIME, 1); - - // Curl docs suggest that this is necessary for multithreaded code. - // However, it also points out that DNS timeouts will not be honored - // during DNS lookup, which can be worked around by using the c-ares - // library, which we do not do yet. - curl_easy_setopt_safe(CURLOPT_NOSIGNAL, 1); - - // Turn off Curl's built-in progress meter - curl_easy_setopt_safe(CURLOPT_NOPROGRESS, 1); - - // xxx todo - support setting the proxy for Curl to use (can't use https - // for proxies though) - - // xxx todo - support setting the network interface for Curl to use - - // I think this is useful - we don't need interactive performance, we need - // to complete large operations quickly - curl_easy_setopt_safe(CURLOPT_TCP_NODELAY, 1); - - // Don't use Curl's 'netrc' feature - curl_easy_setopt_safe(CURLOPT_NETRC, CURL_NETRC_IGNORED); - - // Don't verify S3's certificate, there are known to be issues with - // them sometimes - // xxx todo - support an option for verifying the S3 CA (default false) - curl_easy_setopt_safe(CURLOPT_SSL_VERIFYPEER, 0); - - // Follow any redirection directives that S3 sends - curl_easy_setopt_safe(CURLOPT_FOLLOWLOCATION, 1); - - // A safety valve in case S3 goes bananas with redirects - curl_easy_setopt_safe(CURLOPT_MAXREDIRS, 10); - - // Set the User-Agent; maybe Amazon will track these? - curl_easy_setopt_safe(CURLOPT_USERAGENT, userAgentG); - - // Set the low speed limit and time; we abort transfers that stay at - // less than 1K per second for more than 15 seconds. - // xxx todo - make these configurable - // xxx todo - allow configurable max send and receive speed - curl_easy_setopt_safe(CURLOPT_LOW_SPEED_LIMIT, 1024); - curl_easy_setopt_safe(CURLOPT_LOW_SPEED_TIME, 15); - - // Append standard headers -#define append_standard_header(fieldName) \ - if (values-> fieldName [0]) { \ - request->headers = curl_slist_append(request->headers, \ - values-> fieldName); \ - } - - // Would use CURLOPT_INFILESIZE_LARGE, but it is buggy in libcurl - if (params->httpRequestType == HttpRequestTypePUT) { - char header[256]; - snprintf(header, sizeof(header), "Content-Length: %llu", - (unsigned long long) params->toS3CallbackTotalSize); - request->headers = curl_slist_append(request->headers, header); - request->headers = curl_slist_append(request->headers, - "Transfer-Encoding:"); - } - else if (params->httpRequestType == HttpRequestTypeCOPY) { - request->headers = curl_slist_append(request->headers, - "Transfer-Encoding:"); - } - - append_standard_header(cacheControlHeader); - append_standard_header(contentTypeHeader); - append_standard_header(md5Header); - append_standard_header(contentDispositionHeader); - append_standard_header(contentEncodingHeader); - append_standard_header(expiresHeader); - append_standard_header(ifModifiedSinceHeader); - append_standard_header(ifUnmodifiedSinceHeader); - append_standard_header(ifMatchHeader); - append_standard_header(ifNoneMatchHeader); - append_standard_header(rangeHeader); - append_standard_header(authorizationHeader); - - // Append x-amz- headers - int i; - for (i = 0; i < values->amzHeadersCount; i++) { - request->headers = - curl_slist_append(request->headers, values->amzHeaders[i]); - } - - // Set the HTTP headers - curl_easy_setopt_safe(CURLOPT_HTTPHEADER, request->headers); - - // Set URI - curl_easy_setopt_safe(CURLOPT_URL, request->uri); - - // Set request type. - switch (params->httpRequestType) { - case HttpRequestTypeHEAD: + CURLcode status; + +#define curl_easy_setopt_safe(opt, val) \ + if ((status = curl_easy_setopt \ + (request->curl, opt, val)) != CURLE_OK) { \ + return S3StatusFailedToInitializeRequest; \ + } + + // Debugging only + // curl_easy_setopt_safe(CURLOPT_VERBOSE, 1); + + // Set private data to request for the benefit of S3RequestContext + curl_easy_setopt_safe(CURLOPT_PRIVATE, request); + + // Set header callback and data + curl_easy_setopt_safe(CURLOPT_HEADERDATA, request); + curl_easy_setopt_safe(CURLOPT_HEADERFUNCTION, &curl_header_func); + + // Set read callback, data, and readSize + curl_easy_setopt_safe(CURLOPT_READFUNCTION, &curl_read_func); + curl_easy_setopt_safe(CURLOPT_READDATA, request); + + // Set write callback and data + curl_easy_setopt_safe(CURLOPT_WRITEFUNCTION, &curl_write_func); + curl_easy_setopt_safe(CURLOPT_WRITEDATA, request); + + // Ask curl to parse the Last-Modified header. This is easier than + // parsing it ourselves. + curl_easy_setopt_safe(CURLOPT_FILETIME, 1); + + // Curl docs suggest that this is necessary for multithreaded code. + // However, it also points out that DNS timeouts will not be honored + // during DNS lookup, which can be worked around by using the c-ares + // library, which we do not do yet. + curl_easy_setopt_safe(CURLOPT_NOSIGNAL, 1); + + // Turn off Curl's built-in progress meter + curl_easy_setopt_safe(CURLOPT_NOPROGRESS, 1); + + // xxx todo - support setting the proxy for Curl to use (can't use https + // for proxies though) + + // xxx todo - support setting the network interface for Curl to use + + // I think this is useful - we don't need interactive performance, we need + // to complete large operations quickly + curl_easy_setopt_safe(CURLOPT_TCP_NODELAY, 1); + + // Don't use Curl's 'netrc' feature + curl_easy_setopt_safe(CURLOPT_NETRC, CURL_NETRC_IGNORED); + + // Don't verify S3's certificate, there are known to be issues with + // them sometimes + // xxx todo - support an option for verifying the S3 CA (default false) + curl_easy_setopt_safe(CURLOPT_SSL_VERIFYPEER, 0); + + // Follow any redirection directives that S3 sends + curl_easy_setopt_safe(CURLOPT_FOLLOWLOCATION, 1); + + // A safety valve in case S3 goes bananas with redirects + curl_easy_setopt_safe(CURLOPT_MAXREDIRS, 10); + + // Set the User-Agent; maybe Amazon will track these? + curl_easy_setopt_safe(CURLOPT_USERAGENT, userAgentG); + + // Set the low speed limit and time; we abort transfers that stay at + // less than 1K per second for more than 15 seconds. + // xxx todo - make these configurable + // xxx todo - allow configurable max send and receive speed + curl_easy_setopt_safe(CURLOPT_LOW_SPEED_LIMIT, 1024); + curl_easy_setopt_safe(CURLOPT_LOW_SPEED_TIME, 15); + + // Append standard headers +#define append_standard_header(fieldName) \ + if (values-> fieldName [0]) { \ + request->headers = curl_slist_append(request->headers, \ + values-> fieldName); \ + } + + // Would use CURLOPT_INFILESIZE_LARGE, but it is buggy in libcurl + if (params->httpRequestType == HttpRequestTypePUT) { + char header[256]; + snprintf(header, sizeof(header), "Content-Length: %llu", + (unsigned long long) params->toS3CallbackTotalSize); + request->headers = curl_slist_append(request->headers, header); + request->headers = curl_slist_append(request->headers, + "Transfer-Encoding:"); + } + else if (params->httpRequestType == HttpRequestTypeCOPY) { + request->headers = curl_slist_append(request->headers, + "Transfer-Encoding:"); + } + + append_standard_header(cacheControlHeader); + append_standard_header(contentTypeHeader); + append_standard_header(md5Header); + append_standard_header(contentDispositionHeader); + append_standard_header(contentEncodingHeader); + append_standard_header(expiresHeader); + append_standard_header(ifModifiedSinceHeader); + append_standard_header(ifUnmodifiedSinceHeader); + append_standard_header(ifMatchHeader); + append_standard_header(ifNoneMatchHeader); + append_standard_header(rangeHeader); + append_standard_header(authorizationHeader); + + // Append x-amz- headers + int i; + for (i = 0; i < values->amzHeadersCount; i++) { + request->headers = + curl_slist_append(request->headers, values->amzHeaders[i]); + } + + // Set the HTTP headers + curl_easy_setopt_safe(CURLOPT_HTTPHEADER, request->headers); + + // Set URI + curl_easy_setopt_safe(CURLOPT_URL, request->uri); + + // Set request type. + switch (params->httpRequestType) { + case HttpRequestTypeHEAD: curl_easy_setopt_safe(CURLOPT_NOBODY, 1); - break; - case HttpRequestTypePUT: - case HttpRequestTypeCOPY: - curl_easy_setopt_safe(CURLOPT_UPLOAD, 1); - break; - case HttpRequestTypeDELETE: + break; + case HttpRequestTypePUT: + case HttpRequestTypeCOPY: + curl_easy_setopt_safe(CURLOPT_UPLOAD, 1); + break; + case HttpRequestTypeDELETE: curl_easy_setopt_safe(CURLOPT_CUSTOMREQUEST, "DELETE"); - break; - default: // HttpRequestTypeGET - break; - } - - return S3StatusOK; + break; + default: // HttpRequestTypeGET + break; + } + + return S3StatusOK; } static void request_deinitialize(Request *request) { - if (request->headers) { - curl_slist_free_all(request->headers); - } - - error_parser_deinitialize(&(request->errorParser)); - - // curl_easy_reset prevents connections from being re-used for some - // reason. This makes HTTP Keep-Alive meaningless and is very bad for - // performance. But it is necessary to allow curl to work properly. - // xxx todo figure out why - curl_easy_reset(request->curl); + if (request->headers) { + curl_slist_free_all(request->headers); + } + + error_parser_deinitialize(&(request->errorParser)); + + // curl_easy_reset prevents connections from being re-used for some + // reason. This makes HTTP Keep-Alive meaningless and is very bad for + // performance. But it is necessary to allow curl to work properly. + // xxx todo figure out why + curl_easy_reset(request->curl); } static S3Status request_get(const RequestParams *params, - const RequestComputedValues *values, - Request **reqReturn) + const RequestComputedValues *values, + Request **reqReturn) { - Request *request = 0; - - // Try to get one from the request stack. We hold the lock for the - // shortest time possible here. - pthread_mutex_lock(&requestStackMutexG); - - if (requestStackCountG) { - request = requestStackG[--requestStackCountG]; - } - - pthread_mutex_unlock(&requestStackMutexG); - - // If we got one, deinitialize it for re-use - if (request) { - request_deinitialize(request); - } - // Else there wasn't one available in the request stack, so create one - else { - if (!(request = (Request *) malloc(sizeof(Request)))) { - return S3StatusOutOfMemory; - } - if (!(request->curl = curl_easy_init())) { - free(request); - return S3StatusFailedToInitializeRequest; - } - } - - // Initialize the request - request->prev = 0; - request->next = 0; - - // Request status is initialized to no error, will be updated whenever - // an error occurs - request->status = S3StatusOK; - - S3Status status; - - // Start out with no headers - request->headers = 0; - - // Compute the URL - if ((status = compose_uri - (request->uri, sizeof(request->uri), - &(params->bucketContext), values->urlEncodedKey, - params->subResource, params->queryParams)) != S3StatusOK) { - curl_easy_cleanup(request->curl); - free(request); - return status; - } - - // Set all of the curl handle options - if ((status = setup_curl(request, params, values)) != S3StatusOK) { - curl_easy_cleanup(request->curl); - free(request); - return status; - } - - request->propertiesCallback = params->propertiesCallback; - - request->toS3Callback = params->toS3Callback; - - request->toS3CallbackBytesRemaining = params->toS3CallbackTotalSize; - - request->fromS3Callback = params->fromS3Callback; - - request->completeCallback = params->completeCallback; - - request->callbackData = params->callbackData; - - response_headers_handler_initialize(&(request->responseHeadersHandler)); - - request->propertiesCallbackMade = 0; - - error_parser_initialize(&(request->errorParser)); - - *reqReturn = request; - - return S3StatusOK; + Request *request = 0; + + // Try to get one from the request stack. We hold the lock for the + // shortest time possible here. + pthread_mutex_lock(&requestStackMutexG); + + if (requestStackCountG) { + request = requestStackG[--requestStackCountG]; + } + + pthread_mutex_unlock(&requestStackMutexG); + + // If we got one, deinitialize it for re-use + if (request) { + request_deinitialize(request); + } + // Else there wasn't one available in the request stack, so create one + else { + if (!(request = (Request *) malloc(sizeof(Request)))) { + return S3StatusOutOfMemory; + } + if (!(request->curl = curl_easy_init())) { + free(request); + return S3StatusFailedToInitializeRequest; + } + } + + // Initialize the request + request->prev = 0; + request->next = 0; + + // Request status is initialized to no error, will be updated whenever + // an error occurs + request->status = S3StatusOK; + + S3Status status; + + // Start out with no headers + request->headers = 0; + + // Compute the URL + if ((status = compose_uri + (request->uri, sizeof(request->uri), + &(params->bucketContext), values->urlEncodedKey, + params->subResource, params->queryParams)) != S3StatusOK) { + curl_easy_cleanup(request->curl); + free(request); + return status; + } + + // Set all of the curl handle options + if ((status = setup_curl(request, params, values)) != S3StatusOK) { + curl_easy_cleanup(request->curl); + free(request); + return status; + } + + request->propertiesCallback = params->propertiesCallback; + + request->toS3Callback = params->toS3Callback; + + request->toS3CallbackBytesRemaining = params->toS3CallbackTotalSize; + + request->fromS3Callback = params->fromS3Callback; + + request->completeCallback = params->completeCallback; + + request->callbackData = params->callbackData; + + response_headers_handler_initialize(&(request->responseHeadersHandler)); + + request->propertiesCallbackMade = 0; + + error_parser_initialize(&(request->errorParser)); + + *reqReturn = request; + + return S3StatusOK; } static void request_destroy(Request *request) { - request_deinitialize(request); - curl_easy_cleanup(request->curl); - free(request); + request_deinitialize(request); + curl_easy_cleanup(request->curl); + free(request); } static void request_release(Request *request) { - pthread_mutex_lock(&requestStackMutexG); - - // If the request stack is full, destroy this one - if (requestStackCountG == REQUEST_STACK_SIZE) { - pthread_mutex_unlock(&requestStackMutexG); - request_destroy(request); - } - // Else put this one at the front of the request stack; we do this because - // we want the most-recently-used curl handle to be re-used on the next - // request, to maximize our chances of re-using a TCP connection before it - // times out - else { - requestStackG[requestStackCountG++] = request; - pthread_mutex_unlock(&requestStackMutexG); - } + pthread_mutex_lock(&requestStackMutexG); + + // If the request stack is full, destroy this one + if (requestStackCountG == REQUEST_STACK_SIZE) { + pthread_mutex_unlock(&requestStackMutexG); + request_destroy(request); + } + // Else put this one at the front of the request stack; we do this because + // we want the most-recently-used curl handle to be re-used on the next + // request, to maximize our chances of re-using a TCP connection before it + // times out + else { + requestStackG[requestStackCountG++] = request; + pthread_mutex_unlock(&requestStackMutexG); + } } S3Status request_api_initialize(const char *userAgentInfo, int flags) { - if (curl_global_init(CURL_GLOBAL_ALL & - ~((flags & S3_INIT_WINSOCK) ? 0 : CURL_GLOBAL_WIN32)) - != CURLE_OK) { - return S3StatusInternalError; - } - - pthread_mutex_init(&requestStackMutexG, 0); - - requestStackCountG = 0; - - if (!userAgentInfo || !*userAgentInfo) { - userAgentInfo = "Unknown"; - } - - char platform[96]; - struct utsname utsn; - if (uname(&utsn)) { - strncpy(platform, "Unknown", sizeof(platform)); - // Because strncpy doesn't always zero terminate - platform[sizeof(platform) - 1] = 0; - } - else { - snprintf(platform, sizeof(platform), "%s%s%s", utsn.sysname, - utsn.machine[0] ? " " : "", utsn.machine); - } - - snprintf(userAgentG, sizeof(userAgentG), - "Mozilla/4.0 (Compatible; %s; libs3 %s.%s; %s)", - userAgentInfo, LIBS3_VER_MAJOR, LIBS3_VER_MINOR, platform); - - return S3StatusOK; + if (curl_global_init(CURL_GLOBAL_ALL & + ~((flags & S3_INIT_WINSOCK) ? 0 : CURL_GLOBAL_WIN32)) + != CURLE_OK) { + return S3StatusInternalError; + } + + pthread_mutex_init(&requestStackMutexG, 0); + + requestStackCountG = 0; + + if (!userAgentInfo || !*userAgentInfo) { + userAgentInfo = "Unknown"; + } + + char platform[96]; + struct utsname utsn; + if (uname(&utsn)) { + strncpy(platform, "Unknown", sizeof(platform)); + // Because strncpy doesn't always zero terminate + platform[sizeof(platform) - 1] = 0; + } + else { + snprintf(platform, sizeof(platform), "%s%s%s", utsn.sysname, + utsn.machine[0] ? " " : "", utsn.machine); + } + + snprintf(userAgentG, sizeof(userAgentG), + "Mozilla/4.0 (Compatible; %s; libs3 %s.%s; %s)", + userAgentInfo, LIBS3_VER_MAJOR, LIBS3_VER_MINOR, platform); + + return S3StatusOK; } void request_api_deinitialize() { - pthread_mutex_destroy(&requestStackMutexG); + pthread_mutex_destroy(&requestStackMutexG); - while (requestStackCountG--) { - request_destroy(requestStackG[requestStackCountG]); - } + while (requestStackCountG--) { + request_destroy(requestStackG[requestStackCountG]); + } } void request_perform(const RequestParams *params, S3RequestContext *context) { - Request *request; - S3Status status; - -#define return_status(status) \ - (*(params->completeCallback))(status, 0, params->callbackData); \ - return - - // These will hold the computed values - RequestComputedValues computed; - - // Validate the bucket name - if (params->bucketContext.bucketName && - ((status = S3_validate_bucket_name - (params->bucketContext.bucketName, - params->bucketContext.uriStyle)) != S3StatusOK)) { - return_status(status); - } - - // Compose the amz headers - if ((status = compose_amz_headers(params, &computed)) != S3StatusOK) { - return_status(status); - } - - // Compose standard headers - if ((status = compose_standard_headers - (params, &computed)) != S3StatusOK) { - return_status(status); - } - - // URL encode the key - if ((status = encode_key(params, &computed)) != S3StatusOK) { - return_status(status); - } - - // Compute the canonicalized amz headers - canonicalize_amz_headers(&computed); - - // Compute the canonicalized resource - canonicalize_resource(params->bucketContext.bucketName, - params->subResource, computed.urlEncodedKey, - computed.canonicalizedResource); - - // Compose Authorization header - if ((status = compose_auth_header(params, &computed)) != S3StatusOK) { - return_status(status); - } - - // Get an initialized Request structure now - if ((status = request_get(params, &computed, &request)) != S3StatusOK) { - return_status(status); - } - - // If a RequestContext was provided, add the request to the curl multi - if (context) { - CURLMcode code = curl_multi_add_handle(context->curlm, request->curl); - if (code == CURLM_OK) { - if (context->requests) { - request->prev = context->requests->prev; - request->next = context->requests; - context->requests->prev->next = request; - context->requests->prev = request; - } - else { - context->requests = request->next = request->prev = request; - } - } - else { - if (request->status == S3StatusOK) { - request->status = (code == CURLM_OUT_OF_MEMORY) ? - S3StatusOutOfMemory : S3StatusInternalError; - } - request_finish(request); - } - } - // Else, perform the request immediately - else { - CURLcode code = curl_easy_perform(request->curl); - if ((code != CURLE_OK) && (request->status == S3StatusOK)) { - request->status = request_curl_code_to_status(code); - } - // Finish the request, ensuring that all callbacks have been made, and - // also releases the request - request_finish(request); - } + Request *request; + S3Status status; + +#define return_status(status) \ + (*(params->completeCallback))(status, 0, params->callbackData); \ + return + + // These will hold the computed values + RequestComputedValues computed; + + // Validate the bucket name + if (params->bucketContext.bucketName && + ((status = S3_validate_bucket_name + (params->bucketContext.bucketName, + params->bucketContext.uriStyle)) != S3StatusOK)) { + return_status(status); + } + + // Compose the amz headers + if ((status = compose_amz_headers(params, &computed)) != S3StatusOK) { + return_status(status); + } + + // Compose standard headers + if ((status = compose_standard_headers + (params, &computed)) != S3StatusOK) { + return_status(status); + } + + // URL encode the key + if ((status = encode_key(params, &computed)) != S3StatusOK) { + return_status(status); + } + + // Compute the canonicalized amz headers + canonicalize_amz_headers(&computed); + + // Compute the canonicalized resource + canonicalize_resource(params->bucketContext.bucketName, + params->subResource, computed.urlEncodedKey, + computed.canonicalizedResource); + + // Compose Authorization header + if ((status = compose_auth_header(params, &computed)) != S3StatusOK) { + return_status(status); + } + + // Get an initialized Request structure now + if ((status = request_get(params, &computed, &request)) != S3StatusOK) { + return_status(status); + } + + // If a RequestContext was provided, add the request to the curl multi + if (context) { + CURLMcode code = curl_multi_add_handle(context->curlm, request->curl); + if (code == CURLM_OK) { + if (context->requests) { + request->prev = context->requests->prev; + request->next = context->requests; + context->requests->prev->next = request; + context->requests->prev = request; + } + else { + context->requests = request->next = request->prev = request; + } + } + else { + if (request->status == S3StatusOK) { + request->status = (code == CURLM_OUT_OF_MEMORY) ? + S3StatusOutOfMemory : S3StatusInternalError; + } + request_finish(request); + } + } + // Else, perform the request immediately + else { + CURLcode code = curl_easy_perform(request->curl); + if ((code != CURLE_OK) && (request->status == S3StatusOK)) { + request->status = request_curl_code_to_status(code); + } + // Finish the request, ensuring that all callbacks have been made, and + // also releases the request + request_finish(request); + } } void request_finish(Request *request) { - // If we haven't detected this already, we now know that the headers are - // definitely done being read in - request_headers_done(request); - - // If there was no error processing the request, then possibly there was - // an S3 error parsed, which should be converted into the request status - if (request->status == S3StatusOK) { - error_parser_convert_status(&(request->errorParser), - &(request->status)); - // If there still was no error recorded, then it is possible that - // there was in fact an error but that there was no error XML - // detailing the error - if ((request->status == S3StatusOK) && - ((request->httpResponseCode < 200) || - (request->httpResponseCode > 299))) { - switch (request->httpResponseCode) { - case 0: - // This happens if the request never got any HTTP response - // headers at all, we call this a ConnectionFailed error - request->status = S3StatusConnectionFailed; - break; - case 100: // Some versions of libcurl erroneously set HTTP - // status to this - break; - case 301: - request->status = S3StatusErrorPermanentRedirect; - break; - case 307: - request->status = S3StatusHttpErrorMovedTemporarily; - break; - case 400: - request->status = S3StatusHttpErrorBadRequest; - break; - case 403: - request->status = S3StatusHttpErrorForbidden; - break; - case 404: - request->status = S3StatusHttpErrorNotFound; - break; - case 405: - request->status = S3StatusErrorMethodNotAllowed; - break; - case 409: - request->status = S3StatusHttpErrorConflict; - break; - case 411: - request->status = S3StatusErrorMissingContentLength; - break; - case 412: - request->status = S3StatusErrorPreconditionFailed; - break; - case 416: - request->status = S3StatusErrorInvalidRange; - break; - case 500: - request->status = S3StatusErrorInternalError; - break; - case 501: - request->status = S3StatusErrorNotImplemented; - break; - case 503: - request->status = S3StatusErrorSlowDown; - break; - default: - request->status = S3StatusHttpErrorUnknown; - break; - } - } - } - - (*(request->completeCallback)) - (request->status, &(request->errorParser.s3ErrorDetails), - request->callbackData); - - request_release(request); + // If we haven't detected this already, we now know that the headers are + // definitely done being read in + request_headers_done(request); + + // If there was no error processing the request, then possibly there was + // an S3 error parsed, which should be converted into the request status + if (request->status == S3StatusOK) { + error_parser_convert_status(&(request->errorParser), + &(request->status)); + // If there still was no error recorded, then it is possible that + // there was in fact an error but that there was no error XML + // detailing the error + if ((request->status == S3StatusOK) && + ((request->httpResponseCode < 200) || + (request->httpResponseCode > 299))) { + switch (request->httpResponseCode) { + case 0: + // This happens if the request never got any HTTP response + // headers at all, we call this a ConnectionFailed error + request->status = S3StatusConnectionFailed; + break; + case 100: // Some versions of libcurl erroneously set HTTP + // status to this + break; + case 301: + request->status = S3StatusErrorPermanentRedirect; + break; + case 307: + request->status = S3StatusHttpErrorMovedTemporarily; + break; + case 400: + request->status = S3StatusHttpErrorBadRequest; + break; + case 403: + request->status = S3StatusHttpErrorForbidden; + break; + case 404: + request->status = S3StatusHttpErrorNotFound; + break; + case 405: + request->status = S3StatusErrorMethodNotAllowed; + break; + case 409: + request->status = S3StatusHttpErrorConflict; + break; + case 411: + request->status = S3StatusErrorMissingContentLength; + break; + case 412: + request->status = S3StatusErrorPreconditionFailed; + break; + case 416: + request->status = S3StatusErrorInvalidRange; + break; + case 500: + request->status = S3StatusErrorInternalError; + break; + case 501: + request->status = S3StatusErrorNotImplemented; + break; + case 503: + request->status = S3StatusErrorSlowDown; + break; + default: + request->status = S3StatusHttpErrorUnknown; + break; + } + } + } + + (*(request->completeCallback)) + (request->status, &(request->errorParser.s3ErrorDetails), + request->callbackData); + + request_release(request); } S3Status request_curl_code_to_status(CURLcode code) { - switch (code) { - case CURLE_OUT_OF_MEMORY: - return S3StatusOutOfMemory; - case CURLE_COULDNT_RESOLVE_PROXY: - case CURLE_COULDNT_RESOLVE_HOST: - return S3StatusNameLookupError; - case CURLE_COULDNT_CONNECT: - return S3StatusFailedToConnect; - case CURLE_WRITE_ERROR: - case CURLE_OPERATION_TIMEDOUT: - return S3StatusConnectionFailed; - case CURLE_PARTIAL_FILE: - return S3StatusOK; - case CURLE_SSL_CACERT: - return S3StatusServerFailedVerification; - default: - return S3StatusInternalError; - } + switch (code) { + case CURLE_OUT_OF_MEMORY: + return S3StatusOutOfMemory; + case CURLE_COULDNT_RESOLVE_PROXY: + case CURLE_COULDNT_RESOLVE_HOST: + return S3StatusNameLookupError; + case CURLE_COULDNT_CONNECT: + return S3StatusFailedToConnect; + case CURLE_WRITE_ERROR: + case CURLE_OPERATION_TIMEDOUT: + return S3StatusConnectionFailed; + case CURLE_PARTIAL_FILE: + return S3StatusOK; + case CURLE_SSL_CACERT: + return S3StatusServerFailedVerification; + default: + return S3StatusInternalError; + } } S3Status S3_generate_authenticated_query_string - (char *buffer, const S3BucketContext *bucketContext, - const char *key, int64_t expires, const char *resource) + (char *buffer, const S3BucketContext *bucketContext, + const char *key, int64_t expires, const char *resource) { #define MAX_EXPIRES (((int64_t) 1 << 31) - 1) - // S3 seems to only accept expiration dates up to the number of seconds - // representably by a signed 32-bit integer - if (expires < 0) { - expires = MAX_EXPIRES; - } - else if (expires > MAX_EXPIRES) { - expires = MAX_EXPIRES; - } - - // xxx todo: rework this so that it can be incorporated into shared code - // with request_perform(). It's really unfortunate that this code is not - // shared with request_perform(). - - // URL encode the key - char urlEncodedKey[S3_MAX_KEY_SIZE * 3]; - if (key) { - urlEncode(urlEncodedKey, key, strlen(key)); - } - else { - urlEncodedKey[0] = 0; - } - - // Compute canonicalized resource - char canonicalizedResource[MAX_CANONICALIZED_RESOURCE_SIZE]; - canonicalize_resource(bucketContext->bucketName, resource, urlEncodedKey, - canonicalizedResource); - - // We allow for: - // 17 bytes for HTTP-Verb + \n - // 1 byte for empty Content-MD5 + \n - // 1 byte for empty Content-Type + \n - // 20 bytes for Expires + \n - // 0 bytes for CanonicalizedAmzHeaders - // CanonicalizedResource - char signbuf[17 + 1 + 1 + 1 + 20 + sizeof(canonicalizedResource) + 1]; - int len = 0; - -#define signbuf_append(format, ...) \ - len += snprintf(&(signbuf[len]), sizeof(signbuf) - len, \ - format, __VA_ARGS__) - - signbuf_append("%s\n", "GET"); // HTTP-Verb - signbuf_append("%s\n", ""); // Content-MD5 - signbuf_append("%s\n", ""); // Content-Type - signbuf_append("%llu\n", (unsigned long long) expires); - signbuf_append("%s", canonicalizedResource); - - // Generate an HMAC-SHA-1 of the signbuf - unsigned char hmac[20]; - - HMAC_SHA1(hmac, (unsigned char *) bucketContext->secretAccessKey, - strlen(bucketContext->secretAccessKey), - (unsigned char *) signbuf, len); - - // Now base-64 encode the results - char b64[((20 + 1) * 4) / 3]; - int b64Len = base64Encode(hmac, 20, b64); - - // Now urlEncode that - char signature[sizeof(b64) * 3]; - urlEncode(signature, b64, b64Len); - - // Finally, compose the uri, with params: - // ?AWSAccessKeyId=xxx[&Expires=]&Signature=xxx - char queryParams[sizeof("AWSAccessKeyId=") + 20 + - sizeof("&Expires=") + 20 + - sizeof("&Signature=") + sizeof(signature) + 1]; - - sprintf(queryParams, "AWSAccessKeyId=%s&Expires=%ld&Signature=%s", - bucketContext->accessKeyId, (long) expires, signature); - - return compose_uri(buffer, S3_MAX_AUTHENTICATED_QUERY_STRING_SIZE, - bucketContext, urlEncodedKey, resource, queryParams); + // S3 seems to only accept expiration dates up to the number of seconds + // representably by a signed 32-bit integer + if (expires < 0) { + expires = MAX_EXPIRES; + } + else if (expires > MAX_EXPIRES) { + expires = MAX_EXPIRES; + } + + // xxx todo: rework this so that it can be incorporated into shared code + // with request_perform(). It's really unfortunate that this code is not + // shared with request_perform(). + + // URL encode the key + char urlEncodedKey[S3_MAX_KEY_SIZE * 3]; + if (key) { + urlEncode(urlEncodedKey, key, strlen(key)); + } + else { + urlEncodedKey[0] = 0; + } + + // Compute canonicalized resource + char canonicalizedResource[MAX_CANONICALIZED_RESOURCE_SIZE]; + canonicalize_resource(bucketContext->bucketName, resource, urlEncodedKey, + canonicalizedResource); + + // We allow for: + // 17 bytes for HTTP-Verb + \n + // 1 byte for empty Content-MD5 + \n + // 1 byte for empty Content-Type + \n + // 20 bytes for Expires + \n + // 0 bytes for CanonicalizedAmzHeaders + // CanonicalizedResource + char signbuf[17 + 1 + 1 + 1 + 20 + sizeof(canonicalizedResource) + 1]; + int len = 0; + +#define signbuf_append(format, ...) \ + len += snprintf(&(signbuf[len]), sizeof(signbuf) - len, \ + format, __VA_ARGS__) + + signbuf_append("%s\n", "GET"); // HTTP-Verb + signbuf_append("%s\n", ""); // Content-MD5 + signbuf_append("%s\n", ""); // Content-Type + signbuf_append("%llu\n", (unsigned long long) expires); + signbuf_append("%s", canonicalizedResource); + + // Generate an HMAC-SHA-1 of the signbuf + unsigned char hmac[20]; + + HMAC_SHA1(hmac, (unsigned char *) bucketContext->secretAccessKey, + strlen(bucketContext->secretAccessKey), + (unsigned char *) signbuf, len); + + // Now base-64 encode the results + char b64[((20 + 1) * 4) / 3]; + int b64Len = base64Encode(hmac, 20, b64); + + // Now urlEncode that + char signature[sizeof(b64) * 3]; + urlEncode(signature, b64, b64Len); + + // Finally, compose the uri, with params: + // ?AWSAccessKeyId=xxx[&Expires=]&Signature=xxx + char queryParams[sizeof("AWSAccessKeyId=") + 20 + + sizeof("&Expires=") + 20 + + sizeof("&Signature=") + sizeof(signature) + 1]; + + sprintf(queryParams, "AWSAccessKeyId=%s&Expires=%ld&Signature=%s", + bucketContext->accessKeyId, (long) expires, signature); + + return compose_uri(buffer, S3_MAX_AUTHENTICATED_QUERY_STRING_SIZE, + bucketContext, urlEncodedKey, resource, queryParams); } diff --git a/src/request_context.c b/src/request_context.c index bccaee8..ed4c185 100644 --- a/src/request_context.c +++ b/src/request_context.c @@ -33,158 +33,158 @@ S3Status S3_create_request_context(S3RequestContext **requestContextReturn) { - *requestContextReturn = - (S3RequestContext *) malloc(sizeof(S3RequestContext)); - - if (!*requestContextReturn) { - return S3StatusOutOfMemory; - } - - if (!((*requestContextReturn)->curlm = curl_multi_init())) { - free(*requestContextReturn); - return S3StatusOutOfMemory; - } - - (*requestContextReturn)->requests = 0; - - return S3StatusOK; + *requestContextReturn = + (S3RequestContext *) malloc(sizeof(S3RequestContext)); + + if (!*requestContextReturn) { + return S3StatusOutOfMemory; + } + + if (!((*requestContextReturn)->curlm = curl_multi_init())) { + free(*requestContextReturn); + return S3StatusOutOfMemory; + } + + (*requestContextReturn)->requests = 0; + + return S3StatusOK; } void S3_destroy_request_context(S3RequestContext *requestContext) { - curl_multi_cleanup(requestContext->curlm); - - // For each request in the context, call back its done method with - // 'interrupted' status - Request *r = requestContext->requests, *rFirst = r; - - if (r) do { - r->status = S3StatusInterrupted; - Request *rNext = r->next; - request_finish(r); - r = rNext; - } while (r != rFirst); - - free(requestContext); + curl_multi_cleanup(requestContext->curlm); + + // For each request in the context, call back its done method with + // 'interrupted' status + Request *r = requestContext->requests, *rFirst = r; + + if (r) do { + r->status = S3StatusInterrupted; + Request *rNext = r->next; + request_finish(r); + r = rNext; + } while (r != rFirst); + + free(requestContext); } S3Status S3_runall_request_context(S3RequestContext *requestContext) { - int requestsRemaining; - do { - fd_set readfds, writefds, exceptfds; - FD_ZERO(&readfds); - FD_ZERO(&writefds); - FD_ZERO(&exceptfds); - int maxfd; - S3Status status = S3_get_request_context_fdsets - (requestContext, &readfds, &writefds, &exceptfds, &maxfd); - if (status != S3StatusOK) { - return status; - } - // curl will return -1 if it hasn't even created any fds yet because - // none of the connections have started yet. In this case, don't - // do the select at all, because it will wait forever; instead, just - // skip it and go straight to running the underlying CURL handles - if (maxfd != -1) { - int64_t timeout = S3_get_request_context_timeout(requestContext); - struct timeval tv = { timeout / 1000, (timeout % 1000) * 1000 }; - select(maxfd + 1, &readfds, &writefds, &exceptfds, - (timeout == -1) ? 0 : &tv); - } - status = S3_runonce_request_context(requestContext, - &requestsRemaining); - if (status != S3StatusOK) { - return status; - } - } while (requestsRemaining); - - return S3StatusOK; + int requestsRemaining; + do { + fd_set readfds, writefds, exceptfds; + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&exceptfds); + int maxfd; + S3Status status = S3_get_request_context_fdsets + (requestContext, &readfds, &writefds, &exceptfds, &maxfd); + if (status != S3StatusOK) { + return status; + } + // curl will return -1 if it hasn't even created any fds yet because + // none of the connections have started yet. In this case, don't + // do the select at all, because it will wait forever; instead, just + // skip it and go straight to running the underlying CURL handles + if (maxfd != -1) { + int64_t timeout = S3_get_request_context_timeout(requestContext); + struct timeval tv = { timeout / 1000, (timeout % 1000) * 1000 }; + select(maxfd + 1, &readfds, &writefds, &exceptfds, + (timeout == -1) ? 0 : &tv); + } + status = S3_runonce_request_context(requestContext, + &requestsRemaining); + if (status != S3StatusOK) { + return status; + } + } while (requestsRemaining); + + return S3StatusOK; } S3Status S3_runonce_request_context(S3RequestContext *requestContext, - int *requestsRemainingReturn) + int *requestsRemainingReturn) { - CURLMcode status; - - do { - status = curl_multi_perform(requestContext->curlm, - requestsRemainingReturn); - - switch (status) { - case CURLM_OK: - case CURLM_CALL_MULTI_PERFORM: - break; - case CURLM_OUT_OF_MEMORY: - return S3StatusOutOfMemory; - default: - return S3StatusInternalError; - } - - CURLMsg *msg; - int junk; - while ((msg = curl_multi_info_read(requestContext->curlm, &junk))) { - if (msg->msg != CURLMSG_DONE) { - return S3StatusInternalError; - } - Request *request; - if (curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, - (char **) &request) != CURLE_OK) { - return S3StatusInternalError; - } - // Remove the request from the list of requests - if (request->prev == request->next) { - // It was the only one on the list - requestContext->requests = 0; - } - else { - // It doesn't matter what the order of them are, so just in - // case request was at the head of the list, put the one after - // request to the head of the list - requestContext->requests = request->next; - request->prev->next = request->next; - request->next->prev = request->prev; - } - if ((msg->data.result != CURLE_OK) && - (request->status == S3StatusOK)) { - request->status = request_curl_code_to_status - (msg->data.result); - } - if (curl_multi_remove_handle(requestContext->curlm, - msg->easy_handle) != CURLM_OK) { - return S3StatusInternalError; - } - // Finish the request, ensuring that all callbacks have been made, - // and also releases the request - request_finish(request); - // Now, since a callback was made, there may be new requests - // queued up to be performed immediately, so do so - status = CURLM_CALL_MULTI_PERFORM; - } - } while (status == CURLM_CALL_MULTI_PERFORM); - - return S3StatusOK; + CURLMcode status; + + do { + status = curl_multi_perform(requestContext->curlm, + requestsRemainingReturn); + + switch (status) { + case CURLM_OK: + case CURLM_CALL_MULTI_PERFORM: + break; + case CURLM_OUT_OF_MEMORY: + return S3StatusOutOfMemory; + default: + return S3StatusInternalError; + } + + CURLMsg *msg; + int junk; + while ((msg = curl_multi_info_read(requestContext->curlm, &junk))) { + if (msg->msg != CURLMSG_DONE) { + return S3StatusInternalError; + } + Request *request; + if (curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, + (char **) &request) != CURLE_OK) { + return S3StatusInternalError; + } + // Remove the request from the list of requests + if (request->prev == request->next) { + // It was the only one on the list + requestContext->requests = 0; + } + else { + // It doesn't matter what the order of them are, so just in + // case request was at the head of the list, put the one after + // request to the head of the list + requestContext->requests = request->next; + request->prev->next = request->next; + request->next->prev = request->prev; + } + if ((msg->data.result != CURLE_OK) && + (request->status == S3StatusOK)) { + request->status = request_curl_code_to_status + (msg->data.result); + } + if (curl_multi_remove_handle(requestContext->curlm, + msg->easy_handle) != CURLM_OK) { + return S3StatusInternalError; + } + // Finish the request, ensuring that all callbacks have been made, + // and also releases the request + request_finish(request); + // Now, since a callback was made, there may be new requests + // queued up to be performed immediately, so do so + status = CURLM_CALL_MULTI_PERFORM; + } + } while (status == CURLM_CALL_MULTI_PERFORM); + + return S3StatusOK; } S3Status S3_get_request_context_fdsets(S3RequestContext *requestContext, - fd_set *readFdSet, fd_set *writeFdSet, - fd_set *exceptFdSet, int *maxFd) + fd_set *readFdSet, fd_set *writeFdSet, + fd_set *exceptFdSet, int *maxFd) { - return ((curl_multi_fdset(requestContext->curlm, readFdSet, writeFdSet, - exceptFdSet, maxFd) == CURLM_OK) ? - S3StatusOK : S3StatusInternalError); + return ((curl_multi_fdset(requestContext->curlm, readFdSet, writeFdSet, + exceptFdSet, maxFd) == CURLM_OK) ? + S3StatusOK : S3StatusInternalError); } int64_t S3_get_request_context_timeout(S3RequestContext *requestContext) { - long timeout; + long timeout; - if (curl_multi_timeout(requestContext->curlm, &timeout) != CURLM_OK) { - timeout = 0; - } - - return timeout; + if (curl_multi_timeout(requestContext->curlm, &timeout) != CURLM_OK) { + timeout = 0; + } + + return timeout; } diff --git a/src/response_headers_handler.c b/src/response_headers_handler.c index 14e14a0..c190f0d 100644 --- a/src/response_headers_handler.c +++ b/src/response_headers_handler.c @@ -31,175 +31,175 @@ void response_headers_handler_initialize(ResponseHeadersHandler *handler) { - handler->responseProperties.requestId = 0; - handler->responseProperties.requestId2 = 0; - handler->responseProperties.contentType = 0; - handler->responseProperties.contentLength = 0; - handler->responseProperties.server = 0; - handler->responseProperties.eTag = 0; - handler->responseProperties.lastModified = -1; - handler->responseProperties.metaDataCount = 0; - handler->responseProperties.metaData = 0; - handler->done = 0; - string_multibuffer_initialize(handler->responsePropertyStrings); - string_multibuffer_initialize(handler->responseMetaDataStrings); + handler->responseProperties.requestId = 0; + handler->responseProperties.requestId2 = 0; + handler->responseProperties.contentType = 0; + handler->responseProperties.contentLength = 0; + handler->responseProperties.server = 0; + handler->responseProperties.eTag = 0; + handler->responseProperties.lastModified = -1; + handler->responseProperties.metaDataCount = 0; + handler->responseProperties.metaData = 0; + handler->done = 0; + string_multibuffer_initialize(handler->responsePropertyStrings); + string_multibuffer_initialize(handler->responseMetaDataStrings); } void response_headers_handler_add(ResponseHeadersHandler *handler, - char *header, int len) + char *header, int len) { - S3ResponseProperties *responseProperties = &(handler->responseProperties); - char *end = &(header[len]); - - // Curl might call back the header function after the body has been - // received, for 'chunked encoded' contents. We don't handle this as of - // yet, and it's not clear that it would ever be useful. - if (handler->done) { - return; - } - - // If we've already filled up the response headers, ignore this data. - // This sucks, but it shouldn't happen - S3 should not be sending back - // really long headers. - if (handler->responsePropertyStringsSize == - (sizeof(handler->responsePropertyStrings) - 1)) { - return; - } - - // It should not be possible to have a header line less than 3 long - if (len < 3) { - return; - } - - // Skip whitespace at beginning of header; there never should be any, - // but just to be safe - while (isblank(*header)) { - header++; - } - - // The header must end in \r\n, so skip back over it, and also over any - // trailing whitespace - end -= 3; - while ((end > header) && isblank(*end)) { - end--; - } - if (!isblank(*end)) { - end++; - } - - if (end == header) { - // totally bogus - return; - } - - *end = 0; - - // Find the colon to split the header up - char *c = header; - while (*c && (*c != ':')) { - c++; - } - - int namelen = c - header; - - // Now walk c past the colon - c++; - // Now skip whitespace to the beginning of the value - while (isblank(*c)) { - c++; - } - - int valuelen = (end - c) + 1, fit; - - if (!strncmp(header, "x-amz-request-id", namelen)) { - responseProperties->requestId = - string_multibuffer_current(handler->responsePropertyStrings); - string_multibuffer_add(handler->responsePropertyStrings, c, - valuelen, fit); - } - else if (!strncmp(header, "x-amz-id-2", namelen)) { - responseProperties->requestId2 = - string_multibuffer_current(handler->responsePropertyStrings); - string_multibuffer_add(handler->responsePropertyStrings, c, - valuelen, fit); - } - else if (!strncmp(header, "Content-Type", namelen)) { - responseProperties->contentType = - string_multibuffer_current(handler->responsePropertyStrings); - string_multibuffer_add(handler->responsePropertyStrings, c, - valuelen, fit); - } - else if (!strncmp(header, "Content-Length", namelen)) { - handler->responseProperties.contentLength = 0; - while (*c) { - handler->responseProperties.contentLength *= 10; - handler->responseProperties.contentLength += (*c++ - '0'); - } - } - else if (!strncmp(header, "Server", namelen)) { - responseProperties->server = - string_multibuffer_current(handler->responsePropertyStrings); - string_multibuffer_add(handler->responsePropertyStrings, c, - valuelen, fit); - } - else if (!strncmp(header, "ETag", namelen)) { - responseProperties->eTag = - string_multibuffer_current(handler->responsePropertyStrings); - string_multibuffer_add(handler->responsePropertyStrings, c, - valuelen, fit); - } - else if (!strncmp(header, S3_METADATA_HEADER_NAME_PREFIX, - sizeof(S3_METADATA_HEADER_NAME_PREFIX) - 1)) { - // Make sure there is room for another x-amz-meta header - if (handler->responseProperties.metaDataCount == - sizeof(handler->responseMetaData)) { - return; - } - // Copy the name in - char *metaName = &(header[sizeof(S3_METADATA_HEADER_NAME_PREFIX) - 1]); - int metaNameLen = - (namelen - (sizeof(S3_METADATA_HEADER_NAME_PREFIX) - 1)); - char *copiedName = - string_multibuffer_current(handler->responseMetaDataStrings); - string_multibuffer_add(handler->responseMetaDataStrings, metaName, - metaNameLen, fit); - if (!fit) { - return; - } - - // Copy the value in - char *copiedValue = - string_multibuffer_current(handler->responseMetaDataStrings); - string_multibuffer_add(handler->responseMetaDataStrings, - c, valuelen, fit); - if (!fit) { - return; - } - - if (!handler->responseProperties.metaDataCount) { - handler->responseProperties.metaData = - handler->responseMetaData; - } - - S3NameValue *metaHeader = - &(handler->responseMetaData - [handler->responseProperties.metaDataCount++]); - metaHeader->name = copiedName; - metaHeader->value = copiedValue; - } + S3ResponseProperties *responseProperties = &(handler->responseProperties); + char *end = &(header[len]); + + // Curl might call back the header function after the body has been + // received, for 'chunked encoded' contents. We don't handle this as of + // yet, and it's not clear that it would ever be useful. + if (handler->done) { + return; + } + + // If we've already filled up the response headers, ignore this data. + // This sucks, but it shouldn't happen - S3 should not be sending back + // really long headers. + if (handler->responsePropertyStringsSize == + (sizeof(handler->responsePropertyStrings) - 1)) { + return; + } + + // It should not be possible to have a header line less than 3 long + if (len < 3) { + return; + } + + // Skip whitespace at beginning of header; there never should be any, + // but just to be safe + while (isblank(*header)) { + header++; + } + + // The header must end in \r\n, so skip back over it, and also over any + // trailing whitespace + end -= 3; + while ((end > header) && isblank(*end)) { + end--; + } + if (!isblank(*end)) { + end++; + } + + if (end == header) { + // totally bogus + return; + } + + *end = 0; + + // Find the colon to split the header up + char *c = header; + while (*c && (*c != ':')) { + c++; + } + + int namelen = c - header; + + // Now walk c past the colon + c++; + // Now skip whitespace to the beginning of the value + while (isblank(*c)) { + c++; + } + + int valuelen = (end - c) + 1, fit; + + if (!strncmp(header, "x-amz-request-id", namelen)) { + responseProperties->requestId = + string_multibuffer_current(handler->responsePropertyStrings); + string_multibuffer_add(handler->responsePropertyStrings, c, + valuelen, fit); + } + else if (!strncmp(header, "x-amz-id-2", namelen)) { + responseProperties->requestId2 = + string_multibuffer_current(handler->responsePropertyStrings); + string_multibuffer_add(handler->responsePropertyStrings, c, + valuelen, fit); + } + else if (!strncmp(header, "Content-Type", namelen)) { + responseProperties->contentType = + string_multibuffer_current(handler->responsePropertyStrings); + string_multibuffer_add(handler->responsePropertyStrings, c, + valuelen, fit); + } + else if (!strncmp(header, "Content-Length", namelen)) { + handler->responseProperties.contentLength = 0; + while (*c) { + handler->responseProperties.contentLength *= 10; + handler->responseProperties.contentLength += (*c++ - '0'); + } + } + else if (!strncmp(header, "Server", namelen)) { + responseProperties->server = + string_multibuffer_current(handler->responsePropertyStrings); + string_multibuffer_add(handler->responsePropertyStrings, c, + valuelen, fit); + } + else if (!strncmp(header, "ETag", namelen)) { + responseProperties->eTag = + string_multibuffer_current(handler->responsePropertyStrings); + string_multibuffer_add(handler->responsePropertyStrings, c, + valuelen, fit); + } + else if (!strncmp(header, S3_METADATA_HEADER_NAME_PREFIX, + sizeof(S3_METADATA_HEADER_NAME_PREFIX) - 1)) { + // Make sure there is room for another x-amz-meta header + if (handler->responseProperties.metaDataCount == + sizeof(handler->responseMetaData)) { + return; + } + // Copy the name in + char *metaName = &(header[sizeof(S3_METADATA_HEADER_NAME_PREFIX) - 1]); + int metaNameLen = + (namelen - (sizeof(S3_METADATA_HEADER_NAME_PREFIX) - 1)); + char *copiedName = + string_multibuffer_current(handler->responseMetaDataStrings); + string_multibuffer_add(handler->responseMetaDataStrings, metaName, + metaNameLen, fit); + if (!fit) { + return; + } + + // Copy the value in + char *copiedValue = + string_multibuffer_current(handler->responseMetaDataStrings); + string_multibuffer_add(handler->responseMetaDataStrings, + c, valuelen, fit); + if (!fit) { + return; + } + + if (!handler->responseProperties.metaDataCount) { + handler->responseProperties.metaData = + handler->responseMetaData; + } + + S3NameValue *metaHeader = + &(handler->responseMetaData + [handler->responseProperties.metaDataCount++]); + metaHeader->name = copiedName; + metaHeader->value = copiedValue; + } } void response_headers_handler_done(ResponseHeadersHandler *handler, CURL *curl) { - // Now get the last modification time from curl, since it's easiest to let - // curl parse it - time_t lastModified; - if (curl_easy_getinfo - (curl, CURLINFO_FILETIME, &lastModified) == CURLE_OK) { - handler->responseProperties.lastModified = lastModified; - } - - handler->done = 1; + // Now get the last modification time from curl, since it's easiest to let + // curl parse it + time_t lastModified; + if (curl_easy_getinfo + (curl, CURLINFO_FILETIME, &lastModified) == CURLE_OK) { + handler->responseProperties.lastModified = lastModified; + } + + handler->done = 1; } @@ -102,7 +102,7 @@ static char errorDetailsG[4096] = { 0 }; #define MD5_PREFIX_LEN (sizeof(MD5_PREFIX) - 1) #define CONTENT_DISPOSITION_FILENAME_PREFIX "contentDispositionFilename=" #define CONTENT_DISPOSITION_FILENAME_PREFIX_LEN \ - (sizeof(CONTENT_DISPOSITION_FILENAME_PREFIX) - 1) + (sizeof(CONTENT_DISPOSITION_FILENAME_PREFIX) - 1) #define CONTENT_ENCODING_PREFIX "contentEncoding=" #define CONTENT_ENCODING_PREFIX_LEN (sizeof(CONTENT_ENCODING_PREFIX) - 1) #define EXPIRES_PREFIX "expires=" @@ -113,7 +113,7 @@ static char errorDetailsG[4096] = { 0 }; #define IF_MODIFIED_SINCE_PREFIX_LEN (sizeof(IF_MODIFIED_SINCE_PREFIX) - 1) #define IF_NOT_MODIFIED_SINCE_PREFIX "ifNotmodifiedSince=" #define IF_NOT_MODIFIED_SINCE_PREFIX_LEN \ - (sizeof(IF_NOT_MODIFIED_SINCE_PREFIX) - 1) + (sizeof(IF_NOT_MODIFIED_SINCE_PREFIX) - 1) #define IF_MATCH_PREFIX "ifMatch=" #define IF_MATCH_PREFIX_LEN (sizeof(IF_MATCH_PREFIX) - 1) #define IF_NOT_MATCH_PREFIX "ifNotMatch=" @@ -138,176 +138,176 @@ static char errorDetailsG[4096] = { 0 }; static void S3_init() { - S3Status status; - if ((status = S3_initialize("s3", S3_INIT_ALL)) - != S3StatusOK) { - fprintf(stderr, "Failed to initialize libs3: %s\n", - S3_get_status_name(status)); - exit(-1); - } + S3Status status; + if ((status = S3_initialize("s3", S3_INIT_ALL)) + != S3StatusOK) { + fprintf(stderr, "Failed to initialize libs3: %s\n", + S3_get_status_name(status)); + exit(-1); + } } static void printError() { - if (statusG < S3StatusErrorAccessDenied) { - fprintf(stderr, "\nERROR: %s\n", S3_get_status_name(statusG)); - } - else { - fprintf(stderr, "\nERROR: %s\n", S3_get_status_name(statusG)); - fprintf(stderr, "%s\n", errorDetailsG); - } + if (statusG < S3StatusErrorAccessDenied) { + fprintf(stderr, "\nERROR: %s\n", S3_get_status_name(statusG)); + } + else { + fprintf(stderr, "\nERROR: %s\n", S3_get_status_name(statusG)); + fprintf(stderr, "%s\n", errorDetailsG); + } } static void usageExit(FILE *out) { - fprintf(out, + fprintf(out, "\n Options:\n" "\n" -" Command Line:\n" +" Command Line:\n" "\n" -" -f/--force : force operation despite warnings\n" -" -h/--vhost-style : use virtual-host-style URIs (default is " - "path-style)\n" -" -u/--unencrypted : unencrypted (use HTTP instead of HTTPS)\n" -" -s/--show-properties : show response properties on stdout\n" -" -r/--retries : retry retryable failures this number of times\n" -" (default is 5)\n" +" -f/--force : force operation despite warnings\n" +" -h/--vhost-style : use virtual-host-style URIs (default is " + "path-style)\n" +" -u/--unencrypted : unencrypted (use HTTP instead of HTTPS)\n" +" -s/--show-properties : show response properties on stdout\n" +" -r/--retries : retry retryable failures this number of times\n" +" (default is 5)\n" "\n" -" Environment:\n" +" Environment:\n" "\n" -" S3_ACCESS_KEY_ID : S3 access key ID (required)\n" -" S3_SECRET_ACCESS_KEY : S3 secret access key (required)\n" +" S3_ACCESS_KEY_ID : S3 access key ID (required)\n" +" S3_SECRET_ACCESS_KEY : S3 secret access key (required)\n" "\n" " Commands (with <required parameters> and [optional parameters]) :\n" "\n" -" (NOTE: all command parameters take a value and are specified using the\n" -" pattern parameter=value)\n" +" (NOTE: all command parameters take a value and are specified using the\n" +" pattern parameter=value)\n" "\n" -" help : Prints this help text\n" +" help : Prints this help text\n" "\n" -" list : Lists owned buckets\n" -" [allDetails] : Show full details\n" +" list : Lists owned buckets\n" +" [allDetails] : Show full details\n" "\n" -" test : Tests a bucket for existence and accessibility\n" -" <bucket> : Bucket to test\n" +" test : Tests a bucket for existence and accessibility\n" +" <bucket> : Bucket to test\n" "\n" -" create : Create a new bucket\n" -" <bucket> : Bucket to create\n" -" [cannedAcl] : Canned ACL for the bucket (see Canned ACLs)\n" -" [location] : Location for bucket (for example, EU)\n" +" create : Create a new bucket\n" +" <bucket> : Bucket to create\n" +" [cannedAcl] : Canned ACL for the bucket (see Canned ACLs)\n" +" [location] : Location for bucket (for example, EU)\n" "\n" -" delete : Delete a bucket or key\n" -" <bucket>[/<key>] : Bucket or bucket/key to delete\n" +" delete : Delete a bucket or key\n" +" <bucket>[/<key>] : Bucket or bucket/key to delete\n" "\n" -" list : List bucket contents\n" -" <bucket> : Bucket to list\n" -" [prefix] : Prefix for results set\n" -" [marker] : Where in results set to start listing\n" -" [delimiter] : Delimiter for rolling up results set\n" -" [maxkeys] : Maximum number of keys to return in results set\n" -" [allDetails] : Show full details for each key\n" +" list : List bucket contents\n" +" <bucket> : Bucket to list\n" +" [prefix] : Prefix for results set\n" +" [marker] : Where in results set to start listing\n" +" [delimiter] : Delimiter for rolling up results set\n" +" [maxkeys] : Maximum number of keys to return in results set\n" +" [allDetails] : Show full details for each key\n" "\n" -" getacl : Get the ACL of a bucket or key\n" -" <bucket>[/<key>] : Bucket or bucket/key to get the ACL of\n" -" [filename] : Output filename for ACL (default is stdout)\n" +" getacl : Get the ACL of a bucket or key\n" +" <bucket>[/<key>] : Bucket or bucket/key to get the ACL of\n" +" [filename] : Output filename for ACL (default is stdout)\n" "\n" -" setacl : Set the ACL of a bucket or key\n" -" <bucket>[/<key>] : Bucket or bucket/key to set the ACL of\n" -" [filename] : Input filename for ACL (default is stdin)\n" +" setacl : Set the ACL of a bucket or key\n" +" <bucket>[/<key>] : Bucket or bucket/key to set the ACL of\n" +" [filename] : Input filename for ACL (default is stdin)\n" "\n" -" getlogging : Get the logging status of a bucket\n" -" <bucket> : Bucket to get the logging status of\n" -" [filename] : Output filename for ACL (default is stdout)\n" +" getlogging : Get the logging status of a bucket\n" +" <bucket> : Bucket to get the logging status of\n" +" [filename] : Output filename for ACL (default is stdout)\n" "\n" -" setlogging : Set the logging status of a bucket\n" -" <bucket> : Bucket to set the logging status of\n" -" [targetBucket] : Target bucket to log to; if not present, disables\n" -" logging\n" -" [targetPrefix] : Key prefix to use for logs\n" -" [filename] : Input filename for ACL (default is stdin)\n" +" setlogging : Set the logging status of a bucket\n" +" <bucket> : Bucket to set the logging status of\n" +" [targetBucket] : Target bucket to log to; if not present, disables\n" +" logging\n" +" [targetPrefix] : Key prefix to use for logs\n" +" [filename] : Input filename for ACL (default is stdin)\n" "\n" -" put : Puts an object\n" -" <bucket>/<key> : Bucket/key to put object to\n" -" [filename] : Filename to read source data from " - "(default is stdin)\n" -" [contentLength] : How many bytes of source data to put (required if\n" -" source file is stdin)\n" -" [cacheControl] : Cache-Control HTTP header string to associate with\n" -" object\n" -" [contentType] : Content-Type HTTP header string to associate with\n" -" object\n" -" [md5] : MD5 for validating source data\n" -" [contentDispositionFilename] : Content-Disposition filename string to\n" -" associate with object\n" -" [contentEncoding] : Content-Encoding HTTP header string to associate\n" -" with object\n" -" [expires] : Expiration date to associate with object\n" -" [cannedAcl] : Canned ACL for the object (see Canned ACLs)\n" -" [x-amz-meta-...]] : Metadata headers to associate with the object\n" +" put : Puts an object\n" +" <bucket>/<key> : Bucket/key to put object to\n" +" [filename] : Filename to read source data from " + "(default is stdin)\n" +" [contentLength] : How many bytes of source data to put (required if\n" +" source file is stdin)\n" +" [cacheControl] : Cache-Control HTTP header string to associate with\n" +" object\n" +" [contentType] : Content-Type HTTP header string to associate with\n" +" object\n" +" [md5] : MD5 for validating source data\n" +" [contentDispositionFilename] : Content-Disposition filename string to\n" +" associate with object\n" +" [contentEncoding] : Content-Encoding HTTP header string to associate\n" +" with object\n" +" [expires] : Expiration date to associate with object\n" +" [cannedAcl] : Canned ACL for the object (see Canned ACLs)\n" +" [x-amz-meta-...]] : Metadata headers to associate with the object\n" "\n" -" copy : Copies an object; if any options are set, the " - "entire\n" -" metadata of the object is replaced\n" -" <sourcebucket>/<sourcekey> : Source bucket/key\n" -" <destbucket>/<destkey> : Destination bucket/key\n" -" [cacheControl] : Cache-Control HTTP header string to associate with\n" -" object\n" -" [contentType] : Content-Type HTTP header string to associate with\n" -" object\n" -" [contentDispositionFilename] : Content-Disposition filename string to\n" -" associate with object\n" -" [contentEncoding] : Content-Encoding HTTP header string to associate\n" -" with object\n" -" [expires] : Expiration date to associate with object\n" -" [cannedAcl] : Canned ACL for the object (see Canned ACLs)\n" -" [x-amz-meta-...]] : Metadata headers to associate with the object\n" +" copy : Copies an object; if any options are set, the " + "entire\n" +" metadata of the object is replaced\n" +" <sourcebucket>/<sourcekey> : Source bucket/key\n" +" <destbucket>/<destkey> : Destination bucket/key\n" +" [cacheControl] : Cache-Control HTTP header string to associate with\n" +" object\n" +" [contentType] : Content-Type HTTP header string to associate with\n" +" object\n" +" [contentDispositionFilename] : Content-Disposition filename string to\n" +" associate with object\n" +" [contentEncoding] : Content-Encoding HTTP header string to associate\n" +" with object\n" +" [expires] : Expiration date to associate with object\n" +" [cannedAcl] : Canned ACL for the object (see Canned ACLs)\n" +" [x-amz-meta-...]] : Metadata headers to associate with the object\n" "\n" -" get : Gets an object\n" -" <buckey>/<key> : Bucket/key of object to get\n" -" [filename] : Filename to write object data to (required if -s\n" -" command line parameter was used)\n" -" [ifModifiedSince] : Only return the object if it has been modified " - "since\n" -" this date\n" -" [ifNotmodifiedSince] : Only return the object if it has not been " - "modified\n" -" since this date\n" -" [ifMatch] : Only return the object if its ETag header matches\n" -" this string\n" -" [ifNotMatch] : Only return the object if its ETag header does " - "not\n" -" match this string\n" -" [startByte] : First byte of byte range to return\n" -" [byteCount] : Number of bytes of byte range to return\n" +" get : Gets an object\n" +" <buckey>/<key> : Bucket/key of object to get\n" +" [filename] : Filename to write object data to (required if -s\n" +" command line parameter was used)\n" +" [ifModifiedSince] : Only return the object if it has been modified " + "since\n" +" this date\n" +" [ifNotmodifiedSince] : Only return the object if it has not been " + "modified\n" +" since this date\n" +" [ifMatch] : Only return the object if its ETag header matches\n" +" this string\n" +" [ifNotMatch] : Only return the object if its ETag header does " + "not\n" +" match this string\n" +" [startByte] : First byte of byte range to return\n" +" [byteCount] : Number of bytes of byte range to return\n" "\n" -" head : Gets only the headers of an object, implies -s\n" -" <bucket>/<key> : Bucket/key of object to get headers of\n" +" head : Gets only the headers of an object, implies -s\n" +" <bucket>/<key> : Bucket/key of object to get headers of\n" "\n" -" gqs : Generates an authenticated query string\n" -" <bucket>[/<key>] : Bucket or bucket/key to generate query string for\n" -" [expires] : Expiration date for query string\n" -" [resource] : Sub-resource of key for query string, without a\n" -" leading '?', for example, \"torrent\"\n" +" gqs : Generates an authenticated query string\n" +" <bucket>[/<key>] : Bucket or bucket/key to generate query string for\n" +" [expires] : Expiration date for query string\n" +" [resource] : Sub-resource of key for query string, without a\n" +" leading '?', for example, \"torrent\"\n" "\n" " Canned ACLs:\n" "\n" " The following canned ACLs are supported:\n" -" private (default), public-read, public-read-write, authenticated-read\n" +" private (default), public-read, public-read-write, authenticated-read\n" "\n" " ACL Format:\n" "\n" " For the getacl and setacl commands, the format of the ACL list is:\n" " 1) An initial line giving the owner id in this format:\n" -" OwnerID <Owner ID> <Owner Display Name>\n" +" OwnerID <Owner ID> <Owner Display Name>\n" " 2) Optional header lines, giving column headers, starting with the\n" -" word \"Type\", or with some number of dashes\n" +" word \"Type\", or with some number of dashes\n" " 3) Grant lines, of the form:\n" -" <Grant Type> (whitespace) <Grantee> (whitespace) <Permission>\n" -" where Grant Type is one of: Email, UserID, or Group, and\n" -" Grantee is the identification of the grantee based on this type,\n" -" and Permission is one of: READ, WRITE, READ_ACP, or FULL_CONTROL.\n" +" <Grant Type> (whitespace) <Grantee> (whitespace) <Permission>\n" +" where Grant Type is one of: Email, UserID, or Group, and\n" +" Grantee is the identification of the grantee based on this type,\n" +" and Permission is one of: READ, WRITE, READ_ACP, or FULL_CONTROL.\n" "\n" " Note that the easiest way to modify an ACL is to first get it, saving it\n" " into a file, then modifying the file, and then setting the modified file\n" @@ -317,217 +317,217 @@ static void usageExit(FILE *out) "\n" " The format for dates used in parameters is as ISO 8601 dates, i.e.\n" " YYYY-MM-DDTHH:MM:SS[.s...][T/+-dd:dd]. Examples:\n" -" 2008-07-29T20:36:14.0023T\n" -" 2008-07-29T20:36:14.0023+06:00\n" -" 2008-07-29T20:36:14.0023-10:00\n" +" 2008-07-29T20:36:14.0023T\n" +" 2008-07-29T20:36:14.0023+06:00\n" +" 2008-07-29T20:36:14.0023-10:00\n" "\n"); - exit(-1); + exit(-1); } static uint64_t convertInt(const char *str, const char *paramName) { - uint64_t ret = 0; - - while (*str) { - if (!isdigit(*str)) { - fprintf(stderr, "\nERROR: Nondigit in %s parameter: %c\n", - paramName, *str); - usageExit(stderr); - } - ret *= 10; - ret += (*str++ - '0'); - } - - return ret; + uint64_t ret = 0; + + while (*str) { + if (!isdigit(*str)) { + fprintf(stderr, "\nERROR: Nondigit in %s parameter: %c\n", + paramName, *str); + usageExit(stderr); + } + ret *= 10; + ret += (*str++ - '0'); + } + + return ret; } typedef struct growbuffer { - // The total number of bytes, and the start byte - int size; - // The start byte - int start; - // The blocks - char data[64 * 1024]; - struct growbuffer *prev, *next; + // The total number of bytes, and the start byte + int size; + // The start byte + int start; + // The blocks + char data[64 * 1024]; + struct growbuffer *prev, *next; } growbuffer; // returns nonzero on success, zero on out of memory static int growbuffer_append(growbuffer **gb, const char *data, int dataLen) { - while (dataLen) { - growbuffer *buf = *gb ? (*gb)->prev : 0; - if (!buf || (buf->size == sizeof(buf->data))) { - buf = (growbuffer *) malloc(sizeof(growbuffer)); - if (!buf) { - return 0; - } - buf->size = 0; - buf->start = 0; - if (*gb) { - buf->prev = (*gb)->prev; - buf->next = *gb; - (*gb)->prev->next = buf; - (*gb)->prev = buf; - } - else { - buf->prev = buf->next = buf; - *gb = buf; - } - } - - int toCopy = (sizeof(buf->data) - buf->size); - if (toCopy > dataLen) { - toCopy = dataLen; - } - - memcpy(&(buf->data[buf->size]), data, toCopy); - - buf->size += toCopy, data += toCopy, dataLen -= toCopy; - } - - return 1; + while (dataLen) { + growbuffer *buf = *gb ? (*gb)->prev : 0; + if (!buf || (buf->size == sizeof(buf->data))) { + buf = (growbuffer *) malloc(sizeof(growbuffer)); + if (!buf) { + return 0; + } + buf->size = 0; + buf->start = 0; + if (*gb) { + buf->prev = (*gb)->prev; + buf->next = *gb; + (*gb)->prev->next = buf; + (*gb)->prev = buf; + } + else { + buf->prev = buf->next = buf; + *gb = buf; + } + } + + int toCopy = (sizeof(buf->data) - buf->size); + if (toCopy > dataLen) { + toCopy = dataLen; + } + + memcpy(&(buf->data[buf->size]), data, toCopy); + + buf->size += toCopy, data += toCopy, dataLen -= toCopy; + } + + return 1; } static void growbuffer_read(growbuffer **gb, int amt, int *amtReturn, - char *buffer) + char *buffer) { - *amtReturn = 0; + *amtReturn = 0; - growbuffer *buf = *gb; + growbuffer *buf = *gb; - if (!buf) { - return; - } + if (!buf) { + return; + } - *amtReturn = (buf->size > amt) ? amt : buf->size; + *amtReturn = (buf->size > amt) ? amt : buf->size; - memcpy(buffer, &(buf->data[buf->start]), *amtReturn); - - buf->start += *amtReturn, buf->size -= *amtReturn; + memcpy(buffer, &(buf->data[buf->start]), *amtReturn); + + buf->start += *amtReturn, buf->size -= *amtReturn; - if (buf->size == 0) { - if (buf->next == buf) { - *gb = 0; - } - else { - *gb = buf->next; - } - free(buf); - } + if (buf->size == 0) { + if (buf->next == buf) { + *gb = 0; + } + else { + *gb = buf->next; + } + free(buf); + } } static void growbuffer_destroy(growbuffer *gb) { - growbuffer *start = gb; + growbuffer *start = gb; - while (gb) { - growbuffer *next = gb->next; - free(gb); - gb = (next == start) ? 0 : next; - } + while (gb) { + growbuffer *next = gb->next; + free(gb); + gb = (next == start) ? 0 : next; + } } -// Convenience utility for making the code look nicer. Tests a string +// Convenience utility for making the code look nicer. Tests a string // against a format; only the characters specified in the format are // checked (i.e. if the string is longer than the format, the string still -// checks out ok). Format characters are: +// checks out ok). Format characters are: // d - is a digit // anything else - is that character // Returns nonzero the string checks out, zero if it does not. static int checkString(const char *str, const char *format) { - while (*format) { - if (*format == 'd') { - if (!isdigit(*str)) { - return 0; - } - } - else if (*str != *format) { - return 0; - } - str++, format++; - } - - return 1; + while (*format) { + if (*format == 'd') { + if (!isdigit(*str)) { + return 0; + } + } + else if (*str != *format) { + return 0; + } + str++, format++; + } + + return 1; } static int64_t parseIso8601Time(const char *str) { - // Check to make sure that it has a valid format - if (!checkString(str, "dddd-dd-ddTdd:dd:dd")) { - return -1; - } + // Check to make sure that it has a valid format + if (!checkString(str, "dddd-dd-ddTdd:dd:dd")) { + return -1; + } #define nextnum() (((*str - '0') * 10) + (*(str + 1) - '0')) - // Convert it - struct tm stm; - memset(&stm, 0, sizeof(stm)); - - stm.tm_year = (nextnum() - 19) * 100; - str += 2; - stm.tm_year += nextnum(); - str += 3; - - stm.tm_mon = nextnum() - 1; - str += 3; - - stm.tm_mday = nextnum(); - str += 3; - - stm.tm_hour = nextnum(); - str += 3; - - stm.tm_min = nextnum(); - str += 3; - - stm.tm_sec = nextnum(); - str += 2; - - stm.tm_isdst = -1; - - // This is hokey but it's the recommended way ... - char *tz = getenv("TZ"); - setenv("TZ", "UTC", 1); - - int64_t ret = mktime(&stm); - - if (tz) { - setenv("TZ", tz, 1); - } - else { - unsetenv("TZ"); - } - - // Skip the millis - - if (*str == '.') { - str++; - while (isdigit(*str)) { - str++; - } - } - - if (checkString(str, "-dd:dd") || checkString(str, "+dd:dd")) { - int sign = (*str++ == '-') ? -1 : 1; - int hours = nextnum(); - str += 3; - int minutes = nextnum(); - ret += (-sign * (((hours * 60) + minutes) * 60)); - } - // Else it should be Z to be a conformant time string, but we just assume - // that it is rather than enforcing that - - return ret; + // Convert it + struct tm stm; + memset(&stm, 0, sizeof(stm)); + + stm.tm_year = (nextnum() - 19) * 100; + str += 2; + stm.tm_year += nextnum(); + str += 3; + + stm.tm_mon = nextnum() - 1; + str += 3; + + stm.tm_mday = nextnum(); + str += 3; + + stm.tm_hour = nextnum(); + str += 3; + + stm.tm_min = nextnum(); + str += 3; + + stm.tm_sec = nextnum(); + str += 2; + + stm.tm_isdst = -1; + + // This is hokey but it's the recommended way ... + char *tz = getenv("TZ"); + setenv("TZ", "UTC", 1); + + int64_t ret = mktime(&stm); + + if (tz) { + setenv("TZ", tz, 1); + } + else { + unsetenv("TZ"); + } + + // Skip the millis + + if (*str == '.') { + str++; + while (isdigit(*str)) { + str++; + } + } + + if (checkString(str, "-dd:dd") || checkString(str, "+dd:dd")) { + int sign = (*str++ == '-') ? -1 : 1; + int hours = nextnum(); + str += 3; + int minutes = nextnum(); + ret += (-sign * (((hours * 60) + minutes) * 60)); + } + // Else it should be Z to be a conformant time string, but we just assume + // that it is rather than enforcing that + + return ret; } @@ -537,161 +537,161 @@ static int64_t parseIso8601Time(const char *str) // Email email_address permission // UserID user_id (display_name) permission // Group Authenticated AWS Users permission -// Group All Users permission +// Group All Users permission // permission is one of READ, WRITE, READ_ACP, WRITE_ACP, FULL_CONTROL static int convert_simple_acl(char *aclXml, char *ownerId, - char *ownerDisplayName, - int *aclGrantCountReturn, - S3AclGrant *aclGrants) + char *ownerDisplayName, + int *aclGrantCountReturn, + S3AclGrant *aclGrants) { - *aclGrantCountReturn = 0; - *ownerId = 0; - *ownerDisplayName = 0; - -#define SKIP_SPACE(require_more) \ - do { \ - while (isspace(*aclXml)) { \ - aclXml++; \ - } \ - if (require_more && !*aclXml) { \ - return 0; \ - } \ - } while (0) - -#define COPY_STRING_MAXLEN(field, maxlen) \ - do { \ - SKIP_SPACE(1); \ - int len = 0; \ - while ((len < maxlen) && !isspace(*aclXml)) { \ - field[len++] = *aclXml++; \ - } \ - field[len] = 0; \ - } while (0) - -#define COPY_STRING(field) \ - COPY_STRING_MAXLEN(field, (int) (sizeof(field) - 1)) - - while (1) { - SKIP_SPACE(0); - - if (!*aclXml) { - break; - } - - // Skip Type lines and dash lines - if (!strncmp(aclXml, "Type", sizeof("Type") - 1) || - (*aclXml == '-')) { - while (*aclXml && ((*aclXml != '\n') && (*aclXml != '\r'))) { - aclXml++; - } - continue; - } - - if (!strncmp(aclXml, "OwnerID", sizeof("OwnerID") - 1)) { - aclXml += sizeof("OwnerID") - 1; - COPY_STRING_MAXLEN(ownerId, S3_MAX_GRANTEE_USER_ID_SIZE); - SKIP_SPACE(1); - COPY_STRING_MAXLEN(ownerDisplayName, - S3_MAX_GRANTEE_DISPLAY_NAME_SIZE); - continue; - } - - if (*aclGrantCountReturn == S3_MAX_ACL_GRANT_COUNT) { - return 0; - } - - S3AclGrant *grant = &(aclGrants[(*aclGrantCountReturn)++]); - - if (!strncmp(aclXml, "Email", sizeof("Email") - 1)) { - grant->granteeType = S3GranteeTypeAmazonCustomerByEmail; - aclXml += sizeof("Email") - 1; - COPY_STRING(grant->grantee.amazonCustomerByEmail.emailAddress); - } - else if (!strncmp(aclXml, "UserID", sizeof("UserID") - 1)) { - grant->granteeType = S3GranteeTypeCanonicalUser; - aclXml += sizeof("UserID") - 1; - COPY_STRING(grant->grantee.canonicalUser.id); - SKIP_SPACE(1); - // Now do display name - COPY_STRING(grant->grantee.canonicalUser.displayName); - } - else if (!strncmp(aclXml, "Group", sizeof("Group") - 1)) { - aclXml += sizeof("Group") - 1; - SKIP_SPACE(1); - if (!strncmp(aclXml, "Authenticated AWS Users", - sizeof("Authenticated AWS Users") - 1)) { - grant->granteeType = S3GranteeTypeAllAwsUsers; - aclXml += (sizeof("Authenticated AWS Users") - 1); - } - else if (!strncmp(aclXml, "All Users", sizeof("All Users") - 1)) { - grant->granteeType = S3GranteeTypeAllUsers; - aclXml += (sizeof("All Users") - 1); - } - else if (!strncmp(aclXml, "Log Delivery", - sizeof("Log Delivery") - 1)) { - grant->granteeType = S3GranteeTypeLogDelivery; - aclXml += (sizeof("Log Delivery") - 1); - } - else { - return 0; - } - } - else { - return 0; - } - - SKIP_SPACE(1); - - if (!strncmp(aclXml, "READ_ACP", sizeof("READ_ACP") - 1)) { - grant->permission = S3PermissionReadACP; - aclXml += (sizeof("READ_ACP") - 1); - } - else if (!strncmp(aclXml, "READ", sizeof("READ") - 1)) { - grant->permission = S3PermissionRead; - aclXml += (sizeof("READ") - 1); - } - else if (!strncmp(aclXml, "WRITE_ACP", sizeof("WRITE_ACP") - 1)) { - grant->permission = S3PermissionWriteACP; - aclXml += (sizeof("WRITE_ACP") - 1); - } - else if (!strncmp(aclXml, "WRITE", sizeof("WRITE") - 1)) { - grant->permission = S3PermissionWrite; - aclXml += (sizeof("WRITE") - 1); - } - else if (!strncmp(aclXml, "FULL_CONTROL", - sizeof("FULL_CONTROL") - 1)) { - grant->permission = S3PermissionFullControl; - aclXml += (sizeof("FULL_CONTROL") - 1); - } - } - - return 1; + *aclGrantCountReturn = 0; + *ownerId = 0; + *ownerDisplayName = 0; + +#define SKIP_SPACE(require_more) \ + do { \ + while (isspace(*aclXml)) { \ + aclXml++; \ + } \ + if (require_more && !*aclXml) { \ + return 0; \ + } \ + } while (0) + +#define COPY_STRING_MAXLEN(field, maxlen) \ + do { \ + SKIP_SPACE(1); \ + int len = 0; \ + while ((len < maxlen) && !isspace(*aclXml)) { \ + field[len++] = *aclXml++; \ + } \ + field[len] = 0; \ + } while (0) + +#define COPY_STRING(field) \ + COPY_STRING_MAXLEN(field, (int) (sizeof(field) - 1)) + + while (1) { + SKIP_SPACE(0); + + if (!*aclXml) { + break; + } + + // Skip Type lines and dash lines + if (!strncmp(aclXml, "Type", sizeof("Type") - 1) || + (*aclXml == '-')) { + while (*aclXml && ((*aclXml != '\n') && (*aclXml != '\r'))) { + aclXml++; + } + continue; + } + + if (!strncmp(aclXml, "OwnerID", sizeof("OwnerID") - 1)) { + aclXml += sizeof("OwnerID") - 1; + COPY_STRING_MAXLEN(ownerId, S3_MAX_GRANTEE_USER_ID_SIZE); + SKIP_SPACE(1); + COPY_STRING_MAXLEN(ownerDisplayName, + S3_MAX_GRANTEE_DISPLAY_NAME_SIZE); + continue; + } + + if (*aclGrantCountReturn == S3_MAX_ACL_GRANT_COUNT) { + return 0; + } + + S3AclGrant *grant = &(aclGrants[(*aclGrantCountReturn)++]); + + if (!strncmp(aclXml, "Email", sizeof("Email") - 1)) { + grant->granteeType = S3GranteeTypeAmazonCustomerByEmail; + aclXml += sizeof("Email") - 1; + COPY_STRING(grant->grantee.amazonCustomerByEmail.emailAddress); + } + else if (!strncmp(aclXml, "UserID", sizeof("UserID") - 1)) { + grant->granteeType = S3GranteeTypeCanonicalUser; + aclXml += sizeof("UserID") - 1; + COPY_STRING(grant->grantee.canonicalUser.id); + SKIP_SPACE(1); + // Now do display name + COPY_STRING(grant->grantee.canonicalUser.displayName); + } + else if (!strncmp(aclXml, "Group", sizeof("Group") - 1)) { + aclXml += sizeof("Group") - 1; + SKIP_SPACE(1); + if (!strncmp(aclXml, "Authenticated AWS Users", + sizeof("Authenticated AWS Users") - 1)) { + grant->granteeType = S3GranteeTypeAllAwsUsers; + aclXml += (sizeof("Authenticated AWS Users") - 1); + } + else if (!strncmp(aclXml, "All Users", sizeof("All Users") - 1)) { + grant->granteeType = S3GranteeTypeAllUsers; + aclXml += (sizeof("All Users") - 1); + } + else if (!strncmp(aclXml, "Log Delivery", + sizeof("Log Delivery") - 1)) { + grant->granteeType = S3GranteeTypeLogDelivery; + aclXml += (sizeof("Log Delivery") - 1); + } + else { + return 0; + } + } + else { + return 0; + } + + SKIP_SPACE(1); + + if (!strncmp(aclXml, "READ_ACP", sizeof("READ_ACP") - 1)) { + grant->permission = S3PermissionReadACP; + aclXml += (sizeof("READ_ACP") - 1); + } + else if (!strncmp(aclXml, "READ", sizeof("READ") - 1)) { + grant->permission = S3PermissionRead; + aclXml += (sizeof("READ") - 1); + } + else if (!strncmp(aclXml, "WRITE_ACP", sizeof("WRITE_ACP") - 1)) { + grant->permission = S3PermissionWriteACP; + aclXml += (sizeof("WRITE_ACP") - 1); + } + else if (!strncmp(aclXml, "WRITE", sizeof("WRITE") - 1)) { + grant->permission = S3PermissionWrite; + aclXml += (sizeof("WRITE") - 1); + } + else if (!strncmp(aclXml, "FULL_CONTROL", + sizeof("FULL_CONTROL") - 1)) { + grant->permission = S3PermissionFullControl; + aclXml += (sizeof("FULL_CONTROL") - 1); + } + } + + return 1; } static int should_retry() { - if (retriesG--) { - // Sleep before next retry; start out with a 1 second sleep - static int retrySleepInterval = 1; - sleep(retrySleepInterval); - // Next sleep 1 second longer - retrySleepInterval++; - return 1; - } - - return 0; + if (retriesG--) { + // Sleep before next retry; start out with a 1 second sleep + static int retrySleepInterval = 1; + sleep(retrySleepInterval); + // Next sleep 1 second longer + retrySleepInterval++; + return 1; + } + + return 0; } static struct option longOptionsG[] = { - { "force", no_argument, 0, 'f' }, - { "vhost-style", no_argument, 0, 'h' }, - { "unencrypted", no_argument, 0, 'u' }, - { "show-properties", no_argument, 0, 's' }, - { "retries", required_argument, 0, 'r' }, - { 0, 0, 0, 0 } + { "force", no_argument, 0, 'f' }, + { "vhost-style", no_argument, 0, 'h' }, + { "unencrypted", no_argument, 0, 'u' }, + { "show-properties", no_argument, 0, 's' }, + { "retries", required_argument, 0, 'r' }, + { 0, 0, 0, 0 } }; @@ -700,44 +700,44 @@ static struct option longOptionsG[] = // This callback does the same thing for every request type: prints out the // properties if the user has requested them to be so static S3Status responsePropertiesCallback - (const S3ResponseProperties *properties, void *callbackData) + (const S3ResponseProperties *properties, void *callbackData) { - (void) callbackData; - - if (!showResponsePropertiesG) { - return S3StatusOK; - } - -#define print_nonnull(name, field) \ - do { \ - if (properties-> field) { \ - printf("%s: %s\n", name, properties-> field); \ - } \ - } while (0) - - print_nonnull("Content-Type", contentType); - print_nonnull("Request-Id", requestId); - print_nonnull("Request-Id-2", requestId2); - if (properties->contentLength > 0) { - printf("Content-Length: %lld\n", - (unsigned long long) properties->contentLength); - } - print_nonnull("Server", server); - print_nonnull("ETag", eTag); - if (properties->lastModified > 0) { - char timebuf[256]; - time_t t = (time_t) properties->lastModified; - // gmtime is not thread-safe but we don't care here. - strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&t)); - printf("Last-Modified: %s\n", timebuf); - } - int i; - for (i = 0; i < properties->metaDataCount; i++) { - printf("x-amz-meta-%s: %s\n", properties->metaData[i].name, - properties->metaData[i].value); - } - - return S3StatusOK; + (void) callbackData; + + if (!showResponsePropertiesG) { + return S3StatusOK; + } + +#define print_nonnull(name, field) \ + do { \ + if (properties-> field) { \ + printf("%s: %s\n", name, properties-> field); \ + } \ + } while (0) + + print_nonnull("Content-Type", contentType); + print_nonnull("Request-Id", requestId); + print_nonnull("Request-Id-2", requestId2); + if (properties->contentLength > 0) { + printf("Content-Length: %lld\n", + (unsigned long long) properties->contentLength); + } + print_nonnull("Server", server); + print_nonnull("ETag", eTag); + if (properties->lastModified > 0) { + char timebuf[256]; + time_t t = (time_t) properties->lastModified; + // gmtime is not thread-safe but we don't care here. + strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&t)); + printf("Last-Modified: %s\n", timebuf); + } + int i; + for (i = 0; i < properties->metaDataCount; i++) { + printf("x-amz-meta-%s: %s\n", properties->metaData[i].name, + properties->metaData[i].value); + } + + return S3StatusOK; } @@ -746,39 +746,39 @@ static S3Status responsePropertiesCallback // This callback does the same thing for every request type: saves the status // and error stuff in global variables static void responseCompleteCallback(S3Status status, - const S3ErrorDetails *error, - void *callbackData) + const S3ErrorDetails *error, + void *callbackData) { - (void) callbackData; - - statusG = status; - // Compose the error details message now, although we might not use it. - // Can't just save a pointer to [error] since it's not guaranteed to last - // beyond this callback - int len = 0; - if (error && error->message) { - len += snprintf(&(errorDetailsG[len]), sizeof(errorDetailsG) - len, - " Message: %s\n", error->message); - } - if (error && error->resource) { - len += snprintf(&(errorDetailsG[len]), sizeof(errorDetailsG) - len, - " Resource: %s\n", error->resource); - } - if (error && error->furtherDetails) { - len += snprintf(&(errorDetailsG[len]), sizeof(errorDetailsG) - len, - " Further Details: %s\n", error->furtherDetails); - } - if (error && error->extraDetailsCount) { - len += snprintf(&(errorDetailsG[len]), sizeof(errorDetailsG) - len, - "%s", " Extra Details:\n"); - int i; - for (i = 0; i < error->extraDetailsCount; i++) { - len += snprintf(&(errorDetailsG[len]), - sizeof(errorDetailsG) - len, " %s: %s\n", - error->extraDetails[i].name, - error->extraDetails[i].value); - } - } + (void) callbackData; + + statusG = status; + // Compose the error details message now, although we might not use it. + // Can't just save a pointer to [error] since it's not guaranteed to last + // beyond this callback + int len = 0; + if (error && error->message) { + len += snprintf(&(errorDetailsG[len]), sizeof(errorDetailsG) - len, + " Message: %s\n", error->message); + } + if (error && error->resource) { + len += snprintf(&(errorDetailsG[len]), sizeof(errorDetailsG) - len, + " Resource: %s\n", error->resource); + } + if (error && error->furtherDetails) { + len += snprintf(&(errorDetailsG[len]), sizeof(errorDetailsG) - len, + " Further Details: %s\n", error->furtherDetails); + } + if (error && error->extraDetailsCount) { + len += snprintf(&(errorDetailsG[len]), sizeof(errorDetailsG) - len, + "%s", " Extra Details:\n"); + int i; + for (i = 0; i < error->extraDetailsCount; i++) { + len += snprintf(&(errorDetailsG[len]), + sizeof(errorDetailsG) - len, " %s: %s\n", + error->extraDetails[i].name, + error->extraDetails[i].value); + } + } } @@ -786,93 +786,93 @@ static void responseCompleteCallback(S3Status status, typedef struct list_service_data { - int headerPrinted; - int allDetails; + int headerPrinted; + int allDetails; } list_service_data; static void printListServiceHeader(int allDetails) { - printf("%-56s %-20s", " Bucket", - " Created"); - if (allDetails) { - printf(" %-64s %-12s", - " Owner ID", - "Display Name"); - } - printf("\n"); - printf("-------------------------------------------------------- " - "--------------------"); - if (allDetails) { - printf(" -------------------------------------------------" - "--------------- ------------"); - } - printf("\n"); + printf("%-56s %-20s", " Bucket", + " Created"); + if (allDetails) { + printf(" %-64s %-12s", + " Owner ID", + "Display Name"); + } + printf("\n"); + printf("-------------------------------------------------------- " + "--------------------"); + if (allDetails) { + printf(" -------------------------------------------------" + "--------------- ------------"); + } + printf("\n"); } static S3Status listServiceCallback(const char *ownerId, - const char *ownerDisplayName, - const char *bucketName, - int64_t creationDate, void *callbackData) + const char *ownerDisplayName, + const char *bucketName, + int64_t creationDate, void *callbackData) { - list_service_data *data = (list_service_data *) callbackData; - - if (!data->headerPrinted) { - data->headerPrinted = 1; - printListServiceHeader(data->allDetails); - } - - char timebuf[256]; - if (creationDate >= 0) { - time_t t = (time_t) creationDate; - strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&t)); - } - else { - timebuf[0] = 0; - } - - printf("%-56s %-20s", bucketName, timebuf); - if (data->allDetails) { - printf(" %-64s %-12s", ownerId ? ownerId : "", - ownerDisplayName ? ownerDisplayName : ""); - } - printf("\n"); - - return S3StatusOK; + list_service_data *data = (list_service_data *) callbackData; + + if (!data->headerPrinted) { + data->headerPrinted = 1; + printListServiceHeader(data->allDetails); + } + + char timebuf[256]; + if (creationDate >= 0) { + time_t t = (time_t) creationDate; + strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&t)); + } + else { + timebuf[0] = 0; + } + + printf("%-56s %-20s", bucketName, timebuf); + if (data->allDetails) { + printf(" %-64s %-12s", ownerId ? ownerId : "", + ownerDisplayName ? ownerDisplayName : ""); + } + printf("\n"); + + return S3StatusOK; } static void list_service(int allDetails) { - list_service_data data; - - data.headerPrinted = 0; - data.allDetails = allDetails; - - S3_init(); - - S3ListServiceHandler listServiceHandler = - { - { &responsePropertiesCallback, &responseCompleteCallback }, - &listServiceCallback - }; - - do { - S3_list_service(protocolG, accessKeyIdG, secretAccessKeyG, 0, - &listServiceHandler, &data); - } while (S3_status_is_retryable(statusG) && should_retry()); - - if (statusG == S3StatusOK) { - if (!data.headerPrinted) { - printListServiceHeader(allDetails); - } - } - else { - printError(); - } - - S3_deinitialize(); + list_service_data data; + + data.headerPrinted = 0; + data.allDetails = allDetails; + + S3_init(); + + S3ListServiceHandler listServiceHandler = + { + { &responsePropertiesCallback, &responseCompleteCallback }, + &listServiceCallback + }; + + do { + S3_list_service(protocolG, accessKeyIdG, secretAccessKeyG, 0, + &listServiceHandler, &data); + } while (S3_status_is_retryable(statusG) && should_retry()); + + if (statusG == S3StatusOK) { + if (!data.headerPrinted) { + printListServiceHeader(allDetails); + } + } + else { + printError(); + } + + S3_deinitialize(); } @@ -880,63 +880,63 @@ static void list_service(int allDetails) static void test_bucket(int argc, char **argv, int optindex) { - // test bucket - if (optindex == argc) { - fprintf(stderr, "\nERROR: Missing parameter: bucket\n"); - usageExit(stderr); - } - - const char *bucketName = argv[optindex++]; - - if (optindex != argc) { - fprintf(stderr, "\nERROR: Extraneous parameter: %s\n", argv[optindex]); - usageExit(stderr); - } - - S3_init(); - - S3ResponseHandler responseHandler = - { - &responsePropertiesCallback, &responseCompleteCallback - }; - - char locationConstraint[64]; - do { - S3_test_bucket(protocolG, uriStyleG, accessKeyIdG, secretAccessKeyG, - bucketName, sizeof(locationConstraint), - locationConstraint, 0, &responseHandler, 0); - } while (S3_status_is_retryable(statusG) && should_retry()); - - const char *result; - - switch (statusG) { - case S3StatusOK: - // bucket exists - result = locationConstraint[0] ? locationConstraint : "USA"; - break; - case S3StatusErrorNoSuchBucket: - result = "Does Not Exist"; - break; - case S3StatusErrorAccessDenied: - result = "Access Denied"; - break; - default: - result = 0; - break; - } - - if (result) { - printf("%-56s %-20s\n", " Bucket", - " Status"); - printf("-------------------------------------------------------- " - "--------------------\n"); - printf("%-56s %-20s\n", bucketName, result); - } - else { - printError(); - } - - S3_deinitialize(); + // test bucket + if (optindex == argc) { + fprintf(stderr, "\nERROR: Missing parameter: bucket\n"); + usageExit(stderr); + } + + const char *bucketName = argv[optindex++]; + + if (optindex != argc) { + fprintf(stderr, "\nERROR: Extraneous parameter: %s\n", argv[optindex]); + usageExit(stderr); + } + + S3_init(); + + S3ResponseHandler responseHandler = + { + &responsePropertiesCallback, &responseCompleteCallback + }; + + char locationConstraint[64]; + do { + S3_test_bucket(protocolG, uriStyleG, accessKeyIdG, secretAccessKeyG, + bucketName, sizeof(locationConstraint), + locationConstraint, 0, &responseHandler, 0); + } while (S3_status_is_retryable(statusG) && should_retry()); + + const char *result; + + switch (statusG) { + case S3StatusOK: + // bucket exists + result = locationConstraint[0] ? locationConstraint : "USA"; + break; + case S3StatusErrorNoSuchBucket: + result = "Does Not Exist"; + break; + case S3StatusErrorAccessDenied: + result = "Access Denied"; + break; + default: + result = 0; + break; + } + + if (result) { + printf("%-56s %-20s\n", " Bucket", + " Status"); + printf("-------------------------------------------------------- " + "--------------------\n"); + printf("%-56s %-20s\n", bucketName, result); + } + else { + printError(); + } + + S3_deinitialize(); } @@ -944,76 +944,76 @@ static void test_bucket(int argc, char **argv, int optindex) static void create_bucket(int argc, char **argv, int optindex) { - if (optindex == argc) { - fprintf(stderr, "\nERROR: Missing parameter: bucket\n"); - usageExit(stderr); - } - - const char *bucketName = argv[optindex++]; - - if (!forceG && (S3_validate_bucket_name - (bucketName, S3UriStyleVirtualHost) != S3StatusOK)) { - fprintf(stderr, "\nWARNING: Bucket name is not valid for " - "virtual-host style URI access.\n"); - fprintf(stderr, "Bucket not created. Use -f option to force the " - "bucket to be created despite\n"); - fprintf(stderr, "this warning.\n\n"); - exit(-1); - } - - const char *locationConstraint = 0; - S3CannedAcl cannedAcl = S3CannedAclPrivate; - while (optindex < argc) { - char *param = argv[optindex++]; - if (!strncmp(param, LOCATION_PREFIX, LOCATION_PREFIX_LEN)) { - locationConstraint = &(param[LOCATION_PREFIX_LEN]); - } - else if (!strncmp(param, CANNED_ACL_PREFIX, CANNED_ACL_PREFIX_LEN)) { - char *val = &(param[CANNED_ACL_PREFIX_LEN]); - if (!strcmp(val, "private")) { - cannedAcl = S3CannedAclPrivate; - } - else if (!strcmp(val, "public-read")) { - cannedAcl = S3CannedAclPublicRead; - } - else if (!strcmp(val, "public-read-write")) { - cannedAcl = S3CannedAclPublicReadWrite; - } - else if (!strcmp(val, "authenticated-read")) { - cannedAcl = S3CannedAclAuthenticatedRead; - } - else { - fprintf(stderr, "\nERROR: Unknown canned ACL: %s\n", val); - usageExit(stderr); - } - } - else { - fprintf(stderr, "\nERROR: Unknown param: %s\n", param); - usageExit(stderr); - } - } - - S3_init(); - - S3ResponseHandler responseHandler = - { - &responsePropertiesCallback, &responseCompleteCallback - }; - - do { - S3_create_bucket(protocolG, accessKeyIdG, secretAccessKeyG, - bucketName, cannedAcl, locationConstraint, 0, - &responseHandler, 0); - } while (S3_status_is_retryable(statusG) && should_retry()); - - if (statusG == S3StatusOK) { - printf("Bucket successfully created.\n"); - } - else { - printError(); - } - - S3_deinitialize(); + if (optindex == argc) { + fprintf(stderr, "\nERROR: Missing parameter: bucket\n"); + usageExit(stderr); + } + + const char *bucketName = argv[optindex++]; + + if (!forceG && (S3_validate_bucket_name + (bucketName, S3UriStyleVirtualHost) != S3StatusOK)) { + fprintf(stderr, "\nWARNING: Bucket name is not valid for " + "virtual-host style URI access.\n"); + fprintf(stderr, "Bucket not created. Use -f option to force the " + "bucket to be created despite\n"); + fprintf(stderr, "this warning.\n\n"); + exit(-1); + } + + const char *locationConstraint = 0; + S3CannedAcl cannedAcl = S3CannedAclPrivate; + while (optindex < argc) { + char *param = argv[optindex++]; + if (!strncmp(param, LOCATION_PREFIX, LOCATION_PREFIX_LEN)) { + locationConstraint = &(param[LOCATION_PREFIX_LEN]); + } + else if (!strncmp(param, CANNED_ACL_PREFIX, CANNED_ACL_PREFIX_LEN)) { + char *val = &(param[CANNED_ACL_PREFIX_LEN]); + if (!strcmp(val, "private")) { + cannedAcl = S3CannedAclPrivate; + } + else if (!strcmp(val, "public-read")) { + cannedAcl = S3CannedAclPublicRead; + } + else if (!strcmp(val, "public-read-write")) { + cannedAcl = S3CannedAclPublicReadWrite; + } + else if (!strcmp(val, "authenticated-read")) { + cannedAcl = S3CannedAclAuthenticatedRead; + } + else { + fprintf(stderr, "\nERROR: Unknown canned ACL: %s\n", val); + usageExit(stderr); + } + } + else { + fprintf(stderr, "\nERROR: Unknown param: %s\n", param); + usageExit(stderr); + } + } + + S3_init(); + + S3ResponseHandler responseHandler = + { + &responsePropertiesCallback, &responseCompleteCallback + }; + + do { + S3_create_bucket(protocolG, accessKeyIdG, secretAccessKeyG, + bucketName, cannedAcl, locationConstraint, 0, + &responseHandler, 0); + } while (S3_status_is_retryable(statusG) && should_retry()); + + if (statusG == S3StatusOK) { + printf("Bucket successfully created.\n"); + } + else { + printError(); + } + + S3_deinitialize(); } @@ -1021,35 +1021,35 @@ static void create_bucket(int argc, char **argv, int optindex) static void delete_bucket(int argc, char **argv, int optindex) { - if (optindex == argc) { - fprintf(stderr, "\nERROR: Missing parameter: bucket\n"); - usageExit(stderr); - } + if (optindex == argc) { + fprintf(stderr, "\nERROR: Missing parameter: bucket\n"); + usageExit(stderr); + } - const char *bucketName = argv[optindex++]; + const char *bucketName = argv[optindex++]; - if (optindex != argc) { - fprintf(stderr, "\nERROR: Extraneous parameter: %s\n", argv[optindex]); - usageExit(stderr); - } + if (optindex != argc) { + fprintf(stderr, "\nERROR: Extraneous parameter: %s\n", argv[optindex]); + usageExit(stderr); + } - S3_init(); + S3_init(); - S3ResponseHandler responseHandler = - { - &responsePropertiesCallback, &responseCompleteCallback - }; + S3ResponseHandler responseHandler = + { + &responsePropertiesCallback, &responseCompleteCallback + }; - do { - S3_delete_bucket(protocolG, uriStyleG, accessKeyIdG, secretAccessKeyG, - bucketName, 0, &responseHandler, 0); - } while (S3_status_is_retryable(statusG) && should_retry()); + do { + S3_delete_bucket(protocolG, uriStyleG, accessKeyIdG, secretAccessKeyG, + bucketName, 0, &responseHandler, 0); + } while (S3_status_is_retryable(statusG) && should_retry()); - if (statusG != S3StatusOK) { - printError(); - } + if (statusG != S3StatusOK) { + printError(); + } - S3_deinitialize(); + S3_deinitialize(); } @@ -1057,285 +1057,285 @@ static void delete_bucket(int argc, char **argv, int optindex) typedef struct list_bucket_callback_data { - int isTruncated; - char nextMarker[1024]; - int keyCount; - int allDetails; + int isTruncated; + char nextMarker[1024]; + int keyCount; + int allDetails; } list_bucket_callback_data; static void printListBucketHeader(int allDetails) { - printf("%-50s %-20s %-5s", - " Key", - " Last Modified", "Size"); - if (allDetails) { - printf(" %-34s %-64s %-12s", - " ETag", - " Owner ID", - "Display Name"); - } - printf("\n"); - printf("-------------------------------------------------- " - "-------------------- -----"); - if (allDetails) { - printf(" ---------------------------------- " - "-------------------------------------------------" - "--------------- ------------"); - } - printf("\n"); + printf("%-50s %-20s %-5s", + " Key", + " Last Modified", "Size"); + if (allDetails) { + printf(" %-34s %-64s %-12s", + " ETag", + " Owner ID", + "Display Name"); + } + printf("\n"); + printf("-------------------------------------------------- " + "-------------------- -----"); + if (allDetails) { + printf(" ---------------------------------- " + "-------------------------------------------------" + "--------------- ------------"); + } + printf("\n"); } static S3Status listBucketCallback(int isTruncated, const char *nextMarker, - int contentsCount, - const S3ListBucketContent *contents, - int commonPrefixesCount, - const char **commonPrefixes, - void *callbackData) + int contentsCount, + const S3ListBucketContent *contents, + int commonPrefixesCount, + const char **commonPrefixes, + void *callbackData) { - list_bucket_callback_data *data = - (list_bucket_callback_data *) callbackData; - - data->isTruncated = isTruncated; - // This is tricky. S3 doesn't return the NextMarker if there is no - // delimiter. Why, I don't know, since it's still useful for paging - // through results. We want NextMarker to be the last content in the - // list, so set it to that if necessary. - if ((!nextMarker || !nextMarker[0]) && contentsCount) { - nextMarker = contents[contentsCount - 1].key; - } - if (nextMarker) { - snprintf(data->nextMarker, sizeof(data->nextMarker), "%s", - nextMarker); - } - else { - data->nextMarker[0] = 0; - } - - if (contentsCount && !data->keyCount) { - printListBucketHeader(data->allDetails); - } - - int i; - for (i = 0; i < contentsCount; i++) { - const S3ListBucketContent *content = &(contents[i]); - char timebuf[256]; - if (0) { - time_t t = (time_t) content->lastModified; - strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ", - gmtime(&t)); - printf("\nKey: %s\n", content->key); - printf("Last Modified: %s\n", timebuf); - printf("ETag: %s\n", content->eTag); - printf("Size: %llu\n", (unsigned long long) content->size); - if (content->ownerId) { - printf("Owner ID: %s\n", content->ownerId); - } - if (content->ownerDisplayName) { - printf("Owner Display Name: %s\n", content->ownerDisplayName); - } - } - else { - time_t t = (time_t) content->lastModified; - strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ", - gmtime(&t)); - char sizebuf[16]; - if (content->size < 100000) { - sprintf(sizebuf, "%5llu", (unsigned long long) content->size); - } - else if (content->size < (1024 * 1024)) { - sprintf(sizebuf, "%4lluK", - ((unsigned long long) content->size) / 1024ULL); - } - else if (content->size < (10 * 1024 * 1024)) { - float f = content->size; - f /= (1024 * 1024); - sprintf(sizebuf, "%1.2fM", f); - } - else if (content->size < (1024 * 1024 * 1024)) { - sprintf(sizebuf, "%4lluM", - ((unsigned long long) content->size) / - (1024ULL * 1024ULL)); - } - else { - float f = (content->size / 1024); - f /= (1024 * 1024); - sprintf(sizebuf, "%1.2fG", f); - } - printf("%-50s %s %s", content->key, timebuf, sizebuf); - if (data->allDetails) { - printf(" %-34s %-64s %-12s", - content->eTag, - content->ownerId ? content->ownerId : "", - content->ownerDisplayName ? - content->ownerDisplayName : ""); - } - printf("\n"); - } - } - - data->keyCount += contentsCount; - - for (i = 0; i < commonPrefixesCount; i++) { - printf("\nCommon Prefix: %s\n", commonPrefixes[i]); - } - - return S3StatusOK; + list_bucket_callback_data *data = + (list_bucket_callback_data *) callbackData; + + data->isTruncated = isTruncated; + // This is tricky. S3 doesn't return the NextMarker if there is no + // delimiter. Why, I don't know, since it's still useful for paging + // through results. We want NextMarker to be the last content in the + // list, so set it to that if necessary. + if ((!nextMarker || !nextMarker[0]) && contentsCount) { + nextMarker = contents[contentsCount - 1].key; + } + if (nextMarker) { + snprintf(data->nextMarker, sizeof(data->nextMarker), "%s", + nextMarker); + } + else { + data->nextMarker[0] = 0; + } + + if (contentsCount && !data->keyCount) { + printListBucketHeader(data->allDetails); + } + + int i; + for (i = 0; i < contentsCount; i++) { + const S3ListBucketContent *content = &(contents[i]); + char timebuf[256]; + if (0) { + time_t t = (time_t) content->lastModified; + strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ", + gmtime(&t)); + printf("\nKey: %s\n", content->key); + printf("Last Modified: %s\n", timebuf); + printf("ETag: %s\n", content->eTag); + printf("Size: %llu\n", (unsigned long long) content->size); + if (content->ownerId) { + printf("Owner ID: %s\n", content->ownerId); + } + if (content->ownerDisplayName) { + printf("Owner Display Name: %s\n", content->ownerDisplayName); + } + } + else { + time_t t = (time_t) content->lastModified; + strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ", + gmtime(&t)); + char sizebuf[16]; + if (content->size < 100000) { + sprintf(sizebuf, "%5llu", (unsigned long long) content->size); + } + else if (content->size < (1024 * 1024)) { + sprintf(sizebuf, "%4lluK", + ((unsigned long long) content->size) / 1024ULL); + } + else if (content->size < (10 * 1024 * 1024)) { + float f = content->size; + f /= (1024 * 1024); + sprintf(sizebuf, "%1.2fM", f); + } + else if (content->size < (1024 * 1024 * 1024)) { + sprintf(sizebuf, "%4lluM", + ((unsigned long long) content->size) / + (1024ULL * 1024ULL)); + } + else { + float f = (content->size / 1024); + f /= (1024 * 1024); + sprintf(sizebuf, "%1.2fG", f); + } + printf("%-50s %s %s", content->key, timebuf, sizebuf); + if (data->allDetails) { + printf(" %-34s %-64s %-12s", + content->eTag, + content->ownerId ? content->ownerId : "", + content->ownerDisplayName ? + content->ownerDisplayName : ""); + } + printf("\n"); + } + } + + data->keyCount += contentsCount; + + for (i = 0; i < commonPrefixesCount; i++) { + printf("\nCommon Prefix: %s\n", commonPrefixes[i]); + } + + return S3StatusOK; } static void list_bucket(const char *bucketName, const char *prefix, - const char *marker, const char *delimiter, - int maxkeys, int allDetails) + const char *marker, const char *delimiter, + int maxkeys, int allDetails) { - S3_init(); - - S3BucketContext bucketContext = - { - bucketName, - protocolG, - uriStyleG, - accessKeyIdG, - secretAccessKeyG - }; - - S3ListBucketHandler listBucketHandler = - { - { &responsePropertiesCallback, &responseCompleteCallback }, - &listBucketCallback - }; - - list_bucket_callback_data data; - - snprintf(data.nextMarker, sizeof(data.nextMarker), "%s", marker); - data.keyCount = 0; - data.allDetails = allDetails; - - do { - data.isTruncated = 0; - do { - S3_list_bucket(&bucketContext, prefix, data.nextMarker, - delimiter, maxkeys, 0, &listBucketHandler, &data); - } while (S3_status_is_retryable(statusG) && should_retry()); - if (statusG != S3StatusOK) { - break; - } - marker = data.nextMarker; - } while (data.isTruncated && (!maxkeys || (data.keyCount < maxkeys))); - - if (statusG == S3StatusOK) { - if (!data.keyCount) { - printListBucketHeader(allDetails); - } - } - else { - printError(); - } - - S3_deinitialize(); + S3_init(); + + S3BucketContext bucketContext = + { + bucketName, + protocolG, + uriStyleG, + accessKeyIdG, + secretAccessKeyG + }; + + S3ListBucketHandler listBucketHandler = + { + { &responsePropertiesCallback, &responseCompleteCallback }, + &listBucketCallback + }; + + list_bucket_callback_data data; + + snprintf(data.nextMarker, sizeof(data.nextMarker), "%s", marker); + data.keyCount = 0; + data.allDetails = allDetails; + + do { + data.isTruncated = 0; + do { + S3_list_bucket(&bucketContext, prefix, data.nextMarker, + delimiter, maxkeys, 0, &listBucketHandler, &data); + } while (S3_status_is_retryable(statusG) && should_retry()); + if (statusG != S3StatusOK) { + break; + } + marker = data.nextMarker; + } while (data.isTruncated && (!maxkeys || (data.keyCount < maxkeys))); + + if (statusG == S3StatusOK) { + if (!data.keyCount) { + printListBucketHeader(allDetails); + } + } + else { + printError(); + } + + S3_deinitialize(); } static void list(int argc, char **argv, int optindex) { - if (optindex == argc) { - list_service(0); - return; - } - - const char *bucketName = 0; - - const char *prefix = 0, *marker = 0, *delimiter = 0; - int maxkeys = 0, allDetails = 0; - while (optindex < argc) { - char *param = argv[optindex++]; - if (!strncmp(param, PREFIX_PREFIX, PREFIX_PREFIX_LEN)) { - prefix = &(param[PREFIX_PREFIX_LEN]); - } - else if (!strncmp(param, MARKER_PREFIX, MARKER_PREFIX_LEN)) { - marker = &(param[MARKER_PREFIX_LEN]); - } - else if (!strncmp(param, DELIMITER_PREFIX, DELIMITER_PREFIX_LEN)) { - delimiter = &(param[DELIMITER_PREFIX_LEN]); - } - else if (!strncmp(param, MAXKEYS_PREFIX, MAXKEYS_PREFIX_LEN)) { - maxkeys = convertInt(&(param[MAXKEYS_PREFIX_LEN]), "maxkeys"); - } - else if (!strncmp(param, ALL_DETAILS_PREFIX, - ALL_DETAILS_PREFIX_LEN)) { - const char *ad = &(param[ALL_DETAILS_PREFIX_LEN]); - if (!strcmp(ad, "true") || !strcmp(ad, "TRUE") || - !strcmp(ad, "yes") || !strcmp(ad, "YES") || - !strcmp(ad, "1")) { - allDetails = 1; - } - } - else if (!bucketName) { - bucketName = param; - } - else { - fprintf(stderr, "\nERROR: Unknown param: %s\n", param); - usageExit(stderr); - } - } - - if (bucketName) { - list_bucket(bucketName, prefix, marker, delimiter, maxkeys, - allDetails); - } - else { - list_service(allDetails); - } + if (optindex == argc) { + list_service(0); + return; + } + + const char *bucketName = 0; + + const char *prefix = 0, *marker = 0, *delimiter = 0; + int maxkeys = 0, allDetails = 0; + while (optindex < argc) { + char *param = argv[optindex++]; + if (!strncmp(param, PREFIX_PREFIX, PREFIX_PREFIX_LEN)) { + prefix = &(param[PREFIX_PREFIX_LEN]); + } + else if (!strncmp(param, MARKER_PREFIX, MARKER_PREFIX_LEN)) { + marker = &(param[MARKER_PREFIX_LEN]); + } + else if (!strncmp(param, DELIMITER_PREFIX, DELIMITER_PREFIX_LEN)) { + delimiter = &(param[DELIMITER_PREFIX_LEN]); + } + else if (!strncmp(param, MAXKEYS_PREFIX, MAXKEYS_PREFIX_LEN)) { + maxkeys = convertInt(&(param[MAXKEYS_PREFIX_LEN]), "maxkeys"); + } + else if (!strncmp(param, ALL_DETAILS_PREFIX, + ALL_DETAILS_PREFIX_LEN)) { + const char *ad = &(param[ALL_DETAILS_PREFIX_LEN]); + if (!strcmp(ad, "true") || !strcmp(ad, "TRUE") || + !strcmp(ad, "yes") || !strcmp(ad, "YES") || + !strcmp(ad, "1")) { + allDetails = 1; + } + } + else if (!bucketName) { + bucketName = param; + } + else { + fprintf(stderr, "\nERROR: Unknown param: %s\n", param); + usageExit(stderr); + } + } + + if (bucketName) { + list_bucket(bucketName, prefix, marker, delimiter, maxkeys, + allDetails); + } + else { + list_service(allDetails); + } } - + // delete object ------------------------------------------------------------- static void delete_object(int argc, char **argv, int optindex) { - (void) argc; - - // Split bucket/key - char *slash = argv[optindex]; - - // We know there is a slash in there, put_object is only called if so - while (*slash && (*slash != '/')) { - slash++; - } - *slash++ = 0; - - const char *bucketName = argv[optindex++]; - const char *key = slash; - - S3_init(); - - S3BucketContext bucketContext = - { - bucketName, - protocolG, - uriStyleG, - accessKeyIdG, - secretAccessKeyG - }; - - S3ResponseHandler responseHandler = - { - 0, - &responseCompleteCallback - }; - - do { - S3_delete_object(&bucketContext, key, 0, &responseHandler, 0); - } while (S3_status_is_retryable(statusG) && should_retry()); - - if ((statusG != S3StatusOK) && - (statusG != S3StatusErrorPreconditionFailed)) { - printError(); - } - - S3_deinitialize(); + (void) argc; + + // Split bucket/key + char *slash = argv[optindex]; + + // We know there is a slash in there, put_object is only called if so + while (*slash && (*slash != '/')) { + slash++; + } + *slash++ = 0; + + const char *bucketName = argv[optindex++]; + const char *key = slash; + + S3_init(); + + S3BucketContext bucketContext = + { + bucketName, + protocolG, + uriStyleG, + accessKeyIdG, + secretAccessKeyG + }; + + S3ResponseHandler responseHandler = + { + 0, + &responseCompleteCallback + }; + + do { + S3_delete_object(&bucketContext, key, 0, &responseHandler, 0); + } while (S3_status_is_retryable(statusG) && should_retry()); + + if ((statusG != S3StatusOK) && + (statusG != S3StatusErrorPreconditionFailed)) { + printError(); + } + + S3_deinitialize(); } @@ -1343,283 +1343,283 @@ static void delete_object(int argc, char **argv, int optindex) typedef struct put_object_callback_data { - FILE *infile; - growbuffer *gb; - uint64_t contentLength, originalContentLength; - int noStatus; + FILE *infile; + growbuffer *gb; + uint64_t contentLength, originalContentLength; + int noStatus; } put_object_callback_data; static int putObjectDataCallback(int bufferSize, char *buffer, - void *callbackData) + void *callbackData) { - put_object_callback_data *data = - (put_object_callback_data *) callbackData; - - int ret = 0; - - if (data->contentLength) { - int toRead = ((data->contentLength > (unsigned) bufferSize) ? - (unsigned) bufferSize : data->contentLength); - if (data->gb) { - growbuffer_read(&(data->gb), toRead, &ret, buffer); - } - else if (data->infile) { - ret = fread(buffer, 1, toRead, data->infile); - } - } - - data->contentLength -= ret; - - if (data->contentLength && !data->noStatus) { - // Avoid a weird bug in MingW, which won't print the second integer - // value properly when it's in the same call, so print separately - printf("%llu bytes remaining ", - (unsigned long long) data->contentLength); - printf("(%d%% complete) ...\n", - (int) (((data->originalContentLength - - data->contentLength) * 100) / - data->originalContentLength)); - } - - return ret; + put_object_callback_data *data = + (put_object_callback_data *) callbackData; + + int ret = 0; + + if (data->contentLength) { + int toRead = ((data->contentLength > (unsigned) bufferSize) ? + (unsigned) bufferSize : data->contentLength); + if (data->gb) { + growbuffer_read(&(data->gb), toRead, &ret, buffer); + } + else if (data->infile) { + ret = fread(buffer, 1, toRead, data->infile); + } + } + + data->contentLength -= ret; + + if (data->contentLength && !data->noStatus) { + // Avoid a weird bug in MingW, which won't print the second integer + // value properly when it's in the same call, so print separately + printf("%llu bytes remaining ", + (unsigned long long) data->contentLength); + printf("(%d%% complete) ...\n", + (int) (((data->originalContentLength - + data->contentLength) * 100) / + data->originalContentLength)); + } + + return ret; } static void put_object(int argc, char **argv, int optindex) { - if (optindex == argc) { - fprintf(stderr, "\nERROR: Missing parameter: bucket/key\n"); - usageExit(stderr); - } - - // Split bucket/key - char *slash = argv[optindex]; - while (*slash && (*slash != '/')) { - slash++; - } - if (!*slash || !*(slash + 1)) { - fprintf(stderr, "\nERROR: Invalid bucket/key name: %s\n", - argv[optindex]); - usageExit(stderr); - } - *slash++ = 0; - - const char *bucketName = argv[optindex++]; - const char *key = slash; - - const char *filename = 0; - uint64_t contentLength = 0; - const char *cacheControl = 0, *contentType = 0, *md5 = 0; - const char *contentDispositionFilename = 0, *contentEncoding = 0; - int64_t expires = -1; - S3CannedAcl cannedAcl = S3CannedAclPrivate; - int metaPropertiesCount = 0; - S3NameValue metaProperties[S3_MAX_METADATA_COUNT]; - int noStatus = 0; - - while (optindex < argc) { - char *param = argv[optindex++]; - if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) { - filename = &(param[FILENAME_PREFIX_LEN]); - } - else if (!strncmp(param, CONTENT_LENGTH_PREFIX, - CONTENT_LENGTH_PREFIX_LEN)) { - contentLength = convertInt(&(param[CONTENT_LENGTH_PREFIX_LEN]), - "contentLength"); - if (contentLength > (5LL * 1024 * 1024 * 1024)) { - fprintf(stderr, "\nERROR: contentLength must be no greater " - "than 5 GB\n"); - usageExit(stderr); - } - } - else if (!strncmp(param, CACHE_CONTROL_PREFIX, - CACHE_CONTROL_PREFIX_LEN)) { - cacheControl = &(param[CACHE_CONTROL_PREFIX_LEN]); - } - else if (!strncmp(param, CONTENT_TYPE_PREFIX, - CONTENT_TYPE_PREFIX_LEN)) { - contentType = &(param[CONTENT_TYPE_PREFIX_LEN]); - } - else if (!strncmp(param, MD5_PREFIX, MD5_PREFIX_LEN)) { - md5 = &(param[MD5_PREFIX_LEN]); - } - else if (!strncmp(param, CONTENT_DISPOSITION_FILENAME_PREFIX, - CONTENT_DISPOSITION_FILENAME_PREFIX_LEN)) { - contentDispositionFilename = - &(param[CONTENT_DISPOSITION_FILENAME_PREFIX_LEN]); - } - else if (!strncmp(param, CONTENT_ENCODING_PREFIX, - CONTENT_ENCODING_PREFIX_LEN)) { - contentEncoding = &(param[CONTENT_ENCODING_PREFIX_LEN]); - } - else if (!strncmp(param, EXPIRES_PREFIX, EXPIRES_PREFIX_LEN)) { - expires = parseIso8601Time(&(param[EXPIRES_PREFIX_LEN])); - if (expires < 0) { - fprintf(stderr, "\nERROR: Invalid expires time " - "value; ISO 8601 time format required\n"); - usageExit(stderr); - } - } - else if (!strncmp(param, X_AMZ_META_PREFIX, X_AMZ_META_PREFIX_LEN)) { - if (metaPropertiesCount == S3_MAX_METADATA_COUNT) { - fprintf(stderr, "\nERROR: Too many x-amz-meta- properties, " - "limit %lu: %s\n", - (unsigned long) S3_MAX_METADATA_COUNT, param); - usageExit(stderr); - } - char *name = &(param[X_AMZ_META_PREFIX_LEN]); - char *value = name; - while (*value && (*value != '=')) { - value++; - } - if (!*value || !*(value + 1)) { - fprintf(stderr, "\nERROR: Invalid parameter: %s\n", param); - usageExit(stderr); - } - *value++ = 0; - metaProperties[metaPropertiesCount].name = name; - metaProperties[metaPropertiesCount++].value = value; - } - else if (!strncmp(param, CANNED_ACL_PREFIX, CANNED_ACL_PREFIX_LEN)) { - char *val = &(param[CANNED_ACL_PREFIX_LEN]); - if (!strcmp(val, "private")) { - cannedAcl = S3CannedAclPrivate; - } - else if (!strcmp(val, "public-read")) { - cannedAcl = S3CannedAclPublicRead; - } - else if (!strcmp(val, "public-read-write")) { - cannedAcl = S3CannedAclPublicReadWrite; - } - else if (!strcmp(val, "authenticated-read")) { - cannedAcl = S3CannedAclAuthenticatedRead; - } - else { - fprintf(stderr, "\nERROR: Unknown canned ACL: %s\n", val); - usageExit(stderr); - } - } - else if (!strncmp(param, NO_STATUS_PREFIX, NO_STATUS_PREFIX_LEN)) { - const char *ns = &(param[NO_STATUS_PREFIX_LEN]); - if (!strcmp(ns, "true") || !strcmp(ns, "TRUE") || - !strcmp(ns, "yes") || !strcmp(ns, "YES") || - !strcmp(ns, "1")) { - noStatus = 1; - } - } - else { - fprintf(stderr, "\nERROR: Unknown param: %s\n", param); - usageExit(stderr); - } - } - - put_object_callback_data data; - - data.infile = 0; - data.gb = 0; - data.noStatus = noStatus; - - if (filename) { - if (!contentLength) { - struct stat statbuf; - // Stat the file to get its length - if (stat(filename, &statbuf) == -1) { - fprintf(stderr, "\nERROR: Failed to stat file %s: ", - filename); - perror(0); - exit(-1); - } - contentLength = statbuf.st_size; - } - // Open the file - if (!(data.infile = fopen(filename, "r" FOPEN_EXTRA_FLAGS))) { - fprintf(stderr, "\nERROR: Failed to open input file %s: ", - filename); - perror(0); - exit(-1); - } - } - else { - // Read from stdin. If contentLength is not provided, we have - // to read it all in to get contentLength. - if (!contentLength) { - // Read all if stdin to get the data - char buffer[64 * 1024]; - while (1) { - int amtRead = fread(buffer, 1, sizeof(buffer), stdin); - if (amtRead == 0) { - break; - } - if (!growbuffer_append(&(data.gb), buffer, amtRead)) { - fprintf(stderr, "\nERROR: Out of memory while reading " - "stdin\n"); - exit(-1); - } - contentLength += amtRead; - if (amtRead < (int) sizeof(buffer)) { - break; - } - } - } - else { - data.infile = stdin; - } - } - - data.contentLength = data.originalContentLength = contentLength; - - S3_init(); - - S3BucketContext bucketContext = - { - bucketName, - protocolG, - uriStyleG, - accessKeyIdG, - secretAccessKeyG - }; - - S3PutProperties putProperties = - { - contentType, - md5, - cacheControl, - contentDispositionFilename, - contentEncoding, - expires, - cannedAcl, - metaPropertiesCount, - metaProperties - }; - - S3PutObjectHandler putObjectHandler = - { - { &responsePropertiesCallback, &responseCompleteCallback }, - &putObjectDataCallback - }; - - do { - S3_put_object(&bucketContext, key, contentLength, &putProperties, 0, - &putObjectHandler, &data); - } while (S3_status_is_retryable(statusG) && should_retry()); - - if (data.infile) { - fclose(data.infile); - } - else if (data.gb) { - growbuffer_destroy(data.gb); - } - - if (statusG != S3StatusOK) { - printError(); - } - else if (data.contentLength) { - fprintf(stderr, "\nERROR: Failed to read remaining %llu bytes from " - "input\n", (unsigned long long) data.contentLength); - } - - S3_deinitialize(); + if (optindex == argc) { + fprintf(stderr, "\nERROR: Missing parameter: bucket/key\n"); + usageExit(stderr); + } + + // Split bucket/key + char *slash = argv[optindex]; + while (*slash && (*slash != '/')) { + slash++; + } + if (!*slash || !*(slash + 1)) { + fprintf(stderr, "\nERROR: Invalid bucket/key name: %s\n", + argv[optindex]); + usageExit(stderr); + } + *slash++ = 0; + + const char *bucketName = argv[optindex++]; + const char *key = slash; + + const char *filename = 0; + uint64_t contentLength = 0; + const char *cacheControl = 0, *contentType = 0, *md5 = 0; + const char *contentDispositionFilename = 0, *contentEncoding = 0; + int64_t expires = -1; + S3CannedAcl cannedAcl = S3CannedAclPrivate; + int metaPropertiesCount = 0; + S3NameValue metaProperties[S3_MAX_METADATA_COUNT]; + int noStatus = 0; + + while (optindex < argc) { + char *param = argv[optindex++]; + if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) { + filename = &(param[FILENAME_PREFIX_LEN]); + } + else if (!strncmp(param, CONTENT_LENGTH_PREFIX, + CONTENT_LENGTH_PREFIX_LEN)) { + contentLength = convertInt(&(param[CONTENT_LENGTH_PREFIX_LEN]), + "contentLength"); + if (contentLength > (5LL * 1024 * 1024 * 1024)) { + fprintf(stderr, "\nERROR: contentLength must be no greater " + "than 5 GB\n"); + usageExit(stderr); + } + } + else if (!strncmp(param, CACHE_CONTROL_PREFIX, + CACHE_CONTROL_PREFIX_LEN)) { + cacheControl = &(param[CACHE_CONTROL_PREFIX_LEN]); + } + else if (!strncmp(param, CONTENT_TYPE_PREFIX, + CONTENT_TYPE_PREFIX_LEN)) { + contentType = &(param[CONTENT_TYPE_PREFIX_LEN]); + } + else if (!strncmp(param, MD5_PREFIX, MD5_PREFIX_LEN)) { + md5 = &(param[MD5_PREFIX_LEN]); + } + else if (!strncmp(param, CONTENT_DISPOSITION_FILENAME_PREFIX, + CONTENT_DISPOSITION_FILENAME_PREFIX_LEN)) { + contentDispositionFilename = + &(param[CONTENT_DISPOSITION_FILENAME_PREFIX_LEN]); + } + else if (!strncmp(param, CONTENT_ENCODING_PREFIX, + CONTENT_ENCODING_PREFIX_LEN)) { + contentEncoding = &(param[CONTENT_ENCODING_PREFIX_LEN]); + } + else if (!strncmp(param, EXPIRES_PREFIX, EXPIRES_PREFIX_LEN)) { + expires = parseIso8601Time(&(param[EXPIRES_PREFIX_LEN])); + if (expires < 0) { + fprintf(stderr, "\nERROR: Invalid expires time " + "value; ISO 8601 time format required\n"); + usageExit(stderr); + } + } + else if (!strncmp(param, X_AMZ_META_PREFIX, X_AMZ_META_PREFIX_LEN)) { + if (metaPropertiesCount == S3_MAX_METADATA_COUNT) { + fprintf(stderr, "\nERROR: Too many x-amz-meta- properties, " + "limit %lu: %s\n", + (unsigned long) S3_MAX_METADATA_COUNT, param); + usageExit(stderr); + } + char *name = &(param[X_AMZ_META_PREFIX_LEN]); + char *value = name; + while (*value && (*value != '=')) { + value++; + } + if (!*value || !*(value + 1)) { + fprintf(stderr, "\nERROR: Invalid parameter: %s\n", param); + usageExit(stderr); + } + *value++ = 0; + metaProperties[metaPropertiesCount].name = name; + metaProperties[metaPropertiesCount++].value = value; + } + else if (!strncmp(param, CANNED_ACL_PREFIX, CANNED_ACL_PREFIX_LEN)) { + char *val = &(param[CANNED_ACL_PREFIX_LEN]); + if (!strcmp(val, "private")) { + cannedAcl = S3CannedAclPrivate; + } + else if (!strcmp(val, "public-read")) { + cannedAcl = S3CannedAclPublicRead; + } + else if (!strcmp(val, "public-read-write")) { + cannedAcl = S3CannedAclPublicReadWrite; + } + else if (!strcmp(val, "authenticated-read")) { + cannedAcl = S3CannedAclAuthenticatedRead; + } + else { + fprintf(stderr, "\nERROR: Unknown canned ACL: %s\n", val); + usageExit(stderr); + } + } + else if (!strncmp(param, NO_STATUS_PREFIX, NO_STATUS_PREFIX_LEN)) { + const char *ns = &(param[NO_STATUS_PREFIX_LEN]); + if (!strcmp(ns, "true") || !strcmp(ns, "TRUE") || + !strcmp(ns, "yes") || !strcmp(ns, "YES") || + !strcmp(ns, "1")) { + noStatus = 1; + } + } + else { + fprintf(stderr, "\nERROR: Unknown param: %s\n", param); + usageExit(stderr); + } + } + + put_object_callback_data data; + + data.infile = 0; + data.gb = 0; + data.noStatus = noStatus; + + if (filename) { + if (!contentLength) { + struct stat statbuf; + // Stat the file to get its length + if (stat(filename, &statbuf) == -1) { + fprintf(stderr, "\nERROR: Failed to stat file %s: ", + filename); + perror(0); + exit(-1); + } + contentLength = statbuf.st_size; + } + // Open the file + if (!(data.infile = fopen(filename, "r" FOPEN_EXTRA_FLAGS))) { + fprintf(stderr, "\nERROR: Failed to open input file %s: ", + filename); + perror(0); + exit(-1); + } + } + else { + // Read from stdin. If contentLength is not provided, we have + // to read it all in to get contentLength. + if (!contentLength) { + // Read all if stdin to get the data + char buffer[64 * 1024]; + while (1) { + int amtRead = fread(buffer, 1, sizeof(buffer), stdin); + if (amtRead == 0) { + break; + } + if (!growbuffer_append(&(data.gb), buffer, amtRead)) { + fprintf(stderr, "\nERROR: Out of memory while reading " + "stdin\n"); + exit(-1); + } + contentLength += amtRead; + if (amtRead < (int) sizeof(buffer)) { + break; + } + } + } + else { + data.infile = stdin; + } + } + + data.contentLength = data.originalContentLength = contentLength; + + S3_init(); + + S3BucketContext bucketContext = + { + bucketName, + protocolG, + uriStyleG, + accessKeyIdG, + secretAccessKeyG + }; + + S3PutProperties putProperties = + { + contentType, + md5, + cacheControl, + contentDispositionFilename, + contentEncoding, + expires, + cannedAcl, + metaPropertiesCount, + metaProperties + }; + + S3PutObjectHandler putObjectHandler = + { + { &responsePropertiesCallback, &responseCompleteCallback }, + &putObjectDataCallback + }; + + do { + S3_put_object(&bucketContext, key, contentLength, &putProperties, 0, + &putObjectHandler, &data); + } while (S3_status_is_retryable(statusG) && should_retry()); + + if (data.infile) { + fclose(data.infile); + } + else if (data.gb) { + growbuffer_destroy(data.gb); + } + + if (statusG != S3StatusOK) { + printError(); + } + else if (data.contentLength) { + fprintf(stderr, "\nERROR: Failed to read remaining %llu bytes from " + "input\n", (unsigned long long) data.contentLength); + } + + S3_deinitialize(); } @@ -1627,355 +1627,355 @@ static void put_object(int argc, char **argv, int optindex) static void copy_object(int argc, char **argv, int optindex) { - if (optindex == argc) { - fprintf(stderr, "\nERROR: Missing parameter: source bucket/key\n"); - usageExit(stderr); - } - - // Split bucket/key - char *slash = argv[optindex]; - while (*slash && (*slash != '/')) { - slash++; - } - if (!*slash || !*(slash + 1)) { - fprintf(stderr, "\nERROR: Invalid source bucket/key name: %s\n", - argv[optindex]); - usageExit(stderr); - } - *slash++ = 0; - - const char *sourceBucketName = argv[optindex++]; - const char *sourceKey = slash; - - if (optindex == argc) { - fprintf(stderr, "\nERROR: Missing parameter: " - "destination bucket/key\n"); - usageExit(stderr); - } - - // Split bucket/key - slash = argv[optindex]; - while (*slash && (*slash != '/')) { - slash++; - } - if (!*slash || !*(slash + 1)) { - fprintf(stderr, "\nERROR: Invalid destination bucket/key name: %s\n", - argv[optindex]); - usageExit(stderr); - } - *slash++ = 0; - - const char *destinationBucketName = argv[optindex++]; - const char *destinationKey = slash; - - const char *cacheControl = 0, *contentType = 0; - const char *contentDispositionFilename = 0, *contentEncoding = 0; - int64_t expires = -1; - S3CannedAcl cannedAcl = S3CannedAclPrivate; - int metaPropertiesCount = 0; - S3NameValue metaProperties[S3_MAX_METADATA_COUNT]; - int anyPropertiesSet = 0; - - while (optindex < argc) { - char *param = argv[optindex++]; - if (!strncmp(param, CACHE_CONTROL_PREFIX, - CACHE_CONTROL_PREFIX_LEN)) { - cacheControl = &(param[CACHE_CONTROL_PREFIX_LEN]); - anyPropertiesSet = 1; - } - else if (!strncmp(param, CONTENT_TYPE_PREFIX, - CONTENT_TYPE_PREFIX_LEN)) { - contentType = &(param[CONTENT_TYPE_PREFIX_LEN]); - anyPropertiesSet = 1; - } - else if (!strncmp(param, CONTENT_DISPOSITION_FILENAME_PREFIX, - CONTENT_DISPOSITION_FILENAME_PREFIX_LEN)) { - contentDispositionFilename = - &(param[CONTENT_DISPOSITION_FILENAME_PREFIX_LEN]); - anyPropertiesSet = 1; - } - else if (!strncmp(param, CONTENT_ENCODING_PREFIX, - CONTENT_ENCODING_PREFIX_LEN)) { - contentEncoding = &(param[CONTENT_ENCODING_PREFIX_LEN]); - anyPropertiesSet = 1; - } - else if (!strncmp(param, EXPIRES_PREFIX, EXPIRES_PREFIX_LEN)) { - expires = parseIso8601Time(&(param[EXPIRES_PREFIX_LEN])); - if (expires < 0) { - fprintf(stderr, "\nERROR: Invalid expires time " - "value; ISO 8601 time format required\n"); - usageExit(stderr); - } - anyPropertiesSet = 1; - } - else if (!strncmp(param, X_AMZ_META_PREFIX, X_AMZ_META_PREFIX_LEN)) { - if (metaPropertiesCount == S3_MAX_METADATA_COUNT) { - fprintf(stderr, "\nERROR: Too many x-amz-meta- properties, " - "limit %lu: %s\n", - (unsigned long) S3_MAX_METADATA_COUNT, param); - usageExit(stderr); - } - char *name = &(param[X_AMZ_META_PREFIX_LEN]); - char *value = name; - while (*value && (*value != '=')) { - value++; - } - if (!*value || !*(value + 1)) { - fprintf(stderr, "\nERROR: Invalid parameter: %s\n", param); - usageExit(stderr); - } - *value++ = 0; - metaProperties[metaPropertiesCount].name = name; - metaProperties[metaPropertiesCount++].value = value; - anyPropertiesSet = 1; - } - else if (!strncmp(param, CANNED_ACL_PREFIX, CANNED_ACL_PREFIX_LEN)) { - char *val = &(param[CANNED_ACL_PREFIX_LEN]); - if (!strcmp(val, "private")) { - cannedAcl = S3CannedAclPrivate; - } - else if (!strcmp(val, "public-read")) { - cannedAcl = S3CannedAclPublicRead; - } - else if (!strcmp(val, "public-read-write")) { - cannedAcl = S3CannedAclPublicReadWrite; - } - else if (!strcmp(val, "authenticated-read")) { - cannedAcl = S3CannedAclAuthenticatedRead; - } - else { - fprintf(stderr, "\nERROR: Unknown canned ACL: %s\n", val); - usageExit(stderr); - } - anyPropertiesSet = 1; - } - else { - fprintf(stderr, "\nERROR: Unknown param: %s\n", param); - usageExit(stderr); - } - } - - S3_init(); - - S3BucketContext bucketContext = - { - sourceBucketName, - protocolG, - uriStyleG, - accessKeyIdG, - secretAccessKeyG - }; - - S3PutProperties putProperties = - { - contentType, - 0, - cacheControl, - contentDispositionFilename, - contentEncoding, - expires, - cannedAcl, - metaPropertiesCount, - metaProperties - }; - - S3ResponseHandler responseHandler = - { - &responsePropertiesCallback, - &responseCompleteCallback - }; - - int64_t lastModified; - char eTag[256]; - - do { - S3_copy_object(&bucketContext, sourceKey, destinationBucketName, - destinationKey, anyPropertiesSet ? &putProperties : 0, - &lastModified, sizeof(eTag), eTag, 0, - &responseHandler, 0); - } while (S3_status_is_retryable(statusG) && should_retry()); - - if (statusG == S3StatusOK) { - if (lastModified >= 0) { - char timebuf[256]; - time_t t = (time_t) lastModified; - strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ", - gmtime(&t)); - printf("Last-Modified: %s\n", timebuf); - } - if (eTag[0]) { - printf("ETag: %s\n", eTag); - } - } - else { - printError(); - } - - S3_deinitialize(); + if (optindex == argc) { + fprintf(stderr, "\nERROR: Missing parameter: source bucket/key\n"); + usageExit(stderr); + } + + // Split bucket/key + char *slash = argv[optindex]; + while (*slash && (*slash != '/')) { + slash++; + } + if (!*slash || !*(slash + 1)) { + fprintf(stderr, "\nERROR: Invalid source bucket/key name: %s\n", + argv[optindex]); + usageExit(stderr); + } + *slash++ = 0; + + const char *sourceBucketName = argv[optindex++]; + const char *sourceKey = slash; + + if (optindex == argc) { + fprintf(stderr, "\nERROR: Missing parameter: " + "destination bucket/key\n"); + usageExit(stderr); + } + + // Split bucket/key + slash = argv[optindex]; + while (*slash && (*slash != '/')) { + slash++; + } + if (!*slash || !*(slash + 1)) { + fprintf(stderr, "\nERROR: Invalid destination bucket/key name: %s\n", + argv[optindex]); + usageExit(stderr); + } + *slash++ = 0; + + const char *destinationBucketName = argv[optindex++]; + const char *destinationKey = slash; + + const char *cacheControl = 0, *contentType = 0; + const char *contentDispositionFilename = 0, *contentEncoding = 0; + int64_t expires = -1; + S3CannedAcl cannedAcl = S3CannedAclPrivate; + int metaPropertiesCount = 0; + S3NameValue metaProperties[S3_MAX_METADATA_COUNT]; + int anyPropertiesSet = 0; + + while (optindex < argc) { + char *param = argv[optindex++]; + if (!strncmp(param, CACHE_CONTROL_PREFIX, + CACHE_CONTROL_PREFIX_LEN)) { + cacheControl = &(param[CACHE_CONTROL_PREFIX_LEN]); + anyPropertiesSet = 1; + } + else if (!strncmp(param, CONTENT_TYPE_PREFIX, + CONTENT_TYPE_PREFIX_LEN)) { + contentType = &(param[CONTENT_TYPE_PREFIX_LEN]); + anyPropertiesSet = 1; + } + else if (!strncmp(param, CONTENT_DISPOSITION_FILENAME_PREFIX, + CONTENT_DISPOSITION_FILENAME_PREFIX_LEN)) { + contentDispositionFilename = + &(param[CONTENT_DISPOSITION_FILENAME_PREFIX_LEN]); + anyPropertiesSet = 1; + } + else if (!strncmp(param, CONTENT_ENCODING_PREFIX, + CONTENT_ENCODING_PREFIX_LEN)) { + contentEncoding = &(param[CONTENT_ENCODING_PREFIX_LEN]); + anyPropertiesSet = 1; + } + else if (!strncmp(param, EXPIRES_PREFIX, EXPIRES_PREFIX_LEN)) { + expires = parseIso8601Time(&(param[EXPIRES_PREFIX_LEN])); + if (expires < 0) { + fprintf(stderr, "\nERROR: Invalid expires time " + "value; ISO 8601 time format required\n"); + usageExit(stderr); + } + anyPropertiesSet = 1; + } + else if (!strncmp(param, X_AMZ_META_PREFIX, X_AMZ_META_PREFIX_LEN)) { + if (metaPropertiesCount == S3_MAX_METADATA_COUNT) { + fprintf(stderr, "\nERROR: Too many x-amz-meta- properties, " + "limit %lu: %s\n", + (unsigned long) S3_MAX_METADATA_COUNT, param); + usageExit(stderr); + } + char *name = &(param[X_AMZ_META_PREFIX_LEN]); + char *value = name; + while (*value && (*value != '=')) { + value++; + } + if (!*value || !*(value + 1)) { + fprintf(stderr, "\nERROR: Invalid parameter: %s\n", param); + usageExit(stderr); + } + *value++ = 0; + metaProperties[metaPropertiesCount].name = name; + metaProperties[metaPropertiesCount++].value = value; + anyPropertiesSet = 1; + } + else if (!strncmp(param, CANNED_ACL_PREFIX, CANNED_ACL_PREFIX_LEN)) { + char *val = &(param[CANNED_ACL_PREFIX_LEN]); + if (!strcmp(val, "private")) { + cannedAcl = S3CannedAclPrivate; + } + else if (!strcmp(val, "public-read")) { + cannedAcl = S3CannedAclPublicRead; + } + else if (!strcmp(val, "public-read-write")) { + cannedAcl = S3CannedAclPublicReadWrite; + } + else if (!strcmp(val, "authenticated-read")) { + cannedAcl = S3CannedAclAuthenticatedRead; + } + else { + fprintf(stderr, "\nERROR: Unknown canned ACL: %s\n", val); + usageExit(stderr); + } + anyPropertiesSet = 1; + } + else { + fprintf(stderr, "\nERROR: Unknown param: %s\n", param); + usageExit(stderr); + } + } + + S3_init(); + + S3BucketContext bucketContext = + { + sourceBucketName, + protocolG, + uriStyleG, + accessKeyIdG, + secretAccessKeyG + }; + + S3PutProperties putProperties = + { + contentType, + 0, + cacheControl, + contentDispositionFilename, + contentEncoding, + expires, + cannedAcl, + metaPropertiesCount, + metaProperties + }; + + S3ResponseHandler responseHandler = + { + &responsePropertiesCallback, + &responseCompleteCallback + }; + + int64_t lastModified; + char eTag[256]; + + do { + S3_copy_object(&bucketContext, sourceKey, destinationBucketName, + destinationKey, anyPropertiesSet ? &putProperties : 0, + &lastModified, sizeof(eTag), eTag, 0, + &responseHandler, 0); + } while (S3_status_is_retryable(statusG) && should_retry()); + + if (statusG == S3StatusOK) { + if (lastModified >= 0) { + char timebuf[256]; + time_t t = (time_t) lastModified; + strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ", + gmtime(&t)); + printf("Last-Modified: %s\n", timebuf); + } + if (eTag[0]) { + printf("ETag: %s\n", eTag); + } + } + else { + printError(); + } + + S3_deinitialize(); } // get object ---------------------------------------------------------------- static S3Status getObjectDataCallback(int bufferSize, const char *buffer, - void *callbackData) + void *callbackData) { - FILE *outfile = (FILE *) callbackData; + FILE *outfile = (FILE *) callbackData; - size_t wrote = fwrite(buffer, 1, bufferSize, outfile); - - return ((wrote < (size_t) bufferSize) ? - S3StatusAbortedByCallback : S3StatusOK); + size_t wrote = fwrite(buffer, 1, bufferSize, outfile); + + return ((wrote < (size_t) bufferSize) ? + S3StatusAbortedByCallback : S3StatusOK); } static void get_object(int argc, char **argv, int optindex) { - if (optindex == argc) { - fprintf(stderr, "\nERROR: Missing parameter: bucket/key\n"); - usageExit(stderr); - } - - // Split bucket/key - char *slash = argv[optindex]; - while (*slash && (*slash != '/')) { - slash++; - } - if (!*slash || !*(slash + 1)) { - fprintf(stderr, "\nERROR: Invalid bucket/key name: %s\n", - argv[optindex]); - usageExit(stderr); - } - *slash++ = 0; - - const char *bucketName = argv[optindex++]; - const char *key = slash; - - const char *filename = 0; - int64_t ifModifiedSince = -1, ifNotModifiedSince = -1; - const char *ifMatch = 0, *ifNotMatch = 0; - uint64_t startByte = 0, byteCount = 0; - - while (optindex < argc) { - char *param = argv[optindex++]; - if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) { - filename = &(param[FILENAME_PREFIX_LEN]); - } - else if (!strncmp(param, IF_MODIFIED_SINCE_PREFIX, - IF_MODIFIED_SINCE_PREFIX_LEN)) { - // Parse ifModifiedSince - ifModifiedSince = parseIso8601Time - (&(param[IF_MODIFIED_SINCE_PREFIX_LEN])); - if (ifModifiedSince < 0) { - fprintf(stderr, "\nERROR: Invalid ifModifiedSince time " - "value; ISO 8601 time format required\n"); - usageExit(stderr); - } - } - else if (!strncmp(param, IF_NOT_MODIFIED_SINCE_PREFIX, - IF_NOT_MODIFIED_SINCE_PREFIX_LEN)) { - // Parse ifModifiedSince - ifNotModifiedSince = parseIso8601Time - (&(param[IF_NOT_MODIFIED_SINCE_PREFIX_LEN])); - if (ifNotModifiedSince < 0) { - fprintf(stderr, "\nERROR: Invalid ifNotModifiedSince time " - "value; ISO 8601 time format required\n"); - usageExit(stderr); - } - } - else if (!strncmp(param, IF_MATCH_PREFIX, IF_MATCH_PREFIX_LEN)) { - ifMatch = &(param[IF_MATCH_PREFIX_LEN]); - } - else if (!strncmp(param, IF_NOT_MATCH_PREFIX, - IF_NOT_MATCH_PREFIX_LEN)) { - ifNotMatch = &(param[IF_NOT_MATCH_PREFIX_LEN]); - } - else if (!strncmp(param, START_BYTE_PREFIX, START_BYTE_PREFIX_LEN)) { - startByte = convertInt - (&(param[START_BYTE_PREFIX_LEN]), "startByte"); - } - else if (!strncmp(param, BYTE_COUNT_PREFIX, BYTE_COUNT_PREFIX_LEN)) { - byteCount = convertInt - (&(param[BYTE_COUNT_PREFIX_LEN]), "byteCount"); - } - else { - fprintf(stderr, "\nERROR: Unknown param: %s\n", param); - usageExit(stderr); - } - } - - FILE *outfile = 0; - - if (filename) { - // Stat the file, and if it doesn't exist, open it in w mode - struct stat buf; - if (stat(filename, &buf) == -1) { - outfile = fopen(filename, "w" FOPEN_EXTRA_FLAGS); - } - else { - // Open in r+ so that we don't truncate the file, just in case - // there is an error and we write no bytes, we leave the file - // unmodified - outfile = fopen(filename, "r+" FOPEN_EXTRA_FLAGS); - } - - if (!outfile) { - fprintf(stderr, "\nERROR: Failed to open output file %s: ", - filename); - perror(0); - exit(-1); - } - } - else if (showResponsePropertiesG) { - fprintf(stderr, "\nERROR: get -s requires a filename parameter\n"); - usageExit(stderr); - } - else { - outfile = stdout; - } - - S3_init(); - - S3BucketContext bucketContext = - { - bucketName, - protocolG, - uriStyleG, - accessKeyIdG, - secretAccessKeyG - }; - - S3GetConditions getConditions = - { - ifModifiedSince, - ifNotModifiedSince, - ifMatch, - ifNotMatch - }; - - S3GetObjectHandler getObjectHandler = - { - { &responsePropertiesCallback, &responseCompleteCallback }, - &getObjectDataCallback - }; - - do { - S3_get_object(&bucketContext, key, &getConditions, startByte, - byteCount, 0, &getObjectHandler, outfile); - } while (S3_status_is_retryable(statusG) && should_retry()); - - if (statusG == S3StatusOK) { - if (outfile != stdout) { - ftruncate(fileno(outfile), ftell(outfile)); - } - } - else { - printError(); - } - - fclose(outfile); - - S3_deinitialize(); + if (optindex == argc) { + fprintf(stderr, "\nERROR: Missing parameter: bucket/key\n"); + usageExit(stderr); + } + + // Split bucket/key + char *slash = argv[optindex]; + while (*slash && (*slash != '/')) { + slash++; + } + if (!*slash || !*(slash + 1)) { + fprintf(stderr, "\nERROR: Invalid bucket/key name: %s\n", + argv[optindex]); + usageExit(stderr); + } + *slash++ = 0; + + const char *bucketName = argv[optindex++]; + const char *key = slash; + + const char *filename = 0; + int64_t ifModifiedSince = -1, ifNotModifiedSince = -1; + const char *ifMatch = 0, *ifNotMatch = 0; + uint64_t startByte = 0, byteCount = 0; + + while (optindex < argc) { + char *param = argv[optindex++]; + if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) { + filename = &(param[FILENAME_PREFIX_LEN]); + } + else if (!strncmp(param, IF_MODIFIED_SINCE_PREFIX, + IF_MODIFIED_SINCE_PREFIX_LEN)) { + // Parse ifModifiedSince + ifModifiedSince = parseIso8601Time + (&(param[IF_MODIFIED_SINCE_PREFIX_LEN])); + if (ifModifiedSince < 0) { + fprintf(stderr, "\nERROR: Invalid ifModifiedSince time " + "value; ISO 8601 time format required\n"); + usageExit(stderr); + } + } + else if (!strncmp(param, IF_NOT_MODIFIED_SINCE_PREFIX, + IF_NOT_MODIFIED_SINCE_PREFIX_LEN)) { + // Parse ifModifiedSince + ifNotModifiedSince = parseIso8601Time + (&(param[IF_NOT_MODIFIED_SINCE_PREFIX_LEN])); + if (ifNotModifiedSince < 0) { + fprintf(stderr, "\nERROR: Invalid ifNotModifiedSince time " + "value; ISO 8601 time format required\n"); + usageExit(stderr); + } + } + else if (!strncmp(param, IF_MATCH_PREFIX, IF_MATCH_PREFIX_LEN)) { + ifMatch = &(param[IF_MATCH_PREFIX_LEN]); + } + else if (!strncmp(param, IF_NOT_MATCH_PREFIX, + IF_NOT_MATCH_PREFIX_LEN)) { + ifNotMatch = &(param[IF_NOT_MATCH_PREFIX_LEN]); + } + else if (!strncmp(param, START_BYTE_PREFIX, START_BYTE_PREFIX_LEN)) { + startByte = convertInt + (&(param[START_BYTE_PREFIX_LEN]), "startByte"); + } + else if (!strncmp(param, BYTE_COUNT_PREFIX, BYTE_COUNT_PREFIX_LEN)) { + byteCount = convertInt + (&(param[BYTE_COUNT_PREFIX_LEN]), "byteCount"); + } + else { + fprintf(stderr, "\nERROR: Unknown param: %s\n", param); + usageExit(stderr); + } + } + + FILE *outfile = 0; + + if (filename) { + // Stat the file, and if it doesn't exist, open it in w mode + struct stat buf; + if (stat(filename, &buf) == -1) { + outfile = fopen(filename, "w" FOPEN_EXTRA_FLAGS); + } + else { + // Open in r+ so that we don't truncate the file, just in case + // there is an error and we write no bytes, we leave the file + // unmodified + outfile = fopen(filename, "r+" FOPEN_EXTRA_FLAGS); + } + + if (!outfile) { + fprintf(stderr, "\nERROR: Failed to open output file %s: ", + filename); + perror(0); + exit(-1); + } + } + else if (showResponsePropertiesG) { + fprintf(stderr, "\nERROR: get -s requires a filename parameter\n"); + usageExit(stderr); + } + else { + outfile = stdout; + } + + S3_init(); + + S3BucketContext bucketContext = + { + bucketName, + protocolG, + uriStyleG, + accessKeyIdG, + secretAccessKeyG + }; + + S3GetConditions getConditions = + { + ifModifiedSince, + ifNotModifiedSince, + ifMatch, + ifNotMatch + }; + + S3GetObjectHandler getObjectHandler = + { + { &responsePropertiesCallback, &responseCompleteCallback }, + &getObjectDataCallback + }; + + do { + S3_get_object(&bucketContext, key, &getConditions, startByte, + byteCount, 0, &getObjectHandler, outfile); + } while (S3_status_is_retryable(statusG) && should_retry()); + + if (statusG == S3StatusOK) { + if (outfile != stdout) { + ftruncate(fileno(outfile), ftell(outfile)); + } + } + else { + printError(); + } + + fclose(outfile); + + S3_deinitialize(); } @@ -1983,62 +1983,62 @@ static void get_object(int argc, char **argv, int optindex) static void head_object(int argc, char **argv, int optindex) { - if (optindex == argc) { - fprintf(stderr, "\nERROR: Missing parameter: bucket/key\n"); - usageExit(stderr); - } - - // Head implies showing response properties - showResponsePropertiesG = 1; - - // Split bucket/key - char *slash = argv[optindex]; - - while (*slash && (*slash != '/')) { - slash++; - } - if (!*slash || !*(slash + 1)) { - fprintf(stderr, "\nERROR: Invalid bucket/key name: %s\n", - argv[optindex]); - usageExit(stderr); - } - *slash++ = 0; - - const char *bucketName = argv[optindex++]; - const char *key = slash; - - if (optindex != argc) { - fprintf(stderr, "\nERROR: Extraneous parameter: %s\n", argv[optindex]); - usageExit(stderr); - } - - S3_init(); - - S3BucketContext bucketContext = - { - bucketName, - protocolG, - uriStyleG, - accessKeyIdG, - secretAccessKeyG - }; - - S3ResponseHandler responseHandler = - { - &responsePropertiesCallback, - &responseCompleteCallback - }; - - do { - S3_head_object(&bucketContext, key, 0, &responseHandler, 0); - } while (S3_status_is_retryable(statusG) && should_retry()); - - if ((statusG != S3StatusOK) && - (statusG != S3StatusErrorPreconditionFailed)) { - printError(); - } - - S3_deinitialize(); + if (optindex == argc) { + fprintf(stderr, "\nERROR: Missing parameter: bucket/key\n"); + usageExit(stderr); + } + + // Head implies showing response properties + showResponsePropertiesG = 1; + + // Split bucket/key + char *slash = argv[optindex]; + + while (*slash && (*slash != '/')) { + slash++; + } + if (!*slash || !*(slash + 1)) { + fprintf(stderr, "\nERROR: Invalid bucket/key name: %s\n", + argv[optindex]); + usageExit(stderr); + } + *slash++ = 0; + + const char *bucketName = argv[optindex++]; + const char *key = slash; + + if (optindex != argc) { + fprintf(stderr, "\nERROR: Extraneous parameter: %s\n", argv[optindex]); + usageExit(stderr); + } + + S3_init(); + + S3BucketContext bucketContext = + { + bucketName, + protocolG, + uriStyleG, + accessKeyIdG, + secretAccessKeyG + }; + + S3ResponseHandler responseHandler = + { + &responsePropertiesCallback, + &responseCompleteCallback + }; + + do { + S3_head_object(&bucketContext, key, 0, &responseHandler, 0); + } while (S3_status_is_retryable(statusG) && should_retry()); + + if ((statusG != S3StatusOK) && + (statusG != S3StatusErrorPreconditionFailed)) { + printError(); + } + + S3_deinitialize(); } @@ -2046,75 +2046,75 @@ static void head_object(int argc, char **argv, int optindex) static void generate_query_string(int argc, char **argv, int optindex) { - if (optindex == argc) { - fprintf(stderr, "\nERROR: Missing parameter: bucket[/key]\n"); - usageExit(stderr); - } - - const char *bucketName = argv[optindex]; - const char *key = 0; - - // Split bucket/key - char *slash = argv[optindex++]; - while (*slash && (*slash != '/')) { - slash++; - } - if (*slash) { - *slash++ = 0; - key = slash; - } - else { - key = 0; - } - - int64_t expires = -1; - - const char *resource = 0; - - while (optindex < argc) { - char *param = argv[optindex++]; - if (!strncmp(param, EXPIRES_PREFIX, EXPIRES_PREFIX_LEN)) { - expires = parseIso8601Time(&(param[EXPIRES_PREFIX_LEN])); - if (expires < 0) { - fprintf(stderr, "\nERROR: Invalid expires time " - "value; ISO 8601 time format required\n"); - usageExit(stderr); - } - } - else if (!strncmp(param, RESOURCE_PREFIX, RESOURCE_PREFIX_LEN)) { - resource = &(param[RESOURCE_PREFIX_LEN]); - } - else { - fprintf(stderr, "\nERROR: Unknown param: %s\n", param); - usageExit(stderr); - } - } - - S3_init(); - - S3BucketContext bucketContext = - { - bucketName, - protocolG, - uriStyleG, - accessKeyIdG, - secretAccessKeyG - }; - - char buffer[S3_MAX_AUTHENTICATED_QUERY_STRING_SIZE]; - - S3Status status = S3_generate_authenticated_query_string - (buffer, &bucketContext, key, expires, resource); - - if (status != S3StatusOK) { - printf("Failed to generate authenticated query string: %s\n", - S3_get_status_name(status)); - } - else { - printf("%s\n", buffer); - } - - S3_deinitialize(); + if (optindex == argc) { + fprintf(stderr, "\nERROR: Missing parameter: bucket[/key]\n"); + usageExit(stderr); + } + + const char *bucketName = argv[optindex]; + const char *key = 0; + + // Split bucket/key + char *slash = argv[optindex++]; + while (*slash && (*slash != '/')) { + slash++; + } + if (*slash) { + *slash++ = 0; + key = slash; + } + else { + key = 0; + } + + int64_t expires = -1; + + const char *resource = 0; + + while (optindex < argc) { + char *param = argv[optindex++]; + if (!strncmp(param, EXPIRES_PREFIX, EXPIRES_PREFIX_LEN)) { + expires = parseIso8601Time(&(param[EXPIRES_PREFIX_LEN])); + if (expires < 0) { + fprintf(stderr, "\nERROR: Invalid expires time " + "value; ISO 8601 time format required\n"); + usageExit(stderr); + } + } + else if (!strncmp(param, RESOURCE_PREFIX, RESOURCE_PREFIX_LEN)) { + resource = &(param[RESOURCE_PREFIX_LEN]); + } + else { + fprintf(stderr, "\nERROR: Unknown param: %s\n", param); + usageExit(stderr); + } + } + + S3_init(); + + S3BucketContext bucketContext = + { + bucketName, + protocolG, + uriStyleG, + accessKeyIdG, + secretAccessKeyG + }; + + char buffer[S3_MAX_AUTHENTICATED_QUERY_STRING_SIZE]; + + S3Status status = S3_generate_authenticated_query_string + (buffer, &bucketContext, key, expires, resource); + + if (status != S3StatusOK) { + printf("Failed to generate authenticated query string: %s\n", + S3_get_status_name(status)); + } + else { + printf("%s\n", buffer); + } + + S3_deinitialize(); } @@ -2122,166 +2122,166 @@ static void generate_query_string(int argc, char **argv, int optindex) void get_acl(int argc, char **argv, int optindex) { - if (optindex == argc) { - fprintf(stderr, "\nERROR: Missing parameter: bucket[/key]\n"); - usageExit(stderr); - } - - const char *bucketName = argv[optindex]; - const char *key = 0; - - // Split bucket/key - char *slash = argv[optindex++]; - while (*slash && (*slash != '/')) { - slash++; - } - if (*slash) { - *slash++ = 0; - key = slash; - } - else { - key = 0; - } - - const char *filename = 0; - - while (optindex < argc) { - char *param = argv[optindex++]; - if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) { - filename = &(param[FILENAME_PREFIX_LEN]); - } - else { - fprintf(stderr, "\nERROR: Unknown param: %s\n", param); - usageExit(stderr); - } - } - - FILE *outfile = 0; - - if (filename) { - // Stat the file, and if it doesn't exist, open it in w mode - struct stat buf; - if (stat(filename, &buf) == -1) { - outfile = fopen(filename, "w" FOPEN_EXTRA_FLAGS); - } - else { - // Open in r+ so that we don't truncate the file, just in case - // there is an error and we write no bytes, we leave the file - // unmodified - outfile = fopen(filename, "r+" FOPEN_EXTRA_FLAGS); - } - - if (!outfile) { - fprintf(stderr, "\nERROR: Failed to open output file %s: ", - filename); - perror(0); - exit(-1); - } - } - else if (showResponsePropertiesG) { - fprintf(stderr, "\nERROR: getacl -s requires a filename parameter\n"); - usageExit(stderr); - } - else { - outfile = stdout; - } - - int aclGrantCount; - S3AclGrant aclGrants[S3_MAX_ACL_GRANT_COUNT]; - char ownerId[S3_MAX_GRANTEE_USER_ID_SIZE]; - char ownerDisplayName[S3_MAX_GRANTEE_DISPLAY_NAME_SIZE]; - - S3_init(); - - S3BucketContext bucketContext = - { - bucketName, - protocolG, - uriStyleG, - accessKeyIdG, - secretAccessKeyG - }; - - S3ResponseHandler responseHandler = - { - &responsePropertiesCallback, - &responseCompleteCallback - }; - - do { - S3_get_acl(&bucketContext, key, ownerId, ownerDisplayName, - &aclGrantCount, aclGrants, 0, &responseHandler, 0); - } while (S3_status_is_retryable(statusG) && should_retry()); - - if (statusG == S3StatusOK) { - fprintf(outfile, "OwnerID %s %s\n", ownerId, ownerDisplayName); - fprintf(outfile, "%-6s %-90s %-12s\n", " Type", - " User Identifier", - " Permission"); - fprintf(outfile, "------ " - "------------------------------------------------------------" - "------------------------------ ------------\n"); - int i; - for (i = 0; i < aclGrantCount; i++) { - S3AclGrant *grant = &(aclGrants[i]); - const char *type; - char composedId[S3_MAX_GRANTEE_USER_ID_SIZE + - S3_MAX_GRANTEE_DISPLAY_NAME_SIZE + 16]; - const char *id; - - switch (grant->granteeType) { - case S3GranteeTypeAmazonCustomerByEmail: - type = "Email"; - id = grant->grantee.amazonCustomerByEmail.emailAddress; - break; - case S3GranteeTypeCanonicalUser: - type = "UserID"; - snprintf(composedId, sizeof(composedId), - "%s (%s)", grant->grantee.canonicalUser.id, - grant->grantee.canonicalUser.displayName); - id = composedId; - break; - case S3GranteeTypeAllAwsUsers: - type = "Group"; - id = "Authenticated AWS Users"; - break; - case S3GranteeTypeAllUsers: - type = "Group"; - id = "All Users"; - break; - default: - type = "Group"; - id = "Log Delivery"; - break; - } - const char *perm; - switch (grant->permission) { - case S3PermissionRead: - perm = "READ"; - break; - case S3PermissionWrite: - perm = "WRITE"; - break; - case S3PermissionReadACP: - perm = "READ_ACP"; - break; - case S3PermissionWriteACP: - perm = "WRITE_ACP"; - break; - default: - perm = "FULL_CONTROL"; - break; - } - fprintf(outfile, "%-6s %-90s %-12s\n", type, id, perm); - } - } - else { - printError(); - } - - fclose(outfile); - - S3_deinitialize(); + if (optindex == argc) { + fprintf(stderr, "\nERROR: Missing parameter: bucket[/key]\n"); + usageExit(stderr); + } + + const char *bucketName = argv[optindex]; + const char *key = 0; + + // Split bucket/key + char *slash = argv[optindex++]; + while (*slash && (*slash != '/')) { + slash++; + } + if (*slash) { + *slash++ = 0; + key = slash; + } + else { + key = 0; + } + + const char *filename = 0; + + while (optindex < argc) { + char *param = argv[optindex++]; + if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) { + filename = &(param[FILENAME_PREFIX_LEN]); + } + else { + fprintf(stderr, "\nERROR: Unknown param: %s\n", param); + usageExit(stderr); + } + } + + FILE *outfile = 0; + + if (filename) { + // Stat the file, and if it doesn't exist, open it in w mode + struct stat buf; + if (stat(filename, &buf) == -1) { + outfile = fopen(filename, "w" FOPEN_EXTRA_FLAGS); + } + else { + // Open in r+ so that we don't truncate the file, just in case + // there is an error and we write no bytes, we leave the file + // unmodified + outfile = fopen(filename, "r+" FOPEN_EXTRA_FLAGS); + } + + if (!outfile) { + fprintf(stderr, "\nERROR: Failed to open output file %s: ", + filename); + perror(0); + exit(-1); + } + } + else if (showResponsePropertiesG) { + fprintf(stderr, "\nERROR: getacl -s requires a filename parameter\n"); + usageExit(stderr); + } + else { + outfile = stdout; + } + + int aclGrantCount; + S3AclGrant aclGrants[S3_MAX_ACL_GRANT_COUNT]; + char ownerId[S3_MAX_GRANTEE_USER_ID_SIZE]; + char ownerDisplayName[S3_MAX_GRANTEE_DISPLAY_NAME_SIZE]; + + S3_init(); + + S3BucketContext bucketContext = + { + bucketName, + protocolG, + uriStyleG, + accessKeyIdG, + secretAccessKeyG + }; + + S3ResponseHandler responseHandler = + { + &responsePropertiesCallback, + &responseCompleteCallback + }; + + do { + S3_get_acl(&bucketContext, key, ownerId, ownerDisplayName, + &aclGrantCount, aclGrants, 0, &responseHandler, 0); + } while (S3_status_is_retryable(statusG) && should_retry()); + + if (statusG == S3StatusOK) { + fprintf(outfile, "OwnerID %s %s\n", ownerId, ownerDisplayName); + fprintf(outfile, "%-6s %-90s %-12s\n", " Type", + " User Identifier", + " Permission"); + fprintf(outfile, "------ " + "------------------------------------------------------------" + "------------------------------ ------------\n"); + int i; + for (i = 0; i < aclGrantCount; i++) { + S3AclGrant *grant = &(aclGrants[i]); + const char *type; + char composedId[S3_MAX_GRANTEE_USER_ID_SIZE + + S3_MAX_GRANTEE_DISPLAY_NAME_SIZE + 16]; + const char *id; + + switch (grant->granteeType) { + case S3GranteeTypeAmazonCustomerByEmail: + type = "Email"; + id = grant->grantee.amazonCustomerByEmail.emailAddress; + break; + case S3GranteeTypeCanonicalUser: + type = "UserID"; + snprintf(composedId, sizeof(composedId), + "%s (%s)", grant->grantee.canonicalUser.id, + grant->grantee.canonicalUser.displayName); + id = composedId; + break; + case S3GranteeTypeAllAwsUsers: + type = "Group"; + id = "Authenticated AWS Users"; + break; + case S3GranteeTypeAllUsers: + type = "Group"; + id = "All Users"; + break; + default: + type = "Group"; + id = "Log Delivery"; + break; + } + const char *perm; + switch (grant->permission) { + case S3PermissionRead: + perm = "READ"; + break; + case S3PermissionWrite: + perm = "WRITE"; + break; + case S3PermissionReadACP: + perm = "READ_ACP"; + break; + case S3PermissionWriteACP: + perm = "WRITE_ACP"; + break; + default: + perm = "FULL_CONTROL"; + break; + } + fprintf(outfile, "%-6s %-90s %-12s\n", type, id, perm); + } + } + else { + printError(); + } + + fclose(outfile); + + S3_deinitialize(); } @@ -2289,99 +2289,99 @@ void get_acl(int argc, char **argv, int optindex) void set_acl(int argc, char **argv, int optindex) { - if (optindex == argc) { - fprintf(stderr, "\nERROR: Missing parameter: bucket[/key]\n"); - usageExit(stderr); - } - - const char *bucketName = argv[optindex]; - const char *key = 0; - - // Split bucket/key - char *slash = argv[optindex++]; - while (*slash && (*slash != '/')) { - slash++; - } - if (*slash) { - *slash++ = 0; - key = slash; - } - else { - key = 0; - } - - const char *filename = 0; - - while (optindex < argc) { - char *param = argv[optindex++]; - if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) { - filename = &(param[FILENAME_PREFIX_LEN]); - } - else { - fprintf(stderr, "\nERROR: Unknown param: %s\n", param); - usageExit(stderr); - } - } - - FILE *infile; - - if (filename) { - if (!(infile = fopen(filename, "r" FOPEN_EXTRA_FLAGS))) { - fprintf(stderr, "\nERROR: Failed to open input file %s: ", - filename); - perror(0); - exit(-1); - } - } - else { - infile = stdin; - } - - // Read in the complete ACL - char aclBuf[65536]; - aclBuf[fread(aclBuf, 1, sizeof(aclBuf), infile)] = 0; - char ownerId[S3_MAX_GRANTEE_USER_ID_SIZE]; - char ownerDisplayName[S3_MAX_GRANTEE_DISPLAY_NAME_SIZE]; - - // Parse it - int aclGrantCount; - S3AclGrant aclGrants[S3_MAX_ACL_GRANT_COUNT]; - if (!convert_simple_acl(aclBuf, ownerId, ownerDisplayName, - &aclGrantCount, aclGrants)) { - fprintf(stderr, "\nERROR: Failed to parse ACLs\n"); - fclose(infile); - exit(-1); - } - - S3_init(); - - S3BucketContext bucketContext = - { - bucketName, - protocolG, - uriStyleG, - accessKeyIdG, - secretAccessKeyG - }; - - S3ResponseHandler responseHandler = - { - &responsePropertiesCallback, - &responseCompleteCallback - }; - - do { - S3_set_acl(&bucketContext, key, ownerId, ownerDisplayName, - aclGrantCount, aclGrants, 0, &responseHandler, 0); - } while (S3_status_is_retryable(statusG) && should_retry()); - - if (statusG != S3StatusOK) { - printError(); - } - - fclose(infile); - - S3_deinitialize(); + if (optindex == argc) { + fprintf(stderr, "\nERROR: Missing parameter: bucket[/key]\n"); + usageExit(stderr); + } + + const char *bucketName = argv[optindex]; + const char *key = 0; + + // Split bucket/key + char *slash = argv[optindex++]; + while (*slash && (*slash != '/')) { + slash++; + } + if (*slash) { + *slash++ = 0; + key = slash; + } + else { + key = 0; + } + + const char *filename = 0; + + while (optindex < argc) { + char *param = argv[optindex++]; + if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) { + filename = &(param[FILENAME_PREFIX_LEN]); + } + else { + fprintf(stderr, "\nERROR: Unknown param: %s\n", param); + usageExit(stderr); + } + } + + FILE *infile; + + if (filename) { + if (!(infile = fopen(filename, "r" FOPEN_EXTRA_FLAGS))) { + fprintf(stderr, "\nERROR: Failed to open input file %s: ", + filename); + perror(0); + exit(-1); + } + } + else { + infile = stdin; + } + + // Read in the complete ACL + char aclBuf[65536]; + aclBuf[fread(aclBuf, 1, sizeof(aclBuf), infile)] = 0; + char ownerId[S3_MAX_GRANTEE_USER_ID_SIZE]; + char ownerDisplayName[S3_MAX_GRANTEE_DISPLAY_NAME_SIZE]; + + // Parse it + int aclGrantCount; + S3AclGrant aclGrants[S3_MAX_ACL_GRANT_COUNT]; + if (!convert_simple_acl(aclBuf, ownerId, ownerDisplayName, + &aclGrantCount, aclGrants)) { + fprintf(stderr, "\nERROR: Failed to parse ACLs\n"); + fclose(infile); + exit(-1); + } + + S3_init(); + + S3BucketContext bucketContext = + { + bucketName, + protocolG, + uriStyleG, + accessKeyIdG, + secretAccessKeyG + }; + + S3ResponseHandler responseHandler = + { + &responsePropertiesCallback, + &responseCompleteCallback + }; + + do { + S3_set_acl(&bucketContext, key, ownerId, ownerDisplayName, + aclGrantCount, aclGrants, 0, &responseHandler, 0); + } while (S3_status_is_retryable(statusG) && should_retry()); + + if (statusG != S3StatusOK) { + printError(); + } + + fclose(infile); + + S3_deinitialize(); } @@ -2389,157 +2389,157 @@ void set_acl(int argc, char **argv, int optindex) void get_logging(int argc, char **argv, int optindex) { - if (optindex == argc) { - fprintf(stderr, "\nERROR: Missing parameter: bucket\n"); - usageExit(stderr); - } - - const char *bucketName = argv[optindex++]; - const char *filename = 0; - - while (optindex < argc) { - char *param = argv[optindex++]; - if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) { - filename = &(param[FILENAME_PREFIX_LEN]); - } - else { - fprintf(stderr, "\nERROR: Unknown param: %s\n", param); - usageExit(stderr); - } - } - - FILE *outfile = 0; - - if (filename) { - // Stat the file, and if it doesn't exist, open it in w mode - struct stat buf; - if (stat(filename, &buf) == -1) { - outfile = fopen(filename, "w" FOPEN_EXTRA_FLAGS); - } - else { - // Open in r+ so that we don't truncate the file, just in case - // there is an error and we write no bytes, we leave the file - // unmodified - outfile = fopen(filename, "r+" FOPEN_EXTRA_FLAGS); - } - - if (!outfile) { - fprintf(stderr, "\nERROR: Failed to open output file %s: ", - filename); - perror(0); - exit(-1); - } - } - else if (showResponsePropertiesG) { - fprintf(stderr, "\nERROR: getlogging -s requires a filename " - "parameter\n"); - usageExit(stderr); - } - else { - outfile = stdout; - } - - int aclGrantCount; - S3AclGrant aclGrants[S3_MAX_ACL_GRANT_COUNT]; - char targetBucket[S3_MAX_BUCKET_NAME_SIZE]; - char targetPrefix[S3_MAX_KEY_SIZE]; - - S3_init(); - - S3BucketContext bucketContext = - { - bucketName, - protocolG, - uriStyleG, - accessKeyIdG, - secretAccessKeyG - }; - - S3ResponseHandler responseHandler = - { - &responsePropertiesCallback, - &responseCompleteCallback - }; - - do { - S3_get_server_access_logging(&bucketContext, targetBucket, targetPrefix, - &aclGrantCount, aclGrants, 0, - &responseHandler, 0); - } while (S3_status_is_retryable(statusG) && should_retry()); - - if (statusG == S3StatusOK) { - if (targetBucket[0]) { - printf("Target Bucket: %s\n", targetBucket); - if (targetPrefix[0]) { - printf("Target Prefix: %s\n", targetPrefix); - } - fprintf(outfile, "%-6s %-90s %-12s\n", " Type", - " User Identifier", - " Permission"); - fprintf(outfile, "------ " - "---------------------------------------------------------" - "--------------------------------- ------------\n"); - int i; - for (i = 0; i < aclGrantCount; i++) { - S3AclGrant *grant = &(aclGrants[i]); - const char *type; - char composedId[S3_MAX_GRANTEE_USER_ID_SIZE + - S3_MAX_GRANTEE_DISPLAY_NAME_SIZE + 16]; - const char *id; - - switch (grant->granteeType) { - case S3GranteeTypeAmazonCustomerByEmail: - type = "Email"; - id = grant->grantee.amazonCustomerByEmail.emailAddress; - break; - case S3GranteeTypeCanonicalUser: - type = "UserID"; - snprintf(composedId, sizeof(composedId), - "%s (%s)", grant->grantee.canonicalUser.id, - grant->grantee.canonicalUser.displayName); - id = composedId; - break; - case S3GranteeTypeAllAwsUsers: - type = "Group"; - id = "Authenticated AWS Users"; - break; - default: - type = "Group"; - id = "All Users"; - break; - } - const char *perm; - switch (grant->permission) { - case S3PermissionRead: - perm = "READ"; - break; - case S3PermissionWrite: - perm = "WRITE"; - break; - case S3PermissionReadACP: - perm = "READ_ACP"; - break; - case S3PermissionWriteACP: - perm = "WRITE_ACP"; - break; - default: - perm = "FULL_CONTROL"; - break; - } - fprintf(outfile, "%-6s %-90s %-12s\n", type, id, perm); - } - } - else { - printf("Service logging is not enabled for this bucket.\n"); - } - } - else { - printError(); - } - - fclose(outfile); - - S3_deinitialize(); + if (optindex == argc) { + fprintf(stderr, "\nERROR: Missing parameter: bucket\n"); + usageExit(stderr); + } + + const char *bucketName = argv[optindex++]; + const char *filename = 0; + + while (optindex < argc) { + char *param = argv[optindex++]; + if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) { + filename = &(param[FILENAME_PREFIX_LEN]); + } + else { + fprintf(stderr, "\nERROR: Unknown param: %s\n", param); + usageExit(stderr); + } + } + + FILE *outfile = 0; + + if (filename) { + // Stat the file, and if it doesn't exist, open it in w mode + struct stat buf; + if (stat(filename, &buf) == -1) { + outfile = fopen(filename, "w" FOPEN_EXTRA_FLAGS); + } + else { + // Open in r+ so that we don't truncate the file, just in case + // there is an error and we write no bytes, we leave the file + // unmodified + outfile = fopen(filename, "r+" FOPEN_EXTRA_FLAGS); + } + + if (!outfile) { + fprintf(stderr, "\nERROR: Failed to open output file %s: ", + filename); + perror(0); + exit(-1); + } + } + else if (showResponsePropertiesG) { + fprintf(stderr, "\nERROR: getlogging -s requires a filename " + "parameter\n"); + usageExit(stderr); + } + else { + outfile = stdout; + } + + int aclGrantCount; + S3AclGrant aclGrants[S3_MAX_ACL_GRANT_COUNT]; + char targetBucket[S3_MAX_BUCKET_NAME_SIZE]; + char targetPrefix[S3_MAX_KEY_SIZE]; + + S3_init(); + + S3BucketContext bucketContext = + { + bucketName, + protocolG, + uriStyleG, + accessKeyIdG, + secretAccessKeyG + }; + + S3ResponseHandler responseHandler = + { + &responsePropertiesCallback, + &responseCompleteCallback + }; + + do { + S3_get_server_access_logging(&bucketContext, targetBucket, targetPrefix, + &aclGrantCount, aclGrants, 0, + &responseHandler, 0); + } while (S3_status_is_retryable(statusG) && should_retry()); + + if (statusG == S3StatusOK) { + if (targetBucket[0]) { + printf("Target Bucket: %s\n", targetBucket); + if (targetPrefix[0]) { + printf("Target Prefix: %s\n", targetPrefix); + } + fprintf(outfile, "%-6s %-90s %-12s\n", " Type", + " User Identifier", + " Permission"); + fprintf(outfile, "------ " + "---------------------------------------------------------" + "--------------------------------- ------------\n"); + int i; + for (i = 0; i < aclGrantCount; i++) { + S3AclGrant *grant = &(aclGrants[i]); + const char *type; + char composedId[S3_MAX_GRANTEE_USER_ID_SIZE + + S3_MAX_GRANTEE_DISPLAY_NAME_SIZE + 16]; + const char *id; + + switch (grant->granteeType) { + case S3GranteeTypeAmazonCustomerByEmail: + type = "Email"; + id = grant->grantee.amazonCustomerByEmail.emailAddress; + break; + case S3GranteeTypeCanonicalUser: + type = "UserID"; + snprintf(composedId, sizeof(composedId), + "%s (%s)", grant->grantee.canonicalUser.id, + grant->grantee.canonicalUser.displayName); + id = composedId; + break; + case S3GranteeTypeAllAwsUsers: + type = "Group"; + id = "Authenticated AWS Users"; + break; + default: + type = "Group"; + id = "All Users"; + break; + } + const char *perm; + switch (grant->permission) { + case S3PermissionRead: + perm = "READ"; + break; + case S3PermissionWrite: + perm = "WRITE"; + break; + case S3PermissionReadACP: + perm = "READ_ACP"; + break; + case S3PermissionWriteACP: + perm = "WRITE_ACP"; + break; + default: + perm = "FULL_CONTROL"; + break; + } + fprintf(outfile, "%-6s %-90s %-12s\n", type, id, perm); + } + } + else { + printf("Service logging is not enabled for this bucket.\n"); + } + } + else { + printError(); + } + + fclose(outfile); + + S3_deinitialize(); } @@ -2547,96 +2547,96 @@ void get_logging(int argc, char **argv, int optindex) void set_logging(int argc, char **argv, int optindex) { - if (optindex == argc) { - fprintf(stderr, "\nERROR: Missing parameter: bucket\n"); - usageExit(stderr); - } - - const char *bucketName = argv[optindex++]; - - const char *targetBucket = 0, *targetPrefix = 0, *filename = 0; - - while (optindex < argc) { - char *param = argv[optindex++]; - if (!strncmp(param, TARGET_BUCKET_PREFIX, TARGET_BUCKET_PREFIX_LEN)) { - targetBucket = &(param[TARGET_BUCKET_PREFIX_LEN]); - } - else if (!strncmp(param, TARGET_PREFIX_PREFIX, - TARGET_PREFIX_PREFIX_LEN)) { - targetPrefix = &(param[TARGET_PREFIX_PREFIX_LEN]); - } - else if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) { - filename = &(param[FILENAME_PREFIX_LEN]); - } - else { - fprintf(stderr, "\nERROR: Unknown param: %s\n", param); - usageExit(stderr); - } - } - - int aclGrantCount = 0; - S3AclGrant aclGrants[S3_MAX_ACL_GRANT_COUNT]; - - if (targetBucket) { - FILE *infile; - - if (filename) { - if (!(infile = fopen(filename, "r" FOPEN_EXTRA_FLAGS))) { - fprintf(stderr, "\nERROR: Failed to open input file %s: ", - filename); - perror(0); - exit(-1); - } - } - else { - infile = stdin; - } - - // Read in the complete ACL - char aclBuf[65536]; - aclBuf[fread(aclBuf, 1, sizeof(aclBuf), infile)] = 0; - char ownerId[S3_MAX_GRANTEE_USER_ID_SIZE]; - char ownerDisplayName[S3_MAX_GRANTEE_DISPLAY_NAME_SIZE]; - - // Parse it - if (!convert_simple_acl(aclBuf, ownerId, ownerDisplayName, - &aclGrantCount, aclGrants)) { - fprintf(stderr, "\nERROR: Failed to parse ACLs\n"); - fclose(infile); - exit(-1); - } - - fclose(infile); - } - - S3_init(); - - S3BucketContext bucketContext = - { - bucketName, - protocolG, - uriStyleG, - accessKeyIdG, - secretAccessKeyG - }; - - S3ResponseHandler responseHandler = - { - &responsePropertiesCallback, - &responseCompleteCallback - }; - - do { - S3_set_server_access_logging(&bucketContext, targetBucket, - targetPrefix, aclGrantCount, aclGrants, - 0, &responseHandler, 0); - } while (S3_status_is_retryable(statusG) && should_retry()); - - if (statusG != S3StatusOK) { - printError(); - } - - S3_deinitialize(); + if (optindex == argc) { + fprintf(stderr, "\nERROR: Missing parameter: bucket\n"); + usageExit(stderr); + } + + const char *bucketName = argv[optindex++]; + + const char *targetBucket = 0, *targetPrefix = 0, *filename = 0; + + while (optindex < argc) { + char *param = argv[optindex++]; + if (!strncmp(param, TARGET_BUCKET_PREFIX, TARGET_BUCKET_PREFIX_LEN)) { + targetBucket = &(param[TARGET_BUCKET_PREFIX_LEN]); + } + else if (!strncmp(param, TARGET_PREFIX_PREFIX, + TARGET_PREFIX_PREFIX_LEN)) { + targetPrefix = &(param[TARGET_PREFIX_PREFIX_LEN]); + } + else if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) { + filename = &(param[FILENAME_PREFIX_LEN]); + } + else { + fprintf(stderr, "\nERROR: Unknown param: %s\n", param); + usageExit(stderr); + } + } + + int aclGrantCount = 0; + S3AclGrant aclGrants[S3_MAX_ACL_GRANT_COUNT]; + + if (targetBucket) { + FILE *infile; + + if (filename) { + if (!(infile = fopen(filename, "r" FOPEN_EXTRA_FLAGS))) { + fprintf(stderr, "\nERROR: Failed to open input file %s: ", + filename); + perror(0); + exit(-1); + } + } + else { + infile = stdin; + } + + // Read in the complete ACL + char aclBuf[65536]; + aclBuf[fread(aclBuf, 1, sizeof(aclBuf), infile)] = 0; + char ownerId[S3_MAX_GRANTEE_USER_ID_SIZE]; + char ownerDisplayName[S3_MAX_GRANTEE_DISPLAY_NAME_SIZE]; + + // Parse it + if (!convert_simple_acl(aclBuf, ownerId, ownerDisplayName, + &aclGrantCount, aclGrants)) { + fprintf(stderr, "\nERROR: Failed to parse ACLs\n"); + fclose(infile); + exit(-1); + } + + fclose(infile); + } + + S3_init(); + + S3BucketContext bucketContext = + { + bucketName, + protocolG, + uriStyleG, + accessKeyIdG, + secretAccessKeyG + }; + + S3ResponseHandler responseHandler = + { + &responsePropertiesCallback, + &responseCompleteCallback + }; + + do { + S3_set_server_access_logging(&bucketContext, targetBucket, + targetPrefix, aclGrantCount, aclGrants, + 0, &responseHandler, 0); + } while (S3_status_is_retryable(statusG) && should_retry()); + + if (statusG != S3StatusOK) { + printError(); + } + + S3_deinitialize(); } @@ -2644,132 +2644,132 @@ void set_logging(int argc, char **argv, int optindex) int main(int argc, char **argv) { - // Parse args - while (1) { - int idx = 0; - int c = getopt_long(argc, argv, "fhusr:", longOptionsG, &idx); - - if (c == -1) { - // End of options - break; - } - - switch (c) { - case 'f': - forceG = 1; - break; - case 'h': - uriStyleG = S3UriStyleVirtualHost; - break; - case 'u': - protocolG = S3ProtocolHTTP; - break; - case 's': - showResponsePropertiesG = 1; - break; - case 'r': { - const char *v = optarg; - while (*v) { - retriesG *= 10; - retriesG += *v - '0'; - v++; - } - break; - } - default: - fprintf(stderr, "\nERROR: Unknown option: -%c\n", c); - // Usage exit - usageExit(stderr); - } - } - - // The first non-option argument gives the operation to perform - if (optind == argc) { - fprintf(stderr, "\n\nERROR: Missing argument: command\n\n"); - usageExit(stderr); - } - - const char *command = argv[optind++]; - - if (!strcmp(command, "help")) { - fprintf(stdout, "\ns3 is a program for performing single requests " - "to Amazon S3.\n"); - usageExit(stdout); - } - - accessKeyIdG = getenv("S3_ACCESS_KEY_ID"); - if (!accessKeyIdG) { - fprintf(stderr, "Missing environment variable: S3_ACCESS_KEY_ID\n"); - return -1; - } - secretAccessKeyG = getenv("S3_SECRET_ACCESS_KEY"); - if (!secretAccessKeyG) { - fprintf(stderr, - "Missing environment variable: S3_SECRET_ACCESS_KEY\n"); - return -1; - } - - if (!strcmp(command, "list")) { - list(argc, argv, optind); - } - else if (!strcmp(command, "test")) { - test_bucket(argc, argv, optind); - } - else if (!strcmp(command, "create")) { - create_bucket(argc, argv, optind); - } - else if (!strcmp(command, "delete")) { - if (optind == argc) { - fprintf(stderr, - "\nERROR: Missing parameter: bucket or bucket/key\n"); - usageExit(stderr); - } - char *val = argv[optind]; - int hasSlash = 0; - while (*val) { - if (*val++ == '/') { - hasSlash = 1; - break; - } - } - if (hasSlash) { - delete_object(argc, argv, optind); - } - else { - delete_bucket(argc, argv, optind); - } - } - else if (!strcmp(command, "put")) { - put_object(argc, argv, optind); - } - else if (!strcmp(command, "copy")) { - copy_object(argc, argv, optind); - } - else if (!strcmp(command, "get")) { - get_object(argc, argv, optind); - } - else if (!strcmp(command, "head")) { - head_object(argc, argv, optind); - } - else if (!strcmp(command, "gqs")) { - generate_query_string(argc, argv, optind); - } - else if (!strcmp(command, "getacl")) { - get_acl(argc, argv, optind); - } - else if (!strcmp(command, "setacl")) { - set_acl(argc, argv, optind); - } - else if (!strcmp(command, "getlogging")) { - get_logging(argc, argv, optind); - } - else if (!strcmp(command, "setlogging")) { - set_logging(argc, argv, optind); - } - else { - fprintf(stderr, "Unknown command: %s\n", command); - return -1; - } - - return 0; + // Parse args + while (1) { + int idx = 0; + int c = getopt_long(argc, argv, "fhusr:", longOptionsG, &idx); + + if (c == -1) { + // End of options + break; + } + + switch (c) { + case 'f': + forceG = 1; + break; + case 'h': + uriStyleG = S3UriStyleVirtualHost; + break; + case 'u': + protocolG = S3ProtocolHTTP; + break; + case 's': + showResponsePropertiesG = 1; + break; + case 'r': { + const char *v = optarg; + while (*v) { + retriesG *= 10; + retriesG += *v - '0'; + v++; + } + break; + } + default: + fprintf(stderr, "\nERROR: Unknown option: -%c\n", c); + // Usage exit + usageExit(stderr); + } + } + + // The first non-option argument gives the operation to perform + if (optind == argc) { + fprintf(stderr, "\n\nERROR: Missing argument: command\n\n"); + usageExit(stderr); + } + + const char *command = argv[optind++]; + + if (!strcmp(command, "help")) { + fprintf(stdout, "\ns3 is a program for performing single requests " + "to Amazon S3.\n"); + usageExit(stdout); + } + + accessKeyIdG = getenv("S3_ACCESS_KEY_ID"); + if (!accessKeyIdG) { + fprintf(stderr, "Missing environment variable: S3_ACCESS_KEY_ID\n"); + return -1; + } + secretAccessKeyG = getenv("S3_SECRET_ACCESS_KEY"); + if (!secretAccessKeyG) { + fprintf(stderr, + "Missing environment variable: S3_SECRET_ACCESS_KEY\n"); + return -1; + } + + if (!strcmp(command, "list")) { + list(argc, argv, optind); + } + else if (!strcmp(command, "test")) { + test_bucket(argc, argv, optind); + } + else if (!strcmp(command, "create")) { + create_bucket(argc, argv, optind); + } + else if (!strcmp(command, "delete")) { + if (optind == argc) { + fprintf(stderr, + "\nERROR: Missing parameter: bucket or bucket/key\n"); + usageExit(stderr); + } + char *val = argv[optind]; + int hasSlash = 0; + while (*val) { + if (*val++ == '/') { + hasSlash = 1; + break; + } + } + if (hasSlash) { + delete_object(argc, argv, optind); + } + else { + delete_bucket(argc, argv, optind); + } + } + else if (!strcmp(command, "put")) { + put_object(argc, argv, optind); + } + else if (!strcmp(command, "copy")) { + copy_object(argc, argv, optind); + } + else if (!strcmp(command, "get")) { + get_object(argc, argv, optind); + } + else if (!strcmp(command, "head")) { + head_object(argc, argv, optind); + } + else if (!strcmp(command, "gqs")) { + generate_query_string(argc, argv, optind); + } + else if (!strcmp(command, "getacl")) { + get_acl(argc, argv, optind); + } + else if (!strcmp(command, "setacl")) { + set_acl(argc, argv, optind); + } + else if (!strcmp(command, "getlogging")) { + get_logging(argc, argv, optind); + } + else if (!strcmp(command, "setlogging")) { + set_logging(argc, argv, optind); + } + else { + fprintf(stderr, "Unknown command: %s\n", command); + return -1; + } + + return 0; } diff --git a/src/service.c b/src/service.c index 216b981..dbef8ed 100644 --- a/src/service.c +++ b/src/service.c @@ -33,155 +33,155 @@ typedef struct XmlCallbackData { - SimpleXml simpleXml; - - S3ResponsePropertiesCallback *responsePropertiesCallback; - S3ListServiceCallback *listServiceCallback; - S3ResponseCompleteCallback *responseCompleteCallback; - void *callbackData; - - string_buffer(ownerId, 256); - string_buffer(ownerDisplayName, 256); - string_buffer(bucketName, 256); - string_buffer(creationDate, 128); + SimpleXml simpleXml; + + S3ResponsePropertiesCallback *responsePropertiesCallback; + S3ListServiceCallback *listServiceCallback; + S3ResponseCompleteCallback *responseCompleteCallback; + void *callbackData; + + string_buffer(ownerId, 256); + string_buffer(ownerDisplayName, 256); + string_buffer(bucketName, 256); + string_buffer(creationDate, 128); } XmlCallbackData; static S3Status xmlCallback(const char *elementPath, const char *data, - int dataLen, void *callbackData) + int dataLen, void *callbackData) { - XmlCallbackData *cbData = (XmlCallbackData *) callbackData; - - int fit; - - if (data) { - if (!strcmp(elementPath, "ListAllMyBucketsResult/Owner/ID")) { - string_buffer_append(cbData->ownerId, data, dataLen, fit); - } - else if (!strcmp(elementPath, - "ListAllMyBucketsResult/Owner/DisplayName")) { - string_buffer_append(cbData->ownerDisplayName, data, dataLen, fit); - } - else if (!strcmp(elementPath, - "ListAllMyBucketsResult/Buckets/Bucket/Name")) { - string_buffer_append(cbData->bucketName, data, dataLen, fit); - } - else if (!strcmp - (elementPath, - "ListAllMyBucketsResult/Buckets/Bucket/CreationDate")) { - string_buffer_append(cbData->creationDate, data, dataLen, fit); - } - } - else { - if (!strcmp(elementPath, "ListAllMyBucketsResult/Buckets/Bucket")) { - // Parse date. Assume ISO-8601 date format. - time_t creationDate = parseIso8601Time(cbData->creationDate); - - // Make the callback - a bucket just finished - S3Status status = (*(cbData->listServiceCallback)) - (cbData->ownerId, cbData->ownerDisplayName, - cbData->bucketName, creationDate, cbData->callbackData); - - string_buffer_initialize(cbData->bucketName); - string_buffer_initialize(cbData->creationDate); - - return status; - } - } - - return S3StatusOK; + XmlCallbackData *cbData = (XmlCallbackData *) callbackData; + + int fit; + + if (data) { + if (!strcmp(elementPath, "ListAllMyBucketsResult/Owner/ID")) { + string_buffer_append(cbData->ownerId, data, dataLen, fit); + } + else if (!strcmp(elementPath, + "ListAllMyBucketsResult/Owner/DisplayName")) { + string_buffer_append(cbData->ownerDisplayName, data, dataLen, fit); + } + else if (!strcmp(elementPath, + "ListAllMyBucketsResult/Buckets/Bucket/Name")) { + string_buffer_append(cbData->bucketName, data, dataLen, fit); + } + else if (!strcmp + (elementPath, + "ListAllMyBucketsResult/Buckets/Bucket/CreationDate")) { + string_buffer_append(cbData->creationDate, data, dataLen, fit); + } + } + else { + if (!strcmp(elementPath, "ListAllMyBucketsResult/Buckets/Bucket")) { + // Parse date. Assume ISO-8601 date format. + time_t creationDate = parseIso8601Time(cbData->creationDate); + + // Make the callback - a bucket just finished + S3Status status = (*(cbData->listServiceCallback)) + (cbData->ownerId, cbData->ownerDisplayName, + cbData->bucketName, creationDate, cbData->callbackData); + + string_buffer_initialize(cbData->bucketName); + string_buffer_initialize(cbData->creationDate); + + return status; + } + } + + return S3StatusOK; } static S3Status propertiesCallback - (const S3ResponseProperties *responseProperties, void *callbackData) + (const S3ResponseProperties *responseProperties, void *callbackData) { - XmlCallbackData *cbData = (XmlCallbackData *) callbackData; - - return (*(cbData->responsePropertiesCallback)) - (responseProperties, cbData->callbackData); + XmlCallbackData *cbData = (XmlCallbackData *) callbackData; + + return (*(cbData->responsePropertiesCallback)) + (responseProperties, cbData->callbackData); } static S3Status dataCallback(int bufferSize, const char *buffer, - void *callbackData) + void *callbackData) { - XmlCallbackData *cbData = (XmlCallbackData *) callbackData; + XmlCallbackData *cbData = (XmlCallbackData *) callbackData; - return simplexml_add(&(cbData->simpleXml), buffer, bufferSize); + return simplexml_add(&(cbData->simpleXml), buffer, bufferSize); } static void completeCallback(S3Status requestStatus, - const S3ErrorDetails *s3ErrorDetails, - void *callbackData) + const S3ErrorDetails *s3ErrorDetails, + void *callbackData) { - XmlCallbackData *cbData = (XmlCallbackData *) callbackData; + XmlCallbackData *cbData = (XmlCallbackData *) callbackData; - (*(cbData->responseCompleteCallback)) - (requestStatus, s3ErrorDetails, cbData->callbackData); + (*(cbData->responseCompleteCallback)) + (requestStatus, s3ErrorDetails, cbData->callbackData); - simplexml_deinitialize(&(cbData->simpleXml)); + simplexml_deinitialize(&(cbData->simpleXml)); - free(cbData); + free(cbData); } void S3_list_service(S3Protocol protocol, const char *accessKeyId, - const char *secretAccessKey, - S3RequestContext *requestContext, - const S3ListServiceHandler *handler, void *callbackData) + const char *secretAccessKey, + S3RequestContext *requestContext, + const S3ListServiceHandler *handler, void *callbackData) { - // Create and set up the callback data - XmlCallbackData *data = - (XmlCallbackData *) malloc(sizeof(XmlCallbackData)); - if (!data) { - (*(handler->responseHandler.completeCallback)) - (S3StatusOutOfMemory, 0, callbackData); - return; - } - - simplexml_initialize(&(data->simpleXml), &xmlCallback, data); - - data->responsePropertiesCallback = - handler->responseHandler.propertiesCallback; - data->listServiceCallback = handler->listServiceCallback; - data->responseCompleteCallback = handler->responseHandler.completeCallback; - data->callbackData = callbackData; - - string_buffer_initialize(data->ownerId); - string_buffer_initialize(data->ownerDisplayName); - string_buffer_initialize(data->bucketName); - string_buffer_initialize(data->creationDate); - - // Set up the RequestParams - RequestParams params = - { - HttpRequestTypeGET, // httpRequestType - { 0, // 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 - 0, // requestProperties - &propertiesCallback, // propertiesCallback - 0, // toS3Callback - 0, // toS3CallbackTotalSize - &dataCallback, // fromS3Callback - &completeCallback, // completeCallback - data // callbackData - }; - - // Perform the request - request_perform(¶ms, requestContext); + // Create and set up the callback data + XmlCallbackData *data = + (XmlCallbackData *) malloc(sizeof(XmlCallbackData)); + if (!data) { + (*(handler->responseHandler.completeCallback)) + (S3StatusOutOfMemory, 0, callbackData); + return; + } + + simplexml_initialize(&(data->simpleXml), &xmlCallback, data); + + data->responsePropertiesCallback = + handler->responseHandler.propertiesCallback; + data->listServiceCallback = handler->listServiceCallback; + data->responseCompleteCallback = handler->responseHandler.completeCallback; + data->callbackData = callbackData; + + string_buffer_initialize(data->ownerId); + string_buffer_initialize(data->ownerDisplayName); + string_buffer_initialize(data->bucketName); + string_buffer_initialize(data->creationDate); + + // Set up the RequestParams + RequestParams params = + { + HttpRequestTypeGET, // httpRequestType + { 0, // 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 + 0, // requestProperties + &propertiesCallback, // propertiesCallback + 0, // toS3Callback + 0, // toS3CallbackTotalSize + &dataCallback, // fromS3Callback + &completeCallback, // completeCallback + data // callbackData + }; + + // Perform the request + request_perform(¶ms, requestContext); } diff --git a/src/service_access_logging.c b/src/service_access_logging.c index cbed2c1..fcbce46 100644 --- a/src/service_access_logging.c +++ b/src/service_access_logging.c @@ -34,199 +34,199 @@ 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); + 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) + 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; + 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) + 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; + 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; } @@ -236,119 +236,119 @@ static S3Status convert_bls(char *blsXml, char *targetBucketReturn, typedef struct GetBlsData { - SimpleXml simpleXml; + SimpleXml simpleXml; - S3ResponsePropertiesCallback *responsePropertiesCallback; - S3ResponseCompleteCallback *responseCompleteCallback; - void *callbackData; + S3ResponsePropertiesCallback *responsePropertiesCallback; + S3ResponseCompleteCallback *responseCompleteCallback; + void *callbackData; - char *targetBucketReturn; - char *targetPrefixReturn; - int *aclGrantCountReturn; - S3AclGrant *aclGrants; - string_buffer(blsXmlDocument, BLS_XML_DOC_MAXSIZE); + char *targetBucketReturn; + char *targetPrefixReturn; + int *aclGrantCountReturn; + S3AclGrant *aclGrants; + string_buffer(blsXmlDocument, BLS_XML_DOC_MAXSIZE); } GetBlsData; static S3Status getBlsPropertiesCallback - (const S3ResponseProperties *responseProperties, void *callbackData) + (const S3ResponseProperties *responseProperties, void *callbackData) { - GetBlsData *gsData = (GetBlsData *) callbackData; - - return (*(gsData->responsePropertiesCallback)) - (responseProperties, gsData->callbackData); + GetBlsData *gsData = (GetBlsData *) callbackData; + + return (*(gsData->responsePropertiesCallback)) + (responseProperties, gsData->callbackData); } static S3Status getBlsDataCallback(int bufferSize, const char *buffer, - void *callbackData) + void *callbackData) { - GetBlsData *gsData = (GetBlsData *) callbackData; + GetBlsData *gsData = (GetBlsData *) callbackData; - int fit; + int fit; - string_buffer_append(gsData->blsXmlDocument, buffer, bufferSize, fit); - - return fit ? S3StatusOK : S3StatusXmlDocumentTooLarge; + string_buffer_append(gsData->blsXmlDocument, buffer, bufferSize, fit); + + return fit ? S3StatusOK : S3StatusXmlDocumentTooLarge; } static void getBlsCompleteCallback(S3Status requestStatus, - const S3ErrorDetails *s3ErrorDetails, - void *callbackData) + const S3ErrorDetails *s3ErrorDetails, + void *callbackData) { - GetBlsData *gsData = (GetBlsData *) callbackData; + GetBlsData *gsData = (GetBlsData *) callbackData; - if (requestStatus == S3StatusOK) { - // Parse the document - requestStatus = convert_bls - (gsData->blsXmlDocument, gsData->targetBucketReturn, - gsData->targetPrefixReturn, gsData->aclGrantCountReturn, - gsData->aclGrants); - } + 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); + (*(gsData->responseCompleteCallback)) + (requestStatus, s3ErrorDetails, gsData->callbackData); - free(gsData); + 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) + 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(¶ms, requestContext); + // 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(¶ms, requestContext); } @@ -356,201 +356,201 @@ void S3_get_server_access_logging(const S3BucketContext *bucketContext, // set server access logging--------------------------------------------------- static S3Status generateSalXmlDocument(const char *targetBucket, - const char *targetPrefix, - int aclGrantCount, - const S3AclGrant *aclGrants, - int *xmlDocumentLenReturn, - char *xmlDocument, - int xmlDocumentBufferSize) + 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; + *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; + S3ResponsePropertiesCallback *responsePropertiesCallback; + S3ResponseCompleteCallback *responseCompleteCallback; + void *callbackData; - int salXmlDocumentLen; - char salXmlDocument[BLS_XML_DOC_MAXSIZE]; - int salXmlDocumentBytesWritten; + int salXmlDocumentLen; + char salXmlDocument[BLS_XML_DOC_MAXSIZE]; + int salXmlDocumentBytesWritten; } SetSalData; static S3Status setSalPropertiesCallback - (const S3ResponseProperties *responseProperties, void *callbackData) + (const S3ResponseProperties *responseProperties, void *callbackData) { - SetSalData *paData = (SetSalData *) callbackData; - - return (*(paData->responsePropertiesCallback)) - (responseProperties, paData->callbackData); + SetSalData *paData = (SetSalData *) callbackData; + + return (*(paData->responsePropertiesCallback)) + (responseProperties, paData->callbackData); } static int setSalDataCallback(int bufferSize, char *buffer, void *callbackData) { - SetSalData *paData = (SetSalData *) callbackData; + SetSalData *paData = (SetSalData *) callbackData; - int remaining = (paData->salXmlDocumentLen - - paData->salXmlDocumentBytesWritten); + int remaining = (paData->salXmlDocumentLen - + paData->salXmlDocumentBytesWritten); - int toCopy = bufferSize > remaining ? remaining : bufferSize; - - if (!toCopy) { - return 0; - } + int toCopy = bufferSize > remaining ? remaining : bufferSize; + + if (!toCopy) { + return 0; + } - memcpy(buffer, &(paData->salXmlDocument - [paData->salXmlDocumentBytesWritten]), toCopy); + memcpy(buffer, &(paData->salXmlDocument + [paData->salXmlDocumentBytesWritten]), toCopy); - paData->salXmlDocumentBytesWritten += toCopy; + paData->salXmlDocumentBytesWritten += toCopy; - return toCopy; + return toCopy; } static void setSalCompleteCallback(S3Status requestStatus, - const S3ErrorDetails *s3ErrorDetails, - void *callbackData) + const S3ErrorDetails *s3ErrorDetails, + void *callbackData) { - SetSalData *paData = (SetSalData *) callbackData; + SetSalData *paData = (SetSalData *) callbackData; - (*(paData->responseCompleteCallback)) - (requestStatus, s3ErrorDetails, paData->callbackData); + (*(paData->responseCompleteCallback)) + (requestStatus, s3ErrorDetails, paData->callbackData); - free(paData); + 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) + 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(¶ms, requestContext); + 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(¶ms, requestContext); } diff --git a/src/simplexml.c b/src/simplexml.c index bd8616b..4411824 100644 --- a/src/simplexml.c +++ b/src/simplexml.c @@ -28,12 +28,12 @@ #include <string.h> #include "simplexml.h" -// Use libxml2 for parsing XML. XML is severely overused in modern +// Use libxml2 for parsing XML. XML is severely overused in modern // computing. It is useful for only a very small subset of tasks, but // software developers who don't know better and are afraid to go against the // grain use it for everything, and in most cases, it is completely // inappropriate. Usually, the document structure is severely under-specified -// as well, as is the case with S3. We do our best by just caring about the +// as well, as is the case with S3. We do our best by just caring about the // most important aspects of the S3 "XML document" responses: the elements and // their values. The SAX API (just about the lamest API ever devised and // proof that XML sucks - well, the real proof is how crappy all of the XML @@ -47,161 +47,161 @@ static xmlEntityPtr saxGetEntity(void *user_data, const xmlChar *name) { - (void) user_data; + (void) user_data; - return xmlGetPredefinedEntity(name); + return xmlGetPredefinedEntity(name); } static void saxStartElement(void *user_data, const xmlChar *nameUtf8, - const xmlChar **attr) + const xmlChar **attr) { - (void) attr; - - SimpleXml *simpleXml = (SimpleXml *) user_data; - - if (simpleXml->status != S3StatusOK) { - return; - } - - // Assume that name has no non-ASCII in it - char *name = (char *) nameUtf8; - - // Append the element to the element path - int len = strlen(name); - - if ((simpleXml->elementPathLen + len + 1) >= - (int) sizeof(simpleXml->elementPath)) { - // Cannot handle this element, stop! - simpleXml->status = S3StatusXmlParseFailure; - return; - } - - if (simpleXml->elementPathLen) { - simpleXml->elementPath[simpleXml->elementPathLen++] = '/'; - } - strcpy(&(simpleXml->elementPath[simpleXml->elementPathLen]), name); - simpleXml->elementPathLen += len; + (void) attr; + + SimpleXml *simpleXml = (SimpleXml *) user_data; + + if (simpleXml->status != S3StatusOK) { + return; + } + + // Assume that name has no non-ASCII in it + char *name = (char *) nameUtf8; + + // Append the element to the element path + int len = strlen(name); + + if ((simpleXml->elementPathLen + len + 1) >= + (int) sizeof(simpleXml->elementPath)) { + // Cannot handle this element, stop! + simpleXml->status = S3StatusXmlParseFailure; + return; + } + + if (simpleXml->elementPathLen) { + simpleXml->elementPath[simpleXml->elementPathLen++] = '/'; + } + strcpy(&(simpleXml->elementPath[simpleXml->elementPathLen]), name); + simpleXml->elementPathLen += len; } static void saxEndElement(void *user_data, const xmlChar *name) { - (void) name; + (void) name; - SimpleXml *simpleXml = (SimpleXml *) user_data; + SimpleXml *simpleXml = (SimpleXml *) user_data; - if (simpleXml->status != S3StatusOK) { - return; - } + if (simpleXml->status != S3StatusOK) { + return; + } - // Call back with 0 data - simpleXml->status = (*(simpleXml->callback)) - (simpleXml->elementPath, 0, 0, simpleXml->callbackData); + // Call back with 0 data + simpleXml->status = (*(simpleXml->callback)) + (simpleXml->elementPath, 0, 0, simpleXml->callbackData); - while ((simpleXml->elementPathLen > 0) && - (simpleXml->elementPath[simpleXml->elementPathLen] != '/')) { - simpleXml->elementPathLen--; - } + while ((simpleXml->elementPathLen > 0) && + (simpleXml->elementPath[simpleXml->elementPathLen] != '/')) { + simpleXml->elementPathLen--; + } - simpleXml->elementPath[simpleXml->elementPathLen] = 0; + simpleXml->elementPath[simpleXml->elementPathLen] = 0; } static void saxCharacters(void *user_data, const xmlChar *ch, int len) { - SimpleXml *simpleXml = (SimpleXml *) user_data; + SimpleXml *simpleXml = (SimpleXml *) user_data; - if (simpleXml->status != S3StatusOK) { - return; - } + if (simpleXml->status != S3StatusOK) { + return; + } - simpleXml->status = (*(simpleXml->callback)) - (simpleXml->elementPath, (char *) ch, len, simpleXml->callbackData); + simpleXml->status = (*(simpleXml->callback)) + (simpleXml->elementPath, (char *) ch, len, simpleXml->callbackData); } static void saxError(void *user_data, const char *msg, ...) { - (void) msg; + (void) msg; - SimpleXml *simpleXml = (SimpleXml *) user_data; + SimpleXml *simpleXml = (SimpleXml *) user_data; - if (simpleXml->status != S3StatusOK) { - return; - } + if (simpleXml->status != S3StatusOK) { + return; + } - simpleXml->status = S3StatusXmlParseFailure; + simpleXml->status = S3StatusXmlParseFailure; } static struct _xmlSAXHandler saxHandlerG = { - 0, // internalSubsetSAXFunc - 0, // isStandaloneSAXFunc - 0, // hasInternalSubsetSAXFunc - 0, // hasExternalSubsetSAXFunc - 0, // resolveEntitySAXFunc - &saxGetEntity, // getEntitySAXFunc - 0, // entityDeclSAXFunc - 0, // notationDeclSAXFunc - 0, // attributeDeclSAXFunc - 0, // elementDeclSAXFunc - 0, // unparsedEntityDeclSAXFunc - 0, // setDocumentLocatorSAXFunc - 0, // startDocumentSAXFunc - 0, // endDocumentSAXFunc - &saxStartElement, // startElementSAXFunc - &saxEndElement, // endElementSAXFunc - 0, // referenceSAXFunc - &saxCharacters, // charactersSAXFunc - 0, // ignorableWhitespaceSAXFunc - 0, // processingInstructionSAXFunc - 0, // commentSAXFunc - 0, // warningSAXFunc - &saxError, // errorSAXFunc - &saxError, // fatalErrorSAXFunc - 0, // getParameterEntitySAXFunc - &saxCharacters, // cdataBlockSAXFunc - 0, // externalSubsetSAXFunc - 0, // initialized - 0, // _private - 0, // startElementNsSAX2Func - 0, // endElementNsSAX2Func - 0 // xmlStructuredErrorFunc serror; + 0, // internalSubsetSAXFunc + 0, // isStandaloneSAXFunc + 0, // hasInternalSubsetSAXFunc + 0, // hasExternalSubsetSAXFunc + 0, // resolveEntitySAXFunc + &saxGetEntity, // getEntitySAXFunc + 0, // entityDeclSAXFunc + 0, // notationDeclSAXFunc + 0, // attributeDeclSAXFunc + 0, // elementDeclSAXFunc + 0, // unparsedEntityDeclSAXFunc + 0, // setDocumentLocatorSAXFunc + 0, // startDocumentSAXFunc + 0, // endDocumentSAXFunc + &saxStartElement, // startElementSAXFunc + &saxEndElement, // endElementSAXFunc + 0, // referenceSAXFunc + &saxCharacters, // charactersSAXFunc + 0, // ignorableWhitespaceSAXFunc + 0, // processingInstructionSAXFunc + 0, // commentSAXFunc + 0, // warningSAXFunc + &saxError, // errorSAXFunc + &saxError, // fatalErrorSAXFunc + 0, // getParameterEntitySAXFunc + &saxCharacters, // cdataBlockSAXFunc + 0, // externalSubsetSAXFunc + 0, // initialized + 0, // _private + 0, // startElementNsSAX2Func + 0, // endElementNsSAX2Func + 0 // xmlStructuredErrorFunc serror; }; void simplexml_initialize(SimpleXml *simpleXml, - SimpleXmlCallback *callback, void *callbackData) + SimpleXmlCallback *callback, void *callbackData) { - simpleXml->callback = callback; - simpleXml->callbackData = callbackData; - simpleXml->elementPathLen = 0; - simpleXml->status = S3StatusOK; - simpleXml->xmlParser = 0; + simpleXml->callback = callback; + simpleXml->callbackData = callbackData; + simpleXml->elementPathLen = 0; + simpleXml->status = S3StatusOK; + simpleXml->xmlParser = 0; } void simplexml_deinitialize(SimpleXml *simpleXml) { - if (simpleXml->xmlParser) { - xmlFreeParserCtxt(simpleXml->xmlParser); - } + if (simpleXml->xmlParser) { + xmlFreeParserCtxt(simpleXml->xmlParser); + } } S3Status simplexml_add(SimpleXml *simpleXml, const char *data, int dataLen) { - if (!simpleXml->xmlParser && - (!(simpleXml->xmlParser = xmlCreatePushParserCtxt - (&saxHandlerG, simpleXml, 0, 0, 0)))) { - return S3StatusInternalError; - } - - if (xmlParseChunk((xmlParserCtxtPtr) simpleXml->xmlParser, - data, dataLen, 0)) { - return S3StatusXmlParseFailure; - } - - return simpleXml->status; + if (!simpleXml->xmlParser && + (!(simpleXml->xmlParser = xmlCreatePushParserCtxt + (&saxHandlerG, simpleXml, 0, 0, 0)))) { + return S3StatusInternalError; + } + + if (xmlParseChunk((xmlParserCtxtPtr) simpleXml->xmlParser, + data, dataLen, 0)) { + return S3StatusXmlParseFailure; + } + + return simpleXml->status; } diff --git a/src/testsimplexml.c b/src/testsimplexml.c index 57fba7d..f163a79 100644 --- a/src/testsimplexml.c +++ b/src/testsimplexml.c @@ -31,57 +31,57 @@ #include "simplexml.h" static S3Status simpleXmlCallback(const char *elementPath, const char *data, - int dataLen, void *callbackData) + int dataLen, void *callbackData) { - (void) callbackData; + (void) callbackData; - printf("[%s]: [%.*s]\n", elementPath, dataLen, data); + printf("[%s]: [%.*s]\n", elementPath, dataLen, data); - return S3StatusOK; + return S3StatusOK; } // The only argument allowed is a specification of the random seed to use int main(int argc, char **argv) { - if (argc > 1) { - char *arg = argv[1]; - int seed = 0; - while (*arg) { - seed *= 10; - seed += (*arg++ - '0'); - } - - srand(seed); - } - else { - srand(time(0)); - } + if (argc > 1) { + char *arg = argv[1]; + int seed = 0; + while (*arg) { + seed *= 10; + seed += (*arg++ - '0'); + } + + srand(seed); + } + else { + srand(time(0)); + } - SimpleXml simpleXml; + SimpleXml simpleXml; - simplexml_initialize(&simpleXml, &simpleXmlCallback, 0); + simplexml_initialize(&simpleXml, &simpleXmlCallback, 0); - // Read chunks of 10K from stdin, and then feed them in random chunks - // to simplexml_add - char inbuf[10000]; + // Read chunks of 10K from stdin, and then feed them in random chunks + // to simplexml_add + char inbuf[10000]; - int amt_read; - while ((amt_read = fread(inbuf, 1, sizeof(inbuf), stdin)) > 0) { - char *buf = inbuf; - while (amt_read) { - int amt = (rand() % amt_read) + 1; - S3Status status = simplexml_add(&simpleXml, buf, amt); - if (status != S3StatusOK) { - fprintf(stderr, "ERROR: Parse failure: %d\n", status); - simplexml_deinitialize(&simpleXml); - return -1; - } - buf += amt, amt_read -= amt; - } - } + int amt_read; + while ((amt_read = fread(inbuf, 1, sizeof(inbuf), stdin)) > 0) { + char *buf = inbuf; + while (amt_read) { + int amt = (rand() % amt_read) + 1; + S3Status status = simplexml_add(&simpleXml, buf, amt); + if (status != S3StatusOK) { + fprintf(stderr, "ERROR: Parse failure: %d\n", status); + simplexml_deinitialize(&simpleXml); + return -1; + } + buf += amt, amt_read -= amt; + } + } - simplexml_deinitialize(&simpleXml); + simplexml_deinitialize(&simpleXml); - return 0; + return 0; } @@ -29,218 +29,218 @@ #include "util.h" -// Convenience utility for making the code look nicer. Tests a string +// Convenience utility for making the code look nicer. Tests a string // against a format; only the characters specified in the format are // checked (i.e. if the string is longer than the format, the string still -// checks out ok). Format characters are: +// checks out ok). Format characters are: // d - is a digit // anything else - is that character // Returns nonzero the string checks out, zero if it does not. static int checkString(const char *str, const char *format) { - while (*format) { - if (*format == 'd') { - if (!isdigit(*str)) { - return 0; - } - } - else if (*str != *format) { - return 0; - } - str++, format++; - } - - return 1; + while (*format) { + if (*format == 'd') { + if (!isdigit(*str)) { + return 0; + } + } + else if (*str != *format) { + return 0; + } + str++, format++; + } + + return 1; } int urlEncode(char *dest, const char *src, int maxSrcSize) { - static const char *urlSafe = "-_.!~*'()/"; - static const char *hex = "0123456789ABCDEF"; - - int len = 0; - - if (src) while (*src) { - if (++len > maxSrcSize) { - return 0; - } - const char *urlsafe = urlSafe; - int isurlsafe = 0; - while (*urlsafe) { - if (*urlsafe == *src) { - isurlsafe = 1; - break; - } - urlsafe++; - } - if (isurlsafe || isalnum(*src)) { - *dest++ = *src++; - } - else if (*src == ' ') { - *dest++ = '+'; - src++; - } - else { - *dest++ = '%'; - *dest++ = hex[*src / 16]; - *dest++ = hex[*src % 16]; - src++; - } - } - - *dest = 0; - - return 1; + static const char *urlSafe = "-_.!~*'()/"; + static const char *hex = "0123456789ABCDEF"; + + int len = 0; + + if (src) while (*src) { + if (++len > maxSrcSize) { + return 0; + } + const char *urlsafe = urlSafe; + int isurlsafe = 0; + while (*urlsafe) { + if (*urlsafe == *src) { + isurlsafe = 1; + break; + } + urlsafe++; + } + if (isurlsafe || isalnum(*src)) { + *dest++ = *src++; + } + else if (*src == ' ') { + *dest++ = '+'; + src++; + } + else { + *dest++ = '%'; + *dest++ = hex[*src / 16]; + *dest++ = hex[*src % 16]; + src++; + } + } + + *dest = 0; + + return 1; } int64_t parseIso8601Time(const char *str) { - // Check to make sure that it has a valid format - if (!checkString(str, "dddd-dd-ddTdd:dd:dd")) { - return -1; - } + // Check to make sure that it has a valid format + if (!checkString(str, "dddd-dd-ddTdd:dd:dd")) { + return -1; + } #define nextnum() (((*str - '0') * 10) + (*(str + 1) - '0')) - // Convert it - struct tm stm; - memset(&stm, 0, sizeof(stm)); - - stm.tm_year = (nextnum() - 19) * 100; - str += 2; - stm.tm_year += nextnum(); - str += 3; - - stm.tm_mon = nextnum() - 1; - str += 3; - - stm.tm_mday = nextnum(); - str += 3; - - stm.tm_hour = nextnum(); - str += 3; - - stm.tm_min = nextnum(); - str += 3; - - stm.tm_sec = nextnum(); - str += 2; - - stm.tm_isdst = -1; - - int64_t ret = mktime(&stm); - - // Skip the millis - - if (*str == '.') { - str++; - while (isdigit(*str)) { - str++; - } - } - - if (checkString(str, "-dd:dd") || checkString(str, "+dd:dd")) { - int sign = (*str++ == '-') ? -1 : 1; - int hours = nextnum(); - str += 3; - int minutes = nextnum(); - ret += (-sign * (((hours * 60) + minutes) * 60)); - } - // Else it should be Z to be a conformant time string, but we just assume - // that it is rather than enforcing that - - return ret; + // Convert it + struct tm stm; + memset(&stm, 0, sizeof(stm)); + + stm.tm_year = (nextnum() - 19) * 100; + str += 2; + stm.tm_year += nextnum(); + str += 3; + + stm.tm_mon = nextnum() - 1; + str += 3; + + stm.tm_mday = nextnum(); + str += 3; + + stm.tm_hour = nextnum(); + str += 3; + + stm.tm_min = nextnum(); + str += 3; + + stm.tm_sec = nextnum(); + str += 2; + + stm.tm_isdst = -1; + + int64_t ret = mktime(&stm); + + // Skip the millis + + if (*str == '.') { + str++; + while (isdigit(*str)) { + str++; + } + } + + if (checkString(str, "-dd:dd") || checkString(str, "+dd:dd")) { + int sign = (*str++ == '-') ? -1 : 1; + int hours = nextnum(); + str += 3; + int minutes = nextnum(); + ret += (-sign * (((hours * 60) + minutes) * 60)); + } + // Else it should be Z to be a conformant time string, but we just assume + // that it is rather than enforcing that + + return ret; } uint64_t parseUnsignedInt(const char *str) { - // Skip whitespace - while (isblank(*str)) { - str++; - } + // Skip whitespace + while (isblank(*str)) { + str++; + } - uint64_t ret = 0; + uint64_t ret = 0; - while (isdigit(*str)) { - ret *= 10; - ret += (*str++ - '0'); - } + while (isdigit(*str)) { + ret *= 10; + ret += (*str++ - '0'); + } - return ret; + return ret; } int base64Encode(const unsigned char *in, int inLen, char *out) { - static const char *ENC = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - char *original_out = out; - - while (inLen) { - // first 6 bits of char 1 - *out++ = ENC[*in >> 2]; - if (!--inLen) { - // last 2 bits of char 1, 4 bits of 0 - *out++ = ENC[(*in & 0x3) << 4]; - *out++ = '='; - *out++ = '='; - break; - } - // last 2 bits of char 1, first 4 bits of char 2 - *out++ = ENC[((*in & 0x3) << 4) | (*(in + 1) >> 4)]; - in++; - if (!--inLen) { - // last 4 bits of char 2, 2 bits of 0 - *out++ = ENC[(*in & 0xF) << 2]; - *out++ = '='; - break; - } - // last 4 bits of char 2, first 2 bits of char 3 - *out++ = ENC[((*in & 0xF) << 2) | (*(in + 1) >> 6)]; - in++; - // last 6 bits of char 3 - *out++ = ENC[*in & 0x3F]; - in++, inLen--; - } - - return (out - original_out); + static const char *ENC = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + char *original_out = out; + + while (inLen) { + // first 6 bits of char 1 + *out++ = ENC[*in >> 2]; + if (!--inLen) { + // last 2 bits of char 1, 4 bits of 0 + *out++ = ENC[(*in & 0x3) << 4]; + *out++ = '='; + *out++ = '='; + break; + } + // last 2 bits of char 1, first 4 bits of char 2 + *out++ = ENC[((*in & 0x3) << 4) | (*(in + 1) >> 4)]; + in++; + if (!--inLen) { + // last 4 bits of char 2, 2 bits of 0 + *out++ = ENC[(*in & 0xF) << 2]; + *out++ = '='; + break; + } + // last 4 bits of char 2, first 2 bits of char 3 + *out++ = ENC[((*in & 0xF) << 2) | (*(in + 1) >> 6)]; + in++; + // last 6 bits of char 3 + *out++ = ENC[*in & 0x3F]; + in++, inLen--; + } + + return (out - original_out); } #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) -#define blk0L(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) \ - | (rol(block->l[i], 8) & 0x00FF00FF)) +#define blk0L(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) \ + | (rol(block->l[i], 8) & 0x00FF00FF)) #define blk0B(i) (block->l[i]) -#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \ - block->l[(i + 8) & 15] ^ \ - block->l[(i + 2) & 15] ^ \ - block->l[i & 15], 1)) - -#define R0_L(v, w, x, y, z, i) \ - z += ((w & (x ^ y)) ^ y) + blk0L(i) + 0x5A827999 + rol(v, 5); \ - w = rol(w, 30); -#define R0_B(v, w, x, y, z, i) \ - z += ((w & (x ^ y)) ^ y) + blk0B(i) + 0x5A827999 + rol(v, 5); \ - w = rol(w, 30); -#define R1(v, w, x, y, z, i) \ - z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ - w = rol(w, 30); -#define R2(v, w, x, y, z, i) \ - z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \ - w = rol(w, 30); -#define R3(v, w, x, y, z, i) \ - z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ - w = rol(w, 30); -#define R4(v, w, x, y, z, i) \ - z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ - w = rol(w, 30); +#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \ + block->l[(i + 8) & 15] ^ \ + block->l[(i + 2) & 15] ^ \ + block->l[i & 15], 1)) + +#define R0_L(v, w, x, y, z, i) \ + z += ((w & (x ^ y)) ^ y) + blk0L(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R0_B(v, w, x, y, z, i) \ + z += ((w & (x ^ y)) ^ y) + blk0B(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R1(v, w, x, y, z, i) \ + z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R2(v, w, x, y, z, i) \ + z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \ + w = rol(w, 30); +#define R3(v, w, x, y, z, i) \ + z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ + w = rol(w, 30); +#define R4(v, w, x, y, z, i) \ + z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ + w = rol(w, 30); #define R0A_L(i) R0_L(a, b, c, d, e, i) #define R0B_L(i) R0_L(b, c, d, e, a, i) @@ -281,137 +281,137 @@ int base64Encode(const unsigned char *in, int inLen, char *out) static void SHA1_transform(uint32_t state[5], const unsigned char buffer[64]) { - uint32_t a, b, c, d, e; - - typedef union { - unsigned char c[64]; - uint32_t l[16]; - } u; - - unsigned char w[64]; - u *block = (u *) w; - - memcpy(block, buffer, 64); - - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - - static uint32_t endianness_indicator = 0x1; - if (((unsigned char *) &endianness_indicator)[0]) { - R0A_L( 0); - R0E_L( 1); R0D_L( 2); R0C_L( 3); R0B_L( 4); R0A_L( 5); - R0E_L( 6); R0D_L( 7); R0C_L( 8); R0B_L( 9); R0A_L(10); - R0E_L(11); R0D_L(12); R0C_L(13); R0B_L(14); R0A_L(15); - } - else { - R0A_B( 0); - R0E_B( 1); R0D_B( 2); R0C_B( 3); R0B_B( 4); R0A_B( 5); - R0E_B( 6); R0D_B( 7); R0C_B( 8); R0B_B( 9); R0A_B(10); - R0E_B(11); R0D_B(12); R0C_B(13); R0B_B(14); R0A_B(15); - } - R1E(16); R1D(17); R1C(18); R1B(19); R2A(20); - R2E(21); R2D(22); R2C(23); R2B(24); R2A(25); - R2E(26); R2D(27); R2C(28); R2B(29); R2A(30); - R2E(31); R2D(32); R2C(33); R2B(34); R2A(35); - R2E(36); R2D(37); R2C(38); R2B(39); R3A(40); - R3E(41); R3D(42); R3C(43); R3B(44); R3A(45); - R3E(46); R3D(47); R3C(48); R3B(49); R3A(50); - R3E(51); R3D(52); R3C(53); R3B(54); R3A(55); - R3E(56); R3D(57); R3C(58); R3B(59); R4A(60); - R4E(61); R4D(62); R4C(63); R4B(64); R4A(65); - R4E(66); R4D(67); R4C(68); R4B(69); R4A(70); - R4E(71); R4D(72); R4C(73); R4B(74); R4A(75); - R4E(76); R4D(77); R4C(78); R4B(79); - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; + uint32_t a, b, c, d, e; + + typedef union { + unsigned char c[64]; + uint32_t l[16]; + } u; + + unsigned char w[64]; + u *block = (u *) w; + + memcpy(block, buffer, 64); + + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + + static uint32_t endianness_indicator = 0x1; + if (((unsigned char *) &endianness_indicator)[0]) { + R0A_L( 0); + R0E_L( 1); R0D_L( 2); R0C_L( 3); R0B_L( 4); R0A_L( 5); + R0E_L( 6); R0D_L( 7); R0C_L( 8); R0B_L( 9); R0A_L(10); + R0E_L(11); R0D_L(12); R0C_L(13); R0B_L(14); R0A_L(15); + } + else { + R0A_B( 0); + R0E_B( 1); R0D_B( 2); R0C_B( 3); R0B_B( 4); R0A_B( 5); + R0E_B( 6); R0D_B( 7); R0C_B( 8); R0B_B( 9); R0A_B(10); + R0E_B(11); R0D_B(12); R0C_B(13); R0B_B(14); R0A_B(15); + } + R1E(16); R1D(17); R1C(18); R1B(19); R2A(20); + R2E(21); R2D(22); R2C(23); R2B(24); R2A(25); + R2E(26); R2D(27); R2C(28); R2B(29); R2A(30); + R2E(31); R2D(32); R2C(33); R2B(34); R2A(35); + R2E(36); R2D(37); R2C(38); R2B(39); R3A(40); + R3E(41); R3D(42); R3C(43); R3B(44); R3A(45); + R3E(46); R3D(47); R3C(48); R3B(49); R3A(50); + R3E(51); R3D(52); R3C(53); R3B(54); R3A(55); + R3E(56); R3D(57); R3C(58); R3B(59); R4A(60); + R4E(61); R4D(62); R4C(63); R4B(64); R4A(65); + R4E(66); R4D(67); R4C(68); R4B(69); R4A(70); + R4E(71); R4D(72); R4C(73); R4B(74); R4A(75); + R4E(76); R4D(77); R4C(78); R4B(79); + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; } typedef struct { - uint32_t state[5]; - uint32_t count[2]; - unsigned char buffer[64]; + uint32_t state[5]; + uint32_t count[2]; + unsigned char buffer[64]; } SHA1Context; static void SHA1_init(SHA1Context *context) { - context->state[0] = 0x67452301; - context->state[1] = 0xEFCDAB89; - context->state[2] = 0x98BADCFE; - context->state[3] = 0x10325476; - context->state[4] = 0xC3D2E1F0; - context->count[0] = context->count[1] = 0; + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; } static void SHA1_update(SHA1Context *context, const unsigned char *data, - unsigned int len) + unsigned int len) { - uint32_t i, j; + uint32_t i, j; - j = (context->count[0] >> 3) & 63; + j = (context->count[0] >> 3) & 63; - if ((context->count[0] += len << 3) < (len << 3)) { - context->count[1]++; - } + if ((context->count[0] += len << 3) < (len << 3)) { + context->count[1]++; + } - context->count[1] += (len >> 29); + context->count[1] += (len >> 29); - if ((j + len) > 63) { - memcpy(&(context->buffer[j]), data, (i = 64 - j)); - SHA1_transform(context->state, context->buffer); - for ( ; (i + 63) < len; i += 64) { - SHA1_transform(context->state, &(data[i])); - } - j = 0; - } - else { - i = 0; - } + if ((j + len) > 63) { + memcpy(&(context->buffer[j]), data, (i = 64 - j)); + SHA1_transform(context->state, context->buffer); + for ( ; (i + 63) < len; i += 64) { + SHA1_transform(context->state, &(data[i])); + } + j = 0; + } + else { + i = 0; + } - memcpy(&(context->buffer[j]), &(data[i]), len - i); + memcpy(&(context->buffer[j]), &(data[i]), len - i); } static void SHA1_final(unsigned char digest[20], SHA1Context *context) { - uint32_t i; - unsigned char finalcount[8]; + uint32_t i; + unsigned char finalcount[8]; - for (i = 0; i < 8; i++) { - finalcount[i] = (unsigned char) - ((context->count[(i >= 4 ? 0 : 1)] >> - ((3 - (i & 3)) * 8)) & 255); - } + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char) + ((context->count[(i >= 4 ? 0 : 1)] >> + ((3 - (i & 3)) * 8)) & 255); + } - SHA1_update(context, (unsigned char *) "\200", 1); + SHA1_update(context, (unsigned char *) "\200", 1); - while ((context->count[0] & 504) != 448) { - SHA1_update(context, (unsigned char *) "\0", 1); - } + while ((context->count[0] & 504) != 448) { + SHA1_update(context, (unsigned char *) "\0", 1); + } - SHA1_update(context, finalcount, 8); + SHA1_update(context, finalcount, 8); - for (i = 0; i < 20; i++) { - digest[i] = (unsigned char) - ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); - } + for (i = 0; i < 20; i++) { + digest[i] = (unsigned char) + ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); + } - memset(context->buffer, 0, 64); - memset(context->state, 0, 20); - memset(context->count, 0, 8); - memset(&finalcount, 0, 8); + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(&finalcount, 0, 8); - SHA1_transform(context->state, context->buffer); + SHA1_transform(context->state, context->buffer); } @@ -424,138 +424,138 @@ static void SHA1_final(unsigned char digest[20], SHA1Context *context) // // HMAC(K,m) = SHA1((K ^ OPAD) . SHA1((K ^ IPAD) . m)) void HMAC_SHA1(unsigned char hmac[20], const unsigned char *key, int key_len, - const unsigned char *message, int message_len) + const unsigned char *message, int message_len) { - unsigned char kopad[64], kipad[64]; - int i; - - if (key_len > 64) { - key_len = 64; - } - - for (i = 0; i < key_len; i++) { - kopad[i] = key[i] ^ 0x5c; - kipad[i] = key[i] ^ 0x36; - } - - for ( ; i < 64; i++) { - kopad[i] = 0 ^ 0x5c; - kipad[i] = 0 ^ 0x36; - } - - unsigned char digest[20]; - - SHA1Context context; - - SHA1_init(&context); - SHA1_update(&context, kipad, 64); - SHA1_update(&context, message, message_len); - SHA1_final(digest, &context); - - SHA1_init(&context); - SHA1_update(&context, kopad, 64); - SHA1_update(&context, digest, 20); - SHA1_final(hmac, &context); + unsigned char kopad[64], kipad[64]; + int i; + + if (key_len > 64) { + key_len = 64; + } + + for (i = 0; i < key_len; i++) { + kopad[i] = key[i] ^ 0x5c; + kipad[i] = key[i] ^ 0x36; + } + + for ( ; i < 64; i++) { + kopad[i] = 0 ^ 0x5c; + kipad[i] = 0 ^ 0x36; + } + + unsigned char digest[20]; + + SHA1Context context; + + SHA1_init(&context); + SHA1_update(&context, kipad, 64); + SHA1_update(&context, message, message_len); + SHA1_final(digest, &context); + + SHA1_init(&context); + SHA1_update(&context, kopad, 64); + SHA1_update(&context, digest, 20); + SHA1_final(hmac, &context); } #define rot(x,k) (((x) << (k)) | ((x) >> (32 - (k)))) uint64_t hash(const unsigned char *k, int length) { - uint32_t a, b, c; - - a = b = c = 0xdeadbeef + ((uint32_t) length); - - static uint32_t endianness_indicator = 0x1; - if (((unsigned char *) &endianness_indicator)[0]) { - while (length > 12) { - a += k[0]; - a += ((uint32_t) k[1]) << 8; - a += ((uint32_t) k[2]) << 16; - a += ((uint32_t) k[3]) << 24; - b += k[4]; - b += ((uint32_t) k[5]) << 8; - b += ((uint32_t) k[6]) << 16; - b += ((uint32_t) k[7]) << 24; - c += k[8]; - c += ((uint32_t) k[9]) << 8; - c += ((uint32_t) k[10]) << 16; - c += ((uint32_t) k[11]) << 24; - a -= c; a ^= rot(c, 4); c += b; - b -= a; b ^= rot(a, 6); a += c; - c -= b; c ^= rot(b, 8); b += a; - a -= c; a ^= rot(c, 16); c += b; - b -= a; b ^= rot(a, 19); a += c; - c -= b; c ^= rot(b, 4); b += a; - length -= 12; - k += 12; - } - - switch(length) { - case 12: c += ((uint32_t) k[11]) << 24; - case 11: c += ((uint32_t) k[10]) << 16; - case 10: c += ((uint32_t) k[9]) << 8; - case 9 : c += k[8]; - case 8 : b += ((uint32_t) k[7]) << 24; - case 7 : b += ((uint32_t) k[6]) << 16; - case 6 : b += ((uint32_t) k[5]) << 8; - case 5 : b += k[4]; - case 4 : a += ((uint32_t) k[3]) << 24; - case 3 : a += ((uint32_t) k[2]) << 16; - case 2 : a += ((uint32_t) k[1]) << 8; - case 1 : a += k[0]; break; - case 0 : goto end; - } - } - else { - while (length > 12) { - a += ((uint32_t) k[0]) << 24; - a += ((uint32_t) k[1]) << 16; - a += ((uint32_t) k[2]) << 8; - a += ((uint32_t) k[3]); - b += ((uint32_t) k[4]) << 24; - b += ((uint32_t) k[5]) << 16; - b += ((uint32_t) k[6]) << 8; - b += ((uint32_t) k[7]); - c += ((uint32_t) k[8]) << 24; - c += ((uint32_t) k[9]) << 16; - c += ((uint32_t) k[10]) << 8; - c += ((uint32_t) k[11]); - a -= c; a ^= rot(c, 4); c += b; - b -= a; b ^= rot(a, 6); a += c; - c -= b; c ^= rot(b, 8); b += a; - a -= c; a ^= rot(c, 16); c += b; - b -= a; b ^= rot(a, 19); a += c; - c -= b; c ^= rot(b, 4); b += a; - length -= 12; - k += 12; - } - - switch(length) { - case 12: c += k[11]; - case 11: c += ((uint32_t) k[10]) << 8; - case 10: c += ((uint32_t) k[9]) << 16; - case 9 : c += ((uint32_t) k[8]) << 24; - case 8 : b += k[7]; - case 7 : b += ((uint32_t) k[6]) << 8; - case 6 : b += ((uint32_t) k[5]) << 16; - case 5 : b += ((uint32_t) k[4]) << 24; - case 4 : a += k[3]; - case 3 : a += ((uint32_t) k[2]) << 8; - case 2 : a += ((uint32_t) k[1]) << 16; - case 1 : a += ((uint32_t) k[0]) << 24; break; - case 0 : goto end; - } - } - - c ^= b; c -= rot(b, 14); - a ^= c; a -= rot(c, 11); - b ^= a; b -= rot(a, 25); - c ^= b; c -= rot(b, 16); - a ^= c; a -= rot(c, 4); - b ^= a; b -= rot(a, 14); - c ^= b; c -= rot(b, 24); + uint32_t a, b, c; + + a = b = c = 0xdeadbeef + ((uint32_t) length); + + static uint32_t endianness_indicator = 0x1; + if (((unsigned char *) &endianness_indicator)[0]) { + while (length > 12) { + a += k[0]; + a += ((uint32_t) k[1]) << 8; + a += ((uint32_t) k[2]) << 16; + a += ((uint32_t) k[3]) << 24; + b += k[4]; + b += ((uint32_t) k[5]) << 8; + b += ((uint32_t) k[6]) << 16; + b += ((uint32_t) k[7]) << 24; + c += k[8]; + c += ((uint32_t) k[9]) << 8; + c += ((uint32_t) k[10]) << 16; + c += ((uint32_t) k[11]) << 24; + a -= c; a ^= rot(c, 4); c += b; + b -= a; b ^= rot(a, 6); a += c; + c -= b; c ^= rot(b, 8); b += a; + a -= c; a ^= rot(c, 16); c += b; + b -= a; b ^= rot(a, 19); a += c; + c -= b; c ^= rot(b, 4); b += a; + length -= 12; + k += 12; + } + + switch(length) { + case 12: c += ((uint32_t) k[11]) << 24; + case 11: c += ((uint32_t) k[10]) << 16; + case 10: c += ((uint32_t) k[9]) << 8; + case 9 : c += k[8]; + case 8 : b += ((uint32_t) k[7]) << 24; + case 7 : b += ((uint32_t) k[6]) << 16; + case 6 : b += ((uint32_t) k[5]) << 8; + case 5 : b += k[4]; + case 4 : a += ((uint32_t) k[3]) << 24; + case 3 : a += ((uint32_t) k[2]) << 16; + case 2 : a += ((uint32_t) k[1]) << 8; + case 1 : a += k[0]; break; + case 0 : goto end; + } + } + else { + while (length > 12) { + a += ((uint32_t) k[0]) << 24; + a += ((uint32_t) k[1]) << 16; + a += ((uint32_t) k[2]) << 8; + a += ((uint32_t) k[3]); + b += ((uint32_t) k[4]) << 24; + b += ((uint32_t) k[5]) << 16; + b += ((uint32_t) k[6]) << 8; + b += ((uint32_t) k[7]); + c += ((uint32_t) k[8]) << 24; + c += ((uint32_t) k[9]) << 16; + c += ((uint32_t) k[10]) << 8; + c += ((uint32_t) k[11]); + a -= c; a ^= rot(c, 4); c += b; + b -= a; b ^= rot(a, 6); a += c; + c -= b; c ^= rot(b, 8); b += a; + a -= c; a ^= rot(c, 16); c += b; + b -= a; b ^= rot(a, 19); a += c; + c -= b; c ^= rot(b, 4); b += a; + length -= 12; + k += 12; + } + + switch(length) { + case 12: c += k[11]; + case 11: c += ((uint32_t) k[10]) << 8; + case 10: c += ((uint32_t) k[9]) << 16; + case 9 : c += ((uint32_t) k[8]) << 24; + case 8 : b += k[7]; + case 7 : b += ((uint32_t) k[6]) << 8; + case 6 : b += ((uint32_t) k[5]) << 16; + case 5 : b += ((uint32_t) k[4]) << 24; + case 4 : a += k[3]; + case 3 : a += ((uint32_t) k[2]) << 8; + case 2 : a += ((uint32_t) k[1]) << 16; + case 1 : a += ((uint32_t) k[0]) << 24; break; + case 0 : goto end; + } + } + + c ^= b; c -= rot(b, 14); + a ^= c; a -= rot(c, 11); + b ^= a; b -= rot(a, 25); + c ^= b; c -= rot(b, 16); + a ^= c; a -= rot(c, 4); + b ^= a; b -= rot(a, 14); + c ^= b; c -= rot(b, 24); end: - return ((((uint64_t) c) << 32) | b); + return ((((uint64_t) c) << 32) | b); } |