/***************************************************************************
 *                                                                         *
 *   AHUTIL.CPP                                                            *
 *                                                                         *
 *        Copyright 1997, Galacticomm, Inc., All Rights Reserved           *
 *                                                                         *
 *        Utilites for Active HTML Interface                               *
 *                                                                         *
 *                                                - Phil Henning 3/17/97   *
 *                                                  Nathan Osterc          *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "cstring.h"
#include "ahutil.h"

#define FILREV "$Revision: 38 $"

#define HTTPELEM "http://"         // http request prefix
#define NBSPELEM "&nbsp;"          // HTML code for no-space

const SHORT MAXURLSIZ=256;              // max size of URLs
const SHORT MAXURLRETSIZ=1024;          // max size of returned URL
const SHORT MAXSTPSIZ=4096;             // max size of stripped text

typedef struct usracc USRACC;
static INT olduser=-1;                  // save current usrnum

static
USRACC acc;                             // used by a few functions

GBOOL
isANSI(                                 // Checks if the character item is ANSI
CHAR* pszText,                          // Text to check
CHAR* pszURLStart);                     // Where URL was found

CHAR*                                   // converted string
Str2Url(                                // convert string to url
CHAR* pszText,                          // text to convert
CHAR* target)                           // target tag
{
     static CHAR pszParsed[MAXURLRETSIZ];
     CHAR* pszURLStart;
     CHAR* ptrURL;
     CHAR* ptr;
     INT iURLSize;
     CHAR pszURL[MAXURLSIZ];
     CHAR pszOld;

     ptr=pszText;
     strcpy(pszParsed, "");
     while ((pszURLStart=UrlLink(ptr)) != NULL) {
          iURLSize=FndUrlLngth(pszURLStart);
          stlcpy(pszURL,pszURLStart,iURLSize+1);
          pszOld=*pszURLStart;
          *pszURLStart='\0';
          stlcat(pszParsed, ptr, MAXURLRETSIZ);
          ptrURL=pszURL;
          stlcat(pszParsed, BuildUrlLink(pszURL, ptrURL, target), MAXURLRETSIZ);
          *pszURLStart=pszOld;
          ptr=pszURLStart+iURLSize;
     }
     stlcat(pszParsed, ptr, MAXURLRETSIZ);
     return(pszParsed);
}

CHAR *                                  // NULL = no, else, start of URL
UrlLink(                                // Is text a possible URL?
CHAR* pszText)                          // text to check
{
     // when adding URL prefixes, move the NULL to the end of the list
     static CHAR asUrl[][10]={
        "http:","www.","telnet:","ftp:","gopher:","rlogin:","wais:",
        "tn3270:","mailto:","news:","file:","\0"
     };
     INT iUrl;
     CHAR* pszURLStart;
     CHAR* pszFinal=NULL;

     ASSERT(pszText != NULL);

     iUrl=0;
     while (asUrl[iUrl][0] != '\0') {
          if ((pszURLStart=FindInStr(pszText,&asUrl[iUrl][0])) != NULL) {
               if (pszText == pszURLStart || *(pszURLStart-1) <= 0x20
                || isANSI(pszText, pszURLStart)) {
                    if (pszFinal == NULL || pszFinal > pszURLStart) {
                         pszFinal=pszURLStart;
                    }
               }
          }
          iUrl++;
     }
     return(pszFinal);
}

GBOOL
isANSI(                                 // Checks if the character item is ANSI
CHAR* pszText,                          // Text to check
CHAR* pszURLStart)                      // Where URL was found
{
     CHAR* ptr;

     if (pszText == pszURLStart) {
          return(FALSE);
     }
     ptr=pszURLStart-1;

     switch (*(ptr)) {
     case 'm':
     case 'J':
     case 's':
     case 'K':
     case 'A':
     case 'B':
     case 'C':
     case 'u':
          break;
     default:
          return(FALSE);
     }
     while (pszText != ptr) {
          ptr--;
          if ((validig(*ptr) || *ptr == ';')) {
               continue;
          }
          else if (*ptr == '[') {
               if (pszText != ptr && *(ptr-1) == 0x1b) {
                    return(TRUE);
               }
          }
          break;
     }
     return(FALSE);
}

CHAR*                                   // parsed string, rets. static buffer
Stp4Html(                               // replaces <>"& with &gt;, etc
CHAR* pszText)                          // string to parse
{
     static CHAR newtext[MAXSTPSIZ];
     CHAR* newptr;
     CHAR * pAdd;
     INT byts=0;

     memset(newtext,0,MAXSTPSIZ);
     newptr=newtext;
     while (*pszText != '\0' && byts < MAXSTPSIZ) {
          pAdd=NULL;
          switch (*pszText) {
          case '<':
               pAdd="&lt;";
               break;
          case '>':
               pAdd="&gt;";
               break;
          case '&':
               pAdd="&amp;";
               break;
          case '\"':
               pAdd="&quot;";
               break;
          case '\'':
               pAdd="&#39;";
               break;
          }
          if (pAdd == NULL) {
               *newptr=*pszText;
               newptr++;
               byts++;
          }
          else {
               stlcat(newtext,pAdd,MAXSTPSIZ);
               newptr+=strlen(pAdd);
               byts+=strlen(pAdd);
          }
          pszText++;
     }
     return(newtext);
}

CHAR *                                  // zstring anchor tag
BuildUrlLink(                           // make an anchor tag from psz
CHAR* pszWhole,                         // Pointer to the zstring
CHAR* pszURLStart,                      // Pointer to the url sub zstring
CHAR *target)                           // target tag
{
     // Create an anchor tag out of a URL style text string
     //
     // Warning: If using multiple calls, be sure to copy the contents of the
     //          returned string to your own storage area, as it is from a
     //          static buffer
     //

     const CHAR pszAhrefBegin[]=" <A HREF=\"";
     const CHAR pszAhrefEnd[]="</A>";
     const CHAR* pszTarget = target;

     static CHAR pszParsed[MAXURLRETSIZ];

     CHAR pszUrl[MAXURLSIZ];
     CHAR* slstart;
     INT iUrlSize;

     ASSERT(pszWhole != NULL);
     ASSERT(pszURLStart != NULL);

     memset(pszParsed,0,MAXURLRETSIZ);
     memset(pszUrl,0,MAXURLSIZ);
     iUrlSize=FndUrlLngth(pszURLStart);
     stlcpy(pszUrl,pszURLStart,iUrlSize+1);
     // add http:// on www. urls. if necessary
     if (sameto("www.",pszUrl)) {
          AddHttp(pszUrl);
     }
     if (pszWhole != pszURLStart) {
          stlcpy(pszParsed,pszWhole,(strlen(pszWhole)-strlen(pszURLStart))+1);
          stlcat(pszParsed,pszAhrefBegin,MAXURLRETSIZ);
     }
     else {
          stlcpy(pszParsed,pszAhrefBegin,MAXURLRETSIZ);
     }
     // remove '//' from urls which don't like them, if necessary.
     if (sameto("mailto:",pszUrl)) {
          if ((slstart=FindInStr(pszUrl,"//")) != NULL) {
               memmove(strchr(pszParsed,'\0'),
                       pszUrl,strlen(pszUrl)-strlen(slstart));
               slstart+=2;
               stlcat(pszParsed,slstart,MAXURLRETSIZ);
          }
          else {
               stlcat(pszParsed,pszUrl,MAXURLRETSIZ);
           }
     }
     else {
          stlcat(pszParsed,pszUrl,MAXURLRETSIZ);
     }
     stlcat(pszParsed,pszTarget,MAXURLRETSIZ);
     memmove(strchr(pszParsed,'\0'),pszURLStart,iUrlSize);
     stlcat(pszParsed,pszAhrefEnd,MAXURLRETSIZ);
     stlcat(pszParsed,&pszURLStart[iUrlSize],MAXURLRETSIZ);
     return(pszParsed);
}

//
// Supporting Routines for BuildUrlLink()
//

INT
FndUrlLngth(                            // find length of url
CHAR* psUrl)
{
     INT UrlLngth=0;

     ASSERT(psUrl != NULL);

     while (IsValidUrlChr(*psUrl)) {
          UrlLngth++;
          psUrl++;
     };
     return(UrlLngth);
}

VOID
AddHttp(                                // add http:// to front of zstring
CHAR *pszUrl)
{
     const  CHAR pszHttp[]="http://";
     static CHAR pszNewUrl[MAXURLSIZ];

     ASSERT(pszUrl != NULL);

     memset(pszNewUrl,0,MAXURLSIZ);
     stlcpy(pszNewUrl,pszHttp,MAXURLSIZ);
     stlcat(pszNewUrl,pszUrl,MAXURLSIZ);
     stlcpy(pszUrl,pszNewUrl,MAXURLSIZ);
}

GBOOL
IsValidUrlChr(                          // is char valid for a url?
CHAR ch)
{
     static CHAR achValid[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO" \
                            "PQRSTUVWXYZ_-/:.01234567890~@&%#?\0";

     CHAR* tmp;

     tmp=&achValid[0];
     do {
          if (ch == *tmp) {
               return(TRUE);
          }

          tmp++;
     } while (*tmp != '\0');
     return(FALSE);
}

//
// Supporting Routines for IsUrlLink()
//

CHAR*
FindInStr(                              // find substring within string
CHAR* pszText,                          // string to search
CHAR* pszToFind)                        // substring to search for
{
     // returns: pointer to substring or NULL if not found

     ASSERT(pszToFind != NULL);
     ASSERT(pszText != NULL);

     while (*pszText != '\0') {
          if (sameto(pszToFind,pszText)) {
               return(pszText);
          }
          pszText++;
     }
     return(NULL);
}

CHAR*
pls2spc(                                // converts '+' in string to ' '
CHAR* stg)                              // string to be scanned
{
     static CHAR newstg[UIDSIZ];
     INT i=0;

     ASSERT(stg != NULL);
     do {
          if (*stg == '+') {
               newstg[i]=' ';
          }
          else {
               newstg[i]=(*stg);
          }
          ++stg;
          ++i;
     } while (*stg != '\0');
     newstg[i]='\0';
     return(newstg);
}

CHAR*
spc2pls(                                // converts spaces in string to '+'
CHAR* stg)                              // string to be scanned
{
     static CHAR newstg[UIDSIZ];
     INT i=0;

     ASSERT(stg != NULL);
     do {
          if (*stg == ' ') {
               newstg[i]='+';
          }
          else {
               newstg[i]=(*stg);
          }
          ++stg;
          ++i;
     } while (*stg != '\0');
     newstg[i]='\0';
     return(newstg);
}

struct usracc *
GetAccPtr(                              // Returns pointer to user's usracc struct
const CHAR *uid)
{
     ASSERT(uid != NULL);
     if (onsysn(uid,1)) {
          return(uacoff(othusn));
     }
#ifdef ODBC
     bool got=readAcct(uid,&acc);
#else
     dfaSetBlk(accbb);
     bool got=dfaAcqEQ(&acc,uid,0);
     dfaRstBlk();
#endif
     return(got ? &acc : NULL);
}

VOID
setUserNum4AH(                          // sets usrnum to online user number
const CHAR *uid)                        // the user to look for online
{
     if(onsysn(uid,1)) {
          olduser = usrnum;
          curusr(othusn);
     }
}

VOID
resetUserNum4AH(                        // must be preceded by call to setUserNum4AH
const CHAR *uid)
{
     if(onsysn(uid,1)) {
          curusr(olduser);
     }
}

CHAR *                                  // returns cookie value
getCookie(                              // Gets a cookie value (returns NULL if no)
const CHAR *cookieName,                 // the CookieName
acthSession *ses)                       // Pointer to current session object
{
     ASSERT( cookieName != NULL);
     ASSERT( ses != NULL);

     CHAR buf[1024];
     CHAR *find=NULL, *found;
     static CHAR name[1024];

     setmem(buf,sizeof(buf),0);
     setmem(name,sizeof(name),0);
     if (ses->header( "Cookie", buf, sizeof( buf ) ))
     {
          CHAR *bp = buf;
          if ((find=strchr(buf,'=')) != NULL) {
               if ((found=strchr(buf,';')) != NULL) {
                   *found='\0';
               }
               stlcpy(name,find+1,1024);
               *find='\0';
               find=name;
               if (!sameas(bp,cookieName)) {
                    find=NULL;
               }
          }
          else {
               find=NULL;
          }
     }
     return(find);
}

VOID
setCookie(                              // establish a cookie value for the browser
const CHAR *cookieName,                 // the cookie name to set
const CHAR *cookieVal,                  // value to set in the cookie
const CHAR *expdate,                    // Date of expiration (can be NULL)
const CHAR *path,                       // path (can be NULL)
const CHAR *domain,                     // domain name (can be NULL)
GBOOL secure,                           // Only transmitted over secure connex?
acthSession *ses)                       // pointer to current session object
{
     ASSERT( cookieName != NULL );
     ASSERT( cookieVal  != NULL );
     ASSERT( ses != NULL);

     string cookie = cookieName;
     cookie += "=";
     cookie += cookieVal;
     cookie += "; ";

     if (expdate != NULL) {
          cookie += "expires=";
          cookie += expdate;
          cookie += "; ";
     }

     if (path != NULL) {
          cookie += "path=";
          cookie += path;
          cookie += "; ";
     }

     if (domain != NULL) {
          cookie +="domain=";
          cookie += domain;
          cookie += "; ";
     }

     if (secure) {
          cookie += "secure";
     }

     ses->headerField("Set-Cookie",cookie.c_str());
}

CHAR*
BackSlash2Percent(                      // change '\' to a '%'
CHAR *str)
{
     CHAR *ptr=str;
     while (*str != NULL) {
          if (*str == '\\') {
               *str = '%';
          }
          str++;
     }
     return(ptr);
}

CHAR*
Percent2BackSlash(                      // chane '%' to a '\'
CHAR *str)
{
     CHAR *ptr=str;
     while (*str != NULL) {
          if (*str == '%') {
               *str = '\\';
          }
          str++;
     }
     return(ptr);
}

INT
htoi(
CHAR *hexa)                             // take a hex number and return INT
{
     INT i,retval=0,flag=0;
     static CHAR hex[6]={'A','B','C','D','E','F'};

     while (1) {
          if (isdigit(*hexa)) {
               retval=(retval<<4)+(*hexa-'0');
          }
          else {
               for (i=0 ; i < 6 ; i++) {
                    if (toupper(*hexa) == hex[i]) {
                         retval=(retval<<4)+10+i;
                         break;
                    }
               }
               if (i == 6) {
                    return(flag && (*hexa == ' '
                                 || *hexa == '\0') ? retval : -1);
               }
          }
          hexa++;
          flag=1;
     }
}

BrowserType                        //   BROWSER_IE, BROWSER_NETSCAPE or BROWSER_OTHER
getClientType(                     // get browser type from User-Agent: header
const CHAR *agentStr)              //   User-Agent: header
{
     const char *iePos = strstr( agentStr, "MSIE" );
     const char *nsPos = strstr( agentStr, "Mozilla" );

     BrowserType result = BROWSER_OTHER;

     if (iePos != NULL)
     {
          result = BROWSER_IE;
     }
     else if (nsPos != NULL)
     {
          result = BROWSER_NETSCAPE;
     }

     return( result );
}


INT                                //   browser major version #, or 0
getClientVer(                      // get browser version
const CHAR *agentStr)              //   request User-Agent: header
{
     const char *numStr = "0";

     if (getClientType( agentStr ) == BROWSER_NETSCAPE)
     {
          const char *nsPos = strstr( agentStr, "Mozilla" );

          const char *slash = strchr( nsPos, '/' );

          if (slash == NULL)
          {
               slash = nsPos;
          }

          numStr = slash;
     }
     else if (getClientType( agentStr ) == BROWSER_NETSCAPE)
     {
          const char *iePos = strstr( agentStr, "MSIE" );

          numStr = iePos + 4;
     }

     while ((*numStr != '\0') && (! isdigit( *numStr )))
     {
          ++numStr;
     }

     return( atoi( numStr ) );
}

GBOOL                             // True or false
isURLTemplate(                    // is the template a URL
const CHAR *templStr)             // the template string
{
     ASSERT(templStr != NULL);
     return(*templStr == '/' || sameto(HTTPELEM,templStr));
}

GBOOL
isReflowChar(                      // is character a reflow char?
CHAR ch)                           // char to check
{
     return(ch == '\\' || ch == '/' || ch == '%' || ch == '(' || isalnum((INT)ch));
}


GBOOL
isQuoteChar(                       // is character a quote char?
const CHAR* ch)                    // char to check
{
     return(*ch == '<' || *ch == '>' || *ch == '{' || *ch == '}'
         || *ch == '|' || *ch == '[' || *ch == ']'
         || sameto("&gt;",ch) || sameto("&lt;",ch));
}

GBOOL
isQuoteLine(                       // is quoted line?
const CHAR* line)                  // line to check
{
     ASSERT(line != NULL);
     return(strlen(line) > 1
          && isalnum((INT)(*line)) && isalnum((INT)(*(line+1)))
          && isQuoteChar(line+2));
}

GBOOL
isEOL(                             // is end-of-line?
CHAR ch)                           // char to check
{
     return(ch == '\r' || ch == '\n');
}

const CHAR*
htmlFormatMessage(                 // format a message for HTML output
const CHAR* msg,                   // msg to format
GBOOL reflow,                      // reflow the message?
GBOOL plaintext)                   // display plain-text only (no HTML)
{
     static string dest("");
     const CHAR* ptr=msg;
     GBOOL isqline;
     GBOOL beginline=TRUE;

     static CHAR repone[][7]={" <BR> ","\r\n"};
     static CHAR reptwo[][7]={" <P> ","\r\n\r\n"};

     ASSERT(ptr != NULL);

     dest="";
     while (*ptr != '\0') {
          if (beginline) {
               isqline=isQuoteLine(ptr);
               beginline=FALSE;
          }
          switch (*ptr) {
          case '\r':
          case '\n':
               beginline=TRUE;
               if (isEOL(*(ptr+1))) {
                    dest+=reptwo[plaintext ? 1 : 0];
                    ptr++;
               }
               else {
                    if (reflow) {
                         if (isReflowChar(*(ptr+1))
                          && !(isqline || isQuoteLine(ptr+1))) {
                              beginline=FALSE;
                              dest+=" ";
                              break;
                         }
                    }
                    dest+=repone[plaintext ? 1 : 0];
               }
               break;
          case ' ':
               if (*(ptr+1) == ' ' && !plaintext) {
                    dest+=NBSPELEM;
               }
               else {
                    dest+=" ";
               }
               break;
          default:
               if (!plaintext) {
                    dest+=htmlEncodeCh(*ptr);
               }
               else {
                    dest+=(*ptr);
               }
          }
          ptr++;
     }
     return(dest.c_str());
}

const CHAR*                        // encoded string
htmlEncodeCh(                      // encode a single character
CHAR ch)                           // character to encode
{
     static string dest("");

     dest="";
     switch (ch) {
     case '<':
          dest+="&lt;";
          break;
     case '>':
          dest+="&gt;";
          break;
     case '&':
          dest+="&amp;";
          break;
     case '\"':
          dest+="&quot;";
          break;
     case '\'':
          dest+="&#39;";
          break;
     default:
          dest+=ch;
     }
     return(dest.c_str());
}

bool
stringInRange(                     // see if given string is in a range
CHAR start,                        // start of range
CHAR end,                          // end of range
const CHAR *checkStr)              // string to check
{
     bool inRange=TRUE;
     while (checkStr[0] != '\0') {
          if (checkStr[0] < start || checkStr[0] > end) {
               inRange=FALSE;
               break;
          }
          ++checkStr;
     }
     return(inRange);
}

bool
isProtocol(                        // is string a possible protocol
const CHAR* stg)                   // string to check
{
     bool isProto=false;
     const CHAR* pc=strchr(stg,':');
     if (pc != NULL && pc-stg < 10) {
         while (*stg != ':') {
               if (!isalnum(*stg)) {
                    break;
               }
               stg++;
         }
         isProto=true;
     }
     return(isProto);
}

