cups_connection.cc 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. // Copyright 2016 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_connection.h"
  5. #include <map>
  6. #include <memory>
  7. #include <set>
  8. #include <string>
  9. #include <utility>
  10. #include "base/logging.h"
  11. #include "build/build_config.h"
  12. #include "printing/backend/cups_helper.h"
  13. #include "printing/backend/cups_jobs.h"
  14. #if BUILDFLAG(IS_CHROMEOS)
  15. #include "printing/backend/cups_connection_pool.h"
  16. #endif
  17. namespace printing {
  18. namespace {
  19. // The number of jobs we'll retrieve for a queue. We expect a user to queue at
  20. // most 10 jobs per printer. If they queue more, they won't receive updates for
  21. // the 11th job until one finishes.
  22. constexpr int kProcessingJobsLimit = 10;
  23. // The number of completed jobs that are retrieved. We only need one update for
  24. // a completed job to confirm its final status. We could retrieve one but we
  25. // retrieve the last 3 in case that many finished between queries.
  26. constexpr int kCompletedJobsLimit = 3;
  27. class DestinationEnumerator {
  28. public:
  29. DestinationEnumerator() {}
  30. DestinationEnumerator(const DestinationEnumerator&) = delete;
  31. DestinationEnumerator& operator=(const DestinationEnumerator&) = delete;
  32. static int cups_callback(void* user_data, unsigned flags, cups_dest_t* dest) {
  33. cups_dest_t* copied_dest;
  34. cupsCopyDest(dest, 0, &copied_dest);
  35. reinterpret_cast<DestinationEnumerator*>(user_data)->store_dest(
  36. copied_dest);
  37. // keep going
  38. return 1;
  39. }
  40. void store_dest(cups_dest_t* dest) { dests_.emplace_back(dest); }
  41. // Returns the collected destinations.
  42. std::vector<ScopedDestination>& get_dests() { return dests_; }
  43. private:
  44. std::vector<ScopedDestination> dests_;
  45. };
  46. } // namespace
  47. QueueStatus::QueueStatus() = default;
  48. QueueStatus::QueueStatus(const QueueStatus& other) = default;
  49. QueueStatus::~QueueStatus() = default;
  50. class CupsConnectionImpl : public CupsConnection {
  51. public:
  52. CupsConnectionImpl(const GURL& print_server_url,
  53. http_encryption_t encryption,
  54. bool blocking)
  55. : print_server_url_(print_server_url),
  56. cups_encryption_(encryption),
  57. blocking_(false),
  58. cups_http_(nullptr) {}
  59. CupsConnectionImpl(CupsConnectionImpl&& connection)
  60. : print_server_url_(connection.print_server_url_),
  61. cups_encryption_(connection.cups_encryption_),
  62. blocking_(connection.blocking_),
  63. cups_http_(std::move(connection.cups_http_)) {}
  64. ~CupsConnectionImpl() override {
  65. #if BUILDFLAG(IS_CHROMEOS)
  66. if (cups_http_) {
  67. // If there is a connection pool, then the connection we have came from
  68. // it. We must add the connection back to the pool for possible reuse
  69. // rather than letting it be automatically closed, since we can never get
  70. // it back after closing it.
  71. CupsConnectionPool* connection_pool = CupsConnectionPool::GetInstance();
  72. if (connection_pool)
  73. connection_pool->AddConnection(std::move(cups_http_));
  74. }
  75. #endif // BUILDFLAG(IS_CHROMEOS)
  76. }
  77. bool GetDests(std::vector<std::unique_ptr<CupsPrinter>>& printers) override {
  78. printers.clear();
  79. if (!Connect()) {
  80. LOG(WARNING) << "CUPS connection failed: ";
  81. return false;
  82. }
  83. DestinationEnumerator enumerator;
  84. const int success =
  85. cupsEnumDests(CUPS_DEST_FLAGS_NONE, kCupsTimeoutMs,
  86. /*cancel=*/nullptr,
  87. /*type=*/CUPS_PRINTER_LOCAL, kDestinationsFilterMask,
  88. &DestinationEnumerator::cups_callback, &enumerator);
  89. if (!success) {
  90. LOG(WARNING) << "Enumerating printers failed";
  91. return false;
  92. }
  93. auto dests = std::move(enumerator.get_dests());
  94. for (auto& dest : dests) {
  95. printers.push_back(
  96. CupsPrinter::Create(cups_http_.get(), std::move(dest)));
  97. }
  98. return true;
  99. }
  100. std::unique_ptr<CupsPrinter> GetPrinter(const std::string& name) override {
  101. if (!Connect())
  102. return nullptr;
  103. cups_dest_t* dest =
  104. cupsGetNamedDest(cups_http_.get(), name.c_str(), nullptr);
  105. if (!dest)
  106. return nullptr;
  107. return CupsPrinter::Create(cups_http_.get(), ScopedDestination(dest));
  108. }
  109. bool GetJobs(const std::vector<std::string>& printer_ids,
  110. std::vector<QueueStatus>* queues) override {
  111. DCHECK(queues);
  112. if (!Connect()) {
  113. LOG(ERROR) << "Could not establish connection to CUPS";
  114. return false;
  115. }
  116. std::vector<QueueStatus> temp_queues;
  117. for (const std::string& id : printer_ids) {
  118. temp_queues.emplace_back();
  119. QueueStatus* queue_status = &temp_queues.back();
  120. if (!printing::GetPrinterStatus(cups_http_.get(), id,
  121. &queue_status->printer_status)) {
  122. LOG(WARNING) << "Could not retrieve printer status for " << id;
  123. return false;
  124. }
  125. if (!GetCupsJobs(cups_http_.get(), id, kCompletedJobsLimit, COMPLETED,
  126. &queue_status->jobs)) {
  127. LOG(WARNING) << "Could not get completed jobs for " << id;
  128. return false;
  129. }
  130. if (!GetCupsJobs(cups_http_.get(), id, kProcessingJobsLimit, PROCESSING,
  131. &queue_status->jobs)) {
  132. LOG(WARNING) << "Could not get in progress jobs for " << id;
  133. return false;
  134. }
  135. }
  136. queues->insert(queues->end(), temp_queues.begin(), temp_queues.end());
  137. return true;
  138. }
  139. bool GetPrinterStatus(const std::string& printer_id,
  140. PrinterStatus* printer_status) override {
  141. if (!Connect()) {
  142. LOG(ERROR) << "Could not establish connection to CUPS";
  143. return false;
  144. }
  145. return printing::GetPrinterStatus(cups_http_.get(), printer_id,
  146. printer_status);
  147. }
  148. std::string server_name() const override { return print_server_url_.host(); }
  149. int last_error() const override { return cupsLastError(); }
  150. std::string last_error_message() const override {
  151. return cupsLastErrorString();
  152. }
  153. private:
  154. // lazily initialize http connection
  155. bool Connect() {
  156. if (cups_http_)
  157. return true; // we're already connected
  158. #if BUILDFLAG(IS_CHROMEOS)
  159. // If a connection pool has been created for this process then we must
  160. // allocate a connection from that, and not try to create a new one now.
  161. CupsConnectionPool* connection_pool = CupsConnectionPool::GetInstance();
  162. if (connection_pool) {
  163. cups_http_ = connection_pool->TakeConnection();
  164. if (!cups_http_)
  165. LOG(WARNING) << "No available connections in the CUPS connection pool";
  166. return !!cups_http_;
  167. }
  168. #endif // BUILDFLAG(IS_CHROMEOS)
  169. std::string host;
  170. int port;
  171. if (!print_server_url_.is_empty()) {
  172. host = print_server_url_.host();
  173. port = print_server_url_.IntPort();
  174. } else {
  175. host = cupsServer();
  176. port = ippPort();
  177. }
  178. cups_http_.reset(httpConnect2(host.c_str(), port, nullptr, AF_UNSPEC,
  179. cups_encryption_, blocking_ ? 1 : 0,
  180. kCupsTimeoutMs, nullptr));
  181. return !!cups_http_;
  182. }
  183. GURL print_server_url_;
  184. http_encryption_t cups_encryption_;
  185. bool blocking_;
  186. ScopedHttpPtr cups_http_;
  187. };
  188. std::unique_ptr<CupsConnection> CupsConnection::Create(
  189. const GURL& print_server_url,
  190. http_encryption_t encryption,
  191. bool blocking) {
  192. return std::make_unique<CupsConnectionImpl>(print_server_url, encryption,
  193. blocking);
  194. }
  195. } // namespace printing