/*
                                  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_smbcmdtscrstorage_init(netwib_bool isunicode,
                                         netwox_smbcmdtscrstorage *pstorage)
{

  pstorage->isunicode = isunicode;
  pstorage->setupcount = 0;

  netwib_er(netwib_buf_init_ext_empty(&pstorage->para));
  pstorage->para.flags |= NETWIB_BUF_FLAGS_CANALLOC;

  netwib_er(netwib_buf_init_ext_empty(&pstorage->data));
  pstorage->data.flags |= NETWIB_BUF_FLAGS_CANALLOC;

  return(NETWIB_ERR_OK);
}

/*-------------------------------------------------------------*/
netwib_err netwox_smbcmdtscrstorage_close(netwox_smbcmdtscrstorage *pstorage)
{
  netwib_er(netwib_buf_close(&pstorage->para));
  netwib_er(netwib_buf_close(&pstorage->data));

  return(NETWIB_ERR_OK);
}

/*-------------------------------------------------------------*/
netwib_err netwox_smbcmdtscrstorage_append_smbcmd(netwox_constsmbcmd *psmbcmd,
                                          netwox_smbcmdtscrstorage *pstorage,
                                                   netwib_bool *pneedmoremsg)
{
  netwib_uint32 i;
  netwib_bool isfirst, isalone;

  if (psmbcmd->type != NETWOX_SMBCMD_TYPE_TRANSACTION_R) {
    return(NETWIB_ERR_PAINVALIDTYPE);
  }

  isfirst = NETWIB_TRUE;
  if (psmbcmd->value.transaction_r.paradisplacement ||
      psmbcmd->value.transaction_r.datadisplacement) {
    isfirst = NETWIB_FALSE;
  }
  isalone = NETWIB_TRUE;
  if ( (netwib__buf_ref_data_size(&psmbcmd->value.transaction_r.para) !=
        psmbcmd->value.transaction_r.totalparasize) ||
       (netwib__buf_ref_data_size(&psmbcmd->value.transaction_r.data) !=
        psmbcmd->value.transaction_r.totaldatasize) ) {
    isalone = NETWIB_FALSE;
  }
  if ( (netwib__buf_ref_data_size(&psmbcmd->value.transaction_r.para) >
        psmbcmd->value.transaction_r.totalparasize) ||
       (netwib__buf_ref_data_size(&psmbcmd->value.transaction_r.data) >
        psmbcmd->value.transaction_r.totaldatasize) ) {
    return(NETWIB_ERR_NOTCONVERTED);
  }

  if (isfirst) {
    if (psmbcmd->value.transaction_r.setupcount > NETWOX_SMBCMD_TRANSACTION_SETUP_MAX) {
      return(NETWIB_ERR_NOTCONVERTED);
    }
    pstorage->setupcount = psmbcmd->value.transaction_r.setupcount;
    for (i = 0; i < pstorage->setupcount; i++) {
      pstorage->setup[i] = psmbcmd->value.transaction_r.setup[i];
    }
  } else {
    if (psmbcmd->value.transaction_r.setupcount != 0) {
      return(NETWIB_ERR_NOTCONVERTED);
    }
  }

  if (isalone) {
    netwib_er(netwib_buf_init_ext_buf(&psmbcmd->value.transaction_r.para, &pstorage->para));
    netwib_er(netwib_buf_init_ext_buf(&psmbcmd->value.transaction_r.data, &pstorage->data));
    *pneedmoremsg = NETWIB_FALSE;
  } else {
    if (netwib__buf_ref_data_size(&psmbcmd->value.transaction_r.para)) {
      /* if size of psmbcmd->value.transaction_r.para is zero, we can't
         do this check under WinME, because paradisplacement is incorrectly
         set to zero. */
      if (psmbcmd->value.transaction_r.paradisplacement !=
          netwib__buf_ref_data_size(&pstorage->para)) {
        return(NETWIB_ERR_NOTCONVERTED);
      }
    }
    if (netwib__buf_ref_data_size(&psmbcmd->value.transaction_r.data)) {
      /* if size of psmbcmd->value.transaction_r.data is zero, we can't
         do this check under WinME, because datadisplacement is incorrectly
         set to zero. */
      if (psmbcmd->value.transaction_r.datadisplacement !=
          netwib__buf_ref_data_size(&pstorage->data)) {
        return(NETWIB_ERR_NOTCONVERTED);
      }
    }
    netwib_er(netwib_buf_append_buf(&psmbcmd->value.transaction_r.para, &pstorage->para));
    netwib_er(netwib_buf_append_buf(&psmbcmd->value.transaction_r.data, &pstorage->data));
    if ( (netwib__buf_ref_data_size(&pstorage->para) >
          psmbcmd->value.transaction_r.totalparasize) ||
         (netwib__buf_ref_data_size(&pstorage->data) >
          psmbcmd->value.transaction_r.totaldatasize) ) {
      return(NETWIB_ERR_NOTCONVERTED);
    }
    *pneedmoremsg = NETWIB_FALSE;
    if ( (netwib__buf_ref_data_size(&pstorage->para) !=
          psmbcmd->value.transaction_r.totalparasize) ||
         (netwib__buf_ref_data_size(&pstorage->data) !=
          psmbcmd->value.transaction_r.totaldatasize) ) {
      *pneedmoremsg = NETWIB_TRUE;
    }
  }

  return(NETWIB_ERR_OK);
}

/*-------------------------------------------------------------*/
netwib_err netwox_smbcmdtscrstorage_decode_smbcmd(netwox_constsmbcmdtscrstorage *pstorage,
                                                   netwib_uint32 *poffset,
                                                   netwib_uint32 maxsize,
                                                   netwox_smbcmd *psmbcmd,
                                                   netwib_bool *pneedmoremsg)
{
  netwib_data para, data;
  netwib_uint32 parasize, datasize, i;

  if (*poffset == 0) {
    if (pstorage->setupcount > NETWOX_SMBCMD_TRANSACTION_SETUP_MAX) {
      return(NETWIB_ERR_NOTCONVERTED);
    }
    psmbcmd->value.transaction_r.setupcount = pstorage->setupcount;
    for (i = 0; i < pstorage->setupcount; i++) {
      psmbcmd->value.transaction_r.setup[i] = pstorage->setup[i];
    }
    if (maxsize < (netwib_uint32)(pstorage->setupcount*2)) {
      return(NETWIB_ERR_NOTCONVERTED);
    }
    maxsize -= pstorage->setupcount*2;
  }

  psmbcmd->value.transaction_r.totalparasize = (netwib_uint16)netwib__buf_ref_data_size(&pstorage->para);
  psmbcmd->value.transaction_r.totaldatasize = (netwib_uint16)netwib__buf_ref_data_size(&pstorage->data);

  para = netwib__buf_ref_data_ptr(&pstorage->para);
  parasize = netwib__buf_ref_data_size(&pstorage->para);
  data = netwib__buf_ref_data_ptr(&pstorage->data);
  datasize = netwib__buf_ref_data_size(&pstorage->data);

  *pneedmoremsg = NETWIB_FALSE;
  if (*poffset < parasize) {
    para += *poffset;
    parasize -= *poffset;
    if (parasize > maxsize) {
      parasize = maxsize;
      *pneedmoremsg = NETWIB_TRUE;
    } else if (datasize) {
      *pneedmoremsg = NETWIB_TRUE;
    }
    psmbcmd->value.transaction_r.paradisplacement = (netwib_uint16)*poffset;
    psmbcmd->value.transaction_r.datadisplacement = 0;
    netwib_er(netwib_buf_init_ext_arrayfilled(para, parasize, &psmbcmd->value.transaction_r.para));
    netwib_er(netwib_buf_init_ext_empty(&psmbcmd->value.transaction_r.data));
    *poffset += parasize;
  } else if (*poffset < parasize + datasize) {
    data += *poffset - parasize;
    datasize -= *poffset - parasize;
    if (datasize > maxsize) {
      datasize = maxsize;
      *pneedmoremsg = NETWIB_TRUE;
    }
    psmbcmd->value.transaction_r.paradisplacement = (netwib_uint16)parasize;
    psmbcmd->value.transaction_r.datadisplacement = (netwib_uint16)(*poffset - parasize);
    netwib_er(netwib_buf_init_ext_empty(&psmbcmd->value.transaction_r.para));
    netwib_er(netwib_buf_init_ext_arrayfilled(data, datasize, &psmbcmd->value.transaction_r.data));
    *poffset += datasize;
  } else {
    return(NETWIB_ERR_NOTCONVERTED);
  }

  return(NETWIB_ERR_OK);
}

