opcservices: Write full content type stream.
Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
c009967bae
commit
adccf12e3b
|
@ -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);
|
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);
|
return opc_package_write(package, flags, stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "xmllite.h"
|
#include "xmllite.h"
|
||||||
|
|
||||||
#include "wine/debug.h"
|
#include "wine/debug.h"
|
||||||
|
#include "wine/list.h"
|
||||||
#include "wine/unicode.h"
|
#include "wine/unicode.h"
|
||||||
|
|
||||||
#include "opc_private.h"
|
#include "opc_private.h"
|
||||||
|
@ -1511,23 +1512,243 @@ HRESULT opc_package_create(IOpcFactory *factory, IOpcPackage **out)
|
||||||
return S_OK;
|
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','/',
|
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};
|
'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 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};
|
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;
|
HRESULT hr;
|
||||||
|
|
||||||
if (FAILED(hr = CreateStreamOnHGlobal(NULL, TRUE, &content)))
|
list_init(&types.types);
|
||||||
return hr;
|
|
||||||
|
|
||||||
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))
|
if (SUCCEEDED(hr))
|
||||||
hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Omit);
|
hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Omit);
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr))
|
||||||
hr = IXmlWriter_WriteStartElement(writer, NULL, typesW, uriW);
|
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))
|
if (SUCCEEDED(hr))
|
||||||
hr = IXmlWriter_WriteEndDocument(writer);
|
hr = IXmlWriter_WriteEndDocument(writer);
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr))
|
||||||
|
@ -1535,7 +1756,9 @@ static HRESULT opc_package_write_contenttypes(struct zip_archive *archive, IXmlW
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr))
|
||||||
hr = compress_add_file(archive, contenttypesW, content, OPC_COMPRESSION_NORMAL);
|
hr = compress_add_file(archive, contenttypesW, content, OPC_COMPRESSION_NORMAL);
|
||||||
IStream_Release(content);
|
|
||||||
|
if (content)
|
||||||
|
IStream_Release(content);
|
||||||
|
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
@ -1736,7 +1959,7 @@ HRESULT opc_package_write(IOpcPackage *package, OPC_WRITE_FLAGS flags, IStream *
|
||||||
}
|
}
|
||||||
|
|
||||||
/* [Content_Types].xml */
|
/* [Content_Types].xml */
|
||||||
hr = opc_package_write_contenttypes(archive, writer);
|
hr = opc_package_write_contenttypes(package, archive, writer);
|
||||||
/* Package relationships. */
|
/* Package relationships. */
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr))
|
||||||
hr = IOpcPackage_GetRelationshipSet(package, &rels);
|
hr = IOpcPackage_GetRelationshipSet(package, &rels);
|
||||||
|
@ -1748,10 +1971,13 @@ HRESULT opc_package_write(IOpcPackage *package, OPC_WRITE_FLAGS flags, IStream *
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr))
|
||||||
hr = opc_package_write_parts(archive, package, writer);
|
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);
|
compress_finalize_archive(archive);
|
||||||
IXmlWriter_Release(writer);
|
IXmlWriter_Release(writer);
|
||||||
IOpcUri_Release(uri);
|
|
||||||
|
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1194,6 +1194,84 @@ static void test_create_part_uri(void)
|
||||||
IOpcFactory_Release(factory);
|
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)
|
START_TEST(opcservices)
|
||||||
{
|
{
|
||||||
IOpcFactory *factory;
|
IOpcFactory *factory;
|
||||||
|
@ -1217,6 +1295,7 @@ START_TEST(opcservices)
|
||||||
test_relative_uri();
|
test_relative_uri();
|
||||||
test_combine_uri();
|
test_combine_uri();
|
||||||
test_create_part_uri();
|
test_create_part_uri();
|
||||||
|
test_write_package();
|
||||||
|
|
||||||
IOpcFactory_Release(factory);
|
IOpcFactory_Release(factory);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue