>From 0c6b9b60631c47d12d4caacdf6531c3a257898c8 Mon Sep 17 00:00:00 2001
From: Nikolay Sivov <nsivov@codeweavers.com>
Date: Sun, 11 Dec 2011 02:32:51 +0300
Subject: [PATCH 2/9] Escape '<','&','"' and '>' in attribute value

---
 dlls/msxml3/mxwriter.c        |   72 +++++++++++++++++++++++++++++++++++++++-
 dlls/msxml3/tests/saxreader.c |   58 +++++++++++++++++---------------
 2 files changed, 101 insertions(+), 29 deletions(-)

diff --git a/dlls/msxml3/mxwriter.c b/dlls/msxml3/mxwriter.c
index febabe1..5c5aca8 100644
--- a/dlls/msxml3/mxwriter.c
+++ b/dlls/msxml3/mxwriter.c
@@ -57,7 +57,7 @@ typedef enum
     MXWriter_LastProp
 } MXWRITER_PROPS;
 
-typedef struct _mxwriter
+typedef struct
 {
     DispatchEx dispex;
     IMXWriter IMXWriter_iface;
@@ -106,6 +106,71 @@ static HRESULT bstr_from_xmlCharEncoding(xmlCharEncoding enc, BSTR *encoding)
     return *encoding ? S_OK : E_OUTOFMEMORY;
 }
 
