Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
213 changes: 95 additions & 118 deletions doc/digidoc-tool.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/ASiC_S.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ ASiC_S::ASiC_S(const string &path, bool create)
continue;
else if(const auto directory = File::directory(file);
!directory.empty() && directory != "/" && directory != "./")
THROW("Subfolders are not supported %s", directory.c_str());
THROW("Subfolders are not supported %.*s", STR_VIEW_FMT(directory));
else if(!dataFiles().empty())
THROW("Can not add document to ASiC-S container which already contains a document.");
else
Expand Down
10 changes: 9 additions & 1 deletion src/DataFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,15 @@ void DataFilePrivate::saveAs(const string& path) const
void DataFilePrivate::saveAs(ostream &os) const
{
m_is->clear();
m_is->seekg(0);
bool seekFailed = !m_is->seekg(0);
if(seekFailed)
m_is->clear();
if(m_is->peek() == istream::traits_type::eof())
{
if(seekFailed || m_is->bad() || d->size.value_or(0) != 0)
THROW("Failed to read data file '%s'.", m_filename.c_str());
return;
}
os << m_is->rdbuf();
}

Expand Down
20 changes: 11 additions & 9 deletions src/util/File.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <algorithm>
#include <charconv>
#include <ctime>
#include <iomanip>
#include <locale>
#include <sstream>
#include <sys/stat.h>
Expand Down Expand Up @@ -187,10 +188,10 @@ string File::frameworkResourcesPath(string_view name)
* @param path full path of the file.
* @return returns directory part of the file full path.
*/
string File::directory(const string& path)
string_view File::directory(string_view path)
{
size_t pos = path.find_last_of("/\\");
return pos == string::npos ? string() : path.substr(0, pos);
return pos == string::npos ? string_view() : path.substr(0, pos);
}

/**
Expand Down Expand Up @@ -243,12 +244,12 @@ fs::path File::tempFileName()
* @param path full path of the directory created.
* @throws IOException exception is thrown if the directory creation failed.
*/
void File::createDirectory(string path)
void File::createDirectory(string_view path)
{
if(path.empty())
THROW("Can not create directory with no name.");
if(path.back() == '/' || path.back() == '\\')
path.pop_back();
path.remove_suffix(1);
auto _path = encodeName(path);
#ifdef _WIN32
int result = _wmkdir(_path.c_str());
Expand All @@ -257,13 +258,13 @@ void File::createDirectory(string path)
#endif
if(result == 0 || errno == EEXIST)
{
DEBUG("Created directory or directory exists '%s'", path.c_str());
DEBUG("Created directory or directory exists '%.*s'", STR_VIEW_FMT(path));
return;
}
if(errno != ENOENT)
THROW("Failed to create directory '%s', errno = %d", path.c_str(), errno);
THROW("Failed to create directory '%.*s', errno = %d", STR_VIEW_FMT(path), errno);
createDirectory(directory(path));
createDirectory(std::move(path));
createDirectory(path);
}

string File::digidocppPath()
Expand Down Expand Up @@ -318,7 +319,7 @@ string File::toUriPath(const string &path)
if(isalnum(i, locC) || unreserved.find(i) != string::npos)
dst << i;
else
dst << '%' << hex << uppercase << (static_cast<int>(i) & 0xFF);
dst << '%' << hex << uppercase << setw(2) << setfill('0') << (static_cast<int>(i) & 0xFF);
}
return dst.str();
}
Expand All @@ -328,7 +329,8 @@ constexpr bool fromHexChar(auto pos, auto end, auto &value)
if(distance(pos, end) < 2)
return false;
auto *p = to_address(pos);
return from_chars(p, p + 2, value, 16).ec == errc{};
auto [ptr, ec] = from_chars(p, p + 2, value, 16);
return ec == errc{} && ptr == p + 2;
}

string File::fromUriPath(string_view path)
Expand Down
4 changes: 2 additions & 2 deletions src/util/File.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ namespace digidoc
static bool fileExtension(std::string_view path, std::initializer_list<std::string_view> list);
static unsigned long fileSize(const std::filesystem::path &path) noexcept;
static std::string_view fileName(std::string_view path) noexcept;
static std::string directory(const std::string& path);
static std::string_view directory(std::string_view path);
static std::string path(std::string dir, std::string_view relativePath);
static std::filesystem::path tempFileName();
static void createDirectory(std::string path);
static void createDirectory(std::string_view path);
static std::string toUriPath(const std::string &path);
static std::string fromUriPath(std::string_view path);
static std::vector<unsigned char> hexToBin(std::string_view in);
Expand Down
48 changes: 48 additions & 0 deletions test/libdigidocpp_boost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,40 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(constructor, Doc, DocTypes)
BOOST_CHECK_EQUAL(d->mediaType(), Doc::TYPE);
}

BOOST_AUTO_TEST_CASE(emptyDataFile)
{
auto d = Container::createPtr("empty-datafile.asice");
d->addDataFile(make_unique<stringstream>(), "empty.txt", "text/plain");
d->addDataFile(make_unique<stringstream>("data"), "data.txt", "text/plain");
for(const DataFile *file: d->dataFiles())
{
ostringstream os;
file->saveAs(os);
BOOST_CHECK(os.good());
BOOST_CHECK_EQUAL(os.str().size(), file->fileSize());
}
}

BOOST_AUTO_TEST_CASE(nonSeekableDataFile)
{
struct NonSeekableBuf: stringbuf
{
using stringbuf::stringbuf;
pos_type seekoff(off_type, ios_base::seekdir, ios_base::openmode) override { return pos_type(off_type(-1)); }
pos_type seekpos(pos_type, ios_base::openmode) override { return pos_type(off_type(-1)); }
} buf("data");

auto d = Container::createPtr("nonseekable-datafile.asice");
d->addDataFile(make_unique<istream>(&buf), "data.txt", "text/plain");
ostringstream os;
d->dataFiles().front()->saveAs(os);
BOOST_CHECK_EQUAL(os.str(), "data");
BOOST_CHECK(os.good());

ostringstream exhausted;
BOOST_CHECK_THROW(d->dataFiles().front()->saveAs(exhausted), Exception);
}

BOOST_AUTO_TEST_CASE_TEMPLATE(document, Doc, DocTypes)
{
auto d = Container::createPtr("test." + Doc::EXT);
Expand Down Expand Up @@ -505,6 +539,15 @@ BOOST_AUTO_TEST_CASE(XmlConfCase) {
BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(FileUtilSuite)
BOOST_AUTO_TEST_CASE(ToUriPathEscapesSingleDigitBytes)
{
const string fileName = "file\n\t.txt";
const string uriPath = util::File::toUriPath(fileName);

BOOST_CHECK_EQUAL(uriPath, "file%0A%09.txt");
BOOST_CHECK_EQUAL(util::File::fromUriPath(uriPath), fileName);
}

BOOST_AUTO_TEST_CASE(FromUriPathConvertsAsciiEncodingToCharacters)
{
const string asciiEncodedStr = "%3dtest%20%40";
Expand Down Expand Up @@ -535,6 +578,11 @@ BOOST_AUTO_TEST_CASE(FromUriPathPreservesTrailingPercentageSign)
BOOST_CHECK_EQUAL(expectedDecodedStr, result);
}

BOOST_AUTO_TEST_CASE(FromUriPathPreservesInvalidEscape)
{
BOOST_CHECK_EQUAL(util::File::fromUriPath("file%A.txt"), "file%A.txt");
}

BOOST_AUTO_TEST_CASE(HexToBinConvertsHexEncodedStringToBinaryData)
{
const string asciiEncodedStr = "3031323334";
Expand Down