/*!

*  Copyright 2010 LearnBoost <dev@learnboost.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**

* Module dependencies.
*/

var crypto = require('crypto')

, parse = require('url').parse
;

/**

* Valid keys.
*/

var keys =

[ 'acl'
, 'location'
, 'logging'
, 'notification'
, 'partNumber'
, 'policy'
, 'requestPayment'
, 'torrent'
, 'uploadId'
, 'uploads'
, 'versionId'
, 'versioning'
, 'versions'
, 'website'
]

/**

* Return an "Authorization" header value with the given `options`
* in the form of "AWS <key>:<signature>"
*
* @param {Object} options
* @return {String}
* @api private
*/

function authorization (options) {

return 'AWS ' + options.key + ':' + sign(options)

}

module.exports = authorization module.exports.authorization = authorization

/**

* Simple HMAC-SHA1 Wrapper
*
* @param {Object} options
* @return {String}
* @api private
*/

function hmacSha1 (options) {

return crypto.createHmac('sha1', options.secret).update(options.message).digest('base64')

}

module.exports.hmacSha1 = hmacSha1

/**

* Create a base64 sha1 HMAC for `options`. 
* 
* @param {Object} options
* @return {String}
* @api private
*/

function sign (options) {

options.message = stringToSign(options)
return hmacSha1(options)

} module.exports.sign = sign

/**

* Create a base64 sha1 HMAC for `options`. 
*
* Specifically to be used with S3 presigned URLs
* 
* @param {Object} options
* @return {String}
* @api private
*/

function signQuery (options) {

options.message = queryStringToSign(options)
return hmacSha1(options)

} module.exports.signQuery= signQuery

/**

* Return a string for sign() with the given `options`.
*
* Spec:
* 
*    <verb>\n
*    <md5>\n
*    <content-type>\n
*    <date>\n
*    [headers\n]
*    <resource>
*
* @param {Object} options
* @return {String}
* @api private
*/

function stringToSign (options) {

var headers = options.amazonHeaders || ''
if (headers) headers += '\n'
var r = 
  [ options.verb
  , options.md5
  , options.contentType
  , options.date ? options.date.toUTCString() : ''
  , headers + options.resource
  ]
return r.join('\n')

} module.exports.queryStringToSign = stringToSign

/**

* Return a string for sign() with the given `options`, but is meant exclusively
* for S3 presigned URLs
*
* Spec:
* 
*    <date>\n
*    <resource>
*
* @param {Object} options
* @return {String}
* @api private
*/

function queryStringToSign (options){

return 'GET\n\n\n' + options.date + '\n' + options.resource

} module.exports.queryStringToSign = queryStringToSign

/**

* Perform the following:
*
*  - ignore non-amazon headers
*  - lowercase fields
*  - sort lexicographically
*  - trim whitespace between ":"
*  - join with newline
*
* @param {Object} headers
* @return {String}
* @api private
*/

function canonicalizeHeaders (headers) {

var buf = []
  , fields = Object.keys(headers)
  ;
for (var i = 0, len = fields.length; i < len; ++i) {
  var field = fields[i]
    , val = headers[field]
    , field = field.toLowerCase()
    ;
  if (0 !== field.indexOf('x-amz')) continue
  buf.push(field + ':' + val)
}
return buf.sort().join('\n')

} module.exports.canonicalizeHeaders = canonicalizeHeaders

/**

* Perform the following:
*
*  - ignore non sub-resources
*  - sort lexicographically
*
* @param {String} resource
* @return {String}
* @api private
*/

function canonicalizeResource (resource) {

var url = parse(resource, true)
  , path = url.pathname
  , buf = []
  ;

Object.keys(url.query).forEach(function(key){
  if (!~keys.indexOf(key)) return
  var val = '' == url.query[key] ? '' : '=' + encodeURIComponent(url.query[key])
  buf.push(key + val)
})

return path + (buf.length ? '?' + buf.sort().join('&') : '')

} module.exports.canonicalizeResource = canonicalizeResource