/* * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * 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. */ #include #include #include "hilog/log.h" #include "string_ex.h" #include "uri.h" using std::string; using std::regex; using OHOS::HiviewDFX::HiLog; namespace OHOS { namespace { const string NOT_CACHED = "NOT VALID"; const string EMPTY = ""; const size_t NOT_FOUND = string::npos; const int NOT_CALCULATED = -2; const int PORT_NONE = -1; const char SCHEME_SEPARATOR = ':'; const char SCHEME_FRAGMENT = '#'; const char LEFT_SEPARATOR = '/'; const char RIGHT_SEPARATOR = '\\'; const char QUERY_FLAG = '?'; const char USER_HOST_SEPARATOR = '@'; const char PORT_SEPARATOR = ':'; const size_t POS_INC = 1; const size_t POS_INC_MORE = 2; const size_t POS_INC_AGAIN = 3; }; // namespace Uri::Uri(const string& uriString) { cachedSsi_ = NOT_FOUND; cachedFsi_ = NOT_FOUND; port_ = NOT_CALCULATED; if (uriString.empty()) { return; } uriString_ = uriString; scheme_ = NOT_CACHED; ssp_ = NOT_CACHED; authority_ = NOT_CACHED; host_ = NOT_CACHED; userInfo_ = NOT_CACHED; query_ = NOT_CACHED; path_ = NOT_CACHED; fragment_ = NOT_CACHED; if (!CheckScheme()) { uriString_ = EMPTY; HILOG_IMPL(LOG_CORE, LOG_ERROR, 0xD001305, "URI", "Scheme wrong"); } } Uri::~Uri() {} bool Uri::CheckScheme() { scheme_ = ParseScheme(); if (scheme_.empty()) { return true; } try { regex schemeRegex("[a-zA-Z][a-zA-Z|\\d|\\+|\\-|.]*$"); if (!regex_match(scheme_, schemeRegex)) { return false; } } catch (std::regex_error &message) { HILOG_IMPL(LOG_CORE, LOG_ERROR, 0xD001305, "URI", "regex fail,message:%{public}s", message.what()); return false; } return true; } string Uri::GetScheme() { if (uriString_.empty()) { return EMPTY; } if (scheme_ == NOT_CACHED) { scheme_ = ParseScheme(); } return scheme_; } string Uri::ParseScheme() { size_t ssi = FindSchemeSeparator(); return (ssi == NOT_FOUND) ? EMPTY : uriString_.substr(0, ssi); } string Uri::GetSchemeSpecificPart() { if (uriString_.empty()) { return EMPTY; } return (ssp_ == NOT_CACHED) ? (ssp_ = ParseSsp()) : ssp_; } string Uri::ParseSsp() { size_t ssi = FindSchemeSeparator(); size_t fsi = FindFragmentSeparator(); size_t start = (ssi == NOT_FOUND) ? 0 : (ssi + 1); size_t end = (fsi == NOT_FOUND) ? uriString_.size() : fsi; // Return everything between ssi and fsi. string ssp = EMPTY; if (end > start) { ssp = uriString_.substr(start, end - start); } return ssp; } string Uri::GetAuthority() { if (uriString_.empty()) { return EMPTY; } if (authority_ == NOT_CACHED) { authority_ = ParseAuthority(); } return authority_; } string Uri::ParseAuthority() { size_t ssi = FindSchemeSeparator(); if (ssi == NOT_FOUND) { return EMPTY; } size_t length = uriString_.length(); // If "//" follows the scheme separator, we have an authority. if ((length > (ssi + POS_INC_MORE)) && (uriString_.at(ssi + POS_INC) == LEFT_SEPARATOR) && (uriString_.at(ssi + POS_INC_MORE) == LEFT_SEPARATOR)) { // Look for the start of the path, query, or fragment, or the end of the string. size_t start = ssi + POS_INC_AGAIN; size_t end = start; while (end < length) { char ch = uriString_.at(end); if ((ch == LEFT_SEPARATOR) || (ch == RIGHT_SEPARATOR) || (ch == QUERY_FLAG) || (ch == SCHEME_FRAGMENT)) { break; } end++; } return uriString_.substr(start, end - start); } else { return EMPTY; } } string Uri::GetUserInfo() { if (uriString_.empty()) { return EMPTY; } if (userInfo_ == NOT_CACHED) { userInfo_ = ParseUserInfo(); } return userInfo_; } string Uri::ParseUserInfo() { string authority = GetAuthority(); if (authority.empty()) { return EMPTY; } size_t end = authority.find_last_of(USER_HOST_SEPARATOR); return (end == NOT_FOUND) ? EMPTY : authority.substr(0, end); } string Uri::GetHost() { if (uriString_.empty()) { return EMPTY; } if (host_ == NOT_CACHED) { host_ = ParseHost(); } return host_; } string Uri::ParseHost() { string authority = GetAuthority(); if (authority.empty()) { return EMPTY; } // Parse out user info and then port. size_t userInfoSeparator = authority.find_last_of(USER_HOST_SEPARATOR); size_t start = (userInfoSeparator == NOT_FOUND) ? 0 : (userInfoSeparator + 1); size_t portSeparator = authority.find_first_of(PORT_SEPARATOR, start); size_t end = (portSeparator == NOT_FOUND) ? authority.size() : portSeparator; string host = EMPTY; if (start < end) { host = authority.substr(start, end - start); } return host; } int Uri::GetPort() { if (uriString_.empty()) { return PORT_NONE; } if (port_ == NOT_CALCULATED) { port_ = ParsePort(); } return port_; } int Uri::ParsePort() { string authority = GetAuthority(); if (authority.empty()) { return PORT_NONE; } // Make sure we look for the port separtor *after* the user info separator. size_t userInfoSeparator = authority.find_last_of(USER_HOST_SEPARATOR); size_t start = (userInfoSeparator == NOT_FOUND) ? 0 : (userInfoSeparator + 1); size_t portSeparator = authority.find_first_of(PORT_SEPARATOR, start); if (portSeparator == NOT_FOUND) { return PORT_NONE; } start = portSeparator + 1; string portString = authority.substr(start); int value = PORT_NONE; return StrToInt(portString, value) ? value : PORT_NONE; } string Uri::GetQuery() { if (uriString_.empty()) { return EMPTY; } if (query_ == NOT_CACHED) { query_ = ParseQuery(); } return query_; } string Uri::ParseQuery() { size_t ssi = FindSchemeSeparator(); if (ssi == NOT_FOUND) { ssi = 0; } size_t qsi = uriString_.find_first_of(QUERY_FLAG, ssi); if (qsi == NOT_FOUND) { return EMPTY; } size_t start = qsi + 1; size_t fsi = FindFragmentSeparator(); if (fsi == NOT_FOUND) { return uriString_.substr(start); } if (fsi < qsi) { // Invalid. return EMPTY; } return uriString_.substr(start, fsi - start); } string Uri::GetPath() { if (uriString_.empty()) { return EMPTY; } if (path_ == NOT_CACHED) { path_ = ParsePath(); } return path_; } void Uri::GetPathSegments(std::vector& segments) { if (uriString_.empty()) { return; } if (path_ == NOT_CACHED) { path_ = ParsePath(); } size_t previous = 0; size_t current; while ((current = path_.find(LEFT_SEPARATOR, previous)) != std::string::npos) { if (previous < current) { segments.emplace_back(path_.substr(previous, current - previous)); } previous = current + POS_INC; } // Add in the final path segment. if (previous < path_.length()) { segments.emplace_back(path_.substr(previous)); } } string Uri::ParsePath() { size_t ssi = FindSchemeSeparator(); // If the URI is absolute. if (ssi != NOT_FOUND) { // Is there anything after the ':'? if ((ssi + 1) == uriString_.length()) { // Opaque URI. return EMPTY; } // A '/' after the ':' means this is hierarchical. if (uriString_.at(ssi + 1) != LEFT_SEPARATOR) { // Opaque URI. return EMPTY; } } else { // All relative URIs are hierarchical. } return ParsePath(ssi); } string Uri::ParsePath(size_t ssi) { size_t length = uriString_.length(); // Find start of path. size_t pathStart = (ssi == NOT_FOUND) ? 0 : (ssi + POS_INC); if ((length > (pathStart + POS_INC)) && (uriString_.at(pathStart) == LEFT_SEPARATOR) && (uriString_.at(pathStart + POS_INC) == LEFT_SEPARATOR)) { // Skip over authority to path. pathStart += POS_INC_MORE; while (pathStart < length) { char ch = uriString_.at(pathStart); if ((ch == QUERY_FLAG) || (ch == SCHEME_FRAGMENT)) { return EMPTY; } if ((ch == LEFT_SEPARATOR) || (ch == RIGHT_SEPARATOR)) { break; } pathStart++; } } // Find end of path. size_t pathEnd = pathStart; while (pathEnd < length) { char ch = uriString_.at(pathEnd); if ((ch == QUERY_FLAG) || (ch == SCHEME_FRAGMENT)) { break; } pathEnd++; } return uriString_.substr(pathStart, pathEnd - pathStart); } string Uri::GetFragment() { if (uriString_.empty()) { return EMPTY; } if (fragment_ == NOT_CACHED) { fragment_ = ParseFragment(); } return fragment_; } string Uri::ParseFragment() { size_t fsi = FindFragmentSeparator(); return (fsi == NOT_FOUND) ? EMPTY : uriString_.substr(fsi + 1); } size_t Uri::FindSchemeSeparator() { if (cachedSsi_ == NOT_FOUND) { cachedSsi_ = uriString_.find_first_of(SCHEME_SEPARATOR); } return cachedSsi_; } size_t Uri::FindFragmentSeparator() { if (cachedFsi_ == NOT_FOUND) { cachedFsi_ = uriString_.find_first_of(SCHEME_FRAGMENT, FindSchemeSeparator()); } return cachedFsi_; } bool Uri::IsHierarchical() { if (uriString_.empty()) { return false; } size_t ssi = FindSchemeSeparator(); if (ssi == NOT_FOUND) { // All relative URIs are hierarchical. return true; } if (uriString_.length() == (ssi + 1)) { // No ssp. return false; } // If the ssp starts with a '/', this is hierarchical. return (uriString_.at(ssi + 1) == LEFT_SEPARATOR); } bool Uri::IsOpaque() { if (uriString_.empty()) { return false; } return !IsHierarchical(); } bool Uri::IsAbsolute() { if (uriString_.empty()) { return false; } return !IsRelative(); } bool Uri::IsRelative() { if (uriString_.empty()) { return false; } // Note: We return true if the index is 0 return FindSchemeSeparator() == NOT_FOUND; } bool Uri::Equals(const Uri& other) const { return ToString() == other.ToString(); } int Uri::CompareTo(const Uri& other) const { return ToString().compare(other.ToString()); } string Uri::ToString() const { return uriString_; } bool Uri::operator==(const Uri& other) const { return ToString() == other.ToString(); } bool Uri::Marshalling(Parcel& parcel) const { if (IsAsciiString(uriString_)) { return parcel.WriteString16(Str8ToStr16(uriString_)); } HILOG_IMPL(LOG_CORE, LOG_ERROR, 0xD001305, "URI", "uriString_ is not ascii string"); return false; } Uri* Uri::Unmarshalling(Parcel& parcel) { return new Uri(Str16ToStr8(parcel.ReadString16())); } } // namespace OHOS