Commit 81b4dba5 authored by Olivier F. R. Dierick's avatar Olivier F. R. Dierick Committed by Alexandre Julliard

shell32: Move _SHCreateSymbolicLinks() above shell folders lookup functions.

Looking up shell folders creates real directories when the folder is inexistant, but the intended behavior for some folders is to create symbolic links by default. Prepare that change by moving the helper function above shell folders lookup functions. Also fix whitespace issues in the moved code. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=22974Signed-off-by: 's avatarOlivier F. R. Dierick <o.dierick@piezo-forte.be> Signed-off-by: 's avatarAlexandre Julliard <julliard@winehq.org>
parent 05a8d45e
...@@ -3949,301 +3949,566 @@ end: ...@@ -3949,301 +3949,566 @@ end:
} }
/************************************************************************* /*************************************************************************
* SHGetFolderPathW [SHELL32.@] * _SHAppendToUnixPath [Internal]
* *
* Convert nFolder to path. * Helper function for _SHCreateSymbolicLinks. Appends pwszSubPath (or the
* corresponding resource, if IS_INTRESOURCE) to the unix base path 'szBasePath'
* and replaces backslashes with slashes.
* *
* RETURNS * PARAMS
* Success: S_OK * szBasePath [IO] The unix base path, which will be appended to (CP_UNXICP).
* Failure: standard HRESULT error codes. * pwszSubPath [I] Sub-path or resource id (use MAKEINTRESOURCEW).
* *
* NOTES * RETURNS
* Most values can be overridden in either * Success: TRUE,
* HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders * Failure: FALSE
* or in the same location in HKLM.
* The "Shell Folders" registry key was used in NT4 and earlier systems.
* Beginning with Windows 2000, the "User Shell Folders" key is used, so
* changes made to it are made to the former key too. This synchronization is
* done on-demand: not until someone requests the value of one of these paths
* (by calling one of the SHGet functions) is the value synchronized.
* Furthermore, the HKCU paths take precedence over the HKLM paths.
*/ */
HRESULT WINAPI SHGetFolderPathW( static inline BOOL _SHAppendToUnixPath(char *szBasePath, LPCWSTR pwszSubPath) {
HWND hwndOwner, /* [I] owner window */ WCHAR wszSubPath[MAX_PATH];
int nFolder, /* [I] CSIDL identifying the folder */ int cLen = strlen(szBasePath);
HANDLE hToken, /* [I] access token */ char *pBackslash;
DWORD dwFlags, /* [I] which path to return */
LPWSTR pszPath) /* [O] converted path */
{
HRESULT hr = SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, NULL, pszPath);
if(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
return hr;
}
HRESULT WINAPI SHGetFolderPathAndSubDirA(
HWND hwndOwner, /* [I] owner window */
int nFolder, /* [I] CSIDL identifying the folder */
HANDLE hToken, /* [I] access token */
DWORD dwFlags, /* [I] which path to return */
LPCSTR pszSubPath, /* [I] sub directory of the specified folder */
LPSTR pszPath) /* [O] converted path */
{
int length;
HRESULT hr = S_OK;
LPWSTR pszSubPathW = NULL;
LPWSTR pszPathW = NULL;
TRACE("%p,%#x,%p,%#x,%s,%p\n", hwndOwner, nFolder, hToken, dwFlags, debugstr_a(pszSubPath), pszPath);
if(pszPath) { if (IS_INTRESOURCE(pwszSubPath)) {
pszPathW = heap_alloc(MAX_PATH * sizeof(WCHAR)); if (!LoadStringW(shell32_hInstance, LOWORD(pwszSubPath), wszSubPath, MAX_PATH)) {
if(!pszPathW) { /* Fall back to hard coded defaults. */
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); switch (LOWORD(pwszSubPath)) {
goto cleanup; case IDS_PERSONAL:
} lstrcpyW(wszSubPath, DocumentsW);
break;
case IDS_MYMUSIC:
lstrcpyW(wszSubPath, My_MusicW);
break;
case IDS_MYPICTURES:
lstrcpyW(wszSubPath, My_PicturesW);
break;
case IDS_MYVIDEOS:
lstrcpyW(wszSubPath, My_VideosW);
break;
case IDS_DOWNLOADS:
lstrcpyW(wszSubPath, DownloadsW);
break;
case IDS_TEMPLATES:
lstrcpyW(wszSubPath, TemplatesW);
break;
default:
ERR("LoadString(%d) failed!\n", LOWORD(pwszSubPath));
return FALSE;
} }
TRACE("%08x,%08x,%s\n",nFolder, dwFlags, debugstr_w(pszSubPathW));
/* SHGetFolderPathAndSubDirW does not distinguish if pszSubPath isn't
* set (null), or an empty string.therefore call it without the parameter set
* if pszSubPath is an empty string
*/
if (pszSubPath && pszSubPath[0]) {
length = MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, NULL, 0);
pszSubPathW = heap_alloc(length * sizeof(WCHAR));
if(!pszSubPathW) {
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
goto cleanup;
} }
MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, pszSubPathW, length); } else {
lstrcpyW(wszSubPath, pwszSubPath);
} }
hr = SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, pszSubPathW, pszPathW); if (szBasePath[cLen-1] != '/') szBasePath[cLen++] = '/';
if (SUCCEEDED(hr) && pszPath) if (!WideCharToMultiByte(CP_UNIXCP, 0, wszSubPath, -1, szBasePath + cLen,
WideCharToMultiByte(CP_ACP, 0, pszPathW, -1, pszPath, MAX_PATH, NULL, NULL); FILENAME_MAX - cLen, NULL, NULL))
{
return FALSE;
}
cleanup: pBackslash = szBasePath + cLen;
heap_free(pszPathW); while ((pBackslash = strchr(pBackslash, '\\'))) *pBackslash = '/';
heap_free(pszSubPathW);
return hr; return TRUE;
} }
/************************************************************************* /******************************************************************************
* SHGetFolderPathAndSubDirW [SHELL32.@] * _SHCreateSymbolicLinks [Internal]
*
* Sets up symbol links for various shell folders to point into the user's home
* directory. We do an educated guess about what the user would probably want:
* - If there is a 'My Documents' directory in $HOME, the user probably wants
* wine's 'My Documents' to point there. Furthermore, we infer that the user
* is a Windows lover and has no problem with wine creating subfolders for
* 'My Pictures', 'My Music', 'My Videos' etc. under '$HOME/My Documents', if
* those do not already exist. We put appropriate symbolic links in place for
* those, too.
* - If there is no 'My Documents' directory in $HOME, we let 'My Documents'
* point directly to $HOME. We assume the user to be a unix hacker who does not
* want wine to create anything anywhere besides the .wine directory. So, if
* there already is a 'My Music' directory in $HOME, we symlink the 'My Music'
* shell folder to it. But if not, then we check XDG_MUSIC_DIR - "well known"
* directory, and try to link to that. If that fails, then we symlink to
* $HOME directly. The same holds for 'My Pictures', 'My Videos' etc.
* - The Desktop shell folder is symlinked to XDG_DESKTOP_DIR. If that does not
* exist, then we try '$HOME/Desktop'. If that does not exist, then we leave
* it alone.
* ('My Music',... above in fact means LoadString(IDS_MYMUSIC))
*/ */
HRESULT WINAPI SHGetFolderPathAndSubDirW( static void _SHCreateSymbolicLinks(void)
HWND hwndOwner, /* [I] owner window */
int nFolder, /* [I] CSIDL identifying the folder */
HANDLE hToken, /* [I] access token */
DWORD dwFlags, /* [I] which path to return */
LPCWSTR pszSubPath,/* [I] sub directory of the specified folder */
LPWSTR pszPath) /* [O] converted path */
{ {
static const UINT aidsMyStuff[] = {
IDS_MYPICTURES, IDS_MYVIDEOS, IDS_MYMUSIC, IDS_DOWNLOADS, IDS_TEMPLATES
};
static const WCHAR * const MyOSXStuffW[] = {
PicturesW, MoviesW, MusicW, DownloadsW, TemplatesW
};
static const int acsidlMyStuff[] = {
CSIDL_MYPICTURES, CSIDL_MYVIDEO, CSIDL_MYMUSIC, CSIDL_DOWNLOADS, CSIDL_TEMPLATES
};
static const char * const xdg_dirs[] = {
"PICTURES", "VIDEOS", "MUSIC", "DOWNLOAD", "TEMPLATES", "DOCUMENTS", "DESKTOP"
};
static const unsigned int num = ARRAY_SIZE(xdg_dirs);
WCHAR wszTempPath[MAX_PATH];
char szPersonalTarget[FILENAME_MAX], *pszPersonal;
char szMyStuffTarget[FILENAME_MAX], *pszMyStuff;
char szDesktopTarget[FILENAME_MAX], *pszDesktop;
struct stat statFolder;
const char *pszHome;
HRESULT hr; HRESULT hr;
WCHAR szBuildPath[MAX_PATH], szTemp[MAX_PATH]; char ** xdg_results;
DWORD folder = nFolder & CSIDL_FOLDER_MASK; char * xdg_desktop_dir;
CSIDL_Type type; UINT i;
int ret;
TRACE("%p,%#x,%p,%#x,%s,%p\n", hwndOwner, nFolder, hToken, dwFlags, debugstr_w(pszSubPath), pszPath); /* Create all necessary profile sub-dirs up to 'My Documents' and get the unix path. */
hr = SHGetFolderPathW(NULL, CSIDL_PERSONAL|CSIDL_FLAG_CREATE, NULL,
SHGFP_TYPE_DEFAULT, wszTempPath);
if (FAILED(hr)) return;
pszPersonal = wine_get_unix_file_name(wszTempPath);
if (!pszPersonal) return;
/* Windows always NULL-terminates the resulting path regardless of success hr = XDG_UserDirLookup(xdg_dirs, num, &xdg_results);
* or failure, so do so first if (FAILED(hr)) xdg_results = NULL;
*/
if (pszPath)
*pszPath = '\0';
if (folder >= ARRAY_SIZE(CSIDL_Data)) pszHome = getenv("HOME");
return E_INVALIDARG; if (pszHome && !stat(pszHome, &statFolder) && S_ISDIR(statFolder.st_mode))
if ((SHGFP_TYPE_CURRENT != dwFlags) && (SHGFP_TYPE_DEFAULT != dwFlags))
return E_INVALIDARG;
szTemp[0] = 0;
type = CSIDL_Data[folder].type;
switch (type)
{ {
case CSIDL_Type_Disallowed: while (1)
hr = E_INVALIDARG;
break;
case CSIDL_Type_NonExistent:
hr = S_FALSE;
break;
case CSIDL_Type_WindowsPath:
GetWindowsDirectoryW(szTemp, MAX_PATH);
if (CSIDL_Data[folder].szDefaultPath &&
!IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
*CSIDL_Data[folder].szDefaultPath)
{ {
PathAddBackslashW(szTemp); /* Check if there's already a Wine-specific 'My Documents' folder */
strcatW(szTemp, CSIDL_Data[folder].szDefaultPath); strcpy(szPersonalTarget, pszHome);
} if (_SHAppendToUnixPath(szPersonalTarget, MAKEINTRESOURCEW(IDS_PERSONAL)) &&
hr = S_OK; !stat(szPersonalTarget, &statFolder) && S_ISDIR(statFolder.st_mode))
break;
case CSIDL_Type_SystemPath:
GetSystemDirectoryW(szTemp, MAX_PATH);
if (CSIDL_Data[folder].szDefaultPath &&
!IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
*CSIDL_Data[folder].szDefaultPath)
{ {
PathAddBackslashW(szTemp); /* '$HOME/My Documents' exists. Create subfolders for
strcatW(szTemp, CSIDL_Data[folder].szDefaultPath); * 'My Pictures', 'My Videos', 'My Music' etc. or fail silently
* if they already exist.
*/
for (i = 0; i < ARRAY_SIZE(aidsMyStuff); i++)
{
strcpy(szMyStuffTarget, szPersonalTarget);
if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])))
mkdir(szMyStuffTarget, 0777);
} }
hr = S_OK;
break; break;
case CSIDL_Type_SystemX86Path:
if (!GetSystemWow64DirectoryW(szTemp, MAX_PATH)) GetSystemDirectoryW(szTemp, MAX_PATH);
if (CSIDL_Data[folder].szDefaultPath &&
!IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
*CSIDL_Data[folder].szDefaultPath)
{
PathAddBackslashW(szTemp);
strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
} }
hr = S_OK;
/* Try to point to the XDG Documents folder */
if (xdg_results && xdg_results[num-2] &&
!stat(xdg_results[num-2], &statFolder) &&
S_ISDIR(statFolder.st_mode))
{
strcpy(szPersonalTarget, xdg_results[num-2]);
break; break;
case CSIDL_Type_CurrVer: }
hr = _SHGetCurrentVersionPath(dwFlags, folder, szTemp);
break; /* Or the hardcoded / OS X Documents folder */
case CSIDL_Type_User: strcpy(szPersonalTarget, pszHome);
hr = _SHGetUserProfilePath(hToken, dwFlags, folder, szTemp); if (_SHAppendToUnixPath(szPersonalTarget, DocumentsW) &&
break; !stat(szPersonalTarget, &statFolder) &&
case CSIDL_Type_AllUsers: S_ISDIR(statFolder.st_mode))
case CSIDL_Type_ProgramData:
hr = _SHGetAllUsersProfilePath(dwFlags, folder, szTemp);
break; break;
default:
FIXME("bogus type %d, please fix\n", type); /* As a last resort point to $HOME. */
hr = E_INVALIDARG; strcpy(szPersonalTarget, pszHome);
break; break;
} }
/* Expand environment strings if necessary */ /* Replace 'My Documents' directory with a symlink or fail silently if not empty. */
if (*szTemp == '%') remove(pszPersonal);
hr = _SHExpandEnvironmentStrings(szTemp, szBuildPath); symlink(szPersonalTarget, pszPersonal);
}
else else
strcpyW(szBuildPath, szTemp); {
/* '$HOME' doesn't exist. Create subdirs for 'My Pictures', 'My Videos',
* 'My Music' etc. in '%USERPROFILE%\My Documents' or fail silently if
* they already exist. */
pszHome = NULL;
strcpy(szPersonalTarget, pszPersonal);
for (i = 0; i < ARRAY_SIZE(aidsMyStuff); i++) {
strcpy(szMyStuffTarget, szPersonalTarget);
if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])))
mkdir(szMyStuffTarget, 0777);
}
}
if (FAILED(hr)) goto end; /* Create symbolic links for 'My Pictures', 'My Videos', 'My Music' etc. */
for (i=0; i < ARRAY_SIZE(aidsMyStuff); i++)
{
/* Create the current 'My Whatever' folder and get its unix path. */
hr = SHGetFolderPathW(NULL, acsidlMyStuff[i]|CSIDL_FLAG_CREATE, NULL,
SHGFP_TYPE_DEFAULT, wszTempPath);
if (FAILED(hr)) continue;
if(pszSubPath) { pszMyStuff = wine_get_unix_file_name(wszTempPath);
/* make sure the new path does not exceed the buffer length if (!pszMyStuff) continue;
* and remember to backslash and terminate it */
if(MAX_PATH < (lstrlenW(szBuildPath) + lstrlenW(pszSubPath) + 2)) { while (1)
hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE); {
goto end; /* Check for the Wine-specific '$HOME/My Documents' subfolder */
strcpy(szMyStuffTarget, szPersonalTarget);
if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])) &&
!stat(szMyStuffTarget, &statFolder) && S_ISDIR(statFolder.st_mode))
break;
/* Try the XDG_XXX_DIR folder */
if (xdg_results && xdg_results[i])
{
strcpy(szMyStuffTarget, xdg_results[i]);
break;
} }
PathAppendW(szBuildPath, pszSubPath);
PathRemoveBackslashW(szBuildPath); /* Or the OS X folder (these are never localized) */
if (pszHome)
{
strcpy(szMyStuffTarget, pszHome);
if (_SHAppendToUnixPath(szMyStuffTarget, MyOSXStuffW[i]) &&
!stat(szMyStuffTarget, &statFolder) &&
S_ISDIR(statFolder.st_mode))
break;
} }
/* Copy the path if it's available before we might return */
if (SUCCEEDED(hr) && pszPath)
strcpyW(pszPath, szBuildPath);
/* if we don't care about existing directories we are ready */ /* As a last resort point to the same location as 'My Documents' */
if(nFolder & CSIDL_FLAG_DONT_VERIFY) goto end; strcpy(szMyStuffTarget, szPersonalTarget);
break;
}
remove(pszMyStuff);
symlink(szMyStuffTarget, pszMyStuff);
heap_free(pszMyStuff);
}
if (PathFileExistsW(szBuildPath)) goto end; /* Last but not least, the Desktop folder */
if (pszHome)
strcpy(szDesktopTarget, pszHome);
else
strcpy(szDesktopTarget, pszPersonal);
heap_free(pszPersonal);
/* not existing but we are not allowed to create it. The return value xdg_desktop_dir = xdg_results ? xdg_results[num - 1] : NULL;
* is verified against shell32 version 6.0. if (xdg_desktop_dir ||
*/ (_SHAppendToUnixPath(szDesktopTarget, DesktopW) &&
if (!(nFolder & CSIDL_FLAG_CREATE)) !stat(szDesktopTarget, &statFolder) && S_ISDIR(statFolder.st_mode)))
{ {
hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); hr = SHGetFolderPathW(NULL, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, NULL,
goto end; SHGFP_TYPE_DEFAULT, wszTempPath);
if (SUCCEEDED(hr) && (pszDesktop = wine_get_unix_file_name(wszTempPath)))
{
remove(pszDesktop);
if (xdg_desktop_dir)
symlink(xdg_desktop_dir, pszDesktop);
else
symlink(szDesktopTarget, pszDesktop);
heap_free(pszDesktop);
}
} }
/* create directory/directories */ /* Free resources allocated by XDG_UserDirLookup() */
ret = SHCreateDirectoryExW(hwndOwner, szBuildPath, NULL); if (xdg_results)
if (ret && ret != ERROR_ALREADY_EXISTS)
{ {
ERR("Failed to create directory %s.\n", debugstr_w(szBuildPath)); for (i = 0; i < num; i++)
hr = E_FAIL; heap_free(xdg_results[i]);
goto end; heap_free(xdg_results);
} }
TRACE("Created missing system directory %s\n", debugstr_w(szBuildPath));
end:
TRACE("returning 0x%08x (final path is %s)\n", hr, debugstr_w(szBuildPath));
return hr;
} }
/************************************************************************* /******************************************************************************
* SHGetFolderPathA [SHELL32.@] * SHGetFolderPathW [SHELL32.@]
* *
* See SHGetFolderPathW. * Convert nFolder to path.
*
* RETURNS
* Success: S_OK
* Failure: standard HRESULT error codes.
*
* NOTES
* Most values can be overridden in either
* HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
* or in the same location in HKLM.
* The "Shell Folders" registry key was used in NT4 and earlier systems.
* Beginning with Windows 2000, the "User Shell Folders" key is used, so
* changes made to it are made to the former key too. This synchronization is
* done on-demand: not until someone requests the value of one of these paths
* (by calling one of the SHGet functions) is the value synchronized.
* Furthermore, the HKCU paths take precedence over the HKLM paths.
*/ */
HRESULT WINAPI SHGetFolderPathA( HRESULT WINAPI SHGetFolderPathW(
HWND hwndOwner, HWND hwndOwner, /* [I] owner window */
int nFolder, int nFolder, /* [I] CSIDL identifying the folder */
HANDLE hToken, HANDLE hToken, /* [I] access token */
DWORD dwFlags, DWORD dwFlags, /* [I] which path to return */
LPSTR pszPath) LPWSTR pszPath) /* [O] converted path */
{ {
WCHAR szTemp[MAX_PATH]; HRESULT hr = SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, NULL, pszPath);
HRESULT hr; if(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
TRACE("%p,%d,%p,%#x,%p\n", hwndOwner, nFolder, hToken, dwFlags, pszPath);
if (pszPath)
*pszPath = '\0';
hr = SHGetFolderPathW(hwndOwner, nFolder, hToken, dwFlags, szTemp);
if (SUCCEEDED(hr) && pszPath)
WideCharToMultiByte(CP_ACP, 0, szTemp, -1, pszPath, MAX_PATH, NULL,
NULL);
return hr; return hr;
} }
/* For each folder in folders, if its value has not been set in the registry, HRESULT WINAPI SHGetFolderPathAndSubDirA(
* calls _SHGetUserProfilePath or _SHGetAllUsersProfilePath (depending on the HWND hwndOwner, /* [I] owner window */
* folder's type) to get the unexpanded value first. int nFolder, /* [I] CSIDL identifying the folder */
* Writes the unexpanded value to User Shell Folders, and queries it with HANDLE hToken, /* [I] access token */
* SHGetFolderPathW to force the creation of the directory if it doesn't DWORD dwFlags, /* [I] which path to return */
* already exist. SHGetFolderPathW also returns the expanded value, which LPCSTR pszSubPath, /* [I] sub directory of the specified folder */
* this then writes to Shell Folders. LPSTR pszPath) /* [O] converted path */
*/
static HRESULT _SHRegisterFolders(HKEY hRootKey, HANDLE hToken,
LPCWSTR szUserShellFolderPath, LPCWSTR szShellFolderPath, const UINT folders[],
UINT foldersLen)
{ {
const WCHAR *szValueName; int length;
WCHAR buffer[40];
UINT i;
WCHAR path[MAX_PATH];
HRESULT hr = S_OK; HRESULT hr = S_OK;
HKEY hUserKey = NULL, hKey = NULL; LPWSTR pszSubPathW = NULL;
DWORD dwType, dwPathLen; LPWSTR pszPathW = NULL;
LONG ret;
TRACE("%p,%p,%s,%p,%u\n", hRootKey, hToken, TRACE("%p,%#x,%p,%#x,%s,%p\n", hwndOwner, nFolder, hToken, dwFlags, debugstr_a(pszSubPath), pszPath);
debugstr_w(szUserShellFolderPath), folders, foldersLen);
ret = RegCreateKeyW(hRootKey, szUserShellFolderPath, &hUserKey); if(pszPath) {
if (ret) pszPathW = heap_alloc(MAX_PATH * sizeof(WCHAR));
hr = HRESULT_FROM_WIN32(ret); if(!pszPathW) {
else hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
{ goto cleanup;
ret = RegCreateKeyW(hRootKey, szShellFolderPath, &hKey);
if (ret)
hr = HRESULT_FROM_WIN32(ret);
} }
for (i = 0; SUCCEEDED(hr) && i < foldersLen; i++) }
{ TRACE("%08x,%08x,%s\n",nFolder, dwFlags, debugstr_w(pszSubPathW));
dwPathLen = MAX_PATH * sizeof(WCHAR);
/* For CSIDL_Type_User we also use the GUID if no szValueName is provided */ /* SHGetFolderPathAndSubDirW does not distinguish if pszSubPath isn't
szValueName = CSIDL_Data[folders[i]].szValueName; * set (null), or an empty string.therefore call it without the parameter set
if (!szValueName && CSIDL_Data[folders[i]].type == CSIDL_Type_User) * if pszSubPath is an empty string
{ */
StringFromGUID2( CSIDL_Data[folders[i]].id, buffer, 39 ); if (pszSubPath && pszSubPath[0]) {
szValueName = &buffer[0]; length = MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, NULL, 0);
pszSubPathW = heap_alloc(length * sizeof(WCHAR));
if(!pszSubPathW) {
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
goto cleanup;
}
MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, pszSubPathW, length);
} }
if (RegQueryValueExW(hUserKey, szValueName, NULL, hr = SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, pszSubPathW, pszPathW);
&dwType, (LPBYTE)path, &dwPathLen) || (dwType != REG_SZ &&
dwType != REG_EXPAND_SZ)) if (SUCCEEDED(hr) && pszPath)
WideCharToMultiByte(CP_ACP, 0, pszPathW, -1, pszPath, MAX_PATH, NULL, NULL);
cleanup:
heap_free(pszPathW);
heap_free(pszSubPathW);
return hr;
}
/*************************************************************************
* SHGetFolderPathAndSubDirW [SHELL32.@]
*/
HRESULT WINAPI SHGetFolderPathAndSubDirW(
HWND hwndOwner, /* [I] owner window */
int nFolder, /* [I] CSIDL identifying the folder */
HANDLE hToken, /* [I] access token */
DWORD dwFlags, /* [I] which path to return */
LPCWSTR pszSubPath,/* [I] sub directory of the specified folder */
LPWSTR pszPath) /* [O] converted path */
{
HRESULT hr;
WCHAR szBuildPath[MAX_PATH], szTemp[MAX_PATH];
DWORD folder = nFolder & CSIDL_FOLDER_MASK;
CSIDL_Type type;
int ret;
TRACE("%p,%#x,%p,%#x,%s,%p\n", hwndOwner, nFolder, hToken, dwFlags, debugstr_w(pszSubPath), pszPath);
/* Windows always NULL-terminates the resulting path regardless of success
* or failure, so do so first
*/
if (pszPath)
*pszPath = '\0';
if (folder >= ARRAY_SIZE(CSIDL_Data))
return E_INVALIDARG;
if ((SHGFP_TYPE_CURRENT != dwFlags) && (SHGFP_TYPE_DEFAULT != dwFlags))
return E_INVALIDARG;
szTemp[0] = 0;
type = CSIDL_Data[folder].type;
switch (type)
{
case CSIDL_Type_Disallowed:
hr = E_INVALIDARG;
break;
case CSIDL_Type_NonExistent:
hr = S_FALSE;
break;
case CSIDL_Type_WindowsPath:
GetWindowsDirectoryW(szTemp, MAX_PATH);
if (CSIDL_Data[folder].szDefaultPath &&
!IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
*CSIDL_Data[folder].szDefaultPath)
{
PathAddBackslashW(szTemp);
strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
}
hr = S_OK;
break;
case CSIDL_Type_SystemPath:
GetSystemDirectoryW(szTemp, MAX_PATH);
if (CSIDL_Data[folder].szDefaultPath &&
!IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
*CSIDL_Data[folder].szDefaultPath)
{
PathAddBackslashW(szTemp);
strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
}
hr = S_OK;
break;
case CSIDL_Type_SystemX86Path:
if (!GetSystemWow64DirectoryW(szTemp, MAX_PATH)) GetSystemDirectoryW(szTemp, MAX_PATH);
if (CSIDL_Data[folder].szDefaultPath &&
!IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
*CSIDL_Data[folder].szDefaultPath)
{
PathAddBackslashW(szTemp);
strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
}
hr = S_OK;
break;
case CSIDL_Type_CurrVer:
hr = _SHGetCurrentVersionPath(dwFlags, folder, szTemp);
break;
case CSIDL_Type_User:
hr = _SHGetUserProfilePath(hToken, dwFlags, folder, szTemp);
break;
case CSIDL_Type_AllUsers:
case CSIDL_Type_ProgramData:
hr = _SHGetAllUsersProfilePath(dwFlags, folder, szTemp);
break;
default:
FIXME("bogus type %d, please fix\n", type);
hr = E_INVALIDARG;
break;
}
/* Expand environment strings if necessary */
if (*szTemp == '%')
hr = _SHExpandEnvironmentStrings(szTemp, szBuildPath);
else
strcpyW(szBuildPath, szTemp);
if (FAILED(hr)) goto end;
if(pszSubPath) {
/* make sure the new path does not exceed the buffer length
* and remember to backslash and terminate it */
if(MAX_PATH < (lstrlenW(szBuildPath) + lstrlenW(pszSubPath) + 2)) {
hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
goto end;
}
PathAppendW(szBuildPath, pszSubPath);
PathRemoveBackslashW(szBuildPath);
}
/* Copy the path if it's available before we might return */
if (SUCCEEDED(hr) && pszPath)
strcpyW(pszPath, szBuildPath);
/* if we don't care about existing directories we are ready */
if(nFolder & CSIDL_FLAG_DONT_VERIFY) goto end;
if (PathFileExistsW(szBuildPath)) goto end;
/* not existing but we are not allowed to create it. The return value
* is verified against shell32 version 6.0.
*/
if (!(nFolder & CSIDL_FLAG_CREATE))
{
hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
goto end;
}
/* create directory/directories */
ret = SHCreateDirectoryExW(hwndOwner, szBuildPath, NULL);
if (ret && ret != ERROR_ALREADY_EXISTS)
{
ERR("Failed to create directory %s.\n", debugstr_w(szBuildPath));
hr = E_FAIL;
goto end;
}
TRACE("Created missing system directory %s\n", debugstr_w(szBuildPath));
end:
TRACE("returning 0x%08x (final path is %s)\n", hr, debugstr_w(szBuildPath));
return hr;
}
/*************************************************************************
* SHGetFolderPathA [SHELL32.@]
*
* See SHGetFolderPathW.
*/
HRESULT WINAPI SHGetFolderPathA(
HWND hwndOwner,
int nFolder,
HANDLE hToken,
DWORD dwFlags,
LPSTR pszPath)
{
WCHAR szTemp[MAX_PATH];
HRESULT hr;
TRACE("%p,%d,%p,%#x,%p\n", hwndOwner, nFolder, hToken, dwFlags, pszPath);
if (pszPath)
*pszPath = '\0';
hr = SHGetFolderPathW(hwndOwner, nFolder, hToken, dwFlags, szTemp);
if (SUCCEEDED(hr) && pszPath)
WideCharToMultiByte(CP_ACP, 0, szTemp, -1, pszPath, MAX_PATH, NULL,
NULL);
return hr;
}
/* For each folder in folders, if its value has not been set in the registry,
* calls _SHGetUserProfilePath or _SHGetAllUsersProfilePath (depending on the
* folder's type) to get the unexpanded value first.
* 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, LPCWSTR szShellFolderPath, const UINT folders[],
UINT foldersLen)
{
const WCHAR *szValueName;
WCHAR buffer[40];
UINT i;
WCHAR path[MAX_PATH];
HRESULT hr = S_OK;
HKEY hUserKey = NULL, hKey = NULL;
DWORD dwType, dwPathLen;
LONG ret;
TRACE("%p,%p,%s,%p,%u\n", hRootKey, hToken,
debugstr_w(szUserShellFolderPath), folders, foldersLen);
ret = RegCreateKeyW(hRootKey, szUserShellFolderPath, &hUserKey);
if (ret)
hr = HRESULT_FROM_WIN32(ret);
else
{
ret = RegCreateKeyW(hRootKey, szShellFolderPath, &hKey);
if (ret)
hr = HRESULT_FROM_WIN32(ret);
}
for (i = 0; SUCCEEDED(hr) && i < foldersLen; i++)
{
dwPathLen = MAX_PATH * sizeof(WCHAR);
/* For CSIDL_Type_User we also use the GUID if no szValueName is provided */
szValueName = CSIDL_Data[folders[i]].szValueName;
if (!szValueName && CSIDL_Data[folders[i]].type == CSIDL_Type_User)
{
StringFromGUID2( CSIDL_Data[folders[i]].id, buffer, 39 );
szValueName = &buffer[0];
}
if (RegQueryValueExW(hUserKey, szValueName, NULL,
&dwType, (LPBYTE)path, &dwPathLen) || (dwType != REG_SZ &&
dwType != REG_EXPAND_SZ))
{ {
*path = '\0'; *path = '\0';
if (CSIDL_Data[folders[i]].type == CSIDL_Type_User) if (CSIDL_Data[folders[i]].type == CSIDL_Type_User)
...@@ -4382,271 +4647,6 @@ static HRESULT _SHRegisterCommonShellFolders(void) ...@@ -4382,271 +4647,6 @@ static HRESULT _SHRegisterCommonShellFolders(void)
} }
/****************************************************************************** /******************************************************************************
* _SHAppendToUnixPath [Internal]
*
* Helper function for _SHCreateSymbolicLinks. Appends pwszSubPath (or the
* corresponding resource, if IS_INTRESOURCE) to the unix base path 'szBasePath'
* and replaces backslashes with slashes.
*
* PARAMS
* szBasePath [IO] The unix base path, which will be appended to (CP_UNXICP).
* pwszSubPath [I] Sub-path or resource id (use MAKEINTRESOURCEW).
*
* RETURNS
* Success: TRUE,
* Failure: FALSE
*/
static inline BOOL _SHAppendToUnixPath(char *szBasePath, LPCWSTR pwszSubPath) {
WCHAR wszSubPath[MAX_PATH];
int cLen = strlen(szBasePath);
char *pBackslash;
if (IS_INTRESOURCE(pwszSubPath)) {
if (!LoadStringW(shell32_hInstance, LOWORD(pwszSubPath), wszSubPath, MAX_PATH)) {
/* Fall back to hard coded defaults. */
switch (LOWORD(pwszSubPath)) {
case IDS_PERSONAL:
lstrcpyW(wszSubPath, DocumentsW);
break;
case IDS_MYMUSIC:
lstrcpyW(wszSubPath, My_MusicW);
break;
case IDS_MYPICTURES:
lstrcpyW(wszSubPath, My_PicturesW);
break;
case IDS_MYVIDEOS:
lstrcpyW(wszSubPath, My_VideosW);
break;
case IDS_DOWNLOADS:
lstrcpyW(wszSubPath, DownloadsW);
break;
case IDS_TEMPLATES:
lstrcpyW(wszSubPath, TemplatesW);
break;
default:
ERR("LoadString(%d) failed!\n", LOWORD(pwszSubPath));
return FALSE;
}
}
} else {
lstrcpyW(wszSubPath, pwszSubPath);
}
if (szBasePath[cLen-1] != '/') szBasePath[cLen++] = '/';
if (!WideCharToMultiByte(CP_UNIXCP, 0, wszSubPath, -1, szBasePath + cLen,
FILENAME_MAX - cLen, NULL, NULL))
{
return FALSE;
}
pBackslash = szBasePath + cLen;
while ((pBackslash = strchr(pBackslash, '\\'))) *pBackslash = '/';
return TRUE;
}
/******************************************************************************
* _SHCreateSymbolicLinks [Internal]
*
* Sets up symbol links for various shell folders to point into the user's home
* directory. We do an educated guess about what the user would probably want:
* - If there is a 'My Documents' directory in $HOME, the user probably wants
* wine's 'My Documents' to point there. Furthermore, we infer that the user
* is a Windows lover and has no problem with wine creating subfolders for
* 'My Pictures', 'My Music', 'My Videos' etc. under '$HOME/My Documents', if
* those do not already exist. We put appropriate symbolic links in place for
* those, too.
* - If there is no 'My Documents' directory in $HOME, we let 'My Documents'
* point directly to $HOME. We assume the user to be a unix hacker who does not
* want wine to create anything anywhere besides the .wine directory. So, if
* there already is a 'My Music' directory in $HOME, we symlink the 'My Music'
* shell folder to it. But if not, then we check XDG_MUSIC_DIR - "well known"
* directory, and try to link to that. If that fails, then we symlink to
* $HOME directly. The same holds for 'My Pictures', 'My Videos' etc.
* - The Desktop shell folder is symlinked to XDG_DESKTOP_DIR. If that does not
* exist, then we try '$HOME/Desktop'. If that does not exist, then we leave
* it alone.
* ('My Music',... above in fact means LoadString(IDS_MYMUSIC))
*/
static void _SHCreateSymbolicLinks(void)
{
static const UINT aidsMyStuff[] = {
IDS_MYPICTURES, IDS_MYVIDEOS, IDS_MYMUSIC, IDS_DOWNLOADS, IDS_TEMPLATES
};
static const WCHAR * const MyOSXStuffW[] = {
PicturesW, MoviesW, MusicW, DownloadsW, TemplatesW
};
static const int acsidlMyStuff[] = {
CSIDL_MYPICTURES, CSIDL_MYVIDEO, CSIDL_MYMUSIC, CSIDL_DOWNLOADS, CSIDL_TEMPLATES
};
static const char * const xdg_dirs[] = {
"PICTURES", "VIDEOS", "MUSIC", "DOWNLOAD", "TEMPLATES", "DOCUMENTS", "DESKTOP"
};
static const unsigned int num = ARRAY_SIZE(xdg_dirs);
WCHAR wszTempPath[MAX_PATH];
char szPersonalTarget[FILENAME_MAX], *pszPersonal;
char szMyStuffTarget[FILENAME_MAX], *pszMyStuff;
char szDesktopTarget[FILENAME_MAX], *pszDesktop;
struct stat statFolder;
const char *pszHome;
HRESULT hr;
char ** xdg_results;
char * xdg_desktop_dir;
UINT i;
/* Create all necessary profile sub-dirs up to 'My Documents' and get the unix path. */
hr = SHGetFolderPathW(NULL, CSIDL_PERSONAL|CSIDL_FLAG_CREATE, NULL,
SHGFP_TYPE_DEFAULT, wszTempPath);
if (FAILED(hr)) return;
pszPersonal = wine_get_unix_file_name(wszTempPath);
if (!pszPersonal) return;
hr = XDG_UserDirLookup(xdg_dirs, num, &xdg_results);
if (FAILED(hr)) xdg_results = NULL;
pszHome = getenv("HOME");
if (pszHome && !stat(pszHome, &statFolder) && S_ISDIR(statFolder.st_mode))
{
while (1)
{
/* Check if there's already a Wine-specific 'My Documents' folder */
strcpy(szPersonalTarget, pszHome);
if (_SHAppendToUnixPath(szPersonalTarget, MAKEINTRESOURCEW(IDS_PERSONAL)) &&
!stat(szPersonalTarget, &statFolder) && S_ISDIR(statFolder.st_mode))
{
/* '$HOME/My Documents' exists. Create subfolders for
* 'My Pictures', 'My Videos', 'My Music' etc. or fail silently
* if they already exist.
*/
for (i = 0; i < ARRAY_SIZE(aidsMyStuff); i++)
{
strcpy(szMyStuffTarget, szPersonalTarget);
if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])))
mkdir(szMyStuffTarget, 0777);
}
break;
}
/* Try to point to the XDG Documents folder */
if (xdg_results && xdg_results[num-2] &&
!stat(xdg_results[num-2], &statFolder) &&
S_ISDIR(statFolder.st_mode))
{
strcpy(szPersonalTarget, xdg_results[num-2]);
break;
}
/* Or the hardcoded / OS X Documents folder */
strcpy(szPersonalTarget, pszHome);
if (_SHAppendToUnixPath(szPersonalTarget, DocumentsW) &&
!stat(szPersonalTarget, &statFolder) &&
S_ISDIR(statFolder.st_mode))
break;
/* As a last resort point to $HOME. */
strcpy(szPersonalTarget, pszHome);
break;
}
/* Replace 'My Documents' directory with a symlink or fail silently if not empty. */
remove(pszPersonal);
symlink(szPersonalTarget, pszPersonal);
}
else
{
/* '$HOME' doesn't exist. Create subdirs for 'My Pictures', 'My Videos',
* 'My Music' etc. in '%USERPROFILE%\My Documents' or fail silently if
* they already exist. */
pszHome = NULL;
strcpy(szPersonalTarget, pszPersonal);
for (i = 0; i < ARRAY_SIZE(aidsMyStuff); i++) {
strcpy(szMyStuffTarget, szPersonalTarget);
if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])))
mkdir(szMyStuffTarget, 0777);
}
}
/* Create symbolic links for 'My Pictures', 'My Videos', 'My Music' etc. */
for (i=0; i < ARRAY_SIZE(aidsMyStuff); i++)
{
/* Create the current 'My Whatever' folder and get its unix path. */
hr = SHGetFolderPathW(NULL, acsidlMyStuff[i]|CSIDL_FLAG_CREATE, NULL,
SHGFP_TYPE_DEFAULT, wszTempPath);
if (FAILED(hr)) continue;
pszMyStuff = wine_get_unix_file_name(wszTempPath);
if (!pszMyStuff) continue;
while (1)
{
/* Check for the Wine-specific '$HOME/My Documents' subfolder */
strcpy(szMyStuffTarget, szPersonalTarget);
if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])) &&
!stat(szMyStuffTarget, &statFolder) && S_ISDIR(statFolder.st_mode))
break;
/* Try the XDG_XXX_DIR folder */
if (xdg_results && xdg_results[i])
{
strcpy(szMyStuffTarget, xdg_results[i]);
break;
}
/* Or the OS X folder (these are never localized) */
if (pszHome)
{
strcpy(szMyStuffTarget, pszHome);
if (_SHAppendToUnixPath(szMyStuffTarget, MyOSXStuffW[i]) &&
!stat(szMyStuffTarget, &statFolder) &&
S_ISDIR(statFolder.st_mode))
break;
}
/* As a last resort point to the same location as 'My Documents' */
strcpy(szMyStuffTarget, szPersonalTarget);
break;
}
remove(pszMyStuff);
symlink(szMyStuffTarget, pszMyStuff);
heap_free(pszMyStuff);
}
/* Last but not least, the Desktop folder */
if (pszHome)
strcpy(szDesktopTarget, pszHome);
else
strcpy(szDesktopTarget, pszPersonal);
heap_free(pszPersonal);
xdg_desktop_dir = xdg_results ? xdg_results[num - 1] : NULL;
if (xdg_desktop_dir ||
(_SHAppendToUnixPath(szDesktopTarget, DesktopW) &&
!stat(szDesktopTarget, &statFolder) && S_ISDIR(statFolder.st_mode)))
{
hr = SHGetFolderPathW(NULL, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, NULL,
SHGFP_TYPE_DEFAULT, wszTempPath);
if (SUCCEEDED(hr) && (pszDesktop = wine_get_unix_file_name(wszTempPath)))
{
remove(pszDesktop);
if (xdg_desktop_dir)
symlink(xdg_desktop_dir, pszDesktop);
else
symlink(szDesktopTarget, pszDesktop);
heap_free(pszDesktop);
}
}
/* Free resources allocated by XDG_UserDirLookup() */
if (xdg_results)
{
for (i = 0; i < num; i++)
heap_free(xdg_results[i]);
heap_free(xdg_results);
}
}
/******************************************************************************
* create_extra_folders [Internal] * create_extra_folders [Internal]
* *
* Create some extra folders that don't have a standard CSIDL definition. * Create some extra folders that don't have a standard CSIDL definition.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment