/*++

Copyright (c) 2000, Microsoft Corporation

Module Name:

    

Abstract:

    

Author:

    Savas Guven (savasg)   27-Nov-2000

Revision History:

*/

#include "stdafx.h"

CLIST g_IcqClientList;

CSockDispatcher * g_IcqPeerDispatcherp = NULL;






IcqPrx::IcqPrx()
:m_Socketp(NULL),
 m_ControlChannelp(NULL)
{
    ICQ_TRC(TM_IO, TL_DUMP, (" ICQPRX - Default - Constructor"));

    g_IcqComponentReferencep = dynamic_cast<PCOMPONENT_SYNC>(this);
}


IcqPrx::~IcqPrx()
{
    ICQ_TRC(TM_IO, TL_DUMP, ("ICQPRX - ~ DESTRUCTOR"));

    if(this->bCleanupCalled is FALSE)
    {
        IcqPrx::ComponentCleanUpRoutine();
    }

    this->bCleanupCalled = FALSE;
}


//
// The Local Socket is a resource which needs to be freed up.
// All the Clients needs to be deleted from the entries?
// The CSockDispatcher needs to be cleared up.
//
void
IcqPrx::ComponentCleanUpRoutine(void)
{


    if( m_Socketp ) 
    { 
        DELETE_COMPONENT( m_Socketp );

        m_Socketp = NULL;
    }

    if( g_IcqPeerDispatcherp ) 
    {
        DELETE_COMPONENT( g_IcqPeerDispatcherp );
    }

    if ( m_ControlChannelp )
    {
        m_ControlChannelp->Cancel();

        m_ControlChannelp->Release();

        m_ControlChannelp = NULL;
    }
    
    this->bCleanupCalled = TRUE;
}


void
IcqPrx::StopSync()
{
    ULONG hr = S_OK;

    if( m_Socketp )
    {
        STOP_COMPONENT( m_Socketp );
    }

    if( g_IcqPeerDispatcherp )
    {
        STOP_COMPONENT( g_IcqPeerDispatcherp );
    }

    if( m_ControlChannelp )
    {
        hr = m_ControlChannelp->Cancel();

        if( FAILED(hr) )
        {
            ICQ_TRC(TM_PRX, TL_ERROR, 
                    ("** !! Can't cancel the PRIMARY redirect e(%X)", hr));
        }
        else
        {
            m_ControlChannelp = NULL;
        }
    }
}


ULONG
IcqPrx::RunIcq99Proxy(
                        ULONG BoundaryIp
                     )
/*++

Routine Description:


    
Arguments:

    none.

Return Value:

    

--*/
{
    ULONG hr = S_OK;
    
    USHORT port;
    ULONG ip;


	ICQ_TRC(TM_PRX, TL_INFO, ("> ICQ 99 PRxY %lu", BoundaryIp));

    
    ASSERT(BoundaryIp != 0);

    NEW_OBJECT( m_Socketp, CNhSock );

	if( m_Socketp is NULL) return E_OUTOFMEMORY;


    //
    // Get the Public IP information
    ip = BoundaryIp;

    g_MyPublicIp = ip;


    // __asm int 3
	
	do 
	{

		// Init globals
        __try
        {
		    g_IcqPeerDispatcherp = new CSockDispatcher(ip, 0);
        }
        __except( EXCEPTION_EXECUTE_HANDLER )
        {
            delete g_IcqPeerDispatcherp;

            hr = E_OUTOFMEMORY;

            g_IcqPeerDispatcherp = NULL;
        }

        if ( g_IcqPeerDispatcherp is NULL )
        {
            ICQ_TRC(TM_PRX, TL_ERROR,("Dispatcher creation failed - mem alloc"));

            hr = E_OUTOFMEMORY;

            break;
        }
        
        //
		// Create UDP socket with the given IP:Port in the argv
        //
		hr = m_Socketp->NhCreateDatagramSocket(ip, 0, NULL);

        if ( hr )
        {
            hr = HRESULT_FROM_WIN32( hr );

            ICQ_TRC(TM_PRX, TL_ERROR, 
                    ("Can't Create the Primary UDP socket E(%X)", hr));

            break;
        }

		m_Socketp->NhQueryLocalEndpointSocket( NULL, &port );

        //
		// Create Dynamic SRC redirection to the IP and port
        //
        hr = g_IAlgServicesp->CreatePrimaryControlChannel(eALG_UDP,
                                                          ICQ99_SERVER_PORT,
                                                          eALG_DESTINATION_CAPTURE,
                                                          FALSE,
                                                          ip,
                                                          port,
                                                          &m_ControlChannelp);
		if( FAILED(hr) )
		{
			ICQ_TRC(TM_PRX, TL_ERROR, 
                    ("ERROR !!> DynamicPortRedirect Has Failed E(%X)", hr));

			break;
		}

		ICQ_TRC(TM_PRX, TL_INFO, 
                ("All Outgoing redirects will be sent to %s(%hu)",
				INET_NTOA(ip), htons(port)));

		// Issue a read operation on the UDP socket.
		hr = m_Socketp->NhReadDatagramSocket(g_IcqComponentReferencep,
                                             NULL,
                                             IcqReadClientUdpCompletionRoutine,
                                             NULL,
                                             NULL);
        if ( hr )
        {
            hr = HRESULT_FROM_WIN32( hr );
            
            ICQ_TRC(TM_PRX, TL_ERROR, ("hr Level E(%X)", hr));

            ASSERT( FALSE );
        }
	
    } while (0);

    //
    // Handle the error case
    //
    if ( FAILED(hr) )
    {
        //
        // Delete Dispatcher
        if ( g_IcqPeerDispatcherp  != NULL )
        {
            DELETE_COMPONENT( g_IcqPeerDispatcherp );

            g_IcqPeerDispatcherp = NULL;
        }

        //
        // Delete Socket
        if ( m_Socketp != NULL )
        {
            DELETE_COMPONENT( m_Socketp );

            m_Socketp = NULL;
        }

        //
        // Delete The Primary Control Channel
        if ( m_ControlChannelp )
        {
            m_ControlChannelp->Cancel();

            m_ControlChannelp->Release();

            m_ControlChannelp = NULL;
        }
    }
    
    return hr;
}




ULONG
IcqPrx::ReadFromClientCompletionRoutine
            (
                 ULONG ErrorCode,
                 ULONG BytesTransferred,
                 PNH_BUFFER Bufferp
            )
/*++

Routine Description:

    This is the READ - DISPATCHER for the UDP 
    Clients sending packets to the server will be caught here..
    it will dispatch the packet to the appropriate ICQ client objects READER
    
    
Arguments:

    none.

Return Value:

    

--*/
{
	PCNhSock    Socketp			 = Bufferp->Socketp;
    PICQ_CLIENT IcqClientp       = NULL;

    //	NAT_KEY_SESSION_MAPPING_EX_INFORMATION Key;

	// ULONG  Length = sizeof(NAT_KEY_SESSION_MAPPING_EX_INFORMATION);
	ULONG  hr = NO_ERROR;
	ULONG  LocalIp, ActualClientIp, ActualDestinationIp;
	USHORT LocalPort, ActualClientPort, ActualDestinationPort;


    if(ErrorCode)
    {
        //
        // Delete the List of the Clients here. 
        // Starts from the head of the List
        // Don't try to use a Removed Element unless you're damn sure
        // that its Ref Count hasn't reached zero yet.
        //
        ICQ_TRC(TM_PRX, TL_CRIT, ("STOPPING the MAIN READ Socket for UDP"));

        for(IcqClientp = dynamic_cast<PICQ_CLIENT>(g_IcqClientList.RemoveSortedKeys(0,0));
            IcqClientp != NULL;
            IcqClientp = dynamic_cast<PICQ_CLIENT>(g_IcqClientList.RemoveSortedKeys(0,0))
           )
        {
            // NOTE: decide what to do.
        }

        hr = E_FAIL;
    }
    else
    {
    
        //
        // Read about the local client..
        //
        
        //  Socketp->NhQueryLocalEndpointSocket(&LocalIp, &LocalPort);
    
        ActualClientIp   = Bufferp->ReadAddress.sin_addr.S_un.S_addr;
    
        ActualClientPort = Bufferp->ReadAddress.sin_port;
    
        //
        // Get Information about the actual destination
        // 

        hr = m_ControlChannelp->GetOriginalDestinationInformation(ActualClientIp,
                                                                  ActualClientPort,
                                                                  &ActualDestinationIp,
                                                                  &ActualDestinationPort,
                                                                  NULL);

        if( FAILED(hr) )
        {
            ICQ_TRC(TM_PRX, TL_ERROR, 
                    ("Can't use the GetOriginalDestination Information Interface E(%X)", hr ));
        }
        else
        {

            ICQ_TRC(TM_PRX, TL_TRACE,("DATA CAME from %s-%hu", 
                    INET_NTOA(ActualClientIp), htons(ActualClientPort)));
            
            ICQ_TRC(TM_PRX, TL_TRACE,("DATA WILL BE SEND TO %s-%hu", 
                    INET_NTOA(ActualDestinationIp), htons(ActualDestinationPort)));
        }
    
        //
        // Find the appropriate ICQ client entry for this 
        // ICQ CLIENT ~ ICQ SERVER UDP connection
        // Search the Created ICQ Client List to handle proper one.
        //
        // NOTE: Should we handle this with just IPs?
        // and the other Key might just be the server IP???
        //
        IcqClientp = dynamic_cast<PICQ_CLIENT>
                        (g_IcqClientList.SearchNodeKeys(ActualClientIp, 0));
    
        //
        // If there is no such entry than create one.
        //
        if(IcqClientp is NULL)
        {
            ICQ_TRC(TM_PRX, TL_INFO, ("PRX> NEW ICQ CLIENT DETECTED %s",
                                      INET_NTOA(ActualClientIp)));
    
            NEW_OBJECT(IcqClientp, ICQ_CLIENT);
            
            //
            // NOTE : ERROR CASE - proper handling pls.
            if(IcqClientp is NULL) 
            {
                ICQ_TRC(TM_PRX, TL_ERROR, ("PRX> MEMORY ALLOC ERROR"));
    
                hr = E_OUTOFMEMORY;
    
                return hr;
            }
    
            //
            // Write the IP-PORT information Here The client class
            // will collect rest of the information along the way
            //
    
            hr = IcqClientp->Initialize(Bufferp,
                                        ActualClientIp,
                                        ActualClientPort,
                                        ActualDestinationIp,
                                        ActualDestinationPort,
                                        Socketp);

            _ASSERT( SUCCEEDED(hr) );

            if( FAILED(hr) )
            {
                DELETE_COMPONENT( IcqClientp );
            }
            else // Add the client to the List
            {
                g_IcqClientList.InsertSorted(IcqClientp);
            }
        }
        else
        {
            ICQ_TRC(TM_PRX, TL_TRACE,
				    ("PRX> ICQ CLIENT EXISTS - READ-client will be called"));
    
            hr = IcqClientp->ClientRead( Bufferp,
                                         ActualDestinationIp,
                                         ActualDestinationPort );
        }
    }

    if ( FAILED(hr) )
    {

    }

    return hr;
}