diff --git a/winpr/include/winpr/environment.h b/winpr/include/winpr/environment.h index 21c9c8f0f..f530d59c9 100644 --- a/winpr/include/winpr/environment.h +++ b/winpr/include/winpr/environment.h @@ -134,6 +134,9 @@ extern "C" WINPR_API char** EnvironmentBlockToEnvpA(LPCH lpszEnvironmentBlock); + WINPR_API DWORD GetEnvironmentVariableX(const char* lpName, char* lpBuffer, DWORD nSize); + WINPR_API char* GetEnvAlloc(LPCSTR lpName); + #ifdef __cplusplus } #endif diff --git a/winpr/libwinpr/environment/environment.c b/winpr/libwinpr/environment/environment.c index f99df5ae7..0b07931e4 100644 --- a/winpr/libwinpr/environment/environment.c +++ b/winpr/libwinpr/environment/environment.c @@ -643,3 +643,77 @@ char** EnvironmentBlockToEnvpA(LPCH lpszEnvironmentBlock) return envp; } + +#ifdef _WIN32 + +// https://devblogs.microsoft.com/oldnewthing/20100203-00/?p=15083 +#define WINPR_MAX_ENVIRONMENT_LENGTH 2048 + +DWORD GetEnvironmentVariableX(const char* lpName, char* lpBuffer, DWORD nSize) +{ + int status; + DWORD result = 0; + DWORD nSizeW = 0; + LPWSTR lpNameW = NULL; + LPWSTR lpBufferW = NULL; + LPSTR lpBufferA = lpBuffer; + + if (ConvertToUnicode(CP_UTF8, 0, lpName, -1, &lpNameW, 0) < 1) + goto cleanup; + + if (!lpBuffer) + { + char lpBufferMaxA[WINPR_MAX_ENVIRONMENT_LENGTH]; + WCHAR lpBufferMaxW[WINPR_MAX_ENVIRONMENT_LENGTH]; + + // calling GetEnvironmentVariableX with a NULL buffer should return the expected size + // TODO: dynamically allocate the buffer, or use the theoretical limit of 32,768 characters + + lpBufferA = lpBufferMaxA; + lpBufferW = lpBufferMaxW; + nSizeW = sizeof(lpBufferMaxW) / 2; + + result = GetEnvironmentVariableW(lpNameW, lpBufferW, nSizeW); + + status = ConvertFromUnicode(CP_UTF8, 0, lpBufferW, -1, &lpBufferA, sizeof(lpBufferMaxA), + NULL, NULL); + + if (status > 0) + result = (DWORD)status; + + return result; + } + else + { + nSizeW = nSize + 1; + lpBufferW = calloc(nSizeW, 2); + + if (!lpBufferW) + goto cleanup; + + result = GetEnvironmentVariableW(lpNameW, lpBufferW, nSizeW); + + if (result == 0) + goto cleanup; + + status = ConvertFromUnicode(CP_UTF8, 0, lpBufferW, -1, &lpBufferA, nSize, NULL, NULL); + + if (status > 0) + result = (DWORD)(status - 1); + } + +cleanup: + free(lpBufferW); + free(lpNameW); + + return result; +} + +#else + +DWORD GetEnvironmentVariableX(const char* lpName, char* lpBuffer, DWORD nSize) +{ + return GetEnvironmentVariableA(lpName, lpBuffer, nSize); +} + +#endif diff --git a/winpr/libwinpr/path/shell.c b/winpr/libwinpr/path/shell.c index 23f670649..b49b9fb57 100644 --- a/winpr/libwinpr/path/shell.c +++ b/winpr/libwinpr/path/shell.c @@ -59,20 +59,24 @@ static char* GetPath_XDG_RUNTIME_DIR(void); * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html */ -static char* GetEnvAlloc(LPCSTR lpName) +char* GetEnvAlloc(LPCSTR lpName) { - DWORD length; + DWORD nSize; + DWORD nStatus; char* env = NULL; - length = GetEnvironmentVariableA(lpName, NULL, 0); - if (length > 0) + nSize = GetEnvironmentVariableX(lpName, NULL, 0); + + if (nSize > 0) { - env = malloc(length); + env = malloc(nSize); if (!env) return NULL; - if (GetEnvironmentVariableA(lpName, env, length) != length - 1) + nStatus = GetEnvironmentVariableX(lpName, env, nSize); + + if (nStatus != (nSize - 1)) { free(env); return NULL; @@ -376,7 +380,8 @@ char* GetEnvironmentPath(char* name) { char* env = NULL; DWORD nSize; - nSize = GetEnvironmentVariableA(name, NULL, 0); + DWORD nStatus; + nSize = GetEnvironmentVariableX(name, NULL, 0); if (nSize) { @@ -385,7 +390,9 @@ char* GetEnvironmentPath(char* name) if (!env) return NULL; - if (GetEnvironmentVariableA(name, env, nSize) != nSize - 1) + nStatus = GetEnvironmentVariableX(name, env, nSize); + + if (nStatus != (nSize - 1)) { free(env); return NULL;