bucket.c 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782
  1. /** **************************************************************************
  2. * bucket.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 <string.h>
  33. #include <stdlib.h>
  34. #include "libs3.h"
  35. #include "request.h"
  36. #include "simplexml.h"
  37. // test bucket ---------------------------------------------------------------
  38. typedef struct TestBucketData
  39. {
  40. SimpleXml simpleXml;
  41. S3ResponsePropertiesCallback *responsePropertiesCallback;
  42. S3ResponseCompleteCallback *responseCompleteCallback;
  43. void *callbackData;
  44. int locationConstraintReturnSize;
  45. char *locationConstraintReturn;
  46. string_buffer(locationConstraint, 256);
  47. } TestBucketData;
  48. static S3Status testBucketXmlCallback(const char *elementPath,
  49. const char *data, int dataLen,
  50. void *callbackData)
  51. {
  52. TestBucketData *tbData = (TestBucketData *) callbackData;
  53. int fit;
  54. if (data && !strcmp(elementPath, "LocationConstraint")) {
  55. string_buffer_append(tbData->locationConstraint, data, dataLen, fit);
  56. }
  57. /* Avoid compiler error about variable set but not used */
  58. (void) fit;
  59. return S3StatusOK;
  60. }
  61. static S3Status testBucketPropertiesCallback
  62. (const S3ResponseProperties *responseProperties, void *callbackData)
  63. {
  64. TestBucketData *tbData = (TestBucketData *) callbackData;
  65. return (*(tbData->responsePropertiesCallback))
  66. (responseProperties, tbData->callbackData);
  67. }
  68. static S3Status testBucketDataCallback(int bufferSize, const char *buffer,
  69. void *callbackData)
  70. {
  71. TestBucketData *tbData = (TestBucketData *) callbackData;
  72. return simplexml_add(&(tbData->simpleXml), buffer, bufferSize);
  73. }
  74. static void testBucketCompleteCallback(S3Status requestStatus,
  75. const S3ErrorDetails *s3ErrorDetails,
  76. void *callbackData)
  77. {
  78. TestBucketData *tbData = (TestBucketData *) callbackData;
  79. // Copy the location constraint into the return buffer
  80. snprintf(tbData->locationConstraintReturn,
  81. tbData->locationConstraintReturnSize, "%s",
  82. tbData->locationConstraint);
  83. (*(tbData->responseCompleteCallback))
  84. (requestStatus, s3ErrorDetails, tbData->callbackData);
  85. simplexml_deinitialize(&(tbData->simpleXml));
  86. free(tbData);
  87. }
  88. void S3_test_bucket(S3Protocol protocol, S3UriStyle uriStyle,
  89. const char *accessKeyId, const char *secretAccessKey,
  90. const char *securityToken, const char *hostName,
  91. const char *bucketName, const char *authRegion,
  92. int locationConstraintReturnSize,
  93. char *locationConstraintReturn,
  94. S3RequestContext *requestContext,
  95. int timeoutMs,
  96. const S3ResponseHandler *handler, void *callbackData)
  97. {
  98. // Create the callback data
  99. TestBucketData *tbData =
  100. (TestBucketData *) malloc(sizeof(TestBucketData));
  101. if (!tbData) {
  102. (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData);
  103. return;
  104. }
  105. simplexml_initialize(&(tbData->simpleXml), &testBucketXmlCallback, tbData);
  106. tbData->responsePropertiesCallback = handler->propertiesCallback;
  107. tbData->responseCompleteCallback = handler->completeCallback;
  108. tbData->callbackData = callbackData;
  109. tbData->locationConstraintReturnSize = locationConstraintReturnSize;
  110. tbData->locationConstraintReturn = locationConstraintReturn;
  111. string_buffer_initialize(tbData->locationConstraint);
  112. // Set up the RequestParams
  113. RequestParams params =
  114. {
  115. HttpRequestTypeGET, // httpRequestType
  116. { hostName, // hostName
  117. bucketName, // bucketName
  118. protocol, // protocol
  119. uriStyle, // uriStyle
  120. accessKeyId, // accessKeyId
  121. secretAccessKey, // secretAccessKey
  122. securityToken, // securityToken
  123. authRegion }, // authRegion
  124. 0, // key
  125. 0, // queryParams
  126. "location", // subResource
  127. 0, // copySourceBucketName
  128. 0, // copySourceKey
  129. 0, // getConditions
  130. 0, // startByte
  131. 0, // byteCount
  132. 0, // putProperties
  133. &testBucketPropertiesCallback, // propertiesCallback
  134. 0, // toS3Callback
  135. 0, // toS3CallbackTotalSize
  136. &testBucketDataCallback, // fromS3Callback
  137. &testBucketCompleteCallback, // completeCallback
  138. tbData, // callbackData
  139. timeoutMs // timeoutMs
  140. };
  141. // Perform the request
  142. request_perform(&params, requestContext);
  143. }
  144. // create bucket -------------------------------------------------------------
  145. typedef struct CreateBucketData
  146. {
  147. S3ResponsePropertiesCallback *responsePropertiesCallback;
  148. S3ResponseCompleteCallback *responseCompleteCallback;
  149. void *callbackData;
  150. char doc[1024];
  151. int docLen, docBytesWritten;
  152. } CreateBucketData;
  153. static S3Status createBucketPropertiesCallback
  154. (const S3ResponseProperties *responseProperties, void *callbackData)
  155. {
  156. CreateBucketData *cbData = (CreateBucketData *) callbackData;
  157. return (*(cbData->responsePropertiesCallback))
  158. (responseProperties, cbData->callbackData);
  159. }
  160. static int createBucketDataCallback(int bufferSize, char *buffer,
  161. void *callbackData)
  162. {
  163. CreateBucketData *cbData = (CreateBucketData *) callbackData;
  164. if (!cbData->docLen) {
  165. return 0;
  166. }
  167. int remaining = (cbData->docLen - cbData->docBytesWritten);
  168. int toCopy = bufferSize > remaining ? remaining : bufferSize;
  169. if (!toCopy) {
  170. return 0;
  171. }
  172. memcpy(buffer, &(cbData->doc[cbData->docBytesWritten]), toCopy);
  173. cbData->docBytesWritten += toCopy;
  174. return toCopy;
  175. }
  176. static void createBucketCompleteCallback(S3Status requestStatus,
  177. const S3ErrorDetails *s3ErrorDetails,
  178. void *callbackData)
  179. {
  180. CreateBucketData *cbData = (CreateBucketData *) callbackData;
  181. (*(cbData->responseCompleteCallback))
  182. (requestStatus, s3ErrorDetails, cbData->callbackData);
  183. free(cbData);
  184. }
  185. static S3Status createBucketFromS3Callback(int bufferSize, const char *buffer,
  186. void *callbackData)
  187. {
  188. // Sometimes S3 sends response body. We sillently ignore it.
  189. (void)bufferSize; // avoid unused parameter warning
  190. (void)buffer; // avoid unused parameter warning
  191. (void)callbackData; // avoid unused parameter warning
  192. return S3StatusOK;
  193. }
  194. void S3_create_bucket(S3Protocol protocol, const char *accessKeyId,
  195. const char *secretAccessKey, const char *securityToken,
  196. const char *hostName, const char *bucketName,
  197. const char *authRegion, S3CannedAcl cannedAcl,
  198. const char *locationConstraint,
  199. S3RequestContext *requestContext,
  200. int timeoutMs,
  201. const S3ResponseHandler *handler, void *callbackData)
  202. {
  203. // Create the callback data
  204. CreateBucketData *cbData =
  205. (CreateBucketData *) malloc(sizeof(CreateBucketData));
  206. if (!cbData) {
  207. (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData);
  208. return;
  209. }
  210. cbData->responsePropertiesCallback = handler->propertiesCallback;
  211. cbData->responseCompleteCallback = handler->completeCallback;
  212. cbData->callbackData = callbackData;
  213. if (locationConstraint) {
  214. cbData->docLen =
  215. snprintf(cbData->doc, sizeof(cbData->doc),
  216. "<CreateBucketConfiguration><LocationConstraint>"
  217. "%s</LocationConstraint></CreateBucketConfiguration>",
  218. locationConstraint);
  219. cbData->docBytesWritten = 0;
  220. }
  221. else {
  222. cbData->docLen = 0;
  223. }
  224. // Set up S3PutProperties
  225. S3PutProperties properties =
  226. {
  227. 0, // contentType
  228. 0, // md5
  229. 0, // cacheControl
  230. 0, // contentDispositionFilename
  231. 0, // contentEncoding
  232. -1, // expires
  233. cannedAcl, // cannedAcl
  234. 0, // metaDataCount
  235. 0, // metaData
  236. 0 // useServerSideEncryption
  237. };
  238. // Set up the RequestParams
  239. RequestParams params =
  240. {
  241. HttpRequestTypePUT, // httpRequestType
  242. { hostName, // hostName
  243. bucketName, // bucketName
  244. protocol, // protocol
  245. S3UriStylePath, // uriStyle
  246. accessKeyId, // accessKeyId
  247. secretAccessKey, // secretAccessKey
  248. securityToken, // securityToken
  249. authRegion }, // authRegion
  250. 0, // key
  251. 0, // queryParams
  252. 0, // subResource
  253. 0, // copySourceBucketName
  254. 0, // copySourceKey
  255. 0, // getConditions
  256. 0, // startByte
  257. 0, // byteCount
  258. &properties, // putProperties
  259. &createBucketPropertiesCallback, // propertiesCallback
  260. &createBucketDataCallback, // toS3Callback
  261. cbData->docLen, // toS3CallbackTotalSize
  262. createBucketFromS3Callback, // fromS3Callback
  263. &createBucketCompleteCallback, // completeCallback
  264. cbData, // callbackData
  265. timeoutMs // timeoutMs
  266. };
  267. // Perform the request
  268. request_perform(&params, requestContext);
  269. }
  270. // delete bucket -------------------------------------------------------------
  271. typedef struct DeleteBucketData
  272. {
  273. S3ResponsePropertiesCallback *responsePropertiesCallback;
  274. S3ResponseCompleteCallback *responseCompleteCallback;
  275. void *callbackData;
  276. } DeleteBucketData;
  277. static S3Status deleteBucketPropertiesCallback
  278. (const S3ResponseProperties *responseProperties, void *callbackData)
  279. {
  280. DeleteBucketData *dbData = (DeleteBucketData *) callbackData;
  281. return (*(dbData->responsePropertiesCallback))
  282. (responseProperties, dbData->callbackData);
  283. }
  284. static void deleteBucketCompleteCallback(S3Status requestStatus,
  285. const S3ErrorDetails *s3ErrorDetails,
  286. void *callbackData)
  287. {
  288. DeleteBucketData *dbData = (DeleteBucketData *) callbackData;
  289. (*(dbData->responseCompleteCallback))
  290. (requestStatus, s3ErrorDetails, dbData->callbackData);
  291. free(dbData);
  292. }
  293. void S3_delete_bucket(S3Protocol protocol, S3UriStyle uriStyle,
  294. const char *accessKeyId, const char *secretAccessKey,
  295. const char *securityToken, const char *hostName,
  296. const char *bucketName, const char *authRegion,
  297. S3RequestContext *requestContext,
  298. int timeoutMs,
  299. const S3ResponseHandler *handler, void *callbackData)
  300. {
  301. // Create the callback data
  302. DeleteBucketData *dbData =
  303. (DeleteBucketData *) malloc(sizeof(DeleteBucketData));
  304. if (!dbData) {
  305. (*(handler->completeCallback))(S3StatusOutOfMemory, 0, callbackData);
  306. return;
  307. }
  308. dbData->responsePropertiesCallback = handler->propertiesCallback;
  309. dbData->responseCompleteCallback = handler->completeCallback;
  310. dbData->callbackData = callbackData;
  311. // Set up the RequestParams
  312. RequestParams params =
  313. {
  314. HttpRequestTypeDELETE, // httpRequestType
  315. { hostName, // hostName
  316. bucketName, // bucketName
  317. protocol, // protocol
  318. uriStyle, // uriStyle
  319. accessKeyId, // accessKeyId
  320. secretAccessKey, // secretAccessKey
  321. securityToken, // securityToken
  322. authRegion }, // authRegion
  323. 0, // key
  324. 0, // queryParams
  325. 0, // subResource
  326. 0, // copySourceBucketName
  327. 0, // copySourceKey
  328. 0, // getConditions
  329. 0, // startByte
  330. 0, // byteCount
  331. 0, // putProperties
  332. &deleteBucketPropertiesCallback, // propertiesCallback
  333. 0, // toS3Callback
  334. 0, // toS3CallbackTotalSize
  335. 0, // fromS3Callback
  336. &deleteBucketCompleteCallback, // completeCallback
  337. dbData, // callbackData
  338. timeoutMs // timeoutMs
  339. };
  340. // Perform the request
  341. request_perform(&params, requestContext);
  342. }
  343. // list bucket ----------------------------------------------------------------
  344. typedef struct ListBucketContents
  345. {
  346. string_buffer(key, 1024);
  347. string_buffer(lastModified, 256);
  348. string_buffer(eTag, 256);
  349. string_buffer(size, 24);
  350. string_buffer(ownerId, 256);
  351. string_buffer(ownerDisplayName, 256);
  352. } ListBucketContents;
  353. static void initialize_list_bucket_contents(ListBucketContents *contents)
  354. {
  355. string_buffer_initialize(contents->key);
  356. string_buffer_initialize(contents->lastModified);
  357. string_buffer_initialize(contents->eTag);
  358. string_buffer_initialize(contents->size);
  359. string_buffer_initialize(contents->ownerId);
  360. string_buffer_initialize(contents->ownerDisplayName);
  361. }
  362. // We read up to 32 Contents at a time
  363. #define MAX_CONTENTS 32
  364. // We read up to 8 CommonPrefixes at a time
  365. #define MAX_COMMON_PREFIXES 8
  366. typedef struct ListBucketData
  367. {
  368. SimpleXml simpleXml;
  369. S3ResponsePropertiesCallback *responsePropertiesCallback;
  370. S3ListBucketCallback *listBucketCallback;
  371. S3ResponseCompleteCallback *responseCompleteCallback;
  372. void *callbackData;
  373. string_buffer(isTruncated, 64);
  374. string_buffer(nextMarker, 1024);
  375. int contentsCount;
  376. ListBucketContents contents[MAX_CONTENTS];
  377. int commonPrefixesCount;
  378. char commonPrefixes[MAX_COMMON_PREFIXES][1024];
  379. int commonPrefixLens[MAX_COMMON_PREFIXES];
  380. } ListBucketData;
  381. static void initialize_list_bucket_data(ListBucketData *lbData)
  382. {
  383. lbData->contentsCount = 0;
  384. initialize_list_bucket_contents(lbData->contents);
  385. lbData->commonPrefixesCount = 0;
  386. lbData->commonPrefixes[0][0] = 0;
  387. lbData->commonPrefixLens[0] = 0;
  388. }
  389. static S3Status make_list_bucket_callback(ListBucketData *lbData)
  390. {
  391. int i;
  392. // Convert IsTruncated
  393. int isTruncated = (!strcmp(lbData->isTruncated, "true") ||
  394. !strcmp(lbData->isTruncated, "1")) ? 1 : 0;
  395. // Convert the contents
  396. S3ListBucketContent * contents = new S3ListBucketContent[lbData->contentsCount]; // WINSCP (heap allocation)
  397. int contentsCount = lbData->contentsCount;
  398. for (i = 0; i < contentsCount; i++) {
  399. S3ListBucketContent *contentDest = &(contents[i]);
  400. ListBucketContents *contentSrc = &(lbData->contents[i]);
  401. contentDest->key = contentSrc->key;
  402. contentDest->lastModified =
  403. parseIso8601Time(contentSrc->lastModified);
  404. contentDest->lastModifiedStr = contentSrc->lastModified; // WINSCP
  405. contentDest->eTag = contentSrc->eTag;
  406. contentDest->size = parseUnsignedInt(contentSrc->size);
  407. contentDest->ownerId =
  408. contentSrc->ownerId[0] ?contentSrc->ownerId : 0;
  409. contentDest->ownerDisplayName = (contentSrc->ownerDisplayName[0] ?
  410. contentSrc->ownerDisplayName : 0);
  411. }
  412. // Make the common prefixes array
  413. int commonPrefixesCount = lbData->commonPrefixesCount;
  414. char **commonPrefixes = new char*[commonPrefixesCount]; // WINSCP (heap allocation)
  415. for (i = 0; i < commonPrefixesCount; i++) {
  416. commonPrefixes[i] = lbData->commonPrefixes[i];
  417. }
  418. S3Status status = (*(lbData->listBucketCallback))
  419. (isTruncated, lbData->nextMarker,
  420. contentsCount, contents, commonPrefixesCount,
  421. (const char **) commonPrefixes, lbData->callbackData);
  422. delete[] contents; // WINSCP (heap allocation)
  423. delete[] commonPrefixes;
  424. return status;
  425. }
  426. static S3Status listBucketXmlCallback(const char *elementPath,
  427. const char *data, int dataLen,
  428. void *callbackData)
  429. {
  430. ListBucketData *lbData = (ListBucketData *) callbackData;
  431. int fit;
  432. if (data) {
  433. if (!strcmp(elementPath, "ListBucketResult/IsTruncated")) {
  434. string_buffer_append(lbData->isTruncated, data, dataLen, fit);
  435. }
  436. else if (!strcmp(elementPath, "ListBucketResult/NextMarker")) {
  437. string_buffer_append(lbData->nextMarker, data, dataLen, fit);
  438. }
  439. else if (!strcmp(elementPath, "ListBucketResult/Contents/Key")) {
  440. ListBucketContents *contents =
  441. &(lbData->contents[lbData->contentsCount]);
  442. string_buffer_append(contents->key, data, dataLen, fit);
  443. }
  444. else if (!strcmp(elementPath,
  445. "ListBucketResult/Contents/LastModified")) {
  446. ListBucketContents *contents =
  447. &(lbData->contents[lbData->contentsCount]);
  448. string_buffer_append(contents->lastModified, data, dataLen, fit);
  449. }
  450. else if (!strcmp(elementPath, "ListBucketResult/Contents/ETag")) {
  451. ListBucketContents *contents =
  452. &(lbData->contents[lbData->contentsCount]);
  453. string_buffer_append(contents->eTag, data, dataLen, fit);
  454. }
  455. else if (!strcmp(elementPath, "ListBucketResult/Contents/Size")) {
  456. ListBucketContents *contents =
  457. &(lbData->contents[lbData->contentsCount]);
  458. string_buffer_append(contents->size, data, dataLen, fit);
  459. }
  460. else if (!strcmp(elementPath, "ListBucketResult/Contents/Owner/ID")) {
  461. ListBucketContents *contents =
  462. &(lbData->contents[lbData->contentsCount]);
  463. string_buffer_append(contents->ownerId, data, dataLen, fit);
  464. }
  465. else if (!strcmp(elementPath,
  466. "ListBucketResult/Contents/Owner/DisplayName")) {
  467. ListBucketContents *contents =
  468. &(lbData->contents[lbData->contentsCount]);
  469. string_buffer_append
  470. (contents->ownerDisplayName, data, dataLen, fit);
  471. }
  472. else if (!strcmp(elementPath,
  473. "ListBucketResult/CommonPrefixes/Prefix")) {
  474. int which = lbData->commonPrefixesCount;
  475. size_t oldLen = lbData->commonPrefixLens[which];
  476. lbData->commonPrefixLens[which] +=
  477. snprintf_S(lbData->commonPrefixes[which]+oldLen,
  478. sizeof(lbData->commonPrefixes[which]) -
  479. oldLen - 1,
  480. "%.*s", dataLen, data);
  481. if (lbData->commonPrefixLens[which] >=
  482. (int) sizeof(lbData->commonPrefixes[which])) {
  483. return S3StatusXmlParseFailure;
  484. }
  485. }
  486. }
  487. else {
  488. if (!strcmp(elementPath, "ListBucketResult/Contents")) {
  489. // Finished a Contents
  490. lbData->contentsCount++;
  491. if (lbData->contentsCount == MAX_CONTENTS) {
  492. // Make the callback
  493. S3Status status = make_list_bucket_callback(lbData);
  494. if (status != S3StatusOK) {
  495. return status;
  496. }
  497. initialize_list_bucket_data(lbData);
  498. }
  499. else {
  500. // Initialize the next one
  501. initialize_list_bucket_contents
  502. (&(lbData->contents[lbData->contentsCount]));
  503. }
  504. }
  505. else if (!strcmp(elementPath,
  506. "ListBucketResult/CommonPrefixes/Prefix")) {
  507. // Finished a Prefix
  508. lbData->commonPrefixesCount++;
  509. if (lbData->commonPrefixesCount == MAX_COMMON_PREFIXES) {
  510. // Make the callback
  511. S3Status status = make_list_bucket_callback(lbData);
  512. if (status != S3StatusOK) {
  513. return status;
  514. }
  515. initialize_list_bucket_data(lbData);
  516. }
  517. else {
  518. // Initialize the next one
  519. lbData->commonPrefixes[lbData->commonPrefixesCount][0] = 0;
  520. lbData->commonPrefixLens[lbData->commonPrefixesCount] = 0;
  521. }
  522. }
  523. }
  524. /* Avoid compiler error about variable set but not used */
  525. (void) fit;
  526. return S3StatusOK;
  527. }
  528. static S3Status listBucketPropertiesCallback
  529. (const S3ResponseProperties *responseProperties, void *callbackData)
  530. {
  531. ListBucketData *lbData = (ListBucketData *) callbackData;
  532. return (*(lbData->responsePropertiesCallback))
  533. (responseProperties, lbData->callbackData);
  534. }
  535. static S3Status listBucketDataCallback(int bufferSize, const char *buffer,
  536. void *callbackData)
  537. {
  538. ListBucketData *lbData = (ListBucketData *) callbackData;
  539. return simplexml_add(&(lbData->simpleXml), buffer, bufferSize);
  540. }
  541. static void listBucketCompleteCallback(S3Status requestStatus,
  542. const S3ErrorDetails *s3ErrorDetails,
  543. void *callbackData)
  544. {
  545. ListBucketData *lbData = (ListBucketData *) callbackData;
  546. // WINSCP making callback unconditionally, as we need the isTruncated
  547. make_list_bucket_callback(lbData);
  548. (*(lbData->responseCompleteCallback))
  549. (requestStatus, s3ErrorDetails, lbData->callbackData);
  550. simplexml_deinitialize(&(lbData->simpleXml));
  551. free(lbData);
  552. }
  553. void S3_list_bucket(const S3BucketContext *bucketContext, const char *prefix,
  554. const char *marker, const char *delimiter, int maxkeys,
  555. S3RequestContext *requestContext,
  556. int timeoutMs,
  557. const S3ListBucketHandler *handler, void *callbackData)
  558. {
  559. // Compose the query params
  560. string_buffer(queryParams, 4096);
  561. string_buffer_initialize(queryParams);
  562. #define safe_append(name, value) \
  563. do { \
  564. int fit; \
  565. if (amp) { \
  566. string_buffer_append(queryParams, "&", 1, fit); \
  567. if (!fit) { \
  568. (*(handler->responseHandler.completeCallback)) \
  569. (S3StatusQueryParamsTooLong, 0, callbackData); \
  570. return; \
  571. } \
  572. } \
  573. string_buffer_append(queryParams, name "=", \
  574. sizeof(name "=") - 1, fit); \
  575. if (!fit) { \
  576. (*(handler->responseHandler.completeCallback)) \
  577. (S3StatusQueryParamsTooLong, 0, callbackData); \
  578. return; \
  579. } \
  580. amp = 1; \
  581. char encoded[3 * 1024]; \
  582. if (!urlEncode(encoded, value, 1024, 1)) { \
  583. (*(handler->responseHandler.completeCallback)) \
  584. (S3StatusQueryParamsTooLong, 0, callbackData); \
  585. return; \
  586. } \
  587. string_buffer_append(queryParams, encoded, strlen(encoded), \
  588. fit); \
  589. if (!fit) { \
  590. (*(handler->responseHandler.completeCallback)) \
  591. (S3StatusQueryParamsTooLong, 0, callbackData); \
  592. return; \
  593. } \
  594. } while (0)
  595. int amp = 0;
  596. if (prefix) {
  597. safe_append("prefix", prefix);
  598. }
  599. if (marker && *marker) {
  600. safe_append("marker", marker);
  601. }
  602. if (delimiter && *delimiter) {
  603. safe_append("delimiter", delimiter);
  604. }
  605. if (maxkeys) {
  606. char maxKeysString[64];
  607. snprintf(maxKeysString, sizeof(maxKeysString), "%d", maxkeys);
  608. safe_append("max-keys", maxKeysString);
  609. }
  610. ListBucketData *lbData =
  611. (ListBucketData *) malloc(sizeof(ListBucketData));
  612. if (!lbData) {
  613. (*(handler->responseHandler.completeCallback))
  614. (S3StatusOutOfMemory, 0, callbackData);
  615. return;
  616. }
  617. simplexml_initialize(&(lbData->simpleXml), &listBucketXmlCallback, lbData);
  618. lbData->responsePropertiesCallback =
  619. handler->responseHandler.propertiesCallback;
  620. lbData->listBucketCallback = handler->listBucketCallback;
  621. lbData->responseCompleteCallback =
  622. handler->responseHandler.completeCallback;
  623. lbData->callbackData = callbackData;
  624. string_buffer_initialize(lbData->isTruncated);
  625. string_buffer_initialize(lbData->nextMarker);
  626. initialize_list_bucket_data(lbData);
  627. // Set up the RequestParams
  628. RequestParams params =
  629. {
  630. HttpRequestTypeGET, // httpRequestType
  631. { bucketContext->hostName, // hostName
  632. bucketContext->bucketName, // bucketName
  633. bucketContext->protocol, // protocol
  634. bucketContext->uriStyle, // uriStyle
  635. bucketContext->accessKeyId, // accessKeyId
  636. bucketContext->secretAccessKey, // secretAccessKey
  637. bucketContext->securityToken, // securityToken
  638. bucketContext->authRegion }, // authRegion
  639. 0, // key
  640. queryParams[0] ? queryParams : 0, // queryParams
  641. 0, // subResource
  642. 0, // copySourceBucketName
  643. 0, // copySourceKey
  644. 0, // getConditions
  645. 0, // startByte
  646. 0, // byteCount
  647. 0, // putProperties
  648. &listBucketPropertiesCallback, // propertiesCallback
  649. 0, // toS3Callback
  650. 0, // toS3CallbackTotalSize
  651. &listBucketDataCallback, // fromS3Callback
  652. &listBucketCompleteCallback, // completeCallback
  653. lbData, // callbackData
  654. timeoutMs // timeoutMs
  655. };
  656. // Perform the request
  657. request_perform(&params, requestContext);
  658. }