//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 2000.
//
//  File:       clipformat.cxx
//
//  Contents:   Support for Windows/OLE data types for oleprx32.dll.
//              Used to be transmit_as routines, now user_marshal routines.
//
//              This file contains support for CLIPFORMAT.
//
//  Functions:  
//              CLIPFORMAT_UserSize
//              CLIPFORMAT_UserMarshal
//              CLIPFORMAT_UserUnmarshal
//              CLIPFORMAT_UserFree
//              CLIPFORMAT_UserSize64
//              CLIPFORMAT_UserMarshal64
//              CLIPFORMAT_UserUnmarshal64
//              CLIPFORMAT_UserFree64
//
//  History:    13-Dec-00   JohnDoty    Migrated from transmit.cxx
//
//--------------------------------------------------------------------------
#include "stdrpc.hxx"
#pragma hdrstop

#include <oleauto.h>
#include <objbase.h>
#include "transmit.hxx"
#include <rpcwdt.h>
#include <storext.h>
#include "widewrap.h"
#include <valid.h>
#include <obase.h>
#include <stream.hxx>
#include <winsta.h>
#include <allproc.h>

typedef HANDLE __stdcall FN_WinStationOpenServerW(LPWSTR);
typedef BOOLEAN __stdcall FN_WinStationGetTermSrvCountersValue(HANDLE, ULONG, PVOID);
typedef BOOLEAN __stdcall FN_WinStationCloseServer(HANDLE);

static DWORD g_cTSSessions = -1;

#include "carefulreader.hxx"
#define ARRAYSIZE(a)    (sizeof(a)/sizeof(a[0]))
void CountTSSessions()
{
   DWORD cTSSessions = 0;
   HINSTANCE hTSLib = LoadLibraryW(L"winsta.dll");
   if (hTSLib) 
   {
      FN_WinStationOpenServerW *pFNOpen = (FN_WinStationOpenServerW *)GetProcAddress(hTSLib, "WinStationOpenServerW");
      FN_WinStationGetTermSrvCountersValue *pFNCount = (FN_WinStationGetTermSrvCountersValue *)GetProcAddress(hTSLib, "WinStationGetTermSrvCountersValue");
      FN_WinStationCloseServer *pFNClose = (FN_WinStationCloseServer *)GetProcAddress(hTSLib, "WinStationCloseServer");
      if (pFNOpen && pFNCount && pFNClose) 
      {
	 HANDLE hServer = pFNOpen(reinterpret_cast<WCHAR*>(SERVERNAME_CURRENT));
	 if (hServer != NULL)
	 {
	    TS_COUNTER tsCounters[2] = {0};

	    tsCounters[0].counterHead.dwCounterID = TERMSRV_CURRENT_DISC_SESSIONS;
	    tsCounters[1].counterHead.dwCounterID = TERMSRV_CURRENT_ACTIVE_SESSIONS;

	    if (pFNCount(hServer, ARRAYSIZE(tsCounters), tsCounters))
	    {
	       int i;
               for (i = 0; i < ARRAYSIZE(tsCounters); i++)
	       {
		  if (tsCounters[i].counterHead.bResult)
		  {
                    cTSSessions += tsCounters[i].dwValue;
		  }
	       }
	    }
	    pFNClose(hServer);
	 }
      }
      FreeLibrary(hTSLib);
   }
   g_cTSSessions = cTSSessions;
}

//+-------------------------------------------------------------------------
//
//  Function:   CLIPFORMAT_UserSize
//
//  Synopsis:   Sizes a CLIPFORMAT.
//
//  Derivation: A union of a long and a string.
//
//  history:    Feb-96   Ryszardk      Created
//
//--------------------------------------------------------------------------

