PageRenderTime 55ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/amanda/branches/3_3/device-src/s3.c

#
C | 2132 lines | 1532 code | 286 blank | 314 comment | 350 complexity | 6a32e714936b8f244ba0547fc6b90b65 MD5 | raw file

Large files files are truncated, but you can click here to view the full 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%s>\n\
  83. <LocationConstraint>%s</LocationConstraint>\n\
  84. </CreateBucketConfiguration>"
  85. #define AMAZON_STORAGE_CLASS_HEADER "x-amz-storage-class"
  86. #define AMAZON_SERVER_SIDE_ENCRYPTION_HEADER "x-amz-server-side-encryption"
  87. #define AMAZON_WILDCARD_LOCATION "*"
  88. /* parameters for exponential backoff in the face of retriable errors */
  89. /* start at 0.01s */
  90. #define EXPONENTIAL_BACKOFF_START_USEC G_USEC_PER_SEC/100
  91. /* double at each retry */
  92. #define EXPONENTIAL_BACKOFF_BASE 2
  93. /* retry 14 times (for a total of about 3 minutes spent waiting) */
  94. #define EXPONENTIAL_BACKOFF_MAX_RETRIES 14
  95. /* general "reasonable size" parameters */
  96. #define MAX_ERROR_RESPONSE_LEN (100*1024)
  97. /* Results which should always be retried */
  98. #define RESULT_HANDLING_ALWAYS_RETRY \
  99. { 400, S3_ERROR_RequestTimeout, 0, S3_RESULT_RETRY }, \
  100. { 403, S3_ERROR_RequestTimeTooSkewed,0, S3_RESULT_RETRY }, \
  101. { 409, S3_ERROR_OperationAborted, 0, S3_RESULT_RETRY }, \
  102. { 412, S3_ERROR_PreconditionFailed, 0, S3_RESULT_RETRY }, \
  103. { 500, S3_ERROR_InternalError, 0, S3_RESULT_RETRY }, \
  104. { 501, S3_ERROR_NotImplemented, 0, S3_RESULT_RETRY }, \
  105. { 0, 0, CURLE_COULDNT_CONNECT, S3_RESULT_RETRY }, \
  106. { 0, 0, CURLE_COULDNT_RESOLVE_HOST, S3_RESULT_RETRY }, \
  107. { 0, 0, CURLE_PARTIAL_FILE, S3_RESULT_RETRY }, \
  108. { 0, 0, CURLE_OPERATION_TIMEOUTED, S3_RESULT_RETRY }, \
  109. { 0, 0, CURLE_SSL_CONNECT_ERROR, S3_RESULT_RETRY }, \
  110. { 0, 0, CURLE_SEND_ERROR, S3_RESULT_RETRY }, \
  111. { 0, 0, CURLE_RECV_ERROR, S3_RESULT_RETRY }, \
  112. { 0, 0, CURLE_GOT_NOTHING, S3_RESULT_RETRY }
  113. /*
  114. * Data structures and associated functions
  115. */
  116. struct S3Handle {
  117. /* (all strings in this struct are freed by s3_free()) */
  118. char *access_key;
  119. char *secret_key;
  120. char *user_token;
  121. char *swift_account_id;
  122. char *swift_access_key;
  123. /* attributes for new objects */
  124. char *bucket_location;
  125. char *storage_class;
  126. char *server_side_encryption;
  127. char *proxy;
  128. char *host;
  129. char *service_path;
  130. gboolean use_subdomain;
  131. gboolean openstack_swift_api;
  132. char *ca_info;
  133. char *x_auth_token;
  134. char *x_storage_url;
  135. CURL *curl;
  136. gboolean verbose;
  137. gboolean use_ssl;
  138. guint64 max_send_speed;
  139. guint64 max_recv_speed;
  140. /* information from the last request */
  141. char *last_message;
  142. guint last_response_code;
  143. s3_error_code_t last_s3_error_code;
  144. CURLcode last_curl_code;
  145. guint last_num_retries;
  146. void *last_response_body;
  147. guint last_response_body_size;
  148. /* offset with s3 */
  149. time_t time_offset_with_s3;
  150. };
  151. typedef struct {
  152. CurlBuffer resp_buf;
  153. s3_write_func write_func;
  154. s3_reset_func reset_func;
  155. gpointer write_data;
  156. gboolean headers_done;
  157. gboolean int_write_done;
  158. char *etag;
  159. /* Points to current handle: Added to get hold of s3 offset */
  160. struct S3Handle *hdl;
  161. } S3InternalData;
  162. /* Callback function to examine headers one-at-a-time
  163. *
  164. * @note this is the same as CURLOPT_HEADERFUNCTION
  165. *
  166. * @param data: The pointer to read data from
  167. * @param size: The size of each "element" of the data buffer in bytes
  168. * @param nmemb: The number of elements in the data buffer.
  169. * So, the buffer's size is size*nmemb bytes.
  170. * @param stream: the header_data (an opaque pointer)
  171. *
  172. * @return The number of bytes written to the buffer or
  173. * CURL_WRITEFUNC_PAUSE to pause.
  174. * If it's the number of bytes written, it should match the buffer size
  175. */
  176. typedef size_t (*s3_header_func)(void *data, size_t size, size_t nmemb, void *stream);
  177. /*
  178. * S3 errors */
  179. /* (see preprocessor magic in s3.h) */
  180. static char * s3_error_code_names[] = {
  181. #define S3_ERROR(NAME) #NAME
  182. S3_ERROR_LIST
  183. #undef S3_ERROR
  184. };
  185. /* Convert an s3 error name to an error code. This function
  186. * matches strings case-insensitively, and is appropriate for use
  187. * on data from the network.
  188. *
  189. * @param s3_error_code: the error name
  190. * @returns: the error code (see constants in s3.h)
  191. */
  192. static s3_error_code_t
  193. s3_error_code_from_name(char *s3_error_name);
  194. /* Convert an s3 error code to a string
  195. *
  196. * @param s3_error_code: the error code to convert
  197. * @returns: statically allocated string
  198. */
  199. static const char *
  200. s3_error_name_from_code(s3_error_code_t s3_error_code);
  201. /*
  202. * result handling */
  203. /* result handling is specified by a static array of result_handling structs,
  204. * which match based on response_code (from HTTP) and S3 error code. The result
  205. * given for the first match is used. 0 acts as a wildcard for both response_code
  206. * and s3_error_code. The list is terminated with a struct containing 0 for both
  207. * response_code and s3_error_code; the result for that struct is the default
  208. * result.
  209. *
  210. * See RESULT_HANDLING_ALWAYS_RETRY for an example.
  211. */
  212. typedef enum {
  213. S3_RESULT_RETRY = -1,
  214. S3_RESULT_FAIL = 0,
  215. S3_RESULT_OK = 1
  216. } s3_result_t;
  217. typedef struct result_handling {
  218. guint response_code;
  219. s3_error_code_t s3_error_code;
  220. CURLcode curl_code;
  221. s3_result_t result;
  222. } result_handling_t;
  223. /* Lookup a result in C{result_handling}.
  224. *
  225. * @param result_handling: array of handling specifications
  226. * @param response_code: response code from operation
  227. * @param s3_error_code: s3 error code from operation, if any
  228. * @param curl_code: the CURL error, if any
  229. * @returns: the matching result
  230. */
  231. static s3_result_t
  232. lookup_result(const result_handling_t *result_handling,
  233. guint response_code,
  234. s3_error_code_t s3_error_code,
  235. CURLcode curl_code);
  236. /*
  237. * Precompiled regular expressions */
  238. static regex_t etag_regex, error_name_regex, message_regex, subdomain_regex,
  239. location_con_regex, date_sync_regex, x_auth_token_regex,
  240. x_storage_url_regex;
  241. /*
  242. * Utility functions
  243. */
  244. /* Check if a string is non-empty
  245. *
  246. * @param str: string to check
  247. * @returns: true iff str is non-NULL and not "\0"
  248. */
  249. static gboolean is_non_empty_string(const char *str);
  250. /* Construct the URL for an Amazon S3 REST request.
  251. *
  252. * A new string is allocated and returned; it is the responsiblity of the caller.
  253. *
  254. * @param hdl: the S3Handle object
  255. * @param service_path: A path to add in the URL, or NULL for none.
  256. * @param bucket: the bucket being accessed, or NULL for none
  257. * @param key: the key being accessed, or NULL for none
  258. * @param subresource: the sub-resource being accessed (e.g. "acl"), or NULL for none
  259. * @param query: the query being accessed (e.g. "acl"), or NULL for none
  260. *
  261. * !use_subdomain: http://host/service_path/bucket/key
  262. * use_subdomain : http://bucket.host/service_path/key
  263. *
  264. */
  265. static char *
  266. build_url(
  267. S3Handle *hdl,
  268. const char *bucket,
  269. const char *key,
  270. const char *subresource,
  271. const char *query);
  272. /* Create proper authorization headers for an Amazon S3 REST
  273. * request to C{headers}.
  274. *
  275. * @note: C{X-Amz} headers (in C{headers}) must
  276. * - be in lower-case
  277. * - be in alphabetical order
  278. * - have no spaces around the colon
  279. * (don't yell at me -- see the Amazon Developer Guide)
  280. *
  281. * @param hdl: the S3Handle object
  282. * @param verb: capitalized verb for this request ('PUT', 'GET', etc.)
  283. * @param bucket: the bucket being accessed, or NULL for none
  284. * @param key: the key being accessed, or NULL for none
  285. * @param subresource: the sub-resource being accessed (e.g. "acl"), or NULL for none
  286. * @param md5_hash: the MD5 hash of the request body, or NULL for none
  287. */
  288. static struct curl_slist *
  289. authenticate_request(S3Handle *hdl,
  290. const char *verb,
  291. const char *bucket,
  292. const char *key,
  293. const char *subresource,
  294. const char *md5_hash);
  295. /* Interpret the response to an S3 operation, assuming CURL completed its request
  296. * successfully. This function fills in the relevant C{hdl->last*} members.
  297. *
  298. * @param hdl: The S3Handle object
  299. * @param body: the response body
  300. * @param body_len: the length of the response body
  301. * @param etag: The response's ETag header
  302. * @param content_md5: The hex-encoded MD5 hash of the request body,
  303. * which will be checked against the response's ETag header.
  304. * If NULL, the header is not checked.
  305. * If non-NULL, then the body should have the response headers at its beginnning.
  306. * @returns: TRUE if the response should be retried (e.g., network error)
  307. */
  308. static gboolean
  309. interpret_response(S3Handle *hdl,
  310. CURLcode curl_code,
  311. char *curl_error_buffer,
  312. gchar *body,
  313. guint body_len,
  314. const char *etag,
  315. const char *content_md5);
  316. /* Perform an S3 operation. This function handles all of the details
  317. * of retryig requests and so on.
  318. *
  319. * The concepts of bucket and keys are defined by the Amazon S3 API.
  320. * See: "Components of Amazon S3" - API Version 2006-03-01 pg. 8
  321. *
  322. * Individual sub-resources are defined in several places. In the REST API,
  323. * they they are represented by a "flag" in the "query string".
  324. * See: "Constructing the CanonicalizedResource Element" - API Version 2006-03-01 pg. 60
  325. *
  326. * @param hdl: the S3Handle object
  327. * @param verb: the HTTP request method
  328. * @param bucket: the bucket to access, or NULL for none
  329. * @param key: the key to access, or NULL for none
  330. * @param subresource: the "sub-resource" to request (e.g. "acl") or NULL for none
  331. * @param query: the query string to send (not including th initial '?'),
  332. * or NULL for none
  333. * @param read_func: the callback for reading data
  334. * Will use s3_empty_read_func if NULL is passed in.
  335. * @param read_reset_func: the callback for to reset reading data
  336. * @param size_func: the callback to get the number of bytes to upload
  337. * @param md5_func: the callback to get the MD5 hash of the data to upload
  338. * @param read_data: pointer to pass to the above functions
  339. * @param write_func: the callback for writing data.
  340. * Will use s3_counter_write_func if NULL is passed in.
  341. * @param write_reset_func: the callback for to reset writing data
  342. * @param write_data: pointer to pass to C{write_func}
  343. * @param progress_func: the callback for progress information
  344. * @param progress_data: pointer to pass to C{progress_func}
  345. * @param result_handling: instructions for handling the results; see above.
  346. * @returns: the result specified by result_handling; details of the response
  347. * are then available in C{hdl->last*}
  348. */
  349. static s3_result_t
  350. perform_request(S3Handle *hdl,
  351. const char *verb,
  352. const char *bucket,
  353. const char *key,
  354. const char *subresource,
  355. const char *query,
  356. s3_read_func read_func,
  357. s3_reset_func read_reset_func,
  358. s3_size_func size_func,
  359. s3_md5_func md5_func,
  360. gpointer read_data,
  361. s3_write_func write_func,
  362. s3_reset_func write_reset_func,
  363. gpointer write_data,
  364. s3_progress_func progress_func,
  365. gpointer progress_data,
  366. const result_handling_t *result_handling);
  367. /*
  368. * a CURLOPT_WRITEFUNCTION to save part of the response in memory and
  369. * call an external function if one was provided.
  370. */
  371. static size_t
  372. s3_internal_write_func(void *ptr, size_t size, size_t nmemb, void * stream);
  373. /*
  374. * a function to reset to our internal buffer
  375. */
  376. static void
  377. s3_internal_reset_func(void * stream);
  378. /*
  379. * a CURLOPT_HEADERFUNCTION to save the ETag header only.
  380. */
  381. static size_t
  382. s3_internal_header_func(void *ptr, size_t size, size_t nmemb, void * stream);
  383. static gboolean
  384. compile_regexes(void);
  385. /*
  386. * Static function implementations
  387. */
  388. static s3_error_code_t
  389. s3_error_code_from_name(char *s3_error_name)
  390. {
  391. int i;
  392. if (!s3_error_name) return S3_ERROR_Unknown;
  393. /* do a brute-force search through the list, since it's not sorted */
  394. for (i = 0; i < S3_ERROR_END; i++) {
  395. if (g_ascii_strcasecmp(s3_error_name, s3_error_code_names[i]) == 0)
  396. return i;
  397. }
  398. return S3_ERROR_Unknown;
  399. }
  400. static const char *
  401. s3_error_name_from_code(s3_error_code_t s3_error_code)
  402. {
  403. if (s3_error_code >= S3_ERROR_END)
  404. s3_error_code = S3_ERROR_Unknown;
  405. return s3_error_code_names[s3_error_code];
  406. }
  407. gboolean
  408. s3_curl_supports_ssl(void)
  409. {
  410. static int supported = -1;
  411. if (supported == -1) {
  412. #if defined(CURL_VERSION_SSL)
  413. curl_version_info_data *info = curl_version_info(CURLVERSION_NOW);
  414. if (info->features & CURL_VERSION_SSL)
  415. supported = 1;
  416. else
  417. supported = 0;
  418. #else
  419. supported = 0;
  420. #endif
  421. }
  422. return supported;
  423. }
  424. static gboolean
  425. s3_curl_throttling_compat(void)
  426. {
  427. /* CURLOPT_MAX_SEND_SPEED_LARGE added in 7.15.5 */
  428. #if LIBCURL_VERSION_NUM >= 0x070f05
  429. curl_version_info_data *info;
  430. /* check the runtime version too */
  431. info = curl_version_info(CURLVERSION_NOW);
  432. return info->version_num >= 0x070f05;
  433. #else
  434. return FALSE;
  435. #endif
  436. }
  437. static s3_result_t
  438. lookup_result(const result_handling_t *result_handling,
  439. guint response_code,
  440. s3_error_code_t s3_error_code,
  441. CURLcode curl_code)
  442. {
  443. while (result_handling->response_code
  444. || result_handling->s3_error_code
  445. || result_handling->curl_code) {
  446. if ((result_handling->response_code && result_handling->response_code != response_code)
  447. || (result_handling->s3_error_code && result_handling->s3_error_code != s3_error_code)
  448. || (result_handling->curl_code && result_handling->curl_code != curl_code)) {
  449. result_handling++;
  450. continue;
  451. }
  452. return result_handling->result;
  453. }
  454. /* return the result for the terminator, as the default */
  455. return result_handling->result;
  456. }
  457. static gboolean
  458. is_non_empty_string(const char *str)
  459. {
  460. return str && str[0] != '\0';
  461. }
  462. static char *
  463. build_url(
  464. S3Handle *hdl,
  465. const char *bucket,
  466. const char *key,
  467. const char *subresource,
  468. const char *query)
  469. {
  470. GString *url = NULL;
  471. char *esc_bucket = NULL, *esc_key = NULL;
  472. if (hdl->openstack_swift_api && hdl->x_storage_url) {
  473. url = g_string_new(hdl->x_storage_url);
  474. g_string_append(url, "/");
  475. } else {
  476. /* scheme */
  477. url = g_string_new("http");
  478. if (hdl->use_ssl)
  479. g_string_append(url, "s");
  480. g_string_append(url, "://");
  481. /* domain */
  482. if (hdl->use_subdomain && bucket)
  483. g_string_append_printf(url, "%s.%s", bucket, hdl->host);
  484. else
  485. g_string_append_printf(url, "%s", hdl->host);
  486. if (hdl->service_path) {
  487. g_string_append_printf(url, "%s/", hdl->service_path);
  488. } else {
  489. g_string_append(url, "/");
  490. }
  491. }
  492. /* path */
  493. if (!hdl->use_subdomain && bucket) {
  494. /* curl_easy_escape addeded in 7.15.4 */
  495. #if LIBCURL_VERSION_NUM >= 0x070f04
  496. curl_version_info_data *info;
  497. /* check the runtime version too */
  498. info = curl_version_info(CURLVERSION_NOW);
  499. if (info->version_num >= 0x070f04)
  500. esc_bucket = curl_easy_escape(hdl->curl, bucket, 0);
  501. else
  502. esc_bucket = curl_escape(bucket, 0);
  503. #else
  504. esc_bucket = curl_escape(bucket, 0);
  505. #endif
  506. if (!esc_bucket) goto cleanup;
  507. g_string_append_printf(url, "%s", esc_bucket);
  508. if (key)
  509. g_string_append(url, "/");
  510. curl_free(esc_bucket);
  511. }
  512. if (key) {
  513. /* curl_easy_escape addeded in 7.15.4 */
  514. #if LIBCURL_VERSION_NUM >= 0x070f04
  515. curl_version_info_data *info;
  516. /* check the runtime version too */
  517. info = curl_version_info(CURLVERSION_NOW);
  518. if (info->version_num >= 0x070f04)
  519. esc_key = curl_easy_escape(hdl->curl, key, 0);
  520. else
  521. esc_key = curl_escape(key, 0);
  522. #else
  523. esc_key = curl_escape(key, 0);
  524. #endif
  525. if (!esc_key) goto cleanup;
  526. g_string_append_printf(url, "%s", esc_key);
  527. curl_free(esc_key);
  528. }
  529. /* query string */
  530. if (subresource || query)
  531. g_string_append(url, "?");
  532. if (subresource)
  533. g_string_append(url, subresource);
  534. if (subresource && query)
  535. g_string_append(url, "&");
  536. if (query)
  537. g_string_append(url, query);
  538. cleanup:
  539. return g_string_free(url, FALSE);
  540. }
  541. static struct curl_slist *
  542. authenticate_request(S3Handle *hdl,
  543. const char *verb,
  544. const char *bucket,
  545. const char *key,
  546. const char *subresource,
  547. const char *md5_hash)
  548. {
  549. time_t t;
  550. struct tm tmp;
  551. char *date = NULL;
  552. char *buf = NULL;
  553. HMAC_CTX ctx;
  554. GByteArray *md = NULL;
  555. char *auth_base64 = NULL;
  556. struct curl_slist *headers = NULL;
  557. char *esc_bucket = NULL, *esc_key = NULL;
  558. GString *auth_string = NULL;
  559. /* From RFC 2616 */
  560. static const char *wkday[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
  561. static const char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  562. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  563. /* calculate the date */
  564. t = time(NULL);
  565. /* sync clock with amazon s3 */
  566. t = t + hdl->time_offset_with_s3;
  567. #ifdef _WIN32
  568. if (!gmtime_s(&tmp, &t)) g_debug("localtime error");
  569. #else
  570. if (!gmtime_r(&t, &tmp)) perror("localtime");
  571. #endif
  572. date = g_strdup_printf("%s, %02d %s %04d %02d:%02d:%02d GMT",
  573. wkday[tmp.tm_wday], tmp.tm_mday, month[tmp.tm_mon], 1900+tmp.tm_year,
  574. tmp.tm_hour, tmp.tm_min, tmp.tm_sec);
  575. if (hdl->openstack_swift_api) {
  576. if (!bucket) {
  577. buf = g_strdup_printf("X-Auth-User: %s", hdl->swift_account_id);
  578. headers = curl_slist_append(headers, buf);
  579. g_free(buf);
  580. buf = g_strdup_printf("X-Auth-Key: %s", hdl->swift_access_key);
  581. headers = curl_slist_append(headers, buf);
  582. g_free(buf);
  583. } else {
  584. buf = g_strdup_printf("X-Auth-Token: %s", hdl->x_auth_token);
  585. headers = curl_slist_append(headers, buf);
  586. g_free(buf);
  587. }
  588. } else {
  589. /* Build the string to sign, per the S3 spec.
  590. * See: "Authenticating REST Requests" - API Version 2006-03-01 pg 58
  591. */
  592. /* verb */
  593. auth_string = g_string_new(verb);
  594. g_string_append(auth_string, "\n");
  595. /* Content-MD5 header */
  596. if (md5_hash)
  597. g_string_append(auth_string, md5_hash);
  598. g_string_append(auth_string, "\n");
  599. /* Content-Type is empty*/
  600. g_string_append(auth_string, "\n");
  601. /* Date */
  602. g_string_append(auth_string, date);
  603. g_string_append(auth_string, "\n");
  604. /* CanonicalizedAmzHeaders, sorted lexicographically */
  605. if (is_non_empty_string(hdl->user_token)) {
  606. g_string_append(auth_string, AMAZON_SECURITY_HEADER);
  607. g_string_append(auth_string, ":");
  608. g_string_append(auth_string, hdl->user_token);
  609. g_string_append(auth_string, ",");
  610. g_string_append(auth_string, STS_PRODUCT_TOKEN);
  611. g_string_append(auth_string, "\n");
  612. }
  613. if (g_str_equal(verb,"PUT") &&
  614. is_non_empty_string(hdl->server_side_encryption)) {
  615. g_string_append(auth_string, AMAZON_SERVER_SIDE_ENCRYPTION_HEADER);
  616. g_string_append(auth_string, ":");
  617. g_string_append(auth_string, hdl->server_side_encryption);
  618. g_string_append(auth_string, "\n");
  619. }
  620. if (is_non_empty_string(hdl->storage_class)) {
  621. g_string_append(auth_string, AMAZON_STORAGE_CLASS_HEADER);
  622. g_string_append(auth_string, ":");
  623. g_string_append(auth_string, hdl->storage_class);
  624. g_string_append(auth_string, "\n");
  625. }
  626. /* CanonicalizedResource */
  627. if (hdl->service_path) {
  628. g_string_append(auth_string, hdl->service_path);
  629. }
  630. g_string_append(auth_string, "/");
  631. if (bucket) {
  632. if (hdl->use_subdomain)
  633. g_string_append(auth_string, bucket);
  634. else {
  635. esc_bucket = curl_escape(bucket, 0);
  636. if (!esc_bucket) goto cleanup;
  637. g_string_append(auth_string, esc_bucket);
  638. }
  639. }
  640. if (bucket && (hdl->use_subdomain || key))
  641. g_string_append(auth_string, "/");
  642. if (key) {
  643. esc_key = curl_escape(key, 0);
  644. if (!esc_key) goto cleanup;
  645. g_string_append(auth_string, esc_key);
  646. }
  647. if (subresource) {
  648. g_string_append(auth_string, "?");
  649. g_string_append(auth_string, subresource);
  650. }
  651. /* run HMAC-SHA1 on the canonicalized string */
  652. md = g_byte_array_sized_new(EVP_MAX_MD_SIZE+1);
  653. HMAC_CTX_init(&ctx);
  654. HMAC_Init_ex(&ctx, hdl->secret_key, (int) strlen(hdl->secret_key),
  655. EVP_sha1(), NULL);
  656. HMAC_Update(&ctx, (unsigned char*) auth_string->str, auth_string->len);
  657. HMAC_Final(&ctx, md->data, &md->len);
  658. HMAC_CTX_cleanup(&ctx);
  659. auth_base64 = s3_base64_encode(md);
  660. /* append the new headers */
  661. if (is_non_empty_string(hdl->user_token)) {
  662. /* Devpay headers are included in hash. */
  663. buf = g_strdup_printf(AMAZON_SECURITY_HEADER ": %s",
  664. hdl->user_token);
  665. headers = curl_slist_append(headers, buf);
  666. g_free(buf);
  667. buf = g_strdup_printf(AMAZON_SECURITY_HEADER ": %s",
  668. STS_PRODUCT_TOKEN);
  669. headers = curl_slist_append(headers, buf);
  670. g_free(buf);
  671. }
  672. if (g_str_equal(verb,"PUT") &&
  673. is_non_empty_string(hdl->server_side_encryption)) {
  674. buf = g_strdup_printf(AMAZON_SERVER_SIDE_ENCRYPTION_HEADER ": %s",
  675. hdl->server_side_encryption);
  676. headers = curl_slist_append(headers, buf);
  677. g_free(buf);
  678. }
  679. if (is_non_empty_string(hdl->storage_class)) {
  680. buf = g_strdup_printf(AMAZON_STORAGE_CLASS_HEADER ": %s",
  681. hdl->storage_class);
  682. headers = curl_slist_append(headers, buf);
  683. g_free(buf);
  684. }
  685. buf = g_strdup_printf("Authorization: AWS %s:%s",
  686. hdl->access_key, auth_base64);
  687. headers = curl_slist_append(headers, buf);
  688. g_free(buf);
  689. }
  690. if (md5_hash && '\0' != md5_hash[0]) {
  691. buf = g_strdup_printf("Content-MD5: %s", md5_hash);
  692. headers = curl_slist_append(headers, buf);
  693. g_free(buf);
  694. }
  695. buf = g_strdup_printf("Date: %s", date);
  696. headers = curl_slist_append(headers, buf);
  697. g_free(buf);
  698. cleanup:
  699. g_free(date);
  700. g_free(esc_bucket);
  701. g_free(esc_key);
  702. if (md) g_byte_array_free(md, TRUE);
  703. g_free(auth_base64);
  704. if (auth_string) g_string_free(auth_string, TRUE);
  705. return headers;
  706. }
  707. /* Functions for a SAX parser to parse the XML failure from Amazon */
  708. /* Private structure for our "thunk", which tracks where the user is in the list
  709. * * of keys. */
  710. struct failure_thunk {
  711. gboolean want_text;
  712. gboolean in_title;
  713. gboolean in_body;
  714. gboolean in_code;
  715. gboolean in_message;
  716. gint in_others;
  717. gchar *text;
  718. gsize text_len;
  719. gchar *message;
  720. gchar *error_name;
  721. };
  722. static void
  723. failure_start_element(GMarkupParseContext *context G_GNUC_UNUSED,
  724. const gchar *element_name,
  725. const gchar **attribute_names G_GNUC_UNUSED,
  726. const gchar **attribute_values G_GNUC_UNUSED,
  727. gpointer user_data,
  728. GError **error G_GNUC_UNUSED)
  729. {
  730. struct failure_thunk *thunk = (struct failure_thunk *)user_data;
  731. if (g_ascii_strcasecmp(element_name, "title") == 0) {
  732. thunk->in_title = 1;
  733. thunk->in_others = 0;
  734. thunk->want_text = 1;
  735. } else if (g_ascii_strcasecmp(element_name, "body") == 0) {
  736. thunk->in_body = 1;
  737. thunk->in_others = 0;
  738. thunk->want_text = 1;
  739. } else if (g_ascii_strcasecmp(element_name, "code") == 0) {
  740. thunk->in_code = 1;
  741. thunk->in_others = 0;
  742. thunk->want_text = 1;
  743. } else if (g_ascii_strcasecmp(element_name, "message") == 0) {
  744. thunk->in_message = 1;
  745. thunk->in_others = 0;
  746. thunk->want_text = 1;
  747. } else {
  748. thunk->in_others++;
  749. }
  750. }
  751. static void
  752. failure_end_element(GMarkupParseContext *context G_GNUC_UNUSED,
  753. const gchar *element_name,
  754. gpointer user_data,
  755. GError **error G_GNUC_UNUSED)
  756. {
  757. struct failure_thunk *thunk = (struct failure_thunk *)user_data;
  758. if (g_ascii_strcasecmp(element_name, "title") == 0) {
  759. char *p = strchr(thunk->text, ' ');
  760. if (p) {
  761. p++;
  762. if (*p) {
  763. thunk->error_name = g_strdup(p);
  764. }
  765. }
  766. g_free(thunk->text);
  767. thunk->text = NULL;
  768. thunk->in_title = 0;
  769. } else if (g_ascii_strcasecmp(element_name, "body") == 0) {
  770. thunk->message = thunk->text;
  771. g_strstrip(thunk->message);
  772. thunk->text = NULL;
  773. thunk->in_body = 0;
  774. } else if (g_ascii_strcasecmp(element_name, "code") == 0) {
  775. thunk->error_name = thunk->text;
  776. thunk->text = NULL;
  777. thunk->in_code = 0;
  778. } else if (g_ascii_strcasecmp(element_name, "message") == 0) {
  779. thunk->message = thunk->text;
  780. thunk->text = NULL;
  781. thunk->in_message = 0;
  782. } else {
  783. thunk->in_others--;
  784. }
  785. }
  786. static void
  787. failure_text(GMarkupParseContext *context G_GNUC_UNUSED,
  788. const gchar *text,
  789. gsize text_len,
  790. gpointer user_data,
  791. GError **error G_GNUC_UNUSED)
  792. {
  793. struct failure_thunk *thunk = (struct failure_thunk *)user_data;
  794. if (thunk->want_text && thunk->in_others == 0) {
  795. char *new_text;
  796. new_text = g_strndup(text, text_len);
  797. if (thunk->text) {
  798. strappend(thunk->text, new_text);
  799. g_free(new_text);
  800. } else {
  801. thunk->text = new_text;
  802. }
  803. }
  804. }
  805. static gboolean
  806. interpret_response(S3Handle *hdl,
  807. CURLcode curl_code,
  808. char *curl_error_buffer,
  809. gchar *body,
  810. guint body_len,
  811. const char *etag,
  812. const char *content_md5)
  813. {
  814. long response_code = 0;
  815. gboolean ret = TRUE;
  816. struct failure_thunk thunk;
  817. GMarkupParseContext *ctxt = NULL;
  818. static GMarkupParser parser = { failure_start_element, failure_end_element, failure_text, NULL, NULL };
  819. GError *err = NULL;
  820. if (!hdl) return FALSE;
  821. if (hdl->last_message) g_free(hdl->last_message);
  822. hdl->last_message = NULL;
  823. /* bail out from a CURL error */
  824. if (curl_code != CURLE_OK) {
  825. hdl->last_curl_code = curl_code;
  826. hdl->last_message = g_strdup_printf("CURL error: %s", curl_error_buffer);
  827. return FALSE;
  828. }
  829. /* CURL seems to think things were OK, so get its response code */
  830. curl_easy_getinfo(hdl->curl, CURLINFO_RESPONSE_CODE, &response_code);
  831. hdl->last_response_code = response_code;
  832. /* check ETag, if present */
  833. if (etag && content_md5 && 200 == response_code) {
  834. if (etag && g_ascii_strcasecmp(etag, content_md5))
  835. hdl->last_message = g_strdup("S3 Error: Possible data corruption (ETag returned by Amazon did not match the MD5 hash of the data sent)");
  836. else
  837. ret = FALSE;
  838. return ret;
  839. }
  840. if (200 <= response_code && response_code < 400) {
  841. /* 2xx and 3xx codes won't have a response body we care about */
  842. hdl->last_s3_error_code = S3_ERROR_None;
  843. return FALSE;
  844. }
  845. /* Now look at the body to try to get the actual Amazon error message. */
  846. /* impose a reasonable limit on body size */
  847. if (body_len > MAX_ERROR_RESPONSE_LEN) {
  848. hdl->last_message = g_strdup("S3 Error: Unknown (response body too large to parse)");
  849. return FALSE;
  850. } else if (!body || body_len == 0) {
  851. hdl->last_message = g_strdup("S3 Error: Unknown (empty response body)");
  852. return TRUE; /* perhaps a network error; retry the request */
  853. }
  854. thunk.in_title = FALSE;
  855. thunk.in_body = FALSE;
  856. thunk.in_code = FALSE;
  857. thunk.in_message = FALSE;
  858. thunk.in_others = 0;
  859. thunk.text = NULL;
  860. thunk.want_text = FALSE;
  861. thunk.text_len = 0;
  862. thunk.message = NULL;
  863. thunk.error_name = NULL;
  864. if (hdl->openstack_swift_api &&
  865. !g_strstr_len(body, body_len, "xml version") &&
  866. !g_strstr_len(body, body_len, "<html>")) {
  867. char *body_copy = g_strndup(body, body_len);
  868. char *b = body_copy;
  869. char *p = strchr(b, '\n');
  870. char *p1;
  871. if (p) { /* first line: error code */
  872. *p = '\0';
  873. p++;
  874. p1 = strchr(b, ' ');
  875. if (p1) {
  876. p1++;
  877. if (*p1) {
  878. thunk.error_name = g_strdup(p1);
  879. }
  880. }
  881. b = p;
  882. }
  883. p = strchr(b, '\n');
  884. if (p) { /* second line: error message */
  885. *p = '\0';
  886. p++;
  887. thunk.message = g_strdup(p);
  888. g_strstrip(thunk.message);
  889. b = p;
  890. }
  891. goto parsing_done;
  892. }
  893. /* run the parser over it */
  894. ctxt = g_markup_parse_context_new(&parser, 0, (gpointer)&thunk, NULL);
  895. if (!g_markup_parse_context_parse(ctxt, body, body_len, &err)) {
  896. if (hdl->last_message) g_free(hdl->last_message);
  897. hdl->last_message = g_strdup(err->message);
  898. goto cleanup;
  899. }
  900. if (!g_markup_parse_context_end_parse(ctxt, &err)) {
  901. if (hdl->last_message) g_free(hdl->last_message);
  902. hdl->last_message = g_strdup(err->message);
  903. goto cleanup;
  904. }
  905. g_markup_parse_context_free(ctxt);
  906. ctxt = NULL;
  907. parsing_done:
  908. if (thunk.error_name) {
  909. hdl->last_s3_error_code = s3_error_code_from_name(thunk.error_name);
  910. g_free(thunk.error_name);
  911. thunk.error_name = NULL;
  912. }
  913. if (thunk.message) {
  914. g_free(hdl->last_message);
  915. hdl->last_message = thunk.message;
  916. thunk.message = NULL; /* steal the reference to the string */
  917. }
  918. cleanup:
  919. g_free(thunk.text);
  920. g_free(thunk.message);
  921. g_free(thunk.error_name);
  922. return FALSE;
  923. }
  924. /* a CURLOPT_READFUNCTION to read data from a buffer. */
  925. size_t
  926. s3_buffer_read_func(void *ptr, size_t size, size_t nmemb, void * stream)
  927. {
  928. CurlBuffer *data = stream;
  929. guint bytes_desired = (guint) size * nmemb;
  930. /* check the number of bytes remaining, just to be safe */
  931. if (bytes_desired > data->buffer_len - data->buffer_pos)
  932. bytes_desired = data->buffer_len - data->buffer_pos;
  933. memcpy((char *)ptr, data->buffer + data->buffer_pos, bytes_desired);
  934. data->buffer_pos += bytes_desired;
  935. return bytes_desired;
  936. }
  937. size_t
  938. s3_buffer_size_func(void *stream)
  939. {
  940. CurlBuffer *data = stream;
  941. return data->buffer_len;
  942. }
  943. GByteArray*
  944. s3_buffer_md5_func(void *stream)
  945. {
  946. CurlBuffer *data = stream;
  947. GByteArray req_body_gba = {(guint8 *)data->buffer, data->buffer_len};
  948. return s3_compute_md5_hash(&req_body_gba);
  949. }
  950. void
  951. s3_buffer_reset_func(void *stream)
  952. {
  953. CurlBuffer *data = stream;
  954. data->buffer_pos = 0;
  955. }
  956. /* a CURLOPT_WRITEFUNCTION to write data to a buffer. */
  957. size_t
  958. s3_buffer_write_func(void *ptr, size_t size, size_t nmemb, void *stream)
  959. {
  960. CurlBuffer * data = stream;
  961. guint new_bytes = (guint) size * nmemb;
  962. guint bytes_needed = data->buffer_pos + new_bytes;
  963. /* error out if the new size is greater than the maximum allowed */
  964. if (data->max_buffer_size && bytes_needed > data->max_buffer_size)
  965. return 0;
  966. /* reallocate if necessary. We use exponential sizing to make this
  967. * happen less often. */
  968. if (bytes_needed > data->buffer_len) {
  969. guint new_size = MAX(bytes_needed, data->buffer_len * 2);
  970. if (data->max_buffer_size) {
  971. new_size = MIN(new_size, data->max_buffer_size);
  972. }
  973. data->buffer = g_realloc(data->buffer, new_size);
  974. data->buffer_len = new_size;
  975. }
  976. if (!data->buffer)
  977. return 0; /* returning zero signals an error to libcurl */
  978. /* actually copy the data to the buffer */
  979. memcpy(data->buffer + data->buffer_pos, ptr, new_bytes);
  980. data->buffer_pos += new_bytes;
  981. /* signal success to curl */
  982. return new_bytes;
  983. }
  984. /* a CURLOPT_READFUNCTION that writes nothing. */
  985. size_t
  986. 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)
  987. {
  988. return 0;
  989. }
  990. size_t
  991. s3_empty_size_func(G_GNUC_UNUSED void *stream)
  992. {
  993. return 0;
  994. }
  995. GByteArray*
  996. s3_empty_md5_func(G_GNUC_UNUSED void *stream)
  997. {
  998. static const GByteArray empty = {(guint8 *) "", 0};
  999. return s3_compute_md5_hash(&empty);
  1000. }
  1001. /* a CURLOPT_WRITEFUNCTION to write data that just counts data.
  1002. * s3_write_data should be NULL or a pointer to an gint64.
  1003. */
  1004. size_t
  1005. s3_counter_write_func(G_GNUC_UNUSED void *ptr, size_t size, size_t nmemb, void *stream)
  1006. {
  1007. gint64 *count = (gint64*) stream, inc = nmemb*size;
  1008. if (count) *count += inc;
  1009. return inc;
  1010. }
  1011. void
  1012. s3_counter_reset_func(void *stream)
  1013. {
  1014. gint64 *count = (gint64*) stream;
  1015. if (count) *count = 0;
  1016. }
  1017. #ifdef _WIN32
  1018. /* a CURLOPT_READFUNCTION to read data from a file. */
  1019. size_t
  1020. s3_file_read_func(void *ptr, size_t size, size_t nmemb, void * stream)
  1021. {
  1022. HANDLE *hFile = (HANDLE *) stream;
  1023. DWORD bytes_read;
  1024. ReadFile(hFile, ptr, (DWORD) size*nmemb, &bytes_read, NULL);
  1025. return bytes_read;
  1026. }
  1027. size_t
  1028. s3_file_size_func(void *stream)
  1029. {
  1030. HANDLE *hFile = (HANDLE *) stream;
  1031. DWORD size = GetFileSize(hFile, NULL);
  1032. if (INVALID_FILE_SIZE == size) {
  1033. return -1;
  1034. } else {
  1035. return size;
  1036. }
  1037. }
  1038. GByteArray*
  1039. s3_file_md5_func(void *stream)
  1040. {
  1041. #define S3_MD5_BUF_SIZE (10*1024)
  1042. HANDLE *hFile = (HANDLE *) stream;
  1043. guint8 buf[S3_MD5_BUF_SIZE];
  1044. DWORD bytes_read;
  1045. MD5_CTX md5_ctx;
  1046. GByteArray *ret = NULL;
  1047. g_assert(INVALID_SET_FILE_POINTER != SetFilePointer(hFile, 0, NULL, FILE_BEGIN));
  1048. ret = g_byte_array_sized_new(S3_MD5_HASH_BYTE_LEN);
  1049. g_byte_array_set_size(ret, S3_MD5_HASH_BYTE_LEN);
  1050. MD5_Init(&md5_ctx);
  1051. while (ReadFile(hFile, buf, S3_MD5_BUF_SIZE, &bytes_read, NULL)) {
  1052. MD5_Update(&md5_ctx, buf, bytes_read);
  1053. }
  1054. MD5_Final(ret->data, &md5_ctx);
  1055. g_assert(INVALID_SET_FILE_POINTER != SetFilePointer(hFile, 0, NULL, FILE_BEGIN));
  1056. return ret;
  1057. #undef S3_MD5_BUF_SIZE
  1058. }
  1059. GByteArray*
  1060. s3_file_reset_func(void *stream)
  1061. {
  1062. g_assert(INVALID_SET_FILE_POINTER != SetFilePointer(hFile, 0, NULL, FILE_BEGIN));
  1063. }
  1064. /* a CURLOPT_WRITEFUNCTION to write data to a file. */
  1065. size_t
  1066. s3_file_write_func(void *ptr, size_t size, size_t nmemb, void *stream)
  1067. {
  1068. HANDLE *hFile = (HANDLE *) stream;
  1069. DWORD bytes_written;
  1070. WriteFile(hFile, ptr, (DWORD) size*nmemb, &bytes_written, NULL);
  1071. return bytes_written;
  1072. }
  1073. #endif
  1074. static int
  1075. curl_debug_message(CURL *curl G_GNUC_UNUSED,
  1076. curl_infotype type,
  1077. char *s,
  1078. size_t len,
  1079. void *unused G_GNUC_UNUSED)
  1080. {
  1081. char *lineprefix;
  1082. char *message;
  1083. char **lines, **line;
  1084. switch (type) {
  1085. case CURLINFO_TEXT:
  1086. lineprefix="";
  1087. break;
  1088. case CURLINFO_HEADER_IN:
  1089. lineprefix="Hdr In: ";
  1090. break;
  1091. case CURLINFO_HEADER_OUT:
  1092. lineprefix="Hdr Out: ";
  1093. break;
  1094. /*
  1095. case CURLINFO_DATA_IN:
  1096. if (len > 1000) return 0;
  1097. lineprefix="Data In: ";
  1098. break;
  1099. case CURLINFO_DATA_OUT:
  1100. if (len > 1000) return 0;
  1101. lineprefix="Data Out: ";
  1102. break;
  1103. */
  1104. default:
  1105. /* ignore data in/out -- nobody wants to see that in the
  1106. * debug logs! */
  1107. return 0;
  1108. }
  1109. /* split the input into lines */
  1110. message = g_strndup(s, (gsize) len);
  1111. lines = g_strsplit(message, "\n", -1);
  1112. g_free(message);
  1113. for (line = lines; *line; line++) {
  1114. if (**line == '\0') continue; /* skip blank lines */
  1115. g_debug("%s%s", lineprefix, *line);
  1116. }
  1117. g_strfreev(lines);
  1118. return 0;
  1119. }
  1120. static s3_result_t
  1121. perform_request(S3Handle *hdl,
  1122. const char *verb,
  1123. const char *bucket,
  1124. const char *key,
  1125. const char *subresource,
  1126. const char *query,
  1127. s3_read_func read_func,
  1128. s3_reset_func read_reset_func,
  1129. s3_size_func size_func,
  1130. s3_md5_func md5_func,
  1131. gpointer read_data,
  1132. s3_write_func write_func,
  1133. s3_reset_func write_reset_func,
  1134. gpointer write_data,
  1135. s3_progress_func progress_func,
  1136. gpointer progress_data,
  1137. const result_handling_t *result_handling)
  1138. {
  1139. char *url = NULL;
  1140. s3_result_t result = S3_RESULT_FAIL; /* assume the worst.. */
  1141. CURLcode curl_code = CURLE_OK;
  1142. char curl_error_buffer[CURL_ERROR_SIZE] = "";
  1143. struct curl_slist *headers = NULL;
  1144. /* Set S3Internal Data */
  1145. S3InternalData int_writedata = {{NULL, 0, 0, MAX_ERROR_RESPONSE_LEN}, NULL, NULL, NULL, FALSE, FALSE, NULL, hdl};
  1146. gboolean should_retry;
  1147. guint retries = 0;
  1148. gulong backoff = EXPONENTIAL_BACKOFF_START_USEC;
  1149. /* corresponds to PUT, HEAD, GET, and POST */
  1150. int curlopt_upload = 0, curlopt_nobody = 0, curlopt_httpget = 0, curlopt_post = 0;
  1151. /* do we want to examine the headers */
  1152. const char *curlopt_customrequest = NULL;
  1153. /* for MD5 calculation */
  1154. GByteArray *md5_hash = NULL;
  1155. gchar *md5_hash_hex = NULL, *md5_hash_b64 = NULL;
  1156. size_t request_body_size = 0;
  1157. g_assert(hdl != NULL && hdl->curl != NULL);
  1158. s3_reset(hdl);
  1159. url = build_url(hdl, bucket, key, subresource, query);
  1160. if (!url) goto cleanup;
  1161. /* libcurl may behave strangely if these are not set correctly */
  1162. if (!strncmp(verb, "PUT", 4)) {
  1163. curlopt_upload = 1;
  1164. } else if (!strncmp(verb, "GET", 4)) {
  1165. curlopt_httpget = 1;
  1166. } else if (!strncmp(verb, "POST", 5)) {
  1167. curlopt_post = 1;
  1168. } else if (!strncmp(verb, "HEAD", 5)) {
  1169. curlopt_nobody = 1;
  1170. } else {
  1171. curlopt_customrequest = verb;
  1172. }
  1173. if (size_func) {
  1174. request_body_size = size_func(read_data);
  1175. }
  1176. if (md5_func) {
  1177. md5_hash = md5_func(read_data);
  1178. if (md5_hash) {
  1179. md5_hash_b64 = s3_base64_encode(md5_hash);
  1180. md5_hash_hex = s3_hex_encode(md5_hash);
  1181. g_byte_array_free(md5_hash, TRUE);
  1182. }
  1183. }
  1184. if (!read_func) {
  1185. /* Curl will use fread() otherwise */
  1186. read_func = s3_empty_read_func;
  1187. }
  1188. if (write_func) {
  1189. int_writedata.write_func = write_func;
  1190. int_writedata.reset_func = write_reset_func;
  1191. int_writedata.write_data = write_data;
  1192. } else {
  1193. /* Curl will use fwrite() otherwise */
  1194. int_writedata.write_func = s3_counter_write_func;
  1195. int_writedata.reset_func = s3_counter_reset_func;
  1196. int_writedata.write_data = NULL;
  1197. }
  1198. while (1) {
  1199. /* reset things */
  1200. if (headers) {
  1201. curl_slist_free_all(headers);
  1202. }
  1203. curl_error_buffer[0] = '\0';
  1204. if (read_reset_func) {
  1205. read_reset_func(read_data);
  1206. }
  1207. /* calls write_reset_func */
  1208. s3_internal_reset_func(&int_writedata);
  1209. /* set up the request */
  1210. headers = authenticate_request(hdl, verb, bucket, key, subresource,
  1211. md5_hash_b64);
  1212. if (hdl->use_ssl && hdl->ca_info) {
  1213. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_CAINFO, hdl->ca_info)))
  1214. goto curl_error;
  1215. }
  1216. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_VERBOSE, hdl->verbose)))
  1217. goto curl_error;
  1218. if (hdl->verbose) {
  1219. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_DEBUGFUNCTION,
  1220. curl_debug_message)))
  1221. goto curl_error;
  1222. }
  1223. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_ERRORBUFFER,
  1224. curl_error_buffer)))
  1225. goto curl_error;
  1226. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_NOPROGRESS, 1)))
  1227. goto curl_error;
  1228. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_FOLLOWLOCATION, 1)))
  1229. goto curl_error;
  1230. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_URL, url)))
  1231. goto curl_error;
  1232. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_HTTPHEADER,
  1233. headers)))
  1234. goto curl_error;
  1235. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_WRITEFUNCTION, s3_internal_write_func)))
  1236. goto curl_error;
  1237. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_WRITEDATA, &int_writedata)))
  1238. goto curl_error;
  1239. /* Note: we always have to set this apparently, for consistent "end of header" detection */
  1240. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_HEADERFUNCTION, s3_internal_header_func)))
  1241. goto curl_error;
  1242. /* Note: if set, CURLOPT_HEADERDATA seems to also be used for CURLOPT_WRITEDATA ? */
  1243. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_HEADERDATA, &int_writedata)))
  1244. goto curl_error;
  1245. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_PROGRESSFUNCTION, progress_func)))
  1246. goto curl_error;
  1247. if (progress_func) {
  1248. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_NOPROGRESS,0)))
  1249. goto curl_error;
  1250. }
  1251. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_PROGRESSDATA, progress_data)))
  1252. goto curl_error;
  1253. /* CURLOPT_INFILESIZE_LARGE added in 7.11.0 */
  1254. #if LIBCURL_VERSION_NUM >= 0x070b00
  1255. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)request_body_size)))
  1256. goto curl_error;
  1257. #else
  1258. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_INFILESIZE, (long)request_body_size)))
  1259. goto curl_error;
  1260. #endif
  1261. /* CURLOPT_MAX_{RECV,SEND}_SPEED_LARGE added in 7.15.5 */
  1262. #if LIBCURL_VERSION_NUM >= 0x070f05
  1263. if (s3_curl_throttling_compat()) {
  1264. if (hdl->max_send_speed)
  1265. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_MAX_SEND_SPEED_LARGE, (curl_off_t)hdl->max_send_speed)))
  1266. goto curl_error;
  1267. if (hdl->max_recv_speed)
  1268. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_MAX_RECV_SPEED_LARGE, (curl_off_t)hdl->max_recv_speed)))
  1269. goto curl_error;
  1270. }
  1271. #endif
  1272. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_HTTPGET, curlopt_httpget)))
  1273. goto curl_error;
  1274. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_UPLOAD, curlopt_upload)))
  1275. goto curl_error;
  1276. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_POST, curlopt_post)))
  1277. goto curl_error;
  1278. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_NOBODY, curlopt_nobody)))
  1279. goto curl_error;
  1280. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_CUSTOMREQUEST,
  1281. curlopt_customrequest)))
  1282. goto curl_error;
  1283. if (curlopt_upload) {
  1284. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_READFUNCTION, read_func)))
  1285. goto curl_error;
  1286. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_READDATA, read_data)))
  1287. goto curl_error;
  1288. } else {
  1289. /* Clear request_body options. */
  1290. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_READFUNCTION,
  1291. NULL)))
  1292. goto curl_error;
  1293. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_READDATA,
  1294. NULL)))
  1295. goto curl_error;
  1296. }
  1297. if (hdl->proxy) {
  1298. if ((curl_code = curl_easy_setopt(hdl->curl, CURLOPT_PROXY,
  1299. hdl->proxy)))
  1300. goto curl_error;
  1301. }
  1302. /* Perform the request */
  1303. curl_code = curl_easy_perform(hdl->curl);
  1304. /* interpret the response into hdl->last* */
  1305. curl_error: /* (label for short-circuiting the curl_easy_perform call) */
  1306. should_retry = interpret_response(hdl, curl_code, curl_error_buffer,
  1307. int_writedata.resp_buf.buffer, int_writedata.resp_buf.buffer_pos, int_writedata.etag, md5_hash_hex);
  1308. /* and, unless we know we need to retry, see what we're to do now */
  1309. if (!should_retry) {
  1310. result = lookup_result(result_handling, hdl->last_response_code,
  1311. hdl->last_s3_error_code, hdl->last_curl_code);
  1312. /* break out of the while(1) unless we're retrying */
  1313. if (result != S3_RESULT_RETRY)
  1314. break;
  1315. }
  1316. if (retries >= EXPONENTIAL_BACKOFF_MAX_RETRIES) {
  1317. /* we're out of retries, so annotate hdl->last_message appropriately and bail
  1318. * out. */
  1319. char *m = g_strdup_printf("Too many retries; last message was '%s'", hdl->last_message);
  1320. if (hdl->last_message) g_free(hdl->last_message);
  1321. hdl->last_message = m;
  1322. result = S3_RESULT_FAIL;
  1323. break;
  1324. }
  1325. g_usleep(backoff);
  1326. retries++;
  1327. backoff *= EXPONENTIAL_BACKOFF_BASE;
  1328. }
  1329. if (result != S3_RESULT_OK) {
  1330. g_debug(_("%s %s failed with %d/%s"), verb, url,
  1331. hdl->last_response_code,
  1332. s3_error_name_from_code(hdl->last_s3_error_code));
  1333. }
  1334. cleanup:
  1335. g_free(url);
  1336. if (headers) curl_slist_free_all(headers);
  1337. g_free(md5_hash_b64);
  1338. g_free(md5_hash_hex);
  1339. /* we don't deallocate the response body -- we keep it for later */
  1340. hdl->last_response_body = int_writedata.resp_buf.buffer;
  1341. hdl->last_response_body_size = int_writedata.resp_buf.buffer_pos;
  1342. hdl->last_num_retries = retries;
  1343. return result;
  1344. }
  1345. static size_t
  1346. s3_internal_write_func(void *ptr, size_t size, size_t nmemb, void * stream)
  1347. {
  1348. S3InternalData *data = (S3InternalData *) stream;
  1349. size_t bytes_saved;
  1350. if (!data->headers_done)
  1351. return size*nmemb;
  1352. /* call write on internal buffer (if not full) */
  1353. if (data->int_write_done) {
  1354. bytes_saved = 0;
  1355. } else {
  1356. bytes_saved = s3_buffer_write_func(ptr, size, nmemb, &data->resp_buf);
  1357. if (!bytes_saved) {
  1358. data->int_write_done = TRUE;
  1359. }
  1360. }
  1361. /* call write on user buffer */
  1362. if (data->write_func) {
  1363. return data->write_func(ptr, size, nmemb, data->write_data);
  1364. } else {
  1365. return bytes_saved;
  1366. }
  1367. }
  1368. static void
  1369. s3_internal_reset_func(void * stream)
  1370. {
  1371. S3InternalData *data = (S3InternalData *) stream;
  1372. s3_buffer_reset_func(&data->resp_buf);
  1373. data->headers_done = FALSE;
  1374. data->int_write_done = FALSE;
  1375. data->etag = NULL;
  1376. if (data->reset_func) {
  1377. data->reset_func(data->write_data);
  1378. }
  1379. }
  1380. static size_t
  1381. s3_internal_header_func(void *ptr, size_t size, size_t nmemb, void * stream)
  1382. {
  1383. static const char *final_header = "\r\n";
  1384. time_t remote_time_in_sec,local_time;
  1385. char *header;
  1386. regmatch_t pmatch[2];
  1387. S3InternalData *data = (S3InternalData *) stream;
  1388. header = g_strndup((gchar *) ptr, (gsize) size*nmemb);
  1389. if (header[strlen(header)-1] == '\n')
  1390. header[strlen(header)-1] = '\0';
  1391. if (header[strlen(header)-1] == '\r')
  1392. header[strlen(header)-1] = '\0';
  1393. if (!s3_regexec_wrap(&etag_regex, header, 2, pmatch, 0))
  1394. data->etag = find_regex_substring(header, pmatch[1]);
  1395. if (!s3_regexec_wrap(&x_auth_token_regex, header, 2, pmatch, 0))
  1396. data->hdl->x_auth_token = find_regex_substring(header, pmatch[1]);
  1397. if (!s3_regexec_wrap(&x_storage_url_regex, header, 2, pmatch, 0))
  1398. data->hdl->x_storage_url = find_regex_substring(header, pmatch[1]);
  1399. if (strlen(header) == 0)
  1400. data->headers_done = TRUE;
  1401. if (g_str_equal(final_header, header))
  1402. data->headers_done = TRUE;
  1403. if (g_str_equal("\n", header))
  1404. data->headers_done = TRUE;
  1405. /* If date header is found */
  1406. if (!s3_regexec_wrap(&date_sync_regex, header, 2, pmatch, 0)){
  1407. char *date = find_regex_substring(header, pmatch[1]);
  1408. /* Remote time is always in GMT: RFC 2616 */
  1409. /* both curl_getdate and time operate in UTC, so no timezone math is necessa…

Large files files are truncated, but you can click here to view the full file