~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/pbms/src/cslib/CSS3Protocol.cc

Added the PBMS daemon plugin.

(Augen zu und durch!)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2009 PrimeBase Technologies GmbH, Germany
 
2
 *
 
3
 * PrimeBase Media Stream for MySQL
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License as published by
 
7
 * the Free Software Foundation; either version 2 of the License, or
 
8
 * (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program; if not, write to the Free Software
 
17
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 
18
 *
 
19
 *  Created by Barry Leslie on 10/02/09.
 
20
 *
 
21
 */
 
22
#include "CSConfig.h"
 
23
#include <inttypes.h>
 
24
 
 
25
#undef NO_XML2
 
26
 
 
27
#include <curl/curl.h>
 
28
#ifndef NO_XML2 
 
29
#include <libxml/parser.h>
 
30
#endif
 
31
 
 
32
#include "CSGlobal.h"
 
33
#include "CSString.h"
 
34
#include "CSStrUtil.h"
 
35
#include "CSEncode.h"
 
36
#include "CSS3Protocol.h"
 
37
 
 
38
#ifdef S3_UNIT_TEST
 
39
//#define SHOW_SIGNING
 
40
// Uncomment this line to trace network action during request. Very Usefull!!
 
41
//#define DEBUG_CURL
 
42
#define DUMP_ERRORS
 
43
#endif
 
44
 
 
45
#define DUMP_ERRORS
 
46
//#define SHOW_SIGNING
 
47
 
 
48
#define HEX_CHECKSUM_VALUE_SIZE (2 *CHECKSUM_VALUE_SIZE)
 
49
 
 
50
#define THROW_CURL_IF(v) { if (v) CSException::throwException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, ms_curl_error);}
 
51
 
 
52
//-------------------------------
 
53
static const char *retryCodes[] = {
 
54
        "ExpiredToken",
 
55
        "InternalError",
 
56
        "OperationAborted",
 
57
        "RequestTimeout",
 
58
        "SlowDown",
 
59
        NULL
 
60
};
 
61
 
 
62
//======================================
 
63
static size_t receive_data(void *ptr, size_t size, size_t nmemb, void *stream);
 
64
static size_t receive_header(void *ptr, size_t size, size_t nmemb, void *stream);
 
65
static size_t send_callback(void *ptr, size_t size, size_t nmemb, void *stream);
 
66
 
 
67
//==========================
 
68
typedef struct {
 
69
        bool thow_error; // Must be the first field.
 
70
}ParserInfoRec, *ParserInfoPtr;
 
71
 
 
72
typedef struct {
 
73
        bool thow_error; // Must be the first field.
 
74
        class S3ProtocolCon *s3Con;
 
75
        char code[32];
 
76
        CSStringBuffer *buffer;
 
77
        CSStringBuffer *message;
 
78
} ParserErrorInfoRec, *ParserErrorInfoPtr;
 
79
 
 
80
//-------------------------------
 
81
// There shouldn't be any XML parser errors unless we received an
 
82
// incomplete reply from te S3 server.
 
83
static void saxError(void *user_data, const char *msg, ...)
 
84
{
 
85
        ParserInfoPtr info = (ParserInfoPtr) user_data;
 
86
/*      
 
87
        va_list args;
 
88
        va_start(args, msg);
 
89
        vprintf( msg, args );
 
90
        va_end(args);
 
91
*/
 
92
        if (!info->thow_error) {
 
93
                enter_();
 
94
                CSException::RecordException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, "xml SAX parser error");
 
95
                info->thow_error = true;
 
96
                outer_();
 
97
        }
 
98
}
 
99
 
 
100
class S3ProtocolCon : public CSObject {
 
101
 
 
102
        private:
 
103
        
 
104
        static void saxEndErrorElement(void *user_data, const xmlChar *name)
 
105
        {
 
106
                ParserErrorInfoPtr info = (ParserErrorInfoPtr) user_data;
 
107
 
 
108
                if (info->thow_error)
 
109
                return;
 
110
 
 
111
                enter_();
 
112
                try_(a) {
 
113
                        if (!strcmp("Code", (char *) name)) {           
 
114
                                cs_strcpy(32, info->code, info->buffer->getBuffer(0), info->buffer->length());
 
115
                                
 
116
                                for (int i = 0; retryCodes[i] && !info->s3Con->ms_retry; i++)
 
117
                                        info->s3Con->ms_retry = (strcmp(info->code, retryCodes[i]) == 0);
 
118
                                        
 
119
                                if (info->s3Con->ms_retry && !strcmp("SlowDown", info->code)) 
 
120
                                        info->s3Con->ms_slowDown = true;
 
121
                                        
 
122
                        } else if (!strcmp("Message", (char *) name)) {
 
123
                                info->message->append(info->buffer->getBuffer(0), info->buffer->length());
 
124
                        }
 
125
                }
 
126
                catch_(a);
 
127
                info->thow_error = true;
 
128
                cont_(a);
 
129
                outer_();
 
130
                
 
131
                info->buffer->setLength(0);
 
132
        }
 
133
 
 
134
        //-------------------------------
 
135
        static void saxErrorData(void *user_data, const xmlChar *ch, int len)
 
136
        {
 
137
                ParserErrorInfoPtr info = (ParserErrorInfoPtr) user_data;
 
138
 
 
139
                if (info->thow_error) 
 
140
                        return;
 
141
 
 
142
                enter_();
 
143
                try_(a) {
 
144
                        info->buffer->append( (char*) ch, len);
 
145
                }
 
146
                catch_(a);
 
147
                info->thow_error = true;
 
148
                cont_(a);
 
149
                outer_();
 
150
 
 
151
        }
 
152
 
 
153
        //-------------------------------
 
154
        void parse_s3_error()
 
155
        {
 
156
                ParserErrorInfoRec parser_info = {0};
 
157
                struct _xmlSAXHandler sax_handler = {0};
 
158
                
 
159
                enter_();
 
160
                
 
161
                parser_info.s3Con = this;
 
162
                
 
163
                if (!ms_errorReply)
 
164
                        CSException::throwException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, "Missing HTTP reply: possible S3 connection failure.");
 
165
 
 
166
#ifdef DUMP_ERRORS
 
167
                printf("ms_errorReply:\n===========\n%s\n===========\n", ms_errorReply->getCString());
 
168
#endif
 
169
                
 
170
                //sax_handler.startElement = &saxStartErrorElement;
 
171
                sax_handler.endElement = &saxEndErrorElement;
 
172
                sax_handler.characters = &saxErrorData;
 
173
                sax_handler.cdataBlock = &saxErrorData;
 
174
                sax_handler.error = &saxError;
 
175
                sax_handler.fatalError = &saxError;
 
176
                
 
177
                new_(parser_info.buffer, CSStringBuffer());
 
178
                push_(parser_info.buffer);
 
179
                
 
180
                new_(parser_info.message, CSStringBuffer());
 
181
                push_(parser_info.message);
 
182
                
 
183
                if (xmlSAXUserParseMemory(&sax_handler, &parser_info, ms_errorReply->getBuffer(0), ms_errorReply->length())) {
 
184
                        if (!parser_info.thow_error)
 
185
                                CSException::throwException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, "xml SAX parser error");
 
186
                }
 
187
                
 
188
                if (parser_info.thow_error)
 
189
                        throw_();
 
190
                        
 
191
                release_(parser_info.message);
 
192
                release_(parser_info.buffer);
 
193
 
 
194
                exit_();
 
195
        }
 
196
 
 
197
        public:
 
198
        
 
199
        CSHTTPHeaders   ms_reply_headers;
 
200
        CSStringBuffer  ms_buffer; // A scratch buffer
 
201
 
 
202
        CURL                    *ms_curl;       
 
203
        struct curl_slist       *ms_header_list;        // A curl list of headers to be sent with the next request.
 
204
 
 
205
        CSInputStream   *ms_inputStream;
 
206
        CSOutputStream  *ms_outputStream;
 
