#define _PARSEURL_C

#include "common/parseurl.h"
#include "common/subtasksupp.h"
#include "common/urltype.h"

#define ASC2HEXD(x) (((x) >= '0' && (x) <= '9') ?               \
                     ((x) - '0') : (toupper(x) - 'A' + 10))

#define ASC2DECD(x) ((x) - '0')

extern APTR pool;

/// "constant tables"

const char hexd[] = "0123456789abcdef";

const char *ExtendedCharTable[] =
{
  "&nbsp;",                     /* NBSP */
  "&iexcl;",                    /* ! */
  "&cent;",                     /* ¢ */
  "&pound;",                    /* £ */
  "&curren;",                   /* o */
  "&yen;",                      /* ¥ */
  "&brvbar;",                   /* | */
  "&sect;",                     /* SS */
  "&uml;",                      /* " */
  "&copy;",                     /* copyright */
  "&ordf;",                     /* a_ */
  "&laquo;",                    /* << */
  "&not;",                      /* - */
  "&shy;",                      /* SHY */
  "&reg;",                      /* registered trademark */
  "&macr;",                     /* - */
  "&deg;",                      /* degrees */
  "&plusmn;",                   /* +_ */
  "&sup2;",                     /* 2 */
  "&sup3;",                     /* 3 */
  "&acute;",                    /* ' */
  "&micro;",                    /* u */
  "&para;",                     /* reverse P */
  "&middot;",                   /* . */
  "&cedil;",                    /* , */
  "&sup1;",                     /* 1 */
  "&ordm;",                     /* o_ */
  "&raquo;",                    /* >> */
  "&frac14;",                   /* 1/4 */
  "&frac12;",                   /* 1/2 */
  "&frac34;",                   /* 3/4 */
  "&iquest;",                   /* upside down ? */
  "&Agrave;",                   /* `A */
  "&Aacute;",                   /* 'A */
  "&Acirc;",                    /* ^A */
  "&Atilde;",                   /* ~A */
  "&Auml;",                     /* "A */
  "&Aring;",                    /* oA */
  "&AElig;",                    /* AE */
  "&Ccedil;",                   /* C, */
  "&Egrave;",                   /* `E */
  "&Eacute;",                   /* 'E */
  "&Ecirc;",                    /* ^E */
  "&Euml;",                     /* "E */
  "&Igrave;",                   /* `I */
  "&Iacute;",                   /* 'I */
  "&Icirc;",                    /* ^I */
  "&Iuml;",                     /* "I */
  "&ETH;",                      /* -D */
  "&Ntilde;",                   /* ~N */
  "&Ograve;",                   /* `O */
  "&Oacute;",                   /* 'O */
  "&Ocirc;",                    /* ^O */
  "&Otilde;",                   /* ~O */
  "&Ouml;",                     /* "O */
  "&times;",                    /*  x */
  "&Oslash;",                   /*  0 */
  "&Ugrave;",                   /* `U */
  "&Uacute;",                   /* 'U */
  "&Ucirc;",                    /* ^U */
  "&Uuml;",                     /* "U */
  "&Yacute;",                   /* 'Y */
  "&THORN;",                    /* Thorn */
  "&szlig;",                    /* B */
  "&agrave;",                   /* `a */
  "&aacute;",                   /* 'a */
  "&acirc;",                    /* ^a */
  "&atilde;",                   /* ~a */
  "&auml;",                     /* "a */
  "&aring;",                    /* oa */
  "&aelig;",                    /* ae */
  "&ccedil;",                   /* c, */
  "&egrave;",                   /* `e */
  "&eacute;",                   /* 'e */
  "&ecirc;",                    /* ^e */
  "&euml;",                     /* "e */
  "&igrave;",                   /* `i */
  "&iacute;",                   /* 'i */
  "&icirc;",                    /* ^i */
  "&iuml;",                     /* "i */
  "&eth;",                      /*  d */
  "&ntilde;",                   /* ~n */
  "&ograve;",                   /* `o */
  "&oacute;",                   /* 'o */
  "&ocirc;",                    /* ^o */
  "&otilde;",                   /* ~o */
  "&ouml;",                     /* "o */
  "&divide;",                   /* :- */
  "&oslash;",                   /* o/ */
  "&ugrave;",                   /* `u */
  "&uacute;",                   /* 'u */
  "&ucirc;",                    /* ^u */
  "&uuml;",                     /* "u */
  "&yacute;",                   /* 'y */
  "&thorn;",                    /* thorn */
  "&yuml;",                     /* "y */
  NULL
};

///

/// "decode html"
void
decode_html_string (unsigned char *s)
{
  unsigned char *p = s;
  unsigned char t;
  int i;
  BOOL ins = TRUE;

  for (; *s; s++, p++)
    {
      *p = *s;

      if (*s == '&')
        {
          if (*(s + 1) == '#')
            {
              if (isdigit (*(s + 2)))
                {
                  if (isdigit (*(s + 3)))
                    {
                      if (isdigit (*(s + 4)))
                        {
                          if (*(s + 5) == ';')
                            {
                              t = (ASC2DECD (*(s + 2)) * 100) + (ASC2DECD (*(s + 3)) * 10) + ASC2DECD (*(s + 4));
                              s += 5;

                              if isunres
                                (t)
                                {
                                  *p = t;
                                }
                              else
                                {
                                  *p++ = '%';
                                  *p++ = hexd[t >> 4];
                                  *p = hexd[t & '\x0f'];
                                }
                            }
                        }
                      else if (*(s + 4) == ';')
                        {
                          t = (ASC2DECD (*(s + 2)) * 10) + ASC2DECD (*(s + 3));
                          s += 4;

                          if isunres
                            (t)
                            {
                              *p = t;
                            }
                          else
                            {
                              *p++ = '%';
                              *p++ = hexd[t >> 4];
                              *p = hexd[t & '\x0f'];
                            }
                        }
                    }
                  else if (*(s + 3) == ';')
                    {
                      t = ASC2DECD (*(s + 2));
                      s += 3;

                      if isunres
                        (t)
                        {
                          *p = t;
                        }
                      else
                        {
                          *p++ = '%';
                          *p++ = '0';
                          *p = hexd[t & '\x0f'];

                        }
                    }
                }
            }
          else
            {
              for (i = 0; ExtendedCharTable[i]; i++)
                {
                  if (strncmp (s, (char *) ExtendedCharTable[i], strlen (ExtendedCharTable[i])) == 0)
                    {
                      *p = 160 + i;
                      s += strlen (ExtendedCharTable[i]) - 1;
                      ins = FALSE;
                      break;
                    }
                }

              if (ins)
                {
                  for (i = 0; ExtendedCharTable[i]; i++)
                    {
                      if (strnicmp (s, (char *) ExtendedCharTable[i], strlen (ExtendedCharTable[i])) == 0)
                        {
                          *p = 160 + i;
                          s += strlen (ExtendedCharTable[i]);
                          break;
                        }
                    }
                }
            }
        }
      else if (*s == '%')
        {
          if (isxdigit (*(s + 1)))
            {
              if (isxdigit (*(s + 2)))
                {
                  t = (ASC2HEXD (*(s + 1)) * 16) + ASC2HEXD (*(s + 2));

                  if isunres
                    (t)
                    {
                      *p = t;
                      s += 2;
                    }
                }
            }
        }
    }
  *p = '\0';
}
///

/// "char *encode_url (char *url)"
char *
encode_url (unsigned char *url, unsigned char mask)
{
  unsigned char *s, *tmp2, t;
  ULONG i = 0, r = 0;

  for (; url[i] != '\0'; i++)
    {
      if (isufmask (url[i], mask))
        {
          if (url[i] == '%')
            {
              if (isxdigit (url[i + 1]))
                {
                  if (!isxdigit (url[i + 2]))
                    r++;
                }
              else
                r++;
            }
        }
      else
        r++;
    }

  if ((s = (char *) AllocVecPooled (pool, i + (r * 2) + 1)))
    {
      tmp2 = s;

      for (; *url != '\0'; url++, tmp2++)
        {
          if (isufmask (*url, mask))
            {
              *tmp2 = *url;

              if (*url == '%')
                {
                  if (isxdigit (*(url + 1)))
                    {
                      if (!isxdigit (*(url + 2)))
                        {
                          *++tmp2 = '2';
                          *++tmp2 = '5';
                        }
                    }
                  else
                    {
                      *++tmp2 = '2';
                      *++tmp2 = '5';
                    }
                }
            }
          else
            {
              t = *url;
              *tmp2++ = '%';
              *tmp2++ = hexd[t >> 4];
              *tmp2 = hexd[t & '\x0f'];
            }
        }

      *tmp2 = '\0';
    }

  return (s);
}

///

/// "void free_parseurl (struct parseurl *pu)"
void
free_parseurl (struct parseurl *pu)
{
  if (pu)
    {
      if (pu->pu_scheme)
        FreeVecPooled (pool, pu->pu_scheme);
      if (pu->pu_netloc)
        FreeVecPooled (pool, pu->pu_netloc);
      if (pu->pu_path)
        FreeVecPooled (pool, pu->pu_path);
      if (pu->pu_query)
        FreeVecPooled (pool, pu->pu_query);
      if (pu->pu_fragment)
        FreeVecPooled (pool, pu->pu_fragment);
      if (pu->pu_name)
        FreeVecPooled (pool, pu->pu_name);
      if (pu->pu_passwd)
        FreeVecPooled (pool, pu->pu_passwd);

      FreeSPooled (pool, pu, sizeof (struct parseurl));
    }
};

///

/// "struct parseurl *parse_url (char *url)"
struct parseurl *
parse_url (char *url)
{
  // "scheme:" "//netloc" "/" "path/path/path" "?query" "#section"
  char *tmp, *purl, *end_purl;
  struct parseurl *pu;

  if ((end_purl = purl = newstrcpy (pool, url)))
    {

      if ((pu = (struct parseurl *) AllocSPooled (pool, sizeof (struct parseurl))))
        {
          pu->pu_scheme = pu->pu_netloc = pu->pu_path = pu->pu_query =
            pu->pu_fragment = pu->pu_name = pu->pu_passwd = NULL;
          pu->pu_port = 80;

          // First parse the fragment identifier
          if ((tmp = strrchr (purl, '#')))
            {
              *tmp = '\0';
              pu->pu_fragment = newstrcpy (pool, ++tmp);
            }

          // parse protocol (simply remove it for now...)
          // But it'd check if there are reserved chars (e.g. : ./this:that
          if ((tmp = strchr (purl, ':')))
            {
              *tmp = '\0';
              char *tmp2 = purl;

              while (*tmp2)
                {
                  if (!isscheme (*tmp2))
                    break;
                  tmp2++;
                }

              if ((*tmp2 == '\0') && (tmp2 != purl))    // alpha, digit, "+-."

                {
                  pu->pu_scheme = newstrcpy (pool, purl);
                  purl = ++tmp;
                }
              else
                *tmp = ':';

            }

          // there is a net_location ?
          if (strnicmp (purl, "//", 2) == 0)
            {
              if ((tmp = strchr (purl + 2, '/')))
                {
                  *tmp = '\0';
                  pu->pu_netloc = newstrcpy (pool, purl + 2);
                  *tmp = '/';
                  purl = tmp;

                  if (pu->pu_netloc)
                    {
                      if ((tmp = strchr (pu->pu_netloc, '@')))  // Find name/passwd

                        {
                          char *tmp2;
                          *tmp = '\0';

                          if ((tmp2 = strrchr (pu->pu_netloc, ':')))    // Find passwd

                            {
                              pu->pu_passwd = newstrcpy (pool, tmp2 + 1);
                              *tmp2 = '\0';
                            }

                          pu->pu_name = newstrcpy (pool, pu->pu_netloc);

                          strcpy (pu->pu_netloc, ++tmp);
                        }

                      if ((tmp = strchr (pu->pu_netloc, ':')))
                        {
                          pu->pu_port = atoi (tmp + 1);

                          //todo:
                          //I'd check for every protocol the default port number
                          //Only http for now...
                          if (pu->pu_scheme)
                            {
                              if (stricmp (pu->pu_scheme, "http") == 0)
                                {
                                  if (pu->pu_port == 80)
                                    *tmp = '\0';
                                }
                              else if (stricmp (pu->pu_scheme, "ftp") == 0)
                                {
                                  if (pu->pu_port == 21)
                                    *tmp = '\0';
                                }
                            }
                        }
                    }
                }
              else
                {
                  pu->pu_netloc = newstrcpy (pool, purl + 2);
                  purl = NULL;
                }
            }

          if ((purl) ? (*purl) : FALSE)
            {
              // Now parse query
              if ((tmp = strrchr (purl, '?')))
                {
                  *tmp = '\0';
                  pu->pu_query = newstrcpy (pool, ++tmp);
                }

              // The rest is path_name
              pu->pu_path = encode_url (purl, __UF_UNRESERVED | __UF_PCHAR);
            }
        }

      FreeVecPooled (pool, end_purl);
    }

  return (pu);
}

