[PATCH 10/10] opcservices: Write full content type stream.

Nikolay Sivov nsivov at codeweavers.com
Wed Sep 19 06:19:38 CDT 2018


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 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 9c04964498..ee155b3e71 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 ae610ac82d..074f3c8f99 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 8bcd9140df..764dca1cf3 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);
 
-- 
2.18.0




More information about the wine-devel mailing list