Files
brave-core/components/ipfs/ipfs_utils.cc
T
2021-03-24 00:44:11 +03:00

277 lines
9.2 KiB
C++

/* Copyright (c) 2020 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "brave/components/ipfs/ipfs_utils.h"
#include <vector>
#include "base/feature_list.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "brave/components/ipfs/features.h"
#include "brave/components/ipfs/ipfs_constants.h"
#include "brave/components/ipfs/ipfs_ports.h"
#include "brave/components/ipfs/pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/user_prefs/user_prefs.h"
#include "content/public/browser/browser_context.h"
#include "extensions/common/url_pattern.h"
#include "net/base/url_util.h"
#include "url/gurl.h"
namespace {
GURL AppendLocalPort(const std::string& port) {
GURL gateway = GURL(ipfs::kDefaultIPFSLocalGateway);
GURL::Replacements replacements;
replacements.SetPortStr(port);
return gateway.ReplaceComponents(replacements);
}
// Valid CID multibase prefix, "code" character
// from https://github.com/multiformats/multibase/blob/master/multibase.csv
const char kCIDv1Codes[] = "079fFvVtTbBcChkKzZmMuU";
const char kCIDv0Prefix[] = "Qm";
} // namespace
namespace ipfs {
// Simple CID validation based on multibase table.
bool IsValidCID(const std::string& cid) {
if (!cid.size())
return false;
if (!std::all_of(cid.begin(), cid.end(), [loc = std::locale{}](char c) {
return std::isalnum(c, loc);
}))
return false;
if (std::string(kCIDv1Codes).find(cid.at(0)) != std::string::npos)
return true;
return base::StartsWith(cid, kCIDv0Prefix);
}
bool IsIpfsDisabledByPolicy(content::BrowserContext* context) {
DCHECK(context);
PrefService* prefs = user_prefs::UserPrefs::Get(context);
return prefs->FindPreference(kIPFSEnabled) &&
prefs->IsManagedPreference(kIPFSEnabled) &&
!prefs->GetBoolean(kIPFSEnabled);
}
bool IsIpfsEnabled(content::BrowserContext* context) {
DCHECK(context);
if (context->IsOffTheRecord() || IsIpfsDisabledByPolicy(context) ||
!base::FeatureList::IsEnabled(ipfs::features::kIpfsFeature)) {
return false;
}
return true;
}
bool IsIpfsResolveMethodDisabled(content::BrowserContext* context) {
DCHECK(context);
// Ignore the actual pref value if IPFS feature is disabled.
if (!IsIpfsEnabled(context)) {
return true;
}
PrefService* prefs = user_prefs::UserPrefs::Get(context);
return prefs->FindPreference(kIPFSResolveMethod) &&
prefs->GetInteger(kIPFSResolveMethod) ==
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_DISABLED);
}
bool HasIPFSPath(const GURL& gurl) {
static std::vector<URLPattern> url_patterns(
{URLPattern(URLPattern::SCHEME_ALL, "*://*/ipfs/*"),
URLPattern(URLPattern::SCHEME_ALL, "*://*/ipns/*")});
return std::any_of(
url_patterns.begin(), url_patterns.end(),
[&gurl](URLPattern pattern) { return pattern.MatchesURL(gurl); });
}
bool IsDefaultGatewayURL(const GURL& url, content::BrowserContext* context) {
DCHECK(context);
std::string gateway_host = GetDefaultIPFSGateway(context).host();
return url.DomainIs(gateway_host) &&
(HasIPFSPath(url) ||
url.DomainIs(std::string("ipfs.") + gateway_host) ||
url.DomainIs(std::string("ipns.") + gateway_host));
}
bool IsLocalGatewayURL(const GURL& url) {
return url.SchemeIsHTTPOrHTTPS() &&
((net::IsLocalhost(url) && HasIPFSPath(url)) ||
url.DomainIs(std::string("ipfs.localhost")) ||
url.DomainIs(std::string("ipns.localhost")));
}
bool IsIPFSScheme(const GURL& url) {
return url.SchemeIs(kIPFSScheme) || url.SchemeIs(kIPNSScheme);
}
GURL ToPublicGatewayURL(const GURL& url, content::BrowserContext* context) {
DCHECK(context);
DCHECK(IsIPFSScheme(url) || IsLocalGatewayURL(url));
GURL new_url;
// For ipfs/ipns schemes, use TranslateIPFSURI directly.
if (IsIPFSScheme(url) &&
TranslateIPFSURI(url, &new_url, GetDefaultIPFSGateway(context), false)) {
return new_url;
}
// For local gateway addresses, replace its scheme, host, port with the
// public gateway URL.
if (IsLocalGatewayURL(url)) {
GURL::Replacements replacements;
GURL gateway_url = GetDefaultIPFSGateway(context);
replacements.ClearPort();
replacements.SetSchemeStr(gateway_url.scheme_piece());
replacements.SetHostStr(gateway_url.host_piece());
return url.ReplaceComponents(replacements);
}
return new_url;
}
GURL GetGatewayURL(const std::string& cid,
const std::string& path,
const GURL& base_gateway_url,
bool ipfs) {
GURL uri(base_gateway_url);
GURL::Replacements replacements;
std::string host = base::StringPrintf(
"%s.%s.%s", cid.c_str(), ipfs ? "ipfs" : "ipns", uri.host().c_str());
replacements.SetHostStr(host);
replacements.SetPathStr(path);
return uri.ReplaceComponents(replacements);
}
GURL GetIPFSGatewayURL(const std::string& cid,
const std::string& path,
const GURL& base_gateway_url) {
return GetGatewayURL(cid, path, base_gateway_url, true);
}
GURL GetIPNSGatewayURL(const std::string& cid,
const std::string& path,
const GURL& base_gateway_url) {
return GetGatewayURL(cid, path, base_gateway_url, false);
}
bool IsLocalGatewayConfigured(content::BrowserContext* context) {
PrefService* prefs = user_prefs::UserPrefs::Get(context);
return static_cast<IPFSResolveMethodTypes>(prefs->GetInteger(
kIPFSResolveMethod)) == IPFSResolveMethodTypes::IPFS_LOCAL;
}
GURL GetConfiguredBaseGateway(content::BrowserContext* context,
version_info::Channel channel) {
return IsLocalGatewayConfigured(context)
? ::ipfs::GetDefaultIPFSLocalGateway(channel)
: ::ipfs::GetDefaultIPFSGateway(context);
}
bool ResolveIPFSURI(content::BrowserContext* context,
version_info::Channel channel,
const GURL& ipfs_uri,
GURL* resolved_url) {
CHECK(resolved_url);
return ::ipfs::TranslateIPFSURI(
ipfs_uri, resolved_url, GetConfiguredBaseGateway(context, channel), true);
}
GURL ipfs_default_gateway_for_test;
void SetIPFSDefaultGatewayForTest(const GURL& url) {
ipfs_default_gateway_for_test = url;
}
GURL GetDefaultIPFSLocalGateway(version_info::Channel channel) {
return AppendLocalPort(GetGatewayPort(channel));
}
GURL GetDefaultIPFSGateway(content::BrowserContext* context) {
if (!ipfs_default_gateway_for_test.is_empty()) {
return GURL(ipfs_default_gateway_for_test);
}
DCHECK(context);
PrefService* prefs = user_prefs::UserPrefs::Get(context);
GURL gateway_url(prefs->GetString(kIPFSPublicGatewayAddress));
if (gateway_url.DomainIs(kLocalhostIP)) {
GURL::Replacements replacements;
replacements.SetHostStr(kLocalhostDomain);
return gateway_url.ReplaceComponents(replacements);
}
return gateway_url;
}
GURL GetAPIServer(version_info::Channel channel) {
return AppendLocalPort(GetAPIPort(channel));
}
bool TranslateIPFSURI(const GURL& url,
GURL* new_url,
const GURL& gateway_url,
bool use_subdomain) {
if (!url.SchemeIs(kIPFSScheme) && !url.SchemeIs(kIPNSScheme)) {
return false;
}
if (!url.host().empty())
return false;
// ipfs: or ipfs://
size_t offset = (url.path().substr(0, 2) == "//") ? 2 : 0;
// In the case of a URL like ipfs://[cid]/wiki/Vincent_van_Gogh.html
// host is empty and path is //wiki/Vincent_van_Gogh.html
std::string cid(url.path().substr(offset));
// If we have a path after the CID, get at the real resource path
size_t pos = cid.find("/");
std::string path;
if (pos != std::string::npos && pos != 0) {
// path would be /wiki/Vincent_van_Gogh.html
path = cid.substr(pos, cid.length() - pos);
// cid would be [cid]
cid = cid.substr(0, pos);
}
bool ipfs_scheme = url.scheme() == kIPFSScheme;
bool ipns_scheme = url.scheme() == kIPNSScheme;
if ((ipfs_scheme && std::all_of(cid.begin(), cid.end(),
[loc = std::locale{}](char c) {
return std::isalnum(c, loc);
})) ||
ipns_scheme) {
// new_url would be:
// https://dweb.link/ipfs/[cid]//wiki/Vincent_van_Gogh.html
if (new_url) {
GURL::Replacements replacements;
replacements.SetSchemeStr(gateway_url.scheme_piece());
replacements.SetPortStr(gateway_url.port_piece());
std::string new_host = gateway_url.host();
std::string new_path = path;
if (use_subdomain) {
new_host = base::StringPrintf("%s.%s.%s", cid.c_str(),
ipfs_scheme ? "ipfs" : "ipns",
gateway_url.host().c_str());
} else {
new_path = (ipfs_scheme ? "ipfs/" : "ipns/") + cid + path;
}
replacements.SetHostStr(new_host);
replacements.SetPathStr(new_path);
*new_url = url.ReplaceComponents(replacements);
VLOG(1) << "[IPFS] " << __func__ << " new URL: " << *new_url;
}
return true;
}
return false;
}
} // namespace ipfs