//---------------------------------------------------------------------------
//
// MODULE NAME:     main.cpp
//
// COPYRIGHT:       Copyright (c) 2013, GladeSoft, Inc.
//                  All Rights Reserved.
//
//                  NOTICE: THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF
//                  GLADESOFT, INC., AND SHALL NOT BE REPRODUCED, COPIED
//                  IN WHOLE OR IN PART ADAPTED, MODIFIED, OR
//                  DISSEMINATED WITHOUT THE EXPRESS WRITTEN CONSENT
//                  OF GLADESOFT, INC. ANY REDISTRIBUTION OR
//                  PUBLICATION TO UNAUTHORIZED PERSONS IS STRICTLY
//                  PROHIBITED BY U.S. AND INTERNATIONAL COPYRIGHT LAW.
//
// DESCRIPTION:     Another example program using the Seminole embedded 
//                  web server.
//
// AUTHOR:          S. Conner
//
// CREATION DATE:   Mar  5, 2004
//
//---------------------------------------------------------------------------
//
// HISTORY OF CHANGE  
//
// -----+--------+------------------------------------------+------+---------
// VERS | DATE   | DESCRIPTION OF CHANGE                    | INIT | PR#
// =====+========+==========================================+======+=========
//  001 |03/05/04| File creation.                           | SPC  |
// -----+--------+------------------------------------------+------+---------
//  002 |06/12/09| Const correctness.                       | RAH  |
//      |        | Add file handler to serve up main FORM   |      |
// -----+--------+------------------------------------------+------+---------
//---------------------------------------------------------------------------

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

#include "seminole.h"

#include "sitedata.h"

//**********************************************************************

class DemoCgiHandler : public HttpdHandler
{
public:
  DemoCgiHandler(const char *p_prefix);
  virtual ~DemoCgiHandler();
  virtual bool Handle(HttpdRequest *p_req);
};

class DemoCgiData
{
public:
  HttpdCgiParameter *mpListPost;
  HttpdCgiParameter *mpListQuery;
  int                mResponse;
    
  DemoCgiData(HttpdRequest *p_req);
  ~DemoCgiData();
};    

static const char  gServername[] = "localhost";
static const int   gServerport   = 8080;
static Httpd      *gpWebServer;

//*************************************************************************

//---------------------------------------------------------------------------
//
// NAME:           DemoCgiHandler::DemoCgiHandler
//
// DESCRIPTION:    Construct the demo request handler.
//
// AUTHOR:         S. Conner
// CREATED:        Mar  6, 2004
// LAST MODIFIED:  N/A           BY:  N/A
//
//--------------------------------------------------------------------------
DemoCgiHandler::DemoCgiHandler(const char *p_prefix)
{
  mpPrefix = HttpdUtilities::SaveString(p_prefix);
}

//---------------------------------------------------------------------------
//
// NAME:           DemoCgiHandler::~DemoCgiHandler
//
// DESCRIPTION:    Destroy the demo request handler.
//
// AUTHOR:         S. Conner
// CREATED:        Mar  6, 2004
// LAST MODIFIED:  N/A           BY:  N/A
//
//--------------------------------------------------------------------------
DemoCgiHandler::~DemoCgiHandler()
{
#if HTTPD_INC_SHUTDOWN
  HttpdOpSys::Free((char *)mpPrefix);
#endif
}

//---------------------------------------------------------------------------
//
// NAME:           DemoCgiHandler::Handle
//
// DESCRIPTION:    Process the request.
//
// RETURNS:        True if the request was handled by this handler,
//                 false if no processing has been performed.
//
// AUTHOR:         S. Conner
// CREATED:        Mar  6, 2004
// LAST MODIFIED:  N/A           BY:  N/A
//
//--------------------------------------------------------------------------
bool DemoCgiHandler::Handle(HttpdRequest *p_req)
{
  assert(p_req != NULL);
  
  if (IsMe(p_req))
  {
    bool        is_head = p_req->IsHeadRequest();
    DemoCgiData data(p_req);
    
    if (data.mResponse != HTTPD_RESP_OK)
    {
      p_req->Respond(data.mResponse);
      return (true);
    }

    HttpdDynamicOutput output(p_req, is_head);
    p_req->Respond(HTTPD_RESP_OK);
    output.Header(HttpdUtilities::mContentType, "text/plain");
    output.HeaderComplete();
    
    if (!is_head)
    {
      output.Body()->Printf("Server:  %s:%d\r\n"
                            "Method:  %s\r\n"
                            "Version: %d.%d\r\n"
                            "Path:    %s\r\n"
                            "Query:   %s\r\n"
                            "URI:     %s\r\n",

                            p_req->Server()->ServerHost(),
                            p_req->Server()->Port(),
                            p_req->Method(),
                            (p_req->Protocol() >> 8) & 0xFF,
                            (p_req->Protocol()     ) & 0xFF,
                            p_req->Path(),
                            p_req->Query()       ? p_req->Query()       : "",
                            p_req->CompleteUri() ? p_req->CompleteUri() : ""
                           );

      output.Body()->Printf(
                             "Client:  %d.%d.%d.%d\r\n"
                             "Headers:\r\n",
                             (p_req->ClientAddr() >> 24) & 0xFF,
                             (p_req->ClientAddr() >> 16) & 0xFF,
                             (p_req->ClientAddr() >>  8) & 0xFF,
                             (p_req->ClientAddr()      ) & 0xFF
                           );

      HttpdMimeParser &headers = p_req->Headers();
      
      for(size_t i = 0; i < headers.Count(); i++)
      {
        HttpdPair *p_pair;
        
        p_pair = headers.Pair(i);
        output.Body()->Printf("\t%s: %s\r\n", p_pair->mpKey, p_pair->mpValue);
      }
  
      output.Body()->Printf("CGI:\r\n");
    
      //---------------------------------------------------------------
      // Print out parameters passed in via the query portion (if any)
      //---------------------------------------------------------------

      HttpdCgiParameter *p_list = data.mpListQuery;
      while (p_list != NULL)
      {
        output.Body()->Printf(
                               "\t%s = %s\n",
                               p_list->mPair.mpKey,
                               p_list->mPair.mpValue
                             );
        p_list = p_list->mpNext;
      }
      
      //------------------------------------------------------------
      // Print out parameters passed in via a POST method (if any)
      //------------------------------------------------------------

      p_list = data.mpListPost;
      while (p_list != NULL)
      {
        output.Body()->Printf(
                               "\t%s = %s\n",
                               p_list->mPair.mpKey,
                               p_list->mPair.mpValue
                             );
        p_list = p_list->mpNext;
      }
    }
    
    return (true);
  }

  return (false);
}

