summaryrefslogtreecommitdiff
path: root/lib/resolve_path.cpp
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:21:16 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:21:16 +0200
commit6d15812d7d93370d47e63f6bf9f70be40f5a9c5d (patch)
tree8e7bde205084ca23e1766d42305824c927c2ee5f /lib/resolve_path.cpp
parent5.6 (diff)
downloadFreeFileSync-6d15812d7d93370d47e63f6bf9f70be40f5a9c5d.tar.gz
FreeFileSync-6d15812d7d93370d47e63f6bf9f70be40f5a9c5d.tar.bz2
FreeFileSync-6d15812d7d93370d47e63f6bf9f70be40f5a9c5d.zip
5.7
Diffstat (limited to 'lib/resolve_path.cpp')
-rw-r--r--lib/resolve_path.cpp295
1 files changed, 147 insertions, 148 deletions
diff --git a/lib/resolve_path.cpp b/lib/resolve_path.cpp
index 57f4ff30..425a640b 100644
--- a/lib/resolve_path.cpp
+++ b/lib/resolve_path.cpp
@@ -1,17 +1,18 @@
#include "resolve_path.h"
-#include <wx/utils.h>
-#include <zen/time.h>
-#include <wx+/string_conv.h>
#include <map>
#include <set>
+#include <zen/time.h>
#include <zen/scope_guard.h>
#include <zen/thread.h>
+#include <wx/utils.h>
+#include <wx+/string_conv.h>
#ifdef FFS_WIN
#include <zen/dll.h>
#include <Shlobj.h>
#include <zen/win.h> //includes "windows.h"
#include <zen/long_path_prefix.h>
+#include <zen/file_handling.h>
#ifdef _MSC_VER
#pragma comment(lib, "Mpr.lib")
#endif
@@ -138,119 +139,114 @@ private:
};
//caveat: function scope static initialization is not thread-safe in VS 2010! => make sure to call at app start!
-namespace
-{
struct Dummy { Dummy() { CsidlConstants::get(); }} blah;
-}
#endif
-wxString getEnvValue(const wxString& envName) //return empty on error
+std::unique_ptr<Zstring> getEnvironmentVar(const Zstring& envName) //return nullptr if not found
{
- //try to apply environment variables
- wxString envValue;
- if (wxGetEnv(envName, &envValue))
- {
- //some postprocessing:
- trim(envValue); //remove leading, trailing blanks
-
- //remove leading, trailing double-quotes
- if (startsWith(envValue, L"\"") &&
- endsWith(envValue, L"\"") &&
- envValue.length() >= 2)
- envValue = wxString(envValue.c_str() + 1, envValue.length() - 2);
- }
- return envValue;
+ wxString value;
+ if (!wxGetEnv(utfCvrtTo<wxString>(envName), &value))
+ return nullptr;
+
+ //some postprocessing:
+ trim(value); //remove leading, trailing blanks
+
+ //remove leading, trailing double-quotes
+ if (startsWith(value, L"\"") &&
+ endsWith (value, L"\"") &&
+ value.length() >= 2)
+ value = wxString(value.c_str() + 1, value.length() - 2);
+
+ return make_unique<Zstring>(utfCvrtTo<Zstring>(value));
}
-bool replaceMacro(wxString& macro) //macro without %-characters, return true if replaced successfully
+std::unique_ptr<Zstring> resolveMacro(const Zstring& macro, //macro without %-characters
+ const std::vector<std::pair<Zstring, Zstring>>& ext) //return nullptr if not resolved
{
- if (macro.IsEmpty())
- return false;
+ auto equalNoCase = [](const Zstring& lhs, const Zstring& rhs) { return utfCvrtTo<wxString>(lhs).CmpNoCase(utfCvrtTo<wxString>(rhs)) == 0; };
- //there are equally named environment variables %TIME%, %DATE% existing, so replace these first!
- if (macro.CmpNoCase(L"time") == 0)
- {
- macro = formatTime<wxString>(L"%H%M%S");
- return true;
- }
+ //there exist environment variables named %TIME%, %DATE% so check for our internal macros first!
+ if (equalNoCase(macro, Zstr("time")))
+ return make_unique<Zstring>(formatTime<Zstring>(Zstr("%H%M%S")));
- if (macro.CmpNoCase(L"date") == 0)
- {
- macro = formatTime<wxString>(FORMAT_ISO_DATE);
- return true;
- }
+ if (equalNoCase(macro, Zstr("date")))
+ return make_unique<Zstring>(formatTime<Zstring>(FORMAT_ISO_DATE));
- auto processPhrase = [&](const wchar_t* phrase, const wchar_t* format) -> bool
+ std::unique_ptr<Zstring> cand;
+ auto processPhrase = [&](const Zchar* phrase, const Zchar* format) -> bool
{
- if (macro.CmpNoCase(phrase) != 0)
+ if (!equalNoCase(macro, phrase))
return false;
- macro = formatTime<wxString>(format);
+ cand = make_unique<Zstring>(formatTime<Zstring>(format));
return true;
};
- if (processPhrase(L"weekday", L"%A")) return true;
- if (processPhrase(L"day" , L"%d")) return true;
- if (processPhrase(L"month" , L"%B")) return true;
- if (processPhrase(L"week" , L"%U")) return true;
- if (processPhrase(L"year" , L"%Y")) return true;
- if (processPhrase(L"hour" , L"%H")) return true;
- if (processPhrase(L"min" , L"%M")) return true;
- if (processPhrase(L"sec" , L"%S")) return true;
-
- //try to apply environment variables
- {
- wxString envValue = getEnvValue(macro);
- if (!envValue.empty())
- {
- macro = envValue;
- return true;
- }
- }
+ if (processPhrase(Zstr("weekday"), Zstr("%A"))) return cand;
+ if (processPhrase(Zstr("day" ), Zstr("%d"))) return cand;
+ if (processPhrase(Zstr("month" ), Zstr("%B"))) return cand;
+ if (processPhrase(Zstr("week" ), Zstr("%U"))) return cand;
+ if (processPhrase(Zstr("year" ), Zstr("%Y"))) return cand;
+ if (processPhrase(Zstr("hour" ), Zstr("%H"))) return cand;
+ if (processPhrase(Zstr("min" ), Zstr("%M"))) return cand;
+ if (processPhrase(Zstr("sec" ), Zstr("%S"))) return cand;
+
+ //check domain-specific extensions
+ {
+ auto iter = std::find_if(ext.begin(), ext.end(), [&](const std::pair<Zstring, Zstring>& p){ return equalNoCase(macro, p.first); });
+ if (iter != ext.end())
+ return make_unique<Zstring>(iter->second);
+ }
+
+ //try to resolve as environment variable
+ if (std::unique_ptr<Zstring> value = getEnvironmentVar(macro))
+ return value;
#ifdef FFS_WIN
- //try to resolve CSIDL values
+ //try to resolve as CSIDL value
{
- auto csidlMap = CsidlConstants::get();
- auto iter = csidlMap.find(toZ(macro));
+ const auto& csidlMap = CsidlConstants::get();
+ auto iter = csidlMap.find(macro);
if (iter != csidlMap.end())
- {
- macro = toWx(iter->second);
- return true;
- }
+ return make_unique<Zstring>(iter->second);
}
#endif
- return false;
+ return nullptr;
}
+const Zchar MACRO_SEP = Zstr('%');
//returns expanded or original string
-wxString expandMacros(const wxString& text)
+Zstring expandMacros(const Zstring& text, const std::vector<std::pair<Zstring, Zstring>>& ext)
{
- const wxChar SEPARATOR = L'%';
-
- if (contains(text, SEPARATOR))
+ if (contains(text, MACRO_SEP))
{
- wxString prefix = text.BeforeFirst(SEPARATOR);
- wxString rest = text.AfterFirst(SEPARATOR);
- if (contains(rest, SEPARATOR))
+ Zstring prefix = beforeFirst(text, MACRO_SEP);
+ Zstring rest = afterFirst (text, MACRO_SEP);
+ if (contains(rest, MACRO_SEP))
{
- wxString potentialMacro = beforeFirst(rest, SEPARATOR);
- wxString postfix = afterFirst (rest, SEPARATOR); //text == prefix + SEPARATOR + potentialMacro + SEPARATOR + postfix
+ Zstring potentialMacro = beforeFirst(rest, MACRO_SEP);
+ Zstring postfix = afterFirst (rest, MACRO_SEP); //text == prefix + MACRO_SEP + potentialMacro + MACRO_SEP + postfix
- if (replaceMacro(potentialMacro))
- return prefix + potentialMacro + expandMacros(postfix);
+ if (std::unique_ptr<Zstring> value = resolveMacro(potentialMacro, ext))
+ return prefix + *value + expandMacros(postfix, ext);
else
- return prefix + SEPARATOR + potentialMacro + expandMacros(SEPARATOR + postfix);
+ return prefix + MACRO_SEP + potentialMacro + expandMacros(MACRO_SEP + postfix, ext);
}
}
return text;
}
+}
+
+
+Zstring zen::expandMacros(const Zstring& text) { return ::expandMacros(text, std::vector<std::pair<Zstring, Zstring>>()); }
+namespace
+{
#ifdef FFS_LINUX
class TraverseMedia : public zen::TraverseCallback
{
@@ -266,7 +262,7 @@ public:
devices_.insert(std::make_pair(shortName, fullName));
return nullptr; //DON'T traverse into subdirs
}
- virtual HandleError onError(const std::wstring& errorText) { return ON_ERROR_IGNORE; }
+ virtual HandleError onError(const std::wstring& msg) { return ON_ERROR_IGNORE; }
private:
DeviceList& devices_;
@@ -275,7 +271,7 @@ private:
//networks and cdrom excluded - this should not block
-Zstring volumenNameToPath(const Zstring& volumeName) //return empty string on error
+Zstring getPathByVolumenName(const Zstring& volumeName) //return empty string on error
{
#ifdef FFS_WIN
//FindFirstVolume(): traverses volumes on local hard disks only!
@@ -340,7 +336,7 @@ Zstring volumenNameToPath(const Zstring& volumeName) //return empty string on er
#ifdef FFS_WIN
//networks and cdrom excluded - this should not block
-Zstring volumePathToName(const Zstring& volumePath) //return empty string on error
+Zstring getVolumeName(const Zstring& volumePath) //return empty string on error
{
UINT rv = ::GetDriveType(volumePath.c_str()); //non-blocking call!
if (rv != DRIVE_REMOTE &&
@@ -387,7 +383,7 @@ Zstring expandVolumeName(const Zstring& text) // [volname]:\folder [volna
//[.*] pattern was found...
if (!volname.empty())
{
- Zstring volPath = volumenNameToPath(volname); //should not block?!
+ Zstring volPath = getPathByVolumenName(volname); //should not block?!
if (!volPath.empty())
return appendSeparator(volPath) + rest; //successfully replaced pattern
}
@@ -421,7 +417,7 @@ void getDirectoryAliasesRecursive(const Zstring& dirname, std::set<Zstring, Less
dirname[1] == L':' &&
dirname[2] == L'\\')
{
- Zstring volname = volumePathToName(Zstring(dirname.c_str(), 3)); //should not block
+ Zstring volname = getVolumeName(Zstring(dirname.c_str(), 3)); //should not block
if (!volname.empty())
output.insert(L"[" + volname + L"]" + Zstring(dirname.c_str() + 2));
}
@@ -439,11 +435,10 @@ void getDirectoryAliasesRecursive(const Zstring& dirname, std::set<Zstring, Less
std::map<Zstring, Zstring> envToDir;
//get list of useful variables
- auto addEnvVar = [&](const wxString& envName)
+ auto addEnvVar = [&](const Zstring& envName)
{
- wxString envVal = getEnvValue(envName); //return empty on error
- if (!envVal.empty())
- envToDir.insert(std::make_pair(toZ(envName), toZ(envVal)));
+ if (std::unique_ptr<Zstring> value = getEnvironmentVar(envName))
+ envToDir.insert(std::make_pair(envName, *value));
};
addEnvVar(L"AllUsersProfile"); // C:\ProgramData
addEnvVar(L"AppData"); // C:\Users\username\AppData\Roaming
@@ -457,9 +452,10 @@ void getDirectoryAliasesRecursive(const Zstring& dirname, std::set<Zstring, Less
addEnvVar(L"Temp"); // C:\Windows\Temp
//add CSIDL values: http://msdn.microsoft.com/en-us/library/bb762494(v=vs.85).aspx
- auto csidlMap = CsidlConstants::get();
+ const auto& csidlMap = CsidlConstants::get();
envToDir.insert(csidlMap.begin(), csidlMap.end());
+ //substitute paths by symbolic names
Zstring tmp = dirname;
::makeUpper(tmp);
std::for_each(envToDir.begin(), envToDir.end(),
@@ -468,13 +464,13 @@ void getDirectoryAliasesRecursive(const Zstring& dirname, std::set<Zstring, Less
Zstring tmp2 = entry.second; //case-insensitive "startsWith()"
::makeUpper(tmp2); //
if (startsWith(tmp, tmp2))
- output.insert(L"%" + entry.first + L"%" + (dirname.c_str() + tmp2.size()));
+ output.insert(MACRO_SEP + entry.first + MACRO_SEP + (dirname.c_str() + tmp2.size()));
});
}
//4. replace (all) macros: %USERPROFILE% -> C:\Users\username
{
- Zstring testMacros = toZ(expandMacros(toWx(dirname)));
+ Zstring testMacros = expandMacros(dirname);
if (testMacros != dirname)
if (output.insert(testMacros).second)
getDirectoryAliasesRecursive(testMacros, output); //recurse!
@@ -502,9 +498,9 @@ std::vector<Zstring> zen::getDirectoryAliases(const Zstring& dirString)
Zstring zen::getFormattedDirectoryName(const Zstring& dirString) // throw()
{
- //Formatting is needed since functions expect the directory to end with '\' to be able to split the relative names.
+ //formatting is needed since functions expect the directory to end with '\' to be able to split the relative names.
- Zstring dirname = toZ(expandMacros(toWx(dirString)));
+ Zstring dirname = expandMacros(dirString);
dirname = expandVolumeName(dirname); //should not block
@@ -517,7 +513,7 @@ Zstring zen::getFormattedDirectoryName(const Zstring& dirString) // throw()
return Zstring();
/*
- resolve relative names; required by:
+ need to resolve relative paths:
WINDOWS:
- \\?\-prefix which needs absolute names
- Volume Shadow Copy: volume name needs to be part of each filename
@@ -544,14 +540,68 @@ void zen::loginNetworkShare(const Zstring& dirnameOrig, bool allowUserInteractio
WebDrive | NO_ERROR | \\Webdrive-ZenJu\GNU | NO
Box.net (WebDav) | NO_ERROR | \\www.box.net\DavWWWRoot\dav | YES
NetDrive | ERROR_NOT_CONNECTED | <empty> | NO
+ ____________________________________________________________________________________________________________
+
+ Windows Login Prompt Naming Conventions:
+ user account: <Domain>\<user> e.g. WIN-XP\ZenJu
+ network share: \\<server>\<share> e.g. \\WIN-XP\test
+
+ Windows Command Line:
+ - list *all* active network connections, including deviceless ones which are hidden in Explorer:
+ net use
+ - delete active connection:
+ net use /delete \\server\share
+ ____________________________________________________________________________________________________________
+
+ Scenario: XP-shared folder is accessed by Win 7 over LAN with access limited to a certain user
+
+ Problems:
+ I. WNetAddConnection2() allows (at least certain) invalid credentials (e.g. username: a/password: a) and establishes an *unusable* connection
+ II. WNetAddConnection2() refuses to overwrite an existing (unusable) connection created in I), but shows prompt repeatedly
+ III. WNetAddConnection2() won't bring up the prompt if *wrong* credentials had been entered just recently, even with CONNECT_INTERACTIVE specified! => 2-step proccess
*/
- //if (::GetFileAttributes((driveLetter + L'\\').c_str()) == INVALID_FILE_ATTRIBUTES) <- this will seriously block if network is not reachable!!!
+ auto connect = [&](NETRESOURCE& trgRes) //blocks heavily if network is not reachable!!!
+ {
+ //1. first try to connect without user interaction - blocks!
+ DWORD rv = ::WNetAddConnection2(&trgRes, // __in LPNETRESOURCE lpNetResource,
+ nullptr, // __in LPCTSTR lpPassword,
+ nullptr, // __in LPCTSTR lpUsername,
+ 0); //__in DWORD dwFlags
+ if (somethingExists(trgRes.lpRemoteName)) //blocks!
+ return; //success: connection usable! -> don't care about "rv"
+
+ if (rv == ERROR_BAD_NETPATH || //Windows 7
+ rv == ERROR_BAD_NET_NAME) //XP
+ return; //no need to show a prompt for an unreachable network device
+
+ //2. if first attempt failed, we need to *force* prompt by using CONNECT_PROMPT
+ if (allowUserInteraction)
+ {
+ //avoid problem II.)
+ DWORD rv2= WNetCancelConnection2(trgRes.lpRemoteName, //_In_ LPCTSTR lpName,
+ 0, //_In_ DWORD dwFlags,
+ true); //_In_ BOOL fForce
+ //enforce login prompt
+ DWORD rv3 = ::WNetAddConnection2(&trgRes, // __in LPNETRESOURCE lpNetResource,
+ nullptr, // __in LPCTSTR lpPassword,
+ nullptr, // __in LPCTSTR lpUsername,
+ CONNECT_INTERACTIVE | CONNECT_PROMPT); //__in DWORD dwFlags
+ (void)rv2;
+ (void)rv3;
+ //Sample error codes:
+ //53L ERROR_BAD_NETPATH The network path was not found.
+ //86L ERROR_INVALID_PASSWORD
+ //1219L ERROR_SESSION_CREDENTIAL_CONFLICT Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again.
+ //1326L ERROR_LOGON_FAILURE Logon failure: unknown user name or bad password.
+ }
+ };
+
Zstring dirname = removeLongPathPrefix(dirnameOrig);
trim(dirname, true, false);
- //1. local path
+ //1. locally mapped network share
if (dirname.size() >= 2 && iswalpha(dirname[0]) && dirname[1] == L':')
{
Zstring driveLetter(dirname.c_str(), 2); //e.g.: "Q:"
@@ -577,22 +627,13 @@ void zen::loginNetworkShare(const Zstring& dirnameOrig, bool allowUserInteractio
trgRes.lpLocalName = const_cast<LPWSTR>(driveLetter.c_str()); //lpNetResource is marked "__in", seems WNetAddConnection2 is not const correct!
trgRes.lpRemoteName = const_cast<LPWSTR>(networkShare.c_str()); //
- //note: following function call may block heavily if network is not reachable!!!
- DWORD rv2 = ::WNetAddConnection2(&trgRes, // __in LPNETRESOURCE lpNetResource,
- nullptr, // __in LPCTSTR lpPassword,
- nullptr, // __in LPCTSTR lpUsername,
- allowUserInteraction ? CONNECT_INTERACTIVE : 0); //__in DWORD dwFlags
- if (rv2 == NO_ERROR)
- return; //mapping reestablished
-
- //restoring connection failed for some reason...
- //we could use full UNC path instead: networkShare + (dirname.c_str() + 2); //replace "Q:\subdir" by "\\server\share\subdir"
+ connect(trgRes); //blocks!
}
}
}
}
- //2. UNC path
- else if (startsWith(dirname, L"\\\\"))
+ //2. deviceless network connection
+ else if (startsWith(dirname, L"\\\\")) //UNC path
{
const Zstring networkShare = [&]() -> Zstring //extract prefix "\\server\share"
{
@@ -612,49 +653,7 @@ void zen::loginNetworkShare(const Zstring& dirnameOrig, bool allowUserInteractio
trgRes.dwType = RESOURCETYPE_DISK;
trgRes.lpRemoteName = const_cast<LPWSTR>(networkShare.c_str()); //trgRes is "__in"
- //following function call may block heavily if network is not reachable!!!
- DWORD rv2 = ::WNetAddConnection2(&trgRes, // __in LPNETRESOURCE lpNetResource,
- nullptr, // __in LPCTSTR lpPassword,
- nullptr, // __in LPCTSTR lpUsername,
- allowUserInteraction ? CONNECT_INTERACTIVE : 0); //__in DWORD dwFlags
- if (rv2 == NO_ERROR)
- return; //mapping reestablished
-
- /*
- NETRESOURCE nr = {};
- nr.dwType = RESOURCETYPE_DISK;
- nr.lpRemoteName = const_cast<LPWSTR>(networkShare.c_str()); //nr is "__in"
-
- DWORD bufferSize = sizeof(NETRESOURCE) + 20000;
- std::vector<char> buffer(bufferSize);
-
- LPTSTR relPath = nullptr;
-
- //note: following function call may block heavily if network is not reachable!!!
- const DWORD rv = WNetGetResourceInformation(&nr, // __in LPNETRESOURCE lpNetResource,
- &buffer[0], // __out LPVOID lpBuffer,
- &bufferSize, // __inout LPDWORD lpcbBuffer,
- &relPath); // __out LPTSTR *lplpSystem
- if (rv == NO_ERROR)
- {
- //NO_ERROR: network share is existing, *either* connected or disconnected
-
- //we have no way to check if network is already connected, so let's try to connect anyway:
-
- NETRESOURCE& trgRes = reinterpret_cast<NETRESOURCE&>(buffer[0]);
-
- if (trgRes.dwUsage & RESOURCEUSAGE_CONNECTABLE)
- {
- //note: following function call may block heavily if network is not reachable!!!
- DWORD rv2 = ::WNetAddConnection2(&trgRes, // __in LPNETRESOURCE lpNetResource,
- nullptr, // __in LPCTSTR lpPassword,
- nullptr, // __in LPCTSTR lpUsername,
- allowUserInteraction ? CONNECT_INTERACTIVE : 0); //__in DWORD dwFlags
- if (rv2 == NO_ERROR)
- return; //mapping reestablished
- }
- }
- */
+ connect(trgRes); //blocks!
}
}
}
bgstack15