/******************************************************************************
 *
 *  This code has been modified to compose a filmloop of image files.
 *
 *  This code is released into the public domain. 
 *
 *  Last modfied by Rus Berrett, SurfUtah.Com (http://www.surfutah.com)
 *  November 28, 1995
 *
 *  example of how to use "push.cgi" to push 8 images located in a directory-
 *
 *  image0.gif
 *  image1.gif
 *  image2.gif
 *  image3.gif
 *  image4.gif
 *  image5.gif
 *  image6.gif
 *  image7.gif
 *
 *  <img src="/cgi-bin/push.cgi/usr/local/httpd/images?base=image&lastframe=7">
 *            |---------------||---------------------| |--------| |----------|
 *              path to cgi       path to images        basename   last frame 
 *              executable        that are pushed       of images 
 *
 *  Other options:  firstframe, delay, reverse, numloops, imagetype
 *
 *  By default-
 *  firstframe will be "0"
 *  the delay will be "0"
 *  reverse will be "0" (FALSE)
 *  numloops will be "1"
 *  imagetype will be "gif"
 *
 *  So, if the you had 8 jpeg's in the same directory (as the example above)
 *  and want to cycle 1-8 then 8-1 and also to do it twice.  I could use the 
 *  following cgi call as the "src":
 *
 *  /cgi-bin/push.cgi/usr/local/httpd/images?base=image&lastframe=7&reverse=1&numloops=2&imagetype=jpeg
 *
 *  Please note that name/value arguments are separated by an ampersand "&",
 *  and that no spaces (or carriage returns) are present.
 *
 *  I know, I know- it's really long.  Well. too bad- feel free to modify
 *  the source in any way you see fit.
 *
 *  Also... note that server push only works for Netscape 1.1+, so I just
 *  display the first image in the loop for other HTTP_USER_AGENTs
 *
 *****************************************************************************/

/*
 * doit.c: Quick hack to play a sequence of image files.
 * 
 * Rob McCool
 *
 * This code is released into the public domain.  Do whatever 
 * you want with it.
 *
 */

#define _INCLUDE_POSIX_SOURCE 1
#define _INCLUDE_XOPEN_SOURCE 1
#define _INCLUDE_AES_SOURCE 1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

#define HEADER "Content-type: multipart/x-mixed-replace;boundary=ThisRandomString\n"

#define RANDOMSTRING "\n--ThisRandomString\n"
#define ENDSTRING "\n--ThisRandomString--\n"
char CTSTRING[256];

#define DEBUG 0

/*---------------------------------------------------------------------------*/
static int HexToDecimal(char first, char second)
{
  int value;

  first = tolower(first);
  second = tolower(second);

  switch (first) {
    case '0':  value = 16 * 0;  break;
    case '1':  value = 16 * 1;  break;
    case '2':  value = 16 * 2;  break;
    case '3':  value = 16 * 3;  break;
    case '4':  value = 16 * 4;  break;
    case '5':  value = 16 * 5;  break;
    case '6':  value = 16 * 6;  break;
    case '7':  value = 16 * 7;  break;
    case '8':  value = 16 * 8;  break;
    case '9':  value = 16 * 9;  break;
    case 'a':  value = 16 * 10;  break;
    case 'b':  value = 16 * 11;  break;
    case 'c':  value = 16 * 12;  break;
    case 'd':  value = 16 * 13;  break;
    case 'e':  value = 16 * 14;  break;
    case 'f':  value = 16 * 15;  break;
  }
  switch (second) {
    case '0':  value += 0;  break;
    case '1':  value += 1;  break;
    case '2':  value += 2;  break;
    case '3':  value += 3;  break;
    case '4':  value += 4;  break;
    case '5':  value += 5;  break;
    case '6':  value += 6;  break;
    case '7':  value += 7;  break;
    case '8':  value += 8;  break;
    case '9':  value += 9;  break;
    case 'a':  value += 10;  break;
    case 'b':  value += 11;  break;
    case 'c':  value += 12;  break;
    case 'd':  value += 13;  break;
    case 'e':  value += 14;  break;
    case 'f':  value += 15;  break;
  }

  return(value);

} /* HexToDecimal */

/*---------------------------------------------------------------------------*/
static int ReadNameValuePair(char *thestring, char *name, char *thevalue)
{
  char *cptr = NULL, *endcptr = NULL;

  cptr = strstr(thestring, "=");
  cptr[0] = '\0'; 
  strcpy(name, thestring);
  cptr++;
  endcptr = strstr(cptr, "&");
  if (endcptr) {
    endcptr++;
    endcptr[-1] = '\0';
  }

    /* copy the value of before the original string gets overwritten */
  strcpy(thevalue, cptr);

  if (endcptr)
    strcpy(thestring, endcptr);

  if (strstr(thestring, "="))
    return(0);
  else
    return(1);

} /* ReadNameValuePair */

