url_parse_file.cc 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // Copyright 2013 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "base/check.h"
  5. #include "url/third_party/mozilla/url_parse.h"
  6. #include "url/url_file.h"
  7. #include "url/url_parse_internal.h"
  8. // Interesting IE file:isms...
  9. //
  10. // INPUT OUTPUT
  11. // ========================= ==============================
  12. // file:/foo/bar file:///foo/bar
  13. // The result here seems totally invalid!?!? This isn't UNC.
  14. //
  15. // file:/
  16. // file:// or any other number of slashes
  17. // IE6 doesn't do anything at all if you click on this link. No error:
  18. // nothing. IE6's history system seems to always color this link, so I'm
  19. // guessing that it maps internally to the empty URL.
  20. //
  21. // C:\ file:///C:/
  22. // When on a file: URL source page, this link will work. When over HTTP,
  23. // the file: URL will appear in the status bar but the link will not work
  24. // (security restriction for all file URLs).
  25. //
  26. // file:foo/ file:foo/ (invalid?!?!?)
  27. // file:/foo/ file:///foo/ (invalid?!?!?)
  28. // file://foo/ file://foo/ (UNC to server "foo")
  29. // file:///foo/ file:///foo/ (invalid, seems to be a file)
  30. // file:////foo/ file://foo/ (UNC to server "foo")
  31. // Any more than four slashes is also treated as UNC.
  32. //
  33. // file:C:/ file://C:/
  34. // file:/C:/ file://C:/
  35. // The number of slashes after "file:" don't matter if the thing following
  36. // it looks like an absolute drive path. Also, slashes and backslashes are
  37. // equally valid here.
  38. namespace url {
  39. namespace {
  40. // A subcomponent of DoParseFileURL, the input of this function should be a UNC
  41. // path name, with the index of the first character after the slashes following
  42. // the scheme given in |after_slashes|. This will initialize the host, path,
  43. // query, and ref, and leave the other output components untouched
  44. // (DoParseFileURL handles these for us).
  45. template <typename CHAR>
  46. void DoParseUNC(const CHAR* spec,
  47. int after_slashes,
  48. int spec_len,
  49. Parsed* parsed) {
  50. int next_slash = FindNextSlash(spec, after_slashes, spec_len);
  51. // Everything up until that first slash we found (or end of string) is the
  52. // host name, which will end up being the UNC host. For example,
  53. // "file://foo/bar.txt" will get a server name of "foo" and a path of "/bar".
  54. // Later, on Windows, this should be treated as the filename "\\foo\bar.txt"
  55. // in proper UNC notation.
  56. if (after_slashes < next_slash)
  57. parsed->host = MakeRange(after_slashes, next_slash);
  58. else
  59. parsed->host.reset();
  60. if (next_slash < spec_len) {
  61. ParsePathInternal(spec, MakeRange(next_slash, spec_len),
  62. &parsed->path, &parsed->query, &parsed->ref);
  63. } else {
  64. parsed->path.reset();
  65. }
  66. }
  67. // A subcomponent of DoParseFileURL, the input should be a local file, with the
  68. // beginning of the path indicated by the index in |path_begin|. This will
  69. // initialize the host, path, query, and ref, and leave the other output
  70. // components untouched (DoParseFileURL handles these for us).
  71. template<typename CHAR>
  72. void DoParseLocalFile(const CHAR* spec,
  73. int path_begin,
  74. int spec_len,
  75. Parsed* parsed) {
  76. parsed->host.reset();
  77. ParsePathInternal(spec, MakeRange(path_begin, spec_len),
  78. &parsed->path, &parsed->query, &parsed->ref);
  79. }
  80. // Backend for the external functions that operates on either char type.
  81. // Handles cases where there is a scheme, but also when handed the first
  82. // character following the "file:" at the beginning of the spec. If so,
  83. // this is usually a slash, but needn't be; we allow paths like "file:c:\foo".
  84. template<typename CHAR>
  85. void DoParseFileURL(const CHAR* spec, int spec_len, Parsed* parsed) {
  86. DCHECK(spec_len >= 0);
  87. // Get the parts we never use for file URLs out of the way.
  88. parsed->username.reset();
  89. parsed->password.reset();
  90. parsed->port.reset();
  91. // Many of the code paths don't set these, so it's convenient to just clear
  92. // them. We'll write them in those cases we need them.
  93. parsed->query.reset();
  94. parsed->ref.reset();
  95. // Strip leading & trailing spaces and control characters.
  96. int begin = 0;
  97. TrimURL(spec, &begin, &spec_len);
  98. // Find the scheme, if any.
  99. int num_slashes = CountConsecutiveSlashes(spec, begin, spec_len);
  100. int after_scheme;
  101. int after_slashes;
  102. #ifdef WIN32
  103. // See how many slashes there are. We want to handle cases like UNC but also
  104. // "/c:/foo". This is when there is no scheme, so we can allow pages to do
  105. // links like "c:/foo/bar" or "//foo/bar". This is also called by the
  106. // relative URL resolver when it determines there is an absolute URL, which
  107. // may give us input like "/c:/foo".
  108. after_slashes = begin + num_slashes;
  109. if (DoesBeginWindowsDriveSpec(spec, after_slashes, spec_len)) {
  110. // Windows path, don't try to extract the scheme (for example, "c:\foo").
  111. parsed->scheme.reset();
  112. after_scheme = after_slashes;
  113. } else if (DoesBeginUNCPath(spec, begin, spec_len, false)) {
  114. // Windows UNC path: don't try to extract the scheme, but keep the slashes.
  115. parsed->scheme.reset();
  116. after_scheme = begin;
  117. } else
  118. #endif
  119. {
  120. // ExtractScheme doesn't understand the possibility of filenames with
  121. // colons in them, in which case it returns the entire spec up to the
  122. // colon as the scheme. So handle /foo.c:5 as a file but foo.c:5 as
  123. // the foo.c: scheme.
  124. if (!num_slashes &&
  125. ExtractScheme(&spec[begin], spec_len - begin, &parsed->scheme)) {
  126. // Offset the results since we gave ExtractScheme a substring.
  127. parsed->scheme.begin += begin;
  128. after_scheme = parsed->scheme.end() + 1;
  129. } else {
  130. // No scheme found, remember that.
  131. parsed->scheme.reset();
  132. after_scheme = begin;
  133. }
  134. }
  135. // Handle empty specs ones that contain only whitespace or control chars,
  136. // or that are just the scheme (for example "file:").
  137. if (after_scheme == spec_len) {
  138. parsed->host.reset();
  139. parsed->path.reset();
  140. return;
  141. }
  142. num_slashes = CountConsecutiveSlashes(spec, after_scheme, spec_len);
  143. after_slashes = after_scheme + num_slashes;
  144. #ifdef WIN32
  145. // Check whether the input is a drive again. We checked above for windows
  146. // drive specs, but that's only at the very beginning to see if we have a
  147. // scheme at all. This test will be duplicated in that case, but will
  148. // additionally handle all cases with a real scheme such as "file:///C:/".
  149. if (!DoesBeginWindowsDriveSpec(spec, after_slashes, spec_len) &&
  150. num_slashes != 3) {
  151. // Anything not beginning with a drive spec ("c:\") on Windows is treated
  152. // as UNC, with the exception of three slashes which always means a file.
  153. // Even IE7 treats file:///foo/bar as "/foo/bar", which then fails.
  154. DoParseUNC(spec, after_slashes, spec_len, parsed);
  155. return;
  156. }
  157. #else
  158. // file: URL with exactly 2 slashes is considered to have a host component.
  159. if (num_slashes == 2) {
  160. DoParseUNC(spec, after_slashes, spec_len, parsed);
  161. return;
  162. }
  163. #endif // WIN32
  164. // Easy and common case, the full path immediately follows the scheme
  165. // (modulo slashes), as in "file://c:/foo". Just treat everything from
  166. // there to the end as the path. Empty hosts have 0 length instead of -1.
  167. // We include the last slash as part of the path if there is one.
  168. DoParseLocalFile(spec,
  169. num_slashes > 0 ? after_scheme + num_slashes - 1 : after_scheme,
  170. spec_len, parsed);
  171. }
  172. } // namespace
  173. void ParseFileURL(const char* url, int url_len, Parsed* parsed) {
  174. DoParseFileURL(url, url_len, parsed);
  175. }
  176. void ParseFileURL(const char16_t* url, int url_len, Parsed* parsed) {
  177. DoParseFileURL(url, url_len, parsed);
  178. }
  179. } // namespace url