/* -------------------------------------------------------------------------- */
/*
                      NAME : count.c
               DESCRIPTION : Graphical counter on WWW pages.

                    AUTHOR : Heini Withagen
                    E-MAIL : heini@w4.nl
	          WWW-SITE : http://sparkie.riv.net/w4/index.html
                  CREATION : 05-05-1995
         LAST MODIFICATION : 30-08-1995

*/ 
/* -------------------------------------------------------------------------- */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <fcntl.h>
#include <sys/file.h>
#include <unistd.h>
#include "gd/gd.h"
#include "misc.h"
#include "count.h"
#include "cgi-util.h"
#include "image.h"
#include "conf.h"

int debug;

/* local functions */
int parse_arguments(int arg_count, struct entry *entries, struct info *info);

int update_counters(struct info *info);
struct info *read_admin_file(int *record_count);
int write_admin_file(int record_count, struct info *info_records);
int parse_line(char *line, struct info *info);

int debug_output(struct info *info);

/* --------------------------------------------------------------------------- */

int main(int argc, char *argv[]) 
{
  int arg_count;
  struct entry entries[32];
  struct info info;


  init();

  arg_count = retrieve_arguments(entries);
 
  if ( parse_arguments(arg_count, entries, &info) == ERROR )
  {
    errorlog("program called with bad arguments");
    exit_with_http_error("Bad arguments");
  }

  if (debug)
    debug_output(&info);

  if ( update_counters(&info) == ERROR )
    exit_with_http_error("Internal error image counter");

  if ( construct_counter_image(&info) == ERROR )
    exit_with_http_error("Internal error image counter");

  send_counter_image();

  release_lock();
  return 0;
}

/* --------------------------------------------------------------------------- */

int parse_arguments(int arg_count, struct entry *entries, struct info *info)
{
  int x;

  /* initialize info structure */
  info->width = 0;
  info->link[0] = '\0';
  info->count_value = 0;
  info->increase = 1;
  info->show = TRUE;

  debug = FALSE;


  /* go through the arguments one by one and 
     fill the info structure accordingly */

  for (x = 0; x < arg_count; x++)
  {
    if ( !strcmp(entries[x].name, "width") )
    {
      info->width = abs(atoi(entries[x].val));
      continue;
    }

    if ( !strcmp(entries[x].name, "link") )
    {
      int y = 0;
      char *ptr_src = entries[x].val;
      char *ptr_dst = info->link;

      while ( (*ptr_src) && (y < 64) )
        if ( ((*ptr_src >= 'A') && (*ptr_src <= 'Z')) ||
             ((*ptr_src >= 'a') && (*ptr_src <= 'z')) ||
             ((*ptr_src >= '0') && (*ptr_src <= '9')) ||
             (*ptr_src == ':') ||
             (*ptr_src == '/') ||
             (*ptr_src == '.') )
        {
          *ptr_dst++ = *ptr_src++;
          y++; 
        }
        else
        {
          *ptr_dst++ = '_';
          *ptr_src++;
          y++;
        }
      *ptr_dst = '\0';
      continue;
    }

    if ( !strcmp(entries[x].name, "increase") )
    {
      info->increase = atoi(entries[x].val);
      continue;
    }

    if ( !strcmp(entries[x].name, "show") )
    {
      if ( !strcmp(entries[x].val, "NO") )
        info->show = FALSE;
      continue;
    }

    if ( !strcmp(entries[x].name, "debug") )
    {
      if ( !strcmp(entries[x].val, "ON") )
        debug = TRUE;
      continue;
    }
  }

  if ( !strlen(info->link) )
    return ERROR;

  return OK;
}

/* --------------------------------------------------------------------------- */

int update_counters(struct info *info)
{
  int count, found = FALSE, number_of_records;
  struct info *info_records, *info_ptr;
  char str[256];


  /* make sure we are not interrupted in this function */
  alarm(0);

  /* read in file with counter information */
  info_records = read_admin_file(&number_of_records);
  if ( !info_records && number_of_records)
    return ERROR;


  /* look if the passed link is already in the list
     just read from file,
     if so, adjust the count value for that link */
  info_ptr = info_records;
  for (count = 0; count < number_of_records; count++, info_ptr++)
    if ( !strcmp(info_ptr->link, info->link) )
    {
      found = TRUE;
      info_ptr->count_value += info->increase;
      if (info_ptr->count_value < 0)
        info_ptr->count_value = 0;

      info->count_value = info_ptr->count_value;
      break;
    }


  /* if the passed was not found in the list, add
     it to the list now */
  if (!found)
  {
    info_records = (struct info *)realloc(info_records, sizeof(struct info) * (number_of_records+1) );
    if (!info_records)
      return ERROR;

    strcpy(info_records[number_of_records].link, info->link);
    info_records[number_of_records].count_value = (info->increase > 0)?info->increase:0;

    info->count_value = info_records[number_of_records].count_value;
    number_of_records++;
  }


  /* write file with counter information */
  if (write_admin_file(number_of_records, info_records) == ERROR)
    return ERROR;

  alarm(ALARM_INTERVAL);
   
  return OK;
}

/* --------------------------------------------------------------------------- */

