/*****************************************************************************
 *
 *  EFCOMMON.CPP
 *
 *  Common Email/Forum Functions.
 *
 *
 *                                                      - NC Osterc 6/11/98
 *
 *****************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include <windows.h>
#include <cstring.h>
#include "gme.h"
#include "galtext.h"
#include "msgah.h"
#include "efcommon.h"
#include "galmsg.h"
#include "galmsgah.h"
#include "gcspsrv.h"
#include "ahutil.h"

#define FILREV "$Revision: 46 $"
#define TABLEN 5                   // tab-length

#define NBSPELEM "&nbsp;"          // HTML code for no-space
#define HTTPELEM "http://"         // http request prefix
#define ENCBUFSIZ 4                // size of encoded buffer

// for formatURL function
#define URLPRELEN 10
struct urlpairs {
     CHAR text[URLPRELEN];
     INT  idx;
};

enum {URL_HTTP, URL_TELNET, URL_FTP, URL_GOPHER, URL_WAIS, URL_TN3270
     ,URL_MAILTO, URL_NEWS, URL_FILE, URL_WWW};

static GBOOL                       // TRUE if char should be encoded
isCgiChar(                         // is character a CGI "special" char
unsigned char ch);                 // character to check

static BYTE                        // value of the hex digit
hexVal(                            // get the value of a hex character
CHAR ch);                          // char to evaluate

typedef GBOOL __cdecl (*g2efunc)(CHAR *); // GME->Internet conversion function
typedef CHAR * __cdecl (*aoufunc)(CHAR const *); // alias of user function

MARKSOURCE(efcommon);

const CHAR*                        // returned String
formatMeridiem(                    // format meridiem as desired
unsigned time,                     // time to work with
SHORT type)                        // type of formatting
{
     static CHAR merBuf[sizeof("AM")+1];
     setmem(merBuf,sizeof(merBuf),0);
     static string retstr("");

     const CHAR *ptr=prntim(PRNT_HM_PM,time);

     stlcpy(merBuf,(CHAR *)ptr+(strlen(ptr)-2),sizeof(merBuf));

     switch(type) {
     case FMT_L:
          return(merBuf);
     case FMT_LP:
          retstr=spr("%c.%c.",merBuf[0],merBuf[1]);
          return(retstr.c_str());
     case FMT_S:
          merBuf[1]='\0';
          return(merBuf);
     case FMT_LC:
          merBuf[0]=tolower(merBuf[0]);
          merBuf[1]=tolower(merBuf[1]);
          return(merBuf);
     case FMT_LPC:
          merBuf[0]=tolower(merBuf[0]);
          merBuf[1]=tolower(merBuf[1]);
          retstr=spr("%c.%c.",merBuf[0],merBuf[1]);
          return(retstr.c_str());
     case FMT_SC:
          merBuf[0]=tolower(merBuf[0]);
          merBuf[1]='\0';
          return(merBuf);
     default:
          ASSERT(false);
     }
     return("");
}

const CHAR *                       // encoded String
cgiEncode(                         // encode a String for CGI "safety"
const CHAR* str)                   // original String
{
     string st(str);
     static string result("");

     ASSERT(str != NULL);
     result="";
     for (INT count=0; count < st.length(); ++count ) {
          BYTE ch=st[count];
          char chbuf[ENCBUFSIZ];

          if (ch == ' ') {
               result+='+';
          }
          else if (iscntrl( ch ) || (isCgiChar( ch )) || (ch > 127)) {
               sprintf(chbuf,"%%%02X",(unsigned int)ch);
               result+=chbuf;
          }
          else {
               result+=(char)ch;
          }
     }
     return(result.c_str());
}

const CHAR *                      // decoded String
cgiDecode(                        // decode a String for CGI "safety"
const CHAR* str)                  // original String
{
     string st(str);
     static string result("");

     ASSERT(str != NULL);
     result="";
     for (int count = 0; count < st.length(); ++count )
     {
          BYTE ch=st[count] ;

          if (ch == '+') {
               result+=' ';
          }
          else if ((ch == '%')
           && (count < (st.length() - 2))
           && isxdigit( st[ count + 1 ] )
           && isxdigit( st[ count + 2 ] )) {
               ch=(hexVal(st[count+1] ) << 4)|(hexVal(st[count+2]));
               result+=ch;
               count+=2;
          }
          else {
               result+=(char)ch;
          }
     }
     return(result.c_str());
}

CHAR const *                       //   returns formatted address
mailToAddr(                        // convert address to mailto format
CHAR const * address)              //   address to convert
{
     static g2efunc g2e=NULL;      // GME->Internet converter from GALSMTP
     static aoufunc * paofu=NULL;  // alias of user converter from GALALIAS
     static CHAR ** phstdom=NULL;  // host+domain: "host.domain" from GALDNS
     static bool init=false;       // has address conversion been initialized?
     static CHAR retbuf[MAXADR];   // return buffer

     ASSERT(address != NULL);

     // initialize address conversion to use GALSMTP/GALALIAS if possible
     if (!init) {
          HANDLE hMod;             // temporary handle for GetProcAddress()

          init=true;

          // attempt to reference GALSMTP
          hMod=GetModuleHandle("galsmtp");
          if (hMod != 0) {
               g2e=(g2efunc)GetProcAddress(hMod,"_xltGME2Int");
          }

          // if SMTP not available, use GALALIAS/GALDNS
          if (g2e == NULL) {

               // attempt to reference GALALIAS
               hMod=GetModuleHandle("galalias");
               if (hMod != 0) {
                    paofu=(aoufunc *)GetProcAddress(hMod,"_alsofusr");
               }

               // attempt to reference GALDNS
               hMod=GetModuleHandle("galdns");
               if (hMod != 0) {
                    phstdom=(CHAR **)GetProcAddress(hMod,"_hstdom");
               }
          }
     }

     // use GALSMTP conversion function if available
     if (g2e != NULL) {
          stlcpy(retbuf,address,MAXADR);
          if ((*g2e)(retbuf)) {
               return(retbuf);
          }
     }

     // else fake it using GALALIAS/GALDNS
     if (islocal(address)) {       // try to fake local address

          // get alias from GALALIAS if possible
          if (paofu != NULL) {
               stlcpy(retbuf,(*(*paofu))(address),MAXADR);
          }
          else {                   // fake alias
               stlcpy(retbuf,address,MAXADR);
               strrpl(retbuf,' ','.');
          }

          // get host from GALDNS if possible
          if (phstdom != NULL) {
               stlcat(retbuf,"@",MAXADR);
               stlcat(retbuf,*phstdom,MAXADR);
          }
     }
     else if (isexpa(address)) {   // just yank prefix from exported addresses
          stlcpy(retbuf,skppfx(address),MAXADR);
     }
     else {                        // do nothing to anything else
          stlcpy(retbuf,address,MAXADR);
     }
     return(retbuf);
}

const CHAR*                        // history w/parens
historyParens(                     // convert history to (history)
const CHAR* history)               // history to convert
{
     static string hist("");

     hist="";
     ASSERT(history != NULL);

     if(strlen(history) == 0) {
          return("");
     }
     hist="(";
     hist+=history;
     hist+=")";
     return(hist.c_str());
}

const CHAR*
gmeErrorHandler(                  // Verbose error message
INT code,                         // the actual error code returned
INT operation,                    // the operation being done
ostream *stream)                  // stream to output to or NULL
{
     static string errMsg("");

     errMsg="";

     setmbk(gmeAHmbk);
     switch (operation) {
     case OP_VALIDATION:
          switch (code) {
          case VALNO:
               errMsg=stpans(getmsg(ERVALNO));
               break;
          case VALACC:
               errMsg=stpans(getmsg(ERVALACC));
               break;
          case VALCRD:
               errMsg=stpans(getmsg(ERVALCRD));
               break;
          }
          break;
     case OP_GENERIC:
          switch (code) {
          case GMEERR:
              errMsg=stpans(getmsg(ERGMEERR));
              break;
          case GMEDUP:
              errMsg=stpans(getmsg(ERGMEDUP));
              break;
          case GME2MFR:
              errMsg=stpans(getmsg(ER2MFR));
              break;
          case GMENFID:
              errMsg=stpans(getmsg(ERNFID));
              break;
          case GMENFND:
              errMsg=stpans(getmsg(ERNFND));
              break;
          case GMENDEL:
              errMsg=stpans(getmsg(ERNDEL));
              break;
          case GMENMOD:
              errMsg=stpans(getmsg(ERNMOD));
              break;
          case GMEFDV:
              errMsg=stpans(getmsg(ERFDV));
              break;
          case GMECRD:
              errMsg=stpans(getmsg(ERCRD));
              break;
          case GMEMEM:
              errMsg=stpans(getmsg(ERMEM));
              break;
          case GMEIVA:
              errMsg=stpans(getmsg(ERIVA));
              break;
          case GMENOAT:
              errMsg=stpans(getmsg(ERNOAT));
              break;
          case GMEACC:
              errMsg=stpans(getmsg(ERACC));
              break;
          case GMENRGM:
              errMsg=stpans(getmsg(ERNRGM));
              break;
          case GMENCFL:
              errMsg=stpans(getmsg(ERNCFL));
              break;
          case GMERST:
              errMsg=stpans(getmsg(ERRST));
              break;
          case GMEUSE:
              errMsg=stpans(getmsg(ERUSE));
              break;
          case GMENAPV:
              errMsg=stpans(getmsg(ERNAPV));
              break;
          case GME2MFL:
              errMsg=stpans(getmsg(ER2MFL));
              break;
          case GMENSRC:
              errMsg=stpans(getmsg(ERNSRC));
              break;
          case GMENDST:
              errMsg=stpans(getmsg(ERNDST));
              break;
          }
     }
     rstmbk();
     if (stream != NULL) {
          *stream << errMsg.c_str();
     }
     return(errMsg.c_str());
}


static GBOOL                       // TRUE if char should be encoded
isCgiChar(                         // is character a CGI "special" char
unsigned char ch)                  // character to check
{
     return(ch <= ' ' || ch >= '~' || strchr("\"#%<>?[\\]^`'{|}",ch) != NULL);
}

static BYTE                        // value of the hex digit
hexVal(                            // get the value of a hex character
CHAR ch)                           // char to evaluate
{
     BYTE result=isdigit(ch ) ? (ch - '0') :
                                   ((tolower( ch ) - 'a') + 10);

     ASSERT(result <= 15);

     return(result);
}

const CHAR *                       // returns pointer to destination buf
AddComment(                        // add comments to message text
const CHAR *txt,                   // buffer containing text
const CHAR *cmt,                   // buffer containing comments
CHAR *buf,                         // destination buffer
size_t bufLength,                  // size of buffer to use
const CHAR *userid)                // userid of person making comment
{
     ASSERT(cmt != NULL);

     LONG avlspc;
     CHAR *cmtBuf = new CHAR[strlen(cmt)+1];
     stlcpy(cmtBuf,cmt,strlen(cmt)+1);
     strrpl(cmtBuf,'\r',' ');

     setmbk(efmbAH);

     avlspc=bufLength-strlen(txt)-strlen(getmsg(ABVCMT));
     avlspc-=strlen(getmsg(BLWCMT))+(2*UIDSIZ);
     if (cmtBuf != NULL && cmtBuf[0] != '\r' && cmtBuf[0] != '\n') {
          --avlspc;
     }
     if (cmtBuf == NULL || *cmtBuf == '\0' || avlspc <= 0L) {
          stlcpy(buf,txt,bufLength);
     }
     else {
          sprintf(buf,getmsg(ABVCMT),userid);
          if (cmtBuf[0] != '\r' && cmtBuf[0] != '\n') {
               stlcat(buf,"\r",(UINT)avlspc);
          }
          stlcat(buf,cmtBuf,(UINT)avlspc);
          sprintf(buf+strlen(buf),getmsg(BLWCMT),userid);
          if (isfmtted(txt)) {
               if (buf[0] == '\r' || buf[0] == '\n') {
                    movmem(buf+1,buf,strlen(buf));
               }
               stlcat(buf,"\r",bufLength);
               if (!insasc(FALSE,buf,(CHAR*)txt,bufLength)) {
                    rstmbk();
                    delete [] cmtBuf;
                    return(NULL);
               }
               stlcpy(buf,txt,bufLength);
          }
          else {
               stlcat(buf,txt,bufLength);
          }
     }
     rstmbk();
     delete [] cmtBuf;
     return(buf);
}

VOID
setWebPrefs(                       // set web prefs of a user
const CHAR *user,                  // User-ID to set prefs for
LONG flags)                        // pref flags
{
     static struct webPrefs2Disk prefs;

     ASSERT(user != NULL);
     setmem(&prefs,sizeof(struct webPrefs2Disk),0);
     stlcpy(prefs.userid,user,UIDSIZ);
     stlcpy(prefs.modnam,AHMODNAME,MNMSIZ);
     prefs.flags=flags;
     dfaSetBlk(genbb);
     if (dfaAcqEQ(NULL,&prefs,0)) {
          dfaUpdateV(&prefs,sizeof(struct webPrefs2Disk));
     }
     else {
          dfaInsertV(&prefs,sizeof(struct webPrefs2Disk));
     }
     dfaRstBlk();
}

LONG                               // pref flags
getWebPrefs(                       // get preferences of a user
const CHAR *user)                  // User-ID
{
     static struct webPrefs2Disk prefs;

     ASSERT(user != NULL);

     dfaSetBlk(genbb);
     setmem(&prefs,sizeof(struct webPrefs2Disk),0);
     stlcpy(prefs.userid,user,UIDSIZ);
     stlcpy(prefs.modnam,AHMODNAME,MNMSIZ);
     prefs.flags=0;
     dfaSetBlk(genbb);
     dfaAcqEQ(&prefs,&prefs,0);
     dfaRstBlk();
     dfaRstBlk();
     return(prefs.flags);
}

const CHAR*
formatMessage(                     // 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*                        // linked string
formatURLs(                        // parse URLs, and create links
const CHAR* msg,                   // text to check for URLS
const CHAR* anchor)                // anchor tag to use for linking
{
     static string dest("");
     static struct urlpairs urls[]={
          {"http:",URL_HTTP},
          {"telnet:",URL_TELNET},
          {"ftp://",URL_FTP},
          {"gopher://",URL_GOPHER},
          {"wais:",URL_WAIS},
          {"tn3270:",URL_TN3270},
          {"mailto:",URL_MAILTO},
          {"news:",URL_NEWS},
          {"file:",URL_FILE},
          {"www.",URL_WWW},
          {"",0}
     };
     const CHAR* ptr=msg;

     dest="";
     while (*ptr != '\0') {
          GBOOL isurl=FALSE;
          INT i=0;
          while (*(urls[i].text) != '\0') {
               if (sameto(urls[i].text,ptr)) {
                    size_t urllen=strlen(urls[i].text);
                    if (ptr != msg) {
                        if (*(ptr-1) == ' '
                         || isEOL(*(ptr-1))
                         || isQuoteChar(ptr-1)
                         || (ptr-strlen("&gt;") > msg
                          && isQuoteChar(ptr-strlen("&gt;")))) {
                              if (isalnum(*(ptr+urllen)) || (*(ptr+urllen)) == '/') {
                                   if (*(ptr+urllen) != ' ') {
                                        if (!isEOL(*(ptr+urllen))) {
                                             isurl=TRUE;
                                             break;
                                        }
                                   }
                              }
                         }
                    }
              }
              i++;
          }
          if (isurl) {
               INT bufLen=0;
               string linkbuf;
               linkbuf="";
               if (urls[i].idx == URL_WWW) {
                    bufLen+=strlen(HTTPELEM);
                    linkbuf+=HTTPELEM;
               }
               const CHAR* sp=strchr(ptr,' ');
               size_t len=strlen(ptr);
               INT nbsp=findstg(NBSPELEM,(CHAR*)ptr);
               if (sp != NULL) {
                    if (nbsp != 0 && (nbsp-strlen(NBSPELEM)) < (sp-ptr)) {
                         sp=(ptr)+(nbsp-strlen(NBSPELEM));
                    }
                    len=(sp-ptr);
                    while (len > 0  && !isalnum(*(ptr+len-1))) {
                         --len;
                    }
               }
               linkbuf.append(ptr,0,len);
               bufLen+=(2*len)+strlen("</A>")+strlen(anchor)+1;
               CHAR* tmpBuf=new CHAR[bufLen];
               sprintf(tmpBuf,anchor,linkbuf.c_str());
               ASSERT(strlen(tmpBuf)+len < bufLen);
               strncat(tmpBuf,ptr,len);
               stlcat(tmpBuf,"</A>",bufLen);
               ptr+=len;
               dest+=tmpBuf;
               delete [] tmpBuf;
          }
          else {
               dest+=(*(ptr));
               ptr++;
          }
     }
     return(dest.c_str());
}


const CHAR *                       // string w/ cc-addresses
getCCAddresses(                    // grab CC-addresses from message body
const CHAR *text,                  // body text
INT sizeofText,                    // size of message body text
const CHAR *userWritingMsg)        // user writing the message
{
     static string ccAddrs;
     CHAR *ccPtr=NULL;
     CHAR *toPtr=NULL;
     GBOOL okForCC=FALSE;

     ASSERT(userWritingMsg != NULL);
     ASSERT(sizeofText > 0 && sizeofText <= TXTLEN);
     CHAR *buf=new CHAR[sizeofText];
     stlcpy(buf,text,sizeofText);
     CHAR *txPtr=(buf+sizeofText)-1;

     ccAddrs="";
     while (txPtr != buf) {
          if (sameto("to: ",txPtr)) {
               toPtr=txPtr+sizeof("to:");
               CHAR *ptrnl=strchr(txPtr,'\r');
               if (ptrnl != NULL) {
                    ++ptrnl;
                    if (sameto("cc: ",ptrnl)) {
                         okForCC=TRUE;
                         ccPtr=ptrnl+sizeof("cc:");
                         txPtr=(buf+sizeofText)-1;
                         break;
                    }
               }
               break;
          }
          --txPtr;
     }
     if (okForCC) {
          while (txPtr != ccPtr) {
               if (sameto("\r    ",txPtr)) {
                    CHAR *ptrnl=strchr(txPtr+1,'\r');
                    if (ptrnl != NULL) {
                         *ptrnl='\0';
                    }
                    if (*(txPtr+TABLEN) > '\x20') {
                         ccAddrs+=txPtr+TABLEN;
                         ccAddrs+=";";
                    }
               }
               --txPtr;
          }
          CHAR *termPtr=strchr(ccPtr,'\r');
          if (termPtr != NULL) {
               *termPtr='\0';
          }
          ccAddrs+=ccPtr;
          *(ccPtr-sizeof("cc: "))='\0';
          if (!sameas(toPtr,userWritingMsg)) {
               ccAddrs+=";";
               ccAddrs+=toPtr;
          }
     }
     delete [] buf;
     return (ccAddrs.c_str());
}

GBOOL
pfnerr(                            /* is string too profane?               */
const CHAR* usr,                   /*   userid to check with               */
CHAR *str,                         /*   string to check                    */
USHORT forum)                      /*   forum to use for profanity level   */
{
     SHORT maxpfn;

     ASSERT(usr != NULL);
     if (uhskey(usr,syskey)) {
          return(FALSE);
     }
     if (forum == EMLID) {
          maxpfn=3-pfceil;
     }
     else {
          maxpfn=getdefp(forum)->pfnlvl;
          if (maxpfn == DFTPFN) {
               maxpfn=3-pfceil;
          }
          else {
               maxpfn=3-maxpfn;
          }
     }
     return(profan(str) > maxpfn);
}

