#include "common/extern.h"
#include "common/asyncio.h"
#include "common/parseurl.h"
#include "common/subtasksupp.h"
#include "common/Grab_mcc.h"

#include "http_client.h"

#ifdef __cplusplus
#include <storm/libbase.h>
#endif


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

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/time.h>

#include <netinet/in.h>
#include <netdb.h>

#include <amitcp/socketbasetags.h>

#include <pragma/socket_lib.h>
#include <clib/locale_protos.h>
#include <clib/exec_protos.h>

/// "Global variables"

struct Library *SocketBase = NULL;
int sockerr;

struct Locale *english = NULL;
struct Locale *currloc = NULL;

struct SubTask *st;

struct FileInfoBlock fib;
struct SignalSemaphore *mem_sema;
APTR pool;

extern char *vstring;

///

//#define inline
//#define __inline

/*******************************************************/
/* Support functions....                               */
/*******************************************************/

/// "__inline ULONG DoPortMethod (struct MsgPort *port, struct MsgPort *rport, APTR obj, ...)"

ULONG
DoPortMethod (struct MsgPort *port, struct MsgPort *rport, APTR obj,...)
{
  struct MUIMessage muimsg =
  {obj, 0, (ULONG *) (&obj) + 1};

  struct SubTaskMsg stm_Message =
  {
    {
      {NULL, NULL, 0, 0, NULL},
      rport, sizeof (struct SubTaskMsg)
    },

    STC_MUIMSG,
    &muimsg,
    0
  };

  PutMsg (port, (struct Message *) &stm_Message);
  WaitPort (rport);
  GetMsg (rport);

  return (stm_Message.stm_Result);
}

ULONG
DoPortAMethod (struct MsgPort * port, struct MsgPort * rport, APTR obj, LONG parnum,...)
{
  struct MUIMessage muimsg =
  {obj, parnum, (ULONG *) (&parnum) + 1};

  struct SubTaskMsg stm_Message =
  {
    {
      {NULL, NULL, 0, 0, NULL},
      rport, sizeof (struct SubTaskMsg)
    },

    STC_MUIAMSG,
    &muimsg,
    0
  };

  PutMsg (port, (struct Message *) &stm_Message);
  WaitPort (rport);
  GetMsg (rport);

  return (stm_Message.stm_Result);
}

///

///"Locale Hooks"
void
putCharFunc (register __a0 struct Hook *hk,
             register __a1 char chr,
             register __a2 struct Locale *loc)
{
  *(((char *) hk->h_Data)++) = chr;
}

ULONG
getCharFunc (register __a0 struct Hook *hk,
             register __a2 ULONG nil,
             register __a1 struct Locale *loc)
{
  char chr = *(((char *) hk->h_Data)++);

  if ((chr == '\x0d') || (chr == '\x0a'))
    chr = 0;

  return ((ULONG) chr);
}

struct Hook putCharFuncHook =
{
  {NULL, NULL}, (HOOKFUNC) putCharFunc, NULL, NULL};

struct Hook getCharFuncHook =
{
  {NULL, NULL}, (HOOKFUNC) getCharFunc, NULL, NULL};
///

/// "ParseNetdate"
struct DateStamp *
ParseNetDate (char *date)
{
  struct DateStamp *ds = NULL, dtm =
  {0};

  getCharFuncHook.h_Data = date;

  if (date[3] == ',')
    {                           // Sun, 06 Nov 1994 08:49:37 GMT; RFC 822, updated by RFC 1123

      if (ParseDate (english, &dtm, "%a, %d %b %Y %H:%M:%S" /* GMT" */ , &getCharFuncHook))
        {
          ds = (struct DateStamp *) AllocSPooled (pool, sizeof (struct DateStamp));
          memcpy (ds, &dtm, sizeof (struct DateStamp));

        }
      else
        D (kprintf ("ParseDate error!!!\n"));

    }
  else if (date[3] == ' ')
    {                           // Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format

      if (ParseDate (english, &dtm, "%a %b %e %H:%M:%S %Y", &getCharFuncHook))
        {
          ds = (struct DateStamp *) AllocSPooled (pool, sizeof (struct DateStamp));
          memcpy (ds, &dtm, sizeof (struct DateStamp));

        }
      else
        D (kprintf ("ParseDate error!!!\n"));

    }
  else
    {                           // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036

      if (ParseDate (english, &dtm, "%A, %d-%b-%y %H:%M:%S GMT", &getCharFuncHook))
        {
          ds = (struct DateStamp *) AllocSPooled (pool, sizeof (struct DateStamp));
          memcpy (ds, &dtm, sizeof (struct DateStamp));

        }
      else
        D (kprintf ("ParseDate error!!!\n"));

    }

  return (ds);
}
///

///"Encode64"
/* Encode a single line of binary data to a standard format that uses
   only printing ASCII characters. Line should not be longer than 48
   bytes.  The code was adapted from WWW Library.

   Returns the malloc-ed encoded line. */
char *
base64_encode_line (char *line)
{
  /* Character conversion table. */
  static char cnv[64] =
  {
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
    'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
  };
  int len, i;
  char *p, *res;

  len = strlen (line);
  res = (char *) AllocVecPooled (pool, 4 * ((len + 2) / 3) + 1);
  p = res;
  for (i = 0; i < len; i += 3)
    {
      *p++ = cnv[*line >> 2];
      *p++ = cnv[((*line << 4) & 060) | ((line[1] >> 4) & 017)];
      *p++ = cnv[((line[1] << 2) & 074) | ((line[2] >> 6) & 03)];
      *p++ = cnv[line[2] & 077];
      line += 3;
    }

  /* If len was not a multiple of 3, then we have encoded too many
     characters.  Adjust appropriately.  */
  if (i == len + 1)
    {
      /* There were only 2 bytes in that last group */
      *(p - 1) = '=';
    }
  else if (i == len + 2)
    {
      /* There was only 1 byte in that last group */
      *(p - 1) = '=';
      *(p - 2) = '=';
    }
  *p = '\0';

  return (res);
}

