/*
                                  NETWOX
                             Network toolbox
                Copyright(c) 1999-2005 Laurent Constantin
                                  -----

  Main server    : http://www.laurentconstantin.com/
  Backup servers : http://go.to/laurentconstantin/
                   http://laurentconstantin.est-la.com/
                   http://laurentconstantin.free.fr/
                   http://membres.lycos.fr/lauconstantin/
  [my current email address is on the web servers]

                                  -----
  This file is part of Netwox.

  Netwox is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  version 2 as published by the Free Software Foundation.

  Netwox 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 (http://www.gnu.org/).

------------------------------------------------------------------------
*/

/*-------------------------------------------------------------*/
#include "../netwox.h"

/*-------------------------------------------------------------*/
netwib_err netwox_urlcanon_path(netwib_constbuf *ppath,
                                netwib_buf *pcanonizedpath)
{
  netwib_data data;
  netwib_uint32 datasize;
  netwib_bool endwithslash;

  data = netwib__buf_ref_data_ptr(ppath);
  datasize = netwib__buf_ref_data_size(ppath);

  if (datasize == 0) {
    return(NETWIB_ERR_PAPATHNOTCANON);
  }

  endwithslash = NETWIB_FALSE;
  if (data[datasize-1] == '/') {
    endwithslash = NETWIB_TRUE;
  }

  netwib_er(netwib_path_canon(ppath, pcanonizedpath));

  if (endwithslash) {
    data = netwib__buf_ref_data_ptr(pcanonizedpath);
    datasize = netwib__buf_ref_data_size(pcanonizedpath);
    if (data[datasize-1] != '/') {
      netwib_er(netwib_buf_append_byte('/', pcanonizedpath));
    }
  }

  return(NETWIB_ERR_OK);
}

/*-------------------------------------------------------------*/
netwib_err netwox_urlcanon_query(netwib_bufpool *pbufpool,
                                 netwib_constbuf *pquery,
                                 netwib_buf *pcanonizedquery)
{
  netwib_buf query, *pname, *pvalue;
  netwib_bool addamp, valueset;
  netwib_err ret;

  query = *pquery;

  netwib_er(netwib_bufpool_buf_init(pbufpool, &pname));
  netwib_er(netwib_bufpool_buf_init(pbufpool, &pvalue));

  addamp = NETWIB_FALSE;
  ret = NETWIB_ERR_OK;
  while (NETWIB_TRUE) {
    netwib__buf_reinit(pname);
    netwib__buf_reinit(pvalue);
    ret = netwox_url_queryvar_decode(&query, pname, &valueset, pvalue);
    if (ret == NETWIB_ERR_DATAEND) {
      ret = NETWIB_ERR_NOTFOUND;
      break;
    } else if (ret != NETWIB_ERR_OK) {
      break;
    }
    if (addamp) {
      netwib_er(netwib_buf_append_byte('&', pcanonizedquery));
    }
    ret = netwox_url_queryvar_encode(pname, valueset, pvalue,
                                     pcanonizedquery);
    if (ret != NETWIB_ERR_OK) {
      break;
    }
    addamp = NETWIB_TRUE;
  }

  netwib_er(netwib_bufpool_buf_close(pbufpool, &pvalue));
  netwib_er(netwib_bufpool_buf_close(pbufpool, &pname));

  return(NETWIB_ERR_OK);
}

/*-------------------------------------------------------------*/
typedef struct {
  netwib_buf *ppath;
  netwib_buf *pfragment;
  netwib_buf *ptmp;
  netwib_buf *pcanonizedpath;
  netwib_buf *pserveruser;
  netwib_buf *pserverpassword;
  netwib_buf *pserverhost;
  netwib_buf *pcanonizedquery;
} netwox_urlcanon_urlst;

/*-------------------------------------------------------------*/
static netwib_err netwox_urlcanon_url_abs(netwib_bufpool *pbufpool,
                                          netwox_urlcanon_urlst *purlst,
                                          netwib_constbuf *purl,
                                          netwib_buf *pcanonizedurl)
{
  netwib_buf authority, query;
  netwox_url_scheme scheme;
  netwib_port serverport;

  netwib_er(netwox_url_decode(purl, &scheme, &authority, purlst->ppath,
                              &query, purlst->pfragment));
  netwib_er(netwox_url_authority_decode(scheme, &authority,
                                        purlst->pserveruser,
                                        purlst->pserverpassword,
                                        purlst->pserverhost, &serverport));
  netwib_er(netwox_urlcanon_path(purlst->ppath, purlst->pcanonizedpath));
  netwib_er(netwox_urlcanon_query(pbufpool, &query, purlst->pcanonizedquery));
  netwib_er(netwox_url_authority_encode(scheme, purlst->pserveruser,
                                        purlst->pserverpassword,
                                        purlst->pserverhost, serverport,
                                        purlst->ptmp));
  netwib_er(netwox_url_encode(scheme, purlst->ptmp, purlst->pcanonizedpath,
                              purlst->pcanonizedquery, purlst->pfragment,
                              pcanonizedurl));

  return(NETWIB_ERR_OK);
}