/*---------------------------------------------------------------------------*/
void main(int argc, char *argv[]) 
{
    /* variables set by cgi arguments */
  char PATH_NAME[1000];          /* the path name of the images         */
  char IMAGE_BASE_NAME[256];     /* the base name of the images         */
  int LAST_FRAME;                /* the number of images                */
  int FIRST_FRAME = 0;           /* id of first frame (usually 0 or 1)  */
  int DELAY = 0;                 /* seconds to delay between each image */
  int REVERSE_LOOP = 0;          /* turn reverseing ON (1) or OFF (0)   */
  int NUM_LOOPS = 1;             /* the number of loops to show >= 1    */
  char IMAGE_TYPE[32];           /* the content type, "gif", "jpeg"     */
    /* other local variables */
  struct stat fi;
  char fn[512];
  caddr_t fp;
  int fd, enhanced = 0;
  int done, i, len, forward = 1, nindex, index;
  int frameid = FIRST_FRAME;
  char thestring[2000], agent[512], *cptr = NULL;
  char name[256], value[256];

  strcpy(IMAGE_TYPE, "gif");

  sprintf(PATH_NAME, "%s", getenv("PATH_INFO"));
  len = strlen(PATH_NAME);
  if (PATH_NAME[len-1] != '/') {
    PATH_NAME[len] = '/';
    PATH_NAME[len+1] = '\0';
  }

  sprintf(thestring, "%s", getenv("QUERY_STRING"));

  nindex = 0;
  len = strlen(thestring);
  for (index = 0; index < len; index++) {
    if (thestring[index] == '+') {
      thestring[nindex++] = ' ';
    }
    else if (thestring[index] == '%') {
        /* convert hex to decimal */
      thestring[nindex++] = (char)HexToDecimal(thestring[index+1],
                                               thestring[index+2]);
      index+=2;
    }
    else {
      thestring[nindex++] = thestring[index];
    }
  }
  thestring[nindex] = '\0';

  done = 0;
  while (!done) {
    done = ReadNameValuePair(thestring, name, value);
    if (!strcmp(name, "base"))
      strcpy(IMAGE_BASE_NAME, value);
    else if (!strcmp(name, "lastframe"))
      sscanf(value, "%d", &LAST_FRAME);
    else if (!strcmp(name, "firstframe"))
      sscanf(value, "%d", &FIRST_FRAME);
    else if (!strcmp(name, "delay"))
      sscanf(value, "%d", &DELAY);
    else if (!strcmp(name, "reverse"))
      sscanf(value, "%d", &REVERSE_LOOP);
    else if (!strcmp(name, "numloops"))
      sscanf(value, "%d", &NUM_LOOPS);
    else if (!strcmp(name, "imagetype"))
      strcpy(IMAGE_TYPE, value);
  }

    /* debugging */
  if (DEBUG) {
    printf("Content-Type: text/html\n\n");
    printf("path = %s\n<p>\n", PATH_NAME);
    printf("basename = %s\n<p>\n", IMAGE_BASE_NAME);
    printf("lastframe = %d\n<p>\n", LAST_FRAME);
    printf("firstframe = %d\n<p>\n", FIRST_FRAME);
    printf("delay = %d\n<p>\n", DELAY);
    printf("reverseloop = %d\n<p>\n", REVERSE_LOOP);
    printf("numloops = %d\n<p>\n", NUM_LOOPS);
    printf("imagetype = %s\n<p>\n", IMAGE_TYPE);
  }
   
  sprintf(CTSTRING, "Content-type: image/%s\n\n", IMAGE_TYPE);

    /* test for Netscape 1.1 or higher */
  sprintf(agent, "%s", getenv("HTTP_USER_AGENT"));
  cptr = strstr(agent, "Mozilla");
  if (cptr) {
    cptr += 8;
    if (strncmp("1.1", cptr, 3) <= 0)
      enhanced = 1;
  }

  if (enhanced) {
    if(write(STDOUT_FILENO, HEADER, strlen(HEADER)) == -1)
      exit(0);
    if(write(STDOUT_FILENO, RANDOMSTRING, strlen(RANDOMSTRING)) == -1)
      exit(0);

    for (i = 0; i < NUM_LOOPS; i++) {
      done = 0;
      forward = 1;
      frameid = FIRST_FRAME;
      while (!done) {
        if (!DEBUG) {
          if(write(STDOUT_FILENO, CTSTRING, strlen(CTSTRING)) == -1)
            exit(0);
          sprintf(fn, "%s%s%d.%s", 
                  PATH_NAME, IMAGE_BASE_NAME, frameid, IMAGE_TYPE);
          if((fd = open(fn, O_RDONLY)) == -1)
            continue;
          fstat(fd, &fi);
          fp = mmap(NULL, fi.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
          if(fp == (caddr_t) -1)
            exit(0);
          if(write(STDOUT_FILENO, (void *)fp, fi.st_size) == -1)
            exit(0);
          munmap(fp, fi.st_size);
          close(fd);
          if(write(STDOUT_FILENO, RANDOMSTRING, strlen(RANDOMSTRING)) == -1)
            exit(0);
        }
        else {
          printf("filename = %s%s%d.%s<br>\n",
                  PATH_NAME, IMAGE_BASE_NAME, frameid, IMAGE_TYPE);
        }

        if (REVERSE_LOOP) {
          if (frameid >= LAST_FRAME)
            forward = 0;
          done = (!forward && frameid == FIRST_FRAME);
        } 
        else {
          done = (frameid >= LAST_FRAME);
        }
        if (forward)
          frameid++;
        else
          frameid--;
        if (DELAY)
          sleep(DELAY);
      } 
    }
/*    write(STDOUT_FILENO, ENDSTRING, strlen(ENDSTRING)); */
  } 
  else { 
    if(write(STDOUT_FILENO, CTSTRING, strlen(CTSTRING)) == -1)
      exit(0);
    sprintf(fn, "%s%s%d.%s", PATH_NAME, IMAGE_BASE_NAME, frameid, IMAGE_TYPE);
    if((fd = open(fn, O_RDONLY)) == -1)
      exit(0);
    fstat(fd, &fi);
    fp = mmap(NULL, fi.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if(fp == (caddr_t) -1)
      exit(0);
    if(write(STDOUT_FILENO, (void *)fp, fi.st_size) == -1)
      exit(0);
    munmap(fp, fi.st_size);
    close(fd);
  }

} /* main */

/*---------------------------------------------------------------------------*/
/* eof */

