diff --git a/dlls/msxml3/element.c b/dlls/msxml3/element.c index 26bfd0cbe18..db6b2836544 100644 --- a/dlls/msxml3/element.c +++ b/dlls/msxml3/element.c @@ -732,6 +732,75 @@ static HRESULT WINAPI domelem_get_nodeTypedValue( return hr; } +static HRESULT encode_base64(const BYTE *buf, int len, BSTR *ret) +{ + static const char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + const BYTE *d = buf; + int bytes, pad_bytes, div, i; + DWORD needed; + WCHAR *ptr; + + bytes = (len*8 + 5)/6; + pad_bytes = (bytes % 4) ? 4 - (bytes % 4) : 0; + + TRACE("%d, bytes is %d, pad bytes is %d\n", len, bytes, pad_bytes); + needed = bytes + pad_bytes + 1; + + *ret = SysAllocStringLen(NULL, needed); + if (!*ret) return E_OUTOFMEMORY; + + /* Three bytes of input give 4 chars of output */ + div = len / 3; + + ptr = *ret; + i = 0; + while (div > 0) + { + /* first char is the first 6 bits of the first byte*/ + *ptr++ = b64[ ( d[0] >> 2) & 0x3f ]; + /* second char is the last 2 bits of the first byte and the first 4 + * bits of the second byte */ + *ptr++ = b64[ ((d[0] << 4) & 0x30) | (d[1] >> 4 & 0x0f)]; + /* third char is the last 4 bits of the second byte and the first 2 + * bits of the third byte */ + *ptr++ = b64[ ((d[1] << 2) & 0x3c) | (d[2] >> 6 & 0x03)]; + /* fourth char is the remaining 6 bits of the third byte */ + *ptr++ = b64[ d[2] & 0x3f]; + i += 4; + d += 3; + div--; + } + + switch (pad_bytes) + { + case 1: + /* first char is the first 6 bits of the first byte*/ + *ptr++ = b64[ ( d[0] >> 2) & 0x3f ]; + /* second char is the last 2 bits of the first byte and the first 4 + * bits of the second byte */ + *ptr++ = b64[ ((d[0] << 4) & 0x30) | (d[1] >> 4 & 0x0f)]; + /* third char is the last 4 bits of the second byte padded with + * two zeroes */ + *ptr++ = b64[ ((d[1] << 2) & 0x3c) ]; + /* fourth char is a = to indicate one byte of padding */ + *ptr++ = '='; + break; + case 2: + /* first char is the first 6 bits of the first byte*/ + *ptr++ = b64[ ( d[0] >> 2) & 0x3f ]; + /* second char is the last 2 bits of the first byte padded with + * four zeroes*/ + *ptr++ = b64[ ((d[0] << 4) & 0x30)]; + /* third char is = to indicate padding */ + *ptr++ = '='; + /* fourth char is = to indicate padding */ + *ptr++ = '='; + break; + } + + return S_OK; +} + static HRESULT WINAPI domelem_put_nodeTypedValue( IXMLDOMElement *iface, VARIANT value) @@ -743,9 +812,10 @@ static HRESULT WINAPI domelem_put_nodeTypedValue( TRACE("(%p)->(%s)\n", This, debugstr_variant(&value)); dt = element_get_dt(get_element(This)); - /* for untyped node coerce to BSTR and set */ - if (dt == DT_INVALID) + switch (dt) { + /* for untyped node coerce to BSTR and set */ + case DT_INVALID: if (V_VT(&value) != VT_BSTR) { VARIANT content; @@ -759,9 +829,43 @@ static HRESULT WINAPI domelem_put_nodeTypedValue( } else hr = node_set_content(&This->node, V_BSTR(&value)); - } - else - { + break; + case DT_BIN_BASE64: + if (V_VT(&value) == VT_BSTR) + hr = node_set_content(&This->node, V_BSTR(&value)); + else if (V_VT(&value) == (VT_UI1|VT_ARRAY)) + { + UINT dim = SafeArrayGetDim(V_ARRAY(&value)); + LONG lbound, ubound; + BSTR encoded; + BYTE *ptr; + int len; + + if (dim > 1) + FIXME("unexpected array dimension count %u\n", dim); + + SafeArrayGetUBound(V_ARRAY(&value), 1, &ubound); + SafeArrayGetLBound(V_ARRAY(&value), 1, &lbound); + + len = (ubound - lbound + 1)*SafeArrayGetElemsize(V_ARRAY(&value)); + + hr = SafeArrayAccessData(V_ARRAY(&value), (void*)&ptr); + if (FAILED(hr)) return hr; + + hr = encode_base64(ptr, len, &encoded); + SafeArrayUnaccessData(V_ARRAY(&value)); + if (FAILED(hr)) return hr; + + hr = node_set_content(&This->node, encoded); + SysFreeString(encoded); + } + else + { + FIXME("unhandled variant type %d for dt:%s\n", V_VT(&value), debugstr_dt(dt)); + return E_NOTIMPL; + } + break; + default: FIXME("not implemented for dt:%s\n", debugstr_dt(dt)); return E_NOTIMPL; } diff --git a/dlls/msxml3/tests/domdoc.c b/dlls/msxml3/tests/domdoc.c index 4bbbe561a6d..7a02e199fd0 100644 --- a/dlls/msxml3/tests/domdoc.c +++ b/dlls/msxml3/tests/domdoc.c @@ -9076,8 +9076,13 @@ static void test_put_nodeTypedValue(void) { IXMLDOMDocument *doc; IXMLDOMElement *elem; - VARIANT type; + VARIANT type, value; + LONG ubound, lbound; + IXMLDOMNode *node; + SAFEARRAY *array; HRESULT hr; + BYTE *ptr; + BSTR str; doc = create_document(&IID_IXMLDOMDocument); if (!doc) return; @@ -9110,6 +9115,93 @@ static void test_put_nodeTypedValue(void) "got %s, expected \"1\"\n", wine_dbgstr_w(V_BSTR(&type))); VariantClear(&type); + hr = IXMLDOMElement_get_firstChild(elem, &node); + EXPECT_HR(hr, S_OK); + hr = IXMLDOMElement_removeChild(elem, node, NULL); + EXPECT_HR(hr, S_OK); + IXMLDOMNode_Release(node); + + hr = IXMLDOMDocument_appendChild(doc, (IXMLDOMNode*)elem, NULL); + EXPECT_HR(hr, S_OK); + + /* bin.base64 */ + hr = IXMLDOMElement_put_dataType(elem, _bstr_("bin.base64")); + EXPECT_HR(hr, S_OK); + + V_VT(&value) = VT_BSTR; + V_BSTR(&value) = _bstr_("ABCD"); + hr = IXMLDOMElement_put_nodeTypedValue(elem, value); + EXPECT_HR(hr, S_OK); + + V_VT(&value) = VT_EMPTY; + hr = IXMLDOMElement_get_nodeTypedValue(elem, &value); + EXPECT_HR(hr, S_OK); + ok(V_VT(&value) == (VT_UI1|VT_ARRAY), "got %d\n", V_VT(&value)); + ok(SafeArrayGetDim(V_ARRAY(&value)) == 1, "got wrong dimension\n"); + ubound = 0; + hr = SafeArrayGetUBound(V_ARRAY(&value), 1, &ubound); + EXPECT_HR(hr, S_OK); + ok(ubound == 2, "got %d\n", ubound); + lbound = 0; + hr = SafeArrayGetLBound(V_ARRAY(&value), 1, &lbound); + EXPECT_HR(hr, S_OK); + ok(lbound == 0, "got %d\n", lbound); + hr = SafeArrayAccessData(V_ARRAY(&value), (void*)&ptr); + EXPECT_HR(hr, S_OK); + ok(ptr[0] == 0, "got %x\n", ptr[0]); + ok(ptr[1] == 0x10, "got %x\n", ptr[1]); + ok(ptr[2] == 0x83, "got %x\n", ptr[2]); + SafeArrayUnaccessData(V_ARRAY(&value)); + VariantClear(&value); + + /* when set as VT_BSTR it's stored as is */ + hr = IXMLDOMElement_get_firstChild(elem, &node); + EXPECT_HR(hr, S_OK); + hr = IXMLDOMNode_get_text(node, &str); + EXPECT_HR(hr, S_OK); + ok(!lstrcmpW(str, _bstr_("ABCD")), "%s\n", wine_dbgstr_w(str)); + IXMLDOMNode_Release(node); + + array = SafeArrayCreateVector(VT_UI1, 0, 7); + hr = SafeArrayAccessData(array, (void*)&ptr); + EXPECT_HR(hr, S_OK); + memcpy(ptr, "dGVzdA=", strlen("dGVzdA=")); + SafeArrayUnaccessData(V_ARRAY(&value)); + + V_VT(&value) = VT_UI1|VT_ARRAY; + V_ARRAY(&value) = array; + hr = IXMLDOMElement_put_nodeTypedValue(elem, value); + EXPECT_HR(hr, S_OK); + + V_VT(&value) = VT_EMPTY; + hr = IXMLDOMElement_get_nodeTypedValue(elem, &value); + EXPECT_HR(hr, S_OK); + ok(V_VT(&value) == (VT_UI1|VT_ARRAY), "got %d\n", V_VT(&value)); + ok(SafeArrayGetDim(V_ARRAY(&value)) == 1, "got wrong dimension\n"); + ubound = 0; + hr = SafeArrayGetUBound(V_ARRAY(&value), 1, &ubound); + EXPECT_HR(hr, S_OK); + ok(ubound == 6, "got %d\n", ubound); + lbound = 0; + hr = SafeArrayGetLBound(V_ARRAY(&value), 1, &lbound); + EXPECT_HR(hr, S_OK); + ok(lbound == 0, "got %d\n", lbound); + hr = SafeArrayAccessData(V_ARRAY(&value), (void*)&ptr); + EXPECT_HR(hr, S_OK); + ok(!memcmp(ptr, "dGVzdA=", strlen("dGVzdA=")), "got wrong data, %s\n", ptr); + SafeArrayUnaccessData(V_ARRAY(&value)); + VariantClear(&value); + + /* if set with VT_UI1|VT_ARRAY it's encoded */ + hr = IXMLDOMElement_get_firstChild(elem, &node); + EXPECT_HR(hr, S_OK); + hr = IXMLDOMNode_get_text(node, &str); + EXPECT_HR(hr, S_OK); + ok(!lstrcmpW(str, _bstr_("ZEdWemRBPQ==")), "%s\n", wine_dbgstr_w(str)); + IXMLDOMNode_Release(node); + + SafeArrayDestroyData(array); + IXMLDOMElement_Release(elem); IXMLDOMDocument_Release(doc); free_bstrs();