PageRenderTime 67ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

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

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