/frameworks/base/core/java/android/webkit/URLUtil.java
Java | 409 lines | 257 code | 48 blank | 104 comment | 101 complexity | 7e718c54c4e6da0bae526870f120d898 MD5 | raw file
- /*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed 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 android.webkit;
- import java.io.UnsupportedEncodingException;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
- import android.net.Uri;
- import android.net.ParseException;
- import android.net.WebAddress;
- import android.util.Log;
- public final class URLUtil {
- private static final String LOGTAG = "webkit";
- // to refer to bar.png under your package's asset/foo/ directory, use
- // "file:///android_asset/foo/bar.png".
- static final String ASSET_BASE = "file:///android_asset/";
- // to refer to bar.png under your package's res/drawable/ directory, use
- // "file:///android_res/drawable/bar.png". Use "drawable" to refer to
- // "drawable-hdpi" directory as well.
- static final String RESOURCE_BASE = "file:///android_res/";
- static final String FILE_BASE = "file://";
- static final String PROXY_BASE = "file:///cookieless_proxy/";
- static final String CONTENT_BASE = "content:";
- /**
- * Cleans up (if possible) user-entered web addresses
- */
- public static String guessUrl(String inUrl) {
- String retVal = inUrl;
- WebAddress webAddress;
- if (DebugFlags.URL_UTIL) Log.v(LOGTAG, "guessURL before queueRequest: " + inUrl);
- if (inUrl.length() == 0) return inUrl;
- if (inUrl.startsWith("about:")) return inUrl;
- // Do not try to interpret data scheme URLs
- if (inUrl.startsWith("data:")) return inUrl;
- // Do not try to interpret file scheme URLs
- if (inUrl.startsWith("file:")) return inUrl;
- // Do not try to interpret javascript scheme URLs
- if (inUrl.startsWith("javascript:")) return inUrl;
- // bug 762454: strip period off end of url
- if (inUrl.endsWith(".") == true) {
- inUrl = inUrl.substring(0, inUrl.length() - 1);
- }
- try {
- webAddress = new WebAddress(inUrl);
- } catch (ParseException ex) {
- if (DebugFlags.URL_UTIL) {
- Log.v(LOGTAG, "smartUrlFilter: failed to parse url = " + inUrl);
- }
- return retVal;
- }
- // Check host
- if (webAddress.getHost().indexOf('.') == -1) {
- // no dot: user probably entered a bare domain. try .com
- webAddress.setHost("www." + webAddress.getHost() + ".com");
- }
- return webAddress.toString();
- }
- public static String composeSearchUrl(String inQuery, String template,
- String queryPlaceHolder) {
- int placeHolderIndex = template.indexOf(queryPlaceHolder);
- if (placeHolderIndex < 0) {
- return null;
- }
- String query;
- StringBuilder buffer = new StringBuilder();
- buffer.append(template.substring(0, placeHolderIndex));
- try {
- query = java.net.URLEncoder.encode(inQuery, "utf-8");
- buffer.append(query);
- } catch (UnsupportedEncodingException ex) {
- return null;
- }
- buffer.append(template.substring(
- placeHolderIndex + queryPlaceHolder.length()));
- return buffer.toString();
- }
- public static byte[] decode(byte[] url) throws IllegalArgumentException {
- if (url.length == 0) {
- return new byte[0];
- }
- // Create a new byte array with the same length to ensure capacity
- byte[] tempData = new byte[url.length];
- int tempCount = 0;
- for (int i = 0; i < url.length; i++) {
- byte b = url[i];
- if (b == '%') {
- if (url.length - i > 2) {
- b = (byte) (parseHex(url[i + 1]) * 16
- + parseHex(url[i + 2]));
- i += 2;
- } else {
- throw new IllegalArgumentException("Invalid format");
- }
- }
- tempData[tempCount++] = b;
- }
- byte[] retData = new byte[tempCount];
- System.arraycopy(tempData, 0, retData, 0, tempCount);
- return retData;
- }
- /**
- * @return True iff the url is correctly URL encoded
- */
- static boolean verifyURLEncoding(String url) {
- int count = url.length();
- if (count == 0) {
- return false;
- }
- int index = url.indexOf('%');
- while (index >= 0 && index < count) {
- if (index < count - 2) {
- try {
- parseHex((byte) url.charAt(++index));
- parseHex((byte) url.charAt(++index));
- } catch (IllegalArgumentException e) {
- return false;
- }
- } else {
- return false;
- }
- index = url.indexOf('%', index + 1);
- }
- return true;
- }
- private static int parseHex(byte b) {
- if (b >= '0' && b <= '9') return (b - '0');
- if (b >= 'A' && b <= 'F') return (b - 'A' + 10);
- if (b >= 'a' && b <= 'f') return (b - 'a' + 10);
- throw new IllegalArgumentException("Invalid hex char '" + b + "'");
- }
- /**
- * @return True iff the url is an asset file.
- */
- public static boolean isAssetUrl(String url) {
- return (null != url) && url.startsWith(ASSET_BASE);
- }
- /**
- * @return True iff the url is a resource file.
- * @hide
- */
- public static boolean isResourceUrl(String url) {
- return (null != url) && url.startsWith(RESOURCE_BASE);
- }
- /**
- * @return True iff the url is a proxy url to allow cookieless network
- * requests from a file url.
- * @deprecated Cookieless proxy is no longer supported.
- */
- @Deprecated
- public static boolean isCookielessProxyUrl(String url) {
- return (null != url) && url.startsWith(PROXY_BASE);
- }
- /**
- * @return True iff the url is a local file.
- */
- public static boolean isFileUrl(String url) {
- return (null != url) && (url.startsWith(FILE_BASE) &&
- !url.startsWith(ASSET_BASE) &&
- !url.startsWith(PROXY_BASE));
- }
- /**
- * @return True iff the url is an about: url.
- */
- public static boolean isAboutUrl(String url) {
- return (null != url) && url.startsWith("about:");
- }
- /**
- * @return True iff the url is a data: url.
- */
- public static boolean isDataUrl(String url) {
- return (null != url) && url.startsWith("data:");
- }
- /**
- * @return True iff the url is a javascript: url.
- */
- public static boolean isJavaScriptUrl(String url) {
- return (null != url) && url.startsWith("javascript:");
- }
- /**
- * @return True iff the url is an http: url.
- */
- public static boolean isHttpUrl(String url) {
- return (null != url) &&
- (url.length() > 6) &&
- url.substring(0, 7).equalsIgnoreCase("http://");
- }
- /**
- * @return True iff the url is an https: url.
- */
- public static boolean isHttpsUrl(String url) {
- return (null != url) &&
- (url.length() > 7) &&
- url.substring(0, 8).equalsIgnoreCase("https://");
- }
- /**
- * @return True iff the url is a network url.
- */
- public static boolean isNetworkUrl(String url) {
- if (url == null || url.length() == 0) {
- return false;
- }
- return isHttpUrl(url) || isHttpsUrl(url);
- }
- /**
- * @return True iff the url is a content: url.
- */
- public static boolean isContentUrl(String url) {
- return (null != url) && url.startsWith(CONTENT_BASE);
- }
- /**
- * @return True iff the url is valid.
- */
- public static boolean isValidUrl(String url) {
- if (url == null || url.length() == 0) {
- return false;
- }
- return (isAssetUrl(url) ||
- isResourceUrl(url) ||
- isFileUrl(url) ||
- isAboutUrl(url) ||
- isHttpUrl(url) ||
- isHttpsUrl(url) ||
- isJavaScriptUrl(url) ||
- isContentUrl(url));
- }
- /**
- * Strips the url of the anchor.
- */
- public static String stripAnchor(String url) {
- int anchorIndex = url.indexOf('#');
- if (anchorIndex != -1) {
- return url.substring(0, anchorIndex);
- }
- return url;
- }
-
- /**
- * Guesses canonical filename that a download would have, using
- * the URL and contentDisposition. File extension, if not defined,
- * is added based on the mimetype
- * @param url Url to the content
- * @param contentDisposition Content-Disposition HTTP header or null
- * @param mimeType Mime-type of the content or null
- *
- * @return suggested filename
- */
- public static final String guessFileName(
- String url,
- String contentDisposition,
- String mimeType) {
- String filename = null;
- String extension = null;
- // If we couldn't do anything with the hint, move toward the content disposition
- if (filename == null && contentDisposition != null) {
- filename = parseContentDisposition(contentDisposition);
- if (filename != null) {
- int index = filename.lastIndexOf('/') + 1;
- if (index > 0) {
- filename = filename.substring(index);
- }
- }
- }
- // If all the other http-related approaches failed, use the plain uri
- if (filename == null) {
- String decodedUrl = Uri.decode(url);
- if (decodedUrl != null) {
- int queryIndex = decodedUrl.indexOf('?');
- // If there is a query string strip it, same as desktop browsers
- if (queryIndex > 0) {
- decodedUrl = decodedUrl.substring(0, queryIndex);
- }
- if (!decodedUrl.endsWith("/")) {
- int index = decodedUrl.lastIndexOf('/') + 1;
- if (index > 0) {
- filename = decodedUrl.substring(index);
- }
- }
- }
- }
- // Finally, if couldn't get filename from URI, get a generic filename
- if (filename == null) {
- filename = "downloadfile";
- }
- // Split filename between base and extension
- // Add an extension if filename does not have one
- int dotIndex = filename.indexOf('.');
- if (dotIndex < 0) {
- if (mimeType != null) {
- extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
- if (extension != null) {
- extension = "." + extension;
- }
- }
- if (extension == null) {
- if (mimeType != null && mimeType.toLowerCase().startsWith("text/")) {
- if (mimeType.equalsIgnoreCase("text/html")) {
- extension = ".html";
- } else {
- extension = ".txt";
- }
- } else {
- extension = ".bin";
- }
- }
- } else {
- if (mimeType != null) {
- // Compare the last segment of the extension against the mime type.
- // If there's a mismatch, discard the entire extension.
- int lastDotIndex = filename.lastIndexOf('.');
- String typeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
- filename.substring(lastDotIndex + 1));
- if (typeFromExt != null && !typeFromExt.equalsIgnoreCase(mimeType)) {
- extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
- if (extension != null) {
- extension = "." + extension;
- }
- }
- }
- if (extension == null) {
- extension = filename.substring(dotIndex);
- }
- filename = filename.substring(0, dotIndex);
- }
- return filename + extension;
- }
- /** Regex used to parse content-disposition headers */
- private static final Pattern CONTENT_DISPOSITION_PATTERN =
- Pattern.compile("attachment;\\s*filename\\s*=\\s*(\"?)([^\"]*)\\1\\s*$",
- Pattern.CASE_INSENSITIVE);
- /*
- * Parse the Content-Disposition HTTP Header. The format of the header
- * is defined here: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html
- * This header provides a filename for content that is going to be
- * downloaded to the file system. We only support the attachment type.
- * Note that RFC 2616 specifies the filename value must be double-quoted.
- * Unfortunately some servers do not quote the value so to maintain
- * consistent behaviour with other browsers, we allow unquoted values too.
- */
- static String parseContentDisposition(String contentDisposition) {
- try {
- Matcher m = CONTENT_DISPOSITION_PATTERN.matcher(contentDisposition);
- if (m.find()) {
- return m.group(2);
- }
- } catch (IllegalStateException ex) {
- // This function is defined as returning null when it can't parse the header
- }
- return null;
- }
- }