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

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

First release to xenial

File size: 26.0 KB
Line 
1#include <filezilla.h>
2
3#include "ControlSocket.h"
4#include "engineprivate.h"
5#include "httpcontrolsocket.h"
6#include "local_filesys.h"
7#include "tlssocket.h"
8
9#include <wx/file.h>
10
11#define FZ_REPLY_REDIRECTED FZ_REPLY_ALREADYCONNECTED
12
13// Connect is special for HTTP: It is done on a per-command basis, so we need
14// to establish a connection before each command.
15class CHttpConnectOpData : public CConnectOpData
16{
17public:
18        CHttpConnectOpData()
19                : tls(false)
20        {
21        }
22
23        virtual ~CHttpConnectOpData()
24        {
25        }
26
27        bool tls;
28};
29
30class CHttpOpData
31{
32public:
33        CHttpOpData(COpData* pOpData)
34                : m_pOpData(pOpData)
35        {
36        }
37
38        virtual ~CHttpOpData() = default;
39
40        bool m_gotHeader{};
41        int m_responseCode{-1};
42        wxString m_responseString;
43        wxURI m_newLocation;
44        int m_redirectionCount{};
45
46        int64_t m_totalSize{-1};
47        int64_t m_receivedData{};
48
49        COpData* m_pOpData;
50
51        enum transferEncodings
52        {
53                identity,
54                chunked,
55                unknown
56        };
57        transferEncodings m_transferEncoding{unknown};
58
59        struct t_chunkData
60        {
61                bool getTrailer{};
62                bool terminateChunk{};
63                int64_t size{};
64        } m_chunkData;
65};
66
67class CHttpFileTransferOpData : public CFileTransferOpData, public CHttpOpData
68{
69public:
70        CHttpFileTransferOpData(bool is_download, const wxString& local_file, const wxString& remote_file, const CServerPath& remote_path)
71                : CFileTransferOpData(is_download, local_file, remote_file, remote_path), CHttpOpData(this)
72        {
73                pFile = 0;
74        }
75
76        virtual ~CHttpFileTransferOpData()
77        {
78                delete pFile;
79        }
80
81        wxFile* pFile;
82};
83
84CHttpControlSocket::CHttpControlSocket(CFileZillaEnginePrivate & engine)
85        : CRealControlSocket(engine)
86{
87        m_pRecvBuffer = 0;
88        m_recvBufferPos = 0;
89        m_pTlsSocket = 0;
90        m_pHttpOpData = 0;
91}
92
93CHttpControlSocket::~CHttpControlSocket()
94{
95        RemoveHandler();
96        DoClose();
97        delete [] m_pRecvBuffer;
98}
99
100int CHttpControlSocket::SendNextCommand()
101{
102        LogMessage(MessageType::Debug_Verbose, _T("CHttpControlSocket::SendNextCommand()"));
103        if (!m_pCurOpData)
104        {
105                LogMessage(__TFILE__, __LINE__, this, MessageType::Debug_Warning, _T("SendNextCommand called without active operation"));
106                ResetOperation(FZ_REPLY_ERROR);
107                return FZ_REPLY_ERROR;
108        }
109
110        if (m_pCurOpData->waitForAsyncRequest)
111        {
112                LogMessage(__TFILE__, __LINE__, this, MessageType::Debug_Info, _T("Waiting for async request, ignoring SendNextCommand"));
113                return FZ_REPLY_WOULDBLOCK;
114        }
115
116        switch (m_pCurOpData->opId)
117        {
118        case Command::transfer:
119                return FileTransferSend();
120        default:
121                LogMessage(__TFILE__, __LINE__, this, MessageType::Debug_Warning, _T("Unknown opID (%d) in SendNextCommand"), m_pCurOpData->opId);
122                ResetOperation(FZ_REPLY_INTERNALERROR);
123                break;
124        }
125
126        return FZ_REPLY_ERROR;
127}
128
129
130int CHttpControlSocket::ContinueConnect()
131{
132        LogMessage(__TFILE__, __LINE__, this, MessageType::Debug_Verbose, _T("CHttpControlSocket::ContinueConnect() &engine_=%p"), &engine_);
133        if (GetCurrentCommandId() != Command::connect ||
134                !m_pCurrentServer)
135        {
136                LogMessage(MessageType::Debug_Warning, _T("Invalid context for call to ContinueConnect(), cmd=%d, m_pCurrentServer=%p"), GetCurrentCommandId(), m_pCurrentServer);
137                return DoClose(FZ_REPLY_INTERNALERROR);
138        }
139
140        ResetOperation(FZ_REPLY_OK);
141        return FZ_REPLY_OK;
142}
143
144bool CHttpControlSocket::SetAsyncRequestReply(CAsyncRequestNotification *pNotification)
145{
146        if (m_pCurOpData)
147        {
148                if (!m_pCurOpData->waitForAsyncRequest)
149                {
150                        LogMessage(__TFILE__, __LINE__, this, MessageType::Debug_Info, _T("Not waiting for request reply, ignoring request reply %d"), pNotification->GetRequestID());
151                        return false;
152                }
153                m_pCurOpData->waitForAsyncRequest = false;
154        }
155
156        switch (pNotification->GetRequestID())
157        {
158        case reqId_fileexists:
159                {
160                        if (!m_pCurOpData || m_pCurOpData->opId != Command::transfer)
161                        {
162                                LogMessage(__TFILE__, __LINE__, this, MessageType::Debug_Info, _T("No or invalid operation in progress, ignoring request reply %f"), pNotification->GetRequestID());
163                                return false;
164                        }
165
166                        CFileExistsNotification *pFileExistsNotification = static_cast<CFileExistsNotification *>(pNotification);
167                        return SetFileExistsAction(pFileExistsNotification);
168                }
169                break;
170        case reqId_certificate:
171                {
172                        if (!m_pTlsSocket || m_pTlsSocket->GetState() != CTlsSocket::TlsState::verifycert)
173                        {
174                                LogMessage(__TFILE__, __LINE__, this, MessageType::Debug_Info, _T("No or invalid operation in progress, ignoring request reply %d"), pNotification->GetRequestID());
175                                return false;
176                        }
177
178                        CCertificateNotification* pCertificateNotification = static_cast<CCertificateNotification *>(pNotification);
179                        m_pTlsSocket->TrustCurrentCert(pCertificateNotification->m_trusted);
180                }
181                break;
182        default:
183                LogMessage(__TFILE__, __LINE__, this, MessageType::Debug_Warning, _T("Unknown request %d"), pNotification->GetRequestID());
184                ResetOperation(FZ_REPLY_INTERNALERROR);
185                return false;
186        }
187
188        return true;
189}
190
191void CHttpControlSocket::OnReceive()
192{
193        DoReceive();
194}
195
196int CHttpControlSocket::DoReceive()
197{
198        do
199        {
200                const enum CSocket::SocketState state = m_pSocket->GetState();
201                if (state != CSocket::connected && state != CSocket::closing)
202                        return 0;
203
204                if (!m_pRecvBuffer)
205                {
206                        m_pRecvBuffer = new char[m_recvBufferLen];
207                        m_recvBufferPos = 0;
208                }
209
210                unsigned int len = m_recvBufferLen - m_recvBufferPos;
211                int error;
212                int read = m_pBackend->Read(m_pRecvBuffer + m_recvBufferPos, len, error);
213                if (read == -1)
214                {
215                        if (error != EAGAIN)
216                        {
217                                ResetOperation(FZ_REPLY_ERROR | FZ_REPLY_DISCONNECTED);
218                        }
219                        return 0;
220                }
221
222                SetActive(CFileZillaEngine::recv);
223
224                if (!m_pCurOpData || m_pCurOpData->opId == Command::connect) {
225                        // Just ignore all further data
226                        m_recvBufferPos = 0;
227                        return 0;
228                }
229
230                m_recvBufferPos += read;
231
232                if (!m_pHttpOpData->m_gotHeader) {
233                        if (!read)
234                        {
235                                ResetOperation(FZ_REPLY_ERROR | FZ_REPLY_DISCONNECTED);
236                                return 0;
237                        }
238
239                        int res = ParseHeader(m_pHttpOpData);
240                        if ((res & FZ_REPLY_REDIRECTED) == FZ_REPLY_REDIRECTED)
241                                return FZ_REPLY_REDIRECTED;
242                        if (res != FZ_REPLY_WOULDBLOCK)
243                                return 0;
244                }
245                else if (m_pHttpOpData->m_transferEncoding == CHttpOpData::chunked)
246                {
247                        if (!read)
248                        {
249                                ResetOperation(FZ_REPLY_ERROR | FZ_REPLY_DISCONNECTED);
250                                return 0;
251                        }
252                        OnChunkedData(m_pHttpOpData);
253                }
254                else
255                {
256                        if (!read)
257                        {
258                                wxASSERT(!m_recvBufferPos);
259                                ProcessData(0, 0);
260                                return 0;
261                        }
262                        else
263                        {
264                                m_pHttpOpData->m_receivedData += m_recvBufferPos;
265                                ProcessData(m_pRecvBuffer, m_recvBufferPos);
266                                m_recvBufferPos = 0;
267                        }
268                }
269        }
270        while (m_pSocket);
271
272        return 0;
273}
274
275void CHttpControlSocket::OnConnect()
276{
277        wxASSERT(GetCurrentCommandId() == Command::connect);
278
279        CHttpConnectOpData *pData = static_cast<CHttpConnectOpData *>(m_pCurOpData);
280
281        if (pData->tls) {
282                if (!m_pTlsSocket) {
283                        LogMessage(MessageType::Status, _("Connection established, initializing TLS..."));
284
285                        delete m_pBackend;
286                        m_pTlsSocket = new CTlsSocket(this, *m_pSocket, this);
287                        m_pBackend = m_pTlsSocket;
288
289                        if (!m_pTlsSocket->Init()) {
290                                LogMessage(MessageType::Error, _("Failed to initialize TLS."));
291                                DoClose();
292                                return;
293                        }
294
295                        int res = m_pTlsSocket->Handshake();
296                        if (res == FZ_REPLY_ERROR)
297                                DoClose();
298                }
299                else {
300                        LogMessage(MessageType::Status, _("TLS connection established, sending HTTP request"));
301                        ResetOperation(FZ_REPLY_OK);
302                }
303
304                return;
305        }
306        else
307        {
308                LogMessage(MessageType::Status, _("Connection established, sending HTTP request"));
309                ResetOperation(FZ_REPLY_OK);
310        }
311}
312
313enum filetransferStates
314{
315        filetransfer_init = 0,
316        filetransfer_waitfileexists,
317        filetransfer_transfer
318};
319
320int CHttpControlSocket::FileTransfer(const wxString localFile, const CServerPath &remotePath,
321                                                          const wxString &remoteFile, bool download,
322                                                          const CFileTransferCommand::t_transferSettings&)
323{
324        LogMessage(MessageType::Debug_Verbose, _T("CHttpControlSocket::FileTransfer()"));
325
326        LogMessage(MessageType::Status, _("Downloading %s"), remotePath.FormatFilename(remoteFile));
327
328        if (!download)
329        {
330                ResetOperation(FZ_REPLY_CRITICALERROR | FZ_REPLY_NOTSUPPORTED);
331                return FZ_REPLY_ERROR;
332        }
333
334        if (m_pCurOpData)
335        {
336                LogMessage(__TFILE__, __LINE__, this, MessageType::Debug_Info, _T("deleting nonzero pData"));
337                delete m_pCurOpData;
338        }
339
340        CHttpFileTransferOpData *pData = new CHttpFileTransferOpData(download, localFile, remoteFile, remotePath);
341        m_pCurOpData = pData;
342        m_pHttpOpData = pData;
343
344        m_current_uri = wxURI(m_pCurrentServer->FormatServer() + pData->remotePath.FormatFilename(pData->remoteFile));
345
346        if (!localFile.empty()) {
347                pData->localFileSize = CLocalFileSystem::GetSize(pData->localFile);
348
349                pData->opState = filetransfer_waitfileexists;
350                int res = CheckOverwriteFile();
351                if (res != FZ_REPLY_OK)
352                        return res;
353
354                pData->opState = filetransfer_transfer;
355
356                res = OpenFile(pData);
357                if( res != FZ_REPLY_OK )
358                        return res;
359        }
360        else
361                pData->opState = filetransfer_transfer;
362
363        int res = InternalConnect(m_pCurrentServer->GetHost(), m_pCurrentServer->GetPort(), m_pCurrentServer->GetProtocol() == HTTPS);
364        if (res != FZ_REPLY_OK)
365                return res;
366
367        return FileTransferSend();
368}
369
370int CHttpControlSocket::FileTransferSubcommandResult(int prevResult)
371{
372        LogMessage(MessageType::Debug_Verbose, _T("CHttpControlSocket::FileTransferSubcommandResult(%d)"), prevResult);
373
374        if (!m_pCurOpData)
375        {
376                LogMessage(__TFILE__, __LINE__, this, MessageType::Debug_Info, _T("Empty m_pCurOpData"));
377                ResetOperation(FZ_REPLY_INTERNALERROR);
378                return FZ_REPLY_ERROR;
379        }
380
381        if (prevResult != FZ_REPLY_OK)
382        {
383                ResetOperation(prevResult);
384                return FZ_REPLY_ERROR;
385        }
386
387        return FileTransferSend();
388}
389
390int CHttpControlSocket::FileTransferSend()
391{
392        LogMessage(MessageType::Debug_Verbose, _T("CHttpControlSocket::FileTransferSend()"));
393
394        if (!m_pCurOpData)
395        {
396                LogMessage(__TFILE__, __LINE__, this, MessageType::Debug_Info, _T("Empty m_pCurOpData"));
397                ResetOperation(FZ_REPLY_INTERNALERROR);
398                return FZ_REPLY_ERROR;
399        }
400
401        if( !m_current_uri.HasScheme() || !m_current_uri.HasServer() || !m_current_uri.HasPath() ) {
402                LogMessage(__TFILE__, __LINE__, this, MessageType::Debug_Warning, _T("Invalid URI: %s"), m_current_uri.BuildURI());
403                ResetOperation(FZ_REPLY_INTERNALERROR);
404                return FZ_REPLY_ERROR;
405        }
406
407        CHttpFileTransferOpData *pData = static_cast<CHttpFileTransferOpData *>(m_pCurOpData);
408
409        if (pData->opState == filetransfer_waitfileexists)
410        {
411                pData->opState = filetransfer_transfer;
412
413                int res = OpenFile(pData);
414                if( res != FZ_REPLY_OK)
415                        return res;
416
417                res = InternalConnect(m_pCurrentServer->GetHost(), m_pCurrentServer->GetPort(), m_pCurrentServer->GetProtocol() == HTTPS);
418                if (res != FZ_REPLY_OK)
419                        return res;
420        }
421
422        wxString location = m_current_uri.GetPath();
423        if( m_current_uri.HasQuery() ) {
424                location += _T("?") + m_current_uri.GetQuery();
425        }
426        wxString action = wxString::Format(_T("GET %s HTTP/1.1"), location );
427        LogMessageRaw(MessageType::Command, action);
428
429        wxString hostWithPort = m_current_uri.GetServer();
430        if( m_current_uri.HasPort() ) {
431                hostWithPort += _T(":") + m_current_uri.GetPort();
432        }
433        wxString command = wxString::Format(_T("%s\r\nHost: %s\r\nUser-Agent: %s\r\nConnection: close\r\n"), action, hostWithPort, wxString(PACKAGE_STRING, wxConvLocal));
434        if( pData->resume ) {
435                command += wxString::Format(_T("Range: bytes=%") + wxString(wxFileOffsetFmtSpec) + _T("d-\r\n"), pData->localFileSize);
436        }
437        command += _T("\r\n");
438
439        const wxWX2MBbuf str = command.mb_str();
440        if (!Send(str, strlen(str)))
441                return FZ_REPLY_ERROR;
442
443        return FZ_REPLY_WOULDBLOCK;
444}
445
446int CHttpControlSocket::InternalConnect(wxString host, unsigned short port, bool tls)
447{
448        LogMessage(MessageType::Debug_Verbose, _T("CHttpControlSocket::InternalConnect()"));
449
450        CHttpConnectOpData* pData = new CHttpConnectOpData;
451        pData->pNextOpData = m_pCurOpData;
452        m_pCurOpData = pData;
453        pData->port = port;
454        pData->tls = tls;
455
456        if (!IsIpAddress(host))
457                LogMessage(MessageType::Status, _("Resolving address of %s"), host);
458
459        pData->host = host;
460        return DoInternalConnect();
461}
462
463int CHttpControlSocket::DoInternalConnect()
464{
465        LogMessage(MessageType::Debug_Verbose, _T("CHttpControlSocket::DoInternalConnect()"));
466
467        if (!m_pCurOpData)
468        {
469                LogMessage(__TFILE__, __LINE__, this, MessageType::Debug_Info, _T("Empty m_pCurOpData"));
470                ResetOperation(FZ_REPLY_INTERNALERROR);
471                return FZ_REPLY_ERROR;
472        }
473
474        CHttpConnectOpData *pData = static_cast<CHttpConnectOpData *>(m_pCurOpData);
475
476        delete m_pBackend;
477        m_pBackend = new CSocketBackend(this, *m_pSocket, engine_.GetRateLimiter());
478
479        int res = m_pSocket->Connect(pData->host, pData->port);
480        if (!res)
481                return FZ_REPLY_OK;
482
483        if (res && res != EINPROGRESS)
484                return ResetOperation(FZ_REPLY_ERROR);
485
486        return FZ_REPLY_WOULDBLOCK;
487}
488
489int CHttpControlSocket::FileTransferParseResponse(char* p, unsigned int len)
490{
491        LogMessage(MessageType::Debug_Verbose, _T("CHttpControlSocket::FileTransferParseResponse(%p, %d)"), p, len);
492
493        if (!m_pCurOpData) {
494                LogMessage(__TFILE__, __LINE__, this, MessageType::Debug_Info, _T("Empty m_pCurOpData"));
495                ResetOperation(FZ_REPLY_INTERNALERROR);
496                return FZ_REPLY_ERROR;
497        }
498
499        CHttpFileTransferOpData *pData = static_cast<CHttpFileTransferOpData *>(m_pCurOpData);
500
501        if (!p) {
502                ResetOperation(FZ_REPLY_OK);
503                return FZ_REPLY_OK;
504        }
505
506        if (engine_.transfer_status_.empty()) {
507                engine_.transfer_status_.Init(pData->m_totalSize, 0, false);
508                engine_.transfer_status_.SetStartTime();
509        }
510
511        if (pData->localFile.empty()) {
512                char* q = new char[len];
513                memcpy(q, p, len);
514                engine_.AddNotification(new CDataNotification(q, len));
515        }
516        else {
517                wxASSERT(pData->pFile);
518
519                if (pData->pFile->Write(p, len) != len) {
520                        LogMessage(MessageType::Error, _("Failed to write to file %s"), pData->localFile);
521                        ResetOperation(FZ_REPLY_ERROR);
522                        return FZ_REPLY_ERROR;
523                }
524        }
525
526        engine_.transfer_status_.Update(len);
527
528        return FZ_REPLY_WOULDBLOCK;
529}
530
531int CHttpControlSocket::ParseHeader(CHttpOpData* pData)
532{
533        // Parse the HTTP header.
534        // We do just the neccessary parsing and silently ignore most header fields
535        // Redirects are supported though if the server sends the Location field.
536
537        for (;;) {
538                // Find line ending
539                unsigned int i = 0;
540                for (i = 0; (i + 1) < m_recvBufferPos; i++)
541                {
542                        if (m_pRecvBuffer[i] == '\r')
543                        {
544                                if (m_pRecvBuffer[i + 1] != '\n')
545                                {
546                                        LogMessage(MessageType::Error, _("Malformed reply, server not sending proper line endings"));
547                                        ResetOperation(FZ_REPLY_ERROR);
548                                        return FZ_REPLY_ERROR;
549                                }
550                                break;
551                        }
552                }
553                if ((i + 1) >= m_recvBufferPos)
554                {
555                        if (m_recvBufferPos == m_recvBufferLen)
556                        {
557                                // We don't support header lines larger than 4096
558                                LogMessage(MessageType::Error, _("Too long header line"));
559                                ResetOperation(FZ_REPLY_ERROR);
560                                return FZ_REPLY_ERROR;
561                        }
562                        return FZ_REPLY_WOULDBLOCK;
563                }
564
565                m_pRecvBuffer[i] = 0;
566                wxString const line = wxString(m_pRecvBuffer, wxConvLocal);
567                if (!line.empty())
568                        LogMessageRaw(MessageType::Response, line);
569
570                if (pData->m_responseCode == -1)
571                {
572                        pData->m_responseString = line;
573                        if (m_recvBufferPos < 16 || memcmp(m_pRecvBuffer, "HTTP/1.", 7))
574                        {
575                                // Invalid HTTP Status-Line
576                                LogMessage(MessageType::Error, _("Invalid HTTP Response"));
577                                ResetOperation(FZ_REPLY_ERROR);
578                                return FZ_REPLY_ERROR;
579                        }
580
581                        if (m_pRecvBuffer[9] < '1' || m_pRecvBuffer[9] > '5' ||
582                                m_pRecvBuffer[10] < '0' || m_pRecvBuffer[10] > '9' ||
583                                m_pRecvBuffer[11] < '0' || m_pRecvBuffer[11] > '9')
584                        {
585                                // Invalid response code
586                                LogMessage(MessageType::Error, _("Invalid response code"));
587                                ResetOperation(FZ_REPLY_ERROR);
588                                return FZ_REPLY_ERROR;
589                        }
590
591                        pData->m_responseCode = (m_pRecvBuffer[9] - '0') * 100 + (m_pRecvBuffer[10] - '0') * 10 + m_pRecvBuffer[11] - '0';
592
593                        if( pData->m_responseCode == 416 ) {
594                                CHttpFileTransferOpData* pTransfer = static_cast<CHttpFileTransferOpData*>(pData->m_pOpData);
595                                if( pTransfer->resume ) {
596                                        // Sad, the server does not like our attempt to resume.
597                                        // Get full file instead.
598                                        pTransfer->resume = false;
599                                        int res = OpenFile(pTransfer);
600                                        if( res != FZ_REPLY_OK ) {
601                                                return res;
602                                        }
603                                        pData->m_newLocation = m_current_uri;
604                                        pData->m_responseCode = 300;
605                                }
606                        }
607
608                        if (pData->m_responseCode >= 400)
609                        {
610                                // Failed request
611                                ResetOperation(FZ_REPLY_ERROR);
612                                return FZ_REPLY_ERROR;
613                        }
614
615                        if (pData->m_responseCode == 305)
616                        {
617                                // Unsupported redirect
618                                LogMessage(MessageType::Error, _("Unsupported redirect"));
619                                ResetOperation(FZ_REPLY_ERROR);
620                                return FZ_REPLY_ERROR;
621                        }
622                }
623                else
624                {
625                        if (!i)
626                        {
627                                // End of header, data from now on
628
629                                // Redirect if neccessary
630                                if (pData->m_responseCode >= 300)
631                                {
632                                        if (pData->m_redirectionCount++ == 6) {
633                                                LogMessage(MessageType::Error, _("Too many redirects"));
634                                                ResetOperation(FZ_REPLY_ERROR);
635                                                return FZ_REPLY_ERROR;
636                                        }
637
638                                        ResetSocket();
639                                        ResetHttpData(pData);
640
641                                        if( !pData->m_newLocation.HasScheme() || !pData->m_newLocation.HasServer() || !pData->m_newLocation.HasPath() ) {
642                                                LogMessage(MessageType::Error, _("Redirection to invalid or unsupported URI: %s"), m_current_uri.BuildURI());
643                                                ResetOperation(FZ_REPLY_ERROR);
644                                                return FZ_REPLY_ERROR;
645                                        }
646
647                                        enum ServerProtocol protocol = CServer::GetProtocolFromPrefix(pData->m_newLocation.GetScheme());
648                                        if( protocol != HTTP && protocol != HTTPS ) {
649                                                LogMessage(MessageType::Error, _("Redirection to invalid or unsupported address: %s"), pData->m_newLocation.BuildURI());
650                                                ResetOperation(FZ_REPLY_ERROR);
651                                                return FZ_REPLY_ERROR;
652                                        }
653
654                                        long port = CServer::GetDefaultPort(protocol);
655                                        if( pData->m_newLocation.HasPort() && (!pData->m_newLocation.GetPort().ToLong(&port) || port < 1 || port > 65535) ) {
656                                                LogMessage(MessageType::Error, _("Redirection to invalid or unsupported address: %s"), pData->m_newLocation.BuildURI());
657                                                ResetOperation(FZ_REPLY_ERROR);
658                                                return FZ_REPLY_ERROR;
659                                        }
660
661                                        m_current_uri = pData->m_newLocation;
662
663                                        // International domain names
664                                        wxString host = ConvertDomainName(m_current_uri.GetServer());
665
666                                        int res = InternalConnect(host, static_cast<unsigned short>(port), protocol == HTTPS);
667                                        if (res == FZ_REPLY_WOULDBLOCK)
668                                                res |= FZ_REPLY_REDIRECTED;
669                                        return res;
670                                }
671
672                                if( pData->m_pOpData && pData->m_pOpData->opId == Command::transfer) {
673                                        CHttpFileTransferOpData* pTransfer = static_cast<CHttpFileTransferOpData*>(pData->m_pOpData);
674                                        if( pTransfer->resume && pData->m_responseCode != 206 ) {
675                                                pTransfer->resume = false;
676                                                int res = OpenFile(pTransfer);
677                                                if( res != FZ_REPLY_OK ) {
678                                                        return res;
679                                                }
680                                        }
681                                }
682
683                                pData->m_gotHeader = true;
684
685                                memmove(m_pRecvBuffer, m_pRecvBuffer + 2, m_recvBufferPos - 2);
686                                m_recvBufferPos -= 2;
687
688                                if (m_recvBufferPos)
689                                {
690                                        int res;
691                                        if (pData->m_transferEncoding == pData->chunked)
692                                                res = OnChunkedData(pData);
693                                        else
694                                        {
695                                                pData->m_receivedData += m_recvBufferPos;
696                                                res = ProcessData(m_pRecvBuffer, m_recvBufferPos);
697                                                m_recvBufferPos = 0;
698                                        }
699                                        return res;
700                                }
701
702                                return FZ_REPLY_WOULDBLOCK;
703                        }
704                        if (m_recvBufferPos > 12 && !memcmp(m_pRecvBuffer, "Location: ", 10))
705                        {
706                                pData->m_newLocation = wxURI(wxString(m_pRecvBuffer + 10, wxConvLocal));
707                                pData->m_newLocation.Resolve(m_current_uri);
708                        }
709                        else if (m_recvBufferPos > 21 && !memcmp(m_pRecvBuffer, "Transfer-Encoding: ", 19))
710                        {
711                                if (!strcmp(m_pRecvBuffer + 19, "chunked"))
712                                        pData->m_transferEncoding = CHttpOpData::chunked;
713                                else if (!strcmp(m_pRecvBuffer + 19, "identity"))
714                                        pData->m_transferEncoding = CHttpOpData::identity;
715                                else
716                                        pData->m_transferEncoding = CHttpOpData::unknown;
717                        }
718                        else if (i > 16 && !memcmp(m_pRecvBuffer, "Content-Length: ", 16))
719                        {
720                                pData->m_totalSize = 0;
721                                char* p = m_pRecvBuffer + 16;
722                                while (*p)
723                                {
724                                        if (*p < '0' || *p > '9')
725                                        {
726                                                LogMessage(MessageType::Error, _("Malformed header: %s"), _("Invalid Content-Length"));
727                                                ResetOperation(FZ_REPLY_ERROR);
728                                                return FZ_REPLY_ERROR;
729                                        }
730                                        pData->m_totalSize = pData->m_totalSize * 10 + *p++ - '0';
731                                }
732                        }
733                }
734
735                memmove(m_pRecvBuffer, m_pRecvBuffer + i + 2, m_recvBufferPos - i - 2);
736                m_recvBufferPos -= i + 2;
737
738                if (!m_recvBufferPos)
739                        break;
740        }
741
742        return FZ_REPLY_WOULDBLOCK;
743}
744
745int CHttpControlSocket::OnChunkedData(CHttpOpData* pData)
746{
747        char* p = m_pRecvBuffer;
748        unsigned int len = m_recvBufferPos;
749
750        for (;;)
751        {
752                if (pData->m_chunkData.size != 0)
753                {
754                        unsigned int dataLen = len;
755                        if (pData->m_chunkData.size < len)
756                                dataLen = static_cast<unsigned int>(pData->m_chunkData.size);
757                        pData->m_receivedData += dataLen;
758                        int res = ProcessData(p, dataLen);
759                        if (res != FZ_REPLY_WOULDBLOCK)
760                                return res;
761
762                        pData->m_chunkData.size -= dataLen;
763                        p += dataLen;
764                        len -= dataLen;
765
766                        if (pData->m_chunkData.size == 0)
767                                pData->m_chunkData.terminateChunk = true;
768
769                        if (!len)
770                                break;
771                }
772
773                // Find line ending
774                unsigned int i = 0;
775                for (i = 0; (i + 1) < len; i++)
776                {
777                        if (p[i] == '\r')
778                        {
779                                if (p[i + 1] != '\n')
780                                {
781                                        LogMessage(MessageType::Error, _("Malformed chunk data: %s"), _("Wrong line endings"));
782                                        ResetOperation(FZ_REPLY_ERROR);
783                                        return FZ_REPLY_ERROR;
784                                }
785                                break;
786                        }
787                }
788                if ((i + 1) >= len)
789                {
790                        if (len == m_recvBufferLen)
791                        {
792                                // We don't support lines larger than 4096
793                                LogMessage(MessageType::Error, _("Malformed chunk data: %s"), _("Line length exceeded"));
794                                ResetOperation(FZ_REPLY_ERROR);
795                                return FZ_REPLY_ERROR;
796                        }
797                        break;
798                }
799
800                p[i] = 0;
801
802                if (pData->m_chunkData.terminateChunk)
803                {
804                        if (i)
805                        {
806                                // The chunk data has to end with CRLF. If i is nonzero,
807                                // it didn't end with just CRLF.
808                                LogMessage(MessageType::Error, _("Malformed chunk data: %s"), _("Chunk data improperly terminated"));
809                                ResetOperation(FZ_REPLY_ERROR);
810                                return FZ_REPLY_ERROR;
811                        }
812                        pData->m_chunkData.terminateChunk = false;
813                }
814                else if (pData->m_chunkData.getTrailer)
815                {
816                        if (!i)
817                        {
818                                // We're done
819                                return ProcessData(0, 0);
820                        }
821
822                        // Ignore the trailer
823                }
824                else
825                {
826                        // Read chunk size
827                        for( char* q = p; *q && *q != ';' && *q != ' '; ++q ) {
828                                pData->m_chunkData.size *= 16;
829                                if (*q >= '0' && *q <= '9') {
830                                        pData->m_chunkData.size += *q - '0';
831                                }
832                                else if (*q >= 'A' && *q <= 'F') {
833                                        pData->m_chunkData.size += *q - 'A' + 10;
834                                }
835                                else if (*q >= 'a' && *q <= 'f') {
836                                        pData->m_chunkData.size += *q - 'a' + 10;
837                                }
838                                else {
839                                        // Invalid size
840                                        LogMessage(MessageType::Error, _("Malformed chunk data: %s"), _("Invalid chunk size"));
841                                        ResetOperation(FZ_REPLY_ERROR);
842                                        return FZ_REPLY_ERROR;
843                                }
844                        }
845                        if (pData->m_chunkData.size == 0)
846                                pData->m_chunkData.getTrailer = true;
847                }
848
849                p += i + 2;
850                len -= i + 2;
851
852                if (!len)
853                        break;
854        }
855
856        if (p != m_pRecvBuffer)
857        {
858                memmove(m_pRecvBuffer, p, len);
859                m_recvBufferPos = len;
860        }
861
862        return FZ_REPLY_WOULDBLOCK;
863}
864
865int CHttpControlSocket::ResetOperation(int nErrorCode)
866{
867        if (m_pCurOpData && m_pCurOpData->opId == Command::transfer)
868        {
869                CHttpFileTransferOpData *pData = static_cast<CHttpFileTransferOpData *>(m_pCurOpData);
870                delete pData->pFile;
871                pData->pFile = 0;
872        }
873
874        if (!m_pCurOpData || !m_pCurOpData->pNextOpData)
875        {
876                if (m_pBackend)
877                {
878                        if (nErrorCode == FZ_REPLY_OK)
879                                LogMessage(MessageType::Status, _("Disconnected from server"));
880                        else
881                                LogMessage(MessageType::Error, _("Disconnected from server"));
882                }
883                ResetSocket();
884                m_pHttpOpData = 0;
885        }
886
887        return CControlSocket::ResetOperation(nErrorCode);
888}
889
890void CHttpControlSocket::OnClose(int error)
891{
892        LogMessage(MessageType::Debug_Verbose, _T("CHttpControlSocket::OnClose(%d)"), error);
893
894        if (error) {
895                LogMessage(MessageType::Error, _("Disconnected from server: %s"), CSocket::GetErrorDescription(error));
896                ResetOperation(FZ_REPLY_ERROR | FZ_REPLY_DISCONNECTED);
897                return;
898        }
899
900        // HTTP socket isn't connected outside operations
901        if (!m_pCurOpData)
902                return;
903
904        if (m_pCurOpData->pNextOpData) {
905                ResetOperation(FZ_REPLY_ERROR | FZ_REPLY_DISCONNECTED);
906                return;
907        }
908
909        if (!m_pHttpOpData->m_gotHeader) {
910                ResetOperation(FZ_REPLY_ERROR | FZ_REPLY_DISCONNECTED);
911                return;
912        }
913
914        if (m_pHttpOpData->m_transferEncoding == CHttpOpData::chunked) {
915                if (!m_pHttpOpData->m_chunkData.getTrailer) {
916                        ResetOperation(FZ_REPLY_ERROR | FZ_REPLY_DISCONNECTED);
917                        return;
918                }
919        }
920        else {
921                if (m_pHttpOpData->m_totalSize != -1 && m_pHttpOpData->m_receivedData != m_pHttpOpData->m_totalSize) {
922                        ResetOperation(FZ_REPLY_ERROR | FZ_REPLY_DISCONNECTED);
923                        return;
924                }
925        }
926
927        ProcessData(0, 0);
928}
929
930void CHttpControlSocket::ResetHttpData(CHttpOpData* pData)
931{
932        wxASSERT(pData);
933
934        delete [] m_pRecvBuffer;
935        m_pRecvBuffer = 0;
936
937        pData->m_gotHeader = false;
938        pData->m_responseCode = -1;
939        pData->m_transferEncoding = CHttpOpData::unknown;
940
941        pData->m_chunkData.getTrailer = false;
942        pData->m_chunkData.size = 0;
943        pData->m_chunkData.terminateChunk = false;
944
945        pData->m_totalSize = -1;
946        pData->m_receivedData = 0;
947}
948
949int CHttpControlSocket::ProcessData(char* p, int len)
950{
951        int res;
952        Command commandId = GetCurrentCommandId();
953        switch (commandId)
954        {
955        case Command::transfer:
956                res = FileTransferParseResponse(p, len);
957                break;
958        default:
959                LogMessage(MessageType::Debug_Warning, _T("No action for parsing data for command %d"), (int)commandId);
960                ResetOperation(FZ_REPLY_INTERNALERROR);
961                res = FZ_REPLY_ERROR;
962                break;
963        }
964
965        wxASSERT(p || !m_pCurOpData);
966
967        return res;
968}
969
970int CHttpControlSocket::ParseSubcommandResult(int prevResult)
971{
972        LogMessage(MessageType::Debug_Verbose, _T("CHttpControlSocket::SendNextCommand(%d)"), prevResult);
973        if (!m_pCurOpData)
974        {
975                LogMessage(__TFILE__, __LINE__, this, MessageType::Debug_Warning, _T("SendNextCommand called without active operation"));
976                ResetOperation(FZ_REPLY_ERROR);
977                return FZ_REPLY_ERROR;
978        }
979
980        switch (m_pCurOpData->opId)
981        {
982        case Command::transfer:
983                return FileTransferSubcommandResult(prevResult);
984        default:
985                LogMessage(__TFILE__, __LINE__, this, MessageType::Debug_Warning, _T("Unknown opID (%d) in SendNextCommand"), m_pCurOpData->opId);
986                ResetOperation(FZ_REPLY_INTERNALERROR);
987                break;
988        }
989
990        return FZ_REPLY_ERROR;
991}
992
993int CHttpControlSocket::Disconnect()
994{
995        DoClose();
996        return FZ_REPLY_OK;
997}
998
999int CHttpControlSocket::OpenFile( CHttpFileTransferOpData* pData)
1000{
1001        delete pData->pFile;
1002        pData->pFile = new wxFile();
1003        CreateLocalDir(pData->localFile);
1004
1005        if (!pData->pFile->Open(pData->localFile, pData->resume ? wxFile::write_append : wxFile::write))
1006        {
1007                LogMessage(MessageType::Error, _("Failed to open \"%s\" for writing"), pData->localFile);
1008                ResetOperation(FZ_REPLY_ERROR);
1009                return FZ_REPLY_ERROR;
1010        }
1011        wxFileOffset end = pData->pFile->SeekEnd();
1012        if( !end ) {
1013                pData->resume = false;
1014        }
1015        pData->localFileSize = CLocalFileSystem::GetSize(pData->localFile);
1016        return FZ_REPLY_OK;
1017}
Note: See TracBrowser for help on using the repository browser.