package railo.commons.io.res.type.s3;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;

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

import org.apache.commons.collections.map.ReferenceMap;
import org.xml.sax.SAXException;

import railo.commons.io.CharsetUtil;
import railo.commons.lang.Md5;
import railo.commons.lang.StringUtil;
import railo.commons.net.URLEncoder;
import railo.commons.net.http.Entity;
import railo.commons.net.http.HTTPResponse;
import railo.commons.net.http.Header;
import railo.commons.net.http.httpclient3.HTTPEngine3Impl;
import railo.loader.util.Util;
import railo.runtime.config.Constants;
import railo.runtime.engine.ThreadLocalPageContext;
import railo.runtime.exp.PageException;
import railo.runtime.op.Caster;
import railo.runtime.type.dt.DateTime;

public final class S3 implements S3Constants {

	private static final String DEFAULT_URL="s3.amazonaws.com";
	
	private String secretAccessKey;
	private String accessKeyId;
	private TimeZone timezone;
	private String host;


	private static final Map<String,S3Info> infos=new ReferenceMap();
	private static final Map<String,AccessControlPolicy> acps=new ReferenceMap();

	public static final int MAX_REDIRECT = 15;

	public String toString(){
		return "secretAccessKey:"+secretAccessKey+";accessKeyId:"+accessKeyId+";host:"+host+";timezone:"+
		(timezone==null?"":timezone.getID());
	}
	
	public String hash() {
		try {
			return Md5.getDigestAsString(toString());
		} catch (IOException e) {
			return null;
		}
	}

	public S3(String secretAccessKey, String accessKeyId,TimeZone tz) {
		host=DEFAULT_URL;
		this.secretAccessKey = secretAccessKey;
		this.accessKeyId = accessKeyId;
		this.timezone = tz;
		//testFinal();
	}
	
	

	public S3() {
		
		//testFinal();
	}
	
	/**
	 * @return the secretAccessKey
	 * @throws S3Exception 
	 */
	String getSecretAccessKeyValidate() throws S3Exception {
		if(StringUtil.isEmpty(secretAccessKey))
			throw new S3Exception("secretAccessKey is not defined, define in "+Constants.APP_CFC+" (s3.awsSecretKey) or as part of the path.");
		return secretAccessKey;
	}
	
	/**
	 * @return the accessKeyId
	 * @throws S3Exception 
	 */
	String getAccessKeyIdValidate() throws S3Exception {
		if(StringUtil.isEmpty(accessKeyId))
			throw new S3Exception("accessKeyId is not defined, define in "+Constants.APP_CFC+" (this.s3.accessKeyId) or as part of the path.");
		return accessKeyId;
	}
	
	String getSecretAccessKey() {
		return secretAccessKey;
	}
	
	/**
	 * @return the accessKeyId
	 * @throws S3Exception 
	 */
	String getAccessKeyId() {
		return accessKeyId;
	}

	/**
	 * @return the tz
	 */
	TimeZone getTimeZone() {
		if(timezone==null)timezone=ThreadLocalPageContext.getTimeZone();
		return timezone;
	}
	
	private static byte[] HMAC_SHA1(String key, String message,String charset) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {
		
			SecretKeySpec sks = new SecretKeySpec(key.getBytes(charset),"HmacSHA1");
			Mac mac = Mac.getInstance(sks.getAlgorithm());
			mac.init(sks);
			mac.update(message.getBytes(charset));
			return mac.doFinal();
		
	}

	private static String createSignature(String str, String secretAccessKey,String charset) throws InvalidKeyException, NoSuchAlgorithmException, IOException {
		//str=StringUtil.replace(str, "\\n", String.valueOf((char)10), false);
		byte[] digest = HMAC_SHA1(secretAccessKey,str,charset);
		try {
			return Caster.toB64(digest);
		} catch (Throwable t) {
			throw new IOException(t.getMessage());
		}
	}
	
	public InputStream listBucketsRaw() throws MalformedURLException, IOException, InvalidKeyException, NoSuchAlgorithmException {
		String dateTimeString = Util.toHTTPTimeString();
		String signature = createSignature("GET\n\n\n"+dateTimeString+"\n/", getSecretAccessKeyValidate(), "iso-8859-1");
		
		HTTPResponse rsp = HTTPEngine3Impl.get(new URL("http://"+host), null, null, -1,MAX_REDIRECT, null, "Railo", null,
				new Header[]{
					header("Date",dateTimeString),
					header("Authorization","AWS "+getAccessKeyIdValidate()+":"+signature)
				}
		);
		return rsp.getContentAsStream();
		
	}
	