unsigned long  __RPC_USER
CLIPFORMAT_UserSize(
    unsigned long * pFlags,
    unsigned long   Offset,
    CLIPFORMAT    * pObject )
{
    if ( !pObject )
        return( Offset );

    if (g_cTSSessions == -1) 
    {
       CountTSSessions();
    }
    // userCLIPFORMAT is an encapsulated union with a string.

    LENGTH_ALIGN( Offset, 3);
    Offset += sizeof(long) + sizeof(void *);

    if ( (NON_STANDARD_CLIPFORMAT(pObject)) && ((REMOTE_CLIPFORMAT( pFlags) ) 
	 || (g_cTSSessions > 1 ))) // ignore console session
        {
        wchar_t temp[CLIPFORMAT_BUFFER_MAX];

        int ret = GetClipboardFormatName( *pObject,
                                          temp,
                                          CLIPFORMAT_BUFFER_MAX - 1 );

        if ( ret )
            {
            Offset += 3 * sizeof(long)  +  (ret+1) * sizeof(wchar_t);
            }
        else
            RAISE_RPC_EXCEPTION( DV_E_CLIPFORMAT );
        }

    return( Offset );
}


//+-------------------------------------------------------------------------
//
//  Function:   CLIPFORMAT_UserMarshal
//
//  Synopsis:   Marshals a CLIPFORMAT.
//
//  Derivation: A union of a long and a string.
//
//  history:    Feb-96   Ryszardk      Created
//
//--------------------------------------------------------------------------

unsigned char __RPC_FAR *  __RPC_USER
CLIPFORMAT_UserMarshal(
    unsigned long * pFlags,
    unsigned char * pBuffer,
    CLIPFORMAT    * pObject )
{
    if ( !pObject )
        return pBuffer;

    // userCLIPFORMAT is an encapsulated union with a string.

    ALIGN( pBuffer, 3);

    if ( (NON_STANDARD_CLIPFORMAT(pObject)) && ((REMOTE_CLIPFORMAT( pFlags) ) 
	 || (g_cTSSessions > 1 ))) // ignore console session
        {
        // sending a wide string

        unsigned long    ret;

        *(PULONG_LV_CAST pBuffer)++ = WDT_DATA_MARKER;
        *(PULONG_LV_CAST pBuffer)++ = (ulong) *pObject;

        // On Chicago this is GetClipboardFormatNameX.
        // When the buffer is too short, this call would still
        // return a decent, null terminated, truncated string.
        //

        ret = (ulong) GetClipboardFormatName( *pObject,
                                               (wchar_t *)(pBuffer + 12),
                                               CLIPFORMAT_BUFFER_MAX - 1
                                             );


        if ( ret )
	{
	   ret++;
	   // conformat size etc. for string.
   
	   *(PULONG_LV_CAST pBuffer)++ = ret;
	   *(PULONG_LV_CAST pBuffer)++ = 0;
	   *(PULONG_LV_CAST pBuffer)++ = ret;
            // skip the string in the bbuffer, including the terminator

            pBuffer += ret * sizeof(wchar_t);
	}
	else
            RpcRaiseException( DV_E_CLIPFORMAT );
        }
    else
        {
        // sending the number itself

        *(PULONG_LV_CAST pBuffer)++ = WDT_HANDLE_MARKER;
        *(PULONG_LV_CAST pBuffer)++ = (ulong) *pObject;
        }

    return( pBuffer );
}


//+-------------------------------------------------------------------------
//
//  Function:   CLIPFORMAT_UserUnmarshal
//
//  Synopsis:   Unmarshals a CLIPFORMAT; registers if needed.
//
//  Derivation: A union of a long and a string.
//
//  history:    Feb-96   Ryszardk      Created
//              Aug-99   JohnStra      Added consistency checks
//
//--------------------------------------------------------------------------

