VirtualBox

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

Last change on this file since 20167 was 20043, checked in by vboxsync, 16 years ago

Runtime: export to OSE

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

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