/***************************************************************************
 *                                                                         *
 *   ACTHUTL.CPP                                                           *
 *                                                                         *
 *   Copyright (c) 1998 Galacticomm, Inc.         All rights reserved.     *
 *                                                                         *
 *   Active HTML internal utilities.
 *                                                                         *
 *                                           - J. Alvrus    06/16/1998     *
 *                                                                         *
 ***************************************************************************/

#include "acthutl.h"

#define FILREV "$Revision: 1 $"

MARKSOURCE(acthutl)

// global utilities

CHAR *                             //   returns pointer to destination buffer
acthGetHeaderValue(                // extract a header's value (incl. quoted)
CHAR const * hdr,                  //   entire header contents
size_t * pvalsiz,                  //   pointer to size of buffer (updated)
CHAR * valbuf)                     //   buffer to receive value
{
     CHAR const * valend=FindHeaderValueEnd(hdr,';');
     return(ExtractHeaderValue(hdr,valend,pvalsiz,valbuf));
}

bool                               //   returns true if found
acthGetHeaderParam(                // extract a parameter of header field
CHAR const * hdr,                  //   entire header contents
CHAR const * paramName,            //   name of parameter (e.g. "boundary")
size_t * pvalsiz,                  //   pointer to size of buffer (updated)
CHAR * valbuf)                     //   where to put value (e.g. "*/*")
{
     return(ExtractHeaderParam(hdr,paramName,pvalsiz,valbuf));
}

// internal utilities

CHAR const *                       //   returns ptr to value if found
FindHeader(                        // find start of header field's contents
acthRequest * req,                 //   request to read from
CHAR const * name)                 //   name of field (eg "Accept")
{
     const CHAR *hdr;

     INT nlen=strlen(name);
     if (nlen > 0 && name[nlen-1] == ':') {
          nlen--;                  // allow name to end in ":"
     }
     for (hdr=req->hdr1st() ; hdr != NULL ; hdr=req->hdrnxt()) {
          if (sameto(name,hdr) && hdr[nlen] == ':') {
               return(skptwht(hdr+nlen+1));
          }
     }
     return(NULL);
}

bool                               //   returns true if found
ReadHeader(                        // read a header field's entire contents
acthRequest * req,                 //   request to read from
CHAR const * name,                 //   name of field (eg "Accept")
size_t valsiz,                     //   room for value (incl '\0')
CHAR * valbuf)                     //   where to put value (eg "*/*")
{
     const CHAR *hdr=FindHeader(req,name);
     if (hdr != NULL) {
          stlcpy(valbuf,hdr,valsiz);
          return(true);
     }
     return(false);
}

CHAR const *                       //   pointer to char after end of value
FindHeaderValueEnd(                // find end of header value (incl. quoted)
CHAR const * valStart,             //   start of value
CHAR altBreak)                     //   alternate break character (e.g. ';')
{
     CHAR const * pEnd;

     if (valStart[0] == '\"') {
          for (pEnd=valStart+1 ; *pEnd != '\0' ; ++pEnd) {
               if (*pEnd == '\\') {     // next character is quoted
                    ++pEnd;
               }
               else if (*pEnd == '\"') {
                    ++pEnd;             // skip to character after quote
                    break;
               }
          }
     }
     else {
          if ((pEnd=strchr(valStart,altBreak)) == NULL) {
               pEnd=valStart+strlen(valStart);
          }
     }
     return(pEnd);
}

CHAR *                             //   returns pointer to destination buffer
ExtractHeaderValue(                // extract a header's values (incl. quoted)
CHAR const * pStart,               //   start of value (== '\"' if quoted)
CHAR const * pEnd,                 //   end of value ([-1] == '\"' if quoted)
size_t * pvalsiz,                  //   size of destination buffer (updated)
CHAR * valbuf)                     //   buffer to receive value
{
     // remove quotes if present
     bool isQuoted=(*pStart == '\"');
     if (isQuoted) {
          ++pStart;
     }
     if (isQuoted && *(pEnd-1) == '\"' && pEnd > pStart) {
          --pEnd;
     }

     // copy value into buffer, update size
     if (valbuf == NULL) {
          *pvalsiz=(size_t)((pEnd-pStart)+1);
     }
     else {
          stlcpy(valbuf,pStart,min(*pvalsiz,(size_t)((pEnd-pStart)+1)));
          *pvalsiz=strlen(valbuf)+1;
          if (!isQuoted) {
               unpad(valbuf);
          }
     }
     return(valbuf);
}

// The following function reads the first portion of a structured, semicolon-
// delimited header field.  For example, in the header field:
//
//   Content-Disposition: form-data; name="libname"
//
// The value of the "Content-Disposition" header field is "form-data".
// The "name=..." portion is an additional parameter, read using
// ReadHeaderParam().

