bucket_metadata.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. /** **************************************************************************
  2. * bucket_metadata.c
  3. *
  4. * Copyright 2008 Bryan Ischo <[email protected]>
  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 Lesser General Public License as published by the Free
  10. * Software Foundation, version 3 or above of the License. You can also
  11. * redistribute and/or modify it under the terms of the GNU General Public
  12. * License, version 2 or above of the License.
  13. *
  14. * In addition, as a special exception, the copyright holders give
  15. * permission to link the code of this library and its programs with the
  16. * OpenSSL library, and distribute linked combinations including the two.
  17. *
  18. * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY
  19. * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  20. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  21. * details.
  22. *
  23. * You should have received a copy of the GNU Lesser General Public License
  24. * version 3 along with libs3, in a file named COPYING. If not, see
  25. * <https://www.gnu.org/licenses/>.
  26. *
  27. * You should also have received a copy of the GNU General Public License
  28. * version 2 along with libs3, in a file named COPYING-GPLv2. If not, see
  29. * <https://www.gnu.org/licenses/>.
  30. *
  31. ************************************************************************** **/
  32. #include <stdlib.h>
  33. #include <string.h>
  34. #ifndef __APPLE__
  35. #include <openssl/md5.h>
  36. #include <openssl/bio.h>
  37. #include <openssl/evp.h>
  38. #include <openssl/buffer.h>
  39. #endif
  40. #include "libs3.h"
  41. #include "request.h"
  42. // Use a rather arbitrary max size for the document of 64K
  43. #define ACL_XML_DOC_MAXSIZE (64 * 1024)
  44. // get acl -------------------------------------------------------------------
  45. typedef struct GetAclData
  46. {
  47. SimpleXml simpleXml;
  48. S3ResponsePropertiesCallback *responsePropertiesCallback;
  49. S3ResponseCompleteCallback *responseCompleteCallback;
  50. void *callbackData;
  51. int *aclGrantCountReturn;
  52. S3AclGrant *aclGrants;
  53. char *ownerId;
  54. char *ownerDisplayName;
  55. string_buffer(xmlDocument, ACL_XML_DOC_MAXSIZE);
  56. } GetAclData;
  57. static S3Status getAclPropertiesCallback
  58. (const S3ResponseProperties *responseProperties, void *callbackData)
  59. {
  60. GetAclData *gaData = (GetAclData *) callbackData;
  61. return (*(gaData->responsePropertiesCallback))
  62. (responseProperties, gaData->callbackData);
  63. }
  64. static S3Status getAclDataCallback(int bufferSize, const char *buffer,
  65. void *callbackData)
  66. {
  67. GetAclData *gaData = (GetAclData *) callbackData;
  68. int fit;
  69. string_buffer_append(gaData->xmlDocument, buffer, bufferSize, fit);
  70. return fit ? S3StatusOK : S3StatusXmlDocumentTooLarge;
  71. }
  72. static void getAclCompleteCallback(S3Status requestStatus,
  73. const S3ErrorDetails *s3ErrorDetails,
  74. void *callbackData)
  75. {
  76. GetAclData *gaData = (GetAclData *) callbackData;
  77. if (requestStatus == S3StatusOK) {
  78. // Parse the document
  79. requestStatus = S3_convert_acl
  80. (gaData->xmlDocument, gaData->ownerId, gaData->ownerDisplayName,
  81. gaData->aclGrantCountReturn, gaData->aclGrants);
  82. }
  83. (*(gaData->responseCompleteCallback))
  84. (requestStatus, s3ErrorDetails, gaData->callbackData);
  85. free(gaData);
  86. }
  87. void S3_get_acl(const S3BucketContext *bucketContext, const char *key,
  88. char *ownerId, char *ownerDisplayName,
  89. int *aclGrantCountReturn, S3AclGrant *aclGrants,
  90. S3RequestContext *requestContext,
  91. int timeoutMs,
  92. const S3ResponseHandler *handler, void *callbackData)
  93. {
  94. // Create the callback data
  95. GetAclData *gaData = (GetAclData *) malloc(sizeof(GetAclData));
  96. if (!gaData) {
  97. (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData);
  98. return;
  99. }
  100. gaData->responsePropertiesCallback = handler->propertiesCallback;
  101. gaData->responseCompleteCallback = handler->completeCallback;
  102. gaData->callbackData = callbackData;
  103. gaData->aclGrantCountReturn = aclGrantCountReturn;
  104. gaData->aclGrants = aclGrants;
  105. gaData->ownerId = ownerId;
  106. gaData->ownerDisplayName = ownerDisplayName;
  107. string_buffer_initialize(gaData->xmlDocument);
  108. *aclGrantCountReturn = 0;
  109. // Set up the RequestParams
  110. RequestParams params =
  111. {
  112. HttpRequestTypeGET, // httpRequestType
  113. { bucketContext->hostName, // hostName
  114. bucketContext->bucketName, // bucketName
  115. bucketContext->protocol, // protocol
  116. bucketContext->uriStyle, // uriStyle
  117. bucketContext->accessKeyId, // accessKeyId
  118. bucketContext->secretAccessKey, // secretAccessKey
  119. bucketContext->securityToken, // securityToken
  120. bucketContext->authRegion }, // authRegion
  121. key, // key
  122. 0, // queryParams
  123. "acl", // subResource
  124. 0, // copySourceBucketName
  125. 0, // copySourceKey
  126. 0, // getConditions
  127. 0, // startByte
  128. 0, // byteCount
  129. 0, // putProperties
  130. &getAclPropertiesCallback, // propertiesCallback
  131. 0, // toS3Callback
  132. 0, // toS3CallbackTotalSize
  133. &getAclDataCallback, // fromS3Callback
  134. &getAclCompleteCallback, // completeCallback
  135. gaData, // callbackData
  136. timeoutMs // timeoutMs
  137. };
  138. // Perform the request
  139. request_perform(&params, requestContext);
  140. }
  141. // set acl -------------------------------------------------------------------
  142. static S3Status generateAclXmlDocument(const char *ownerId,
  143. const char *ownerDisplayName,
  144. int aclGrantCount,
  145. const S3AclGrant *aclGrants,
  146. int *xmlDocumentLenReturn,
  147. char *xmlDocument,
  148. int xmlDocumentBufferSize)
  149. {
  150. *xmlDocumentLenReturn = 0;
  151. #define append(fmt, ...) \
  152. do { \
  153. *xmlDocumentLenReturn += snprintf \
  154. (&(xmlDocument[*xmlDocumentLenReturn]), \
  155. xmlDocumentBufferSize - *xmlDocumentLenReturn - 1, \
  156. fmt, __VA_ARGS__); \
  157. if (*xmlDocumentLenReturn >= xmlDocumentBufferSize) { \
  158. return S3StatusXmlDocumentTooLarge; \
  159. } \
  160. } while (0)
  161. append("<AccessControlPolicy><Owner><ID>%s</ID><DisplayName>%s"
  162. "</DisplayName></Owner><AccessControlList>", ownerId,
  163. ownerDisplayName);
  164. int i;
  165. for (i = 0; i < aclGrantCount; i++) {
  166. append("%s", "<Grant><Grantee xmlns:xsi=\"http://www.w3.org/2001/"
  167. "XMLSchema-instance\" xsi:type=\"");
  168. const S3AclGrant *grant = &(aclGrants[i]);
  169. switch (grant->granteeType) {
  170. case S3GranteeTypeAmazonCustomerByEmail:
  171. append("AmazonCustomerByEmail\"><EmailAddress>%s</EmailAddress>",
  172. grant->grantee.amazonCustomerByEmail.emailAddress);
  173. break;
  174. case S3GranteeTypeCanonicalUser:
  175. append("CanonicalUser\"><ID>%s</ID><DisplayName>%s</DisplayName>",
  176. grant->grantee.canonicalUser.id,
  177. grant->grantee.canonicalUser.displayName);
  178. break;
  179. default: { // case S3GranteeTypeAllAwsUsers/S3GranteeTypeAllUsers:
  180. const char *grantee;
  181. switch (grant->granteeType) {
  182. case S3GranteeTypeAllAwsUsers:
  183. grantee = ACS_GROUP_AWS_USERS;
  184. break;
  185. case S3GranteeTypeAllUsers:
  186. grantee = ACS_GROUP_ALL_USERS;
  187. break;
  188. default:
  189. grantee = ACS_GROUP_LOG_DELIVERY;
  190. break;
  191. }
  192. append("Group\"><URI>%s</URI>", grantee);
  193. }
  194. break;
  195. }
  196. append("</Grantee><Permission>%s</Permission></Grant>",
  197. ((grant->permission == S3PermissionRead) ? "READ" :
  198. (grant->permission == S3PermissionWrite) ? "WRITE" :
  199. (grant->permission == S3PermissionReadACP) ? "READ_ACP" :
  200. (grant->permission == S3PermissionWriteACP) ? "WRITE_ACP" :
  201. "FULL_CONTROL"));
  202. }
  203. append("%s", "</AccessControlList></AccessControlPolicy>");
  204. return S3StatusOK;
  205. }
  206. typedef struct SetXmlData
  207. {
  208. S3ResponsePropertiesCallback *responsePropertiesCallback;
  209. S3ResponseCompleteCallback *responseCompleteCallback;
  210. void *callbackData;
  211. int xmlDocumentLen;
  212. const char *xmlDocument;
  213. int xmlDocumentBytesWritten;
  214. } SetXmlData;
  215. static S3Status setXmlPropertiesCallback
  216. (const S3ResponseProperties *responseProperties, void *callbackData)
  217. {
  218. SetXmlData *paData = (SetXmlData *) callbackData;
  219. return (*(paData->responsePropertiesCallback))
  220. (responseProperties, paData->callbackData);
  221. }
  222. static int setXmlDataCallback(int bufferSize, char *buffer, void *callbackData)
  223. {
  224. SetXmlData *paData = (SetXmlData *) callbackData;
  225. int remaining = (paData->xmlDocumentLen -
  226. paData->xmlDocumentBytesWritten);
  227. int toCopy = bufferSize > remaining ? remaining : bufferSize;
  228. if (!toCopy) {
  229. return 0;
  230. }
  231. memcpy(buffer, &(paData->xmlDocument
  232. [paData->xmlDocumentBytesWritten]), toCopy);
  233. paData->xmlDocumentBytesWritten += toCopy;
  234. return toCopy;
  235. }
  236. static void setXmlCompleteCallback(S3Status requestStatus,
  237. const S3ErrorDetails *s3ErrorDetails,
  238. void *callbackData)
  239. {
  240. SetXmlData *paData = (SetXmlData *) callbackData;
  241. (*(paData->responseCompleteCallback))
  242. (requestStatus, s3ErrorDetails, paData->callbackData);
  243. free(paData);
  244. }
  245. void S3_set_acl(const S3BucketContext *bucketContext, const char *key,
  246. const char *ownerId, const char *ownerDisplayName,
  247. int aclGrantCount, const S3AclGrant *aclGrants,
  248. S3RequestContext *requestContext,
  249. int timeoutMs,
  250. const S3ResponseHandler *handler, void *callbackData)
  251. {
  252. char aclBuffer[ACL_XML_DOC_MAXSIZE];
  253. if (aclGrantCount > S3_MAX_ACL_GRANT_COUNT) {
  254. (*(handler->completeCallback))
  255. (S3StatusTooManyGrants, 0, callbackData);
  256. return;
  257. }
  258. SetXmlData *data = (SetXmlData *) malloc(sizeof(SetXmlData));
  259. if (!data) {
  260. (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData);
  261. return;
  262. }
  263. data->xmlDocument = aclBuffer;
  264. // Convert aclGrants to XML document
  265. S3Status status = generateAclXmlDocument
  266. (ownerId, ownerDisplayName, aclGrantCount, aclGrants,
  267. &(data->xmlDocumentLen), aclBuffer,
  268. sizeof(aclBuffer));
  269. if (status != S3StatusOK) {
  270. free(data);
  271. (*(handler->completeCallback))(status, 0, callbackData);
  272. return;
  273. }
  274. data->responsePropertiesCallback = handler->propertiesCallback;
  275. data->responseCompleteCallback = handler->completeCallback;
  276. data->callbackData = callbackData;
  277. data->xmlDocumentBytesWritten = 0;
  278. // Set up the RequestParams
  279. RequestParams params =
  280. {
  281. HttpRequestTypePUT, // httpRequestType
  282. { bucketContext->hostName, // hostName
  283. bucketContext->bucketName, // bucketName
  284. bucketContext->protocol, // protocol
  285. bucketContext->uriStyle, // uriStyle
  286. bucketContext->accessKeyId, // accessKeyId
  287. bucketContext->secretAccessKey, // secretAccessKey
  288. bucketContext->securityToken, // securityToken
  289. bucketContext->authRegion }, // authRegion
  290. key, // key
  291. 0, // queryParams
  292. "acl", // subResource
  293. 0, // copySourceBucketName
  294. 0, // copySourceKey
  295. 0, // getConditions
  296. 0, // startByte
  297. 0, // byteCount
  298. 0, // putProperties
  299. &setXmlPropertiesCallback, // propertiesCallback
  300. &setXmlDataCallback, // toS3Callback
  301. data->xmlDocumentLen, // toS3CallbackTotalSize
  302. 0, // fromS3Callback
  303. &setXmlCompleteCallback, // completeCallback
  304. data, // callbackData
  305. timeoutMs // timeoutMs
  306. };
  307. // Perform the request
  308. request_perform(&params, requestContext);
  309. }
  310. // get lifecycle -------------------------------------------------------------------
  311. typedef struct GetLifecycleData
  312. {
  313. S3ResponsePropertiesCallback *responsePropertiesCallback;
  314. S3ResponseCompleteCallback *responseCompleteCallback;
  315. void *callbackData;
  316. char *lifecycleXmlDocumentReturn;
  317. int lifecycleXmlDocumentBufferSize;
  318. int lifecycleXmlDocumentWritten;
  319. } GetLifecycleData;
  320. static S3Status getLifecyclePropertiesCallback
  321. (const S3ResponseProperties *responseProperties, void *callbackData)
  322. {
  323. GetLifecycleData *gaData = (GetLifecycleData *) callbackData;
  324. return (*(gaData->responsePropertiesCallback))
  325. (responseProperties, gaData->callbackData);
  326. }
  327. static S3Status getLifecycleDataCallback(int bufferSize, const char *buffer,
  328. void *callbackData)
  329. {
  330. GetLifecycleData *gaData = (GetLifecycleData *) callbackData;
  331. if ((gaData->lifecycleXmlDocumentWritten + bufferSize) >= gaData->lifecycleXmlDocumentBufferSize)
  332. return S3StatusXmlDocumentTooLarge;
  333. snprintf(gaData->lifecycleXmlDocumentReturn + gaData->lifecycleXmlDocumentWritten, bufferSize + 1, "%s", buffer);
  334. gaData->lifecycleXmlDocumentWritten += bufferSize;
  335. return S3StatusOK;
  336. }
  337. static void getLifecycleCompleteCallback(S3Status requestStatus,
  338. const S3ErrorDetails *s3ErrorDetails,
  339. void *callbackData)
  340. {
  341. GetLifecycleData *gaData = (GetLifecycleData *) callbackData;
  342. (*(gaData->responseCompleteCallback))
  343. (requestStatus, s3ErrorDetails, gaData->callbackData);
  344. free(gaData);
  345. }
  346. void S3_get_lifecycle(const S3BucketContext *bucketContext,
  347. char *lifecycleXmlDocumentReturn, int lifecycleXmlDocumentBufferSize,
  348. S3RequestContext *requestContext,
  349. int timeoutMs,
  350. const S3ResponseHandler *handler, void *callbackData)
  351. {
  352. // Create the callback data
  353. GetLifecycleData *gaData = (GetLifecycleData *) malloc(sizeof(GetLifecycleData));
  354. if (!gaData) {
  355. (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData);
  356. return;
  357. }
  358. gaData->responsePropertiesCallback = handler->propertiesCallback;
  359. gaData->responseCompleteCallback = handler->completeCallback;
  360. gaData->callbackData = callbackData;
  361. gaData->lifecycleXmlDocumentReturn = lifecycleXmlDocumentReturn;
  362. gaData->lifecycleXmlDocumentBufferSize = lifecycleXmlDocumentBufferSize;
  363. gaData->lifecycleXmlDocumentWritten = 0;
  364. // Set up the RequestParams
  365. RequestParams params =
  366. {
  367. HttpRequestTypeGET, // httpRequestType
  368. { bucketContext->hostName, // hostName
  369. bucketContext->bucketName, // bucketName
  370. bucketContext->protocol, // protocol
  371. bucketContext->uriStyle, // uriStyle
  372. bucketContext->accessKeyId, // accessKeyId
  373. bucketContext->secretAccessKey, // secretAccessKey
  374. bucketContext->securityToken, // securityToken
  375. bucketContext->authRegion }, // authRegion
  376. 0, // key
  377. 0, // queryParams
  378. "lifecycle", // subResource
  379. 0, // copySourceBucketName
  380. 0, // copySourceKey
  381. 0, // getConditions
  382. 0, // startByte
  383. 0, // byteCount
  384. 0, // putProperties
  385. &getLifecyclePropertiesCallback, // propertiesCallback
  386. 0, // toS3Callback
  387. 0, // toS3CallbackTotalSize
  388. &getLifecycleDataCallback, // fromS3Callback
  389. &getLifecycleCompleteCallback, // completeCallback
  390. gaData, // callbackData
  391. timeoutMs // timeoutMs
  392. };
  393. // Perform the request
  394. request_perform(&params, requestContext);
  395. }
  396. #ifndef __APPLE__
  397. // Calculate MD5 and encode it as base64
  398. void generate_content_md5(const char* data, int size,
  399. char* retBuffer, int retBufferSize) {
  400. MD5_CTX mdContext;
  401. BIO *bio, *b64;
  402. BUF_MEM *bufferPtr;
  403. char md5Buffer[MD5_DIGEST_LENGTH];
  404. MD5_Init(&mdContext);
  405. MD5_Update(&mdContext, data, size);
  406. MD5_Final((unsigned char*)md5Buffer, &mdContext);
  407. b64 = BIO_new(BIO_f_base64());
  408. bio = BIO_new(BIO_s_mem());
  409. bio = BIO_push(b64, bio);
  410. BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Ignore newlines - write everything in one line
  411. BIO_write(bio, md5Buffer, sizeof(md5Buffer));
  412. (void) BIO_flush(bio);
  413. BIO_get_mem_ptr(bio, &bufferPtr);
  414. (void) BIO_set_close(bio, BIO_NOCLOSE);
  415. if ((unsigned int)retBufferSize + 1 < bufferPtr->length) {
  416. retBuffer[0] = '\0';
  417. BIO_free_all(bio);
  418. return;
  419. }
  420. memcpy(retBuffer, bufferPtr->data, bufferPtr->length);
  421. retBuffer[bufferPtr->length] = '\0';
  422. BIO_free_all(bio);
  423. }
  424. #endif
  425. void S3_set_lifecycle(const S3BucketContext *bucketContext,
  426. const char *lifecycleXmlDocument,
  427. S3RequestContext *requestContext,
  428. int timeoutMs,
  429. const S3ResponseHandler *handler, void *callbackData)
  430. {
  431. #ifdef __APPLE__
  432. /* This request requires calculating MD5 sum.
  433. * MD5 sum requires OpenSSL library, which is not used on Apple.
  434. * TODO Implement some MD5+Base64 caculation on Apple
  435. */
  436. (*(handler->completeCallback))(S3StatusNotSupported, 0, callbackData);
  437. return;
  438. #else
  439. char md5Base64[MD5_DIGEST_LENGTH * 2];
  440. SetXmlData *data = (SetXmlData *) malloc(sizeof(SetXmlData));
  441. if (!data) {
  442. (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData);
  443. return;
  444. }
  445. data->xmlDocument = lifecycleXmlDocument;
  446. data->xmlDocumentLen = strlen(lifecycleXmlDocument);
  447. data->responsePropertiesCallback = handler->propertiesCallback;
  448. data->responseCompleteCallback = handler->completeCallback;
  449. data->callbackData = callbackData;
  450. data->xmlDocumentBytesWritten = 0;
  451. generate_content_md5(data->xmlDocument, data->xmlDocumentLen,
  452. md5Base64, sizeof (md5Base64));
  453. // Set up S3PutProperties
  454. S3PutProperties properties =
  455. {
  456. 0, // contentType
  457. md5Base64, // md5
  458. 0, // cacheControl
  459. 0, // contentDispositionFilename
  460. 0, // contentEncoding
  461. -1, // expires
  462. (S3CannedAcl)0, // cannedAcl WINSCP (cast)
  463. 0, // metaDataCount
  464. 0, // metaData
  465. 0 // useServerSideEncryption
  466. };
  467. // Set up the RequestParams
  468. RequestParams params =
  469. {
  470. HttpRequestTypePUT, // httpRequestType
  471. { bucketContext->hostName, // hostName
  472. bucketContext->bucketName, // bucketName
  473. bucketContext->protocol, // protocol
  474. bucketContext->uriStyle, // uriStyle
  475. bucketContext->accessKeyId, // accessKeyId
  476. bucketContext->secretAccessKey, // secretAccessKey
  477. bucketContext->securityToken, // securityToken
  478. bucketContext->authRegion }, // authRegion
  479. 0, // key
  480. 0, // queryParams
  481. "lifecycle", // subResource
  482. 0, // copySourceBucketName
  483. 0, // copySourceKey
  484. 0, // getConditions
  485. 0, // startByte
  486. 0, // byteCount
  487. &properties, // putProperties
  488. &setXmlPropertiesCallback, // propertiesCallback
  489. &setXmlDataCallback, // toS3Callback
  490. data->xmlDocumentLen, // toS3CallbackTotalSize
  491. 0, // fromS3Callback
  492. &setXmlCompleteCallback, // completeCallback
  493. data, // callbackData
  494. timeoutMs // timeoutMs
  495. };
  496. // Perform the request
  497. request_perform(&params, requestContext);
  498. #endif
  499. }