chromium/chrome/browser/ash/printing/printer_info_cups.cc

// Copyright 2017 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/printer_info.h"

#include <array>
#include <string>

#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/ranges/algorithm.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/version.h"
#include "chromeos/printing/cups_printer_status.h"
#include "printing/backend/cups_jobs.h"
#include "printing/printer_status.h"

namespace {

const char kPdfMimeType[] = "application/pdf";
const char kPwgRasterMimeType[] = "image/pwg-raster";

// Wraps several printing data structures so that we can use
// PostTaskAndReplyWithResult().
struct QueryResult {
  printing::PrinterQueryResult result;
  printing::PrinterInfo printer_info;
  printing::PrinterStatus printer_status;
};

// Returns true if any of the |ipp_versions| are greater than or equal to 2.0.
bool AllowedIpp(const std::vector<base::Version>& ipp_versions) {
  return base::ranges::any_of(ipp_versions, [](const base::Version& version) {
    return version.IsValid() && version.components()[0] >= 2;
  });
}

// Returns true if |mime_type| is one of the supported types.
bool SupportedMime(const std::string& mime_type) {
  return mime_type == kPwgRasterMimeType || mime_type == kPdfMimeType;
}

// Returns true if |formats| contains one of the supported printer description
// languages for an autoconf printer identified by MIME type.
bool SupportsRequiredPDLS(const std::vector<std::string>& formats) {
  return base::ranges::any_of(formats, &SupportedMime);
}

// Returns true if |info| describes a printer for which we want to attempt
// automatic configuration.
bool IsAutoconf(const ::printing::PrinterInfo& info) {
  return info.ipp_everywhere || (AllowedIpp(info.ipp_versions) &&
                                 SupportsRequiredPDLS(info.document_formats));
}

// Dispatches an IPP request to |host| to retrieve printer information.  Returns
// a nullptr if the request fails.
QueryResult QueryPrinterImpl(const std::string& host,
                             const int port,
                             const std::string& path,
                             bool encrypted) {
  QueryResult result;
  result.result =
      ::printing::GetPrinterInfo(host, port, path, encrypted,
                                 &result.printer_info, &result.printer_status);
  if (result.result != ::printing::PrinterQueryResult::kSuccess) {
    LOG(ERROR) << "Could not retrieve printer info";
  }

  return result;
}

// Handles the request for |info|.  Parses make and model information before
// calling |callback|.
void OnPrinterQueried(ash::PrinterInfoCallback callback,
                      const QueryResult& query_result) {
  const ::printing::PrinterQueryResult& result = query_result.result;
  const ::printing::PrinterInfo& printer_info = query_result.printer_info;
  const ::printing::PrinterStatus& printer_status = query_result.printer_status;
  if (result != ::printing::PrinterQueryResult::kSuccess) {
    VLOG(1) << "Could not reach printer";
    std::move(callback).Run(result, ::printing::PrinterStatus(),
                            /*make_and_model=*/std::string(),
                            /*document_formats=*/{}, /*ipp_everywhere=*/false,
                            chromeos::PrinterAuthenticationInfo{});
    return;
  }

  std::move(callback).Run(result, printer_status, printer_info.make_and_model,
                          printer_info.document_formats,
                          IsAutoconf(printer_info),
                          {.oauth_server = printer_info.oauth_server,
                           .oauth_scope = printer_info.oauth_scope});
}

}  // namespace

namespace ash {

void QueryIppPrinter(const std::string& host,
                     const int port,
                     const std::string& path,
                     bool encrypted,
                     PrinterInfoCallback callback) {
  DCHECK(!host.empty());

  // QueryPrinterImpl could block on a network call for a noticable amount of
  // time (100s of ms). Also the user is waiting on this result.  Thus, run at
  // USER_VISIBLE with MayBlock.
  base::ThreadPool::PostTaskAndReplyWithResult(
      FROM_HERE, {base::TaskPriority::USER_VISIBLE, base::MayBlock()},
      base::BindOnce(&QueryPrinterImpl, host, port, path, encrypted),
      base::BindOnce(&OnPrinterQueried, std::move(callback)));
}

}  // namespace ash