| Index: printing/backend/cups_jobs.cc
|
| diff --git a/printing/backend/cups_jobs.cc b/printing/backend/cups_jobs.cc
|
| index e73ae8fb331041aaa5754074a924856980eca18d..80daed78723f5dba657dd4c7814f2f55d4dc3887 100644
|
| --- a/printing/backend/cups_jobs.cc
|
| +++ b/printing/backend/cups_jobs.cc
|
| @@ -11,8 +11,12 @@
|
| #include <memory>
|
|
|
| #include "base/logging.h"
|
| +#include "base/stl_util.h"
|
| #include "base/strings/string_piece.h"
|
| #include "base/strings/stringprintf.h"
|
| +#include "base/threading/thread_restrictions.h"
|
| +#include "base/version.h"
|
| +#include "printing/backend/cups_deleters.h"
|
|
|
| namespace printing {
|
| namespace {
|
| @@ -26,6 +30,11 @@ const char kPrinterState[] = "printer-state";
|
| const char kPrinterStateReasons[] = "printer-state-reasons";
|
| const char kPrinterStateMessage[] = "printer-state-message";
|
|
|
| +const char kPrinterMakeAndModel[] = "printer-make-and-model";
|
| +const char kIppVersionsSupported[] = "ipp-versions-supported";
|
| +const char kIppFeaturesSupported[] = "ipp-features-supported";
|
| +const char kDocumentFormatSupported[] = "document-format-supported";
|
| +
|
| // job attributes
|
| const char kJobUri[] = "job-uri";
|
| const char kJobId[] = "job-id";
|
| @@ -44,6 +53,9 @@ const char kLimit[] = "limit";
|
| const char kCompleted[] = "completed";
|
| const char kNotCompleted[] = "not-completed";
|
|
|
| +// ipp features
|
| +const char kIppEverywhere[] = "ipp-everywhere";
|
| +
|
| // printer state severities
|
| const char kSeverityReport[] = "report";
|
| const char kSeverityWarn[] = "warning";
|
| @@ -88,10 +100,18 @@ const char kInterpreterResourceUnavailable[] =
|
| constexpr std::array<const char* const, 3> kPrinterAttributes{
|
| {kPrinterState, kPrinterStateReasons, kPrinterStateMessage}};
|
|
|
| -std::unique_ptr<ipp_t, void (*)(ipp_t*)> WrapIpp(ipp_t* ipp) {
|
| - return std::unique_ptr<ipp_t, void (*)(ipp_t*)>(ipp, &ippDelete);
|
| +constexpr std::array<const char* const, 4> kPrinterInfo{
|
| + {kPrinterMakeAndModel, kIppVersionsSupported, kIppFeaturesSupported,
|
| + kDocumentFormatSupported}};
|
| +
|
| +using ScopedIppPtr = std::unique_ptr<ipp_t, void (*)(ipp_t*)>;
|
| +
|
| +ScopedIppPtr WrapIpp(ipp_t* ipp) {
|
| + return ScopedIppPtr(ipp, &ippDelete);
|
| }
|
|
|
| +using ScopedHttpPtr = std::unique_ptr<http_t, HttpDeleter>;
|
| +
|
| // Converts an IPP attribute |attr| to the appropriate JobState enum.
|
| CupsJob::JobState ToJobState(ipp_attribute_t* attr) {
|
| DCHECK_EQ(IPP_TAG_ENUM, ippGetValueTag(attr));
|
| @@ -281,6 +301,40 @@ std::string PrinterUriFromName(const std::string& id) {
|
| return base::StringPrintf("ipp://localhost/printers/%s", id.c_str());
|
| }
|
|
|
| +// Extracts PrinterInfo fields from |response| and populates |printer_info|.
|
| +// Returns true if at least printer-make-and-model and ipp-versions-supported
|
| +// were read.
|
| +bool ParsePrinterInfo(ipp_t* response, PrinterInfo* printer_info) {
|
| + for (ipp_attribute_t* attr = ippFirstAttribute(response); attr != nullptr;
|
| + attr = ippNextAttribute(response)) {
|
| + base::StringPiece name = ippGetName(attr);
|
| +
|
| + if (name == base::StringPiece(kPrinterMakeAndModel)) {
|
| + DCHECK_EQ(IPP_TAG_TEXT, ippGetValueTag(attr));
|
| + printer_info->make_and_model = ippGetString(attr, 0, nullptr);
|
| + } else if (name == base::StringPiece(kIppVersionsSupported)) {
|
| + std::vector<std::string> ipp_versions;
|
| + ParseCollection(attr, &ipp_versions);
|
| + for (const std::string& version : ipp_versions) {
|
| + base::Version major_minor(version);
|
| + if (major_minor.IsValid()) {
|
| + printer_info->ipp_versions.push_back(major_minor);
|
| + }
|
| + }
|
| + } else if (name == base::StringPiece(kIppFeaturesSupported)) {
|
| + std::vector<std::string> features;
|
| + ParseCollection(attr, &features);
|
| + printer_info->ipp_everywhere =
|
| + base::ContainsValue(features, kIppEverywhere);
|
| + } else if (name == base::StringPiece(kDocumentFormatSupported)) {
|
| + ParseCollection(attr, &printer_info->document_formats);
|
| + }
|
| + }
|
| +
|
| + return !printer_info->make_and_model.empty() &&
|
| + !printer_info->ipp_versions.empty();
|
| +}
|
| +
|
| } // namespace
|
|
|
| CupsJob::CupsJob() = default;
|
| @@ -295,6 +349,10 @@ PrinterStatus::PrinterStatus(const PrinterStatus& other) = default;
|
|
|
| PrinterStatus::~PrinterStatus() = default;
|
|
|
| +PrinterInfo::PrinterInfo() = default;
|
| +
|
| +PrinterInfo::~PrinterInfo() = default;
|
| +
|
| void ParseJobsResponse(ipp_t* response,
|
| const std::string& printer_id,
|
| std::vector<CupsJob>* jobs) {
|
| @@ -309,6 +367,40 @@ void ParseJobsResponse(ipp_t* response,
|
| }
|
| }
|
|
|
| +// Returns an IPP response for a Get-Printer-Attributes request to |http|. For
|
| +// print servers, |printer_uri| is used as the printer-uri value.
|
| +// |resource_path| specifies the path portion of the server URI.
|
| +// |num_attributes| is the number of attributes in |attributes| which should be
|
| +// a list of IPP attributes. |status| is updated with status code for the
|
| +// request. A successful request will have the |status| IPP_STATUS_OK.
|
| +ScopedIppPtr GetPrinterAttributes(http_t* http,
|
| + const std::string& printer_uri,
|
| + const std::string& resource_path,
|
| + int num_attributes,
|
| + const char* const* attributes,
|
| + ipp_status_t* status) {
|
| + base::ThreadRestrictions::AssertIOAllowed();
|
| + DCHECK(http);
|
| +
|
| + // CUPS expects a leading slash for resource names. Add one if it's missing.
|
| + std::string rp = !resource_path.empty() && resource_path.front() == '/'
|
| + ? resource_path
|
| + : "/" + resource_path;
|
| +
|
| + auto request = WrapIpp(ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES));
|
| +
|
| + ippAddString(request.get(), IPP_TAG_OPERATION, IPP_TAG_URI, kPrinterUri,
|
| + nullptr, printer_uri.data());
|
| +
|
| + ippAddStrings(request.get(), IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
|
| + kRequestedAttributes, num_attributes, nullptr, attributes);
|
| +
|
| + auto response = WrapIpp(cupsDoRequest(http, request.release(), rp.c_str()));
|
| + *status = ippGetStatusCode(response.get());
|
| +
|
| + return response;
|
| +}
|
| +
|
| void ParsePrinterStatus(ipp_t* response, PrinterStatus* printer_status) {
|
| for (ipp_attribute_t* attr = ippFirstAttribute(response); attr != nullptr;
|
| attr = ippNextAttribute(response)) {
|
| @@ -332,25 +424,45 @@ void ParsePrinterStatus(ipp_t* response, PrinterStatus* printer_status) {
|
| }
|
| }
|
|
|
| +bool GetPrinterInfo(const std::string& address,
|
| + const int port,
|
| + const std::string& resource,
|
| + PrinterInfo* printer_info) {
|
| + base::ThreadRestrictions::AssertIOAllowed();
|
| +
|
| + ScopedHttpPtr http = ScopedHttpPtr(
|
| + httpConnect2(address.data(), port, nullptr, AF_INET,
|
| + HTTP_ENCRYPTION_IF_REQUESTED, 0, 200, nullptr));
|
| + if (!http) {
|
| + LOG(WARNING) << "Could not connect to host";
|
| + return false;
|
| + }
|
| +
|
| + ipp_status_t status;
|
| + ScopedIppPtr response =
|
| + GetPrinterAttributes(http.get(), resource, resource, kPrinterInfo.size(),
|
| + kPrinterInfo.data(), &status);
|
| + if (status != IPP_STATUS_OK || response.get() == nullptr) {
|
| + LOG(WARNING) << "Get attributes failure: " << status;
|
| + return false;
|
| + }
|
| +
|
| + return ParsePrinterInfo(response.get(), printer_info);
|
| +}
|
| +
|
| bool GetPrinterStatus(http_t* http,
|
| const std::string& printer_id,
|
| PrinterStatus* printer_status) {
|
| - DCHECK(http);
|
| -
|
| - auto request = WrapIpp(ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES));
|
| + base::ThreadRestrictions::AssertIOAllowed();
|
|
|
| + ipp_status_t status;
|
| const std::string printer_uri = PrinterUriFromName(printer_id);
|
| - ippAddString(request.get(), IPP_TAG_OPERATION, IPP_TAG_URI, kPrinterUri,
|
| - nullptr, printer_uri.data());
|
| -
|
| - ippAddStrings(request.get(), IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
|
| - kRequestedAttributes, kPrinterAttributes.size(), nullptr,
|
| - kPrinterAttributes.data());
|
|
|
| - auto response =
|
| - WrapIpp(cupsDoRequest(http, request.release(), printer_uri.c_str()));
|
| + ScopedIppPtr response =
|
| + GetPrinterAttributes(http, printer_uri, "/", kPrinterAttributes.size(),
|
| + kPrinterAttributes.data(), &status);
|
|
|
| - if (ippGetStatusCode(response.get()) != IPP_STATUS_OK)
|
| + if (status != IPP_STATUS_OK)
|
| return false;
|
|
|
| ParsePrinterStatus(response.get(), printer_status);
|
| @@ -363,6 +475,7 @@ bool GetCupsJobs(http_t* http,
|
| int limit,
|
| JobCompletionState which,
|
| std::vector<CupsJob>* jobs) {
|
| + base::ThreadRestrictions::AssertIOAllowed();
|
| DCHECK(http);
|
|
|
| auto request = WrapIpp(ippNewRequest(IPP_OP_GET_JOBS));
|
| @@ -390,12 +503,11 @@ bool GetCupsJobs(http_t* http,
|
| }
|
|
|
| // cupsDoRequest will delete the request.
|
| - auto response =
|
| - WrapIpp(cupsDoRequest(http, request.release(), printer_uri.c_str()));
|
| + auto response = WrapIpp(cupsDoRequest(http, request.release(), "/"));
|
|
|
| ipp_status_t status = ippGetStatusCode(response.get());
|
|
|
| - if (status != IPP_OK) {
|
| + if (status != IPP_STATUS_OK) {
|
| LOG(WARNING) << "IPP Error: " << cupsLastErrorString();
|
| return false;
|
| }
|
|
|