/*****************************************************************

  (c) Rob Hartill  1994


  This program converts a HTTP POSTed FORMs request into another
  FORM which describes the selections made in the previous FORM,
  allowing the user to toggle each selection on or off.

  An addional toggle enables the user to select whether the listed
  items are to be searched for on a ANDed or ORed matching field basis.

  The output is a HTTP POSTed request consisting of a series of items,
  at most one item per field, with the following format,
      field_number|search_type|search_item[/search_item]*=on&
                                                         ^^^
               the '=on' is attached by the WWW browser -+++

      search_type is a numeric description of the type of search to
      be performed, e.g. substrings ANDed
                         substrings ORed
                         numeric/numeric ranges
                         century/century ranges

  Nothing in this file needs to be altered, all user (re)configurable
  features are accessed via the config.h file.

*****************************************************************/

#include <syslog.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/param.h>
#include "config.h"

char *in_lowercase();
char *space_to_plus();
char *plus_to_space();
char *slash_to_and();
char *slash_to_or();
char *decode_escape_sequences();
char *encode_escape_sequences();
char *in_lowercase();
int escaped_length();
int anded_length();
int unplussed_length();
char *plus_to_20();
char *slash_from_2F();

#define FORMAT_FOR_POSTING(LIST) \
  slash_from_2F(plus_to_20(LIST))

#define FORMAT_ANDED_ITEMS_FOR_VIEWING(LIST) \
   slash_to_and(decode_escape_sequences(plus_to_space(LIST)))

#define FORMAT_ORED_ITEMS_FOR_VIEWING(LIST) \
   slash_to_or(decode_escape_sequences(plus_to_space(LIST)))