207
        
 
208
        CSMd5                   ms_md5;
 
209
        char                    ms_s3Checksum[HEX_CHECKSUM_VALUE_SIZE +1];
 
210
        
 
211
        bool                    ms_notFound; // True if the object could not be found
 
212
        bool                    ms_retry; // True if the request failed with a retry error.
 
213
        bool                    ms_slowDown;
 
214
        
 
215
        CSStringBuffer  *ms_errorReply;
 
216
        char                    ms_curl_error[CURL_ERROR_SIZE];
 
217
        
 
218
        off_t                   ms_data_size;
 
219
        
 
220
        unsigned int    ms_replyStatus;
 
221
        bool                    ms_throw_error; // Gets set if an exception occurs in a callback.
 
222
 
 
223
        S3ProtocolCon(CSS3Protocol *prot):
 
224
                ms_curl(NULL),
 
225
                ms_header_list(NULL),
 
226
                ms_inputStream(NULL),
 
227
                ms_outputStream(NULL),
 
228
                ms_notFound(false),
 
229
                ms_retry(false),
 
230
                ms_slowDown(false),
 
231
                ms_errorReply(NULL),
 
232
                ms_replyStatus(0),
 
233
                ms_data_size(0),
 
234
                ms_throw_error(false)
 
235
        {
 
236
        
 
237
                ms_curl = curl_easy_init();
 
238
                if (!ms_curl)
 
239
                        CSException::throwException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, "curl_easy_init() failed.");
 
240
                
 
241
                if (curl_easy_setopt(ms_curl, CURLOPT_ERRORBUFFER, ms_curl_error))
 
242
                        CSException::throwException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, "curl_easy_setopt(CURLOPT_ERRORBUFFER) failed.");
 
243
                
 
244
#ifdef DEBUG_CURL
 
245
                curl_easy_setopt(ms_curl, CURLOPT_VERBOSE, 1L);
 
246
#endif          
 
247
                //THROW_CURL_IF(curl_easy_setopt(ms_curl, CURLOPT_TCP_NODELAY, 1L));
 
248
        
 
249
 
 
250
                THROW_CURL_IF(curl_easy_setopt(ms_curl, CURLOPT_NOPROGRESS, 1L));
 
251
                THROW_CURL_IF(curl_easy_setopt(ms_curl, CURLOPT_WRITEFUNCTION, receive_data));
 
252
                THROW_CURL_IF(curl_easy_setopt(ms_curl, CURLOPT_READFUNCTION, send_callback));  
 
253
                THROW_CURL_IF(curl_easy_setopt(ms_curl, CURLOPT_HEADERFUNCTION, receive_header));
 
254
                THROW_CURL_IF(curl_easy_setopt(ms_curl, CURLOPT_WRITEDATA, this));
 
255
                THROW_CURL_IF(curl_easy_setopt(ms_curl, CURLOPT_READDATA, this));
 
256
                THROW_CURL_IF(curl_easy_setopt(ms_curl, CURLOPT_WRITEHEADER, this));
 
257
                
 
258
 
 
259
                THROW_CURL_IF(curl_easy_setopt(ms_curl, CURLOPT_FOLLOWLOCATION, 1L)); // Follow redirects.
 
260
        
 
261
        }
 
262
        
 
263
        ~S3ProtocolCon()
 
264
        {
 
265
                if (ms_curl) 
 
266
                        curl_easy_cleanup(ms_curl);                     
 
267
                if (ms_header_list) 
 
268
                        curl_slist_free_all(ms_header_list);                    
 
269
                if (ms_inputStream)
 
270
                        ms_inputStream->release();
 
271
                if (ms_outputStream)
 
272
                        ms_outputStream->release();
 
273
                if (ms_errorReply)
 
274
                        ms_errorReply->release();
 
275
                        
 
276
                ms_reply_headers.clearHeaders();
 
277
        }
 
278
 
 
279
        inline void check_reply_status() 
 
280
        {
 
281
                if (ms_replyStatus > 199 && ms_replyStatus < 300)
 
282
                        return;
 
283
                
 
284
                
 
285
                
 
286
                switch (ms_replyStatus) {
 
287
                        case 200:
 
288
                        case 204:       // No Content
 
289
                        //case 301: // Moved Permanently
 
290
                        //case 307: // Temporary Redirect
 
291
                                break;
 
292
                        case 404:       // Not Found
 
293
                                ms_notFound = true;
 
294
                                break;
 
295
                        case 500:       
 
296
                                ms_retry = true;
 
297
                                break;
 
298
                        default: {
 
299
                                parse_s3_error();
 
300
                                
 
301
                                
 
302
                                
 
303
                                if (!ms_retry) {
 
304
                                        enter_();
 
305
                                        CSException::throwException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, ms_errorReply->getCString());
 
306
                                        outer_();
 
307
                                } else if (ms_slowDown) {
 
308
                                        enter_();
 
309
                                        CSException::logException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, "S3 slow down request.");
 
310
                                        self->sleep(10); // sleep for 1/100 second.
 
311
                                        outer_();
 
312
                                }
 
313
                        }
 
314
                }
 
315
                
 
316
        }
 
317
 
 
318
        
 
319
        inline void ms_reset()
 
320
        {
 
321
                // Remove any old headers
 
322
                if (ms_header_list) {
 
323
                        curl_slist_free_all(ms_header_list);
 
324
                        ms_header_list = NULL;
 
325
                }
 
326
 
 
327
                ms_reply_headers.clearHeaders();
 
328
                ms_replyStatus = 0;
 
329
                ms_throw_error = false;
 
330
                if (ms_errorReply)
 
331
                        ms_errorReply->setLength(0);
 
332
                        
 
333
                ms_s3Checksum[0] = 0;
 
334
                ms_notFound = false;
 
335
                ms_retry = false;
 
336
                
 
337
                if (ms_outputStream) {
 
338
                        ms_outputStream->release();
 
339
                        ms_outputStream = NULL;
 
340
                }
 
341
                if (ms_inputStream) {
 
342
                        ms_inputStream->release();
 
343
                        ms_inputStream = NULL;
 
344
                }
 
345
        }
 
346
        
 
347
        inline void ms_setHeader(const char *header)
 
348
        {
 
349
                ms_header_list = curl_slist_append(ms_header_list, header);
 
350
                if (!ms_header_list) 
 
351
                        CSException::throwException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, "curl_slist_append() failed.");
 
352
        }
 
353
        
 
354
        inline void ms_setURL(const char *url)
 
355
        {
 
356
                //printf("URL: \"%s\n", url);
 
357
 
 
358
                THROW_CURL_IF(curl_easy_setopt(ms_curl, CURLOPT_URL, url));
 
359
        }
 
360
        
 
361
        inline void ms_execute_delete_request()
 
362
        {
 
363
                CURLcode rtc;
 
364
                
 
365
                THROW_CURL_IF(curl_easy_setopt(ms_curl, CURLOPT_HTTPHEADER, ms_header_list));
 
366
                THROW_CURL_IF(curl_easy_setopt(ms_curl, CURLOPT_CUSTOMREQUEST, "DELETE"));
 
367
 
 
368
                rtc = curl_easy_perform(ms_curl);
 
369
                
 
370
                THROW_CURL_IF(curl_easy_setopt(ms_curl, CURLOPT_CUSTOMREQUEST, NULL)); // IMPORTANT: Reset this to it's default value
 
371
 
 
372
                if (rtc && !ms_throw_error)
 
373
                        CSException::throwException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, ms_curl_error);
 
374
                        
 
375
                if (ms_throw_error) {
 
376
                        enter_();
 
377
                        throw_();
 
378
                        outer_();
 
379
                }
 
380
                
 
381
                check_reply_status();
 
382
        }
 
383
        
 
384
        inline void ms_execute_copy_request()
 
