chromium/chrome/browser/ash/printing/specifics_translation.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/specifics_translation.h"

#include <string>
#include <string_view>
#include <vector>

#include "base/check_op.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "chromeos/printing/printer_configuration.h"

namespace ash {

namespace {

chromeos::Printer::PpdReference SpecificsToPpd(
    const sync_pb::PrinterPPDReference& specifics) {
  chromeos::Printer::PpdReference ref;
  if (specifics.autoconf()) {
    ref.autoconf = specifics.autoconf();
  } else if (specifics.has_user_supplied_ppd_url()) {
    ref.user_supplied_ppd_url = specifics.user_supplied_ppd_url();
  } else if (specifics.has_effective_make_and_model()) {
    ref.effective_make_and_model = specifics.effective_make_and_model();
  }

  return ref;
}

// Overwrite fields in |specifics| with an appropriately filled field from
// |ref|.  If |ref| is the default object, nothing will be changed in
// |specifics|.
void MergeReferenceToSpecifics(sync_pb::PrinterPPDReference* specifics,
                               const chromeos::Printer::PpdReference& ref) {
  if (ref.autoconf) {
    specifics->Clear();
    specifics->set_autoconf(ref.autoconf);
  } else if (!ref.user_supplied_ppd_url.empty()) {
    specifics->Clear();
    specifics->set_user_supplied_ppd_url(ref.user_supplied_ppd_url);
  } else if (!ref.effective_make_and_model.empty()) {
    specifics->Clear();
    specifics->set_effective_make_and_model(ref.effective_make_and_model);
  }
}

}  // namespace

std::unique_ptr<chromeos::Printer> SpecificsToPrinter(
    const sync_pb::PrinterSpecifics& specifics) {
  DCHECK(!specifics.id().empty());

  auto printer = std::make_unique<chromeos::Printer>(specifics.id());
  printer->set_display_name(specifics.display_name());
  printer->set_description(specifics.description());
  if (!specifics.make_and_model().empty()) {
    printer->set_make_and_model(specifics.make_and_model());
  } else {
    printer->set_make_and_model(
        MakeAndModel(specifics.manufacturer(), specifics.model()));
  }

  bool result = false;
  std::string message;
  chromeos::Uri uri(specifics.uri());
  const chromeos::Uri::ParserStatus uri_error_code =
      uri.GetLastParsingError().status;
  if (uri_error_code == chromeos::Uri::ParserStatus::kNoErrors) {
    // Versions of Chrome <= R85 saved incorrectly AppSocket printers with a
    // default IPP path. Here, we have to make sure that URIs of these types of
    // printers do not contain a path component. It would cause an error in the
    // printer->SetUri(...) method.
    if (uri.GetScheme() == "socket")
      uri.SetPathEncoded("");
    result = printer->SetUri(uri, &message);
  } else {
    message = "Malformed URI, error code: " +
              base::NumberToString(static_cast<int>(uri_error_code));
  }
  if (!result)
    LOG(WARNING) << message;

  printer->set_uuid(specifics.uuid());
  printer->set_print_server_uri(specifics.print_server_uri());

  *printer->mutable_ppd_reference() = SpecificsToPpd(specifics.ppd_reference());

  return printer;
}

std::unique_ptr<sync_pb::PrinterSpecifics> PrinterToSpecifics(
    const chromeos::Printer& printer) {
  DCHECK(!printer.id().empty());

  auto specifics = std::make_unique<sync_pb::PrinterSpecifics>();
  specifics->set_id(printer.id());
  MergePrinterToSpecifics(printer, specifics.get());
  return specifics;
}

void MergePrinterToSpecifics(const chromeos::Printer& printer,
                             sync_pb::PrinterSpecifics* specifics) {
  // Never update id it needs to be stable.
  DCHECK_EQ(printer.id(), specifics->id());

  if (!printer.display_name().empty())
    specifics->set_display_name(printer.display_name());

  if (!printer.description().empty())
    specifics->set_description(printer.description());

  if (!printer.make_and_model().empty())
    specifics->set_make_and_model(printer.make_and_model());

  if (printer.HasUri())
    specifics->set_uri(printer.uri().GetNormalized());

  if (!printer.uuid().empty())
    specifics->set_uuid(printer.uuid());

  if (!printer.print_server_uri().empty())
    specifics->set_print_server_uri(printer.print_server_uri());

  MergeReferenceToSpecifics(specifics->mutable_ppd_reference(),
                            printer.ppd_reference());
}

std::string MakeAndModel(std::string_view make, std::string_view model) {
  return base::StartsWith(model, make) ? std::string(model)
                                       : base::JoinString({make, model}, " ");
}

}  // namespace ash