/* Some of the code in this file was originally based on the following file:
 * gateway.c : Paul Healy, EI9GL, 900818
 *
 * Rewrote forwarding mechanism to use "X-Forwarded-To" paradigm instead of
 * "X-BBS-To", added timer support, etc.  Anders Klemets, SM0RGV, 901009.
 */
 /* Mods by G1EMM and WG7J */
#include <ctype.h>
#include <time.h>
#include "global.h"
#ifdef MBFWD
#include "bm.h"
#include "mailbox.h"
#include "smtp.h"
#include "cmdparse.h"
#include "proc.h"
#include "socket.h"
#include "timer.h"
#include "usock.h"
#include "netuser.h"
#include "ax25.h"
#include "netrom.h"
#include "nr4.h"
#include "files.h"
#include "index.h"
  
extern int MbForwarded;
extern char MboxId[];
#define ISPROMPT(s) (strlen(s) > 1 && s[strlen(s)-2] == '>')
static struct timer fwdtimer;
static struct proc *FwdProc;
  
static char *findident __ARGS((char *str, int n, char *result));
static char *fwdanybbs __ARGS((struct mbx *m,int *poll));
static int timeok __ARGS((char *line));
static void fwdtick __ARGS((void *v));
static void fwdproc __ARGS((void));
static int isconnbbs __ARGS((struct mbx *m));
static void startfwd __ARGS((int a,void *v1,void *v2));
static int openconn __ARGS((int argc,char *argv[],void *p));
static int sendmsgtobbs __ARGS((struct fwd *f,int msgn,char *dest));
static char *grabtext __ARGS((char *from,char *to,int marker));
static int16 calc_crc __ARGS((char *buf, unsigned int len, char *buf2));
  

/* Compute CCITT CRC-16 over a buffer */
static int16
calc_crc(buf,len,buf2)
char *buf,*buf2;
unsigned int len;
{
/* 16 bit CRC-CCITT stuff. Extracted from Bill Simpson's PPP */

#define FCS_START       0xffff  /* Starting bit string for FCS calculation */
#define FCS(fcs, c)     (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0x00ff])

    extern int16 fcstab[];
    int16 crc;

    crc = FCS_START;
    if(len == 0)
        len = strlen(buf);

    while(len--)
        crc = FCS(crc,*buf++);

    if (buf2 != NULLCHAR) {   /* include another buffer segment in crc? */
        len = strlen(buf2);
        while (len--)
            crc = FCS(crc,*buf2++);
    }
    crc ^= 0xffff;
    return crc;
}

/***************************************************************************
   findident copies the 'n'th alphanumeric sequence from 'str' to result.
   It returns a ptr to result. It returns "\0" for missing identifier etc.
   Uses isalnum macro to decide on alphanumeric/non-alnum status.
*/
static char *
findident(str, n, result)
char *str, *result;
int n;
{
    int count; /* current identifier */
    count = 0;
    *result = '\0';
    while ( (count<n) && (*str!='\0') ) { /* Process alnum or non alnum seq */
        while ( (*str!='\0') && (!isalnum(*str)) ) /* Get rid of ';:.@%"# etc */
            str++;
        if ( (*str!='\0') && isalnum(*str) ) { /* this is an alnum seq */
            count++;
            while ( (*str!='\0') && (isalnum(*str) || (*str=='_')) )
                if (count==n)
                    *result++ = *str++;
                else str++;
            if (count==n)
                *result = '\0';
        }
    }
    return result;
}
/**************************************************************************/
/* sendmsg() modified to send the R: line conditioned on Mbheader.
 * also added some additional strings like qth and zipcode etc. to R: line.
 * Original SMTP headers get forwarded optionally.  920114 - WG7J
 * N5KNX 2/95: if we can't send a complete msg, return code -1, otherwise
 * return code 0.
 */
extern char *Mbhaddress;
extern char *Mbfwdinfo;
extern char *Mbqth;
extern char *Mbzip;
extern int Mbsmtptoo;
extern int UtcOffset;
extern int Mbheader;
extern char shortversion[];

  
int
sendmsg(f,msgn)
struct fwd *f;
int msgn;
{
    struct mbx *m = f->m;
    int i, rheader=0, result=0;
    long start;
    char buf[LINELEN], tb[LINELEN], *cp;
  
    /* If the data part of the message starts with "R:" the RFC-822
     * headers will not be forwarded. Instead we will add an R:
     * line of our own.
     */
    /* Forward with our "R:" line conditional upon Mbheader - 921201, WG7J */
    if(Mbheader) {
        /* First send recv. date/time and bbs address */
        usprintf(m->user,"R:%s",mbxtime(f->ind.mydate));
        /* If exists, send H-address */
        if(Mbhaddress != NULLCHAR)
            usprintf(m->user," @:%s",Mbhaddress);
        /* location, if any */
        if(Mbqth != NULLCHAR)
            usprintf(m->user," [%s]",Mbqth);
        /*if there is info, put it next */
        if(Mbfwdinfo != NULLCHAR)
            usprintf(m->user," %s",Mbfwdinfo);
        /* number of the message */
        usprintf(m->user," #:%lu",(f->ind.msgid % 100000L));  /* n5knx: use rightmost 5 digits */
        /* The BID, is any */
        if(f->bid[0] != '\0')
            usprintf(m->user," $:%s",&f->bid[1]);
        /* zip code of the bbs */
        if(Mbzip != NULLCHAR)
            usprintf(m->user," Z:%s",Mbzip);
        usputc(m->user,'\n');
    }
  
    /* Open the mailbox file */
    sprintf(buf,"%s/%s.txt",Mailspool,m->area);
    if((m->mfile = fopen(buf,READ_TEXT)) == NULLFILE) goto early_quit;
  
    /* point to start of this message in file */
    start = m->mbox[msgn].start;
    fseek(m->mfile,start,SEEK_SET);
    if (ferror(m->mfile)) goto early_quit;

    /* If we also send the smtp headers, now see if the message
     * has any R: headers. If so, send them first.
     */
    if(Mbsmtptoo) {
        while(fgets(buf,sizeof(buf),m->mfile) != NULLCHAR) {
            if(*buf == '\n')
                break;          /* End of smtp headers */
        }
        if(feof(m->mfile) || ferror(m->mfile)) goto early_quit;
        /* Found start of msg text, check for R: lines */
        while(fgets(buf,sizeof(buf),m->mfile) != NULLCHAR &&
        !strncmp(buf,"R:",2)) {
            rheader = 1;
            usputs(m->user,buf);
        }
        /* again point to start of this message in file */
        fseek(m->mfile,start,SEEK_SET);
        if(ferror(m->mfile)) goto early_quit;
    }
  
    /* Go past the SMTP headers to the data of the message.
     * Check if we need to forward the SMTP headers!
     * 920114 - WG7J
     */
    if(Mbsmtptoo && (rheader || Mbheader))
        usputc(m->user,'\n');
    i = NOHEADER;
    while(fgets(buf,sizeof(buf),m->mfile) != NULLCHAR && *buf != '\n') {
        if(Mbsmtptoo) {
            /* YES, forward SMTP headers TOO !*/
            switch(htype(buf, &i)) {
                case XFORWARD: /* Do not forward the "X-Forwarded-To:" lines */
                case STATUS:   /* Don't forward the "Status:" line either */
                case BBSTYPE:
                case SUBJECT:
                case TO:
                case APPARTO:
                case CC:
                case DATE:
                    break;
                case FROM:
                /* Don't forward the "From: " line either.
                 * make it ">From: "
                 */
                    usputc(m->user,'>');
                /*note fall-through*/
                default:
                    if(!strncmp(buf,"From ",5))
                        usputc(m->user,'>');
                    usputs(m->user,buf);
            }
        }
    }
    if(feof(m->mfile) || ferror(m->mfile)) goto early_quit;
  
    /* Now we are at the start of message text.
     * the rest of the message is treated below.
     * Remember that R: lines have already been sent,
     * if we sent smtp headers !
     */
    i = 1;
  
    while(fgets(buf,sizeof(buf),m->mfile) != NULLCHAR &&
    strncmp(buf,"From ",5)) {
        if(i) {
            if(!strncmp(buf,"R:",2)) {
                if(Mbsmtptoo) continue;
            } else {
                i = 0;
                if(*buf != '\n')
                    /* Ensure body is separated from R: line */
                    usputc(m->user,'\n');
            }
        }
        usputs(m->user,buf);
    }
    if(feof(m->mfile)) clearerr(m->mfile);  /* only place EOF is acceptable */

early_quit:
    if(m->mfile == NULLFILE || ferror(m->mfile) || feof(m->mfile) ||
        (ftell(m->mfile)-start < m->mbox[msgn].size)) {
#ifdef JPDEBUG
        log(m->user,"forward aborted: truncated %s msg %d would result from err %d",m->area,msgn,errno);
#endif
        usputs(m->user,"\n*** Cannot find complete message body!\n");
        usflush(m->user);  /* in case disconnect still yields a truncated msg */
#if defined(MAILERROR) && defined(JPDEBUG)
        mail_error("MBX FWD %s: Cannot find body for %s msg %d", \
            m->name,m->area,msgn);
#endif
/* OK, we were unable to send a complete msg body.  We rely on returning an
   error code to result in a disconnect rather than a completed msg.
*/
        m->user = -1;  /* this will fail subsequent recvline(m->user...) calls */
        result = -1;   /* this will prevent a /ex or ^Z from being sent */
    }

    fclose(m->mfile);
    m->mfile = NULL;
    return result;
}
  
/* Parse a line for date and time in Arpanet format
 * (Day, day Month year hh:mm:ss Zone) and return it in mailbox format
 * (yymmdd/hhmmz)
 */
char *
mbxtime(time_t date) {
    time_t cdate;   /* Date corrected for timezone offset */
    extern char *Months[];
    static char buf[13];
    char *cp;
    int month;
  
    cdate = date;
    /* adjust for GMT/UTC time - WG7J */
    if(UtcOffset != 0)
        cdate -= UtcOffset*3600L;
    cp = ctime(&cdate);
  
    /* Check month */
    for(month=0; month < 12; ++month)
        if(strnicmp(Months[month],cp+4,3) == 0)
            break;
    if(month == 12)
        return NULL;
    month++;
  
    sprintf(buf,"%2.2s%02d%2.2s/%2.2s%2.2sz",cp+22,month,cp+8,cp+11,cp+14);
    return buf;
}
  
static char *
grabtext(from, to, marker)
char *from, *to;
int marker;
{
    while (*from!=marker)
        *to++ = *from++;
    *to = '\0';
    return from+1;
}
  
