PageRenderTime 58ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/amanda/tags/3_3_0/device-src/s3.c

#
C | 2088 lines | 1472 code | 297 blank | 319 comment | 318 complexity | 4da888ed94b0851df4c62e86e7160f14 MD5 | raw file
  1. /*
  2. * Copyright (c) 2008, 2009, 2010 Zmanda, Inc. All Rights Reserved.
  3. *
  4. * This program is free software; you can redistribute it and/or modify it
  5. * under the terms of the GNU General Public License version 2 as published
  6. * by the Free Software Foundation.
  7. *
  8. * This program 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 General Public License
  11. * for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License along
  14. * with this program; if not, write to the Free Software Foundation, Inc.,
  15. * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. *
  17. * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
  18. * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
  19. */
  20. /* TODO
  21. * - collect speed statistics
  22. * - debugging mode
  23. */
  24. #ifdef HAVE_CONFIG_H
  25. /* use a relative path here to avoid conflicting with Perl's config.h. */
  26. #include "../config/config.h"
  27. #endif
  28. #include <string.h>
  29. #include "s3.h"
  30. #include "s3-util.h"
  31. #ifdef HAVE_REGEX_H
  32. #include <regex.h>
  33. #endif
  34. #ifdef HAVE_SYS_TYPES_H
  35. #include <sys/types.h>
  36. #endif
  37. #ifdef HAVE_SYS_STAT_H
  38. #include <sys/stat.h>
  39. #endif
  40. #ifdef HAVE_UNISTD_H
  41. #include <unistd.h>
  42. #endif
  43. #ifdef HAVE_DIRENT_H
  44. #include <dirent.h>
  45. #endif
  46. #ifdef HAVE_TIME_H
  47. #include <time.h>
  48. #endif
  49. #ifdef HAVE_UTIL_H
  50. #include "util.h"
  51. #endif
  52. #ifdef HAVE_AMANDA_H
  53. #include "amanda.h"
  54. #endif
  55. #include <curl/curl.h>
  56. /* Constant renamed after version 7.10.7 */
  57. #ifndef CURLINFO_RESPONSE_CODE
  58. #define CURLINFO_RESPONSE_CODE CURLINFO_HTTP_CODE
  59. #endif
  60. /* We don't need OpenSSL's kerberos support, and it's broken in
  61. * RHEL 3 anyway. */
  62. #define OPENSSL_NO_KRB5
  63. #ifdef HAVE_OPENSSL_HMAC_H
  64. # include <openssl/hmac.h>
  65. #else
  66. # ifdef HAVE_CRYPTO_HMAC_H
  67. # include <crypto/hmac.h>
  68. # else
  69. # ifdef HAVE_HMAC_H
  70. # include <hmac.h>
  71. # endif
  72. # endif
  73. #endif
  74. #include <openssl/err.h>
  75. #include <openssl/ssl.h>
  76. #include <openssl/md5.h>
  77. /* Maximum key length as specified in the S3 documentation
  78. * (*excluding* null terminator) */
  79. #define S3_MAX_KEY_LENGTH 1024
  80. #define AMAZON_SECURITY_HEADER "x-amz-security-token"
  81. #define AMAZON_BUCKET_CONF_TEMPLATE "\
  82. <CreateBucketConfiguration>\n\
  83. <LocationConstraint>%s</LocationConstraint>\n\
  84. </CreateBucketConfiguration>"
  85. #define AMAZON_STORAGE_CLASS_HEADER "x-amz-storage-class"
  86. #define AMAZON_WILDCARD_LOCATION "*"
  87. /* parameters for exponential backoff in the face of retriable errors */
  88. /* start at 0.01s */
  89. #define EXPONENTIAL_BACKOFF_START_USEC G_USEC_PER_SEC/100
  90. /* double at each retry */
  91. #define EXPONENTIAL_BACKOFF_BASE 2
  92. /* retry 14 times (for a total of about 3 minutes spent waiting) */
  93. #define EXPONENTIAL_BACKOFF_MAX_RETRIES 14
  94. /* general "reasonable size" parameters */
  95. #define MAX_ERROR_RESPONSE_LEN (100*1024)
  96. /* Results which should always be retried */
  97. #define RESULT_HANDLING_ALWAYS_RETRY \
  98. { 400, S3_ERROR_RequestTimeout, 0, S3_RESULT_RETRY }, \
  99. { 403, S3_ERROR_RequestTimeTooSkewed,0, S3_RESULT_RETRY }, \
  100. { 409, S3_ERROR_OperationAborted, 0, S3_RESULT_RETRY }, \
  101. { 412, S3_ERROR_PreconditionFailed, 0, S3_RESULT_RETRY }, \
  102. { 500, S3_ERROR_InternalError, 0, S3_RESULT_RETRY }, \
  103. { 501, S3_ERROR_NotImplemented, 0, S3_RESULT_RETRY }, \
  104. { 0, 0, CURLE_COULDNT_CONNECT, S3_RESULT_RETRY }, \
  105. { 0, 0, CURLE_COULDNT_RESOLVE_HOST, S3_RESULT_RETRY }, \
  106. { 0, 0, CURLE_PARTIAL_FILE, S3_RESULT_RETRY }, \
  107. { 0, 0, CURLE_OPERATION_TIMEOUTED, S3_RESULT_RETRY }, \
  108. { 0, 0, CURLE_SEND_ERROR, S3_RESULT_RETRY }, \
  109. { 0, 0, CURLE_RECV_ERROR, S3_RESULT_RETRY }, \
  110. { 0, 0, CURLE_GOT_NOTHING, S3_RESULT_RETRY }
  111. /*
  112. * Data structures and associated functions
  113. */
  114. struct S3Handle {
  115. /* (all strings in this struct are freed by s3_free()) */
  116. char *access_key;
  117. char *secret_key;
  118. char *user_token;
  119. /* attributes for new objects */
  120. char *bucket_location;
  121. char *storage_class;
  122. char *host;
  123. char *service_path;
  124. gboolean use_subdomain;
  125. char *ca_info;
  126. CURL *curl;
  127. gboolean verbose;
  128. gboolean use_ssl;
  129. guint64 max_send_speed;
  130. guint64 max_recv_speed;
  131. /* information from the last request */
  132. char *last_message;
  133. guint last_response_code;
  134. s3_error_code_t last_s3_error_code;
  135. CURLcode last_curl_code;
  136. guint last_num_retries;
  137. void *last_response_body;
  138. guint last_response_body_size;
  139. /* offset with s3 */
  140. time_t time_offset_with_s3;
  141. };
  142. typedef struct {
  143. CurlBuffer resp_buf;
  144. s3_write_func write_func;
  145. s3_reset_func reset_func;
  146. gpointer write_data;
  147. gboolean headers_done;
  148. gboolean int_write_done;
  149. char *etag;
  150. /* Points to current handle: Added to get hold of s3 offset */
  151. struct S3Handle *hdl;
  152. } S3InternalData;
  153. /* Callback function to examine headers one-at-a-time
  154. *
  155. * @note this is the same as CURLOPT_HEADERFUNCTION
  156. *
  157. * @param data: The pointer to read data from
  158. * @param size: The size of each "element" of the data buffer in bytes
  159. * @param nmemb: The number of elements in the data buffer.
  160. * So, the buffer's size is size*nmemb bytes.
  161. * @param stream: the header_data (an opaque pointer)
  162. *
  163. * @return The number of bytes written to the buffer or
  164. * CURL_WRITEFUNC_PAUSE to pause.
  165. * If it's the number of bytes written, it should match the buffer size
  166. */
  167. typedef size_t (*s3_header_func)(void *data, size_t size, size_t nmemb, void *stream);
  168. /*
  169. * S3 errors */
  170. /* (see preprocessor magic in s3.h) */
  171. static char * s3_error_code_names[] = {
  172. #define S3_ERROR(NAME) #NAME
  173. S3_ERROR_LIST
  174. #undef S3_ERROR
  175. };
  176. /* Convert an s3 error name to an error code. This function
  177. * matches strings case-insensitively, and is appropriate for use
  178. * on data from the network.
  179. *
  180. * @param s3_error_code: the error name
  181. * @returns: the error code (see constants in s3.h)
  182. */
  183. static s3_error_code_t
  184. s3_error_code_from_name(char *s3_error_name);
  185. /* Convert an s3 error code to a string
  186. *
  187. * @param s3_error_code: the error code to convert
  188. * @returns: statically allocated string
  189. */
  190. static const char *
  191. s3_error_name_from_code(s3_error_code_t s3_error_code);
  192. /*
  193. * result handling */
  194. /* result handling is specified by a static array of result_handling structs,
  195. * which match based on response_code (from HTTP) and S3 error code. The result
  196. * given for the first match is used. 0 acts as a wildcard for both response_code
  197. * and s3_error_code. The list is terminated with a struct containing 0 for both
  198. * response_code and s3_error_code; the result for that struct is the default
  199. * result.
  200. *
  201. * See RESULT_HANDLING_ALWAYS_RETRY for an example.
  202. */
  203. typedef enum {
  204. S3_RESULT_RETRY = -1,
  205. S3_RESULT_FAIL = 0,
  206. S3_RESULT_OK = 1
  207. } s3_result_t;
  208. typedef struct result_handling {
  209. guint response_code;
  210. s3_error_code_t s3_error_code;
  211. CURLcode curl_code;
  212. s3_result_t result;
  213. } result_handling_t;
  214. /* Lookup a result in C{result_handling}.
  215. *
  216. * @param result_handling: array of handling specifications
  217. * @param response_code: response code from operation
  218. * @param s3_error_code: s3 error code from operation, if any
  219. * @param curl_code: the CURL error, if any
  220. * @returns: the matching result
  221. */
  222. static s3_result_t
  223. lookup_result(const result_handling_t *result_handling,
  224. guint response_code,
  225. s3_error_code_t s3_error_code,
  226. CURLcode curl_code);
  227. /*
  228. * Precompiled regular expressions */
  229. static regex_t etag_regex, error_name_regex, message_regex, subdomain_regex,
  230. location_con_regex, date_sync_regex;
  231. /*
  232. * Utility functions
  233. */
  234. /* Check if a string is non-empty
  235. *
  236. * @param str: string to check
  237. * @returns: true iff str is non-NULL and not "\0"
  238. */
  239. static gboolean is_non_empty_string(const char *str);
  240. /* Construct the URL for an Amazon S3 REST request.
  241. *
  242. * A new string is allocated and returned; it is the responsiblity of the caller.
  243. *
  244. * @param hdl: the S3Handle object
  245. * @param verb: capitalized verb for this request ('PUT', 'GET', etc.)
  246. * @param host: the host name to connect to, 's3.amazonaws.com'
  247. * @param service_path: A path to add in the URL, or NULL for none.
  248. * @param bucket: the bucket being accessed, or NULL for none
  249. * @param key: the key being accessed, or NULL for none
  250. * @param subresource: the sub-resource being accessed (e.g. "acl"), or NULL for none
  251. * @param use_subdomain: if TRUE, a subdomain of 'host' will be used
  252. * @param use_ssl: if TRUE, use 'https'
  253. *
  254. * !use_subdomain: http://host/service_path/bucket/key
  255. * use_subdomain : http://bucket.host/service_path/key
  256. *
  257. */
  258. static char *
  259. build_url(
  260. const char *host,
  261. const char *service_path,
  262. const char *bucket,
  263. const char *key,
  264. const char *subresource,
  265. const char *query,
  266. gboolean use_subdomain,
  267. gboolean use_ssl);
  268. /* Create proper authorization headers for an Amazon S3 REST
  269. * request to C{headers}.
  270. *
  271. * @note: C{X-Amz} headers (in C{headers}) must
  272. * - be in lower-case
  273. * - be in alphabetical order
  274. * - have no spaces around the colon
  275. * (don't yell at me -- see the Amazon Developer Guide)
  276. *
  277. * @param hdl: the S3Handle object
  278. * @param verb: capitalized verb for this request ('PUT', 'GET', etc.)
  279. * @param bucket: the bucket being accessed, or NULL for none
  280. * @param key: the key being accessed, or NULL for none
  281. * @param subresource: the sub-resource being accessed (e.g. "acl"), or NULL for none
  282. * @param md5_hash: the MD5 hash of the request body, or NULL for none
  283. */
  284. static struct curl_slist *
  285. authenticate_request(S3Handle *hdl,
  286. const char *verb,
  287. const char *bucket,
  288. const char *key,
  289. const char *subresource,
  290. const char *md5_hash);
  291. /* Interpret the response to an S3 operation, assuming CURL completed its request
  292. * successfully. This function fills in the relevant C{hdl->last*} members.
  293. *
  294. * @param hdl: The S3Handle object
  295. * @param body: the response body
  296. * @param body_len: the length of the response body
  297. * @param etag: The response's ETag header
  298. * @param content_md5: The hex-encoded MD5 hash of the request body,
  299. * which will be checked against the response's ETag header.
  300. * If NULL, the header is not checked.
  301. * If non-NULL, then the body should have the response headers at its beginnning.
  302. * @returns: TRUE if the response should be retried (e.g., network error)
  303. */
  304. static gboolean
  305. interpret_response(S3Handle *hdl,
  306. CURLcode curl_code,
  307. char *curl_error_buffer,
  308. gchar *body,
  309. guint body_len,
  310. const char *etag,
  311. const char *content_md5);
  312. /* Perform an S3 operation. This function handles all of the details
  313. * of retryig requests and so on.
  314. *
  315. * The concepts of bucket and keys are defined by the Amazon S3 API.
  316. * See: "Components of Amazon S3" - API Version 2006-03-01 pg. 8
  317. *
  318. * Individual sub-resources are defined in several places. In the REST API,
  319. * they they are represented by a "flag" in the "query string".
  320. * See: "Constructing the CanonicalizedResource Element" - API Version 2006-03-01 pg. 60
  321. *
  322. * @param hdl: the S3Handle object
  323. * @param verb: the HTTP request method
  324. * @param bucket: the bucket to access, or NULL for none
  325. * @param key: the key to access, or NULL for none
  326. * @param subresource: the "sub-resource" to request (e.g. "acl") or NULL for none
  327. * @param query: the query string to send (not including th initial '?'),
  328. * or NULL for none
  329. * @param read_func: the callback for reading data
  330. * Will use s3_empty_read_func if NULL is passed in.
  331. * @param read_reset_func: the callback for to reset reading data
  332. * @param size_func: the callback to get the number of bytes to upload
  333. * @param md5_func: the callback to get the MD5 hash of the data to upload
  334. * @param read_data: pointer to pass to the above functions
  335. * @param write_func: the callback for writing data.
  336. * Will use s3_counter_write_func if NULL is passed in.
  337. * @param write_reset_func: the callback for to reset writing data
  338. * @param write_data: pointer to pass to C{write_func}
  339. * @param progress_func: the callback for progress information
  340. * @param progress_data: pointer to pass to C{progress_func}
  341. * @param result_handling: instructions for handling the results; see above.
  342. * @returns: the result specified by result_handling; details of the response
  343. * are then available in C{hdl->last*}
  344. */
  345. static s3_result_t
  346. perform_request(S3Handle *hdl,
  347. const char *verb,
  348. const char *bucket,
  349. const char *key,
  350. const char *subresource,
  351. const char *query,
  352. s3_read_func read_func,
  353. s3_reset_func read_reset_func,
  354. s3_size_func size_func,
  355. s3_md5_func md5_func,
  356. gpointer read_data,
  357. s3_write_func write_func,
  358. s3_reset_func write_reset_func,
  359. gpointer write_data,
  360. s3_progress_func progress_func,
  361. gpointer progress_data,
  362. const result_handling_t *result_handling);
  363. /*
  364. * a CURLOPT_WRITEFUNCTION to save part of the response in memory and
  365. * call an external function if one was provided.
  366. */
  367. static size_t
  368. s3_internal_write_func(void *ptr, size_t size, size_t nmemb, void * stream);
  369. /*
  370. * a function to reset to our internal buffer
  371. */
  372. static void
  373. s3_internal_reset_func(void * stream);
  374. /*
  375. * a CURLOPT_HEADERFUNCTION to save the ETag header only.
  376. */
  377. static size_t
  378. s3_internal_header_func(void *ptr, size_t size, size_t nmemb, void * stream);
  379. static gboolean
  380. compile_regexes(void);
  381. /*
  382. * Static function implementations
  383. */
  384. static s3_error_code_t
  385. s3_error_code_from_name(char *s3_error_name)
  386. {
  387. int i;
  388. if (!s3_error_name) return S3_ERROR_Unknown;
  389. /* do a brute-force search through the list, since it's not sorted */
  390. for (i = 0; i < S3_ERROR_END; i++) {
  391. if (g_ascii_strcasecmp(s3_error_name, s3_error_code_names[i]) == 0)
  392. return i;
  393. }
  394. return S3_ERROR_Unknown;
  395. }
  396. static const char *
  397. s3_error_name_from_code(s3_error_code_t s3_error_code)
  398. {
  399. if (s3_error_code >= S3_ERROR_END)
  400. s3_error_code = S3_ERROR_Unknown;
  401. return s3_error_code_names[s3_error_code];
  402. }
  403. gboolean
  404. s3_curl_supports_ssl(void)
  405. {
  406. static int supported = -1;
  407. if (supported == -1) {
  408. #if defined(CURL_VERSION_SSL)
  409. curl_version_info_data *info = curl_version_info(CURLVERSION_NOW);
  410. if (info->features & CURL_VERSION_SSL)
  411. supported = 1;
  412. else
  413. supported = 0;
  414. #else
  415. supported = 0;
  416. #endif
  417. }
  418. return supported;
  419. }
  420. static gboolean
  421. s3_curl_throttling_compat(void)
  422. {
  423. /* CURLOPT_MAX_SEND_SPEED_LARGE added in 7.15.5 */
  424. #if LIBCURL_VERSION_NUM >= 0x070f05
  425. curl_version_info_data *info;
  426. /* check the runtime version too */
  427. info = curl_version_info(CURLVERSION_NOW);
  428. return info->version_num >= 0x070f05;
  429. #else
  430. return FALSE;
  431. #endif
  432. }
  433. static s3_result_t
  434. lookup_result(const result_handling_t *result_handling,
  435. guint response_code,
  436. s3_error_code_t s3_error_code,
  437. CURLcode curl_code)
  438. {
  439. while (result_handling->response_code
  440. || result_handling->s3_error_code
  441. || result_handling->curl_code) {
  442. if ((result_handling->response_code && result_handling->response_code != response_code)
  443. || (result_handling->s3_error_code && result_handling->s3_error_code != s3_error_code)
  444. || (result_handling->curl_code && result_handling->curl_code != curl_code)) {
  445. result_handling++;
  446. continue;
  447. }
  448. return result_handling->result;
  449. }
  450. /* return the result for the terminator, as the default */
  451. return result_handling->result;
  452. }
  453. static gboolean
  454. is_non_empty_string(const char *str)
  455. {
  456. return str && str[0] != '\0';
  457. }
  458. static char *
  459. build_url(
  460. const char *host,
  461. const char *service_path,
  462. const char *bucket,
  463. const char *key,
  464. const char *subresource,
  465. const char *query,
  466. gboolean use_subdomain,
  467. gboolean use_ssl)
  468. {
  469. GString *url = NULL;
  470. char *esc_bucket = NULL, *esc_key = NULL;
  471. /* scheme */
  472. url = g_string_new("http");
  473. if (use_ssl)
  474. g_string_append(url, "s");
  475. g_string_append(url, "://");
  476. /* domain */
  477. if (use_subdomain && bucket)
  478. g_string_append_printf(url, "%s.%s", bucket, host);
  479. else
  480. g_string_append_printf(url, "%s", host);
  481. if (service_path) {
  482. g_string_append_printf(url, "%s/", service_path);
  483. } else {
  484. g_string_append(url, "/");
  485. }
  486. /* path */
  487. if (!use_subdomain && bucket) {
  488. esc_bucket = curl_escape(bucket, 0);
  489. if (!esc_bucket) goto cleanup;
  490. g_string_append_printf(url, "%s", esc_bucket);
  491. if (key)
  492. g_string_append(url, "/");
  493. }
  494. if (key) {
  495. esc_key = curl_escape(key, 0);
  496. if (!esc_key) goto cleanup;
  497. g_string_append_printf(url, "%s", esc_key);
  498. }
  499. /* query string */
  500. if (subresource || query)
  501. g_string_append(url, "?");
  502. if (subresource)
  503. g_string_append(url, subresource);
  504. if (subresource && query)
  505. g_string_append(url, "&");
  506. if (query)
  507. g_string_append(url, query);
  508. cleanup:
  509. if (esc_bucket) curl_free(esc_bucket);
  510. if (esc_key) curl_free(esc_key);
  511. return g_string_free(url, FALSE);
  512. }
  513. static struct curl_slist *
  514. authenticate_request(S3Handle *hdl,
  515. const char *verb,
  516. const char *bucket,
  517. const char *key,
  518. const char *subresource,
  519. const char *md5_hash)
  520. {
  521. time_t t;
  522. struct tm tmp;
  523. char *date = NULL;
  524. char *buf = NULL;
  525. HMAC_CTX ctx;
  526. GByteArray *md = NULL;
  527. char *auth_base64 = NULL;
  528. struct curl_slist *headers = NULL;
  529. char *esc_bucket = NULL, *esc_key = NULL;
  530. GString *auth_string = NULL;
  531. /* From RFC 2616 */
  532. static const char *wkday[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
  533. static const char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  534. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  535. /* Build the string to sign, per the S3 spec.
  536. * See: "Authenticating REST Requests" - API Version 2006-03-01 pg 58
  537. */
  538. /* verb */
  539. auth_string = g_string_new(verb);
  540. g_string_append(auth_string, "\n");
  541. /* Content-MD5 header */
  542. if (md5_hash)
  543. g_string_append(auth_string, md5_hash);
  544. g_string_append(auth_string, "\n");
  545. /* Content-Type is empty*/
  546. g_string_append(auth_string, "\n");
  547. /* calculate the date */
  548. t = time(NULL);
  549. /* sync clock with amazon s3 */
  550. t = t + hdl->time_offset_with_s3;
  551. #ifdef _WIN32
  552. if (!gmtime_s(&tmp, &t)) g_debug("localtime error");
  553. #else
  554. if (!gmtime_r(&t, &tmp)) perror("localtime");
  555. #endif
  556. date = g_strdup_printf("%s, %02d %s %04d %02d:%02d:%02d GMT",
  557. wkday[tmp.tm_wday], tmp.tm_mday, month[tmp.tm_mon], 1900+tmp.tm_year,
  558. tmp.tm_hour, tmp.tm_min, tmp.tm_sec);
  559. g_string_append(auth_string, date);
  560. g_string_append(auth_string, "\n");
  561. /* CanonicalizedAmzHeaders, sorted lexicographically */
  562. if (is_non_empty_string(hdl->user_token)) {
  563. g_string_append(auth_string, AMAZON_SECURITY_HEADER);
  564. g_string_append(auth_string, ":");
  565. g_string_append(auth_string, hdl->user_token);
  566. g_string_append(auth_string, ",");
  567. g_string_append(auth_string, STS_PRODUCT_TOKEN);
  568. g_string_append(auth_string, "\n");
  569. }
  570. if (is_non_empty_string(hdl->storage_class)) {
  571. g_string_append(auth_string, AMAZON_STORAGE_CLASS_HEADER);
  572. g_string_append(auth_string, ":");
  573. g_string_append(auth_string, hdl->storage_class);
  574. g_string_append(auth_string, "\n");
  575. }
  576. /* CanonicalizedResource */
  577. if (hdl->service_path) {
  578. g_string_append(auth_string, hdl->service_path);
  579. }
  580. g_string_append(auth_string, "/");
  581. if (bucket) {
  582. if (hdl->use_subdomain)
  583. g_string_append(auth_string, bucket);
  584. else {
  585. esc_bucket = curl_escape(bucket, 0);
  586. if (!esc_bucket) goto cleanup;
  587. g_string_append(auth_string, esc_bucket);
  588. }
  589. }
  590. if (bucket && (hdl->use_subdomain || key))
  591. g_string_append(auth_string, "/");
  592. if (key) {
  593. esc_key = curl_escape(key, 0);
  594. if (!esc_key) goto cleanup;
  595. g_string_append(auth_string, esc_key);
  596. }
  597. if (subresource) {
  598. g_string_append(auth_string, "?");
  599. g_string_append(auth_string, subresource);
  600. }
  601. /* run HMAC-SHA1 on the canonicalized string */
  602. md = g_byte_array_sized_new(EVP_MAX_MD_SIZE+1);
  603. HMAC_CTX_init(&ctx);
  604. HMAC_Init_ex(&ctx, hdl->secret_key, (int) strlen(hdl->secret_key), EVP_sha1(), NULL);
  605. HMAC_Update(&ctx, (unsigned char*) auth_string->str, auth_string->len);
  606. HMAC_Final(&ctx, md->data, &md->len);
  607. HMAC_CTX_cleanup(&ctx);
  608. auth_base64 = s3_base64_encode(md);
  609. /* append the new headers */
  610. if (is_non_empty_string(hdl->user_token)) {
  611. /* Devpay headers are included in hash. */
  612. buf = g_strdup_printf(AMAZON_SECURITY_HEADER ": %s", hdl->user_token);
  613. headers = curl_slist_append(headers, buf);
  614. g_free(buf);
  615. buf = g_strdup_printf(AMAZON_SECURITY_HEADER ": %s", STS_PRODUCT_TOKEN);
  616. headers = curl_slist_append(headers, buf);
  617. g_free(buf);
  618. }
  619. if (is_non_empty_string(hdl->storage_class)) {
  620. buf = g_strdup_printf(AMAZON_STORAGE_CLASS_HEADER ": %s", hdl->storage_class);
  621. headers = curl_slist_append(headers, buf);
  622. g_free(buf);
  623. }
  624. buf = g_strdup_printf("Authorization: AWS %s:%s",
  625. hdl->access_key, auth_base64);
  626. headers = curl_slist_append(headers, buf);
  627. g_free(buf);
  628. if (md5_hash && '\0' != md5_hash[0]) {
  629. buf = g_strdup_printf("Content-MD5: %s", md5_hash);
  630. headers = curl_slist_append(headers, buf);
  631. g_free(buf);
  632. }
  633. buf = g_strdup_printf("Date: %s", date);
  634. headers = curl_slist_append(headers, buf);
  635. g_free(buf);
  636. cleanup:
  637. g_free(date);
  638. g_free(esc_bucket);
  639. g_free(esc_key);
  640. g_byte_array_free(md, TRUE);
  641. g_free(auth_base64);
  642. g_string_free(auth_string, TRUE);
  643. return headers;
  644. }
  645. static gboolean
  646. interpret_response(S3Handle *hdl,
  647. CURLcode curl_code,
  648. char *curl_error_buffer,
  649. gchar *body,
  650. guint body_len,
  651. const char *etag,
  652. const char *content_md5)
  653. {
  654. long response_code = 0;
  655. regmatch_t pmatch[2];
  656. char *error_name = NULL, *message = NULL;
  657. char *body_copy = NULL;
  658. gboolean ret = TRUE;
  659. if (!hdl) return FALSE;
  660. if (hdl->last_message) g_free(hdl->last_message);
  661. hdl->last_message = NULL;
  662. /* bail out from a CURL error */
  663. if (curl_code != CURLE_OK) {
  664. hdl->last_curl_code = curl_code;
  665. hdl->last_message = g_strdup_printf("CURL error: %s", curl_error_buffer);
  666. return FALSE;
  667. }
  668. /* CURL seems to think things were OK, so get its response code */
  669. curl_easy_getinfo(hdl->curl, CURLINFO_RESPONSE_CODE, &response_code);
  670. hdl->last_response_code = response_code;
  671. /* check ETag, if present */
  672. if (etag && content_md5 && 200 == response_code) {
  673. if (etag && g_ascii_strcasecmp(etag, content_md5))
  674. hdl->last_message = g_strdup("S3 Error: Possible data corruption (ETag returned by Amazon did not match the MD5 hash of the data sent)");
  675. else
  676. ret = FALSE;
  677. return ret;
  678. }
  679. if (200 <= response_code && response_code < 400) {
  680. /* 2xx and 3xx codes won't have a response body we care about */
  681. hdl->last_s3_error_code = S3_ERROR_None;
  682. return FALSE;
  683. }
  684. /* Now look at the body to try to get the actual Amazon error message. Rather
  685. * than parse out the XML, just use some regexes. */
  686. /* impose a reasonable limit on body size */
  687. if (body_len > MAX_ERROR_RESPONSE_LEN) {
  688. hdl->last_message = g_strdup("S3 Error: Unknown (response body too large to parse)");
  689. return FALSE;
  690. } else if (!body || body_len == 0) {
  691. hdl->last_message = g_strdup("S3 Error: Unknown (empty response body)");
  692. return TRUE; /* perhaps a network error; retry the request */
  693. }
  694. /* use strndup to get a zero-terminated string */
  695. body_copy = g_strndup(body, body_len);
  696. if (!body_copy) goto cleanup;
  697. if (!s3_regexec_wrap(&error_name_regex, body_copy, 2, pmatch, 0))
  698. error_name = find_regex_substring(body_copy, pmatch[1]);
  699. if (!s3_regexec_wrap(&message_regex, body_copy, 2, pmatch, 0))
  700. message = find_regex_substring(body_copy, pmatch[1]);
  701. if (error_name) {
  702. hdl->last_s3_error_code = s3_error_code_from_name(error_name);
  703. }
  704. if (message) {
  705. hdl->last_message = message;
  706. message = NULL; /* steal the reference to the string */
  707. }
  708. cleanup:
  709. g_free(body_copy);
  710. g_free(message);
  711. g_free(error_name);
  712. return FALSE;
  713. }
  714. /* a CURLOPT_READFUNCTION to read data from a buffer. */
  715. size_t
  716. s3_buffer_read_func(void *ptr, size_t size, size_t nmemb, void * stream)
  717. {
  718. CurlBuffer *data = stream;
  719. guint bytes_desired = (guint) size * nmemb;
  720. /* check the number of bytes remaining, just to be safe */
  721. if (bytes_desired > data->buffer_len - data->buffer_pos)
  722. bytes_desired = data->buffer_len - data->buffer_pos;
  723. memcpy((char *)ptr, data->buffer + data->buffer_pos, bytes_desired);
  724. data->buffer_pos += bytes_desired;
  725. return bytes_desired;
  726. }
  727. size_t
  728. s3_buffer_size_func(void *stream)
  729. {
  730. CurlBuffer *data = stream;
  731. return data->buffer_len;
  732. }
  733. GByteArray*
  734. s3_buffer_md5_func(void *stream)
  735. {
  736. CurlBuffer *data = stream;
  737. GByteArray req_body_gba = {(guint8 *)data->buffer, data->buffer_len};
  738. return s3_compute_md5_hash(&req_body_gba);
  739. }
  740. void
  741. s3_buffer_reset_func(void *stream)
  742. {
  743. CurlBuffer *data = stream;
  744. data->buffer_pos = 0;
  745. }
  746. /* a CURLOPT_WRITEFUNCTION to write data to a buffer. */
  747. size_t
  748. s3_buffer_write_func(void *ptr, size_t size, size_t nmemb, void *stream)
  749. {
  750. CurlBuffer * data = stream;
  751. guint new_bytes = (guint) size * nmemb;
  752. guint bytes_needed = data->buffer_pos + new_bytes;
  753. /* error out if the new size is greater than the maximum allowed */
  754. if (data->max_buffer_size && bytes_needed > data->max_buffer_size)
  755. return 0;
  756. /* reallocate if necessary. We use exponential sizing to make this
  757. * happen less often. */
  758. if (bytes_needed > data->buffer_len) {
  759. guint new_size = MAX(bytes_needed, data->buffer_len * 2);
  760. if (data->max_buffer_size) {
  761. new_size = MIN(new_size, data->max_buffer_size);
  762. }
  763. data->buffer = g_realloc(data->buffer, new_size);
  764. data->buffer_len = new_size;
  765. }
  766. if (!data->buffer)
  767. return 0; /* returning zero signals an error to libcurl */
  768. /* actually copy the data to the buffer */
  769. memcpy(data->buffer + data->buffer_pos, ptr, new_bytes);
  770. data->buffer_pos += new_bytes;
  771. /* signal success to curl */
  772. return new_bytes;
  773. }
  774. /* a CURLOPT_READFUNCTION that writes nothing. */
  775. size_t
  776. s3_empty_read_func(G_GNUC_UNUSED void *ptr, G_GNUC_UNUSED size_t size, G_GNUC_UNUSED size_t nmemb, G_GNUC_UNUSED void * stream)
  777. {
  778. return 0;
  779. }
  780. size_t
  781. s3_empty_size_func(G_GNUC_UNUSED void *stream)
  782. {
  783. return 0;
  784. }
  785. GByteArray*
  786. s3_empty_md5_func(G_GNUC_UNUSED void *stream)
  787. {
  788. static const GByteArray empty = {(guint8 *) "", 0};
  789. return s3_compute_md5_hash(&empty);
  790. }
  791. /* a CURLOPT_WRITEFUNCTION to write data that just counts data.
  792. * s3_write_data should be NULL or a pointer to an gint64.
  793. */
  794. size_t
  795. s3_counter_write_func(G_GNUC_UNUSED void *ptr, size_t size, size_t nmemb, void *stream)
  796. {
  797. gint64 *count = (gint64*) stream, inc = nmemb*size;
  798. if (count) *count += inc;
  799. return inc;
  800. }
  801. void
  802. s3_counter_reset_func(void *stream)
  803. {
  804. gint64 *count = (gint64*) stream;
  805. if (count) *count = 0;
  806. }
  807. #ifdef _WIN32
  808. /* a CURLOPT_READFUNCTION to read data from a file. */
  809. size_t
  810. s3_file_read_func(void *ptr, size_t size, size_t nmemb, void * stream)
  811. {
  812. HANDLE *hFile = (HANDLE *) stream;
  813. DWORD bytes_read;
  814. ReadFile(hFile, ptr, (DWORD) size*nmemb, &bytes_read, NULL);
  815. return bytes_read;
  816. }
  817. size_t
  818. s3_file_size_func(void *stream)
  819. {
  820. HANDLE *hFile = (HANDLE *) stream;
  821. DWORD size = GetFileSize(hFile, NULL);
  822. if (INVALID_FILE_SIZE == size) {
  823. return -1;
  824. } else {
  825. return size;
  826. }
  827. }
  828. GByteArray*
  829. s3_file_md5_func(void *stream)
  830. {
  831. #define S3_MD5_BUF_SIZE (10*1024)
  832. HANDLE *hFile = (HANDLE *) stream;
  833. guint8 buf[S3_MD5_BUF_SIZE];
  834. DWORD bytes_read;
  835. MD5_CTX md5_ctx;
  836. GByteArray *ret = NULL;
  837. g_assert(INVALID_SET_FILE_POINTER != SetFilePointer(hFile, 0, NULL, FILE_BEGIN));
  838. ret = g_byte_array_sized_new(S3_MD5_HASH_BYTE_LEN);
  839. g_byte_array_set_size(ret, S3_MD5_HASH_BYTE_LEN);
  840. MD5_Init(&md5_ctx);
  841. while (ReadFile(hFile, buf, S3_MD5_BUF_SIZE, &bytes_read, NULL)) {
  842. MD5_Update(&md5_ctx, buf, bytes_read);
  843. }
  844. MD5_Final(ret->data, &md5_ctx);
  845. g_assert(INVALID_SET_FILE_POINTER != SetFilePointer(hFile, 0, NULL, FILE_BEGIN));
  846. return ret;
  847. #undef S3_MD5_BUF_SIZE
  848. }
  849. GByteArray*
  850. s3_file_reset_func(void *stream)
  851. {
  852. g_assert(INVALID_SET_FILE_POINTER != SetFilePointer(hFile, 0, NULL, FILE_BEGIN));
  853. }
  854. /* a CURLOPT_WRITEFUNCTION to write data to a file. */
  855. size_t
  856. s3_file_write_func(void *ptr, size_t size, size_t nmemb, void *stream)
  857. {
  858. HANDLE *hFile = (HANDLE *) stream;
  859. DWORD bytes_written;
  860. WriteFile(hFile, ptr, (DWORD) size*nmemb, &bytes_written, NULL);
  861. return bytes_written;
  862. }
  863. #endif
  864. static int
  865. curl_debug_message(CURL *curl G_GNUC_UNUSED,
  866. curl_infotype type,
  867. char *s,
  868. size_t len,
  869. void *unused G_GNUC_UNUSED)
  870. {
  871. char *lineprefix;
  872. char *message;
  873. char **lines, **line;
  874. switch (type) {
  875. case CURLINFO_TEXT:
  876. lineprefix="";
  877. break;
  878. case CURLINFO_HEADER_IN:
  879. lineprefix="Hdr In: ";
  880. break;
  881. case CURLINFO_HEADER_OUT:
  882. lineprefix="Hdr Out: ";
  883. break;
  884. default:
  885. /* ignore data in/out -- nobody wants to see that in the
  886. * debug logs! */
  887. return 0;
  888. }
  889. /* split the input into lines */
  890. message = g_strndup(s, (gsize) len);
  891. lines = g_strsplit(message, "\n", -1);
  892. g_free(message);
  893. for (line = lines; *line; line++) {
  894. if (**line == '\0') continue; /* skip blank lines */
  895. g_debug("%s%s", lineprefix, *line);
  896. }
  897. g_strfreev(lines);
  898. return 0;
  899. }
  900. static s3_result_t
  901. perform_request(S3Handle *hdl,
  902. const char *verb,
  903. const char *bucket,
  904. const char *key,
  905. const char *subresource,
  906. const char *query,
  907. s3_read_func read_func,
  908. s3_reset_func read_reset_func,
  909. s3_size_func size_func,
  910. s3_md5_func md5_func,
  911. gpointer read_data,
  912. s3_write_func write_func,
  913. s3_reset_func write_reset_func,
  914. gpointer write_data,
  915. s3_progress_func progress_func,
  916. gpointer progress_data,
  917. const result_handling_t *result_handling)
  918. {
  919. char *url = NULL;
  920. s3_result_t result = S3_RESULT_FAIL; /* assume the worst.. */
  921. CURLcode curl_code = CURLE_OK;
  922. char curl_error_buffer[CURL_ERROR_SIZE] = "";
  923. struct curl_slist *headers = NULL;
  924. /* Set S3Internal Data */
  925. S3InternalData int_writedata = {{NULL, 0, 0, MAX_ERROR_RESPONSE_LEN}, NULL, NULL, NULL, FALSE, FALSE, NULL, hdl};
  926. gboolean should_retry;
  927. guint retries = 0;
  928. gulong backoff = EXPONENTIAL_BACKOFF_START_USEC;
  929. /* corresponds to PUT, HEAD, GET, and POST */
  930. int curlopt_upload = 0, curlopt_nobody = 0, curlopt_httpget = 0, curlopt_post = 0;
  931. /* do we want to examine the headers */
  932. const char *curlopt_customrequest = NULL;
  933. /* for MD5 calculation */
  934. GByteArray *md5_hash = NULL;
  935. gchar *md5_hash_hex = NULL, *md5_hash_b64 = NULL;
  936. size_t request_body_size = 0;
  937. g_assert(hdl != NULL && hdl->curl != NULL);
  938. s3_reset(hdl);
  939. url = build_url(hdl->host, hdl->service_path, bucket, key, subresource,
  940. query, hdl->use_subdomain, hdl->use_ssl);
  941. if (!url) goto cleanup;
  942. /* libcurl may behave strangely if these are not set correctly */
  943. if (!strncmp(verb, "PUT", 4)) {
  944. curlopt_upload = 1;
  945. } else if (!strncmp(verb, "GET", 4)) {
  946. curlopt_httpget = 1;
  947. } else if (!strncmp(verb, "POST", 5)) {
  948. curlopt_post = 1;
  949. } else if (!strncmp(verb, "HEAD", 5)) {
  950. curlopt_nobody = 1;
  951. } else {
  952. curlopt_customrequest = verb;
  953. }
  954. if (size_func) {
  955. request_body_size = size_func(read_data);
  956. }
  957. if (md5_func) {
  958. md5_hash = md5_func(read_data);
  959. if (md5_hash) {
  960. md5_hash_b64 = s3_base64_encode(md5_hash);
  961. md5_hash_hex = s3_hex_encode(md5_hash);
  962. g_byte_array_free(md5_hash, TRUE);
  963. }
  964. }
  965. if (!read_func) {
  966. /* Curl will use fread() otherwise */
  967. read_func = s3_empty_read_func;
  968. }
  969. if (write_func) {
  970. int_writedata.write_func = write_func;
  971. int_writedata.reset_func = write_reset_func;
  972. int_writedata.write_data = write_data;
  973. } else {
  974. /* Curl will use fwrite() otherwise */
  975. int_writedata.write_func = s3_counter_write_func;
  976. int_writedata.reset_func = s3_counter_reset_func;
  977. int_writedata.write_data = NULL;
  978. }
  979. while (1) {
  980. /* reset things */
  981. if (headers) {
  982. curl_slist_free_all(headers);
  983. }
  984. curl_error_buffer[0] = '\0';
  985. if (read_reset_func) {
  986. read_reset_func(read_data);
  987. }
  988. /* calls write_reset_func */
  989. s3_internal_reset_func(&int_writedata);
  990. /* set up the request */
  991. headers = authenticate_request(hdl, verb, bucket, key, subresource,
  992. md5_hash_b64);
  993. if (hdl->use_ssl && hdl->ca_info) {
  994. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_CAINFO, hdl->ca_info)))
  995. goto curl_error;
  996. }
  997. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_VERBOSE, hdl->verbose)))
  998. goto curl_error;
  999. if (hdl->verbose) {
  1000. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_DEBUGFUNCTION,
  1001. curl_debug_message)))
  1002. goto curl_error;
  1003. }
  1004. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_ERRORBUFFER,
  1005. curl_error_buffer)))
  1006. goto curl_error;
  1007. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_NOPROGRESS, 1)))
  1008. goto curl_error;
  1009. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_FOLLOWLOCATION, 1)))
  1010. goto curl_error;
  1011. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_URL, url)))
  1012. goto curl_error;
  1013. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_HTTPHEADER,
  1014. headers)))
  1015. goto curl_error;
  1016. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_WRITEFUNCTION, s3_internal_write_func)))
  1017. goto curl_error;
  1018. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_WRITEDATA, &int_writedata)))
  1019. goto curl_error;
  1020. /* Note: we always have to set this apparently, for consistent "end of header" detection */
  1021. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_HEADERFUNCTION, s3_internal_header_func)))
  1022. goto curl_error;
  1023. /* Note: if set, CURLOPT_HEADERDATA seems to also be used for CURLOPT_WRITEDATA ? */
  1024. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_HEADERDATA, &int_writedata)))
  1025. goto curl_error;
  1026. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_PROGRESSFUNCTION, progress_func)))
  1027. goto curl_error;
  1028. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_PROGRESSDATA, progress_data)))
  1029. goto curl_error;
  1030. /* CURLOPT_INFILESIZE_LARGE added in 7.11.0 */
  1031. #if LIBCURL_VERSION_NUM >= 0x070b00
  1032. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)request_body_size)))
  1033. goto curl_error;
  1034. #else
  1035. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_INFILESIZE, (long)request_body_size)))
  1036. goto curl_error;
  1037. #endif
  1038. /* CURLOPT_MAX_{RECV,SEND}_SPEED_LARGE added in 7.15.5 */
  1039. #if LIBCURL_VERSION_NUM >= 0x070f05
  1040. if (s3_curl_throttling_compat()) {
  1041. if (hdl->max_send_speed)
  1042. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_MAX_SEND_SPEED_LARGE, (curl_off_t)hdl->max_send_speed)))
  1043. goto curl_error;
  1044. if (hdl->max_recv_speed)
  1045. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_MAX_SEND_SPEED_LARGE, (curl_off_t)hdl->max_recv_speed)))
  1046. goto curl_error;
  1047. }
  1048. #endif
  1049. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_HTTPGET, curlopt_httpget)))
  1050. goto curl_error;
  1051. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_UPLOAD, curlopt_upload)))
  1052. goto curl_error;
  1053. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_POST, curlopt_post)))
  1054. goto curl_error;
  1055. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_NOBODY, curlopt_nobody)))
  1056. goto curl_error;
  1057. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_CUSTOMREQUEST,
  1058. curlopt_customrequest)))
  1059. goto curl_error;
  1060. if (curlopt_upload) {
  1061. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_READFUNCTION, read_func)))
  1062. goto curl_error;
  1063. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_READDATA, read_data)))
  1064. goto curl_error;
  1065. } else {
  1066. /* Clear request_body options. */
  1067. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_READFUNCTION,
  1068. NULL)))
  1069. goto curl_error;
  1070. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_READDATA,
  1071. NULL)))
  1072. goto curl_error;
  1073. }
  1074. /* Perform the request */
  1075. curl_code = curl_easy_perform(hdl->curl);
  1076. /* interpret the response into hdl->last* */
  1077. curl_error: /* (label for short-circuiting the curl_easy_perform call) */
  1078. should_retry = interpret_response(hdl, curl_code, curl_error_buffer,
  1079. int_writedata.resp_buf.buffer, int_writedata.resp_buf.buffer_pos, int_writedata.etag, md5_hash_hex);
  1080. /* and, unless we know we need to retry, see what we're to do now */
  1081. if (!should_retry) {
  1082. result = lookup_result(result_handling, hdl->last_response_code,
  1083. hdl->last_s3_error_code, hdl->last_curl_code);
  1084. /* break out of the while(1) unless we're retrying */
  1085. if (result != S3_RESULT_RETRY)
  1086. break;
  1087. }
  1088. if (retries >= EXPONENTIAL_BACKOFF_MAX_RETRIES) {
  1089. /* we're out of retries, so annotate hdl->last_message appropriately and bail
  1090. * out. */
  1091. char *m = g_strdup_printf("Too many retries; last message was '%s'", hdl->last_message);
  1092. if (hdl->last_message) g_free(hdl->last_message);
  1093. hdl->last_message = m;
  1094. result = S3_RESULT_FAIL;
  1095. break;
  1096. }
  1097. g_usleep(backoff);
  1098. retries++;
  1099. backoff *= EXPONENTIAL_BACKOFF_BASE;
  1100. }
  1101. if (result != S3_RESULT_OK) {
  1102. g_debug(_("%s %s failed with %d/%s"), verb, url,
  1103. hdl->last_response_code,
  1104. s3_error_name_from_code(hdl->last_s3_error_code));
  1105. }
  1106. cleanup:
  1107. g_free(url);
  1108. if (headers) curl_slist_free_all(headers);
  1109. g_free(md5_hash_b64);
  1110. g_free(md5_hash_hex);
  1111. /* we don't deallocate the response body -- we keep it for later */
  1112. hdl->last_response_body = int_writedata.resp_buf.buffer;
  1113. hdl->last_response_body_size = int_writedata.resp_buf.buffer_pos;
  1114. hdl->last_num_retries = retries;
  1115. return result;
  1116. }
  1117. static size_t
  1118. s3_internal_write_func(void *ptr, size_t size, size_t nmemb, void * stream)
  1119. {
  1120. S3InternalData *data = (S3InternalData *) stream;
  1121. size_t bytes_saved;
  1122. if (!data->headers_done)
  1123. return size*nmemb;
  1124. /* call write on internal buffer (if not full) */
  1125. if (data->int_write_done) {
  1126. bytes_saved = 0;
  1127. } else {
  1128. bytes_saved = s3_buffer_write_func(ptr, size, nmemb, &data->resp_buf);
  1129. if (!bytes_saved) {
  1130. data->int_write_done = TRUE;
  1131. }
  1132. }
  1133. /* call write on user buffer */
  1134. if (data->write_func) {
  1135. return data->write_func(ptr, size, nmemb, data->write_data);
  1136. } else {
  1137. return bytes_saved;
  1138. }
  1139. }
  1140. static void
  1141. s3_internal_reset_func(void * stream)
  1142. {
  1143. S3InternalData *data = (S3InternalData *) stream;
  1144. s3_buffer_reset_func(&data->resp_buf);
  1145. data->headers_done = FALSE;
  1146. data->int_write_done = FALSE;
  1147. data->etag = NULL;
  1148. if (data->reset_func) {
  1149. data->reset_func(data->write_data);
  1150. }
  1151. }
  1152. static size_t
  1153. s3_internal_header_func(void *ptr, size_t size, size_t nmemb, void * stream)
  1154. {
  1155. static const char *final_header = "\r\n";
  1156. time_t remote_time_in_sec,local_time;
  1157. char *header;
  1158. regmatch_t pmatch[2];
  1159. S3InternalData *data = (S3InternalData *) stream;
  1160. header = g_strndup((gchar *) ptr, (gsize) size*nmemb);
  1161. if (!s3_regexec_wrap(&etag_regex, header, 2, pmatch, 0))
  1162. data->etag = find_regex_substring(header, pmatch[1]);
  1163. if (!strcmp(final_header, header))
  1164. data->headers_done = TRUE;
  1165. /* If date header is found */
  1166. if (!s3_regexec_wrap(&date_sync_regex, header, 2, pmatch, 0)){
  1167. char *date = find_regex_substring(header, pmatch[1]);
  1168. /* Remote time is always in GMT: RFC 2616 */
  1169. /* both curl_getdate and time operate in UTC, so no timezone math is necessary */
  1170. if ( (remote_time_in_sec = curl_getdate(date, NULL)) < 0 ){
  1171. g_debug("Error: Conversion of remote time to seconds failed.");
  1172. data->hdl->time_offset_with_s3 = 0;
  1173. }else{
  1174. local_time = time(NULL);
  1175. /* Offset time */
  1176. data->hdl->time_offset_with_s3 = remote_time_in_sec - local_time;
  1177. if (data->hdl->verbose)
  1178. g_debug("Time Offset (remote - local) :%ld",(long)data->hdl->time_offset_with_s3);
  1179. }
  1180. g_free(date);
  1181. }
  1182. g_free(header);
  1183. return size*nmemb;
  1184. }
  1185. static gboolean
  1186. compile_regexes(void)
  1187. {
  1188. #ifdef HAVE_REGEX_H
  1189. /* using POSIX regular expressions */
  1190. struct {const char * str; int flags; regex_t *regex;} regexes[] = {
  1191. {"<Code>[[:space:]]*([^<]*)[[:space:]]*</Code>", REG_EXTENDED | REG_ICASE, &error_name_regex},
  1192. {"^ETag:[[:space:]]*\"([^\"]+)\"[[:space:]]*$", REG_EXTENDED | REG_ICASE | REG_NEWLINE, &etag_regex},
  1193. {"<Message>[[:space:]]*([^<]*)[[:space:]]*</Message>", REG_EXTENDED | REG_ICASE, &message_regex},
  1194. {"^[a-z0-9](-*[a-z0-9]){2,62}$", REG_EXTENDED | REG_NOSUB, &subdomain_regex},
  1195. {"(/>)|(>([^<]*)</LocationConstraint>)", REG_EXTENDED | REG_ICASE, &location_con_regex},
  1196. {"^Date:(.*)\r",REG_EXTENDED | REG_ICASE | REG_NEWLINE, &date_sync_regex},
  1197. {NULL, 0, NULL}
  1198. };
  1199. char regmessage[1024];
  1200. int size, i;
  1201. int reg_result;
  1202. for (i = 0; regexes[i].str; i++) {
  1203. reg_result = regcomp(regexes[i].regex, regexes[i].str, regexes[i].flags);
  1204. if (reg_result != 0) {
  1205. size = regerror(reg_result, regexes[i].regex, regmessage, sizeof(regmessage));
  1206. g_error(_("Regex error: %s"), regmessage);
  1207. return FALSE;
  1208. }
  1209. }
  1210. #else /* ! HAVE_REGEX_H */
  1211. /* using PCRE via GLib */
  1212. struct {const char * str; int flags; regex_t *regex;} regexes[] = {
  1213. {"<Code>\\s*([^<]*)\\s*</Code>",
  1214. G_REGEX_OPTIMIZE | G_REGEX_CASELESS,
  1215. &error_name_regex},
  1216. {"^ETag:\\s*\"([^\"]+)\"\\s*$",
  1217. G_REGEX_OPTIMIZE | G_REGEX_CASELESS,
  1218. &etag_regex},
  1219. {"<Message>\\s*([^<]*)\\s*</Message>",
  1220. G_REGEX_OPTIMIZE | G_REGEX_CASELESS,
  1221. &message_regex},
  1222. {"^[a-z0-9]((-*[a-z0-9])|(\\.[a-z0-9])){2,62}$",
  1223. G_REGEX_OPTIMIZE | G_REGEX_NO_AUTO_CAPTURE,
  1224. &subdomain_regex},
  1225. {"(/>)|(>([^<]*)</LocationConstraint>)",
  1226. G_REGEX_CASELESS,
  1227. &location_con_regex},
  1228. {"^Date:(.*)\\r",
  1229. G_REGEX_OPTIMIZE | G_REGEX_CASELESS,
  1230. &date_sync_regex},
  1231. {NULL, 0, NULL}
  1232. };
  1233. int i;
  1234. GError *err = NULL;
  1235. for (i = 0; regexes[i].str; i++) {
  1236. *(regexes[i].regex) = g_regex_new(regexes[i].str, regexes[i].flags, 0, &err);
  1237. if (err) {
  1238. g_error(_("Regex error: %s"), err->message);
  1239. g_error_free(err);
  1240. return FALSE;
  1241. }
  1242. }
  1243. #endif
  1244. return TRUE;
  1245. }
  1246. /*
  1247. * Public function implementations
  1248. */
  1249. gboolean s3_init(void)
  1250. {
  1251. static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
  1252. static gboolean init = FALSE, ret;
  1253. /* n.b. curl_global_init is called in common-src/glib-util.c:glib_init() */
  1254. g_static_mutex_lock (&mutex);
  1255. if (!init) {
  1256. ret = compile_regexes();
  1257. init = TRUE;
  1258. }
  1259. g_static_mutex_unlock(&mutex);
  1260. return ret;
  1261. }
  1262. gboolean
  1263. s3_curl_location_compat(void)
  1264. {
  1265. curl_version_info_data *info;
  1266. info = curl_version_info(CURLVERSION_NOW);
  1267. return info->version_num > 0x070a02;
  1268. }
  1269. gboolean
  1270. s3_bucket_location_compat(const char *bucket)
  1271. {
  1272. return !s3_regexec_wrap(&subdomain_regex, bucket, 0, NULL, 0);
  1273. }
  1274. S3Handle *
  1275. s3_open(const char *access_key,
  1276. const char *secret_key,
  1277. const char *host,
  1278. const char *service_path,
  1279. const gboolean use_subdomain,
  1280. const char *user_token,
  1281. const char *bucket_location,
  1282. const char *storage_class,
  1283. const char *ca_info
  1284. )
  1285. {
  1286. S3Handle *hdl;
  1287. hdl = g_new0(S3Handle, 1);
  1288. if (!hdl) goto error;
  1289. hdl->verbose = FALSE;
  1290. hdl->use_ssl = s3_curl_supports_ssl();
  1291. g_assert(access_key);
  1292. hdl->access_key = g_strdup(access_key);
  1293. g_assert(secret_key);
  1294. hdl->secret_key = g_strdup(secret_key);
  1295. /* NULL is okay */
  1296. hdl->user_token = g_strdup(user_token);
  1297. /* NULL is okay */
  1298. hdl->bucket_location = g_strdup(bucket_location);
  1299. /* NULL is ok */
  1300. hdl->storage_class = g_strdup(storage_class);
  1301. /* NULL is okay */
  1302. hdl->ca_info = g_strdup(ca_info);
  1303. if (!is_non_empty_string(host))
  1304. host = "s3.amazonaws.com";
  1305. hdl->host = g_strdup(host);
  1306. hdl->use_subdomain = use_subdomain ||
  1307. (strcmp(host, "s3.amazonaws.com") == 0 &&
  1308. is_non_empty_string(hdl->bucket_location));
  1309. if (service_path) {
  1310. if (service_path[0] != '/')
  1311. hdl->service_path = g_strdup_printf("/%s", service_path);
  1312. else
  1313. hdl->service_path = g_strdup(service_path);
  1314. } else {
  1315. hdl->service_path = NULL;
  1316. }
  1317. hdl->curl = curl_easy_init();
  1318. if (!hdl->curl) goto error;
  1319. return hdl;
  1320. error:
  1321. s3_free(hdl);
  1322. return NULL;
  1323. }
  1324. void
  1325. s3_free(S3Handle *hdl)
  1326. {
  1327. s3_reset(hdl);
  1328. if (hdl) {
  1329. g_free(hdl->access_key);
  1330. g_free(hdl->secret_key);
  1331. if (hdl->user_token) g_free(hdl->user_token);
  1332. if (hdl->bucket_location) g_free(hdl->bucket_location);
  1333. if (hdl->storage_class) g_free(hdl->storage_class);
  1334. if (hdl->host) g_free(hdl->host);
  1335. if (hdl->service_path) g_free(hdl->service_path);
  1336. if (hdl->curl) curl_easy_cleanup(hdl->curl);
  1337. g_free(hdl);
  1338. }
  1339. }
  1340. void
  1341. s3_reset(S3Handle *hdl)
  1342. {
  1343. if (hdl) {
  1344. /* We don't call curl_easy_reset here, because doing that in curl
  1345. * < 7.16 blanks the default CA certificate path, and there's no way
  1346. * to get it back. */
  1347. if (hdl->last_message) {
  1348. g_free(hdl->last_message);
  1349. hdl->last_message = NULL;
  1350. }
  1351. hdl->last_response_code = 0;
  1352. hdl->last_curl_code = 0;
  1353. hdl->last_s3_error_code = 0;
  1354. hdl->last_num_retries = 0;
  1355. if (hdl->last_response_body) {
  1356. g_free(hdl->last_response_body);
  1357. hdl->last_response_body = NULL;
  1358. }
  1359. hdl->last_response_body_size = 0;
  1360. }
  1361. }
  1362. void
  1363. s3_error(S3Handle *hdl,
  1364. const char **message,
  1365. guint *response_code,
  1366. s3_error_code_t *s3_error_code,
  1367. const char **s3_error_name,
  1368. CURLcode *curl_code,
  1369. guint *num_retries)
  1370. {
  1371. if (hdl) {
  1372. if (message) *message = hdl->last_message;
  1373. if (response_code) *response_code = hdl->last_response_code;
  1374. if (s3_error_code) *s3_error_code = hdl->last_s3_error_code;
  1375. if (s3_error_name) *s3_error_name = s3_error_name_from_code(hdl->last_s3_error_code);
  1376. if (curl_code) *curl_code = hdl->last_curl_code;
  1377. if (num_retries) *num_retries = hdl->last_num_retries;
  1378. } else {
  1379. /* no hdl? return something coherent, anyway */
  1380. if (message) *message = "NULL S3Handle";
  1381. if (response_code) *response_code = 0;
  1382. if (s3_error_code) *s3_error_code = 0;
  1383. if (s3_error_name) *s3_error_name = NULL;
  1384. if (curl_code) *curl_code = 0;
  1385. if (num_retries) *num_retries = 0;
  1386. }
  1387. }
  1388. void
  1389. s3_verbose(S3Handle *hdl, gboolean verbose)
  1390. {
  1391. hdl->verbose = verbose;
  1392. }
  1393. gboolean
  1394. s3_set_max_send_speed(S3Handle *hdl, guint64 max_send_speed)
  1395. {
  1396. if (!s3_curl_throttling_compat())
  1397. return FALSE;
  1398. hdl->max_send_speed = max_send_speed;
  1399. return TRUE;
  1400. }
  1401. gboolean
  1402. s3_set_max_recv_speed(S3Handle *hdl, guint64 max_recv_speed)
  1403. {
  1404. if (!s3_curl_throttling_compat())
  1405. return FALSE;
  1406. hdl->max_recv_speed = max_recv_speed;
  1407. return TRUE;
  1408. }
  1409. gboolean
  1410. s3_use_ssl(S3Handle *hdl, gboolean use_ssl)
  1411. {
  1412. gboolean ret = TRUE;
  1413. if (use_ssl & !s3_curl_supports_ssl()) {
  1414. ret = FALSE;
  1415. } else {
  1416. hdl->use_ssl = use_ssl;
  1417. }
  1418. return ret;
  1419. }
  1420. char *
  1421. s3_strerror(S3Handle *hdl)
  1422. {
  1423. const char *message;
  1424. guint response_code;
  1425. const char *s3_error_name;
  1426. CURLcode curl_code;
  1427. guint num_retries;
  1428. char s3_info[256] = "";
  1429. char response_info[16] = "";
  1430. char curl_info[32] = "";
  1431. char retries_info[32] = "";
  1432. s3_error(hdl, &message, &response_code, NULL, &s3_error_name, &curl_code, &num_retries);
  1433. if (!message)
  1434. message = "Unknown S3 error";
  1435. if (s3_error_name)
  1436. g_snprintf(s3_info, sizeof(s3_info), " (%s)", s3_error_name);
  1437. if (response_code)
  1438. g_snprintf(response_info, sizeof(response_info), " (HTTP %d)", response_code);
  1439. if (curl_code)
  1440. g_snprintf(curl_info, sizeof(curl_info), " (CURLcode %d)", curl_code);
  1441. if (num_retries)
  1442. g_snprintf(retries_info, sizeof(retries_info), " (after %d retries)", num_retries);
  1443. return g_strdup_printf("%s%s%s%s%s", message, s3_info, curl_info, response_info, retries_info);
  1444. }
  1445. /* Perform an upload. When this function returns, KEY and
  1446. * BUFFER remain the responsibility of the caller.
  1447. *
  1448. * @param self: the s3 device
  1449. * @param bucket: the bucket to which the upload should be made
  1450. * @param key: the key to which the upload should be made
  1451. * @param buffer: the data to be uploaded
  1452. * @param buffer_len: the length of the data to upload
  1453. * @returns: false if an error ocurred
  1454. */
  1455. gboolean
  1456. s3_upload(S3Handle *hdl,
  1457. const char *bucket,
  1458. const char *key,
  1459. s3_read_func read_func,
  1460. s3_reset_func reset_func,
  1461. s3_size_func size_func,
  1462. s3_md5_func md5_func,
  1463. gpointer read_data,
  1464. s3_progress_func progress_func,
  1465. gpointer progress_data)
  1466. {
  1467. s3_result_t result = S3_RESULT_FAIL;
  1468. static result_handling_t result_handling[] = {
  1469. { 200, 0, 0, S3_RESULT_OK },
  1470. RESULT_HANDLING_ALWAYS_RETRY,
  1471. { 0, 0, 0, /* default: */ S3_RESULT_FAIL }
  1472. };
  1473. g_assert(hdl != NULL);
  1474. result = perform_request(hdl, "PUT", bucket, key, NULL, NULL,
  1475. read_func, reset_func, size_func, md5_func, read_data,
  1476. NULL, NULL, NULL, progress_func, progress_data,
  1477. result_handling);
  1478. return result == S3_RESULT_OK;
  1479. }
  1480. /* Private structure for our "thunk", which tracks where the user is in the list
  1481. * of keys. */
  1482. struct list_keys_thunk {
  1483. GSList *filename_list; /* all pending filenames */
  1484. gboolean in_contents; /* look for "key" entities in here */
  1485. gboolean in_common_prefixes; /* look for "prefix" entities in here */
  1486. gboolean is_truncated;
  1487. gchar *next_marker;
  1488. guint64 size;
  1489. gboolean want_text;
  1490. gchar *text;
  1491. gsize text_len;
  1492. };
  1493. /* Functions for a SAX parser to parse the XML from Amazon */
  1494. static void
  1495. list_start_element(GMarkupParseContext *context G_GNUC_UNUSED,
  1496. const gchar *element_name,
  1497. const gchar **attribute_names G_GNUC_UNUSED,
  1498. const gchar **attribute_values G_GNUC_UNUSED,
  1499. gpointer user_data,
  1500. GError **error G_GNUC_UNUSED)
  1501. {
  1502. struct list_keys_thunk *thunk = (struct list_keys_thunk *)user_data;
  1503. thunk->want_text = 0;
  1504. if (g_ascii_strcasecmp(element_name, "contents") == 0) {
  1505. thunk->in_contents = 1;
  1506. } else if (g_ascii_strcasecmp(element_name, "commonprefixes") == 0) {
  1507. thunk->in_common_prefixes = 1;
  1508. } else if (g_ascii_strcasecmp(element_name, "prefix") == 0 && thunk->in_common_prefixes) {
  1509. thunk->want_text = 1;
  1510. } else if (g_ascii_strcasecmp(element_name, "key") == 0 && thunk->in_contents) {
  1511. thunk->want_text = 1;
  1512. } else if (g_ascii_strcasecmp(element_name, "size") == 0 && thunk->in_contents) {
  1513. thunk->want_text = 1;
  1514. } else if (g_ascii_strcasecmp(element_name, "istruncated")) {
  1515. thunk->want_text = 1;
  1516. } else if (g_ascii_strcasecmp(element_name, "nextmarker")) {
  1517. thunk->want_text = 1;
  1518. }
  1519. }
  1520. static void
  1521. list_end_element(GMarkupParseContext *context G_GNUC_UNUSED,
  1522. const gchar *element_name,
  1523. gpointer user_data,
  1524. GError **error G_GNUC_UNUSED)
  1525. {
  1526. struct list_keys_thunk *thunk = (struct list_keys_thunk *)user_data;
  1527. if (g_ascii_strcasecmp(element_name, "contents") == 0) {
  1528. thunk->in_contents = 0;
  1529. } else if (g_ascii_strcasecmp(element_name, "commonprefixes") == 0) {
  1530. thunk->in_common_prefixes = 0;
  1531. } else if (g_ascii_strcasecmp(element_name, "key") == 0 && thunk->in_contents) {
  1532. thunk->filename_list = g_slist_prepend(thunk->filename_list, thunk->text);
  1533. thunk->text = NULL;
  1534. } else if (g_ascii_strcasecmp(element_name, "size") == 0 && thunk->in_contents) {
  1535. thunk->size += g_ascii_strtoull (thunk->text, NULL, 10);
  1536. thunk->text = NULL;
  1537. } else if (g_ascii_strcasecmp(element_name, "prefix") == 0 && thunk->in_common_prefixes) {
  1538. thunk->filename_list = g_slist_prepend(thunk->filename_list, thunk->text);
  1539. thunk->text = NULL;
  1540. } else if (g_ascii_strcasecmp(element_name, "istruncated") == 0) {
  1541. if (thunk->text && g_ascii_strncasecmp(thunk->text, "false", 5) != 0)
  1542. thunk->is_truncated = TRUE;
  1543. } else if (g_ascii_strcasecmp(element_name, "nextmarker") == 0) {
  1544. if (thunk->next_marker) g_free(thunk->next_marker);
  1545. thunk->next_marker = thunk->text;
  1546. thunk->text = NULL;
  1547. }
  1548. }
  1549. static void
  1550. list_text(GMarkupParseContext *context G_GNUC_UNUSED,
  1551. const gchar *text,
  1552. gsize text_len,
  1553. gpointer user_data,
  1554. GError **error G_GNUC_UNUSED)
  1555. {
  1556. struct list_keys_thunk *thunk = (struct list_keys_thunk *)user_data;
  1557. if (thunk->want_text) {
  1558. if (thunk->text) g_free(thunk->text);
  1559. thunk->text = g_strndup(text, text_len);
  1560. }
  1561. }
  1562. /* Perform a fetch from S3; several fetches may be involved in a
  1563. * single listing operation */
  1564. static s3_result_t
  1565. list_fetch(S3Handle *hdl,
  1566. const char *bucket,
  1567. const char *prefix,
  1568. const char *delimiter,
  1569. const char *marker,
  1570. const char *max_keys,
  1571. CurlBuffer *buf)
  1572. {
  1573. s3_result_t result = S3_RESULT_FAIL;
  1574. static result_handling_t result_handling[] = {
  1575. { 200, 0, 0, S3_RESULT_OK },
  1576. RESULT_HANDLING_ALWAYS_RETRY,
  1577. { 0, 0, 0, /* default: */ S3_RESULT_FAIL }
  1578. };
  1579. const char* pos_parts[][2] = {
  1580. {"prefix", prefix},
  1581. {"delimiter", delimiter},
  1582. {"marker", marker},
  1583. {"max-keys", max_keys},
  1584. {NULL, NULL}
  1585. };
  1586. char *esc_value;
  1587. GString *query;
  1588. guint i;
  1589. gboolean have_prev_part = FALSE;
  1590. /* loop over possible parts to build query string */
  1591. query = g_string_new("");
  1592. for (i = 0; pos_parts[i][0]; i++) {
  1593. if (pos_parts[i][1]) {
  1594. if (have_prev_part)
  1595. g_string_append(query, "&");
  1596. else
  1597. have_prev_part = TRUE;
  1598. esc_value = curl_escape(pos_parts[i][1], 0);
  1599. g_string_append_printf(query, "%s=%s", pos_parts[i][0], esc_value);
  1600. curl_free(esc_value);
  1601. }
  1602. }
  1603. /* and perform the request on that URI */
  1604. result = perform_request(hdl, "GET", bucket, NULL, NULL, query->str,
  1605. NULL, NULL, NULL, NULL, NULL,
  1606. S3_BUFFER_WRITE_FUNCS, buf, NULL, NULL,
  1607. result_handling);
  1608. if (query) g_string_free(query, TRUE);
  1609. return result;
  1610. }
  1611. gboolean
  1612. s3_list_keys(S3Handle *hdl,
  1613. const char *bucket,
  1614. const char *prefix,
  1615. const char *delimiter,
  1616. GSList **list,
  1617. guint64 *total_size)
  1618. {
  1619. /*
  1620. * max len of XML variables:
  1621. * bucket: 255 bytes (p12 API Version 2006-03-01)
  1622. * key: 1024 bytes (p15 API Version 2006-03-01)
  1623. * size per key: 5GB bytes (p6 API Version 2006-03-01)
  1624. * size of size 10 bytes (i.e. 10 decimal digits)
  1625. * etag: 44 (observed+assumed)
  1626. * owner ID: 64 (observed+assumed)
  1627. * owner DisplayName: 255 (assumed)
  1628. * StorageClass: const (p18 API Version 2006-03-01)
  1629. */
  1630. static const guint MAX_RESPONSE_LEN = 1000*2000;
  1631. static const char *MAX_KEYS = "1000";
  1632. struct list_keys_thunk thunk;
  1633. GMarkupParseContext *ctxt = NULL;
  1634. static GMarkupParser parser = { list_start_element, list_end_element, list_text, NULL, NULL };
  1635. GError *err = NULL;
  1636. s3_result_t result = S3_RESULT_FAIL;
  1637. CurlBuffer buf = {NULL, 0, 0, MAX_RESPONSE_LEN};
  1638. g_assert(list);
  1639. *list = NULL;
  1640. thunk.filename_list = NULL;
  1641. thunk.text = NULL;
  1642. thunk.next_marker = NULL;
  1643. thunk.size = 0;
  1644. /* Loop until S3 has given us the entire picture */
  1645. do {
  1646. s3_buffer_reset_func(&buf);
  1647. /* get some data from S3 */
  1648. result = list_fetch(hdl, bucket, prefix, delimiter, thunk.next_marker, MAX_KEYS, &buf);
  1649. if (result != S3_RESULT_OK) goto cleanup;
  1650. /* run the parser over it */
  1651. thunk.in_contents = FALSE;
  1652. thunk.in_common_prefixes = FALSE;
  1653. thunk.is_truncated = FALSE;
  1654. thunk.want_text = FALSE;
  1655. ctxt = g_markup_parse_context_new(&parser, 0, (gpointer)&thunk, NULL);
  1656. if (!g_markup_parse_context_parse(ctxt, buf.buffer, buf.buffer_pos, &err)) {
  1657. if (hdl->last_message) g_free(hdl->last_message);
  1658. hdl->last_message = g_strdup(err->message);
  1659. result = S3_RESULT_FAIL;
  1660. goto cleanup;
  1661. }
  1662. if (!g_markup_parse_context_end_parse(ctxt, &err)) {
  1663. if (hdl->last_message) g_free(hdl->last_message);
  1664. hdl->last_message = g_strdup(err->message);
  1665. result = S3_RESULT_FAIL;
  1666. goto cleanup;
  1667. }
  1668. g_markup_parse_context_free(ctxt);
  1669. ctxt = NULL;
  1670. } while (thunk.next_marker);
  1671. cleanup:
  1672. if (err) g_error_free(err);
  1673. if (thunk.text) g_free(thunk.text);
  1674. if (thunk.next_marker) g_free(thunk.next_marker);
  1675. if (ctxt) g_markup_parse_context_free(ctxt);
  1676. if (buf.buffer) g_free(buf.buffer);
  1677. if (result != S3_RESULT_OK) {
  1678. g_slist_free(thunk.filename_list);
  1679. return FALSE;
  1680. } else {
  1681. *list = thunk.filename_list;
  1682. if(total_size) {
  1683. *total_size = thunk.size;
  1684. }
  1685. return TRUE;
  1686. }
  1687. }
  1688. gboolean
  1689. s3_read(S3Handle *hdl,
  1690. const char *bucket,
  1691. const char *key,
  1692. s3_write_func write_func,
  1693. s3_reset_func reset_func,
  1694. gpointer write_data,
  1695. s3_progress_func progress_func,
  1696. gpointer progress_data)
  1697. {
  1698. s3_result_t result = S3_RESULT_FAIL;
  1699. static result_handling_t result_handling[] = {
  1700. { 200, 0, 0, S3_RESULT_OK },
  1701. RESULT_HANDLING_ALWAYS_RETRY,
  1702. { 0, 0, 0, /* default: */ S3_RESULT_FAIL }
  1703. };
  1704. g_assert(hdl != NULL);
  1705. g_assert(write_func != NULL);
  1706. result = perform_request(hdl, "GET", bucket, key, NULL, NULL,
  1707. NULL, NULL, NULL, NULL, NULL, write_func, reset_func, write_data,
  1708. progress_func, progress_data, result_handling);
  1709. return result == S3_RESULT_OK;
  1710. }
  1711. gboolean
  1712. s3_delete(S3Handle *hdl,
  1713. const char *bucket,
  1714. const char *key)
  1715. {
  1716. s3_result_t result = S3_RESULT_FAIL;
  1717. static result_handling_t result_handling[] = {
  1718. { 204, 0, 0, S3_RESULT_OK },
  1719. { 404, S3_ERROR_NoSuchBucket, 0, S3_RESULT_OK },
  1720. RESULT_HANDLING_ALWAYS_RETRY,
  1721. { 0, 0, 0, /* default: */ S3_RESULT_FAIL }
  1722. };
  1723. g_assert(hdl != NULL);
  1724. result = perform_request(hdl, "DELETE", bucket, key, NULL, NULL,
  1725. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  1726. result_handling);
  1727. return result == S3_RESULT_OK;
  1728. }
  1729. gboolean
  1730. s3_make_bucket(S3Handle *hdl,
  1731. const char *bucket)
  1732. {
  1733. char *body = NULL;
  1734. s3_result_t result = S3_RESULT_FAIL;
  1735. static result_handling_t result_handling[] = {
  1736. { 200, 0, 0, S3_RESULT_OK },
  1737. { 404, S3_ERROR_NoSuchBucket, 0, S3_RESULT_RETRY },
  1738. RESULT_HANDLING_ALWAYS_RETRY,
  1739. { 0, 0, 0, /* default: */ S3_RESULT_FAIL }
  1740. };
  1741. regmatch_t pmatch[4];
  1742. char *loc_end_open, *loc_content;
  1743. CurlBuffer buf = {NULL, 0, 0, 0}, *ptr = NULL;
  1744. s3_read_func read_func = NULL;
  1745. s3_reset_func reset_func = NULL;
  1746. s3_md5_func md5_func = NULL;
  1747. s3_size_func size_func = NULL;
  1748. g_assert(hdl != NULL);
  1749. if (is_non_empty_string(hdl->bucket_location) &&
  1750. 0 != strcmp(AMAZON_WILDCARD_LOCATION, hdl->bucket_location)) {
  1751. if (s3_bucket_location_compat(bucket)) {
  1752. ptr = &buf;
  1753. buf.buffer = g_strdup_printf(AMAZON_BUCKET_CONF_TEMPLATE, hdl->bucket_location);
  1754. buf.buffer_len = (guint) strlen(buf.buffer);
  1755. buf.buffer_pos = 0;
  1756. buf.max_buffer_size = buf.buffer_len;
  1757. read_func = s3_buffer_read_func;
  1758. reset_func = s3_buffer_reset_func;
  1759. size_func = s3_buffer_size_func;
  1760. md5_func = s3_buffer_md5_func;
  1761. } else {
  1762. hdl->last_message = g_strdup_printf(_(
  1763. "Location constraint given for Amazon S3 bucket, "
  1764. "but the bucket name (%s) is not usable as a subdomain."), bucket);
  1765. return FALSE;
  1766. }
  1767. }
  1768. result = perform_request(hdl, "PUT", bucket, NULL, NULL, NULL,
  1769. read_func, reset_func, size_func, md5_func, ptr,
  1770. NULL, NULL, NULL, NULL, NULL, result_handling);
  1771. if (result == S3_RESULT_OK ||
  1772. (result != S3_RESULT_OK &&
  1773. hdl->last_s3_error_code == S3_ERROR_BucketAlreadyOwnedByYou)) {
  1774. /* verify the that the location constraint on the existing bucket matches
  1775. * the one that's configured.
  1776. */
  1777. if (is_non_empty_string(hdl->bucket_location)) {
  1778. result = perform_request(hdl, "GET", bucket, NULL, "location", NULL,
  1779. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  1780. NULL, NULL, result_handling);
  1781. } else {
  1782. result = perform_request(hdl, "GET", bucket, NULL, NULL, NULL,
  1783. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  1784. NULL, NULL, result_handling);
  1785. }
  1786. if (result == S3_RESULT_OK && is_non_empty_string(hdl->bucket_location)) {
  1787. /* return to the default state of failure */
  1788. result = S3_RESULT_FAIL;
  1789. if (body) g_free(body);
  1790. /* use strndup to get a null-terminated string */
  1791. body = g_strndup(hdl->last_response_body, hdl->last_response_body_s