385
        {
 
386
                CURLcode rtc;
 
387
                
 
388
                THROW_CURL_IF(curl_easy_setopt(ms_curl, CURLOPT_HTTPHEADER, ms_header_list));
 
389
                THROW_CURL_IF(curl_easy_setopt(ms_curl, CURLOPT_INFILESIZE_LARGE, 0));
 
390
                THROW_CURL_IF(curl_easy_setopt(ms_curl, CURLOPT_UPLOAD, 1L));
 
391
                
 
392
                rtc = curl_easy_perform(ms_curl);
 
393
                
 
394
                if (rtc && !ms_throw_error)
 
395
                        CSException::throwException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, ms_curl_error);
 
396
                        
 
397
                if (ms_throw_error) {
 
398
                        enter_();
 
399
                        throw_();
 
400
                        outer_();
 
401
                }
 
402
                
 
403
                check_reply_status();
 
404
        }
 
405
        
 
406
        inline void ms_execute_get_request(CSOutputStream *output)
 
407
        {
 
408
                enter_();
 
409
                
 
410
                push_(output);  
 
411
                THROW_CURL_IF(curl_easy_setopt(ms_curl, CURLOPT_HTTPHEADER, ms_header_list));
 
412
                THROW_CURL_IF(curl_easy_setopt(ms_curl, CURLOPT_HTTPGET, 1L));
 
413
 
 
414
                ms_outputStream = output;       
 
415
                if (curl_easy_perform(ms_curl) && !ms_throw_error) {
 
416
                        ms_outputStream = NULL; 
 
417
                        CSException::throwException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, ms_curl_error);
 
418
                }
 
419
                ms_outputStream = NULL; 
 
420
                release_(output);       
 
421
                
 
422
                if (ms_throw_error) 
 
423
                        throw_();
 
424
                
 
425
                check_reply_status();
 
426
                exit_();                
 
427
        }
 
428
        
 
429
        inline void ms_execute_put_request(CSInputStream *input, off_t size, Md5Digest *digest)
 
430
        {
 
431
                enter_();
 
432
                
 
433
                push_(input);   
 
434
                THROW_CURL_IF(curl_easy_setopt(ms_curl, CURLOPT_HTTPHEADER, ms_header_list));
 
435
                THROW_CURL_IF(curl_easy_setopt(ms_curl, CURLOPT_INFILESIZE_LARGE, size));
 
436
                THROW_CURL_IF(curl_easy_setopt(ms_curl, CURLOPT_UPLOAD, 1L));
 
437
                //THROW_CURL_IF(curl_easy_setopt(ms_curl, CURLOPT_POSTFIELDSIZE_LARGE, size));
 
438
                //THROW_CURL_IF(curl_easy_setopt(ms_curl, CURLOPT_POST, 1L));
 
439
 
 
440
                ms_md5.md5_init();
 
441
                
 
442
                ms_data_size = size;
 
443
                ms_inputStream = input; 
 
444
                if (curl_easy_perform(ms_curl) && !ms_throw_error) {
 
445
                        ms_inputStream = NULL;  
 
446
                        CSException::throwException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, ms_curl_error);
 
447
                }
 
448
                ms_inputStream = NULL;
 
449
                release_(input);        
 
450
 
 
451
                        
 
452
                if (ms_throw_error)
 
453
                        throw_();
 
454
                
 
455
                check_reply_status();
 
456
                
 
457
                // Check that the checksums agree
 
458
                char checksum[HEX_CHECKSUM_VALUE_SIZE +1];
 
459
                
 
460
                ms_md5.md5_digest(digest);
 
461
                cs_bin_to_hex(HEX_CHECKSUM_VALUE_SIZE, checksum, CHECKSUM_VALUE_SIZE, digest->val);
 
462
                checksum[HEX_CHECKSUM_VALUE_SIZE] = 0;
 
463
                
 
464
                cs_strToUpper(ms_s3Checksum);
 
465
                if (strcmp(checksum, ms_s3Checksum)) {
 
466
                        // The request should be restarted in this case.
 
467
                        ms_retry = true;
 
468
                        CSException::logException(CS_CONTEXT, CS_ERR_CHECKSUM_ERROR, "Calculated checksum did not match S3 checksum");
 
469
                }
 
470
                
 
471
                exit_();                
 
472
        }
 
473
        
 
474
};
 
475
 
 
476
//======================================
 
477
 
 
478
 
 
479
 
 
480
 
 
481
//======================================
 
482
CSS3Protocol::CSS3Protocol():
 
483
        con_data(NULL),
 
484
        s3_server(NULL),
 
485
        s3_public_key(NULL),
 
486
        s3_private_key(NULL),
 
487
        s3_maxRetrys(5)
 
488
{
 
489
        new_(s3_server, CSStringBuffer());
 
490
        s3_server->append("s3.amazonaws.com/");
 
491
 
 
492
        s3_public_key = CSString::newString("");
 
493
        s3_private_key = CSString::newString("");
 
494
        
 
495
}
 
496
 
 
497
//------------------
 
498
CSS3Protocol::~CSS3Protocol()
 
499
{
 
500
        if (s3_server)
 
501
                s3_server->release();
 
502
        
 
503
        if (s3_public_key)
 
504
                s3_public_key->release();
 
505
        
 
506
        if (s3_private_key)
 
507
                s3_private_key->release();
 
508
}
 
509
        
 
510
//------------------
 
511
CSString *CSS3Protocol::s3_getSignature(const char *verb, 
 
512
                                                                                const char *md5, 
 
513
                                                                                const char *content_type, 
 
514
                                                                                const char *date, 
 
515
                                                                                const char *bucket, 
 
516
                                                                                const char *key,
 
517
                                                                                CSString *headers 
 
518
                                                                        )
 
519
{
 
520
        CSStringBuffer *s3_buffer;
 
521
        enter_();
 
522
        if (headers)
 
523
                push_(headers);
 
524
        
 
525
        new_(s3_buffer, CSStringBuffer());
 
526
        push_(s3_buffer);
 
527
        
 
528
        s3_buffer->setLength(0);
 
529
        s3_buffer->append(verb);        
 
530
        s3_buffer->append("\n");        
 
531
        if (md5) s3_buffer->append(md5);        
 
532
        s3_buffer->append("\n");        
 
533
        if (content_type) s3_buffer->append(content_type);      
 
534
        s3_buffer->append("\n");        
 
535
        s3_buffer->append(date);
 
536
        if (headers) { 
 
537
                // Note: headers are assumed to be in lower case, sorted, and containing no white space.
 
538
                s3_buffer->append("\n");        
 
539
                s3_buffer->append(headers->getCString());
 
540
        }
 
541
        s3_buffer->append("\n/");
 
542
        s3_buffer->append(bucket);
 
543
        s3_buffer->append("/");
 
544
        s3_buffer->append(key);
 
545
        
 
546
#ifdef SHOW_SIGNING
 
547
printf("signing:\n=================\n%s\n=================\n",  s3_buffer->getCString());
 
548
if(0){
 
549
        const char *ptr = s3_buffer->getCString();
 
550
        while (*ptr) {
 
551
                printf("%x ", *ptr); ptr++;
 
552
        }
 
553
        printf("\n");
 
554
}
 
555
#endif
 
556
 
 
557
        CSString *sig = signature(s3_buffer->getCString(), s3_private_key->getCString());
 
558
        release_(s3_buffer);
 
559
        if (headers) 
 
560
                release_(headers);
 
561
 
 
562
        return_(sig);
 
563
}
 
564
//----------------------
 
565
// CURL callback functions:
 
566
////////////////////////////
 
567
//----------------------
 
568
static size_t send_callback(void *ptr, size_t objs, size_t obj_size, void *v_con)
 
