Directly construct IPP requests for print job management
Instead of relying on libcups functions to do it for us. This allows us
to bypass the awkward abstraction of cups_option_t, where all attributes
had to be encoded as strings, and send collection-type attributes like
"client-info" without relying on the undocumented internal format used
by libcups.
Bug: b:267349303
Test: send print jobs with various options to ippeveprinter
Test: print something using an actual printer
Change-Id: I7ec030c28e33dbf9a68cfbb14185c585b3a797b9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4209858
Reviewed-by: Benjamin Gordon <bmgordon@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Reviewed-by: Sparik Hayrapetyan <ust@google.com>
Auto-Submit: Bryan Cain <bryancain@chromium.org>
Commit-Queue: Bryan Cain <bryancain@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1103416}
diff --git a/printing/backend/cups_ipp_constants.cc b/printing/backend/cups_ipp_constants.cc
index 56c15dc..5f03904 100644
--- a/printing/backend/cups_ipp_constants.cc
+++ b/printing/backend/cups_ipp_constants.cc
@@ -10,15 +10,24 @@
namespace printing {
+// operation attributes
+constexpr char kIppDocumentFormat[] = "document-format"; // RFC 8011
+constexpr char kIppDocumentName[] = "document-name"; // RFC 8011
+constexpr char kIppJobId[] = "job-id"; // RFC 8011
+constexpr char kIppJobName[] = "job-name"; // RFC 8011
+constexpr char kIppLastDocument[] = "last-document"; // RFC 8011
+constexpr char kIppPin[] = "job-password"; // PWG 5100.11
+constexpr char kIppPinEncryption[] = "job-password-encryption"; // PWG 5100.11
+constexpr char kIppPrinterUri[] = "printer-uri"; // RFC 8011
+constexpr char kIppRequestingUserName[] = "requesting-user-name"; // RFC 8011
+
+// job attributes
constexpr char kIppCollate[] = "multiple-document-handling"; // PWG 5100.19
constexpr char kIppCopies[] = CUPS_COPIES;
constexpr char kIppColor[] = CUPS_PRINT_COLOR_MODE;
constexpr char kIppMedia[] = CUPS_MEDIA;
constexpr char kIppDuplex[] = CUPS_SIDES;
-constexpr char kIppResolution[] = "printer-resolution"; // RFC 8011
-constexpr char kIppRequestingUserName[] = "requesting-user-name"; // RFC 8011
-constexpr char kIppPin[] = "job-password"; // PWG 5100.11
-constexpr char kIppPinEncryption[] = "job-password-encryption"; // PWG 5100.11
+constexpr char kIppResolution[] = "printer-resolution"; // RFC 8011
// collation values
constexpr char kCollated[] = "separate-documents-collated-copies";
diff --git a/printing/backend/cups_ipp_constants.h b/printing/backend/cups_ipp_constants.h
index ba5575e..f107c02 100644
--- a/printing/backend/cups_ipp_constants.h
+++ b/printing/backend/cups_ipp_constants.h
@@ -10,16 +10,24 @@
namespace printing {
-// property names
+// operation attributes
+COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppDocumentFormat[];
+COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppDocumentName[];
+COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppJobId[];
+COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppJobName[];
+COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppLastDocument[];
+COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppPin[];
+COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppPinEncryption[];
+COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppPrinterUri[];
+COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppRequestingUserName[];
+
+// job attributes
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppCollate[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppCopies[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppColor[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppMedia[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppDuplex[];
-COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppRequestingUserName[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppResolution[];
-COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppPin[];
-COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppPinEncryption[];
// collation values
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kCollated[];
diff --git a/printing/backend/cups_printer.cc b/printing/backend/cups_printer.cc
index 5f7dfddd..1e0707c 100644
--- a/printing/backend/cups_printer.cc
+++ b/printing/backend/cups_printer.cc
@@ -15,9 +15,11 @@
#include "build/build_config.h"
#include "printing/backend/cups_connection.h"
#include "printing/backend/cups_ipp_constants.h"
+#include "printing/backend/cups_ipp_helper.h"
#include "printing/backend/print_backend.h"
#include "printing/backend/print_backend_consts.h"
#include "printing/print_job_constants.h"
+#include "url/gurl.h"
namespace printing {
@@ -27,6 +29,11 @@
: cups_http_(http), destination_(std::move(dest)) {
DCHECK(cups_http_);
DCHECK(destination_);
+
+ printer_uri_ = cupsGetOption(kCUPSOptPrinterUriSupported,
+ destination_.get()->num_options,
+ destination_.get()->options);
+ resource_path_ = std::string(GURL(printer_uri_).path_piece());
}
CupsPrinterImpl(const CupsPrinterImpl&) = delete;
@@ -172,40 +179,56 @@
ipp_status_t CreateJob(int* job_id,
const std::string& title,
const std::string& username,
- const std::vector<cups_option_t>& options) override {
- DCHECK(dest_info_) << "Verify availability before starting a print job";
+ ipp_t* attributes) override {
+ ScopedIppPtr request = CreateRequest(IPP_OP_CREATE_JOB, username);
- cups_option_t* data = const_cast<cups_option_t*>(
- options.data()); // createDestJob will not modify the data
- if (!username.empty())
- cupsSetUser(username.c_str());
+ if (!title.empty()) {
+ ippAddString(request.get(), IPP_TAG_OPERATION, IPP_TAG_NAME, kIppJobName,
+ nullptr, title.c_str());
+ }
- ipp_status_t create_status = cupsCreateDestJob(
- cups_http_, destination_.get(), dest_info_.get(), job_id,
- title.empty() ? nullptr : title.c_str(), options.size(), data);
- cupsSetUser(nullptr); // reset to default username ("anonymous")
- return create_status;
+ CopyAttributeGroup(request.get(), attributes, IPP_TAG_OPERATION);
+ CopyAttributeGroup(request.get(), attributes, IPP_TAG_JOB);
+ // We would also copy subscription attributes here if we actually used
+ // any. We don't, though.
+
+ // cupsDoRequest() takes ownership of the request and frees it for us.
+ ScopedIppPtr response = WrapIpp(
+ cupsDoRequest(cups_http_, request.release(), resource_path_.c_str()));
+
+ ipp_attribute_t* attr =
+ ippFindAttribute(response.get(), kIppJobId, IPP_TAG_INTEGER);
+ *job_id = ippGetInteger(attr, 0);
+
+ return ippGetStatusCode(response.get());
}
bool StartDocument(int job_id,
const std::string& docname,
bool last_document,
const std::string& username,
- const std::vector<cups_option_t>& options) override {
- DCHECK(dest_info_);
+ ipp_t* attributes) override {
DCHECK(job_id);
- if (!username.empty())
- cupsSetUser(username.c_str());
+ ScopedIppPtr request = CreateRequest(IPP_OP_SEND_DOCUMENT, username);
- cups_option_t* data = const_cast<cups_option_t*>(
- options.data()); // createStartDestDocument will not modify the data
- http_status_t start_doc_status = cupsStartDestDocument(
- cups_http_, destination_.get(), dest_info_.get(), job_id,
- docname.empty() ? nullptr : docname.c_str(), CUPS_FORMAT_PDF,
- options.size(), data, last_document ? 1 : 0);
+ ippAddInteger(request.get(), IPP_TAG_OPERATION, IPP_TAG_INTEGER, kIppJobId,
+ job_id);
+ ippAddBoolean(request.get(), IPP_TAG_OPERATION, kIppLastDocument,
+ static_cast<char>(last_document));
+ ippAddString(request.get(), IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
+ kIppDocumentFormat, nullptr, CUPS_FORMAT_PDF);
+ if (!docname.empty()) {
+ ippAddString(request.get(), IPP_TAG_OPERATION, IPP_TAG_NAME,
+ kIppDocumentName, nullptr, docname.c_str());
+ }
- cupsSetUser(nullptr); // reset to default username ("anonymous")
- return start_doc_status == HTTP_CONTINUE;
+ CopyAttributeGroup(request.get(), attributes, IPP_TAG_OPERATION);
+ CopyAttributeGroup(request.get(), attributes, IPP_TAG_DOCUMENT);
+
+ http_status_t status =
+ cupsSendRequest(cups_http_, request.get(), resource_path_.c_str(),
+ CUPS_LENGTH_VARIABLE);
+ return status == HTTP_CONTINUE;
}
bool StreamData(const std::vector<char>& buffer) override {
@@ -215,24 +238,22 @@
}
bool FinishDocument() override {
- DCHECK(dest_info_);
-
- ipp_status_t status = cupsFinishDestDocument(cups_http_, destination_.get(),
- dest_info_.get());
-
+ ScopedIppPtr response =
+ WrapIpp(cupsGetResponse(cups_http_, resource_path_.c_str()));
+ ipp_status_t status = ippGetStatusCode(response.get());
return status == IPP_STATUS_OK;
}
ipp_status_t CloseJob(int job_id, const std::string& username) override {
- DCHECK(dest_info_);
DCHECK(job_id);
- if (!username.empty())
- cupsSetUser(username.c_str());
+ ScopedIppPtr request = CreateRequest(IPP_OP_CLOSE_JOB, username);
- ipp_status_t result = cupsCloseDestJob(cups_http_, destination_.get(),
- dest_info_.get(), job_id);
- cupsSetUser(nullptr); // reset to default username ("anonymous")
- return result;
+ ippAddInteger(request.get(), IPP_TAG_OPERATION, IPP_TAG_INTEGER, kIppJobId,
+ job_id);
+
+ ScopedIppPtr response = WrapIpp(
+ cupsDoRequest(cups_http_, request.release(), resource_path_.c_str()));
+ return ippGetStatusCode(response.get());
}
bool CancelJob(int job_id) override {
@@ -258,6 +279,29 @@
}
private:
+ // internal helper function to initialize an IPP request
+ ScopedIppPtr CreateRequest(ipp_op_t op, const std::string& username) {
+ const char* c_username = username.empty() ? cupsUser() : username.c_str();
+
+ ipp_t* request = ippNewRequest(op);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, kIppPrinterUri,
+ nullptr, printer_uri_.c_str());
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ kIppRequestingUserName, nullptr, c_username);
+
+ return WrapIpp(request);
+ }
+
+ // internal helper function to copy attributes to an IPP request
+ void CopyAttributeGroup(ipp_t* request, ipp_t* attributes, ipp_tag_t group) {
+ for (ipp_attribute_t* attr = ippFirstAttribute(attributes); attr;
+ attr = ippNextAttribute(attributes)) {
+ if (ippGetGroupTag(attr) == group) {
+ ippCopyAttribute(request, attr, 0);
+ }
+ }
+ }
+
// http connection owned by the CupsConnection which created this object
const raw_ptr<http_t> cups_http_;
@@ -266,6 +310,12 @@
// opaque object containing printer attributes and options
mutable ScopedDestInfo dest_info_;
+
+ // uri used to connect to this printer
+ std::string printer_uri_;
+
+ // resource path used to connect to this printer
+ std::string resource_path_;
};
std::unique_ptr<CupsPrinter> CupsPrinter::Create(http_t* http,
diff --git a/printing/backend/cups_printer.h b/printing/backend/cups_printer.h
index 0f2f30ef..b273b529 100644
--- a/printing/backend/cups_printer.h
+++ b/printing/backend/cups_printer.h
@@ -94,18 +94,19 @@
virtual ipp_status_t CreateJob(int* job_id,
const std::string& title,
const std::string& username,
- const std::vector<cups_option_t>& options) = 0;
+ ipp_t* attributes) = 0;
// Add a document to a print job. `job_id` must be non-zero and refer to a
// job started with CreateJob. `docname` will be displayed in print status
// if not empty. `last_doc` should be true if this is the last document for
- // this print job. `username` is not sent if empty. `options` should be IPP
- // key value pairs for the Send-Document operation.
+ // this print job. `username` is not sent if empty. `attributes` should
+ // contain IPP operation and document attributes for the Send-Document
+ // operation.
virtual bool StartDocument(int job_id,
const std::string& docname,
bool last_doc,
const std::string& username,
- const std::vector<cups_option_t>& options) = 0;
+ ipp_t* attributes) = 0;
// Add data to the current document started by StartDocument. Calling this
// without a started document will fail.
diff --git a/printing/backend/mock_cups_printer.h b/printing/backend/mock_cups_printer.h
index f5f478d1..504d97a 100644
--- a/printing/backend/mock_cups_printer.h
+++ b/printing/backend/mock_cups_printer.h
@@ -27,13 +27,13 @@
ipp_status_t(int* job_id,
const std::string& title,
const std::string& username,
- const std::vector<cups_option_t>& options));
+ ipp_t* attributes));
MOCK_METHOD5(StartDocument,
bool(int job_id,
const std::string& docname,
bool last_doc,
const std::string& username,
- const std::vector<cups_option_t>& options));
+ ipp_t* attributes));
MOCK_METHOD1(StreamData, bool(const std::vector<char>& buffer));
MOCK_METHOD0(FinishDocument, bool());
MOCK_METHOD2(CloseJob, ipp_status_t(int job_id, const std::string& username));
diff --git a/printing/backend/print_backend_consts.cc b/printing/backend/print_backend_consts.cc
index 7c1d139..2127b77 100644
--- a/printing/backend/print_backend_consts.cc
+++ b/printing/backend/print_backend_consts.cc
@@ -25,3 +25,4 @@
const char kCUPSOptPrinterMakeAndModel[] = "printer-make-and-model";
const char kCUPSOptPrinterState[] = "printer-state";
const char kCUPSOptPrinterType[] = "printer-type";
+const char kCUPSOptPrinterUriSupported[] = "printer-uri-supported";
diff --git a/printing/backend/print_backend_consts.h b/printing/backend/print_backend_consts.h
index 3cfd3f47..bf4957b 100644
--- a/printing/backend/print_backend_consts.h
+++ b/printing/backend/print_backend_consts.h
@@ -24,5 +24,6 @@
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kCUPSOptPrinterMakeAndModel[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kCUPSOptPrinterState[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kCUPSOptPrinterType[];
+COMPONENT_EXPORT(PRINT_BACKEND) extern const char kCUPSOptPrinterUriSupported[];
#endif // PRINTING_BACKEND_PRINT_BACKEND_CONSTS_H_
diff --git a/printing/client_info_helpers.cc b/printing/client_info_helpers.cc
index 5c5ac8c..f68951b 100644
--- a/printing/client_info_helpers.cc
+++ b/printing/client_info_helpers.cc
@@ -5,8 +5,6 @@
#include "printing/client_info_helpers.h"
#include "base/no_destructor.h"
-#include "base/strings/strcat.h"
-#include "base/strings/stringprintf.h"
#include "base/types/optional_util.h"
#include "printing/mojom/print.mojom.h"
#include "third_party/re2/src/re2/re2.h"
@@ -26,11 +24,8 @@
(value->size() <= max_length && RE2::FullMatch(*value, *kStringRegex));
}
-// Returns true if all members of `client_info` are valid.
-// String members are considered valid if they match the regex [a-zA-Z0-9_.-]*
-// and do not exceed the maximum length specified for the respective IPP member
-// attribute. The `client_type` member is valid if it is equal to one of the
-// enum values defined for the `client-type` IPP attribute.
+} // namespace
+
bool ValidateClientInfoItem(const mojom::IppClientInfo& client_info) {
return ValidateClientType(client_info.client_type) &&
ValidateStringMember(&client_info.client_name,
@@ -43,39 +38,4 @@
kClientInfoMaxVersionLength);
}
-} // namespace
-
-absl::optional<std::string> ClientInfoCollectionToCupsOptionValue(
- const mojom::IppClientInfo& client_info) {
- if (!ValidateClientInfoItem(client_info)) {
- return absl::nullopt;
- }
- std::string name = base::StrCat({"client-name=", client_info.client_name});
- std::string type = base::StringPrintf(
- "client-type=%d", static_cast<int>(client_info.client_type));
- std::string string_version = base::StrCat(
- {"client-string-version=", client_info.client_string_version});
-
- // Missing values for 'client-version' and 'client-patches' correspond to
- // 'no-value' out-of-band IPP values. We omit them because there is no
- // string encoding as a cups_option_t for them that CUPS understands.
- std::string version;
- if (client_info.client_version.has_value()) {
- version =
- base::StrCat({"client-version=", client_info.client_version.value()});
- }
- std::string patches;
- if (client_info.client_patches.has_value()) {
- patches =
- base::StrCat({"client-patches=", client_info.client_patches.value()});
- }
-
- // The resulting string may have extra spaces between attributes because
- // of missing member attributes which is okay because they are ignored
- // by cupsParseOptions.
- return base::StringPrintf("{%s %s %s %s %s}", name.c_str(), type.c_str(),
- version.c_str(), string_version.c_str(),
- patches.c_str());
-}
-
} // namespace printing
diff --git a/printing/client_info_helpers.h b/printing/client_info_helpers.h
index 3c306ade..df96eea 100644
--- a/printing/client_info_helpers.h
+++ b/printing/client_info_helpers.h
@@ -5,11 +5,9 @@
#ifndef PRINTING_CLIENT_INFO_HELPERS_H_
#define PRINTING_CLIENT_INFO_HELPERS_H_
-#include <string>
-
+#include <cstddef>
#include "base/component_export.h"
#include "printing/mojom/print.mojom-forward.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
namespace printing {
@@ -19,15 +17,13 @@
inline constexpr size_t kClientInfoMaxStringVersionLength = 127;
inline constexpr size_t kClientInfoMaxVersionLength = 64;
-// Returns the string representation of `client_info` in a format suitable for
-// use as a `cups_option_t` value, or absl::nullopt if `client_info` is invalid.
-// `client_info` represents one value of the 'client-info' multi-valued IPP
-// attribute. `client_info` is considered valid if all string members match the
-// regex [a-zA-Z0-9_.-]* and do not exceed the maximum length specified for the
-// respective IPP member attribute.
+// Returns true if all members of `client_info` are valid.
+// String members are considered valid if they match the regex [a-zA-Z0-9_.-]*
+// and do not exceed the maximum length specified for the respective IPP member
+// attribute. The `client_type` member is valid if it is equal to one of the
+// enum values defined for the `client-type` IPP attribute.
COMPONENT_EXPORT(PRINTING)
-absl::optional<std::string> ClientInfoCollectionToCupsOptionValue(
- const mojom::IppClientInfo& client_info);
+bool ValidateClientInfoItem(const mojom::IppClientInfo& client_info);
} // namespace printing
diff --git a/printing/client_info_helpers_unittest.cc b/printing/client_info_helpers_unittest.cc
index fd834dc..2f28fa3 100644
--- a/printing/client_info_helpers_unittest.cc
+++ b/printing/client_info_helpers_unittest.cc
@@ -17,60 +17,24 @@
namespace {
-TEST(ClientInfoHelpersTest,
- ClientInfoCollectionToCupsOptionValueValidWithAllFields) {
+TEST(ClientInfoHelpersTest, ValidateClientInfoItemValidWithAllFields) {
mojom::IppClientInfo client_info(
mojom::IppClientInfo::ClientType::kOperatingSystem, "a-", "B_", "1.",
"a.1-B_");
- absl::optional<std::string> option_val =
- ClientInfoCollectionToCupsOptionValue(client_info);
- ASSERT_TRUE(option_val.has_value());
- ASSERT_GE(option_val.value().size(), 2u);
- ASSERT_EQ(option_val.value().front(), '{');
- ASSERT_EQ(option_val.value().back(), '}');
-
- base::StringPiece option_without_braces(option_val.value());
- option_without_braces.remove_prefix(1);
- option_without_braces.remove_suffix(1);
-
- std::vector<std::string> member_options =
- base::SplitString(option_without_braces, " ", base::TRIM_WHITESPACE,
- base::SPLIT_WANT_NONEMPTY);
-
- EXPECT_THAT(member_options,
- testing::UnorderedElementsAre(
- "client-name=a-", "client-type=4", "client-patches=B_",
- "client-string-version=1.", "client-version=a.1-B_"));
+ EXPECT_TRUE(ValidateClientInfoItem(client_info));
}
-TEST(ClientInfoHelpersTest,
- ClientInfoCollectionToCupsOptionValueValidWithMissingFields) {
+TEST(ClientInfoHelpersTest, ValidateClientInfoItemValidWithMissingFields) {
mojom::IppClientInfo::ClientType type =
mojom::IppClientInfo::ClientType::kApplication;
mojom::IppClientInfo client_info(type, "a-", absl::nullopt, "1.",
absl::nullopt);
- absl::optional<std::string> option_val =
- ClientInfoCollectionToCupsOptionValue(client_info);
- ASSERT_TRUE(option_val.has_value());
- ASSERT_GE(option_val.value().size(), 2u);
- ASSERT_EQ(option_val.value().front(), '{');
- ASSERT_EQ(option_val.value().back(), '}');
- base::StringPiece option_without_braces(option_val.value());
- option_without_braces.remove_prefix(1);
- option_without_braces.remove_suffix(1);
-
- std::vector<std::string> member_options =
- base::SplitString(option_without_braces, " ", base::TRIM_WHITESPACE,
- base::SPLIT_WANT_NONEMPTY);
-
- EXPECT_THAT(member_options,
- testing::UnorderedElementsAre("client-name=a-", "client-type=3",
- "client-string-version=1."));
+ EXPECT_TRUE(ValidateClientInfoItem(client_info));
}
-TEST(ClientInfoHelpersTest, ClientInfoCollectionToCupsOptionValueInvalidChars) {
+TEST(ClientInfoHelpersTest, ValidateClientInfoItemInvalidChars) {
mojom::IppClientInfo valid_client_info(
mojom::IppClientInfo::ClientType::kOther, "name", "patch", "version",
absl::nullopt);
@@ -79,22 +43,22 @@
client_info = valid_client_info;
client_info.client_name = " ";
- EXPECT_FALSE(ClientInfoCollectionToCupsOptionValue(client_info).has_value());
+ EXPECT_FALSE(ValidateClientInfoItem(client_info));
client_info = valid_client_info;
client_info.client_patches = ";";
- EXPECT_FALSE(ClientInfoCollectionToCupsOptionValue(client_info).has_value());
+ EXPECT_FALSE(ValidateClientInfoItem(client_info));
client_info = valid_client_info;
client_info.client_version = "\\";
- EXPECT_FALSE(ClientInfoCollectionToCupsOptionValue(client_info).has_value());
+ EXPECT_FALSE(ValidateClientInfoItem(client_info));
client_info = valid_client_info;
client_info.client_string_version = "{";
- EXPECT_FALSE(ClientInfoCollectionToCupsOptionValue(client_info).has_value());
+ EXPECT_FALSE(ValidateClientInfoItem(client_info));
}
-TEST(ClientInfoHelpersTest, ClientInfoCollectionToCupsOptionValueInvalidRange) {
+TEST(ClientInfoHelpersTest, ValidateClientInfoItemInvalidRange) {
mojom::IppClientInfo valid_client_info(
mojom::IppClientInfo::ClientType::kOther, "name", "patch", "version",
absl::nullopt);
@@ -103,32 +67,32 @@
client_info = valid_client_info;
client_info.client_name = std::string(kClientInfoMaxNameLength + 1, 'A');
- EXPECT_FALSE(ClientInfoCollectionToCupsOptionValue(client_info).has_value());
+ EXPECT_FALSE(ValidateClientInfoItem(client_info));
client_info = valid_client_info;
client_info.client_patches =
std::string(kClientInfoMaxPatchesLength + 1, 'A');
- EXPECT_FALSE(ClientInfoCollectionToCupsOptionValue(client_info).has_value());
+ EXPECT_FALSE(ValidateClientInfoItem(client_info));
client_info = valid_client_info;
client_info.client_version =
std::string(kClientInfoMaxVersionLength + 1, 'A');
- EXPECT_FALSE(ClientInfoCollectionToCupsOptionValue(client_info).has_value());
+ EXPECT_FALSE(ValidateClientInfoItem(client_info));
client_info = valid_client_info;
client_info.client_string_version =
std::string(kClientInfoMaxStringVersionLength + 1, 'A');
- EXPECT_FALSE(ClientInfoCollectionToCupsOptionValue(client_info).has_value());
+ EXPECT_FALSE(ValidateClientInfoItem(client_info));
client_info = valid_client_info;
client_info.client_type = static_cast<mojom::IppClientInfo::ClientType>(
static_cast<int>(mojom::IppClientInfo::ClientType::kMinValue) - 1);
- EXPECT_FALSE(ClientInfoCollectionToCupsOptionValue(client_info).has_value());
+ EXPECT_FALSE(ValidateClientInfoItem(client_info));
client_info = valid_client_info;
client_info.client_type = static_cast<mojom::IppClientInfo::ClientType>(
static_cast<int>(mojom::IppClientInfo::ClientType::kMaxValue) + 1);
- EXPECT_FALSE(ClientInfoCollectionToCupsOptionValue(client_info).has_value());
+ EXPECT_FALSE(ValidateClientInfoItem(client_info));
}
} // namespace
diff --git a/printing/print_settings_conversion_chromeos_unittest.cc b/printing/print_settings_conversion_chromeos_unittest.cc
index 43abdb6..92c4a2e6 100644
--- a/printing/print_settings_conversion_chromeos_unittest.cc
+++ b/printing/print_settings_conversion_chromeos_unittest.cc
@@ -4,25 +4,15 @@
#include "printing/print_settings_conversion_chromeos.h"
-#include <ostream>
#include <string>
#include "base/test/values_test_util.h"
#include "base/values.h"
-#include "printing/client_info_helpers.h"
#include "printing/mojom/print.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace printing {
-namespace mojom {
-std::ostream& operator<<(std::ostream& os, const IppClientInfo& value) {
- absl::optional<std::string> str =
- ClientInfoCollectionToCupsOptionValue(value);
- return os << (str.has_value() ? str.value() : std::string(""));
-}
-} // namespace mojom
-
namespace {
const base::Value::List kClientInfoJobSetting = base::test::ParseJsonList(R"([
diff --git a/printing/printing_context_chromeos.cc b/printing/printing_context_chromeos.cc
index 796358eb..68b0f869 100644
--- a/printing/printing_context_chromeos.cc
+++ b/printing/printing_context_chromeos.cc
@@ -46,37 +46,54 @@
base::StartsWith(uri, "usb:") || base::StartsWith(uri, "ippusb:");
}
-// Returns the string representation of `client_infos` in a format suitable to
-// be used as a `cups_option_t` value.
-// `client_infos` represents the 'client-info' IPP attribute. Each item in
-// `client_infos` represents one collection in 'client-info'.
+// Populates the 'client-info' attribute of the IPP collection `options`. Each
+// item in `client_infos` represents one collection in 'client-info'.
// Invalid 'client-info' items will be dropped.
-std::string ClientInfoToCupsOptionValue(
- const std::vector<mojom::IppClientInfo>& client_infos) {
- // String representation for each client-info item.
- std::vector<std::string> option_values;
+void EncodeClientInfo(const std::vector<mojom::IppClientInfo>& client_infos,
+ ipp_t* options) {
+ std::vector<ScopedIppPtr> option_values;
+ std::vector<const ipp_t*> raw_option_values;
option_values.reserve(client_infos.size());
+ raw_option_values.reserve(client_infos.size());
+
for (const mojom::IppClientInfo& client_info : client_infos) {
- if (auto option_value =
- ClientInfoCollectionToCupsOptionValue(client_info)) {
- option_values.emplace_back(option_value.value());
- } else {
+ if (!ValidateClientInfoItem(client_info)) {
LOG(WARNING) << "Invalid client-info item skipped";
+ continue;
+ }
+
+ // Create a temporary collection object owned by this function.
+ ipp_t* collection = ippNew();
+ option_values.emplace_back(WrapIpp(collection));
+ raw_option_values.emplace_back(collection);
+
+ ippAddString(collection, IPP_TAG_ZERO, IPP_TAG_NAME, kIppClientName,
+ nullptr, client_info.client_name.c_str());
+ ippAddInteger(collection, IPP_TAG_ZERO, IPP_TAG_ENUM, kIppClientType,
+ static_cast<int>(client_info.client_type));
+ ippAddString(collection, IPP_TAG_ZERO, IPP_TAG_TEXT,
+ kIppClientStringVersion, nullptr,
+ client_info.client_string_version.c_str());
+
+ if (client_info.client_version.has_value()) {
+ ippAddOctetString(collection, IPP_TAG_ZERO, kIppClientVersion,
+ client_info.client_version.value().data(),
+ client_info.client_version.value().size());
+ }
+
+ if (client_info.client_patches.has_value()) {
+ ippAddString(collection, IPP_TAG_ZERO, IPP_TAG_TEXT, kIppClientPatches,
+ nullptr, client_info.client_patches.value().c_str());
}
}
- return base::JoinString(option_values, ",");
-}
-ScopedCupsOption ConstructOption(std::string name, std::string value) {
- // ScopedCupsOption frees the name and value buffers on deletion
- cups_option_t* cups_option = nullptr;
- int num_options = 0;
- // Use cupsAddOption so that the pair of malloc and free are used.
- num_options =
- cupsAddOption(name.c_str(), value.c_str(), num_options, &cups_option);
- DCHECK(cups_option);
- DCHECK_EQ(num_options, 1);
- return ScopedCupsOption(cups_option);
+ if (raw_option_values.empty()) {
+ return;
+ }
+
+ // Now add the client-info list to the options.
+ ippAddCollections(options, IPP_TAG_OPERATION, kIppClientInfo,
+ raw_option_values.size(), raw_option_values.data());
}
std::string GetCollateString(bool collate) {
@@ -137,8 +154,10 @@
} // namespace
-std::vector<ScopedCupsOption> SettingsToCupsOptions(
- const PrintSettings& settings) {
+ScopedIppPtr SettingsToIPPOptions(const PrintSettings& settings) {
+ ScopedIppPtr scoped_options = WrapIpp(ippNew());
+ ipp_t* options = scoped_options.get();
+
const char* sides = nullptr;
switch (settings.duplex_mode()) {
case mojom::DuplexMode::kSimplex:
@@ -154,33 +173,36 @@
NOTREACHED();
}
- std::vector<ScopedCupsOption> options;
- options.push_back(
- ConstructOption(kIppColor,
- GetIppColorModelForModel(settings.color()))); // color
- options.push_back(ConstructOption(kIppDuplex, sides)); // duplexing
- options.push_back(
- ConstructOption(kIppMedia,
- settings.requested_media().vendor_id)); // paper size
- options.push_back(
- ConstructOption(kIppCopies,
- base::NumberToString(settings.copies()))); // copies
- options.push_back(
- ConstructOption(kIppCollate,
- GetCollateString(settings.collate()))); // collate
+ // duplexing
+ ippAddString(options, IPP_TAG_JOB, IPP_TAG_KEYWORD, kIppDuplex, nullptr,
+ sides);
+ // color
+ ippAddString(options, IPP_TAG_JOB, IPP_TAG_KEYWORD, kIppColor, nullptr,
+ GetIppColorModelForModel(settings.color()).c_str());
+ // paper size
+ ippAddString(options, IPP_TAG_JOB, IPP_TAG_KEYWORD, kIppMedia, nullptr,
+ settings.requested_media().vendor_id.c_str());
+ // copies
+ ippAddInteger(options, IPP_TAG_JOB, IPP_TAG_INTEGER, kIppCopies,
+ settings.copies());
+ // collate
+ ippAddString(options, IPP_TAG_JOB, IPP_TAG_KEYWORD, kIppCollate, nullptr,
+ GetCollateString(settings.collate()).c_str());
+
if (!settings.pin_value().empty()) {
- options.push_back(ConstructOption(kIppPin, settings.pin_value()));
- options.push_back(ConstructOption(kIppPinEncryption, kPinEncryptionNone));
+ ippAddOctetString(options, IPP_TAG_OPERATION, kIppPin,
+ settings.pin_value().data(), settings.pin_value().size());
+ ippAddString(options, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, kIppPinEncryption,
+ nullptr, kPinEncryptionNone);
}
+ // resolution
if (settings.dpi_horizontal() > 0 && settings.dpi_vertical() > 0) {
- std::string dpi = base::NumberToString(settings.dpi_horizontal());
- if (settings.dpi_horizontal() != settings.dpi_vertical())
- dpi += "x" + base::NumberToString(settings.dpi_vertical());
- options.push_back(ConstructOption(kIppResolution, dpi + "dpi"));
+ ippAddResolution(options, IPP_TAG_JOB, kIppResolution, IPP_RES_PER_INCH,
+ settings.dpi_horizontal(), settings.dpi_vertical());
}
- std::map<std::string, std::vector<std::string>> multival;
+ std::map<std::string, std::vector<int>> multival;
for (const auto& setting : settings.advanced_settings()) {
const std::string& key = setting.first;
const std::string& value = setting.second.GetString();
@@ -191,36 +213,39 @@
size_t pos = key.find('/');
if (pos == std::string::npos) {
// Regular value.
- options.push_back(ConstructOption(key, value));
+ ippAddString(options, IPP_TAG_JOB, IPP_TAG_KEYWORD, key.c_str(), nullptr,
+ value.c_str());
continue;
}
// Store selected enum values.
- if (value == kOptionTrue)
- multival[key.substr(0, pos)].push_back(key.substr(pos + 1));
+ if (value == kOptionTrue) {
+ std::string option_name = key.substr(0, pos);
+ std::string enum_string = key.substr(pos + 1);
+ int enum_value = ippEnumValue(option_name.c_str(), enum_string.c_str());
+ DCHECK_NE(enum_value, -1);
+ multival[option_name].push_back(enum_value);
+ }
}
- // Pass multivalue enums as comma-separated lists.
+ // Add multivalue enum options.
for (const auto& it : multival) {
- options.push_back(
- ConstructOption(it.first, base::JoinString(it.second, ",")));
+ ippAddIntegers(options, IPP_TAG_JOB, IPP_TAG_ENUM, it.first.c_str(),
+ it.second.size(), it.second.data());
}
// OAuth access token
if (!settings.oauth_token().empty()) {
- options.push_back(ConstructOption(kSettingChromeOSAccessOAuthToken,
- settings.oauth_token()));
+ ippAddString(options, IPP_TAG_JOB, IPP_TAG_NAME,
+ kSettingChromeOSAccessOAuthToken, nullptr,
+ settings.oauth_token().c_str());
}
// IPP client-info attribute.
if (!settings.client_infos().empty()) {
- std::string option_value =
- ClientInfoToCupsOptionValue(settings.client_infos());
- if (!option_value.empty()) {
- options.push_back(ConstructOption(kIppClientInfo, option_value));
- }
+ EncodeClientInfo(settings.client_infos(), options);
}
- return options;
+ return scoped_options;
}
// static
@@ -247,12 +272,15 @@
PrintingContextChromeos::PrintingContextChromeos(Delegate* delegate)
: PrintingContext(delegate),
- connection_(CupsConnection::Create(GURL(), HTTP_ENCRYPT_NEVER, true)) {}
+ connection_(CupsConnection::Create(GURL(), HTTP_ENCRYPT_NEVER, true)),
+ ipp_options_(WrapIpp(nullptr)) {}
PrintingContextChromeos::PrintingContextChromeos(
Delegate* delegate,
std::unique_ptr<CupsConnection> connection)
- : PrintingContext(delegate), connection_(std::move(connection)) {}
+ : PrintingContext(delegate),
+ connection_(std::move(connection)),
+ ipp_options_(WrapIpp(nullptr)) {}
PrintingContextChromeos::~PrintingContextChromeos() {
ReleaseContext();
@@ -362,7 +390,7 @@
CupsPrinter::CupsMediaMargins margins =
printer_->GetMediaMarginsByName(media.vendor_id);
SetPrintableArea(settings_.get(), media, margins);
- cups_options_ = SettingsToCupsOptions(*settings_);
+ ipp_options_ = SettingsToIPPOptions(*settings_);
send_user_info_ = settings_->send_user_info();
if (send_user_info_) {
DCHECK(printer_);
@@ -404,18 +432,8 @@
: kDocumentNamePlaceholder;
}
- std::vector<cups_option_t> options;
- for (const ScopedCupsOption& option : cups_options_) {
- if (printer_->CheckOptionSupported(option->name, option->value)) {
- options.push_back(*(option.get()));
- } else {
- DVLOG(1) << "Unsupported option skipped " << option->name << ", "
- << option->value;
- }
- }
-
- ipp_status_t create_status =
- printer_->CreateJob(&job_id_, converted_name, username_, options);
+ ipp_status_t create_status = printer_->CreateJob(
+ &job_id_, converted_name, username_, ipp_options_.get());
if (job_id_ == 0) {
DLOG(WARNING) << "Creating cups job failed"
@@ -425,7 +443,7 @@
// we only send one document, so it's always the last one
if (!printer_->StartDocument(job_id_, converted_name, true, username_,
- options)) {
+ ipp_options_.get())) {
LOG(ERROR) << "Starting document failed";
return OnError();
}
diff --git a/printing/printing_context_chromeos.h b/printing/printing_context_chromeos.h
index d31180c..4950f9f 100644
--- a/printing/printing_context_chromeos.h
+++ b/printing/printing_context_chromeos.h
@@ -10,7 +10,7 @@
#include <vector>
#include "printing/backend/cups_connection.h"
-#include "printing/backend/cups_deleters.h"
+#include "printing/backend/cups_ipp_helper.h"
#include "printing/backend/cups_printer.h"
#include "printing/mojom/print.mojom.h"
#include "printing/printing_context.h"
@@ -59,7 +59,7 @@
const std::unique_ptr<CupsConnection> connection_;
std::unique_ptr<CupsPrinter> printer_;
- std::vector<ScopedCupsOption> cups_options_;
+ ScopedIppPtr ipp_options_;
bool send_user_info_ = false;
std::string username_;
};
@@ -67,8 +67,7 @@
// This has the side effect of recording UMA for advanced attributes usage,
// so only call once per job.
COMPONENT_EXPORT(PRINTING)
-std::vector<ScopedCupsOption> SettingsToCupsOptions(
- const PrintSettings& settings);
+ScopedIppPtr SettingsToIPPOptions(const PrintSettings& settings);
} // namespace printing
diff --git a/printing/printing_context_chromeos_unittest.cc b/printing/printing_context_chromeos_unittest.cc
index a6e1346..7654413 100644
--- a/printing/printing_context_chromeos_unittest.cc
+++ b/printing/printing_context_chromeos_unittest.cc
@@ -78,34 +78,69 @@
printing_context_->UpdatePrintSettingsFromPOD(std::move(settings));
}
- void TestCupsOptionValue(const char* option_name,
- const char* expected_option_value) const {
- DCHECK(option_name);
- auto cups_options = SettingsToCupsOptions(settings_);
- const char* ret = nullptr;
- for (const auto& option : cups_options) {
- EXPECT_TRUE(option->name);
- EXPECT_TRUE(option->value);
- if (option->name && !strcmp(option_name, option->name)) {
+ ipp_attribute_t* GetAttribute(ipp_t* attributes,
+ const char* attr_name) const {
+ DCHECK(attr_name);
+ ipp_attribute_t* ret = nullptr;
+ for (ipp_attribute_t* attr = ippFirstAttribute(attributes); attr;
+ attr = ippNextAttribute(attributes)) {
+ const char* name = ippGetName(attr);
+ if (name && !strcmp(attr_name, name)) {
EXPECT_EQ(nullptr, ret)
- << "Multiple options with name " << option_name << " found.";
- ret = option->value;
+ << "Multiple attributes with name " << attr_name << " found.";
+ ret = attr;
}
}
- EXPECT_STREQ(expected_option_value, ret);
+ EXPECT_TRUE(ret);
+ return ret;
}
- absl::optional<std::string> GetCupsOptionValue(
- const char* option_name) const {
- DCHECK(option_name);
- auto cups_options = SettingsToCupsOptions(settings_);
- absl::optional<std::string> ret;
- for (const auto& option : cups_options) {
- if (option->name && !strcmp(option_name, option->name)) {
- ret = std::string(option->value);
- }
- }
- return ret;
+ void TestStringOptionValue(const char* attr_name,
+ const char* expected_value) const {
+ auto attributes = SettingsToIPPOptions(settings_);
+ auto* attr = GetAttribute(attributes.get(), attr_name);
+ EXPECT_STREQ(expected_value, ippGetString(attr, 0, nullptr));
+ }
+
+ void TestIntegerOptionValue(const char* attr_name, int expected_value) const {
+ auto attributes = SettingsToIPPOptions(settings_);
+ auto* attr = GetAttribute(attributes.get(), attr_name);
+ EXPECT_EQ(expected_value, ippGetInteger(attr, 0));
+ }
+
+ void TestOctetStringOptionValue(const char* attr_name,
+ base::span<const char> expected_value) const {
+ auto attributes = SettingsToIPPOptions(settings_);
+ auto* attr = GetAttribute(attributes.get(), attr_name);
+ int length;
+ void* value = ippGetOctetString(attr, 0, &length);
+ ASSERT_EQ(expected_value.size(), static_cast<size_t>(length));
+ ASSERT_TRUE(value);
+ EXPECT_EQ(0, memcmp(expected_value.data(), value, expected_value.size()));
+ }
+
+ void TestResolutionOptionValue(const char* attr_name,
+ int expected_x_res,
+ int expected_y_res) const {
+ auto attributes = SettingsToIPPOptions(settings_);
+ auto* attr = GetAttribute(attributes.get(), attr_name);
+ ipp_res_t unit;
+ int y_res;
+ int x_res = ippGetResolution(attr, 0, &y_res, &unit);
+ EXPECT_EQ(unit, IPP_RES_PER_INCH);
+ EXPECT_EQ(expected_x_res, x_res);
+ EXPECT_EQ(expected_y_res, y_res);
+ }
+
+ bool HasAttribute(const char* attr_name) const {
+ auto attributes = SettingsToIPPOptions(settings_);
+ return !!ippFindAttribute(attributes.get(), attr_name, IPP_TAG_ZERO);
+ }
+
+ int GetAttrValueCount(const char* attr_name) const {
+ auto attributes = SettingsToIPPOptions(settings_);
+ auto* attr = GetAttribute(attributes.get(), attr_name);
+ return ippGetCount(attr);
}
TestPrintSettings settings_;
@@ -118,59 +153,59 @@
raw_ptr<MockCupsPrinter> printer_;
};
-TEST_F(PrintingContextTest, SettingsToCupsOptions_Color) {
+TEST_F(PrintingContextTest, SettingsToIPPOptions_Color) {
settings_.set_color(mojom::ColorModel::kGray);
- TestCupsOptionValue(kIppColor, "monochrome");
+ TestStringOptionValue(kIppColor, "monochrome");
settings_.set_color(mojom::ColorModel::kColor);
- TestCupsOptionValue(kIppColor, "color");
+ TestStringOptionValue(kIppColor, "color");
}
-TEST_F(PrintingContextTest, SettingsToCupsOptions_Duplex) {
+TEST_F(PrintingContextTest, SettingsToIPPOptions_Duplex) {
settings_.set_duplex_mode(mojom::DuplexMode::kSimplex);
- TestCupsOptionValue(kIppDuplex, "one-sided");
+ TestStringOptionValue(kIppDuplex, "one-sided");
settings_.set_duplex_mode(mojom::DuplexMode::kLongEdge);
- TestCupsOptionValue(kIppDuplex, "two-sided-long-edge");
+ TestStringOptionValue(kIppDuplex, "two-sided-long-edge");
settings_.set_duplex_mode(mojom::DuplexMode::kShortEdge);
- TestCupsOptionValue(kIppDuplex, "two-sided-short-edge");
+ TestStringOptionValue(kIppDuplex, "two-sided-short-edge");
}
-TEST_F(PrintingContextTest, SettingsToCupsOptions_Media) {
- TestCupsOptionValue(kIppMedia, "");
+TEST_F(PrintingContextTest, SettingsToIPPOptions_Media) {
+ TestStringOptionValue(kIppMedia, "");
settings_.set_requested_media(
{gfx::Size(297000, 420000), "iso_a3_297x420mm"});
- TestCupsOptionValue(kIppMedia, "iso_a3_297x420mm");
+ TestStringOptionValue(kIppMedia, "iso_a3_297x420mm");
}
-TEST_F(PrintingContextTest, SettingsToCupsOptions_Copies) {
+TEST_F(PrintingContextTest, SettingsToIPPOptions_Copies) {
settings_.set_copies(3);
- TestCupsOptionValue(kIppCopies, "3");
+ TestIntegerOptionValue(kIppCopies, 3);
}
-TEST_F(PrintingContextTest, SettingsToCupsOptions_Collate) {
- TestCupsOptionValue(kIppCollate, "separate-documents-uncollated-copies");
+TEST_F(PrintingContextTest, SettingsToIPPOptions_Collate) {
+ TestStringOptionValue(kIppCollate, "separate-documents-uncollated-copies");
settings_.set_collate(true);
- TestCupsOptionValue(kIppCollate, "separate-documents-collated-copies");
+ TestStringOptionValue(kIppCollate, "separate-documents-collated-copies");
}
-TEST_F(PrintingContextTest, SettingsToCupsOptions_Pin) {
- TestCupsOptionValue(kIppPin, nullptr);
+TEST_F(PrintingContextTest, SettingsToIPPOptions_Pin) {
+ EXPECT_FALSE(HasAttribute(kIppPin));
settings_.set_pin_value("1234");
- TestCupsOptionValue(kIppPin, "1234");
+ TestOctetStringOptionValue(kIppPin, base::make_span("1234", 4u));
}
-TEST_F(PrintingContextTest, SettingsToCupsOptions_Resolution) {
- TestCupsOptionValue(kIppResolution, nullptr);
+TEST_F(PrintingContextTest, SettingsToIPPOptions_Resolution) {
+ EXPECT_FALSE(HasAttribute(kIppResolution));
settings_.set_dpi_xy(0, 300);
- TestCupsOptionValue(kIppResolution, nullptr);
+ EXPECT_FALSE(HasAttribute(kIppResolution));
settings_.set_dpi_xy(300, 0);
- TestCupsOptionValue(kIppResolution, nullptr);
+ EXPECT_FALSE(HasAttribute(kIppResolution));
settings_.set_dpi(600);
- TestCupsOptionValue(kIppResolution, "600dpi");
+ TestResolutionOptionValue(kIppResolution, 600, 600);
settings_.set_dpi_xy(600, 1200);
- TestCupsOptionValue(kIppResolution, "600x1200dpi");
+ TestResolutionOptionValue(kIppResolution, 600, 1200);
}
-TEST_F(PrintingContextTest, SettingsToCupsOptions_SendUserInfo_Secure) {
+TEST_F(PrintingContextTest, SettingsToIPPOptions_SendUserInfo_Secure) {
ipp_status_t status = ipp_status_t::IPP_STATUS_OK;
std::u16string document_name = kDocumentName16;
SetDefaultSettings(/*send_user_info=*/true, "ipps://test-uri");
@@ -194,7 +229,7 @@
EXPECT_EQ(start_document_username, kUsername);
}
-TEST_F(PrintingContextTest, SettingsToCupsOptions_SendUserInfo_Insecure) {
+TEST_F(PrintingContextTest, SettingsToIPPOptions_SendUserInfo_Insecure) {
ipp_status_t status = ipp_status_t::IPP_STATUS_OK;
std::u16string document_name = kDocumentName16;
std::string default_username = "chronos";
@@ -220,7 +255,7 @@
EXPECT_EQ(start_document_username, default_username);
}
-TEST_F(PrintingContextTest, SettingsToCupsOptions_DoNotSendUserInfo) {
+TEST_F(PrintingContextTest, SettingsToIPPOptions_DoNotSendUserInfo) {
ipp_status_t status = ipp_status_t::IPP_STATUS_OK;
std::u16string document_name = kDocumentName16;
SetDefaultSettings(/*send_user_info=*/false, "ipps://test-uri");
@@ -244,7 +279,41 @@
EXPECT_EQ(start_document_username, "");
}
-TEST_F(PrintingContextTest, SettingsToCupsOptionsClientInfo) {
+TEST_F(PrintingContextTest, SettingsToIPPOptionsClientInfo) {
+ mojom::IppClientInfo client_info(
+ mojom::IppClientInfo::ClientType::kOperatingSystem, "a-", "B_", "1.",
+ "a.1-B_");
+ settings_.set_client_infos({client_info});
+
+ auto attributes = SettingsToIPPOptions(settings_);
+ auto* attr = ippFindAttribute(attributes.get(), kIppClientInfo,
+ IPP_TAG_BEGIN_COLLECTION);
+ auto* client_info_collection = ippGetCollection(attr, 0);
+
+ attr = ippFindAttribute(client_info_collection, kIppClientName, IPP_TAG_NAME);
+ EXPECT_STREQ("a-", ippGetString(attr, 0, nullptr));
+
+ attr = ippFindAttribute(client_info_collection, kIppClientType, IPP_TAG_ENUM);
+ EXPECT_EQ(4, ippGetInteger(attr, 0));
+
+ attr =
+ ippFindAttribute(client_info_collection, kIppClientPatches, IPP_TAG_TEXT);
+ EXPECT_STREQ("B_", ippGetString(attr, 0, nullptr));
+
+ attr = ippFindAttribute(client_info_collection, kIppClientStringVersion,
+ IPP_TAG_TEXT);
+ EXPECT_STREQ("1.", ippGetString(attr, 0, nullptr));
+
+ attr = ippFindAttribute(client_info_collection, kIppClientVersion,
+ IPP_TAG_STRING);
+ int length;
+ void* version = ippGetOctetString(attr, 0, &length);
+ ASSERT_TRUE(version);
+ EXPECT_EQ(6, length);
+ EXPECT_EQ(0, memcmp("a.1-B_", version, 6));
+}
+
+TEST_F(PrintingContextTest, SettingsToIPPOptionsClientInfoSomeValid) {
mojom::IppClientInfo valid_client_info(
mojom::IppClientInfo::ClientType::kOperatingSystem, "aB.1-_", "aB.1-_",
"aB.1-_", "aB.1-_");
@@ -253,28 +322,22 @@
"aB.1-_", "aB.1-_");
settings_.set_client_infos(
{valid_client_info, invalid_client_info, valid_client_info});
- absl::optional<std::string> option_val = GetCupsOptionValue(kIppClientInfo);
- ASSERT_TRUE(option_val.has_value());
- // Check that the invalid item is skipped in the CUPS option string.
- size_t client_info_item_count =
- base::SplitString(option_val.value(), ",", base::KEEP_WHITESPACE,
- base::SPLIT_WANT_ALL)
- .size();
- EXPECT_EQ(client_info_item_count, 2u);
+ // Check that the invalid item is skipped in the client-info collection.
+ EXPECT_EQ(GetAttrValueCount(kIppClientInfo), 2);
}
-TEST_F(PrintingContextTest, SettingsToCupsOptionsClientInfoEmpty) {
+TEST_F(PrintingContextTest, SettingsToIPPOptionsClientInfoEmpty) {
settings_.set_client_infos({});
- absl::optional<std::string> option_val = GetCupsOptionValue(kIppClientInfo);
- EXPECT_FALSE(option_val.has_value());
+ EXPECT_FALSE(HasAttribute(kIppClientInfo));
mojom::IppClientInfo invalid_client_info(
mojom::IppClientInfo::ClientType::kOther, "$", " ", "{}", absl::nullopt);
settings_.set_client_infos({invalid_client_info});
- EXPECT_FALSE(GetCupsOptionValue(kIppClientInfo).has_value());
+ EXPECT_FALSE(HasAttribute(kIppClientInfo));
}
+
} // namespace
} // namespace printing