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 ************************************************************************** **/
28 * This is a 'driver' program that simply converts command-line input into
29 * calls to libs3 functions, and prints the results.
39 #include <sys/types.h>
45 #ifndef FOPEN_EXTRA_FLAGS
46 #define FOPEN_EXTRA_FLAGS ""
49 // Also needed for Windows, because somehow MinGW doesn't define this
50 extern int putenv(char *);
53 // Command-line options, saved as globals ------------------------------------
55 static int forceG = 0;
56 static int showResponsePropertiesG = 0;
57 static S3Protocol protocolG = S3ProtocolHTTPS;
58 static S3UriStyle uriStyleG = S3UriStylePath;
59 static int retriesG = 5;
62 // Environment variables, saved as globals ----------------------------------
64 static const char *accessKeyIdG = 0;
65 static const char *secretAccessKeyG = 0;
68 // Request results, saved as globals -----------------------------------------
70 static int statusG = 0;
71 static char errorDetailsG[4096] = { 0 };
74 // Other globals -------------------------------------------------------------
76 static char putenvBufG[256];
79 // Option prefixes -----------------------------------------------------------
81 #define LOCATION_PREFIX "location="
82 #define LOCATION_PREFIX_LEN (sizeof(LOCATION_PREFIX) - 1)
83 #define CANNED_ACL_PREFIX "cannedAcl="
84 #define CANNED_ACL_PREFIX_LEN (sizeof(CANNED_ACL_PREFIX) - 1)
85 #define PREFIX_PREFIX "prefix="
86 #define PREFIX_PREFIX_LEN (sizeof(PREFIX_PREFIX) - 1)
87 #define MARKER_PREFIX "marker="
88 #define MARKER_PREFIX_LEN (sizeof(MARKER_PREFIX) - 1)
89 #define DELIMITER_PREFIX "delimiter="
90 #define DELIMITER_PREFIX_LEN (sizeof(DELIMITER_PREFIX) - 1)
91 #define MAXKEYS_PREFIX "maxkeys="
92 #define MAXKEYS_PREFIX_LEN (sizeof(MAXKEYS_PREFIX) - 1)
93 #define FILENAME_PREFIX "filename="
94 #define FILENAME_PREFIX_LEN (sizeof(FILENAME_PREFIX) - 1)
95 #define CONTENT_LENGTH_PREFIX "contentLength="
96 #define CONTENT_LENGTH_PREFIX_LEN (sizeof(CONTENT_LENGTH_PREFIX) - 1)
97 #define CACHE_CONTROL_PREFIX "cacheControl="
98 #define CACHE_CONTROL_PREFIX_LEN (sizeof(CACHE_CONTROL_PREFIX) - 1)
99 #define CONTENT_TYPE_PREFIX "contentType="
100 #define CONTENT_TYPE_PREFIX_LEN (sizeof(CONTENT_TYPE_PREFIX) - 1)
101 #define MD5_PREFIX "md5="
102 #define MD5_PREFIX_LEN (sizeof(MD5_PREFIX) - 1)
103 #define CONTENT_DISPOSITION_FILENAME_PREFIX "contentDispositionFilename="
104 #define CONTENT_DISPOSITION_FILENAME_PREFIX_LEN \
105 (sizeof(CONTENT_DISPOSITION_FILENAME_PREFIX) - 1)
106 #define CONTENT_ENCODING_PREFIX "contentEncoding="
107 #define CONTENT_ENCODING_PREFIX_LEN (sizeof(CONTENT_ENCODING_PREFIX) - 1)
108 #define EXPIRES_PREFIX "expires="
109 #define EXPIRES_PREFIX_LEN (sizeof(EXPIRES_PREFIX) - 1)
110 #define X_AMZ_META_PREFIX "x-amz-meta-"
111 #define X_AMZ_META_PREFIX_LEN (sizeof(X_AMZ_META_PREFIX) - 1)
112 #define IF_MODIFIED_SINCE_PREFIX "ifModifiedSince="
113 #define IF_MODIFIED_SINCE_PREFIX_LEN (sizeof(IF_MODIFIED_SINCE_PREFIX) - 1)
114 #define IF_NOT_MODIFIED_SINCE_PREFIX "ifNotmodifiedSince="
115 #define IF_NOT_MODIFIED_SINCE_PREFIX_LEN \
116 (sizeof(IF_NOT_MODIFIED_SINCE_PREFIX) - 1)
117 #define IF_MATCH_PREFIX "ifMatch="
118 #define IF_MATCH_PREFIX_LEN (sizeof(IF_MATCH_PREFIX) - 1)
119 #define IF_NOT_MATCH_PREFIX "ifNotMatch="
120 #define IF_NOT_MATCH_PREFIX_LEN (sizeof(IF_NOT_MATCH_PREFIX) - 1)
121 #define START_BYTE_PREFIX "startByte="
122 #define START_BYTE_PREFIX_LEN (sizeof(START_BYTE_PREFIX) - 1)
123 #define BYTE_COUNT_PREFIX "byteCount="
124 #define BYTE_COUNT_PREFIX_LEN (sizeof(BYTE_COUNT_PREFIX) - 1)
125 #define ALL_DETAILS_PREFIX "allDetails="
126 #define ALL_DETAILS_PREFIX_LEN (sizeof(ALL_DETAILS_PREFIX) - 1)
127 #define NO_STATUS_PREFIX "noStatus="
128 #define NO_STATUS_PREFIX_LEN (sizeof(NO_STATUS_PREFIX) - 1)
129 #define RESOURCE_PREFIX "resource="
130 #define RESOURCE_PREFIX_LEN (sizeof(RESOURCE_PREFIX) - 1)
131 #define TARGET_BUCKET_PREFIX "targetBucket="
132 #define TARGET_BUCKET_PREFIX_LEN (sizeof(TARGET_BUCKET_PREFIX) - 1)
133 #define TARGET_PREFIX_PREFIX "targetPrefix="
134 #define TARGET_PREFIX_PREFIX_LEN (sizeof(TARGET_PREFIX_PREFIX) - 1)
137 // util ----------------------------------------------------------------------
139 static void S3_init()
142 if ((status = S3_initialize("s3", S3_INIT_ALL))
144 fprintf(stderr, "Failed to initialize libs3: %s\n",
145 S3_get_status_name(status));
151 static void printError()
153 if (statusG < S3StatusErrorAccessDenied) {
154 fprintf(stderr, "\nERROR: %s\n", S3_get_status_name(statusG));
157 fprintf(stderr, "\nERROR: %s\n", S3_get_status_name(statusG));
158 fprintf(stderr, "%s\n", errorDetailsG);
163 static void usageExit(FILE *out)
170 " -f/--force : force operation despite warnings\n"
171 " -h/--vhost-style : use virtual-host-style URIs (default is "
173 " -u/--unencrypted : unencrypted (use HTTP instead of HTTPS)\n"
174 " -s/--show-properties : show response properties on stdout\n"
175 " -r/--retries : retry retryable failures this number of times\n"
180 " S3_ACCESS_KEY_ID : S3 access key ID (required)\n"
181 " S3_SECRET_ACCESS_KEY : S3 secret access key (required)\n"
183 " Commands (with <required parameters> and [optional parameters]) :\n"
185 " (NOTE: all command parameters take a value and are specified using the\n"
186 " pattern parameter=value)\n"
188 " help : Prints this help text\n"
190 " list : Lists owned buckets\n"
191 " [allDetails] : Show full details\n"
193 " test : Tests a bucket for existence and accessibility\n"
194 " <bucket> : Bucket to test\n"
196 " create : Create a new bucket\n"
197 " <bucket> : Bucket to create\n"
198 " [cannedAcl] : Canned ACL for the bucket (see Canned ACLs)\n"
199 " [location] : Location for bucket (for example, EU)\n"
201 " delete : Delete a bucket or key\n"
202 " <bucket>[/<key>] : Bucket or bucket/key to delete\n"
204 " list : List bucket contents\n"
205 " <bucket> : Bucket to list\n"
206 " [prefix] : Prefix for results set\n"
207 " [marker] : Where in results set to start listing\n"
208 " [delimiter] : Delimiter for rolling up results set\n"
209 " [maxkeys] : Maximum number of keys to return in results set\n"
210 " [allDetails] : Show full details for each key\n"
212 " getacl : Get the ACL of a bucket or key\n"
213 " <bucket>[/<key>] : Bucket or bucket/key to get the ACL of\n"
214 " [filename] : Output filename for ACL (default is stdout)\n"
216 " setacl : Set the ACL of a bucket or key\n"
217 " <bucket>[/<key>] : Bucket or bucket/key to set the ACL of\n"
218 " [filename] : Input filename for ACL (default is stdin)\n"
220 " getlogging : Get the logging status of a bucket\n"
221 " <bucket> : Bucket to get the logging status of\n"
222 " [filename] : Output filename for ACL (default is stdout)\n"
224 " setlogging : Set the logging status of a bucket\n"
225 " <bucket> : Bucket to set the logging status of\n"
226 " [targetBucket] : Target bucket to log to; if not present, disables\n"
228 " [targetPrefix] : Key prefix to use for logs\n"
229 " [filename] : Input filename for ACL (default is stdin)\n"
231 " put : Puts an object\n"
232 " <bucket>/<key> : Bucket/key to put object to\n"
233 " [filename] : Filename to read source data from "
234 "(default is stdin)\n"
235 " [contentLength] : How many bytes of source data to put (required if\n"
236 " source file is stdin)\n"
237 " [cacheControl] : Cache-Control HTTP header string to associate with\n"
239 " [contentType] : Content-Type HTTP header string to associate with\n"
241 " [md5] : MD5 for validating source data\n"
242 " [contentDispositionFilename] : Content-Disposition filename string to\n"
243 " associate with object\n"
244 " [contentEncoding] : Content-Encoding HTTP header string to associate\n"
246 " [expires] : Expiration date to associate with object\n"
247 " [cannedAcl] : Canned ACL for the object (see Canned ACLs)\n"
248 " [x-amz-meta-...]] : Metadata headers to associate with the object\n"
250 " copy : Copies an object; if any options are set, the "
252 " metadata of the object is replaced\n"
253 " <sourcebucket>/<sourcekey> : Source bucket/key\n"
254 " <destbucket>/<destkey> : Destination bucket/key\n"
255 " [cacheControl] : Cache-Control HTTP header string to associate with\n"
257 " [contentType] : Content-Type HTTP header string to associate with\n"
259 " [contentDispositionFilename] : Content-Disposition filename string to\n"
260 " associate with object\n"
261 " [contentEncoding] : Content-Encoding HTTP header string to associate\n"
263 " [expires] : Expiration date to associate with object\n"
264 " [cannedAcl] : Canned ACL for the object (see Canned ACLs)\n"
265 " [x-amz-meta-...]] : Metadata headers to associate with the object\n"
267 " get : Gets an object\n"
268 " <buckey>/<key> : Bucket/key of object to get\n"
269 " [filename] : Filename to write object data to (required if -s\n"
270 " command line parameter was used)\n"
271 " [ifModifiedSince] : Only return the object if it has been modified "
274 " [ifNotmodifiedSince] : Only return the object if it has not been "
277 " [ifMatch] : Only return the object if its ETag header matches\n"
279 " [ifNotMatch] : Only return the object if its ETag header does "
281 " match this string\n"
282 " [startByte] : First byte of byte range to return\n"
283 " [byteCount] : Number of bytes of byte range to return\n"
285 " head : Gets only the headers of an object, implies -s\n"
286 " <bucket>/<key> : Bucket/key of object to get headers of\n"
288 " gqs : Generates an authenticated query string\n"
289 " <bucket>[/<key>] : Bucket or bucket/key to generate query string for\n"
290 " [expires] : Expiration date for query string\n"
291 " [resource] : Sub-resource of key for query string, without a\n"
292 " leading '?', for example, \"torrent\"\n"
296 " The following canned ACLs are supported:\n"
297 " private (default), public-read, public-read-write, authenticated-read\n"
301 " For the getacl and setacl commands, the format of the ACL list is:\n"
302 " 1) An initial line giving the owner id in this format:\n"
303 " OwnerID <Owner ID> <Owner Display Name>\n"
304 " 2) Optional header lines, giving column headers, starting with the\n"
305 " word \"Type\", or with some number of dashes\n"
306 " 3) Grant lines, of the form:\n"
307 " <Grant Type> (whitespace) <Grantee> (whitespace) <Permission>\n"
308 " where Grant Type is one of: Email, UserID, or Group, and\n"
309 " Grantee is the identification of the grantee based on this type,\n"
310 " and Permission is one of: READ, WRITE, READ_ACP, or FULL_CONTROL.\n"
312 " Note that the easiest way to modify an ACL is to first get it, saving it\n"
313 " into a file, then modifying the file, and then setting the modified file\n"
314 " back as the new ACL for the bucket/object.\n"
318 " The format for dates used in parameters is as ISO 8601 dates, i.e.\n"
319 " YYYY-MM-DDTHH:MM:SS[.s...][T/+-dd:dd]. Examples:\n"
320 " 2008-07-29T20:36:14.0023T\n"
321 " 2008-07-29T20:36:14.0023+06:00\n"
322 " 2008-07-29T20:36:14.0023-10:00\n"
329 static uint64_t convertInt(const char *str, const char *paramName)
334 if (!isdigit(*str)) {
335 fprintf(stderr, "\nERROR: Nondigit in %s parameter: %c\n",
340 ret += (*str++ - '0');
347 typedef struct growbuffer
349 // The total number of bytes, and the start byte
354 char data[64 * 1024];
355 struct growbuffer *prev, *next;
359 // returns nonzero on success, zero on out of memory
360 static int growbuffer_append(growbuffer **gb, const char *data, int dataLen)
363 growbuffer *buf = *gb ? (*gb)->prev : 0;
364 if (!buf || (buf->size == sizeof(buf->data))) {
365 buf = (growbuffer *) malloc(sizeof(growbuffer));
372 buf->prev = (*gb)->prev;
374 (*gb)->prev->next = buf;
378 buf->prev = buf->next = buf;
383 int toCopy = (sizeof(buf->data) - buf->size);
384 if (toCopy > dataLen) {
388 memcpy(&(buf->data[buf->size]), data, toCopy);
390 buf->size += toCopy, data += toCopy, dataLen -= toCopy;
397 static void growbuffer_read(growbuffer **gb, int amt, int *amtReturn,
402 growbuffer *buf = *gb;
408 *amtReturn = (buf->size > amt) ? amt : buf->size;
410 memcpy(buffer, &(buf->data[buf->start]), *amtReturn);
412 buf->start += *amtReturn, buf->size -= *amtReturn;
414 if (buf->size == 0) {
415 if (buf->next == buf) {
426 static void growbuffer_destroy(growbuffer *gb)
428 growbuffer *start = gb;
431 growbuffer *next = gb->next;
433 gb = (next == start) ? 0 : next;
438 // Convenience utility for making the code look nicer. Tests a string
439 // against a format; only the characters specified in the format are
440 // checked (i.e. if the string is longer than the format, the string still
441 // checks out ok). Format characters are:
443 // anything else - is that character
444 // Returns nonzero the string checks out, zero if it does not.
445 static int checkString(const char *str, const char *format)
448 if (*format == 'd') {
449 if (!isdigit(*str)) {
453 else if (*str != *format) {
463 static int64_t parseIso8601Time(const char *str)
465 // Check to make sure that it has a valid format
466 if (!checkString(str, "dddd-dd-ddTdd:dd:dd")) {
470 #define nextnum() (((*str - '0') * 10) + (*(str + 1) - '0'))
474 memset(&stm, 0, sizeof(stm));
476 stm.tm_year = (nextnum() - 19) * 100;
478 stm.tm_year += nextnum();
481 stm.tm_mon = nextnum() - 1;
484 stm.tm_mday = nextnum();
487 stm.tm_hour = nextnum();
490 stm.tm_min = nextnum();
493 stm.tm_sec = nextnum();
498 // This is hokey but it's the recommended way ...
499 char *tz = getenv("TZ");
500 snprintf(putenvBufG, sizeof(putenvBufG), "TZ=UTC");
503 int64_t ret = mktime(&stm);
505 snprintf(putenvBufG, sizeof(putenvBufG), "TZ=%s", tz ? tz : "");
512 while (isdigit(*str)) {
517 if (checkString(str, "-dd:dd") || checkString(str, "+dd:dd")) {
518 int sign = (*str++ == '-') ? -1 : 1;
519 int hours = nextnum();
521 int minutes = nextnum();
522 ret += (-sign * (((hours * 60) + minutes) * 60));
524 // Else it should be Z to be a conformant time string, but we just assume
525 // that it is rather than enforcing that
531 // Simple ACL format: Lines of this format:
533 // Starting with a dash - ignored
534 // Email email_address permission
535 // UserID user_id (display_name) permission
536 // Group Authenticated AWS Users permission
537 // Group All Users permission
538 // permission is one of READ, WRITE, READ_ACP, WRITE_ACP, FULL_CONTROL
539 static int convert_simple_acl(char *aclXml, char *ownerId,
540 char *ownerDisplayName,
541 int *aclGrantCountReturn,
542 S3AclGrant *aclGrants)
544 *aclGrantCountReturn = 0;
546 *ownerDisplayName = 0;
548 #define SKIP_SPACE(require_more) \
550 while (isspace(*aclXml)) { \
553 if (require_more && !*aclXml) { \
558 #define COPY_STRING_MAXLEN(field, maxlen) \
562 while ((len < maxlen) && !isspace(*aclXml)) { \
563 field[len++] = *aclXml++; \
568 #define COPY_STRING(field) \
569 COPY_STRING_MAXLEN(field, (int) (sizeof(field) - 1))
578 // Skip Type lines and dash lines
579 if (!strncmp(aclXml, "Type", sizeof("Type") - 1) ||
581 while (*aclXml && ((*aclXml != '\n') && (*aclXml != '\r'))) {
587 if (!strncmp(aclXml, "OwnerID", sizeof("OwnerID") - 1)) {
588 aclXml += sizeof("OwnerID") - 1;
589 COPY_STRING_MAXLEN(ownerId, S3_MAX_GRANTEE_USER_ID_SIZE);
591 COPY_STRING_MAXLEN(ownerDisplayName,
592 S3_MAX_GRANTEE_DISPLAY_NAME_SIZE);
596 if (*aclGrantCountReturn == S3_MAX_ACL_GRANT_COUNT) {
600 S3AclGrant *grant = &(aclGrants[(*aclGrantCountReturn)++]);
602 if (!strncmp(aclXml, "Email", sizeof("Email") - 1)) {
603 grant->granteeType = S3GranteeTypeAmazonCustomerByEmail;
604 aclXml += sizeof("Email") - 1;
605 COPY_STRING(grant->grantee.amazonCustomerByEmail.emailAddress);
607 else if (!strncmp(aclXml, "UserID", sizeof("UserID") - 1)) {
608 grant->granteeType = S3GranteeTypeCanonicalUser;
609 aclXml += sizeof("UserID") - 1;
610 COPY_STRING(grant->grantee.canonicalUser.id);
612 // Now do display name
613 COPY_STRING(grant->grantee.canonicalUser.displayName);
615 else if (!strncmp(aclXml, "Group", sizeof("Group") - 1)) {
616 aclXml += sizeof("Group") - 1;
618 if (!strncmp(aclXml, "Authenticated AWS Users",
619 sizeof("Authenticated AWS Users") - 1)) {
620 grant->granteeType = S3GranteeTypeAllAwsUsers;
621 aclXml += (sizeof("Authenticated AWS Users") - 1);
623 else if (!strncmp(aclXml, "All Users", sizeof("All Users") - 1)) {
624 grant->granteeType = S3GranteeTypeAllUsers;
625 aclXml += (sizeof("All Users") - 1);
627 else if (!strncmp(aclXml, "Log Delivery",
628 sizeof("Log Delivery") - 1)) {
629 grant->granteeType = S3GranteeTypeLogDelivery;
630 aclXml += (sizeof("Log Delivery") - 1);
642 if (!strncmp(aclXml, "READ_ACP", sizeof("READ_ACP") - 1)) {
643 grant->permission = S3PermissionReadACP;
644 aclXml += (sizeof("READ_ACP") - 1);
646 else if (!strncmp(aclXml, "READ", sizeof("READ") - 1)) {
647 grant->permission = S3PermissionRead;
648 aclXml += (sizeof("READ") - 1);
650 else if (!strncmp(aclXml, "WRITE_ACP", sizeof("WRITE_ACP") - 1)) {
651 grant->permission = S3PermissionWriteACP;
652 aclXml += (sizeof("WRITE_ACP") - 1);
654 else if (!strncmp(aclXml, "WRITE", sizeof("WRITE") - 1)) {
655 grant->permission = S3PermissionWrite;
656 aclXml += (sizeof("WRITE") - 1);
658 else if (!strncmp(aclXml, "FULL_CONTROL",
659 sizeof("FULL_CONTROL") - 1)) {
660 grant->permission = S3PermissionFullControl;
661 aclXml += (sizeof("FULL_CONTROL") - 1);
669 static int should_retry()
672 // Sleep before next retry; start out with a 1 second sleep
673 static int retrySleepInterval = 1;
674 sleep(retrySleepInterval);
675 // Next sleep 1 second longer
676 retrySleepInterval++;
684 static struct option longOptionsG[] =
686 { "force", no_argument, 0, 'f' },
687 { "vhost-style", no_argument, 0, 'h' },
688 { "unencrypted", no_argument, 0, 'u' },
689 { "show-properties", no_argument, 0, 's' },
690 { "retries", required_argument, 0, 'r' },
695 // response properties callback ----------------------------------------------
697 // This callback does the same thing for every request type: prints out the
698 // properties if the user has requested them to be so
699 static S3Status responsePropertiesCallback
700 (const S3ResponseProperties *properties, void *callbackData)
704 if (!showResponsePropertiesG) {
708 #define print_nonnull(name, field) \
710 if (properties-> field) { \
711 printf("%s: %s\n", name, properties-> field); \
715 print_nonnull("Content-Type", contentType);
716 print_nonnull("Request-Id", requestId);
717 print_nonnull("Request-Id-2", requestId2);
718 if (properties->contentLength > 0) {
719 printf("Content-Length: %lld\n",
720 (unsigned long long) properties->contentLength);
722 print_nonnull("Server", server);
723 print_nonnull("ETag", eTag);
724 if (properties->lastModified > 0) {
726 time_t t = (time_t) properties->lastModified;
727 // gmtime is not thread-safe but we don't care here.
728 strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&t));
729 printf("Last-Modified: %s\n", timebuf);
732 for (i = 0; i < properties->metaDataCount; i++) {
733 printf("x-amz-meta-%s: %s\n", properties->metaData[i].name,
734 properties->metaData[i].value);
741 // response complete callback ------------------------------------------------
743 // This callback does the same thing for every request type: saves the status
744 // and error stuff in global variables
745 static void responseCompleteCallback(S3Status status,
746 const S3ErrorDetails *error,
752 // Compose the error details message now, although we might not use it.
753 // Can't just save a pointer to [error] since it's not guaranteed to last
754 // beyond this callback
756 if (error && error->message) {
757 len += snprintf(&(errorDetailsG[len]), sizeof(errorDetailsG) - len,
758 " Message: %s\n", error->message);
760 if (error && error->resource) {
761 len += snprintf(&(errorDetailsG[len]), sizeof(errorDetailsG) - len,
762 " Resource: %s\n", error->resource);
764 if (error && error->furtherDetails) {
765 len += snprintf(&(errorDetailsG[len]), sizeof(errorDetailsG) - len,
766 " Further Details: %s\n", error->furtherDetails);
768 if (error && error->extraDetailsCount) {
769 len += snprintf(&(errorDetailsG[len]), sizeof(errorDetailsG) - len,
770 "%s", " Extra Details:\n");
772 for (i = 0; i < error->extraDetailsCount; i++) {
773 len += snprintf(&(errorDetailsG[len]),
774 sizeof(errorDetailsG) - len, " %s: %s\n",
775 error->extraDetails[i].name,
776 error->extraDetails[i].value);
782 // list service --------------------------------------------------------------
784 typedef struct list_service_data
791 static void printListServiceHeader(int allDetails)
793 printf("%-56s %-20s", " Bucket",
796 printf(" %-64s %-12s",
801 printf("-------------------------------------------------------- "
802 "--------------------");
804 printf(" -------------------------------------------------"
805 "--------------- ------------");
811 static S3Status listServiceCallback(const char *ownerId,
812 const char *ownerDisplayName,
813 const char *bucketName,
814 int64_t creationDate, void *callbackData)
816 list_service_data *data = (list_service_data *) callbackData;
818 if (!data->headerPrinted) {
819 data->headerPrinted = 1;
820 printListServiceHeader(data->allDetails);
824 if (creationDate >= 0) {
825 time_t t = (time_t) creationDate;
826 strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&t));
832 printf("%-56s %-20s", bucketName, timebuf);
833 if (data->allDetails) {
834 printf(" %-64s %-12s", ownerId ? ownerId : "",
835 ownerDisplayName ? ownerDisplayName : "");
843 static void list_service(int allDetails)
845 list_service_data data;
847 data.headerPrinted = 0;
848 data.allDetails = allDetails;
852 S3ListServiceHandler listServiceHandler =
854 { &responsePropertiesCallback, &responseCompleteCallback },
859 S3_list_service(protocolG, accessKeyIdG, secretAccessKeyG, 0,
860 &listServiceHandler, &data);
861 } while (S3_status_is_retryable(statusG) && should_retry());
863 if (statusG == S3StatusOK) {
864 if (!data.headerPrinted) {
865 printListServiceHeader(allDetails);
876 // test bucket ---------------------------------------------------------------
878 static void test_bucket(int argc, char **argv, int optindex)
881 if (optindex == argc) {
882 fprintf(stderr, "\nERROR: Missing parameter: bucket\n");
886 const char *bucketName = argv[optindex++];
888 if (optindex != argc) {
889 fprintf(stderr, "\nERROR: Extraneous parameter: %s\n", argv[optindex]);
895 S3ResponseHandler responseHandler =
897 &responsePropertiesCallback, &responseCompleteCallback
900 char locationConstraint[64];
902 S3_test_bucket(protocolG, uriStyleG, accessKeyIdG, secretAccessKeyG,
903 bucketName, sizeof(locationConstraint),
904 locationConstraint, 0, &responseHandler, 0);
905 } while (S3_status_is_retryable(statusG) && should_retry());
912 result = locationConstraint[0] ? locationConstraint : "USA";
914 case S3StatusErrorNoSuchBucket:
915 result = "Does Not Exist";
917 case S3StatusErrorAccessDenied:
918 result = "Access Denied";
926 printf("%-56s %-20s\n", " Bucket",
928 printf("-------------------------------------------------------- "
929 "--------------------\n");
930 printf("%-56s %-20s\n", bucketName, result);
940 // create bucket -------------------------------------------------------------
942 static void create_bucket(int argc, char **argv, int optindex)
944 if (optindex == argc) {
945 fprintf(stderr, "\nERROR: Missing parameter: bucket\n");
949 const char *bucketName = argv[optindex++];
951 if (!forceG && (S3_validate_bucket_name
952 (bucketName, S3UriStyleVirtualHost) != S3StatusOK)) {
953 fprintf(stderr, "\nWARNING: Bucket name is not valid for "
954 "virtual-host style URI access.\n");
955 fprintf(stderr, "Bucket not created. Use -f option to force the "
956 "bucket to be created despite\n");
957 fprintf(stderr, "this warning.\n\n");
961 const char *locationConstraint = 0;
962 S3CannedAcl cannedAcl = S3CannedAclPrivate;
963 while (optindex < argc) {
964 char *param = argv[optindex++];
965 if (!strncmp(param, LOCATION_PREFIX, LOCATION_PREFIX_LEN)) {
966 locationConstraint = &(param[LOCATION_PREFIX_LEN]);
968 else if (!strncmp(param, CANNED_ACL_PREFIX, CANNED_ACL_PREFIX_LEN)) {
969 char *val = &(param[CANNED_ACL_PREFIX_LEN]);
970 if (!strcmp(val, "private")) {
971 cannedAcl = S3CannedAclPrivate;
973 else if (!strcmp(val, "public-read")) {
974 cannedAcl = S3CannedAclPublicRead;
976 else if (!strcmp(val, "public-read-write")) {
977 cannedAcl = S3CannedAclPublicReadWrite;
979 else if (!strcmp(val, "authenticated-read")) {
980 cannedAcl = S3CannedAclAuthenticatedRead;
983 fprintf(stderr, "\nERROR: Unknown canned ACL: %s\n", val);
988 fprintf(stderr, "\nERROR: Unknown param: %s\n", param);
995 S3ResponseHandler responseHandler =
997 &responsePropertiesCallback, &responseCompleteCallback
1001 S3_create_bucket(protocolG, accessKeyIdG, secretAccessKeyG,
1002 bucketName, cannedAcl, locationConstraint, 0,
1003 &responseHandler, 0);
1004 } while (S3_status_is_retryable(statusG) && should_retry());
1006 if (statusG == S3StatusOK) {
1007 printf("Bucket successfully created.\n");
1017 // delete bucket -------------------------------------------------------------
1019 static void delete_bucket(int argc, char **argv, int optindex)
1021 if (optindex == argc) {
1022 fprintf(stderr, "\nERROR: Missing parameter: bucket\n");
1026 const char *bucketName = argv[optindex++];
1028 if (optindex != argc) {
1029 fprintf(stderr, "\nERROR: Extraneous parameter: %s\n", argv[optindex]);
1035 S3ResponseHandler responseHandler =
1037 &responsePropertiesCallback, &responseCompleteCallback
1041 S3_delete_bucket(protocolG, uriStyleG, accessKeyIdG, secretAccessKeyG,
1042 bucketName, 0, &responseHandler, 0);
1043 } while (S3_status_is_retryable(statusG) && should_retry());
1045 if (statusG != S3StatusOK) {
1053 // list bucket ---------------------------------------------------------------
1055 typedef struct list_bucket_callback_data
1058 char nextMarker[1024];
1061 } list_bucket_callback_data;
1064 static void printListBucketHeader(int allDetails)
1066 printf("%-50s %-20s %-5s",
1068 " Last Modified", "Size");
1070 printf(" %-34s %-64s %-12s",
1076 printf("-------------------------------------------------- "
1077 "-------------------- -----");
1079 printf(" ---------------------------------- "
1080 "-------------------------------------------------"
1081 "--------------- ------------");
1087 static S3Status listBucketCallback(int isTruncated, const char *nextMarker,
1089 const S3ListBucketContent *contents,
1090 int commonPrefixesCount,
1091 const char **commonPrefixes,
1094 list_bucket_callback_data *data =
1095 (list_bucket_callback_data *) callbackData;
1097 data->isTruncated = isTruncated;
1098 // This is tricky. S3 doesn't return the NextMarker if there is no
1099 // delimiter. Why, I don't know, since it's still useful for paging
1100 // through results. We want NextMarker to be the last content in the
1101 // list, so set it to that if necessary.
1102 if ((!nextMarker || !nextMarker[0]) && contentsCount) {
1103 nextMarker = contents[contentsCount - 1].key;
1106 snprintf(data->nextMarker, sizeof(data->nextMarker), "%s",
1110 data->nextMarker[0] = 0;
1113 if (contentsCount && !data->keyCount) {
1114 printListBucketHeader(data->allDetails);
1118 for (i = 0; i < contentsCount; i++) {
1119 const S3ListBucketContent *content = &(contents[i]);
1122 time_t t = (time_t) content->lastModified;
1123 strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ",
1125 printf("\nKey: %s\n", content->key);
1126 printf("Last Modified: %s\n", timebuf);
1127 printf("ETag: %s\n", content->eTag);
1128 printf("Size: %llu\n", (unsigned long long) content->size);
1129 if (content->ownerId) {
1130 printf("Owner ID: %s\n", content->ownerId);
1132 if (content->ownerDisplayName) {
1133 printf("Owner Display Name: %s\n", content->ownerDisplayName);
1137 time_t t = (time_t) content->lastModified;
1138 strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ",
1141 if (content->size < 100000) {
1142 sprintf(sizebuf, "%5llu", (unsigned long long) content->size);
1144 else if (content->size < (1024 * 1024)) {
1145 sprintf(sizebuf, "%4lluK",
1146 ((unsigned long long) content->size) / 1024ULL);
1148 else if (content->size < (10 * 1024 * 1024)) {
1149 float f = content->size;
1151 sprintf(sizebuf, "%1.2fM", f);
1153 else if (content->size < (1024 * 1024 * 1024)) {
1154 sprintf(sizebuf, "%4lluM",
1155 ((unsigned long long) content->size) /
1156 (1024ULL * 1024ULL));
1159 float f = (content->size / 1024);
1161 sprintf(sizebuf, "%1.2fG", f);
1163 printf("%-50s %s %s", content->key, timebuf, sizebuf);
1164 if (data->allDetails) {
1165 printf(" %-34s %-64s %-12s",
1167 content->ownerId ? content->ownerId : "",
1168 content->ownerDisplayName ?
1169 content->ownerDisplayName : "");
1175 data->keyCount += contentsCount;
1177 for (i = 0; i < commonPrefixesCount; i++) {
1178 printf("\nCommon Prefix: %s\n", commonPrefixes[i]);
1185 static void list_bucket(const char *bucketName, const char *prefix,
1186 const char *marker, const char *delimiter,
1187 int maxkeys, int allDetails)
1191 S3BucketContext bucketContext =
1200 S3ListBucketHandler listBucketHandler =
1202 { &responsePropertiesCallback, &responseCompleteCallback },
1206 list_bucket_callback_data data;
1208 snprintf(data.nextMarker, sizeof(data.nextMarker), "%s", marker);
1210 data.allDetails = allDetails;
1213 data.isTruncated = 0;
1215 S3_list_bucket(&bucketContext, prefix, data.nextMarker,
1216 delimiter, maxkeys, 0, &listBucketHandler, &data);
1217 } while (S3_status_is_retryable(statusG) && should_retry());
1218 if (statusG != S3StatusOK) {
1221 marker = data.nextMarker;
1222 } while (data.isTruncated && (!maxkeys || (data.keyCount < maxkeys)));
1224 if (statusG == S3StatusOK) {
1225 if (!data.keyCount) {
1226 printListBucketHeader(allDetails);
1237 static void list(int argc, char **argv, int optindex)
1239 if (optindex == argc) {
1244 const char *bucketName = 0;
1246 const char *prefix = 0, *marker = 0, *delimiter = 0;
1247 int maxkeys = 0, allDetails = 0;
1248 while (optindex < argc) {
1249 char *param = argv[optindex++];
1250 if (!strncmp(param, PREFIX_PREFIX, PREFIX_PREFIX_LEN)) {
1251 prefix = &(param[PREFIX_PREFIX_LEN]);
1253 else if (!strncmp(param, MARKER_PREFIX, MARKER_PREFIX_LEN)) {
1254 marker = &(param[MARKER_PREFIX_LEN]);
1256 else if (!strncmp(param, DELIMITER_PREFIX, DELIMITER_PREFIX_LEN)) {
1257 delimiter = &(param[DELIMITER_PREFIX_LEN]);
1259 else if (!strncmp(param, MAXKEYS_PREFIX, MAXKEYS_PREFIX_LEN)) {
1260 maxkeys = convertInt(&(param[MAXKEYS_PREFIX_LEN]), "maxkeys");
1262 else if (!strncmp(param, ALL_DETAILS_PREFIX,
1263 ALL_DETAILS_PREFIX_LEN)) {
1264 const char *ad = &(param[ALL_DETAILS_PREFIX_LEN]);
1265 if (!strcmp(ad, "true") || !strcmp(ad, "TRUE") ||
1266 !strcmp(ad, "yes") || !strcmp(ad, "YES") ||
1271 else if (!bucketName) {
1275 fprintf(stderr, "\nERROR: Unknown param: %s\n", param);
1281 list_bucket(bucketName, prefix, marker, delimiter, maxkeys,
1285 list_service(allDetails);
1291 // delete object -------------------------------------------------------------
1293 static void delete_object(int argc, char **argv, int optindex)
1298 char *slash = argv[optindex];
1300 // We know there is a slash in there, put_object is only called if so
1301 while (*slash && (*slash != '/')) {
1306 const char *bucketName = argv[optindex++];
1307 const char *key = slash;
1311 S3BucketContext bucketContext =
1320 S3ResponseHandler responseHandler =
1323 &responseCompleteCallback
1327 S3_delete_object(&bucketContext, key, 0, &responseHandler, 0);
1328 } while (S3_status_is_retryable(statusG) && should_retry());
1330 if ((statusG != S3StatusOK) &&
1331 (statusG != S3StatusErrorPreconditionFailed)) {
1339 // put object ----------------------------------------------------------------
1341 typedef struct put_object_callback_data
1345 uint64_t contentLength, originalContentLength;
1347 } put_object_callback_data;
1350 static int putObjectDataCallback(int bufferSize, char *buffer,
1353 put_object_callback_data *data =
1354 (put_object_callback_data *) callbackData;
1358 if (data->contentLength) {
1359 int toRead = ((data->contentLength > (unsigned) bufferSize) ?
1360 (unsigned) bufferSize : data->contentLength);
1362 growbuffer_read(&(data->gb), toRead, &ret, buffer);
1364 else if (data->infile) {
1365 ret = fread(buffer, 1, toRead, data->infile);
1369 data->contentLength -= ret;
1371 if (data->contentLength && !data->noStatus) {
1372 // Avoid a weird bug in MingW, which won't print the second integer
1373 // value properly when it's in the same call, so print separately
1374 printf("%llu bytes remaining ",
1375 (unsigned long long) data->contentLength);
1376 printf("(%d%% complete) ...\n",
1377 (int) (((data->originalContentLength -
1378 data->contentLength) * 100) /
1379 data->originalContentLength));
1386 static void put_object(int argc, char **argv, int optindex)
1388 if (optindex == argc) {
1389 fprintf(stderr, "\nERROR: Missing parameter: bucket/key\n");
1394 char *slash = argv[optindex];
1395 while (*slash && (*slash != '/')) {
1398 if (!*slash || !*(slash + 1)) {
1399 fprintf(stderr, "\nERROR: Invalid bucket/key name: %s\n",
1405 const char *bucketName = argv[optindex++];
1406 const char *key = slash;
1408 const char *filename = 0;
1409 uint64_t contentLength = 0;
1410 const char *cacheControl = 0, *contentType = 0, *md5 = 0;
1411 const char *contentDispositionFilename = 0, *contentEncoding = 0;
1412 int64_t expires = -1;
1413 S3CannedAcl cannedAcl = S3CannedAclPrivate;
1414 int metaPropertiesCount = 0;
1415 S3NameValue metaProperties[S3_MAX_METADATA_COUNT];
1418 while (optindex < argc) {
1419 char *param = argv[optindex++];
1420 if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) {
1421 filename = &(param[FILENAME_PREFIX_LEN]);
1423 else if (!strncmp(param, CONTENT_LENGTH_PREFIX,
1424 CONTENT_LENGTH_PREFIX_LEN)) {
1425 contentLength = convertInt(&(param[CONTENT_LENGTH_PREFIX_LEN]),
1427 if (contentLength > (5LL * 1024 * 1024 * 1024)) {
1428 fprintf(stderr, "\nERROR: contentLength must be no greater "
1433 else if (!strncmp(param, CACHE_CONTROL_PREFIX,
1434 CACHE_CONTROL_PREFIX_LEN)) {
1435 cacheControl = &(param[CACHE_CONTROL_PREFIX_LEN]);
1437 else if (!strncmp(param, CONTENT_TYPE_PREFIX,
1438 CONTENT_TYPE_PREFIX_LEN)) {
1439 contentType = &(param[CONTENT_TYPE_PREFIX_LEN]);
1441 else if (!strncmp(param, MD5_PREFIX, MD5_PREFIX_LEN)) {
1442 md5 = &(param[MD5_PREFIX_LEN]);
1444 else if (!strncmp(param, CONTENT_DISPOSITION_FILENAME_PREFIX,
1445 CONTENT_DISPOSITION_FILENAME_PREFIX_LEN)) {
1446 contentDispositionFilename =
1447 &(param[CONTENT_DISPOSITION_FILENAME_PREFIX_LEN]);
1449 else if (!strncmp(param, CONTENT_ENCODING_PREFIX,
1450 CONTENT_ENCODING_PREFIX_LEN)) {
1451 contentEncoding = &(param[CONTENT_ENCODING_PREFIX_LEN]);
1453 else if (!strncmp(param, EXPIRES_PREFIX, EXPIRES_PREFIX_LEN)) {
1454 expires = parseIso8601Time(&(param[EXPIRES_PREFIX_LEN]));
1456 fprintf(stderr, "\nERROR: Invalid expires time "
1457 "value; ISO 8601 time format required\n");
1461 else if (!strncmp(param, X_AMZ_META_PREFIX, X_AMZ_META_PREFIX_LEN)) {
1462 if (metaPropertiesCount == S3_MAX_METADATA_COUNT) {
1463 fprintf(stderr, "\nERROR: Too many x-amz-meta- properties, "
1465 (unsigned long) S3_MAX_METADATA_COUNT, param);
1468 char *name = &(param[X_AMZ_META_PREFIX_LEN]);
1470 while (*value && (*value != '=')) {
1473 if (!*value || !*(value + 1)) {
1474 fprintf(stderr, "\nERROR: Invalid parameter: %s\n", param);
1478 metaProperties[metaPropertiesCount].name = name;
1479 metaProperties[metaPropertiesCount++].value = value;
1481 else if (!strncmp(param, CANNED_ACL_PREFIX, CANNED_ACL_PREFIX_LEN)) {
1482 char *val = &(param[CANNED_ACL_PREFIX_LEN]);
1483 if (!strcmp(val, "private")) {
1484 cannedAcl = S3CannedAclPrivate;
1486 else if (!strcmp(val, "public-read")) {
1487 cannedAcl = S3CannedAclPublicRead;
1489 else if (!strcmp(val, "public-read-write")) {
1490 cannedAcl = S3CannedAclPublicReadWrite;
1492 else if (!strcmp(val, "authenticated-read")) {
1493 cannedAcl = S3CannedAclAuthenticatedRead;
1496 fprintf(stderr, "\nERROR: Unknown canned ACL: %s\n", val);
1500 else if (!strncmp(param, NO_STATUS_PREFIX, NO_STATUS_PREFIX_LEN)) {
1501 const char *ns = &(param[NO_STATUS_PREFIX_LEN]);
1502 if (!strcmp(ns, "true") || !strcmp(ns, "TRUE") ||
1503 !strcmp(ns, "yes") || !strcmp(ns, "YES") ||
1509 fprintf(stderr, "\nERROR: Unknown param: %s\n", param);
1514 put_object_callback_data data;
1518 data.noStatus = noStatus;
1521 if (!contentLength) {
1522 struct stat statbuf;
1523 // Stat the file to get its length
1524 if (stat(filename, &statbuf) == -1) {
1525 fprintf(stderr, "\nERROR: Failed to stat file %s: ",
1530 contentLength = statbuf.st_size;
1533 if (!(data.infile = fopen(filename, "r" FOPEN_EXTRA_FLAGS))) {
1534 fprintf(stderr, "\nERROR: Failed to open input file %s: ",
1541 // Read from stdin. If contentLength is not provided, we have
1542 // to read it all in to get contentLength.
1543 if (!contentLength) {
1544 // Read all if stdin to get the data
1545 char buffer[64 * 1024];
1547 int amtRead = fread(buffer, 1, sizeof(buffer), stdin);
1551 if (!growbuffer_append(&(data.gb), buffer, amtRead)) {
1552 fprintf(stderr, "\nERROR: Out of memory while reading "
1556 contentLength += amtRead;
1557 if (amtRead < (int) sizeof(buffer)) {
1563 data.infile = stdin;
1567 data.contentLength = data.originalContentLength = contentLength;
1571 S3BucketContext bucketContext =
1580 S3PutProperties putProperties =
1585 contentDispositionFilename,
1589 metaPropertiesCount,
1593 S3PutObjectHandler putObjectHandler =
1595 { &responsePropertiesCallback, &responseCompleteCallback },
1596 &putObjectDataCallback
1600 S3_put_object(&bucketContext, key, contentLength, &putProperties, 0,
1601 &putObjectHandler, &data);
1602 } while (S3_status_is_retryable(statusG) && should_retry());
1605 fclose(data.infile);
1608 growbuffer_destroy(data.gb);
1611 if (statusG != S3StatusOK) {
1614 else if (data.contentLength) {
1615 fprintf(stderr, "\nERROR: Failed to read remaining %llu bytes from "
1616 "input\n", (unsigned long long) data.contentLength);
1623 // copy object ---------------------------------------------------------------
1625 static void copy_object(int argc, char **argv, int optindex)
1627 if (optindex == argc) {
1628 fprintf(stderr, "\nERROR: Missing parameter: source bucket/key\n");
1633 char *slash = argv[optindex];
1634 while (*slash && (*slash != '/')) {
1637 if (!*slash || !*(slash + 1)) {
1638 fprintf(stderr, "\nERROR: Invalid source bucket/key name: %s\n",
1644 const char *sourceBucketName = argv[optindex++];
1645 const char *sourceKey = slash;
1647 if (optindex == argc) {
1648 fprintf(stderr, "\nERROR: Missing parameter: "
1649 "destination bucket/key\n");
1654 slash = argv[optindex];
1655 while (*slash && (*slash != '/')) {
1658 if (!*slash || !*(slash + 1)) {
1659 fprintf(stderr, "\nERROR: Invalid destination bucket/key name: %s\n",
1665 const char *destinationBucketName = argv[optindex++];
1666 const char *destinationKey = slash;
1668 const char *cacheControl = 0, *contentType = 0;
1669 const char *contentDispositionFilename = 0, *contentEncoding = 0;
1670 int64_t expires = -1;
1671 S3CannedAcl cannedAcl = S3CannedAclPrivate;
1672 int metaPropertiesCount = 0;
1673 S3NameValue metaProperties[S3_MAX_METADATA_COUNT];
1674 int anyPropertiesSet = 0;
1676 while (optindex < argc) {
1677 char *param = argv[optindex++];
1678 if (!strncmp(param, CACHE_CONTROL_PREFIX,
1679 CACHE_CONTROL_PREFIX_LEN)) {
1680 cacheControl = &(param[CACHE_CONTROL_PREFIX_LEN]);
1681 anyPropertiesSet = 1;
1683 else if (!strncmp(param, CONTENT_TYPE_PREFIX,
1684 CONTENT_TYPE_PREFIX_LEN)) {
1685 contentType = &(param[CONTENT_TYPE_PREFIX_LEN]);
1686 anyPropertiesSet = 1;
1688 else if (!strncmp(param, CONTENT_DISPOSITION_FILENAME_PREFIX,
1689 CONTENT_DISPOSITION_FILENAME_PREFIX_LEN)) {
1690 contentDispositionFilename =
1691 &(param[CONTENT_DISPOSITION_FILENAME_PREFIX_LEN]);
1692 anyPropertiesSet = 1;
1694 else if (!strncmp(param, CONTENT_ENCODING_PREFIX,
1695 CONTENT_ENCODING_PREFIX_LEN)) {
1696 contentEncoding = &(param[CONTENT_ENCODING_PREFIX_LEN]);
1697 anyPropertiesSet = 1;
1699 else if (!strncmp(param, EXPIRES_PREFIX, EXPIRES_PREFIX_LEN)) {
1700 expires = parseIso8601Time(&(param[EXPIRES_PREFIX_LEN]));
1702 fprintf(stderr, "\nERROR: Invalid expires time "
1703 "value; ISO 8601 time format required\n");
1706 anyPropertiesSet = 1;
1708 else if (!strncmp(param, X_AMZ_META_PREFIX, X_AMZ_META_PREFIX_LEN)) {
1709 if (metaPropertiesCount == S3_MAX_METADATA_COUNT) {
1710 fprintf(stderr, "\nERROR: Too many x-amz-meta- properties, "
1712 (unsigned long) S3_MAX_METADATA_COUNT, param);
1715 char *name = &(param[X_AMZ_META_PREFIX_LEN]);
1717 while (*value && (*value != '=')) {
1720 if (!*value || !*(value + 1)) {
1721 fprintf(stderr, "\nERROR: Invalid parameter: %s\n", param);
1725 metaProperties[metaPropertiesCount].name = name;
1726 metaProperties[metaPropertiesCount++].value = value;
1727 anyPropertiesSet = 1;
1729 else if (!strncmp(param, CANNED_ACL_PREFIX, CANNED_ACL_PREFIX_LEN)) {
1730 char *val = &(param[CANNED_ACL_PREFIX_LEN]);
1731 if (!strcmp(val, "private")) {
1732 cannedAcl = S3CannedAclPrivate;
1734 else if (!strcmp(val, "public-read")) {
1735 cannedAcl = S3CannedAclPublicRead;
1737 else if (!strcmp(val, "public-read-write")) {
1738 cannedAcl = S3CannedAclPublicReadWrite;
1740 else if (!strcmp(val, "authenticated-read")) {
1741 cannedAcl = S3CannedAclAuthenticatedRead;
1744 fprintf(stderr, "\nERROR: Unknown canned ACL: %s\n", val);
1747 anyPropertiesSet = 1;
1750 fprintf(stderr, "\nERROR: Unknown param: %s\n", param);
1757 S3BucketContext bucketContext =
1766 S3PutProperties putProperties =
1771 contentDispositionFilename,
1775 metaPropertiesCount,
1779 S3ResponseHandler responseHandler =
1781 &responsePropertiesCallback,
1782 &responseCompleteCallback
1785 int64_t lastModified;
1789 S3_copy_object(&bucketContext, sourceKey, destinationBucketName,
1790 destinationKey, anyPropertiesSet ? &putProperties : 0,
1791 &lastModified, sizeof(eTag), eTag, 0,
1792 &responseHandler, 0);
1793 } while (S3_status_is_retryable(statusG) && should_retry());
1795 if (statusG == S3StatusOK) {
1796 if (lastModified >= 0) {
1798 time_t t = (time_t) lastModified;
1799 strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ",
1801 printf("Last-Modified: %s\n", timebuf);
1804 printf("ETag: %s\n", eTag);
1815 // get object ----------------------------------------------------------------
1817 static S3Status getObjectDataCallback(int bufferSize, const char *buffer,
1820 FILE *outfile = (FILE *) callbackData;
1822 size_t wrote = fwrite(buffer, 1, bufferSize, outfile);
1824 return ((wrote < (size_t) bufferSize) ?
1825 S3StatusAbortedByCallback : S3StatusOK);
1829 static void get_object(int argc, char **argv, int optindex)
1831 if (optindex == argc) {
1832 fprintf(stderr, "\nERROR: Missing parameter: bucket/key\n");
1837 char *slash = argv[optindex];
1838 while (*slash && (*slash != '/')) {
1841 if (!*slash || !*(slash + 1)) {
1842 fprintf(stderr, "\nERROR: Invalid bucket/key name: %s\n",
1848 const char *bucketName = argv[optindex++];
1849 const char *key = slash;
1851 const char *filename = 0;
1852 int64_t ifModifiedSince = -1, ifNotModifiedSince = -1;
1853 const char *ifMatch = 0, *ifNotMatch = 0;
1854 uint64_t startByte = 0, byteCount = 0;
1856 while (optindex < argc) {
1857 char *param = argv[optindex++];
1858 if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) {
1859 filename = &(param[FILENAME_PREFIX_LEN]);
1861 else if (!strncmp(param, IF_MODIFIED_SINCE_PREFIX,
1862 IF_MODIFIED_SINCE_PREFIX_LEN)) {
1863 // Parse ifModifiedSince
1864 ifModifiedSince = parseIso8601Time
1865 (&(param[IF_MODIFIED_SINCE_PREFIX_LEN]));
1866 if (ifModifiedSince < 0) {
1867 fprintf(stderr, "\nERROR: Invalid ifModifiedSince time "
1868 "value; ISO 8601 time format required\n");
1872 else if (!strncmp(param, IF_NOT_MODIFIED_SINCE_PREFIX,
1873 IF_NOT_MODIFIED_SINCE_PREFIX_LEN)) {
1874 // Parse ifModifiedSince
1875 ifNotModifiedSince = parseIso8601Time
1876 (&(param[IF_NOT_MODIFIED_SINCE_PREFIX_LEN]));
1877 if (ifNotModifiedSince < 0) {
1878 fprintf(stderr, "\nERROR: Invalid ifNotModifiedSince time "
1879 "value; ISO 8601 time format required\n");
1883 else if (!strncmp(param, IF_MATCH_PREFIX, IF_MATCH_PREFIX_LEN)) {
1884 ifMatch = &(param[IF_MATCH_PREFIX_LEN]);
1886 else if (!strncmp(param, IF_NOT_MATCH_PREFIX,
1887 IF_NOT_MATCH_PREFIX_LEN)) {
1888 ifNotMatch = &(param[IF_NOT_MATCH_PREFIX_LEN]);
1890 else if (!strncmp(param, START_BYTE_PREFIX, START_BYTE_PREFIX_LEN)) {
1891 startByte = convertInt
1892 (&(param[START_BYTE_PREFIX_LEN]), "startByte");
1894 else if (!strncmp(param, BYTE_COUNT_PREFIX, BYTE_COUNT_PREFIX_LEN)) {
1895 byteCount = convertInt
1896 (&(param[BYTE_COUNT_PREFIX_LEN]), "byteCount");
1899 fprintf(stderr, "\nERROR: Unknown param: %s\n", param);
1907 // Stat the file, and if it doesn't exist, open it in w mode
1909 if (stat(filename, &buf) == -1) {
1910 outfile = fopen(filename, "w" FOPEN_EXTRA_FLAGS);
1913 // Open in r+ so that we don't truncate the file, just in case
1914 // there is an error and we write no bytes, we leave the file
1916 outfile = fopen(filename, "r+" FOPEN_EXTRA_FLAGS);
1920 fprintf(stderr, "\nERROR: Failed to open output file %s: ",
1926 else if (showResponsePropertiesG) {
1927 fprintf(stderr, "\nERROR: get -s requires a filename parameter\n");
1936 S3BucketContext bucketContext =
1945 S3GetConditions getConditions =
1953 S3GetObjectHandler getObjectHandler =
1955 { &responsePropertiesCallback, &responseCompleteCallback },
1956 &getObjectDataCallback
1960 S3_get_object(&bucketContext, key, &getConditions, startByte,
1961 byteCount, 0, &getObjectHandler, outfile);
1962 } while (S3_status_is_retryable(statusG) && should_retry());
1964 if (statusG != S3StatusOK) {
1974 // head object ---------------------------------------------------------------
1976 static void head_object(int argc, char **argv, int optindex)
1978 if (optindex == argc) {
1979 fprintf(stderr, "\nERROR: Missing parameter: bucket/key\n");
1983 // Head implies showing response properties
1984 showResponsePropertiesG = 1;
1987 char *slash = argv[optindex];
1989 while (*slash && (*slash != '/')) {
1992 if (!*slash || !*(slash + 1)) {
1993 fprintf(stderr, "\nERROR: Invalid bucket/key name: %s\n",
1999 const char *bucketName = argv[optindex++];
2000 const char *key = slash;
2002 if (optindex != argc) {
2003 fprintf(stderr, "\nERROR: Extraneous parameter: %s\n", argv[optindex]);
2009 S3BucketContext bucketContext =
2018 S3ResponseHandler responseHandler =
2020 &responsePropertiesCallback,
2021 &responseCompleteCallback
2025 S3_head_object(&bucketContext, key, 0, &responseHandler, 0);
2026 } while (S3_status_is_retryable(statusG) && should_retry());
2028 if ((statusG != S3StatusOK) &&
2029 (statusG != S3StatusErrorPreconditionFailed)) {
2037 // generate query string ------------------------------------------------------
2039 static void generate_query_string(int argc, char **argv, int optindex)
2041 if (optindex == argc) {
2042 fprintf(stderr, "\nERROR: Missing parameter: bucket[/key]\n");
2046 const char *bucketName = argv[optindex];
2047 const char *key = 0;
2050 char *slash = argv[optindex++];
2051 while (*slash && (*slash != '/')) {
2062 int64_t expires = -1;
2064 const char *resource = 0;
2066 while (optindex < argc) {
2067 char *param = argv[optindex++];
2068 if (!strncmp(param, EXPIRES_PREFIX, EXPIRES_PREFIX_LEN)) {
2069 expires = parseIso8601Time(&(param[EXPIRES_PREFIX_LEN]));
2071 fprintf(stderr, "\nERROR: Invalid expires time "
2072 "value; ISO 8601 time format required\n");
2076 else if (!strncmp(param, RESOURCE_PREFIX, RESOURCE_PREFIX_LEN)) {
2077 resource = &(param[RESOURCE_PREFIX_LEN]);
2080 fprintf(stderr, "\nERROR: Unknown param: %s\n", param);
2087 S3BucketContext bucketContext =
2096 char buffer[S3_MAX_AUTHENTICATED_QUERY_STRING_SIZE];
2098 S3Status status = S3_generate_authenticated_query_string
2099 (buffer, &bucketContext, key, expires, resource);
2101 if (status != S3StatusOK) {
2102 printf("Failed to generate authenticated query string: %s\n",
2103 S3_get_status_name(status));
2106 printf("%s\n", buffer);
2113 // get acl -------------------------------------------------------------------
2115 void get_acl(int argc, char **argv, int optindex)
2117 if (optindex == argc) {
2118 fprintf(stderr, "\nERROR: Missing parameter: bucket[/key]\n");
2122 const char *bucketName = argv[optindex];
2123 const char *key = 0;
2126 char *slash = argv[optindex++];
2127 while (*slash && (*slash != '/')) {
2138 const char *filename = 0;
2140 while (optindex < argc) {
2141 char *param = argv[optindex++];
2142 if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) {
2143 filename = &(param[FILENAME_PREFIX_LEN]);
2146 fprintf(stderr, "\nERROR: Unknown param: %s\n", param);
2154 // Stat the file, and if it doesn't exist, open it in w mode
2156 if (stat(filename, &buf) == -1) {
2157 outfile = fopen(filename, "w" FOPEN_EXTRA_FLAGS);
2160 // Open in r+ so that we don't truncate the file, just in case
2161 // there is an error and we write no bytes, we leave the file
2163 outfile = fopen(filename, "r+" FOPEN_EXTRA_FLAGS);
2167 fprintf(stderr, "\nERROR: Failed to open output file %s: ",
2173 else if (showResponsePropertiesG) {
2174 fprintf(stderr, "\nERROR: getacl -s requires a filename parameter\n");
2182 S3AclGrant aclGrants[S3_MAX_ACL_GRANT_COUNT];
2183 char ownerId[S3_MAX_GRANTEE_USER_ID_SIZE];
2184 char ownerDisplayName[S3_MAX_GRANTEE_DISPLAY_NAME_SIZE];
2188 S3BucketContext bucketContext =
2197 S3ResponseHandler responseHandler =
2199 &responsePropertiesCallback,
2200 &responseCompleteCallback
2204 S3_get_acl(&bucketContext, key, ownerId, ownerDisplayName,
2205 &aclGrantCount, aclGrants, 0, &responseHandler, 0);
2206 } while (S3_status_is_retryable(statusG) && should_retry());
2208 if (statusG == S3StatusOK) {
2209 fprintf(outfile, "OwnerID %s %s\n", ownerId, ownerDisplayName);
2210 fprintf(outfile, "%-6s %-90s %-12s\n", " Type",
2213 fprintf(outfile, "------ "
2214 "------------------------------------------------------------"
2215 "------------------------------ ------------\n");
2217 for (i = 0; i < aclGrantCount; i++) {
2218 S3AclGrant *grant = &(aclGrants[i]);
2220 char composedId[S3_MAX_GRANTEE_USER_ID_SIZE +
2221 S3_MAX_GRANTEE_DISPLAY_NAME_SIZE + 16];
2224 switch (grant->granteeType) {
2225 case S3GranteeTypeAmazonCustomerByEmail:
2227 id = grant->grantee.amazonCustomerByEmail.emailAddress;
2229 case S3GranteeTypeCanonicalUser:
2231 snprintf(composedId, sizeof(composedId),
2232 "%s (%s)", grant->grantee.canonicalUser.id,
2233 grant->grantee.canonicalUser.displayName);
2236 case S3GranteeTypeAllAwsUsers:
2238 id = "Authenticated AWS Users";
2240 case S3GranteeTypeAllUsers:
2246 id = "Log Delivery";
2250 switch (grant->permission) {
2251 case S3PermissionRead:
2254 case S3PermissionWrite:
2257 case S3PermissionReadACP:
2260 case S3PermissionWriteACP:
2264 perm = "FULL_CONTROL";
2267 fprintf(outfile, "%-6s %-90s %-12s\n", type, id, perm);
2280 // set acl -------------------------------------------------------------------
2282 void set_acl(int argc, char **argv, int optindex)
2284 if (optindex == argc) {
2285 fprintf(stderr, "\nERROR: Missing parameter: bucket[/key]\n");
2289 const char *bucketName = argv[optindex];
2290 const char *key = 0;
2293 char *slash = argv[optindex++];
2294 while (*slash && (*slash != '/')) {
2305 const char *filename = 0;
2307 while (optindex < argc) {
2308 char *param = argv[optindex++];
2309 if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) {
2310 filename = &(param[FILENAME_PREFIX_LEN]);
2313 fprintf(stderr, "\nERROR: Unknown param: %s\n", param);
2321 if (!(infile = fopen(filename, "r" FOPEN_EXTRA_FLAGS))) {
2322 fprintf(stderr, "\nERROR: Failed to open input file %s: ",
2332 // Read in the complete ACL
2334 aclBuf[fread(aclBuf, 1, sizeof(aclBuf), infile)] = 0;
2335 char ownerId[S3_MAX_GRANTEE_USER_ID_SIZE];
2336 char ownerDisplayName[S3_MAX_GRANTEE_DISPLAY_NAME_SIZE];
2340 S3AclGrant aclGrants[S3_MAX_ACL_GRANT_COUNT];
2341 if (!convert_simple_acl(aclBuf, ownerId, ownerDisplayName,
2342 &aclGrantCount, aclGrants)) {
2343 fprintf(stderr, "\nERROR: Failed to parse ACLs\n");
2350 S3BucketContext bucketContext =
2359 S3ResponseHandler responseHandler =
2361 &responsePropertiesCallback,
2362 &responseCompleteCallback
2366 S3_set_acl(&bucketContext, key, ownerId, ownerDisplayName,
2367 aclGrantCount, aclGrants, 0, &responseHandler, 0);
2368 } while (S3_status_is_retryable(statusG) && should_retry());
2370 if (statusG != S3StatusOK) {
2380 // get logging ----------------------------------------------------------------
2382 void get_logging(int argc, char **argv, int optindex)
2384 if (optindex == argc) {
2385 fprintf(stderr, "\nERROR: Missing parameter: bucket\n");
2389 const char *bucketName = argv[optindex++];
2390 const char *filename = 0;
2392 while (optindex < argc) {
2393 char *param = argv[optindex++];
2394 if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) {
2395 filename = &(param[FILENAME_PREFIX_LEN]);
2398 fprintf(stderr, "\nERROR: Unknown param: %s\n", param);
2406 // Stat the file, and if it doesn't exist, open it in w mode
2408 if (stat(filename, &buf) == -1) {
2409 outfile = fopen(filename, "w" FOPEN_EXTRA_FLAGS);
2412 // Open in r+ so that we don't truncate the file, just in case
2413 // there is an error and we write no bytes, we leave the file
2415 outfile = fopen(filename, "r+" FOPEN_EXTRA_FLAGS);
2419 fprintf(stderr, "\nERROR: Failed to open output file %s: ",
2425 else if (showResponsePropertiesG) {
2426 fprintf(stderr, "\nERROR: getlogging -s requires a filename "
2435 S3AclGrant aclGrants[S3_MAX_ACL_GRANT_COUNT];
2436 char targetBucket[S3_MAX_BUCKET_NAME_SIZE];
2437 char targetPrefix[S3_MAX_KEY_SIZE];
2441 S3BucketContext bucketContext =
2450 S3ResponseHandler responseHandler =
2452 &responsePropertiesCallback,
2453 &responseCompleteCallback
2457 S3_get_server_access_logging(&bucketContext, targetBucket, targetPrefix,
2458 &aclGrantCount, aclGrants, 0,
2459 &responseHandler, 0);
2460 } while (S3_status_is_retryable(statusG) && should_retry());
2462 if (statusG == S3StatusOK) {
2463 if (targetBucket[0]) {
2464 printf("Target Bucket: %s\n", targetBucket);
2465 if (targetPrefix[0]) {
2466 printf("Target Prefix: %s\n", targetPrefix);
2468 fprintf(outfile, "%-6s %-90s %-12s\n", " Type",
2471 fprintf(outfile, "------ "
2472 "---------------------------------------------------------"
2473 "--------------------------------- ------------\n");
2475 for (i = 0; i < aclGrantCount; i++) {
2476 S3AclGrant *grant = &(aclGrants[i]);
2478 char composedId[S3_MAX_GRANTEE_USER_ID_SIZE +
2479 S3_MAX_GRANTEE_DISPLAY_NAME_SIZE + 16];
2482 switch (grant->granteeType) {
2483 case S3GranteeTypeAmazonCustomerByEmail:
2485 id = grant->grantee.amazonCustomerByEmail.emailAddress;
2487 case S3GranteeTypeCanonicalUser:
2489 snprintf(composedId, sizeof(composedId),
2490 "%s (%s)", grant->grantee.canonicalUser.id,
2491 grant->grantee.canonicalUser.displayName);
2494 case S3GranteeTypeAllAwsUsers:
2496 id = "Authenticated AWS Users";
2504 switch (grant->permission) {
2505 case S3PermissionRead:
2508 case S3PermissionWrite:
2511 case S3PermissionReadACP:
2514 case S3PermissionWriteACP:
2518 perm = "FULL_CONTROL";
2521 fprintf(outfile, "%-6s %-90s %-12s\n", type, id, perm);
2525 printf("Service logging is not enabled for this bucket.\n");
2538 // set logging ----------------------------------------------------------------
2540 void set_logging(int argc, char **argv, int optindex)
2542 if (optindex == argc) {
2543 fprintf(stderr, "\nERROR: Missing parameter: bucket\n");
2547 const char *bucketName = argv[optindex++];
2549 const char *targetBucket = 0, *targetPrefix = 0, *filename = 0;
2551 while (optindex < argc) {
2552 char *param = argv[optindex++];
2553 if (!strncmp(param, TARGET_BUCKET_PREFIX, TARGET_BUCKET_PREFIX_LEN)) {
2554 targetBucket = &(param[TARGET_BUCKET_PREFIX_LEN]);
2556 else if (!strncmp(param, TARGET_PREFIX_PREFIX,
2557 TARGET_PREFIX_PREFIX_LEN)) {
2558 targetPrefix = &(param[TARGET_PREFIX_PREFIX_LEN]);
2560 else if (!strncmp(param, FILENAME_PREFIX, FILENAME_PREFIX_LEN)) {
2561 filename = &(param[FILENAME_PREFIX_LEN]);
2564 fprintf(stderr, "\nERROR: Unknown param: %s\n", param);
2569 int aclGrantCount = 0;
2570 S3AclGrant aclGrants[S3_MAX_ACL_GRANT_COUNT];
2576 if (!(infile = fopen(filename, "r" FOPEN_EXTRA_FLAGS))) {
2577 fprintf(stderr, "\nERROR: Failed to open input file %s: ",
2587 // Read in the complete ACL
2589 aclBuf[fread(aclBuf, 1, sizeof(aclBuf), infile)] = 0;
2590 char ownerId[S3_MAX_GRANTEE_USER_ID_SIZE];
2591 char ownerDisplayName[S3_MAX_GRANTEE_DISPLAY_NAME_SIZE];
2594 if (!convert_simple_acl(aclBuf, ownerId, ownerDisplayName,
2595 &aclGrantCount, aclGrants)) {
2596 fprintf(stderr, "\nERROR: Failed to parse ACLs\n");
2606 S3BucketContext bucketContext =
2615 S3ResponseHandler responseHandler =
2617 &responsePropertiesCallback,
2618 &responseCompleteCallback
2622 S3_set_server_access_logging(&bucketContext, targetBucket,
2623 targetPrefix, aclGrantCount, aclGrants,
2624 0, &responseHandler, 0);
2625 } while (S3_status_is_retryable(statusG) && should_retry());
2627 if (statusG != S3StatusOK) {
2635 // main ----------------------------------------------------------------------
2637 int main(int argc, char **argv)
2642 int c = getopt_long(argc, argv, "fhusr:", longOptionsG, &idx);
2654 uriStyleG = S3UriStyleVirtualHost;
2657 protocolG = S3ProtocolHTTP;
2660 showResponsePropertiesG = 1;
2663 const char *v = optarg;
2666 retriesG += *v - '0';
2672 fprintf(stderr, "\nERROR: Unknown option: -%c\n", c);
2678 // The first non-option argument gives the operation to perform
2679 if (optind == argc) {
2680 fprintf(stderr, "\n\nERROR: Missing argument: command\n\n");
2684 const char *command = argv[optind++];
2686 if (!strcmp(command, "help")) {
2687 fprintf(stdout, "\ns3 is a program for performing single requests "
2692 accessKeyIdG = getenv("S3_ACCESS_KEY_ID");
2693 if (!accessKeyIdG) {
2694 fprintf(stderr, "Missing environment variable: S3_ACCESS_KEY_ID\n");
2697 secretAccessKeyG = getenv("S3_SECRET_ACCESS_KEY");
2698 if (!secretAccessKeyG) {
2700 "Missing environment variable: S3_SECRET_ACCESS_KEY\n");
2704 if (!strcmp(command, "list")) {
2705 list(argc, argv, optind);
2707 else if (!strcmp(command, "test")) {
2708 test_bucket(argc, argv, optind);
2710 else if (!strcmp(command, "create")) {
2711 create_bucket(argc, argv, optind);
2713 else if (!strcmp(command, "delete")) {
2714 if (optind == argc) {
2716 "\nERROR: Missing parameter: bucket or bucket/key\n");
2719 char *val = argv[optind];
2722 if (*val++ == '/') {
2728 delete_object(argc, argv, optind);
2731 delete_bucket(argc, argv, optind);
2734 else if (!strcmp(command, "put")) {
2735 put_object(argc, argv, optind);
2737 else if (!strcmp(command, "copy")) {
2738 copy_object(argc, argv, optind);
2740 else if (!strcmp(command, "get")) {
2741 get_object(argc, argv, optind);
2743 else if (!strcmp(command, "head")) {
2744 head_object(argc, argv, optind);
2746 else if (!strcmp(command, "gqs")) {
2747 generate_query_string(argc, argv, optind);
2749 else if (!strcmp(command, "getacl")) {
2750 get_acl(argc, argv, optind);
2752 else if (!strcmp(command, "setacl")) {
2753 set_acl(argc, argv, optind);
2755 else if (!strcmp(command, "getlogging")) {
2756 get_logging(argc, argv, optind);
2758 else if (!strcmp(command, "setlogging")) {
2759 set_logging(argc, argv, optind);
2762 fprintf(stderr, "Unknown command: %s\n", command);