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