Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/opcservices/factory.c | 2 +- dlls/opcservices/opc_private.h | 2 +- dlls/opcservices/package.c | 40 ++++++++++++++++++++----- dlls/opcservices/tests/opcservices.c | 45 +++++++++++++++++++++++++--- 4 files changed, 76 insertions(+), 13 deletions(-)
diff --git a/dlls/opcservices/factory.c b/dlls/opcservices/factory.c index 0c16cc2501..4578f41d7c 100644 --- a/dlls/opcservices/factory.c +++ b/dlls/opcservices/factory.c @@ -336,7 +336,7 @@ static HRESULT WINAPI opc_factory_CreatePackage(IOpcFactory *iface, IOpcPackage { TRACE("iface %p, package %p.\n", iface, package);
- return opc_package_create(package); + return opc_package_create(iface, package); }
static HRESULT WINAPI opc_factory_ReadPackageFromStream(IOpcFactory *iface, IStream *stream, diff --git a/dlls/opcservices/opc_private.h b/dlls/opcservices/opc_private.h index 80b1e954b7..3c78195242 100644 --- a/dlls/opcservices/opc_private.h +++ b/dlls/opcservices/opc_private.h @@ -45,6 +45,6 @@ static inline BOOL opc_array_reserve(void **elements, size_t *capacity, size_t c return TRUE; }
-extern HRESULT opc_package_create(IOpcPackage **package) DECLSPEC_HIDDEN; +extern HRESULT opc_package_create(IOpcFactory *factory, IOpcPackage **package) DECLSPEC_HIDDEN; extern HRESULT opc_part_uri_create(const WCHAR *uri, IOpcPartUri **part_uri) DECLSPEC_HIDDEN; extern HRESULT opc_uri_create(const WCHAR *uri, IOpcUri **opc_uri) DECLSPEC_HIDDEN; diff --git a/dlls/opcservices/package.c b/dlls/opcservices/package.c index cac89c523e..c69239b84e 100644 --- a/dlls/opcservices/package.c +++ b/dlls/opcservices/package.c @@ -37,6 +37,7 @@ struct opc_package
IOpcPartSet *part_set; IOpcRelationshipSet *relationship_set; + IOpcUri *source_uri; };
struct opc_part @@ -69,6 +70,7 @@ struct opc_relationship WCHAR *type; IUri *target; OPC_URI_TARGET_MODE target_mode; + IOpcUri *source_uri; };
struct opc_relationship_set @@ -79,6 +81,7 @@ struct opc_relationship_set struct opc_relationship **relationships; size_t size; size_t count; + IOpcUri *source_uri; };
static inline struct opc_package *impl_from_IOpcPackage(IOpcPackage *iface) @@ -106,7 +109,7 @@ static inline struct opc_relationship *impl_from_IOpcRelationship(IOpcRelationsh return CONTAINING_RECORD(iface, struct opc_relationship, IOpcRelationship_iface); }
-static HRESULT opc_relationship_set_create(IOpcRelationshipSet **relationship_set); +static HRESULT opc_relationship_set_create(IOpcUri *source_uri, IOpcRelationshipSet **relationship_set);
static WCHAR *opc_strdupW(const WCHAR *str) { @@ -177,7 +180,7 @@ static HRESULT WINAPI opc_part_GetRelationshipSet(IOpcPart *iface, IOpcRelations
TRACE("iface %p, relationship_set %p.\n", iface, relationship_set);
- if (!part->relationship_set && FAILED(hr = opc_relationship_set_create(&part->relationship_set))) + if (!part->relationship_set && FAILED(hr = opc_relationship_set_create((IOpcUri *)part->name, &part->relationship_set))) return hr;
*relationship_set = part->relationship_set; @@ -401,6 +404,7 @@ static ULONG WINAPI opc_relationship_Release(IOpcRelationship *iface) { CoTaskMemFree(relationship->id); CoTaskMemFree(relationship->type); + IOpcUri_Release(relationship->source_uri); IUri_Release(relationship->target); heap_free(relationship); } @@ -430,9 +434,14 @@ static HRESULT WINAPI opc_relationship_GetRelationshipType(IOpcRelationship *ifa
static HRESULT WINAPI opc_relationship_GetSourceUri(IOpcRelationship *iface, IOpcUri **uri) { - FIXME("iface %p, uri %p stub!\n", iface, uri); + struct opc_relationship *relationship = impl_from_IOpcRelationship(iface);
- return E_NOTIMPL; + TRACE("iface %p, uri %p.\n", iface, uri); + + *uri = relationship->source_uri; + IOpcUri_AddRef(*uri); + + return S_OK; }
static HRESULT WINAPI opc_relationship_GetTargetUri(IOpcRelationship *iface, IUri **target) @@ -486,6 +495,8 @@ static HRESULT opc_relationship_create(struct opc_relationship_set *set, const W
relationship->target = target_uri; IUri_AddRef(relationship->target); + relationship->source_uri = set->source_uri; + IOpcUri_AddRef(relationship->source_uri);
/* FIXME: test that id is unique */ if (id) @@ -557,6 +568,7 @@ static ULONG WINAPI opc_relationship_set_Release(IOpcRelationshipSet *iface)
for (i = 0; i < relationship_set->count; ++i) IOpcRelationship_Release(&relationship_set->relationships[i]->IOpcRelationship_iface); + IOpcUri_Release(relationship_set->source_uri); heap_free(relationship_set->relationships); heap_free(relationship_set); } @@ -637,7 +649,7 @@ static const IOpcRelationshipSetVtbl opc_relationship_set_vtbl = opc_relationship_set_GetRelationshipsContentStream, };
-static HRESULT opc_relationship_set_create(IOpcRelationshipSet **out) +static HRESULT opc_relationship_set_create(IOpcUri *source_uri, IOpcRelationshipSet **out) { struct opc_relationship_set *relationship_set;
@@ -646,6 +658,8 @@ static HRESULT opc_relationship_set_create(IOpcRelationshipSet **out)
relationship_set->IOpcRelationshipSet_iface.lpVtbl = &opc_relationship_set_vtbl; relationship_set->refcount = 1; + relationship_set->source_uri = source_uri; + IOpcUri_AddRef(relationship_set->source_uri);
*out = &relationship_set->IOpcRelationshipSet_iface; TRACE("Created relationship set %p.\n", *out); @@ -691,6 +705,8 @@ static ULONG WINAPI opc_package_Release(IOpcPackage *iface) IOpcPartSet_Release(package->part_set); if (package->relationship_set) IOpcRelationshipSet_Release(package->relationship_set); + if (package->source_uri) + IOpcUri_Release(package->source_uri); heap_free(package); }
@@ -728,8 +744,11 @@ static HRESULT WINAPI opc_package_GetRelationshipSet(IOpcPackage *iface, IOpcRel
TRACE("iface %p, relationship_set %p.\n", iface, relationship_set);
- if (!package->relationship_set && FAILED(hr = opc_relationship_set_create(&package->relationship_set))) + if (!package->relationship_set) + { + if (FAILED(hr = opc_relationship_set_create(package->source_uri, &package->relationship_set))) return hr; + }
*relationship_set = package->relationship_set; IOpcRelationshipSet_AddRef(*relationship_set); @@ -746,9 +765,10 @@ static const IOpcPackageVtbl opc_package_vtbl = opc_package_GetRelationshipSet, };
-HRESULT opc_package_create(IOpcPackage **out) +HRESULT opc_package_create(IOpcFactory *factory, IOpcPackage **out) { struct opc_package *package; + HRESULT hr;
if (!(package = heap_alloc_zero(sizeof(*package)))) return E_OUTOFMEMORY; @@ -756,6 +776,12 @@ HRESULT opc_package_create(IOpcPackage **out) package->IOpcPackage_iface.lpVtbl = &opc_package_vtbl; package->refcount = 1;
+ if (FAILED(hr = IOpcFactory_CreatePackageRootUri(factory, &package->source_uri))) + { + heap_free(package); + return hr; + } + *out = &package->IOpcPackage_iface; TRACE("Created package %p.\n", *out); return S_OK; diff --git a/dlls/opcservices/tests/opcservices.c b/dlls/opcservices/tests/opcservices.c index 9af7aa49b9..a2a032f406 100644 --- a/dlls/opcservices/tests/opcservices.c +++ b/dlls/opcservices/tests/opcservices.c @@ -38,12 +38,16 @@ static IOpcFactory *create_factory(void) static void test_package(void) { static const WCHAR typeW[] = {'t','y','p','e','/','s','u','b','t','y','p','e',0}; + static const WCHAR targetW[] = {'t','a','r','g','e','t',0}; static const WCHAR uriW[] = {'/','u','r','i',0}; IOpcRelationshipSet *relset, *relset2; IOpcPartSet *partset, *partset2; + IOpcRelationship *rel; IOpcPartUri *part_uri; IOpcFactory *factory; IOpcPackage *package; + IOpcUri *source_uri; + IUri *target_uri; IOpcPart *part; HRESULT hr; BOOL ret; @@ -81,6 +85,22 @@ static void test_package(void) hr = IOpcPart_GetRelationshipSet(part, &relset2); ok(SUCCEEDED(hr), "Failed to get relationship set, hr %#x.\n", hr); ok(relset == relset2, "Expected same part set instance.\n"); + + hr = CreateUri(targetW, Uri_CREATE_ALLOW_RELATIVE, 0, &target_uri); + ok(SUCCEEDED(hr), "Failed to create target uri, hr %#x.\n", hr); + + hr = IOpcRelationshipSet_CreateRelationship(relset, NULL, typeW, target_uri, OPC_URI_TARGET_MODE_INTERNAL, &rel); + ok(SUCCEEDED(hr), "Failed to create relationship, hr %#x.\n", hr); + + hr = IOpcRelationship_GetSourceUri(rel, &source_uri); + ok(SUCCEEDED(hr), "Failed to get source uri, hr %#x.\n", hr); + ok(source_uri == (IOpcUri *)part_uri, "Unexpected source uri.\n"); + + IOpcUri_Release(source_uri); + + IOpcRelationship_Release(rel); + IUri_Release(target_uri); + IOpcRelationshipSet_Release(relset); IOpcRelationshipSet_Release(relset2);
@@ -212,11 +232,11 @@ static void test_relationship(void) static const WCHAR typeW[] = {'t','y','p','e',0}; static const WCHAR rootW[] = {'/',0}; IUri *target_uri, *target_uri2, *uri; + IOpcUri *source_uri, *source_uri2; + IOpcRelationship *rel, *rel2; IOpcRelationshipSet *rels; - IOpcRelationship *rel; IOpcFactory *factory; IOpcPackage *package; - IOpcUri *source_uri; IUnknown *unk; DWORD mode; HRESULT hr; @@ -256,6 +276,9 @@ todo_wine hr = IOpcRelationshipSet_CreateRelationship(rels, NULL, typeW, target_uri, OPC_URI_TARGET_MODE_INTERNAL, &rel); ok(SUCCEEDED(hr), "Failed to create relationship, hr %#x.\n", hr);
+ hr = IOpcRelationshipSet_CreateRelationship(rels, NULL, typeW, target_uri, OPC_URI_TARGET_MODE_INTERNAL, &rel2); + ok(SUCCEEDED(hr), "Failed to create relationship, hr %#x.\n", hr); + /* Autogenerated relationship id */ hr = IOpcRelationship_GetId(rel, &id); ok(SUCCEEDED(hr), "Failed to get id, hr %#x.\n", hr); @@ -272,8 +295,17 @@ todo_wine ok(mode == OPC_URI_TARGET_MODE_INTERNAL, "Unexpected mode %d.\n", mode);
/* Source uri */ + hr = IOpcFactory_CreatePackageRootUri(factory, &source_uri); + ok(SUCCEEDED(hr), "Failed to create root uri, hr %#x.\n", hr); + + hr = IOpcFactory_CreatePackageRootUri(factory, &source_uri2); + ok(SUCCEEDED(hr), "Failed to create root uri, hr %#x.\n", hr); + ok(source_uri != source_uri2, "Unexpected uri instance.\n"); + + IOpcUri_Release(source_uri); + IOpcUri_Release(source_uri2); + hr = IOpcRelationship_GetSourceUri(rel, &source_uri); -todo_wine ok(SUCCEEDED(hr), "Failed to get source uri, hr %#x.\n", hr);
hr = IOpcUri_QueryInterface(source_uri, &IID_IOpcPartUri, (void **)&unk); @@ -282,12 +314,17 @@ todo_wine str = NULL; hr = IOpcUri_GetRawUri(source_uri, &str); ok(SUCCEEDED(hr), "Failed to get raw uri, hr %#x.\n", hr); -todo_wine ok(!lstrcmpW(rootW, str), "Unexpected uri %s.\n", wine_dbgstr_w(str)); SysFreeString(str);
+ hr = IOpcRelationship_GetSourceUri(rel2, &source_uri2); + ok(SUCCEEDED(hr), "Failed to get source uri, hr %#x.\n", hr); + ok(source_uri2 == source_uri, "Unexpected source uri.\n"); + + IOpcUri_Release(source_uri2); IOpcUri_Release(source_uri);
+ IOpcRelationship_Release(rel2); IOpcRelationship_Release(rel);
IOpcRelationshipSet_Release(rels);
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/opcservices/Makefile.in | 4 +- dlls/opcservices/compress.c | 364 +++++++++++++++++++++++++++++++++ dlls/opcservices/factory.c | 5 +- dlls/opcservices/opc_private.h | 8 + dlls/opcservices/package.c | 55 +++++ 5 files changed, 433 insertions(+), 3 deletions(-) create mode 100644 dlls/opcservices/compress.c
diff --git a/dlls/opcservices/Makefile.in b/dlls/opcservices/Makefile.in index f165b5ceb8..bdc4c80ba8 100644 --- a/dlls/opcservices/Makefile.in +++ b/dlls/opcservices/Makefile.in @@ -1,7 +1,9 @@ MODULE = opcservices.dll -IMPORTS = uuid ole32 advapi32 urlmon +IMPORTS = uuid ole32 advapi32 urlmon xmllite +EXTRALIBS = $(Z_LIBS)
C_SRCS = \ + compress.c \ factory.c \ package.c \ uri.c diff --git a/dlls/opcservices/compress.c b/dlls/opcservices/compress.c new file mode 100644 index 0000000000..cd1cb2d912 --- /dev/null +++ b/dlls/opcservices/compress.c @@ -0,0 +1,364 @@ +/* + * Copyright 2018 Nikolay Sivov for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define COBJMACROS + +#include "config.h" + +#ifdef HAVE_ZLIB +# include <zlib.h> +#endif + +#include "windef.h" +#include "winternl.h" +#include "msopc.h" + +#include "opc_private.h" + +#include "wine/debug.h" +#include "wine/heap.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msopc); + +#include <pshpack2.h> +struct local_file_header +{ + DWORD signature; + WORD version; + WORD flags; + WORD method; + DWORD mtime; + DWORD crc32; + DWORD compressed_size; + DWORD uncompressed_size; + WORD name_length; + WORD extra_length; +}; + +struct data_descriptor +{ + DWORD signature; + DWORD crc32; + DWORD compressed_size; + DWORD uncompressed_size; +}; + +struct central_directory_header +{ + DWORD signature; + WORD version; + WORD min_version; + WORD flags; + WORD method; + DWORD mtime; + DWORD crc32; + DWORD compressed_size; + DWORD uncompressed_size; + WORD name_length; + WORD extra_length; + WORD comment_length; + WORD diskid; + WORD internal_attributes; + DWORD external_attributes; + DWORD local_file_offset; +}; + +struct central_directory_end +{ + DWORD signature; + WORD diskid; + WORD firstdisk; + WORD records_num; + WORD records_total; + DWORD directory_size; + DWORD directory_offset; + WORD comment_length; +}; +#include <poppack.h> + +#define CENTRAL_DIR_SIGNATURE 0x02014b50 +#define LOCAL_HEADER_SIGNATURE 0x04034b50 +#define DIRECTORY_END_SIGNATURE 0x06054b50 +#define DATA_DESCRIPTOR_SIGNATURE 0x08074b50 +#define VERSION 20 + +enum entry_flags +{ + USE_DATA_DESCRIPTOR = 0x8, +}; + +struct zip_archive +{ + struct central_directory_header **files; + size_t file_count; + size_t file_size; + + DWORD mtime; + IStream *output; + DWORD position; + HRESULT write_result; + + unsigned char input_buffer[0x8000]; + unsigned char output_buffer[0x8000]; +}; + +HRESULT compress_create_archive(IStream *output, struct zip_archive **out) +{ + struct zip_archive *archive; + WORD date, time; + FILETIME ft; + + if (!(archive = heap_alloc(sizeof(*archive)))) + return E_OUTOFMEMORY; + + archive->files = NULL; + archive->file_size = 0; + archive->file_count = 0; + archive->write_result = S_OK; + + archive->output = output; + IStream_AddRef(archive->output); + + GetSystemTimeAsFileTime(&ft); + FileTimeToDosDateTime(&ft, &date, &time); + archive->mtime = date << 16 | time; + + *out = archive; + + return S_OK; +} + +static void compress_write(struct zip_archive *archive, void *data, ULONG size) +{ + ULONG written; + + archive->write_result = IStream_Write(archive->output, data, size, &written); + if (written != size) + archive->write_result = E_FAIL; + else + archive->position += written; + + if (FAILED(archive->write_result)) + WARN("Failed to write output %p, size %u, written %u, hr %#x.\n", data, size, written, archive->write_result); +} + +void compress_finalize_archive(struct zip_archive *archive) +{ + struct central_directory_end dir_end = { 0 }; + size_t i; + + dir_end.directory_offset = archive->position; + dir_end.records_num = archive->file_count; + dir_end.records_total = archive->file_count; + + /* Directory entries */ + for (i = 0; i < archive->file_count; ++i) + { + compress_write(archive, archive->files[i], sizeof(*archive->files[i])); + compress_write(archive, archive->files[i] + 1, archive->files[i]->name_length); + dir_end.directory_size += archive->files[i]->name_length + sizeof(*archive->files[i]); + } + + /* End record */ + dir_end.signature = DIRECTORY_END_SIGNATURE; + compress_write(archive, &dir_end, sizeof(dir_end)); + + IStream_Release(archive->output); + + for (i = 0; i < archive->file_count; i++) + heap_free(archive->files[i]); + heap_free(archive->files); + heap_free(archive); +} + +static void compress_write_content(struct zip_archive *archive, IStream *content, + OPC_COMPRESSION_OPTIONS options, struct data_descriptor *data_desc) +{ +#ifdef HAVE_ZLIB + int level, flush; + z_stream z_str; +#endif + LARGE_INTEGER move = { 0 }; + ULONG num_read; + HRESULT hr; + + data_desc->crc32 = RtlComputeCrc32(0, NULL, 0); + IStream_Seek(content, move, STREAM_SEEK_SET, NULL); + +#ifdef HAVE_ZLIB + + switch (options) + { + case OPC_COMPRESSION_NONE: + level = Z_NO_COMPRESSION; + break; + case OPC_COMPRESSION_NORMAL: + level = Z_DEFAULT_COMPRESSION; + break; + case OPC_COMPRESSION_MAXIMUM: + level = Z_BEST_COMPRESSION; + break; + case OPC_COMPRESSION_FAST: + level = 2; + break; + case OPC_COMPRESSION_SUPERFAST: + level = Z_BEST_SPEED; + break; + default: + WARN("Unsupported compression options %d.\n", options); + level = Z_DEFAULT_COMPRESSION; + } + + memset(&z_str, 0, sizeof(z_str)); + deflateInit2(&z_str, level, Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); + + do + { + int ret; + + if (FAILED(hr = IStream_Read(content, archive->input_buffer, sizeof(archive->input_buffer), &num_read))) + { + archive->write_result = hr; + break; + } + + z_str.avail_in = num_read; + z_str.next_in = archive->input_buffer; + data_desc->crc32 = RtlComputeCrc32(data_desc->crc32, archive->input_buffer, num_read); + + flush = sizeof(archive->input_buffer) > num_read ? Z_FINISH : Z_NO_FLUSH; + + do + { + ULONG have; + + z_str.avail_out = sizeof(archive->output_buffer); + z_str.next_out = archive->output_buffer; + + if ((ret = deflate(&z_str, flush))) + WARN("Failed to deflate, ret %d.\n", ret); + have = sizeof(archive->output_buffer) - z_str.avail_out; + compress_write(archive, archive->output_buffer, have); + } while (z_str.avail_out == 0); + } while (flush != Z_FINISH); + + deflateEnd(&z_str); + + data_desc->compressed_size = z_str.total_out; + data_desc->uncompressed_size = z_str.total_in; + +#else + + if (options != OPC_COMPRESSION_NONE) + FIXME("Writing without compression.\n") + + do + { + if (FAILED(hr = IStream_Read(content, archive->input_buffer, sizeof(archive->input_buffer), &num_read))) + { + archive->write_result = hr; + break; + } + + if (num_read == 0) + break; + + data_desc->uncompressed_size += num_read; + data_desc->crc32 = RtlComputeCrc32(data_desc->crc32, archive->input_buffer, num_read); + compress_write(archive, archive->input_buffer, num_read); + } while (num_read != 0 && archive->write_result == S_OK); + + data_desc->compressed_size = data_desc->uncompressed_size; + +#endif /* HAVE_ZLIB */ +} + +HRESULT compress_add_file(struct zip_archive *archive, const WCHAR *path, + IStream *content, OPC_COMPRESSION_OPTIONS options) +{ + struct central_directory_header *entry; + struct local_file_header local_header; + struct data_descriptor data_desc; + DWORD local_header_pos; + char *name; + DWORD len; + + len = WideCharToMultiByte(CP_ACP, 0, path, -1, NULL, 0, NULL, NULL); + if (!(name = heap_alloc(len))) + return E_OUTOFMEMORY; + WideCharToMultiByte(CP_ACP, 0, path, -1, name, len, NULL, NULL); + + /* Local header */ + local_header.signature = LOCAL_HEADER_SIGNATURE; + local_header.version = VERSION; + local_header.flags = USE_DATA_DESCRIPTOR; + local_header.method = Z_DEFLATED; + local_header.mtime = archive->mtime; + local_header.crc32 = 0; + local_header.compressed_size = 0; + local_header.uncompressed_size = 0; + local_header.name_length = len - 1; + local_header.extra_length = 0; + + local_header_pos = archive->position; + + compress_write(archive, &local_header, sizeof(local_header)); + compress_write(archive, name, local_header.name_length); + + /* Content */ + compress_write_content(archive, content, options, &data_desc); + + /* Data descriptor */ + data_desc.signature = DATA_DESCRIPTOR_SIGNATURE; + compress_write(archive, &data_desc, sizeof(data_desc)); + + if (FAILED(archive->write_result)) + return archive->write_result; + + /* Set directory entry */ + if (!(entry = heap_alloc_zero(sizeof(*entry) + local_header.name_length))) + { + heap_free(name); + return E_OUTOFMEMORY; + } + + entry->signature = CENTRAL_DIR_SIGNATURE; + entry->version = local_header.version; + entry->min_version = local_header.version; + entry->flags = local_header.flags; + entry->method = local_header.method; + entry->mtime = local_header.mtime; + entry->crc32 = data_desc.crc32; + entry->compressed_size = data_desc.compressed_size; + entry->uncompressed_size = data_desc.uncompressed_size; + entry->name_length = local_header.name_length; + entry->local_file_offset = local_header_pos; + memcpy(entry + 1, name, entry->name_length); + heap_free(name); + + if (!opc_array_reserve((void **)&archive->files, &archive->file_size, archive->file_count + 1, + sizeof(*archive->files))) + { + heap_free(entry); + return E_OUTOFMEMORY; + } + + archive->files[archive->file_count++] = entry; + + return S_OK; +} diff --git a/dlls/opcservices/factory.c b/dlls/opcservices/factory.c index 4578f41d7c..13fa702293 100644 --- a/dlls/opcservices/factory.c +++ b/dlls/opcservices/factory.c @@ -26,6 +26,7 @@ #include "ole2.h" #include "rpcproxy.h" #include "msopc.h" +#include "xmllite.h"
#include "wine/debug.h"
@@ -350,9 +351,9 @@ static HRESULT WINAPI opc_factory_ReadPackageFromStream(IOpcFactory *iface, IStr static HRESULT WINAPI opc_factory_WritePackageToStream(IOpcFactory *iface, IOpcPackage *package, OPC_WRITE_FLAGS flags, IStream *stream) { - FIXME("iface %p, package %p, flags %#x, stream %p stub!\n", iface, package, flags, stream); + TRACE("iface %p, package %p, flags %#x, stream %p.\n", iface, package, flags, stream);
- return E_NOTIMPL; + return opc_package_write(package, flags, stream); }
static HRESULT WINAPI opc_factory_CreateDigitalSignatureManager(IOpcFactory *iface, IOpcPackage *package, diff --git a/dlls/opcservices/opc_private.h b/dlls/opcservices/opc_private.h index 3c78195242..afe5c68561 100644 --- a/dlls/opcservices/opc_private.h +++ b/dlls/opcservices/opc_private.h @@ -48,3 +48,11 @@ static inline BOOL opc_array_reserve(void **elements, size_t *capacity, size_t c extern HRESULT opc_package_create(IOpcFactory *factory, IOpcPackage **package) DECLSPEC_HIDDEN; extern HRESULT opc_part_uri_create(const WCHAR *uri, IOpcPartUri **part_uri) DECLSPEC_HIDDEN; extern HRESULT opc_uri_create(const WCHAR *uri, IOpcUri **opc_uri) DECLSPEC_HIDDEN; + +extern HRESULT opc_package_write(IOpcPackage *package, OPC_WRITE_FLAGS flags, IStream *stream) DECLSPEC_HIDDEN; + +struct zip_archive; +extern HRESULT compress_create_archive(IStream *output, struct zip_archive **archive) DECLSPEC_HIDDEN; +extern HRESULT compress_add_file(struct zip_archive *archive, const WCHAR *path, IStream *content, + OPC_COMPRESSION_OPTIONS options) DECLSPEC_HIDDEN; +extern void compress_finalize_archive(struct zip_archive *archive) DECLSPEC_HIDDEN; diff --git a/dlls/opcservices/package.c b/dlls/opcservices/package.c index c69239b84e..20d7b4e5ed 100644 --- a/dlls/opcservices/package.c +++ b/dlls/opcservices/package.c @@ -22,6 +22,7 @@ #include "windef.h" #include "winbase.h" #include "ntsecapi.h" +#include "xmllite.h"
#include "wine/debug.h" #include "wine/unicode.h" @@ -786,3 +787,57 @@ HRESULT opc_package_create(IOpcFactory *factory, IOpcPackage **out) TRACE("Created package %p.\n", *out); return S_OK; } + +static HRESULT opc_package_write_contenttypes(struct zip_archive *archive, IXmlWriter *writer) +{ + static const WCHAR contenttypesW[] = {'[','C','o','n','t','e','n','t','_','T','y','p','e','s',']','.','x','m','l',0}; + static const WCHAR typesW[] = {'T','y','p','e','s',0}; + IStream *content; + HRESULT hr; + + if (FAILED(hr = CreateStreamOnHGlobal(NULL, TRUE, &content))) + return hr; + + IXmlWriter_SetOutput(writer, (IUnknown *)content); + + hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Omit); + if (SUCCEEDED(hr)) + hr = IXmlWriter_WriteStartElement(writer, NULL, typesW, NULL); + if (SUCCEEDED(hr)) + hr = IXmlWriter_WriteEndDocument(writer); + if (SUCCEEDED(hr)) + hr = IXmlWriter_Flush(writer); + + if (SUCCEEDED(hr)) + hr = compress_add_file(archive, contenttypesW, content, OPC_COMPRESSION_NORMAL); + IStream_Release(content); + + return hr; +} + +HRESULT opc_package_write(IOpcPackage *input, OPC_WRITE_FLAGS flags, IStream *stream) +{ + struct zip_archive *archive; + IXmlWriter *writer; + HRESULT hr; + + if (flags != OPC_WRITE_FORCE_ZIP32) + FIXME("Unsupported write flags %#x.\n", flags); + + if (FAILED(hr = CreateXmlWriter(&IID_IXmlWriter, (void **)&writer, NULL))) + return hr; + + if (FAILED(hr = compress_create_archive(stream, &archive))) + { + IXmlWriter_Release(writer); + return hr; + } + + /* [Content_Types].xml */ + hr = opc_package_write_contenttypes(archive, writer); + + compress_finalize_archive(archive); + IXmlWriter_Release(writer); + + return hr; +}
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/opcservices/package.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/dlls/opcservices/package.c b/dlls/opcservices/package.c index 20d7b4e5ed..00f4a4fa4b 100644 --- a/dlls/opcservices/package.c +++ b/dlls/opcservices/package.c @@ -221,9 +221,12 @@ static HRESULT WINAPI opc_part_GetContentType(IOpcPart *iface, LPWSTR *type)
static HRESULT WINAPI opc_part_GetCompressionOptions(IOpcPart *iface, OPC_COMPRESSION_OPTIONS *options) { - FIXME("iface %p, options %p stub!\n", iface, options); + struct opc_part *part = impl_from_IOpcPart(iface);
- return E_NOTIMPL; + TRACE("iface %p, options %p.\n", iface, options); + + *options = part->compression_options; + return S_OK; }
static const IOpcPartVtbl opc_part_vtbl =
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/opcservices/package.c | 25 +++++++++++++++++++++++-- dlls/opcservices/tests/opcservices.c | 21 ++++++++++++++++++++- include/opcbase.idl | 1 + 3 files changed, 44 insertions(+), 3 deletions(-)
diff --git a/dlls/opcservices/package.c b/dlls/opcservices/package.c index 00f4a4fa4b..d0d39a9804 100644 --- a/dlls/opcservices/package.c +++ b/dlls/opcservices/package.c @@ -583,9 +583,30 @@ static ULONG WINAPI opc_relationship_set_Release(IOpcRelationshipSet *iface) static HRESULT WINAPI opc_relationship_set_GetRelationship(IOpcRelationshipSet *iface, const WCHAR *id, IOpcRelationship **relationship) { - FIXME("iface %p, id %s, relationship %p stub!\n", iface, debugstr_w(id), relationship); + struct opc_relationship_set *relationship_set = impl_from_IOpcRelationshipSet(iface); + size_t i;
- return E_NOTIMPL; + TRACE("iface %p, id %s, relationship %p.\n", iface, debugstr_w(id), relationship); + + if (!relationship) + return E_POINTER; + + *relationship = NULL; + + if (!id) + return E_POINTER; + + for (i = 0; i < relationship_set->count; i++) + { + if (!strcmpW(id, relationship_set->relationships[i]->id)) + { + *relationship = &relationship_set->relationships[i]->IOpcRelationship_iface; + IOpcRelationship_AddRef(*relationship); + break; + } + } + + return *relationship ? S_OK : OPC_E_NO_SUCH_RELATIONSHIP; }
static HRESULT WINAPI opc_relationship_set_CreateRelationship(IOpcRelationshipSet *iface, const WCHAR *id, diff --git a/dlls/opcservices/tests/opcservices.c b/dlls/opcservices/tests/opcservices.c index a2a032f406..e370e97ba8 100644 --- a/dlls/opcservices/tests/opcservices.c +++ b/dlls/opcservices/tests/opcservices.c @@ -232,8 +232,8 @@ static void test_relationship(void) static const WCHAR typeW[] = {'t','y','p','e',0}; static const WCHAR rootW[] = {'/',0}; IUri *target_uri, *target_uri2, *uri; + IOpcRelationship *rel, *rel2, *rel3; IOpcUri *source_uri, *source_uri2; - IOpcRelationship *rel, *rel2; IOpcRelationshipSet *rels; IOpcFactory *factory; IOpcPackage *package; @@ -283,6 +283,25 @@ todo_wine hr = IOpcRelationship_GetId(rel, &id); ok(SUCCEEDED(hr), "Failed to get id, hr %#x.\n", hr); ok(lstrlenW(id) == 9 && *id == 'R', "Unexpected relationship id %s.\n", wine_dbgstr_w(id)); + + hr = IOpcRelationshipSet_GetRelationship(rels, id, &rel3); + ok(SUCCEEDED(hr), "Failed to get relationship, hr %#x.\n", hr); + IOpcRelationship_Release(rel3); + + hr = IOpcRelationshipSet_GetRelationship(rels, id, NULL); + ok(hr = E_POINTER, "Unexpected hr %#x.\n", hr); + + rel3 = (void *)0xdeadbeef; + hr = IOpcRelationshipSet_GetRelationship(rels, NULL, &rel3); + ok(hr = E_POINTER, "Unexpected hr %#x.\n", hr); + ok(rel3 == NULL, "Expected null pointer.\n"); + + *id = 'r'; + rel3 = (void *)0xdeadbeef; + hr = IOpcRelationshipSet_GetRelationship(rels, id, &rel3); + ok(hr == OPC_E_NO_SUCH_RELATIONSHIP, "Unexpected hr %#x.\n", hr); + ok(rel3 == NULL, "Expected null pointer.\n"); + CoTaskMemFree(id);
hr = IOpcRelationship_GetTargetUri(rel, &uri); diff --git a/include/opcbase.idl b/include/opcbase.idl index 80f459fe8b..ca42ceb4e3 100644 --- a/include/opcbase.idl +++ b/include/opcbase.idl @@ -42,3 +42,4 @@ typedef [v1_enum] enum } OPC_URI_TARGET_MODE;
cpp_quote("#define OPC_E_INVALID_RELATIONSHIP_TARGET MAKE_HRESULT(SEVERITY_ERROR, FACILITY_OPC, 0x12)") +cpp_quote("#define OPC_E_NO_SUCH_RELATIONSHIP MAKE_HRESULT(SEVERITY_ERROR, FACILITY_OPC, 0x48)")
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/opcservices/package.c | 37 ++++++++++++++++++++-------- dlls/opcservices/tests/opcservices.c | 19 ++++++++++++++ 2 files changed, 46 insertions(+), 10 deletions(-)
diff --git a/dlls/opcservices/package.c b/dlls/opcservices/package.c index d0d39a9804..370b42e83b 100644 --- a/dlls/opcservices/package.c +++ b/dlls/opcservices/package.c @@ -580,11 +580,25 @@ static ULONG WINAPI opc_relationship_set_Release(IOpcRelationshipSet *iface) return refcount; }
+static struct opc_relationship *opc_relationshipset_get_item(struct opc_relationship_set *relationship_set, + const WCHAR *id) +{ + size_t i; + + for (i = 0; i < relationship_set->count; i++) + { + if (!strcmpW(id, relationship_set->relationships[i]->id)) + return relationship_set->relationships[i]; + } + + return NULL; +} + static HRESULT WINAPI opc_relationship_set_GetRelationship(IOpcRelationshipSet *iface, const WCHAR *id, IOpcRelationship **relationship) { struct opc_relationship_set *relationship_set = impl_from_IOpcRelationshipSet(iface); - size_t i; + struct opc_relationship *ret;
TRACE("iface %p, id %s, relationship %p.\n", iface, debugstr_w(id), relationship);
@@ -596,14 +610,10 @@ static HRESULT WINAPI opc_relationship_set_GetRelationship(IOpcRelationshipSet * if (!id) return E_POINTER;
- for (i = 0; i < relationship_set->count; i++) + if ((ret = opc_relationshipset_get_item(relationship_set, id))) { - if (!strcmpW(id, relationship_set->relationships[i]->id)) - { - *relationship = &relationship_set->relationships[i]->IOpcRelationship_iface; - IOpcRelationship_AddRef(*relationship); - break; - } + *relationship = &ret->IOpcRelationship_iface; + IOpcRelationship_AddRef(*relationship); }
return *relationship ? S_OK : OPC_E_NO_SUCH_RELATIONSHIP; @@ -632,9 +642,16 @@ static HRESULT WINAPI opc_relationship_set_DeleteRelationship(IOpcRelationshipSe
static HRESULT WINAPI opc_relationship_set_RelationshipExists(IOpcRelationshipSet *iface, const WCHAR *id, BOOL *exists) { - FIXME("iface %p, id %s, exists %p stub!\n", iface, debugstr_w(id), exists); + struct opc_relationship_set *relationship_set = impl_from_IOpcRelationshipSet(iface);
- return E_NOTIMPL; + TRACE("iface %p, id %s, exists %p.\n", iface, debugstr_w(id), exists); + + if (!id || !exists) + return E_POINTER; + + *exists = opc_relationshipset_get_item(relationship_set, id) != NULL; + + return S_OK; }
static HRESULT WINAPI opc_relationship_set_GetEnumerator(IOpcRelationshipSet *iface, diff --git a/dlls/opcservices/tests/opcservices.c b/dlls/opcservices/tests/opcservices.c index e370e97ba8..2deaf30809 100644 --- a/dlls/opcservices/tests/opcservices.c +++ b/dlls/opcservices/tests/opcservices.c @@ -241,6 +241,7 @@ static void test_relationship(void) DWORD mode; HRESULT hr; WCHAR *id; + BOOL ret; BSTR str;
factory = create_factory(); @@ -284,6 +285,19 @@ todo_wine ok(SUCCEEDED(hr), "Failed to get id, hr %#x.\n", hr); ok(lstrlenW(id) == 9 && *id == 'R', "Unexpected relationship id %s.\n", wine_dbgstr_w(id));
+ ret = 123; + hr = IOpcRelationshipSet_RelationshipExists(rels, NULL, &ret); + ok(hr == E_POINTER, "Unexpected hr %#x.\n", hr); + ok(ret == 123, "Unexpected result %d.\n", ret); + + hr = IOpcRelationshipSet_RelationshipExists(rels, id, NULL); + ok(hr == E_POINTER, "Unexpected hr %#x.\n", hr); + + ret = FALSE; + hr = IOpcRelationshipSet_RelationshipExists(rels, id, &ret); + ok(SUCCEEDED(hr), "Failed to get relationship, hr %#x.\n", hr); + ok(ret, "Unexpected result %d.\n", ret); + hr = IOpcRelationshipSet_GetRelationship(rels, id, &rel3); ok(SUCCEEDED(hr), "Failed to get relationship, hr %#x.\n", hr); IOpcRelationship_Release(rel3); @@ -302,6 +316,11 @@ todo_wine ok(hr == OPC_E_NO_SUCH_RELATIONSHIP, "Unexpected hr %#x.\n", hr); ok(rel3 == NULL, "Expected null pointer.\n");
+ ret = TRUE; + hr = IOpcRelationshipSet_RelationshipExists(rels, id, &ret); + ok(SUCCEEDED(hr), "Unexpected hr %#x.\n", hr); + ok(!ret, "Unexpected result %d.\n", ret); + CoTaskMemFree(id);
hr = IOpcRelationship_GetTargetUri(rel, &uri);