/***********************************************************************
 * 
 *  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 "platform.h"

// Externals
extern
char     *pServerAddress;

extern
int      serverPort;

extern
bool     execHttpTest;

extern
char     serviceName[];

extern
char     *pServiceName;


/***********************************************************************
 *
 * EncodeData()
 *
 ***********************************************************************/
int
EncodeData(
   IN    const void *pData,
   IN    const int32_t dataLen,
   INOUT char **ppEncodedData,
   INOUT int32_t *pEncodedDataLen)
{
   int8_t      base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
   int         retStatus;
   int         encodedSize;

   char        *pTmp;

   // Determine the encoded size and allocate a buffer to hold the encoded data
   encodedSize = ((dataLen * 4 + 2) / 3) - (dataLen % 3 ) + 4;
   pTmp = (char*) malloc(encodedSize);
   *ppEncodedData = pTmp;
   if (*ppEncodedData)
   {
      uint8_t  *pOut, *pIn;
      int      i;

      // Setup pointers to move through the buffers
      pIn = (uint8_t*) pData;
      pOut = (uint8_t*) *ppEncodedData;

      // Perform the encoding
      for (i = 0; i < dataLen - 2; i += 3)
      {
          *pOut++ = base64[(pIn[i] >> 2) & 0x3F];
          *pOut++ = base64[((pIn[i] & 0x3) << 4) |
                          ((int32_t)(pIn[i + 1] & 0xF0) >> 4)];
          *pOut++ = base64[((pIn[i + 1] & 0xF) << 2) |
                          ((int32_t)(pIn[i + 2] & 0xC0) >> 6)];
          *pOut++ = base64[pIn[i + 2] & 0x3F];
      }
      if (i < dataLen)
      {
          *pOut++ = base64[(pIn[i] >> 2) & 0x3F];
          if (i == (dataLen - 1))
          {
              *pOut++ = base64[((pIn[i] & 0x3) << 4)];
              *pOut++ = '=';
          }
          else
          {
              *pOut++ = base64[((pIn[i] & 0x3) << 4) |
                              ((int32_t)(pIn[i + 1] & 0xF0) >> 4)];
              *pOut++ = base64[((pIn[i + 1] & 0xF) << 2)];
          }
          *pOut++ = '=';
      }
      *pOut++ = '\0';

      // Return the encoded data length
      *pEncodedDataLen = (int32_t)(pOut - (uint8_t*)*ppEncodedData); 

      // Success
      retStatus = 0;
   }
   else
   {
      printf("-EncodeData- Buffer allocation failure\n");
      retStatus = -1;
   }

   return retStatus;
}


/***********************************************************************
 *
 * NonHttpTest()
 *
 ***********************************************************************/
