VirtualBox

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

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