Fix BlueSky build on modern Linux and libs3.
[bluesky.git] / libs3-1.4 / src / general.c
1 /** **************************************************************************
2  * general.c
3  * 
4  * Copyright 2008 Bryan Ischo <bryan@ischo.com>
5  * 
6  * This file is part of libs3.
7  * 
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.
11  *
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.
15  *
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
19  * details.
20  *
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/>.
24  *
25  ************************************************************************** **/
26
27 #include <ctype.h>
28 #include <string.h>
29 #include "request.h"
30 #include "simplexml.h"
31 #include "util.h"
32
33 static int initializeCountG = 0;
34
35 S3Status S3_initialize(const char *userAgentInfo, int flags)
36 {
37     if (initializeCountG++) {
38         return S3StatusOK;
39     }
40
41     return request_api_initialize(userAgentInfo, flags);
42 }
43
44
45 void S3_deinitialize()
46 {
47     if (--initializeCountG) {
48         return;
49     }
50
51     request_api_deinitialize();
52 }
53
54 const char *S3_get_status_name(S3Status status)
55 {
56     switch (status) {
57 #define handlecase(s)                           \
58         case S3Status##s:                       \
59             return #s
60
61         handlecase(OK);
62         handlecase(InternalError);
63         handlecase(OutOfMemory);
64         handlecase(Interrupted);
65         handlecase(InvalidBucketNameTooLong);
66         handlecase(InvalidBucketNameFirstCharacter);
67         handlecase(InvalidBucketNameCharacter);
68         handlecase(InvalidBucketNameCharacterSequence);
69         handlecase(InvalidBucketNameTooShort);
70         handlecase(InvalidBucketNameDotQuadNotation);
71         handlecase(QueryParamsTooLong);
72         handlecase(FailedToInitializeRequest);
73         handlecase(MetaDataHeadersTooLong);
74         handlecase(BadMetaData);
75         handlecase(BadContentType);
76         handlecase(ContentTypeTooLong);
77         handlecase(BadMD5);
78         handlecase(MD5TooLong);
79         handlecase(BadCacheControl);
80         handlecase(CacheControlTooLong);
81         handlecase(BadContentDispositionFilename);
82         handlecase(ContentDispositionFilenameTooLong);
83         handlecase(BadContentEncoding);
84         handlecase(ContentEncodingTooLong);
85         handlecase(BadIfMatchETag);
86         handlecase(IfMatchETagTooLong);
87         handlecase(BadIfNotMatchETag);
88         handlecase(IfNotMatchETagTooLong);
89         handlecase(HeadersTooLong);
90         handlecase(KeyTooLong);
91         handlecase(UriTooLong);
92         handlecase(XmlParseFailure);
93         handlecase(EmailAddressTooLong);
94         handlecase(UserIdTooLong);
95         handlecase(UserDisplayNameTooLong);
96         handlecase(GroupUriTooLong);
97         handlecase(PermissionTooLong);
98         handlecase(TargetBucketTooLong);
99         handlecase(TargetPrefixTooLong);
100         handlecase(TooManyGrants);
101         handlecase(BadGrantee);
102         handlecase(BadPermission);
103         handlecase(XmlDocumentTooLarge);
104         handlecase(NameLookupError);
105         handlecase(FailedToConnect);
106         handlecase(ServerFailedVerification);
107         handlecase(ConnectionFailed);
108         handlecase(AbortedByCallback);
109         handlecase(ErrorAccessDenied);
110         handlecase(ErrorAccountProblem);
111         handlecase(ErrorAmbiguousGrantByEmailAddress);
112         handlecase(ErrorBadDigest);
113         handlecase(ErrorBucketAlreadyExists);
114         handlecase(ErrorBucketAlreadyOwnedByYou);
115         handlecase(ErrorBucketNotEmpty);
116         handlecase(ErrorCredentialsNotSupported);
117         handlecase(ErrorCrossLocationLoggingProhibited);
118         handlecase(ErrorEntityTooSmall);
119         handlecase(ErrorEntityTooLarge);
120         handlecase(ErrorExpiredToken);
121         handlecase(ErrorIncompleteBody);
122         handlecase(ErrorIncorrectNumberOfFilesInPostRequest);
123         handlecase(ErrorInlineDataTooLarge);
124         handlecase(ErrorInternalError);
125         handlecase(ErrorInvalidAccessKeyId);
126         handlecase(ErrorInvalidAddressingHeader);
127         handlecase(ErrorInvalidArgument);
128         handlecase(ErrorInvalidBucketName);
129         handlecase(ErrorInvalidDigest);
130         handlecase(ErrorInvalidLocationConstraint);
131         handlecase(ErrorInvalidPayer);
132         handlecase(ErrorInvalidPolicyDocument);
133         handlecase(ErrorInvalidRange);
134         handlecase(ErrorInvalidSecurity);
135         handlecase(ErrorInvalidSOAPRequest);
136         handlecase(ErrorInvalidStorageClass);
137         handlecase(ErrorInvalidTargetBucketForLogging);
138         handlecase(ErrorInvalidToken);
139         handlecase(ErrorInvalidURI);
140         handlecase(ErrorKeyTooLong);
141         handlecase(ErrorMalformedACLError);
142         handlecase(ErrorMalformedXML);
143         handlecase(ErrorMaxMessageLengthExceeded);
144         handlecase(ErrorMaxPostPreDataLengthExceededError);
145         handlecase(ErrorMetadataTooLarge);
146         handlecase(ErrorMethodNotAllowed);
147         handlecase(ErrorMissingAttachment);
148         handlecase(ErrorMissingContentLength);
149         handlecase(ErrorMissingSecurityElement);
150         handlecase(ErrorMissingSecurityHeader);
151         handlecase(ErrorNoLoggingStatusForKey);
152         handlecase(ErrorNoSuchBucket);
153         handlecase(ErrorNoSuchKey);
154         handlecase(ErrorNotImplemented);
155         handlecase(ErrorNotSignedUp);
156         handlecase(ErrorOperationAborted);
157         handlecase(ErrorPermanentRedirect);
158         handlecase(ErrorPreconditionFailed);
159         handlecase(ErrorRedirect);
160         handlecase(ErrorRequestIsNotMultiPartContent);
161         handlecase(ErrorRequestTimeout);
162         handlecase(ErrorRequestTimeTooSkewed);
163         handlecase(ErrorRequestTorrentOfBucketError);
164         handlecase(ErrorSignatureDoesNotMatch);
165         handlecase(ErrorSlowDown);
166         handlecase(ErrorTemporaryRedirect);
167         handlecase(ErrorTokenRefreshRequired);
168         handlecase(ErrorTooManyBuckets);
169         handlecase(ErrorUnexpectedContent);
170         handlecase(ErrorUnresolvableGrantByEmailAddress);
171         handlecase(ErrorUserKeyMustBeSpecified);
172         handlecase(ErrorUnknown);    
173         handlecase(HttpErrorMovedTemporarily);
174         handlecase(HttpErrorBadRequest);
175         handlecase(HttpErrorForbidden);
176         handlecase(HttpErrorNotFound);
177         handlecase(HttpErrorConflict);
178         handlecase(HttpErrorUnknown);
179     }
180
181     return "Unknown";
182 }
183
184
185 S3Status S3_validate_bucket_name(const char *bucketName, S3UriStyle uriStyle)
186 {
187     int virtualHostStyle = (uriStyle == S3UriStyleVirtualHost);
188     int len = 0, maxlen = virtualHostStyle ? 63 : 255;
189     const char *b = bucketName;
190
191     int hasDot = 0;
192     int hasNonDigit = 0;
193
194     while (*b) {
195         if (len == maxlen) {
196             return S3StatusInvalidBucketNameTooLong;
197         }
198         else if (isalpha(*b)) {
199             len++, b++;
200             hasNonDigit = 1;
201         }
202         else if (isdigit(*b)) {
203             len++, b++;
204         }
205         else if (len == 0) {
206             return S3StatusInvalidBucketNameFirstCharacter;
207         }
208         else if (*b == '_') {
209             /* Virtual host style bucket names cannot have underscores */
210             if (virtualHostStyle) {
211                 return S3StatusInvalidBucketNameCharacter;
212             }
213             len++, b++;
214             hasNonDigit = 1;
215         }
216         else if (*b == '-') {
217             /* Virtual host style bucket names cannot have .- */
218             if (virtualHostStyle && (b > bucketName) && (*(b - 1) == '.')) {
219                 return S3StatusInvalidBucketNameCharacterSequence;
220             }
221             len++, b++;
222             hasNonDigit = 1;
223         }
224         else if (*b == '.') {
225             /* Virtual host style bucket names cannot have -. */
226             if (virtualHostStyle && (b > bucketName) && (*(b - 1) == '-')) {
227                 return S3StatusInvalidBucketNameCharacterSequence;
228             }
229             len++, b++;
230             hasDot = 1;
231         }
232         else {
233             return S3StatusInvalidBucketNameCharacter;
234         }
235     }
236
237     if (len < 3) {
238         return S3StatusInvalidBucketNameTooShort;
239     }
240
241     /* It's not clear from Amazon's documentation exactly what 'IP address
242        style' means.  In its strictest sense, it could mean 'could be a valid
243        IP address', which would mean that 255.255.255.255 would be invalid,
244        wherase 256.256.256.256 would be valid.  Or it could mean 'has 4 sets
245        of digits separated by dots'.  Who knows.  Let's just be really
246        conservative here: if it has any dots, and no non-digit characters,
247        then we reject it */
248     if (hasDot && !hasNonDigit) {
249         return S3StatusInvalidBucketNameDotQuadNotation;
250     }
251
252     return S3StatusOK;
253 }
254
255
256 typedef struct ConvertAclData
257 {
258     char *ownerId;
259     int ownerIdLen;
260     char *ownerDisplayName;
261     int ownerDisplayNameLen;
262     int *aclGrantCountReturn;
263     S3AclGrant *aclGrants;
264
265     string_buffer(emailAddress, S3_MAX_GRANTEE_EMAIL_ADDRESS_SIZE);
266     string_buffer(userId, S3_MAX_GRANTEE_USER_ID_SIZE);
267     string_buffer(userDisplayName, S3_MAX_GRANTEE_DISPLAY_NAME_SIZE);
268     string_buffer(groupUri, 128);
269     string_buffer(permission, 32);
270 } ConvertAclData;
271
272
273 static S3Status convertAclXmlCallback(const char *elementPath,
274                                       const char *data, int dataLen,
275                                       void *callbackData)
276 {
277     ConvertAclData *caData = (ConvertAclData *) callbackData;
278
279     int fit;
280
281     if (data) {
282         if (!strcmp(elementPath, "AccessControlPolicy/Owner/ID")) {
283             caData->ownerIdLen += 
284                 snprintf(&(caData->ownerId[caData->ownerIdLen]),
285                          S3_MAX_GRANTEE_USER_ID_SIZE - caData->ownerIdLen - 1,
286                          "%.*s", dataLen, data);
287             if (caData->ownerIdLen >= S3_MAX_GRANTEE_USER_ID_SIZE) {
288                 return S3StatusUserIdTooLong;
289             }
290         }
291         else if (!strcmp(elementPath, "AccessControlPolicy/Owner/"
292                          "DisplayName")) {
293             caData->ownerDisplayNameLen += 
294                 snprintf(&(caData->ownerDisplayName
295                            [caData->ownerDisplayNameLen]),
296                          S3_MAX_GRANTEE_DISPLAY_NAME_SIZE -
297                          caData->ownerDisplayNameLen - 1, 
298                          "%.*s", dataLen, data);
299             if (caData->ownerDisplayNameLen >= 
300                 S3_MAX_GRANTEE_DISPLAY_NAME_SIZE) {
301                 return S3StatusUserDisplayNameTooLong;
302             }
303         }
304         else if (!strcmp(elementPath, 
305                     "AccessControlPolicy/AccessControlList/Grant/"
306                     "Grantee/EmailAddress")) {
307             // AmazonCustomerByEmail
308             string_buffer_append(caData->emailAddress, data, dataLen, fit);
309             if (!fit) {
310                 return S3StatusEmailAddressTooLong;
311             }
312         }
313         else if (!strcmp(elementPath,
314                          "AccessControlPolicy/AccessControlList/Grant/"
315                          "Grantee/ID")) {
316             // CanonicalUser
317             string_buffer_append(caData->userId, data, dataLen, fit);
318             if (!fit) {
319                 return S3StatusUserIdTooLong;
320             }
321         }
322         else if (!strcmp(elementPath,
323                          "AccessControlPolicy/AccessControlList/Grant/"
324                          "Grantee/DisplayName")) {
325             // CanonicalUser
326             string_buffer_append(caData->userDisplayName, data, dataLen, fit);
327             if (!fit) {
328                 return S3StatusUserDisplayNameTooLong;
329             }
330         }
331         else if (!strcmp(elementPath,
332                          "AccessControlPolicy/AccessControlList/Grant/"
333                          "Grantee/URI")) {
334             // Group
335             string_buffer_append(caData->groupUri, data, dataLen, fit);
336             if (!fit) {
337                 return S3StatusGroupUriTooLong;
338             }
339         }
340         else if (!strcmp(elementPath,
341                          "AccessControlPolicy/AccessControlList/Grant/"
342                          "Permission")) {
343             // Permission
344             string_buffer_append(caData->permission, data, dataLen, fit);
345             if (!fit) {
346                 return S3StatusPermissionTooLong;
347             }
348         }
349     }
350     else {
351         if (!strcmp(elementPath, "AccessControlPolicy/AccessControlList/"
352                     "Grant")) {
353             // A grant has just been completed; so add the next S3AclGrant
354             // based on the values read
355             if (*(caData->aclGrantCountReturn) == S3_MAX_ACL_GRANT_COUNT) {
356                 return S3StatusTooManyGrants;
357             }
358
359             S3AclGrant *grant = &(caData->aclGrants
360                                   [*(caData->aclGrantCountReturn)]);
361
362             if (caData->emailAddress[0]) {
363                 grant->granteeType = S3GranteeTypeAmazonCustomerByEmail;
364                 strcpy(grant->grantee.amazonCustomerByEmail.emailAddress,
365                        caData->emailAddress);
366             }
367             else if (caData->userId[0] && caData->userDisplayName[0]) {
368                 grant->granteeType = S3GranteeTypeCanonicalUser;
369                 strcpy(grant->grantee.canonicalUser.id, caData->userId);
370                 strcpy(grant->grantee.canonicalUser.displayName, 
371                        caData->userDisplayName);
372             }
373             else if (caData->groupUri[0]) {
374                 if (!strcmp(caData->groupUri,
375                             "http://acs.amazonaws.com/groups/global/"
376                             "AuthenticatedUsers")) {
377                     grant->granteeType = S3GranteeTypeAllAwsUsers;
378                 }
379                 else if (!strcmp(caData->groupUri,
380                                  "http://acs.amazonaws.com/groups/global/"
381                                  "AllUsers")) {
382                     grant->granteeType = S3GranteeTypeAllUsers;
383                 }
384                 else if (!strcmp(caData->groupUri,
385                                  "http://acs.amazonaws.com/groups/s3/"
386                                  "LogDelivery")) {
387                     grant->granteeType = S3GranteeTypeLogDelivery;
388                 }
389                 else {
390                     return S3StatusBadGrantee;
391                 }
392             }
393             else {
394                 return S3StatusBadGrantee;
395             }
396
397             if (!strcmp(caData->permission, "READ")) {
398                 grant->permission = S3PermissionRead;
399             }
400             else if (!strcmp(caData->permission, "WRITE")) {
401                 grant->permission = S3PermissionWrite;
402             }
403             else if (!strcmp(caData->permission, "READ_ACP")) {
404                 grant->permission = S3PermissionReadACP;
405             }
406             else if (!strcmp(caData->permission, "WRITE_ACP")) {
407                 grant->permission = S3PermissionWriteACP;
408             }
409             else if (!strcmp(caData->permission, "FULL_CONTROL")) {
410                 grant->permission = S3PermissionFullControl;
411             }
412             else {
413                 return S3StatusBadPermission;
414             }
415
416             (*(caData->aclGrantCountReturn))++;
417
418             string_buffer_initialize(caData->emailAddress);
419             string_buffer_initialize(caData->userId);
420             string_buffer_initialize(caData->userDisplayName);
421             string_buffer_initialize(caData->groupUri);
422             string_buffer_initialize(caData->permission);
423         }
424     }
425
426     return S3StatusOK;
427 }
428
429
430 S3Status S3_convert_acl(char *aclXml, char *ownerId, char *ownerDisplayName,
431                         int *aclGrantCountReturn, S3AclGrant *aclGrants)
432 {
433     ConvertAclData data;
434
435     data.ownerId = ownerId;
436     data.ownerIdLen = 0;
437     data.ownerId[0] = 0;
438     data.ownerDisplayName = ownerDisplayName;
439     data.ownerDisplayNameLen = 0;
440     data.ownerDisplayName[0] = 0;
441     data.aclGrantCountReturn = aclGrantCountReturn;
442     data.aclGrants = aclGrants;
443     *aclGrantCountReturn = 0;
444     string_buffer_initialize(data.emailAddress);
445     string_buffer_initialize(data.userId);
446     string_buffer_initialize(data.userDisplayName);
447     string_buffer_initialize(data.groupUri);
448     string_buffer_initialize(data.permission);
449
450     // Use a simplexml parser
451     SimpleXml simpleXml;
452     simplexml_initialize(&simpleXml, &convertAclXmlCallback, &data);
453
454     S3Status status = simplexml_add(&simpleXml, aclXml, strlen(aclXml));
455
456     simplexml_deinitialize(&simpleXml);
457                                           
458     return status;
459 }
460
461
462 int S3_status_is_retryable(S3Status status)
463 {
464     switch (status) {
465     case S3StatusNameLookupError:
466     case S3StatusFailedToConnect:
467     case S3StatusConnectionFailed:
468     case S3StatusErrorInternalError:
469     case S3StatusErrorOperationAborted:
470     case S3StatusErrorRequestTimeout:
471         return 1;
472     default:
473         return 0;
474     }
475 }