///

/*******************************************************/
/* Network functions....                               */
/*******************************************************/

/// "messages"
__inline void
message (struct HttpData *data, int i = 0, char *msg = NULL)
{
  ObtainSemaphore (data->sema);

  if (data->app)
    {
      ReleaseSemaphore (data->sema);
      DoPortMethod (st->st_Reply, st->st_Port, data->self, MUIM_Grab_Update, i, msg);
    }
  else
    ReleaseSemaphore (data->sema);
}

__inline void
errormessage (struct HttpData *data, char *msg = NULL, int delay = 50)
{
  ObtainSemaphore (data->sema);

  if (data->app)
    {
      ReleaseSemaphore (data->sema);
      DoPortMethod (st->st_Reply, st->st_Port, data->self, MUIM_Grab_Update, 0, msg);
    }
  else
    ReleaseSemaphore (data->sema);

  Delay (delay);
}
///

///"Netperror"
static void
netperror (char *banner)
{
  ULONG taglist[5];

  taglist[0] = SBTM_GETVAL (SBTC_ERRNO);
  /* taglist[1] set when tag 0 executed */
  taglist[2] = SBTM_GETREF (SBTC_ERRNOSTRPTR);
  taglist[3] = (LONG) & taglist[1];     /* reads and writes tag[1] */
  taglist[4] = NULL;            /* TAG_END if <utility/tagitem> is included */

  SocketBaseTagList ((struct TagItem *) taglist);

  if (banner)
    D (kprintf ("%s:", banner));

  D (kprintf ("%s\n", (char *) taglist[1]));
}
///

/// "socket funcs"
void
closesd (int sd)
{
  shutdown (sd, 2);
  CloseSocket (sd);
}

/*

   Devo aggiungere il supporto per il LWSP

 */

LONG
recv_string (int sd, char *buf, int bufsize)
{
  char *tmp, *buf2 = buf;
  int i;
  bufsize--;
  *buf = '\0';

  while (bufsize > 0)
    {
      if ((i = recv (sd, buf, bufsize, MSG_PEEK)) > 0)
        {
          *(buf + i) = '\0';

          // search for CRLF
          if ((tmp = strstr (buf2, CRLF)))
            {
              recv (sd, buf, tmp - buf + 2, 0);
              *tmp = '\0';

              return (1);
            }
          else
            {
              recv (sd, buf, bufsize, 0);
              bufsize -= i;
              buf += i;
            }
        }
      else
        break;
    }

  if (bufsize <= 0)
    i = -2;

  if (i == -1)
    {
      if (sockerr == EINTR)
        {
          SetSignal (0L, SIGBREAKF_CTRL_C);
        }

      netperror ("recv");
    }

  return (i);
}

///

/// "Connect"
int ONE = 1;

long
do_connect (char *host, int port)
{
  struct sockaddr_in addr;
  struct hostent *remote;
  int sd;

  memset (&addr, 0, sizeof (struct sockaddr_in));

  if ((addr.sin_addr.s_addr = inet_addr (host)) == INADDR_NONE)
    {
      if ((remote = gethostbyname (host)) != NULL)
        memcpy (&addr.sin_addr, remote->h_addr, sizeof (addr.sin_addr));
      else
        {
          D (kprintf ("HTTP: Cannot resolve host address\n"));
          return (-1);
        }
    }

  addr.sin_port = htons (port);
  addr.sin_family = AF_INET;

  if ((sd = socket (AF_INET, SOCK_STREAM, 0)) != -1)
    {
      setsockopt (sd, SOL_SOCKET, SO_KEEPALIVE, &ONE, sizeof (ONE));

      if (connect (sd, (struct sockaddr *) &addr, sizeof (struct sockaddr_in)) == -1)
        {
          netperror ("HTTP: connect");
          return (-1);
        }

    }

  return (sd);
}

long
Connect (struct HttpData *data, struct http_url *url)
{
  long sd;
  char *tmpproxy;
  int tmpport;

  ObtainSemaphore (&data->prefs->sema);
  tmpproxy = newstrcpy (pool, data->prefs->httpprefs.proxy);
  tmpport = data->prefs->httpprefs.proxyport;
  ReleaseSemaphore (&data->prefs->sema);

  if (tmpproxy)
    {
      D (kprintf ("HTTP: Connecting to proxy server\n"));
      sd = do_connect (tmpproxy, tmpport);
      FreeVecPooled (pool, tmpproxy);
    }
  else
    {
      sd = do_connect (url->host, url->port);
    }

  return (sd);
}
///

