/****************************************************************************
 *
 *    $Archive:   S:/STURGEON/SRC/Q931/VCS/q931pdu.c_v  $
 *
 *  INTEL Corporation Prorietary Information
 *
 *  This listing is supplied under the terms of a license agreement
 *  with INTEL Corporation and may not be copied nor disclosed except
 *  in accordance with the terms of that agreement.
 *
 *    Copyright (c) 1996 Intel Corporation.
 *
 *    $Revision:   1.67.1.1  $
 *    $Date:   20 Jun 1997 14:13:22  $
 *    $Author:   MANDREWS  $
 *
 *    Deliverable:
 *
 *    Abstract: Parser routines for Q931 PDUs
 *
 *    Notes:
 *
 ***************************************************************************/
#pragma comment (exestr, "$Workfile:   q931pdu.c  $ $Revision:   1.67.1.1  $")

// [ ]  Do another integration of own q931test area.
// [ ]  Alias values displayed in tracing routines.
// - - - -  -  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// STANDARDS ISSUES
// - - - -  -  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// [ ]  !!! EndpointType contains MC info, so Setup_UUIE doesnt need MC field !!!
// [ ]  !!! Need to decide how CallType is to be used !!!
// [ ]  !!! ALERTING message is missing the ConferenceID field !!!
// [ ]  !!! Place needed for Caller and Callee transport addr, or else explanation of how this information is available round-trip !!!
// [ ]  !!! FACILITY message is missing the protocolIdentifier field !!!

//------------------------------------------------------------------------------
// Note:  These parsing details have not yet been supported:
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// 1) variable octet fields having extending groups,
//    extending indications, or escape for extensions. (See 4.5.1)
// 2) codeset recognition and exclusion based on SHIFT (See 4.5.2)
// 3) correct ignoring of escapes for nationally specific message types.
// 4) The call reference value is 2 bytes long sizeof(WORD).
//    A call reference of 0 means, the message pertains to all
//    calls on the same data link.
//------------------------------------------------------------------------------

#pragma warning ( disable : 4100 4115 4201 4214 4514 )

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>

#include <windows.h>
#include <string.h>

#include "q931asn.h"
#include "q931asn1.h"

#include "common.h"
#include "q931.h"
#include "isrg.h"

#include "utils.h"
#include "q931pdu.h"

#ifdef UNICODE_TRACE
// We include this header to fix problems with macro expansion when Unicode is turned on.
#include "unifix.h"
#endif


//==========================================================
// CALLED PARTY FIELD DEFINITIONS
//==========================================================
// called party encoding bits...
#define CALLED_PARTY_EXT_BIT        0x80

// called party number type
#define CALLED_PARTY_TYPE_UNKNOWN   0x00
        // ...other types are not defined because they are not used...

// called party numbering plan
#define CALLED_PARTY_PLAN_E164      0x01
        // ...other plans are not defined because they are not used...
 


//==========================================================
// BEARER FIELD DEFINITIONS
//==========================================================
// bearer encoding bits...
#define BEAR_EXT_BIT                0x80

// bearer coding standards...
#define BEAR_CCITT                  0x00
        // ...others not needed...

// bearer information transfer capability...
#define BEAR_UNRESTRICTED_DIGITAL   0x08
        // ...others not needed...

// bearer transfer mode...
#define BEAR_CIRCUIT_MODE			0x00
#define BEAR_PACKET_MODE            0x40
        // ...others not needed...

// bearer information transfer rate...
#define BEAR_NO_CIRCUIT_RATE        0x00
#define BEAR_MULTIRATE				0x18
        // ...others not needed...

// bearer layer1 protocol...
#define BEAR_LAYER1_INDICATOR       0x20
#define BEAR_LAYER1_H221_H242       0x05
        // ...others not needed...


#define Q931_PROTOCOL_ID1           0
#define Q931_PROTOCOL_ID2           0
#define Q931_PROTOCOL_ID3           8
#define Q931_PROTOCOL_ID4           2250
#define Q931_PROTOCOL_ID5           0
#define Q931_PROTOCOL_ID6           1

static struct ObjectID_ ProtocolId1;
static struct ObjectID_ ProtocolId2;
static struct ObjectID_ ProtocolId3;
static struct ObjectID_ ProtocolId4;
static struct ObjectID_ ProtocolId5;
static struct ObjectID_ ProtocolId6;

static struct _seqof1 TempProtocol;

MESSAGEIDTYPE MessageSet[] =
{
    ALERTINGMESSAGETYPE,
    PROCEEDINGMESSAGETYPE,
    CONNECTMESSAGETYPE,
    CONNECTACKMESSAGETYPE,
    PROGRESSMESSAGETYPE,
    SETUPMESSAGETYPE,
    SETUPACKMESSAGETYPE,

    RESUMEMESSAGETYPE,
    RESUMEACKMESSAGETYPE,
    RESUMEREJMESSAGETYPE,
    SUSPENDMESSAGETYPE,
    SUSPENDACKMESSAGETYPE,
    SUSPENDREJMESSAGETYPE,
    USERINFOMESSAGETYPE,

    DISCONNECTMESSAGETYPE,
    RELEASEMESSAGETYPE,
    RELEASECOMPLMESSAGETYPE,
    RESTARTMESSAGETYPE,
    RESTARTACKMESSAGETYPE,

    SEGMENTMESSAGETYPE,
    CONGCTRLMESSAGETYPE,
    INFORMATIONMESSAGETYPE,
    NOTIFYMESSAGETYPE,
    STATUSMESSAGETYPE,
    STATUSENQUIRYMESSAGETYPE,

	HOLDMESSAGETYPE,
	HOLDACKMESSAGETYPE,
	HOLDREJECTMESSAGETYPE,
	RETRIEVEMESSAGETYPE,
	RETRIEVEACKMESSAGETYPE,
	RETRIEVEREJECTMESSAGETYPE,
    FACILITYMESSAGETYPE,
	REGISTERMESSAGETYPE
};

//====================================================================================
//====================================================================================
static CS_STATUS
AliasToSeqof(struct _seqof3 **ppTarget, PCC_ALIASNAMES pSource)
{
    if (ppTarget == NULL)
    {
        return CS_BAD_PARAM;
    }
    *ppTarget = NULL;
    if (pSource == NULL)
    {
        return CS_OK;
    }
    if (pSource && (pSource->wCount))
    {
        struct _seqof3 *ListHead = NULL;
        struct _seqof3 *CurrentNode = NULL;
        LPWSTR pData = NULL;         // UNICODE STRING
        int SourceItem;
        WORD x;

        for (SourceItem = pSource->wCount - 1; SourceItem >= 0; SourceItem--)
        {
            BOOL Cleanup = FALSE;

            // first do the required memory allocations...
            CurrentNode = (struct _seqof3 *)Malloc(sizeof(struct _seqof3));
            if (CurrentNode == NULL)
            {
                Cleanup = TRUE;
            }
            else
            {
                if (pSource->pItems[SourceItem].wType == CC_ALIAS_H323_ID)
                {
                    if ((pSource->pItems[SourceItem].wDataLength != 0) &&
                        (pSource->pItems[SourceItem].pData != NULL))
                    {
                        pData = (LPWSTR)Malloc(pSource->pItems[SourceItem].wDataLength *
                            sizeof(WCHAR));
                        if (pData == NULL)
                        {
                            Free(CurrentNode);
                            Cleanup = TRUE;
                        }
                    }
                }
            }
            if (Cleanup)
            {
                for (CurrentNode = ListHead; CurrentNode; CurrentNode = ListHead)
                {
                    ListHead = CurrentNode->next;
                    if (CurrentNode->value.choice == h323_ID_chosen)
                    {
                        if (CurrentNode->value.u.h323_ID.value)
                        {
                            Free(CurrentNode->value.u.h323_ID.value);
                        }
                    }
                    Free(CurrentNode);
                }
                return CS_NO_MEMORY;
            }

            // then do the required memory copying.
            if (pSource->pItems[SourceItem].wType == CC_ALIAS_H323_ID)
            {
                CurrentNode->value.choice = h323_ID_chosen;
                if ((pSource->pItems[SourceItem].wDataLength != 0) &&
                    (pSource->pItems[SourceItem].pData != NULL))
                {
                    CurrentNode->value.u.h323_ID.length =
                        pSource->pItems[SourceItem].wDataLength;
                    for (x = 0; x < pSource->pItems[SourceItem].wDataLength; x++)
                    {
                        pData[x] = pSource->pItems[SourceItem].pData[x];
                    }
                    CurrentNode->value.u.h323_ID.value = pData;
                }
                else
                {
                    CurrentNode->value.u.h323_ID.length = 0;
                    CurrentNode->value.u.h323_ID.value = NULL;
                }
            }
            else if (pSource->pItems[SourceItem].wType == CC_ALIAS_H323_PHONE)
            {
                CurrentNode->value.choice = e164_chosen;
                if ((pSource->pItems[SourceItem].wDataLength != 0) &&
                    (pSource->pItems[SourceItem].pData != NULL))
                {
                    for (x = 0; x < pSource->pItems[SourceItem].wDataLength; x++)
                    {
                        CurrentNode->value.u.e164[x] = (BYTE)(pSource->pItems[SourceItem].pData[x]);
                    }
                    CurrentNode->value.u.e164[pSource->pItems[SourceItem].wDataLength] = '\0';
                }
                else
                {
                    CurrentNode->value.u.e164[0] = '\0';
                }
            }
            CurrentNode->next = ListHead;
            ListHead = CurrentNode;
        }
        *ppTarget = ListHead;
    }
    return CS_OK;
}

//====================================================================================
//====================================================================================
static CS_STATUS
AliasWithPrefixToSeqof(struct _seqof3 **ppTarget, PCC_ALIASNAMES pSource)
{
    if (ppTarget == NULL)
    {
        return CS_BAD_PARAM;
    }
    *ppTarget = NULL;
    if (pSource == NULL)
    {
        return CS_OK;
    }
    if (pSource && (pSource->wCount))
    {
        struct _seqof3 *ListHead = NULL;
        struct _seqof3 *CurrentNode = NULL;
        int SourceItem;

        for (SourceItem = pSource->wCount - 1; SourceItem >= 0; SourceItem--)
        {
			PCC_ALIASITEM pItem = &pSource->pItems[SourceItem];
			LPWSTR pData = NULL;        // UNICODE STRING
            BOOL Cleanup = FALSE;
            unsigned uPrefixLength;
            unsigned uDataLength;
            unsigned x;
            
            if (pItem->pPrefix != NULL &&
                pItem->wPrefixLength > 0)
            {
                uPrefixLength = (unsigned) pItem->wPrefixLength;
            }
            else
            {
                uPrefixLength = 0;
            }

            if (pItem->pData != NULL &&
                pItem->wDataLength > 0)
            {
                uDataLength = (unsigned) pItem->wDataLength;
            }
            else
            {
                uDataLength = 0;
            }

            // first do the required memory allocations...
            CurrentNode = (struct _seqof3 *)Malloc(sizeof(struct _seqof3));
            if (CurrentNode == NULL)
            {
                Cleanup = TRUE;
            }
            else
            {
                if (pItem->wType == CC_ALIAS_H323_ID)
                {
#ifdef USE_PREFIX_FOR_H323_ID
                    if (uPrefixLength != 0 || uDataLength != 0)
                    {
                        pData = (LPWSTR)Malloc((uPrefixLength + uDataLength) * sizeof(WCHAR));
#else
                    if (uDataLength != 0)
                    {
                        pData = (LPWSTR)Malloc((uDataLength) * sizeof(WCHAR));
#endif
                        if (pData == NULL)
                        {
                            Free(CurrentNode);
                            Cleanup = TRUE;
                        }
                    }
                }
            }
            if (Cleanup)
            {
                for (CurrentNode = ListHead; CurrentNode; CurrentNode = ListHead)
                {
                    ListHead = CurrentNode->next;
                    if (CurrentNode->value.choice == h323_ID_chosen)
                    {
                        if (CurrentNode->value.u.h323_ID.value)
                        {
                            Free(CurrentNode->value.u.h323_ID.value);
                        }
                    }
                    Free(CurrentNode);
                }
                return CS_NO_MEMORY;
            }

            // then do the required memory copying.
            switch (pItem->wType)
            {
            case CC_ALIAS_H323_ID:
                CurrentNode->value.choice = h323_ID_chosen;
#ifdef USE_PREFIX_FOR_H323_ID
                if (uPrefixLength != 0 || uDataLength != 0)
                {
                    CurrentNode->value.u.h323_ID.length = (WORD)(uPrefixLength + uDataLength);
                    for (x = 0; x < uPrefixLength; ++x)
                    {
                        pData[x] = pItem->pPrefix[x];
                    }
                    for (x = 0; x < uDataLength; ++x)
                    {
                        pData[uPrefixLength + x] = pItem->pData[x];
                    }
#else
                if (uDataLength != 0)
                {
                    CurrentNode->value.u.h323_ID.length = (WORD)(uDataLength);
                    for (x = 0; x < uDataLength; ++x)
                    {
                        pData[x] = pItem->pData[x];
                    }
#endif
                    CurrentNode->value.u.h323_ID.value = pData;
                }
                else
                {
                    CurrentNode->value.u.h323_ID.length = 0;
                    CurrentNode->value.u.h323_ID.value  = NULL;
                }
                break;

            case CC_ALIAS_H323_PHONE:
                CurrentNode->value.choice = e164_chosen;
                for (x = 0; x < uPrefixLength; ++x)
                {
                    CurrentNode->value.u.e164[x] = (BYTE)(pItem->pPrefix[x]);
                }
                for (x = 0; x < uDataLength; ++x)
                {
                    CurrentNode->value.u.e164[uPrefixLength + x] = (BYTE)(pItem->pData[x]);
                }
                for (x = uDataLength + uPrefixLength; x < sizeof(CurrentNode->value.u.e164); ++x)
                {
                    CurrentNode->value.u.e164[x] = 0;
                }
                break;

            default:
                Free(CurrentNode);
                for (CurrentNode = ListHead; CurrentNode; CurrentNode = ListHead)
                {
                    ListHead = CurrentNode->next;
                    if (CurrentNode->value.choice == h323_ID_chosen)
                    {
                        if (CurrentNode->value.u.h323_ID.value)
                        {
                            Free(CurrentNode->value.u.h323_ID.value);
                        }
                    }
                    Free(CurrentNode);
                }
                return CS_BAD_PARAM;
            } // switch
            CurrentNode->next = ListHead;
            ListHead = CurrentNode;
        }
        *ppTarget = ListHead;
    }
    return CS_OK;
}

//====================================================================================
//====================================================================================
static CS_STATUS
SeqofToAlias(PCC_ALIASNAMES *ppTarget, struct _seqof3 *pSource)
{
    struct _seqof3 *ListHead = NULL;
    struct _seqof3 *CurrentNode = NULL;
    WORD wCount;
    WORD x = 0;
    PCC_ALIASITEM p = NULL;
    CS_STATUS status = CS_OK;

    if (ppTarget == NULL)
    {
        return CS_BAD_PARAM;
    }
    *ppTarget = NULL;
    if (pSource == NULL)
    {
        return CS_OK;
    }

    wCount = 0;
    for (CurrentNode = pSource; CurrentNode; CurrentNode = CurrentNode->next)
    {
        wCount++;
    }

    *ppTarget = (PCC_ALIASNAMES)Malloc(sizeof(CC_ALIASNAMES));
    if (*ppTarget == NULL)
    {
        return CS_NO_MEMORY;
    }

    (*ppTarget)->pItems = (PCC_ALIASITEM)Malloc(wCount * sizeof(CC_ALIASITEM));
    if ((*ppTarget)->pItems == NULL)
    {
        Free(*ppTarget);
        *ppTarget = NULL;
        return CS_NO_MEMORY;
    }

    p = (*ppTarget)->pItems;

    for (CurrentNode = pSource; CurrentNode; CurrentNode = CurrentNode->next)
    {
        WORD y;

	    p[x].wPrefixLength = 0;
        p[x].pPrefix = NULL;

        switch (CurrentNode->value.choice)
        {
        case h323_ID_chosen:
            p[x].wType = CC_ALIAS_H323_ID;
            if ((CurrentNode->value.u.h323_ID.length != 0) &&
                    (CurrentNode->value.u.h323_ID.value != NULL))
            {
                p[x].wDataLength = (WORD)CurrentNode->value.u.h323_ID.length;
                p[x].pData = (LPWSTR)Malloc(CurrentNode->value.u.h323_ID.length * sizeof(p[x].pData[0]));
                if (p[x].pData != NULL)
                {
                    for (y = 0; y < CurrentNode->value.u.h323_ID.length; y++)
                    {
                        p[x].pData[y] = (WCHAR)((CurrentNode->value.u.h323_ID.value)[y]);
                    }
                    x++;
                }
                else
                {
                    status = CS_NO_MEMORY;
                }
            }
            break;


        case e164_chosen:
            p[x].wType = CC_ALIAS_H323_PHONE;
            p[x].wDataLength = (WORD)strlen(CurrentNode->value.u.e164);
            p[x].pData = (LPWSTR)Malloc((p[x].wDataLength+1) * sizeof(p[x].pData[0]));
            if (p[x].pData != NULL)
            {
                for (y = 0; y < p[x].wDataLength; y++)
                {
                    p[x].pData[y] = CurrentNode->value.u.e164[y];
                }
                p[x].pData[p[x].wDataLength] = 0;
                x++;
            }
            else
            {
                status = CS_NO_MEMORY;
            }
            break;

        default:
            // we don't currently handle other alias types
            break;
        } // switch

        if (status != CS_OK)
        {
            // Free everything that has been allocated so far...
            for (y = 0; y < x; y++)
            {
                Free(p[y].pData);
            }
            Free(p);
            Free(*ppTarget);
            *ppTarget = NULL;
            return status;
        }
    }

    // any aliases?
    if (x > 0) {

        // save number of aliases
        (*ppTarget)->wCount = x;

    } else {

        //
        // Note Q931FreeAliasNames does not free CC_ALIASNAMES structure
        // when wCount is set to zero
        //
        Free(p);
        Free(*ppTarget);
        *ppTarget = NULL;
    }

    return CS_OK;
}

//====================================================================================
//====================================================================================
static CS_STATUS
FreeSeqof(struct _seqof3 *pSource)
{
    struct _seqof3 *CurrentNode = NULL;

    for (CurrentNode = pSource; CurrentNode; CurrentNode = pSource)
    {
        pSource = CurrentNode->next;
        if (CurrentNode->value.choice == h323_ID_chosen)
        {
            if (CurrentNode->value.u.h323_ID.value)
            {
                Free(CurrentNode->value.u.h323_ID.value);
            }
        }
        Free(CurrentNode);
    }
    return CS_OK;
}

//====================================================================================
//====================================================================================
static CS_STATUS
Q931CopyAliasItemToAliasAddr(AliasAddress *pTarget, PCC_ALIASITEM pSource)
{
    AliasAddress *pNewAddress = NULL;
    WORD x;

    if (pTarget == NULL)
    {
        return CS_BAD_PARAM;
    }
    if (pSource == NULL)
    {
        return CS_BAD_PARAM;
    }

    pNewAddress = pTarget;

    if (pSource->wType == CC_ALIAS_H323_ID)
    {
        pNewAddress->choice = h323_ID_chosen;
        if ((pSource->wDataLength != 0) && (pSource->pData != NULL))
        {
            LPWSTR pData = NULL;         // UNICODE STRING
            pData = (LPWSTR)Malloc(pSource->wDataLength * sizeof(WCHAR));
            if (pData == NULL)
            {
                return CS_NO_MEMORY;
            }
            pNewAddress->u.h323_ID.length = pSource->wDataLength;
            for (x = 0; x < pSource->wDataLength; x++)
            {
                pData[x] = pSource->pData[x];
            }
            pNewAddress->u.h323_ID.value = pData;
        }
        else
        {
            pNewAddress->u.h323_ID.length = 0;
            pNewAddress->u.h323_ID.value = NULL;
        }
    }
    else if (pSource->wType == CC_ALIAS_H323_PHONE)
    {
        pNewAddress->choice = e164_chosen;
        if ((pSource->wDataLength != 0) && (pSource->pData != NULL))
        {
            for (x = 0; x < pSource->wDataLength; x++)
            {
                pNewAddress->u.e164[x] = (BYTE)(pSource->pData[x]);
            }
            pNewAddress->u.e164[pSource->wDataLength] = '\0';
        }
        else
        {
            pNewAddress->u.e164[0] = '\0';
        }
    }

    return CS_OK;
}

//====================================================================================
//====================================================================================
static CS_STATUS
Q931AliasAddrToAliasItem(PCC_ALIASITEM *ppTarget, AliasAddress *pSource)
{
    PCC_ALIASITEM pNewItem = NULL;
    WORD y;

    if (ppTarget == NULL)
    {
        return CS_BAD_PARAM;
    }
    if (pSource == NULL)
    {
        *ppTarget = NULL;
        return CS_OK;
    }

    pNewItem = (PCC_ALIASITEM)Malloc(sizeof(CC_ALIASITEM));
    if (pNewItem == NULL)
    {
        *ppTarget = NULL;
        return CS_NO_MEMORY;
    }
    memset(pNewItem, 0, sizeof(*pNewItem));

    switch (pSource->choice)
    {
    case h323_ID_chosen:
        pNewItem->wType = CC_ALIAS_H323_ID;
        if ((pSource->u.h323_ID.length != 0) &&
            (pSource->u.h323_ID.value  != NULL))
        {
            // convert the text from UNICODE to ascii.
            pNewItem->wDataLength = (WORD)pSource->u.h323_ID.length;
            pNewItem->pData = (LPWSTR)Malloc(pSource->u.h323_ID.length * sizeof(pNewItem->pData[0]));
            if (pNewItem->pData == NULL)
            {
                Free(pNewItem);
                return CS_NO_MEMORY;
            }
            for (y = 0; y < pSource->u.h323_ID.length; y++)
            {
                pNewItem->pData[y] = (WCHAR)((pSource->u.h323_ID.value)[y]);
            }
        }
        break;

    case e164_chosen:
        pNewItem->wType = CC_ALIAS_H323_PHONE;
        pNewItem->wDataLength = (WORD)strlen(pSource->u.e164);
        pNewItem->pData = (LPWSTR)Malloc((pNewItem->wDataLength + 1) * sizeof(pNewItem->pData[0]));
        if (pNewItem->pData == NULL)
        {
            Free(pNewItem);
            return CS_NO_MEMORY;
        }
        for (y = 0; y < pNewItem->wDataLength; y++)
        {
            pNewItem->pData[y] = pSource->u.e164[y];
        }
        pNewItem->pData[pNewItem->wDataLength] = 0;
        break;

    default:
        Free(pNewItem);
        *ppTarget = NULL;
        return CS_BAD_PARAM;
    } // switch

    *ppTarget = pNewItem;
    return CS_OK;
}

//====================================================================================
//====================================================================================
static CS_STATUS
Q931ClearAliasAddr(AliasAddress *pSource)
{
    if (pSource)
    {
        if (pSource->choice == h323_ID_chosen)
        {
            if (pSource->u.h323_ID.value)
            {
                Free(pSource->u.h323_ID.value);
            }
        }
    }
    return CS_OK;
}





//------------------------------------------------------------------------------
// Parse and return a single octet encoded value, See Q931 section 4.5.1.
//
// Parameters:
//     BufferPtr  Pointer to a descriptor of the buffer
//                containing the length and a pointer
//                to the raw bytes of the input stream.
//     Ident      Pointer to space for field identifier
//     Value      Pointer to space for field value
//------------------------------------------------------------------------------
static HRESULT 
ParseSingleOctetType1(
    PBUFFERDESCR BufferDescriptor,
    BYTE *Ident,
    BYTE *Value)
{
    // There has to be at least 1 byte in the stream to be
    // able to parse the single octet value
    if (BufferDescriptor->Length < 1)
    {
        return CS_ENDOFINPUT;
    }

    // low bits (0, 1, 2, 3) of the byte are the value
    *Value = (BYTE)(*BufferDescriptor->BufferPtr & TYPE1VALUEMASK);

    // higher bits (4, 5, 6) are the identifier.  bit 7 is always 1,
    // and is not returned as part of the id.
    *Ident = (BYTE)((*BufferDescriptor->BufferPtr & 0x70) >> 4);

    BufferDescriptor->BufferPtr++;
    BufferDescriptor->Length--;

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse and return a single octet encoded value, See Q931 section 4.5.1.
// This octet has no value, only an identifier.
//
// Parameters:
//     BufferPtr  Pointer to a descriptor of the buffer containing the
//                length and a pointer to the raw bytes of the input stream.
//     Ident      Pointer to space for field identifier
//------------------------------------------------------------------------------
static HRESULT
ParseSingleOctetType2(
    PBUFFERDESCR BufferDescriptor,
    BYTE *Ident)
{
    // There has to be at least 1 byte in the stream to be
    // able to parse the single octet value
    if (BufferDescriptor->Length < 1)
    {
        return CS_ENDOFINPUT;
    }

    // low 7 bits of the byte are the identifier
    *Ident = (BYTE)(*BufferDescriptor->BufferPtr & 0x7f);

    BufferDescriptor->BufferPtr++;
    BufferDescriptor->Length--;

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse and return a variable length Q931 field see Q931 section 4.5.1.
//
// Parameters :
//     BufferPtr  Pointer to a descriptor of the buffer
//                containing the length and a pointer
//                to the raw bytes of the input stream.
//     Ident      Pointer to space for field identifier
//     Length     Pointer to space for the length
//     Contents   Pointer to space for the bytes of the field
//------------------------------------------------------------------------------
static HRESULT 
ParseVariableOctet(
    PBUFFERDESCR BufferDescriptor,
    BYTE *Ident,
    BYTE *Length,
    BYTE *Contents)
{
    register int i;
    BYTE *Tempptr;

    // There has to be at least 2 bytes in order just to get 
    // the length and the identifier
    // able to parse the single octet value
    if (BufferDescriptor->Length < 2)
    {
        return CS_ENDOFINPUT;
    }

    // low 7 bits of the first byte are the identifier
    *Ident= (BYTE)(*BufferDescriptor->BufferPtr & 0x7f);

    BufferDescriptor->BufferPtr++;
    BufferDescriptor->Length--;

    // The next byte is the length
    *Length = *BufferDescriptor->BufferPtr;
    BufferDescriptor->BufferPtr++;
    BufferDescriptor->Length--;

    if (BufferDescriptor->Length < *Length)
    {
        return CS_ENDOFINPUT;
    }

    Tempptr = Contents;
    for (i = 0; i < *Length; i++)
    {
        // Copy the bytes out of the rest of the buffer
        *Tempptr = *BufferDescriptor->BufferPtr;
        BufferDescriptor->BufferPtr++;
        BufferDescriptor->Length--;
        Tempptr++;
    }
    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse and return a variable length Q931 field see Q931 section 4.5.1.
//------------------------------------------------------------------------------
static HRESULT 
ParseVariableASN(
    PBUFFERDESCR BufferDescriptor,
    BYTE *Ident,
    BYTE *ProtocolDiscriminator,
    WORD *UserInformationLength,     // Length of the User Information.
    BYTE *UserInformation)           // Bytes of the User Information.
{
    register int i;
    BYTE *Tempptr;
    WORD ContentsLength;     // Length of the full UserUser contents.

    *UserInformationLength = 0;

    // There has to be at least 4 bytes for the IE identifier,
    // the contents length, and the protocol discriminator (1 + 2 + 1).
    if (BufferDescriptor->Length < 4)
    {
        return CS_ENDOFINPUT;
    }

    // low 7 bits of the first byte are the identifier
    *Ident= (BYTE)(*BufferDescriptor->BufferPtr & 0x7f);
    BufferDescriptor->BufferPtr++;
    BufferDescriptor->Length--;

    // The next 2 bytes are the length
    ContentsLength = *(BufferDescriptor->BufferPtr);
    BufferDescriptor->BufferPtr++;
    BufferDescriptor->Length--;
    ContentsLength = (WORD)((ContentsLength << 8) + *BufferDescriptor->BufferPtr);
    BufferDescriptor->BufferPtr++;
    BufferDescriptor->Length--;

    if (BufferDescriptor->Length < ContentsLength)
    {
        return CS_ENDOFINPUT;
    }

    // The next byte is the protocol discriminator.
    *ProtocolDiscriminator = *BufferDescriptor->BufferPtr;
    BufferDescriptor->BufferPtr++;
    BufferDescriptor->Length--;

    if (ContentsLength > 0)
    {
        *UserInformationLength = (WORD)(ContentsLength - 1);
    }

    Tempptr = UserInformation;
    for (i = 0; i < *UserInformationLength; i++)
    {
        // Copy the bytes out of the rest of the buffer
        *Tempptr = *BufferDescriptor->BufferPtr;
        BufferDescriptor->BufferPtr++;
        BufferDescriptor->Length--;
        Tempptr++;
    }
    return CS_OK;
}

//------------------------------------------------------------------------------
// Get the identifier of the next field from the buffer and
// return it.  The buffer pointer is not incremented, To
// parse the field and extract its values, the above functions
// should be used.  See Q931 table 4-3 for the encodings of the 
// identifiers.
//
// Parameters:
//      BufferPtr        Pointer to the buffer space
//------------------------------------------------------------------------------
static BYTE
GetNextIdent(
    void *BufferPtr)
{
    FIELDIDENTTYPE Ident;

    // Extract the first byte from the buffer
    Ident= (*(FIELDIDENTTYPE *)BufferPtr);

    // This value can be returned as the identifier as long
    // as it is not a single Octet - Type 1 element.
    // Those items must have the value removed from them
    // before they can be returned.
    if ((Ident & 0x80) && ((Ident & TYPE1IDENTMASK) != 0xA0))
    {
        return (BYTE)(Ident & TYPE1IDENTMASK);
    }

    return Ident;
}

//------------------------------------------------------------------------------
// Parse and return a protocol discriminator. See Q931 section 4.2.
// The octet pointed to by **BufferPtr is the protocol Discriminator.
//
// Parameters:
//     BufferPtr  Pointer to a descriptor of the buffer
//                containing the length and a pointer
//                to the raw bytes of the input stream.
//     Discrim    Pointer to space for discriminator
//------------------------------------------------------------------------------
static HRESULT
ParseProtocolDiscriminator(
    PBUFFERDESCR BufferDescriptor,
    PDTYPE *Discrim)
{
    // There has to be at least enough bytes left in the 
    // string for the operation
    if (BufferDescriptor->Length < sizeof(PDTYPE))
    {
        return CS_MESSAGE_TOO_SHORT;
    }

    *Discrim = *(PDTYPE *)BufferDescriptor->BufferPtr;
    if (*Discrim != Q931PDVALUE)
    {
        return CS_INVALID_PROTOCOL;
    }

    BufferDescriptor->BufferPtr += sizeof(PDTYPE);
    BufferDescriptor->Length -= sizeof(PDTYPE);
    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse and return a variable length Q931 call reference see 
// Q931 section 4.3.
//
// Parameters:
//     BufferPtr  Pointer to a descriptor of the buffer
//                containing the length and a pointer
//                to the raw bytes of the input stream.
//     Length     Pointer to space for the length
//     Contents   Pointer to space for the bytes of the field
//------------------------------------------------------------------------------
static HRESULT
ParseCallReference(
    PBUFFERDESCR BufferDescriptor,
    CRTYPE *CallReference)
{
    register int i;
    BYTE Length;

    // There has to be at least enough bytes left in the 
    // string for the length byte
    if (BufferDescriptor->Length < 1)
    {
        return CS_MESSAGE_TOO_SHORT;
    }

    // low 4 bits of the first byte are the length.
    // the rest of the bits are zeroes.
    Length = (BYTE)(*BufferDescriptor->BufferPtr & 0x0f);

    BufferDescriptor->BufferPtr++;
    BufferDescriptor->Length--;

    // There has to be at least enough bytes left in the 
    // string for the operation
    if (BufferDescriptor->Length < Length)
    {
        return CS_MESSAGE_TOO_SHORT;
    }

    *CallReference = 0;     // length can be 0, so initialize here first...
    for (i = 0; i < Length; i++)
    {
        if (i < sizeof(CRTYPE))
        {
            // Copy the bytes out of the rest of the buffer
            *CallReference = (WORD)((*CallReference << 8) +
                *BufferDescriptor->BufferPtr);
        }
        BufferDescriptor->BufferPtr++;
        BufferDescriptor->Length--;
    }

    // note:  the high order bit of the value represents callee relationship.

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse and return a message type.  See Q931 section 4.4.
// The octet pointed to by **BufferPtr is the message type.
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     MessageType  Pointer to space for message type
//------------------------------------------------------------------------------
static HRESULT
ParseMessageType(
    PBUFFERDESCR BufferDescriptor,
    MESSAGEIDTYPE *MessageType)
{
    register int i;

    // There has to be at least enough bytes left in the 
    // string for the operation
    if (BufferDescriptor->Length < sizeof(MESSAGEIDTYPE))
    {
        return CS_MESSAGE_TOO_SHORT;
    }

    *MessageType = (BYTE)(*((MESSAGEIDTYPE *)BufferDescriptor->BufferPtr) & MESSAGETYPEMASK);
    for (i = 0; i < sizeof(MessageSet) / sizeof(MESSAGEIDTYPE); i++)
    {
        if (MessageSet[i] == *MessageType)
        {
            break;
        }
    }
    if (i >= sizeof(MessageSet) / sizeof(MESSAGEIDTYPE))
    {
        return CS_INVALID_MESSAGE_TYPE;
    }

    BufferDescriptor->BufferPtr += sizeof(MESSAGEIDTYPE);
    BufferDescriptor->Length -= sizeof(MESSAGEIDTYPE);
    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional shift field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer containing the
//                  length and a pointer to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed shift message information.
//------------------------------------------------------------------------------
static HRESULT
ParseShift(
    PBUFFERDESCR BufferDescriptor,
    PSHIFTIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(SHIFTIE));
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_SHIFT)
    {
        FieldStruct->Present = TRUE;
        return ParseSingleOctetType1(BufferDescriptor,
            &Ident, &FieldStruct->Value);
    }
    else
    {
        FieldStruct->Present = FALSE;
    }
    return CS_OK;
}


//------------------------------------------------------------------------------
// Parse an optional facility ie field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed facility
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseFacility(
    PBUFFERDESCR BufferDescriptor,
    PFACILITYIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(FACILITYIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_FACILITY)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor,
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional more data field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed field information
//------------------------------------------------------------------------------
static HRESULT 
ParseMoreData(
    PBUFFERDESCR BufferDescriptor,
    PMOREDATAIE FieldStruct)
{
    BYTE Ident;

    memset(FieldStruct, 0, sizeof(MOREDATAIE));
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_MORE)
    {
        FieldStruct->Present = TRUE;
        return ParseSingleOctetType2(BufferDescriptor, &Ident);
    }
    else
    {
        FieldStruct->Present = FALSE;
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional sending clomplete field.  Q931 section 4.4.
// The octet pointed to by **BufferPtr is the message type.
//
// Parameters:
//      BufferPtr    Pointer to a descriptor of the buffer
//                   containing the length and a pointer
//                   to the raw bytes of the input stream.
//      MessageType  Pointer to space for message type
//------------------------------------------------------------------------------
static HRESULT
ParseSendingComplete(
    PBUFFERDESCR BufferDescriptor,
    PSENDCOMPLIE FieldStruct)
{
    BYTE Ident;

    memset(FieldStruct, 0, sizeof(SENDCOMPLIE));
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_SENDINGCOMPLETE)
    {
        FieldStruct->Present = TRUE;
        return ParseSingleOctetType2(BufferDescriptor, &Ident);
    }
    else
    {
        FieldStruct->Present = FALSE;
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional congestion level field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed congestion 
//                  level information.
//------------------------------------------------------------------------------
static HRESULT 
ParseCongestionLevel(
    PBUFFERDESCR BufferDescriptor,
    PCONGESTIONIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(CONGESTIONIE));
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_CONGESTION)
    {
        FieldStruct->Present = TRUE;
        return ParseSingleOctetType1(BufferDescriptor,
            &Ident, &FieldStruct->Value);
    }
    else
    {
        FieldStruct->Present = FALSE;
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional repeat indicator field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed repeat
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseRepeatIndicator(
    PBUFFERDESCR BufferDescriptor,
    PREPEATIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(REPEATIE));
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_REPEAT)
    {
        FieldStruct->Present = TRUE;
        return ParseSingleOctetType1(BufferDescriptor,
            &Ident, &FieldStruct->Value);
    }
    else
    {
        FieldStruct->Present = FALSE;
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional segmented message field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed segmented message
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseSegmented(
    PBUFFERDESCR BufferDescriptor,
    PSEGMENTEDIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(SEGMENTEDIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_SEGMENTED)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor,
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional bearer capability field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed bearer capability
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseBearerCapability(
    PBUFFERDESCR BufferDescriptor,
    PBEARERCAPIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(BEARERCAPIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_BEARERCAP)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor,
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional cause field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed cause
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseCause(
    PBUFFERDESCR BufferDescriptor,
    PCAUSEIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(CAUSEIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_CAUSE)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor,
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional call identity field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed call identity
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseCallIdentity(
    PBUFFERDESCR BufferDescriptor,
    PCALLIDENTIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(CALLIDENTIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_CALLIDENT)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor,
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional call state field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed call state
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseCallState(
    PBUFFERDESCR BufferDescriptor,
    PCALLSTATEIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(CALLSTATEIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_CALLSTATE)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor,
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional channel identification field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed channel identity
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseChannelIdentification(
    PBUFFERDESCR BufferDescriptor,
    PCHANIDENTIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(CHANIDENTIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_CHANNELIDENT)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional progress indication field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed progress
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseProgress(
    PBUFFERDESCR BufferDescriptor,
    PPROGRESSIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(PROGRESSIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_PROGRESS)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional network specific facilities field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed network facitlities
//                  information.
//------------------------------------------------------------------------------
static HRESULT 
ParseNetworkSpec(
    PBUFFERDESCR BufferDescriptor,
    PNETWORKIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(NETWORKIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_NETWORKSPEC)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional notification indicator field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parse notification indicator
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseNotificationIndicator(
    PBUFFERDESCR BufferDescriptor,
    PNOTIFICATIONINDIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(NOTIFICATIONINDIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_NOTIFICATION)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional display field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed display
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseDisplay(
    PBUFFERDESCR BufferDescriptor,
    PDISPLAYIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(DISPLAYIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_DISPLAY)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional date/time field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed date/time
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseDate(
    PBUFFERDESCR BufferDescriptor,
    PDATEIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(DATEIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_DATE)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional keypad field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed keypad
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseKeypad(
    PBUFFERDESCR BufferDescriptor,
    PKEYPADIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(KEYPADIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_KEYPAD)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional signal field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed signal
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseSignal(
    PBUFFERDESCR BufferDescriptor,
    PSIGNALIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(SIGNALIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_SIGNAL)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional information rate field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed information rate
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseInformationRate(
    PBUFFERDESCR BufferDescriptor,
    PINFORATEIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(INFORATEIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_INFORMATIONRATE)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional end to end transit delay field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed end to end
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseEndToEndDelay(
    PBUFFERDESCR BufferDescriptor,
    PENDTOENDDELAYIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(ENDTOENDDELAYIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_ENDTOENDDELAY)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional transit delay field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed transit delay
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseTransitDelay(
    PBUFFERDESCR BufferDescriptor,
    PTRANSITDELAYIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(TRANSITDELAYIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_TRANSITDELAY)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional packet layer binary params field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed 
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParsePacketLayerParams(
    PBUFFERDESCR BufferDescriptor,
    PPLBINARYPARAMSIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(PLBINARYPARAMSIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_PLBINARYPARAMS)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional packet layer window size field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed 
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParsePacketLayerWindowSize(
    PBUFFERDESCR BufferDescriptor,
    PPLWINDOWSIZEIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(PLWINDOWSIZEIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_PLWINDOWSIZE)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional packet size field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parse packet size
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParsePacketSize(
    PBUFFERDESCR BufferDescriptor,
    PPACKETSIZEIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(PACKETSIZEIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_PACKETSIZE)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional closed user group field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed 
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseClosedUserGroup(
    PBUFFERDESCR BufferDescriptor,
    PCLOSEDUGIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(CLOSEDUGIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_CLOSEDUG)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional reverse charge field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed 
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseReverseCharge(
    PBUFFERDESCR BufferDescriptor,
    PREVERSECHARGEIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(REVERSECHARGEIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_REVCHARGE)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional calling party number field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed 
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseCallingPartyNumber(
    PBUFFERDESCR BufferDescriptor,
    PCALLINGNUMBERIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(CALLINGNUMBERIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_CALLINGNUMBER)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional calling party subaddress field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed 
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseCallingPartySubaddress(
    PBUFFERDESCR BufferDescriptor,
    PCALLINGSUBADDRIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(CALLINGSUBADDRIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_CALLINGSUBADDR)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional called party number field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed 
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseCalledPartyNumber(
    PBUFFERDESCR BufferDescriptor, 
    PCALLEDNUMBERIE FieldStruct)
{
    memset(FieldStruct, 0, sizeof(PCALLEDNUMBERIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_CALLEDNUMBER)
    {
        register int i;
        BYTE RemainingLength = 0;
        BYTE *Tempptr;
    
        // Need 3 bytes for the ident (1), length (1),
        // and type + plan (1) fields.
        if (BufferDescriptor->Length < 3)
        {
            return CS_ENDOFINPUT;
        }

        // skip the ie identifier...    
        BufferDescriptor->BufferPtr++;
        BufferDescriptor->Length--;

        // Get the length of the contents following the length field.
        RemainingLength = *BufferDescriptor->BufferPtr;
        BufferDescriptor->BufferPtr++;
        BufferDescriptor->Length--;

        // make sure we have at least that much length left...    
        if (BufferDescriptor->Length < RemainingLength)
        {
            return CS_ENDOFINPUT;
        }

        // Get the type + plan fields.
        if (*(BufferDescriptor->BufferPtr) & 0x80)
        {
            FieldStruct->NumberType =
                (BYTE)(*BufferDescriptor->BufferPtr & 0xf0);
            FieldStruct->NumberingPlan =
                (BYTE)(*BufferDescriptor->BufferPtr & 0x0f);
            BufferDescriptor->BufferPtr++;
            BufferDescriptor->Length--;
            RemainingLength--;
        }

        FieldStruct->PartyNumberLength = RemainingLength;
        FieldStruct->Present = TRUE;

        Tempptr = FieldStruct->PartyNumbers;
        for (i = 0; i < RemainingLength; i++)
        {
            // Copy the bytes out of the rest of the buffer
            *Tempptr = *(BufferDescriptor->BufferPtr);
            BufferDescriptor->BufferPtr++;
            BufferDescriptor->Length--;
            Tempptr++;
        }
        *Tempptr = (BYTE)0;
    }
    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional called party subaddress field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed 
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseCalledPartySubaddress(
    PBUFFERDESCR BufferDescriptor,
    PCALLEDSUBADDRIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(CALLEDSUBADDRIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_CALLEDSUBADDR)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional redirecting number field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed 
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseRedirectingNumber(
    PBUFFERDESCR BufferDescriptor, 
    PREDIRECTINGIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(REDIRECTINGIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_REDIRECTING)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional transit network selection field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed 
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseTransitNetwork(
    PBUFFERDESCR BufferDescriptor, 
    PTRANSITNETIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(TRANSITNETIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_TRANSITNET)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional restart indicator field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed 
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseRestart(
    PBUFFERDESCR BufferDescriptor,
    PRESTARTIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(PRESTARTIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_RESTART)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional lower layer compatibility field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed 
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseLowLayerCompatibility(
    PBUFFERDESCR BufferDescriptor,
    PLLCOMPATIBILITYIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(LLCOMPATIBILITYIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_LLCOMPATIBILITY)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional higher layer compatibility field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed 
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseHighLayerCompatibility(
    PBUFFERDESCR BufferDescriptor,
    PHLCOMPATIBILITYIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(HLCOMPATIBILITYIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_HLCOMPATIBILITY)
    {
        HRESULT ParseResult;
        ParseResult = ParseVariableOctet(BufferDescriptor, 
            &Ident, &FieldStruct->Length, &FieldStruct->Contents[0]);
        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->Length > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse an optional user to user field
//
// Parameters:
//     BufferPtr    Pointer to a descriptor of the buffer
//                  containing the length and a pointer
//                  to the raw bytes of the input stream.
//     FieldStruct  Pointer to space for parsed 
//                  information.
//------------------------------------------------------------------------------
static HRESULT
ParseUserToUser(
    PBUFFERDESCR BufferDescriptor,
    PUSERUSERIE FieldStruct)
{
    BYTE Ident;
    
    memset(FieldStruct, 0, sizeof(USERUSERIE));
    FieldStruct->Present = FALSE;
    if (GetNextIdent(BufferDescriptor->BufferPtr) == IDENT_USERUSER)
    {
        HRESULT ParseResult;

        ParseResult = ParseVariableASN(BufferDescriptor, 
            &Ident, &(FieldStruct->ProtocolDiscriminator),
            &(FieldStruct->UserInformationLength),
            &(FieldStruct->UserInformation[0]));

        if (ParseResult != CS_OK)
        {
            return ParseResult;
        }
        if (FieldStruct->UserInformationLength > 0)
        {
            FieldStruct->Present = TRUE;
        }
    }

    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse the next Q931 field in the given message
//
// Parameters:
//      BufferDescriptor  Pointer to buffer descriptor of a 
//                        the network packet of the 931 message
//      Message           Pointer to space for parsed information.
//------------------------------------------------------------------------------
static HRESULT
ParseQ931Field(
    PBUFFERDESCR BufferDescriptor,
    PQ931MESSAGE Message)
{
    FIELDIDENTTYPE Ident;

    Ident = GetNextIdent(BufferDescriptor->BufferPtr);
    switch (Ident)
    {
    case IDENT_SHIFT:
        return ParseShift(BufferDescriptor,
            &Message->Shift);
    case IDENT_FACILITY:
        return ParseFacility(BufferDescriptor,
            &Message->Facility);
    case IDENT_MORE:
        return ParseMoreData(BufferDescriptor,
            &Message->MoreData);
    case IDENT_SENDINGCOMPLETE:
        return ParseSendingComplete(BufferDescriptor,
            &Message->SendingComplete);
    case IDENT_CONGESTION:
        return ParseCongestionLevel(BufferDescriptor,
            &Message->CongestionLevel);
    case IDENT_REPEAT:
        return ParseRepeatIndicator(BufferDescriptor,
            &Message->RepeatIndicator);
    case IDENT_SEGMENTED:
        return ParseSegmented(BufferDescriptor,
            &Message->SegmentedMessage);
    case IDENT_BEARERCAP:
        return ParseBearerCapability(BufferDescriptor,
            &Message->BearerCapability);
    case IDENT_CAUSE:
        return ParseCause(BufferDescriptor,
            &Message->Cause);
    case IDENT_CALLIDENT:
        return ParseCallIdentity(BufferDescriptor,
            &Message->CallIdentity);
    case IDENT_CALLSTATE:
        return ParseCallState(BufferDescriptor,
            &Message->CallState);
    case IDENT_CHANNELIDENT:
        return ParseChannelIdentification(BufferDescriptor,
            &Message->ChannelIdentification);
    case IDENT_PROGRESS:
        return ParseProgress(BufferDescriptor,
            &Message->ProgressIndicator);
    case IDENT_NETWORKSPEC:
        return ParseNetworkSpec(BufferDescriptor,
            &Message->NetworkFacilities);
    case IDENT_NOTIFICATION:
        return ParseNotificationIndicator(BufferDescriptor,
            &Message->NotificationIndicator);
    case IDENT_DISPLAY:
        return ParseDisplay(BufferDescriptor,
            &Message->Display);
    case IDENT_DATE:
        return ParseDate(BufferDescriptor,
            &Message->Date);
    case IDENT_KEYPAD:
        return ParseKeypad(BufferDescriptor,
            &Message->Keypad);
    case IDENT_SIGNAL:
        return ParseSignal(BufferDescriptor,
            &Message->Signal);
    case IDENT_INFORMATIONRATE:
        return ParseInformationRate(BufferDescriptor,
            &Message->InformationRate);
    case IDENT_ENDTOENDDELAY:
        return ParseEndToEndDelay(BufferDescriptor,
            &Message->EndToEndTransitDelay);
    case IDENT_TRANSITDELAY:
        return ParseTransitDelay(BufferDescriptor,
            &Message->TransitDelay);
    case IDENT_PLBINARYPARAMS:
        return ParsePacketLayerParams(BufferDescriptor,
            &Message->PacketLayerBinaryParams);
    case IDENT_PLWINDOWSIZE:
        return ParsePacketLayerWindowSize(BufferDescriptor,
            &Message->PacketLayerWindowSize);
    case IDENT_PACKETSIZE:
        return ParsePacketSize(BufferDescriptor,
            &Message->PacketSize);
    case IDENT_CLOSEDUG:
        return ParseClosedUserGroup(BufferDescriptor,
            &Message->ClosedUserGroup);
    case IDENT_REVCHARGE:
        return ParseReverseCharge(BufferDescriptor,
            &Message->ReverseChargeIndication);
    case IDENT_CALLINGNUMBER:
        return ParseCallingPartyNumber(BufferDescriptor,
            &Message->CallingPartyNumber);
    case IDENT_CALLINGSUBADDR:
        return ParseCallingPartySubaddress(BufferDescriptor,
            &Message->CallingPartySubaddress);
    case IDENT_CALLEDNUMBER:
        return ParseCalledPartyNumber(BufferDescriptor,
            &Message->CalledPartyNumber);
    case IDENT_CALLEDSUBADDR:
        return ParseCalledPartySubaddress(BufferDescriptor,
            &Message->CalledPartySubaddress);
    case IDENT_REDIRECTING:
        return ParseRedirectingNumber(BufferDescriptor,
            &Message->RedirectingNumber);
    case IDENT_TRANSITNET:
        return ParseTransitNetwork(BufferDescriptor,
            &Message->TransitNetworkSelection);
    case IDENT_RESTART:
        return ParseRestart(BufferDescriptor,
            &Message->RestartIndicator);
    case IDENT_LLCOMPATIBILITY:
        return ParseLowLayerCompatibility(BufferDescriptor,
            &Message->LowLayerCompatibility);
    case IDENT_HLCOMPATIBILITY:
        return ParseHighLayerCompatibility(BufferDescriptor,
            &Message->HighLayerCompatibility);
    case IDENT_USERUSER:
        return ParseUserToUser(BufferDescriptor,
            &Message->UserToUser);
    default:
        return CS_INVALID_FIELD;
    }
}

//------------------------------------------------------------------------------
// Parse a generic Q931 message and place the fields of the buffer
// into the appropriate structure fields.
//
// Parameters:
//     BufferDescriptor  Pointer to buffer descriptor of an
//                       input packet containing the 931 message.
//     Message           Pointer to space for parsed output information.
//------------------------------------------------------------------------------
HRESULT
Q931ParseMessage(
    BYTE *CodedBufferPtr,
    DWORD CodedBufferLength,
    PQ931MESSAGE Message)
{
    HRESULT Result;
    BUFFERDESCR BufferDescriptor;

    BufferDescriptor.Length = CodedBufferLength;
    BufferDescriptor.BufferPtr = CodedBufferPtr;

    memset(Message, 0, sizeof(Q931MESSAGE));

    if ((Result = ParseProtocolDiscriminator(&BufferDescriptor,
        &Message->ProtocolDiscriminator)) != CS_OK)
    {
        return Result;
    }

    if ((Result = ParseCallReference(&BufferDescriptor,
        &Message->CallReference)) != CS_OK)
    {
        return Result;
    }

    if ((Result = ParseMessageType(&BufferDescriptor,
        &Message->MessageType)) != CS_OK)
    {
        return Result;
    }

    while (BufferDescriptor.Length)
    {
        Result = ParseQ931Field(&BufferDescriptor, Message);
        if (Result != CS_OK)
        {
            return Result;
        }
    }
    return CS_OK;
}


//==============================================================================
//==============================================================================
//==============================================================================
// BELOW HERE ARE THE OUTPUT ROUTINES...
//==============================================================================
//==============================================================================
//==============================================================================


//------------------------------------------------------------------------------
// Write the protocol discriminator. See Q931 section 4.2.
//------------------------------------------------------------------------------
static HRESULT
WriteProtocolDiscriminator(
    PBUFFERDESCR BufferDescriptor)
{
    BufferDescriptor->Length += sizeof(PDTYPE);
    if (BufferDescriptor->BufferPtr)
    {
        *(PDTYPE *)BufferDescriptor->BufferPtr = Q931PDVALUE;
        BufferDescriptor->BufferPtr += sizeof(PDTYPE);
    }
    return CS_OK;
}

//------------------------------------------------------------------------------
// Write a variable length Q931 call reference.  See Q931 section 4.3.
//------------------------------------------------------------------------------
static HRESULT
WriteCallReference(
    PBUFFERDESCR BufferDescriptor,
    CRTYPE *CallReference)
{
    register int i;

    // space for the length byte
    BufferDescriptor->Length++;

    // the length byte
    if (BufferDescriptor->BufferPtr != NULL)
    {
        *BufferDescriptor->BufferPtr = (BYTE)sizeof(CRTYPE);
        BufferDescriptor->BufferPtr++;
    }

    for (i = 0; i < sizeof(CRTYPE); i++)
    {
        // Copy the value bytes to the buffer
        BufferDescriptor->Length++;
        if (BufferDescriptor->BufferPtr != NULL)
        {
            *BufferDescriptor->BufferPtr =
                (BYTE)(((*CallReference) >> ((sizeof(CRTYPE) - 1 -i) * 8)) & 0xff);
            BufferDescriptor->BufferPtr++;
        }
    }
    return CS_OK;
}

//------------------------------------------------------------------------------
// Write a Q931 message type.  See Q931 section 4.4.
//------------------------------------------------------------------------------
static HRESULT
WriteMessageType(
    PBUFFERDESCR BufferDescriptor,
    MESSAGEIDTYPE *MessageType)
{
    register int i;

    for (i = 0; i < sizeof(MessageSet) / sizeof(MESSAGEIDTYPE); i++)
    {
        if (MessageSet[i] == *MessageType)
        {
            break;
        }
    }
    if (i >= sizeof(MessageSet) / sizeof(MESSAGEIDTYPE))
    {
        return CS_INVALID_MESSAGE_TYPE;
    }

    BufferDescriptor->Length += sizeof(MESSAGEIDTYPE);
    if (BufferDescriptor->BufferPtr != NULL)
    {
        *(MESSAGEIDTYPE *)(BufferDescriptor->BufferPtr) =
            (BYTE)(*MessageType & MESSAGETYPEMASK);
        BufferDescriptor->BufferPtr += sizeof(MESSAGEIDTYPE);
    }
    return CS_OK;
}

//------------------------------------------------------------------------------
// Write a single octet encoded value, See Q931 section 4.5.1.
//------------------------------------------------------------------------------
static HRESULT 
WriteSingleOctetType1(
    PBUFFERDESCR BufferDescriptor,
    BYTE Ident,
    BYTE Value)
{
    BufferDescriptor->Length++;
    if (BufferDescriptor->BufferPtr)
    {
        *BufferDescriptor->BufferPtr =
            (BYTE)(0x80 | Ident | (Value & TYPE1VALUEMASK));
        BufferDescriptor->BufferPtr++;
    }
    return CS_OK;
}

//------------------------------------------------------------------------------
// Write a single octet encoded value, See Q931 section 4.5.1.
//------------------------------------------------------------------------------
static HRESULT 
WriteSingleOctetType2(
    PBUFFERDESCR BufferDescriptor,
    BYTE Ident)
{
    BufferDescriptor->Length++;
    if (BufferDescriptor->BufferPtr)
    {
        *BufferDescriptor->BufferPtr = (BYTE)(0x80 | Ident);
        BufferDescriptor->BufferPtr++;
    }
    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse and return a variable length Q931 field see Q931 section 4.5.1.
//------------------------------------------------------------------------------
static HRESULT 
WriteVariableOctet(
    PBUFFERDESCR BufferDescriptor,
    BYTE Ident,
    BYTE Length,
    BYTE *Contents)
{
    register int i;
    BYTE *Tempptr;

    if (Contents == NULL)
    {
        Length = 0;
    }

    // space for the length and the identifier bytes
    BufferDescriptor->Length += 2;

    // the id byte, then the length byte
    if (BufferDescriptor->BufferPtr != NULL)
    {
        // low 7 bits of the first byte are the identifier
        *BufferDescriptor->BufferPtr = (BYTE)(Ident & 0x7f);
        BufferDescriptor->BufferPtr++;
        *BufferDescriptor->BufferPtr = Length;
        BufferDescriptor->BufferPtr++;
    }

    Tempptr = Contents;
    for (i = 0; i < Length; i++)
    {
        // Copy the value bytes to the buffer
        BufferDescriptor->Length++;
        if (BufferDescriptor->BufferPtr != NULL)
        {
            *BufferDescriptor->BufferPtr = *Tempptr;
            BufferDescriptor->BufferPtr++;
            Tempptr++;
        }
    }
    return CS_OK;
}

//------------------------------------------------------------------------------
//Write out the Party number.
//------------------------------------------------------------------------------
static HRESULT 
WritePartyNumber(
    PBUFFERDESCR BufferDescriptor,
    BYTE Ident,
    BYTE NumberType,
    BYTE NumberingPlan,
    BYTE PartyNumberLength,
    BYTE *PartyNumbers)
{
    register int i;
    BYTE *Tempptr;

    if (PartyNumbers == NULL)
    {
        PartyNumberLength = 0;
    }

    // space for the ident (1), length (1), and type + plan (1) fields.
    BufferDescriptor->Length += 3;

    // write the fields out.
    if (BufferDescriptor->BufferPtr != NULL)
    {
        // low 7 bits of byte 1 are the ie identifier
        *BufferDescriptor->BufferPtr = (BYTE)(Ident & 0x7f);
        BufferDescriptor->BufferPtr++;

        // byte 2 is the ie contents length following the length field.
        *BufferDescriptor->BufferPtr = (BYTE)(PartyNumberLength + 1);
        BufferDescriptor->BufferPtr++;

        // byte 3 is the type and plan field.
        *BufferDescriptor->BufferPtr = (BYTE)(NumberType | NumberingPlan);
        BufferDescriptor->BufferPtr++;
    }

    Tempptr = PartyNumbers;
    for (i = 0; i < PartyNumberLength; i++)
    {
        // Copy the value bytes to the buffer
        BufferDescriptor->Length++;
        if (BufferDescriptor->BufferPtr != NULL)
        {
            *BufferDescriptor->BufferPtr = *Tempptr;
            BufferDescriptor->BufferPtr++;
            Tempptr++;
        }
    }
    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse and return a variable length Q931 field see Q931 section 4.5.1.
//------------------------------------------------------------------------------
static HRESULT 
WriteVariableASN(
    PBUFFERDESCR BufferDescriptor,
    BYTE Ident,
    WORD UserInformationLength,
    BYTE *UserInformation)
{
    register int i;
    BYTE *Tempptr;
    WORD ContentsLength = (WORD)(UserInformationLength + 1);

    // There has to be at least 4 bytes for the IE identifier,
    // the contents length, and the protocol discriminator (1 + 2 + 1).
    BufferDescriptor->Length += 4;

    if (BufferDescriptor->BufferPtr != NULL)
    {
        // low 7 bits of the first byte are the identifier
        *BufferDescriptor->BufferPtr = (BYTE)(Ident & 0x7f);
        BufferDescriptor->BufferPtr++;

        // write the contents length bytes.
        *BufferDescriptor->BufferPtr = (BYTE)(ContentsLength >> 8);
        BufferDescriptor->BufferPtr++;
        *BufferDescriptor->BufferPtr = (BYTE)ContentsLength;
        BufferDescriptor->BufferPtr++;

        // write the protocol discriminator byte.
        *(BufferDescriptor->BufferPtr) = Q931_PROTOCOL_X209;
        BufferDescriptor->BufferPtr++;
    }

    Tempptr = UserInformation;
    for (i = 0; i < UserInformationLength; i++)
    {
        // Copy the value bytes to the buffer
        BufferDescriptor->Length++;
        if (BufferDescriptor->BufferPtr != NULL)
        {
            *BufferDescriptor->BufferPtr = *Tempptr;
            BufferDescriptor->BufferPtr++;
            Tempptr++;
        }
    }
    return CS_OK;
}

//------------------------------------------------------------------------------
// Write the Q931 fields to the encoding buffer.
//
// Parameters:
//      BufferDescriptor  Pointer to buffer descriptor for
//                        the encoded output buffer.
//      Message           Pointer to space for parsed input information.
//------------------------------------------------------------------------------
static HRESULT
WriteQ931Fields(
    PBUFFERDESCR BufferDescriptor,
    PQ931MESSAGE Message)
{
    // write the required information elements...
    WriteProtocolDiscriminator(BufferDescriptor);
    WriteCallReference(BufferDescriptor,
        &Message->CallReference);
    WriteMessageType(BufferDescriptor,
        &Message->MessageType);

    // try to write all other information elements...
// don't write this message.
#if 0
    if (Message->Shift.Present)
    {
        WriteSingleOctetType1(BufferDescriptor, IDENT_SHIFT,
            Message->Shift.Value);
    }
#endif

    if (Message->Facility.Present)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_FACILITY,
            Message->Facility.Length,
            Message->Facility.Contents);
    }

    if (Message->MoreData.Present)
    {
        WriteSingleOctetType2(BufferDescriptor, IDENT_MORE);
    }
    if (Message->SendingComplete.Present)
    {
        WriteSingleOctetType2(BufferDescriptor, IDENT_SENDINGCOMPLETE);
    }
    if (Message->CongestionLevel.Present)
    {
        WriteSingleOctetType1(BufferDescriptor, IDENT_CONGESTION,
            Message->CongestionLevel.Value);
    }
    if (Message->RepeatIndicator.Present)
    {
        WriteSingleOctetType1(BufferDescriptor, IDENT_REPEAT,
            Message->RepeatIndicator.Value);
    }

    if (Message->SegmentedMessage.Present &&
            Message->SegmentedMessage.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_SEGMENTED,
            Message->SegmentedMessage.Length,
            Message->SegmentedMessage.Contents);
    }
    if (Message->BearerCapability.Present &&
            Message->BearerCapability.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_BEARERCAP,
            Message->BearerCapability.Length,
            Message->BearerCapability.Contents);
    }
    if (Message->Cause.Present &&
            Message->Cause.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_CAUSE,
            Message->Cause.Length,
            Message->Cause.Contents);
    }
    if (Message->CallIdentity.Present &&
            Message->CallIdentity.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_CALLIDENT,
            Message->CallIdentity.Length,
            Message->CallIdentity.Contents);
    }
    if (Message->CallState.Present &&
            Message->CallState.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_CALLSTATE,
            Message->CallState.Length,
            Message->CallState.Contents);
    }
    if (Message->ChannelIdentification.Present &&
            Message->ChannelIdentification.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_CHANNELIDENT,
            Message->ChannelIdentification.Length,
            Message->ChannelIdentification.Contents);
    }
    if (Message->ProgressIndicator.Present &&
            Message->ProgressIndicator.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_PROGRESS,
            Message->ProgressIndicator.Length,
            Message->ProgressIndicator.Contents);
    }
    if (Message->NetworkFacilities.Present &&
            Message->NetworkFacilities.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_NETWORKSPEC,
            Message->NetworkFacilities.Length,
            Message->NetworkFacilities.Contents);
    }
    if (Message->NotificationIndicator.Present &&
            Message->NotificationIndicator.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_NOTIFICATION,
            Message->NotificationIndicator.Length,
            Message->NotificationIndicator.Contents);
    }
    if (Message->Display.Present &&
            Message->Display.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_DISPLAY,
            Message->Display.Length,
            Message->Display.Contents);
    }
    if (Message->Date.Present &&
            Message->Date.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_DATE,
            Message->Date.Length,
            Message->Date.Contents);
    }
    if (Message->Keypad.Present &&
            Message->Keypad.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_KEYPAD,
            Message->Keypad.Length,
            Message->Keypad.Contents);
    }
    if (Message->Signal.Present &&
            Message->Signal.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_SIGNAL,
            Message->Signal.Length,
            Message->Signal.Contents);
    }
    if (Message->InformationRate.Present &&
            Message->InformationRate.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_INFORMATIONRATE,
            Message->InformationRate.Length,
            Message->InformationRate.Contents);
    }
    if (Message->EndToEndTransitDelay.Present &&
            Message->EndToEndTransitDelay.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_ENDTOENDDELAY,
            Message->EndToEndTransitDelay.Length,
            Message->EndToEndTransitDelay.Contents);
    }
    if (Message->TransitDelay.Present &&
            Message->TransitDelay.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_TRANSITDELAY,
            Message->TransitDelay.Length,
            Message->TransitDelay.Contents);
    }
    if (Message->PacketLayerBinaryParams.Present &&
            Message->PacketLayerBinaryParams.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_PLBINARYPARAMS,
            Message->PacketLayerBinaryParams.Length,
            Message->PacketLayerBinaryParams.Contents);
    }
    if (Message->PacketLayerWindowSize.Present &&
            Message->PacketLayerWindowSize.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_PLWINDOWSIZE,
            Message->PacketLayerWindowSize.Length,
            Message->PacketLayerWindowSize.Contents);
    }
    if (Message->PacketSize.Present &&
            Message->PacketSize.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_PACKETSIZE,
            Message->PacketSize.Length,
            Message->PacketSize.Contents);
    }
    if (Message->ClosedUserGroup.Present &&
            Message->ClosedUserGroup.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_CLOSEDUG,
            Message->ClosedUserGroup.Length,
            Message->ClosedUserGroup.Contents);
    }
    if (Message->ReverseChargeIndication.Present &&
            Message->ReverseChargeIndication.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_REVCHARGE,
            Message->ReverseChargeIndication.Length,
            Message->ReverseChargeIndication.Contents);
    }
    if (Message->CallingPartyNumber.Present &&
            Message->CallingPartyNumber.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_CALLINGNUMBER,
            Message->CallingPartyNumber.Length,
            Message->CallingPartyNumber.Contents);
    }
    if (Message->CallingPartySubaddress.Present &&
            Message->CallingPartySubaddress.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_CALLINGSUBADDR,
            Message->CallingPartySubaddress.Length,
            Message->CallingPartySubaddress.Contents);
    }
    if (Message->CalledPartyNumber.Present)
    {
        WritePartyNumber(BufferDescriptor, IDENT_CALLEDNUMBER,
            Message->CalledPartyNumber.NumberType,
            Message->CalledPartyNumber.NumberingPlan,
            Message->CalledPartyNumber.PartyNumberLength,
            Message->CalledPartyNumber.PartyNumbers);
     }
    if (Message->CalledPartySubaddress.Present &&
            Message->CalledPartySubaddress.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_CALLEDSUBADDR,
            Message->CalledPartySubaddress.Length,
            Message->CalledPartySubaddress.Contents);
    }
    if (Message->RedirectingNumber.Present &&
            Message->RedirectingNumber.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_REDIRECTING,
            Message->RedirectingNumber.Length,
            Message->RedirectingNumber.Contents);
    }
    if (Message->TransitNetworkSelection.Present &&
            Message->TransitNetworkSelection.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_TRANSITNET,
            Message->TransitNetworkSelection.Length,
            Message->TransitNetworkSelection.Contents);
    }
    if (Message->RestartIndicator.Present &&
            Message->RestartIndicator.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_RESTART,
            Message->RestartIndicator.Length,
            Message->RestartIndicator.Contents);
    }
    if (Message->LowLayerCompatibility.Present &&
            Message->LowLayerCompatibility.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_LLCOMPATIBILITY,
            Message->LowLayerCompatibility.Length,
            Message->LowLayerCompatibility.Contents);
    }
    if (Message->HighLayerCompatibility.Present &&
            Message->HighLayerCompatibility.Length)
    {
        WriteVariableOctet(BufferDescriptor, IDENT_HLCOMPATIBILITY,
            Message->HighLayerCompatibility.Length,
            Message->HighLayerCompatibility.Contents);
    }
    if (Message->UserToUser.Present &&
            Message->UserToUser.UserInformationLength)
    {
        WriteVariableASN(BufferDescriptor,
            IDENT_USERUSER,
            Message->UserToUser.UserInformationLength,
            Message->UserToUser.UserInformation);
    }
    return CS_OK;
}

//------------------------------------------------------------------------------
// Parse a generic Q931 message and place the fields of the 
// of the buffer into the appropriate field structure.
//
// Parameters:
//     BufferDescriptor  Pointer to buffer descriptor of a 
//                       the network packet of the 931 message
//     Message           Pointer to space for parsed information.
//------------------------------------------------------------------------------
HRESULT
Q931MakeEncodedMessage(
    PQ931MESSAGE Message,
    BYTE **CodedBufferPtr,
    DWORD *CodedBufferLength)
{
    BUFFERDESCR BufferDescriptor;
    BYTE *OutBuffer = NULL;
    DWORD Pass1Length = 0;

    if ((CodedBufferPtr == NULL) || (CodedBufferLength == NULL))
    {
        return CS_BAD_PARAM;
    }

    BufferDescriptor.Length = 0;
    BufferDescriptor.BufferPtr = NULL;

    WriteQ931Fields(&BufferDescriptor, Message);
    if (BufferDescriptor.Length == 0)
    {
        return CS_NO_FIELD_DATA;
    }

    Pass1Length = BufferDescriptor.Length;

    OutBuffer = (BYTE *)Malloc(BufferDescriptor.Length + 1000);
    if (OutBuffer == NULL)
    {
        return CS_NO_MEMORY;
    }

    BufferDescriptor.Length = 0;
    BufferDescriptor.BufferPtr = OutBuffer;

    WriteQ931Fields(&BufferDescriptor, Message);

    if (Pass1Length != BufferDescriptor.Length)
    {
        // this is a serious error, since memory may have been overrun.
        return CS_BAD_PARAM;
    }

    *CodedBufferPtr = OutBuffer;
    *CodedBufferLength = BufferDescriptor.Length;

    return CS_OK;
}


//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
HRESULT
Q931SetupEncodePDU(
    WORD wCallReference,
    char *pszDisplay,
    char *pszCalledPartyNumber,
	DWORD dwBandwidth,
    BINARY_STRING *pUserUserData,
    BYTE **CodedBufferPtr,
    DWORD *CodedBufferLength)
{
    Q931MESSAGE *pMessage;
    HRESULT Result = CS_OK;
	BYTE bBandwidth;

    pMessage = (Q931MESSAGE *)Malloc(sizeof(Q931MESSAGE));
    if (pMessage == NULL)
    {
        return CS_NO_MEMORY;
    }

    // fill in the required fields for the setup message.
    memset(pMessage, 0, sizeof(Q931MESSAGE));
    pMessage->ProtocolDiscriminator = Q931PDVALUE;
    pMessage->CallReference = wCallReference;
    pMessage->MessageType = SETUPMESSAGETYPE;

    pMessage->BearerCapability.Present = TRUE;
    pMessage->BearerCapability.Length = 4;
    pMessage->BearerCapability.Contents[0] =
        (BYTE)(BEAR_EXT_BIT | BEAR_CCITT | BEAR_UNRESTRICTED_DIGITAL);
    pMessage->BearerCapability.Contents[1] = 
        (BYTE)(BEAR_CIRCUIT_MODE | BEAR_MULTIRATE);
	bBandwidth = (BYTE)(dwBandwidth / 64000);
	if ((dwBandwidth % 64000) != 0)
		bBandwidth++;
    pMessage->BearerCapability.Contents[2] =
		(BYTE)(BEAR_EXT_BIT | bBandwidth);
    pMessage->BearerCapability.Contents[3] = 
        (BYTE)(BEAR_EXT_BIT | BEAR_LAYER1_INDICATOR | BEAR_LAYER1_H221_H242);

    if (pszDisplay && *pszDisplay)
    {
        pMessage->Display.Present = TRUE;
        pMessage->Display.Length = (BYTE)(strlen(pszDisplay) + 1);
        strcpy((char *)pMessage->Display.Contents, pszDisplay);
    }

    if (pszCalledPartyNumber && *pszCalledPartyNumber)
    {
        WORD wLen = (WORD)strlen(pszCalledPartyNumber);
        pMessage->CalledPartyNumber.Present = TRUE;

        pMessage->CalledPartyNumber.NumberType =
            (BYTE)(CALLED_PARTY_EXT_BIT | CALLED_PARTY_TYPE_UNKNOWN);
        pMessage->CalledPartyNumber.NumberingPlan =
            (BYTE)(CALLED_PARTY_PLAN_E164);
        pMessage->CalledPartyNumber.PartyNumberLength = (BYTE)wLen;
        memcpy(pMessage->CalledPartyNumber.PartyNumbers,
            pszCalledPartyNumber, wLen);
    }

    if (pUserUserData && pUserUserData->ptr)
    {
        if (pUserUserData->length > sizeof(pMessage->UserToUser.UserInformation))
        {
            Free(pMessage);
            return CS_BAD_PARAM;
        }
        pMessage->UserToUser.Present = TRUE;
        pMessage->UserToUser.UserInformationLength = (pUserUserData->length);
        memcpy(pMessage->UserToUser.UserInformation,
            pUserUserData->ptr, pUserUserData->length);
    }
    Result = Q931MakeEncodedMessage(pMessage, CodedBufferPtr,
        CodedBufferLength);

    Free(pMessage);
    return Result;
}

//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
HRESULT
Q931ReleaseCompleteEncodePDU(
    WORD wCallReference,
    BYTE *pbCause,
    BINARY_STRING *pUserUserData,
    BYTE **CodedBufferPtr,
    DWORD *CodedBufferLength)
{
    Q931MESSAGE *pMessage;
    HRESULT Result = CS_OK;

    if (pbCause)
    {
        switch (*pbCause)
        {
            case CAUSE_VALUE_NORMAL_CLEAR:
            case CAUSE_VALUE_USER_BUSY:
            case CAUSE_VALUE_NO_ANSWER:
            case CAUSE_VALUE_REJECTED:
            case CAUSE_VALUE_NOT_IMPLEMENTED:
            case CAUSE_VALUE_INVALID_CRV:
            case CAUSE_VALUE_IE_MISSING:
            case CAUSE_VALUE_IE_CONTENTS:
            case CAUSE_VALUE_TIMER_EXPIRED:
                break;
            default:
                return CS_BAD_PARAM;
                break;
        }
    }

    pMessage = (Q931MESSAGE *)Malloc(sizeof(Q931MESSAGE));
    if (pMessage == NULL)
    {
        return CS_NO_MEMORY;
    }

    // fill in the required fields for the setup message.
    memset(pMessage, 0, sizeof(Q931MESSAGE));
    pMessage->ProtocolDiscriminator = Q931PDVALUE;
    pMessage->CallReference = wCallReference;
    pMessage->MessageType = RELEASECOMPLMESSAGETYPE;

    if (pbCause)
    {
        pMessage->Cause.Present = TRUE;
        pMessage->Cause.Length = 3;
        pMessage->Cause.Contents[0] = (BYTE)(CAUSE_CODING_CCITT | CAUSE_LOCATION_USER);
        pMessage->Cause.Contents[1] = (BYTE)(CAUSE_RECOMMENDATION_Q931);
        pMessage->Cause.Contents[2] = (BYTE)(CAUSE_EXT_BIT | *pbCause);
    }
    else
    {
        pMessage->Cause.Present = FALSE;
    }

    if (pUserUserData && pUserUserData->ptr)
    {
        if (pUserUserData->length > sizeof(pMessage->UserToUser.UserInformation))
        {
            Free(pMessage);
            return CS_BAD_PARAM;
        }
        pMessage->UserToUser.Present = TRUE;
        pMessage->UserToUser.UserInformationLength = (pUserUserData->length);
        memcpy(pMessage->UserToUser.UserInformation,
            pUserUserData->ptr, pUserUserData->length);
    }
    Result = Q931MakeEncodedMessage(pMessage, CodedBufferPtr,
        CodedBufferLength);

    Free(pMessage);
    return Result;
}

//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
HRESULT
Q931ConnectEncodePDU(
    WORD wCallReference,
    char *pszDisplay,
	DWORD dwBandwidth,
    BINARY_STRING *pUserUserData,
    BYTE **CodedBufferPtr,
    DWORD *CodedBufferLength)
{
    Q931MESSAGE *pMessage;
    HRESULT Result = CS_OK;
	BYTE bBandwidth;

    pMessage = (Q931MESSAGE *)Malloc(sizeof(Q931MESSAGE));
    if (pMessage == NULL)
    {
        return CS_NO_MEMORY;
    }

    // fill in the required fields for the setup message.
    memset(pMessage, 0, sizeof(Q931MESSAGE));
    pMessage->ProtocolDiscriminator = Q931PDVALUE;
    pMessage->CallReference = wCallReference;
    pMessage->MessageType = CONNECTMESSAGETYPE;

    pMessage->BearerCapability.Present = TRUE;
    pMessage->BearerCapability.Length = 4;
    pMessage->BearerCapability.Contents[0] =
        (BYTE)(BEAR_EXT_BIT | BEAR_CCITT | BEAR_UNRESTRICTED_DIGITAL);
    pMessage->BearerCapability.Contents[1] = 
        (BYTE)(BEAR_CIRCUIT_MODE | BEAR_MULTIRATE);
	bBandwidth = (BYTE)(dwBandwidth / 64000);
	if ((dwBandwidth % 64000) != 0)
		bBandwidth++;
    pMessage->BearerCapability.Contents[2] =
		(BYTE)(BEAR_EXT_BIT | bBandwidth);
    pMessage->BearerCapability.Contents[3] = 
        (BYTE)(BEAR_EXT_BIT | BEAR_LAYER1_INDICATOR | BEAR_LAYER1_H221_H242);

    if (pszDisplay && *pszDisplay)
    {
        pMessage->Display.Present = TRUE;
        pMessage->Display.Length = (BYTE)strlen(pszDisplay);
        strcpy((char *)pMessage->Display.Contents, pszDisplay);
    }

    if (pUserUserData && pUserUserData->ptr)
    {
        if (pUserUserData->length > sizeof(pMessage->UserToUser.UserInformation))
        {
            Free(pMessage);
            return CS_BAD_PARAM;
        }
        pMessage->UserToUser.Present = TRUE;
        pMessage->UserToUser.UserInformationLength = (pUserUserData->length);
        memcpy(pMessage->UserToUser.UserInformation,
            pUserUserData->ptr, pUserUserData->length);
    }
    Result = Q931MakeEncodedMessage(pMessage, CodedBufferPtr,
        CodedBufferLength);

    Free(pMessage);
    return Result;
}

//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
HRESULT
Q931ProceedingEncodePDU(
    WORD wCallReference,
    BINARY_STRING *pUserUserData,
    BYTE **CodedBufferPtr,
    DWORD *CodedBufferLength)
{
    Q931MESSAGE *pMessage;
    HRESULT Result = CS_OK;

    pMessage = (Q931MESSAGE *)Malloc(sizeof(Q931MESSAGE));
    if (pMessage == NULL)
    {
        return CS_NO_MEMORY;
    }

    // fill in the required fields for the setup message.
    memset(pMessage, 0, sizeof(Q931MESSAGE));
    pMessage->ProtocolDiscriminator = Q931PDVALUE;
    pMessage->CallReference = wCallReference;
    pMessage->MessageType = PROCEEDINGMESSAGETYPE;

    if (pUserUserData && pUserUserData->ptr)
    {
        if (pUserUserData->length > sizeof(pMessage->UserToUser.UserInformation))
        {
            Free(pMessage);
            return CS_BAD_PARAM;
        }
        pMessage->UserToUser.Present = TRUE;
        pMessage->UserToUser.UserInformationLength = (pUserUserData->length);
        memcpy(pMessage->UserToUser.UserInformation,
            pUserUserData->ptr, pUserUserData->length);
    }
    Result = Q931MakeEncodedMessage(pMessage, CodedBufferPtr,
        CodedBufferLength);

    Free(pMessage);
    return Result;
}

//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
HRESULT
Q931AlertingEncodePDU(
    WORD wCallReference,
    BINARY_STRING *pUserUserData,
    BYTE **CodedBufferPtr,
    DWORD *CodedBufferLength)
{
    Q931MESSAGE *pMessage;
    HRESULT Result = CS_OK;

    pMessage = (Q931MESSAGE *)Malloc(sizeof(Q931MESSAGE));
    if (pMessage == NULL)
    {
        return CS_NO_MEMORY;
    }

    // fill in the required fields for the setup message.
    memset(pMessage, 0, sizeof(Q931MESSAGE));
    pMessage->ProtocolDiscriminator = Q931PDVALUE;
    pMessage->CallReference = wCallReference;
    pMessage->MessageType = ALERTINGMESSAGETYPE;

    if (pUserUserData && pUserUserData->ptr)
    {
        if (pUserUserData->length > sizeof(pMessage->UserToUser.UserInformation))
        {
            Free(pMessage);
            return CS_BAD_PARAM;
        }
        pMessage->UserToUser.Present = TRUE;
        pMessage->UserToUser.UserInformationLength = (pUserUserData->length);
        memcpy(pMessage->UserToUser.UserInformation,
            pUserUserData->ptr, pUserUserData->length);
    }
    Result = Q931MakeEncodedMessage(pMessage, CodedBufferPtr,
        CodedBufferLength);

    Free(pMessage);
    return Result;
}

//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
HRESULT
Q931FacilityEncodePDU(
    WORD wCallReference,
    BINARY_STRING *pUserUserData,
    BYTE **CodedBufferPtr,
    DWORD *CodedBufferLength)
{
    Q931MESSAGE *pMessage;
    HRESULT Result = CS_OK;

    pMessage = (Q931MESSAGE *)Malloc(sizeof(Q931MESSAGE));
    if (pMessage == NULL)
    {
        return CS_NO_MEMORY;
    }

    // fill in the required fields for the setup message.
    memset(pMessage, 0, sizeof(Q931MESSAGE));
    pMessage->ProtocolDiscriminator = Q931PDVALUE;
    pMessage->CallReference = wCallReference;
    pMessage->MessageType = FACILITYMESSAGETYPE;

    // The facility ie is encoded as present, but empty...
    pMessage->Facility.Present = TRUE;
    pMessage->Facility.Length = 0;
    pMessage->Facility.Contents[0] = 0;

    if (pUserUserData && pUserUserData->ptr)
    {
        if (pUserUserData->length > sizeof(pMessage->UserToUser.UserInformation))
        {
            Free(pMessage);
            return CS_BAD_PARAM;
        }
        pMessage->UserToUser.Present = TRUE;
        pMessage->UserToUser.UserInformationLength = (pUserUserData->length);
        memcpy(pMessage->UserToUser.UserInformation,
            pUserUserData->ptr, pUserUserData->length);
    }
    Result = Q931MakeEncodedMessage(pMessage, CodedBufferPtr,
        CodedBufferLength);

    Free(pMessage);
    return Result;
}

//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
HRESULT
Q931StatusEncodePDU(
    WORD wCallReference,
    char *pszDisplay,
    BYTE bCause,
    BYTE bCallState,
    BYTE **CodedBufferPtr,
    DWORD *CodedBufferLength)
{
    Q931MESSAGE *pMessage;
    HRESULT Result = CS_OK;

    pMessage = (Q931MESSAGE *)Malloc(sizeof(Q931MESSAGE));
    if (pMessage == NULL)
    {
        return CS_NO_MEMORY;
    }

    // fill in the required fields for the setup message.
    memset(pMessage, 0, sizeof(Q931MESSAGE));
    pMessage->ProtocolDiscriminator = Q931PDVALUE;
    pMessage->CallReference = wCallReference;
    pMessage->MessageType = STATUSMESSAGETYPE;

    if (pszDisplay && *pszDisplay)
    {
        pMessage->Display.Present = TRUE;
        pMessage->Display.Length = (BYTE)(strlen(pszDisplay) + 1);
        strcpy((char *)pMessage->Display.Contents, pszDisplay);
    }

    pMessage->Cause.Present = TRUE;
    pMessage->Cause.Length = 3;
    pMessage->Cause.Contents[0] = (BYTE)(CAUSE_CODING_CCITT | CAUSE_LOCATION_USER);
    pMessage->Cause.Contents[1] = (BYTE)(CAUSE_RECOMMENDATION_Q931);
    pMessage->Cause.Contents[2] = (BYTE)(CAUSE_EXT_BIT | bCause);

    pMessage->CallState.Present = TRUE;
    pMessage->CallState.Length = 1;
    pMessage->CallState.Contents[0] = (BYTE)(bCallState);

    Result = Q931MakeEncodedMessage(pMessage, CodedBufferPtr,
        CodedBufferLength);

    Free(pMessage);
    return Result;
}
#if(0)
//========================================================================
//========================================================================
//========================================================================
// THIS IS THE ASN PART...
//========================================================================
//========================================================================
//========================================================================

static ERROR_MAP EncodeErrorMap[] =
{
    PDU_ENCODED, __TEXT("PDU successfully encoded"),
    MORE_BUF, __TEXT("User-provided output buffer too small"),
    PDU_RANGE, __TEXT("PDU specified out of range"),
    BAD_ARG, __TEXT("Bad pointer was passed"),
    BAD_VERSION, __TEXT("Versions of encoder and table do not match"),
    OUT_MEMORY, __TEXT("Memory-allocation error"),
    BAD_CHOICE, __TEXT("Unknown selector for a choice"),
    BAD_OBJID, __TEXT("Object identifier conflicts with x.208"),
    BAD_PTR, __TEXT("Unexpected NULL pointer in input buffer"),
    BAD_TIME, __TEXT("Bad value in time type"),
    MEM_ERROR, __TEXT("Memory violation signal trapped"),
    BAD_TABLE, __TEXT("Table was bad, but not NULL"),
    TOO_LONG, __TEXT("Type was longer than constraint"),
    CONSTRAINT_VIOLATED, __TEXT("Constraint violation error occured"),
    FATAL_ERROR, __TEXT("Serious internal error"),
    ACCESS_SERIALIZATION_ERROR, __TEXT("Thread access to global data failed"),
    NULL_TBL, __TEXT("NULL control table pointer"),
    NULL_FCN, __TEXT("Encoder called via a NULL pointer"),
    BAD_ENCRULES, __TEXT("Unknown encoding rules"),
    UNAVAIL_ENCRULES, __TEXT("Encoding rules requested are not implemented"),
    UNIMPLEMENTED, __TEXT("Type was not implemented yet"),
//    LOAD_ERR, __TEXT("Unable to load DLL"),
    CANT_OPEN_TRACE_FILE, __TEXT("Error when opening a trace file"),
    TRACE_FILE_ALREADY_OPEN, __TEXT("Trace file has been opened"),
    TABLE_MISMATCH, __TEXT("Control table mismatch"),
    0, NULL
};

static ERROR_MAP DecodeErrorMap[] =
{
    PDU_DECODED, __TEXT("PDU successfully decoded"),
    MORE_BUF, __TEXT("User-provided output buffer too small"),
    NEGATIVE_UINTEGER, __TEXT("The first unsigned bit of the encoding is 1"),
    PDU_RANGE, __TEXT("Pdu specified out of range"),
    MORE_INPUT, __TEXT("Unexpected end of input buffer"),
    DATA_ERROR, __TEXT("An error exists in the encoded data"),
    BAD_VERSION, __TEXT("Versions of encoder and table do not match"),
    OUT_MEMORY, __TEXT("Memory-allocation error"),
    PDU_MISMATCH, __TEXT("The PDU tag does not match data"),
    LIMITED, __TEXT("Size implementation limit exceeded"),
    CONSTRAINT_VIOLATED, __TEXT("Constraint violation error occured"),
    ACCESS_SERIALIZATION_ERROR, __TEXT("Thread access to global data failed"),
    NULL_TBL, __TEXT("NULL control table pointer"),
    NULL_FCN, __TEXT("Encoder called via a NULL pointer"),
    BAD_ENCRULES, __TEXT("Unknown encoding rules"),
    UNAVAIL_ENCRULES, __TEXT("Encoding rules requested are not implemented"),
    UNIMPLEMENTED, __TEXT("The type was not implemented yet"),
//    LOAD_ERR, __TEXT("Unable to load DLL"),
    CANT_OPEN_TRACE_FILE, __TEXT("Error when opening a trace file"),
    TRACE_FILE_ALREADY_OPEN, __TEXT("The trace file has been opened"),
    TABLE_MISMATCH, __TEXT("Control table mismatch"),
    0, NULL
};

#endif // if(0)

//====================================================================================
//====================================================================================
#ifdef UNICODE_TRACE
LPWSTR
#else
LPSTR
#endif
ErrorToTextASN(ERROR_MAP *Map, int nErrorCode)
{
    register int nIndex = 0;

    if (Map != NULL)
    {
        for (nIndex = 0; Map[nIndex].pszErrorText; nIndex++)
        {
            if (Map[nIndex].nErrorCode == nErrorCode)
            {
                return Map[nIndex].pszErrorText;
            }
        }
    }
    return __TEXT("Unknown ASN.1 Error");
}

#if 0
//------------------------------------------------------------------------
//------------------------------------------------------------------------
int
ASN1LinePrint(FILE *stream, const char *format, ...)
{
    va_list marker;
    char buf[300];
    int i;

    va_start(marker, format);
    i = wsprintf(buf, format, marker);
    va_end(marker);

    // TRACE the buf...
    QTRACE((buf));

    return i;
}
#endif


//------------------------------------------------------------------------
//------------------------------------------------------------------------
HRESULT
Q931InitPER()
{
    // initialize TELES ASN.1 module structure
    if (Q931_InitModule() != ASN1_SUCCESS)
    {
        ASSERT(FALSE);
        return CS_SUBSYSTEM_FAILURE;
    }

    // note: init of world struct moved to CreateCallObject()

    ProtocolId1.value = Q931_PROTOCOL_ID1;
    ProtocolId1.next = &ProtocolId2;
    ProtocolId2.value = Q931_PROTOCOL_ID2;
    ProtocolId2.next = &ProtocolId3;
    ProtocolId3.value = Q931_PROTOCOL_ID3;
    ProtocolId3.next = &ProtocolId4;
    ProtocolId4.value = Q931_PROTOCOL_ID4;
    ProtocolId4.next = &ProtocolId5;
    ProtocolId5.value = Q931_PROTOCOL_ID5;
    ProtocolId5.next = &ProtocolId6;
    ProtocolId6.value = Q931_PROTOCOL_ID6;
    ProtocolId6.next = NULL;

    // gateway protocol supported.  For now, hard-coded to only 1:  H323.
    TempProtocol.next = NULL;
    TempProtocol.value.choice = h323_chosen;

    return CS_OK;
}

//------------------------------------------------------------------------
//------------------------------------------------------------------------
HRESULT
Q931DeInitPER()
{
    // clean up TELES ASN.1 module structure
    Q931_TermModule();
    return CS_OK;
}

#define USE_ASN1_ENCODING 5

//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
HRESULT
Q931SetupEncodeASN(
    PCC_NONSTANDARDDATA pNonStandardData,
    CC_ADDR *pCallerAddr,                     // this data is not yet passed in the PDU...
    CC_ADDR *pCalleeAddr,
    WORD wGoal,
    WORD wCallType,
    BOOL bCallerIsMC,
    CC_CONFERENCEID *pConferenceID,
    PCC_ALIASNAMES pCallerAliasList,
    PCC_ALIASNAMES pCalleeAliasList,
    PCC_ALIASNAMES pExtraAliasList,
    PCC_ALIASITEM pExtensionAliasItem,
    PCC_VENDORINFO pVendorInfo,
    BOOL bIsTerminal,
    BOOL bIsGateway,
    ASN1_CODER_INFO *pWorld,
    BYTE **ppEncodedBuf,
    DWORD *pdwEncodedLength)
{
    int rc;
    H323_UserInformation UserInfo;

    *ppEncodedBuf = NULL;
    *pdwEncodedLength = 0;

    memset(&UserInfo, 0, sizeof(H323_UserInformation));

    UserInfo.bit_mask = 0;

    // make sure the user_data_present flag is turned off.
    UserInfo.bit_mask &= (~user_data_present);

    if (pNonStandardData)
    {
        UserInfo.h323_uu_pdu.bit_mask |= H323_UU_PDU_nnStndrdDt_present;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.choice = h221NonStandard_chosen;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35CountryCode =
            pNonStandardData->bCountryCode;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35Extension =
            pNonStandardData->bExtension;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.manufacturerCode =
            pNonStandardData->wManufacturerCode;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.length =
            pNonStandardData->sData.wOctetStringLength;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.value =
            pNonStandardData->sData.pOctetString;
    }
    else
    {
        UserInfo.h323_uu_pdu.bit_mask &= (~H323_UU_PDU_nnStndrdDt_present);
    }

    UserInfo.h323_uu_pdu.h323_message_body.choice = setup_chosen;
    UserInfo.h323_uu_pdu.h323_message_body.u.setup.bit_mask = 0;

    UserInfo.h323_uu_pdu.h323_message_body.u.setup.protocolIdentifier = &ProtocolId1;

    if (pCallerAliasList)
    {
        CS_STATUS AliasResult = CS_OK;
        AliasResult = AliasToSeqof((struct _seqof3 **)&(UserInfo.h323_uu_pdu.
            h323_message_body.u.setup.sourceAddress), pCallerAliasList);
        if (AliasResult != CS_OK)
        {
            return CS_NO_MEMORY;
        }
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.bit_mask |=
            (sourceAddress_present);
    }
    else
    {
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.bit_mask &=
            (~sourceAddress_present);
    }

    UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceInfo.bit_mask = 0;

    if (pVendorInfo)
    {
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceInfo.bit_mask |= vendor_present;
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceInfo.vendor.bit_mask = 0;
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceInfo.vendor.vendor.t35CountryCode = pVendorInfo->bCountryCode;
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceInfo.vendor.vendor.t35Extension = pVendorInfo->bExtension;
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceInfo.vendor.vendor.manufacturerCode = pVendorInfo->wManufacturerCode;
        if (pVendorInfo->pProductNumber && pVendorInfo->pProductNumber->pOctetString &&
                pVendorInfo->pProductNumber->wOctetStringLength)
        {
            UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceInfo.vendor.bit_mask |= productId_present;
            UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceInfo.vendor.productId.length =
                pVendorInfo->pProductNumber->wOctetStringLength;
            memcpy(UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceInfo.vendor.productId.value,
                pVendorInfo->pProductNumber->pOctetString,
                pVendorInfo->pProductNumber->wOctetStringLength);
        }
        if (pVendorInfo->pVersionNumber && pVendorInfo->pVersionNumber->pOctetString &&
                pVendorInfo->pVersionNumber->wOctetStringLength)
        {
            UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceInfo.vendor.bit_mask |= versionId_present;
            UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceInfo.vendor.versionId.length =
                pVendorInfo->pVersionNumber->wOctetStringLength;
            memcpy(UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceInfo.vendor.versionId.value,
                pVendorInfo->pVersionNumber->pOctetString,
                pVendorInfo->pVersionNumber->wOctetStringLength);
        }
    }

    if (bIsTerminal)
    {
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceInfo.bit_mask |=
            terminal_present;
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceInfo.terminal.bit_mask = 0;
    }

    if (bIsGateway)
    {
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceInfo.bit_mask |=
            gateway_present;
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceInfo.gateway.bit_mask = protocol_present;
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceInfo.gateway.protocol = &TempProtocol;
    }

    UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceInfo.mc = (ASN1_BOOL)bCallerIsMC;
    UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceInfo.undefinedNode = 0;

    if (pCalleeAliasList)
    {
        CS_STATUS AliasResult = CS_OK;
        AliasResult = AliasWithPrefixToSeqof((struct _seqof3 **)&(UserInfo.h323_uu_pdu.
            h323_message_body.u.setup.destinationAddress), pCalleeAliasList);
        if (AliasResult != CS_OK)
        {
            FreeSeqof((struct _seqof3 *)UserInfo.h323_uu_pdu.
                h323_message_body.u.setup.sourceAddress);
            UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceAddress = NULL;
            return CS_NO_MEMORY;
        }
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.bit_mask |=
            (destinationAddress_present);
    }
    else
    {
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.bit_mask &=
            (~destinationAddress_present);
    }

    if (pExtraAliasList)
    {
        CS_STATUS AliasResult = CS_OK;
        AliasResult = AliasWithPrefixToSeqof((struct _seqof3 **)&(UserInfo.h323_uu_pdu.
            h323_message_body.u.setup.destExtraCallInfo), pExtraAliasList);
        if (AliasResult != CS_OK)
        {
            FreeSeqof((struct _seqof3 *)UserInfo.h323_uu_pdu.h323_message_body.u.setup.destinationAddress);
            UserInfo.h323_uu_pdu.h323_message_body.u.setup.destinationAddress = NULL;
            FreeSeqof((struct _seqof3 *)UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceAddress);
            UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceAddress = NULL;
            return CS_NO_MEMORY;
        }
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.bit_mask |=
            (destExtraCallInfo_present);
    }
    else
    {
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.bit_mask &=
            (~destExtraCallInfo_present);
    }

    if (pCalleeAddr)
    {
        DWORD a = pCalleeAddr->Addr.IP_Binary.dwAddr;
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.destCallSignalAddress.choice = ipAddress_chosen;
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.destCallSignalAddress.u.ipAddress.ip.length = 4;
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.destCallSignalAddress.u.ipAddress.port =
            pCalleeAddr->Addr.IP_Binary.wPort;
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.destCallSignalAddress.u.ipAddress.ip.value[0] =
            ((BYTE *)&a)[3];
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.destCallSignalAddress.u.ipAddress.ip.value[1] =
            ((BYTE *)&a)[2];
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.destCallSignalAddress.u.ipAddress.ip.value[2] =
            ((BYTE *)&a)[1];
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.destCallSignalAddress.u.ipAddress.ip.value[3] =
            ((BYTE *)&a)[0];
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.bit_mask |=
            (destCallSignalAddress_present);
    }
    else
    {
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.bit_mask &=
            (~destCallSignalAddress_present);
    }

    UserInfo.h323_uu_pdu.h323_message_body.u.setup.activeMC = (ASN1_BOOL)bCallerIsMC;

    if (pConferenceID != NULL)
    {
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.conferenceID.length =
            sizeof(UserInfo.h323_uu_pdu.h323_message_body.u.setup.conferenceID.value);
        memcpy(UserInfo.h323_uu_pdu.h323_message_body.u.setup.conferenceID.value,
            pConferenceID->buffer,
            UserInfo.h323_uu_pdu.h323_message_body.u.setup.conferenceID.length);
    }

    switch (wGoal)
	{
	case CSG_INVITE:
		UserInfo.h323_uu_pdu.h323_message_body.u.setup.conferenceGoal.choice = invite_chosen;
		break;
    case CSG_JOIN:
		UserInfo.h323_uu_pdu.h323_message_body.u.setup.conferenceGoal.choice = join_chosen;
		break;
	default:
		UserInfo.h323_uu_pdu.h323_message_body.u.setup.conferenceGoal.choice = create_chosen;
	} // switch

	switch (wCallType)
	{
	case CC_CALLTYPE_1_N:
		UserInfo.h323_uu_pdu.h323_message_body.u.setup.callType.choice = oneToN_chosen;
		break;
	case CC_CALLTYPE_N_1:
		UserInfo.h323_uu_pdu.h323_message_body.u.setup.callType.choice = nToOne_chosen;
		break;
	case CC_CALLTYPE_N_N:
		UserInfo.h323_uu_pdu.h323_message_body.u.setup.callType.choice = nToN_chosen;
		break;
	default:
		UserInfo.h323_uu_pdu.h323_message_body.u.setup.callType.choice = pointToPoint_chosen;
	} // switch

    if (pCallerAddr)
    {
        DWORD a = pCallerAddr->Addr.IP_Binary.dwAddr;
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceCallSignalAddress.choice = ipAddress_chosen;
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceCallSignalAddress.u.ipAddress.ip.length = 4;
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceCallSignalAddress.u.ipAddress.port =
            pCallerAddr->Addr.IP_Binary.wPort;
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceCallSignalAddress.u.ipAddress.ip.value[0] =
            ((BYTE *)&a)[3];
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceCallSignalAddress.u.ipAddress.ip.value[1] =
            ((BYTE *)&a)[2];
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceCallSignalAddress.u.ipAddress.ip.value[2] =
            ((BYTE *)&a)[1];
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceCallSignalAddress.u.ipAddress.ip.value[3] =
            ((BYTE *)&a)[0];
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.bit_mask |=
            (sourceCallSignalAddress_present);
    }
    else
    {
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.bit_mask &=
            (~sourceCallSignalAddress_present);
    }

    if (pExtensionAliasItem)
    {
        CS_STATUS AliasResult = CS_OK;
        AliasResult = Q931CopyAliasItemToAliasAddr(&(UserInfo.h323_uu_pdu.
            h323_message_body.u.setup.remoteExtensionAddress), pExtensionAliasItem);
        if (AliasResult != CS_OK)
        {
            FreeSeqof((struct _seqof3 *)UserInfo.h323_uu_pdu.h323_message_body.u.setup.destExtraCallInfo);
            UserInfo.h323_uu_pdu.h323_message_body.u.setup.destExtraCallInfo = NULL;
            FreeSeqof((struct _seqof3 *)UserInfo.h323_uu_pdu.h323_message_body.u.setup.destinationAddress);
            UserInfo.h323_uu_pdu.h323_message_body.u.setup.destinationAddress = NULL;
            FreeSeqof((struct _seqof3 *)UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceAddress);
            UserInfo.h323_uu_pdu.h323_message_body.u.setup.sourceAddress = NULL;
            return CS_NO_MEMORY;
        }
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.bit_mask |=
            (remoteExtensionAddress_present);
    }
    else
    {
        UserInfo.h323_uu_pdu.h323_message_body.u.setup.bit_mask &=
            (~remoteExtensionAddress_present);
    }

    rc = Q931_Encode(pWorld,
                     (void *) &UserInfo,
                     H323_UserInformation_PDU,
                     ppEncodedBuf,
                     pdwEncodedLength);

    // Free the alias name structures from the UserInfo area.
    FreeSeqof((struct _seqof3 *)UserInfo.h323_uu_pdu.h323_message_body.u.
        setup.sourceAddress);
    FreeSeqof((struct _seqof3 *)UserInfo.h323_uu_pdu.h323_message_body.u.
        setup.destinationAddress);
    FreeSeqof((struct _seqof3 *)UserInfo.h323_uu_pdu.h323_message_body.u.
        setup.destExtraCallInfo);
    Q931ClearAliasAddr(&(UserInfo.h323_uu_pdu.h323_message_body.u.setup.remoteExtensionAddress));

    if (ASN1_FAILED(rc))
    {
        ASSERT(FALSE);
        return CS_SUBSYSTEM_FAILURE;
    }

    return CS_OK;
}

//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
void
Q931FreeEncodedBuffer(ASN1_CODER_INFO *pWorld, BYTE *pEncodedBuf)
{
    ASN1_FreeEncoded(pWorld->pEncInfo, pEncodedBuf);
}

//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
HRESULT
Q931ReleaseCompleteEncodeASN(
    PCC_NONSTANDARDDATA pNonStandardData,
    CC_CONFERENCEID *pConferenceID,          // not passed in PDU!
    BYTE *pbReason,
    ASN1_CODER_INFO *pWorld,
    BYTE **ppEncodedBuf,
    DWORD *pdwEncodedLength)
{
    int rc;
    H323_UserInformation UserInfo;

    *ppEncodedBuf = NULL;
    *pdwEncodedLength = 0;

    memset(&UserInfo, 0, sizeof(H323_UserInformation));

    UserInfo.bit_mask = 0;

    // make sure the user_data_present flag is turned off.
    UserInfo.bit_mask &= (~user_data_present);

    UserInfo.h323_uu_pdu.bit_mask = 0;

    if (pNonStandardData)
    {
        UserInfo.h323_uu_pdu.bit_mask |= H323_UU_PDU_nnStndrdDt_present;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.choice = h221NonStandard_chosen;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35CountryCode =
            pNonStandardData->bCountryCode;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35Extension =
            pNonStandardData->bExtension;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.manufacturerCode =
            pNonStandardData->wManufacturerCode;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.length =
            pNonStandardData->sData.wOctetStringLength;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.value =
            pNonStandardData->sData.pOctetString;
    }
    else
    {
        UserInfo.h323_uu_pdu.bit_mask &= (~H323_UU_PDU_nnStndrdDt_present);
    }

    UserInfo.h323_uu_pdu.h323_message_body.choice = releaseComplete_chosen;

    UserInfo.h323_uu_pdu.h323_message_body.u.releaseComplete.protocolIdentifier = &ProtocolId1;

    if (pbReason)
    {
        unsigned short choice = 0;

        UserInfo.h323_uu_pdu.h323_message_body.u.releaseComplete.bit_mask |=
            (reason_present);
        switch (*pbReason)
        {
        case CC_REJECT_NO_BANDWIDTH:
            choice = noBandwidth_chosen;
            break;
        case CC_REJECT_GATEKEEPER_RESOURCES:
            choice = gatekeeperResources_chosen;
            break;
        case CC_REJECT_UNREACHABLE_DESTINATION:
            choice = unreachableDestination_chosen;
            break;
        case CC_REJECT_DESTINATION_REJECTION:
            choice = destinationRejection_chosen;
            break;
        case CC_REJECT_INVALID_REVISION:
            choice = invalidRevision_chosen;
            break;
        case CC_REJECT_NO_PERMISSION:
            choice = noPermission_chosen;
            break;
        case CC_REJECT_UNREACHABLE_GATEKEEPER:
            choice = unreachableGatekeeper_chosen;
            break;
        case CC_REJECT_GATEWAY_RESOURCES:
            choice = gatewayResources_chosen;
            break;
        case CC_REJECT_BAD_FORMAT_ADDRESS:
            choice = badFormatAddress_chosen;
            break;
        case CC_REJECT_ADAPTIVE_BUSY:
            choice = adaptiveBusy_chosen;
            break;
        case CC_REJECT_IN_CONF:
            choice = inConf_chosen;
            break;
        case CC_REJECT_CALL_DEFLECTION:
            choice = facilityCallDeflection_chosen;
            break;
        case CC_REJECT_UNDEFINED_REASON:
            choice = RlsCmpltRsn_undfndRsn_chosen;
            break;
        case CC_REJECT_USER_BUSY:
            choice = inConf_chosen;
            break;
        default:
            return CS_BAD_PARAM;
            break;
        }
        UserInfo.h323_uu_pdu.h323_message_body.u.releaseComplete.reason.choice = choice;
    }

    rc = Q931_Encode(pWorld,
                     (void *) &UserInfo,
                     H323_UserInformation_PDU,
                     ppEncodedBuf,
                     pdwEncodedLength);

    if (ASN1_FAILED(rc))
    {
        ASSERT(FALSE);
        return CS_SUBSYSTEM_FAILURE;
    }

    return CS_OK;
}

//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
HRESULT
Q931ConnectEncodeASN(
    PCC_NONSTANDARDDATA pNonStandardData,
    CC_CONFERENCEID *pConferenceID, // must be able to support 16 byte conf id's!
    CC_ADDR *h245Addr,
    PCC_ENDPOINTTYPE pEndpointType,
    ASN1_CODER_INFO *pWorld,
    BYTE **ppEncodedBuf,
    DWORD *pdwEncodedLength)
{
    int rc;
    H323_UserInformation UserInfo;

    *ppEncodedBuf = NULL;
    *pdwEncodedLength = 0;

    memset(&UserInfo, 0, sizeof(H323_UserInformation));
    UserInfo.bit_mask = 0;

    // make sure the user_data_present flag is turned off.
    UserInfo.bit_mask &= (~user_data_present);

    UserInfo.h323_uu_pdu.bit_mask = 0;

    if (pNonStandardData)
    {
        UserInfo.h323_uu_pdu.bit_mask |= H323_UU_PDU_nnStndrdDt_present;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.choice = h221NonStandard_chosen;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35CountryCode =
            pNonStandardData->bCountryCode;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35Extension =
            pNonStandardData->bExtension;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.manufacturerCode =
            pNonStandardData->wManufacturerCode;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.length =
            pNonStandardData->sData.wOctetStringLength;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.value =
            pNonStandardData->sData.pOctetString;
    }
    else
    {
        UserInfo.h323_uu_pdu.bit_mask &= (~H323_UU_PDU_nnStndrdDt_present);
    }

    UserInfo.h323_uu_pdu.h323_message_body.choice = connect_chosen;

    UserInfo.h323_uu_pdu.h323_message_body.u.connect.protocolIdentifier = &ProtocolId1;

    if (h245Addr != NULL)
    {
        DWORD a = h245Addr->Addr.IP_Binary.dwAddr;
        UserInfo.h323_uu_pdu.h323_message_body.u.connect.Cnnct_UUIE_h245Address.choice = ipAddress_chosen;
        UserInfo.h323_uu_pdu.h323_message_body.u.connect.Cnnct_UUIE_h245Address.u.ipAddress.ip.length = 4;
        UserInfo.h323_uu_pdu.h323_message_body.u.connect.Cnnct_UUIE_h245Address.u.ipAddress.port =
            h245Addr->Addr.IP_Binary.wPort;
        UserInfo.h323_uu_pdu.h323_message_body.u.connect.Cnnct_UUIE_h245Address.u.ipAddress.ip.value[0] =
            ((BYTE *)&a)[3];
        UserInfo.h323_uu_pdu.h323_message_body.u.connect.Cnnct_UUIE_h245Address.u.ipAddress.ip.value[1] =
            ((BYTE *)&a)[2];
        UserInfo.h323_uu_pdu.h323_message_body.u.connect.Cnnct_UUIE_h245Address.u.ipAddress.ip.value[2] =
            ((BYTE *)&a)[1];
        UserInfo.h323_uu_pdu.h323_message_body.u.connect.Cnnct_UUIE_h245Address.u.ipAddress.ip.value[3] =
            ((BYTE *)&a)[0];
        UserInfo.h323_uu_pdu.h323_message_body.u.connect.bit_mask |=
            (Cnnct_UUIE_h245Address_present);
    }
    else
    {
        UserInfo.h323_uu_pdu.h323_message_body.u.connect.bit_mask &=
            (~Cnnct_UUIE_h245Address_present);
    }

    UserInfo.h323_uu_pdu.h323_message_body.u.connect.destinationInfo.bit_mask = 0;

    if (pEndpointType)
    {
        PCC_VENDORINFO pVendorInfo = pEndpointType->pVendorInfo;
        if (pVendorInfo)
        {
            UserInfo.h323_uu_pdu.h323_message_body.u.connect.destinationInfo.bit_mask |= vendor_present;
            UserInfo.h323_uu_pdu.h323_message_body.u.connect.destinationInfo.vendor.bit_mask = 0;
            UserInfo.h323_uu_pdu.h323_message_body.u.connect.destinationInfo.vendor.vendor.t35CountryCode = pVendorInfo->bCountryCode;
            UserInfo.h323_uu_pdu.h323_message_body.u.connect.destinationInfo.vendor.vendor.t35Extension = pVendorInfo->bExtension;
            UserInfo.h323_uu_pdu.h323_message_body.u.connect.destinationInfo.vendor.vendor.manufacturerCode = pVendorInfo->wManufacturerCode;

            if (pVendorInfo->pProductNumber && pVendorInfo->pProductNumber->pOctetString &&
                    pVendorInfo->pProductNumber->wOctetStringLength)
            {
                UserInfo.h323_uu_pdu.h323_message_body.u.connect.destinationInfo.vendor.bit_mask |= productId_present;
                UserInfo.h323_uu_pdu.h323_message_body.u.connect.destinationInfo.vendor.productId.length =
                    pVendorInfo->pProductNumber->wOctetStringLength;
                memcpy(UserInfo.h323_uu_pdu.h323_message_body.u.connect.destinationInfo.vendor.productId.value,
                    pVendorInfo->pProductNumber->pOctetString,
                    pVendorInfo->pProductNumber->wOctetStringLength);
            }
            if (pVendorInfo->pVersionNumber && pVendorInfo->pVersionNumber->pOctetString &&
                    pVendorInfo->pVersionNumber->wOctetStringLength)
            {
                UserInfo.h323_uu_pdu.h323_message_body.u.connect.destinationInfo.vendor.bit_mask |= versionId_present;
                UserInfo.h323_uu_pdu.h323_message_body.u.connect.destinationInfo.vendor.versionId.length =
                    pVendorInfo->pVersionNumber->wOctetStringLength;
                memcpy(UserInfo.h323_uu_pdu.h323_message_body.u.connect.destinationInfo.vendor.versionId.value,
                    pVendorInfo->pVersionNumber->pOctetString,
                    pVendorInfo->pVersionNumber->wOctetStringLength);
            }
        }
        if (pEndpointType->bIsTerminal)
        {
            UserInfo.h323_uu_pdu.h323_message_body.u.connect.destinationInfo.bit_mask |=
                terminal_present;
            UserInfo.h323_uu_pdu.h323_message_body.u.connect.destinationInfo.terminal.bit_mask = 0;
        }
        if (pEndpointType->bIsGateway)
        {
            UserInfo.h323_uu_pdu.h323_message_body.u.connect.destinationInfo.bit_mask |=
                gateway_present;
            UserInfo.h323_uu_pdu.h323_message_body.u.connect.destinationInfo.gateway.bit_mask = protocol_present;
            UserInfo.h323_uu_pdu.h323_message_body.u.connect.destinationInfo.gateway.protocol = &TempProtocol;
        }
    }

    UserInfo.h323_uu_pdu.h323_message_body.u.connect.destinationInfo.mc = 0;
    UserInfo.h323_uu_pdu.h323_message_body.u.connect.destinationInfo.undefinedNode = 0;

    if (pConferenceID != NULL)
    {
        UserInfo.h323_uu_pdu.h323_message_body.u.connect.conferenceID.length =
            sizeof(UserInfo.h323_uu_pdu.h323_message_body.u.connect.conferenceID.value);
        memcpy(UserInfo.h323_uu_pdu.h323_message_body.u.connect.conferenceID.value,
            pConferenceID->buffer,
            UserInfo.h323_uu_pdu.h323_message_body.u.connect.conferenceID.length);
    }

    rc = Q931_Encode(pWorld,
                     (void *) &UserInfo,
                     H323_UserInformation_PDU,
                     ppEncodedBuf,
                     pdwEncodedLength);

    if (ASN1_FAILED(rc))
    {
        ASSERT(FALSE);
        return CS_SUBSYSTEM_FAILURE;
    }

    return CS_OK;
}

//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
HRESULT
Q931AlertingEncodeASN(
    PCC_NONSTANDARDDATA pNonStandardData,
    CC_ADDR *h245Addr,
    PCC_ENDPOINTTYPE pEndpointType,
    ASN1_CODER_INFO *pWorld,
    BYTE **ppEncodedBuf,
    DWORD *pdwEncodedLength)
{
    int rc;
    H323_UserInformation UserInfo;

    *ppEncodedBuf = NULL;
    *pdwEncodedLength = 0;

    memset(&UserInfo, 0, sizeof(H323_UserInformation));
    UserInfo.bit_mask = 0;

    // make sure the user_data_present flag is turned off.
    UserInfo.bit_mask &= (~user_data_present);

    UserInfo.h323_uu_pdu.bit_mask = 0;

    if (pNonStandardData)
    {
        UserInfo.h323_uu_pdu.bit_mask |= H323_UU_PDU_nnStndrdDt_present;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.choice = h221NonStandard_chosen;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35CountryCode =
            pNonStandardData->bCountryCode;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35Extension =
            pNonStandardData->bExtension;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.manufacturerCode =
            pNonStandardData->wManufacturerCode;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.length =
            pNonStandardData->sData.wOctetStringLength;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.value =
            pNonStandardData->sData.pOctetString;
    }
    else
    {
        UserInfo.h323_uu_pdu.bit_mask &= (~H323_UU_PDU_nnStndrdDt_present);
    }

    UserInfo.h323_uu_pdu.h323_message_body.choice = alerting_chosen;

    UserInfo.h323_uu_pdu.h323_message_body.u.alerting.protocolIdentifier = &ProtocolId1;

    UserInfo.h323_uu_pdu.h323_message_body.u.alerting.destinationInfo.bit_mask = 0;
    if (pEndpointType)
    {
        PCC_VENDORINFO pVendorInfo = pEndpointType->pVendorInfo;
        if (pVendorInfo)
        {
            UserInfo.h323_uu_pdu.h323_message_body.u.alerting.destinationInfo.bit_mask |= vendor_present;
            UserInfo.h323_uu_pdu.h323_message_body.u.alerting.destinationInfo.vendor.bit_mask = 0;
            UserInfo.h323_uu_pdu.h323_message_body.u.alerting.destinationInfo.vendor.vendor.t35CountryCode = pVendorInfo->bCountryCode;
            UserInfo.h323_uu_pdu.h323_message_body.u.alerting.destinationInfo.vendor.vendor.t35Extension = pVendorInfo->bExtension;
            UserInfo.h323_uu_pdu.h323_message_body.u.alerting.destinationInfo.vendor.vendor.manufacturerCode = pVendorInfo->wManufacturerCode;

            if (pVendorInfo->pProductNumber && pVendorInfo->pProductNumber->pOctetString &&
                    pVendorInfo->pProductNumber->wOctetStringLength)
            {
                UserInfo.h323_uu_pdu.h323_message_body.u.alerting.destinationInfo.vendor.bit_mask |= productId_present;
                UserInfo.h323_uu_pdu.h323_message_body.u.alerting.destinationInfo.vendor.productId.length =
                    pVendorInfo->pProductNumber->wOctetStringLength;
                memcpy(UserInfo.h323_uu_pdu.h323_message_body.u.alerting.destinationInfo.vendor.productId.value,
                    pVendorInfo->pProductNumber->pOctetString,
                    pVendorInfo->pProductNumber->wOctetStringLength);
            }
            if (pVendorInfo->pVersionNumber && pVendorInfo->pVersionNumber->pOctetString &&
                    pVendorInfo->pVersionNumber->wOctetStringLength)
            {
                UserInfo.h323_uu_pdu.h323_message_body.u.alerting.destinationInfo.vendor.bit_mask |= versionId_present;
                UserInfo.h323_uu_pdu.h323_message_body.u.alerting.destinationInfo.vendor.versionId.length =
                    pVendorInfo->pVersionNumber->wOctetStringLength;
                memcpy(UserInfo.h323_uu_pdu.h323_message_body.u.alerting.destinationInfo.vendor.versionId.value,
                    pVendorInfo->pVersionNumber->pOctetString,
                    pVendorInfo->pVersionNumber->wOctetStringLength);
            }
        }
        if (pEndpointType->bIsTerminal)
        {
            UserInfo.h323_uu_pdu.h323_message_body.u.alerting.destinationInfo.bit_mask =
                terminal_present;
            UserInfo.h323_uu_pdu.h323_message_body.u.alerting.destinationInfo.terminal.bit_mask = 0;
        }
        if (pEndpointType->bIsGateway)
        {
            UserInfo.h323_uu_pdu.h323_message_body.u.alerting.destinationInfo.bit_mask =
                gateway_present;
            UserInfo.h323_uu_pdu.h323_message_body.u.alerting.destinationInfo.gateway.bit_mask = protocol_present;
            UserInfo.h323_uu_pdu.h323_message_body.u.alerting.destinationInfo.gateway.protocol = &TempProtocol;
        }
    }

    UserInfo.h323_uu_pdu.h323_message_body.u.alerting.destinationInfo.mc = 0;
    UserInfo.h323_uu_pdu.h323_message_body.u.alerting.destinationInfo.undefinedNode = 0;

    if (h245Addr != NULL)
    {
        DWORD a = h245Addr->Addr.IP_Binary.dwAddr;
        UserInfo.h323_uu_pdu.h323_message_body.u.alerting.CPg_UUIE_h245Addrss.choice = ipAddress_chosen;
        UserInfo.h323_uu_pdu.h323_message_body.u.alerting.CPg_UUIE_h245Addrss.u.ipAddress.ip.length = 4;
        UserInfo.h323_uu_pdu.h323_message_body.u.alerting.CPg_UUIE_h245Addrss.u.ipAddress.port =
            h245Addr->Addr.IP_Binary.wPort;
        UserInfo.h323_uu_pdu.h323_message_body.u.alerting.CPg_UUIE_h245Addrss.u.ipAddress.ip.value[0] =
            ((BYTE *)&a)[3];
        UserInfo.h323_uu_pdu.h323_message_body.u.alerting.CPg_UUIE_h245Addrss.u.ipAddress.ip.value[1] =
            ((BYTE *)&a)[2];
        UserInfo.h323_uu_pdu.h323_message_body.u.alerting.CPg_UUIE_h245Addrss.u.ipAddress.ip.value[2] =
            ((BYTE *)&a)[1];
        UserInfo.h323_uu_pdu.h323_message_body.u.alerting.CPg_UUIE_h245Addrss.u.ipAddress.ip.value[3] =
            ((BYTE *)&a)[0];
        UserInfo.h323_uu_pdu.h323_message_body.u.alerting.bit_mask |=
            (CPg_UUIE_h245Addrss_present);
    }
    else
    {
        UserInfo.h323_uu_pdu.h323_message_body.u.alerting.bit_mask &=
            (~CPg_UUIE_h245Addrss_present);
    }

    rc = Q931_Encode(pWorld,
                     (void *) &UserInfo,
                     H323_UserInformation_PDU,
                     ppEncodedBuf,
                     pdwEncodedLength);

    if (ASN1_FAILED(rc))
    {
        ASSERT(FALSE);
        return CS_SUBSYSTEM_FAILURE;
    }

    return CS_OK;
}

//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
HRESULT
Q931ProceedingEncodeASN(
    PCC_NONSTANDARDDATA pNonStandardData,
    CC_ADDR *h245Addr,
    PCC_ENDPOINTTYPE pEndpointType,
    ASN1_CODER_INFO *pWorld,
    BYTE **ppEncodedBuf,
    DWORD *pdwEncodedLength)
{
    int rc;
    H323_UserInformation UserInfo;

    *ppEncodedBuf = NULL;
    *pdwEncodedLength = 0;

    memset(&UserInfo, 0, sizeof(H323_UserInformation));
    UserInfo.bit_mask = 0;

    // make sure the user_data_present flag is turned off.
    UserInfo.bit_mask &= (~user_data_present);

    UserInfo.h323_uu_pdu.bit_mask = 0;

    if (pNonStandardData)
    {
        UserInfo.h323_uu_pdu.bit_mask |= H323_UU_PDU_nnStndrdDt_present;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.choice = h221NonStandard_chosen;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35CountryCode =
            pNonStandardData->bCountryCode;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35Extension =
            pNonStandardData->bExtension;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.manufacturerCode =
            pNonStandardData->wManufacturerCode;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.length =
            pNonStandardData->sData.wOctetStringLength;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.value =
            pNonStandardData->sData.pOctetString;
    }
    else
    {
        UserInfo.h323_uu_pdu.bit_mask &= (~H323_UU_PDU_nnStndrdDt_present);
    }

    UserInfo.h323_uu_pdu.h323_message_body.choice = callProceeding_chosen;

    UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.protocolIdentifier = &ProtocolId1;

    if (h245Addr != NULL)
    {
        DWORD a = h245Addr->Addr.IP_Binary.dwAddr;
        UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.CPg_UUIE_h245Addrss.choice = ipAddress_chosen;
        UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.CPg_UUIE_h245Addrss.u.ipAddress.ip.length = 4;
        UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.CPg_UUIE_h245Addrss.u.ipAddress.port =
            h245Addr->Addr.IP_Binary.wPort;
        UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.CPg_UUIE_h245Addrss.u.ipAddress.ip.value[0] =
            ((BYTE *)&a)[3];
        UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.CPg_UUIE_h245Addrss.u.ipAddress.ip.value[1] =
            ((BYTE *)&a)[2];
        UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.CPg_UUIE_h245Addrss.u.ipAddress.ip.value[2] =
            ((BYTE *)&a)[1];
        UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.CPg_UUIE_h245Addrss.u.ipAddress.ip.value[3] =
            ((BYTE *)&a)[0];
        UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.bit_mask |=
            (CPg_UUIE_h245Addrss_present);
    }
    else
    {
        UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.bit_mask &=
            (~CPg_UUIE_h245Addrss_present);
    }

    UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.destinationInfo.bit_mask = 0;
    if (pEndpointType)
    {
        PCC_VENDORINFO pVendorInfo = pEndpointType->pVendorInfo;
        if (pVendorInfo)
        {
            UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.destinationInfo.bit_mask |= vendor_present;
            UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.destinationInfo.vendor.bit_mask = 0;
            UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.destinationInfo.vendor.vendor.t35CountryCode = pVendorInfo->bCountryCode;
            UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.destinationInfo.vendor.vendor.t35Extension = pVendorInfo->bExtension;
            UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.destinationInfo.vendor.vendor.manufacturerCode = pVendorInfo->wManufacturerCode;

            if (pVendorInfo->pProductNumber && pVendorInfo->pProductNumber->pOctetString &&
                pVendorInfo->pProductNumber->wOctetStringLength)
            {
                UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.destinationInfo.vendor.bit_mask |= productId_present;
                UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.destinationInfo.vendor.productId.length =
                    pVendorInfo->pProductNumber->wOctetStringLength;
                memcpy(UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.destinationInfo.vendor.productId.value,
                    pVendorInfo->pProductNumber->pOctetString,
                    pVendorInfo->pProductNumber->wOctetStringLength);
            }
            if (pVendorInfo->pVersionNumber && pVendorInfo->pVersionNumber->pOctetString &&
                    pVendorInfo->pVersionNumber->wOctetStringLength)
            {
                UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.destinationInfo.vendor.bit_mask |= versionId_present;
                UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.destinationInfo.vendor.versionId.length =
                    pVendorInfo->pVersionNumber->wOctetStringLength;
                memcpy(UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.destinationInfo.vendor.versionId.value,
                    pVendorInfo->pVersionNumber->pOctetString,
                    pVendorInfo->pVersionNumber->wOctetStringLength);
            }
        }
        if (pEndpointType->bIsTerminal)
        {
            UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.destinationInfo.bit_mask =
                terminal_present;
            UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.destinationInfo.terminal.bit_mask = 0;
        }
        if (pEndpointType->bIsGateway)
        {
            UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.destinationInfo.bit_mask =
                gateway_present;
            UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.destinationInfo.gateway.bit_mask = protocol_present;
            UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.destinationInfo.gateway.protocol = &TempProtocol;
        }
    }

    UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.destinationInfo.mc = 0;
    UserInfo.h323_uu_pdu.h323_message_body.u.callProceeding.destinationInfo.undefinedNode = 0;

    rc = Q931_Encode(pWorld,
                     (void *) &UserInfo,
                     H323_UserInformation_PDU,
                     ppEncodedBuf,
                     pdwEncodedLength);

    if (ASN1_FAILED(rc))
    {
        ASSERT(FALSE);
        return CS_SUBSYSTEM_FAILURE;
    }

    return CS_OK;
}

//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
HRESULT
Q931FacilityEncodeASN(
    PCC_NONSTANDARDDATA pNonStandardData,
    CC_ADDR *AlternativeAddr,
    BYTE bReason,
    CC_CONFERENCEID *pConferenceID,
    PCC_ALIASNAMES pAlternativeAliasList,
    ASN1_CODER_INFO *pWorld,
    BYTE **ppEncodedBuf,
    DWORD *pdwEncodedLength)
{
    int rc;
    H323_UserInformation UserInfo;

    *ppEncodedBuf = NULL;
    *pdwEncodedLength = 0;

    memset(&UserInfo, 0, sizeof(H323_UserInformation));

    UserInfo.bit_mask = 0;

    // make sure the user_data_present flag is turned off.
    UserInfo.bit_mask &= (~user_data_present);

    if (pNonStandardData)
    {
        UserInfo.h323_uu_pdu.bit_mask |= H323_UU_PDU_nnStndrdDt_present;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.choice = h221NonStandard_chosen;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35CountryCode =
            pNonStandardData->bCountryCode;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35Extension =
            pNonStandardData->bExtension;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.manufacturerCode =
            pNonStandardData->wManufacturerCode;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.length =
            pNonStandardData->sData.wOctetStringLength;
        UserInfo.h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.value =
            pNonStandardData->sData.pOctetString;
    }
    else
    {
        UserInfo.h323_uu_pdu.bit_mask &= (~H323_UU_PDU_nnStndrdDt_present);
    }

    UserInfo.h323_uu_pdu.h323_message_body.choice = facility_chosen;

    UserInfo.h323_uu_pdu.h323_message_body.u.facility.protocolIdentifier = &ProtocolId1;

    if (AlternativeAddr != NULL)
    {
        DWORD a = AlternativeAddr->Addr.IP_Binary.dwAddr;
        UserInfo.h323_uu_pdu.h323_message_body.u.facility.alternativeAddress.choice = ipAddress_chosen;
        UserInfo.h323_uu_pdu.h323_message_body.u.facility.alternativeAddress.u.ipAddress.ip.length = 4;
        UserInfo.h323_uu_pdu.h323_message_body.u.facility.alternativeAddress.u.ipAddress.port =
            AlternativeAddr->Addr.IP_Binary.wPort;
        UserInfo.h323_uu_pdu.h323_message_body.u.facility.alternativeAddress.u.ipAddress.ip.value[0] =
            ((BYTE *)&a)[3];
        UserInfo.h323_uu_pdu.h323_message_body.u.facility.alternativeAddress.u.ipAddress.ip.value[1] =
            ((BYTE *)&a)[2];
        UserInfo.h323_uu_pdu.h323_message_body.u.facility.alternativeAddress.u.ipAddress.ip.value[2] =
            ((BYTE *)&a)[1];
        UserInfo.h323_uu_pdu.h323_message_body.u.facility.alternativeAddress.u.ipAddress.ip.value[3] =
            ((BYTE *)&a)[0];
        UserInfo.h323_uu_pdu.h323_message_body.u.facility.bit_mask |=
            (alternativeAddress_present);
    }
    else
    {
        UserInfo.h323_uu_pdu.h323_message_body.u.facility.bit_mask &=
            (~alternativeAddress_present);
    }

    if (pAlternativeAliasList)
    {
        CS_STATUS AliasResult = CS_OK;
        AliasResult = AliasToSeqof((struct _seqof3 **)&(UserInfo.h323_uu_pdu.
            h323_message_body.u.facility.alternativeAliasAddress), pAlternativeAliasList);
        if (AliasResult != CS_OK)
        {
            return CS_NO_MEMORY;
        }
        UserInfo.h323_uu_pdu.h323_message_body.u.facility.bit_mask |=
            (alternativeAliasAddress_present);
    }
    else
    {
        UserInfo.h323_uu_pdu.h323_message_body.u.facility.bit_mask &=
            (~alternativeAliasAddress_present);
    }

    if (pConferenceID != NULL)
    {
        UserInfo.h323_uu_pdu.h323_message_body.u.facility.conferenceID.length =
            sizeof(UserInfo.h323_uu_pdu.h323_message_body.u.facility.conferenceID.value);
        memcpy(UserInfo.h323_uu_pdu.h323_message_body.u.facility.conferenceID.value,
            pConferenceID->buffer,
            UserInfo.h323_uu_pdu.h323_message_body.u.facility.conferenceID.length);
        UserInfo.h323_uu_pdu.h323_message_body.u.facility.bit_mask |=
            (conferenceID_present);
    }
    else
    {
        UserInfo.h323_uu_pdu.h323_message_body.u.facility.bit_mask &=
            (~conferenceID_present);
    }

    switch (bReason)
	{
	case CC_REJECT_ROUTE_TO_GATEKEEPER:
		UserInfo.h323_uu_pdu.h323_message_body.u.facility.reason.choice = routeCallToGatekeeper_chosen;
		break;
	case CC_REJECT_CALL_FORWARDED:
		UserInfo.h323_uu_pdu.h323_message_body.u.facility.reason.choice = callForwarded_chosen;
		break;
	case CC_REJECT_ROUTE_TO_MC:
		UserInfo.h323_uu_pdu.h323_message_body.u.facility.reason.choice = routeCallToMC_chosen;
		break;
	default:
        UserInfo.h323_uu_pdu.h323_message_body.u.facility.reason.choice = RlsCmpltRsn_undfndRsn_chosen;
	} // switch

    rc = Q931_Encode(pWorld,
                     (void *) &UserInfo,
                     H323_UserInformation_PDU,
                     ppEncodedBuf,
                     pdwEncodedLength);

    // Free the alias name structures from the UserInfo area.
    FreeSeqof((struct _seqof3 *)UserInfo.h323_uu_pdu.h323_message_body.u.
        facility.alternativeAliasAddress);

    if (ASN1_FAILED(rc))
    {
        ASSERT(FALSE);
        return CS_SUBSYSTEM_FAILURE;
    }
    
    return CS_OK;
}

//------------------------------------------------------------------------
//------------------------------------------------------------------------
BOOL
Q931ValidPduVersion(struct ObjectID_ *id)
{
// not sure what version checking to put here
#if 0
    if ((id != NULL) && (id->value == 0) && (id->next != NULL) && (id->next->value <= 1))
    {
        return TRUE;
    }
    return FALSE;
#else
    return TRUE;
#endif
}

//------------------------------------------------------------------------
//------------------------------------------------------------------------
HRESULT
Q931SetupParseASN(
    ASN1_CODER_INFO *pWorld,
    BYTE *pEncodedBuf,
    DWORD dwEncodedLength,
    Q931_SETUP_ASN *pParsedData)
{
    int PDU = H323_UserInformation_PDU;
    char *pDecodedBuf = NULL;
    H323_UserInformation *pUserInfo;
    struct ObjectID_ *id;
    int Result;

    if (pParsedData == NULL)
    {
        return CS_BAD_PARAM;
    }

    Result = Q931_Decode(pWorld,
                         (void **) &pDecodedBuf,
                         PDU,
                         pEncodedBuf,
                         dwEncodedLength);

    if (ASN1_FAILED(Result) || (pDecodedBuf == NULL))
    {
        ASSERT(FALSE);
        // trace and return an decoding error of some sort.
        // Note: some values of Result should cause CS_SUBSYSTEM_FAILURE return.
        return CS_BAD_PARAM;
    }

    // validate some basic things about the PDU...
    pUserInfo = (H323_UserInformation *)pDecodedBuf;

    // validate that this is a H323 PDU.
    if (PDU != H323_UserInformation_PDU)
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_BAD_PARAM;
    }

    // validate that the PDU user-data uses ASN encoding.
    if (((pUserInfo->bit_mask & user_data_present) != 0) &&
            (pUserInfo->user_data.protocol_discriminator != USE_ASN1_ENCODING))
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_BAD_PARAM;
    }

    // validate that the PDU is H323 Setup information.
    if (pUserInfo->h323_uu_pdu.h323_message_body.choice != setup_chosen)
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_BAD_PARAM;
    }

    id = pUserInfo->h323_uu_pdu.h323_message_body.u.setup.protocolIdentifier;
    if (!Q931ValidPduVersion(id))
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_INCOMPATIBLE_VERSION;
    }

    // make sure that the conference id is formed correctly.
    if (pUserInfo->h323_uu_pdu.h323_message_body.u.setup.conferenceID.length >
            sizeof(pUserInfo->h323_uu_pdu.h323_message_body.u.setup.conferenceID.value))
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_BAD_PARAM;
    }

#if 0
    if (pUserInfo->h323_uu_pdu.h323_message_body.u.setup.conferenceGoal.choice != create_chosen)
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_OPTION_NOT_IMPLEMENTED;
    }
    if (pUserInfo->h323_uu_pdu.h323_message_body.u.setup.callType.choice != pointToPoint_chosen)
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_OPTION_NOT_IMPLEMENTED;
    }
#endif

    // parse the message contained in pUserInfo.
    memset(pParsedData, 0, sizeof(Q931_SETUP_ASN));
    pParsedData->SourceAddr.bMulticast = FALSE;
    pParsedData->CallerAddr.bMulticast = FALSE;
    pParsedData->CalleeDestAddr.bMulticast = FALSE;
    pParsedData->CalleeAddr.bMulticast = FALSE;

    // no validation of sourceInfo needed.

    pParsedData->EndpointType.pVendorInfo = NULL;
    if (pUserInfo->h323_uu_pdu.h323_message_body.u.setup.sourceInfo.bit_mask & (vendor_present))
    {
        pParsedData->EndpointType.pVendorInfo = &(pParsedData->VendorInfo);
        pParsedData->VendorInfo.bCountryCode =
            (BYTE)pUserInfo->h323_uu_pdu.h323_message_body.u.setup.sourceInfo.vendor.vendor.t35CountryCode;
        pParsedData->VendorInfo.bExtension =
            (BYTE)pUserInfo->h323_uu_pdu.h323_message_body.u.setup.sourceInfo.vendor.vendor.t35Extension;
        pParsedData->VendorInfo.wManufacturerCode =
            pUserInfo->h323_uu_pdu.h323_message_body.u.setup.sourceInfo.vendor.vendor.manufacturerCode;
        if (pUserInfo->h323_uu_pdu.h323_message_body.u.setup.sourceInfo.vendor.bit_mask & (productId_present))
        {
            pParsedData->VendorInfo.pProductNumber = Malloc(sizeof(CC_OCTETSTRING));
            if (pParsedData->VendorInfo.pProductNumber == NULL)
            {
                freePDU(pWorld, PDU, pDecodedBuf, q931asn);
                return CS_NO_MEMORY;
            }
            pParsedData->VendorInfo.pProductNumber->wOctetStringLength = (WORD)
                min(pUserInfo->h323_uu_pdu.h323_message_body.u.setup.sourceInfo.vendor.productId.length,
                CC_MAX_PRODUCT_LENGTH - 1);
            memcpy(pParsedData->bufProductValue,
                pUserInfo->h323_uu_pdu.h323_message_body.u.setup.sourceInfo.vendor.productId.value,
                pParsedData->VendorInfo.pProductNumber->wOctetStringLength);
            pParsedData->bufProductValue[pParsedData->VendorInfo.pProductNumber->wOctetStringLength] = '\0';
            pParsedData->VendorInfo.pProductNumber->pOctetString = pParsedData->bufProductValue;
        }
        if (pUserInfo->h323_uu_pdu.h323_message_body.u.setup.sourceInfo.vendor.bit_mask & (versionId_present))
        {
            pParsedData->VendorInfo.pVersionNumber = Malloc(sizeof(CC_OCTETSTRING));
            if (pParsedData->VendorInfo.pVersionNumber == NULL)
            {
                Free(pParsedData->VendorInfo.pProductNumber);
                freePDU(pWorld, PDU, pDecodedBuf, q931asn);
                return CS_NO_MEMORY;
            }
            pParsedData->VendorInfo.pVersionNumber->wOctetStringLength = (WORD)
                min(pUserInfo->h323_uu_pdu.h323_message_body.u.setup.sourceInfo.vendor.versionId.length,
                CC_MAX_VERSION_LENGTH - 1);
            memcpy(pParsedData->bufVersionValue,
                pUserInfo->h323_uu_pdu.h323_message_body.u.setup.sourceInfo.vendor.versionId.value,
                pParsedData->VendorInfo.pVersionNumber->wOctetStringLength);
            pParsedData->bufVersionValue[pParsedData->VendorInfo.pVersionNumber->wOctetStringLength] = '\0';
            pParsedData->VendorInfo.pVersionNumber->pOctetString = pParsedData->bufVersionValue;
        }
    }

    pParsedData->EndpointType.bIsTerminal = FALSE;
    if (pUserInfo->h323_uu_pdu.h323_message_body.u.setup.sourceInfo.bit_mask & (terminal_present))
    {
        pParsedData->EndpointType.bIsTerminal = TRUE;
    }
    pParsedData->EndpointType.bIsGateway = FALSE;
    if (pUserInfo->h323_uu_pdu.h323_message_body.u.setup.sourceInfo.bit_mask & (gateway_present))
    {
        pParsedData->EndpointType.bIsGateway = TRUE;
    }

    if ((pUserInfo->h323_uu_pdu.bit_mask & H323_UU_PDU_nnStndrdDt_present) != 0)
    {
        pParsedData->NonStandardDataPresent = TRUE;
        if (pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.choice ==
                h221NonStandard_chosen)
        {
            pParsedData->NonStandardData.bCountryCode =
                (BYTE)(pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35CountryCode);
            pParsedData->NonStandardData.bExtension =
                (BYTE)(pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35Extension);
            pParsedData->NonStandardData.wManufacturerCode =
                pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.manufacturerCode;
        }
        pParsedData->NonStandardData.sData.wOctetStringLength =	(WORD)
            pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.length;
		pParsedData->NonStandardData.sData.pOctetString =
			(BYTE *)Malloc(pParsedData->NonStandardData.sData.wOctetStringLength);
		if (pParsedData->NonStandardData.sData.pOctetString == NULL)
		{
            Free(pParsedData->VendorInfo.pProductNumber);
            Free(pParsedData->VendorInfo.pVersionNumber);
            freePDU(pWorld, PDU, pDecodedBuf, q931asn);
            return CS_NO_MEMORY;
		}
		memcpy(pParsedData->NonStandardData.sData.pOctetString,
			   pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.value,
			   pParsedData->NonStandardData.sData.wOctetStringLength);
    }
    else
    {
        pParsedData->NonStandardDataPresent = FALSE;
    }

//RMO. ignore the h245 address.

    {
        CS_STATUS AliasResult = CS_OK;

        // parse the sourceAddress aliases here...
        AliasResult = SeqofToAlias(&(pParsedData->pCallerAliasList),
            (struct _seqof3 *)pUserInfo->h323_uu_pdu.h323_message_body.u.setup.sourceAddress);
        if (AliasResult != CS_OK)
        {
  			if (pParsedData->NonStandardData.sData.pOctetString != NULL)
				Free(pParsedData->NonStandardData.sData.pOctetString);
			Free(pParsedData->VendorInfo.pProductNumber);
            Free(pParsedData->VendorInfo.pVersionNumber);
            freePDU(pWorld, PDU, pDecodedBuf, q931asn);
            return CS_NO_MEMORY;
        }

        // parse the destinationAddress aliases here...
        AliasResult = SeqofToAlias(&(pParsedData->pCalleeAliasList),
            (struct _seqof3 *)pUserInfo->h323_uu_pdu.h323_message_body.u.setup.destinationAddress);
        if (AliasResult != CS_OK)
        {
            Q931FreeAliasNames(pParsedData->pCallerAliasList);
   			if (pParsedData->NonStandardData.sData.pOctetString != NULL)
				Free(pParsedData->NonStandardData.sData.pOctetString);
            pParsedData->pCallerAliasList = NULL;
            Free(pParsedData->VendorInfo.pProductNumber);
            Free(pParsedData->VendorInfo.pVersionNumber);
            freePDU(pWorld, PDU, pDecodedBuf, q931asn);
            return CS_NO_MEMORY;
        }

        // parse the destExtraCallInfo aliases here...
        AliasResult = SeqofToAlias(&(pParsedData->pExtraAliasList),
            (struct _seqof3 *)pUserInfo->h323_uu_pdu.h323_message_body.u.setup.destExtraCallInfo);
        if (AliasResult != CS_OK)
        {
            Q931FreeAliasNames(pParsedData->pCalleeAliasList);
            Q931FreeAliasNames(pParsedData->pCallerAliasList);
  			if (pParsedData->NonStandardData.sData.pOctetString != NULL)
				Free(pParsedData->NonStandardData.sData.pOctetString);
            pParsedData->pCallerAliasList = NULL;
            Free(pParsedData->VendorInfo.pProductNumber);
            Free(pParsedData->VendorInfo.pVersionNumber);
            freePDU(pWorld, PDU, pDecodedBuf, q931asn);
            return CS_NO_MEMORY;
        }

        // parse the remoteExtensionAddress aliases here...
        if ((pUserInfo->h323_uu_pdu.h323_message_body.u.setup.bit_mask &
                remoteExtensionAddress_present) != 0)
        {
            AliasResult = Q931AliasAddrToAliasItem(&(pParsedData->pExtensionAliasItem),
                &(pUserInfo->h323_uu_pdu.h323_message_body.u.setup.remoteExtensionAddress));
            if (AliasResult != CS_OK)
            {
                Q931FreeAliasNames(pParsedData->pExtraAliasList);
                Q931FreeAliasNames(pParsedData->pCalleeAliasList);
                Q931FreeAliasNames(pParsedData->pCallerAliasList);
                pParsedData->pCallerAliasList = NULL;
  				if (pParsedData->NonStandardData.sData.pOctetString != NULL)
					Free(pParsedData->NonStandardData.sData.pOctetString);
                Free(pParsedData->VendorInfo.pProductNumber);
                Free(pParsedData->VendorInfo.pVersionNumber);
                freePDU(pWorld, PDU, pDecodedBuf, q931asn);
                return CS_NO_MEMORY;
            }
        }
    }

    if ((pUserInfo->h323_uu_pdu.h323_message_body.u.setup.bit_mask &
            destCallSignalAddress_present) != 0)
    {
        BYTE *a = (BYTE *)(&(pParsedData->CalleeDestAddr.Addr.IP_Binary.dwAddr));
        pParsedData->CalleeDestAddr.nAddrType = CC_IP_BINARY;
        pParsedData->CalleeDestAddr.Addr.IP_Binary.wPort = 
            pUserInfo->h323_uu_pdu.h323_message_body.u.setup.destCallSignalAddress.u.ipAddress.port;
        a[3] = pUserInfo->h323_uu_pdu.h323_message_body.u.setup.destCallSignalAddress.u.ipAddress.ip.value[0];
        a[2] = pUserInfo->h323_uu_pdu.h323_message_body.u.setup.destCallSignalAddress.u.ipAddress.ip.value[1];
        a[1] = pUserInfo->h323_uu_pdu.h323_message_body.u.setup.destCallSignalAddress.u.ipAddress.ip.value[2];
        a[0] = pUserInfo->h323_uu_pdu.h323_message_body.u.setup.destCallSignalAddress.u.ipAddress.ip.value[3];
        pParsedData->CalleeDestAddrPresent = TRUE;
    }

    if ((pUserInfo->h323_uu_pdu.h323_message_body.u.setup.bit_mask &
            sourceCallSignalAddress_present) != 0)
    {
        BYTE *a = (BYTE *)(&(pParsedData->SourceAddr.Addr.IP_Binary.dwAddr));
        pParsedData->SourceAddr.nAddrType = CC_IP_BINARY;
        pParsedData->SourceAddr.Addr.IP_Binary.wPort = 
            pUserInfo->h323_uu_pdu.h323_message_body.u.setup.sourceCallSignalAddress.u.ipAddress.port;
        a[3] = pUserInfo->h323_uu_pdu.h323_message_body.u.setup.sourceCallSignalAddress.u.ipAddress.ip.value[0];
        a[2] = pUserInfo->h323_uu_pdu.h323_message_body.u.setup.sourceCallSignalAddress.u.ipAddress.ip.value[1];
        a[1] = pUserInfo->h323_uu_pdu.h323_message_body.u.setup.sourceCallSignalAddress.u.ipAddress.ip.value[2];
        a[0] = pUserInfo->h323_uu_pdu.h323_message_body.u.setup.sourceCallSignalAddress.u.ipAddress.ip.value[3];
        pParsedData->SourceAddrPresent = TRUE;
    }

    pParsedData->bCallerIsMC = pUserInfo->h323_uu_pdu.h323_message_body.u.setup.activeMC;

    memcpy(pParsedData->ConferenceID.buffer,
        pUserInfo->h323_uu_pdu.h323_message_body.u.setup.conferenceID.value,
        pUserInfo->h323_uu_pdu.h323_message_body.u.setup.conferenceID.length);

    switch (pUserInfo->h323_uu_pdu.h323_message_body.u.setup.conferenceGoal.choice)
	{
	case invite_chosen:
		pParsedData->wGoal = CSG_INVITE;
		break;
	case join_chosen:
		pParsedData->wGoal = CSG_JOIN;
		break;
	default:
		pParsedData->wGoal = CSG_CREATE;
	} // switch

	switch (pUserInfo->h323_uu_pdu.h323_message_body.u.setup.callType.choice)
    {
	case oneToN_chosen:
        pParsedData->wCallType = CC_CALLTYPE_1_N;
		break;
	case nToOne_chosen:
        pParsedData->wCallType = CC_CALLTYPE_N_1;
		break;
	case nToN_chosen:
        pParsedData->wCallType = CC_CALLTYPE_N_N;
		break;
	default:
        pParsedData->wCallType = CC_CALLTYPE_PT_PT;
    } // switch

    // Free the PDU data.
    Result = freePDU(pWorld, PDU, pDecodedBuf, q931asn);
    ASSERT(ASN1_SUCCEEDED(Result));
    return CS_OK;
}

//------------------------------------------------------------------------
//------------------------------------------------------------------------
HRESULT
Q931ReleaseCompleteParseASN(
    ASN1_CODER_INFO *pWorld,
    BYTE *pEncodedBuf,
    DWORD dwEncodedLength,
    Q931_RELEASE_COMPLETE_ASN *pParsedData)
{
    int PDU = H323_UserInformation_PDU;
    char *pDecodedBuf = NULL;
    H323_UserInformation *pUserInfo;
    struct ObjectID_ *id;
    int Result;

    if (pParsedData == NULL)
    {
        return CS_BAD_PARAM;
    }

    Result = Q931_Decode(pWorld,
                         (void **) &pDecodedBuf,
                         PDU,
                         pEncodedBuf,
                         dwEncodedLength);

    if (ASN1_FAILED(Result) || (pDecodedBuf == NULL))
    {
        ASSERT(FALSE);
        // trace and return an decoding error of some sort.
        // Note: some values of Result should cause CS_SUBSYSTEM_FAILURE return.
        return CS_BAD_PARAM;
    }

    // validate some basic things about the PDU...
    pUserInfo = (H323_UserInformation *)pDecodedBuf;

    // validate that this is a H323 PDU.
    if (PDU != H323_UserInformation_PDU)
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_BAD_PARAM;
    }

    // validate that the PDU user-data uses ASN encoding.
    if (((pUserInfo->bit_mask & user_data_present) != 0) &&
            (pUserInfo->user_data.protocol_discriminator != USE_ASN1_ENCODING))
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_BAD_PARAM;
    }

    // validate that the PDU is H323 Release Complete information.
    if (pUserInfo->h323_uu_pdu.h323_message_body.choice != releaseComplete_chosen)
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_BAD_PARAM;
    }

    id = pUserInfo->h323_uu_pdu.h323_message_body.u.releaseComplete.protocolIdentifier;
    if (!Q931ValidPduVersion(id))
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_INCOMPATIBLE_VERSION;
    }

    // parse the message contained in pUserInfo.
    memset(pParsedData, 0, sizeof(Q931_RELEASE_COMPLETE_ASN));

    if ((pUserInfo->h323_uu_pdu.bit_mask & H323_UU_PDU_nnStndrdDt_present) != 0)
    {
        pParsedData->NonStandardDataPresent = TRUE;
        if (pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.choice ==
                h221NonStandard_chosen)
        {
            pParsedData->NonStandardData.bCountryCode =
                (BYTE)(pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35CountryCode);
            pParsedData->NonStandardData.bExtension =
                (BYTE)(pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35Extension);
            pParsedData->NonStandardData.wManufacturerCode =
                pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.manufacturerCode;
        }
        pParsedData->NonStandardData.sData.wOctetStringLength =	(WORD)
            pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.length;
		pParsedData->NonStandardData.sData.pOctetString =
			(BYTE *)Malloc(pParsedData->NonStandardData.sData.wOctetStringLength);
		if (pParsedData->NonStandardData.sData.pOctetString == NULL)
		{
            freePDU(pWorld, PDU, pDecodedBuf, q931asn);
            return CS_NO_MEMORY;
		}
		memcpy(pParsedData->NonStandardData.sData.pOctetString,
			   pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.value,
			   pParsedData->NonStandardData.sData.wOctetStringLength);    }
    else
    {
        pParsedData->NonStandardDataPresent = FALSE;
    }

    if (pUserInfo->h323_uu_pdu.h323_message_body.u.releaseComplete.bit_mask & reason_present)
    {
        switch (pUserInfo->h323_uu_pdu.h323_message_body.u.releaseComplete.reason.choice)
		{
        case noBandwidth_chosen:
			pParsedData->bReason = CC_REJECT_NO_BANDWIDTH;
			break;
        case gatekeeperResources_chosen:
			pParsedData->bReason = CC_REJECT_GATEKEEPER_RESOURCES;
			break;
        case unreachableDestination_chosen:
			pParsedData->bReason = CC_REJECT_UNREACHABLE_DESTINATION;
			break;
        case destinationRejection_chosen:
			pParsedData->bReason = CC_REJECT_DESTINATION_REJECTION;
			break;
        case invalidRevision_chosen:
			pParsedData->bReason = CC_REJECT_INVALID_REVISION;
			break;
        case noPermission_chosen:
			pParsedData->bReason = CC_REJECT_NO_PERMISSION;
			break;
        case unreachableGatekeeper_chosen:
			pParsedData->bReason = CC_REJECT_UNREACHABLE_GATEKEEPER;
			break;
        case gatewayResources_chosen:
			pParsedData->bReason = CC_REJECT_GATEWAY_RESOURCES;
			break;
        case badFormatAddress_chosen:
			pParsedData->bReason = CC_REJECT_BAD_FORMAT_ADDRESS;
			break;
        case adaptiveBusy_chosen:
			pParsedData->bReason = CC_REJECT_ADAPTIVE_BUSY;
			break;
        case inConf_chosen:
			pParsedData->bReason = CC_REJECT_IN_CONF;
			break;
        case facilityCallDeflection_chosen:
			pParsedData->bReason = CC_REJECT_CALL_DEFLECTION;
			break;
		default:
            pParsedData->bReason = CC_REJECT_UNDEFINED_REASON;
		} // switch
    }
	else
	{
		pParsedData->bReason = CC_REJECT_UNDEFINED_REASON;
	}

    // Free the PDU data.
    Result = freePDU(pWorld, PDU, pDecodedBuf, q931asn);
    ASSERT(ASN1_SUCCEEDED(Result));
    return CS_OK;
}

//------------------------------------------------------------------------
//------------------------------------------------------------------------
HRESULT
Q931ConnectParseASN(
    ASN1_CODER_INFO *pWorld,
    BYTE *pEncodedBuf,
    DWORD dwEncodedLength,
    Q931_CONNECT_ASN *pParsedData)
{
    int PDU = H323_UserInformation_PDU;
    char *pDecodedBuf = NULL;
    H323_UserInformation *pUserInfo;
    struct ObjectID_ *id;
    int Result;

    if (pParsedData == NULL)
    {
        return CS_BAD_PARAM;
    }

    Result = Q931_Decode(pWorld,
                         (void **) &pDecodedBuf,
                         PDU,
                         pEncodedBuf,
                         dwEncodedLength);

    if (ASN1_FAILED(Result) || (pDecodedBuf == NULL))
    {
        ASSERT(FALSE);
        // trace and return an decoding error of some sort.
        // Note: some values of Result should cause CS_SUBSYSTEM_FAILURE return.
        return CS_BAD_PARAM;
    }

    // validate some basic things about the PDU...
    pUserInfo = (H323_UserInformation *)pDecodedBuf;

    // validate that this is a H323 PDU.
    if (PDU != H323_UserInformation_PDU)
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_BAD_PARAM;
    }

    // validate that the PDU user-data uses ASN encoding.
    if (((pUserInfo->bit_mask & user_data_present) != 0) &&
            (pUserInfo->user_data.protocol_discriminator != USE_ASN1_ENCODING))
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_BAD_PARAM;
    }

    // validate that the PDU is H323 Connect information.
    if (pUserInfo->h323_uu_pdu.h323_message_body.choice != connect_chosen)
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_BAD_PARAM;
    }

    id = pUserInfo->h323_uu_pdu.h323_message_body.u.connect.protocolIdentifier;
    if (!Q931ValidPduVersion(id))
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_INCOMPATIBLE_VERSION;
    }

    // make sure that the conference id is formed correctly.
    if (pUserInfo->h323_uu_pdu.h323_message_body.u.connect.conferenceID.length >
            sizeof(pUserInfo->h323_uu_pdu.h323_message_body.u.connect.conferenceID.value))
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_BAD_PARAM;
    }

    // parse the message contained in pUserInfo.
    memset(pParsedData, 0, sizeof(Q931_CONNECT_ASN));
    pParsedData->h245Addr.bMulticast = FALSE;

    if ((pUserInfo->h323_uu_pdu.bit_mask & H323_UU_PDU_nnStndrdDt_present) != 0)
    {
        pParsedData->NonStandardDataPresent = TRUE;
        if (pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.choice ==
                h221NonStandard_chosen)
        {
            pParsedData->NonStandardData.bCountryCode =
                (BYTE)(pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35CountryCode);
            pParsedData->NonStandardData.bExtension =
                (BYTE)(pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35Extension);
            pParsedData->NonStandardData.wManufacturerCode =
                pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.manufacturerCode;
        }
        pParsedData->NonStandardData.sData.wOctetStringLength = (WORD)
            pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.length;
		pParsedData->NonStandardData.sData.pOctetString =
			(BYTE *)Malloc(pParsedData->NonStandardData.sData.wOctetStringLength);
		if (pParsedData->NonStandardData.sData.pOctetString == NULL)
		{
            freePDU(pWorld, PDU, pDecodedBuf, q931asn);
            return CS_NO_MEMORY;
		}
		memcpy(pParsedData->NonStandardData.sData.pOctetString,
			   pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.value,
			   pParsedData->NonStandardData.sData.wOctetStringLength);
    }
    else
    {
        pParsedData->NonStandardDataPresent = FALSE;
    }

    if ((pUserInfo->h323_uu_pdu.h323_message_body.u.connect.bit_mask &
            Cnnct_UUIE_h245Address_present) != 0)
    {
        BYTE *a = (BYTE *)(&(pParsedData->h245Addr.Addr.IP_Binary.dwAddr));
        pParsedData->h245Addr.nAddrType = CC_IP_BINARY;
        pParsedData->h245Addr.Addr.IP_Binary.wPort = 
            pUserInfo->h323_uu_pdu.h323_message_body.u.connect.Cnnct_UUIE_h245Address.u.ipAddress.port;
        a[3] = pUserInfo->h323_uu_pdu.h323_message_body.u.connect.Cnnct_UUIE_h245Address.u.ipAddress.ip.value[0];
        a[2] = pUserInfo->h323_uu_pdu.h323_message_body.u.connect.Cnnct_UUIE_h245Address.u.ipAddress.ip.value[1];
        a[1] = pUserInfo->h323_uu_pdu.h323_message_body.u.connect.Cnnct_UUIE_h245Address.u.ipAddress.ip.value[2];
        a[0] = pUserInfo->h323_uu_pdu.h323_message_body.u.connect.Cnnct_UUIE_h245Address.u.ipAddress.ip.value[3];
        pParsedData->h245AddrPresent = TRUE;
    }
    else
    {
        pParsedData->h245AddrPresent = FALSE;
    }

    // no validation of destinationInfo needed.

    pParsedData->EndpointType.pVendorInfo = NULL;
    if (pUserInfo->h323_uu_pdu.h323_message_body.u.connect.destinationInfo.bit_mask & (vendor_present))
    {
        pParsedData->EndpointType.pVendorInfo = &(pParsedData->VendorInfo);
        pParsedData->VendorInfo.bCountryCode =
            (BYTE)pUserInfo->h323_uu_pdu.h323_message_body.u.connect.destinationInfo.vendor.vendor.t35CountryCode;
        pParsedData->VendorInfo.bExtension =
            (BYTE)pUserInfo->h323_uu_pdu.h323_message_body.u.connect.destinationInfo.vendor.vendor.t35Extension;
        pParsedData->VendorInfo.wManufacturerCode =
            pUserInfo->h323_uu_pdu.h323_message_body.u.connect.destinationInfo.vendor.vendor.manufacturerCode;

        if (pUserInfo->h323_uu_pdu.h323_message_body.u.connect.destinationInfo.vendor.bit_mask & (productId_present))
        {
            pParsedData->VendorInfo.pProductNumber = Malloc(sizeof(CC_OCTETSTRING));
            if (pParsedData->VendorInfo.pProductNumber == NULL)
            {
				if (pParsedData->NonStandardData.sData.pOctetString != NULL)
					Free(pParsedData->NonStandardData.sData.pOctetString);
                freePDU(pWorld, PDU, pDecodedBuf, q931asn);
                return CS_NO_MEMORY;
            }
            pParsedData->VendorInfo.pProductNumber->wOctetStringLength = (WORD)
                min(pUserInfo->h323_uu_pdu.h323_message_body.u.connect.destinationInfo.vendor.productId.length,
                CC_MAX_PRODUCT_LENGTH - 1);
            memcpy(pParsedData->bufProductValue,
                pUserInfo->h323_uu_pdu.h323_message_body.u.connect.destinationInfo.vendor.productId.value,
                pParsedData->VendorInfo.pProductNumber->wOctetStringLength);
            pParsedData->bufProductValue[pParsedData->VendorInfo.pProductNumber->wOctetStringLength] = '\0';
            pParsedData->VendorInfo.pProductNumber->pOctetString = pParsedData->bufProductValue;
        }
        if (pUserInfo->h323_uu_pdu.h323_message_body.u.connect.destinationInfo.vendor.bit_mask & (versionId_present))
        {
            pParsedData->VendorInfo.pVersionNumber = Malloc(sizeof(CC_OCTETSTRING));
            if (pParsedData->VendorInfo.pVersionNumber == NULL)
            {
				if (pParsedData->NonStandardData.sData.pOctetString != NULL)
					Free(pParsedData->NonStandardData.sData.pOctetString);
                Free(pParsedData->VendorInfo.pProductNumber);
                freePDU(pWorld, PDU, pDecodedBuf, q931asn);
                return CS_NO_MEMORY;
            }
            pParsedData->VendorInfo.pVersionNumber->wOctetStringLength = (WORD)
                min(pUserInfo->h323_uu_pdu.h323_message_body.u.connect.destinationInfo.vendor.versionId.length,
                CC_MAX_VERSION_LENGTH - 1);
            memcpy(pParsedData->bufVersionValue,
                pUserInfo->h323_uu_pdu.h323_message_body.u.connect.destinationInfo.vendor.versionId.value,
                pParsedData->VendorInfo.pVersionNumber->wOctetStringLength);
            pParsedData->bufVersionValue[pParsedData->VendorInfo.pVersionNumber->wOctetStringLength] = '\0';
            pParsedData->VendorInfo.pVersionNumber->pOctetString = pParsedData->bufVersionValue;
        }

    }

    pParsedData->EndpointType.bIsTerminal = FALSE;
    if (pUserInfo->h323_uu_pdu.h323_message_body.u.connect.destinationInfo.bit_mask & (terminal_present))
    {
        pParsedData->EndpointType.bIsTerminal = TRUE;
    }
    pParsedData->EndpointType.bIsGateway = FALSE;
    if (pUserInfo->h323_uu_pdu.h323_message_body.u.connect.destinationInfo.bit_mask & (gateway_present))
    {
        pParsedData->EndpointType.bIsGateway = TRUE;
    }


    memcpy(pParsedData->ConferenceID.buffer,
        pUserInfo->h323_uu_pdu.h323_message_body.u.connect.conferenceID.value,
        pUserInfo->h323_uu_pdu.h323_message_body.u.connect.conferenceID.length);

    // Free the PDU data.
    Result = freePDU(pWorld, PDU, pDecodedBuf, q931asn);
    ASSERT(ASN1_SUCCEEDED(Result));
    return CS_OK;
}

//------------------------------------------------------------------------
//------------------------------------------------------------------------
HRESULT
Q931AlertingParseASN(
    ASN1_CODER_INFO *pWorld,
    BYTE *pEncodedBuf,
    DWORD dwEncodedLength,
    Q931_ALERTING_ASN *pParsedData)
{
    int PDU = H323_UserInformation_PDU;
    char *pDecodedBuf = NULL;
    H323_UserInformation *pUserInfo;
    struct ObjectID_ *id;
    int Result;

    if (pParsedData == NULL)
    {
        return CS_BAD_PARAM;
    }

    Result = Q931_Decode(pWorld,
                         (void **) &pDecodedBuf,
                         PDU,
                         pEncodedBuf,
                         dwEncodedLength);

    if (ASN1_FAILED(Result) || (pDecodedBuf == NULL))
    {
        ASSERT(FALSE);
        // trace and return an decoding error of some sort.
        // Note: some values of Result should cause CS_SUBSYSTEM_FAILURE return.
        return CS_BAD_PARAM;
    }

    // validate some basic things about the PDU...
    pUserInfo = (H323_UserInformation *)pDecodedBuf;

    // validate that this is a H323 PDU.
    if (PDU != H323_UserInformation_PDU)
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_BAD_PARAM;
    }

    // validate that the PDU user-data uses ASN encoding.
    if (((pUserInfo->bit_mask & user_data_present) != 0) &&
            (pUserInfo->user_data.protocol_discriminator != USE_ASN1_ENCODING))
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_BAD_PARAM;
    }

    // validate that the PDU is H323 Alerting information.
    if (pUserInfo->h323_uu_pdu.h323_message_body.choice != alerting_chosen)
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_BAD_PARAM;
    }

    id = pUserInfo->h323_uu_pdu.h323_message_body.u.alerting.protocolIdentifier;
    if (!Q931ValidPduVersion(id))
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_INCOMPATIBLE_VERSION;
    }

    // parse the message contained in pUserInfo.
    memset(pParsedData, 0, sizeof(Q931_ALERTING_ASN));
    pParsedData->h245Addr.bMulticast = FALSE;

    if ((pUserInfo->h323_uu_pdu.bit_mask & H323_UU_PDU_nnStndrdDt_present) != 0)
    {
        pParsedData->NonStandardDataPresent = TRUE;
        if (pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.choice ==
                h221NonStandard_chosen)
        {
            pParsedData->NonStandardData.bCountryCode =
                (BYTE)(pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35CountryCode);
            pParsedData->NonStandardData.bExtension =
                (BYTE)(pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35Extension);
            pParsedData->NonStandardData.wManufacturerCode =
                pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.manufacturerCode;
        }
        pParsedData->NonStandardData.sData.wOctetStringLength =	(WORD)
            pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.length;
		pParsedData->NonStandardData.sData.pOctetString =
			(BYTE *)Malloc(pParsedData->NonStandardData.sData.wOctetStringLength);
		if (pParsedData->NonStandardData.sData.pOctetString == NULL)
		{
            freePDU(pWorld, PDU, pDecodedBuf, q931asn);
            return CS_NO_MEMORY;
		}
		memcpy(pParsedData->NonStandardData.sData.pOctetString,
			   pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.value,
			   pParsedData->NonStandardData.sData.wOctetStringLength);
    }
    else
    {
        pParsedData->NonStandardDataPresent = FALSE;
    }

    if ((pUserInfo->h323_uu_pdu.h323_message_body.u.alerting.bit_mask &
            CPg_UUIE_h245Addrss_present) != 0)
    {
        BYTE *a = (BYTE *)(&(pParsedData->h245Addr.Addr.IP_Binary.dwAddr));
        pParsedData->h245Addr.nAddrType = CC_IP_BINARY;
        pParsedData->h245Addr.Addr.IP_Binary.wPort = 
            pUserInfo->h323_uu_pdu.h323_message_body.u.alerting.CPg_UUIE_h245Addrss.u.ipAddress.port;
        a[3] = pUserInfo->h323_uu_pdu.h323_message_body.u.alerting.CPg_UUIE_h245Addrss.u.ipAddress.ip.value[0];
        a[2] = pUserInfo->h323_uu_pdu.h323_message_body.u.alerting.CPg_UUIE_h245Addrss.u.ipAddress.ip.value[1];
        a[1] = pUserInfo->h323_uu_pdu.h323_message_body.u.alerting.CPg_UUIE_h245Addrss.u.ipAddress.ip.value[2];
        a[0] = pUserInfo->h323_uu_pdu.h323_message_body.u.alerting.CPg_UUIE_h245Addrss.u.ipAddress.ip.value[3];
    }

//RMO. ignore the destinationInfo field.

    // Free the PDU data.
    Result = freePDU(pWorld, PDU, pDecodedBuf, q931asn);
    ASSERT(ASN1_SUCCEEDED(Result));
    return CS_OK;
}

//------------------------------------------------------------------------
//------------------------------------------------------------------------
HRESULT
Q931ProceedingParseASN(
    ASN1_CODER_INFO *pWorld,
    BYTE *pEncodedBuf,
    DWORD dwEncodedLength,
    Q931_CALL_PROCEEDING_ASN *pParsedData)
{
    int PDU = H323_UserInformation_PDU;
    char *pDecodedBuf = NULL;
    H323_UserInformation *pUserInfo;
    struct ObjectID_ *id;
    int Result;

    if (pParsedData == NULL)
    {
        return CS_BAD_PARAM;
    }

    Result = Q931_Decode(pWorld,
                         (void **) &pDecodedBuf,
                         PDU,
                         pEncodedBuf,
                         dwEncodedLength);

    if (ASN1_FAILED(Result) || (pDecodedBuf == NULL))
    {
        ASSERT(FALSE);
        // trace and return an decoding error of some sort.
        // Note: some values of Result should cause CS_SUBSYSTEM_FAILURE return.
        return CS_BAD_PARAM;
    }

    // validate some basic things about the PDU...
    pUserInfo = (H323_UserInformation *)pDecodedBuf;

    // validate that this is a H323 PDU.
    if (PDU != H323_UserInformation_PDU)
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_BAD_PARAM;
    }

    // validate that the PDU user-data uses ASN encoding.
    if (((pUserInfo->bit_mask & user_data_present) != 0) &&
            (pUserInfo->user_data.protocol_discriminator != USE_ASN1_ENCODING))
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_BAD_PARAM;
    }

    // validate that the PDU is H323 Call Proceeding information.
    if (pUserInfo->h323_uu_pdu.h323_message_body.choice != callProceeding_chosen)
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_BAD_PARAM;
    }

    id = pUserInfo->h323_uu_pdu.h323_message_body.u.callProceeding.protocolIdentifier;
    if (!Q931ValidPduVersion(id))
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_INCOMPATIBLE_VERSION;
    }

    // parse the message contained in pUserInfo.
    memset(pParsedData, 0, sizeof(Q931_CALL_PROCEEDING_ASN));
    pParsedData->h245Addr.bMulticast = FALSE;

    if ((pUserInfo->h323_uu_pdu.bit_mask & H323_UU_PDU_nnStndrdDt_present) != 0)
    {
        pParsedData->NonStandardDataPresent = TRUE;
        if (pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.choice ==
                h221NonStandard_chosen)
        {
            pParsedData->NonStandardData.bCountryCode =
                (BYTE)(pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35CountryCode);
            pParsedData->NonStandardData.bExtension =
                (BYTE)(pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35Extension);
            pParsedData->NonStandardData.wManufacturerCode =
                pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.manufacturerCode;
        }
        pParsedData->NonStandardData.sData.wOctetStringLength =	(WORD)
        pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.length;
				pParsedData->NonStandardData.sData.pOctetString =
			(BYTE *)Malloc(pParsedData->NonStandardData.sData.wOctetStringLength);
		if (pParsedData->NonStandardData.sData.pOctetString == NULL)
		{
            freePDU(pWorld, PDU, pDecodedBuf, q931asn);
            return CS_NO_MEMORY;
		}
		memcpy(pParsedData->NonStandardData.sData.pOctetString,
			   pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.value,
			   pParsedData->NonStandardData.sData.wOctetStringLength);
    }
    else
    {
        pParsedData->NonStandardDataPresent = FALSE;
    }

    if ((pUserInfo->h323_uu_pdu.h323_message_body.u.callProceeding.bit_mask &
            CPg_UUIE_h245Addrss_present) != 0)
    {
        BYTE *a = (BYTE *)(&(pParsedData->h245Addr.Addr.IP_Binary.dwAddr));
        pParsedData->h245Addr.nAddrType = CC_IP_BINARY;
        pParsedData->h245Addr.Addr.IP_Binary.wPort = 
            pUserInfo->h323_uu_pdu.h323_message_body.u.callProceeding.CPg_UUIE_h245Addrss.u.ipAddress.port;
        a[3] = pUserInfo->h323_uu_pdu.h323_message_body.u.callProceeding.CPg_UUIE_h245Addrss.u.ipAddress.ip.value[0];
        a[2] = pUserInfo->h323_uu_pdu.h323_message_body.u.callProceeding.CPg_UUIE_h245Addrss.u.ipAddress.ip.value[1];
        a[1] = pUserInfo->h323_uu_pdu.h323_message_body.u.callProceeding.CPg_UUIE_h245Addrss.u.ipAddress.ip.value[2];
        a[0] = pUserInfo->h323_uu_pdu.h323_message_body.u.callProceeding.CPg_UUIE_h245Addrss.u.ipAddress.ip.value[3];
    }

//RMO. ignore the destinationInfo field.

    // Free the PDU data.
    Result = freePDU(pWorld, PDU, pDecodedBuf, q931asn);
    ASSERT(ASN1_SUCCEEDED(Result));
    return CS_OK;
}

//------------------------------------------------------------------------
//------------------------------------------------------------------------
HRESULT
Q931FacilityParseASN(
    ASN1_CODER_INFO *pWorld,
    BYTE *pEncodedBuf,
    DWORD dwEncodedLength,
    Q931_FACILITY_ASN *pParsedData)
{
    int PDU = H323_UserInformation_PDU;
    char *pDecodedBuf = NULL;
    H323_UserInformation *pUserInfo;
    int Result;

    if (pParsedData == NULL)
    {
        return CS_BAD_PARAM;
    }

    Result = Q931_Decode(pWorld,
                         (void **) &pDecodedBuf,
                         PDU,
                         pEncodedBuf,
                         dwEncodedLength);

    if (ASN1_FAILED(Result) || (pDecodedBuf == NULL))
    {
        ASSERT(FALSE);
        // trace and return an decoding error of some sort.
        // Note: some values of Result should cause CS_SUBSYSTEM_FAILURE return.
        return CS_BAD_PARAM;
    }

    // validate some basic things about the PDU...
    pUserInfo = (H323_UserInformation *)pDecodedBuf;

    // validate that this is a H323 PDU.
    if (PDU != H323_UserInformation_PDU)
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_BAD_PARAM;
    }

    // validate that the PDU user-data uses ASN encoding.
    if (((pUserInfo->bit_mask & user_data_present) != 0) &&
            (pUserInfo->user_data.protocol_discriminator != USE_ASN1_ENCODING))
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_BAD_PARAM;
    }

    // validate that the PDU is H323 Facility information.
    if (pUserInfo->h323_uu_pdu.h323_message_body.choice != facility_chosen)
    {
        freePDU(pWorld, PDU, pDecodedBuf, q931asn);
        return CS_BAD_PARAM;
    }

    {
        struct ObjectID_ *id;

        id = pUserInfo->h323_uu_pdu.h323_message_body.u.facility.protocolIdentifier;
        if (!Q931ValidPduVersion(id))
        {
            freePDU(pWorld, PDU, pDecodedBuf, q931asn);
            return CS_INCOMPATIBLE_VERSION;
        }
    }

    // if there is a conference id, make sure that it is formed correctly.
    if ((pUserInfo->h323_uu_pdu.h323_message_body.u.facility.bit_mask &
            conferenceID_present) != 0)
    {
        if (pUserInfo->h323_uu_pdu.h323_message_body.u.facility.conferenceID.length >
                sizeof(pUserInfo->h323_uu_pdu.h323_message_body.u.facility.conferenceID.value))
        {
            freePDU(pWorld, PDU, pDecodedBuf, q931asn);
            return CS_BAD_PARAM;
        }
    }

    // parse the message contained in pUserInfo.
    memset(pParsedData, 0, sizeof(Q931_FACILITY_ASN));
    pParsedData->AlternativeAddr.bMulticast = FALSE;

    if ((pUserInfo->h323_uu_pdu.bit_mask & H323_UU_PDU_nnStndrdDt_present) != 0)
    {
        pParsedData->NonStandardDataPresent = TRUE;
        if (pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.choice ==
                h221NonStandard_chosen)
        {
            pParsedData->NonStandardData.bCountryCode =
                (BYTE)(pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35CountryCode);
            pParsedData->NonStandardData.bExtension =
                (BYTE)(pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.t35Extension);
            pParsedData->NonStandardData.wManufacturerCode =
                pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.nonStandardIdentifier.u.h221NonStandard.manufacturerCode;
        }
        pParsedData->NonStandardData.sData.wOctetStringLength =	(WORD)
            pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.length;
		pParsedData->NonStandardData.sData.pOctetString =
			(BYTE *)Malloc(pParsedData->NonStandardData.sData.wOctetStringLength);
		if (pParsedData->NonStandardData.sData.pOctetString == NULL)
		{
            freePDU(pWorld, PDU, pDecodedBuf, q931asn);
            return CS_NO_MEMORY;
		}
		memcpy(pParsedData->NonStandardData.sData.pOctetString,
			   pUserInfo->h323_uu_pdu.H323_UU_PDU_nnStndrdDt.data.value,
			   pParsedData->NonStandardData.sData.wOctetStringLength);
    }
    else
    {
        pParsedData->NonStandardDataPresent = FALSE;
    }

    if ((pUserInfo->h323_uu_pdu.h323_message_body.u.facility.bit_mask &
            alternativeAddress_present) != 0)
    {
        BYTE *a = (BYTE *)(&(pParsedData->AlternativeAddr.Addr.IP_Binary.dwAddr));
        pParsedData->AlternativeAddr.nAddrType = CC_IP_BINARY;
        pParsedData->AlternativeAddr.Addr.IP_Binary.wPort = 
            pUserInfo->h323_uu_pdu.h323_message_body.u.facility.alternativeAddress.u.ipAddress.port;
        a[3] = pUserInfo->h323_uu_pdu.h323_message_body.u.facility.alternativeAddress.u.ipAddress.ip.value[0];
        a[2] = pUserInfo->h323_uu_pdu.h323_message_body.u.facility.alternativeAddress.u.ipAddress.ip.value[1];
        a[1] = pUserInfo->h323_uu_pdu.h323_message_body.u.facility.alternativeAddress.u.ipAddress.ip.value[2];
        a[0] = pUserInfo->h323_uu_pdu.h323_message_body.u.facility.alternativeAddress.u.ipAddress.ip.value[3];
    }

    if ((pUserInfo->h323_uu_pdu.h323_message_body.u.facility.bit_mask &
            alternativeAliasAddress_present) != 0)
    {
        CS_STATUS AliasResult = CS_OK;

        // parse the sourceAddress aliases here...
        AliasResult = SeqofToAlias(&(pParsedData->pAlternativeAliasList),
            (struct _seqof3 *)pUserInfo->h323_uu_pdu.h323_message_body.u.facility.alternativeAliasAddress);
        if (AliasResult != CS_OK)
        {
            freePDU(pWorld, PDU, pDecodedBuf, q931asn);
            return CS_NO_MEMORY;
        }
    }

    if ((pUserInfo->h323_uu_pdu.h323_message_body.u.facility.bit_mask &
            conferenceID_present) != 0)
    {
        memcpy(pParsedData->ConferenceID.buffer,
            pUserInfo->h323_uu_pdu.h323_message_body.u.facility.conferenceID.value,
            pUserInfo->h323_uu_pdu.h323_message_body.u.facility.conferenceID.length);
        pParsedData->ConferenceIDPresent = TRUE;
    }

	switch (pUserInfo->h323_uu_pdu.h323_message_body.u.facility.reason.choice)
    {
	case routeCallToGatekeeper_chosen:
        pParsedData->bReason = CC_REJECT_ROUTE_TO_GATEKEEPER;
		break;
	case callForwarded_chosen:
        pParsedData->bReason = CC_REJECT_CALL_FORWARDED;
		break;
	case routeCallToMC_chosen:
        pParsedData->bReason = CC_REJECT_ROUTE_TO_MC;
		break;
	default:
        pParsedData->bReason = CC_REJECT_UNDEFINED_REASON;
	} // switch

    // Free the PDU data.
    Result = freePDU(pWorld, PDU, pDecodedBuf, q931asn);
    ASSERT(ASN1_SUCCEEDED(Result));
    return CS_OK;
}

// THE FOLLOWING IS ADDED FOR TELES ASN.1 INTEGRATION

int Q931_InitModule(void)
{
    Q931ASN_Module_Startup();
    return (Q931ASN_Module != NULL) ? ASN1_SUCCESS : ASN1_ERR_MEMORY;
}

int Q931_TermModule(void)
{
    Q931ASN_Module_Cleanup();
    return ASN1_SUCCESS;
}

int Q931_InitWorld(ASN1_CODER_INFO *pWorld)
{
    int rc;

    ZeroMemory(pWorld, sizeof(*pWorld));

    if (Q931ASN_Module == NULL)
    {
        return ASN1_ERR_BADARGS;
    }

    rc = ASN1_CreateEncoder(
                Q931ASN_Module,         // ptr to mdule
                &(pWorld->pEncInfo),    // ptr to encoder info
                NULL,                   // buffer ptr
                0,                      // buffer size
                NULL);                  // parent ptr
    if (rc == ASN1_SUCCESS)
    {
        ASSERT(pWorld->pEncInfo != NULL);
        rc = ASN1_CreateDecoder(
                Q931ASN_Module,         // ptr to mdule
                &(pWorld->pDecInfo),    // ptr to decoder info
                NULL,                   // buffer ptr
                0,                      // buffer size
                NULL);                  // parent ptr
        ASSERT(pWorld->pDecInfo != NULL);
    }

    if (rc != ASN1_SUCCESS)
    {
        Q931_TermWorld(pWorld);
    }

    return rc;
}

int Q931_TermWorld(ASN1_CODER_INFO *pWorld)
{
    if (Q931ASN_Module == NULL)
    {
        return ASN1_ERR_BADARGS;
    }

    ASN1_CloseEncoder(pWorld->pEncInfo);
    ASN1_CloseDecoder(pWorld->pDecInfo);

    ZeroMemory(pWorld, sizeof(*pWorld));

    return ASN1_SUCCESS;
}

int Q931_Encode(ASN1_CODER_INFO *pWorld, void *pStruct, int nPDU, BYTE **ppEncoded, DWORD *pcbEncodedSize)
{
    ASN1encoding_t pEncInfo = pWorld->pEncInfo;
    int rc = ASN1_Encode(
                    pEncInfo,                   // ptr to encoder info
                    pStruct,                    // pdu data structure
                    nPDU,                       // pdu id
                    ASN1ENCODE_ALLOCATEBUFFER,  // flags
                    NULL,                       // do not provide buffer
                    0);                         // buffer size if provided
    if (ASN1_SUCCEEDED(rc))
    {
        ASSERT(rc == ASN1_SUCCESS);
        *pcbEncodedSize = pEncInfo->len;        // len of encoded data in buffer
        *ppEncoded = pEncInfo->buf;             // buffer to encode into
    }
    else
    {
        ASSERT(FALSE);
        *pcbEncodedSize = 0;
        *ppEncoded = NULL;
    }
    return rc;
}

int Q931_Decode(ASN1_CODER_INFO *pWorld, void **ppStruct, int nPDU, BYTE *pEncoded, DWORD cbEncodedSize)
{
    ASN1decoding_t pDecInfo = pWorld->pDecInfo;
    int rc = ASN1_Decode(
                    pDecInfo,                   // ptr to encoder info
                    ppStruct,                   // pdu data structure
                    nPDU,                       // pdu id
                    ASN1DECODE_SETBUFFER,       // flags
                    pEncoded,                   // do not provide buffer
                    cbEncodedSize);             // buffer size if provided
    if (ASN1_SUCCEEDED(rc))
    {
        ASSERT(rc == ASN1_SUCCESS);
    }
    else
    {
        ASSERT(FALSE);
        *ppStruct = NULL;
    }
    return rc;
}
