#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "TSServer.h"

#ifdef TS_USE_MFC
#else

#ifndef TS_USE_WINSOCK
#ifndef TS_USE_WININET
#include <errno.h>
#include <netdb.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#endif

BOOL CSocket::Create( unsigned int socketPort,
                      int socketType,
                      const char* socketAddress )
{
	if ( Socket( socketType ))
	{
		if ( Bind( socketPort,socketAddress ))
    {
			return TRUE;
    }
#ifdef TS_USE_WINSOCK
		int result = GetLastError();
#endif
		Close();
#ifdef TS_USE_WINSOCK
		WSASetLastError( result );
#endif
	}
	return FALSE;
}

BOOL CSocket::Bind( unsigned int socketPort,
                    const char* socketAddress )
{
	SOCKADDR_IN sockAddr;
	memset( &sockAddr, 0, sizeof( sockAddr ));
	//LPSTR lpszAscii = T2A((LPTSTR)lpszSocketAddress);
	sockAddr.sin_family = AF_INET;
	if ( socketAddress == NULL )
  {
		sockAddr.sin_addr.s_addr = htonl( INADDR_ANY );
  }
	else
	{
		unsigned long lResult = inet_addr( socketAddress );
		if ( lResult == INADDR_NONE )
		{
#ifdef TS_USE_WINSOCK
			WSASetLastError( WSAEINVAL );
#endif
			return FALSE;
		}
		sockAddr.sin_addr.s_addr = lResult;
	}
	sockAddr.sin_port = htons(( u_short )socketPort );
  return ( SOCKET_ERROR != bind( m_hSocket, (SOCKADDR*)&sockAddr, sizeof( sockAddr )));
}

BOOL CSocket::Socket( int socketType,
                     	int protocolType,
                      int addressFormat )
{
	m_hSocket = socket( addressFormat, socketType, protocolType );
	if ( m_hSocket != INVALID_SOCKET )
	{
    return TRUE;
	}
  return FALSE;
}

BOOL CSocket::Connect( const char* hostAddress,
                       unsigned int portNumber )
{
	SOCKADDR_IN sockAddr;
	memset( &sockAddr, 0, sizeof( sockAddr ));
	//LPSTR lpszAscii = T2A((LPTSTR)hostAddress);
	sockAddr.sin_family = AF_INET;
	sockAddr.sin_addr.s_addr = inet_addr( hostAddress );
	if ( sockAddr.sin_addr.s_addr == INADDR_NONE )
	{
		LPHOSTENT lphost;
		lphost = gethostbyname( hostAddress );
		if ( lphost != NULL )
    {
			sockAddr.sin_addr.s_addr = (( LPIN_ADDR )lphost->h_addr )->s_addr;
    }
		else
		{
#ifdef TS_USE_WINSOCK
			WSASetLastError( WSAEINVAL );
#endif
			return FALSE;
		}
	}
	sockAddr.sin_port = htons(( u_short )portNumber );
	return Connect( (SOCKADDR*)&sockAddr, sizeof( sockAddr ));
}

BOOL CSocket::Connect( const SOCKADDR* lpSockAddr,
                       int sockAddrLen )
{
	if ( connect( m_hSocket, lpSockAddr, sockAddrLen ) == SOCKET_ERROR )
	{
		return FALSE;
	}
	return TRUE;
}

void CSocket::Close()
{
#ifdef WIN32
  closesocket( m_hSocket );
#else
  close( m_hSocket );
#endif
}

int CSocket::Send( const void* lpBuf,
                   int nBufLen,
                   int nFlags )
{
	int nLeft, nWritten;
	char* pBuf = (char *)lpBuf;
	nLeft = nBufLen;
	while ( nLeft > 0 )
	{
		nWritten = send( m_hSocket, static_cast<const char*>( lpBuf ), nBufLen, nFlags );
		if ( nWritten == SOCKET_ERROR )
    {
			return nWritten;
    }
		nLeft -= nWritten;
		pBuf += nWritten;
	}
	return nBufLen - nLeft;
}

int CSocket::Receive( void* lpBuf,
                      int nBufLen,
                      int nFlags )
{
  return recv( m_hSocket, static_cast<char*>( lpBuf ), nBufLen, nFlags );
}

int CSocket::GetLastError()
{
#ifdef WIN32
  return WSAGetLastError();
#else
  return errno;
#endif
}

#endif

TSSocket::TSSocket() :
  socketBuffer      ( NULL ),
  socketBufferSize  ( 8196 ),
  bufferStart       ( 0 ),
  bufferEnd         ( 0 ),
  connected         ( FALSE ),
#ifdef TS_USE_WININET
  hOpen             ( NULL ),
  hConnect          ( NULL ),
  hRequest          ( NULL ),
#endif
  protocolString    ( NULL ),
  directoryName     ( NULL ),
  dllName           ( NULL ),
  authString        ( NULL ),
  proxyString       ( NULL )
{
  Initialize();
}

TSSocket::~TSSocket()
{
  if ( connected )
  {
    Close();
  }
  if ( socketBuffer )
  {
    free( socketBuffer );
  }
}
BOOL TSSocket::Create(  const char* protocol, const char* directory,
                        const char* dll,      const char* auth,
                        const char* proxy )
{
  bufferStart     = 0;
  bufferEnd       = 0;
  protocolString  = protocol;
  directoryName   = directory;
  dllName         = dll;
  authString      = auth;
  proxyString     = proxy;

#ifdef TS_USE_WININET
  hOpen     = NULL;
  hConnect  = NULL;
  hRequest  = NULL;
  return TRUE;
#else
  return CSocket::Create();
#endif
}

void TSSocket::Initialize()
{
  bufferStart = bufferEnd = 0;
  socketBuffer = static_cast<char*>( malloc( socketBufferSize ));
}

BOOL TSSocket::Connect( const char*  hostAddress,
                        unsigned int portNumber )
{
  BOOL  retval = FALSE;

#ifdef TS_USE_WININET
  // Initializes the use of the Windows Internet functions
  if ( ( proxyString != NULL ) && ( *proxyString != '\0' ) )
  {
    hOpen = InternetOpen( "TeamTrack API", INTERNET_OPEN_TYPE_PROXY, proxyString, 0, 0 );
  }
  else
  {
    hOpen = InternetOpen( "TeamTrack API", INTERNET_OPEN_TYPE_DIRECT, NULL, 0, 0 );
  }
  if (hOpen)
  {
    // Opens an HTTP session for a given site 
    hConnect = InternetConnect( hOpen, hostAddress, portNumber, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0 );
    if ( hConnect )
    {
      retval = TRUE;
    }
  }
#else
  retval = CSocket::Connect(hostAddress, portNumber);
#endif
  if(retval)
  {
    connected = TRUE;
  }
  return retval;
}

void TSSocket::Close()
{
#ifdef TS_USE_WININET
  if ( hOpen )
  {
    InternetCloseHandle( hOpen );
    hOpen = NULL;
  }
  
  if ( hConnect )
  {
    InternetCloseHandle( hConnect );
    hConnect = NULL;
  }

  if ( hRequest )
  {
    InternetCloseHandle( hRequest ); 
    hRequest = NULL;
  }
#else
  CSocket::Close();
#endif
}

int TSSocket::Send(const void* lpBuf, int nBufLen, int nFlags)
{
  TSString header;
  char lengthStr[64];

  sprintf( lengthStr, "Content-length: %d\r\n\r\n", nBufLen);

#ifdef TS_USE_WININET
  DWORD dwError;
  DWORD dwFlags;
  TSString object;

  // Build the header, object and flags
  header = authString;
  header += "\r\n";
  header += lengthStr;

  object = "/";
  object += this->directoryName;
  object += "/";
  object += this->dllName;
  object += "APIPage";

  dwFlags = (INTERNET_FLAG_RELOAD|INTERNET_FLAG_NO_UI|INTERNET_FLAG_NO_CACHE_WRITE);
  if ( strcmp( protocolString, "https" ) == 0 )
  {
    dwFlags |= (INTERNET_FLAG_SECURE                    |
                  INTERNET_FLAG_IGNORE_CERT_CN_INVALID    |
                  INTERNET_FLAG_IGNORE_CERT_DATE_INVALID  |
                  INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP   |
                  INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS );
  }

  // Send http request
  while ( 1 ) 
  {
    if ( !hRequest )
    {
      // Opens an HTTP request handle
      hRequest = HttpOpenRequest( hConnect, "POST", object, NULL, NULL, NULL, dwFlags, 0 );
      if ( !hRequest )
      {
        return SOCKET_ERROR;
      }
    }
  
    if ( !HttpSendRequest( hRequest, header.GetBuffer(), header.Length(), (LPVOID)lpBuf, nBufLen ) )
    {
      dwError = GetLastError();
      switch ( dwError )
      {
        case ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED:
        case ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR:
        case ERROR_INTERNET_INCORRECT_PASSWORD: 
        case ERROR_INTERNET_INVALID_CA: 
        case ERROR_INTERNET_POST_IS_NON_SECURE: 
        case ERROR_INTERNET_SEC_CERT_CN_INVALID: 
        case ERROR_INTERNET_SEC_CERT_DATE_INVALID: 
          if ( InternetErrorDlg( ::GetDesktopWindow(), hRequest, dwError, 
                FLAGS_ERROR_UI_FILTER_FOR_ERRORS |
                FLAGS_ERROR_UI_FLAGS_GENERATE_DATA |
                FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS, 
                NULL ) == ERROR_SUCCESS )
          {
            // BUG here - this dialog returns ERROR_SUCCESS whether OK or Cancel was pressed
            char line[1024];
            while ( ReadLine ( line, 1024 ) );
          }
          else
          {
            return SOCKET_ERROR;
          }
          break;
        default:
          return SOCKET_ERROR;
      }
    }
    else
    {
      return nBufLen;
    }

    // Close the request handle so it can be re-opened at the top of the loop
    InternetCloseHandle( hRequest );
    hRequest = NULL;
  }

  return nBufLen;
#else
  header = "POST /";
  header += directoryName;
  header += "/";
  header += dllName;
  header += "APIPage HTTP/1.0\r\nContent-Type: html/text\r\n";
  header += authString;
  header += "\r\n";
  header += lengthStr;
  header += (const char*)lpBuf;
  header += "\r\n";
  
  return CSocket::Send( header.GetBuffer(), header.Length(), nFlags );
#endif
}

BOOL TSSocket::GetHttpStatusCode( int& code )
{
  code = -1;
  char line[1024];

#ifdef TS_USE_WININET
  DWORD dwSize = sizeof(line);
  if ( !HttpQueryInfo( hRequest, HTTP_QUERY_STATUS_CODE, line, &dwSize, NULL ) )
  {
    return FALSE;
  }
  code = atoi( line );
#else
  BOOL bFirst = TRUE;
  BOOL bError = FALSE;

  while( ReadLine( line, sizeof(line) ) > 0 && strlen( line ) != 0 )
  {
    if ( bFirst )
    {
      sscanf( line, "%*s %d", &code );
      if( code != 200 )
      {
        bError = TRUE;
      }
    }
    bFirst = FALSE;
  }

  if ( bError || bFirst )
  {
    return FALSE;
  }
#endif
  return TRUE;
}

// This works just like read except that the \n are stripped off
// because that is what is convenient for out uses.
int TSSocket::ReadLine( char* buffer,
                        int   size )
{
  int nRead = 0;
  for ( int ii = 0; ii < size; ii++ )
  {
    if ( bufferStart == bufferEnd )
    {
      if ( ( nRead = FillBuffer()) == 0 )
      {
        return ii-1;
      }
    }

    if ( socketBuffer[bufferStart] == '\r' )
    {
      bufferStart++;
      ii--;
      continue;
    }
    else if ( socketBuffer[bufferStart] == '\n' )
    {
      bufferStart++;
      if ( ii < size )
      {
        buffer[ii] = '\0';
      }

      return ii;
    }

    buffer[ii] = socketBuffer[bufferStart];
    bufferStart++;
  }

  return nRead;
}

int TSSocket::Read( char* buffer, 
                    int   size )
{

  int bufferSize = bufferEnd - bufferStart;
  if ( bufferSize == size )
  {
    memcpy( buffer, socketBuffer, size );
    bufferStart = bufferEnd = 0;
    return size;
  }
  else if ( bufferSize < size )
  {
    if ( bufferSize != 0 )
    {
      memcpy( buffer, socketBuffer, bufferSize );
      bufferStart = bufferEnd = 0;
    }
    if ( FillBuffer() > 0 )
    {
      return bufferSize + Read( buffer + bufferSize, size - bufferSize );
    }
    else
    {
      return bufferSize;
    }
  }
  else // bufferSize > size
  {
    memcpy( buffer, socketBuffer, size );
    bufferStart += size;
    return size;
  }
}

char TSSocket::ReadChar()
{
  if ( bufferStart == bufferEnd )
  {
    if ( FillBuffer() == 0 )
    {
      return 0;
    }
  }
  return socketBuffer[bufferStart++];
}

int TSSocket::FillBuffer()
{
  if ( bufferStart != bufferEnd )
  {
    return 0;
  }
  bufferStart = 0;
#ifdef TS_USE_WININET
  BOOL          bValidRead;
  unsigned long nMessageLength;
  char          pcMessage[256];

  // Read more data
  bValidRead = InternetReadFile( hRequest, (LPVOID)socketBuffer, socketBufferSize, (unsigned long*)&bufferEnd );
  
  // The following 2 if statements were put in place to help debug BUG05359
  if ( !bValidRead )
  {
    unsigned long nError = ::GetLastError();
    nMessageLength = sizeof( pcMessage );
    ::InternetGetLastResponseInfo( &nError, pcMessage, &nMessageLength );
    bufferEnd = 0;
  }
  if ( bufferEnd < 0 )
  {
    nMessageLength = sizeof( pcMessage );
    HttpQueryInfo( hRequest, HTTP_QUERY_CONTENT_LENGTH, pcMessage, &nMessageLength, NULL );
    bufferEnd = 0;
  }
#else
  bufferEnd = Receive(socketBuffer, socketBufferSize, 0);
  if ( bufferEnd == SOCKET_ERROR || bufferEnd < 0 )
  {
    int WSAerror = WSAGetLastError();
    printf("Received a socket error: %d\r\n", WSAerror);
    bufferEnd = 0;
  }
#endif

  return bufferEnd;
}

int TSSocket::ReceiveInt( int* val )
{
  char ch;
  TSString buf = "";
  *val = 0;
  while (( ch = ReadChar() ) != 0 )
  {
    if ( ch == '\n' || ch == '\r' || ch == ',' )
    {
      break;
    }
    buf += ch;
  }
  if ( buf == "" )
  {
    return TS_ERROR;
  }
  *val = atol( buf.GetBuffer() );
  return TS_OK;
}

int TSSocket::ReceiveDouble( double* val )
{
  char ch;
  TSString buf = "";
  *val = 0.0;
  while (( ch = ReadChar() ) != 0 )
  {
    if ( ch == '\n' || ch == '\r' || ch == ',' )
    {
      break;
    }
    if ( ch == '$' )
    {
      char temp[3];
      if (( temp[0] = ReadChar() ) == 0 )
      {
        return TS_OK;
      }
      if (( temp[1] = ReadChar() ) == 0 )
      {
        return TS_OK;
      }
      temp[2] = '\0';
      int charVal;
      sscanf( temp, "%x", &charVal );
      buf += (char)charVal;
    }
    else
    {
      buf += ch;
    }
  }
  if ( buf == "" )
  {
    return TS_ERROR;
  }
  *val = strtod( buf.GetBuffer(), NULL );
  return TS_ERROR;
}