/// "send http req"
long
sendhttpreq (int sd, struct http_url *url, struct HttpData *data)
{
  char *sendmsg, *method, *end, *connection;
  int i = 0;

  if ((sendmsg = end = (char *) AllocVecPooled (pool, 1024 * 5)))
    {
      sendmsg[0] = '\0';

      struct DateStamp *ds = &url->ds;


      ObtainSemaphore (&data->prefs->sema);
      if (data->prefs->httpprefs.proxy)
        {
          const char proxymsg[] =
          "GET %s HTTP/1.0" CRLF
          "Host: %s:%d" CRLF
          "Accept: */*" CRLF
          "Accept-Encoding: gzip, compress" CRLF
          "User-Agent: GiambyNetGrabber/" __VER__ CRLF "\0";

          end += sprintf (sendmsg, proxymsg, method, url->path, url->host, url->port);
          url->method = GET;
        }
      else
        {
          const char parsemsg[] =
          "%s %s HTTP/1.1" CRLF
          "Host: %s:%d" CRLF
          "Accept: */*" CRLF
          "Accept-Encoding: gzip, compress" CRLF
          "Connection: %s" CRLF
          "User-Agent: GiambyNetGrabber/" __VER__ " by Giambattista Bloisi <giamby@geocities.com>" CRLF "\0";


          if (url->method == GET)
            {
              method = "GET";
              connection = "close";
            }
          else if (url->method == HEAD)
            {
              method = "HEAD";
              connection = "Keep-Alive";
            }
          else
            method = NULL;

          end += sprintf (sendmsg, parsemsg, method, url->path, url->host, url->port, connection);
        }
      ReleaseSemaphore (&data->prefs->sema);

      ObtainSemaphore (&data->prefs->sema);

      if (data->prefs->email)
        end += sprintf (end, "From: %s" CRLF, data->prefs->email);

      if (data->prefs->httpprefs.languages)
        end += sprintf (end, "Accept-Language: %s" CRLF, data->prefs->httpprefs.languages);


      if (data->prefs->httpprefs.proxy)
        if (data->prefs->httpprefs.proxyauth)
          {
            //Perhaps I'd encode auth.
            end += sprintf (end, "Proxy-Authorization: %s" CRLF, data->prefs->httpprefs.proxyauth);
          }
      ReleaseSemaphore (&data->prefs->sema);



      if (url->auth && url->passwd)
        {
          char *t1, *t2;

          t1 = (char *) AllocVecPooled (pool, strlen (url->auth) + 1 + 2 * strlen (url->passwd));
          sprintf (t1, "%s:%s", url->auth, url->passwd);
          t2 = base64_encode_line (t1);
          FreeVecPooled (pool, t1);

          end += sprintf (end, "Authorization: Basic %s" CRLF, t2);

          FreeVecPooled (pool, t2);
        }

      if (url->referer)
        end += sprintf (end, "Referer: %s" CRLF, url->referer);


      /*if (url->nocache)
         {
         strcat (sendmsg, "Pagma: no-cache" CRLF);
         }

         if (url->range)
         {
         strcat (sendmsg, "Range: bytes=");
         strcat bytes
         strcat (sendmsg, "-" CRLF);
         } */

      /*if ((url->ds.ds_Days + url->ds.ds_Minute + url->ds.ds_Tick) > 0)
         {
         putCharFuncHook.h_Data = sendmsg + strlen (sendmsg);

         FormatDate (english, "If-Modified-Since: %a, %d %b %Y %T", &url->ds, &putCharFuncHook);
         strcat (sendmsg, " GMT" CRLF);
         } */

      // HTTP/1.1 only
      if (url->fsize > 0)
        {

          if ((url->fib->fib_Date.ds_Days + url->fib->fib_Date.ds_Minute + url->fib->fib_Date.ds_Tick) > 0)
            {
              putCharFuncHook.h_Data = end;
              FormatDate (english, "If-Range: %a, %d %b %Y %T", &url->fib->fib_Date, &putCharFuncHook);
              end += 10 + 25;
              end += sprintf (end, " GMT" CRLF);

            }

          end += sprintf (end, "Range: bytes=%d-" CRLF, url->fsize);

        }


      strncpy (end, CRLF "\0", 3);      //End CRLF

      i = strlen (sendmsg);

      D (kprintf ("sending:\n %s--\n\n", sendmsg));


      if ((send (sd, sendmsg, i, 0) != i))
        {
          netperror ("send");
          return (-1);
        }

      FreeVecPooled (pool, sendmsg);
    }

  return (i);
}
///

/// "lististr"
char *
lististr (struct List *ls, char *s)
{
  char *r = NULL;
  struct Node *wn = ls->lh_Head;

  while (wn != NULL)
    {
      if ((r = stristr (wn->ln_Name, s)))
        return (r);

      wn = wn->ln_Succ;
    }

  return (r);
}

///

/// "Receiving header"