//---------------------------------------------------------------------------
//
// NAME:           DemoCgiData::DemoCgiData
//
// DESCRIPTION:    Construct the data processing class.
//
// AUTHOR:         S. Conner
// CREATED:        Mar  6, 2004
// LAST MODIFIED:  N/A           BY:  N/A
//
//--------------------------------------------------------------------------
DemoCgiData::DemoCgiData(HttpdRequest *p_req)
{
  assert(p_req != NULL);
  
  mpListPost  = NULL;
  mpListQuery = NULL;
  mResponse   = HTTPD_RESP_OK;

  //------------------------------------------------------------------
  // If there was query information on the URL passed in, grab the 
  // name/value pairs.
  //------------------------------------------------------------------

  mpListQuery = HttpdCgiParameter::ParseUriString(p_req->Query());
    
  //----------------------------------------------------------------
  // now check for additional CGI parameters being passed in as a    
  // document body in the incoming request.  First, the most common
  // type of POST request
  //----------------------------------------------------------------

  mpListPost = HttpdCgiParameter::ParsePostData(p_req);
  if (mpListPost == NULL)
  {
    //----------------------------------------------------------
    // Now try the MIME Multipart format---usually used for
    // file uploads.
    //----------------------------------------------------------
    
    HttpdMultipartCgiParser parser(p_req);
    if (parser.Parse() != 0)
      return;
    
    mpListPost = parser.TakeList();
  }
}

//---------------------------------------------------------------------------
//
// NAME:           DemoCgiData::~DemoCgiData
//
// DESCRIPTION:    Destroy the data processing class.
//
// AUTHOR:         S. Conner
// CREATED:        Mar  6, 2004
// LAST MODIFIED:  N/A           BY:  N/A
//
//--------------------------------------------------------------------------
DemoCgiData::~DemoCgiData()
{
  HttpdCgiParameter::FreeList(mpListPost);
  HttpdCgiParameter::FreeList(mpListQuery);
}

//*************************************************************************

//---------------------------------------------------------------------------
//
// NAME:           failure
//
// DESCRIPTION:    Handle a fatal error.
//
// AUTHOR:         S. Conner
// CREATED:        Mar  6, 2004
// LAST MODIFIED:  N/A           BY:  N/A
//
//--------------------------------------------------------------------------
static void failure(const char *p_file, int line, const char *p_msg)
{
  assert(p_file != NULL);
  assert(line   >  0);
  assert(p_msg  != NULL);
  
  fprintf(stderr, "%s:%d Call to %s failed\n", p_file, line, p_msg);
  exit(EXIT_FAILURE);
}

//---------------------------------------------------------------------------
//
// NAME:           Initialize_The_Web_Server
//
// DESCRIPTION:    Start and configure Seminole.
//
// AUTHOR:         S. Conner
// CREATED:        Mar  6, 2004
// LAST MODIFIED:  N/A           BY:  N/A
//
//--------------------------------------------------------------------------
static void Initialize_The_Web_Server()
{
  //-------------------------------------------------------
  // Initialize the environment.
  //-------------------------------------------------------
  
  if (Httpd::Init() != 0)
    failure(__FILE__, __LINE__, "Httpd::Init()");
  
  //--------------------------------------------------------
  // Create the webserver instance.  This just initializes
  // the object --- it doesn't start it up.
  //--------------------------------------------------------
    
  gpWebServer = new Httpd(gServername, gServerport);
  if (gpWebServer == NULL)
    failure(__FILE__, __LINE__, "Httpd::Httpd()");

  static HttpdMemoryDataSource builtin(SemWeb, sizeof(SemWeb));
  static HttpdRomFileSystem    romfs;
  if (romfs.Mount(&builtin) != 0)
    failure(__FILE__, __LINE__, "HttpdRomFileSystem::Mount()");

  static HttpdFileHandler file_hand(&romfs, "/", "/");
  gpWebServer->Install(&file_hand);

  static DemoCgiHandler demo_hand("/sample.cgi");
  gpWebServer->Install(&demo_hand);

  printf("Server started on host %s port %d\n", gServername, gServerport);
}

//---------------------------------------------------------------------------
//
// NAME:           main
//
// DESCRIPTION:    Entry point for the demo.
//
// AUTHOR:         S. Conner
// CREATED:        Mar  6, 2004
// LAST MODIFIED:  N/A           BY:  N/A
//
//--------------------------------------------------------------------------
int main(int argc, const char *const *argv)
{
  char buf[4];

  Initialize_The_Web_Server();
  gpWebServer->Start();
  
  printf("Press <ENTER> to stop.\n");
  fgets(buf, sizeof(buf), stdin);
  
#if HTTPD_INC_SHUTDOWN
  gpWebServer->Stop(HARD);
#endif

  return (EXIT_SUCCESS);
}