int TSSocket::ReceiveString( char* str, int size )
{
  TSString tmp;
  ReceiveString( &tmp );
  strncpy( str, tmp.GetBuffer(), size );
  str[size-1] = '\0';
  return TS_OK;
}

int TSSocket::ReceiveString( TSString* str )
{
  char ch;
  if (( ch = ReadChar()) == 0 )
  {
    return TS_ERROR;
  }
  if ( ch == '\r' || ch == '\n' )
  {
    ReadChar();
    return TS_ERROR;
  }
  if ( ch != '"' )
  {
    return TS_ERROR;
  }
  while (( ch = ReadChar() ) != 0 )
  {
    if ( ch == '"' )
    {
      ReadChar(); // Read the separating comma
      return TS_OK;
    }
    if ( ch == '$' )
    {
      char temp[3];
      if (( temp[0] = ReadChar() ) == 0 )
      {
        return TS_OK;
      }
      if (( temp[1] = ReadChar() ) == 0 )
      {
        return TS_OK;
      }
      temp[2] = '\0';
      int val;
      sscanf( temp, "%x", &val );
      (*str) += static_cast<char>( val );
    }
    else
    {
      (*str) += ch;
    }
  }
  // no ending quote
  return TS_ERROR;
}

// Encodes a string to be sent through the web server enclosed in quotes.
void TSEncodeString( TSString& in, TSString& out )
{
  TSString text;
  TSEncodeText( in, text );
  out = "\"";
  out += text;
  out += "\",";
}

void TSEncodeString( const char* in,
                     TSString&   out )
{
  static TSString tmp;
  if ( in == NULL )
  {
    tmp = "";
  }
  else
  {
    tmp = in;
  }
  TSEncodeString( tmp, out );
}

// Encodes a string to be sent through the web server, but doesn't enclose
// it in quotation marks.
void TSEncodeText( const char* in,
                   TSString&   out )
{
  static TSString tmp;
  tmp = in;
  TSEncodeText( tmp, out );
}

void TSEncodeText( TSString& in,
                   TSString& out )
{
  char encode[16];
  char* p = in.GetBuffer();
  out = "";
  while ( *p )
  {
    if ( isalnum( *p ) || *p == '-' ||
             *p == '_' || *p == '.' || *p == 13 || *p == 10 )
    {
      out += ( *p );
    }
    else
    {
      int n = 0x000000FF & *p;  // stop it from sign-extending upper ASCII codes
      sprintf( encode, "$%02x", n );
      out += encode;
    }
    p++;
  }
}

void TSEncodeInt( int       in,
                  TSString& out )
{
  static char tmp[24];
  sprintf( tmp, "%d,", in );
  out = tmp;
}

void TSEncodeDouble( double    in,
                     TSString& out )
{
  static char tmp[32];
  sprintf( tmp, "%.16g,", in );
  out = tmp;
}

#if defined( TS_USE_WINSOCK ) || defined( TS_USE_WININET )
int AFX_EXT_CLASS TSInitializeWinsock()
{
// This is the Windows socket startup stuff to initialize the Winsock DLL.
#ifdef TS_USE_MFC
  if ( !AfxSocketInit() )
  {
    printf( "Error intializing winsock!\n" );
    return TS_ERROR;
  }
  printf( "Winsock initialized\n" );
#else
  WORD wVersionRequested;
  WSADATA wsaData;
  int err;
  wVersionRequested = MAKEWORD( 2, 2 );
  err = WSAStartup( wVersionRequested, &wsaData );
  if ( err != 0 )
  {
    return TS_ERROR;
  }
#endif
  return TS_OK;
}
#endif

