#include "FileZilla.h"
#include "ControlSocket.h"
#include "httpcontrolsocket.h"
// Connect is special for HTTP: It is done on a per-command basis, so we need
// to establish a connection before each command.
class CHttpConnectOpData : public COpData
{
public:
CHttpConnectOpData()
: COpData(cmd_connect)
{
}
virtual ~CHttpConnectOpData()
{
}
wxString host;
unsigned int port;
};
class CHttpOpData
{
public:
CHttpOpData(COpData* pOpData)
: m_pOpData(pOpData)
{
m_gotHeader = false;
m_responseCode = -1;
m_redirectionCount = 0;
m_transferEncoding = unknown;
m_chunkData.getTrailer = false;
m_chunkData.size = 0;
m_chunkData.terminateChunk = false;
m_totalSize = -1;
m_receivedData = 0;
}
bool m_gotHeader;
int m_responseCode;
wxString m_responseString;
wxString m_newLocation;
wxString m_newHostWithPort;
int m_redirectionCount;
wxLongLong m_totalSize;
wxLongLong m_receivedData;
COpData* m_pOpData;
enum transferEncodings
{
identity,
chunked,
unknown
};
enum transferEncodings m_transferEncoding;
struct t_chunkData
{
bool getTrailer;
bool terminateChunk;
wxLongLong size;
} m_chunkData;
};
class CHttpFileTransferOpData : public CFileTransferOpData, public CHttpOpData
{
public:
CHttpFileTransferOpData()
: CHttpOpData(this)
{
pFile = 0;
}
virtual ~CHttpFileTransferOpData()
{
delete pFile;
}
wxFile* pFile;
};
CHttpControlSocket::CHttpControlSocket(CFileZillaEnginePrivate *pEngine)
: CRealControlSocket(pEngine)
{
m_pRecvBuffer = 0;
m_pAddress = 0;
}
CHttpControlSocket::~CHttpControlSocket()
{
delete [] m_pRecvBuffer;
delete m_pAddress;
}
int CHttpControlSocket::SendNextCommand(int prevResult /*=FZ_REPLY_OK*/)
{
LogMessage(Debug_Verbose, _T("CHttpControlSocket::SendNextCommand(%d)"), prevResult);
if (!m_pCurOpData)
{
LogMessage(__TFILE__, __LINE__, this, Debug_Warning, _T("SendNextCommand called without active operation"));
ResetOperation(FZ_REPLY_ERROR);
return FZ_REPLY_ERROR;
}
if (m_pCurOpData->waitForAsyncRequest)
{
LogMessage(__TFILE__, __LINE__, this, Debug_Info, _T("Waiting for async request, ignoring SendNextCommand"));
return FZ_REPLY_WOULDBLOCK;
}
switch (m_pCurOpData->opId)
{
case cmd_transfer:
return FileTransferSend(prevResult);
default:
LogMessage(__TFILE__, __LINE__, this, ::Debug_Warning, _T("Unknown opID (%d) in SendNextCommand"), m_pCurOpData->opId);
ResetOperation(FZ_REPLY_INTERNALERROR);
break;
}
return FZ_REPLY_ERROR;
}
int CHttpControlSocket::ContinueConnect(const wxIPV4address *address)
{
LogMessage(__TFILE__, __LINE__, this, Debug_Verbose, _T("CHttpControlSocket::ContinueConnect(%p) m_pEngine=%p"), address, m_pEngine);
if (GetCurrentCommandId() != cmd_connect ||
!m_pCurrentServer)
{
LogMessage(Debug_Warning, _T("Invalid context for call to ContinueConnect(), cmd=%d, m_pCurrentServer=%p"), GetCurrentCommandId(), m_pCurrentServer);
return DoClose(FZ_REPLY_INTERNALERROR);
}
if (!address)
{
if (m_pCurrentServer)
{
delete m_pCurrentServer;
m_pCurrentServer = 0;
}
LogMessage(::Error, _("Invalid hostname or host not found"));
return ResetOperation(FZ_REPLY_ERROR | FZ_REPLY_CRITICALERROR);
}
if (m_pCurOpData && m_pCurOpData->opId == cmd_connect)
{
CHttpConnectOpData *pData = static_cast<CHttpConnectOpData *>(m_pCurOpData);
pData->host = address->IPAddress();
return DoInternalConnect();
}
if (m_pAddress)
delete m_pAddress;
m_pAddress = new wxIPV4address(*address);
// Now we got the server's IP address for later use, e.g. transfer commands
ResetOperation(FZ_REPLY_OK);
return FZ_REPLY_OK;
}
bool CHttpControlSocket::SetAsyncRequestReply(CAsyncRequestNotification *pNotification)
{
switch (pNotification->GetRequestID())
{
case reqId_fileexists:
{
if (!m_pCurOpData || m_pCurOpData->opId != cmd_transfer)
{
LogMessage(__TFILE__, __LINE__, this, Debug_Info, _T("No or invalid operation in progress, ignoring request reply %f"), pNotification->GetRequestID());
return false;
}
CHttpFileTransferOpData *pData = static_cast<CHttpFileTransferOpData *>(m_pCurOpData);
if (!pData->waitForAsyncRequest)
{
LogMessage(__TFILE__, __LINE__, this, Debug_Info, _T("Not waiting for request reply, ignoring request reply %d"), pNotification->GetRequestID());
return false;
}
pData->waitForAsyncRequest = false;
CFileExistsNotification *pFileExistsNotification = reinterpret_cast<CFileExistsNotification *>(pNotification);
switch (pFileExistsNotification->overwriteAction)
{
case CFileExistsNotification::overwrite:
SendNextCommand();
break;
case CFileExistsNotification::overwriteNewer:
if (!pFileExistsNotification->localTime.IsValid() || !pFileExistsNotification->remoteTime.IsValid())
SendNextCommand();
else if (pFileExistsNotification->download && pFileExistsNotification->localTime.IsEarlierThan(pFileExistsNotification->remoteTime))
SendNextCommand();
else if (!pFileExistsNotification->download && pFileExistsNotification->localTime.IsLaterThan(pFileExistsNotification->remoteTime))
SendNextCommand();
else
{
if (pData->download)
{
wxString filename = pData->remotePath.FormatFilename(pData->remoteFile);
LogMessage(Status, _("Skipping download of %s"), filename.c_str());
}
else
{
LogMessage(Status, _("Skipping upload of %s"), pData->localFile.c_str());
}
ResetOperation(FZ_REPLY_OK);
}
break;
case CFileExistsNotification::resume:
ResetOperation(FZ_REPLY_CRITICALERROR | FZ_REPLY_NOTSUPPORTED);
break;
case CFileExistsNotification::rename:
if (pData->download)
{
wxFileName fn = pData->localFile;
fn.SetFullName(pFileExistsNotification->newName);
pData->localFile = fn.GetFullPath();
wxStructStat buf;
int result;
result = wxStat(pData->localFile, &buf);
if (!result)
pData->localFileSize = buf.st_size;
else
pData->localFileSize = -1;
if (CheckOverwriteFile() == FZ_REPLY_OK)
SendNextCommand();
}
else
{
ResetOperation(FZ_REPLY_CRITICALERROR | FZ_REPLY_NOTSUPPORTED);
break;
}
break;
case CFileExistsNotification::skip:
if (pData->download)
{
wxString filename = pData->remotePath.FormatFilename(pData->remoteFile);
LogMessage(Status, _("Skipping download of %s"), filename.c_str());
}
else
{
LogMessage(Status, _("Skipping upload of %s"), pData->localFile.c_str());
}
ResetOperation(FZ_REPLY_OK);
break;
default:
LogMessage(__TFILE__, __LINE__, this, Debug_Warning, _T("Unknown file exists action: %d"), pFileExistsNotification->overwriteAction);
ResetOperation(FZ_REPLY_INTERNALERROR);
return false;
}
}
break;
default:
LogMessage(__TFILE__, __LINE__, this, Debug_Warning, _T("Unknown request %d"), pNotification->GetRequestID());
ResetOperation(FZ_REPLY_INTERNALERROR);
return false;
}
return true;
}
void CHttpControlSocket::OnReceive()
{
if (!m_pRecvBuffer)
{
m_pRecvBuffer = new char[m_recvBufferLen];
m_recvBufferPos = 0;
}
unsigned int len = m_recvBufferLen - m_recvBufferPos;
Read(m_pRecvBuffer + m_recvBufferPos, len);
if (Error())
{
if (LastError() != wxSOCKET_WOULDBLOCK)
ResetOperation(FZ_REPLY_ERROR | FZ_REPLY_DISCONNECTED);
return;
}
int read;
if (!(read = LastCount()))
{
ResetOperation(FZ_REPLY_ERROR | FZ_REPLY_DISCONNECTED);
return;
}
m_pEngine->SetActive(true);
if (!m_pCurOpData || m_pCurOpData->opId == cmd_connect)
{
// Just ignore all further data
m_recvBufferPos = 0;
return;
}
m_recvBufferPos += read;
if (!m_pHttpOpData->m_gotHeader)
ParseHeader(m_pHttpOpData);
else if (m_pHttpOpData->m_transferEncoding == CHttpOpData::chunked)
OnChunkedData(m_pHttpOpData);
else
{
m_pHttpOpData->m_receivedData += m_recvBufferPos;
ProcessData(m_pRecvBuffer, m_recvBufferPos);
m_recvBufferPos = 0;
}
}
void CHttpControlSocket::OnConnect()
{
LogMessage(Status, _("Connection established, sending HTTP request"));
ResetOperation(FZ_REPLY_OK);
}
enum filetransferStates
{
filetransfer_init = 0,
filetransfer_waitfileexists,
filetransfer_transfer
};
int CHttpControlSocket::FileTransfer(const wxString localFile, const CServerPath &remotePath,
const wxString &remoteFile, bool download,
const CFileTransferCommand::t_transferSettings& transferSettings)
{
LogMessage(Debug_Verbose, _T("CHttpControlSocket::FileTransfer()"));
LogMessage(Status, _("Downloading %s"), remotePath.FormatFilename(remoteFile).c_str());
if (!download)
{
ResetOperation(FZ_REPLY_CRITICALERROR | FZ_REPLY_NOTSUPPORTED);
return FZ_REPLY_ERROR;
}
if (m_pCurOpData)
{
LogMessage(__TFILE__, __LINE__, this, Debug_Info, _T("deleting nonzero pData"));
delete m_pCurOpData;
}
CHttpFileTransferOpData *pData = new CHttpFileTransferOpData;
m_pCurOpData = pData;
m_pHttpOpData = pData;
pData->localFile = localFile;
pData->remotePath = remotePath;
pData->remoteFile = remoteFile;
pData->download = download;
if (localFile != _T(""))
{
pData->opState = filetransfer_waitfileexists;
int res = CheckOverwriteFile();
if (res != FZ_REPLY_OK)
return res;
pData->opState = filetransfer_transfer;
pData->pFile = new wxFile();
// Create local directory
wxFileName fn(pData->localFile);
wxFileName::Mkdir(fn.GetPath(), 0777, wxPATH_MKDIR_FULL);
if (!pData->pFile->Open(pData->localFile, wxFile::write))
{
LogMessage(::Error, _("Failed to open \"%s\" for writing"), pData->localFile.c_str());
ResetOperation(FZ_REPLY_ERROR);
return FZ_REPLY_ERROR;
}
}
else
pData->opState = filetransfer_transfer;
int res = InternalConnect(m_pAddress->IPAddress(), m_pCurrentServer->GetPort());
if (res != FZ_REPLY_OK)
return res;
return FileTransferSend();
}
int CHttpControlSocket::FileTransferSend(int prevResult /*=FZ_RESULT_OK*/)
{
LogMessage(Debug_Verbose, _T("CHttpControlSocket::FileTransferSend(%d)"), prevResult);
if (!m_pCurOpData)
{
LogMessage(__TFILE__, __LINE__, this, Debug_Info, _T("Empty m_pCurOpData"));
ResetOperation(FZ_REPLY_INTERNALERROR);
return FZ_REPLY_ERROR;
}
CHttpFileTransferOpData *pData = static_cast<CHttpFileTransferOpData *>(m_pCurOpData);
if (prevResult != FZ_REPLY_OK)
{
ResetOperation(prevResult);
return FZ_REPLY_ERROR;
}
if (pData->opState == filetransfer_waitfileexists)
{
pData->opState = filetransfer_transfer;
pData->pFile = new wxFile();
// Create local directory
wxFileName fn(pData->localFile);
wxFileName::Mkdir(fn.GetPath(), 0777, wxPATH_MKDIR_FULL);
if (!pData->pFile->Open(pData->localFile, wxFile::write))
{
LogMessage(::Error, _("Failed to open \"%s\" for writing"), pData->localFile.c_str());
ResetOperation(FZ_REPLY_ERROR);
return FZ_REPLY_ERROR;
}
int res = InternalConnect(m_pAddress->IPAddress(), m_pCurrentServer->GetPort());
if (res != FZ_REPLY_OK)
return res;
}
wxString location;
wxString hostWithPort;
if (pData->m_newLocation == _T(""))
{
location = _T("http://") + m_pCurrentServer->GetHost() + pData->remotePath.FormatFilename(pData->remoteFile).c_str();
hostWithPort = wxString::Format(_T("%s:%d"), m_pCurrentServer->GetHost().c_str(), m_pCurrentServer->GetPort());
}
else
{
location = pData->m_newLocation;
hostWithPort = pData->m_newHostWithPort;
}
wxString action = wxString::Format(_T("GET %s HTTP/1.1"), location.c_str());
LogMessageRaw(Command, action);
wxString command = wxString::Format(_T("%s\r\nHost: %s\r\nUser-Agent: %s\r\nConnection: close\r\n\r\n"), action.c_str(), hostWithPort.c_str(), wxString(PACKAGE_STRING, wxConvLocal).c_str());
const wxWX2MBbuf str = command.mb_str();
if (!Send(str, strlen(str)))
return FZ_REPLY_ERROR;
return FZ_REPLY_WOULDBLOCK;
}
int CHttpControlSocket::InternalConnect(const wxString& host, unsigned short port)
{
LogMessage(Debug_Verbose, _T("CHttpControlSocket::InternalConnect()"));
CHttpConnectOpData* pData = new CHttpConnectOpData;
pData->pNextOpData = m_pCurOpData;
m_pCurOpData = pData;
pData->port = port;
if (!IsIpAddress(host))
{
LogMessage(Status, _("Resolving IP-Address for %s"), host.c_str());
CAsyncHostResolver *resolver = new CAsyncHostResolver(m_pEngine, ConvertDomainName(host));
m_pEngine->AddNewAsyncHostResolver(resolver);
resolver->Create();
resolver->Run();
return FZ_REPLY_WOULDBLOCK;
}
pData->host = host;
return DoInternalConnect();
}
int CHttpControlSocket::DoInternalConnect()
{
LogMessage(Debug_Verbose, _T("CHttpControlSocket::DoInternalConnect()"));
if (!m_pCurOpData)
{
LogMessage(__TFILE__, __LINE__, this, Debug_Info, _T("Empty m_pCurOpData"));
ResetOperation(FZ_REPLY_INTERNALERROR);
return FZ_REPLY_ERROR;
}
CHttpConnectOpData *pData = static_cast<CHttpConnectOpData *>(m_pCurOpData);
LogMessage(Status, _("Connecting to %s:%d..."), m_pAddress->IPAddress().c_str(), pData->port);
if (m_pBackend)
delete m_pBackend;
m_pBackend = new CSocketBackend(this, this);
wxIPV4address addr;
addr.Hostname(pData->host);
addr.Service(pData->port);
bool res = wxSocketClient::Connect(addr, false);
if (res)
return FZ_REPLY_OK;
else if (LastError() != wxSOCKET_WOULDBLOCK)
return ResetOperation(FZ_REPLY_ERROR);
return FZ_REPLY_WOULDBLOCK;
}
int CHttpControlSocket::FileTransferParseResponse(char* p, unsigned int len)
{
LogMessage(Debug_Verbose, _T("CHttpControlSocket::FileTransferParseResponse(%p, %d)"), p, len);
if (!m_pCurOpData)
{
LogMessage(__TFILE__, __LINE__, this, Debug_Info, _T("Empty m_pCurOpData"));
ResetOperation(FZ_REPLY_INTERNALERROR);
return FZ_REPLY_ERROR;
}
CHttpFileTransferOpData *pData = static_cast<CHttpFileTransferOpData *>(m_pCurOpData);
if (!p)
{
ResetOperation(FZ_REPLY_OK);
return FZ_REPLY_OK;
}
if (!m_pTransferStatus)
{
InitTransferStatus(pData->m_totalSize.GetLo() + ((wxFileOffset)pData->m_totalSize.GetHi() << 32), 0, false);
SetTransferStatusStartTime();
}
if (pData->localFile == _T(""))
{
char* q = new char[len];
memcpy(q, p, len);
m_pEngine->AddNotification(new CDataNotification(q, len));
}
else
{
wxASSERT(pData->pFile);
if (pData->pFile->Write(p, len) != len)
{
LogMessage(::Error, _("Failed to write to file %s"), pData->localFile.c_str());
ResetOperation(FZ_REPLY_ERROR);
return FZ_REPLY_ERROR;
}
}
UpdateTransferStatus(len);
return FZ_REPLY_WOULDBLOCK;
}
int CHttpControlSocket::ParseHeader(CHttpOpData* pData)
{
// Parse the HTTP header.
// We do just the neccessary parsing and silently ignore most header fields
// Redirects are supported though if the server sends the Location field.
while (true)
{
// Find line ending
unsigned int i = 0;
for (i = 0; (i + 1) < m_recvBufferPos; i++)
{
if (m_pRecvBuffer[i] == '\r')
{
if (m_pRecvBuffer[i + 1] != '\n')
{
LogMessage(::Error, _("Malformed reply, server not sending proper lineendings"));
ResetOperation(FZ_REPLY_ERROR);
return FZ_REPLY_ERROR;
}
break;
}
}
if ((i + 1) >= m_recvBufferPos)
{
if (m_recvBufferPos == m_recvBufferLen)
{
// We don't support header lines larger than 4096
LogMessage(::Error, _("Too long header line"));
ResetOperation(FZ_REPLY_ERROR);
return FZ_REPLY_ERROR;
}
return FZ_REPLY_WOULDBLOCK;
}
m_pRecvBuffer[i] = 0;
const wxString& line = wxString(m_pRecvBuffer, wxConvLocal);
if (line != _T(""))
LogMessageRaw(Response, line);
if (pData->m_responseCode == -1)
{
pData->m_responseString = line;
if (m_recvBufferPos < 16 || memcmp(m_pRecvBuffer, "HTTP/1.", 7))
{
// Invalid HTTP Status-Line
LogMessage(::Error, _("Invalid HTTP Response"));
ResetOperation(FZ_REPLY_ERROR);
return FZ_REPLY_ERROR;
}
if (m_pRecvBuffer[9] < '1' || m_pRecvBuffer[9] > '5' ||
m_pRecvBuffer[10] < '0' || m_pRecvBuffer[10] > '9' ||
m_pRecvBuffer[11] < '0' || m_pRecvBuffer[11] > '9')
{
// Invalid response code
LogMessage(::Error, _("Invalid response code"));
ResetOperation(FZ_REPLY_ERROR);
return FZ_REPLY_ERROR;
}
pData->m_responseCode = (m_pRecvBuffer[9] - '0') * 100 + (m_pRecvBuffer[10] - '0') * 10 + m_pRecvBuffer[11] - '0';
if (pData->m_responseCode >= 400)
{
// Failed request
ResetOperation(FZ_REPLY_ERROR);
return FZ_REPLY_ERROR;
}
if (pData->m_responseCode == 305)
{
// Unsupported redirect
LogMessage(::Error, _("Unsuppored redirect"));
ResetOperation(FZ_REPLY_ERROR);
return FZ_REPLY_ERROR;
}
}
else
{
if (!i)
{
// End of header, data from now on
// Redirect if neccessary
if (pData->m_responseCode >= 300)
{
if (pData->m_redirectionCount++ == 5)
{
LogMessage(::Error, _("Too many redirects"));
ResetOperation(FZ_REPLY_ERROR);
return FZ_REPLY_ERROR;
}
ResetSocket();
ResetHttpData(pData);
wxString host;
int pos;
if ((pos = pData->m_newLocation.Find(_T("://"))) != -1)
host = pData->m_newLocation.Mid(pos + 3);
else
host = pData->m_newLocation;
if ((pos = host.Find(_T("/"))) != -1)
host = host.Left(pos);
unsigned long port;
if ((pos = host.Find(':', true)) != -1)
{
wxString strport = host.Mid(pos + 1);
if (!strport.ToULong(&port) || port < 1 || port > 65535)
port = 80;
host = host.Left(pos);
}
else
port = 80;
if (host == _T(""))
{
// Unsupported redirect
LogMessage(::Error, _("Redirection to invalid address"));
ResetOperation(FZ_REPLY_ERROR);
return FZ_REPLY_ERROR;
}
pData->m_newHostWithPort = wxString::Format(_T("%s:%d"), host.c_str(), port);
return InternalConnect(host, port);
}
pData->m_gotHeader = true;
memmove(m_pRecvBuffer, m_pRecvBuffer + 2, m_recvBufferPos - 2);
m_recvBufferPos -= 2;
if (m_recvBufferPos)
{
int res;
if (pData->m_transferEncoding == pData->chunked)
res = OnChunkedData(pData);
else
{
pData->m_receivedData += m_recvBufferPos;
res = ProcessData(m_pRecvBuffer, m_recvBufferPos);
m_recvBufferPos = 0;
}
return res;
}
return FZ_REPLY_WOULDBLOCK;
}
if (m_recvBufferPos > 12 && !memcmp(m_pRecvBuffer, "Location: ", 10))
{
pData->m_newLocation = wxString(m_pRecvBuffer + 10, wxConvLocal);
}
else if (m_recvBufferPos > 21 && !memcmp(m_pRecvBuffer, "Transfer-Encoding: ", 19))
{
if (!strcmp(m_pRecvBuffer + 19, "chunked"))
pData->m_transferEncoding = CHttpOpData::chunked;
else if (!strcmp(m_pRecvBuffer + 19, "identity"))
pData->m_transferEncoding = CHttpOpData::identity;
else
pData->m_transferEncoding = CHttpOpData::unknown;
}
else if (i > 16 && !memcmp(m_pRecvBuffer, "Content-Length: ", 16))
{
pData->m_totalSize = 0;
char* p = m_pRecvBuffer + 16;
while (*p)
{
if (*p < '0' || *p > '9')
{
LogMessage(::Error, _("Malformed header: %s"), _("Invalid Content-Length"));
ResetOperation(FZ_REPLY_ERROR);
return FZ_REPLY_ERROR;
}
pData->m_totalSize = pData->m_totalSize * 10 + *p++ - '0';
}
}
}
memmove(m_pRecvBuffer, m_pRecvBuffer + i + 2, m_recvBufferPos - i - 2);
m_recvBufferPos -= i + 2;
if (!m_recvBufferPos)
break;
}
return FZ_REPLY_WOULDBLOCK;
}
int CHttpControlSocket::OnChunkedData(CHttpOpData* pData)
{
char* p = m_pRecvBuffer;
unsigned int len = m_recvBufferPos;
while (true)
{
if (pData->m_chunkData.size != 0)
{
unsigned int dataLen = len;
if (pData->m_chunkData.size < len)
dataLen = pData->m_chunkData.size.GetLo();
pData->m_receivedData += dataLen;
int res = ProcessData(p, dataLen);
if (res != FZ_REPLY_WOULDBLOCK)
return res;
pData->m_chunkData.size -= dataLen;
p += dataLen;
len -= dataLen;
if (pData->m_chunkData.size == 0)
pData->m_chunkData.terminateChunk = true;
if (!len)
break;
}
// Find line ending
unsigned int i = 0;
for (i = 0; (i + 1) < len; i++)
{
if (p[i] == '\r')
{
if (p[i + 1] != '\n')
{
LogMessage(::Error, _("Malformed chunk data: %s"), _("Wrong lineendings"));
ResetOperation(FZ_REPLY_ERROR);
return FZ_REPLY_ERROR;
}
break;
}
}
if ((i + 1) >= len)
{
if (len == m_recvBufferLen)
{
// We don't support lines larger than 4096
LogMessage(::Error, _("Malformed chunk data: %s"), _("Line length exceeded"));
ResetOperation(FZ_REPLY_ERROR);
return FZ_REPLY_ERROR;
}
break;
}
p[i] = 0;
if (pData->m_chunkData.terminateChunk)
{
if (i)
{
// The chunk data has to end with CRLF. If i is nonzero,
// it didn't end with just CRLF.
LogMessage(::Error, _("Malformed chunk data: %s"), _("Chunk data improperly terminated"));
ResetOperation(FZ_REPLY_ERROR);
return FZ_REPLY_ERROR;
}
pData->m_chunkData.terminateChunk = false;
}
else if (pData->m_chunkData.getTrailer)
{
if (!i)
{
// We're done
return ProcessData(0, 0);
}
// Ignore the trailer
}
else
{
// Read chunk size
char* q = p;
while (*q)
{
if (*q >= '0' && *q <= '9')
{
pData->m_chunkData.size *= 16;
pData->m_chunkData.size += *q - '0';
}
else if (*q >= 'A' && *q <= 'F')
{
pData->m_chunkData.size *= 10;
pData->m_chunkData.size += *q - 'A' + 10;
}
else if (*q >= 'a' && *q <= 'f')
{
pData->m_chunkData.size *= 10;
pData->m_chunkData.size += *q - 'a' + 10;
}
else if (*q == ';' || *q == ' ')
break;
else
{
// Invalid size
LogMessage(::Error, _("Malformed chunk data: %s"), _("Invalid chunk size"));
ResetOperation(FZ_REPLY_ERROR);
return FZ_REPLY_ERROR;
}
q++;
}
if (pData->m_chunkData.size == 0)
pData->m_chunkData.getTrailer = true;
}
p += i + 2;
len -= i + 2;
if (!len)
break;
}
if (p != m_pRecvBuffer)
{
memmove(m_pRecvBuffer, p, len);
m_recvBufferPos = len;
}
return FZ_REPLY_WOULDBLOCK;
}
int CHttpControlSocket::ResetOperation(int nErrorCode)
{
if (m_pCurOpData && m_pCurOpData->opId == cmd_transfer)
{
CHttpFileTransferOpData *pData = static_cast<CHttpFileTransferOpData *>(m_pCurOpData);
delete pData->pFile;
pData->pFile = 0;
}
if (!m_pCurOpData || !m_pCurOpData->pNextOpData)
{
if (nErrorCode == FZ_REPLY_OK)
LogMessage(Status, _("Disconnected from server"));
else
LogMessage(::Error, _("Disconnected from server"));
ResetSocket();
m_pHttpOpData = 0;
}
return CControlSocket::ResetOperation(nErrorCode);
}
void CHttpControlSocket::OnClose()
{
char tmp[1];
for (Peek(tmp, 1); !Error() && LastCount(); Peek(tmp, 1))
OnReceive();
// HTTP socket isn't connected outside operations
if (!m_pCurOpData)
return;
if (m_pCurOpData->pNextOpData)
{
ResetOperation(FZ_REPLY_ERROR | FZ_REPLY_DISCONNECTED);
return;
}
if (!m_pHttpOpData->m_gotHeader)
{
ResetOperation(FZ_REPLY_ERROR | FZ_REPLY_DISCONNECTED);
return;
}
if (m_pHttpOpData->m_transferEncoding == CHttpOpData::chunked)
{
if (!m_pHttpOpData->m_chunkData.getTrailer)
{
ResetOperation(FZ_REPLY_ERROR | FZ_REPLY_DISCONNECTED);
return;
}
}
else
{
if (m_pHttpOpData->m_totalSize != -1 && m_pHttpOpData->m_receivedData != m_pHttpOpData->m_totalSize)
{
ResetOperation(FZ_REPLY_ERROR | FZ_REPLY_DISCONNECTED);
return;
}
}
ProcessData(0, 0);
}
void CHttpControlSocket::ResetHttpData(CHttpOpData* pData)
{
wxASSERT(pData);
delete m_pRecvBuffer;
m_pRecvBuffer = 0;
pData->m_gotHeader = false;
pData->m_responseCode = -1;
pData->m_transferEncoding = CHttpOpData::unknown;
pData->m_chunkData.getTrailer = false;
pData->m_chunkData.size = 0;
pData->m_chunkData.terminateChunk = false;
pData->m_totalSize = -1;
pData->m_receivedData = 0;
}
int CHttpControlSocket::ProcessData(char* p, int len)
{
int res;
enum Command commandId = GetCurrentCommandId();
switch (commandId)
{
case cmd_transfer:
res = FileTransferParseResponse(p, len);
break;
default:
LogMessage(Debug_Warning, _T("No action for parsing data for command %d"), (int)commandId);
ResetOperation(FZ_REPLY_INTERNALERROR);
res = FZ_REPLY_ERROR;
break;
}
wxASSERT(p || !m_pCurOpData);
return res;
}
syntax highlighted by Code2HTML, v. 0.9.1