VirtualBox

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

Last change on this file since 21107 was 20293, checked in by vboxsync, 16 years ago

Runtime/s3: be compatible with older libcurls

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