From adccf12e3b68d9cd0a58e5a2ef34acbe87648c2e Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Wed, 19 Sep 2018 14:19:38 +0300 Subject: [PATCH] opcservices: Write full content type stream. Signed-off-by: Nikolay Sivov Signed-off-by: Alexandre Julliard --- dlls/opcservices/factory.c | 3 + dlls/opcservices/package.c | 244 ++++++++++++++++++++++++++- dlls/opcservices/tests/opcservices.c | 79 +++++++++ 3 files changed, 317 insertions(+), 9 deletions(-) diff --git a/dlls/opcservices/factory.c b/dlls/opcservices/factory.c index 9c04964498d..ee155b3e714 100644 --- a/dlls/opcservices/factory.c +++ b/dlls/opcservices/factory.c @@ -387,6 +387,9 @@ static HRESULT WINAPI opc_factory_WritePackageToStream(IOpcFactory *iface, IOpcP { TRACE("iface %p, package %p, flags %#x, stream %p.\n", iface, package, flags, stream); + if (!package || !stream) + return E_POINTER; + return opc_package_write(package, flags, stream); } diff --git a/dlls/opcservices/package.c b/dlls/opcservices/package.c index ae610ac82d6..074f3c8f997 100644 --- a/dlls/opcservices/package.c +++ b/dlls/opcservices/package.c @@ -25,6 +25,7 @@ #include "xmllite.h" #include "wine/debug.h" +#include "wine/list.h" #include "wine/unicode.h" #include "opc_private.h" @@ -1511,23 +1512,243 @@ HRESULT opc_package_create(IOpcFactory *factory, IOpcPackage **out) return S_OK; } -static HRESULT opc_package_write_contenttypes(struct zip_archive *archive, IXmlWriter *writer) +struct content_types +{ + struct list types; +}; + +enum content_type_element +{ + CONTENT_TYPE_DEFAULT, + CONTENT_TYPE_OVERRIDE, +}; + +struct content_type +{ + struct list entry; + enum content_type_element element; + union + { + struct default_type + { + WCHAR *ext; + WCHAR *type; + } def; + struct override_type + { + IOpcPart *part; + } override; + } u; +}; + +static HRESULT opc_package_add_override_content_type(struct content_types *types, IOpcPart *part) +{ + struct content_type *type; + + if (!(type = heap_alloc(sizeof(*type)))) + return E_OUTOFMEMORY; + + type->element = CONTENT_TYPE_OVERRIDE; + type->u.override.part = part; + IOpcPart_AddRef(part); + + list_add_tail(&types->types, &type->entry); + + return S_OK; +} + +static HRESULT opc_package_add_default_content_type(struct content_types *types, + const WCHAR *ext, const WCHAR *content_type) +{ + struct content_type *type; + + if (!(type = heap_alloc(sizeof(*type)))) + return E_OUTOFMEMORY; + + type->element = CONTENT_TYPE_DEFAULT; + type->u.def.ext = opc_strdupW(ext); + type->u.def.type = opc_strdupW(content_type); + if (!type->u.def.ext || !type->u.def.type) + { + CoTaskMemFree(type->u.def.ext); + CoTaskMemFree(type->u.def.type); + heap_free(type); + return E_OUTOFMEMORY; + } + + list_add_tail(&types->types, &type->entry); + + return S_OK; +} + +static HRESULT opc_package_add_content_type(struct content_types *types, IOpcPart *part) +{ + struct content_type *cur; + BSTR ext, content_type; + BOOL added = FALSE; + IOpcPartUri *name; + HRESULT hr; + + if (FAILED(hr = IOpcPart_GetName(part, &name))) + return hr; + + hr = IOpcPartUri_GetExtension(name, &ext); + IOpcPartUri_Release(name); + if (hr == S_FALSE) + { + hr = opc_package_add_override_content_type(types, part); + SysFreeString(ext); + return hr; + } + + if (FAILED(hr)) + return hr; + + if (FAILED(hr = IOpcPart_GetContentType(part, &content_type))) + return hr; + + LIST_FOR_EACH_ENTRY(cur, &types->types, struct content_type, entry) + { + if (cur->element == CONTENT_TYPE_OVERRIDE) + continue; + + if (!strcmpiW(cur->u.def.ext, ext)) + { + added = TRUE; + + if (!strcmpW(cur->u.def.type, content_type)) + break; + + hr = opc_package_add_override_content_type(types, part); + break; + } + } + + if (!added) + hr = opc_package_add_default_content_type(types, ext, content_type); + + SysFreeString(ext); + SysFreeString(content_type); + + return hr; +} + +static HRESULT opc_package_collect_content_types(IOpcPackage *package, struct content_types *types) +{ + IOpcPartEnumerator *enumerator; + IOpcPartSet *parts; + BOOL has_next; + HRESULT hr; + + if (FAILED(hr = IOpcPackage_GetPartSet(package, &parts))) + return hr; + + hr = IOpcPartSet_GetEnumerator(parts, &enumerator); + IOpcPartSet_Release(parts); + if (FAILED(hr)) + return hr; + + if (FAILED(hr = IOpcPartEnumerator_MoveNext(enumerator, &has_next)) || !has_next) + { + IOpcPartEnumerator_Release(enumerator); + return hr; + } + + while (has_next) + { + IOpcPart *part; + + if (FAILED(hr = IOpcPartEnumerator_GetCurrent(enumerator, &part))) + break; + + hr = opc_package_add_content_type(types, part); + IOpcPart_Release(part); + if (FAILED(hr)) + break; + + IOpcPartEnumerator_MoveNext(enumerator, &has_next); + } + + IOpcPartEnumerator_Release(enumerator); + + return hr; +} + +static HRESULT opc_package_write_contenttypes(IOpcPackage *package, struct zip_archive *archive, IXmlWriter *writer) { static const WCHAR uriW[] = {'h','t','t','p',':','/','/','s','c','h','e','m','a','s','.','o','p','e','n','x','m','l','f','o','r','m','a','t','s','.','o','r','g','/', 'p','a','c','k','a','g','e','/','2','0','0','6','/','c','o','n','t','e','n','t','-','t','y','p','e','s',0}; static const WCHAR contenttypesW[] = {'[','C','o','n','t','e','n','t','_','T','y','p','e','s',']','.','x','m','l',0}; + static const WCHAR contenttypeW[] = {'C','o','n','t','e','n','t','T','y','p','e',0}; + static const WCHAR extensionW[] = {'E','x','t','e','n','s','i','o','n',0}; + static const WCHAR overrideW[] = {'O','v','e','r','r','i','d','e',0}; + static const WCHAR partnameW[] = {'P','a','r','t','N','a','m','e',0}; + static const WCHAR defaultW[] = {'D','e','f','a','u','l','t',0}; static const WCHAR typesW[] = {'T','y','p','e','s',0}; - IStream *content; + struct content_type *content_type, *content_type2; + struct content_types types; + IStream *content = NULL; HRESULT hr; - if (FAILED(hr = CreateStreamOnHGlobal(NULL, TRUE, &content))) - return hr; + list_init(&types.types); - hr = IXmlWriter_SetOutput(writer, (IUnknown *)content); + hr = CreateStreamOnHGlobal(NULL, TRUE, &content); + if (SUCCEEDED(hr)) + hr = opc_package_collect_content_types(package, &types); + if (SUCCEEDED(hr)) + hr = IXmlWriter_SetOutput(writer, (IUnknown *)content); if (SUCCEEDED(hr)) hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Omit); if (SUCCEEDED(hr)) hr = IXmlWriter_WriteStartElement(writer, NULL, typesW, uriW); + + LIST_FOR_EACH_ENTRY_SAFE(content_type, content_type2, &types.types, struct content_type, entry) + { + if (content_type->element == CONTENT_TYPE_DEFAULT) + { + if (SUCCEEDED(hr)) + hr = IXmlWriter_WriteStartElement(writer, NULL, defaultW, NULL); + if (SUCCEEDED(hr)) + hr = IXmlWriter_WriteAttributeString(writer, NULL, extensionW, NULL, content_type->u.def.ext + 1); + if (SUCCEEDED(hr)) + hr = IXmlWriter_WriteAttributeString(writer, NULL, contenttypeW, NULL, content_type->u.def.type); + + CoTaskMemFree(content_type->u.def.ext); + CoTaskMemFree(content_type->u.def.type); + } + else + { + IOpcPartUri *uri = NULL; + WCHAR *type = NULL; + BSTR name = NULL; + + if (SUCCEEDED(hr)) + hr = IXmlWriter_WriteStartElement(writer, NULL, overrideW, NULL); + if (SUCCEEDED(hr)) + hr = IOpcPart_GetName(content_type->u.override.part, &uri); + if (SUCCEEDED(hr)) + hr = IOpcPartUri_GetRawUri(uri, &name); + if (SUCCEEDED(hr)) + hr = IXmlWriter_WriteAttributeString(writer, NULL, partnameW, NULL, name); + if (SUCCEEDED(hr)) + hr = IOpcPart_GetContentType(content_type->u.override.part, &type); + if (SUCCEEDED(hr)) + hr = IXmlWriter_WriteAttributeString(writer, NULL, contenttypeW, NULL, type); + + if (uri) + IOpcPartUri_Release(uri); + SysFreeString(name); + CoTaskMemFree(type); + + IOpcPart_Release(content_type->u.override.part); + } + if (SUCCEEDED(hr)) + hr = IXmlWriter_WriteEndElement(writer); + + list_remove(&content_type->entry); + heap_free(content_type); + } + if (SUCCEEDED(hr)) hr = IXmlWriter_WriteEndDocument(writer); if (SUCCEEDED(hr)) @@ -1535,7 +1756,9 @@ static HRESULT opc_package_write_contenttypes(struct zip_archive *archive, IXmlW if (SUCCEEDED(hr)) hr = compress_add_file(archive, contenttypesW, content, OPC_COMPRESSION_NORMAL); - IStream_Release(content); + + if (content) + IStream_Release(content); return hr; } @@ -1736,7 +1959,7 @@ HRESULT opc_package_write(IOpcPackage *package, OPC_WRITE_FLAGS flags, IStream * } /* [Content_Types].xml */ - hr = opc_package_write_contenttypes(archive, writer); + hr = opc_package_write_contenttypes(package, archive, writer); /* Package relationships. */ if (SUCCEEDED(hr)) hr = IOpcPackage_GetRelationshipSet(package, &rels); @@ -1748,10 +1971,13 @@ HRESULT opc_package_write(IOpcPackage *package, OPC_WRITE_FLAGS flags, IStream * if (SUCCEEDED(hr)) hr = opc_package_write_parts(archive, package, writer); - IOpcRelationshipSet_Release(rels); + if (rels) + IOpcRelationshipSet_Release(rels); + if (uri) + IOpcUri_Release(uri); + compress_finalize_archive(archive); IXmlWriter_Release(writer); - IOpcUri_Release(uri); return hr; } diff --git a/dlls/opcservices/tests/opcservices.c b/dlls/opcservices/tests/opcservices.c index 8bcd9140df7..764dca1cf3f 100644 --- a/dlls/opcservices/tests/opcservices.c +++ b/dlls/opcservices/tests/opcservices.c @@ -1194,6 +1194,84 @@ static void test_create_part_uri(void) IOpcFactory_Release(factory); } +static HRESULT WINAPI custom_package_QueryInterface(IOpcPackage *iface, REFIID iid, void **out) +{ + if (IsEqualIID(iid, &IID_IOpcPackage) || IsEqualIID(iid, &IID_IUnknown)) + { + *out = iface; + IOpcPackage_AddRef(iface); + return S_OK; + } + + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI custom_package_AddRef(IOpcPackage *iface) +{ + return 2; +} + +static ULONG WINAPI custom_package_Release(IOpcPackage *iface) +{ + return 1; +} + +static HRESULT WINAPI custom_package_GetPartSet(IOpcPackage *iface, IOpcPartSet **part_set) +{ + return 0x80000001; +} + +static HRESULT WINAPI custom_package_GetRelationshipSet(IOpcPackage *iface, IOpcRelationshipSet **relationship_set) +{ + return 0x80000001; +} + +static const IOpcPackageVtbl custom_package_vtbl = +{ + custom_package_QueryInterface, + custom_package_AddRef, + custom_package_Release, + custom_package_GetPartSet, + custom_package_GetRelationshipSet, +}; + +static void test_write_package(void) +{ + IOpcPackage custom_package = { &custom_package_vtbl }; + IOpcFactory *factory; + IOpcPackage *package; + IStream *stream; + HRESULT hr; + + factory = create_factory(); + + hr = IOpcFactory_CreatePackage(factory, &package); + ok(SUCCEEDED(hr) || broken(hr == E_NOTIMPL) /* Vista */, "Failed to create a package, hr %#x.\n", hr); + if (FAILED(hr)) + { + IOpcFactory_Release(factory); + return; + } + + hr = IOpcFactory_WritePackageToStream(factory, NULL, OPC_WRITE_FORCE_ZIP32, NULL); + ok(hr == E_POINTER, "Unexpected hr %#x.\n", hr); + + hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); + ok(SUCCEEDED(hr), "Failed to create a stream, hr %#x.\n", hr); + + hr = IOpcFactory_WritePackageToStream(factory, NULL, OPC_WRITE_FORCE_ZIP32, stream); + ok(hr == E_POINTER, "Unexpected hr %#x.\n", hr); + + hr = IOpcFactory_WritePackageToStream(factory, &custom_package, OPC_WRITE_FORCE_ZIP32, stream); + ok(hr == 0x80000001, "Unexpected hr %#x.\n", hr); + + IStream_Release(stream); + + IOpcFactory_Release(factory); + IOpcPackage_Release(package); +} + START_TEST(opcservices) { IOpcFactory *factory; @@ -1217,6 +1295,7 @@ START_TEST(opcservices) test_relative_uri(); test_combine_uri(); test_create_part_uri(); + test_write_package(); IOpcFactory_Release(factory);