/*-------------------------------------------------------------*/
netwib_err netwox_smbcmdtscr_netshareenum_item_init(netwox_smbcmdtscr_netshareenum_item *pitem)
{
  netwib_er(netwox_smbcmdcmn_data_initdefault(&pitem->name));
  netwib_er(netwox_smbcmdcmn_data_initdefault(&pitem->comment));

  return(NETWIB_ERR_OK);
}

/*-------------------------------------------------------------*/
netwib_err netwox_smbcmdtscr_netshareenum_item_setdefault(netwox_smbcmdtscr_netshareenum_item *pitem)
{

  netwib_er(netwox_smbcmdcmn_data_setdefault(&pitem->name));
  pitem->sharetype = NETWOX_SMBCMDCMN_SHARETYPE_DIR;
  pitem->unknown = 0;
  netwib_er(netwox_smbcmdcmn_data_setdefault(&pitem->comment));

  return(NETWIB_ERR_OK);
}

/*-------------------------------------------------------------*/
netwib_err netwox_smbcmdtscr_netshareenum_item_close(netwox_smbcmdtscr_netshareenum_item *pitem)
{
  netwib_er(netwox_smbcmdcmn_data_close(&pitem->name));
  netwib_er(netwox_smbcmdcmn_data_close(&pitem->comment));

  return(NETWIB_ERR_OK);
}

/*-------------------------------------------------------------*/
netwib_err netwox_smbcmdtscrfmt_init_smbcmdtscq(netwox_constsmbcmdtscq *psmbcmdtscq,
                                                netwox_smbcmdtscrfmt *psmbcmdtscrfmt)
{
  netwib_buf buf;
  netwib_cmp cmp;

  switch(psmbcmdtscq->type) {
  case NETWOX_SMBCMDTSCQ_TYPE_NETSHAREENUM :
    psmbcmdtscrfmt->type = NETWOX_SMBCMDTSCR_TYPE_NETSHAREENUM;
    netwib_er(netwib_buf_init_ext_text(NETWOX_SMBCMDTSC_DESCRIPTOR_B13BWZ,
                                       &buf));
    netwib_er(netwib_buf_cmp(&psmbcmdtscq->value.netshareenum.returndescriptor,
                             &buf, &cmp));
    if (cmp == NETWIB_CMP_EQ) {
      psmbcmdtscrfmt->value.netshareenum.sharenamelen = 14;
    } else {
      return(NETWIB_ERR_NOTCONVERTED);
    }
    break;
  default :
    return(NETWIB_ERR_PAINVALIDTYPE);
  }

  return(NETWIB_ERR_OK);
}

/*-------------------------------------------------------------*/
netwib_err netwox_smbcmdtscr_init(netwox_smbcmdtscr *psmbcmdtscr)
{
  psmbcmdtscr->type = NETWOX_SMBCMDTSCR_TYPE_UNKNOWN;
  return(NETWIB_ERR_OK);
}

