//  Beta release only.
//
//  Copyright(c) Ivyware Pty Ltd  2002-2007
//               MELBOURNE, VICTORIA, AUSTRALIA, 3130
//
//  Description: P2Peer implementation.
//               NOTES: Proprietary and private implementation not
//                      for use or release without prior written
//                      authorisation.
//

#include "StdAfx.h"
#include "P2Peer.h"

//
//  Description:
//
//
//  Parameters:  char *szHostname
//               Name of host whose IP address is to be determined
//
//  Returns:     DWORD
//               IP address
//
DWORD
GetHostID ( )
{
   char szLclHost[120];
   LPHOSTENT lpstHostent;
   SOCKADDR_IN stLclAddr;
   SOCKADDR_IN stRmtAddr;
   int nAddrSize = sizeof(SOCKADDR);
   SOCKET hSock;
   int nRet;
   
   /* Init local address (to zero) */
   stLclAddr.sin_addr.s_addr = INADDR_ANY;

   /* Get the local hostname */
   nRet = gethostname(szLclHost, sizeof(szLclHost));
   if (nRet != SOCKET_ERROR)
      {
      /* Resolve hostname for local address */
      lpstHostent = gethostbyname((LPSTR)szLclHost);
      if (lpstHostent)
         stLclAddr.sin_addr.s_addr = *((u_long FAR*)
(lpstHostent->h_addr));
      }
   
   /* If still not resolved, then try second strategy */
   if (stLclAddr.sin_addr.s_addr == INADDR_ANY)
      {
      /* Get a UDP socket */
      hSock = socket(AF_INET, SOCK_DGRAM, 0);
      if (hSock != INVALID_SOCKET)
         {
         /* Connect to arbitrary port and address (NOT loopback) */
         stRmtAddr.sin_family = AF_INET;
         stRmtAddr.sin_port   = htons(IPPORT_ECHO);
         stRmtAddr.sin_addr.s_addr = inet_addr("128.127.50.1");
         nRet = connect(hSock, (LPSOCKADDR)&stRmtAddr,
sizeof(SOCKADDR));
         if (nRet != SOCKET_ERROR)
            {
            /* Get local address */
            getsockname(hSock, (LPSOCKADDR)&stLclAddr, (int
FAR*)&nAddrSize);
            }
         closesocket(hSock);   /* we're done with the socket */
         }
      }
   return stLclAddr.sin_addr.s_addr;
}

//
//
//  Description: Static function for the retrieval of WSA error
//               descriptions
//               NOTES: WSABASEEER
//
//  Parameters:  HRESULT hr
//               WSA error code.
//
//
//  Returns:     LPCTSTR
//               Error text associated with above code
//                 0.. Text not located
//
//LPCTSTR
//P2PeerWSAEDesc ( HRESULT hr )
//{
//    if ( hr == WSAEINTR )
//      return "Interrupted function call";
//    if ( hr == WSAEBADF )
//      return "WSAEBADF";
//    if ( hr == WSAEACCES )
//      return "Permission denied";
//    if ( hr == WSAEFAULT )
//      return "Bad address";
//    if ( hr == WSAEINVAL )
//      return "Invalid argument";
//    if ( hr == WSAEMFILE )
//      return "Too many open files";
//
//    // Windows Sockets definitions of regular Berkeley
//    // error constants
//    if ( hr == WSAEWOULDBLOCK )
//      return "Resource temporarily unavailable";
//    if ( hr == WSAEINPROGRESS )
//      return "Operation now in progress";
//    if ( hr == WSAEALREADY )
//      return "Operation already in progress";
//    if ( hr == WSAENOTSOCK )
//      return "Socket operation on non-socket";
//    if ( hr == WSAEDESTADDRREQ )
//      return "Destination address required";
//    if ( hr == WSAEMSGSIZE )
//      return "Message too long";
//    if ( hr == WSAEPROTOTYPE )
//      return "Protocol wrong type for socket";
//    if ( hr == WSAENOPROTOOPT )
//      return "Bad protocol option";
//    if ( hr == WSAEPROTONOSUPPORT )
//      return "Protocol not supported";
//    if ( hr == WSAESOCKTNOSUPPORT )
//      return "Socket type not supported";
//    if ( hr == WSAEOPNOTSUPP )
//      return "Operation not supported";
//    if ( hr == WSAEPFNOSUPPORT )
//      return "Protocol family not supported";
//    if ( hr == WSAEAFNOSUPPORT )
//      return "Address family not supported by protocol family";
//    if ( hr == WSAEADDRINUSE )
//      return "Address already in use";
//    if ( hr == WSAEADDRNOTAVAIL )
//      return "Cannot assign requested address";
//    if ( hr == WSAENETDOWN )
//      return "Network is down";
//    if ( hr == WSAENETUNREACH )
//      return "Network is unreachable";
//    if ( hr == WSAENETRESET )
//      return "Network dropped connection on reset";
//    if ( hr == WSAECONNABORTED )
//      return "Software caused connection abort";
//    if ( hr == WSAECONNRESET )
//      return "Connection reset by peer";
//    if ( hr == WSAENOBUFS )
//      return "No buffer space available";
//    if ( hr == WSAEISCONN )
//      return "Socket is already connected";
//    if ( hr == WSAENOTCONN )
//      return "Socket is not connected";
//    if ( hr == WSAESHUTDOWN )
//      return "Cannot send after socket shutdown";
//    if ( hr == WSAETOOMANYREFS )
//      return "WSATOOMANYREFS";
//    if ( hr == WSAETIMEDOUT )
//      return "Connection timed out";
//    if ( hr == WSAECONNREFUSED )
//      return "Connection refused";
//    if ( hr == WSAELOOP )
//      return "WSAELOOP";
//    if ( hr == WSAENAMETOOLONG )
//      return "WSAENAMETOOLONG";
//    if ( hr == WSAEHOSTDOWN )
//      return "Host is down";
//    if ( hr == WSAEHOSTUNREACH )
//      return "No route to host";
//    if ( hr == WSAENOTEMPTY )
//      return "WSAENOTEMPTY";
//    if ( hr == WSAEPROCLIM )
//      return "Too many processes";
//    if ( hr == WSAEUSERS )
//      return "WSAEUSERS";
//    if ( hr == WSAEDQUOT )
//      return "WSAEDQUOT";
//    if ( hr == WSAESTALE )
//      return "WSAESTALE";
//    if ( hr == WSAEREMOTE )
//      return "WSAEREMOTE";
//    if ( hr == WSAEDISCON )
//      return "WSAEDISCON";
//
////  Extended Windows Sockets error constant definitions
//    if ( hr == WSASYSNOTREADY )
//      return "WSASYSNOTREADY";
//    if ( hr == WSAVERNOTSUPPORTED )
//      return "WINSOCK.DLL version out of range";
//    if ( hr == WSANOTINITIALISED )
//      return "Successful WSAStartup not yet performed";
//    if ( hr == WSAEDISCON )
//      return "Graceful shutdown in progress";
//
//    // Whatever
//    if ( hr == WSATYPE_NOT_FOUND )
//      return "Class type not found";
//    if ( hr == WSAHOST_NOT_FOUND )
//      return "Host not found";
//    if ( hr == WSA_INVALID_HANDLE )
//      return "Specified event object handle is invalid";
//    if ( hr == WSA_INVALID_PARAMETER )
//      return "One or more parameters are invalid";
////  if ( hr == WSAINVALIDPROCTABLE )
////    return "Invalid procedure table from service provider";
////  if ( hr == WSAINVALIDPROVIDER )
////    return "Invalid service provider version number";
//    if ( hr == WSA_IO_INCOMPLETE )
//      return "Overlapped I/O event object not in signaled state";
//    if ( hr == WSA_IO_PENDING )
//      return "Overlapped operations will complete later";
//    if ( hr == WSA_NOT_ENOUGH_MEMORY )
//      return "Insufficient memory available";
//    if ( hr == WSANO_DATA )
//      return "Valid name, no data record of requested type";
//    if ( hr == WSANO_RECOVERY )
//      return "This is a non-recoverable error";
////  if ( hr == WSAPROVIDERFAILEDINIT )
////    return "Unable to initialize a service provider";
//    if ( hr == WSASYSCALLFAILURE )
//      return "System call failure";
//    if ( hr == WSASYSNOTREADY )
//      return "Network subsystem is unavailable";
//    if ( hr == WSATRY_AGAIN )
//      return "Non-authoritative host not found";
//    if ( hr == WSA_OPERATION_ABORTED )
//      return "Overlapped operation aborted";
//
//    // Tidy up and
//    return 0;
//}
//
//


///////////////////////////////////////////////////////////////////////
//  P2Paddr implementation
//  NOTES: Support formats of the form
//         VNetname:Hubname.Hubname[i]. . .Hubname etc

//  Constructors and destructors
P2Paddr::P2Paddr ( )
{ }
P2Paddr::P2Paddr ( const P2Paddr& rhs )
{   m_sP2Paddr = rhs.m_sP2Paddr; }
P2Paddr::P2Paddr ( P2PaddrSTR strP2Paddr )
{   m_sP2Paddr = strP2Paddr; }
P2Paddr::P2Paddr ( P2PaddrSTR strP2PaddrHub
                 , const char *pszFormatCon, ... )
{   va_list       ap;
    char          szBuffer[256];
    va_start ( ap, pszFormatCon );
    vsprintf_s ( szBuffer, sizeof(szBuffer), pszFormatCon, ap );
    va_end ( ap );
    m_sP2Paddr  = strP2PaddrHub;
    m_sP2Paddr += ".";
    m_sP2Paddr += szBuffer; }
P2Paddr::~P2Paddr ( )
{ }

//  Operators
P2Paddr&
P2Paddr::operator = ( const P2Paddr& rhs )
{
    Empty();
    m_sP2Paddr = rhs.m_sP2Paddr;
    return *this;
}
P2Paddr&
P2Paddr::operator = ( P2PaddrSTR strP2Paddr )
{
    Empty();
    m_sP2Paddr = strP2Paddr;
    return *this;
}
P2Paddr::operator P2PaddrSTR ( ) const
{
    return m_sP2Paddr.GetLength()>0?m_sP2Paddr:"\0";
}
bool
P2Paddr::operator == ( const P2Paddr& rhs ) const
{   
    return m_sP2Paddr == rhs.m_sP2Paddr;
}
bool
P2Paddr::operator == ( P2PaddrSTR rhs ) const
{   
    return m_sP2Paddr == rhs;
}
bool
P2Paddr::operator != ( const P2Paddr& rhs ) const
{
    return m_sP2Paddr != rhs.m_sP2Paddr;
}
bool
P2Paddr::operator != ( P2PaddrSTR rhs ) const
{
    return m_sP2Paddr != rhs;
}

//  Operations
void
P2Paddr::Empty ( )
{
    m_sP2Paddr.Empty();
    m_sVNetname.Empty();
    m_sVNetaddr.Empty();
    m_sHubname.Empty();
}

///////////////////////////////////////////////////////////////////////
//  Routing

//  Child address comparison
//  NOTES: An equivalent address is not a child
//
//  Returns:     bool
//                 true... strP2Paddr is a direct child of the
//                         contained address
//                 false.. Not a child
//
bool
P2Paddr::IsChild ( P2PaddrSTR strP2PaddrChild ) const
{
    size_t lenP2PaddrThis = m_sP2Paddr.GetLength();
    if ( lenP2PaddrThis >= _tcslen(strP2PaddrChild) )
      return false;
    return !memcmp(strP2PaddrChild,m_sP2Paddr,lenP2PaddrThis);
}
//  Routable address comparison
//  NOTES: Both equivalent and child addresses are routable
//       : CEX.SDrvs.AAD is routable through CEX.SDrvs
//         CEX.XDrvs is routable through CEX
//         CEX.XDrvs is NOT routable through CEX.SDrvs
//         CEX is routable through CEX
//         CEX is not routable through CEX.SDrvs
//
//
//  Parameters:  P2PaddrSTR strP2PaddrDestin
//               Destination address to be checked
//  Returns:     bool
//                 true... strP2Paddr is routable through the
//                         contained address
//                 false.. Not a routable address
//
bool
P2Paddr::IsRable ( P2PaddrSTR strP2PaddrDestin ) const
{
    size_t lenP2PaddrThis = m_sP2Paddr.GetLength();
    if ( lenP2PaddrThis>_tcslen(strP2PaddrDestin) )
      return false;
    return !memcmp(strP2PaddrDestin,m_sP2Paddr,lenP2PaddrThis);
}

//  Property exposure
bool
P2Paddr::IsNull ( ) const
{   return m_sP2Paddr.IsEmpty() ? true : false; }
P2PaddrSTR
P2Paddr::c_str ( ) const
{   
    return m_sP2Paddr;
}
P2PaddrSTR
P2Paddr::c_name ( ) const
{
    CString& csHubname = (CString&)m_sHubname;
             csHubname = "";
    for ( int i = 0; i < m_sP2Paddr.GetLength(); i++ )
    {
      csHubname += m_sP2Paddr[i];
      if ( m_sP2Paddr[i] == '.' )
        csHubname.Empty();
    }
    ASSERT(csHubname==m_sHubname);
    return m_sHubname;
}
P2PaddrSTR
P2Paddr::c_hopname ( int nLevel ) const
{
    CString& csHubname = (CString&)m_sHubname;
             csHubname = "";
    for ( int i = 0; i <= m_sP2Paddr.GetLength(); i++ )
    {
      if ( m_sP2Paddr[i] != '.' )
      {
        csHubname += m_sP2Paddr[i];
        continue;
      }
      if ( nLevel <= 0 )
        break;
      nLevel--;
      csHubname.Empty();
    }
    if ( nLevel > 0 )
      csHubname.Empty();
    ASSERT(csHubname==m_sHubname);
    return m_sHubname;
}
P2Psize_t
P2Paddr::Sizeof ( ) const
{   
    return (P2Psize_t)m_sP2Paddr.GetLength();
}
BOOL
P2Paddr::IsEmpty ( )
{
    return m_sP2Paddr.IsEmpty();
}

//LPCTSTR
//P2Paddr::GetName ( );
//LPCTSTR
//P2Paddr::GetPath ( );  
//
//  Description: P2PeerCon_MAP name matching utility
//               NOTES: Internal P2Peer path is matched against the
//                      passed P2PeerCon_MAP wildcard
//                    : Example 1 (Backwards connection)
//                        Hub1/Hub2/whatever, matches wildcards
//                           */Hub2/whatever, and
//                                */whatever, and
//
//
//  Parameters:  P2PaddrSTR pszMapWildcard
//               P2PeerHub name wildcard
//
//  Returns:     bool
//                 true... Connection matches wildcard
//                 false.. No match
//
bool
P2Paddr::IsMapped ( P2PaddrSTR strP2Paddr ) const
{
    // Preparation
    const TCHAR *pszMapWildcard = m_sP2Paddr;
    bool  star   = false;
    const TCHAR *pszHub = strP2Paddr;

    // Implmentation
Top:
    const TCHAR *p, *s;
    for ( s = pszHub, p = pszMapWildcard; *s; ++s, ++p )
    {
      switch (*p) {
         case '?':
            if (*s == '.') goto starCheck;
            break;
         case '*':
            star = TRUE;
            pszHub = s, pszMapWildcard = p;
            if (!*++pszMapWildcard) return TRUE;
            goto Top;
         case '#':
            if ( !isdigit(*s) )
              goto starCheck;
            while ( isdigit(*(s+1)) )
              s++;
            break;
         default:
            //if (mapCaseTable[*s] != mapCaseTable[*p])
            if (*s != *p )
               goto starCheck;
            break;
      } /* endswitch */
   } /* endfor */
   if (*p == '*') ++p;
   return (!*p);

starCheck:
   if (!star) return FALSE;
   pszHub++;
   goto Top;
}

///////////////////////////////////////////////////////////////////////
//  P2Padomain implementation
//  NOTES: Supports Virtual P2Peer Network addresses of the form
//         [VNetname:]RootHubname.Hubname[i]. . .Hubname etc

//  Constructors and destructor
P2Padomain::P2Padomain ( )
{
}

P2Padomain::P2Padomain ( P2PadomSTR strP2PaddrDomainTP )
{
    // Firstly
    m_bWildcard = false;

    // Locals
    int nToken = 0;
    m_csDomain = strP2PaddrDomainTP;
    CString csDomain   = m_csDomain.Tokenize ( _T("|"), nToken );

    // Possible multiple domain templates
    while ( !csDomain.IsEmpty() )
    {
      if ( csDomain == _T("*") )
        m_bWildcard = true;
      m_oCListDomains.AddTail ( new CString(csDomain) );
      csDomain = m_csDomain.Tokenize( _T("|"), nToken );
    }
}

P2Padomain::~P2Padomain ( )
{
    // Garbage collection
    while ( !m_oCListDomains.IsEmpty() )
      delete m_oCListDomains.RemoveHead();
}

//  Operators
P2Padomain&
P2Padomain::operator = ( const P2Padomain& rhs )
{
	m_csDomain = rhs.m_csDomain;
	return *this;
}
P2Padomain&
P2Padomain::operator = ( P2PadomSTR rhs )
{   
	m_csDomain = rhs;
    return *this;
}
P2Padomain::operator P2PadomSTR ( ) const
{   
	return m_csDomain;
}

//
//  Maps strP2Paddr against the encapsulated domain
//
//
//  Parameters:  P2PaddrSTR strP2Paddr
//               Address to be mapped against domain
//
//  Returns:     bool
//               Mapping result
//                 true... Mapped within domain
//                 false.. Not within domain
bool
P2Padomain::IsMapped ( P2PaddrSTR strP2Paddr ) const
{
    // Optimisation
    if ( m_bWildcard )
      return true;

    // Domains
    // NOTES: Multiple ";" delimitered domains may have been
    //        specified
    POSITION pos = m_oCListDomains.GetHeadPosition ( );
    while ( pos )
    {
      CString *pDomain = m_oCListDomains.GetNext ( pos );
      if ( (*pDomain) == strP2Paddr )
        return true;

      // Itemize LHS domain into individual P2Paddr's
      long    nStateLHS = 0;
      P2Paddr oP2PaddrLHS;
      while ( GetNextP2Paddr(*pDomain,oP2PaddrLHS,nStateLHS) )
      {
        // Itemize RHS domain into individual P2Paddr's
        if ( oP2PaddrLHS.IsMapped(strP2Paddr) )
          return true;
      }
    }

    // Mapping failed
    return false;
}

int
GetNextItem ( LPCTSTR lpszDomain, long& nState, int nSet, CString& csSetItem )
{
    // Locals
    UCHAR   *pucSets = reinterpret_cast<UCHAR *>(&nState);
    int nSetItem;

    // Initialise
TOP:nSetItem = 0;

    // Each domain character
	int n;
    for ( n = 1; lpszDomain[n]; n++ )
    {
      // Domain set, recursive
      if ( lpszDomain[n] == '<' )
      {
        CString csSetRecurs;
        int      nSetRecurs = nSet + 1;
        n += GetNextItem ( &lpszDomain[n], nState, nSetRecurs, csSetRecurs );
        if ( pucSets[nSetRecurs] != 255 )
        {
          nSet++;
          csSetItem += csSetItem;
          continue;
        }
        if ( nSetRecurs == 0 )
          return false;
        memset (&pucSets[nSetRecurs], 0, sizeof(nState)-nSetRecurs );
                 pucSets[nSetRecurs-1]++;
        goto TOP;
      }

      // Set delimiter
      else if ( lpszDomain[n] == ',' )
      {
        nSetItem++;
      }

      // End of set
      else if ( lpszDomain[n] == '>' )
      {
        if ( nSetItem < pucSets[nSet] )
          pucSets[nSet] = 255;
        else
          pucSets[nSet]++;
        break;
      }
      
      // Address component
      else
      {
        if ( pucSets[nSet] == nSetItem )
          csSetItem += lpszDomain[n];
      }
    }

    // Tidy up, and
    return n;
}
bool
P2Padomain::GetNextP2Paddr ( P2PadomSTR strDomain
                           , P2Paddr& oP2Paddr, long& nState ) const
{
    CString csWork;
    UCHAR   *pucSets = reinterpret_cast<UCHAR *>(&nState);
    int      nSet    = 0;

    // Initialise
    if ( pucSets[0] == 255 )
      return false;
TOP:csWork.Empty ( );
    nSet = 0;

    // Synchronise
    for ( int n = 0; strDomain[n]; n++ )
    {
      // Domain set
      if ( strDomain[n] == '<' )
      {
        CString csSetItem;
        n += GetNextItem ( &strDomain[n], nState, nSet, csSetItem );
        if ( pucSets[nSet] != 255 )
        {
          nSet++;
          csWork += csSetItem;
          continue;
        }
        if ( nSet == 0 )
          return false;
        memset (&pucSets[nSet], 0, sizeof(nState)-nSet );
                 pucSets[nSet-1]++;
        goto TOP;
      }

      // Illegals
      else if ( strDomain[n] == ',' ||
                strDomain[n] == '>'    )
      {
        ASSERT(0);
        return false;
      }

      // Address
      else
        csWork += strDomain[n];
    }

    // Tidy up, and
    // NOTES: Provide single shot monitoring
    if ( pucSets[0] == 0 )
      pucSets[0] = 255;
    oP2Paddr = csWork;
    return csWork.IsEmpty() ? false : true;
}

//  Property exposure
bool
P2Padomain::IsNull ( ) const
{   return m_csDomain.IsEmpty() ? true : false; }
P2PaddrSTR
P2Padomain::c_str ( ) const
{   return m_csDomain; }
P2Psize_t
P2Padomain::Sizeof ( ) const
{   return (P2Psize_t)m_csDomain.GetLength(); }

///////////////////////////////////////////////////////////////////////
//  P2Parser

//  Constructors and destructor
/*P2Parser::P2Parser ()
{   RenderThisSafe(); }

P2Parser::P2Parser ( char *pCommand )
{   RenderThisSafe();
    m_pCommand    = pCommand;
    m_pCompleted  = pCommand;
    m_pCheckpoint = pCommand; }
P2Parser::~P2Parser ()
{   delete m_pP2ParserPrev;
    m_pP2ParserPrev = 0; }
void
P2Parser::RenderThisSafe ( )
{   m_nLevel      =  0;
    m_bSkip       = false;
    m_bOptional   = false;
    m_eFCF;
    m_nEnumItem   = -1;
    m_eSeqNum     = -1;
    m_pCommand    =  0;
    m_pCompleted  =  0;
    m_pCheckpoint =  0;
    m_pP2ParserPrev =  0; }

//  Stack management
P2Parser*
P2Parser::Push ( char *pCheckpoint, char *pCompleted, char *pCommand )
{   P2Parser *pP2Parser = new P2Parser ( );
    pP2Parser -> m_pP2ParserPrev = this;

    // Attribute transfer
    pP2Parser -> m_nLevel      = m_nLevel + 1;
    pP2Parser -> m_bSkip       = m_bSkip;
    pP2Parser -> m_bOptional   = m_bOptional;
    pP2Parser -> m_eFCF        = m_eFCF;
    pP2Parser -> m_nEnumItem   = -1;
    pP2Parser -> m_eSeqNum     = m_eSeqNum;
    pP2Parser -> m_pCommand    = m_pCommand;
    pP2Parser -> m_pCompleted  = m_pCompleted;
    pP2Parser -> m_pCheckpoint = m_pCheckpoint;


    // Initialise new level
    if ( pCheckpoint )
      pP2Parser -> m_pCheckpoint = pCheckpoint;
    if ( pCompleted )
      pP2Parser -> m_pCompleted  = pCompleted;
    if ( pCommand )
      pP2Parser -> m_pCommand    = pCommand;

    // Tidy up and
    return pP2Parser;
}

P2Parser*
P2Parser::Pop ( char *pCheckpoint, char *pCompleted, char *pCommand )
{
    // Introduce locals
    P2Parser   *pP2ParserPrev = m_pP2ParserPrev;
    m_pP2ParserPrev         = 0;
    
    // Manage command decode position on previous level
    if ( pCheckpoint )
      pP2ParserPrev -> m_pCheckpoint = pCheckpoint;
    if ( pCompleted )
      pP2ParserPrev -> m_pCompleted  = pCompleted;
    if ( pCommand )
      pP2ParserPrev -> m_pCommand    = pCommand;
    // Tidy up and
    delete this;
    return pP2ParserPrev;
}

//  Operations
bool
P2Parser::GetINT ( int& iValue )
{
    char *pCommand = m_pCommand;
    iValue = strtol ( m_pCommand, &m_pCommand, 10 );
    return (m_pCommand==pCommand)?false:true;
}*/