VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/misc/s3.cpp@ 78052

Last change on this file since 78052 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.6 KB
Line 
1/* $Id: s3.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * IPRT - S3 communication API.
4 */
5
6/*
7 * Copyright (C) 2009-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/s3.h>
32#include "internal/iprt.h"
33
34#include <iprt/err.h>
35#include <iprt/mem.h>
36#include <iprt/string.h>
37#include <iprt/base64.h>
38#include <iprt/file.h>
39#include <iprt/stream.h>
40
41#ifdef RT_OS_WINDOWS /* OpenSSL drags in Windows.h, which isn't compatible with -Wall. */
42# include <iprt/win/windows.h>
43#endif
44#include <curl/curl.h>
45#include <openssl/hmac.h>
46#include <libxml/parser.h>
47
48#include "internal/magics.h"
49
50
51/*********************************************************************************************************************************
52* Structures and Typedefs *
53*********************************************************************************************************************************/
54typedef struct RTS3INTERNAL
55{
56 uint32_t u32Magic;
57 CURL *pCurl;
58 char *pszAccessKey;
59 char *pszSecretKey;
60 char *pszBaseUrl;
61 char *pszUserAgent;
62
63 PFNRTS3PROGRESS pfnProgressCallback;
64 void *pvUser;
65
66 long lLastResp;
67} RTS3INTERNAL;
68typedef RTS3INTERNAL* PRTS3INTERNAL;
69
70typedef struct RTS3TMPMEMCHUNK
71{
72 char *pszMem;
73 size_t cSize;
74} RTS3TMPMEMCHUNK;
75typedef RTS3TMPMEMCHUNK *PRTS3TMPMEMCHUNK;
76
77
78/*********************************************************************************************************************************
79* Defined Constants And Macros *
80*********************************************************************************************************************************/
81
82/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
83#define RTS3_VALID_RETURN_RC(hS3, rc) \
84 do { \
85 AssertPtrReturn((hS3), (rc)); \
86 AssertReturn((hS3)->u32Magic == RTS3_MAGIC, (rc)); \
87 } while (0)
88
89/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
90#define RTS3_VALID_RETURN(hS3) RTS3_VALID_RETURN_RC((hS3), VERR_INVALID_HANDLE)
91
92/** Validates a handle and returns (void) if not valid. */
93#define RTS3_VALID_RETURN_VOID(hS3) \
94 do { \
95 AssertPtrReturnVoid(hS3); \
96 AssertReturnVoid((hS3)->u32Magic == RTS3_MAGIC); \
97 } while (0)
98
99
100/*********************************************************************************************************************************
101* Private RTS3 helper *
102*********************************************************************************************************************************/
103
104static char* rtS3Host(const char* pszBucket, const char* pszKey, const char* pszBaseUrl)
105{
106 char* pszUrl;
107 /* Host header entry */
108 if (pszBucket[0] == 0)
109 RTStrAPrintf(&pszUrl, "%s", pszBaseUrl);
110 else if (pszKey[0] == 0)
111 RTStrAPrintf(&pszUrl, "%s.%s", pszBucket, pszBaseUrl);
112 else
113 RTStrAPrintf(&pszUrl, "%s.%s/%s", pszBucket, pszBaseUrl, pszKey);
114 return pszUrl;
115}
116
117static char* rtS3HostHeader(const char* pszBucket, const char* pszBaseUrl)
118{
119 char* pszUrl;
120 /* Host header entry */
121 if (pszBucket[0] != 0)
122 RTStrAPrintf(&pszUrl, "Host: %s.%s", pszBucket, pszBaseUrl);
123 else
124 RTStrAPrintf(&pszUrl, "Host: %s", pszBaseUrl);
125 return pszUrl;
126}
127
128static char* rtS3DateHeader()
129{
130 /* Date header entry */
131 RTTIMESPEC TimeSpec;
132 RTTIME Time;
133 RTTimeExplode(&Time, RTTimeNow(&TimeSpec));
134
135 static const char s_apszDayNms[7][4] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
136 static const char s_apszMonthNms[1+12][4] =
137 { "???", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
138 char *pszDate;
139 RTStrAPrintf(&pszDate, "Date: %s, %02u %s %04d %02u:%02u:%02u UTC",
140 s_apszDayNms[Time.u8WeekDay],
141 Time.u8MonthDay,
142 s_apszMonthNms[Time.u8Month],
143 Time.i32Year,
144 Time.u8Hour,
145 Time.u8Minute,
146 Time.u8Second);
147
148 return pszDate;
149}
150
151static char* rtS3ParseHeaders(char** ppHeaders, size_t cHeadEnts)
152{
153 char pszEmpty[] = "";
154 char *pszRes = NULL;
155 char *pszDate = pszEmpty;
156 char *pszType = pszEmpty;
157 for(size_t i=0; i < cHeadEnts; ++i)
158 {
159 if(ppHeaders[i] != NULL)
160 {
161 if (RTStrStr(ppHeaders[i], "Date: ") == ppHeaders[i])
162 {
163 pszDate = &(ppHeaders[i][6]);
164 }
165 else if(RTStrStr(ppHeaders[i], "Content-Type: ") == ppHeaders[i])
166 {
167 pszType = &(ppHeaders[i][14]);
168// char *pszTmp = RTStrDup (&(ppHeaders[i][14]));
169// if (pszRes)
170// {
171// char *pszTmp1 = pszRes;
172// RTStrAPrintf(&pszRes, "%s\n%s", pszRes, pszTmp);
173// RTStrFree(pszTmp);
174// RTStrFree(pszTmp1);
175// }
176// else
177// pszRes = pszTmp;
178 }
179 }
180 }
181 RTStrAPrintf(&pszRes, "\n%s\n%s", pszType, pszDate);
182 return pszRes;
183}
184
185static char* rtS3Canonicalize(const char* pszAction, const char* pszBucket, const char* pszKey, char** papszHeadEnts, size_t cHeadEnts)
186{
187 char* pszRes;
188 /* Grep the necessary info out of the headers & put them in a string */
189 char* pszHead = rtS3ParseHeaders(papszHeadEnts, cHeadEnts);
190 /* Create the string which will be used as signature */
191 RTStrAPrintf(&pszRes, "%s\n%s\n/",
192 pszAction,
193 pszHead);
194 RTStrFree(pszHead);
195 /* Add the bucket if the bucket isn't empty */
196 if (pszBucket[0] != 0)
197 {
198 char* pszTmp = pszRes;
199 RTStrAPrintf(&pszRes, "%s%s/", pszRes, pszBucket);
200 RTStrFree(pszTmp);
201 }
202 /* Add the key if the key isn't empty. */
203 if (pszKey[0] != 0)
204 {
205 char* pszTmp = pszRes;
206 RTStrAPrintf(&pszRes, "%s%s", pszRes, pszKey);
207 RTStrFree(pszTmp);
208 }
209
210 return pszRes;
211}
212
213static char* rtS3CreateSignature(PRTS3INTERNAL pS3Int, const char* pszAction, const char* pszBucket, const char* pszKey,
214 char** papszHeadEnts, size_t cHeadEnts)
215{
216 /* Create a string we can sign */
217 char* pszSig = rtS3Canonicalize(pszAction, pszBucket, pszKey, papszHeadEnts, cHeadEnts);
218// printf ("Sig %s\n", pszSig);
219 /* Sign the string by creating a SHA1 finger print */
220 char pszSigEnc[1024];
221 unsigned int cSigEnc = sizeof(pszSigEnc);
222 HMAC(EVP_sha1(), pS3Int->pszSecretKey, (int)strlen(pS3Int->pszSecretKey),
223 (const unsigned char*)pszSig, strlen(pszSig),
224 (unsigned char*)pszSigEnc, &cSigEnc);
225 RTStrFree(pszSig);
226 /* Convert the signature to Base64 */
227 size_t cSigBase64Enc = RTBase64EncodedLength(cSigEnc) + 1; /* +1 for the 0 */
228 char *pszSigBase64Enc = (char*)RTMemAlloc(cSigBase64Enc);
229 size_t cRes;
230 RTBase64Encode(pszSigEnc, cSigEnc, pszSigBase64Enc, cSigBase64Enc, &cRes);
231
232 return pszSigBase64Enc;
233}
234
235static char* rtS3CreateAuthHeader(PRTS3INTERNAL pS3Int, const char* pszAction, const char* pszBucket, const char* pszKey,
236 char** papszHeadEnts, size_t cHeadEnts)
237{
238 char *pszAuth;
239 /* Create a signature out of the header & the bucket/key info */
240 char *pszSigBase64Enc = rtS3CreateSignature(pS3Int, pszAction, pszBucket, pszKey, papszHeadEnts, cHeadEnts);
241 /* Create the authorization header entry */
242 RTStrAPrintf(&pszAuth, "Authorization: AWS %s:%s",
243 pS3Int->pszAccessKey,
244 pszSigBase64Enc);
245 RTStrFree(pszSigBase64Enc);
246 return pszAuth;
247}
248
249static int rtS3Perform(PRTS3INTERNAL pS3Int)
250{
251 int rc = VERR_INTERNAL_ERROR;
252 CURLcode code = curl_easy_perform(pS3Int->pCurl);
253 if (code == CURLE_OK)
254 {
255 curl_easy_getinfo(pS3Int->pCurl, CURLINFO_RESPONSE_CODE, &pS3Int->lLastResp);
256 switch (pS3Int->lLastResp)
257 {
258 case 200:
259 case 204: rc = VINF_SUCCESS; break; /* No content */
260 case 403: rc = VERR_S3_ACCESS_DENIED; break; /* Access denied */
261 case 404: rc = VERR_S3_NOT_FOUND; break; /* Site not found */
262 }
263 }
264 else
265 {
266 switch(code)
267 {
268 case CURLE_URL_MALFORMAT:
269 case CURLE_COULDNT_RESOLVE_HOST:
270#if defined(CURLE_REMOTE_FILE_NOT_FOUND)
271 case CURLE_REMOTE_FILE_NOT_FOUND: rc = VERR_S3_NOT_FOUND; break;
272#elif defined(CURLE_FILE_COULDNT_READ_FILE)
273 case CURLE_FILE_COULDNT_READ_FILE: rc = VERR_S3_NOT_FOUND; break;
274#endif
275#if defined(CURLE_REMOTE_ACCESS_DENIED)
276 case CURLE_REMOTE_ACCESS_DENIED: rc = VERR_S3_ACCESS_DENIED; break;
277#elif defined(CURLE_FTP_ACCESS_DENIED)
278 case CURLE_FTP_ACCESS_DENIED: rc = VERR_S3_ACCESS_DENIED; break;
279#endif
280 case CURLE_ABORTED_BY_CALLBACK: rc = VERR_S3_CANCELED; break;
281 default: break;
282 }
283 }
284 return rc;
285}
286
287static size_t rtS3WriteNothingCallback(void *pvBuf, size_t cbItem, size_t cItems, void *pvUser)
288{
289 NOREF(pvBuf); NOREF(pvUser);
290 return cbItem * cItems;
291}
292
293static size_t rtS3WriteMemoryCallback(void *pvBuf, size_t cSize, size_t cBSize, void *pvUser)
294{
295 PRTS3TMPMEMCHUNK pTmpMem = (PRTS3TMPMEMCHUNK)pvUser;
296 size_t cRSize = cSize * cBSize;
297
298 pTmpMem->pszMem = (char*)RTMemRealloc(pTmpMem->pszMem, pTmpMem->cSize + cRSize + 1);
299 if (pTmpMem->pszMem)
300 {
301 memcpy(&(pTmpMem->pszMem[pTmpMem->cSize]), pvBuf, cRSize);
302 pTmpMem->cSize += cRSize;
303 pTmpMem->pszMem[pTmpMem->cSize] = 0;
304 }
305 return cRSize;
306}
307
308static size_t rtS3WriteFileCallback(void *pvBuf, size_t cSize, size_t cBSize, void *pvUser)
309{
310 size_t cWritten;
311 RTFileWrite(*(RTFILE*)pvUser, pvBuf, cSize * cBSize, &cWritten);
312 return cWritten;
313}
314
315static size_t rtS3ReadFileCallback(void *pvBuf, size_t cSize, size_t cBSize, void *pvUser)
316{
317 size_t cRead;
318 RTFileRead(*(RTFILE*)pvUser, pvBuf, cSize * cBSize, &cRead);
319
320 return cRead;
321}
322
323static int rtS3ProgressCallback(void *pvUser, double dDlTotal, double dDlNow, double dUlTotal, double dUlNow)
324{
325 if (pvUser)
326 {
327 PRTS3INTERNAL pS3Int = (PRTS3INTERNAL)pvUser;
328 if (pS3Int->pfnProgressCallback)
329 {
330 int rc = VINF_SUCCESS;
331 if (dDlTotal > 0)
332 rc = pS3Int->pfnProgressCallback((unsigned)(100.0/dDlTotal*dDlNow), pS3Int->pvUser);
333 else if (dUlTotal > 0)
334 rc = pS3Int->pfnProgressCallback((unsigned)(100.0/dUlTotal*dUlNow), pS3Int->pvUser);
335 if (rc != VINF_SUCCESS)
336 return -1;
337 }
338 }
339 return CURLE_OK;
340}
341
342static void rtS3ReinitCurl(PRTS3INTERNAL pS3Int)
343{
344 if (pS3Int &&
345 pS3Int->pCurl)
346 {
347 /* Reset the CURL object to an defined state */
348 curl_easy_reset(pS3Int->pCurl);
349 /* Make sure HTTP 1.1 is used */
350 curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
351 /* We are cool we are a user agent now */
352 if (pS3Int->pszUserAgent)
353 curl_easy_setopt(pS3Int->pCurl, CURLOPT_USERAGENT, pS3Int->pszUserAgent);
354 /* Check if the user has a progress callback requested */
355 if (pS3Int->pfnProgressCallback)
356 {
357 /* Yes, we are willing to receive progress info */
358 curl_easy_setopt(pS3Int->pCurl, CURLOPT_NOPROGRESS, 0);
359 /* Callback for the progress info */
360 curl_easy_setopt(pS3Int->pCurl, CURLOPT_PROGRESSFUNCTION, rtS3ProgressCallback);
361 curl_easy_setopt(pS3Int->pCurl, CURLOPT_PROGRESSDATA, pS3Int);
362 }
363 /* Disable the internal cURL write function by providing one which does
364 * nothing */
365 curl_easy_setopt(pS3Int->pCurl, CURLOPT_WRITEFUNCTION, rtS3WriteNothingCallback);
366 /* Set this do get some verbose info what CURL is doing */
367// curl_easy_setopt(pS3Int->pCurl, CURLOPT_VERBOSE, 1);
368 }
369}
370
371
372/*********************************************************************************************************************************
373* Private XML helper *
374*********************************************************************************************************************************/
375
376static xmlNodePtr rtS3FindNode(xmlNodePtr pNode, const char *pszName)
377{
378 pNode = pNode->xmlChildrenNode;
379 while (pNode != NULL)
380 {
381 /* Check this level. */
382 if (!xmlStrcmp(pNode->name, (const xmlChar *)pszName))
383 return pNode;
384
385 /* Recursively check the children of this node. */
386 xmlNodePtr pChildNode = rtS3FindNode(pNode, pszName);
387 if (pChildNode != NULL)
388 return pChildNode;
389
390 /* Next node. */
391 pNode = pNode->next;
392 }
393 return pNode;
394}
395
396static int rtS3ReadXmlFromMemory(PRTS3TMPMEMCHUNK pChunk, const char* pszRootElement, xmlDocPtr *ppDoc, xmlNodePtr *ppCur)
397{
398 *ppDoc = xmlReadMemory(pChunk->pszMem, (int)pChunk->cSize, "", "ISO-8859-1", XML_PARSE_NOBLANKS | XML_PARSE_NONET);
399 if (*ppDoc == NULL)
400 return VERR_PARSE_ERROR;
401
402 *ppCur = xmlDocGetRootElement(*ppDoc);
403 if (*ppCur == NULL)
404 {
405 xmlFreeDoc(*ppDoc);
406 return VERR_PARSE_ERROR;
407 }
408 if (xmlStrcmp((*ppCur)->name, (const xmlChar *) pszRootElement))
409 {
410 xmlFreeDoc(*ppDoc);
411 return VERR_PARSE_ERROR;
412 }
413 return VINF_SUCCESS;
414}
415
416static void rtS3ExtractAllBuckets(xmlDocPtr pDoc, xmlNodePtr pNode, PCRTS3BUCKETENTRY *ppBuckets)
417{
418 pNode = rtS3FindNode(pNode, "Buckets");
419 if (pNode != NULL)
420 {
421 PRTS3BUCKETENTRY pPrevBucket = NULL;
422 xmlNodePtr pCurBucket = pNode->xmlChildrenNode;
423 while (pCurBucket != NULL)
424 {
425 if ((!xmlStrcmp(pCurBucket->name, (const xmlChar *)"Bucket")))
426 {
427 PRTS3BUCKETENTRY pBucket = (PRTS3BUCKETENTRY)RTMemAllocZ(sizeof(RTS3BUCKETENTRY));
428 pBucket->pPrev = pPrevBucket;
429 if (pPrevBucket)
430 pPrevBucket->pNext = pBucket;
431 else
432 (*ppBuckets) = pBucket;
433 pPrevBucket = pBucket;
434 xmlNodePtr pCurCont = pCurBucket->xmlChildrenNode;
435 while (pCurCont != NULL)
436 {
437 if ((!xmlStrcmp(pCurCont->name, (const xmlChar *)"Name")))
438 {
439 xmlChar *pszKey = xmlNodeListGetString(pDoc, pCurCont->xmlChildrenNode, 1);
440 pBucket->pszName = RTStrDup((const char*)pszKey);
441 xmlFree(pszKey);
442 }
443 if ((!xmlStrcmp(pCurCont->name, (const xmlChar*)"CreationDate")))
444 {
445 xmlChar *pszKey = xmlNodeListGetString(pDoc, pCurCont->xmlChildrenNode, 1);
446 pBucket->pszCreationDate = RTStrDup((const char*)pszKey);
447 xmlFree(pszKey);
448 }
449 pCurCont = pCurCont->next;
450 }
451 }
452 pCurBucket = pCurBucket->next;
453 }
454 }
455}
456
457static void rtS3ExtractAllKeys(xmlDocPtr pDoc, xmlNodePtr pNode, PCRTS3KEYENTRY *ppKeys)
458{
459 if (pNode != NULL)
460 {
461 PRTS3KEYENTRY pPrevKey = NULL;
462 xmlNodePtr pCurKey = pNode->xmlChildrenNode;
463 while (pCurKey != NULL)
464 {
465 if ((!xmlStrcmp(pCurKey->name, (const xmlChar *)"Contents")))
466 {
467 PRTS3KEYENTRY pKey = (PRTS3KEYENTRY)RTMemAllocZ(sizeof(RTS3KEYENTRY));
468 pKey->pPrev = pPrevKey;
469 if (pPrevKey)
470 pPrevKey->pNext = pKey;
471 else
472 (*ppKeys) = pKey;
473 pPrevKey = pKey;
474 xmlNodePtr pCurCont = pCurKey->xmlChildrenNode;
475 while (pCurCont != NULL)
476 {
477 if ((!xmlStrcmp(pCurCont->name, (const xmlChar *)"Key")))
478 {
479 xmlChar *pszKey = xmlNodeListGetString(pDoc, pCurCont->xmlChildrenNode, 1);
480 pKey->pszName = RTStrDup((const char*)pszKey);
481 xmlFree(pszKey);
482 }
483 if ((!xmlStrcmp(pCurCont->name, (const xmlChar*)"LastModified")))
484 {
485 xmlChar *pszKey = xmlNodeListGetString(pDoc, pCurCont->xmlChildrenNode, 1);
486 pKey->pszLastModified = RTStrDup((const char*)pszKey);
487 xmlFree(pszKey);
488 }
489 if ((!xmlStrcmp(pCurCont->name, (const xmlChar*)"Size")))
490 {
491 xmlChar *pszKey = xmlNodeListGetString(pDoc, pCurCont->xmlChildrenNode, 1);
492 pKey->cbFile = RTStrToUInt64((const char*)pszKey);
493 xmlFree(pszKey);
494 }
495 pCurCont = pCurCont->next;
496 }
497 }
498 pCurKey = pCurKey->next;
499 }
500 }
501}
502
503
504/*********************************************************************************************************************************
505* Public RTS3 interface *
506*********************************************************************************************************************************/
507
508RTR3DECL(int) RTS3Create(PRTS3 ppS3, const char* pszAccessKey, const char* pszSecretKey, const char* pszBaseUrl, const char* pszUserAgent /* = NULL */)
509{
510 AssertPtrReturn(ppS3, VERR_INVALID_POINTER);
511
512 /* We need at least an URL to connect with */
513 if (pszBaseUrl == NULL ||
514 pszBaseUrl[0] == 0)
515 return VERR_INVALID_PARAMETER;
516
517 /* In windows, this will init the winsock stuff */
518 if (curl_global_init(CURL_GLOBAL_ALL) != 0)
519 return VERR_INTERNAL_ERROR;
520
521 CURL* pCurl = curl_easy_init();
522 if (!pCurl)
523 return VERR_INTERNAL_ERROR;
524
525 PRTS3INTERNAL pS3Int = (PRTS3INTERNAL)RTMemAllocZ(sizeof(RTS3INTERNAL));
526 if (pS3Int == NULL)
527 return VERR_NO_MEMORY;
528
529 pS3Int->u32Magic = RTS3_MAGIC;
530 pS3Int->pCurl = pCurl;
531 pS3Int->pszAccessKey = RTStrDup(pszAccessKey);
532 pS3Int->pszSecretKey = RTStrDup(pszSecretKey);
533 pS3Int->pszBaseUrl = RTStrDup(pszBaseUrl);
534 if (pszUserAgent)
535 pS3Int->pszUserAgent = RTStrDup(pszUserAgent);
536
537 *ppS3 = (RTS3)pS3Int;
538
539 return VINF_SUCCESS;
540}
541
542RTR3DECL(void) RTS3Destroy(RTS3 hS3)
543{
544 if (hS3 == NIL_RTS3)
545 return;
546
547 PRTS3INTERNAL pS3Int = hS3;
548 RTS3_VALID_RETURN_VOID(pS3Int);
549
550 curl_easy_cleanup(pS3Int->pCurl);
551
552 pS3Int->u32Magic = RTS3_MAGIC_DEAD;
553
554 if (pS3Int->pszUserAgent)
555 RTStrFree(pS3Int->pszUserAgent);
556 RTStrFree(pS3Int->pszBaseUrl);
557 RTStrFree(pS3Int->pszSecretKey);
558 RTStrFree(pS3Int->pszAccessKey);
559
560 RTMemFree(pS3Int);
561
562 curl_global_cleanup();
563}
564
565RTR3DECL(void) RTS3SetProgressCallback(RTS3 hS3, PFNRTS3PROGRESS pfnProgressCallback, void *pvUser /* = NULL */)
566{
567 PRTS3INTERNAL pS3Int = hS3;
568 RTS3_VALID_RETURN_VOID(pS3Int);
569
570 pS3Int->pfnProgressCallback = pfnProgressCallback;
571 pS3Int->pvUser = pvUser;
572}
573
574RTR3DECL(int) RTS3GetBuckets(RTS3 hS3, PCRTS3BUCKETENTRY *ppBuckets)
575{
576 PRTS3INTERNAL pS3Int = hS3;
577 RTS3_VALID_RETURN(pS3Int);
578
579 /* Properly initialize this */
580 *ppBuckets = NULL;
581
582 /* Reset the CURL object to an defined state */
583 rtS3ReinitCurl(pS3Int);
584 /* Create the CURL object to operate on */
585 curl_easy_setopt(pS3Int->pCurl, CURLOPT_URL, pS3Int->pszBaseUrl);
586
587 /* Create the three basic header entries */
588 char *apszHead[3] =
589 {
590 rtS3HostHeader("", pS3Int->pszBaseUrl), /* Host entry */
591 rtS3DateHeader(), /* Date entry */
592 NULL /* Authorization entry */
593 };
594 /* Create the authorization header entry */
595 apszHead[RT_ELEMENTS(apszHead)-1] = rtS3CreateAuthHeader(pS3Int, "GET", "", "", apszHead, RT_ELEMENTS(apszHead));
596
597 /* Add all headers to curl */
598 struct curl_slist* pHeaders = NULL; /* Init to NULL is important */
599 for(size_t i=0; i < RT_ELEMENTS(apszHead); ++i)
600 pHeaders = curl_slist_append(pHeaders, apszHead[i]);
601
602 /* Pass our list of custom made headers */
603 curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTPHEADER, pHeaders);
604
605 RTS3TMPMEMCHUNK chunk = { NULL, 0 };
606 /* Set the callback which receive the content */
607 curl_easy_setopt(pS3Int->pCurl, CURLOPT_WRITEFUNCTION, rtS3WriteMemoryCallback);
608 curl_easy_setopt(pS3Int->pCurl, CURLOPT_WRITEDATA, (void *)&chunk);
609 /* Start the request */
610 int rc = rtS3Perform(pS3Int);
611
612 /* Regardless of the result, free all used resources first*/
613 curl_slist_free_all(pHeaders);
614 for(size_t i=0; i < RT_ELEMENTS(apszHead); ++i)
615 RTStrFree(apszHead[i]);
616
617 /* On success parse the result */
618 if (RT_SUCCESS(rc))
619 {
620 xmlDocPtr pDoc;
621 xmlNodePtr pCur;
622 /* Parse the xml memory for "ListAllMyBucketsResult" */
623 rc = rtS3ReadXmlFromMemory(&chunk, "ListAllMyBucketsResult", &pDoc, &pCur);
624 if (RT_SUCCESS(rc))
625 {
626 /* Now extract all buckets */
627 rtS3ExtractAllBuckets(pDoc, pCur, ppBuckets);
628 /* Free the xml stuff */
629 xmlFreeDoc(pDoc);
630 }
631 }
632 /* Free the temporary memory */
633 RTMemFree(chunk.pszMem);
634
635 return rc;
636}
637
638RTR3DECL(int) RTS3BucketsDestroy(PCRTS3BUCKETENTRY pBuckets)
639{
640 if (!pBuckets)
641 return VINF_SUCCESS;
642
643 while (pBuckets)
644 {
645 PCRTS3BUCKETENTRY pTemp = pBuckets;
646 RTStrFree((char*)pBuckets->pszName);
647 RTStrFree((char*)pBuckets->pszCreationDate);
648 pBuckets = pBuckets->pNext;
649 RTMemFree((PRTS3BUCKETENTRY )pTemp);
650 }
651 return VINF_SUCCESS;
652}
653
654RTR3DECL(int) RTS3CreateBucket(RTS3 hS3, const char* pszBucketName)
655{
656 PRTS3INTERNAL pS3Int = hS3;
657 RTS3_VALID_RETURN(pS3Int);
658
659 /* Reset the CURL object to an defined state */
660 rtS3ReinitCurl(pS3Int);
661
662 char* pszUrl = rtS3Host(pszBucketName, "", pS3Int->pszBaseUrl);
663 curl_easy_setopt(pS3Int->pCurl, CURLOPT_URL, pszUrl);
664 RTStrFree(pszUrl);
665
666 /* Create the basic header entries */
667 char *apszHead[4] =
668 {
669 RTStrDup("Content-Length: 0"), /* Content length entry */
670 rtS3HostHeader(pszBucketName, pS3Int->pszBaseUrl), /* Host entry */
671 rtS3DateHeader(), /* Date entry */
672 NULL /* Authorization entry */
673 };
674 /* Create the authorization header entry */
675 apszHead[RT_ELEMENTS(apszHead)-1] = rtS3CreateAuthHeader(pS3Int, "PUT", pszBucketName, "", apszHead, RT_ELEMENTS(apszHead));
676
677 /* Add all headers to curl */
678 struct curl_slist* pHeaders = NULL; /* Init to NULL is important */
679 for(size_t i=0; i < RT_ELEMENTS(apszHead); ++i)
680 pHeaders = curl_slist_append(pHeaders, apszHead[i]);
681
682 /* Pass our list of custom made headers */
683 curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTPHEADER, pHeaders);
684
685 /* Set CURL in upload mode */
686 curl_easy_setopt(pS3Int->pCurl, CURLOPT_PUT, 1);
687 curl_easy_setopt(pS3Int->pCurl, CURLOPT_UPLOAD, 1);
688
689 /* Set the size of the file we like to transfer */
690 curl_easy_setopt(pS3Int->pCurl, CURLOPT_INFILESIZE_LARGE, 0);
691
692 /* Start the request */
693 int rc = rtS3Perform(pS3Int);
694 if (RT_FAILURE(rc))
695 {
696 /* Handle special failures */
697 if (pS3Int->lLastResp == 409)
698 rc = VERR_S3_BUCKET_ALREADY_EXISTS;
699 }
700
701 /* Regardless of the result, free all used resources first*/
702 curl_slist_free_all(pHeaders);
703 for(size_t i=0; i < RT_ELEMENTS(apszHead); ++i)
704 RTStrFree(apszHead[i]);
705
706 return rc;
707}
708
709RTR3DECL(int) RTS3DeleteBucket(RTS3 hS3, const char* pszBucketName)
710{
711 PRTS3INTERNAL pS3Int = hS3;
712 RTS3_VALID_RETURN(pS3Int);
713
714 /* Reset the CURL object to an defined state */
715 rtS3ReinitCurl(pS3Int);
716
717 char* pszUrl = rtS3Host(pszBucketName, "", pS3Int->pszBaseUrl);
718 curl_easy_setopt(pS3Int->pCurl, CURLOPT_URL, pszUrl);
719 RTStrFree(pszUrl);
720
721 /* Create the three basic header entries */
722 char *apszHead[3] =
723 {
724 rtS3HostHeader(pszBucketName, pS3Int->pszBaseUrl), /* Host entry */
725 rtS3DateHeader(), /* Date entry */
726 NULL /* Authorization entry */
727 };
728 /* Create the authorization header entry */
729 apszHead[RT_ELEMENTS(apszHead)-1] = rtS3CreateAuthHeader(pS3Int, "DELETE", pszBucketName, "", apszHead, RT_ELEMENTS(apszHead));
730
731 /* Add all headers to curl */
732 struct curl_slist* pHeaders = NULL; /* Init to NULL is important */
733 for(size_t i=0; i < RT_ELEMENTS(apszHead); ++i)
734 pHeaders = curl_slist_append(pHeaders, apszHead[i]);
735
736 /* Pass our list of custom made headers */
737 curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTPHEADER, pHeaders);
738
739 /* Set CURL in delete mode */
740 curl_easy_setopt(pS3Int->pCurl, CURLOPT_CUSTOMREQUEST, "DELETE");
741
742 /* Start the request */
743 int rc = rtS3Perform(pS3Int);
744 if (RT_FAILURE(rc))
745 {
746 /* Handle special failures */
747 if (pS3Int->lLastResp == 409)
748 rc = VERR_S3_BUCKET_NOT_EMPTY;
749 }
750
751 /* Regardless of the result, free all used resources first*/
752 curl_slist_free_all(pHeaders);
753 for(size_t i=0; i < RT_ELEMENTS(apszHead); ++i)
754 RTStrFree(apszHead[i]);
755
756 return rc;
757}
758
759RTR3DECL(int) RTS3GetBucketKeys(RTS3 hS3, const char* pszBucketName, PCRTS3KEYENTRY *ppKeys)
760{
761 PRTS3INTERNAL pS3Int = hS3;
762 RTS3_VALID_RETURN(pS3Int);
763
764 *ppKeys = NULL;
765
766 /* Reset the CURL object to an defined state */
767 rtS3ReinitCurl(pS3Int);
768
769 char* pszUrl = rtS3Host(pszBucketName, "", pS3Int->pszBaseUrl);
770 curl_easy_setopt(pS3Int->pCurl, CURLOPT_URL, pszUrl);
771 RTStrFree(pszUrl);
772
773 /* Create the three basic header entries */
774 char *apszHead[3] =
775 {
776 rtS3HostHeader(pszBucketName, pS3Int->pszBaseUrl), /* Host entry */
777 rtS3DateHeader(), /* Date entry */
778 NULL /* Authorization entry */
779 };
780 /* Create the authorization header entry */
781 apszHead[RT_ELEMENTS(apszHead)-1] = rtS3CreateAuthHeader(pS3Int, "GET", pszBucketName, "", apszHead, RT_ELEMENTS(apszHead));
782
783 /* Add all headers to curl */
784 struct curl_slist* pHeaders = NULL; /* Init to NULL is important */
785 for(size_t i=0; i < RT_ELEMENTS(apszHead); ++i)
786 pHeaders = curl_slist_append(pHeaders, apszHead[i]);
787
788 /* Pass our list of custom made headers */
789 curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTPHEADER, pHeaders);
790
791 RTS3TMPMEMCHUNK chunk = { NULL, 0 };
792 /* Set the callback which receive the content */
793 curl_easy_setopt(pS3Int->pCurl, CURLOPT_WRITEFUNCTION, rtS3WriteMemoryCallback);
794 curl_easy_setopt(pS3Int->pCurl, CURLOPT_WRITEDATA, (void *)&chunk);
795
796 /* Start the request */
797 int rc = rtS3Perform(pS3Int);
798
799 /* Regardless of the result, free all used resources first*/
800 curl_slist_free_all(pHeaders);
801 for(size_t i=0; i < RT_ELEMENTS(apszHead); ++i)
802 RTStrFree(apszHead[i]);
803
804 /* On success parse the result */
805 if (RT_SUCCESS(rc))
806 {
807 xmlDocPtr pDoc;
808 xmlNodePtr pCur;
809 /* Parse the xml memory for "ListBucketResult" */
810 rc = rtS3ReadXmlFromMemory(&chunk, "ListBucketResult", &pDoc, &pCur);
811 if (RT_SUCCESS(rc))
812 {
813 /* Now extract all buckets */
814 rtS3ExtractAllKeys(pDoc, pCur, ppKeys);
815 /* Free the xml stuff */
816 xmlFreeDoc(pDoc);
817 }
818 }
819 /* Free the temporary memory */
820 RTMemFree(chunk.pszMem);
821
822 return rc;
823}
824
825RTR3DECL(int) RTS3KeysDestroy(PCRTS3KEYENTRY pKeys)
826{
827 if (!pKeys)
828 return VINF_SUCCESS;
829
830 while (pKeys)
831 {
832 PCRTS3KEYENTRY pTemp = pKeys;
833 RTStrFree((char*)pKeys->pszName);
834 RTStrFree((char*)pKeys->pszLastModified);
835 pKeys = pKeys->pNext;
836 RTMemFree((PRTS3KEYENTRY)pTemp);
837 }
838 return VINF_SUCCESS;
839}
840
841RTR3DECL(int) RTS3DeleteKey(RTS3 hS3, const char* pszBucketName, const char* pszKeyName)
842{
843 PRTS3INTERNAL pS3Int = hS3;
844 RTS3_VALID_RETURN(pS3Int);
845
846 /* Reset the CURL object to an defined state */
847 rtS3ReinitCurl(pS3Int);
848
849 char* pszUrl = rtS3Host(pszBucketName, pszKeyName, pS3Int->pszBaseUrl);
850 curl_easy_setopt(pS3Int->pCurl, CURLOPT_URL, pszUrl);
851 RTStrFree(pszUrl);
852
853 /* Create the three basic header entries */
854 char *apszHead[3] =
855 {
856 rtS3HostHeader(pszBucketName, pS3Int->pszBaseUrl), /* Host entry */
857 rtS3DateHeader(), /* Date entry */
858 NULL /* Authorization entry */
859 };
860 /* Create the authorization header entry */
861 apszHead[RT_ELEMENTS(apszHead)-1] = rtS3CreateAuthHeader(pS3Int, "DELETE", pszBucketName, pszKeyName, apszHead, RT_ELEMENTS(apszHead));
862
863 /* Add all headers to curl */
864 struct curl_slist* pHeaders = NULL; /* Init to NULL is important */
865 for(size_t i=0; i < RT_ELEMENTS(apszHead); ++i)
866 pHeaders = curl_slist_append(pHeaders, apszHead[i]);
867
868 /* Pass our list of custom made headers */
869 curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTPHEADER, pHeaders);
870
871 /* Set CURL in delete mode */
872 curl_easy_setopt(pS3Int->pCurl, CURLOPT_CUSTOMREQUEST, "DELETE");
873
874 /* Start the request */
875 int rc = rtS3Perform(pS3Int);
876
877 /* Regardless of the result, free all used resources first*/
878 curl_slist_free_all(pHeaders);
879 for(size_t i=0; i < RT_ELEMENTS(apszHead); ++i)
880 RTStrFree(apszHead[i]);
881
882 return rc;
883}
884
885RTR3DECL(int) RTS3GetKey(RTS3 hS3, const char *pszBucketName, const char *pszKeyName, const char *pszFilename)
886{
887 PRTS3INTERNAL pS3Int = hS3;
888 RTS3_VALID_RETURN(pS3Int);
889
890 /* Reset the CURL object to an defined state */
891 rtS3ReinitCurl(pS3Int);
892
893 /* Open the file */
894 RTFILE hFile;
895 int rc = RTFileOpen(&hFile, pszFilename, RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
896 if (RT_FAILURE(rc))
897 return rc;
898
899 char* pszUrl = rtS3Host(pszBucketName, pszKeyName, pS3Int->pszBaseUrl);
900 curl_easy_setopt(pS3Int->pCurl, CURLOPT_URL, pszUrl);
901 RTStrFree(pszUrl);
902
903 /* Create the three basic header entries */
904 char *apszHead[3] =
905 {
906 rtS3HostHeader(pszBucketName, pS3Int->pszBaseUrl), /* Host entry */
907 rtS3DateHeader(), /* Date entry */
908 NULL /* Authorization entry */
909 };
910 /* Create the authorization header entry */
911 apszHead[RT_ELEMENTS(apszHead)-1] = rtS3CreateAuthHeader(pS3Int, "GET", pszBucketName, pszKeyName, apszHead, RT_ELEMENTS(apszHead));
912
913 /* Add all headers to curl */
914 struct curl_slist* pHeaders = NULL; /* Init to NULL is important */
915 for(size_t i=0; i < RT_ELEMENTS(apszHead); ++i)
916 pHeaders = curl_slist_append(pHeaders, apszHead[i]);
917
918 /* Pass our list of custom made headers */
919 curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTPHEADER, pHeaders);
920
921 /* Set the callback which receive the content */
922 curl_easy_setopt(pS3Int->pCurl, CURLOPT_WRITEFUNCTION, rtS3WriteFileCallback);
923 curl_easy_setopt(pS3Int->pCurl, CURLOPT_WRITEDATA, &hFile);
924
925 /* Start the request */
926 rc = rtS3Perform(pS3Int);
927
928 /* Regardless of the result, free all used resources first*/
929 curl_slist_free_all(pHeaders);
930 for(size_t i=0; i < RT_ELEMENTS(apszHead); ++i)
931 RTStrFree(apszHead[i]);
932
933 /* Close the open file */
934 RTFileClose(hFile);
935
936 /* If there was an error delete the newly created file */
937 if (RT_FAILURE(rc))
938 RTFileDelete(pszFilename);
939
940 return rc;
941}
942
943RTR3DECL(int) RTS3PutKey(RTS3 hS3, const char *pszBucketName, const char *pszKeyName, const char *pszFilename)
944{
945 PRTS3INTERNAL pS3Int = hS3;
946 RTS3_VALID_RETURN(pS3Int);
947
948 /* Reset the CURL object to an defined state */
949 rtS3ReinitCurl(pS3Int);
950
951 /* Open the file */
952 RTFILE hFile;
953 int rc = RTFileOpen(&hFile, pszFilename, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
954 if (RT_FAILURE(rc))
955 return rc;
956
957 uint64_t cbFileSize;
958 rc = RTFileGetSize(hFile, &cbFileSize);
959 if (RT_FAILURE(rc))
960 {
961 RTFileClose(hFile);
962 return rc;
963 }
964
965 char* pszUrl = rtS3Host(pszBucketName, pszKeyName, pS3Int->pszBaseUrl);
966 curl_easy_setopt(pS3Int->pCurl, CURLOPT_URL, pszUrl);
967 RTStrFree(pszUrl);
968
969 char* pszContentLength;
970 RTStrAPrintf(&pszContentLength, "Content-Length: %lu", cbFileSize);
971 /* Create the three basic header entries */
972 char *apszHead[5] =
973 {
974 /** @todo For now we use octet-stream for all types. Later we should try
975 * to set the right one (libmagic from the file packet could be a
976 * candidate for finding the right type). */
977 RTStrDup("Content-Type: octet-stream"), /* Content type entry */
978 pszContentLength, /* Content length entry */
979 rtS3HostHeader(pszBucketName, pS3Int->pszBaseUrl), /* Host entry */
980 rtS3DateHeader(), /* Date entry */
981 NULL /* Authorization entry */
982 };
983 /* Create the authorization header entry */
984 apszHead[RT_ELEMENTS(apszHead)-1] = rtS3CreateAuthHeader(pS3Int, "PUT", pszBucketName, pszKeyName, apszHead, RT_ELEMENTS(apszHead));
985
986 /* Add all headers to curl */
987 struct curl_slist* pHeaders = NULL; /* Init to NULL is important */
988 for(size_t i=0; i < RT_ELEMENTS(apszHead); ++i)
989 pHeaders = curl_slist_append(pHeaders, apszHead[i]);
990
991 /* Pass our list of custom made headers */
992 curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTPHEADER, pHeaders);
993
994 /* Set CURL in upload mode */
995 curl_easy_setopt(pS3Int->pCurl, CURLOPT_PUT, 1);
996 curl_easy_setopt(pS3Int->pCurl, CURLOPT_UPLOAD, 1);
997
998 /* Set the size of the file we like to transfer */
999 curl_easy_setopt(pS3Int->pCurl, CURLOPT_INFILESIZE_LARGE, cbFileSize);
1000
1001 /* Set the callback which send the content */
1002 curl_easy_setopt(pS3Int->pCurl, CURLOPT_READFUNCTION, rtS3ReadFileCallback);
1003 curl_easy_setopt(pS3Int->pCurl, CURLOPT_READDATA, &hFile);
1004 curl_easy_setopt(pS3Int->pCurl, CURLOPT_SSLVERSION, (long)CURL_SSLVERSION_TLSv1);
1005
1006 /* Start the request */
1007 rc = rtS3Perform(pS3Int);
1008
1009 /* Regardless of the result, free all used resources first*/
1010 curl_slist_free_all(pHeaders);
1011 for(size_t i=0; i < RT_ELEMENTS(apszHead); ++i)
1012 RTStrFree(apszHead[i]);
1013
1014 /* Close the open file */
1015 RTFileClose(hFile);
1016
1017 return rc;
1018}
1019
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette