1 // Copyright 2020 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/ui/webui/chromeos/network_logs_message_handler.h"
6
7 #include <iostream>
8
9 #include "base/files/file_util.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/system/sys_info.h"
12 #include "base/task/task_traits.h"
13 #include "base/task/thread_pool.h"
14 #include "chrome/browser/chromeos/file_manager/filesystem_api_util.h"
15 #include "chrome/browser/chromeos/system_logs/debug_log_writer.h"
16 #include "chrome/browser/chromeos/system_logs/system_logs_writer.h"
17 #include "chrome/browser/download/download_prefs.h"
18 #include "chrome/browser/policy/chrome_policy_conversions_client.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/common/logging_chrome.h"
21 #include "chrome/grit/generated_resources.h"
22 #include "chromeos/dbus/dbus_thread_manager.h"
23 #include "chromeos/dbus/debug_daemon/debug_daemon_client.h"
24 #include "components/policy/core/browser/policy_conversions.h"
25 #include "content/public/browser/web_contents.h"
26 #include "content/public/browser/web_ui.h"
27 #include "ui/base/l10n/l10n_util.h"
28
29 namespace chromeos {
30
31 namespace {
32
GetDownloadsDirectory(content::WebUI * web_ui)33 base::FilePath GetDownloadsDirectory(content::WebUI* web_ui) {
34 Profile* profile = Profile::FromWebUI(web_ui);
35 const DownloadPrefs* const prefs = DownloadPrefs::FromBrowserContext(profile);
36 base::FilePath path = prefs->DownloadPath();
37 if (file_manager::util::IsUnderNonNativeLocalPath(profile, path))
38 path = prefs->GetDefaultDownloadDirectoryForProfile();
39 return path;
40 }
41
GetJsonPolicies(content::WebUI * web_ui)42 std::string GetJsonPolicies(content::WebUI* web_ui) {
43 auto client = std::make_unique<policy::ChromePolicyConversionsClient>(
44 web_ui->GetWebContents()->GetBrowserContext());
45 return policy::DictionaryPolicyConversions(std::move(client)).ToJSON();
46 }
47
WriteTimestampedFile(const base::FilePath & file_path,const std::string & contents)48 bool WriteTimestampedFile(const base::FilePath& file_path,
49 const std::string& contents) {
50 base::FilePath timestamped_file_path =
51 logging::GenerateTimestampedName(file_path, base::Time::Now());
52 int bytes_written =
53 base::WriteFile(timestamped_file_path, contents.data(), contents.size());
54 return bytes_written > 0;
55 }
56
GetBoolOrFalse(const base::Value * dict,const char * keyname)57 bool GetBoolOrFalse(const base::Value* dict, const char* keyname) {
58 const base::Value* key = dict->FindKey(keyname);
59 return key && key->GetBool();
60 }
61
62 } // namespace
63
64 NetworkLogsMessageHandler::NetworkLogsMessageHandler() = default;
65
66 NetworkLogsMessageHandler::~NetworkLogsMessageHandler() = default;
67
RegisterMessages()68 void NetworkLogsMessageHandler::RegisterMessages() {
69 out_dir_ = GetDownloadsDirectory(web_ui());
70 web_ui()->RegisterMessageCallback(
71 "storeLogs", base::BindRepeating(&NetworkLogsMessageHandler::OnStoreLogs,
72 base::Unretained(this)));
73 web_ui()->RegisterMessageCallback(
74 "setShillDebugging",
75 base::BindRepeating(&NetworkLogsMessageHandler::OnSetShillDebugging,
76 base::Unretained(this)));
77 }
78
Respond(const std::string & callback_id,const std::string & result,bool is_error)79 void NetworkLogsMessageHandler::Respond(const std::string& callback_id,
80 const std::string& result,
81 bool is_error) {
82 base::Value response(base::Value::Type::LIST);
83 response.Append(result);
84 response.Append(is_error);
85 ResolveJavascriptCallback(base::Value(callback_id), response);
86 }
87
OnStoreLogs(const base::ListValue * list)88 void NetworkLogsMessageHandler::OnStoreLogs(const base::ListValue* list) {
89 CHECK_EQ(2u, list->GetSize());
90 std::string callback_id;
91 CHECK(list->GetString(0, &callback_id));
92 const base::Value* options;
93 CHECK(list->Get(1, &options));
94 AllowJavascript();
95
96 if (GetBoolOrFalse(options, "systemLogs")) {
97 bool scrub_data = GetBoolOrFalse(options, "filterPII");
98 chromeos::system_logs_writer::WriteSystemLogs(
99 out_dir_, scrub_data,
100 base::BindOnce(&NetworkLogsMessageHandler::OnWriteSystemLogs,
101 weak_factory_.GetWeakPtr(), callback_id,
102 options->Clone()));
103 } else {
104 MaybeWriteDebugLogs(callback_id, options->Clone());
105 }
106 }
107
OnWriteSystemLogs(const std::string & callback_id,base::Value && options,base::Optional<base::FilePath> syslogs_path)108 void NetworkLogsMessageHandler::OnWriteSystemLogs(
109 const std::string& callback_id,
110 base::Value&& options,
111 base::Optional<base::FilePath> syslogs_path) {
112 if (!syslogs_path) {
113 Respond(callback_id, "Error writing system logs file.", /*is_error=*/true);
114 return;
115 }
116 MaybeWriteDebugLogs(callback_id, std::move(options));
117 }
118
MaybeWriteDebugLogs(const std::string & callback_id,base::Value && options)119 void NetworkLogsMessageHandler::MaybeWriteDebugLogs(
120 const std::string& callback_id,
121 base::Value&& options) {
122 if (GetBoolOrFalse(&options, "debugLogs")) {
123 if (!base::SysInfo::IsRunningOnChromeOS()) {
124 Respond(callback_id, "Debug logs unavailable on Linux build.",
125 /*is_error=*/true);
126 return;
127 }
128 bool include_chrome = GetBoolOrFalse(&options, "chromeLogs");
129 chromeos::debug_log_writer::StoreLogs(
130 out_dir_, include_chrome,
131 base::BindOnce(&NetworkLogsMessageHandler::OnWriteDebugLogs,
132 weak_factory_.GetWeakPtr(), callback_id,
133 std::move(options)));
134 } else {
135 MaybeWritePolicies(callback_id, std::move(options));
136 }
137 }
138
OnWriteDebugLogs(const std::string & callback_id,base::Value && options,base::Optional<base::FilePath> logs_path)139 void NetworkLogsMessageHandler::OnWriteDebugLogs(
140 const std::string& callback_id,
141 base::Value&& options,
142 base::Optional<base::FilePath> logs_path) {
143 if (!logs_path) {
144 Respond(callback_id, "Error writing debug logs.", /*is_error=*/true);
145 return;
146 }
147 MaybeWritePolicies(callback_id, std::move(options));
148 }
149
MaybeWritePolicies(const std::string & callback_id,base::Value && options)150 void NetworkLogsMessageHandler::MaybeWritePolicies(
151 const std::string& callback_id,
152 base::Value&& options) {
153 if (GetBoolOrFalse(&options, "policies")) {
154 std::string json_policies = GetJsonPolicies(web_ui());
155 base::ThreadPool::PostTaskAndReplyWithResult(
156 FROM_HERE,
157 {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
158 base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
159 base::BindOnce(WriteTimestampedFile, out_dir_.Append("policies.json"),
160 json_policies),
161 base::BindOnce(&NetworkLogsMessageHandler::OnWritePolicies,
162 weak_factory_.GetWeakPtr(), callback_id));
163 } else {
164 OnWriteSystemLogsCompleted(callback_id);
165 }
166 }
167
OnWritePolicies(const std::string & callback_id,bool result)168 void NetworkLogsMessageHandler::OnWritePolicies(const std::string& callback_id,
169 bool result) {
170 if (!result) {
171 Respond(callback_id, "Error writing policies.", /*is_error=*/true);
172 return;
173 }
174 OnWriteSystemLogsCompleted(callback_id);
175 }
176
OnWriteSystemLogsCompleted(const std::string & callback_id)177 void NetworkLogsMessageHandler::OnWriteSystemLogsCompleted(
178 const std::string& callback_id) {
179 Respond(callback_id,
180 l10n_util::GetStringUTF8(IDS_NETWORK_UI_NETWORK_LOGS_SUCCESS),
181 /*is_error=*/false);
182 }
183
OnSetShillDebugging(const base::ListValue * list)184 void NetworkLogsMessageHandler::OnSetShillDebugging(
185 const base::ListValue* list) {
186 CHECK_EQ(2u, list->GetSize());
187 std::string callback_id, subsystem;
188 CHECK(list->GetString(0, &callback_id));
189 CHECK(list->GetString(1, &subsystem));
190 AllowJavascript();
191 chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->SetDebugMode(
192 subsystem,
193 base::BindOnce(&NetworkLogsMessageHandler::OnSetShillDebuggingCompleted,
194 weak_factory_.GetWeakPtr(), callback_id));
195 }
196
OnSetShillDebuggingCompleted(const std::string & callback_id,bool succeeded)197 void NetworkLogsMessageHandler::OnSetShillDebuggingCompleted(
198 const std::string& callback_id,
199 bool succeeded) {
200 Respond(callback_id, /*result=*/"", !succeeded);
201 }
202
203 } // namespace chromeos
204