VirtualBox

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

Last change on this file since 62559 was 62477, checked in by vboxsync, 8 years ago

(C) 2016

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