ruby-libstorj 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.gitmodules +3 -0
  4. data/.rspec +1 -0
  5. data/Gemfile +23 -0
  6. data/Gemfile.lock +111 -0
  7. data/Guardfile +21 -0
  8. data/LICENSE +502 -0
  9. data/README.md +262 -0
  10. data/Rakefile +76 -0
  11. data/ext/libstorj/.gitignore +47 -0
  12. data/ext/libstorj/.travis.yml +27 -0
  13. data/ext/libstorj/Doxyfile +2427 -0
  14. data/ext/libstorj/LICENSE +502 -0
  15. data/ext/libstorj/Makefile.am +6 -0
  16. data/ext/libstorj/README.md +198 -0
  17. data/ext/libstorj/autogen.sh +3 -0
  18. data/ext/libstorj/configure.ac +64 -0
  19. data/ext/libstorj/depends/Makefile +153 -0
  20. data/ext/libstorj/depends/config.guess +1462 -0
  21. data/ext/libstorj/depends/config.sub +1823 -0
  22. data/ext/libstorj/depends/extract-osx-sdk.sh +33 -0
  23. data/ext/libstorj/depends/packages/cctools.mk +7 -0
  24. data/ext/libstorj/depends/packages/clang.mk +7 -0
  25. data/ext/libstorj/depends/packages/gmp.mk +23 -0
  26. data/ext/libstorj/depends/packages/gnutls.mk +25 -0
  27. data/ext/libstorj/depends/packages/json-c.mk +7 -0
  28. data/ext/libstorj/depends/packages/libcurl.mk +39 -0
  29. data/ext/libstorj/depends/packages/libmicrohttpd.mk +7 -0
  30. data/ext/libstorj/depends/packages/libuv.mk +7 -0
  31. data/ext/libstorj/depends/packages/nettle.mk +30 -0
  32. data/ext/libstorj/libstorj.pc.in +11 -0
  33. data/ext/libstorj/src/Makefile.am +23 -0
  34. data/ext/libstorj/src/bip39.c +233 -0
  35. data/ext/libstorj/src/bip39.h +64 -0
  36. data/ext/libstorj/src/bip39_english.h +2074 -0
  37. data/ext/libstorj/src/cli.c +1494 -0
  38. data/ext/libstorj/src/crypto.c +525 -0
  39. data/ext/libstorj/src/crypto.h +178 -0
  40. data/ext/libstorj/src/downloader.c +1923 -0
  41. data/ext/libstorj/src/downloader.h +163 -0
  42. data/ext/libstorj/src/http.c +688 -0
  43. data/ext/libstorj/src/http.h +175 -0
  44. data/ext/libstorj/src/rs.c +962 -0
  45. data/ext/libstorj/src/rs.h +99 -0
  46. data/ext/libstorj/src/storj.c +1523 -0
  47. data/ext/libstorj/src/storj.h +1014 -0
  48. data/ext/libstorj/src/uploader.c +2736 -0
  49. data/ext/libstorj/src/uploader.h +181 -0
  50. data/ext/libstorj/src/utils.c +336 -0
  51. data/ext/libstorj/src/utils.h +65 -0
  52. data/ext/libstorj/test/Makefile.am +27 -0
  53. data/ext/libstorj/test/mockbridge.c +260 -0
  54. data/ext/libstorj/test/mockbridge.json +687 -0
  55. data/ext/libstorj/test/mockbridgeinfo.json +1836 -0
  56. data/ext/libstorj/test/mockfarmer.c +358 -0
  57. data/ext/libstorj/test/storjtests.h +41 -0
  58. data/ext/libstorj/test/tests.c +1617 -0
  59. data/ext/libstorj/test/tests_rs.c +869 -0
  60. data/ext/ruby-libstorj/extconf.rb +8 -0
  61. data/ext/ruby-libstorj/ruby-libstorj.cc +17 -0
  62. data/lib/ruby-libstorj.rb +1 -0
  63. data/lib/ruby-libstorj/arg_forwarding_task.rb +58 -0
  64. data/lib/ruby-libstorj/env.rb +178 -0
  65. data/lib/ruby-libstorj/ext/bucket.rb +71 -0
  66. data/lib/ruby-libstorj/ext/create_bucket_request.rb +53 -0
  67. data/lib/ruby-libstorj/ext/curl_code.rb +139 -0
  68. data/lib/ruby-libstorj/ext/ext.rb +71 -0
  69. data/lib/ruby-libstorj/ext/file.rb +84 -0
  70. data/lib/ruby-libstorj/ext/get_bucket_request.rb +45 -0
  71. data/lib/ruby-libstorj/ext/json_request.rb +51 -0
  72. data/lib/ruby-libstorj/ext/list_files_request.rb +63 -0
  73. data/lib/ruby-libstorj/ext/types.rb +226 -0
  74. data/lib/ruby-libstorj/ext/upload_options.rb +38 -0
  75. data/lib/ruby-libstorj/libstorj.rb +22 -0
  76. data/lib/ruby-libstorj/mixins/storj.rb +27 -0
  77. data/lib/ruby-libstorj/struct.rb +42 -0
  78. data/ruby-libstorj.gemspec +57 -0
  79. data/spec/helpers/options.yml.example +22 -0
  80. data/spec/helpers/shared_rake_examples.rb +132 -0
  81. data/spec/helpers/storj_options.rb +96 -0
  82. data/spec/helpers/upload.data +3 -0
  83. data/spec/helpers/upload.data.sha256 +1 -0
  84. data/spec/libstorj_spec.rb +0 -0
  85. data/spec/ruby-libstorj/arg_forwarding_task_spec.rb +311 -0
  86. data/spec/ruby-libstorj/env_spec.rb +353 -0
  87. data/spec/ruby-libstorj/ext_spec.rb +75 -0
  88. data/spec/ruby-libstorj/json_request_spec.rb +13 -0
  89. data/spec/ruby-libstorj/libstorj_spec.rb +81 -0
  90. data/spec/ruby-libstorj/struct_spec.rb +64 -0
  91. data/spec/spec_helper.rb +113 -0
  92. metadata +136 -0
