1 // Copyright 2019 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
5 #include "chrome/browser/chromeos/input_method/ime_service_connector.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/files/file_util.h"
11 #include "chrome/browser/chromeos/service_sandbox_type.h"
12 #include "chromeos/constants/chromeos_features.h"
13 #include "chromeos/services/ime/constants.h"
14 #include "chromeos/strings/grit/chromeos_strings.h"
15 #include "content/public/browser/service_process_host.h"
16 #include "net/base/load_flags.h"
17 #include "net/http/http_status_code.h"
18 #include "net/traffic_annotation/network_traffic_annotation.h"
19 #include "services/network/public/cpp/resource_request.h"
20 #include "services/network/public/cpp/shared_url_loader_factory.h"
21 #include "services/network/public/mojom/network_context.mojom.h"
22
23 namespace chromeos {
24 namespace input_method {
25
26 namespace {
27
28 constexpr net::NetworkTrafficAnnotationTag traffic_annotation =
29 net::DefineNetworkTrafficAnnotation("ime_url_downloader", R"(
30 semantics {
31 sender: "IME Service Downloader"
32 description:
33 "When user selects a new input method in ChromeOS, it may request a"
34 "corresponding language module downloaded if it does not exist."
35 trigger: "User switches to an input method without language module."
36 data:
37 "The language module download URL. No user identifier is sent."
38 destination: GOOGLE_OWNED_SERVICE
39 }
40 policy {
41 cookies_allowed: NO
42 policy_exception_justification:
43 "Not implemented, considered not useful."
44 })");
45
IsDownloadPathValid(const base::FilePath & file_path)46 bool IsDownloadPathValid(const base::FilePath& file_path) {
47 // Only non-empty, relative path which doesn't reference a parent is allowed.
48 if (file_path.empty() || file_path.IsAbsolute() ||
49 file_path.ReferencesParent())
50 return false;
51
52 // Target path must be restricted in the provided path.
53 base::FilePath parent(chromeos::ime::kInputMethodsDirName);
54 parent = parent.Append(chromeos::ime::kLanguageDataDirName);
55 return parent.IsParent(file_path);
56 }
57
IsDownloadURLValid(const GURL & url)58 bool IsDownloadURLValid(const GURL& url) {
59 // TODO(https://crbug.com/837156): Allowlist all URLs instead of some general
60 // checks below.
61 return url.SchemeIs(url::kHttpsScheme) &&
62 url.DomainIs(chromeos::ime::kGoogleKeyboardDownloadDomain);
63 }
64
65 } // namespace
66
ImeServiceConnector(Profile * profile)67 ImeServiceConnector::ImeServiceConnector(Profile* profile)
68 : profile_(profile), url_loader_factory_(profile->GetURLLoaderFactory()) {}
69
70 ImeServiceConnector::~ImeServiceConnector() = default;
71
DownloadImeFileTo(const GURL & url,const base::FilePath & file_path,DownloadImeFileToCallback callback)72 void ImeServiceConnector::DownloadImeFileTo(
73 const GURL& url,
74 const base::FilePath& file_path,
75 DownloadImeFileToCallback callback) {
76 // For now, we don't allow the client to download multi files at same time.
77 // Downloading request will be aborted and return empty before the current
78 // downloading task exits.
79 // TODO(https://crbug.com/971954): Support multi downloads.
80 // Validate url and file_path, return an empty file path if not.
81 if (url_loader_ || !IsDownloadURLValid(url) ||
82 !IsDownloadPathValid(file_path)) {
83 base::FilePath empty_path;
84 std::move(callback).Run(empty_path);
85 return;
86 }
87
88 auto resource_request = std::make_unique<network::ResourceRequest>();
89 resource_request->url = url;
90 resource_request->load_flags =
91 net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
92 // Disable cookies for this request.
93 resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
94
95 url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
96 traffic_annotation);
97
98 // Download the language module into a preconfigured ime folder of current
99 // user's home which is allowed in IME service's sandbox.
100 base::FilePath full_path = profile_->GetPath().Append(file_path);
101 url_loader_->DownloadToFile(
102 url_loader_factory_.get(),
103 base::BindOnce(&ImeServiceConnector::OnFileDownloadComplete,
104 base::Unretained(this), std::move(callback)),
105 full_path);
106 }
107
SetupImeService(mojo::PendingReceiver<chromeos::ime::mojom::InputEngineManager> receiver)108 void ImeServiceConnector::SetupImeService(
109 mojo::PendingReceiver<chromeos::ime::mojom::InputEngineManager> receiver) {
110 if (!remote_service_) {
111 content::ServiceProcessHost::Launch(
112 remote_service_.BindNewPipeAndPassReceiver(),
113 content::ServiceProcessHost::Options()
114 .WithDisplayName(IDS_IME_SERVICE_DISPLAY_NAME)
115 .Pass());
116 remote_service_.reset_on_disconnect();
117
118 platform_access_receiver_.reset();
119 remote_service_->SetPlatformAccessProvider(
120 platform_access_receiver_.BindNewPipeAndPassRemote());
121 }
122
123 remote_service_->BindInputEngineManager(std::move(receiver));
124 }
125
OnFileDownloadComplete(DownloadImeFileToCallback client_callback,base::FilePath path)126 void ImeServiceConnector::OnFileDownloadComplete(
127 DownloadImeFileToCallback client_callback,
128 base::FilePath path) {
129 std::move(client_callback).Run(path);
130 url_loader_.reset();
131 return;
132 }
133
134 } // namespace input_method
135 } // namespace chromeos
136