source: filezilla/trunk/fuentes/src/engine/engineprivate.cpp @ 130

Last change on this file since 130 was 130, checked in by jrpelegrina, 3 years ago

First release to xenial

File size: 24.3 KB
Line 
1#include <filezilla.h>
2#include "ControlSocket.h"
3#include "directorycache.h"
4#include "engineprivate.h"
5#include "event_loop.h"
6#include "ftpcontrolsocket.h"
7#include "httpcontrolsocket.h"
8#include "logging_private.h"
9#include "pathcache.h"
10#include "ratelimiter.h"
11#include "sftpcontrolsocket.h"
12
13#include <algorithm>
14
15mutex CFileZillaEnginePrivate::mutex_;
16std::vector<CFileZillaEnginePrivate*> CFileZillaEnginePrivate::m_engineList;
17std::atomic_int CFileZillaEnginePrivate::m_activeStatus[2] = {{0}, {0}};
18std::list<CFileZillaEnginePrivate::t_failedLogins> CFileZillaEnginePrivate::m_failedLogins;
19
20CFileZillaEnginePrivate::CFileZillaEnginePrivate(CFileZillaEngineContext& context, CFileZillaEngine& parent)
21        : CEventHandler(context.GetEventLoop())
22        , transfer_status_(*this)
23        , m_options(context.GetOptions())
24        , m_rateLimiter(context.GetRateLimiter())
25        , directory_cache_(context.GetDirectoryCache())
26        , path_cache_(context.GetPathCache())
27        , parent_(parent)
28{
29        m_engineList.push_back(this);
30
31        {
32                scoped_lock lock(mutex_);
33                static int id = 0;
34                m_engine_id = ++id;
35        }
36
37        m_pLogging = new CLogging(*this);
38
39        {
40                bool queue_logs = ShouldQueueLogsFromOptions();
41                scoped_lock lock(notification_mutex_);
42                queue_logs_ = queue_logs;
43        }
44
45        RegisterOption(OPTION_LOGGING_SHOW_DETAILED_LOGS);
46        RegisterOption(OPTION_LOGGING_DEBUGLEVEL);
47        RegisterOption(OPTION_LOGGING_RAWLISTING);
48}
49
50bool CFileZillaEnginePrivate::ShouldQueueLogsFromOptions() const
51{
52        return
53                m_options.GetOptionVal(OPTION_LOGGING_RAWLISTING) == 0 &&
54                m_options.GetOptionVal(OPTION_LOGGING_DEBUGLEVEL) == 0 &&
55                m_options.GetOptionVal(OPTION_LOGGING_SHOW_DETAILED_LOGS) == 0;
56}
57
58CFileZillaEnginePrivate::~CFileZillaEnginePrivate()
59{
60        RemoveHandler();
61        m_maySendNotificationEvent = false;
62
63        m_pControlSocket.reset();
64        m_pCurrentCommand.reset();
65
66        // Delete notification list
67        for (auto & notification : m_NotificationList) {
68                delete notification;
69        }
70
71        // Remove ourself from the engine list
72        m_engineList.erase(std::remove(m_engineList.begin(), m_engineList.end(), this), m_engineList.end());
73        for (auto iter = m_engineList.begin(); iter != m_engineList.end(); ++iter) {
74                if (*iter == this) {
75                        m_engineList.erase(iter);
76                        break;
77                }
78        }
79
80        delete m_pLogging;
81
82        if (m_engineList.empty())
83                CSocket::Cleanup(true);
84}
85
86void CFileZillaEnginePrivate::OnEngineEvent(EngineNotificationType type)
87{
88        switch (type)
89        {
90        case engineCancel:
91                DoCancel();
92                break;
93        case engineTransferEnd:
94                if (m_pControlSocket)
95                        m_pControlSocket->TransferEnd();
96        default:
97                break;
98        }
99}
100
101bool CFileZillaEnginePrivate::IsBusy() const
102{
103        scoped_lock lock(mutex_);
104        return m_pCurrentCommand != 0;
105}
106
107bool CFileZillaEnginePrivate::IsConnected() const
108{
109        scoped_lock lock(mutex_);
110        if (!m_pControlSocket)
111                return false;
112
113        return m_pControlSocket->Connected();
114}
115
116const CCommand *CFileZillaEnginePrivate::GetCurrentCommand() const
117{
118        scoped_lock lock(mutex_);
119        return m_pCurrentCommand.get();
120}
121
122Command CFileZillaEnginePrivate::GetCurrentCommandId() const
123{
124        scoped_lock lock(mutex_);
125        if (!m_pCurrentCommand)
126                return Command::none;
127        else
128                return GetCurrentCommand()->GetId();
129}
130
131void CFileZillaEnginePrivate::AddNotification(CNotification *pNotification)
132{
133        {
134                scoped_lock lock(notification_mutex_);
135                m_NotificationList.push_back(pNotification);
136
137                if (!m_maySendNotificationEvent || !m_pEventHandler) {
138                        return;
139                }
140                m_maySendNotificationEvent = false;
141        }
142
143        m_pEventHandler->QueueEvent(new wxFzEvent(&parent_));
144}
145
146void CFileZillaEnginePrivate::AddLogNotification(CLogmsgNotification *pNotification)
147{
148        scoped_lock lock(notification_mutex_);
149
150        if (pNotification->msgType == MessageType::Error) {
151                queue_logs_ = false;
152                SendQueuedLogs();
153                AddNotification(pNotification);
154        }
155        else if (pNotification->msgType == MessageType::Status) {
156                ClearQueuedLogs(false);
157                AddNotification(pNotification);
158        }
159        else if (!queue_logs_) {
160                AddNotification(pNotification);
161        }
162        else {
163                queued_logs_.push_back(pNotification);
164        }
165}
166
167void CFileZillaEnginePrivate::SendQueuedLogs(bool reset_flag)
168{
169        {
170                scoped_lock lock(notification_mutex_);
171                m_NotificationList.insert(m_NotificationList.end(), queued_logs_.begin(), queued_logs_.end());
172                queued_logs_.clear();
173
174                if (reset_flag) {
175                        queue_logs_ = ShouldQueueLogsFromOptions();
176                }
177
178                if (!m_maySendNotificationEvent || !m_pEventHandler || m_NotificationList.empty()) {
179                        return;
180                }
181                m_maySendNotificationEvent = false;
182        }
183
184        m_pEventHandler->QueueEvent(new wxFzEvent(&parent_));
185}
186
187void CFileZillaEnginePrivate::ClearQueuedLogs(bool reset_flag)
188{
189        scoped_lock lock(notification_mutex_);
190
191        for (auto msg : queued_logs_) {
192                delete msg;
193        }
194        queued_logs_.clear();
195
196        if (reset_flag) {
197                queue_logs_ = ShouldQueueLogsFromOptions();
198        }
199}
200
201int CFileZillaEnginePrivate::ResetOperation(int nErrorCode)
202{
203        scoped_lock lock(mutex_);
204        m_pLogging->LogMessage(MessageType::Debug_Debug, _T("CFileZillaEnginePrivate::ResetOperation(%d)"), nErrorCode);
205
206        if (nErrorCode & FZ_REPLY_DISCONNECTED)
207                m_lastListDir.clear();
208
209        if (m_pCurrentCommand) {
210                if ((nErrorCode & FZ_REPLY_NOTSUPPORTED) == FZ_REPLY_NOTSUPPORTED) {
211                        m_pLogging->LogMessage(MessageType::Error, _("Command not supported by this protocol"));
212                }
213
214                if (m_pCurrentCommand->GetId() == Command::connect) {
215                        if (!(nErrorCode & ~(FZ_REPLY_ERROR | FZ_REPLY_DISCONNECTED | FZ_REPLY_TIMEOUT | FZ_REPLY_CRITICALERROR | FZ_REPLY_PASSWORDFAILED)) &&
216                                nErrorCode & (FZ_REPLY_ERROR | FZ_REPLY_DISCONNECTED))
217                        {
218                                CConnectCommand const& connectCommand = static_cast<CConnectCommand const&>(*m_pCurrentCommand.get());
219
220                                RegisterFailedLoginAttempt(connectCommand.GetServer(), (nErrorCode & FZ_REPLY_CRITICALERROR) == FZ_REPLY_CRITICALERROR);
221
222                                if ((nErrorCode & FZ_REPLY_CRITICALERROR) != FZ_REPLY_CRITICALERROR) {
223                                        ++m_retryCount;
224                                        if (m_retryCount < m_options.GetOptionVal(OPTION_RECONNECTCOUNT) && connectCommand.RetryConnecting()) {
225                                                unsigned int delay = GetRemainingReconnectDelay(connectCommand.GetServer());
226                                                if (!delay)
227                                                        delay = 1;
228                                                m_pLogging->LogMessage(MessageType::Status, _("Waiting to retry..."));
229                                                StopTimer(m_retryTimer);
230                                                m_retryTimer = AddTimer(duration::from_milliseconds(delay), true);
231                                                return FZ_REPLY_WOULDBLOCK;
232                                        }
233                                }
234                        }
235                }
236
237                if (!m_bIsInCommand) {
238                        COperationNotification *notification = new COperationNotification();
239                        notification->nReplyCode = nErrorCode;
240                        notification->commandId = m_pCurrentCommand->GetId();
241                        AddNotification(notification);
242                }
243                else
244                        m_nControlSocketError |= nErrorCode;
245
246                m_pCurrentCommand.reset();
247        }
248        else if (nErrorCode & FZ_REPLY_DISCONNECTED) {
249                if (!m_bIsInCommand) {
250                        COperationNotification *notification = new COperationNotification();
251                        notification->nReplyCode = nErrorCode;
252                        notification->commandId = Command::none;
253                        AddNotification(notification);
254                }
255        }
256
257        if (nErrorCode != FZ_REPLY_OK) {
258                SendQueuedLogs(true);
259        }
260        else {
261                ClearQueuedLogs(true);
262        }
263
264        return nErrorCode;
265}
266
267unsigned int CFileZillaEnginePrivate::GetNextAsyncRequestNumber()
268{
269        scoped_lock lock(notification_mutex_);
270        return ++m_asyncRequestCounter;
271}
272
273// Command handlers
274int CFileZillaEnginePrivate::Connect(const CConnectCommand &command)
275{
276        if (IsConnected())
277                return FZ_REPLY_ALREADYCONNECTED;
278
279        m_retryCount = 0;
280
281        // Need to delete before setting m_pCurrentCommand.
282        // The destructor can call CFileZillaEnginePrivate::ResetOperation
283        // which would delete m_pCurrentCommand
284        m_pControlSocket.reset();
285        m_nControlSocketError = 0;
286
287        if (command.GetServer().GetPort() != CServer::GetDefaultPort(command.GetServer().GetProtocol())) {
288                ServerProtocol protocol = CServer::GetProtocolFromPort(command.GetServer().GetPort(), true);
289                if (protocol != UNKNOWN && protocol != command.GetServer().GetProtocol())
290                        m_pLogging->LogMessage(MessageType::Status, _("Selected port usually in use by a different protocol."));
291        }
292
293        return ContinueConnect();
294}
295
296int CFileZillaEnginePrivate::Disconnect(CDisconnectCommand const&)
297{
298        int res = FZ_REPLY_OK;
299        if (m_pControlSocket) {
300                res = m_pControlSocket->Disconnect();
301                m_pControlSocket.reset();
302        }
303
304        return res;
305}
306
307int CFileZillaEnginePrivate::List(const CListCommand &command)
308{
309        int flags = command.GetFlags();
310        bool const refresh = (command.GetFlags() & LIST_FLAG_REFRESH) != 0;
311        bool const avoid = (command.GetFlags() & LIST_FLAG_AVOID) != 0;
312
313        if (!refresh && !command.GetPath().empty()) {
314                const CServer* pServer = m_pControlSocket->GetCurrentServer();
315                if (pServer) {
316                        CServerPath path(path_cache_.Lookup(*pServer, command.GetPath(), command.GetSubDir()));
317                        if (path.empty() && command.GetSubDir().empty())
318                                path = command.GetPath();
319                        if (!path.empty()) {
320                                CDirectoryListing *pListing = new CDirectoryListing;
321                                bool is_outdated = false;
322                                bool found = directory_cache_.Lookup(*pListing, *pServer, path, true, is_outdated);
323                                if (found && !is_outdated) {
324                                        if (pListing->get_unsure_flags())
325                                                flags |= LIST_FLAG_REFRESH;
326                                        else {
327                                                if (!avoid) {
328                                                        m_lastListDir = pListing->path;
329                                                        m_lastListTime = CMonotonicClock::now();
330                                                        CDirectoryListingNotification *pNotification = new CDirectoryListingNotification(pListing->path);
331                                                        AddNotification(pNotification);
332                                                }
333                                                delete pListing;
334                                                return FZ_REPLY_OK;
335                                        }
336                                }
337                                if (is_outdated)
338                                        flags |= LIST_FLAG_REFRESH;
339                                delete pListing;
340                        }
341                }
342        }
343
344        return m_pControlSocket->List(command.GetPath(), command.GetSubDir(), flags);
345}
346
347int CFileZillaEnginePrivate::FileTransfer(const CFileTransferCommand &command)
348{
349        return m_pControlSocket->FileTransfer(command.GetLocalFile(), command.GetRemotePath(), command.GetRemoteFile(), command.Download(), command.GetTransferSettings());
350}
351
352int CFileZillaEnginePrivate::RawCommand(const CRawCommand& command)
353{
354        {
355                scoped_lock lock(notification_mutex_);
356                queue_logs_ = false;
357        }
358        return m_pControlSocket->RawCommand(command.GetCommand());
359}
360
361int CFileZillaEnginePrivate::Delete(CDeleteCommand& command)
362{
363        if (command.GetFiles().size() == 1) {
364                m_pLogging->LogMessage(MessageType::Status, _("Deleting \"%s\""), command.GetPath().FormatFilename(command.GetFiles().front()));
365        }
366        else {
367                m_pLogging->LogMessage(MessageType::Status, _("Deleting %u files from \"%s\""), static_cast<unsigned int>(command.GetFiles().size()), command.GetPath().GetPath());
368        }
369        return m_pControlSocket->Delete(command.GetPath(), command.ExtractFiles());
370}
371
372int CFileZillaEnginePrivate::RemoveDir(const CRemoveDirCommand& command)
373{
374        return m_pControlSocket->RemoveDir(command.GetPath(), command.GetSubDir());
375}
376
377int CFileZillaEnginePrivate::Mkdir(const CMkdirCommand& command)
378{
379        return m_pControlSocket->Mkdir(command.GetPath());
380}
381
382int CFileZillaEnginePrivate::Rename(const CRenameCommand& command)
383{
384        return m_pControlSocket->Rename(command);
385}
386
387int CFileZillaEnginePrivate::Chmod(const CChmodCommand& command)
388{
389        return m_pControlSocket->Chmod(command);
390}
391
392void CFileZillaEnginePrivate::SendDirectoryListingNotification(const CServerPath& path, bool onList, bool modified, bool failed)
393{
394        scoped_lock lock(mutex_);
395
396        wxASSERT(m_pControlSocket);
397
398        const CServer* const pOwnServer = m_pControlSocket->GetCurrentServer();
399        wxASSERT(pOwnServer);
400
401        m_lastListDir = path;
402
403        if (failed) {
404                AddNotification(new CDirectoryListingNotification(path, false, true));
405                m_lastListTime = CMonotonicClock::now();
406
407                // On failed messages, we don't notify other engines
408                return;
409        }
410
411        CMonotonicClock changeTime;
412        if (!directory_cache_.GetChangeTime(changeTime, *pOwnServer, path))
413                return;
414
415        AddNotification(new CDirectoryListingNotification(path, !onList));
416        m_lastListTime = changeTime;
417
418        if (!modified)
419                return;
420
421        // Iterate over the other engine, send notification if last listing
422        // directory is the same
423        for (auto & engine : m_engineList) {
424                if (!engine->m_pControlSocket || engine->m_pControlSocket == m_pControlSocket) {
425                        continue;
426                }
427
428                CServer const* const pServer = engine->m_pControlSocket->GetCurrentServer();
429                if (!pServer || *pServer != *pOwnServer)
430                        continue;
431
432                if (engine->m_lastListDir != path)
433                        continue;
434
435                if (engine->m_lastListTime && changeTime <= engine->m_lastListTime)
436                        continue;
437
438                engine->m_lastListTime = changeTime;
439                engine->AddNotification(new CDirectoryListingNotification(path, true));
440        }
441}
442
443void CFileZillaEnginePrivate::RegisterFailedLoginAttempt(const CServer& server, bool critical)
444{
445        scoped_lock lock(mutex_);
446        std::list<t_failedLogins>::iterator iter = m_failedLogins.begin();
447        while (iter != m_failedLogins.end()) {
448                duration const span = CDateTime::Now() - iter->time;
449                if (span.get_seconds() >= m_options.GetOptionVal(OPTION_RECONNECTDELAY) ||
450                        iter->server == server || (!critical && (iter->server.GetHost() == server.GetHost() && iter->server.GetPort() == server.GetPort())))
451                {
452                        std::list<t_failedLogins>::iterator prev = iter;
453                        ++iter;
454                        m_failedLogins.erase(prev);
455                }
456                else
457                        ++iter;
458        }
459
460        t_failedLogins failure;
461        failure.server = server;
462        failure.time = CDateTime::Now();
463        failure.critical = critical;
464        m_failedLogins.push_back(failure);
465}
466
467unsigned int CFileZillaEnginePrivate::GetRemainingReconnectDelay(const CServer& server)
468{
469        scoped_lock lock(mutex_);
470        std::list<t_failedLogins>::iterator iter = m_failedLogins.begin();
471        while (iter != m_failedLogins.end()) {
472                duration const span = CDateTime::Now() - iter->time;
473                const int delay = m_options.GetOptionVal(OPTION_RECONNECTDELAY);
474                if (span.get_seconds() >= delay) {
475                        std::list<t_failedLogins>::iterator prev = iter;
476                        ++iter;
477                        m_failedLogins.erase(prev);
478                }
479                else if (!iter->critical && iter->server.GetHost() == server.GetHost() && iter->server.GetPort() == server.GetPort())
480                        return delay * 1000 - span.get_milliseconds();
481                else if (iter->server == server)
482                        return delay * 1000 - span.get_milliseconds();
483                else
484                        ++iter;
485        }
486
487        return 0;
488}
489
490void CFileZillaEnginePrivate::OnTimer(int)
491{
492        if (!m_retryTimer) {
493                return;
494        }
495        m_retryTimer = 0;
496
497        if (!m_pCurrentCommand || m_pCurrentCommand->GetId() != Command::connect) {
498                wxFAIL_MSG(_T("CFileZillaEnginePrivate::OnTimer called without pending Command::connect"));
499                return;
500        }
501        wxASSERT(!IsConnected());
502
503        m_pControlSocket.reset();
504
505        ContinueConnect();
506}
507
508int CFileZillaEnginePrivate::ContinueConnect()
509{
510        scoped_lock lock(mutex_);
511
512        if (!m_pCurrentCommand || m_pCurrentCommand->GetId() != Command::connect) {
513                m_pLogging->LogMessage(MessageType::Debug_Warning, _T("CFileZillaEnginePrivate::ContinueConnect called without pending Command::connect"));
514                return ResetOperation(FZ_REPLY_INTERNALERROR);
515        }
516
517        const CConnectCommand *pConnectCommand = static_cast<CConnectCommand *>(m_pCurrentCommand.get());
518        const CServer& server = pConnectCommand->GetServer();
519        unsigned int delay = GetRemainingReconnectDelay(server);
520        if (delay) {
521                m_pLogging->LogMessage(MessageType::Status, wxPLURAL("Delaying connection for %d second due to previously failed connection attempt...", "Delaying connection for %d seconds due to previously failed connection attempt...", (delay + 999) / 1000), (delay + 999) / 1000);
522                StopTimer(m_retryTimer);
523                m_retryTimer = AddTimer(duration::from_milliseconds(delay), true);
524                return FZ_REPLY_WOULDBLOCK;
525        }
526
527        switch (server.GetProtocol())
528        {
529        case FTP:
530        case FTPS:
531        case FTPES:
532        case INSECURE_FTP:
533                m_pControlSocket = std::make_unique<CFtpControlSocket>(*this);
534                break;
535        case SFTP:
536                m_pControlSocket = std::make_unique<CSftpControlSocket>(*this);
537                break;
538        case HTTP:
539        case HTTPS:
540                m_pControlSocket = std::make_unique<CHttpControlSocket>(*this);
541                break;
542        default:
543                m_pLogging->LogMessage(MessageType::Debug_Warning, _T("Not a valid protocol: %d"), server.GetProtocol());
544                return FZ_REPLY_SYNTAXERROR|FZ_REPLY_DISCONNECTED;
545        }
546
547        int res = m_pControlSocket->Connect(server);
548        if (m_retryTimer)
549                return FZ_REPLY_WOULDBLOCK;
550
551        return res;
552}
553
554void CFileZillaEnginePrivate::InvalidateCurrentWorkingDirs(const CServerPath& path)
555{
556        scoped_lock lock(mutex_);
557
558        wxASSERT(m_pControlSocket);
559        const CServer* const pOwnServer = m_pControlSocket->GetCurrentServer();
560        wxASSERT(pOwnServer);
561
562        for (auto & engine : m_engineList) {
563                if (engine == this)
564                        continue;
565
566                if (!engine->m_pControlSocket)
567                        continue;
568
569                const CServer* const pServer = engine->m_pControlSocket->GetCurrentServer();
570                if (!pServer || *pServer != *pOwnServer)
571                        continue;
572
573                engine->m_pControlSocket->InvalidateCurrentWorkingDir(path);
574        }
575}
576
577void CFileZillaEnginePrivate::operator()(CEventBase const& ev)
578{
579        scoped_lock lock(mutex_);
580
581        Dispatch<CFileZillaEngineEvent, CCommandEvent, CAsyncRequestReplyEvent, CTimerEvent>(ev, this,
582                &CFileZillaEnginePrivate::OnEngineEvent,
583                &CFileZillaEnginePrivate::OnCommandEvent,
584                &CFileZillaEnginePrivate::OnSetAsyncRequestReplyEvent,
585                &CFileZillaEnginePrivate::OnTimer
586                );
587}
588
589int CFileZillaEnginePrivate::CheckCommandPreconditions(CCommand const& command, bool checkBusy)
590{
591        if (!command.valid()) {
592                return FZ_REPLY_SYNTAXERROR;
593        }
594        else if (checkBusy && IsBusy()) {
595                return FZ_REPLY_BUSY;
596        }
597        else if (command.GetId() != Command::connect && command.GetId() != Command::disconnect && !IsConnected()) {
598                return FZ_REPLY_NOTCONNECTED;
599        }
600        else if (command.GetId() == Command::connect && m_pControlSocket) {
601                return FZ_REPLY_ALREADYCONNECTED;
602        }
603        return FZ_REPLY_OK;
604}
605
606void CFileZillaEnginePrivate::OnCommandEvent()
607{
608        scoped_lock lock(mutex_);
609
610        if (m_pCurrentCommand) {
611                CCommand & command = *m_pCurrentCommand;
612                Command id = command.GetId();
613
614                int res = CheckCommandPreconditions(command, false);
615                if (res == FZ_REPLY_OK) {
616                        switch (command.GetId())
617                        {
618                        case Command::connect:
619                                res = Connect(static_cast<CConnectCommand const&>(command));
620                                break;
621                        case Command::disconnect:
622                                res = Disconnect(static_cast<CDisconnectCommand const&>(command));
623                                break;
624                        case Command::list:
625                                res = List(static_cast<CListCommand const&>(command));
626                                break;
627                        case Command::transfer:
628                                res = FileTransfer(static_cast<CFileTransferCommand const&>(command));
629                                break;
630                        case Command::raw:
631                                res = RawCommand(static_cast<CRawCommand const&>(command));
632                                break;
633                        case Command::del:
634                                res = Delete(static_cast<CDeleteCommand &>(command));
635                                break;
636                        case Command::removedir:
637                                res = RemoveDir(static_cast<CRemoveDirCommand const&>(command));
638                                break;
639                        case Command::mkdir:
640                                res = Mkdir(static_cast<CMkdirCommand const&>(command));
641                                break;
642                        case Command::rename:
643                                res = Rename(static_cast<CRenameCommand const&>(command));
644                                break;
645                        case Command::chmod:
646                                res = Chmod(static_cast<CChmodCommand const&>(command));
647                                break;
648                        default:
649                                res = FZ_REPLY_SYNTAXERROR;
650                        }
651                }
652
653                if (id != Command::disconnect)
654                        res |= m_nControlSocketError;
655                else if (res & FZ_REPLY_DISCONNECTED)
656                        res = FZ_REPLY_OK;
657                m_nControlSocketError = 0;
658
659                if (res != FZ_REPLY_WOULDBLOCK)
660                        ResetOperation(res);
661        }
662}
663
664void CFileZillaEnginePrivate::DoCancel()
665{
666        scoped_lock lock(mutex_);
667        if (!IsBusy())
668                return;
669
670        if (m_retryTimer) {
671                wxASSERT(m_pCurrentCommand && m_pCurrentCommand->GetId() == Command::connect);
672
673                m_pControlSocket.reset();
674
675                m_pCurrentCommand.reset();
676
677                StopTimer(m_retryTimer);
678                m_retryTimer = 0;
679
680                m_pLogging->LogMessage(MessageType::Error, _("Connection attempt interrupted by user"));
681                COperationNotification *notification = new COperationNotification();
682                notification->nReplyCode = FZ_REPLY_DISCONNECTED | FZ_REPLY_CANCELED;
683                notification->commandId = Command::connect;
684                AddNotification(notification);
685
686                ClearQueuedLogs(true);
687        }
688        else {
689                if (m_pControlSocket)
690                        m_pControlSocket->Cancel();
691                else
692                        ResetOperation(FZ_REPLY_CANCELED);
693        }
694}
695
696bool CFileZillaEnginePrivate::CheckAsyncRequestReplyPreconditions(std::unique_ptr<CAsyncRequestNotification> const& reply)
697{
698        if (!reply)
699                return false;
700        if (!IsBusy())
701                return false;
702
703        bool match;
704        {
705                scoped_lock l(notification_mutex_);
706                match = reply->requestNumber == m_asyncRequestCounter;
707        }
708
709        return match && m_pControlSocket;
710}
711
712void CFileZillaEnginePrivate::OnSetAsyncRequestReplyEvent(std::unique_ptr<CAsyncRequestNotification> const& reply)
713{
714        scoped_lock lock(mutex_);
715        if (!CheckAsyncRequestReplyPreconditions(reply)) {
716                return;
717        }
718
719        m_pControlSocket->SetAlive();
720        m_pControlSocket->SetAsyncRequestReply(reply.get());
721}
722
723int CFileZillaEnginePrivate::Init(wxEvtHandler *pEventHandler)
724{
725        scoped_lock lock(mutex_);
726        m_pEventHandler = pEventHandler;
727        return FZ_REPLY_OK;
728}
729
730int CFileZillaEnginePrivate::Execute(const CCommand &command)
731{
732        scoped_lock lock(mutex_);
733
734        int res = CheckCommandPreconditions(command, true);
735        if (res != FZ_REPLY_OK) {
736                return res;
737        }
738
739        m_pCurrentCommand.reset(command.Clone());
740        SendEvent<CCommandEvent>();
741
742        return FZ_REPLY_WOULDBLOCK;
743}
744
745std::unique_ptr<CNotification> CFileZillaEnginePrivate::GetNextNotification()
746{
747        scoped_lock lock(notification_mutex_);
748
749        if (m_NotificationList.empty()) {
750                m_maySendNotificationEvent = true;
751                return 0;
752        }
753        std::unique_ptr<CNotification> pNotification(m_NotificationList.front());
754        m_NotificationList.pop_front();
755
756        return pNotification;
757}
758
759bool CFileZillaEnginePrivate::SetAsyncRequestReply(std::unique_ptr<CAsyncRequestNotification> && pNotification)
760{
761        scoped_lock lock(mutex_);
762        if (!CheckAsyncRequestReplyPreconditions(pNotification)) {
763                return false;
764        }
765
766        SendEvent<CAsyncRequestReplyEvent>(std::move(pNotification));
767
768        return true;
769}
770
771bool CFileZillaEnginePrivate::IsPendingAsyncRequestReply(std::unique_ptr<CAsyncRequestNotification> const& pNotification)
772{
773        if (!pNotification)
774                return false;
775
776        if (!IsBusy())
777                return false;
778
779        scoped_lock lock(notification_mutex_);
780        return pNotification->requestNumber == m_asyncRequestCounter;
781}
782
783void CFileZillaEnginePrivate::SetActive(int direction)
784{
785        int const old_status = m_activeStatus[direction].fetch_or(0x1);
786        if (!old_status) {
787                AddNotification(new CActiveNotification(direction));
788        }
789}
790
791bool CFileZillaEnginePrivate::IsActive(CFileZillaEngine::_direction direction)
792{
793        int const old = m_activeStatus[direction].exchange(0x2);
794        if (!(old & 0x1)) {
795                // Race: We might lose updates between the first exchange and this assignment.
796                // It is harmless though
797                m_activeStatus[direction] = 0;
798                return false;
799        }
800        return true;
801}
802
803CTransferStatus CFileZillaEnginePrivate::GetTransferStatus(bool &changed)
804{
805        return transfer_status_.Get(changed);
806}
807
808int CFileZillaEnginePrivate::CacheLookup(const CServerPath& path, CDirectoryListing& listing)
809{
810        // TODO: Possible optimization: Atomically get current server. The cache has its own mutex.
811        scoped_lock lock(mutex_);
812
813        if (!IsConnected())
814                return FZ_REPLY_ERROR;
815
816        wxASSERT(m_pControlSocket->GetCurrentServer());
817
818        bool is_outdated = false;
819        if (!directory_cache_.Lookup(listing, *m_pControlSocket->GetCurrentServer(), path, true, is_outdated))
820                return FZ_REPLY_ERROR;
821
822        return FZ_REPLY_OK;
823}
824
825int CFileZillaEnginePrivate::Cancel()
826{
827        scoped_lock lock(mutex_);
828        if (!IsBusy())
829                return FZ_REPLY_OK;
830
831        SendEvent<CFileZillaEngineEvent>(engineCancel);
832        return FZ_REPLY_WOULDBLOCK;
833}
834
835void CFileZillaEnginePrivate::OnOptionsChanged(changed_options_t const&)
836{
837        bool queue_logs = ShouldQueueLogsFromOptions();
838        scoped_lock lock(notification_mutex_);
839        queue_logs_ = queue_logs;
840
841        if (!queue_logs_) {
842                SendQueuedLogs();
843        }
844}
845
846
847CTransferStatusManager::CTransferStatusManager(CFileZillaEnginePrivate& engine)
848        : engine_(engine)
849{
850}
851
852void CTransferStatusManager::Reset()
853{
854        {
855                scoped_lock lock(mutex_);
856                status_.clear();
857                send_state_ = 0;
858        }
859
860        engine_.AddNotification(new CTransferStatusNotification());
861}
862
863void CTransferStatusManager::Init(wxFileOffset totalSize, wxFileOffset startOffset, bool list)
864{
865        scoped_lock lock(mutex_);
866        if (startOffset < 0)
867                startOffset = 0;
868
869        status_ = CTransferStatus(totalSize, startOffset, list);
870        currentOffset_ = 0;
871}
872
873void CTransferStatusManager::SetStartTime()
874{
875        scoped_lock lock(mutex_);
876        if (!status_)
877                return;
878
879        status_.started = CDateTime::Now();
880}
881
882void CTransferStatusManager::SetMadeProgress()
883{
884        scoped_lock lock(mutex_);
885        if (!status_)
886                return;
887
888        status_.madeProgress = true;
889}
890
891void CTransferStatusManager::Update(wxFileOffset transferredBytes)
892{
893        CNotification* notification = 0;
894
895        {
896                int64_t oldOffset = currentOffset_.fetch_add(transferredBytes);
897                if (!oldOffset) {
898                        scoped_lock lock(mutex_);
899                        if (!status_) {
900                                return;
901                        }
902
903                        if (!send_state_) {
904                                status_.currentOffset += currentOffset_.exchange(0);
905                                notification = new CTransferStatusNotification(status_);
906                        }
907                        send_state_ = 2;
908                }
909        }
910
911        if (notification) {
912                engine_.AddNotification(notification);
913        }
914}
915
916CTransferStatus CTransferStatusManager::Get(bool &changed)
917{
918        scoped_lock lock(mutex_);
919        if (!status_) {
920                changed = false;
921                send_state_ = 0;
922        }
923        else {
924                status_.currentOffset += currentOffset_.exchange(0);
925                if (send_state_ == 2) {
926                        changed = true;
927                        send_state_ = 1;
928                }
929                else {
930                        changed = false;
931                        send_state_ = 0;
932                }
933        }
934        return status_;
935}
936
937bool CTransferStatusManager::empty()
938{
939        scoped_lock lock(mutex_);
940        return status_.empty();
941}
Note: See TracBrowser for help on using the repository browser.