VirtualBox

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

Last change on this file since 38730 was 38730, checked in by vboxsync, 13 years ago

rtS3DateHeader: How to generate a date field using IPRT (disabled).

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