///

/// "void parse_path (char *path)"

void
parse_path (char *path)
{

  if (path)
    {
      char *s = path + 1;
      char *p = s, *t;

      // First remove "./"

      for (; *s; s++, p++)
        {
          if (*s == '.')        // there is a dot

            {
              if (*(s + 1) == '/')      // is "./"

                {
                  if (((s - 1) != path) && (*(s - 1) == '/'))
                    {
                      //skip this
                      s++;
                      p--;
                    }
                  else
                    *p = *s;
                }
              else if (*(s + 1) == '\0')        // is at end

                {
                  if (((s - 1) != path) && (*(s - 1) == '/'))
                    {
                      //skip this
                      p--;
                    }
                  else
                    *p = *s;
                }
              else
                *p = *s;

            }
          else
            *p = *s;
        }

      *p = '\0';

      p = path;

      // /../../../../
      // Now parse "segment/../"

      while ((s = strstr (p, "/../")))
        {
          *s = '\0';

          if ((t = strrchr (p, '/')))
            {
              if (((t + 3) == s) && (*(t + 1) == '.') && (*(t + 2) == '.') && (*(t + 3) == '/'))
                {
                  *s = '/';
                  p += 3;
                }
              else
                {
                  strcpy (t, s + 3);
                }
            }
          else
            {
              *s = '/';
              p += 3;
            }

        }

      // Remove final "/.."
      if ((s = strrchr (path + 1, '/')))
        {
          if (strncmp (s, "/..\0", 4) == 0)
            {
              *s = '\0';

              if ((s = strrchr (path, '/')))
                {
                  if (strncmp (++s, "..\0", 3) != 0)
                    *s = '\0';
                }
            }
        }

    }
}

///

/// "char *parse_relurl2absurl (struct parseurl *abs_url, struct parseurl *rel_url)"

