Skip to content

Commit f90a086

Browse files
authored
ASiC-S TimeStamp save support (#643)
IB-8290 Signed-off-by: Raul Metsma <[email protected]>
1 parent 046e5a9 commit f90a086

13 files changed

+176
-135
lines changed

src/ASiC_E.cpp

Lines changed: 9 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ using namespace digidoc;
3535
using namespace digidoc::util;
3636
using namespace std;
3737

38-
constexpr string_view MANIFEST_NS {"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"};
39-
4038
class ASiC_E::Private
4139
{
4240
public:
@@ -88,28 +86,10 @@ vector<DataFile*> ASiC_E::metaFiles() const
8886
* document does not exist.
8987
* @throws Exception is thrown if ASiC_E class is not correctly initialized.
9088
*/
91-
void ASiC_E::save(const string &path)
89+
void ASiC_E::save(const ZipSerialize &s)
9290
{
93-
if(dataFiles().empty())
94-
THROW("Can not save, container is empty.");
95-
if(mediaType() != MIMETYPE_ASIC_E)
96-
THROW("'%s' format is not supported", mediaType().c_str());
97-
98-
if(!path.empty())
99-
zpath(path);
100-
ZipSerialize s(zpath(), true);
101-
102-
stringstream mimetype;
103-
mimetype << mediaType();
104-
s.addFile("mimetype", mimetype, zproperty("mimetype"), false);
105-
106-
stringstream manifest;
107-
if(!createManifest().save(manifest))
91+
if(!createManifest().save(s.addFile("META-INF/manifest.xml", zproperty("META-INF/manifest.xml")), true))
10892
THROW("Failed to create manifest XML");
109-
s.addFile("META-INF/manifest.xml", manifest, zproperty("META-INF/manifest.xml"));
110-
111-
for(const DataFile *file: dataFiles())
112-
s.addFile(file->fileName(), *(static_cast<const DataFilePrivate*>(file)->m_is), zproperty(file->fileName()));
11393

11494
std::set<Signatures*> saved;
11595
for(Signature *iter: signatures())
@@ -122,10 +102,8 @@ void ASiC_E::save(const string &path)
122102
});
123103
if(name == d->signatures.cend())
124104
THROW("Unkown signature object");
125-
stringstream ofs;
126-
if(!signatures->save(ofs))
105+
if(!signatures->save(s.addFile(name->first, zproperty(name->first))))
127106
THROW("Failed to create signature XML file.");
128-
s.addFile(name->first, ofs, zproperty(name->first));
129107
}
130108
}
131109

@@ -159,32 +137,16 @@ void ASiC_E::addAdESSignature(istream &data)
159137
}
160138
}
161139

162-
unique_ptr<Container> ASiC_E::openInternal(const string &path)
140+
void ASiC_E::canSave()
163141
{
164-
DEBUG("ASiC_E::openInternal(%s)", path.c_str());
165-
return unique_ptr<Container>(new ASiC_E(path));
142+
if(mediaType() != MIMETYPE_ASIC_E)
143+
THROW("'%s' format is not supported", mediaType().c_str());
166144
}
167145

168-
/**
169-
* Creates BDoc container manifest file and returns its path.
170-
*
171-
* @return returns created manifest file path.
172-
* @throws Exception exception is thrown if manifest file creation failed.
173-
*/
174-
XMLDocument ASiC_E::createManifest() const
146+
unique_ptr<Container> ASiC_E::openInternal(const string &path)
175147
{
176-
DEBUG("ASiC_E::createManifest()");
177-
auto doc = XMLDocument::create("manifest", MANIFEST_NS, "manifest");
178-
doc.setProperty("version", "1.2", MANIFEST_NS);
179-
auto add = [&doc](string_view path, string_view mime) {
180-
auto file = doc+"file-entry";
181-
file.setProperty("full-path", path, MANIFEST_NS);
182-
file.setProperty("media-type", mime, MANIFEST_NS);
183-
};
184-
add("/", mediaType());
185-
for(const DataFile *file: dataFiles())
186-
add(file->fileName(), file->mediaType());
187-
return doc;
148+
DEBUG("ASiC_E::openInternal(%s)", path.c_str());
149+
return unique_ptr<Container>(new ASiC_E(path));
188150
}
189151

