/***********************************************************************
 * 
 *  Copyright (C) 2006 Novell, Inc. All Rights Reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; version 2.1
 *  of the License.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, Novell, Inc.
 * 
 *  To contact Novell about this file by physical or electronic mail, 
 *  you may find current contact information at www.novell.com.
 * 
 *  Author: Juan Carlos Luciani <jluciani@novell.com>
 *
 ***********************************************************************/


//===[ Include files ]=====================================================

#include "internal.h"

//===[ Type definitions ]==================================================

//
// XML Constants for the CASA Identity Token
// 
#define CASA_IDENT_TOKEN_ELEMENT_NAME "casa_ident_tok"
#define ID_ELEMENT_NAME "id"
#define SOURCE_NAME_ELEMENT_NAME "source_name"
#define SOURCE_URL_ELEMENT_NAME "source_url"
#define TARGET_SERVICE_ELEMENT_NAME "target_service"
#define TARGET_HOST_ELEMENT_NAME "target_host"
#define ATTRIBUTES_ELEMENT_NAME "attributes"

//
// Parse states
//
#define AWAITING_ROOT_ELEMENT_START             0x0
#define AWAITING_ROOT_ELEMENT_END               0x1
#define AWAITING_ID_DATA                        0x2
#define AWAITING_ID_ELEMENT_START               0x3
#define AWAITING_ID_ELEMENT_END                 0x4
#define AWAITING_SOURCE_NAME_DATA               0x5
#define AWAITING_SOURCE_NAME_ELEMENT_START      0x6
#define AWAITING_SOURCE_NAME_ELEMENT_END        0x7
#define AWAITING_SOURCE_URL_DATA                0x8
#define AWAITING_SOURCE_URL_ELEMENT_START       0x9
#define AWAITING_SOURCE_URL_ELEMENT_END         0xA
#define AWAITING_TARGET_SERVICE_DATA            0xB
#define AWAITING_TARGET_SERVICE_ELEMENT_START   0xC
#define AWAITING_TARGET_SERVICE_ELEMENT_END     0xD
#define AWAITING_TARGET_HOST_DATA               0xE
#define AWAITING_TARGET_HOST_ELEMENT_START      0xF
#define AWAITING_TARGET_HOST_ELEMENT_END        0x10
#define AWAITING_ATTRIBUTES_ELEMENT_START       0x11
#define AWAITING_ATTRIBUTE_DATA                 0x12
#define AWAITING_ATTRIBUTE_START                0x13
#define AWAITING_ATTRIBUTE_END                  0x14
#define DONE_PARSING                            0x15

//
// Attribute structure
//
typedef struct _Attribute
{
   LIST_ENTRY  listEntry;
   char        *pAttribName;
   size_t      attribNameLen;
   char        *pAttribValue;
   size_t      attribValueLen;

} Attribute, *PAttribute;


//
// Identity Token Interface instance data
// 
typedef struct _IdenTokenIfInstance
{
   int            refCount;
   char           *pIdentId;
   size_t         identIdLen;
   char           *pIdentSourceName;
   size_t         identSourceNameLen;
   char           *pIdentSourceUrl;
   size_t         identSourceUrlLen;
   char           *pTargetService;
   size_t         targetServiceLen;
   char           *pTargetHost;
   size_t         targetHostLen;
   LIST_ENTRY     attributeListHead;
   IdenTokenIf    idenTokenIf;

} IdenTokenIfInstance, *PIdenTokenIfInstance;


//
// Identity Token Parse Structure
//
typedef struct _IdenTokenParse
{
   XML_Parser           p;
   int                  state;
   size_t               elementDataProcessed;
   IdenTokenIfInstance  *pIdenTokenIfInstance;
   CasaStatus           status;

} IdenTokenParse, *PIdenTokenParse;

//===[ Function prototypes ]===============================================

//===[ Global variables ]==================================================

// IdenTokenIf variables
static
int      g_numIdenTokenIfObjs = 0;

// Synchronization mutex
static
HANDLE   g_idenTokenIfMutex = NULL;


//++=======================================================================
static
void
FreeIdenTokenIfInstance(
   IN    IdenTokenIfInstance *pIdenTokenIfInstance)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   LIST_ENTRY  *pListEntry;

   DbgTrace(2, "-FreeIdenTokenIfInstance- Start\n", 0);

   // Free all of the resources associated with the IdenTokenIfInstance
   if (pIdenTokenIfInstance->pIdentId)
      free(pIdenTokenIfInstance->pIdentId);

   if (pIdenTokenIfInstance->pIdentSourceName)
      free(pIdenTokenIfInstance->pIdentSourceName);

   if (pIdenTokenIfInstance->pIdentSourceUrl)
      free(pIdenTokenIfInstance->pIdentSourceUrl);

   if (pIdenTokenIfInstance->pTargetService)
      free(pIdenTokenIfInstance->pTargetService);

   if (pIdenTokenIfInstance->pTargetHost)
      free(pIdenTokenIfInstance->pTargetHost);

   // Go through all of the associated attributes
   pListEntry = pIdenTokenIfInstance->attributeListHead.Flink;
   while (pListEntry != &pIdenTokenIfInstance->attributeListHead)
   {
      Attribute   *pAttribute = CONTAINING_RECORD(pListEntry, Attribute, listEntry);

      // Free resources associated with the attribute
      if (pAttribute->pAttribName)
         free(pAttribute->pAttribName);

      if (pAttribute->pAttribValue)
         free(pAttribute->pAttribValue);

      // Forget about this attribute
      RemoveEntryList(&pAttribute->listEntry);
      free(pAttribute);

      // Start from the top again
      pListEntry = pIdenTokenIfInstance->attributeListHead.Flink;
   }

   // Free the identity token if instance structure
   free(pIdenTokenIfInstance);

   DbgTrace(2, "-FreeIdenTokenIfInstance- End\n", 0);
}


//++=======================================================================
static
void XMLCALL
IdenTokenStartElementHandler(
   IN    void *pUserData,
   IN    const XML_Char *name,
   IN    const XML_Char **atts)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   IdenTokenParse    *pIdenTokenParse = (IdenTokenParse*) pUserData;
   Attribute         *pAttribute;

   DbgTrace(2, "-IdenTokenStartElementHandler- Start\n", 0);

   // Proceed based on the state
   switch (pIdenTokenParse->state)
   {
      case AWAITING_ROOT_ELEMENT_START:

         // In this state, we are only expecting the CASA Identity
         // Token Element.
         if (strcmp(name, CASA_IDENT_TOKEN_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pIdenTokenParse->state = AWAITING_ID_ELEMENT_START;
         }
         else
         {
            DbgTrace(0, "-IdenTokenStartElementHandler- Un-expected start element\n", 0);
            XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         }
         break;

      case AWAITING_ID_ELEMENT_START:
   
         // In this state, we are only expecting the ID Element.
         if (strcmp(name, ID_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pIdenTokenParse->state = AWAITING_ID_DATA;
         }
         else
         {
            DbgTrace(0, "-IdenTokenStartElementHandler- Un-expected start element\n", 0);
            XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         }
         break;

      case AWAITING_SOURCE_NAME_ELEMENT_START:
   
         // In this state, we are only expecting the Source Name Element.
         if (strcmp(name, SOURCE_NAME_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pIdenTokenParse->state = AWAITING_SOURCE_NAME_DATA;
         }
         else
         {
            DbgTrace(0, "-IdenTokenStartElementHandler- Un-expected start element\n", 0);
            XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         }
         break;

      case AWAITING_SOURCE_URL_ELEMENT_START:
   
         // In this state, we are only expecting the Source Url Element.
         if (strcmp(name, SOURCE_URL_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pIdenTokenParse->state = AWAITING_SOURCE_URL_DATA;
         }
         else
         {
            DbgTrace(0, "-IdenTokenStartElementHandler- Un-expected start element\n", 0);
            XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         }
         break;
   
      case AWAITING_TARGET_SERVICE_ELEMENT_START:
   
         // In this state, we are only expecting the Target Service Element.
         if (strcmp(name, TARGET_SERVICE_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pIdenTokenParse->state = AWAITING_TARGET_SERVICE_DATA;
         }
         else
         {
            DbgTrace(0, "-IdenTokenStartElementHandler- Un-expected start element\n", 0);
            XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         }
         break;

      case AWAITING_TARGET_HOST_ELEMENT_START:
   
         // In this state, we are only expecting the Target Host Element.
         if (strcmp(name, TARGET_HOST_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pIdenTokenParse->state = AWAITING_TARGET_HOST_DATA;
         }
         else
         {
            DbgTrace(0, "-IdenTokenStartElementHandler- Un-expected start element\n", 0);
            XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         }
         break;

      case AWAITING_ATTRIBUTES_ELEMENT_START:
   
         // In this state, we are only expecting the Attributes Element.
         if (strcmp(name, ATTRIBUTES_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pIdenTokenParse->state = AWAITING_ATTRIBUTE_START;
         }
         else
         {
            DbgTrace(0, "-IdenTokenStartElementHandler- Un-expected start element\n", 0);
            XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         }
         break;

      case AWAITING_ATTRIBUTE_START:

         // Allocate an initialize Attribute structure
         pAttribute = malloc(sizeof(*pAttribute));
         if (pAttribute)
         {
            memset(pAttribute, 0, sizeof(*pAttribute));
            InsertTailList(&pIdenTokenParse->pIdenTokenIfInstance->attributeListHead,
                           &pAttribute->listEntry);

            // Now save the attribute name
            pAttribute->attribNameLen = strlen(name) + 1;
            pAttribute->pAttribName = malloc(pAttribute->attribNameLen);
            if (pAttribute->pAttribName)
            {
               strcpy(pAttribute->pAttribName, name);

               // Good, advance to the next state.
               pIdenTokenParse->state = AWAITING_ATTRIBUTE_DATA;
            }
            else
            {
               DbgTrace(0, "-IdenTokenStartElementHandler- Buffer allocation failure\n", 0);
               XML_StopParser(pIdenTokenParse->p, XML_FALSE);
            }
         }
         else
         {
            DbgTrace(0, "-IdenTokenStartElementHandler- Buffer allocation failure\n", 0);
            XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         }
         break;

      default:
         DbgTrace(0, "-IdenTokenStartElementHandler- Un-expected state = %d\n", pIdenTokenParse->state);
         XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         break;
   }

   DbgTrace(2, "-IdenTokenStartElementHandler- End\n", 0);
}


//++=======================================================================
static
CasaStatus
ConsumeElementData(
   IN    IdenTokenParse *pIdenTokenParse,
   IN    const XML_Char *s,
   IN    int len,
   INOUT char **ppElementData,
   INOUT size_t *pElementDataLen)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   CasaStatus  retStatus = CASA_STATUS_SUCCESS;

   DbgTrace(3, "-ConsumeElementData- Start\n", 0);

   // Proceed based on whether or not we have already consumed data
   // for this element.
   if (*ppElementData == NULL)
   {
      // We have not yet consumed data for this element
      pIdenTokenParse->elementDataProcessed = len;

      // Allocate a buffer to hold this element data (null terminated).
      *ppElementData = (char*) malloc(len + 1);
      if (*ppElementData)
      {
         memset(*ppElementData, 0, len + 1);
         memcpy(*ppElementData, s, len);

         // Return the length of the element data buffer
         *pElementDataLen = pIdenTokenParse->elementDataProcessed + 1;
      }
      else
      {
         DbgTrace(0, "-ConsumeElementData- Buffer allocation failure\n", 0);
         retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                     CASA_FACILITY_AUTHTOKEN,
                                     CASA_STATUS_INSUFFICIENT_RESOURCES);
      }
   }
   else
   {
      char  *pNewBuf;

      // We have already received token data, append this data to it.
      pNewBuf = (char*) malloc((size_t)(pIdenTokenParse->elementDataProcessed + len + 1));
      if (pNewBuf)
      {
         memset(pNewBuf,
                0,
                pIdenTokenParse->elementDataProcessed + len + 1);
         memcpy(pNewBuf,
                *ppElementData,
                pIdenTokenParse->elementDataProcessed);
         memcpy(pNewBuf + pIdenTokenParse->elementDataProcessed, s, len);
         pIdenTokenParse->elementDataProcessed += len;

         // Swap the buffers
         free(*ppElementData);
         *ppElementData = pNewBuf;

         // Return the length of the element data buffer
         *pElementDataLen = pIdenTokenParse->elementDataProcessed + 1;
      }
      else
      {
         DbgTrace(0, "-ConsumeElementData- Buffer allocation failure\n", 0);
         retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                     CASA_FACILITY_AUTHTOKEN,
                                     CASA_STATUS_INSUFFICIENT_RESOURCES);
      }
   }

   DbgTrace(3, "-ConsumeElementData- End, retStatus = %0X\n", retStatus);

   return retStatus;
}


//++=======================================================================
static
void XMLCALL
IdenTokenCharDataHandler(
   IN    void *pUserData,
   IN    const XML_Char *s,
   IN    int len)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   IdenTokenParse    *pIdenTokenParse = (IdenTokenParse*) pUserData;
   Attribute         *pAttribute;

   DbgTrace(2, "-IdenTokenCharDataHandler- Start\n", 0);

   // Just exit if being called to process LF and CR characters
   if (len == 1
       && ((*s == '\n') || (*s == '\r')))
   {
      goto exit;
   }

   // Proceed based on the state
   switch (pIdenTokenParse->state)
   {
      case AWAITING_ID_DATA:
      case AWAITING_ID_ELEMENT_END:

         pIdenTokenParse->status = ConsumeElementData(pIdenTokenParse,
                                                      s,
                                                      len,
                                                      &pIdenTokenParse->pIdenTokenIfInstance->pIdentId,
                                                      &pIdenTokenParse->pIdenTokenIfInstance->identIdLen);
         if (CASA_SUCCESS(pIdenTokenParse->status))
         {
            // Advanced to the next state
            pIdenTokenParse->state = AWAITING_ID_ELEMENT_END;
         }
         else
         {
            XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         }
         break;

      case AWAITING_SOURCE_NAME_DATA:
      case AWAITING_SOURCE_NAME_ELEMENT_END:
   
         pIdenTokenParse->status = ConsumeElementData(pIdenTokenParse,
                                                      s,
                                                      len,
                                                      &pIdenTokenParse->pIdenTokenIfInstance->pIdentSourceName,
                                                      &pIdenTokenParse->pIdenTokenIfInstance->identSourceNameLen);
         if (CASA_SUCCESS(pIdenTokenParse->status))
         {
            // Advanced to the next state
            pIdenTokenParse->state = AWAITING_SOURCE_NAME_ELEMENT_END;
         }
         else
         {
            XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         }
         break;

      case AWAITING_SOURCE_URL_DATA:
      case AWAITING_SOURCE_URL_ELEMENT_END:
   
         pIdenTokenParse->status = ConsumeElementData(pIdenTokenParse,
                                                      s,
                                                      len,
                                                      &pIdenTokenParse->pIdenTokenIfInstance->pIdentSourceUrl,
                                                      &pIdenTokenParse->pIdenTokenIfInstance->identSourceUrlLen);
         if (CASA_SUCCESS(pIdenTokenParse->status))
         {
            // Advanced to the next state
            pIdenTokenParse->state = AWAITING_SOURCE_URL_ELEMENT_END;
         }
         else
         {
            XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         }
         break;
   
      case AWAITING_TARGET_SERVICE_DATA:
      case AWAITING_TARGET_SERVICE_ELEMENT_END:
   
         pIdenTokenParse->status = ConsumeElementData(pIdenTokenParse,
                                                      s,
                                                      len,
                                                      &pIdenTokenParse->pIdenTokenIfInstance->pTargetService,
                                                      &pIdenTokenParse->pIdenTokenIfInstance->targetServiceLen);
         if (CASA_SUCCESS(pIdenTokenParse->status))
         {
            // Advanced to the next state
            pIdenTokenParse->state = AWAITING_TARGET_SERVICE_ELEMENT_END;
         }
         else
         {
            XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         }
         break;

      case AWAITING_TARGET_HOST_DATA:
      case AWAITING_TARGET_HOST_ELEMENT_END:
   
         pIdenTokenParse->status = ConsumeElementData(pIdenTokenParse,
                                                      s,
                                                      len,
                                                      &pIdenTokenParse->pIdenTokenIfInstance->pTargetHost,
                                                      &pIdenTokenParse->pIdenTokenIfInstance->targetHostLen);
         if (CASA_SUCCESS(pIdenTokenParse->status))
         {
            // Advanced to the next state
            pIdenTokenParse->state = AWAITING_TARGET_HOST_ELEMENT_END;
         }
         else
         {
            XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         }
         break;

      case AWAITING_ATTRIBUTE_DATA:
      case AWAITING_ATTRIBUTE_END:

         // Get a pointer to current attribute structure
         pAttribute = CONTAINING_RECORD(pIdenTokenParse->pIdenTokenIfInstance->attributeListHead.Blink,
                                        Attribute,
                                        listEntry);

         pIdenTokenParse->status = ConsumeElementData(pIdenTokenParse,
                                                      s,
                                                      len,
                                                      &pAttribute->pAttribValue,
                                                      &pAttribute->attribValueLen);
         if (CASA_SUCCESS(pIdenTokenParse->status))
         {
            // Advanced to the next state
            pIdenTokenParse->state = AWAITING_ATTRIBUTE_END;
         }
         else
         {
            XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         }
         break;

      default:
         DbgTrace(0, "-IdenTokenCharDataHandler- Un-expected state = %d\n", pIdenTokenParse->state);
         XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         break;
   }

exit:

   DbgTrace(2, "-IdenTokenCharDataHandler- End\n", 0);
}


//++=======================================================================
static
void XMLCALL
IdenTokenEndElementHandler(
   IN    void *pUserData,
   IN    const XML_Char *name)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   IdenTokenParse    *pIdenTokenParse = (IdenTokenParse*) pUserData;

   DbgTrace(2, "-IdenTokenEndElementHandler- Start\n", 0);

   // Proceed based on the state
   switch (pIdenTokenParse->state)
   {
      case AWAITING_ROOT_ELEMENT_END:

         // In this state, we are only expecting the CASA Identity
         // Token Element.
         if (strcmp(name, CASA_IDENT_TOKEN_ELEMENT_NAME) == 0)
         {
            // Done.
            pIdenTokenParse->state = DONE_PARSING;
         }
         else
         {
            DbgTrace(0, "-IdenTokenEndHandler- Un-expected end element, state = %08x\n", pIdenTokenParse->state);
            XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         }
         break;

      case AWAITING_ID_ELEMENT_END:
   
         // In this state, we are only expecting the Id Element.
         if (strcmp(name, ID_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pIdenTokenParse->state = AWAITING_SOURCE_NAME_ELEMENT_START;
         }
         else
         {
            DbgTrace(0, "-IdenTokenEndHandler- Un-expected end element, state = %08x\n", pIdenTokenParse->state);
            XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         }
         break;

      case AWAITING_SOURCE_NAME_ELEMENT_END:
   
         // In this state, we are only expecting the Source Name Element.
         if (strcmp(name, SOURCE_NAME_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pIdenTokenParse->state = AWAITING_SOURCE_URL_ELEMENT_START;
         }
         else
         {
            DbgTrace(0, "-IdenTokenEndHandler- Un-expected end element, state = %08x\n", pIdenTokenParse->state);
            XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         }
         break;
   
      case AWAITING_SOURCE_URL_ELEMENT_END:
   
         // In this state, we are only expecting the Source URL Element.
         if (strcmp(name, SOURCE_URL_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pIdenTokenParse->state = AWAITING_TARGET_SERVICE_ELEMENT_START;
         }
         else
         {
            DbgTrace(0, "-IdenTokenEndHandler- Un-expected end element, state = %08x\n", pIdenTokenParse->state);
            XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         }
         break;

      case AWAITING_TARGET_SERVICE_ELEMENT_END:
   
         // In this state, we are only expecting the Target Service Element.
         if (strcmp(name, TARGET_SERVICE_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pIdenTokenParse->state = AWAITING_TARGET_HOST_ELEMENT_START;
         }
         else
         {
            DbgTrace(0, "-IdenTokenEndHandler- Un-expected end element, state = %08x\n", pIdenTokenParse->state);
            XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         }
         break;
   
      case AWAITING_TARGET_HOST_ELEMENT_END:
   
         // In this state, we are only expecting the Target Host Element.
         if (strcmp(name, TARGET_HOST_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pIdenTokenParse->state = AWAITING_ATTRIBUTES_ELEMENT_START;
         }
         else
         {
            DbgTrace(0, "-IdenTokenEndHandler- Un-expected end element, state = %08x\n", pIdenTokenParse->state);
            XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         }
         break;
   
      case AWAITING_ATTRIBUTE_END:
   
         // Good, advance to the next state.
         pIdenTokenParse->state = AWAITING_ATTRIBUTE_START;
         break;
   
      case AWAITING_ATTRIBUTE_START:
   
         // We should we done with the attributes, in this state we are only expecting
         // the Attributes element.
         if (strcmp(name, ATTRIBUTES_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pIdenTokenParse->state = AWAITING_ROOT_ELEMENT_END;
         }
         else
         {
            DbgTrace(0, "-IdenTokenEndHandler- Un-expected end element, state = %08x\n", pIdenTokenParse->state);
            XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         }
         break;

      default:
         DbgTrace(0, "-IdenTokenEndElementHandler- Un-expected state = %d\n", pIdenTokenParse->state);
         XML_StopParser(pIdenTokenParse->p, XML_FALSE);
         break;
   }

   DbgTrace(2, "-IdenTokenEndElementHandler- End\n", 0);
}


//++=======================================================================
static
int SSCS_CALL
AddReference(
   IN       const void  *pIfInstance)
//
// Arguments:  
//    pIfInstance -
//       Pointer to interface object.
//   
// Returns:
//    Interface reference count.
//                           
// Description:
//    Increases interface reference count.
//
// L2
//=======================================================================--
{
   int                  refCount;
   IdenTokenIfInstance  *pIdenTokenIfInstance = CONTAINING_RECORD(pIfInstance, IdenTokenIfInstance, idenTokenIf);

   DbgTrace(2, "-AddReference- Start\n", 0);

   // Increment the reference count on the object
   PlatAcquireMutex(g_idenTokenIfMutex);
   pIdenTokenIfInstance->refCount ++;
   refCount = pIdenTokenIfInstance->refCount;
   PlatReleaseMutex(g_idenTokenIfMutex);

   DbgTrace(2, "-AddReference- End, refCount = %0X\n", refCount);

   return refCount;
}


//++=======================================================================
static
void SSCS_CALL
ReleaseReference(
   IN       const void  *pIfInstance)
//
// Arguments:  
//    pIfInstance -
//       Pointer to interface object.
//   
// Returns:
//    Nothing.
//                           
// Description:
//    Decreases interface reference count. The interface is deallocated if
//    the reference count becomes zero.
//
// L2
//=======================================================================--
{
   bool                 freeObj = false;
   IdenTokenIfInstance  *pIdenTokenIfInstance = CONTAINING_RECORD(pIfInstance, IdenTokenIfInstance, idenTokenIf);

   DbgTrace(2, "-ReleaseReference- Start\n", 0);

   // Decrement the reference count on the object and determine if it needs to
   // be released.
   PlatAcquireMutex(g_idenTokenIfMutex);
   pIdenTokenIfInstance->refCount --;
   if (pIdenTokenIfInstance->refCount == 0)
   {
      // The object needs to be released, forget about it.
      freeObj = true;
      g_numIdenTokenIfObjs --;
   }
   PlatReleaseMutex(g_idenTokenIfMutex);

   // Free object if necessary
   if (freeObj)
   {
      FreeIdenTokenIfInstance(pIdenTokenIfInstance);
   }

   DbgTrace(2, "-ReleaseReference- End\n", 0);
}


//++=======================================================================
static
CasaStatus SSCS_CALL
GetIdentityId(
   IN       const void  *pIfInstance,
   INOUT    char        *pIdentIdBuf,
   INOUT    size_t      *pIdentIdLen)
//
// Arguments:  
//    pIfInstance -
//       Pointer to interface object.
//   
//    pIdentIdBuf -
//       Pointer to buffer that will receive the identity id. The returned
//       id will be in the form of a NULL terminated string.
//
//    pIdentIdBufLen -
//       Pointer to variable with the length of the buffer pointed by
//       pIdentIdBuf. On exit it contains the length of the returned id
//       (including the NULL terminator).
//
//   
// Returns:
//    Casa Status
//                           
// Description:
//    Get the identity id associated with the identity token.
//
// L2
//=======================================================================--
{
   CasaStatus           retStatus;
   IdenTokenIfInstance  *pIdenTokenIfInstance = CONTAINING_RECORD(pIfInstance, IdenTokenIfInstance, idenTokenIf);

   DbgTrace(2, "-GetIdentityId- Start\n", 0);

   // Check the input parameters
   if (pIfInstance == NULL
       || pIdentIdLen == NULL
       || (pIdentIdBuf == NULL && *pIdentIdLen != 0))
   {
      DbgTrace(0, "-GetIdentityId- Invalid parameter\n", 0);
      retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_INVALID_PARAMETER);
      goto exit;
   }

   // Determine if the caller's buffer is large enough
   if (*pIdentIdLen >= pIdenTokenIfInstance->identIdLen)
   {
      // Return the data to the caller
      memcpy(pIdentIdBuf, pIdenTokenIfInstance->pIdentId, pIdenTokenIfInstance->identIdLen);

      // Success
      retStatus = CASA_STATUS_SUCCESS;
   }
   else
   {
      retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_BUFFER_OVERFLOW);
   }

   // Return the lenght of the id
   *pIdentIdLen = pIdenTokenIfInstance->identIdLen;

exit:

   DbgTrace(2, "-GetIdentityId- End, retStatus = %0X\n", retStatus);

   return retStatus;
}


//++=======================================================================
static
CasaStatus SSCS_CALL
GetSourceName(
   IN       const void  *pIfInstance,
   INOUT    char        *pSourceNameBuf,
   INOUT    size_t      *pSourceNameLen)
//
// Arguments:  
//    pIfInstance -
//       Pointer to interface object.
//   
//    pSourceNameBuf -
//       Pointer to buffer that will receive the name associated with the
//       identity information source. The returned name will be in the form
//       of a NULL terminated string.
//
//    pSourceNameBufLen -
//       Pointer to variable with the length of the buffer pointed by
//       pSourceNameBuf. On exit it contains the length of the returned
//       name (including the NULL terminator).
//
//   
// Returns:
//    Casa Status
//                           
// Description:
//    Get the name of the identity source associated with the identity token.
//
// L2
//=======================================================================--
{
   CasaStatus           retStatus;
   IdenTokenIfInstance  *pIdenTokenIfInstance = CONTAINING_RECORD(pIfInstance, IdenTokenIfInstance, idenTokenIf);

   DbgTrace(2, "-GetSourceName- Start\n", 0);

   // Check the input parameters
   if (pIfInstance == NULL
       || pSourceNameLen == NULL
       || (pSourceNameBuf == NULL && *pSourceNameLen != 0))
   {
      DbgTrace(0, "-GetSourceName- Invalid parameter\n", 0);
      retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_INVALID_PARAMETER);
      goto exit;
   }

   // Determine if the caller's buffer is large enough
   if (*pSourceNameLen >= pIdenTokenIfInstance->identSourceNameLen)
   {
      // Return the data to the caller
      memcpy(pSourceNameBuf, pIdenTokenIfInstance->pIdentSourceName, pIdenTokenIfInstance->identSourceNameLen);

      // Success
      retStatus = CASA_STATUS_SUCCESS;
   }
   else
   {
      retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_BUFFER_OVERFLOW);
   }

   // Return the lenght of the source name
   *pSourceNameLen = pIdenTokenIfInstance->identSourceNameLen;

exit:

   DbgTrace(2, "-GetSourceName- End, retStatus = %0X\n", retStatus);

   return retStatus;
}


//++=======================================================================
static
CasaStatus SSCS_CALL
GetSourceUrl(
   IN       const void  *pIfInstance,
   INOUT    char        *pSourceUrlBuf,
   INOUT    size_t      *pSourceUrlLen)
//
// Arguments:  
//    pIfInstance -
//       Pointer to interface object.
//   
//    pSourceUrlBuf -
//       Pointer to buffer that will receive the URL associated with the
//       identity information source. The returned URL will be in the form
//       of a NULL terminated string.
//
//    pSourceUrlBufLen -
//       Pointer to variable with the length of the buffer pointed by
//       pSourceUrlBuf. On exit it contains the length of the returned
//       URL (including the NULL terminator).
//
//   
// Returns:
//    Casa Status
//                           
// Description:
//    Get the URL to the identity source associated with the identity token.
//
// L2
//=======================================================================--
{
   CasaStatus           retStatus;
   IdenTokenIfInstance  *pIdenTokenIfInstance = CONTAINING_RECORD(pIfInstance, IdenTokenIfInstance, idenTokenIf);

   DbgTrace(2, "-GetSourceUrl- Start\n", 0);

   // Check the input parameters
   if (pIfInstance == NULL
       || pSourceUrlLen == NULL
       || (pSourceUrlBuf == NULL && *pSourceUrlLen != 0))
   {
      DbgTrace(0, "-GetSourceUrl- Invalid parameter\n", 0);
      retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_INVALID_PARAMETER);
      goto exit;
   }

   // Determine if the caller's buffer is large enough
   if (*pSourceUrlLen >= pIdenTokenIfInstance->identSourceUrlLen)
   {
      // Return the data to the caller
      memcpy(pSourceUrlBuf, pIdenTokenIfInstance->pIdentSourceUrl, pIdenTokenIfInstance->identSourceUrlLen);

      // Success
      retStatus = CASA_STATUS_SUCCESS;
   }
   else
   {
      retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_BUFFER_OVERFLOW);
   }

   // Return the lenght of the source url
   *pSourceUrlLen = pIdenTokenIfInstance->identSourceUrlLen;

exit:

   DbgTrace(2, "-GetSourceUrl- End, retStatus = %0X\n", retStatus);

   return retStatus;
}


//++=======================================================================
static
CasaStatus SSCS_CALL
AttributeEnumerate(
   IN       const void     *pIfInstance,
   INOUT    unsigned int   *pEnumHandle,       
   INOUT    char           *pAttribNameBuf,
   INOUT    size_t         *pAttribNameLen,
   INOUT    char           *pAttribValueBuf,
   INOUT    size_t         *pAttribValueLen)
//
// Arguments:  
//    pIfInstance -
//       Pointer to interface object.
//   
//    pEnumHandle -
//       Pointer to enumeration handle. Must be set to 0 to start an
//       enumeration. Note the enumeration handle advances if the
//       function returns success.
//
//    pAttribNameBuf -
//       Pointer to buffer that will receive the identity attribute name. The
//       returned name will be in the form of a NULL terminated string.
//
//    pAttribNameLen -
//       Pointer to variable with the length of the buffer pointed by
//       pAttribNameBuf. On exit it contains the length of the returned
//       name (including the NULL terminator).
//
//    pAttribValueBuf -
//       Pointer to buffer that will receive the identity attribute value. The
//       returned value will be in the form of a NULL terminated string.
//
//    pAttribValueLen -
//       Pointer to variable with the length of the buffer pointed by
//       pAttribValueBuf. On exit it contains the length of the returned
//       value (including the NULL terminator).
//
//   
// Returns:
//    Casa Status
//                           
// Description:
//    Enumerates through the attributes associated with the identity token.
//
// L2
//=======================================================================--
{
   CasaStatus           retStatus;
   IdenTokenIfInstance  *pIdenTokenIfInstance = CONTAINING_RECORD(pIfInstance, IdenTokenIfInstance, idenTokenIf);
   LIST_ENTRY           *pListEntry;
   int                  i;
   Attribute            *pAttribute;

   DbgTrace(2, "-AttributeEnumerate- Start\n", 0);

   // Check the input parameters
   if (pIfInstance == NULL
       || pEnumHandle == NULL
       || pAttribNameLen == NULL
       || pAttribValueLen == NULL
       || (pAttribNameBuf == NULL && *pAttribNameLen != 0)
       || (pAttribValueBuf == NULL && *pAttribValueLen != 0))
   {
      DbgTrace(0, "-AttributeEnumerate- Invalid parameter\n", 0);
      retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_INVALID_PARAMETER);
      goto exit;
   }

   // Find the appropriate attribute based on the enum handle which is used as
   // an index.
   i = 0;
   pAttribute = NULL;
   pListEntry = pIdenTokenIfInstance->attributeListHead.Flink;
   while (pListEntry != &pIdenTokenIfInstance->attributeListHead)
   {
      // Is this the attribute needed
      if (i == *pEnumHandle)
      {
         // This is the attribute needed
         pAttribute = CONTAINING_RECORD(pListEntry, Attribute, listEntry);
         break;
      }
      else
      {
         // No, try the next one.
         pListEntry = pListEntry->Flink;
         i++;
      }
   }

   // Check if we found an attribute for the indicated enum handle
   if (pAttribute)
   {
      // Try to return the attribute name
      if (*pAttribNameLen >= pAttribute->attribNameLen)
      {
         // Return the attribute name
         memcpy(pAttribNameBuf, pAttribute->pAttribName, pAttribute->attribNameLen);

         // Now, try to return the attribute value
         if (*pAttribValueLen >= pAttribute->attribValueLen)
         {
            // Return the attribute value
            memcpy(pAttribValueBuf, pAttribute->pAttribValue, pAttribute->attribValueLen);

            // Success
            retStatus = CASA_STATUS_SUCCESS;

            // Advance the enum handle
            *pEnumHandle = *pEnumHandle + 1;
         }
         else
         {
            retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                        CASA_FACILITY_AUTHTOKEN,
                                        CASA_STATUS_BUFFER_OVERFLOW);
         }
      }
      else
      {
         retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                     CASA_FACILITY_AUTHTOKEN,
                                     CASA_STATUS_BUFFER_OVERFLOW);
      }

      // Return the attribute lengths
      *pAttribNameLen = pAttribute->attribNameLen;
      *pAttribValueLen = pAttribute->attribValueLen;
   }
   else
   {
      retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_NO_MORE_ENTRIES);
   }

exit:

   DbgTrace(2, "-AttributeEnumerate- End, retStatus = %0X\n", retStatus);

   return retStatus;
}


//++=======================================================================
CasaStatus
GetIdenTokenInterface(
   IN       const char     *pTokenBuf,
   IN       const uint32_t tokenLen,
   INOUT    IdenTokenIf    **ppIdenTokenIf)
//
// Arguments:  
//   
// Returns:
//    Casa Status
//                           
// Description:
//    Get principal interface instanced for the identity associated
//    with specified identity token.
//
// L2
//=======================================================================--
{
   IdenTokenIfInstance  *pIdenTokenIfInstance;
   CasaStatus           retStatus;

   DbgTrace(2, "-GetIdenTokenInterface- Start\n", 0);

   // Verify that the token is not too large for the parser
   if (tokenLen > INT32_MAX)
   {
      DbgTrace(0, "-GetIdenTokenInterface- Token too large\n", 0);
      retStatus = CasaStatusBuild(CASA_SEVERITY_INFORMATIONAL,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_UNSUCCESSFUL);
      goto exit;
   }

   // Create a IdenTokenIfInstance object for it.
   pIdenTokenIfInstance = malloc(sizeof(*pIdenTokenIfInstance));
   if (pIdenTokenIfInstance)
   {
      XML_Parser     p;
      IdenTokenParse idenTokenParse = {0};

      /*
      * CASA identity tokens have the following format:
      * 
      * <?xml version="1.0" encoding="ISO-8859-1"?>
      * <casa_ident_tok>
      * <id>identity id</id>
      * <source_name>identity data source name</source_name>
      * <source_url>identity data source url</source_url>
      * <target_service>target service name</target_service>
      * <target_host>target host name</target_host>
      * <attributes>
      * <attribute name> attribute value</attribute name>
      * <attribute2 name> attribute value</attribute2 name>
      * ...
      * </attributes>
      * </casa_ident_tok>
      *
      */

      // Initialize the IdentTokenIfInstance object and set it in the
      // idenTokenParse object.
      memset(pIdenTokenIfInstance, 0, sizeof(*pIdenTokenIfInstance));
      InitializeListHead(&pIdenTokenIfInstance->attributeListHead);
      pIdenTokenIfInstance->idenTokenIf.addReference = AddReference;
      pIdenTokenIfInstance->idenTokenIf.releaseReference = ReleaseReference;
      pIdenTokenIfInstance->idenTokenIf.getIdentityId = GetIdentityId;
      pIdenTokenIfInstance->idenTokenIf.getSourceName = GetSourceName;
      pIdenTokenIfInstance->idenTokenIf.getSourceUrl = GetSourceUrl;
      pIdenTokenIfInstance->idenTokenIf.attributeEnumerate = AttributeEnumerate;

      idenTokenParse.pIdenTokenIfInstance = pIdenTokenIfInstance;

      // Create parser
      p = XML_ParserCreate(NULL);
      if (p)
      {
         // Keep track of the parser in our parse object
         idenTokenParse.p = p;

         // Initialize the status within the parse object
         idenTokenParse.status = CASA_STATUS_SUCCESS;

         // Set the start and end element handlers
         XML_SetElementHandler(p,
                               IdenTokenStartElementHandler,
                               IdenTokenEndElementHandler);

         // Set the character data handler
         XML_SetCharacterDataHandler(p, IdenTokenCharDataHandler);


         // Set our user data
         XML_SetUserData(p, &idenTokenParse);

         // Parse the document
         if (XML_Parse(p, pTokenBuf, (int) tokenLen, 1) == XML_STATUS_OK)
         {
            // Verify that the parse operation completed successfully
            if (idenTokenParse.state == DONE_PARSING)
            {
               // The parse operation succeded.
               retStatus = CASA_STATUS_SUCCESS;
            }
            else
            {
               DbgTrace(0, "-GetIdenTokenInterface- Parse operation did not complete\n", 0);

               // Check if a status has been recorded
               if (idenTokenParse.status != CASA_STATUS_SUCCESS)
               {
                  retStatus = idenTokenParse.status;
               }
               else
               {
                  retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                              CASA_FACILITY_AUTHTOKEN,
                                              CASA_STATUS_PROTOCOL_ERROR);
               }
            }
         }
         else
         {
            DbgTrace(0, "-GetIdenTokenInterface- Parse error %d\n", XML_GetErrorCode(p));
            retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                        CASA_FACILITY_AUTHTOKEN,
                                        CASA_STATUS_PROTOCOL_ERROR);
         }

         // Free the parser
         XML_ParserFree(p);
      }
      else
      {
         DbgTrace(0, "-CreateAuthToken- Parser creation error\n", 0);
         retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                     CASA_FACILITY_AUTHTOKEN,
                                     CASA_STATUS_INSUFFICIENT_RESOURCES);
      }


      // Return the Identity Token Interface to the caller if successful
      if (CASA_SUCCESS(retStatus))
      {
         // Return the IdenTokenIf associated with the instance data after
         // incrementing its reference count.
         pIdenTokenIfInstance->refCount ++;
         *ppIdenTokenIf = &pIdenTokenIfInstance->idenTokenIf;

         // Bump up our interface instance count
         PlatAcquireMutex(g_idenTokenIfMutex);
         g_numIdenTokenIfObjs ++;
         PlatReleaseMutex(g_idenTokenIfMutex);
      }
      else
      {
         FreeIdenTokenIfInstance(pIdenTokenIfInstance);
      }
   }
   else
   {
      DbgTrace(0, "-GetIdenTokenInterface- Buffer allocation failure\n", 0);
      retStatus = CasaStatusBuild(CASA_SEVERITY_INFORMATIONAL,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_INSUFFICIENT_RESOURCES);
   }