569
{
 
570
        S3ProtocolCon *con = (S3ProtocolCon*) v_con;
 
571
        size_t data_sent, buffer_size = objs * obj_size;
 
572
 
 
573
        if (!con->ms_data_size)
 
574
                return 0;
 
575
                
 
576
        enter_();
 
577
        try_(a) {
 
578
                data_sent = con->ms_inputStream->read((char*)ptr, buffer_size);
 
579
                if (data_sent <= con->ms_data_size) {
 
580
                        con->ms_data_size -= data_sent;
 
581
                        if (data_sent)
 
582
                                con->ms_md5.md5_append((u_char*)ptr, data_sent); // Calculating the checksum for the data sent.
 
583
                } else if (data_sent > con->ms_data_size) 
 
584
                        CSException::RecordException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, "Blob larger than expected.");
 
585
                else if (con->ms_data_size && !data_sent)
 
586
                        CSException::RecordException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, "Blob smaller than expected.");
 
587
        }
 
588
        catch_(a);
 
589
        con->ms_throw_error = true;
 
590
        data_sent = -1;
 
591
        
 
592
        cont_(a);
 
593
        
 
594
        return data_sent;
 
595
}
 
596
//----------------------
 
597
static size_t receive_data(void *vptr, size_t objs, size_t obj_size, void *v_con)
 
598
{
 
599
        S3ProtocolCon *con = (S3ProtocolCon*) v_con;
 
600
        size_t data_len = objs * obj_size;
 
601
 
 
602
        enter_();
 
603
        try_(a) {
 
604
                if (con->ms_replyStatus >= 400) { // Collect the error reply.
 
605
                        if (!con->ms_errorReply)
 
606
                                con->ms_errorReply = new CSStringBuffer(50);            
 
607
                        con->ms_errorReply->append((char*)vptr, data_len);
 
608
                } else if (     con->ms_outputStream)
 
609
                        con->ms_outputStream->write((char*)vptr, data_len);
 
610
        }
 
611
        catch_(a);
 
612
        con->ms_throw_error = true;
 
613
        data_len = -1;
 
614
        
 
615
        cont_(a);
 
616
        return_(data_len);      
 
617
}
 
618
 
 
619
#define IS_REDIRECT(s) ((s >= 300) && (s < 400))
 
620
//----------------------
 
621
static size_t receive_header(void *header, size_t objs, size_t obj_size, void *v_con)
 
622
{
 
623
        S3ProtocolCon *con = (S3ProtocolCon*) v_con;
 
624
        size_t size = objs * obj_size;
 
625
        char *end, *ptr = (char*) header, *name, *value;
 
626
        u_int name_len, value_len;
 
627
//printf(       "receive_header: %s\n", ptr);
 
628
        end = ptr + size;
 
629
        if (*(end -2) == '\r' && *(end -1) == '\n')
 
630
                end -=2;
 
631
                
 
632
        while ((end != ptr) && (*ptr == ' ')) ptr++;
 
633
        if (end == ptr)
 
634
                return size;
 
635
        
 
636
        // Get the reply status.
 
637
        // Status 100 = Continue
 
638
        if (((!con->ms_replyStatus) || (con->ms_replyStatus == 100) || IS_REDIRECT(con->ms_replyStatus) ) 
 
639
                        && !strncasecmp(ptr, "HTTP", 4)
 
640
                ) {
 
641
                char status[4];
 
642
                while ((end != ptr) && (*ptr != ' ')) ptr++; // skip HTTP stuff
 
643
                while ((end != ptr) && (*ptr == ' ')) ptr++; // find the start of eh status code.
 
644
                if (end == ptr)
 
645
                        return size;
 
646
                        
 
647
                if (end < (ptr +3)) // expecting a 3 digit status code.
 
648
                        return size;
 
649
                        
 
650
                memcpy(status, ptr, 3);
 
651
                status[3] = 0;
 
652
                
 
653
                con->ms_replyStatus = atoi(status);
 
654
        }
 
655
        
 
656
        name = ptr;
 
657
        while ((end != ptr) && (*ptr != ':')) ptr++;
 
658
        if (end == ptr)
 
659
                return size;
 
660
        name_len = ptr - name;
 
661
        
 
662
        ptr++; 
 
663
        while ((end != ptr) && (*ptr == ' ')) ptr++;
 
664
        if (end == ptr)
 
665
                return size;
 
666
        
 
667
        value = ptr;
 
668
        value_len = end - value;
 
669
        
 
670
        while (name[name_len-1] == ' ') name_len--;
 
671
        while (value[value_len-1] == ' ') value_len--;
 
672
        
 
673
        if (!strncasecmp(name, "ETag", 4)) {
 
674
                value++; value_len -=2; // Strip quotation marks from checksum string.
 
675
                if (value_len == HEX_CHECKSUM_VALUE_SIZE) {
 
676
                        memcpy(con->ms_s3Checksum, value, value_len);
 
677
                        con->ms_s3Checksum[value_len] = 0;
 
678
                }
 
679
        }
 
680
        
 
681
        enter_();
 
682
        try_(a) {
 
683
                con->ms_reply_headers.addHeader(name, name_len, value, value_len);
 
684
        }
 
685
        
 
686
        catch_(a);
 
687
        con->ms_throw_error = true;
 
688
        return_(-1);
 
689
                
 
690
        cont_(a);
 
691
        return_(size);
 
692
}
 
693
 
 
694
//----------------------
 
695
 
 
696
#define SET_DATE_FROM_TIME(t, d) {strftime(d, sizeof(d), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&t));}
 
697
#define SET_DATE(d) {time_t t = time(NULL); SET_DATE_FROM_TIME(t, d);}
 
698
 
 
699
bool CSS3Protocol::s3_delete(const char *bucket, const char *key)
 
700
{
 
701
        CSStringBuffer *s3_buffer;
 
702
        char date[64];
 
703
        CSString *signed_str;
 
704
        uint32_t retry_count = 0;
 
705
        S3ProtocolCon *con_data;
 
706
 
 
707
        enter_();
 
708
 
 
709
        new_(s3_buffer, CSStringBuffer());
 
710
        push_(s3_buffer);
 
711
 
 
712
        new_(con_data, S3ProtocolCon(this));
 
713
        push_(con_data);
 
714
 
 
715
retry:
 
716
        // Clear old settings. 
 
717
        con_data->ms_reset();   
 
718
        
 
719
        SET_DATE(date);
 
720
 
 
721
        // Build the URL
 
722
        s3_buffer->setLength(0);
 
723
        s3_buffer->append("http://");
 
724
        s3_buffer->append(bucket);
 
725
        s3_buffer->append("."); 
 
726
        s3_buffer->append(s3_server->getCString());
 
727
        s3_buffer->append(key);
 
728
 
 
729
        con_data->ms_setURL(s3_buffer->getCString());
 
730
        
 
731
        // Add the 'DATE' header
 
732
        s3_buffer->setLength(0);
 
733
        s3_buffer->append("Date: ");    
 
734
        s3_buffer->append(date);
 
735
        con_data->ms_setHeader(s3_buffer->getCString());
 
736
 
 
737
        // Create the authentication signature and add the 'Authorization' header
 
738
        signed_str = s3_getSignature("DELETE", NULL, NULL, date, bucket, key);
 
739
        push_(signed_str);
 
740
        s3_buffer->setLength(0);
 
741
        s3_buffer->append("Authorization: AWS ");       
 
742
        s3_buffer->append(s3_public_key->getCString());
 
743
        s3_buffer->append(":"); 
 
744
        s3_buffer->append(signed_str->getCString());    
 
745
        release_(signed_str); signed_str = NULL;
 
746
        
 
747
        con_data->ms_setHeader(s3_buffer->getCString());
 
748
        
 
749
        con_data->ms_execute_delete_request();
 
750
        
 
751
        if (con_data->ms_retry) {
 
752
                if (retry_count == s3_maxRetrys) {
 
753
                        CSException::throwException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, "S3 operation aborted after max retries.");
 
754
                }
 
755
        printf("RETRY: s3_delete()\n");
 
756
                retry_count++;
 
757
                goto retry;
 
758
        }
 
759
        
 
760
        bool notFound = con_data->ms_notFound;
 
761
        release_(con_data);
 
762
        release_(s3_buffer);
 
763
        
 
764
        return_(!notFound);
 
765
}
 
766
 
 
767
//-------------------------------
 
