From bf83ba1fa312849df9ad146f4a5f495a9931aab5 Mon Sep 17 00:00:00 2001
From: Juan Lang <juan_lang@yahoo.com>
Date: Sat, 23 Apr 2005 19:04:49 +0000
Subject: [PATCH] - convert strings between property storage's code page and
 system code   page - add tests for setting code page - fix tests and behavior
 to match WinXP

---
 dlls/ole32/stg_prop.c       | 198 +++++++++++++++++++++++++++++++-----
 dlls/ole32/tests/stg_prop.c | 169 ++++++++++++++++++++++++++++--
 2 files changed, 337 insertions(+), 30 deletions(-)

diff --git a/dlls/ole32/stg_prop.c b/dlls/ole32/stg_prop.c
index 302840ad6b..fb1e54ca8e 100644
--- a/dlls/ole32/stg_prop.c
+++ b/dlls/ole32/stg_prop.c
@@ -31,7 +31,7 @@
  * below, but it gives the best "big picture" that I've found.
  *
  * TODO: There's a lot missing in here.  Biggies:
- * - There are all sorts of restricions I don't honor, like maximum property
+ * - There are all sorts of restrictions I don't honor, like maximum property
  *   set byte size, maximum property name length
  * - Certain bogus files could result in reading past the end of a buffer.
  * - This will probably fail on big-endian machines, especially reading and
@@ -130,6 +130,22 @@ static HRESULT PropertyStorage_CreateDictionaries(
 static void PropertyStorage_DestroyDictionaries(
  struct tagPropertyStorage_impl *);
 
+/* Copies from propvar to prop.  If propvar's type is VT_LPSTR, copies the
+ * string using PropertyStorage_StringCopy.
+ */
+static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
+ const PROPVARIANT *propvar, LCID targetCP, LCID srcCP);
+
+/* Copies the string src, which is encoded using code page srcCP, and returns
+ * it in *dst, in the code page specified by targetCP.  The returned string is
+ * allocated using CoTaskMemAlloc.
+ * If srcCP is CP_UNICODE, src is in fact an LPCWSTR.  Similarly, if targetCP
+ * is CP_UNICODE, the returned string is in fact an LPWSTR.
+ * Returns S_OK on success, something else on failure.
+ */
+static HRESULT PropertyStorage_StringCopy(LPCSTR src, LPSTR *dst, LCID targetCP,
+ LCID srcCP);
+
 static IPropertyStorageVtbl IPropertyStorage_Vtbl;
 
 /***********************************************************************
@@ -280,23 +296,144 @@ static HRESULT WINAPI IPropertyStorage_fnReadMultiple(
              rgpspec[i].u.lpwstr);
 
             if (prop)
-                PropVariantCopy(&rgpropvar[i], prop);
+                PropertyStorage_PropVariantCopy(&rgpropvar[i], prop, GetACP(),
+                 This->codePage);
         }
         else
         {
-            PROPVARIANT *prop = PropertyStorage_FindProperty(This,
-             rgpspec[i].u.propid);
+            switch (rgpspec[i].u.propid)
+            {
+                case PID_CODEPAGE:
+                    rgpropvar[i].vt = VT_I2;
+                    rgpropvar[i].u.iVal = This->codePage;
+                    break;
+                case PID_LOCALE:
+                    rgpropvar[i].vt = VT_I4;
+                    rgpropvar[i].u.lVal = This->locale;
+                    break;
+                default:
+                {
+                    PROPVARIANT *prop = PropertyStorage_FindProperty(This,
+                     rgpspec[i].u.propid);
 
-            if (prop)
-                PropVariantCopy(&rgpropvar[i], prop);
+                    if (prop)
+                        PropertyStorage_PropVariantCopy(&rgpropvar[i], prop,
+                         GetACP(), This->codePage);
+                }
+            }
         }
     }
     LeaveCriticalSection(&This->cs);
     return hr;
 }
 
+static HRESULT PropertyStorage_StringCopy(LPCSTR src, LPSTR *dst, LCID targetCP,
+ LCID srcCP)
+{
+    HRESULT hr = S_OK;
+    int len;
+
+    TRACE("%s, %p, %ld, %ld\n",
+     srcCP == CP_UNICODE ? debugstr_w((LPCWSTR)src) : debugstr_a(src), dst,
+     targetCP, srcCP);
+    assert(src);
+    assert(dst);
+    *dst = NULL;
+    if (targetCP == srcCP)
+    {
+        size_t len;
+
+        if (targetCP == CP_UNICODE)
+            len = (strlenW((LPCWSTR)src) + 1) * sizeof(WCHAR);
+        else
+            len = strlen(src) + 1;
+        *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
+        if (!*dst)
+            hr = STG_E_INSUFFICIENTMEMORY;
+        else
+            memcpy(*dst, src, len);
+    }
+    else
+    {
+        if (targetCP == CP_UNICODE)
+        {
+            len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
+            *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
+            if (!*dst)
+                hr = STG_E_INSUFFICIENTMEMORY;
+            else
+                MultiByteToWideChar(srcCP, 0, src, -1, (LPWSTR)*dst, len);
+        }
+        else
+        {
+            LPWSTR wideStr;
+
+            if (srcCP == CP_UNICODE)
+                wideStr = (LPWSTR)src;
+            else
+            {
+                len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
+                wideStr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+                if (wideStr)
+                    MultiByteToWideChar(srcCP, 0, src, -1, wideStr, len);
+                else
+                    hr = STG_E_INSUFFICIENTMEMORY;
+            }
+            if (SUCCEEDED(hr))
+            {
+                len = WideCharToMultiByte(targetCP, 0, wideStr, -1, NULL, 0,
+                 NULL, NULL);
+                *dst = CoTaskMemAlloc(len);
+                if (!*dst)
+                    hr = STG_E_INSUFFICIENTMEMORY;
+                else
+                {
+                    BOOL defCharUsed = FALSE;
+
+                    if (WideCharToMultiByte(targetCP, 0, wideStr, -1, *dst,
+                     len, NULL, &defCharUsed) == 0 || defCharUsed)
+                    {
+                        CoTaskMemFree(*dst);
+                        *dst = NULL;
+                        hr = HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION);
+                    }
+                }
+            }
+            if (wideStr != (LPWSTR)src)
+                HeapFree(GetProcessHeap(), 0, wideStr);
+        }
+    }
+    TRACE("returning 0x%08lx (%s)\n", hr,
+     targetCP == CP_UNICODE ? debugstr_w((LPCWSTR)*dst) : debugstr_a(*dst));
+    return hr;
+}
+
+static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
+ const PROPVARIANT *propvar, LCID targetCP, LCID srcCP)
+{
+    HRESULT hr = S_OK;
+
+    assert(prop);
+    assert(propvar);
+    if (propvar->vt == VT_LPSTR)
+    {
+        hr = PropertyStorage_StringCopy(propvar->u.pszVal, &prop->u.pszVal,
+         targetCP, srcCP);
+        if (SUCCEEDED(hr))
+            prop->vt = VT_LPSTR;
+    }
+    else
+        PropVariantCopy(prop, propvar);
+    return hr;
+}
+
+/* Stores the property with id propid and value propvar into this property
+ * storage.  lcid is ignored if propvar's type is not VT_LPSTR.  If propvar's
+ * type is VT_LPSTR, converts the string using lcid as the source code page
+ * and This->codePage as the target code page before storing.
+ */
 static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
- PROPID propid, const PROPVARIANT *propvar)
+ PROPID propid, const PROPVARIANT *propvar, LCID lcid)
 {
     HRESULT hr = S_OK;
     PROPVARIANT *prop = PropertyStorage_FindProperty(This, propid);
@@ -307,7 +444,8 @@ static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
     if (prop)
     {
         PropVariantClear(prop);
-        PropVariantCopy(prop, propvar);
+        hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
+         lcid);
     }
     else
     {
@@ -315,10 +453,16 @@ static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
          sizeof(PROPVARIANT));
         if (prop)
         {
-            PropVariantCopy(prop, propvar);
-            dictionary_insert(This->propid_to_prop, (void *)propid, prop);
-            if (propid > This->highestProp)
-                This->highestProp = propid;
+            hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
+             lcid);
+            if (SUCCEEDED(hr))
+            {
+                dictionary_insert(This->propid_to_prop, (void *)propid, prop);
+                if (propid > This->highestProp)
+                    This->highestProp = propid;
+            }
+            else
+                HeapFree(GetProcessHeap(), 0, prop);
         }
         else
             hr = STG_E_INSUFFICIENTMEMORY;
@@ -383,7 +527,7 @@ static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
                     dictionary_insert(This->propid_to_name, (void *)nextId,
                      name);
                     hr = PropertyStorage_StorePropWithId(This, nextId,
-                     &rgpropvar[i]);
+                     &rgpropvar[i], GetACP());
                 }
             }
         }
@@ -419,7 +563,7 @@ static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
                     hr = STG_E_INVALIDPARAMETER;
                 else
                     hr = PropertyStorage_StorePropWithId(This,
-                     rgpspec[i].u.propid, &rgpropvar[i]);
+                     rgpspec[i].u.propid, &rgpropvar[i], GetACP());
             }
         }
     }
@@ -541,16 +685,23 @@ static HRESULT WINAPI IPropertyStorage_fnWritePropertyNames(
     hr = S_OK;
     EnterCriticalSection(&This->cs);
     This->dirty = TRUE;
-    for (i = 0; i < cpropid; i++)
+    for (i = 0; SUCCEEDED(hr) && i < cpropid; i++)
     {
         if (rgpropid[i] != PID_ILLEGAL)
         {
             size_t len = lstrlenW(rglpwstrName[i]) + 1;
             LPWSTR name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
 
-            strcpyW(name, rglpwstrName[i]);
-            dictionary_insert(This->name_to_propid, name, (void *)rgpropid[i]);
-            dictionary_insert(This->propid_to_name, (void *)rgpropid[i], name);
+            if (name)
+            {
+                strcpyW(name, rglpwstrName[i]);
+                dictionary_insert(This->name_to_propid, name,
+                 (void *)rgpropid[i]);
+                dictionary_insert(This->propid_to_name, (void *)rgpropid[i],
+                 name);
+            }
+            else
+                hr = STG_E_INSUFFICIENTMEMORY;
         }
     }
     if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
@@ -879,8 +1030,9 @@ static HRESULT PropertyStorage_ReadProperty(PROPVARIANT *prop, const BYTE *data)
         {
             /* FIXME: if the host is big-endian, this'll suck */
             memcpy(prop->u.pszVal, data + sizeof(DWORD), count);
-            /* FIXME: so far so good, but this may be Unicode or DBCS depending
-             * on This->codePage.
+            /* This is stored in the code page specified in This->codePage.
+             * Don't convert it, the caller will just store it as-is.
+             * (Note the trace will be misleading if the code page is Unicode.)
              */
             TRACE("Read string value %s\n", debugstr_a(prop->u.pszVal));
         }
@@ -1160,7 +1312,7 @@ static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This)
                         break;
                     default:
                         hr = PropertyStorage_StorePropWithId(This,
-                         idOffset->propid, &prop);
+                         idOffset->propid, &prop, This->codePage);
                     }
                 }
             }
@@ -2106,12 +2258,12 @@ HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str)
  */
 HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid)
 {
-    HRESULT hr = E_INVALIDARG;
+    HRESULT hr = STG_E_INVALIDNAME;
 
     TRACE("%s, %p\n", debugstr_w(str), rfmtid);
 
     if (!rfmtid) return E_INVALIDARG;
-    if (!str) return E_INVALIDARG;
+    if (!str) return STG_E_INVALIDNAME;
 
     if (!lstrcmpiW(str, szDocSummaryInfo))
     {
diff --git a/dlls/ole32/tests/stg_prop.c b/dlls/ole32/tests/stg_prop.c
index e309093f93..f1059f6531 100644
--- a/dlls/ole32/tests/stg_prop.c
+++ b/dlls/ole32/tests/stg_prop.c
@@ -211,12 +211,10 @@ static void testProps(void)
     /* revert it */
     hr = IPropertyStorage_Revert(propertyStorage);
     ok(SUCCEEDED(hr), "Revert failed: 0x%08lx\n", hr);
-    /* and make sure it's still an integer */
-    hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
-    ok(SUCCEEDED(hr), "ReadMultiple failed: 0x%08lx\n", hr);
-    ok(var.vt == VT_I4 && U(var).lVal == 1,
-     "Didn't get expected type or value for property (got type %d, value %ld)\n",
-     var.vt, U(var).lVal);
+    /* Oddly enough, there's no guarantee that a successful revert actually
+     * implies the value wasn't saved.  Maybe transactional mode needs to be
+     * used for that?
+     */
 
     IPropertyStorage_Release(propertyStorage);
     propertyStorage = NULL;
@@ -260,6 +258,161 @@ static void testProps(void)
     DeleteFileW(filename);
 }
 
+void testCodepage(void)
+{
+    static const WCHAR szDot[] = { '.',0 };
+    static const WCHAR szPrefix[] = { 's','t','g',0 };
+    static const WCHAR wval[] = { 'h','i',0 };
+    HRESULT hr;
+    IStorage *storage = NULL;
+    IPropertySetStorage *propSetStorage = NULL;
+    IPropertyStorage *propertyStorage = NULL;
+    PROPSPEC spec;
+    PROPVARIANT var;
+    WCHAR fileName[MAX_PATH];
+
+    if(!GetTempFileNameW(szDot, szPrefix, 0, fileName))
+        return;
+
+    hr = StgCreateDocfile(fileName,
+     STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, &storage);
+    ok(SUCCEEDED(hr), "StgCreateDocfile failed: 0x%08lx\n", hr);
+
+    hr = StgCreatePropSetStg(storage, 0, &propSetStorage);
+    ok(SUCCEEDED(hr), "StgCreatePropSetStg failed: 0x%08lx\n", hr);
+
+    hr = IPropertySetStorage_Create(propSetStorage,
+     &FMTID_SummaryInformation, NULL, PROPSETFLAG_DEFAULT,
+     STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE,
+     &propertyStorage);
+    ok(SUCCEEDED(hr), "IPropertySetStorage_Create failed: 0x%08lx\n", hr);
+
+    PropVariantInit(&var);
+    spec.ulKind = PRSPEC_PROPID;
+    U(spec).propid = PID_CODEPAGE;
+    /* check code page before it's been explicitly set */
+    hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
+    ok(SUCCEEDED(hr), "ReadMultiple failed: 0x%08lx\n", hr);
+    ok(var.vt == VT_I2 && U(var).iVal == 1200,
+     "Didn't get expected type or value for property\n");
+    /* Set the code page to ascii */
+    var.vt = VT_I2;
+    U(var).iVal = 1252;
+    hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
+    ok(SUCCEEDED(hr), "WriteMultiple failed: 0x%08lx\n", hr);
+    /* check code page */
+    hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
+    ok(SUCCEEDED(hr), "ReadMultiple failed: 0x%08lx\n", hr);
+    ok(var.vt == VT_I2 && U(var).iVal == 1252,
+     "Didn't get expected type or value for property\n");
+    /* Set code page to Unicode */
+    U(var).iVal = 1200;
+    hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
+    ok(SUCCEEDED(hr), "WriteMultiple failed: 0x%08lx\n", hr);
+    /* check code page */
+    hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
+    ok(SUCCEEDED(hr), "ReadMultiple failed: 0x%08lx\n", hr);
+    ok(var.vt == VT_I2 && U(var).iVal == 1200,
+     "Didn't get expected type or value for property\n");
+    /* Set a string value */
+    spec.ulKind = PRSPEC_PROPID;
+    U(spec).propid = PID_FIRST_USABLE;
+    var.vt = VT_LPSTR;
+    U(var).pszVal = "hi";
+    hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
+    ok(SUCCEEDED(hr), "WriteMultiple failed: 0x%08lx\n", hr);
+    hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
+    ok(SUCCEEDED(hr), "ReadMultiple failed: 0x%08lx\n", hr);
+    ok(var.vt == VT_LPSTR && !strcmp(U(var).pszVal, "hi"),
+     "Didn't get expected type or value for property\n");
+    /* This seemingly non-sensical test is to show that the string is indeed
+     * interpreted according to the current system code page, not according to
+     * the property set's code page.  (If the latter were true, the whole
+     * string would be maintained.  As it is, only the first character is.)
+     */
+    U(var).pszVal = (LPSTR)wval;
+    hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
+    ok(SUCCEEDED(hr), "WriteMultiple failed: 0x%08lx\n", hr);
+    hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
+    ok(SUCCEEDED(hr), "ReadMultiple failed: 0x%08lx\n", hr);
+    ok(var.vt == VT_LPSTR && !strcmp(U(var).pszVal, "h"),
+     "Didn't get expected type or value for property\n");
+    /* now that a property's been set, you can't change the code page */
+    spec.ulKind = PRSPEC_PROPID;
+    U(spec).propid = PID_CODEPAGE;
+    var.vt = VT_I2;
+    U(var).iVal = 1200;
+    hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
+    ok(hr == STG_E_INVALIDPARAMETER,
+     "Expected STG_E_INVALIDPARAMETER, got 0x%08lx\n", hr);
+
+    IPropertyStorage_Release(propertyStorage);
+    IPropertySetStorage_Release(propSetStorage);
+    IStorage_Release(storage);
+
+    DeleteFileW(fileName);
+
+    /* same tests, but with PROPSETFLAG_ANSI */
+    hr = StgCreateDocfile(fileName,
+     STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, &storage);
+    ok(SUCCEEDED(hr), "StgCreateDocfile failed: 0x%08lx\n", hr);
+
+    hr = StgCreatePropSetStg(storage, 0, &propSetStorage);
+    ok(SUCCEEDED(hr), "StgCreatePropSetStg failed: 0x%08lx\n", hr);
+
+    hr = IPropertySetStorage_Create(propSetStorage,
+     &FMTID_SummaryInformation, NULL, PROPSETFLAG_ANSI,
+     STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE,
+     &propertyStorage);
+    ok(SUCCEEDED(hr), "IPropertySetStorage_Create failed: 0x%08lx\n", hr);
+
+    /* check code page before it's been explicitly set */
+    hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
+    ok(SUCCEEDED(hr), "ReadMultiple failed: 0x%08lx\n", hr);
+    ok(var.vt == VT_I2 && U(var).iVal == 1252,
+     "Didn't get expected type or value for property\n");
+    /* Set code page to Unicode */
+    U(var).iVal = 1200;
+    hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
+    ok(SUCCEEDED(hr), "WriteMultiple failed: 0x%08lx\n", hr);
+    /* check code page */
+    hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
+    ok(SUCCEEDED(hr), "ReadMultiple failed: 0x%08lx\n", hr);
+    ok(var.vt == VT_I2 && U(var).iVal == 1200,
+     "Didn't get expected type or value for property\n");
+    /* This test is commented out for documentation.  It fails under Wine,
+     * and I expect it would under Windows as well, yet it succeeds.  There's
+     * obviously something about string conversion I don't understand.
+     */
+#if 0
+    static const char strVal[] = { 0x81, 0xff, 0x04, 0 };
+    /* Set code page to 950 (Traditional Chinese) */
+    U(var).iVal = 950;
+    hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
+    ok(SUCCEEDED(hr), "WriteMultiple failed: 0x%08lx\n", hr);
+    /* Try writing an invalid string: lead byte 0x81 is unused in Traditional
+     * Chinese.
+     */
+    spec.ulKind = PRSPEC_PROPID;
+    U(spec).propid = PID_FIRST_USABLE;
+    var.vt = VT_LPSTR;
+    U(var).pszVal = (LPSTR)strVal;
+    hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
+    ok(SUCCEEDED(hr), "WriteMultiple failed: 0x%08lx\n", hr);
+    /* Check returned string */
+    hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
+    ok(SUCCEEDED(hr), "ReadMultiple failed: 0x%08lx\n", hr);
+    ok(var.vt == VT_LPSTR && !strcmp(U(var).pszVal, strVal),
+     "Didn't get expected type or value for property\n");
+#endif
+
+    IPropertyStorage_Release(propertyStorage);
+    IPropertySetStorage_Release(propSetStorage);
+    IStorage_Release(storage);
+
+    DeleteFileW(fileName);
+}
+
 static void testFmtId(void)
 {
     WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y',
@@ -302,7 +455,8 @@ static void testFmtId(void)
     hr = pPropStgNameToFmtId(NULL, NULL);
     ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08lx\n", hr);
     hr = pPropStgNameToFmtId(NULL, &fmtid);
-    ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08lx\n", hr);
+    ok(hr == STG_E_INVALIDNAME, "Expected STG_E_INVALIDNAME, got 0x%08lx\n",
+     hr);
     hr = pPropStgNameToFmtId(szDocSummaryInfo, NULL);
     ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08lx\n", hr);
     /* test the known format IDs */
@@ -337,5 +491,6 @@ START_TEST(stg_prop)
 {
     init_function_pointers();
     testProps();
+    testCodepage();
     testFmtId();
 }
-- 
2.32.0.93.g670b81a890