From: Vibhav Pant vibhavp@gmail.com
--- dlls/opcservices/compress.c | 189 ++++++++++++++++++++++++++- dlls/opcservices/tests/opcservices.c | 2 +- include/opcbase.idl | 2 + 3 files changed, 188 insertions(+), 5 deletions(-)
diff --git a/dlls/opcservices/compress.c b/dlls/opcservices/compress.c index 7098734ab3a..dbd928d8aef 100644 --- a/dlls/opcservices/compress.c +++ b/dlls/opcservices/compress.c @@ -55,6 +55,14 @@ struct data_descriptor DWORD uncompressed_size; };
+struct data_descriptor64 +{ + DWORD signature; + DWORD crc32; + ULONG64 compressed_size; + ULONG64 uncompressed_size; +}; + struct central_directory_header { DWORD signature; @@ -424,6 +432,9 @@ HRESULT compress_add_file(struct zip_archive *archive, const WCHAR *path, struct zip_entry { OPC_COMPRESSION_OPTIONS opt; + ULONG64 compressed_size; + ULONG64 uncompressed_size; + DWORD crc32; ULARGE_INTEGER local_file_offset; };
@@ -626,6 +637,8 @@ static HRESULT zip_part_set_create(IOpcPart **parts, size_t len, IOpcPartSet **o struct zip_part { IOpcPart IOpcPart_iface; + IStream *archive; + IStream *content; struct zip_entry entry; IOpcPartUri *name; LONG ref; @@ -669,6 +682,8 @@ static ULONG WINAPI zip_part_Release(IOpcPart *iface)
if (!ref) { + IStream_Release(part->archive); + if (part->content) IStream_Release(part->content); IOpcPartUri_Release(part->name); free(part); } @@ -682,11 +697,172 @@ static HRESULT WINAPI zip_part_GetRelationshipSet(IOpcPart *iface, IOpcRelations return E_NOTIMPL; }
+static BOOL compress_validate_part_size(const struct zip_entry *entry, const struct local_file_header *file, + const struct zip64_extended_field *ext) +{ + if (file->flags & USE_DATA_DESCRIPTOR) + return !(file->compressed_size || file->uncompressed_size || file->crc32 || ext->compressed_size || + ext->uncompressed_size); + else + { + ULONG64 compressed_size = file->compressed_size == UINT32_MAX ? ext->compressed_size : file->compressed_size; + ULONG64 uncompressed_size = file->uncompressed_size == UINT32_MAX ? ext->uncompressed_size : file->uncompressed_size; + + return compressed_size == entry->compressed_size && uncompressed_size == entry->uncompressed_size && + file->crc32 == entry->crc32; + } +} + +static HRESULT part_create_content_stream(IStream *archive, const struct zip_entry *entry, IStream **out) +{ + ULONG64 compressed_data_read = 0, data_uncompressed = 0; + ULONG ext_size = 0, read, crc32 = 0, exp_crc32 = 0; + static const ULONG input_buffer_size = 0x8000; + struct zip64_extended_field ext = {0}; + struct local_file_header file = {0}; + BYTE *input_buf, *output_buf; + z_stream z_str = {0}; + LARGE_INTEGER off; + HGLOBAL global; + HRESULT hr; + int ret; + + *out = NULL; + off.QuadPart = entry->local_file_offset.QuadPart; + if (FAILED(hr = IStream_Seek(archive, off, STREAM_SEEK_SET, NULL))) + return hr; + hr = IStream_Read(archive, &file, sizeof(file), &read); + if (hr != S_OK) + return hr == S_FALSE ? OPC_E_ZIP_DECOMPRESSION_FAILED : hr; + if (file.method && file.method != Z_DEFLATED) + return OPC_E_ZIP_UNSUPPORTEDARCHIVE; + if (file.uncompressed_size == UINT32_MAX) + ext_size = 8; + if (file.compressed_size == UINT32_MAX) + ext_size = 16; + if (ext_size) + ext_size += 4; /* For the signature and size fields. */ + if (file.extra_length < ext_size) + return OPC_E_ZIP_CORRUPTED_ARCHIVE; + + off.QuadPart = file.name_length; + if (FAILED(hr = IStream_Seek(archive, off, STREAM_SEEK_CUR, NULL))) + return hr; + if (ext_size && ((hr = IStream_Read(archive, &ext, ext_size, &read) != S_OK))) + return hr == S_FALSE ? OPC_E_ZIP_DECOMPRESSION_FAILED : hr; + if (!compress_validate_part_size(entry, &file, &ext)) + return OPC_E_ZIP_CORRUPTED_ARCHIVE; + if (!(file.flags & USE_DATA_DESCRIPTOR)) + exp_crc32 = file.crc32; + if (!(global = GlobalAlloc(GMEM_MOVEABLE, entry->uncompressed_size))) + return E_OUTOFMEMORY; + if (!(input_buf = malloc(input_buffer_size))) + { + GlobalFree(global); + return E_OUTOFMEMORY; + } + + z_str.zalloc = zalloc; + z_str.zfree = zfree; + z_str.next_in = input_buf; + z_str.next_out = output_buf = GlobalLock(global); + z_str.avail_out = min(entry->uncompressed_size, UINT32_MAX); + ret = inflateInit2(&z_str, -MAX_WBITS); + if (ret) + { + GlobalUnlock(global); + GlobalFree(global); + free(input_buf); + return OPC_E_ZIP_DECOMPRESSION_FAILED; + } + hr = OPC_E_ZIP_DECOMPRESSION_FAILED; + while (compressed_data_read < entry->compressed_size && data_uncompressed < entry->uncompressed_size) + { + ULONG to_read = min(entry->compressed_size - compressed_data_read, input_buffer_size); + + z_str.next_out = &z_str.next_out[z_str.total_out]; + z_str.avail_out = min(entry->uncompressed_size - data_uncompressed, UINT32_MAX); + z_str.total_out = 0; + hr = IStream_Read(archive, input_buf, to_read, &read); + if (hr != S_OK) + { + if (hr == S_FALSE) hr = OPC_E_ZIP_DECOMPRESSION_FAILED; + goto fail; + } + z_str.avail_in = read; + ret = inflate(&z_str, Z_SYNC_FLUSH); + if (ret && ret != Z_STREAM_END) + goto fail; + compressed_data_read += read; + data_uncompressed += z_str.total_out; + } + if (compressed_data_read != entry->compressed_size || data_uncompressed != entry->uncompressed_size) + goto fail; + inflateEnd(&z_str); + free(input_buf); + crc32 = RtlComputeCrc32(0, output_buf, data_uncompressed); + GlobalUnlock(global); + if (file.flags & USE_DATA_DESCRIPTOR) + { + ULONG64 exp_compressed, exp_uncompressed; + union { + struct data_descriptor desc32; + struct data_descriptor64 desc64; + } desc = {0}; + + hr = IStream_Read(archive, &desc, ext_size ? sizeof(desc.desc64) : sizeof(desc.desc32), &read); + if (hr != S_OK) + { + GlobalFree(global); + return hr == S_FALSE ? OPC_E_ZIP_DECOMPRESSION_FAILED : hr; + } + if (ext_size) + { + exp_compressed = desc.desc64.compressed_size; + exp_uncompressed = desc.desc64.uncompressed_size; + } + else + { + exp_compressed = desc.desc32.compressed_size; + exp_uncompressed = desc.desc32.uncompressed_size; + } + if (exp_compressed != compressed_data_read || exp_uncompressed != data_uncompressed) + { + GlobalFree(global); + return OPC_E_ZIP_CORRUPTED_ARCHIVE; + } + exp_crc32 = desc.desc64.crc32; + } + if (exp_crc32 != crc32) + { + GlobalFree(global); + return OPC_E_ZIP_CORRUPTED_ARCHIVE; + } + if (FAILED(hr = CreateStreamOnHGlobal(global, TRUE, out))) + GlobalFree(global); + return hr; + +fail: + inflateEnd(&z_str); + GlobalUnlock(global); + GlobalFree(global); + free(input_buf); + return hr; +} + static HRESULT WINAPI zip_part_GetContentStream(IOpcPart *iface, IStream **stream) { - FIXME("(%p, %p) stub!\n", iface, stream); + struct zip_part *part = impl_from_IOpcPart(iface); + HRESULT hr; + + TRACE("(%p, %p)\n", iface, stream); + *stream = NULL; - return E_NOTIMPL; + if (!part->content && FAILED(hr = part_create_content_stream(part->archive, &part->entry, &part->content))) + return hr; + + IStream_AddRef((*stream = part->content)); + return S_OK; }
static HRESULT WINAPI zip_part_GetName(IOpcPart *iface, IOpcPartUri **name) @@ -728,7 +904,7 @@ static const IOpcPartVtbl zip_part_vtbl = zip_part_GetCompressionOptions, };
-static HRESULT zip_part_create(IOpcPartUri *name, struct zip_entry entry, IOpcPart **part) +static HRESULT zip_part_create(IOpcPartUri *name, IStream *stream, struct zip_entry entry, IOpcPart **part) { struct zip_part *zip_part;
@@ -737,6 +913,7 @@ static HRESULT zip_part_create(IOpcPartUri *name, struct zip_entry entry, IOpcPa
zip_part->IOpcPart_iface.lpVtbl = &zip_part_vtbl; IOpcPartUri_AddRef((zip_part->name = name)); + IStream_AddRef((zip_part->archive = stream)); zip_part->entry = entry; zip_part->ref = 1; *part = &zip_part->IOpcPart_iface; @@ -808,7 +985,11 @@ static HRESULT compress_read_entries(IOpcFactory *factory, IStream *stream, ULON goto failed; } entry.opt = dir.method ? deflate_opts[(dir.flags & DEFLATE_LEVEL_MASK) >> 1] : OPC_COMPRESSION_NONE; + entry.uncompressed_size = (dir.uncompressed_size == UINT32_MAX) ? ext.uncompressed_size : dir.uncompressed_size; + entry.compressed_size = (dir.compressed_size == UINT32_MAX) ? ext.compressed_size : dir.compressed_size; + entry.crc32 = dir.crc32; entry.local_file_offset.QuadPart = (dir.local_file_offset == UINT32_MAX) ? ext.local_file_offset : dir.local_file_offset; + off.QuadPart = dir.comment_length; if (dir.comment_length && FAILED(hr = IStream_Seek(stream, off, STREAM_SEEK_CUR, NULL))) { @@ -854,7 +1035,7 @@ static HRESULT compress_read_entries(IOpcFactory *factory, IStream *stream, ULON if (dup) hr = OPC_E_ZIP_DUPLICATE_NAME; goto failed; } - hr = zip_part_create(name_uri, entry, &new_part); + hr = zip_part_create(name_uri, stream, entry, &new_part); IOpcPartUri_Release(name_uri); if (FAILED(hr)) goto failed; diff --git a/dlls/opcservices/tests/opcservices.c b/dlls/opcservices/tests/opcservices.c index ef087e0d2ea..f671b5022e3 100644 --- a/dlls/opcservices/tests/opcservices.c +++ b/dlls/opcservices/tests/opcservices.c @@ -1349,7 +1349,7 @@ static void test_read_package(void) ok(opt == parts[i].opt, "Got opt %d != %d.\n", opt, parts[i].opt); hr = IOpcPart_GetContentStream(part, &stream); IOpcPart_Release(part); - todo_wine ok(hr == S_OK, "Failed to get content stream, hr %#lx.\n", hr); + ok(hr == S_OK, "Failed to get content stream, hr %#lx.\n", hr); if (SUCCEEDED(hr)) { BYTE exp_buf[256]; diff --git a/include/opcbase.idl b/include/opcbase.idl index 6f86045561e..8418f8475ae 100644 --- a/include/opcbase.idl +++ b/include/opcbase.idl @@ -52,4 +52,6 @@ cpp_quote("#define OPC_E_ENUM_COLLECTION_CHANGED MAKE_HRESULT(SEVERITY_ERROR, FA cpp_quote("#define OPC_E_ENUM_CANNOT_MOVE_NEXT MAKE_HRESULT(SEVERITY_ERROR, FACILITY_OPC, 0x51)") cpp_quote("#define OPC_E_ENUM_INVALID_POSITION MAKE_HRESULT(SEVERITY_ERROR, FACILITY_OPC, 0x53)") cpp_quote("#define OPC_E_ZIP_CORRUPTED_ARCHIVE MAKE_HRESULT(SEVERITY_ERROR, FACILITY_OPC, 0x1002)") +cpp_quote("#define OPC_E_ZIP_DECOMPRESSION_FAILED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_OPC, 0x1004)") +cpp_quote("#define OPC_E_ZIP_UNSUPPORTEDARCHIVE MAKE_HRESULT(SEVERITY_ERROR, FACILITY_OPC, 0x1008)") cpp_quote("#define OPC_E_ZIP_DUPLICATE_NAME MAKE_HRESULT(SEVERITY_ERROR, FACILITY_OPC, 0x100b)")