#include "FileZilla.h" #include "asyncrequestqueue.h" #include "fileexistsdlg.h" #include "Mainfrm.h" #include "defaultfileexistsdlg.h" #include "Options.h" #include "queue.h" #include "verifycertdialog.h" #include "verifyhostkeydialog.h" #include "loginmanager.h" DECLARE_EVENT_TYPE(fzEVT_PROCESSASYNCREQUESTQUEUE, -1) DEFINE_EVENT_TYPE(fzEVT_PROCESSASYNCREQUESTQUEUE) BEGIN_EVENT_TABLE(CAsyncRequestQueue, wxEvtHandler) EVT_COMMAND(wxID_ANY, fzEVT_PROCESSASYNCREQUESTQUEUE, CAsyncRequestQueue::OnProcessQueue) END_EVENT_TABLE() CAsyncRequestQueue::CAsyncRequestQueue(CMainFrame *pMainFrame) { m_pMainFrame = pMainFrame; m_pQueueView = 0; m_pVerifyCertDlg = new CVerifyCertDialog; m_inside_request = false; } CAsyncRequestQueue::~CAsyncRequestQueue() { delete m_pVerifyCertDlg; } bool CAsyncRequestQueue::ProcessDefaults(CFileZillaEngine *pEngine, CAsyncRequestNotification *pNotification) { // Process notifications, see if we have defaults not requirering user interaction. switch (pNotification->GetRequestID()) { case reqId_fileexists: { CFileExistsNotification *pFileExistsNotification = reinterpret_cast(pNotification); // Get the action, go up the hierarchy till one is found enum CFileExistsNotification::OverwriteAction action = pFileExistsNotification->overwriteAction; if (action == CFileExistsNotification::unknown) action = CDefaultFileExistsDlg::GetDefault(pFileExistsNotification->download); if (action ==CFileExistsNotification::unknown) { int option = COptions::Get()->GetOptionVal(pFileExistsNotification->download ? OPTION_FILEEXISTS_DOWNLOAD : OPTION_FILEEXISTS_UPLOAD); if (option < CFileExistsNotification::unknown || option >= CFileExistsNotification::ACTION_COUNT) action = CFileExistsNotification::unknown; else action = (enum CFileExistsNotification::OverwriteAction)option; } // Ask and rename options require user interaction if (action == CFileExistsNotification::unknown || action == CFileExistsNotification::ask || action == CFileExistsNotification::rename) break; if (action == CFileExistsNotification::resume && pFileExistsNotification->ascii) { // Check if resuming ascii files is allowed if (!COptions::Get()->GetOptionVal(OPTION_ASCIIRESUME)) // Overwrite instead action = CFileExistsNotification::overwrite; } pFileExistsNotification->overwriteAction = action; pEngine->SetAsyncRequestReply(pNotification); delete pNotification; return true; } case reqId_hostkey: case reqId_hostkeyChanged: { CHostKeyNotification *pHostKeyNotification = reinterpret_cast(pNotification); if (!CVerifyHostkeyDialog::IsTrusted(pHostKeyNotification)) break; pHostKeyNotification->m_trust = true; pHostKeyNotification->m_alwaysTrust = false; pEngine->SetAsyncRequestReply(pNotification); delete pNotification; return true; } case reqId_certificate: { CCertificateNotification* pCertNotification = reinterpret_cast(pNotification); if (!m_pVerifyCertDlg->IsTrusted(pCertNotification)) break; pCertNotification->m_trusted = true; pEngine->SetAsyncRequestReply(pNotification); delete pNotification; return true; } break; default: break; } return false; } bool CAsyncRequestQueue::AddRequest(CFileZillaEngine *pEngine, CAsyncRequestNotification *pNotification) { ClearPending(pEngine); if (ProcessDefaults(pEngine, pNotification)) return false; t_queueEntry entry; entry.pEngine = pEngine; entry.pNotification = pNotification; m_requestList.push_back(entry); if (m_requestList.size() == 1) { wxCommandEvent evt(fzEVT_PROCESSASYNCREQUESTQUEUE); wxPostEvent(this, evt); } return true; } bool CAsyncRequestQueue::ProcessNextRequest() { if (m_requestList.empty()) return true; t_queueEntry &entry = m_requestList.front(); if (!entry.pEngine->IsPendingAsyncRequestReply(entry.pNotification)) { delete entry.pNotification; m_requestList.pop_front(); return true; } if (entry.pNotification->GetRequestID() == reqId_fileexists) { CFileExistsNotification *pNotification = reinterpret_cast(entry.pNotification); // Get the action, go up the hierarchy till one is found enum CFileExistsNotification::OverwriteAction action = pNotification->overwriteAction; if (action == CFileExistsNotification::unknown) action = CDefaultFileExistsDlg::GetDefault(pNotification->download); if (action == CFileExistsNotification::unknown) { int option = COptions::Get()->GetOptionVal(pNotification->download ? OPTION_FILEEXISTS_DOWNLOAD : OPTION_FILEEXISTS_UPLOAD); if (option <= CFileExistsNotification::unknown || option >= CFileExistsNotification::ACTION_COUNT) action = CFileExistsNotification::ask; else action = (enum CFileExistsNotification::OverwriteAction)option; } if (action == CFileExistsNotification::ask) { if (!CheckWindowState()) return false; CFileExistsDlg dlg(pNotification); dlg.Create(m_pMainFrame); int res = dlg.ShowModal(); if (res == wxID_OK) { action = dlg.GetAction(); bool directionOnly, queueOnly; if (dlg.Always(directionOnly, queueOnly)) { if (!queueOnly) { if (pNotification->download || !directionOnly) CDefaultFileExistsDlg::SetDefault(true, action); if (!pNotification->download || !directionOnly) CDefaultFileExistsDlg::SetDefault(false, action); } else { // For the notifications already in the request queue, we have to set the queue action directly for (std::list::iterator iter = ++m_requestList.begin(); iter != m_requestList.end(); iter++) { if (pNotification->GetRequestID() != reqId_fileexists) continue; CFileExistsNotification* p = reinterpret_cast(iter->pNotification); if (!directionOnly || pNotification->download == p->download) p->overwriteAction = CFileExistsNotification::OverwriteAction(action); } enum TransferDirection direction; if (directionOnly) { if (pNotification->download) direction = download; else direction = upload; } else direction = both; if (m_pQueueView) m_pQueueView->SetDefaultFileExistsAction(action, direction); } } } else action = CFileExistsNotification::skip; } if (action == CFileExistsNotification::unknown || action == CFileExistsNotification::ask) action = CFileExistsNotification::skip; if (action == CFileExistsNotification::resume && pNotification->ascii) { // Check if resuming ascii files is allowed if (!COptions::Get()->GetOptionVal(OPTION_ASCIIRESUME)) // Overwrite instead action = CFileExistsNotification::overwrite; } switch (action) { case CFileExistsNotification::rename: { if (!CheckWindowState()) return false; wxString msg; wxString defaultName; if (pNotification->download) { msg.Printf(_("The file %s already exists.\nPlease enter a new name:"), pNotification->localFile.c_str()); wxFileName fn = pNotification->localFile; defaultName = fn.GetFullName(); } else { wxString fullName = pNotification->remotePath.GetPath() + pNotification->remoteFile; msg.Printf(_("The file %s already exists.\nPlease enter a new name:"), fullName.c_str()); defaultName = pNotification->remoteFile; } wxTextEntryDialog dlg(m_pMainFrame, msg, _("Rename file"), defaultName); // Repeat until user cancels or enters a new name while (1) { int res = dlg.ShowModal(); if (res == wxID_OK) { if (dlg.GetValue() == _T("")) continue; // Disallow empty names if (dlg.GetValue() == defaultName) { wxMessageDialog dlg2(m_pMainFrame, _("You did not enter a new name for the file. Overwrite the file instead?"), _("Filename unchanged"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION | wxCANCEL); int res = dlg2.ShowModal(); if (res == wxID_CANCEL) pNotification->overwriteAction = CFileExistsNotification::skip; else if (res == wxID_NO) continue; else pNotification->overwriteAction = CFileExistsNotification::skip; } else { pNotification->overwriteAction = CFileExistsNotification::rename; pNotification->newName = dlg.GetValue(); // If request got processed successfully, notify queue about filename change if (entry.pEngine->SetAsyncRequestReply(entry.pNotification) && m_pQueueView) m_pQueueView->RenameFileInTransfer(entry.pEngine, dlg.GetValue(), pNotification->download); delete pNotification; // Jump near end of function goto ProcessNextRequest_done; } } else pNotification->overwriteAction = CFileExistsNotification::skip; break; } } break; default: pNotification->overwriteAction = action; break; } entry.pEngine->SetAsyncRequestReply(entry.pNotification); delete pNotification; } else if (entry.pNotification->GetRequestID() == reqId_interactiveLogin) { CInteractiveLoginNotification* pNotification = reinterpret_cast(entry.pNotification); if (CLoginManager::Get().GetPassword(pNotification->server, true, _T(""), pNotification->GetChallenge())) pNotification->passwordSet = true; else { // Retry with prompt if (!CheckWindowState()) return false; if (CLoginManager::Get().GetPassword(pNotification->server, false, _T(""), pNotification->GetChallenge())) pNotification->passwordSet = true; } entry.pEngine->SetAsyncRequestReply(pNotification); delete pNotification; } else if (entry.pNotification->GetRequestID() == reqId_hostkey || entry.pNotification->GetRequestID() == reqId_hostkeyChanged) { if (!CheckWindowState()) return false; CHostKeyNotification *pNotification = reinterpret_cast(entry.pNotification); if (CVerifyHostkeyDialog::IsTrusted(pNotification)) { pNotification->m_trust = true; pNotification->m_alwaysTrust = false; } else CVerifyHostkeyDialog::ShowVerificationDialog(m_pMainFrame, pNotification); entry.pEngine->SetAsyncRequestReply(pNotification); delete pNotification; } else if (entry.pNotification->GetRequestID() == reqId_certificate) { if (!CheckWindowState()) return false; CCertificateNotification* pNotification = reinterpret_cast(entry.pNotification); m_pVerifyCertDlg->ShowVerificationDialog(pNotification); entry.pEngine->SetAsyncRequestReply(entry.pNotification); delete entry.pNotification; } else { entry.pEngine->SetAsyncRequestReply(entry.pNotification); delete entry.pNotification; } ProcessNextRequest_done: RecheckDefaults(); m_requestList.pop_front(); return true; } void CAsyncRequestQueue::ClearPending(const CFileZillaEngine *pEngine) { if (m_requestList.empty()) return; // Remove older requests coming from the same engine, but never the first // entry in the list as that one displays a dialog at this moment. for (std::list::iterator iter = ++m_requestList.begin(); iter != m_requestList.end(); iter++) { if (iter->pEngine == pEngine) { m_requestList.erase(iter); // At most one pending request per engine possible, // so we can stop here break; } } } void CAsyncRequestQueue::RecheckDefaults() { if (m_requestList.size() <= 1) return; std::list::iterator cur, next; cur = ++m_requestList.begin(); while (cur != m_requestList.end()) { next = cur; next++; if (ProcessDefaults(cur->pEngine, cur->pNotification)) m_requestList.erase(cur); cur = next; } } void CAsyncRequestQueue::SetQueue(CQueueView *pQueue) { m_pQueueView = pQueue; } void CAsyncRequestQueue::OnProcessQueue(wxCommandEvent &event) { if (m_inside_request) return; m_inside_request = true; bool success = ProcessNextRequest(); m_inside_request = false; if (success && !m_requestList.empty()) { wxCommandEvent evt(fzEVT_PROCESSASYNCREQUESTQUEUE); wxPostEvent(this, evt); } } void CAsyncRequestQueue::TriggerProcessing() { if (m_inside_request) return; wxCommandEvent evt(fzEVT_PROCESSASYNCREQUESTQUEUE); wxPostEvent(this, evt); } bool CAsyncRequestQueue::CheckWindowState() { #ifndef __WXMAC__ if (m_pMainFrame->IsIconized()) { #ifndef __WXGTK__ m_pMainFrame->Show(); m_pMainFrame->Iconize(true); m_pMainFrame->RequestUserAttention(); #endif return false; } wxWindow* pFocus = m_pMainFrame->FindFocus(); while (pFocus && pFocus != m_pMainFrame) pFocus = pFocus->GetParent(); if (!pFocus) m_pMainFrame->RequestUserAttention(); #endif return true; }