/*
    LGirl: A proxy filter for the Reason Hit & Run blog
    Copyright (C) 2007 Richard Heurtley

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    History:

    2007-07-11 v1.0 First release
    2007-07-12 v1.1 Fixed a problem with chunk decoding
    2007-07-13 v1.2 Added URL replacement
*/
/**---------------------------------------------------------------------------*/
#ifdef __WATCOMC__
#define WINDOWS
#endif

#ifdef _MSC_VER
#define WINDOWS
#endif

#ifndef WINDOWS
#define LINUX
#endif
/**---------------------------------------------------------------------------*/
#ifdef WINDOWS
#include <winsock2.h>
#endif

#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#ifdef LINUX
#include <arpa/inet.h>
#include <asm/errno.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#endif
/**---------------------------------------------------------------------------*/
//#define DEBUG

#define STATE_HEADER		(0)
#define STATE_CONTENTLENGTH	(1)
#define STATE_CHUNKED		(2)

#define memeq !memcmp
#define streq !strcmp

#ifdef LINUX
#define SOCKET int
#define WSAGetLastError() errno
#define closesocket close
#endif

#ifdef WINDOWS
#define socklen_t int
#endif
/**---------------------------------------------------------------------------*/
typedef struct
{
 char type;		// 'n' or 'l'
 int len;
 char text[64+1];
} FILTER;

#define NFILTERS (64)

typedef struct
{
 int bufferlen;
 int headerlen;
 int bodylen;
 int nlines;
 char *bodyp;
 char *lines[1024];
 char header[65536];
 char buffer[2 * 1024 * 1024];
} LOCAL;

static LOCAL l;
/**---------------------------------------------------------------------------*/
int streqz(char *p0, char *p1)
{
 int rv;
 char c0;
 char c1;

 if (!*p0 || !*p1)		// if either string is empty
  return(0);			// then return no match

 for (;;)
 {
  c0 = *p0++; 
  c1 = *p1++;
  if (!c0 || !c1)
   break;
  if (c0 != c1)
   break;
 }

 if (!c0 || !c1)
  rv = 1;
 else
  rv = 0;

 return(rv);
}
/**---------------------------------------------------------------------------*/
int strieqz(char *p0, char *p1)
{
 char c0;
 char c1;
 int rv;

 if (!*p0 || !*p1)		// if either string is empty
  return(0);			// then return no match

 for (;;)
 {
  c0 = *p0++; 
  c1 = *p1++;
  if (!c0 || !c1)
   break;
  if (toupper(c0) != toupper(c1))
   break;
 }

 if (!c0 || !c1)
  rv = 1;
 else
  rv = 0;

 return(rv);
}
/**---------------------------------------------------------------------------*/
char *stristr(char *srcp, char *subp)
{
 int i;
 int scanlen;
 char *scanp;

 scanlen = strlen(srcp) - strlen(subp);

 for (i = 0; i <= scanlen; i++)
 {
  scanp = &srcp[i];
  if (strieqz(scanp, subp))
   break;
 }

 if (i > scanlen)
  scanp = 0;

 return(scanp);
}
/**---------------------------------------------------------------------------*/
int BigSend(SOCKET socket, char *bufferp, int bufferlen, socklen_t *socklenp)
{
 int rv = 0;
 int n;
 int nbytes;
 socklen_t socklen;
 int remain;
 char *p;

 p = bufferp;
 remain = bufferlen;
 socklen = 0;

 while (remain)
 {
  if (remain > 32768)
   nbytes = 32786;
  else
   nbytes = remain;

  n = send(socket, p, nbytes, 0);
  if (n != nbytes)
  {
   printf("Error while transmitting.\n");
   printf("Tried to send %d bytes, sent %d bytes.\n", nbytes, n);
   rv = 1;
   goto EGRESS;
  }

  socklen += n;
  p += n;
  remain -= n;
 }

EGRESS:

 if (socklenp)
  *socklenp = socklen;

 return(rv);
}
/**---------------------------------------------------------------------------*/
char *TimeStamp(void)
{
 time_t timet;
 static char text[4+1+2+1+2 +1+ 2+1+2+1+2 +1];

 timet = time(0);
 strftime(text, sizeof(text), "%Y-%m-%d %H:%M:%S", localtime(&timet));

 return(text);
}
/**---------------------------------------------------------------------------*/
// parse a buffer into an array of pointers to lines

