001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.commons.io.res.type.s3;
020
021import java.io.IOException;
022import java.io.InputStream;
023import java.io.UnsupportedEncodingException;
024import java.net.HttpURLConnection;
025import java.net.MalformedURLException;
026import java.net.URL;
027import java.security.InvalidKeyException;
028import java.security.NoSuchAlgorithmException;
029import java.util.ArrayList;
030import java.util.HashMap;
031import java.util.Iterator;
032import java.util.List;
033import java.util.Map;
034import java.util.TimeZone;
035
036import javax.crypto.Mac;
037import javax.crypto.spec.SecretKeySpec;
038
039import lucee.commons.io.CharsetUtil;
040import lucee.commons.lang.ExceptionUtil;
041import lucee.commons.lang.Md5;
042import lucee.commons.lang.StringUtil;
043import lucee.commons.net.URLEncoder;
044import lucee.commons.net.http.Entity;
045import lucee.commons.net.http.HTTPResponse;
046import lucee.commons.net.http.Header;
047import lucee.commons.net.http.httpclient3.HTTPEngine3Impl;
048import lucee.loader.util.Util;
049import lucee.runtime.config.Constants;
050import lucee.runtime.engine.ThreadLocalPageContext;
051import lucee.runtime.exp.PageException;
052import lucee.runtime.op.Caster;
053import lucee.runtime.type.dt.DateTime;
054
055import org.apache.commons.collections.map.ReferenceMap;
056import org.xml.sax.SAXException;
057
058public final class S3 implements S3Constants {
059
060        private static final String DEFAULT_URL="s3.amazonaws.com";
061        
062        private String secretAccessKey;
063        private String accessKeyId;
064        private TimeZone timezone;
065        private String host;
066
067
068        private static final Map<String,S3Info> infos=new ReferenceMap();
069        private static final Map<String,AccessControlPolicy> acps=new ReferenceMap();
070
071        public static final int MAX_REDIRECT = 15;
072
073        public String toString(){
074                return "secretAccessKey:"+secretAccessKey+";accessKeyId:"+accessKeyId+";host:"+host+";timezone:"+
075                (timezone==null?"":timezone.getID());
076        }
077        
078        public String hash() {
079                try {
080                        return Md5.getDigestAsString(toString());
081                } catch (IOException e) {
082                        return null;
083                }
084        }
085
086        public S3(String secretAccessKey, String accessKeyId,TimeZone tz) {
087                host=DEFAULT_URL;
088                this.secretAccessKey = secretAccessKey;
089                this.accessKeyId = accessKeyId;
090                this.timezone = tz;
091                //testFinal();
092        }
093        
094        
095
096        public S3() {
097                
098                //testFinal();
099        }
100        
101        /**
102         * @return the secretAccessKey
103         * @throws S3Exception 
104         */
105        String getSecretAccessKeyValidate() throws S3Exception {
106                if(StringUtil.isEmpty(secretAccessKey))
107                        throw new S3Exception("secretAccessKey is not defined, define in "+Constants.APP_CFC+" (s3.awsSecretKey) or as part of the path.");
108                return secretAccessKey;
109        }
110        
111        /**
112         * @return the accessKeyId
113         * @throws S3Exception 
114         */
115        String getAccessKeyIdValidate() throws S3Exception {
116                if(StringUtil.isEmpty(accessKeyId))
117                        throw new S3Exception("accessKeyId is not defined, define in "+Constants.APP_CFC+" (this.s3.accessKeyId) or as part of the path.");
118                return accessKeyId;
119        }
120        
121        String getSecretAccessKey() {
122                return secretAccessKey;
123        }
124        
125        /**
126         * @return the accessKeyId
127         * @throws S3Exception 
128         */
129        String getAccessKeyId() {
130                return accessKeyId;
131        }
132
133        /**
134         * @return the tz
135         */
136        TimeZone getTimeZone() {
137                if(timezone==null)timezone=ThreadLocalPageContext.getTimeZone();
138                return timezone;
139        }
140        
141        private static byte[] HMAC_SHA1(String key, String message,String charset) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {
142                
143                        SecretKeySpec sks = new SecretKeySpec(key.getBytes(charset),"HmacSHA1");
144                        Mac mac = Mac.getInstance(sks.getAlgorithm());
145                        mac.init(sks);
146                        mac.update(message.getBytes(charset));
147                        return mac.doFinal();
148                
149        }
150
151        private static String createSignature(String str, String secretAccessKey,String charset) throws InvalidKeyException, NoSuchAlgorithmException, IOException {
152                //str=StringUtil.replace(str, "\\n", String.valueOf((char)10), false);
153                byte[] digest = HMAC_SHA1(secretAccessKey,str,charset);
154                try {
155                        return Caster.toB64(digest);
156                } catch (Throwable t) {
157                ExceptionUtil.rethrowIfNecessary(t);
158                        throw new IOException(t.getMessage());
159                }
160        }
161        
162        public InputStream listBucketsRaw() throws MalformedURLException, IOException, InvalidKeyException, NoSuchAlgorithmException {
163                String dateTimeString = Util.toHTTPTimeString();
164                String signature = createSignature("GET\n\n\n"+dateTimeString+"\n/", getSecretAccessKeyValidate(), "iso-8859-1");
165                
166                HTTPResponse rsp = HTTPEngine3Impl.get(new URL("http://"+host), null, null, -1,MAX_REDIRECT, null, "Lucee", null,
167                                new Header[]{
168                                        header("Date",dateTimeString),
169                                        header("Authorization","AWS "+getAccessKeyIdValidate()+":"+signature)
170                                }
171                );
172                return rsp.getContentAsStream();
173                
174        }
175        
176
177        public HTTPResponse head(String bucketName, String objectName) throws MalformedURLException, IOException, InvalidKeyException, NoSuchAlgorithmException {
178                bucketName=checkBucket(bucketName);
179                boolean hasObj=!StringUtil.isEmpty(objectName);
180                if(hasObj)objectName=checkObjectName(objectName);
181                
182                String dateTimeString = Util.toHTTPTimeString();
183                String signature = createSignature("HEAD\n\n\n"+dateTimeString+"\n/"+bucketName+"/"+(hasObj?objectName:""), getSecretAccessKeyValidate(), "iso-8859-1");
184                
185                
186                List<Header> headers=new ArrayList<Header>();
187                headers.add(header("Date",dateTimeString));
188                headers.add(header("Authorization","AWS "+getAccessKeyIdValidate()+":"+signature));
189                headers.add(header("Host",bucketName+"."+host));
190                
191                String strUrl="http://"+bucketName+"."+host+"/";
192                //if(Util.hasUpperCase(bucketName))strUrl="http://"+host+"/"+bucketName+"/";
193                if(hasObj) {
194                        strUrl+=objectName;
195                }
196                HTTPResponse method = HTTPEngine3Impl.head(new URL(strUrl), null, null, -1,MAX_REDIRECT, null, "Lucee", null,headers.toArray(new Header[headers.size()]));
197                return method;
198                
199        }
200        
201        
202        
203        
204        public InputStream aclRaw(String bucketName, String objectName) throws MalformedURLException, IOException, InvalidKeyException, NoSuchAlgorithmException {
205                bucketName=checkBucket(bucketName);
206                boolean hasObj=!StringUtil.isEmpty(objectName);
207                if(hasObj)objectName=checkObjectName(objectName);
208                
209                String dateTimeString = Util.toHTTPTimeString();
210                String signature = createSignature("GET\n\n\n"+dateTimeString+"\n/"+bucketName+"/"+(hasObj?objectName:"")+"?acl", getSecretAccessKeyValidate(), "iso-8859-1");
211                
212                
213                List<Header> headers=new ArrayList<Header>();
214                headers.add(header("Date",dateTimeString));
215                headers.add(header("Authorization","AWS "+getAccessKeyIdValidate()+":"+signature));
216                headers.add(header("Host",bucketName+"."+host));
217                
218                String strUrl="http://"+bucketName+"."+host+"/";
219                //if(Util.hasUpperCase(bucketName))strUrl="http://"+host+"/"+bucketName+"/";
220                if(hasObj) {
221                        strUrl+=objectName;
222                }
223                strUrl+="?acl";
224                
225                HTTPResponse method = HTTPEngine3Impl.get(new URL(strUrl), null, null, -1,MAX_REDIRECT, null, "Lucee", null,headers.toArray(new Header[headers.size()]));
226                return method.getContentAsStream();
227                
228        }
229        
230        public AccessControlPolicy getAccessControlPolicy(String bucketName, String objectName) throws InvalidKeyException, MalformedURLException, NoSuchAlgorithmException, IOException, SAXException {
231                InputStream raw = aclRaw(bucketName,objectName);
232                //print.o(IOUtil.toString(raw, null));
233                ACLFactory factory=new ACLFactory(raw, this);
234                return factory.getAccessControlPolicy();
235        }
236        
237        
238
239
240        public void setAccessControlPolicy(String bucketName, String objectName,AccessControlPolicy acp) throws IOException, InvalidKeyException, NoSuchAlgorithmException, SAXException {
241                bucketName=checkBucket(bucketName);
242                boolean hasObj=!StringUtil.isEmpty(objectName);
243                if(hasObj)objectName=checkObjectName(objectName);
244                
245
246                Entity re = HTTPEngine3Impl.getByteArrayEntity(acp.toXMLString().getBytes(CharsetUtil.ISO88591),"text/html");
247                
248                
249                String dateTimeString = Util.toHTTPTimeString();
250                
251                
252                String cs = "PUT\n\n"+re.contentType()+"\n"+dateTimeString+"\n/"+bucketName+"/"+(hasObj?objectName:"")+"?acl";
253                String signature = createSignature(cs, getSecretAccessKeyValidate(), "iso-8859-1");
254                Header[] headers = new Header[]{
255                                header("Content-Type",re.contentType()),
256                                header("Content-Length",Long.toString(re.contentLength())),
257                                header("Date",dateTimeString),
258                                header("Authorization","AWS "+getAccessKeyIdValidate()+":"+signature),
259                };
260                
261                String strUrl="http://"+bucketName+"."+host+"/";
262                if(hasObj) {
263                        strUrl+=objectName;
264                }
265                strUrl+="?acl";
266                
267                
268                
269                HTTPResponse method = HTTPEngine3Impl.put(new URL(strUrl), null, null, -1,MAX_REDIRECT, null, 
270                                "Lucee", null,headers,re);
271                if(method.getStatusCode()!=200){
272                        new ErrorFactory(method.getContentAsStream());
273                }
274                
275                
276        }
277        
278        public InputStream listContentsRaw(String bucketName,String prefix,String marker,int maxKeys) throws MalformedURLException, IOException, InvalidKeyException, NoSuchAlgorithmException {
279                bucketName=checkBucket(bucketName);
280                String dateTimeString = Util.toHTTPTimeString();
281                String signature = createSignature("GET\n\n\n"+dateTimeString+"\n/"+bucketName+"/", getSecretAccessKeyValidate(), "iso-8859-1");
282                
283                
284                List<Header> headers=new ArrayList<Header>();
285                headers.add(header("Date",dateTimeString));
286                headers.add(header("Authorization","AWS "+getAccessKeyIdValidate()+":"+signature));
287                headers.add(header("Host",bucketName+"."+host));
288                
289                String strUrl="http://"+bucketName+"."+host+"/";
290                if(Util.hasUpperCase(bucketName))strUrl="http://"+host+"/"+bucketName+"/";
291                
292                
293                char amp='?';
294                if(!Util.isEmpty(prefix)){
295                        strUrl+=amp+"prefix="+encodeEL(prefix);
296                        amp='&';
297                }
298                if(!Util.isEmpty(marker)) {
299                        strUrl+=amp+"marker="+encodeEL(marker);
300                        amp='&';
301                }
302                if(maxKeys!=-1) {
303                        strUrl+=amp+"max-keys="+maxKeys;
304                        amp='&';
305                }
306                
307                HTTPResponse method = HTTPEngine3Impl.get(new URL(strUrl), null, null, -1,MAX_REDIRECT, null, "Lucee", null,headers.toArray(new Header[headers.size()]));
308                return method.getContentAsStream();
309        }
310        
311
312        public Content[] listContents(String bucketName,String prefix) throws InvalidKeyException, MalformedURLException, NoSuchAlgorithmException, IOException, SAXException {
313                String marker=null,last=null;
314                ContentFactory factory;
315                Content[] contents;
316                List<Content[]> list = new ArrayList<Content[]>();
317                int size=0;
318                while(true) {
319                        factory = new ContentFactory(listContentsRaw(bucketName, prefix, marker, -1),this);
320                        contents = factory.getContents();
321                        list.add(contents);
322                        size+=contents.length;
323                        if(factory.isTruncated() && contents.length>0) {
324                                last=marker;
325                                marker=contents[contents.length-1].getKey();
326                                if(marker.equals(last))break;
327                        }
328                        else break;
329                }
330                
331                if(list.size()==1) return list.get(0);
332                if(list.size()==0) return new Content[0];
333                
334                Content[] rtn=new Content[size];
335                Iterator<Content[]> it = list.iterator();
336                int index=0;
337                while(it.hasNext()) {
338                        contents=it.next();
339                        for(int i=0;i<contents.length;i++) {
340                                rtn[index++]=contents[i];
341                        }
342                }
343                
344                return rtn;
345        }
346
347        public Content[] listContents(String bucketName,String prefix,String marker,int maxKeys) throws InvalidKeyException, MalformedURLException, NoSuchAlgorithmException, IOException, SAXException {
348                InputStream raw = listContentsRaw(bucketName, prefix, marker, maxKeys);
349                //print.o(IOUtil.toString(raw, null));
350                ContentFactory factory = new ContentFactory(raw,this);
351                return factory.getContents();
352        }
353        
354        public Bucket[] listBuckets() throws InvalidKeyException, MalformedURLException, NoSuchAlgorithmException, IOException, SAXException {
355                InputStream raw = listBucketsRaw();
356                //print.o(IOUtil.toString(raw, null));
357                BucketFactory factory = new BucketFactory(raw,this);
358                return factory.getBuckets();
359        }
360        
361        public void putBuckets(String bucketName,int acl, int storage) throws IOException, InvalidKeyException, NoSuchAlgorithmException, SAXException {
362                String strXML = "";
363                if(storage==STORAGE_EU) {
364                        strXML="<CreateBucketConfiguration><LocationConstraint>EU</LocationConstraint></CreateBucketConfiguration>";
365                }
366                
367                byte[] barr = strXML.getBytes(CharsetUtil.ISO88591);
368                put(bucketName, null, acl,HTTPEngine3Impl.getByteArrayEntity(barr,"text/html"));        
369        }
370        
371        /*public void putObject(String bucketName,String objectName,int acl,Resource res) throws IOException, InvalidKeyException, NoSuchAlgorithmException, PageException, SAXException, EncoderException {
372                String contentType = IOUtil.getMimeType(res, "application");
373                InputStream is = null;
374                try {
375                        is = res.getInputStream();
376                        put(bucketName, objectName, acl, is, contentType);
377                }
378                finally {
379                        IOUtil.closeEL(is);
380                }
381        }*/
382        /*
383        public void put(String bucketName,String objectName,int acl, InputStream is,long length, String contentType) throws IOException, InvalidKeyException, NoSuchAlgorithmException, PageException, SAXException, EncoderException {
384                put(bucketName, objectName, acl, HTTPUtil.toRequestEntity(is),length, contentType);
385        }*/
386                
387        public void put(String bucketName,String objectName,int acl, Entity re) throws IOException, InvalidKeyException, NoSuchAlgorithmException, SAXException {
388                bucketName=checkBucket(bucketName);
389                objectName=checkObjectName(objectName);
390                
391                String dateTimeString = Util.toHTTPTimeString();
392                // Create a canonical string to send based on operation requested 
393                String cs = "PUT\n\n"+re.contentType()+"\n"+dateTimeString+"\nx-amz-acl:"+toStringACL(acl)+"\n/"+bucketName+"/"+objectName;
394                String signature = createSignature(cs, getSecretAccessKeyValidate(), "iso-8859-1");
395                Header[] headers = new Header[]{
396                                header("Content-Type",re.contentType()),
397                                header("Content-Length",Long.toString(re.contentLength())),
398                                header("Date",dateTimeString),
399                                header("x-amz-acl",toStringACL(acl)),
400                                header("Authorization","AWS "+getAccessKeyIdValidate()+":"+signature),
401                };
402                
403                String strUrl="http://"+bucketName+"."+host+"/"+objectName;
404                if(Util.hasUpperCase(bucketName))strUrl="http://"+host+"/"+bucketName+"/"+objectName;
405                
406                
407                
408                HTTPResponse method = HTTPEngine3Impl.put(new URL(strUrl), null, null, -1,MAX_REDIRECT, null, 
409                                "Lucee", null,headers,re);
410                if(method.getStatusCode()!=200){
411                        new ErrorFactory(method.getContentAsStream());
412                }
413                
414                
415        }
416                
417        public HttpURLConnection preput(String bucketName,String objectName,int acl, String contentType) throws IOException, InvalidKeyException, NoSuchAlgorithmException {
418                bucketName=checkBucket(bucketName);
419                objectName=checkObjectName(objectName);
420                
421                String dateTimeString = Util.toHTTPTimeString();
422                // Create a canonical string to send based on operation requested 
423                String cs = "PUT\n\n"+contentType+"\n"+dateTimeString+"\nx-amz-acl:"+toStringACL(acl)+"\n/"+bucketName+"/"+objectName;
424                String signature = createSignature(cs, getSecretAccessKeyValidate(), "iso-8859-1");
425                
426                String strUrl="http://"+bucketName+"."+host+"/"+objectName;
427                if(Util.hasUpperCase(bucketName))strUrl="http://"+host+"/"+bucketName+"/"+objectName;
428                
429                URL url = new URL(strUrl);
430                
431                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
432                conn.setRequestMethod("PUT");
433                
434                conn.setFixedLengthStreamingMode(227422142);
435                conn.setDoOutput(true);
436                conn.setUseCaches(false);
437                conn.setRequestProperty("CONTENT-TYPE", contentType);
438                conn.setRequestProperty("USER-AGENT", "S3 Resource");        
439                //conn.setRequestProperty("Transfer-Encoding", "chunked" );
440                conn.setRequestProperty("Date", dateTimeString);
441                conn.setRequestProperty("x-amz-acl", toStringACL(acl));
442                conn.setRequestProperty("Authorization", "AWS "+getAccessKeyIdValidate()+":"+signature);
443                return conn;
444        }
445
446        public String getObjectLink(String bucketName,String objectName,int secondsValid) throws InvalidKeyException, NoSuchAlgorithmException, IOException {
447                bucketName=checkBucket(bucketName);
448                objectName=checkObjectName(objectName);
449                
450                long epoch = (System.currentTimeMillis()/1000)+(secondsValid);
451                String cs = "GET\n\n\n"+epoch+"\n/"+bucketName+"/"+objectName;
452                String signature = createSignature(cs, getSecretAccessKeyValidate(), "iso-8859-1");
453                
454                String strUrl="http://"+bucketName+"."+host+"/"+objectName;
455                if(Util.hasUpperCase(bucketName))strUrl="http://"+host+"/"+bucketName+"/"+objectName;
456                
457                
458                return strUrl+"?AWSAccessKeyId="+getAccessKeyIdValidate()+"&Expires="+epoch+"&Signature="+signature;
459        }
460
461        public InputStream getInputStream(String bucketName,String objectName) throws InvalidKeyException, NoSuchAlgorithmException, IOException, SAXException  {
462                return getData(bucketName, objectName).getContentAsStream();
463        }
464        
465        public Map<String, String> getMetadata(String bucketName,String objectName) throws InvalidKeyException, NoSuchAlgorithmException, IOException, SAXException  {
466                HTTPResponse method = getData(bucketName, objectName);
467                Header[] headers = method.getAllHeaders();
468                Map<String,String> rtn=new HashMap<String, String>();
469                String name;
470                if(headers!=null)for(int i=0;i<headers.length;i++){
471                        name=headers[i].getName();
472                        if(name.startsWith("x-amz-meta-"))
473                                rtn.put(name.substring(11), headers[i].getValue());
474                }
475                return rtn;
476        }
477        
478        private HTTPResponse getData(String bucketName,String objectName) throws InvalidKeyException, NoSuchAlgorithmException, IOException, SAXException  {
479                bucketName=checkBucket(bucketName);
480                objectName=checkObjectName(objectName);
481                
482                String dateTimeString = Util.toHTTPTimeString();
483                //long epoch = (System.currentTimeMillis()/1000)+6000;
484                String cs = "GET\n\n\n"+dateTimeString+"\n/"+bucketName+"/"+objectName;
485                    
486                
487                String signature = createSignature(cs, getSecretAccessKeyValidate(), "iso-8859-1");
488                
489                String strUrl="http://"+bucketName+"."+host+"/"+objectName;
490                if(Util.hasUpperCase(bucketName))strUrl="http://"+host+"/"+bucketName+"/"+objectName;
491                URL url = new URL(strUrl);
492                
493                
494                HTTPResponse method = HTTPEngine3Impl.get(url, null, null, -1,MAX_REDIRECT, null, "Lucee", null,
495                                new Header[]{
496                                header("Date",dateTimeString),
497                                header("Host",bucketName+"."+host),
498                                header("Authorization","AWS "+getAccessKeyIdValidate()+":"+signature)
499                                }
500                );
501                if(method.getStatusCode()!=200)
502                        new ErrorFactory(method.getContentAsStream());
503                return method;
504        }
505
506        public void delete(String bucketName, String objectName) throws IOException, InvalidKeyException, NoSuchAlgorithmException, SAXException {
507                bucketName=checkBucket(bucketName);
508                objectName = checkObjectName(objectName);
509
510                String dateTimeString = Util.toHTTPTimeString();
511                // Create a canonical string to send based on operation requested 
512                String cs ="DELETE\n\n\n"+dateTimeString+"\n/"+bucketName+"/"+objectName;
513                //print.out(cs);
514                String signature = createSignature(cs, getSecretAccessKeyValidate(), "iso-8859-1");
515                
516                Header[] headers =new Header[]{
517                                header("Date",dateTimeString),
518                                header("Authorization","AWS "+getAccessKeyIdValidate()+":"+signature),
519                };
520                
521                String strUrl="http://"+bucketName+"."+host+"/"+objectName;
522                if(Util.hasUpperCase(bucketName))strUrl="http://"+host+"/"+bucketName+"/"+objectName;
523                
524                
525                
526                HTTPResponse rsp = HTTPEngine3Impl.delete(new URL(strUrl), null, null, -1,MAX_REDIRECT, null, "Lucee",  null,headers);
527                
528                if(rsp.getStatusCode()!=200)
529                        new ErrorFactory(rsp.getContentAsStream());
530        }
531
532        
533        
534        
535        
536        
537        // --------------------------------
538        public static String toStringACL(int acl) throws S3Exception {
539                switch(acl) {
540                        case ACL_AUTH_READ:return "authenticated-read";
541                        case ACL_PUBLIC_READ:return "public-read";
542                        case ACL_PRIVATE:return "private";
543                        case ACL_PUBLIC_READ_WRITE:return "public-read-write";
544                }
545                throw new S3Exception("invalid acl definition");
546        }
547
548        public static String toStringStorage(int storage) throws S3Exception {
549                String s = toStringStorage(storage, null);
550                if(s==null)
551                        throw new S3Exception("invalid storage definition");
552                return s;
553        }
554        public static String toStringStorage(int storage, String defaultValue) {
555                switch(storage) {
556                        case STORAGE_EU:return "eu";
557                        case STORAGE_US:return "us";
558                        case STORAGE_US_WEST:return "us-west";
559                }
560                return defaultValue;
561        }
562        
563        public static int toIntACL(String acl) throws S3Exception {
564                acl=acl.toLowerCase().trim();
565                if("public-read".equals(acl)) return ACL_PUBLIC_READ;
566                if("private".equals(acl)) return ACL_PRIVATE;
567                if("public-read-write".equals(acl)) return ACL_PUBLIC_READ_WRITE;
568                if("authenticated-read".equals(acl)) return ACL_AUTH_READ;
569                
570                if("public_read".equals(acl)) return ACL_PUBLIC_READ;
571                if("public_read_write".equals(acl)) return ACL_PUBLIC_READ_WRITE;
572                if("authenticated_read".equals(acl)) return ACL_AUTH_READ;
573                
574                if("publicread".equals(acl)) return ACL_PUBLIC_READ;
575                if("publicreadwrite".equals(acl)) return ACL_PUBLIC_READ_WRITE;
576                if("authenticatedread".equals(acl)) return ACL_AUTH_READ;
577                
578                throw new S3Exception("invalid acl value, valid values are [public-read, private, public-read-write, authenticated-read]");
579        }
580
581        public static int toIntStorage(String storage) throws S3Exception {
582                int s=toIntStorage(storage,-1);
583                if(s==-1)
584                        throw new S3Exception("invalid storage value, valid values are [eu,us,us-west]");
585                return s;
586        }
587        public static int toIntStorage(String storage, int defaultValue) {
588                storage=storage.toLowerCase().trim();
589                if("us".equals(storage)) return STORAGE_US;
590                if("usa".equals(storage)) return STORAGE_US;
591                if("eu".equals(storage)) return STORAGE_EU;
592                
593                if("u.s.".equals(storage)) return STORAGE_US;
594                if("u.s.a.".equals(storage)) return STORAGE_US;
595                if("europe.".equals(storage)) return STORAGE_EU;
596                if("euro.".equals(storage)) return STORAGE_EU;
597                if("e.u.".equals(storage)) return STORAGE_EU;
598                if("united states of america".equals(storage)) return STORAGE_US;
599                
600                if("us-west".equals(storage)) return STORAGE_US_WEST;
601                if("usa-west".equals(storage)) return STORAGE_US_WEST;
602                
603                
604                return defaultValue;
605        }
606        
607
608        private String checkObjectName(String objectName) throws UnsupportedEncodingException {
609                if(Util.isEmpty(objectName)) return "";
610                if(objectName.startsWith("/"))objectName=objectName.substring(1);
611                return encode(objectName);
612        }
613
614        private String checkBucket(String name) {
615                /*if(!Decision.isVariableName(name)) 
616                        throw new S3Exception("invalid bucket name definition ["+name+"], name should only contain letters, digits, dashes and underscores");
617                
618                if(name.length()<3 || name.length()>255) 
619                        throw new S3Exception("invalid bucket name definition ["+name+"], the length of a bucket name must be between 3 and 255");
620                */
621                
622                return encodeEL(name);
623        }
624
625        private String encodeEL(String name) {
626                try {
627                        return encode(name);
628                
629                } catch (UnsupportedEncodingException e) {
630                        return name;
631                }
632        }
633        private String encode(String name) throws UnsupportedEncodingException {
634                return URLEncoder.encode(name,"UTF-8");
635        }
636
637        /**
638         * @param secretAccessKey the secretAccessKey to set
639         */
640        public void setSecretAccessKey(String secretAccessKey) {
641                this.secretAccessKey = secretAccessKey;
642        }
643
644        /**
645         * @param accessKeyId the accessKeyId to set
646         */
647        public void setAccessKeyId(String accessKeyId) {
648                this.accessKeyId = accessKeyId;
649        }
650
651        /**
652         * @param url the url to set
653         */
654        public void setHost(String host) {
655                this.host=host;
656        }
657
658        public String getHost() {
659                return host;
660        }
661
662        public S3Info getInfo(String path) {
663                return infos.get(toKey(path));
664        }
665
666        public void setInfo(String path,S3Info info) {
667                infos.put(toKey(path),info);
668        }
669
670        public AccessControlPolicy getACP(String path) {
671                return acps.get(toKey(path));
672        }
673
674        public void setACP(String path,AccessControlPolicy acp) {
675                acps.put(toKey(path),acp);
676        }
677
678        public void releaseCache(String path) {
679                Object k = toKey(path);
680                infos.remove(k);
681                acps.remove(k);
682        }
683
684        private String toKey(String path) {
685                return toString()+":"+path.toLowerCase();
686        }
687        
688        private Header header(String name, String value) {
689                return HTTPEngine3Impl.header(name,value);
690        }
691
692        public static DateTime toDate(String strDate, TimeZone tz) throws PageException {
693                if(strDate.endsWith("Z"))
694                        strDate=strDate.substring(0,strDate.length()-1);
695                return Caster.toDate(strDate, tz);
696        }
697}
698
699
700
701
702