X-Git-Url: http://git.vrable.net/?a=blobdiff_plain;f=libs3-1.4%2Fsrc%2Fgeneral.c;fp=libs3-1.4%2Fsrc%2Fgeneral.c;h=861c289956f311158ee870ae2a791c5423237e8a;hb=1d62668cef91cf8f93f078ff47b4f99dec0f1669;hp=0000000000000000000000000000000000000000;hpb=45a4f324927f8fe3b7e689dd0c9a28716d0d1e93;p=bluesky.git diff --git a/libs3-1.4/src/general.c b/libs3-1.4/src/general.c new file mode 100644 index 0000000..861c289 --- /dev/null +++ b/libs3-1.4/src/general.c @@ -0,0 +1,475 @@ +/** ************************************************************************** + * general.c + * + * Copyright 2008 Bryan Ischo + * + * This file is part of libs3. + * + * libs3 is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, version 3 of the License. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of this library and its programs with the + * OpenSSL library, and distribute linked combinations including the two. + * + * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License version 3 + * along with libs3, in a file named COPYING. If not, see + * . + * + ************************************************************************** **/ + +#include +#include +#include "request.h" +#include "simplexml.h" +#include "util.h" + +static int initializeCountG = 0; + +S3Status S3_initialize(const char *userAgentInfo, int flags) +{ + if (initializeCountG++) { + return S3StatusOK; + } + + return request_api_initialize(userAgentInfo, flags); +} + + +void S3_deinitialize() +{ + if (--initializeCountG) { + return; + } + + request_api_deinitialize(); +} + +const char *S3_get_status_name(S3Status status) +{ + switch (status) { +#define handlecase(s) \ + case S3Status##s: \ + return #s + + handlecase(OK); + handlecase(InternalError); + handlecase(OutOfMemory); + handlecase(Interrupted); + handlecase(InvalidBucketNameTooLong); + handlecase(InvalidBucketNameFirstCharacter); + handlecase(InvalidBucketNameCharacter); + handlecase(InvalidBucketNameCharacterSequence); + handlecase(InvalidBucketNameTooShort); + handlecase(InvalidBucketNameDotQuadNotation); + handlecase(QueryParamsTooLong); + handlecase(FailedToInitializeRequest); + handlecase(MetaDataHeadersTooLong); + handlecase(BadMetaData); + handlecase(BadContentType); + handlecase(ContentTypeTooLong); + handlecase(BadMD5); + handlecase(MD5TooLong); + handlecase(BadCacheControl); + handlecase(CacheControlTooLong); + handlecase(BadContentDispositionFilename); + handlecase(ContentDispositionFilenameTooLong); + handlecase(BadContentEncoding); + handlecase(ContentEncodingTooLong); + handlecase(BadIfMatchETag); + handlecase(IfMatchETagTooLong); + handlecase(BadIfNotMatchETag); + handlecase(IfNotMatchETagTooLong); + handlecase(HeadersTooLong); + handlecase(KeyTooLong); + handlecase(UriTooLong); + handlecase(XmlParseFailure); + handlecase(EmailAddressTooLong); + handlecase(UserIdTooLong); + handlecase(UserDisplayNameTooLong); + handlecase(GroupUriTooLong); + handlecase(PermissionTooLong); + handlecase(TargetBucketTooLong); + handlecase(TargetPrefixTooLong); + handlecase(TooManyGrants); + handlecase(BadGrantee); + handlecase(BadPermission); + handlecase(XmlDocumentTooLarge); + handlecase(NameLookupError); + handlecase(FailedToConnect); + handlecase(ServerFailedVerification); + handlecase(ConnectionFailed); + handlecase(AbortedByCallback); + handlecase(ErrorAccessDenied); + handlecase(ErrorAccountProblem); + handlecase(ErrorAmbiguousGrantByEmailAddress); + handlecase(ErrorBadDigest); + handlecase(ErrorBucketAlreadyExists); + handlecase(ErrorBucketAlreadyOwnedByYou); + handlecase(ErrorBucketNotEmpty); + handlecase(ErrorCredentialsNotSupported); + handlecase(ErrorCrossLocationLoggingProhibited); + handlecase(ErrorEntityTooSmall); + handlecase(ErrorEntityTooLarge); + handlecase(ErrorExpiredToken); + handlecase(ErrorIncompleteBody); + handlecase(ErrorIncorrectNumberOfFilesInPostRequest); + handlecase(ErrorInlineDataTooLarge); + handlecase(ErrorInternalError); + handlecase(ErrorInvalidAccessKeyId); + handlecase(ErrorInvalidAddressingHeader); + handlecase(ErrorInvalidArgument); + handlecase(ErrorInvalidBucketName); + handlecase(ErrorInvalidDigest); + handlecase(ErrorInvalidLocationConstraint); + handlecase(ErrorInvalidPayer); + handlecase(ErrorInvalidPolicyDocument); + handlecase(ErrorInvalidRange); + handlecase(ErrorInvalidSecurity); + handlecase(ErrorInvalidSOAPRequest); + handlecase(ErrorInvalidStorageClass); + handlecase(ErrorInvalidTargetBucketForLogging); + handlecase(ErrorInvalidToken); + handlecase(ErrorInvalidURI); + handlecase(ErrorKeyTooLong); + handlecase(ErrorMalformedACLError); + handlecase(ErrorMalformedXML); + handlecase(ErrorMaxMessageLengthExceeded); + handlecase(ErrorMaxPostPreDataLengthExceededError); + handlecase(ErrorMetadataTooLarge); + handlecase(ErrorMethodNotAllowed); + handlecase(ErrorMissingAttachment); + handlecase(ErrorMissingContentLength); + handlecase(ErrorMissingSecurityElement); + handlecase(ErrorMissingSecurityHeader); + handlecase(ErrorNoLoggingStatusForKey); + handlecase(ErrorNoSuchBucket); + handlecase(ErrorNoSuchKey); + handlecase(ErrorNotImplemented); + handlecase(ErrorNotSignedUp); + handlecase(ErrorOperationAborted); + handlecase(ErrorPermanentRedirect); + handlecase(ErrorPreconditionFailed); + handlecase(ErrorRedirect); + handlecase(ErrorRequestIsNotMultiPartContent); + handlecase(ErrorRequestTimeout); + handlecase(ErrorRequestTimeTooSkewed); + handlecase(ErrorRequestTorrentOfBucketError); + handlecase(ErrorSignatureDoesNotMatch); + handlecase(ErrorSlowDown); + handlecase(ErrorTemporaryRedirect); + handlecase(ErrorTokenRefreshRequired); + handlecase(ErrorTooManyBuckets); + handlecase(ErrorUnexpectedContent); + handlecase(ErrorUnresolvableGrantByEmailAddress); + handlecase(ErrorUserKeyMustBeSpecified); + handlecase(ErrorUnknown); + handlecase(HttpErrorMovedTemporarily); + handlecase(HttpErrorBadRequest); + handlecase(HttpErrorForbidden); + handlecase(HttpErrorNotFound); + handlecase(HttpErrorConflict); + handlecase(HttpErrorUnknown); + } + + return "Unknown"; +} + + +S3Status S3_validate_bucket_name(const char *bucketName, S3UriStyle uriStyle) +{ + int virtualHostStyle = (uriStyle == S3UriStyleVirtualHost); + int len = 0, maxlen = virtualHostStyle ? 63 : 255; + const char *b = bucketName; + + int hasDot = 0; + int hasNonDigit = 0; + + while (*b) { + if (len == maxlen) { + return S3StatusInvalidBucketNameTooLong; + } + else if (isalpha(*b)) { + len++, b++; + hasNonDigit = 1; + } + else if (isdigit(*b)) { + len++, b++; + } + else if (len == 0) { + return S3StatusInvalidBucketNameFirstCharacter; + } + else if (*b == '_') { + /* Virtual host style bucket names cannot have underscores */ + if (virtualHostStyle) { + return S3StatusInvalidBucketNameCharacter; + } + len++, b++; + hasNonDigit = 1; + } + else if (*b == '-') { + /* Virtual host style bucket names cannot have .- */ + if (virtualHostStyle && (b > bucketName) && (*(b - 1) == '.')) { + return S3StatusInvalidBucketNameCharacterSequence; + } + len++, b++; + hasNonDigit = 1; + } + else if (*b == '.') { + /* Virtual host style bucket names cannot have -. */ + if (virtualHostStyle && (b > bucketName) && (*(b - 1) == '-')) { + return S3StatusInvalidBucketNameCharacterSequence; + } + len++, b++; + hasDot = 1; + } + else { + return S3StatusInvalidBucketNameCharacter; + } + } + + if (len < 3) { + return S3StatusInvalidBucketNameTooShort; + } + + /* It's not clear from Amazon's documentation exactly what 'IP address + style' means. In its strictest sense, it could mean 'could be a valid + IP address', which would mean that 255.255.255.255 would be invalid, + wherase 256.256.256.256 would be valid. Or it could mean 'has 4 sets + of digits separated by dots'. Who knows. Let's just be really + conservative here: if it has any dots, and no non-digit characters, + then we reject it */ + if (hasDot && !hasNonDigit) { + return S3StatusInvalidBucketNameDotQuadNotation; + } + + return S3StatusOK; +} + + +typedef struct ConvertAclData +{ + char *ownerId; + int ownerIdLen; + char *ownerDisplayName; + int ownerDisplayNameLen; + int *aclGrantCountReturn; + S3AclGrant *aclGrants; + + string_buffer(emailAddress, S3_MAX_GRANTEE_EMAIL_ADDRESS_SIZE); + string_buffer(userId, S3_MAX_GRANTEE_USER_ID_SIZE); + string_buffer(userDisplayName, S3_MAX_GRANTEE_DISPLAY_NAME_SIZE); + string_buffer(groupUri, 128); + string_buffer(permission, 32); +} ConvertAclData; + + +static S3Status convertAclXmlCallback(const char *elementPath, + const char *data, int dataLen, + void *callbackData) +{ + ConvertAclData *caData = (ConvertAclData *) callbackData; + + int fit; + + if (data) { + if (!strcmp(elementPath, "AccessControlPolicy/Owner/ID")) { + caData->ownerIdLen += + snprintf(&(caData->ownerId[caData->ownerIdLen]), + S3_MAX_GRANTEE_USER_ID_SIZE - caData->ownerIdLen - 1, + "%.*s", dataLen, data); + if (caData->ownerIdLen >= S3_MAX_GRANTEE_USER_ID_SIZE) { + return S3StatusUserIdTooLong; + } + } + else if (!strcmp(elementPath, "AccessControlPolicy/Owner/" + "DisplayName")) { + caData->ownerDisplayNameLen += + snprintf(&(caData->ownerDisplayName + [caData->ownerDisplayNameLen]), + S3_MAX_GRANTEE_DISPLAY_NAME_SIZE - + caData->ownerDisplayNameLen - 1, + "%.*s", dataLen, data); + if (caData->ownerDisplayNameLen >= + S3_MAX_GRANTEE_DISPLAY_NAME_SIZE) { + return S3StatusUserDisplayNameTooLong; + } + } + else if (!strcmp(elementPath, + "AccessControlPolicy/AccessControlList/Grant/" + "Grantee/EmailAddress")) { + // AmazonCustomerByEmail + string_buffer_append(caData->emailAddress, data, dataLen, fit); + if (!fit) { + return S3StatusEmailAddressTooLong; + } + } + else if (!strcmp(elementPath, + "AccessControlPolicy/AccessControlList/Grant/" + "Grantee/ID")) { + // CanonicalUser + string_buffer_append(caData->userId, data, dataLen, fit); + if (!fit) { + return S3StatusUserIdTooLong; + } + } + else if (!strcmp(elementPath, + "AccessControlPolicy/AccessControlList/Grant/" + "Grantee/DisplayName")) { + // CanonicalUser + string_buffer_append(caData->userDisplayName, data, dataLen, fit); + if (!fit) { + return S3StatusUserDisplayNameTooLong; + } + } + else if (!strcmp(elementPath, + "AccessControlPolicy/AccessControlList/Grant/" + "Grantee/URI")) { + // Group + string_buffer_append(caData->groupUri, data, dataLen, fit); + if (!fit) { + return S3StatusGroupUriTooLong; + } + } + else if (!strcmp(elementPath, + "AccessControlPolicy/AccessControlList/Grant/" + "Permission")) { + // Permission + string_buffer_append(caData->permission, data, dataLen, fit); + if (!fit) { + return S3StatusPermissionTooLong; + } + } + } + else { + if (!strcmp(elementPath, "AccessControlPolicy/AccessControlList/" + "Grant")) { + // A grant has just been completed; so add the next S3AclGrant + // based on the values read + if (*(caData->aclGrantCountReturn) == S3_MAX_ACL_GRANT_COUNT) { + return S3StatusTooManyGrants; + } + + S3AclGrant *grant = &(caData->aclGrants + [*(caData->aclGrantCountReturn)]); + + if (caData->emailAddress[0]) { + grant->granteeType = S3GranteeTypeAmazonCustomerByEmail; + strcpy(grant->grantee.amazonCustomerByEmail.emailAddress, + caData->emailAddress); + } + else if (caData->userId[0] && caData->userDisplayName[0]) { + grant->granteeType = S3GranteeTypeCanonicalUser; + strcpy(grant->grantee.canonicalUser.id, caData->userId); + strcpy(grant->grantee.canonicalUser.displayName, + caData->userDisplayName); + } + else if (caData->groupUri[0]) { + if (!strcmp(caData->groupUri, + "http://acs.amazonaws.com/groups/global/" + "AuthenticatedUsers")) { + grant->granteeType = S3GranteeTypeAllAwsUsers; + } + else if (!strcmp(caData->groupUri, + "http://acs.amazonaws.com/groups/global/" + "AllUsers")) { + grant->granteeType = S3GranteeTypeAllUsers; + } + else if (!strcmp(caData->groupUri, + "http://acs.amazonaws.com/groups/s3/" + "LogDelivery")) { + grant->granteeType = S3GranteeTypeLogDelivery; + } + else { + return S3StatusBadGrantee; + } + } + else { + return S3StatusBadGrantee; + } + + if (!strcmp(caData->permission, "READ")) { + grant->permission = S3PermissionRead; + } + else if (!strcmp(caData->permission, "WRITE")) { + grant->permission = S3PermissionWrite; + } + else if (!strcmp(caData->permission, "READ_ACP")) { + grant->permission = S3PermissionReadACP; + } + else if (!strcmp(caData->permission, "WRITE_ACP")) { + grant->permission = S3PermissionWriteACP; + } + else if (!strcmp(caData->permission, "FULL_CONTROL")) { + grant->permission = S3PermissionFullControl; + } + else { + return S3StatusBadPermission; + } + + (*(caData->aclGrantCountReturn))++; + + string_buffer_initialize(caData->emailAddress); + string_buffer_initialize(caData->userId); + string_buffer_initialize(caData->userDisplayName); + string_buffer_initialize(caData->groupUri); + string_buffer_initialize(caData->permission); + } + } + + return S3StatusOK; +} + + +S3Status S3_convert_acl(char *aclXml, char *ownerId, char *ownerDisplayName, + int *aclGrantCountReturn, S3AclGrant *aclGrants) +{ + ConvertAclData data; + + data.ownerId = ownerId; + data.ownerIdLen = 0; + data.ownerId[0] = 0; + data.ownerDisplayName = ownerDisplayName; + data.ownerDisplayNameLen = 0; + data.ownerDisplayName[0] = 0; + data.aclGrantCountReturn = aclGrantCountReturn; + data.aclGrants = aclGrants; + *aclGrantCountReturn = 0; + string_buffer_initialize(data.emailAddress); + string_buffer_initialize(data.userId); + string_buffer_initialize(data.userDisplayName); + string_buffer_initialize(data.groupUri); + string_buffer_initialize(data.permission); + + // Use a simplexml parser + SimpleXml simpleXml; + simplexml_initialize(&simpleXml, &convertAclXmlCallback, &data); + + S3Status status = simplexml_add(&simpleXml, aclXml, strlen(aclXml)); + + simplexml_deinitialize(&simpleXml); + + return status; +} + + +int S3_status_is_retryable(S3Status status) +{ + switch (status) { + case S3StatusNameLookupError: + case S3StatusFailedToConnect: + case S3StatusConnectionFailed: + case S3StatusErrorInternalError: + case S3StatusErrorOperationAborted: + case S3StatusErrorRequestTimeout: + return 1; + default: + return 0; + } +}