unsigned char __RPC_FAR *  __RPC_USER
CLIPFORMAT_UserUnmarshal(
    unsigned long * pFlags,
    unsigned char * pBuffer,
    CLIPFORMAT    * pObject )
{
    ulong UnionDisc;
    UINT  cf;

    // Get the buffer size and the start of the buffer.
    CUserMarshalInfo MarshalInfo( pFlags, pBuffer );
    ULONG_PTR BufferSize   = MarshalInfo.GetBufferSize();
    UCHAR*    pBufferStart = MarshalInfo.GetBuffer();

    // Align the buffer and save the fixup size.
    ALIGN( pBuffer, 3 );
    ULONG_PTR cbFixup = (ULONG_PTR)(pBuffer - pBufferStart);

    // Check for EOB before accessing buffer.
    CHECK_BUFFER_SIZE( BufferSize, cbFixup + (2 * sizeof( ULONG )) );

    UnionDisc =        *(PULONG_LV_CAST pBuffer)++;
    cf        = (WORD) *(PULONG_LV_CAST pBuffer)++;

    if ( WDT_DATA_MARKER == UnionDisc )
        {
        // CLIPFORMAT value must be in valid range.
        if ( cf < 0xc000 || cf > 0xffff )
            RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );

        // Check for EOB before accessing string header.
        CHECK_BUFFER_SIZE( BufferSize, cbFixup + (5 * sizeof( ULONG )) );

        // Get the string header from the buffer and subtract the string
        // header from the BufferSize.
        ULONG ConfSize = *(PULONG_LV_CAST pBuffer)++;
        ULONG Offset = *(PULONG_LV_CAST pBuffer)++;
        ULONG ActualSize = *(PULONG_LV_CAST pBuffer)++;

        // Verify the header: Offset must always be zero, length must match
        // size, and size can't be zero since that would mean no NULL
        // terminator.
        if ( 0 != Offset || ActualSize != ConfSize || 0 == ActualSize )
            RAISE_RPC_EXCEPTION( RPC_X_INVALID_BOUND );

        // Check for EOB before accessing string.
        CHECK_BUFFER_SIZE(
            BufferSize,
            cbFixup + (5 * sizeof(ULONG)) + (ActualSize * sizeof(WCHAR)) );

        // Last two bytes of the buffer must be unicode terminator
        if ( *(WCHAR*)(pBuffer + ((ActualSize-1) * sizeof(WCHAR))) != 0x0000 )
            RAISE_RPC_EXCEPTION( RPC_X_INVALID_BOUND );

        // Must be only 1 unicode terminator.
        if ( (ULONG)(lstrlenW( (WCHAR*)pBuffer ) + 1) != ActualSize )
            RAISE_RPC_EXCEPTION( RPC_X_INVALID_BOUND );

        // Register the clipboard format.
        cf = RegisterClipboardFormat( (wchar_t *)pBuffer );
        if ( cf == 0 )
            RAISE_RPC_EXCEPTION( DV_E_CLIPFORMAT );

        // Advance buffer pointer past string.
        pBuffer += ActualSize * sizeof(wchar_t);
        }
    else if ( WDT_HANDLE_MARKER != UnionDisc )
        {
        RAISE_RPC_EXCEPTION( RPC_S_INVALID_TAG );
        }

    *pObject = (CLIPFORMAT) cf;

    return( pBuffer );
}

//+-------------------------------------------------------------------------
//
//  Function:   CLIPFORMAT_UserFree
//
//  Synopsis:   Frees remnants of CLIPFORMAT.
//
//  Derivation: A union of a long and a string.
//
//  history:    Feb-96   Ryszardk      Created
//
//--------------------------------------------------------------------------

void  __RPC_USER
CLIPFORMAT_UserFree(
    unsigned long * pFlags,
    CLIPFORMAT * pObject )
{
    // Nothing to free, as nothing gets allocated when we unmarshal.
}

#if defined(_WIN64)

//+-------------------------------------------------------------------------
//
//  Function:   CLIPFORMAT_UserSize64
//
//  Synopsis:   Sizes a CLIPFORMAT.
//
//  Derivation: A union of a long and a string.
//
//  history:    Dec-00   JohnDoty      Created from 32bit function
//
//--------------------------------------------------------------------------

