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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ The release run heads these entries with the version and opens a fresh

## Unreleased

- Python, Java and Apple file/image/font reads report stream failures instead
of returning partial data after a ZIP checksum or other read error.

- Android packaging rejects ABI directories missing either native library or
containing empty libraries. JNI jar configuration now requires its actual JDK 17 minimum.

Expand Down
14 changes: 3 additions & 11 deletions apple/src/ODRFile.mm
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#include <odr/file.hpp>
#include <odr/odr.hpp>

#include <istream>
#include <optional>
#include <sstream>
#include <vector>
Expand Down Expand Up @@ -289,10 +288,7 @@ - (nullable NSString *)diskPath {
}

- (nullable NSData *)dataWithError:(NSError **)error {
return guarded(error, [&]() -> NSData * {
const std::unique_ptr<std::istream> stream = _handle->stream();
return to_nsdata(*stream);
});
return guarded(error, [&]() -> NSData * { return to_nsdata(*_handle); });
}

- (BOOL)copyTo:(NSString *)path error:(NSError **)error {
Expand Down Expand Up @@ -698,9 +694,7 @@ @implementation ODRImageFile

- (nullable NSData *)dataWithError:(NSError **)error {
return guarded(error, [&]() -> NSData * {
const std::unique_ptr<std::istream> stream =
self.handle.as_image_file().stream();
return to_nsdata(*stream);
return to_nsdata(self.handle.as_image_file().file());
});
}

Expand Down Expand Up @@ -809,9 +803,7 @@ @implementation ODRFontFile

- (nullable NSData *)dataWithError:(NSError **)error {
return guarded(error, [&]() -> NSData * {
const std::unique_ptr<std::istream> stream =
self.handle.as_font_file().stream();
return to_nsdata(*stream);
return to_nsdata(self.handle.as_font_file().file());
});
}

Expand Down
8 changes: 6 additions & 2 deletions apple/src/ODRInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@

NS_ASSUME_NONNULL_BEGIN

namespace odr {
class File;
}

namespace odr::apple {

/// Real UTF-8 <-> UTF-16, the way `odr_jni::to_string` does it. Never route
Expand All @@ -28,9 +32,9 @@ std::string to_string(NSString *string);
NSString *to_nsstring(const std::string &string);
NSString *to_nsstring(std::string_view string);

/// Bytes as an `NSData`, drained from a stream where odrcore hands one out.
/// Copies bytes into `NSData`; file reads propagate I/O errors.
NSData *to_nsdata(const std::string &bytes);
NSData *to_nsdata(std::istream &stream);
NSData *to_nsdata(const File &file);

/// Fills `*error` from the exception currently being handled. Call only from
/// inside a `catch`.
Expand Down
6 changes: 3 additions & 3 deletions apple/src/ODRInternal.mm
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
#include <odr/error_code.hpp>

#include <odr/exceptions.hpp>
#include <odr/file.hpp>

#include <exception>
#include <istream>
#include <sstream>

NSErrorDomain const ODRErrorDomain = @"app.opendocument.OdrCore.ErrorDomain";
Expand Down Expand Up @@ -53,9 +53,9 @@
return [NSData dataWithBytes:bytes.data() length:bytes.size()];
}

NSData *apple::to_nsdata(std::istream &stream) {
NSData *apple::to_nsdata(const File &file) {
std::ostringstream buffer;
buffer << stream.rdbuf();
file.pipe(buffer);
return to_nsdata(std::move(buffer).str());
}

Expand Down
4 changes: 2 additions & 2 deletions jni/src/jni_file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ Java_app_opendocument_core_ImageFile_readNative(JNIEnv *env, jobject,
jlong handle) {
return guarded(env, [&] {
std::ostringstream out;
out << decoded(handle).as_image_file().stream()->rdbuf();
decoded(handle).as_image_file().file().pipe(out);
return to_jbytes(env, out.str());
});
}
Expand Down Expand Up @@ -523,7 +523,7 @@ Java_app_opendocument_core_FontFile_readNative(JNIEnv *env, jobject,
jlong handle) {
return guarded(env, [&] {
std::ostringstream out;
out << decoded(handle).as_font_file().stream()->rdbuf();
decoded(handle).as_font_file().file().pipe(out);
return to_jbytes(env, out.str());
});
}
Expand Down
4 changes: 2 additions & 2 deletions python/src/bind_file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ void odr_python::bind_file(py::module_ &m) {
py::class_<odr::ImageFile, odr::DecodedFile>(m, "ImageFile")
.def("read", [](const odr::ImageFile &file) {
std::ostringstream out;
out << file.stream()->rdbuf();
file.file().pipe(out);
return py::bytes(out.str());
});

Expand Down Expand Up @@ -421,7 +421,7 @@ void odr_python::bind_file(py::module_ &m) {
py::class_<odr::FontFile, odr::DecodedFile>(m, "FontFile")
.def("read", [](const odr::FontFile &file) {
std::ostringstream out;
out << file.stream()->rdbuf();
file.file().pipe(out);
return py::bytes(out.str());
});

Expand Down
16 changes: 16 additions & 0 deletions python/tests/test_file.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import io
import zipfile

import pytest
Expand Down Expand Up @@ -251,3 +252,18 @@ def test_a_markdown_file_holds_a_text_file_too(tmp_path):
assert file.is_markdown_file()
assert file.as_markdown_file().text_file().text() == "# hello\n"
assert file.as_markdown_file().document().document_type() == pyodr.DocumentType.text


def test_image_read_rejects_archive_corruption():
output = io.BytesIO()
with zipfile.ZipFile(output, "w") as zip_file:
zip_file.writestr("image.png", b"\x89PNG\r\n\x1a\n" + b"x" * 1_000_000)
corrupt = bytearray(output.getvalue())
# past the bytes the format detection reads, so only the full read fails
corrupt[corrupt.index(b"x") + 900_000] ^= 1
archive = (
pyodr.open(pyodr.File.from_memory(bytes(corrupt))).as_archive_file().archive()
)
image = pyodr.open(archive.as_filesystem().open("/image.png")).as_image_file()
with pytest.raises(RuntimeError, match="stream read failed"):
image.read()
Loading