blob: 8a8b009155a903623419459db0c7c24f9b3d7dd0 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// 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/settings/chromeos/cups_printers_handler.h"
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/files/file_util.h"
#include "base/json/json_string_value_serializer.h"
#include "base/memory/ptr_util.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/printing/ppd_provider_factory.h"
#include "chrome/browser/chromeos/printing/printer_configurer.h"
#include "chrome/browser/chromeos/printing/printer_discoverer.h"
#include "chrome/browser/chromeos/printing/printers_manager_factory.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_select_file_policy.h"
#include "chrome/common/chrome_paths.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/debug_daemon_client.h"
#include "chromeos/printing/ppd_cache.h"
#include "chromeos/printing/ppd_provider.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_ui.h"
#include "google_apis/google_api_keys.h"
#include "net/base/filename_util.h"
#include "net/url_request/url_request_context_getter.h"
#include "printing/backend/print_backend.h"
#include "url/third_party/mozilla/url_parse.h"
namespace chromeos {
namespace settings {
namespace {
void OnRemovedPrinter(bool success) {}
std::unique_ptr<base::DictionaryValue> GetPrinterInfo(const Printer& printer) {
std::unique_ptr<base::DictionaryValue> printer_info =
base::MakeUnique<base::DictionaryValue>();
printer_info->SetString("printerId", printer.id());
printer_info->SetString("printerName", printer.display_name());
printer_info->SetString("printerDescription", printer.description());
printer_info->SetString("printerManufacturer", printer.manufacturer());
printer_info->SetString("printerModel", printer.model());
// Get protocol, ip address and queue from the printer's URI.
const std::string printer_uri = printer.uri();
url::Parsed parsed;
url::ParseStandardURL(printer_uri.c_str(), printer_uri.length(), &parsed);
std::string scheme;
std::string host;
std::string path;
if (parsed.scheme.len > 0)
scheme = std::string(printer_uri, parsed.scheme.begin, parsed.scheme.len);
if (parsed.host.len > 0)
host = std::string(printer_uri, parsed.host.begin, parsed.host.len);
if (parsed.path.len > 0)
path = std::string(printer_uri, parsed.path.begin, parsed.path.len);
printer_info->SetString("printerAddress", host);
printer_info->SetString("printerProtocol", base::ToLowerASCII(scheme));
if (base::ToLowerASCII(scheme) == "lpd" && !path.empty())
printer_info->SetString("printerQueue", path.substr(1));
return printer_info;
}
} // namespace
CupsPrintersHandler::CupsPrintersHandler(content::WebUI* webui)
: printer_discoverer_(nullptr),
profile_(Profile::FromWebUI(webui)),
weak_factory_(this) {
ppd_provider_ = printing::CreateProvider(profile_);
printer_configurer_ = chromeos::PrinterConfigurer::Create(profile_);
}
CupsPrintersHandler::~CupsPrintersHandler() {}
void CupsPrintersHandler::RegisterMessages() {
web_ui()->RegisterMessageCallback(
"getCupsPrintersList",
base::Bind(&CupsPrintersHandler::HandleGetCupsPrintersList,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"updateCupsPrinter",
base::Bind(&CupsPrintersHandler::HandleUpdateCupsPrinter,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"removeCupsPrinter",
base::Bind(&CupsPrintersHandler::HandleRemoveCupsPrinter,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"addCupsPrinter", base::Bind(&CupsPrintersHandler::HandleAddCupsPrinter,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"getCupsPrinterManufacturersList",
base::Bind(&CupsPrintersHandler::HandleGetCupsPrinterManufacturers,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"getCupsPrinterModelsList",
base::Bind(&CupsPrintersHandler::HandleGetCupsPrinterModels,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"selectPPDFile", base::Bind(&CupsPrintersHandler::HandleSelectPPDFile,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"startDiscoveringPrinters",
base::Bind(&CupsPrintersHandler::HandleStartDiscovery,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"stopDiscoveringPrinters",
base::Bind(&CupsPrintersHandler::HandleStopDiscovery,
base::Unretained(this)));
}
void CupsPrintersHandler::HandleGetCupsPrintersList(
const base::ListValue* args) {
AllowJavascript();
CHECK_EQ(1U, args->GetSize());
std::string callback_id;
CHECK(args->GetString(0, &callback_id));
std::vector<std::unique_ptr<Printer>> printers =
PrintersManagerFactory::GetForBrowserContext(profile_)->GetPrinters();
base::ListValue* printers_list = new base::ListValue;
for (const std::unique_ptr<Printer>& printer : printers) {
std::unique_ptr<base::DictionaryValue> printer_info =
GetPrinterInfo(*printer.get());
printers_list->Append(std::move(printer_info));
}
std::unique_ptr<base::DictionaryValue> response =
base::MakeUnique<base::DictionaryValue>();
response->Set("printerList", printers_list);
ResolveJavascriptCallback(base::Value(callback_id), *response);
}
void CupsPrintersHandler::HandleUpdateCupsPrinter(const base::ListValue* args) {
std::string printer_id;
std::string printer_name;
CHECK(args->GetString(0, &printer_id));
CHECK(args->GetString(1, &printer_name));
std::unique_ptr<Printer> printer = base::MakeUnique<Printer>(printer_id);
printer->set_display_name(printer_name);
PrintersManagerFactory::GetForBrowserContext(profile_)->RegisterPrinter(
std::move(printer));
}
void CupsPrintersHandler::HandleRemoveCupsPrinter(const base::ListValue* args) {
std::string printer_id;
std::string printer_name;
CHECK(args->GetString(0, &printer_id));
CHECK(args->GetString(1, &printer_name));
PrintersManagerFactory::GetForBrowserContext(profile_)->RemovePrinter(
printer_id);
chromeos::DebugDaemonClient* client =
chromeos::DBusThreadManager::Get()->GetDebugDaemonClient();
client->CupsRemovePrinter(printer_name, base::Bind(&OnRemovedPrinter),
base::Bind(&base::DoNothing));
}
void CupsPrintersHandler::HandleAddCupsPrinter(const base::ListValue* args) {
AllowJavascript();
const base::DictionaryValue* printer_dict = nullptr;
CHECK(args->GetDictionary(0, &printer_dict));
std::string printer_id;
std::string printer_name;
std::string printer_description;
std::string printer_manufacturer;
std::string printer_model;
std::string printer_address;
std::string printer_protocol;
std::string printer_queue;
std::string printer_ppd_path;
CHECK(printer_dict->GetString("printerId", &printer_id));
CHECK(printer_dict->GetString("printerName", &printer_name));
CHECK(printer_dict->GetString("printerDescription", &printer_description));
CHECK(printer_dict->GetString("printerManufacturer", &printer_manufacturer));
CHECK(printer_dict->GetString("printerModel", &printer_model));
CHECK(printer_dict->GetString("printerAddress", &printer_address));
CHECK(printer_dict->GetString("printerProtocol", &printer_protocol));
// printerQueue might be null for a printer whose protocol is not 'LPD'.
printer_dict->GetString("printerQueue", &printer_queue);
// printerPPDPath might be null for an auto-discovered printer.
printer_dict->GetString("printerPPDPath", &printer_ppd_path);
std::string printer_uri =
printer_protocol + "://" + printer_address + "/" + printer_queue;
std::unique_ptr<Printer> printer = base::MakeUnique<Printer>(printer_id);
printer->set_display_name(printer_name);
printer->set_description(printer_description);
printer->set_manufacturer(printer_manufacturer);
printer->set_model(printer_model);
printer->set_uri(printer_uri);
// Verify a valid ppd path is present.
if (!printer_ppd_path.empty()) {
GURL tmp = net::FilePathToFileURL(base::FilePath(printer_ppd_path));
if (!tmp.is_valid()) {
LOG(ERROR) << "Invalid ppd path: " << printer_ppd_path;
OnAddPrinterError();
return;
}
printer->mutable_ppd_reference()->user_supplied_ppd_url = tmp.spec();
} else if (!printer_manufacturer.empty() && !printer_model.empty()) {
// Using the manufacturer and model, get a ppd reference.
if (!ppd_provider_->GetPpdReference(printer_manufacturer, printer_model,
printer->mutable_ppd_reference())) {
LOG(ERROR) << "Failed to get ppd reference";
OnAddPrinterError();
return;
}
}
// Copy the printer for the configurer. Ownership needs to be transfered to
// the receiver of the callback.
const Printer printer_copy = *printer;
printer_configurer_->SetUpPrinter(
printer_copy,
base::Bind(&CupsPrintersHandler::OnAddedPrinter,
weak_factory_.GetWeakPtr(), base::Passed(&printer)));
}
void CupsPrintersHandler::OnAddedPrinter(
std::unique_ptr<Printer> printer,
chromeos::PrinterSetupResult result_code) {
std::string printer_name = printer->display_name();
switch (result_code) {
case chromeos::PrinterSetupResult::SUCCESS:
PrintersManagerFactory::GetForBrowserContext(profile_)->RegisterPrinter(
std::move(printer));
break;
case chromeos::PrinterSetupResult::PPD_NOT_FOUND:
LOG(WARNING) << "Could not locate requested PPD";
break;
case chromeos::PrinterSetupResult::PPD_TOO_LARGE:
LOG(WARNING) << "PPD is too large";
break;
case chromeos::PrinterSetupResult::PPD_UNRETRIEVABLE:
LOG(WARNING) << "Could not retrieve PPD from server";
break;
case chromeos::PrinterSetupResult::INVALID_PPD:
LOG(WARNING) << "Provided PPD is invalid.";
break;
case chromeos::PrinterSetupResult::PRINTER_UNREACHABLE:
LOG(WARNING) << "Could not contact printer for configuration";
break;
case chromeos::PrinterSetupResult::DBUS_ERROR:
case chromeos::PrinterSetupResult::FATAL_ERROR:
LOG(ERROR) << "Unrecoverable error. Reboot required.";
break;
}
CallJavascriptFunction(
"cr.webUIListenerCallback", base::Value("on-add-cups-printer"),
base::Value(result_code == chromeos::PrinterSetupResult::SUCCESS),
base::Value(printer_name));
}
void CupsPrintersHandler::OnAddPrinterError() {
CallJavascriptFunction("cr.webUIListenerCallback",
base::Value("on-add-cups-printer"), base::Value(false),
base::Value(""));
}
void CupsPrintersHandler::HandleGetCupsPrinterManufacturers(
const base::ListValue* args) {
AllowJavascript();
std::string js_callback;
CHECK_EQ(1U, args->GetSize());
CHECK(args->GetString(0, &js_callback));
ppd_provider_->ResolveManufacturers(
base::Bind(&CupsPrintersHandler::ResolveManufacturersDone,
weak_factory_.GetWeakPtr(), js_callback));
}
void CupsPrintersHandler::HandleGetCupsPrinterModels(
const base::ListValue* args) {
AllowJavascript();
std::string js_callback;
std::string manufacturer;
CHECK_EQ(2U, args->GetSize());
CHECK(args->GetString(0, &js_callback));
CHECK(args->GetString(1, &manufacturer));
// Empty manufacturer queries may be triggered as a part of the ui
// initialization, and should just return empty results.
if (manufacturer.empty()) {
base::DictionaryValue response;
response.SetBoolean("success", true);
response.Set("models", base::MakeUnique<base::ListValue>());
ResolveJavascriptCallback(base::Value(js_callback), response);
return;
}
ppd_provider_->ResolvePrinters(
manufacturer, base::Bind(&CupsPrintersHandler::ResolvePrintersDone,
weak_factory_.GetWeakPtr(), js_callback));
}
void CupsPrintersHandler::HandleSelectPPDFile(const base::ListValue* args) {
CHECK_EQ(1U, args->GetSize());
CHECK(args->GetString(0, &webui_callback_id_));
base::FilePath downloads_path =
DownloadPrefs::FromDownloadManager(
content::BrowserContext::GetDownloadManager(profile_))
->DownloadPath();
select_file_dialog_ = ui::SelectFileDialog::Create(
this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
gfx::NativeWindow owning_window =
chrome::FindBrowserWithWebContents(web_ui()->GetWebContents())
->window()
->GetNativeWindow();
select_file_dialog_->SelectFile(
ui::SelectFileDialog::SELECT_OPEN_FILE, base::string16(), downloads_path,
nullptr, 0, FILE_PATH_LITERAL(""), owning_window, nullptr);
}
void CupsPrintersHandler::ResolveManufacturersDone(
const std::string& js_callback,
chromeos::printing::PpdProvider::CallbackResultCode result_code,
const std::vector<std::string>& manufacturers) {
auto manufacturers_value = base::MakeUnique<base::ListValue>();
if (result_code == chromeos::printing::PpdProvider::SUCCESS) {
manufacturers_value->AppendStrings(manufacturers);
}
base::DictionaryValue response;
response.SetBoolean("success",
result_code == chromeos::printing::PpdProvider::SUCCESS);
response.Set("manufacturers", std::move(manufacturers_value));
ResolveJavascriptCallback(base::Value(js_callback), response);
}
void CupsPrintersHandler::ResolvePrintersDone(
const std::string& js_callback,
chromeos::printing::PpdProvider::CallbackResultCode result_code,
const std::vector<std::string>& printers) {
auto printers_value = base::MakeUnique<base::ListValue>();
if (result_code == chromeos::printing::PpdProvider::SUCCESS) {
printers_value->AppendStrings(printers);
}
base::DictionaryValue response;
response.SetBoolean("success",
result_code == chromeos::printing::PpdProvider::SUCCESS);
response.Set("models", std::move(printers_value));
ResolveJavascriptCallback(base::Value(js_callback), response);
}
void CupsPrintersHandler::FileSelected(const base::FilePath& path,
int index,
void* params) {
DCHECK(!webui_callback_id_.empty());
ResolveJavascriptCallback(base::Value(webui_callback_id_),
base::Value(path.value()));
webui_callback_id_.clear();
}
void CupsPrintersHandler::HandleStartDiscovery(const base::ListValue* args) {
if (!printer_discoverer_.get()) {
printer_discoverer_ =
chromeos::PrinterDiscoverer::CreateForProfile(profile_);
}
printer_discoverer_->AddObserver(this);
}
void CupsPrintersHandler::HandleStopDiscovery(const base::ListValue* args) {
printer_discoverer_.reset();
}
void CupsPrintersHandler::OnPrintersFound(
const std::vector<Printer>& printers) {
std::unique_ptr<base::ListValue> printers_list =
base::MakeUnique<base::ListValue>();
for (const auto& printer : printers) {
printers_list->Append(GetPrinterInfo(printer));
}
CallJavascriptFunction("cr.webUIListenerCallback",
base::Value("on-printer-discovered"), *printers_list);
}
void CupsPrintersHandler::OnDiscoveryInitialScanDone() {
CallJavascriptFunction("cr.webUIListenerCallback",
base::Value("on-printer-discovery-done"));
}
} // namespace settings
} // namespace chromeos