cups_jobs.cc 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  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
  3. // found in the LICENSE file.
  4. #include "printing/backend/cups_jobs.h"
  5. #include <cups/ipp.h>
  6. #include <array>
  7. #include <cstring>
  8. #include <map>
  9. #include <memory>
  10. #include <string>
  11. #include "base/containers/contains.h"
  12. #include "base/logging.h"
  13. #include "base/notreached.h"
  14. #include "base/strings/string_number_conversions.h"
  15. #include "base/strings/string_piece.h"
  16. #include "base/strings/stringprintf.h"
  17. #include "base/threading/scoped_blocking_call.h"
  18. #include "base/version.h"
  19. #include "printing/backend/cups_deleters.h"
  20. #include "printing/backend/cups_ipp_helper.h"
  21. #include "printing/printer_status.h"
  22. namespace printing {
  23. namespace {
  24. using PReason = PrinterStatus::PrinterReason::Reason;
  25. using PSeverity = PrinterStatus::PrinterReason::Severity;
  26. // printer attributes
  27. constexpr char kPrinterUri[] = "printer-uri";
  28. constexpr char kPrinterState[] = "printer-state";
  29. constexpr char kPrinterStateReasons[] = "printer-state-reasons";
  30. constexpr char kPrinterStateMessage[] = "printer-state-message";
  31. constexpr char kPrinterMakeAndModel[] = "printer-make-and-model";
  32. constexpr char kIppVersionsSupported[] = "ipp-versions-supported";
  33. constexpr char kIppFeaturesSupported[] = "ipp-features-supported";
  34. constexpr char kDocumentFormatSupported[] = "document-format-supported";
  35. // job attributes
  36. constexpr char kJobUri[] = "job-uri";
  37. constexpr char kJobId[] = "job-id";
  38. constexpr char kJobState[] = "job-state";
  39. constexpr char kJobStateReasons[] = "job-state-reasons";
  40. constexpr char kJobStateMessage[] = "job-state-message";
  41. constexpr char kJobImpressionsCompleted[] = "job-impressions-completed";
  42. constexpr char kTimeAtProcessing[] = "time-at-processing";
  43. // request parameters
  44. constexpr char kRequestedAttributes[] = "requested-attributes";
  45. constexpr char kWhichJobs[] = "which-jobs";
  46. constexpr char kLimit[] = "limit";
  47. // request values
  48. constexpr char kCompleted[] = "completed";
  49. constexpr char kNotCompleted[] = "not-completed";
  50. // ipp features
  51. constexpr char kIppEverywhere[] = "ipp-everywhere";
  52. // job state reason values
  53. constexpr char kJobCompletedWithErrors[] = "job-completed-with-errors";
  54. constexpr char kCupsHeldForAuthentication[] = "cups-held-for-authentication";
  55. // printer state severities
  56. constexpr char kSeverityReport[] = "report";
  57. constexpr char kSeverityWarn[] = "warning";
  58. constexpr char kSeverityError[] = "error";
  59. // printer state reason values
  60. constexpr char kNone[] = "none";
  61. constexpr char kMediaNeeded[] = "media-needed";
  62. constexpr char kMediaJam[] = "media-jam";
  63. constexpr char kMovingToPaused[] = "moving-to-paused";
  64. constexpr char kPaused[] = "paused";
  65. constexpr char kShutdown[] = "shutdown";
  66. constexpr char kConnectingToDevice[] = "connecting-to-device";
  67. constexpr char kTimedOut[] = "timed-out";
  68. constexpr char kStopping[] = "stopping";
  69. constexpr char kStoppedPartly[] = "stopped-partly";
  70. constexpr char kTonerLow[] = "toner-low";
  71. constexpr char kTonerEmpty[] = "toner-empty";
  72. constexpr char kSpoolAreaFull[] = "spool-area-full";
  73. constexpr char kCoverOpen[] = "cover-open";
  74. constexpr char kInterlockOpen[] = "interlock-open";
  75. constexpr char kDoorOpen[] = "door-open";
  76. constexpr char kInputTrayMissing[] = "input-tray-missing";
  77. constexpr char kMediaLow[] = "media-low";
  78. constexpr char kMediaEmpty[] = "media-empty";
  79. constexpr char kOutputTrayMissing[] = "output-tray-missing";
  80. constexpr char kOutputAreaAlmostFull[] = "output-area-almost-full";
  81. constexpr char kOutputAreaFull[] = "output-area-full";
  82. constexpr char kMarkerSupplyLow[] = "marker-supply-low";
  83. constexpr char kMarkerSupplyEmpty[] = "marker-supply-empty";
  84. constexpr char kMarkerWasteAlmostFull[] = "marker-waste-almost-full";
  85. constexpr char kMarkerWasteFull[] = "marker-waste-full";
  86. constexpr char kFuserOverTemp[] = "fuser-over-temp";
  87. constexpr char kFuserUnderTemp[] = "fuser-under-temp";
  88. constexpr char kOpcNearEol[] = "opc-near-eol";
  89. constexpr char kOpcLifeOver[] = "opc-life-over";
  90. constexpr char kDeveloperLow[] = "developer-low";
  91. constexpr char kDeveloperEmpty[] = "developer-empty";
  92. constexpr char kInterpreterResourceUnavailable[] =
  93. "interpreter-resource-unavailable";
  94. constexpr char kIppScheme[] = "ipp";
  95. constexpr char kIppsScheme[] = "ipps";
  96. // Timeout for establishing a HTTP connection in milliseconds. Anecdotally,
  97. // some print servers are slow and can use the extra time.
  98. constexpr int kHttpConnectTimeoutMs = 1000;
  99. constexpr std::array<const char* const, 3> kPrinterAttributes{
  100. {kPrinterState, kPrinterStateReasons, kPrinterStateMessage}};
  101. constexpr std::array<const char* const, 7> kPrinterInfoAndStatus{
  102. {kPrinterMakeAndModel, kIppVersionsSupported, kIppFeaturesSupported,
  103. kDocumentFormatSupported, kPrinterState, kPrinterStateReasons,
  104. kPrinterStateMessage}};
  105. // Converts an IPP attribute `attr` to the appropriate JobState enum.
  106. CupsJob::JobState ToJobState(ipp_attribute_t* attr) {
  107. DCHECK_EQ(IPP_TAG_ENUM, ippGetValueTag(attr));
  108. int state = ippGetInteger(attr, 0);
  109. switch (state) {
  110. case IPP_JOB_ABORTED:
  111. return CupsJob::ABORTED;
  112. case IPP_JOB_CANCELLED:
  113. return CupsJob::CANCELED;
  114. case IPP_JOB_COMPLETED:
  115. return CupsJob::COMPLETED;
  116. case IPP_JOB_HELD:
  117. return CupsJob::HELD;
  118. case IPP_JOB_PENDING:
  119. return CupsJob::PENDING;
  120. case IPP_JOB_PROCESSING:
  121. return CupsJob::PROCESSING;
  122. case IPP_JOB_STOPPED:
  123. return CupsJob::STOPPED;
  124. default:
  125. NOTREACHED() << "Unidentifed state " << state;
  126. break;
  127. }
  128. return CupsJob::UNKNOWN;
  129. }
  130. // Returns a lookup map from strings to PrinterReason::Reason.
  131. const std::map<base::StringPiece, PReason>& GetLabelToReason() {
  132. static const std::map<base::StringPiece, PReason> kLabelToReason =
  133. std::map<base::StringPiece, PReason>{
  134. {kNone, PReason::kNone},
  135. {kMediaNeeded, PReason::kMediaNeeded},
  136. {kMediaJam, PReason::kMediaJam},
  137. {kMovingToPaused, PReason::kMovingToPaused},
  138. {kPaused, PReason::kPaused},
  139. {kShutdown, PReason::kShutdown},
  140. {kConnectingToDevice, PReason::kConnectingToDevice},
  141. {kTimedOut, PReason::kTimedOut},
  142. {kStopping, PReason::kStopping},
  143. {kStoppedPartly, PReason::kStoppedPartly},
  144. {kTonerLow, PReason::kTonerLow},
  145. {kTonerEmpty, PReason::kTonerEmpty},
  146. {kSpoolAreaFull, PReason::kSpoolAreaFull},
  147. {kCoverOpen, PReason::kCoverOpen},
  148. {kInterlockOpen, PReason::kInterlockOpen},
  149. {kDoorOpen, PReason::kDoorOpen},
  150. {kInputTrayMissing, PReason::kInputTrayMissing},
  151. {kMediaLow, PReason::kMediaLow},
  152. {kMediaEmpty, PReason::kMediaEmpty},
  153. {kOutputTrayMissing, PReason::kOutputTrayMissing},
  154. {kOutputAreaAlmostFull, PReason::kOutputAreaAlmostFull},
  155. {kOutputAreaFull, PReason::kOutputAreaFull},
  156. {kMarkerSupplyLow, PReason::kMarkerSupplyLow},
  157. {kMarkerSupplyEmpty, PReason::kMarkerSupplyEmpty},
  158. {kMarkerWasteAlmostFull, PReason::kMarkerWasteAlmostFull},
  159. {kMarkerWasteFull, PReason::kMarkerWasteFull},
  160. {kFuserOverTemp, PReason::kFuserOverTemp},
  161. {kFuserUnderTemp, PReason::kFuserUnderTemp},
  162. {kOpcNearEol, PReason::kOpcNearEol},
  163. {kOpcLifeOver, PReason::kOpcLifeOver},
  164. {kDeveloperLow, PReason::kDeveloperLow},
  165. {kDeveloperEmpty, PReason::kDeveloperEmpty},
  166. {kInterpreterResourceUnavailable,
  167. PReason::kInterpreterResourceUnavailable},
  168. };
  169. return kLabelToReason;
  170. }
  171. // Returns the Reason corresponding to the string `reason`. Returns
  172. // UNKOWN_REASON if the string is not recognized.
  173. PrinterStatus::PrinterReason::Reason ToReason(base::StringPiece reason) {
  174. const auto& enum_map = GetLabelToReason();
  175. const auto& entry = enum_map.find(reason);
  176. return entry != enum_map.end() ? entry->second : PReason::kUnknownReason;
  177. }
  178. // Returns the Severity corresponding to `severity`. Returns UNKNOWN_SEVERITY
  179. // if the strin gis not recognized.
  180. PSeverity ToSeverity(base::StringPiece severity) {
  181. if (severity == kSeverityError)
  182. return PSeverity::kError;
  183. if (severity == kSeverityWarn)
  184. return PSeverity::kWarning;
  185. if (severity == kSeverityReport)
  186. return PSeverity::kReport;
  187. return PSeverity::kUnknownSeverity;
  188. }
  189. // Parses the `reason` string into a PrinterReason. Splits the string based on
  190. // the last '-' to determine severity. If a recognized severity is not
  191. // included, severity is assumed to be ERROR per RFC2911.
  192. PrinterStatus::PrinterReason ToPrinterReason(base::StringPiece reason) {
  193. PrinterStatus::PrinterReason parsed;
  194. if (reason == kNone) {
  195. parsed.reason = PReason::kNone;
  196. parsed.severity = PSeverity::kUnknownSeverity;
  197. return parsed;
  198. }
  199. size_t last_dash = reason.rfind('-');
  200. auto severity = PSeverity::kUnknownSeverity;
  201. if (last_dash != base::StringPiece::npos) {
  202. // try to parse the last part of the string as the severity.
  203. severity = ToSeverity(reason.substr(last_dash + 1));
  204. }
  205. if (severity == PSeverity::kUnknownSeverity) {
  206. // Severity is unknown. No severity in the reason.
  207. // Per spec, if there is no severity, severity is error.
  208. parsed.severity = PSeverity::kError;
  209. parsed.reason = ToReason(reason);
  210. } else {
  211. parsed.severity = severity;
  212. // reason is the beginning of the string
  213. parsed.reason = ToReason(reason.substr(0, last_dash));
  214. }
  215. return parsed;
  216. }
  217. // Populates `collection` with the collection of strings in `attr`.
  218. void ParseCollection(ipp_attribute_t* attr,
  219. std::vector<std::string>* collection) {
  220. int count = ippGetCount(attr);
  221. for (int i = 0; i < count; i++) {
  222. const char* const value = ippGetString(attr, i, nullptr);
  223. if (value) {
  224. collection->push_back(value);
  225. }
  226. }
  227. }
  228. // Parse a field for the CupsJob `job` from IPP attribute `attr` using the
  229. // attribute name `name`.
  230. void ParseField(ipp_attribute_t* attr, base::StringPiece name, CupsJob* job) {
  231. DCHECK(!name.empty());
  232. if (name == kJobId) {
  233. job->id = ippGetInteger(attr, 0);
  234. } else if (name == kJobImpressionsCompleted) {
  235. job->current_pages = ippGetInteger(attr, 0);
  236. } else if (name == kJobState) {
  237. job->state = ToJobState(attr);
  238. } else if (name == kJobStateReasons) {
  239. ParseCollection(attr, &(job->state_reasons));
  240. } else if (name == kJobStateMessage) {
  241. const char* message_string = ippGetString(attr, 0, nullptr);
  242. if (message_string) {
  243. job->state_message = message_string;
  244. }
  245. } else if (name == kTimeAtProcessing) {
  246. job->processing_started = ippGetInteger(attr, 0);
  247. }
  248. }
  249. // Returns a new CupsJob allocated in `jobs` with `printer_id` populated.
  250. CupsJob* NewJob(const std::string& printer_id, std::vector<CupsJob>* jobs) {
  251. jobs->emplace_back();
  252. CupsJob* job = &jobs->back();
  253. job->printer_id = printer_id;
  254. return job;
  255. }
  256. void ParseJobs(ipp_t* response,
  257. const std::string& printer_id,
  258. ipp_attribute_t* starting_attr,
  259. std::vector<CupsJob>* jobs) {
  260. // We know this is a non-empty job section. Start parsing fields for at least
  261. // one job.
  262. CupsJob* current_job = NewJob(printer_id, jobs);
  263. for (ipp_attribute_t* attr = starting_attr; attr != nullptr;
  264. attr = ippNextAttribute(response)) {
  265. const char* const attribute_name = ippGetName(attr);
  266. // Separators indicate a new job. Separators have empty names.
  267. if (!attribute_name || strlen(attribute_name) == 0) {
  268. current_job = NewJob(printer_id, jobs);
  269. continue;
  270. }
  271. // Continue to populate the job fileds.
  272. ParseField(attr, attribute_name, current_job);
  273. }
  274. }
  275. // Extracts PrinterInfo fields from `response` and populates `printer_info`.
  276. // Returns true if at least printer-make-and-model and ipp-versions-supported
  277. // were read.
  278. bool ParsePrinterInfo(ipp_t* response, PrinterInfo* printer_info) {
  279. for (ipp_attribute_t* attr = ippFirstAttribute(response); attr != nullptr;
  280. attr = ippNextAttribute(response)) {
  281. const char* const value = ippGetName(attr);
  282. if (!value) {
  283. continue;
  284. }
  285. base::StringPiece name(value);
  286. if (name == base::StringPiece(kPrinterMakeAndModel)) {
  287. int tag = ippGetValueTag(attr);
  288. if (tag != IPP_TAG_TEXT && tag != IPP_TAG_TEXTLANG) {
  289. LOG(WARNING) << "printer-make-and-model value tag is " << tag << ".";
  290. }
  291. const char* make_and_model_string = ippGetString(attr, 0, nullptr);
  292. if (make_and_model_string) {
  293. printer_info->make_and_model = make_and_model_string;
  294. }
  295. } else if (name == base::StringPiece(kIppVersionsSupported)) {
  296. std::vector<std::string> ipp_versions;
  297. ParseCollection(attr, &ipp_versions);
  298. for (const std::string& version : ipp_versions) {
  299. base::Version major_minor(version);
  300. if (major_minor.IsValid()) {
  301. printer_info->ipp_versions.push_back(major_minor);
  302. }
  303. }
  304. } else if (name == base::StringPiece(kIppFeaturesSupported)) {
  305. std::vector<std::string> features;
  306. ParseCollection(attr, &features);
  307. printer_info->ipp_everywhere = base::Contains(features, kIppEverywhere);
  308. } else if (name == base::StringPiece(kDocumentFormatSupported)) {
  309. ParseCollection(attr, &printer_info->document_formats);
  310. }
  311. }
  312. if (printer_info->ipp_versions.empty()) {
  313. // ipp-versions-supported is missing from the response. This is IPP 1.0.
  314. printer_info->ipp_versions.push_back(base::Version({1, 0}));
  315. }
  316. // All IPP versions require make and model to be populated so we use it to
  317. // verify that we parsed the response.
  318. return !printer_info->make_and_model.empty();
  319. }
  320. // Returns true if `status` represents a complete failure in the IPP request.
  321. bool StatusError(ipp_status_e status) {
  322. return status != IPP_STATUS_OK &&
  323. status != IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED;
  324. }
  325. } // namespace
  326. CupsJob::CupsJob() = default;
  327. CupsJob::CupsJob(const CupsJob& other) = default;
  328. CupsJob::~CupsJob() = default;
  329. bool CupsJob::ContainsStateReason(CupsJob::JobStateReason reason) const {
  330. return base::Contains(state_reasons, ToJobStateReasonString(reason));
  331. }
  332. PrinterInfo::PrinterInfo() = default;
  333. PrinterInfo::~PrinterInfo() = default;
  334. const base::StringPiece ToJobStateReasonString(
  335. CupsJob::JobStateReason state_reason) {
  336. switch (state_reason) {
  337. case CupsJob::JobStateReason::kJobCompletedWithErrors:
  338. return kJobCompletedWithErrors;
  339. case CupsJob::JobStateReason::kCupsHeldForAuthentication:
  340. return kCupsHeldForAuthentication;
  341. }
  342. return "";
  343. }
  344. std::string PrinterUriFromName(const std::string& id) {
  345. return base::StringPrintf("ipp://localhost/printers/%s", id.c_str());
  346. }
  347. void ParseJobsResponse(ipp_t* response,
  348. const std::string& printer_id,
  349. std::vector<CupsJob>* jobs) {
  350. // Advance the position in the response to the jobs section.
  351. ipp_attribute_t* attr = ippFirstAttribute(response);
  352. while (attr != nullptr && ippGetGroupTag(attr) != IPP_TAG_JOB) {
  353. attr = ippNextAttribute(response);
  354. }
  355. if (attr != nullptr) {
  356. ParseJobs(response, printer_id, attr, jobs);
  357. }
  358. }
  359. // Returns an IPP response for a Get-Printer-Attributes request to `http`. For
  360. // print servers, `printer_uri` is used as the printer-uri value.
  361. // `resource_path` specifies the path portion of the server URI.
  362. // `num_attributes` is the number of attributes in `attributes` which should be
  363. // a list of IPP attributes. `status` is updated with status code for the
  364. // request. A successful request will have the `status` IPP_STATUS_OK.
  365. ScopedIppPtr GetPrinterAttributes(http_t* http,
  366. const std::string& printer_uri,
  367. const std::string& resource_path,
  368. int num_attributes,
  369. const char* const* attributes,
  370. ipp_status_t* status) {
  371. DCHECK(http);
  372. // CUPS expects a leading slash for resource names. Add one if it's missing.
  373. std::string rp = !resource_path.empty() && resource_path.front() == '/'
  374. ? resource_path
  375. : "/" + resource_path;
  376. auto request = WrapIpp(ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES));
  377. // We support IPP up to 2.2 but are compatible down to v1.1.
  378. ippSetVersion(request.get(), 1, 1);
  379. ippAddString(request.get(), IPP_TAG_OPERATION, IPP_TAG_URI, kPrinterUri,
  380. nullptr, printer_uri.c_str());
  381. ippAddStrings(request.get(), IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
  382. kRequestedAttributes, num_attributes, nullptr, attributes);
  383. DCHECK_EQ(ippValidateAttributes(request.get()), 1);
  384. base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
  385. base::BlockingType::WILL_BLOCK);
  386. auto response = WrapIpp(cupsDoRequest(http, request.release(), rp.c_str()));
  387. *status = ippGetStatusCode(response.get());
  388. return response;
  389. }
  390. void ParsePrinterStatus(ipp_t* response, PrinterStatus* printer_status) {
  391. *printer_status = PrinterStatus();
  392. for (ipp_attribute_t* attr = ippFirstAttribute(response); attr != nullptr;
  393. attr = ippNextAttribute(response)) {
  394. const char* const value = ippGetName(attr);
  395. if (!value) {
  396. continue;
  397. }
  398. base::StringPiece name(value);
  399. if (name == kPrinterState) {
  400. DCHECK_EQ(IPP_TAG_ENUM, ippGetValueTag(attr));
  401. printer_status->state = static_cast<ipp_pstate_t>(ippGetInteger(attr, 0));
  402. } else if (name == kPrinterStateReasons) {
  403. std::vector<std::string> reason_strings;
  404. ParseCollection(attr, &reason_strings);
  405. for (const std::string& reason : reason_strings) {
  406. printer_status->reasons.push_back(ToPrinterReason(reason));
  407. }
  408. } else if (name == kPrinterStateMessage) {
  409. const char* message_string = ippGetString(attr, 0, nullptr);
  410. if (message_string) {
  411. printer_status->message = message_string;
  412. }
  413. }
  414. }
  415. }
  416. PrinterQueryResult GetPrinterInfo(const std::string& address,
  417. const int port,
  418. const std::string& resource,
  419. bool encrypted,
  420. PrinterInfo* printer_info,
  421. PrinterStatus* printer_status) {
  422. DCHECK(printer_info);
  423. DCHECK(printer_status);
  424. // Lookup the printer IP address.
  425. http_addrlist_t* addr_list = httpAddrGetList(
  426. address.c_str(), AF_INET, base::NumberToString(port).c_str());
  427. if (!addr_list) {
  428. LOG(WARNING) << "Unable to resolve IP address from hostname";
  429. return PrinterQueryResult::kHostnameResolution;
  430. }
  431. ScopedHttpPtr http = ScopedHttpPtr(httpConnect2(
  432. address.c_str(), port, addr_list, AF_INET,
  433. encrypted ? HTTP_ENCRYPTION_ALWAYS : HTTP_ENCRYPTION_IF_REQUESTED, 0,
  434. kHttpConnectTimeoutMs, nullptr));
  435. if (!http) {
  436. LOG(WARNING) << "Could not connect to host";
  437. return PrinterQueryResult::kUnreachable;
  438. }
  439. // TODO(crbug.com/821497): Use a library to canonicalize the URL.
  440. size_t first_non_slash = resource.find_first_not_of('/');
  441. const std::string path = (first_non_slash == std::string::npos)
  442. ? ""
  443. : resource.substr(first_non_slash);
  444. std::string printer_uri =
  445. base::StringPrintf("%s://%s:%d/%s", encrypted ? kIppsScheme : kIppScheme,
  446. address.c_str(), port, path.c_str());
  447. ipp_status_t status;
  448. ScopedIppPtr response = GetPrinterAttributes(
  449. http.get(), printer_uri, resource, kPrinterInfoAndStatus.size(),
  450. kPrinterInfoAndStatus.data(), &status);
  451. if (StatusError(status) || response.get() == nullptr) {
  452. LOG(WARNING) << "Get attributes failure: "
  453. << base::StringPrintf("0x%04x", status);
  454. return PrinterQueryResult::kUnknownFailure;
  455. }
  456. ParsePrinterStatus(response.get(), printer_status);
  457. if (ParsePrinterInfo(response.get(), printer_info)) {
  458. return PrinterQueryResult::kSuccess;
  459. }
  460. return PrinterQueryResult::kUnknownFailure;
  461. }
  462. bool GetPrinterStatus(http_t* http,
  463. const std::string& printer_id,
  464. PrinterStatus* printer_status) {
  465. ipp_status_t status;
  466. const std::string printer_uri = PrinterUriFromName(printer_id);
  467. ScopedIppPtr response =
  468. GetPrinterAttributes(http, printer_uri, "/", kPrinterAttributes.size(),
  469. kPrinterAttributes.data(), &status);
  470. if (status != IPP_STATUS_OK)
  471. return false;
  472. ParsePrinterStatus(response.get(), printer_status);
  473. return true;
  474. }
  475. bool GetCupsJobs(http_t* http,
  476. const std::string& printer_id,
  477. int limit,
  478. JobCompletionState which,
  479. std::vector<CupsJob>* jobs) {
  480. DCHECK(http);
  481. auto request = WrapIpp(ippNewRequest(IPP_OP_GET_JOBS));
  482. const std::string printer_uri = PrinterUriFromName(printer_id);
  483. ippAddString(request.get(), IPP_TAG_OPERATION, IPP_TAG_URI, kPrinterUri,
  484. nullptr, printer_uri.c_str());
  485. ippAddInteger(request.get(), IPP_TAG_OPERATION, IPP_TAG_INTEGER, kLimit,
  486. limit);
  487. std::vector<const char*> job_attributes = {
  488. kJobUri, kJobId, kJobState,
  489. kJobStateReasons, kJobStateMessage, kJobImpressionsCompleted,
  490. kTimeAtProcessing};
  491. ippAddStrings(request.get(), IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
  492. kRequestedAttributes, job_attributes.size(), nullptr,
  493. job_attributes.data());
  494. ippAddString(request.get(), IPP_TAG_OPERATION, IPP_TAG_KEYWORD, kWhichJobs,
  495. nullptr, which == COMPLETED ? kCompleted : kNotCompleted);
  496. if (ippValidateAttributes(request.get()) != 1) {
  497. LOG(WARNING) << "Could not validate ipp request: " << cupsLastErrorString();
  498. return false;
  499. }
  500. base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
  501. base::BlockingType::MAY_BLOCK);
  502. // cupsDoRequest will delete the request.
  503. auto response = WrapIpp(cupsDoRequest(http, request.release(), "/"));
  504. ipp_status_t status = ippGetStatusCode(response.get());
  505. if (status != IPP_STATUS_OK) {
  506. LOG(WARNING) << "IPP Error: " << cupsLastErrorString();
  507. return false;
  508. }
  509. ParseJobsResponse(response.get(), printer_id, jobs);
  510. return true;
  511. }
  512. } // namespace printing