[PATCH 1/3] opcservices: Implement relationships parts uri support.

Nikolay Sivov nsivov at codeweavers.com
Fri Sep 7 05:56:12 CDT 2018


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/opcservices/Makefile.in         |   2 +-
 dlls/opcservices/factory.c           |  19 ++-
 dlls/opcservices/opc_private.h       |  15 ++-
 dlls/opcservices/tests/opcservices.c | 167 +++++++++++++++++++++++++
 dlls/opcservices/uri.c               | 179 ++++++++++++++++++++++-----
 include/opcbase.idl                  |   2 +
 6 files changed, 347 insertions(+), 37 deletions(-)

diff --git a/dlls/opcservices/Makefile.in b/dlls/opcservices/Makefile.in
index bdc4c80ba8..42b64b3ba9 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 13fa702293..2ecd5adca9 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;
+
+    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(uri, part_uri);
+    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 afe5c68561..c7b1d0cab3 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 4bb3d6407a..63932fe832 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 594abc36f1..0c5afa9b29 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;
+    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;
+}
+
+static HRESULT opc_source_uri_create(struct opc_uri *uri, IOpcUri **out)
+{
+    struct opc_uri *obj;
+    HRESULT hr;
+
+    if (!out)
+        return E_POINTER;
 
-    if (FAILED(hr = CreateUri(uri, Uri_CREATE_ALLOW_RELATIVE, 0, &object->uri)))
+    *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(obj, NULL, uri->source_uri->is_part_uri, uri->source_uri->uri)))
+    {
+        WARN("Failed to init part uri, hr %#x.\n", hr);
+        heap_free(obj);
         return hr;
+    }
+
+    *out = (IOpcUri *)&obj->IOpcPartUri_iface;
+
+    TRACE("Created source uri %p.\n", *out);
 
     return S_OK;
 }
 
-HRESULT opc_part_uri_create(const WCHAR *str, IOpcPartUri **out)
+HRESULT opc_part_uri_create(IUri *uri, struct opc_uri *source_uri, IOpcPartUri **out)
 {
-    struct opc_uri *uri;
+    struct opc_uri *obj;
     HRESULT hr;
 
-    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, TRUE, str)))
+    if (FAILED(hr = opc_part_uri_init(obj, source_uri, TRUE, uri)))
     {
         WARN("Failed to init part uri, hr %#x.\n", hr);
-        heap_free(uri);
+        heap_free(obj);
         return hr;
     }
 
-    *out = &uri->IOpcPartUri_iface;
+    *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 ca42ceb4e3..fae5858ed6 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)")
-- 
2.18.0




More information about the wine-devel mailing list