void Parse(char *bufferp, int bufferlen, char *lines[], int *nlinesp)
{
 int nlines;
 char *p;
 char *endp;

 endp = &bufferp[bufferlen];		// get end of buffer

 *endp = 0;				// terminate buffer

 for (p = bufferp; p < endp; p++)	// null out CRs
 {
  if (*p == '\r')
   *p = 0;
 }

 nlines = 0;
 for (p = bufferp; p < endp; p++)
 {
  lines[nlines++] = p;			// note line
  for (; p < endp && *p != '\n'; p++);	// find terminator
  *p = 0;				// terminate line
 }

 *nlinesp = nlines;

 return;
}
/**---------------------------------------------------------------------------*/
void Append(char **dstpp, char *srcp)
{
 int nbytes;
 char *dstp;

 dstp = *dstpp;

 nbytes = strlen(srcp);
 memcpy(dstp, srcp, nbytes);
 dstp += nbytes;

 *dstpp = dstp;

 return;
}
/**---------------------------------------------------------------------------*/
void AppendF(char **dstpp, char *formatp, ...)
{
 va_list valist;
 static char text[1024];

 va_start(valist, formatp);
 vsprintf(text, formatp, valist);
 va_end(valist);

 Append(dstpp, text);

 return;
}
/**---------------------------------------------------------------------------*/
int Receive(SOCKET socket, char *remotep, char *filenamep)
{
 int rv = 0;
 int bufferfree;
 int state;
 int retry;
 int packetlen;
 int line;
 int open;
 int remain;
 int chunkremain;
 int n;
 int nbytes;
 char *datap;
 char *packetp;
 char *nextp;
 char *linep;
#ifdef DEBUG
 FILE *filep = 0;
#endif

#ifndef DEBUG
 filenamep = 0;
#endif
 
#ifdef DEBUG
 filep = fopen(filenamep, "ab");
 fwrite("\r\n--------\r\n", 1, 2+8+2, filep);
#endif

 l.bufferlen = 0;
 nextp = l.buffer;
 open = 0;
 bufferfree = sizeof(l.buffer);
 state = STATE_HEADER;
/**---------------------------------------------------------------------------*/
 for (;;)					// multiple packets
 {
  if (open)
  {
   printf("\n");
   fflush(stdout);
   open = 0;
  }

  packetlen = recv(socket, nextp, bufferfree, 0);
// $ errors
  if (!packetlen || packetlen == bufferfree)
  {
   l.bufferlen = 0;
   goto EGRESS;
  }

#ifdef DEBUG
  fwrite(nextp, 1, packetlen, filep);
  fflush(filep);
#endif

  printf("%s %7s: recv packet %d", TimeStamp(), remotep, packetlen);
  open = 1;

  nextp += packetlen;
  *nextp = 0;					// delimit buffer
  l.bufferlen += packetlen;
  bufferfree -= packetlen;

  for (;;)					// state retry loop
  {
   retry = 0;

   switch (state)
   {
/**---------------------------------------------------------------------------*/
    case STATE_HEADER:
    {
     l.bodyp = strstr(l.buffer, "\r\n\r\n");	// find line break

     if (!l.bodyp)				// if none
      break;					// get next packet

     l.bodyp += 4;				// get start of body
     l.headerlen = l.bodyp - l.buffer;		// get header length
     packetlen = l.bufferlen - l.headerlen;	// discount header from packet

     printf(": header %d", l.headerlen);
  
     memcpy(l.header, l.buffer, l.headerlen);	// make local copy

     Parse(l.header, l.headerlen, l.lines, &l.nlines);

     for (line = 0; line < l.nlines; line++)	// find encoding type
     {
      linep = l.lines[line];

      if (streqz(linep, "Content-Length: "))
      {
       remain = atoi(&linep[16]);		// get content length
       printf(": expect %d", remain);
       state = STATE_CONTENTLENGTH;
       retry = 1;				// retry state
       break;					// break out of line loop
      }

      if (streqz(linep, "Transfer-Encoding: chunked"))
      {
       printf(": chunked");
       remain = -1;				// note data is expected
       chunkremain = 0;
       state = STATE_CHUNKED;
       retry = 1;				// retry state
       break;					// break out of line loop
      }

     }				// for (line = 0; line < l.nlines; line++)

     if (line >= l.nlines)			// if no encoding type
      goto EGRESS;

     break;					// break out of case
    }						// case STATE_HEADER:
/**---------------------------------------------------------------------------*/
    case STATE_CONTENTLENGTH:
    {
     remain -= packetlen;			// discount remainder
     if (remain < 0)
      remain = 0;
     printf(": remain %d", remain);
     break;					// break out of case
    }
/**---------------------------------------------------------------------------*/
// MRP = most recent packet
// nextp = start of free space
// packetp = start of MRP
// packetlen = length of MRP
// l.bufferlen = buffer length including MRP
// bufferfree = amount of free buffer space

    case STATE_CHUNKED:
    {
     for (;;)					// retry loop
     {
      packetp = nextp - packetlen;

      if (!chunkremain)
      {
       n = sscanf(packetp, "%x\r\n", &chunkremain);
       if (n != 1)
        goto EGRESS;

       printf(": expect %d", chunkremain);

       if (!chunkremain)			// if assembly complete
       {
        l.bufferlen -= packetlen;		// forget MRP
        remain = 0;				// note assembly complete
        break;					// break out of retry loop
       }

       datap = strstr(packetp, "\r\n");
       datap += 2;

       nbytes = datap - packetp;
       memcpy(packetp, datap, packetlen - nbytes + 1);	// keep null

       packetlen -= nbytes;
       nextp -= nbytes;
       l.bufferlen -= nbytes;
       bufferfree += nbytes;
      }

      if (packetlen <= chunkremain)
      {
       chunkremain -= packetlen;
       printf(": remain %d", chunkremain);
       break;					// break out of retry loop
      }

// packetlen > chunkremain, retry

      packetp += chunkremain;
      packetlen -= chunkremain;
      chunkremain = 0;
      printf(": remain %d", chunkremain);

     }						// for (;;) retry loop
     
     break;
    }						// case STATE_CHUNKED:
/**---------------------------------------------------------------------------*/
   }						// switch (state)

   if (!retry)
    break;

  }						// for (;;) retry loop

  if
  (
   state != STATE_HEADER
   &&
   !remain
  )
   break;

 }						// for (packet = 0; ; packet++)

EGRESS:

 l.bodylen = l.bufferlen - l.headerlen;

 if (open)
 {
  printf(": body %d", l.bodylen);
  printf("\n");
  fflush(stdout);
  open = 0;
 }

#ifdef DEBUG
 if (filep)
  fclose(filep);
#endif

 return(rv);
}
/**---------------------------------------------------------------------------*/
int main(void)
{
 int rv = 0;

 int rc;
 int linklen;
 int line;
 int header2len;
 int port;
 int deltalen;
 int nimages;
 int imageurllen;
#ifdef LINUX
 int n;
#endif
 
socklen_t socklen;

 char *p;
 char *q;
 char *reasonp;
 char *imgp;
 char *linep;
 char *linkp;
 char *linkendp;
 char *bodyendp;
 FILE *filep;

 struct hostent *hostentp;

 static struct sockaddr_in sockaddrbrowser;
 static struct sockaddr_in sockaddrlocal;
 static struct sockaddr_in sockaddrserver;

 static SOCKET socketlisten;
 static SOCKET socketbrowser;
 static SOCKET socketserver;

#ifdef WINDOWS
 static WSADATA wsadata;
#endif

 static char link[256];
 static char hostname[256];
 static char servername[256];
 static char imageurl[256];
 static char header2[65536];
/**---------------------------------------------------------------------------*/
 printf("LGirl v1.2 2007-07-13\n");
 printf("\n");
/**---------------------------------------------------------------------------*/
// initialize networking subsystem

#ifdef WINDOWS

 rc = WSAStartup(MAKEWORD(1,1), &wsadata);		// version 1.1
 if (rc)
 {
  rc = WSAGetLastError();
  printf("Unable to start socket service: rc = %d.\n", rc);
  rv = 1;
  goto EGRESS;
 }

#endif
/**---------------------------------------------------------------------------*/
// parse configuration file

// get default values

 strcpy(servername, "www.reason.com");
 gethostname(hostname, sizeof(hostname));
 port = 4004;
 strcpy(imageurl, "http://static.flickr.com/103/300668985_fb38ae14d6.jpg");

// read file

 filep = fopen("lgirl.conf", "r");
 if (!filep)
  goto NOCONFIG;
 l.bufferlen = fread(l.buffer, 1, sizeof(l.buffer), filep);
 fclose(filep);

 if (!l.bufferlen || l.bufferlen == sizeof(l.buffer))
  goto NOCONFIG;

 l.buffer[l.bufferlen] = 0;	// terminate buffer

// reduce whitespace

 for (p = l.buffer, q = p+1; *p; p++, q++)
 {
  if (*p == '\t')
   *p = ' ';

  if (*p != ' ')
   continue;

  if (*q == ' ' || *q == '\r' || *q == '\n' || *q == 0)
  {
   strcpy(p, q);
   p--;
   q--;
  }
 }

// parse file

 Parse(l.buffer, l.bufferlen, l.lines, &l.nlines);

 for (line = 0; line < l.nlines; line++)
 {
  linep = l.lines[line];

  if (linep[0] == '#')			// skip comments
   continue;

  if (streqz(linep, "server"))
   strcpy(servername, &linep[6+1]);
  else
  if (streqz(linep, "host"))
   strcpy(hostname, &linep[4+1]);
  else
  if (streqz(linep, "port"))
   port = atoi(&linep[4+1]);
  else
  if (streqz(linep, "image"))
   strcpy(imageurl, &linep[5+1]);
 }

NOCONFIG:
/**---------------------------------------------------------------------------*/
// report configuration

 printf("servername: \"%s\"\n", servername);
 printf("  hostname: \"%s\"\n", hostname);
 printf("      port: %d\n", port);
 printf(" image URL: \"%s\"\n", imageurl);
 printf("\n");
/**---------------------------------------------------------------------------*/
// get web server address

 hostentp = gethostbyname(servername);
 if (!hostentp)
 {
  rc = WSAGetLastError();
  printf("Unable to resolve server \"%s\": rc = %d.\n", servername, rc);
  rv = 1;
  goto EGRESS;
 }

 sockaddrserver.sin_family = AF_INET;
 sockaddrserver.sin_port = htons(80);
 memcpy(&sockaddrserver.sin_addr, hostentp->h_addr, hostentp->h_length);

 printf("Server \"%s\" resolves to \"%s\"\n", servername, inet_ntoa(*(struct in_addr *)hostentp->h_addr));
 printf("\n");
/**---------------------------------------------------------------------------*/
// allocate listening socket

 socketlisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
#ifdef WINDOWS
 if (socketlisten == INVALID_SOCKET)
#else
 if (socketlisten < 0)
#endif
 {
  rc = WSAGetLastError();
  printf("Unable to create socket: rc = %d.\n", rc);
  rv = 1;
  goto EGRESS;
 }

// allow quick reuse of the socket

#ifdef LINUX

 n = 1;
 setsockopt(socketlisten, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));