void NonHttpTest(void)
{
   CasaStatus  retStatus;
   char        authToken[8192];
   int         authTokenLen = sizeof(authToken);

   // Obtain an authentication token for the targeted service
   retStatus = ObtainAuthToken(pServiceName, pServerAddress, authToken, &authTokenLen);
   if (!CASA_SUCCESS(retStatus))
   {
      printf("-NonHttpTest- ObtainAuthToken failed with status %d\n", retStatus);
   }
   else
   {
      SOCKET               sock;
      struct sockaddr_in   localAddr = {0};
      struct sockaddr_in   remoteAddr = {0};
      struct linger        linger_opt = {1, 15};
      struct hostent       *pLookupResult;

      printf("-NonHttpTest- ObtainAuthToken succedded, tokenlen = %d\n", authTokenLen);

      // Send the token to the server
      //
      // Open socket
      sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
      if (sock != INVALID_SOCKET)
      {
         // Setup the local address structure
         localAddr.sin_family = AF_INET;
         localAddr.sin_addr.s_addr = htonl(INADDR_ANY);

         // Bind socket
         if (!bind(sock, (const struct sockaddr*) &localAddr, sizeof(struct sockaddr_in)))
         {
            // Resolve the server address
            pLookupResult = gethostbyname(pServerAddress);
            if (pLookupResult)
            {
               // Validate the address type returned
               if (pLookupResult->h_addrtype == AF_INET)
               {
                  int   numAddressesFound = 0;

                  // Determine how many addresses where returned
                  while (pLookupResult->h_addr_list[numAddressesFound] != NULL)
                  {
                     //printf("ServerAddress = %08X\n", *((int*) pLookupResult->h_addr_list[numAddressesFound]));
                     numAddressesFound ++;
                  }
                  //printf("Found %d addresses\n", numAddressesFound);

                  // Setup the remote address structure with the lookup results
                  remoteAddr.sin_family = AF_INET;
                  remoteAddr.sin_port = serverPort;
                  remoteAddr.sin_addr.s_addr = *((int*) pLookupResult->h_addr_list[0]); // Short-cut
                  //printf("ServerAddress = %08X\n", remoteAddr.sin_addr.s_addr);

                  // Perform connect operation
                  if (connect(sock,
                              (struct sockaddr*) &remoteAddr,
                              sizeof(struct sockaddr_in)) == SOCKET_ERROR)
                  {
                     printf("-NonHttpTest- Connection creation failed, error = %d\n", errno);
                  }
                  else
                  {
                     // Now the connection is setup, send the credentials to the server as one line.
                     // using our cheesy protocol followed by a hello string.
                     //
                     // Send the token to the server (including NULL terminator)
                     send(sock, authToken, (int) strlen(authToken) + 1, 0);

                     // Send new line
                     send(sock, "\n", 1, 0);

                     // Send "hello"
                     //send(sock, helloString, strlen(helloString) + 1, MSG_NOSIGNAL);

                     // Send new line
                     //send(sock, "\n", 1, 0);

                     // Shutdown the connection
                     shutdown(sock, 0);
                  }
               }
               else
               {
                  printf("-NonHttpTest- Unsupported address type returned %08X\n", pLookupResult->h_addrtype);
               }
            }
            else
            {
               printf("-NonHttpTest- Lookup for %s failed\n", pServerAddress);
            }
         }
         else
         {
            printf("-NonHttpTest- Unable to bind socket, error = %d", errno);
         }

         // Close the socket
         setsockopt(sock, SOL_SOCKET, SO_LINGER, (const char*) &linger_opt, sizeof(linger_opt));
         closesocket(sock);
      }
      else
      {
         printf("-NonHttpTest- Unable to open socket, error = %d\n", errno);
      }
   }
}


/***********************************************************************
 *
 * HttpTest()
 *
 ***********************************************************************/
