/***********************************************************************
 * 
 *  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 ]==================================================

//
// Parse states
//
#define AWAITING_ROOT_ELEMENT_START             0x0
#define AWAITING_ROOT_ELEMENT_END               0x1
#define AWAITING_STATUS_ELEMENT_START           0x2
#define AWAITING_STATUS_ELEMENT_END             0x3
#define AWAITING_STATUS_DATA                    0x4
#define AWAITING_DESCRIPTION_ELEMENT_START      0x5
#define AWAITING_DESCRIPTION_ELEMENT_END        0x6
#define AWAITING_DESCRIPTION_DATA               0x7
#define AWAITING_SESSION_TOKEN_ELEMENT_START    0x8
#define AWAITING_SESSION_TOKEN_ELEMENT_END      0x9
#define AWAITING_SESSION_TOKEN_DATA             0xA
#define AWAITING_LIFETIME_DATA                  0xB
#define AWAITING_LIFETIME_ELEMENT_START         0xC
#define AWAITING_LIFETIME_ELEMENT_END           0xD
#define AWAITING_AUTH_TOKEN_ELEMENT_START       0xE
#define AWAITING_AUTH_TOKEN_ELEMENT_END         0xF
#define AWAITING_AUTH_TOKEN_DATA                0x10
#define AWAITING_AUTH_POLICY_ELEMENT_START      0x11
#define AWAITING_AUTH_POLICY_ELEMENT_END        0x12
#define AWAITING_AUTH_POLICY_DATA               0x13
#define AWAITING_AUTH_SOURCE_ELEMENT_START      0x14
#define AWAITING_AUTH_SOURCE_ELEMENT_END        0x15
#define AWAITING_AUTH_SOURCE_CHILD_START        0x16
#define AWAITING_REALM_DATA                     0x17
#define AWAITING_REALM_ELEMENT_END              0x18
#define AWAITING_MECHANISM_DATA                 0x19
#define AWAITING_MECHANISM_ELEMENT_END          0x1A
#define AWAITING_MECHANISM_INFO_DATA            0x1B
#define AWAITING_MECHANISM_INFO_ELEMENT_END     0x1C
#define AWAITING_UNKNOWN_DATA                   0x1D
#define AWAITING_UNKNOWN_ELEMENT_END            0x1E
#define DONE_PARSING                            0x1F

//
// Authentication Policy Parse Structure
//
typedef struct _AuthPolicyParse
{
   XML_Parser              p;
   int                     state;
   AuthPolicy              *pAuthPolicy;
   CasaStatus              status;

} AuthPolicyParse, *PAuthPolicyParse;


//
// Get Authentication Policy Response Parse Structure
//
typedef struct _GetAuthPolicyRespParse
{
   XML_Parser              p;
   int                     state;
   GetAuthPolicyResp       *pGetAuthPolicyResp;
   CasaStatus              status;

} GetAuthPolicyRespParse, *PGetAuthPolicyRespParse;


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

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

//++=======================================================================
static
void XMLCALL
AuthPolicyStartElementHandler(
   IN    AuthPolicyParse *pAuthPolicyParse,
   IN    const XML_Char *name,
   IN    const XML_Char **atts)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L0
//=======================================================================--
{
   DbgTrace(2, "-AuthPolicyStartElementHandler- Start\n", 0);

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

         // In this state, we are only expecting the Authentication
         // Policy Element.
         if (strcmp(name, AUTH_POLICY_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pAuthPolicyParse->state = AWAITING_AUTH_SOURCE_ELEMENT_START;
         }
         else
         {
            DbgTrace(0, "-AuthPolicyStartElementHandler- Un-expected start element\n", 0);
            XML_StopParser(pAuthPolicyParse->p, XML_FALSE);
         }
         break;

   case AWAITING_AUTH_SOURCE_ELEMENT_START:
   case AWAITING_ROOT_ELEMENT_END:

      // In this state, we are only expecting the Authentication
      // Source Element.
      if (strcmp(name, AUTH_SOURCE_ELEMENT_NAME) == 0)
      {
         AuthContext *pAuthContext;

         // Create an authentication context structure
         pAuthContext = (AuthContext*) malloc(sizeof(AuthContext));
         if (pAuthContext)
         {
            // Initialize the allocated AuthContext structure and associate it
            // with the AuthPolicy structure.
            memset(pAuthContext, 0, sizeof(*pAuthContext));
            InsertTailList(&pAuthPolicyParse->pAuthPolicy->authContextListHead, &pAuthContext->listEntry);
         }
         else
         {
            DbgTrace(0, "-AuthPolicyStartElementHandler- Buffer allocation error\n", 0);
            XML_StopParser(pAuthPolicyParse->p, XML_FALSE);
         }

         // Good, advance to the next state.
         pAuthPolicyParse->state = AWAITING_AUTH_SOURCE_CHILD_START;
      }
      else
      {
         DbgTrace(0, "-AuthPolicyStartElementHandler- Un-expected start element\n", 0);
         XML_StopParser(pAuthPolicyParse->p, XML_FALSE);
      }
      break;

      case AWAITING_AUTH_SOURCE_CHILD_START:

         // Proceed based on the name of the element
         if (strcmp(name, REALM_ELEMENT_NAME) == 0)
         {
            // Advance to the next state.
            pAuthPolicyParse->state = AWAITING_REALM_DATA;
         }
         else if (strcmp(name, MECHANISM_ELEMENT_NAME) == 0)
         {
            // Advance to the next state.
            pAuthPolicyParse->state = AWAITING_MECHANISM_DATA;
         }
         else if (strcmp(name, MECHANISM_INFO_ELEMENT_NAME) == 0)
         {
            // Advance to the next state.
            pAuthPolicyParse->state = AWAITING_MECHANISM_INFO_DATA;
         }
         else if (strcmp(name, AUTH_SOURCE_ELEMENT_NAME) == 0)
         {
            // We are starting a new auth source entry, create an authentication
            // context structure to hold its information.
            AuthContext *pAuthContext;

            // Create an authentication context structure
            pAuthContext = (AuthContext*) malloc(sizeof(AuthContext));
            if (pAuthContext)
            {
               // Initialize the allocated AuthContext structure and associate it
               // with the AuthPolicy structure.
               memset(pAuthContext, 0, sizeof(*pAuthContext));
               InsertTailList(&pAuthPolicyParse->pAuthPolicy->authContextListHead, &pAuthContext->listEntry);
            }
            else
            {
               DbgTrace(0, "-AuthPolicyStartElementHandler- Buffer allocation error\n", 0);
               XML_StopParser(pAuthPolicyParse->p, XML_FALSE);
            }
         }
         else
         {
            // Advance to the next state.
            pAuthPolicyParse->state = AWAITING_UNKNOWN_DATA;
         }
         break;
   
      default:
         DbgTrace(0, "-AuthPolicyStartElementHandler- Un-expected state = %d\n", pAuthPolicyParse->state);
         XML_StopParser(pAuthPolicyParse->p, XML_FALSE);
         break;
   }

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


//++=======================================================================
static
void XMLCALL
AuthPolicyCharDataHandler(
   IN    AuthPolicyParse *pAuthPolicyParse,
   IN    const XML_Char *s,
   IN    int len)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L0
//=======================================================================--
{
   AuthContext *pAuthContext;

   DbgTrace(2, "-AuthPolicyCharDataHandler- 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 (pAuthPolicyParse->state)
   {
      case AWAITING_REALM_DATA:

         // Get access to the AuthContext at the tail of the list
         pAuthContext = CONTAINING_RECORD(pAuthPolicyParse->pAuthPolicy->authContextListHead.Blink,
                                          AuthContext,
                                          listEntry);

         // Keep a copy of the realm data (null terminated)
         pAuthContext->pContext = (char*) malloc(len + 1);
         if (pAuthContext->pContext)
         {
            memset(pAuthContext->pContext, 0, len + 1);
            memcpy(pAuthContext->pContext, s, len);

            // Advanced to the next state
            pAuthPolicyParse->state = AWAITING_REALM_ELEMENT_END;
         }
         else
         {
            DbgTrace(0, "-AuthPolicyCharDataHandler- Buffer allocation failure\n", 0);
            pAuthPolicyParse->status = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                                       CASA_FACILITY_AUTHTOKEN,
                                                       CASA_STATUS_INSUFFICIENT_RESOURCES);
            XML_StopParser(pAuthPolicyParse->p, XML_FALSE);
         }
         break;

      case AWAITING_MECHANISM_DATA:

         // Get access to the AuthContext at the tail of the list
         pAuthContext = CONTAINING_RECORD(pAuthPolicyParse->pAuthPolicy->authContextListHead.Blink,
                                          AuthContext,
                                          listEntry);
   
         // Keep a copy of the mechanism data (null terminated)
         pAuthContext->pMechanism = (char*) malloc(len + 1);
         if (pAuthContext->pMechanism)
         {
            memset(pAuthContext->pMechanism, 0, len + 1);
            memcpy(pAuthContext->pMechanism, s, len);
   
            // Advanced to the next state
            pAuthPolicyParse->state = AWAITING_MECHANISM_ELEMENT_END;
         }
         else
         {
            DbgTrace(0, "-AuthPolicyCharDataHandler- Buffer allocation failure\n", 0);
            pAuthPolicyParse->status = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                                       CASA_FACILITY_AUTHTOKEN,
                                                       CASA_STATUS_INSUFFICIENT_RESOURCES);
            XML_StopParser(pAuthPolicyParse->p, XML_FALSE);
         }
         break;

      case AWAITING_MECHANISM_INFO_DATA:

         // Get access to the AuthContext at the tail of the list
         pAuthContext = CONTAINING_RECORD(pAuthPolicyParse->pAuthPolicy->authContextListHead.Blink,
                                          AuthContext,
                                          listEntry);
   
         // Keep a copy of the mechanism info data (null terminated)
         pAuthContext->pMechInfo = (char*) malloc(len + 1);
         if (pAuthContext->pMechInfo)
         {
            memset(pAuthContext->pMechInfo, 0, len + 1);
            memcpy(pAuthContext->pMechInfo, s, len);
   
            // Advanced to the next state
            pAuthPolicyParse->state = AWAITING_MECHANISM_INFO_ELEMENT_END;
         }
         else
         {
            DbgTrace(0, "-AuthPolicyCharDataHandler- Buffer allocation failure\n", 0);
            pAuthPolicyParse->status = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                                       CASA_FACILITY_AUTHTOKEN,
                                                       CASA_STATUS_INSUFFICIENT_RESOURCES);
            XML_StopParser(pAuthPolicyParse->p, XML_FALSE);
         }
         break;

      case AWAITING_UNKNOWN_DATA:

         // Just advance the state
         pAuthPolicyParse->state = AWAITING_UNKNOWN_ELEMENT_END;
         break;

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

exit:

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


//++=======================================================================
static
void XMLCALL
AuthPolicyEndElementHandler(
   IN    AuthPolicyParse *pAuthPolicyParse,
   IN    const XML_Char *name)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L0
//=======================================================================--
{
   AuthContext *pAuthContext;

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

   // Proceed based on the state
   switch (pAuthPolicyParse->state)
   {
      case AWAITING_ROOT_ELEMENT_END:
   
         // In this state, we are only expecting the Authentication
         // Policy Element.
         if (strcmp(name, AUTH_POLICY_ELEMENT_NAME) == 0)
         {
            // Done.
            pAuthPolicyParse->state = DONE_PARSING;
         }
         else
         {
            DbgTrace(0, "-AuthPolicyEndElementHandler- Un-expected end element\n", 0);
            XML_StopParser(pAuthPolicyParse->p, XML_FALSE);
         }
         break;

      case AWAITING_AUTH_SOURCE_CHILD_START:

         // In this state, we are only expecting the Authentication
         // Source Response Element.
         if (strcmp(name, AUTH_SOURCE_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pAuthPolicyParse->state = AWAITING_ROOT_ELEMENT_END;
         }
         else
         {
            DbgTrace(0, "-AuthPolicyEndHandler- Un-expected end element\n", 0);
            XML_StopParser(pAuthPolicyParse->p, XML_FALSE);
         }
         break;

      case AWAITING_REALM_ELEMENT_END:
   
         // In this state, we are only expecting the Realm Element.
         if (strcmp(name, REALM_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pAuthPolicyParse->state = AWAITING_AUTH_SOURCE_CHILD_START;
         }
         else
         {
            DbgTrace(0, "-AuthPolicyEndElementHandler- Un-expected end element\n", 0);
            XML_StopParser(pAuthPolicyParse->p, XML_FALSE);
         }
         break;

      case AWAITING_MECHANISM_ELEMENT_END:
   
         // In this state, we are only expecting the Mechanism Element.
         if (strcmp(name, MECHANISM_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pAuthPolicyParse->state = AWAITING_AUTH_SOURCE_CHILD_START;
         }
         else
         {
            DbgTrace(0, "-AuthPolicyEndElementHandler- Un-expected end element\n", 0);
            XML_StopParser(pAuthPolicyParse->p, XML_FALSE);
         }
         break;

      case AWAITING_MECHANISM_INFO_DATA:
   
         // Get access to the AuthContext at the tail of the list
         pAuthContext = CONTAINING_RECORD(pAuthPolicyParse->pAuthPolicy->authContextListHead.Blink,
                                          AuthContext,
                                          listEntry);
   
         // There was no mechanism info data. Set it to an empty string.
         pAuthContext->pMechInfo = (char*) malloc(1);
         if (pAuthContext->pMechInfo)
         {
            *pAuthContext->pMechInfo = '\0';
         }
         else
         {
            DbgTrace(0, "-AuthPolicyEndElementHandler- Buffer allocation failure\n", 0);
            pAuthPolicyParse->status = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                                       CASA_FACILITY_AUTHTOKEN,
                                                       CASA_STATUS_INSUFFICIENT_RESOURCES);
            XML_StopParser(pAuthPolicyParse->p, XML_FALSE);
            break;
         }
         // Fall through

      case AWAITING_MECHANISM_INFO_ELEMENT_END:
   
         // In this state, we are only expecting the Mechanism Info Element.
         if (strcmp(name, MECHANISM_INFO_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pAuthPolicyParse->state = AWAITING_AUTH_SOURCE_CHILD_START;
         }
         else
         {
            DbgTrace(0, "-AuthPolicyEndElementHandler- Un-expected end element\n", 0);
            XML_StopParser(pAuthPolicyParse->p, XML_FALSE);
         }
         break;
   
      case AWAITING_UNKNOWN_ELEMENT_END:
   
         // Advance to the next state.
         pAuthPolicyParse->state = AWAITING_AUTH_SOURCE_CHILD_START;
         break;

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

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


//++=======================================================================
CasaStatus
CreateAuthPolicy(
   IN    char *pEncodedData,
   IN    int encodedDataLen,
   INOUT AuthPolicy **ppAuthPolicy)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L0
//=======================================================================--
{
   CasaStatus        retStatus;
   AuthPolicy        *pAuthPolicy = NULL;
   AuthPolicyParse   authPolicyParse = {0};
   char              *pData = NULL;
   int               dataLen = 0;

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

   // Initialize output parameter
   *ppAuthPolicy = NULL;

   // Decode the data
   retStatus = DecodeData(pEncodedData,
                          encodedDataLen,
                          &pData,
                          &dataLen);
   if (CASA_SUCCESS(retStatus))
   {
      // Allocate space for the AuthPolicy structure
      pAuthPolicy = (AuthPolicy*) malloc(sizeof(*pAuthPolicy));
      if (pAuthPolicy)
      {
         XML_Parser  p;

         // Initialize the list head within the structure
         InitializeListHead(&pAuthPolicy->authContextListHead);

         // Set the AuthPolicy object in the parse oject
         authPolicyParse.pAuthPolicy = pAuthPolicy;

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

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

            // Set the start and end element handlers
            XML_SetElementHandler(p,
                                  AuthPolicyStartElementHandler,
                                  AuthPolicyEndElementHandler);

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

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

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

                  retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                              CASA_FACILITY_AUTHTOKEN,
                                              CASA_STATUS_PROTOCOL_ERROR);
               }
            }
            else
            {
               DbgTrace(0, "-CreateAuthPolicy- 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, "-CreateAuthPolicy- Parser creation error\n", 0);
            retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                        CASA_FACILITY_AUTHTOKEN,
                                        CASA_STATUS_INSUFFICIENT_RESOURCES);
         }

         // Return the AuthPolicy object to the caller if necessary
         if (CASA_SUCCESS(retStatus))
         {
            *ppAuthPolicy = pAuthPolicy;
         }
      }
      else
      {
         DbgTrace(0, "-CreateAuthPolicy- Buffer allocation error\n", 0);
         retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                     CASA_FACILITY_AUTHTOKEN,
                                     CASA_STATUS_INSUFFICIENT_RESOURCES);
      }
   }
   else
   {
      DbgTrace(0, "-CreateAuthPolicy- Buffer allocation error\n", 0);
      retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_INSUFFICIENT_RESOURCES);
   }

   // Release allocated resources if not successful
   if (!CASA_SUCCESS(retStatus)
       && pAuthPolicy)
      RelAuthPolicy(pAuthPolicy);

   DbgTrace(1, "-CreateAuthPolicy- End, retStatus = %08X\n", retStatus);

   return retStatus;
}


//++=======================================================================
void
RelAuthPolicy(
   IN    AuthPolicy *pAuthPolicy)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L0
//=======================================================================--
{
   LIST_ENTRY  *pListEntry;

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

   // Free all of the associated AuthContexts
   pListEntry = pAuthPolicy->authContextListHead.Flink;
   while (pListEntry != &pAuthPolicy->authContextListHead)
   {
      AuthContext *pAuthContext;

      // Get pointer to AuthContext structure
      pAuthContext = CONTAINING_RECORD(pListEntry, AuthContext, listEntry);

      // Free associated buffers
      if (pAuthContext->pContext)
         free(pAuthContext->pContext);
         
      if (pAuthContext->pMechanism)
         free(pAuthContext->pMechanism);
         
      if (pAuthContext->pMechInfo)
         free(pAuthContext->pMechInfo);

      // Remove the entry from the list
      RemoveEntryList(&pAuthContext->listEntry);

      // Free the AuthContext
      free(pAuthContext);

      // Advance to the next entry
      pListEntry = pAuthPolicy->authContextListHead.Flink;
   }

   // Free the AuthPolicy
   free(pAuthPolicy);

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


//++=======================================================================
char*
BuildGetAuthPolicyMsg(
   IN    char *pServiceName,
   IN    char *pHostName)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L0
//=======================================================================--
{
   char  *pMsg = NULL;
   int   bufferSize;

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

   /*
   * The format of the get authentication policy request message is as follows:
   * 
   * <?xml version="1.0" encoding="ISO-8859-1"?>
   * <get_auth_policy_req>
   * <service>service name<\service>
   * <host>host name</host>
   * </get_auth_policy_req>
   *
   */

   // Determine the buffer size necessary to hold the msg
   bufferSize = strlen(XML_DECLARATION)
                + 2  // crlf
                + 1  // <
                + strlen(GET_AUTH_POLICY_REQUEST_ELEMENT_NAME)
                + 3  // >crlf
                + 1  // <
                + strlen(SERVICE_ELEMENT_NAME)
                + 1  // >
                + strlen(pServiceName)
                + 2  // </
                + strlen(SERVICE_ELEMENT_NAME)
                + 3  // >crlf
                + 2  // </
                + strlen(HOST_ELEMENT_NAME)
                + 1  // >
                + strlen(pHostName)
                + 2  // </
                + strlen(HOST_ELEMENT_NAME)
                + 3  // >crlf
                + 2  // </
                + strlen(GET_AUTH_POLICY_REQUEST_ELEMENT_NAME)
                + 2; // >null

   // Allocate the msg buffer
   pMsg = (char*) malloc(bufferSize);
   if (pMsg)
   {
      // Now build the message
      memset(pMsg, 0, bufferSize);
      strcat(pMsg, XML_DECLARATION);
      strcat(pMsg, "\r\n");
      strcat(pMsg, "<");
      strcat(pMsg, GET_AUTH_POLICY_REQUEST_ELEMENT_NAME);
      strcat(pMsg, ">\r\n");
      strcat(pMsg, "<");
      strcat(pMsg, SERVICE_ELEMENT_NAME);
      strcat(pMsg, ">");
      strcat(pMsg, pServiceName);
      strcat(pMsg, "</");
      strcat(pMsg, SERVICE_ELEMENT_NAME);
      strcat(pMsg, ">\r\n");
      strcat(pMsg, "<");
      strcat(pMsg, HOST_ELEMENT_NAME);
      strcat(pMsg, ">");
      strcat(pMsg, pHostName);
      strcat(pMsg, "</");
      strcat(pMsg, HOST_ELEMENT_NAME);
      strcat(pMsg, ">\r\n");
      strcat(pMsg, "</");
      strcat(pMsg, GET_AUTH_POLICY_REQUEST_ELEMENT_NAME);
      strcat(pMsg, ">");
   }
   else
   {
      DbgTrace(0, "-BuildGetAuthPolicyMsg- Buffer allocation error\n", 0);
   }

   DbgTrace(1, "-BuildGetAuthPolicyMsg- End, pMsg = %08X\n", pMsg);

   return pMsg;
}


