/*************************************************************************
* Copyright 2009-2014 Eucalyptus Systems, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
* Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta
* CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need
* additional information or have any questions.
************************************************************************/
package com.eucalyptus.cloudformation.template.url;
import com.eucalyptus.cloudformation.ValidationErrorException;
import com.google.common.net.InetAddresses;
import java.net.URL;
import java.net.URLDecoder;
import java.util.StringTokenizer;
/**
* Created by ethomas on 9/21/14.
*/
public class S3Helper {
public static BucketAndKey getBucketAndKeyFromUrl(URL url, String[] validServicePaths, String[] validHostBucketSuffixes, String[] validDomains) throws ValidationErrorException {
if (url.getHost() == null || url.getPath() == null) {
throw new ValidationErrorException("Invalid S3 " + url);
}
// We may not get every form of possible URLs right at this point, but we will allow 3 types
// 1) http://<ip address>/<service_path>/bucket/key (No address checks will be done)
// 2) Valid external domain, including something with ".objectstorage" or ".walrus". In this case bucket name is first, and key is path
// 3) Valid external domain, must start with objectstorage or walrus. In this case bucket name is part of the path
// 4) Valid external domain, with no objectstorage or walrus. In this case there must be a service path, and /bucket/key will be assumed to be there.
if (InetAddresses.isInetAddress(url.getHost())) {
// must end in valid service path...
for (String validServicePath : validServicePaths) {
if (url.getPath().startsWith(validServicePath)) {
return getBucketAndKeyFromPath(url.getPath().substring(validServicePath.length()), url);
}
}
throw new ValidationErrorException("Invalid S3 url" + url + ", contains IP address but no OSG service path");
}
for (String validDomain : validDomains) {
// DNS case insensitive
String hostLower = url.getHost().toLowerCase();
String validDomainLower = validDomain.toLowerCase();
if (hostLower.endsWith(validDomainLower)) {
// check bucket style...
for (String validHostBucketSuffix : validHostBucketSuffixes) {
String validHostNameBucketNameSuffixLower = validHostBucketSuffix.toLowerCase();
if (hostLower.contains("." + validHostNameBucketNameSuffixLower)) {
String bucket = hostLower.substring(0, hostLower.indexOf("." + validHostNameBucketNameSuffixLower));
return getBucketAndKeyFromPath(bucket + "/" + url.getPath(), url);
}
}
// check beginnings
for (String validHostBucketSuffix : validHostBucketSuffixes) {
String validHostNameBucketNameSuffixLower = validHostBucketSuffix.toLowerCase();
if (hostLower.startsWith(validHostNameBucketNameSuffixLower + ".")) {
return getBucketAndKeyFromPath(url.getPath(), url);
}
}
// check service path
for (String validServicePath : validServicePaths) {
if (url.getPath().startsWith(validServicePath)) {
return getBucketAndKeyFromPath(url.getPath().substring(validServicePath.length()), url);
}
}
}
}
throw new ValidationErrorException("Invalid S3 url " + url + ", does not match any known S3 domains");
}
private static BucketAndKey getBucketAndKeyFromPath(String path, URL url) throws ValidationErrorException {
try {
StringTokenizer stok = new StringTokenizer(path, "/");
String bucket = URLDecoder.decode(stok.nextToken());
String delimiter = "";
StringBuilder keyBuilder = new StringBuilder();
do {
keyBuilder.append(delimiter).append(URLDecoder.decode(stok.nextToken()));
delimiter = "/";
} while (stok.hasMoreTokens()); // must have at least one token for key, otherwise not valid
return new BucketAndKey(bucket, keyBuilder.toString());
} catch (Exception ex) {
throw new ValidationErrorException("S3 URL " + url + " parses to invalid bucket/key pairs.");
}
}
public static class BucketAndKey {
String bucket;
String key;
BucketAndKey(String bucket, String key) {
this.bucket = bucket;
this.key = key;
}
public String getBucket() {
return bucket;
}
public String getKey() {
return key;
}
@Override
public String toString() {
return "BucketAndKey{" +
"bucket='" + bucket + '\'' +
", key='" + key + '\'' +
'}';
}
}
// public static void main(String[] args) throws MalformedURLException, ValidationErrorException {
// String[] validDomains = new String[]{"h-14.autoqa.qa1.eucalyptus-systems.com"};
// String[] validServicePaths = new String[]{"/services/objectstorage","/services/walrus"};
// String[] validHostNameBucketNameSuffixes = new String[]{"walrus","objectstorage"};
// System.out.println(getBucketAndKeyFromUrl(new URL("http://10.0.0.10/services/objectstorage/path/to/my/key/a+b"), validServicePaths, validHostNameBucketNameSuffixes, validDomains));
// System.out.println(getBucketAndKeyFromUrl(new URL("http://10.0.0.10/services/walrus/path/to/my/key/a+b"), validServicePaths, validHostNameBucketNameSuffixes, validDomains));
// System.out.println(getBucketAndKeyFromUrl(new URL("http://boots.objectstorage.h-14.autoqa.qa1.eucalyptus-systems.com/key"), validServicePaths, validHostNameBucketNameSuffixes, validDomains));
// System.out.println(getBucketAndKeyFromUrl(new URL("http://boots.walrus.h-14.autoqa.qa1.eucalyptus-systems.com/key"), validServicePaths, validHostNameBucketNameSuffixes, validDomains));
// System.out.println(getBucketAndKeyFromUrl(new URL("http://walrus.h-14.autoqa.qa1.eucalyptus-systems.com/boots/key"), validServicePaths, validHostNameBucketNameSuffixes, validDomains));
// System.out.println(getBucketAndKeyFromUrl(new URL("http://walrus.h-14.autoqa.qa1.eucalyptus-systems.com/boots/key"), validServicePaths, validHostNameBucketNameSuffixes, validDomains));
// System.out.println(getBucketAndKeyFromUrl(new URL("http://walrus.h-15.autoqa.qa1.eucalyptus-systems.com/boots/key"), validServicePaths, validHostNameBucketNameSuffixes, validDomains));
// }
}