struct info *read_admin_file(int *record_count)
{
  struct info *info_records, *info_ptr;
  FILE *fp;
  int count, errors = 0;
  char line[MAX_LENGTH], str[128];


  if ( !(fp = fopen(COUNT_FILE, "r")) )
  {
    *record_count = 0;
    return NULL;
  }

  /* read the number of entries in the file */
  fgets(line, MAX_LENGTH, fp);
  *record_count = atoi(line);

  /* allocate space */
  info_records = (struct info *)malloc( sizeof(struct info) * (*record_count) );
  if (!info_records)
  {
    errorlog("memory allocation problem");
    return NULL;
  }
  info_ptr = info_records;
  errors = 0;

  for (count = 0; count < (*record_count); count++)
  {
    fgets(line, MAX_LENGTH, fp);
    if (parse_line(line, info_ptr) == ERROR)
    {
      errors++; 
      errorlog("error detected in count_file; removing line");
      continue;
    }
    info_ptr++;
  }
  fclose(fp);

  /* correct for any errors encountered
     in the count_file */
  *record_count -= errors;

  return info_records;
}

/* --------------------------------------------------------------------------- */

int write_admin_file(int record_count, struct info *info_records)
{
  FILE *fp;
  int count; 
  struct info *info_ptr;

  if ( !(fp = fopen(COUNT_FILE, "w")) )
  {
    errorlog("problem opening admin file for writing");
    return ERROR;
  }

  fprintf(fp,"%d\n", record_count);

  info_ptr = info_records;
  for (count = 0; count < record_count; count++, info_ptr++)
    fprintf(fp, "%s\t%d\n", info_ptr->link, info_ptr->count_value);

  fclose(fp);
  free(info_records);

  return OK;
}

/* --------------------------------------------------------------------------- */

int parse_line(char *line, struct info *info_ptr)
{
  char *line_ptr = line;
  char *link_ptr = info_ptr->link;

  while ((*line_ptr) && (*line_ptr != '\t'))
  {
    *link_ptr = *line_ptr;
    link_ptr++;
    line_ptr++;
  }

  *link_ptr = '\0';

  if (*line_ptr)
    line_ptr++;

  info_ptr->count_value = atoi(line_ptr);

  if ( !strlen(info_ptr->link) )
    return ERROR;

  return OK;
}

/* --------------------------------------------------------------------------- */

int debug_output(struct info *info)
{
  char str[128];
  int errors = 0;

  fputs("HTTP/1.0 200 OK\n", stdout);
  fputs("Content-type: text/html\n\n", stdout);

  fputs("<TITLE>WWW-page Access Counter</TITLE>\n" , stdout);
  fputs("<H2>WWW-page Access Counter Debugging</H2><HR>\n", stdout);

  fputs("<H3>Received input arguments:</H3><UL>", stdout);
 
  if (info->width)
  {
    sprintf(str, "<LI> <B>width</B> = %d", info->width);
    fputs(str, stdout);
  }

  if (info->link[0])
  {
    sprintf(str, "<LI> <B>link</B> = %s", info->link);
    fputs(str, stdout);
  }

  if (info->increase != 1)
  {
    sprintf(str, "<LI> <B>increase</B> = %d", info->increase);
    fputs(str, stdout);
  }

  if (!info->show)
  {
    sprintf(str, "<LI> <B>show</B> = NO");
    fputs(str, stdout);
  }

  fputs("</UL><HR>", stdout);

  fputs("<H3>Checking necessary files:</H3><UL>", stdout);


  sprintf(str, "<LI>Administration file: <I>%s</I><BR>", COUNT_FILE);
  fputs(str, stdout);

  if ( fopen(COUNT_FILE, "a+") )  
  {
    sprintf(str, "File opened for reading and writing.<BR>");
    fputs(str, stdout);
  }
  else
  {
    sprintf(str, "File could <B>NOT</B> be opened for reading and writing.<BR>");
    fputs(str, stdout);
    fputs("<B>WARNING: Check the filename and permissions</B>", stdout);
    errors++;
  }


  sprintf(str, "<LI>Image directory: <I>%s</I><BR>", PIC_PATH);
  fputs(str, stdout);

  sprintf(str, "%s0.gd", PIC_PATH);
  if ( fopen(str, "r") )
  {
    fputs("Image files located and reading is possible<BR>", stdout);
  }
  else
  {
    fputs("Problems opening image files<BR>", stdout);
    fputs("<B>WARNING: Check the directory-name and permissions</B>", stdout);
    errors++;
  }

  if (!errors)
    fputs("</UL><B>Looking good !!</B><HR>\n", stdout);
  else
    fputs("</UL><HR>\n", stdout);


  fputs("<H3>Output test:</H3>", stdout);
  if (!errors)
   fputs("The counter image should appear below:<P>", stdout);
  else
   fputs("Errors were detected. Most probably the broken image will appear below:<P>", stdout);

  sprintf(str,"<IMG SRC=\"/cgi-bin/nph-count?width=%d&link=%s&increase=%d\">", info->width, info->link, info->increase);
  fputs(str, stdout);

  fputs("<P><HR><I>&copy <A HREF=\"http://sparkie.riv.net/w4/\">W4 Consultancy</A>, All rights reserved</I>", stdout);

  release_lock();
  exit(0);
}

/* --------------------------------------------------------------------------- */
