Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(206)

Side by Side Diff: printing/backend/cups_jobs.cc

Issue 2891643002: Add a method to query IPP printers for attributes. (Closed)
Patch Set: Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« printing/backend/cups_jobs.h ('K') | « printing/backend/cups_jobs.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "printing/backend/cups_jobs.h" 5 #include "printing/backend/cups_jobs.h"
6 6
7 #include <cups/ipp.h> 7 #include <cups/ipp.h>
8 8
9 #include <array> 9 #include <array>
10 #include <map> 10 #include <map>
11 #include <memory> 11 #include <memory>
12 12
13 #include "base/logging.h" 13 #include "base/logging.h"
14 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_piece.h" 15 #include "base/strings/string_piece.h"
16 #include "base/strings/string_split.h"
15 #include "base/strings/stringprintf.h" 17 #include "base/strings/stringprintf.h"
18 #include "printing/backend/cups_deleters.h"
16 19
17 namespace printing { 20 namespace printing {
18 namespace { 21 namespace {
19 22
20 using PReason = PrinterStatus::PrinterReason::Reason; 23 using PReason = PrinterStatus::PrinterReason::Reason;
21 using PSeverity = PrinterStatus::PrinterReason::Severity; 24 using PSeverity = PrinterStatus::PrinterReason::Severity;
22 25
23 // printer attributes 26 // printer attributes
24 const char kPrinterUri[] = "printer-uri"; 27 const char kPrinterUri[] = "printer-uri";
25 const char kPrinterState[] = "printer-state"; 28 const char kPrinterState[] = "printer-state";
26 const char kPrinterStateReasons[] = "printer-state-reasons"; 29 const char kPrinterStateReasons[] = "printer-state-reasons";
27 const char kPrinterStateMessage[] = "printer-state-message"; 30 const char kPrinterStateMessage[] = "printer-state-message";
28 31
32 const char kPrinterMakeAndModel[] = "printer-make-and-model";
33 const char kIppVersionsSupported[] = "ipp-versions-supported";
34 const char kIppFeaturesSupported[] = "ipp-features-supported";
35
29 // job attributes 36 // job attributes
30 const char kJobUri[] = "job-uri"; 37 const char kJobUri[] = "job-uri";
31 const char kJobId[] = "job-id"; 38 const char kJobId[] = "job-id";
32 const char kJobState[] = "job-state"; 39 const char kJobState[] = "job-state";
33 const char kJobStateReasons[] = "job-state-reasons"; 40 const char kJobStateReasons[] = "job-state-reasons";
34 const char kJobStateMessage[] = "job-state-message"; 41 const char kJobStateMessage[] = "job-state-message";
35 const char kJobImpressionsCompleted[] = "job-impressions-completed"; 42 const char kJobImpressionsCompleted[] = "job-impressions-completed";
36 const char kTimeAtProcessing[] = "time-at-processing"; 43 const char kTimeAtProcessing[] = "time-at-processing";
37 44
38 // request parameters 45 // request parameters
39 const char kRequestedAttributes[] = "requested-attributes"; 46 const char kRequestedAttributes[] = "requested-attributes";
40 const char kWhichJobs[] = "which-jobs"; 47 const char kWhichJobs[] = "which-jobs";
41 const char kLimit[] = "limit"; 48 const char kLimit[] = "limit";
42 49
43 // request values 50 // request values
44 const char kCompleted[] = "completed"; 51 const char kCompleted[] = "completed";
45 const char kNotCompleted[] = "not-completed"; 52 const char kNotCompleted[] = "not-completed";
46 53
54 // ipp features
55 const char kIppEverywhere[] = "ipp-everywhere";
56
47 // printer state severities 57 // printer state severities
48 const char kSeverityReport[] = "report"; 58 const char kSeverityReport[] = "report";
49 const char kSeverityWarn[] = "warning"; 59 const char kSeverityWarn[] = "warning";
50 const char kSeverityError[] = "error"; 60 const char kSeverityError[] = "error";
51 61
52 // printer state reason values 62 // printer state reason values
53 const char kNone[] = "none"; 63 const char kNone[] = "none";
54 const char kMediaNeeded[] = "media-needed"; 64 const char kMediaNeeded[] = "media-needed";
55 const char kMediaJam[] = "media-jam"; 65 const char kMediaJam[] = "media-jam";
56 const char kMovingToPaused[] = "moving-to-paused"; 66 const char kMovingToPaused[] = "moving-to-paused";
(...skipping 24 matching lines...) Expand all
81 const char kOpcNearEol[] = "opc-near-eol"; 91 const char kOpcNearEol[] = "opc-near-eol";
82 const char kOpcLifeOver[] = "opc-life-over"; 92 const char kOpcLifeOver[] = "opc-life-over";
83 const char kDeveloperLow[] = "developer-low"; 93 const char kDeveloperLow[] = "developer-low";
84 const char kDeveloperEmpty[] = "developer-empty"; 94 const char kDeveloperEmpty[] = "developer-empty";
85 const char kInterpreterResourceUnavailable[] = 95 const char kInterpreterResourceUnavailable[] =
86 "interpreter-resource-unavailable"; 96 "interpreter-resource-unavailable";
87 97
88 constexpr std::array<const char* const, 3> kPrinterAttributes{ 98 constexpr std::array<const char* const, 3> kPrinterAttributes{
89 {kPrinterState, kPrinterStateReasons, kPrinterStateMessage}}; 99 {kPrinterState, kPrinterStateReasons, kPrinterStateMessage}};
90 100
91 std::unique_ptr<ipp_t, void (*)(ipp_t*)> WrapIpp(ipp_t* ipp) { 101 constexpr std::array<const char* const, 3> kPrinterInfo{
92 return std::unique_ptr<ipp_t, void (*)(ipp_t*)>(ipp, &ippDelete); 102 {kPrinterMakeAndModel, kIppVersionsSupported, kIppFeaturesSupported}};
103
104 using IppPtr = std::unique_ptr<ipp_t, void (*)(ipp_t*)>;
105
106 IppPtr WrapIpp(ipp_t* ipp) {
107 return IppPtr(ipp, &ippDelete);
93 } 108 }
94 109
110 using HttpPtr = std::unique_ptr<http_t, HttpDeleter>;
111
95 // Converts an IPP attribute |attr| to the appropriate JobState enum. 112 // Converts an IPP attribute |attr| to the appropriate JobState enum.
96 CupsJob::JobState ToJobState(ipp_attribute_t* attr) { 113 CupsJob::JobState ToJobState(ipp_attribute_t* attr) {
97 DCHECK_EQ(IPP_TAG_ENUM, ippGetValueTag(attr)); 114 DCHECK_EQ(IPP_TAG_ENUM, ippGetValueTag(attr));
98 int state = ippGetInteger(attr, 0); 115 int state = ippGetInteger(attr, 0);
99 switch (state) { 116 switch (state) {
100 case IPP_JOB_ABORTED: 117 case IPP_JOB_ABORTED:
101 return CupsJob::ABORTED; 118 return CupsJob::ABORTED;
102 case IPP_JOB_CANCELLED: 119 case IPP_JOB_CANCELLED:
103 return CupsJob::CANCELED; 120 return CupsJob::CANCELED;
104 case IPP_JOB_COMPLETED: 121 case IPP_JOB_COMPLETED:
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after
288 CupsJob::CupsJob(const CupsJob& other) = default; 305 CupsJob::CupsJob(const CupsJob& other) = default;
289 306
290 CupsJob::~CupsJob() = default; 307 CupsJob::~CupsJob() = default;
291 308
292 PrinterStatus::PrinterStatus() = default; 309 PrinterStatus::PrinterStatus() = default;
293 310
294 PrinterStatus::PrinterStatus(const PrinterStatus& other) = default; 311 PrinterStatus::PrinterStatus(const PrinterStatus& other) = default;
295 312
296 PrinterStatus::~PrinterStatus() = default; 313 PrinterStatus::~PrinterStatus() = default;
297 314
315 PrinterInfo::PrinterInfo() = default;
316
317 PrinterInfo::~PrinterInfo() = default;
318
298 void ParseJobsResponse(ipp_t* response, 319 void ParseJobsResponse(ipp_t* response,
299 const std::string& printer_id, 320 const std::string& printer_id,
300 std::vector<CupsJob>* jobs) { 321 std::vector<CupsJob>* jobs) {
301 // Advance the position in the response to the jobs section. 322 // Advance the position in the response to the jobs section.
302 ipp_attribute_t* attr = ippFirstAttribute(response); 323 ipp_attribute_t* attr = ippFirstAttribute(response);
303 while (attr != nullptr && ippGetGroupTag(attr) != IPP_TAG_JOB) { 324 while (attr != nullptr && ippGetGroupTag(attr) != IPP_TAG_JOB) {
304 attr = ippNextAttribute(response); 325 attr = ippNextAttribute(response);
305 } 326 }
306 327
307 if (attr != nullptr) { 328 if (attr != nullptr) {
308 ParseJobs(response, printer_id, attr, jobs); 329 ParseJobs(response, printer_id, attr, jobs);
309 } 330 }
310 } 331 }
311 332
333 IppPtr GetPrinterAttributes(http_t* http,
334 const std::string& printer_uri,
335 int num_attributes,
336 const char* const* attributes,
337 ipp_status_t* status) {
338 DCHECK(http);
339
340 auto request = WrapIpp(ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES));
341
342 ippAddString(request.get(), IPP_TAG_OPERATION, IPP_TAG_URI, kPrinterUri,
343 nullptr, printer_uri.data());
344
345 ippAddStrings(request.get(), IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
346 kRequestedAttributes, num_attributes, nullptr, attributes);
347
348 auto response =
349 WrapIpp(cupsDoRequest(http, request.release(), printer_uri.c_str()));
350 *status = ippGetStatusCode(response.get());
351
352 return response;
353 }
354
355 std::pair<int, int> ToVersionNumber(const std::string& version, bool* success) {
Carlson 2017/05/25 19:04:36 Not sure this is a formal thing, but it seems like
Carlson 2017/05/25 19:04:36 Function comment? (Also, shouldn't these free fun
skau 2017/05/27 02:01:20 I've seen both but returning a bool has been more
356 std::vector<base::StringPiece> pieces = SplitStringPiece(
357 version, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
358 DCHECK_EQ(2U, pieces.size());
Carlson 2017/05/25 19:04:36 This is input validation, and so can't be a DCHECK
skau 2017/05/27 02:01:20 Done.
359 int major;
360 int minor;
361 *success = base::StringToInt(pieces[0], &major) &&
362 base::StringToInt(pieces[1], &minor);
363
364 return {major, minor};
Carlson 2017/05/25 19:04:36 Structure this so that we return a known bad major
skau 2017/05/27 02:01:20 Done.
365 }
366
367 // Extracts PrinterInfo fields from |response| and populates |printer_info|.
368 // Returns true if at least printer-make-and-model and ipp-versions-supported
369 // were read.
370 bool ParsePrinterInfo(ipp_t* response, PrinterInfo* printer_info) {
371 for (ipp_attribute_t* attr = ippFirstAttribute(response); attr != nullptr;
372 attr = ippNextAttribute(response)) {
373 base::StringPiece name = ippGetName(attr);
374 if (name.empty()) {
Carlson 2017/05/25 19:04:36 This case is not explicitly needed, but if you lik
skau 2017/05/27 02:01:19 Done.
375 continue;
376 }
377
378 if (name == kPrinterMakeAndModel) {
Carlson 2017/05/25 19:04:36 Paranoid favor, can you make the StringPiece conve
skau 2017/05/27 02:01:20 Sounds like a traumatic experience. Let's avoid t
379 DCHECK_EQ(IPP_TAG_TEXT, ippGetValueTag(attr));
380 printer_info->make_and_model = ippGetString(attr, 0, nullptr);
381 } else if (name == kIppVersionsSupported) {
382 std::vector<std::string> ipp_versions;
383 ParseCollection(attr, &ipp_versions);
384 for (const std::string& version : ipp_versions) {
385 bool success;
386 std::pair<int, int> major_minor = ToVersionNumber(version, &success);
387 if (success) {
388 printer_info->ipp_versions.push_back(major_minor);
389 }
390 }
391 } else if (name == kIppFeaturesSupported) {
392 std::vector<std::string> features;
393 ParseCollection(attr, &features);
394 for (const std::string& feature : features) {
395 if (feature == kIppEverywhere) {
396 printer_info->ipp_everywhere = true;
397 break;
398 }
399 }
400 }
401 }
402
403 return !printer_info->make_and_model.empty() &&
404 !printer_info->ipp_versions.empty();
405 }
406
312 void ParsePrinterStatus(ipp_t* response, PrinterStatus* printer_status) { 407 void ParsePrinterStatus(ipp_t* response, PrinterStatus* printer_status) {
313 for (ipp_attribute_t* attr = ippFirstAttribute(response); attr != nullptr; 408 for (ipp_attribute_t* attr = ippFirstAttribute(response); attr != nullptr;
314 attr = ippNextAttribute(response)) { 409 attr = ippNextAttribute(response)) {
315 base::StringPiece name = ippGetName(attr); 410 base::StringPiece name = ippGetName(attr);
316 if (name.empty()) { 411 if (name.empty()) {
317 continue; 412 continue;
318 } 413 }
319 414
320 if (name == kPrinterState) { 415 if (name == kPrinterState) {
321 DCHECK_EQ(IPP_TAG_ENUM, ippGetValueTag(attr)); 416 DCHECK_EQ(IPP_TAG_ENUM, ippGetValueTag(attr));
322 printer_status->state = static_cast<ipp_pstate_t>(ippGetInteger(attr, 0)); 417 printer_status->state = static_cast<ipp_pstate_t>(ippGetInteger(attr, 0));
323 } else if (name == kPrinterStateReasons) { 418 } else if (name == kPrinterStateReasons) {
324 std::vector<std::string> reason_strings; 419 std::vector<std::string> reason_strings;
325 ParseCollection(attr, &reason_strings); 420 ParseCollection(attr, &reason_strings);
326 for (const std::string& reason : reason_strings) { 421 for (const std::string& reason : reason_strings) {
327 printer_status->reasons.push_back(ToPrinterReason(reason)); 422 printer_status->reasons.push_back(ToPrinterReason(reason));
328 } 423 }
329 } else if (name == kPrinterStateMessage) { 424 } else if (name == kPrinterStateMessage) {
330 printer_status->message = ippGetString(attr, 0, nullptr); 425 printer_status->message = ippGetString(attr, 0, nullptr);
331 } 426 }
332 } 427 }
333 } 428 }
334 429
430 bool GetPrinterInfo(const std::string& address,
431 const int port,
432 const std::string& resource,
433 PrinterInfo* printer_info) {
434 ipp_status_t status;
435 HttpPtr http =
436 HttpPtr(httpConnect2(address.data(), port, nullptr, AF_INET,
437 HTTP_ENCRYPTION_IF_REQUESTED, 0, 200, nullptr));
438 auto response = GetPrinterAttributes(
439 http.get(), resource, kPrinterInfo.size(), kPrinterInfo.data(), &status);
440 return status == IPP_STATUS_OK &&
441 ParsePrinterInfo(response.get(), printer_info);
442 }
443
335 bool GetPrinterStatus(http_t* http, 444 bool GetPrinterStatus(http_t* http,
336 const std::string& printer_id, 445 const std::string& printer_id,
337 PrinterStatus* printer_status) { 446 PrinterStatus* printer_status) {
338 DCHECK(http); 447 ipp_status_t status;
339
340 auto request = WrapIpp(ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES));
341
342 const std::string printer_uri = PrinterUriFromName(printer_id); 448 const std::string printer_uri = PrinterUriFromName(printer_id);
343 ippAddString(request.get(), IPP_TAG_OPERATION, IPP_TAG_URI, kPrinterUri,
344 nullptr, printer_uri.data());
345
346 ippAddStrings(request.get(), IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
347 kRequestedAttributes, kPrinterAttributes.size(), nullptr,
348 kPrinterAttributes.data());
349 449
350 auto response = 450 auto response =
351 WrapIpp(cupsDoRequest(http, request.release(), printer_uri.c_str())); 451 GetPrinterAttributes(http, printer_uri, kPrinterAttributes.size(),
452 kPrinterAttributes.data(), &status);
352 453
353 if (ippGetStatusCode(response.get()) != IPP_STATUS_OK) 454 if (status != IPP_STATUS_OK)
354 return false; 455 return false;
355 456
356 ParsePrinterStatus(response.get(), printer_status); 457 ParsePrinterStatus(response.get(), printer_status);
357 458
358 return true; 459 return true;
359 } 460 }
360 461
361 bool GetCupsJobs(http_t* http, 462 bool GetCupsJobs(http_t* http,
362 const std::string& printer_id, 463 const std::string& printer_id,
363 int limit, 464 int limit,
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
399 LOG(WARNING) << "IPP Error: " << cupsLastErrorString(); 500 LOG(WARNING) << "IPP Error: " << cupsLastErrorString();
400 return false; 501 return false;
401 } 502 }
402 503
403 ParseJobsResponse(response.get(), printer_id, jobs); 504 ParseJobsResponse(response.get(), printer_id, jobs);
404 505
405 return true; 506 return true;
406 } 507 }
407 508
408 } // namespace printing 509 } // namespace printing
OLDNEW
« printing/backend/cups_jobs.h ('K') | « printing/backend/cups_jobs.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698