/*(( "Header" */
/*
 * $Id: httpfetch.c,v 1.3 1996/06/03 04:13:31 mshopf Exp mshopf $
 *
 * (c) 1995-96 Matthias Hopf
 *
 * The usual fetchurl tool, with some extensions for httpproxy.
 */

/*
 * $Log: httpfetch.c,v $
 * Revision 1.3  1996/06/03  04:13:31  mshopf
 * support for net errors.
 *
 * Revision 1.2  1996/05/15  01:07:58  mshopf
 * small bug fix.
 *
 * Revision 1.1  1996/04/16  04:39:29  mshopf
 */


/*)) */
/*(( "Includes & Konstanten" */


#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

#include <proto/exec.h>
#include <proto/dos.h>

#include "net.h"

#define  BUFSIZE        1024    /* don't make this one too large (it's on the stack) */

#define  MAX_HOSTNAMELEN 128    /* including port number specifiers */
#define  MAX_URLLEN      1024   /* maximum size of URL request */
#define  MAX_OVERHEAD    128    /* maximum size of protocol overhead */

#define  DEFAULT_HTTPPORT  80
#define  DEFAULT_PROXYPORT 8080
#define  DEFAULT_RETRIES   1
#define  DEFAULT_HEADER    FALSE
#define  DEFAULT_SERVICE   FALSE

/*)) */
/*(( "Global Variables" */

long   __oslibversion      = 37;
const  char *Version       = "httpfetch/1.0";

static netmethods_t *Net   = NULL;
static int  InSocket       = -1;
static FILE *OutStream     = stdout;
static struct RDArgs *RDArgs = NULL;

static char *Template      = "URL/A,FROM=PROXY/K,TO=FILE,RETRIES=RETRY/N/K,HEADER=FULL/S,SERVICE/S,DISCARD=QUIET/S";
#define arg_url     ((char *) (RetArray[0]))
#define arg_from    ((char *) (RetArray[1]))
#define arg_to      ((char *) (RetArray[2]))
#define arg_retries (RetArray[3] ? *((long *) (RetArray[3])) : DEFAULT_RETRIES)
#define arg_header  ((long)   (RetArray[4]))
#define arg_service ((long)   (RetArray[5]))
#define arg_discard ((long)   (RetArray[6]))

/*)) */

/*(( "ExitAll () / ExitErr ()" */

/* Close everything and return */

void ExitAll (int Ret)
{
    if (Net && InSocket >= 0)
	Net->close (InSocket);
    InSocket = -1;
    if (Net)
	Net->exit ();
    Net       = NULL;
    if (OutStream && OutStream != stdout)
	fclose (OutStream);
    OutStream = NULL;
    if (RDArgs)
	FreeArgs (RDArgs);
    RDArgs    = NULL;
    exit (Ret);
}

/* Print errormessage and exit with 10 */

void ExitErr (const char *Msg)
{
    if (errno)
    {
	if (Net)
	    fprintf (stderr, "%s: %s\n", Msg, Net->strerror (errno));
	else
	    perror (Msg);
    }
    else
	fprintf (stderr, "%s\n", Msg);
    ExitAll (10);
}

/*)) */
/*(( "SendRequest ()" */

/* send request to Socket fd */

int SendRequest (int Fd, char *Req)
{
    int Len;
    Len = strlen (Req);
    return (Net->write (Fd, Req, Len) == Len);
}

/*)) */
/*(( "SkipHeader ()" */

/* skip header data */

