ruby-libstorj 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }