Logo Search packages:      
Sourcecode: filezilla version File versions

externalipresolver.cpp

#include "FileZilla.h"
#include "externalipresolver.h"
#include "asynchostresolver.h"
#include "wx/regex.h"

const wxEventType fzEVT_EXTERNALIPRESOLVE = wxNewEventType();

fzExternalIPResolveEvent::fzExternalIPResolveEvent(int id)
      : wxEvent(id, fzEVT_EXTERNALIPRESOLVE)
{
}

wxEvent* fzExternalIPResolveEvent::Clone() const
{
      return new fzExternalIPResolveEvent(GetId());
}

wxString CExternalIPResolver::m_ip;
bool CExternalIPResolver::m_checked = false;

BEGIN_EVENT_TABLE(CExternalIPResolver, wxEvtHandler)
      EVT_FZ_ASYNCHOSTRESOLVE(wxID_ANY, CExternalIPResolver::OnAsyncHostResolver)
      EVT_SOCKET(0, CExternalIPResolver::OnSocketEvent)
END_EVENT_TABLE();

CExternalIPResolver::CExternalIPResolver(wxEvtHandler* handler, int id /*=wxID_ANY*/)
      : m_handler(handler), m_id(id), m_pHostResolver(0)
{
      m_pSocket = 0;
      m_done = false;
      m_pSendBuffer = 0;
      m_sendBufferPos = 0;
      m_pRecvBuffer = 0;
      m_recvBufferPos = 0;

      ResetHttpData(true);
}

CExternalIPResolver::~CExternalIPResolver()
{
      delete [] m_pSendBuffer;
      m_pSendBuffer = 0;
      delete [] m_pRecvBuffer;
      m_pRecvBuffer = 0;

      delete m_pSocket;
      m_pSocket = 0;
      if (m_pHostResolver)
      {
            m_pHostResolver->SetObsolete();
            m_pHostResolver->Wait();
            delete m_pHostResolver;
      }
}

void CExternalIPResolver::GetExternalIP(const wxString& address /*=_T("")*/, bool force /*=false*/)
{
      if (m_checked)
      {
            if (force)
                  m_checked = false;
            else
            {
                  m_done = true;
                  return;
            }
      }

      m_address = address;

      wxString host;
      int pos;
      if ((pos = address.Find(_T("://"))) != -1)
            host = address.Mid(pos + 3);
      else
            host = address;

      if ((pos = host.Find(_T("/"))) != -1)
            host = host.Left(pos);

      wxString hostWithPort = host;

      if ((pos = host.Find(':', true)) != -1)
      {
            wxString port = host.Mid(pos + 1);
            if (!port.ToULong(&m_port) || m_port < 1 || m_port > 65535)
                  m_port = 80;
            host = host.Left(pos);
      }
      else
            m_port = 80;

      if (host == _T(""))
      {
            m_done = true;
            return;
      }

      m_pHostResolver = new CAsyncHostResolver(this, host);
      m_pHostResolver->Create();
      m_pHostResolver->Run();

      wxString buffer = wxString::Format(_T("GET %s HTTP/1.1\r\nHost: %s\r\nUser-Agent: %s\r\nConnection: close\r\n\r\n"), address.c_str(), hostWithPort.c_str(), wxString(PACKAGE_STRING, wxConvLocal).c_str());
      m_pSendBuffer = new char[strlen(buffer.mb_str()) + 1];
      strcpy(m_pSendBuffer, buffer.mb_str());
}

void CExternalIPResolver::OnAsyncHostResolver(fzAsyncHostResolveEvent& event)
{
      if (!m_pHostResolver)
            return;

      if (!m_pHostResolver->Done())
            return;

      if (!m_pHostResolver->Successful())
      {
            Close(false);
            return;
      }

      m_pHostResolver->Wait();

      if (m_pSocket)
      {
            delete m_pHostResolver;
            m_pHostResolver = 0;
            return;
      }

      m_pSocket = new wxSocketClient(wxSOCKET_NOWAIT);
      m_pSocket->SetEventHandler(*this, 0);
      m_pSocket->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_OUTPUT_FLAG | wxSOCKET_CONNECTION_FLAG | wxSOCKET_LOST_FLAG);
      m_pSocket->Notify(true);

      m_pHostResolver->m_Address.Service(m_port);
      m_pSocket->Connect(m_pHostResolver->m_Address, false);

      delete m_pHostResolver;
      m_pHostResolver = 0;
}

void CExternalIPResolver::OnSocketEvent(wxSocketEvent& event)
{
      if (!m_pSocket)
            return;

      switch (event.GetSocketEvent())
      {
      case wxSOCKET_INPUT:
            OnReceive();
            break;
      case wxSOCKET_CONNECTION:
            OnConnect();
            break;
      case wxSOCKET_LOST:
            OnClose();
            break;
      case wxSOCKET_OUTPUT:
            OnSend();
            break;
      }

}

void CExternalIPResolver::OnConnect()
{
}

void CExternalIPResolver::OnClose()
{
      if (m_data != _T(""))
            OnData(0, 0);
      else
            Close(false);
}

void CExternalIPResolver::OnReceive()
{
      if (!m_pRecvBuffer)
      {
            m_pRecvBuffer = new char[m_recvBufferLen];
            m_recvBufferPos = 0;
      }

      if (m_pSendBuffer)
            return;

      unsigned int len = m_recvBufferLen - m_recvBufferPos;
      m_pSocket->Read(m_pRecvBuffer + m_recvBufferPos, len);
      if (m_pSocket->Error())
      {
            if (m_pSocket->LastError() != wxSOCKET_WOULDBLOCK)
            {
                  Close(false);
            }
            return;
      }

      int read;
      if (!(read = m_pSocket->LastCount()))
      {
            Close(false);
            return;
      }

      if (m_finished)
      {
            // Just ignore all further data
            m_recvBufferPos = 0;
            return;
      }

      m_recvBufferPos += read;

      if (!m_gotHeader)
            OnHeader();
      else
      {
            if (m_transferEncoding == chunked)
                  OnChunkedData();
            else
                  OnData(m_pRecvBuffer, m_recvBufferPos);
      }
}

void CExternalIPResolver::OnSend()
{
      if (!m_pSendBuffer)
            return;

      unsigned int len = strlen(m_pSendBuffer + m_sendBufferPos);
      m_pSocket->Write(m_pSendBuffer + m_sendBufferPos, len);
      if (m_pSocket->Error())
      {
            if (m_pSocket->LastError() != wxSOCKET_WOULDBLOCK)
            {
                  Close(false);
            }
            return;
      }

      unsigned int written;
      if (!(written = m_pSocket->LastCount()))
      {
            Close(false);
            return;
      }

      if (written == len)
      {
            delete [] m_pSendBuffer;
            m_pSendBuffer = 0;

            OnReceive();
      }
      else
            m_sendBufferPos += written;
}

void CExternalIPResolver::Close(bool successful)
{
      delete [] m_pSendBuffer;
      m_pSendBuffer = 0;

      delete [] m_pRecvBuffer;
      m_pRecvBuffer = 0;

      delete m_pSocket;
      m_pSocket = 0;

      if (m_done)
            return;

      m_done = true;

      if (!successful)
            m_ip = _T("");
      m_checked = true;

      if (m_handler)
      {
            fzExternalIPResolveEvent event;
            wxPostEvent(m_handler, event);
            m_handler = 0;
      }
}

void CExternalIPResolver::OnHeader()
{
      // 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')
                        {
                              Close(false);
                              return;
                        }
                        break;
                  }
            }
            if ((i + 1) >= m_recvBufferPos)
            {
                  if (m_recvBufferPos == m_recvBufferLen)
                  {
                        // We don't support header lines larger than 4096
                        Close(false);
                        return;
                  }
                  return;
            }

            m_pRecvBuffer[i] = 0;

            if (!m_responseCode)
            {
                  m_responseString = wxString(m_pRecvBuffer, wxConvLocal);
                  if (m_recvBufferPos < 16 || memcmp(m_pRecvBuffer, "HTTP/1.", 7))
                  {
                        // Invalid HTTP Status-Line
                        Close(false);
                        return;
                  }

                  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
                        Close(false);
                        return;
                  }

                  m_responseCode = (m_pRecvBuffer[9] - '0') * 100 + (m_pRecvBuffer[10] - '0') * 10 + m_pRecvBuffer[11] - '0';

                  if (m_responseCode >= 400)
                  {
                        // Failed request
                        Close(false);
                        return;
                  }

                  if (m_responseCode == 305)
                  {
                        // Unsupported redirect
                        Close(false);
                        return;
                  }
            }
            else
            {
                  if (!i)
                  {
                        // End of header, data from now on

                        // Redirect if neccessary
                        if (m_responseCode >= 300)
                        {
                              delete m_pSocket;
                              m_pSocket = 0;

                              delete [] m_pRecvBuffer;
                              m_pRecvBuffer = 0;

                              wxString location = m_location;

                              ResetHttpData(false);

                              GetExternalIP(location);
                              return;
                        }

                        m_gotHeader = true;

                        memmove(m_pRecvBuffer, m_pRecvBuffer + 2, m_recvBufferPos - 2);
                        m_recvBufferPos -= 2;
                        if (m_recvBufferPos)
                        {
                              if (m_transferEncoding == chunked)
                                    OnChunkedData();
                              else
                                    OnData(m_pRecvBuffer, m_recvBufferPos);
                        }
                        return;
                  }
                  if (m_recvBufferPos > 12 && !memcmp(m_pRecvBuffer, "Location: ", 10))
                  {
                        m_location = wxString(m_pRecvBuffer + 10, wxConvLocal);
                  }
                  else if (m_recvBufferPos > 21 && !memcmp(m_pRecvBuffer, "Transfer-Encoding: ", 19))
                  {
                        if (!strcmp(m_pRecvBuffer + 19, "chunked"))
                              m_transferEncoding = chunked;
                        else if (!strcmp(m_pRecvBuffer + 19, "identity"))
                              m_transferEncoding = identity;
                        else
                              m_transferEncoding = unknown;
                  }
            }

            memmove(m_pRecvBuffer, m_pRecvBuffer + i + 2, m_recvBufferPos - i - 2);
            m_recvBufferPos -= i + 2;

            if (!m_recvBufferPos)
                  break;
      }
}

void CExternalIPResolver::OnData(char* buffer, unsigned int len)
{
      if (buffer)
      {
            unsigned int i;
            for (i = 0; i < len; i++)
            {
                  if (buffer[i] == '\r' || buffer[i] == '\n')
                        break;
            }

            if (i)
                  m_data += wxString(buffer, wxConvLocal, i);

            if (i == len)
                  return;
      }

      // Validate ip address
      wxString digit = _T("0*[0-9]{1,3}");
      const wxChar* dot = _T("\\.");
      wxString exp = _T("(^|[^\\.[:digit:]])(") + digit + dot + digit + dot + digit + dot + digit + _T(")([^\\.[:digit:]]|$)");
      wxRegEx regex;
      regex.Compile(exp);

      if (!regex.Matches(m_data))
      {
            Close(false);
            return;
      }

      m_ip = regex.GetMatch(m_data, 2);
      Close(true);
}

void CExternalIPResolver::ResetHttpData(bool resetRedirectCount)
{
      m_gotHeader = false;
      m_location = _T("");
      m_responseCode = 0;
      m_responseString = _T("");
      if (resetRedirectCount)
            m_redirectCount = 0;

      m_transferEncoding = unknown;

      m_chunkData.getTrailer = false;
      m_chunkData.size = 0;
      m_chunkData.terminateChunk = false;

      m_finished = false;
}

void CExternalIPResolver::OnChunkedData()
{
      char* p = m_pRecvBuffer;
      unsigned int len = m_recvBufferPos;

      while (true)
      {
            if (m_chunkData.size != 0)
            {
                  unsigned int dataLen = len;
                  if (m_chunkData.size < len)
                        dataLen = m_chunkData.size.GetLo();
                  OnData(p, dataLen);
                  if (!m_pRecvBuffer)
                        return;

            m_chunkData.size -= dataLen;
                  p += dataLen;
                  len -= dataLen;

                  if (m_chunkData.size == 0)
                        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')
                        {
                              Close(false);
                              return;
                        }
                        break;
                  }
            }
            if ((i + 1) >= len)
            {
                  if (len == m_recvBufferLen)
                  {
                        // We don't support lines larger than 4096
                        Close(false);
                        return;
                  }
                  break;
            }

            p[i] = 0;

            if (m_chunkData.terminateChunk)
            {
                  if (i)
                  {
                        // Chunk has to end with CRLF
                        Close(false);
                        return;
                  }
                  m_chunkData.terminateChunk = false;
            }
            else if (m_chunkData.getTrailer)
            {
                  if (!i)
                  {
                        m_finished = true;
                        m_recvBufferPos = 0;
                        return;
                  }

                  // Ignore the trailer
            }
            else
            {
                  // Read chunk size
                  char* q = p;
                  while (*q)
                  {
                        if (*q >= '0' && *q <= '9')
                        {
                              m_chunkData.size *= 16;
                              m_chunkData.size += *q - '0';
                        }
                        else if (*q >= 'A' && *q <= 'F')
                        {
                              m_chunkData.size *= 10;
                              m_chunkData.size += *q - 'A' + 10;
                        }
                        else if (*q >= 'a' && *q <= 'f')
                        {
                              m_chunkData.size *= 10;
                              m_chunkData.size += *q - 'a' + 10;
                        }
                        else if (*q == ';' || *q == ' ')
                              break;
                        else
                        {
                              // Invalid size
                              Close(false);
                              return;
                        }
                        q++;
                  }
                  if (m_chunkData.size == 0)
                        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;
      }
}

Generated by  Doxygen 1.6.0   Back to index