/****************************************************************************/
/* Copyright (c) 2014 MBARI                                                 */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  : Simple HTML interface to instrument web servers.              */
/* Filename : HtmlUtil.cc                                                   */
/* Author   : Henthorn                                                      */
/* Project  : i2MAP                                                         */
/* Version  : 1.0                                                           */
/* Created  : 11/07/2014                                                    */
/* Modified :                                                               */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/****************************************************************************/
#include <time.h>

#include "HtmlUtil.h"

// Constructor stores the server ip address.
//
HtmlUtil::HtmlUtil(const char* server_ip)
   : _server_ip(0), _sockfd(0)
{
   _server_ip = strdup(server_ip);
}

HtmlUtil::~HtmlUtil()
{
   if (_sockfd > 0) close(_sockfd);
   if (_server_ip) free(_server_ip);
}

// Perform an HTML GET request using the URL string from the caller.
// Returns 0 if all goes well, otherwise -1.
// Use get_response() function to get the text of the operation, good or bad.
// The connection to the server is closed.
//
int HtmlUtil::get(const char* url)
{
   // Only if the socket is open and connected
   //
   if (_sockfd < 0) return _sockfd;

   // Build the query string
   //
   char *query;
   char *tpl = "GET /%s HTTP/1.1\r\nHost: %s\r\nUser-Agent: HTMLUTIL 1.0\r\nConnection: close\r\n\r\n";
   char *getpage = (char*)url;

   if(getpage[0] == '/')
   {
     getpage = getpage + 1;
   }

   query = (char *)malloc(strlen(_server_ip)+strlen(getpage)+strlen(tpl)-3);
   sprintf(query, tpl, getpage, _server_ip);
   //fprintf(stdout,"query: <%s>\n", query);

   //Send the query to the server
   //
   int tmpres, sent = 0;
   while(sent < strlen(query))
   {
      tmpres = send(_sockfd, query+sent, strlen(query)-sent, 0);
      if(tmpres < 0)
      {
         sprintf(_response, "Can't send query: code = %d", errno);
         return tmpres;
      }
      sent += tmpres;
   }

   tmpres = read_response(_sockfd);

   return tmpres;
}

// Perform an HTML POST operation using the URL and param strings
// from the caller. Param string expected in the format
// "paramID=parameter&anotherParam=246"
// Returns 0 if all goes well, otherwise -1.
// Use get_response() function to get the text of the operation, good or bad.
// The connection to the server is closed.
//
int HtmlUtil::post(const char* url, const char* params)
{
   // Only if the socket is open and connected
   //
   if (_sockfd < 0) return _sockfd;

   // Build the query string
   //
   char *query;
   char *tpl = "POST /%s HTTP/1.1\r\nHost: %s\r\nUser-Agent: HTMLUTIL 1.0\r\nConnection: close\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: %d\r\n\r\n%s ";
   char *getpage = (char*)url;

   if(getpage[0] == '/')
   {
     getpage = getpage + 1;
   }

   query = (char *)malloc(strlen(_server_ip)+strlen(getpage)+strlen(params)+1+strlen(tpl)-5);
   sprintf(query, tpl, getpage, _server_ip, strlen(params)+1, params);
//   fprintf(stdout,"query: <%s>\n", query);

   //Send the query to the server
   int tmpres, sent = 0;
   while(sent < strlen(query))
   {
      tmpres = send(_sockfd, query+sent, strlen(query)-sent, 0);
      if(tmpres == -1)
      {
         sprintf(_response, "Can't send query: code = %d", errno);
         break;
      }
      sent += tmpres;
   }

   if (tmpres >= 0) tmpres = read_response(_sockfd);

   return tmpres;
}

// Returns the result of the latest operation in the callers
// buffer limited by the buffer length. Returns the number
// of bytes copied into the buffer. If 0 is passed in for the
// buffer, the length of the latest result is returned.
//
int HtmlUtil::get_response(const char* your_response_buffer,
                           unsigned int your_response_buffer_length)
{
   if (!your_response_buffer)
      return strlen(_response)+1;

   unsigned int amount = your_response_buffer_length < strlen(_response) ?
                your_response_buffer_length : strlen(_response);

   memcpy((void*)your_response_buffer, (void*)_response, amount);

   return amount;

}

int HtmlUtil::read_response(int sock)
{
   memset(_response, 0, sizeof(_response));
   int tmpres = 0, total = 0;
   while((tmpres = recv(sock, _response+total, sizeof(_response)-1-total, 0)) > 0)
   {
      total += tmpres;
   }

   if(tmpres < 0)
   {
      sprintf(_response, "Error receiving data: code = %d", errno);
   }
   else if(tmpres == 0)
   {
      _response[total] = '\0';
//      fprintf(stdout, "_response[0..%d] = [%s]\n", total, _response);
      if (strstr(_response, "200 OK"))
      {
//         printf("Success!\n");
      }
      else
      {
//         printf("Failed!\n");
         tmpres = -1;
      }

   }
   return tmpres;
}

// Create a connection socket and connect to the server.
// Return the file descriptor of the socket if successful,
// otherwise value < 0.
// If the socket already is open, just return the socket fd.
//
int HtmlUtil::create_tcp_socket()
{
   if (_sockfd > 0) return _sockfd;

   if((_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
   {
      sprintf(_response, "Can't create socket: code = %d", errno);
      return -1;
   }

   int tmpres;
   _server_addr.sin_family = AF_INET;
   tmpres = inet_aton(_server_ip, (&(_server_addr.sin_addr)));

   if( tmpres < 0)  
   {
      sprintf(_response, "Can't set remote->sin_addr.s_addr: code = %d", errno);
      return -1;
   }
   else if(tmpres == 0)
   {
      sprintf(_response, "%s is not a valid IP address\n", _server_ip);
      return -1;
   }
   
   _server_addr.sin_port = htons(80);

   struct timeval tv;
   tv.tv_sec = 0;
   tv.tv_usec = 250000L;

   if (0 > setsockopt(_sockfd, SOL_SOCKET, SO_RCVTIMEO,
          (const void **)&tv, sizeof(struct timeval))) {
      sprintf(_response, "Could not initialize connection: setsockopt failed with %d", errno);
      return -1;
    }

#if 0
   int opts = fcntl(_sockfd, F_GETFL);
   fcntl(_sockfd, F_SETFL, (opts | O_NONBLOCK));

   if(connect(_sockfd, (struct sockaddr *)&_server_addr, sizeof(struct sockaddr)) < 0)
   {
      timespec req, rem;
      req.tv_sec = 0;
      req.tv_nsec = 100000000L;
      nanosleep(&req, &rem);
      if( (connect(_sockfd, (struct sockaddr *)&_server_addr, sizeof(struct sockaddr)) < 0) &&
           errno != EISCONN)
      {
         sprintf(_response, "Could not connect to %s: code = %d", _server_ip, errno);
         return -1;
      }
   }

   fcntl(_sockfd, F_SETFL, (opts & (~O_NONBLOCK)));
#endif

   if(connect(_sockfd, (struct sockaddr *)&_server_addr, sizeof(struct sockaddr)) < 0)
   {
      sprintf(_response, "Could not connect to %s: code = %d", _server_ip, errno);
      return -1;
   }
   return _sockfd;

}

