VirtualBox

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

Last change on this file since 21844 was 21743, checked in by vboxsync, 16 years ago

Runtime: more cleanup in the error case

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.8 KB
Line 
1/* $Id: s3.cpp 21743 2009-07-21 12:27:03Z 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 case CURLE_REMOTE_FILE_NOT_FOUND: rc = VERR_S3_NOT_FOUND; break;
256#if defined(CURLE_REMOTE_ACCESS_DENIED)
257 case CURLE_REMOTE_ACCESS_DENIED: rc = VERR_S3_ACCESS_DENIED; break;
258#elif defined(CURLE_FTP_ACCESS_DENIED)
259 case CURLE_FTP_ACCESS_DENIED: rc = VERR_S3_ACCESS_DENIED; break;
260#endif
261 case CURLE_ABORTED_BY_CALLBACK: rc = VERR_S3_CANCELED; break;
262 default: break;
263 }
264 }
265 return rc;
266}
267
268static size_t rtS3WriteNothingCallback(void *pvBuf, size_t cSize, size_t cBSize, void *pvUser)
269{
270 return cSize*cBSize;
271}
272
273static size_t rtS3WriteMemoryCallback(void *pvBuf, size_t cSize, size_t cBSize, void *pvUser)
274{
275 PRTS3TMPMEMCHUNK pTmpMem = (PRTS3TMPMEMCHUNK)pvUser;
276 size_t cRSize = cSize * cBSize;
277
278 pTmpMem->pszMem = (char*)RTMemRealloc(pTmpMem->pszMem, pTmpMem->cSize + cRSize + 1);
279 if (pTmpMem->pszMem)
280 {
281 memcpy(&(pTmpMem->pszMem[pTmpMem->cSize]), pvBuf, cRSize);
282 pTmpMem->cSize += cRSize;
283 pTmpMem->pszMem[pTmpMem->cSize] = 0;
284 }
285 return cRSize;
286}
287
288static size_t rtS3WriteFileCallback(void *pvBuf, size_t cSize, size_t cBSize, void *pvUser)
289{
290 size_t cWritten;
291 RTFileWrite(*(RTFILE*)pvUser, pvBuf, cSize * cBSize, &cWritten);
292 return cWritten;
293}
294
295static size_t rtS3ReadFileCallback(void *pvBuf, size_t cSize, size_t cBSize, void *pvUser)
296{
297 size_t cRead;
298 RTFileRead(*(RTFILE*)pvUser, pvBuf, cSize * cBSize, &cRead);
299
300 return cRead;
301}
302
303static int rtS3ProgressCallback(void *pvUser, double dDlTotal, double dDlNow, double dUlTotal, double dUlNow)
304{
305 if (pvUser)
306 {
307 PRTS3INTERNAL pS3Int = (PRTS3INTERNAL)pvUser;
308 if (pS3Int->pfnProgressCallback)
309 {
310 int rc = VINF_SUCCESS;
311 if (dDlTotal > 0)
312 rc = pS3Int->pfnProgressCallback((unsigned)(100.0/dDlTotal*dDlNow), pS3Int->pvUser);
313 else if (dUlTotal > 0)
314 rc = pS3Int->pfnProgressCallback((unsigned)(100.0/dUlTotal*dUlNow), pS3Int->pvUser);
315 if (rc != VINF_SUCCESS)
316 return -1;
317 }
318 }
319 return CURLE_OK;
320}
321
322static void rtS3ReinitCurl(PRTS3INTERNAL pS3Int)
323{
324 if (pS3Int &&
325 pS3Int->pCurl)
326 {
327 /* Reset the CURL object to an defined state */
328 curl_easy_reset(pS3Int->pCurl);
329 /* Make sure HTTP 1.1 is used */
330 curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
331 /* We are cool we are a user agent now */
332 if (pS3Int->pszUserAgent)
333 curl_easy_setopt(pS3Int->pCurl, CURLOPT_USERAGENT, pS3Int->pszUserAgent);
334 /* Check if the user has a progress callback requested */
335 if (pS3Int->pfnProgressCallback)
336 {
337 /* Yes, we are willing to receive progress info */
338 curl_easy_setopt(pS3Int->pCurl, CURLOPT_NOPROGRESS, 0);
339 /* Callback for the progress info */
340 curl_easy_setopt(pS3Int->pCurl, CURLOPT_PROGRESSFUNCTION, rtS3ProgressCallback);
341 curl_easy_setopt(pS3Int->pCurl, CURLOPT_PROGRESSDATA, pS3Int);
342 }
343 /* Disable the internal cURL write function by providing one which does
344 * nothing */
345 curl_easy_setopt(pS3Int->pCurl, CURLOPT_WRITEFUNCTION, rtS3WriteNothingCallback);
346 /* Set this do get some verbose info what CURL is doing */
347// curl_easy_setopt(pS3Int->pCurl, CURLOPT_VERBOSE, 1);
348 }
349}
350
351/*******************************************************************************
352* Private XML helper *
353*******************************************************************************/
354
355static xmlNodePtr rtS3FindNode(xmlNodePtr pNode, const char* pszName)
356{
357 pNode = pNode->xmlChildrenNode;
358 while (pNode != NULL)
359 {
360 /* Check this level */
361 if ((!xmlStrcmp(pNode->name, (const xmlChar *)pszName)))
362 return pNode;
363 /* Recursively check the childs of this node */
364 xmlNodePtr pChildNode = rtS3FindNode(pNode, pszName);
365 if (pChildNode != NULL)
366 return pChildNode;
367 /* Next node*/
368 pNode = pNode->next;
369 }
370 return pNode;
371}
372
373static int rtS3ReadXmlFromMemory(PRTS3TMPMEMCHUNK pChunk, const char* pszRootElement, xmlDocPtr *ppDoc, xmlNodePtr *ppCur)
374{
375 *ppDoc = xmlReadMemory(pChunk->pszMem, pChunk->cSize, "", "ISO-8859-1", XML_PARSE_NOBLANKS);
376 if (*ppDoc == NULL)
377 return VERR_PARSE_ERROR;
378
379 *ppCur = xmlDocGetRootElement(*ppDoc);
380 if (*ppCur == NULL)
381 {
382 xmlFreeDoc(*ppDoc);
383 return VERR_PARSE_ERROR;
384 }
385 if (xmlStrcmp((*ppCur)->name, (const xmlChar *) pszRootElement))
386 {
387 xmlFreeDoc(*ppDoc);
388 return VERR_PARSE_ERROR;
389 }
390 return VINF_SUCCESS;
391}
392
393static void rtS3ExtractAllBuckets(xmlDocPtr pDoc, xmlNodePtr pNode, PCRTS3BUCKETENTRY *ppBuckets)
394{
395 pNode = rtS3FindNode(pNode, "Buckets");
396 if (pNode != NULL)
397 {
398 PRTS3BUCKETENTRY pPrevBucket = NULL;
399 xmlNodePtr pCurBucket = pNode->xmlChildrenNode;
400 while (pCurBucket != NULL)
401 {
402 if ((!xmlStrcmp(pCurBucket->name, (const xmlChar *)"Bucket")))
403 {
404 PRTS3BUCKETENTRY pBucket = (PRTS3BUCKETENTRY)RTMemAllocZ(sizeof(RTS3BUCKETENTRY));
405 pBucket->pPrev = pPrevBucket;
406 if (pPrevBucket)
407 pPrevBucket->pNext = pBucket;
408 else
409 (*ppBuckets) = pBucket;
410 pPrevBucket = pBucket;
411 xmlNodePtr pCurCont = pCurBucket->xmlChildrenNode;
412 while (pCurCont != NULL)
413 {
414 if ((!xmlStrcmp(pCurCont->name, (const xmlChar *)"Name")))
415 {
416 xmlChar *pszKey = xmlNodeListGetString(pDoc, pCurCont->xmlChildrenNode, 1);
417 pBucket->pszName = RTStrDup((const char*)pszKey);
418 xmlFree(pszKey);
419 }
420 if ((!xmlStrcmp(pCurCont->name, (const xmlChar*)"CreationDate")))
421 {
422 xmlChar *pszKey = xmlNodeListGetString(pDoc, pCurCont->xmlChildrenNode, 1);
423 pBucket->pszCreationDate = RTStrDup((const char*)pszKey);
424 xmlFree(pszKey);
425 }
426 pCurCont = pCurCont->next;
427 }
428 }
429 pCurBucket = pCurBucket->next;
430 }
431 }
432}
433
434static void rtS3ExtractAllKeys(xmlDocPtr pDoc, xmlNodePtr pNode, PCRTS3KEYENTRY *ppKeys)
435{
436 if (pNode != NULL)
437 {
438 PRTS3KEYENTRY pPrevKey = NULL;
439 xmlNodePtr pCurKey = pNode->xmlChildrenNode;
440 while (pCurKey != NULL)
441 {
442 if ((!xmlStrcmp(pCurKey->name, (const xmlChar *)"Contents")))
443 {
444 PRTS3KEYENTRY pKey = (PRTS3KEYENTRY)RTMemAllocZ(sizeof(RTS3KEYENTRY));
445 pKey->pPrev = pPrevKey;
446 if (pPrevKey)
447 pPrevKey->pNext = pKey;
448 else
449 (*ppKeys) = pKey;
450 pPrevKey = pKey;
451 xmlNodePtr pCurCont = pCurKey->xmlChildrenNode;
452 while (pCurCont != NULL)
453 {
454 if ((!xmlStrcmp(pCurCont->name, (const xmlChar *)"Key")))
455 {
456 xmlChar *pszKey = xmlNodeListGetString(pDoc, pCurCont->xmlChildrenNode, 1);
457 pKey->pszName = RTStrDup((const char*)pszKey);
458 xmlFree(pszKey);
459 }
460 if ((!xmlStrcmp(pCurCont->name, (const xmlChar*)"LastModified")))
461 {
462 xmlChar *pszKey = xmlNodeListGetString(pDoc, pCurCont->xmlChildrenNode, 1);
463 pKey->pszLastModified = RTStrDup((const char*)pszKey);
464 xmlFree(pszKey);
465 }
466 if ((!xmlStrcmp(pCurCont->name, (const xmlChar*)"Size")))
467 {
468 xmlChar *pszKey = xmlNodeListGetString(pDoc, pCurCont->xmlChildrenNode, 1);
469 pKey->cbFile = RTStrToUInt64((const char*)pszKey);
470 xmlFree(pszKey);
471 }
472 pCurCont = pCurCont->next;
473 }
474 }
475 pCurKey = pCurKey->next;
476 }
477 }
478}
479
480/*******************************************************************************
481* Public RTS3 interface *
482*******************************************************************************/
483
484RTR3DECL(int) RTS3Create(PRTS3 ppS3, const char* pszAccessKey, const char* pszSecretKey, const char* pszBaseUrl, const char* pszUserAgent /* = NULL */)
485{
486 AssertPtrReturn(ppS3, VERR_INVALID_POINTER);
487
488 /* We need at least an URL to connect with */
489 if (pszBaseUrl == NULL ||
490 pszBaseUrl[0] == 0)
491 return VERR_INVALID_PARAMETER;
492
493 /* In windows, this will init the winsock stuff */
494 if (curl_global_init(CURL_GLOBAL_ALL) != 0)
495 return VERR_INTERNAL_ERROR;
496
497 CURL* pCurl = curl_easy_init();
498 if (!pCurl)
499 return VERR_INTERNAL_ERROR;
500
501 PRTS3INTERNAL pS3Int = (PRTS3INTERNAL)RTMemAllocZ(sizeof(RTS3INTERNAL));
502 if (pS3Int == NULL)
503 return VERR_NO_MEMORY;
504
505 pS3Int->u32Magic = RTS3_MAGIC;
506 pS3Int->pCurl = pCurl;
507 pS3Int->pszAccessKey = RTStrDup(pszAccessKey);
508 pS3Int->pszSecretKey = RTStrDup(pszSecretKey);
509 pS3Int->pszBaseUrl = RTStrDup(pszBaseUrl);
510 if (pszUserAgent)
511 pS3Int->pszUserAgent = RTStrDup(pszUserAgent);
512
513 *ppS3 = (RTS3)pS3Int;
514
515 return VINF_SUCCESS;
516}
517
518RTR3DECL(void) RTS3Destroy(RTS3 hS3)
519{
520 if (hS3 == NIL_RTS3)
521 return;
522
523 PRTS3INTERNAL pS3Int = hS3;
524 RTS3_VALID_RETURN_VOID(pS3Int);
525
526 curl_easy_cleanup(pS3Int->pCurl);
527
528 pS3Int->u32Magic = RTS3_MAGIC_DEAD;
529
530 if (pS3Int->pszUserAgent)
531 RTStrFree(pS3Int->pszUserAgent);
532 RTStrFree(pS3Int->pszBaseUrl);
533 RTStrFree(pS3Int->pszSecretKey);
534 RTStrFree(pS3Int->pszAccessKey);
535
536 RTMemFree(pS3Int);
537
538 curl_global_cleanup();
539}
540
541RTR3DECL(void) RTS3SetProgressCallback(RTS3 hS3, PFNRTS3PROGRESS pfnProgressCallback, void *pvUser /* = NULL */)
542{
543 PRTS3INTERNAL pS3Int = hS3;
544 RTS3_VALID_RETURN_VOID(pS3Int);
545
546 pS3Int->pfnProgressCallback = pfnProgressCallback;
547 pS3Int->pvUser = pvUser;
548}
549
550RTR3DECL(int) RTS3GetBuckets(RTS3 hS3, PCRTS3BUCKETENTRY *ppBuckets)
551{
552 PRTS3INTERNAL pS3Int = hS3;
553 RTS3_VALID_RETURN(pS3Int);
554
555 /* Properly initialize this */
556 *ppBuckets = NULL;
557
558 /* Reset the CURL object to an defined state */
559 rtS3ReinitCurl(pS3Int);
560 /* Create the CURL object to operate on */
561 curl_easy_setopt(pS3Int->pCurl, CURLOPT_URL, pS3Int->pszBaseUrl);
562
563 /* Create the three basic header entries */
564 char *ppszHead[3] =
565 {
566 rtS3HostHeader("", pS3Int->pszBaseUrl), /* Host entry */
567 rtS3DateHeader(), /* Date entry */
568 NULL /* Authorization entry */
569 };
570 /* Create the authorization header entry */
571 ppszHead[RT_ELEMENTS(ppszHead)-1] = rtS3CreateAuthHeader(pS3Int, "GET", "", "", ppszHead, RT_ELEMENTS(ppszHead));
572
573 /* Add all headers to curl */
574 struct curl_slist* pHeaders = NULL; /* Init to NULL is important */
575 for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
576 pHeaders = curl_slist_append(pHeaders, ppszHead[i]);
577
578 /* Pass our list of custom made headers */
579 curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTPHEADER, pHeaders);
580
581 RTS3TMPMEMCHUNK chunk = { NULL, 0 };
582 /* Set the callback which receive the content */
583 curl_easy_setopt(pS3Int->pCurl, CURLOPT_WRITEFUNCTION, rtS3WriteMemoryCallback);
584 curl_easy_setopt(pS3Int->pCurl, CURLOPT_WRITEDATA, (void *)&chunk);
585 /* Start the request */
586 int rc = rtS3Perform(pS3Int);
587
588 /* Regardless of the result, free all used resources first*/
589 curl_slist_free_all(pHeaders);
590 for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
591 RTStrFree(ppszHead[i]);
592
593 /* On success parse the result */
594 if (RT_SUCCESS(rc))
595 {
596 xmlDocPtr pDoc;
597 xmlNodePtr pCur;
598 /* Parse the xml memory for "ListAllMyBucketsResult" */
599 rc = rtS3ReadXmlFromMemory(&chunk, "ListAllMyBucketsResult", &pDoc, &pCur);
600 if (RT_SUCCESS(rc))
601 {
602 /* Now extract all buckets */
603 rtS3ExtractAllBuckets(pDoc, pCur, ppBuckets);
604 /* Free the xml stuff */
605 xmlFreeDoc(pDoc);
606 }
607 }
608 /* Free the temporary memory */
609 RTMemFree(chunk.pszMem);
610
611 return rc;
612}
613
614RTR3DECL(int) RTS3BucketsDestroy(PCRTS3BUCKETENTRY pBuckets)
615{
616 if (!pBuckets)
617 return VINF_SUCCESS;
618
619 while (pBuckets)
620 {
621 PCRTS3BUCKETENTRY pTemp = pBuckets;
622 RTStrFree((char*)pBuckets->pszName);
623 RTStrFree((char*)pBuckets->pszCreationDate);
624 pBuckets = pBuckets->pNext;
625 RTMemFree((PRTS3BUCKETENTRY )pTemp);
626 }
627 return VINF_SUCCESS;
628}
629
630RTR3DECL(int) RTS3CreateBucket(RTS3 hS3, const char* pszBucketName)
631{
632 PRTS3INTERNAL pS3Int = hS3;
633 RTS3_VALID_RETURN(pS3Int);
634
635 /* Reset the CURL object to an defined state */
636 rtS3ReinitCurl(pS3Int);
637
638 char* pszUrl = rtS3Host(pszBucketName, "", pS3Int->pszBaseUrl);
639 curl_easy_setopt(pS3Int->pCurl, CURLOPT_URL, pszUrl);
640 RTStrFree(pszUrl);
641
642 /* Create the basic header entries */
643 char *ppszHead[4] =
644 {
645 RTStrDup("Content-Length: 0"), /* Content length entry */
646 rtS3HostHeader(pszBucketName, pS3Int->pszBaseUrl), /* Host entry */
647 rtS3DateHeader(), /* Date entry */
648 NULL /* Authorization entry */
649 };
650 /* Create the authorization header entry */
651 ppszHead[RT_ELEMENTS(ppszHead)-1] = rtS3CreateAuthHeader(pS3Int, "PUT", pszBucketName, "", ppszHead, RT_ELEMENTS(ppszHead));
652
653 /* Add all headers to curl */
654 struct curl_slist* pHeaders = NULL; /* Init to NULL is important */
655 for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
656 pHeaders = curl_slist_append(pHeaders, ppszHead[i]);
657
658 /* Pass our list of custom made headers */
659 curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTPHEADER, pHeaders);
660
661 /* Set CURL in upload mode */
662 curl_easy_setopt(pS3Int->pCurl, CURLOPT_PUT, 1);
663 curl_easy_setopt(pS3Int->pCurl, CURLOPT_UPLOAD, 1);
664
665 /* Set the size of the file we like to transfer */
666 curl_easy_setopt(pS3Int->pCurl, CURLOPT_INFILESIZE_LARGE, 0);
667
668 /* Start the request */
669 int rc = rtS3Perform(pS3Int);
670 if (RT_FAILURE(rc))
671 {
672 /* Handle special failures */
673 if (pS3Int->lLastResp == 409)
674 rc = VERR_S3_BUCKET_ALREADY_EXISTS;
675 }
676
677 /* Regardless of the result, free all used resources first*/
678 curl_slist_free_all(pHeaders);
679 for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
680 RTStrFree(ppszHead[i]);
681
682 return rc;
683}
684
685RTR3DECL(int) RTS3DeleteBucket(RTS3 hS3, const char* pszBucketName)
686{
687 PRTS3INTERNAL pS3Int = hS3;
688 RTS3_VALID_RETURN(pS3Int);
689
690 /* Reset the CURL object to an defined state */
691 rtS3ReinitCurl(pS3Int);
692
693 char* pszUrl = rtS3Host(pszBucketName, "", pS3Int->pszBaseUrl);
694 curl_easy_setopt(pS3Int->pCurl, CURLOPT_URL, pszUrl);
695 RTStrFree(pszUrl);
696
697 /* Create the three basic header entries */
698 char *ppszHead[3] =
699 {
700 rtS3HostHeader(pszBucketName, pS3Int->pszBaseUrl), /* Host entry */
701 rtS3DateHeader(), /* Date entry */
702 NULL /* Authorization entry */
703 };
704 /* Create the authorization header entry */
705 ppszHead[RT_ELEMENTS(ppszHead)-1] = rtS3CreateAuthHeader(pS3Int, "DELETE", pszBucketName, "", ppszHead, RT_ELEMENTS(ppszHead));
706
707 /* Add all headers to curl */
708 struct curl_slist* pHeaders = NULL; /* Init to NULL is important */
709 for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
710 pHeaders = curl_slist_append(pHeaders, ppszHead[i]);
711
712 /* Pass our list of custom made headers */
713 curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTPHEADER, pHeaders);
714
715 /* Set CURL in delete mode */
716 curl_easy_setopt(pS3Int->pCurl, CURLOPT_CUSTOMREQUEST, "DELETE");
717
718 /* Start the request */
719 int rc = rtS3Perform(pS3Int);
720 if (RT_FAILURE(rc))
721 {
722 /* Handle special failures */
723 if (pS3Int->lLastResp == 409)
724 rc = VERR_S3_BUCKET_NOT_EMPTY;
725 }
726
727 /* Regardless of the result, free all used resources first*/
728 curl_slist_free_all(pHeaders);
729 for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
730 RTStrFree(ppszHead[i]);
731
732 return rc;
733}
734
735RTR3DECL(int) RTS3GetBucketKeys(RTS3 hS3, const char* pszBucketName, PCRTS3KEYENTRY *ppKeys)
736{
737 PRTS3INTERNAL pS3Int = hS3;
738 RTS3_VALID_RETURN(pS3Int);
739
740 *ppKeys = NULL;
741
742 /* Reset the CURL object to an defined state */
743 rtS3ReinitCurl(pS3Int);
744
745 char* pszUrl = rtS3Host(pszBucketName, "", pS3Int->pszBaseUrl);
746 curl_easy_setopt(pS3Int->pCurl, CURLOPT_URL, pszUrl);
747 RTStrFree(pszUrl);
748
749 /* Create the three basic header entries */
750 char *ppszHead[3] =
751 {
752 rtS3HostHeader(pszBucketName, pS3Int->pszBaseUrl), /* Host entry */
753 rtS3DateHeader(), /* Date entry */
754 NULL /* Authorization entry */
755 };
756 /* Create the authorization header entry */
757 ppszHead[RT_ELEMENTS(ppszHead)-1] = rtS3CreateAuthHeader(pS3Int, "GET", pszBucketName, "", ppszHead, RT_ELEMENTS(ppszHead));
758
759 /* Add all headers to curl */
760 struct curl_slist* pHeaders = NULL; /* Init to NULL is important */
761 for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
762 pHeaders = curl_slist_append(pHeaders, ppszHead[i]);
763
764 /* Pass our list of custom made headers */
765 curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTPHEADER, pHeaders);
766
767 RTS3TMPMEMCHUNK chunk = { NULL, 0 };
768 /* Set the callback which recieve the content */
769 curl_easy_setopt(pS3Int->pCurl, CURLOPT_WRITEFUNCTION, rtS3WriteMemoryCallback);
770 curl_easy_setopt(pS3Int->pCurl, CURLOPT_WRITEDATA, (void *)&chunk);
771
772 /* Start the request */
773 int rc = rtS3Perform(pS3Int);
774
775 /* Regardless of the result, free all used resources first*/
776 curl_slist_free_all(pHeaders);
777 for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
778 RTStrFree(ppszHead[i]);
779
780 /* On success parse the result */
781 if (RT_SUCCESS(rc))
782 {
783 xmlDocPtr pDoc;
784 xmlNodePtr pCur;
785 /* Parse the xml memory for "ListBucketResult" */
786 rc = rtS3ReadXmlFromMemory(&chunk, "ListBucketResult", &pDoc, &pCur);
787 if (RT_SUCCESS(rc))
788 {
789 /* Now extract all buckets */
790 rtS3ExtractAllKeys(pDoc, pCur, ppKeys);
791 /* Free the xml stuff */
792 xmlFreeDoc(pDoc);
793 }
794 }
795 /* Free the tempory memory */
796 RTMemFree(chunk.pszMem);
797
798 return rc;
799}
800
801RTR3DECL(int) RTS3KeysDestroy(PCRTS3KEYENTRY pKeys)
802{
803 if (!pKeys)
804 return VINF_SUCCESS;
805
806 while (pKeys)
807 {
808 PCRTS3KEYENTRY pTemp = pKeys;
809 RTStrFree((char*)pKeys->pszName);
810 RTStrFree((char*)pKeys->pszLastModified);
811 pKeys = pKeys->pNext;
812 RTMemFree((PRTS3KEYENTRY)pTemp);
813 }
814 return VINF_SUCCESS;
815}
816
817RTR3DECL(int) RTS3DeleteKey(RTS3 hS3, const char* pszBucketName, const char* pszKeyName)
818{
819 PRTS3INTERNAL pS3Int = hS3;
820 RTS3_VALID_RETURN(pS3Int);
821
822 /* Reset the CURL object to an defined state */
823 rtS3ReinitCurl(pS3Int);
824
825 char* pszUrl = rtS3Host(pszBucketName, pszKeyName, pS3Int->pszBaseUrl);
826 curl_easy_setopt(pS3Int->pCurl, CURLOPT_URL, pszUrl);
827 RTStrFree(pszUrl);
828
829 /* Create the three basic header entries */
830 char *ppszHead[3] =
831 {
832 rtS3HostHeader(pszBucketName, pS3Int->pszBaseUrl), /* Host entry */
833 rtS3DateHeader(), /* Date entry */
834 NULL /* Authorization entry */
835 };
836 /* Create the authorization header entry */
837 ppszHead[RT_ELEMENTS(ppszHead)-1] = rtS3CreateAuthHeader(pS3Int, "DELETE", pszBucketName, pszKeyName, ppszHead, RT_ELEMENTS(ppszHead));
838
839 /* Add all headers to curl */
840 struct curl_slist* pHeaders = NULL; /* Init to NULL is important */
841 for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
842 pHeaders = curl_slist_append(pHeaders, ppszHead[i]);
843
844 /* Pass our list of custom made headers */
845 curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTPHEADER, pHeaders);
846
847 /* Set CURL in delete mode */
848 curl_easy_setopt(pS3Int->pCurl, CURLOPT_CUSTOMREQUEST, "DELETE");
849
850 /* Start the request */
851 int rc = rtS3Perform(pS3Int);
852
853 /* Regardless of the result, free all used resources first*/
854 curl_slist_free_all(pHeaders);
855 for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
856 RTStrFree(ppszHead[i]);
857
858 return rc;
859}
860
861RTR3DECL(int) RTS3GetKey(RTS3 hS3, const char* pszBucketName, const char* pszKeyName, const char* pszFileName)
862{
863 PRTS3INTERNAL pS3Int = hS3;
864 RTS3_VALID_RETURN(pS3Int);
865
866 /* Reset the CURL object to an defined state */
867 rtS3ReinitCurl(pS3Int);
868
869 /* Open the file */
870 RTFILE hFile;
871 int rc = RTFileOpen(&hFile, pszFileName, RTFILE_O_CREATE | RTFILE_O_WRITE);
872 if (RT_FAILURE(rc))
873 return rc;
874
875 char* pszUrl = rtS3Host(pszBucketName, pszKeyName, pS3Int->pszBaseUrl);
876 curl_easy_setopt(pS3Int->pCurl, CURLOPT_URL, pszUrl);
877 RTStrFree(pszUrl);
878
879 /* Create the three basic header entries */
880 char *ppszHead[3] =
881 {
882 rtS3HostHeader(pszBucketName, pS3Int->pszBaseUrl), /* Host entry */
883 rtS3DateHeader(), /* Date entry */
884 NULL /* Authorization entry */
885 };
886 /* Create the authorization header entry */
887 ppszHead[RT_ELEMENTS(ppszHead)-1] = rtS3CreateAuthHeader(pS3Int, "GET", pszBucketName, pszKeyName, ppszHead, RT_ELEMENTS(ppszHead));
888
889 /* Add all headers to curl */
890 struct curl_slist* pHeaders = NULL; /* Init to NULL is important */
891 for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
892 pHeaders = curl_slist_append(pHeaders, ppszHead[i]);
893
894 /* Pass our list of custom made headers */
895 curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTPHEADER, pHeaders);
896
897 /* Set the callback which receive the content */
898 curl_easy_setopt(pS3Int->pCurl, CURLOPT_WRITEFUNCTION, rtS3WriteFileCallback);
899 curl_easy_setopt(pS3Int->pCurl, CURLOPT_WRITEDATA, &hFile);
900
901 /* Start the request */
902 rc = rtS3Perform(pS3Int);
903
904 /* Regardless of the result, free all used resources first*/
905 curl_slist_free_all(pHeaders);
906 for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
907 RTStrFree(ppszHead[i]);
908
909 /* Close the open file */
910 RTFileClose(hFile);
911
912 /* If there was an error delete the newly created file */
913 if (RT_FAILURE(rc))
914 RTFileDelete(pszFileName);
915
916 return rc;
917}
918
919RTR3DECL(int) RTS3PutKey(RTS3 hS3, const char* pszBucketName, const char* pszKeyName, const char* pszFileName)
920{
921 PRTS3INTERNAL pS3Int = hS3;
922 RTS3_VALID_RETURN(pS3Int);
923
924 /* Reset the CURL object to an defined state */
925 rtS3ReinitCurl(pS3Int);
926
927 /* Open the file */
928 RTFILE hFile;
929 int rc = RTFileOpen(&hFile, pszFileName, RTFILE_O_OPEN | RTFILE_O_READ);
930 if (RT_FAILURE(rc))
931 return rc;
932
933 uint64_t cbFileSize;
934 rc = RTFileGetSize(hFile, &cbFileSize);
935 if (RT_FAILURE(rc))
936 {
937 RTFileClose(hFile);
938 return rc;
939 }
940
941 char* pszUrl = rtS3Host(pszBucketName, pszKeyName, pS3Int->pszBaseUrl);
942 curl_easy_setopt(pS3Int->pCurl, CURLOPT_URL, pszUrl);
943 RTStrFree(pszUrl);
944
945 char* pszContentLength;
946 RTStrAPrintf(&pszContentLength, "Content-Length: %lu", cbFileSize);
947 /* Create the three basic header entries */
948 char *ppszHead[5] =
949 {
950 /* todo: For now we use octet-stream for all types. Later we should try
951 * to set the right one (libmagic from the file packet could be a
952 * candidate for finding the right type). */
953 RTStrDup("Content-Type: octet-stream"), /* Content type entry */
954 pszContentLength, /* Content length entry */
955 rtS3HostHeader(pszBucketName, pS3Int->pszBaseUrl), /* Host entry */
956 rtS3DateHeader(), /* Date entry */
957 NULL /* Authorization entry */
958 };
959 /* Create the authorization header entry */
960 ppszHead[RT_ELEMENTS(ppszHead)-1] = rtS3CreateAuthHeader(pS3Int, "PUT", pszBucketName, pszKeyName, ppszHead, RT_ELEMENTS(ppszHead));
961
962 /* Add all headers to curl */
963 struct curl_slist* pHeaders = NULL; /* Init to NULL is important */
964 for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
965 pHeaders = curl_slist_append(pHeaders, ppszHead[i]);
966
967 /* Pass our list of custom made headers */
968 curl_easy_setopt(pS3Int->pCurl, CURLOPT_HTTPHEADER, pHeaders);
969
970 /* Set CURL in upload mode */
971 curl_easy_setopt(pS3Int->pCurl, CURLOPT_PUT, 1);
972 curl_easy_setopt(pS3Int->pCurl, CURLOPT_UPLOAD, 1);
973
974 /* Set the size of the file we like to transfer */
975 curl_easy_setopt(pS3Int->pCurl, CURLOPT_INFILESIZE_LARGE, cbFileSize);
976
977 /* Set the callback which send the content */
978 curl_easy_setopt(pS3Int->pCurl, CURLOPT_READFUNCTION, rtS3ReadFileCallback);
979 curl_easy_setopt(pS3Int->pCurl, CURLOPT_READDATA, &hFile);
980
981 /* Start the request */
982 rc = rtS3Perform(pS3Int);
983
984 /* Regardless of the result, free all used resources first*/
985 curl_slist_free_all(pHeaders);
986 for(size_t i=0; i < RT_ELEMENTS(ppszHead); ++i)
987 RTStrFree(ppszHead[i]);
988
989 /* Close the open file */
990 RTFileClose(hFile);
991
992 return rc;
993}
994
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