char *
parse_relurl2absurl (struct parseurl *abs_url, struct parseurl *rel_url)
{
  char *url = NULL;

  // rel url inherits abs_url data...
  if (abs_url)
    {
      if (!rel_url->pu_scheme)
        {
          rel_url->pu_scheme = newstrcpy (pool, abs_url->pu_scheme);

          if (!rel_url->pu_netloc)
            {
              rel_url->pu_netloc = newstrcpy (pool, abs_url->pu_netloc);

              if (!rel_url->pu_name)
                rel_url->pu_name = newstrcpy (pool, abs_url->pu_name);

              if (!rel_url->pu_passwd)
                rel_url->pu_passwd = newstrcpy (pool, abs_url->pu_passwd);

              if (!rel_url->pu_path)
                {
                  rel_url->pu_path = newstrcpy (pool, abs_url->pu_path);

                  if (!rel_url->pu_query)
                    {
                      rel_url->pu_query = newstrcpy (pool, abs_url->pu_query);
                    }
                }
              else if (*(rel_url->pu_path) != '/')      /* is not absolute ? */
                {
                  char *tmp, *tmp2;

                  if (abs_url->pu_path)
                    {
                      if ((tmp = (char *) AllocVecPooled (pool, strlen (abs_url->pu_path) + strlen (rel_url->pu_path) + 1)))
                        {
                          // First copy two paths
                          strcpy (tmp, abs_url->pu_path);
                          if ((tmp2 = strrchr (tmp, '/')))
                            {
                              *(++tmp2) = '\0';
                            }

                          strcat (tmp, rel_url->pu_path);

                          FreeVecPooled (pool, rel_url->pu_path);
                          rel_url->pu_path = tmp;
                        }
                    }
                }
            }
        }
    }

  parse_path (rel_url->pu_path);

  {
    char *tmp;

    if ((rel_url->pu_netloc) && (tmp = strchr (rel_url->pu_netloc, ':')))
      {
        rel_url->pu_port = atoi (tmp + 1);

        //todo:
        //I'd check for every protocol the default port number
        //Only http & ftp for now...
        if (rel_url->pu_scheme)
          {
            if (stricmp (rel_url->pu_scheme, "http") == 0)
              {
                if (rel_url->pu_port == 80)
                  *tmp = '\0';
              }
            else if (stricmp (rel_url->pu_scheme, "ftp") == 0)
              {
                if (rel_url->pu_port == 21)
                  *tmp = '\0';
              }
          }
      }
  }

  if ((url = (STRPTR) AllocVecPooled (pool, s_strlen (rel_url->pu_scheme) +
                                      s_strlen (rel_url->pu_netloc) +
                                      s_strlen (rel_url->pu_path) +
                                      s_strlen (rel_url->pu_query) + 5

       )))
    {
      char *end = url;

      if (rel_url->pu_scheme)
        {
          end += sprintf (end, "%s:", rel_url->pu_scheme);
        }

      if (rel_url->pu_netloc)
        {
          end += sprintf (end, "//%s", rel_url->pu_netloc);
        }

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

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

  return (url);
}

///

 /*
  * DOS FUNCTIONS
  */


/// "char *UrlpToDos (char *url)"
char *
UrlpToDos (char *url)
{
  char *s, *tmp, t;
  ULONG i = 0, r = 0;

  for (; url[i] != '\0'; i++)
    {
      if (isndos (url[i]))
        r++;
    }

  if ((s = (char *) AllocVecPooled (pool, (i += (r * 2) + 1))))
    {
      tmp = s;

      for (; *url != '\0'; url++)
        {
          if (isndos (*url))
            {
              t = *url;
              *tmp++ = '^';
              *tmp++ = hexd[t >> 4];
              *tmp++ = hexd[t & '\x0f'];
            }
          else
            *tmp++ = *url;
        }

      *tmp = '\0';
    }

#define BUFFERSIZE 1024*8
  if (--i > 30)
    {
      BPTR filein;
      char readbuffer[BUFFERSIZE];
      char filename[31];
      char bufn[100];
      int n = 0;
      BOOL found = FALSE;

      memset (bufn, 0, sizeof (bufn));

      filename[0] = s[0];
      filename[1] = s[1];
      filename[2] = s[2];
      filename[3] = s[3];
      filename[4] = s[4];
      filename[5] = s[5];
      filename[6] = s[6];
      filename[7] = s[7];
      filename[8] = s[8];
      filename[9] = s[8];
      filename[10] = s[10];
      filename[11] = s[11];
      filename[12] = s[12];
      filename[13] = s[13];
      filename[14] = s[14];
      filename[15] = s[15];
      filename[16] = s[16];
      filename[17] = s[17];
      filename[18] = s[18];
      filename[19] = s[19];
      filename[20] = s[20];
      filename[21] = s[21];
      filename[22] = s[22];
      filename[23] = s[23];
      filename[24] = s[24];
      filename[25] = s[25];
      filename[26] = s[26];
      filename[27] = s[27];
      filename[28] = '^';
      filename[29] = 'i';
      filename[30] = '\0';

      if ((filein = Open (filename, MODE_READWRITE)))
        {
          while (FGets (filein, readbuffer, BUFFERSIZE) != NULL)
            {
              if (strncmp (s, readbuffer, i) == 0)
                {
                  n = atoi (readbuffer + i + 1);
                  found = TRUE;
                  break;
                }
              else
                {
                  if ((tmp = strchr (readbuffer, '=')))
                    {
                      n = atoi (++tmp);
                      if (n > 99)
                        n = 99;
                      bufn[n] = 1;
                    }
                }
            }

          if (!found)
            {
              for (i = 0; i < 100; i++)
                {
                  if (bufn[i] == 0)
                    {
                      n = i;
                      break;
                    }
                }

              i = sprintf (readbuffer, "%s=%ld\n", s, n);
              FPuts (filein, readbuffer);
            }

          Close (filein);

          if ((tmp = (char *) AllocVecPooled (pool, 30)))
            {
              strncpy (tmp, s, 30);
              sprintf (tmp + 26, "^i%ld", n);
            }

          FreeVecPooled (pool, s);
          s = tmp;

        }
    }

  return (s);
}

///

/// "BPTR cd (STRPTR dirname)"

BPTR
cd (STRPTR dirname)
{
  BPTR old = NULL, dir = NULL;
  char *dname;

  if ((dname = UrlpToDos (dirname)))
    {
      if (!(dir = Lock (dname, ACCESS_READ)))
        {
          if ((dir = CreateDir (dname)))
            ChangeMode (CHANGE_LOCK, dir, SHARED_LOCK);
        }

      if(dir)
        {
          if ((old = CurrentDir (dir)))
            UnLock (old);
          else
            dir = NULL;
        }

      FreeVecPooled (pool, dname);
    }

  return (dir);
}

///

/// "char *parseurl2file (struct parseurl *pu, struct proj_prefs *pp)"

char *
parseurl2file (struct parseurl *pu, struct proj_prefs *pp)
{
  char *t = NULL, *s = NULL;

  // First prefs downloading dir
  // then scheme...

  BPTR dir = NULL, old = NULL;

  UnLock (CurrentDir (Lock ("PROGDIR:", ACCESS_READ)));

  ObtainSemaphore (&pp->sema);
  t = pp->strdowndir + s_strlen (pp->strdowndir) - 1;
  if (*t == '/')
    *t = 0;

  if (!(dir = Lock (pp->strdowndir, ACCESS_READ)))
    {
      if ((dir = CreateDir (pp->strdowndir)))
        ChangeMode (CHANGE_LOCK, dir, SHARED_LOCK);
    }

  if (t)
    if (*t == 0)
      *t = '/';
  ReleaseSemaphore (&pp->sema);

  if (dir)
    {
      if ((old = CurrentDir (dir)))
        UnLock (old);
      else
        return (NULL);
    }
  else
    return (NULL);


  // then netloc

  if (!cd (pu->pu_netloc))
    return (NULL);

  // now parsepath...

  if ((s = pu->pu_path))
    {

      while ((t = strchr (++s, '/')))
        {
          *t = '\0';

          if (!cd (s))
            {
              *t = '/';
              return (NULL);
            }

          *t = '/';

          s = t;
        }
    }

  int size;

  if (!s || *s == '\0')
    {
      ObtainSemaphore (&pp->sema);
      s = pp->indexname;
      size = s_strlen (s);
      ReleaseSemaphore (&pp->sema);
    }
  else
    size = s_strlen (s);

  if ((t = (char *) AllocVecPooled (pool, size +
                                    s_strlen (pu->pu_query) + 2
       )))
    {
      char *end = t;

      ObtainSemaphore (&pp->sema);
      end += sprintf (end, "%s", s);
      ReleaseSemaphore (&pp->sema);

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

  if ((s = UrlpToDos (t)))
    {
      FreeVecPooled (pool, t);
      return (s);
    }

  return (NULL);
}

///
