Chromium Code Reviews| Index: chrome/browser/chromeos/printing/usb_util.cc |
| diff --git a/chrome/browser/chromeos/printing/usb_util.cc b/chrome/browser/chromeos/printing/usb_util.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c8c3a4fe801715543fa1204785087001ec0ba5be |
| --- /dev/null |
| +++ b/chrome/browser/chromeos/printing/usb_util.cc |
| @@ -0,0 +1,106 @@ |
| +// Copyright 2017 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 "chrome/browser/chromeos/printing/usb_util.h" |
| + |
| +#include <ctype.h> |
| +#include <stdint.h> |
| +#include <vector> |
| + |
| +#include "device/usb/usb_device_filter.h" |
| +#include "ui/base/l10n/l10n_util.h" |
| + |
| +namespace chromeos { |
| +namespace { |
| + |
| +// Base class used for printer USB interfaces |
| +// (https://www.usb.org/developers/defined_class). |
| +constexpr uint8_t kPrinterInterfaceClass = 7; |
| + |
| +// Escape URI strings the same way cups does it, so we end up with a URI cups |
| +// recognizes. Cups hex-encodes '%', ' ', and anything with the MSB set, and |
|
skau
2017/03/11 00:00:34
Out of curiosity, how'd you find out it encodes ch
Carlson
2017/03/11 01:19:17
By inspecting the cups source code. Ultimately it
skau
2017/03/13 21:56:25
Thanks. It looks like they allow any ASCII charac
Carlson
2017/03/15 00:31:15
I agree that the escaping choice is...weird.
Upda
|
| +// lets everything else through unchanged. |
| +std::string CupsURIEscape(const std::string& in) { |
| + static const char kHexDigits[] = "0123456789ABCDEF"; |
| + std::vector<char> buf; |
| + buf.reserve(in.size()); |
| + for (char c : in) { |
| + if (c == ' ' || c == '%' || (c & 0x80)) { |
| + buf.push_back('%'); |
| + buf.push_back(kHexDigits[(c >> 4) & 0xf]); |
| + buf.push_back(kHexDigits[c & 0xf]); |
| + } else { |
| + buf.push_back(c); |
| + } |
| + } |
| + return std::string(buf.data(), buf.size()); |
| +} |
| + |
| +} // namespace |
| + |
| +bool UsbDeviceIsPrinter(scoped_refptr<device::UsbDevice> usb_device) { |
| + device::UsbDeviceFilter printer_filter; |
| + printer_filter.interface_class = kPrinterInterfaceClass; |
| + return printer_filter.Matches(usb_device); |
| +} |
| + |
| +std::string UsbPrinterDeviceDetailsAsString(const device::UsbDevice& device) { |
| + return base::StringPrintf( |
| + " guid: %s\n" |
| + " usb version: %d\n" |
| + " device class: %d\n" |
| + " device subclass: %d\n" |
| + " device protocol: %d\n" |
| + " vendor id: %04x\n" |
| + " product id: %04x\n" |
| + " device version: %d\n" |
| + " manufacturer string: %s\n" |
| + " product string: %s\n" |
| + " serial number: %s", |
| + device.guid().c_str(), device.usb_version(), device.device_class(), |
| + device.device_subclass(), device.device_protocol(), device.vendor_id(), |
| + device.product_id(), device.device_version(), |
| + base::UTF16ToUTF8(device.manufacturer_string()).c_str(), |
| + base::UTF16ToUTF8(device.product_string()).c_str(), |
| + base::UTF16ToUTF8(device.serial_number()).c_str()); |
| +} |
| + |
| +// Attempt to gather all the information we need to work with this printer by |
| +// querying the USB device. This should only be called using devices we believe |
| +// are printers, not arbitrary USB devices, as we may get weird partial results |
| +// from arbitrary devices. |
| +// |
| +bool UsbDeviceToPrinter(const device::UsbDevice& device, |
| + chromeos::Printer* printer) { |
| + // Preflight all required fields and log errors if we find something wrong. |
| + if (device.vendor_id() == 0 || device.product_id() == 0 || |
| + device.manufacturer_string().empty() || device.product_string().empty()) { |
| + LOG(ERROR) << "Failed to convert USB device to printer. Fields were:\n" |
| + << UsbPrinterDeviceDetailsAsString(device); |
| + return false; |
| + } |
| + |
| + printer->set_manufacturer(base::UTF16ToUTF8(device.manufacturer_string())); |
| + printer->set_model(base::UTF16ToUTF8(device.product_string())); |
| + printer->set_display_name(base::StringPrintf("%s %s (USB)", |
| + printer->manufacturer().c_str(), |
| + printer->model().c_str())); |
| + printer->set_description(printer->display_name()); |
| + |
| + // Note that serial may, for some devices, be empty or bogus (all zeros, non |
| + // unique, or otherwise not really a serial number), but having a non-unique |
| + // or empty serial field in the URI still lets us print, it just means we |
| + // don't have a way to uniquely identify a printer if there are multiple ones |
| + // plugged in with the same VID/PID, so we may print to the *wrong* printer. |
| + // There doesn't seem to be a robust solution to this problem; if printers |
| + // don't supply a serial number, we don't have any reliable way to do that |
| + // differentiation. |
| + std::string serial = base::UTF16ToUTF8(device.serial_number()); |
| + printer->set_uri(CupsURIEscape( |
| + base::StringPrintf("usb://%04x/%04x?serial=%s", device.vendor_id(), |
| + device.product_id(), serial.c_str()))); |
| + return true; |
| +} |
| + |
| +} // namespace chromeos |