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

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

First release to xenial

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