VirtualBox

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

Last change on this file since 37596 was 33540, checked in by vboxsync, 14 years ago

*: spelling fixes, thanks Timeless!

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