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

Last change on this file since 3185 was 3185, checked in by jrpelegrina, 2 years ago

Update new version: 3.15.02

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