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