| /* | 
 |  * Implementation of userenv.dll | 
 |  * | 
 |  * Copyright 2006 Mike McCormack for CodeWeavers | 
 |  * | 
 |  * This library is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU Lesser General Public | 
 |  * License as published by the Free Software Foundation; either | 
 |  * version 2.1 of the License, or (at your option) any later version. | 
 |  * | 
 |  * This library is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 |  * Lesser General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU Lesser General Public | 
 |  * License along with this library; if not, write to the Free Software | 
 |  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
 |  */ | 
 |  | 
 | #include <stdarg.h> | 
 |  | 
 | #include "ntstatus.h" | 
 | #define WIN32_NO_STATUS | 
 | #include "windef.h" | 
 | #include "winbase.h" | 
 | #include "winreg.h" | 
 | #include "winternl.h" | 
 | #include "winnls.h" | 
 | #include "sddl.h" | 
 | #include "objbase.h" | 
 | #include "userenv.h" | 
 |  | 
 | #include "wine/debug.h" | 
 | #include "wine/unicode.h" | 
 |  | 
 | WINE_DEFAULT_DEBUG_CHANNEL( userenv ); | 
 |  | 
 | BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) | 
 | { | 
 |     TRACE("%p %d %p\n", hinstDLL, fdwReason, lpvReserved); | 
 |  | 
 |     switch (fdwReason) | 
 |     { | 
 |     case DLL_WINE_PREATTACH: | 
 |         return FALSE;  /* prefer native version */ | 
 |     case DLL_PROCESS_ATTACH: | 
 |         DisableThreadLibraryCalls(hinstDLL); | 
 |         break; | 
 |     case DLL_PROCESS_DETACH: | 
 |         break; | 
 |     } | 
 |     return TRUE; | 
 | } | 
 |  | 
 | static BOOL get_reg_value(WCHAR *env, HKEY hkey, const WCHAR *name, WCHAR *val, DWORD size) | 
 | { | 
 |     DWORD type, res_size=0; | 
 |  | 
 |     if (RegQueryValueExW(hkey, name, 0, &type, NULL, &res_size) != ERROR_SUCCESS) | 
 |         return FALSE; | 
 |  | 
 |     if (type == REG_SZ) | 
 |     { | 
 |         if (res_size > size) | 
 |             return FALSE; | 
 |  | 
 |         return RegQueryValueExW(hkey, name, 0, NULL, (BYTE*)val, &size) == ERROR_SUCCESS; | 
 |     } | 
 |     else if (type == REG_EXPAND_SZ) | 
 |     { | 
 |         UNICODE_STRING us_buf, us_expanded; | 
 |         WCHAR *buf = HeapAlloc(GetProcessHeap(), 0, res_size); | 
 |         if (!buf) | 
 |             return FALSE; | 
 |  | 
 |         if (RegQueryValueExW(hkey, name, 0, NULL, (BYTE*)buf, &res_size) != ERROR_SUCCESS) | 
 |         { | 
 |             HeapFree(GetProcessHeap(), 0, buf); | 
 |             return FALSE; | 
 |         } | 
 |  | 
 |         RtlInitUnicodeString(&us_buf, buf); | 
 |         us_expanded.Buffer = val; | 
 |         us_expanded.MaximumLength = size; | 
 |         if (RtlExpandEnvironmentStrings_U(env, &us_buf, &us_expanded, &size) != STATUS_SUCCESS) | 
 |         { | 
 |             HeapFree(GetProcessHeap(), 0, buf); | 
 |             return FALSE; | 
 |         } | 
 |  | 
 |         HeapFree(GetProcessHeap(), 0, buf); | 
 |         return TRUE; | 
 |     } | 
 |  | 
 |     return FALSE; | 
 | } | 
 |  | 
 | static void set_registry_variables(WCHAR **env, HKEY hkey, DWORD type, BOOL set_path) | 
 | { | 
 |     static const WCHAR SystemRootW[] = {'S','y','s','t','e','m','R','o','o','t',0}; | 
 |     static const WCHAR SystemDriveW[] = {'S','y','s','t','e','m','D','r','i','v','e',0}; | 
 |     static const WCHAR PATHW[] = {'P','A','T','H'}; | 
 |  | 
 |     UNICODE_STRING us_name, us_value; | 
 |     WCHAR name[1024], value[1024]; | 
 |     DWORD ret, index, size; | 
 |  | 
 |     for (index = 0; ; index++) | 
 |     { | 
 |         size = sizeof(name)/sizeof(WCHAR); | 
 |         ret = RegEnumValueW(hkey, index, name, &size, NULL, NULL, NULL, NULL); | 
 |         if (ret != ERROR_SUCCESS) | 
 |             break; | 
 |  | 
 |         if (!memicmpW(name, SystemRootW, sizeof(SystemRootW)/sizeof(WCHAR))) | 
 |             continue; | 
 |         if (!memicmpW(name, SystemDriveW, sizeof(SystemDriveW)/sizeof(WCHAR))) | 
 |             continue; | 
 |  | 
 |         RtlInitUnicodeString(&us_name, name); | 
 |         us_value.Buffer = value; | 
 |         us_value.MaximumLength = sizeof(value); | 
 |         if (!memicmpW(name, PATHW, sizeof(PATHW)/sizeof(WCHAR)) && | 
 |                 !RtlQueryEnvironmentVariable_U(*env, &us_name, &us_value)) | 
 |         { | 
 |             if (!set_path) | 
 |                 continue; | 
 |  | 
 |             size = strlenW(value)+1; | 
 |             if (!get_reg_value(*env, hkey, name, value+size, | 
 |                         sizeof(value)-size*sizeof(WCHAR))) | 
 |                 continue; | 
 |  | 
 |             value[size] = ';'; | 
 |             RtlInitUnicodeString(&us_value, value); | 
 |             RtlSetEnvironmentVariable(env, &us_name, &us_value); | 
 |             continue; | 
 |         } | 
 |  | 
 |         if (!get_reg_value(*env, hkey, name, value, sizeof(value))) | 
 |             continue; | 
 |  | 
 |         if(!value[0]) | 
 |             continue; | 
 |  | 
 |         RtlInitUnicodeString(&us_value, value); | 
 |         RtlSetEnvironmentVariable(env, &us_name, &us_value); | 
 |     } | 
 | } | 
 |  | 
 | static void set_wow64_environment(WCHAR **env) | 
 | { | 
 |     static const WCHAR versionW[] = {'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}; | 
 |     static const WCHAR progdirW[]   = {'P','r','o','g','r','a','m','F','i','l','e','s','D','i','r',0}; | 
 |     static const WCHAR progdir86W[] = {'P','r','o','g','r','a','m','F','i','l','e','s','D','i','r',' ','(','x','8','6',')',0}; | 
 |     static const WCHAR progfilesW[] = {'P','r','o','g','r','a','m','F','i','l','e','s',0}; | 
 |     static const WCHAR progw6432W[] = {'P','r','o','g','r','a','m','W','6','4','3','2',0}; | 
 |     static const WCHAR commondirW[]   = {'C','o','m','m','o','n','F','i','l','e','s','D','i','r',0}; | 
 |     static const WCHAR commondir86W[] = {'C','o','m','m','o','n','F','i','l','e','s','D','i','r',' ','(','x','8','6',')',0}; | 
 |     static const WCHAR commonfilesW[] = {'C','o','m','m','o','n','P','r','o','g','r','a','m','F','i','l','e','s',0}; | 
 |     static const WCHAR commonw6432W[] = {'C','o','m','m','o','n','P','r','o','g','r','a','m','W','6','4','3','2',0}; | 
 |  | 
 |     UNICODE_STRING nameW, valueW; | 
 |     WCHAR buf[64]; | 
 |     HKEY hkey; | 
 |     BOOL is_win64 = (sizeof(void *) > sizeof(int)); | 
 |     BOOL is_wow64; | 
 |  | 
 |     IsWow64Process( GetCurrentProcess(), &is_wow64 ); | 
 |  | 
 |     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, versionW, 0, | 
 |                 KEY_READ|KEY_WOW64_64KEY, &hkey)) | 
 |         return; | 
 |  | 
 |     /* set the ProgramFiles variables */ | 
 |  | 
 |     if (get_reg_value(*env, hkey, progdirW, buf, sizeof(buf))) | 
 |     { | 
 |         if (is_win64 || is_wow64) | 
 |         { | 
 |             RtlInitUnicodeString(&nameW, progw6432W); | 
 |             RtlInitUnicodeString(&valueW, buf); | 
 |             RtlSetEnvironmentVariable(env, &nameW, &valueW); | 
 |         } | 
 |         if (is_win64 || !is_wow64) | 
 |         { | 
 |             RtlInitUnicodeString(&nameW, progfilesW); | 
 |             RtlInitUnicodeString(&valueW, buf); | 
 |             RtlSetEnvironmentVariable(env, &nameW, &valueW); | 
 |         } | 
 |     } | 
 |     if (is_wow64 && get_reg_value(*env, hkey, progdir86W, buf, sizeof(buf))) | 
 |     { | 
 |         RtlInitUnicodeString(&nameW, progfilesW); | 
 |         RtlInitUnicodeString(&valueW, buf); | 
 |         RtlSetEnvironmentVariable(env, &nameW, &valueW); | 
 |     } | 
 |  | 
 |     /* set the CommonProgramFiles variables */ | 
 |  | 
 |     if (get_reg_value(*env, hkey, commondirW, buf, sizeof(buf))) | 
 |     { | 
 |         if (is_win64 || is_wow64) | 
 |         { | 
 |             RtlInitUnicodeString(&nameW, commonw6432W); | 
 |             RtlInitUnicodeString(&valueW, buf); | 
 |             RtlSetEnvironmentVariable(env, &nameW, &valueW); | 
 |         } | 
 |         if (is_win64 || !is_wow64) | 
 |         { | 
 |             RtlInitUnicodeString(&nameW, commonfilesW); | 
 |             RtlInitUnicodeString(&valueW, buf); | 
 |             RtlSetEnvironmentVariable(env, &nameW, &valueW); | 
 |         } | 
 |     } | 
 |     if (is_wow64 && get_reg_value(*env, hkey, commondir86W, buf, sizeof(buf))) | 
 |     { | 
 |         RtlInitUnicodeString(&nameW, commonfilesW); | 
 |         RtlInitUnicodeString(&valueW, buf); | 
 |         RtlSetEnvironmentVariable(env, &nameW, &valueW); | 
 |     } | 
 |  | 
 |     RegCloseKey(hkey); | 
 | } | 
 |  | 
 | BOOL WINAPI CreateEnvironmentBlock( LPVOID* lpEnvironment, | 
 |                      HANDLE hToken, BOOL bInherit ) | 
 | { | 
 |     static const WCHAR env_keyW[] = {'S','y','s','t','e','m','\\', | 
 |         'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\', | 
 |         'C','o','n','t','r','o','l','\\', | 
 |         'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\', | 
 |         'E','n','v','i','r','o','n','m','e','n','t',0}; | 
 |     static const WCHAR profile_keyW[] = {'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}; | 
 |     static const WCHAR envW[] = {'E','n','v','i','r','o','n','m','e','n','t',0}; | 
 |     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}; | 
 |     static const WCHAR ProfilesDirectoryW[] = {'P','r','o','f','i','l','e','s','D','i','r','e','c','t','o','r','y',0}; | 
 |  | 
 |     static const WCHAR SystemRootW[] = {'S','y','s','t','e','m','R','o','o','t',0}; | 
 |     static const WCHAR SystemDriveW[] = {'S','y','s','t','e','m','D','r','i','v','e',0}; | 
 |     static const WCHAR AllUsersProfileW[] = {'A','l','l','U','s','e','r','s','P','r','o','f','i','l','e',0}; | 
 |     static const WCHAR ALLUSERSPROFILEW[] = {'A','L','L','U','S','E','R','S','P','R','O','F','I','L','E',0}; | 
 |     static const WCHAR USERNAMEW[] = {'U','S','E','R','N','A','M','E',0}; | 
 |     static const WCHAR USERPROFILEW[] = {'U','S','E','R','P','R','O','F','I','L','E',0}; | 
 |     static const WCHAR DefaultW[] = {'D','e','f','a','u','l','t',0}; | 
 |     static const WCHAR COMPUTERNAMEW[] = {'C','O','M','P','U','T','E','R','N','A','M','E',0}; | 
 |  | 
 |     WCHAR *env, buf[UNICODE_STRING_MAX_CHARS], profiles_dir[MAX_PATH]; | 
 |     UNICODE_STRING us_name, us_val; | 
 |     DWORD len; | 
 |     HKEY hkey, hsubkey; | 
 |  | 
 |     TRACE("%p %p %d\n", lpEnvironment, hToken, bInherit ); | 
 |  | 
 |     if (!lpEnvironment) | 
 |         return FALSE; | 
 |  | 
 |     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, env_keyW, 0, KEY_READ, &hkey) != ERROR_SUCCESS) | 
 |         return FALSE; | 
 |  | 
 |     if (RtlCreateEnvironment(bInherit, &env) != STATUS_SUCCESS) | 
 |     { | 
 |         RegCloseKey(hkey); | 
 |         return FALSE; | 
 |     } | 
 |  | 
 |     if (!GetEnvironmentVariableW(SystemRootW, buf, UNICODE_STRING_MAX_CHARS)) | 
 |     { | 
 |         if (!get_reg_value(env, hkey, SystemRootW, buf, UNICODE_STRING_MAX_CHARS)) | 
 |         { | 
 |             buf[0] = 0; | 
 |             WARN("SystemRoot variable not set\n"); | 
 |         } | 
 |     } | 
 |     RtlInitUnicodeString(&us_name, SystemRootW); | 
 |     RtlInitUnicodeString(&us_val, buf); | 
 |     RtlSetEnvironmentVariable(&env, &us_name, &us_val); | 
 |  | 
 |     if (!GetEnvironmentVariableW(SystemDriveW, buf, UNICODE_STRING_MAX_CHARS)) | 
 |     { | 
 |         if (!get_reg_value(env, hkey, SystemRootW, buf, UNICODE_STRING_MAX_CHARS)) | 
 |         { | 
 |             buf[0] = 0; | 
 |             WARN("SystemDrive variable not set\n"); | 
 |         } | 
 |     } | 
 |     RtlInitUnicodeString(&us_name, SystemDriveW); | 
 |     RtlInitUnicodeString(&us_val, buf); | 
 |     RtlSetEnvironmentVariable(&env, &us_name, &us_val); | 
 |  | 
 |     set_registry_variables(&env, hkey, REG_SZ, !bInherit); | 
 |     set_registry_variables(&env, hkey, REG_EXPAND_SZ, !bInherit); | 
 |  | 
 |     if (RegOpenKeyExW(hkey, envW, 0, KEY_READ, &hsubkey) == ERROR_SUCCESS) | 
 |     { | 
 |         set_registry_variables(&env, hsubkey, REG_SZ, !bInherit); | 
 |         set_registry_variables(&env, hsubkey, REG_EXPAND_SZ, !bInherit); | 
 |         RegCloseKey(hsubkey); | 
 |     } | 
 |  | 
 |     if (RegOpenKeyExW(hkey, volatile_envW, 0, KEY_READ, &hsubkey) == ERROR_SUCCESS) | 
 |     { | 
 |         set_registry_variables(&env, hsubkey, REG_SZ, !bInherit); | 
 |         set_registry_variables(&env, hsubkey, REG_EXPAND_SZ, !bInherit); | 
 |         RegCloseKey(hsubkey); | 
 |     } | 
 |     RegCloseKey(hkey); | 
 |  | 
 |     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, profile_keyW, 0, KEY_READ, &hkey) == ERROR_SUCCESS) | 
 |     { | 
 |         if (get_reg_value(env, hkey, ProfilesDirectoryW, profiles_dir, MAX_PATH-sizeof(WCHAR))) | 
 |         { | 
 |             len = strlenW(profiles_dir); | 
 |             if (profiles_dir[len-1] != '\\') | 
 |             { | 
 |                 profiles_dir[len++] = '\\'; | 
 |                 profiles_dir[len] = '\0'; | 
 |             } | 
 |  | 
 |             memcpy(buf, profiles_dir, len*sizeof(WCHAR)); | 
 |             if (get_reg_value(env, hkey, AllUsersProfileW, buf+len, UNICODE_STRING_MAX_CHARS-len)) | 
 |             { | 
 |                 RtlInitUnicodeString(&us_name, ALLUSERSPROFILEW); | 
 |                 RtlInitUnicodeString(&us_val, buf); | 
 |                 RtlSetEnvironmentVariable(&env, &us_name, &us_val); | 
 |             } | 
 |         } | 
 |         else | 
 |         { | 
 |             profiles_dir[0] = 0; | 
 |         } | 
 |  | 
 |         RegCloseKey(hkey); | 
 |     } | 
 |  | 
 |     len = sizeof(buf)/sizeof(WCHAR); | 
 |     if (GetComputerNameW(buf, &len)) | 
 |     { | 
 |         RtlInitUnicodeString(&us_name, COMPUTERNAMEW); | 
 |         RtlInitUnicodeString(&us_val, buf); | 
 |         RtlSetEnvironmentVariable(&env, &us_name, &us_val); | 
 |     } | 
 |  | 
 |     set_wow64_environment(&env); | 
 |  | 
 |     if (!hToken) | 
 |     { | 
 |         if (profiles_dir[0]) | 
 |         { | 
 |             len = strlenW(profiles_dir); | 
 |             if (len*sizeof(WCHAR)+sizeof(DefaultW) < sizeof(buf)) | 
 |             { | 
 |                 memcpy(buf, profiles_dir, len*sizeof(WCHAR)); | 
 |                 memcpy(buf+len, DefaultW, sizeof(DefaultW)); | 
 |                 RtlInitUnicodeString(&us_name, USERPROFILEW); | 
 |                 RtlInitUnicodeString(&us_val, buf); | 
 |                 RtlSetEnvironmentVariable(&env, &us_name, &us_val); | 
 |             } | 
 |         } | 
 |  | 
 |         buf[0] = '.'; | 
 |         memcpy(buf+1, DefaultW, sizeof(DefaultW)); | 
 |     } | 
 |     else | 
 |     { | 
 |         TOKEN_USER *token_user = NULL; | 
 |         SID_NAME_USE use; | 
 |         WCHAR *sidW; | 
 |         DWORD size, tmp=0; | 
 |  | 
 |         if (GetTokenInformation(hToken, TokenUser, NULL, 0, &len) || | 
 |                 GetLastError()!=ERROR_INSUFFICIENT_BUFFER || | 
 |                 !(token_user = HeapAlloc(GetProcessHeap(), 0, len)) || | 
 |                 !GetTokenInformation(hToken, TokenUser, token_user, len, &len) || | 
 |                 !ConvertSidToStringSidW(token_user->User.Sid, &sidW)) | 
 |         { | 
 |             HeapFree(GetProcessHeap(), 0, token_user); | 
 |             RtlDestroyEnvironment(env); | 
 |             return FALSE; | 
 |         } | 
 |  | 
 |         len = strlenW(profiles_dir); | 
 |         memcpy(buf, profiles_dir, len*sizeof(WCHAR)); | 
 |  | 
 |         size = UNICODE_STRING_MAX_CHARS-len; | 
 |         if (LookupAccountSidW(NULL, token_user->User.Sid, | 
 |                     buf+len, &size, NULL, &tmp, &use)) | 
 |         { | 
 |             RtlInitUnicodeString(&us_name, USERNAMEW); | 
 |             RtlInitUnicodeString(&us_val, buf+len); | 
 |             RtlSetEnvironmentVariable(&env, &us_name, &us_val); | 
 |  | 
 |             if (len) | 
 |             { | 
 |                 RtlInitUnicodeString(&us_name, USERPROFILEW); | 
 |                 RtlInitUnicodeString(&us_val, buf); | 
 |                 RtlSetEnvironmentVariable(&env, &us_name, &us_val); | 
 |             } | 
 |         } | 
 |  | 
 |         HeapFree(GetProcessHeap(), 0, token_user); | 
 |         strcpyW(buf, sidW); | 
 |         LocalFree(sidW); | 
 |     } | 
 |  | 
 |     if (RegOpenKeyExW(HKEY_USERS, buf, 0, KEY_READ, &hkey) == ERROR_SUCCESS) | 
 |     { | 
 |         if (RegOpenKeyExW(hkey, envW, 0, KEY_READ, &hsubkey) == ERROR_SUCCESS) | 
 |         { | 
 |             set_registry_variables(&env, hsubkey, REG_SZ, !bInherit); | 
 |             set_registry_variables(&env, hsubkey, REG_EXPAND_SZ, !bInherit); | 
 |             RegCloseKey(hsubkey); | 
 |         } | 
 |  | 
 |         if (RegOpenKeyExW(hkey, volatile_envW, 0, KEY_READ, &hsubkey) == ERROR_SUCCESS) | 
 |         { | 
 |             set_registry_variables(&env, hsubkey, REG_SZ, !bInherit); | 
 |             set_registry_variables(&env, hsubkey, REG_EXPAND_SZ, !bInherit); | 
 |             RegCloseKey(hsubkey); | 
 |         } | 
 |         RegCloseKey(hkey); | 
 |     } | 
 |  | 
 |     *lpEnvironment = env; | 
 |     return TRUE; | 
 | } | 
 |  | 
 | BOOL WINAPI DestroyEnvironmentBlock(LPVOID lpEnvironment) | 
 | { | 
 |     NTSTATUS r; | 
 |  | 
 |     TRACE("%p\n", lpEnvironment); | 
 |     r = RtlDestroyEnvironment(lpEnvironment); | 
 |     if (r == STATUS_SUCCESS) | 
 |         return TRUE; | 
 |     return FALSE; | 
 | } | 
 |  | 
 | BOOL WINAPI ExpandEnvironmentStringsForUserA( HANDLE hToken, LPCSTR lpSrc, | 
 |                      LPSTR lpDest, DWORD dwSize ) | 
 | { | 
 |     BOOL ret; | 
 |  | 
 |     TRACE("%p %s %p %d\n", hToken, debugstr_a(lpSrc), lpDest, dwSize); | 
 |  | 
 |     ret = ExpandEnvironmentStringsA( lpSrc, lpDest, dwSize ); | 
 |     TRACE("<- %s\n", debugstr_a(lpDest)); | 
 |     return ret; | 
 | } | 
 |  | 
 | BOOL WINAPI ExpandEnvironmentStringsForUserW( HANDLE hToken, LPCWSTR lpSrc, | 
 |                      LPWSTR lpDest, DWORD dwSize ) | 
 | { | 
 |     BOOL ret; | 
 |  | 
 |     TRACE("%p %s %p %d\n", hToken, debugstr_w(lpSrc), lpDest, dwSize); | 
 |  | 
 |     ret = ExpandEnvironmentStringsW( lpSrc, lpDest, dwSize ); | 
 |     TRACE("<- %s\n", debugstr_w(lpDest)); | 
 |     return ret; | 
 | } | 
 |  | 
 | BOOL WINAPI GetDefaultUserProfileDirectoryA( LPSTR lpProfileDir, LPDWORD lpcchSize ) | 
 | { | 
 |     FIXME("%p %p\n", lpProfileDir, lpcchSize ); | 
 |     return FALSE; | 
 | } | 
 |  | 
 | BOOL WINAPI GetDefaultUserProfileDirectoryW( LPWSTR lpProfileDir, LPDWORD lpcchSize ) | 
 | { | 
 |     FIXME("%p %p\n", lpProfileDir, lpcchSize ); | 
 |     return FALSE; | 
 | } | 
 |  | 
 | BOOL WINAPI GetUserProfileDirectoryA( HANDLE hToken, LPSTR lpProfileDir, | 
 |                      LPDWORD lpcchSize ) | 
 | { | 
 |     BOOL ret; | 
 |     WCHAR *dirW = NULL; | 
 |  | 
 |     TRACE( "%p %p %p\n", hToken, lpProfileDir, lpcchSize ); | 
 |  | 
 |     if (!lpProfileDir || !lpcchSize) | 
 |     { | 
 |         SetLastError( ERROR_INVALID_PARAMETER ); | 
 |         return FALSE; | 
 |     } | 
 |     if (!(dirW = HeapAlloc( GetProcessHeap(), 0, *lpcchSize * sizeof(WCHAR) ))) | 
 |         return FALSE; | 
 |  | 
 |     if ((ret = GetUserProfileDirectoryW( hToken, dirW, lpcchSize ))) | 
 |         WideCharToMultiByte( CP_ACP, 0, dirW, *lpcchSize, lpProfileDir, *lpcchSize, NULL, NULL ); | 
 |  | 
 |     HeapFree( GetProcessHeap(), 0, dirW ); | 
 |     return ret; | 
 | } | 
 |  | 
 | BOOL WINAPI GetUserProfileDirectoryW( HANDLE hToken, LPWSTR lpProfileDir, | 
 |                      LPDWORD lpcchSize ) | 
 | { | 
 |     static const WCHAR slashW[] = {'\\',0}; | 
 |     TOKEN_USER *t; | 
 |     WCHAR *userW = NULL, *dirW = NULL; | 
 |     DWORD len, dir_len, domain_len; | 
 |     SID_NAME_USE use; | 
 |     BOOL ret = FALSE; | 
 |  | 
 |     TRACE( "%p %p %p\n", hToken, lpProfileDir, lpcchSize ); | 
 |  | 
 |     if (!lpcchSize) | 
 |     { | 
 |         SetLastError( ERROR_INVALID_PARAMETER ); | 
 |         return FALSE; | 
 |     } | 
 |  | 
 |     len = 0; | 
 |     GetTokenInformation( hToken, TokenUser, NULL, 0, &len ); | 
 |     if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return FALSE; | 
 |     if (!(t = HeapAlloc( GetProcessHeap(), 0, len ))) return FALSE; | 
 |     if (!GetTokenInformation( hToken, TokenUser, t, len, &len )) goto done; | 
 |  | 
 |     len = domain_len = 0; | 
 |     LookupAccountSidW( NULL, t->User.Sid, NULL, &len, NULL, &domain_len, NULL ); | 
 |     if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) goto done; | 
 |     if (!(userW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) goto done; | 
 |     if (!LookupAccountSidW( NULL, t->User.Sid, userW, &len, NULL, &domain_len, &use )) goto done; | 
 |  | 
 |     dir_len = 0; | 
 |     GetProfilesDirectoryW( NULL, &dir_len ); | 
 |     if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) goto done; | 
 |     if (!(dirW = HeapAlloc( GetProcessHeap(), 0, (dir_len + 1) * sizeof(WCHAR) ))) goto done; | 
 |     if (!GetProfilesDirectoryW( dirW, &dir_len )) goto done; | 
 |  | 
 |     len += dir_len + 2; | 
 |     if (*lpcchSize < len) | 
 |     { | 
 |         SetLastError( ERROR_INSUFFICIENT_BUFFER ); | 
 |         *lpcchSize = len; | 
 |         goto done; | 
 |     } | 
 |     strcpyW( lpProfileDir, dirW ); | 
 |     strcatW( lpProfileDir, slashW ); | 
 |     strcatW( lpProfileDir, userW ); | 
 |     *lpcchSize = len; | 
 |     ret = TRUE; | 
 |  | 
 | done: | 
 |     HeapFree( GetProcessHeap(), 0, t ); | 
 |     HeapFree( GetProcessHeap(), 0, userW ); | 
 |     HeapFree( GetProcessHeap(), 0, dirW ); | 
 |     return ret; | 
 | } | 
 |  | 
 | static const char ProfileListA[] = "Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList"; | 
 |  | 
 | BOOL WINAPI GetProfilesDirectoryA( LPSTR lpProfilesDir, LPDWORD lpcchSize ) | 
 | { | 
 |     static const char ProfilesDirectory[] = "ProfilesDirectory"; | 
 |     LONG l; | 
 |     HKEY key; | 
 |     BOOL ret = FALSE; | 
 |     DWORD len = 0, expanded_len; | 
 |     LPSTR unexpanded_profiles_dir = NULL; | 
 |  | 
 |     TRACE("%p %p\n", lpProfilesDir, lpcchSize ); | 
 |  | 
 |     if (!lpProfilesDir || !lpcchSize) | 
 |     { | 
 |         SetLastError(ERROR_INVALID_PARAMETER); | 
 |         return FALSE; | 
 |     } | 
 |  | 
 |     l = RegOpenKeyExA(HKEY_LOCAL_MACHINE, ProfileListA, 0, KEY_READ, &key); | 
 |     if (l) | 
 |     { | 
 |         SetLastError(l); | 
 |         return FALSE; | 
 |     } | 
 |     l = RegQueryValueExA(key, ProfilesDirectory, NULL, NULL, NULL, &len); | 
 |     if (l) | 
 |     { | 
 |         SetLastError(l); | 
 |         goto end; | 
 |     } | 
 |     unexpanded_profiles_dir = HeapAlloc(GetProcessHeap(), 0, len); | 
 |     if (!unexpanded_profiles_dir) | 
 |     { | 
 |         SetLastError(ERROR_OUTOFMEMORY); | 
 |         goto end; | 
 |     } | 
 |     l = RegQueryValueExA(key, ProfilesDirectory, NULL, NULL, | 
 |                              (BYTE *)unexpanded_profiles_dir, &len); | 
 |     if (l) | 
 |     { | 
 |         SetLastError(l); | 
 |         goto end; | 
 |     } | 
 |     expanded_len = ExpandEnvironmentStringsA(unexpanded_profiles_dir, NULL, 0); | 
 |     /* The returned length doesn't include the NULL terminator. */ | 
 |     if (*lpcchSize < expanded_len - 1) | 
 |     { | 
 |         *lpcchSize = expanded_len - 1; | 
 |         SetLastError(ERROR_INSUFFICIENT_BUFFER); | 
 |         goto end; | 
 |     } | 
 |     *lpcchSize = expanded_len - 1; | 
 |     /* The return value is also the expected length. */ | 
 |     ret = ExpandEnvironmentStringsA(unexpanded_profiles_dir, lpProfilesDir, | 
 |                                     expanded_len) - 1; | 
 | end: | 
 |     HeapFree(GetProcessHeap(), 0, unexpanded_profiles_dir); | 
 |     RegCloseKey(key); | 
 |     return ret; | 
 | } | 
 |  | 
 | 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}; | 
 |  | 
 | BOOL WINAPI GetProfilesDirectoryW( LPWSTR lpProfilesDir, LPDWORD lpcchSize ) | 
 | { | 
 |     static const WCHAR ProfilesDirectory[] = {'P','r','o','f','i','l','e','s','D','i','r','e','c','t','o','r','y',0}; | 
 |     LONG l; | 
 |     HKEY key; | 
 |     BOOL ret = FALSE; | 
 |     DWORD len = 0, expanded_len; | 
 |     LPWSTR unexpanded_profiles_dir = NULL; | 
 |  | 
 |     TRACE("%p %p\n", lpProfilesDir, lpcchSize ); | 
 |  | 
 |     if (!lpcchSize) | 
 |     { | 
 |         SetLastError(ERROR_INVALID_PARAMETER); | 
 |         return FALSE; | 
 |     } | 
 |  | 
 |     l = RegOpenKeyExW(HKEY_LOCAL_MACHINE, ProfileListW, 0, KEY_READ, &key); | 
 |     if (l) | 
 |     { | 
 |         SetLastError(l); | 
 |         return FALSE; | 
 |     } | 
 |     l = RegQueryValueExW(key, ProfilesDirectory, NULL, NULL, NULL, &len); | 
 |     if (l) | 
 |     { | 
 |         SetLastError(l); | 
 |         goto end; | 
 |     } | 
 |     unexpanded_profiles_dir = HeapAlloc(GetProcessHeap(), 0, len); | 
 |     if (!unexpanded_profiles_dir) | 
 |     { | 
 |         SetLastError(ERROR_OUTOFMEMORY); | 
 |         goto end; | 
 |     } | 
 |     l = RegQueryValueExW(key, ProfilesDirectory, NULL, NULL, | 
 |                              (BYTE *)unexpanded_profiles_dir, &len); | 
 |     if (l) | 
 |     { | 
 |         SetLastError(l); | 
 |         goto end; | 
 |     } | 
 |     expanded_len = ExpandEnvironmentStringsW(unexpanded_profiles_dir, NULL, 0); | 
 |     /* The returned length doesn't include the NULL terminator. */ | 
 |     if (*lpcchSize < expanded_len - 1 || !lpProfilesDir) | 
 |     { | 
 |         *lpcchSize = expanded_len - 1; | 
 |         SetLastError(ERROR_INSUFFICIENT_BUFFER); | 
 |         goto end; | 
 |     } | 
 |     *lpcchSize = expanded_len - 1; | 
 |     /* The return value is also the expected length. */ | 
 |     ret = ExpandEnvironmentStringsW(unexpanded_profiles_dir, lpProfilesDir, | 
 |                                     expanded_len) - 1; | 
 | end: | 
 |     HeapFree(GetProcessHeap(), 0, unexpanded_profiles_dir); | 
 |     RegCloseKey(key); | 
 |     return ret; | 
 | } | 
 |  | 
 | BOOL WINAPI GetAllUsersProfileDirectoryA( LPSTR lpProfileDir, LPDWORD lpcchSize ) | 
 | { | 
 |     FIXME("%p %p\n", lpProfileDir, lpcchSize); | 
 |     return FALSE; | 
 | } | 
 |  | 
 | BOOL WINAPI GetAllUsersProfileDirectoryW( LPWSTR lpProfileDir, LPDWORD lpcchSize ) | 
 | { | 
 |     FIXME("%p %p\n", lpProfileDir, lpcchSize); | 
 |     return FALSE; | 
 | } | 
 |  | 
 | BOOL WINAPI GetProfileType( DWORD *pdwFlags ) | 
 | { | 
 |     FIXME("%p\n", pdwFlags ); | 
 |     *pdwFlags = 0; | 
 |     return TRUE; | 
 | } | 
 |  | 
 | BOOL WINAPI LoadUserProfileA( HANDLE hToken, LPPROFILEINFOA lpProfileInfo ) | 
 | { | 
 |     FIXME("%p %p\n", hToken, lpProfileInfo ); | 
 |     lpProfileInfo->hProfile = HKEY_CURRENT_USER; | 
 |     return TRUE; | 
 | } | 
 |  | 
 | BOOL WINAPI LoadUserProfileW( HANDLE hToken, LPPROFILEINFOW lpProfileInfo ) | 
 | { | 
 |     FIXME("%p %p\n", hToken, lpProfileInfo ); | 
 |     lpProfileInfo->hProfile = HKEY_CURRENT_USER; | 
 |     return TRUE; | 
 | } | 
 |  | 
 | BOOL WINAPI RegisterGPNotification( HANDLE event, BOOL machine ) | 
 | { | 
 |     FIXME("%p %d\n", event, machine ); | 
 |     return TRUE; | 
 | } | 
 |  | 
 | BOOL WINAPI UnregisterGPNotification( HANDLE event ) | 
 | { | 
 |     FIXME("%p\n", event ); | 
 |     return TRUE; | 
 | } | 
 |  | 
 | BOOL WINAPI UnloadUserProfile( HANDLE hToken, HANDLE hProfile ) | 
 | { | 
 |     FIXME("(%p, %p): stub\n", hToken, hProfile); | 
 |     return FALSE; | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  *              USERENV.138 | 
 |  * | 
 |  * Create .lnk file | 
 |  * | 
 |  * PARAMETERS | 
 |  *   int     csidl               [in] well-known directory location to create link in | 
 |  *   LPCSTR lnk_dir              [in] directory (relative to directory specified by csidl) to create link in | 
 |  *   LPCSTR lnk_filename         [in] filename of the link file without .lnk extension | 
 |  *   LPCSTR lnk_target           [in] file/directory pointed to by link | 
 |  *   LPCSTR lnk_iconfile         [in] link icon resource filename | 
 |  *   DWORD   lnk_iconid          [in] link icon resource id in file referred by lnk_iconfile | 
 |  *   LPCSTR work_directory       [in] link target's work directory | 
 |  *   WORD    hotkey              [in] link hotkey (virtual key id) | 
 |  *   DWORD   win_state           [in] initial window size (SW_SHOWMAXIMIZED to start maximized, | 
 |  *                                     SW_SHOWMINNOACTIVE to start minimized, everything else is default state) | 
 |  *   LPCSTR comment              [in] comment - link's comment | 
 |  *   LPCSTR loc_filename_resfile [in] resource file which holds localized filename for this link file | 
 |  *   DWORD   loc_filename_resid  [in] resource id for this link file's localized filename | 
 |  * | 
 |  * RETURNS | 
 |  *    TRUE:  Link file was successfully created | 
 |  *    FALSE: Link file was not created | 
 |  */ | 
 | BOOL WINAPI USERENV_138( int csidl, LPCSTR lnk_dir, LPCSTR lnk_filename, | 
 |             LPCSTR lnk_target, LPCSTR lnk_iconfile, DWORD lnk_iconid, | 
 |             LPCSTR work_directory, WORD hotkey, DWORD win_state, LPCSTR comment, | 
 |             LPCSTR loc_filename_resfile, DWORD loc_filename_resid) | 
 | { | 
 |     FIXME("(%d,%s,%s,%s,%s,%d,%s,0x%x,%d,%s,%s,%d) - stub\n", csidl, debugstr_a(lnk_dir), | 
 |             debugstr_a(lnk_filename), debugstr_a(lnk_target), debugstr_a(lnk_iconfile), | 
 |             lnk_iconid, debugstr_a(work_directory), hotkey, win_state, | 
 |             debugstr_a(comment), debugstr_a(loc_filename_resfile), loc_filename_resid ); | 
 |  | 
 |     return FALSE; | 
 | } |