/*++

Copyright (C) 1997-2001 Microsoft Corporation

Module Name:

    PIPEOPN.CPP

Abstract:

    Declares the abstract and generic (to proxy and stub) pipe operation classes

History:

    alanbos  18-Dec-97   Created.

--*/

#include "precomp.h"
#include "wmishared.h"

// Used as a placeholder in the encode stream
static PACKET_HEADER gDummyPacketHeader (0,0,0);

// IOperation_LPipe

bool IOperation_LPipe::DecodeCallHeader (CTransportStream& decodeStream,
                    OUT DWORD& dwStubAddr, OUT DWORD& dwStubType,
                    OUT DWORD& dwStubFunc)
{
    // Decode the stub address, type and method call id
    DWORD dwInSt = CTransportStream::no_error;
    dwInSt |= decodeStream.ReadDWORD (&dwStubAddr);
    dwInSt |= decodeStream.ReadDWORD (&dwStubType);
    dwInSt |= decodeStream.ReadDWORD (&dwStubFunc);

    return (CTransportStream::no_error == dwInSt);
}

// IBasicOperation_LPipe
void    IBasicOperation_LPipe::EncodePacketHeader (CTransportStream& encodeStream,
                                                   DWORD msgType)
{
#if 0
    // TODO - this is gross as we have to copy the current contents twice!
    // What we need is either (1) WriteBytes should not reset the end of stream,
    // or (2) introduce a Prepend operation.

    // Copy the current encoded stream contents - this is the data minus the
    // header
    CTransportStream        dataStream (encodeStream);

    // Determine the data length and set in the header
    PACKET_HEADER   ph (msgType, dataStream.Size () - PHSIZE, GetRequestId ());
    
    // Serialize the header into the stream - note this will squelch the original
    // data hence the copy above.
    encodeStream.Reset ();
    encodeStream.WriteBytes (&ph, PHSIZE);

    // Add the original data contents again
    encodeStream.Append (&dataStream);
#else
    PACKET_HEADER ph (msgType, encodeStream.Size () - PHSIZE, GetRequestId ());
    DWORD   curPos = encodeStream.GetCurrentPos ();
    encodeStream.Reset ();
    encodeStream.WriteBytes (&ph, PHSIZE);
    encodeStream.SetCurrentPos (curPos);
#endif
}

bool IBasicOperation_LPipe::DecodeHeader (BYTE *pByte, DWORD numBytes, PACKET_HEADER& header)
{
    if (PHSIZE != numBytes)
        return FALSE;

    memcpy ((void *) &header, pByte, PHSIZE);
    return TRUE;
}

void IBasicOperation_LPipe::DecodeResult (CTransportStream& decodeStream)
{
    DWORD dwCallRet = WBEM_E_FAILED;    // Just in case
    decodeStream.ReadDWORD(&dwCallRet);
    SetStatus (dwCallRet);
}

bool IBasicOperation_LPipe::EncodeRequest 
            (CTransportStream& encodeStream, ISecurityHelper& secHelp)
{
#if 0
#else
    // Put a dummy header in for now.
    encodeStream.WriteBytes ((LPVOID) &gDummyPacketHeader, PHSIZE);
#endif

    // Encode the per-operation data
    EncodeOperationReq (encodeStream);
    // Encode header now because we finally know the data length
    EncodePacketHeader (encodeStream, GetMessageType ());  
    return true;
}

bool IBasicOperation_LPipe::DecodeRequest 
            (CTransportStream& encodeStream, ISecurityHelper& secHelp)
{
    // Note: the packet header has already been removed
    DecodeOperationReq (encodeStream);
    return true;
}

bool IBasicOperation_LPipe::EncodeResponse 
            (CTransportStream& encodeStream, ISecurityHelper& secHelp)
{
#if 0
#else
    // Put a dummy header in for now
    encodeStream.WriteBytes ((LPVOID) &gDummyPacketHeader, PHSIZE);
#endif 

    // Encode the per-operation data
    EncodeOperationRsp (encodeStream);

    // Encode header now because we finally know the data length
    EncodePacketHeader (encodeStream, GetResponseType ());  
    return true;
}

