2323#include " DataFile_p.h"
2424#include " SignatureXAdES_LTA.h"
2525#include " XMLDocument.h"
26- #include " crypto/Digest.h"
2726#include " crypto/Signer.h"
2827#include " util/File.h"
2928
3029#include < algorithm>
3130#include < set>
32- #include < sstream>
3331
3432using namespace digidoc ;
3533using namespace digidoc ::util;
3634using namespace std ;
3735
36+ #define STR_VIEW_FMT (str ) int (str.size()), str.data()
37+
3838class ASiC_E ::Private
3939{
4040public:
@@ -51,21 +51,83 @@ class ASiC_E::Private
5151/* *
5252 * Initialize BDOC container.
5353 */
54- ASiC_E::ASiC_E ()
55- : ASiContainer(MIMETYPE_ASIC_E )
54+ ASiC_E::ASiC_E (const string &path, bool create) try
55+ : ASiContainer(path, MIMETYPE_ASIC_E )
5656 , d(make_unique<Private>())
5757{
58- }
58+ if (create)
59+ return ;
60+ auto z = load (true , {MIMETYPE_ASIC_E , MIMETYPE_ADOC });
61+ auto doc = XMLDocument::open (z.read (" META-INF/manifest.xml" ), {" manifest" , MANIFEST_NS });
62+ doc.validateSchema (File::path (Conf::instance ()->xsdPath (), " OpenDocument_manifest_v1_2.xsd" ));
63+
64+ set<string_view> manifestFiles;
65+ bool mimeFound = false ;
66+ for (auto file = doc/" file-entry" ; file; file++)
67+ {
68+ auto full_path = file[{" full-path" , MANIFEST_NS }];
69+ auto media_type = file[{" media-type" , MANIFEST_NS }];
70+ DEBUG (" full_path = '%.*s', media_type = '%.*s'" , STR_VIEW_FMT (full_path), STR_VIEW_FMT (media_type));
5971
60- /* *
61- * Opens ASiC container from a file
62- */
63- ASiC_E::ASiC_E (const string &path)
64- : ASiContainer(MIMETYPE_ASIC_E )
65- , d(make_unique<Private>())
72+ // ODF does not specify that mimetype should be first in manifest
73+ if (full_path == " /" )
74+ {
75+ if (mimeFound)
76+ THROW (" Manifest multiple entries defined for file '/'." );
77+ if (mediaType () != media_type)
78+ THROW (" Manifest has incorrect container media type defined '%.*s', expecting '%s'." , STR_VIEW_FMT (media_type), mediaType ().c_str ());
79+ mimeFound = true ;
80+ continue ;
81+ }
82+ if (full_path.back () == ' /' ) // Skip Directory entries
83+ continue ;
84+
85+ if (const auto &[pos, inserted] = manifestFiles.insert (full_path); !inserted)
86+ THROW (" Manifest multiple entries defined for file '%.*s'." , STR_VIEW_FMT (full_path));
87+ if (mediaType () == MIMETYPE_ADOC &&
88+ (full_path.starts_with (" META-INF/" ) || full_path.starts_with (" metadata/" )))
89+ d->metadata .push_back (new DataFilePrivate (z, string (full_path), string (media_type)));
90+ else
91+ addDataFilePrivate (new DataFilePrivate (z, string (full_path), string (media_type)));
92+ }
93+ if (!mimeFound)
94+ THROW (" Manifest is missing mediatype file entry." );
95+
96+ for (const string &file: z.list ())
97+ {
98+ /* *
99+ * http://www.etsi.org/deliver/etsi_ts/102900_102999/102918/01.03.01_60/ts_102918v010301p.pdf
100+ * 6.2.2 Contents of Container
101+ * 3) The root element of each "*signatures*.xml" content shall be either:
102+ */
103+ if (file.starts_with (" META-INF/" ) && file.contains (" signatures" ))
104+ {
105+ try
106+ {
107+ loadSignatures (XMLDocument::open (z.read (file)), file);
108+ }
109+ catch (const Exception &e)
110+ {
111+ THROW_CAUSE (e, " Failed to parse signature '%s'." , file.c_str ());
112+ }
113+ continue ;
114+ }
115+
116+ if (file == " mimetype" || file.starts_with (" META-INF" ))
117+ continue ;
118+ if (manifestFiles.erase (file) == 0 )
119+ THROW (" File '%s' found in container is not described in manifest." , file.c_str ());
120+ }
121+ if (!manifestFiles.empty ())
122+ THROW (" Manifest describes files that are not found in container." );
123+ }
124+ catch (const Exception &e)
66125{
67- auto zip = load (path, true , {MIMETYPE_ASIC_E , MIMETYPE_ADOC });
68- parseManifestAndLoadFiles (zip);
126+ THROW_CAUSE (e, " Failed to parse manifest" );
127+ }
128+ catch (...)
129+ {
130+ THROW (" Failed to parse manifest XML: Unknown exception" );
69131}
70132
71133ASiC_E::~ASiC_E ()
@@ -110,9 +172,7 @@ void ASiC_E::save(const ZipSerialize &s)
110172unique_ptr<Container> ASiC_E::createInternal (const string &path)
111173{
112174 DEBUG (" ASiC_E::createInternal(%s)" , path.c_str ());
113- unique_ptr<ASiC_E> doc = unique_ptr<ASiC_E>(new ASiC_E);
114- doc->zpath (path);
115- return doc;
175+ return unique_ptr<Container>(new ASiC_E (path, true ));
116176}
117177
118178/* *
@@ -146,7 +206,7 @@ void ASiC_E::canSave()
146206unique_ptr<Container> ASiC_E::openInternal (const string &path)
147207{
148208 DEBUG (" ASiC_E::openInternal(%s)" , path.c_str ());
149- return unique_ptr<Container>(new ASiC_E (path));
209+ return unique_ptr<Container>(new ASiC_E (path, false ));
150210}
151211
152212void ASiC_E::loadSignatures (XMLDocument &&doc, const string &file)
@@ -157,92 +217,6 @@ void ASiC_E::loadSignatures(XMLDocument &&doc, const string &file)
157217 addSignature (make_unique<SignatureXAdES_LTA>(signatures, s, this ));
158218}
159219
160- /* *
161- * Parses manifest file and checks that files described in manifest exist, also
162- * checks that no extra file do exist that are not described in manifest.xml.
163- *
164- * @param path directory on disk of the BDOC container.
165- * @throws Exception exception is thrown if the manifest.xml file parsing failed.
166- */
167- void ASiC_E::parseManifestAndLoadFiles (const ZipSerialize &z)
168- {
169- DEBUG (" ASiC_E::readManifest()" );
170-
171- try
172- {
173- auto doc = XMLDocument::open (z.read (" META-INF/manifest.xml" ), {" manifest" , MANIFEST_NS });
174- doc.validateSchema (File::path (Conf::instance ()->xsdPath (), " OpenDocument_manifest_v1_2.xsd" ));
175-
176- set<string_view> manifestFiles;
177- bool mimeFound = false ;
178- for (auto file = doc/" file-entry" ; file; file++)
179- {
180- auto full_path = file[{" full-path" , MANIFEST_NS }];
181- auto media_type = file[{" media-type" , MANIFEST_NS }];
182- DEBUG (" full_path = '%s', media_type = '%s'" , full_path.data (), media_type.data ());
183-
184- if (manifestFiles.contains (full_path))
185- THROW (" Manifest multiple entries defined for file '%s'." , full_path.data ());
186-
187- // ODF does not specify that mimetype should be first in manifest
188- if (full_path == " /" )
189- {
190- if (mediaType () != media_type)
191- THROW (" Manifest has incorrect container media type defined '%s', expecting '%s'." , media_type.data (), mediaType ().c_str ());
192- mimeFound = true ;
193- continue ;
194- }
195- if (full_path.back () == ' /' ) // Skip Directory entries
196- continue ;
197-
198- manifestFiles.insert (full_path);
199- if (mediaType () == MIMETYPE_ADOC &&
200- (full_path.starts_with (" META-INF/" ) ||
201- full_path.starts_with (" metadata/" )))
202- d->metadata .push_back (new DataFilePrivate (z, string (full_path), string (media_type)));
203- else
204- addDataFilePrivate (new DataFilePrivate (z, string (full_path), string (media_type)));
205- }
206- if (!mimeFound)
207- THROW (" Manifest is missing mediatype file entry." );
208-
209- for (const string &file: z.list ())
210- {
211- /* *
212- * http://www.etsi.org/deliver/etsi_ts/102900_102999/102918/01.03.01_60/ts_102918v010301p.pdf
213- * 6.2.2 Contents of Container
214- * 3) The root element of each "*signatures*.xml" content shall be either:
215- */
216- if (file.starts_with (" META-INF/" ) &&
217- file.find (" signatures" ) != string::npos)
218- {
219- try
220- {
221- loadSignatures (XMLDocument::open (z.read (file)), file);
222- }
223- catch (const Exception &e)
224- {
225- THROW_CAUSE (e, " Failed to parse signature '%s'." , file.c_str ());
226- }
227- continue ;
228- }
229-
230- if (file == " mimetype" || file.starts_with (" META-INF" ))
231- continue ;
232- if (!manifestFiles.contains (file))
233- THROW (" File '%s' found in container is not described in manifest." , file.c_str ());
234- }
235- }
236- catch (const Exception &e)
237- {
238- THROW_CAUSE (e, " Failed to parse manifest" );
239- }
240- catch (...)
241- {
242- THROW (" Failed to parse manifest XML: Unknown exception" );
243- }
244- }
245-
246220Signature* ASiC_E::prepareSignature (Signer *signer)
247221{
248222 if (mediaType () != MIMETYPE_ASIC_E )
0 commit comments