xref: /reactos/dll/win32/shell32/wine/shellpath.c (revision 06024c97)
1 /*
2  * Path Functions
3  *
4  * Copyright 1998, 1999, 2000 Juergen Schmied
5  * Copyright 2004 Juan Lang
6  * Copyright 2018-2022 Katayama Hirofumi MZ
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  *
22  * NOTES:
23  *
24  * Many of these functions are in SHLWAPI.DLL also
25  *
26  */
27 
28 #define WIN32_NO_STATUS
29 #define _INC_WINDOWS
30 #define COBJMACROS
31 
32 #include <wine/config.h>
33 
34 #include <windef.h>
35 #include <winbase.h>
36 #include <shlobj.h>
37 #include <undocshell.h>
38 #include <shlwapi.h>
39 #include <sddl.h>
40 #include <strsafe.h>
41 #include <wine/debug.h>
42 #include <wine/unicode.h>
43 #include <assert.h>
44 
45 #include <shlwapi_undoc.h>
46 #include <shellutils.h>
47 
48 #include <userenv.h>
49 
50 #include "pidl.h"
51 #include "shell32_main.h"
52 #include "shresdef.h"
53 
54 #undef _WIN32_WINNT
55 #define _WIN32_WINNT _WIN32_WINNT_WS03
56 
57 WINE_DEFAULT_DEBUG_CHANNEL(shell);
58 
59 static const BOOL is_win64 = sizeof(void *) > sizeof(int);
60 
61 /* FIXME: Remove this */
62 typedef enum _NT_PRODUCT_TYPE
63 {
64     NtProductWinNt = 1,
65     NtProductLanManNt,
66     NtProductServer
67 } NT_PRODUCT_TYPE, *PNT_PRODUCT_TYPE;
68 
69 /* FIXME: We cannot refresh the RtlGetNtProductType value before reboot. */
70 static BOOL
DoGetProductType(PNT_PRODUCT_TYPE ProductType)71 DoGetProductType(PNT_PRODUCT_TYPE ProductType)
72 {
73     HKEY hKey;
74     LONG error;
75     WCHAR szValue[9];
76     DWORD cbValue;
77     static DWORD s_dwProductType = 0;
78 
79     if (s_dwProductType != 0)
80     {
81         *ProductType = s_dwProductType;
82         return TRUE;
83     }
84 
85     *ProductType = NtProductServer;
86 
87     error = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions", 0, KEY_READ, &hKey);
88     if (error)
89         return FALSE;
90 
91     cbValue = sizeof(szValue);
92     error = RegGetValueW(hKey, NULL, L"ProductType", RRF_RT_REG_SZ, NULL, (PVOID)szValue, &cbValue);
93     if (!error)
94     {
95         if (lstrcmpW(szValue, L"WinNT") == 0)
96             *ProductType = NtProductWinNt;
97         else if (lstrcmpW(szValue, L"LanmanNT") == 0)
98             *ProductType = NtProductLanManNt;
99     }
100 
101     s_dwProductType = *ProductType;
102 
103     RegCloseKey(hKey);
104     return TRUE;
105 }
106 
IsRemovableDrive(DWORD iDrive)107 BOOL APIENTRY IsRemovableDrive(DWORD iDrive)
108 {
109     WCHAR szRoot[] = L"C:\\";
110     assert(L'A' + iDrive <= L'Z');
111     szRoot[0] = (WCHAR)(L'A' + iDrive);
112     return GetDriveTypeW(szRoot) == DRIVE_REMOVABLE;
113 }
114 
115 /*
116 	########## Combining and Constructing paths ##########
117 */
118 
119 static BOOL WINAPI
PathSearchOnExtensionsW(_Inout_ LPWSTR pszPath,_In_opt_ LPCWSTR * ppszDirs,_In_ BOOL bDoSearch,_In_ DWORD dwWhich)120 PathSearchOnExtensionsW(
121     _Inout_ LPWSTR pszPath,
122     _In_opt_ LPCWSTR *ppszDirs,
123     _In_ BOOL bDoSearch,
124     _In_ DWORD dwWhich)
125 {
126     if (*PathFindExtensionW(pszPath) != 0)
127         return FALSE;
128 
129     if (bDoSearch)
130         return PathFindOnPathExW(pszPath, ppszDirs, dwWhich);
131     else
132         return PathFileExistsDefExtW(pszPath, dwWhich);
133 }
134 
135 #if (_WIN32_WINNT >= _WIN32_WINNT_WS03)
PathIsAbsoluteW(_In_ LPCWSTR path)136 static BOOL WINAPI PathIsAbsoluteW(_In_ LPCWSTR path)
137 {
138     return PathIsUNCW(path) || (PathGetDriveNumberW(path) != -1 && path[2] == L'\\');
139 }
140 
PathMakeAbsoluteW(_Inout_ LPWSTR path)141 static BOOL WINAPI PathMakeAbsoluteW(_Inout_ LPWSTR path)
142 {
143     WCHAR path1[MAX_PATH];
144     DWORD cch;
145 
146     if (path == NULL)
147         return FALSE;
148     cch = GetCurrentDirectoryW(_countof(path1), path1);
149     if (!cch || cch > _countof(path1))
150         return FALSE;
151     return (PathCombineW(path, path1, path) != NULL);
152 }
153 #endif
154 
155 BOOL WINAPI IsLFNDriveW(LPCWSTR lpszPath);
156 
157 static VOID WINAPI
PathQualifyExW(_Inout_ LPWSTR pszPath,_Inout_opt_ LPCWSTR pszDir,_In_ DWORD dwFlags)158 PathQualifyExW(_Inout_ LPWSTR pszPath, _Inout_opt_ LPCWSTR pszDir, _In_ DWORD dwFlags)
159 {
160     INT iDrive, cchPathLeft;
161     WCHAR szTemp[MAX_PATH], szRoot[MAX_PATH];
162     PWCHAR pchTemp = szTemp, pchPath;
163     BOOL bLFN;
164 
165     TRACE("(%s,%s,0x%08x)\n", debugstr_w(pszPath), debugstr_w(pszDir), dwFlags);
166 
167     /* Save pszPath path into szTemp for rebuilding the path later */
168     if (FAILED(StringCchCopyW(szTemp, _countof(szTemp), pszPath)))
169         return;
170 
171     /* Replace every '/' by '\' */
172     FixSlashesAndColonW(szTemp);
173 
174     cchPathLeft = MAX_PATH;
175 
176     /* Build the root-like path on pszPath, and set pchTemp */
177     if (!PathIsUNCW(szTemp))
178     {
179         /*
180          * Non-UNC path.
181          * Determine and normalize the root drive.
182          */
183         iDrive = PathGetDriveNumberW(szTemp);
184         if (iDrive == -1)
185         {
186             /*
187              * No drive was specified in the path. Try to find one from the
188              * optional directory (if this fails, fall back to the one of the
189              * Windows directory).
190              */
191             if (!pszDir || FAILED(StringCchCopyW(szRoot, _countof(szRoot), pszDir)))
192             {
193                 /* pszDir was invalid or NULL. Fall back to the
194                  * Windows directory and find its root. */
195                 szRoot[0] = UNICODE_NULL;
196                 GetWindowsDirectoryW(szRoot, _countof(szRoot));
197                 iDrive = PathGetDriveNumberW(szRoot);
198                 if (iDrive != -1)
199                     PathBuildRootW(szRoot, iDrive);
200             }
201 
202             if (szTemp[0] == L'\\')
203             {
204                 PathStripToRootW(szRoot);
205             }
206         }
207         else
208         {
209             /*
210              * A drive is specified in the path, that can be either of the
211              * form 'C:\xxx' or of the form 'C:xxx' (relative path). Isolate
212              * the root part 'C:' and the rest of the path 'xxx' in pchTemp.
213              */
214             PathBuildRootW(szRoot, iDrive);
215             pchTemp += 2;
216             if (*pchTemp == L'\\')
217                 ++pchTemp;
218         }
219 
220         bLFN = IsLFNDriveW(szRoot);
221         if (!bLFN)
222         {
223             PWCHAR pch;
224             for (pch = pchTemp; *pch != UNICODE_NULL; ++pch)
225             {
226 #define VALID_SHORT_PATH_CHAR_CLASSES ( \
227     PATH_CHAR_CLASS_DOT | \
228     PATH_CHAR_CLASS_BACKSLASH | \
229     PATH_CHAR_CLASS_COLON | \
230     PATH_CHAR_CLASS_OTHER_VALID \
231 )
232                 if (!PathIsValidCharW(*pch, VALID_SHORT_PATH_CHAR_CLASSES))
233                 {
234                     *pch = L'_';
235                 }
236             }
237         }
238 
239         StringCchCopyW(pszPath, MAX_PATH, szRoot);
240         cchPathLeft -= lstrlenW(pszPath) + 1;
241     }
242     else /* UNC path: Begins with double backslash */
243     {
244         bLFN = IsLFNDriveW(pchTemp);
245         if (bLFN)
246         {
247             pszPath[2] = UNICODE_NULL; /* Cut off */
248             cchPathLeft -= (2 + 1);
249             pchTemp += 2;
250         }
251         else
252         {
253             PWCHAR pchSlash = StrChrW(pszPath + 2, L'\\');
254             if (pchSlash)
255                 pchSlash = StrChrW(pchSlash + 1, L'\\');
256 
257             if (pchSlash)
258             {
259                 *(pchSlash + 1) = UNICODE_NULL; /* Cut off */
260                 pchTemp += pchSlash - pszPath;
261                 cchPathLeft -= (INT)(SIZE_T)(pchSlash - pszPath) + 1;
262             }
263             else
264             {
265                 bLFN = TRUE;
266                 pszPath[2] = UNICODE_NULL; /* Cut off */
267                 cchPathLeft -= 2;
268                 pchTemp += 2;
269             }
270         }
271     }
272     /* Now pszPath is a root-like path or an empty string. */
273 
274     /* Start appending the path components of szTemp to pszPath. */
275     while (*pchTemp && cchPathLeft > 0)
276     {
277         /* Collapse any .\ and ..\ parts in the path */
278         if (*pchTemp == L'.')
279         {
280             BOOL bDots = FALSE; /* '.' or '..' ? */
281 
282             if (pchTemp[1] == UNICODE_NULL || pchTemp[1] == L'\\')
283             {
284                 /* Component '.' */
285                 bDots = TRUE;
286             }
287             else if (pchTemp[1] == L'.' && (pchTemp[2] == UNICODE_NULL || pchTemp[2] == L'\\'))
288             {
289                 /* Component '..' */
290                 PathRemoveFileSpecW(pszPath); /* Remove the last component from pszPath */
291                 bDots = TRUE;
292             }
293 
294             /* If a '.' or '..' was encountered, skip to the next component */
295             if (bDots)
296             {
297                 while (*pchTemp && *pchTemp != L'\\')
298                 {
299                     ++pchTemp;
300                 }
301 
302                 while (*pchTemp == L'\\')
303                 {
304                     ++pchTemp;
305                 }
306 
307                 continue;
308             }
309         }
310 
311         /* Otherwise, copy the other path component */
312 
313         if (!PathAddBackslashW(pszPath)) /* Append a backslash at the end */
314             break;
315 
316         --cchPathLeft;
317 
318         pchPath = &pszPath[lstrlenW(pszPath)];
319 
320         if (!bLFN) /* Not LFN? */
321         {
322             /* Copy MS-DOS 8.3 filename */
323             PWCHAR pchDot = NULL;
324             INT ich;
325             WCHAR szTitle[8 + 1] = L"";
326             INT cchTitle = 0;
327             WCHAR szDotExtension[1 + 3 + 1] = L"";
328             INT cchDotExtension = 0;
329 
330             /* Copy the component to szTitle and szDotExtension... */
331             while (*pchTemp && *pchTemp != L'\\')
332             {
333                 if (*pchTemp == L'.')
334                 {
335                     pchDot = pchTemp; /* Remember the last position */
336 
337                     /* Clear szDotExtension */
338                     cchDotExtension = 0;
339                     ZeroMemory(szDotExtension, sizeof(szDotExtension));
340                 }
341 
342                 if (pchDot)
343                 {
344                     if (cchDotExtension < 1 + 3)
345                         szDotExtension[cchDotExtension++] = *pchTemp;
346                 }
347                 else
348                 {
349                     if (cchTitle < 8)
350                         szTitle[cchTitle++] = *pchTemp;
351                 }
352 
353                 ++pchTemp;
354             }
355 
356             /* Add file title 'szTitle' to pchPath */
357             for (ich = 0; szTitle[ich] && cchPathLeft > 0; ++ich)
358             {
359                 *pchPath++ = szTitle[ich];
360                 --cchPathLeft;
361             }
362 
363             /* Add file extension 'szDotExtension' to pchPath */
364             if (pchDot)
365             {
366                 for (ich = 0; szDotExtension[ich] && cchPathLeft > 0; ++ich)
367                 {
368                     *pchPath++ = szDotExtension[ich];
369                     --cchPathLeft;
370                 }
371             }
372         }
373         else /* LFN */
374         {
375             /* Copy the component up to the next separator */
376             while (*pchTemp != UNICODE_NULL && *pchTemp != L'\\' && cchPathLeft > 0)
377             {
378                 *pchPath++ = *pchTemp++;
379                 --cchPathLeft;
380             }
381         }
382 
383         /* Skip the backslashes */
384         while (*pchTemp == L'\\')
385         {
386             ++pchTemp;
387         }
388 
389         /* Keep null-terminated */
390         *pchPath = UNICODE_NULL;
391     }
392 
393     /* Remove any trailing backslash */
394     PathRemoveBackslashW(pszPath);
395 
396     if (!(dwFlags & 1)) /* Remove the trailing dot? */
397     {
398         pchPath = CharPrevW(pszPath, pszPath + lstrlenW(pszPath));
399         if (*pchPath == L'.')
400             *pchPath = UNICODE_NULL;
401     }
402 }
403 
404 /*************************************************************************
405  * PathAppend		[SHELL32.36]
406  */
PathAppendAW(LPVOID lpszPath1,LPCVOID lpszPath2)407 BOOL WINAPI PathAppendAW(
408 	LPVOID lpszPath1,
409 	LPCVOID lpszPath2)
410 {
411 	if (SHELL_OsIsUnicode())
412 	  return PathAppendW(lpszPath1, lpszPath2);
413 	return PathAppendA(lpszPath1, lpszPath2);
414 }
415 
416 /*************************************************************************
417  * PathGetExtensionA		[internal]
418  *
419  * NOTES
420  *  exported by ordinal
421  *  return value points to the first char after the dot
422  */
PathGetExtensionA(LPCSTR lpszPath)423 static LPSTR PathGetExtensionA(LPCSTR lpszPath)
424 {
425 	TRACE("(%s)\n",lpszPath);
426 
427 	lpszPath = PathFindExtensionA(lpszPath);
428 	return (LPSTR)(*lpszPath?(lpszPath+1):lpszPath);
429 }
430 
431 /*************************************************************************
432  * PathGetExtensionW		[internal]
433  */
PathGetExtensionW(LPCWSTR lpszPath)434 static LPWSTR PathGetExtensionW(LPCWSTR lpszPath)
435 {
436 	TRACE("(%s)\n",debugstr_w(lpszPath));
437 
438 	lpszPath = PathFindExtensionW(lpszPath);
439 	return (LPWSTR)(*lpszPath?(lpszPath+1):lpszPath);
440 }
441 
442 /*************************************************************************
443  * SHPathGetExtension        [SHELL32.158]
444  */
SHPathGetExtensionW(LPCWSTR lpszPath,DWORD void1,DWORD void2)445 LPVOID WINAPI SHPathGetExtensionW(LPCWSTR lpszPath, DWORD void1, DWORD void2)
446 {
447     return PathGetExtensionW(lpszPath);
448 }
449 
450 /*************************************************************************
451  * PathRemoveFileSpec [SHELL32.35]
452  */
PathRemoveFileSpecAW(LPVOID lpszPath)453 BOOL WINAPI PathRemoveFileSpecAW(LPVOID lpszPath)
454 {
455 	if (SHELL_OsIsUnicode())
456 	  return PathRemoveFileSpecW(lpszPath);
457 	return PathRemoveFileSpecA(lpszPath);
458 }
459 
460 /*
461 	Path Manipulations
462 */
463 
464 /*************************************************************************
465  * PathGetShortPathA [internal]
466  */
PathGetShortPathA(LPSTR pszPath)467 static void PathGetShortPathA(LPSTR pszPath)
468 {
469 	CHAR path[MAX_PATH];
470 
471 	TRACE("%s\n", pszPath);
472 
473 	if (GetShortPathNameA(pszPath, path, MAX_PATH))
474 	{
475 	  lstrcpyA(pszPath, path);
476 	}
477 }
478 
479 /*************************************************************************
480  * PathGetShortPathW [internal]
481  */
PathGetShortPathW(LPWSTR pszPath)482 static void PathGetShortPathW(LPWSTR pszPath)
483 {
484 	WCHAR path[MAX_PATH];
485 
486 	TRACE("%s\n", debugstr_w(pszPath));
487 
488 	if (GetShortPathNameW(pszPath, path, MAX_PATH))
489 	{
490 	  lstrcpyW(pszPath, path);
491 	}
492 }
493 
494 /*************************************************************************
495  * PathGetShortPath [SHELL32.92]
496  */
PathGetShortPathAW(LPVOID pszPath)497 VOID WINAPI PathGetShortPathAW(LPVOID pszPath)
498 {
499 	if(SHELL_OsIsUnicode())
500 	  PathGetShortPathW(pszPath);
501 	PathGetShortPathA(pszPath);
502 }
503 
504 /*
505 	########## Path Testing ##########
506 */
507 
508 /*************************************************************************
509  * PathIsRoot		[SHELL32.29]
510  */
PathIsRootAW(LPCVOID lpszPath)511 BOOL WINAPI PathIsRootAW(LPCVOID lpszPath)
512 {
513 	if (SHELL_OsIsUnicode())
514 	  return PathIsRootW(lpszPath);
515 	return PathIsRootA(lpszPath);
516 }
517 
518 /*************************************************************************
519  *  PathIsExeA		[internal]
520  */
PathIsExeA(LPCSTR lpszPath)521 static BOOL PathIsExeA (LPCSTR lpszPath)
522 {
523 	LPCSTR lpszExtension = PathGetExtensionA(lpszPath);
524         int i;
525         static const char * const lpszExtensions[] =
526             {"exe", "com", "pif", "cmd", "bat", "scf", "scr", NULL };
527 
528 	TRACE("path=%s\n",lpszPath);
529 
530 	for(i=0; lpszExtensions[i]; i++)
531 	  if (!lstrcmpiA(lpszExtension,lpszExtensions[i])) return TRUE;
532 
533 	return FALSE;
534 }
535 
536 /*************************************************************************
537  *  PathIsExeW		[internal]
538  */
PathIsExeW(LPCWSTR lpszPath)539 BOOL PathIsExeW (LPCWSTR lpszPath)
540 {
541 	LPCWSTR lpszExtension = PathGetExtensionW(lpszPath);
542         int i;
543         static const WCHAR lpszExtensions[][4] =
544             {L"exe", L"com", L"pif", L"cmd", L"bat", L"scf", L"scr", L"" };
545 
546 	TRACE("path=%s\n",debugstr_w(lpszPath));
547 
548 	for(i=0; lpszExtensions[i][0]; i++)
549 	  if (!wcsicmp(lpszExtension,lpszExtensions[i])) return TRUE;
550 
551 	return FALSE;
552 }
553 
554 /*************************************************************************
555  *  PathIsExe		[SHELL32.43]
556  */
PathIsExeAW(LPCVOID path)557 BOOL WINAPI PathIsExeAW (LPCVOID path)
558 {
559 	if (SHELL_OsIsUnicode())
560 	  return PathIsExeW (path);
561 	return PathIsExeA(path);
562 }
563 
564 /*************************************************************************
565  * PathFileExists	[SHELL32.45]
566  */
PathFileExistsAW(LPCVOID lpszPath)567 BOOL WINAPI PathFileExistsAW (LPCVOID lpszPath)
568 {
569 	if (SHELL_OsIsUnicode())
570 	  return PathFileExistsW (lpszPath);
571 	return PathFileExistsA (lpszPath);
572 }
573 
574 /*************************************************************************
575  * IsLFNDriveA		[SHELL32.41]
576  */
IsLFNDriveA(LPCSTR lpszPath)577 BOOL WINAPI IsLFNDriveA(LPCSTR lpszPath)
578 {
579     WCHAR szBuffW[MAX_PATH], *pszW = NULL;
580     if (lpszPath)
581     {
582         SHAnsiToUnicode(lpszPath, szBuffW, _countof(szBuffW));
583         pszW = szBuffW;
584     }
585     return IsLFNDriveW(pszW);
586 }
587 
588 /*************************************************************************
589  * IsLFNDriveW		[SHELL32.42]
590  */
IsLFNDriveW(LPCWSTR lpszPath)591 BOOL WINAPI IsLFNDriveW(LPCWSTR lpszPath)
592 {
593     DWORD cchMaxFileName, iDrive;
594     WCHAR szRoot[MAX_PATH];
595 
596     if (lpszPath == NULL || lpszPath[0] == UNICODE_NULL)
597     {
598         szRoot[0] = 0;
599         GetWindowsDirectoryW(szRoot, _countof(szRoot));
600         lpszPath = szRoot;
601     }
602 
603     if (PathIsUNCW(lpszPath))
604     {
605         StringCchCopyW(szRoot, _countof(szRoot), lpszPath);
606         PathStripToRootW(szRoot);
607 
608         if (StrChrW(szRoot + 2, L'\\') == NULL)
609             return TRUE; /* LFN */
610 
611         StringCchCatW(szRoot, _countof(szRoot), L"\\"); /* Add a backslash */
612     }
613     else
614     {
615         iDrive = ((lpszPath[0] - L'A') & 0x1F);
616         PathBuildRootW(szRoot, iDrive);
617 
618         if (!IsRemovableDrive(iDrive))
619         {
620             /* FIXME: Cache correctly */
621         }
622     }
623 
624 #define MSDOS_8DOT3_LEN 12 /* MS-DOS 8.3 filename == length 12 */
625 
626     /* GetVolumeInformation requires a root path */
627     if (!GetVolumeInformationW(szRoot, NULL, 0, NULL, &cchMaxFileName, NULL, NULL, 0))
628     {
629         /* Don't return FALSE when GetVolumeInformationW fails. */
630         return TRUE;
631     }
632 
633     return cchMaxFileName > MSDOS_8DOT3_LEN;
634 }
635 
636 /*************************************************************************
637  * IsLFNDrive		[SHELL32.119]
638  */
IsLFNDriveAW(LPCVOID lpszPath)639 BOOL WINAPI IsLFNDriveAW(LPCVOID lpszPath)
640 {
641 	if (SHELL_OsIsUnicode())
642 	  return IsLFNDriveW(lpszPath);
643 	return IsLFNDriveA(lpszPath);
644 }
645 
646 /*
647 	########## Creating Something Unique ##########
648 */
649 /*************************************************************************
650  * PathMakeUniqueNameA	[internal]
651  */
PathMakeUniqueNameA(LPSTR lpszBuffer,DWORD dwBuffSize,LPCSTR lpszShortName,LPCSTR lpszLongName,LPCSTR lpszPathName)652 static BOOL PathMakeUniqueNameA(
653 	LPSTR lpszBuffer,
654 	DWORD dwBuffSize,
655 	LPCSTR lpszShortName,
656 	LPCSTR lpszLongName,
657 	LPCSTR lpszPathName)
658 {
659 	FIXME("%p %u %s %s %s stub\n",
660 	 lpszBuffer, dwBuffSize, debugstr_a(lpszShortName),
661 	 debugstr_a(lpszLongName), debugstr_a(lpszPathName));
662 	return TRUE;
663 }
664 
665 /*************************************************************************
666  * PathMakeUniqueNameW	[internal]
667  */
PathMakeUniqueNameW(LPWSTR lpszBuffer,DWORD dwBuffSize,LPCWSTR lpszShortName,LPCWSTR lpszLongName,LPCWSTR lpszPathName)668 static BOOL PathMakeUniqueNameW(
669 	LPWSTR lpszBuffer,
670 	DWORD dwBuffSize,
671 	LPCWSTR lpszShortName,
672 	LPCWSTR lpszLongName,
673 	LPCWSTR lpszPathName)
674 {
675 	FIXME("%p %u %s %s %s stub\n",
676 	 lpszBuffer, dwBuffSize, debugstr_w(lpszShortName),
677 	 debugstr_w(lpszLongName), debugstr_w(lpszPathName));
678 	return TRUE;
679 }
680 
681 /*************************************************************************
682  * PathMakeUniqueName	[SHELL32.47]
683  */
PathMakeUniqueNameAW(LPVOID lpszBuffer,DWORD dwBuffSize,LPCVOID lpszShortName,LPCVOID lpszLongName,LPCVOID lpszPathName)684 BOOL WINAPI PathMakeUniqueNameAW(
685 	LPVOID lpszBuffer,
686 	DWORD dwBuffSize,
687 	LPCVOID lpszShortName,
688 	LPCVOID lpszLongName,
689 	LPCVOID lpszPathName)
690 {
691 	if (SHELL_OsIsUnicode())
692 	  return PathMakeUniqueNameW(lpszBuffer,dwBuffSize, lpszShortName,lpszLongName,lpszPathName);
693 	return PathMakeUniqueNameA(lpszBuffer,dwBuffSize, lpszShortName,lpszLongName,lpszPathName);
694 }
695 
696 /*************************************************************************
697  * PathYetAnotherMakeUniqueName [SHELL32.75]
698  */
PathYetAnotherMakeUniqueName(LPWSTR buffer,LPCWSTR path,LPCWSTR shortname,LPCWSTR longname)699 BOOL WINAPI PathYetAnotherMakeUniqueName(LPWSTR buffer, LPCWSTR path, LPCWSTR shortname, LPCWSTR longname)
700 {
701     WCHAR pathW[MAX_PATH], retW[MAX_PATH];
702     const WCHAR *file, *ext;
703     int i = 2;
704 
705     TRACE("(%p, %s, %s, %s)\n", buffer, debugstr_w(path), debugstr_w(shortname), debugstr_w(longname));
706 
707     file = longname ? longname : shortname;
708     PathCombineW(pathW, path, file);
709     strcpyW(retW, pathW);
710     PathRemoveExtensionW(pathW);
711 
712     ext = PathFindExtensionW(file);
713 
714     /* now try to make it unique */
715     while (PathFileExistsW(retW))
716     {
717         sprintfW(retW, L"%s (%d)%s", pathW, i, ext);
718         i++;
719     }
720 
721     strcpyW(buffer, retW);
722     TRACE("ret - %s\n", debugstr_w(buffer));
723 
724     return TRUE;
725 }
726 
727 /*
728 	########## cleaning and resolving paths ##########
729  */
730 
731 /*************************************************************************
732  * PathCleanupSpec	[SHELL32.171]
733  *
734  * lpszFile is changed in place.
735  */
PathCleanupSpec(LPCWSTR lpszPathW,LPWSTR lpszFileW)736 int WINAPI PathCleanupSpec( LPCWSTR lpszPathW, LPWSTR lpszFileW )
737 {
738     int i = 0;
739     DWORD rc = 0;
740     int length = 0;
741 
742     if (SHELL_OsIsUnicode())
743     {
744         LPWSTR p = lpszFileW;
745 
746         TRACE("Cleanup %s\n",debugstr_w(lpszFileW));
747 
748         if (lpszPathW)
749             length = strlenW(lpszPathW);
750 
751         while (*p)
752         {
753             int gct = PathGetCharTypeW(*p);
754             if (gct == GCT_INVALID || gct == GCT_WILD || gct == GCT_SEPARATOR)
755             {
756                 lpszFileW[i]='-';
757                 rc |= PCS_REPLACEDCHAR;
758             }
759             else
760                 lpszFileW[i]=*p;
761             i++;
762             p++;
763             if (length + i == MAX_PATH)
764             {
765                 rc |= PCS_FATAL | PCS_PATHTOOLONG;
766                 break;
767             }
768         }
769         lpszFileW[i]=0;
770     }
771     else
772     {
773         LPSTR lpszFileA = (LPSTR)lpszFileW;
774         LPCSTR lpszPathA = (LPCSTR)lpszPathW;
775         LPSTR p = lpszFileA;
776 
777         TRACE("Cleanup %s\n",debugstr_a(lpszFileA));
778 
779         if (lpszPathA)
780             length = strlen(lpszPathA);
781 
782         while (*p)
783         {
784             int gct = PathGetCharTypeA(*p);
785             if (gct == GCT_INVALID || gct == GCT_WILD || gct == GCT_SEPARATOR)
786             {
787                 lpszFileA[i]='-';
788                 rc |= PCS_REPLACEDCHAR;
789             }
790             else
791                 lpszFileA[i]=*p;
792             i++;
793             p++;
794             if (length + i == MAX_PATH)
795             {
796                 rc |= PCS_FATAL | PCS_PATHTOOLONG;
797                 break;
798             }
799         }
800         lpszFileA[i]=0;
801     }
802     return rc;
803 }
804 
805 /*************************************************************************
806  * PathQualifyA		[SHELL32]
807  */
PathQualifyA(LPSTR pszPath)808 VOID WINAPI PathQualifyA(LPSTR pszPath)
809 {
810     WCHAR szPath[MAX_PATH];
811     TRACE("%s\n",pszPath);
812     SHAnsiToUnicode(pszPath, szPath, _countof(szPath));
813     PathQualifyW(szPath);
814     SHUnicodeToAnsi(szPath, pszPath, MAX_PATH);
815 }
816 
817 /*************************************************************************
818  * PathQualifyW		[SHELL32]
819  */
PathQualifyW(LPWSTR pszPath)820 VOID WINAPI PathQualifyW(LPWSTR pszPath)
821 {
822     TRACE("%s\n",debugstr_w(pszPath));
823     PathQualifyExW(pszPath, NULL, 0);
824 }
825 
826 /*************************************************************************
827  * PathQualify	[SHELL32.49]
828  */
PathQualifyAW(LPVOID pszPath)829 VOID WINAPI PathQualifyAW(LPVOID pszPath)
830 {
831     if (SHELL_OsIsUnicode())
832         PathQualifyW(pszPath);
833     else
834         PathQualifyA(pszPath);
835 }
836 
PathResolveA(LPSTR path,LPCSTR * dirs,DWORD flags)837 BOOL WINAPI PathResolveA(LPSTR path, LPCSTR *dirs, DWORD flags)
838 {
839     BOOL ret = FALSE;
840     LPWSTR *dirsW = NULL;
841     DWORD iDir, cDirs, cbDirs;
842     WCHAR pathW[MAX_PATH];
843 
844     TRACE("(%s,%p,0x%08x)\n", debugstr_a(path), dirs, flags);
845 
846     if (dirs)
847     {
848         for (cDirs = 0; dirs[cDirs]; ++cDirs)
849             ;
850 
851         cbDirs = (cDirs + 1) * sizeof(LPWSTR);
852         dirsW = SHAlloc(cbDirs);
853         if (!dirsW)
854             goto Cleanup;
855 
856         ZeroMemory(dirsW, cbDirs);
857         for (iDir = 0; iDir < cDirs; ++iDir)
858         {
859             __SHCloneStrAtoW(&dirsW[iDir], dirs[iDir]);
860             if (dirsW[iDir] == NULL)
861                 goto Cleanup;
862         }
863     }
864 
865     SHAnsiToUnicode(path, pathW, _countof(pathW));
866 
867     ret = PathResolveW(pathW, (LPCWSTR*)dirsW, flags);
868     if (ret)
869         SHUnicodeToAnsi(pathW, path, MAX_PATH);
870 
871 Cleanup:
872     if (dirsW)
873     {
874         for (iDir = 0; iDir < cDirs; ++iDir)
875         {
876             SHFree(dirsW[iDir]);
877         }
878         SHFree(dirsW);
879     }
880     return ret;
881 }
882 
PathResolveW(_Inout_ LPWSTR path,_Inout_opt_ LPCWSTR * dirs,_In_ DWORD flags)883 BOOL WINAPI PathResolveW(_Inout_ LPWSTR path, _Inout_opt_ LPCWSTR *dirs, _In_ DWORD flags)
884 {
885     DWORD dwWhich = WHICH_DEFAULT; /* The extensions to be searched */
886 
887     TRACE("(%s,%p,0x%08x)\n", debugstr_w(path), dirs, flags);
888 
889     if (flags & PRF_DONTFINDLNK)
890         dwWhich &= ~WHICH_LNK; /* Don't search '.LNK' (shortcut) */
891 
892     if (flags & PRF_VERIFYEXISTS)
893         SetLastError(ERROR_FILE_NOT_FOUND); /* We set this error code at first in verification */
894 
895     PathUnquoteSpacesW(path);
896 
897     if (PathIsRootW(path)) /* Root path */
898     {
899         if (path[0] == L'\\' && path[1] == UNICODE_NULL) /* '\' only? */
900             PathQualifyExW(path, ((flags & PRF_FIRSTDIRDEF) ? *dirs : NULL), 0); /* Qualify */
901 
902         if (flags & PRF_VERIFYEXISTS)
903             return PathFileExistsAndAttributesW(path, NULL); /* Check the existence */
904 
905         return TRUE;
906     }
907 
908     if (PathIsFileSpecW(path)) /* Filename only */
909     {
910         /* Try to find the path with program extensions applied? */
911         if ((flags & PRF_TRYPROGRAMEXTENSIONS) &&
912             PathSearchOnExtensionsW(path, dirs, TRUE, dwWhich))
913         {
914             return TRUE; /* Found */
915         }
916 
917         /* Try to find the filename in the directories */
918         if (PathFindOnPathW(path, dirs))
919             goto CheckAbsoluteAndFinish;
920 
921         return FALSE; /* Not found */
922     }
923 
924     if (PathIsURLW(path)) /* URL? */
925         return FALSE;
926 
927     /* Qualify the path */
928     PathQualifyExW(path, ((flags & PRF_FIRSTDIRDEF) ? *dirs : NULL), 1);
929 
930     TRACE("(%s)\n", debugstr_w(path));
931 
932     if (!(flags & PRF_VERIFYEXISTS)) /* Don't verify the existence? */
933         return TRUE;
934 
935     /* Try to find the path with program extensions applied? */
936     if (!(flags & PRF_TRYPROGRAMEXTENSIONS) ||
937         !PathSearchOnExtensionsW(path, dirs, FALSE, dwWhich))
938     {
939         if (!PathFileExistsAndAttributesW(path, NULL))
940             return FALSE; /* Not found */
941     }
942 
943 CheckAbsoluteAndFinish:
944 #if (_WIN32_WINNT >= _WIN32_WINNT_WS03)
945     if (!(flags & PRF_REQUIREABSOLUTE) || PathIsAbsoluteW(path))
946         return TRUE;
947 
948     if (!PathMakeAbsoluteW(path))
949         return FALSE;
950 
951     return PathFileExistsAndAttributesW(path, NULL);
952 #else
953     return TRUE; /* Found */
954 #endif
955 }
956 
957 /*************************************************************************
958  * PathResolve [SHELL32.51]
959  */
PathResolveAW(LPVOID path,LPCVOID * paths,DWORD flags)960 BOOL WINAPI PathResolveAW(LPVOID path, LPCVOID *paths, DWORD flags)
961 {
962     if (SHELL_OsIsUnicode())
963         return PathResolveW(path, (LPCWSTR*)paths, flags);
964     else
965         return PathResolveA(path, (LPCSTR*)paths, flags);
966 }
967 
968 /*************************************************************************
969 *	PathProcessCommandA
970 */
PathProcessCommandA(LPCSTR lpszPath,LPSTR lpszBuff,DWORD dwBuffSize,DWORD dwFlags)971 static LONG PathProcessCommandA (
972 	LPCSTR lpszPath,
973 	LPSTR lpszBuff,
974 	DWORD dwBuffSize,
975 	DWORD dwFlags)
976 {
977 	FIXME("%s %p 0x%04x 0x%04x stub\n",
978 	lpszPath, lpszBuff, dwBuffSize, dwFlags);
979 	if(!lpszPath) return -1;
980 	if(lpszBuff) strcpy(lpszBuff, lpszPath);
981 	return strlen(lpszPath);
982 }
983 
984 /*************************************************************************
985 *	PathProcessCommandW
986 */
PathProcessCommandW(LPCWSTR lpszPath,LPWSTR lpszBuff,DWORD dwBuffSize,DWORD dwFlags)987 static LONG PathProcessCommandW (
988 	LPCWSTR lpszPath,
989 	LPWSTR lpszBuff,
990 	DWORD dwBuffSize,
991 	DWORD dwFlags)
992 {
993 	FIXME("(%s, %p, 0x%04x, 0x%04x) stub\n",
994 	debugstr_w(lpszPath), lpszBuff, dwBuffSize, dwFlags);
995 	if(!lpszPath) return -1;
996 	if(lpszBuff) strcpyW(lpszBuff, lpszPath);
997 	return strlenW(lpszPath);
998 }
999 
1000 /*************************************************************************
1001 *	PathProcessCommand (SHELL32.653)
1002 */
PathProcessCommandAW(LPCVOID lpszPath,LPVOID lpszBuff,DWORD dwBuffSize,DWORD dwFlags)1003 LONG WINAPI PathProcessCommandAW (
1004 	LPCVOID lpszPath,
1005 	LPVOID lpszBuff,
1006 	DWORD dwBuffSize,
1007 	DWORD dwFlags)
1008 {
1009 	if (SHELL_OsIsUnicode())
1010 	  return PathProcessCommandW(lpszPath, lpszBuff, dwBuffSize, dwFlags);
1011 	return PathProcessCommandA(lpszPath, lpszBuff, dwBuffSize, dwFlags);
1012 }
1013 
1014 /*
1015 	########## special ##########
1016 */
1017 
1018 /* !! MISSING Win2k3-compatible paths from the list below; absent from Wine !! */
1019 #ifndef __REACTOS__
1020 static const WCHAR Application_DataW[] = L"Application Data";
1021 static const WCHAR Local_Settings_Application_DataW[] = L"Local Settings\\Application Data";
1022 static const WCHAR Local_Settings_HistoryW[] = L"Local Settings\\History";
1023 static const WCHAR Local_Settings_Temporary_Internet_FilesW[] = L"Local Settings\\Temporary Internet Files";
1024 static const WCHAR MusicW[] = L"Music";
1025 static const WCHAR PicturesW[] = L"Pictures";
1026 static const WCHAR Program_FilesW[] = L"Program Files";
1027 static const WCHAR Program_Files_Common_FilesW[] = L"Program Files\\Common Files";
1028 static const WCHAR Start_Menu_ProgramsW[] = L"Start Menu\\Programs";
1029 static const WCHAR Start_Menu_Admin_ToolsW[] = L"Start Menu\\Programs\\Administrative Tools";
1030 static const WCHAR Start_Menu_StartupW[] = L"Start Menu\\Programs\\StartUp";
1031 #endif
1032 
1033 /* Long strings that are repeated many times: keep them here */
1034 static const WCHAR szSHFolders[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders";
1035 static const WCHAR szSHUserFolders[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders";
1036 #ifndef __REACTOS__
1037 static const WCHAR szKnownFolderDescriptions[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FolderDescriptions";
1038 static const WCHAR szKnownFolderRedirections[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders";
1039 #endif
1040 
1041 typedef enum _CSIDL_Type {
1042     CSIDL_Type_User,
1043 #ifdef __REACTOS__
1044     CSIDL_Type_InMyDocuments,
1045 #endif
1046     CSIDL_Type_AllUsers,
1047     CSIDL_Type_CurrVer,
1048     CSIDL_Type_Disallowed,
1049     CSIDL_Type_NonExistent,
1050     CSIDL_Type_WindowsPath,
1051     CSIDL_Type_SystemPath,
1052     CSIDL_Type_SystemX86Path,
1053 } CSIDL_Type;
1054 
1055 /* Cannot use #if _WIN32_WINNT >= 0x0600 because _WIN32_WINNT == 0x0600 here. */
1056 #ifndef __REACTOS__
1057 #define CSIDL_CONTACTS         0x0043
1058 #define CSIDL_DOWNLOADS        0x0047
1059 #define CSIDL_LINKS            0x004d
1060 #define CSIDL_APPDATA_LOCALLOW 0x004e
1061 #define CSIDL_SAVED_GAMES      0x0062
1062 #define CSIDL_SEARCHES         0x0063
1063 #endif
1064 
1065 typedef struct
1066 {
1067     const KNOWNFOLDERID *id;
1068     CSIDL_Type type;
1069     LPCWSTR    szValueName;
1070     LPCWSTR    szDefaultPath; /* fallback string or resource ID */
1071     INT        nShell32IconIndex;
1072 } CSIDL_DATA;
1073 
1074 static const CSIDL_DATA CSIDL_Data[] =
1075 {
1076     { /* 0x00 - CSIDL_DESKTOP */
1077         &FOLDERID_Desktop,
1078         CSIDL_Type_User,
1079         L"Desktop",
1080         MAKEINTRESOURCEW(IDS_DESKTOPDIRECTORY),
1081 #ifdef __REACTOS__
1082         0
1083 #else
1084         -IDI_SHELL_DESKTOP
1085 #endif
1086     },
1087     { /* 0x01 - CSIDL_INTERNET */
1088         &FOLDERID_InternetFolder,
1089         CSIDL_Type_Disallowed,
1090         NULL,
1091         NULL
1092     },
1093     { /* 0x02 - CSIDL_PROGRAMS */
1094         &FOLDERID_Programs,
1095         CSIDL_Type_User,
1096         L"Programs",
1097         MAKEINTRESOURCEW(IDS_PROGRAMS),
1098 #ifdef __REACTOS__
1099         0
1100 #else
1101         -IDI_SHELL_PROGRAMS_FOLDER
1102 #endif
1103     },
1104     { /* 0x03 - CSIDL_CONTROLS (.CPL files) */
1105         &FOLDERID_ControlPanelFolder,
1106         CSIDL_Type_SystemPath,
1107         L"ControlPanelFolder",
1108         NULL,
1109         -IDI_SHELL_CONTROL_PANEL
1110     },
1111     { /* 0x04 - CSIDL_PRINTERS */
1112         &FOLDERID_PrintersFolder,
1113         CSIDL_Type_SystemPath,
1114         L"PrintersFolder",
1115         NULL,
1116         -IDI_SHELL_PRINTERS_FOLDER
1117     },
1118     { /* 0x05 - CSIDL_PERSONAL */
1119         &FOLDERID_Documents,
1120         CSIDL_Type_User,
1121         L"Personal",
1122         MAKEINTRESOURCEW(IDS_PERSONAL),
1123         -IDI_SHELL_MY_DOCUMENTS
1124     },
1125     { /* 0x06 - CSIDL_FAVORITES */
1126         &FOLDERID_Favorites,
1127         CSIDL_Type_User,
1128         L"Favorites",
1129         MAKEINTRESOURCEW(IDS_FAVORITES),
1130         -IDI_SHELL_FAVORITES
1131     },
1132     { /* 0x07 - CSIDL_STARTUP */
1133         &FOLDERID_Startup,
1134         CSIDL_Type_User,
1135         L"StartUp",
1136         MAKEINTRESOURCEW(IDS_STARTUP)
1137     },
1138     { /* 0x08 - CSIDL_RECENT */
1139         &FOLDERID_Recent,
1140         CSIDL_Type_User,
1141         L"Recent",
1142         MAKEINTRESOURCEW(IDS_RECENT),
1143         -IDI_SHELL_RECENT_DOCUMENTS
1144     },
1145     { /* 0x09 - CSIDL_SENDTO */
1146         &FOLDERID_SendTo,
1147         CSIDL_Type_User,
1148         L"SendTo",
1149         MAKEINTRESOURCEW(IDS_SENDTO)
1150     },
1151     { /* 0x0a - CSIDL_BITBUCKET - Recycle Bin */
1152         &FOLDERID_RecycleBinFolder,
1153         CSIDL_Type_Disallowed,
1154         L"RecycleBinFolder",
1155         NULL
1156     },
1157     { /* 0x0b - CSIDL_STARTMENU */
1158         &FOLDERID_StartMenu,
1159         CSIDL_Type_User,
1160         L"Start Menu",
1161         MAKEINTRESOURCEW(IDS_STARTMENU),
1162         -IDI_SHELL_TSKBAR_STARTMENU
1163     },
1164     { /* 0x0c - CSIDL_MYDOCUMENTS */
1165         &GUID_NULL,
1166         CSIDL_Type_Disallowed, /* matches WinXP--can't get its path */
1167         NULL,
1168         NULL,
1169         -IDI_SHELL_MY_DOCUMENTS
1170     },
1171     { /* 0x0d - CSIDL_MYMUSIC */
1172         &FOLDERID_Music,
1173 #ifdef __REACTOS__
1174         CSIDL_Type_InMyDocuments,
1175 #else
1176         CSIDL_Type_User,
1177 #endif
1178         L"My Music",
1179         MAKEINTRESOURCEW(IDS_MYMUSIC),
1180         -IDI_SHELL_MY_MUSIC
1181     },
1182     { /* 0x0e - CSIDL_MYVIDEO */
1183         &FOLDERID_Videos,
1184 #ifdef __REACTOS__
1185         CSIDL_Type_InMyDocuments,
1186 #else
1187         CSIDL_Type_User,
1188 #endif
1189         L"My Video",
1190         MAKEINTRESOURCEW(IDS_MYVIDEO),
1191         -IDI_SHELL_MY_MOVIES
1192     },
1193     { /* 0x0f - unassigned */
1194         &GUID_NULL,
1195         CSIDL_Type_Disallowed,
1196         NULL,
1197         NULL,
1198     },
1199     { /* 0x10 - CSIDL_DESKTOPDIRECTORY */
1200         &FOLDERID_Desktop,
1201         CSIDL_Type_User,
1202         L"Desktop",
1203         MAKEINTRESOURCEW(IDS_DESKTOPDIRECTORY),
1204 #ifdef __REACTOS__
1205         0
1206 #else
1207         -IDI_SHELL_DESKTOP
1208 #endif
1209     },
1210     { /* 0x11 - CSIDL_DRIVES */
1211         &FOLDERID_ComputerFolder,
1212         CSIDL_Type_Disallowed,
1213         L"MyComputerFolder",
1214         NULL,
1215         -IDI_SHELL_COMPUTER_FOLDER
1216     },
1217     { /* 0x12 - CSIDL_NETWORK */
1218         &FOLDERID_NetworkFolder,
1219         CSIDL_Type_Disallowed,
1220         L"NetworkPlacesFolder",
1221         NULL,
1222         -IDI_SHELL_NETWORK_FOLDER
1223     },
1224     { /* 0x13 - CSIDL_NETHOOD */
1225         &FOLDERID_NetHood,
1226         CSIDL_Type_User,
1227         L"NetHood",
1228         MAKEINTRESOURCEW(IDS_NETHOOD),
1229         -IDI_SHELL_NETWORK
1230     },
1231     { /* 0x14 - CSIDL_FONTS */
1232         &FOLDERID_Fonts,
1233         CSIDL_Type_WindowsPath,
1234         L"Fonts",
1235         L"Fonts",
1236         -IDI_SHELL_FONTS_FOLDER
1237     },
1238     { /* 0x15 - CSIDL_TEMPLATES */
1239         &FOLDERID_Templates,
1240         CSIDL_Type_User,
1241         L"Templates",
1242         MAKEINTRESOURCEW(IDS_TEMPLATES)
1243     },
1244     { /* 0x16 - CSIDL_COMMON_STARTMENU */
1245         &FOLDERID_CommonStartMenu,
1246         CSIDL_Type_AllUsers,
1247         L"Common Start Menu",
1248         MAKEINTRESOURCEW(IDS_STARTMENU),
1249         -IDI_SHELL_TSKBAR_STARTMENU
1250     },
1251     { /* 0x17 - CSIDL_COMMON_PROGRAMS */
1252         &FOLDERID_CommonPrograms,
1253         CSIDL_Type_AllUsers,
1254         L"Common Programs",
1255         MAKEINTRESOURCEW(IDS_PROGRAMS),
1256 #ifdef __REACTOS__
1257         0
1258 #else
1259         -IDI_SHELL_PROGRAMS_FOLDER
1260 #endif
1261     },
1262     { /* 0x18 - CSIDL_COMMON_STARTUP */
1263         &FOLDERID_CommonStartup,
1264         CSIDL_Type_AllUsers,
1265         L"Common StartUp",
1266         MAKEINTRESOURCEW(IDS_STARTUP)
1267     },
1268     { /* 0x19 - CSIDL_COMMON_DESKTOPDIRECTORY */
1269         &FOLDERID_PublicDesktop,
1270         CSIDL_Type_AllUsers,
1271         L"Common Desktop",
1272         MAKEINTRESOURCEW(IDS_DESKTOPDIRECTORY),
1273 #ifdef __REACTOS__
1274         0
1275 #else
1276         -IDI_SHELL_DESKTOP
1277 #endif
1278     },
1279     { /* 0x1a - CSIDL_APPDATA */
1280         &FOLDERID_RoamingAppData,
1281         CSIDL_Type_User,
1282         L"AppData",
1283         MAKEINTRESOURCEW(IDS_APPDATA)
1284     },
1285     { /* 0x1b - CSIDL_PRINTHOOD */
1286         &FOLDERID_PrintHood,
1287         CSIDL_Type_User,
1288         L"PrintHood",
1289         MAKEINTRESOURCEW(IDS_PRINTHOOD),
1290         -IDI_SHELL_PRINTERS_FOLDER
1291     },
1292     { /* 0x1c - CSIDL_LOCAL_APPDATA */
1293         &FOLDERID_LocalAppData,
1294         CSIDL_Type_User,
1295         L"Local AppData",
1296         MAKEINTRESOURCEW(IDS_LOCAL_APPDATA)
1297     },
1298     { /* 0x1d - CSIDL_ALTSTARTUP */
1299         &GUID_NULL,
1300         CSIDL_Type_NonExistent,
1301         NULL,
1302         NULL
1303     },
1304     { /* 0x1e - CSIDL_COMMON_ALTSTARTUP */
1305         &GUID_NULL,
1306         CSIDL_Type_NonExistent,
1307         NULL,
1308         NULL
1309     },
1310     { /* 0x1f - CSIDL_COMMON_FAVORITES */
1311         &FOLDERID_Favorites,
1312         CSIDL_Type_AllUsers,
1313         L"Common Favorites",
1314         MAKEINTRESOURCEW(IDS_FAVORITES),
1315         -IDI_SHELL_FAVORITES
1316     },
1317     { /* 0x20 - CSIDL_INTERNET_CACHE */
1318         &FOLDERID_InternetCache,
1319         CSIDL_Type_User,
1320         L"Cache",
1321         MAKEINTRESOURCEW(IDS_INTERNET_CACHE)
1322     },
1323     { /* 0x21 - CSIDL_COOKIES */
1324         &FOLDERID_Cookies,
1325         CSIDL_Type_User,
1326         L"Cookies",
1327         MAKEINTRESOURCEW(IDS_COOKIES)
1328     },
1329     { /* 0x22 - CSIDL_HISTORY */
1330         &FOLDERID_History,
1331         CSIDL_Type_User,
1332         L"History",
1333         MAKEINTRESOURCEW(IDS_HISTORY)
1334     },
1335     { /* 0x23 - CSIDL_COMMON_APPDATA */
1336         &FOLDERID_ProgramData,
1337         CSIDL_Type_AllUsers,
1338         L"Common AppData",
1339         MAKEINTRESOURCEW(IDS_APPDATA)
1340     },
1341     { /* 0x24 - CSIDL_WINDOWS */
1342         &FOLDERID_Windows,
1343         CSIDL_Type_WindowsPath,
1344         L"Windows",
1345         NULL,
1346         -IDI_SHELL_SYSTEM_GEAR
1347     },
1348     { /* 0x25 - CSIDL_SYSTEM */
1349         &FOLDERID_System,
1350         CSIDL_Type_SystemPath,
1351         L"System",
1352         NULL,
1353         -IDI_SHELL_SYSTEM_GEAR
1354     },
1355     { /* 0x26 - CSIDL_PROGRAM_FILES */
1356         &FOLDERID_ProgramFiles,
1357         CSIDL_Type_CurrVer,
1358         L"ProgramFiles",
1359         MAKEINTRESOURCEW(IDS_PROGRAM_FILES),
1360 #ifdef __REACTOS__
1361         0
1362 #else
1363         -IDI_SHELL_PROGRAMS_FOLDER
1364 #endif
1365     },
1366     { /* 0x27 - CSIDL_MYPICTURES */
1367         &FOLDERID_Pictures,
1368 #ifdef __REACTOS__
1369         CSIDL_Type_InMyDocuments,
1370 #else
1371         CSIDL_Type_User,
1372 #endif
1373         L"My Pictures",
1374         MAKEINTRESOURCEW(IDS_MYPICTURES),
1375         -IDI_SHELL_MY_PICTURES
1376     },
1377     { /* 0x28 - CSIDL_PROFILE */
1378         &FOLDERID_Profile,
1379         CSIDL_Type_User,
1380         NULL,
1381         NULL
1382     },
1383     { /* 0x29 - CSIDL_SYSTEMX86 */
1384         &FOLDERID_SystemX86,
1385         CSIDL_Type_SystemX86Path,
1386         NULL,
1387         NULL,
1388         -IDI_SHELL_SYSTEM_GEAR
1389     },
1390     { /* 0x2a - CSIDL_PROGRAM_FILESX86 */
1391         &FOLDERID_ProgramFilesX86,
1392         CSIDL_Type_CurrVer,
1393         L"ProgramFilesX86",
1394         L"Program Files (x86)",
1395         -IDI_SHELL_PROGRAMS_FOLDER
1396     },
1397     { /* 0x2b - CSIDL_PROGRAM_FILES_COMMON */
1398         &FOLDERID_ProgramFilesCommon,
1399         CSIDL_Type_CurrVer,
1400         L"ProgramFilesCommon",
1401         MAKEINTRESOURCEW(IDS_PROGRAM_FILES_COMMON),
1402         -IDI_SHELL_PROGRAMS_FOLDER
1403     },
1404     { /* 0x2c - CSIDL_PROGRAM_FILES_COMMONX86 */
1405         &FOLDERID_ProgramFilesCommonX86,
1406         CSIDL_Type_CurrVer,
1407         L"ProgramFilesCommonX86",
1408         L"Program Files (x86)\\Common Files",
1409         -IDI_SHELL_PROGRAMS_FOLDER
1410     },
1411     { /* 0x2d - CSIDL_COMMON_TEMPLATES */
1412         &FOLDERID_CommonTemplates,
1413         CSIDL_Type_AllUsers,
1414         L"Common Templates",
1415         MAKEINTRESOURCEW(IDS_TEMPLATES)
1416     },
1417     { /* 0x2e - CSIDL_COMMON_DOCUMENTS */
1418         &FOLDERID_PublicDocuments,
1419         CSIDL_Type_AllUsers,
1420         L"Common Documents",
1421         MAKEINTRESOURCEW(IDS_PERSONAL),
1422         -IDI_SHELL_MY_DOCUMENTS
1423     },
1424     { /* 0x2f - CSIDL_COMMON_ADMINTOOLS */
1425         &FOLDERID_CommonAdminTools,
1426         CSIDL_Type_AllUsers,
1427         L"Common Administrative Tools",
1428         MAKEINTRESOURCEW(IDS_ADMINTOOLS)
1429     },
1430     { /* 0x30 - CSIDL_ADMINTOOLS */
1431         &FOLDERID_AdminTools,
1432         CSIDL_Type_User,
1433         L"Administrative Tools",
1434         MAKEINTRESOURCEW(IDS_ADMINTOOLS)
1435     },
1436     { /* 0x31 - CSIDL_CONNECTIONS */
1437         &FOLDERID_ConnectionsFolder,
1438         CSIDL_Type_Disallowed,
1439         L"ConnectionsFolder",
1440         NULL,
1441         -IDI_SHELL_NETWORK_CONNECTIONS
1442     },
1443     { /* 0x32 - unassigned */
1444         &GUID_NULL,
1445         CSIDL_Type_Disallowed,
1446         NULL,
1447         NULL
1448     },
1449     { /* 0x33 - unassigned */
1450         &GUID_NULL,
1451         CSIDL_Type_Disallowed,
1452         NULL,
1453         NULL
1454     },
1455     { /* 0x34 - unassigned */
1456         &GUID_NULL,
1457         CSIDL_Type_Disallowed,
1458         NULL,
1459         NULL
1460     },
1461     { /* 0x35 - CSIDL_COMMON_MUSIC */
1462         &FOLDERID_PublicMusic,
1463         CSIDL_Type_AllUsers,
1464         L"CommonMusic",
1465         MAKEINTRESOURCEW(IDS_COMMON_MUSIC),
1466         -IDI_SHELL_MY_MUSIC
1467     },
1468     { /* 0x36 - CSIDL_COMMON_PICTURES */
1469         &FOLDERID_PublicPictures,
1470         CSIDL_Type_AllUsers,
1471         L"CommonPictures",
1472         MAKEINTRESOURCEW(IDS_COMMON_PICTURES),
1473         -IDI_SHELL_MY_PICTURES
1474     },
1475     { /* 0x37 - CSIDL_COMMON_VIDEO */
1476         &FOLDERID_PublicVideos,
1477         CSIDL_Type_AllUsers,
1478         L"CommonVideo",
1479         MAKEINTRESOURCEW(IDS_COMMON_VIDEO),
1480         -IDI_SHELL_MY_MOVIES
1481     },
1482     { /* 0x38 - CSIDL_RESOURCES */
1483         &FOLDERID_ResourceDir,
1484         CSIDL_Type_WindowsPath,
1485         NULL,
1486         L"Resources"
1487     },
1488     { /* 0x39 - CSIDL_RESOURCES_LOCALIZED */
1489         &FOLDERID_LocalizedResourcesDir,
1490         CSIDL_Type_NonExistent,
1491         NULL,
1492         NULL
1493     },
1494     { /* 0x3a - CSIDL_COMMON_OEM_LINKS */
1495         &FOLDERID_CommonOEMLinks,
1496         CSIDL_Type_AllUsers,
1497         NULL,
1498         L"OEM Links"
1499     },
1500     { /* 0x3b - CSIDL_CDBURN_AREA */
1501         &FOLDERID_CDBurning,
1502         CSIDL_Type_User,
1503         L"CD Burning",
1504         L"Local Settings\\Application Data\\Microsoft\\CD Burning"
1505     },
1506     { /* 0x3c unassigned */
1507         &GUID_NULL,
1508         CSIDL_Type_Disallowed,
1509         NULL,
1510         NULL
1511     },
1512     { /* 0x3d - CSIDL_COMPUTERSNEARME */
1513         &GUID_NULL,
1514         CSIDL_Type_Disallowed, /* FIXME */
1515         NULL,
1516         NULL
1517     },
1518     { /* 0x3e - CSIDL_PROFILES */
1519         &GUID_NULL,
1520         CSIDL_Type_Disallowed, /* oddly, this matches WinXP */
1521         NULL,
1522         NULL
1523     },
1524 /* Cannot use #if _WIN32_WINNT >= 0x0600 because _WIN32_WINNT == 0x0600 here. */
1525 #ifndef __REACTOS__
1526     { /* 0x3f */
1527         &FOLDERID_AddNewPrograms,
1528         CSIDL_Type_Disallowed,
1529         NULL,
1530         NULL
1531     },
1532     { /* 0x40 */
1533         &FOLDERID_AppUpdates,
1534         CSIDL_Type_Disallowed,
1535         NULL,
1536         NULL
1537     },
1538     { /* 0x41 */
1539         &FOLDERID_ChangeRemovePrograms,
1540         CSIDL_Type_Disallowed,
1541         NULL,
1542         NULL
1543     },
1544     { /* 0x42 */
1545         &FOLDERID_ConflictFolder,
1546         CSIDL_Type_Disallowed,
1547         NULL,
1548         NULL
1549     },
1550     { /* 0x43 - CSIDL_CONTACTS */
1551         &FOLDERID_Contacts,
1552         CSIDL_Type_User,
1553         NULL,
1554         L"Contacts"
1555     },
1556     { /* 0x44 */
1557         &FOLDERID_DeviceMetadataStore,
1558         CSIDL_Type_Disallowed, /* FIXME */
1559         NULL,
1560         NULL
1561     },
1562     { /* 0x45 */
1563         &GUID_NULL,
1564         CSIDL_Type_User,
1565         NULL,
1566         L"Documents"
1567     },
1568     { /* 0x46 */
1569         &FOLDERID_DocumentsLibrary,
1570         CSIDL_Type_Disallowed, /* FIXME */
1571         NULL,
1572         NULL
1573     },
1574     { /* 0x47 - CSIDL_DOWNLOADS */
1575         &FOLDERID_Downloads,
1576 #ifdef __REACTOS__
1577         CSIDL_Type_InMyDocuments,
1578 #else
1579         CSIDL_Type_User,
1580 #endif
1581         NULL,
1582         L"Downloads"
1583     },
1584     { /* 0x48 */
1585         &FOLDERID_Games,
1586         CSIDL_Type_Disallowed,
1587         NULL,
1588         NULL
1589     },
1590     { /* 0x49 */
1591         &FOLDERID_GameTasks,
1592         CSIDL_Type_Disallowed, /* FIXME */
1593         NULL,
1594         NULL
1595     },
1596     { /* 0x4a */
1597         &FOLDERID_HomeGroup,
1598         CSIDL_Type_Disallowed,
1599         NULL,
1600         NULL
1601     },
1602     { /* 0x4b */
1603         &FOLDERID_ImplicitAppShortcuts,
1604         CSIDL_Type_Disallowed, /* FIXME */
1605         NULL,
1606         NULL
1607     },
1608     { /* 0x4c */
1609         &FOLDERID_Libraries,
1610         CSIDL_Type_Disallowed, /* FIXME */
1611         NULL,
1612         NULL
1613     },
1614     { /* 0x4d - CSIDL_LINKS */
1615         &FOLDERID_Links,
1616         CSIDL_Type_User,
1617         NULL,
1618         L"Links"
1619     },
1620     { /* 0x4e - CSIDL_APPDATA_LOCALLOW */
1621         &FOLDERID_LocalAppDataLow,
1622         CSIDL_Type_User,
1623         NULL,
1624         L"AppData\\LocalLow"
1625     },
1626     { /* 0x4f */
1627         &FOLDERID_MusicLibrary,
1628         CSIDL_Type_Disallowed, /* FIXME */
1629         NULL,
1630         NULL
1631     },
1632     { /* 0x50 */
1633         &FOLDERID_OriginalImages,
1634         CSIDL_Type_Disallowed, /* FIXME */
1635         NULL,
1636         NULL
1637     },
1638     { /* 0x51 */
1639         &FOLDERID_PhotoAlbums,
1640         CSIDL_Type_User,
1641         NULL,
1642         L"Pictures\\Slide Shows"
1643     },
1644     { /* 0x52 */
1645         &FOLDERID_PicturesLibrary,
1646         CSIDL_Type_Disallowed, /* FIXME */
1647         NULL,
1648         NULL
1649     },
1650     { /* 0x53 */
1651         &FOLDERID_Playlists,
1652         CSIDL_Type_User,
1653         NULL,
1654         L"Music\\Playlists"
1655     },
1656     { /* 0x54 */
1657         &FOLDERID_ProgramFilesX64,
1658         CSIDL_Type_NonExistent,
1659         NULL,
1660         NULL
1661     },
1662     { /* 0x55 */
1663         &FOLDERID_ProgramFilesCommonX64,
1664         CSIDL_Type_NonExistent,
1665         NULL,
1666         NULL
1667     },
1668     { /* 0x56 */
1669         &FOLDERID_Public,
1670         CSIDL_Type_CurrVer, /* FIXME */
1671         NULL,
1672         L"Users\\Public"
1673     },
1674     { /* 0x57 */
1675         &FOLDERID_PublicDownloads,
1676         CSIDL_Type_AllUsers,
1677         NULL,
1678         L"Downloads"
1679     },
1680     { /* 0x58 */
1681         &FOLDERID_PublicGameTasks,
1682         CSIDL_Type_AllUsers,
1683         NULL,
1684         L"Microsoft\\Windows\\GameExplorer"
1685     },
1686     { /* 0x59 */
1687         &FOLDERID_PublicLibraries,
1688         CSIDL_Type_AllUsers,
1689         NULL,
1690         L"Microsoft\\Windows\\Libraries"
1691     },
1692     { /* 0x5a */
1693         &FOLDERID_PublicRingtones,
1694         CSIDL_Type_AllUsers,
1695         NULL,
1696         L"Microsoft\\Windows\\Ringtones"
1697     },
1698     { /* 0x5b */
1699         &FOLDERID_QuickLaunch,
1700         CSIDL_Type_Disallowed, /* FIXME */
1701         NULL,
1702         NULL
1703     },
1704     { /* 0x5c */
1705         &FOLDERID_RecordedTVLibrary,
1706         CSIDL_Type_Disallowed, /* FIXME */
1707         NULL,
1708         NULL
1709     },
1710     { /* 0x5d */
1711         &FOLDERID_Ringtones,
1712         CSIDL_Type_Disallowed, /* FIXME */
1713         NULL,
1714         NULL
1715     },
1716     { /* 0x5e */
1717         &FOLDERID_SampleMusic,
1718         CSIDL_Type_AllUsers,
1719         NULL,
1720         L"Music\\Sample Music"
1721     },
1722     { /* 0x5f */
1723         &FOLDERID_SamplePictures,
1724         CSIDL_Type_AllUsers,
1725         NULL,
1726         L"Pictures\\Sample Pictures"
1727     },
1728     { /* 0x60 */
1729         &FOLDERID_SamplePlaylists,
1730         CSIDL_Type_AllUsers,
1731         NULL,
1732         L"Music\\Sample Playlists"
1733     },
1734     { /* 0x61 */
1735         &FOLDERID_SampleVideos,
1736         CSIDL_Type_AllUsers,
1737         NULL,
1738         L"Videos\\Sample Videos"
1739     },
1740     { /* 0x62 - CSIDL_SAVED_GAMES */
1741         &FOLDERID_SavedGames,
1742         CSIDL_Type_User,
1743         NULL,
1744         L"Saved Games"
1745     },
1746     { /* 0x63 - CSIDL_SEARCHES */
1747         &FOLDERID_SavedSearches,
1748         CSIDL_Type_User,
1749         NULL,
1750         L"Searches"
1751     },
1752     { /* 0x64 */
1753         &FOLDERID_SEARCH_CSC,
1754         CSIDL_Type_Disallowed,
1755         NULL,
1756         NULL
1757     },
1758     { /* 0x65 */
1759         &FOLDERID_SEARCH_MAPI,
1760         CSIDL_Type_Disallowed,
1761         NULL,
1762         NULL
1763     },
1764     { /* 0x66 */
1765         &FOLDERID_SearchHome,
1766         CSIDL_Type_Disallowed,
1767         NULL,
1768         NULL
1769     },
1770     { /* 0x67 */
1771         &FOLDERID_SidebarDefaultParts,
1772         CSIDL_Type_Disallowed, /* FIXME */
1773         NULL,
1774         NULL
1775     },
1776     { /* 0x68 */
1777         &FOLDERID_SidebarParts,
1778         CSIDL_Type_Disallowed, /* FIXME */
1779         NULL,
1780         NULL
1781     },
1782     { /* 0x69 */
1783         &FOLDERID_SyncManagerFolder,
1784         CSIDL_Type_Disallowed,
1785         NULL,
1786         NULL
1787     },
1788     { /* 0x6a */
1789         &FOLDERID_SyncResultsFolder,
1790         CSIDL_Type_Disallowed,
1791         NULL,
1792         NULL
1793     },
1794     { /* 0x6b */
1795         &FOLDERID_SyncSetupFolder,
1796         CSIDL_Type_Disallowed,
1797         NULL,
1798         NULL
1799     },
1800     { /* 0x6c */
1801         &FOLDERID_UserPinned,
1802         CSIDL_Type_Disallowed, /* FIXME */
1803         NULL,
1804         NULL
1805     },
1806     { /* 0x6d */
1807         &FOLDERID_UserProfiles,
1808         CSIDL_Type_CurrVer,
1809         L"Users",
1810         L"Users"
1811     },
1812     { /* 0x6e */
1813         &FOLDERID_UserProgramFiles,
1814         CSIDL_Type_Disallowed, /* FIXME */
1815         NULL,
1816         NULL
1817     },
1818     { /* 0x6f */
1819         &FOLDERID_UserProgramFilesCommon,
1820         CSIDL_Type_Disallowed, /* FIXME */
1821         NULL,
1822         NULL
1823     },
1824     { /* 0x70 */
1825         &FOLDERID_UsersFiles,
1826         CSIDL_Type_Disallowed,
1827         NULL,
1828         NULL
1829     },
1830     { /* 0x71 */
1831         &FOLDERID_UsersLibraries,
1832         CSIDL_Type_Disallowed,
1833         NULL,
1834         NULL
1835     },
1836     { /* 0x72 */
1837         &FOLDERID_VideosLibrary,
1838         CSIDL_Type_Disallowed, /* FIXME */
1839         NULL,
1840         NULL
1841     }
1842 #endif
1843 };
1844 
SHGetSpecialFolderID(_In_ LPCWSTR pszName)1845 INT SHGetSpecialFolderID(_In_ LPCWSTR pszName)
1846 {
1847     UINT csidl;
1848 
1849     for (csidl = 0; csidl < _countof(CSIDL_Data); ++csidl)
1850     {
1851         const CSIDL_DATA *pData = &CSIDL_Data[csidl];
1852         if (pData->szValueName && lstrcmpiW(pszName, pData->szValueName) == 0)
1853             return csidl;
1854     }
1855 
1856     return -1;
1857 }
1858 
Shell_ParseSpecialFolder(_In_ LPCWSTR pszStart,_Out_ LPWSTR * ppch,_Out_ INT * pcch)1859 INT Shell_ParseSpecialFolder(_In_ LPCWSTR pszStart, _Out_ LPWSTR *ppch, _Out_ INT *pcch)
1860 {
1861     LPCWSTR pszPath, pchBackslash;
1862     WCHAR szPath[MAX_PATH];
1863 
1864     pchBackslash = StrChrW(pszStart, L'\\');
1865     if (pchBackslash)
1866     {
1867         *ppch = (LPWSTR)(pchBackslash + 1);
1868         *pcch = (pchBackslash - pszStart) + 1;
1869         StrCpyNW(szPath, pszStart, min(*pcch, _countof(szPath)));
1870         pszPath = szPath;
1871     }
1872     else
1873     {
1874         *ppch = NULL;
1875         *pcch = lstrlenW(pszStart);
1876         pszPath = pszStart;
1877     }
1878 
1879     return SHGetSpecialFolderID(pszPath);
1880 }
1881 
1882 #ifndef __REACTOS__
1883 static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest);
1884 #else
1885 static HRESULT _SHExpandEnvironmentStrings(HANDLE hToken, LPCWSTR szSrc, LPWSTR szDest, DWORD cchDest);
1886 #endif
1887 
1888 /* Gets the value named value from the registry key
1889  * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
1890  * (or from rootKey\userPrefix\... if userPrefix is not NULL) into path, which
1891  * is assumed to be MAX_PATH WCHARs in length.
1892  * If it exists, expands the value and writes the expanded value to
1893  * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
1894  * Returns successful error code if the value was retrieved from the registry,
1895  * and a failure otherwise.
1896  */
1897 #ifndef __REACTOS__
_SHGetUserShellFolderPath(HKEY rootKey,LPCWSTR userPrefix,LPCWSTR value,LPWSTR path)1898 static HRESULT _SHGetUserShellFolderPath(HKEY rootKey, LPCWSTR userPrefix,
1899 #else
1900 static HRESULT _SHGetUserShellFolderPath(HKEY rootKey, HANDLE hToken, LPCWSTR userPrefix,
1901 #endif
1902  LPCWSTR value, LPWSTR path)
1903 {
1904     HRESULT hr;
1905     WCHAR shellFolderPath[MAX_PATH], userShellFolderPath[MAX_PATH];
1906     LPCWSTR pShellFolderPath, pUserShellFolderPath;
1907     HKEY userShellFolderKey, shellFolderKey;
1908     DWORD dwType, dwPathLen;
1909 
1910     TRACE("%p,%s,%s,%p\n",rootKey, debugstr_w(userPrefix), debugstr_w(value),
1911      path);
1912 
1913     if (userPrefix)
1914     {
1915         strcpyW(shellFolderPath, userPrefix);
1916         PathAddBackslashW(shellFolderPath);
1917         strcatW(shellFolderPath, szSHFolders);
1918         pShellFolderPath = shellFolderPath;
1919         strcpyW(userShellFolderPath, userPrefix);
1920         PathAddBackslashW(userShellFolderPath);
1921         strcatW(userShellFolderPath, szSHUserFolders);
1922         pUserShellFolderPath = userShellFolderPath;
1923     }
1924     else
1925     {
1926         pUserShellFolderPath = szSHUserFolders;
1927         pShellFolderPath = szSHFolders;
1928     }
1929 
1930     if (RegCreateKeyW(rootKey, pShellFolderPath, &shellFolderKey))
1931     {
1932         TRACE("Failed to create %s\n", debugstr_w(pShellFolderPath));
1933         return E_FAIL;
1934     }
1935     if (RegCreateKeyW(rootKey, pUserShellFolderPath, &userShellFolderKey))
1936     {
1937         TRACE("Failed to create %s\n",
1938          debugstr_w(pUserShellFolderPath));
1939         RegCloseKey(shellFolderKey);
1940         return E_FAIL;
1941     }
1942 
1943     dwPathLen = MAX_PATH * sizeof(WCHAR);
1944     if (!RegQueryValueExW(userShellFolderKey, value, NULL, &dwType,
1945      (LPBYTE)path, &dwPathLen) && (dwType == REG_EXPAND_SZ || dwType == REG_SZ))
1946     {
1947         LONG ret;
1948 
1949         path[dwPathLen / sizeof(WCHAR)] = '\0';
1950         if (dwType == REG_EXPAND_SZ && path[0] == '%')
1951         {
1952             WCHAR szTemp[MAX_PATH];
1953 
1954 #ifndef __REACTOS__
1955             _SHExpandEnvironmentStrings(path, szTemp);
1956 #else
1957             hr = _SHExpandEnvironmentStrings(hToken, path, szTemp, _countof(szTemp));
1958             if (FAILED(hr))
1959                 goto end;
1960 #endif
1961             lstrcpynW(path, szTemp, MAX_PATH);
1962         }
1963         ret = RegSetValueExW(shellFolderKey, value, 0, REG_SZ, (LPBYTE)path,
1964          (strlenW(path) + 1) * sizeof(WCHAR));
1965         if (ret != ERROR_SUCCESS)
1966             hr = HRESULT_FROM_WIN32(ret);
1967         else
1968             hr = S_OK;
1969     }
1970     else
1971         hr = E_FAIL;
1972 #ifdef __REACTOS__
1973 end:
1974 #endif
1975     RegCloseKey(shellFolderKey);
1976     RegCloseKey(userShellFolderKey);
1977     TRACE("returning 0x%08x\n", hr);
1978     return hr;
1979 }
1980 
_SHGetUserProfileDirectoryW(HANDLE hToken,LPWSTR szPath,LPDWORD lpcchPath)1981 BOOL _SHGetUserProfileDirectoryW(HANDLE hToken, LPWSTR szPath, LPDWORD lpcchPath)
1982 {
1983     BOOL result;
1984     if (!hToken)
1985     {
1986         OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken);
1987         result = GetUserProfileDirectoryW(hToken, szPath, lpcchPath);
1988         CloseHandle(hToken);
1989     }
1990     else if ((INT) hToken == -1)
1991     {
1992         result = GetDefaultUserProfileDirectoryW(szPath, lpcchPath);
1993     }
1994     else
1995     {
1996         result = GetUserProfileDirectoryW(hToken, szPath, lpcchPath);
1997     }
1998     TRACE("_SHGetUserProfileDirectoryW returning %S\n", szPath);
1999     return result;
2000 }
2001 
2002 /* Gets a 'semi-expanded' default value of the CSIDL with index folder into
2003  * pszPath, based on the entries in CSIDL_Data.  By semi-expanded, I mean:
2004  * - The entry's szDefaultPath may be either a string value or an integer
2005  *   resource identifier.  In the latter case, the string value of the resource
2006  *   is written.
2007  * - Depending on the entry's type, the path may begin with an (unexpanded)
2008  *   environment variable name.  The caller is responsible for expanding
2009  *   environment strings if so desired.
2010  *   The types that are prepended with environment variables are:
2011  *   CSIDL_Type_User:     %USERPROFILE%
2012  *   CSIDL_Type_AllUsers: %ALLUSERSPROFILE%
2013  *   CSIDL_Type_CurrVer:  %SystemDrive%
2014  *   (Others might make sense too, but as yet are unneeded.)
2015  */
2016 #ifndef __REACTOS__
_SHGetDefaultValue(BYTE folder,LPWSTR pszPath)2017 static HRESULT _SHGetDefaultValue(BYTE folder, LPWSTR pszPath)
2018 #else
2019 static HRESULT _SHGetDefaultValue(HANDLE hToken, BYTE folder, LPWSTR pszPath)
2020 #endif
2021 {
2022     HRESULT hr;
2023     WCHAR resourcePath[MAX_PATH];
2024 #ifdef __REACTOS__
2025     NT_PRODUCT_TYPE ProductType;
2026 #endif
2027 
2028     TRACE("0x%02x,%p\n", folder, pszPath);
2029 
2030     if (folder >= ARRAY_SIZE(CSIDL_Data))
2031         return E_INVALIDARG;
2032 
2033     if (!pszPath)
2034         return E_INVALIDARG;
2035 
2036 #ifdef __REACTOS__
2037     if (hToken != NULL && hToken != (HANDLE)-1)
2038     {
2039         FIXME("unsupported for user other than current or default\n");
2040     }
2041 #endif
2042 
2043     if (!is_win64)
2044     {
2045         BOOL is_wow64;
2046 
2047         switch (folder)
2048         {
2049         case CSIDL_PROGRAM_FILES:
2050         case CSIDL_PROGRAM_FILESX86:
2051             IsWow64Process( GetCurrentProcess(), &is_wow64 );
2052             folder = is_wow64 ? CSIDL_PROGRAM_FILESX86 : CSIDL_PROGRAM_FILES;
2053             break;
2054         case CSIDL_PROGRAM_FILES_COMMON:
2055         case CSIDL_PROGRAM_FILES_COMMONX86:
2056             IsWow64Process( GetCurrentProcess(), &is_wow64 );
2057             folder = is_wow64 ? CSIDL_PROGRAM_FILES_COMMONX86 : CSIDL_PROGRAM_FILES_COMMON;
2058             break;
2059         }
2060     }
2061 
2062     switch (CSIDL_Data[folder].type)
2063     {
2064         case CSIDL_Type_User:
2065             strcpyW(pszPath, L"%USERPROFILE%");
2066             break;
2067 #ifdef __REACTOS__
2068         case CSIDL_Type_InMyDocuments:
2069             strcpyW(pszPath, L"%USERPROFILE%");
2070             if (DoGetProductType(&ProductType) && ProductType == NtProductWinNt)
2071             {
2072                 if (IS_INTRESOURCE(CSIDL_Data[CSIDL_MYDOCUMENTS].szDefaultPath))
2073                 {
2074                     WCHAR szItem[MAX_PATH];
2075                     LoadStringW(shell32_hInstance,
2076                                 LOWORD(CSIDL_Data[CSIDL_MYDOCUMENTS].szDefaultPath),
2077                                 szItem, ARRAY_SIZE(szItem));
2078                     PathAppendW(pszPath, szItem);
2079                 }
2080                 else
2081                 {
2082                     PathAppendW(pszPath, CSIDL_Data[CSIDL_MYDOCUMENTS].szDefaultPath);
2083                 }
2084             }
2085             break;
2086 #endif
2087         case CSIDL_Type_AllUsers:
2088 #ifndef __REACTOS__
2089             strcpyW(pszPath, L"%PUBLIC%");
2090 #else
2091             strcpyW(pszPath, L"%ALLUSERSPROFILE%");
2092 #endif
2093             break;
2094         case CSIDL_Type_CurrVer:
2095             strcpyW(pszPath, L"%SystemDrive%");
2096             break;
2097         default:
2098             ; /* no corresponding env. var, do nothing */
2099     }
2100 
2101     hr = S_OK;
2102     if (CSIDL_Data[folder].szDefaultPath)
2103     {
2104         if (IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath))
2105         {
2106             if (LoadStringW(shell32_hInstance,
2107                 LOWORD(CSIDL_Data[folder].szDefaultPath), resourcePath, MAX_PATH))
2108             {
2109                 PathAppendW(pszPath, resourcePath);
2110             }
2111             else
2112             {
2113                 ERR("(%d,%s), LoadString failed, missing translation?\n", folder,
2114                       debugstr_w(pszPath));
2115                 hr = E_FAIL;
2116             }
2117         }
2118         else
2119         {
2120             PathAppendW(pszPath, CSIDL_Data[folder].szDefaultPath);
2121         }
2122     }
2123     TRACE("returning 0x%08x\n", hr);
2124     return hr;
2125 }
2126 
2127 /* Gets the (unexpanded) value of the folder with index folder into pszPath.
2128  * The folder's type is assumed to be CSIDL_Type_CurrVer.  Its default value
2129  * can be overridden in the HKLM\\Software\\Microsoft\\Windows\\CurrentVersion key.
2130  * If dwFlags has SHGFP_TYPE_DEFAULT set or if the value isn't overridden in
2131  * the registry, uses _SHGetDefaultValue to get the value.
2132  */
_SHGetCurrentVersionPath(DWORD dwFlags,BYTE folder,LPWSTR pszPath)2133 static HRESULT _SHGetCurrentVersionPath(DWORD dwFlags, BYTE folder,
2134  LPWSTR pszPath)
2135 {
2136     HRESULT hr;
2137 
2138     TRACE("0x%08x,0x%02x,%p\n", dwFlags, folder, pszPath);
2139 
2140     if (folder >= ARRAY_SIZE(CSIDL_Data))
2141         return E_INVALIDARG;
2142     if (CSIDL_Data[folder].type != CSIDL_Type_CurrVer)
2143         return E_INVALIDARG;
2144     if (!pszPath)
2145         return E_INVALIDARG;
2146 
2147     if (dwFlags & SHGFP_TYPE_DEFAULT)
2148 #ifndef __REACTOS__
2149         hr = _SHGetDefaultValue(folder, pszPath);
2150 #else
2151         hr = _SHGetDefaultValue(NULL, folder, pszPath);
2152 #endif
2153     else
2154     {
2155         HKEY hKey;
2156 
2157         if (RegCreateKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion", &hKey))
2158             hr = E_FAIL;
2159         else
2160         {
2161             DWORD dwType, dwPathLen = MAX_PATH * sizeof(WCHAR);
2162 
2163             if (RegQueryValueExW(hKey, CSIDL_Data[folder].szValueName, NULL,
2164              &dwType, (LPBYTE)pszPath, &dwPathLen) ||
2165              (dwType != REG_SZ && dwType != REG_EXPAND_SZ))
2166             {
2167 #ifndef __REACTOS__
2168                 hr = _SHGetDefaultValue(folder, pszPath);
2169 #else
2170                 hr = _SHGetDefaultValue(NULL, folder, pszPath);
2171 #endif
2172                 dwType = REG_EXPAND_SZ;
2173                 switch (folder)
2174                 {
2175                 case CSIDL_PROGRAM_FILESX86:
2176                 case CSIDL_PROGRAM_FILES_COMMONX86:
2177                     /* these two should never be set on 32-bit setups */
2178                     if (!is_win64)
2179                     {
2180                         BOOL is_wow64;
2181                         IsWow64Process( GetCurrentProcess(), &is_wow64 );
2182                         if (!is_wow64) break;
2183                     }
2184                     /* fall through */
2185                 default:
2186                     RegSetValueExW(hKey, CSIDL_Data[folder].szValueName, 0, dwType,
2187                                    (LPBYTE)pszPath, (strlenW(pszPath)+1)*sizeof(WCHAR));
2188                 }
2189             }
2190             else
2191             {
2192                 pszPath[dwPathLen / sizeof(WCHAR)] = '\0';
2193                 hr = S_OK;
2194             }
2195             RegCloseKey(hKey);
2196         }
2197     }
2198     TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
2199     return hr;
2200 }
2201 
_GetUserSidStringFromToken(HANDLE Token)2202 static LPWSTR _GetUserSidStringFromToken(HANDLE Token)
2203 {
2204     char InfoBuffer[64];
2205     PTOKEN_USER UserInfo;
2206     DWORD InfoSize;
2207     LPWSTR SidStr;
2208 
2209     UserInfo = (PTOKEN_USER) InfoBuffer;
2210     if (! GetTokenInformation(Token, TokenUser, InfoBuffer, sizeof(InfoBuffer),
2211                               &InfoSize))
2212     {
2213         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
2214             return NULL;
2215         UserInfo = HeapAlloc(GetProcessHeap(), 0, InfoSize);
2216         if (UserInfo == NULL)
2217             return NULL;
2218         if (! GetTokenInformation(Token, TokenUser, UserInfo, InfoSize,
2219                                   &InfoSize))
2220         {
2221             HeapFree(GetProcessHeap(), 0, UserInfo);
2222             return NULL;
2223         }
2224     }
2225 
2226     if (! ConvertSidToStringSidW(UserInfo->User.Sid, &SidStr))
2227         SidStr = NULL;
2228 
2229     if (UserInfo != (PTOKEN_USER) InfoBuffer)
2230         HeapFree(GetProcessHeap(), 0, UserInfo);
2231 
2232     return SidStr;
2233 }
2234 
2235 /* Gets the user's path (unexpanded) for the CSIDL with index folder:
2236  * If SHGFP_TYPE_DEFAULT is set, calls _SHGetDefaultValue for it.  Otherwise
2237  * calls _SHGetUserShellFolderPath for it.  Where it looks depends on hToken:
2238  * - if hToken is -1, looks in HKEY_USERS\.Default
2239  * - otherwise looks first in HKEY_CURRENT_USER, followed by HKEY_LOCAL_MACHINE
2240  *   if HKEY_CURRENT_USER doesn't contain any entries.  If both fail, finally
2241  *   calls _SHGetDefaultValue for it.
2242  */
_SHGetUserProfilePath(HANDLE hToken,DWORD dwFlags,BYTE folder,LPWSTR pszPath)2243 static HRESULT _SHGetUserProfilePath(HANDLE hToken, DWORD dwFlags, BYTE folder,
2244  LPWSTR pszPath)
2245 {
2246     const WCHAR *szValueName;
2247     WCHAR buffer[40];
2248     HRESULT hr;
2249 
2250     TRACE("%p,0x%08x,0x%02x,%p\n", hToken, dwFlags, folder, pszPath);
2251 
2252     if (folder >= ARRAY_SIZE(CSIDL_Data))
2253         return E_INVALIDARG;
2254 #ifdef __REACTOS__
2255     if (CSIDL_Data[folder].type != CSIDL_Type_User &&
2256         CSIDL_Data[folder].type != CSIDL_Type_InMyDocuments)
2257 #else
2258     if (CSIDL_Data[folder].type != CSIDL_Type_User)
2259 #endif
2260     {
2261         return E_INVALIDARG;
2262     }
2263     if (!pszPath)
2264         return E_INVALIDARG;
2265 
2266     if (dwFlags & SHGFP_TYPE_DEFAULT)
2267     {
2268 #ifndef __REACTOS__
2269         hr = _SHGetDefaultValue(folder, pszPath);
2270 #else
2271         hr = _SHGetDefaultValue(hToken, folder, pszPath);
2272 #endif
2273     }
2274     else
2275     {
2276         static const WCHAR DefaultW[] = L".Default";
2277         LPCWSTR userPrefix = NULL;
2278         HKEY hRootKey;
2279 
2280         if (hToken == (HANDLE)-1)
2281         {
2282             hRootKey = HKEY_USERS;
2283             userPrefix = DefaultW;
2284         }
2285         else if (hToken == NULL)
2286             hRootKey = HKEY_CURRENT_USER;
2287         else
2288         {
2289             hRootKey = HKEY_USERS;
2290             userPrefix = _GetUserSidStringFromToken(hToken);
2291             if (userPrefix == NULL)
2292             {
2293                 hr = E_FAIL;
2294                 goto error;
2295             }
2296         }
2297 
2298         /* For CSIDL_Type_User we also use the GUID if no szValueName is provided */
2299         szValueName = CSIDL_Data[folder].szValueName;
2300         if (!szValueName)
2301         {
2302             StringFromGUID2( CSIDL_Data[folder].id, buffer, 39 );
2303             szValueName = &buffer[0];
2304         }
2305 
2306 #ifndef __REACTOS__
2307         hr = _SHGetUserShellFolderPath(hRootKey, userPrefix, szValueName, pszPath);
2308         if (FAILED(hr) && hRootKey != HKEY_LOCAL_MACHINE)
2309             hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL, szValueName, pszPath);
2310         if (FAILED(hr))
2311             hr = _SHGetDefaultValue(folder, pszPath);
2312 #else
2313         hr = _SHGetUserShellFolderPath(hRootKey, hToken, userPrefix, szValueName, pszPath);
2314         if (FAILED(hr) && hRootKey != HKEY_LOCAL_MACHINE)
2315             hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, hToken, NULL, szValueName, pszPath);
2316         if (FAILED(hr))
2317             hr = _SHGetDefaultValue(hToken, folder, pszPath);
2318 #endif
2319         if (userPrefix != NULL && userPrefix != DefaultW)
2320             LocalFree((HLOCAL) userPrefix);
2321     }
2322 error:
2323     TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
2324     return hr;
2325 }
2326 
2327 /* Gets the (unexpanded) path for the CSIDL with index folder.  If dwFlags has
2328  * SHGFP_TYPE_DEFAULT set, calls _SHGetDefaultValue.  Otherwise calls
2329  * _SHGetUserShellFolderPath for it, looking only in HKEY_LOCAL_MACHINE.
2330  * If this fails, falls back to _SHGetDefaultValue.
2331  */
_SHGetAllUsersProfilePath(DWORD dwFlags,BYTE folder,LPWSTR pszPath)2332 static HRESULT _SHGetAllUsersProfilePath(DWORD dwFlags, BYTE folder,
2333  LPWSTR pszPath)
2334 {
2335     HRESULT hr;
2336 
2337     TRACE("0x%08x,0x%02x,%p\n", dwFlags, folder, pszPath);
2338 
2339     if (folder >= ARRAY_SIZE(CSIDL_Data))
2340         return E_INVALIDARG;
2341     if (CSIDL_Data[folder].type != CSIDL_Type_AllUsers)
2342         return E_INVALIDARG;
2343     if (!pszPath)
2344         return E_INVALIDARG;
2345 
2346     if (dwFlags & SHGFP_TYPE_DEFAULT)
2347 #ifndef __REACTOS__
2348         hr = _SHGetDefaultValue(folder, pszPath);
2349 #else
2350         hr = _SHGetDefaultValue(NULL, folder, pszPath);
2351 #endif
2352     else
2353     {
2354 #ifndef __REACTOS__
2355         hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL,
2356 #else
2357         hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL, NULL,
2358 #endif
2359          CSIDL_Data[folder].szValueName, pszPath);
2360         if (FAILED(hr))
2361 #ifndef __REACTOS__
2362             hr = _SHGetDefaultValue(folder, pszPath);
2363 #else
2364             hr = _SHGetDefaultValue(NULL, folder, pszPath);
2365 #endif
2366     }
2367     TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
2368     return hr;
2369 }
2370 
2371 #ifndef __REACTOS__
_SHOpenProfilesKey(PHKEY pKey)2372 static HRESULT _SHOpenProfilesKey(PHKEY pKey)
2373 {
2374     LONG lRet;
2375     DWORD disp;
2376 
2377     lRet = RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList", 0, NULL, 0,
2378      KEY_ALL_ACCESS, NULL, pKey, &disp);
2379     return HRESULT_FROM_WIN32(lRet);
2380 }
2381 
2382 /* Reads the value named szValueName from the key profilesKey (assumed to be
2383  * opened by _SHOpenProfilesKey) into szValue, which is assumed to be MAX_PATH
2384  * WCHARs in length.  If it doesn't exist, returns szDefault (and saves
2385  * szDefault to the registry).
2386  */
_SHGetProfilesValue(HKEY profilesKey,LPCWSTR szValueName,LPWSTR szValue,LPCWSTR szDefault)2387 static HRESULT _SHGetProfilesValue(HKEY profilesKey, LPCWSTR szValueName,
2388  LPWSTR szValue, LPCWSTR szDefault)
2389 {
2390     HRESULT hr;
2391     DWORD type, dwPathLen = MAX_PATH * sizeof(WCHAR);
2392     LONG lRet;
2393 
2394     TRACE("%p,%s,%p,%s\n", profilesKey, debugstr_w(szValueName), szValue,
2395      debugstr_w(szDefault));
2396     lRet = RegQueryValueExW(profilesKey, szValueName, NULL, &type,
2397      (LPBYTE)szValue, &dwPathLen);
2398     if (!lRet && (type == REG_SZ || type == REG_EXPAND_SZ) && dwPathLen
2399      && *szValue)
2400     {
2401         dwPathLen /= sizeof(WCHAR);
2402         szValue[dwPathLen] = '\0';
2403         hr = S_OK;
2404     }
2405     else
2406     {
2407         /* Missing or invalid value, set a default */
2408         lstrcpynW(szValue, szDefault, MAX_PATH);
2409         TRACE("Setting missing value %s to %s\n", debugstr_w(szValueName),
2410                                                   debugstr_w(szValue));
2411         lRet = RegSetValueExW(profilesKey, szValueName, 0, REG_EXPAND_SZ,
2412                               (LPBYTE)szValue,
2413                               (strlenW(szValue) + 1) * sizeof(WCHAR));
2414         if (lRet)
2415             hr = HRESULT_FROM_WIN32(lRet);
2416         else
2417             hr = S_OK;
2418     }
2419     TRACE("returning 0x%08x (output value is %s)\n", hr, debugstr_w(szValue));
2420     return hr;
2421 }
2422 #endif
2423 
2424 /* Attempts to expand environment variables from szSrc into szDest, which is
2425  * assumed to be MAX_PATH characters in length.  Before referring to the
2426  * environment, handles a few variables directly, because the environment
2427  * variables may not be set when this is called (as during Wine's installation
2428  * when default values are being written to the registry).
2429  * The directly handled environment variables, and their source, are:
2430  * - ALLUSERSPROFILE, USERPROFILE: reads from the registry
2431  * - SystemDrive: uses GetSystemDirectoryW and uses the drive portion of its
2432  *   path
2433  * If one of the directly handled environment variables is expanded, only
2434  * expands a single variable, and only in the beginning of szSrc.
2435  */
2436 #ifndef __REACTOS__
_SHExpandEnvironmentStrings(LPCWSTR szSrc,LPWSTR szDest)2437 static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest)
2438 #else
2439 static HRESULT _SHExpandEnvironmentStrings(HANDLE hToken, LPCWSTR szSrc, LPWSTR szDest, DWORD cchDest)
2440 #endif
2441 {
2442     HRESULT hr;
2443 #ifndef __REACTOS__
2444     WCHAR szTemp[MAX_PATH], szProfilesPrefix[MAX_PATH] = { 0 };
2445     HKEY key = NULL;
2446 #else
2447     WCHAR szTemp[MAX_PATH];
2448 #endif
2449 
2450     TRACE("%s, %p\n", debugstr_w(szSrc), szDest);
2451 
2452     if (!szSrc || !szDest) return E_INVALIDARG;
2453 
2454     /* short-circuit if there's nothing to expand */
2455     if (szSrc[0] != '%')
2456     {
2457         strcpyW(szDest, szSrc);
2458         hr = S_OK;
2459         goto end;
2460     }
2461 #ifndef __REACTOS__
2462     /* Get the profile prefix, we'll probably be needing it */
2463     hr = _SHOpenProfilesKey(&key);
2464     if (SUCCEEDED(hr))
2465     {
2466         WCHAR def_val[MAX_PATH];
2467 
2468         /* get the system drive */
2469         GetSystemDirectoryW(def_val, MAX_PATH);
2470         strcpyW( def_val + 3, L"Users" );
2471 
2472         hr = _SHGetProfilesValue(key, L"ProfilesDirectory", szProfilesPrefix, def_val );
2473     }
2474 #else
2475     hr = S_OK;
2476 #endif
2477 
2478     *szDest = 0;
2479     strcpyW(szTemp, szSrc);
2480     while (SUCCEEDED(hr) && szTemp[0] == '%')
2481     {
2482         if (!strncmpiW(szTemp, L"%ALLUSERSPROFILE%", ARRAY_SIZE(L"%ALLUSERSPROFILE%")-1))
2483         {
2484 #ifndef __REACTOS__
2485             WCHAR szAllUsers[MAX_PATH];
2486 
2487             strcpyW(szDest, szProfilesPrefix);
2488             hr = _SHGetProfilesValue(key, L"AllUsersProfile", szAllUsers, L"Public");
2489             PathAppendW(szDest, szAllUsers);
2490 #else
2491             DWORD cchSize = cchDest;
2492             if (!GetAllUsersProfileDirectoryW(szDest, &cchSize))
2493                 goto fallback_expand;
2494 #endif
2495             PathAppendW(szDest, szTemp + ARRAY_SIZE(L"%ALLUSERSPROFILE%")-1);
2496         }
2497 #ifndef __REACTOS__
2498         else if (!strncmpiW(szTemp, L"%PUBLIC%", ARRAY_SIZE(L"%PUBLIC%")-1))
2499         {
2500             WCHAR szAllUsers[MAX_PATH], def_val[MAX_PATH];
2501 
2502             GetSystemDirectoryW(def_val, MAX_PATH);
2503             strcpyW( def_val + 3, L"Users\\Public" );
2504 
2505             hr = _SHGetProfilesValue(key, L"Public", szAllUsers, def_val);
2506             PathAppendW(szDest, szAllUsers);
2507             PathAppendW(szDest, szTemp + ARRAY_SIZE(L"%PUBLIC%")-1);
2508         }
2509 #endif
2510         else if (!strncmpiW(szTemp, L"%USERPROFILE%", ARRAY_SIZE(L"%USERPROFILE%")-1))
2511         {
2512 #ifndef __REACTOS__
2513             WCHAR userName[MAX_PATH];
2514             DWORD userLen = MAX_PATH;
2515 
2516             strcpyW(szDest, szProfilesPrefix);
2517             GetUserNameW(userName, &userLen);
2518             PathAppendW(szDest, userName);
2519 #else
2520             DWORD cchSize = cchDest;
2521             if (!_SHGetUserProfileDirectoryW(hToken, szDest, &cchSize))
2522                 goto fallback_expand;
2523 #endif
2524             PathAppendW(szDest, szTemp + ARRAY_SIZE(L"%USERPROFILE%")-1);
2525         }
2526         else if (!strncmpiW(szTemp, L"%SystemDrive%", ARRAY_SIZE(L"%SystemDrive%")-1))
2527         {
2528 #ifndef __REACTOS__
2529             GetSystemDirectoryW(szDest, MAX_PATH);
2530 #else
2531             if (!GetSystemDirectoryW(szDest, cchDest))
2532                 goto fallback_expand;
2533 #endif
2534             strcpyW(szDest + 3, szTemp + ARRAY_SIZE(L"%SystemDrive%")-1 + 1);
2535         }
2536         else
2537 #ifdef __REACTOS__
2538 fallback_expand:
2539 #endif
2540         {
2541 #ifndef __REACTOS__
2542             DWORD ret = ExpandEnvironmentStringsW(szTemp, szDest, MAX_PATH);
2543 #else
2544             DWORD ret = SHExpandEnvironmentStringsForUserW(hToken, szTemp, szDest, cchDest);
2545 #endif
2546 
2547 #ifndef __REACTOS__
2548             if (ret > MAX_PATH)
2549 #else
2550             if (ret > cchDest)
2551 #endif
2552                 hr = E_NOT_SUFFICIENT_BUFFER;
2553             else if (ret == 0)
2554                 hr = HRESULT_FROM_WIN32(GetLastError());
2555             else if (!strcmpW( szTemp, szDest )) break;  /* nothing expanded */
2556         }
2557         if (SUCCEEDED(hr)) strcpyW(szTemp, szDest);
2558     }
2559 end:
2560 #ifndef __REACTOS__
2561     if (key)
2562         RegCloseKey(key);
2563 #endif
2564     TRACE("returning 0x%08x (input was %s, output is %s)\n", hr,
2565      debugstr_w(szSrc), debugstr_w(szDest));
2566     return hr;
2567 }
2568 
2569 /*************************************************************************
2570  * SHGetFolderPathW			[SHELL32.@]
2571  *
2572  * Convert nFolder to path.
2573  *
2574  * RETURNS
2575  *  Success: S_OK
2576  *  Failure: standard HRESULT error codes.
2577  *
2578  * NOTES
2579  * Most values can be overridden in either
2580  * HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
2581  * or in the same location in HKLM.
2582  * The "Shell Folders" registry key was used in NT4 and earlier systems.
2583  * Beginning with Windows 2000, the "User Shell Folders" key is used, so
2584  * changes made to it are made to the former key too.  This synchronization is
2585  * done on-demand: not until someone requests the value of one of these paths
2586  * (by calling one of the SHGet functions) is the value synchronized.
2587  * Furthermore, the HKCU paths take precedence over the HKLM paths.
2588  */
SHGetFolderPathW(HWND hwndOwner,int nFolder,HANDLE hToken,DWORD dwFlags,LPWSTR pszPath)2589 HRESULT WINAPI SHGetFolderPathW(
2590 	HWND hwndOwner,    /* [I] owner window */
2591 	int nFolder,       /* [I] CSIDL identifying the folder */
2592 	HANDLE hToken,     /* [I] access token */
2593 	DWORD dwFlags,     /* [I] which path to return */
2594 	LPWSTR pszPath)    /* [O] converted path */
2595 {
2596     HRESULT hr =  SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, NULL, pszPath);
2597     if(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
2598         hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
2599     return hr;
2600 }
2601 
SHGetFolderPathAndSubDirA(HWND hwndOwner,int nFolder,HANDLE hToken,DWORD dwFlags,LPCSTR pszSubPath,LPSTR pszPath)2602 HRESULT WINAPI SHGetFolderPathAndSubDirA(
2603 	HWND hwndOwner,    /* [I] owner window */
2604 	int nFolder,       /* [I] CSIDL identifying the folder */
2605 	HANDLE hToken,     /* [I] access token */
2606 	DWORD dwFlags,     /* [I] which path to return */
2607 	LPCSTR pszSubPath, /* [I] sub directory of the specified folder */
2608 	LPSTR pszPath)     /* [O] converted path */
2609 {
2610     int length;
2611     HRESULT hr = S_OK;
2612     LPWSTR pszSubPathW = NULL;
2613     LPWSTR pszPathW = NULL;
2614 
2615     TRACE("%p,%#x,%p,%#x,%s,%p\n", hwndOwner, nFolder, hToken, dwFlags, debugstr_a(pszSubPath), pszPath);
2616 
2617     if(pszPath) {
2618         pszPathW = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
2619         if(!pszPathW) {
2620             hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
2621             goto cleanup;
2622         }
2623     }
2624     TRACE("%08x,%08x,%s\n",nFolder, dwFlags, debugstr_w(pszSubPathW));
2625 
2626     /* SHGetFolderPathAndSubDirW does not distinguish if pszSubPath isn't
2627      * set (null), or an empty string.therefore call it without the parameter set
2628      * if pszSubPath is an empty string
2629      */
2630     if (pszSubPath && pszSubPath[0]) {
2631         length = MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, NULL, 0);
2632         pszSubPathW = HeapAlloc(GetProcessHeap(), 0, length * sizeof(WCHAR));
2633         if(!pszSubPathW) {
2634             hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
2635             goto cleanup;
2636         }
2637         MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, pszSubPathW, length);
2638     }
2639 
2640     hr = SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, pszSubPathW, pszPathW);
2641 
2642     if (SUCCEEDED(hr) && pszPath)
2643         WideCharToMultiByte(CP_ACP, 0, pszPathW, -1, pszPath, MAX_PATH, NULL, NULL);
2644 
2645 cleanup:
2646     HeapFree(GetProcessHeap(), 0, pszPathW);
2647     HeapFree(GetProcessHeap(), 0, pszSubPathW);
2648     return hr;
2649 }
2650 
2651 /*************************************************************************
2652  * SHGetFolderPathAndSubDirW		[SHELL32.@]
2653  */
SHGetFolderPathAndSubDirW(HWND hwndOwner,int nFolder,HANDLE hToken,DWORD dwFlags,LPCWSTR pszSubPath,LPWSTR pszPath)2654 HRESULT WINAPI SHGetFolderPathAndSubDirW(
2655 	HWND hwndOwner,    /* [I] owner window */
2656 	int nFolder,       /* [I] CSIDL identifying the folder */
2657 	HANDLE hToken,     /* [I] access token */
2658 	DWORD dwFlags,     /* [I] which path to return */
2659 	LPCWSTR pszSubPath,/* [I] sub directory of the specified folder */
2660 	LPWSTR pszPath)    /* [O] converted path */
2661 {
2662     HRESULT    hr;
2663     WCHAR      szBuildPath[MAX_PATH], szTemp[MAX_PATH];
2664     DWORD      folder = nFolder & CSIDL_FOLDER_MASK;
2665     CSIDL_Type type;
2666     int        ret;
2667 
2668     TRACE("%p,%#x,%p,%#x,%s,%p\n", hwndOwner, nFolder, hToken, dwFlags, debugstr_w(pszSubPath), pszPath);
2669 
2670     /* Windows always NULL-terminates the resulting path regardless of success
2671      * or failure, so do so first
2672      */
2673     if (pszPath)
2674         *pszPath = '\0';
2675 
2676     if (folder >= ARRAY_SIZE(CSIDL_Data))
2677         return E_INVALIDARG;
2678     if ((SHGFP_TYPE_CURRENT != dwFlags) && (SHGFP_TYPE_DEFAULT != dwFlags))
2679         return E_INVALIDARG;
2680     szTemp[0] = 0;
2681     type = CSIDL_Data[folder].type;
2682     switch (type)
2683     {
2684         case CSIDL_Type_Disallowed:
2685             hr = E_INVALIDARG;
2686             break;
2687         case CSIDL_Type_NonExistent:
2688             hr = S_FALSE;
2689             break;
2690         case CSIDL_Type_WindowsPath:
2691             GetWindowsDirectoryW(szTemp, MAX_PATH);
2692             if (CSIDL_Data[folder].szDefaultPath &&
2693              !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
2694              *CSIDL_Data[folder].szDefaultPath)
2695             {
2696                 PathAddBackslashW(szTemp);
2697                 strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
2698             }
2699             hr = S_OK;
2700             break;
2701         case CSIDL_Type_SystemPath:
2702             GetSystemDirectoryW(szTemp, MAX_PATH);
2703             if (CSIDL_Data[folder].szDefaultPath &&
2704              !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
2705              *CSIDL_Data[folder].szDefaultPath)
2706             {
2707                 PathAddBackslashW(szTemp);
2708                 strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
2709             }
2710             hr = S_OK;
2711             break;
2712         case CSIDL_Type_SystemX86Path:
2713             if (!GetSystemWow64DirectoryW(szTemp, MAX_PATH)) GetSystemDirectoryW(szTemp, MAX_PATH);
2714             if (CSIDL_Data[folder].szDefaultPath &&
2715              !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
2716              *CSIDL_Data[folder].szDefaultPath)
2717             {
2718                 PathAddBackslashW(szTemp);
2719                 strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
2720             }
2721             hr = S_OK;
2722             break;
2723         case CSIDL_Type_CurrVer:
2724             hr = _SHGetCurrentVersionPath(dwFlags, folder, szTemp);
2725             break;
2726         case CSIDL_Type_User:
2727 #ifdef __REACTOS__
2728         case CSIDL_Type_InMyDocuments:
2729 #endif
2730             hr = _SHGetUserProfilePath(hToken, dwFlags, folder, szTemp);
2731             break;
2732         case CSIDL_Type_AllUsers:
2733             hr = _SHGetAllUsersProfilePath(dwFlags, folder, szTemp);
2734             break;
2735         default:
2736             FIXME("bogus type %d, please fix\n", type);
2737             hr = E_INVALIDARG;
2738             break;
2739     }
2740 
2741     /* Expand environment strings if necessary */
2742     if (*szTemp == '%')
2743 #ifndef __REACTOS__
2744         hr = _SHExpandEnvironmentStrings(szTemp, szBuildPath);
2745 #else
2746         hr = _SHExpandEnvironmentStrings(hToken, szTemp, szBuildPath, _countof(szBuildPath));
2747 #endif
2748     else
2749         strcpyW(szBuildPath, szTemp);
2750 
2751     if (FAILED(hr)) goto end;
2752 
2753     if(pszSubPath) {
2754         /* make sure the new path does not exceed the buffer length
2755          * and remember to backslash and terminate it */
2756         if(MAX_PATH < (lstrlenW(szBuildPath) + lstrlenW(pszSubPath) + 2)) {
2757             hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
2758             goto end;
2759         }
2760         PathAppendW(szBuildPath, pszSubPath);
2761         PathRemoveBackslashW(szBuildPath);
2762     }
2763     /* Copy the path if it's available before we might return */
2764     if (SUCCEEDED(hr) && pszPath)
2765         strcpyW(pszPath, szBuildPath);
2766 
2767     /* if we don't care about existing directories we are ready */
2768     if(nFolder & CSIDL_FLAG_DONT_VERIFY) goto end;
2769 
2770     if (PathFileExistsW(szBuildPath)) goto end;
2771 
2772     /* not existing but we are not allowed to create it.  The return value
2773      * is verified against shell32 version 6.0.
2774      */
2775     if (!(nFolder & CSIDL_FLAG_CREATE))
2776     {
2777         hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
2778         goto end;
2779     }
2780 
2781     /* create directory/directories */
2782     ret = SHCreateDirectoryExW(hwndOwner, szBuildPath, NULL);
2783     if (ret && ret != ERROR_ALREADY_EXISTS)
2784     {
2785         ERR("Failed to create directory %s.\n", debugstr_w(szBuildPath));
2786         hr = E_FAIL;
2787         goto end;
2788     }
2789 
2790     TRACE("Created missing system directory %s\n", debugstr_w(szBuildPath));
2791 
2792 end:
2793 #ifdef __REACTOS__
2794     /* create desktop.ini for custom icon */
2795     if ((nFolder & CSIDL_FLAG_CREATE) &&
2796         CSIDL_Data[folder].nShell32IconIndex)
2797     {
2798         WCHAR szIconLocation[MAX_PATH];
2799         DWORD dwAttributes;
2800 
2801         /* make the directory a read-only folder */
2802         dwAttributes = GetFileAttributesW(szBuildPath);
2803         dwAttributes |= FILE_ATTRIBUTE_READONLY;
2804         SetFileAttributesW(szBuildPath, dwAttributes);
2805 
2806         /* build the desktop.ini file path */
2807         PathAppendW(szBuildPath, L"desktop.ini");
2808 
2809         /* build the icon location */
2810         StringCchPrintfW(szIconLocation, _countof(szIconLocation),
2811                          L"%%SystemRoot%%\\system32\\shell32.dll,%d",
2812                          CSIDL_Data[folder].nShell32IconIndex);
2813 
2814         /* write desktop.ini */
2815         WritePrivateProfileStringW(L".ShellClassInfo", L"IconResource", szIconLocation, szBuildPath);
2816 
2817         /* flush! */
2818         WritePrivateProfileStringW(NULL, NULL, NULL, szBuildPath);
2819 
2820         /* make the desktop.ini a system and hidden file */
2821         dwAttributes = GetFileAttributesW(szBuildPath);
2822         dwAttributes |= FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
2823         SetFileAttributesW(szBuildPath, dwAttributes);
2824     }
2825 #endif
2826 
2827     TRACE("returning 0x%08x (final path is %s)\n", hr, debugstr_w(szBuildPath));
2828     return hr;
2829 }
2830 
2831 /*************************************************************************
2832  * SHGetFolderPathA			[SHELL32.@]
2833  *
2834  * See SHGetFolderPathW.
2835  */
SHGetFolderPathA(HWND hwndOwner,int nFolder,HANDLE hToken,DWORD dwFlags,LPSTR pszPath)2836 HRESULT WINAPI SHGetFolderPathA(
2837 	HWND hwndOwner,
2838 	int nFolder,
2839 	HANDLE hToken,
2840 	DWORD dwFlags,
2841 	LPSTR pszPath)
2842 {
2843     WCHAR szTemp[MAX_PATH];
2844     HRESULT hr;
2845 
2846     TRACE("%p,%d,%p,%#x,%p\n", hwndOwner, nFolder, hToken, dwFlags, pszPath);
2847 
2848     if (pszPath)
2849         *pszPath = '\0';
2850     hr = SHGetFolderPathW(hwndOwner, nFolder, hToken, dwFlags, szTemp);
2851     if (SUCCEEDED(hr) && pszPath)
2852         WideCharToMultiByte(CP_ACP, 0, szTemp, -1, pszPath, MAX_PATH, NULL,
2853          NULL);
2854 
2855     return hr;
2856 }
2857 
2858 /* For each folder in folders, if its value has not been set in the registry,
2859  * calls _SHGetUserProfilePath or _SHGetAllUsersProfilePath (depending on the
2860  * folder's type) to get the unexpanded value first.
2861  * Writes the unexpanded value to User Shell Folders, and queries it with
2862  * SHGetFolderPathW to force the creation of the directory if it doesn't
2863  * already exist.  SHGetFolderPathW also returns the expanded value, which
2864  * this then writes to Shell Folders.
2865  */
_SHRegisterFolders(HKEY hRootKey,HANDLE hToken,LPCWSTR szUserShellFolderPath,LPCWSTR szShellFolderPath,const UINT folders[],UINT foldersLen)2866 static HRESULT _SHRegisterFolders(HKEY hRootKey, HANDLE hToken,
2867  LPCWSTR szUserShellFolderPath, LPCWSTR szShellFolderPath, const UINT folders[],
2868  UINT foldersLen)
2869 {
2870     const WCHAR *szValueName;
2871     WCHAR buffer[40];
2872     UINT i;
2873     WCHAR path[MAX_PATH];
2874     HRESULT hr = S_OK;
2875     HKEY hUserKey = NULL, hKey = NULL;
2876     DWORD dwType, dwPathLen;
2877     LONG ret;
2878 
2879     TRACE("%p,%p,%s,%p,%u\n", hRootKey, hToken,
2880      debugstr_w(szUserShellFolderPath), folders, foldersLen);
2881 
2882     ret = RegCreateKeyW(hRootKey, szUserShellFolderPath, &hUserKey);
2883     if (ret)
2884         hr = HRESULT_FROM_WIN32(ret);
2885     else
2886     {
2887         ret = RegCreateKeyW(hRootKey, szShellFolderPath, &hKey);
2888         if (ret)
2889             hr = HRESULT_FROM_WIN32(ret);
2890     }
2891     for (i = 0; SUCCEEDED(hr) && i < foldersLen; i++)
2892     {
2893         dwPathLen = MAX_PATH * sizeof(WCHAR);
2894 
2895         /* For CSIDL_Type_User we also use the GUID if no szValueName is provided */
2896         szValueName = CSIDL_Data[folders[i]].szValueName;
2897 #ifdef __REACTOS__
2898         if (!szValueName &&
2899             (CSIDL_Data[folders[i]].type == CSIDL_Type_User ||
2900              CSIDL_Data[folders[i]].type == CSIDL_Type_InMyDocuments))
2901 #else
2902         if (!szValueName && CSIDL_Data[folders[i]].type == CSIDL_Type_User)
2903 #endif
2904         {
2905             StringFromGUID2( CSIDL_Data[folders[i]].id, buffer, 39 );
2906             szValueName = &buffer[0];
2907         }
2908 
2909         if (!RegQueryValueExW(hUserKey, szValueName, NULL,
2910                               &dwType, (LPBYTE)path, &dwPathLen) &&
2911             (dwType == REG_SZ || dwType == REG_EXPAND_SZ))
2912         {
2913             hr = SHGetFolderPathW(NULL, folders[i] | CSIDL_FLAG_CREATE,
2914                                   hToken, SHGFP_TYPE_CURRENT, path);
2915         }
2916         else
2917         {
2918             *path = '\0';
2919 #ifdef __REACTOS__
2920             if (CSIDL_Data[folders[i]].type == CSIDL_Type_User ||
2921                 CSIDL_Data[folders[i]].type == CSIDL_Type_InMyDocuments)
2922 #else
2923             if (CSIDL_Data[folders[i]].type == CSIDL_Type_User)
2924 #endif
2925                 _SHGetUserProfilePath(hToken, SHGFP_TYPE_CURRENT, folders[i],
2926                  path);
2927             else if (CSIDL_Data[folders[i]].type == CSIDL_Type_AllUsers)
2928                 _SHGetAllUsersProfilePath(SHGFP_TYPE_CURRENT, folders[i], path);
2929             else if (CSIDL_Data[folders[i]].type == CSIDL_Type_WindowsPath)
2930             {
2931                 GetWindowsDirectoryW(path, MAX_PATH);
2932                 if (CSIDL_Data[folders[i]].szDefaultPath &&
2933                     !IS_INTRESOURCE(CSIDL_Data[folders[i]].szDefaultPath))
2934                 {
2935                     PathAddBackslashW(path);
2936                     strcatW(path, CSIDL_Data[folders[i]].szDefaultPath);
2937                 }
2938             }
2939             else
2940                 hr = E_FAIL;
2941             if (*path)
2942             {
2943                 ret = RegSetValueExW(hUserKey, szValueName, 0, REG_EXPAND_SZ,
2944                  (LPBYTE)path, (strlenW(path) + 1) * sizeof(WCHAR));
2945                 if (ret)
2946                     hr = HRESULT_FROM_WIN32(ret);
2947                 else
2948                 {
2949                     hr = SHGetFolderPathW(NULL, folders[i] | CSIDL_FLAG_CREATE,
2950                      hToken, SHGFP_TYPE_CURRENT, path);
2951                     ret = RegSetValueExW(hKey, szValueName, 0, REG_SZ,
2952                      (LPBYTE)path, (strlenW(path) + 1) * sizeof(WCHAR));
2953                     if (ret)
2954                         hr = HRESULT_FROM_WIN32(ret);
2955                 }
2956             }
2957         }
2958     }
2959     if (hUserKey)
2960         RegCloseKey(hUserKey);
2961     if (hKey)
2962         RegCloseKey(hKey);
2963 
2964     TRACE("returning 0x%08x\n", hr);
2965     return hr;
2966 }
2967 
_SHRegisterUserShellFolders(BOOL bDefault)2968 static HRESULT _SHRegisterUserShellFolders(BOOL bDefault)
2969 {
2970     static const UINT folders[] = {
2971      CSIDL_PROGRAMS,
2972      CSIDL_PERSONAL,
2973      CSIDL_FAVORITES,
2974      CSIDL_APPDATA,
2975      CSIDL_STARTUP,
2976      CSIDL_RECENT,
2977      CSIDL_SENDTO,
2978      CSIDL_STARTMENU,
2979      CSIDL_MYMUSIC,
2980      CSIDL_MYVIDEO,
2981      CSIDL_DESKTOPDIRECTORY,
2982      CSIDL_NETHOOD,
2983      CSIDL_TEMPLATES,
2984      CSIDL_PRINTHOOD,
2985      CSIDL_LOCAL_APPDATA,
2986      CSIDL_INTERNET_CACHE,
2987      CSIDL_COOKIES,
2988      CSIDL_HISTORY,
2989      CSIDL_MYPICTURES,
2990      CSIDL_FONTS,
2991      CSIDL_ADMINTOOLS,
2992 /* Cannot use #if _WIN32_WINNT >= 0x0600 because _WIN32_WINNT == 0x0600 here. */
2993 #ifndef __REACTOS__
2994      CSIDL_CONTACTS,
2995      CSIDL_DOWNLOADS,
2996      CSIDL_LINKS,
2997      CSIDL_APPDATA_LOCALLOW,
2998      CSIDL_SAVED_GAMES,
2999      CSIDL_SEARCHES
3000 #endif
3001     };
3002     WCHAR userShellFolderPath[MAX_PATH], shellFolderPath[MAX_PATH];
3003     LPCWSTR pUserShellFolderPath, pShellFolderPath;
3004     HRESULT hr = S_OK;
3005     HKEY hRootKey;
3006     HANDLE hToken;
3007 
3008     TRACE("%s\n", bDefault ? "TRUE" : "FALSE");
3009     if (bDefault)
3010     {
3011         hToken = (HANDLE)-1;
3012         hRootKey = HKEY_USERS;
3013         strcpyW(userShellFolderPath, L".Default");
3014         PathAddBackslashW(userShellFolderPath);
3015         strcatW(userShellFolderPath, szSHUserFolders);
3016         pUserShellFolderPath = userShellFolderPath;
3017         strcpyW(shellFolderPath, L".Default");
3018         PathAddBackslashW(shellFolderPath);
3019         strcatW(shellFolderPath, szSHFolders);
3020         pShellFolderPath = shellFolderPath;
3021     }
3022     else
3023     {
3024         hToken = NULL;
3025         hRootKey = HKEY_CURRENT_USER;
3026         pUserShellFolderPath = szSHUserFolders;
3027         pShellFolderPath = szSHFolders;
3028     }
3029 
3030     hr = _SHRegisterFolders(hRootKey, hToken, pUserShellFolderPath,
3031      pShellFolderPath, folders, ARRAY_SIZE(folders));
3032     TRACE("returning 0x%08x\n", hr);
3033     return hr;
3034 }
3035 
_SHRegisterCommonShellFolders(void)3036 static HRESULT _SHRegisterCommonShellFolders(void)
3037 {
3038     static const UINT folders[] = {
3039      CSIDL_COMMON_STARTMENU,
3040      CSIDL_COMMON_PROGRAMS,
3041      CSIDL_COMMON_STARTUP,
3042      CSIDL_COMMON_DESKTOPDIRECTORY,
3043      CSIDL_COMMON_FAVORITES,
3044      CSIDL_COMMON_APPDATA,
3045      CSIDL_COMMON_TEMPLATES,
3046      CSIDL_COMMON_DOCUMENTS,
3047      CSIDL_COMMON_ADMINTOOLS,
3048      CSIDL_COMMON_MUSIC,
3049      CSIDL_COMMON_PICTURES,
3050      CSIDL_COMMON_VIDEO,
3051     };
3052     HRESULT hr;
3053 
3054     TRACE("\n");
3055     hr = _SHRegisterFolders(HKEY_LOCAL_MACHINE, NULL, szSHUserFolders,
3056      szSHFolders, folders, ARRAY_SIZE(folders));
3057     TRACE("returning 0x%08x\n", hr);
3058     return hr;
3059 }
3060 
3061 /* Register the default values in the registry, as some apps seem to depend
3062  * on their presence.  The set registered was taken from Windows XP.
3063  */
SHELL_RegisterShellFolders(void)3064 HRESULT SHELL_RegisterShellFolders(void)
3065 {
3066     HRESULT hr;
3067 
3068     hr = _SHRegisterUserShellFolders(TRUE);
3069     if (SUCCEEDED(hr))
3070         hr = _SHRegisterUserShellFolders(FALSE);
3071     if (SUCCEEDED(hr))
3072         hr = _SHRegisterCommonShellFolders();
3073     return hr;
3074 }
3075 
3076 /*************************************************************************
3077  * SHGetSpecialFolderPathA [SHELL32.@]
3078  */
SHGetSpecialFolderPathA(HWND hwndOwner,LPSTR szPath,int nFolder,BOOL bCreate)3079 BOOL WINAPI SHGetSpecialFolderPathA (
3080 	HWND hwndOwner,
3081 	LPSTR szPath,
3082 	int nFolder,
3083 	BOOL bCreate)
3084 {
3085     return SHGetFolderPathA(hwndOwner, nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0), NULL, 0,
3086                             szPath) == S_OK;
3087 }
3088 
3089 /*************************************************************************
3090  * SHGetSpecialFolderPathW
3091  */
SHGetSpecialFolderPathW(HWND hwndOwner,LPWSTR szPath,int nFolder,BOOL bCreate)3092 BOOL WINAPI SHGetSpecialFolderPathW (
3093 	HWND hwndOwner,
3094 	LPWSTR szPath,
3095 	int nFolder,
3096 	BOOL bCreate)
3097 {
3098     return SHGetFolderPathW(hwndOwner, nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0), NULL, 0,
3099                             szPath) == S_OK;
3100 }
3101 
3102 #ifdef __REACTOS__
SHGetFolderLocationHelper(HWND hwnd,int nFolder,REFCLSID clsid,LPITEMIDLIST * ppidl)3103 HRESULT SHGetFolderLocationHelper(HWND hwnd, int nFolder, REFCLSID clsid, LPITEMIDLIST *ppidl)
3104 {
3105     HRESULT hr;
3106     IShellFolder *psf;
3107     LPITEMIDLIST parent, child;
3108     EXTERN_C HRESULT SHBindToObject(IShellFolder *psf, LPCITEMIDLIST pidl, REFIID riid, void **ppvObj);
3109     *ppidl = NULL;
3110     if (FAILED(hr = SHGetFolderLocation(hwnd, nFolder, NULL, 0, &parent)))
3111         return hr;
3112     if (SUCCEEDED(hr = SHBindToObject(NULL, parent, &IID_IShellFolder, (void**)&psf)))
3113     {
3114         WCHAR clsidstr[2 + 38 + 1];
3115         clsidstr[0] = clsidstr[1] = L':';
3116         StringFromGUID2(clsid, clsidstr + 2, 38 + 1);
3117         hr = IShellFolder_ParseDisplayName(psf, hwnd, NULL, clsidstr, NULL, &child, NULL);
3118         if (SUCCEEDED(hr))
3119             *ppidl = ILCombine(parent, child);
3120         IShellFolder_Release(psf);
3121         ILFree(child);
3122     }
3123     ILFree(parent);
3124     return hr;
3125 }
3126 #endif
3127 
3128 /*************************************************************************
3129  * SHGetFolderLocation [SHELL32.@]
3130  *
3131  * Gets the folder locations from the registry and creates a pidl.
3132  *
3133  * PARAMS
3134  *   hwndOwner  [I]
3135  *   nFolder    [I] CSIDL_xxxxx
3136  *   hToken     [I] token representing user, or NULL for current user, or -1 for
3137  *                  default user
3138  *   dwReserved [I] must be zero
3139  *   ppidl      [O] PIDL of a special folder
3140  *
3141  * RETURNS
3142  *  Success: S_OK
3143  *  Failure: Standard OLE-defined error result, S_FALSE or E_INVALIDARG
3144  *
3145  * NOTES
3146  *  Creates missing reg keys and directories.
3147  *  Mostly forwards to SHGetFolderPathW, but a few values of nFolder return
3148  *  virtual folders that are handled here.
3149  */
SHGetFolderLocation(HWND hwndOwner,int nFolder,HANDLE hToken,DWORD dwReserved,LPITEMIDLIST * ppidl)3150 HRESULT WINAPI SHGetFolderLocation(
3151 	HWND hwndOwner,
3152 	int nFolder,
3153 	HANDLE hToken,
3154 	DWORD dwReserved,
3155 	LPITEMIDLIST *ppidl)
3156 {
3157     HRESULT hr = E_INVALIDARG;
3158 #ifdef __REACTOS__
3159     WCHAR szPath[MAX_PATH];
3160 #endif
3161 
3162     TRACE("%p 0x%08x %p 0x%08x %p\n",
3163      hwndOwner, nFolder, hToken, dwReserved, ppidl);
3164 
3165     if (!ppidl)
3166         return E_INVALIDARG;
3167     if (dwReserved)
3168         return E_INVALIDARG;
3169 
3170 #ifdef __REACTOS__
3171     if ((nFolder & CSIDL_FLAG_NO_ALIAS) &&
3172         SHGetSpecialFolderPathW(hwndOwner, szPath, (nFolder & CSIDL_FOLDER_MASK), FALSE))
3173     {
3174         *ppidl = ILCreateFromPathW(szPath);
3175         if (*ppidl)
3176             return S_OK;
3177     }
3178 #endif
3179     /* The virtual folders' locations are not user-dependent */
3180     *ppidl = NULL;
3181     switch (nFolder & CSIDL_FOLDER_MASK)
3182     {
3183         case CSIDL_DESKTOP:
3184             *ppidl = _ILCreateDesktop();
3185             break;
3186 
3187         case CSIDL_PERSONAL:
3188             *ppidl = _ILCreateMyDocuments();
3189             break;
3190 
3191         case CSIDL_INTERNET:
3192             *ppidl = _ILCreateIExplore();
3193             break;
3194 
3195         case CSIDL_CONTROLS:
3196             *ppidl = _ILCreateControlPanel();
3197             break;
3198 
3199         case CSIDL_PRINTERS:
3200             *ppidl = _ILCreatePrinters();
3201             break;
3202 
3203         case CSIDL_BITBUCKET:
3204             *ppidl = _ILCreateBitBucket();
3205             break;
3206 
3207         case CSIDL_DRIVES:
3208             *ppidl = _ILCreateMyComputer();
3209             break;
3210 
3211         case CSIDL_NETWORK:
3212             *ppidl = _ILCreateNetwork();
3213             break;
3214 
3215 #ifdef __REACTOS__
3216         case CSIDL_CONNECTIONS:
3217             hr = SHGetFolderLocationHelper(hwndOwner, CSIDL_CONTROLS, &CLSID_NetworkConnections, ppidl);
3218             break;
3219 #endif
3220 
3221         default:
3222         {
3223             WCHAR szPath[MAX_PATH];
3224 
3225             hr = SHGetFolderPathW(hwndOwner, nFolder, hToken,
3226              SHGFP_TYPE_CURRENT, szPath);
3227             if (SUCCEEDED(hr))
3228             {
3229                 DWORD attributes=0;
3230 
3231                 TRACE("Value=%s\n", debugstr_w(szPath));
3232                 hr = SHILCreateFromPathW(szPath, ppidl, &attributes);
3233             }
3234             else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
3235             {
3236                 /* unlike SHGetFolderPath, SHGetFolderLocation in shell32
3237                  * version 6.0 returns E_FAIL for nonexistent paths
3238                  */
3239                 hr = E_FAIL;
3240             }
3241         }
3242     }
3243     if(*ppidl)
3244         hr = S_OK;
3245 
3246     TRACE("-- (new pidl %p)\n",*ppidl);
3247     return hr;
3248 }
3249 
3250 /*************************************************************************
3251  * SHGetSpecialFolderLocation		[SHELL32.@]
3252  *
3253  * NOTES
3254  *   In NT5, SHGetSpecialFolderLocation needs the <winntdir>/Recent
3255  *   directory.
3256  */
SHGetSpecialFolderLocation(HWND hwndOwner,INT nFolder,LPITEMIDLIST * ppidl)3257 HRESULT WINAPI SHGetSpecialFolderLocation(
3258 	HWND hwndOwner,
3259 	INT nFolder,
3260 	LPITEMIDLIST * ppidl)
3261 {
3262     HRESULT hr = E_INVALIDARG;
3263 
3264     TRACE("(%p,0x%x,%p)\n", hwndOwner,nFolder,ppidl);
3265 
3266     if (!ppidl)
3267         return E_INVALIDARG;
3268 
3269     hr = SHGetFolderLocation(hwndOwner, nFolder, NULL, 0, ppidl);
3270     return hr;
3271 }
3272