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