unsigned long  __RPC_USER
CLIPFORMAT_UserSize64 (
    unsigned long * pFlags,
    unsigned long   Offset,
    CLIPFORMAT    * pObject )
{
    if ( !pObject )
        return( Offset );

    // userCLIPFORMAT is an encapsulated union with a string.
    // max align of the union is size of 64b pointer.
    LENGTH_ALIGN( Offset, 7 );
    Offset += 8;               // 4 byte discriminant, 4 byte alignment
    
    if ( (NON_STANDARD_CLIPFORMAT(pObject)) && ((REMOTE_CLIPFORMAT( pFlags) ) 
	 || (g_cTSSessions > 1 ))) // ignore console session
    {
        // Writing another pointer...
        Offset += 8;           // 64b pointer

        wchar_t temp[CLIPFORMAT_BUFFER_MAX + 1];

        int ret = GetClipboardFormatName( *pObject,
                                          temp,
                                          CLIPFORMAT_BUFFER_MAX );

        if ( ret )
        {
            // This string has 3 conformance fields (64b) followed by an
            // array of 16b chars.
            Offset += (3 * 8)  +  ((ret + 1) * 2);
        }
        else
            RAISE_RPC_EXCEPTION( DV_E_CLIPFORMAT );
    }
    else
    {
        // Writing a DWORD
        Offset += 4;
    }

    return( Offset );
}


//+-------------------------------------------------------------------------
//
//  Function:   CLIPFORMAT_UserMarshal64
//
//  Synopsis:   Marshals a CLIPFORMAT.
//
//  Derivation: A union of a long and a string.
//
//  history:    Dec-00   JohnDoty      Created from 32bit function
//
//--------------------------------------------------------------------------

unsigned char __RPC_FAR *  __RPC_USER
CLIPFORMAT_UserMarshal64 (
    unsigned long * pFlags,
    unsigned char * pBuffer,
    CLIPFORMAT    * pObject )
{
    if ( !pObject )
        return pBuffer;

    // userCLIPFORMAT is an encapsulated union with a string.

    ALIGN( pBuffer, 7 );

    if ( (NON_STANDARD_CLIPFORMAT(pObject)) && ((REMOTE_CLIPFORMAT( pFlags) ) 
	 || (g_cTSSessions > 1 ))) // ignore console session
    {
        // sending a wide string
        unsigned long    ret;

        *(PULONG_LV_CAST pBuffer)++ = WDT_DATA_MARKER;
        ALIGN( pBuffer, 7 );
        *(PHYPER_LV_CAST pBuffer)++ = (hyper) *pObject;

        // On Chicago this is GetClipboardFormatNameX.
        // When the buffer is too short, this call would still
        // return a decent, null terminated, truncated string.
        //
        ret = (ulong) GetClipboardFormatName( *pObject,
                                               (wchar_t *)(pBuffer + (3 * 8)),
                                               CLIPFORMAT_BUFFER_MAX
                                            );

        if ( ret )
        {
            // Account for the trailing NULL.
            ret ++;

            // conformat size etc. for string.
            *(PHYPER_LV_CAST pBuffer)++ = ret;   // Conformance
            *(PHYPER_LV_CAST pBuffer)++ = 0;     // Offset
            *(PHYPER_LV_CAST pBuffer)++ = ret;   // Actual Size

            // skip the string in the buffer, including the terminator
            pBuffer += (ret * 2);
        }
        else
            RpcRaiseException( DV_E_CLIPFORMAT );
    }
    else
    {
        // sending the number itself

        *(PULONG_LV_CAST pBuffer)++ = WDT_HANDLE_MARKER;
        ALIGN( pBuffer, 7 );
        *(PULONG_LV_CAST pBuffer)++ = (ulong) *pObject;
    }

    return( pBuffer );
}