long
rec_header (struct HttpData *data, struct http_url *url, int sd, char *buf, int buf_size)
{
  struct DateStamp *ds;
  int status = 0;

  struct List headerl;
  struct Node *worknode;
  char *tmp;
  int len;

  NewList (&headerl);

  D (kprintf ("*** Header:\n"));

  while ((status = recv_string (sd, buf, buf_size)) > 0)
    {
      if (*buf != '\0')
        {
          if ((tmp = (char *) AllocVecPooled (pool, len = strlen (buf) + 1)))
            {
              if ((worknode = (struct Node *) AllocSPooled (pool, sizeof (struct Node))))
                {
                  strncpy (tmp, buf, len);
                  worknode->ln_Name = tmp;
                  D (kprintf ("  %s\n", tmp));
                  AddTail (&headerl, worknode);
                }
              else
                {
                  status = -3;
                  FreeVecPooled (pool, tmp);
                  break;
                }
            }
          else
            {
              status = -3;
              break;
            }
        }
      else
        {
          // End message
          status = 1;
          break;
        }
    }

  D (kprintf ("*** End Header\n\n"));

  if (status > 0)
    {
      if ((strnicmp (headerl.lh_Head->ln_Name, "HTTP/", 5) == 0) || (strnicmp (headerl.lh_Head->ln_Name, "HTTP ", 5) == 0))
        {
          char *len;

          if (sscanf (headerl.lh_Head->ln_Name, "HTTP/%ld.%ld %ld", &url->httpver, &url->httprev, &status) < 3)
            {
              sscanf (headerl.lh_Head->ln_Name, "HTTP %ld", &status);
              url->httpver = 0;
              url->httprev = 9;
            }

          D (kprintf ("httpversion: %ld.", url->httpver));
          D (kprintf ("%ld\n", url->httprev));


          if ((url->httpver > 0) && (url->httprev > 0))         //HTTP1.1 or higher ?

            {
              if (lististr (&headerl, "Connection: close") == NULL)
                url->connection = TRUE;
            }

          if ((status > 199) && (status < 300))
            {
              // An http/1.1 client MUST support chunked encodings
              if (lististr (&headerl, "Transfer-Encoding: chunked"))
                {
                  D (kprintf ("chunked data\n"));
                  url->chunked = TRUE;
                  url->chunksize = 0;
                }

              if ((len = lististr (&headerl, "Content-Type: ")))
                {
                  len += 14;

                  if (url->type)
                    FreeVecPooled (pool, url->type);

                  url->type = newstrcpy (pool, len);
                }

              if ((len = lististr (&headerl, "Content-Length: ")))
              {
                  len += 16;

                  url->size = atoi (len);

                  // check if size <= maxsize
                  if ((url->size + ((status == 206) ? url->fsize : 0)) <= url->msize)
                  {
                    if (url->method == GET)
                      {
                        ObtainSemaphore (data->sema);
                        if (data->app)
                          {
                            ReleaseSemaphore (data->sema);
                            DoPortAMethod (st->st_Reply, st->st_Port, data->self, 3, MUIM_Set, MUIA_Gauge_Max, url->fsize + url->size);
                          }
                        else
                          ReleaseSemaphore (data->sema);
                      }
                  }
                  else
                  {
                    if (url->method == GET)
                      {

                        ObtainSemaphore (data->sema);
                        if (data->app)
                          {
                            ReleaseSemaphore (data->sema);
                            DoPortAMethod (st->st_Reply, st->st_Port, data->self, 3, MUIM_Set, MUIA_Gauge_Max, 99999999);
                          }
                        else
                          ReleaseSemaphore (data->sema);

                        url->size = 0;
                      }
                  }
              }
              else
              {
                if (url->method == GET)
                  {

                    ObtainSemaphore (data->sema);
                    if (data->app)
                      {
                        ReleaseSemaphore (data->sema);
                        DoPortAMethod (st->st_Reply, st->st_Port, data->self, 3, MUIM_Set, MUIA_Gauge_Max, 99999999);
                      }
                    else
                      ReleaseSemaphore (data->sema);

                    url->size = 0;
                  }
              }
               

              if ((len = lististr (&headerl, "Last-modified: ")))
                {
                  len += 15;

                  ds = ParseNetDate (len);

                  if (ds)
                    {
                      memcpy (&url->ds, ds, sizeof (struct DateStamp));
                      FreeSPooled (pool, ds, sizeof (struct DateStamp));
                    }
                  else
                    memset (&url->ds, 0, sizeof (struct DateStamp));
                }

              if (url->method == GET)
                {

                  if (!(url->fileout = OpenAsync (url->filename, (status == 206) ? MODE_APPEND : MODE_WRITE, 8 * 1024)))
                    {
                      D (kprintf ("I can't open write file: \"%s\"\n", url->filename));
                      status = -4;
                    }

                }

              if (status == 201)        // Created resource to location...

                {
                  if ((len = lististr (&headerl, "Location: ")))
                    {
                      len += 10;

                      if (url->redirect)
                        FreeVecPooled (pool, url->redirect);

                      url->redirect = newstrcpy (pool, len);

                      D (kprintf ("Created as: %s\n", url->redirect));
                    }
                }
              else if (status == 206)   //Partial content

                {
                  ;
                }
              else              //other 2xx messages

                {
                  ;
                }
            }                   // status  2xx

          else if ((status > 299) && (status < 400))    // 3xx message

            {
              if (lististr (&headerl, "Transfer-Encoding: chunked"))
                {
                  D (kprintf ("chunked data\n"));
                  url->chunked = TRUE;
                  url->chunksize = 0;
                }

              if ((len = lististr (&headerl, "Location: ")))
                {
                  len += 10;

                  if (url->redirect)
                    FreeVecPooled (pool, url->redirect);

                  url->redirect = newstrcpy (pool, len);

                  D (kprintf ("Redirect to: %s\n", url->redirect));
                }

              if (status != 304)        // If != Not Modified

                if (url->method == GET)
                  {
                    if (!(url->fileout = OpenAsync (url->filename, MODE_WRITE, 8 * 1024)))
                      {
                        D (kprintf ("I can't open write file: \"%s\"\n", url->filename));
                        status = -4;
                      }
                  }
            }                   // status 3xx

        }
    }

  while ((worknode = RemHead (&headerl)))
    {
      FreeVecPooled (pool, worknode->ln_Name);
      FreeSPooled (pool, worknode, sizeof (struct Node));
    }

  return (status);
}

///

/******************************************************/
/* Subtask which does all the time-consuming network. */
/******************************************************/

/// "Parse url"
BOOL
parseurl (struct http_url * url)
{
  char *u = url->url;

  if ((url->host = newstrcpy (pool, url->pu->pu_netloc)))
    {
      if ((url->path = (char *) AllocVecPooled (pool, s_strlen (url->pu->pu_path) +
                                            s_strlen (url->pu->pu_query) + 2
           )))
        {
          char *end = url->path;

          if (url->pu->pu_path)
            {
              end += sprintf (end, "%s", url->pu->pu_path);
            }

          if (url->pu->pu_query)
            {
              end += sprintf (end, "?%s", url->pu->pu_query);
            }

          url->port = url->pu->pu_port;

          return (TRUE);

          FreeVecPooled (pool, url->path);
        }

      FreeVecPooled (pool, url->host);
    }

  return (FALSE);
}
///

