/* testhttp.c */

/* Copyright 1997-2000 by Eberhard Mattes <em-gw@windhager.de>
   Donated to the public domain.  No warranty.

   1997-07-27 Initial version
   1997-09-12 -log simple-response
   1997-09-13 url_check_path() removed; use "href" for Refresh
   1997-10-20 Add openlog(), closelog(), and setlogmask()
   1997-10-21 libemfw.h defines octet
   1998-09-03 "-log content-type-conflict"
   1999-01-01 "-log unknown-content-type"
   1999-03-17 CONNECT (https/SSL)
   1999-05-10 "referer" configuration
   1999-09-16 allow/block style
   2000-04-15 Make NAME_data const */

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "magic.h"
#include "libemfw.h"
#include "emio.h"
#include "squid-gw.h"
#include "ctype.h"

int needauth = 0;
magic_t magic;
struct ctype_t* ctypes;

/* These variables must be global. */

EMI_FILE *c_in, *s_in;		/* Input from client, from server */
EMO_FILE *c_out, *s_out;	/* Output to client, to server */

static void usage (void) ATTR_NORETURN;

static void usage (void)
{
  fputs ("Usage: testhttp -client [<option>...]\n", stderr);
  fputs ("       testhttp -server <method> <URL> [<option>...]\n", stderr);
  fputs ("<method>: GET HEAD POST\n", stderr);
  fputs ("<option>:\n", stderr);
  fputs ("  -allow <what>...       Allow (don't block) <what>\n", stderr);
  fputs ("  -auto-html-limit <LIMIT>\n", stderr);
  fputs ("                         Set limit for auto HTML detection", stderr);
  fputs ("  -block <what>...       Block (don't allow) <what>\n", stderr);
  fputs ("  -cookies <cookies>...  How to parse cookies \n", stderr);
  fputs ("  -log <event>...        Log <event>\n", stderr);
  fputs ("  -http-fields <type>:<rej>[/log]\n", stderr);
  fputs ("                         Policy for header fields\n", stderr);
  fputs ("  -referer <referer> [<mask>]\n", stderr);
  fputs ("                         How to handle the Referer field \n", stderr);
  fputs ("  -user-agent <ua>       Set user-agent to <ua>\n", stderr);
  fputs ("<what>:\n", stderr);
  fputs ("  cookies                Cookies\n", stderr);
  fputs ("  javascript             JavaScript / LiveScript\n", stderr);
  fputs ("  style                  Style sheets\n", stderr);
  fputs ("<cookies>:\n", stderr);
  fputs ("  nocheck                Don't check cookies\n", stderr);
  fputs ("  netscape               Relaxed parsing\n", stderr);
  fputs ("  rfc2109                Strict parsing\n", stderr);
  fputs ("<referer>:\n", stderr);
  fputs ("  drop                   Drop unconditionally\n", stderr);
  fputs ("  keep-all               Keep unconditionally\n", stderr);
  fputs ("  keep-same-site         Keep if from same site\n", stderr);
  fputs ("  keep-match             Keep if matching <mask>\n", stderr);
  fputs ("<event>:\n", stderr);
  fputs ("  content-type           Content-Type from server\n", stderr);
  fputs ("  request                Request line\n", stderr);
  fputs ("  simple-response        simple-response\n", stderr);
  fputs ("<type>:\n", stderr);
  fputs ("  dangerous              Dangerous fields\n", stderr);
  fputs ("  invalid                Invalid fields\n", stderr);
  fputs ("  privacy                Fields disturbing privacy\n", stderr);
  fputs ("  silent                 Fields suggested to be dropped silently\n", stderr);
  fputs ("  unknown                Unknown fields\n", stderr);
  fputs ("<rej>:\n", stderr);
  fputs ("  copy                   Copy to output\n", stderr);
  fputs ("  drop                   Drop without any trace\n", stderr);
  fputs ("  prefix                 Prefix name with \"REMOVED-\"\n", stderr);
  exit (1);
}

void quit (int rc)
{
  exit (rc);
}

static int recursion = 0;

void vsyslog (int prio, const char *fmt, va_list arg_ptr)
{
  if (recursion++ != 0)
    abort ();
  emo_flush (s_out); emo_flush (c_out);
  emo_vprintf (s_out, fmt, arg_ptr);
  emo_write (s_out, "\r\n", 2);
  emo_flush (s_out); emo_flush (c_out);
  --recursion;
}

void syslog (int prio, const char *fmt, ...)
{
  va_list arg_ptr;

  va_start (arg_ptr, fmt);
  vsyslog (prio, fmt, arg_ptr);
  va_end (arg_ptr);
}

/* In case all the syslog()-related functions are in one module... */

void openlog (const char *ident, int logopt, int facility)
{
}

void closelog (void)
{
}

int setlogmask (int maskpri)
{
  return 0;
}

void html_copy (EMO_FILE *out, EMI_FILE *in, long size)
{
  emo_puts (out, "html_copy\r\n");
  http_copy (out, in, size);
}

static void prej (const char *s, struct rej_policy *dst, unsigned flags)
{
  if (parse_rej_policy (s, dst, flags) != 0)
    usage ();
}

void config_browsers (const char *s)
{
}

int ctype_policy (const char *ctype, char ***filters)
{
  return 0;
}

int config_destinations (const char *s, const struct url *u,
			 const char *method, const char **msg,
			 const char **redir)
{
  return 0;
}

int config_connect (const char *hostname, int port,
		    octet *ipaddr, const char **msg)
{
  return 0;
}

int config_href (const char *s, const struct url *u)
{
  return u->scheme != URL_OTHER_SCHEME ? 0 : -1; /* TODO */
}