	public HTTPResponse head(String bucketName, String objectName) throws MalformedURLException, IOException, InvalidKeyException, NoSuchAlgorithmException {
		bucketName=checkBucket(bucketName);
		boolean hasObj=!StringUtil.isEmpty(objectName);
		if(hasObj)objectName=checkObjectName(objectName);
		
		String dateTimeString = Util.toHTTPTimeString();
		String signature = createSignature("HEAD\n\n\n"+dateTimeString+"\n/"+bucketName+"/"+(hasObj?objectName:""), getSecretAccessKeyValidate(), "iso-8859-1");
		
		
		List<Header> headers=new ArrayList<Header>();
		headers.add(header("Date",dateTimeString));
		headers.add(header("Authorization","AWS "+getAccessKeyIdValidate()+":"+signature));
		headers.add(header("Host",bucketName+"."+host));
		
		String strUrl="http://"+bucketName+"."+host+"/";
		//if(Util.hasUpperCase(bucketName))strUrl="http://"+host+"/"+bucketName+"/";
		if(hasObj) {
			strUrl+=objectName;
		}
		HTTPResponse method = HTTPEngine3Impl.head(new URL(strUrl), null, null, -1,MAX_REDIRECT, null, "Railo", null,headers.toArray(new Header[headers.size()]));
		return method;
		
	}
	
	
	
	
	public InputStream aclRaw(String bucketName, String objectName) throws MalformedURLException, IOException, InvalidKeyException, NoSuchAlgorithmException {
		bucketName=checkBucket(bucketName);
		boolean hasObj=!StringUtil.isEmpty(objectName);
		if(hasObj)objectName=checkObjectName(objectName);
		
		String dateTimeString = Util.toHTTPTimeString();
		String signature = createSignature("GET\n\n\n"+dateTimeString+"\n/"+bucketName+"/"+(hasObj?objectName:"")+"?acl", getSecretAccessKeyValidate(), "iso-8859-1");
		
		
		List<Header> headers=new ArrayList<Header>();
		headers.add(header("Date",dateTimeString));
		headers.add(header("Authorization","AWS "+getAccessKeyIdValidate()+":"+signature));
		headers.add(header("Host",bucketName+"."+host));
		
		String strUrl="http://"+bucketName+"."+host+"/";
		//if(Util.hasUpperCase(bucketName))strUrl="http://"+host+"/"+bucketName+"/";
		if(hasObj) {
			strUrl+=objectName;
		}
		strUrl+="?acl";
		
		HTTPResponse method = HTTPEngine3Impl.get(new URL(strUrl), null, null, -1,MAX_REDIRECT, null, "Railo", null,headers.toArray(new Header[headers.size()]));
		return method.getContentAsStream();
		
	}
	
	public AccessControlPolicy getAccessControlPolicy(String bucketName, String objectName) throws InvalidKeyException, MalformedURLException, NoSuchAlgorithmException, IOException, SAXException {
		InputStream raw = aclRaw(bucketName,objectName);
		//print.o(IOUtil.toString(raw, null));
		ACLFactory factory=new ACLFactory(raw, this);
		return factory.getAccessControlPolicy();
	}
	
	


	public void setAccessControlPolicy(String bucketName, String objectName,AccessControlPolicy acp) throws IOException, InvalidKeyException, NoSuchAlgorithmException, SAXException {
		bucketName=checkBucket(bucketName);
		boolean hasObj=!StringUtil.isEmpty(objectName);
		if(hasObj)objectName=checkObjectName(objectName);
		

		Entity re = HTTPEngine3Impl.getByteArrayEntity(acp.toXMLString().getBytes(CharsetUtil.ISO88591),"text/html");
		
		
		String dateTimeString = Util.toHTTPTimeString();
		
		
		String cs = "PUT\n\n"+re.contentType()+"\n"+dateTimeString+"\n/"+bucketName+"/"+(hasObj?objectName:"")+"?acl";
		String signature = createSignature(cs, getSecretAccessKeyValidate(), "iso-8859-1");
		Header[] headers = new Header[]{
				header("Content-Type",re.contentType()),
				header("Content-Length",Long.toString(re.contentLength())),
				header("Date",dateTimeString),
				header("Authorization","AWS "+getAccessKeyIdValidate()+":"+signature),
		};
		
		String strUrl="http://"+bucketName+"."+host+"/";
		if(hasObj) {
			strUrl+=objectName;
		}
		strUrl+="?acl";
		
		
		
		HTTPResponse method = HTTPEngine3Impl.put(new URL(strUrl), null, null, -1,MAX_REDIRECT, null, 
				"Railo", null,headers,re);
		if(method.getStatusCode()!=200){
			new ErrorFactory(method.getContentAsStream());
		}
		
		
	}
	