/* Makes a command line and returns -1 if the message cannot be sent */
int
makecl(f, msgn, dest, line, subj, bul)
struct fwd *f;
int msgn;               /* Message number */
char *dest;             /* Destination address to use instead of To: line */
char *line, **subj;      /* Buffers to keep command line and subject */
int *bul;       /* True if message is in public message area */
{
    struct mbx *m = f->m;
    struct let *cmsg = &m->mbox[msgn];
    struct mailindex *ind = &f->ind;
    struct fwdbbs *bbs;
    int bulletin = *bul;
    int foundbid = 0;
    char *cp, *tmp;
    char *to,*atbbs,*from;
    char bid[60];
#ifdef FBBFWD
    char bid2[25];   /* room for atol(LONG_MAX,10) */
#endif
  
    if((cmsg->status & BM_HOLD) || (!bulletin && (cmsg->status & BM_READ)))
        return -1;      /* the message was on hold or already read */
  
    /* The following code tries to parse the "To: " line where the
     * address looks like "to", "to@atbbs", "to%atbbs@host" or
       (UUCP-style) "...!athost!toid"
     */
    if(dest != NULL && *dest != '\0')
        to = strdup(dest);           /* use replacement destination */
    else
        to = strdup(ind->to);
    if((atbbs = strchr(to,'%')) != NULL) {
        *atbbs++ = '\0';    /* "to" ends at the '%' character */
        /* Now get rid of the following '@host' field */
        if((cp = strchr(atbbs,'@')) != NULL)
            *cp = '\0';
        atbbs=strdup(atbbs);
    }
    if((cp = strchr(to,'@')) != NULL) {
        *cp = '\0';   /* "to" ends at the '@' character */
        if(!atbbs)
            atbbs = strdup(cp + 1);
    }
    else if((cp = strrchr(to,'!')) != NULL) {  /* DL1BJL (but @ takes precedence) */
        tmp = cp + 1;
        *cp = '\0';
        free(atbbs);
        atbbs = strdup(((cp = strrchr(to, '!')) != NULL) ? cp + 1 : to);
        tmp=strdup(tmp);
        free(to);
        to = tmp;
    }
  
    /* "to" or "atbbs" should not be more than 6 characters (ALEN).
     * If "to" is too long, it might simply be because the area name
     * is longer than 6 characters, but it might also be because
     * the address on the To: line is in an obscure format that we
     * failed to parse.
     */
    if(strlen(to) > ALEN) {
        /* Play safe and set "to" and "atbbs" to the area name */
        free(to);
        to = strdup(m->area);
        free(atbbs);
        atbbs = strdup(m->area);
        if(strlen(to) > ALEN)
            to[ALEN] = '\0';    /* Maximum length is 6 */
    }

    /* Only if the BBS supports "hierarchical routing designators"
     * is the atbbs field allowd to be longer than 6 characters and
     * have dots in it.
     */
    if((m->sid & MBX_HIER_SID) == 0) {
        if(atbbs && strlen(atbbs) > ALEN)
            atbbs[ALEN] = '\0';    /* 6 character limit */
        if(atbbs && (cp = strchr(atbbs,'.')) != NULLCHAR)
            *cp = '\0';       /* cut "atbbs" at first dot */
    }
  
    from = ind->from;
    if(from) {
        if((cp=strchr(from,'@')) != NULL)
            *cp = '\0';
        if((cp=strchr(from,'%')) != NULL)
            *cp = '\0';
        if((cp=strrchr(from,'!')) != NULL)
            from = cp + 1;
        if(strstr(from,m->name) != NULL) {
            /* This message came from the connected BBS so Abort */
            free(to);
            free(atbbs);
            return -1;
        }
        if(strlen(from) > ALEN)
            *(from+ALEN) = '\0';      /* 6 character limit */
    }
  
    if(*to == '\0' || *from == '\0') {
        free(to);
        free(atbbs);
        return -1;
    }
  
    /* The following code distinguishes between three different types
     * of Message-IDs: abcde@callsign.bbs, abcde@otherhost.domain,
     * and abcde@ourhost.domain.
     * The first type is converted to $abcde and the second to
     * $abcde_host.domain. The last to $abcde_first-part-of-H-address.
     * This preserves compability with BBSes.
     * 2/95 N5KNX: in ourhost.domain case, use msgid%100000 for bid #
     * 6/95 N5KNX: use <from> not our Hostname after the CRC in some bids.
     */
    f->bid[0] = '\0';
    strncpy(&bid[1],ind->messageid,sizeof(bid) - 1);
    bid[sizeof(bid)-1] = '\0';
    /* Is there a @hostname part ? */
    if((cp = strchr(&bid[1],'@')) != NULL) {
        bid[0] = '$';
        if(stricmp(cp+strlen(cp) - 4, ".bbs") == 0) {
            /*retain the bid given by user*/
            *cp = '\0';
            foundbid = 1; /* Indicate we found a 'real' bid - WG7J */
        } else {
            if(strcmp(cp+1,Hostname)) {
                /* This is NOT one of our messages!  The Message-ID is quite
                 * likely too long to work as an AX.25 bid, so we'll
                 * use a CRC value computed from the Message-ID and ToAddr,
                 * then take the userid part from the from address to form our bid.
                 * This eliminates dups when multiple Jnos systems send the same
                 * smtp msg out as a bull.  We must save the bid into Historyfile so
                 * we don't see dups ourselves.
                 *  - KF5MG/N5KNX
                 */
                FILE *fp;
                long now;
                int locked = 0;
                int16 crc = calc_crc(bid,0,to);

                sprintf(bid, "$%u_%s", crc, from /* was: (Mbhaddress)?Mbhaddress:Hostname */);

                if (msgidcheck(&bid[1]) == 0)  /* bid not on file */
                    for (locked=30; locked>0; locked--) {  /* try to lock bid History file */
                        if(!mlock(Historyfile,NULLCHAR)) break;
                        pause(2000L);
                    }
                if (locked) {
                    if ((fp = fopen(Historyfile,APPEND_TEXT)) != NULL) {
                        time(&now);
                        fprintf(fp,"%s %ld\n",&bid[1],now); /* Save BID */
                        fclose(fp);
                    }
                    (void)rmlock(Historyfile,NULLCHAR);
                }
            }
            else {  /* we could use msgid%100000L, but it's not unique for multiple-To-addr case */
                *cp = '\0';
                cp -= 5;  /* backup at most 5 digits */
                if (cp < &bid[1]) cp = &bid[1];
                sprintf(bid, "$%s_%s", cp, (Mbhaddress)?Mbhaddress:Hostname);  /* tricky overlap */
            }
            cp = bid;

            /* This is now either $msg#_my_h-address, $msg#_myhostname, or $crc#_fromadr
             * Make this BID style '$msg#_host'
             * ie. cut off after first period - WG7J
             */
            if((cp = strchr(cp,'.')) != NULLCHAR)
                *cp = '\0';
        }
        bid[13] = '\0';     /* BIDs should be no longer than 13 bytes (includes $)*/
        strcpy(f->bid,bid);
    }
  
    if(line != NULL) {
#ifdef FBBCMP
        if(m->sid & MBX_FBBCMP) {
           if(atbbs)
              sprintf(line, "FA %c %s %s %s ", ind->type, from, atbbs, to);
           else
#ifdef use_local
              sprintf(line, "FA %c %s %s %s ", ind->type, from, "local", to);
#else
              sprintf(line, "FA %c %s %s %s ", ind->type, from, m->name, to);
#endif
        } else
#endif
#ifdef FBBFWD
        if(m->sid & MBX_FBBFWD) {
           if(atbbs)
              sprintf(line, "FB %c %s %s %s ", ind->type, from, atbbs, to);
           else
#ifdef use_local
              sprintf(line, "FB %c %s %s %s ", ind->type, from, "local", to);
#else
              sprintf(line, "FB %c %s %s %s ", ind->type, from, m->name, to);
#endif
        } else
#endif
        if(atbbs)
            sprintf(line, "S%c %s @ %s < %s ", ind->type, to, atbbs, from);
        else
            sprintf(line, "S%c %s < %s ", ind->type, to, from);

        /* Add the bid to bulletins,
         * AND ALSO to anything that came in with a bid !
         * Takes care of duplicate 'SP SYSOP@xxx $BID' problems.
         * ALSO add it to ALL messages if the remote system supports MID's - WG7J
         */
        if((m->sid & MBX_MID) || ((bulletin || foundbid) & (m->sid & MBX_SID)))
#ifdef FBBFWD
            if(m->sid & MBX_FBBFWD)
                strcat(line,&f->bid[1]);
            else
#endif
            strcat(line,f->bid);
#ifdef FBBFWD
       if(m->sid & MBX_FBBFWD) {
          strcat(line," ");
          strcat(line,ltoa(ind->size,bid2,10));
       }
#endif
        strcat(line,"\n");
    }
    if(subj)
        *subj = strdup(ind->subject);
    free(to);
    free(atbbs);
    return 0;
}
  
static int /* 0 = ok, -1 = problem so disc */
sendmsgtobbs(f, msgn, dest)
struct fwd *f;
int msgn;
char *dest;             /* Optional destination address to override To: line */
{
    int result = -1;
    struct mbx *m = f->m;
    struct fwdbbs *bbs;
    int bulletin = (m->areatype == AREA);
    char *subj = NULL;
    char line[64];
  
    /* Check x-forwarded-to fields */
    for(bbs=f->ind.bbslist;bbs;bbs=bbs->next)
        if(!stricmp(bbs->call,m->name))
            return 0;
  
    if(makecl(f, msgn, dest, line, &subj, &bulletin) == -1)
        return 0;       /* do not forward this particular message */
  
    tputs(line);          /* Send mail offer to bbs */
/*    rip(line); */
    usflush(m->user);
    if(recvline(m->user, m->line, MBXLINE) != -1 ) {
        if(m->line[0] == 'O' || m->line[0] == 'o' || (m->sid & MBX_SID) == 0) {
            /* Got 'OK' or any line if the bbs is unsofisticated */
            tprintf("%s\n", subj);
            if (sendmsg(f,msgn) != -1)   /* send the message */
#ifdef FWDCTLZ
                /* Some bbs code doesn't like /EX too well... */
                tputs("\032\n");
#else
                tputs("/EX\n"); /* was 0x1a */
#endif
            usflush(m->user);
            /* get F> for a good deliver */
            while (recvline (m->user, m->line, MBXLINE) != -1 ) {
                if(ISPROMPT(m->line)) {
                    rip(line);  /* N5KNX: now drop NL for nicer log entry */
                    log(m->user,"MBOX bbs mail sent: %s ", line);
                    if(m->areatype == AREA)
                        m->mbox[msgn].status |= BM_FORWARDED;
                    else
                        m->mbox[msgn].status |= BM_DELETE;
                    m->change = 1;
                    result = 0;
                    MbForwarded++;
                    break;
                }
            }
        } else { /* OK response not received from bbs */
            if (m->line[0] == 'N' || m->line[0] == 'n') { /* 'NO' respone */
                rip(m->line);   /* N5KNX: nicer log entry */
                log(m->user,"MBOX bbs mail refused: %s\t%s",line,m->line);
                /* Mark refused message as forwarded if it is a bulletin.
                 * The message was probably a duplicate. Non-bulletin
                 * messages are sent without BID, so they cannot be dected
                 * as duplicates. The reason why it was refused is probably
                 * because the address was invalid. Retry later.
                 *
                 * After lots of complaining, this behaviour is changed:
                 * ALL messages that get the NO reply are marked
                 * as forwarded ! - WG7J 930124
                 */
                if(m->areatype == AREA)
                    m->mbox[msgn].status |= BM_FORWARDED;
                else
                    m->mbox[msgn].status |= BM_DELETE;
                m->change = 1;
                /* Count this as forwarded ! - WG7J */
                MbForwarded++;
            }
            /* should get a F> here */
            while (recvline (m->user, m->line, MBXLINE) != -1 ) {
                if (ISPROMPT(m->line)) {
                    result = 0;
                    break;
                }
            }
        }
    } /* OK or NO here */
    free(subj);
    return result;
}
  
int FwdUsers;
void exitfwd(struct mbx *m) {
    FwdUsers--;
    if(m->state != MBX_TRYING) log(m->user,"MBOX fwd exit: %s",m->name); /* N5KNX: log exits */
    exitbbs(m);
}


/* This is the main entry point for reverse forwarding. It is also used
 * for normal, "forward", forwarding.
 */
int
dorevfwd(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    char *cp, *dp;
    int i, idx, err = 0;
    struct fwd f;
    struct indexhdr hdr;
    struct fwdbbs *bbs;
    char fn[FILE_PATH_SIZE];
    char oldarea[64];
  
    long before,after;
  
    f.m = (struct mbx *)p;
    memset(&f.ind,0,sizeof(struct mailindex));
  
    log(f.m->user,"MBOX forwarding mail to: %s ", f.m->name);
    /* indicate we are doing reverse forwarding, if we are not already
     * doing normal forwarding.
     */
    if(f.m->state != MBX_FORWARD)
        f.m->state = MBX_REVFWD;
  
    if(fwdinit(f.m) != -1) {
        strcpy(oldarea,f.m->area);
        while(!err && fgets(f.m->line,MBXLINE,f.m->tfile) != NULLCHAR) {
            pwait(NULL);
            if(*f.m->line == '-')     /* end of record reached */
                break;
            cp = f.m->line;
            rip(cp);           /* adds extra null at end */
            /* skip spaces */
            while(*cp && (*cp == ' ' || *cp == '\t'))
                cp++;
            if(*cp == '\0' || *cp == '.' || *cp == '#' ||
               *cp == '+'  || *cp == '&' || *cp == '@')
                continue;	/* ignore empty or connect-script lines */
            /* find end of area name, and beginning of optional destination string */
            for (dp=cp; *dp && *dp != ' ' && *dp != '\t' && *dp != '\n'; dp++) ;
            if (*dp) *dp++ = '\0';
            changearea(f.m,cp);
            /* Now create the index filename */
            sprintf(fn,"%s/%s.ind",Mailspool,cp);
  
            /* strip leading blanks from dest */
            cp=dp;
            while(*cp && (*cp == ' ' || *cp == '\t'))
                cp++;
            /* find end of optional destination */
            for (dp=cp; *dp && *dp != ' ' && *dp != '\t' && *dp != '\n'; dp++) ;
            if (*dp) *dp = '\0';

            cp = strdup(cp);
            /* open the index file */
            if((idx=open(fn,READBINARY)) != -1) {
                /* check if there are any messages in this area
                 * that need to be forwarded.
                 */
                if(read_header(idx,&hdr) != -1) {
                    for(i=1; i<=f.m->nmsgs; i++) {
                        pwait(NULL);
                        if(read_index(idx,&f.ind) == -1) {
                            err = 1;
                            break;
                        }
                        if(sendmsgtobbs(&f, i, cp) == -1) {
                            err = 1;        /* abort */
                            break;
                        }
                        /* Done with this index, clear it */
                        default_index(f.m->area,&f.ind);
                        scanmail(f.m);
                    }
                }
                close(idx);
                idx = 0;
            }
            if(f.m->mfile) {
                fclose(f.m->mfile);
                f.m->mfile = NULL;
            }
            free(cp);
        }
        fclose(f.m->tfile);
        f.m->tfile = NULLFILE;
        if(*oldarea != '\0')
            changearea(f.m,oldarea);
    }
    default_index("",&f.ind);
    if(f.m->state == MBX_FORWARD)
        return 0;
    tputs("*** Done\n");
#ifdef notdef
    /* Can't do this with polling anymore */
    if((f.m->sid & MBX_RLI_SID))      /* disconnect if it is a W0RLI bbs */
        return domboxbye(0,NULL,f.m);
#endif
    return 0;
}
  
/* Read the forward file for a record for the connected BBS. If found,
 * return 1 if this is the right time to forward, m->tfile is left pointing
 * at the first message area to be forwarded.
 */
int
fwdinit(m)
struct mbx *m;
{
    char host[80];
    int start = 1;
#if defined(EXPIRY) && defined(FBBFWD)
extern int Eproc;

    if (Mfbb && (m->sid & MBX_FBBFWD) && Eproc)
        return -1;  /* don't FBB-forward while expire is running */
                    /* since our saved group of 5 indices is unreliable */
#endif /* EXPIRY && FBBFWD */

    if((m->tfile = fopen(Forwardfile,READ_TEXT)) == NULLFILE)
        return -1;
  
    while(fgets(m->line,MBXLINE,m->tfile) != NULLCHAR) {
        if(*m->line == '\n')
            continue;
        /* lines starting with '-' separate the forwarding records */
        if(*m->line == '-') {
            start = 1;
            continue;
        }
        if(start) {
            start = 0;
            /* get the name of this forwarding record */
            findident(m->line,1,host);
            if(stricmp(m->name,host) == 0) {
                if(!timeok(m->line))
                    continue;  /* N5KNX: too early or late.  Continue scan, */
                         /* thus allowing multiple time-differentiated entries for same host */
                /* eat the connect command line */
                fgets(m->line,MBXLINE,m->tfile);
                return 0;
            }
        }
    }
    fclose(m->tfile);
    m->tfile = NULLFILE;
    return -1;
}
/* Read the forward file for a record for the connected BBS. If found,
 * determine if this is the right time to forward, and return the command
 * line to establish a forwarding connection. m->tfile is left pointing
 * at the first message area to be forwarded.
 */
static char *
fwdanybbs(m,poll)
struct mbx *m;
int *poll;
{
    char host[80];
    int start = 1,i;
  
    if(m->tfile == NULLFILE && (m->tfile = fopen(Forwardfile,READ_TEXT))
        == NULLFILE)
        return NULLCHAR;
    *poll = 0;  /* Default to no polling */
    while(fgets(m->line,MBXLINE,m->tfile) != NULLCHAR) {
        if(*m->line == '\n')
            continue;
        /* lines starting with '-' separate the forwarding records */
        if(*m->line == '-') {
            start = 1;
            continue;
        }
        if(start) {
            start = 0;
            /* get the name of this forwarding record */
            findident(m->line,1,host);
            strcpy(m->name,host);
            if(!timeok(m->line))
                continue;       /* too late or too early */
            /* Check for polling - WG7J */
            i=2;
            findident(m->line,2,host);
            while(*host) {
                if(*host == 'P' || *host == 'p') {
                    *poll = 1;
                    break;
                }
                i++;
                findident(m->line,i,host);
            }
            /* get the connect command line */
            fgets(m->line,MBXLINE,m->tfile);
            return strdup(m->line);
        }
    }
    fclose(m->tfile);
    m->tfile = NULLFILE;
    return NULLCHAR;
}
  
/* get any groups of four digits that specify the begin and ending hours of
 * forwarding. Returns 1 if forwarding may take place.
 */
static int
timeok(line)
char *line;
{
    char hours[80], *now;
    long t;
    int t1, t2, pos = 2;
    findident(line,pos++,hours);
    if(*hours == '\0' || *hours == 'P' || *hours == 'p')
        return 1;       /* no digits default to 0023, ie. anytime */
    time(&t);
    now = ctime(&t) + 11;
    *(now + 2) = '\0';
    while(*hours != '\0') {
        t1 = (*hours - '0') * 10 + (*(hours+1) - '0');
        t2 = (*(hours+2) - '0') * 10 + (*(hours+3) - '0');
        if(atoi(now) >= t1 && atoi(now) <= t2)
            return 1;               /* right in time */
        findident(line,pos++,hours);    /* get next group if any */
        if(*hours == 'P' || *hours == 'p')
            return 0;
    }
    return 0;       /* too early or too late */
}
  
