Created
November 25, 2008 21:48
-
-
Save atifaziz/29109 to your computer and use it in GitHub Desktop.
Command-line interface for LitS3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# The MIT License | |
# | |
# Copyright (c) 2008, Atif Aziz | |
# | |
# Permission is hereby granted, free of charge, to any person obtaining a copy | |
# of this software and associated documentation files (the "Software"), to deal | |
# in the Software without restriction, including without limitation the rights | |
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
# copies of the Software, and to permit persons to whom the Software is | |
# furnished to do so, subject to the following conditions: | |
# | |
# The above copyright notice and this permission notice shall be included in | |
# all copies or substantial portions of the Software. | |
# | |
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
# THE SOFTWARE. | |
# | |
import sys, clr | |
# Built and tested against LitS3 v0.8.2: | |
# http://code.google.com/p/lits3/downloads/detail?name=LitS3-0.8.2.zip | |
clr.AddReferenceToFileAndPath('LitS3.dll') | |
from LitS3 import * | |
from System import Int64, Byte, Array | |
from System.IO import Path, FileInfo, Directory, MemoryStream | |
from System.Text import Encoding | |
from System.Environment import GetEnvironmentVariable | |
from System.Net import ServicePointManager, ICertificatePolicy | |
MIME_MAP = { | |
'.323' : 'text/h323', | |
'.asx' : 'video/x-ms-asf', | |
'.acx' : 'application/internet-property-stream', | |
'.ai' : 'application/postscript', | |
'.aif' : 'audio/x-aiff', | |
'.aiff' : 'audio/aiff', | |
'.axs' : 'application/olescript', | |
'.aifc' : 'audio/aiff', | |
'.asr' : 'video/x-ms-asf', | |
'.avi' : 'video/x-msvideo', | |
'.asf' : 'video/x-ms-asf', | |
'.au' : 'audio/basic', | |
'.bin' : 'application/octet-stream', | |
'.bas' : 'text/plain', | |
'.bcpio' : 'application/x-bcpio', | |
'.bmp' : 'image/bmp', | |
'.cdf' : 'application/x-cdf', | |
'.cat' : 'application/vndms-pkiseccat', | |
'.crt' : 'application/x-x509-ca-cert', | |
'.c' : 'text/plain', | |
'.css' : 'text/css', | |
'.cer' : 'application/x-x509-ca-cert', | |
'.crl' : 'application/pkix-crl', | |
'.cmx' : 'image/x-cmx', | |
'.csh' : 'application/x-csh', | |
'.cod' : 'image/cis-cod', | |
'.cpio' : 'application/x-cpio', | |
'.clp' : 'application/x-msclip', | |
'.crd' : 'application/x-mscardfile', | |
'.dll' : 'application/x-msdownload', | |
'.dot' : 'application/msword', | |
'.doc' : 'application/msword', | |
'.dvi' : 'application/x-dvi', | |
'.dir' : 'application/x-director', | |
'.dxr' : 'application/x-director', | |
'.der' : 'application/x-x509-ca-cert', | |
'.dib' : 'image/bmp', | |
'.dcr' : 'application/x-director', | |
'.disco' : 'text/xml', | |
'.exe' : 'application/octet-stream', | |
'.etx' : 'text/x-setext', | |
'.evy' : 'application/envoy', | |
'.eml' : 'message/rfc822', | |
'.eps' : 'application/postscript', | |
'.flr' : 'x-world/x-vrml', | |
'.fif' : 'application/fractals', | |
'.gtar' : 'application/x-gtar', | |
'.gif' : 'image/gif', | |
'.gz' : 'application/x-gzip', | |
'.hta' : 'application/hta', | |
'.htc' : 'text/x-component', | |
'.htt' : 'text/webviewhtml', | |
'.h' : 'text/plain', | |
'.hdf' : 'application/x-hdf', | |
'.hlp' : 'application/winhlp', | |
'.html' : 'text/html', | |
'.htm' : 'text/html', | |
'.hqx' : 'application/mac-binhex40', | |
'.isp' : 'application/x-internet-signup', | |
'.iii' : 'application/x-iphone', | |
'.ief' : 'image/ief', | |
'.ivf' : 'video/x-ivf', | |
'.ins' : 'application/x-internet-signup', | |
'.ico' : 'image/x-icon', | |
'.jpg' : 'image/jpeg', | |
'.jfif' : 'image/pjpeg', | |
'.jpe' : 'image/jpeg', | |
'.jpeg' : 'image/jpeg', | |
'.js' : 'application/x-javascript', | |
'.lsx' : 'video/x-la-asf', | |
'.latex' : 'application/x-latex', | |
'.lsf' : 'video/x-la-asf', | |
'.mhtml' : 'message/rfc822', | |
'.mny' : 'application/x-msmoney', | |
'.mht' : 'message/rfc822', | |
'.mid' : 'audio/mid', | |
'.mpv2' : 'video/mpeg', | |
'.man' : 'application/x-troff-man', | |
'.mvb' : 'application/x-msmediaview', | |
'.mpeg' : 'video/mpeg', | |
'.m3u' : 'audio/x-mpegurl', | |
'.mdb' : 'application/x-msaccess', | |
'.mpp' : 'application/vnd.ms-project', | |
'.m1v' : 'video/mpeg', | |
'.mpa' : 'video/mpeg', | |
'.me' : 'application/x-troff-me', | |
'.m13' : 'application/x-msmediaview', | |
'.movie' : 'video/x-sgi-movie', | |
'.m14' : 'application/x-msmediaview', | |
'.mpe' : 'video/mpeg', | |
'.mp2' : 'video/mpeg', | |
'.mov' : 'video/quicktime', | |
'.mp3' : 'audio/mpeg', | |
'.mpg' : 'video/mpeg', | |
'.ms' : 'application/x-troff-ms', | |
'.nc' : 'application/x-netcdf', | |
'.nws' : 'message/rfc822', | |
'.oda' : 'application/oda', | |
'.ods' : 'application/oleobject', | |
'.pmc' : 'application/x-perfmon', | |
'.p7r' : 'application/x-pkcs7-certreqresp', | |
'.p7b' : 'application/x-pkcs7-certificates', | |
'.p7s' : 'application/pkcs7-signature', | |
'.pmw' : 'application/x-perfmon', | |
'.ps' : 'application/postscript', | |
'.p7c' : 'application/pkcs7-mime', | |
'.pbm' : 'image/x-portable-bitmap', | |
'.ppm' : 'image/x-portable-pixmap', | |
'.pub' : 'application/x-mspublisher', | |
'.png' : 'image/png', | |
'.pnm' : 'image/x-portable-anymap', | |
'.pml' : 'application/x-perfmon', | |
'.p10' : 'application/pkcs10', | |
'.pfx' : 'application/x-pkcs12', | |
'.p12' : 'application/x-pkcs12', | |
'.pdf' : 'application/pdf', | |
'.pps' : 'application/vnd.ms-powerpoint', | |
'.p7m' : 'application/pkcs7-mime', | |
'.pko' : 'application/vndms-pkipko', | |
'.ppt' : 'application/vnd.ms-powerpoint', | |
'.pmr' : 'application/x-perfmon', | |
'.pma' : 'application/x-perfmon', | |
'.pot' : 'application/vnd.ms-powerpoint', | |
'.prf' : 'application/pics-rules', | |
'.pgm' : 'image/x-portable-graymap', | |
'.qt' : 'video/quicktime', | |
'.ra' : 'audio/x-pn-realaudio', | |
'.rgb' : 'image/x-rgb', | |
'.ram' : 'audio/x-pn-realaudio', | |
'.rmi' : 'audio/mid', | |
'.ras' : 'image/x-cmu-raster', | |
'.roff' : 'application/x-troff', | |
'.rtf' : 'application/rtf', | |
'.rtx' : 'text/richtext', | |
'.sv4crc' : 'application/x-sv4crc', | |
'.spc' : 'application/x-pkcs7-certificates', | |
'.setreg' : 'application/set-registration-initiation', | |
'.snd' : 'audio/basic', | |
'.stl' : 'application/vndms-pkistl', | |
'.setpay' : 'application/set-payment-initiation', | |
'.stm' : 'text/html', | |
'.shar' : 'application/x-shar', | |
'.sh' : 'application/x-sh', | |
'.sit' : 'application/x-stuffit', | |
'.spl' : 'application/futuresplash', | |
'.sct' : 'text/scriptlet', | |
'.scd' : 'application/x-msschedule', | |
'.sst' : 'application/vndms-pkicertstore', | |
'.src' : 'application/x-wais-source', | |
'.sv4cpio': 'application/x-sv4cpio', | |
'.tex' : 'application/x-tex', | |
'.tgz' : 'application/x-compressed', | |
'.t' : 'application/x-troff', | |
'.tar' : 'application/x-tar', | |
'.tr' : 'application/x-troff', | |
'.tif' : 'image/tiff', | |
'.txt' : 'text/plain', | |
'.texinfo': 'application/x-texinfo', | |
'.trm' : 'application/x-msterminal', | |
'.tiff' : 'image/tiff', | |
'.tcl' : 'application/x-tcl', | |
'.texi' : 'application/x-texinfo', | |
'.tsv' : 'text/tab-separated-values', | |
'.ustar' : 'application/x-ustar', | |
'.uls' : 'text/iuls', | |
'.vcf' : 'text/x-vcard', | |
'.wps' : 'application/vnd.ms-works', | |
'.wav' : 'audio/wav', | |
'.wrz' : 'x-world/x-vrml', | |
'.wri' : 'application/x-mswrite', | |
'.wks' : 'application/vnd.ms-works', | |
'.wmf' : 'application/x-msmetafile', | |
'.wcm' : 'application/vnd.ms-works', | |
'.wrl' : 'x-world/x-vrml', | |
'.wdb' : 'application/vnd.ms-works', | |
'.wsdl' : 'text/xml', | |
'.xml' : 'text/xml', | |
'.xlm' : 'application/vnd.ms-excel', | |
'.xaf' : 'x-world/x-vrml', | |
'.xla' : 'application/vnd.ms-excel', | |
'.xls' : 'application/vnd.ms-excel', | |
'.xof' : 'x-world/x-vrml', | |
'.xlt' : 'application/vnd.ms-excel', | |
'.xlc' : 'application/vnd.ms-excel', | |
'.xsl' : 'text/xml', | |
'.xbm' : 'image/x-xbitmap', | |
'.xlw' : 'application/vnd.ms-excel', | |
'.xpm' : 'image/x-xpixmap', | |
'.xwd' : 'image/x-xwindowdump', | |
'.xsd' : 'text/xml', | |
'.z' : 'application/x-compress', | |
'.zip' : 'application/x-zip-compressed', | |
'.*' : 'application/octet-stream', | |
# Office 2007 MIME types | |
# http://www.bram.us/2007/05/25/office-2007-mime-types-for-iis/ | |
'docm' : 'application/vnd.ms-word.document.macroEnabled.12', | |
'docx' : 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', | |
'dotm' : 'application/vnd.ms-word.template.macroEnabled.12', | |
'dotx' : 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', | |
'potm' : 'application/vnd.ms-powerpoint.template.macroEnabled.12', | |
'potx' : 'application/vnd.openxmlformats-officedocument.presentationml.template', | |
'ppam' : 'application/vnd.ms-powerpoint.addin.macroEnabled.12', | |
'ppsm' : 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12', | |
'ppsx' : 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', | |
'pptm' : 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', | |
'pptx' : 'application/vnd.openxmlformats-officedocument.presentationml.presentation', | |
'xlam' : 'application/vnd.ms-excel.addin.macroEnabled.12', | |
'xlsb' : 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', | |
'xlsm' : 'application/vnd.ms-excel.sheet.macroEnabled.12', | |
'xlsx' : 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', | |
'xltm' : 'application/vnd.ms-excel.template.macroEnabled.12', | |
'xltx' : 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', | |
} | |
def parse_s3obj_path(path): | |
"""Parses an S3 object path into its bucket and key constituents.""" | |
i = path.find('/') | |
return i >= 0 and (path[0:i], path[i+1:]) or (path, '') | |
def copy_stream(source, dest, length): | |
buffer = Array.CreateInstance(Byte, 8192) | |
while length > 0: | |
bytesRead = source.Read(buffer, 0, buffer.Length) | |
if bytesRead > 0: | |
dest.Write(buffer, 0, bytesRead) | |
else: | |
raise Exception("Unexpected end of stream while copying.") | |
length -= bytesRead | |
class S3Commander(object): | |
def __init__(self, s3): | |
self.s3 = s3 | |
def buckets(self, args): | |
"""Lists all buckets.""" | |
print '\n'.join( | |
['%s %s' % (b.CreationDate.ToString('r'), b.Name) for b in self.s3.GetAllBuckets()]) | |
def list(self, args): | |
"""Lists all objects in a bucket, optionally constrained by a prefix.""" | |
if not args: | |
raise Exception('Missing S3 path.') | |
bucket, prefix = parse_s3obj_path(args.pop(0)) | |
objs = self.s3.ListObjects(bucket, prefix) | |
for obj in objs: | |
if type(obj) == CommonPrefix: | |
print ' ' * 53 + obj.Prefix | |
else: | |
print '%s %20s %s' % ( | |
obj.LastModified.ToString('r'), | |
obj.Size.ToString('N0'), | |
obj.Key[len(prefix):]) | |
def put(self, args): | |
"""Puts a local file as an object in a bucket.""" | |
if not args: | |
raise Exception('Missing target object path.') | |
bucket, key = parse_s3obj_path(args.pop(0)) | |
if not args: | |
raise Exception('Missing local file path.') | |
fpath = args.pop(0) | |
if not key or key[-1] == '/': | |
key = (key and key or '') + Path.GetFileName(fpath) | |
content_type = MIME_MAP.get(Path.GetExtension(fpath), 'application/octet-stream') | |
fname = Path.GetFileName(fpath) | |
print 'Uploading %s (%s bytes) as %s...' % (fname, FileInfo(fpath).Length.ToString('N0'), content_type), | |
self.s3.AddObject(fpath, bucket, key, content_type, CannedAcl.Private) | |
print 'OK' | |
def puts(self, args): | |
"""Puts text from standard input as an object in a bucket.""" | |
if not args: | |
raise Exception('Missing target object for text.') | |
bucket, key = parse_s3obj_path(args.pop(0)) | |
if not key: | |
raise Exception('Missing key for text.') | |
txt = sys.stdin.read() | |
print 'Uploading %s characters of text...' % len(txt).ToString("N0"), | |
self.s3.AddObjectString(txt, bucket, key, 'text/plain', CannedAcl.Private) | |
print 'OK' | |
def get(self, args): | |
"""Gets an object from a bucket as a local file.""" | |
if not args: | |
raise Exception('Missing source object path.') | |
bucket, key = parse_s3obj_path(args.pop(0)) | |
if not key: | |
raise Exception('Missing key.') | |
name = key.split('/')[-1] | |
fpath = args and args.pop(0) or None | |
isdir = Directory.Exists(fpath) | |
if not fpath or isdir: | |
fpath = (isdir and fpath + '\\' or '') + name | |
print 'Downloading %s to %s...' % (key, Path.GetFileName(fpath)), | |
self.s3.GetObject(bucket, key, fpath) | |
print 'OK' | |
def gets(self, args): | |
"""Sends an object from a bucket to standard output.""" | |
print self.__gets(args)[-1] | |
def pops(self, args): | |
"""Removes and sends an object from a bucket to standard output.""" | |
bucket, key, txt = self.__gets(args) | |
print txt | |
self.rm([bucket + '/' + key]) | |
def rm(self, args): | |
"""Removes an from a bucket.""" | |
if not args: | |
raise Exception('Missing object path.') | |
bucket, key = parse_s3obj_path(args.pop(0)) | |
if not key: | |
raise Exception('Missing key.') | |
self.s3.DeleteObject(bucket, key) | |
def __gets(self, args): | |
if not args: | |
raise Exception('Missing source object path.') | |
bucket, key = parse_s3obj_path(args.pop(0)) | |
if not key: | |
raise Exception('Missing key.') | |
content_type = clr.Reference[str]() | |
content_length = clr.Reference[Int64]() | |
input = self.s3.GetObjectStream(bucket, key, content_length, content_type) | |
try: | |
content_length, content_type = content_length.Value, content_type.Value | |
if 'text/plain' != content_type: | |
raise Exception('Object is %s, not plain/text.' % content_type) | |
output = MemoryStream() | |
copy_stream(input, output, content_length) | |
finally: | |
input.Close() | |
txt = Encoding.UTF8.GetString(output.GetBuffer(), 0, content_length) | |
return (bucket, key, txt) | |
class TrustAnyCertificatePolicy(ICertificatePolicy): | |
def CheckValidationResult(self, sp, cert, request, problem): | |
return True | |
def main(args): | |
if not args: | |
raise Exception('Missing command.') | |
cmd_name = args.pop(0).replace('del', 'rm') # pop + alias | |
if 'help' == cmd_name: | |
print 'Help will be on its way soon... ;)' | |
return | |
id = args and args.pop(0) or None | |
if id == '-': | |
id = GetEnvironmentVariable('AWS_ACCESS_KEY_ID') | |
if not id: | |
raise Exception('Missing AWS access key ID.') | |
key = args and args.pop(0) or None | |
if key == '-': | |
key = GetEnvironmentVariable('AWS_SECRET_ACCESS_KEY') | |
if not key: | |
raise Exception('Missing AWS secret access key.') | |
s3 = S3Service(AccessKeyID = id, SecretAccessKey = key) | |
# TODO Review policy-hack to trust any certificate. | |
# See: http://groups.google.com/group/lits3/t/7321c65188be7792 | |
ServicePointManager.CertificatePolicy = TrustAnyCertificatePolicy() | |
commander = S3Commander(s3) | |
cmd = getattr(commander, cmd_name, None) | |
if not cmd: | |
raise Exception('Unknown command (%s).' % cmd_name) | |
cmd(args) | |
if __name__ == '__main__': | |
try: | |
main(sys.argv[1:]) | |
except Exception, e: | |
print >> sys.stderr, e | |
sys.exit(1) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment