Sync to Wine-0_9_3:
[reactos.git] / reactos / lib / shell32 / shellpath.c
index 3201c64..fe6aef1 100644 (file)
@@ -805,7 +805,7 @@ static const WCHAR szSHUserFolders[] = {'S','o','f','t','w','a','r','e','\\','M'
 /* This defaults to L"Documents and Settings" on Windows 2000/XP, but we're
  * acting more Windows 9x-like for now.
  */
-static const WCHAR szDefaultProfileDirW[] = {'w','i','n','d','o','w','s','\\','p','r','o','f','i','l','e','s','\0'};
+static const WCHAR szDefaultProfileDirW[] = {'p','r','o','f','i','l','e','s','\0'};
 static const WCHAR AllUsersW[] = {'A','l','l',' ','U','s','e','r','s','\0'};
 
 typedef enum _CSIDL_Type {
@@ -1202,17 +1202,22 @@ static HRESULT _SHGetUserShellFolderPath(HKEY rootKey, LPCWSTR userPrefix,
     if (!RegQueryValueExW(userShellFolderKey, value, NULL, &dwType,
      (LPBYTE)path, &dwPathLen) && (dwType == REG_EXPAND_SZ || dwType == REG_SZ))
     {
+        LONG ret;
+
         path[dwPathLen / sizeof(WCHAR)] = '\0';
         if (dwType == REG_EXPAND_SZ && path[0] == '%')
         {
             WCHAR szTemp[MAX_PATH];
 
             _SHExpandEnvironmentStrings(path, szTemp);
-            strncpyW(path, szTemp, MAX_PATH);
+            lstrcpynW(path, szTemp, MAX_PATH);
         }
-        RegSetValueExW(shellFolderKey, value, 0, REG_SZ, (LPBYTE)path,
+        ret = RegSetValueExW(shellFolderKey, value, 0, REG_SZ, (LPBYTE)path,
          (strlenW(path) + 1) * sizeof(WCHAR));
-        hr = S_OK;
+        if (ret != ERROR_SUCCESS)
+            hr = HRESULT_FROM_WIN32(ret);
+        else
+            hr = S_OK;
     }
     else
         hr = E_FAIL;
@@ -1235,13 +1240,6 @@ static HRESULT _SHGetUserShellFolderPath(HKEY rootKey, LPCWSTR userPrefix,
  *   CSIDL_Type_AllUsers: %ALLUSERSPROFILE%
  *   CSIDL_Type_CurrVer:  %SystemDrive%
  *   (Others might make sense too, but as yet are unneeded.)
- * FIXME: there are two special cases for the default value:
- * - the "My Documents" (CSIDL_PERSONAL) entry should be $HOME
- * - the CSIDL_DESKTOP and CSIDL_DESKTOPDIRECTORY (which have the same path)
- *   should be $HOME/Desktop if it exists
- * But, $HOME doesn't seem to be inherited into the Wine environment.  I could
- * use getenv, but this returns me a UNIX path, which may or may not be
- * reachable from any currently mounted DOS drives.
  */
 static HRESULT _SHGetDefaultValue(BYTE folder, LPWSTR pszPath)
 {
@@ -1256,18 +1254,67 @@ static HRESULT _SHGetDefaultValue(BYTE folder, LPWSTR pszPath)
     if (!pszPath)
         return E_INVALIDARG;
 
+    /* Try special cases first */
+    hr = E_FAIL;
+    switch (folder)
+    {
+        case CSIDL_PERSONAL:
+        case CSIDL_MYMUSIC:
+        case CSIDL_MYPICTURES:
+        case CSIDL_MYVIDEO:
+        {
+            const char *home = getenv("HOME");
+
+            /* special case for "My Documents", map to $HOME */
+            if (home)
+            {
+                WCHAR homeW[MAX_PATH];
+
+                MultiByteToWideChar(CP_ACP, 0, home, -1, homeW, MAX_PATH);
+                if (GetFullPathNameW(homeW, MAX_PATH, pszPath, NULL) != 0 &&
+                 PathIsDirectoryW(pszPath))
+                    hr = S_OK;
+            }
+            break;
+        }
+        case CSIDL_DESKTOP:
+        case CSIDL_DESKTOPDIRECTORY:
+        {
+            const char *home = getenv("HOME");
+
+            /* special case for Desktop, map to $HOME/Desktop if it exists */
+            if (home)
+            {
+                WCHAR desktopW[MAX_PATH];
+
+                MultiByteToWideChar(CP_ACP, 0, home, -1, desktopW, MAX_PATH);
+                PathAppendW(desktopW, DesktopW);
+                if (GetFullPathNameW(desktopW, MAX_PATH, pszPath, NULL) != 0 &&
+                 PathIsDirectoryW(pszPath))
+                    hr = S_OK;
+            }
+            break;
+        }
+    }
+    if (SUCCEEDED(hr))
+        return hr;
+
+    /* Either the folder was unhandled, or a suitable default wasn't found,
+     * so use one of the resource-based defaults
+     */
     if (CSIDL_Data[folder].szDefaultPath &&
      IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath))
     {
         if (LoadStringW(shell32_hInstance,
-         (UINT)CSIDL_Data[folder].szDefaultPath, resourcePath, MAX_PATH))
+         LOWORD(CSIDL_Data[folder].szDefaultPath), resourcePath, MAX_PATH))
         {
             hr = S_OK;
             pDefaultPath = resourcePath;
         }
         else
         {
-            FIXME("(%d,%s), LoadString failed, missing translation?\n", folder, debugstr_w(pszPath));
+            FIXME("(%d,%s), LoadString failed, missing translation?\n", folder,
+             debugstr_w(pszPath));
             hr = E_FAIL;
         }
     }
@@ -1451,57 +1498,6 @@ static HRESULT _SHGetAllUsersProfilePath(DWORD dwFlags, BYTE folder,
     return hr;
 }
 
-static HRESULT _SHOpenProfilesKey(PHKEY pKey)
-{
-    LONG lRet;
-    DWORD disp;
-
-    lRet = RegCreateKeyExW(HKEY_LOCAL_MACHINE, ProfileListW, 0, NULL, 0,
-     KEY_ALL_ACCESS, NULL, pKey, &disp);
-    return HRESULT_FROM_WIN32(lRet);
-}
-
-/* Reads the value named szValueName from the key profilesKey (assumed to be
- * opened by _SHOpenProfilesKey) into szValue, which is assumed to be MAX_PATH
- * WCHARs in length.  If it doesn't exist, returns szDefault (and saves
- * szDefault to the registry).
- */
-static HRESULT _SHGetProfilesValue(HKEY profilesKey, LPCWSTR szValueName,
- LPWSTR szValue, LPCWSTR szDefault)
-{
-    HRESULT hr;
-    DWORD type, dwPathLen = MAX_PATH * sizeof(WCHAR);
-    LONG lRet;
-
-    TRACE("%p,%s,%p,%s\n", profilesKey, debugstr_w(szValueName), szValue,
-     debugstr_w(szDefault));
-    lRet = RegQueryValueExW(profilesKey, szValueName, NULL, &type,
-     (LPBYTE)szValue, &dwPathLen);
-    if (!lRet && (type == REG_SZ || type == REG_EXPAND_SZ) && dwPathLen
-     && *szValue)
-    {
-        dwPathLen /= sizeof(WCHAR);
-        szValue[dwPathLen] = '\0';
-        hr = S_OK;
-    }
-    else
-    {
-        /* Missing or invalid value, set a default */
-        strncpyW(szValue, szDefault, MAX_PATH);
-        szValue[MAX_PATH - 1] = '\0';
-        TRACE("Setting missing value %s to %s\n", debugstr_w(szValueName),
-         debugstr_w(szValue));
-        lRet = RegSetValueExW(profilesKey, szValueName, 0, REG_EXPAND_SZ,
-         (LPBYTE)szValue, (strlenW(szValue) + 1) * sizeof(WCHAR));
-        if (lRet)
-            hr = HRESULT_FROM_WIN32(lRet);
-        else
-            hr = S_OK;
-    }
-    TRACE("returning 0x%08lx (output value is %s)\n", hr, debugstr_w(szValue));
-    return hr;
-}
-
 /* From the original Wine source:
  *
  * Attempts to expand environment variables from szSrc into szDest, which is
@@ -1523,7 +1519,7 @@ static HRESULT _SHGetProfilesValue(HKEY profilesKey, LPCWSTR szValueName,
 static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest)
 {
     HRESULT hr = S_OK;
-    WCHAR szTemp[MAX_PATH], szProfilesPrefix[MAX_PATH] = { 0 };
+    WCHAR szTemp[MAX_PATH];
 
     TRACE("%s, %p\n", debugstr_w(szSrc), szDest);
 
@@ -1566,8 +1562,14 @@ end:
 /*************************************************************************
  * SHGetFolderPathW                    [SHELL32.@]
  *
+ * Convert nFolder to path.  
+ *
+ * RETURNS
+ *  Success: S_OK
+ *  Failure: standard HRESULT error codes.
+ *
  * NOTES
- * Converts nFolder to path.  Most values can be overridden in either
+ * Most values can be overridden in either
  * HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
  * or in the same location in HKLM.
  * The registry usage is explained by the following tech note:
@@ -1581,13 +1583,13 @@ end:
  * http://www.microsoft.com/windows2000/techinfo/reskit/en-us/default.asp?url=/windows2000/techinfo/reskit/en-us/regentry/36276.asp
  * the HKCU paths take precedence over the HKLM paths.
  *
- **********************************************************************/
+ */
 HRESULT WINAPI SHGetFolderPathW(
-       HWND hwndOwner,
-       int nFolder,
-       HANDLE hToken,
-       DWORD dwFlags,
-       LPWSTR pszPath)
+       HWND hwndOwner,    /* [I] owner window */
+       int nFolder,       /* [I] CSIDL identifying the folder */
+       HANDLE hToken,     /* [I] access token */
+       DWORD dwFlags,     /* [I] which path to return */
+       LPWSTR pszPath)    /* [O] converted path */
 {
     HRESULT    hr;
     WCHAR      szBuildPath[MAX_PATH], szTemp[MAX_PATH];
@@ -1693,6 +1695,8 @@ end:
 
 /*************************************************************************
  * SHGetFolderPathA                    [SHELL32.@]
+ *
+ * See SHGetFolderPathW.
  */
 HRESULT WINAPI SHGetFolderPathA(
        HWND hwndOwner,
@@ -1717,21 +1721,21 @@ HRESULT WINAPI SHGetFolderPathA(
 }
 
 /* For each folder in folders, if its value has not been set in the registry,
- * call _SHGetUserProfilePath or _SHGetAllUsersProfilePath (depending on the
+ * calls _SHGetUserProfilePath or _SHGetAllUsersProfilePath (depending on the
  * folder's type) to get the unexpanded value first.
- * This will create the expanded value in the Shell Folders key, and
- * return the unexpanded value.
- * Write the unexpanded value to User Shell Folders, and query it with
- * SHGetFolderPath to force the creation of the directory if it doesn't
- * already exist.
+ * Writes the unexpanded value to User Shell Folders, and queries it with
+ * SHGetFolderPathW to force the creation of the directory if it doesn't
+ * already exist.  SHGetFolderPathW also returns the expanded value, which
+ * this then writes to Shell Folders.
  */
 static HRESULT _SHRegisterFolders(HKEY hRootKey, HANDLE hToken,
- LPCWSTR szUserShellFolderPath, const UINT folders[], UINT foldersLen)
+ LPCWSTR szUserShellFolderPath, LPCWSTR szShellFolderPath, const UINT folders[],
+ UINT foldersLen)
 {
     UINT i;
     WCHAR path[MAX_PATH];
     HRESULT hr = S_OK;
-    HKEY hKey = NULL;
+    HKEY hUserKey = NULL, hKey = NULL;
     DWORD dwDisp, dwType, dwPathLen;
     LONG ret;
 
@@ -1739,13 +1743,20 @@ static HRESULT _SHRegisterFolders(HKEY hRootKey, HANDLE hToken,
      debugstr_w(szUserShellFolderPath), folders, foldersLen);
 
     ret = RegCreateKeyExW(hRootKey, szUserShellFolderPath, 0, NULL, 0,
-     KEY_ALL_ACCESS, NULL, &hKey, &dwDisp);
+     KEY_ALL_ACCESS, NULL, &hUserKey, &dwDisp);
     if (ret)
         hr = HRESULT_FROM_WIN32(ret);
+    else
+    {
+        ret = RegCreateKeyExW(hRootKey, szShellFolderPath, 0, NULL, 0,
+         KEY_ALL_ACCESS, NULL, &hKey, &dwDisp);
+        if (ret)
+            hr = HRESULT_FROM_WIN32(ret);
+    }
     for (i = 0; SUCCEEDED(hr) && i < foldersLen; i++)
     {
         dwPathLen = MAX_PATH * sizeof(WCHAR);
-        if (RegQueryValueExW(hKey, CSIDL_Data[folders[i]].szValueName, NULL,
+        if (RegQueryValueExW(hUserKey, CSIDL_Data[folders[i]].szValueName, NULL,
          &dwType, (LPBYTE)path, &dwPathLen) || (dwType != REG_SZ &&
          dwType != REG_EXPAND_SZ))
         {
@@ -1759,17 +1770,26 @@ static HRESULT _SHRegisterFolders(HKEY hRootKey, HANDLE hToken,
                 hr = E_FAIL;
             if (*path)
             {
-                ret = RegSetValueExW(hKey, CSIDL_Data[folders[i]].szValueName,
-                 0, REG_EXPAND_SZ, (LPBYTE)path,
-                 (strlenW(path) + 1) * sizeof(WCHAR));
+                ret = RegSetValueExW(hUserKey,
+                 CSIDL_Data[folders[i]].szValueName, 0, REG_EXPAND_SZ,
+                 (LPBYTE)path, (strlenW(path) + 1) * sizeof(WCHAR));
                 if (ret)
                     hr = HRESULT_FROM_WIN32(ret);
                 else
+                {
                     hr = SHGetFolderPathW(NULL, folders[i] | CSIDL_FLAG_CREATE,
-                     hToken, SHGFP_TYPE_DEFAULT, NULL);
+                     hToken, SHGFP_TYPE_DEFAULT, path);
+                    ret = RegSetValueExW(hKey,
+                     CSIDL_Data[folders[i]].szValueName, 0, REG_SZ,
+                     (LPBYTE)path, (strlenW(path) + 1) * sizeof(WCHAR));
+                    if (ret)
+                        hr = HRESULT_FROM_WIN32(ret);
+                }
             }
         }
     }
+    if (hUserKey)
+        RegCloseKey(hUserKey);
     if (hKey)
         RegCloseKey(hKey);
 
@@ -1783,19 +1803,23 @@ static HRESULT _SHRegisterUserShellFolders(BOOL bDefault)
      CSIDL_PROGRAMS,
      CSIDL_PERSONAL,
      CSIDL_FAVORITES,
+     CSIDL_APPDATA,
      CSIDL_STARTUP,
      CSIDL_RECENT,
      CSIDL_SENDTO,
      CSIDL_STARTMENU,
+     CSIDL_MYMUSIC,
+     CSIDL_MYVIDEO,
      CSIDL_DESKTOPDIRECTORY,
      CSIDL_NETHOOD,
      CSIDL_TEMPLATES,
      CSIDL_PRINTHOOD,
      CSIDL_COOKIES,
      CSIDL_HISTORY,
+     CSIDL_MYPICTURES
     };
-    WCHAR userShellFolderPath[MAX_PATH];
-    LPCWSTR pUserShellFolderPath;
+    WCHAR userShellFolderPath[MAX_PATH], shellFolderPath[MAX_PATH];
+    LPCWSTR pUserShellFolderPath, pShellFolderPath;
     HRESULT hr = S_OK;
     HKEY hRootKey;
     HANDLE hToken;
@@ -1809,16 +1833,21 @@ static HRESULT _SHRegisterUserShellFolders(BOOL bDefault)
         PathAddBackslashW(userShellFolderPath);
         strcatW(userShellFolderPath, szSHUserFolders);
         pUserShellFolderPath = userShellFolderPath;
+        strcpyW(shellFolderPath, DefaultW);
+        PathAddBackslashW(shellFolderPath);
+        strcatW(shellFolderPath, szSHFolders);
+        pShellFolderPath = shellFolderPath;
     }
     else
     {
         hToken = NULL;
         hRootKey = HKEY_CURRENT_USER;
         pUserShellFolderPath = szSHUserFolders;
+        pShellFolderPath = szSHFolders;
     }
 
     hr = _SHRegisterFolders(hRootKey, hToken, pUserShellFolderPath,
-     folders, sizeof(folders) / sizeof(folders[0]));
+     pShellFolderPath, folders, sizeof(folders) / sizeof(folders[0]));
     TRACE("returning 0x%08lx\n", hr);
     return hr;
 }
@@ -1839,7 +1868,7 @@ static HRESULT _SHRegisterCommonShellFolders(void)
 
     TRACE("\n");
     hr = _SHRegisterFolders(HKEY_LOCAL_MACHINE, NULL, szSHUserFolders,
-     folders, sizeof(folders) / sizeof(folders[0]));
+     szSHFolders, folders, sizeof(folders) / sizeof(folders[0]));
     TRACE("returning 0x%08lx\n", hr);
     return hr;
 }
@@ -1910,11 +1939,7 @@ BOOL WINAPI SHGetSpecialFolderPathAW (
 /*************************************************************************
  * SHGetFolderLocation [SHELL32.@]
  *
- * NOTES
  * Gets the folder locations from the registry and creates a pidl.
- * Creates missing reg keys and directories.
- * Mostly forwards to SHGetFolderPathW, but a few values of nFolder return
- * virtual folders that are handled here.
  *
  * PARAMS
  *   hwndOwner  [I]
@@ -1924,7 +1949,14 @@ BOOL WINAPI SHGetSpecialFolderPathAW (
  *   dwReserved [I] must be zero
  *   ppidl      [O] PIDL of a special folder
  *
+ * RETURNS
+ *  Success: S_OK
+ *  Failure: Standard OLE-defined error result, S_FALSE or E_INVALIDARG
+ *
  * NOTES
+ *  Creates missing reg keys and directories.
+ *  Mostly forwards to SHGetFolderPathW, but a few values of nFolder return
+ *  virtual folders that are handled here.
  */
 HRESULT WINAPI SHGetFolderLocation(
        HWND hwndOwner,