/***************************************************************************
 *                                                                         *
 *   WLMUTL.C                                                              *
 *                                                                         *
 *   Copyright (c) 1997 Galacticomm, Inc.         All Rights Reserved.     *
 *                                                                         *
 *   Worldlink Messaging utilities.                                        *
 *                                                                         *
 *                                            - J. Alvrus   1/14/97        *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "gme.h"
#include "wlmutl.h"

#define FILREV "$Revision: 6 $"

                                   /* from SMTPEXP.H                       */
#define INUIDSZ     64             /* longest user alias supported         */

HMCVFILE wlmb;                     /* .MCV file handle                     */
GBOOL wlActive=FALSE;              /* is Worldlink mail service active     */
CHAR inpfx[PFXSIZ+1]="";           /* foreign internet mail prefix         */

VOID impSenseIN(VOID);
CHAR *findSect(const CHAR *sectName,const CHAR *src);
CHAR *findSectName(const CHAR *sectName,const CHAR *src);
CHAR *findItem(const CHAR *itemName,const CHAR *src);
CHAR *findItemName(const CHAR *itemName,const CHAR *src);
GBOOL addSect(const CHAR *sectName,CHAR *buf,size_t bufSiz);
VOID delSect(const CHAR *sectName,CHAR *buf);
VOID delItem(const CHAR *sectName,const CHAR *itemName,CHAR *buf);
CHAR *nextSect(const CHAR *str);
CHAR *nextItem(const CHAR *str);

VOID
initINPfx(                         /* initialize internet exporter prefix  */
INT msgnum)                        /*   MCV number to get from             */
{
     if (*stlcpy(inpfx,rawmsg(msgnum),sizeof(inpfx)) == '\0') {
          rtkick(1,impSenseIN);
     }
     else if (!samend(inpfx,":")) {
          stlcat(inpfx,":",sizeof(inpfx));
     }
}

VOID
impSenseIN(VOID)                   /* auto-sense internet exporter         */
{
     INT i,n;
     const struct expinfo *exp;

     for (i=0,n=numexp() ; i < n ; ++i) {
          exp=expinf(i);
          if (sameto("in",exp->prefix)
           || samein("internet",exp->name) || samein("smtp",exp->name)
           || samein("internet",exp->desc) || samein("smtp",exp->desc)) {
               stlcpy(inpfx,exp->prefix,PFXSIZ);
               stlcat(inpfx,":",PFXSIZ+1);
               return;
          }
     }
}

GBOOL
wfval(                             /* Worldlink forums address validator   */
const CHAR *addr)                  /*   address to check                   */
{
     if (!wlActive) {
          return(FALSE);
     }
     return(wlmIsValForum(addr));
}

GBOOL
wlmIsInetAddr(                     /* is this a valid SMTP address?        */
const CHAR *addr)                  /*   address to check                   */
{
     CHAR *cp;

     return(((cp=strchr(addr,'@')) != NULL && strchr(cp,'.') != NULL)
         || strchr(addr,'!') != NULL);
}

GBOOL
wlmIsValForum(                     /* is this a valid WL forum name?       */
const CHAR *addr)                  /*   address to check                   */
{
     CHAR c;

     if (strlen(addr) >= MAXFNAM || strlen(addr) < CSTRLEN("x.y")
      || strchr(addr,'.') == NULL) {
          return(FALSE);
     }
     while ((c=*addr++) != '\0') {
          if (!isalnum(c) && strchr("+-._",c) == NULL) {
               return(FALSE);
          }
     }
     return(TRUE);
}

GBOOL
wlmIsPostmaster(                   /* does this addr refer to Postmaster?  */
const CHAR *addr)                  /*   address to check                   */
{
     return((islocal(addr) && sameas(WLPOSTM,addr))
         || sameas(WLPOSTM,worUseridUnmake(addr)));
}

GBOOL
isRegNo(                           /* is this string a registration number?*/
const CHAR *rno)                   /*   string to check                    */
{
     size_t l;

     return(((l=strlen(rno)) == 8 || l == 7) && alldgs(rno));
}

CHAR *                             /*   returns pointer to destination     */
wlmSysID2Reg(                      /* get reg # from GME System-ID         */
LONG sysid,                        /*   GME System-ID to convert           */
CHAR *regno)                       /*   buffer for reg # (size >= 9)       */
{
     CHAR *cp;
     ULONG usid;

     ASSERT(regno != NULL);
     ASSERT((sysid&0x80808080L) == 0x80808080L);
     cp=regno;
     usid=(ULONG)sysid;
     while (cp < regno+8) {
          *cp++=(CHAR)((usid&0x7F)/10)+'0';
          *cp++=(CHAR)((usid&0x7F)%10)+'0';
          usid>>=8;
     }
     *cp='\0';
     return(regno);
}

