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)") |