Release 1.5.29.
[wine] / dlls / userenv / userenv_main.c
1 /*
2  * Implementation of userenv.dll
3  *
4  * Copyright 2006 Mike McCormack for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22
23 #include "ntstatus.h"
24 #define WIN32_NO_STATUS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winreg.h"
28 #include "winternl.h"
29 #include "winnls.h"
30 #include "sddl.h"
31 #include "objbase.h"
32 #include "userenv.h"
33
34 #include "wine/debug.h"
35 #include "wine/unicode.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL( userenv );
38
39 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
40 {
41     TRACE("%p %d %p\n", hinstDLL, fdwReason, lpvReserved);
42
43     switch (fdwReason)
44     {
45     case DLL_WINE_PREATTACH:
46         return FALSE;  /* prefer native version */
47     case DLL_PROCESS_ATTACH:
48         DisableThreadLibraryCalls(hinstDLL);
49         break;
50     }
51     return TRUE;
52 }
53
54 static BOOL get_reg_value(WCHAR *env, HKEY hkey, const WCHAR *name, WCHAR *val, DWORD size)
55 {
56     DWORD type, res_size=0;
57
58     if (RegQueryValueExW(hkey, name, 0, &type, NULL, &res_size) != ERROR_SUCCESS)
59         return FALSE;
60
61     if (type == REG_SZ)
62     {
63         if (res_size > size)
64             return FALSE;
65
66         return RegQueryValueExW(hkey, name, 0, NULL, (BYTE*)val, &size) == ERROR_SUCCESS;
67     }
68     else if (type == REG_EXPAND_SZ)
69     {
70         UNICODE_STRING us_buf, us_expanded;
71         WCHAR *buf = HeapAlloc(GetProcessHeap(), 0, res_size);
72         if (!buf)
73             return FALSE;
74
75         if (RegQueryValueExW(hkey, name, 0, NULL, (BYTE*)buf, &res_size) != ERROR_SUCCESS)
76         {
77             HeapFree(GetProcessHeap(), 0, buf);
78             return FALSE;
79         }
80
81         RtlInitUnicodeString(&us_buf, buf);
82         us_expanded.Buffer = val;
83         us_expanded.MaximumLength = size;
84         if (RtlExpandEnvironmentStrings_U(env, &us_buf, &us_expanded, &size) != STATUS_SUCCESS)
85         {
86             HeapFree(GetProcessHeap(), 0, buf);
87             return FALSE;
88         }
89
90         HeapFree(GetProcessHeap(), 0, buf);
91         return TRUE;
92     }
93
94     return FALSE;
95 }
96
97 static void set_registry_variables(WCHAR **env, HKEY hkey, DWORD type, BOOL set_path)
98 {
99     static const WCHAR SystemRootW[] = {'S','y','s','t','e','m','R','o','o','t',0};
100     static const WCHAR SystemDriveW[] = {'S','y','s','t','e','m','D','r','i','v','e',0};
101     static const WCHAR PATHW[] = {'P','A','T','H'};
102
103     UNICODE_STRING us_name, us_value;
104     WCHAR name[1024], value[1024];
105     DWORD ret, index, size;
106
107     for (index = 0; ; index++)
108     {
109         size = sizeof(name)/sizeof(WCHAR);
110         ret = RegEnumValueW(hkey, index, name, &size, NULL, NULL, NULL, NULL);
111         if (ret != ERROR_SUCCESS)
112             break;
113
114         if (!memicmpW(name, SystemRootW, sizeof(SystemRootW)/sizeof(WCHAR)))
115             continue;
116         if (!memicmpW(name, SystemDriveW, sizeof(SystemDriveW)/sizeof(WCHAR)))
117             continue;
118
119         RtlInitUnicodeString(&us_name, name);
120         us_value.Buffer = value;
121         us_value.MaximumLength = sizeof(value);
122         if (!memicmpW(name, PATHW, sizeof(PATHW)/sizeof(WCHAR)) &&
123                 !RtlQueryEnvironmentVariable_U(*env, &us_name, &us_value))
124         {
125             if (!set_path)
126                 continue;
127
128             size = strlenW(value)+1;
129             if (!get_reg_value(*env, hkey, name, value+size,
130                         sizeof(value)-size*sizeof(WCHAR)))
131                 continue;
132
133             value[size] = ';';
134             RtlInitUnicodeString(&us_value, value);
135             RtlSetEnvironmentVariable(env, &us_name, &us_value);
136             continue;
137         }
138
139         if (!get_reg_value(*env, hkey, name, value, sizeof(value)))
140             continue;
141
142         if(!value[0])
143             continue;
144
145         RtlInitUnicodeString(&us_value, value);
146         RtlSetEnvironmentVariable(env, &us_name, &us_value);
147     }
148 }
149
150 static void set_wow64_environment(WCHAR **env)
151 {
152     static const WCHAR versionW[] = {'S','o','f','t','w','a','r','e','\\',
153         'M','i','c','r','o','s','o','f','t','\\',
154         'W','i','n','d','o','w','s','\\',
155         'C','u','r','r','e','n','t','V','e','r','s','i','o','n',0};
156     static const WCHAR progdirW[]   = {'P','r','o','g','r','a','m','F','i','l','e','s','D','i','r',0};
157     static const WCHAR progdir86W[] = {'P','r','o','g','r','a','m','F','i','l','e','s','D','i','r',' ','(','x','8','6',')',0};
158     static const WCHAR progfilesW[] = {'P','r','o','g','r','a','m','F','i','l','e','s',0};
159     static const WCHAR progw6432W[] = {'P','r','o','g','r','a','m','W','6','4','3','2',0};
160     static const WCHAR commondirW[]   = {'C','o','m','m','o','n','F','i','l','e','s','D','i','r',0};
161     static const WCHAR commondir86W[] = {'C','o','m','m','o','n','F','i','l','e','s','D','i','r',' ','(','x','8','6',')',0};
162     static const WCHAR commonfilesW[] = {'C','o','m','m','o','n','P','r','o','g','r','a','m','F','i','l','e','s',0};
163     static const WCHAR commonw6432W[] = {'C','o','m','m','o','n','P','r','o','g','r','a','m','W','6','4','3','2',0};
164
165     UNICODE_STRING nameW, valueW;
166     WCHAR buf[64];
167     HKEY hkey;
168     BOOL is_win64 = (sizeof(void *) > sizeof(int));
169     BOOL is_wow64;
170
171     IsWow64Process( GetCurrentProcess(), &is_wow64 );
172
173     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, versionW, 0,
174                 KEY_READ|KEY_WOW64_64KEY, &hkey))
175         return;
176
177     /* set the ProgramFiles variables */
178
179     if (get_reg_value(*env, hkey, progdirW, buf, sizeof(buf)))
180     {
181         if (is_win64 || is_wow64)
182         {
183             RtlInitUnicodeString(&nameW, progw6432W);
184             RtlInitUnicodeString(&valueW, buf);
185             RtlSetEnvironmentVariable(env, &nameW, &valueW);
186         }
187         if (is_win64 || !is_wow64)
188         {
189             RtlInitUnicodeString(&nameW, progfilesW);
190             RtlInitUnicodeString(&valueW, buf);
191             RtlSetEnvironmentVariable(env, &nameW, &valueW);
192         }
193     }
194     if (is_wow64 && get_reg_value(*env, hkey, progdir86W, buf, sizeof(buf)))
195     {
196         RtlInitUnicodeString(&nameW, progfilesW);
197         RtlInitUnicodeString(&valueW, buf);
198         RtlSetEnvironmentVariable(env, &nameW, &valueW);
199     }
200
201     /* set the CommonProgramFiles variables */
202
203     if (get_reg_value(*env, hkey, commondirW, buf, sizeof(buf)))
204     {
205         if (is_win64 || is_wow64)
206         {
207             RtlInitUnicodeString(&nameW, commonw6432W);
208             RtlInitUnicodeString(&valueW, buf);
209             RtlSetEnvironmentVariable(env, &nameW, &valueW);
210         }
211         if (is_win64 || !is_wow64)
212         {
213             RtlInitUnicodeString(&nameW, commonfilesW);
214             RtlInitUnicodeString(&valueW, buf);
215             RtlSetEnvironmentVariable(env, &nameW, &valueW);
216         }
217     }
218     if (is_wow64 && get_reg_value(*env, hkey, commondir86W, buf, sizeof(buf)))
219     {
220         RtlInitUnicodeString(&nameW, commonfilesW);
221         RtlInitUnicodeString(&valueW, buf);
222         RtlSetEnvironmentVariable(env, &nameW, &valueW);
223     }
224
225     RegCloseKey(hkey);
226 }
227
228 BOOL WINAPI CreateEnvironmentBlock( LPVOID* lpEnvironment,
229                      HANDLE hToken, BOOL bInherit )
230 {
231     static const WCHAR env_keyW[] = {'S','y','s','t','e','m','\\',
232         'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
233         'C','o','n','t','r','o','l','\\',
234         'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
235         'E','n','v','i','r','o','n','m','e','n','t',0};
236     static const WCHAR profile_keyW[] = {'S','o','f','t','w','a','r','e','\\',
237         'M','i','c','r','o','s','o','f','t','\\',
238         'W','i','n','d','o','w','s',' ','N','T','\\',
239         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
240         'P','r','o','f','i','l','e','L','i','s','t',0};
241     static const WCHAR envW[] = {'E','n','v','i','r','o','n','m','e','n','t',0};
242     static const WCHAR volatile_envW[] = {'V','o','l','a','t','i','l','e',' ','E','n','v','i','r','o','n','m','e','n','t',0};
243     static const WCHAR ProfilesDirectoryW[] = {'P','r','o','f','i','l','e','s','D','i','r','e','c','t','o','r','y',0};
244
245     static const WCHAR SystemRootW[] = {'S','y','s','t','e','m','R','o','o','t',0};
246     static const WCHAR SystemDriveW[] = {'S','y','s','t','e','m','D','r','i','v','e',0};
247     static const WCHAR AllUsersProfileW[] = {'A','l','l','U','s','e','r','s','P','r','o','f','i','l','e',0};
248     static const WCHAR ALLUSERSPROFILEW[] = {'A','L','L','U','S','E','R','S','P','R','O','F','I','L','E',0};
249     static const WCHAR USERNAMEW[] = {'U','S','E','R','N','A','M','E',0};
250     static const WCHAR USERPROFILEW[] = {'U','S','E','R','P','R','O','F','I','L','E',0};
251     static const WCHAR DefaultW[] = {'D','e','f','a','u','l','t',0};
252     static const WCHAR COMPUTERNAMEW[] = {'C','O','M','P','U','T','E','R','N','A','M','E',0};
253
254     WCHAR *env, buf[UNICODE_STRING_MAX_CHARS], profiles_dir[MAX_PATH];
255     UNICODE_STRING us_name, us_val;
256     DWORD len;
257     HKEY hkey, hsubkey;
258
259     TRACE("%p %p %d\n", lpEnvironment, hToken, bInherit );
260
261     if (!lpEnvironment)
262         return FALSE;
263
264     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, env_keyW, 0, KEY_READ, &hkey) != ERROR_SUCCESS)
265         return FALSE;
266
267     if (RtlCreateEnvironment(bInherit, &env) != STATUS_SUCCESS)
268     {
269         RegCloseKey(hkey);
270         return FALSE;
271     }
272
273     if (!GetEnvironmentVariableW(SystemRootW, buf, UNICODE_STRING_MAX_CHARS))
274     {
275         if (!get_reg_value(env, hkey, SystemRootW, buf, UNICODE_STRING_MAX_CHARS))
276         {
277             buf[0] = 0;
278             WARN("SystemRoot variable not set\n");
279         }
280     }
281     RtlInitUnicodeString(&us_name, SystemRootW);
282     RtlInitUnicodeString(&us_val, buf);
283     RtlSetEnvironmentVariable(&env, &us_name, &us_val);
284
285     if (!GetEnvironmentVariableW(SystemDriveW, buf, UNICODE_STRING_MAX_CHARS))
286     {
287         if (!get_reg_value(env, hkey, SystemRootW, buf, UNICODE_STRING_MAX_CHARS))
288         {
289             buf[0] = 0;
290             WARN("SystemDrive variable not set\n");
291         }
292     }
293     RtlInitUnicodeString(&us_name, SystemDriveW);
294     RtlInitUnicodeString(&us_val, buf);
295     RtlSetEnvironmentVariable(&env, &us_name, &us_val);
296
297     set_registry_variables(&env, hkey, REG_SZ, !bInherit);
298     set_registry_variables(&env, hkey, REG_EXPAND_SZ, !bInherit);
299
300     if (RegOpenKeyExW(hkey, envW, 0, KEY_READ, &hsubkey) == ERROR_SUCCESS)
301     {
302         set_registry_variables(&env, hsubkey, REG_SZ, !bInherit);
303         set_registry_variables(&env, hsubkey, REG_EXPAND_SZ, !bInherit);
304         RegCloseKey(hsubkey);
305     }
306
307     if (RegOpenKeyExW(hkey, volatile_envW, 0, KEY_READ, &hsubkey) == ERROR_SUCCESS)
308     {
309         set_registry_variables(&env, hsubkey, REG_SZ, !bInherit);
310         set_registry_variables(&env, hsubkey, REG_EXPAND_SZ, !bInherit);
311         RegCloseKey(hsubkey);
312     }
313     RegCloseKey(hkey);
314
315     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, profile_keyW, 0, KEY_READ, &hkey) == ERROR_SUCCESS)
316     {
317         if (get_reg_value(env, hkey, ProfilesDirectoryW, profiles_dir, MAX_PATH-sizeof(WCHAR)))
318         {
319             len = strlenW(profiles_dir);
320             if (profiles_dir[len-1] != '\\')
321             {
322                 profiles_dir[len++] = '\\';
323                 profiles_dir[len] = '\0';
324             }
325
326             memcpy(buf, profiles_dir, len*sizeof(WCHAR));
327             if (get_reg_value(env, hkey, AllUsersProfileW, buf+len, UNICODE_STRING_MAX_CHARS-len))
328             {
329                 RtlInitUnicodeString(&us_name, ALLUSERSPROFILEW);
330                 RtlInitUnicodeString(&us_val, buf);
331                 RtlSetEnvironmentVariable(&env, &us_name, &us_val);
332             }
333         }
334         else
335         {
336             profiles_dir[0] = 0;
337         }
338
339         RegCloseKey(hkey);
340     }
341
342     len = sizeof(buf)/sizeof(WCHAR);
343     if (GetComputerNameW(buf, &len))
344     {
345         RtlInitUnicodeString(&us_name, COMPUTERNAMEW);
346         RtlInitUnicodeString(&us_val, buf);
347         RtlSetEnvironmentVariable(&env, &us_name, &us_val);
348     }
349
350     set_wow64_environment(&env);
351
352     if (!hToken)
353     {
354         if (profiles_dir[0])
355         {
356             len = strlenW(profiles_dir);
357             if (len*sizeof(WCHAR)+sizeof(DefaultW) < sizeof(buf))
358             {
359                 memcpy(buf, profiles_dir, len*sizeof(WCHAR));
360                 memcpy(buf+len, DefaultW, sizeof(DefaultW));
361                 RtlInitUnicodeString(&us_name, USERPROFILEW);
362                 RtlInitUnicodeString(&us_val, buf);
363                 RtlSetEnvironmentVariable(&env, &us_name, &us_val);
364             }
365         }
366
367         buf[0] = '.';
368         memcpy(buf+1, DefaultW, sizeof(DefaultW));
369     }
370     else
371     {
372         TOKEN_USER *token_user = NULL;
373         SID_NAME_USE use;
374         WCHAR *sidW;
375         DWORD size, tmp=0;
376
377         if (GetTokenInformation(hToken, TokenUser, NULL, 0, &len) ||
378                 GetLastError()!=ERROR_INSUFFICIENT_BUFFER ||
379                 !(token_user = HeapAlloc(GetProcessHeap(), 0, len)) ||
380                 !GetTokenInformation(hToken, TokenUser, token_user, len, &len) ||
381                 !ConvertSidToStringSidW(token_user->User.Sid, &sidW))
382         {
383             HeapFree(GetProcessHeap(), 0, token_user);
384             RtlDestroyEnvironment(env);
385             return FALSE;
386         }
387
388         len = strlenW(profiles_dir);
389         memcpy(buf, profiles_dir, len*sizeof(WCHAR));
390
391         size = UNICODE_STRING_MAX_CHARS-len;
392         if (LookupAccountSidW(NULL, token_user->User.Sid,
393                     buf+len, &size, NULL, &tmp, &use))
394         {
395             RtlInitUnicodeString(&us_name, USERNAMEW);
396             RtlInitUnicodeString(&us_val, buf+len);
397             RtlSetEnvironmentVariable(&env, &us_name, &us_val);
398
399             if (len)
400             {
401                 RtlInitUnicodeString(&us_name, USERPROFILEW);
402                 RtlInitUnicodeString(&us_val, buf);
403                 RtlSetEnvironmentVariable(&env, &us_name, &us_val);
404             }
405         }
406
407         HeapFree(GetProcessHeap(), 0, token_user);
408         strcpyW(buf, sidW);
409         LocalFree(sidW);
410     }
411
412     if (RegOpenKeyExW(HKEY_USERS, buf, 0, KEY_READ, &hkey) == ERROR_SUCCESS)
413     {
414         if (RegOpenKeyExW(hkey, envW, 0, KEY_READ, &hsubkey) == ERROR_SUCCESS)
415         {
416             set_registry_variables(&env, hsubkey, REG_SZ, !bInherit);
417             set_registry_variables(&env, hsubkey, REG_EXPAND_SZ, !bInherit);
418             RegCloseKey(hsubkey);
419         }
420
421         if (RegOpenKeyExW(hkey, volatile_envW, 0, KEY_READ, &hsubkey) == ERROR_SUCCESS)
422         {
423             set_registry_variables(&env, hsubkey, REG_SZ, !bInherit);
424             set_registry_variables(&env, hsubkey, REG_EXPAND_SZ, !bInherit);
425             RegCloseKey(hsubkey);
426         }
427         RegCloseKey(hkey);
428     }
429
430     *lpEnvironment = env;
431     return TRUE;
432 }
433
434 BOOL WINAPI DestroyEnvironmentBlock(LPVOID lpEnvironment)
435 {
436     NTSTATUS r;
437
438     TRACE("%p\n", lpEnvironment);
439     r = RtlDestroyEnvironment(lpEnvironment);
440     if (r == STATUS_SUCCESS)
441         return TRUE;
442     return FALSE;
443 }
444
445 BOOL WINAPI ExpandEnvironmentStringsForUserA( HANDLE hToken, LPCSTR lpSrc,
446                      LPSTR lpDest, DWORD dwSize )
447 {
448     BOOL ret;
449
450     TRACE("%p %s %p %d\n", hToken, debugstr_a(lpSrc), lpDest, dwSize);
451
452     ret = ExpandEnvironmentStringsA( lpSrc, lpDest, dwSize );
453     TRACE("<- %s\n", debugstr_a(lpDest));
454     return ret;
455 }
456
457 BOOL WINAPI ExpandEnvironmentStringsForUserW( HANDLE hToken, LPCWSTR lpSrc,
458                      LPWSTR lpDest, DWORD dwSize )
459 {
460     BOOL ret;
461
462     TRACE("%p %s %p %d\n", hToken, debugstr_w(lpSrc), lpDest, dwSize);
463
464     ret = ExpandEnvironmentStringsW( lpSrc, lpDest, dwSize );
465     TRACE("<- %s\n", debugstr_w(lpDest));
466     return ret;
467 }
468
469 BOOL WINAPI GetDefaultUserProfileDirectoryA( LPSTR lpProfileDir, LPDWORD lpcchSize )
470 {
471     FIXME("%p %p\n", lpProfileDir, lpcchSize );
472     return FALSE;
473 }
474
475 BOOL WINAPI GetDefaultUserProfileDirectoryW( LPWSTR lpProfileDir, LPDWORD lpcchSize )
476 {
477     FIXME("%p %p\n", lpProfileDir, lpcchSize );
478     return FALSE;
479 }
480
481 BOOL WINAPI GetUserProfileDirectoryA( HANDLE hToken, LPSTR lpProfileDir,
482                      LPDWORD lpcchSize )
483 {
484     BOOL ret;
485     WCHAR *dirW = NULL;
486
487     TRACE( "%p %p %p\n", hToken, lpProfileDir, lpcchSize );
488
489     if (!lpProfileDir || !lpcchSize)
490     {
491         SetLastError( ERROR_INVALID_PARAMETER );
492         return FALSE;
493     }
494     if (!(dirW = HeapAlloc( GetProcessHeap(), 0, *lpcchSize * sizeof(WCHAR) )))
495         return FALSE;
496
497     if ((ret = GetUserProfileDirectoryW( hToken, dirW, lpcchSize )))
498         WideCharToMultiByte( CP_ACP, 0, dirW, *lpcchSize, lpProfileDir, *lpcchSize, NULL, NULL );
499
500     HeapFree( GetProcessHeap(), 0, dirW );
501     return ret;
502 }
503
504 BOOL WINAPI GetUserProfileDirectoryW( HANDLE hToken, LPWSTR lpProfileDir,
505                      LPDWORD lpcchSize )
506 {
507     static const WCHAR slashW[] = {'\\',0};
508     TOKEN_USER *t;
509     WCHAR *userW = NULL, *dirW = NULL;
510     DWORD len, dir_len, domain_len;
511     SID_NAME_USE use;
512     BOOL ret = FALSE;
513
514     TRACE( "%p %p %p\n", hToken, lpProfileDir, lpcchSize );
515
516     if (!lpcchSize)
517     {
518         SetLastError( ERROR_INVALID_PARAMETER );
519         return FALSE;
520     }
521
522     len = 0;
523     GetTokenInformation( hToken, TokenUser, NULL, 0, &len );
524     if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return FALSE;
525     if (!(t = HeapAlloc( GetProcessHeap(), 0, len ))) return FALSE;
526     if (!GetTokenInformation( hToken, TokenUser, t, len, &len )) goto done;
527
528     len = domain_len = 0;
529     LookupAccountSidW( NULL, t->User.Sid, NULL, &len, NULL, &domain_len, NULL );
530     if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) goto done;
531     if (!(userW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) goto done;
532     if (!LookupAccountSidW( NULL, t->User.Sid, userW, &len, NULL, &domain_len, &use )) goto done;
533
534     dir_len = 0;
535     GetProfilesDirectoryW( NULL, &dir_len );
536     if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) goto done;
537     if (!(dirW = HeapAlloc( GetProcessHeap(), 0, (dir_len + 1) * sizeof(WCHAR) ))) goto done;
538     if (!GetProfilesDirectoryW( dirW, &dir_len )) goto done;
539
540     len += dir_len + 2;
541     if (*lpcchSize < len)
542     {
543         SetLastError( ERROR_INSUFFICIENT_BUFFER );
544         *lpcchSize = len;
545         goto done;
546     }
547     strcpyW( lpProfileDir, dirW );
548     strcatW( lpProfileDir, slashW );
549     strcatW( lpProfileDir, userW );
550     *lpcchSize = len;
551     ret = TRUE;
552
553 done:
554     HeapFree( GetProcessHeap(), 0, t );
555     HeapFree( GetProcessHeap(), 0, userW );
556     HeapFree( GetProcessHeap(), 0, dirW );
557     return ret;
558 }
559
560 static const char ProfileListA[] = "Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList";
561
562 BOOL WINAPI GetProfilesDirectoryA( LPSTR lpProfilesDir, LPDWORD lpcchSize )
563 {
564     static const char ProfilesDirectory[] = "ProfilesDirectory";
565     LONG l;
566     HKEY key;
567     BOOL ret = FALSE;
568     DWORD len = 0, expanded_len;
569     LPSTR unexpanded_profiles_dir = NULL;
570
571     TRACE("%p %p\n", lpProfilesDir, lpcchSize );
572
573     if (!lpProfilesDir || !lpcchSize)
574     {
575         SetLastError(ERROR_INVALID_PARAMETER);
576         return FALSE;
577     }
578
579     l = RegOpenKeyExA(HKEY_LOCAL_MACHINE, ProfileListA, 0, KEY_READ, &key);
580     if (l)
581     {
582         SetLastError(l);
583         return FALSE;
584     }
585     l = RegQueryValueExA(key, ProfilesDirectory, NULL, NULL, NULL, &len);
586     if (l)
587     {
588         SetLastError(l);
589         goto end;
590     }
591     unexpanded_profiles_dir = HeapAlloc(GetProcessHeap(), 0, len);
592     if (!unexpanded_profiles_dir)
593     {
594         SetLastError(ERROR_OUTOFMEMORY);
595         goto end;
596     }
597     l = RegQueryValueExA(key, ProfilesDirectory, NULL, NULL,
598                              (BYTE *)unexpanded_profiles_dir, &len);
599     if (l)
600     {
601         SetLastError(l);
602         goto end;
603     }
604     expanded_len = ExpandEnvironmentStringsA(unexpanded_profiles_dir, NULL, 0);
605     /* The returned length doesn't include the NULL terminator. */
606     if (*lpcchSize < expanded_len - 1)
607     {
608         *lpcchSize = expanded_len - 1;
609         SetLastError(ERROR_INSUFFICIENT_BUFFER);
610         goto end;
611     }
612     *lpcchSize = expanded_len - 1;
613     /* The return value is also the expected length. */
614     ret = ExpandEnvironmentStringsA(unexpanded_profiles_dir, lpProfilesDir,
615                                     expanded_len) - 1;
616 end:
617     HeapFree(GetProcessHeap(), 0, unexpanded_profiles_dir);
618     RegCloseKey(key);
619     return ret;
620 }
621
622 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};
623
624 BOOL WINAPI GetProfilesDirectoryW( LPWSTR lpProfilesDir, LPDWORD lpcchSize )
625 {
626     static const WCHAR ProfilesDirectory[] = {'P','r','o','f','i','l','e','s','D','i','r','e','c','t','o','r','y',0};
627     LONG l;
628     HKEY key;
629     BOOL ret = FALSE;
630     DWORD len = 0, expanded_len;
631     LPWSTR unexpanded_profiles_dir = NULL;
632
633     TRACE("%p %p\n", lpProfilesDir, lpcchSize );
634
635     if (!lpcchSize)
636     {
637         SetLastError(ERROR_INVALID_PARAMETER);
638         return FALSE;
639     }
640
641     l = RegOpenKeyExW(HKEY_LOCAL_MACHINE, ProfileListW, 0, KEY_READ, &key);
642     if (l)
643     {
644         SetLastError(l);
645         return FALSE;
646     }
647     l = RegQueryValueExW(key, ProfilesDirectory, NULL, NULL, NULL, &len);
648     if (l)
649     {
650         SetLastError(l);
651         goto end;
652     }
653     unexpanded_profiles_dir = HeapAlloc(GetProcessHeap(), 0, len);
654     if (!unexpanded_profiles_dir)
655     {
656         SetLastError(ERROR_OUTOFMEMORY);
657         goto end;
658     }
659     l = RegQueryValueExW(key, ProfilesDirectory, NULL, NULL,
660                              (BYTE *)unexpanded_profiles_dir, &len);
661     if (l)
662     {
663         SetLastError(l);
664         goto end;
665     }
666     expanded_len = ExpandEnvironmentStringsW(unexpanded_profiles_dir, NULL, 0);
667     /* The returned length doesn't include the NULL terminator. */
668     if (*lpcchSize < expanded_len - 1 || !lpProfilesDir)
669     {
670         *lpcchSize = expanded_len - 1;
671         SetLastError(ERROR_INSUFFICIENT_BUFFER);
672         goto end;
673     }
674     *lpcchSize = expanded_len - 1;
675     /* The return value is also the expected length. */
676     ret = ExpandEnvironmentStringsW(unexpanded_profiles_dir, lpProfilesDir,
677                                     expanded_len) - 1;
678 end:
679     HeapFree(GetProcessHeap(), 0, unexpanded_profiles_dir);
680     RegCloseKey(key);
681     return ret;
682 }
683
684 BOOL WINAPI GetAllUsersProfileDirectoryA( LPSTR lpProfileDir, LPDWORD lpcchSize )
685 {
686     FIXME("%p %p\n", lpProfileDir, lpcchSize);
687     return FALSE;
688 }
689
690 BOOL WINAPI GetAllUsersProfileDirectoryW( LPWSTR lpProfileDir, LPDWORD lpcchSize )
691 {
692     FIXME("%p %p\n", lpProfileDir, lpcchSize);
693     return FALSE;
694 }
695
696 BOOL WINAPI GetProfileType( DWORD *pdwFlags )
697 {
698     FIXME("%p\n", pdwFlags );
699     *pdwFlags = 0;
700     return TRUE;
701 }
702
703 BOOL WINAPI LoadUserProfileA( HANDLE hToken, LPPROFILEINFOA lpProfileInfo )
704 {
705     FIXME("%p %p\n", hToken, lpProfileInfo );
706     lpProfileInfo->hProfile = HKEY_CURRENT_USER;
707     return TRUE;
708 }
709
710 BOOL WINAPI LoadUserProfileW( HANDLE hToken, LPPROFILEINFOW lpProfileInfo )
711 {
712     FIXME("%p %p\n", hToken, lpProfileInfo );
713     lpProfileInfo->hProfile = HKEY_CURRENT_USER;
714     return TRUE;
715 }
716
717 BOOL WINAPI RegisterGPNotification( HANDLE event, BOOL machine )
718 {
719     FIXME("%p %d\n", event, machine );
720     return TRUE;
721 }
722
723 BOOL WINAPI UnregisterGPNotification( HANDLE event )
724 {
725     FIXME("%p\n", event );
726     return TRUE;
727 }
728
729 BOOL WINAPI UnloadUserProfile( HANDLE hToken, HANDLE hProfile )
730 {
731     FIXME("(%p, %p): stub\n", hToken, hProfile);
732     return FALSE;
733 }
734
735 /******************************************************************************
736  *              USERENV.138
737  *
738  * Create .lnk file
739  *
740  * PARAMETERS
741  *   int     csidl               [in] well-known directory location to create link in
742  *   LPCSTR lnk_dir              [in] directory (relative to directory specified by csidl) to create link in
743  *   LPCSTR lnk_filename         [in] filename of the link file without .lnk extension
744  *   LPCSTR lnk_target           [in] file/directory pointed to by link
745  *   LPCSTR lnk_iconfile         [in] link icon resource filename
746  *   DWORD   lnk_iconid          [in] link icon resource id in file referred by lnk_iconfile
747  *   LPCSTR work_directory       [in] link target's work directory
748  *   WORD    hotkey              [in] link hotkey (virtual key id)
749  *   DWORD   win_state           [in] initial window size (SW_SHOWMAXIMIZED to start maximized,
750  *                                     SW_SHOWMINNOACTIVE to start minimized, everything else is default state)
751  *   LPCSTR comment              [in] comment - link's comment
752  *   LPCSTR loc_filename_resfile [in] resource file which holds localized filename for this link file
753  *   DWORD   loc_filename_resid  [in] resource id for this link file's localized filename
754  *
755  * RETURNS
756  *    TRUE:  Link file was successfully created
757  *    FALSE: Link file was not created
758  */
759 BOOL WINAPI USERENV_138( int csidl, LPCSTR lnk_dir, LPCSTR lnk_filename,
760             LPCSTR lnk_target, LPCSTR lnk_iconfile, DWORD lnk_iconid,
761             LPCSTR work_directory, WORD hotkey, DWORD win_state, LPCSTR comment,
762             LPCSTR loc_filename_resfile, DWORD loc_filename_resid)
763 {
764     FIXME("(%d,%s,%s,%s,%s,%d,%s,0x%x,%d,%s,%s,%d) - stub\n", csidl, debugstr_a(lnk_dir),
765             debugstr_a(lnk_filename), debugstr_a(lnk_target), debugstr_a(lnk_iconfile),
766             lnk_iconid, debugstr_a(work_directory), hotkey, win_state,
767             debugstr_a(comment), debugstr_a(loc_filename_resfile), loc_filename_resid );
768
769     return FALSE;
770 }