>From d05b273dc26c6775b73d05e7770d6059c9c3afdf Mon Sep 17 00:00:00 2001
From: Nikolay Sivov <nsivov@codeweavers.com>
Date: Fri, 24 Feb 2012 23:20:48 +0300
Subject: [PATCH 1/1] Properly escape character data in text nodes

---
 dlls/msxml3/mxwriter.c        |   41 ++++++++++++++++++++----
 dlls/msxml3/tests/saxreader.c |   70 ++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 103 insertions(+), 8 deletions(-)

diff --git a/dlls/msxml3/mxwriter.c b/dlls/msxml3/mxwriter.c
index cb1a67f..72db31d 100644
--- a/dlls/msxml3/mxwriter.c
+++ b/dlls/msxml3/mxwriter.c
@@ -68,6 +68,12 @@ typedef enum
     MXWriter_LastProp
 } mxwriter_prop;
 
+typedef enum
+{
+    EscapeValue,
+    EscapeText
+} escape_mode;
+
 typedef struct
 {
     char *data;
@@ -94,6 +100,7 @@ typedef struct
 
     VARIANT_BOOL props[MXWriter_LastProp];
     BOOL prop_changed;
+    BOOL cdata;
 
     BSTR version;
 
@@ -275,7 +282,7 @@ static void close_output_buffer(mxwriter *This)
    '"' -> "&quot;"
    '>' -> "&gt;"
 */
-static WCHAR *get_escaped_string(const WCHAR *str, int *len)
+static WCHAR *get_escaped_string(const WCHAR *str, escape_mode mode, int *len)
 {
     static const WCHAR ltW[]    = {'&','l','t',';'};
     static const WCHAR ampW[]   = {'&','a','m','p',';'};
@@ -311,14 +318,18 @@ static WCHAR *get_escaped_string(const WCHAR *str, int *len)
             memcpy(ptr, ampW, sizeof(ampW));
             ptr += sizeof(ampW)/sizeof(WCHAR);
             break;
-        case '"':
-            memcpy(ptr, equotW, sizeof(equotW));
-            ptr += sizeof(equotW)/sizeof(WCHAR);
-            break;
         case '>':
             memcpy(ptr, gtW, sizeof(gtW));
             ptr += sizeof(gtW)/sizeof(WCHAR);
             break;
+        case '"':
+            if (mode == EscapeValue)
+            {
+                memcpy(ptr, equotW, sizeof(equotW));
+                ptr += sizeof(equotW)/sizeof(WCHAR);
+                break;
+            }
+            /* fallthrough for text mode */
         default:
             *ptr++ = *str;
             break;
@@ -429,6 +440,7 @@ static inline HRESULT flush_output_buffer(mxwriter *This)
 {
     close_element_starttag(This);
     set_element_name(This, NULL, 0);
+    This->cdata = FALSE;
     return write_data_to_stream(This);
 }
 
@@ -978,7 +990,7 @@ static HRESULT WINAPI SAXContentHandler_startElement(
             hr = ISAXAttributes_getValue(attr, i, &str, &len);
             if (FAILED(hr)) return hr;
 
-            escaped = get_escaped_string(str, &len);
+            escaped = get_escaped_string(str, EscapeValue, &len);
             write_output_buffer_quoted(This->buffer, escaped, len);
             heap_free(escaped);
         }
@@ -1040,7 +1052,19 @@ static HRESULT WINAPI SAXContentHandler_characters(
     set_element_name(This, NULL, 0);
 
     if (nchars)
-        write_output_buffer(This->buffer, chars, nchars);
+    {
+        if (This->cdata)
+            write_output_buffer(This->buffer, chars, nchars);
+        else
+        {
+            int len = nchars;
+            WCHAR *escaped;
+
+            escaped = get_escaped_string(chars, EscapeText, &len);
+            write_output_buffer(This->buffer, escaped, len);
+            heap_free(escaped);
+        }
+    }
 
     return S_OK;
 }
@@ -1203,6 +1227,7 @@ static HRESULT WINAPI SAXLexicalHandler_startCDATA(ISAXLexicalHandler *iface)
     TRACE("(%p)\n", This);
 
     write_output_buffer(This->buffer, scdataW, sizeof(scdataW)/sizeof(WCHAR));
+    This->cdata = TRUE;
 
     return S_OK;
 }
@@ -1215,6 +1240,7 @@ static HRESULT WINAPI SAXLexicalHandler_endCDATA(ISAXLexicalHandler *iface)
     TRACE("(%p)\n", This);
 
     write_output_buffer(This->buffer, ecdataW, sizeof(ecdataW)/sizeof(WCHAR));
+    This->cdata = FALSE;
 
     return S_OK;
 }
@@ -1296,6 +1322,7 @@ HRESULT MXWriter_create(MSXML_VERSION version, IUnknown *outer, void **ppObj)
     This->xml_enc  = XmlEncoding_UTF16;
 
     This->element = NULL;
+    This->cdata = FALSE;
 
     This->dest = NULL;
     This->dest_written = 0;
diff --git a/dlls/msxml3/tests/saxreader.c b/dlls/msxml3/tests/saxreader.c
index cfead73..74afc67 100644
--- a/dlls/msxml3/tests/saxreader.c
+++ b/dlls/msxml3/tests/saxreader.c
@@ -2439,13 +2439,29 @@ static void test_mxwriter_startendelement(void)
     free_bstrs();
 }
 
+struct writer_characters_t {
+    const GUID *clsid;
+    const char *data;
+    const char *output;
+};
+
+static const struct writer_characters_t writer_characters[] = {
+    { &CLSID_MXXMLWriter,   "< > & \"", "&lt; &gt; &amp; \"" },
+    { &CLSID_MXXMLWriter30, "< > & \"", "&lt; &gt; &amp; \"" },
+    { &CLSID_MXXMLWriter40, "< > & \"", "&lt; &gt; &amp; \"" },
+    { &CLSID_MXXMLWriter60, "< > & \"", "&lt; &gt; &amp; \"" },
+    { NULL }
+};
+
 static void test_mxwriter_characters(void)
 {
     static const WCHAR chardataW[] = {'T','E','S','T','C','H','A','R','D','A','T','A',' ','.',0};
+    const struct writer_characters_t *table = writer_characters;
     ISAXContentHandler *content;
     IMXWriter *writer;
     VARIANT dest;
     HRESULT hr;
+    int i = 0;
 
     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
             &IID_IMXWriter, (void**)&writer);
@@ -2515,6 +2531,54 @@ static void test_mxwriter_characters(void)
     ISAXContentHandler_Release(content);
     IMXWriter_Release(writer);
 
+    /* batch tests */
+    while (table->clsid)
+    {
+        ISAXContentHandler *content;
+        IMXWriter *writer;
+        HRESULT hr;
+
+        if (!is_clsid_supported(table->clsid, mxwriter_support_data))
+        {
+            table++;
+            i++;
+            continue;
+        }
+
+        hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
+            &IID_IMXWriter, (void**)&writer);
+        EXPECT_HR(hr, S_OK);
+
+        hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
+        EXPECT_HR(hr, S_OK);
+
+        hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
+        EXPECT_HR(hr, S_OK);
+
+        hr = ISAXContentHandler_startDocument(content);
+        EXPECT_HR(hr, S_OK);
+
+        hr = ISAXContentHandler_characters(content, _bstr_(table->data), strlen(table->data));
+        EXPECT_HR(hr, S_OK);
+
+        /* test output */
+        if (hr == S_OK)
+        {
+            VARIANT dest;
+
+            V_VT(&dest) = VT_EMPTY;
+            hr = IMXWriter_get_output(writer, &dest);
+            EXPECT_HR(hr, S_OK);
+            ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
+            ok(!lstrcmpW(_bstr_(table->output), V_BSTR(&dest)),
+                "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->output);
+            VariantClear(&dest);
+        }
+
+        table++;
+        i++;
+    }
+
     free_bstrs();
 }
 
@@ -2975,6 +3039,10 @@ static void test_mxwriter_cdata(void)
     hr = ISAXLexicalHandler_startCDATA(lexical);
     EXPECT_HR(hr, S_OK);
 
+    /* all these are escaped for text nodes */
+    hr = ISAXContentHandler_characters(content, _bstr_("< > & \""), 7);
+    EXPECT_HR(hr, S_OK);
+
     hr = ISAXLexicalHandler_endCDATA(lexical);
     EXPECT_HR(hr, S_OK);
 
@@ -2982,7 +3050,7 @@ static void test_mxwriter_cdata(void)
     hr = IMXWriter_get_output(writer, &dest);
     EXPECT_HR(hr, S_OK);
     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(_bstr_("<![CDATA[<![CDATA[]]>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
+    ok(!lstrcmpW(_bstr_("<![CDATA[<![CDATA[< > & \"]]>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
     VariantClear(&dest);
 
     ISAXContentHandler_Release(content);
-- 
1.5.6.5

