uri_impl.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. // Copyright 2020 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 "chromeos/printing/uri_impl.h"
  5. #include <algorithm>
  6. #include <array>
  7. #include <set>
  8. #include "base/check_op.h"
  9. #include "base/i18n/streaming_utf8_validator.h"
  10. #include "base/strings/string_util.h"
  11. #include "base/strings/utf_string_conversion_utils.h"
  12. #include "chromeos/printing/uri.h"
  13. namespace chromeos {
  14. namespace {
  15. constexpr int kPortInvalid = -2;
  16. constexpr int kPortUnspecified = -1;
  17. constexpr int kPortMaxNumber = 65535;
  18. // Parses a single character from *|current| and interprets it as a hex
  19. // digit ('0'-'9' or 'A'-'F' or 'a'-'f'). If the character is incorrect or
  20. // *|current| is not less than |end|, the function returns false.
  21. // Otherwise, the value in *|out| is shifted left by 4 bits and the parsed
  22. // value is saved on its rightmost 4 bits. The iterator *|current| is
  23. // increased by one, and the function returns true.
  24. // |current| and |out| must be not nullptr.
  25. bool ParseHexDigit(const Iter& end, Iter* current, unsigned char* out) {
  26. Iter& it = *current;
  27. if (it >= end)
  28. return false;
  29. *out <<= 4;
  30. if (base::IsAsciiDigit(*it)) {
  31. *out += (*it - '0');
  32. } else if (*it >= 'A' && *it <= 'F') {
  33. *out += (*it - 'A' + 10);
  34. } else if (*it >= 'a' && *it <= 'f') {
  35. *out += (*it - 'a' + 10);
  36. } else {
  37. return false;
  38. }
  39. ++it;
  40. return true;
  41. }
  42. // The function parses from *|current|-|end| the first character and saves it
  43. // to |out|. If |encoded| equals true, the % sign is treated as the beginning
  44. // of %-escaped character - in this case the whole escaped character is read
  45. // and decoded. The function fails and returns false when unexpected end of
  46. // string is reached or invalid %-escaped character is spotted. The iterator
  47. // *|current| is shifted accordingly.
  48. // |current| and |out| must be not nullptr and *|current| must be less than
  49. // |end|.
  50. template <bool encoded>
  51. bool ParseCharacter(const Iter& end, Iter* current, char* out) {
  52. Iter& it = *current;
  53. DCHECK(it < end);
  54. *out = *it;
  55. ++it;
  56. if (encoded && *out == '%') {
  57. unsigned char c = 0;
  58. if (!ParseHexDigit(end, &it, &c))
  59. return false;
  60. if (!ParseHexDigit(end, &it, &c))
  61. return false;
  62. *out = static_cast<char>(c);
  63. }
  64. return true;
  65. }
  66. // Helper struct for the function below.
  67. class Comparator {
  68. public:
  69. // The string given as a parameter must be valid for the whole lifetime
  70. // of this object.
  71. explicit Comparator(const std::string& chars) : chars_(chars) {}
  72. bool operator()(std::string::value_type element) const {
  73. return (chars_.find(element) != std::string::npos);
  74. }
  75. private:
  76. const std::string& chars_;
  77. };
  78. // Returns iterator to the first occurrence of any character from |chars|
  79. // in |begin|-|end|. Returns |end| if none of the characters were found.
  80. Iter FindFirstOf(Iter begin, Iter end, const std::string& chars) {
  81. return std::find_if(begin, end, Comparator(chars));
  82. }
  83. } // namespace
  84. template <bool encoded, bool case_insensitive>
  85. bool Uri::Pim::ParseString(const Iter& begin,
  86. const Iter& end,
  87. std::string* out,
  88. bool plus_to_space) {
  89. parser_error_.parsed_chars = 0;
  90. out->reserve(end - begin);
  91. for (Iter it = begin; it < end;) {
  92. char c;
  93. // Read and decode a single character or a %-escaped character.
  94. if (plus_to_space && *it == '+') {
  95. c = ' ';
  96. ++it;
  97. } else if (!ParseCharacter<encoded>(end, &it, &c)) {
  98. parser_error_.status = ParserStatus::kInvalidPercentEncoding;
  99. return false;
  100. }
  101. // Analyze the character.
  102. if (base::IsAsciiPrintable(c)) { // c >= 0x20(' ') && c <= 0x7E('~')
  103. // Copy the character with normalization.
  104. out->push_back(case_insensitive ? base::ToLowerASCII(c) : c);
  105. parser_error_.parsed_chars = it - begin;
  106. } else {
  107. // Try to parse UTF-8 character.
  108. base::StreamingUtf8Validator utf_parser;
  109. base::StreamingUtf8Validator::State state = utf_parser.AddBytes(&c, 1);
  110. if (state != base::StreamingUtf8Validator::State::VALID_MIDPOINT) {
  111. parser_error_.status = ParserStatus::kDisallowedASCIICharacter;
  112. return false;
  113. }
  114. std::string utf8_character(1, c);
  115. parser_error_.parsed_chars = it - begin;
  116. do {
  117. if (it == end) {
  118. parser_error_.status = ParserStatus::kInvalidUTF8Character;
  119. return false;
  120. }
  121. if (!ParseCharacter<encoded>(end, &it, &c)) {
  122. parser_error_.status = ParserStatus::kInvalidPercentEncoding;
  123. return false;
  124. }
  125. state = utf_parser.AddBytes(&c, 1);
  126. if (state == base::StreamingUtf8Validator::State::INVALID) {
  127. parser_error_.status = ParserStatus::kInvalidUTF8Character;
  128. return false;
  129. }
  130. utf8_character.push_back(c);
  131. parser_error_.parsed_chars = it - begin;
  132. } while (state != base::StreamingUtf8Validator::State::VALID_ENDPOINT);
  133. // Saves the UTF-8 character to the output.
  134. out->append(std::move(utf8_character));
  135. }
  136. }
  137. ++(parser_error_.parsed_strings);
  138. return true;
  139. }
  140. template <bool encoded>
  141. bool Uri::Pim::SaveUserinfo(const std::string& val) {
  142. parser_error_.status = ParserStatus::kNoErrors;
  143. parser_error_.parsed_strings = 0;
  144. std::string out;
  145. if (!ParseString<encoded>(val.begin(), val.end(), &out))
  146. return false;
  147. userinfo_ = std::move(out);
  148. return true;
  149. }
  150. template <bool encoded>
  151. bool Uri::Pim::SaveHost(const std::string& val) {
  152. parser_error_.status = ParserStatus::kNoErrors;
  153. parser_error_.parsed_strings = 0;
  154. std::string out;
  155. if (!ParseString<encoded, true>(val.begin(), val.end(), &out))
  156. return false;
  157. host_ = std::move(out);
  158. return true;
  159. }
  160. bool Uri::Pim::SavePort(int value) {
  161. parser_error_.status = ParserStatus::kNoErrors;
  162. parser_error_.parsed_strings = 0;
  163. parser_error_.parsed_chars = 0;
  164. if (value < -1 || value > 65535) {
  165. parser_error_.status = ParserStatus::kInvalidPortNumber;
  166. return false;
  167. }
  168. if (value == kPortUnspecified)
  169. value = Uri::GetDefaultPort(scheme_);
  170. port_ = value;
  171. return true;
  172. }
  173. template <bool encoded>
  174. bool Uri::Pim::SavePath(const std::vector<std::string>& val) {
  175. parser_error_.status = ParserStatus::kNoErrors;
  176. parser_error_.parsed_strings = 0;
  177. parser_error_.parsed_chars = 0;
  178. std::vector<std::string> out;
  179. out.reserve(val.size());
  180. for (size_t i = 0; i < val.size(); ++i) {
  181. std::string segment;
  182. auto it1 = val[i].begin();
  183. auto it2 = val[i].end();
  184. if (!ParseString<encoded>(it1, it2, &segment))
  185. return false;
  186. if (segment == ".") {
  187. // do nothing
  188. } else if (segment == ".." && !out.empty() && out.back() != "..") {
  189. out.pop_back();
  190. } else if (segment.empty()) {
  191. --parser_error_.parsed_strings; // it was already counted
  192. parser_error_.parsed_chars = 0;
  193. parser_error_.status = ParserStatus::kEmptySegmentInPath;
  194. return false;
  195. } else {
  196. out.push_back(std::move(segment));
  197. }
  198. }
  199. path_ = std::move(out);
  200. return true;
  201. }
  202. template <bool encoded>
  203. bool Uri::Pim::SaveQuery(
  204. const std::vector<std::pair<std::string, std::string>>& val) {
  205. parser_error_.status = ParserStatus::kNoErrors;
  206. parser_error_.parsed_strings = 0;
  207. parser_error_.parsed_chars = 0;
  208. std::vector<std::pair<std::string, std::string>> out(val.size());
  209. for (size_t i = 0; i < out.size(); ++i) {
  210. // Process parameter name.
  211. auto it1 = val[i].first.begin();
  212. auto it2 = val[i].first.end();
  213. if (!ParseString<encoded>(it1, it2, &out[i].first, encoded))
  214. return false;
  215. if (out[i].first.empty()) {
  216. --parser_error_.parsed_strings; // it was already counted
  217. parser_error_.parsed_chars = 0;
  218. parser_error_.status = ParserStatus::kEmptyParameterNameInQuery;
  219. return false;
  220. }
  221. // Process parameter value.
  222. it1 = val[i].second.begin();
  223. it2 = val[i].second.end();
  224. if (!ParseString<encoded>(it1, it2, &out[i].second, encoded))
  225. return false;
  226. }
  227. query_ = std::move(out);
  228. return true;
  229. }
  230. template <bool encoded>
  231. bool Uri::Pim::SaveFragment(const std::string& val) {
  232. parser_error_.status = ParserStatus::kNoErrors;
  233. parser_error_.parsed_strings = 0;
  234. std::string out;
  235. if (!ParseString<encoded>(val.begin(), val.end(), &out))
  236. return false;
  237. fragment_ = std::move(out);
  238. return true;
  239. }
  240. bool Uri::Pim::ParseScheme(const Iter& begin, const Iter& end) {
  241. parser_error_.status = ParserStatus::kNoErrors;
  242. parser_error_.parsed_strings = 0;
  243. parser_error_.parsed_chars = 0;
  244. // Special case for an empty string on the input.
  245. if (begin == end) {
  246. scheme_.clear();
  247. return true;
  248. }
  249. // Temporary output string.
  250. std::string out;
  251. out.reserve(end - begin);
  252. // Checks the first character - must be an ASCII letter.
  253. auto it = begin;
  254. if (base::IsAsciiAlpha(*it)) {
  255. out.push_back(base::ToLowerASCII(*it));
  256. } else {
  257. parser_error_.status = ParserStatus::kInvalidScheme;
  258. return false;
  259. }
  260. // Checks the rest of characters.
  261. for (++it; it < end; ++it) {
  262. if (base::IsAsciiAlpha(*it) || base::IsAsciiDigit(*it) || *it == '+' ||
  263. *it == '-' || *it == '.') {
  264. out.push_back(base::ToLowerASCII(*it));
  265. } else {
  266. parser_error_.status = ParserStatus::kInvalidScheme;
  267. parser_error_.parsed_chars = it - begin;
  268. return false;
  269. }
  270. }
  271. // Success - save the Scheme.
  272. scheme_ = std::move(out);
  273. // If the current Port is unspecified and the new Scheme has default port
  274. // number, set the default port number.
  275. if (port_ == kPortUnspecified)
  276. port_ = Uri::GetDefaultPort(scheme_);
  277. return true;
  278. }
  279. bool Uri::Pim::ParseAuthority(const Iter& begin, const Iter& end) {
  280. // Parse and save Userinfo.
  281. Iter it = std::find(begin, end, '@');
  282. if (it != end) {
  283. if (!SaveUserinfo<true>(std::string(begin, it))) {
  284. parser_error_.parsed_chars += it - begin;
  285. return false;
  286. }
  287. ++it; // to omit '@' character
  288. } else {
  289. it = begin;
  290. }
  291. // Parse and save Host.
  292. Iter it2 = std::find(it, end, ':');
  293. if (!SaveHost<true>(std::string(it, it2))) {
  294. parser_error_.parsed_chars += it - begin;
  295. return false;
  296. }
  297. // Parse and save Port.
  298. if (it2 != end) {
  299. ++it2; // omit the ':' character
  300. if (!ParsePort(it2, end)) {
  301. parser_error_.parsed_chars += it2 - begin;
  302. return false;
  303. }
  304. }
  305. return true;
  306. }
  307. bool Uri::Pim::ParsePort(const Iter& begin, const Iter& end) {
  308. if (begin == end)
  309. return SavePort(kPortUnspecified);
  310. int number = 0;
  311. for (Iter it = begin; it < end; ++it) {
  312. if (!base::IsAsciiDigit(*it))
  313. return SavePort(kPortInvalid);
  314. number *= 10;
  315. number += *it - '0';
  316. if (number > kPortMaxNumber)
  317. return SavePort(kPortInvalid);
  318. }
  319. return SavePort(number);
  320. }
  321. bool Uri::Pim::ParsePath(const Iter& begin, const Iter& end) {
  322. // Path must be empty or start with '/'.
  323. if (begin < end && *begin != '/') {
  324. parser_error_.status = ParserStatus::kRelativePathsNotAllowed;
  325. parser_error_.parsed_chars = 0;
  326. parser_error_.parsed_strings = 0;
  327. return false;
  328. }
  329. // This holds Path's segments.
  330. std::vector<std::string> path;
  331. // This stores offset from begin of every segment.
  332. std::vector<size_t> strings_positions;
  333. // Parsing...
  334. for (Iter it1 = begin; it1 < end;) {
  335. if (++it1 == end) // omit '/' character
  336. break;
  337. Iter it2 = std::find(it1, end, '/');
  338. path.push_back(std::string(it1, it2));
  339. strings_positions.push_back(it1 - begin);
  340. it1 = it2;
  341. }
  342. // Try to set the new Path and return true if succeed.
  343. if (SavePath<true>(path))
  344. return true;
  345. // An error occurred, adjust parser error fields set by SetPath(...).
  346. parser_error_.parsed_chars += strings_positions[parser_error_.parsed_strings];
  347. parser_error_.parsed_strings = 0;
  348. return false;
  349. }
  350. bool Uri::Pim::ParseQuery(const Iter& begin, const Iter& end) {
  351. // This holds pairs name=value.
  352. std::vector<std::pair<std::string, std::string>> query;
  353. // This stores offset from begin of every name and value.
  354. std::vector<size_t> strings_positions;
  355. // Parsing...
  356. for (Iter it = begin; it < end;) {
  357. Iter it_am = std::find(it, end, '&');
  358. Iter it_eq = std::find(it, it_am, '=');
  359. // Extract name.
  360. std::string name(it, it_eq);
  361. // Extract value.
  362. if (it_eq < it_am) // to omit '=' character
  363. ++it_eq;
  364. std::string value(it_eq, it_am);
  365. // Save the pair (name,value).
  366. query.push_back(std::make_pair(std::move(name), std::move(value)));
  367. // Store the offset of the name.
  368. strings_positions.push_back(it - begin);
  369. // Store the offset of the value.
  370. strings_positions.push_back(it_eq - begin);
  371. // Move |it| to the beginning of the next pair.
  372. if (it_am < end)
  373. ++it_am; // to omit '&' character
  374. it = it_am;
  375. }
  376. // Try to set the new Query and return true if succeed.
  377. if (SaveQuery<true>(query))
  378. return true;
  379. // An error occurred, adjust the |parser_error| set by SetQuery(...).
  380. parser_error_.parsed_chars += strings_positions[parser_error_.parsed_strings];
  381. parser_error_.parsed_strings = 0;
  382. return false;
  383. }
  384. bool Uri::Pim::ParseFragment(const Iter& begin, const Iter& end) {
  385. parser_error_.parsed_strings = 0;
  386. std::string out;
  387. if (!ParseString<true>(begin, end, &out))
  388. return false;
  389. fragment_ = std::move(out);
  390. return true;
  391. }
  392. bool Uri::Pim::ParseUri(const Iter& begin, const Iter end) {
  393. parser_error_.status = ParserStatus::kNoErrors;
  394. parser_error_.parsed_strings = 0;
  395. parser_error_.parsed_chars = 0;
  396. Iter it1 = begin;
  397. // The Scheme component starts from character different than slash ("/"),
  398. // question mark ("?"), and number sign ("#"). Non-empty Scheme must be
  399. // followed by the colon (":") character.
  400. if (it1 < end && *it1 != '/' && *it1 != '?' && *it1 != '#') {
  401. auto it2 = std::find(it1, end, ':');
  402. if (it2 == end) {
  403. parser_error_.status = ParserStatus::kInvalidScheme;
  404. return false;
  405. }
  406. if (!ParseScheme(it1, it2))
  407. return false;
  408. it1 = ++it2;
  409. }
  410. // The authority component is preceded by a double slash ("//") and is
  411. // terminated by the next slash ("/"), question mark ("?"), or number
  412. // sign ("#") character, or by the end of the URI.
  413. if (it1 < end && *it1 == '/') {
  414. ++it1;
  415. if (it1 < end && *it1 == '/') {
  416. ++it1;
  417. auto it_auth_end = FindFirstOf(it1, end, "/?#");
  418. if (!ParseAuthority(it1, it_auth_end)) {
  419. parser_error_.parsed_chars += it1 - begin;
  420. return false;
  421. }
  422. it1 = it_auth_end;
  423. } else {
  424. --it1;
  425. }
  426. }
  427. // The Path is terminated by the first question mark ("?") or number
  428. // sign ("#") character, or by the end of the URI.
  429. if (it1 < end) {
  430. auto it2 = FindFirstOf(it1, end, "?#");
  431. if (!ParsePath(it1, it2)) {
  432. parser_error_.parsed_chars += it1 - begin;
  433. return false;
  434. }
  435. it1 = it2;
  436. }
  437. // The Query component is indicated by the first question mark ("?")
  438. // character and terminated by a number sign ("#") character or by the end
  439. // of the URI.
  440. if (it1 < end && *it1 == '?') {
  441. ++it1;
  442. auto it2 = std::find(it1, end, '#');
  443. if (!ParseQuery(it1, it2)) {
  444. parser_error_.parsed_chars += it1 - begin;
  445. return false;
  446. }
  447. it1 = it2;
  448. }
  449. // A Fragment component is indicated by the presence of a number
  450. // sign ("#") character and terminated by the end of the URI.
  451. if (it1 < end) {
  452. DCHECK_EQ(*it1, '#');
  453. ++it1; // to omit '#' character
  454. if (!ParseFragment(it1, end)) {
  455. parser_error_.parsed_chars += it1 - begin;
  456. return false;
  457. }
  458. }
  459. // Success!
  460. return true;
  461. }
  462. template bool Uri::Pim::ParseString<false, false>(const Iter& begin,
  463. const Iter& end,
  464. std::string* out,
  465. bool plus_to_space);
  466. template bool Uri::Pim::ParseString<false, true>(const Iter& begin,
  467. const Iter& end,
  468. std::string* out,
  469. bool plus_to_space);
  470. template bool Uri::Pim::ParseString<true, false>(const Iter& begin,
  471. const Iter& end,
  472. std::string* out,
  473. bool plus_to_space);
  474. template bool Uri::Pim::ParseString<true, true>(const Iter& begin,
  475. const Iter& end,
  476. std::string* out,
  477. bool plus_to_space);
  478. template bool Uri::Pim::SaveUserinfo<false>(const std::string& val);
  479. template bool Uri::Pim::SaveUserinfo<true>(const std::string& val);
  480. template bool Uri::Pim::SaveHost<false>(const std::string& val);
  481. template bool Uri::Pim::SaveHost<true>(const std::string& val);
  482. template bool Uri::Pim::SavePath<false>(const std::vector<std::string>& val);
  483. template bool Uri::Pim::SavePath<true>(const std::vector<std::string>& val);
  484. template bool Uri::Pim::SaveQuery<false>(
  485. const std::vector<std::pair<std::string, std::string>>& val);
  486. template bool Uri::Pim::SaveQuery<true>(
  487. const std::vector<std::pair<std::string, std::string>>& val);
  488. template bool Uri::Pim::SaveFragment<false>(const std::string& val);
  489. template bool Uri::Pim::SaveFragment<true>(const std::string& val);
  490. } // namespace chromeos