Chromium Code Reviews| Index: chrome/utility/media_router/dial_device_description_parser.cc |
| diff --git a/chrome/utility/media_router/dial_device_description_parser.cc b/chrome/utility/media_router/dial_device_description_parser.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c443609a49111fa49d7c86bcdc7fdabacf933341 |
| --- /dev/null |
| +++ b/chrome/utility/media_router/dial_device_description_parser.cc |
| @@ -0,0 +1,132 @@ |
| +// 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/utility/media_router/dial_device_description_parser.h" |
| + |
| +#include <libxml/parser.h> |
| +#include <libxml/tree.h> |
| +#include <libxml/xpath.h> |
| +#include <libxslt/transform.h> |
| +#include <libxslt/xslt.h> |
| +#include <libxslt/xsltutils.h> |
| + |
| +#include "base/strings/string_util.h" |
| +#include "third_party/libxml/chromium/libxml_utils.h" |
| + |
| +namespace { |
| + |
| +constexpr char xslt_text[] = |
| + "<?xml version=\"1.0\" encoding=\"utf-8\"?>" |
| + "<xsl:stylesheet version=\"1.0\" " |
| + "xmlns:t=\"urn:schemas-upnp-org:device-1-0\" " |
| + "xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">" |
| + "<xsl:template match=\"node()|@*\">" |
| + "<xsl:copy>" |
| + "<xsl:apply-templates select=\"node()|@*\"/>" |
| + "</xsl:copy>" |
| + "</xsl:template>" |
| + "<xsl:template match=\"t:root/t:device/t:UDN/text()\">***</xsl:template>" |
| + "<xsl:template " |
| + "match=\"t:root/t:device/t:serialNumber/text()\">***</xsl:template>" |
| + "</xsl:stylesheet>"; |
| + |
| +static std::string Validate( |
| + const media_router::DialDeviceDescription& description) { |
| + if (description.unique_id.empty()) { |
| + return "Missing uniqueId"; |
| + } |
| + if (description.friendly_name.empty()) { |
| + return "Missing friendlyName"; |
| + } |
| + return ""; |
| +} |
| + |
| +} // namespace |
| + |
| +namespace media_router { |
| + |
| +DialDeviceDescriptionParser::DialDeviceDescriptionParser() = default; |
| +DialDeviceDescriptionParser::~DialDeviceDescriptionParser() = default; |
| + |
| +bool DialDeviceDescriptionParser::Parse(const std::string& xml) { |
| + device_description_ = DialDeviceDescription(); |
| + |
| + XmlReader xml_reader; |
| + if (!xml_reader.Load(xml)) |
| + return false; |
| + |
| + while (xml_reader.Read()) { |
| + xml_reader.SkipToElement(); |
| + std::string node_name(xml_reader.NodeName()); |
| + |
| + if (node_name == "UDN") { |
| + if (!xml_reader.ReadElementContent(&device_description_.unique_id)) |
| + return false; |
| + } else if (node_name == "friendlyName") { |
| + if (!xml_reader.ReadElementContent(&device_description_.friendly_name)) |
| + return false; |
| + } else if (node_name == "modelName") { |
| + if (!xml_reader.ReadElementContent(&device_description_.model_name)) |
| + return false; |
| + } else if (node_name == "deviceType") { |
| + if (!xml_reader.ReadElementContent(&device_description_.device_type)) |
| + return false; |
| + } |
| + } |
| + |
| + // If friendly name does not exist, fall back to use model name + last 4 |
| + // digits of UUID as friendly name. |
| + if (device_description_.friendly_name.empty()) { |
| + auto model_name = device_description_.model_name; |
| + auto unique_id = device_description_.unique_id; |
| + |
| + if (!model_name.empty() && unique_id.length() >= 4) { |
| + device_description_.friendly_name = |
| + model_name + "[" + unique_id.substr(unique_id.length() - 4) + "]"; |
| + |
| + DVLOG(2) |
| + << "Fixed device description: created friendlyName from modelName."; |
|
mark a. foltz
2017/03/17 19:05:47
In my opinion, it will be pretty clear from the fr
zhaobin
2017/03/18 00:17:39
Done.
|
| + } |
| + } |
| + |
| + std::string error = Validate(device_description_); |
| + if (!error.empty()) { |
| + DLOG(WARNING) << "Device description failed to validate: " << error; |
| + return false; |
| + } |
| + |
| + return true; |
| +} |
| + |
| +std::string DialDeviceDescriptionParser::ScrubXmlForLogging( |
| + const std::string& xml) { |
| + xmlDocPtr doc = xmlParseMemory(xml.c_str(), xml.size()); |
| + |
| + if (doc == NULL) { |
|
mark a. foltz
2017/03/17 19:05:47
s/NULL/nullptr/ assuming doc is a pointer type.
zhaobin
2017/03/18 00:17:39
Done.
|
| + LOG(WARNING) << "Document not parsed successfully"; |
| + return ""; |
| + } |
| + |
| + xmlDocPtr xslt_doc = xmlParseMemory(xslt_text, sizeof(xslt_text)); |
| + DCHECK(xslt_doc); |
|
mark a. foltz
2017/03/17 19:05:47
Assuming xmlDocPtr is a pointer type, it would be
zhaobin
2017/03/18 00:17:39
Done.
|
| + |
| + xsltStylesheetPtr style_ptr = xsltParseStylesheetDoc(xslt_doc); |
| + xmlDocPtr res = xsltApplyStylesheet(style_ptr, doc, NULL); |
| + |
| + xmlChar* output; |
| + int size; |
| + xsltSaveResultToString(&output, &size, res, style_ptr); |
|
mark a. foltz
2017/03/17 19:05:47
I assume this allocates output to be sufficiently
zhaobin
2017/03/18 00:17:39
Seems so. output is allocated inside xsltSaveResul
|
| + |
| + std::string logging_xml = std::string(reinterpret_cast<char*>(output)); |
|
mark a. foltz
2017/03/17 19:05:47
I'm not an expert in XML APIs to know how safe thi
zhaobin
2017/03/18 00:17:39
xmlChar is unsigned char, and seems that output is
|
| + |
| + xmlFreeDoc(xslt_doc); |
| + xmlFreeDoc(doc); |
| + |
| + xsltCleanupGlobals(); |
| + xmlCleanupParser(); |
|
mark a. foltz
2017/03/17 19:05:47
Who frees output?
zhaobin
2017/03/18 00:17:39
Done.
|
| + |
| + return logging_xml; |
| +} |
| + |
| +} // namespace media_router |