768
void CSS3Protocol::s3_copy(const char *dest_server, const char *dest_bucket, const char *dest_key, const char *src_bucket, const char *src_key)
 
769
{
 
770
        CSStringBuffer *s3_buffer;
 
771
        char date[64];
 
772
        CSString *signed_str;
 
773
        uint32_t retry_count = 0;
 
774
        S3ProtocolCon *con_data;
 
775
 
 
776
        enter_();
 
777
 
 
778
        new_(s3_buffer, CSStringBuffer());
 
779
        push_(s3_buffer);
 
780
 
 
781
        new_(con_data, S3ProtocolCon(this));
 
782
        push_(con_data);
 
783
        
 
784
        if (!dest_server)
 
785
                dest_server = s3_server->getCString();
 
786
 
 
787
retry:
 
788
        // Clear old settings. 
 
789
        con_data->ms_reset();   
 
790
        
 
791
        SET_DATE(date);
 
792
 
 
793
        // Build the URL
 
794
        s3_buffer->setLength(0);
 
795
        s3_buffer->append("http://");
 
796
        s3_buffer->append(dest_bucket);
 
797
        s3_buffer->append("."); 
 
798
        s3_buffer->append(s3_server->getCString());
 
799
        s3_buffer->append(dest_key);
 
800
 
 
801
        con_data->ms_setURL(s3_buffer->getCString());
 
802
        
 
803
        // Add the destination location
 
804
        s3_buffer->setLength(0);
 
805
        s3_buffer->append("Host: ");    
 
806
        s3_buffer->append(dest_bucket);
 
807
        s3_buffer->append("."); 
 
808
        s3_buffer->append(dest_server);
 
809
        s3_buffer->setLength(s3_buffer->length() -1); // trim the '/'
 
810
        con_data->ms_setHeader(s3_buffer->getCString());
 
811
        
 
812
        // Add the source location
 
813
        s3_buffer->setLength(0);
 
814
        s3_buffer->append("x-amz-copy-source:");        
 
815
        s3_buffer->append(src_bucket);
 
816
        s3_buffer->append("/");
 
817
        s3_buffer->append(src_key);
 
818
        con_data->ms_setHeader(s3_buffer->getCString());
 
819
        
 
820
        // Create the authentication signature and add the 'Authorization' header
 
821
        signed_str = s3_getSignature("PUT", NULL, NULL, date, dest_bucket, dest_key, CSString::newString(s3_buffer->getCString()));
 
822
        push_(signed_str);
 
823
 
 
824
        // Add the 'DATE' header
 
825
        s3_buffer->setLength(0);
 
826
        s3_buffer->append("Date: ");    
 
827
        s3_buffer->append(date);
 
828
        con_data->ms_setHeader(s3_buffer->getCString());
 
829
 
 
830
        // Add the signature
 
831
        s3_buffer->setLength(0);
 
832
        s3_buffer->append("Authorization: AWS ");       
 
833
        s3_buffer->append(s3_public_key->getCString());
 
834
        s3_buffer->append(":"); 
 
835
        s3_buffer->append(signed_str->getCString());    
 
836
        release_(signed_str); signed_str = NULL;
 
837
        con_data->ms_setHeader(s3_buffer->getCString());
 
838
        
 
839
        con_data->ms_execute_copy_request();
 
840
        
 
841
        if (con_data->ms_notFound) {
 
842
                s3_buffer->setLength(0);
 
843
                s3_buffer->append("Cloud copy failed, object not found: ");
 
844
                s3_buffer->append(src_bucket);
 
845
                s3_buffer->append(" ");
 
846
                s3_buffer->append(src_key);
 
847
                CSException::throwException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, s3_buffer->getCString());
 
848
        }
 
849
        
 
850
        if (con_data->ms_retry) {
 
851
                if (retry_count == s3_maxRetrys) {
 
852
                        CSException::throwException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, "S3 operation aborted after max retries.");
 
853
                }
 
854
        printf("RETRY: s3_copy()\n");
 
855
                retry_count++;
 
856
                goto retry;
 
857
        }
 
858
        
 
859
        release_(con_data);
 
860
        release_(s3_buffer);
 
861
        
 
862
        exit_();
 
863
}
 
864
 
 
865
 
 
866
//-------------------------------
 
867
CSVector *CSS3Protocol::s3_receive(CSOutputStream *output, const char *bucket, const char *key, bool *found)
 
868
{
 
869
        CSStringBuffer *s3_buffer;
 
870
    char date[64];
 
871
        CSString *signed_str;
 
872
        uint32_t retry_count = 0;
 
873
        S3ProtocolCon *con_data;
 
874
        CSVector *replyHeaders;
 
875
 
 
876
        enter_();
 
877
 
 
878
        push_(output);
 
879
 
 
880
        new_(s3_buffer, CSStringBuffer());
 
881
        push_(s3_buffer);
 
882
 
 
883
        new_(con_data, S3ProtocolCon(this));
 
884
        push_(con_data);
 
885
 
 
886
retry:
 
887
        // Clear old settings. 
 
888
        con_data->ms_reset();   
 
889
        
 
890
        SET_DATE(date);
 
891
 
 
892
        // Build the URL
 
893
        s3_buffer->setLength(0);
 
894
        s3_buffer->append("http://");
 
895
        s3_buffer->append(bucket);
 
896
        s3_buffer->append("."); 
 
897
        s3_buffer->append(s3_server->getCString());
 
898
        s3_buffer->append(key);
 
899
 
 
900
        con_data->ms_setURL(s3_buffer->getCString());
 
901
        
 
902
        // Add the 'DATE' header
 
903
        s3_buffer->setLength(0);
 
904
        s3_buffer->append("Date: ");    
 
905
        s3_buffer->append(date);
 
906
        con_data->ms_setHeader(s3_buffer->getCString());
 
907
 
 
908
        // Create the authentication signature and add the 'Authorization' header
 
909
        signed_str = s3_getSignature("GET", NULL, NULL, date, bucket, key);
 
910
        push_(signed_str);
 
911
        s3_buffer->setLength(0);
 
912
        s3_buffer->append("Authorization: AWS ");       
 
913
        s3_buffer->append(s3_public_key->getCString());
 
914
        s3_buffer->append(":"); 
 
915
        s3_buffer->append(signed_str->getCString());    
 
916
        release_(signed_str); signed_str = NULL;
 
917
        con_data->ms_setHeader(s3_buffer->getCString());
 
918
        
 
919
        con_data->ms_execute_get_request(RETAIN(output));
 
920
        
 
921
        if (con_data->ms_retry) {
 
922
                if (retry_count == s3_maxRetrys) {
 
923
                        CSException::throwException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, "S3 operation aborted after max retries.");
 
924
                }
 
925
        printf("RETRY: s3_receive()\n");
 
926
                retry_count++;
 
927
                output->reset();
 
928
                goto retry;
 
929
        }
 
930
        
 
931
        *found = !con_data->ms_notFound;
 
932
        replyHeaders = con_data->ms_reply_headers.takeHeaders();
 
933
        release_(con_data);
 
934
        release_(s3_buffer);
 
935
        release_(output);
 
936
        
 
937
        return_(replyHeaders);
 
938
}
 
939
 
 
940
typedef struct {
 
941
        bool thow_error; // Must be the first field.
 
942
        CSVector *list;
 
943
        CSStringBuffer *buffer;
 
944
        bool isKey;
 
945
} ParserListInfoRec, *ParserListInfoPtr;
 
946
 
 
947
#ifndef NO_XML2 
 
948
//==========================
 
949
// XML parser callbacks:
 
950
 
 
951
 
 
952
//-------------------------------
 
953
static void saxStartListElement(void *user_data, const xmlChar *name, const xmlChar **)
 
