blob: 85f63dbe8f3ac3bfab6af474f0a73960d8341036 [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/crosapi/cpp/gurl_os_handler_utils.h"
#include <stddef.h>
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "url/url_util.h"
namespace {
const char kOsScheme[] = "os";
const char kOsUrlPrefix[] = "os://";
const char kChromeUIScheme[] = "chrome";
const char kChromeUrlPrefix[] = "chrome://";
const char kOsUISettingsURL[] = "os://settings";
const char kChromeUIOSSettingsHost[] = "os-settings";
// The start of the host portion of a GURL which starts with the os scheme.
const size_t kHostStart = sizeof(kOsUrlPrefix) - 1;
// Used for sanitation - any of the characters will cut the rest of the URL.
const char kTerminatingCharacters[] = "/\\? #.%$&*<>+";
// Note that GURL can't operate on the "os://" scheme as it is intentionally
// not a registered scheme.
std::string GetValidHostAndSubhostFromOsUrl(const GURL& url,
bool include_path) {
// Only keep the scheme, host and sub-host. Everything else gets cut off.
const std::string& url_spec = base::ToLowerASCII(url.spec());
// Find the first character after the host start
std::size_t valid_spec_end =
url_spec.find_first_of(kTerminatingCharacters, kHostStart);
if (valid_spec_end == std::string::npos)
return url_spec.substr(kHostStart);
if (url_spec[valid_spec_end] == '/' && include_path) {
// A sub URL is allowed (e.g. chrome://settings/network) - so we skip the
// "/" with +1 and parse till we find the next terminating character.
const std::size_t sub_host_end =
url_spec.find_first_of(kTerminatingCharacters, valid_spec_end + 1);
if (sub_host_end == std::string::npos)
return url_spec.substr(kHostStart);
if (sub_host_end > valid_spec_end + 1)
valid_spec_end = sub_host_end;
}
// Copy beginning from after "os://" all characters in host and sub-host.
return url_spec.substr(kHostStart, valid_spec_end - kHostStart);
}
GURL GetValidHostAndSubhostFromGURL(GURL gurl, bool include_path) {
if (!gurl.is_valid() || !gurl.has_host())
return GURL();
if (!gurl.has_ref() && !gurl.has_username() && !gurl.has_password() &&
!gurl.has_query() && !gurl.has_port() &&
(include_path || !gurl.has_path())) {
return gurl;
}
GURL::Replacements replacements;
if (!include_path)
replacements.ClearPath();
replacements.ClearRef();
replacements.ClearUsername();
replacements.ClearPassword();
replacements.ClearQuery();
replacements.ClearPort();
return gurl.ReplaceComponents(replacements);
}
} // namespace
namespace crosapi {
namespace gurl_os_handler_utils {
GURL SanitizeAshURL(const GURL& url, bool include_path) {
if (!IsAshOsUrl(url))
return GetValidHostAndSubhostFromGURL(url, include_path);
return GURL(kOsUrlPrefix +
GetValidHostAndSubhostFromOsUrl(url, include_path));
}
GURL GetTargetURLFromLacrosURL(const GURL& url) {
GURL target_url = crosapi::gurl_os_handler_utils::SanitizeAshURL(url);
GURL short_target_url = crosapi::gurl_os_handler_utils::SanitizeAshURL(
url, /*include_path=*/false);
if (short_target_url != GURL(kOsUISettingsURL))
return target_url;
// Change os://settings/* into chrome://os-settings/* which will be the long
// term home for our OS-settings.
// This converts the os (GURL lib unusable) address into a chrome
// (GURL lib usable) address.
target_url =
crosapi::gurl_os_handler_utils::GetChromeUrlFromSystemUrl(target_url);
GURL::Replacements replacements;
replacements.SetHostStr(kChromeUIOSSettingsHost);
return target_url.ReplaceComponents(replacements);
}
bool IsUrlInList(const GURL& test_url, const std::vector<GURL>& list) {
// It is assumed that the provided URL is sanitized as requested by
// security at this point.
DCHECK(SanitizeAshURL(test_url) == test_url);
const GURL short_url = SanitizeAshURL(test_url, /*include_path=*/false);
for (const GURL& url : list) {
// It is expected that all os:// scheme items in the list are lower case
// no "/" at the end and properly sanitized as the GURL comparison will
// treat everything past the unknown scheme as an unknown string and hence
// do no processing.
DCHECK(!IsAshOsUrl(url) || SanitizeAshURL(url) == url);
if (test_url == url || short_url == url)
return true;
}
return false;
}
bool IsAshOsUrl(const GURL& url) {
if (!url.is_valid() || url.spec().length() <= strlen(kOsUrlPrefix))
return false;
// Do not use "url.SchemeIs(kOsScheme)" here as it would require further
// parsing of the "://" portion to see if the Url is also valid.
return base::StartsWith(url.spec(), kOsUrlPrefix,
base::CompareCase::INSENSITIVE_ASCII);
}
bool IsAshOsAsciiScheme(const base::StringPiece& scheme) {
return base::EqualsCaseInsensitiveASCII(scheme, kOsScheme);
}
std::string AshOsUrlHost(const GURL& url) {
if (!url.is_valid())
return "";
// If we are using a default scheme, GURL does all for us.
if (!IsAshOsUrl(url)) {
if (!url.has_host())
return "";
return url.host() + (url.path().length() > 1 ? url.path() : "");
}
return GetValidHostAndSubhostFromOsUrl(url, /*include_path=*/true);
}
// Convert a passed GURL from os:// to chrome://.
GURL GetSystemUrlFromChromeUrl(const GURL& url) {
DCHECK(url.SchemeIs(kChromeUIScheme));
return GURL(kOsUrlPrefix + url.host());
}
// Convert a passed GURL from chrome:// to os://.
GURL GetChromeUrlFromSystemUrl(const GURL& url) {
DCHECK(IsAshOsUrl(url));
return GURL(kChromeUrlPrefix + AshOsUrlHost(url));
}
} // namespace gurl_os_handler_utils
} // namespace crosapi