PageRenderTime 45ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/amanda/tags/amanda260p2/device-src/s3.c

#
C | 1397 lines | 952 code | 201 blank | 244 comment | 237 complexity | 2a5b162c9b82740e5b8160564db209c3 MD5 | raw file
  1. /*
  2. * Copyright (c) 2005 Zmanda, Inc. All Rights Reserved.
  3. *
  4. * This library is free software; you can redistribute it and/or modify it
  5. * under the terms of the GNU Lesser General Public License version 2.1 as
  6. * published by the Free Software Foundation.
  7. *
  8. * This library is distributed in the hope that it will be useful, but
  9. * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  10. * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  11. * License for more details.
  12. *
  13. * You should have received a copy of the GNU Lesser General Public License
  14. * along with this library; if not, write to the Free Software Foundation,
  15. * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  16. *
  17. * Contact information: Zmanda Inc., 505 N Mathlida Ave, Suite 120
  18. * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
  19. */
  20. /* TODO
  21. * - Compute and send Content-MD5 header
  22. * - check SSL certificate
  23. * - collect speed statistics
  24. * - debugging mode
  25. */
  26. #include <string.h>
  27. #include <sys/types.h>
  28. #include <sys/stat.h>
  29. #include <unistd.h>
  30. #include <dirent.h>
  31. #include <regex.h>
  32. #include <time.h>
  33. #include "util.h"
  34. #include "amanda.h"
  35. #include "s3.h"
  36. #include "base64.h"
  37. #include <curl/curl.h>
  38. /* Constant renamed after version 7.10.7 */
  39. #ifndef CURLINFO_RESPONSE_CODE
  40. #define CURLINFO_RESPONSE_CODE CURLINFO_HTTP_CODE
  41. #endif
  42. /* We don't need OpenSSL's kerberos support, and it's broken in
  43. * RHEL 3 anyway. */
  44. #define OPENSSL_NO_KRB5
  45. #ifdef HAVE_OPENSSL_HMAC_H
  46. # include <openssl/hmac.h>
  47. #else
  48. # ifdef HAVE_CRYPTO_HMAC_H
  49. # include <crypto/hmac.h>
  50. # else
  51. # ifdef HAVE_HMAC_H
  52. # include <hmac.h>
  53. # endif
  54. # endif
  55. #endif
  56. #include <openssl/err.h>
  57. #include <openssl/ssl.h>
  58. /*
  59. * Constants / definitions
  60. */
  61. /* Maximum key length as specified in the S3 documentation
  62. * (*excluding* null terminator) */
  63. #define S3_MAX_KEY_LENGTH 1024
  64. #define AMAZON_SECURITY_HEADER "x-amz-security-token"
  65. /* parameters for exponential backoff in the face of retriable errors */
  66. /* start at 0.01s */
  67. #define EXPONENTIAL_BACKOFF_START_USEC 10000
  68. /* double at each retry */
  69. #define EXPONENTIAL_BACKOFF_BASE 2
  70. /* retry 15 times (for a total of about 5 minutes spent waiting) */
  71. #define EXPONENTIAL_BACKOFF_MAX_RETRIES 5
  72. /* general "reasonable size" parameters */
  73. #define MAX_ERROR_RESPONSE_LEN (100*1024)
  74. /* Results which should always be retried */
  75. #define RESULT_HANDLING_ALWAYS_RETRY \
  76. { 400, S3_ERROR_RequestTimeout, 0, S3_RESULT_RETRY }, \
  77. { 409, S3_ERROR_OperationAborted, 0, S3_RESULT_RETRY }, \
  78. { 412, S3_ERROR_PreconditionFailed, 0, S3_RESULT_RETRY }, \
  79. { 500, S3_ERROR_InternalError, 0, S3_RESULT_RETRY }, \
  80. { 501, S3_ERROR_NotImplemented, 0, S3_RESULT_RETRY }, \
  81. { 0, 0, CURLE_COULDNT_CONNECT, S3_RESULT_RETRY }, \
  82. { 0, 0, CURLE_PARTIAL_FILE, S3_RESULT_RETRY }, \
  83. { 0, 0, CURLE_OPERATION_TIMEOUTED, S3_RESULT_RETRY }, \
  84. { 0, 0, CURLE_SEND_ERROR, S3_RESULT_RETRY }, \
  85. { 0, 0, CURLE_RECV_ERROR, S3_RESULT_RETRY }
  86. /*
  87. * Data structures and associated functions
  88. */
  89. struct S3Handle {
  90. /* (all strings in this struct are freed by s3_free()) */
  91. char *access_key;
  92. char *secret_key;
  93. #ifdef WANT_DEVPAY
  94. char *user_token;
  95. #endif
  96. CURL *curl;
  97. gboolean verbose;
  98. /* information from the last request */
  99. char *last_message;
  100. guint last_response_code;
  101. s3_error_code_t last_s3_error_code;
  102. CURLcode last_curl_code;
  103. guint last_num_retries;
  104. void *last_response_body;
  105. guint last_response_body_size;
  106. };
  107. /*
  108. * S3 errors */
  109. /* (see preprocessor magic in s3.h) */
  110. static char * s3_error_code_names[] = {
  111. #define S3_ERROR(NAME) #NAME
  112. S3_ERROR_LIST
  113. #undef S3_ERROR
  114. };
  115. /* Convert an s3 error name to an error code. This function
  116. * matches strings case-insensitively, and is appropriate for use
  117. * on data from the network.
  118. *
  119. * @param s3_error_code: the error name
  120. * @returns: the error code (see constants in s3.h)
  121. */
  122. static s3_error_code_t
  123. s3_error_code_from_name(char *s3_error_name);
  124. /* Convert an s3 error code to a string
  125. *
  126. * @param s3_error_code: the error code to convert
  127. * @returns: statically allocated string
  128. */
  129. static const char *
  130. s3_error_name_from_code(s3_error_code_t s3_error_code);
  131. /* Does this install of curl support SSL?
  132. *
  133. * @returns: boolean
  134. */
  135. static gboolean
  136. s3_curl_supports_ssl(void);
  137. /*
  138. * result handling */
  139. /* result handling is specified by a static array of result_handling structs,
  140. * which match based on response_code (from HTTP) and S3 error code. The result
  141. * given for the first match is used. 0 acts as a wildcard for both response_code
  142. * and s3_error_code. The list is terminated with a struct containing 0 for both
  143. * response_code and s3_error_code; the result for that struct is the default
  144. * result.
  145. *
  146. * See RESULT_HANDLING_ALWAYS_RETRY for an example.
  147. */
  148. typedef enum {
  149. S3_RESULT_RETRY = -1,
  150. S3_RESULT_FAIL = 0,
  151. S3_RESULT_OK = 1
  152. } s3_result_t;
  153. typedef struct result_handling {
  154. guint response_code;
  155. s3_error_code_t s3_error_code;
  156. CURLcode curl_code;
  157. s3_result_t result;
  158. } result_handling_t;
  159. /* Lookup a result in C{result_handling}.
  160. *
  161. * @param result_handling: array of handling specifications
  162. * @param response_code: response code from operation
  163. * @param s3_error_code: s3 error code from operation, if any
  164. * @param curl_code: the CURL error, if any
  165. * @returns: the matching result
  166. */
  167. static s3_result_t
  168. lookup_result(const result_handling_t *result_handling,
  169. guint response_code,
  170. s3_error_code_t s3_error_code,
  171. CURLcode curl_code);
  172. /*
  173. * Precompiled regular expressions */
  174. static const char *error_name_regex_string = "<Code>[:space:]*([^<]*)[:space:]*</Code>";
  175. static const char *message_regex_string = "<Message>[:space:]*([^<]*)[:space:]*</Message>";
  176. static regex_t error_name_regex, message_regex;
  177. /*
  178. * Utility functions
  179. */
  180. /* Build a resource URI as /[bucket[/key]], with proper URL
  181. * escaping.
  182. *
  183. * The caller is responsible for freeing the resulting string.
  184. *
  185. * @param bucket: the bucket, or NULL if none is involved
  186. * @param key: the key within the bucket, or NULL if none is involved
  187. * @returns: completed URI
  188. */
  189. static char *
  190. build_resource(const char *bucket,
  191. const char *key);
  192. /* Create proper authorization headers for an Amazon S3 REST
  193. * request to C{headers}.
  194. *
  195. * @note: C{X-Amz} headers (in C{headers}) must
  196. * - be in lower-case
  197. * - be in alphabetical order
  198. * - have no spaces around the colon
  199. * (don't yell at me -- see the Amazon Developer Guide)
  200. *
  201. * @param hdl: the S3Handle object
  202. * @param verb: capitalized verb for this request ('PUT', 'GET', etc.)
  203. * @param resource: the resource being accessed
  204. */
  205. static struct curl_slist *
  206. authenticate_request(S3Handle *hdl,
  207. const char *verb,
  208. const char *resource);
  209. /* Interpret the response to an S3 operation, assuming CURL completed its request
  210. * successfully. This function fills in the relevant C{hdl->last*} members.
  211. *
  212. * @param hdl: The S3Handle object
  213. * @param body: the response body
  214. * @param body_len: the length of the response body
  215. * @returns: TRUE if the response should be retried (e.g., network error)
  216. */
  217. static gboolean
  218. interpret_response(S3Handle *hdl,
  219. CURLcode curl_code,
  220. char *curl_error_buffer,
  221. void *body,
  222. guint body_len);
  223. /* Perform an S3 operation. This function handles all of the details
  224. * of retryig requests and so on.
  225. *
  226. * @param hdl: the S3Handle object
  227. * @param resource: the UTF-8 encoded resource to access
  228. (without query parameters)
  229. * @param uri: the urlencoded URI to access at Amazon (may be identical to resource)
  230. * @param verb: the HTTP request method
  231. * @param request_body: the request body, or NULL if none should be sent
  232. * @param request_body_size: the length of the request body
  233. * @param max_response_size: the maximum number of bytes to accept in the
  234. * response, or 0 for no limit.
  235. * @param preallocate_response_size: for more efficient operation, preallocate
  236. * a buffer of this size for the response body. Addition space will be allocated
  237. * if the response exceeds this size.
  238. * @param result_handling: instructions for handling the results; see above.
  239. * @returns: the result specified by result_handling; details of the response
  240. * are then available in C{hdl->last*}
  241. */
  242. static s3_result_t
  243. perform_request(S3Handle *hdl,
  244. const char *resource,
  245. const char *uri,
  246. const char *verb,
  247. const void *request_body,
  248. guint request_body_size,
  249. guint max_response_size,
  250. guint preallocate_response_size,
  251. const result_handling_t *result_handling);
  252. /*
  253. * Static function implementations
  254. */
  255. /* {{{ s3_error_code_from_name */
  256. static s3_error_code_t
  257. s3_error_code_from_name(char *s3_error_name)
  258. {
  259. int i;
  260. if (!s3_error_name) return S3_ERROR_Unknown;
  261. /* do a brute-force search through the list, since it's not sorted */
  262. for (i = 0; i < S3_ERROR_END; i++) {
  263. if (strcasecmp(s3_error_name, s3_error_code_names[i]) == 0)
  264. return i;
  265. }
  266. return S3_ERROR_Unknown;
  267. }
  268. /* }}} */
  269. /* {{{ s3_error_name_from_code */
  270. static const char *
  271. s3_error_name_from_code(s3_error_code_t s3_error_code)
  272. {
  273. if (s3_error_code >= S3_ERROR_END)
  274. s3_error_code = S3_ERROR_Unknown;
  275. if (s3_error_code == 0)
  276. return NULL;
  277. return s3_error_code_names[s3_error_code];
  278. }
  279. /* }}} */
  280. /* {{{ s3_curl_supports_ssl */
  281. static gboolean
  282. s3_curl_supports_ssl(void)
  283. {
  284. static int supported = -1;
  285. if (supported == -1) {
  286. #if defined(CURL_VERSION_SSL)
  287. curl_version_info_data *info = curl_version_info(CURLVERSION_NOW);
  288. if (info->features & CURL_VERSION_SSL)
  289. supported = 1;
  290. else
  291. supported = 0;
  292. #else
  293. supported = 0;
  294. #endif
  295. }
  296. return supported;
  297. }
  298. /* }}} */
  299. /* {{{ lookup_result */
  300. static s3_result_t
  301. lookup_result(const result_handling_t *result_handling,
  302. guint response_code,
  303. s3_error_code_t s3_error_code,
  304. CURLcode curl_code)
  305. {
  306. g_return_val_if_fail(result_handling != NULL, S3_RESULT_FAIL);
  307. while (result_handling->response_code
  308. || result_handling->s3_error_code
  309. || result_handling->curl_code) {
  310. if ((result_handling->response_code && result_handling->response_code != response_code)
  311. || (result_handling->s3_error_code && result_handling->s3_error_code != s3_error_code)
  312. || (result_handling->curl_code && result_handling->curl_code != curl_code)) {
  313. result_handling++;
  314. continue;
  315. }
  316. return result_handling->result;
  317. }
  318. /* return the result for the terminator, as the default */
  319. return result_handling->result;
  320. }
  321. /* }}} */
  322. /* {{{ build_resource */
  323. static char *
  324. build_resource(const char *bucket,
  325. const char *key)
  326. {
  327. char *esc_bucket = NULL, *esc_key = NULL;
  328. char *resource = NULL;
  329. if (bucket)
  330. if (!(esc_bucket = curl_escape(bucket, 0)))
  331. goto cleanup;
  332. if (key)
  333. if (!(esc_key = curl_escape(key, 0)))
  334. goto cleanup;
  335. if (esc_bucket) {
  336. if (esc_key) {
  337. resource = g_strdup_printf("/%s/%s", esc_bucket, esc_key);
  338. } else {
  339. resource = g_strdup_printf("/%s", esc_bucket);
  340. }
  341. } else {
  342. resource = g_strdup("/");
  343. }
  344. cleanup:
  345. if (esc_bucket) curl_free(esc_bucket);
  346. if (esc_key) curl_free(esc_key);
  347. return resource;
  348. }
  349. /* }}} */
  350. /* {{{ authenticate_request */
  351. static struct curl_slist *
  352. authenticate_request(S3Handle *hdl,
  353. const char *verb,
  354. const char *resource)
  355. {
  356. time_t t;
  357. struct tm tmp;
  358. char date[100];
  359. char * buf;
  360. HMAC_CTX ctx;
  361. char md_value[EVP_MAX_MD_SIZE+1];
  362. char auth_base64[40];
  363. unsigned int md_len;
  364. struct curl_slist *headers = NULL;
  365. char * auth_string;
  366. /* calculate the date */
  367. t = time(NULL);
  368. if (!localtime_r(&t, &tmp)) perror("localtime");
  369. if (!strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %Z", &tmp))
  370. perror("strftime");
  371. /* run HMAC-SHA1 on the canonicalized string */
  372. HMAC_CTX_init(&ctx);
  373. HMAC_Init_ex(&ctx, hdl->secret_key, strlen(hdl->secret_key), EVP_sha1(), NULL);
  374. auth_string = g_strconcat(verb, "\n\n\n", date, "\n",
  375. #ifdef WANT_DEVPAY
  376. AMAZON_SECURITY_HEADER, ":",
  377. hdl->user_token, ",",
  378. STS_PRODUCT_TOKEN, "\n",
  379. #endif
  380. resource, NULL);
  381. HMAC_Update(&ctx, (unsigned char*) auth_string, strlen(auth_string));
  382. g_free(auth_string);
  383. md_len = EVP_MAX_MD_SIZE;
  384. HMAC_Final(&ctx, (unsigned char*)md_value, &md_len);
  385. HMAC_CTX_cleanup(&ctx);
  386. base64_encode(md_value, md_len, auth_base64, sizeof(auth_base64));
  387. /* append the new headers */
  388. #ifdef WANT_DEVPAY
  389. /* Devpay headers are included in hash. */
  390. buf = g_strdup_printf(AMAZON_SECURITY_HEADER ": %s", hdl->user_token);
  391. headers = curl_slist_append(headers, buf);
  392. amfree(buf);
  393. buf = g_strdup_printf(AMAZON_SECURITY_HEADER ": %s", STS_PRODUCT_TOKEN);
  394. headers = curl_slist_append(headers, buf);
  395. amfree(buf);
  396. #endif
  397. buf = g_strdup_printf("Authorization: AWS %s:%s",
  398. hdl->access_key, auth_base64);
  399. headers = curl_slist_append(headers, buf);
  400. amfree(buf);
  401. buf = g_strdup_printf("Date: %s", date);
  402. headers = curl_slist_append(headers, buf);
  403. amfree(buf);
  404. return headers;
  405. }
  406. /* }}} */
  407. /* {{{ interpret_response */
  408. static void
  409. regex_error(regex_t *regex, int reg_result)
  410. {
  411. char *message;
  412. int size;
  413. size = regerror(reg_result, regex, NULL, 0);
  414. message = g_malloc(size);
  415. if (!message) abort(); /* we're really out of luck */
  416. regerror(reg_result, regex, message, size);
  417. /* this is programmer error (bad regexp), so just log
  418. * and abort(). There's no good way to signal a
  419. * permanaent error from interpret_response. */
  420. g_error(_("Regex error: %s"), message);
  421. g_assert_not_reached();
  422. }
  423. static gboolean
  424. interpret_response(S3Handle *hdl,
  425. CURLcode curl_code,
  426. char *curl_error_buffer,
  427. void *body,
  428. guint body_len)
  429. {
  430. long response_code = 0;
  431. regmatch_t pmatch[2];
  432. int reg_result;
  433. char *error_name = NULL, *message = NULL;
  434. char *body_copy = NULL;
  435. if (!hdl) return FALSE;
  436. if (hdl->last_message) g_free(hdl->last_message);
  437. hdl->last_message = NULL;
  438. /* bail out from a CURL error */
  439. if (curl_code != CURLE_OK) {
  440. hdl->last_curl_code = curl_code;
  441. hdl->last_message = g_strdup_printf("CURL error: %s", curl_error_buffer);
  442. return FALSE;
  443. }
  444. /* CURL seems to think things were OK, so get its response code */
  445. curl_easy_getinfo(hdl->curl, CURLINFO_RESPONSE_CODE, &response_code);
  446. hdl->last_response_code = response_code;
  447. /* 2xx and 3xx codes won't have a response body*/
  448. if (200 <= response_code && response_code < 400) {
  449. hdl->last_s3_error_code = S3_ERROR_None;
  450. return FALSE;
  451. }
  452. /* Now look at the body to try to get the actual Amazon error message. Rather
  453. * than parse out the XML, just use some regexes. */
  454. /* impose a reasonable limit on body size */
  455. if (body_len > MAX_ERROR_RESPONSE_LEN) {
  456. hdl->last_message = g_strdup("S3 Error: Unknown (response body too large to parse)");
  457. return FALSE;
  458. } else if (!body || body_len == 0) {
  459. hdl->last_message = g_strdup("S3 Error: Unknown (empty response body)");
  460. return TRUE; /* perhaps a network error; retry the request */
  461. }
  462. /* use strndup to get a zero-terminated string */
  463. body_copy = g_strndup(body, body_len);
  464. if (!body_copy) goto cleanup;
  465. reg_result = regexec(&error_name_regex, body_copy, 2, pmatch, 0);
  466. if (reg_result != 0) {
  467. if (reg_result == REG_NOMATCH) {
  468. error_name = NULL;
  469. } else {
  470. regex_error(&error_name_regex, reg_result);
  471. g_assert_not_reached();
  472. }
  473. } else {
  474. error_name = find_regex_substring(body_copy, pmatch[1]);
  475. }
  476. reg_result = regexec(&message_regex, body_copy, 2, pmatch, 0);
  477. if (reg_result != 0) {
  478. if (reg_result == REG_NOMATCH) {
  479. message = NULL;
  480. } else {
  481. regex_error(&message_regex, reg_result);
  482. g_assert_not_reached();
  483. }
  484. } else {
  485. message = find_regex_substring(body_copy, pmatch[1]);
  486. }
  487. if (error_name) {
  488. hdl->last_s3_error_code = s3_error_code_from_name(error_name);
  489. }
  490. if (message) {
  491. hdl->last_message = message;
  492. message = NULL; /* steal the reference to the string */
  493. }
  494. cleanup:
  495. if (body_copy) g_free(body_copy);
  496. if (message) g_free(message);
  497. if (error_name) g_free(error_name);
  498. return FALSE;
  499. }
  500. /* }}} */
  501. /* {{{ perform_request */
  502. size_t buffer_readfunction(void *ptr, size_t size,
  503. size_t nmemb, void * stream) {
  504. CurlBuffer *data = stream;
  505. guint bytes_desired = size * nmemb;
  506. /* check the number of bytes remaining, just to be safe */
  507. if (bytes_desired > data->buffer_len - data->buffer_pos)
  508. bytes_desired = data->buffer_len - data->buffer_pos;
  509. memcpy((char *)ptr, data->buffer + data->buffer_pos, bytes_desired);
  510. data->buffer_pos += bytes_desired;
  511. return bytes_desired;
  512. }
  513. size_t
  514. buffer_writefunction(void *ptr, size_t size, size_t nmemb, void *stream)
  515. {
  516. CurlBuffer * data = stream;
  517. guint new_bytes = size * nmemb;
  518. guint bytes_needed = data->buffer_pos + new_bytes;
  519. /* error out if the new size is greater than the maximum allowed */
  520. if (data->max_buffer_size && bytes_needed > data->max_buffer_size)
  521. return 0;
  522. /* reallocate if necessary. We use exponential sizing to make this
  523. * happen less often. */
  524. if (bytes_needed > data->buffer_len) {
  525. guint new_size = MAX(bytes_needed, data->buffer_len * 2);
  526. if (data->max_buffer_size) {
  527. new_size = MIN(new_size, data->max_buffer_size);
  528. }
  529. data->buffer = g_realloc(data->buffer, new_size);
  530. data->buffer_len = new_size;
  531. }
  532. g_return_val_if_fail(data->buffer, 0); /* returning zero signals an error to libcurl */
  533. /* actually copy the data to the buffer */
  534. memcpy(data->buffer + data->buffer_pos, ptr, new_bytes);
  535. data->buffer_pos += new_bytes;
  536. /* signal success to curl */
  537. return new_bytes;
  538. }
  539. static int
  540. curl_debug_message(CURL *curl G_GNUC_UNUSED,
  541. curl_infotype type,
  542. char *s,
  543. size_t len,
  544. void *unused G_GNUC_UNUSED)
  545. {
  546. char *lineprefix;
  547. char *message;
  548. char **lines, **line;
  549. switch (type) {
  550. case CURLINFO_TEXT:
  551. lineprefix="";
  552. break;
  553. case CURLINFO_HEADER_IN:
  554. lineprefix="Hdr In: ";
  555. break;
  556. case CURLINFO_HEADER_OUT:
  557. lineprefix="Hdr Out: ";
  558. break;
  559. default:
  560. /* ignore data in/out -- nobody wants to see that in the
  561. * debug logs! */
  562. return 0;
  563. }
  564. /* split the input into lines */
  565. message = g_strndup(s, len);
  566. lines = g_strsplit(message, "\n", -1);
  567. g_free(message);
  568. for (line = lines; *line; line++) {
  569. if (**line == '\0') continue; /* skip blank lines */
  570. g_debug("%s%s", lineprefix, *line);
  571. }
  572. g_strfreev(lines);
  573. return 0;
  574. }
  575. static s3_result_t
  576. perform_request(S3Handle *hdl,
  577. const char *resource,
  578. const char *uri,
  579. const char *verb,
  580. const void *request_body,
  581. guint request_body_size,
  582. guint max_response_size,
  583. guint preallocate_response_size,
  584. const result_handling_t *result_handling)
  585. {
  586. const char *baseurl;
  587. char *url = NULL;
  588. s3_result_t result = S3_RESULT_FAIL; /* assume the worst.. */
  589. CURLcode curl_code = CURLE_OK;
  590. char curl_error_buffer[CURL_ERROR_SIZE] = "";
  591. struct curl_slist *headers = NULL;
  592. CurlBuffer readdata = { (void*)request_body, request_body_size, 0, 0 };
  593. CurlBuffer writedata = { NULL, 0, 0, max_response_size };
  594. gboolean should_retry;
  595. guint retries = 0;
  596. gulong backoff = EXPONENTIAL_BACKOFF_START_USEC;
  597. g_return_val_if_fail(hdl != NULL && hdl->curl != NULL, S3_RESULT_FAIL);
  598. s3_reset(hdl);
  599. baseurl = s3_curl_supports_ssl()? "https://s3.amazonaws.com":"http://s3.amazonaws.com";
  600. url = g_strconcat(baseurl, uri, NULL);
  601. if (!url) goto cleanup;
  602. if (preallocate_response_size) {
  603. writedata.buffer = g_malloc(preallocate_response_size);
  604. if (!writedata.buffer) goto cleanup;
  605. writedata.buffer_len = preallocate_response_size;
  606. }
  607. while (1) {
  608. /* reset things */
  609. if (headers) {
  610. curl_slist_free_all(headers);
  611. }
  612. readdata.buffer_pos = 0;
  613. writedata.buffer_pos = 0;
  614. curl_error_buffer[0] = '\0';
  615. /* set up the request */
  616. headers = authenticate_request(hdl, verb, resource);
  617. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_VERBOSE, hdl->verbose)))
  618. goto curl_error;
  619. if (hdl->verbose)
  620. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_DEBUGFUNCTION,
  621. curl_debug_message)))
  622. goto curl_error;
  623. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_ERRORBUFFER,
  624. curl_error_buffer)))
  625. goto curl_error;
  626. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_NOPROGRESS, 1)))
  627. goto curl_error;
  628. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_URL, url)))
  629. goto curl_error;
  630. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_HTTPHEADER,
  631. headers)))
  632. goto curl_error;
  633. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_CUSTOMREQUEST,
  634. verb)))
  635. goto curl_error;
  636. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_WRITEFUNCTION, buffer_writefunction)))
  637. goto curl_error;
  638. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_WRITEDATA, &writedata)))
  639. goto curl_error;
  640. if (max_response_size) {
  641. #ifdef CURLOPT_MAXFILESIZE_LARGE
  642. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_MAXFILESIZE_LARGE, (curl_off_t)max_response_size)))
  643. goto curl_error;
  644. #else
  645. # ifdef CURLOPT_MAXFILESIZE
  646. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_MAXFILESIZE, (long)max_response_size)))
  647. goto curl_error;
  648. # else
  649. /* no MAXFILESIZE option -- that's OK */
  650. # endif
  651. #endif
  652. }
  653. if (request_body) {
  654. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_UPLOAD, 1)))
  655. goto curl_error;
  656. #ifdef CURLOPT_INFILESIZE_LARGE
  657. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)request_body_size)))
  658. goto curl_error;
  659. #else
  660. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_INFILESIZE, (long)request_body_size)))
  661. goto curl_error;
  662. #endif
  663. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_READFUNCTION, buffer_readfunction)))
  664. goto curl_error;
  665. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_READDATA, &readdata)))
  666. goto curl_error;
  667. } else {
  668. /* Clear request_body options. */
  669. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_UPLOAD, 0)))
  670. goto curl_error;
  671. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_READFUNCTION,
  672. NULL)))
  673. goto curl_error;
  674. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_READDATA,
  675. NULL)))
  676. goto curl_error;
  677. }
  678. /* Perform the request */
  679. curl_code = curl_easy_perform(hdl->curl);
  680. /* interpret the response into hdl->last* */
  681. curl_error: /* (label for short-circuiting the curl_easy_perform call) */
  682. should_retry = interpret_response(hdl, curl_code, curl_error_buffer,
  683. writedata.buffer, writedata.buffer_pos);
  684. /* and, unless we know we need to retry, see what we're to do now */
  685. if (!should_retry) {
  686. result = lookup_result(result_handling, hdl->last_response_code,
  687. hdl->last_s3_error_code, hdl->last_curl_code);
  688. /* break out of the while(1) unless we're retrying */
  689. if (result != S3_RESULT_RETRY)
  690. break;
  691. }
  692. if (retries >= EXPONENTIAL_BACKOFF_MAX_RETRIES) {
  693. /* we're out of retries, so annotate hdl->last_message appropriately and bail
  694. * out. */
  695. char *m = g_strdup_printf("Too many retries; last message was '%s'", hdl->last_message);
  696. if (hdl->last_message) g_free(hdl->last_message);
  697. hdl->last_message = m;
  698. result = S3_RESULT_FAIL;
  699. break;
  700. }
  701. g_usleep(backoff);
  702. retries++;
  703. backoff *= EXPONENTIAL_BACKOFF_BASE;
  704. }
  705. if (result != S3_RESULT_OK) {
  706. g_debug(_("%s %s failed with %d/%s"), verb, url,
  707. hdl->last_response_code,
  708. s3_error_name_from_code(hdl->last_s3_error_code));
  709. }
  710. cleanup:
  711. if (url) g_free(url);
  712. if (headers) curl_slist_free_all(headers);
  713. /* we don't deallocate the response body -- we keep it for later */
  714. hdl->last_response_body = writedata.buffer;
  715. hdl->last_response_body_size = writedata.buffer_pos;
  716. hdl->last_num_retries = retries;
  717. return result;
  718. }
  719. /* }}} */
  720. /*
  721. * Public function implementations
  722. */
  723. /* {{{ s3_init */
  724. gboolean
  725. s3_init(void)
  726. {
  727. char regmessage[1024];
  728. int size;
  729. int reg_result;
  730. reg_result = regcomp(&error_name_regex, error_name_regex_string, REG_EXTENDED | REG_ICASE);
  731. if (reg_result != 0) {
  732. size = regerror(reg_result, &error_name_regex, regmessage, sizeof(regmessage));
  733. g_error(_("Regex error: %s"), regmessage);
  734. return FALSE;
  735. }
  736. reg_result = regcomp(&message_regex, message_regex_string, REG_EXTENDED | REG_ICASE);
  737. if (reg_result != 0) {
  738. size = regerror(reg_result, &message_regex, regmessage, sizeof(regmessage));
  739. g_error(_("Regex error: %s"), regmessage);
  740. return FALSE;
  741. }
  742. return TRUE;
  743. }
  744. /* }}} */
  745. /* {{{ s3_open */
  746. S3Handle *
  747. s3_open(const char *access_key,
  748. const char *secret_key
  749. #ifdef WANT_DEVPAY
  750. ,
  751. const char *user_token
  752. #endif
  753. ) {
  754. S3Handle *hdl;
  755. hdl = g_new0(S3Handle, 1);
  756. if (!hdl) goto error;
  757. hdl->verbose = FALSE;
  758. hdl->access_key = g_strdup(access_key);
  759. if (!hdl->access_key) goto error;
  760. hdl->secret_key = g_strdup(secret_key);
  761. if (!hdl->secret_key) goto error;
  762. #ifdef WANT_DEVPAY
  763. hdl->user_token = g_strdup(user_token);
  764. if (!hdl->user_token) goto error;
  765. #endif
  766. hdl->curl = curl_easy_init();
  767. if (!hdl->curl) goto error;
  768. return hdl;
  769. error:
  770. s3_free(hdl);
  771. return NULL;
  772. }
  773. /* }}} */
  774. /* {{{ s3_free */
  775. void
  776. s3_free(S3Handle *hdl)
  777. {
  778. s3_reset(hdl);
  779. if (hdl) {
  780. if (hdl->access_key) g_free(hdl->access_key);
  781. if (hdl->secret_key) g_free(hdl->secret_key);
  782. #ifdef WANT_DEVPAY
  783. if (hdl->user_token) g_free(hdl->user_token);
  784. #endif
  785. if (hdl->curl) curl_easy_cleanup(hdl->curl);
  786. g_free(hdl);
  787. }
  788. }
  789. /* }}} */
  790. /* {{{ s3_reset */
  791. void
  792. s3_reset(S3Handle *hdl)
  793. {
  794. if (hdl) {
  795. /* We don't call curl_easy_reset here, because doing that in curl
  796. * < 7.16 blanks the default CA certificate path, and there's no way
  797. * to get it back. */
  798. if (hdl->last_message) {
  799. g_free(hdl->last_message);
  800. hdl->last_message = NULL;
  801. }
  802. hdl->last_response_code = 0;
  803. hdl->last_curl_code = 0;
  804. hdl->last_s3_error_code = 0;
  805. hdl->last_num_retries = 0;
  806. if (hdl->last_response_body) {
  807. g_free(hdl->last_response_body);
  808. hdl->last_response_body = NULL;
  809. }
  810. hdl->last_response_body_size = 0;
  811. }
  812. }
  813. /* }}} */
  814. /* {{{ s3_error */
  815. void
  816. s3_error(S3Handle *hdl,
  817. const char **message,
  818. guint *response_code,
  819. s3_error_code_t *s3_error_code,
  820. const char **s3_error_name,
  821. CURLcode *curl_code,
  822. guint *num_retries)
  823. {
  824. if (hdl) {
  825. if (message) *message = hdl->last_message;
  826. if (response_code) *response_code = hdl->last_response_code;
  827. if (s3_error_code) *s3_error_code = hdl->last_s3_error_code;
  828. if (s3_error_name) *s3_error_name = s3_error_name_from_code(hdl->last_s3_error_code);
  829. if (curl_code) *curl_code = hdl->last_curl_code;
  830. if (num_retries) *num_retries = hdl->last_num_retries;
  831. } else {
  832. /* no hdl? return something coherent, anyway */
  833. if (message) *message = "NULL S3Handle";
  834. if (response_code) *response_code = 0;
  835. if (s3_error_code) *s3_error_code = 0;
  836. if (s3_error_name) *s3_error_name = NULL;
  837. if (curl_code) *curl_code = 0;
  838. if (num_retries) *num_retries = 0;
  839. }
  840. }
  841. /* }}} */
  842. /* {{{ s3_verbose */
  843. void
  844. s3_verbose(S3Handle *hdl, gboolean verbose)
  845. {
  846. hdl->verbose = verbose;
  847. }
  848. /* }}} */
  849. /* {{{ s3_sterror */
  850. char *
  851. s3_strerror(S3Handle *hdl)
  852. {
  853. const char *message;
  854. guint response_code;
  855. const char *s3_error_name;
  856. CURLcode curl_code;
  857. guint num_retries;
  858. char s3_info[256] = "";
  859. char response_info[16] = "";
  860. char curl_info[32] = "";
  861. char retries_info[32] = "";
  862. s3_error(hdl, &message, &response_code, NULL, &s3_error_name, &curl_code, &num_retries);
  863. if (!message)
  864. message = "Unkonwn S3 error";
  865. if (s3_error_name)
  866. g_snprintf(s3_info, sizeof(s3_info), " (%s)", s3_error_name);
  867. if (response_code)
  868. g_snprintf(response_info, sizeof(response_info), " (HTTP %d)", response_code);
  869. if (curl_code)
  870. g_snprintf(curl_info, sizeof(curl_info), " (CURLcode %d)", curl_code);
  871. if (num_retries)
  872. g_snprintf(retries_info, sizeof(retries_info), " (after %d retries)", num_retries);
  873. return g_strdup_printf("%s%s%s%s%s", message, s3_info, curl_info, response_info, retries_info);
  874. }
  875. /* }}} */
  876. /* {{{ s3_upload */
  877. /* Perform an upload. When this function returns, KEY and
  878. * BUFFER remain the responsibility of the caller.
  879. *
  880. * @param self: the s3 device
  881. * @param key: the key to which the upload should be made
  882. * @param buffer: the data to be uploaded
  883. * @param buffer_len: the length of the data to upload
  884. * @returns: false if an error ocurred
  885. */
  886. gboolean
  887. s3_upload(S3Handle *hdl,
  888. const char *bucket,
  889. const char *key,
  890. gpointer buffer,
  891. guint buffer_len)
  892. {
  893. char *resource = NULL;
  894. s3_result_t result = S3_RESULT_FAIL;
  895. static result_handling_t result_handling[] = {
  896. { 200, 0, 0, S3_RESULT_OK },
  897. RESULT_HANDLING_ALWAYS_RETRY,
  898. { 0, 0, 0, /* default: */ S3_RESULT_FAIL }
  899. };
  900. g_return_val_if_fail(hdl != NULL, FALSE);
  901. resource = build_resource(bucket, key);
  902. if (resource) {
  903. result = perform_request(hdl, resource, resource, "PUT",
  904. buffer, buffer_len, MAX_ERROR_RESPONSE_LEN, 0,
  905. result_handling);
  906. g_free(resource);
  907. }
  908. return result == S3_RESULT_OK;
  909. }
  910. /* }}} */
  911. /* {{{ s3_list_keys */
  912. /* Private structure for our "thunk", which tracks where the user is in the list
  913. * of keys. */
  914. struct list_keys_thunk {
  915. GSList *filename_list; /* all pending filenames */
  916. gboolean in_contents; /* look for "key" entities in here */
  917. gboolean in_common_prefixes; /* look for "prefix" entities in here */
  918. gboolean is_truncated;
  919. gchar *next_marker;
  920. gboolean want_text;
  921. gchar *text;
  922. gsize text_len;
  923. };
  924. /* Functions for a SAX parser to parse the XML from Amazon */
  925. static void
  926. list_start_element(GMarkupParseContext *context G_GNUC_UNUSED,
  927. const gchar *element_name,
  928. const gchar **attribute_names G_GNUC_UNUSED,
  929. const gchar **attribute_values G_GNUC_UNUSED,
  930. gpointer user_data,
  931. GError **error G_GNUC_UNUSED)
  932. {
  933. struct list_keys_thunk *thunk = (struct list_keys_thunk *)user_data;
  934. thunk->want_text = 0;
  935. if (strcasecmp(element_name, "contents") == 0) {
  936. thunk->in_contents = 1;
  937. } else if (strcasecmp(element_name, "commonprefixes") == 0) {
  938. thunk->in_common_prefixes = 1;
  939. } else if (strcasecmp(element_name, "prefix") == 0 && thunk->in_common_prefixes) {
  940. thunk->want_text = 1;
  941. } else if (strcasecmp(element_name, "key") == 0 && thunk->in_contents) {
  942. thunk->want_text = 1;
  943. } else if (strcasecmp(element_name, "istruncated")) {
  944. thunk->want_text = 1;
  945. } else if (strcasecmp(element_name, "nextmarker")) {
  946. thunk->want_text = 1;
  947. }
  948. }
  949. static void
  950. list_end_element(GMarkupParseContext *context G_GNUC_UNUSED,
  951. const gchar *element_name,
  952. gpointer user_data,
  953. GError **error G_GNUC_UNUSED)
  954. {
  955. struct list_keys_thunk *thunk = (struct list_keys_thunk *)user_data;
  956. if (strcasecmp(element_name, "contents") == 0) {
  957. thunk->in_contents = 0;
  958. } else if (strcasecmp(element_name, "commonprefixes") == 0) {
  959. thunk->in_common_prefixes = 0;
  960. } else if (strcasecmp(element_name, "key") == 0 && thunk->in_contents) {
  961. thunk->filename_list = g_slist_prepend(thunk->filename_list, thunk->text);
  962. thunk->text = NULL;
  963. } else if (strcasecmp(element_name, "prefix") == 0 && thunk->in_common_prefixes) {
  964. thunk->filename_list = g_slist_prepend(thunk->filename_list, thunk->text);
  965. thunk->text = NULL;
  966. } else if (strcasecmp(element_name, "istruncated") == 0) {
  967. if (thunk->text && strncasecmp(thunk->text, "false", 5) != 0)
  968. thunk->is_truncated = TRUE;
  969. } else if (strcasecmp(element_name, "nextmarker") == 0) {
  970. if (thunk->next_marker) g_free(thunk->next_marker);
  971. thunk->next_marker = thunk->text;
  972. thunk->text = NULL;
  973. }
  974. }
  975. static void
  976. list_text(GMarkupParseContext *context G_GNUC_UNUSED,
  977. const gchar *text,
  978. gsize text_len,
  979. gpointer user_data,
  980. GError **error G_GNUC_UNUSED)
  981. {
  982. struct list_keys_thunk *thunk = (struct list_keys_thunk *)user_data;
  983. if (thunk->want_text) {
  984. if (thunk->text) g_free(thunk->text);
  985. thunk->text = g_strndup(text, text_len);
  986. }
  987. }
  988. /* Helper function for list_fetch */
  989. static gboolean
  990. list_build_url_component(char **rv,
  991. const char *delim,
  992. const char *key,
  993. const char *value)
  994. {
  995. char *esc_value = NULL;
  996. char *new_rv = NULL;
  997. esc_value = curl_escape(value, 0);
  998. if (!esc_value) goto cleanup;
  999. new_rv = g_strconcat(*rv, delim, key, "=", esc_value, NULL);
  1000. if (!new_rv) goto cleanup;
  1001. g_free(*rv);
  1002. *rv = new_rv;
  1003. curl_free(esc_value);
  1004. return TRUE;
  1005. cleanup:
  1006. if (new_rv) g_free(new_rv);
  1007. if (esc_value) curl_free(esc_value);
  1008. return FALSE;
  1009. }
  1010. /* Perform a fetch from S3; several fetches may be involved in a
  1011. * single listing operation */
  1012. static s3_result_t
  1013. list_fetch(S3Handle *hdl,
  1014. const char *resource,
  1015. const char *prefix,
  1016. const char *delimiter,
  1017. const char *marker,
  1018. const char *max_keys)
  1019. {
  1020. char *urldelim = "?";
  1021. char *uri = g_strdup(resource);
  1022. s3_result_t result = S3_RESULT_FAIL;
  1023. static result_handling_t result_handling[] = {
  1024. { 200, 0, 0, S3_RESULT_OK },
  1025. RESULT_HANDLING_ALWAYS_RETRY,
  1026. { 0, 0, 0, /* default: */ S3_RESULT_FAIL }
  1027. };
  1028. /* build the URI */
  1029. if (prefix) {
  1030. if (!list_build_url_component(&uri, urldelim, "prefix", prefix)) goto cleanup;
  1031. urldelim = "&";
  1032. }
  1033. if (delimiter) {
  1034. if (!list_build_url_component(&uri, urldelim, "delimiter", delimiter)) goto cleanup;
  1035. urldelim = "&";
  1036. }
  1037. if (marker) {
  1038. if (!list_build_url_component(&uri, urldelim, "marker", marker)) goto cleanup;
  1039. urldelim = "&";
  1040. }
  1041. if (max_keys) {
  1042. if (!list_build_url_component(&uri, urldelim, "max-keys", max_keys)) goto cleanup;
  1043. urldelim = "&";
  1044. }
  1045. /* and perform the request on that URI */
  1046. result = perform_request(hdl, resource, uri, "GET", NULL,
  1047. 0, MAX_ERROR_RESPONSE_LEN, 0, result_handling);
  1048. cleanup:
  1049. if (uri) g_free(uri);
  1050. return result;
  1051. }
  1052. gboolean
  1053. s3_list_keys(S3Handle *hdl,
  1054. const char *bucket,
  1055. const char *prefix,
  1056. const char *delimiter,
  1057. GSList **list)
  1058. {
  1059. char *resource = NULL;
  1060. struct list_keys_thunk thunk;
  1061. GMarkupParseContext *ctxt = NULL;
  1062. static GMarkupParser parser = { list_start_element, list_end_element, list_text, NULL, NULL };
  1063. GError *err = NULL;
  1064. s3_result_t result = S3_RESULT_FAIL;
  1065. g_assert(list);
  1066. *list = NULL;
  1067. thunk.filename_list = NULL;
  1068. thunk.text = NULL;
  1069. thunk.next_marker = NULL;
  1070. resource = build_resource(bucket, NULL);
  1071. if (!resource) goto cleanup;
  1072. /* Loop until S3 has given us the entire picture */
  1073. do {
  1074. /* get some data from S3 */
  1075. result = list_fetch(hdl, resource, prefix, delimiter, thunk.next_marker, NULL);
  1076. if (result != S3_RESULT_OK) goto cleanup;
  1077. /* run the parser over it */
  1078. thunk.in_contents = FALSE;
  1079. thunk.in_common_prefixes = FALSE;
  1080. thunk.is_truncated = FALSE;
  1081. thunk.want_text = FALSE;
  1082. ctxt = g_markup_parse_context_new(&parser, 0, (gpointer)&thunk, NULL);
  1083. if (!g_markup_parse_context_parse(ctxt, hdl->last_response_body,
  1084. hdl->last_response_body_size, &err)) {
  1085. if (hdl->last_message) g_free(hdl->last_message);
  1086. hdl->last_message = g_strdup(err->message);
  1087. result = S3_RESULT_FAIL;
  1088. goto cleanup;
  1089. }
  1090. if (!g_markup_parse_context_end_parse(ctxt, &err)) {
  1091. if (hdl->last_message) g_free(hdl->last_message);
  1092. hdl->last_message = g_strdup(err->message);
  1093. result = S3_RESULT_FAIL;
  1094. goto cleanup;
  1095. }
  1096. g_markup_parse_context_free(ctxt);
  1097. ctxt = NULL;
  1098. } while (thunk.next_marker);
  1099. cleanup:
  1100. if (err) g_error_free(err);
  1101. if (thunk.text) g_free(thunk.text);
  1102. if (thunk.next_marker) g_free(thunk.next_marker);
  1103. if (resource) g_free(resource);
  1104. if (ctxt) g_markup_parse_context_free(ctxt);
  1105. if (result != S3_RESULT_OK) {
  1106. g_slist_free(thunk.filename_list);
  1107. return FALSE;
  1108. } else {
  1109. *list = thunk.filename_list;
  1110. return TRUE;
  1111. }
  1112. }
  1113. /* }}} */
  1114. /* {{{ s3_read */
  1115. gboolean
  1116. s3_read(S3Handle *hdl,
  1117. const char *bucket,
  1118. const char *key,
  1119. gpointer *buf_ptr,
  1120. guint *buf_size,
  1121. guint max_size)
  1122. {
  1123. char *resource = NULL;
  1124. s3_result_t result = S3_RESULT_FAIL;
  1125. static result_handling_t result_handling[] = {
  1126. { 200, 0, 0, S3_RESULT_OK },
  1127. RESULT_HANDLING_ALWAYS_RETRY,
  1128. { 0, 0, 0, /* default: */ S3_RESULT_FAIL }
  1129. };
  1130. g_return_val_if_fail(hdl != NULL, FALSE);
  1131. g_assert(buf_ptr != NULL);
  1132. g_assert(buf_size != NULL);
  1133. *buf_ptr = NULL;
  1134. *buf_size = 0;
  1135. resource = build_resource(bucket, key);
  1136. if (resource) {
  1137. result = perform_request(hdl, resource, resource,
  1138. "GET", NULL, 0, max_size, 0, result_handling);
  1139. g_free(resource);
  1140. /* copy the pointer to the result parameters and remove
  1141. * our reference to it */
  1142. if (result == S3_RESULT_OK) {
  1143. *buf_ptr = hdl->last_response_body;
  1144. *buf_size = hdl->last_response_body_size;
  1145. hdl->last_response_body = NULL;
  1146. hdl->last_response_body_size = 0;
  1147. }
  1148. }
  1149. return result == S3_RESULT_OK;
  1150. }
  1151. /* }}} */
  1152. /* {{{ s3_delete */
  1153. gboolean
  1154. s3_delete(S3Handle *hdl,
  1155. const char *bucket,
  1156. const char *key)
  1157. {
  1158. char *resource = NULL;
  1159. s3_result_t result = S3_RESULT_FAIL;
  1160. static result_handling_t result_handling[] = {
  1161. { 204, 0, 0, S3_RESULT_OK },
  1162. RESULT_HANDLING_ALWAYS_RETRY,
  1163. { 0, 0, 0, /* default: */ S3_RESULT_FAIL }
  1164. };
  1165. g_return_val_if_fail(hdl != NULL, FALSE);
  1166. resource = build_resource(bucket, key);
  1167. if (resource) {
  1168. result = perform_request(hdl, resource, resource, "DELETE", NULL, 0,
  1169. MAX_ERROR_RESPONSE_LEN, 0, result_handling);
  1170. g_free(resource);
  1171. }
  1172. return result == S3_RESULT_OK;
  1173. }
  1174. /* }}} */
  1175. /* {{{ s3_make_bucket */
  1176. gboolean
  1177. s3_make_bucket(S3Handle *hdl,
  1178. const char *bucket)
  1179. {
  1180. char *resource = NULL;
  1181. s3_result_t result = result = S3_RESULT_FAIL;
  1182. static result_handling_t result_handling[] = {
  1183. { 200, 0, 0, S3_RESULT_OK },
  1184. RESULT_HANDLING_ALWAYS_RETRY,
  1185. { 0, 0, 0, /* default: */ S3_RESULT_FAIL }
  1186. };
  1187. g_return_val_if_fail(hdl != NULL, FALSE);
  1188. resource = build_resource(bucket, NULL);
  1189. if (resource) {
  1190. result = perform_request(hdl, resource, resource, "PUT", NULL, 0,
  1191. MAX_ERROR_RESPONSE_LEN, 0, result_handling);
  1192. g_free(resource);
  1193. }
  1194. return result == S3_RESULT_OK;
  1195. }
  1196. /* }}} */