/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.milton.common;
import io.milton.resource.CollectionResource;
import io.milton.resource.Resource;
import io.milton.http.exceptions.BadRequestException;
import io.milton.http.exceptions.NotAuthorizedException;
import io.milton.http.webdav.Dest;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
public class Utils {
public static final Charset UTF8 = Charset.forName("UTF-8");
private final static char[] hexDigits = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
public static Resource findChild(Resource parent, Path path) throws NotAuthorizedException, BadRequestException {
return _findChild(parent, path.getParts(), 0);
}
/**
* does percentage decoding on a path portion of a url
*
* E.g. /foo > /foo /with%20space -> /with space
*
* @param href
*/
public static String decodePath(String href) {
// For IPv6
href = href.replace("[", "%5B").replace("]", "%5D");
// Seems that some client apps send spaces.. maybe..
href = href.replace(" ", "%20");
try {
if (href.startsWith("/")) {
URI uri = new URI("http://anything.com" + href);
return uri.getPath();
} else {
URI uri = new URI("http://anything.com/" + href);
String s = uri.getPath();
return s.substring(1);
}
} catch (URISyntaxException ex) {
throw new RuntimeException(ex);
}
}
private static Resource _findChild(Resource parent, String[] arr, int i) throws NotAuthorizedException, BadRequestException {
if (parent instanceof CollectionResource) {
CollectionResource col = (CollectionResource) parent;
String childName = arr[i];
Resource child = col.child(childName);
if (child == null) {
return null;
} else {
if (i < arr.length - 1) {
return _findChild(child, arr, i + 1);
} else {
return child;
}
}
} else {
return null;
}
}
public static Date now() {
return new Date();
}
public static Date addSeconds(Date dt, long seconds) {
return addSeconds(dt, (int) seconds);
}
public static Date addSeconds(Date dt, int seconds) {
Calendar cal = Calendar.getInstance();
cal.setTime(dt);
cal.add(Calendar.SECOND, seconds);
return cal.getTime();
}
public static String getProtocol(String url) {
String protocol = url.substring(0, url.indexOf(":"));
return protocol;
}
public static String escapeXml(String s) {
s = s.replaceAll("\"", """);
s = s.replaceAll("&", "&");
s = s.replaceAll("'", "'");
s = s.replaceAll("<", "<");
s = s.replaceAll(">", ">");
// s = s.replaceAll("�", "ae");
return s;
}
/**
* this is a modified verion of java.net.URI.encode(s)
*
* the java.net version only encodes characters over \u0080, but this
* version also applies encoding to characters below char 48
*
* this method should be applied only to parts of a URL, not the whole URL
* as forward slashes, semi-colons etc will be encoded
*
* by "part of url" i mean the bits between slashes
*
* @param s
*/
public static String percentEncode(String s) {
//s = _percentEncode( s ); // the original method, from java.net
s = encodeURL(s, "UTF-8");
return s;
}
/**
* This method has been provided by Andr� Kunert - looks a bit better then
* my shabby implementation! BM
*
* @param str
* @param charset
* @return
*/
public static String encodeURL(String str, String charset) {
StringBuilder buf = new StringBuilder();
byte[] daten;
try {
daten = charset == null ? str.getBytes() : str.getBytes(charset);
} catch (Exception e) {
daten = str.getBytes();
}
int length = daten.length;
for (int i = 0; i < length; i++) {
char c = (char) (daten[i] & 0xFF);
switch (c) {
case '-':
case '_':
case '.':
case '*':
// case ':':
// case '/':
buf.append(c);
break;
default:
if (('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) {
buf.append(c);
} else {
buf.append('%');
buf.append(hexDigits[(c >> 4) & 0x0F]);
buf.append(hexDigits[c & 0x0F]);
}
}
}
return buf.toString();
}
private static String _percentEncode(String s) {
int n = s.length();
if (n == 0) {
return s;
}
String ns = normalize(s);
ByteBuffer bb = null;
bb = Charset.forName("UTF-8").encode(CharBuffer.wrap(ns));
StringBuilder sb = new StringBuilder();
while (bb.hasRemaining()) {
int b = bb.get() & 0xff;
// **ONLY** unreserved characters can be added unencoded
if (isUnReserved(b)) {
sb.append((char) b);
} else {
appendEscape(sb, (byte) b);
}
}
return sb.toString();
}
/**
* Range 1 - dec 65 - 90 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
*
* Range 2 - dec 97 - 122 a b c d e f g h i j k l m n o p q r s t u v w x y
* z
*
* Range 3 - dec 48 - 57 0 1 2 3 4 5 6 7 8 9
*
* 45 46 95 126 - . _ ~
*
* @param b
* @return
*/
private static boolean isUnReserved(int b) {
return inRange(b, 65, 90)
|| inRange(b, 97, 122)
|| inRange(b, 48, 57)
|| inList(b, 45, 46, 95, 126);
}
private static boolean inRange(int b, int lower, int upper) {
return b >= lower && b <= upper;
}
private static boolean inList(int b, int... nums) {
for (int i : nums) {
if (b == i) {
return true;
}
}
return false;
}
private static boolean isSquareBracket(int b) {
return b == 0x5B || b == 0x5D;
}
private static void appendEscape(StringBuilder sb, byte b) {
sb.append('%');
sb.append(hexDigits[(b >> 4) & 0x0f]);
sb.append(hexDigits[(b) & 0x0f]);
}
public static Date mostRecent(Date... dates) {
if (dates == null || dates.length == 0) {
return null;
}
Date recent = dates[0];
for (Date dt : dates) {
if (dt.getTime() > recent.getTime()) {
recent = dt;
}
}
return recent;
}
/**
* java.text.Normalizer is only available for jdk 1.6. Since it isnt really
* required and we don't want to annoy our 1.5 colleagues, this is commented
* out.
*
* It isnt really needed because URLs still get consistently encoded and
* decoded without it. Its just that you might get different results on
* different platforms
*
* @param s
* @return
*/
private static String normalize(String s) {
//return Normalizer.normalize(s, Normalizer.Form.NFC);
return s;
}
/**
* Convert the list of strings to a comma separated string
*
* @param list
* @return - a comma seperated list of values
*/
public static String toCsv(Collection<String> list) {
return toCsv(list, true);
}
public static String toCsv(Collection<String> list, boolean addSpace) {
if (list == null || list.isEmpty()) {
return null;
}
String res = "";
Iterator<String> it = list.iterator();
while (it.hasNext()) {
res += it.next();
if (it.hasNext()) {
if (addSpace) {
res += ", ";
} else {
res += ",";
}
}
}
return res;
}
public static String stripServer(String href) {
if (href.startsWith("http")) {
return href.substring(href.indexOf("/", 8));
} else {
return href;
}
}
/**
* Used for parsing uploaded file names. MS web browsers tend to transmit
* the complete path for an uploaded file, but we generally only want to
* know the last part of the path.
*
* TODO: move this into milton
*
* @param s
* @return
*/
public static String truncateFileName(String agent, String s) {
if (agent == null) {
return s;
} else {
if (agent.contains("MSIE")) {
if (s.contains("\\")) {
int pos = s.lastIndexOf("\\");
return s.substring(pos + 1);
} else {
return s;
}
} else {
return s;
}
}
}
/**
* If n is > max, returns max. Otherwise n
*
* @param n
* @param max
* @return
*/
public static long withMax(long n, long max) {
if (n > max) {
return max;
} else {
return n;
}
}
/**
* Add a slash if not present
*
* @param parentHref
* @return
*/
public static String suffixSlash(String parentHref) {
if (parentHref == null) {
return null;
} else if (parentHref.endsWith("/")) {
return parentHref;
} else {
return parentHref + "/";
}
}
public static Dest getDecodedDestination(String destinationHeader) {
String sDest = destinationHeader;
URI destUri = URI.create(sDest);
sDest = destUri.getPath();
Dest dest = new Dest(destUri.getHost(), sDest);
return dest;
}
}