summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBryan Ischo <bryan@ischo.com>2008-07-13 12:48:54 +0000
committerBryan Ischo <bryan@ischo.com>2008-07-13 12:48:54 +0000
commit77c7ea225a5c977e2590eb1b4e7451d26919b577 (patch)
treed25ddb1a53b3ac3d4b6053213fa2ac57530aa706
parent56a9e0707d0014327f57a71a0dbb2ec53e79a4c9 (diff)
downloadceph-libs3-77c7ea225a5c977e2590eb1b4e7451d26919b577.tar.gz
* More work in progress; fixed some bugs, initial get object implementation
-rw-r--r--inc/libs3.h111
-rw-r--r--inc/request.h12
-rw-r--r--inc/response_headers_handler.h14
-rw-r--r--inc/util.h4
-rw-r--r--src/bucket.c110
-rw-r--r--src/error_parser.c5
-rw-r--r--src/general.c4
-rw-r--r--src/object.c44
-rw-r--r--src/request.c87
-rw-r--r--src/response_headers_handler.c91
-rw-r--r--src/s3.c425
-rw-r--r--src/service.c20
-rw-r--r--src/util.c6
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;
diff --git a/inc/util.h b/inc/util.h
index 7e62157..0ac3250 100644
--- a/inc/util.h
+++ b/inc/util.h
@@ -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(&params, 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;
diff --git a/src/s3.c b/src/s3.c
index 8c4e6bb..cd1ef5f 100644
--- a/src/s3.c
+++ b/src/s3.c
@@ -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
diff --git a/src/util.c b/src/util.c
index 9cf075c..2d2ed4b 100644
--- a/src/util.c
+++ b/src/util.c
@@ -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;
}