>From c2520b47d19a13e7ea04858d7555057e631fbbaa Mon Sep 17 00:00:00 2001
From: Nikolay Sivov <nsivov@codeweavers.com>
Date: Sun, 18 Dec 2011 21:00:24 +0300
Subject: [PATCH 2/6] Implement response headers access methods

---
 dlls/msxml3/httprequest.c  |  137 +++++++++++++++++++++++++++++++++++++++----
 dlls/msxml3/tests/domdoc.c |   25 ++++++++-
 2 files changed, 148 insertions(+), 14 deletions(-)

diff --git a/dlls/msxml3/httprequest.c b/dlls/msxml3/httprequest.c
index cf4afe4..26241dd 100644
--- a/dlls/msxml3/httprequest.c
+++ b/dlls/msxml3/httprequest.c
@@ -56,7 +56,7 @@ static const WCHAR crlfW[] = {'\r','\n',0};
 
 typedef struct BindStatusCallback BindStatusCallback;
 
-struct reqheader
+struct httpheader
 {
     struct list entry;
     BSTR header;
@@ -80,6 +80,9 @@ typedef struct
     struct list reqheaders;
     /* cached resulting custom request headers string length in WCHARs */
     LONG reqheader_size;
+    /* response headers */
+    struct list respheaders;
+    BSTR raw_respheaders;
 
     /* credentials */
     BSTR user;
@@ -126,6 +129,22 @@ static void httprequest_setreadystate(httprequest *This, READYSTATE state)
     }
 }
 
