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