	public InputStream listContentsRaw(String bucketName,String prefix,String marker,int maxKeys) throws MalformedURLException, IOException, InvalidKeyException, NoSuchAlgorithmException {
		bucketName=checkBucket(bucketName);
		String dateTimeString = Util.toHTTPTimeString();
		String signature = createSignature("GET\n\n\n"+dateTimeString+"\n/"+bucketName+"/", getSecretAccessKeyValidate(), "iso-8859-1");
		
		
		List<Header> headers=new ArrayList<Header>();
		headers.add(header("Date",dateTimeString));
		headers.add(header("Authorization","AWS "+getAccessKeyIdValidate()+":"+signature));
		headers.add(header("Host",bucketName+"."+host));
		
		String strUrl="http://"+bucketName+"."+host+"/";
		if(Util.hasUpperCase(bucketName))strUrl="http://"+host+"/"+bucketName+"/";
		
		
		char amp='?';
		if(!Util.isEmpty(prefix)){
			strUrl+=amp+"prefix="+encodeEL(prefix);
			amp='&';
		}
		if(!Util.isEmpty(marker)) {
			strUrl+=amp+"marker="+encodeEL(marker);
			amp='&';
		}
		if(maxKeys!=-1) {
			strUrl+=amp+"max-keys="+maxKeys;
			amp='&';
		}
		
		HTTPResponse method = HTTPEngine3Impl.get(new URL(strUrl), null, null, -1,MAX_REDIRECT, null, "Railo", null,headers.toArray(new Header[headers.size()]));
		return method.getContentAsStream();
	}
	

	public Content[] listContents(String bucketName,String prefix) throws InvalidKeyException, MalformedURLException, NoSuchAlgorithmException, IOException, SAXException {
		String marker=null,last=null;
		ContentFactory factory;
		Content[] contents;
		List<Content[]> list = new ArrayList<Content[]>();
		int size=0;
		while(true) {
			factory = new ContentFactory(listContentsRaw(bucketName, prefix, marker, -1),this);
			contents = factory.getContents();
			list.add(contents);
			size+=contents.length;
			if(factory.isTruncated() && contents.length>0) {
				last=marker;
				marker=contents[contents.length-1].getKey();
				if(marker.equals(last))break;
			}
			else break;
		}
		
		if(list.size()==1) return list.get(0);
		if(list.size()==0) return new Content[0];
		
		Content[] rtn=new Content[size];
		Iterator<Content[]> it = list.iterator();
		int index=0;
		while(it.hasNext()) {
			contents=it.next();
			for(int i=0;i<contents.length;i++) {
				rtn[index++]=contents[i];
			}
		}
		
		return rtn;
	}

	public Content[] listContents(String bucketName,String prefix,String marker,int maxKeys) throws InvalidKeyException, MalformedURLException, NoSuchAlgorithmException, IOException, SAXException {
		InputStream raw = listContentsRaw(bucketName, prefix, marker, maxKeys);
		//print.o(IOUtil.toString(raw, null));
		ContentFactory factory = new ContentFactory(raw,this);
		return factory.getContents();
	}
	
	public Bucket[] listBuckets() throws InvalidKeyException, MalformedURLException, NoSuchAlgorithmException, IOException, SAXException {
		InputStream raw = listBucketsRaw();
		//print.o(IOUtil.toString(raw, null));
		BucketFactory factory = new BucketFactory(raw,this);
		return factory.getBuckets();
	}
	
	public void putBuckets(String bucketName,int acl, int storage) throws IOException, InvalidKeyException, NoSuchAlgorithmException, SAXException {
		String strXML = "";
		if(storage==STORAGE_EU) {
			strXML="<CreateBucketConfiguration><LocationConstraint>EU</LocationConstraint></CreateBucketConfiguration>";
		}
		
		byte[] barr = strXML.getBytes(CharsetUtil.ISO88591);
		put(bucketName, null, acl,HTTPEngine3Impl.getByteArrayEntity(barr,"text/html"));	
	}
	
