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>