| // 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 |