int
dombtimer(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    if(argc < 2){
        tprintf("Forwarding timer: %lu/%lu server %s.\n",
        read_timer(&fwdtimer)/1000L,
        dur_timer(&fwdtimer)/1000L,
        FwdProc != NULLPROC ? "started":"stopped");
        return 0;
    }
    fwdtimer.func = (void (*)__ARGS((void *)))fwdtick;/* what to call on timeout */
    fwdtimer.arg = NULL;            /* dummy value */
    set_timer(&fwdtimer,atol(argv[1])*1000L); /* set timer duration */
    pwait(NULL);
    if (FwdProc != NULLPROC)    /* if someone is listening */
        start_timer(&fwdtimer);     /* fire it up */
    else
        if(dur_timer(&fwdtimer) != 0)
            tputs("Warning: forward server not started.\n");
    return 0;
}
  
int
dombkick(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    if (FwdProc == NULLPROC) {
        tputs("Forward server not started\n");
        return 1;
    }
    psignal(&fwdtimer,0);
    return 0;
}
  
/* called when the forward timer expires or explicitly by dombkick() */
/* MDMII: fwdproc is the old fwdtick.   But, since it can call pause, which is
 * very very bad for timer functions :-( this has been converted to a server.
 */
static void
fwdtick(v)
void *v;
{
    psignal(&fwdtimer,0);           /* awake the forwarder */
    start_timer(&fwdtimer);     /* and restart the timer */
}
  
/* the main process for the mailbox forwarder */
int
fwdstart(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    if (FwdProc != NULLPROC)
        return 0;       /* already started */
  
    FwdProc = Curproc;      /* set our flag */
  
    psignal(Curproc,0);     /* don't wait on us */
  
    start_timer(&fwdtimer);     /* start timer (ignored if 0) */
  
    for (;!pwait(&fwdtimer);)   /* wait for someone to tell us to try */
        if (availmem() > Memthresh)
            fwdproc();
        else if(Mtrace)
            tputs("fwd: forwarding skipped due to low memory\n");
  
    FwdProc = NULLPROC;     /* we are exiting */
    return 0;           /* alerted from somewhere */
}
  
/* (attempt to) kill the forwarder process */
int
fwd0(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    int i, max;         /* max attempts */
  
    stop_timer(&fwdtimer);      /* no more timer awakes */
    max = 1;            /* Maximum attempts */
    if (argc > 2)
        setint(&max,NULLCHAR,argc,argv);
  
    for(i=0;i<max && FwdProc != NULLPROC;i++) {
        alert(FwdProc,1);   /* signal regardless of location */
        pwait(NULL);        /* let it see the alert */
    }
  
    stop_timer(&fwdtimer);      /* in case timer tick restarted it */
    return 0;
}
  
/* MDMII: This is the old fwdtick function */
static void
fwdproc(void)
{
    char *cc, *cp, *fp;
    struct mbx *m;
    struct fwdbbs *bbs;
    struct indexhdr hdr;
    struct mailindex ind;
    int i, idx, bulletin, poll,skip = 0;
    char fn[FILE_PATH_SIZE];
  
    if(Mtrace)
        tputs("mbox: fwd started\n");
    if((m = newmbx()) == NULLMBX){
        if(Mtrace)
            tputs("fwd: no new mbox\n");
        return;
    }
    m->user = Curproc->output;
    m->state = MBX_TRYING;
    memset(&ind,0,sizeof(struct mailindex));
    while((cc = fwdanybbs(m,&poll)) != NULLCHAR) {
        if(isconnbbs(m)) { /* already connected to this BBS, skip it */
            skip = 1;
            if(Mtrace)
                tprintf("fwd: %s already connected\n",m->name);
        }
        /* If we poll, there is no need to check message area, since this
         * is also done later. It will speed things up here - WG7J
         */
        if(!skip && poll) {
            if(Mtrace)
                tprintf("fwd: polling %s\n",m->name);
            newproc("Mbox forwarding", 2048,startfwd, 0, (void *)cc,
            (void *)strdup(m->name),0);
            cc = NULLCHAR;
            skip = 1;
        }
        if(!skip && Mtrace)
            tprintf("fwd: %s - checking for messages\n",m->name);
        while(fgets(m->line,MBXLINE,m->tfile) != NULLCHAR) {
            pwait(NULL);
            if(*m->line == '-') {   /* end of record reached */
                skip = 0;
                break;
            }
            if((cp = strchr(m->line,' ')) != NULLCHAR)
                *cp = '\0';
            if((cp = strchr(m->line,'\t')) != NULLCHAR)
                *cp = '\0';
            if(skip || *m->line == '\0' || *m->line == '#' || *m->line == '.' || \
                *m->line == '+' || *m->line == '@' || *m->line == '&')
                continue;
            rip(m->line);
            bulletin = isarea(m->line);     /* public area */
            sprintf(fn,"%s/%s.ind",Mailspool,m->line);
            if((idx=open(fn,READBINARY)) != -1) {
                /* check if there are any messages in this area
                 * that need to be forwarded.
                 */
                if(read_header(idx,&hdr) == -1)
                    hdr.msgs = 0;
                for(i=1; i<=hdr.msgs; i++) {
                    pwait(NULL);
                    /* Done with this index, clear it */
                    default_index("",&ind);
                    if(read_index(idx,&ind) == -1)
                        break; /* Should not happen ! */
                    /* Apply same tests as in makecl() */
                    if(ind.status & BM_HOLD) continue;
                    if(bulletin) {
                        for(bbs = ind.bbslist;bbs;bbs=bbs->next)
                            if(!stricmp(bbs->call,m->name))
                                break;
                        if(bbs)
                            continue;
                    } else if(ind.status & BM_READ)
                        continue;
                    if(ind.from) {    /* Don't fwd back to originator */
                        fp=strdup(ind.from);
                        if((cp=strchr(fp,'@')) != NULL)
                            *cp = '\0';
                        if((cp=strchr(fp,'%')) != NULL)
                            *cp = '\0';
                        if((cp=strrchr(fp,'!')) != NULL)
                            cp++;
                        else cp = fp;
                        if(strstr(cp,m->name) != NULL) {
                            free(fp);
                            continue;
                        }
                        free(fp);
                    }
                    if(Mtrace)
                        tprintf("fwd: starting %s (%s,#%d)\n",m->name, m->line, i);
                    newproc("Mbox forwarding", 2048,
                    startfwd, 0, (void *)cc,
                    (void *)strdup(m->name),0);
                    skip = 1;
                    cc = NULLCHAR;
                    break;
                }
                /* Done with this index, clear it */
                default_index("",&ind);
                close(idx);
            }
        }
        free(cc);
    }
    default_index("",&ind);
    usesock(Curproc->output);   /* compensate for close_s() in exitbbs */
    exitbbs(m);
}
  
