git-svn-id: https://s3tools.svn.sourceforge.net/svnroot/s3tools/s3cmd/branches/0.9.8.x@236 830e0280-6d2a-0410-9c65-932aecc39d9d
Michal Ludvig authored on 2008/09/15 13:29:47... | ... |
@@ -1,5 +1,19 @@ |
1 | 1 |
2008-09-15 Michal Ludvig <michal@logix.cz> |
2 | 2 |
|
3 |
+ * Merge from trunk, revisions 218, 226, 233, 234: |
|
4 |
+ * s3cmd, S3/S3.py, S3/Config.py: Allow access to upper-case |
|
5 |
+ named buckets again with --use-old-connect-method |
|
6 |
+ (uses http://s3.amazonaws.com/bucket/object instead of |
|
7 |
+ http://bucket.s3.amazonaws.com/object) |
|
8 |
+ * s3cmd, S3/S3.py, S3/Config.py: Removed --use-old-connect-method |
|
9 |
+ again. Autodetect the need for old connect method instead. |
|
10 |
+ * S3/S3.py: "s3cmd mb" can create upper-case buckets again |
|
11 |
+ in US. Non-US (e.g. EU) bucket names must conform to strict |
|
12 |
+ DNS-rules. |
|
13 |
+ * S3/S3Uri.py: Display public URLs correctly for non-DNS buckets. |
|
14 |
+ |
|
15 |
+2008-09-15 Michal Ludvig <michal@logix.cz> |
|
16 |
+ |
|
3 | 17 |
* Merge from trunk, revisions 227, 230: |
4 | 18 |
|
5 | 19 |
* r227: s3cmd: Rework UTF-8 output to keep sys.stdout untouched |
... | ... |
@@ -71,7 +71,7 @@ class S3(object): |
71 | 71 |
return httplib.HTTPConnection(self.get_hostname(bucket)) |
72 | 72 |
|
73 | 73 |
def get_hostname(self, bucket): |
74 |
- if bucket: |
|
74 |
+ if bucket and self.check_bucket_name_dns_conformity(bucket): |
|
75 | 75 |
if self.redir_map.has_key(bucket): |
76 | 76 |
host = self.redir_map[bucket] |
77 | 77 |
else: |
... | ... |
@@ -85,10 +85,12 @@ class S3(object): |
85 | 85 |
self.redir_map[bucket] = redir_hostname |
86 | 86 |
|
87 | 87 |
def format_uri(self, resource): |
88 |
- if self.config.proxy_host != "": |
|
89 |
- uri = "http://%s%s" % (self.get_hostname(resource['bucket']), resource['uri']) |
|
88 |
+ if resource['bucket'] and not self.check_bucket_name_dns_conformity(resource['bucket']): |
|
89 |
+ uri = "/%s%s" % (resource['bucket'], resource['uri']) |
|
90 | 90 |
else: |
91 | 91 |
uri = resource['uri'] |
92 |
+ if self.config.proxy_host != "": |
|
93 |
+ uri = "http://%s%s" % (self.get_hostname(resource['bucket']), uri) |
|
92 | 94 |
debug('format_uri(): ' + uri) |
93 | 95 |
return uri |
94 | 96 |
|
... | ... |
@@ -124,7 +126,6 @@ class S3(object): |
124 | 124 |
return response |
125 | 125 |
|
126 | 126 |
def bucket_create(self, bucket, bucket_location = None): |
127 |
- self.check_bucket_name(bucket) |
|
128 | 127 |
headers = SortedDict() |
129 | 128 |
body = "" |
130 | 129 |
if bucket_location and bucket_location.strip().upper() != "US": |
... | ... |
@@ -132,6 +133,9 @@ class S3(object): |
132 | 132 |
body += bucket_location.strip().upper() |
133 | 133 |
body += "</LocationConstraint></CreateBucketConfiguration>" |
134 | 134 |
debug("bucket_location: " + body) |
135 |
+ self.check_bucket_name(bucket, dns_strict = True) |
|
136 |
+ else: |
|
137 |
+ self.check_bucket_name(bucket, dns_strict = False) |
|
135 | 138 |
headers["content-length"] = len(body) |
136 | 139 |
if self.config.acl_public: |
137 | 140 |
headers["x-amz-acl"] = "public-read" |
... | ... |
@@ -475,13 +479,37 @@ class S3(object): |
475 | 475 |
debug("SignHeaders: " + repr(h)) |
476 | 476 |
return base64.encodestring(hmac.new(self.config.secret_key, h, sha).digest()).strip() |
477 | 477 |
|
478 |
- def check_bucket_name(self, bucket): |
|
479 |
- invalid = re.compile("([^a-z0-9\._-])").search(bucket) |
|
480 |
- if invalid: |
|
481 |
- raise ParameterError("Bucket name '%s' contains disallowed character '%s'. The only supported ones are: lowercase us-ascii letters (a-z), digits (0-9), dot (.), hyphen (-) and underscore (_)." % (bucket, invalid.groups()[0])) |
|
478 |
+ @staticmethod |
|
479 |
+ def check_bucket_name(bucket, dns_strict = True): |
|
480 |
+ if dns_strict: |
|
481 |
+ invalid = re.search("([^a-z0-9\.-])", bucket) |
|
482 |
+ if invalid: |
|
483 |
+ raise ParameterError("Bucket name '%s' contains disallowed character '%s'. The only supported ones are: lowercase us-ascii letters (a-z), digits (0-9), dot (.) and hyphen (-)." % (bucket, invalid.groups()[0])) |
|
484 |
+ else: |
|
485 |
+ invalid = re.search("([^A-Za-z0-9\._-])", bucket) |
|
486 |
+ if invalid: |
|
487 |
+ raise ParameterError("Bucket name '%s' contains disallowed character '%s'. The only supported ones are: us-ascii letters (a-z, A-Z), digits (0-9), dot (.), hyphen (-) and underscore (_)." % (bucket, invalid.groups()[0])) |
|
488 |
+ |
|
482 | 489 |
if len(bucket) < 3: |
483 | 490 |
raise ParameterError("Bucket name '%s' is too short (min 3 characters)" % bucket) |
484 | 491 |
if len(bucket) > 255: |
485 | 492 |
raise ParameterError("Bucket name '%s' is too long (max 255 characters)" % bucket) |
493 |
+ if dns_strict: |
|
494 |
+ if len(bucket) > 63: |
|
495 |
+ raise ParameterError("Bucket name '%s' is too long (max 63 characters)" % bucket) |
|
496 |
+ if re.search("-\.", bucket): |
|
497 |
+ raise ParameterError("Bucket name '%s' must not contain sequence '-.' for DNS compatibility" % bucket) |
|
498 |
+ if re.search("\.\.", bucket): |
|
499 |
+ raise ParameterError("Bucket name '%s' must not contain sequence '..' for DNS compatibility" % bucket) |
|
500 |
+ if not re.search("^[0-9a-z]", bucket): |
|
501 |
+ raise ParameterError("Bucket name '%s' must start with a letter or a digit" % bucket) |
|
502 |
+ if not re.search("[0-9a-z]$", bucket): |
|
503 |
+ raise ParameterError("Bucket name '%s' must end with a letter or a digit" % bucket) |
|
486 | 504 |
return True |
487 | 505 |
|
506 |
+ @staticmethod |
|
507 |
+ def check_bucket_name_dns_conformity(bucket): |
|
508 |
+ try: |
|
509 |
+ return S3.check_bucket_name(bucket, dns_strict = True) |
|
510 |
+ except ParameterError: |
|
511 |
+ return False |
... | ... |
@@ -7,6 +7,7 @@ import re |
7 | 7 |
import sys |
8 | 8 |
from BidirMap import BidirMap |
9 | 9 |
from logging import debug |
10 |
+from S3 import S3 |
|
10 | 11 |
|
11 | 12 |
class S3Uri(object): |
12 | 13 |
type = None |
... | ... |
@@ -79,12 +80,15 @@ class S3UriS3(S3Uri): |
79 | 79 |
return "/".join(["s3:/", self._bucket, self._object]) |
80 | 80 |
|
81 | 81 |
def public_url(self): |
82 |
- return "http://%s.s3.amazonaws.com/%s" % (self._bucket, self._object) |
|
82 |
+ if S3.check_bucket_name_dns_conformity(self._bucket): |
|
83 |
+ return "http://%s.s3.amazonaws.com/%s" % (self._bucket, self._object) |
|
84 |
+ else: |
|
85 |
+ return "http://s3.amazonaws.com/%s/%s" % (self._bucket, self._object) |
|
83 | 86 |
|
84 | 87 |
@staticmethod |
85 | 88 |
def compose_uri(bucket, object = ""): |
86 | 89 |
return "s3://%s/%s" % (bucket, object) |
87 |
- |
|
90 |
+ |
|
88 | 91 |
class S3UriS3FS(S3Uri): |
89 | 92 |
type = "s3fs" |
90 | 93 |
_re = re.compile("^s3fs://([^/]*)/?(.*)", re.IGNORECASE) |