Original patch taken from:
https://github.com/NumberFour/s3cmd/commit/579ab33f222da6c112eab76bc32757de2bf09d98
(repo not clone of this tree)
Contributor: Jens Braeuer (aka NumberFour)
| ... | ... |
@@ -244,6 +244,57 @@ class S3(object): |
| 244 | 244 |
response['bucket-location'] = getTextFromXml(response['data'], "LocationConstraint") or "any" |
| 245 | 245 |
return response |
| 246 | 246 |
|
| 247 |
+ def website_list(self, uri, bucket_location = None): |
|
| 248 |
+ headers = SortedDict(ignore_case = True) |
|
| 249 |
+ bucket = uri.bucket() |
|
| 250 |
+ body = "" |
|
| 251 |
+ |
|
| 252 |
+ request = self.create_request("BUCKET_LIST", bucket = bucket, extra="?website")
|
|
| 253 |
+ response = None |
|
| 254 |
+ try: |
|
| 255 |
+ response = self.send_request(request, body) |
|
| 256 |
+ except S3Error, e: |
|
| 257 |
+ if e.status == 404: |
|
| 258 |
+ debug("Could not get ?website. Assuming none set.")
|
|
| 259 |
+ else: |
|
| 260 |
+ raise |
|
| 261 |
+ return response |
|
| 262 |
+ |
|
| 263 |
+ def website_create(self, uri, bucket_location = None): |
|
| 264 |
+ headers = SortedDict(ignore_case = True) |
|
| 265 |
+ bucket = uri.bucket() |
|
| 266 |
+ body = '<WebsiteConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">' |
|
| 267 |
+ body += ' <IndexDocument>' |
|
| 268 |
+ body += (' <Suffix>%s</Suffix>' % self.config.website_index)
|
|
| 269 |
+ body += ' </IndexDocument>' |
|
| 270 |
+ if self.config.website_error: |
|
| 271 |
+ body += ' <ErrorDocument>' |
|
| 272 |
+ body += (' <Key>%s</Key>' % self.config.website_error)
|
|
| 273 |
+ body += ' </ErrorDocument>' |
|
| 274 |
+ body += '</WebsiteConfiguration>' |
|
| 275 |
+ |
|
| 276 |
+ request = self.create_request("BUCKET_CREATE", bucket = bucket, extra="?website")
|
|
| 277 |
+ debug("About to send request '%s' with body '%s'" % (request, body))
|
|
| 278 |
+ response = self.send_request(request, body) |
|
| 279 |
+ debug("Received response '%s'" % (response))
|
|
| 280 |
+ |
|
| 281 |
+ return response |
|
| 282 |
+ |
|
| 283 |
+ def website_delete(self, uri, bucket_location = None): |
|
| 284 |
+ headers = SortedDict(ignore_case = True) |
|
| 285 |
+ bucket = uri.bucket() |
|
| 286 |
+ body = "" |
|
| 287 |
+ |
|
| 288 |
+ request = self.create_request("BUCKET_DELETE", bucket = bucket, extra="?website")
|
|
| 289 |
+ debug("About to send request '%s' with body '%s'" % (request, body))
|
|
| 290 |
+ response = self.send_request(request, body) |
|
| 291 |
+ debug("Received response '%s'" % (response))
|
|
| 292 |
+ |
|
| 293 |
+ if response['status'] != 204: |
|
| 294 |
+ raise S3ResponseError("Expected status 204: %s" % response)
|
|
| 295 |
+ |
|
| 296 |
+ return response |
|
| 297 |
+ |
|
| 247 | 298 |
def object_put(self, filename, uri, extra_headers = None, extra_label = ""): |
| 248 | 299 |
# TODO TODO |
| 249 | 300 |
# Make it consistent with stream-oriented object_get() |
| ... | ... |
@@ -164,6 +164,59 @@ def cmd_bucket_create(args): |
| 164 | 164 |
else: |
| 165 | 165 |
raise |
| 166 | 166 |
|
| 167 |
+def cmd_website_list(args): |
|
| 168 |
+ s3 = S3(Config()) |
|
| 169 |
+ for arg in args: |
|
| 170 |
+ uri = S3Uri(arg) |
|
| 171 |
+ if not uri.type == "s3" or not uri.has_bucket() or uri.has_object(): |
|
| 172 |
+ raise ParameterError("Expecting S3 URI with just the bucket name set instead of '%s'" % arg)
|
|
| 173 |
+ try: |
|
| 174 |
+ response = s3.website_list(uri, cfg.bucket_location) |
|
| 175 |
+ if response: |
|
| 176 |
+ import xml.dom.minidom |
|
| 177 |
+ xml = xml.dom.minidom.parseString(response['data']) |
|
| 178 |
+ output(u"Bucket '%s': website configuration:\n%s" % (uri.uri(), xml.toprettyxml())) |
|
| 179 |
+ else: |
|
| 180 |
+ output(u"Bucket '%s': unable to receive website configuration. None set?" % (uri.uri())) |
|
| 181 |
+ except S3Error, e: |
|
| 182 |
+ if S3.codes.has_key(e.info["Code"]): |
|
| 183 |
+ error(S3.codes[e.info["Code"]] % uri.bucket()) |
|
| 184 |
+ return |
|
| 185 |
+ else: |
|
| 186 |
+ raise |
|
| 187 |
+ |
|
| 188 |
+def cmd_website_create(args): |
|
| 189 |
+ s3 = S3(Config()) |
|
| 190 |
+ for arg in args: |
|
| 191 |
+ uri = S3Uri(arg) |
|
| 192 |
+ if not uri.type == "s3" or not uri.has_bucket() or uri.has_object(): |
|
| 193 |
+ raise ParameterError("Expecting S3 URI with just the bucket name set instead of '%s'" % arg)
|
|
| 194 |
+ try: |
|
| 195 |
+ response = s3.website_create(uri, cfg.bucket_location) |
|
| 196 |
+ output(u"Bucket '%s': website configuration created." % (uri.uri())) |
|
| 197 |
+ except S3Error, e: |
|
| 198 |
+ if S3.codes.has_key(e.info["Code"]): |
|
| 199 |
+ error(S3.codes[e.info["Code"]] % uri.bucket()) |
|
| 200 |
+ return |
|
| 201 |
+ else: |
|
| 202 |
+ raise |
|
| 203 |
+ |
|
| 204 |
+def cmd_website_delete(args): |
|
| 205 |
+ s3 = S3(Config()) |
|
| 206 |
+ for arg in args: |
|
| 207 |
+ uri = S3Uri(arg) |
|
| 208 |
+ if not uri.type == "s3" or not uri.has_bucket() or uri.has_object(): |
|
| 209 |
+ raise ParameterError("Expecting S3 URI with just the bucket name set instead of '%s'" % arg)
|
|
| 210 |
+ try: |
|
| 211 |
+ response = s3.website_delete(uri, cfg.bucket_location) |
|
| 212 |
+ output(u"Bucket '%s': website configuration deleted." % (uri.uri())) |
|
| 213 |
+ except S3Error, e: |
|
| 214 |
+ if S3.codes.has_key(e.info["Code"]): |
|
| 215 |
+ error(S3.codes[e.info["Code"]] % uri.bucket()) |
|
| 216 |
+ return |
|
| 217 |
+ else: |
|
| 218 |
+ raise |
|
| 219 |
+ |
|
| 167 | 220 |
def cmd_bucket_delete(args): |
| 168 | 221 |
def _bucket_delete_one(uri): |
| 169 | 222 |
try: |
| ... | ... |
@@ -1323,6 +1376,11 @@ def get_commands_list(): |
| 1323 | 1323 |
{"cmd":"sign", "label":"Sign arbitrary string using the secret key", "param":"STRING-TO-SIGN", "func":cmd_sign, "argc":1},
|
| 1324 | 1324 |
{"cmd":"fixbucket", "label":"Fix invalid file names in a bucket", "param":"s3://BUCKET[/PREFIX]", "func":cmd_fixbucket, "argc":1},
|
| 1325 | 1325 |
|
| 1326 |
+ ## Website commands |
|
| 1327 |
+ {"cmd":"ws-create", "label":"Create Website from bucket", "param":"s3://BUCKET", "func":cmd_website_create, "argc":1},
|
|
| 1328 |
+ {"cmd":"ws-delete", "label":"Delete Website", "param":"s3://BUCKET", "func":cmd_website_delete, "argc":1},
|
|
| 1329 |
+ {"cmd":"ws-list", "label":"List Websites", "param":"s3://BUCKET", "func":cmd_website_list, "argc":1},
|
|
| 1330 |
+ |
|
| 1326 | 1331 |
## CloudFront commands |
| 1327 | 1332 |
{"cmd":"cflist", "label":"List CloudFront distribution points", "param":"", "func":CfCmd.info, "argc":0},
|
| 1328 | 1333 |
{"cmd":"cfinfo", "label":"Display CloudFront distribution point parameters", "param":"[cf://DIST_ID]", "func":CfCmd.info, "argc":0},
|
| ... | ... |
@@ -1449,6 +1507,9 @@ def main(): |
| 1449 | 1449 |
optparser.add_option( "--list-md5", dest="list_md5", action="store_true", help="Include MD5 sums in bucket listings (only for 'ls' command).") |
| 1450 | 1450 |
optparser.add_option("-H", "--human-readable-sizes", dest="human_readable_sizes", action="store_true", help="Print sizes in human readable form (eg 1kB instead of 1234).")
|
| 1451 | 1451 |
|
| 1452 |
+ optparser.add_option( "--ws-index", dest="website_index", action="store", help="Name of error-document (only for [ws-create] command)") |
|
| 1453 |
+ optparser.add_option( "--ws-error", dest="website_error", action="store", help="Name of index-document (only for [ws-create] command)") |
|
| 1454 |
+ |
|
| 1452 | 1455 |
optparser.add_option( "--progress", dest="progress_meter", action="store_true", help="Display progress meter (default on TTY).") |
| 1453 | 1456 |
optparser.add_option( "--no-progress", dest="progress_meter", action="store_false", help="Don't display progress meter (default on non-TTY).") |
| 1454 | 1457 |
optparser.add_option( "--enable", dest="enable", action="store_true", help="Enable given CloudFront distribution (only for [cfmodify] command)") |