Browse code

* s3cmd, S3/Config.py, S3/S3.py: Added --add-header option. * NEWS: Documented --add-header.

git-svn-id: https://s3tools.svn.sourceforge.net/svnroot/s3tools/s3cmd/trunk@374 830e0280-6d2a-0410-9c65-932aecc39d9d

Michal Ludvig authored on 2009/02/14 16:01:41
Showing 5 changed files
... ...
@@ -1,5 +1,7 @@
1 1
 2009-02-14  Michal Ludvig  <michal@logix.cz>
2 2
 
3
+	* s3cmd, S3/Config.py, S3/S3.py: Added --add-header option.
4
+	* NEWS: Documented --add-header.
3 5
 	* run-tests.py: Fixed for new messages.
4 6
 
5 7
 2009-02-14  Michal Ludvig  <michal@logix.cz>
... ...
@@ -1,5 +1,6 @@
1 1
 s3cmd 0.9.9
2 2
 ===========
3
+* Added --add-header option for [put], [sync], [cp] and [mv].
3 4
 * Added --list-md5 option for [ls].
4 5
 * Always send Content-Length header to satisfy some http proxies.
5 6
 
... ...
@@ -7,6 +7,7 @@ import logging
7 7
 from logging import debug, info, warning, error
8 8
 import re
9 9
 import Progress
10
+from SortedDict import SortedDict
10 11
 
11 12
 class Config(object):
12 13
 	_instance = None
... ...
@@ -26,6 +27,7 @@ class Config(object):
26 26
 	recv_chunk = 4096
27 27
 	list_md5 = False
28 28
 	human_readable_sizes = False
29
+	extra_headers = SortedDict()
29 30
 	force = False
30 31
 	get_continue = False
31 32
 	skip_existing = False
... ...
@@ -326,6 +326,8 @@ class S3(object):
326 326
 		if not headers:
327 327
 			headers = SortedDict()
328 328
 
329
+		debug("headers: %s" % headers)
330
+
329 331
 		if headers.has_key("date"):
330 332
 			if not headers.has_key("x-amz-date"):
331 333
 				headers["x-amz-date"] = headers["date"]
... ...
@@ -320,7 +320,7 @@ def cmd_object_put(args):
320 320
 
321 321
 		uri_final = S3Uri(local_list[key]['remote_uri'])
322 322
 
323
-		extra_headers = {}
323
+		extra_headers = copy(cfg.extra_headers)
324 324
 		full_name_orig = local_list[key]['full_name']
325 325
 		full_name = full_name_orig
326 326
 		seq_label = "[%d of %d]" % (seq, local_count)
... ...
@@ -518,8 +518,9 @@ def subcmd_cp_mv(args, process_fce, message):
518 518
 
519 519
 	if dst_uri.object() == "":
520 520
 		dst_uri = S3Uri(dst_uri.uri() + src_uri.object())
521
- 
522
-	response = process_fce(src_uri, dst_uri) 
521
+
522
+	extra_headers = copy(cfg.extra_headers)
523
+	response = process_fce(src_uri, dst_uri, extra_headers) 
523 524
 	output(message % { "src" : src_uri, "dst" : dst_uri})
524 525
 	if Config().acl_public:
525 526
 		output(u"Public URL is: %s" % dst_uri.public_url())
... ...
@@ -979,12 +980,13 @@ def cmd_sync_local2remote(args):
979 979
 		src = item['full_name']
980 980
 		uri = S3Uri(item['remote_uri'])
981 981
 		seq_label = "[%d of %d]" % (seq, local_count)
982
-		attr_header = None
982
+		extra_headers = copy(cfg.extra_headers)
983 983
 		if cfg.preserve_attrs:
984 984
 			attr_header = _build_attr_header(src)
985
-			debug(attr_header)
985
+			debug(u"attr_header: %s" % attr_header)
986
+			extra_headers.update(attr_header)
986 987
 		try:
987
-			response = s3.object_put(src, uri, attr_header, extra_label = seq_label)
988
+			response = s3.object_put(src, uri, extra_headers, extra_label = seq_label)
988 989
 		except S3UploadError, e:
989 990
 			error(u"%s: upload failed too many times. Skipping that file." % item['full_name_unicode'])
990 991
 			continue
... ...
@@ -1375,6 +1377,8 @@ def main():
1375 1375
 	optparser.add_option("-m", "--mime-type", dest="default_mime_type", type="mimetype", metavar="MIME/TYPE", help="Default MIME-type to be set for objects stored.")
1376 1376
 	optparser.add_option("-M", "--guess-mime-type", dest="guess_mime_type", action="store_true", help="Guess MIME-type of files by their extension. Falls back to default MIME-Type as specified by --mime-type option")
1377 1377
 
1378
+	optparser.add_option(      "--add-header", dest="add_header", action="append", metavar="NAME:VALUE", help="Add a given HTTP header to the upload request. Can be used multiple times. (only for [put] and [sync] commands).")
1379
+
1378 1380
 	optparser.add_option(      "--encoding", dest="encoding", metavar="ENCODING", help="Override autodetected terminal and filesystem encoding (character set). Autodetected: %s" % preferred_encoding)
1379 1381
 
1380 1382
 	optparser.add_option(      "--list-md5", dest="list_md5", action="store_true", help="Include MD5 sums in bucket listings (only for 'ls' command).")
... ...
@@ -1446,6 +1450,21 @@ def main():
1446 1446
 			error(u"Option --progress is not yet supported on MS Windows platform. Assuming --no-progress.")
1447 1447
 			cfg.progress_meter = False
1448 1448
 
1449
+	## Pre-process --add-header's and put them to Config.extra_headers SortedDict()
1450
+	if options.add_header:
1451
+		for hdr in options.add_header:
1452
+			try:
1453
+				key, val = hdr.split(":", 1)
1454
+			except ValueError:
1455
+				raise ParameterError("Invalid header format: %s" % hdr)
1456
+			key_inval = re.sub("[a-zA-Z0-9-.]", "", key)
1457
+			if key_inval:
1458
+				key_inval = key_inval.replace(" ", "<space>")
1459
+				key_inval = key_inval.replace("\t", "<tab>")
1460
+				raise ParameterError("Invalid character(s) in header name '%s': \"%s\"" % (key, key_inval))
1461
+			debug(u"Updating Config.Config extra_headers[%s] -> %s" % (key.strip(), val.strip()))
1462
+			cfg.extra_headers[key.strip()] = val.strip()
1463
+
1449 1464
 	## Update Config with other parameters
1450 1465
 	for option in cfg.option_list():
1451 1466
 		try:
... ...
@@ -1530,9 +1549,6 @@ def main():
1530 1530
 	except S3Error, e:
1531 1531
 		error(u"S3 error: %s" % e)
1532 1532
 		sys.exit(1)
1533
-	except ParameterError, e:
1534
-		error(u"Parameter problem: %s" % e)
1535
-		sys.exit(1)
1536 1533
 
1537 1534
 if __name__ == '__main__':
1538 1535
 	try:
... ...
@@ -1552,6 +1568,10 @@ if __name__ == '__main__':
1552 1552
 		main()
1553 1553
 		sys.exit(0)
1554 1554
 
1555
+	except ParameterError, e:
1556
+		error(u"Parameter problem: %s" % e)
1557
+		sys.exit(1)
1558
+
1555 1559
 	except SystemExit, e:
1556 1560
 		sys.exit(e.code)
1557 1561