text: var AWS = require('../core');
  var inherit = AWS.util.inherit;
  
  /**
   * @api private
   */
  AWS.Signers.S3 = inherit(AWS.Signers.RequestSigner, {
    /**
     * When building the stringToSign, these sub resource params should be
     * part of the canonical resource string with their NON-decoded values
     */
    subResources: {
      'acl': 1,
      'cors': 1,
      'lifecycle': 1,
      'delete': 1,
      'location': 1,
      'logging': 1,
      'notification': 1,
      'partNumber': 1,
      'policy': 1,
      'requestPayment': 1,
      'restore': 1,
      'tagging': 1,
      'torrent': 1,
      'uploadId': 1,
      'uploads': 1,
      'versionId': 1,
      'versioning': 1,
      'versions': 1,
      'website': 1
    },
  
    // when building the stringToSign, these querystring params should be
    // part of the canonical resource string with their NON-encoded values
    responseHeaders: {
      'response-content-type': 1,
      'response-content-language': 1,
      'response-expires': 1,
      'response-cache-control': 1,
      'response-content-disposition': 1,
      'response-content-encoding': 1
    },
  
    addAuthorization: function addAuthorization(credentials, date) {
      if (!this.request.headers['presigned-expires']) {
        this.request.headers['X-Amz-Date'] = AWS.util.date.rfc822(date);
      }
  
      if (credentials.sessionToken) {
        // presigned URLs require this header to be lowercased
        this.request.headers['x-amz-security-token'] = credentials.sessionToken;
      }
  
      var signature = this.sign(credentials.secretAccessKey, this.stringToSign());
      var auth = 'AWS ' + credentials.accessKeyId + ':' + signature;
  
      this.request.headers['Authorization'] = auth;
    },
  
    stringToSign: function stringToSign() {
      var r = this.request;
  
      var parts = [];
      parts.push(r.method);
      parts.push(r.headers['Content-MD5'] || '');
      parts.push(r.headers['Content-Type'] || '');
  
      // This is the "Date" header, but we use X-Amz-Date.
      // The S3 signing mechanism requires us to pass an empty
      // string for this Date header regardless.
      parts.push(r.headers['presigned-expires'] || '');
  
      var headers = this.canonicalizedAmzHeaders();
      if (headers) parts.push(headers);
      parts.push(this.canonicalizedResource());
  
      return parts.join('\n');
  
    },
  
    canonicalizedAmzHeaders: function canonicalizedAmzHeaders() {
  
      var amzHeaders = [];
  
      AWS.util.each(this.request.headers, function (name) {
        if (name.match(/^x-amz-/i))
          amzHeaders.push(name);
      });
  
      amzHeaders.sort(function (a, b) {
        return a.toLowerCase() < b.toLowerCase() ? -1 : 1;
      });
  
      var parts = [];
      AWS.util.arrayEach.call(this, amzHeaders, function (name) {
        parts.push(name.toLowerCase() + ':' + String(this.request.headers[name]));
      });
  
      return parts.join('\n');
  
    },
  
    canonicalizedResource: function canonicalizedResource() {
  
      var r = this.request;
  
      var parts = r.path.split('?');
      var path = parts[0];
      var querystring = parts[1];
  
      var resource = '';
  
      if (r.virtualHostedBucket)
        resource += '/' + r.virtualHostedBucket;
  
      resource += path;
  
      if (querystring) {
  
        // collect a list of sub resources and query params that need to be signed
        var resources = [];
  
        AWS.util.arrayEach.call(this, querystring.split('&'), function (param) {
          var name = param.split('=')[0];
          var value = param.split('=')[1];
          if (this.subResources[name] || this.responseHeaders[name]) {
            var subresource = { name: name };
            if (value !== undefined) {
              if (this.subResources[name]) {
                subresource.value = value;
              } else {
                subresource.value = decodeURIComponent(value);
              }
            }
            resources.push(subresource);
          }
        });
  
        resources.sort(function (a, b) { return a.name < b.name ? -1 : 1; });
  
        if (resources.length) {
  
          querystring = [];
          AWS.util.arrayEach(resources, function (res) {
            if (res.value === undefined)
              querystring.push(res.name);
            else
              querystring.push(res.name + '=' + res.value);
          });
  
          resource += '?' + querystring.join('&');
        }
  
      }
  
      return resource;
  
    },
  
    sign: function sign(secret, string) {
      return AWS.util.crypto.hmac(secret, string, 'base64', 'sha1');
    }
  });
  
  module.exports = AWS.Signers.S3;
  