/*-------------------------------------------------------------*/
netwib_err netwox_smbcmdtscr_selecttype(netwox_smbcmdtscr *psmbcmdtscr,
                                        netwox_smbcmdtscr_type type)
{

  if (psmbcmdtscr->type != NETWOX_SMBCMDTSCR_TYPE_UNKNOWN) {
    return(NETWOX_ERR_INTERNALERROR);
  }

  psmbcmdtscr->type = type;
  switch(type) {
  case NETWOX_SMBCMDTSCR_TYPE_NETSHAREENUM :
    netwib_er(netwib_array_init(sizeof(netwox_smbcmdtscr_netshareenum_item),
                                0, &psmbcmdtscr->value.netshareenum.shares));
    break;
  default :
    psmbcmdtscr->type = NETWOX_SMBCMDTSCR_TYPE_UNKNOWN;
    return(NETWIB_ERR_PAINVALIDTYPE);
  }

  return(NETWIB_ERR_OK);
}

/*-------------------------------------------------------------*/
netwib_err netwox_smbcmdtscr_setdefault(netwox_smbcmdtscr *psmbcmdtscr)
{
  netwox_smbcmdtscr_netshareenum_item *pitem;
  netwib_uint32 i;

  switch(psmbcmdtscr->type) {
  case NETWOX_SMBCMDTSCR_TYPE_NETSHAREENUM :
    psmbcmdtscr->value.netshareenum.status = 0;
    psmbcmdtscr->value.netshareenum.totalshares = 0;
    for (i = 0; i < psmbcmdtscr->value.netshareenum.shares.size; i++) {
      pitem = (netwox_smbcmdtscr_netshareenum_item*)psmbcmdtscr->value.netshareenum.shares.p[i];
      netwib_er(netwox_smbcmdtscr_netshareenum_item_close(pitem));
    }
    netwib_er(netwib_array_ctl_set_size(&psmbcmdtscr->value.netshareenum.shares, 0));
    break;
  default :
    psmbcmdtscr->type = NETWOX_SMBCMDTSCR_TYPE_UNKNOWN;
    return(NETWIB_ERR_PAINVALIDTYPE);
  }

  return(NETWIB_ERR_OK);
}

/*-------------------------------------------------------------*/
netwib_err netwox_smbcmdtscr_close(netwox_smbcmdtscr *psmbcmdtscr)
{
  netwox_smbcmdtscr_netshareenum_item *pitem;
  netwib_uint32 i;

  switch(psmbcmdtscr->type) {
  case NETWOX_SMBCMDTSCR_TYPE_UNKNOWN :
    break;
  case NETWOX_SMBCMDTSCR_TYPE_NETSHAREENUM :
    for (i = 0; i < psmbcmdtscr->value.netshareenum.shares.size; i++) {
      pitem = (netwox_smbcmdtscr_netshareenum_item*)psmbcmdtscr->value.netshareenum.shares.p[i];
      netwib_er(netwox_smbcmdtscr_netshareenum_item_close(pitem));
    }
    netwib_er(netwib_array_close(&psmbcmdtscr->value.netshareenum.shares));
    break;
  default :
    return(NETWIB_ERR_PAINVALIDTYPE);
  }

  return(NETWIB_ERR_OK);
}