bool IBasicOperation_LPipe::DecodeResponse 
            (CTransportStream& encodeStream, ISecurityHelper& secHelp)
{
    // Note: the packet header has already been removed
    DecodeOperationRsp (encodeStream);
    return true;
}

// Given a PH and decodeStream, create an IOperation of the right type
bool IBasicOperation_LPipe::Decode (PACKET_HEADER& header, CTransportStream& decodeStream, 
                        IOperation** ppOpn) 
{
    *ppOpn = NULL;

    /*
     * We only deal with incoming call request messages.  If this isn't one
     * we can go no further.
     */
    if (COMLINK_MSG_CALL != header.GetType ())
        return false;

    /*
     * Decode the call message header
     */
    DWORD   dwStubAddr = 0;
    DWORD   dwStubType = 0;
    DWORD   dwStubFunc = 0;
    
    /*
     * If we cannot decode the call header, discard the message.
     */
    if (!IOperation_LPipe::DecodeCallHeader (decodeStream, dwStubAddr, 
                dwStubType, dwStubFunc))
        return false;

    bool    isAsync = false;
    CStubAddress_WinMgmt    stubAddr (dwStubAddr);

    switch (dwStubType)
    {
    case OBJECTSINK:
        switch (dwStubFunc)
        {
        case RELEASE:
            *ppOpn = new CStubOperation_LPipe_Release (stubAddr, dwStubType);
            break;

        case INDICATE:
            *ppOpn = new CStubOperation_LPipe_Indicate (stubAddr);
            break;

        case SETSTATUS:
            *ppOpn = new CStubOperation_LPipe_SetStatus (stubAddr);
            break;
        }
        break;

        // The remaining messages would only be handled by the server stub
#ifdef MARSHALER_STUB
    case PROVIDER:
        switch (dwStubFunc)
        {
        case RELEASE:
            *ppOpn = new CStubOperation_LPipe_Release (stubAddr, dwStubType);
            break;

        case WBEM_METHOD_CancelAsyncCall:
            *ppOpn = new CStubOperation_LPipe_CancelAsyncCall (stubAddr);
            break;

        case WBEM_METHOD_CreateClassEnumAsync:
        case WBEM_METHOD_CreateInstanceEnumAsync:
            isAsync = true;
        case WBEM_METHOD_CreateClassEnum:
        case WBEM_METHOD_CreateInstanceEnum:
            *ppOpn = new CStubOperation_LPipe_CreateEnum (stubAddr, dwStubFunc, isAsync);
            break;

        case WBEM_METHOD_DeleteClassAsync:
        case WBEM_METHOD_DeleteInstanceAsync:
            isAsync = true;
        case WBEM_METHOD_DeleteClass:
        case WBEM_METHOD_DeleteInstance:
            *ppOpn = new CStubOperation_LPipe_Delete (stubAddr, dwStubFunc, isAsync);
            break;

        case WBEM_METHOD_ExecMethodAsync:
            isAsync = true;
        case WBEM_METHOD_ExecMethod:
            *ppOpn = new CStubOperation_LPipe_ExecMethod (stubAddr, dwStubFunc, isAsync);
            break;

        case WBEM_METHOD_ExecNotificationQueryAsync:
        case WBEM_METHOD_ExecQueryAsync:
            isAsync = true;
        case WBEM_METHOD_ExecNotificationQuery:
        case WBEM_METHOD_ExecQuery:
            *ppOpn = new CStubOperation_LPipe_ExecQuery (stubAddr, dwStubFunc, isAsync);
            break;

        case WBEM_METHOD_GetObjectAsync:
            isAsync = true;
        case WBEM_METHOD_GetObject:
            *ppOpn = new CStubOperation_LPipe_GetObject (stubAddr, dwStubFunc, isAsync);
            break;

        case WBEM_METHOD_OpenNamespace:
            *ppOpn = new CStubOperation_LPipe_OpenNamespace (stubAddr);
            break;

        case WBEM_METHOD_PutClassAsync:
        case WBEM_METHOD_PutInstanceAsync:
            isAsync = true;
        case WBEM_METHOD_PutClass:
        case WBEM_METHOD_PutInstance:
            *ppOpn = new CStubOperation_LPipe_Put (stubAddr, dwStubFunc, isAsync); 
            break;

        case WBEM_METHOD_QueryObjectSink:
            *ppOpn = new CStubOperation_LPipe_QueryObjectSink (stubAddr);
            break;
        }
        break;

    case CALLRESULT:
        switch (dwStubFunc)
        {
        case RELEASE:
            *ppOpn = new CStubOperation_LPipe_Release (stubAddr, dwStubType);
            break;

        case GETRESULTOBJECT:
        case GETRESULTSTRING:
        case GETCALLSTATUS:
        case GETSERVICES:
            *ppOpn = new CStubOperation_LPipe_CallResult (stubAddr, dwStubFunc);
            break;
        }
        break;

    case ENUMERATOR:
        switch (dwStubFunc)
        {
        case RELEASE:
            *ppOpn = new CStubOperation_LPipe_Release (stubAddr, dwStubType);
            break;

        case NEXT:
            *ppOpn = new CStubOperation_LPipe_Next (stubAddr);
            break;

        case RESET:
            *ppOpn = new CStubOperation_LPipe_Reset (stubAddr);
            break;

        case CLONE:
            *ppOpn = new CStubOperation_LPipe_Clone (stubAddr);
            break;

        case SKIP:
            *ppOpn = new CStubOperation_LPipe_Skip (stubAddr);
            break;

        case NEXTASYNC:
            *ppOpn = new CStubOperation_LPipe_NextAsync (stubAddr);
            break;
        }
        break;

    case LOGIN:
        switch (dwStubFunc)
        {
        case RELEASE:
            *ppOpn = new CStubOperation_LPipe_Release (stubAddr, dwStubType);
            break;

        case REQUESTCHALLENGE:
            *ppOpn = new CStubOperation_LPipe_RequestChallenge (stubAddr);
            break;

        case ESTABLISHPOSITION:
            *ppOpn = new CStubOperation_LPipe_EstablishPosition (stubAddr);
            break;

        case SSPIPRELOGIN:
            *ppOpn = new CStubOperation_LPipe_SspiPreLogin (stubAddr);
            break;

        case LOGINBYTOKEN:
            *ppOpn = new CStubOperation_LPipe_Login (stubAddr);
            break;

        case WBEMLOGIN:
            *ppOpn = new CStubOperation_LPipe_WBEMLogin (stubAddr);
            break;
        }
        break;
#endif
    }

    if (!(*ppOpn))
    {
        // We could not recognize the message type or function.  In this 
        // case we make a NotSupported message to send back.

        *ppOpn = new COperation_LPipe_NotSupported ();
    }

    // Set the correct request id into operation.  It is taken from the
    // incoming header so as to ensure the response id matches the request.
    (*ppOpn)->SetRequestId (header.GetRequestId ());
    DEBUGTRACE((LOG,"\nDecode Request [Func = %d, RequestId = %d]", dwStubFunc, (*ppOpn)->GetRequestId ()));
        
    return true; 
}

// IProxyOperation_LPipe

void IProxyOperation_LPipe::DecodeStubAddress (ObjType ot, CTransportStream& decodeStream,
                                               bool checkValid)
{
    CStubAddress_WinMgmt stubAddr;
    stubAddr.Deserialize (decodeStream);

    if ((checkValid && !stubAddr.IsValid ()) || 
        (decodeStream.Status () != CTransportStream::no_error))
        SetStatus (WBEM_E_INVALID_STREAM);
    else
        SetProxyAddress (ot, stubAddr);
}

void IProxyOperation_LPipe::EncodeContext (CTransportStream& encodeStream)
{
    bool    setContext = FALSE;
    IWbemContext *pContext = GetContext ();

    if (pContext)
    {
        IStream     *pStr = NULL;   // This is released when the write stream is deleted
        IUnknown    *pUnk = NULL;   // This is released below

        if ((SUCCEEDED (encodeStream.QueryInterface (IID_IStream, (void **) &pStr)))
            && (SUCCEEDED (pContext->QueryInterface (IID_IUnknown, (void **) &pUnk))))
        {
            setContext = TRUE;
            encodeStream.WriteDWORD (1);
            CoMarshalInterface (pStr, IID_IWbemContext, pUnk, MSHCTX_LOCAL, NULL, 
                                    MSHLFLAGS_NORMAL);
            pUnk->Release ();
        }
    }
    
    if (!setContext)
        encodeStream.WriteDWORD (0);
}
    
void IProxyOperation_LPipe::EncodeCallHeader (CTransportStream& encodeStream)
{
    // Encode the stub address, type and method call id
    encodeStream.WriteDWORD (m_stubAddr);
    encodeStream.WriteDWORD (m_stubType);
    encodeStream.WriteDWORD (m_stubFunc);
}

void IProxyOperation_LPipe::DecodeErrorObject (CTransportStream& decodeStream)
{
    if(decodeStream.NextType() != VT_UI4)
    {
        // Got an error object to deserialize
        IErrorInfo* pInfo = NULL;
        IStream* pStr = NULL;

        HRESULT t_Result ;
        if ((S_OK == ( t_Result = decodeStream.QueryInterface(IID_IStream, (void**)&pStr))) )
        {
            if ((S_OK == ( t_Result = CoUnmarshalInterface(pStr, IID_IErrorInfo, (void**)&pInfo))) && pInfo)
            {
                SetErrorInfoIntoObject (pInfo);
                pInfo->Release ();
            }
        }
    }
}

// IStubOperation_LPipe

void IStubOperation_LPipe::EncodeStubAddress (ObjType ot, CTransportStream& encodeStream)
{
    CStubAddress_WinMgmt *pStubAddr = (CStubAddress_WinMgmt *) GetProxyAddress (ot);

    if (pStubAddr && pStubAddr->IsValid ())
        pStubAddr->Serialize (encodeStream);
    else
        encodeStream.WriteDWORD (0);
}

void IStubOperation_LPipe::EncodeErrorObject (CTransportStream& encodeStream)
{
    IErrorInfo* pInfo = GetErrorInfoFromObject ();

    if (pInfo)
    {
        IStream * pStr = NULL;
        HRESULT t_Result ;

        if(S_OK == ( t_Result = encodeStream.QueryInterface(IID_IStream, (void**)&pStr)))
        {
            t_Result = CoMarshalInterface(pStr, IID_IWbemClassObject, pInfo, 
                                MSHCTX_LOCAL, NULL, MSHLFLAGS_NORMAL);
        }
        pInfo->Release ();
    }
}

void IStubOperation_LPipe::DecodeContext (CTransportStream& decodeStream)
{
    DWORD           gotContext;

    if (CTransportStream::no_error != decodeStream.ReadDWORD (&gotContext))
        SetStatus (WBEM_E_INVALID_STREAM);
    else if (1 == gotContext)
    {
        IStream     *pStr = NULL;   // This is released when the read stream is deleted
        IWbemContext*   pContext = NULL;
    
        if ((SUCCEEDED (decodeStream.QueryInterface (IID_IStream, (void **) &pStr)))
             && pStr 
             && (SUCCEEDED (CoUnmarshalInterface (pStr, IID_IWbemContext, 
                    (void **) &pContext))))
        {
            SetContext (pContext);
            pContext->Release ();
        }
        else 
            SetStatus (WBEM_E_INVALID_STREAM);
    }
}

void IStubOperation_LPipe::HandleCall (CComLink& comLink)
{
    DEBUGTRACE((LOG,"\nHandle Call [Func = %d, RequestId = %d]", m_stubFunc, GetRequestId ()));

    // Always protect our stub; if the following call succeeds then we will have AddRef'd
    // the stub we are about to use in this operation.
    IUnknown *pStub = comLink.GetStub (m_stubAddr, m_stubType);

    if (!pStub)
        SetStatus (WBEM_E_FAILED);
    
    if (WBEM_NO_ERROR == GetStatus ())
    {
        Execute (comLink, pStub);
        SetErrorInfoIntoObject ();
    }

    // Now release the stub as this operation is done with it.
    if ( pStub )
        pStub->Release ();
    else
    {
ERRORTRACE((LOG,"\nHANDLE CALL FAILED[Func = %d, RequestId = %d]", m_stubFunc, GetRequestId ()));
    }
}

// CProxyOperation_LPipe_Indicate
void CProxyOperation_LPipe_Indicate::EncodeOp (CTransportStream& encodeStream)
{
    encodeStream.WriteLong(m_objectCount);

    for(long lCnt = 0; lCnt < m_objectCount; lCnt++)
         ((CWbemObject *)m_pObjArray[lCnt])->WriteToStream(&encodeStream);
}