exit:

   DbgTrace(2, "-GetIdenTokenInterface- End, retStatus = %0X\n", retStatus);

   return retStatus;
}


//++=======================================================================
CasaStatus
IdenTokenIfInit(void)
//
// Arguments:  
//   
// Returns:
//    Casa Status
//                           
// Description:
//    Initializes the identity token interface complex.
//
// L2
//=======================================================================--
{
   CasaStatus        retStatus;

   DbgTrace(1, "-IdenTokenIfInit- Start\n", 0);

   // Allocate mutex
   if ((g_idenTokenIfMutex = PlatAllocMutex()) != NULL)
      retStatus = CASA_STATUS_SUCCESS;
   else
      retStatus = CasaStatusBuild(CASA_SEVERITY_INFORMATIONAL,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_INSUFFICIENT_RESOURCES);

   DbgTrace(1, "-IdenTokenIfInit- End, retStatus = %0X\n", retStatus);

   return retStatus;
}


//++=======================================================================
void
IdenTokenIfUninit(void)
//
// Arguments:  
//   
// Returns:
//    Casa Status
//                           
// Description:
//    Uninitializes the configuration interface complex.
//
// L2
//=======================================================================--
{
   DbgTrace(1, "-IdenTokenIfUninit- Start\n", 0);

   // Free mutex if necessary
   if (g_idenTokenIfMutex)
   {
      PlatDestroyMutex(g_idenTokenIfMutex);
      g_idenTokenIfMutex = NULL;
   }

   DbgTrace(1, "-IdenTokenIfUninit- End\n", 0);
}


//++=======================================================================
//++=======================================================================
//++=======================================================================