bool                               //   returns true if found
ReadHeaderValue(                   // read a value of header field
acthRequest * req,                 //   request to read from
CHAR const * name,                 //   name of field (eg "Accept")
size_t valsiz,                     //   room for value (incl '\0')
CHAR * valbuf)                     //   where to put value (eg "*/*")
{
     // try to find header
     CHAR const * hdr=FindHeader(req,name);
     if (hdr != NULL) {

          // find end of value
          CHAR const * valend=FindHeaderValueEnd(hdr,';');

          // save and exit
          ExtractHeaderValue(hdr,valend,&valsiz,valbuf);
          return(true);
     }
     return(false);
}

bool                               //   returns true if found
ReadHeaderParam(                   // read a parameter of header field
acthRequest * req,                 //   request to read from
CHAR const * hdrName,              //   name of header (e.g. "Content-type")
CHAR const * paramName,            //   name of parameter (e.g. "boundary")
size_t valsiz,                     //   room for value (incl '\0')
CHAR * valbuf)                     //   where to put value (e.g. "*/*")
{
     CHAR const * hdr=FindHeader(req,hdrName);
     if (hdr != NULL) {
          return(ExtractHeaderParam(hdr,paramName,&valsiz,valbuf));
     }
     return(false);
}

bool                               //   returns true if found
ExtractHeaderParam(                // extract a parameter of header field
CHAR const * hdr,                  //   entire header contents
CHAR const * paramName,            //   name of parameter (e.g. "boundary")
size_t * pvalsiz,                  //   room for value (incl '\0')
CHAR * valbuf)                     //   where to put value (e.g. "*/*")
{
     // skip past header value to start of first parameter name
     CHAR const * pParam=FindHeaderValueEnd(hdr,';');
     pParam=strchr(pParam,';');
     if (pParam == NULL) {
          return(false);
     }
     pParam=skptwht(pParam+1);

     // try to find requested parameter
     do {
          bool found=false;

          // check if this is the one we want
          if (sameto(paramName,pParam)) {

               // if char after name and following whitespace is '=',
               // we found it
               found=(*skptwht(pParam+strlen(paramName)) == '=');
          }

          // find end of current parameter's value
          CHAR const * pValEnd=FindHeaderValueEnd(pParam,';');

          // find beginning of current parameter's value
          CHAR const * pValBeg=find(pParam,pValEnd,'=');
          if (pValBeg != pValEnd) {
               pValBeg=skptwht(pValBeg+1);
          }

          // if found, save and exit
          if (found) {
               if (pvalsiz != NULL) {
                    ExtractHeaderValue(pValBeg,pValEnd,pvalsiz,valbuf);
               }
               return(true);
          }

          // find start of next parameter name
          if ((pValEnd=strchr(pValEnd,';')) == NULL) {
               break;
          }
          pParam=skptwht(pValEnd+1);
     } while (*pParam != '\0');
     return(false);
}

INT                                //   returns decoded size
urlDecode(                         // decode a URL encoded parameter
CharIStreamIt ncfirst,             //   start of parameter value
CharIStreamIt nclast,              //   end of parameter value
CHAR *dcbuffer,                    //   buffer to receive result
INT dcsize)                        //   size of buffer
{
     INT i;
     INT nc;
     for (i=0 ; i < dcsize-1 && ncfirst != nclast ; ++i) {
          if ((nc=urlDecodeChar(&ncfirst,nclast)) == EOF) {
               break;
          }
          dcbuffer[i]=nc;
     }
     ASSERT(i < dcsize);
     dcbuffer[i]='\0';
     return(i);
}

INT                                //   returns next char or EOF
urlDecodeChar(                     // decode next char from URL encoded stream
CharIStreamIt * pitCur,            //   iterator to current char (updated)
CharIStreamIt const & itEnd)       //   end of stream iterator
{
     if ((*pitCur) == itEnd) {
          return(EOF);
     }
     INT c=*(*pitCur);
     switch (c) {
     case '+':
          c=' ';
          break;
     case '%':
          {
               CHAR h1,h2;
               if ((*pitCur)+1 != itEnd && isxdigit(h1=*((*pitCur)+1))
                && (*pitCur)+2 != itEnd && isxdigit(h2=*((*pitCur)+2))) {
                    c=::hexval(h1)*16+::hexval(h2);
                    ++(*pitCur);
                    ++(*pitCur);
               }
          }
          break;
     }
     ++(*pitCur);
     return(c);
}

size_t                             //   returns length of value (less term.)
StreamToBuffer(                    // extract from a stream to a buffer
CharIStreamIt const & stBeg,       //   beginning of stream data
CharIStreamIt const & stEnd,       //   past end of stream data
CHAR * buf,                        //   buffer to receive data
size_t bufSiz)                     //   size of buffer
{
     // if no buffer supplied, return value length
     if (buf == NULL || bufSiz == 0) {
          return(stEnd-stBeg);
     }

     // copy into output buffer
     CharIStreamIt stOut=stBeg;
     size_t n=0;
     while (stOut != stEnd && n < bufSiz-1) {
          *buf++=*stOut++;
          ++n;
     }
     *buf='\0';
     return(n);
}