void main()
  {
  int xx,                              /* check the xxth field called 
                                          'xx' to tie in with s_xx r_xx etc
                                       */
      query_method[FIELDS_PER_RECORD], /* Each field being searched will have
                                          a search method (AND/OR/YEAR/CENTURY)
                                       */
      content_length;                  /* The length in bytes of the input 
                                       */

  char *unchecked,                     /* ptr to the remaining unchecked
                                           input for the xxth field
                                       */
       key[5],                         /* s_xx, r_xx, c_xx etc 
                                       */
      *next_key,                       /* ptr to a s_xx, r_xx, c_xx in the input
                                       */
      *query_string[FIELDS_PER_RECORD],/* ptr to a list of search items for a
                                           particular field.
                                       */ 
      *next_query_string,              /* ptr to a list of search items for the
                                           current field.
                                       */
      *unprocessed_query,              /* The input to this program, POSTed
                                           by a HTTP browser.
                                       */
      *for_viewing,                    /* will point to a malloc'ed string
                                           holding a list of search items.
                                       */
      *for_posting;                    /* will point to a malloc'ed string
                                           holding a list of search items.
                                       */



  printf("Content-type: text/html\n\n<TITLE>%s</TITLE>",URL_TITLE);
       /* keeps mosaic happy */

  printf(QUERY_CHECK_MESSAGE);

  content_length = atoi(getenv("CONTENT_LENGTH"));

  /* Reserve some space to hold the input
  */
  unprocessed_query = (char *) malloc(content_length+2);
  /* Reserve some space to hold the output
  */
  next_query_string = (char *) malloc(content_length+2);

  if (unprocessed_query == NULL || next_query_string == NULL)
     {  
     printf("Unable to reserve enough memory to parse the request<br>\n");
     return;
     }

  /* Read the input query.
  */
  fgets(unprocessed_query, content_length+1, stdin);

  /* Add a '&' field delimiter for good measure. */
  strcat(unprocessed_query,"&");

#ifdef DEBUGGING
  printf("DEBUGGING..%s %d<br>",unprocessed_query, strlen(unprocessed_query));
#endif

  printf("<FORM ACTION=\"http:perform_query\" METHOD=\"POST\">\n");

  /* For each possible field of the database, look to see if we want to
      perform a search on it.
  */
  for (xx= 1; xx<= MAX_NUMBER_OF_FIELDS; xx++)
     {
     (char *) (query_string[xx-1]) = next_query_string;
     *next_query_string = '\0';
  
     /*================================================================*/
     /* first look for  s_xx entires
     */ 
     sprintf(key, "s_%02d=", xx);   /* key = 's_01=' 's_02=' 's_03='  etc  */
     
     unchecked = unprocessed_query;  /* look at the start of the input */

     while ( (next_key = strstr(unchecked, key)) != NULL)
       {
       /* substring searches for a list of susbstrings will be for
           matching records with ALL (and) or ANY (or) of the substrings.
       */
        if (strncmp(next_key + 5, "ANDING", 6) ==0 )
           {
           unchecked = next_key + (5 + 6);
           query_method[xx-1] = ANDING;
           continue;
           }
 
        if (strncmp(next_key + 5, "ORING", 5) == 0)
           {
           unchecked = next_key + (5 + 5);
           query_method[xx-1] = ORING;
           continue;
           }


        /* This item must be a substring or '/' delimited list of
            substrings.
        */
        if (*(next_key + 5) != '&')
            {
            strcat(next_query_string, "/");
            strcat(next_query_string, next_key + 5);
            }

        if ( strchr(next_query_string, '&') != NULL)
           *strchr(next_query_string, '&') = '\0';

        if ( (next_key = strchr(next_key, '&')) != NULL)
            unchecked = next_key+1;
        else 
           break;   /* There are no more '&' delimited items in the input */
        }
     /*================================================================*/

     /* now look for  r_xx entires */ 
     sprintf(key, "r_%02d=", xx);   /* key = 'r_01=' 'r_02=' 'r_03='  etc  */
     
     unchecked = unprocessed_query;  /* go back to the start of the input */

     /* There will be at most one 'r_xx' entry, so either it's there or it isn't 
     */
     if ( (next_key = strstr(unchecked, key)) != NULL)
       {
       strcat(next_query_string,"/");
       strcat(next_query_string, next_key+5);
       if ( (next_key = strchr(next_query_string, '&')) != NULL)
          *next_key = '\0';

       query_method[xx-1] = YEARS;
       }

     /*================================================================*/

     /* NOW LOOK FOR  r_xx ENTIRES */ 
     sprintf(key, "c_%02d=", xx);   /* key = 'c_01=' 'c_02=' 'c_03='  ETC  */
     
     unchecked = unprocessed_query;  /* go back to the start of the input */

     /* There will be at most one 'c_xx' entry, so either it's there or it isn't 
     */
     if ( (next_key = strstr(unchecked, key)) != NULL)
       {
       strcat(next_query_string,"/");
       strcat(next_query_string, next_key+5);
       if ( (next_key = strchr(next_query_string, '&')) != NULL)
          *next_key = '\0';

       query_method[xx-1] = CENTURIES;
       }

     next_query_string += strlen(next_query_string) +1;

     } 

  /*================================================================*/

  printf("<dl>\n");
  for (xx= 1; xx<= MAX_NUMBER_OF_FIELDS; xx++)
     {
     next_query_string = query_string[xx-1];

     if ( strlen(next_query_string) > 1 )
        {
        for_viewing = (char *) malloc(anded_length(next_query_string) + 1);
        if (for_viewing == NULL) {printf("Not enough memory "); return;}

        for_posting = (char *) malloc(escaped_length(next_query_string) + 1);
        if (for_posting == NULL) {printf("Not enough memory "); return;}

        strcpy(for_posting, next_query_string+1);
        FORMAT_FOR_POSTING(for_posting);

        strcpy(for_viewing, next_query_string+1);
        if (query_method[xx-1] == ANDING)
            FORMAT_ANDED_ITEMS_FOR_VIEWING(for_viewing);
        else
            FORMAT_ORED_ITEMS_FOR_VIEWING(for_viewing);


        if (query_method[xx-1] == ANDING)
            printf("<dt><inPUT TYPE=\"checkbox\" NAME=\"%d|%d|%s/\" CHECKED>The <b>%s</b> must contain:\n<dd><b>%s</b>\n",
                xx,
                ANDING,
                for_posting,
                field_names[xx-1],
                for_viewing);

        if (query_method[xx-1] == ORING)
            printf("<dt><inPUT TYPE=\"checkbox\" NAME=\"%d|%d|%s/\" CHECKED>The <b>%s</b> must contain:\n<br><dd><b>%s</b>\n",
                xx,
                ORING,
                for_posting,
                field_names[xx-1],
                for_viewing);
 
        if (query_method[xx-1] == YEARS)
            printf("<dt><inPUT TYPE=\"checkbox\" NAME=\"%d|%d|%s/\" CHECKED>The <b>%s</b> must be a year equal to or in the range... <b>%s</b>\n",
                xx,
                YEARS,
                for_posting,
                field_names[xx-1],
                for_viewing);

        if (query_method[xx-1] == CENTURIES)
            printf("<dt><inPUT TYPE=\"checkbox\" NAME=\"%d|%d|%s/\" CHECKED>The <b>%s</b> must be a century equal to or in the range... <b>%s</b>\n",
                xx,
                CENTURIES,
                for_posting,
                field_names[xx-1],
                for_viewing);


        free(for_viewing); 
        free(for_posting);
        }
      }


  printf("</dl><hr>\n");

  printf("<inPUT TYPE=\"radio\" NAME=\"ANDING\" VALUE=\"on\" CHECKED> See the records in which <b>all</b> the selected conditions apply.<br>or<br>\n");
  printf("<inPUT TYPE=\"radio\" NAME=\"ANDING\" VALUE=\"off\"> See the records in which <b>any</b> of selected conditions apply.<br>\n");

  printf("<br><inPUT TYPE=\"submit\" VALUE=\"Submit this query.\"></FORM>\n");

  free(unprocessed_query);
  free(query_string[0]);
  }



/******************************************************************
 in_lowercase returns a pointer to it's input which will 
  have had it's uppercase characters converted to lowercase
*******************************************************************/
char *in_lowercase(to_convert)
char *to_convert;
   {
   int indx = 0;

   while (to_convert[indx] != '\0')
     {
     to_convert[indx] = tolower(to_convert[indx]);
     indx++;
     }
   return to_convert;
   }


/******************************************************************
 plus_to_space returns a pointer to it's input which will 
  have had it's pluses converted to spaces
*******************************************************************/
char *plus_to_space(string)
char *string;
  {
  int indx;

  indx = 0;
  while( string[indx] != '\0' )
     {
     if (string[indx] == '+') string[indx] = ' ';
     indx++;
     }

  return string;
  }

/**************************************************************
 decode_escape_sequences takes a string as input, and translates 
   HTTP escape sequences, format %hh, into plain ASCII
***************************************************************/
char *decode_escape_sequences(request)
  char *request;
  {
  int indx,indx2;
  long hex_char;
  char hex_num[3];

  indx = indx2 = 0;
  while ( request[indx] != '\0' )
     {
     if ( request[indx] == '%')
        {
        if ((request[indx+1] >='0' && request[indx+1] <= '9') ||
           (request[indx+1] >='A' && request[indx+1] <= 'F'))
           { 
           if ((request[indx+2] >='0' && request[indx+2] <= '9') ||
              (request[indx+2] >='A' && request[indx+2] <= 'F'))
              {
              hex_num[0] = request[indx+1];
              hex_num[1] = request[indx+2];
              hex_num[2] = '\0';
              hex_char = strtol(hex_num,(char **)NULL,16);

              request[indx2++] = (char) hex_char;
                 
              indx += 3;
              }
             else request[indx2++] = request[indx++];
           }
          else request[indx2++] = request[indx++];
        }
        else request[indx2++] = request[indx++];
     }
  request[indx2] = '\0';
  return request;
  }

/**************************************************************
 encode_escape_sequences takes a string as input, and translates 
   ASCII into HTTP escape sequences, format %hh
***************************************************************/
char *encode_escape_sequences(request)
  char *request;
  {
  char *tmp_string;
  int indx,indx2;
  long hex_char;
  char hex_num[3];


  tmp_string = (char *) malloc(escaped_length(request)+1);
  if (tmp_string == NULL) { printf("Error. Not enough memory\n<br>") ; exit(-1); }
 
  indx = indx2 = 0;
  while ( request[indx] != '\0' )
     {
     if ( /*request[indx] != '+' &&*/ request[indx] != '/' &&
          ((request[indx] < 'a' || request[indx] > 'z') &&
          (request[indx] < 'A' || request[indx] > 'Z'))
          )
        {
        sprintf(tmp_string+indx2,"%%%2X", (char) request[indx]); 
        indx2 += 3; 
        indx++;
        }
     else tmp_string[indx2++] = request[indx++];
     }
     tmp_string[indx2] = '\0';

     strcpy(request, tmp_string);
     free(tmp_string);
     return request;
  }


