diff options
Diffstat (limited to 'indra/llvfs/lldir_win32.cpp')
-rw-r--r-- | indra/llvfs/lldir_win32.cpp | 111 |
1 files changed, 105 insertions, 6 deletions
diff --git a/indra/llvfs/lldir_win32.cpp b/indra/llvfs/lldir_win32.cpp index 9836fa28f2..b3b3afb37e 100644 --- a/indra/llvfs/lldir_win32.cpp +++ b/indra/llvfs/lldir_win32.cpp @@ -30,7 +30,9 @@ #include "lldir_win32.h" #include "llerror.h" -#include "llrand.h" // for gLindenLabRandomNumber +#include "llstring.h" +#include "stringize.h" +#include "llfile.h" #include <shlobj.h> #include <fstream> @@ -43,15 +45,87 @@ #define PACKVERSION(major,minor) MAKELONG(minor,major) DWORD GetDllVersion(LPCTSTR lpszDllName); +namespace +{ // anonymous + enum class prst { INIT, OPEN, SKIP } state = prst::INIT; + // This is called so early that we can't count on static objects being + // properly constructed yet, so declare a pointer instead of an instance. + std::ofstream* prelogf = nullptr; + + void prelog(const std::string& message) + { + boost::optional<std::string> prelog_name; + + switch (state) + { + case prst::INIT: + // assume we failed, until we succeed + state = prst::SKIP; + + prelog_name = LLStringUtil::getoptenv("PRELOG"); + if (! prelog_name) + // no PRELOG variable set, carry on + return; + prelogf = new llofstream(*prelog_name, std::ios_base::app); + if (! (prelogf && prelogf->is_open())) + // can't complain to anybody; how? + return; + // got the log file open, cool! + state = prst::OPEN; + (*prelogf) << "========================================================================" + << std::endl; + // fall through, don't break + + case prst::OPEN: + (*prelogf) << message << std::endl; + break; + + case prst::SKIP: + // either PRELOG isn't set, or we failed to open that pathname + break; + } + } +} // anonymous namespace + +#define PRELOG(expression) prelog(STRINGIZE(expression)) + LLDir_Win32::LLDir_Win32() { // set this first: used by append() and add() methods mDirDelimiter = "\\"; + WCHAR w_str[MAX_PATH]; // Application Data is where user settings go. We rely on $APPDATA being - // correct; in fact the VMP makes a point of setting it properly, since - // Windows itself botches the job for non-ASCII usernames (MAINT-8087). - mOSUserDir = ll_safe_string(getenv("APPDATA")); + // correct. + auto APPDATA = LLStringUtil::getoptenv("APPDATA"); + if (APPDATA) + { + mOSUserDir = *APPDATA; + } + PRELOG("APPDATA='" << mOSUserDir << "'"); + // On Windows, we could have received a plain-ASCII pathname in which + // non-ASCII characters have been munged to '?', or the pathname could + // have been badly encoded and decoded such that we now have garbage + // instead of a valid path. Check that mOSUserDir actually exists. + if (mOSUserDir.empty() || ! fileExists(mOSUserDir)) + { + PRELOG("APPDATA does not exist"); + //HRESULT okay = SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, w_str); + wchar_t *pwstr = NULL; + HRESULT okay = SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &pwstr); + PRELOG("SHGetKnownFolderPath(FOLDERID_RoamingAppData) returned " << okay); + if (SUCCEEDED(okay) && pwstr) + { + // But of course, only update mOSUserDir if SHGetKnownFolderPath() works. + mOSUserDir = ll_convert_wide_to_string(pwstr); + // Not only that: update our environment so that child processes + // will see a reasonable value as well. + _wputenv_s(L"APPDATA", pwstr); + // SHGetKnownFolderPath() contract requires us to free pwstr + CoTaskMemFree(pwstr); + PRELOG("mOSUserDir='" << mOSUserDir << "'"); + } + } // We want cache files to go on the local disk, even if the // user is on a network with a "roaming profile". @@ -61,9 +135,34 @@ LLDir_Win32::LLDir_Win32() // // We used to store the cache in AppData\Roaming, and the installer // cleans up that version on upgrade. JC - mOSCacheDir = ll_safe_string(getenv("LOCALAPPDATA")); + auto LOCALAPPDATA = LLStringUtil::getoptenv("LOCALAPPDATA"); + if (LOCALAPPDATA) + { + mOSCacheDir = *LOCALAPPDATA; + } + PRELOG("LOCALAPPDATA='" << mOSCacheDir << "'"); + // Windows really does not deal well with pathnames containing non-ASCII + // characters. See above remarks about APPDATA. + if (mOSCacheDir.empty() || ! fileExists(mOSCacheDir)) + { + PRELOG("LOCALAPPDATA does not exist"); + //HRESULT okay = SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, w_str); + wchar_t *pwstr = NULL; + HRESULT okay = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &pwstr); + PRELOG("SHGetKnownFolderPath(FOLDERID_LocalAppData) returned " << okay); + if (SUCCEEDED(okay) && pwstr) + { + // But of course, only update mOSCacheDir if SHGetKnownFolderPath() works. + mOSCacheDir = ll_convert_wide_to_string(pwstr); + // Update our environment so that child processes will see a + // reasonable value as well. + _wputenv_s(L"LOCALAPPDATA", pwstr); + // SHGetKnownFolderPath() contract requires us to free pwstr + CoTaskMemFree(pwstr); + PRELOG("mOSCacheDir='" << mOSCacheDir << "'"); + } + } - WCHAR w_str[MAX_PATH]; if (GetTempPath(MAX_PATH, w_str)) { if (wcslen(w_str)) /* Flawfinder: ignore */ |