#endif

// configure listening socket

 hostentp = gethostbyname(hostname);
 if (!hostentp)
 {
  rc = WSAGetLastError();
  printf("Unable to resolve hostname \"%s\": rc = %d.\n", hostname, rc);
  rv = 1;
  goto EGRESS;
 }

 sockaddrlocal.sin_family = AF_INET;
 sockaddrlocal.sin_port = htons((unsigned short)port);
 memcpy(&sockaddrlocal.sin_addr, hostentp->h_addr, hostentp->h_length);

 printf("Hostname \"%s\" resolves to \"%s\"\n", hostname, inet_ntoa(*(struct in_addr *)hostentp->h_addr));
 printf("\n");

// bind local address to listening socket

 rc = bind(socketlisten, (struct sockaddr *)&sockaddrlocal, sizeof(sockaddrlocal));	
 if (rc)
 {
  rc = WSAGetLastError();
  printf("Unable to bind address to socket: rc = %d.\n", rc);
  rv = 1;
  goto EGRESS;
 }

// listen for connections

 rc = listen(socketlisten, 1);
 if (rc)
 {
  rc = WSAGetLastError();
  printf("Unable to listen to socket: rc = %d.\n", rc);
  rv = 1;
  goto EGRESS;
 }

 for (;;)				// infinite loop of connections
 {
/**---------------------------------------------------------------------------*/
// accept connection from browser

  socklen = sizeof(sockaddrbrowser);
  socketbrowser = accept(socketlisten, (struct sockaddr *)&sockaddrbrowser, &socklen);
#ifdef WINDOWS
  if (socketbrowser == SOCKET_ERROR)
#else
  if (socketbrowser < 0)
#endif
  {
   rc = WSAGetLastError();
   printf("Unable to accept connection: rc = %d.\n", rc);
   rv = 1;
   goto EGRESS;
  }

// receive request from browser

  rv = Receive(socketbrowser, "browser", "request.txt");
  if (rv)
  {
   printf("Error receiving data from browser.\n");
   goto EGRESS;
  }

  if (!l.nlines)				// header parsed by Receive()
  {
   header2len = l.headerlen;
   memcpy(header2, l.buffer, header2len);	// copy original header
   goto SKIP;
  }
/**---------------------------------------------------------------------------*/
// build modified header to send to server

  p = header2;

  for (line = 0; line < l.nlines; line++)
  {
   linep = l.lines[line];

   if (streqz(linep, "Keep-Alive: "))	// nuke keep alive headers
    continue;
   if (streqz(linep, "Connection: "))
    continue;

   if (streqz(linep, "Host: "))
    AppendF(&p, "Host: %s", servername);	// replace Host:
   else
   if (streqz(linep, "Referer: "))		// restore Referer:
   {
    q = strstr(linep, "//");
    if (q)
    {
     q += 2;
     q = strchr(q, '/');
    }
    if (!q)
     Append(&p, linep);
    else
    {
     AppendF(&p, "Referer: http://%s", servername);
     Append(&p, q);				// append rest of original
    }
   }
   else
    Append(&p, linep);

   Append(&p, "\r\n");
  }

  header2len = p - header2;

SKIP:
/**---------------------------------------------------------------------------*/
// create network socket

  socketserver = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
#ifdef WINDOWS
  if (socketserver == INVALID_SOCKET)
#else
  if (socketserver < 0)
#endif
  {
   rc = WSAGetLastError();
   printf("Unable to create socket: rc = %d.\n", rc);
   rv = 1;
   goto EGRESS;
  }

// connect to server

  rc = connect(socketserver, (struct sockaddr *)&sockaddrserver, sizeof(sockaddrserver));
#ifdef WINDOWS
  if (rc == SOCKET_ERROR)
#else
  if (rc)
#endif
  {
   rc = WSAGetLastError();
   printf("Unable to connect to web server: rc = %d.\n", rc);
   rv = 1;
   goto EGRESS;
  }
/**---------------------------------------------------------------------------*/
// send request to server in two parts

  rv = BigSend(socketserver, header2, header2len, &socklen);
  if (rv)
  {
   printf("Error while sending header to server.\n");
   rv = 1;
   goto EGRESS;
  }
  if (socklen != header2len)
  {
   printf("Error while sending header to server.\n");
   printf("Tried to send %d bytes, sent %d bytes.\n", header2len, socklen);
   rv = 1;
   goto EGRESS;
  }

  printf("%s  server: xmit header %d\n", TimeStamp(), header2len);
  fflush(stdout);

  if (l.bodylen)
  {
   rv = BigSend(socketserver, l.bodyp, l.bodylen, &socklen);
   if (rv)
   {
    printf("Error while sending body to server.\n");
    rv = 1;
    goto EGRESS;
   }
   if (socklen != l.bodylen)
   {
    printf("Error while sending body to server.\n");
    printf("Tried to send %d bytes, sent %d bytes.\n", l.bodylen, socklen);
    rv = 1;
    goto EGRESS;
   }

   printf("%s  server: xmit body %d\n", TimeStamp(), l.bodylen);
   fflush(stdout);
  }

#ifdef DEBUG
  filep = fopen("request.txt", "ab");
  fwrite("\r\n--------\r\n", 1, 2+8+2, filep);
  fwrite(header2, 1, header2len, filep);
  fwrite(l.bodyp, 1, l.bodylen, filep);
  fclose(filep);
#endif
/**---------------------------------------------------------------------------*/
// get reply from server

  rv = Receive(socketserver, "server", "reply.txt");
  if (rv)
  {
   printf("Error while receiving from server.\n");
   goto EGRESS;
  }

  closesocket(socketserver);
/**---------------------------------------------------------------------------*/
// process server reply

  bodyendp = &l.bodyp[l.bodylen];
  *bodyendp = 0;			// treat body like one big string
/**---------------------------------------------------------------------------*/
// replace image references

  imageurllen = strlen(imageurl);

  nimages = 0;
  imgp = l.bodyp;

  for (;;)
  {
   imgp = stristr(imgp, "<img src=\"");
   if (!imgp)
    break;

   imgp++;

   linkp = strchr(imgp, '"');
   if (!linkp)
    break;

   linkp++;

   linkendp = strchr(linkp, '"');
   if (!linkendp)
    break;

   linklen = linkendp - linkp;

   if (linklen > imageurllen)
   {
    deltalen = linklen - imageurllen;

    memcpy					// shorten body, keep null
    (
     linkp + imageurllen,
     linkp + linklen,
     bodyendp - (linkp + linklen) + 1
    );
    l.bodylen -= deltalen;			// adjust body length
    bodyendp -= deltalen; 			// adjust body endp
   }
   else
   if (imageurllen > linklen)
   {
    deltalen = imageurllen - linklen;

    memmove					// lengthen body, keep null
    (
     linkp + imageurllen,
     linkp + linklen,
     bodyendp - (linkp + linklen) + 1
    );
    l.bodylen += deltalen;			// adjust body length
    bodyendp += deltalen;			// adjust body endp
   }

   memcpy(linkp, imageurl, imageurllen);	// replace URL

   nimages++;
  }

  if (nimages)
   printf("%s %d images replaced\n", TimeStamp(), nimages);
/**---------------------------------------------------------------------------*/
// build modified header to send to browser

  sprintf(link, "%s:%d", hostname, port);

  p = header2;

  for (line = 0; line < l.nlines; line++)
  {
   linep = l.lines[line];

   if
   (
    streqz(linep, "Content-Length: ")
    ||
    streqz(linep, "Transfer-Encoding: chunked")
   )
   {
    AppendF(&p, "Content-Length: %d\r\n", l.bodylen);
    continue;
   }

//                         "12345678901234"
   reasonp = strstr(linep, "www.reason.com");
   if (reasonp)
   {
    *reasonp = 0;
    AppendF(&p, "%s%s%s\r\n", linep, link, &reasonp[14]);
    continue;
   }

   AppendF(&p, "%s\r\n", linep);
  }

  header2len = p - header2;
/**---------------------------------------------------------------------------*/
// send reply to browser in two parts

  rv = BigSend(socketbrowser, header2, header2len, &socklen);
  if (rv)
  {
   printf("Error while sending header to browser.\n");
   rv = 1;
   goto EGRESS;
  }
  if (socklen != header2len)
  {
   printf("Error while sending header to browser.\n");
   printf("Tried to send %d bytes, sent %d bytes.\n", header2len, socklen);
   rv = 1;
   goto EGRESS;
  }

  printf("%s browser: xmit header %d\n", TimeStamp(), header2len);
  fflush(stdout);

  if (l.bodylen)
  {
   rv = BigSend(socketbrowser, l.bodyp, l.bodylen, &socklen);
   if (rv)
   {
    printf("Error while sending body to browser.\n");
    rv = 1;
    goto EGRESS;
   }
   if (socklen != l.bodylen)
   {
    printf("Error while sending body to browser.\n");
    printf("Tried to send %d bytes, sent %d bytes.\n", l.bodylen, socklen);
    rv = 1;
    goto EGRESS;
   }

   printf("%s browser: xmit body %d\n", TimeStamp(), l.bodylen);
   fflush(stdout);
  }

#ifdef DEBUG
  filep = fopen("reply.txt", "ab");
  fwrite("\r\n--------\r\n", 1, 2+8+2, filep);
  fwrite(header2, 1, header2len, filep);
  fwrite(l.bodyp, 1, l.bodylen, filep);
  fclose(filep);
#endif
/**---------------------------------------------------------------------------*/ 
  closesocket(socketbrowser);

 }				// for (;;) listen loop

EGRESS:

 closesocket(socketbrowser);
 closesocket(socketlisten);
 closesocket(socketserver);

// deinitialize networking

#ifdef WINDOWS

 if (wsadata.wVersion)
  WSACleanup();

#endif

 return(rv);
}