int SkipHeader (int Fd)
{
    char Buffer [BUFSIZE+1];
    char *c, *Pos;
    int  i;

    Pos = Buffer;
    for (;;)
    {
	    /* read partial header */
	if ( (i = Net->read (Fd, Pos, (int) (BUFSIZE - (Buffer - Pos)))) <= 0)
	    return FALSE;
	Pos [i] = '\0';
	    /* check for header end and flush the rest into the output stream */
	if ( (c = strstr (Buffer, "\r\n\r\n")) || (c = strstr (Buffer, "\n\r\n\r")) )
	{
	    if (OutStream)
		return ((int) fwrite (c + 4, (size_t) (Pos + i - c - 4), 1, OutStream));    /* fwrite returns number of blocks=1 on success... */
	    return TRUE;
	}
	if ( (c = strstr (Buffer, "\r\r")) || (c = strstr (Buffer, "\n\n")) )
	{
	    if (OutStream)
		return ((int) fwrite (c + 2, (size_t) (Pos + i - c - 2), 1, OutStream));    /* fwrite returns number of blocks=1 on success... */
	    return TRUE;
	}
	    /* header not yet done - check end and skip back until we're save for flushing */
	if (Pos [i-1] != '\r' && Pos [i-1] != '\n')
	    Pos = Buffer;
	else
	{
	    c = &Pos [i-2];
	    while (c >= Buffer)
		if (*c != '\r' && *c != '\n')
		    break;
		else
		    c--;
		/* move remaining '\n' and '\r's to beginning of buffer */
	    memmove (Buffer, c + 1, (size_t) (Pos + i - c - 1));
	    Pos = Buffer + (Pos + i - c - 1);
	}
    }
    /* NOTREACHED */
}

/*)) */
/*(( "ReceiveData ()" */

/* save received data */

int ReceiveData (int Fd)
{
    char Buffer [BUFSIZE];
    int i;

    for (;;)
    {
	if ( (i = Net->read (Fd, Buffer, BUFSIZE)) <= 0)
	    return (i < 0 ? FALSE : TRUE);
	if (OutStream)
	    if (fwrite (Buffer, i, 1, OutStream) != 1)
		return FALSE;
    }
    /* NOTREACHED */
}

/*)) */
/*(( "MakeRequest ()" */

void MakeRequest (char *Req, char *Url)
{
    int Len;

    Len  = strlen (Req);
    Req += Len;
    Len += strlen (Url);
    if (Len > MAX_URLLEN)
	ExitErr ("URL too long error");
    strcpy (Req, Url);
    Req += strlen (Req);
    sprintf (Req, " HTTP/1.0\r\nUser-Agent: %s\r\nAccept: */*\r\n\r\n", Version);
}

/*)) */
/*(( "MakeService ()" */

/* Make a httpproxy service request */

void MakeService (char *Req, char *Service)
{
    int Len;

    Len = strlen (Req);
    Req += Len;          /* skip protocol part and GET command */
    Len += strlen (Service);
    if (Len > MAX_URLLEN)
	ExitErr ("Service too long error");
    strcpy (Req, "http://proxy.../");
    Req += 16;
    strcpy (Req, Service);
    Req += strlen (Req);
    sprintf (Req, " HTTP/1.0\r\nUser-Agent: %s\r\nAccept: */*\r\n\r\n", Version);
}
/*)) */
/*(( "Action ()" */

/* Interprete Arguments */
/* RetArray has to be exactly written like this in order to have the arg_* macros working */

