1 /** **************************************************************************
2 * server_access_logging.c
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 ************************************************************************** **/
33 // get server access logging---------------------------------------------------
35 typedef struct ConvertBlsData
37 char *targetBucketReturn;
38 int targetBucketReturnLen;
39 char *targetPrefixReturn;
40 int targetPrefixReturnLen;
41 int *aclGrantCountReturn;
42 S3AclGrant *aclGrants;
44 string_buffer(emailAddress, S3_MAX_GRANTEE_EMAIL_ADDRESS_SIZE);
45 string_buffer(userId, S3_MAX_GRANTEE_USER_ID_SIZE);
46 string_buffer(userDisplayName, S3_MAX_GRANTEE_DISPLAY_NAME_SIZE);
47 string_buffer(groupUri, 128);
48 string_buffer(permission, 32);
52 static S3Status convertBlsXmlCallback(const char *elementPath,
53 const char *data, int dataLen,
56 ConvertBlsData *caData = (ConvertBlsData *) callbackData;
61 if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/"
63 caData->targetBucketReturnLen +=
64 snprintf(&(caData->targetBucketReturn
65 [caData->targetBucketReturnLen]),
66 255 - caData->targetBucketReturnLen - 1,
67 "%.*s", dataLen, data);
68 if (caData->targetBucketReturnLen >= 255) {
69 return S3StatusTargetBucketTooLong;
72 else if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/"
74 caData->targetPrefixReturnLen +=
75 snprintf(&(caData->targetPrefixReturn
76 [caData->targetPrefixReturnLen]),
77 255 - caData->targetPrefixReturnLen - 1,
78 "%.*s", dataLen, data);
79 if (caData->targetPrefixReturnLen >= 255) {
80 return S3StatusTargetPrefixTooLong;
83 else if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/"
84 "TargetGrants/Grant/Grantee/EmailAddress")) {
85 // AmazonCustomerByEmail
86 string_buffer_append(caData->emailAddress, data, dataLen, fit);
88 return S3StatusEmailAddressTooLong;
91 else if (!strcmp(elementPath,
92 "AccessControlPolicy/AccessControlList/Grant/"
95 string_buffer_append(caData->userId, data, dataLen, fit);
97 return S3StatusUserIdTooLong;
100 else if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/"
101 "TargetGrants/Grant/Grantee/DisplayName")) {
103 string_buffer_append(caData->userDisplayName, data, dataLen, fit);
105 return S3StatusUserDisplayNameTooLong;
108 else if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/"
109 "TargetGrants/Grant/Grantee/URI")) {
111 string_buffer_append(caData->groupUri, data, dataLen, fit);
113 return S3StatusGroupUriTooLong;
116 else if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/"
117 "TargetGrants/Grant/Permission")) {
119 string_buffer_append(caData->permission, data, dataLen, fit);
121 return S3StatusPermissionTooLong;
126 if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/"
127 "TargetGrants/Grant")) {
128 // A grant has just been completed; so add the next S3AclGrant
129 // based on the values read
130 if (*(caData->aclGrantCountReturn) == S3_MAX_ACL_GRANT_COUNT) {
131 return S3StatusTooManyGrants;
134 S3AclGrant *grant = &(caData->aclGrants
135 [*(caData->aclGrantCountReturn)]);
137 if (caData->emailAddress[0]) {
138 grant->granteeType = S3GranteeTypeAmazonCustomerByEmail;
139 strcpy(grant->grantee.amazonCustomerByEmail.emailAddress,
140 caData->emailAddress);
142 else if (caData->userId[0] && caData->userDisplayName[0]) {
143 grant->granteeType = S3GranteeTypeCanonicalUser;
144 strcpy(grant->grantee.canonicalUser.id, caData->userId);
145 strcpy(grant->grantee.canonicalUser.displayName,
146 caData->userDisplayName);
148 else if (caData->groupUri[0]) {
149 if (!strcmp(caData->groupUri,
150 "http://acs.amazonaws.com/groups/global/"
151 "AuthenticatedUsers")) {
152 grant->granteeType = S3GranteeTypeAllAwsUsers;
154 else if (!strcmp(caData->groupUri,
155 "http://acs.amazonaws.com/groups/global/"
157 grant->granteeType = S3GranteeTypeAllUsers;
160 return S3StatusBadGrantee;
164 return S3StatusBadGrantee;
167 if (!strcmp(caData->permission, "READ")) {
168 grant->permission = S3PermissionRead;
170 else if (!strcmp(caData->permission, "WRITE")) {
171 grant->permission = S3PermissionWrite;
173 else if (!strcmp(caData->permission, "READ_ACP")) {
174 grant->permission = S3PermissionReadACP;
176 else if (!strcmp(caData->permission, "WRITE_ACP")) {
177 grant->permission = S3PermissionWriteACP;
179 else if (!strcmp(caData->permission, "FULL_CONTROL")) {
180 grant->permission = S3PermissionFullControl;
183 return S3StatusBadPermission;
186 (*(caData->aclGrantCountReturn))++;
188 string_buffer_initialize(caData->emailAddress);
189 string_buffer_initialize(caData->userId);
190 string_buffer_initialize(caData->userDisplayName);
191 string_buffer_initialize(caData->groupUri);
192 string_buffer_initialize(caData->permission);
200 static S3Status convert_bls(char *blsXml, char *targetBucketReturn,
201 char *targetPrefixReturn, int *aclGrantCountReturn,
202 S3AclGrant *aclGrants)
206 data.targetBucketReturn = targetBucketReturn;
207 data.targetBucketReturn[0] = 0;
208 data.targetBucketReturnLen = 0;
209 data.targetPrefixReturn = targetPrefixReturn;
210 data.targetPrefixReturn[0] = 0;
211 data.targetPrefixReturnLen = 0;
212 data.aclGrantCountReturn = aclGrantCountReturn;
213 data.aclGrants = aclGrants;
214 *aclGrantCountReturn = 0;
215 string_buffer_initialize(data.emailAddress);
216 string_buffer_initialize(data.userId);
217 string_buffer_initialize(data.userDisplayName);
218 string_buffer_initialize(data.groupUri);
219 string_buffer_initialize(data.permission);
221 // Use a simplexml parser
223 simplexml_initialize(&simpleXml, &convertBlsXmlCallback, &data);
225 S3Status status = simplexml_add(&simpleXml, blsXml, strlen(blsXml));
227 simplexml_deinitialize(&simpleXml);
233 // Use a rather arbitrary max size for the document of 64K
234 #define BLS_XML_DOC_MAXSIZE (64 * 1024)
237 typedef struct GetBlsData
241 S3ResponsePropertiesCallback *responsePropertiesCallback;
242 S3ResponseCompleteCallback *responseCompleteCallback;
245 char *targetBucketReturn;
246 char *targetPrefixReturn;
247 int *aclGrantCountReturn;
248 S3AclGrant *aclGrants;
249 string_buffer(blsXmlDocument, BLS_XML_DOC_MAXSIZE);
253 static S3Status getBlsPropertiesCallback
254 (const S3ResponseProperties *responseProperties, void *callbackData)
256 GetBlsData *gsData = (GetBlsData *) callbackData;
258 return (*(gsData->responsePropertiesCallback))
259 (responseProperties, gsData->callbackData);
263 static S3Status getBlsDataCallback(int bufferSize, const char *buffer,
266 GetBlsData *gsData = (GetBlsData *) callbackData;
270 string_buffer_append(gsData->blsXmlDocument, buffer, bufferSize, fit);
272 return fit ? S3StatusOK : S3StatusXmlDocumentTooLarge;
276 static void getBlsCompleteCallback(S3Status requestStatus,
277 const S3ErrorDetails *s3ErrorDetails,
280 GetBlsData *gsData = (GetBlsData *) callbackData;
282 if (requestStatus == S3StatusOK) {
283 // Parse the document
284 requestStatus = convert_bls
285 (gsData->blsXmlDocument, gsData->targetBucketReturn,
286 gsData->targetPrefixReturn, gsData->aclGrantCountReturn,
290 (*(gsData->responseCompleteCallback))
291 (requestStatus, s3ErrorDetails, gsData->callbackData);
297 void S3_get_server_access_logging(const S3BucketContext *bucketContext,
298 char *targetBucketReturn,
299 char *targetPrefixReturn,
300 int *aclGrantCountReturn,
301 S3AclGrant *aclGrants,
302 S3RequestContext *requestContext,
303 const S3ResponseHandler *handler,
306 // Create the callback data
307 GetBlsData *gsData = (GetBlsData *) malloc(sizeof(GetBlsData));
309 (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData);
313 gsData->responsePropertiesCallback = handler->propertiesCallback;
314 gsData->responseCompleteCallback = handler->completeCallback;
315 gsData->callbackData = callbackData;
317 gsData->targetBucketReturn = targetBucketReturn;
318 gsData->targetPrefixReturn = targetPrefixReturn;
319 gsData->aclGrantCountReturn = aclGrantCountReturn;
320 gsData->aclGrants = aclGrants;
321 string_buffer_initialize(gsData->blsXmlDocument);
322 *aclGrantCountReturn = 0;
324 // Set up the RequestParams
325 RequestParams params =
327 HttpRequestTypeGET, // httpRequestType
328 { bucketContext->bucketName, // bucketName
329 bucketContext->protocol, // protocol
330 bucketContext->uriStyle, // uriStyle
331 bucketContext->accessKeyId, // accessKeyId
332 bucketContext->secretAccessKey }, // secretAccessKey
335 "logging", // subResource
336 0, // copySourceBucketName
342 &getBlsPropertiesCallback, // propertiesCallback
344 0, // toS3CallbackTotalSize
345 &getBlsDataCallback, // fromS3Callback
346 &getBlsCompleteCallback, // completeCallback
347 gsData // callbackData
350 // Perform the request
351 request_perform(¶ms, requestContext);
356 // set server access logging---------------------------------------------------
358 static S3Status generateSalXmlDocument(const char *targetBucket,
359 const char *targetPrefix,
361 const S3AclGrant *aclGrants,
362 int *xmlDocumentLenReturn,
364 int xmlDocumentBufferSize)
366 *xmlDocumentLenReturn = 0;
368 #define append(fmt, ...) \
370 *xmlDocumentLenReturn += snprintf \
371 (&(xmlDocument[*xmlDocumentLenReturn]), \
372 xmlDocumentBufferSize - *xmlDocumentLenReturn - 1, \
374 if (*xmlDocumentLenReturn >= xmlDocumentBufferSize) { \
375 return S3StatusXmlDocumentTooLarge; \
379 append("%s", "<BucketLoggingStatus "
380 "xmlns=\"http://doc.s3.amazonaws.com/2006-03-01\">");
382 if (targetBucket && targetBucket[0]) {
383 append("<LoggingEnabled><TargetBucket>%s</TargetBucket>", targetBucket);
384 append("<TargetPrefix>%s</TargetPrefix>",
385 targetPrefix ? targetPrefix : "");
388 append("%s", "<TargetGrants>");
390 for (i = 0; i < aclGrantCount; i++) {
391 append("%s", "<Grant><Grantee "
392 "xmlns:xsi=\"http://www.w3.org/2001/"
393 "XMLSchema-instance\" xsi:type=\"");
394 const S3AclGrant *grant = &(aclGrants[i]);
395 switch (grant->granteeType) {
396 case S3GranteeTypeAmazonCustomerByEmail:
397 append("AmazonCustomerByEmail\"><EmailAddress>%s"
399 grant->grantee.amazonCustomerByEmail.emailAddress);
401 case S3GranteeTypeCanonicalUser:
402 append("CanonicalUser\"><ID>%s</ID><DisplayName>%s"
404 grant->grantee.canonicalUser.id,
405 grant->grantee.canonicalUser.displayName);
407 default: // case S3GranteeTypeAllAwsUsers/S3GranteeTypeAllUsers:
408 append("Group\"><URI>http://acs.amazonaws.com/groups/"
410 (grant->granteeType == S3GranteeTypeAllAwsUsers) ?
411 "AuthenticatedUsers" : "AllUsers");
414 append("</Grantee><Permission>%s</Permission></Grant>",
415 ((grant->permission == S3PermissionRead) ? "READ" :
416 (grant->permission == S3PermissionWrite) ? "WRITE" :
417 (grant->permission ==
418 S3PermissionReadACP) ? "READ_ACP" :
419 (grant->permission ==
420 S3PermissionWriteACP) ? "WRITE_ACP" : "FULL_CONTROL"));
422 append("%s", "</TargetGrants>");
424 append("%s", "</LoggingEnabled>");
427 append("%s", "</BucketLoggingStatus>");
433 typedef struct SetSalData
435 S3ResponsePropertiesCallback *responsePropertiesCallback;
436 S3ResponseCompleteCallback *responseCompleteCallback;
439 int salXmlDocumentLen;
440 char salXmlDocument[BLS_XML_DOC_MAXSIZE];
441 int salXmlDocumentBytesWritten;
446 static S3Status setSalPropertiesCallback
447 (const S3ResponseProperties *responseProperties, void *callbackData)
449 SetSalData *paData = (SetSalData *) callbackData;
451 return (*(paData->responsePropertiesCallback))
452 (responseProperties, paData->callbackData);
456 static int setSalDataCallback(int bufferSize, char *buffer, void *callbackData)
458 SetSalData *paData = (SetSalData *) callbackData;
460 int remaining = (paData->salXmlDocumentLen -
461 paData->salXmlDocumentBytesWritten);
463 int toCopy = bufferSize > remaining ? remaining : bufferSize;
469 memcpy(buffer, &(paData->salXmlDocument
470 [paData->salXmlDocumentBytesWritten]), toCopy);
472 paData->salXmlDocumentBytesWritten += toCopy;
478 static void setSalCompleteCallback(S3Status requestStatus,
479 const S3ErrorDetails *s3ErrorDetails,
482 SetSalData *paData = (SetSalData *) callbackData;
484 (*(paData->responseCompleteCallback))
485 (requestStatus, s3ErrorDetails, paData->callbackData);
491 void S3_set_server_access_logging(const S3BucketContext *bucketContext,
492 const char *targetBucket,
493 const char *targetPrefix, int aclGrantCount,
494 const S3AclGrant *aclGrants,
495 S3RequestContext *requestContext,
496 const S3ResponseHandler *handler,
499 if (aclGrantCount > S3_MAX_ACL_GRANT_COUNT) {
500 (*(handler->completeCallback))
501 (S3StatusTooManyGrants, 0, callbackData);
505 SetSalData *data = (SetSalData *) malloc(sizeof(SetSalData));
507 (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData);
511 // Convert aclGrants to XML document
512 S3Status status = generateSalXmlDocument
513 (targetBucket, targetPrefix, aclGrantCount, aclGrants,
514 &(data->salXmlDocumentLen), data->salXmlDocument,
515 sizeof(data->salXmlDocument));
516 if (status != S3StatusOK) {
518 (*(handler->completeCallback))(status, 0, callbackData);
522 data->responsePropertiesCallback = handler->propertiesCallback;
523 data->responseCompleteCallback = handler->completeCallback;
524 data->callbackData = callbackData;
526 data->salXmlDocumentBytesWritten = 0;
528 // Set up the RequestParams
529 RequestParams params =
531 HttpRequestTypePUT, // httpRequestType
532 { bucketContext->bucketName, // bucketName
533 bucketContext->protocol, // protocol
534 bucketContext->uriStyle, // uriStyle
535 bucketContext->accessKeyId, // accessKeyId
536 bucketContext->secretAccessKey }, // secretAccessKey
539 "logging", // subResource
540 0, // copySourceBucketName
546 &setSalPropertiesCallback, // propertiesCallback
547 &setSalDataCallback, // toS3Callback
548 data->salXmlDocumentLen, // toS3CallbackTotalSize
550 &setSalCompleteCallback, // completeCallback
554 // Perform the request
555 request_perform(¶ms, requestContext);