chromium/chrome/browser/ui/webui/ash/network_logs_message_handler.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/webui/ash/network_logs_message_handler.h"

#include <iostream>

#include "base/files/file_util.h"
#include "base/system/sys_info.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "chrome/browser/ash/file_manager/filesystem_api_util.h"
#include "chrome/browser/ash/system_logs/debug_log_writer.h"
#include "chrome/browser/ash/system_logs/system_logs_writer.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/policy/chrome_policy_conversions_client.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/logging_chrome.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/ash/components/dbus/debug_daemon/debug_daemon_client.h"
#include "components/policy/core/browser/policy_conversions.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "ui/base/l10n/l10n_util.h"

namespace ash {

namespace {

base::FilePath GetDownloadsDirectory(content::WebUI* web_ui) {
  Profile* profile = Profile::FromWebUI(web_ui);
  const DownloadPrefs* const prefs = DownloadPrefs::FromBrowserContext(profile);
  base::FilePath path = prefs->DownloadPath();
  if (file_manager::util::IsUnderNonNativeLocalPath(profile, path))
    path = prefs->GetDefaultDownloadDirectoryForProfile();
  return path;
}

std::string GetJsonPolicies(content::WebUI* web_ui) {
  return policy::PolicyConversions(
             std::make_unique<policy::ChromePolicyConversionsClient>(
                 web_ui->GetWebContents()->GetBrowserContext()))
      .ToJSON();
}

bool WriteTimestampedFile(const base::FilePath& file_path,
                          const std::string& contents) {
  base::FilePath timestamped_file_path =
      logging::GenerateTimestampedName(file_path, base::Time::Now());
  return base::WriteFile(timestamped_file_path, contents);
}

bool GetBoolOrFalse(const base::Value::Dict& dict, const char* keyname) {
  const auto key = dict.FindBool(keyname);
  return key && *key;
}

}  // namespace

NetworkLogsMessageHandler::NetworkLogsMessageHandler() = default;

NetworkLogsMessageHandler::~NetworkLogsMessageHandler() = default;

void NetworkLogsMessageHandler::RegisterMessages() {
  out_dir_ = GetDownloadsDirectory(web_ui());
  web_ui()->RegisterMessageCallback(
      "storeLogs", base::BindRepeating(&NetworkLogsMessageHandler::OnStoreLogs,
                                       base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "setShillDebugging",
      base::BindRepeating(&NetworkLogsMessageHandler::OnSetShillDebugging,
                          base::Unretained(this)));
}

void NetworkLogsMessageHandler::Respond(const std::string& callback_id,
                                        const std::string& result,
                                        bool is_error) {
  base::Value::List response;
  response.Append(result);
  response.Append(is_error);
  ResolveJavascriptCallback(base::Value(callback_id), response);
}

void NetworkLogsMessageHandler::OnStoreLogs(const base::Value::List& list) {
  CHECK_EQ(2u, list.size());
  std::string callback_id = list[0].GetString();
  const base::Value::Dict& options = list[1].GetDict();
  AllowJavascript();

  if (GetBoolOrFalse(options, "systemLogs")) {
    bool scrub_data = GetBoolOrFalse(options, "filterPII");
    system_logs_writer::WriteSystemLogs(
        Profile::FromWebUI(web_ui()), out_dir_, scrub_data,
        base::BindOnce(&NetworkLogsMessageHandler::OnWriteSystemLogs,
                       weak_factory_.GetWeakPtr(), callback_id,
                       options.Clone()));
  } else {
    MaybeWriteDebugLogs(callback_id, options.Clone());
  }
}

void NetworkLogsMessageHandler::OnWriteSystemLogs(
    const std::string& callback_id,
    base::Value::Dict&& options,
    std::optional<base::FilePath> syslogs_path) {
  if (!syslogs_path) {
    Respond(callback_id, "Error writing system logs file.", /*is_error=*/true);
    return;
  }
  MaybeWriteDebugLogs(callback_id, std::move(options));
}

void NetworkLogsMessageHandler::MaybeWriteDebugLogs(
    const std::string& callback_id,
    base::Value::Dict&& options) {
  if (GetBoolOrFalse(options, "debugLogs")) {
    if (!base::SysInfo::IsRunningOnChromeOS()) {
      Respond(callback_id, "Debug logs unavailable on Linux build.",
              /*is_error=*/true);
      return;
    }
    bool include_chrome = GetBoolOrFalse(options, "chromeLogs");
    debug_log_writer::StoreLogs(
        out_dir_, include_chrome,
        base::BindOnce(&NetworkLogsMessageHandler::OnWriteDebugLogs,
                       weak_factory_.GetWeakPtr(), callback_id,
                       std::move(options)));
  } else {
    MaybeWritePolicies(callback_id, std::move(options));
  }
}

void NetworkLogsMessageHandler::OnWriteDebugLogs(
    const std::string& callback_id,
    base::Value::Dict&& options,
    std::optional<base::FilePath> logs_path) {
  if (!logs_path) {
    Respond(callback_id, "Error writing debug logs.", /*is_error=*/true);
    return;
  }
  MaybeWritePolicies(callback_id, std::move(options));
}

void NetworkLogsMessageHandler::MaybeWritePolicies(
    const std::string& callback_id,
    base::Value::Dict&& options) {
  if (GetBoolOrFalse(options, "policies")) {
    std::string json_policies = GetJsonPolicies(web_ui());
    base::ThreadPool::PostTaskAndReplyWithResult(
        FROM_HERE,
        {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
         base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
        base::BindOnce(WriteTimestampedFile, out_dir_.Append("policies.json"),
                       json_policies),
        base::BindOnce(&NetworkLogsMessageHandler::OnWritePolicies,
                       weak_factory_.GetWeakPtr(), callback_id));
  } else {
    OnWriteSystemLogsCompleted(callback_id);
  }
}

void NetworkLogsMessageHandler::OnWritePolicies(const std::string& callback_id,
                                                bool result) {
  if (!result) {
    Respond(callback_id, "Error writing policies.", /*is_error=*/true);
    return;
  }
  OnWriteSystemLogsCompleted(callback_id);
}

void NetworkLogsMessageHandler::OnWriteSystemLogsCompleted(
    const std::string& callback_id) {
  Respond(callback_id,
          l10n_util::GetStringUTF8(IDS_NETWORK_UI_NETWORK_LOGS_SUCCESS),
          /*is_error=*/false);
}

void NetworkLogsMessageHandler::OnSetShillDebugging(
    const base::Value::List& list) {
  CHECK_EQ(2u, list.size());
  std::string callback_id = list[0].GetString();
  std::string subsystem = list[1].GetString();
  AllowJavascript();
  DebugDaemonClient::Get()->SetDebugMode(
      subsystem,
      base::BindOnce(&NetworkLogsMessageHandler::OnSetShillDebuggingCompleted,
                     weak_factory_.GetWeakPtr(), callback_id));
}

void NetworkLogsMessageHandler::OnSetShillDebuggingCompleted(
    const std::string& callback_id,
    bool succeeded) {
  Respond(callback_id, /*result=*/"", !succeeded);
}

}  // namespace ash