diff --git a/components/ipfs/brave_ipfs_client_updater.cc b/components/ipfs/brave_ipfs_client_updater.cc index 0adac261abecb..66f2ce3e86fc4 100644 --- a/components/ipfs/brave_ipfs_client_updater.cc +++ b/components/ipfs/brave_ipfs_client_updater.cc @@ -13,8 +13,8 @@ #include "base/task/thread_pool.h" #include "base/task_runner.h" #include "base/task_runner_util.h" +#include "brave/components/ipfs/ipfs_utils.h" #include "components/component_updater/component_updater_service.h" -#include "third_party/re2/src/re2/re2.h" namespace ipfs { @@ -57,8 +57,7 @@ base::FilePath InitExecutablePath(const base::FilePath& install_dir) { for (base::FilePath current = traversal.Next(); !current.empty(); current = traversal.Next()) { base::FileEnumerator::FileInfo file_info = traversal.GetInfo(); - if (!RE2::FullMatch(file_info.GetName().MaybeAsASCII(), - "go-ipfs_v\\d+\\.\\d+\\.\\d+\\_\\w+-amd64")) + if (!ipfs::IsValidNodeFilename(file_info.GetName().MaybeAsASCII())) continue; executable_path = current; break; diff --git a/components/ipfs/ipfs_service.cc b/components/ipfs/ipfs_service.cc index fa3eea903b550..e5430155f9e27 100644 --- a/components/ipfs/ipfs_service.cc +++ b/components/ipfs/ipfs_service.cc @@ -109,6 +109,7 @@ IpfsService::IpfsService(content::BrowserContext* context, if (ipfs_client_updater_) { ipfs_client_updater_->AddObserver(this); OnExecutableReady(ipfs_client_updater_->GetExecutablePath()); + RegisterIpfsClientUpdater(); } ipns_keys_manager_ = std::make_unique(context_, server_endpoint_); diff --git a/components/ipfs/ipfs_utils.cc b/components/ipfs/ipfs_utils.cc index 182181fbf611a..45e1ef28f001b 100644 --- a/components/ipfs/ipfs_utils.cc +++ b/components/ipfs/ipfs_utils.cc @@ -21,6 +21,7 @@ #include "content/public/browser/browser_context.h" #include "extensions/common/url_pattern.h" #include "net/base/url_util.h" +#include "third_party/re2/src/re2/re2.h" #include "url/gurl.h" namespace { @@ -32,6 +33,12 @@ GURL AppendLocalPort(const std::string& port) { return gateway.ReplaceComponents(replacements); } +// RegEx to validate the node name: +// go-ipfs_v0.9.0-rc1_windows-amd64 - valid +// go-ipfs_v0.9.0_windows-amd64 - valid +constexpr char kExecutableRegEx[] = + "go-ipfs_v(\\d+\\.\\d+\\.\\d+)(-rc\\d+)?\\_\\w+-amd64"; + // Valid CID multibase prefix, "code" character // from https://github.com/multiformats/multibase/blob/master/multibase.csv const char kCIDv1Codes[] = "079fFvVtTbBcChkKzZmMuU"; @@ -331,4 +338,8 @@ bool ParsePeerConnectionString(const std::string& value, return valid; } +bool IsValidNodeFilename(const std::string& filename) { + return RE2::FullMatch(filename, kExecutableRegEx); +} + } // namespace ipfs diff --git a/components/ipfs/ipfs_utils.h b/components/ipfs/ipfs_utils.h index c7d1098839881..fb020269b706b 100644 --- a/components/ipfs/ipfs_utils.h +++ b/components/ipfs/ipfs_utils.h @@ -58,6 +58,7 @@ bool TranslateIPFSURI(const GURL& url, const GURL& gateway_url, bool use_subdomain); bool IsIpfsMenuEnabled(content::BrowserContext* browser_context); +bool IsValidNodeFilename(const std::string& filename); bool ParsePeerConnectionString(const std::string& value, std::string* id, diff --git a/components/ipfs/ipfs_utils_unittest.cc b/components/ipfs/ipfs_utils_unittest.cc index 00748687a27d9..827bd89dd4eca 100644 --- a/components/ipfs/ipfs_utils_unittest.cc +++ b/components/ipfs/ipfs_utils_unittest.cc @@ -596,3 +596,21 @@ TEST_F(IpfsUtilsUnitTest, ParsePeerConnectionStringTest) { value, "12D3KooWBdmLJjhpgJ9KZgLM3f894ff9xyBfPvPjFNn7MKJpyrC2", "/ip4/46.21.210.45/udp/14406/quic")); } + +TEST_F(IpfsUtilsUnitTest, ValidateNodeFilename) { + ASSERT_TRUE(ipfs::IsValidNodeFilename("go-ipfs_v0.9.0-rc1_windows-amd64")); + ASSERT_TRUE(ipfs::IsValidNodeFilename("go-ipfs_v0.9.0-rc21_windows-amd64")); + ASSERT_TRUE(ipfs::IsValidNodeFilename("go-ipfs_v0.9.0_windows-amd64")); + + ASSERT_TRUE(ipfs::IsValidNodeFilename("go-ipfs_v0.9.0-rc1_darwin-amd64")); + ASSERT_TRUE(ipfs::IsValidNodeFilename("go-ipfs_v0.9.0-rc21_darwin-amd64")); + ASSERT_TRUE(ipfs::IsValidNodeFilename("go-ipfs_v0.9.0_darwin-amd64")); + + ASSERT_TRUE(ipfs::IsValidNodeFilename("go-ipfs_v0.9.0-rc1_linux-amd64")); + ASSERT_TRUE(ipfs::IsValidNodeFilename("go-ipfs_v0.9.0-rc21_linux-amd64")); + ASSERT_TRUE(ipfs::IsValidNodeFilename("go-ipfs_v0.9.0_linux-amd64")); + + ASSERT_FALSE(ipfs::IsValidNodeFilename("")); + ASSERT_FALSE(ipfs::IsValidNodeFilename("ipfs.exe")); + ASSERT_FALSE(ipfs::IsValidNodeFilename("go-ipfs_v0.9.0_linux")); +} diff --git a/components/services/ipfs/BUILD.gn b/components/services/ipfs/BUILD.gn index 7a2380db0d87c..39d683a8a2f06 100644 --- a/components/services/ipfs/BUILD.gn +++ b/components/services/ipfs/BUILD.gn @@ -1,5 +1,9 @@ source_set("ipfs") { - sources = [ "ipfs_service_impl.h" ] + sources = [ + "ipfs_service_impl.h", + "ipfs_service_utils.cc", + "ipfs_service_utils.h", + ] if (!is_android) { sources += [ "ipfs_service_impl.cc" ] } else { diff --git a/components/services/ipfs/ipfs_service_impl.cc b/components/services/ipfs/ipfs_service_impl.cc index 14f46648cfcde..a4b9e5a91f6c9 100644 --- a/components/services/ipfs/ipfs_service_impl.cc +++ b/components/services/ipfs/ipfs_service_impl.cc @@ -12,6 +12,7 @@ #include "base/command_line.h" #include "base/files/file_util.h" #include "base/process/launch.h" +#include "brave/components/services/ipfs/ipfs_service_utils.h" namespace { @@ -105,22 +106,36 @@ void IpfsServiceImpl::Launch(mojom::IpfsConfigPtr config, if (!base::PathExists(config_path)) { // run ipfs init to gen config if (!LaunchProcessAndExit(config->binary_path, {"init"}, options)) { - std::move(callback).Run(false, -1); + if (callback) + std::move(callback).Run(false, -1); return; } } + std::string data; + if (!base::ReadFileToString(config_path, &data)) { + VLOG(1) << "Unable to read the ipfs config:" << config_path; + if (callback) + std::move(callback).Run(false, -1); + return; + } + + std::string updated_config; + if (!ipfs::UpdateConfigJSON(data, config.get(), &updated_config)) { + VLOG(1) << "Unable to update the ipfs config:" << config_path; + if (callback) + std::move(callback).Run(false, -1); + return; + } + if (!base::WriteFile(config_path, updated_config)) { + VLOG(1) << "Unable to write the ipfs config:" << config_path; + if (callback) + std::move(callback).Run(false, -1); + return; + } std::initializer_list> config_args = { {"shutdown"}, // Cleanup left-over daemon process. - {"config", "Addresses.API", "/ip4/127.0.0.1/tcp/" + config->api_port}, - {"config", "Addresses.Gateway", - "/ip4/127.0.0.1/tcp/" + config->gateway_port}, - {"config", "--json", "Addresses.Swarm", - "[\"/ip4/0.0.0.0/tcp/" + config->swarm_port + "\", \"/ip6/::/tcp/" + - config->swarm_port + "\"]"}, - {"config", "Datastore.GCPeriod", "1h"}, - {"config", "--json", "Swarm.ConnMgr.LowWater", "50"}, - {"config", "--json", "Swarm.ConnMgr.HighWater", "300"}}; + }; for (auto args : config_args) { if (!LaunchProcessAndExit(config->binary_path, args, options)) { @@ -129,14 +144,6 @@ void IpfsServiceImpl::Launch(mojom::IpfsConfigPtr config, } } - // Configure storage - if (!LaunchProcessAndExit( - config->binary_path, - {"config", "Datastore.StorageMax", config->storage_max}, options)) { - std::move(callback).Run(false, -1); - return; - } - child_monitor_ = std::make_unique(); // Launch IPFS daemon. diff --git a/components/services/ipfs/ipfs_service_utils.cc b/components/services/ipfs/ipfs_service_utils.cc new file mode 100644 index 0000000000000..2004c4a403e54 --- /dev/null +++ b/components/services/ipfs/ipfs_service_utils.cc @@ -0,0 +1,58 @@ +/* Copyright (c) 2021 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 +#include + +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" + +#include "brave/components/services/ipfs/ipfs_service_utils.h" + +#include "brave/components/services/ipfs/public/mojom/ipfs_service.mojom.h" + +namespace ipfs { + +// Updates the ipfs node config to meet current preferences +bool UpdateConfigJSON(const std::string& source, + const ipfs::mojom::IpfsConfig* config, + std::string* result) { + base::JSONReader::ValueWithError value_with_error = + base::JSONReader::ReadAndReturnValueWithError( + source, base::JSONParserOptions::JSON_PARSE_RFC); + base::Optional& records_v = value_with_error.value; + if (!records_v) { + VLOG(1) << "Could not parse JSON, JSON is: " << source; + return false; + } + base::DictionaryValue* dict = nullptr; + if (!records_v->GetAsDictionary(&dict)) { + VLOG(1) << "Could not parse JSON, JSON is: " << source; + return false; + } + dict->Set("Addresses.API", std::make_unique( + "/ip4/127.0.0.1/tcp/" + config->api_port)); + dict->Set("Addresses.Gateway", + std::make_unique("/ip4/127.0.0.1/tcp/" + + config->gateway_port)); + dict->Set("Datastore.GCPeriod", std::make_unique("1h")); + dict->Set("Swarm.ConnMgr.LowWater", std::make_unique(50)); + dict->Set("Swarm.ConnMgr.HighWater", std::make_unique(300)); + dict->Set("Datastore.StorageMax", + std::make_unique(config->storage_max)); + std::unique_ptr list = std::make_unique(); + list->Append(base::Value("/ip4/0.0.0.0/tcp/" + config->swarm_port)); + list->Append(base::Value("/ip6/::/tcp/" + config->swarm_port)); + dict->Set("Addresses.Swarm", std::move(list)); + std::string json_string; + if (!base::JSONWriter::Write(records_v.value(), &json_string) || + json_string.empty()) { + return false; + } + *result = json_string; + return true; +} + +} // namespace ipfs diff --git a/components/services/ipfs/ipfs_service_utils.h b/components/services/ipfs/ipfs_service_utils.h new file mode 100644 index 0000000000000..01867beb8094c --- /dev/null +++ b/components/services/ipfs/ipfs_service_utils.h @@ -0,0 +1,24 @@ +/* Copyright (c) 2021 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/. */ + +#ifndef BRAVE_COMPONENTS_SERVICES_IPFS_IPFS_SERVICE_UTILS_H_ +#define BRAVE_COMPONENTS_SERVICES_IPFS_IPFS_SERVICE_UTILS_H_ + +#include + +namespace ipfs { + +namespace mojom { +class IpfsConfig; +} + +// Updates the ipfs node config to meet current preferences +bool UpdateConfigJSON(const std::string& source, + const ipfs::mojom::IpfsConfig* config, + std::string* result); + +} // namespace ipfs + +#endif // BRAVE_COMPONENTS_SERVICES_IPFS_IPFS_SERVICE_UTILS_H_ diff --git a/components/services/ipfs/ipfs_service_utils_unittest.cc b/components/services/ipfs/ipfs_service_utils_unittest.cc new file mode 100644 index 0000000000000..92d417170541c --- /dev/null +++ b/components/services/ipfs/ipfs_service_utils_unittest.cc @@ -0,0 +1,68 @@ +/* Copyright (c) 2021 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 + +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" + +#include "brave/components/services/ipfs/ipfs_service_utils.h" + +#include "brave/components/services/ipfs/public/mojom/ipfs_service.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace ipfs { + +typedef testing::Test IPFSServiceUtils; + +TEST_F(IPFSServiceUtils, UpdateConfigJSONTest) { + std::string json = R"({})"; + std::string updated; + auto config = ipfs::mojom::IpfsConfig::New( + base::FilePath(), base::FilePath(), base::FilePath(), "GatewayPort", + "APIPort", "SwarmPort", "StorageSize"); + + std::string expect = + "{\"Addresses\":{\"API\":\"/ip4/127.0.0.1/tcp/APIPort\"," + "\"Gateway\":\"/ip4/127.0.0.1/tcp/GatewayPort\",\"Swarm\":" + "[\"/ip4/0.0.0.0/tcp/SwarmPort\",\"/ip6/::/tcp/SwarmPort\"" + "]},\"Datastore\":{\"GCPeriod\":\"1h\",\"StorageMax\":" + "\"StorageSize\"},\"Swarm\":{\"ConnMgr\":{\"HighWater\"" + ":300,\"LowWater\":50}}}"; + ASSERT_TRUE(UpdateConfigJSON(json, config.get(), &updated)); + EXPECT_EQ(updated, expect); + updated.clear(); + json = R"({ + "Addresses":{ + "API":"/ip4/127.0.0.1/tcp/111", + "Gateway":"/ip4/127.0.0.1/tcp/111", + "Swarm":[ + "/ip4/0.0.0.0/tcp/222", + "/ip6/::/tcp/222", + "/ip6/::/udp/666" + ] + }, + "Datastore":{ + "GCPeriod":"6h", + "StorageMax":"9GB" + }, + "Swarm":{ + "ConnMgr":{ + "HighWater":0, + "LowWater":0 + } + } + })"; + ASSERT_TRUE(UpdateConfigJSON(json, config.get(), &updated)); + EXPECT_EQ(updated, expect); + updated.clear(); + ASSERT_FALSE(UpdateConfigJSON("{", config.get(), &updated)); + EXPECT_EQ(updated, ""); + updated.clear(); + ASSERT_FALSE(UpdateConfigJSON("[]", config.get(), &updated)); + EXPECT_EQ(updated, ""); +} + +} // namespace ipfs diff --git a/components/services/ipfs/test/BUILD.gn b/components/services/ipfs/test/BUILD.gn new file mode 100644 index 0000000000000..4433dd4992a9b --- /dev/null +++ b/components/services/ipfs/test/BUILD.gn @@ -0,0 +1,23 @@ +# Copyright (c) 2021 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/. */ + +import("//brave/build/config.gni") +import("//brave/components/ipfs/buildflags/buildflags.gni") +import("//testing/test.gni") + +source_set("ipfs_service_unit_tests") { + testonly = true + if (ipfs_enabled) { + sources = + [ "//brave/components/services/ipfs/ipfs_service_utils_unittest.cc" ] + + deps = [ + "//base/test:test_support", + "//brave/components/services/ipfs", + "//brave/components/services/ipfs/public/mojom", + "//testing/gtest", + ] + } # if (ipfs_enabled) +} # source_set("ipfs_service_unit_tests") diff --git a/test/BUILD.gn b/test/BUILD.gn index 0c9c26f5358e7..14b9c82459987 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -165,6 +165,7 @@ test("brave_unit_tests") { "//brave/components/ntp_widget_utils/browser", "//brave/components/p3a", "//brave/components/permissions:unit_tests", + "//brave/components/services/ipfs/test:ipfs_service_unit_tests", "//brave/components/sidebar:unit_tests", "//brave/components/tor:tor_unit_tests", "//brave/components/tor/buildflags",