CHAR *                             /*   returns pointer to address         */
subpfx(                            /* substitute an exporter prefix        */
CHAR *adr,                         /*   address to substitue on            */
const CHAR *pfx)                   /*   new prefix (including ':')         */
{
     strmove(adr+strlen(pfx),skppfx(adr));
     memcpy(adr,pfx,strlen(pfx));
     return(adr);
}

CHAR *                             /*   returns updated src, NULL if done  */
parseAccLine(                      /* extract a forum/system/access line   */
CHAR *src,                         /*   buffer to extract from             */
CHAR *sysName,                     /*   buf for sys name (must be WBOASIZ) */
INT *access)                       /*   buf for access or NULL to ignore   */
{
     CHAR *rp,*sp,*ap;

     if (src == NULL || *(src=skpwht(src)) == '\0') {
          return(NULL);
     }
     if ((rp=strchr(src,'\n')) != NULL) {
          *rp++='\0';
     }
     else {
          rp=src+strlen(src);
     }
     *sysName='\0';
     if ((sp=parseAccItem(&src,access == NULL ? '\0' : '\t')) != NULL
      && (access == NULL || (ap=parseAccItem(&src,'\0')) != NULL)) {
          unpad(stlcpy(sysName,sp,WBOASIZ));
          if (access != NULL) {
               *access=atoi(ap);
          }
     }
     return(rp);
}

CHAR *                             /*   ptr to next item or NULL if no more*/
parseAccItem(                      /* parse an item out of an access line  */
CHAR **psrc,                       /*   ptr to ptr to source (updated)     */
CHAR sepchr)                       /*   item separator character           */
{
     CHAR *cp,*rp;

     rp=*psrc;
     if (rp == NULL || *(rp=skpwht(rp)) == '\0') {
          return(NULL);
     }
     if ((cp=strchr(rp,sepchr)) != NULL) {
          *cp++='\0';
     }
     *psrc=cp;
     return(rp);
}

CHAR *                             /*   pointer to parsed-out word         */
parseWord(                         /* parse a word out of a string         */
CHAR **ppstr)                      /*   ptr to ptr to string (updated)     */
{
     CHAR *scp,*ecp;

     scp=skpwht(*ppstr);
     if ((ecp=strchr(scp,' ')) != NULL) {
          *ecp++='\0';
          *ppstr=ecp;
     }
     return(scp);
}

CHAR *                             /*   returns copy of ptr to dest        */
strmove(                           /* move string w/overlapping src/dest   */
CHAR *dst,                         /*   destination                        */
const CHAR *src)                   /*   source                             */
{
#ifdef GCDOSP
     /* necessary because naughty galmovmem() returns void */
     memmove(dst,src,strlen(src)+1);
     return(dst);
#else
     return(memmove(dst,src,strlen(src)+1));
#endif // GCDOSP
}

CHAR *                             /*   copy of pointer to destination     */
makePath(                          /* combine directory and file name      */
CHAR *dst,                         /*   destination buffer                 */
const CHAR *dir,                   /*   directory name                     */
const CHAR *file,                  /*   file name                          */
size_t dstSiz)                     /*   size of destination buffer         */
{
     if (dir != NULL && dir != dst) {
          stlcpy(dst,dir,dstSiz);
     }
     if (dst[strlen(dst)-1] != SL) {
          stlcat(dst,SLS,dstSiz);
     }
     return(stlcat(dst,file,dstSiz));
}

ULONG                              /*   index or numElems if not found     */
binSearch(                         /* generic binary search utility        */
const VOID *target,                /*   target object                      */
const VOID *array,                 /*   array object                       */
ULONG numElems,                    /*   number of elements in array object */
binSrchComp compFunc)              /*   comparison function                */
{
     INT comp;
     LONG i;

     i=binFindNear(&comp,target,array,numElems,compFunc);
     if (comp == 0) {
          return(i);
     }
     return(numElems);
}

ULONG                              /*   index where element should be      */
binFindNear(                       /* generic binary find nearest utility  */
INT *lastComp,                     /*   result of last comparison          */
const VOID *target,                /*   target object                      */
const VOID *array,                 /*   array object                       */
ULONG numElems,                    /*   number of elements in array object */
binSrchComp compFunc)              /*   comparison function                */
{
     ULONG lo,md,hi;
     INT comp;

     comp=1;
     lo=md=0;
     if (numElems > 0) {
          hi=numElems-1;
          while (lo <= hi) {
               md=lo+(hi-lo)/2;
               if ((comp=(*compFunc)(target,array,md)) > 0) {
                    if (md == hi) {
                         ++md;
                         break;
                    }
                    lo=md+1;
               }
               else if (comp < 0) {
                    if (md == lo) {
                         break;
                    }
                    hi=md-1;
               }
               else {
                    break;
               }
          }
     }
     *lastComp=comp;
     return(md);
}

INT
hexval(                            /* get hexadecimal value from digit     */
CHAR dig)                          /*   hexadecimal digit to convert       */
{
     ASSERT(isxdigit(dig));
     if (isdigit((dig=toupper(dig)))) {
          return(dig-'0');
     }
     return((dig-'A')+10);
}

CHAR
hexdig(                            /* get hexadecimal value from digit     */
UINT val)                          /*   value to convert                   */
{
     CHAR digits[16]="0123456789ABCDEF";

     ASSERT(val < 16);
     return(digits[val]);
}

CHAR *                             /*   returns ptr to destination         */
getConfigStr(                      /* get string from configuration buffer */
const CHAR *sectName,              /*   section name (NULL for default)    */
const CHAR *itemName,              /*   item name (NULL for all)           */
const CHAR *dftVal,                /*   default value                      */
CHAR *dst,                         /*   destination buffer                 */
size_t dstSiz,                     /*   size of destination buffer         */
const CHAR *src)                   /*   configuration buffer               */
{
     size_t len;
     const CHAR *endp;

     ASSERT(sectName == NULL || strchr(sectName,']') == NULL);
     ASSERT(itemName == NULL || strchr(itemName,'=') == NULL);
     ASSERT(dftVal != NULL);
     ASSERT(dst != NULL);
     ASSERT(src != NULL);
     if ((src=findSect(sectName,src)) != NULL) {
          if (NULSTR(itemName)) {
               itemName=NULL;
          }
          if (itemName == NULL || (src=findItem(itemName,src)) != NULL) {
               endp=itemName == NULL ? nextSect(src) : nextItem(src);
               len=(endp == NULL ? strlen(src) : (size_t)(endp-src))+1;
               stlcpy(dst,src,min(dstSiz,len));
               return(unpad(dst));
          }
     }
     return(stlcpy(dst,dftVal,dstSiz));
}

GBOOL                              /*   returns TRUE if able to set        */
setConfigStr(                      /* set string in configuration buffer   */
const CHAR *sectName,              /*   section name (NULL for default)    */
const CHAR *itemName,              /*   item name (NULL for all)           */
const CHAR *value,                 /*   string to add (NULL to delete)     */
CHAR *buf,                         /*   configuration buffer               */
size_t bufSiz)                     /*   size of configuration buffer       */
{
     CHAR *sectStart,*sectEnd,*itemStart,*itemEnd;
     size_t bufLen,newItemLen,oldItemLen;

     ASSERT(sectName == NULL || strchr(sectName,']') == NULL);
     ASSERT(itemName == NULL || strchr(itemName,'=') == NULL);
     ASSERT(buf != NULL);
     ASSERT(strlen(buf) < bufSiz);
     if (NULSTR(sectName)) {
          sectName=NULL;
     }
     if (NULSTR(itemName)) {
          itemName=NULL;
     }
     if (NULSTR(value)) {
          if (itemName == NULL) {
               delSect(sectName,buf);
          }
          else {
               delItem(sectName,itemName,buf);
          }
          return(TRUE);
     }
     if (sectName == NULL) {
          sectStart=buf;
     }
     else if ((sectStart=findSectName(sectName,buf)) == NULL) {
          if (!addSect(sectName,buf,bufSiz)) {
               return(FALSE);
          }
          sectStart=buf+strlen(buf);
     }
     else if ((sectStart=nextItem(sectStart)) == NULL) {
          if ((bufLen=strlen(buf)) >= bufSiz-1) {
               return(FALSE);
          }
          sectStart=buf+bufLen;
          *sectStart++='\n';
          *sectStart='\0';
     }
     sectEnd=nextSect(sectStart);
     if (itemName == NULL) {
          itemStart=sectStart;
          itemEnd=sectEnd;
          newItemLen=strlen(value);
          ASSERT(newItemLen > 0);
          if (value[newItemLen-1] != '\n') {
               ++newItemLen;
          }
     }
     else {
          newItemLen=strlen(itemName)+1+strlen(value)+1;
          if ((itemStart=findItemName(itemName,sectStart)) == NULL) {
               itemStart=itemEnd=sectEnd;
          }
          else if ((itemEnd=nextItem(itemStart)) == NULL) {
               itemEnd=itemStart+strlen(itemStart);
          }
     }
     if (itemStart > buf && *(itemStart-1) != '\n') {
          ++newItemLen;
     }
     oldItemLen=(size_t)(itemEnd-itemStart);
     if (bufSiz <= (strlen(buf)-oldItemLen)+newItemLen) {
          return(FALSE);
     }
     strmove(itemStart+newItemLen,itemEnd);
     if (itemStart > buf && *(itemStart-1) != '\n') {
          *itemStart++='\n';
     }
     if (itemName == NULL) {
          memcpy(itemStart,value,newItemLen-1);
          ASSERT(newItemLen > 0);
          itemStart[newItemLen-1]='\n';
     }
     else {
          itemStart=stpcpy(itemStart,itemName);
          *itemStart++='=';
          itemStart=stpcpy(itemStart,value);
          *itemStart='\n';
     }
     return(TRUE);
}