+/* escapes special characters like:
+   '<' -> "&lt;"
+   '&' -> "&amp;"
+   '"' -> "&quot;"
+   '>' -> "&gt;"
+*/
+static WCHAR *get_escaped_string(const WCHAR *str, int *len)
+{
+    static const WCHAR ltW[]   = {'&','l','t',';'};
+    static const WCHAR ampW[]  = {'&','a','m','p',';'};
+    static const WCHAR quotW[] = {'&','q','u','o','t',';'};
+    static const WCHAR gtW[]   = {'&','g','t',';'};
+
+    const int default_alloc = 100;
+    const int grow_thresh = 10;
+    int p = *len, conv_len;
+    WCHAR *ptr, *ret;
+
+    /* default buffer size to something if length is unknown */
+    conv_len = *len == -1 ? default_alloc : max(2**len, default_alloc);
+    ptr = ret = heap_alloc(conv_len*sizeof(WCHAR));
+
+    while (*str && p)
+    {
+        if (ptr - ret > conv_len - grow_thresh)
+        {
+            int written = ptr - ret;
+            conv_len *= 2;
+            ptr = ret = heap_realloc(ret, conv_len*sizeof(WCHAR));
+            ptr += written;
+        }
+
+        switch (*str)
+        {
+        case '<':
+            memcpy(ptr, ltW, sizeof(ltW));
+            ptr += sizeof(ltW)/sizeof(WCHAR);
+            break;
+        case '&':
+            memcpy(ptr, ampW, sizeof(ampW));
+            ptr += sizeof(ampW)/sizeof(WCHAR);
+            break;
+        case '"':
+            memcpy(ptr, quotW, sizeof(quotW));
+            ptr += sizeof(quotW)/sizeof(WCHAR);
+            break;
+        case '>':
+            memcpy(ptr, gtW, sizeof(gtW));
+            ptr += sizeof(gtW)/sizeof(WCHAR);
+            break;
+        default:
+            *ptr++ = *str;
+            break;
+        }
+
+        str++;
+        if (*len != -1) p--;
+    }
+
+    if (*len != -1) *len = ptr-ret;
+    *++ptr = 0;
+
+    return ret;
+}
+
 /* creates UTF-8 encoded prolog string with specified or store encoding value */
 static int write_prolog_buffer(const mxwriter *This, xmlCharEncoding enc, xmlOutputBufferPtr buffer)
 {
@@ -824,6 +889,7 @@ static HRESULT WINAPI mxwriter_saxcontent_startElement(
         for (i = 0; i < length; i++)
         {
             const WCHAR *str;
+            WCHAR *escaped;
             INT len = 0;
 
             hr = ISAXAttributes_getQName(attr, i, &str, &len);
@@ -842,8 +908,10 @@ static HRESULT WINAPI mxwriter_saxcontent_startElement(
             hr = ISAXAttributes_getValue(attr, i, &str, &len);
             if (FAILED(hr)) return hr;
 
-            s = xmlchar_from_wcharn(str, len);
+            escaped = get_escaped_string(str, &len);
+            s = xmlchar_from_wcharn(escaped, len);
             write_output_buffer_str(This, (char*)s);
+            heap_free(escaped);
             heap_free(s);
 
             write_output_buffer(This, "\"", 1);
diff --git a/dlls/msxml3/tests/saxreader.c b/dlls/msxml3/tests/saxreader.c
index d713fd3..7b3feea 100644
--- a/dlls/msxml3/tests/saxreader.c
+++ b/dlls/msxml3/tests/saxreader.c
@@ -737,7 +737,7 @@ static ULONG WINAPI isaxattributes_Release(ISAXAttributes* iface)
 
 static HRESULT WINAPI isaxattributes_getLength(ISAXAttributes* iface, int *length)
 {
-    *length = 2;
+    *length = 3;
     return S_OK;
 }
 
@@ -763,17 +763,19 @@ static HRESULT WINAPI isaxattributes_getLocalName(
 
 static HRESULT WINAPI isaxattributes_getQName(
     ISAXAttributes* iface,
-    int nIndex,
-    const WCHAR **pQName,
-    int *pQNameLength)
+    int index,
+    const WCHAR **QName,
+    int *QNameLength)
 {
-    static const WCHAR attr1W[] = {'a',':','a','t','t','r','1','j','u','n','k',0};
-    static const WCHAR attr2W[] = {'a','t','t','r','2','j','u','n','k',0};
+    static const WCHAR attrqnamesW[][15] = {{'a',':','a','t','t','r','1','j','u','n','k',0},
+                                            {'a','t','t','r','2','j','u','n','k',0},
+                                            {'a','t','t','r','3',0}};
+    static const int attrqnamelen[] = {7, 5, 5};
 
-    ok(nIndex == 0 || nIndex == 1, "invalid index received %d\n", nIndex);
+    ok(index >= 0 && index <= 2, "invalid index received %d\n", index);
 
-    *pQName = (nIndex == 0) ? attr1W : attr2W;
-    *pQNameLength = (nIndex == 0) ? 7 : 5;
+    *QName = attrqnamesW[index];
+    *QNameLength = attrqnamelen[index];
 
     return S_OK;
 }
@@ -848,19 +850,18 @@ static HRESULT WINAPI isaxattributes_getTypeFromQName(
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI isaxattributes_getValue(
-    ISAXAttributes* iface,
-    int nIndex,
-    const WCHAR ** pValue,
-    int * nValue)
+static HRESULT WINAPI isaxattributes_getValue(ISAXAttributes* iface, int index,
+    const WCHAR **value, int *nValue)
 {
-    static const WCHAR attrval1W[] = {'a','1','j','u','n','k',0};
-    static const WCHAR attrval2W[] = {'a','2','j','u','n','k',0};
+    static const WCHAR attrvaluesW[][10] = {{'a','1','j','u','n','k',0},
+                                            {'a','2','j','u','n','k',0},
+                                            {'<','&','"','>',0}};
+    static const int attrvalueslen[] = {2, 2, 4};
 
-    ok(nIndex == 0 || nIndex == 1, "invalid index received %d\n", nIndex);
+    ok(index >= 0 && index <= 2, "invalid index received %d\n", index);
 
-    *pValue = (nIndex == 0) ? attrval1W : attrval2W;
-    *nValue = 2;
+    *value = attrvaluesW[index];
+    *nValue = attrvalueslen[index];
 
     return S_OK;
 }
@@ -2039,6 +2040,9 @@ struct writer_startendelement_t {
     ISAXAttributes *attr;
 };
 
+static const char startelement_xml[] = "<uri:local a:attr1=\"a1\" attr2=\"a2\" attr3=\"&lt;&amp;&quot;&gt;\">";
+static const char startendelement_xml[] = "<uri:local a:attr1=\"a1\" attr2=\"a2\" attr3=\"&lt;&amp;&quot;&gt;\"/>";
+
 static const struct writer_startendelement_t writer_startendelement[] = {
     /* 0 */
     { &CLSID_MXXMLWriter,   StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
@@ -2120,17 +2124,17 @@ static const struct writer_startendelement_t writer_startendelement[] = {
     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
 
     /* with attributes */
-    { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\">", S_OK, &saxattributes },
+    { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
     /* 65 */
-    { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\">", S_OK, &saxattributes },
-    { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\">", S_OK, &saxattributes },
-    { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\">", S_OK, &saxattributes },
+    { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
+    { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
+    { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
     /* empty elements */
-    { &CLSID_MXXMLWriter,   StartEndElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\"/>", S_OK, &saxattributes },
-    { &CLSID_MXXMLWriter30, StartEndElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\"/>", S_OK, &saxattributes },
+    { &CLSID_MXXMLWriter,   StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
+    { &CLSID_MXXMLWriter30, StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
     /* 70 */
-    { &CLSID_MXXMLWriter40, StartEndElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\"/>", S_OK, &saxattributes },
-    { &CLSID_MXXMLWriter60, StartEndElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\"/>", S_OK, &saxattributes },
+    { &CLSID_MXXMLWriter40, StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
+    { &CLSID_MXXMLWriter60, StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
     { &CLSID_MXXMLWriter,   StartEndElement, "", "", "", "</>", S_OK },
     { &CLSID_MXXMLWriter30, StartEndElement, "", "", "", "</>", S_OK },
     { &CLSID_MXXMLWriter40, StartEndElement, "", "", "", "</>", S_OK },
-- 
1.5.6.5


