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