LONG                               /*   returns value of item              */
getConfigInt(                      /* get integer from configuration buffer*/
const CHAR *sectName,              /*   section name                       */
const CHAR *itemName,              /*   item name                          */
LONG dftVal,                       /*   default value                      */
const CHAR *src)                   /*   configuration buffer               */
{
     CHAR numBuf[sizeof("-1234567890")],dftBuf[sizeof("-1234567890")];

     sprintf(dftBuf,"%ld",dftVal);
     return(atol(getConfigStr(sectName,itemName,dftBuf,numBuf,sizeof(numBuf),src)));
}

GBOOL                              /*   returns TRUE if able to set        */
setConfigInt(                      /* set integer in configuration buffer  */
const CHAR *sectName,              /*   section name                       */
const CHAR *itemName,              /*   item name                          */
LONG value,                        /*   integer to add                     */
CHAR *buf,                         /*   configuration buffer               */
size_t bufSiz)                     /*   size of configuration buffer       */
{
     CHAR numBuf[sizeof("-1234567890")];

     sprintf(numBuf,"%ld",value);
     return(setConfigStr(sectName,itemName,numBuf,buf,bufSiz));
}

GBOOL                              /*   returns TRUE if able to set        */
mergeConfig(                       /* merge two configuration buffers      */
CHAR *dst,                         /*   destination config buf (added to)  */
const CHAR *src,                   /*   source config (to be added)        */
size_t dstSiz)                     /*   size of destination buffer         */
{
     CHAR *cp,*curlin,*nxtlin,*sect,*item,*tmpsrc;

     curlin=tmpsrc=strdup(src);
     unpad(tmpsrc);
     sect=NULL;
     do {
          if ((nxtlin=nextItem(curlin)) != NULL) {
               *(nxtlin-1)='\0';
          }
          if (*curlin == '[') {
               sect=skpwht(curlin);
               if ((cp=strchr(sect+1,']')) == NULL) {
                    free(tmpsrc);
                    return(FALSE);
               }
               *cp='\0';
               unpad(sect);
          }
          else {
               item=skpwht(curlin);
               if ((cp=strchr(item+1,'=')) == NULL) {
                    free(tmpsrc);
                    return(FALSE);
               }
               *cp++='\0';
               unpad(item);
               cp=unpad(skpwht(cp));
               if (!setConfigStr(sect,item,cp,dst,dstSiz)) {
                    free(tmpsrc);
                    return(FALSE);
               }
          }
     } while ((curlin=nxtlin) != NULL);
     free(tmpsrc);
     return(TRUE);
}

CHAR *                             /*   start of sect or NULL if not found */
findSect(                          /* find a section in a config buffer    */
const CHAR *sectName,              /*   section name (NULL for default)    */
const CHAR *src)                   /*   configuration buffer               */
{
     const CHAR *tmps;

     if (!NULSTR(sectName)) {
          if ((src=findSectName(sectName,src)) != NULL) {
               if ((tmps=nextItem(src)) == NULL) {
                    src+=strlen(src);
               }
               else {
                    src=tmps;
               }
          }
     }
     return((CHAR *)src);
}

CHAR *                             /*   line containing section name       */
findSectName(                      /* find a section in a config buffer    */
const CHAR *sectName,              /*   section name (NULL for default)    */
const CHAR *src)                   /*   configuration buffer               */
{
     size_t namlen;
     const CHAR *tmps;

     if (!NULSTR(sectName)) {
          namlen=strlen(sectName);
          do {
               if (*src == '[') {
                    tmps=skpwht(src+1);
                    if (sameto(sectName,tmps) && *skpwht(tmps+namlen) == ']') {
                         break;
                    }
               }
          } while ((src=nextItem(src)) != NULL);
     }
     return((CHAR *)src);
}

CHAR *                             /*   start of value or NULL if not found*/
findItem(                          /* find item in a configuration section */
const CHAR *itemName,              /*   item name                          */
const CHAR *src)                   /*   start of configuration section     */
{
     if ((src=findItemName(itemName,src)) != NULL) {
          src=skpwht(strchr(src+strlen(itemName),'=')+1);
     }
     return((CHAR *)src);
}

CHAR *                             /*   line containing item               */
findItemName(                      /* find item in a configuration section */
const CHAR *itemName,              /*   item name                          */
const CHAR *src)                   /*   start of configuration section     */
{
     size_t namlen;

     namlen=strlen(itemName);
     do {
          if (*src == '[') {
               return(NULL);
          }
          if (sameto(itemName,src) && *skpwht(src+namlen) == '=') {
               break;
          }
     } while ((src=nextItem(src)) != NULL);
     return((CHAR *)src);
}

GBOOL                              /*   returns FALSE if not enough room   */
addSect(                           /* add a section name                   */
const CHAR *sectName,              /*   section name (NULL for default)    */
CHAR *buf,                         /*   configuration buffer               */
size_t bufSiz)                     /*   size of configuration buffer       */
{
     CHAR *cp;
     size_t orgLen;

     if ((orgLen=strlen(buf)) == 0) {
          cp=buf;
     }
     else if (*(cp=&buf[orgLen-1]) == '\n') {
          ++cp;
     }
     else if (++orgLen < bufSiz) {
          *++cp='\n';
          *++cp='\0';
     }
     if (bufSiz <= orgLen+strlen(sectName)+3) {
          return(FALSE);
     }
     *cp++='[';
     cp=stpcpy(cp,sectName);
     *cp++=']';
     *cp++='\n';
     *cp='\0';
     return(TRUE);
}

VOID
delSect(                           /* delete a configuration section       */
const CHAR *sectName,              /*   section name (NULL for default)    */
CHAR *buf)                         /*   configuration buffer               */
{
     CHAR *scp,*ecp;

     ASSERT(buf != NULL);
     if ((scp=findSectName(sectName,buf)) != NULL) {
          ecp=nextSect(scp+1);
          strmove(scp,ecp);
     }
}

VOID
delItem(                           /* delete a configuration item          */
const CHAR *sectName,              /*   section name (NULL for default)    */
const CHAR *itemName,              /*   item name                          */
CHAR *buf)                         /*   configuration buffer               */
{
     CHAR *scp,*ecp;

     ASSERT(!NULSTR(itemName));
     ASSERT(buf != NULL);
     if ((scp=findSect(sectName,buf)) != NULL) {
          if ((scp=findItemName(itemName,scp)) != NULL) {
               if ((ecp=nextItem(scp)) == NULL) {
                    *scp='\0';
               }
               else {
                    strmove(scp,ecp);
               }
          }
     }
}

CHAR *                             /*   ptr to start of next section       */
nextSect(                          /* find next section name in config buf */
const CHAR *str)                   /*   buffer to search                   */
{
     const CHAR *cp;

     do {
          if (*str == '[') {
               return((CHAR *)str);
          }
          cp=str;
     } while ((str=nextItem(cp)) != NULL);
     return((CHAR *)(cp+strlen(cp)));
}

CHAR *                             /*   ptr to first char on line or NULL  */
nextItem(                          /* get next line in configuration buffer*/
const CHAR *str)                   /*   buffer to search                   */
{
     return(nextLine(str,"\n"));
}

CHAR *                             /*   ptr to first char on line or NULL  */
nextLine(                          /* find next line in a string           */
const CHAR *str,                   /*   string to search                   */
const CHAR *eol)                   /*   end-of-line marker string          */
{
     if ((str=strstr(str,eol)) != NULL) {
          str+=strlen(eol);
     }
     return((CHAR *)str);
}
