source: filezilla/trunk/fuentes/src/interface/asyncrequestqueue.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.6 KB
Line 
1#include <filezilla.h>
2
3#include "asyncrequestqueue.h"
4#include "defaultfileexistsdlg.h"
5#include "fileexistsdlg.h"
6#include "loginmanager.h"
7#include "Mainfrm.h"
8#include "Options.h"
9#include "queue.h"
10#include "verifycertdialog.h"
11#include "verifyhostkeydialog.h"
12
13DECLARE_EVENT_TYPE(fzEVT_PROCESSASYNCREQUESTQUEUE, -1)
14DEFINE_EVENT_TYPE(fzEVT_PROCESSASYNCREQUESTQUEUE)
15
16BEGIN_EVENT_TABLE(CAsyncRequestQueue, wxEvtHandler)
17EVT_COMMAND(wxID_ANY, fzEVT_PROCESSASYNCREQUESTQUEUE, CAsyncRequestQueue::OnProcessQueue)
18EVT_TIMER(wxID_ANY, CAsyncRequestQueue::OnTimer)
19END_EVENT_TABLE()
20
21CAsyncRequestQueue::CAsyncRequestQueue(CMainFrame *pMainFrame)
22{
23        m_pMainFrame = pMainFrame;
24        m_pQueueView = 0;
25        m_pVerifyCertDlg = new CVerifyCertDialog;
26        m_inside_request = false;
27        m_timer.SetOwner(this);
28}
29
30CAsyncRequestQueue::~CAsyncRequestQueue()
31{
32        delete m_pVerifyCertDlg;
33}
34
35bool CAsyncRequestQueue::ProcessDefaults(CFileZillaEngine *pEngine, std::unique_ptr<CAsyncRequestNotification> & pNotification)
36{
37        // Process notifications, see if we have defaults not requirering user interaction.
38        switch (pNotification->GetRequestID())
39        {
40        case reqId_fileexists:
41                {
42                        CFileExistsNotification *pFileExistsNotification = static_cast<CFileExistsNotification *>(pNotification.get());
43
44                        // Get the action, go up the hierarchy till one is found
45                        enum CFileExistsNotification::OverwriteAction action = pFileExistsNotification->overwriteAction;
46                        if (action == CFileExistsNotification::unknown)
47                                action = CDefaultFileExistsDlg::GetDefault(pFileExistsNotification->download);
48                        if (action == CFileExistsNotification::unknown) {
49                                int option = COptions::Get()->GetOptionVal(pFileExistsNotification->download ? OPTION_FILEEXISTS_DOWNLOAD : OPTION_FILEEXISTS_UPLOAD);
50                                if (option < CFileExistsNotification::unknown || option >= CFileExistsNotification::ACTION_COUNT)
51                                        action = CFileExistsNotification::unknown;
52                                else
53                                        action = (enum CFileExistsNotification::OverwriteAction)option;
54                        }
55
56                        // Ask and rename options require user interaction
57                        if (action == CFileExistsNotification::unknown || action == CFileExistsNotification::ask || action == CFileExistsNotification::rename)
58                                break;
59
60                        if (action == CFileExistsNotification::resume && pFileExistsNotification->ascii) {
61                                // Check if resuming ascii files is allowed
62                                if (!COptions::Get()->GetOptionVal(OPTION_ASCIIRESUME))
63                                        // Overwrite instead
64                                        action = CFileExistsNotification::overwrite;
65                        }
66
67                        pFileExistsNotification->overwriteAction = action;
68
69                        pEngine->SetAsyncRequestReply(std::move(pNotification));
70
71                        return true;
72                }
73        case reqId_hostkey:
74        case reqId_hostkeyChanged:
75                {
76                        auto & hostKeyNotification = static_cast<CHostKeyNotification&>(*pNotification.get());
77
78                        if (!CVerifyHostkeyDialog::IsTrusted(hostKeyNotification))
79                                break;
80
81                        hostKeyNotification.m_trust = true;
82                        hostKeyNotification.m_alwaysTrust = false;
83
84                        pEngine->SetAsyncRequestReply(std::move(pNotification));
85
86                        return true;
87                }
88        case reqId_certificate:
89                {
90                        auto & certNotification = static_cast<CCertificateNotification&>(*pNotification.get());
91
92                        if (!m_pVerifyCertDlg->IsTrusted(certNotification))
93                                break;
94
95                        certNotification.m_trusted = true;
96                        pEngine->SetAsyncRequestReply(std::move(pNotification));
97
98                        return true;
99                }
100                break;
101        default:
102                break;
103        }
104
105        return false;
106}
107
108bool CAsyncRequestQueue::AddRequest(CFileZillaEngine *pEngine, std::unique_ptr<CAsyncRequestNotification> && pNotification)
109{
110        ClearPending(pEngine);
111
112        if (ProcessDefaults(pEngine, pNotification))
113                return false;
114
115        m_requestList.emplace_back(pEngine, std::move(pNotification));
116
117        if (m_requestList.size() == 1) {
118                QueueEvent(new wxCommandEvent(fzEVT_PROCESSASYNCREQUESTQUEUE));
119        }
120
121        return true;
122}
123
124bool CAsyncRequestQueue::ProcessNextRequest()
125{
126        if (m_requestList.empty())
127                return true;
128
129        t_queueEntry &entry = m_requestList.front();
130
131        if (!entry.pEngine || !entry.pEngine->IsPendingAsyncRequestReply(entry.pNotification)) {
132                m_requestList.pop_front();
133                return true;
134        }
135
136        if (entry.pNotification->GetRequestID() == reqId_fileexists) {
137                if (!ProcessFileExistsNotification(entry)) {
138                        return false;
139                }
140        }
141        else if (entry.pNotification->GetRequestID() == reqId_interactiveLogin) {
142                auto & notification = static_cast<CInteractiveLoginNotification&>(*entry.pNotification.get());
143
144                if (notification.IsRepeated()) {
145                        CLoginManager::Get().CachedPasswordFailed(notification.server, notification.GetChallenge());
146                }
147                bool canRemember = notification.GetType() == CInteractiveLoginNotification::keyfile;
148
149                if (CLoginManager::Get().GetPassword(notification.server, true, wxString(), notification.GetChallenge(), canRemember))
150                        notification.passwordSet = true;
151                else {
152                        // Retry with prompt
153
154                        if (!CheckWindowState())
155                                return false;
156
157                        if (CLoginManager::Get().GetPassword(notification.server, false, wxString(), notification.GetChallenge(), canRemember))
158                                notification.passwordSet = true;
159                }
160
161                entry.pEngine->SetAsyncRequestReply(std::move(entry.pNotification));
162        }
163        else if (entry.pNotification->GetRequestID() == reqId_hostkey || entry.pNotification->GetRequestID() == reqId_hostkeyChanged) {
164                if (!CheckWindowState())
165                        return false;
166
167                auto & notification = static_cast<CHostKeyNotification&>(*entry.pNotification.get());
168
169                if (CVerifyHostkeyDialog::IsTrusted(notification)) {
170                        notification.m_trust = true;
171                        notification.m_alwaysTrust = false;
172                }
173                else
174                        CVerifyHostkeyDialog::ShowVerificationDialog(m_pMainFrame, notification);
175
176                entry.pEngine->SetAsyncRequestReply(std::move(entry.pNotification));
177        }
178        else if (entry.pNotification->GetRequestID() == reqId_certificate) {
179                if (!CheckWindowState())
180                        return false;
181
182                auto & notification = static_cast<CCertificateNotification&>(*entry.pNotification.get());
183                m_pVerifyCertDlg->ShowVerificationDialog(notification);
184
185                entry.pEngine->SetAsyncRequestReply(std::move(entry.pNotification));
186        }
187        else {
188                entry.pEngine->SetAsyncRequestReply(std::move(entry.pNotification));
189        }
190
191        RecheckDefaults();
192        m_requestList.pop_front();
193
194        return true;
195}
196
197bool CAsyncRequestQueue::ProcessFileExistsNotification(t_queueEntry &entry)
198{
199        auto & notification = static_cast<CFileExistsNotification&>(*entry.pNotification.get());
200
201        // Get the action, go up the hierarchy till one is found
202        enum CFileExistsNotification::OverwriteAction action = notification.overwriteAction;
203        if (action == CFileExistsNotification::unknown)
204                action = CDefaultFileExistsDlg::GetDefault(notification.download);
205        if (action == CFileExistsNotification::unknown) {
206                int option = COptions::Get()->GetOptionVal(notification.download ? OPTION_FILEEXISTS_DOWNLOAD : OPTION_FILEEXISTS_UPLOAD);
207                if (option <= CFileExistsNotification::unknown || option >= CFileExistsNotification::ACTION_COUNT)
208                        action = CFileExistsNotification::ask;
209                else
210                        action = (enum CFileExistsNotification::OverwriteAction)option;
211        }
212
213        if (action == CFileExistsNotification::ask) {
214                if (!CheckWindowState())
215                        return false;
216
217                CFileExistsDlg dlg(&notification);
218                dlg.Create(m_pMainFrame);
219                int res = dlg.ShowModal();
220
221                if (res == wxID_OK) {
222                        action = dlg.GetAction();
223
224                        bool directionOnly, queueOnly;
225                        if (dlg.Always(directionOnly, queueOnly)) {
226                                if (!queueOnly) {
227                                        if (notification.download || !directionOnly)
228                                                CDefaultFileExistsDlg::SetDefault(true, action);
229
230                                        if (!notification.download || !directionOnly)
231                                                CDefaultFileExistsDlg::SetDefault(false, action);
232                                }
233                                else {
234                                        // For the notifications already in the request queue, we have to set the queue action directly
235                                        for (auto iter = ++m_requestList.begin(); iter != m_requestList.end(); ++iter) {
236                                                if (!iter->pNotification || iter->pNotification->GetRequestID() != reqId_fileexists)
237                                                        continue;
238                                                auto & p = static_cast<CFileExistsNotification&>(*iter->pNotification.get());
239
240                                                if (!directionOnly || notification.download == p.download)
241                                                        p.overwriteAction = CFileExistsNotification::OverwriteAction(action);
242                                        }
243
244                                        TransferDirection direction;
245                                        if (directionOnly) {
246                                                if (notification.download)
247                                                        direction = TransferDirection::download;
248                                                else
249                                                        direction = TransferDirection::upload;
250                                        }
251                                        else
252                                                direction = TransferDirection::both;
253
254                                        if (m_pQueueView)
255                                                m_pQueueView->SetDefaultFileExistsAction(action, direction);
256                                }
257                        }
258                }
259                else
260                        action = CFileExistsNotification::skip;
261        }
262
263        if (action == CFileExistsNotification::unknown || action == CFileExistsNotification::ask)
264                action = CFileExistsNotification::skip;
265
266        if (action == CFileExistsNotification::resume && notification.ascii) {
267                // Check if resuming ascii files is allowed
268                if (!COptions::Get()->GetOptionVal(OPTION_ASCIIRESUME))
269                        // Overwrite instead
270                        action = CFileExistsNotification::overwrite;
271        }
272
273        switch (action)
274        {
275                case CFileExistsNotification::rename:
276                {
277                        if (!CheckWindowState())
278                                return false;
279
280                        wxString msg;
281                        wxString defaultName;
282                        if (notification.download) {
283                                msg.Printf(_("The file %s already exists.\nPlease enter a new name:"), notification.localFile);
284                                wxFileName fn = notification.localFile;
285                                defaultName = fn.GetFullName();
286                        }
287                        else {
288                                wxString fullName = notification.remotePath.GetPath() + notification.remoteFile;
289                                msg.Printf(_("The file %s already exists.\nPlease enter a new name:"), fullName);
290                                defaultName = notification.remoteFile;
291                        }
292                        wxTextEntryDialog dlg(m_pMainFrame, msg, _("Rename file"), defaultName);
293
294                        // Repeat until user cancels or enters a new name
295                        for (;;) {
296                                int res = dlg.ShowModal();
297                                if (res == wxID_OK) {
298                                        if (dlg.GetValue().empty())
299                                                continue; // Disallow empty names
300                                        if (dlg.GetValue() == defaultName) {
301                                                wxMessageDialog dlg2(m_pMainFrame, _("You did not enter a new name for the file. Overwrite the file instead?"), _("Filename unchanged"),
302                                                        wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION | wxCANCEL);
303                                                int res2 = dlg2.ShowModal();
304
305                                                if (res2 == wxID_CANCEL)
306                                                        notification.overwriteAction = CFileExistsNotification::skip;
307                                                else if (res2 == wxID_NO)
308                                                        continue;
309                                                else
310                                                        notification.overwriteAction = CFileExistsNotification::skip;
311                                        }
312                                        else {
313                                                notification.overwriteAction = CFileExistsNotification::rename;
314                                                notification.newName = dlg.GetValue();
315
316                                                // If request got processed successfully, notify queue about filename change
317                                                if (entry.pEngine->SetAsyncRequestReply(std::move(entry.pNotification)) && m_pQueueView)
318                                                        m_pQueueView->RenameFileInTransfer(entry.pEngine, dlg.GetValue(), notification.download);
319                                                return true;
320                                        }
321                                }
322                                else
323                                        notification.overwriteAction = CFileExistsNotification::skip;
324                                break;
325                        }
326                }
327                break;
328                default:
329                        notification.overwriteAction = action;
330                        break;
331        }
332
333        entry.pEngine->SetAsyncRequestReply(std::move(entry.pNotification));
334        return true;
335}
336
337void CAsyncRequestQueue::ClearPending(const CFileZillaEngine *pEngine)
338{
339        if (m_requestList.empty())
340                return;
341
342        // Remove older requests coming from the same engine, but never the first
343        // entry in the list as that one displays a dialog at this moment.
344        for (auto iter = ++m_requestList.begin(); iter != m_requestList.end(); ++iter) {
345                if (iter->pEngine == pEngine) {
346                        m_requestList.erase(iter);
347
348                        // At most one pending request per engine possible,
349                        // so we can stop here
350                        break;
351                }
352        }
353}
354
355void CAsyncRequestQueue::RecheckDefaults()
356{
357        if (m_requestList.size() <= 1)
358                return;
359
360        std::list<t_queueEntry>::iterator cur, next;
361        cur = ++m_requestList.begin();
362        while (cur != m_requestList.end()) {
363                next = cur;
364                ++next;
365
366                if (ProcessDefaults(cur->pEngine, cur->pNotification))
367                        m_requestList.erase(cur);
368                cur = next;
369        }
370}
371
372void CAsyncRequestQueue::SetQueue(CQueueView *pQueue)
373{
374        m_pQueueView = pQueue;
375}
376
377void CAsyncRequestQueue::OnProcessQueue(wxCommandEvent &)
378{
379        if (m_inside_request)
380                return;
381
382        m_inside_request = true;
383        bool success = ProcessNextRequest();
384        m_inside_request = false;
385
386        if (success && !m_requestList.empty()) {
387                QueueEvent(new wxCommandEvent(fzEVT_PROCESSASYNCREQUESTQUEUE));
388        }
389}
390
391void CAsyncRequestQueue::TriggerProcessing()
392{
393        if (m_inside_request)
394                return;
395
396        QueueEvent(new wxCommandEvent(fzEVT_PROCESSASYNCREQUESTQUEUE));
397}
398
399bool CAsyncRequestQueue::CheckWindowState()
400{
401        m_timer.Stop();
402        if (!wxDialogEx::CanShowPopupDialog()) {
403                m_timer.Start(100, true);
404                return false;
405        }
406
407#ifndef __WXMAC__
408        if (m_pMainFrame->IsIconized())
409        {
410#ifndef __WXGTK__
411                m_pMainFrame->Show();
412                m_pMainFrame->Iconize(true);
413                m_pMainFrame->RequestUserAttention();
414#endif
415                return false;
416        }
417
418        wxWindow* pFocus = m_pMainFrame->FindFocus();
419        while (pFocus && pFocus != m_pMainFrame)
420                pFocus = pFocus->GetParent();
421        if (!pFocus)
422                m_pMainFrame->RequestUserAttention();
423#endif
424
425        return true;
426}
427
428void CAsyncRequestQueue::OnTimer(wxTimerEvent&)
429{
430        TriggerProcessing();
431}
432
Note: See TracBrowser for help on using the repository browser.