1 /*
2 * Copyright (C) 2020-2021 Bareos GmbH & Co. KG
3 * Copyright (C) 2010 SCALITY SA. All rights reserved.
4 * http://www.scality.com
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 *
13 * Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY SCALITY SA ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL SCALITY SA OR CONTRIBUTORS BE LIABLE FOR
21 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * The views and conclusions contained in the software and documentation
30 * are those of the authors and should not be interpreted as representing
31 * official policies, either expressed or implied, of SCALITY SA.
32 *
33 * https://github.com/scality/Droplet
34 */
35 #include "dropletp.h"
36 #include <droplet/s3/s3.h>
37
38 /**
39 * parse a HTTP header into a suitable metadata or sysmd
40 *
41 * @param header
42 * @param value
43 * @param metadatum_func optional
44 * @param cb_arg for metadatum_func
45 * @param metadata optional
46 * @param sysmdp optional
47 *
48 * @return
49 */
dpl_s3_get_metadatum_from_header(const char * header,const char * value,dpl_metadatum_func_t metadatum_func,void * cb_arg,dpl_dict_t * metadata,dpl_sysmd_t * sysmdp)50 dpl_status_t dpl_s3_get_metadatum_from_header(
51 const char* header,
52 const char* value,
53 dpl_metadatum_func_t metadatum_func,
54 void* cb_arg,
55 dpl_dict_t* metadata,
56 dpl_sysmd_t* sysmdp)
57 {
58 dpl_status_t ret, ret2;
59
60 if (!strncmp(header, DPL_X_AMZ_META_PREFIX, strlen(DPL_X_AMZ_META_PREFIX))) {
61 char* key;
62
63 key = (char*)header + strlen(DPL_X_AMZ_META_PREFIX);
64
65 if (metadatum_func) {
66 dpl_sbuf_t sbuf;
67 dpl_value_t val;
68
69 sbuf.allocated = 0;
70 sbuf.buf = (char*)value;
71 sbuf.len = strlen(value);
72 val.type = DPL_VALUE_STRING;
73 val.string = &sbuf;
74 ret2 = metadatum_func(cb_arg, key, &val);
75 if (DPL_SUCCESS != ret2) {
76 ret = ret2;
77 goto end;
78 }
79 }
80
81 if (metadata) {
82 ret2 = dpl_dict_add(metadata, key, value, 1);
83 if (DPL_SUCCESS != ret2) {
84 ret = ret2;
85 goto end;
86 }
87 }
88 } else {
89 if (sysmdp) {
90 if (!strcmp(header, "content-length")) {
91 sysmdp->mask |= DPL_SYSMD_MASK_SIZE;
92 sysmdp->size = atoi(value);
93 } else if (!strcmp(header, "last-modified")) {
94 sysmdp->mask |= DPL_SYSMD_MASK_MTIME;
95 sysmdp->mtime = dpl_get_date(value, NULL);
96 } else if (!strcmp(header, "etag")) {
97 int value_len = strlen(value);
98
99 if (value_len < DPL_SYSMD_ETAG_SIZE && value_len >= 2) {
100 sysmdp->mask |= DPL_SYSMD_MASK_ETAG;
101 // supress double quotes
102 strncpy(sysmdp->etag, value + 1, DPL_SYSMD_ETAG_SIZE);
103 sysmdp->etag[value_len - 2] = 0;
104 }
105 }
106 }
107 }
108
109 ret = DPL_SUCCESS;
110
111 end:
112
113 return ret;
114 }
115
116 struct metadata_conven {
117 dpl_dict_t* metadata;
118 dpl_sysmd_t* sysmdp;
119 };
120
cb_headers_iterate(dpl_dict_var_t * var,void * cb_arg)121 static dpl_status_t cb_headers_iterate(dpl_dict_var_t* var, void* cb_arg)
122 {
123 struct metadata_conven* mc = (struct metadata_conven*)cb_arg;
124
125 assert(var->val->type == DPL_VALUE_STRING);
126 return dpl_s3_get_metadatum_from_header(var->key,
127 dpl_sbuf_get_str(var->val->string),
128 NULL, NULL, mc->metadata, mc->sysmdp);
129 }
130
dpl_s3_get_metadata_from_headers(const dpl_dict_t * headers,dpl_dict_t ** metadatap,dpl_sysmd_t * sysmdp)131 dpl_status_t dpl_s3_get_metadata_from_headers(const dpl_dict_t* headers,
132 dpl_dict_t** metadatap,
133 dpl_sysmd_t* sysmdp)
134 {
135 dpl_dict_t* metadata = NULL;
136 dpl_status_t ret, ret2;
137 struct metadata_conven mc;
138
139 if (metadatap) {
140 metadata = dpl_dict_new(13);
141 if (NULL == metadata) {
142 ret = DPL_ENOMEM;
143 goto end;
144 }
145 }
146
147 memset(&mc, 0, sizeof(mc));
148 mc.metadata = metadata;
149 mc.sysmdp = sysmdp;
150
151 if (sysmdp) sysmdp->mask = 0;
152
153 ret2 = dpl_dict_iterate(headers, cb_headers_iterate, &mc);
154 if (DPL_SUCCESS != ret2) {
155 ret = ret2;
156 goto end;
157 }
158
159 if (NULL != metadatap) {
160 *metadatap = metadata;
161 metadata = NULL;
162 }
163
164 ret = DPL_SUCCESS;
165
166 end:
167
168 if (NULL != metadata) dpl_dict_free(metadata);
169
170 return ret;
171 }
172
parse_list_all_my_buckets_bucket(xmlNode * node,dpl_vec_t * vec)173 static dpl_status_t parse_list_all_my_buckets_bucket(xmlNode* node,
174 dpl_vec_t* vec)
175 {
176 xmlNode* tmp;
177 dpl_bucket_t* bucket = NULL;
178 int ret;
179
180 bucket = malloc(sizeof(*bucket));
181 if (NULL == bucket) goto bad;
182
183 memset(bucket, 0, sizeof(*bucket));
184
185 for (tmp = node; NULL != tmp; tmp = tmp->next) {
186 if (tmp->type == XML_ELEMENT_NODE) {
187 DPRINTF("name: %s\n", tmp->name);
188 if (!strcmp((char*)tmp->name, "Name")) {
189 bucket->name = strdup((char*)tmp->children->content);
190 if (NULL == bucket->name) goto bad;
191 }
192
193 if (!strcmp((char*)tmp->name, "CreationDate")) {
194 bucket->creation_time
195 = dpl_iso8601totime((char*)tmp->children->content);
196 }
197
198 } else if (tmp->type == XML_TEXT_NODE) {
199 DPRINTF("content: %s\n", tmp->content);
200 }
201 }
202
203 ret = dpl_vec_add(vec, bucket);
204 if (DPL_SUCCESS != ret) goto bad;
205
206 return DPL_SUCCESS;
207
208 bad:
209
210 if (NULL != bucket) dpl_bucket_free(bucket);
211
212 return DPL_FAILURE;
213 }
214
parse_list_all_my_buckets_buckets(xmlNode * node,dpl_vec_t * vec)215 static dpl_status_t parse_list_all_my_buckets_buckets(xmlNode* node,
216 dpl_vec_t* vec)
217 {
218 xmlNode* tmp;
219 int ret;
220
221 for (tmp = node; NULL != tmp; tmp = tmp->next) {
222 if (tmp->type == XML_ELEMENT_NODE) {
223 DPRINTF("name: %s\n", tmp->name);
224
225 if (!strcmp((char*)tmp->name, "Bucket")) {
226 ret = parse_list_all_my_buckets_bucket(tmp->children, vec);
227 if (DPL_SUCCESS != ret) return DPL_FAILURE;
228 }
229
230 } else if (tmp->type == XML_TEXT_NODE) {
231 DPRINTF("content: %s\n", tmp->content);
232 }
233 }
234
235 return DPL_SUCCESS;
236 }
237
parse_list_all_my_buckets_children(xmlNode * node,dpl_vec_t * vec)238 static dpl_status_t parse_list_all_my_buckets_children(xmlNode* node,
239 dpl_vec_t* vec)
240 {
241 xmlNode* tmp;
242 int ret;
243
244 for (tmp = node; NULL != tmp; tmp = tmp->next) {
245 if (tmp->type == XML_ELEMENT_NODE) {
246 DPRINTF("name: %s\n", tmp->name);
247
248 if (!strcmp((char*)tmp->name, "Buckets")) {
249 ret = parse_list_all_my_buckets_buckets(tmp->children, vec);
250 if (DPL_SUCCESS != ret) return DPL_FAILURE;
251 }
252 } else if (tmp->type == XML_TEXT_NODE) {
253 DPRINTF("content: %s\n", tmp->content);
254 }
255 }
256
257 return DPL_SUCCESS;
258 }
259
dpl_s3_parse_list_all_my_buckets(const dpl_ctx_t * ctx,const char * buf,int len,dpl_vec_t * vec)260 dpl_status_t dpl_s3_parse_list_all_my_buckets(const dpl_ctx_t* ctx,
261 const char* buf,
262 int len,
263 dpl_vec_t* vec)
264 {
265 xmlParserCtxtPtr ctxt = NULL;
266 xmlDocPtr doc = NULL;
267 int ret;
268 xmlNode* tmp;
269 // ssize_t cc;
270
271 // cc = write(1, buf, len);
272
273 if ((ctxt = xmlNewParserCtxt()) == NULL) {
274 ret = DPL_FAILURE;
275 goto end;
276 }
277
278 doc = xmlCtxtReadMemory(ctxt, buf, len, NULL, NULL, 0u);
279 if (NULL == doc) {
280 ret = DPL_FAILURE;
281 goto end;
282 }
283
284 for (tmp = xmlDocGetRootElement(doc); NULL != tmp; tmp = tmp->next) {
285 if (tmp->type == XML_ELEMENT_NODE) {
286 DPRINTF("name: %s\n", tmp->name);
287
288 if (!strcmp((char*)tmp->name, "ListAllMyBucketsResult")) {
289 ret = parse_list_all_my_buckets_children(tmp->children, vec);
290 if (DPL_SUCCESS != ret) return DPL_FAILURE;
291 }
292 } else if (tmp->type == XML_TEXT_NODE) {
293 DPRINTF("content: %s\n", tmp->content);
294 }
295 }
296
297 ret = DPL_SUCCESS;
298
299 end:
300
301 if (NULL != doc) xmlFreeDoc(doc);
302
303 if (NULL != ctxt) xmlFreeParserCtxt(ctxt);
304
305 return ret;
306 }
307
308 /**/
309
parse_list_bucket_content(xmlNode * node,dpl_vec_t * vec)310 static dpl_status_t parse_list_bucket_content(xmlNode* node, dpl_vec_t* vec)
311 {
312 xmlNode* tmp;
313 dpl_object_t* object = NULL;
314 int ret;
315
316 object = malloc(sizeof(*object));
317 if (NULL == object) goto bad;
318
319 memset(object, 0, sizeof(*object));
320
321 for (tmp = node; NULL != tmp; tmp = tmp->next) {
322 if (tmp->type == XML_ELEMENT_NODE) {
323 DPRINTF("name: %s\n", tmp->name);
324 if (!strcmp((char*)tmp->name, "Key")) {
325 object->path = strdup((char*)tmp->children->content);
326 if (NULL == object->path) goto bad;
327 } else if (!strcmp((char*)tmp->name, "LastModified")) {
328 object->last_modified
329 = dpl_iso8601totime((char*)tmp->children->content);
330 } else if (!strcmp((char*)tmp->name, "Size")) {
331 object->size = strtoull((char*)tmp->children->content, NULL, 0);
332 }
333 object->type = DPL_FTYPE_REG;
334
335 } else if (tmp->type == XML_TEXT_NODE) {
336 DPRINTF("content: %s\n", tmp->content);
337 }
338 }
339
340 ret = dpl_vec_add(vec, object);
341 if (DPL_SUCCESS != ret) goto bad;
342
343 return DPL_SUCCESS;
344
345 bad:
346
347 if (NULL != object) dpl_object_free(object);
348
349 return DPL_FAILURE;
350 }
351
parse_list_bucket_common_prefixes(xmlNode * node,dpl_vec_t * vec)352 static dpl_status_t parse_list_bucket_common_prefixes(xmlNode* node,
353 dpl_vec_t* vec)
354 {
355 xmlNode* tmp;
356 dpl_common_prefix_t* common_prefix = NULL;
357 int ret;
358
359 common_prefix = malloc(sizeof(*common_prefix));
360 if (NULL == common_prefix) goto bad;
361
362 memset(common_prefix, 0, sizeof(*common_prefix));
363
364 for (tmp = node; NULL != tmp; tmp = tmp->next) {
365 if (tmp->type == XML_ELEMENT_NODE) {
366 DPRINTF("name: %s\n", tmp->name);
367 if (!strcmp((char*)tmp->name, "Prefix")) {
368 common_prefix->prefix = strdup((char*)tmp->children->content);
369 if (NULL == common_prefix->prefix) goto bad;
370 }
371 } else if (tmp->type == XML_TEXT_NODE) {
372 DPRINTF("content: %s\n", tmp->content);
373 }
374 }
375
376 ret = dpl_vec_add(vec, common_prefix);
377 if (DPL_SUCCESS != ret) goto bad;
378
379 return DPL_SUCCESS;
380
381 bad:
382
383 if (NULL != common_prefix) dpl_common_prefix_free(common_prefix);
384
385 return DPL_FAILURE;
386 }
387
parse_list_bucket_children(xmlNode * node,dpl_vec_t * objects,dpl_vec_t * common_prefixes)388 static dpl_status_t parse_list_bucket_children(xmlNode* node,
389 dpl_vec_t* objects,
390 dpl_vec_t* common_prefixes)
391 {
392 xmlNode* tmp;
393 int ret;
394
395 for (tmp = node; NULL != tmp; tmp = tmp->next) {
396 if (tmp->type == XML_ELEMENT_NODE) {
397 DPRINTF("name: %s\n", tmp->name);
398
399 if (!strcmp((char*)tmp->name, "Contents")) {
400 ret = parse_list_bucket_content(tmp->children, objects);
401 if (DPL_SUCCESS != ret) return DPL_FAILURE;
402 } else if (!strcmp((char*)tmp->name, "CommonPrefixes")) {
403 ret = parse_list_bucket_common_prefixes(tmp->children, common_prefixes);
404 if (DPL_SUCCESS != ret) return DPL_FAILURE;
405 }
406 } else if (tmp->type == XML_TEXT_NODE) {
407 DPRINTF("content: %s\n", tmp->content);
408 }
409 }
410
411 return DPL_SUCCESS;
412 }
413
dpl_s3_parse_list_bucket(const dpl_ctx_t * ctx,const char * buf,int len,dpl_vec_t * objects,dpl_vec_t * common_prefixes)414 dpl_status_t dpl_s3_parse_list_bucket(const dpl_ctx_t* ctx,
415 const char* buf,
416 int len,
417 dpl_vec_t* objects,
418 dpl_vec_t* common_prefixes)
419 {
420 int ret = DPL_SUCCESS;
421 xmlParserCtxtPtr ctxt;
422 xmlDocPtr doc;
423 xmlNode* tmp;
424
425 ctxt = xmlNewParserCtxt();
426 if (ctxt == NULL) return DPL_FAILURE;
427
428 doc = xmlCtxtReadMemory(ctxt, buf, len, NULL, NULL, 0u);
429 if (doc == NULL) {
430 xmlFreeParserCtxt(ctxt);
431 return DPL_FAILURE;
432 }
433
434 tmp = xmlDocGetRootElement(doc);
435 while (tmp != NULL) {
436 if (tmp->type == XML_ELEMENT_NODE) {
437 DPRINTF("name: %s\n", tmp->name);
438
439 if (!strcmp((char*)tmp->name, "ListBucketResult")) {
440 ret = parse_list_bucket_children(tmp->children, objects,
441 common_prefixes);
442 if (ret != DPL_SUCCESS) break;
443 }
444 } else if (tmp->type == XML_TEXT_NODE)
445 DPRINTF("content: %s\n", tmp->content);
446
447 tmp = tmp->next;
448 }
449
450 xmlFreeDoc(doc);
451 xmlFreeParserCtxt(ctxt);
452
453 return ret;
454 }
455
parse_delete_all_deleted(const dpl_ctx_t * ctx,xmlNode * elem,dpl_vec_t * objects)456 static dpl_status_t parse_delete_all_deleted(const dpl_ctx_t* ctx,
457 xmlNode* elem,
458 dpl_vec_t* objects)
459 {
460 dpl_status_t ret = DPL_SUCCESS;
461 dpl_delete_object_t* object;
462
463 object = (dpl_delete_object_t*)malloc(sizeof(dpl_delete_object_t));
464 if (object == NULL) return DPL_ENOMEM;
465
466 object->status = DPL_SUCCESS;
467 object->name = NULL;
468 object->version_id = NULL;
469 object->error = NULL;
470
471 while (elem != NULL && ret == DPL_SUCCESS) {
472 if (elem->type == XML_ELEMENT_NODE) {
473 char** pstr;
474
475 if (!strcmp((char*)elem->name, "Key"))
476 pstr = &object->name;
477 else if (!strcmp((char*)elem->name, "VersionId"))
478 pstr = &object->version_id;
479 else
480 pstr = NULL;
481
482 if (pstr != NULL && elem->children != NULL) {
483 *pstr = strdup((char*)elem->children->content);
484 if (*pstr == NULL) ret = DPL_ENOMEM;
485 }
486 }
487 elem = elem->next;
488 }
489
490 if (ret == DPL_SUCCESS) ret = dpl_vec_add(objects, object);
491
492 if (ret != DPL_SUCCESS) dpl_delete_object_free(object);
493
494 return ret;
495 }
496
parse_delete_all_delete_error(const dpl_ctx_t * ctx,xmlNode * elem,dpl_vec_t * objects)497 static dpl_status_t parse_delete_all_delete_error(const dpl_ctx_t* ctx,
498 xmlNode* elem,
499 dpl_vec_t* objects)
500 {
501 dpl_status_t ret = DPL_SUCCESS;
502 dpl_delete_object_t* object;
503
504 object = (dpl_delete_object_t*)malloc(sizeof(dpl_delete_object_t));
505 if (object == NULL) return DPL_ENOMEM;
506
507 object->status = DPL_FAILURE;
508 object->name = NULL;
509 object->version_id = NULL;
510 object->error = NULL;
511
512 while (elem != NULL && ret == DPL_SUCCESS) {
513 if (elem->type == XML_ELEMENT_NODE) {
514 char** pstr;
515
516 if (!strcmp((char*)elem->name, "Key"))
517 pstr = &object->name;
518 else if (!strcmp((char*)elem->name, "VersionId"))
519 pstr = &object->version_id;
520 else if (!strcmp((char*)elem->name, "Message"))
521 pstr = &object->error;
522 else
523 pstr = NULL;
524
525 if (pstr != NULL && elem->children != NULL) {
526 *pstr = strdup((char*)elem->children->content);
527 if (*pstr == NULL) ret = DPL_ENOMEM;
528 }
529 }
530 elem = elem->next;
531 }
532
533 if (ret == DPL_SUCCESS) ret = dpl_vec_add(objects, object);
534
535 if (ret != DPL_SUCCESS) dpl_delete_object_free(object);
536
537 return ret;
538 }
539
parse_delete_all_children(const dpl_ctx_t * ctx,xmlNode * elem,dpl_vec_t * objects)540 static dpl_status_t parse_delete_all_children(const dpl_ctx_t* ctx,
541 xmlNode* elem,
542 dpl_vec_t* objects)
543 {
544 dpl_status_t ret = DPL_SUCCESS;
545
546 while (elem != NULL) {
547 if (elem->type == XML_ELEMENT_NODE) {
548 if (!strcmp((char*)elem->name, "Deleted"))
549 ret = parse_delete_all_deleted(ctx, elem->children, objects);
550 else if (!strcmp((char*)elem->name, "Error"))
551 ret = parse_delete_all_delete_error(ctx, elem->children, objects);
552
553 if (ret != DPL_SUCCESS) break;
554 }
555 elem = elem->next;
556 }
557
558 return ret;
559 }
560
parse_delete_all_error(const dpl_ctx_t * ctx,xmlNode * elem)561 static void parse_delete_all_error(const dpl_ctx_t* ctx, xmlNode* elem)
562 {
563 char *code = NULL, *message = NULL;
564
565 while (elem != NULL) {
566 if (elem->type == XML_ELEMENT_NODE) {
567 if (!strcmp((char*)elem->name, "Code")) {
568 if (elem->children != NULL) code = (char*)elem->children->content;
569 } else if (!strcmp((char*)elem->name, "Message")) {
570 if (elem->children != NULL) message = (char*)elem->children->content;
571 }
572 }
573 elem = elem->next;
574 }
575
576 if (message == NULL) return;
577
578 if (code != NULL)
579 DPL_LOG((dpl_ctx_t*)ctx, DPL_ERROR, "Error: %s (%s)", message, code);
580 else
581 DPL_LOG((dpl_ctx_t*)ctx, DPL_ERROR, "Error: %s", message);
582 }
583
dpl_s3_parse_delete_all(const dpl_ctx_t * ctx,const char * buf,int len,dpl_vec_t * objects)584 dpl_status_t dpl_s3_parse_delete_all(const dpl_ctx_t* ctx,
585 const char* buf,
586 int len,
587 dpl_vec_t* objects)
588 {
589 int ret = DPL_SUCCESS;
590 xmlParserCtxtPtr ctxt;
591 xmlDocPtr doc;
592 xmlNode* elem;
593
594 ctxt = xmlNewParserCtxt();
595 if (ctxt == NULL) return DPL_FAILURE;
596
597 doc = xmlCtxtReadMemory(ctxt, buf, len, NULL, NULL, 0u);
598 if (doc == NULL) {
599 xmlFreeParserCtxt(ctxt);
600 return DPL_FAILURE;
601 }
602
603 elem = xmlDocGetRootElement(doc);
604 while (elem != NULL) {
605 if (elem->type == XML_ELEMENT_NODE) {
606 if (!strcmp((char*)elem->name, "DeleteResult")) {
607 ret = parse_delete_all_children(ctx, elem->children, objects);
608 if (ret != DPL_SUCCESS) break;
609 } else if (!strcmp((char*)elem->name, "Error"))
610 parse_delete_all_error(ctx, elem->children);
611 }
612 elem = elem->next;
613 }
614
615 xmlFreeDoc(doc);
616 xmlFreeParserCtxt(ctxt);
617
618 return ret;
619 }
620
621 /**/
622