blob: 16b60459a32d5b07679596986627e4866ed1fb10 [file] [log] [blame]
// Copyright 2025 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/ash/printing/local_printer_impl.h"
#include <vector>
#include "ash/constants/ash_features.h"
#include "base/logging.h"
#include "chrome/browser/ash/printing/cups_printers_manager.h"
#include "chrome/browser/ash/printing/cups_printers_manager_factory.h"
#include "chrome/browser/ash/printing/oauth2/authorization_zones_manager.h"
#include "chrome/browser/ash/printing/oauth2/authorization_zones_manager_factory.h"
#include "chrome/browser/ash/printing/printer_authenticator.h"
#include "chrome/browser/ash/printing/printer_setup_util.h"
#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
#include "components/account_id/account_id.h"
namespace ash {
namespace {
LocalPrinter* g_instance = nullptr;
std::vector<chromeos::Printer> GetLocalPrinters(const AccountId& accountId) {
static constexpr chromeos::PrinterClass printer_classes_to_fetch[] = {
chromeos::PrinterClass::kSaved, chromeos::PrinterClass::kEnterprise,
chromeos::PrinterClass::kAutomatic, chromeos::PrinterClass::kDiscovered};
ash::CupsPrintersManager* printers_manager =
ash::CupsPrintersManagerFactory::GetForBrowserContext(
ash::BrowserContextHelper::Get()->GetBrowserContextByAccountId(
accountId));
std::vector<chromeos::Printer> printers;
for (chromeos::PrinterClass pc : printer_classes_to_fetch) {
for (const chromeos::Printer& p : printers_manager->GetPrinters(pc)) {
VLOG(1) << "Found printer " << p.display_name() << " with device name "
<< p.id();
printers.push_back(p);
}
}
return printers;
}
// Mark if a not yet installed printer is autoconf then continue with setup.
void OnPrinterQueriedForAutoConf(ash::CupsPrintersManager* printers_manager,
LocalPrinter::GetCapabilityCallback callback,
chromeos::Printer printer,
bool is_printer_autoconf,
const chromeos::IppPrinterInfo& info) {
if (!is_printer_autoconf) {
std::move(callback).Run(std::nullopt);
return;
}
printer.mutable_ppd_reference()->autoconf = true;
printer.set_ipp_printer_info(info);
printing::SetUpPrinter(printers_manager, printer, std::move(callback));
}
// Query the printer for setup metrics then continue with setup.
void OnPrinterQueriedForAutoConfMetricsOnly(
ash::CupsPrintersManager* printers_manager,
LocalPrinter::GetCapabilityCallback callback,
chromeos::Printer printer,
bool is_printer_autoconf,
const chromeos::IppPrinterInfo& info) {
printer.set_ipp_printer_info(info);
printing::SetUpPrinter(printers_manager, printer, std::move(callback));
}
// This function is called when user's rights to access the printer were
// verified. The user can use the printer <=> `status` == StatusCode::kOK.
// Other values of `status` mean that the access was denied or an error
// occurred. The function is supposed to set-up the printer <=> the access was
// granted. The first parameter is used only for keep the pointer alive until
// this callback is executed.
void OnPrinterAuthenticated(
std::unique_ptr<ash::printing::PrinterAuthenticator> /* authenticator */,
ash::CupsPrintersManager* printers_manager,
const chromeos::Printer& printer,
LocalPrinter::GetCapabilityCallback callback,
ash::printing::oauth2::StatusCode status,
std::string /* access_token */) {
if (status != ash::printing::oauth2::StatusCode::kOK) {
// An error occurred.
std::move(callback).Run(std::nullopt);
return;
}
// In order for an IPP printer to be valid for set up, it needs to either be
// previously installed, be autoconf compatible, or have a valid PPD
// reference. If necessary, the printer is queried to determine its autoconf
// compatibility.
if (!printers_manager->IsPrinterInstalled(printer)) {
if (!printer.HasUri()) {
std::move(callback).Run(std::nullopt);
return;
}
// If the printer is autoconf compatible or has a valid PPD reference then
// continue with normal setup.
if (printer.ppd_reference().IsFilled()) {
printers_manager->QueryPrinterForAutoConf(
printer,
base::BindOnce(OnPrinterQueriedForAutoConfMetricsOnly,
printers_manager, std::move(callback), printer));
return;
}
// CupsPrintersManager should have marked compatible USB printers as having
// a valid PPD reference or autoconf, so this USB printer is incompatible.
if (printer.IsUsbProtocol()) {
std::move(callback).Run(std::nullopt);
return;
}
printers_manager->QueryPrinterForAutoConf(
printer, base::BindOnce(OnPrinterQueriedForAutoConf, printers_manager,
std::move(callback), printer));
return;
}
printers_manager->QueryPrinterForAutoConf(
printer, base::BindOnce(OnPrinterQueriedForAutoConfMetricsOnly,
printers_manager, std::move(callback), printer));
}
} // namespace
LocalPrinter* LocalPrinterImpl::Get() {
CHECK(g_instance);
return g_instance;
}
LocalPrinterImpl::LocalPrinterImpl() {
CHECK(!g_instance);
g_instance = this;
}
LocalPrinterImpl::~LocalPrinterImpl() {
g_instance = nullptr;
}
void LocalPrinterImpl::GetPrinters(const AccountId& accountId,
LocalPrinter::GetPrintersCallback callback) {
std::move(callback).Run(GetLocalPrinters(accountId));
}
void LocalPrinterImpl::GetCapability(
const AccountId& accountId,
const std::string& printer_id,
LocalPrinter::GetCapabilityCallback callback) {
content::BrowserContext* context =
ash::BrowserContextHelper::Get()->GetBrowserContextByAccountId(accountId);
ash::CupsPrintersManager* printers_manager =
ash::CupsPrintersManagerFactory::GetForBrowserContext(context);
DCHECK(printers_manager);
std::optional<chromeos::Printer> printer =
printers_manager->GetPrinter(printer_id);
if (!printer) {
// If the printer was removed, the lookup will fail.
std::move(callback).Run(std::nullopt);
return;
}
if (ash::features::IsOAuthIppEnabled()) {
ash::printing::oauth2::AuthorizationZonesManager* auth_manager =
ash::printing::oauth2::AuthorizationZonesManagerFactory::
GetForBrowserContext(context);
DCHECK(auth_manager);
auto authenticator = std::make_unique<ash::printing::PrinterAuthenticator>(
printers_manager, auth_manager, *printer);
ash::printing::PrinterAuthenticator* authenticator_ptr =
authenticator.get();
authenticator_ptr->ObtainAccessTokenIfNeeded(
base::BindOnce(OnPrinterAuthenticated, std::move(authenticator),
printers_manager, *printer, std::move(callback)));
} else {
OnPrinterAuthenticated(nullptr, printers_manager, *printer,
std::move(callback),
ash::printing::oauth2::StatusCode::kOK, "");
}
}
} // namespace ash