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

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

Update new version: 3.15.02

File size: 8.3 KB
Line 
1#include <filezilla.h>
2#include "commandqueue.h"
3#include "Mainfrm.h"
4#include "state.h"
5#include "recursive_operation.h"
6#include "loginmanager.h"
7#include "queue.h"
8#include "RemoteListView.h"
9
10#include <algorithm>
11
12DEFINE_EVENT_TYPE(fzEVT_GRANTEXCLUSIVEENGINEACCESS)
13
14int CCommandQueue::m_requestIdCounter = 0;
15
16CCommandQueue::CCommandQueue(CFileZillaEngine *pEngine, CMainFrame* pMainFrame, CState* pState)
17{
18        m_pEngine = pEngine;
19        m_pMainFrame = pMainFrame;
20        m_pState = pState;
21        m_exclusiveEngineRequest = false;
22        m_exclusiveEngineLock = false;
23        m_requestId = 0;
24}
25
26CCommandQueue::~CCommandQueue()
27{
28}
29
30bool CCommandQueue::Idle(command_origin origin) const
31{
32        if (m_exclusiveEngineLock) {
33                return false;
34        }
35
36        if (origin == any) {
37                return m_CommandList.empty();
38        }
39       
40        return std::find_if(m_CommandList.begin(), m_CommandList.end(), [origin](CommandInfo const& c) { return c.origin == origin; }) == m_CommandList.end();
41}
42
43void CCommandQueue::ProcessCommand(CCommand *pCommand, CCommandQueue::command_origin origin)
44{
45        wxASSERT(origin != any);
46        if (m_quit) {
47                delete pCommand;
48                return;
49        }
50
51        m_CommandList.emplace_back(origin, std::unique_ptr<CCommand>(pCommand));
52        if (m_CommandList.size() == 1) {
53                m_pState->NotifyHandlers(STATECHANGE_REMOTE_IDLE);
54                ProcessNextCommand();
55        }
56}
57
58void CCommandQueue::ProcessNextCommand()
59{
60        if (m_inside_commandqueue)
61                return;
62
63        if (m_exclusiveEngineLock)
64                return;
65
66        if (m_pEngine->IsBusy())
67                return;
68
69        ++m_inside_commandqueue;
70
71        if (m_CommandList.empty()) {
72                // Possible sequence of events:
73                // - Engine emits listing and operation finished
74                // - Connection gets terminated
75                // - Interface cannot obtain listing since not connected
76                // - Yet getting operation successful
77                // To keep things flowing, we need to advance the recursive operation.
78                m_pState->GetRecursiveOperationHandler()->NextOperation();
79        }
80
81        while (!m_CommandList.empty()) {
82                auto const& commandInfo = m_CommandList.front();
83
84                int res = m_pEngine->Execute(*commandInfo.command);
85                ProcessReply(res, commandInfo.command->GetId());
86                if (res == FZ_REPLY_WOULDBLOCK) {
87                        break;
88                }
89        }
90
91        --m_inside_commandqueue;
92
93        if (m_CommandList.empty()) {
94                if (m_exclusiveEngineRequest)
95                        GrantExclusiveEngineRequest();
96                else
97                        m_pState->NotifyHandlers(STATECHANGE_REMOTE_IDLE);
98
99                if (!m_pState->SuccessfulConnect())
100                        m_pState->SetServer(0);
101        }
102}
103
104bool CCommandQueue::Cancel()
105{
106        if (m_exclusiveEngineLock)
107                return false;
108
109        if (m_CommandList.empty())
110                return true;
111
112        m_CommandList.erase(++m_CommandList.begin(), m_CommandList.end());
113
114        if (!m_pEngine) {
115                m_CommandList.clear();
116                m_pState->NotifyHandlers(STATECHANGE_REMOTE_IDLE);
117                return true;
118        }
119
120        int res = m_pEngine->Cancel();
121        if (res == FZ_REPLY_WOULDBLOCK)
122                return false;
123        else {
124                m_CommandList.clear();
125                m_pState->NotifyHandlers(STATECHANGE_REMOTE_IDLE);
126                return true;
127        }
128}
129
130void CCommandQueue::Finish(std::unique_ptr<COperationNotification> && pNotification)
131{
132        if (m_exclusiveEngineLock) {
133                m_pMainFrame->GetQueue()->ProcessNotification(m_pEngine, std::move(pNotification));
134                return;
135        }
136
137        ProcessReply(pNotification->nReplyCode, pNotification->commandId);
138}
139
140void CCommandQueue::ProcessReply(int nReplyCode, Command commandId)
141{
142        if (nReplyCode == FZ_REPLY_WOULDBLOCK) {
143                return;
144        }
145        if (nReplyCode & FZ_REPLY_DISCONNECTED) {
146                if (commandId == Command::none && !m_CommandList.empty()) {
147                        // Pending event, has no relevance during command execution
148                        return;
149                }
150                if (nReplyCode & FZ_REPLY_PASSWORDFAILED)
151                        CLoginManager::Get().CachedPasswordFailed(*m_pState->GetServer());
152        }
153
154        if (m_CommandList.empty()) {
155                return;
156        }
157
158        if (commandId != Command::connect &&
159                commandId != Command::disconnect &&
160                (nReplyCode & FZ_REPLY_CANCELED) != FZ_REPLY_CANCELED)
161        {
162                bool reconnect = false;
163                if (nReplyCode == FZ_REPLY_NOTCONNECTED) {
164                        reconnect = true;
165                }
166                else if (nReplyCode & FZ_REPLY_DISCONNECTED) {
167                        auto & info = m_CommandList.front();
168                        if (!info.didReconnect) {
169                                info.didReconnect = true;
170                                reconnect = true;
171                        }
172                }
173
174                if (reconnect) {
175                        // Try automatic reconnect
176                        const CServer* pServer = m_pState->GetServer();
177                        if (pServer) {
178                                m_CommandList.emplace_front(normal, std::make_unique<CConnectCommand>(*pServer));
179                                ProcessNextCommand();
180                                return;
181                        }
182                }
183        }
184
185        ++m_inside_commandqueue;
186
187        auto const& commandInfo = m_CommandList.front();
188
189        if (commandInfo.command->GetId() == Command::list && nReplyCode != FZ_REPLY_OK) {
190                if (nReplyCode & FZ_REPLY_LINKNOTDIR) {
191                        // Symbolic link does not point to a directory. Either points to file
192                        // or is completely invalid
193                        CListCommand* pListCommand = static_cast<CListCommand*>(commandInfo.command.get());
194                        wxASSERT(pListCommand->GetFlags() & LIST_FLAG_LINK);
195
196                        m_pState->LinkIsNotDir(pListCommand->GetPath(), pListCommand->GetSubDir());
197                }
198                else {
199                        if (commandInfo.origin == recursiveOperation) {
200                                // Let the recursive operation handler know if a LIST command failed,
201                                // so that it may issue the next command in recursive operations.
202                                m_pState->GetRecursiveOperationHandler()->ListingFailed(nReplyCode);
203                        }
204                        else {
205                                m_pState->ListingFailed(nReplyCode);
206                        }
207                }
208                m_CommandList.pop_front();
209        }
210        else if (nReplyCode == FZ_REPLY_ALREADYCONNECTED && commandInfo.command->GetId() == Command::connect) {
211                m_CommandList.emplace_front(normal, std::make_unique<CDisconnectCommand>());
212        }
213        else if (commandInfo.command->GetId() == Command::connect && nReplyCode != FZ_REPLY_OK) {
214                // Remove pending events
215                auto it = ++m_CommandList.begin();
216                while (it != m_CommandList.end() && it->command->GetId() != Command::connect) {
217                        ++it;
218                }
219                m_CommandList.erase(m_CommandList.begin(), it);
220
221                // If this was an automatic reconnect during a recursive
222                // operation, stop the recursive operation
223                m_pState->GetRecursiveOperationHandler()->StopRecursiveOperation();
224        }
225        else if (commandInfo.command->GetId() == Command::connect && nReplyCode == FZ_REPLY_OK) {
226                m_pState->SetSuccessfulConnect();
227                m_CommandList.pop_front();
228        }
229        else
230                m_CommandList.pop_front();
231
232        --m_inside_commandqueue;
233
234        ProcessNextCommand();
235}
236
237void CCommandQueue::RequestExclusiveEngine(bool requestExclusive)
238{
239        wxASSERT(!m_exclusiveEngineLock || !requestExclusive);
240
241        if (!m_exclusiveEngineRequest && requestExclusive)
242        {
243                m_requestId = ++m_requestIdCounter;
244                if (m_requestId < 0)
245                {
246                        m_requestIdCounter = 0;
247                        m_requestId = 0;
248                }
249                if (m_CommandList.empty())
250                {
251                        m_pState->NotifyHandlers(STATECHANGE_REMOTE_IDLE);
252                        GrantExclusiveEngineRequest();
253                        return;
254                }
255        }
256        if (!requestExclusive)
257                m_exclusiveEngineLock = false;
258        m_exclusiveEngineRequest = requestExclusive;
259        m_pState->NotifyHandlers(STATECHANGE_REMOTE_IDLE);
260}
261
262void CCommandQueue::GrantExclusiveEngineRequest()
263{
264        wxASSERT(!m_exclusiveEngineLock);
265        m_exclusiveEngineLock = true;
266        m_exclusiveEngineRequest = false;
267
268        wxCommandEvent *evt = new wxCommandEvent(fzEVT_GRANTEXCLUSIVEENGINEACCESS);
269        evt->SetId(m_requestId);
270        m_pMainFrame->GetQueue()->GetEventHandler()->QueueEvent(evt);
271}
272
273CFileZillaEngine* CCommandQueue::GetEngineExclusive(int requestId)
274{
275        if (!m_exclusiveEngineLock)
276                return 0;
277
278        if (requestId != m_requestId)
279                return 0;
280
281        return m_pEngine;
282}
283
284
285void CCommandQueue::ReleaseEngine()
286{
287        m_exclusiveEngineLock = false;
288
289        ProcessNextCommand();
290}
291
292bool CCommandQueue::Quit()
293{
294        m_quit = true;
295        return Cancel();
296}
297
298void CCommandQueue::ProcessDirectoryListing(CDirectoryListingNotification const& listingNotification)
299{
300        auto const firstListing = std::find_if(m_CommandList.begin(), m_CommandList.end(), [](CommandInfo const& v) { return v.command->GetId() == Command::list; });
301        bool const listingIsRecursive = firstListing != m_CommandList.end() && firstListing->origin == recursiveOperation;
302
303        std::shared_ptr<CDirectoryListing> pListing;
304        if (!listingNotification.GetPath().empty()) {
305                pListing = std::make_shared<CDirectoryListing>();
306                if (listingNotification.Failed() ||
307                        m_pState->m_pEngine->CacheLookup(listingNotification.GetPath(), *pListing) != FZ_REPLY_OK)
308                {
309                        pListing = std::make_shared<CDirectoryListing>();
310                        pListing->path = listingNotification.GetPath();
311                        pListing->m_flags |= CDirectoryListing::listing_failed;
312                        pListing->m_firstListTime = fz::monotonic_clock::now();
313                }
314        }
315
316        if (listingIsRecursive) {
317                if (!listingNotification.Modified() && m_pState->GetRecursiveOperationHandler()->IsActive()) {
318                        m_pState->NotifyHandlers(STATECHANGE_REMOTE_DIR_OTHER, wxString(), &pListing);
319                }
320        }
321        else {
322                m_pState->SetRemoteDir(pListing, listingNotification.Modified());
323        }
324}
Note: See TracBrowser for help on using the repository browser.