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

#define INITIAL_RESPONSE_DATA_BUF_SIZE 1028
#define INCREMENT_RESPONSE_DATA_BUF_SIZE 256

#define MAX_RPC_RETRIES 3

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

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

//++=======================================================================
static
CasaStatus
CopyMultiToWideAlloc(
   IN    char *pMulti,
   IN    int multiSize,
   INOUT LPWSTR *ppWide,
   INOUT int *pWideSize)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   int      retStatus;
   int      size, i;

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

   size = (multiSize + 1) * sizeof(WCHAR);

   if ((*ppWide = (PWCHAR) malloc(size)) != NULL)
   {
      for (i = 0; i < multiSize; i++)
      {
         *(*ppWide + i) = (unsigned char) *(pMulti + i);
      }

      *(*ppWide + i) = L'\0';

      if (pWideSize)
      {
         *pWideSize = size - sizeof(WCHAR);
      }

      retStatus = CASA_STATUS_SUCCESS;
   }
   else
   {
      retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_INSUFFICIENT_RESOURCES);
   }

   DbgTrace(2, "-CopyMultiToWideAlloc- End, retStatus = %08X\n", retStatus);

   return retStatus;
}


//++=======================================================================
static
CasaStatus
CopyWideToMultiAlloc(
   IN    LPWSTR pWide,
   IN    int wideSize,
   INOUT char **ppMulti,
   INOUT int *pMultiSize)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   int      retStatus;
   int      size, i;

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

   size = wideSize + 1;

   if ((*ppMulti = malloc(size)) != NULL)
   {
      for (i = 0; i < wideSize; i++)
      {
         *(*ppMulti + i) = (char) *(pWide + i);
      }

      *(*ppMulti + i) = '\0';

      if (pMultiSize)
      {
         *pMultiSize = size - 1;
      }

      retStatus = CASA_STATUS_SUCCESS;
   }
   else
   {
      retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_INSUFFICIENT_RESOURCES);
   }

   DbgTrace(2, "-CopyWideToMultiAlloc- End, retStatus = %08X\n", retStatus);

   return retStatus;
}


//++=======================================================================
RpcSession*
OpenRpcSession(
   IN    char *pHostName,
   IN    uint16_t hostPort)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   RpcSession  *pSession;
   bool        success = false;

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

   // Allocate space for the session
   pSession = (RpcSession*) malloc(sizeof(*pSession));
   if (pSession)
   {
      // Zero the session structure
      memset(pSession, 0, sizeof(*pSession));

      // Save copy of the hostname
      pSession->pHostName = malloc(strlen(pHostName) + 1);
      if (pSession->pHostName)
      {
         strcpy(pSession->pHostName, pHostName);

         // Open a Winhttp session
         pSession->hSession = WinHttpOpen(L"CASA Client/1.0",
                                          WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
                                          WINHTTP_NO_PROXY_NAME,
                                          WINHTTP_NO_PROXY_BYPASS,
                                          0);
         if (pSession->hSession)
         {
            LPWSTR   pWideHostName;
            int      wideHostLen;

            // Session opened, now convert the host name to Unicode so that
            // we can open a connection.
            if (CopyMultiToWideAlloc(pHostName,
                                     (int) strlen(pHostName),
                                     &pWideHostName,
                                     &wideHostLen) == CASA_STATUS_SUCCESS)
            {
               // Now open connection
               pSession->hConnection = WinHttpConnect(pSession->hSession,
                                                      pWideHostName,
                                                      hostPort,
                                                      0);
               if (pSession->hConnection == NULL)
               {
                  DbgTrace(0, "-OpenRpcSession- Failed to open connection, error = %d\n", GetLastError());
               }
               else
               {
                  success = true;
               }

               // Free the host name wide string buffer
               free(pWideHostName);
            }
            else
            {
               DbgTrace(0, "-OpenRpcSession- Error converting host name to wide string\n", 0);
            }
         }
         else
         {
            DbgTrace(0, "-OpenRpcSession- Failed to open session, error = %d\n", GetLastError());
         }
      }
      else
      {
         DbgTrace(0, "-OpenRpcSession- Failed to allocate buffer for host name\n", 0);
      }
   }
   else
   {
      DbgTrace(0, "-OpenRpcSession- Failed to allocate buffer for rpc session\n", 0);
   }

   // Clean up if we did not succeed
   if (!success)
   {
      if (pSession)
      {
         if (pSession->hConnection)
            WinHttpCloseHandle(pSession->hConnection);

         if (pSession->hSession)
            WinHttpCloseHandle(pSession->hSession);

         if (pSession->pHostName)
            free(pSession->pHostName);

         free(pSession);
         pSession = NULL;
      }
   }

   DbgTrace(2, "-OpenRpcSession- End, pSession = %08X\n", pSession);

   return pSession;
}


//++=======================================================================
void
CloseRpcSession(
   IN    RpcSession *pSession)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   DbgTrace(1, "-CloseRpcSession- Start\n", 0);

   // Close the connection handle
   WinHttpCloseHandle(pSession->hConnection);

   // Close the session handle
   WinHttpCloseHandle(pSession->hSession);

   // Free hostname buffer if necessary
   if (pSession->pHostName)
      free(pSession->pHostName);

   // Free the space allocated for the session
   free(pSession);

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


//++=======================================================================
static
void CALLBACK
SecureFailureStatusCallback(
   IN    HINTERNET hRequest,
   IN    DWORD *pContext,
   IN    DWORD internetStatus,
   IN    LPVOID pStatusInformation,
   IN    DWORD statusInformationLength)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L0
//=======================================================================--
{
   DbgTrace(1, "-SecureFailureStatusCallback- Start\n", 0);

   // Only deal with failures related to certificates
   if (internetStatus == WINHTTP_CALLBACK_STATUS_SECURE_FAILURE)
   {
      // Save the specific failure status
      *pContext = *(DWORD*) pStatusInformation;
   }

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


//++=======================================================================
static
CasaStatus
InternalRpc(
   IN    RpcSession *pSession,
   IN    char *pMethod,
   IN    long flags,
   IN    char *pRequestData,
   INOUT char **ppResponseData,
   INOUT int *pResponseDataLen)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
#define RPC_TARGET_FMT_STRING "CasaAuthTokenSvc/Rpc?method=%s"

#ifndef CASA_STATUS_INVALID_SERVER_CERTIFICATE
#define CASA_STATUS_INVALID_SERVER_CERTIFICATE ((CasaStatus)0x00000023)
#endif

   CasaStatus  retStatus = CASA_STATUS_SUCCESS;
   char        *pRpcTarget;
   LPWSTR      pWideRpcTarget;
   int         wideRpcTargetLen;
   WCHAR       sendHeaders[] = L"Content-Type: text/html";
   DWORD       securityFailureStatusFlags;
   int         retriesAllowed = 1;
   bool        attemptRetry;

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

   // Initialize output parameter
   *ppResponseData = NULL;

   // Create rpc target string and convert it to a wide string
   pRpcTarget = (char*) malloc(sizeof(RPC_TARGET_FMT_STRING) + strlen(pMethod));
   if (pRpcTarget)
   {
      sprintf(pRpcTarget, RPC_TARGET_FMT_STRING, pMethod);
      retStatus = CopyMultiToWideAlloc(pRpcTarget,
                                       (int) strlen(pRpcTarget),
                                       &pWideRpcTarget,
                                       &wideRpcTargetLen);
      if (CASA_SUCCESS(retStatus))
      {
         HINTERNET   hRequest;

         do
         {
            // Forget about having been told to retry
            attemptRetry = false;

            // Open a request handle
            hRequest = WinHttpOpenRequest(pSession->hConnection,
                                          L"POST",
                                          pWideRpcTarget,
                                          NULL,
                                          WINHTTP_NO_REFERER,
                                          WINHTTP_DEFAULT_ACCEPT_TYPES,
                                          flags & SECURE_RPC_FLAG? WINHTTP_FLAG_REFRESH | WINHTTP_FLAG_SECURE : WINHTTP_FLAG_REFRESH);
            if (hRequest)
            {
               int   reqDataLen = (int) strlen(pRequestData);

               // Check if we need to set options to deal with secure connections
               if (flags & SECURE_RPC_FLAG)
               {
                  // We are using secure connections, now proceed based on whether or not
                  // we are configured to allow invalid certificates.
                  if (flags & ALLOW_INVALID_CERTS_RPC_FLAG
                      || (flags & ALLOW_INVALID_CERTS_USER_APPROVAL_RPC_FLAG
                          && InvalidCertsFromHostAllowed(pSession->pHostName)))
                  {
                     DWORD secFlags = SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_UNKNOWN_CA;

                     // We are configured to allow invalid certificates, inform the HTTP stack.
                     if (WinHttpSetOption(hRequest,
                                          WINHTTP_OPTION_SECURITY_FLAGS,
                                          &secFlags,
                                          sizeof(secFlags)) == FALSE)
                     {
                        DbgTrace(0, "-InternalRpc- Failed setting options to ignore invalid certs, error = %d\n", GetLastError());
                     }
                  }
                  else
                  {
                     // We are not configured to allow invalid certificates, set a callback handler
                     // to detect invalid certificate conditions.
                     if (WinHttpSetStatusCallback(hRequest,
                                                  SecureFailureStatusCallback,
                                                  WINHTTP_CALLBACK_FLAG_SECURE_FAILURE,
                                                  (DWORD_PTR) NULL) == WINHTTP_INVALID_STATUS_CALLBACK)
                     {
                        DbgTrace(0, "-InternalRpc- Failed setting status callback, error = %d\n", GetLastError());
                     }
                  }
               }

               // Send the request
               securityFailureStatusFlags = 0;
               if (WinHttpSendRequest(hRequest,
                                      sendHeaders,
                                      -1,
                                      pRequestData,
                                      reqDataLen,
                                      reqDataLen,
                                      (DWORD_PTR) &securityFailureStatusFlags))
               {
                  // Request sent, now await for the response.
                  if (WinHttpReceiveResponse(hRequest, NULL))
                  {
                     WCHAR httpCompStatus[4] = {0};
                     DWORD httpCompStatusLen = sizeof(httpCompStatus);

                     // Response received, make sure that it completed successfully.
                     if (WinHttpQueryHeaders(hRequest,
                                             WINHTTP_QUERY_STATUS_CODE,
                                             NULL,
                                             &httpCompStatus,
                                             &httpCompStatusLen,
                                             WINHTTP_NO_HEADER_INDEX))
                     {
                        // Check that the request completed successfully
                        if (memcmp(httpCompStatus, L"200", sizeof(httpCompStatus)) == 0)
                        {
                           char  *pResponseData;
                           int   responseDataBufSize = INITIAL_RESPONSE_DATA_BUF_SIZE;      
                           int   responseDataRead = 0;

                           // Now read the response data, to do so we need to allocate a buffer.
                           pResponseData = (char*) malloc(INITIAL_RESPONSE_DATA_BUF_SIZE);
                           if (pResponseData)
                           {
                              char     *pCurrLocation = pResponseData;
                              DWORD    bytesRead;

                              do
                              {
                                 bytesRead = 0;
                                 if (WinHttpReadData(hRequest,
                                                     (LPVOID) pCurrLocation,
                                                     responseDataBufSize - responseDataRead,
                                                     &bytesRead))
                                 {
                                    pCurrLocation += bytesRead;
                                    responseDataRead += bytesRead;

                                    // Check if we need to allocate a larger buffer
                                    if (responseDataRead == responseDataBufSize)
                                    {
                                       char  *pTmpBuf;

                                       // We need to upgrade the receive buffer
                                       pTmpBuf = (char*) malloc(responseDataBufSize + INCREMENT_RESPONSE_DATA_BUF_SIZE);
                                       if (pTmpBuf)
                                       {
                                          memcpy(pTmpBuf, pResponseData, responseDataBufSize);
                                          free(pResponseData);
                                          pResponseData = pTmpBuf;
                                          pCurrLocation = pResponseData + responseDataBufSize;
                                          responseDataBufSize += INCREMENT_RESPONSE_DATA_BUF_SIZE;
                                       }
                                       else
                                       {
                                          DbgTrace(0, "-InternalRpc- Buffer allocation failure\n", 0);
                                          retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                                                      CASA_FACILITY_AUTHTOKEN,
                                                                      CASA_STATUS_INSUFFICIENT_RESOURCES);
                                       }
                                    }
                                 }
                                 else
                                 {
                                    DbgTrace(0, "-InternalRpc- Failed reading response data, error = %d\n", GetLastError());
                                    retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                                                CASA_FACILITY_AUTHTOKEN,
                                                                CASA_STATUS_UNSUCCESSFUL);
                                 }
                              } while (CASA_SUCCESS(retStatus)
                                       && bytesRead != 0);

                              // Check if the response data was successfully received
                              if (CASA_SUCCESS(retStatus))
                              {
                                 // The response data was received, return it to the caller.
                                 *ppResponseData = pResponseData;
                                 *pResponseDataLen = responseDataRead; 
                              }
                              else
                              {
                                 // Failed to receive the response data, free the allocated buffer.
                                 free(pResponseData);
                              }
                           }
                           else
                           {
                              DbgTrace(0, "-InternalRpc- Buffer allocation failure\n", 0);
                              retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                                          CASA_FACILITY_AUTHTOKEN,
                                                          CASA_STATUS_INSUFFICIENT_RESOURCES);
                           }
                        }
                        else
                        {
                           DbgTrace(0, "-InternalRpc- HTTP request did not complete successfully, status = %S\n", httpCompStatus);
                           retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                                       CASA_FACILITY_AUTHTOKEN,
                                                       CASA_STATUS_UNSUCCESSFUL);
                        }
                     }
                     else
                     {
                        DbgTrace(0, "-InternalRpc- Unable to obtain http request completion status, error = %d\n", GetLastError());
                        retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                                    CASA_FACILITY_AUTHTOKEN,
                                                    CASA_STATUS_UNSUCCESSFUL);
                     }
                  }
                  else
                  {
                     DbgTrace(0, "-InternalRpc- Unable to receive response, error = %d\n", GetLastError());
                     retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                                 CASA_FACILITY_AUTHTOKEN,
                                                 CASA_STATUS_UNSUCCESSFUL);
                  }
               }
               else
               {
                  int   error = GetLastError();

                  if (error == ERROR_WINHTTP_CANNOT_CONNECT)
                  {
                     DbgTrace(0, "-InternalRpc- Unable to connect to server\n", 0);
                     retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                                 CASA_FACILITY_AUTHTOKEN,
                                                 CASA_STATUS_AUTH_SERVER_UNAVAILABLE);
                  }
                  else if (error == ERROR_WINHTTP_SECURE_FAILURE)
                  {
                     DbgTrace(1, "-InternalRpc- Secure connection failure, flags = %0x\n", securityFailureStatusFlags);

                     // Try to deal with the issue
                     if ((securityFailureStatusFlags & ~(WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA
                                                         | WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID
                                                         | WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID)) == 0
                         && flags & ALLOW_INVALID_CERTS_USER_APPROVAL_RPC_FLAG)
                     {
                        WINHTTP_CERTIFICATE_INFO certInfo;
                        DWORD certInfoLen = sizeof(certInfo);

                        // The failure was due to an invalid CN, CA, or both.
                        //
                        // Obtain information about the server certificate to give user
                        // the choice of accepting it.
                        if (WinHttpQueryOption(hRequest,
                                               WINHTTP_OPTION_SECURITY_CERTIFICATE_STRUCT,
                                               &certInfo,
                                               &certInfoLen)
                            && certInfo.lpszSubjectInfo != NULL
                            && certInfo.lpszIssuerInfo != NULL)
                        {
                           char  *pSubjectInfo;
                           int   subjectInfoLen;

                           // Convert the subjectInfo to multi-byte
                           retStatus = CopyWideToMultiAlloc(certInfo.lpszSubjectInfo,
                                                            (int) wcslen(certInfo.lpszSubjectInfo),
                                                            &pSubjectInfo,
                                                            &subjectInfoLen);
                           if (CASA_SUCCESS(retStatus))
                           {
                              char  *pIssuerInfo;
                              int   issuerInfoLen;

                              // Convert the issuerInfo to multi-byte
                              retStatus = CopyWideToMultiAlloc(certInfo.lpszIssuerInfo,
                                                               (int) wcslen(certInfo.lpszIssuerInfo),
                                                               &pIssuerInfo,
                                                               &issuerInfoLen);
                              if (CASA_SUCCESS(retStatus))
                              {
                                 long  invalidCertFlags = 0;

                                 // Setup the invalid cert flags
                                 if (securityFailureStatusFlags & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA)
                                    invalidCertFlags |= INVALID_CERT_CA_FLAG;

                                 if (securityFailureStatusFlags & WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID)
                                    invalidCertFlags |= INVALID_CERT_CN_FLAG;

                                 if (securityFailureStatusFlags & WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID)
                                    invalidCertFlags |= INVALID_CERT_DATE_FLAG;

                                 // Give user the choice to accept the certificate
                                 if (UserApprovedCert(pSession->pHostName,
                                                      pSubjectInfo,
                                                      pIssuerInfo,
                                                      invalidCertFlags))
                                 {
                                    DbgTrace(1, "-InternalRpc- User approved invalid certificate from %s\n", pSession->pHostName);

                                    // tbd - Investigate if there is a way to set the accepted certificate in a store so that
                                    // it can be utilized by the SSL stack directly. This would be a better method for dealing with
                                    // this issue.

                                    AllowInvalidCertsFromHost(pSession->pHostName);

                                    // Try to retry the request
                                    attemptRetry = true;
                                 }
                                 else
                                 {
                                    DbgTrace(1, "-InternalRpc- User did not approve invalid certificate from %s\n", pSession->pHostName);

                                    retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                                                CASA_FACILITY_AUTHTOKEN,
                                                                CASA_STATUS_INVALID_SERVER_CERTIFICATE);
                                 }

                                 // Free the buffer containing the issuerInfo
                                 free(pIssuerInfo);
                              }

                              // Free the buffer containing the subjectInfo
                              free(pSubjectInfo);
                           }

                           // Free necessary certificate information
                           if (certInfo.lpszSubjectInfo) LocalFree(certInfo.lpszSubjectInfo);
                           if (certInfo.lpszIssuerInfo) LocalFree(certInfo.lpszIssuerInfo);
                           if (certInfo.lpszProtocolName) LocalFree(certInfo.lpszProtocolName);
                           if (certInfo.lpszSignatureAlgName) LocalFree(certInfo.lpszSignatureAlgName);
                           if (certInfo.lpszEncryptionAlgName) LocalFree(certInfo.lpszEncryptionAlgName);
                        }
                        else
                        {
                           DbgTrace(0, "-InternalRpc- Unable to obtain server certificate struct, error = %0x\n", GetLastError());
                        }
                     }
                     else
                     {
                        // Decided to no give the user a choice to accept invalid server certificate
                        retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                                    CASA_FACILITY_AUTHTOKEN,
                                                    CASA_STATUS_INVALID_SERVER_CERTIFICATE);
                     }
                  }
                  else
                  {
                     DbgTrace(0, "-InternalRpc- Unsuccessful send http request, error = %d\n", error);
                     retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                                 CASA_FACILITY_AUTHTOKEN,
                                                 CASA_STATUS_UNSUCCESSFUL);
                  }
               }

               // Close the request handle
               WinHttpCloseHandle(hRequest);
            }
            else
            {
               DbgTrace(0, "-InternalRpc- Unable to open http request, error = %d\n", GetLastError());
               retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                           CASA_FACILITY_AUTHTOKEN,
                                           CASA_STATUS_UNSUCCESSFUL);
            }
         } while (attemptRetry && retriesAllowed--);

         // Free the rpc target wide string buffer
         free(pWideRpcTarget);
      }
      else
      {
         DbgTrace(0, "-InternalRpc- Error converting method name to wide string\n", 0);
      }

      // Free buffer used to hold the rpc target string
      free(pRpcTarget);
   }
   else
   {
      DbgTrace(0, "-InternalRpc- Buffer allocation failure\n", 0);
      retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_INSUFFICIENT_RESOURCES);
   }

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

   return retStatus;
}


//++=======================================================================
CasaStatus
Rpc(
   IN    RpcSession *pSession,
   IN    char *pMethod,
   IN    long flags,
   IN    char *pRequestData,
   INOUT char **ppResponseData,
   INOUT int *pResponseDataLen)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   CasaStatus  retStatus;
   int         retries = 0;

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

   // Retry the RPC as needed
   do
   {
      // Issue the RPC
      retStatus = InternalRpc(pSession,
                              pMethod,
                              flags,
                              pRequestData,
                              ppResponseData,
                              pResponseDataLen);

      // Account for this try
      retries ++;

   } while (CasaStatusCode(retStatus) == CASA_STATUS_AUTH_SERVER_UNAVAILABLE
            && retries < MAX_RPC_RETRIES);

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

   return retStatus;
}


//++=======================================================================
CasaStatus
InitializeRpc(void)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   CasaStatus  retStatus = CASA_STATUS_SUCCESS;

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

   // Nothing to do for windows

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

   return retStatus;
}


//++=======================================================================
void
UnInitializeRpc(void)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   DbgTrace(1, "-UnInitializeRpc- Start\n", 0);

   // Nothing to do for windows

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


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