/*
 * Copyright (c) 1996-2007, OpenFWTK Development Group
 * All rights reserved. See LICENSE.
 */

/* squid-log.c */

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

   1997-08-27 Initial version
   2000-07-07 Use <unistd.h> instead of <getopt.h> */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#include <errno.h>
#include "libemfw.h"

#define ISDIGIT(c) ((c) >= '0' && (c) <= '9')

struct connection
{
  struct connection *next;
  struct connection *prev;
  char *content_type;
  unsigned long pid;
};

#define HASH_SIZE 997
#define HASH(x) ((x) % HASH_SIZE)

static struct connection *hash_table[HASH_SIZE];

static void usage (void)
{
  puts ("Usage: squid-log [<log_file>...]");
  exit (1);
}

static struct connection *new_connection (long pid)
{
  unsigned h;
  struct connection *p;

  h = HASH (pid);
  p = xmalloc (sizeof (struct connection));
  p->pid = pid;
  p->content_type = NULL;
  p->prev = NULL;
  p->next = hash_table[h];
  if (hash_table[h] != NULL)
    hash_table[h]->prev = p;
  hash_table[h] = p;
  return p;
}

static void do_content_type (const char *s, unsigned long pid,
			     struct connection *p)
{
  if (p != NULL)
    free (p->content_type);
  else
    p = new_connection (pid);
  p->content_type = xstrdup (s);
}

static void do_exit (const char *s, unsigned long pid, struct connection *p)
{
  int host_len;
  const char *req = NULL, *ct = NULL, *host, *in, *out;

  if (p != NULL)
    {
      ct = p->content_type;
    }
  if (! (req =strstr(s,"cmd='")) || !*(req +=5))
    req = "- - -";
  if (ct == NULL)
    ct = "-";
  if (strchr (s, '\t') != NULL)
    return;
  host = s;
  s = strchr (s, ' ');
  if (s == NULL)
    return;
  host_len = s - host;
  ++s;
  if (strncmp(s,"in=",3))
    return;

  in = (s+=3);
  while (ISDIGIT (*s))
    s++;
  if (*s++ == ' ') {
    if (!strncmp(s,"out=",4)) 
      out = (s+=4);
    else
      return;
  } else
    return;
  printf ("%.*s\t", host_len, host);
  while (*req && (*req != '\''))
    putchar (*req++);
  printf("\t%s\t",ct);
  while (ISDIGIT (*out))
    putchar (*out++);
  putchar(' ');
  while (ISDIGIT (*in))
	putchar (*in++);
  putchar ('\n');
  if (p != NULL)
    {
      if (p->prev == NULL)
	hash_table[HASH (pid)] = p->next;
      else
	p->prev->next = p->next;
      if (p->next != NULL)
	p->next->prev = p->prev;
      free (p);
    }
}

static void read_log (FILE *f)
{
  static char buf[65536];
  size_t len;
  int c, i;
  unsigned long pid;
  char *s;
  struct connection *p;
  const char *pattern = " squid-gw[";

  for (i = 0; i < HASH_SIZE; ++i)
    hash_table[i] = NULL;
  while (fgets (buf, sizeof (buf), f) != NULL)
    {
      len = strlen (buf);
      if (len == 0 || buf[len-1] != '\n')
	{
	  do
	    {
	      c = fgetc (f);
	    } while (c != EOF && c != '\n');
	  if (c == EOF)
	    break;
	  continue;
	}
      buf[len-1] = 0;
      s = strstr (buf, pattern);
      if (s == NULL)
	continue;
      s += strlen (pattern);
      if (!ISDIGIT (*s))
	continue;
      pid = 0;
      while (ISDIGIT (*s))
	{
	  pid = pid * 10 + *s - '0';
	  ++s;
	}
      if (strncmp (s, "]: ", 3) != 0)
	continue;
      s += 3;
      for (p = hash_table[HASH (pid)]; p != NULL; p = p->next)
	if (p->pid == pid)
	  break;
      if (strncmp (s, "Content-Type: ", 14) == 0)
	do_content_type (s + 14, pid, p);
      else if (strncmp (s, "exit host=", 10) == 0)
	do_exit (s + 10, pid, p);
    }
}

int main (int argc, char *argv[])
{
  int i;
  FILE *f;

  if (argc > 1 && argv[1][0] == '-' && argv[1][1] != 0)
    usage ();
  if (argc == 1)
    read_log (stdin);
  else
    {
      for (i = 1; i < argc; ++i)
	{
	  f = fopen (argv[i], "r");
	  if (f == NULL)
	    {
	      perror (argv[i]);
	      exit (1);
	    }
	  read_log (f);
	  fclose (f);
	}
    }
  return 0;
}