@@ -0,0 +1,163 @@
1
+ /**
2
+ * @file downloader.h
3
+ * @brief Storj download methods and definitions.
4
+ *
5
+ * Structures and functions useful for downloading files.
6
+ */
7
+ #ifndef STORJ_DOWNLOADER_H
8
+ #define STORJ_DOWNLOADER_H
9
+
10
+ #include "storj.h"
11
+ #include "http.h"
12
+ #include "utils.h"
13
+ #include "crypto.h"
14
+ #include "rs.h"
15
+
16
+ #define STORJ_DOWNLOAD_CONCURRENCY 24
17
+ #define STORJ_DOWNLOAD_WRITESYNC_CONCURRENCY 4
18
+ #define STORJ_DEFAULT_MIRRORS 5
19
+ #define STORJ_MAX_REPORT_TRIES 2
20
+ #define STORJ_MAX_TOKEN_TRIES 6
21
+ #define STORJ_MAX_POINTER_TRIES 6
22
+ #define STORJ_MAX_INFO_TRIES 6
23
+
24
+ /** @brief Enumerable that defines that status of a pointer
25
+ *
26
+ * A pointer will begin as created, and move forward until an error
27
+ * occurs, in which case it will start moving backwards from the error
28
+ * state until it has been replaced and reset back to created. This process
29
+ * can continue until success.
30
+ */
31
+ typedef enum {
32
+ POINTER_BEING_REPLACED = -3,
33
+ POINTER_ERROR_REPORTED = -2,
34
+ POINTER_ERROR = -1,
35
+ POINTER_CREATED = 0,
36
+ POINTER_BEING_DOWNLOADED = 1,
37
+ POINTER_DOWNLOADED = 2,
38
+ POINTER_MISSING = 3,
39
+ POINTER_FINISHED = 4
40
+ } storj_pointer_status_t;
41
+
42
+ /** @brief A structure for sharing data with worker threads for writing
43
+ * a shard to a file decriptor.
44
+ */
45
+ typedef struct {
46
+ char *shard_data;
47
+ ssize_t shard_total_bytes;
48
+ int error_status;
49
+ FILE *destination;
50
+ uint32_t pointer_index;
51
+ /* state should not be modified in worker threads */
52
+ storj_download_state_t *state;
53
+ } shard_request_write_t;
54
+
55
+ /** @brief A structure for repairing shards from parity shards */
56
+ typedef struct {
57
+ int fd;
58
+ uint64_t filesize;
59
+ uint64_t data_filesize;
60
+ uint32_t data_shards;
61
+ uint32_t parity_shards;
62
+ uint64_t shard_size;
63
+ uint8_t *decrypt_key;
64
+ uint8_t *decrypt_ctr;
65
+ uint8_t *zilch;
66
+ bool has_missing;
67
+ /* state should not be modified in worker threads */
68
+ storj_download_state_t *state;
69
+ int error_status;
70
+ } file_request_recover_t;
71
+
72
+ /** @brief A structure for sharing data with worker threads for downloading
73
+ * shards from farmers.
74
+ */
75
+ typedef struct {
76
+ storj_http_options_t *http_options;
77
+ char *farmer_id;
78
+ char *farmer_proto;
79
+ char *farmer_host;
80
+ int farmer_port;
81
+ char *shard_hash;
82
+ uint32_t pointer_index;
83
+ char *token;
84
+ uint64_t start;
85
+ uint64_t end;
86
+ uint64_t shard_total_bytes;
87
+ uv_async_t progress_handle;
88
+ uint64_t byte_position;
89
+ /* state should not be modified in worker threads */
90
+ storj_download_state_t *state;
91
+ int error_status;
92
+ bool *canceled;
93
+ } shard_request_download_t;
94
+
95
+ /** @brief A structure for sharing data with worker threads for sending
96
+ * exchange reports to the bridge.
97
+ */
98
+ typedef struct {
99
+ uint32_t pointer_index;
100
+ storj_http_options_t *http_options;
101
+ storj_bridge_options_t *options;
102
+ int status_code;
103
+ storj_exchange_report_t *report;
104
+ /* state should not be modified in worker threads */
105
+ storj_download_state_t *state;
106
+ } shard_send_report_t;
107
+
108
+ typedef struct {
109
+ storj_http_options_t *http_options;
110
+ storj_bridge_options_t *options;
111
+ int status_code;
112
+ const char *bucket_id;
113
+ const char *file_id;
114
+ int error_status;
115
+ storj_file_meta_t *info;
116
+ /* state should not be modified in worker threads */
117
+ storj_download_state_t *state;
118
+ } file_info_request_t;
119
+
120
+ /** @brief A structure for sharing data with worker threads for replacing a
121
+ * pointer with a new farmer.
122
+ */
123
+ typedef struct {
124
+ storj_http_options_t *http_options;
125
+ storj_bridge_options_t *options;
126
+ uint32_t pointer_index;
127
+ const char *bucket_id;
128
+ const char *file_id;
129
+ char *excluded_farmer_ids;
130
+ /* state should not be modified in worker threads */
131
+ storj_download_state_t *state;
132
+ struct json_object *response;
133
+ int error_status;
134
+ int status_code;
135
+ } json_request_replace_pointer_t;
136
+
137
+ /** @brief A structure for sharing data with worker threads for making JSON
138
+ * requests with the bridge.
139
+ */
140
+ typedef struct {
141
+ storj_http_options_t *http_options;
142
+ storj_bridge_options_t *options;
143
+ char *method;
144
+ char *path;
145
+ bool auth;
146
+ struct json_object *body;
147
+ struct json_object *response;
148
+ /* state should not be modified in worker threads */
149
+ storj_download_state_t *state;
150
+ int status_code;
151
+ } json_request_download_t;
152
+
153
+ /** @brief A method that determines the next work necessary to download a file
154
+ *
155
+ * This method is called after each individual work is complete, and will
156
+ * determine and queue the next set of work that needs to be completed. Once
157
+ * the file is completely downloaded, it will call the finished callback.
158
+ *
159
+ * This method should only be called with in the main loop thread.
160
+ */
161
+ static void queue_next_work(storj_download_state_t *state);
162
+
163
+ #endif /* STORJ_DOWNLOADER_H */
@@ -0,0 +1,688 @@
1
+ #include "http.h"
2
+
3
+ static size_t body_ignore_receive(void *buffer, size_t size, size_t nmemb,
4
+ void *userp)
5
+ {
6
+ size_t buflen = size * nmemb;
7
+ return buflen;
8
+ }
9
+
10
+ static size_t body_shard_send(void *buffer, size_t size, size_t nmemb,
11
+ void *userp)
12
+ {
13
+ shard_body_send_t *body = userp;
14
+
15
+ if (*body->canceled) {
16
+ return CURL_READFUNC_ABORT;
17
+ }
18
+
19
+ size_t read_bytes = 0;
20
+ size_t buflen = size * nmemb / AES_BLOCK_SIZE * AES_BLOCK_SIZE;
21
+ uint8_t clr_txt[buflen];
22
+ memset_zero(clr_txt, buflen);
23
+
24
+ if (buflen > 0) {
25
+ if (body->remain < buflen) {
26
+ buflen = body->remain;
27
+ }
28
+
29
+ // Read shard data from file
30
+ read_bytes = pread(fileno(body->fd), clr_txt, buflen, body->offset + body->total_sent);
31
+ if (read_bytes == -1) {
32
+ body->error_code = errno;
33
+ return CURL_READFUNC_ABORT;
34
+ }
35
+
36
+ if (body->ctx != NULL) {
37
+ ctr_crypt(body->ctx->ctx, (nettle_cipher_func *)aes256_encrypt,
38
+ AES_BLOCK_SIZE, body->ctx->encryption_ctr, read_bytes,
39
+ (uint8_t *)buffer, (uint8_t *)clr_txt);
40
+ } else {
41
+ memcpy(buffer, clr_txt, read_bytes);
42
+ }
43
+
44
+ if (ferror(body->fd)) {
45
+ return CURL_READFUNC_ABORT;
46
+ }
47
+
48
+ body->total_sent += read_bytes;
49
+ body->bytes_since_progress += read_bytes;
50
+
51
+ body->remain -= read_bytes;
52
+
53
+ memset_zero(clr_txt, buflen);
54
+ }
55
+
56
+ // give progress updates at set interval
57
+ if (body->progress_handle && read_bytes > 0 &&
58
+ (body->bytes_since_progress > SHARD_PROGRESS_INTERVAL ||
59
+ body->remain == 0)) {
60
+
61
+ shard_upload_progress_t *progress = body->progress_handle->data;
62
+ progress->bytes = body->total_sent;
63
+ uv_async_send(body->progress_handle);
64
+
65
+ body->bytes_since_progress = 0;
66
+ }
67
+
68
+ return read_bytes;
69
+ }
70
+
71
+ int put_shard(storj_http_options_t *http_options,
72
+ char *farmer_id,
73
+ char *proto,
74
+ char *host,
75
+ int port,
76
+ char *shard_hash,
77
+ uint64_t shard_total_bytes,
78
+ FILE *original_file,
79
+ uint64_t file_position,
80
+ storj_encryption_ctx_t *ctx,
81
+ char *token,
82
+ int *status_code,
83
+ int *read_code,
84
+ uv_async_t *progress_handle,
85
+ bool *canceled)
86
+ {
87
+ int return_code = 0;
88
+
89
+ CURL *curl = curl_easy_init();
90
+ if (!curl) {
91
+ return 1;
92
+ }
93
+
94
+ char query_args[80];
95
+ snprintf(query_args, 80, "?token=%s", token);
96
+
97
+ int url_len = strlen(proto) + 3 + strlen(host) + 1 + 10 + 8
98
+ + strlen(shard_hash) + strlen(query_args);
99
+ char *url = calloc(url_len + 1, sizeof(char));
100
+ if (!url) {
101
+ return 1;
102
+ }
103
+
104
+ snprintf(url, url_len, "%s://%s:%i/shards/%s%s", proto, host, port,
105
+ shard_hash, query_args);
106
+
107
+ curl_easy_setopt(curl, CURLOPT_URL, url);
108
+
109
+ curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT,
110
+ http_options->low_speed_limit);
111
+
112
+ curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME,
113
+ http_options->low_speed_time);
114
+
115
+ if (http_options->user_agent) {
116
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, http_options->user_agent);
117
+ }
118
+
119
+ if (http_options->proxy_url) {
120
+ curl_easy_setopt(curl, CURLOPT_PROXY, http_options->proxy_url);
121
+ }
122
+
123
+ if (http_options->cainfo_path) {
124
+ curl_easy_setopt(curl, CURLOPT_CAINFO, http_options->cainfo_path);
125
+ }
126
+
127
+ curl_easy_setopt(curl, CURLOPT_POST, 1);
128
+
129
+ struct curl_slist *header_list = NULL;
130
+ header_list = curl_slist_append(header_list, "Content-Type: application/octet-stream");
131
+
132
+ char *header = calloc(17 + 40 + 1, sizeof(char));
133
+ if (!header) {
134
+ return 1;
135
+ }
136
+ strcat(header, "x-storj-node-id: ");
137
+ strncat(header, farmer_id, 40);
138
+ header_list = curl_slist_append(header_list, header);
139
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
140
+
141
+ shard_body_send_t *shard_body = NULL;
142
+
143
+
144
+ if (original_file && shard_total_bytes) {
145
+
146
+ shard_body = malloc(sizeof(shard_body_send_t));
147
+ if (!shard_body) {
148
+ return 1;
149
+ }
150
+
151
+ shard_body->fd = original_file;
152
+ shard_body->offset = file_position;
153
+ shard_body->ctx = ctx;
154
+ shard_body->length = shard_total_bytes;
155
+ shard_body->remain = shard_total_bytes;
156
+ shard_body->total_sent = 0;
157
+ shard_body->bytes_since_progress = 0;
158
+ shard_body->progress_handle = progress_handle;
159
+ shard_body->canceled = canceled;
160
+ shard_body->error_code = 0;
161
+
162
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, body_shard_send);
163
+ curl_easy_setopt(curl, CURLOPT_READDATA, (void *)shard_body);
164
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, (uint64_t)shard_total_bytes);
165
+ }
166
+
167
+ // Ignore any data sent back, we only need to know the status code
168
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, body_ignore_receive);
169
+
170
+ int req = curl_easy_perform(curl);
171
+
172
+ curl_slist_free_all(header_list);
173
+ free(header);
174
+
175
+ if (*canceled) {
176
+ return_code = 1;
177
+ goto clean_up;
178
+ }
179
+
180
+ if (req != CURLE_OK) {
181
+ return_code = req;
182
+ goto clean_up;
183
+ }
184
+
185
+ // set the status code
186
+ if (shard_body && shard_total_bytes) {
187
+ *read_code = shard_body->error_code;
188
+ }
189
+
190
+ long int _status_code;
191
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &_status_code);
192
+ *status_code = (int)_status_code;
193
+
194
+ // check that total bytes have been sent
195
+ if (shard_body->total_sent != shard_total_bytes) {
196
+ return_code = 1;
197
+ goto clean_up;
198
+ }
199
+
200
+ clean_up:
201
+
202
+ // clean up memory
203
+ if (shard_body) {
204
+ free(shard_body);
205
+ }
206
+ free(url);
207
+ curl_easy_cleanup(curl);
208
+
209
+ return return_code;
210
+ }
211
+
212
+ static size_t body_shard_receive(void *buffer, size_t size, size_t nmemb,
213
+ void *userp)
214
+ {
215
+ size_t buflen = size * nmemb;
216
+ shard_body_receive_t *body = (shard_body_receive_t *)userp;
217
+
218
+ if (*body->canceled) {
219
+ return CURL_READFUNC_ABORT;
220
+ }
221
+
222
+ if (body->length + body->tail_position + buflen > body->shard_total_bytes) {
223
+ return CURL_READFUNC_ABORT;
224
+ }
225
+
226
+ // Resize the buffer if necessary
227
+ if (body->tail_position + buflen > body->tail_length) {
228
+ body->tail_length = (body->tail_position + buflen) * 2;
229
+ body->tail = realloc(body->tail, body->tail_length);
230
+
231
+ if (!body->tail) {
232
+ return CURL_READFUNC_ABORT;
233
+ }
234
+ }
235
+
236
+ // Copy buffer to tail
237
+ memcpy(body->tail + body->tail_position, buffer, buflen);
238
+
239
+ size_t writelen = body->tail_position + buflen;
240
+ if (body->length + writelen != body->shard_total_bytes) {
241
+ writelen = (writelen / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
242
+ }
243
+
244
+ // Update the hash
245
+ sha256_update(body->sha256_ctx, writelen, (uint8_t *)body->tail);
246
+
247
+ // Write directly to the file at the correct position
248
+ if (writelen == pwrite(fileno(body->destination),
249
+ body->tail,
250
+ writelen,
251
+ body->file_position)) {
252
+
253
+ if (writelen == -1) {
254
+ body->error_code = errno;
255
+ return CURL_READFUNC_ABORT;
256
+ }
257
+
258
+ body->file_position += writelen;
259
+ } else {
260
+ // TODO handle error
261
+ return CURL_READFUNC_ABORT;
262
+ }
263
+
264
+ body->length += writelen;
265
+ body->bytes_since_progress += writelen;
266
+
267
+ // Move any remaining data to the beginning and mark position
268
+ size_t tailing_size = body->tail_position + buflen - writelen;
269
+ if (tailing_size > 0) {
270
+ uint8_t tmp[tailing_size];
271
+ memcpy(&tmp, body->tail + writelen, tailing_size);
272
+ memcpy(body->tail, &tmp, tailing_size);
273
+ body->tail_position = tailing_size;
274
+ } else {
275
+ body->tail_position = 0;
276
+ }
277
+
278
+ // Give progress updates at set interval
279
+ if (body->progress_handle &&
280
+ body->bytes_since_progress > SHARD_PROGRESS_INTERVAL) {
281
+ shard_download_progress_t *progress = body->progress_handle->data;
282
+ progress->bytes = body->length;
283
+ uv_async_send(body->progress_handle);
284
+ body->bytes_since_progress = 0;
285
+ }
286
+
287
+ return buflen;
288
+ }
289
+
290
+ /* shard_data must be allocated for shard_total_bytes */
291
+ int fetch_shard(storj_http_options_t *http_options,
292
+ char *farmer_id,
293
+ char *proto,
294
+ char *host,
295
+ int port,
296
+ char *shard_hash,
297
+ uint64_t shard_total_bytes,
298
+ char *token,
299
+ FILE *destination,
300
+ uint64_t file_position,
301
+ int *status_code,
302
+ int *write_code,
303
+ uv_async_t *progress_handle,
304
+ bool *canceled)
305
+ {
306
+ CURL *curl = curl_easy_init();
307
+ if (!curl) {
308
+ return 1;
309
+ }
310
+
311
+ if (http_options->user_agent) {
312
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, http_options->user_agent);
313
+ }
314
+
315
+ if (http_options->proxy_url) {
316
+ curl_easy_setopt(curl, CURLOPT_PROXY, http_options->proxy_url);
317
+ }
318
+
319
+ if (http_options->cainfo_path) {
320
+ curl_easy_setopt(curl, CURLOPT_CAINFO, http_options->cainfo_path);
321
+ }
322
+
323
+ char query_args[80];
324
+ snprintf(query_args, 80, "?token=%s", token);
325
+ int url_len = strlen(proto) + 3 + strlen(host) + 1 + 10
326
+ + 8 + strlen(shard_hash) + strlen(query_args);
327
+ char *url = calloc(url_len + 1, sizeof(char));
328
+ if (!url) {
329
+ return 1;
330
+ }
331
+ snprintf(url, url_len, "%s://%s:%i/shards/%s%s", proto, host, port,
332
+ shard_hash, query_args);
333
+
334
+ curl_easy_setopt(curl, CURLOPT_URL, url);
335
+ curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
336
+
337
+ curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT,
338
+ http_options->low_speed_limit);
339
+
340
+ curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME,
341
+ http_options->low_speed_time);
342
+
343
+ // Set the node id header
344
+ struct curl_slist *node_chunk = NULL;
345
+ char *header = calloc(17 + 40 + 1, sizeof(char));
346
+ if (!header) {
347
+ return 1;
348
+ }
349
+ strcat(header, "x-storj-node-id: ");
350
+ strncat(header, farmer_id, 40);
351
+ node_chunk = curl_slist_append(node_chunk, header);
352
+ free(header);
353
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, node_chunk);
354
+
355
+ // Set the body handler
356
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, body_shard_receive);
357
+ shard_body_receive_t *body = malloc(sizeof(shard_body_receive_t));
358
+ if (!body) {
359
+ return 1;
360
+ }
361
+
362
+ body->tail = malloc(BUFSIZ);
363
+ body->tail_length = BUFSIZ;
364
+ body->tail_position = 0;
365
+ body->length = 0;
366
+ body->progress_handle = progress_handle;
367
+ body->shard_total_bytes = shard_total_bytes;
368
+ body->bytes_since_progress = 0;
369
+ body->canceled = canceled;
370
+ body->sha256_ctx = malloc(sizeof(struct sha256_ctx));
371
+ body->error_code = 0;
372
+ if (!body->sha256_ctx) {
373
+ return 1;
374
+ }
375
+ sha256_init(body->sha256_ctx);
376
+
377
+ body->destination = destination;
378
+ body->file_position = file_position;
379
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)body);
380
+
381
+ int req = curl_easy_perform(curl);
382
+
383
+ curl_slist_free_all(node_chunk);
384
+ free(body->tail);
385
+
386
+ // set the status code
387
+ if (body) {
388
+ *write_code = body->error_code;
389
+ }
390
+
391
+
392
+ int error_code = 0;
393
+ if (req != CURLE_OK) {
394
+ // TODO include the actual http error code
395
+ error_code = STORJ_FARMER_REQUEST_ERROR;
396
+ }
397
+
398
+ // set the status code
399
+ long int _status_code;
400
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &_status_code);
401
+ *status_code = (int)_status_code;
402
+
403
+ curl_easy_cleanup(curl);
404
+
405
+ free(url);
406
+
407
+ if (error_code) {
408
+ free(body->sha256_ctx);
409
+ free(body);
410
+ return error_code;
411
+ }
412
+
413
+ if (body->length != shard_total_bytes) {
414
+ free(body->sha256_ctx);
415
+ free(body);
416
+ return STORJ_FARMER_INTEGRITY_ERROR;
417
+ }
418
+
419
+ uint8_t *hash_sha256 = calloc(SHA256_DIGEST_SIZE, sizeof(uint8_t));
420
+ if (!hash_sha256) {
421
+ return 1;
422
+ }
423
+ sha256_digest(body->sha256_ctx, SHA256_DIGEST_SIZE, hash_sha256);
424
+
425
+ struct ripemd160_ctx rctx;
426
+ ripemd160_init(&rctx);
427
+ ripemd160_update(&rctx, SHA256_DIGEST_SIZE, hash_sha256);
428
+
429
+ free(hash_sha256);
430
+
431
+ uint8_t *hash_rmd160 = calloc(RIPEMD160_DIGEST_SIZE + 1, sizeof(uint8_t));
432
+ if (!hash_rmd160) {
433
+ return 1;
434
+ }
435
+ ripemd160_digest(&rctx, RIPEMD160_DIGEST_SIZE, hash_rmd160);
436
+
437
+ char *hash = calloc(RIPEMD160_DIGEST_SIZE * 2 + 1, sizeof(char));
438
+ if (!hash) {
439
+ return 1;
440
+ }
441
+ for (unsigned i = 0; i < RIPEMD160_DIGEST_SIZE; i++) {
442
+ sprintf(&hash[i*2], "%02x", hash_rmd160[i]);
443
+ }
444
+
445
+ free(body->sha256_ctx);
446
+ free(body);
447
+ free(hash_rmd160);
448
+
449
+ if (strcmp(shard_hash, hash) != 0) {
450
+ error_code = STORJ_FARMER_INTEGRITY_ERROR;
451
+ }
452
+
453
+ free(hash);
454
+
455
+ if (error_code) {
456
+ return error_code;
457
+ }
458
+
459
+ // final progress update
460
+ if (progress_handle) {
461
+ shard_download_progress_t *progress = progress_handle->data;
462
+ progress->bytes = shard_total_bytes;
463
+ uv_async_send(progress_handle);
464
+ }
465
+
466
+ return 0;
467
+ }
468
+
469
+ static size_t body_json_send(void *buffer, size_t size, size_t nmemb,
470
+ void *userp)
471
+ {
472
+ http_body_send_t *body = (http_body_send_t *)userp;
473
+
474
+ size_t buflen = size * nmemb;
475
+
476
+ if (buflen > 0) {
477
+ if (body->remain < buflen) {
478
+ buflen = body->remain;
479
+ }
480
+ memcpy(buffer, body->pnt, buflen);
481
+
482
+ body->pnt += buflen;
483
+ body->remain -= buflen;
484
+ }
485
+
486
+ return buflen;
487
+ }
488
+
489
+ static size_t body_json_receive(void *buffer, size_t size, size_t nmemb,
490
+ void *userp)
491
+ {
492
+ size_t buflen = size * nmemb;
493
+ http_body_receive_t *body = (http_body_receive_t *)userp;
494
+
495
+ body->data = realloc(body->data, body->length + buflen + 1);
496
+ if (body->data == NULL) {
497
+ return 0;
498
+ }
499
+
500
+ memcpy(&(body->data[body->length]), buffer, buflen);
501
+
502
+ body->length += buflen;
503
+ body->data[body->length] = 0;
504
+
505
+ return buflen;
506
+ }
507
+
508
+ int fetch_json(storj_http_options_t *http_options,
509
+ storj_bridge_options_t *options,
510
+ char *method,
511
+ char *path,
512
+ struct json_object *request_body,
513
+ bool auth,
514
+ struct json_object **response,
515
+ int *status_code)
516
+ {
517
+ CURL *curl = curl_easy_init();
518
+ if (!curl) {
519
+ return 1;
520
+ }
521
+
522
+ // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
523
+
524
+ char *user_pass = NULL;
525
+
526
+ // Set the url
527
+ int url_len = strlen(options->proto) + 3 + strlen(options->host) +
528
+ 1 + 10 + strlen(path);
529
+ char *url = calloc(url_len + 1, sizeof(char));
530
+ if (!url) {
531
+ return 1;
532
+ }
533
+
534
+ snprintf(url, url_len, "%s://%s:%i%s", options->proto, options->host,
535
+ options->port, path);
536
+ curl_easy_setopt(curl, CURLOPT_URL, url);
537
+
538
+ // Set the user agent
539
+ if (http_options->user_agent) {
540
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, http_options->user_agent);
541
+ }
542
+
543
+ // Set the HTTP method
544
+ if (0 == strcmp(method, "PUT")) {
545
+ curl_easy_setopt(curl, CURLOPT_PUT, 1);
546
+ } else if (0 == strcmp(method, "POST")) {
547
+ curl_easy_setopt(curl, CURLOPT_POST, 1);
548
+ } else if (0 == strcmp(method, "GET")) {
549
+ curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
550
+ } else if (0 == strcmp(method, "DELETE")) {
551
+ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
552
+ } else {
553
+ return 1;
554
+ }
555
+
556
+ // Set the proxy
557
+ if (http_options->proxy_url) {
558
+ curl_easy_setopt(curl, CURLOPT_PROXY, http_options->proxy_url);
559
+ }
560
+
561
+ // Set the path to the Certificate Authority (CA) bundle
562
+ if (http_options->cainfo_path) {
563
+ curl_easy_setopt(curl, CURLOPT_CAINFO, http_options->cainfo_path);
564
+ }
565
+
566
+ // Set the timeout
567
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, http_options->timeout);
568
+
569
+ // Setup the body handler
570
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, body_json_receive);
571
+ http_body_receive_t *body = malloc(sizeof(http_body_receive_t));
572
+ if (!body) {
573
+ return 1;
574
+ }
575
+ body->data = NULL;
576
+ body->length = 0;
577
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)body);
578
+
579
+ // Include authentication headers if info is provided
580
+ if (auth && options->user && options->pass) {
581
+
582
+ // Hash password
583
+ uint8_t *pass_hash = calloc(SHA256_DIGEST_SIZE, sizeof(uint8_t));
584
+ if (!pass_hash) {
585
+ return 1;
586
+ }
587
+ char *pass = calloc(SHA256_DIGEST_SIZE * 2 + 1, sizeof(char));
588
+ if (!pass) {
589
+ return 1;
590
+ }
591
+ struct sha256_ctx ctx;
592
+ sha256_init(&ctx);
593
+ sha256_update(&ctx, strlen(options->pass), (uint8_t *)options->pass);
594
+ sha256_digest(&ctx, SHA256_DIGEST_SIZE, pass_hash);
595
+ for (unsigned i = 0; i < SHA256_DIGEST_SIZE; i++) {
596
+ sprintf(&pass[i*2], "%02x", pass_hash[i]);
597
+ }
598
+
599
+ free(pass_hash);
600
+
601
+ int user_pass_len = strlen(options->user) + 1 + strlen(pass);
602
+ user_pass = calloc(user_pass_len + 1, sizeof(char));
603
+ if (!user_pass) {
604
+ return 1;
605
+ }
606
+ strcat(user_pass, options->user);
607
+ strcat(user_pass, ":");
608
+ strcat(user_pass, pass);
609
+
610
+ free(pass);
611
+
612
+ curl_easy_setopt(curl, CURLOPT_USERPWD, user_pass);
613
+
614
+ }
615
+
616
+ struct curl_slist *header_list = NULL;
617
+
618
+ // Include body if request body json is provided
619
+ http_body_send_t *post_body = NULL;
620
+ const char *req_buf = NULL;
621
+ if (request_body) {
622
+ req_buf = json_object_to_json_string(request_body);
623
+
624
+ header_list = curl_slist_append(header_list,
625
+ "Content-Type: application/json");
626
+
627
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
628
+
629
+ post_body = malloc(sizeof(http_body_send_t));
630
+ if (!post_body) {
631
+ return 1;
632
+ }
633
+ post_body->pnt = (char *)req_buf;
634
+ post_body->remain = strlen(req_buf);
635
+
636
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, body_json_send);
637
+ curl_easy_setopt(curl, CURLOPT_READDATA, (void *)post_body);
638
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, (uint64_t)strlen(req_buf));
639
+ } else {
640
+ header_list = curl_slist_append(header_list, "Content-Length: 0");
641
+ header_list = curl_slist_append(header_list, "Content-Type: application/json");
642
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
643
+ }
644
+
645
+ int ret = 0;
646
+ int req = curl_easy_perform(curl);
647
+
648
+ free(url);
649
+
650
+ if (header_list) {
651
+ curl_slist_free_all(header_list);
652
+ }
653
+
654
+ if (post_body) {
655
+ free(post_body);
656
+ }
657
+
658
+ if (user_pass) {
659
+ free(user_pass);
660
+ }
661
+
662
+ *response = NULL;
663
+
664
+ if (req != CURLE_OK) {
665
+ ret = req;
666
+ goto cleanup;
667
+ }
668
+
669
+ // set the status code
670
+ long int _status_code;
671
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &_status_code);
672
+ *status_code = (int)_status_code;
673
+
674
+ if (body->data && body->length > 0) {
675
+ *response = json_tokener_parse((char *)body->data);
676
+ }
677
+
678
+ cleanup:
679
+ curl_easy_cleanup(curl);
680
+ if (body->data) {
681
+ free(body->data);
682
+ }
683
+ if (body) {
684
+ free(body);
685
+ }
686
+
687
+ return ret;
688
+ }