// CProxyOperation_LPipe::SetStatus
void CProxyOperation_LPipe_SetStatus::EncodeOp (CTransportStream& encodeStream)
{
    encodeStream.WriteLong(m_flags);
    encodeStream.WriteLong(m_param);
    encodeStream.WriteBSTR(m_strParam);

    if (m_pObjParam)
    {
        encodeStream.WriteLong (1);
        ((CWbemObject *)m_pObjParam)->WriteToStream(&encodeStream);
    }
    else
        encodeStream.WriteLong (0);
}

// CStubOperation_LPipe_Indicate
void CStubOperation_LPipe_Indicate::DecodeOp (CTransportStream& decodeStream)
{
    if ((CTransportStream::no_error == decodeStream.ReadLong (&m_objectCount)) 
                && (0 < m_objectCount))
    {
        m_pObjArray = (IWbemClassObject **) (new CWbemObject * [m_objectCount]);

        if (!m_pObjArray)
            SetStatus (WBEM_E_OUT_OF_MEMORY);
        else
        {
            long lCnt;

            for (lCnt = 0; lCnt < m_objectCount; lCnt++)
            {
                m_pObjArray [lCnt] = CreateClassObject (&decodeStream);
                
                if (!bVerifyPointer (m_pObjArray [lCnt]))
                    break;

                m_pObjArray [lCnt]->Release (); // Reduce ref count back to 1
            }

            if (lCnt < m_objectCount)
            {
                // We terminated prematurely
                SetStatus (WBEM_E_FAILED);
                long lDelTo = lCnt;

                for (lCnt = 0; lCnt < lDelTo; lCnt++)
                    delete m_pObjArray [lCnt];
            }
        }
    }
}
    
void CStubOperation_LPipe_Indicate::Execute (CComLink& comLink, IUnknown *pStub)
{
    IWbemObjectSink*    pObj = (IWbemObjectSink *) pStub;
    DEBUGTRACE((LOG,"\nCalling Indicate into stub = %X", pObj));
    SetStatus (pObj->Indicate (m_objectCount, m_pObjArray));
    
    // Drop the reference count
    for (long lCnt = 0; lCnt < m_objectCount; lCnt++)
        m_pObjArray [lCnt]->Release ();
}

// CStubOperation_LPipe::SetStatus
void CStubOperation_LPipe_SetStatus::DecodeOp (CTransportStream& decodeStream)
{
    DWORD dwInSt = CTransportStream::no_error;
    long    lGotObj= 0;
    dwInSt |= decodeStream.ReadLong (&m_flags);
    dwInSt |= decodeStream.ReadLong (&m_param);
    dwInSt |= decodeStream.ReadBSTR (&m_strParam);
    dwInSt |= decodeStream.ReadLong (&lGotObj);

    if (lGotObj)
    {
        m_pObjParam = CreateClassObject (&decodeStream);

        if (!bVerifyPointer (m_pObjParam))
        {
            m_pObjParam = NULL;
            SetStatus (WBEM_E_FAILED);
        }
        else
            m_pObjParam->Release ();    // Bumped up by bVerifyPointer
    }
}

void CStubOperation_LPipe_SetStatus::Execute (CComLink& comLink, IUnknown *pStub)
{
    IWbemObjectSink*    pObj = (IWbemObjectSink *) pStub;
    DEBUGTRACE((LOG,"\nCalling SetStatus into stub = %X", pObj));
    SetStatus (pObj->SetStatus (m_flags, m_param, m_strParam,
                        m_pObjParam));
}

// CStubOperation_LPipe_Release
void CStubOperation_LPipe_Release::Execute (CComLink& comLink, IUnknown *pStub)
{
    switch (m_stubType)
    {
        case LOGIN:
        case ENUMERATOR:
        case PROVIDER:
        case OBJECTSINK:
        case CALLRESULT:
            comLink.Release2 ((void *) pStub, (enum ObjType) m_stubType);
            SetStatus (pStub->Release ());
            break;
            
        default:
            SetStatus (WBEM_E_FAILED);
            return;
    }
}