/*-------------------------------------------------------------*/
netwib_err netwox_smbcmdtscr_encode_storager(netwox_constsmbcmdtscrstorage *pstorage,
                                    netwox_constsmbcmdtscrfmt *psmbcmdtscrfmt,
                                    netwox_smbcmdtscr *psmbcmdtscr)
{
  netwib_data para, data, dataori, datatmp, pc;
  netwib_uint32 parasize, datasize, datasizeori, datasizetmp;
  netwib_uint32 convert, i, countshares, cmtoffset, sharenamelen;

  para = netwib__buf_ref_data_ptr(&pstorage->para);
  parasize = netwib__buf_ref_data_size(&pstorage->para);
  data = netwib__buf_ref_data_ptr(&pstorage->data);
  dataori = data;
  datasize = netwib__buf_ref_data_size(&pstorage->data);
  datasizeori = datasize;

  switch(psmbcmdtscrfmt->type) {
  case NETWOX_SMBCMDTSCR_TYPE_NETSHAREENUM :
    netwib_er(netwox_smbcmdtscr_selecttype(psmbcmdtscr, psmbcmdtscrfmt->type));
    if (pstorage->setupcount) {
      return(NETWIB_ERR_NOTCONVERTED);
    }
    if (parasize < 8 /* can be up to 1024 bytes of garbage under Win98 */) {
      return(NETWIB_ERR_NOTCONVERTED);
    }
    netwib__data_decode_uint16_le(para, psmbcmdtscr->value.netshareenum.status);
    netwib__data_decode_uint16_le(para, convert);
    netwib__data_decode_uint16_le(para, countshares);
    netwib__data_decode_uint16_le(para, psmbcmdtscr->value.netshareenum.totalshares);
    /* here, ignore Win98 garbage in para */
    sharenamelen = psmbcmdtscrfmt->value.netshareenum.sharenamelen;
    for (i = 0; i < countshares; i++) {
      netwox_smbcmdtscr_netshareenum_item *pitem;
      netwib_er(netwib_array_ctl_set_size(&psmbcmdtscr->value.netshareenum.shares, i+1));
      pitem = (netwox_smbcmdtscr_netshareenum_item*)psmbcmdtscr->value.netshareenum.shares.p[i];
      netwib_er(netwox_smbcmdtscr_netshareenum_item_init(pitem));
      netwib_er(netwox_smbcmdcmn_data_decodeext(&data, &datasize, sharenamelen, &pitem->name));
      /* suppress ending zeroes in name */
      pc = netwib_c_memchr(netwib__buf_ref_data_ptr(&pitem->name), '\0',
                           netwib__buf_ref_data_size(&pitem->name));
      if (pc != NULL) {
        pitem->name.endoffset = pc - pitem->name.totalptr;
      } else {
        /* a zero must be present */
        return(NETWIB_ERR_NOTCONVERTED);
      }
      if (datasize < 6) {
        return(NETWIB_ERR_DATAMISSING);
      }
      netwib__data_decode_uint16_le(data, pitem->sharetype);
      netwib__data_decode_uint16_le(data, cmtoffset);
      netwib__data_decode_uint16_le(data, pitem->unknown);
      datasize -= 6;
      /* offset of comment is a bit strange to compute */
      if (cmtoffset < convert) {
        return(NETWIB_ERR_NOTCONVERTED);
      }
      cmtoffset = cmtoffset - convert;
      if (cmtoffset > datasizeori) {
        return(NETWIB_ERR_NOTCONVERTED);
      }
      /* we don't check for overlapping comments */
      datatmp = dataori + cmtoffset;
      datasizetmp = datasizeori - cmtoffset;
      netwib_er(netwox_smbcmdcmn_data_decodestr(&datatmp, &datasizetmp,
                                                NETWIB_FALSE,
                                                &pitem->comment));
    }
    break;
  default :
    return(NETWIB_ERR_NOTCONVERTED);
  }


  return(NETWIB_ERR_OK);
}