//+-------------------------------------------------------------------------
//
//  Function:   CLIPFORMAT_UserUnmarshal64
//
//  Synopsis:   Unmarshals a CLIPFORMAT; registers if needed.
//
//  Derivation: A union of a long and a string.
//
//  history:    Dec-00   JohnDoty      Created from 32bit function
//
//--------------------------------------------------------------------------

unsigned char __RPC_FAR *  __RPC_USER
CLIPFORMAT_UserUnmarshal64 (
    unsigned long * pFlags,
    unsigned char * pBuffer,
    CLIPFORMAT    * pObject )
{
    ulong  UnionDisc;
    hyper  cf;

    // Get the buffer size and the start of the buffer.
    CUserMarshalInfo MarshalInfo( pFlags, pBuffer );
    CarefulBufferReader stream( pBuffer, MarshalInfo.GetBufferSize() );

    stream.Align( 8 );                 // Must align on 8, union rules.
    UnionDisc = stream.ReadULONGNA();  // ...so no need to align on 4 here...
    stream.Align( 8 );                 // ...but must explicitly align to 8 here again.

    if ( WDT_DATA_MARKER == UnionDisc )
    {
        cf = stream.ReadHYPERNA();     // Just aligned 8, so don't align again.

        // CLIPFORMAT value must be in valid range.
        if ( cf < 0xc000 || cf > 0xffff )
            RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );

        // Get the string header from the buffer and subtract the string
        // header from the BufferSize.
        hyper ConfSize   = stream.ReadHYPERNA();
        hyper Offset     = stream.ReadHYPERNA();
        hyper ActualSize = stream.ReadHYPERNA();

        // Verify the header: Offset must always be zero, length must match
        // size, and size can't be zero since that would mean no NULL
        // terminator.
        if ( 0 != Offset || ActualSize != ConfSize || 0 == ActualSize )
            RAISE_RPC_EXCEPTION( RPC_X_INVALID_BOUND );

        // Check for EOB before accessing string.
        stream.CheckSize((unsigned long)(ActualSize * sizeof(WCHAR)));

        // Last two bytes of the buffer must be unicode terminator
        WCHAR *pCheck = (WCHAR *)stream.GetBuffer();
        if ( pCheck[ActualSize-1] != 0x0000 )
            RAISE_RPC_EXCEPTION( RPC_X_INVALID_BOUND );
        
        // Must be only 1 unicode terminator.
        if ( (ULONG)(lstrlenW( pCheck ) + 1) != ActualSize )
            RAISE_RPC_EXCEPTION( RPC_X_INVALID_BOUND );

        // Register the clipboard format.
        cf = RegisterClipboardFormat( pCheck );
        if ( cf == 0 )
            RAISE_RPC_EXCEPTION( DV_E_CLIPFORMAT );

        // Advance buffer pointer past string.
        stream.Advance((unsigned long)(ActualSize * sizeof(WCHAR)));
    }
    else if ( WDT_HANDLE_MARKER == UnionDisc )
    {        
        cf = (hyper)stream.ReadULONGNA();  // Just aligned on 8...
    }
    else
    {
        RAISE_RPC_EXCEPTION( RPC_S_INVALID_TAG );
    }

    *pObject = (CLIPFORMAT) cf;

    return( stream.GetBuffer() );
}

//+-------------------------------------------------------------------------
//
//  Function:   CLIPFORMAT_UserFree64
//
//  Synopsis:   Frees remnants of CLIPFORMAT.
//
//  Derivation: A union of a long and a string.
//
//  history:    Dec-00   JohnDoty      Created from 32bit function
//
//--------------------------------------------------------------------------

void  __RPC_USER
CLIPFORMAT_UserFree64 (
    unsigned long * pFlags,
    CLIPFORMAT * pObject )
{
    // Nothing to free, as nothing gets allocated when we unmarshal.
}

#endif // win64