954
{
 
955
         ParserListInfoPtr info = (ParserListInfoPtr) user_data;
 
956
 
 
957
        if (!strcmp("Key", (char *) name)) {
 
958
                if (info->isKey) {
 
959
                        enter_();
 
960
                        CSException::RecordException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, "Unexpected <Key> tag");
 
961
                        info->thow_error = true;
 
962
                        outer_();
 
963
                        return;
 
964
                }
 
965
                info->isKey = true;
 
966
        }
 
967
}
 
968
 
 
969
 
 
970
//-------------------------------
 
971
static void saxEndListElement(void *user_data, const xmlChar *name)
 
972
{
 
973
        ParserListInfoPtr info = (ParserListInfoPtr) user_data;
 
974
 
 
975
        if (info->thow_error)
 
976
        return;
 
977
 
 
978
        if (!strcmp("Key", (char *) name)) {
 
979
                enter_();
 
980
                try_(a) {
 
981
                        if (!info->isKey) 
 
982
                                CSException::throwException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, "Unexpected </Key> tag");
 
983
 
 
984
//printf("%s\n", info->buffer->getCString());
 
985
                        info->isKey = false;
 
986
                        info->list->add(CSString::newString(info->buffer->getCString()));
 
987
                        info->buffer->setLength(0);
 
988
                }
 
989
                catch_(a);
 
990
                info->thow_error = true;
 
991
                cont_(a);
 
992
                outer_();
 
993
        }
 
994
}
 
995
 
 
996
 
 
997
//-------------------------------
 
998
static void saxListData(void *user_data, const xmlChar *ch, int len)
 
999
{
 
1000
        ParserListInfoPtr info = (ParserListInfoPtr) user_data;
 
1001
 
 
1002
        if (info->thow_error || !info->isKey) 
 
1003
                return;
 
1004
 
 
1005
        info->buffer->append( (char*) ch, len);
 
1006
 
 
1007
}
 
1008
 
 
1009
 
 
1010
//-------------------------------
 
1011
static CSVector *parse_s3_list(CSMemoryOutputStream *output)
 
1012
{
 
1013
        ParserListInfoRec parser_info = {0};
 
1014
        struct _xmlSAXHandler sax_handler = {0};
 
1015
        const char *data;
 
1016
        size_t len;
 
1017
        
 
1018
        enter_();
 
1019
        
 
1020
        push_(output);
 
1021
        data = (const char *) output->getMemory(&len);
 
1022
        
 
1023
        sax_handler.startElement = &saxStartListElement;
 
1024
        sax_handler.endElement = &saxEndListElement;
 
1025
        sax_handler.characters = &saxListData;
 
1026
        sax_handler.cdataBlock = &saxListData;
 
1027
        sax_handler.error = &saxError;
 
1028
        sax_handler.fatalError = &saxError;
 
1029
        
 
1030
        new_(parser_info.list, CSVector(10));
 
1031
        push_(parser_info.list);
 
1032
        
 
1033
        new_(parser_info.buffer, CSStringBuffer());
 
1034
        push_(parser_info.buffer);
 
1035
        
 
1036
        if (xmlSAXUserParseMemory(&sax_handler, &parser_info, data, len)) {
 
1037
                if (!parser_info.thow_error)
 
1038
                        CSException::throwException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, "xml SAX parser error");
 
1039
        }
 
1040
        
 
1041
        if (parser_info.thow_error)
 
1042
                throw_();
 
1043
                
 
1044
        
 
1045
        release_(parser_info.buffer);
 
1046
        pop_(parser_info.list);
 
1047
        release_(output);
 
1048
        return_(parser_info.list);
 
1049
}
 
1050
 
 
1051
//-------------------------------
 
1052
CSVector *CSS3Protocol::s3_list(const char *bucket, const char *key_prefix, uint32_t max)
 
1053
{
 
1054
        CSStringBuffer *s3_buffer;
 
1055
    char date[64];
 
1056
        CSString *signed_str;
 
1057
        CSMemoryOutputStream *output;
 
1058
        uint32_t retry_count = 0;
 
1059
        S3ProtocolCon *con_data;
 
1060
 
 
1061
        enter_();
 
1062
 
 
1063
        new_(s3_buffer, CSStringBuffer());
 
1064
        push_(s3_buffer);
 
1065
 
 
1066
        output = CSMemoryOutputStream::newStream(1024, 1024);
 
1067
        push_(output);
 
1068
        
 
1069
        new_(con_data, S3ProtocolCon(this));
 
1070
        push_(con_data);
 
1071
 
 
1072
retry:
 
1073
 
 
1074
        // Clear old settings. 
 
1075
        con_data->ms_reset();   
 
1076
        
 
1077
        SET_DATE(date);
 
1078
 
 
1079
        // Build the URL
 
1080
        s3_buffer->setLength(0);
 
1081
        s3_buffer->append("http://");
 
1082
        s3_buffer->append(bucket);
 
1083
        s3_buffer->append("."); 
 
1084
        s3_buffer->append(s3_server->getCString());
 
1085
        if (key_prefix) {
 
1086
                s3_buffer->append("?prefix=");
 
1087
                s3_buffer->append(key_prefix);
 
1088
        }
 
1089
        
 
1090
        if (max) {
 
1091
                if (key_prefix)
 
1092
                        s3_buffer->append("&max-keys=");
 
1093
                else
 
1094
                        s3_buffer->append("?max-keys=");
 
1095
                s3_buffer->append(max);
 
1096
        }
 
1097
 
 
1098
        con_data->ms_setURL(s3_buffer->getCString());
 
1099
        
 
1100
        // Add the 'DATE' header
 
1101
        s3_buffer->setLength(0);
 
1102
        s3_buffer->append("Date: ");    
 
1103
        s3_buffer->append(date);
 
1104
        con_data->ms_setHeader(s3_buffer->getCString());
 
1105
 
 
1106
        // Create the authentication signature and add the 'Authorization' header
 
1107
        signed_str = s3_getSignature("GET", NULL, NULL, date, bucket, "");
 
1108
        push_(signed_str);
 
1109
        s3_buffer->setLength(0);
 
1110
        s3_buffer->append("Authorization: AWS ");       
 
1111
        s3_buffer->append(s3_public_key->getCString());
 
1112
        s3_buffer->append(":"); 
 
1113
        s3_buffer->append(signed_str->getCString());    
 
1114
        release_(signed_str); signed_str = NULL;
 
1115
        con_data->ms_setHeader(s3_buffer->getCString());
 
1116
        
 
1117
        con_data->ms_execute_get_request(RETAIN(output));
 
1118
        
 
1119
        if (con_data->ms_retry) {
 
1120
                if (retry_count == s3_maxRetrys) {
 
1121
                        CSException::throwException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, "S3 operation aborted after max retries.");
 
1122
                }
 
1123
        printf("RETRY: s3_list()\n");
 
1124
                retry_count++;
 
1125
                output->reset();
 
1126
                goto retry;
 
1127
        }
 
1128
        
 
1129
        release_(con_data);
 
1130
        pop_(output);
 
1131
        release_(s3_buffer);
 
1132
        return_(parse_s3_list(output));
 
1133
}
 
1134
#endif //       NO_XML2 
 
1135
 
 
1136
//-------------------------------
 
1137
CSString *CSS3Protocol::s3_getAuthorization(const char *bucket, const char *key, const char *content_type, uint32_t *s3AuthorizationTime)
 
1138
{
 
1139
    char date[64];
 
1140
        CSString *signed_str;
 
1141
        time_t sys_time;
 
1142
 
 
1143
        enter_();
 
1144
 
 
1145
        if (!content_type)
 
1146
                content_type = "binary/octet-stream";
 
1147
                
 
1148
        sys_time = time(NULL);
 
1149
        
 
1150
        *s3AuthorizationTime = (uint32_t)sys_time;
 
1151
        
 
1152
        SET_DATE_FROM_TIME(sys_time, date);
 
1153
        signed_str = s3_getSignature("PUT", NULL, content_type, date, bucket, key);
 
1154
        return_(signed_str);
 
1155
}
 
1156
 
 
1157
//-------------------------------
 