void HttpTest(void)
{
   CasaStatus  retStatus;
   char        authToken[4096];
   int         authTokenLen = sizeof(authToken);

   // Obtain an authentication token for the targeted service
   retStatus = ObtainAuthToken(pServiceName, pServerAddress, authToken, &authTokenLen);
   if (!CASA_SUCCESS(retStatus))
   {
      printf("-HttpTest- ObtainAuthToken failed with status %d\n", retStatus);
   }
   else
   {
      SOCKET               sock;
      struct sockaddr_in   localAddr = {0};
      struct sockaddr_in   remoteAddr = {0};
      struct linger        linger_opt = {1, 15};
      struct hostent       *pLookupResult;

      //printf("ObtainAuthToken succedded, token = %s\n", authToken);
      printf("-HttpTest- ObtainAuthToken succedded, tokenlen = %d\n", authTokenLen);

      // Send the token to the server
      // Open socket
      sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
      if (sock != INVALID_SOCKET)
      {
         // Setup the local address structure
         localAddr.sin_family = AF_INET;
         localAddr.sin_addr.s_addr = htonl(INADDR_ANY);

         // Bind socket
         if (!bind(sock, (const struct sockaddr*) &localAddr, sizeof(struct sockaddr_in)))
         {
            // Resolve the server address
            pLookupResult = gethostbyname(pServerAddress);
            if (pLookupResult)
            {
               // Validate the address type returned
               if (pLookupResult->h_addrtype == AF_INET)
               {
                  int   numAddressesFound = 0;

                  // Determine how many addresses where returned
                  while (pLookupResult->h_addr_list[numAddressesFound] != NULL)
                  {
                     //printf("ServerAddress = %08X\n", *((int*) pLookupResult->h_addr_list[numAddressesFound]));
                     numAddressesFound ++;
                  }
                  //printf("Found %d addresses\n", numAddressesFound);


                  // Setup the remote address structure with the lookup results
                  remoteAddr.sin_family = AF_INET;
                  remoteAddr.sin_port = serverPort;
                  remoteAddr.sin_addr.s_addr = *((int*) pLookupResult->h_addr_list[0]); // Short-cut
                  //printf("ServerAddress = %08X\n", remoteAddr.sin_addr.s_addr);

                  // Perform connect operation
                  if (connect(sock,
                              (struct sockaddr*) &remoteAddr,
                              sizeof(struct sockaddr_in)) == SOCKET_ERROR)
                  {
                     printf("-HttpTest- Connection creation failed, error = %d\n", errno);
                  }
                  else
                  {
                     char *pBasicCredentials;
                     char *pEncodedBasicCredentials;
                     int  encodedLength;
                     char CasaPrincipal[] = "CasaPrincipal:";
                     char HTTPReqPart1[] = "GET /example-info HTTP/1.1\r\\nUser-Agent: CasaTestClient\r\nHost: jcstation.dnsdhcp.provo.novell.com:4096\r\nConnection: Keep-Alive\r\nAuthorization: Basic ";

                     // Now the connection is setup, send 1st part of HTTP request to the server.
                     send(sock, HTTPReqPart1, (int) strlen(HTTPReqPart1), 0);

                     // Now setup the HTTP Basic Credentials
                     pBasicCredentials = (char*) malloc(strlen(CasaPrincipal) + strlen(authToken) + 1);
                     if (pBasicCredentials)
                     {
                        memcpy(pBasicCredentials, CasaPrincipal, sizeof(CasaPrincipal));
                        strcat(pBasicCredentials, authToken);

                        // Now Base64 encode the credentials
                        if (EncodeData((const void*) pBasicCredentials,
                                       (const int32_t) strlen(pBasicCredentials),
                                       &pEncodedBasicCredentials,
                                       (int32_t *) &encodedLength) == 0)
                        {
                           // Send the encoded credentials
                           send(sock, pEncodedBasicCredentials, encodedLength - 1, 0);

                           // Send the rest of the header
                           send(sock, "\r\n\r\n", 4, 0);

                           // Free the buffer holding the encoded credentials
                           free(pEncodedBasicCredentials);
                        }
                        else
                        {
                           printf("-HttpTest- Error encoding credentials\n");
                        }

                        // Free the buffer containing the basic credentials
                        free(pBasicCredentials);
                     }
                     else
                     {
                        printf("-HttpTest- Buffer allocation failure\n");
                     }

                     // Shutdown the connection
                     shutdown(sock, 0);
                  }
               }
               else
               {
                  printf("-HttpTest- Unsupported address type returned %08X\n", pLookupResult->h_addrtype);
               }
            }
            else
            {
               printf("-HttpTest- Lookup for %s failed\n", pServerAddress);
            }
         }
         else
         {
            printf("-HttpTest- Unable to bind socket, error = %d", errno);
         }

         // Close the socket
         setsockopt(sock, SOL_SOCKET, SO_LINGER, (const char*) &linger_opt, sizeof(linger_opt));
         closesocket(sock);
      }
      else
      {
         printf("-HttpTest- Unable to open socket, error = %d\n", errno);
      }
   }
}