+static void free_response_headers(httprequest *This)
+{
+    struct httpheader *header, *header2;
+
+    LIST_FOR_EACH_ENTRY_SAFE(header, header2, &This->respheaders, struct httpheader, entry)
+    {
+        list_remove(&header->entry);
+        SysFreeString(header->header);
+        SysFreeString(header->value);
+        heap_free(header);
+    }
+
+    SysFreeString(This->raw_respheaders);
+    This->raw_respheaders = NULL;
+}
+
 struct BindStatusCallback
 {
     IBindStatusCallback IBindStatusCallback_iface;
@@ -386,7 +405,7 @@ static HRESULT WINAPI BSCHttpNegotiate_BeginningTransaction(IHttpNegotiate *ifac
         LPCWSTR url, LPCWSTR headers, DWORD reserved, LPWSTR *add_headers)
 {
     BindStatusCallback *This = impl_from_IHttpNegotiate(iface);
-    const struct reqheader *entry;
+    const struct httpheader *entry;
     WCHAR *buff, *ptr;
 
     TRACE("(%p)->(%s %s %d %p)\n", This, debugstr_w(url), debugstr_w(headers), reserved, add_headers);
@@ -399,7 +418,7 @@ static HRESULT WINAPI BSCHttpNegotiate_BeginningTransaction(IHttpNegotiate *ifac
     if (!buff) return E_OUTOFMEMORY;
 
     ptr = buff;
-    LIST_FOR_EACH_ENTRY(entry, &This->request->reqheaders, struct reqheader, entry)
+    LIST_FOR_EACH_ENTRY(entry, &This->request->reqheaders, struct httpheader, entry)
     {
         lstrcpyW(ptr, entry->header);
         ptr += SysStringLen(entry->header);
@@ -419,6 +438,37 @@ static HRESULT WINAPI BSCHttpNegotiate_BeginningTransaction(IHttpNegotiate *ifac
     return S_OK;
 }
 
+static void add_response_header(httprequest *This, const WCHAR *data, int len)
+{
+    struct httpheader *entry;
+    const WCHAR *ptr = data;
+    BSTR header, value;
+
+    while (*ptr)
+    {
+        if (*ptr == ':')
+        {
+            header = SysAllocStringLen(data, ptr-data);
+            /* skip leading spaces for a value */
+            while (*++ptr == ' ')
+                ;
+            value = SysAllocStringLen(ptr, len-(ptr-data));
+            break;
+        }
+        ptr++;
+    }
+
+    if (!*ptr) return;
+
+    /* new header */
+    TRACE("got header %s:%s\n", debugstr_w(header), debugstr_w(value));
+
+    entry = heap_alloc(sizeof(*header));
+    entry->header = header;
+    entry->value  = value;
+    list_add_head(&This->respheaders, &entry->entry);
+}
+
 static HRESULT WINAPI BSCHttpNegotiate_OnResponse(IHttpNegotiate *iface, DWORD code,
         LPCWSTR resp_headers, LPCWSTR req_headers, LPWSTR *add_reqheaders)
 {
@@ -428,6 +478,28 @@ static HRESULT WINAPI BSCHttpNegotiate_OnResponse(IHttpNegotiate *iface, DWORD c
           debugstr_w(req_headers), add_reqheaders);
 
     This->request->status = code;
+    /* store headers */
+    free_response_headers(This->request);
+    if (resp_headers)
+    {
+        const WCHAR *ptr, *line;
+
+        ptr = line = resp_headers;
+
+        /* skip status line */
+        while (*ptr)
+        {
+            if (*ptr == '\r' && *(ptr+1) == '\n')
+            {
+                line = ++ptr+1;
+                break;
+            }
+            ptr++;
+        }
+
+        /* store as unparsed string for now */
+        This->request->raw_respheaders = SysAllocString(line);
+    }
 
     return S_OK;
 }
@@ -563,7 +635,7 @@ static ULONG WINAPI httprequest_Release(IXMLHTTPRequest *iface)
 
     if ( ref == 0 )
     {
-        struct reqheader *header, *header2;
+        struct httpheader *header, *header2;
 
         if (This->site)
             IUnknown_Release( This->site );
@@ -573,13 +645,15 @@ static ULONG WINAPI httprequest_Release(IXMLHTTPRequest *iface)
         SysFreeString(This->password);
 
         /* request headers */
-        LIST_FOR_EACH_ENTRY_SAFE(header, header2, &This->reqheaders, struct reqheader, entry)
+        LIST_FOR_EACH_ENTRY_SAFE(header, header2, &This->reqheaders, struct httpheader, entry)
         {
             list_remove(&header->entry);
             SysFreeString(header->header);
             SysFreeString(header->value);
             heap_free(header);
         }
+        /* response headers */
+        free_response_headers(This);
 
         /* detach callback object */
         BindStatusCallback_Detach(This->bsc);
@@ -719,7 +793,7 @@ static HRESULT WINAPI httprequest_open(IXMLHTTPRequest *iface, BSTR method, BSTR
 static HRESULT WINAPI httprequest_setRequestHeader(IXMLHTTPRequest *iface, BSTR header, BSTR value)
 {
     httprequest *This = impl_from_IXMLHTTPRequest( iface );
-    struct reqheader *entry;
+    struct httpheader *entry;
 
     TRACE("(%p)->(%s %s)\n", This, debugstr_w(header), debugstr_w(value));
 
@@ -728,7 +802,7 @@ static HRESULT WINAPI httprequest_setRequestHeader(IXMLHTTPRequest *iface, BSTR
     if (!value) return E_INVALIDARG;
 
     /* replace existing header value if already added */
-    LIST_FOR_EACH_ENTRY(entry, &This->reqheaders, struct reqheader, entry)
+    LIST_FOR_EACH_ENTRY(entry, &This->reqheaders, struct httpheader, entry)
     {
         if (lstrcmpW(entry->header, header) == 0)
         {
@@ -760,22 +834,56 @@ static HRESULT WINAPI httprequest_setRequestHeader(IXMLHTTPRequest *iface, BSTR
     return S_OK;
 }
 
-static HRESULT WINAPI httprequest_getResponseHeader(IXMLHTTPRequest *iface, BSTR bstrHeader, BSTR *pbstrValue)
+static HRESULT WINAPI httprequest_getResponseHeader(IXMLHTTPRequest *iface, BSTR header, BSTR *value)
 {
     httprequest *This = impl_from_IXMLHTTPRequest( iface );
+    struct httpheader *entry;
 
-    FIXME("stub (%p) %s %p\n", This, debugstr_w(bstrHeader), pbstrValue);
+    TRACE("(%p)->(%s %p)\n", This, debugstr_w(header), value);
 
-    return E_NOTIMPL;
+    if (!header || !value) return E_INVALIDARG;
+
+    if (This->raw_respheaders && list_empty(&This->respheaders))
+    {
+        WCHAR *ptr, *line;
+
+        ptr = line = This->raw_respheaders;
+        while (*ptr)
+        {
+            if (*ptr == '\r' && *(ptr+1) == '\n')
+            {
+                add_response_header(This, line, ptr-line);
+                ptr++; line = ++ptr;
+                continue;
+            }
+            ptr++;
+        }
+    }
+
+    LIST_FOR_EACH_ENTRY(entry, &This->respheaders, struct httpheader, entry)
+    {
+        if (!strcmpiW(entry->header, header))
+        {
+            *value = SysAllocString(entry->value);
+            TRACE("header value %s\n", debugstr_w(*value));
+            return S_OK;
+        }
+    }
+
+    return S_FALSE;
 }
 
-static HRESULT WINAPI httprequest_getAllResponseHeaders(IXMLHTTPRequest *iface, BSTR *pbstrHeaders)
+static HRESULT WINAPI httprequest_getAllResponseHeaders(IXMLHTTPRequest *iface, BSTR *respheaders)
 {
     httprequest *This = impl_from_IXMLHTTPRequest( iface );
 
-    FIXME("stub (%p) %p\n", This, pbstrHeaders);
+    TRACE("(%p)->(%p)\n", This, respheaders);
 
-    return E_NOTIMPL;
+    if (!respheaders) return E_INVALIDARG;
+
+    *respheaders = SysAllocString(This->raw_respheaders);
+
+    return S_OK;
 }
 
 static HRESULT WINAPI httprequest_send(IXMLHTTPRequest *iface, VARIANT body)
@@ -1189,6 +1297,9 @@ HRESULT XMLHTTPRequest_create(IUnknown *pUnkOuter, void **ppObj)
     req->status = 0;
     req->reqheader_size = 0;
     list_init(&req->reqheaders);
+    list_init(&req->respheaders);
+    req->raw_respheaders = NULL;
+
     req->site = NULL;
     req->safeopt = 0;
 
diff --git a/dlls/msxml3/tests/domdoc.c b/dlls/msxml3/tests/domdoc.c
index 0a2dbcf..86d0cbf 100644
--- a/dlls/msxml3/tests/domdoc.c
+++ b/dlls/msxml3/tests/domdoc.c
@@ -4690,7 +4690,7 @@ static void test_XMLHTTP(void)
     IXMLHttpRequest *xhr;
     IObjectSafety *safety;
     IObjectWithSite *obj_site, *obj_site2;
-    BSTR bstrResponse, url;
+    BSTR bstrResponse, url, str, str1;
     VARIANT dummy;
     VARIANT async;
     VARIANT varbody;
@@ -4840,6 +4840,29 @@ static void test_XMLHTTP(void)
     }
     EXPECT_HR(hr, S_OK);
 
+    /* response headers */
+    hr = IXMLHttpRequest_getAllResponseHeaders(xhr, NULL);
+    EXPECT_HR(hr, E_INVALIDARG);
+    hr = IXMLHttpRequest_getAllResponseHeaders(xhr, &str);
+    EXPECT_HR(hr, S_OK);
+    /* status line is stripped already */
+    ok(memcmp(str, _bstr_("HTTP"), 4*sizeof(WCHAR)), "got response headers %s\n", wine_dbgstr_w(str));
+    ok(*str, "got empty headers\n");
+    hr = IXMLHttpRequest_getAllResponseHeaders(xhr, &str1);
+    EXPECT_HR(hr, S_OK);
+    ok(str1 != str, "got %p\n", str1);
+    SysFreeString(str1);
+    SysFreeString(str);
+
+    hr = IXMLHttpRequest_getResponseHeader(xhr, NULL, NULL);
+    EXPECT_HR(hr, E_INVALIDARG);
+    hr = IXMLHttpRequest_getResponseHeader(xhr, _bstr_("Date"), NULL);
+    EXPECT_HR(hr, E_INVALIDARG);
+    hr = IXMLHttpRequest_getResponseHeader(xhr, _bstr_("Date"), &str);
+    EXPECT_HR(hr, S_OK);
+    ok(*str != ' ', "got leading space in header %s\n", wine_dbgstr_w(str));
+    SysFreeString(str);
+
     /* status code after ::send() */
     status = 0xdeadbeef;
     hr = IXMLHttpRequest_get_status(xhr, &status);
-- 
1.5.6.5