	/*public void putObject(String bucketName,String objectName,int acl,Resource res) throws IOException, InvalidKeyException, NoSuchAlgorithmException, PageException, SAXException, EncoderException {
		String contentType = IOUtil.getMimeType(res, "application");
		InputStream is = null;
		try {
			is = res.getInputStream();
			put(bucketName, objectName, acl, is, contentType);
		}
		finally {
			IOUtil.closeEL(is);
		}
	}*/
	/*
	public void put(String bucketName,String objectName,int acl, InputStream is,long length, String contentType) throws IOException, InvalidKeyException, NoSuchAlgorithmException, PageException, SAXException, EncoderException {
		put(bucketName, objectName, acl, HTTPUtil.toRequestEntity(is),length, contentType);
	}*/
		
	public void put(String bucketName,String objectName,int acl, Entity re) throws IOException, InvalidKeyException, NoSuchAlgorithmException, SAXException {
		bucketName=checkBucket(bucketName);
		objectName=checkObjectName(objectName);
		
		String dateTimeString = Util.toHTTPTimeString();
		// Create a canonical string to send based on operation requested 
		String cs = "PUT\n\n"+re.contentType()+"\n"+dateTimeString+"\nx-amz-acl:"+toStringACL(acl)+"\n/"+bucketName+"/"+objectName;
		String signature = createSignature(cs, getSecretAccessKeyValidate(), "iso-8859-1");
		Header[] headers = new Header[]{
				header("Content-Type",re.contentType()),
				header("Content-Length",Long.toString(re.contentLength())),
				header("Date",dateTimeString),
				header("x-amz-acl",toStringACL(acl)),
				header("Authorization","AWS "+getAccessKeyIdValidate()+":"+signature),
		};
		
		String strUrl="http://"+bucketName+"."+host+"/"+objectName;
		if(Util.hasUpperCase(bucketName))strUrl="http://"+host+"/"+bucketName+"/"+objectName;
		
		
		
		HTTPResponse method = HTTPEngine3Impl.put(new URL(strUrl), null, null, -1,MAX_REDIRECT, null, 
				"Railo", null,headers,re);
		if(method.getStatusCode()!=200){
			new ErrorFactory(method.getContentAsStream());
		}
		
		
	}
		
	public HttpURLConnection preput(String bucketName,String objectName,int acl, String contentType) throws IOException, InvalidKeyException, NoSuchAlgorithmException {
		bucketName=checkBucket(bucketName);
		objectName=checkObjectName(objectName);
		
		String dateTimeString = Util.toHTTPTimeString();
		// Create a canonical string to send based on operation requested 
		String cs = "PUT\n\n"+contentType+"\n"+dateTimeString+"\nx-amz-acl:"+toStringACL(acl)+"\n/"+bucketName+"/"+objectName;
		String signature = createSignature(cs, getSecretAccessKeyValidate(), "iso-8859-1");
		
		String strUrl="http://"+bucketName+"."+host+"/"+objectName;
		if(Util.hasUpperCase(bucketName))strUrl="http://"+host+"/"+bucketName+"/"+objectName;
		
		URL url = new URL(strUrl);
		
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setRequestMethod("PUT");
		
		conn.setFixedLengthStreamingMode(227422142);
		conn.setDoOutput(true);
		conn.setUseCaches(false);
		conn.setRequestProperty("CONTENT-TYPE", contentType);
		conn.setRequestProperty("USER-AGENT", "S3 Resource");        
		//conn.setRequestProperty("Transfer-Encoding", "chunked" );
		conn.setRequestProperty("Date", dateTimeString);
		conn.setRequestProperty("x-amz-acl", toStringACL(acl));
		conn.setRequestProperty("Authorization", "AWS "+getAccessKeyIdValidate()+":"+signature);
		return conn;
	}

	public String getObjectLink(String bucketName,String objectName,int secondsValid) throws InvalidKeyException, NoSuchAlgorithmException, IOException {
		bucketName=checkBucket(bucketName);
		objectName=checkObjectName(objectName);
		
		long epoch = (System.currentTimeMillis()/1000)+(secondsValid);
		String cs = "GET\n\n\n"+epoch+"\n/"+bucketName+"/"+objectName;
		String signature = createSignature(cs, getSecretAccessKeyValidate(), "iso-8859-1");
		
		String strUrl="http://"+bucketName+"."+host+"/"+objectName;
		if(Util.hasUpperCase(bucketName))strUrl="http://"+host+"/"+bucketName+"/"+objectName;
		
		
		return strUrl+"?AWSAccessKeyId="+getAccessKeyIdValidate()+"&Expires="+epoch+"&Signature="+signature;
	}