/*-------------------------------------------------------------*/
netwib_err netwox_smbcmdtscr_decode_storager(netwox_constsmbcmdtscr *psmbcmdtscr,
                                    netwox_constsmbcmdtscrfmt *psmbcmdtscrfmt,
                                    netwox_smbcmdtscrstorage *pstorage)
{
  netwib_data para, data;
  netwib_uint32 convert, cmtoffset, i, sharenamelen;

  switch(psmbcmdtscr->type) {
  case NETWOX_SMBCMDTSCR_TYPE_NETSHAREENUM :
    /* compute convert */
    convert = 0; /* 0 should be ok */
    netwib_er(netwib_buf_wantspace(&pstorage->para, 8, &para));
    netwib__data_append_uint16_le(para, psmbcmdtscr->value.netshareenum.status);
    netwib__data_append_uint16_le(para, convert);
    netwib__data_append_uint16_le(para, psmbcmdtscr->value.netshareenum.shares.size);
    netwib__data_append_uint16_le(para, psmbcmdtscr->value.netshareenum.totalshares);
    pstorage->para.endoffset += 8;
    sharenamelen = psmbcmdtscrfmt->value.netshareenum.sharenamelen;
    cmtoffset = psmbcmdtscr->value.netshareenum.shares.size * (sharenamelen + 6);
    for (i = 0; i < psmbcmdtscr->value.netshareenum.shares.size; i++) {
      netwox_smbcmdtscr_netshareenum_item *pitem;
      pitem = (netwox_smbcmdtscr_netshareenum_item*)psmbcmdtscr->value.netshareenum.shares.p[i];
      netwib_er(netwib_buf_wantspace(&pstorage->data, sharenamelen + 6, &data));
      netwib_c_memset(data, 0, sharenamelen);
      if (netwib__buf_ref_data_size(&pitem->name) > sharenamelen-1) {
        return(NETWIB_ERR_PATOOHIGH);
      }
      netwib_c_memcpy(data, netwib__buf_ref_data_ptr(&pitem->name),
                      netwib__buf_ref_data_size(&pitem->name));
      data += sharenamelen;
      netwib__data_append_uint16_le(data, pitem->sharetype);
      netwib__data_append_uint16_le(data, convert+cmtoffset);
      cmtoffset += netwib__buf_ref_data_size(&pitem->comment) + 1/*for '\0'*/;
      netwib__data_append_uint16_le(data, pitem->unknown);
      pstorage->data.endoffset += sharenamelen + 6;
    }
    for (i = 0; i < psmbcmdtscr->value.netshareenum.shares.size; i++) {
      netwox_smbcmdtscr_netshareenum_item *pitem;
      pitem = (netwox_smbcmdtscr_netshareenum_item*)psmbcmdtscr->value.netshareenum.shares.p[i];
      netwib_er(netwib_buf_append_buf(&pitem->comment, &pstorage->data));
      netwib_er(netwib_buf_append_byte('\0', &pstorage->data));
    }
    break;
  default :
    return(NETWIB_ERR_PAINVALIDTYPE);
  }

  return(NETWIB_ERR_OK);
}

/*-------------------------------------------------------------*/
netwib_err netwox_smbcmdtscr_show(netwox_constsmbcmdtscr *psmbcmdtscr,
                                  netwib_buf *pbuf)
{
  netwib_uint32 i;

  switch(psmbcmdtscr->type) {
  case NETWOX_SMBCMDTSCR_TYPE_NETSHAREENUM :
    netwib_er(netwib_show_array_fmt32(pbuf, " Net Share Enum Reply"));
    netwib_er(netwib_show_array_fmt32(pbuf, " status=%{uint16}", psmbcmdtscr->value.netshareenum.status));
    netwib_er(netwib_show_array_fmt32(pbuf, " totalshares=%{uint16}", psmbcmdtscr->value.netshareenum.totalshares));
    for (i = 0; i < psmbcmdtscr->value.netshareenum.shares.size; i++) {
      netwox_smbcmdtscr_netshareenum_item *pitem;
      pitem = (netwox_smbcmdtscr_netshareenum_item*)psmbcmdtscr->value.netshareenum.shares.p[i];
      netwib_er(netwib_show_array_fmt32(pbuf, " share %{uint32}:", i));
      netwib_er(netwox_smbcmdcmn_data_show("  name", NETWOX_SMBCMDCMN_DATATYPE_STRINGONLY_NOUNI, &pitem->name, pbuf));
      netwib_er(netwox_smbcmdcmn_sharetype_show("  sharetype", pitem->sharetype, pbuf));
      netwib_er(netwib_show_array_fmt32(pbuf, "  unknown=%{uint16:#04X}", pitem->unknown));
      netwib_er(netwox_smbcmdcmn_data_show("  comment", NETWOX_SMBCMDCMN_DATATYPE_STRINGONLY_NOUNI, &pitem->comment, pbuf));
    }
    break;
  default :
    return(NETWIB_ERR_PAINVALIDTYPE);
  }

  return(NETWIB_ERR_OK);
}
