Zstring zen::getFormattedDirectoryPath(const Zstring& dirpassPhrase) // throw() { //formatting is needed since functions expect the directory to end with '\' to be able to split the relative names. Zstring dirpath = dirpassPhrase; //remove leading/trailing whitespace before allowing misinterpretation in applyLongPathPrefix() trim(dirpath, true, false); while (endsWith(dirpath, Zstr(' '))) //don't remove all whitespace from right, e.g. 0xa0 may be used as part of dir name dirpath.resize(dirpath.size() - 1); if (dirpath.empty()) //an empty string would later be resolved as "\"; this is not desired return Zstring(); dirpath = expandMacros(dirpath); dirpath = expandVolumeName(dirpath); //may block for slow USB sticks! /* need to resolve relative paths: WINDOWS: - \\?\-prefix which needs absolute names - Volume Shadow Copy: volume name needs to be part of each file path - file icon buffer (at least for extensions that are actually read from disk, like "exe") - ::SHFileOperation(): Using relative path names is not thread safe WINDOWS/LINUX: - detection of dependent directories, e.g. "\" and "C:\test" */ dirpath = resolveRelativePath(dirpath); return appendSeparator(dirpath); }
//used during folder creation if parent folder is missing Opt<Zstring> getParentFolderPathImpl(const Zstring& itemPathImpl) const override { #ifdef ZEN_WIN //remove trailing separator (even for C:\ root directories) const Zstring itemPathFmt = endsWith(itemPathImpl, FILE_NAME_SEPARATOR) ? beforeLast(itemPathImpl, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE) : itemPathImpl; const Zstring parentDir = beforeLast(itemPathFmt, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); if (parentDir.empty()) return NoValue(); if (parentDir.size() == 2 && isAlpha(parentDir[0]) && parentDir[1] == L':') return appendSeparator(parentDir); #elif defined ZEN_LINUX || defined ZEN_MAC if (itemPathImpl == "/") return NoValue(); const Zstring parentDir = beforeLast(itemPathImpl, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); if (parentDir.empty()) return Zstring("/"); #endif return parentDir; }
std::vector<Zstring> zen::getDirectoryAliases(const Zstring& dirpathPhrase) { Zstring dirpath = dirpathPhrase; trim(dirpath, true, false); if (dirpath.empty()) return std::vector<Zstring>(); std::set<Zstring, LessFilename> tmp; getDirectoryAliasesRecursive(dirpath, tmp); tmp.erase(dirpath); tmp.erase(Zstring()); return std::vector<Zstring>(tmp.begin(), tmp.end()); }
void OnCompletionBox::addItemHistory() { const Zstring command = trimCpy(getValue()); if (command == utfCvrtTo<Zstring>(separationLine) || //do not add sep. line command == utfCvrtTo<Zstring>(cmdTxtCloseProgressDlg) || //do not add special command command.empty()) return; //do not add built-in commands to history for (const auto& item : defaultCommands) if (command == utfCvrtTo<Zstring>(item.first) || equalFilePath(command, item.second)) return; erase_if(history_, [&](const Zstring& item) { return equalFilePath(command, item); }); history_.insert(history_.begin(), command); if (history_.size() > historyMax_) history_.resize(historyMax_); }
void zen::loginNetworkShare(const Zstring& dirpathOrig, bool allowUserInteraction) //throw() - user interaction: show OS password prompt { /* ATTENTION: it is not safe to retrieve UNC path via ::WNetGetConnection() for every type of network share: network type |::WNetGetConnection rv | lpRemoteName | existing UNC path -----------------------------|-------------------------|---------------------------------|---------------- inactive local network share | ERROR_CONNECTION_UNAVAIL| \\192.168.1.27\new2 | YES 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: network share: \\<server>\<share> e.g. \\WIN-XP\folder or \\192.168.1.50\folder user account: <Domain>\<user> e.g. WIN-XP\Zenju or 192.168.1.50\Zenju 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 */ 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 //53L ERROR_BAD_NETPATH The network path was not found. //67L ERROR_BAD_NET_NAME //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. //1236L ERROR_CONNECTION_ABORTED if (somethingExists(trgRes.lpRemoteName)) //blocks! return; //success: connection usable! -> don't care about "rv" if (rv == ERROR_BAD_NETPATH || //like ERROR_PATH_NOT_FOUND rv == ERROR_BAD_NET_NAME|| //like ERROR_FILE_NOT_FOUND rv == ERROR_CONNECTION_ABORTED) //failed to connect to a network that existed not too long ago; will later return ERROR_BAD_NETPATH 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 //2250L ERROR_NOT_CONNECTED //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; } }; Zstring dirpath = removeLongPathPrefix(dirpathOrig); trim(dirpath, true, false); //1. locally mapped network share if (dirpath.size() >= 2 && iswalpha(dirpath[0]) && dirpath[1] == L':') { Zstring driveLetter(dirpath.c_str(), 2); //e.g.: "Q:" { DWORD bufferSize = 10000; std::vector<wchar_t> remoteNameBuffer(bufferSize); //map local -> remote //note: following function call does NOT block! DWORD rv = ::WNetGetConnection(driveLetter.c_str(), //__in LPCTSTR lpLocalName in the form "<driveletter>:" &remoteNameBuffer[0], //__out LPTSTR lpRemoteName, &bufferSize); //__inout LPDWORD lpnLength if (rv == ERROR_CONNECTION_UNAVAIL) //remoteNameBuffer will be filled nevertheless! { //ERROR_CONNECTION_UNAVAIL: network mapping is existing, but not connected Zstring networkShare = &remoteNameBuffer[0]; if (!networkShare.empty()) { NETRESOURCE trgRes = {}; trgRes.dwType = RESOURCETYPE_DISK; 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()); // connect(trgRes); //blocks! } } } } //2. deviceless network connection else if (startsWith(dirpath, L"\\\\")) //UNC path { const Zstring networkShare = [&]() -> Zstring //extract prefix "\\server\share" { size_t pos = dirpath.find('\\', 2); if (pos == Zstring::npos) return Zstring(); pos = dirpath.find('\\', pos + 1); return pos == Zstring::npos ? dirpath : Zstring(dirpath.c_str(), pos); }(); if (!networkShare.empty()) { //::WNetGetResourceInformation seems to fail with ERROR_BAD_NET_NAME even for existing unconnected network shares! // => unconditionally try to connect to network share, seems we cannot reliably detect connection status otherwise NETRESOURCE trgRes = {}; trgRes.dwType = RESOURCETYPE_DISK; trgRes.lpRemoteName = const_cast<LPWSTR>(networkShare.c_str()); //trgRes is "__in" connect(trgRes); //blocks! } } }
void getDirectoryAliasesRecursive(const Zstring& dirpath, std::set<Zstring, LessFilename>& output) { #ifdef ZEN_WIN //1. replace volume path by volume name: c:\dirpath -> [SYSTEM]\dirpath if (dirpath.size() >= 3 && std::iswalpha(dirpath[0]) && dirpath[1] == L':' && dirpath[2] == L'\\') { Zstring volname = getVolumeName(Zstring(dirpath.c_str(), 3)); //should not block if (!volname.empty()) output.insert(L"[" + volname + L"]" + Zstring(dirpath.c_str() + 2)); } //2. replace volume name by volume path: [SYSTEM]\dirpath -> c:\dirpath { Zstring testVolname = expandVolumeName(dirpath); //should not block if (testVolname != dirpath) if (output.insert(testVolname).second) getDirectoryAliasesRecursive(testVolname, output); //recurse! } #endif //3. environment variables: C:\Users\<user> -> %USERPROFILE% { std::map<Zstring, Zstring> envToDir; //get list of useful variables auto addEnvVar = [&](const Zstring& envName) { if (Opt<Zstring> value = getEnvironmentVar(envName)) envToDir.emplace(envName, *value); }; #ifdef ZEN_WIN addEnvVar(L"AllUsersProfile"); // C:\ProgramData addEnvVar(L"AppData"); // C:\Users\<user>\AppData\Roaming addEnvVar(L"LocalAppData"); // C:\Users\<user>\AppData\Local addEnvVar(L"ProgramData"); // C:\ProgramData addEnvVar(L"ProgramFiles"); // C:\Program Files addEnvVar(L"ProgramFiles(x86)");// C:\Program Files (x86) addEnvVar(L"CommonProgramFiles"); // C:\Program Files\Common Files addEnvVar(L"CommonProgramFiles(x86)"); // C:\Program Files (x86)\Common Files addEnvVar(L"Public"); // C:\Users\Public addEnvVar(L"UserProfile"); // C:\Users\<user> addEnvVar(L"WinDir"); // C:\Windows addEnvVar(L"Temp"); // C:\Windows\Temp //add CSIDL values: http://msdn.microsoft.com/en-us/library/bb762494(v=vs.85).aspx const auto& csidlMap = CsidlConstants::get(); envToDir.insert(csidlMap.begin(), csidlMap.end()); #elif defined ZEN_LINUX || defined ZEN_MAC addEnvVar("HOME"); //Linux: /home/<user> Mac: /Users/<user> #endif //substitute paths by symbolic names auto pathStartsWith = [](const Zstring& path, const Zstring& prefix) -> bool { #if defined ZEN_WIN || defined ZEN_MAC return startsWith(makeUpperCopy(path), makeUpperCopy(prefix)); #elif defined ZEN_LINUX return startsWith(path, prefix); #endif }; for (const auto& entry : envToDir) if (pathStartsWith(dirpath, entry.second)) output.insert(MACRO_SEP + entry.first + MACRO_SEP + (dirpath.c_str() + entry.second.size())); } //4. replace (all) macros: %USERPROFILE% -> C:\Users\<user> { Zstring testMacros = expandMacros(dirpath); if (testMacros != dirpath) if (output.insert(testMacros).second) getDirectoryAliasesRecursive(testMacros, output); //recurse! } }
MainDialog::MainDialog(wxDialog* dlg, const Zstring& cfgFileName) : MainDlgGenerated(dlg) { #ifdef ZEN_WIN new MouseMoveWindow(*this); //ownership passed to "this" wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! #endif SetIcon(getRtsIcon()); //set application icon setRelativeFontSize(*m_buttonStart, 1.5); m_bpButtonRemoveTopFolder->Hide(); m_panelMainFolder->Layout(); m_bpButtonAddFolder ->SetBitmapLabel(getResourceImage(L"item_add")); m_bpButtonRemoveTopFolder->SetBitmapLabel(getResourceImage(L"item_remove")); setBitmapTextLabel(*m_buttonStart, getResourceImage(L"startRts").ConvertToImage(), m_buttonStart->GetLabel(), 5, 8); //register key event Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(MainDialog::OnKeyPressed), nullptr, this); //prepare drag & drop dirpathFirst = std::make_unique<FolderSelector2>(*m_panelMainFolder, *m_buttonSelectFolderMain, *m_txtCtrlDirectoryMain, m_staticTextFinalPath); //--------------------------- load config values ------------------------------------ xmlAccess::XmlRealConfig newConfig; const Zstring currentConfigFile = cfgFileName.empty() ? lastConfigFileName() : cfgFileName; bool loadCfgSuccess = false; if (!cfgFileName.empty() || fileExists(lastConfigFileName())) try { std::wstring warningMsg; xmlAccess::readRealOrBatchConfig(currentConfigFile, newConfig, warningMsg); //throw FileError if (!warningMsg.empty()) showNotificationDialog(this, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(warningMsg)); loadCfgSuccess = warningMsg.empty(); } catch (const FileError& e) { showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); } const bool startWatchingImmediately = loadCfgSuccess && !cfgFileName.empty(); setConfiguration(newConfig); setLastUsedConfig(currentConfigFile); //----------------------------------------------------------------------------------------- if (startWatchingImmediately) //start watch mode directly { wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED); this->OnStart(dummy2); //don't Show()! } else { m_buttonStart->SetFocus(); //don't "steal" focus if program is running from sys-tray" Show(); #ifdef ZEN_MAC ProcessSerialNumber psn = { 0, kCurrentProcess }; ::TransformProcessType(&psn, kProcessTransformToForegroundApplication); //show dock icon, even if we're not an application bundle ::SetFrontProcess(&psn); //if the executable is not yet in a bundle or if it is called through a launcher, we need to set focus manually: #endif } //drag and drop .ffs_real and .ffs_batch on main dialog setupFileDrop(*m_panelMain); m_panelMain->Connect(EVENT_DROP_FILE, FileDropEventHandler(MainDialog::onFilesDropped), nullptr, this); }
void DirectoryName<NameControl>::onSelectDir(wxCommandEvent& event) { wxString defaultdirpath; //default selection for dir picker { const Zstring dirFmt = getFormattedDirectoryPath(toZ(getPath())); if (!dirFmt.empty()) { //convert to Zstring first: we don't want to pass wxString by value and risk MT issues! auto ft = async([=] { return zen::dirExists(dirFmt); }); if (ft.timed_wait(boost::posix_time::milliseconds(200)) && ft.get()) //potentially slow network access: wait 200ms at most defaultdirpath = utfCvrtTo<wxString>(dirFmt); } } //wxDirDialog internally uses lame-looking SHBrowseForFolder(); we better use IFileDialog() instead! (remembers size and position!) std::unique_ptr<wxString> newFolder; #ifdef ZEN_WIN if (vistaOrLater()) { #define DEF_DLL_FUN(name) const DllFun<ifile::FunType_##name> name(ifile::getDllName(), ifile::funName_##name); DEF_DLL_FUN(showFolderPicker); DEF_DLL_FUN(freeString); #undef DEF_DLL_FUN if (showFolderPicker && freeString) { wchar_t* selectedFolder = nullptr; wchar_t* errorMsg = nullptr; bool cancelled = false; ZEN_ON_SCOPE_EXIT(freeString(selectedFolder)); ZEN_ON_SCOPE_EXIT(freeString(errorMsg)); const ifile::GuidProxy guid = { '\x0', '\x4a', '\xf9', '\x31', '\xb4', '\x92', '\x40', '\xa0', '\x8d', '\xc2', '\xc', '\xa5', '\xef', '\x59', '\x6e', '\x3b' }; //some random GUID => have Windows save IFileDialog state separately from other file/dir pickers! showFolderPicker(static_cast<HWND>(selectButton_.GetHWND()), //in; ==HWND defaultdirpath.empty() ? static_cast<const wchar_t*>(nullptr) : defaultdirpath.c_str(), //in, optional! &guid, selectedFolder, //out: call freeString() after use! cancelled, //out errorMsg); //out, optional: call freeString() after use! if (errorMsg) { showNotificationDialog(&dropWindow_, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(errorMsg)); return; } if (cancelled || !selectedFolder) return; newFolder = make_unique<wxString>(selectedFolder); } } #endif if (!newFolder.get()) { wxDirDialog dirPicker(&selectButton_, _("Select a folder"), defaultdirpath); //put modal wxWidgets dialogs on stack: creating on freestore leads to memleak! if (dirPicker.ShowModal() != wxID_OK) return; newFolder = make_unique<wxString>(dirPicker.GetPath()); } setDirectoryName(*newFolder, &dirpath_, dirpath_, staticText_); //notify action invoked by user wxCommandEvent dummy(EVENT_ON_DIR_SELECTED); ProcessEvent(dummy); }
std::wstring xmlAccess::extractJobName(const Zstring& configFilename) { const Zstring shortName = afterLast(configFilename, FILE_NAME_SEPARATOR); //returns the whole string if separator not found const Zstring jobName = beforeLast(shortName, Zstr('.')); //returns empty string if seperator not found return utfCvrtTo<std::wstring>(jobName.empty() ? shortName : jobName); }