/// "Init"
BOOL
init_gnd (struct HttpData * data, struct http_url * hurl, struct URLNode * und)
{
  hurl->und = und;

  /*
     *** This set the right dir
   */
  
  if ((hurl->url = newstrcpy (pool, und->url)))
    {
      if ((hurl->pu = parse_url (hurl->url)))
        {
          if (parseurl (hurl))
            {
              memcpy (&hurl->ds, &und->date, sizeof (struct DateStamp));

              if ((hurl->filename = parseurl2file (hurl->pu, hurl->pprefs)))
                {
                  BPTR filei;
                  hurl->method = GET;
                  hurl->fsize = 0;
                  memset (&hurl->ds, 0, sizeof (struct DateStamp));
                  memset (hurl->fib, 0, sizeof (struct FileInfoBlock));

                  if ((filei = Lock (hurl->filename, ACCESS_READ)))
                    {
                      if ((Examine (filei, hurl->fib)))
                        {
                          memcpy (&hurl->ds, &hurl->fib->fib_Date, sizeof (struct DateStamp));
                          if ((hurl->fsize = hurl->fib->fib_Size) > 0)
                            hurl->method = HEAD;

                          UnLock (filei);
                        }
                    }

                  hurl->size = 0;
                  hurl->rec = 0;

                  if ((hurl->msize = und->maxsize) > 0)
                    {
                      hurl->method = HEAD;
                    }

                  hurl->auth = und->name;
                  hurl->passwd = und->password;
                  hurl->referer = NULL;

                  hurl->httpver = 0;
                  hurl->httprev = 9;
                  hurl->connection = FALSE;
                  hurl->chunked = FALSE;
                  hurl->type = NULL;
                  hurl->fileout = NULL;
                  hurl->redirect = NULL;
                  hurl->status = ST_ERROR;
                  hurl->etag = NULL;


                  return (TRUE);
                }
              else
                D (kprintf ("erro chkfilename\n"));
            }
          else
            D (kprintf ("error parsig\n"));
        }
      else
        D (kprintf ("error parseurl\n"));

      FreeVecPooled (pool, hurl->url);
      hurl->url = NULL;
    }

  return (FALSE);
}
///

/// "SubTask functions"
struct SubTask *
Grab_InitSubTask (void)
{
  struct Task *me = FindTask (NULL);
  struct SubTask *st;
  struct SubTaskMsg *stm;

  /*
     ** Wait for our startup message from the SpawnSubTask() function.
   */

  WaitPort (&((struct Process *) me)->pr_MsgPort);
  stm = (struct SubTaskMsg *) GetMsg (&((struct Process *) me)->pr_MsgPort);
  st = (struct SubTask *) stm->stm_Parameter;

  if ((st->st_Port = CreateMsgPort ()))
    {
      /*
         ** Reply startup message, everything ok.
         ** Note that if the initialization fails, the code falls
         ** through and replies the startup message with a stm_Result
         ** of 0 after a Forbid(). This tells SpawnSubTask() that the
         ** sub task failed to run.
       */

      stm->stm_Result = TRUE;
      ReplyMsg ((struct Message *) stm);
      return (st);
    }
  else
    {
      Forbid ();
      stm->stm_Result = FALSE;
      ReplyMsg ((struct Message *) stm);

      return (NULL);
    }
}
///

