//
// typica - A client library for Amazon Web Services
// Copyright (C) 2007 Xerox Corporation
// 
// 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.
//

package com.xerox.amazonws.common;

import java.io.InputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.SortedMap;
import java.util.TimeZone;
import java.util.TreeMap;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

/**
 * This class provides common code to the query and rest connection classes
 *
 * @author D. Kavanagh
 * @author developer@dotech.com
 */
public abstract class AWSConnection {
    private String awsAccessId;
    private String awsSecretKey;
    private boolean isSecure;
    private String server;
    private int port;
	protected Map <String, List<String>> headers;
	// used for caching last used Mac obj.. to save time 99.99% of the time
	private static Mac mac;
	private static String lastSecretKey;

    /**
	 * Initializes the queue service with your AWS login information.
	 *
     * @param awsAccessId The your user key into AWS
     * @param awsSecretKey The secret string used to generate signatures for authentication.
     * @param isSecure True if the data should be encrypted on the wire on the way to or from SQS.
     * @param server Which host to connect to.  Usually, this will be s3.amazonaws.com
     * @param port Which port to use.
     */
    public AWSConnection(String awsAccessId, String awsSecretKey, boolean isSecure,
                             String server, int port)
    {
        this.awsAccessId = awsAccessId;
        this.awsSecretKey = awsSecretKey;
        this.isSecure = isSecure;
        this.server = server;
        this.port = port;
		this.headers = new TreeMap<String, List<String>>();
    }

	/**
	 * This method provides the URL for the queue service based on initialization.
	 *
	 * @return generated queue service url
	 */
	public URL getUrl() {
		try {
			return makeURL("");
		} catch (MalformedURLException ex) {
			return null;
		}
	}

	protected String getAwsAccessKeyId() {
		return this.awsAccessId;
	}

	protected String getSecretAccessKey() {
		return this.awsSecretKey;
	}

	protected boolean isSecure() {
		return this.isSecure;
	}

	protected String getServer() {
		return this.server;
	}

	protected int getPort() {
		return this.port;
	}

    /**
     * Create a new URL object for a given resource.
     * @param resource The resource name (bucketName + "/" + key).
     */
    protected URL makeURL(String resource) throws MalformedURLException {
        String protocol = this.isSecure ? "https" : "http";
        return new URL(protocol, this.server, this.port, "/"+resource);
    }

    /**
     * Calculate the HMAC/SHA1 on a string.
     * @param awsSecretKey passcode to sign it with
     * @param canonicalString data to sign
     * @return signature
     * @throws NoSuchAlgorithmException If the algorithm does not exist.  Unlikely
     * @throws InvalidKeyException If the key is invalid.
     */
    protected String encode(String awsSecretKey, String canonicalString,
                                boolean urlencode)
    {
        // The following HMAC/SHA1 code for the signature is taken from the
        // AWS Platform's implementation of RFC2104 (amazon.webservices.common.Signature)
        //
        // Acquire an HMAC/SHA1 from the raw key bytes.
        SecretKeySpec signingKey =
            new SecretKeySpec(awsSecretKey.getBytes(), "HmacSHA1");

        // Acquire the MAC instance and initialize with the signing key.
		if (mac == null || !lastSecretKey.equals(awsSecretKey)) {
			try {
				mac = Mac.getInstance("HmacSHA1");
			} catch (NoSuchAlgorithmException e) {
				// should not happen
				throw new RuntimeException("Could not find sha1 algorithm", e);
			}
			try {
				mac.init(signingKey);
			} catch (InvalidKeyException e) {
				// also should not happen
				throw new RuntimeException("Could not initialize the MAC algorithm", e);
			}
			lastSecretKey = awsSecretKey;
		}

        // Compute the HMAC on the digest, and set it.
		byte [] signedBytes = null;
		synchronized (mac) {
        	signedBytes = mac.doFinal(canonicalString.getBytes());
		}
        String b64 = new String(Base64.encodeBase64(signedBytes));

        if (urlencode) {
            return urlencode(b64);
        } else {
            return b64;
        }
    }

    protected String urlencode(String unencoded) {
        try {
            return URLEncoder.encode(unencoded, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            // should never happen
            throw new RuntimeException("Could not url encode to UTF-8", e);
        }
    }
}

@@ -4991,32 +4991,49 @@
 uld not happen%0D%0A
+%09%09%09%09mac = null;%0D%0A
 %09%09%09%09throw new Ru

clear mac object in the rare occurance of an initialization failure