package org.wordpress.android.util;
import android.net.Uri;
import android.text.TextUtils;
import android.webkit.MimeTypeMap;
import android.webkit.URLUtil;
import org.wordpress.android.util.AppLog.T;
import java.io.UnsupportedEncodingException;
import java.net.IDN;
import java.net.URI;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
public class UrlUtils {
public static String urlEncode(final String text) {
try {
return URLEncoder.encode(text, "UTF-8");
} catch (UnsupportedEncodingException e) {
return text;
}
}
public static String urlDecode(final String text) {
try {
return URLDecoder.decode(text, "UTF-8");
} catch (UnsupportedEncodingException e) {
return text;
}
}
/**
* @param urlString url to get host from
* @return host of uri if available. Empty string otherwise.
*/
public static String getHost(final String urlString) {
if (urlString != null) {
Uri uri = Uri.parse(urlString);
if (uri.getHost() != null) {
return uri.getHost();
}
}
return "";
}
/**
* Convert IDN names to punycode if necessary
*/
public static String convertUrlToPunycodeIfNeeded(String url) {
if (!Charset.forName("US-ASCII").newEncoder().canEncode(url)) {
if (url.toLowerCase().startsWith("http://")) {
url = "http://" + IDN.toASCII(url.substring(7), IDN.ALLOW_UNASSIGNED);
} else if (url.toLowerCase().startsWith("https://")) {
url = "https://" + IDN.toASCII(url.substring(8), IDN.ALLOW_UNASSIGNED);
} else {
url = IDN.toASCII(url, IDN.ALLOW_UNASSIGNED);
}
}
return url;
}
/**
* Remove leading double slash, and inherit protocol scheme
*/
public static String removeLeadingDoubleSlash(String url, String scheme) {
if (url != null && url.startsWith("//")) {
url = url.substring(2);
if (scheme != null) {
if (scheme.endsWith("://")){
url = scheme + url;
} else {
AppLog.e(T.UTILS, "Invalid scheme used: " + scheme);
}
}
}
return url;
}
/**
* Add scheme prefix to an URL. This method must be called on all user entered or server fetched URLs to ensure
* http client will work as expected.
*
* @param url url entered by the user or fetched from a server
* @param addHttps true and the url is not starting with http://, it will make the url starts with https://
* @return url prefixed by http:// or https://
*/
public static String addUrlSchemeIfNeeded(String url, boolean addHttps) {
if (url == null) {
return null;
}
// Remove leading double slash (eg. //example.com), needed for some wporg instances configured to
// switch between http or https
url = removeLeadingDoubleSlash(url, (addHttps ? "https" : "http") + "://");
// If the URL is a valid http or https URL, we're good to go
if (URLUtil.isHttpUrl(url) || URLUtil.isHttpsUrl(url)) {
return url;
}
// Else, remove the old scheme and prefix it by https:// or http://
return (addHttps ? "https" : "http") + "://" + removeScheme(url);
}
/**
* normalizes a URL, primarily for comparison purposes, for example so that
* normalizeUrl("http://google.com/") = normalizeUrl("http://google.com")
*/
public static String normalizeUrl(final String urlString) {
if (urlString == null) {
return null;
}
// this routine is called from some performance-critical code and creating a URI from a string
// is slow, so skip it when possible - if we know it's not a relative path (and 99.9% of the
// time it won't be for our purposes) then we can normalize it without java.net.URI.normalize()
if (urlString.startsWith("http") &&
!urlString.contains("build/intermediates/exploded-aar/org.wordpress/graphview/3.1.1")) {
// return without a trailing slash
if (urlString.endsWith("/")) {
return urlString.substring(0, urlString.length() - 1);
}
return urlString;
}
// url is relative, so fall back to using slower java.net.URI normalization
try {
URI uri = URI.create(urlString);
return uri.normalize().toString();
} catch (IllegalArgumentException e) {
return urlString;
}
}
/**
* returns the passed url without the scheme
*/
public static String removeScheme(final String urlString) {
if (urlString == null) {
return null;
}
int doubleslash = urlString.indexOf("//");
if (doubleslash == -1) {
doubleslash = 0;
} else {
doubleslash += 2;
}
return urlString.substring(doubleslash, urlString.length());
}
/**
* returns the passed url without the query parameters
*/
public static String removeQuery(final String urlString) {
if (urlString == null) {
return null;
}
return Uri.parse(urlString).buildUpon().clearQuery().toString();
}
/**
* returns true if passed url is https:
*/
public static boolean isHttps(final String urlString) {
return (urlString != null && urlString.startsWith("https:"));
}
public static boolean isHttps(URL url) {
return url != null && "https".equals(url.getProtocol());
}
public static boolean isHttps(URI uri) {
if (uri == null) return false;
String protocol = uri.getScheme();
return protocol != null && protocol.equals("https");
}
/**
* returns https: version of passed http: url
*/
public static String makeHttps(final String urlString) {
if (urlString == null || !urlString.startsWith("http:")) {
return urlString;
}
return "https:" + urlString.substring(5, urlString.length());
}
/**
* see http://stackoverflow.com/a/8591230/1673548
*/
public static String getUrlMimeType(final String urlString) {
if (urlString == null) {
return null;
}
String extension = MimeTypeMap.getFileExtensionFromUrl(urlString);
if (extension == null) {
return null;
}
MimeTypeMap mime = MimeTypeMap.getSingleton();
String mimeType = mime.getMimeTypeFromExtension(extension);
if (mimeType == null) {
return null;
}
return mimeType;
}
/**
* returns false if the url is not valid or if the url host is null, else true
*/
public static boolean isValidUrlAndHostNotNull(String url) {
try {
URI uri = URI.create(url);
if (uri.getHost() == null) {
return false;
}
} catch (IllegalArgumentException e) {
return false;
}
return true;
}
// returns true if the passed url is for an image
public static boolean isImageUrl(String url) {
if (TextUtils.isEmpty(url)) return false;
String cleanedUrl = removeQuery(url.toLowerCase());
return cleanedUrl.endsWith("jpg") || cleanedUrl.endsWith("jpeg") ||
cleanedUrl.endsWith("gif") || cleanedUrl.endsWith("png");
}
public static String appendUrlParameter(String url, String paramName, String paramValue) {
Map<String, String> parameters = new HashMap<>();
parameters.put(paramName, paramValue);
return appendUrlParameters(url, parameters);
}
public static String appendUrlParameters(String url, Map<String, String> parameters) {
Uri.Builder uriBuilder = Uri.parse(url).buildUpon();
for (Map.Entry<String, String> parameter : parameters.entrySet()) {
uriBuilder.appendQueryParameter(parameter.getKey(), parameter.getValue());
}
return uriBuilder.build().toString();
}
/**
* Extracts the subdomain from a domain string.
* @param domain A domain is expected. Protocol is optional
* @return The subdomain or an empty string.
*/
public static String extractSubDomain(String domain) {
String str = UrlUtils.addUrlSchemeIfNeeded(domain, false);
String host = UrlUtils.getHost(str);
if (host.length() > 0) {
String[] parts = host.split("\\.");
if (parts.length > 1) { // There should be at least 2 dots for there to be a subdomain.
return parts[0];
}
}
return "";
}
}