/* returns 1 if m->name matches the name of another connected mailbox. */
static int
isconnbbs(m)
struct mbx *m;
{
    struct mbx *mp;
  
    for(mp=Mbox;mp;mp=mp->next)
        if((stricmp(mp->name,m->name) == 0) && (mp->state != MBX_TRYING) )
            return 1;
    return 0;
}
  
/* possible commands on the command line in the forwarding file */
static struct cmds cfwdcmds[] = {
    "tcp",          openconn,       0, 0, NULLCHAR,
    "telnet",       openconn,       0, 0, NULLCHAR,
#ifdef AX25
    "ax25",         openconn,       0, 0, NULLCHAR,
    "connect",      openconn,       0, 0, NULLCHAR,
#endif
#ifdef NETROM
    "netrom",       openconn,       0, 0, NULLCHAR,
#endif
    NULLCHAR
};
  
  
/* this function is called whenever the forwarding timer expires */
static void
startfwd(a,v1,v2)
int a;
void *v1, *v2;
{
    struct mbx *m;
    char *cc;
    int32 timeout;
    int rval;
    char *cp;
    char Continue[MBXLINE];
  
    cc = (char *) v1;
    if((m = newmbx()) == NULLMBX) {
        free(cc);
        free((char *)v2);
        return;
    }
    FwdUsers++;
    strcpy(m->name,(char *)v2);
    free((char *)v2);
    m->state = MBX_TRYING;
    /* open the connection, m->user will be the new socket */
    if(cmdparse(cfwdcmds,cc,(void *)m) == -1) {
        free(cc);
        usesock(Curproc->output);   /* compensate for close_s() in exitbbs */
        exitfwd(m);
        return;
    }
    free(cc);
    m->state = MBX_FORWARD;
    sockowner(m->user,Curproc);
    close_s(Curproc->output);
    close_s(Curproc->input);
    /* m->user will be closed automatically when this process exits */
    Curproc->output = Curproc->input = m->user;
    /* We'll do our own flushing right before we read input */
    setflush(m->user,-1);
  
    if(fwdinit(m) == -1) {
        /* it is probably not the right time to forward anymore */
        exitfwd(m);
        return;
    }
    /* read the connect script. Lines starting with a dot will be sent
     * to the remote BBS.
     */
    Continue[0] = '\0';
    while((cp=fgets(m->line,MBXLINE,m->tfile)) != NULLCHAR) {
        /* Expanded to do timeouts, and return string recognition - WG7J */
        switch(*m->line) {
            case '.':               /* send this line */
                tputs(m->line + 1);
                if(Mtrace)
                    printf("fwd: %s > %s\n",m->name,m->line+1);
                Continue[0] = '\0';         /* reset reply string */
                break;
            case '#':               /* comment line, ignore */
                break;
            case '+':               /* string upon wich we continue ! */
                strcpy(Continue,m->line+1);
                rip(Continue);              /* get rid of \n */
                break;
            case '&':               /* Wait a certain number of seconds */
                timeout = atol(m->line+1);
                pause(timeout * 1000L);
                break;
            case '@':               /* string that sets timeout */
                timeout = atol(m->line+1);
                if(timeout)                 /* if a valid conversion */
                    timeout *= 1000;        /* in ms ! */
                else
                    timeout = 90*1000L;     /* default to 1.5 minutes */
                if(Mtrace)
                    printf("fwd: %s, wait %ld < %s\n",m->name,timeout/1000,Continue);
            /* Now do the actual response interpretations */
                alarm(timeout);
                rval = recvline(m->user,m->line,MBXLINE);
                alarm(0L);
            /* Did we timeout, or connection disappear ? */
                if(Mtrace) {
                    printf("fwd: %s, rx %d",m->name,rval);
                    if(rval >= 0)
                        printf(", %s",m->line);
                    else
                        putchar('\n');
                }
                if((rval < 0) || (strstr(m->line,Continue) == NULLCHAR)) {
                    if(Mtrace)
                        printf("fwd: %s, aborted!\n",m->name);
                    exitfwd(m);
                    return;
                }
                Continue[0] = '\0';         /* reset reply string */
                break;
            default:        /* must be the end of the script */
                goto go_on;
        }
  
        usflush(m->user);   /* send it, if any */
    }
    /* Now we've past all in-between stuff, go talk to the bbs ! */
    go_on:
    usflush(m->user);
    fclose(m->tfile);
    m->tfile = NULLFILE;
  
    /* make sure there is something left ! */
    if(cp == NULLCHAR) {
        if(Mtrace)
            puts("fwd: forward.bbs error!");
        exitfwd(m);
        return;
    }
    if(Mtrace)
        printf("fwd: %s, script done\n",m->name);
  
    /* read the initial output from the bbs, looking for the SID */
    for(;;) {
        if(recvline(m->user,m->line,MBXLINE) == -1) {
            exitfwd(m);
            return;
        }
        if(ISPROMPT(m->line))
            break;
        if(*m->line == '[') {           /* parse the SID */
            rip(m->line);
            mbx_parse(m);
            continue;
        }
    }
    /* Now sync the two ends as telnet password messes them up */
    if(socklen(m->user,0))          /* discard any remaining input */
        recv_mbuf(m->user,NULL,0,NULLCHAR,0);
  
    /* send our SID if the peer announced its SID */
#ifdef FBBFWD
    if(m->sid & MBX_FBBFWD) {
       // All we do is send a SID and start forwarding.
       // The remote box doesn't send any OK prompts.
       tputs(MboxIdF);
    } else
#endif
    if(m->sid & MBX_SID) {
        tputs(MboxId);
        usflush(m->user);
        for(;;) {
            if(recvline(m->user,m->line,MBXLINE) == -1) {
                exitfwd(m);
                return;
            }
            if(ISPROMPT(m->line))
                break;
        }
    }
    /* start the actual forwarding */
#ifdef FBBFWD
    if(m->sid & MBX_FBBFWD) {
       dofbbfwd(0,NULL,(void *)m);
       return;
    }
    else
#endif
    dorevfwd(0,NULL,(void *)m);
    /* ask for reverse forwarding or just disconnect */
    if(((m->sid & MBX_SID) && tputs("F>\n") == -1) ||
    (m->sid & MBX_SID) == 0) {
        exitfwd(m);
        /* close_s(Curproc->output); exitfwd() calls exitbbs() which does this! */
        return;
    }
    usflush(m->user);
    /* parse the commands that are are received during reverse
     * forwarding.
     */
    while(recvline(m->user,m->line,MBXLINE) > 0) {
        rip(m->line);
        if(mbx_parse(m) == -2)   /* got the "*** Done" command */
            break;
        tputs("F>\n");
        usflush(m->user);
    }
    exitfwd(m);
    /* close_s(Curproc->output); exitfwd() calls exitbbs() which does this! */
}
  
