+
+class DCPErrorHandler : public ErrorHandler
+{
+public:
+ void warning(const SAXParseException& e)
+ {
+ maybe_add (XMLValidationError(e));
+ }
+
+ void error(const SAXParseException& e)
+ {
+ maybe_add (XMLValidationError(e));
+ }
+
+ void fatalError(const SAXParseException& e)
+ {
+ maybe_add (XMLValidationError(e));
+ }
+
+ void resetErrors() {
+ _errors.clear ();
+ }
+
+ list<XMLValidationError> errors () const {
+ return _errors;
+ }
+
+private:
+ void maybe_add (XMLValidationError e)
+ {
+ /* XXX: nasty hack */
+ if (
+ e.message().find("schema document") != string::npos &&
+ e.message().find("has different target namespace from the one specified in instance document") != string::npos
+ ) {
+ return;
+ }
+
+ _errors.push_back (e);
+ }
+
+ list<XMLValidationError> _errors;
+};
+
+class StringToXMLCh : public boost::noncopyable
+{
+public:
+ StringToXMLCh (string a)
+ {
+ _buffer = XMLString::transcode(a.c_str());
+ }
+
+ ~StringToXMLCh ()
+ {
+ XMLString::release (&_buffer);
+ }
+
+ XMLCh const * get () const {
+ return _buffer;
+ }
+
+private:
+ XMLCh* _buffer;
+};
+
+class LocalFileResolver : public EntityResolver
+{
+public:
+ LocalFileResolver (boost::filesystem::path xsd_dtd_directory)
+ : _xsd_dtd_directory (xsd_dtd_directory)
+ {
+ add("http://www.w3.org/2001/XMLSchema.dtd", "XMLSchema.dtd");
+ add("http://www.w3.org/2001/03/xml.xsd", "xml.xsd");
+ add("http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/xmldsig-core-schema.xsd", "xmldsig-core-schema.xsd");
+ add("http://www.digicine.com/schemas/437-Y/2007/Main-Stereo-Picture-CPL.xsd", "Main-Stereo-Picture-CPL.xsd");
+ add("http://www.digicine.com/PROTO-ASDCP-CPL-20040511.xsd", "PROTO-ASDCP-CPL-20040511.xsd");
+ add("http://www.digicine.com/PROTO-ASDCP-PKL-20040311.xsd", "PROTO-ASDCP-PKL-20040311.xsd");
+ add("http://www.digicine.com/PROTO-ASDCP-AM-20040311.xsd", "PROTO-ASDCP-AM-20040311.xsd");
+ add("http://www.digicine.com/PROTO-ASDCP-CC-CPL-20070926#", "PROTO-ASDCP-CC-CPL-20070926.xsd");
+ add("interop-subs", "DCSubtitle.v1.mattsson.xsd");
+ add("http://www.smpte-ra.org/schemas/428-7/2010/DCST.xsd", "SMPTE-428-7-2010-DCST.xsd");
+ }
+
+ InputSource* resolveEntity(XMLCh const *, XMLCh const * system_id)
+ {
+ string system_id_str = xml_ch_to_string (system_id);
+ boost::filesystem::path p = _xsd_dtd_directory;
+ if (_files.find(system_id_str) == _files.end()) {
+ p /= system_id_str;
+ } else {
+ p /= _files[system_id_str];
+ }
+ StringToXMLCh ch (p.string());
+ return new LocalFileInputSource(ch.get());
+ }
+
+private:
+ void add (string uri, string file)
+ {
+ _files[uri] = file;
+ }
+
+ std::map<string, string> _files;
+ boost::filesystem::path _xsd_dtd_directory;
+};
+
+
+static void
+parse (XercesDOMParser& parser, boost::filesystem::path xml)
+{
+ parser.parse(xml.string().c_str());
+}
+
+
+static void
+parse (XercesDOMParser& parser, std::string xml)
+{
+ xercesc::MemBufInputSource buf(reinterpret_cast<unsigned char const*>(xml.c_str()), xml.size(), "");
+ parser.parse(buf);
+}
+
+
+template <class T>
+void
+validate_xml (T xml, boost::filesystem::path xsd_dtd_directory, list<VerificationNote>& notes)
+{
+ try {
+ XMLPlatformUtils::Initialize ();
+ } catch (XMLException& e) {
+ throw MiscError ("Failed to initialise xerces library");
+ }
+
+ DCPErrorHandler error_handler;
+
+ /* All the xerces objects in this scope must be destroyed before XMLPlatformUtils::Terminate() is called */
+ {
+ XercesDOMParser parser;
+ parser.setValidationScheme(XercesDOMParser::Val_Always);
+ parser.setDoNamespaces(true);
+ parser.setDoSchema(true);
+
+ vector<string> schema;
+ schema.push_back("xmldsig-core-schema.xsd");
+ schema.push_back("SMPTE-429-7-2006-CPL.xsd");
+ schema.push_back("SMPTE-429-8-2006-PKL.xsd");
+ schema.push_back("SMPTE-429-9-2007-AM.xsd");
+ schema.push_back("Main-Stereo-Picture-CPL.xsd");
+ schema.push_back("PROTO-ASDCP-CPL-20040511.xsd");
+ schema.push_back("PROTO-ASDCP-PKL-20040311.xsd");
+ schema.push_back("PROTO-ASDCP-AM-20040311.xsd");
+ schema.push_back("DCSubtitle.v1.mattsson.xsd");
+ schema.push_back("DCDMSubtitle-2010.xsd");
+ schema.push_back("PROTO-ASDCP-CC-CPL-20070926.xsd");
+
+ /* XXX: I'm not especially clear what this is for, but it seems to be necessary */
+ string locations;
+ BOOST_FOREACH (string i, schema) {
+ locations += String::compose("%1 %1 ", i, i);
+ }
+
+ parser.setExternalSchemaLocation(locations.c_str());
+ parser.setValidationSchemaFullChecking(true);
+ parser.setErrorHandler(&error_handler);
+
+ LocalFileResolver resolver (xsd_dtd_directory);
+ parser.setEntityResolver(&resolver);
+
+ try {
+ parser.resetDocumentPool();
+ parse(parser, xml);
+ } catch (XMLException& e) {
+ throw MiscError(xml_ch_to_string(e.getMessage()));
+ } catch (DOMException& e) {
+ throw MiscError(xml_ch_to_string(e.getMessage()));
+ } catch (...) {
+ throw MiscError("Unknown exception from xerces");
+ }
+ }
+
+ XMLPlatformUtils::Terminate ();
+
+ BOOST_FOREACH (XMLValidationError i, error_handler.errors()) {
+ notes.push_back (
+ VerificationNote(
+ VerificationNote::VERIFY_ERROR,
+ VerificationNote::XML_VALIDATION_ERROR,
+ i.message(),
+ xml,
+ i.line()
+ )
+ );
+ }
+}
+
+
+enum VerifyAssetResult {
+ VERIFY_ASSET_RESULT_GOOD,
+ VERIFY_ASSET_RESULT_CPL_PKL_DIFFER,
+ VERIFY_ASSET_RESULT_BAD
+};
+
+
+static VerifyAssetResult
+verify_asset (shared_ptr<const DCP> dcp, shared_ptr<const ReelMXF> reel_mxf, function<void (float)> progress)