190152
void ASiC_E::loadSignatures(istream &data, const string &file)

src/ASiC_E.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@
2323

2424
namespace digidoc
2525
{
26-
struct XMLDocument;
27-
2826
/**
2927
* Implements the BDOC specification of the signed digital document container.
3028
* Container can contain several files and all these files can be signed using
@@ -42,7 +40,6 @@ namespace digidoc
4240
static constexpr std::string_view ASIC_TSA_PROFILE = "time-stamp-archive";
4341

4442
~ASiC_E() final;
45-
void save(const std::string &path = {}) final;
4643
std::vector<DataFile*> metaFiles() const;
4744

4845
void addAdESSignature(std::istream &data) final;
@@ -56,9 +53,10 @@ namespace digidoc
5653
ASiC_E();
5754
ASiC_E(const std::string &path);
5855
DISABLE_COPY(ASiC_E);
59-
XMLDocument createManifest() const;
56+
void canSave() final;
6057
void loadSignatures(std::istream &data, const std::string &file);
6158
void parseManifestAndLoadFiles(const ZipSerialize &z);
59+
void save(const ZipSerialize &s) final;
6260

6361
class Private;
6462
std::unique_ptr<Private> d;

src/ASiC_S.cpp

Lines changed: 45 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -34,47 +34,51 @@ using namespace std;
3434
/**
3535
* Initialize ASiCS container.
3636
*/
37-
ASiC_S::ASiC_S(): ASiContainer(MIMETYPE_ASIC_S)
37+
ASiC_S::ASiC_S()
38+
: ASiContainer(MIMETYPE_ASIC_S)
3839
{}
3940

4041
/**
4142
* Opens ASiC-S container from a file
4243
*/
43-
ASiC_S::ASiC_S(const string &path): ASiContainer(MIMETYPE_ASIC_S)
44+
ASiC_S::ASiC_S(const string &path)
45+
: ASiContainer(MIMETYPE_ASIC_S)
4446
{
4547
auto z = load(path, false, {mediaType()});
46-
static const string_view metaInf = "META-INF/";
48+
auto starts_with = [](string_view str, string_view needle) constexpr {
49+
return str.size() >= needle.size() && str.compare(0, needle.size(), needle) == 0;
50+
};
4751

4852
for(const string &file: z.list())
4953
{
50-
if(file == "mimetype" ||
51-
(metaInf.size() < file.size() && file.compare(0, metaInf.size(), metaInf) == 0))
52-
{
53-
if(file == "META-INF/timestamp.tst")
54-
{
55-
if(!signatures().empty())
56-
THROW("Can not add signature to ASiC-S container which already contains a signature.");
57-
addSignature(make_unique<SignatureTST>(z.extract<stringstream>(file).str(), this));
58-
}
59-
if(file == "META-INF/signatures.xml")
60-
{
61-
if(!signatures().empty())
62-
THROW("Can not add signature to ASiC-S container which already contains a signature.");
63-
auto data = z.extract<stringstream>(file);
64-
auto signatures = make_shared<Signatures>(data, mediaType());
65-
for(auto s = signatures->signature(); s; s++)
66-
addSignature(make_unique<SignatureXAdES_LTA>(signatures, s, this));
67-
}
54+
if(file == "mimetype")
6855
continue;
56+
if(file == "META-INF/timestamp.tst")
57+
{
58+
if(!signatures().empty())
59+
THROW("Can not add signature to ASiC-S container which already contains a signature.");
60+
addSignature(make_unique<SignatureTST>(z.extract<stringstream>(file).str(), this));
6961
}
70-
71-
const auto directory = File::directory(file);
72-
if(directory.empty() || directory == "/" || directory == "./")
62+
else if(file == "META-INF/signatures.xml")
7363
{
74-
if(!dataFiles().empty())
75-
THROW("Can not add document to ASiC-S container which already contains a document.");
76-
addDataFile(dataStream(file, z), file, "application/octet-stream");
64+
if(!signatures().empty())
65+
THROW("Can not add signature to ASiC-S container which already contains a signature.");
66+
auto data = z.extract<stringstream>(file);
67+
auto signatures = make_shared<Signatures>(data, mediaType());
68+
for(auto s = signatures->signature(); s; s++)
69+
addSignature(make_unique<SignatureXAdES_LTA>(signatures, s, this));
7770
}
71+
else if(file == "META-INF/ASiCArchiveManifest.xml")
72+
THROW("ASiCArchiveManifest are not supported.");
73+
else if(starts_with(file, "META-INF/"))
74+
continue;
75+
else if(const auto directory = File::directory(file);
76+
!directory.empty() && directory != "/" && directory != "./")
77+
THROW("Subfolders are not supported %s", directory.c_str());
78+
else if(!dataFiles().empty())
79+
THROW("Can not add document to ASiC-S container which already contains a document.");
80+
else
81+
addDataFile(dataStream(file, z), file, "application/octet-stream");
7882
}
7983

8084
if(dataFiles().empty())
@@ -83,11 +87,6 @@ ASiC_S::ASiC_S(const string &path): ASiContainer(MIMETYPE_ASIC_S)
8387
THROW("ASiC-S container does not contain any signatures.");
8488
}
8589

86-
void ASiC_S::save(const string & /*path*/)
87-
{
88-
THROW("Not implemented.");
89-
}
90-
9190
unique_ptr<Container> ASiC_S::createInternal(const string & /*path*/)
9291
{
9392
return {};
@@ -98,6 +97,12 @@ void ASiC_S::addAdESSignature(istream & /*signature*/)
9897
THROW("Not implemented.");
9998
}
10099

100+
void ASiC_S::canSave()
101+
{
102+
if(auto list = signatures(); !list.empty() && list.front()->profile() != ASIC_TST_PROFILE)
103+
THROW("ASiC-S container supports only saving TimeStampToken signatures.");
104+
}
105+
101106
unique_ptr<Container> ASiC_S::openInternal(const string &path, ContainerOpenCB * /*cb*/)
102107
{
103108
if (!isContainerSimpleFormat(path))
@@ -111,6 +116,14 @@ Signature* ASiC_S::prepareSignature(Signer * /*signer*/)
111116
THROW("Not implemented.");
112117
}
113118

119+
void ASiC_S::save(const ZipSerialize &s)
120+
{
121+
if(zproperty("META-INF/manifest.xml").size && !createManifest().save(s.addFile("META-INF/manifest.xml", zproperty("META-INF/manifest.xml")), true))
122+
THROW("Failed to create manifest XML");
123+
if(auto list = signatures(); !list.empty())
124+
s.addFile("META-INF/timestamp.tst", zproperty("META-INF/timestamp.tst"))(static_cast<SignatureTST*>(list.front())->save());
125+
}
126+
114127
Signature *ASiC_S::sign(Signer * /*signer*/)
115128
{
116129
THROW("Not implemented.");

src/ASiC_S.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,8 @@ namespace digidoc
3030
*/
3131
class ASiC_S : public ASiContainer
3232
{
33-
3433
public:
35-
void save(const std::string &path = {}) override;
34+
static constexpr std::string_view ASIC_TST_PROFILE = "TimeStampToken";
3635

3736
void addAdESSignature(std::istream &sigdata) override;
3837
Signature* prepareSignature(Signer *signer) override;
@@ -46,6 +45,9 @@ namespace digidoc
4645
ASiC_S(const std::string &path);
4746
DISABLE_COPY(ASiC_S);
4847

48+
void canSave() final;
49+
void save(const ZipSerialize &s) final;
50+
4951
static bool isContainerSimpleFormat(const std::string &path);
5052
};
5153
}

src/ASiContainer.cpp

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@
2121

2222
#include "DataFile_p.h"
2323
#include "Signature.h"
24+
#include "XMLDocument.h"
2425
#include "util/File.h"
2526
#include "util/log.h"
2627

2728
#include <algorithm>
29+
#include <array>
2830
#include <ctime>
2931
#include <fstream>
3032
#include <map>
@@ -51,7 +53,23 @@ class ASiContainer::Private
5153
ASiContainer::ASiContainer(string_view mimetype)
5254
: d(make_unique<Private>())
5355
{
54-
d->mimetype = mimetype;
56+
d->mimetype = string(mimetype);
57+
}
58+
59+
XMLDocument ASiContainer::createManifest() const
60+
{
61+
DEBUG("ASiContainer::createManifest()");
62+
auto doc = XMLDocument::create("manifest", MANIFEST_NS, "manifest");
63+
doc.setProperty("version", "1.2", MANIFEST_NS);
64+
auto add = [&doc](string_view path, string_view mime) {
65+
auto file = doc+"file-entry";
66+
file.setProperty("full-path", path, MANIFEST_NS);
67+
file.setProperty("media-type", mime, MANIFEST_NS);
68+
};
69+
add("/", mediaType());
70+
for(const DataFile *file: dataFiles())
71+
add(file->fileName(), file->mediaType());
72+
return doc;
5573
}
5674

5775
/**
@@ -166,15 +184,15 @@ void ASiContainer::addDataFile(const string &path, const string &mediaType)
166184
is = std::move(data);
167185
}
168186
d->properties[fileName] = { appInfo(), File::modifiedTime(path), size };
169-
addDataFilePrivate(std::move(is), std::move(fileName), mediaType);
187+
d->documents.push_back(new DataFilePrivate(std::move(is), std::move(fileName), mediaType));
170188
}
171189

172190
void ASiContainer::addDataFile(unique_ptr<istream> is, const string &fileName, const string &mediaType)
173191
{
174192
addDataFileChecks(fileName, mediaType);
175193
if(fileName.find_last_of("/\\") != string::npos)
176194
THROW("Document file '%s' cannot contain directory path.", fileName.c_str());
177-
addDataFilePrivate(std::move(is), fileName, mediaType);
195+
d->documents.push_back(new DataFilePrivate(std::move(is), fileName, mediaType));
178196
}
179197

180198
void ASiContainer::addDataFileChecks(const string &fileName, const string &mediaType)
@@ -241,6 +259,34 @@ void ASiContainer::deleteSignature(Signature* s)
241259
delete s;
242260
}
243261

262+
void ASiContainer::save(const string &path)
263+
{
264+
if(dataFiles().empty())
265+
THROW("Can not save, container is empty.");
266+
canSave();
267+
if(!path.empty())
268+
zpath(path);
269+
ZipSerialize s(zpath(), true);
270+
s.addFile("mimetype", zproperty("mimetype"), false)(mediaType());
271+
272+
array<char,10240> buf{};
273+
for(const DataFile *file: dataFiles())
274+
{
275+
auto f = s.addFile(file->fileName(), zproperty(file->fileName()));
276+
const auto &is = static_cast<const DataFilePrivate*>(file)->m_is;
277+
is->clear();
278+
is->seekg(0);
279+
while(*is)
280+
{
281+
is->read(buf.data(), buf.size());
282+
if(auto size = is->gcount(); size > 0)
283+
f(buf.data(), size_t(size));
284+
}
285+
}
286+
287+
save(s);
288+
}
289+
244290
void ASiContainer::zpath(const string &file)
245291
{
246292
d->path = file;
@@ -251,11 +297,11 @@ string ASiContainer::zpath() const
251297
return d->path;
252298
}
253299

254-
const ZipSerialize::Properties& ASiContainer::zproperty(const string &file) const
300+
const ZipSerialize::Properties& ASiContainer::zproperty(string_view file) const
255301
{
256302
if(auto i = d->properties.find(file); i != d->properties.cend())
257303
return i->second;
258-
return d->properties[file] = { appInfo(), time(nullptr), 0 };
304+
return d->properties[string(file)] = { appInfo(), time(nullptr), 0 };
259305
}
260306

261307
/**

0 commit comments

Comments
 (0)