cbed2c1da5589f30e2e4b2389eb242b53c3e5b76
[bluesky.git] / libs3-1.4 / src / service_access_logging.c
1 /** **************************************************************************
2  * server_access_logging.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 <stdlib.h>
28 #include <string.h>
29 #include "libs3.h"
30 #include "request.h"
31
32
33 // get server access logging---------------------------------------------------
34
35 typedef struct ConvertBlsData
36 {
37     char *targetBucketReturn;
38     int targetBucketReturnLen;
39     char *targetPrefixReturn;
40     int targetPrefixReturnLen;
41     int *aclGrantCountReturn;
42     S3AclGrant *aclGrants;
43
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);
49 } ConvertBlsData;
50
51
52 static S3Status convertBlsXmlCallback(const char *elementPath,
53                                       const char *data, int dataLen,
54                                       void *callbackData)
55 {
56     ConvertBlsData *caData = (ConvertBlsData *) callbackData;
57
58     int fit;
59
60     if (data) {
61         if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/"
62                     "TargetBucket")) {
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;
70             }
71         }
72         else if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/"
73                     "TargetPrefix")) {
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;
81             }
82         }
83         else if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/"
84                          "TargetGrants/Grant/Grantee/EmailAddress")) {
85             // AmazonCustomerByEmail
86             string_buffer_append(caData->emailAddress, data, dataLen, fit);
87             if (!fit) {
88                 return S3StatusEmailAddressTooLong;
89             }
90         }
91         else if (!strcmp(elementPath,
92                          "AccessControlPolicy/AccessControlList/Grant/"
93                          "Grantee/ID")) {
94             // CanonicalUser
95             string_buffer_append(caData->userId, data, dataLen, fit);
96             if (!fit) {
97                 return S3StatusUserIdTooLong;
98             }
99         }
100         else if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/"
101                          "TargetGrants/Grant/Grantee/DisplayName")) {
102             // CanonicalUser
103             string_buffer_append(caData->userDisplayName, data, dataLen, fit);
104             if (!fit) {
105                 return S3StatusUserDisplayNameTooLong;
106             }
107         }
108         else if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/"
109                          "TargetGrants/Grant/Grantee/URI")) {
110             // Group
111             string_buffer_append(caData->groupUri, data, dataLen, fit);
112             if (!fit) {
113                 return S3StatusGroupUriTooLong;
114             }
115         }
116         else if (!strcmp(elementPath, "BucketLoggingStatus/LoggingEnabled/"
117                          "TargetGrants/Grant/Permission")) {
118             // Permission
119             string_buffer_append(caData->permission, data, dataLen, fit);
120             if (!fit) {
121                 return S3StatusPermissionTooLong;
122             }
123         }
124     }
125     else {
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;
132             }
133
134             S3AclGrant *grant = &(caData->aclGrants
135                                   [*(caData->aclGrantCountReturn)]);
136
137             if (caData->emailAddress[0]) {
138                 grant->granteeType = S3GranteeTypeAmazonCustomerByEmail;
139                 strcpy(grant->grantee.amazonCustomerByEmail.emailAddress,
140                        caData->emailAddress);
141             }
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);
147             }
148             else if (caData->groupUri[0]) {
149                 if (!strcmp(caData->groupUri,
150                             "http://acs.amazonaws.com/groups/global/"
151                             "AuthenticatedUsers")) {
152                     grant->granteeType = S3GranteeTypeAllAwsUsers;
153                 }
154                 else if (!strcmp(caData->groupUri,
155                                  "http://acs.amazonaws.com/groups/global/"
156                                  "AllUsers")) {
157                     grant->granteeType = S3GranteeTypeAllUsers;
158                 }
159                 else {
160                     return S3StatusBadGrantee;
161                 }
162             }
163             else {
164                 return S3StatusBadGrantee;
165             }
166
167             if (!strcmp(caData->permission, "READ")) {
168                 grant->permission = S3PermissionRead;
169             }
170             else if (!strcmp(caData->permission, "WRITE")) {
171                 grant->permission = S3PermissionWrite;
172             }
173             else if (!strcmp(caData->permission, "READ_ACP")) {
174                 grant->permission = S3PermissionReadACP;
175             }
176             else if (!strcmp(caData->permission, "WRITE_ACP")) {
177                 grant->permission = S3PermissionWriteACP;
178             }
179             else if (!strcmp(caData->permission, "FULL_CONTROL")) {
180                 grant->permission = S3PermissionFullControl;
181             }
182             else {
183                 return S3StatusBadPermission;
184             }
185
186             (*(caData->aclGrantCountReturn))++;
187
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);
193         }
194     }
195
196     return S3StatusOK;
197 }
198
199
200 static S3Status convert_bls(char *blsXml, char *targetBucketReturn,
201                             char *targetPrefixReturn, int *aclGrantCountReturn,
202                             S3AclGrant *aclGrants)
203 {
204     ConvertBlsData data;
205
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);
220
221     // Use a simplexml parser
222     SimpleXml simpleXml;
223     simplexml_initialize(&simpleXml, &convertBlsXmlCallback, &data);
224
225     S3Status status = simplexml_add(&simpleXml, blsXml, strlen(blsXml));
226
227     simplexml_deinitialize(&simpleXml);
228                                           
229     return status;
230 }
231
232
233 // Use a rather arbitrary max size for the document of 64K
234 #define BLS_XML_DOC_MAXSIZE (64 * 1024)
235
236
237 typedef struct GetBlsData
238 {
239     SimpleXml simpleXml;
240
241     S3ResponsePropertiesCallback *responsePropertiesCallback;
242     S3ResponseCompleteCallback *responseCompleteCallback;
243     void *callbackData;
244
245     char *targetBucketReturn;
246     char *targetPrefixReturn;
247     int *aclGrantCountReturn;
248     S3AclGrant *aclGrants;
249     string_buffer(blsXmlDocument, BLS_XML_DOC_MAXSIZE);
250 } GetBlsData;
251
252
253 static S3Status getBlsPropertiesCallback
254     (const S3ResponseProperties *responseProperties, void *callbackData)
255 {
256     GetBlsData *gsData = (GetBlsData *) callbackData;
257     
258     return (*(gsData->responsePropertiesCallback))
259         (responseProperties, gsData->callbackData);
260 }
261
262
263 static S3Status getBlsDataCallback(int bufferSize, const char *buffer,
264                                    void *callbackData)
265 {
266     GetBlsData *gsData = (GetBlsData *) callbackData;
267
268     int fit;
269
270     string_buffer_append(gsData->blsXmlDocument, buffer, bufferSize, fit);
271     
272     return fit ? S3StatusOK : S3StatusXmlDocumentTooLarge;
273 }
274
275
276 static void getBlsCompleteCallback(S3Status requestStatus, 
277                                    const S3ErrorDetails *s3ErrorDetails,
278                                    void *callbackData)
279 {
280     GetBlsData *gsData = (GetBlsData *) callbackData;
281
282     if (requestStatus == S3StatusOK) {
283         // Parse the document
284         requestStatus = convert_bls
285             (gsData->blsXmlDocument, gsData->targetBucketReturn,
286              gsData->targetPrefixReturn, gsData->aclGrantCountReturn, 
287              gsData->aclGrants);
288     }
289
290     (*(gsData->responseCompleteCallback))
291         (requestStatus, s3ErrorDetails, gsData->callbackData);
292
293     free(gsData);
294 }
295
296
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,
304                                   void *callbackData)
305 {
306     // Create the callback data
307     GetBlsData *gsData = (GetBlsData *) malloc(sizeof(GetBlsData));
308     if (!gsData) {
309         (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData);
310         return;
311     }
312
313     gsData->responsePropertiesCallback = handler->propertiesCallback;
314     gsData->responseCompleteCallback = handler->completeCallback;
315     gsData->callbackData = callbackData;
316
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;
323
324     // Set up the RequestParams
325     RequestParams params =
326     {
327         HttpRequestTypeGET,                           // httpRequestType
328         { bucketContext->bucketName,                  // bucketName
329           bucketContext->protocol,                    // protocol
330           bucketContext->uriStyle,                    // uriStyle
331           bucketContext->accessKeyId,                 // accessKeyId
332           bucketContext->secretAccessKey },           // secretAccessKey
333         0,                                            // key
334         0,                                            // queryParams
335         "logging",                                    // subResource
336         0,                                            // copySourceBucketName
337         0,                                            // copySourceKey
338         0,                                            // getConditions
339         0,                                            // startByte
340         0,                                            // byteCount
341         0,                                            // putProperties
342         &getBlsPropertiesCallback,                    // propertiesCallback
343         0,                                            // toS3Callback
344         0,                                            // toS3CallbackTotalSize
345         &getBlsDataCallback,                          // fromS3Callback
346         &getBlsCompleteCallback,                      // completeCallback
347         gsData                                        // callbackData
348     };
349
350     // Perform the request
351     request_perform(&params, requestContext);
352 }
353
354
355
356 // set server access logging---------------------------------------------------
357
358 static S3Status generateSalXmlDocument(const char *targetBucket,
359                                        const char *targetPrefix,
360                                        int aclGrantCount, 
361                                        const S3AclGrant *aclGrants,
362                                        int *xmlDocumentLenReturn,
363                                        char *xmlDocument,
364                                        int xmlDocumentBufferSize)
365 {
366     *xmlDocumentLenReturn = 0;
367
368 #define append(fmt, ...)                                        \
369     do {                                                        \
370         *xmlDocumentLenReturn += snprintf                       \
371             (&(xmlDocument[*xmlDocumentLenReturn]),             \
372              xmlDocumentBufferSize - *xmlDocumentLenReturn - 1, \
373              fmt, __VA_ARGS__);                                 \
374         if (*xmlDocumentLenReturn >= xmlDocumentBufferSize) {   \
375             return S3StatusXmlDocumentTooLarge;                 \
376         } \
377     } while (0)
378
379     append("%s", "<BucketLoggingStatus "
380            "xmlns=\"http://doc.s3.amazonaws.com/2006-03-01\">");
381
382     if (targetBucket && targetBucket[0]) {
383         append("<LoggingEnabled><TargetBucket>%s</TargetBucket>", targetBucket);
384         append("<TargetPrefix>%s</TargetPrefix>", 
385                targetPrefix ? targetPrefix : "");
386
387         if (aclGrantCount) {
388             append("%s", "<TargetGrants>");
389             int i;
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"
398                            "</EmailAddress>",
399                            grant->grantee.amazonCustomerByEmail.emailAddress);
400                     break;
401                 case S3GranteeTypeCanonicalUser:
402                     append("CanonicalUser\"><ID>%s</ID><DisplayName>%s"
403                            "</DisplayName>",
404                            grant->grantee.canonicalUser.id, 
405                            grant->grantee.canonicalUser.displayName);
406                     break;
407                 default: // case S3GranteeTypeAllAwsUsers/S3GranteeTypeAllUsers:
408                     append("Group\"><URI>http://acs.amazonaws.com/groups/"
409                            "global/%s</URI>",
410                            (grant->granteeType == S3GranteeTypeAllAwsUsers) ?
411                            "AuthenticatedUsers" : "AllUsers");
412                     break;
413                 }
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"));
421             }
422             append("%s", "</TargetGrants>");
423         }
424         append("%s", "</LoggingEnabled>");
425     }
426
427     append("%s", "</BucketLoggingStatus>");
428
429     return S3StatusOK;
430 }
431
432
433 typedef struct SetSalData
434 {
435     S3ResponsePropertiesCallback *responsePropertiesCallback;
436     S3ResponseCompleteCallback *responseCompleteCallback;
437     void *callbackData;
438
439     int salXmlDocumentLen;
440     char salXmlDocument[BLS_XML_DOC_MAXSIZE];
441     int salXmlDocumentBytesWritten;
442
443 } SetSalData;
444
445
446 static S3Status setSalPropertiesCallback
447     (const S3ResponseProperties *responseProperties, void *callbackData)
448 {
449     SetSalData *paData = (SetSalData *) callbackData;
450     
451     return (*(paData->responsePropertiesCallback))
452         (responseProperties, paData->callbackData);
453 }
454
455
456 static int setSalDataCallback(int bufferSize, char *buffer, void *callbackData)
457 {
458     SetSalData *paData = (SetSalData *) callbackData;
459
460     int remaining = (paData->salXmlDocumentLen - 
461                      paData->salXmlDocumentBytesWritten);
462
463     int toCopy = bufferSize > remaining ? remaining : bufferSize;
464     
465     if (!toCopy) {
466         return 0;
467     }
468
469     memcpy(buffer, &(paData->salXmlDocument
470                      [paData->salXmlDocumentBytesWritten]), toCopy);
471
472     paData->salXmlDocumentBytesWritten += toCopy;
473
474     return toCopy;
475 }
476
477
478 static void setSalCompleteCallback(S3Status requestStatus, 
479                                    const S3ErrorDetails *s3ErrorDetails,
480                                    void *callbackData)
481 {
482     SetSalData *paData = (SetSalData *) callbackData;
483
484     (*(paData->responseCompleteCallback))
485         (requestStatus, s3ErrorDetails, paData->callbackData);
486
487     free(paData);
488 }
489
490
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,
497                                   void *callbackData)
498 {
499     if (aclGrantCount > S3_MAX_ACL_GRANT_COUNT) {
500         (*(handler->completeCallback))
501             (S3StatusTooManyGrants, 0, callbackData);
502         return;
503     }
504
505     SetSalData *data = (SetSalData *) malloc(sizeof(SetSalData));
506     if (!data) {
507         (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData);
508         return;
509     }
510     
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) {
517         free(data);
518         (*(handler->completeCallback))(status, 0, callbackData);
519         return;
520     }
521
522     data->responsePropertiesCallback = handler->propertiesCallback;
523     data->responseCompleteCallback = handler->completeCallback;
524     data->callbackData = callbackData;
525
526     data->salXmlDocumentBytesWritten = 0;
527
528     // Set up the RequestParams
529     RequestParams params =
530     {
531         HttpRequestTypePUT,                           // httpRequestType
532         { bucketContext->bucketName,                  // bucketName
533           bucketContext->protocol,                    // protocol
534           bucketContext->uriStyle,                    // uriStyle
535           bucketContext->accessKeyId,                 // accessKeyId
536           bucketContext->secretAccessKey },           // secretAccessKey
537         0,                                            // key
538         0,                                            // queryParams
539         "logging",                                    // subResource
540         0,                                            // copySourceBucketName
541         0,                                            // copySourceKey
542         0,                                            // getConditions
543         0,                                            // startByte
544         0,                                            // byteCount
545         0,                                            // putProperties
546         &setSalPropertiesCallback,                    // propertiesCallback
547         &setSalDataCallback,                          // toS3Callback
548         data->salXmlDocumentLen,                      // toS3CallbackTotalSize
549         0,                                            // fromS3Callback
550         &setSalCompleteCallback,                      // completeCallback
551         data                                          // callbackData
552     };
553
554     // Perform the request
555     request_perform(&params, requestContext);
556 }