blob: ee27e5e3c586f01443cf4a71609aca63786a11c0 [file] [log] [blame]
// 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 "chromeos/printing/printer_configuration.h"
#include <string>
#include "base/guid.h"
#include "base/optional.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "net/base/ip_endpoint.h"
#include "url/third_party/mozilla/url_parse.h"
#include "url/url_constants.h"
#include "chromeos/printing/printing_constants.h"
namespace chromeos {
// Returns true if the scheme is both valid and non-empty.
bool IsSchemeValid(const url::Parsed& parsed) {
return parsed.scheme.is_valid() && parsed.scheme.is_nonempty();
}
// Returns true if |parsed| contains a valid port. A valid port is one that
// either contains a valid value or is completely missing.
bool IsPortValid(const url::Parsed& parsed) {
// A length of -1 indicates that the port is missing.
return parsed.port.len == -1 ||
(parsed.port.is_valid() && parsed.port.is_nonempty());
}
// Returns |printer_uri| broken into components if it represents a valid uri. A
// valid uri contains a scheme, host, and a valid or missing port number.
// Optionally, the uri contains a path.
base::Optional<UriComponents> ParseUri(const std::string& printer_uri) {
const char* uri_ptr = printer_uri.c_str();
url::Parsed parsed;
url::ParseStandardURL(uri_ptr, printer_uri.length(), &parsed);
if (!IsSchemeValid(parsed) || !parsed.host.is_valid() ||
!IsPortValid(parsed)) {
LOG(WARNING) << "Could not parse printer uri";
return {};
}
base::StringPiece scheme(&uri_ptr[parsed.scheme.begin], parsed.scheme.len);
base::StringPiece host(&uri_ptr[parsed.host.begin], parsed.host.len);
base::StringPiece path =
parsed.path.is_valid()
? base::StringPiece(&uri_ptr[parsed.path.begin], parsed.path.len)
: "";
int port = ParsePort(uri_ptr, parsed.port);
if (port == url::SpecialPort::PORT_INVALID) {
LOG(WARNING) << "Port is invalid";
return {};
}
// Port not specified.
if (port == url::SpecialPort::PORT_UNSPECIFIED) {
if (scheme == kIppScheme) {
port = kIppPort;
} else if (scheme == kIppsScheme) {
port = kIppsPort;
}
}
bool encrypted = scheme != kIppScheme;
return base::Optional<UriComponents>(base::in_place, encrypted,
scheme.as_string(), host.as_string(),
port, path.as_string());
}
namespace {
// Returns the index of the first character representing the hostname in |uri|.
// Returns npos if the start of the hostname is not found.
//
// We should use GURL to do this except that uri could start with ipp:// which
// is not a standard url scheme (according to GURL).
size_t HostnameStart(base::StringPiece uri) {
size_t scheme_separator_start = uri.find(url::kStandardSchemeSeparator);
if (scheme_separator_start == base::StringPiece::npos) {
return base::StringPiece::npos;
}
return scheme_separator_start + strlen(url::kStandardSchemeSeparator);
}
base::StringPiece HostAndPort(base::StringPiece uri) {
size_t hostname_start = HostnameStart(uri);
if (hostname_start == base::StringPiece::npos) {
return "";
}
size_t hostname_end = uri.find("/", hostname_start);
if (hostname_end == base::StringPiece::npos) {
// No trailing slash. Use end of string.
hostname_end = uri.size();
}
CHECK_GT(hostname_end, hostname_start);
return uri.substr(hostname_start, hostname_end - hostname_start);
}
} // namespace
Printer::Printer() : source_(SRC_USER_PREFS) {
id_ = base::GenerateGUID();
}
Printer::Printer(const std::string& id) : id_(id), source_(SRC_USER_PREFS) {
if (id_.empty())
id_ = base::GenerateGUID();
}
Printer::Printer(const Printer& other) = default;
Printer& Printer::operator=(const Printer& other) = default;
Printer::~Printer() = default;
bool Printer::IsIppEverywhere() const {
return ppd_reference_.autoconf;
}
bool Printer::RequiresIpResolution() const {
return effective_uri_.empty() &&
base::StartsWith(id_, "zeroconf-", base::CompareCase::SENSITIVE);
}
net::HostPortPair Printer::GetHostAndPort() const {
if (uri_.empty()) {
return net::HostPortPair();
}
return net::HostPortPair::FromString(HostAndPort(uri_).as_string());
}
std::string Printer::ReplaceHostAndPort(const net::IPEndPoint& ip) const {
if (uri_.empty()) {
return "";
}
size_t hostname_start = HostnameStart(uri_);
if (hostname_start == base::StringPiece::npos) {
return "";
}
size_t host_port_len = HostAndPort(uri_).length();
return base::JoinString({uri_.substr(0, hostname_start), ip.ToString(),
uri_.substr(hostname_start + host_port_len)},
"");
}
Printer::PrinterProtocol Printer::GetProtocol() const {
const base::StringPiece uri(uri_);
if (uri.starts_with("usb:"))
return PrinterProtocol::kUsb;
if (uri.starts_with("ipp:"))
return PrinterProtocol::kIpp;
if (uri.starts_with("ipps:"))
return PrinterProtocol::kIpps;
if (uri.starts_with("http:"))
return PrinterProtocol::kHttp;
if (uri.starts_with("https:"))
return PrinterProtocol::kHttps;
if (uri.starts_with("socket:"))
return PrinterProtocol::kSocket;
if (uri.starts_with("lpd:"))
return PrinterProtocol::kLpd;
if (uri.starts_with("ippusb:"))
return PrinterProtocol::kIppUsb;
return PrinterProtocol::kUnknown;
}
bool Printer::HasNetworkProtocol() const {
Printer::PrinterProtocol current_protocol = GetProtocol();
switch (current_protocol) {
case PrinterProtocol::kIpp:
case PrinterProtocol::kIpps:
case PrinterProtocol::kHttp:
case PrinterProtocol::kHttps:
case PrinterProtocol::kSocket:
case PrinterProtocol::kLpd:
return true;
default:
return false;
}
}
std::string Printer::UriForCups() const {
if (!effective_uri_.empty()) {
return effective_uri_;
} else {
return uri_;
}
}
base::Optional<UriComponents> Printer::GetUriComponents() const {
return chromeos::ParseUri(uri_);
}
bool Printer::PpdReference::operator==(
const Printer::PpdReference& other) const {
return user_supplied_ppd_url == other.user_supplied_ppd_url &&
effective_make_and_model == other.effective_make_and_model &&
autoconf == other.autoconf;
}
} // namespace chromeos