/* Copyright (C) 2012-2016 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "package_base.h" #include "compose.hpp" #include "exceptions.h" #include "util.h" #include "dcp_assert.h" #include "font_asset.h" #include "raw_convert.h" #include "certificate_chain.h" #include #include #include #include using std::list; using std::map; using std::string; using boost::shared_ptr; using boost::algorithm::starts_with; using namespace dcp; PackageBase::PackageBase (boost::filesystem::path directory) { if (!boost::filesystem::exists (directory)) { boost::filesystem::create_directories (directory); } _directory = boost::filesystem::canonical (directory); } list > PackageBase::read_assetmap (bool keep_going, ReadErrors* errors) const { boost::filesystem::path asset_map_file; if (boost::filesystem::exists (_directory / "ASSETMAP")) { asset_map_file = _directory / "ASSETMAP"; } else if (boost::filesystem::exists (_directory / "ASSETMAP.xml")) { asset_map_file = _directory / "ASSETMAP.xml"; } else { boost::throw_exception (PackageReadError (String::compose ("could not find AssetMap file in `%1'", _directory.string()))); } cxml::Document asset_map ("AssetMap"); asset_map.read_file (asset_map_file); list > asset_nodes = asset_map.node_child("AssetList")->node_children ("Asset"); map paths; BOOST_FOREACH (shared_ptr i, asset_nodes) { if (i->node_child("ChunkList")->node_children("Chunk").size() != 1) { boost::throw_exception (XMLError ("unsupported asset chunk count")); } string p = i->node_child("ChunkList")->node_child("Chunk")->string_child ("Path"); if (starts_with (p, "file://")) { p = p.substr (7); } paths.insert (make_pair (remove_urn_uuid (i->string_child ("Id")), p)); } /* Read all the assets from the asset map */ /* XXX: I think we should be looking at the PKL here to decide type, not the extension of the file. */ list > assets; for (map::const_iterator i = paths.begin(); i != paths.end(); ++i) { boost::filesystem::path path = _directory / i->second; if (!boost::filesystem::exists (path)) { if (keep_going) { if (errors) { errors->push_back (shared_ptr (new MissingAssetError (path))); } } else { throw MissingAssetError (path); } continue; } if (boost::filesystem::extension(path) == ".xml") { xmlpp::DomParser* p = new xmlpp::DomParser; try { p->parse_file (path.string()); } catch (std::exception& e) { delete p; continue; } string const root = p->get_document()->get_root_node()->get_name (); delete p; shared_ptr a = xml_asset_factory (path, root); if (a) { assets.push_back (a); } } else if (boost::filesystem::extension (path) == ".mxf") { assets.push_back (mxf_asset_factory (path)); } else if (boost::filesystem::extension (path) == ".ttf") { assets.push_back (shared_ptr (new FontAsset (i->first, path))); } } return assets; } /** Write the VOLINDEX file. * @param standard DCP standard to use (INTEROP or SMPTE) */ void PackageBase::write_volindex (Standard standard) const { boost::filesystem::path p = _directory; switch (standard) { case DCP_INTEROP: p /= "VOLINDEX"; break; case DCP_SMPTE: p /= "VOLINDEX.xml"; break; case IMP: /* XXX */ DCP_ASSERT (false); break; } xmlpp::Document doc; xmlpp::Element* root = 0; switch (standard) { case DCP_INTEROP: root = doc.create_root_node ("VolumeIndex", "http://www.digicine.com/PROTO-ASDCP-AM-20040311#"); break; case DCP_SMPTE: case IMP: root = doc.create_root_node ("VolumeIndex", "http://www.smpte-ra.org/schemas/429-9/2007/AM"); break; } root->add_child("Index")->add_child_text ("1"); doc.write_to_file (p.string (), "UTF-8"); } void PackageBase::write_assetmap (Standard standard, string pkl_uuid, int pkl_length, dcp::XMLMetadata metadata) const { boost::filesystem::path p = _directory; switch (standard) { case DCP_INTEROP: p /= "ASSETMAP"; break; case DCP_SMPTE: case IMP: p /= "ASSETMAP.xml"; break; } xmlpp::Document doc; xmlpp::Element* root = 0; switch (standard) { case DCP_INTEROP: root = doc.create_root_node ("AssetMap", "http://www.digicine.com/PROTO-ASDCP-AM-20040311#"); break; case DCP_SMPTE: case IMP: root = doc.create_root_node ("AssetMap", "http://www.smpte-ra.org/schemas/429-9/2007/AM"); break; } root->add_child("Id")->add_child_text ("urn:uuid:" + make_uuid()); root->add_child("AnnotationText")->add_child_text ("Created by " + metadata.creator); switch (standard) { case DCP_INTEROP: root->add_child("VolumeCount")->add_child_text ("1"); root->add_child("IssueDate")->add_child_text (metadata.issue_date); root->add_child("Issuer")->add_child_text (metadata.issuer); root->add_child("Creator")->add_child_text (metadata.creator); break; case DCP_SMPTE: case IMP: root->add_child("Creator")->add_child_text (metadata.creator); root->add_child("VolumeCount")->add_child_text ("1"); root->add_child("IssueDate")->add_child_text (metadata.issue_date); root->add_child("Issuer")->add_child_text (metadata.issuer); break; } xmlpp::Node* asset_list = root->add_child ("AssetList"); xmlpp::Node* asset = asset_list->add_child ("Asset"); asset->add_child("Id")->add_child_text ("urn:uuid:" + pkl_uuid); asset->add_child("PackingList")->add_child_text ("true"); xmlpp::Node* chunk_list = asset->add_child ("ChunkList"); xmlpp::Node* chunk = chunk_list->add_child ("Chunk"); chunk->add_child("Path")->add_child_text ("pkl_" + pkl_uuid + ".xml"); chunk->add_child("VolumeIndex")->add_child_text ("1"); chunk->add_child("Offset")->add_child_text ("0"); chunk->add_child("Length")->add_child_text (raw_convert (pkl_length)); BOOST_FOREACH (shared_ptr i, assets ()) { i->write_to_assetmap (asset_list, _directory); } /* This must not be the _formatted version otherwise signature digests will be wrong */ doc.write_to_file (p.string (), "UTF-8"); } boost::filesystem::path PackageBase::write_pkl (dcp::Standard standard, string pkl_uuid, dcp::XMLMetadata metadata, shared_ptr signer) const { boost::filesystem::path p = _directory; p /= String::compose ("pkl_%1.xml", pkl_uuid); xmlpp::Document doc; xmlpp::Element* pkl; if (standard == DCP_INTEROP) { pkl = doc.create_root_node("PackingList", "http://www.digicine.com/PROTO-ASDCP-PKL-20040311#"); } else { pkl = doc.create_root_node("PackingList", "http://www.smpte-ra.org/schemas/429-8/2007/PKL"); } if (signer) { pkl->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "dsig"); } pkl->add_child("Id")->add_child_text ("urn:uuid:" + pkl_uuid); pkl->add_child("AnnotationText")->add_child_text (pkl_annotation_text ()); pkl->add_child("IssueDate")->add_child_text (metadata.issue_date); pkl->add_child("Issuer")->add_child_text (metadata.issuer); pkl->add_child("Creator")->add_child_text (metadata.creator); xmlpp::Element* asset_list = pkl->add_child("AssetList"); BOOST_FOREACH (shared_ptr i, assets ()) { i->write_to_pkl (asset_list, _directory, standard); } if (signer) { signer->sign (pkl, standard); } doc.write_to_file (p.string (), "UTF-8"); return p.string (); }