/*-------------------------------------------------------------*/
static netwib_err netwox_urlcanon_url_rel(netwib_bufpool *pbufpool,
                                          netwox_urlcanon_urlst *purlst,
                                          netwib_constbuf *purl,
                                          netwib_buf *pcanonizedurl)
{
  netwib_buf query;

  netwib_er(netwox_url_pqf_decode(purl, purlst->ppath, &query,
                                  purlst->pfragment));
  netwib_er(netwox_urlcanon_path(purlst->ppath, purlst->pcanonizedpath));
  netwib_er(netwox_urlcanon_query(pbufpool, &query, purlst->pcanonizedquery));
  netwib_er(netwox_url_pqf_encode(purlst->pcanonizedpath,
                                  purlst->pcanonizedquery, purlst->pfragment,
                                  pcanonizedurl));

  return(NETWIB_ERR_OK);
}

/*-------------------------------------------------------------*/
netwib_err netwox_urlcanon_url(netwib_bufpool *pbufpool,
                               netwib_constbuf *purl,
                               netwib_buf *pcanonizedurl)
{
  netwox_urlcanon_urlst urlst;
  netwox_url_scheme scheme;
  netwib_err ret=NETWIB_ERR_OK;

  /* first, determine scheme */
  netwib_er(netwox_url_decode_scheme(purl, &scheme, NULL));
  if (scheme == NETWOX_URL_SCHEME_UNKNOWN) {
    return(NETWOX_ERR_NOTSUPPORTED);
  }

  /* initialize local variables */
  netwib_er(netwib_bufpool_buf_init(pbufpool, &urlst.ppath));
  netwib_er(netwib_bufpool_buf_init(pbufpool, &urlst.pfragment));
  netwib_er(netwib_bufpool_buf_init(pbufpool, &urlst.pcanonizedpath));
  netwib_er(netwib_bufpool_buf_init(pbufpool, &urlst.pcanonizedquery));

  /* decode url */
  if (scheme == NETWOX_URL_SCHEME_NONE) {
    ret = netwox_urlcanon_url_rel(pbufpool, &urlst, purl, pcanonizedurl);
  } else {
    netwib_er(netwib_bufpool_buf_init(pbufpool, &urlst.ptmp));
    netwib_er(netwib_bufpool_buf_init(pbufpool, &urlst.pserveruser));
    netwib_er(netwib_bufpool_buf_init(pbufpool, &urlst.pserverpassword));
    netwib_er(netwib_bufpool_buf_init(pbufpool, &urlst.pserverhost));
    ret = netwox_urlcanon_url_abs(pbufpool, &urlst, purl, pcanonizedurl);
    netwib_er(netwib_bufpool_buf_close(pbufpool, &urlst.ptmp));
    netwib_er(netwib_bufpool_buf_close(pbufpool, &urlst.pserveruser));
    netwib_er(netwib_bufpool_buf_close(pbufpool, &urlst.pserverpassword));
    netwib_er(netwib_bufpool_buf_close(pbufpool, &urlst.pserverhost));
  }

  /* close */
  netwib_er(netwib_bufpool_buf_close(pbufpool, &urlst.ppath));
  netwib_er(netwib_bufpool_buf_close(pbufpool, &urlst.pfragment));
  netwib_er(netwib_bufpool_buf_close(pbufpool, &urlst.pcanonizedpath));
  netwib_er(netwib_bufpool_buf_close(pbufpool, &urlst.pcanonizedquery));
  return(ret);
}

/*-------------------------------------------------------------*/
netwib_err netwox_urlcanon_file_read(netwib_bufpool *pbufpool,
                                     netwib_constbuf *pfileurl,
                                     netwib_constbuf *pbasehref,
                                     netwib_constbuf *purl,
                                     netwib_buf *pcanonizedurl)
{
  netwib_buf *pcanonurl, *ppath, *ppath2;
  netwib_buf authority;
  netwox_url_scheme scheme;
  netwib_data data;
  netwib_uint32 datasize;
  netwib_err ret=NETWIB_ERR_OK;

  /* if url is absolute (http://...), directly return it */
  netwib_er(netwox_url_decode_scheme(purl, &scheme, NULL));
  if (scheme == NETWOX_URL_SCHEME_UNKNOWN) {
    return(NETWOX_ERR_NOTSUPPORTED);
  }
  if (scheme != NETWOX_URL_SCHEME_NONE) {
    netwib_er(netwox_urlcanon_url(pbufpool, purl, pcanonizedurl));
    return(NETWIB_ERR_OK);
  }

  /* canonize url found in the file */
  netwib_er(netwib_bufpool_buf_init(pbufpool, &pcanonurl));
  ret = netwox_urlcanon_url(pbufpool, purl, pcanonurl);
  if (ret != NETWIB_ERR_OK) {
    netwib_er(netwib_bufpool_buf_close(pbufpool, &pcanonurl));
    return(ret);
  }

  /* determine if use pfileurl or pbasehref, and decode it */
  netwib_er(netwib_bufpool_buf_init(pbufpool, &ppath));
  if (netwib__buf_ref_data_sizenull(pbasehref)) {
    ret = netwox_url_decode(pbasehref, &scheme, &authority, ppath, NULL, NULL);
  } else {
    ret = netwox_url_decode(pfileurl, &scheme, &authority, ppath, NULL, NULL);
  }
  if (ret != NETWIB_ERR_OK) {
    netwib_er(netwib_bufpool_buf_close(pbufpool, &ppath));
    netwib_er(netwib_bufpool_buf_close(pbufpool, &pcanonurl));
    return(ret);
  }
  if (scheme == NETWOX_URL_SCHEME_UNKNOWN ||
      scheme == NETWOX_URL_SCHEME_NONE) {
    ret = NETWOX_ERR_NOTSUPPORTED;
  }

  /* construct full url */
  netwib_er(netwox_url_encode_scheme(scheme, pcanonizedurl));
  netwib_er(netwib_buf_append_buf(&authority, pcanonizedurl));
  if (scheme == NETWOX_URL_SCHEME_FTP) {
    netwib_er(netwib_buf_append_byte('/', pcanonizedurl));
  }
  /* append path */
  data = netwib__buf_ref_data_ptr(pcanonurl);
  if (data[0] == '/') {
    netwib_er(netwib_buf_append_buf(pcanonurl, pcanonizedurl));
  } else {
    /* obtain father dir of ppath (end it with '/') */
    data = netwib__buf_ref_data_ptr(ppath);
    datasize = netwib__buf_ref_data_size(ppath) - 1;
    while (NETWIB_TRUE) {
      if (data[datasize] == '/') {
        ppath->endoffset = ppath->beginoffset + datasize + 1;
        break;
      }
      if (datasize == 0) {
        /* possible with FTP which does not start with '/' */
        netwib__buf_reinit(ppath);
        netwib_er(netwib_buf_append_text("./", ppath));
        break;
      }
      datasize--;
    }
    /* append pcanonurl */
    netwib_er(netwib_buf_append_buf(pcanonurl, ppath));
    /* construct */
    if (scheme == NETWOX_URL_SCHEME_FTP) {
      netwib_er(netwib_bufpool_buf_init(pbufpool, &ppath2));
      ret = netwox_urlcanon_url(pbufpool, ppath, ppath2);
      if (ret == NETWIB_ERR_OK) {
        data = netwib__buf_ref_data_ptr(ppath2);
        datasize = netwib__buf_ref_data_size(ppath2);
        if (datasize == 1) {
          if (data[0] == '.') {
            ppath2->beginoffset++;
          }
        } else if (datasize >= 2) {
          if (data[0] == '.' && data[1] == '/') {
            ppath2->beginoffset += 2;
          }
        }
        ret = netwox_url_part_encode(ppath2, NETWOX_URL_PARTTYPE_PATH,
                                     pcanonizedurl);
      }
      netwib_er(netwib_bufpool_buf_close(pbufpool, &ppath2));
    } else {
      ret = netwox_urlcanon_url(pbufpool, ppath, pcanonizedurl);
    }
  }

  netwib_er(netwib_bufpool_buf_close(pbufpool, &ppath));
  netwib_er(netwib_bufpool_buf_close(pbufpool, &pcanonurl));
  return(ret);
}
