source: filezilla/trunk/fuentes/src/interface/recursive_operation.cpp @ 3185

Last change on this file since 3185 was 3185, checked in by jrpelegrina, 3 years ago

Update new version: 3.15.02

File size: 13.0 KB
Line 
1#include <filezilla.h>
2#include "recursive_operation.h"
3#include "commandqueue.h"
4#include "chmoddialog.h"
5#include "filter.h"
6#include "Options.h"
7#include "queue.h"
8
9recursion_root::recursion_root(CServerPath const& start_dir, bool allow_parent)
10        : m_startDir(start_dir)
11        , m_allowParent(allow_parent)
12{
13        wxASSERT_MSG(!start_dir.empty(), _T("Empty startDir in recursion_root constructor"));
14}
15
16void recursion_root::add_dir_to_visit(CServerPath const& path, wxString const& subdir, CLocalPath const& localDir, bool is_link)
17{
18        new_dir dirToVisit;
19
20        dirToVisit.localDir = localDir;
21        dirToVisit.parent = path;
22        dirToVisit.subdir = subdir;
23        dirToVisit.link = is_link ? 2 : 0;
24        m_dirsToVisit.push_back(dirToVisit);
25}
26
27void recursion_root::add_dir_to_visit_restricted(CServerPath const& path, wxString const& restrict, bool recurse)
28{
29        new_dir dirToVisit;
30        dirToVisit.parent = path;
31        dirToVisit.recurse = recurse;
32        if (!restrict.empty()) {
33                dirToVisit.restrict = CSparseOptional<wxString>(restrict);
34        }
35        m_dirsToVisit.push_back(dirToVisit);
36}
37
38
39
40CRecursiveOperation::CRecursiveOperation(CState* pState)
41        : CStateEventHandler(pState),
42          m_operationMode(recursive_none)
43{
44        pState->RegisterHandler(this, STATECHANGE_REMOTE_DIR_OTHER);
45        pState->RegisterHandler(this, STATECHANGE_REMOTE_LINKNOTDIR);
46}
47
48CRecursiveOperation::~CRecursiveOperation()
49{
50        if (m_pChmodDlg)
51        {
52                m_pChmodDlg->Destroy();
53                m_pChmodDlg = 0;
54        }
55}
56
57void CRecursiveOperation::OnStateChange(CState*, enum t_statechange_notifications notification, const wxString&, const void* data2)
58{
59        if (notification == STATECHANGE_REMOTE_DIR_OTHER && data2) {
60                std::shared_ptr<CDirectoryListing> const& listing = *reinterpret_cast<std::shared_ptr<CDirectoryListing> const*>(data2);
61                ProcessDirectoryListing(listing.get());
62        }
63        else if (notification == STATECHANGE_REMOTE_LINKNOTDIR) {
64                wxASSERT(data2);
65                LinkIsNotDir();
66        }
67}
68
69void CRecursiveOperation::AddRecursionRoot(recursion_root && root)
70{
71        if (!root.empty()) {
72                recursion_roots_.push_back(std::forward<recursion_root>(root));
73        }
74}
75
76void CRecursiveOperation::StartRecursiveOperation(OperationMode mode, std::vector<CFilter> const& filters, CServerPath const& finalDir)
77{
78        wxCHECK_RET(m_operationMode == recursive_none, _T("StartRecursiveOperation called with m_operationMode != recursive_none"));
79        wxCHECK_RET(m_pState->IsRemoteConnected(), _T("StartRecursiveOperation while disconnected"));
80        wxCHECK_RET(!finalDir.empty(), _T("Empty final dir in recursive operation"));
81
82        if (mode == recursive_chmod && !m_pChmodDlg)
83                return;
84
85        if ((mode == recursive_download || mode == recursive_addtoqueue || mode == recursive_download_flatten || mode == recursive_addtoqueue_flatten) && !m_pQueue)
86                return;
87
88        if (recursion_roots_.empty()) {
89                // Nothing to do in this case
90                return;
91        }
92
93        m_processedFiles = 0;
94        m_processedDirectories = 0;
95
96        m_operationMode = mode;
97        m_pState->NotifyHandlers(STATECHANGE_REMOTE_IDLE);
98        m_pState->NotifyHandlers(STATECHANGE_RECURSION_STATUS);
99
100        m_filters = filters;
101
102        NextOperation();
103}
104
105bool CRecursiveOperation::NextOperation()
106{
107        if (m_operationMode == recursive_none)
108                return false;
109
110        while (!recursion_roots_.empty()) {
111                auto & root = recursion_roots_.front();
112                while (!root.m_dirsToVisit.empty()) {
113                        const recursion_root::new_dir& dirToVisit = root.m_dirsToVisit.front();
114                        if (m_operationMode == recursive_delete && !dirToVisit.doVisit) {
115                                m_pState->m_pCommandQueue->ProcessCommand(new CRemoveDirCommand(dirToVisit.parent, dirToVisit.subdir), CCommandQueue::recursiveOperation);
116                                root.m_dirsToVisit.pop_front();
117                                continue;
118                        }
119
120                        CListCommand* cmd = new CListCommand(dirToVisit.parent, dirToVisit.subdir, dirToVisit.link ? LIST_FLAG_LINK : 0);
121                        m_pState->m_pCommandQueue->ProcessCommand(cmd, CCommandQueue::recursiveOperation);
122                        return true;
123                }
124
125                recursion_roots_.pop_front();
126        }
127
128        if (m_operationMode == recursive_delete && !m_finalDir.empty()) {
129                // After a deletion we cannot refresh if inside the deleted directories. Navigate user out if it
130                auto curPath = m_pState->GetRemotePath();
131                if (!curPath.empty() && (curPath == m_finalDir || m_finalDir.IsParentOf(curPath, false))) {
132                        StopRecursiveOperation();
133                        m_pState->ChangeRemoteDir(m_finalDir, wxString(), LIST_FLAG_REFRESH);
134                        return false;
135                }
136        }
137
138        StopRecursiveOperation();
139        m_pState->RefreshRemote();
140        return false;
141}
142
143bool CRecursiveOperation::BelowRecursionRoot(const CServerPath& path, recursion_root::new_dir &dir)
144{
145        if (!dir.start_dir.empty()) {
146                if (path.IsSubdirOf(dir.start_dir, false))
147                        return true;
148                else
149                        return false;
150        }
151
152        auto & root = recursion_roots_.front();
153        if (path.IsSubdirOf(root.m_startDir, false))
154                return true;
155
156        // In some cases (chmod from tree for example) it is neccessary to list the
157        // parent first
158        if (path == root.m_startDir && root.m_allowParent)
159                return true;
160
161        if (dir.link == 2) {
162                dir.start_dir = path;
163                return true;
164        }
165
166        return false;
167}
168
169// Defined in RemoteListView.cpp
170extern wxString StripVMSRevision(const wxString& name);
171
172void CRecursiveOperation::ProcessDirectoryListing(const CDirectoryListing* pDirectoryListing)
173{
174        if (!pDirectoryListing) {
175                StopRecursiveOperation();
176                return;
177        }
178
179        if (m_operationMode == recursive_none || recursion_roots_.empty())
180                return;
181
182        if (pDirectoryListing->failed()) {
183                // Ignore this.
184                // It will get handled by the failed command in ListingFailed
185                return;
186        }
187
188        auto & root = recursion_roots_.front();
189        wxASSERT(!root.m_dirsToVisit.empty());
190
191        if (!m_pState->IsRemoteConnected() || root.m_dirsToVisit.empty()) {
192                StopRecursiveOperation();
193                return;
194        }
195
196        recursion_root::new_dir dir = root.m_dirsToVisit.front();
197        root.m_dirsToVisit.pop_front();
198
199        if (!BelowRecursionRoot(pDirectoryListing->path, dir)) {
200                NextOperation();
201                return;
202        }
203
204        if (m_operationMode == recursive_delete && dir.doVisit && !dir.subdir.empty()) {
205                // After recursing into directory to delete its contents, delete directory itself
206                // Gets handled in NextOperation
207                recursion_root::new_dir dir2 = dir;
208                dir2.doVisit = false;
209                root.m_dirsToVisit.push_front(dir2);
210        }
211
212        if (dir.link && !dir.recurse) {
213                NextOperation();
214                return;
215        }
216
217        // Check if we have already visited the directory
218        if (!root.m_visitedDirs.insert(pDirectoryListing->path).second) {
219                NextOperation();
220                return;
221        }
222
223        ++m_processedDirectories;
224
225        const CServer* pServer = m_pState->GetServer();
226        wxASSERT(pServer);
227
228        if (!pDirectoryListing->GetCount()) {
229                if (m_operationMode == recursive_download) {
230                        wxFileName::Mkdir(dir.localDir.GetPath(), 0777, wxPATH_MKDIR_FULL);
231                        m_pState->RefreshLocalFile(dir.localDir.GetPath());
232                }
233                else if (m_operationMode == recursive_addtoqueue) {
234                        m_pQueue->QueueFile(true, true, _T(""), _T(""), dir.localDir, CServerPath(), *pServer, -1);
235                        m_pQueue->QueueFile_Finish(false);
236                }
237        }
238
239        CFilterManager filter;
240
241        // Is operation restricted to a single child?
242        bool const restrict = static_cast<bool>(dir.restrict);
243
244        std::deque<wxString> filesToDelete;
245
246        const wxString path = pDirectoryListing->path.GetPath();
247
248        bool added = false;
249
250        for (int i = pDirectoryListing->GetCount() - 1; i >= 0; --i) {
251                const CDirentry& entry = (*pDirectoryListing)[i];
252
253                if (restrict) {
254                        if (entry.name != *dir.restrict)
255                                continue;
256                }
257                else if (filter.FilenameFiltered(m_filters, entry.name, path, entry.is_dir(), entry.size, 0, entry.time))
258                        continue;
259
260                if (!entry.is_dir()) {
261                        ++m_processedFiles;
262                }
263
264                if (entry.is_dir() && (!entry.is_link() || m_operationMode != recursive_delete)) {
265                        if (dir.recurse) {
266                                recursion_root::new_dir dirToVisit;
267                                dirToVisit.parent = pDirectoryListing->path;
268                                dirToVisit.subdir = entry.name;
269                                dirToVisit.localDir = dir.localDir;
270                                dirToVisit.start_dir = dir.start_dir;
271
272                                if (m_operationMode == recursive_download || m_operationMode == recursive_addtoqueue)
273                                        dirToVisit.localDir.AddSegment(CQueueView::ReplaceInvalidCharacters(entry.name));
274                                if (entry.is_link()) {
275                                        dirToVisit.link = 1;
276                                        dirToVisit.recurse = false;
277                                }
278                                root.m_dirsToVisit.push_front(dirToVisit);
279                        }
280                }
281                else {
282                        switch (m_operationMode)
283                        {
284                        case recursive_download:
285                        case recursive_download_flatten:
286                                {
287                                        wxString localFile = CQueueView::ReplaceInvalidCharacters(entry.name);
288                                        if (pDirectoryListing->path.GetType() == VMS && COptions::Get()->GetOptionVal(OPTION_STRIP_VMS_REVISION))
289                                                localFile = StripVMSRevision(localFile);
290                                        m_pQueue->QueueFile(m_operationMode == recursive_addtoqueue, true,
291                                                entry.name, (entry.name == localFile) ? wxString() : localFile,
292                                                dir.localDir, pDirectoryListing->path, *pServer, entry.size);
293                                        added = true;
294                                }
295                                break;
296                        case recursive_addtoqueue:
297                        case recursive_addtoqueue_flatten:
298                                {
299                                        wxString localFile = CQueueView::ReplaceInvalidCharacters(entry.name);
300                                        if (pDirectoryListing->path.GetType() == VMS && COptions::Get()->GetOptionVal(OPTION_STRIP_VMS_REVISION))
301                                                localFile = StripVMSRevision(localFile);
302                                        m_pQueue->QueueFile(true, true,
303                                                entry.name, (entry.name == localFile) ? wxString() : localFile,
304                                                dir.localDir, pDirectoryListing->path, *pServer, entry.size);
305                                        added = true;
306                                }
307                                break;
308                        case recursive_delete:
309                                filesToDelete.push_back(entry.name);
310                                break;
311                        default:
312                                break;
313                        }
314                }
315
316                if (m_operationMode == recursive_chmod && m_pChmodDlg) {
317                        const int applyType = m_pChmodDlg->GetApplyType();
318                        if (!applyType ||
319                                (!entry.is_dir() && applyType == 1) ||
320                                (entry.is_dir() && applyType == 2))
321                        {
322                                char permissions[9];
323                                bool res = m_pChmodDlg->ConvertPermissions(*entry.permissions, permissions);
324                                wxString newPerms = m_pChmodDlg->GetPermissions(res ? permissions : 0, entry.is_dir());
325                                m_pState->m_pCommandQueue->ProcessCommand(new CChmodCommand(pDirectoryListing->path, entry.name, newPerms), CCommandQueue::recursiveOperation);
326                        }
327                }
328        }
329        if (added)
330                m_pQueue->QueueFile_Finish(m_operationMode != recursive_addtoqueue && m_operationMode != recursive_addtoqueue_flatten);
331
332        if (m_operationMode == recursive_delete && !filesToDelete.empty())
333                m_pState->m_pCommandQueue->ProcessCommand(new CDeleteCommand(pDirectoryListing->path, std::move(filesToDelete)), CCommandQueue::recursiveOperation);
334
335        m_pState->NotifyHandlers(STATECHANGE_RECURSION_STATUS);
336
337        NextOperation();
338}
339
340void CRecursiveOperation::SetChmodDialog(CChmodDialog* pChmodDialog)
341{
342        wxASSERT(pChmodDialog);
343
344        if (m_pChmodDlg)
345                m_pChmodDlg->Destroy();
346
347        m_pChmodDlg = pChmodDialog;
348}
349
350void CRecursiveOperation::StopRecursiveOperation()
351{
352        if (m_operationMode != recursive_none) {
353                m_operationMode = recursive_none;
354                m_pState->NotifyHandlers(STATECHANGE_REMOTE_IDLE);
355                m_pState->NotifyHandlers(STATECHANGE_RECURSION_STATUS);
356        }
357        recursion_roots_.clear();
358
359        if (m_pChmodDlg) {
360                m_pChmodDlg->Destroy();
361                m_pChmodDlg = 0;
362        }
363}
364
365void CRecursiveOperation::ListingFailed(int error)
366{
367        if (m_operationMode == recursive_none || recursion_roots_.empty())
368                return;
369
370        if( (error & FZ_REPLY_CANCELED) == FZ_REPLY_CANCELED) {
371                // User has cancelled operation
372                StopRecursiveOperation();
373                return;
374        }
375
376        auto & root = recursion_roots_.front();
377        wxCHECK_RET(!root.m_dirsToVisit.empty(), _T("Empty dirs to visit"));
378
379        recursion_root::new_dir dir = root.m_dirsToVisit.front();
380        root.m_dirsToVisit.pop_front();
381        if ((error & FZ_REPLY_CRITICALERROR) != FZ_REPLY_CRITICALERROR && !dir.second_try) {
382                // Retry, could have been a temporary socket creating failure
383                // (e.g. hitting a blocked port) or a disconnect (e.g. no-filetransfer-timeout)
384                dir.second_try = true;
385                root.m_dirsToVisit.push_front(dir);
386        }
387
388        NextOperation();
389}
390
391void CRecursiveOperation::SetQueue(CQueueView* pQueue)
392{
393        m_pQueue = pQueue;
394}
395
396bool CRecursiveOperation::ChangeOperationMode(enum OperationMode mode)
397{
398        if (mode != recursive_addtoqueue && m_operationMode != recursive_download && mode != recursive_addtoqueue_flatten && m_operationMode != recursive_download_flatten)
399                return false;
400
401        m_operationMode = mode;
402
403        return true;
404}
405
406void CRecursiveOperation::LinkIsNotDir()
407{
408        if (m_operationMode == recursive_none || recursion_roots_.empty())
409                return;
410
411        auto & root = recursion_roots_.front();
412        wxCHECK_RET(!root.m_dirsToVisit.empty(), _T("Empty dirs to visit"));
413
414        recursion_root::new_dir dir = root.m_dirsToVisit.front();
415        root.m_dirsToVisit.pop_front();
416
417        const CServer* pServer = m_pState->GetServer();
418        if (!pServer) {
419                NextOperation();
420                return;
421        }
422
423        if (m_operationMode == recursive_delete) {
424                if (!dir.subdir.empty()) {
425                        std::deque<wxString> files;
426                        files.push_back(dir.subdir);
427                        m_pState->m_pCommandQueue->ProcessCommand(new CDeleteCommand(dir.parent, std::move(files)), CCommandQueue::recursiveOperation);
428                }
429                NextOperation();
430                return;
431        }
432        else if (m_operationMode != recursive_list) {
433                CLocalPath localPath = dir.localDir;
434                wxString localFile = dir.subdir;
435                if (m_operationMode != recursive_addtoqueue_flatten && m_operationMode != recursive_download_flatten)
436                        localPath.MakeParent();
437                m_pQueue->QueueFile(m_operationMode == recursive_addtoqueue || m_operationMode == recursive_addtoqueue_flatten, true, dir.subdir, (dir.subdir == localFile) ? wxString() : localFile, localPath, dir.parent, *pServer, -1);
438                m_pQueue->QueueFile_Finish(m_operationMode != recursive_addtoqueue);
439        }
440
441        NextOperation();
442}
Note: See TracBrowser for help on using the repository browser.