1158
CSVector *CSS3Protocol::s3_send(CSInputStream *input, const char *bucket, const char *key, off_t size, const char *content_type, Md5Digest *digest, const char *s3Authorization, time_t s3AuthorizationTime)
 
1159
{
 
1160
        CSStringBuffer *s3_buffer;
 
1161
    char date[64];
 
1162
        CSString *signed_str;
 
1163
        Md5Digest dummy_digest;
 
1164
        uint32_t retry_count = 0;
 
1165
        S3ProtocolCon *con_data;
 
1166
        CSVector *replyHeaders;
 
1167
 
 
1168
        enter_();
 
1169
        push_(input);
 
1170
 
 
1171
        new_(s3_buffer, CSStringBuffer());
 
1172
        push_(s3_buffer);
 
1173
 
 
1174
        new_(con_data, S3ProtocolCon(this));
 
1175
        push_(con_data);
 
1176
 
 
1177
        if (!digest)
 
1178
                digest = &dummy_digest;
 
1179
                
 
1180
        if (!content_type)
 
1181
                content_type = "binary/octet-stream";
 
1182
                
 
1183
retry:
 
1184
 
 
1185
        // Clear old settings. 
 
1186
        con_data->ms_reset();   
 
1187
        
 
1188
        if (s3Authorization) {
 
1189
                SET_DATE_FROM_TIME(s3AuthorizationTime, date);
 
1190
        } else {
 
1191
                SET_DATE(date);
 
1192
        }
 
1193
        
 
1194
        // Build the URL
 
1195
        s3_buffer->setLength(0);
 
1196
        s3_buffer->append("http://");
 
1197
        s3_buffer->append(bucket);
 
1198
        s3_buffer->append("."); 
 
1199
        s3_buffer->append(s3_server->getCString());
 
1200
        s3_buffer->append(key);
 
1201
 
 
1202
        con_data->ms_setURL(s3_buffer->getCString());
 
1203
        
 
1204
        // Add the 'DATE' header
 
1205
        s3_buffer->setLength(0);
 
1206
        s3_buffer->append("Date: ");    
 
1207
        s3_buffer->append(date);
 
1208
        con_data->ms_setHeader(s3_buffer->getCString());
 
1209
        
 
1210
        // Add the 'Content-Type' header
 
1211
        s3_buffer->setLength(0);
 
1212
        s3_buffer->append("Content-Type: ");    
 
1213
        s3_buffer->append(content_type);
 
1214
        con_data->ms_setHeader(s3_buffer->getCString());
 
1215
                
 
1216
        // Create the authentication signature and add the 'Authorization' header
 
1217
        if (!s3Authorization)
 
1218
                signed_str = s3_getSignature("PUT", NULL, content_type, date, bucket, key);
 
1219
        else
 
1220
                signed_str = CSString::newString(s3Authorization);
 
1221
        push_(signed_str);
 
1222
        s3_buffer->setLength(0);
 
1223
        s3_buffer->append("Authorization: AWS ");       
 
1224
        s3_buffer->append(s3_public_key->getCString());
 
1225
        s3_buffer->append(":"); 
 
1226
        s3_buffer->append(signed_str->getCString());    
 
1227
        release_(signed_str); signed_str = NULL;
 
1228
        con_data->ms_setHeader(s3_buffer->getCString());
 
1229
        
 
1230
        con_data->ms_execute_put_request(RETAIN(input), size, digest);
 
1231
        
 
1232
        if (con_data->ms_retry) {
 
1233
                if (retry_count == s3_maxRetrys) {
 
1234
                        CSException::throwException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, "S3 operation aborted after max retries.");
 
1235
                }
 
1236
        printf("RETRY: s3_send()\n");
 
1237
                retry_count++;
 
1238
                input->reset();
 
1239
                goto retry;
 
1240
        }
 
1241
        
 
1242
        replyHeaders = con_data->ms_reply_headers.takeHeaders();
 
1243
        release_(con_data);
 
1244
        release_(s3_buffer);
 
1245
        release_(input);
 
1246
        return_(replyHeaders);
 
1247
}
 
1248
 
 
1249
//-------------------------------
 
1250
CSString *CSS3Protocol::s3_getDataURL(const char *bucket, const char *key, uint32_t keep_alive)
 
1251
{
 
1252
        CSStringBuffer *s3_buffer;
 
1253
        char timeout[32];
 
1254
        CSString *signed_str;
 
1255
        enter_();
 
1256
        
 
1257
        new_(s3_buffer, CSStringBuffer());
 
1258
        push_(s3_buffer);
 
1259
 
 
1260
        snprintf(timeout, 32, "%"PRId32"", ((uint32_t)time(NULL)) + keep_alive);
 
1261
        
 
1262
        signed_str = s3_getSignature("GET", NULL, NULL, timeout, bucket, key);
 
1263
//printf("Unsafe: \"%s\"\n", signed_str->getCString());
 
1264
        signed_str = urlEncode(signed_str); // Because the signature is in the URL it must be URL encoded.
 
1265
//printf("  Safe: \"%s\"\n", signed_str->getCString());
 
1266
        push_(signed_str);
 
1267
        
 
1268
        s3_buffer->setLength(0);        
 
1269
        s3_buffer->append("http://");
 
1270
        s3_buffer->append(bucket);
 
1271
        s3_buffer->append("."); 
 
1272
        s3_buffer->append(s3_server->getCString());
 
1273
        s3_buffer->append(key);
 
1274
 
 
1275
        s3_buffer->append("?AWSAccessKeyId=");
 
1276
        s3_buffer->append(s3_public_key->getCString());
 
1277
        s3_buffer->append("&Expires=");
 
1278
        s3_buffer->append(timeout);
 
1279
        s3_buffer->append("&Signature=");
 
1280
        s3_buffer->append(signed_str->getCString());
 
1281
        
 
1282
        release_(signed_str);
 
1283
        
 
1284
        pop_(s3_buffer);
 
1285
        CSString *str = CSString::newString(s3_buffer);
 
1286
        return_(str);   
 
1287
}
 
1288
 
 
1289
//#define S3_UNIT_TEST
 
1290
#ifdef S3_UNIT_TEST
 
1291
static void show_help_info(const char *cmd)
 
1292
{
 
1293
        printf("Get authenticated query string:\n\t%s q <bucket> <object_key> <timeout>\n", cmd);
 
1294
        printf("Delete object:\n\t%s d <bucket> <object_key>\n", cmd);
 
1295
        printf("Delete all object with a given prefix:\n\t%s D <bucket> <object_prefix>\n", cmd);
 
1296
        printf("Get object, data will be written to 'prottest.out':\n\t%s g <bucket> <object_key> <timeout>\n", cmd);
 
1297
        printf("Put (Upload) an object:\n\t%s p <bucket> <object_key> <file>\n", cmd);
 
1298
        printf("List objects in the bucket:\n\t%s l <bucket> [<object_prefix> [max_list_size]]\n", cmd);
 
1299
        printf("Copy object:\n\t%s c <src_bucket> <src_object_key> <dst_bucket> <dst_object_key> \n", cmd);
 
1300
        printf("Copy all object with a given prefix:\n\t%s C <src_bucket> <object_key_prefix> <dst_bucket> \n", cmd);
 
1301
}
 
1302
 
 
1303
void dump_headers(CSVector *header_array)
 
1304
{
 
1305
        CSHTTPHeaders headers;
 
1306
        
 
1307
        headers.setHeaders(header_array);
 
1308
        printf("Reply Headers:\n");
 
1309
        printf("--------------\n");
 
1310
        
 
1311
        for (u_int i = 0; i < headers.numHeaders(); i++) {
 
1312
                CSHeader *h = headers.getHeader(i);
 
1313
                
 
1314
                printf("%s : %s\n", h->getNameCString(), h->getValueCString());
 
1315
                h->release();
 
1316
        }
 
1317
        printf("--------------\n");
 
1318
        headers.clearHeaders();
 
1319
}
 
1320
 
 
1321
int main(int argc, char **argv)
 
