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

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

Update new version: 3.15.02

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