s3download.xsh

<xsharper switchPrefixes="-">
    <usage options="default ifNoArguments autoSuffix" />
    <versionInfo version="1.0.0.0">Program to download files from Amazon S3.</versionInfo>
    <param name="directory" required="1">Output directory</param>
    <param name="files" required="1" count="multiple" description="file [file]">Files to download, formatted as bucket/directory/filename</param>
    <param />
    <param switch="range">Range header, in bytes. For example, 10-20</param>
	<param switch="head" default="0" count="none" unspecified="1">Issue HEAD instead of GET, and do not save to file</param>
	<param switch="httpHeaders" default="0" count="none" unspecified="1">Dump HTTP headers</param>

    <param />
    <param switch="baseUrl" default="http://s3.amazonaws.com">Service URL</param>
    <param switch="accesskey" required="1">Access key</param>
    <param switch="secretkey">Secret key</param>
    <param switch="secretkeyfile">Text file, first line of which contains the key</param>
    <param />
    <param>Note: Either -secretkey or -secretkeyfile must be specified.</param>

    <if isNotSet="secretKey">
        <if isNotSet="secretkeyfile">
            <throw>Either -secretkey or -secretkeyfile must be specified.</throw>
        </if>
        <set secretKey="${=.ReadLines($secretKeyFile)[0].Trim()}" />
    </if>


    <foreach file="${files}">
		<set fname="${=Path.Combine($directory,Path.GetFileName($file)) }" />
        <print outTo="^info" nl="false">s3://${file} =&gt; ${fname} ...</print>
        <code />
        <set url="${=X.SignUrl($baseUrl,$accesskey,$secretkey,$file, $head)}" />
        <print outTo="^debug">${url}</print>


        <?_ try 
            {
                HttpWebRequest wr=(HttpWebRequest)WebRequest.Create(c.GetStr("url"));
                {
					if  (c.IsSet("range"))
					{
						MethodInfo httpWebRequestAddRangeHelper = typeof(WebHeaderCollection).GetMethod("AddWithoutValidate", BindingFlags.Instance | BindingFlags.NonPublic);
	                    httpWebRequestAddRangeHelper.Invoke(wr.Headers, new object[] { "Range", string.Format("bytes={0}", c.GetStr("range"))});						
					}
					wr.ReadWriteTimeout = 100000;
					wr.Timeout = 100000;
					wr.AllowWriteStreamBuffering=false;
					wr.SendChunked=true;
					
					if (c.GetBool("head"))
						wr.Method="HEAD";
					HttpWebResponse resp=(HttpWebResponse)wr.GetResponse();
									
					if (!c.GetBool("head"))
					{
		            	Stream rs= resp.GetResponseStream();
	                	using (Stream fs=c.CreateStream(c.GetStr("fname")))
						{
							byte[] buf=new byte[4096];
							int n;
							while ((n=rs.Read(buf,0,buf.Length))!=0)
								fs.Write(buf,0,n);
						}
					}
					c.WriteLine(XS.OutputType.Info, "Done");
					if (c.GetBool("httpHeaders"))
					{
						c.WriteLine("-- HTTP headers for {0} ---", Path.GetFileName(c.GetStr("fname")));
						for (int n=0;n<resp.Headers.Count;++n)
							c.WriteLine("{0}: {1}",resp.Headers.GetKey(n),resp.Headers[n]);
					}

                }

            }
            catch (WebException e)
            {
                if (e.Response!=null)
                    try {   
                        XS.XmlDoc xs=new XS.XmlDoc(((HttpWebResponse)e.Response).GetResponseStream());
                        c.Error.WriteLine("Error message: {0}",xs.V("/Error/Message/text()"));
                    }
                    catch 
                    {
                    }
                throw;
            }
        ?>
    </foreach>

    <?h using System.Security.Cryptography;
     using System.Net;
	 using System.Reflection;


     public static class X
     {
        public static string SignUrl(string baseUrl, string accesskey, string secretkey, string filename, bool head)
        {                            
            string  path=filename.TrimStart('/');
            string expiration=((long)((DateTime.UtcNow.AddDays(1)-new DateTime(1970, 1, 1)).TotalSeconds)).ToString();
            using (HMACSHA1 sign = new HMACSHA1(Encoding.UTF8.GetBytes(secretkey)))
            {
                string escaped=Uri.EscapeUriString(path);
                string stringToSign = string.Format("{0}\n\n\n{1}\n/{2}",head?"HEAD":"GET",expiration,escaped);
                string authorization = Uri.EscapeDataString(Convert.ToBase64String(sign.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)),Base64FormattingOptions.None).Trim());
				// Console.WriteLine(stringToSign);
                return string.Format( "{0}/{1}?AWSAccessKeyId={2}&Expires={3}&Signature={4}",baseUrl, escaped,accesskey,expiration,authorization);
            }
        }
      }
   ?>
</xsharper>