diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8ba287acc..74d620175 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -233,7 +233,9 @@ target_link_libraries(digidocpp PRIVATE ${CMAKE_DL_LIBS} minizip digidocpp_priv) if( BUILD_TOOLS ) add_executable(digidoc-tool digidoc-tool.rc digidoc-tool.cpp) - target_link_libraries(digidoc-tool digidocpp digidocpp_priv Threads::Threads) + target_link_libraries(digidoc-tool digidocpp digidocpp_priv Threads::Threads + "$<$,$,9.0>>:-lstdc++fs>" + ) configure_file( digidoc-tool.1.cmake digidoc-tool.1 ) endif() diff --git a/src/digidoc-tool.cpp b/src/digidoc-tool.cpp index 499fc9f6b..ec3d4c18e 100644 --- a/src/digidoc-tool.cpp +++ b/src/digidoc-tool.cpp @@ -51,6 +51,13 @@ using namespace digidoc; using namespace digidoc::util; using namespace std; +#if __has_include() +#include +namespace fs = std::filesystem; +#else +#include +namespace fs = std::experimental::filesystem; +#endif namespace std { @@ -256,23 +263,18 @@ class ToolConfig: public XmlConfCurrent unique_ptr getSigner(bool getwebsigner = false) const; static string decodeParameter(const string ¶m) { - if(param.empty()) - return {}; -#ifdef _WIN32 - int len = MultiByteToWideChar(CP_ACP, 0, param.data(), int(param.size()), nullptr, 0); - wstring out(size_t(len), 0); - len = MultiByteToWideChar(CP_ACP, 0, param.data(), int(param.size()), &out[0], len); - return File::decodeName(out); -#else - return File::decodeName(param); -#endif + return File::decodeName(fs::path(param)); } // Config - int _logLevel; - bool expired = false; - vector tslcerts; - string _logFile, tsurl, tslurl, uri, siguri; + int _logLevel = XmlConfCurrent::logLevel(); + bool expired = XmlConfCurrent::TSLAllowExpired(); + vector tslcerts = XmlConfCurrent::TSLCerts(); + string _logFile = XmlConfCurrent::logFile(); + string tsurl = XmlConfCurrent::TSUrl(); + string tslurl = XmlConfCurrent::TSLUrl(); + string uri = XmlConfCurrent::digestUri(); + string siguri = XmlConfCurrent::signatureDigestUri(); // Params string path, profile, pkcs11, pkcs12, pin, city, street, state, postalCode, country, cert; @@ -375,14 +377,6 @@ string ToolConfig::YELLOW = "\033[33m"; string ToolConfig::RESET = "\033[0m"; ToolConfig::ToolConfig(int argc, char *argv[]) - : _logLevel(XmlConfCurrent::logLevel()) - , expired(XmlConfCurrent::TSLAllowExpired()) - , tslcerts(XmlConfCurrent::TSLCerts()) - , _logFile(XmlConfCurrent::logFile()) - , tsurl(XmlConfCurrent::TSUrl()) - , tslurl(XmlConfCurrent::TSLUrl()) - , uri(XmlConfCurrent::digestUri()) - , siguri(XmlConfCurrent::signatureDigestUri()) { for(int i = 2; i < argc; i++) { @@ -617,74 +611,68 @@ static int open(int argc, char* argv[]) return EXIT_SUCCESS; }; - try { - if(!extractPath.empty() && !validateOnExtract) - return extractFiles(extractPath); + if(!extractPath.empty() && !validateOnExtract) + return extractFiles(extractPath); - cout << "Container file: " << path << endl; - cout << "Container type: " << doc->mediaType() << endl; + cout << "Container file: " << path << endl; + cout << "Container type: " << doc->mediaType() << endl; - // Print container document list. - cout << "Documents (" << doc->dataFiles().size() << "):\n" << endl; - for(const DataFile *file: doc->dataFiles()) + // Print container document list. + cout << "Documents (" << doc->dataFiles().size() << "):\n" << endl; + for(const DataFile *file: doc->dataFiles()) + { + cout << " Document (" << file->mediaType() << "): " << file->fileName() + << " (" << file->fileSize() << " bytes)" << endl; + } + + // Print container signatures list. + cout << endl << "Signatures (" << doc->signatures().size() << "):" << endl; + unsigned int pos = 0; + for(const Signature *s: doc->signatures()) + { + cout << " Signature " << pos++ << " (" << s->profile().c_str() << "):" << endl; + // Validate signature. Checks, whether signature format is correct + // and signed documents checksums are correct. + if(validateSignature(s, reportwarnings) == EXIT_FAILURE) + returnCode = EXIT_FAILURE; + + // Get signature production place info. + if(!s->city().empty() || !s->stateOrProvince().empty() || !s->streetAddress().empty() || !s->postalCode().empty() || !s->countryName().empty()) { - cout << " Document (" << file->mediaType() << "): " << file->fileName() - << " (" << file->fileSize() << " bytes)" << endl; + cout << " Signature production place:" << endl + << " City: " << s->city() << endl + << " State or Province: " << s->stateOrProvince() << endl + << " Street address: " << s->streetAddress() << endl + << " Postal code: " << s->postalCode() << endl + << " Country: " << s->countryName() << endl; } - // Print container signatures list. - cout << endl << "Signatures (" << doc->signatures().size() << "):" << endl; - unsigned int pos = 0; - for(const Signature *s: doc->signatures()) + // Get signer role info. + vector roles = s->signerRoles(); + if(!roles.empty()) { - cout << " Signature " << pos++ << " (" << s->profile().c_str() << "):" << endl; - // Validate signature. Checks, whether signature format is correct - // and signed documents checksums are correct. - if(validateSignature(s, reportwarnings) == EXIT_FAILURE) - returnCode = EXIT_FAILURE; - - // Get signature production place info. - if(!s->city().empty() || !s->stateOrProvince().empty() || !s->streetAddress().empty() || !s->postalCode().empty() || !s->countryName().empty()) - { - cout << " Signature production place:" << endl - << " City: " << s->city() << endl - << " State or Province: " << s->stateOrProvince() << endl - << " Street address: " << s->streetAddress() << endl - << " Postal code: " << s->postalCode() << endl - << " Country: " << s->countryName() << endl; - } - - // Get signer role info. - vector roles = s->signerRoles(); - if(!roles.empty()) - { - cout << " Signer role(s):" << endl; - for(const string &role : roles) - cout << " " << role << endl; - } - - vector msgImprint = s->messageImprint(); - cout << " EPES policy: " << s->policy() << endl - << " SPUri: " << s->SPUri() << endl - << " Signature method: " << s->signatureMethod() << endl - << " Signing time: " << s->claimedSigningTime() << endl - << " Signing cert: " << s->signingCertificate() << endl - << " Signed by: " << s->signedBy() << endl - << " Produced At: " << s->OCSPProducedAt() << endl - << " OCSP Responder: " << s->OCSPCertificate() << endl - << " Message imprint (" << msgImprint.size() << "): " << msgImprint << endl - << " TS: " << s->TimeStampCertificate() << endl - << " TS time: " << s->TimeStampTime() << endl - << " TSA: " << s->ArchiveTimeStampCertificate() << endl - << " TSA time: " << s->ArchiveTimeStampTime() << endl; + cout << " Signer role(s):" << endl; + for(const string &role : roles) + cout << " " << role << endl; } - if(returnCode == EXIT_SUCCESS && !extractPath.empty()) - return extractFiles(extractPath); - } catch(const Exception &e) { - cout << "Caught Exception:" << endl << e; - returnCode = EXIT_FAILURE; - } + vector msgImprint = s->messageImprint(); + cout << " EPES policy: " << s->policy() << endl + << " SPUri: " << s->SPUri() << endl + << " Signature method: " << s->signatureMethod() << endl + << " Signing time: " << s->claimedSigningTime() << endl + << " Signing cert: " << s->signingCertificate() << endl + << " Signed by: " << s->signedBy() << endl + << " Produced At: " << s->OCSPProducedAt() << endl + << " OCSP Responder: " << s->OCSPCertificate() << endl + << " Message imprint (" << msgImprint.size() << "): " << msgImprint << endl + << " TS: " << s->TimeStampCertificate() << endl + << " TS time: " << s->TimeStampTime() << endl + << " TSA: " << s->ArchiveTimeStampCertificate() << endl + << " TSA time: " << s->ArchiveTimeStampTime() << endl; + } + if(returnCode == EXIT_SUCCESS && !extractPath.empty()) + return extractFiles(extractPath); return returnCode; } @@ -725,33 +713,22 @@ static int remove(int argc, char *argv[]) return EXIT_FAILURE; } - try { - if(!signatures.empty()) - { - sort(signatures.begin(), signatures.end(), greater()); - for(vector::const_iterator i = signatures.begin(); i != signatures.end(); ++i) - { - cout << " Removing signature " << *i << endl; - doc->removeSignature(*i); - } - } - - if(!documents.empty()) - { - sort(documents.begin(), documents.end(), greater()); - for(vector::const_iterator i = documents.begin(); i != documents.end(); ++i) - { - cout << " Removing document " << *i << endl; - doc->removeDataFile(*i); - } - } - - doc->save(); + sort(signatures.begin(), signatures.end(), greater()); + for(unsigned int i : signatures) + { + cout << " Removing signature " << i << endl; + doc->removeSignature(i); + } - return EXIT_SUCCESS; - } catch(const Exception &e) { cout << "Caught Exception:" << endl << e; } + sort(documents.begin(), documents.end(), greater()); + for(unsigned int i : documents) + { + cout << " Removing document " << i << endl; + doc->removeDataFile(i); + } - return EXIT_FAILURE; + doc->save(); + return EXIT_SUCCESS; } @@ -762,7 +739,7 @@ static int remove(int argc, char *argv[]) * @param program command line argument. * @return EXIT_FAILURE (1) - failure, EXIT_SUCCESS (0) - success */ -static int add(const ToolConfig &p, char *program) +static int add(const ToolConfig &p, const char *program) { if(p.path.empty() || p.files.empty()) { @@ -779,16 +756,10 @@ static int add(const ToolConfig &p, char *program) return EXIT_FAILURE; } - try { - for(const pair &file: p.files) - doc->addDataFile(file.first, file.second); - - doc->save(); - - return EXIT_SUCCESS; - } catch(const Exception &e) { cout << "Caught Exception:" << endl << e; } - - return EXIT_FAILURE; + for(const pair &file: p.files) + doc->addDataFile(file.first, file.second); + doc->save(); + return EXIT_SUCCESS; } /** @@ -825,7 +796,7 @@ static int signContainer(Container *doc, const unique_ptr &signer, bool * @param program command line argument. * @return EXIT_FAILURE (1) - failure, EXIT_SUCCESS (0) - success */ -static int create(const ToolConfig &p, char *program) +static int create(const ToolConfig &p, const char *program) { if(p.path.empty() || p.files.empty()) { @@ -842,19 +813,14 @@ static int create(const ToolConfig &p, char *program) return EXIT_FAILURE; } - try { - for(const pair &file: p.files) - doc->addDataFile(file.first, file.second); + for(const pair &file: p.files) + doc->addDataFile(file.first, file.second); - int returnCode = EXIT_SUCCESS; - if(p.doSign) - returnCode = signContainer(doc.get(), p.getSigner(), p.dontValidate); - doc->save(); - return returnCode; - } catch(const Exception &e) { - cout << "Caught Exception:" << endl << e; - return EXIT_FAILURE; - } + int returnCode = EXIT_SUCCESS; + if(p.doSign) + returnCode = signContainer(doc.get(), p.getSigner(), p.dontValidate); + doc->save(); + return returnCode; } /** @@ -864,7 +830,7 @@ static int create(const ToolConfig &p, char *program) * @param program command line argument. * @return EXIT_FAILURE (1) - failure, EXIT_SUCCESS (0) - success */ -static int createBatch(const ToolConfig &p, char *program) +static int createBatch(const ToolConfig &p, const char *program) { if(p.path.empty()) { @@ -880,15 +846,22 @@ static int createBatch(const ToolConfig &p, char *program) return EXIT_FAILURE; } + std::error_code ec; + fs::directory_iterator it{fs::u8path(p.path), ec}; + if(ec) + { + cout << "Failed to open directory %s" << p.path << endl; + return EXIT_FAILURE; + } int returnCode = EXIT_SUCCESS; - for(const string &file: File::listFiles(p.path)) + for(const auto &file: it) { - if(file.compare(file.size() - 6, 6, ".asice") == 0) + if(!fs::is_regular_file(file.status()) || file.path().extension() == ".asice") continue; - cout << "Signing file: " << file << endl; + cout << "Signing file: " << file.path().u8string() << endl; try { - unique_ptr doc = Container::createPtr(file + ".asice"); - doc->addDataFile(file, "application/octet-stream"); + unique_ptr doc = Container::createPtr(file.path().u8string() + ".asice"); + doc->addDataFile(file.path().u8string(), "application/octet-stream"); if(signContainer(doc.get(), signer, p.dontValidate) == EXIT_FAILURE) returnCode = EXIT_FAILURE; doc->save(); @@ -908,7 +881,7 @@ static int createBatch(const ToolConfig &p, char *program) * @param program command line argument. * @return EXIT_FAILURE (1) - failure, EXIT_SUCCESS (0) - success */ -static int sign(const ToolConfig &p, char *program) +static int sign(const ToolConfig &p, const char *program) { if(p.path.empty()) { @@ -925,17 +898,12 @@ static int sign(const ToolConfig &p, char *program) return EXIT_FAILURE; } - try { - int returnCode = signContainer(doc.get(), p.getSigner(), p.dontValidate); - doc->save(); - return returnCode; - } catch(const Exception &e) { - cout << "Caught Exception:" << endl << e; - return EXIT_FAILURE; - } + int returnCode = signContainer(doc.get(), p.getSigner(), p.dontValidate); + doc->save(); + return returnCode; } -static int websign(const ToolConfig &p, char *program) +static int websign(const ToolConfig &p, const char *program) { if(p.path.empty()) { @@ -1045,68 +1013,55 @@ static int tslcmd(int /*argc*/, char* /*argv*/[]) * @param argv command line arguments. * @return EXIT_FAILURE (1) - failure, EXIT_SUCCESS (0) - success */ -int main(int argc, char *argv[]) +int main(int argc, char *argv[]) try { printf("Version\n"); printf(" digidoc-tool version: %s\n", FILE_VER_STR); printf(" libdigidocpp version: %s\n", version().c_str()); ToolConfig *conf = nullptr; - try { - Conf::init(conf = new ToolConfig(argc, argv)); - stringstream info; - info << "digidoc-tool/" << FILE_VER_STR << " ("; + Conf::init(conf = new ToolConfig(argc, argv)); + stringstream info; + info << "digidoc-tool/" << FILE_VER_STR << " ("; #ifdef _WIN32 - info << "Windows"; + info << "Windows"; #elif __APPLE__ - info << "OS X"; + info << "OS X"; #else - info << "Unknown"; + info << "Unknown"; #endif - info << ")"; - digidoc::initialize("digidoc-tool", info.str()); - } catch(const Exception &e) { - cout << "Failed to initalize library:" << endl; - cout << "Caught Exception:" << endl << e; - return EXIT_FAILURE; - } + info << ")"; + digidoc::initialize("digidoc-tool", info.str()); + std::atexit(&digidoc::terminate); 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]); - } catch(const Exception &e) { - cout << "Caught Exception:" << endl << e; - returnCode = EXIT_FAILURE; - } - - digidoc::terminate(); - - return returnCode; + string_view command(argv[1]); + if(command == "open") + 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]); + return EXIT_FAILURE; +} catch(const Exception &e) { + cout << "Caught Exception:" << endl << e; + return EXIT_FAILURE; } diff --git a/src/util/File.cpp b/src/util/File.cpp index a50e720a8..9c8c7ac8e 100644 --- a/src/util/File.cpp +++ b/src/util/File.cpp @@ -17,9 +17,6 @@ * */ -// This file has platform-specific implementations. -// Treat all POSIX systems (Linux, MAC) the same way. Treat non-POSIX as Windows. - #include "File.h" #include "log.h" @@ -300,7 +297,6 @@ unsigned long File::fileSize(const string &path) return f_stat(encodeName(path).c_str(), &fileInfo) ? 0 : (unsigned long)fileInfo.st_size; } - /** * Parses file path and returns file name from file full path. * @@ -446,80 +442,6 @@ bool File::isRelative(const string &path) return true; } -/** - * Returns list of files (and empty directories, if listEmptyDirectories is set) - * found in the directory directory. - * - * @param directory full path of the directory. - * @throws IOException throws exception if the directory listing failed. - */ -vector File::listFiles(const string& directory) -{ - vector files; - -#ifdef _POSIX_VERSION - string _directory = encodeName(directory); - DIR* pDir = opendir(_directory.c_str()); - if(!pDir) - THROW("Failed to open directory '%s'", _directory.c_str()); - - char fullPath[MAXPATHLEN]; - struct stat info; - for(dirent *entry = readdir(pDir); entry; entry = readdir(pDir)) - { - static const string dot("."); - static const string dotdot(".."); - if(dot == entry->d_name || dotdot == entry->d_name) - continue; - - sprintf(fullPath, "%s/%s", _directory.c_str(), entry->d_name); - if(entry->d_type == 0x08 || (lstat(fullPath, &info) != 0 && S_ISREG(info.st_mode))) - files.push_back(path(directory, decodeName(entry->d_name))); - } - - closedir(pDir); -#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) - WIN32_FIND_DATAW findFileData; - HANDLE hFind = nullptr; - - try - { - if ( directory.size() > MAX_PATH ) - { - // MSDN: "Relative paths are limited to MAX_PATH characters." - can this be true? - THROW("Directory path '%s' exceeds the limit %d", directory.c_str(), MAX_PATH); - } - - wstring findPattern = encodeName(directory + "\\*"); - hFind = ::FindFirstFileW(findPattern.c_str(), &findFileData); - if (hFind == INVALID_HANDLE_VALUE) - THROW("Listing contents of directory '%s' failed with error %d", directory.c_str(), ::GetLastError()); - - do - { - wstring fileName(findFileData.cFileName); - if ( fileName == L"." || fileName == L".." ) - continue; // skip those too - - if(!(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) - files.push_back(path(directory, decodeName(fileName))); - } while ( ::FindNextFileW(hFind, &findFileData) != FALSE ); - - // double-check for errors - if ( ::GetLastError() != ERROR_NO_MORE_FILES ) - THROW("Listing contents of directory '%s' failed with error %d", directory.c_str(), ::GetLastError()); - - ::FindClose(hFind); - } - catch (...) - { - ::FindClose(hFind); - throw; - } -#endif - return files; -} - /** * Constructs the full file path in the format "file:///fullpath" in URI encoding. * @@ -527,7 +449,6 @@ vector File::listFiles(const string& directory) * @param relativeFilePath file name to be appended to the full path * @return full file path in the format "file:///fullpath" in URI encoding. */ - string File::fullPathUrl(const string &path) { #ifdef _WIN32 diff --git a/src/util/File.h b/src/util/File.h index 0e61f0af5..40eb9fbbc 100644 --- a/src/util/File.h +++ b/src/util/File.h @@ -56,7 +56,6 @@ namespace digidoc static std::string fullPathUrl(const std::string &path); static std::string tempFileName(); static void createDirectory(const std::string& path); - static std::vector listFiles(const std::string& directory); static void deleteTempFiles(); static bool removeFile(const std::string &path); static std::string toUri(const std::string &path);