1322
{
 
1323
        CSThread *main_thread;
 
1324
        const char *pub_key;
 
1325
        const char *priv_key;
 
1326
        CSS3Protocol *prot = NULL;
 
1327
        
 
1328
        if (argc < 3) {
 
1329
                show_help_info(argv[0]);
 
1330
                return 0;
 
1331
        }
 
1332
        
 
1333
        if (! CSThread::startUp()) {
 
1334
                CSException::throwException(CS_CONTEXT, ENOMEM, "CSThread::startUp() failed.");
 
1335
                return 1;
 
1336
        }
 
1337
        
 
1338
        cs_init_memory();
 
1339
        
 
1340
        main_thread = new CSThread( NULL);
 
1341
        CSThread::setSelf(main_thread);
 
1342
        
 
1343
        enter_();
 
1344
        try_(a) {
 
1345
        
 
1346
                pub_key = getenv("S3_ACCESS_KEY_ID");
 
1347
                priv_key = getenv("S3_SECRET_ACCESS_KEY");
 
1348
                new_(prot, CSS3Protocol());
 
1349
                push_(prot);
 
1350
                
 
1351
                prot->s3_setServer("s3.amazonaws.com/");
 
1352
                prot->s3_setPublicKey(pub_key);
 
1353
                prot->s3_setPrivateKey(priv_key);
 
1354
                
 
1355
                switch (argv[1][0]) {
 
1356
                        case 'q': // Get the query string
 
1357
                                if (argc == 5) {
 
1358
                                        CSString *qstr = prot->s3_getDataURL(argv[2], argv[3], atoi(argv[4]));
 
1359
                                        printf("To test call:\ncurl -L -D - \"%s\"\n", qstr->getCString());
 
1360
                                        qstr->release();
 
1361
                                } else
 
1362
                                        printf("Bad command: q <bucket> <object_key> <timeout>\n");
 
1363
                                
 
1364
                                break;
 
1365
                        case 'd': // Delete the object
 
1366
                                if (argc == 4) {
 
1367
                                        printf("delete %s %s\n", argv[2], argv[3]);
 
1368
                                        if (!prot->s3_delete(argv[2], argv[3]))
 
1369
                                                printf("%s/%s could not be found.\n", argv[2], argv[3]);
 
1370
 
 
1371
                                } else
 
1372
                                        printf("Bad command: d <bucket> <object_key>\n");
 
1373
                                
 
1374
                                break;
 
1375
                        case 'D': // Delete  objects like
 
1376
                                if (argc == 4) {
 
1377
                                        CSVector *list;
 
1378
                                        CSString *key;
 
1379
                                        
 
1380
                                        list = prot->s3_list(argv[2], argv[3]);
 
1381
                                        push_(list);
 
1382
                                        while (key = (CSString*) list->take(0)) {
 
1383
                                                printf("Deleting %s\n", key->getCString());
 
1384
                                                prot->s3_delete(argv[2], key->getCString());
 
1385
                                                key->release();
 
1386
                                        }
 
1387
                                        release_(list);
 
1388
                                        
 
1389
                                } else
 
1390
                                        printf("Bad command: D <bucket> <object_key_prefix>\n");
 
1391
                                
 
1392
                                break;
 
1393
                        case 'g':  // Get the object
 
1394
                                if (argc == 4) {
 
1395
                                        CSFile *output; 
 
1396
                                        CSVector *headers;
 
1397
                                        bool found;                             
 
1398
                                        
 
1399
                                        output = CSFile::newFile("prottest.out");
 
1400
                                        push_(output);
 
1401
                                        output->open(CSFile::CREATE | CSFile::TRUNCATE);
 
1402
                                        headers = prot->s3_receive(output->getOutputStream(), argv[2], argv[3], &found);
 
1403
                                        if (!found)
 
1404
                                                printf("%s/%s could not be found.\n", argv[2], argv[3]);
 
1405
                                                
 
1406
                                        dump_headers(headers);
 
1407
                                                
 
1408
                                        release_(output);
 
1409
                                } else
 
1410
                                        printf("Bad command: g <bucket> <object_key>\n");
 
1411
                                
 
1412
                                break;
 
1413
                                
 
1414
                        case 'p':  // Put (Upload) the object
 
1415
                                if (argc == 5) {
 
1416
                                        CSFile *input;
 
1417
                                        CSVector *headers;
 
1418
                                        
 
1419
                                        input = CSFile::newFile(argv[4]);
 
1420
                                        push_(input);
 
1421
                                        input->open(CSFile::READONLY);
 
1422
                                        headers = prot->s3_send(input->getInputStream(), argv[2], argv[3], input->myFilePath->getSize());
 
1423
                                        dump_headers(headers);
 
1424
                                        release_(input);
 
1425
                                } else
 
1426
                                        printf("Bad command: p <bucket> <object_key> <file> \n");
 
1427
                                
 
1428
                                break;
 
1429
                                
 
1430
                        case 'c':  // Copy the object
 
1431
                                if (argc == 6) {
 
1432
                                        prot->s3_copy(NULL, argv[4], argv[5], argv[2], argv[3]);
 
1433
                                } else
 
1434
                                        printf("Bad command: c <src_bucket> <src_object_key> <dst_bucket> <dst_object_key>\n");
 
1435
                                
 
1436
                                break;
 
1437
                                
 
1438
                        case 'C':  // Copy  objects like
 
1439
                                if (argc == 5) {
 
1440
                                        CSVector *list;
 
1441
                                        CSString *key;
 
1442
                                        
 
1443
                                        list = prot->s3_list(argv[2], argv[3]);
 
1444
                                        push_(list);
 
1445
                                        while (key = (CSString*) list->take(0)) {
 
1446
                                                printf("Copying %s\n", key->getCString());
 
1447
                                                prot->s3_copy(NULL, argv[4], key->getCString(), argv[2], key->getCString());
 
1448
                                                key->release();
 
1449
                                        }
 
1450
                                        release_(list);
 
1451
                                        
 
1452
                                } else
 
1453
                                        printf("Bad command: C <src_bucket> <object_key_prefix> <dst_bucket>\n");
 
1454
                                
 
1455
                                break;
 
1456
                        case 'l':  // List the object
 
1457
                                if ((argc == 3) || (argc == 4) || (argc == 5)) {
 
1458
                                        uint32_t max = 0;
 
1459
                                        char *prefix = NULL;
 
1460
                                        CSVector *list;
 
1461
                                        CSString *key;
 
1462
                                        
 
1463
                                        if (argc > 3) {
 
1464
                                                prefix = argv[3];
 
1465
                                                if (!strlen(prefix))
 
1466
                                                        prefix = NULL;
 
1467
                                        }
 
1468
                                        
 
1469
                                        if (argc == 5) 
 
1470
                                                max = atol(argv[4]);
 
1471
                                                
 
1472
                                        list = prot->s3_list(argv[2], prefix, max);
 
1473
                                        push_(list);
 
1474
                                        while (key = (CSString*) list->take(0)) {
 
1475
                                                printf("%s\n", key->getCString());
 
1476
                                                key->release();
 
1477
                                        }
 
1478
                                        release_(list);
 
1479
                                        
 
1480
                                } else
 
1481
                                        printf("Bad command: l <bucket> [<object_prefix> [max_list_size]] \n");
 
1482
                                
 
1483
                                break;
 
1484
                        default:
 
1485
                                printf("Unknown command.\n");
 
1486
                                show_help_info(argv[0]);
 
1487
                }
 
1488
                
 
1489
                release_(prot);
 
1490
        }
 
1491
        
 
1492
        catch_(a);              
 
1493
        self->logException();
 
1494
        
 
1495
        cont_(a);
 
1496
                
 
1497
        outer_()
 
1498
        main_thread->release();
 
1499
        cs_exit_memory();
 
1500
        CSThread::shutDown();
 
1501
        return 0;
 
1502
}
 
1503
 
 
1504
#endif
 
1505
 
 
1506