- Remove _SHExpandEnvironmentStrings and replace calls to it by usual ExpandEnvironme...
[reactos.git] / reactos / dll / win32 / shell32 / shellpath.c
1 /*
2 * Path Functions
3 *
4 * Copyright 1998, 1999, 2000 Juergen Schmied
5 * Copyright 2004 Juan Lang
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 *
21 * NOTES:
22 *
23 * Many of these functions are in SHLWAPI.DLL also
24 *
25 */
26
27 #include <precomp.h>
28
29 WINE_DEFAULT_DEBUG_CHANNEL(shell);
30
31 /*
32 ########## Combining and Constructing paths ##########
33 */
34
35 /*************************************************************************
36 * PathAppend [SHELL32.36]
37 */
38 BOOL WINAPI PathAppendAW(
39 LPVOID lpszPath1,
40 LPCVOID lpszPath2)
41 {
42 if (SHELL_OsIsUnicode())
43 return PathAppendW(lpszPath1, lpszPath2);
44 return PathAppendA(lpszPath1, lpszPath2);
45 }
46
47 /*************************************************************************
48 * PathBuildRoot [SHELL32.30]
49 */
50 LPVOID WINAPI PathBuildRootAW(LPVOID lpszPath, int drive)
51 {
52 if(SHELL_OsIsUnicode())
53 return PathBuildRootW(lpszPath, drive);
54 return PathBuildRootA(lpszPath, drive);
55 }
56
57 /*************************************************************************
58 * PathGetExtensionA [internal]
59 *
60 * NOTES
61 * exported by ordinal
62 * return value points to the first char after the dot
63 */
64 static LPSTR PathGetExtensionA(LPCSTR lpszPath)
65 {
66 TRACE("(%s)\n",lpszPath);
67
68 lpszPath = PathFindExtensionA(lpszPath);
69 return (LPSTR)(*lpszPath?(lpszPath+1):lpszPath);
70 }
71
72 /*************************************************************************
73 * PathGetExtensionW [internal]
74 */
75 LPWSTR PathGetExtensionW(LPCWSTR lpszPath)
76 {
77 TRACE("(%s)\n",debugstr_w(lpszPath));
78
79 lpszPath = PathFindExtensionW(lpszPath);
80 return (LPWSTR)(*lpszPath?(lpszPath+1):lpszPath);
81 }
82
83 /*************************************************************************
84 * SHPathGetExtension [SHELL32.158]
85 */
86 LPVOID WINAPI SHPathGetExtensionW(LPCWSTR lpszPath, DWORD void1, DWORD void2)
87 {
88 return PathGetExtensionW(lpszPath);
89 }
90
91 /*************************************************************************
92 * PathRemoveFileSpec [SHELL32.35]
93 */
94 BOOL WINAPI PathRemoveFileSpecAW(LPVOID lpszPath)
95 {
96 if (SHELL_OsIsUnicode())
97 return PathRemoveFileSpecW(lpszPath);
98 return PathRemoveFileSpecA(lpszPath);
99 }
100
101 /*
102 Path Manipulations
103 */
104
105 /*************************************************************************
106 * PathGetShortPathA [internal]
107 */
108 static void PathGetShortPathA(LPSTR pszPath)
109 {
110 CHAR path[MAX_PATH];
111
112 TRACE("%s\n", pszPath);
113
114 if (GetShortPathNameA(pszPath, path, MAX_PATH))
115 {
116 lstrcpyA(pszPath, path);
117 }
118 }
119
120 /*************************************************************************
121 * PathGetShortPathW [internal]
122 */
123 static void PathGetShortPathW(LPWSTR pszPath)
124 {
125 WCHAR path[MAX_PATH];
126
127 TRACE("%s\n", debugstr_w(pszPath));
128
129 if (GetShortPathNameW(pszPath, path, MAX_PATH))
130 {
131 wcscpy(pszPath, path);
132 }
133 }
134
135 /*************************************************************************
136 * PathGetShortPath [SHELL32.92]
137 */
138 VOID WINAPI PathGetShortPathAW(LPVOID pszPath)
139 {
140 if(SHELL_OsIsUnicode())
141 PathGetShortPathW(pszPath);
142 PathGetShortPathA(pszPath);
143 }
144
145 /*
146 ########## Path Testing ##########
147 */
148
149 /*************************************************************************
150 * PathIsRoot [SHELL32.29]
151 */
152 BOOL WINAPI PathIsRootAW(LPCVOID lpszPath)
153 {
154 if (SHELL_OsIsUnicode())
155 return PathIsRootW(lpszPath);
156 return PathIsRootA(lpszPath);
157 }
158
159 /*************************************************************************
160 * PathIsExeA [internal]
161 */
162 static BOOL PathIsExeA (LPCSTR lpszPath)
163 {
164 LPCSTR lpszExtension = PathGetExtensionA(lpszPath);
165 int i;
166 static const char * const lpszExtensions[] =
167 {"exe", "com", "pif", "cmd", "bat", "scf", "scr", NULL };
168
169 TRACE("path=%s\n",lpszPath);
170
171 for(i=0; lpszExtensions[i]; i++)
172 if (!lstrcmpiA(lpszExtension,lpszExtensions[i])) return TRUE;
173
174 return FALSE;
175 }
176
177 /*************************************************************************
178 * PathIsExeW [internal]
179 */
180 static BOOL PathIsExeW (LPCWSTR lpszPath)
181 {
182 LPCWSTR lpszExtension = PathGetExtensionW(lpszPath);
183 int i;
184 static const WCHAR lpszExtensions[][4] =
185 {{'e','x','e','\0'}, {'c','o','m','\0'}, {'p','i','f','\0'},
186 {'c','m','d','\0'}, {'b','a','t','\0'}, {'s','c','f','\0'},
187 {'s','c','r','\0'}, {'\0'} };
188
189 TRACE("path=%s\n",debugstr_w(lpszPath));
190
191 for(i=0; lpszExtensions[i][0]; i++)
192 if (!strcmpiW(lpszExtension,lpszExtensions[i])) return TRUE;
193
194 return FALSE;
195 }
196
197 /*************************************************************************
198 * PathIsExe [SHELL32.43]
199 */
200 BOOL WINAPI PathIsExeAW (LPCVOID path)
201 {
202 if (SHELL_OsIsUnicode())
203 return PathIsExeW (path);
204 return PathIsExeA(path);
205 }
206
207 /*************************************************************************
208 * PathFileExists [SHELL32.45]
209 */
210 BOOL WINAPI PathFileExistsAW (LPCVOID lpszPath)
211 {
212 if (SHELL_OsIsUnicode())
213 return PathFileExistsW (lpszPath);
214 return PathFileExistsA (lpszPath);
215 }
216
217 /*************************************************************************
218 * PathIsSameRoot [SHELL32.650]
219 */
220 BOOL WINAPI PathIsSameRootAW(LPCVOID lpszPath1, LPCVOID lpszPath2)
221 {
222 if (SHELL_OsIsUnicode())
223 return PathIsSameRootW(lpszPath1, lpszPath2);
224 return PathIsSameRootA(lpszPath1, lpszPath2);
225 }
226
227 /*************************************************************************
228 * IsLFNDriveA [SHELL32.41]
229 */
230 BOOL WINAPI IsLFNDriveA(LPCSTR lpszPath)
231 {
232 DWORD fnlen;
233
234 if (!GetVolumeInformationA(lpszPath, NULL, 0, NULL, &fnlen, NULL, NULL, 0))
235 return FALSE;
236 return fnlen > 12;
237 }
238
239 /*************************************************************************
240 * IsLFNDriveW [SHELL32.42]
241 */
242 BOOL WINAPI IsLFNDriveW(LPCWSTR lpszPath)
243 {
244 DWORD fnlen;
245
246 if (!GetVolumeInformationW(lpszPath, NULL, 0, NULL, &fnlen, NULL, NULL, 0))
247 return FALSE;
248 return fnlen > 12;
249 }
250
251 /*************************************************************************
252 * IsLFNDrive [SHELL32.119]
253 */
254 BOOL WINAPI IsLFNDriveAW(LPCVOID lpszPath)
255 {
256 if (SHELL_OsIsUnicode())
257 return IsLFNDriveW(lpszPath);
258 return IsLFNDriveA(lpszPath);
259 }
260
261 /*
262 ########## Creating Something Unique ##########
263 */
264 /*************************************************************************
265 * PathMakeUniqueNameA [internal]
266 */
267 BOOL WINAPI PathMakeUniqueNameA(
268 LPSTR lpszBuffer,
269 DWORD dwBuffSize,
270 LPCSTR lpszShortName,
271 LPCSTR lpszLongName,
272 LPCSTR lpszPathName)
273 {
274 FIXME("%p %u %s %s %s stub\n",
275 lpszBuffer, dwBuffSize, debugstr_a(lpszShortName),
276 debugstr_a(lpszLongName), debugstr_a(lpszPathName));
277 return TRUE;
278 }
279
280 /*************************************************************************
281 * PathMakeUniqueNameW [internal]
282 */
283 BOOL WINAPI PathMakeUniqueNameW(
284 LPWSTR lpszBuffer,
285 DWORD dwBuffSize,
286 LPCWSTR lpszShortName,
287 LPCWSTR lpszLongName,
288 LPCWSTR lpszPathName)
289 {
290 FIXME("%p %u %s %s %s stub\n",
291 lpszBuffer, dwBuffSize, debugstr_w(lpszShortName),
292 debugstr_w(lpszLongName), debugstr_w(lpszPathName));
293 return TRUE;
294 }
295
296 /*************************************************************************
297 * PathMakeUniqueName [SHELL32.47]
298 */
299 BOOL WINAPI PathMakeUniqueNameAW(
300 LPVOID lpszBuffer,
301 DWORD dwBuffSize,
302 LPCVOID lpszShortName,
303 LPCVOID lpszLongName,
304 LPCVOID lpszPathName)
305 {
306 if (SHELL_OsIsUnicode())
307 return PathMakeUniqueNameW(lpszBuffer,dwBuffSize, lpszShortName,lpszLongName,lpszPathName);
308 return PathMakeUniqueNameA(lpszBuffer,dwBuffSize, lpszShortName,lpszLongName,lpszPathName);
309 }
310
311 /*************************************************************************
312 * PathYetAnotherMakeUniqueName [SHELL32.75]
313 *
314 * NOTES
315 * exported by ordinal
316 */
317 BOOL WINAPI PathYetAnotherMakeUniqueName(
318 LPWSTR lpszBuffer,
319 LPCWSTR lpszPathName,
320 LPCWSTR lpszShortName,
321 LPCWSTR lpszLongName)
322 {
323 FIXME("(%p, %s, %s ,%s):stub.\n",
324 lpszBuffer, debugstr_w(lpszPathName), debugstr_w(lpszShortName), debugstr_w(lpszLongName));
325 return TRUE;
326 }
327
328
329 /*
330 ########## cleaning and resolving paths ##########
331 */
332
333 /*************************************************************************
334 * PathCleanupSpec [SHELL32.171]
335 *
336 * lpszFile is changed in place.
337 */
338 int WINAPI PathCleanupSpec( LPCWSTR lpszPathW, LPWSTR lpszFileW )
339 {
340 int i = 0;
341 DWORD rc = 0;
342 int length = 0;
343
344 if (SHELL_OsIsUnicode())
345 {
346 LPWSTR p = lpszFileW;
347
348 TRACE("Cleanup %s\n",debugstr_w(lpszFileW));
349
350 if (lpszPathW)
351 length = wcslen(lpszPathW);
352
353 while (*p)
354 {
355 int gct = PathGetCharTypeW(*p);
356 if (gct == GCT_INVALID || gct == GCT_WILD || gct == GCT_SEPARATOR)
357 {
358 lpszFileW[i]='-';
359 rc |= PCS_REPLACEDCHAR;
360 }
361 else
362 lpszFileW[i]=*p;
363 i++;
364 p++;
365 if (length + i == MAX_PATH)
366 {
367 rc |= PCS_FATAL | PCS_PATHTOOLONG;
368 break;
369 }
370 }
371 lpszFileW[i]=0;
372 }
373 else
374 {
375 LPSTR lpszFileA = (LPSTR)lpszFileW;
376 LPCSTR lpszPathA = (LPCSTR)lpszPathW;
377 LPSTR p = lpszFileA;
378
379 TRACE("Cleanup %s\n",debugstr_a(lpszFileA));
380
381 if (lpszPathA)
382 length = strlen(lpszPathA);
383
384 while (*p)
385 {
386 int gct = PathGetCharTypeA(*p);
387 if (gct == GCT_INVALID || gct == GCT_WILD || gct == GCT_SEPARATOR)
388 {
389 lpszFileA[i]='-';
390 rc |= PCS_REPLACEDCHAR;
391 }
392 else
393 lpszFileA[i]=*p;
394 i++;
395 p++;
396 if (length + i == MAX_PATH)
397 {
398 rc |= PCS_FATAL | PCS_PATHTOOLONG;
399 break;
400 }
401 }
402 lpszFileA[i]=0;
403 }
404 return rc;
405 }
406
407 /*************************************************************************
408 * PathQualifyA [SHELL32]
409 */
410 BOOL WINAPI PathQualifyA(LPCSTR pszPath)
411 {
412 FIXME("%s\n",pszPath);
413 return 0;
414 }
415
416 /*************************************************************************
417 * PathQualifyW [SHELL32]
418 */
419 BOOL WINAPI PathQualifyW(LPCWSTR pszPath)
420 {
421 FIXME("%s\n",debugstr_w(pszPath));
422 return 0;
423 }
424
425 /*************************************************************************
426 * PathQualify [SHELL32.49]
427 */
428 BOOL WINAPI PathQualifyAW(LPCVOID pszPath)
429 {
430 if (SHELL_OsIsUnicode())
431 return PathQualifyW(pszPath);
432 return PathQualifyA(pszPath);
433 }
434
435 /*************************************************************************
436 * PathResolveA [SHELL32.51]
437 */
438 BOOL WINAPI PathResolveA(
439 LPSTR lpszPath,
440 LPCSTR *alpszPaths,
441 DWORD dwFlags)
442 {
443 FIXME("(%s,%p,0x%08x),stub!\n",
444 lpszPath, *alpszPaths, dwFlags);
445 return 0;
446 }
447
448 /*************************************************************************
449 * PathResolveW [SHELL32]
450 */
451 BOOL WINAPI PathResolveW(
452 LPWSTR lpszPath,
453 LPCWSTR *alpszPaths,
454 DWORD dwFlags)
455 {
456 FIXME("(%s,%p,0x%08x),stub!\n",
457 debugstr_w(lpszPath), debugstr_w(*alpszPaths), dwFlags);
458 return 0;
459 }
460
461 /*************************************************************************
462 * PathResolve [SHELL32.51]
463 */
464 BOOL WINAPI PathResolveAW(
465 LPVOID lpszPath,
466 LPCVOID *alpszPaths,
467 DWORD dwFlags)
468 {
469 if (SHELL_OsIsUnicode())
470 return PathResolveW(lpszPath, (LPCWSTR*)alpszPaths, dwFlags);
471 return PathResolveA(lpszPath, (LPCSTR*)alpszPaths, dwFlags);
472 }
473
474 /*************************************************************************
475 * PathProcessCommandA [SHELL32.653]
476 */
477 LONG WINAPI PathProcessCommandA (
478 LPCSTR lpszPath,
479 LPSTR lpszBuff,
480 DWORD dwBuffSize,
481 DWORD dwFlags)
482 {
483 FIXME("%s %p 0x%04x 0x%04x stub\n",
484 lpszPath, lpszBuff, dwBuffSize, dwFlags);
485 if(!lpszPath) return -1;
486 if(lpszBuff) strcpy(lpszBuff, lpszPath);
487 return strlen(lpszPath);
488 }
489
490 /*************************************************************************
491 * PathProcessCommandW
492 */
493 LONG WINAPI PathProcessCommandW (
494 LPCWSTR lpszPath,
495 LPWSTR lpszBuff,
496 DWORD dwBuffSize,
497 DWORD dwFlags)
498 {
499 FIXME("(%s, %p, 0x%04x, 0x%04x) stub\n",
500 debugstr_w(lpszPath), lpszBuff, dwBuffSize, dwFlags);
501 if(!lpszPath) return -1;
502 if(lpszBuff) wcscpy(lpszBuff, lpszPath);
503 return wcslen(lpszPath);
504 }
505
506 /*************************************************************************
507 * PathProcessCommand (SHELL32.653)
508 */
509 LONG WINAPI PathProcessCommandAW (
510 LPCVOID lpszPath,
511 LPVOID lpszBuff,
512 DWORD dwBuffSize,
513 DWORD dwFlags)
514 {
515 if (SHELL_OsIsUnicode())
516 return PathProcessCommandW(lpszPath, lpszBuff, dwBuffSize, dwFlags);
517 return PathProcessCommandA(lpszPath, lpszBuff, dwBuffSize, dwFlags);
518 }
519
520 /*
521 ########## special ##########
522 */
523
524 static const WCHAR szCurrentVersion[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\0'};
525 static const WCHAR Administrative_ToolsW[] = {'A','d','m','i','n','i','s','t','r','a','t','i','v','e',' ','T','o','o','l','s','\0'};
526 static const WCHAR AppDataW[] = {'A','p','p','D','a','t','a','\0'};
527 static const WCHAR CacheW[] = {'C','a','c','h','e','\0'};
528 static const WCHAR CD_BurningW[] = {'C','D',' ','B','u','r','n','i','n','g','\0'};
529 static const WCHAR Common_Administrative_ToolsW[] = {'C','o','m','m','o','n',' ','A','d','m','i','n','i','s','t','r','a','t','i','v','e',' ','T','o','o','l','s','\0'};
530 static const WCHAR Common_AppDataW[] = {'C','o','m','m','o','n',' ','A','p','p','D','a','t','a','\0'};
531 static const WCHAR Common_DesktopW[] = {'C','o','m','m','o','n',' ','D','e','s','k','t','o','p','\0'};
532 static const WCHAR Common_DocumentsW[] = {'C','o','m','m','o','n',' ','D','o','c','u','m','e','n','t','s','\0'};
533 static const WCHAR CommonFilesDirW[] = {'C','o','m','m','o','n','F','i','l','e','s','D','i','r','\0'};
534 static const WCHAR CommonMusicW[] = {'C','o','m','m','o','n','M','u','s','i','c','\0'};
535 static const WCHAR CommonPicturesW[] = {'C','o','m','m','o','n','P','i','c','t','u','r','e','s','\0'};
536 static const WCHAR Common_ProgramsW[] = {'C','o','m','m','o','n',' ','P','r','o','g','r','a','m','s','\0'};
537 static const WCHAR Common_StartUpW[] = {'C','o','m','m','o','n',' ','S','t','a','r','t','U','p','\0'};
538 static const WCHAR Common_Start_MenuW[] = {'C','o','m','m','o','n',' ','S','t','a','r','t',' ','M','e','n','u','\0'};
539 static const WCHAR Common_TemplatesW[] = {'C','o','m','m','o','n',' ','T','e','m','p','l','a','t','e','s','\0'};
540 static const WCHAR CommonVideoW[] = {'C','o','m','m','o','n','V','i','d','e','o','\0'};
541 static const WCHAR CookiesW[] = {'C','o','o','k','i','e','s','\0'};
542 static const WCHAR DesktopW[] = {'D','e','s','k','t','o','p','\0'};
543 static const WCHAR FavoritesW[] = {'F','a','v','o','r','i','t','e','s','\0'};
544 static const WCHAR FontsW[] = {'F','o','n','t','s','\0'};
545 static const WCHAR HistoryW[] = {'H','i','s','t','o','r','y','\0'};
546 static const WCHAR Local_AppDataW[] = {'L','o','c','a','l',' ','A','p','p','D','a','t','a','\0'};
547 static const WCHAR My_MusicW[] = {'M','y',' ','M','u','s','i','c','\0'};
548 static const WCHAR My_PicturesW[] = {'M','y',' ','P','i','c','t','u','r','e','s','\0'};
549 static const WCHAR My_VideoW[] = {'M','y',' ','V','i','d','e','o','\0'};
550 static const WCHAR NetHoodW[] = {'N','e','t','H','o','o','d','\0'};
551 static const WCHAR PersonalW[] = {'P','e','r','s','o','n','a','l','\0'};
552 static const WCHAR PrintHoodW[] = {'P','r','i','n','t','H','o','o','d','\0'};
553 static const WCHAR ProgramFilesDirW[] = {'P','r','o','g','r','a','m','F','i','l','e','s','D','i','r','\0'};
554 static const WCHAR ProgramsW[] = {'P','r','o','g','r','a','m','s','\0'};
555 static const WCHAR RecentW[] = {'R','e','c','e','n','t','\0'};
556 static const WCHAR ResourcesW[] = {'R','e','s','o','u','r','c','e','s','\0'};
557 static const WCHAR SendToW[] = {'S','e','n','d','T','o','\0'};
558 static const WCHAR StartUpW[] = {'S','t','a','r','t','U','p','\0'};
559 static const WCHAR Start_MenuW[] = {'S','t','a','r','t',' ','M','e','n','u','\0'};
560 static const WCHAR TemplatesW[] = {'T','e','m','p','l','a','t','e','s','\0'};
561 static const WCHAR DefaultW[] = {'.','D','e','f','a','u','l','t','\0'};
562 static const WCHAR AllUsersProfileW[] = {'%','A','L','L','U','S','E','R','S','P','R','O','F','I','L','E','%','\0'};
563 static const WCHAR UserProfileW[] = {'%','U','S','E','R','P','R','O','F','I','L','E','%','\0'};
564 static const WCHAR SystemDriveW[] = {'%','S','y','s','t','e','m','D','r','i','v','e','%','\0'};
565 static const WCHAR ProfileListW[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s',' ','N','T','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','P','r','o','f','i','l','e','L','i','s','t',0};
566 static const WCHAR ProfilesDirectoryW[] = {'P','r','o','f','i','l','e','s','D','i','r','e','c','t','o','r','y',0};
567 static const WCHAR AllUsersProfileValueW[] = {'A','l','l','U','s','e','r','s','P','r','o','f','i','l','e','\0'};
568 static const WCHAR szSHFolders[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','E','x','p','l','o','r','e','r','\\','S','h','e','l','l',' ','F','o','l','d','e','r','s','\0'};
569 static const WCHAR szSHUserFolders[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','E','x','p','l','o','r','e','r','\\','U','s','e','r',' ','S','h','e','l','l',' ','F','o','l','d','e','r','s','\0'};
570 /* This defaults to L"Documents and Settings" on Windows 2000/XP, but we're
571 * acting more Windows 9x-like for now.
572 */
573 static const WCHAR szDefaultProfileDirW[] = {'p','r','o','f','i','l','e','s','\0'};
574 static const WCHAR AllUsersW[] = {'A','l','l',' ','U','s','e','r','s','\0'};
575
576 typedef enum _CSIDL_Type {
577 CSIDL_Type_User,
578 CSIDL_Type_AllUsers,
579 CSIDL_Type_CurrVer,
580 CSIDL_Type_Disallowed,
581 CSIDL_Type_NonExistent,
582 CSIDL_Type_WindowsPath,
583 CSIDL_Type_SystemPath,
584 } CSIDL_Type;
585
586 typedef struct
587 {
588 CSIDL_Type type;
589 LPCWSTR szValueName;
590 LPCWSTR szDefaultPath; /* fallback string or resource ID */
591 } CSIDL_DATA;
592
593 static const CSIDL_DATA CSIDL_Data[] =
594 {
595 { /* 0x00 - CSIDL_DESKTOP */
596 CSIDL_Type_User,
597 DesktopW,
598 MAKEINTRESOURCEW(IDS_DESKTOPDIRECTORY)
599 },
600 { /* 0x01 - CSIDL_INTERNET */
601 CSIDL_Type_Disallowed,
602 NULL,
603 NULL
604 },
605 { /* 0x02 - CSIDL_PROGRAMS */
606 CSIDL_Type_User,
607 ProgramsW,
608 MAKEINTRESOURCEW(IDS_PROGRAMS)
609 },
610 { /* 0x03 - CSIDL_CONTROLS (.CPL files) */
611 CSIDL_Type_SystemPath,
612 NULL,
613 NULL
614 },
615 { /* 0x04 - CSIDL_PRINTERS */
616 CSIDL_Type_SystemPath,
617 NULL,
618 NULL
619 },
620 { /* 0x05 - CSIDL_PERSONAL */
621 CSIDL_Type_User,
622 PersonalW,
623 MAKEINTRESOURCEW(IDS_PERSONAL)
624 },
625 { /* 0x06 - CSIDL_FAVORITES */
626 CSIDL_Type_User,
627 FavoritesW,
628 MAKEINTRESOURCEW(IDS_FAVORITES)
629 },
630 { /* 0x07 - CSIDL_STARTUP */
631 CSIDL_Type_User,
632 StartUpW,
633 MAKEINTRESOURCEW(IDS_STARTUP)
634 },
635 { /* 0x08 - CSIDL_RECENT */
636 CSIDL_Type_User,
637 RecentW,
638 MAKEINTRESOURCEW(IDS_RECENT)
639 },
640 { /* 0x09 - CSIDL_SENDTO */
641 CSIDL_Type_User,
642 SendToW,
643 MAKEINTRESOURCEW(IDS_SENDTO)
644 },
645 { /* 0x0a - CSIDL_BITBUCKET - Recycle Bin */
646 CSIDL_Type_Disallowed,
647 NULL,
648 NULL,
649 },
650 { /* 0x0b - CSIDL_STARTMENU */
651 CSIDL_Type_User,
652 Start_MenuW,
653 MAKEINTRESOURCEW(IDS_STARTMENU)
654 },
655 { /* 0x0c - CSIDL_MYDOCUMENTS */
656 CSIDL_Type_Disallowed, /* matches WinXP--can't get its path */
657 NULL,
658 NULL
659 },
660 { /* 0x0d - CSIDL_MYMUSIC */
661 CSIDL_Type_User,
662 My_MusicW,
663 MAKEINTRESOURCEW(IDS_MYMUSIC)
664 },
665 { /* 0x0e - CSIDL_MYVIDEO */
666 CSIDL_Type_User,
667 My_VideoW,
668 MAKEINTRESOURCEW(IDS_MYVIDEO)
669 },
670 { /* 0x0f - unassigned */
671 CSIDL_Type_Disallowed,
672 NULL,
673 NULL,
674 },
675 { /* 0x10 - CSIDL_DESKTOPDIRECTORY */
676 CSIDL_Type_User,
677 DesktopW,
678 MAKEINTRESOURCEW(IDS_DESKTOPDIRECTORY)
679 },
680 { /* 0x11 - CSIDL_DRIVES */
681 CSIDL_Type_Disallowed,
682 NULL,
683 NULL,
684 },
685 { /* 0x12 - CSIDL_NETWORK */
686 CSIDL_Type_Disallowed,
687 NULL,
688 NULL,
689 },
690 { /* 0x13 - CSIDL_NETHOOD */
691 CSIDL_Type_User,
692 NetHoodW,
693 MAKEINTRESOURCEW(IDS_NETHOOD)
694 },
695 { /* 0x14 - CSIDL_FONTS */
696 CSIDL_Type_WindowsPath,
697 FontsW,
698 FontsW
699 },
700 { /* 0x15 - CSIDL_TEMPLATES */
701 CSIDL_Type_User,
702 TemplatesW,
703 MAKEINTRESOURCEW(IDS_TEMPLATES)
704 },
705 { /* 0x16 - CSIDL_COMMON_STARTMENU */
706 CSIDL_Type_AllUsers,
707 Common_Start_MenuW,
708 MAKEINTRESOURCEW(IDS_STARTMENU)
709 },
710 { /* 0x17 - CSIDL_COMMON_PROGRAMS */
711 CSIDL_Type_AllUsers,
712 Common_ProgramsW,
713 MAKEINTRESOURCEW(IDS_PROGRAMS)
714 },
715 { /* 0x18 - CSIDL_COMMON_STARTUP */
716 CSIDL_Type_AllUsers,
717 Common_StartUpW,
718 MAKEINTRESOURCEW(IDS_STARTUP)
719 },
720 { /* 0x19 - CSIDL_COMMON_DESKTOPDIRECTORY */
721 CSIDL_Type_AllUsers,
722 Common_DesktopW,
723 MAKEINTRESOURCEW(IDS_DESKTOP)
724 },
725 { /* 0x1a - CSIDL_APPDATA */
726 CSIDL_Type_User,
727 AppDataW,
728 MAKEINTRESOURCEW(IDS_APPDATA)
729 },
730 { /* 0x1b - CSIDL_PRINTHOOD */
731 CSIDL_Type_User,
732 PrintHoodW,
733 MAKEINTRESOURCEW(IDS_PRINTHOOD)
734 },
735 { /* 0x1c - CSIDL_LOCAL_APPDATA */
736 CSIDL_Type_User,
737 Local_AppDataW,
738 MAKEINTRESOURCEW(IDS_LOCAL_APPDATA)
739 },
740 { /* 0x1d - CSIDL_ALTSTARTUP */
741 CSIDL_Type_NonExistent,
742 NULL,
743 NULL
744 },
745 { /* 0x1e - CSIDL_COMMON_ALTSTARTUP */
746 CSIDL_Type_NonExistent,
747 NULL,
748 NULL
749 },
750 { /* 0x1f - CSIDL_COMMON_FAVORITES */
751 CSIDL_Type_AllUsers,
752 FavoritesW,
753 MAKEINTRESOURCEW(IDS_FAVORITES)
754 },
755 { /* 0x20 - CSIDL_INTERNET_CACHE */
756 CSIDL_Type_User,
757 CacheW,
758 MAKEINTRESOURCEW(IDS_INTERNET_CACHE)
759 },
760 { /* 0x21 - CSIDL_COOKIES */
761 CSIDL_Type_User,
762 CookiesW,
763 MAKEINTRESOURCEW(IDS_COOKIES)
764 },
765 { /* 0x22 - CSIDL_HISTORY */
766 CSIDL_Type_User,
767 HistoryW,
768 MAKEINTRESOURCEW(IDS_HISTORY)
769 },
770 { /* 0x23 - CSIDL_COMMON_APPDATA */
771 CSIDL_Type_AllUsers,
772 Common_AppDataW,
773 MAKEINTRESOURCEW(IDS_APPDATA)
774 },
775 { /* 0x24 - CSIDL_WINDOWS */
776 CSIDL_Type_WindowsPath,
777 NULL,
778 NULL
779 },
780 { /* 0x25 - CSIDL_SYSTEM */
781 CSIDL_Type_SystemPath,
782 NULL,
783 NULL
784 },
785 { /* 0x26 - CSIDL_PROGRAM_FILES */
786 CSIDL_Type_CurrVer,
787 ProgramFilesDirW,
788 MAKEINTRESOURCEW(IDS_PROGRAM_FILES)
789 },
790 { /* 0x27 - CSIDL_MYPICTURES */
791 CSIDL_Type_User,
792 My_PicturesW,
793 MAKEINTRESOURCEW(IDS_MYPICTURES)
794 },
795 { /* 0x28 - CSIDL_PROFILE */
796 CSIDL_Type_User,
797 NULL,
798 NULL
799 },
800 { /* 0x29 - CSIDL_SYSTEMX86 */
801 CSIDL_Type_NonExistent,
802 NULL,
803 NULL
804 },
805 { /* 0x2a - CSIDL_PROGRAM_FILESX86 */
806 CSIDL_Type_NonExistent,
807 NULL,
808 NULL
809 },
810 { /* 0x2b - CSIDL_PROGRAM_FILES_COMMON */
811 CSIDL_Type_CurrVer,
812 CommonFilesDirW,
813 MAKEINTRESOURCEW(IDS_PROGRAM_FILES_COMMON)
814 },
815 { /* 0x2c - CSIDL_PROGRAM_FILES_COMMONX86 */
816 CSIDL_Type_NonExistent,
817 NULL,
818 NULL
819 },
820 { /* 0x2d - CSIDL_COMMON_TEMPLATES */
821 CSIDL_Type_AllUsers,
822 Common_TemplatesW,
823 MAKEINTRESOURCEW(IDS_TEMPLATES)
824 },
825 { /* 0x2e - CSIDL_COMMON_DOCUMENTS */
826 CSIDL_Type_AllUsers,
827 Common_DocumentsW,
828 MAKEINTRESOURCEW(IDS_COMMON_DOCUMENTS)
829 },
830 { /* 0x2f - CSIDL_COMMON_ADMINTOOLS */
831 CSIDL_Type_AllUsers,
832 Common_Administrative_ToolsW,
833 MAKEINTRESOURCEW(IDS_ADMINTOOLS)
834 },
835 { /* 0x30 - CSIDL_ADMINTOOLS */
836 CSIDL_Type_User,
837 Administrative_ToolsW,
838 MAKEINTRESOURCEW(IDS_ADMINTOOLS)
839 },
840 { /* 0x31 - CSIDL_CONNECTIONS */
841 CSIDL_Type_Disallowed,
842 NULL,
843 NULL
844 },
845 { /* 0x32 - unassigned */
846 CSIDL_Type_Disallowed,
847 NULL,
848 NULL
849 },
850 { /* 0x33 - unassigned */
851 CSIDL_Type_Disallowed,
852 NULL,
853 NULL
854 },
855 { /* 0x34 - unassigned */
856 CSIDL_Type_Disallowed,
857 NULL,
858 NULL
859 },
860 { /* 0x35 - CSIDL_COMMON_MUSIC */
861 CSIDL_Type_AllUsers,
862 CommonMusicW,
863 MAKEINTRESOURCEW(IDS_COMMON_MUSIC)
864 },
865 { /* 0x36 - CSIDL_COMMON_PICTURES */
866 CSIDL_Type_AllUsers,
867 CommonPicturesW,
868 MAKEINTRESOURCEW(IDS_COMMON_PICTURES)
869 },
870 { /* 0x37 - CSIDL_COMMON_VIDEO */
871 CSIDL_Type_AllUsers,
872 CommonVideoW,
873 MAKEINTRESOURCEW(IDS_COMMON_VIDEO)
874 },
875 { /* 0x38 - CSIDL_RESOURCES */
876 CSIDL_Type_WindowsPath,
877 NULL,
878 ResourcesW
879 },
880 { /* 0x39 - CSIDL_RESOURCES_LOCALIZED */
881 CSIDL_Type_NonExistent,
882 NULL,
883 NULL
884 },
885 { /* 0x3a - CSIDL_COMMON_OEM_LINKS */
886 CSIDL_Type_NonExistent,
887 NULL,
888 NULL
889 },
890 { /* 0x3b - CSIDL_CDBURN_AREA */
891 CSIDL_Type_User,
892 CD_BurningW,
893 MAKEINTRESOURCEW(IDS_CDBURN_AREA)
894 },
895 { /* 0x3c unassigned */
896 CSIDL_Type_Disallowed,
897 NULL,
898 NULL
899 },
900 { /* 0x3d - CSIDL_COMPUTERSNEARME */
901 CSIDL_Type_Disallowed, /* FIXME */
902 NULL,
903 NULL
904 },
905 { /* 0x3e - CSIDL_PROFILES */
906 CSIDL_Type_Disallowed, /* oddly, this matches WinXP */
907 NULL,
908 NULL
909 }
910 };
911
912 /* Gets the value named value from the registry key
913 * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
914 * (or from rootKey\userPrefix\... if userPrefix is not NULL) into path, which
915 * is assumed to be MAX_PATH WCHARs in length.
916 * If it exists, expands the value and writes the expanded value to
917 * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
918 * Returns successful error code if the value was retrieved from the registry,
919 * and a failure otherwise.
920 */
921 static HRESULT _SHGetUserShellFolderPath(HKEY rootKey, LPCWSTR userPrefix,
922 LPCWSTR value, LPWSTR path)
923 {
924 HRESULT hr;
925 WCHAR shellFolderPath[MAX_PATH], userShellFolderPath[MAX_PATH];
926 LPCWSTR pShellFolderPath, pUserShellFolderPath;
927 DWORD dwDisp, dwType, dwPathLen = MAX_PATH;
928 HKEY userShellFolderKey, shellFolderKey;
929
930 TRACE("%p,%s,%s,%p\n",rootKey, debugstr_w(userPrefix), debugstr_w(value),
931 path);
932
933 if (userPrefix)
934 {
935 wcscpy(shellFolderPath, userPrefix);
936 PathAddBackslashW(shellFolderPath);
937 wcscat(shellFolderPath, szSHFolders);
938 pShellFolderPath = shellFolderPath;
939 wcscpy(userShellFolderPath, userPrefix);
940 PathAddBackslashW(userShellFolderPath);
941 wcscat(userShellFolderPath, szSHUserFolders);
942 pUserShellFolderPath = userShellFolderPath;
943 }
944 else
945 {
946 pUserShellFolderPath = szSHUserFolders;
947 pShellFolderPath = szSHFolders;
948 }
949
950 if (RegCreateKeyExW(rootKey, pShellFolderPath, 0, NULL, 0, KEY_ALL_ACCESS,
951 NULL, &shellFolderKey, &dwDisp))
952 {
953 TRACE("Failed to create %s\n", debugstr_w(pShellFolderPath));
954 return E_FAIL;
955 }
956 if (RegCreateKeyExW(rootKey, pUserShellFolderPath, 0, NULL, 0,
957 KEY_ALL_ACCESS, NULL, &userShellFolderKey, &dwDisp))
958 {
959 TRACE("Failed to create %s\n",
960 debugstr_w(pUserShellFolderPath));
961 RegCloseKey(shellFolderKey);
962 return E_FAIL;
963 }
964
965 if (!RegQueryValueExW(userShellFolderKey, value, NULL, &dwType,
966 (LPBYTE)path, &dwPathLen) && (dwType == REG_EXPAND_SZ || dwType == REG_SZ))
967 {
968 LONG ret;
969
970 path[dwPathLen / sizeof(WCHAR)] = '\0';
971 if (dwType == REG_EXPAND_SZ && path[0] == '%')
972 {
973 WCHAR szTemp[MAX_PATH];
974
975 ExpandEnvironmentStringsW(path, szTemp, MAX_PATH);
976 lstrcpynW(path, szTemp, MAX_PATH);
977 }
978 ret = RegSetValueExW(shellFolderKey, value, 0, REG_SZ, (LPBYTE)path,
979 (wcslen(path) + 1) * sizeof(WCHAR));
980 if (ret != ERROR_SUCCESS)
981 hr = HRESULT_FROM_WIN32(ret);
982 else
983 hr = S_OK;
984 }
985 else
986 hr = E_FAIL;
987 RegCloseKey(shellFolderKey);
988 RegCloseKey(userShellFolderKey);
989 TRACE("returning 0x%08x\n", hr);
990 return hr;
991 }
992
993 /* Gets a 'semi-expanded' default value of the CSIDL with index folder into
994 * pszPath, based on the entries in CSIDL_Data. By semi-expanded, I mean:
995 * - The entry's szDefaultPath may be either a string value or an integer
996 * resource identifier. In the latter case, the string value of the resource
997 * is written.
998 * - Depending on the entry's type, the path may begin with an (unexpanded)
999 * environment variable name. The caller is responsible for expanding
1000 * environment strings if so desired.
1001 * The types that are prepended with environment variables are:
1002 * CSIDL_Type_User: %USERPROFILE%
1003 * CSIDL_Type_AllUsers: %ALLUSERSPROFILE%
1004 * CSIDL_Type_CurrVer: %SystemDrive%
1005 * (Others might make sense too, but as yet are unneeded.)
1006 */
1007 static HRESULT _SHGetDefaultValue(BYTE folder, LPWSTR pszPath)
1008 {
1009 HRESULT hr;
1010 WCHAR resourcePath[MAX_PATH];
1011 LPCWSTR pDefaultPath = NULL;
1012
1013 TRACE("0x%02x,%p\n", folder, pszPath);
1014
1015 if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1016 return E_INVALIDARG;
1017 if (!pszPath)
1018 return E_INVALIDARG;
1019
1020 if (CSIDL_Data[folder].szDefaultPath &&
1021 IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath))
1022 {
1023 if (LoadStringW(shell32_hInstance,
1024 LOWORD(CSIDL_Data[folder].szDefaultPath), resourcePath, MAX_PATH))
1025 {
1026 hr = S_OK;
1027 pDefaultPath = resourcePath;
1028 }
1029 else
1030 {
1031 FIXME("(%d,%s), LoadString failed, missing translation?\n", folder,
1032 debugstr_w(pszPath));
1033 hr = E_FAIL;
1034 }
1035 }
1036 else
1037 {
1038 hr = S_OK;
1039 pDefaultPath = CSIDL_Data[folder].szDefaultPath;
1040 }
1041 if (SUCCEEDED(hr))
1042 {
1043 switch (CSIDL_Data[folder].type)
1044 {
1045 case CSIDL_Type_User:
1046 wcscpy(pszPath, UserProfileW);
1047 break;
1048 case CSIDL_Type_AllUsers:
1049 wcscpy(pszPath, AllUsersProfileW);
1050 break;
1051 case CSIDL_Type_CurrVer:
1052 wcscpy(pszPath, SystemDriveW);
1053 break;
1054 default:
1055 ; /* no corresponding env. var, do nothing */
1056 }
1057 if (pDefaultPath)
1058 {
1059 PathAddBackslashW(pszPath);
1060 wcscat(pszPath, pDefaultPath);
1061 }
1062 }
1063 TRACE("returning 0x%08x\n", hr);
1064 return hr;
1065 }
1066
1067 /* Gets the (unexpanded) value of the folder with index folder into pszPath.
1068 * The folder's type is assumed to be CSIDL_Type_CurrVer. Its default value
1069 * can be overridden in the HKLM\\szCurrentVersion key.
1070 * If dwFlags has SHGFP_TYPE_DEFAULT set or if the value isn't overridden in
1071 * the registry, uses _SHGetDefaultValue to get the value.
1072 */
1073 static HRESULT _SHGetCurrentVersionPath(DWORD dwFlags, BYTE folder,
1074 LPWSTR pszPath)
1075 {
1076 HRESULT hr;
1077
1078 TRACE("0x%08x,0x%02x,%p\n", dwFlags, folder, pszPath);
1079
1080 if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1081 return E_INVALIDARG;
1082 if (CSIDL_Data[folder].type != CSIDL_Type_CurrVer)
1083 return E_INVALIDARG;
1084 if (!pszPath)
1085 return E_INVALIDARG;
1086
1087 if (dwFlags & SHGFP_TYPE_DEFAULT)
1088 hr = _SHGetDefaultValue(folder, pszPath);
1089 else
1090 {
1091 HKEY hKey;
1092 DWORD dwDisp;
1093
1094 if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, szCurrentVersion, 0,
1095 NULL, 0, KEY_ALL_ACCESS, NULL, &hKey, &dwDisp))
1096 hr = E_FAIL;
1097 else
1098 {
1099 DWORD dwType, dwPathLen = MAX_PATH * sizeof(WCHAR);
1100
1101 if (RegQueryValueExW(hKey, CSIDL_Data[folder].szValueName, NULL,
1102 &dwType, (LPBYTE)pszPath, &dwPathLen) ||
1103 (dwType != REG_SZ && dwType != REG_EXPAND_SZ))
1104 {
1105 hr = _SHGetDefaultValue(folder, pszPath);
1106 dwType = REG_EXPAND_SZ;
1107 RegSetValueExW(hKey, CSIDL_Data[folder].szValueName, 0, dwType,
1108 (LPBYTE)pszPath, (wcslen(pszPath)+1)*sizeof(WCHAR));
1109 }
1110 else
1111 {
1112 pszPath[dwPathLen / sizeof(WCHAR)] = '\0';
1113 hr = S_OK;
1114 }
1115 RegCloseKey(hKey);
1116 }
1117 }
1118 TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
1119 return hr;
1120 }
1121
1122 /* Gets the user's path (unexpanded) for the CSIDL with index folder:
1123 * If SHGFP_TYPE_DEFAULT is set, calls _SHGetDefaultValue for it. Otherwise
1124 * calls _SHGetUserShellFolderPath for it. Where it looks depends on hToken:
1125 * - if hToken is -1, looks in HKEY_USERS\.Default
1126 * - otherwise looks first in HKEY_CURRENT_USER, followed by HKEY_LOCAL_MACHINE
1127 * if HKEY_CURRENT_USER doesn't contain any entries. If both fail, finally
1128 * calls _SHGetDefaultValue for it.
1129 */
1130 static HRESULT _SHGetUserProfilePath(HANDLE hToken, DWORD dwFlags, BYTE folder,
1131 LPWSTR pszPath)
1132 {
1133 HRESULT hr;
1134
1135 TRACE("%p,0x%08x,0x%02x,%p\n", hToken, dwFlags, folder, pszPath);
1136
1137 if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1138 return E_INVALIDARG;
1139
1140 if (CSIDL_Data[folder].type != CSIDL_Type_User)
1141 return E_INVALIDARG;
1142
1143 if (!pszPath)
1144 return E_INVALIDARG;
1145
1146 if (dwFlags & SHGFP_TYPE_DEFAULT)
1147 {
1148 hr = _SHGetDefaultValue(folder, pszPath);
1149 }
1150 else
1151 {
1152 LPWSTR userPrefix;
1153 HKEY hRootKey;
1154
1155 if (hToken == (HANDLE)-1)
1156 {
1157 /* Get the folder of the default user */
1158 hRootKey = HKEY_USERS;
1159 userPrefix = (LPWSTR)DefaultW;
1160 }
1161 else if(!hToken)
1162 {
1163 /* Get the folder of the current user */
1164 hRootKey = HKEY_CURRENT_USER;
1165 userPrefix = NULL;
1166 }
1167 else
1168 {
1169 /* Get the folder of the specified user */
1170 DWORD InfoLength;
1171 PTOKEN_USER UserInfo;
1172
1173 hRootKey = HKEY_USERS;
1174
1175 GetTokenInformation(hToken, TokenUser, NULL, 0, &InfoLength);
1176 UserInfo = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), 0, InfoLength);
1177
1178 if(!GetTokenInformation(hToken, TokenUser, UserInfo, InfoLength, &InfoLength))
1179 {
1180 WARN("GetTokenInformation failed for %x!\n", hToken);
1181 HeapFree(GetProcessHeap(), 0, UserInfo);
1182 return E_FAIL;
1183 }
1184
1185 if(!ConvertSidToStringSidW(UserInfo->User.Sid, &userPrefix))
1186 {
1187 WARN("ConvertSidToStringSidW failed for %x!\n", hToken);
1188 HeapFree(GetProcessHeap(), 0, UserInfo);
1189 return E_FAIL;
1190 }
1191
1192 HeapFree(GetProcessHeap(), 0, UserInfo);
1193 }
1194
1195 hr = _SHGetUserShellFolderPath(hRootKey, userPrefix, CSIDL_Data[folder].szValueName, pszPath);
1196
1197 /* Free the memory allocated by ConvertSidToStringSidW */
1198 if(hToken && hToken != (HANDLE)-1)
1199 LocalFree(userPrefix);
1200
1201 if (FAILED(hr) && hRootKey != HKEY_LOCAL_MACHINE)
1202 hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL, CSIDL_Data[folder].szValueName, pszPath);
1203
1204 if (FAILED(hr))
1205 hr = _SHGetDefaultValue(folder, pszPath);
1206 }
1207
1208 TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
1209 return hr;
1210 }
1211
1212 /* Gets the (unexpanded) path for the CSIDL with index folder. If dwFlags has
1213 * SHGFP_TYPE_DEFAULT set, calls _SHGetDefaultValue. Otherwise calls
1214 * _SHGetUserShellFolderPath for it, looking only in HKEY_LOCAL_MACHINE.
1215 * If this fails, falls back to _SHGetDefaultValue.
1216 */
1217 static HRESULT _SHGetAllUsersProfilePath(DWORD dwFlags, BYTE folder,
1218 LPWSTR pszPath)
1219 {
1220 HRESULT hr;
1221
1222 TRACE("0x%08x,0x%02x,%p\n", dwFlags, folder, pszPath);
1223
1224 if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1225 return E_INVALIDARG;
1226 if (CSIDL_Data[folder].type != CSIDL_Type_AllUsers)
1227 return E_INVALIDARG;
1228 if (!pszPath)
1229 return E_INVALIDARG;
1230
1231 if (dwFlags & SHGFP_TYPE_DEFAULT)
1232 hr = _SHGetDefaultValue(folder, pszPath);
1233 else
1234 {
1235 hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL,
1236 CSIDL_Data[folder].szValueName, pszPath);
1237 if (FAILED(hr))
1238 hr = _SHGetDefaultValue(folder, pszPath);
1239 }
1240 TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
1241 return hr;
1242 }
1243
1244 /*************************************************************************
1245 * SHGetFolderPathW [SHELL32.@]
1246 *
1247 * Convert nFolder to path.
1248 *
1249 * RETURNS
1250 * Success: S_OK
1251 * Failure: standard HRESULT error codes.
1252 *
1253 * NOTES
1254 * Most values can be overridden in either
1255 * HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
1256 * or in the same location in HKLM.
1257 * The "Shell Folders" registry key was used in NT4 and earlier systems.
1258 * Beginning with Windows 2000, the "User Shell Folders" key is used, so
1259 * changes made to it are made to the former key too. This synchronization is
1260 * done on-demand: not until someone requests the value of one of these paths
1261 * (by calling one of the SHGet functions) is the value synchronized.
1262 * Furthermore, the HKCU paths take precedence over the HKLM paths.
1263 */
1264 HRESULT WINAPI SHGetFolderPathW(
1265 HWND hwndOwner, /* [I] owner window */
1266 int nFolder, /* [I] CSIDL identifying the folder */
1267 HANDLE hToken, /* [I] access token */
1268 DWORD dwFlags, /* [I] which path to return */
1269 LPWSTR pszPath) /* [O] converted path */
1270 {
1271 HRESULT hr = SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, NULL, pszPath);
1272 if(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
1273 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
1274 return hr;
1275 }
1276
1277 HRESULT WINAPI SHGetFolderPathAndSubDirA(
1278 HWND hwndOwner, /* [I] owner window */
1279 int nFolder, /* [I] CSIDL identifying the folder */
1280 HANDLE hToken, /* [I] access token */
1281 DWORD dwFlags, /* [I] which path to return */
1282 LPCSTR pszSubPath, /* [I] sub directory of the specified folder */
1283 LPSTR pszPath) /* [O] converted path */
1284 {
1285 int length;
1286 HRESULT hr = S_OK;
1287 LPWSTR pszSubPathW = NULL;
1288 LPWSTR pszPathW = NULL;
1289 TRACE("%08x,%08x,%s\n",nFolder, dwFlags, debugstr_w(pszSubPathW));
1290
1291 if(pszPath) {
1292 pszPathW = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
1293 if(!pszPathW) {
1294 hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
1295 goto cleanup;
1296 }
1297 }
1298 TRACE("%08x,%08x,%s\n",nFolder, dwFlags, debugstr_w(pszSubPathW));
1299
1300 /* SHGetFolderPathAndSubDirW does not distinguish if pszSubPath isn't
1301 * set (null), or an empty string.therefore call it without the parameter set
1302 * if pszSubPath is an empty string
1303 */
1304 if (pszSubPath && pszSubPath[0]) {
1305 length = MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, NULL, 0);
1306 pszSubPathW = HeapAlloc(GetProcessHeap(), 0, length * sizeof(WCHAR));
1307 if(!pszSubPathW) {
1308 hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
1309 goto cleanup;
1310 }
1311 MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, pszSubPathW, length);
1312 }
1313
1314 hr = SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, pszSubPathW, pszPathW);
1315
1316 if (SUCCEEDED(hr) && pszPath)
1317 WideCharToMultiByte(CP_ACP, 0, pszPathW, -1, pszPath, MAX_PATH, NULL, NULL);
1318
1319 cleanup:
1320 HeapFree(GetProcessHeap(), 0, pszPathW);
1321 HeapFree(GetProcessHeap(), 0, pszSubPathW);
1322 return hr;
1323 }
1324
1325 HRESULT WINAPI SHGetFolderPathAndSubDirW(
1326 HWND hwndOwner, /* [I] owner window */
1327 int nFolder, /* [I] CSIDL identifying the folder */
1328 HANDLE hToken, /* [I] access token */
1329 DWORD dwFlags, /* [I] which path to return */
1330 LPCWSTR pszSubPath,/* [I] sub directory of the specified folder */
1331 LPWSTR pszPath) /* [O] converted path */
1332 {
1333 HRESULT hr;
1334 WCHAR szBuildPath[MAX_PATH], szTemp[MAX_PATH];
1335 DWORD folder = nFolder & CSIDL_FOLDER_MASK; //FIXME
1336 CSIDL_Type type;
1337 int ret;
1338
1339 TRACE("%p,%p,nFolder=0x%04x,%s\n", hwndOwner,pszPath,nFolder,debugstr_w(pszSubPath));
1340
1341 /* Windows always NULL-terminates the resulting path regardless of success
1342 * or failure, so do so first
1343 */
1344 if (pszPath)
1345 *pszPath = '\0';
1346
1347 if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1348 return E_INVALIDARG;
1349 if ((SHGFP_TYPE_CURRENT != dwFlags) && (SHGFP_TYPE_DEFAULT != dwFlags))
1350 return E_INVALIDARG;
1351 szTemp[0] = 0;
1352 type = CSIDL_Data[folder].type;
1353 switch (type)
1354 {
1355 case CSIDL_Type_Disallowed:
1356 hr = E_INVALIDARG;
1357 break;
1358 case CSIDL_Type_NonExistent:
1359 hr = S_FALSE;
1360 break;
1361 case CSIDL_Type_WindowsPath:
1362 GetWindowsDirectoryW(szTemp, MAX_PATH);
1363 if (CSIDL_Data[folder].szDefaultPath &&
1364 !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
1365 *CSIDL_Data[folder].szDefaultPath)
1366 {
1367 PathAddBackslashW(szTemp);
1368 wcscat(szTemp, CSIDL_Data[folder].szDefaultPath);
1369 }
1370 hr = S_OK;
1371 break;
1372 case CSIDL_Type_SystemPath:
1373 GetSystemDirectoryW(szTemp, MAX_PATH);
1374 if (CSIDL_Data[folder].szDefaultPath &&
1375 !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
1376 *CSIDL_Data[folder].szDefaultPath)
1377 {
1378 PathAddBackslashW(szTemp);
1379 wcscat(szTemp, CSIDL_Data[folder].szDefaultPath);
1380 }
1381 hr = S_OK;
1382 break;
1383 case CSIDL_Type_CurrVer:
1384 hr = _SHGetCurrentVersionPath(dwFlags, folder, szTemp);
1385 break;
1386 case CSIDL_Type_User:
1387 hr = _SHGetUserProfilePath(hToken, dwFlags, folder, szTemp);
1388 break;
1389 case CSIDL_Type_AllUsers:
1390 hr = _SHGetAllUsersProfilePath(dwFlags, folder, szTemp);
1391 break;
1392 default:
1393 FIXME("bogus type %d, please fix\n", type);
1394 hr = E_INVALIDARG;
1395 break;
1396 }
1397
1398 /* Expand environment strings if necessary */
1399 if (*szTemp == '%')
1400 {
1401 DWORD ExpandRet = ExpandEnvironmentStringsW(szTemp, szBuildPath, MAX_PATH);
1402
1403 if (ExpandRet > MAX_PATH)
1404 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
1405 else if (ExpandRet == 0)
1406 hr = HRESULT_FROM_WIN32(GetLastError());
1407 else
1408 hr = S_OK;
1409 }
1410 else
1411 {
1412 wcscpy(szBuildPath, szTemp);
1413 }
1414
1415 if (FAILED(hr)) goto end;
1416
1417 if(pszSubPath) {
1418 /* make sure the new path does not exceed th bufferlength
1419 * rememebr to backslash and the termination */
1420 if(MAX_PATH < (wcslen(szBuildPath) + wcslen(pszSubPath) + 2)) {
1421 hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
1422 goto end;
1423 }
1424 PathAppendW(szBuildPath, pszSubPath);
1425 PathRemoveBackslashW(szBuildPath);
1426 }
1427 /* Copy the path if it's available before we might return */
1428 if (SUCCEEDED(hr) && pszPath)
1429 wcscpy(pszPath, szBuildPath);
1430
1431 /* if we don't care about existing directories we are ready */
1432 if(nFolder & CSIDL_FLAG_DONT_VERIFY) goto end;
1433
1434 if (PathFileExistsW(szBuildPath)) goto end;
1435
1436 /* not existing but we are not allowed to create it. The return value
1437 * is verified against shell32 version 6.0.
1438 */
1439 if (!(nFolder & CSIDL_FLAG_CREATE))
1440 {
1441 hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
1442 goto end;
1443 }
1444
1445 /* create directory/directories */
1446 ret = SHCreateDirectoryExW(hwndOwner, szBuildPath, NULL);
1447 if (ret && ret != ERROR_ALREADY_EXISTS)
1448 {
1449 ERR("Failed to create directory %s.\n", debugstr_w(szBuildPath));
1450 hr = E_FAIL;
1451 goto end;
1452 }
1453
1454 TRACE("Created missing system directory %s\n", debugstr_w(szBuildPath));
1455 end:
1456 TRACE("returning 0x%08x (final path is %s)\n", hr, debugstr_w(szBuildPath));
1457 return hr;
1458 }
1459
1460 /*************************************************************************
1461 * SHGetFolderPathA [SHELL32.@]
1462 *
1463 * See SHGetFolderPathW.
1464 */
1465 HRESULT WINAPI SHGetFolderPathA(
1466 HWND hwndOwner,
1467 int nFolder,
1468 HANDLE hToken,
1469 DWORD dwFlags,
1470 LPSTR pszPath)
1471 {
1472 WCHAR szTemp[MAX_PATH];
1473 HRESULT hr;
1474
1475 TRACE("%p,%p,nFolder=0x%04x\n",hwndOwner,pszPath,nFolder);
1476
1477 if (pszPath)
1478 *pszPath = '\0';
1479 hr = SHGetFolderPathW(hwndOwner, nFolder, hToken, dwFlags, szTemp);
1480 if (SUCCEEDED(hr) && pszPath)
1481 WideCharToMultiByte(CP_ACP, 0, szTemp, -1, pszPath, MAX_PATH, NULL,
1482 NULL);
1483
1484 return hr;
1485 }
1486
1487 /* For each folder in folders, if its value has not been set in the registry,
1488 * calls _SHGetUserProfilePath or _SHGetAllUsersProfilePath (depending on the
1489 * folder's type) to get the unexpanded value first.
1490 * Writes the unexpanded value to User Shell Folders, and queries it with
1491 * SHGetFolderPathW to force the creation of the directory if it doesn't
1492 * already exist. SHGetFolderPathW also returns the expanded value, which
1493 * this then writes to Shell Folders.
1494 */
1495 static HRESULT _SHRegisterFolders(HKEY hRootKey, HANDLE hToken,
1496 LPCWSTR szUserShellFolderPath, LPCWSTR szShellFolderPath, const UINT folders[],
1497 UINT foldersLen)
1498 {
1499 UINT i;
1500 WCHAR path[MAX_PATH];
1501 HRESULT hr = S_OK;
1502 HKEY hUserKey = NULL, hKey = NULL;
1503 DWORD dwDisp, dwType, dwPathLen;
1504 LONG ret;
1505
1506 TRACE("%p,%p,%s,%p,%u\n", hRootKey, hToken,
1507 debugstr_w(szUserShellFolderPath), folders, foldersLen);
1508
1509 ret = RegCreateKeyExW(hRootKey, szUserShellFolderPath, 0, NULL, 0,
1510 KEY_ALL_ACCESS, NULL, &hUserKey, &dwDisp);
1511 if (ret)
1512 hr = HRESULT_FROM_WIN32(ret);
1513 else
1514 {
1515 ret = RegCreateKeyExW(hRootKey, szShellFolderPath, 0, NULL, 0,
1516 KEY_ALL_ACCESS, NULL, &hKey, &dwDisp);
1517 if (ret)
1518 hr = HRESULT_FROM_WIN32(ret);
1519 }
1520 for (i = 0; SUCCEEDED(hr) && i < foldersLen; i++)
1521 {
1522 dwPathLen = MAX_PATH * sizeof(WCHAR);
1523 if (RegQueryValueExW(hUserKey, CSIDL_Data[folders[i]].szValueName, NULL,
1524 &dwType, (LPBYTE)path, &dwPathLen) || (dwType != REG_SZ &&
1525 dwType != REG_EXPAND_SZ))
1526 {
1527 *path = '\0';
1528 if (CSIDL_Data[folders[i]].type == CSIDL_Type_User)
1529 _SHGetUserProfilePath(hToken, SHGFP_TYPE_DEFAULT, folders[i],
1530 path);
1531 else if (CSIDL_Data[folders[i]].type == CSIDL_Type_AllUsers)
1532 _SHGetAllUsersProfilePath(SHGFP_TYPE_DEFAULT, folders[i], path);
1533 else if (CSIDL_Data[folders[i]].type == CSIDL_Type_WindowsPath)
1534 GetWindowsDirectoryW(path, MAX_PATH);
1535 else
1536 hr = E_FAIL;
1537 if (*path)
1538 {
1539 ret = RegSetValueExW(hUserKey,
1540 CSIDL_Data[folders[i]].szValueName, 0, REG_EXPAND_SZ,
1541 (LPBYTE)path, (wcslen(path) + 1) * sizeof(WCHAR));
1542 if (ret)
1543 hr = HRESULT_FROM_WIN32(ret);
1544 else
1545 {
1546 hr = SHGetFolderPathW(NULL, folders[i] | CSIDL_FLAG_CREATE,
1547 hToken, SHGFP_TYPE_DEFAULT, path);
1548 ret = RegSetValueExW(hKey,
1549 CSIDL_Data[folders[i]].szValueName, 0, REG_SZ,
1550 (LPBYTE)path, (wcslen(path) + 1) * sizeof(WCHAR));
1551 if (ret)
1552 hr = HRESULT_FROM_WIN32(ret);
1553 }
1554 }
1555 }
1556 }
1557 if (hUserKey)
1558 RegCloseKey(hUserKey);
1559 if (hKey)
1560 RegCloseKey(hKey);
1561
1562 TRACE("returning 0x%08x\n", hr);
1563 return hr;
1564 }
1565
1566 static HRESULT _SHRegisterUserShellFolders(BOOL bDefault)
1567 {
1568 static const UINT folders[] = {
1569 CSIDL_PROGRAMS,
1570 CSIDL_PERSONAL,
1571 CSIDL_FAVORITES,
1572 CSIDL_APPDATA,
1573 CSIDL_STARTUP,
1574 CSIDL_RECENT,
1575 CSIDL_SENDTO,
1576 CSIDL_STARTMENU,
1577 CSIDL_MYMUSIC,
1578 CSIDL_MYVIDEO,
1579 CSIDL_DESKTOPDIRECTORY,
1580 CSIDL_NETHOOD,
1581 CSIDL_TEMPLATES,
1582 CSIDL_PRINTHOOD,
1583 CSIDL_LOCAL_APPDATA,
1584 CSIDL_INTERNET_CACHE,
1585 CSIDL_COOKIES,
1586 CSIDL_HISTORY,
1587 CSIDL_MYPICTURES,
1588 CSIDL_FONTS
1589 };
1590 WCHAR userShellFolderPath[MAX_PATH], shellFolderPath[MAX_PATH];
1591 LPCWSTR pUserShellFolderPath, pShellFolderPath;
1592 HRESULT hr = S_OK;
1593 HKEY hRootKey;
1594 HANDLE hToken;
1595
1596 TRACE("%s\n", bDefault ? "TRUE" : "FALSE");
1597 if (bDefault)
1598 {
1599 hToken = (HANDLE)-1;
1600 hRootKey = HKEY_USERS;
1601 wcscpy(userShellFolderPath, DefaultW);
1602 PathAddBackslashW(userShellFolderPath);
1603 wcscat(userShellFolderPath, szSHUserFolders);
1604 pUserShellFolderPath = userShellFolderPath;
1605 wcscpy(shellFolderPath, DefaultW);
1606 PathAddBackslashW(shellFolderPath);
1607 wcscat(shellFolderPath, szSHFolders);
1608 pShellFolderPath = shellFolderPath;
1609 }
1610 else
1611 {
1612 hToken = NULL;
1613 hRootKey = HKEY_CURRENT_USER;
1614 pUserShellFolderPath = szSHUserFolders;
1615 pShellFolderPath = szSHFolders;
1616 }
1617
1618 hr = _SHRegisterFolders(hRootKey, hToken, pUserShellFolderPath,
1619 pShellFolderPath, folders, sizeof(folders) / sizeof(folders[0]));
1620 TRACE("returning 0x%08x\n", hr);
1621 return hr;
1622 }
1623
1624 static HRESULT _SHRegisterCommonShellFolders(void)
1625 {
1626 static const UINT folders[] = {
1627 CSIDL_COMMON_STARTMENU,
1628 CSIDL_COMMON_PROGRAMS,
1629 CSIDL_COMMON_STARTUP,
1630 CSIDL_COMMON_DESKTOPDIRECTORY,
1631 CSIDL_COMMON_FAVORITES,
1632 CSIDL_COMMON_APPDATA,
1633 CSIDL_COMMON_TEMPLATES,
1634 CSIDL_COMMON_DOCUMENTS,
1635 };
1636 HRESULT hr;
1637
1638 TRACE("\n");
1639 hr = _SHRegisterFolders(HKEY_LOCAL_MACHINE, NULL, szSHUserFolders,
1640 szSHFolders, folders, sizeof(folders) / sizeof(folders[0]));
1641 TRACE("returning 0x%08x\n", hr);
1642 return hr;
1643 }
1644
1645 /******************************************************************************
1646 * _SHAppendToUnixPath [Internal]
1647 *
1648 * Helper function for _SHCreateSymbolicLinks. Appends pwszSubPath (or the
1649 * corresponding resource, if IS_INTRESOURCE) to the unix base path 'szBasePath'
1650 * and replaces backslashes with slashes.
1651 *
1652 * PARAMS
1653 * szBasePath [IO] The unix base path, which will be appended to (CP_UNXICP).
1654 * pwszSubPath [I] Sub-path or resource id (use MAKEINTRESOURCEW).
1655 *
1656 * RETURNS
1657 * Success: TRUE,
1658 * Failure: FALSE
1659 */
1660 static BOOL __inline _SHAppendToUnixPath(char *szBasePath, LPCWSTR pwszSubPath) {
1661 WCHAR wszSubPath[MAX_PATH];
1662 int cLen = strlen(szBasePath);
1663 char *pBackslash;
1664
1665 if (IS_INTRESOURCE(pwszSubPath)) {
1666 if (!LoadStringW(shell32_hInstance, LOWORD(pwszSubPath), wszSubPath, MAX_PATH)) {
1667 /* Fall back to hard coded defaults. */
1668 switch (LOWORD(pwszSubPath)) {
1669 case IDS_PERSONAL:
1670 wcscpy(wszSubPath, PersonalW);
1671 break;
1672 case IDS_MYMUSIC:
1673 wcscpy(wszSubPath, My_MusicW);
1674 break;
1675 case IDS_MYPICTURES:
1676 wcscpy(wszSubPath, My_PicturesW);
1677 break;
1678 case IDS_MYVIDEO:
1679 wcscpy(wszSubPath, My_VideoW);
1680 break;
1681 default:
1682 ERR("LoadString(%d) failed!\n", LOWORD(pwszSubPath));
1683 return FALSE;
1684 }
1685 }
1686 } else {
1687 wcscpy(wszSubPath, pwszSubPath);
1688 }
1689
1690 if (szBasePath[cLen-1] != '/') szBasePath[cLen++] = '/';
1691
1692 if (!WideCharToMultiByte(CP_ACP, 0, wszSubPath, -1, szBasePath + cLen,
1693 FILENAME_MAX - cLen, NULL, NULL))
1694 {
1695 return FALSE;
1696 }
1697
1698 pBackslash = szBasePath + cLen;
1699 while ((pBackslash = strchr(pBackslash, '\\'))) *pBackslash = '/';
1700
1701 return TRUE;
1702 }
1703 #if 0
1704 /******************************************************************************
1705 * _SHCreateSymbolicLinks [Internal]
1706 *
1707 * Sets up symbol links for various shell folders to point into the users home
1708 * directory. We do an educated guess about what the user would probably want:
1709 * - If there is a 'My Documents' directory in $HOME, the user probably wants
1710 * wine's 'My Documents' to point there. Furthermore, we imply that the user
1711 * is a Windows lover and has no problem with wine creating 'My Pictures',
1712 * 'My Music' and 'My Video' subfolders under '$HOME/My Documents', if those
1713 * do not already exits. We put appropriate symbolic links in place for those,
1714 * too.
1715 * - If there is no 'My Documents' directory in $HOME, we let 'My Documents'
1716 * point directly to $HOME. We assume the user to be a unix hacker who does not
1717 * want wine to create anything anywhere besides the .wine directory. So, if
1718 * there already is a 'My Music' directory in $HOME, we symlink the 'My Music'
1719 * shell folder to it. But if not, we symlink it to $HOME directly. The same
1720 * holds fo 'My Pictures' and 'My Video'.
1721 * - The Desktop shell folder is symlinked to '$HOME/Desktop', if that does
1722 * exists and left alone if not.
1723 * ('My Music',... above in fact means LoadString(IDS_MYMUSIC))
1724 */
1725 static void _SHCreateSymbolicLinks(void)
1726 {
1727 UINT aidsMyStuff[] = { IDS_MYPICTURES, IDS_MYVIDEO, IDS_MYMUSIC }, i;
1728 int acsidlMyStuff[] = { CSIDL_MYPICTURES, CSIDL_MYVIDEO, CSIDL_MYMUSIC };
1729 WCHAR wszTempPath[MAX_PATH];
1730 char szPersonalTarget[FILENAME_MAX], *pszPersonal;
1731 char szMyStuffTarget[FILENAME_MAX], *pszMyStuff;
1732 char szDesktopTarget[FILENAME_MAX], *pszDesktop;
1733 struct stat statFolder;
1734 const char *pszHome;
1735 HRESULT hr;
1736
1737 /* Create all necessary profile sub-dirs up to 'My Documents' and get the unix path. */
1738 hr = SHGetFolderPathW(NULL, CSIDL_PERSONAL|CSIDL_FLAG_CREATE, NULL,
1739 SHGFP_TYPE_DEFAULT, wszTempPath);
1740 if (FAILED(hr)) return;
1741 pszPersonal = wine_get_unix_file_name(wszTempPath);
1742 if (!pszPersonal) return;
1743
1744 pszHome = getenv("HOME");
1745 if (pszHome && !stat(pszHome, &statFolder) && S_ISDIR(statFolder.st_mode)) {
1746 strcpy(szPersonalTarget, pszHome);
1747 if (_SHAppendToUnixPath(szPersonalTarget, MAKEINTRESOURCEW(IDS_PERSONAL)) &&
1748 !stat(szPersonalTarget, &statFolder) && S_ISDIR(statFolder.st_mode))
1749 {
1750 /* '$HOME/My Documents' exists. Create 'My Pictures', 'My Videos' and
1751 * 'My Music' subfolders or fail silently if they already exist. */
1752 for (i = 0; i < sizeof(aidsMyStuff)/sizeof(aidsMyStuff[0]); i++) {
1753 strcpy(szMyStuffTarget, szPersonalTarget);
1754 if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])))
1755 mkdir(szMyStuffTarget);
1756 }
1757 }
1758 else
1759 {
1760 /* '$HOME/My Documents' doesn't exists, but '$HOME' does. */
1761 strcpy(szPersonalTarget, pszHome);
1762 }
1763
1764 /* Replace 'My Documents' directory with a symlink of fail silently if not empty. */
1765 rmdir(pszPersonal);
1766 symlink(szPersonalTarget, pszPersonal);
1767 }
1768 else
1769 {
1770 /* '$HOME' doesn't exist. Create 'My Pictures', 'My Videos' and 'My Music' subdirs
1771 * in '%USERPROFILE%\\My Documents' or fail silently if they already exist. */
1772 strcpy(szPersonalTarget, pszPersonal);
1773 for (i = 0; i < sizeof(aidsMyStuff)/sizeof(aidsMyStuff[0]); i++) {
1774 strcpy(szMyStuffTarget, szPersonalTarget);
1775 if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])))
1776 mkdir(szMyStuffTarget);
1777 }
1778 }
1779
1780 /* Create symbolic links for 'My Pictures', 'My Video' and 'My Music'. */
1781 for (i=0; i < sizeof(aidsMyStuff)/sizeof(aidsMyStuff[0]); i++) {
1782 /* Create the current 'My Whatever' folder and get it's unix path. */
1783 hr = SHGetFolderPathW(NULL, acsidlMyStuff[i]|CSIDL_FLAG_CREATE, NULL,
1784 SHGFP_TYPE_DEFAULT, wszTempPath);
1785 if (FAILED(hr)) continue;
1786 pszMyStuff = wine_get_unix_file_name(wszTempPath);
1787 if (!pszMyStuff) continue;
1788
1789 strcpy(szMyStuffTarget, szPersonalTarget);
1790 if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])) &&
1791 !stat(szMyStuffTarget, &statFolder) && S_ISDIR(statFolder.st_mode))
1792 {
1793 /* If there's a 'My Whatever' directory where 'My Documents' links to, link to it. */
1794 rmdir(pszMyStuff);
1795 symlink(szMyStuffTarget, pszMyStuff);
1796 }
1797 else
1798 {
1799 /* Else link to where 'My Documents' itself links to. */
1800 rmdir(pszMyStuff);
1801 symlink(szPersonalTarget, pszMyStuff);
1802 }
1803 HeapFree(GetProcessHeap(), 0, pszMyStuff);
1804 }
1805
1806 /* Last but not least, the Desktop folder */
1807 if (pszHome)
1808 strcpy(szDesktopTarget, pszHome);
1809 else
1810 strcpy(szDesktopTarget, pszPersonal);
1811 HeapFree(GetProcessHeap(), 0, pszPersonal);
1812
1813 if (_SHAppendToUnixPath(szDesktopTarget, DesktopW) &&
1814 !stat(szDesktopTarget, &statFolder) && S_ISDIR(statFolder.st_mode))
1815 {
1816 hr = SHGetFolderPathW(NULL, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, NULL,
1817 SHGFP_TYPE_DEFAULT, wszTempPath);
1818 if (SUCCEEDED(hr) && (pszDesktop = wine_get_unix_file_name(wszTempPath)))
1819 {
1820 rmdir(pszDesktop);
1821 symlink(szDesktopTarget, pszDesktop);
1822 HeapFree(GetProcessHeap(), 0, pszDesktop);
1823 }
1824 }
1825 }
1826 #endif
1827
1828 /* Register the default values in the registry, as some apps seem to depend
1829 * on their presence. The set registered was taken from Windows XP.
1830 */
1831 HRESULT SHELL_RegisterShellFolders(void)
1832 {
1833 HRESULT hr;
1834
1835 /* Set up '$HOME' targeted symlinks for 'My Documents', 'My Pictures',
1836 * 'My Video', 'My Music' and 'Desktop' in advance, so that the
1837 * _SHRegister*ShellFolders() functions will find everything nice and clean
1838 * and thus will not attempt to create them in the profile directory. */
1839 #if 0
1840 _SHCreateSymbolicLinks();
1841 #endif
1842
1843 hr = _SHRegisterUserShellFolders(TRUE);
1844 if (SUCCEEDED(hr))
1845 hr = _SHRegisterUserShellFolders(FALSE);
1846 if (SUCCEEDED(hr))
1847 hr = _SHRegisterCommonShellFolders();
1848 return hr;
1849 }
1850
1851 /*************************************************************************
1852 * SHGetSpecialFolderPathA [SHELL32.@]
1853 */
1854 BOOL WINAPI SHGetSpecialFolderPathA (
1855 HWND hwndOwner,
1856 LPSTR szPath,
1857 int nFolder,
1858 BOOL bCreate)
1859 {
1860 return (SHGetFolderPathA(
1861 hwndOwner,
1862 nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0),
1863 NULL,
1864 0,
1865 szPath)) == S_OK ? TRUE : FALSE;
1866 }
1867
1868 /*************************************************************************
1869 * SHGetSpecialFolderPathW
1870 */
1871 BOOL WINAPI SHGetSpecialFolderPathW (
1872 HWND hwndOwner,
1873 LPWSTR szPath,
1874 int nFolder,
1875 BOOL bCreate)
1876 {
1877 return (SHGetFolderPathW(
1878 hwndOwner,
1879 nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0),
1880 NULL,
1881 0,
1882 szPath)) == S_OK ? TRUE : FALSE;
1883 }
1884
1885 /*************************************************************************
1886 * SHGetFolderLocation [SHELL32.@]
1887 *
1888 * Gets the folder locations from the registry and creates a pidl.
1889 *
1890 * PARAMS
1891 * hwndOwner [I]
1892 * nFolder [I] CSIDL_xxxxx
1893 * hToken [I] token representing user, or NULL for current user, or -1 for
1894 * default user
1895 * dwReserved [I] must be zero
1896 * ppidl [O] PIDL of a special folder
1897 *
1898 * RETURNS
1899 * Success: S_OK
1900 * Failure: Standard OLE-defined error result, S_FALSE or E_INVALIDARG
1901 *
1902 * NOTES
1903 * Creates missing reg keys and directories.
1904 * Mostly forwards to SHGetFolderPathW, but a few values of nFolder return
1905 * virtual folders that are handled here.
1906 */
1907 HRESULT WINAPI SHGetFolderLocation(
1908 HWND hwndOwner,
1909 int nFolder,
1910 HANDLE hToken,
1911 DWORD dwReserved,
1912 LPITEMIDLIST *ppidl)
1913 {
1914 HRESULT hr = E_INVALIDARG;
1915
1916 TRACE("%p 0x%08x %p 0x%08x %p\n",
1917 hwndOwner, nFolder, hToken, dwReserved, ppidl);
1918
1919 if (!ppidl)
1920 return E_INVALIDARG;
1921 if (dwReserved)
1922 return E_INVALIDARG;
1923
1924 /* The virtual folders' locations are not user-dependent */
1925 *ppidl = NULL;
1926 switch (nFolder)
1927 {
1928 case CSIDL_DESKTOP:
1929 *ppidl = _ILCreateDesktop();
1930 break;
1931
1932 case CSIDL_PERSONAL:
1933 *ppidl = _ILCreateMyDocuments();
1934 break;
1935
1936 case CSIDL_INTERNET:
1937 *ppidl = _ILCreateIExplore();
1938 break;
1939
1940 case CSIDL_CONTROLS:
1941 *ppidl = _ILCreateControlPanel();
1942 break;
1943
1944 case CSIDL_PRINTERS:
1945 *ppidl = _ILCreatePrinters();
1946 break;
1947
1948 case CSIDL_BITBUCKET:
1949 *ppidl = _ILCreateBitBucket();
1950 break;
1951
1952 case CSIDL_DRIVES:
1953 *ppidl = _ILCreateMyComputer();
1954 break;
1955
1956 case CSIDL_NETWORK:
1957 *ppidl = _ILCreateNetwork();
1958 break;
1959
1960 default:
1961 {
1962 WCHAR szPath[MAX_PATH];
1963
1964 hr = SHGetFolderPathW(hwndOwner, nFolder, hToken,
1965 SHGFP_TYPE_CURRENT, szPath);
1966 if (SUCCEEDED(hr))
1967 {
1968 DWORD attributes=0;
1969
1970 TRACE("Value=%s\n", debugstr_w(szPath));
1971 hr = SHILCreateFromPathW(szPath, ppidl, &attributes);
1972 }
1973 else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
1974 {
1975 /* unlike SHGetFolderPath, SHGetFolderLocation in shell32
1976 * version 6.0 returns E_FAIL for nonexistent paths
1977 */
1978 hr = E_FAIL;
1979 }
1980 }
1981 }
1982 if(*ppidl)
1983 hr = NOERROR;
1984
1985 TRACE("-- (new pidl %p)\n",*ppidl);
1986 return hr;
1987 }
1988
1989 /*************************************************************************
1990 * SHGetSpecialFolderLocation [SHELL32.@]
1991 *
1992 * NOTES
1993 * In NT5, SHGetSpecialFolderLocation needs the <winntdir>/Recent
1994 * directory.
1995 */
1996 HRESULT WINAPI SHGetSpecialFolderLocation(
1997 HWND hwndOwner,
1998 INT nFolder,
1999 LPITEMIDLIST * ppidl)
2000 {
2001 HRESULT hr = E_INVALIDARG;
2002
2003 TRACE("(%p,0x%x,%p)\n", hwndOwner,nFolder,ppidl);
2004
2005 if (!ppidl)
2006 return E_INVALIDARG;
2007
2008 hr = SHGetFolderLocation(hwndOwner, nFolder, NULL, 0, ppidl);
2009 return hr;
2010 }