/* open a network connection based upon information in the cc line.
 * m->user is set to the socket number.
 */
static int
openconn(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    struct mbx *m;
    char sock[MAXSOCKSIZE], alias[AXBUF];
    struct nrroute_tab *rp;
    union sp sp;
    int len;
    char *remote;
#ifdef AX25
    char digis[MAXDIGIS][AXALEN];
    int ndigis,i;
#endif
  
    m = (struct mbx *)p;
    sp.p = sock;
    if(argc < 2) {
        if(Mtrace)
            tputs("fwd: connect command error\n");
        return -1;
    }
    remote = argv[1];
    switch(*argv[0]) {
        case 't':
            sp.in->sin_family = AF_INET;
            if((sp.in->sin_addr.s_addr = resolve(argv[1])) == 0) {
                if(Mtrace)
                    tprintf("fwd: telnet - unknown host %s\n",argv[1]);
                return -1;
            }
        /* get the optional port number */
            if(argc > 2)
                sp.in->sin_port = atoip(argv[2]);
            else
                sp.in->sin_port = IPPORT_TELNET;
            if((m->user = socket(AF_INET,SOCK_STREAM,0)) == -1) {
                if(Mtrace)
                    tputs("fwd: unable to open telnet socket\n");
                return -1;
            }
            len = sizeof(*sp.in);
            m->family = AF_INET; /*So the user list will be correct! - WG7J */
            break;
#ifdef AX25
        case 'a':
        case 'c':       /* allow 'c' for 'connect' as well */
            if(argc < 3) {
                if(Mtrace)
                    tprintf("fwd: connect syntax error - %s %s ?\n",argv[0],argv[1]);
                return -1;
            }
            sp.ax->sax_family = AF_AX25;
            strncpy(sp.ax->iface,argv[1],ILEN); /* the interface name */
            setcall(sp.ax->ax25_addr,argv[2]); /* the remote callsign */
            if(argc > 3){  /* process digipeater string; do auto-ax25-route-add */
                ndigis = argc - 3;
                if(ndigis > MAXDIGIS){
                    if (Mtrace) tprintf("Too many digipeaters to reach %s\n",argv[2]);
                    return -1;
                }
                for(i=0;i<ndigis;i++){
                    if(setcall(digis[i],argv[i+3]) == -1){
                        if (Mtrace) tprintf("Bad digipeater %s\n",argv[i+3]);
                        return -1;
                    }
                }
                if(ax_add(sp.ax->ax25_addr,AX_AUTO,digis,ndigis,if_lookup(argv[1])) == NULLAXR){
                    if (Mtrace) tprintf("auto AX25 route add failed for %s\n",argv[2]);
                    return -1;
                }
            }

            if((m->user = socket(AF_AX25,SOCK_STREAM,0)) == -1) {
                if(Mtrace)
                    tputs("fwd: Unable to open ax25 socket\n");
                return -1;
            }
            len = sizeof(*sp.ax);
            m->family = AF_AX25; /*So the user list will be correct! - WG7J */
            remote = argv[2];
            break;
#endif /* AX25 */
#ifdef NETROM
        case 'n':
    /* See if the requested destination could be an alias, and
     * use it if it is.  Otherwise assume it is an AX.25
     * address.
     */
            putalias(alias,argv[1],0);
            strupr(argv[1]);
            if ((rp = find_nrboth(alias,argv[1])) == NULLNRRTAB)  {
                if(Mtrace)
                    tprintf("fwd: Netrom route unavailable - %s\n",argv[1]);
                return -1;
            }
    /* Setup the local side of the connection */
            sp.nr->nr_family = AF_NETROM;
            len = sizeof(*sp.nr);
            if((m->user = socket(AF_NETROM,SOCK_SEQPACKET,0)) == -1) {
                if(Mtrace)
                    tprintf("fwd: unable to open netrom socket - %s\n",argv[1]);
  
                return -1;
            }
            memcpy(sp.nr->nr_addr.user,Nr4user,AXALEN);
            memcpy(sp.nr->nr_addr.node,Nr_iface->hwaddr,AXALEN);
            bind(m->user,sp.p,len);
  
    /* Now the remote side */
            memcpy(sp.nr->nr_addr.node,rp->call,AXALEN) ;
    /* The user callsign of the remote station is never
         * used by NET/ROM, but it is needed for the psocket() call.
         */
            memcpy(sp.nr->nr_addr.user,rp->call,AXALEN) ;
  
            m->family = AF_NETROM; /*So the user list will be correct! - WG7J */
            break;
#endif /* NETROM */
        default:
            if(Mtrace)
                tprintf("fwd: Invalid connect mode - %s\n",argv[0]);
  
            return -1;
    }
    sockmode(m->user,SOCK_ASCII);
    if(connect(m->user,sp.p,len) == -1) {
        log(m->user,"MBOX fwd failed: %s - %s errno %d",
        remote,sockerr(m->user),errno);
        if(Mtrace)
            tprintf("fwd: Connection failed to %s\n",remote);
        close_s(m->user);
        return -1;
    }
    return m->user;
}
  
#endif /*MBFWD*/
