From 3a42624f74dad0d8ccb4e5ba67f72a8a15cf0a83 Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Wed, 26 Jan 2022 16:17:51 +0200 Subject: [PATCH] Option to open container offline mode IB-7287 Signed-off-by: Raul Metsma --- libdigidocpp.dox | 2 ++ libdigidocpp.i | 13 +++++--- src/ASiC_E.cpp | 16 +++++----- src/ASiC_E.h | 2 +- src/ASiC_S.cpp | 12 ++++---- src/ASiC_S.h | 2 +- src/Container.cpp | 25 +++++++++++---- src/Container.h | 5 +++ src/SiVaContainer.cpp | 39 +++++++++++++----------- src/SiVaContainer.h | 4 +-- src/digidoc-tool.1.cmake | 3 +- src/digidoc-tool.cpp | 66 +++++++++++++++++++--------------------- 12 files changed, 108 insertions(+), 81 deletions(-) diff --git a/libdigidocpp.dox b/libdigidocpp.dox index 84b5e9633..97d30fedb 100644 --- a/libdigidocpp.dox +++ b/libdigidocpp.dox @@ -1400,6 +1400,8 @@ Signature Validation Policy Default POLv2 http://open-eid.github.io/SiVa/siva/appendix/validation_policy/" +\-\-offline Optional +open container offline (eg. Don't send to SiVa) \-\-warnings= (ignore, warning, error) Optional diff --git a/libdigidocpp.i b/libdigidocpp.i index 7b1e0c678..8f14b4f5a 100644 --- a/libdigidocpp.i +++ b/libdigidocpp.i @@ -241,7 +241,7 @@ namespace std { %include "crypto/Signer.h" %include "crypto/PKCS12Signer.h" %include "crypto/PKCS11Signer.h" -#ifdef SWIGCSHARP +#ifdef _WIN32 %include "crypto/WinSigner.h" #endif %include "libdigidocpp.i.h" @@ -277,16 +277,21 @@ namespace std { } } %extend digidoc::Container { + static Container* open(const std::string &path, OpenFlags flags) + { + return Container::openPtr(path, flags).release(); + } + Signature* prepareWebSignature(const std::vector &cert, const std::string &profile = {}, const std::vector &roles = {}, const std::string &city = {}, const std::string &state = {}, const std::string &postalCode = {}, const std::string &country = {}) { - class : public digidoc::Signer + class final: public digidoc::Signer { public: - digidoc::X509Cert cert() const override { return _cert; } - std::vector sign(const std::string &, const std::vector &) const override + digidoc::X509Cert cert() const final { return _cert; } + std::vector sign(const std::string &, const std::vector &) const final { THROW("Not implemented"); } diff --git a/src/ASiC_E.cpp b/src/ASiC_E.cpp index 48045dea3..fd21a05c2 100644 --- a/src/ASiC_E.cpp +++ b/src/ASiC_E.cpp @@ -113,7 +113,7 @@ void ASiC_E::save(const string &path) s.addFile("META-INF/manifest.xml", manifest, zproperty("META-INF/manifest.xml")); for(const DataFile *file: dataFiles()) - s.addFile(file->fileName(), *(static_cast(file)->m_is.get()), zproperty(file->fileName())); + s.addFile(file->fileName(), *(static_cast(file)->m_is), zproperty(file->fileName())); unsigned int i = 0; for(Signature *iter: signatures()) @@ -158,9 +158,9 @@ void ASiC_E::addAdESSignature(istream &sigdata) } } -unique_ptr ASiC_E::openInternal(const string &path) +unique_ptr ASiC_E::openInternal(const string &path, OpenFlags flags) { - DEBUG("ASiC_E::openInternal(%s)", path.c_str()); + DEBUG("ASiC_E::openInternal(%s, %d)", path.c_str(), flags); return unique_ptr(new ASiC_E(path)); } @@ -181,13 +181,13 @@ void ASiC_E::createManifest(ostream &os) try { Manifest manifest; - manifest.file_entry().push_back({"/", mediaType()}); + manifest.file_entry().push_back(make_unique("/", mediaType())); for(const DataFile *file: dataFiles()) - manifest.file_entry().push_back({file->fileName(), file->mediaType()}); + manifest.file_entry().push_back(make_unique(file->fileName(), file->mediaType())); xml_schema::NamespaceInfomap map; map["manifest"].name = ASiC_E::MANIFEST_NAMESPACE; - manifest::manifest(os, manifest, map, {}, xml_schema::Flags::dont_initialize); + manifest::manifest(os, manifest, map, "UTF-8", xml_schema::Flags::dont_initialize); if(os.fail()) THROW("Failed to create manifest XML"); } @@ -233,8 +233,8 @@ void ASiC_E::parseManifestAndLoadFiles(const ZipSerialize &z) z.extract("META-INF/manifest.xml", manifestdata); xml_schema::Properties p; p.schema_location(ASiC_E::MANIFEST_NAMESPACE, - File::fullPathUrl(Conf::instance()->xsdPath() + "/OpenDocument_manifest.xsd")); - unique_ptr doc = SecureDOMParser(p.schema_location(), true).parseIStream(manifestdata); + File::fullPathUrl(Conf::instance()->xsdPath() + "/OpenDocument_manifest.xsd")); + unique_ptr doc = SecureDOMParser(p.schema_location(), true).parseIStream(manifestdata); unique_ptr manifest = manifest::manifest(*doc, {}, p); set manifestFiles; diff --git a/src/ASiC_E.h b/src/ASiC_E.h index 56bbc3b32..8411735f9 100644 --- a/src/ASiC_E.h +++ b/src/ASiC_E.h @@ -53,7 +53,7 @@ namespace digidoc Signature* sign(Signer* signer) final; static std::unique_ptr createInternal(const std::string &path); - static std::unique_ptr openInternal(const std::string &path); + static std::unique_ptr openInternal(const std::string &path, OpenFlags flags); private: ASiC_E(); diff --git a/src/ASiC_S.cpp b/src/ASiC_S.cpp index bd288a443..84b56eb3c 100644 --- a/src/ASiC_S.cpp +++ b/src/ASiC_S.cpp @@ -58,7 +58,7 @@ void ASiC_S::addDataFile(const string &path, const string &mediaType) { if(!dataFiles().empty()) THROW("Can not add document to ASiC-S container which already contains a document."); - + ASiContainer::addDataFile(path, mediaType); } @@ -80,11 +80,11 @@ void ASiC_S::addAdESSignature(istream & /*signature*/) THROW("Not implemented."); } -unique_ptr ASiC_S::openInternal(const string &path) +unique_ptr ASiC_S::openInternal(const string &path, OpenFlags flags) { if (!isContainerSimpleFormat(path)) return nullptr; - DEBUG("ASiC_S::openInternal(%s)", path.c_str()); + DEBUG("ASiC_S::openInternal(%s, %d)", path.c_str(), flags); return unique_ptr(new ASiC_S(path)); } @@ -149,10 +149,10 @@ bool ASiC_S::isTimestampedASiC_S(const vector &list) { DEBUG("isTimestampedASiC_S()"); bool isASiCS = false; - + auto dataFiles = 0; auto hasTimestamp = false; - + // container has only one file in root folder and has a timestamp for(const string &file: list) { @@ -191,7 +191,7 @@ bool ASiC_S::isContainerSimpleFormat(const string &path) { ZipSerialize z(path, false); vector list = z.list(); - if(find(list.begin(), list.end(), "mimetype") != list.end()) + if(find(list.cbegin(), list.cend(), "mimetype") != list.cend()) { stringstream iss; z.extract("mimetype", iss); diff --git a/src/ASiC_S.h b/src/ASiC_S.h index 65e94621f..2e608ec41 100644 --- a/src/ASiC_S.h +++ b/src/ASiC_S.h @@ -44,7 +44,7 @@ namespace digidoc Signature* sign(Signer* signer) override; static std::unique_ptr createInternal(const std::string &path); - static std::unique_ptr openInternal(const std::string &path); + static std::unique_ptr openInternal(const std::string &path, OpenFlags flags); private: ASiC_S(); diff --git a/src/Container.cpp b/src/Container.cpp index 37a8aef5a..1fc846261 100644 --- a/src/Container.cpp +++ b/src/Container.cpp @@ -63,14 +63,15 @@ using namespace digidoc; using namespace std; using namespace xercesc; -using plugin = unique_ptr (*)(const std::string &); +using pluginCreate = unique_ptr (*)(const std::string &); +using pluginOpen = unique_ptr (*)(const std::string &, Container::OpenFlags); namespace digidoc { static string m_appName = "libdigidocpp"; static string m_userAgent = "libdigidocpp"; -static vector m_createList = {}; -static vector m_openList = {}; +static vector m_createList = {}; +static vector m_openList = {}; } /** @@ -349,13 +350,25 @@ Container* Container::open(const string &path) * @throws Exception */ unique_ptr Container::openPtr(const string &path) +{ + return openPtr(path, OpenDefault); +} + +/** + * Opens container from a file + * + * @param path + * @param flags + * @throws Exception + */ +unique_ptr Container::openPtr(const string &path, OpenFlags flags) { for(auto open: m_openList) { - if(unique_ptr container = open(path)) + if(unique_ptr container = open(path, flags)) return container; } - return ASiC_E::openInternal(path); + return ASiC_E::openInternal(path, flags); } /** @@ -418,7 +431,7 @@ unique_ptr Container::openPtr(const string &path) * * It must contain static members: * * static Container* createInternal(const std::string &path); - * * static Container* openInternal(const std::string &path); + * * static Container* openInternal(const std::string &path, OpenFlags flags); * * @see Container::create, Container::open */ diff --git a/src/Container.h b/src/Container.h index c2f9ac6d7..d4bf6f5d8 100644 --- a/src/Container.h +++ b/src/Container.h @@ -43,6 +43,10 @@ DIGIDOCPP_EXPORT std::string version(); class DIGIDOCPP_EXPORT Container { public: + enum OpenFlags { + OpenDefault = 0, + OpenOffline = 1, + }; virtual ~Container(); virtual void save(const std::string &path = "") = 0; @@ -66,6 +70,7 @@ class DIGIDOCPP_EXPORT Container static std::unique_ptr createPtr(const std::string &path); DIGIDOCPP_DEPRECATED static Container* open(const std::string &path); static std::unique_ptr openPtr(const std::string &path); + static std::unique_ptr openPtr(const std::string &path, OpenFlags flags); template static void addContainerImplementation(); diff --git a/src/SiVaContainer.cpp b/src/SiVaContainer.cpp index f6d65d535..e46784037 100644 --- a/src/SiVaContainer.cpp +++ b/src/SiVaContainer.cpp @@ -140,7 +140,7 @@ void SignatureSiVa::validate(const string &policy) const } -SiVaContainer::SiVaContainer(const string &path, const string &ext, bool useHashCode) +SiVaContainer::SiVaContainer(const string &path, const string &ext, bool useHashCode, bool offline) : d(new Private) { DEBUG("SiVaContainer::SiVaContainer(%s, %s, %d)", path.c_str(), ext.c_str(), useHashCode); @@ -159,6 +159,10 @@ SiVaContainer::SiVaContainer(const string &path, const string &ext, bool useHash d->dataFiles.push_back(new DataFilePrivate(move(ifs), File::fileName(path), "application/pdf", File::fileName(path))); } + if(offline) { + return; + } + XMLByte buf[48*100]; string b64; is->clear(); @@ -185,6 +189,7 @@ SiVaContainer::SiVaContainer(const string &path, const string &ext, bool useHash Connect::Result r = Connect(url, "POST", 0, {}, CONF(verifyServiceCerts)).exec({ {"Content-Type", "application/json;charset=UTF-8"} }, (const unsigned char*)req.c_str(), req.size()); + req.clear(); if(!r.isOK() && !r.isStatusCode("400")) THROW("Failed to send request to SiVa"); @@ -254,7 +259,7 @@ SiVaContainer::SiVaContainer(const string &path, const string &ext, bool useHash for(const json &error: signature.value("errors", {})) { string message = error["content"]; - if(message.find("Bad digest for DataFile") == 0 && useHashCode) + if(message.find("Bad digest for DataFile") != string::npos && useHashCode) THROW(message.c_str()); s->_exceptions.emplace_back(EXCEPTION_PARAMS(message.c_str())); } @@ -265,7 +270,7 @@ SiVaContainer::SiVaContainer(const string &path, const string &ext, bool useHash if(message == "X509IssuerName has none or invalid namespace: null" || message == "X509SerialNumber has none or invalid namespace: null") ex.setCode(Exception::IssuerNameSpaceWarning); - else if(message.find("Bad digest for DataFile") == 0) + else if(message.find("Bad digest for DataFile") != string::npos) ex.setCode(Exception::DataFileNameSpaceWarning); else if(message == "Old and unsupported format: SK-XML version: 1.0") continue; @@ -277,10 +282,8 @@ SiVaContainer::SiVaContainer(const string &path, const string &ext, bool useHash SiVaContainer::~SiVaContainer() { - for(const Signature *s: d->signatures) - delete s; - for(const DataFile *f: d->dataFiles) - delete f; + for_each(d->signatures.cbegin(), d->signatures.cend(), default_delete()); + for_each(d->dataFiles.cbegin(), d->dataFiles.cend(), default_delete()); delete d; } @@ -314,7 +317,7 @@ vector SiVaContainer::dataFiles() const return d->dataFiles; } -unique_ptr SiVaContainer::openInternal(const string &path) +unique_ptr SiVaContainer::openInternal(const string &path, OpenFlags flags) { static const set supported = {"PDF", "DDOC"}; string ext = File::fileExtension(path); @@ -322,22 +325,20 @@ unique_ptr SiVaContainer::openInternal(const string &path) if(!supported.count(ext)) return {}; try { - return unique_ptr(new SiVaContainer(path, ext, true)); + return unique_ptr(new SiVaContainer(path, ext, true, flags & OpenOffline)); } catch(const Exception &e) { - if(e.msg().find("Bad digest for DataFile") == 0) - return unique_ptr(new SiVaContainer(path, ext, false)); + if(e.msg().find("Bad digest for DataFile") != string::npos) + return unique_ptr(new SiVaContainer(path, ext, false, flags & OpenOffline)); throw; } } stringstream* SiVaContainer::parseDDoc(istream &is, bool useHashCode) { - auto transcode = [](const XMLCh *chr) { - return xsd::cxx::xml::transcode(chr); - }; + using xsd::cxx::xml::transcode; + using cpXMLCh = const XMLCh*; try { - using cpXMLCh = const XMLCh*; unique_ptr dom(SecureDOMParser().parseIStream(is)); DOMNodeList *nodeList = dom->getElementsByTagName(cpXMLCh(u"DataFile")); for(XMLSize_t i = 0; i < nodeList->getLength(); ++i) @@ -354,7 +355,9 @@ stringstream* SiVaContainer::parseDDoc(istream &is, bool useHashCode) if(const XMLCh *b64 = item->getTextContent()) { d->dataFiles.push_back(new DataFilePrivate(unique_ptr(new stringstream(base64_decode(b64))), - transcode(item->getAttribute(cpXMLCh(u"Filename"))), transcode(item->getAttribute(cpXMLCh(u"MimeType"))), transcode(item->getAttribute(cpXMLCh(u"Id"))))); + transcode(item->getAttribute(cpXMLCh(u"Filename"))), + transcode(item->getAttribute(cpXMLCh(u"MimeType"))), + transcode(item->getAttribute(cpXMLCh(u"Id"))))); } if(!useHashCode) @@ -387,7 +390,7 @@ stringstream* SiVaContainer::parseDDoc(istream &is, bool useHashCode) catch(const XMLException& e) { try { - string result = transcode(e.getMessage()); + string result = transcode(e.getMessage()); THROW("Failed to parse DDoc XML: %s", result.c_str()); } catch(const xsd::cxx::xml::invalid_utf16_string & /* ex */) { THROW("Failed to parse DDoc XML."); @@ -396,7 +399,7 @@ stringstream* SiVaContainer::parseDDoc(istream &is, bool useHashCode) catch(const DOMException& e) { try { - string result = transcode(e.getMessage()); + string result = transcode(e.getMessage()); THROW("Failed to parse DDoc XML: %s", result.c_str()); } catch(const xsd::cxx::xml::invalid_utf16_string & /* ex */) { THROW("Failed to parse DDoc XML."); diff --git a/src/SiVaContainer.h b/src/SiVaContainer.h index 122f8f149..768212b76 100644 --- a/src/SiVaContainer.h +++ b/src/SiVaContainer.h @@ -99,10 +99,10 @@ class SiVaContainer final: public Container Signature* sign(Signer* signer) final; static std::unique_ptr createInternal(const std::string &path); - static std::unique_ptr openInternal(const std::string &path); + static std::unique_ptr openInternal(const std::string &path, OpenFlags flags); private: - SiVaContainer(const std::string &path, const std::string &ext, bool useHashCode); + SiVaContainer(const std::string &path, const std::string &ext, bool useHashCode = true, bool offline = true); DISABLE_COPY(SiVaContainer); std::stringstream* parseDDoc(std::istream &is, bool useHashCode); diff --git a/src/digidoc-tool.1.cmake b/src/digidoc-tool.1.cmake index 44ed91b31..fdfb0a5c5 100644 --- a/src/digidoc-tool.1.cmake +++ b/src/digidoc-tool.1.cmake @@ -24,7 +24,8 @@ Command open: --warnings=(ignore,warning,error) - warning handling --policy=(POLv1,POLv2) - Signature Validation Policy (default POLv2) http://open-eid.github.io/SiVa/siva/appendix/validation_policy/ - --extractAll[=path] - extracts documents (to path when provided) + --extractAll[=path] - extracts documents (to path when provided) + --offline - open container offline (eg. Don't send to SiVa) Command add: Example: digidoc-tool add --file=file1.txt container-file.asice diff --git a/src/digidoc-tool.cpp b/src/digidoc-tool.cpp index 624f88be1..95f2c9fb7 100644 --- a/src/digidoc-tool.cpp +++ b/src/digidoc-tool.cpp @@ -310,7 +310,8 @@ static void printUsage(const char *executable) << " --warnings=(ignore,warning,error) - warning handling" << endl << " --policy=(POLv1,POLv2) - Signature Validation Policy (default POLv2)" << endl << " http://open-eid.github.io/SiVa/siva/appendix/validation_policy/" << endl - << " --extractAll[=path] - extracts documents (to path when provided)" << endl << endl + << " --extractAll[=path] - extracts documents (to path when provided)" << endl << endl + << " --offline - open container offline (eg. Don't send to SiVa)" << endl << endl << " Command add:" << endl << " Example: " << executable << " add --file=file1.txt container-file.asice" << endl << " Available options:" << endl @@ -368,10 +369,10 @@ const map ToolConfig::profiles = { {"time-mark-archive", "time-mark-archive"}, {"time-stamp-archive", "time-stamp-archive"}, }; -string ToolConfig::RED = "\033[31m"; -string ToolConfig::GREEN = "\033[32m"; -string ToolConfig::YELLOW = "\033[33m"; -string ToolConfig::RESET = "\033[0m"; +string ToolConfig::RED = "\e[31m"; +string ToolConfig::GREEN = "\e[32m"; +string ToolConfig::YELLOW = "\e[33m"; +string ToolConfig::RESET = "\e[0m"; ToolConfig::ToolConfig(int argc, char *argv[]) : _logLevel(XmlConfCurrent::logLevel()) @@ -395,8 +396,8 @@ ToolConfig::ToolConfig(int argc, char *argv[]) else if(arg.find("--file=") == 0) { string arg2(i+1 < argc ? decodeParameter(argv[i+1]) : string()); - files.emplace_back(pair(arg.substr(7), - arg2.find("--mime=") == 0 ? arg2.substr(7) : "application/octet-stream")); + files.emplace_back(arg.substr(7), + arg2.find("--mime=") == 0 ? arg2.substr(7) : "application/octet-stream"); } #ifdef _WIN32 else if(arg == "--cng") cng = true; @@ -553,6 +554,7 @@ static int open(int argc, char* argv[]) { ToolConfig::Warning reportwarnings = ToolConfig::WWarning; string path, extractPath, policy; + Container::OpenFlags flags = Container::OpenDefault; int returnCode = EXIT_SUCCESS; // Parse command line arguments. @@ -575,6 +577,8 @@ static int open(int argc, char* argv[]) } else if(arg.find("--policy=") == 0) policy = arg.substr(9); + else if(arg.find("--offline") == 0) + flags = Container::OpenOffline; else path = arg; } @@ -587,7 +591,7 @@ static int open(int argc, char* argv[]) unique_ptr doc; try { - doc = Container::openPtr(path); + doc = Container::openPtr(path, flags); } catch(const Exception &e) { cout << "Failed to parse container" << endl; cout << " Exception:" << endl << e; @@ -1055,6 +1059,7 @@ int main(int argc, char *argv[]) #endif info << ")"; digidoc::initialize("digidoc-tool", info.str()); + std::atexit(&digidoc::terminate); } catch(const Exception &e) { cout << "Failed to initalize library:" << endl; cout << "Caught Exception:" << endl << e; @@ -1064,39 +1069,32 @@ int main(int argc, char *argv[]) if(argc < 2) { printUsage(argv[0]); - digidoc::terminate(); return EXIT_SUCCESS; } - int returnCode = EXIT_FAILURE; try { string command(argv[1]); if(command == "open") - returnCode = open(argc, argv); - else if(command == "create") - returnCode = create(*conf, argv[0]); - else if(command == "add") - returnCode = add(*conf, argv[0]); - else if(command == "createBatch") - returnCode = createBatch(*conf, argv[0]); - else if(command == "remove") - returnCode = remove(argc, argv); - else if(command == "sign") - returnCode = sign(*conf, argv[0]); - else if(command == "websign") - returnCode = websign(*conf, argv[0]); - else if(command == "tsl") - returnCode = tslcmd(argc, argv); - else if(command == "version") - returnCode = EXIT_SUCCESS; - else - printUsage(argv[0]); + return open(argc, argv); + if(command == "create") + return create(*conf, argv[0]); + if(command == "add") + return add(*conf, argv[0]); + if(command == "createBatch") + return createBatch(*conf, argv[0]); + if(command == "remove") + return remove(argc, argv); + if(command == "sign") + return sign(*conf, argv[0]); + if(command == "websign") + return websign(*conf, argv[0]); + if(command == "tsl") + return tslcmd(argc, argv); + if(command == "version") + return EXIT_SUCCESS; + printUsage(argv[0]); } catch(const Exception &e) { cout << "Caught Exception:" << endl << e; - returnCode = EXIT_FAILURE; } - - digidoc::terminate(); - - return returnCode; + return EXIT_FAILURE; }