/// "Main"
void
main (void)
{
  struct SubTaskMsg *stm;
  struct http_url gnd;
  gnd.fib = &fib;

  char msg[256], *buf = NULL;
  int buf_size;

  BOOL stalled = FALSE, running = TRUE, connecting = FALSE, sending = FALSE,
    rec_head = FALSE, onlydata = FALSE;

  BOOL end = FALSE;

  int sd = -1, i;
  int retry = 0, retries, delay;

  ULONG sigs = 0;

  D (kprintf ("HTTP: Start module\n"));

  if ((st = Grab_InitSubTask ()))
    {
      struct HttpData *data = (struct HttpData *) st->st_Data;

      SetProgramName ("GNG-HTTP Downloader");

      gnd.prefs = data->prefs;
      gnd.pprefs = data->pprefs;
      mem_sema = data->mem_sema;
      pool = data->pool;

      ObtainSemaphore (&data->prefs->sema);

      buf_size = data->prefs->httpprefs.recbuf * 1024;  // BufSize is in Kbytes

      delay = data->prefs->httpprefs.recdelay * 50;     // delay in secs

      retries = data->prefs->httpprefs.numretries;

      ReleaseSemaphore (&data->prefs->sema);

      currloc = OpenLocale (NULL);
      english = OpenLocale ("PROGDIR:modules/locale.prefs");

      OpenCat(currloc);

      buf = (char *) AllocVecPooled (pool, buf_size);
      buf[0] = '\0';

      if (currloc && english && buf)
        while (((stm = (struct SubTaskMsg *) GetMsg (st->st_Port))) || running)
          {
            if (stm)
              {
                switch (stm->stm_Command)
                  {
                  case STC_SHUTDOWN:
                    /*
                     *  This is the shutdown message from KillSubTask().
                     */
                    D (kprintf ("HTTP: shut down\n"));
                    running = FALSE;
                    break;

                  case STC_START:
                    break;

                  case STC_GRABURL:
                    if (!SocketBase)
                      if ((SocketBase = OpenLibrary ("bsdsocket.library", 0)))
                        {
                          SetErrnoPtr (&sockerr, sizeof (sockerr));
                        }

                    if (SocketBase)
                      {
                        if (!init_gnd (data, &gnd, (struct URLNode *) stm->stm_Parameter))
                          {
                            ((struct URLNode *) stm->stm_Parameter)->status = ST_ERROR;
                            stm->stm_Result = -2;
                            D (kprintf ("HTTP: error urlpath!\n"));
                          }
                        else
                          {
                            stm->stm_Result = 1;
                            D (kprintf ("HTTP: grabbing %s\n", gnd.url));
                            connecting = TRUE;
                            retry = 0;
                          }
                      }
                    else
                      {
                        ((struct URLNode *) stm->stm_Parameter)->status = ST_ERROR;
                        stm->stm_Result = -1;
                        D (kprintf ("HTTP: error no bsdsocklib!\n"));
                      }
                    break;

                  }

                if (!running)
                  break;

                ReplyMsg ((struct Message *) stm);

                if (connecting)
                  for (;;)
                    {
                      /*
                       ****************
                       *** CONNECTING ***
                       ****************
                       */

                      if (connecting)
                        {
                          sprintf (msg, STRING (HTTP_CONNECTING, "Connecting to %s\n"), gnd.host);
                          D (kprintf ("HTTP: Connecting to %s\n", gnd.host));
                          message (data, 0, msg);

                          if (SetSignal (0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)      // check CTRL_C

                            {
                              gnd.status = ST_ERROR;    // INTERRUPT

                              end = TRUE;
                              connecting = FALSE;
                            }
                          else
                            {
                              if ((sd = Connect (data, &gnd)) > -1)
                                {
                                  message (data, 0, STRING (HTTP_CONNECTED, "Connected"));
                                  retry = 0;
                                  sending = TRUE;
                                  connecting = FALSE;
                                }
                              else
                                {
                                  errormessage (data, STRING (ERR_CONNECT, "Connect error!"));
                                  D (kprintf ("HTTP: connect error %ld\n", sd));

                                  if (retry++ < retries)
                                    {
                                      message (data, 0, STRING (HTTP_CONNECTRETRY, "Retry to connect..."));
                                      Delay (delay);
                                    }
                                  else
                                    {
                                      gnd.status = ST_ERROR;
                                      end = TRUE;
                                      connecting = FALSE;
                                    }
                                }
                            }
                        }

                      /*
                       *********************
                       *** SENDING REQUEST ***
                       *********************
                       */

                      else if (sending)
                        {
                          D (kprintf ("HTTP: Sending request\n"));
                          message (data, 0, STRING (HTTP_SENDING, "Sending Request..."));

                          if (SetSignal (0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)      // check CTRL_C

                            {
                              gnd.status = ST_ERROR;    // INTERRUPT

                              end = TRUE;
                            }
                          else
                            {
                              if (sendhttpreq (sd, &gnd, data) > -1)
                                rec_head = TRUE;
                              else
                                {
                                  errormessage (data, STRING (ERR_SEND, "Sending request error!"));
                                  gnd.status = ST_ERROR;
                                  end = TRUE;
                                }
                            }
                          sending = FALSE;
                        }

                      /*
                       **********************
                       *** RECEIVING HEADER ***
                       **********************
                       */

                      else if (rec_head)
                        {
                          int status;
                          message (data, 0, STRING (HTTP_RECEIVING, "Receiving header..."));

                          D (kprintf ("HTTP: %s\n", STRING (HTTP_RECEIVING, "Receiving header...")));

                          if (SetSignal (0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)      // check CTRL_C

                            {
                              gnd.status = ST_ERROR;    // INTERRUPT

                              end = TRUE;
                            }
                          else
                            {
                              status = rec_header (data, &gnd, sd, buf, buf_size);

                              if ((status > 199) && (status < 300))
                                {
                                  if (status == 206)
                                    {
                                      if (gnd.method == HEAD)
                                        {
                                          gnd.method = GET;
                                          gnd.rec = gnd.fsize;

                                          if (gnd.connection)
                                            sending = TRUE;
                                          else
                                            {
                                              closesd (sd);
                                              connecting = TRUE;
                                            }
                                        }
                                      else
                                        {
                                          onlydata = TRUE;
                                          gnd.rec = gnd.fsize;

                                          message (data, 0, STRING (HTTP_RECVDATA, "Receiving data..."));

                                          if (gnd.size)
                                            {
                                              gnd.size += gnd.fsize;
                                              sprintf (msg, STRING (HTTP_RECEIVED, "Received: %%ld of %ld"), gnd.size);
                                            }
                                          else
                                            sprintf (msg, STRING (HTTP_RECEIVEDU, "Received: %%ld of (unknown)"));
                                        }
                                    }
                                  else
                                    {
                                      if (gnd.method == GET)
                                        {
                                          onlydata = TRUE;

                                          message (data, 0, STRING (HTTP_RECVDATA, "Receiving data..."));

                                          if (gnd.size)
                                            {
                                              gnd.size += gnd.fsize;
                                              sprintf (msg, STRING (HTTP_RECEIVED, "Received: %%ld of %ld"), gnd.size);
                                            }
                                          else
                                            sprintf (msg, STRING (HTTP_RECEIVEDU, "Received: %%ld of (unknown)"));

                                        }
                                      else
                                        {
                                          if (CompareDates (&gnd.ds, &gnd.fib->fib_Date) > 0)
                                            {
                                              D (kprintf ("Different for dates\n"));

                                              gnd.fsize = 0;
                                              gnd.method = GET;

                                              if (gnd.connection)
                                                sending = TRUE;
                                              else
                                                {
                                                  closesd (sd);
                                                  connecting = TRUE;
                                                }
                                            }
                                          else if (gnd.fsize < gnd.size)
                                            {
                                              D (kprintf ("Different for size\n"));
                                              gnd.method = GET;
                                              gnd.rec = gnd.fsize;
                                              if (gnd.connection)
                                                sending = TRUE;
                                              else
                                                {
                                                  closesd (sd);
                                                  connecting = TRUE;
                                                }
                                            }
                                          else if ((gnd.fsize > 0) && (gnd.fsize == gnd.size))
                                            {
                                              errormessage (data, STRING (HTTP_NMODIFIED, "Not Modified"));
                                              gnd.status = ST_GRABBED;
                                              end = TRUE;
                                            }
                                          else
                                            {
                                              D (kprintf ("Different & however read\n"));

                                              gnd.method = GET;

                                              //gnd.rec = gnd.fsize;

                                              if (gnd.connection)
                                                sending = TRUE;
                                              else
                                                {
                                                  closesd (sd);
                                                  connecting = TRUE;
                                                }
                                            }
                                        }
                                    }
                                }
                              // 300 redirection
                              else if ((status > 299) && (status < 400))
                                {

                                  if (status == 304)
                                    {
                                      errormessage (data, STRING (HTTP_NMODIFIED, "Not Modified"));
                                      gnd.status = ST_GRABBED;
                                      gnd.size = gnd.fsize;
                                      end = TRUE;
                                    }
                                  else if (gnd.method == HEAD)
                                    {

                                      sprintf (msg, STRING (HTTP_REDIRECT, "Redirect to: %s"), gnd.redirect);
                                      errormessage (data, msg);
                                      gnd.method = GET;
                                      //gnd.und->depth++;         //I can't access to this data... :(

                                      sending = TRUE;
                                    }
                                  else
                                    {
                                      onlydata = TRUE;

                                      message (data, 0, STRING (HTTP_RECVDATA, "Receiving data..."));

                                      if (gnd.size)
                                        {
                                          gnd.size += gnd.fsize;
                                          sprintf (msg, STRING (HTTP_RECEIVED, "Received: %%ld of %ld"), gnd.size);
                                        }
                                      else
                                        sprintf (msg, STRING (HTTP_RECEIVEDU, "Received: %%ld of (unknown)"));
                                    }
                                }
                              /// 400 fatal error and here there are all of HTTP/1.1
                              else if ((status > 399) && (status < 500))
                                {
                                  if (status == 400)
                                    errormessage (data, STRING (ERR_400, "Bad Request"));
                                  else if (status == 401)
                                    errormessage (data, STRING (ERR_401, "Unauthorized"));
                                  else if (status == 402)
                                    errormessage (data, STRING (ERR_402, "Payment Required"));
                                  else if (status == 403)
                                    errormessage (data, STRING (ERR_403, "Forbidden"));
                                  else if (status == 404)
                                    errormessage (data, STRING (ERR_404, "Not Found"));
                                  else if (status == 405)
                                    errormessage (data, STRING (ERR_405, "Method Not Allowed"));
                                  else if (status == 406)
                                    errormessage (data, STRING (ERR_406, "Not Acceptable"));
                                  else if (status == 407)
                                    errormessage (data, STRING (ERR_407, "Proxy Authentication Required"));
                                  else if (status == 408)
                                    errormessage (data, STRING (ERR_408, "Request Timeout"));
                                  else if (status == 409)
                                    errormessage (data, STRING (ERR_409, "Conflict"));
                                  else if (status == 410)
                                    errormessage (data, STRING (ERR_410, "Gone"));
                                  else if (status == 411)
                                    errormessage (data, STRING (ERR_411, "Length Required"));
                                  else if (status == 412)
                                    errormessage (data, STRING (ERR_412, "Precondition Failed"));
                                  else if (status == 413)
                                    errormessage (data, STRING (ERR_413, "Request Entity Too Large"));
                                  else if (status == 414)
                                    errormessage (data, STRING (ERR_414, "Request-URI Too Long"));
                                  else if (status == 415)
                                    errormessage (data, STRING (ERR_415, "Unsupported Media Type"));
                                  else if (status == 416)
                                    errormessage (data, STRING (ERR_416, "Requested range not valid"));
                                  else if (status == 419)
                                    errormessage (data, STRING (ERR_419, "Expectation failed"));
                                  else
                                    errormessage (data, STRING (ERR_GENERIC400, "Generic 4xx error (Bad Request)"));

                                  end = TRUE;
                                }
                              else if ((status > 499) && (status < 600))
                                {
                                  // 5xx errors
                                  if (status == 500)
                                    errormessage (data, STRING (ERR_500, "Internal Server Error"));
                                  else if (status == 501)
                                    errormessage (data, STRING (ERR_501, "Not implemented"));
                                  else if (status == 502)
                                    errormessage (data, STRING (ERR_502, "Bad Gateway"));
                                  else if (status == 503)
                                    errormessage (data, STRING (ERR_503, "Service Unavailable"));
                                  else if (status == 504)
                                    errormessage (data, STRING (ERR_504, "Gateway Timeout"));
                                  else if (status == 505)
                                    errormessage (data, STRING (ERR_505, "HTTP Version Not Supported"));
                                  else if (status == 506)
                                    errormessage (data, STRING (ERR_506, "Redirection Failed"));
                                  else
                                    errormessage (data, STRING (ERR_GENERIC500, "Generic 5xx error (Internal Server Error)"));

                                  end = TRUE;
                                }
                              else if (status == 0)     // Connection closed after header

                                {
                                  errormessage (data, STRING (ERR_PCLOSED, "Connection prematurely closed"));
                                  end = TRUE;
                                }
                              else if (status < 0)
                                {
                                  if (gnd.method == HEAD)       // HEAD not supported ??

                                    {
                                      gnd.method = GET;
                                      closesd (sd);
                                      connecting = TRUE;
                                    }
                                  else
                                    {
                                      sprintf (msg, STRING (ERR_INTERNAL, "Internal error: %d"), status);
                                      errormessage (data, msg);
                                      end = TRUE;
                                    }
                                }
                              else
                                {
                                  sprintf (msg, STRING (ERR_UNKMSG, "Unknown number message: %d"), status);
                                  errormessage (data, msg);
                                  end = TRUE;
                                }

                            }

                          rec_head = FALSE;
                        }

                      /*
                       **************
                       *** REC DATA ***
                       **************
                       */

                      else if (onlydata)
                        {
                          if (gnd.chunked)
                            {
                              while (recv_string (sd, buf, buf_size) > 0)
                                {
                                  if ((gnd.chunksize = strtol (buf, 0, 16)) > 0)
                                    {
                                      while (gnd.chunksize > 0)
                                        {
                                          if ((i = recv (sd, buf, buf_size, MSG_PEEK)) > 0)
                                            {
                                              if ((gnd.chunksize - i) > 0)
                                                {
                                                  gnd.chunksize -= i;
                                                  recv (sd, buf, i, 0);
                                                  WriteAsync (gnd.fileout, buf, i);
                                                  gnd.rec += i;

                                                  message (data, gnd.rec, msg);
                                                }
                                              else
                                                {
                                                  recv (sd, buf, gnd.chunksize, 0);
                                                  WriteAsync (gnd.fileout, buf, gnd.chunksize);
                                                  gnd.rec += gnd.chunksize;
                                                  gnd.chunksize = -1;

                                                  message (data, gnd.rec, msg);
                                                }
                                            }
                                          else
                                            break;
                                        }

                                      if (i == -1)
                                        {
                                          netperror ("recv");

                                          if (sockerr == EINTR)
                                            {
                                              SetSignal (0L, SIGBREAKF_CTRL_C);
                                              gnd.status = ST_ERROR;
                                            }

                                          break;
                                        }

                                      if (recv_string (sd, buf, buf_size) < 1)  // trailer CRLF

                                        {
                                          gnd.status = ST_ERROR;
                                          break;
                                        }
                                    }
                                  else if (gnd.chunksize == 0)  // end chunk

                                    {
                                      gnd.status = ST_GRABBED;
                                      break;
                                    }
                                }

                              D (kprintf ("HTTP: End chunked\n"));
                            }
                          else  // not chunked ? normal!

                            {
                              while ((i = recv (sd, buf, buf_size, 0)) > 0)
                                {
                                  WriteAsync (gnd.fileout, buf, i);
                                  gnd.rec += i;

                                  message (data, gnd.rec, msg);
                                }

                              if (i == -1)
                                {
                                  netperror ("recv");

                                  if (sockerr == EINTR)
                                    {
                                      SetSignal (0L, SIGBREAKF_CTRL_C);
                                    }

                                  gnd.status = ST_ERROR;
                                }
                              else
                                {
                                  gnd.status = ST_GRABBED;
                                }
                            }

                          CloseAsync (gnd.fileout);
                          gnd.fileout = NULL;

                          if (SocketBase)
                            {
                              CloseLibrary (SocketBase);
                              SocketBase = NULL;
                            }

                          sd = -1;

                          if (&gnd.ds != NULL)
                            SetFileDate (gnd.filename, &gnd.ds);


                          if (gnd.size == 0)
                            gnd.size = gnd.rec;

                          ObtainSemaphore (data->sema);
                          if (data->app)
                            {
                              ReleaseSemaphore (data->sema);
                              DoPortMethod (st->st_Reply, st->st_Port, data->self, MUIM_Grab_SetUrl,
                                            gnd.type, gnd.etag, &gnd.ds, gnd.size, gnd.status);
                            }
                          else
                            ReleaseSemaphore (data->sema);

                          ObtainSemaphore (data->sema);
                          if (data->app)
                            {
                              ReleaseSemaphore (data->sema);
                              DoPortAMethod (st->st_Reply, st->st_Port, data->self, 2, MUIM_Grab_Finish, NULL);
                            }
                          else
                            ReleaseSemaphore (data->sema);


                          FreeVecPooled (pool, gnd.url);
                          FreeVecPooled (pool, gnd.host);
                          FreeVecPooled (pool, gnd.path);
                          FreeVecPooled (pool, gnd.filename);
                          FreeVecPooled (pool, gnd.type);

                          onlydata = FALSE;

                          break;
                        }

                      if (end)
                        {
                          if (gnd.fileout)
                            {
                              CloseAsync (gnd.fileout);
                              gnd.fileout = NULL;
                            }

                          if (SocketBase)
                            {
                              CloseLibrary (SocketBase);
                              SocketBase = NULL;
                            }

                          if (gnd.size == 0)
                            gnd.size = gnd.rec;

                          ObtainSemaphore (data->sema);
                          if (data->app)
                            {
                              ReleaseSemaphore (data->sema);
                              DoPortMethod (st->st_Reply, st->st_Port, data->self, MUIM_Grab_SetUrl,
                                            gnd.type, gnd.etag, &gnd.ds, gnd.size, gnd.status);
                            }
                          else
                            ReleaseSemaphore (data->sema);

                          ObtainSemaphore (data->sema);
                          if (data->app)
                            {
                              ReleaseSemaphore (data->sema);
                              DoPortAMethod (st->st_Reply, st->st_Port, data->self, 2, MUIM_Grab_Finish, NULL);
                            }
                          else
                            ReleaseSemaphore (data->sema);

                          sd = -1;

                          FreeVecPooled (pool, gnd.url);
                          FreeVecPooled (pool, gnd.host);
                          FreeVecPooled (pool, gnd.path);
                          FreeVecPooled (pool, gnd.type);

                          end = FALSE;

                          break;
                        }
                    }           /* end for (;;) */
              }
            else                // if (!stm)

              {
                sigs = Wait (SIGBREAKF_CTRL_C | (1 << st->st_Port->mp_SigBit));
              }
          }

      // Exit section:
      if (buf)
        FreeVecPooled (pool, buf);

      if (SocketBase)
        CloseLibrary (SocketBase);

      CloseCat();

      if (currloc)
        CloseLocale (currloc);

      if (english)
        CloseLocale (english);

      if (st->st_Port)
        DeleteMsgPort (st->st_Port);

      D (kprintf ("HTTP: module END\n"));

      Forbid ();

      if (stm)
        {
          stm->stm_Result = FALSE;
          ReplyMsg ((struct Message *) stm);
        }
    }
}

///























/* End */
