Skip to content

Commit

Permalink
Implement a new backend for modern CUPS implementations
Browse files Browse the repository at this point in the history
As of CUPS 1.6, CUPS has moved away from PPD attributes to IPP attributes.  This new backend uses the IPP APIs and will be better suited to use with CUPS going forward.  Until we can bump our minumum CUPS version to 1.7, the only client will be Chrome OS where the version is 2.1.3.

BUG=607668

TEST=With --enable-native-cups enabled, print dialog will show available CUPS printers if any are configured.

Review-Url: https://codereview.chromium.org/2105463002
Cr-Commit-Position: refs/heads/master@{#407011}
  • Loading branch information
skau authored and Commit bot committed Jul 22, 2016
1 parent dd157e1 commit b779ce6
Show file tree
Hide file tree
Showing 14 changed files with 1,431 additions and 20 deletions.
34 changes: 27 additions & 7 deletions printing/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -169,12 +169,27 @@ component("printing") {
# of the print backend and enables a custom implementation instead.
defines += [ "PRINT_BACKEND_AVAILABLE" ]

sources += [
"backend/cups_helper.cc",
"backend/cups_helper.h",
"backend/print_backend_cups.cc",
"backend/print_backend_cups.h",
]
if (is_chromeos) {
sources += [
"backend/cups_connection.cc",
"backend/cups_connection.h",
"backend/cups_deleters.cc",
"backend/cups_deleters.h",
"backend/cups_ipp_util.cc",
"backend/cups_ipp_util.h",
"backend/cups_printer.cc",
"backend/cups_printer.h",
"backend/print_backend_cups_ipp.cc",
"backend/print_backend_cups_ipp.h",
]
} else {
sources += [
"backend/cups_helper.cc",
"backend/cups_helper.h",
"backend/print_backend_cups.cc",
"backend/print_backend_cups.h",
]
}
}

if (is_chromeos) {
Expand Down Expand Up @@ -240,7 +255,12 @@ test("printing_unittests") {

if (use_cups) {
configs += [ ":cups" ]
sources += [ "backend/cups_helper_unittest.cc" ]

if (is_chromeos) {
sources += [ "backend/cups_ipp_util_unittest.cc" ]
} else {
sources += [ "backend/cups_helper_unittest.cc" ]
}
}
}

Expand Down
137 changes: 137 additions & 0 deletions printing/backend/cups_connection.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "printing/backend/cups_connection.h"

#include <string>
#include <utility>

#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/strings/stringprintf.h"

namespace printing {

namespace {

const int kTimeoutMs = 3000;

class DestinationEnumerator {
public:
DestinationEnumerator() {}

static int cups_callback(void* user_data, unsigned flags, cups_dest_t* dest) {
cups_dest_t* copied_dest;
cupsCopyDest(dest, 0, &copied_dest);
reinterpret_cast<DestinationEnumerator*>(user_data)->store_dest(
copied_dest);

// keep going
return 1;
}

void store_dest(cups_dest_t* dest) { dests_.emplace_back(dest); }

// Returns the collected destinations.
std::vector<std::unique_ptr<cups_dest_t, DestinationDeleter>>& get_dests() {
return dests_;
}

private:
std::vector<std::unique_ptr<cups_dest_t, DestinationDeleter>> dests_;

DISALLOW_COPY_AND_ASSIGN(DestinationEnumerator);
};

} // namespace

CupsConnection::CupsConnection(const GURL& print_server_url,
http_encryption_t encryption,
bool blocking)
: print_server_url_(print_server_url),
cups_encryption_(encryption),
blocking_(blocking),
cups_http_(nullptr) {}

CupsConnection::CupsConnection(CupsConnection&& connection)
: print_server_url_(connection.print_server_url_),
cups_encryption_(connection.cups_encryption_),
blocking_(connection.blocking_),
cups_http_(std::move(connection.cups_http_)) {}

CupsConnection::~CupsConnection() {}

bool CupsConnection::Connect() {
if (cups_http_)
return true; // we're already connected

std::string host;
int port;

if (!print_server_url_.is_empty()) {
host = print_server_url_.host();
port = print_server_url_.IntPort();
} else {
host = cupsServer();
port = ippPort();
}

cups_http_.reset(httpConnect2(host.c_str(), port, nullptr, AF_UNSPEC,
cups_encryption_, blocking_ ? 1 : 0, kTimeoutMs,
nullptr));
return !!cups_http_;
}

std::vector<CupsPrinter> CupsConnection::GetDests() {
if (!Connect()) {
LOG(WARNING) << "CUPS connection failed";
return std::vector<CupsPrinter>();
}

DestinationEnumerator enumerator;
int success =
cupsEnumDests(CUPS_DEST_FLAGS_NONE, kTimeoutMs,
nullptr, // no cancel signal
0, // all the printers
CUPS_PRINTER_SCANNER, // except the scanners
&DestinationEnumerator::cups_callback, &enumerator);

if (!success) {
LOG(WARNING) << "Enumerating printers failed";
return std::vector<CupsPrinter>();
}

auto dests = std::move(enumerator.get_dests());
std::vector<CupsPrinter> printers;
for (auto& dest : dests) {
printers.emplace_back(cups_http_.get(), std::move(dest), nullptr);
}

return printers;
}

std::unique_ptr<CupsPrinter> CupsConnection::GetPrinter(
const std::string& name) {
if (!Connect())
return nullptr;

cups_dest_t* dest = cupsGetNamedDest(cups_http_.get(), name.c_str(), nullptr);
if (!dest)
return nullptr;

cups_dinfo_t* info = cupsCopyDestInfo(cups_http_.get(), dest);
return base::MakeUnique<CupsPrinter>(
cups_http_.get(), std::unique_ptr<cups_dest_t, DestinationDeleter>(dest),
std::unique_ptr<cups_dinfo_t, DestInfoDeleter>(info));
}

std::string CupsConnection::server_name() const {
return print_server_url_.host();
}

int CupsConnection::last_error() const {
return cupsLastError();
}

} // namespace printing
59 changes: 59 additions & 0 deletions printing/backend/cups_connection.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef PRINTING_BACKEND_CUPS_CONNECTION_H_
#define PRINTING_BACKEND_CUPS_CONNECTION_H_

#include <cups/cups.h>

#include <memory>
#include <string>
#include <vector>

#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "printing/backend/cups_deleters.h"
#include "printing/backend/cups_printer.h"
#include "printing/printing_export.h"
#include "url/gurl.h"

namespace printing {

// Represents a connection to a CUPS server.
class PRINTING_EXPORT CupsConnection {
public:
CupsConnection(const GURL& print_server_url,
http_encryption_t encryption,
bool blocking);

CupsConnection(CupsConnection&& connection);

~CupsConnection();

// Returns a vector of all the printers configure on the CUPS server.
std::vector<CupsPrinter> GetDests();

// Returns a printer for |printer_name| from the connected server.
std::unique_ptr<CupsPrinter> GetPrinter(const std::string& printer_name);

std::string server_name() const;

int last_error() const;

private:
// lazily initialize http connection
bool Connect();

GURL print_server_url_;
http_encryption_t cups_encryption_;
bool blocking_;

std::unique_ptr<http_t, HttpDeleter> cups_http_;

DISALLOW_COPY_AND_ASSIGN(CupsConnection);
};

} // namespace printing

#endif // PRINTING_BACKEND_CUPS_CONNECTION_H_
21 changes: 21 additions & 0 deletions printing/backend/cups_deleters.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "printing/backend/cups_deleters.h"

namespace printing {

void HttpDeleter::operator()(http_t* http) const {
httpClose(http);
}

void DestinationDeleter::operator()(cups_dest_t* dest) const {
cupsFreeDests(1, dest);
}

void DestInfoDeleter::operator()(cups_dinfo_t* info) const {
cupsFreeDestInfo(info);
}

} // namespace printing
29 changes: 29 additions & 0 deletions printing/backend/cups_deleters.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef PRINTING_BACKEND_CUPS_DELETERS_H_
#define PRINTING_BACKEND_CUPS_DELETERS_H_

#include <cups/cups.h>

namespace printing {

struct HttpDeleter {
public:
void operator()(http_t* http) const;
};

struct DestinationDeleter {
public:
void operator()(cups_dest_t* dest) const;
};

struct DestInfoDeleter {
public:
void operator()(cups_dinfo_t* info) const;
};

} // namespace printing

#endif // PRINTING_BACKEND_CUPS_DELETERS_H_
Loading

0 comments on commit b779ce6

Please sign in to comment.