/*
 * Copyright (c) 1998-2001 Caucho Technology -- all rights reserved
 *
 * Caucho Technology permits redistribution, modification and use
 * of this file in source and binary form ("the Software") under the
 * Caucho Developer Source License ("the License").  The following
 * conditions must be met:
 *
 * 1. Each copy or derived work of the Software must preserve the copyright
 *    notice and this notice unmodified.
 *
 * 2. Redistributions of the Software in source or binary form must include 
 *    an unmodified copy of the License, normally in a plain ASCII text
 *
 * 3. The names "Resin" or "Caucho" are trademarks of Caucho Technology and
 *    may not be used to endorse products derived from this software.
 *    "Resin" or "Caucho" may not appear in the names of products derived
 *    from this software.
 *
 * 4. Caucho Technology requests that attribution be given to Resin
 *    in any manner possible.  We suggest using the "Resin Powered"
 *    button or creating a "powered by Resin(tm)" link to
 *    http://www.caucho.com for each page served by Resin.
 *
 * This Software is provided "AS IS," without a warranty of any kind. 
 * ALL EXPRESS OR IMPLIED REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
 *
 * CAUCHO TECHNOLOGY AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES
 * SUFFERED BY LICENSEE OR ANY THIRD PARTY AS A RESULT OF USING OR
 * DISTRIBUTING SOFTWARE. IN NO EVENT WILL CAUCHO OR ITS LICENSORS BE LIABLE
 * FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
 * INABILITY TO USE SOFTWARE, EVEN IF HE HAS BEEN ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGES.      
 *
 * @author Scott Ferguson
 */

#include <linux/kernel.h>
#include <linux/ctype.h>
#include <linux/proc_fs.h>
#include <linux/in.h>

#include "hardcore.h"

static void
parse_header(browser_t *browser, char *key, char *value)
{
  if (! strnicmp(key, "content-length", sizeof("content-length"))) {
    int length = 0;
    int has_digit = 0;
    int ch;
          
    for (; (ch = *value) && (ch == ' ' || ch == '\t'); value++) {
    }
          
    for (; (ch = *value) >= '0' && ch <= '9'; value++) {
      has_digit = 1;
      length = 10 * length + ch - '0';
    }

    if (has_digit)
      browser->request_length = length;
  }
}

/**
 * Parses the request from the browser.
 */

int
request_parse(browser_t *browser)
{
  char *buf = browser->cin_buf;
  int i = browser->cin_offset;
  int len = browser->cin_length;
  int ch = 0;

  LOG(("parse off:%d len:%d\n", i, len));

  while (i < len && browser->state != RHC_BROWSER_BODY) {
    switch (browser->state) {
      /* At the beginning of the request, skip whitespace. */
    case RHC_BROWSER_START:
      for (; i < len && isspace((ch = buf[i])); i++) {
      }
      if (i < len) {
        browser->state = RHC_BROWSER_METHOD;
        browser->method = 0;
        browser->url = 0;
        browser->protocol = 0;
        browser->header_count = 0;
        browser->key[0] = 0;
      }
      break;

      /* whitespace skipping state, will switch to the next state */
    case RHC_BROWSER_SPACE:
      for (; i < len && ((ch = buf[i]) == ' ' || ch == '\t'); i++) {
      }
      if (i < len)
        browser->state = browser->next_state;
      break;
      
      /* skip to end of line */
    case RHC_BROWSER_EOL:
      for (; i < len && buf[i] != '\n'; i++) {
      }
      if (i < len) {
        i++;
        browser->state = browser->next_state;
      }
      break;

      /* parses the http method */
    case RHC_BROWSER_METHOD:
      if (! browser->method)
        browser->method = buf + i;
      for (; i < len && ! isspace((ch = buf[i])); i++) {
      }
      
      if (i < len) {
        buf[i++] =  0;

        browser->state = RHC_BROWSER_SPACE;
        browser->next_state = RHC_BROWSER_URL;
      }
      break;
    
      /* parses the http URL */
    case RHC_BROWSER_URL:
      if (! browser->url)
        browser->url = buf + i;
      for (; i < len && ! isspace((ch = buf[i])); i++) {
      }
      if (i < len) {
        buf[i++] = 0;

        if (ch == ' ' || ch == '\t') {
          browser->state = RHC_BROWSER_SPACE;
          browser->next_state = RHC_BROWSER_PROTOCOL;
        }
        else {
          browser->state = RHC_BROWSER_HEADER_DONE;
          browser->protocol = 0;
        }
      }
      else
        browser->state = RHC_BROWSER_HEADER_DONE;
      break;
    
      /* parses the http protocol */
    case RHC_BROWSER_PROTOCOL:
      if (! browser->protocol)
        browser->protocol = browser->cin_buf + i;
      for (; i < len && ! isspace((ch = buf[i])); i++) {
      }
      if (i < len) {
        buf[i++] = 0;
        browser->state = RHC_BROWSER_EOL;
        browser->next_state = RHC_BROWSER_KEY;
      }
      break;

    case RHC_BROWSER_KEY:
      /* If blank line, it's the end of the headers. */
      if (i < len && ((ch = buf[i]) == '\n' ||
                      (ch == '\r' && buf[i + 1] == '\n'))) {
        if (ch == '\n')
          i += 1;
        else
          i += 2;
        browser->state = RHC_BROWSER_HEADER_DONE;
        break;
      }
      /* If too many headers, ignore it and skip to the end of line */
      else if (browser->header_count >= 64) {
        browser->next_state = RHC_BROWSER_KEY;
        browser->state = RHC_BROWSER_EOL;
        break;
      }

      /* If at beginning of key, store the key offset */
      if (! browser->key[browser->header_count])
        browser->key[browser->header_count] = browser->cin_buf + i;

      for (; i < len && (ch = buf[i]) != ':'; i++) {
        if (ch == ' ' || ch == '\t')
          buf[i] = 0;
      }
      if (i < len) {
        buf[i++] = 0;
        browser->value[browser->header_count] = 0;
        browser->state = RHC_BROWSER_SPACE;
        browser->next_state = RHC_BROWSER_VALUE;
      }
      break;
      
    case RHC_BROWSER_VALUE:
      /* If at beginning of value, store the value offset */
      if (! browser->value[browser->header_count])
        browser->value[browser->header_count] = browser->cin_buf + i;

      for (; i < len && (ch = buf[i]) != '\n' && ch != '\r'; i++) {
      }
      if (i < len) {
        if (ch == '\r' && buf[i + 1] == '\n') {
          buf[i] = 0;
          i += 2;
        }
        else
          buf[i++] = 0;
        
        LOG(("header %s: %s\n",
             browser->key[browser->header_count],
             browser->value[browser->header_count]));

        parse_header(browser, browser->key[browser->header_count],
                     browser->value[browser->header_count]);
        
        browser->header_count++;
        if (browser->header_count < 64)
          browser->key[browser->header_count] = 0;
        
        browser->state = RHC_BROWSER_KEY;
      }
      break;

    case RHC_BROWSER_HEADER_DONE:
      LOG(("resin: %s %s %s\n",
           browser->method ? browser->method : "null",
           browser->url ? browser->url : "null",
           browser->protocol ? browser->protocol : "null"));
      browser->state = RHC_BROWSER_BODY;
      break;

    default:
      i++;
      break;
    }
  }

  if (i >= len && browser->state == RHC_BROWSER_HEADER_DONE) {
    browser->state = RHC_BROWSER_BODY;
    browser->in_header = 0;
  }

  browser->cin_offset = i;

  return i;
}