//++=======================================================================
static
void XMLCALL
GetAuthPolicyRespStartElementHandler(
   IN    GetAuthPolicyRespParse *pGetAuthPolicyRespParse,
   IN    const XML_Char *name,
   IN    const XML_Char **atts)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L0
//=======================================================================--
{
   DbgTrace(2, "-GetAuthPolicyRespStartElementHandler- Start\n", 0);

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

         // In this state, we are only expecting the Get Authentication
         // Policy Response Element.
         if (strcmp(name, GET_AUTH_POLICY_RESPONSE_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pGetAuthPolicyRespParse->state = AWAITING_STATUS_ELEMENT_START;
         }
         else
         {
            DbgTrace(0, "-GetAuthPolicyRespStartElementHandler- Un-expected start element\n", 0);
            XML_StopParser(pGetAuthPolicyRespParse->p, XML_FALSE);
         }
         break;

      case AWAITING_STATUS_ELEMENT_START:
   
         // In this state, we are only expecting the Status Element.
         if (strcmp(name, STATUS_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pGetAuthPolicyRespParse->state = AWAITING_DESCRIPTION_ELEMENT_START;
         }
         else
         {
            DbgTrace(0, "-GetAuthPolicyRespStartElementHandler- Un-expected start element\n", 0);
            XML_StopParser(pGetAuthPolicyRespParse->p, XML_FALSE);
         }
         break;

      case AWAITING_DESCRIPTION_ELEMENT_START:
   
         // In this state, we are only expecting the Description Element.
         if (strcmp(name, DESCRIPTION_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pGetAuthPolicyRespParse->state = AWAITING_DESCRIPTION_DATA;
         }
         else
         {
            DbgTrace(0, "-GetAuthPolicyRespStartElementHandler- Un-expected start element\n", 0);
            XML_StopParser(pGetAuthPolicyRespParse->p, XML_FALSE);
         }
         break;

      case AWAITING_AUTH_POLICY_ELEMENT_START:
   
         // In this state, we are only expecting the Authentication Policy Element.
         if (strcmp(name, AUTH_POLICY_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pGetAuthPolicyRespParse->state = AWAITING_AUTH_POLICY_DATA;
         }
         else
         {
            DbgTrace(0, "-GetAuthPolicyRespStartElementHandler- Un-expected start element\n", 0);
            XML_StopParser(pGetAuthPolicyRespParse->p, XML_FALSE);
         }
         break;
   
      default:
         DbgTrace(0, "-GetAuthPolicyRespStartElementHandler- Un-expected state = %d\n", pGetAuthPolicyRespParse->state);
         XML_StopParser(pGetAuthPolicyRespParse->p, XML_FALSE);
         break;
   }

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


//++=======================================================================
static
void XMLCALL
GetAuthPolicyRespCharDataHandler(
   IN    GetAuthPolicyRespParse *pGetAuthPolicyRespParse,
   IN    const XML_Char *s,
   IN    int len)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L0
//=======================================================================--
{
   DbgTrace(2, "-GetAuthPolicyRespCharDataHandler- 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 (pGetAuthPolicyRespParse->state)
   {
      case AWAITING_DESCRIPTION_DATA:

         // Ignore the status description data for now.

         // Advanced to the next state
         pGetAuthPolicyRespParse->state = AWAITING_DESCRIPTION_ELEMENT_END;
         break;

      case AWAITING_STATUS_DATA:

         // Set the appropriate status in the AuthenticationResp based on the
         // returned status.
         if (strncmp(HTTP_OK_STATUS_CODE, s, len) == 0)
         {
            pGetAuthPolicyRespParse->status = CASA_STATUS_SUCCESS;
         }
         else if (strncmp(HTTP_UNAUTHORIZED_STATUS_CODE, s, len) == 0)
         {
            pGetAuthPolicyRespParse->status = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                                             CASA_FACILITY_AUTHTOKEN,
                                                             CASA_STATUS_AUTHENTICATION_FAILURE);
         }
         else if (strncmp(HTTP_SERVER_ERROR_STATUS_CODE, s, len) == 0)
         {
            pGetAuthPolicyRespParse->status = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                                             CASA_FACILITY_AUTHTOKEN,
                                                             CASA_STATUS_SERVER_ERROR);
         }
         else
         {
            DbgTrace(0, "-GetAuthPolicyRespCharDataHandler- Un-expected status\n", 0);
            pGetAuthPolicyRespParse->status = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                                             CASA_FACILITY_AUTHTOKEN,
                                                             CASA_STATUS_UNSUCCESSFUL);
         }

         // Advanced to the next state
         pGetAuthPolicyRespParse->state = AWAITING_STATUS_ELEMENT_END;
         break;

      case AWAITING_AUTH_POLICY_DATA:
      case AWAITING_AUTH_POLICY_ELEMENT_END:

         // Check if we have already processed policy data
         if (pGetAuthPolicyRespParse->pGetAuthPolicyResp->policyLen == 0)
         {
            // Keep a copy of the policy (null terminated)
            pGetAuthPolicyRespParse->pGetAuthPolicyResp->pPolicy = (char*) malloc(len + 1);
            if (pGetAuthPolicyRespParse->pGetAuthPolicyResp->pPolicy)
            {
               memset(pGetAuthPolicyRespParse->pGetAuthPolicyResp->pPolicy, 0, len + 1);
               memcpy(pGetAuthPolicyRespParse->pGetAuthPolicyResp->pPolicy, s, len);
               pGetAuthPolicyRespParse->pGetAuthPolicyResp->policyLen = len;

               // Advanced to the next state
               pGetAuthPolicyRespParse->state = AWAITING_AUTH_POLICY_ELEMENT_END;
            }
            else
            {
               DbgTrace(0, "-GetAuthPolicyRespCharDataHandler- Buffer allocation failure\n", 0);
               pGetAuthPolicyRespParse->status = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                                                CASA_FACILITY_AUTHTOKEN,
                                                                CASA_STATUS_INSUFFICIENT_RESOURCES);
               XML_StopParser(pGetAuthPolicyRespParse->p, XML_FALSE);
            }
         }
         else
         {
            char  *pNewBuf;

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

               // Swap the buffers
               free(pGetAuthPolicyRespParse->pGetAuthPolicyResp->pPolicy);
               pGetAuthPolicyRespParse->pGetAuthPolicyResp->pPolicy = pNewBuf;
            }
            else
            {
               DbgTrace(0, "-GetAuthPolicyRespCharDataHandler- Buffer allocation failure\n", 0);
               pGetAuthPolicyRespParse->status = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                                                CASA_FACILITY_AUTHTOKEN,
                                                                CASA_STATUS_INSUFFICIENT_RESOURCES);
               XML_StopParser(pGetAuthPolicyRespParse->p, XML_FALSE);
            }
         }
         break;

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

exit:

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


//++=======================================================================
static
void XMLCALL
GetAuthPolicyRespEndElementHandler(
   IN    GetAuthPolicyRespParse *pGetAuthPolicyRespParse,
   IN    const XML_Char *name)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L0
//=======================================================================--
{
   DbgTrace(2, "-GetAuthPolicyRespEndElementHandler- Start\n", 0);

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

         // In this state, we are only expecting the Get Authentication
         // Policy Response Element.
         if (strcmp(name, GET_AUTH_POLICY_RESPONSE_ELEMENT_NAME) == 0)
         {
            // Done.
            pGetAuthPolicyRespParse->state = DONE_PARSING;
         }
         else
         {
            DbgTrace(0, "-GetAuthPolicyRespEndHandler- Un-expected end element\n", 0);
            XML_StopParser(pGetAuthPolicyRespParse->p, XML_FALSE);
         }
         break;

      case AWAITING_DESCRIPTION_ELEMENT_END:
   
         // In this state, we are only expecting the Description Element.
         if (strcmp(name, DESCRIPTION_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pGetAuthPolicyRespParse->state = AWAITING_STATUS_DATA;
         }
         else
         {
            DbgTrace(0, "-GetAuthPolicyRespEndElementHandler- Un-expected end element\n", 0);
            XML_StopParser(pGetAuthPolicyRespParse->p, XML_FALSE);
         }
         break;

      case AWAITING_STATUS_ELEMENT_END:
   
         // In this state, we are only expecting the Status Element.
         if (strcmp(name, STATUS_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state based on the status code.
            if (CASA_SUCCESS(pGetAuthPolicyRespParse->status))
            {
               // The request completed successfully
               pGetAuthPolicyRespParse->state = AWAITING_AUTH_POLICY_ELEMENT_START;
            }
            else
            {
               pGetAuthPolicyRespParse->state = AWAITING_ROOT_ELEMENT_END;
            }
         }
         else
         {
            DbgTrace(0, "-GetAuthPolicyRespEndElementHandler- Un-expected start element\n", 0);
            XML_StopParser(pGetAuthPolicyRespParse->p, XML_FALSE);
         }
         break;
   
      case AWAITING_AUTH_POLICY_ELEMENT_END:
   
         // In this state, we are only expecting the Authentication Policy Element.
         if (strcmp(name, AUTH_POLICY_ELEMENT_NAME) == 0)
         {
            // Good, advance to the next state.
            pGetAuthPolicyRespParse->state = AWAITING_ROOT_ELEMENT_END;
         }
         else
         {
            DbgTrace(0, "-GetAuthPolicyRespEndElementHandler- Un-expected start element\n", 0);
            XML_StopParser(pGetAuthPolicyRespParse->p, XML_FALSE);
         }
         break;
   
      default:
         DbgTrace(0, "-GetAuthPolicyRespEndElementHandler- Un-expected state = %d\n", pGetAuthPolicyRespParse->state);
         XML_StopParser(pGetAuthPolicyRespParse->p, XML_FALSE);
         break;
   }

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


//++=======================================================================
CasaStatus
CreateGetAuthPolicyResp(
   IN    char *pRespMsg,
   IN    int respLen,
   INOUT GetAuthPolicyResp **ppGetAuthPolicyResp)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L0
//=======================================================================--
{
   CasaStatus              retStatus = CASA_STATUS_SUCCESS;
   GetAuthPolicyRespParse  getAuthPolicyRespParse = {0};
   GetAuthPolicyResp       *pGetAuthPolicyResp;

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

   /*
   * When a get authentication policy request is processed successfully, the
   * server replies to the client with a message with the following format:
   * 
   * <?xml version="1.0" encoding="ISO-8859-1"?>
   * <get_auth_policy_resp>
   * <status><description>ok</description>200</status>
   * <auth_policy>authentication policy data</auth_policy>
   * </get_auth_policy_resp>
   * 
   * When a get authentication policy request fails to be successfully processed,
   * the server responds with an error and a error description string. The message
   * format of an unsuccessful reply is as follows:
   * 
   * <?xml version="1.0" encoding="ISO-8859-1"?>
   * <get_auth_policy_resp>
   * <status><description>status description</description>status code</status>
   * </get_auth_policy_resp>
   * 
   * Plase note that the protocol utilizes the status codes defined
   * in the HTTP 1.1 Specification.
   *
   */

   // Allocate GetAuthPolicyResp object
   pGetAuthPolicyResp = malloc(sizeof(*pGetAuthPolicyResp));
   if (pGetAuthPolicyResp)
   {
      XML_Parser  p;

      // Initialize the GetAuthPolicyResp object and set it in the
      // parse oject.
      memset(pGetAuthPolicyResp, 0, sizeof(*pGetAuthPolicyResp));
      getAuthPolicyRespParse.pGetAuthPolicyResp = pGetAuthPolicyResp;

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

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

         // Set the start and end element handlers
         XML_SetElementHandler(p,
                               GetAuthPolicyRespStartElementHandler,
                               GetAuthPolicyRespEndElementHandler);

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


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

         // Parse the document
         if (XML_Parse(p, pRespMsg, respLen, 1) == XML_STATUS_OK)
         {
            // Verify that the parse operation completed successfully
            if (getAuthPolicyRespParse.state == DONE_PARSING)
            {
               // The parse operation succeded, obtain the status returned
               // by the server.
               retStatus = getAuthPolicyRespParse.status;
            }
            else
            {
               DbgTrace(0, "-CreateGetAuthPolicyResp- Parse operation did not complete\n", 0);

               // Check if a status has been recorded
               if (getAuthPolicyRespParse.status != CASA_STATUS_SUCCESS)
               {
                  retStatus = getAuthPolicyRespParse.status;
               }
               else
               {
                  retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                              CASA_FACILITY_AUTHTOKEN,
                                              CASA_STATUS_PROTOCOL_ERROR);
               }
            }
         }
         else
         {
            DbgTrace(0, "-CreateGetAuthPolicyResp- 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, "-CreateGetAuthPolicyResp- Parser creation error\n", 0);
         retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                     CASA_FACILITY_AUTHTOKEN,
                                     CASA_STATUS_INSUFFICIENT_RESOURCES);
      }

      // Return the AuthenticationResp object to the caller if necessary
      if (CASA_SUCCESS(retStatus))
      {
         *ppGetAuthPolicyResp = pGetAuthPolicyResp;
      }
      else
      {
         free(pGetAuthPolicyResp);
      }
   }
   else
   {
      DbgTrace(0, "-CreateGetAuthPolicyResp- Memory allocation error\n", 0);
      retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_INSUFFICIENT_RESOURCES);
   }
   DbgTrace(1, "-CreateGetAuthPolicyResp- End, retStatus = %08X\n", retStatus);

   return retStatus;
}


//++=======================================================================
void
RelGetAuthPolicyResp(
   IN    GetAuthPolicyResp *pGetAuthPolicyResp)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L0
//=======================================================================--
{
   DbgTrace(1, "-RelGetAuthPolicyResp- Start\n", 0);

   // Free the buffer holding the authentication policy
   if (pGetAuthPolicyResp->pPolicy)
      free(pGetAuthPolicyResp->pPolicy);

   // Free the GetAuthPolicyResp
   free(pGetAuthPolicyResp);

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