From: Hans Leidekker Subject: [1/8] webservices: Escape XML special characters where needed. Message-Id: <1475059086-6011-1-git-send-email-hans@codeweavers.com> Date: Wed, 28 Sep 2016 12:37:59 +0200 Signed-off-by: Hans Leidekker --- dlls/webservices/tests/writer.c | 175 ++++++++++++++++++++++++++++++++++------ dlls/webservices/writer.c | 67 +++++++++++++-- 2 files changed, 213 insertions(+), 29 deletions(-) diff --git a/dlls/webservices/tests/writer.c b/dlls/webservices/tests/writer.c index 80b9f43..326aed2 100644 --- a/dlls/webservices/tests/writer.c +++ b/dlls/webservices/tests/writer.c @@ -441,7 +441,7 @@ static void test_WsWriteStartAttribute(void) HRESULT hr; WS_XML_WRITER *writer; WS_XML_STRING prefix = {1, (BYTE *)"p"}, localname = {3, (BYTE *)"str"}, ns = {2, (BYTE *)"ns"}; - WS_XML_UTF8_TEXT text; + WS_XML_UTF8_TEXT text = {{WS_XML_TEXT_TYPE_UTF8}}; hr = WsCreateWriter( NULL, 0, &writer, NULL ); ok( hr == S_OK, "got %08x\n", hr ); @@ -462,10 +462,8 @@ static void test_WsWriteStartAttribute(void) ok( hr == S_OK, "got %08x\n", hr ); check_output( writer, "", __LINE__ ); - text.text.textType = WS_XML_TEXT_TYPE_UTF8; text.value.length = 1; text.value.bytes = (BYTE *)"0"; - text.value.dictionary = NULL; hr = WsWriteText( writer, &text.text, NULL ); ok( hr == S_OK, "got %08x\n", hr ); check_output( writer, "", __LINE__ ); @@ -1103,7 +1101,7 @@ static void test_WsWriteStartCData(void) HRESULT hr; WS_XML_WRITER *writer; WS_XML_STRING localname = {1, (BYTE *)"t"}, ns = {0, NULL}; - WS_XML_UTF8_TEXT text; + WS_XML_UTF8_TEXT text = {{WS_XML_TEXT_TYPE_UTF8}}; hr = WsCreateWriter( NULL, 0, &writer, NULL ); ok( hr == S_OK, "got %08x\n", hr ); @@ -1128,10 +1126,8 @@ static void test_WsWriteStartCData(void) ok( hr == S_OK, "got %08x\n", hr ); check_output( writer, ""; text.value.length = 6; - text.value.dictionary = NULL; hr = WsWriteText( writer, &text.text, NULL ); ok( hr == S_OK, "got %08x\n", hr ); check_output( writer, "", __LINE__ ); @@ -1790,12 +1786,12 @@ static void test_WsWriteNode(void) WS_XML_STRING localname = {1, (BYTE *)"t"}, localname2 = {4, (BYTE *)"attr"}, ns = {0, NULL}; WS_XML_WRITER *writer; WS_XML_BUFFER *buffer; - WS_XML_UTF8_TEXT utf8; + WS_XML_UTF8_TEXT utf8 = {{WS_XML_TEXT_TYPE_UTF8}}; WS_XML_ATTRIBUTE attr, *attrs[1]; WS_XML_ELEMENT_NODE elem; - WS_XML_COMMENT_NODE comment; + WS_XML_COMMENT_NODE comment = {{WS_XML_NODE_TYPE_COMMENT}}; WS_XML_NODE node; - WS_XML_TEXT_NODE text; + WS_XML_TEXT_NODE text = {{WS_XML_NODE_TYPE_TEXT}}; WS_HEAP *heap; HRESULT hr; @@ -1817,10 +1813,8 @@ static void test_WsWriteNode(void) hr = WsSetOutputToBuffer( writer, buffer, NULL, 0, NULL ); ok( hr == S_OK, "got %08x\n", hr ); - utf8.text.textType = WS_XML_TEXT_TYPE_UTF8; utf8.value.bytes = (BYTE *)"value"; utf8.value.length = sizeof("value") - 1; - utf8.value.dictionary = NULL; attr.singleQuote = TRUE; attr.isXmlNs = FALSE; @@ -1837,14 +1831,12 @@ static void test_WsWriteNode(void) elem.attributeCount = 1; elem.attributes = attrs; elem.isEmpty = FALSE; - hr = WsWriteNode( writer, (const WS_XML_NODE *)&elem, NULL ); + hr = WsWriteNode( writer, &elem.node, NULL ); ok( hr == S_OK, "got %08x\n", hr ); - comment.node.nodeType = WS_XML_NODE_TYPE_COMMENT; comment.value.bytes = (BYTE *)"comment"; comment.value.length = sizeof("comment") - 1; - comment.value.dictionary = NULL; - hr = WsWriteNode( writer, (const WS_XML_NODE *)&comment, NULL ); + hr = WsWriteNode( writer, &comment.node, NULL ); ok( hr == S_OK, "got %08x\n", hr ); node.nodeType = WS_XML_NODE_TYPE_EOF; @@ -1861,10 +1853,8 @@ static void test_WsWriteNode(void) utf8.value.bytes = (BYTE *)"cdata"; utf8.value.length = sizeof("cdata") - 1; - utf8.value.dictionary = NULL; - text.node.nodeType = WS_XML_NODE_TYPE_TEXT; text.text = &utf8.text; - hr = WsWriteNode( writer, (const WS_XML_NODE *)&text, NULL ); + hr = WsWriteNode( writer, &text.node, NULL ); ok( hr == S_OK, "got %08x\n", hr ); node.nodeType = WS_XML_NODE_TYPE_END_CDATA; @@ -1873,8 +1863,7 @@ static void test_WsWriteNode(void) utf8.value.bytes = (BYTE *)"text"; utf8.value.length = sizeof("text") - 1; - utf8.value.dictionary = NULL; - hr = WsWriteNode( writer, (const WS_XML_NODE *)&text, NULL ); + hr = WsWriteNode( writer, &text.node, NULL ); ok( hr == S_OK, "got %08x\n", hr ); node.nodeType = WS_XML_NODE_TYPE_END_ELEMENT; @@ -2205,7 +2194,7 @@ static void test_WsWriteText(void) { HRESULT hr; WS_XML_WRITER *writer; - WS_XML_UTF8_TEXT utf8; + WS_XML_UTF8_TEXT utf8 = {{WS_XML_TEXT_TYPE_UTF8}}; hr = WsCreateWriter( NULL, 0, &writer, NULL ); ok( hr == S_OK, "got %08x\n", hr ); @@ -2213,12 +2202,10 @@ static void test_WsWriteText(void) hr = set_output( writer ); ok( hr == S_OK, "got %08x\n", hr ); - utf8.text.textType = WS_XML_TEXT_TYPE_UTF8; utf8.value.bytes = (BYTE *)"test"; utf8.value.length = 4; - utf8.value.dictionary = NULL; hr = WsWriteText( writer, &utf8.text, NULL ); - todo_wine ok( hr == WS_E_INVALID_FORMAT, "got %08x\n", hr ); + ok( hr == WS_E_INVALID_FORMAT, "got %08x\n", hr ); WsFreeWriter( writer ); } @@ -2309,6 +2296,145 @@ static void test_WsWriteArray(void) WsFreeWriter( writer ); } +static void test_escapes(void) +{ + WS_XML_STRING localname = {1, (BYTE *)"t"}, ns = {0, NULL}; + WS_XML_UTF8_TEXT utf8 = {{WS_XML_TEXT_TYPE_UTF8}}; + WS_XML_WRITER *writer; + struct test + { + const char *text; + const char *result; + BOOL single; + }; + static const struct test tests_elem[] = + { + { "<", "<" }, + { ">", ">" }, + { "\"", "\"" }, + { "&", "&" }, + { "&&", "&&" }, + { "'", "'" }, + }; + static const struct test tests_attr[] = + { + { "<", "" }, + { ">", "\"/>" }, + { "\"", "" }, + { "&", "" }, + { "'", "" }, + { "\"", "", TRUE }, + { "'", "", TRUE }, + }; + static const struct test tests_cdata[] = + { + { "<", "" }, + { ">", "]]>" }, + { "\"", "" }, + { "&", "" }, + { "[", "" }, + { "]", "" }, + { "'", "" }, + }; + static const struct test tests_comment[] = + { + { "<", "" }, + { ">", "-->" }, + { "\"", "" }, + { "&", "" }, + { "'", "" }, + }; + HRESULT hr; + ULONG i; + + hr = WsCreateWriter( NULL, 0, &writer, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + for (i = 0; i < sizeof(tests_elem)/sizeof(tests_elem[0]); i++) + { + hr = set_output( writer ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + hr = WsWriteStartElement( writer, NULL, &localname, &ns, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + + utf8.value.bytes = (BYTE *)tests_elem[i].text; + utf8.value.length = strlen( tests_elem[i].text ); + hr = WsWriteText( writer, &utf8.text, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + + hr = WsWriteEndElement( writer, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + check_output( writer, tests_elem[i].result, __LINE__ ); + } + + for (i = 0; i < sizeof(tests_attr)/sizeof(tests_attr[0]); i++) + { + hr = set_output( writer ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + hr = WsWriteStartElement( writer, NULL, &localname, &ns, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + + hr = WsWriteStartAttribute( writer, NULL, &localname, &ns, tests_attr[i].single, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + + utf8.value.bytes = (BYTE *)tests_attr[i].text; + utf8.value.length = strlen( tests_attr[i].text ); + hr = WsWriteText( writer, &utf8.text, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + + hr = WsWriteEndAttribute( writer, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + + hr = WsWriteEndElement( writer, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + check_output( writer, tests_attr[i].result, __LINE__ ); + } + + for (i = 0; i < sizeof(tests_cdata)/sizeof(tests_cdata[0]); i++) + { + hr = set_output( writer ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + hr = WsWriteStartElement( writer, NULL, &localname, &ns, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + + hr = WsWriteStartCData( writer, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + + utf8.value.bytes = (BYTE *)tests_cdata[i].text; + utf8.value.length = strlen( tests_cdata[i].text ); + hr = WsWriteText( writer, &utf8.text, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + + hr = WsWriteEndCData( writer, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + + hr = WsWriteEndElement( writer, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + check_output( writer, tests_cdata[i].result, __LINE__ ); + } + + for (i = 0; i < sizeof(tests_comment)/sizeof(tests_comment[0]); i++) + { + WS_XML_COMMENT_NODE comment = {{WS_XML_NODE_TYPE_COMMENT}}; + + hr = set_output( writer ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + hr = WsWriteStartElement( writer, NULL, &localname, &ns, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + + comment.value.bytes = (BYTE *)tests_comment[i].text; + comment.value.length = strlen( tests_comment[i].text ); + hr = WsWriteNode( writer, &comment.node, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + + hr = WsWriteEndElement( writer, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + check_output( writer, tests_comment[i].result, __LINE__ ); + } + + WsFreeWriter( writer ); +} + START_TEST(writer) { test_WsCreateWriter(); @@ -2338,4 +2464,5 @@ START_TEST(writer) test_field_flags(); test_WsWriteText(); test_WsWriteArray(); + test_escapes(); } diff --git a/dlls/webservices/writer.c b/dlls/webservices/writer.c index 3665613..2f74f75 100644 --- a/dlls/webservices/writer.c +++ b/dlls/webservices/writer.c @@ -425,6 +425,45 @@ static inline void write_bytes( struct writer *writer, const BYTE *bytes, ULONG writer->write_pos += len; } +struct escape +{ + char ch; + const char *entity; + ULONG len; +}; +static const struct escape escape_lt = { '<', "<", 4 }; +static const struct escape escape_gt = { '>', ">", 4 }; +static const struct escape escape_amp = { '&', "&", 5 }; +static const struct escape escape_apos = { '\'', "'", 6 }; +static const struct escape escape_quot = { '"', """, 6 }; + +static HRESULT write_bytes_escape( struct writer *writer, const BYTE *bytes, ULONG len, + const struct escape **escapes, ULONG nb_escapes ) +{ + ULONG i, j, size; + const BYTE *ptr; + HRESULT hr; + + for (i = 0; i < len; i++) + { + ptr = &bytes[i]; + size = 1; + for (j = 0; j < nb_escapes; j++) + { + if (bytes[i] == escapes[j]->ch) + { + ptr = (const BYTE *)escapes[j]->entity; + size = escapes[j]->len; + break; + } + } + if ((hr = write_grow_buffer( writer, size )) != S_OK) return hr; + write_bytes( writer, ptr, size ); + } + + return S_OK; +} + static HRESULT write_attribute( struct writer *writer, WS_XML_ATTRIBUTE *attr ) { WS_XML_UTF8_TEXT *text = (WS_XML_UTF8_TEXT *)attr->value; @@ -452,10 +491,17 @@ static HRESULT write_attribute( struct writer *writer, WS_XML_ATTRIBUTE *attr ) write_bytes( writer, attr->localName->bytes, attr->localName->length ); write_char( writer, '=' ); write_char( writer, quote ); - if (text) write_bytes( writer, text->value.bytes, text->value.length ); + if (text) + { + const struct escape *escapes[3]; + escapes[0] = attr->singleQuote ? &escape_apos : &escape_quot; + escapes[1] = &escape_lt; + escapes[2] = &escape_amp; + hr = write_bytes_escape( writer, text->value.bytes, text->value.length, escapes, 3 ); + } write_char( writer, quote ); - return S_OK; + return hr; } static inline BOOL is_current_namespace( struct writer *writer, const WS_XML_STRING *ns ) @@ -1285,9 +1331,20 @@ static HRESULT write_text( struct writer *writer ) const WS_XML_UTF8_TEXT *utf8 = (const WS_XML_UTF8_TEXT *)text->text; HRESULT hr; - if ((hr = write_grow_buffer( writer, utf8->value.length )) != S_OK) return hr; - write_bytes( writer, utf8->value.bytes, utf8->value.length ); - return S_OK; + if (!writer->current->parent) return WS_E_INVALID_FORMAT; + if (node_type( writer->current->parent ) == WS_XML_NODE_TYPE_ELEMENT) + { + const struct escape *escapes[3] = { &escape_lt, &escape_gt, &escape_amp }; + return write_bytes_escape( writer, utf8->value.bytes, utf8->value.length, escapes, 3 ); + } + else if (node_type( writer->current->parent ) == WS_XML_NODE_TYPE_CDATA) + { + if ((hr = write_grow_buffer( writer, utf8->value.length )) != S_OK) return hr; + write_bytes( writer, utf8->value.bytes, utf8->value.length ); + return S_OK; + } + + return WS_E_INVALID_FORMAT; } static HRESULT write_text_node( struct writer *writer, const WS_XML_TEXT *text ) -- 2.1.4