	public InputStream getInputStream(String bucketName,String objectName) throws InvalidKeyException, NoSuchAlgorithmException, IOException, SAXException  {
		return getData(bucketName, objectName).getContentAsStream();
	}
	
	public Map<String, String> getMetadata(String bucketName,String objectName) throws InvalidKeyException, NoSuchAlgorithmException, IOException, SAXException  {
		HTTPResponse method = getData(bucketName, objectName);
		Header[] headers = method.getAllHeaders();
		Map<String,String> rtn=new HashMap<String, String>();
		String name;
		if(headers!=null)for(int i=0;i<headers.length;i++){
			name=headers[i].getName();
			if(name.startsWith("x-amz-meta-"))
				rtn.put(name.substring(11), headers[i].getValue());
		}
		return rtn;
	}
	
	private HTTPResponse getData(String bucketName,String objectName) throws InvalidKeyException, NoSuchAlgorithmException, IOException, SAXException  {
		bucketName=checkBucket(bucketName);
		objectName=checkObjectName(objectName);
		
		String dateTimeString = Util.toHTTPTimeString();
		//long epoch = (System.currentTimeMillis()/1000)+6000;
		String cs = "GET\n\n\n"+dateTimeString+"\n/"+bucketName+"/"+objectName;
		    
		
		String signature = createSignature(cs, getSecretAccessKeyValidate(), "iso-8859-1");
		
		String strUrl="http://"+bucketName+"."+host+"/"+objectName;
		if(Util.hasUpperCase(bucketName))strUrl="http://"+host+"/"+bucketName+"/"+objectName;
		URL url = new URL(strUrl);
		
		
		HTTPResponse method = HTTPEngine3Impl.get(url, null, null, -1,MAX_REDIRECT, null, "Railo", null,
				new Header[]{
				header("Date",dateTimeString),
				header("Host",bucketName+"."+host),
				header("Authorization","AWS "+getAccessKeyIdValidate()+":"+signature)
				}
		);
		if(method.getStatusCode()!=200)
			new ErrorFactory(method.getContentAsStream());
		return method;
	}

	public void delete(String bucketName, String objectName) throws IOException, InvalidKeyException, NoSuchAlgorithmException, SAXException {
		bucketName=checkBucket(bucketName);
		objectName = checkObjectName(objectName);

		String dateTimeString = Util.toHTTPTimeString();
		// Create a canonical string to send based on operation requested 
		String cs ="DELETE\n\n\n"+dateTimeString+"\n/"+bucketName+"/"+objectName;
		//print.out(cs);
		String signature = createSignature(cs, getSecretAccessKeyValidate(), "iso-8859-1");
		
		Header[] headers =new Header[]{
				header("Date",dateTimeString),
				header("Authorization","AWS "+getAccessKeyIdValidate()+":"+signature),
		};
		
		String strUrl="http://"+bucketName+"."+host+"/"+objectName;
		if(Util.hasUpperCase(bucketName))strUrl="http://"+host+"/"+bucketName+"/"+objectName;
		
		
		
		HTTPResponse rsp = HTTPEngine3Impl.delete(new URL(strUrl), null, null, -1,MAX_REDIRECT, null, "Railo",  null,headers);
		
		if(rsp.getStatusCode()!=200)
			new ErrorFactory(rsp.getContentAsStream());
	}

	
	
	
	
	
	// --------------------------------
	public static String toStringACL(int acl) throws S3Exception {
		switch(acl) {
			case ACL_AUTH_READ:return "authenticated-read";
			case ACL_PUBLIC_READ:return "public-read";
			case ACL_PRIVATE:return "private";
			case ACL_PUBLIC_READ_WRITE:return "public-read-write";
		}
		throw new S3Exception("invalid acl definition");
	}

	public static String toStringStorage(int storage) throws S3Exception {
		String s = toStringStorage(storage, null);
		if(s==null)
			throw new S3Exception("invalid storage definition");
		return s;
	}
	public static String toStringStorage(int storage, String defaultValue) {
		switch(storage) {
			case STORAGE_EU:return "eu";
			case STORAGE_US:return "us";
			case STORAGE_US_WEST:return "us-west";
		}
		return defaultValue;
	}
	
