1 /** **************************************************************************
4 * Copyright 2008 Bryan Ischo <bryan@ischo.com>
6 * This file is part of libs3.
8 * libs3 is free software: you can redistribute it and/or modify it under the
9 * terms of the GNU General Public License as published by the Free Software
10 * Foundation, version 3 of the License.
12 * In addition, as a special exception, the copyright holders give
13 * permission to link the code of this library and its programs with the
14 * OpenSSL library, and distribute linked combinations including the two.
16 * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY
17 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
21 * You should have received a copy of the GNU General Public License version 3
22 * along with libs3, in a file named COPYING. If not, see
23 * <http://www.gnu.org/licenses/>.
25 ************************************************************************** **/
31 #include "simplexml.h"
33 // test bucket ---------------------------------------------------------------
35 typedef struct TestBucketData
39 S3ResponsePropertiesCallback *responsePropertiesCallback;
40 S3ResponseCompleteCallback *responseCompleteCallback;
43 int locationConstraintReturnSize;
44 char *locationConstraintReturn;
46 string_buffer(locationConstraint, 256);
50 static S3Status testBucketXmlCallback(const char *elementPath,
51 const char *data, int dataLen,
54 TestBucketData *tbData = (TestBucketData *) callbackData;
58 if (data && !strcmp(elementPath, "LocationConstraint")) {
59 string_buffer_append(tbData->locationConstraint, data, dataLen, fit);
66 static S3Status testBucketPropertiesCallback
67 (const S3ResponseProperties *responseProperties, void *callbackData)
69 TestBucketData *tbData = (TestBucketData *) callbackData;
71 return (*(tbData->responsePropertiesCallback))
72 (responseProperties, tbData->callbackData);
76 static S3Status testBucketDataCallback(int bufferSize, const char *buffer,
79 TestBucketData *tbData = (TestBucketData *) callbackData;
81 return simplexml_add(&(tbData->simpleXml), buffer, bufferSize);
85 static void testBucketCompleteCallback(S3Status requestStatus,
86 const S3ErrorDetails *s3ErrorDetails,
89 TestBucketData *tbData = (TestBucketData *) callbackData;
91 // Copy the location constraint into the return buffer
92 snprintf(tbData->locationConstraintReturn,
93 tbData->locationConstraintReturnSize, "%s",
94 tbData->locationConstraint);
96 (*(tbData->responseCompleteCallback))
97 (requestStatus, s3ErrorDetails, tbData->callbackData);
99 simplexml_deinitialize(&(tbData->simpleXml));
105 void S3_test_bucket(S3Protocol protocol, S3UriStyle uriStyle,
106 const char *accessKeyId, const char *secretAccessKey,
107 const char *bucketName, int locationConstraintReturnSize,
108 char *locationConstraintReturn,
109 S3RequestContext *requestContext,
110 const S3ResponseHandler *handler, void *callbackData)
112 // Create the callback data
113 TestBucketData *tbData =
114 (TestBucketData *) malloc(sizeof(TestBucketData));
116 (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData);
120 simplexml_initialize(&(tbData->simpleXml), &testBucketXmlCallback, tbData);
122 tbData->responsePropertiesCallback = handler->propertiesCallback;
123 tbData->responseCompleteCallback = handler->completeCallback;
124 tbData->callbackData = callbackData;
126 tbData->locationConstraintReturnSize = locationConstraintReturnSize;
127 tbData->locationConstraintReturn = locationConstraintReturn;
128 string_buffer_initialize(tbData->locationConstraint);
130 // Set up the RequestParams
131 RequestParams params =
133 HttpRequestTypeGET, // httpRequestType
134 { bucketName, // bucketName
135 protocol, // protocol
136 uriStyle, // uriStyle
137 accessKeyId, // accessKeyId
138 secretAccessKey }, // secretAccessKey
141 "location", // subResource
142 0, // copySourceBucketName
148 &testBucketPropertiesCallback, // propertiesCallback
150 0, // toS3CallbackTotalSize
151 &testBucketDataCallback, // fromS3Callback
152 &testBucketCompleteCallback, // completeCallback
153 tbData // callbackData
156 // Perform the request
157 request_perform(¶ms, requestContext);
161 // create bucket -------------------------------------------------------------
163 typedef struct CreateBucketData
165 S3ResponsePropertiesCallback *responsePropertiesCallback;
166 S3ResponseCompleteCallback *responseCompleteCallback;
170 int docLen, docBytesWritten;
174 static S3Status createBucketPropertiesCallback
175 (const S3ResponseProperties *responseProperties, void *callbackData)
177 CreateBucketData *cbData = (CreateBucketData *) callbackData;
179 return (*(cbData->responsePropertiesCallback))
180 (responseProperties, cbData->callbackData);
184 static int createBucketDataCallback(int bufferSize, char *buffer,
187 CreateBucketData *cbData = (CreateBucketData *) callbackData;
189 if (!cbData->docLen) {
193 int remaining = (cbData->docLen - cbData->docBytesWritten);
195 int toCopy = bufferSize > remaining ? remaining : bufferSize;
201 memcpy(buffer, &(cbData->doc[cbData->docBytesWritten]), toCopy);
203 cbData->docBytesWritten += toCopy;
209 static void createBucketCompleteCallback(S3Status requestStatus,
210 const S3ErrorDetails *s3ErrorDetails,
213 CreateBucketData *cbData = (CreateBucketData *) callbackData;
215 (*(cbData->responseCompleteCallback))
216 (requestStatus, s3ErrorDetails, cbData->callbackData);
222 void S3_create_bucket(S3Protocol protocol, const char *accessKeyId,
223 const char *secretAccessKey, const char *bucketName,
224 S3CannedAcl cannedAcl, const char *locationConstraint,
225 S3RequestContext *requestContext,
226 const S3ResponseHandler *handler, void *callbackData)
228 // Create the callback data
229 CreateBucketData *cbData =
230 (CreateBucketData *) malloc(sizeof(CreateBucketData));
232 (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData);
236 cbData->responsePropertiesCallback = handler->propertiesCallback;
237 cbData->responseCompleteCallback = handler->completeCallback;
238 cbData->callbackData = callbackData;
240 if (locationConstraint) {
242 snprintf(cbData->doc, sizeof(cbData->doc),
243 "<CreateBucketConfiguration><LocationConstraint>"
244 "%s</LocationConstraint></CreateBucketConfiguration>",
246 cbData->docBytesWritten = 0;
252 // Set up S3PutProperties
253 S3PutProperties properties =
258 0, // contentDispositionFilename
259 0, // contentEncoding
261 cannedAcl, // cannedAcl
266 // Set up the RequestParams
267 RequestParams params =
269 HttpRequestTypePUT, // httpRequestType
270 { bucketName, // bucketName
271 protocol, // protocol
272 S3UriStylePath, // uriStyle
273 accessKeyId, // accessKeyId
274 secretAccessKey }, // secretAccessKey
278 0, // copySourceBucketName
283 &properties, // putProperties
284 &createBucketPropertiesCallback, // propertiesCallback
285 &createBucketDataCallback, // toS3Callback
286 cbData->docLen, // toS3CallbackTotalSize
288 &createBucketCompleteCallback, // completeCallback
289 cbData // callbackData
292 // Perform the request
293 request_perform(¶ms, requestContext);
297 // delete bucket -------------------------------------------------------------
299 typedef struct DeleteBucketData
301 S3ResponsePropertiesCallback *responsePropertiesCallback;
302 S3ResponseCompleteCallback *responseCompleteCallback;
307 static S3Status deleteBucketPropertiesCallback
308 (const S3ResponseProperties *responseProperties, void *callbackData)
310 DeleteBucketData *dbData = (DeleteBucketData *) callbackData;
312 return (*(dbData->responsePropertiesCallback))
313 (responseProperties, dbData->callbackData);
317 static void deleteBucketCompleteCallback(S3Status requestStatus,
318 const S3ErrorDetails *s3ErrorDetails,
321 DeleteBucketData *dbData = (DeleteBucketData *) callbackData;
323 (*(dbData->responseCompleteCallback))
324 (requestStatus, s3ErrorDetails, dbData->callbackData);
330 void S3_delete_bucket(S3Protocol protocol, S3UriStyle uriStyle,
331 const char *accessKeyId, const char *secretAccessKey,
332 const char *bucketName,
333 S3RequestContext *requestContext,
334 const S3ResponseHandler *handler, void *callbackData)
336 // Create the callback data
337 DeleteBucketData *dbData =
338 (DeleteBucketData *) malloc(sizeof(DeleteBucketData));
340 (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData);
344 dbData->responsePropertiesCallback = handler->propertiesCallback;
345 dbData->responseCompleteCallback = handler->completeCallback;
346 dbData->callbackData = callbackData;
348 // Set up the RequestParams
349 RequestParams params =
351 HttpRequestTypeDELETE, // httpRequestType
352 { bucketName, // bucketName
353 protocol, // protocol
354 uriStyle, // uriStyle
355 accessKeyId, // accessKeyId
356 secretAccessKey }, // secretAccessKey
360 0, // copySourceBucketName
366 &deleteBucketPropertiesCallback, // propertiesCallback
368 0, // toS3CallbackTotalSize
370 &deleteBucketCompleteCallback, // completeCallback
371 dbData // callbackData
374 // Perform the request
375 request_perform(¶ms, requestContext);
379 // list bucket ----------------------------------------------------------------
381 typedef struct ListBucketContents
383 string_buffer(key, 1024);
384 string_buffer(lastModified, 256);
385 string_buffer(eTag, 256);
386 string_buffer(size, 24);
387 string_buffer(ownerId, 256);
388 string_buffer(ownerDisplayName, 256);
389 } ListBucketContents;
392 static void initialize_list_bucket_contents(ListBucketContents *contents)
394 string_buffer_initialize(contents->key);
395 string_buffer_initialize(contents->lastModified);
396 string_buffer_initialize(contents->eTag);
397 string_buffer_initialize(contents->size);
398 string_buffer_initialize(contents->ownerId);
399 string_buffer_initialize(contents->ownerDisplayName);
402 // We read up to 32 Contents at a time
403 #define MAX_CONTENTS 32
404 // We read up to 8 CommonPrefixes at a time
405 #define MAX_COMMON_PREFIXES 8
407 typedef struct ListBucketData
411 S3ResponsePropertiesCallback *responsePropertiesCallback;
412 S3ListBucketCallback *listBucketCallback;
413 S3ResponseCompleteCallback *responseCompleteCallback;
416 string_buffer(isTruncated, 64);
417 string_buffer(nextMarker, 1024);
420 ListBucketContents contents[MAX_CONTENTS];
422 int commonPrefixesCount;
423 char commonPrefixes[MAX_COMMON_PREFIXES][1024];
424 int commonPrefixLens[MAX_COMMON_PREFIXES];
428 static void initialize_list_bucket_data(ListBucketData *lbData)
430 lbData->contentsCount = 0;
431 initialize_list_bucket_contents(lbData->contents);
432 lbData->commonPrefixesCount = 0;
433 lbData->commonPrefixes[0][0] = 0;
434 lbData->commonPrefixLens[0] = 0;
438 static S3Status make_list_bucket_callback(ListBucketData *lbData)
442 // Convert IsTruncated
443 int isTruncated = (!strcmp(lbData->isTruncated, "true") ||
444 !strcmp(lbData->isTruncated, "1")) ? 1 : 0;
446 // Convert the contents
447 S3ListBucketContent contents[lbData->contentsCount];
449 int contentsCount = lbData->contentsCount;
450 for (i = 0; i < contentsCount; i++) {
451 S3ListBucketContent *contentDest = &(contents[i]);
452 ListBucketContents *contentSrc = &(lbData->contents[i]);
453 contentDest->key = contentSrc->key;
454 contentDest->lastModified =
455 parseIso8601Time(contentSrc->lastModified);
456 contentDest->eTag = contentSrc->eTag;
457 contentDest->size = parseUnsignedInt(contentSrc->size);
458 contentDest->ownerId =
459 contentSrc->ownerId[0] ?contentSrc->ownerId : 0;
460 contentDest->ownerDisplayName = (contentSrc->ownerDisplayName[0] ?
461 contentSrc->ownerDisplayName : 0);
464 // Make the common prefixes array
465 int commonPrefixesCount = lbData->commonPrefixesCount;
466 char *commonPrefixes[commonPrefixesCount];
467 for (i = 0; i < commonPrefixesCount; i++) {
468 commonPrefixes[i] = lbData->commonPrefixes[i];
471 return (*(lbData->listBucketCallback))
472 (isTruncated, lbData->nextMarker,
473 contentsCount, contents, commonPrefixesCount,
474 (const char **) commonPrefixes, lbData->callbackData);
478 static S3Status listBucketXmlCallback(const char *elementPath,
479 const char *data, int dataLen,
482 ListBucketData *lbData = (ListBucketData *) callbackData;
487 if (!strcmp(elementPath, "ListBucketResult/IsTruncated")) {
488 string_buffer_append(lbData->isTruncated, data, dataLen, fit);
490 else if (!strcmp(elementPath, "ListBucketResult/NextMarker")) {
491 string_buffer_append(lbData->nextMarker, data, dataLen, fit);
493 else if (!strcmp(elementPath, "ListBucketResult/Contents/Key")) {
494 ListBucketContents *contents =
495 &(lbData->contents[lbData->contentsCount]);
496 string_buffer_append(contents->key, data, dataLen, fit);
498 else if (!strcmp(elementPath,
499 "ListBucketResult/Contents/LastModified")) {
500 ListBucketContents *contents =
501 &(lbData->contents[lbData->contentsCount]);
502 string_buffer_append(contents->lastModified, data, dataLen, fit);
504 else if (!strcmp(elementPath, "ListBucketResult/Contents/ETag")) {
505 ListBucketContents *contents =
506 &(lbData->contents[lbData->contentsCount]);
507 string_buffer_append(contents->eTag, data, dataLen, fit);
509 else if (!strcmp(elementPath, "ListBucketResult/Contents/Size")) {
510 ListBucketContents *contents =
511 &(lbData->contents[lbData->contentsCount]);
512 string_buffer_append(contents->size, data, dataLen, fit);
514 else if (!strcmp(elementPath, "ListBucketResult/Contents/Owner/ID")) {
515 ListBucketContents *contents =
516 &(lbData->contents[lbData->contentsCount]);
517 string_buffer_append(contents->ownerId, data, dataLen, fit);
519 else if (!strcmp(elementPath,
520 "ListBucketResult/Contents/Owner/DisplayName")) {
521 ListBucketContents *contents =
522 &(lbData->contents[lbData->contentsCount]);
524 (contents->ownerDisplayName, data, dataLen, fit);
526 else if (!strcmp(elementPath,
527 "ListBucketResult/CommonPrefixes/Prefix")) {
528 int which = lbData->commonPrefixesCount;
529 lbData->commonPrefixLens[which] +=
530 snprintf(lbData->commonPrefixes[which],
531 sizeof(lbData->commonPrefixes[which]) -
532 lbData->commonPrefixLens[which] - 1,
533 "%.*s", dataLen, data);
534 if (lbData->commonPrefixLens[which] >=
535 (int) sizeof(lbData->commonPrefixes[which])) {
536 return S3StatusXmlParseFailure;
541 if (!strcmp(elementPath, "ListBucketResult/Contents")) {
542 // Finished a Contents
543 lbData->contentsCount++;
544 if (lbData->contentsCount == MAX_CONTENTS) {
546 S3Status status = make_list_bucket_callback(lbData);
547 if (status != S3StatusOK) {
550 initialize_list_bucket_data(lbData);
553 // Initialize the next one
554 initialize_list_bucket_contents
555 (&(lbData->contents[lbData->contentsCount]));
558 else if (!strcmp(elementPath,
559 "ListBucketResult/CommonPrefixes/Prefix")) {
561 lbData->commonPrefixesCount++;
562 if (lbData->commonPrefixesCount == MAX_COMMON_PREFIXES) {
564 S3Status status = make_list_bucket_callback(lbData);
565 if (status != S3StatusOK) {
568 initialize_list_bucket_data(lbData);
571 // Initialize the next one
572 lbData->commonPrefixes[lbData->commonPrefixesCount][0] = 0;
573 lbData->commonPrefixLens[lbData->commonPrefixesCount] = 0;
582 static S3Status listBucketPropertiesCallback
583 (const S3ResponseProperties *responseProperties, void *callbackData)
585 ListBucketData *lbData = (ListBucketData *) callbackData;
587 return (*(lbData->responsePropertiesCallback))
588 (responseProperties, lbData->callbackData);
592 static S3Status listBucketDataCallback(int bufferSize, const char *buffer,
595 ListBucketData *lbData = (ListBucketData *) callbackData;
597 return simplexml_add(&(lbData->simpleXml), buffer, bufferSize);
601 static void listBucketCompleteCallback(S3Status requestStatus,
602 const S3ErrorDetails *s3ErrorDetails,
605 ListBucketData *lbData = (ListBucketData *) callbackData;
607 // Make the callback if there is anything
608 if (lbData->contentsCount || lbData->commonPrefixesCount) {
609 make_list_bucket_callback(lbData);
612 (*(lbData->responseCompleteCallback))
613 (requestStatus, s3ErrorDetails, lbData->callbackData);
615 simplexml_deinitialize(&(lbData->simpleXml));
621 void S3_list_bucket(const S3BucketContext *bucketContext, const char *prefix,
622 const char *marker, const char *delimiter, int maxkeys,
623 S3RequestContext *requestContext,
624 const S3ListBucketHandler *handler, void *callbackData)
626 // Compose the query params
627 string_buffer(queryParams, 4096);
628 string_buffer_initialize(queryParams);
630 #define safe_append(name, value) \
634 string_buffer_append(queryParams, "&", 1, fit); \
636 (*(handler->responseHandler.completeCallback)) \
637 (S3StatusQueryParamsTooLong, 0, callbackData); \
641 string_buffer_append(queryParams, name "=", \
642 sizeof(name "=") - 1, fit); \
644 (*(handler->responseHandler.completeCallback)) \
645 (S3StatusQueryParamsTooLong, 0, callbackData); \
649 char encoded[3 * 1024]; \
650 if (!urlEncode(encoded, value, 1024)) { \
651 (*(handler->responseHandler.completeCallback)) \
652 (S3StatusQueryParamsTooLong, 0, callbackData); \
655 string_buffer_append(queryParams, encoded, strlen(encoded), \
658 (*(handler->responseHandler.completeCallback)) \
659 (S3StatusQueryParamsTooLong, 0, callbackData); \
667 safe_append("prefix", prefix);
670 safe_append("marker", marker);
673 safe_append("delimiter", delimiter);
676 char maxKeysString[64];
677 snprintf(maxKeysString, sizeof(maxKeysString), "%d", maxkeys);
678 safe_append("max-keys", maxKeysString);
681 ListBucketData *lbData =
682 (ListBucketData *) malloc(sizeof(ListBucketData));
685 (*(handler->responseHandler.completeCallback))
686 (S3StatusOutOfMemory, 0, callbackData);
690 simplexml_initialize(&(lbData->simpleXml), &listBucketXmlCallback, lbData);
692 lbData->responsePropertiesCallback =
693 handler->responseHandler.propertiesCallback;
694 lbData->listBucketCallback = handler->listBucketCallback;
695 lbData->responseCompleteCallback =
696 handler->responseHandler.completeCallback;
697 lbData->callbackData = callbackData;
699 string_buffer_initialize(lbData->isTruncated);
700 string_buffer_initialize(lbData->nextMarker);
701 initialize_list_bucket_data(lbData);
703 // Set up the RequestParams
704 RequestParams params =
706 HttpRequestTypeGET, // httpRequestType
707 { bucketContext->bucketName, // bucketName
708 bucketContext->protocol, // protocol
709 bucketContext->uriStyle, // uriStyle
710 bucketContext->accessKeyId, // accessKeyId
711 bucketContext->secretAccessKey }, // secretAccessKey
713 queryParams[0] ? queryParams : 0, // queryParams
715 0, // copySourceBucketName
721 &listBucketPropertiesCallback, // propertiesCallback
723 0, // toS3CallbackTotalSize
724 &listBucketDataCallback, // fromS3Callback
725 &listBucketCompleteCallback, // completeCallback
726 lbData // callbackData
729 // Perform the request
730 request_perform(¶ms, requestContext);