void CRemoteRecursiveOperation::ProcessDirectoryListing(const CDirectoryListing* pDirectoryListing) { if (!pDirectoryListing) { StopRecursiveOperation(); return; } if (m_operationMode == recursive_none || recursion_roots_.empty()) return; if (pDirectoryListing->failed()) { // Ignore this. // It will get handled by the failed command in ListingFailed return; } auto & root = recursion_roots_.front(); wxASSERT(!root.m_dirsToVisit.empty()); if (!m_state.IsRemoteConnected() || root.m_dirsToVisit.empty()) { StopRecursiveOperation(); return; } recursion_root::new_dir dir = root.m_dirsToVisit.front(); root.m_dirsToVisit.pop_front(); if (!BelowRecursionRoot(pDirectoryListing->path, dir)) { NextOperation(); return; } if (m_operationMode == recursive_delete && dir.doVisit && !dir.subdir.empty()) { // After recursing into directory to delete its contents, delete directory itself // Gets handled in NextOperation recursion_root::new_dir dir2 = dir; dir2.doVisit = false; root.m_dirsToVisit.push_front(dir2); } if (dir.link && !dir.recurse) { NextOperation(); return; } // Check if we have already visited the directory if (!root.m_visitedDirs.insert(pDirectoryListing->path).second) { NextOperation(); return; } ++m_processedDirectories; const CServer* pServer = m_state.GetServer(); wxASSERT(pServer); if (!pDirectoryListing->GetCount()) { if (m_operationMode == recursive_transfer) { wxFileName::Mkdir(dir.localDir.GetPath(), 0777, wxPATH_MKDIR_FULL); m_state.RefreshLocalFile(dir.localDir.GetPath()); } else if (m_operationMode == recursive_addtoqueue) { m_pQueue->QueueFile(true, true, _T(""), _T(""), dir.localDir, CServerPath(), *pServer, -1); m_pQueue->QueueFile_Finish(false); } } CFilterManager filter; // Is operation restricted to a single child? bool const restrict = static_cast<bool>(dir.restrict); std::deque<wxString> filesToDelete; const wxString path = pDirectoryListing->path.GetPath(); bool added = false; for (int i = pDirectoryListing->GetCount() - 1; i >= 0; --i) { const CDirentry& entry = (*pDirectoryListing)[i]; if (restrict) { if (entry.name != *dir.restrict) continue; } else if (filter.FilenameFiltered(m_filters, entry.name, path, entry.is_dir(), entry.size, 0, entry.time)) continue; if (!entry.is_dir()) { ++m_processedFiles; } if (entry.is_dir() && (!entry.is_link() || m_operationMode != recursive_delete)) { if (dir.recurse) { recursion_root::new_dir dirToVisit; dirToVisit.parent = pDirectoryListing->path; dirToVisit.subdir = entry.name; dirToVisit.localDir = dir.localDir; dirToVisit.start_dir = dir.start_dir; if (m_operationMode == recursive_transfer || m_operationMode == recursive_addtoqueue) { // Non-flatten case dirToVisit.localDir.AddSegment(CQueueView::ReplaceInvalidCharacters(entry.name)); } if (entry.is_link()) { dirToVisit.link = 1; dirToVisit.recurse = false; } root.m_dirsToVisit.push_front(dirToVisit); } } else { switch (m_operationMode) { case recursive_transfer: case recursive_transfer_flatten: { wxString localFile = CQueueView::ReplaceInvalidCharacters(entry.name); if (pDirectoryListing->path.GetType() == VMS && COptions::Get()->GetOptionVal(OPTION_STRIP_VMS_REVISION)) localFile = StripVMSRevision(localFile); m_pQueue->QueueFile(m_operationMode == recursive_addtoqueue, true, entry.name, (entry.name == localFile) ? wxString() : localFile, dir.localDir, pDirectoryListing->path, *pServer, entry.size); added = true; } break; case recursive_addtoqueue: case recursive_addtoqueue_flatten: { wxString localFile = CQueueView::ReplaceInvalidCharacters(entry.name); if (pDirectoryListing->path.GetType() == VMS && COptions::Get()->GetOptionVal(OPTION_STRIP_VMS_REVISION)) localFile = StripVMSRevision(localFile); m_pQueue->QueueFile(true, true, entry.name, (entry.name == localFile) ? wxString() : localFile, dir.localDir, pDirectoryListing->path, *pServer, entry.size); added = true; } break; case recursive_delete: filesToDelete.push_back(entry.name); break; default: break; } } if (m_operationMode == recursive_chmod && m_pChmodDlg) { const int applyType = m_pChmodDlg->GetApplyType(); if (!applyType || (!entry.is_dir() && applyType == 1) || (entry.is_dir() && applyType == 2)) { char permissions[9]; bool res = m_pChmodDlg->ConvertPermissions(*entry.permissions, permissions); wxString newPerms = m_pChmodDlg->GetPermissions(res ? permissions : 0, entry.is_dir()); m_state.m_pCommandQueue->ProcessCommand(new CChmodCommand(pDirectoryListing->path, entry.name, newPerms), CCommandQueue::recursiveOperation); } } } if (added) m_pQueue->QueueFile_Finish(m_operationMode != recursive_addtoqueue && m_operationMode != recursive_addtoqueue_flatten); if (m_operationMode == recursive_delete && !filesToDelete.empty()) m_state.m_pCommandQueue->ProcessCommand(new CDeleteCommand(pDirectoryListing->path, std::move(filesToDelete)), CCommandQueue::recursiveOperation); m_state.NotifyHandlers(STATECHANGE_REMOTE_RECURSION_STATUS); NextOperation(); }
void CRemoteRecursiveOperation::ProcessDirectoryListing(const CDirectoryListing* pDirectoryListing) { if (!pDirectoryListing) { StopRecursiveOperation(); return; } if (m_operationMode == recursive_none || recursion_roots_.empty()) return; if (pDirectoryListing->failed()) { // Ignore this. // It will get handled by the failed command in ListingFailed return; } auto & root = recursion_roots_.front(); wxASSERT(!root.m_dirsToVisit.empty()); if (!m_state.IsRemoteConnected() || root.m_dirsToVisit.empty()) { StopRecursiveOperation(); return; } recursion_root::new_dir dir = root.m_dirsToVisit.front(); root.m_dirsToVisit.pop_front(); if (!BelowRecursionRoot(pDirectoryListing->path, dir)) { NextOperation(); return; } if (m_operationMode == recursive_delete && dir.doVisit && !dir.subdir.empty()) { // After recursing into directory to delete its contents, delete directory itself // Gets handled in NextOperation recursion_root::new_dir dir2 = dir; dir2.doVisit = false; root.m_dirsToVisit.push_front(dir2); } if (dir.link && !dir.recurse) { NextOperation(); return; } // Check if we have already visited the directory if (!root.m_visitedDirs.insert(pDirectoryListing->path).second) { NextOperation(); return; } ++m_processedDirectories; const CServer* pServer = m_state.GetServer(); wxASSERT(pServer); if (!pDirectoryListing->GetCount() && m_operationMode == recursive_transfer) { if (m_immediate) { wxFileName::Mkdir(dir.localDir.GetPath(), 0777, wxPATH_MKDIR_FULL); m_state.RefreshLocalFile(dir.localDir.GetPath()); } else { m_pQueue->QueueFile(true, true, _T(""), _T(""), dir.localDir, CServerPath(), *pServer, -1); m_pQueue->QueueFile_Finish(false); } } CFilterManager filter; // Is operation restricted to a single child? bool const restrict = static_cast<bool>(dir.restrict); std::deque<std::wstring> filesToDelete; std::wstring const remotePath = pDirectoryListing->path.GetPath(); if (m_operationMode == recursive_synchronize_download && !dir.localDir.empty()) { // Step one in synchronization: Delete local files not on the server fz::local_filesys fs; if (fs.begin_find_files(fz::to_native(dir.localDir.GetPath()))) { std::list<fz::native_string> paths_to_delete; bool isLink{}; fz::native_string name; bool isDir{}; int64_t size{}; fz::datetime time; int attributes{}; while (fs.get_next_file(name, isLink, isDir, &size, &time, &attributes)) { if (isLink) { continue; } auto const wname = fz::to_wstring(name); if (filter.FilenameFiltered(m_filters.first, wname, dir.localDir.GetPath(), isDir, size, attributes, time)) { continue; } // Local item isn't filtered int remoteIndex = pDirectoryListing->FindFile_CmpCase(fz::to_wstring(name)); if (remoteIndex != -1) { CDirentry const& entry = (*pDirectoryListing)[remoteIndex]; if (!filter.FilenameFiltered(m_filters.second, entry.name, remotePath, entry.is_dir(), entry.size, 0, entry.time)) { // Both local and remote items exist if (isDir == entry.is_dir() || entry.is_link()) { // Normal item, nothing we should do continue; } } } // Local item should be deleted if reaching this point paths_to_delete.push_back(fz::to_native(dir.localDir.GetPath()) + name); } fz::recursive_remove r; r.remove(paths_to_delete); } } bool added = false; for (int i = pDirectoryListing->GetCount() - 1; i >= 0; --i) { const CDirentry& entry = (*pDirectoryListing)[i]; if (restrict) { if (entry.name != *dir.restrict) continue; } else if (filter.FilenameFiltered(m_filters.second, entry.name, remotePath, entry.is_dir(), entry.size, 0, entry.time)) continue; if (!entry.is_dir()) { ++m_processedFiles; } if (entry.is_dir() && (!entry.is_link() || m_operationMode != recursive_delete)) { if (dir.recurse) { recursion_root::new_dir dirToVisit; dirToVisit.parent = pDirectoryListing->path; dirToVisit.subdir = entry.name; dirToVisit.localDir = dir.localDir; dirToVisit.start_dir = dir.start_dir; if (m_operationMode == recursive_transfer || m_operationMode == recursive_synchronize_download) { // Non-flatten case dirToVisit.localDir.AddSegment(CQueueView::ReplaceInvalidCharacters(entry.name)); } if (entry.is_link()) { dirToVisit.link = 1; dirToVisit.recurse = false; } root.m_dirsToVisit.push_front(dirToVisit); } } else { switch (m_operationMode) { case recursive_transfer: case recursive_transfer_flatten: case recursive_synchronize_download: { std::wstring localFile = CQueueView::ReplaceInvalidCharacters(entry.name); if (pDirectoryListing->path.GetType() == VMS && COptions::Get()->GetOptionVal(OPTION_STRIP_VMS_REVISION)) { localFile = StripVMSRevision(localFile); } m_pQueue->QueueFile(!m_immediate, true, entry.name, (entry.name == localFile) ? std::wstring() : localFile, dir.localDir, pDirectoryListing->path, *pServer, entry.size); added = true; } break; case recursive_delete: filesToDelete.push_back(entry.name); break; default: break; } } if (m_operationMode == recursive_chmod && m_pChmodDlg) { const int applyType = m_pChmodDlg->GetApplyType(); if (!applyType || (!entry.is_dir() && applyType == 1) || (entry.is_dir() && applyType == 2)) { char permissions[9]; bool res = m_pChmodDlg->ConvertPermissions(*entry.permissions, permissions); std::wstring newPerms = m_pChmodDlg->GetPermissions(res ? permissions : 0, entry.is_dir()).ToStdWstring(); m_state.m_pCommandQueue->ProcessCommand(new CChmodCommand(pDirectoryListing->path, entry.name, newPerms), CCommandQueue::recursiveOperation); } } } if (added) { m_pQueue->QueueFile_Finish(m_immediate); } if (m_operationMode == recursive_delete && !filesToDelete.empty()) { m_state.m_pCommandQueue->ProcessCommand(new CDeleteCommand(pDirectoryListing->path, std::move(filesToDelete)), CCommandQueue::recursiveOperation); } m_state.NotifyHandlers(STATECHANGE_REMOTE_RECURSION_STATUS); NextOperation(); }