	public static int toIntACL(String acl) throws S3Exception {
		acl=acl.toLowerCase().trim();
		if("public-read".equals(acl)) return ACL_PUBLIC_READ;
		if("private".equals(acl)) return ACL_PRIVATE;
		if("public-read-write".equals(acl)) return ACL_PUBLIC_READ_WRITE;
		if("authenticated-read".equals(acl)) return ACL_AUTH_READ;
		
		if("public_read".equals(acl)) return ACL_PUBLIC_READ;
		if("public_read_write".equals(acl)) return ACL_PUBLIC_READ_WRITE;
		if("authenticated_read".equals(acl)) return ACL_AUTH_READ;
		
		if("publicread".equals(acl)) return ACL_PUBLIC_READ;
		if("publicreadwrite".equals(acl)) return ACL_PUBLIC_READ_WRITE;
		if("authenticatedread".equals(acl)) return ACL_AUTH_READ;
		
		throw new S3Exception("invalid acl value, valid values are [public-read, private, public-read-write, authenticated-read]");
	}

	public static int toIntStorage(String storage) throws S3Exception {
		int s=toIntStorage(storage,-1);
		if(s==-1)
			throw new S3Exception("invalid storage value, valid values are [eu,us,us-west]");
		return s;
	}
	public static int toIntStorage(String storage, int defaultValue) {
		storage=storage.toLowerCase().trim();
		if("us".equals(storage)) return STORAGE_US;
		if("usa".equals(storage)) return STORAGE_US;
		if("eu".equals(storage)) return STORAGE_EU;
		
		if("u.s.".equals(storage)) return STORAGE_US;
		if("u.s.a.".equals(storage)) return STORAGE_US;
		if("europe.".equals(storage)) return STORAGE_EU;
		if("euro.".equals(storage)) return STORAGE_EU;
		if("e.u.".equals(storage)) return STORAGE_EU;
		if("united states of america".equals(storage)) return STORAGE_US;
		
		if("us-west".equals(storage)) return STORAGE_US_WEST;
		if("usa-west".equals(storage)) return STORAGE_US_WEST;
		
		
		return defaultValue;
	}
	

	private String checkObjectName(String objectName) throws UnsupportedEncodingException {
		if(Util.isEmpty(objectName)) return "";
		if(objectName.startsWith("/"))objectName=objectName.substring(1);
		return encode(objectName);
	}

	private String checkBucket(String name) {
		/*if(!Decision.isVariableName(name)) 
			throw new S3Exception("invalid bucket name definition ["+name+"], name should only contain letters, digits, dashes and underscores");
		
		if(name.length()<3 || name.length()>255) 
			throw new S3Exception("invalid bucket name definition ["+name+"], the length of a bucket name must be between 3 and 255");
		*/
		
		return encodeEL(name);
	}

	private String encodeEL(String name) {
		try {
			return encode(name);
		
		} catch (UnsupportedEncodingException e) {
			return name;
		}
	}
	private String encode(String name) throws UnsupportedEncodingException {
		return URLEncoder.encode(name,"UTF-8");
	}

	/**
	 * @param secretAccessKey the secretAccessKey to set
	 */
	public void setSecretAccessKey(String secretAccessKey) {
		this.secretAccessKey = secretAccessKey;
	}

	/**
	 * @param accessKeyId the accessKeyId to set
	 */
	public void setAccessKeyId(String accessKeyId) {
		this.accessKeyId = accessKeyId;
	}

	/**
	 * @param url the url to set
	 */
	public void setHost(String host) {
		this.host=host;
	}

	public String getHost() {
		return host;
	}

	public S3Info getInfo(String path) {
		return infos.get(toKey(path));
	}

	public void setInfo(String path,S3Info info) {
		infos.put(toKey(path),info);
	}

	public AccessControlPolicy getACP(String path) {
		return acps.get(toKey(path));
	}

	public void setACP(String path,AccessControlPolicy acp) {
		acps.put(toKey(path),acp);
	}

	public void releaseCache(String path) {
		Object k = toKey(path);
		infos.remove(k);
		acps.remove(k);
	}

	private String toKey(String path) {
		return toString()+":"+path.toLowerCase();
	}
	
	private Header header(String name, String value) {
		return HTTPEngine3Impl.header(name,value);
	}

	public static DateTime toDate(String strDate, TimeZone tz) throws PageException {
		if(strDate.endsWith("Z"))
			strDate=strDate.substring(0,strDate.length()-1);
		return Caster.toDate(strDate, tz);
	}
}





