1 #include "filezilla.h"
2 #include "state.h"
3 #include "commandqueue.h"
4 #include "Options.h"
5 #include "Mainfrm.h"
6 #include "queue.h"
7 #include "filezillaapp.h"
8 #include "local_recursive_operation.h"
9 #include "remote_recursive_operation.h"
10 #include "listingcomparison.h"
11 #include "xrc_helper.h"
12
13 #include "../include/FileZillaEngine.h"
14
15 #include <libfilezilla/local_filesys.hpp>
16 #include <libfilezilla/glue/wx.hpp>
17 #include <libfilezilla/glue/wxinvoker.hpp>
18
19 #include <algorithm>
20
FilenameFiltered(std::wstring const & name,std::wstring const & path,bool dir,int64_t size,bool local,int attributes,fz::datetime const & date) const21 bool CStateFilterManager::FilenameFiltered(std::wstring const& name, std::wstring const& path, bool dir, int64_t size, bool local, int attributes, fz::datetime const& date) const
22 {
23 if (local) {
24 if (m_localFilter && FilenameFilteredByFilter(m_localFilter, name, path, dir, size, attributes, date)) {
25 return true;
26 }
27 }
28 else {
29 if (m_remoteFilter && FilenameFilteredByFilter(m_remoteFilter, name, path, dir, size, attributes, date)) {
30 return true;
31 }
32 }
33
34 return CFilterManager::FilenameFiltered(name, path, dir, size, local, attributes, date);
35 }
36
37 CContextManager CContextManager::m_the_context_manager;
38
CContextManager()39 CContextManager::CContextManager()
40 {
41 m_current_context = -1;
42 }
43
Get()44 CContextManager* CContextManager::Get()
45 {
46 return &m_the_context_manager;
47 }
48
CreateState(CMainFrame & mainFrame)49 CState* CContextManager::CreateState(CMainFrame &mainFrame)
50 {
51 CState* pState = new CState(mainFrame);
52
53 m_contexts.push_back(pState);
54
55 NotifyHandlers(pState, STATECHANGE_NEWCONTEXT, _T(""), 0);
56
57 if (!pState->CreateEngine()) {
58 DestroyState(pState);
59 return nullptr;
60 }
61
62 return pState;
63 }
64
DestroyState(CState * pState)65 void CContextManager::DestroyState(CState* pState)
66 {
67 for (unsigned int i = 0; i < m_contexts.size(); ++i) {
68 if (m_contexts[i] != pState) {
69 continue;
70 }
71
72 m_contexts.erase(m_contexts.begin() + i);
73 if ((int)i < m_current_context) {
74 --m_current_context;
75 }
76 else if ((int)i == m_current_context) {
77 if (i >= m_contexts.size()) {
78 --m_current_context;
79 }
80 NotifyHandlers(GetCurrentContext(), STATECHANGE_CHANGEDCONTEXT, _T(""), 0);
81 }
82
83 break;
84 }
85
86 NotifyHandlers(pState, STATECHANGE_REMOVECONTEXT, _T(""), 0);
87 delete pState;
88 }
89
SetCurrentContext(CState * pState)90 void CContextManager::SetCurrentContext(CState* pState)
91 {
92 if (GetCurrentContext() == pState) {
93 return;
94 }
95
96 for (unsigned int i = 0; i < m_contexts.size(); ++i) {
97 if (m_contexts[i] != pState) {
98 continue;
99 }
100
101 m_current_context = i;
102 NotifyHandlers(GetCurrentContext(), STATECHANGE_CHANGEDCONTEXT, _T(""), 0);
103 }
104 }
105
DestroyAllStates()106 void CContextManager::DestroyAllStates()
107 {
108 m_current_context = -1;
109 NotifyHandlers(GetCurrentContext(), STATECHANGE_CHANGEDCONTEXT, _T(""), 0);
110
111 while (!m_contexts.empty()) {
112 CState* pState = m_contexts.back();
113 m_contexts.pop_back();
114
115 NotifyHandlers(pState, STATECHANGE_REMOVECONTEXT, _T(""), 0);
116 delete pState;
117 }
118 }
119
RegisterHandler(CGlobalStateEventHandler * pHandler,t_statechange_notifications notification,bool current_only)120 void CContextManager::RegisterHandler(CGlobalStateEventHandler* pHandler, t_statechange_notifications notification, bool current_only)
121 {
122 wxASSERT(pHandler);
123 wxASSERT(notification != STATECHANGE_MAX && notification != STATECHANGE_NONE);
124
125 auto &handlers = m_handlers[notification];
126 for (auto const& it : handlers) {
127 if (it.pHandler == pHandler) {
128 return;
129 }
130 }
131
132 t_handler handler;
133 handler.pHandler = pHandler;
134 handler.current_only = current_only;
135 handlers.push_back(handler);
136 }
137
UnregisterHandler(CGlobalStateEventHandler * pHandler,t_statechange_notifications notification)138 void CContextManager::UnregisterHandler(CGlobalStateEventHandler* pHandler, t_statechange_notifications notification)
139 {
140 wxASSERT(pHandler);
141 wxASSERT(notification != STATECHANGE_MAX);
142
143 if (notification == STATECHANGE_NONE) {
144 for (int i = 0; i < STATECHANGE_MAX; ++i) {
145 auto &handlers = m_handlers[i];
146 for (auto iter = handlers.begin(); iter != handlers.end(); ++iter) {
147 if (iter->pHandler == pHandler) {
148 handlers.erase(iter);
149 break;
150 }
151 }
152 }
153 }
154 else {
155 auto &handlers = m_handlers[notification];
156 for (auto iter = handlers.begin(); iter != handlers.end(); ++iter) {
157 if (iter->pHandler == pHandler) {
158 handlers.erase(iter);
159 return;
160 }
161 }
162 }
163 }
164
HandlerCount(t_statechange_notifications notification) const165 size_t CContextManager::HandlerCount(t_statechange_notifications notification) const
166 {
167 wxASSERT(notification != STATECHANGE_NONE && notification != STATECHANGE_MAX);
168 return m_handlers[notification].size();
169 }
170
NotifyHandlers(CState * pState,t_statechange_notifications notification,std::wstring const & data,void const * data2)171 void CContextManager::NotifyHandlers(CState* pState, t_statechange_notifications notification, std::wstring const& data, void const* data2)
172 {
173 wxASSERT(notification != STATECHANGE_NONE && notification != STATECHANGE_MAX);
174
175 auto const& handlers = m_handlers[notification];
176 for (auto const& handler : handlers) {
177 if (handler.current_only && pState != GetCurrentContext()) {
178 continue;
179 }
180
181 handler.pHandler->OnStateChange(pState, notification, data, data2);
182 }
183 }
184
GetCurrentContext()185 CState* CContextManager::GetCurrentContext()
186 {
187 if (m_current_context == -1) {
188 return 0;
189 }
190
191 return m_contexts[m_current_context];
192 }
193
NotifyAllHandlers(t_statechange_notifications notification,std::wstring const & data,void const * data2)194 void CContextManager::NotifyAllHandlers(t_statechange_notifications notification, std::wstring const& data, void const* data2)
195 {
196 for (auto const& context : m_contexts) {
197 context->NotifyHandlers(notification, data, data2);
198 }
199 }
200
NotifyGlobalHandlers(t_statechange_notifications notification,std::wstring const & data,void const * data2)201 void CContextManager::NotifyGlobalHandlers(t_statechange_notifications notification, std::wstring const& data, void const* data2)
202 {
203 auto const& handlers = m_handlers[notification];
204 for (auto const& handler : handlers) {
205 handler.pHandler->OnStateChange(0, notification, data, data2);
206 }
207 }
208
ProcessDirectoryListing(CServer const & server,std::shared_ptr<CDirectoryListing> const & listing,CState const * exempt)209 void CContextManager::ProcessDirectoryListing(CServer const& server, std::shared_ptr<CDirectoryListing> const& listing, CState const* exempt)
210 {
211 for (auto state : m_contexts) {
212 if (state == exempt) {
213 continue;
214 }
215 if (state->GetSite() && state->GetSite().server == server) {
216 state->SetRemoteDir(listing, false);
217 }
218 }
219 }
220
CState(CMainFrame & mainFrame)221 CState::CState(CMainFrame &mainFrame)
222 : pool_(mainFrame.GetEngineContext().GetThreadPool())
223 , m_mainFrame(mainFrame)
224 {
225 m_title = _("Not connected");
226
227 m_pComparisonManager = new CComparisonManager(*this);
228
229 m_pLocalRecursiveOperation = new CLocalRecursiveOperation(*this);
230 m_pRemoteRecursiveOperation = new CRemoteRecursiveOperation(*this);
231
232 m_localDir.SetPath(std::wstring(1, CLocalPath::path_separator));
233 }
234
~CState()235 CState::~CState()
236 {
237 delete m_pComparisonManager;
238 delete m_pCommandQueue;
239 engine_.reset();
240 delete m_pLocalRecursiveOperation;
241 delete m_pRemoteRecursiveOperation;
242
243 // Unregister all handlers
244 for (int i = 0; i < STATECHANGE_MAX; ++i) {
245 wxASSERT(m_handlers[i].handlers.empty());
246 }
247 }
248
GetLocalDir() const249 CLocalPath CState::GetLocalDir() const
250 {
251 return m_localDir;
252 }
253
254
SetLocalDir(std::wstring const & dir,std::wstring * error,bool rememberPreviousSubdir)255 bool CState::SetLocalDir(std::wstring const& dir, std::wstring *error, bool rememberPreviousSubdir)
256 {
257 CLocalPath p(m_localDir);
258 #ifdef __WXMSW__
259 if (dir == _T("..") && !p.HasParent() && p.HasLogicalParent()) {
260 // Parent of C:\ is drive list
261 if (!p.MakeParent()) {
262 return false;
263 }
264 }
265 else
266 #endif
267 if (!p.ChangePath(dir)) {
268 return false;
269 }
270
271 return SetLocalDir(p, error, rememberPreviousSubdir);
272 }
273
SetLocalDir(CLocalPath const & dir,std::wstring * error,bool rememberPreviousSubdir)274 bool CState::SetLocalDir(CLocalPath const& dir, std::wstring *error, bool rememberPreviousSubdir)
275 {
276 if (m_changeDirFlags.syncbrowse) {
277 wxMessageBoxEx(_("Cannot change directory, there already is a synchronized browsing operation in progress."), _("Synchronized browsing"));
278 return false;
279 }
280
281 if (!dir.Exists(error)) {
282 return false;
283 }
284
285 if (!m_sync_browse.local_root.empty()) {
286 wxASSERT(m_site);
287
288 if (dir != m_sync_browse.local_root && !dir.IsSubdirOf(m_sync_browse.local_root)) {
289 wxString msg = wxString::Format(_("The local directory '%s' is not below the synchronization root (%s).\nDisable synchronized browsing and continue changing the local directory?"),
290 dir.GetPath(),
291 m_sync_browse.local_root.GetPath());
292 if (wxMessageBoxEx(msg, _("Synchronized browsing"), wxICON_QUESTION | wxYES_NO) != wxYES) {
293 return false;
294 }
295 SetSyncBrowse(false);
296 }
297 else if (!IsRemoteIdle(true)) {
298 wxString msg(_("A remote operation is in progress and synchronized browsing is enabled.\nDisable synchronized browsing and continue changing the local directory?"));
299 if (wxMessageBoxEx(msg, _("Synchronized browsing"), wxICON_QUESTION | wxYES_NO) != wxYES) {
300 return false;
301 }
302 SetSyncBrowse(false);
303 }
304 else {
305 CServerPath remote_path = GetSynchronizedDirectory(dir);
306 if (remote_path.empty()) {
307 SetSyncBrowse(false);
308 wxString msg = wxString::Format(_("Could not obtain corresponding remote directory for the local directory '%s'.\nSynchronized browsing has been disabled."),
309 dir.GetPath());
310 wxMessageBoxEx(msg, _("Synchronized browsing"));
311 return false;
312 }
313
314 m_changeDirFlags.syncbrowse = true;
315 m_changeDirFlags.compare = m_pComparisonManager->IsComparing();
316 m_sync_browse.target_path = remote_path;
317 CListCommand *pCommand = new CListCommand(remote_path);
318 m_pCommandQueue->ProcessCommand(pCommand);
319
320 return true;
321 }
322 }
323
324 if (dir == m_localDir.GetParent() && rememberPreviousSubdir) {
325 #ifdef __WXMSW__
326 if (dir.GetPath() == _T("\\")) {
327 m_previouslyVisitedLocalSubdir = m_localDir.GetPath();
328 m_previouslyVisitedLocalSubdir.pop_back();
329 }
330 else
331 #endif
332 {
333 m_previouslyVisitedLocalSubdir = m_localDir.GetLastSegment();
334 }
335 }
336 else {
337 m_previouslyVisitedLocalSubdir.clear();
338 }
339
340
341 m_localDir = dir;
342
343 NotifyHandlers(STATECHANGE_LOCAL_DIR);
344
345 return true;
346 }
347
SetRemoteDir(std::shared_ptr<CDirectoryListing> const & pDirectoryListing,bool primary)348 bool CState::SetRemoteDir(std::shared_ptr<CDirectoryListing> const& pDirectoryListing, bool primary)
349 {
350 if (!pDirectoryListing) {
351 m_changeDirFlags.compare = false;
352 SetSyncBrowse(false);
353 if (!primary) {
354 return false;
355 }
356
357 if (m_pDirectoryListing) {
358 m_pDirectoryListing = 0;
359 NotifyHandlers(STATECHANGE_REMOTE_DIR, std::wstring(), &primary);
360 }
361 m_previouslyVisitedRemoteSubdir.clear();
362 return true;
363 }
364
365 wxASSERT(pDirectoryListing->m_firstListTime);
366
367 if (pDirectoryListing && m_pDirectoryListing &&
368 pDirectoryListing->path == m_pDirectoryListing->path.GetParent())
369 {
370 m_previouslyVisitedRemoteSubdir = m_pDirectoryListing->path.GetLastSegment();
371 }
372 else {
373 m_previouslyVisitedRemoteSubdir.clear();
374 }
375
376 if (!primary) {
377 if (!m_pDirectoryListing || m_pDirectoryListing->path != pDirectoryListing->path) {
378 // We aren't interested in these listings
379 return true;
380 }
381 }
382 else {
383 m_last_path = pDirectoryListing->path;
384 }
385
386 if (m_pDirectoryListing && m_pDirectoryListing->path == pDirectoryListing->path &&
387 pDirectoryListing->failed())
388 {
389 // We still got an old listing, no need to display the new one
390 return true;
391 }
392
393 m_pDirectoryListing = pDirectoryListing;
394
395 NotifyHandlers(STATECHANGE_REMOTE_DIR, std::wstring(), &primary);
396
397 bool compare = m_changeDirFlags.compare;
398 if (primary) {
399 m_changeDirFlags.compare = false;
400 }
401
402 if (m_changeDirFlags.syncbrowse && primary) {
403 m_changeDirFlags.syncbrowse = false;
404 if (m_pDirectoryListing->path != m_sync_browse.remote_root && !m_pDirectoryListing->path.IsSubdirOf(m_sync_browse.remote_root, false)) {
405 SetSyncBrowse(false);
406 wxString msg = wxString::Format(_("Current remote directory (%s) is not below the synchronization root (%s).\nSynchronized browsing has been disabled."),
407 m_pDirectoryListing->path.GetPath(),
408 m_sync_browse.remote_root.GetPath());
409 wxMessageBoxEx(msg, _("Synchronized browsing"));
410 }
411 else {
412 CLocalPath local_path = GetSynchronizedDirectory(m_pDirectoryListing->path);
413 if (local_path.empty()) {
414 SetSyncBrowse(false);
415 wxString msg = wxString::Format(_("Could not obtain corresponding local directory for the remote directory '%s'.\nSynchronized browsing has been disabled."),
416 m_pDirectoryListing->path.GetPath());
417 wxMessageBoxEx(msg, _("Synchronized browsing"));
418 compare = false;
419 }
420 else {
421 std::wstring error;
422 if (!local_path.Exists(&error)) {
423 SetSyncBrowse(false);
424 wxString msg = error + _T("\n") + _("Synchronized browsing has been disabled.");
425 wxMessageBoxEx(msg, _("Synchronized browsing"));
426 compare = false;
427 }
428 else {
429 m_localDir = local_path;
430
431 NotifyHandlers(STATECHANGE_LOCAL_DIR);
432 }
433 }
434 }
435 }
436
437 if (compare && !m_pComparisonManager->IsComparing()) {
438 m_pComparisonManager->CompareListings();
439 }
440
441 return true;
442 }
443
GetRemoteDir() const444 std::shared_ptr<CDirectoryListing> CState::GetRemoteDir() const
445 {
446 return m_pDirectoryListing;
447 }
448
GetRemotePath() const449 const CServerPath CState::GetRemotePath() const
450 {
451 if (!m_pDirectoryListing) {
452 return CServerPath();
453 }
454
455 return m_pDirectoryListing->path;
456 }
457
RefreshLocal()458 void CState::RefreshLocal()
459 {
460 NotifyHandlers(STATECHANGE_LOCAL_DIR);
461 }
462
RefreshLocalFile(std::wstring const & file)463 void CState::RefreshLocalFile(std::wstring const& file)
464 {
465 std::wstring file_name;
466 CLocalPath path(file, &file_name);
467 if (path.empty()) {
468 return;
469 }
470
471 if (file_name.empty()) {
472 if (!path.HasParent()) {
473 return;
474 }
475 path.MakeParent(&file_name);
476 wxASSERT(!file_name.empty());
477 }
478
479 if (path != m_localDir) {
480 return;
481 }
482
483 NotifyHandlers(STATECHANGE_LOCAL_REFRESH_FILE, file_name);
484 }
485
LocalDirCreated(const CLocalPath & path)486 void CState::LocalDirCreated(const CLocalPath& path)
487 {
488 if (!path.IsSubdirOf(m_localDir)) {
489 return;
490 }
491
492 std::wstring next_segment = path.GetPath().substr(m_localDir.GetPath().size());
493 size_t pos = next_segment.find(CLocalPath::path_separator);
494 if (pos == std::wstring::npos || !pos) {
495 // Shouldn't ever come true
496 return;
497 }
498
499 // Current local path is /foo/
500 // Called with /foo/bar/baz/
501 // -> Refresh /foo/bar/
502 next_segment = next_segment.substr(0, pos);
503 NotifyHandlers(STATECHANGE_LOCAL_REFRESH_FILE, next_segment);
504 }
505
SetSite(Site const & site,CServerPath const & path)506 void CState::SetSite(Site const& site, CServerPath const& path)
507 {
508 if (m_site) {
509 if (site == m_site) {
510 // Nothing changes, other than possibly credentials
511 m_site = site;
512 return;
513 }
514
515 SetRemoteDir(nullptr, true);
516 m_pCertificate.reset();
517 m_pSftpEncryptionInfo.reset();
518 }
519 if (site) {
520 if (!path.empty()) {
521 m_last_path = path;
522 }
523 else if (m_last_site.server != site.server) {
524 m_last_path.clear();
525 }
526 m_last_site = site;
527 }
528
529 m_site = site;
530
531 UpdateTitle();
532
533 m_successful_connect = false;
534
535 NotifyHandlers(STATECHANGE_SERVER);
536 }
537
GetSite() const538 Site const& CState::GetSite() const
539 {
540 return m_site;
541 }
542
GetTitle() const543 wxString CState::GetTitle() const
544 {
545 return m_title;
546 }
547
Connect(Site const & site,CServerPath const & path,bool compare)548 bool CState::Connect(Site const& site, CServerPath const& path, bool compare)
549 {
550 if (!site) {
551 return false;
552 }
553 if (!engine_) {
554 return false;
555 }
556 if (engine_->IsConnected() || engine_->IsBusy() || !m_pCommandQueue->Idle()) {
557 m_pCommandQueue->Cancel();
558 }
559 m_pRemoteRecursiveOperation->StopRecursiveOperation();
560 SetSyncBrowse(false);
561 m_changeDirFlags.compare = compare;
562
563 SetSite(site, path);
564
565 // Use m_site from here on
566 m_pCommandQueue->ProcessCommand(new CConnectCommand(m_site.server, m_site.Handle(), m_site.credentials));
567 m_pCommandQueue->ProcessCommand(new CListCommand(path, std::wstring(), LIST_FLAG_FALLBACK_CURRENT));
568
569 return true;
570 }
571
Disconnect()572 bool CState::Disconnect()
573 {
574 if (!engine_) {
575 return false;
576 }
577
578 if (!IsRemoteConnected()) {
579 return true;
580 }
581
582 if (!IsRemoteIdle()) {
583 return false;
584 }
585
586 SetSite(Site());
587 m_pCommandQueue->ProcessCommand(new CDisconnectCommand());
588
589 return true;
590 }
591
CreateEngine()592 bool CState::CreateEngine()
593 {
594 wxASSERT(!engine_);
595 if (engine_) {
596 return true;
597 }
598
599 engine_ = std::make_unique<CFileZillaEngine>(m_mainFrame.GetEngineContext(), fz::make_invoker(m_mainFrame, [frame = &m_mainFrame](CFileZillaEngine* engine){ frame->OnEngineEvent(engine); }));
600
601 m_pCommandQueue = new CCommandQueue(engine_.get(), &m_mainFrame, *this);
602
603 return true;
604 }
605
DestroyEngine()606 void CState::DestroyEngine()
607 {
608 delete m_pCommandQueue;
609 m_pCommandQueue = 0;
610 engine_.reset();
611 }
612
RegisterHandler(CStateEventHandler * pHandler,t_statechange_notifications notification,CStateEventHandler * insertBefore)613 void CState::RegisterHandler(CStateEventHandler* pHandler, t_statechange_notifications notification, CStateEventHandler* insertBefore)
614 {
615 wxASSERT(pHandler);
616 wxASSERT(&pHandler->m_state == this);
617 if (!pHandler || &pHandler->m_state != this) {
618 return;
619 }
620 wxASSERT(notification != STATECHANGE_MAX && notification != STATECHANGE_NONE);
621 wxASSERT(pHandler != insertBefore);
622
623
624 auto &handlers = m_handlers[notification];
625
626 wxASSERT(!insertBefore || !handlers.inNotify_);
627
628 auto insertionPoint = handlers.handlers.end();
629
630 for (auto it = handlers.handlers.begin(); it != handlers.handlers.end(); ++it) {
631 if (*it == insertBefore) {
632 insertionPoint = it;
633 }
634 if (*it == pHandler) {
635 wxASSERT(insertionPoint == handlers.handlers.end());
636 return;
637 }
638 }
639
640 handlers.handlers.insert(insertionPoint, pHandler);
641 }
642
UnregisterHandler(CStateEventHandler * pHandler,t_statechange_notifications notification)643 void CState::UnregisterHandler(CStateEventHandler* pHandler, t_statechange_notifications notification)
644 {
645 wxASSERT(pHandler);
646 wxASSERT(notification != STATECHANGE_MAX);
647
648 if (notification == STATECHANGE_NONE) {
649 for (int i = 0; i < STATECHANGE_MAX; ++i) {
650 auto &handlers = m_handlers[i];
651 for (auto iter = handlers.handlers.begin(); iter != handlers.handlers.end(); ++iter) {
652 if (*iter == pHandler) {
653 if (handlers.inNotify_) {
654 handlers.compact_ = true;
655 *iter = 0;
656 }
657 else {
658 handlers.handlers.erase(iter);
659 }
660 break;
661 }
662 }
663 }
664 }
665 else {
666 auto &handlers = m_handlers[notification];
667 for (auto iter = handlers.handlers.begin(); iter != handlers.handlers.end(); ++iter) {
668 if (*iter == pHandler) {
669 if (handlers.inNotify_) {
670 handlers.compact_ = true;
671 *iter = 0;
672 }
673 else {
674 handlers.handlers.erase(iter);
675 }
676 return;
677 }
678 }
679 }
680 }
681
NotifyHandlers(t_statechange_notifications notification,std::wstring const & data,const void * data2)682 void CState::NotifyHandlers(t_statechange_notifications notification, std::wstring const& data, const void* data2)
683 {
684 wxASSERT(notification != STATECHANGE_NONE && notification != STATECHANGE_MAX);
685
686 auto & handlers = m_handlers[notification];
687
688 wxASSERT(!handlers.inNotify_);
689
690 handlers.inNotify_ = true;
691 // Can't use iterators as inserting a handler might invalidate them
692 for (size_t i = 0; i < handlers.handlers.size(); ++i) {
693 if (handlers.handlers[i]) {
694 handlers.handlers[i]->OnStateChange(notification, data, data2);
695 }
696 }
697
698 if (handlers.compact_) {
699 handlers.handlers.erase(std::remove(handlers.handlers.begin(), handlers.handlers.end(), nullptr), handlers.handlers.end());
700 handlers.compact_ = false;
701 }
702
703 handlers.inNotify_ = false;
704
705 CContextManager::Get()->NotifyHandlers(this, notification, data, data2);
706 }
707
CStateEventHandler(CState & state)708 CStateEventHandler::CStateEventHandler(CState &state)
709 : m_state(state)
710 {
711 }
712
~CStateEventHandler()713 CStateEventHandler::~CStateEventHandler()
714 {
715 m_state.UnregisterHandler(this, STATECHANGE_NONE);
716 }
717
~CGlobalStateEventHandler()718 CGlobalStateEventHandler::~CGlobalStateEventHandler()
719 {
720 CContextManager::Get()->UnregisterHandler(this, STATECHANGE_NONE);
721 }
722
UploadDroppedFiles(CLocalDataObject const * pLocalDataObject,std::wstring const & subdir,bool queueOnly)723 void CState::UploadDroppedFiles(CLocalDataObject const* pLocalDataObject, std::wstring const& subdir, bool queueOnly)
724 {
725 if (!m_site || !m_pDirectoryListing) {
726 return;
727 }
728
729 CServerPath path = m_pDirectoryListing->path;
730 if (subdir == L".." && path.HasParent()) {
731 path = path.GetParent();
732 }
733 else if (!subdir.empty()) {
734 path.AddSegment(subdir);
735 }
736
737 UploadDroppedFiles(pLocalDataObject, path, queueOnly);
738 }
739
UploadDroppedFiles(const wxFileDataObject * pFileDataObject,std::wstring const & subdir,bool queueOnly)740 void CState::UploadDroppedFiles(const wxFileDataObject* pFileDataObject, std::wstring const& subdir, bool queueOnly)
741 {
742 if (!m_site || !m_pDirectoryListing) {
743 return;
744 }
745
746 CServerPath path = m_pDirectoryListing->path;
747 if (subdir == L".." && path.HasParent()) {
748 path = path.GetParent();
749 }
750 else if (!subdir.empty()) {
751 path.AddSegment(subdir);
752 }
753
754 UploadDroppedFiles(pFileDataObject, path, queueOnly);
755 }
756
757 namespace {
758 template <typename T>
DoUploadDroppedFiles(CState & state,CMainFrame & mainFrame,T const & files,const CServerPath & path,bool queueOnly)759 void DoUploadDroppedFiles(CState& state, CMainFrame & mainFrame, T const& files, const CServerPath& path, bool queueOnly)
760 {
761 if (files.empty()) {
762 return;
763 }
764
765 if (!state.GetSite()) {
766 return;
767 }
768
769 if (path.empty()) {
770 return;
771 }
772
773 auto recursiveOperation = state.GetLocalRecursiveOperation();
774 if (!recursiveOperation || recursiveOperation->IsActive()) {
775 wxBell();
776 return;
777 }
778
779 for (auto const& file : files) {
780 int64_t size;
781 bool is_link;
782 fz::local_filesys::type type = fz::local_filesys::get_file_info(fz::to_native(file), is_link, &size, 0, 0);
783 if (type == fz::local_filesys::file) {
784 std::wstring localFile;
785 CLocalPath const localPath(fz::to_wstring(file), &localFile);
786 mainFrame.GetQueue()->QueueFile(queueOnly, false, localFile, wxEmptyString, localPath, path, state.GetSite(), size);
787 mainFrame.GetQueue()->QueueFile_Finish(!queueOnly);
788 }
789 else if (type == fz::local_filesys::dir) {
790 CLocalPath localPath(fz::to_wstring(file));
791 if (localPath.HasParent()) {
792
793 CServerPath remotePath = path;
794 if (!remotePath.ChangePath(localPath.GetLastSegment())) {
795 continue;
796 }
797
798 local_recursion_root root;
799 root.add_dir_to_visit(localPath, remotePath);
800 recursiveOperation->AddRecursionRoot(std::move(root));
801 }
802 }
803 }
804
805 CFilterManager filter;
806 recursiveOperation->StartRecursiveOperation(recursive_operation::recursive_transfer, filter.GetActiveFilters(), !queueOnly);
807 }
808 }
809
UploadDroppedFiles(const CLocalDataObject * pLocalDataObject,const CServerPath & path,bool queueOnly)810 void CState::UploadDroppedFiles(const CLocalDataObject* pLocalDataObject, const CServerPath& path, bool queueOnly)
811 {
812 auto const files = pLocalDataObject->GetFilesW();
813 DoUploadDroppedFiles(*this, m_mainFrame, files, path, queueOnly);
814 }
815
UploadDroppedFiles(const wxFileDataObject * pFileDataObject,const CServerPath & path,bool queueOnly)816 void CState::UploadDroppedFiles(const wxFileDataObject* pFileDataObject, const CServerPath& path, bool queueOnly)
817 {
818 wxArrayString const& files = pFileDataObject->GetFilenames();
819 DoUploadDroppedFiles(*this, m_mainFrame, files, path, queueOnly);
820 }
821
822 namespace {
823 template<typename T>
DoHandleDroppedFiles(CState & state,CMainFrame & mainFrame,T const & files,CLocalPath const & path,bool copy)824 void DoHandleDroppedFiles(CState & state, CMainFrame & mainFrame, T const& files, CLocalPath const& path, bool copy)
825 {
826 if (files.empty()) {
827 return;
828 }
829
830 #ifdef __WXMSW__
831 int len = 1;
832
833 for (unsigned int i = 0; i < files.size(); ++i) {
834 len += files[i].size() + 1;
835 }
836
837 // SHFILEOPSTRUCT's pTo and pFrom accept null-terminated lists
838 // of null-terminated filenames.
839 wchar_t* from = new wchar_t[len];
840 wchar_t* p = from;
841 for (auto const& file : files) {
842 memcpy(p, static_cast<wchar_t const*>(file.c_str()), (file.size() + 1) * sizeof(wchar_t));
843 p += file.size() + 1;
844 }
845 *p = 0; // End of list
846
847 wchar_t* to = new wchar_t[path.GetPath().size() + 2];
848 memcpy(to, static_cast<wchar_t const*>(path.GetPath().c_str()), (path.GetPath().size() + 1) * sizeof(wchar_t));
849 to[path.GetPath().size() + 1] = 0; // End of list
850
851 SHFILEOPSTRUCT op{};
852 op.pFrom = from;
853 op.pTo = to;
854 op.wFunc = copy ? FO_COPY : FO_MOVE;
855 op.hwnd = (HWND)mainFrame.GetHandle();
856 SHFileOperation(&op);
857
858 delete[] to;
859 delete[] from;
860 #else
861 (void)mainFrame;
862
863 wxString error;
864 for (auto const& file : files) {
865 int64_t size;
866 bool is_link;
867 fz::local_filesys::type type = fz::local_filesys::get_file_info(fz::to_native(file), is_link, &size, 0, 0);
868 if (type == fz::local_filesys::file) {
869 std::wstring name;
870 CLocalPath sourcePath(fz::to_wstring(file), &name);
871 if (name.empty()) {
872 continue;
873 }
874 wxString target = path.GetPath() + name;
875 if (file == target) {
876 continue;
877 }
878
879 if (copy) {
880 wxCopyFile(file, target);
881 }
882 else {
883 wxRenameFile(file, target);
884 }
885 }
886 else if (type == fz::local_filesys::dir) {
887 CLocalPath sourcePath(fz::to_wstring(file));
888 if (sourcePath == path || sourcePath.GetParent() == path) {
889 continue;
890 }
891 if (sourcePath.IsParentOf(path)) {
892 error = _("A directory cannot be dragged into one of its subdirectories.");
893 continue;
894 }
895
896 if (copy) {
897 state.RecursiveCopy(sourcePath, path);
898 }
899 else {
900 if (!sourcePath.HasParent()) {
901 continue;
902 }
903 wxRenameFile(file, path.GetPath() + sourcePath.GetLastSegment());
904 }
905 }
906 }
907 if (!error.empty()) {
908 wxMessageBoxEx(error, _("Could not complete operation"));
909 }
910 #endif
911
912 state.RefreshLocal();
913 }
914 }
915
HandleDroppedFiles(CLocalDataObject const * pLocalDataObject,CLocalPath const & path,bool copy)916 void CState::HandleDroppedFiles(CLocalDataObject const* pLocalDataObject, CLocalPath const& path, bool copy)
917 {
918 auto const files = pLocalDataObject->GetFilesW();
919 DoHandleDroppedFiles(*this, m_mainFrame, files, path, copy);
920 }
921
HandleDroppedFiles(const wxFileDataObject * pFileDataObject,const CLocalPath & path,bool copy)922 void CState::HandleDroppedFiles(const wxFileDataObject* pFileDataObject, const CLocalPath& path, bool copy)
923 {
924 wxArrayString const& files = pFileDataObject->GetFilenames();
925 DoHandleDroppedFiles(*this, m_mainFrame, files, path, copy);
926 }
927
RecursiveCopy(CLocalPath source,const CLocalPath & target)928 bool CState::RecursiveCopy(CLocalPath source, const CLocalPath& target)
929 {
930 if (source.empty() || target.empty()) {
931 return false;
932 }
933
934 if (source == target) {
935 return false;
936 }
937
938 if (source.IsParentOf(target)) {
939 return false;
940 }
941
942 if (!source.HasParent()) {
943 return false;
944 }
945
946 std::wstring last_segment;
947 if (!source.MakeParent(&last_segment)) {
948 return false;
949 }
950
951 std::list<std::wstring> dirsToVisit;
952 dirsToVisit.push_back(last_segment + CLocalPath::path_separator);
953
954 fz::native_string const nsource = fz::to_native(source.GetPath());
955
956 // Process any subdirs which still have to be visited
957 while (!dirsToVisit.empty()) {
958 std::wstring dirname = dirsToVisit.front();
959 dirsToVisit.pop_front();
960 wxMkdir(target.GetPath() + dirname);
961
962 fz::local_filesys fs;
963 if (!fs.begin_find_files(nsource + fz::to_native(dirname), false)) {
964 continue;
965 }
966
967 bool is_link{};
968 fz::local_filesys::type t{};
969 fz::native_string file;
970 while (fs.get_next_file(file, is_link, t, 0, 0, 0)) {
971 if (file.empty()) {
972 wxGetApp().DisplayEncodingWarning();
973 continue;
974 }
975
976 if (t == fz::local_filesys::dir) {
977 if (is_link) {
978 continue;
979 }
980
981 std::wstring const subDir = dirname + fz::to_wstring(file) + CLocalPath::path_separator;
982 dirsToVisit.push_back(subDir);
983 }
984 else {
985 wxCopyFile(source.GetPath() + dirname + file, target.GetPath() + dirname + file);
986 }
987 }
988 }
989
990 return true;
991 }
992
DownloadDroppedFiles(const CRemoteDataObject * pRemoteDataObject,const CLocalPath & path,bool queueOnly)993 bool CState::DownloadDroppedFiles(const CRemoteDataObject* pRemoteDataObject, const CLocalPath& path, bool queueOnly /*=false*/)
994 {
995 bool hasDirs = false;
996 bool hasFiles = false;
997 std::vector<CRemoteDataObject::t_fileInfo> const& files = pRemoteDataObject->GetFiles();
998 for (auto const& fileInfo : files) {
999 if (fileInfo.dir) {
1000 hasDirs = true;
1001 }
1002 else {
1003 hasFiles = true;
1004 }
1005 }
1006
1007 if (hasDirs) {
1008 if (!IsRemoteConnected() || !IsRemoteIdle()) {
1009 return false;
1010 }
1011 }
1012
1013 if (hasFiles) {
1014 m_mainFrame.GetQueue()->QueueFiles(queueOnly, path, *pRemoteDataObject);
1015 }
1016
1017 if (!hasDirs) {
1018 return true;
1019 }
1020
1021 recursion_root root(pRemoteDataObject->GetServerPath(), false);
1022 for (auto const& fileInfo : files) {
1023 if (!fileInfo.dir) {
1024 continue;
1025 }
1026
1027 CLocalPath newPath(path);
1028 newPath.AddSegment(CQueueView::ReplaceInvalidCharacters(fileInfo.name));
1029 root.add_dir_to_visit(pRemoteDataObject->GetServerPath(), fileInfo.name, newPath, fileInfo.link);
1030 }
1031
1032 if (!root.empty()) {
1033 m_pRemoteRecursiveOperation->AddRecursionRoot(std::move(root));
1034
1035 CFilterManager filter;
1036 m_pRemoteRecursiveOperation->StartRecursiveOperation(recursive_operation::recursive_transfer, filter.GetActiveFilters(), !queueOnly);
1037 }
1038
1039 return true;
1040 }
1041
IsRemoteConnected() const1042 bool CState::IsRemoteConnected() const
1043 {
1044 if (!engine_) {
1045 return false;
1046 }
1047
1048 return static_cast<bool>(m_site);
1049 }
1050
IsRemoteIdle(bool ignore_recursive) const1051 bool CState::IsRemoteIdle(bool ignore_recursive) const
1052 {
1053 if (!ignore_recursive && m_pRemoteRecursiveOperation && m_pRemoteRecursiveOperation->GetOperationMode() != recursive_operation::recursive_none) {
1054 return false;
1055 }
1056
1057 if (!m_pCommandQueue) {
1058 return true;
1059 }
1060
1061 return m_pCommandQueue->Idle(ignore_recursive ? CCommandQueue::normal : CCommandQueue::any);
1062 }
1063
IsLocalIdle(bool ignore_recursive) const1064 bool CState::IsLocalIdle(bool ignore_recursive) const
1065 {
1066 if (!ignore_recursive && m_pLocalRecursiveOperation && m_pLocalRecursiveOperation->GetOperationMode() != recursive_operation::recursive_none) {
1067 return false;
1068 }
1069
1070 return true;
1071 }
1072
ListingFailed(int)1073 void CState::ListingFailed(int)
1074 {
1075 bool const compare = m_changeDirFlags.compare;
1076 m_changeDirFlags.compare = false;
1077
1078 if (m_changeDirFlags.syncbrowse && !m_sync_browse.target_path.empty()) {
1079 wxDialogEx dlg;
1080 if (!dlg.Load(0, _T("ID_SYNCBROWSE_NONEXISTING"))) {
1081 SetSyncBrowse(false);
1082 return;
1083 }
1084
1085 xrc_call(dlg, "ID_SYNCBROWSE_NONEXISTING_LABEL", &wxStaticText::SetLabel, wxString::Format(_("The remote directory '%s' does not exist."), m_sync_browse.target_path.GetPath()));
1086 xrc_call(dlg, "ID_SYNCBROWSE_CREATE", &wxRadioButton::SetLabel, _("Create &missing remote directory and enter it"));
1087 xrc_call(dlg, "ID_SYNCBROWSE_DISABLE", &wxRadioButton::SetLabel, _("&Disable synchronized browsing and continue changing the local directory"));
1088 dlg.GetSizer()->Fit(&dlg);
1089 if (dlg.ShowModal() == wxID_OK) {
1090 if (xrc_call(dlg, "ID_SYNCBROWSE_CREATE", &wxRadioButton::GetValue)) {
1091 m_pCommandQueue->ProcessCommand(new CMkdirCommand(m_sync_browse.target_path));
1092 m_pCommandQueue->ProcessCommand(new CListCommand(m_sync_browse.target_path));
1093 m_sync_browse.target_path.clear();
1094 m_changeDirFlags.compare = compare;
1095 return;
1096 }
1097 else {
1098 CLocalPath local = GetSynchronizedDirectory(m_sync_browse.target_path);
1099 SetSyncBrowse(false);
1100 SetLocalDir(local);
1101 }
1102 }
1103 else {
1104 m_changeDirFlags.syncbrowse = false;
1105 }
1106 }
1107 }
1108
LinkIsNotDir(const CServerPath & path,std::wstring const & subdir)1109 void CState::LinkIsNotDir(const CServerPath& path, std::wstring const& subdir)
1110 {
1111 m_changeDirFlags.compare = false;
1112 m_changeDirFlags.syncbrowse = false;
1113
1114 NotifyHandlers(STATECHANGE_REMOTE_LINKNOTDIR, subdir, &path);
1115 }
1116
ChangeRemoteDir(CServerPath const & path,std::wstring const & subdir,int flags,bool ignore_busy,bool compare)1117 bool CState::ChangeRemoteDir(CServerPath const& path, std::wstring const& subdir, int flags, bool ignore_busy, bool compare)
1118 {
1119 if (!m_site || !m_pCommandQueue) {
1120 return false;
1121 }
1122
1123 if (!m_sync_browse.local_root.empty()) {
1124 CServerPath p(path);
1125 if (!subdir.empty() && !p.ChangePath(subdir)) {
1126 wxString msg = wxString::Format(_("Could not get full remote path."));
1127 wxMessageBoxEx(msg, _("Synchronized browsing"));
1128 return false;
1129 }
1130
1131 if (p != m_sync_browse.remote_root && !p.IsSubdirOf(m_sync_browse.remote_root, false)) {
1132 wxString msg = wxString::Format(_("The remote directory '%s' is not below the synchronization root (%s).\nDisable synchronized browsing and continue changing the remote directory?"),
1133 p.GetPath(),
1134 m_sync_browse.remote_root.GetPath());
1135 if (wxMessageBoxEx(msg, _("Synchronized browsing"), wxICON_QUESTION | wxYES_NO) != wxYES) {
1136 return false;
1137 }
1138 SetSyncBrowse(false);
1139 }
1140 else if (!IsRemoteIdle(true) && !ignore_busy) {
1141 wxString msg(_("Another remote operation is already in progress, cannot change directory now."));
1142 wxMessageBoxEx(msg, _("Synchronized browsing"), wxICON_EXCLAMATION);
1143 return false;
1144 }
1145 else {
1146 std::wstring error;
1147 CLocalPath local_path = GetSynchronizedDirectory(p);
1148 if (local_path.empty()) {
1149 wxString msg = wxString::Format(_("Could not obtain corresponding local directory for the remote directory '%s'.\nDisable synchronized browsing and continue changing the remote directory?"),
1150 p.GetPath());
1151 if (wxMessageBoxEx(msg, _("Synchronized browsing"), wxICON_QUESTION | wxYES_NO) != wxYES) {
1152 return false;
1153 }
1154 SetSyncBrowse(false);
1155 }
1156 else if (!local_path.Exists(&error)) {
1157 wxString msg = error + _T("\n") + _("Disable synchronized browsing and continue changing the remote directory?");
1158
1159 wxDialogEx dlg;
1160 if (!dlg.Load(0, _T("ID_SYNCBROWSE_NONEXISTING"))) {
1161 return false;
1162 }
1163 xrc_call(dlg, "ID_SYNCBROWSE_NONEXISTING_LABEL", &wxStaticText::SetLabel, wxString::Format(_("The local directory '%s' does not exist."), local_path.GetPath()));
1164 xrc_call(dlg, "ID_SYNCBROWSE_CREATE", &wxRadioButton::SetLabel, _("Create &missing local directory and enter it"));
1165 xrc_call(dlg, "ID_SYNCBROWSE_DISABLE", &wxRadioButton::SetLabel, _("&Disable synchronized browsing and continue changing the remote directory"));
1166 dlg.GetSizer()->Fit(&dlg);
1167 if (dlg.ShowModal() != wxID_OK) {
1168 return false;
1169 }
1170
1171 if (xrc_call(dlg, "ID_SYNCBROWSE_CREATE", &wxRadioButton::GetValue)) {
1172 {
1173 wxLogNull log;
1174 wxMkdir(local_path.GetPath());
1175 }
1176
1177 if (!local_path.Exists(&error)) {
1178 wxMessageBoxEx(wxString::Format(_("The local directory '%s' could not be created."), local_path.GetPath()), _("Synchronized browsing"), wxICON_EXCLAMATION);
1179 return false;
1180 }
1181 m_changeDirFlags.syncbrowse = true;
1182 m_changeDirFlags.compare = m_pComparisonManager->IsComparing();
1183 m_sync_browse.target_path.clear();
1184 }
1185 else {
1186 SetSyncBrowse(false);
1187 }
1188 }
1189 else {
1190 m_changeDirFlags.syncbrowse = true;
1191 m_changeDirFlags.compare = m_pComparisonManager->IsComparing();
1192 m_sync_browse.target_path.clear();
1193 }
1194 }
1195 }
1196
1197 CListCommand *pCommand = new CListCommand(path, subdir, flags);
1198 m_pCommandQueue->ProcessCommand(pCommand);
1199
1200 if (compare) {
1201 m_changeDirFlags.compare = true;
1202 }
1203
1204 return true;
1205 }
1206
SetSyncBrowse(bool enable,CServerPath const & assumed_remote_root)1207 bool CState::SetSyncBrowse(bool enable, CServerPath const& assumed_remote_root)
1208 {
1209 if (enable != m_sync_browse.local_root.empty()) {
1210 return enable;
1211 }
1212
1213 if (!enable) {
1214 wxASSERT(assumed_remote_root.empty());
1215 m_sync_browse.local_root.clear();
1216 m_sync_browse.remote_root.clear();
1217 m_changeDirFlags.syncbrowse = false;
1218
1219 NotifyHandlers(STATECHANGE_SYNC_BROWSE);
1220 return false;
1221 }
1222
1223 if (!m_pDirectoryListing && assumed_remote_root.empty()) {
1224 NotifyHandlers(STATECHANGE_SYNC_BROWSE);
1225 return false;
1226 }
1227
1228 m_changeDirFlags.syncbrowse = false;
1229 m_sync_browse.local_root = m_localDir;
1230
1231 if (assumed_remote_root.empty()) {
1232 m_sync_browse.remote_root = m_pDirectoryListing->path;
1233 }
1234 else {
1235 m_sync_browse.remote_root = assumed_remote_root;
1236 m_changeDirFlags.syncbrowse = true;
1237 }
1238
1239 while (m_sync_browse.local_root.HasParent() && m_sync_browse.remote_root.HasParent() &&
1240 m_sync_browse.local_root.GetLastSegment() == m_sync_browse.remote_root.GetLastSegment())
1241 {
1242 m_sync_browse.local_root.MakeParent();
1243 m_sync_browse.remote_root = m_sync_browse.remote_root.GetParent();
1244 }
1245
1246 NotifyHandlers(STATECHANGE_SYNC_BROWSE);
1247 return true;
1248 }
1249
GetSynchronizedDirectory(CServerPath remote_path)1250 CLocalPath CState::GetSynchronizedDirectory(CServerPath remote_path)
1251 {
1252 std::list<std::wstring> segments;
1253 while (remote_path.HasParent() && remote_path != m_sync_browse.remote_root) {
1254 segments.push_front(remote_path.GetLastSegment());
1255 remote_path = remote_path.GetParent();
1256 }
1257 if (remote_path != m_sync_browse.remote_root) {
1258 return CLocalPath();
1259 }
1260
1261 CLocalPath local_path = m_sync_browse.local_root;
1262 for (auto const& segment : segments) {
1263 local_path.AddSegment(segment);
1264 }
1265
1266 return local_path;
1267 }
1268
1269
GetSynchronizedDirectory(CLocalPath local_path)1270 CServerPath CState::GetSynchronizedDirectory(CLocalPath local_path)
1271 {
1272 std::list<std::wstring> segments;
1273 while (local_path.HasParent() && local_path != m_sync_browse.local_root) {
1274 std::wstring last;
1275 local_path.MakeParent(&last);
1276 segments.push_front(last);
1277 }
1278 if (local_path != m_sync_browse.local_root) {
1279 return CServerPath();
1280 }
1281
1282 CServerPath remote_path = m_sync_browse.remote_root;
1283 for (auto const& segment : segments) {
1284 remote_path.AddSegment(segment);
1285 }
1286
1287 return remote_path;
1288 }
1289
RefreshRemote(bool clear_cache)1290 bool CState::RefreshRemote(bool clear_cache)
1291 {
1292 if (!m_pCommandQueue) {
1293 return false;
1294 }
1295
1296 if (!IsRemoteConnected() || !IsRemoteIdle(true)) {
1297 return false;
1298 }
1299
1300 int flags = LIST_FLAG_REFRESH;
1301 if (clear_cache) {
1302 flags |= LIST_FLAG_CLEARCACHE;
1303 }
1304
1305 return ChangeRemoteDir(GetRemotePath(), std::wstring(), flags);
1306 }
1307
GetSecurityInfo(CCertificateNotification * & pInfo)1308 bool CState::GetSecurityInfo(CCertificateNotification *& pInfo)
1309 {
1310 pInfo = m_pCertificate.get();
1311 return m_pCertificate != 0;
1312 }
1313
GetSecurityInfo(CSftpEncryptionNotification * & pInfo)1314 bool CState::GetSecurityInfo(CSftpEncryptionNotification *& pInfo)
1315 {
1316 pInfo = m_pSftpEncryptionInfo.get();
1317 return m_pSftpEncryptionInfo != 0;
1318 }
1319
SetSecurityInfo(CCertificateNotification const & info)1320 void CState::SetSecurityInfo(CCertificateNotification const& info)
1321 {
1322 m_pSftpEncryptionInfo.reset();
1323 m_pCertificate = std::make_unique<CCertificateNotification>(info);
1324 NotifyHandlers(STATECHANGE_ENCRYPTION);
1325 }
1326
SetSecurityInfo(CSftpEncryptionNotification const & info)1327 void CState::SetSecurityInfo(CSftpEncryptionNotification const& info)
1328 {
1329 m_pCertificate.reset();
1330 m_pSftpEncryptionInfo = std::make_unique<CSftpEncryptionNotification>(info);
1331 NotifyHandlers(STATECHANGE_ENCRYPTION);
1332 }
1333
UpdateSite(std::wstring const & oldPath,Site const & newSite)1334 void CState::UpdateSite(std::wstring const& oldPath, Site const& newSite)
1335 {
1336 if (newSite.SitePath().empty() || !newSite) {
1337 return;
1338 }
1339
1340 bool changed = false;
1341 if (m_site && m_site != newSite) {
1342 if (m_site.SitePath() == oldPath && m_site.GetOriginalServer().SameResource(newSite.GetOriginalServer())) {
1343 // Update handles
1344 m_site.Update(newSite);
1345 changed = true;
1346 }
1347 }
1348 if (m_last_site && m_last_site != newSite) {
1349 if (m_last_site.SitePath() == oldPath && m_last_site.GetOriginalServer().SameResource(newSite.GetOriginalServer())) {
1350 m_last_site.Update(newSite);
1351 if (!m_site) {
1352 // Active site has precedence over historic data
1353 changed = true;
1354 }
1355 }
1356 }
1357 if (changed) {
1358 UpdateTitle();
1359 NotifyHandlers(STATECHANGE_SERVER);
1360 }
1361 }
1362
UpdateKnownSites(std::vector<CSiteManagerDialog::_connected_site> const & active_sites)1363 void CState::UpdateKnownSites(std::vector<CSiteManagerDialog::_connected_site> const& active_sites)
1364 {
1365 bool changed{};
1366 if (m_site) {
1367 for (auto const& active_site : active_sites) {
1368 if (active_site.old_path == m_site.SitePath()) {
1369 if (active_site.old_path == m_site.SitePath() && active_site.site && m_site.GetOriginalServer().SameResource(active_site.site.GetOriginalServer())) {
1370 if (m_site != active_site.site) {
1371 changed = true;
1372 m_site.Update(active_site.site);
1373 }
1374 }
1375 else {
1376 changed = true;
1377 m_site.SetSitePath(std::wstring());
1378 }
1379 m_last_site = m_site;
1380 break;
1381 }
1382 }
1383 }
1384 else if (m_last_site) {
1385 for (auto const& active_site : active_sites) {
1386 if (active_site.old_path == m_last_site.SitePath()) {
1387 if (active_site.old_path == m_last_site.SitePath() && active_site.site && m_last_site.GetOriginalServer().SameResource(active_site.site.GetOriginalServer())) {
1388 if (m_last_site != active_site.site) {
1389 changed = true;
1390 m_last_site.Update(active_site.site);
1391 }
1392 }
1393 else {
1394 changed = true;
1395 m_last_site.SetSitePath(std::wstring());
1396 }
1397 }
1398 break;
1399 }
1400 }
1401
1402 if (changed) {
1403 UpdateTitle();
1404 NotifyHandlers(STATECHANGE_SERVER);
1405 }
1406 }
1407
UpdateTitle()1408 void CState::UpdateTitle()
1409 {
1410 if (m_site) {
1411 std::wstring const& name = m_site.GetName();
1412 m_title.clear();
1413 if (!name.empty()) {
1414 m_title = name + _T(" - ");
1415 }
1416 m_title += m_site.Format(ServerFormat::with_user_and_optional_port);
1417 }
1418 else {
1419 m_title = _("Not connected");
1420 }
1421 }
1422
ChangeServer(CServer const & newServer)1423 void CState::ChangeServer(CServer const& newServer)
1424 {
1425 if (m_site) {
1426 if (!m_site.originalServer) {
1427 m_site.originalServer = m_site.server;
1428 }
1429 m_site.server = newServer;
1430 }
1431 }
1432