VirtualBox

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

Last change on this file since 25528 was 23973, checked in by vboxsync, 15 years ago

*,RTFileOpen: Fixing RTFileOpen flag misdesign: The deny, access and action flags are mandatory now.

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