/******************************************************************
 slash_to_and returns a pointer to it's input which will 
  have had it's /s converted to " and "s
*******************************************************************/
char *slash_to_and(request)
  char *request;
  {
  char *tmp_string;
  int indx,indx2;

  tmp_string = (char *) malloc(anded_length(request)+1);
  if (tmp_string == NULL) {printf("Error. Not enough memory\n<br>") ; exit(-1);}
 
  strcpy(tmp_string,"");

  indx = indx2 = 0;
  while ( request[indx] != '\0' )
     {
     if ( request[indx] == '/' )
        {
        strcat(tmp_string," <i>and</i> ");
        indx2 += 12;
        }
     else
        sprintf(tmp_string+(indx2++),"%c\0", request[indx]);
     indx++;
     }
     strcpy(request, tmp_string);
     free(tmp_string);
     return request;
  }


/******************************************************************
 slash_to_and returns a pointer to it's input which will 
  have had it's /s converted to " or "s
*******************************************************************/
char *slash_to_or(request)
  char *request;
  {
  char *tmp_string;
  int indx,indx2;

  tmp_string = (char *) malloc(anded_length(request));
  if (tmp_string == NULL) {printf("Error. Not enough memory\n<br>") ; exit(-1);}

  strcpy(tmp_string,"");

 
  indx = indx2 = 0;
  while ( request[indx] != '\0' )
     {
     if ( request[indx] == '/' )
        {
        strcat(tmp_string," <i>or</i> ");
        indx2 += 11;
        }
     else
        sprintf(tmp_string+(indx2++),"%c\0", request[indx]);
     indx++;
     }
     strcpy(request, tmp_string);
     free(tmp_string);
     return request;
  }


/******************************************************************
 escaped_length returns the length of its input string, after it
  would have replaced characters with escape sequences.
*******************************************************************/
int escaped_length(request)
  char *request;
  {
  int indx, indx2;

  indx = indx2 = 0;
  while ( request[indx] != '\0' )
     {
     if ( /*request[indx] != '+' &&*/
          ((request[indx] < 'a' || request[indx] > 'z') &&
          (request[indx] < 'A' || request[indx] > 'Z'))
          )
        {
        indx2 += 3;
        indx++;
        }
     else {indx2++; indx++;}
     }
     return indx2;
  }

/******************************************************************
 andedd_length returns the length of its input string, after it
  would have replaced /s with " and "s.
*******************************************************************/
int anded_length(request)
  char *request;
  {
  int indx, indx2;

  indx = indx2 = 0;
  while ( request[indx] != '\0' )
     {
     if ( request[indx] == '/' )
        {
        indx2 += 12;
        indx++;
        }
     else {indx2++; indx++;}
     }
  return indx2;
  }


/**************************************************************
 plus_to_20 takes a string as input, and translates 
   + into %20
***************************************************************/
char *plus_to_20(request)
  char *request;
  {
  char *tmp_string;
  int indx,indx2;
  long hex_char;
  char hex_num[3];


  tmp_string = (char *) malloc(unplussed_length(request)+1);
  if (tmp_string == NULL) { printf("Error. Not enough memory\n<br>") ; exit(-1); }
 
  indx = indx2 = 0;
  while ( request[indx] != '\0' )
     {
     if ( request[indx] == '+' )
        {
        sprintf(tmp_string+indx2,"%%20"); 
        indx2 += 3; 
        indx++;
        }
     else tmp_string[indx2++] = request[indx++];
     }
     tmp_string[indx2] = '\0';

     strcpy(request, tmp_string);
     free(tmp_string);
     return request;
  }


/**************************************************************
 unplussed_length takes a string as input, and returns its
  length if it were to translates + into %20
***************************************************************/
int unplussed_length(request)
  char *request;
  {
  int indx,indx2;
  long hex_char;
  char hex_num[3];

  indx = indx2 = 0;
  while ( request[indx] != '\0' )
     {
     if ( request[indx] == '+' )
        {
        indx2 += 3; 
        indx++;
        }
     {indx2++; indx++;}
     }

     return indx2;
  }

/**************************************************************
 slash_from_2F takes a string as input, and translates 
   %2F into '/'
***************************************************************/
char *slash_from_2F(request)
  char *request;
  {
  int indx,indx2;
  long hex_char;
  char hex_num[3];

  indx = indx2 = 0;
  while ( request[indx] != '\0' )
     {
     if ( strncmp(request+indx,"%2F",3) == 0) 
        {
        request[indx2++] = '/';
        indx += 3;
        }
     else request[indx2++] = request[indx++];
     }
  request[indx2] = '\0';
  return request;
  }

