VirtualBox

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

Last change on this file since 45256 was 42261, checked in by vboxsync, 12 years ago

enabled shared clipboard support for Linux hosts (guest=>host only)

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