int main (int argc, char *argv[])
{
  int i, client, block;
  const char *s;
  char *end;
  long n;

  http_default_config ();
  cf_http.test = 1;		/* Accept multiple Referer fields */

  i = 1;
  if (argc < 2)
    usage ();
  if (strcmp (argv[i], "-client") == 0)
    {
      client = 1; ++i;
    }
  else if (argc >= 4 && strcmp (argv[i], "-server") == 0)
    {
      client = 0;
      if (http_method (argv[i+1], strlen (argv[i+1])) != 0)
	usage ();
      req_url_str = argv[i+2];
      if (url_parse (&req_url, (octet*) argv[i+2], strlen (argv[i+2]), 0) != 0)
	usage ();
      i += 3;
    }
  else
    usage ();

  while (i < argc)
    if (strcmp (argv[i], "-block") == 0 || strcmp (argv[i], "-allow") == 0
	|| strcmp (argv[i], "-b") == 0 || strcmp (argv[i], "-a") == 0)
      {
	block = argv[i][1] == 'b';
	++i;
	if (i >= argc || argv[i][0] == '-')
	  usage ();
	while (i < argc && argv[i][0] != '-')
	  {
	    if (strcmp (argv[i], "cookies") == 0)
	      cf_http.block_cookies.v = block;
	    else if (strcmp (argv[i], "javascript") == 0)
	      cf_http.block_javascript.v = block;
	    else if (strcmp (argv[i], "style") == 0)
	      cf_http.block_style.v = block;
	    else
	      usage ();
	    ++i;
	  }
      }
    else if (strcmp (argv[i], "-auto-html-limit") == 0)
      {
	++i;
	n = strtol (argv[i], &end, 10);
	if (n < 0 || *end != 0 || n > S_IN_BUFSIZE)
	  usage ();
	cf_http.auto_html_limit = (int)n;
	++i;
      }
    else if (strcmp (argv[i], "-log") == 0)
      {
	++i;
	if (i >= argc || argv[i][0] == '-')
	  usage ();
	while (i < argc && argv[i][0] != '-')
	  {
	    if (strcmp (argv[i], "content-type") == 0)
	      cf_http.log_content_type = 1;
	    else if (strcmp (argv[i], "content-type-conflict") == 0)
	      cf_http.log_content_type_conflict = 1;
	    else if (strcmp (argv[i], "missing-content-type") == 0)
	      cf_http.log_missing_content_type = 1;
	    else if (strcmp (argv[i], "request") == 0)
	      cf_http.log_request = 1;
	    else if (strcmp (argv[i], "simple-response") == 0)
	      cf_http.log_simple_response = 1;
	    else if (strcmp (argv[i], "unknown-content-type") == 0)
	      cf_http.log_unknown_content_type = 1;
	    else
	      usage ();
	    ++i;
	  }
      }
    else if (strcmp (argv[i], "-cookies") == 0)
      {
	++i;
	if (i >= argc || argv[i][0] == '-')
	  usage ();
	if (parse_cookies_mode (argv[i]) != 0)
	  usage ();
	++i;
      }
    else if (strcmp (argv[i], "-referer") == 0)
      {
	++i;
	if (i >= argc || argv[i][0] == '-')
	  usage ();
	if (parse_referer_mode (argv[i]) != 0)
	  usage ();
	++i;
	if (cf_http.referer == REF_KEEP_MATCH)
	  {
	    if (i >= argc || argv[i][0] == '-')
	      usage ();
	    cf_http.referer_mask = argv[i];
	    if (url_parse (&cf_http.referer_url, (octet*) cf_http.referer_mask,
			   strlen (cf_http.referer_mask),
			   UPF_WILDCARD | UPF_NODEFPORT) != 0)
	      {
		fputs ("-referer: invalid URL\n", stderr);
		exit (1);
	      }
	    ++i;
	  }
      }
    else if (strcmp (argv[i], "-user-agent") == 0)
      {
	if (++i >= argc)
	  usage ();
	cf_http.user_agent = argv[i++];
      }
    else if (strcmp (argv[i], "-http-fields") == 0)
      {
	if (++i >= argc)
	  usage ();
	while (i < argc && argv[i][0] != '-')
	  {
	    s = argv[i++];
	    if (strncmp (s, "dangerous:", 10) == 0)
	      prej (s + 10, &cf_http.field_dangerous,
		    RM_DROP | RM_PREFIX);
	    else if (strncmp (s, "invalid:", 8) == 0)
	      prej (s + 8, &cf_http.field_invalid,
		    RM_DROP);
	    else if (strncmp (s, "unknown:", 8) == 0)
	      prej (s + 8, &cf_http.field_unknown,
		    RM_DROP | RM_PREFIX | RM_COPY);
	    else if (strncmp (s, "privacy:", 8) == 0)
	      prej (s + 8, &cf_http.field_privacy,
		    RM_DROP | RM_PREFIX | RM_COPY);
	    else if (strncmp (s, "silent:", 7) == 0)
	      prej (s + 7, &cf_http.field_silent,
		    RM_DROP | RM_PREFIX);
	    else
	      usage ();
	  }
      }
    else
      usage ();

  if (i != argc)
    usage ();

  if (client)
    {
      c_in = emi_fdopen (0, 4096);
      c_out = emo_fdopen (3, 4096);
      s_in = NULL;
      s_out = emo_fdopen (1, 4096);

      if (c_in == NULL || c_out == NULL || s_out == NULL)
	{
	  perror ("emi_fdopen()");
	  exit (2);
	}

      http_read_request ();
      if (http_process_request ())
	http_send_request1 ();
    }
  else
    {
      c_in = NULL;
      c_out = emo_fdopen (1, 4096);
      s_in = emi_fdopen (0, 4096);;
      s_out = c_out;

      if (c_out == NULL || s_in == NULL)
	{
	  perror ("emi_fdopen()");
	  exit (2);
	}

      /* TODO: req_st.method_flags */
      http_response ();
    }

  /* Don't close the files as we might want to write to file
     descriptor 2! */

  if (emo_flush (c_out) != 0 || (s_out != c_out && emo_flush (s_out) != 0))
    {
      perror ("emo_flush()");
      exit (2);
    }
  return 0;
}
