diff --git a/dlls/opcservices/Makefile.in b/dlls/opcservices/Makefile.in index bdc4c80ba8e..42b64b3ba96 100644 --- a/dlls/opcservices/Makefile.in +++ b/dlls/opcservices/Makefile.in @@ -1,5 +1,5 @@ MODULE = opcservices.dll -IMPORTS = uuid ole32 advapi32 urlmon xmllite +IMPORTS = uuid ole32 advapi32 urlmon xmllite oleaut32 EXTRALIBS = $(Z_LIBS) C_SRCS = \ diff --git a/dlls/opcservices/factory.c b/dlls/opcservices/factory.c index 13fa702293e..2ecd5adca9d 100644 --- a/dlls/opcservices/factory.c +++ b/dlls/opcservices/factory.c @@ -310,18 +310,25 @@ static ULONG WINAPI opc_factory_Release(IOpcFactory *iface) static HRESULT WINAPI opc_factory_CreatePackageRootUri(IOpcFactory *iface, IOpcUri **uri) { - static const WCHAR rootW[] = {'/',0}; - TRACE("iface %p, uri %p.\n", iface, uri); - return opc_uri_create(rootW, uri); + return opc_root_uri_create(uri); } -static HRESULT WINAPI opc_factory_CreatePartUri(IOpcFactory *iface, LPCWSTR uri, IOpcPartUri **part_uri) +static HRESULT WINAPI opc_factory_CreatePartUri(IOpcFactory *iface, LPCWSTR uri, IOpcPartUri **out) { - TRACE("iface %p, uri %s, part_uri %p.\n", iface, debugstr_w(uri), part_uri); + IUri *part_uri; + HRESULT hr; - return opc_part_uri_create(uri, part_uri); + TRACE("iface %p, uri %s, out %p.\n", iface, debugstr_w(uri), out); + + if (FAILED(hr = CreateUri(uri, Uri_CREATE_ALLOW_RELATIVE, 0, &part_uri))) + { + WARN("Failed to create uri, hr %#x.\n", hr); + return hr; + } + + return opc_part_uri_create(part_uri, NULL, out); } static HRESULT WINAPI opc_factory_CreateStreamOnFile(IOpcFactory *iface, LPCWSTR filename, diff --git a/dlls/opcservices/opc_private.h b/dlls/opcservices/opc_private.h index afe5c685614..c7b1d0cab3d 100644 --- a/dlls/opcservices/opc_private.h +++ b/dlls/opcservices/opc_private.h @@ -45,9 +45,20 @@ static inline BOOL opc_array_reserve(void **elements, size_t *capacity, size_t c return TRUE; } +struct opc_uri +{ + IOpcPartUri IOpcPartUri_iface; + LONG refcount; + BOOL is_part_uri; + + IUri *uri; + IUri *rels_part_uri; + struct opc_uri *source_uri; +}; + 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_part_uri_create(IUri *uri, struct opc_uri *source_uri, IOpcPartUri **part_uri) DECLSPEC_HIDDEN; +extern HRESULT opc_root_uri_create(IOpcUri **opc_uri) DECLSPEC_HIDDEN; extern HRESULT opc_package_write(IOpcPackage *package, OPC_WRITE_FLAGS flags, IStream *stream) DECLSPEC_HIDDEN; diff --git a/dlls/opcservices/tests/opcservices.c b/dlls/opcservices/tests/opcservices.c index 4bb3d6407a2..63932fe832e 100644 --- a/dlls/opcservices/tests/opcservices.c +++ b/dlls/opcservices/tests/opcservices.c @@ -26,6 +26,7 @@ #include "msopc.h" #include "urlmon.h" +#include "wine/heap.h" #include "wine/test.h" static IOpcFactory *create_factory(void) @@ -373,6 +374,171 @@ todo_wine IOpcFactory_Release(factory); } +static WCHAR *strdupAtoW(const char *str) +{ + WCHAR *ret = NULL; + DWORD len; + + if (!str) return ret; + len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); + ret = heap_alloc(len * sizeof(WCHAR)); + if (ret) + MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len); + return ret; +} + +static void test_rel_part_uri(void) +{ + static const struct + { + const char *uri; + const char *rel_uri; + HRESULT hr; + } rel_part_uri_tests[] = + { + { "/uri", "/_rels/uri.rels" }, + { "/uri.ext", "/_rels/uri.ext.rels" }, + { "/", "/_rels/.rels" }, + { "/_rels/uri.ext.rels", "", OPC_E_NONCONFORMING_URI }, + }; + static const struct + { + const char *uri; + BOOL ret; + } is_rel_part_tests[] = + { + { "/uri", FALSE }, + { "/_rels/uri", FALSE }, + { "/_rels/uri/uri", FALSE }, + { "/_rels/uri/uri.rels", FALSE }, + { "/uri/uri.rels", FALSE }, + { "/uri/_rels/uri.rels", TRUE }, + { "/_rels/.rels", TRUE }, + }; + static const WCHAR testuriW[] = {'/','u','r','i',0}; + IOpcPartUri *part_uri; + IOpcFactory *factory; + IOpcUri *source_uri; + unsigned int i; + WCHAR *uriW; + HRESULT hr; + + factory = create_factory(); + + hr = IOpcFactory_CreatePartUri(factory, testuriW, &part_uri); + ok(SUCCEEDED(hr), "Failed to create part uri, hr %#x.\n", hr); + + hr = IOpcPartUri_GetRelationshipsPartUri(part_uri, NULL); + ok(hr == E_POINTER, "Unexpected hr %#x.\n", hr); + + hr = IOpcPartUri_IsRelationshipsPartUri(part_uri, NULL); + ok(hr == E_POINTER, "Unexpected hr %#x.\n", hr); + + hr = IOpcPartUri_GetSourceUri(part_uri, NULL); + ok(hr == E_POINTER, "Unexpected hr %#x.\n", hr); + + source_uri = (void *)0xdeadbeef; + hr = IOpcPartUri_GetSourceUri(part_uri, &source_uri); + ok(hr == OPC_E_RELATIONSHIP_URI_REQUIRED, "Unexpected hr %#x.\n", hr); + ok(source_uri == NULL, "Expected null uri.\n"); + + IOpcPartUri_Release(part_uri); + + for (i = 0; i < ARRAY_SIZE(rel_part_uri_tests); ++i) + { + BOOL is_root = FALSE; + IOpcPartUri *rel_uri; + IOpcUri *part_uri; + WCHAR *rel_uriW; + + uriW = strdupAtoW(rel_part_uri_tests[i].uri); + rel_uriW = strdupAtoW(rel_part_uri_tests[i].rel_uri); + + if (!strcmp(rel_part_uri_tests[i].uri, "/")) + { + hr = IOpcFactory_CreatePackageRootUri(factory, &part_uri); + is_root = TRUE; + } + else + hr = IOpcFactory_CreatePartUri(factory, uriW, (IOpcPartUri **)&part_uri); + ok(SUCCEEDED(hr), "Failed to create part uri, hr %#x.\n", hr); + + rel_uri = (void *)0xdeadbeef; + hr = IOpcUri_GetRelationshipsPartUri(part_uri, &rel_uri); + if (SUCCEEDED(hr)) + { + IOpcPartUri *rel_uri2; + IOpcUri *source_uri2; + IUnknown *unk = NULL; + BOOL ret; + BSTR str; + + hr = IOpcPartUri_GetSourceUri(rel_uri, &source_uri); + ok(SUCCEEDED(hr), "Failed to get source uri, hr %#x.\n", hr); + hr = IOpcPartUri_GetSourceUri(rel_uri, &source_uri2); + ok(SUCCEEDED(hr), "Failed to get source uri, hr %#x.\n", hr); + ok(source_uri != source_uri2, "Unexpected instance.\n"); + hr = IOpcUri_IsEqual(source_uri, (IUri *)source_uri2, &ret); + todo_wine { + ok(SUCCEEDED(hr), "IsEqual failed, hr %#x.\n", hr); + ok(ret, "Expected equal uris.\n"); + } + hr = IOpcUri_QueryInterface(source_uri, &IID_IOpcPartUri, (void **)&unk); + ok(hr == (is_root ? E_NOINTERFACE : S_OK), "Unexpected hr %#x, %s.\n", hr, rel_part_uri_tests[i].uri); + if (unk) + IUnknown_Release(unk); + + IOpcUri_Release(source_uri2); + IOpcUri_Release(source_uri); + + hr = IOpcUri_GetRelationshipsPartUri(part_uri, &rel_uri2); + ok(SUCCEEDED(hr), "Failed to get rels part uri, hr %#x.\n", hr); + ok(rel_uri2 != rel_uri, "Unexpected instance.\n"); + IOpcPartUri_Release(rel_uri2); + + hr = IOpcPartUri_GetRawUri(rel_uri, &str); + ok(SUCCEEDED(hr), "Failed to get rel uri, hr %#x.\n", hr); + ok(!lstrcmpW(str, rel_uriW), "%u: unexpected rel uri %s, expected %s.\n", i, wine_dbgstr_w(str), + wine_dbgstr_w(rel_uriW)); + SysFreeString(str); + + IOpcPartUri_Release(rel_uri); + } + else + { + ok(hr == rel_part_uri_tests[i].hr, "%u: unexpected hr %#x.\n", i, hr); + ok(rel_uri == NULL, "%u: unexpected out pointer.\n", i); + } + + heap_free(uriW); + heap_free(rel_uriW); + + IOpcUri_Release(part_uri); + } + + for (i = 0; i < ARRAY_SIZE(is_rel_part_tests); ++i) + { + IOpcPartUri *part_uri; + BOOL ret; + + uriW = strdupAtoW(is_rel_part_tests[i].uri); + + hr = IOpcFactory_CreatePartUri(factory, uriW, &part_uri); + ok(SUCCEEDED(hr), "Failed to create part uri, hr %#x.\n", hr); + + ret = 123; + hr = IOpcPartUri_IsRelationshipsPartUri(part_uri, &ret); + ok(SUCCEEDED(hr), "Unexpected hr %#x.\n", hr); + ok(ret == is_rel_part_tests[i].ret, "%u: unexpected result %d.\n", i, ret); + + heap_free(uriW); + + IOpcPartUri_Release(part_uri); + } + + IOpcFactory_Release(factory); +} + START_TEST(opcservices) { IOpcFactory *factory; @@ -390,6 +556,7 @@ START_TEST(opcservices) test_package(); test_file_stream(); test_relationship(); + test_rel_part_uri(); IOpcFactory_Release(factory); diff --git a/dlls/opcservices/uri.c b/dlls/opcservices/uri.c index 594abc36f18..0c5afa9b299 100644 --- a/dlls/opcservices/uri.c +++ b/dlls/opcservices/uri.c @@ -23,25 +23,19 @@ #include "winbase.h" #include "wine/debug.h" +#include "wine/unicode.h" #include "opc_private.h" WINE_DEFAULT_DEBUG_CHANNEL(msopc); -struct opc_uri -{ - IOpcPartUri IOpcPartUri_iface; - LONG refcount; - BOOL is_part_uri; - - IUri *uri; -}; - static inline struct opc_uri *impl_from_IOpcPartUri(IOpcPartUri *iface) { return CONTAINING_RECORD(iface, struct opc_uri, IOpcPartUri_iface); } +static HRESULT opc_source_uri_create(struct opc_uri *uri, IOpcUri **out); + static HRESULT WINAPI opc_uri_QueryInterface(IOpcPartUri *iface, REFIID iid, void **out) { struct opc_uri *uri = impl_from_IOpcPartUri(iface); @@ -81,6 +75,10 @@ static ULONG WINAPI opc_uri_Release(IOpcPartUri *iface) if (!refcount) { + if (uri->rels_part_uri) + IUri_Release(uri->rels_part_uri); + if (uri->source_uri) + IOpcPartUri_Release(&uri->source_uri->IOpcPartUri_iface); IUri_Release(uri->uri); heap_free(uri); } @@ -319,9 +317,20 @@ static HRESULT WINAPI opc_uri_IsEqual(IOpcPartUri *iface, IUri *comparand, BOOL static HRESULT WINAPI opc_uri_GetRelationshipsPartUri(IOpcPartUri *iface, IOpcPartUri **part_uri) { - FIXME("iface %p, part_uri %p stub!\n", iface, part_uri); + struct opc_uri *uri = impl_from_IOpcPartUri(iface); - return E_NOTIMPL; + TRACE("iface %p, part_uri %p.\n", iface, part_uri); + + if (!part_uri) + return E_POINTER; + + if (!uri->rels_part_uri) + { + *part_uri = NULL; + return OPC_E_NONCONFORMING_URI; + } + + return opc_part_uri_create(uri->rels_part_uri, uri, part_uri); } static HRESULT WINAPI opc_uri_GetRelativeUri(IOpcPartUri *iface, IOpcPartUri *part_uri, @@ -349,16 +358,25 @@ static HRESULT WINAPI opc_uri_ComparePartUri(IOpcPartUri *iface, IOpcPartUri *pa static HRESULT WINAPI opc_uri_GetSourceUri(IOpcPartUri *iface, IOpcUri **source_uri) { - FIXME("iface %p, source_uri %p stub!\n", iface, source_uri); + struct opc_uri *uri = impl_from_IOpcPartUri(iface); - return E_NOTIMPL; + TRACE("iface %p, source_uri %p.\n", iface, source_uri); + + return opc_source_uri_create(uri, source_uri); } static HRESULT WINAPI opc_uri_IsRelationshipsPartUri(IOpcPartUri *iface, BOOL *result) { - FIXME("iface %p, result %p stub!\n", iface, result); + struct opc_uri *uri = impl_from_IOpcPartUri(iface); - return E_NOTIMPL; + TRACE("iface %p, result %p.\n", iface, result); + + if (!result) + return E_POINTER; + + *result = !uri->rels_part_uri; + + return S_OK; } static const IOpcPartUriVtbl opc_part_uri_vtbl = @@ -399,56 +417,161 @@ static const IOpcPartUriVtbl opc_part_uri_vtbl = opc_uri_IsRelationshipsPartUri, }; -static HRESULT opc_part_uri_init(struct opc_uri *object, BOOL is_part_uri, const WCHAR *uri) +static IUri *opc_part_uri_get_rels_uri(IUri *uri) { + static const WCHAR relsdirW[] = {'/','_','r','e','l','s',0}; + static const WCHAR relsextW[] = {'.','r','e','l','s',0}; + WCHAR *start = NULL, *end, *ret; + IUri *rels_uri; HRESULT hr; + DWORD len; + BSTR path; + if (FAILED(IUri_GetPath(uri, &path))) + return NULL; + + if (FAILED(IUri_GetPropertyLength(uri, Uri_PROPERTY_PATH, &len, 0))) + { + SysFreeString(path); + return NULL; + } + + end = strrchrW(path, '/'); + if (end && end >= path + ARRAY_SIZE(relsdirW) - 1) + start = end - ARRAY_SIZE(relsdirW) + 1; + if (!start) + start = end; + + /* Test if it's already relationships uri. */ + if (len > ARRAY_SIZE(relsextW)) + { + if (!strcmpW(path + len - ARRAY_SIZE(relsextW) + 1, relsextW)) + { + if (start && !memcmp(start, relsdirW, ARRAY_SIZE(relsdirW) - sizeof(WCHAR))) + { + SysFreeString(path); + return NULL; + } + } + } + + ret = heap_alloc((len + ARRAY_SIZE(relsextW) + ARRAY_SIZE(relsdirW)) * sizeof(WCHAR)); + if (!ret) + { + SysFreeString(path); + return NULL; + } + ret[0] = 0; + + if (start != path) + { + memcpy(ret, path, (start - path) * sizeof(WCHAR)); + ret[start - path] = 0; + } + + strcatW(ret, relsdirW); + strcatW(ret, end); + strcatW(ret, relsextW); + + if (FAILED(hr = CreateUri(ret, Uri_CREATE_ALLOW_RELATIVE, 0, &rels_uri))) + WARN("Failed to create rels uri, hr %#x.\n", hr); + heap_free(ret); + + return rels_uri; +} + +static HRESULT opc_part_uri_init(struct opc_uri *object, struct opc_uri *source_uri, BOOL is_part_uri, IUri *uri) +{ object->IOpcPartUri_iface.lpVtbl = &opc_part_uri_vtbl; object->refcount = 1; object->is_part_uri = is_part_uri; - - if (FAILED(hr = CreateUri(uri, Uri_CREATE_ALLOW_RELATIVE, 0, &object->uri))) - return hr; + object->uri = uri; + IUri_AddRef(object->uri); + object->rels_part_uri = opc_part_uri_get_rels_uri(object->uri); + object->source_uri = source_uri; + if (object->source_uri) + IOpcPartUri_AddRef(&object->source_uri->IOpcPartUri_iface); return S_OK; } -HRESULT opc_part_uri_create(const WCHAR *str, IOpcPartUri **out) +static HRESULT opc_source_uri_create(struct opc_uri *uri, IOpcUri **out) { - struct opc_uri *uri; + struct opc_uri *obj; HRESULT hr; - if (!(uri = heap_alloc_zero(sizeof(*uri)))) + if (!out) + return E_POINTER; + + *out = NULL; + + if (!uri->source_uri) + return OPC_E_RELATIONSHIP_URI_REQUIRED; + + if (!(obj = heap_alloc_zero(sizeof(*obj)))) return E_OUTOFMEMORY; - if (FAILED(hr = opc_part_uri_init(uri, TRUE, str))) + if (FAILED(hr = opc_part_uri_init(obj, NULL, uri->source_uri->is_part_uri, uri->source_uri->uri))) { WARN("Failed to init part uri, hr %#x.\n", hr); - heap_free(uri); + heap_free(obj); return hr; } - *out = &uri->IOpcPartUri_iface; + *out = (IOpcUri *)&obj->IOpcPartUri_iface; + + TRACE("Created source uri %p.\n", *out); + + return S_OK; +} + +HRESULT opc_part_uri_create(IUri *uri, struct opc_uri *source_uri, IOpcPartUri **out) +{ + struct opc_uri *obj; + HRESULT hr; + + if (!(obj = heap_alloc_zero(sizeof(*obj)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = opc_part_uri_init(obj, source_uri, TRUE, uri))) + { + WARN("Failed to init part uri, hr %#x.\n", hr); + heap_free(obj); + return hr; + } + + *out = &obj->IOpcPartUri_iface; TRACE("Created part uri %p.\n", *out); return S_OK; } -HRESULT opc_uri_create(const WCHAR *str, IOpcUri **out) +HRESULT opc_root_uri_create(IOpcUri **out) { - struct opc_uri *uri; + static const WCHAR rootW[] = {'/',0}; + struct opc_uri *obj; HRESULT hr; + IUri *uri; - if (!(uri = heap_alloc_zero(sizeof(*uri)))) + if (!(obj = heap_alloc_zero(sizeof(*obj)))) return E_OUTOFMEMORY; - if (FAILED(hr = opc_part_uri_init(uri, FALSE, str))) + if (FAILED(hr = CreateUri(rootW, Uri_CREATE_ALLOW_RELATIVE, 0, &uri))) + { + WARN("Failed to create rels uri, hr %#x.\n", hr); + heap_free(obj); + return hr; + } + + hr = opc_part_uri_init(obj, NULL, FALSE, uri); + IUri_Release(uri); + if (FAILED(hr)) { WARN("Failed to init uri, hr %#x.\n", hr); heap_free(uri); return hr; } - *out = (IOpcUri *)&uri->IOpcPartUri_iface; + *out = (IOpcUri *)&obj->IOpcPartUri_iface; TRACE("Created part uri %p.\n", *out); return S_OK; } diff --git a/include/opcbase.idl b/include/opcbase.idl index ca42ceb4e33..fae5858ed6d 100644 --- a/include/opcbase.idl +++ b/include/opcbase.idl @@ -41,5 +41,7 @@ typedef [v1_enum] enum OPC_URI_TARGET_MODE_EXTERNAL = 1, } OPC_URI_TARGET_MODE; +cpp_quote("#define OPC_E_NONCONFORMING_URI MAKE_HRESULT(SEVERITY_ERROR, FACILITY_OPC, 0x1)") +cpp_quote("#define OPC_E_RELATIONSHIP_URI_REQUIRED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_OPC, 0x3)") 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)")