diff options
author | Bryan Ischo <bryan@ischo.com> | 2008-07-13 12:48:54 +0000 |
---|---|---|
committer | Bryan Ischo <bryan@ischo.com> | 2008-07-13 12:48:54 +0000 |
commit | 77c7ea225a5c977e2590eb1b4e7451d26919b577 (patch) | |
tree | d25ddb1a53b3ac3d4b6053213fa2ac57530aa706 | |
parent | 56a9e0707d0014327f57a71a0dbb2ec53e79a4c9 (diff) | |
download | ceph-libs3-77c7ea225a5c977e2590eb1b4e7451d26919b577.tar.gz |
* More work in progress; fixed some bugs, initial get object implementation
-rw-r--r-- | inc/libs3.h | 111 | ||||
-rw-r--r-- | inc/request.h | 12 | ||||
-rw-r--r-- | inc/response_headers_handler.h | 14 | ||||
-rw-r--r-- | inc/util.h | 4 | ||||
-rw-r--r-- | src/bucket.c | 110 | ||||
-rw-r--r-- | src/error_parser.c | 5 | ||||
-rw-r--r-- | src/general.c | 4 | ||||
-rw-r--r-- | src/object.c | 44 | ||||
-rw-r--r-- | src/request.c | 87 | ||||
-rw-r--r-- | src/response_headers_handler.c | 91 | ||||
-rw-r--r-- | src/s3.c | 425 | ||||
-rw-r--r-- | src/service.c | 20 | ||||
-rw-r--r-- | src/util.c | 6 |
13 files changed, 629 insertions, 304 deletions
diff --git a/inc/libs3.h b/inc/libs3.h index 30eb07b..e9c55c0 100644 --- a/inc/libs3.h +++ b/inc/libs3.h @@ -69,27 +69,27 @@ /** - * S3_MAX_META_HEADERS_SIZE is the maximum number of bytes allowed for + * S3_MAX_METADATA_SIZE is the maximum number of bytes allowed for * x-amz-meta header names and values in any request passed to Amazon S3 **/ -#define S3_MAX_META_HEADER_SIZE 2048 +#define S3_MAX_METADATA_SIZE 2048 /** - * S3_META_HEADER_NAME_PREFIX is the prefix of an S3 "meta header" + * S3_METADATA_HEADER_NAME_PREFIX is the prefix of an S3 "meta header" **/ -#define S3_META_HEADER_NAME_PREFIX "x-amz-meta-" +#define S3_METADATA_HEADER_NAME_PREFIX "x-amz-meta-" /** - * S3_MAX_META_HEADER_COUNT is the maximum number of x-amz-meta- headers that + * S3_MAX_METADATA_COUNT is the maximum number of x-amz-meta- headers that * could be included in a request to S3. The smallest meta header is * "x-amz-meta-n: v". Since S3 doesn't count the ": " against the total, the * smallest amount of data to count for a header would be the length of * "x-amz-meta-nv". **/ -#define S3_MAX_META_HEADER_COUNT \ - (S3_MAX_META_HEADER_SIZE / (sizeof(S3_META_HEADER_NAME_PREFIX "nv") - 1)) +#define S3_MAX_METADATA_COUNT \ + (S3_MAX_METADATA_SIZE / (sizeof(S3_METADATA_HEADER_NAME_PREFIX "nv") - 1)) /** @@ -126,8 +126,8 @@ typedef enum S3StatusFailedToCreateRequest , S3StatusFailedToInitializeRequest , S3StatusFailedToCreateRequestContext , - S3StatusMetaHeadersTooLong , - S3StatusBadMetaHeader , + S3StatusMetaDataHeadersTooLong , + S3StatusBadMetaData , S3StatusBadContentType , S3StatusContentTypeTooLong , S3StatusBadMD5 , @@ -308,12 +308,12 @@ typedef struct S3NameValue } S3NameValue; /** - * S3ResponseHeaders is passed to the header callback function which is called - * when the complete response status code and headers have been received. - * Some of the fields of this structure are optional and may not be provided - * in the response, and some will always be provided in the response. + * S3ResponseProperties is passed to the properties callback function which is + * called when the complete response status code and properties have been + * received. Some of the fields of this structure are optional and may not be + * provided in the response, and some will always be provided in the response. **/ -typedef struct S3ResponseHeaders +typedef struct S3ResponseProperties { /** * This optional field identifies the request ID and may be used when @@ -337,7 +337,7 @@ typedef struct S3ResponseHeaders * provided in the response. A value of 0 means that there is no content * provided. **/ - int64_t contentLength; + uint64_t contentLength; /** * This optional field names the server which serviced the request. It * may or may not be provided. @@ -359,15 +359,15 @@ typedef struct S3ResponseHeaders **/ time_t lastModified; /** - * This is the number of user-provided metadata headers associated with - * the resource. + * This is the number of user-provided meta data associated with the + * resource. **/ - int metaHeadersCount; + int metaDataCount; /** - * These are the metadata headers associated with the resource. + * These are the meta data associated with the resource. **/ - const S3NameValue *metaHeaders; -} S3ResponseHeaders; + const S3NameValue *metaData; +} S3ResponseProperties; /** @@ -495,7 +495,7 @@ typedef struct S3ListBucketContent /** - * S3RequestHeaders is the set of headers that may optionally be set by the + * S3PutProperties is the set of properties that may optionally be set by the * user when putting objects to S3. Each field of this structure is optional * and may or may not be present. **/ @@ -510,7 +510,7 @@ typedef struct S3ListBucketContent // care. // expires is optional */ -typedef struct S3RequestHeaders +typedef struct S3PutProperties { /** * If present, this is the Content-Type that should be associated with the @@ -567,15 +567,37 @@ typedef struct S3RequestHeaders **/ S3MetaDataDirective metaDataDirective; /** - * This is the number of headers in the metaHeaders field. Ignored and - * assumed to be 0, for all except put_object and copy_object operations. + * This is the number of values in the metaData field. **/ - int metaHeadersCount; + int metaDataCount; /** - * These are the meta headers to pass to S3. + * These are the meta data to pass to S3. **/ - const S3NameValue *metaHeaders; -} S3RequestHeaders; + const S3NameValue *metaData; +} S3PutProperties; + + +// Used for get object or head object, specify properties for controlling +// the get/head +typedef struct S3GetProperties +{ + /** + * If >= 0, ... + **/ + time_t ifModifiedSince; + /** + * If >= 0 ... + **/ + time_t ifNotModifiedSince; + /** + * If present ... + **/ + const char *ifMatchETag; + /** + * If present ... + **/ + const char *ifNotMatchETag; +} S3GetProperties; typedef struct S3ErrorDetails @@ -612,17 +634,16 @@ typedef void (S3MutexDestroyCallback)(struct S3Mutex *mutex); /** - * This callback is made whenever the response headers become available for + * This callback is made whenever the response properties become available for * any request. * * @param callbackData is the callback data as specified when the S3Request * for which this callback was specified was initialized - * @param headers is the headers (includes the response status code) that - * are available from the response. + * @param properties is the properties that are available from the response. * @return S3Status??? **/ -typedef S3Status (S3ResponseHeadersCallback)(const S3ResponseHeaders *headers, - void *callbackData); +typedef S3Status (S3ResponsePropertiesCallback) + (const S3ResponseProperties *properties, void *callbackData); typedef void (S3ResponseCompleteCallback)(S3Status status, int httpResponseCode, @@ -853,8 +874,8 @@ S3Status S3_runonce_request_context(S3RequestContext *requestContext, typedef struct S3ResponseHandler { - // Headers callback - S3ResponseHeadersCallback *headersCallback; + // Properties callback + S3ResponsePropertiesCallback *propertiesCallback; // Request complete callback - always called if the call which initiates // the request doesn't return an error code @@ -1034,7 +1055,7 @@ void S3_list_bucket(S3BucketContext *bucketContext, */ void S3_put_object(S3BucketContext *bucketContext, const char *key, uint64_t contentLength, - const S3RequestHeaders *requestHeaders, + const S3PutProperties *putProperties, S3RequestContext *requestContext, S3PutObjectHandler *handler, void *callbackData); @@ -1042,12 +1063,12 @@ void S3_put_object(S3BucketContext *bucketContext, const char *key, /* // destinationBucket NULL means the same bucket as in pBucketContext // destinationKey NULL means the same object key as [key] -// if pOptionalHeaders is NULL, existing headers will not be changed +// if putProperties is NULL, existing properties will not be changed */ void S3_copy_object(S3BucketContext *bucketContext, const char *key, const char *destinationBucket, const char *destinationKey, - const S3RequestHeaders *requestHeaders, + const S3PutProperties *putProperties, S3RequestContext *requestContext, S3ResponseHandler *handler, void *callbackData); @@ -1057,8 +1078,7 @@ void S3_copy_object(S3BucketContext *bucketContext, // not, fail and close the request. We expect S3 to be sensible about // Range and anything not returned properly must indicate an error in the // request. -// byte range is a freeform Byte Range specification string, of the form: -// MMM-NNN[,OOO-PPP...] +// byteRangeCount == 0 means get everything // We only allow complete ranges and we enforce this on the request // The response has to have the exact same set of ranges, or it is an error. // In this way, the caller can be sure that they will get exactly what they @@ -1066,16 +1086,15 @@ void S3_copy_object(S3BucketContext *bucketContext, // ifModifiedSince and ifUnmodifiedSince if > 0 will be used */ void S3_get_object(S3BucketContext *bucketContext, const char *key, - long ifModifiedSince, long ifUnmodifiedSince, - const char *ifMatchETag, const char *ifNotMatchETag, - const char *byteRange, S3RequestContext *requestContext, + const S3GetProperties *getProperties, + uint64_t startByte, uint64_t byteCount, + S3RequestContext *requestContext, S3GetObjectHandler *handler, void *callbackData); // ifModifiedSince and ifUnmodifiedSince if > 0 will be used void S3_head_object(S3BucketContext *bucketContext, const char *key, - long ifModifiedSince, long ifUnmodifiedSince, - const char *ifMatchETag, const char *ifNotMatchETag, + const S3GetProperties *getProperties, S3RequestContext *requestContext, S3ResponseHandler *handler, void *callbackData); diff --git a/inc/request.h b/inc/request.h index a5ed101..d1df84a 100644 --- a/inc/request.h +++ b/inc/request.h @@ -73,11 +73,11 @@ typedef struct RequestParams // AWS Secret Access Key const char *secretAccessKey; - // Request headers - const S3RequestHeaders *requestHeaders; + // Put properties + const S3PutProperties *putProperties; // Callback to be made when headers are available. Might not be called. - S3ResponseHeadersCallback *headersCallback; + S3ResponsePropertiesCallback *propertiesCallback; // Callback to be made to supply data to send to S3. Might not be called. S3PutObjectDataCallback *toS3Callback; @@ -119,7 +119,7 @@ typedef struct Request int httpResponseCode; // Callback to be made when headers are available. Might not be called. - S3ResponseHeadersCallback *headersCallback; + S3ResponsePropertiesCallback *propertiesCallback; // Callback to be made to supply data to send to S3. Might not be called. S3PutObjectDataCallback *toS3Callback; @@ -141,8 +141,8 @@ typedef struct Request // Handler of response headers ResponseHeadersHandler responseHeadersHandler; - // This is set to nonzero after the haders callback has been made - int headersCallbackMade; + // This is set to nonzero after the properties callback has been made + int propertiesCallbackMade; // Parser of errors ErrorParser errorParser; diff --git a/inc/response_headers_handler.h b/inc/response_headers_handler.h index 917fa32..2fbafcf 100644 --- a/inc/response_headers_handler.h +++ b/inc/response_headers_handler.h @@ -34,20 +34,20 @@ typedef struct ResponseHeadersHandler { // The structure to pass to the headers callback. This is filled in by // the ResponseHeadersHandler from the headers added to it. - S3ResponseHeaders responseHeaders; + S3ResponseProperties responseProperties; // Set to 1 after the done call has been made int done; // copied into here. We allow 128 bytes for each header, plus \0 term. - string_multibuffer(responseHeaderStrings, 5 * 129); + string_multibuffer(responsePropertyStrings, 5 * 129); - // responseHeaders.metaHeaders strings get copied into here - string_multibuffer(responseMetaHeaderStrings, - COMPACTED_META_HEADER_BUFFER_SIZE); + // responseproperties.metaHeaders strings get copied into here + string_multibuffer(responseMetaDataStrings, + COMPACTED_METADATA_BUFFER_SIZE); - // Response meta headers - S3NameValue responseMetaHeaders[S3_MAX_META_HEADER_COUNT]; + // Response meta data + S3NameValue responseMetaData[S3_MAX_METADATA_COUNT]; } ResponseHeadersHandler; @@ -34,8 +34,8 @@ // This is the maximum number of bytes needed in a "compacted meta header" // buffer, which is a buffer storing all of the compacted meta headers. -#define COMPACTED_META_HEADER_BUFFER_SIZE \ - (S3_MAX_META_HEADER_COUNT * sizeof(S3_META_HEADER_NAME_PREFIX "n: v")) +#define COMPACTED_METADATA_BUFFER_SIZE \ + (S3_MAX_METADATA_COUNT * sizeof(S3_METADATA_HEADER_NAME_PREFIX "n: v")) // Maximum url encoded key size; since every single character could require // URL encoding, it's 3 times the size of a key (since each url encoded diff --git a/src/bucket.c b/src/bucket.c index 4922baa..22d2558 100644 --- a/src/bucket.c +++ b/src/bucket.c @@ -28,13 +28,13 @@ #include "request.h" #include "simplexml.h" -// test bucket ---------------------------------------------------------------- +// test bucket --------------------------------------------------------------- typedef struct TestBucketData { SimpleXml simpleXml; - S3ResponseHeadersCallback *responseHeadersCallback; + S3ResponsePropertiesCallback *responsePropertiesCallback; S3ResponseCompleteCallback *responseCompleteCallback; void *callbackData; @@ -45,8 +45,9 @@ typedef struct TestBucketData } TestBucketData; -static S3Status testBucketXmlCallback(const char *elementPath, const char *data, - int dataLen, void *callbackData) +static S3Status testBucketXmlCallback(const char *elementPath, + const char *data, int dataLen, + void *callbackData) { TestBucketData *tbData = (TestBucketData *) callbackData; @@ -60,13 +61,13 @@ static S3Status testBucketXmlCallback(const char *elementPath, const char *data, } -static S3Status testBucketHeadersCallback - (const S3ResponseHeaders *responseHeaders, void *callbackData) +static S3Status testBucketPropertiesCallback + (const S3ResponseProperties *responseProperties, void *callbackData) { TestBucketData *tbData = (TestBucketData *) callbackData; - return (*(tbData->responseHeadersCallback)) - (responseHeaders, tbData->callbackData); + return (*(tbData->responsePropertiesCallback)) + (responseProperties, tbData->callbackData); } @@ -109,7 +110,8 @@ void S3_test_bucket(S3Protocol protocol, S3UriStyle uriStyle, S3ResponseHandler *handler, void *callbackData) { // Create the callback data - TestBucketData *tbData = (TestBucketData *) malloc(sizeof(TestBucketData)); + TestBucketData *tbData = + (TestBucketData *) malloc(sizeof(TestBucketData)); if (!tbData) { (*(handler->completeCallback)) (S3StatusOutOfMemory, 0, 0, callbackData); @@ -124,7 +126,7 @@ void S3_test_bucket(S3Protocol protocol, S3UriStyle uriStyle, return; } - tbData->responseHeadersCallback = handler->headersCallback; + tbData->responsePropertiesCallback = handler->propertiesCallback; tbData->responseCompleteCallback = handler->completeCallback; tbData->callbackData = callbackData; @@ -144,8 +146,8 @@ void S3_test_bucket(S3Protocol protocol, S3UriStyle uriStyle, "?location", // subResource accessKeyId, // accessKeyId secretAccessKey, // secretAccessKey - 0, // requestHeaders - &testBucketHeadersCallback, // headersCallback + 0, // requestProperties + &testBucketPropertiesCallback, // propertiesCallback 0, // toS3Callback 0, // toS3CallbackTotalSize &testBucketDataCallback, // fromS3Callback @@ -158,11 +160,11 @@ void S3_test_bucket(S3Protocol protocol, S3UriStyle uriStyle, } -// create bucket -------------------------------------------------------------- +// create bucket ------------------------------------------------------------- typedef struct CreateBucketData { - S3ResponseHeadersCallback *responseHeadersCallback; + S3ResponsePropertiesCallback *responsePropertiesCallback; S3ResponseCompleteCallback *responseCompleteCallback; void *callbackData; @@ -171,13 +173,13 @@ typedef struct CreateBucketData } CreateBucketData; -static S3Status createBucketHeadersCallback - (const S3ResponseHeaders *responseHeaders, void *callbackData) +static S3Status createBucketPropertiesCallback + (const S3ResponseProperties *responseProperties, void *callbackData) { CreateBucketData *cbData = (CreateBucketData *) callbackData; - return (*(cbData->responseHeadersCallback)) - (responseHeaders, cbData->callbackData); + return (*(cbData->responsePropertiesCallback)) + (responseProperties, cbData->callbackData); } @@ -236,7 +238,7 @@ void S3_create_bucket(S3Protocol protocol, const char *accessKeyId, return; } - cbData->responseHeadersCallback = handler->headersCallback; + cbData->responsePropertiesCallback = handler->propertiesCallback; cbData->responseCompleteCallback = handler->completeCallback; cbData->callbackData = callbackData; @@ -252,8 +254,8 @@ void S3_create_bucket(S3Protocol protocol, const char *accessKeyId, cbData->docLen = 0; } - // Set up S3RequestHeaders - S3RequestHeaders headers = + // Set up S3PutProperties + S3PutProperties properties = { 0, // contentType 0, // md5 @@ -264,8 +266,8 @@ void S3_create_bucket(S3Protocol protocol, const char *accessKeyId, cannedAcl, // cannedAcl 0, // sourceObject 0, // metaDataDirective - 0, // metaHeadersCount - 0 // metaHeaders + 0, // metaDataCount + 0 // metaData }; // Set up the RequestParams @@ -280,8 +282,8 @@ void S3_create_bucket(S3Protocol protocol, const char *accessKeyId, 0, // subResource accessKeyId, // accessKeyId secretAccessKey, // secretAccessKey - &headers, // requestHeaders - &createBucketHeadersCallback, // headersCallback + &properties, // requestProperties + &createBucketPropertiesCallback, // propertiesCallback &createBucketDataCallback, // toS3Callback cbData->docLen, // toS3CallbackTotalSize 0, // fromS3Callback @@ -294,23 +296,23 @@ void S3_create_bucket(S3Protocol protocol, const char *accessKeyId, } -// delete bucket -------------------------------------------------------------- +// delete bucket ------------------------------------------------------------- typedef struct DeleteBucketData { - S3ResponseHeadersCallback *responseHeadersCallback; + S3ResponsePropertiesCallback *responsePropertiesCallback; S3ResponseCompleteCallback *responseCompleteCallback; void *callbackData; } DeleteBucketData; -static S3Status deleteBucketHeadersCallback - (const S3ResponseHeaders *responseHeaders, void *callbackData) +static S3Status deleteBucketPropertiesCallback + (const S3ResponseProperties *responseProperties, void *callbackData) { DeleteBucketData *dbData = (DeleteBucketData *) callbackData; - return (*(dbData->responseHeadersCallback)) - (responseHeaders, dbData->callbackData); + return (*(dbData->responsePropertiesCallback)) + (responseProperties, dbData->callbackData); } @@ -331,7 +333,8 @@ static void deleteBucketCompleteCallback(S3Status requestStatus, void S3_delete_bucket(S3Protocol protocol, S3UriStyle uriStyle, const char *accessKeyId, const char *secretAccessKey, - const char *bucketName, S3RequestContext *requestContext, + const char *bucketName, + S3RequestContext *requestContext, S3ResponseHandler *handler, void *callbackData) { // Create the callback data @@ -343,7 +346,7 @@ void S3_delete_bucket(S3Protocol protocol, S3UriStyle uriStyle, return; } - dbData->responseHeadersCallback = handler->headersCallback; + dbData->responsePropertiesCallback = handler->propertiesCallback; dbData->responseCompleteCallback = handler->completeCallback; dbData->callbackData = callbackData; @@ -359,8 +362,8 @@ void S3_delete_bucket(S3Protocol protocol, S3UriStyle uriStyle, 0, // subResource accessKeyId, // accessKeyId secretAccessKey, // secretAccessKey - 0, // requestHeaders - &deleteBucketHeadersCallback, // headersCallback + 0, // requestProperties + &deleteBucketPropertiesCallback, // propertiesCallback 0, // toS3Callback 0, // toS3CallbackTotalSize 0, // fromS3Callback @@ -405,7 +408,7 @@ typedef struct ListBucketData { SimpleXml simpleXml; - S3ResponseHeadersCallback *responseHeadersCallback; + S3ResponsePropertiesCallback *responsePropertiesCallback; S3ListBucketCallback *listBucketCallback; S3ResponseCompleteCallback *responseCompleteCallback; void *callbackData; @@ -448,10 +451,12 @@ static S3Status make_list_bucket_callback(ListBucketData *lbData) S3ListBucketContent *contentDest = &(contents[i]); ListBucketContents *contentSrc = &(lbData->contents[i]); contentDest->key = contentSrc->key; - contentDest->lastModified = parseIso8601Time(contentSrc->lastModified); + contentDest->lastModified = + parseIso8601Time(contentSrc->lastModified); contentDest->eTag = contentSrc->eTag; contentDest->size = parseUnsignedInt(contentSrc->size); - contentDest->ownerId = contentSrc->ownerId[0] ?contentSrc->ownerId : 0; + contentDest->ownerId = + contentSrc->ownerId[0] ?contentSrc->ownerId : 0; contentDest->ownerDisplayName = (contentSrc->ownerDisplayName[0] ? contentSrc->ownerDisplayName : 0); } @@ -470,8 +475,9 @@ static S3Status make_list_bucket_callback(ListBucketData *lbData) } -static S3Status listBucketXmlCallback(const char *elementPath, const char *data, - int dataLen, void *callbackData) +static S3Status listBucketXmlCallback(const char *elementPath, + const char *data, int dataLen, + void *callbackData) { ListBucketData *lbData = (ListBucketData *) callbackData; @@ -517,7 +523,8 @@ static S3Status listBucketXmlCallback(const char *elementPath, const char *data, string_buffer_append (contents->ownerDisplayName, data, dataLen, fit); } - else if (!strcmp(elementPath, "ListBucketResult/CommonPrefix/Prefix")) { + else if (!strcmp(elementPath, + "ListBucketResult/CommonPrefix/Prefix")) { int which = lbData->commonPrefixesCount; lbData->commonPrefixLens[which] += snprintf(lbData->commonPrefixes[which], @@ -544,7 +551,8 @@ static S3Status listBucketXmlCallback(const char *elementPath, const char *data, (&(lbData->contents[lbData->contentsCount])); } } - else if (!strcmp(elementPath, "ListBucketResult/CommonPrefix/Prefix")) { + else if (!strcmp(elementPath, + "ListBucketResult/CommonPrefix/Prefix")) { // Finished a Prefix lbData->commonPrefixesCount++; if (lbData->commonPrefixesCount == MAX_COMMON_PREFIXES) { @@ -567,13 +575,13 @@ static S3Status listBucketXmlCallback(const char *elementPath, const char *data, } -static S3Status listBucketHeadersCallback - (const S3ResponseHeaders *responseHeaders, void *callbackData) +static S3Status listBucketPropertiesCallback + (const S3ResponseProperties *responseProperties, void *callbackData) { ListBucketData *lbData = (ListBucketData *) callbackData; - return (*(lbData->responseHeadersCallback)) - (responseHeaders, lbData->callbackData); + return (*(lbData->responsePropertiesCallback)) + (responseProperties, lbData->callbackData); } @@ -659,7 +667,8 @@ void S3_list_bucket(S3BucketContext *bucketContext, const char *prefix, safe_append("max-keys", maxKeysString); } - ListBucketData *lbData = (ListBucketData *) malloc(sizeof(ListBucketData)); + ListBucketData *lbData = + (ListBucketData *) malloc(sizeof(ListBucketData)); if (!lbData) { (*(handler->responseHandler.completeCallback)) @@ -676,7 +685,8 @@ void S3_list_bucket(S3BucketContext *bucketContext, const char *prefix, return; } - lbData->responseHeadersCallback = handler->responseHandler.headersCallback; + lbData->responsePropertiesCallback = + handler->responseHandler.propertiesCallback; lbData->listBucketCallback = handler->listBucketCallback; lbData->responseCompleteCallback = handler->responseHandler.completeCallback; @@ -698,8 +708,8 @@ void S3_list_bucket(S3BucketContext *bucketContext, const char *prefix, 0, // subResource bucketContext->accessKeyId, // accessKeyId bucketContext->secretAccessKey, // secretAccessKey - 0, // requestHeaders - &listBucketHeadersCallback, // headersCallback + 0, // requestProperties + &listBucketPropertiesCallback, // propertiesCallback 0, // toS3Callback 0, // toS3CallbackTotalSize &listBucketDataCallback, // fromS3Callback diff --git a/src/error_parser.c b/src/error_parser.c index d26bba8..825c1df 100644 --- a/src/error_parser.c +++ b/src/error_parser.c @@ -102,8 +102,9 @@ static S3Status errorXmlCallback(const char *elementPath, const char *data, // Value didn't fit; ignore this one. return S3StatusOK; } - S3NameValue *nv = &(errorParser->extraDetails - [errorParser->s3ErrorDetails.extraDetailsCount++]); + S3NameValue *nv = + &(errorParser->extraDetails + [errorParser->s3ErrorDetails.extraDetailsCount++]); nv->name = name; nv->value = value; } diff --git a/src/general.c b/src/general.c index 1a7ced5..3a32f23 100644 --- a/src/general.c +++ b/src/general.c @@ -184,8 +184,8 @@ const char *S3_get_status_name(S3Status status) handlecase(FailedToCreateRequest); handlecase(FailedToInitializeRequest); handlecase(FailedToCreateRequestContext); - handlecase(MetaHeadersTooLong); - handlecase(BadMetaHeader); + handlecase(MetaDataHeadersTooLong); + handlecase(BadMetaData); handlecase(BadContentType); handlecase(ContentTypeTooLong); handlecase(BadMD5); diff --git a/src/object.c b/src/object.c index c2e7e85..d4844de 100644 --- a/src/object.c +++ b/src/object.c @@ -26,11 +26,11 @@ #include "request.h" -// put object ----------------------------------------------------------------- +// put object ---------------------------------------------------------------- void S3_put_object(S3BucketContext *bucketContext, const char *key, uint64_t contentLength, - const S3RequestHeaders *requestHeaders, + const S3PutProperties *putProperties, S3RequestContext *requestContext, S3PutObjectHandler *handler, void *callbackData) { @@ -46,8 +46,8 @@ void S3_put_object(S3BucketContext *bucketContext, const char *key, 0, // subResource bucketContext->accessKeyId, // accessKeyId bucketContext->secretAccessKey, // secretAccessKey - requestHeaders, // requestHeaders - handler->responseHandler.headersCallback, // headersCallback + putProperties, // putProperties + handler->responseHandler.propertiesCallback, // propertiesCallback handler->putObjectDataCallback, // toS3Callback contentLength, // toS3CallbackTotalSize 0, // fromS3Callback @@ -62,7 +62,7 @@ void S3_put_object(S3BucketContext *bucketContext, const char *key, void S3_copy_object(S3BucketContext *bucketContext, const char *key, const char *destinationBucket, const char *destinationKey, - const S3RequestHeaders *requestHeaders, + const S3PutProperties *putProperties, S3RequestContext *requestContext, S3ResponseHandler *handler, void *callbackData) { @@ -70,17 +70,41 @@ void S3_copy_object(S3BucketContext *bucketContext, const char *key, void S3_get_object(S3BucketContext *bucketContext, const char *key, - long ifModifiedSince, long ifUnmodifiedSince, - const char *ifMatchETag, const char *ifNotMatchETag, - const char *byteRange, S3RequestContext *requestContext, + const S3GetProperties *getProperties, + uint64_t startByte, uint64_t byteCount, + S3RequestContext *requestContext, S3GetObjectHandler *handler, void *callbackData) { + // xxx todo - do something with getProperties, startByte, and byteCount + + // Set up the RequestParams + RequestParams params = + { + HttpRequestTypeGET, // httpRequestType + bucketContext->protocol, // protocol + bucketContext->uriStyle, // uriStyle + bucketContext->bucketName, // bucketName + key, // key + 0, // queryParams + 0, // subResource + bucketContext->accessKeyId, // accessKeyId + bucketContext->secretAccessKey, // secretAccessKey + 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); } void S3_head_object(S3BucketContext *bucketContext, const char *key, - long ifModifiedSince, long ifUnmodifiedSince, - const char *ifMatchETag, const char *ifNotMatchETag, + const S3GetProperties *getProperties, S3RequestContext *requestContext, S3ResponseHandler *handler, void *callbackData) { diff --git a/src/request.c b/src/request.c index 32e5772..d2abc4c 100644 --- a/src/request.c +++ b/src/request.c @@ -51,17 +51,17 @@ static int requestStackCountG; typedef struct RequestComputedValues { // All x-amz- headers, in normalized form (i.e. NAME: VALUE, no other ws) - char *amzHeaders[S3_MAX_META_HEADER_COUNT + 2]; // + 2 for acl and date + char *amzHeaders[S3_MAX_METADATA_COUNT + 2]; // + 2 for acl and date // 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_META_HEADER_BUFFER_SIZE + 256 + 1]; + char amzHeadersRaw[COMPACTED_METADATA_BUFFER_SIZE + 256 + 1]; // Canonicalized x-amz- headers string_multibuffer(canonicalizedAmzHeaders, - COMPACTED_META_HEADER_BUFFER_SIZE + 256 + 1); + COMPACTED_METADATA_BUFFER_SIZE + 256 + 1); // URL-Encoded key char urlEncodedKey[MAX_URLENCODED_KEY_SIZE + 1]; @@ -97,11 +97,11 @@ typedef struct RequestComputedValues // or the request is finished being procesed static S3Status request_headers_done(Request *request) { - if (request->headersCallbackMade) { + if (request->propertiesCallbackMade) { return S3StatusOK; } - request->headersCallbackMade = 1; + request->propertiesCallbackMade = 1; // Get the http response code if (curl_easy_getinfo(request->curl, CURLINFO_RESPONSE_CODE, @@ -112,13 +112,14 @@ static S3Status request_headers_done(Request *request) response_headers_handler_done(&(request->responseHeadersHandler), request->curl); - return (*(request->headersCallback)) - (&(request->responseHeadersHandler.responseHeaders), + return (*(request->propertiesCallback)) + (&(request->responseHeadersHandler.responseProperties), request->callbackData); } -static size_t curl_header_func(void *ptr, size_t size, size_t nmemb, void *data) +static size_t curl_header_func(void *ptr, size_t size, size_t nmemb, + void *data) { Request *request = (Request *) data; @@ -151,7 +152,8 @@ static size_t curl_read_func(void *ptr, size_t size, size_t nmemb, void *data) } -static size_t curl_write_func(void *ptr, size_t size, size_t nmemb, void *data) +static size_t curl_write_func(void *ptr, size_t size, size_t nmemb, + void *data) { Request *request = (Request *) data; @@ -194,7 +196,7 @@ static size_t curl_write_func(void *ptr, size_t size, size_t nmemb, void *data) static S3Status compose_amz_headers(const RequestParams *params, RequestComputedValues *values) { - const S3RequestHeaders *headers = params->requestHeaders; + const S3PutProperties *properties = params->putProperties; values->amzHeadersCount = 0; values->amzHeadersRaw[0] = 0; @@ -212,7 +214,7 @@ static S3Status compose_amz_headers(const RequestParams *params, sizeof(values->amzHeadersRaw) - len, \ format, __VA_ARGS__); \ if (len >= sizeof(values->amzHeadersRaw)) { \ - return S3StatusMetaHeadersTooLong; \ + return S3StatusMetaDataHeadersTooLong; \ } \ while ((len > 0) && (values->amzHeadersRaw[len - 1] == ' ')) { \ len--; \ @@ -225,7 +227,7 @@ static S3Status compose_amz_headers(const RequestParams *params, values->amzHeaders[values->amzHeadersCount++] = \ &(values->amzHeadersRaw[len]); \ if ((len + l) >= sizeof(values->amzHeadersRaw)) { \ - return S3StatusMetaHeadersTooLong; \ + return S3StatusMetaDataHeadersTooLong; \ } \ int todo = l; \ while (todo--) { \ @@ -240,22 +242,22 @@ static S3Status compose_amz_headers(const RequestParams *params, } while (0) // Check and copy in the x-amz-meta headers - if (headers) { + if (properties) { int i; - for (i = 0; i < headers->metaHeadersCount; i++) { - const S3NameValue *header = &(headers->metaHeaders[i]); - char headerName[S3_MAX_META_HEADER_SIZE - sizeof(": v")]; - int len = snprintf(headerName, sizeof(headerName), - "x-amz-meta-%s", header->name); + 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), + "x-amz-meta-%s", property->name); char *hn = headerName; - header_name_tolower_copy(hn, len); - // Copy in a space and then the value - headers_append(0, " %s", header->value); + 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->requestHeaders->cannedAcl) { + switch (params->putProperties->cannedAcl) { case S3CannedAclPrivate: cannedAclString = 0; break; @@ -282,13 +284,13 @@ static S3Status compose_amz_headers(const RequestParams *params, if (params->httpRequestType == HttpRequestTypeCOPY) { // Add the x-amz-copy-source header - if (params->requestHeaders->sourceObject && - params->requestHeaders->sourceObject[0]) { + if (params->putProperties->sourceObject && + params->putProperties->sourceObject[0]) { headers_append(1, "x-amz-copy-source: %s", - params->requestHeaders->sourceObject); + params->putProperties->sourceObject); } // And the x-amz-metadata-directive header - if (params->requestHeaders->metaDataDirective != + if (params->putProperties->metaDataDirective != S3MetaDataDirectiveCopy) { headers_append(1, "%s", "x-amz-metadata-directive: REPLACE"); } @@ -305,11 +307,11 @@ static S3Status compose_standard_headers(const RequestParams *params, #define do_header(fmt, sourceField, destField, badError, tooLongError) \ do { \ - if (params->requestHeaders && \ - params->requestHeaders-> sourceField && \ - params->requestHeaders-> sourceField[0]) { \ + if (params->putProperties && \ + params->putProperties-> sourceField && \ + params->putProperties-> sourceField[0]) { \ /* Skip whitespace at beginning of val */ \ - const char *val = params->requestHeaders-> sourceField; \ + const char *val = params->putProperties-> sourceField; \ while (*val && isblank(*val)) { \ val++; \ } \ @@ -357,10 +359,10 @@ static S3Status compose_standard_headers(const RequestParams *params, S3StatusBadContentEncoding, S3StatusContentEncodingTooLong); // 6. Expires - if (params->requestHeaders && (params->requestHeaders->expires >= 0)) { + if (params->putProperties && (params->putProperties->expires >= 0)) { char date[100]; - strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S GMT", - gmtime(&(params->requestHeaders->expires))); + strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S UTC", + gmtime(&(params->putProperties->expires))); snprintf(values->expiresHeader, sizeof(values->expiresHeader), "Expires: %s", date); } @@ -433,7 +435,7 @@ static void header_gnome_sort(const char **headers, int size) static void canonicalize_amz_headers(RequestComputedValues *values) { // Make a copy of the headers that will be sorted - const char *sortedHeaders[S3_MAX_META_HEADER_COUNT]; + const char *sortedHeaders[S3_MAX_METADATA_COUNT]; memcpy(sortedHeaders, values->amzHeaders, (values->amzHeadersCount * sizeof(sortedHeaders[0]))); @@ -563,7 +565,8 @@ static S3Status compose_auth_header(const RequestParams *params, len += snprintf(&(signbuf[len]), sizeof(signbuf) - len, \ format, __VA_ARGS__) - signbuf_append("%s\n", http_request_type_to_verb(params->httpRequestType)); + 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 @@ -597,7 +600,8 @@ static S3Status compose_auth_header(const RequestParams *params, base64mem->data[base64mem->length - 1] = 0; snprintf(values->authorizationHeader, sizeof(values->authorizationHeader), - "Authorization: AWS %s:%s", params->accessKeyId, base64mem->data); + "Authorization: AWS %s:%s", params->accessKeyId, + base64mem->data); BIO_free_all(base64); @@ -844,7 +848,7 @@ static S3Status request_get(const RequestParams *params, return status; } - request->headersCallback = params->headersCallback; + request->propertiesCallback = params->propertiesCallback; request->toS3Callback = params->toS3Callback; @@ -856,7 +860,7 @@ static S3Status request_get(const RequestParams *params, response_headers_handler_initialize(&(request->responseHeadersHandler)); - request->headersCallbackMade = 0; + request->propertiesCallbackMade = 0; error_parser_initialize(&(request->errorParser)); @@ -949,8 +953,8 @@ void request_perform(const RequestParams *params, S3RequestContext *context) // Validate the bucket name if (params->bucketName && - ((status = S3_validate_bucket_name(params->bucketName, - params->uriStyle)) != S3StatusOK)) { + ((status = S3_validate_bucket_name + (params->bucketName, params->uriStyle)) != S3StatusOK)) { return_status(status); } @@ -960,7 +964,8 @@ void request_perform(const RequestParams *params, S3RequestContext *context) } // Compose standard headers - if ((status = compose_standard_headers(params, &computed)) != S3StatusOK) { + if ((status = compose_standard_headers + (params, &computed)) != S3StatusOK) { return_status(status); } diff --git a/src/response_headers_handler.c b/src/response_headers_handler.c index d4ebb5e..1927af3 100644 --- a/src/response_headers_handler.c +++ b/src/response_headers_handler.c @@ -29,25 +29,25 @@ void response_headers_handler_initialize(ResponseHeadersHandler *handler) { - handler->responseHeaders.requestId = 0; - handler->responseHeaders.requestId2 = 0; - handler->responseHeaders.contentType = 0; - handler->responseHeaders.contentLength = -1; - handler->responseHeaders.server = 0; - handler->responseHeaders.eTag = 0; - handler->responseHeaders.lastModified = -1; - handler->responseHeaders.metaHeadersCount = 0; - handler->responseHeaders.metaHeaders = 0; + handler->responseProperties.requestId = 0; + handler->responseProperties.requestId2 = 0; + handler->responseProperties.contentType = 0; + handler->responseProperties.contentLength = -1; + 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->responseHeaderStrings); - string_multibuffer_initialize(handler->responseMetaHeaderStrings); + string_multibuffer_initialize(handler->responsePropertyStrings); + string_multibuffer_initialize(handler->responseMetaDataStrings); } void response_headers_handler_add(ResponseHeadersHandler *handler, char *header, int len) { - S3ResponseHeaders *responseHeaders = &(handler->responseHeaders); + S3ResponseProperties *responseProperties = &(handler->responseProperties); char *end = &(header[len]); // Curl might call back the header function after the body has been @@ -60,8 +60,8 @@ void response_headers_handler_add(ResponseHeadersHandler *handler, // 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->responseHeaderStringsSize == - (sizeof(handler->responseHeaderStrings) - 1)) { + if (handler->responsePropertyStringsSize == + (sizeof(handler->responsePropertyStrings) - 1)) { return; } @@ -111,54 +111,54 @@ void response_headers_handler_add(ResponseHeadersHandler *handler, int valuelen = (end - c) + 1, fit; if (!strncmp(header, "x-amz-request-id", namelen)) { - responseHeaders->requestId = - string_multibuffer_current(handler->responseHeaderStrings); - string_multibuffer_add(handler->responseHeaderStrings, c, + responseProperties->requestId = + string_multibuffer_current(handler->responsePropertyStrings); + string_multibuffer_add(handler->responsePropertyStrings, c, valuelen, fit); } else if (!strncmp(header, "x-amz-id-2", namelen)) { - responseHeaders->requestId2 = - string_multibuffer_current(handler->responseHeaderStrings); - string_multibuffer_add(handler->responseHeaderStrings, c, + responseProperties->requestId2 = + string_multibuffer_current(handler->responsePropertyStrings); + string_multibuffer_add(handler->responsePropertyStrings, c, valuelen, fit); } else if (!strncmp(header, "Content-Type", namelen)) { - responseHeaders->contentType = - string_multibuffer_current(handler->responseHeaderStrings); - string_multibuffer_add(handler->responseHeaderStrings, c, + responseProperties->contentType = + string_multibuffer_current(handler->responsePropertyStrings); + string_multibuffer_add(handler->responsePropertyStrings, c, valuelen, fit); } else if (!strncmp(header, "Content-Length", namelen)) { - handler->responseHeaders.contentLength = 0; + handler->responseProperties.contentLength = 0; while (*c) { - handler->responseHeaders.contentLength *= 10; - handler->responseHeaders.contentLength += (*c++ - '0'); + handler->responseProperties.contentLength *= 10; + handler->responseProperties.contentLength += (*c++ - '0'); } } else if (!strncmp(header, "Server", namelen)) { - responseHeaders->server = - string_multibuffer_current(handler->responseHeaderStrings); - string_multibuffer_add(handler->responseHeaderStrings, c, + responseProperties->server = + string_multibuffer_current(handler->responsePropertyStrings); + string_multibuffer_add(handler->responsePropertyStrings, c, valuelen, fit); } else if (!strncmp(header, "ETag", namelen)) { - responseHeaders->eTag = - string_multibuffer_current(handler->responseHeaderStrings); - string_multibuffer_add(handler->responseHeaderStrings, c, + responseProperties->eTag = + string_multibuffer_current(handler->responsePropertyStrings); + string_multibuffer_add(handler->responsePropertyStrings, c, valuelen, fit); } else if (!strncmp(header, "x-amz-meta-", sizeof("x-amz-meta-") - 1)) { // Make sure there is room for another x-amz-meta header - if (handler->responseHeaders.metaHeadersCount == - sizeof(handler->responseMetaHeaders)) { + if (handler->responseProperties.metaDataCount == + sizeof(handler->responseMetaData)) { return; } // Copy the name in - char *metaName = &(header[sizeof("x-amz-meta-")]); + char *metaName = &(header[sizeof("x-amz-meta-") - 1]); int metaNameLen = (namelen - (sizeof("x-amz-meta-") - 1)); char *copiedName = - string_multibuffer_current(handler->responseMetaHeaderStrings); - string_multibuffer_add(handler->responseMetaHeaderStrings, metaName, + string_multibuffer_current(handler->responseMetaDataStrings); + string_multibuffer_add(handler->responseMetaDataStrings, metaName, metaNameLen, fit); if (!fit) { return; @@ -166,16 +166,21 @@ void response_headers_handler_add(ResponseHeadersHandler *handler, // Copy the value in char *copiedValue = - string_multibuffer_current(handler->responseMetaHeaderStrings); - string_multibuffer_add(handler->responseMetaHeaderStrings, + 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->responseMetaHeaders - [handler->responseHeaders.metaHeadersCount++]); + &(handler->responseMetaData + [handler->responseProperties.metaDataCount++]); metaHeader->name = copiedName; metaHeader->value = copiedValue; } @@ -188,8 +193,8 @@ void response_headers_handler_done(ResponseHeadersHandler *handler, CURL *curl) // curl parse it if (curl_easy_getinfo (curl, CURLINFO_FILETIME, - &(handler->responseHeaders.lastModified)) != CURLE_OK) { - handler->responseHeaders.lastModified = -1; + &(handler->responseProperties.lastModified)) != CURLE_OK) { + handler->responseProperties.lastModified = -1; } handler->done = 1; @@ -38,12 +38,18 @@ #include "libs3.h" +// Something is weird with glibc ... setenv/unsetenv are not defined in +// stdlib.h as they should be +extern int setenv(const char *, const char *, int); +extern int unsetenv(const char *); + + // Command-line options, saved as globals ------------------------------------ -static int showResponseHeadersG = 0; +static int showResponsePropertiesG = 0; static S3Protocol protocolG = S3ProtocolHTTPS; static S3UriStyle uriStyleG = S3UriStyleVirtualHost; -// request headers stuff +// request properties stuff // acl stuff @@ -88,10 +94,23 @@ static const S3ErrorDetails *errorG = 0; (sizeof(CONTENT_DISPOSITION_FILENAME_PREFIX) - 1) #define CONTENT_ENCODING_PREFIX "contentEncoding=" #define CONTENT_ENCODING_PREFIX_LEN (sizeof(CONTENT_ENCODING_PREFIX) - 1) -#define VALID_DURATION_PREFIX "validDuration=" -#define VALID_DURATION_PREFIX_LEN (sizeof(VALID_DURATION_PREFIX) - 1) +#define EXPIRES_PREFIX "expires=" +#define EXPIRES_PREFIX_LEN (sizeof(EXPIRES_PREFIX) - 1) #define X_AMZ_META_PREFIX "x-amz-meta-" #define X_AMZ_META_PREFIX_LEN (sizeof(X_AMZ_META_PREFIX) - 1) +#define IF_MODIFIED_SINCE_PREFIX "ifModifiedSince=" +#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) +#define IF_MATCH_PREFIX "ifMatch=" +#define IF_MATCH_PREFIX_LEN (sizeof(IF_MATCH_PREFIX) - 1) +#define IF_NOT_MATCH_PREFIX "ifNotMatch=" +#define IF_NOT_MATCH_PREFIX_LEN (sizeof(IF_NOT_MATCH_PREFIX) - 1) +#define START_BYTE_PREFIX "startByte=" +#define START_BYTE_PREFIX_LEN (sizeof(START_BYTE_PREFIX) - 1) +#define BYTE_COUNT_PREFIX "byteCount=" +#define BYTE_COUNT_PREFIX_LEN (sizeof(BYTE_COUNT_PREFIX) - 1) // libs3 mutex stuff --------------------------------------------------------- @@ -194,7 +213,7 @@ static void usageExit(FILE *out) "\n" " -p : use path-style URIs (--path-style)\n" " -u : unencrypted (use HTTP instead of HTTPS) (--https)\n" -" -s : show response headers (--show-headers)\n" +" -s : show response properties (--show-properties)\n" "\n" " Environment:\n" "\n" @@ -212,11 +231,12 @@ static void usageExit(FILE *out) " put <bucket>/<key> [filename, contentLength, cacheControl, contentType,\n" " md5, contentDispositionFilename, contentEncoding,\n" " validDuration, cannedAcl, [x-amz-meta-...]]\n" -" copy <sourcebucket>/<sourcekey> <destbucket>/<destkey> [headers]\n" -" get <buckey>/<key> [filename (required if -r is used), if-modified-since,\n" -" if-unmodified-since, if-match, if-not-match,\n" -" range-start, range-end]\n" -" head <bucket>/<key>\n" +" copy <sourcebucket>/<sourcekey> <destbucket>/<destkey> [properties]\n" +" get <buckey>/<key> [filename (required if -s is used), ifModifiedSince,\n" +" ifNotmodifiedSince, ifMatch, ifNotMatch,\n" +" startByte, byteCount]\n" +" head <bucket>/<key> [ifModifiedSince, ifNotmodifiedSince, ifMatch,\n" +" ifNotMatch] (implies -s)\n" " delete <bucket>/<key>\n" " todo : acl stuff\n" "\n"); @@ -334,53 +354,149 @@ static void growbuffer_destroy(growbuffer *gb) } +// 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: +// 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; +} + + +static time_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; + } + +#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); + + time_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; +} + + static struct option longOptionsG[] = { - { "path-style", no_argument , 0, 'p' }, - { "unencrypted", no_argument , 0, 'u' }, + { "path-style", no_argument, 0, 'p' }, + { "unencrypted", no_argument, 0, 'u' }, { "show-headers", no_argument, 0, 's' }, { 0, 0, 0, 0 } }; -// response header callback -------------------------------------------------- +// response properties callback ---------------------------------------------- // This callback does the same thing for every request type: prints out the -// headers if the user has requested them to be so -static S3Status responseHeadersCallback(const S3ResponseHeaders *headers, - void *callbackData) +// properties if the user has requested them to be so +static S3Status responsePropertiesCallback + (const S3ResponseProperties *properties, void *callbackData) { - if (!showResponseHeadersG) { + if (!showResponsePropertiesG) { return S3StatusOK; } #define print_nonnull(name, field) \ do { \ - if (headers-> field) { \ - printf("%s: %s\n", name, headers-> field); \ + if (properties-> field) { \ + printf("%s: %s\n", name, properties-> field); \ } \ } while (0) print_nonnull("Request-Id", requestId); print_nonnull("Request-Id-2", requestId2); - if (headers->contentLength > 0) { - printf("Content-Length: %lld\n", headers->contentLength); + if (properties->contentLength > 0) { + printf("Content-Length: %lld\n", properties->contentLength); } print_nonnull("Server", server); print_nonnull("ETag", eTag); - if (headers->lastModified > 0) { + if (properties->lastModified > 0) { char timebuf[256]; // localtime is not thread-safe but we don't care here. xxx note - // localtime doesn't seem to actually do anything, 0 locatime of 0 // returns EST Unix epoch, it should return the NZST equivalent ... strftime(timebuf, sizeof(timebuf), "%Y/%m/%d %H:%M:%S %Z", - localtime(&(headers->lastModified))); + localtime(&(properties->lastModified))); printf("Last-Modified: %s\n", timebuf); } int i; - for (i = 0; i < headers->metaHeadersCount; i++) { - printf("x-amz-meta-%s: %s\n", headers->metaHeaders[i].name, - headers->metaHeaders[i].value); + for (i = 0; i < properties->metaDataCount; i++) { + printf("x-amz-meta-%s: %s\n", properties->metaData[i].name, + properties->metaData[i].value); } return S3StatusOK; @@ -437,7 +553,7 @@ static void list_service() S3ListServiceHandler listServiceHandler = { - { &responseHeadersCallback, &responseCompleteCallback }, + { &responsePropertiesCallback, &responseCompleteCallback }, &listServiceCallback }; @@ -473,7 +589,7 @@ static void test_bucket(int argc, char **argv, int optind) S3ResponseHandler responseHandler = { - &responseHeadersCallback, &responseCompleteCallback + &responsePropertiesCallback, &responseCompleteCallback }; char locationConstraint[64]; @@ -557,7 +673,7 @@ static void create_bucket(int argc, char **argv, int optind) S3ResponseHandler responseHandler = { - &responseHeadersCallback, &responseCompleteCallback + &responsePropertiesCallback, &responseCompleteCallback }; S3_create_bucket(protocolG, accessKeyIdG, secretAccessKeyG, bucketName, @@ -586,7 +702,7 @@ static void delete_bucket(int argc, char **argv, int optind) S3ResponseHandler responseHandler = { - &responseHeadersCallback, &responseCompleteCallback + &responsePropertiesCallback, &responseCompleteCallback }; S3_delete_bucket(protocolG, uriStyleG, accessKeyIdG, secretAccessKeyG, @@ -707,7 +823,7 @@ static void list_bucket(int argc, char **argv, int optind) S3ListBucketHandler listBucketHandler = { - { &responseHeadersCallback, &responseCompleteCallback }, + { &responsePropertiesCallback, &responseCompleteCallback }, &listBucketCallback }; @@ -728,7 +844,7 @@ static void list_bucket(int argc, char **argv, int optind) } -// put object ----------------------------------------------------------------- +// put object ---------------------------------------------------------------- typedef struct put_object_callback_data { @@ -738,7 +854,8 @@ typedef struct put_object_callback_data } put_object_callback_data; -static int putObjectCallback(int bufferSize, char *buffer, void *callbackData) +static int putObjectDataCallback(int bufferSize, char *buffer, + void *callbackData) { put_object_callback_data *data = (put_object_callback_data *) callbackData; @@ -788,8 +905,8 @@ static void put_object(int argc, char **argv, int optind) const char *contentDispositionFilename = 0, *contentEncoding = 0; time_t expires = -1; S3CannedAcl cannedAcl = S3CannedAclPrivate; - int metaHeadersCount = 0; - S3NameValue metaHeaders[S3_MAX_META_HEADER_COUNT]; + int metaPropertiesCount = 0; + S3NameValue metaProperties[S3_MAX_METADATA_COUNT]; while (optind < argc) { char *param = argv[optind++]; @@ -826,16 +943,18 @@ static void put_object(int argc, char **argv, int optind) CONTENT_ENCODING_PREFIX_LEN)) { contentEncoding = &(param[CONTENT_ENCODING_PREFIX_LEN]); } - else if (!strncmp(param, VALID_DURATION_PREFIX, - VALID_DURATION_PREFIX_LEN)) { - expires = (time(NULL) + - convertInt(&(param[VALID_DURATION_PREFIX_LEN]), - "validDuration")); + else if (!strncmp(param, EXPIRES_PREFIX, EXPIRES_PREFIX_LEN)) { + expires = parseIso8601Time(&(param[EXPIRES_PREFIX_LEN])); + if (expires < 0) { + fprintf(stderr, "ERROR: 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 (metaHeadersCount == S3_MAX_META_HEADER_COUNT) { - fprintf(stderr, "ERROR: Too many x-amz-meta- headers, " - "limit %d: %s\n", S3_MAX_META_HEADER_COUNT, param); + if (metaPropertiesCount == S3_MAX_METADATA_COUNT) { + fprintf(stderr, "ERROR: Too many x-amz-meta- properties, " + "limit %d: %s\n", S3_MAX_METADATA_COUNT, param); usageExit(stderr); } char *name = &(param[X_AMZ_META_PREFIX_LEN]); @@ -848,8 +967,8 @@ static void put_object(int argc, char **argv, int optind) usageExit(stderr); } *value++ = 0; - metaHeaders[metaHeadersCount].name = name; - metaHeaders[metaHeadersCount++].value = value; + 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]); @@ -876,38 +995,6 @@ static void put_object(int argc, char **argv, int optind) } } - S3_init(); - - S3BucketContext bucketContext = - { - bucketName, - protocolG, - uriStyleG, - accessKeyIdG, - secretAccessKeyG - }; - - S3RequestHeaders requestHeaders = - { - contentType, - md5, - cacheControl, - contentDispositionFilename, - contentEncoding, - expires, - cannedAcl, - 0, - 0, - metaHeadersCount, - metaHeaders - }; - - S3PutObjectHandler listBucketHandler = - { - { &responseHeadersCallback, &responseCompleteCallback }, - &putObjectCallback - }; - put_object_callback_data data; data.infile = 0; @@ -960,8 +1047,40 @@ static void put_object(int argc, char **argv, int optind) data.contentLength = contentLength; - S3_put_object(&bucketContext, key, contentLength, &requestHeaders, 0, - &listBucketHandler, &data); + S3_init(); + + S3BucketContext bucketContext = + { + bucketName, + protocolG, + uriStyleG, + accessKeyIdG, + secretAccessKeyG + }; + + S3PutProperties putProperties = + { + contentType, + md5, + cacheControl, + contentDispositionFilename, + contentEncoding, + expires, + cannedAcl, + 0, + 0, + metaPropertiesCount, + metaProperties + }; + + S3PutObjectHandler putObjectHandler = + { + { &responsePropertiesCallback, &responseCompleteCallback }, + &putObjectDataCallback + }; + + S3_put_object(&bucketContext, key, contentLength, &putProperties, 0, + &putObjectHandler, &data); if (data.infile) { fclose(data.infile); @@ -982,7 +1101,150 @@ static void put_object(int argc, char **argv, int optind) } -// main ----------------------------------------------------------------------- +// get object ---------------------------------------------------------------- + +static S3Status getObjectDataCallback(int bufferSize, const char *buffer, + void *callbackData) +{ + FILE *outfile = (FILE *) callbackData; + + size_t wrote = fwrite(buffer, 1, bufferSize, outfile); + + return (wrote < bufferSize) ? S3StatusFailure : S3StatusOK; +} + + +static void get_object(int argc, char **argv, int optind) +{ + if (optind == argc) { + fprintf(stderr, "ERROR: Missing parameter: bucket/key\n"); + usageExit(stderr); + } + + // Split bucket/key + char *slash = argv[optind]; + while (*slash && (*slash != '/')) { + slash++; + } + if (!*slash || !*(slash + 1)) { + fprintf(stderr, "ERROR: Invalid bucket/key name: %s\n", argv[optind]); + usageExit(stderr); + } + *slash++ = 0; + + const char *bucketName = argv[optind++]; + const char *key = slash; + + const char *filename = 0; + time_t ifModifiedSince = -1, ifNotModifiedSince = -1; + const char *ifMatch = 0, *ifNotMatch = 0; + uint64_t startByte = 0, byteCount = 0; + + while (optind < argc) { + char *param = argv[optind++]; + 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, "ERROR: 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, "ERROR: 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, "ERROR: Unknown param: %s\n", param); + usageExit(stderr); + } + } + + FILE *outfile; + + if (filename) { + if ((outfile = fopen(filename, "w")) == NULL) { + fprintf(stderr, "ERROR: Failed to open output file %s: ", + filename); + perror(0); + exit(-1); + } + } + else if (showResponsePropertiesG) { + fprintf(stderr, "ERROR: get -s requires a filename parameter\n"); + usageExit(stderr); + } + else { + outfile = stdout; + } + + S3_init(); + + S3BucketContext bucketContext = + { + bucketName, + protocolG, + uriStyleG, + accessKeyIdG, + secretAccessKeyG + }; + + S3GetProperties getProperties = + { + ifModifiedSince, + ifNotModifiedSince, + ifMatch, + ifNotMatch + }; + + S3GetObjectHandler getObjectHandler = + { + { &responsePropertiesCallback, &responseCompleteCallback }, + &getObjectDataCallback + }; + + S3_get_object(&bucketContext, key, &getProperties, startByte, byteCount, + 0, &getObjectHandler, outfile); + + fclose(outfile); + + if (statusG != S3StatusOK) { + printError(); + } + + S3_deinitialize(); +} + + +// main ---------------------------------------------------------------------- int main(int argc, char **argv) { @@ -1004,7 +1266,7 @@ int main(int argc, char **argv) protocolG = S3ProtocolHTTP; break; case 's': - showResponseHeadersG = 1; + showResponsePropertiesG = 1; break; default: fprintf(stderr, "ERROR: Unknown options: -%c\n", c); @@ -1058,6 +1320,7 @@ int main(int argc, char **argv) else if (!strcmp(command, "copy")) { } else if (!strcmp(command, "get")) { + get_object(argc, argv, optind); } else if (!strcmp(command, "head")) { } diff --git a/src/service.c b/src/service.c index 466c00a..4b89619 100644 --- a/src/service.c +++ b/src/service.c @@ -33,7 +33,7 @@ typedef struct XmlCallbackData { SimpleXml simpleXml; - S3ResponseHeadersCallback *responseHeadersCallback; + S3ResponsePropertiesCallback *responsePropertiesCallback; S3ListServiceCallback *listServiceCallback; S3ResponseCompleteCallback *responseCompleteCallback; void *callbackData; @@ -93,13 +93,13 @@ static S3Status xmlCallback(const char *elementPath, const char *data, } -static S3Status headersCallback(const S3ResponseHeaders *responseHeaders, - void *callbackData) +static S3Status propertiesCallback + (const S3ResponseProperties *responseProperties, void *callbackData) { XmlCallbackData *cbData = (XmlCallbackData *) callbackData; - return (*(cbData->responseHeadersCallback)) - (responseHeaders, cbData->callbackData); + return (*(cbData->responsePropertiesCallback)) + (responseProperties, cbData->callbackData); } @@ -151,7 +151,8 @@ void S3_list_service(S3Protocol protocol, const char *accessKeyId, return; } - data->responseHeadersCallback = handler->responseHandler.headersCallback; + data->responsePropertiesCallback = + handler->responseHandler.propertiesCallback; data->listServiceCallback = handler->listServiceCallback; data->responseCompleteCallback = handler->responseHandler.completeCallback; data->callbackData = callbackData; @@ -161,9 +162,6 @@ void S3_list_service(S3Protocol protocol, const char *accessKeyId, string_buffer_initialize(data->bucketName); string_buffer_initialize(data->creationDate); - // stopped here - change the params that follow, and make sure that the - // complete callback frees the XmlCallbackData that we allocated - // Set up the RequestParams RequestParams params = { @@ -176,8 +174,8 @@ void S3_list_service(S3Protocol protocol, const char *accessKeyId, 0, // subResource accessKeyId, // accessKeyId secretAccessKey, // secretAccessKey - 0, // requestHeaders - &headersCallback, // headersCallback + 0, // requestProperties + &propertiesCallback, // propertiesCallback 0, // toS3Callback 0, // toS3CallbackTotalSize &dataCallback, // fromS3Callback @@ -42,16 +42,16 @@ static int checkString(const char *str, const char *format) while (*format) { if (*format == 'd') { if (!isdigit(*str)) { - return 1; + return 0; } } else if (*str != *format) { - return 1; + return 0; } str++, format++; } - return 0; + return 1; } |