void Action (long *RetArray)
{
    static char Request [MAX_URLLEN + MAX_OVERHEAD];
    char Host [MAX_HOSTNAMELEN];
    int  Port = DEFAULT_HTTPPORT;
    char *c;
    int  i;

    Host [0] = '\0';

    if (arg_to && arg_discard)
	ExitErr ("don't specify both TO=FILE and DISCARD=QUIET");

	/* - destination file */
    if (arg_to)
	if (! (OutStream = fopen (arg_to, "wb+")))
	    ExitErr ("can't open file");
    if (arg_discard)
	OutStream = NULL;

	/* - proxy host, url and service */
    if (arg_from)
    {                                                      /* get in proxy mode */
	Port = DEFAULT_PROXYPORT;
	strncpy (Host, arg_from, MAX_HOSTNAMELEN-1);
	Host [MAX_HOSTNAMELEN-1] = '\0';
	if ( (c = strchr (Host, ':')) )
	{
	    *c = '\0';
	    Port = atoi (c+1);
	}
	strcpy (Request, "GET ");
	if (arg_service)
	    MakeService (Request, arg_url);
	else
	    MakeRequest (Request, arg_url);
    }
    else
    {
	if (! arg_service)
	{
		/* check protocol name for 'http://' */
	    if (strncmp (arg_url, "http://", 7) != 0)
		ExitErr ("invalid URL");
	    strcpy  (Request, "GET ");
		/* now get host name and port */
	    strncpy (Host, arg_url + 7, MAX_HOSTNAMELEN-1);      /* ok, that's too much, but who cares */
	    Host [MAX_HOSTNAMELEN-1] = '\0';
	    if ( (c = strchr (Host, '/')) )             /* get hostname and port */
		*c = '\0';
	    if ( (c = strchr (Host, ':')) )
	    {
		*c = '\0';
		Port = atoi (c+1);
	    }
	    if (Host [0] == '\0')                        /* no host??? */
		ExitErr ("invalid URL");
		/* get local object name */
	    if ( (c = strchr (arg_url + 7, '/')) )
		MakeRequest (Request, c);
	    else
		MakeRequest (Request, "/");
	}
	else
	{
	    strcpy  (Host, "localhost");            /* on service we'll address our local proxy */
	    Port = DEFAULT_PROXYPORT;
	    strcpy (Request, "GET ");
	    MakeService (& Request [4], arg_url);
	}
    }

    for (i = 0; i < arg_retries; i++)
    {
	if (i)
	    Delay (50);          /* one second delay after first try */
	if ( (InSocket = Net->open (Host, Port)) < 0)
	    continue;
	if (! SendRequest (InSocket, Request))
	{
	    Net->close (InSocket);
	    InSocket = -2;
	    continue;
	}
	break;
    }
    switch (InSocket) {
    case -1:
	ExitErr ("can't contact host");
    case -2:
	ExitErr ("failed sending request");
    }

    if (OutStream && ! arg_header)
	if (! SkipHeader (InSocket))
	    ExitErr ("failed while receiving header");
    if (! ReceiveData (InSocket))
	ExitErr ("failed while receiving data");
    Net->close (InSocket);
    InSocket = -1;
}

/*)) */
/*(( "main ()" */

/* main function */

void main (int argc, char *argv[])
{
    netmethods_t *NetList[] = { &NetAmiTCP, &NetAS225, NULL }; /* all available net protocolls */
    netmethods_t **TestNet;
    long RetArray[] = { 0, 0, 0, 0, 0, 0, 0 };

	/* get net handler */
    for (TestNet = NetList; (Net = *TestNet); TestNet++)
	if (Net->init (TRUE))                 /* check for opening in blocking mode */
	    break;

    if (! Net)
    {
	fprintf (stderr, "Couldn't open network protocol handler - perhaps the network stack is not running.\n"
			 "Available network protocol handlers:\n");
	for (TestNet = NetList; (Net = *TestNet); TestNet++)
	    fprintf (stderr, "%s\n", Net->Descr);
	ExitAll (20);
    }

	/* Get arguments */
    if (! (RDArgs = ReadArgs (Template, RetArray, NULL)))
    {
	fprintf (stderr, "%s\n"
		 "\tURL:     The URL to be retrieved.\n"
		 "\tFROM:    Proxy host (and :port) to be used for fetching.\n"
		 "\tTO:      Filename for the resulting document.\n"
		 "\tRETRIES: The number of retries after one second delay each.\n"
		 "\tHEADER:  The response header is saved, too.\n"
		 "\tSERVICE: Url specifies the proxy service to be done.\n"
		 "\tDISCARD: Discard received data.\n",
		 Template);
	ExitAll (10);
    }

    Action (RetArray);

    ExitAll (0);
}

/*)) */

