~drizzle-trunk/drizzle/development

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