PageRenderTime 24ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/frameworks/base/core/java/android/webkit/URLUtil.java

https://gitlab.com/brian0218/rk3066_r-box_android4.2.2_sdk
Java | 409 lines | 257 code | 48 blank | 104 comment | 101 complexity | 7e718c54c4e6da0bae526870f120d898 MD5 | raw file
  1. /*
  2. * Copyright (C) 2006 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package android.webkit;
  17. import java.io.UnsupportedEncodingException;
  18. import java.util.regex.Matcher;
  19. import java.util.regex.Pattern;
  20. import android.net.Uri;
  21. import android.net.ParseException;
  22. import android.net.WebAddress;
  23. import android.util.Log;
  24. public final class URLUtil {
  25. private static final String LOGTAG = "webkit";
  26. // to refer to bar.png under your package's asset/foo/ directory, use
  27. // "file:///android_asset/foo/bar.png".
  28. static final String ASSET_BASE = "file:///android_asset/";
  29. // to refer to bar.png under your package's res/drawable/ directory, use
  30. // "file:///android_res/drawable/bar.png". Use "drawable" to refer to
  31. // "drawable-hdpi" directory as well.
  32. static final String RESOURCE_BASE = "file:///android_res/";
  33. static final String FILE_BASE = "file://";
  34. static final String PROXY_BASE = "file:///cookieless_proxy/";
  35. static final String CONTENT_BASE = "content:";
  36. /**
  37. * Cleans up (if possible) user-entered web addresses
  38. */
  39. public static String guessUrl(String inUrl) {
  40. String retVal = inUrl;
  41. WebAddress webAddress;
  42. if (DebugFlags.URL_UTIL) Log.v(LOGTAG, "guessURL before queueRequest: " + inUrl);
  43. if (inUrl.length() == 0) return inUrl;
  44. if (inUrl.startsWith("about:")) return inUrl;
  45. // Do not try to interpret data scheme URLs
  46. if (inUrl.startsWith("data:")) return inUrl;
  47. // Do not try to interpret file scheme URLs
  48. if (inUrl.startsWith("file:")) return inUrl;
  49. // Do not try to interpret javascript scheme URLs
  50. if (inUrl.startsWith("javascript:")) return inUrl;
  51. // bug 762454: strip period off end of url
  52. if (inUrl.endsWith(".") == true) {
  53. inUrl = inUrl.substring(0, inUrl.length() - 1);
  54. }
  55. try {
  56. webAddress = new WebAddress(inUrl);
  57. } catch (ParseException ex) {
  58. if (DebugFlags.URL_UTIL) {
  59. Log.v(LOGTAG, "smartUrlFilter: failed to parse url = " + inUrl);
  60. }
  61. return retVal;
  62. }
  63. // Check host
  64. if (webAddress.getHost().indexOf('.') == -1) {
  65. // no dot: user probably entered a bare domain. try .com
  66. webAddress.setHost("www." + webAddress.getHost() + ".com");
  67. }
  68. return webAddress.toString();
  69. }
  70. public static String composeSearchUrl(String inQuery, String template,
  71. String queryPlaceHolder) {
  72. int placeHolderIndex = template.indexOf(queryPlaceHolder);
  73. if (placeHolderIndex < 0) {
  74. return null;
  75. }
  76. String query;
  77. StringBuilder buffer = new StringBuilder();
  78. buffer.append(template.substring(0, placeHolderIndex));
  79. try {
  80. query = java.net.URLEncoder.encode(inQuery, "utf-8");
  81. buffer.append(query);
  82. } catch (UnsupportedEncodingException ex) {
  83. return null;
  84. }
  85. buffer.append(template.substring(
  86. placeHolderIndex + queryPlaceHolder.length()));
  87. return buffer.toString();
  88. }
  89. public static byte[] decode(byte[] url) throws IllegalArgumentException {
  90. if (url.length == 0) {
  91. return new byte[0];
  92. }
  93. // Create a new byte array with the same length to ensure capacity
  94. byte[] tempData = new byte[url.length];
  95. int tempCount = 0;
  96. for (int i = 0; i < url.length; i++) {
  97. byte b = url[i];
  98. if (b == '%') {
  99. if (url.length - i > 2) {
  100. b = (byte) (parseHex(url[i + 1]) * 16
  101. + parseHex(url[i + 2]));
  102. i += 2;
  103. } else {
  104. throw new IllegalArgumentException("Invalid format");
  105. }
  106. }
  107. tempData[tempCount++] = b;
  108. }
  109. byte[] retData = new byte[tempCount];
  110. System.arraycopy(tempData, 0, retData, 0, tempCount);
  111. return retData;
  112. }
  113. /**
  114. * @return True iff the url is correctly URL encoded
  115. */
  116. static boolean verifyURLEncoding(String url) {
  117. int count = url.length();
  118. if (count == 0) {
  119. return false;
  120. }
  121. int index = url.indexOf('%');
  122. while (index >= 0 && index < count) {
  123. if (index < count - 2) {
  124. try {
  125. parseHex((byte) url.charAt(++index));
  126. parseHex((byte) url.charAt(++index));
  127. } catch (IllegalArgumentException e) {
  128. return false;
  129. }
  130. } else {
  131. return false;
  132. }
  133. index = url.indexOf('%', index + 1);
  134. }
  135. return true;
  136. }
  137. private static int parseHex(byte b) {
  138. if (b >= '0' && b <= '9') return (b - '0');
  139. if (b >= 'A' && b <= 'F') return (b - 'A' + 10);
  140. if (b >= 'a' && b <= 'f') return (b - 'a' + 10);
  141. throw new IllegalArgumentException("Invalid hex char '" + b + "'");
  142. }
  143. /**
  144. * @return True iff the url is an asset file.
  145. */
  146. public static boolean isAssetUrl(String url) {
  147. return (null != url) && url.startsWith(ASSET_BASE);
  148. }
  149. /**
  150. * @return True iff the url is a resource file.
  151. * @hide
  152. */
  153. public static boolean isResourceUrl(String url) {
  154. return (null != url) && url.startsWith(RESOURCE_BASE);
  155. }
  156. /**
  157. * @return True iff the url is a proxy url to allow cookieless network
  158. * requests from a file url.
  159. * @deprecated Cookieless proxy is no longer supported.
  160. */
  161. @Deprecated
  162. public static boolean isCookielessProxyUrl(String url) {
  163. return (null != url) && url.startsWith(PROXY_BASE);
  164. }
  165. /**
  166. * @return True iff the url is a local file.
  167. */
  168. public static boolean isFileUrl(String url) {
  169. return (null != url) && (url.startsWith(FILE_BASE) &&
  170. !url.startsWith(ASSET_BASE) &&
  171. !url.startsWith(PROXY_BASE));
  172. }
  173. /**
  174. * @return True iff the url is an about: url.
  175. */
  176. public static boolean isAboutUrl(String url) {
  177. return (null != url) && url.startsWith("about:");
  178. }
  179. /**
  180. * @return True iff the url is a data: url.
  181. */
  182. public static boolean isDataUrl(String url) {
  183. return (null != url) && url.startsWith("data:");
  184. }
  185. /**
  186. * @return True iff the url is a javascript: url.
  187. */
  188. public static boolean isJavaScriptUrl(String url) {
  189. return (null != url) && url.startsWith("javascript:");
  190. }
  191. /**
  192. * @return True iff the url is an http: url.
  193. */
  194. public static boolean isHttpUrl(String url) {
  195. return (null != url) &&
  196. (url.length() > 6) &&
  197. url.substring(0, 7).equalsIgnoreCase("http://");
  198. }
  199. /**
  200. * @return True iff the url is an https: url.
  201. */
  202. public static boolean isHttpsUrl(String url) {
  203. return (null != url) &&
  204. (url.length() > 7) &&
  205. url.substring(0, 8).equalsIgnoreCase("https://");
  206. }
  207. /**
  208. * @return True iff the url is a network url.
  209. */
  210. public static boolean isNetworkUrl(String url) {
  211. if (url == null || url.length() == 0) {
  212. return false;
  213. }
  214. return isHttpUrl(url) || isHttpsUrl(url);
  215. }
  216. /**
  217. * @return True iff the url is a content: url.
  218. */
  219. public static boolean isContentUrl(String url) {
  220. return (null != url) && url.startsWith(CONTENT_BASE);
  221. }
  222. /**
  223. * @return True iff the url is valid.
  224. */
  225. public static boolean isValidUrl(String url) {
  226. if (url == null || url.length() == 0) {
  227. return false;
  228. }
  229. return (isAssetUrl(url) ||
  230. isResourceUrl(url) ||
  231. isFileUrl(url) ||
  232. isAboutUrl(url) ||
  233. isHttpUrl(url) ||
  234. isHttpsUrl(url) ||
  235. isJavaScriptUrl(url) ||
  236. isContentUrl(url));
  237. }
  238. /**
  239. * Strips the url of the anchor.
  240. */
  241. public static String stripAnchor(String url) {
  242. int anchorIndex = url.indexOf('#');
  243. if (anchorIndex != -1) {
  244. return url.substring(0, anchorIndex);
  245. }
  246. return url;
  247. }
  248. /**
  249. * Guesses canonical filename that a download would have, using
  250. * the URL and contentDisposition. File extension, if not defined,
  251. * is added based on the mimetype
  252. * @param url Url to the content
  253. * @param contentDisposition Content-Disposition HTTP header or null
  254. * @param mimeType Mime-type of the content or null
  255. *
  256. * @return suggested filename
  257. */
  258. public static final String guessFileName(
  259. String url,
  260. String contentDisposition,
  261. String mimeType) {
  262. String filename = null;
  263. String extension = null;
  264. // If we couldn't do anything with the hint, move toward the content disposition
  265. if (filename == null && contentDisposition != null) {
  266. filename = parseContentDisposition(contentDisposition);
  267. if (filename != null) {
  268. int index = filename.lastIndexOf('/') + 1;
  269. if (index > 0) {
  270. filename = filename.substring(index);
  271. }
  272. }
  273. }
  274. // If all the other http-related approaches failed, use the plain uri
  275. if (filename == null) {
  276. String decodedUrl = Uri.decode(url);
  277. if (decodedUrl != null) {
  278. int queryIndex = decodedUrl.indexOf('?');
  279. // If there is a query string strip it, same as desktop browsers
  280. if (queryIndex > 0) {
  281. decodedUrl = decodedUrl.substring(0, queryIndex);
  282. }
  283. if (!decodedUrl.endsWith("/")) {
  284. int index = decodedUrl.lastIndexOf('/') + 1;
  285. if (index > 0) {
  286. filename = decodedUrl.substring(index);
  287. }
  288. }
  289. }
  290. }
  291. // Finally, if couldn't get filename from URI, get a generic filename
  292. if (filename == null) {
  293. filename = "downloadfile";
  294. }
  295. // Split filename between base and extension
  296. // Add an extension if filename does not have one
  297. int dotIndex = filename.indexOf('.');
  298. if (dotIndex < 0) {
  299. if (mimeType != null) {
  300. extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
  301. if (extension != null) {
  302. extension = "." + extension;
  303. }
  304. }
  305. if (extension == null) {
  306. if (mimeType != null && mimeType.toLowerCase().startsWith("text/")) {
  307. if (mimeType.equalsIgnoreCase("text/html")) {
  308. extension = ".html";
  309. } else {
  310. extension = ".txt";
  311. }
  312. } else {
  313. extension = ".bin";
  314. }
  315. }
  316. } else {
  317. if (mimeType != null) {
  318. // Compare the last segment of the extension against the mime type.
  319. // If there's a mismatch, discard the entire extension.
  320. int lastDotIndex = filename.lastIndexOf('.');
  321. String typeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
  322. filename.substring(lastDotIndex + 1));
  323. if (typeFromExt != null && !typeFromExt.equalsIgnoreCase(mimeType)) {
  324. extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
  325. if (extension != null) {
  326. extension = "." + extension;
  327. }
  328. }
  329. }
  330. if (extension == null) {
  331. extension = filename.substring(dotIndex);
  332. }
  333. filename = filename.substring(0, dotIndex);
  334. }
  335. return filename + extension;
  336. }
  337. /** Regex used to parse content-disposition headers */
  338. private static final Pattern CONTENT_DISPOSITION_PATTERN =
  339. Pattern.compile("attachment;\\s*filename\\s*=\\s*(\"?)([^\"]*)\\1\\s*$",
  340. Pattern.CASE_INSENSITIVE);
  341. /*
  342. * Parse the Content-Disposition HTTP Header. The format of the header
  343. * is defined here: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html
  344. * This header provides a filename for content that is going to be
  345. * downloaded to the file system. We only support the attachment type.
  346. * Note that RFC 2616 specifies the filename value must be double-quoted.
  347. * Unfortunately some servers do not quote the value so to maintain
  348. * consistent behaviour with other browsers, we allow unquoted values too.
  349. */
  350. static String parseContentDisposition(String contentDisposition) {
  351. try {
  352. Matcher m = CONTENT_DISPOSITION_PATTERN.matcher(contentDisposition);
  353. if (m.find()) {
  354. return m.group(2);
  355. }
  356. } catch (IllegalStateException ex) {
  357. // This function is defined as returning null when it can't parse the header
  358. }
  359. return null;
  360. }
  361. }