/* The are the MAILCMDS */
#include <time.h>
#include <ctype.h>
#ifdef MSDOS
#include <alloc.h>
#endif
#ifdef  UNIX
#include <sys/types.h>
#include <sys/stat.h>
#endif
#include "global.h"
#ifdef MAILCMDS
#include "timer.h"
#include "proc.h"
#include "socket.h"
#include "usock.h"
#include "session.h"
#include "smtp.h"
#include "dirutil.h"
#include "telnet.h"
#include "ftp.h"
#include "ftpserv.h"
#include "commands.h"
#include "netuser.h"
#include "files.h"
#include "bm.h"
#include "pktdrvr.h"
#include "ax25.h"
#include "mailbox.h"
#include "ax25mail.h"
#include "nr4mail.h"
#include "cmdparse.h"
#include "mailfor.h"
  
/* By setting the fp to NULL, we can check in exitbbs()
 * wether a tempfile has been closed or not - WG7J
 */
#define MYFCLOSE(x) { fclose(x); x = (FILE *) 0; }
  
char CcLine[] = "Cc: ";
char Mbwarning[] = "Third Party mail is not permitted.\n";
char InvalidNameChars[] = "?*<>[],;:+=\"";
  
int MbSent;
int MbRead;
int MbRecvd;
  
#ifdef MBFWD
int MbForwarded;
#endif
  
int
dosid(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    struct mbx *m;
    char *cp;
  
    m = (struct mbx *)p;
    if(argc == 1)
        return 1;
    if(argv[1][strlen(argv[1]) - 1] != ']') /* must be an SID */
        return 1;
#ifdef notdef
    if(m->stype == 'Z' && strncmp(argv[1],"cz",2) == 0) {
        /* LAN-LINK's [ZCZ] */
        m->sid |= MBX_LL;
        return 0;
    }
#endif
    /* Other bbs's */
    m->sid = MBX_SID;
  
    /* Now check to see if this is an RLI board.
     * As usual, Hank does it a bit differently from
     * the rest of the world.
     */
    if(m->stype == 'R' && strncmp(argv[1],"li",2) == 0)/* [RLI] at a minimum */
        m->sid |= MBX_RLI_SID;
    /* Or maybe it is F6FBB ? */
    else if(m->stype == 'F')
        m->sid |= MBX_FBB;
    /* Check to see if the BBS supports a kludge called "hierarchical
     * routing designators."
     *
     * No need to check for ']' -- it must be there or this is not
     * a valid mbox id -- it is checked earlier (fix de OH3LKU)
     *
     * Sid format is [BBSTYPE-VERSION-OPTIONS]
     * check for LAST -, to allow for version portion. - WG7J
     */
    if((cp = strrchr(argv[1],'-')) != NULLCHAR) {
        cp++;
    /* Okay, now parse the options */
        if((strchr(cp,'h') != NULLCHAR) && (strchr(cp,'$') != NULLCHAR))
            m->sid |= MBX_HIER_SID;
        if(strchr(cp,'m') != NULLCHAR)
            m->sid |= MBX_MID;
    }
    return 0;
}
  
int
doarea(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    struct mbx *m;
    FILE *fp;
    char buf[MBXLINE];
  
    m = (struct mbx *) p;
  
    if(argc < 2){
#ifdef USERLOG
        if(m->stype == 'N') {
            listnewmail(m,0);
            return 0;
        }
#endif
        tprintf("Current message area is: %s\n\n",m->area);
        tprintf("Available areas are:\n%-15s",m->name);
        if(m->stype == 'F')
            tputs("  Your private mail area\n");
        else
            tputc('\n');
        if((fp = fopen(Arealist,READ_TEXT)) == NULLFILE)
            return 0;
        if(m->stype == 'F')
            sendfile(fp,m->user,ASCII_TYPE,0,m);
        else {
            /* send only the area names, not the description */
            while(fgets(buf,MBXLINE,fp) != NULLCHAR) {
                if(buf[0] != '#') { /* skip comments */
                    firsttoken(buf);
                    tprintf("%s\n",buf);
                }
            }
            tputs("\nType AF to get description of areas\n\n");
        }
        fclose(fp);
        return 0;
    } else {
        dotformat(argv[1]);
        if(!strcmp(m->name,argv[1]) || isarea(argv[1]) ||
           ((m->privs&SYSOP_CMD) && strpbrk(argv[1],InvalidNameChars)==NULLCHAR
            && *(argv[1]+strlen(argv[1])-1)!='.' )){
            changearea(m,argv[1]);
            if(m->areatype == PRIVATE)
                tputs("You have ");
            else
                tprintf("%s: ",m->area);
            if(m->nmsgs){
#ifdef USERLOG
                tprintf("%d message%s -  %d new.\n",m->nmsgs,
                m->nmsgs == 1 ? " " : "s ", m->newmsgs);
#else
                if(m->areatype == PRIVATE)
                    tprintf("%d message%s -  %d new.\n", m->nmsgs,
                    m->nmsgs == 1 ? " " : "s ", m->newmsgs);
                else
                    tprintf("%d message%s.\n", m->nmsgs,
                    m->nmsgs == 1 ? "" : "s");
#endif
            } else
                tputs("0 messages.\n");
        } else
            tprintf("No such message area: %s\n",argv[1]);
    }
    return 0;
}
  
/* subroutine to do the actual switch from one area to another */
/* USERLOGGING added by WG7J */
void
changearea(m,area)
struct mbx *m;
char *area;
{
    if (m->area[0]) {    /* current area non-null? */
#ifdef USERLOG
        setlastread(m);
#endif
        closenotes(m);
    }
    dotformat(area);
    strcpy(m->area,area);
    /* Check areas first, so regular users loging in with areaname
     * cannot gain permission to kill messages in areas
     * (if univperm in ftpusers doesn't allow that)
     */
    if(isarea(area))
        m->areatype = AREA;
    else if(!strcmp(m->name,area))
        m->areatype = PRIVATE;
    else    /* Sysop checking someone else's area */
        m->areatype = USER;
  
#ifdef USERLOG
    /* only read last read message-id if this is not a bbs,
     * current area is a public area and not 'help'
     * or area starts with 'sys'
     */
    if(!(m->sid & MBX_SID))
        if( (strcmp(area,"help") && m->areatype == AREA) || \
            !strncmp(m->area,"sys",3) || \
            (m->privs & MBX_SYSOP) )
            getlastread(m);
#endif
  
    scanmail(m);
}
  
/* States for send line parser state machine */
#define         LOOK_FOR_USER           2
#define         IN_USER                 3
#define         AFTER_USER              4
#define         LOOK_FOR_HOST           5
#define         IN_HOST                 6
#define         AFTER_HOST              7
#define         LOOK_FOR_FROM           8
#define         IN_FROM                 9
#define         AFTER_FROM              10
#define         LOOK_FOR_MSGID          11
#define         IN_MSGID                12
#define         FINAL_STATE             13
#define         ERROR_STATE             14
  
/* Prepare the addressee.  If the address is bad, return -1, otherwise
 * return 0
 */
int
mbx_to(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    char *cp;
    int state, i;
    char *user, *host, *from, *msgid;
    int userlen = 0, hostlen = 0, fromlen = 0, msgidlen = 0;
    struct mbx *m;
  
    m = (struct mbx *)p;
    /* Free anything that might be allocated
     * since the last call to mbx_to() or mbx_reply()
     */
    free(m->to);
    m->to = NULLCHAR;
    free(m->tofrom);
    m->tofrom = NULLCHAR;
    free(m->tomsgid);
    m->tomsgid = NULLCHAR;
    free(m->origto);
    m->origto = NULLCHAR;
    free(m->origbbs);
    m->origbbs = NULLCHAR;
    free(m->subject);
    m->subject = NULLCHAR;
    free(m->date);
    m->date = NULLCHAR;
  
    if(argc == 1)
        return -1;
    i = 1;
    cp = argv[i];
    state = LOOK_FOR_USER;
    while(state < FINAL_STATE){
#ifdef MBDEBUG
        tprintf("State is %d, char is %c\n", state, *cp);
#endif
        switch(state){
            case LOOK_FOR_USER:
                if(*cp == '@' || *cp == '<' || *cp == '$'){
                    state = ERROR_STATE;            /* no user */
                } else {
                    user = cp;                      /* point at start */
                    userlen++;                      /* start counting */
                    state = IN_USER;
                }
                break;
            case IN_USER:
            switch(*cp){
                case '\0':
                    state = AFTER_USER;             /* done with username */
                    break;
                case '@':
                    state = LOOK_FOR_HOST;          /* hostname should follow */
                    break;
                case '<':
                    state = LOOK_FOR_FROM;          /* from name should follow */
                    break;
                case '$':
                    state = LOOK_FOR_MSGID; /* message id should follow */
                    break;
                default:
                    userlen++;                      /* part of username */
            }
                break;
            case AFTER_USER:
            switch(*cp){
                case '@':
                    state = LOOK_FOR_HOST;          /* hostname follows */
                    break;
                case '<':
                    state = LOOK_FOR_FROM;          /* fromname follows */
                    break;
                case '$':
                    state = LOOK_FOR_MSGID; /* message id follows */
                    break;
                default:
                    state = ERROR_STATE;
            }
                break;
            case LOOK_FOR_HOST:
                if(*cp == '@' || *cp == '<' || *cp == '$'){
                    state = ERROR_STATE;
                    break;
                }
                if(*cp == '\0')
                    break;
                host = cp;
                hostlen++;
                state = IN_HOST;
                break;
            case IN_HOST:
            switch(*cp){
                case '\0':
                    state = AFTER_HOST;             /* found user@host */
                    break;
                case '@':
                    state = ERROR_STATE;            /* user@host@? */
                    break;
                case '<':
                    state = LOOK_FOR_FROM;          /* fromname follows */
                    break;
                case '$':
                    state = LOOK_FOR_MSGID; /* message id follows */
                    break;
                default:
                    hostlen++;
            }
                break;
            case AFTER_HOST:
            switch(*cp){
                case '@':
                    state = ERROR_STATE;            /* user@host @ */
                    break;
                case '<':
                    state = LOOK_FOR_FROM;          /* user@host < */
                    break;
                case '$':
                    state = LOOK_FOR_MSGID; /* user@host $ */
                    break;
                default:
                    state = ERROR_STATE;            /* user@host foo */
            }
                break;
            case LOOK_FOR_FROM:
                if(*cp == '@' || *cp == '<' || *cp == '$'){
                    state = ERROR_STATE;
                    break;
                }
                if(*cp == '\0')
                    break;
                from = cp;
                fromlen++;
                state = IN_FROM;
                break;
            case IN_FROM:
            switch(*cp){
                case '\0':
                    state = AFTER_FROM;             /* user@host <foo */
                    break;
                case '<':
                    state = ERROR_STATE;            /* user@host <foo< */
                    break;
                case '$':
                    state = LOOK_FOR_MSGID; /* message id follows */
                    break;
                default:
                    fromlen++;
            }
                break;
            case AFTER_FROM:
            switch(*cp){
                case '@':                               /* user@host <foo @ */
                case '<':                               /* user@host <foo < */
                    state = ERROR_STATE;
                    break;
                case '$':
                    state = LOOK_FOR_MSGID; /* user@host <foo $ */
                    break;
                default:
                    state = ERROR_STATE;            /* user@host foo */
            }
                break;
            case LOOK_FOR_MSGID:
                if(*cp == '\0')
                    break;
                msgid = cp;
                msgidlen++;
                state = IN_MSGID;
                break;
            case IN_MSGID:
                if(*cp == '\0')
                    state = FINAL_STATE;
                else
                    msgidlen++;
                break;
            default:
            /* what are we doing in this state? */
                state = ERROR_STATE;
        }
        if(*(cp) == '\0'){
            ++i;
            if(i < argc)
                cp = argv[i];
            else break;
        } else
            ++cp;
    }
    if(state == ERROR_STATE || state == LOOK_FOR_HOST
        || state == LOOK_FOR_FROM || state == LOOK_FOR_MSGID)
        return -1;              /* syntax error */
  
    m->to = mallocw(userlen + hostlen + 2);
  
    strncpy(m->to, user, userlen);
    m->to[userlen] = '\0';
  
    if(hostlen){
        m->to[userlen] = '@';
        strncpy(m->to + userlen + 1, host, hostlen);
        m->to[userlen + hostlen + 1] = '\0';
    }
    if(fromlen){
        m->tofrom = mallocw(fromlen + 1);
        strncpy(m->tofrom, from, fromlen);
        m->tofrom[fromlen] = '\0';
    }
    if(msgidlen){
        m->tomsgid = mallocw(msgidlen + 1);
        strncpy(m->tomsgid, msgid, msgidlen);
        m->tomsgid[msgidlen] = '\0';
    }
    return 0;
}
  
/* This opens the data file and writes the mail header into it.
 * Returns 0 if OK, and -1 if not.
 */
int
mbx_data(m,cclist,extra)
struct mbx *m;
struct list *cclist;    /* list of carbon copy recipients */
char *extra;        /* optional extra header lines */
{
    time_t t;
    struct list *ap;
    int cccnt = 0;
  
#ifdef notdef
    time(&t);
    /*These 2 lines get added again when the smtp-server handles the mail
     *not really needed - WG7J
     */
    fprintf(m->tfile,Hdrs[RECEIVED]);
    if(m->tofrom != NULLCHAR)
        fprintf(m->tfile,"from %s ",m->name);
    fprintf(m->tfile,"by %s (%s)\n\tid AA%ld ; %s",
    Hostname, Version, get_msgid(),ptime(&t));
#endif
  
    /* If m->date is set, use this one (comes from bbs-forwarded mail) */
    if(m->date != NULLCHAR)
        fprintf(m->tfile,"%s%s",Hdrs[DATE],m->date);
    else {
        time(&t);
        fprintf(m->tfile,"%s%s",Hdrs[DATE],ptime(&t));
    }
  
    /* Bulletin ID, if any */
    fprintf(m->tfile,Hdrs[MSGID]);
    if(m->tomsgid)
        fprintf(m->tfile,"<%s@%s.bbs>\n", m->tomsgid, m->name);
    else
  
        fprintf(m->tfile,"<%ld@%s>\n",get_msgid(), Hostname);
  
    /* From : , could use 'real bbs address', if origbbs is set */
    fprintf(m->tfile,Hdrs[FROM]);
    if(m->tofrom) {  /* BBS style '< call' */
        if(m->origbbs != NULLCHAR)
            fprintf(m->tfile,"%s@%s\n",m->tofrom,m->origbbs);
        else
            fprintf(m->tfile,"%s%%%s.bbs@%s\n",m->tofrom, m->name, Hostname);
    } else {
        if(m->origbbs != NULLCHAR)
            fprintf(m->tfile,"%s@%s\n",m->name,m->origbbs);
        else {
            int found = 0;
            char *cp,*cp2;
            FILE *fp;
            char line[128];
  
            if((fp = fopen(Pdbase,READ_TEXT)) != NULLFILE) {
                while(fgets(line,128,fp) != NULLCHAR) {
                    rip(line);
                    cp = skipwhite(line);
                    if(*cp == '#')
                        continue;
                    /* Now find end of fist entry */
                    cp2 = cp;
                    while(*cp2 && *cp2 != ' ' && *cp2 != '\t')
                        cp2++;
                    if(*cp2 == '\0') /* No additional data */
                        continue;
                    *cp2 = '\0';    /* terminate first entry */
                    if(!stricmp(cp,m->name)) {
                        /* Found one, now get the name */
                        cp = skipwhite(++cp2);
                        if(*cp) {
                            found = 1;
                            fprintf(m->tfile,"%s <%s@%s>\n",cp,m->name,Hostname);
                        }
                    }
                }
                fclose(fp);
            }
            if(!found)
                fprintf(m->tfile,"%s@%s\n",m->name,Hostname);
        }
    }
  
#if defined USERLOG && defined REGISTER
    if((m->sid & MBX_REPLYADDR) && m->IPemail)
        fprintf(m->tfile,"%s%s\n",Hdrs[REPLYTO],m->IPemail);
#endif /* REPLY-TO HEADER */
  
    fprintf(m->tfile,"%s%s\n",Hdrs[TO],m->origto != NULLCHAR ? m->origto : m->to);
  
    /* Write Cc: line */
    for(ap = cclist; ap != NULLLIST; ap = ap->next) {
        if(cccnt == 0){
            fprintf(m->tfile,"%s",Hdrs[CC]);
            cccnt = 4;
        }
        else {
            fprintf(m->tfile,", ");
            cccnt += 2;
        }
        if(cccnt + strlen(ap->val) > 80 - 3) {
            fprintf(m->tfile,"\n    ");
            cccnt = 4;
        }
        fputs(ap->val,m->tfile);
        cccnt += strlen(ap->val);
    }
    if(cccnt)
        fputc('\n',m->tfile);
    fprintf(m->tfile,"%s%s\n",Hdrs[SUBJECT],m->subject);
    if(!isspace(m->stype) && ((m->stype != 'R' && m->stype != 'F') ||
        (m->sid & MBX_SID) !=0))
        fprintf(m->tfile,"%s%c\n", Hdrs[BBSTYPE],m->stype);
  
#ifdef notdef
    /*not really needed, the 'From <user%fwdbbs@host>' shows this too!*/
    /* Also store the 'real smtp from' address */
    if((m->tofrom != NULLCHAR) && (m->origbbs != NULLCHAR))
        fprintf(m->tfile,"%s%s%%%s@%s\n",Hdrs[XFROM],m->tofrom,m->name, Hostname);
#endif
  
    if(extra != NULLCHAR)
        fprintf(m->tfile,extra);
  
    return 0;
}
  
/* Returns true if string is in history file or if string appears to be a
 * message id generated by our system.
 */
int
msgidcheck(string)
char *string;
{
    FILE *fp;
    char *cp,buf[LINELEN];
  
    if(string == NULLCHAR)
        return 0;
  
    /* BID's that we have generated ourselves are not kept in the history
     * file. Such BID's are in the nnnn_hhhh form, where hhhh is a part of
     * our hostname, truncated so that the BID is no longer than 11
     * characters.
     */
    if((cp = strchr(string,'_')) != NULLCHAR && *(cp+1) != '\0' &&
        strnicmp(cp+1,Hostname,strlen(cp+1)) == 0)
        return 1;
  
    if((fp = fopen(Historyfile,READ_TEXT)) == NULLFILE)
        return 0;
    while(fgets(buf,LINELEN,fp) != NULLCHAR) {
        firsttoken(buf);
        if(stricmp(string,buf) == 0) {    /* found */
            fclose(fp);
            return 1;
        }
    }
    fclose(fp);
    return 0;
}
  
/* Attempt to determine if this is third-pary mail. */
int
thirdparty(m)
struct mbx *m;
{
    char buf[MBXLINE], *cp, *rp;
    FILE *fp;
  
    if(strchr(m->to,'@') != NULLCHAR || strchr(m->to,'%') != NULLCHAR
        || strchr(m->to,'!') != NULLCHAR)
        return 0;
  
    rp = strdup(Hostname);
  
    if((cp = strchr(rp, '.')) != NULLCHAR)
        *cp = '\0';
  
    if(stricmp(m->to,rp) == 0){
        free(rp);
        return -1;
    }
    free(rp);
  
    if(stricmp(m->to,"sysop") == 0)
        return -1;
  
    if((fp = fopen(Arealist,READ_TEXT)) == NULLFILE)
        return 0;
  
    while(fgets(buf,MBXLINE,fp) != NULLCHAR){
        /* The first word on each line is all that matters */
        firsttoken(buf);
        if(stricmp(m->to,buf) == 0){
            fclose(fp);
            return -1;
        }
    }
    fclose(fp);
    return 0;
}
  
/* Move messages from current area to another */
int
dombmovemail(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    long pos;
    int num,i,j;
    int move[NARG];
    char *to;
    struct mbx *m;
    char *cp;
    FILE * Mfile;           /* file to move to */
    int not_area;           /* is to-file an area or a regular file? */
    struct let *cmsg;
    int thisone;
    int start;
    int end;
    char *area;
    struct mailindex ind;
    char buf[MBXLINE];
  
    m = (struct mbx *)p;
  
    if(argc == 1) {
        tputs("Syntax: MM area - moves current message\n"
        "        MM n1 [n2...] area - move message n1 (n2...)\n");
        return 0;
    }
    if(argc == 2) {
        /* NO message #, use current message */
        num = 1;
        to = argv[1];
        move[0] = m->current;
    } else {
        /* See if x - y format was used and use a for i = x to y loop */
        if(argv[2][0] == '-') {
            num=0;
            start = atoi(argv[1]);
            end   = atoi(argv[3]);
            if(start < 0 || start > m->nmsgs || end < 0 || end > m->nmsgs ) {
                tprintf(Badmsg,start);
                start = end+1;
            }
            for(i=start;i<=end;i++) {
                if((move[num]=i) != 0 ) {
                    if(move[num] <= m->nmsgs)
                        num++;
                } else
                    tprintf(Badmsg,argv[i]);
            } /* endfor */
        } else {
            /* Scan all message # to move */
            num=0;
            for(i=1;i<argc-1;i++) {
                if( (move[num]=atoi(argv[i])) != 0 ) {
                    if(move[num] <= m->nmsgs)
                        num++;
                } else
                    tprintf(Badmsg,argv[i]);
            }
        }
        to = argv[argc-1];
    }

#ifdef UNIX  
    if (*to == '/') {  /* not area if begins with slash */
#else
    if (*to == '/' || *to == '\\' || *(to+1) == ':') {  /* not area if begins with slash or drive spec */
#endif
        not_area=1;
        strncpy(buf, to, sizeof(buf));  buf[sizeof(buf)-1] = '\0';
    }
    else {
        int c;

        not_area=0;
        dotformat(to);
        if (!isarea(to)) {
            tprintf("%s is not a public area... Are you sure? ",to);
            if(charmode_ok(m))
                c = tkeywait("Move(N=no)?",0);
            else  /* For AX.25 and NET/ROM connects */
                c = mykeywait("Move(N=No)?",m);
            if(c == -1 || c == 'n' || c == 'N') {
                tputs("Aborted.\n");
                return 0;
            }
        } /* endif */
        dirformat(to);

        /* Now try to lock the destination file */
        if(mlock(Mailspool,to) == -1) {
            tprintf("Can't lock '%s', please try later\n",to);
            return 0;
        }
        sprintf(buf,"%s/%s.txt",Mailspool,to);
    }
    /* open the destination file for appending, and set file pos for ftell() */
    if( ((Mfile=fopen(buf,"a+")) == NULLFILE) || fseek(Mfile, 0L, SEEK_END)) {
        tprintf("Can't open/seek '%s'\n",buf);
        if(!not_area) rmlock(Mailspool,to);
        return 0;
    }
  
    /* Open the mailbox file for reading */
    area = strdup(m->area);
    dirformat(area);
    sprintf(buf,"%s/%s.txt",Mailspool,area);
    if((m->mfile=fopen(buf,"rt")) == NULLFILE) {
        tprintf("Can't open '%s'\n",buf);
        fclose(Mfile);
        if(!not_area) rmlock(Mailspool,to);
        free(area);
        return 0;
    }
    memset(&ind,0,sizeof(struct mailindex));
  
    /* Okay, let's do the work */
    for(i=0;i<num;i++) {
        thisone = move[i];
        cmsg = &m->mbox[thisone];
  
        /* find start of this message */
        fseek(m->mfile,cmsg->start,0);
  
        /* Get the index for this message */
        get_index(thisone,area,&ind);
  
        /* now read this message */
        fgets(buf,MBXLINE,m->mfile);    /* The 'From ' line */
        pos = ftell(Mfile);
        fputs(buf,Mfile);
        while(fgets(buf,MBXLINE,m->mfile)!= NULL) {
            if(!strncmp(buf,"From ",5))
                break;
            fputs(buf,Mfile);
        }
        if (!not_area) {
            /* Update the index with the new message */
            ind.size = ftell(Mfile) - pos;
            if (write_index(to,&ind) == -1)
                log(m->user, "index update failed for %s", to);
        }
  
        if((m->stype == 'C') || (m->stype == 'c')) {
            tprintf("Message %d copied...\n",thisone);
        } else {
            /* delete this message */
            cmsg->status |= BM_DELETE;
            m->change = 1;
            tprintf("Message %d moved...\n",thisone);
        }
    }
    fclose(Mfile);
    free(area);
  
    fclose(m->mfile);
    m->mfile = NULL;
    if(!not_area) rmlock(Mailspool,to);
  
    return 0;
}
  
/*Some additional security - WG7J
 *NO_3PARTY =  disallow all 3rd party mail
 *NO_SENDCMD = only allow mail to sysop
 */
int
dosend(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    int cccnt = 0, fail = 0;
    char *host, *cp, fullfrom[MBXLINE], sigwork[LINELEN], *rhdr = NULLCHAR;
    struct list *ap, *cclist = NULLLIST;
    struct mbx *m;
    FILE *fp;
#ifdef notdef
    time_t now;
#endif
    int done = 0;
    char *cp2;
    int c;
  
#ifdef RLINE
    struct tm t;
#define ODLEN   16
#define OBLEN   32
    char tmpline[MBXLINE];
    char fwdbbs[NUMFWDBBS][FWDBBSLEN+1];
    int myfwds = 0;
    int i;
    int zulu;
    int check_r = 0;
    int found_r = 0;
    char origdate[ODLEN];
    char origbbs[OBLEN];
    int loops = 0;
    char Me[15];
  
    origdate[0] = '\0';
    origbbs[0] = '\0';
#endif
  
    m = (struct mbx *)p;
  
    if((m->stype != 'R' || (m->sid & MBX_SID)) && mbx_to(argc,argv,m)
    == -1){
        tputs((m->sid & MBX_SID) ? "NO - syntax error\n" : \
        "S command syntax error - format is:\n" \
        "S[C|F] name [@ host] [< from_addr] [$bulletin_id]\n" \
        "SR [number]\n");
#ifdef MAILERROR
        mail_error("%s: MBOX S syntax error - %s\n",m->name,cmd_line(argc,argv,m->stype));
#endif
        return 0;
    }
  
    /*Check for send permission */
    if(m->privs & NO_SENDCMD) { /*is this to 'SYSOP' or 'sysop' ?*/
        if(stricmp(m->to,"sysop")) {
            tputs((m->sid & MBX_SID) ? "NO - permission denied\n" : \
            "Sorry, only mail to 'sysop' allowed!\n");
#ifdef MAILERROR
            mail_error("%s: no mail permission - %s\n",m->name,m->to);
#endif
            return 0;
        }
    }
  
    /* Check for a BID on bulletins from other bbs's - WG7J */
    if((m->sid & MBX_SID) && !NoBid &&
    (m->stype == 'B') && (m->tomsgid == NULLCHAR)) {
        tputs("NO - No BID!\n");
        log(m->user,"MBOX %s: SB without BID - %s",m->name,m->to);
#ifdef MAILERROR
        mail_error("MBOX %s: SB without BID - %s",m->name,m->to);
#endif
        return 0;
    }
  
    if(m->stype != 'R' && msgidcheck(m->tomsgid)) {
        if(m->sid & MBX_SID)
            tputs("NO - ");
        tprintf("Already have %s\n",m->tomsgid);
        return 0;
    }
    if(m->stype == 'R' && !(m->sid & MBX_SID) &&
    mbx_reply(argc,argv,m,&cclist,&rhdr) == -1) {
        tputs("Can not reply\n");
        return 0;
    }
    if((cp = rewrite_address(m->to)) != NULLCHAR)
        if(strcmp(m->to,cp) != 0){
            m->origto = m->to;
            m->to = cp;
        }
        else
            free(cp);
  
    /* refuse any mail that gets rewritten into 'refuse' - WG7J */
    if(!strcmp(m->to,"refuse")) {
        tputs((m->sid & MBX_SID) ? "NO - refused\n" : \
        "Bad user or hostname,  please mail 'sysop' for help\n");
        return 0;
    }
  
    if( (!ThirdParty && !(m->privs & SYSOP_CMD)) || (m->privs & NO_3PARTY) )
        if(thirdparty(m) == 0){
            tputs(Mbwarning);
#ifdef MAILERROR
            mail_error("%s: 3rd party mail refused - %s\n",m->name,m->to);
#endif
            return 0;
        }
  
    /* Send the new 'To:' line to sysops only - WG7J */
    if((m->privs&SYSOP_CMD) && (m->origto != NULLCHAR || m->stype == 'R') \
        && !(m->sid & MBX_SID))
        tprintf("To: %s\n", m->to);
    if(validate_address(m->to) == 0){
        tputs((m->sid & MBX_SID) ? "NO - bad address\n" : \
        "Bad user or hostname,  please mail 'sysop' for help\n");
        free(rhdr);
        del_list(cclist);
        /* We don't free any more buffers here. They are freed upon
         * the next call to mbx_to() or to domboxbye()
         */
        return 0;
    }
    /* Display the Cc: line (during SR command) */
    for(ap = cclist; ap != NULLLIST; ap = ap->next) {
        if(cccnt == 0){
            tprintf("%s",Hdrs[CC]);
            cccnt = 4;
        }
        else {
            tputs(", ");
            cccnt += 2;
        }
        if(cccnt + strlen(ap->val) > 80 - 3) {
            tputs("\n    ");
            cccnt = 4;
        }
        tputs(ap->val);
        cccnt += strlen(ap->val);
    }
    if(cccnt)
        tputc('\n');
  
    /* If the the command was 'SC' then read the Cc: list now - WG7J */
    if((m->stype == 'C') && !(m->sid & MBX_SID)) {
        m->stype = 'P'; /* make everything private */
        tputs(CcLine);
        if(mbxrecvline(m) != -1) {
            if(strlen(m->line)) {
                if(*m->line == 0x01) { /* CTRL-A, abort */
                    free(rhdr);
                    del_list(cclist);
                    tputs(MsgAborted);
                    return 0;
                }
                cp = m->line;
        /* get all the Cc addresses, separated by comma's */
                while((cp2=strchr(cp,',')) != NULLCHAR) {
                    *cp2 = '\0';
            /*get rid of leading spaces or tabs*/
                    while(*cp == ' ' || *cp == '\t')
                        cp++;
                    if(strlen(cp))
                        addlist(&cclist,cp,0);
                    cp = cp2 + 1;
                }
        /* Do the last or only one */
        /* get rid of leading spaces or tabs*/
                while(*cp == ' ' || *cp == '\t')
                    cp++;
                if(strlen(cp))
                    addlist(&cclist,cp,0);
            }
        } else {
            free(rhdr);
            del_list(cclist);
            return 0;
        }
    }
  
    /* Now check to make sure we can create the needed tempfiles - WG7J */
    if((m->tfile = tmpfile()) == NULLFILE) {
        free(rhdr);
        del_list(cclist);
/*
    tputs((m->sid & MBX_SID) ? "NO - no temp file\n" : \
        "Can't create temp file for mail\n");
    return 0;
 */
    /* instead of saying NO and have the other bbs think we already
     * have the message, disconnect !
     */
        if(m->sid & MBX_SID)
            return -2;
    /* tell regualr users about it */
        tputs("Can't create temp file for mail\n");
        return 0;
    }
#ifdef RLINE
    /* Only accept R: lines from bbs's */
    if((m->sid & MBX_SID)&&(Rdate || Rreturn || Rfwdcheck || Mbloophold)){
    /* Going to interpret R:headers,
     * we need another tempfile !
     */
        if((m->tfp = tmpfile()) == NULLFILE) {
            free(rhdr);
            del_list(cclist);
/*
        tputs("NO - no temp file\n");
        return 0;
 */
        /* disconnect to avoid the other bbs to think that we already have
         * the message !
         */
            return -2;
        }
    /* Now we got enough :-) */
        check_r = 1;
        Checklock++;
    /* Set the call, used in loop detect code - WG7J */
        if(Mbloophold) {
            pax25(Me,Mycall);
            if((cp = strchr(Me,'-')) != NULLCHAR)
                *cp = '\0'; /* remove SSID */
        }
    }
#endif
  
    m->state = MBX_SUBJ;
    if(m->stype != 'R' || (m->sid & MBX_SID)) {
        tputs((m->sid & MBX_SID) ? "OK\n" : "Subject:\n");
        if(mbxrecvline(m) == -1) {
#ifdef RLINE
            if(check_r) {
                MYFCLOSE(m->tfp);
                Checklock--;
            }
#endif
            return 0;
        }
    }
    else                            /* Replying to a message */
        tprintf("Subject: %s\n",m->line);
  
    m->subject = strdup(m->line);
  
#ifdef RLINE
    if(!check_r) {
#endif
        mbx_data(m,cclist,rhdr);
    /*Finish smtp headers*/
        fprintf(m->tfile,"\n");
#ifdef RLINE
    }
#endif
    m->state = MBX_DATA;
    if(!(m->sid & MBX_SID) && m->stype != 'F')
        tprintf("Enter message.  %s",Howtoend);
  
    if(m->stype != 'F' || (m->sid & MBX_SID) != 0) {
        while(mbxrecvline(m) != -1){
            if(m->line[0] == 0x01){  /* CTRL-A */
                MYFCLOSE(m->tfile);
#ifdef RLINE
                if(check_r)
                    MYFCLOSE(m->tfp);
#endif
                tputs(MsgAborted);
                free(rhdr);
                del_list(cclist);
                return 0;
            }
            if(m->line[0] != CTLZ && stricmp(m->line, "/ex")) {
#ifdef RLINE
                if(check_r) {
            /* Check for R: lines to start with */
                    if(!strncmp(m->line,"R:",2)) { /*found one*/
                        found_r = 1;
            /*Write this line to the second tempfile
             *for later rewriting to the real one
             */
                        fprintf(m->tfp,"%s\n",m->line);
            /* Find the '@[:]CALL.STATE.COUNTRY'or
             * or the '?[:]CALL.STATE.COUNTRY' string
             * The : is optional.
             */
                        if( ((cp=strchr(m->line,'@')) != NULLCHAR) ||
                        ((cp=strchr(m->line,'?')) != NULLCHAR) ) {
                            if((cp2=strchr(cp,' ')) != NULLCHAR)
                                *cp2 = '\0';
                            if((cp2=strchr(cp,'\n')) != NULLCHAR)
                                *cp2 = '\0';
                            if((cp2=strchr(cp,'\t')) != NULLCHAR)
                                *cp2 = '\0';
                /* Some bbs's send @bbs instead of @:bbs*/
                            if (*++cp == ':')
                                cp++;
                /* if we use 'return addres'
                 * copy whole 'domain' name
                 */
                            if(Rreturn)
                                if(strlen(cp) <= OBLEN)
                                    strcpy(origbbs,cp);
                /* Optimize forwarding ? */
                            if(Rfwdcheck || Mbloophold) {
                /*if there is a HADDRESS, cut off after '.'*/
                                if((cp2=strchr(cp,'.')) != NULLCHAR)
                                    *cp2 = '\0';
                                if(Mbloophold)
                    /* check to see if this is my call ! */
                                    if(!stricmp(Me,cp))
                                        loops++;
                /*cross-check with MyFwds list*/
                                if(Rfwdcheck) {
                                    for(i=0;i<Numfwds;i++) {
                                        if(!strcmp(MyFwds[i],cp)) {
                        /*Found one !*/
                                            strcpy(fwdbbs[myfwds++],cp);
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                        if(Rdate) {
                /* Find the 'R:yymmdd/hhmmz' string */
                            if((cp=strchr(m->line,' ')) != NULLCHAR) {
                                *cp = '\0';
                                if(strlen(m->line+2) <= ODLEN)
                                    strcpy(origdate,m->line+2);
                            }
                        }
                    } else {
            /* The previous line was last R: line
             * so we're done checking
             * now write the smtp headers and
             * all saved R: lines to the right tempfile
             */
                        check_r = 0;
                        Checklock--;
            /*Did we actually find one ?*/
                        if(found_r) {
                            if(Rreturn)
                                m->origbbs = strdup(strlwr(origbbs));
                            if(Rdate) {
                                if((cp=strchr(origdate,'/')) != NULLCHAR) {
                                    if((*(cp+5) == 'z') || (*(cp+5) == 'Z')) {
                                        *(cp+5) = '\0';
                                        zulu = 1;
                                    }
                                    t.tm_min = atoi(cp+3);
                                    *(cp+3) = '\0';
                                    t.tm_hour = atoi(cp+1);
                                    *cp = '\0';
                                    t.tm_mday = atoi(&origdate[4]);
                                    origdate[4] = '\0';
                                    t.tm_mon = (atoi(&origdate[2]) - 1);
                                    origdate[2] = '\0';
                                    t.tm_year = atoi(origdate);
                    /* Set the date in rfc 822 format */
                                    m->date = mallocw(40);
                                    sprintf(m->date,"%.2d %s %02d %02d:%02d:00 %.3s\n",
                                    t.tm_mday,
                                    Months[t.tm_mon],
                                    t.tm_year,
                                    t.tm_hour,
                                    t.tm_min,
                                    zulu ? "UTC" : "");
                                }
                            }
                        }
            /* Now write the headers,
             * possibly adding Xforwarded lines for bulletins,
             * or anything that has a BID.
             * Add the X-Forwarded lines and loop detect
             * headers FIRST,
             * this speeds up forwarding...
             */
                        if(Mbloophold && loops >= Mbloophold)
                            fprintf(m->tfile,"%sLoop\n",Hdrs[XBBSHOLD]);
                        if(Rfwdcheck && found_r && \
                        ((m->stype == 'B') || (m->tomsgid)) ){
                /*write Xforwarded headers*/
                            for(i=0;i<myfwds;i++) {
                                fprintf(m->tfile,"%s%s\n",Hdrs[XFORWARD],fwdbbs[i]);
                            }
                        }
            /*write regular headers*/
                        mbx_data(m,cclist,rhdr);
            /* Finish smtp headers */
                        fprintf(m->tfile,"\n");
  
            /* Now copy the R: lines back */
                        if(found_r) {
                            rewind(m->tfp);
                            while(fgets(tmpline,sizeof(tmpline),m->tfp)!=NULLCHAR)
                                fputs(tmpline,m->tfile);
                        }
                        MYFCLOSE(m->tfp);
  
            /* And add this first non-R: line */
                        fprintf(m->tfile,"%s\n",m->line);
                        if(m->line[strlen(m->line)-1] == CTLZ)
                            goto eol_ctlz;
                    }
                } else
#endif
                    fprintf(m->tfile,"%s\n",m->line);
                if(m->line[strlen(m->line)-1] == CTLZ)
                    goto eol_ctlz;
            } else {
                eol_ctlz:
#ifdef RLINE
                if(check_r) {
            /* Hmm, this means we never finished the R: headers
             * tmp file still open !
             */
                    MYFCLOSE(m->tfp);
                }
#endif
                done = 1; /* To indicate the difference between
               * mbxrecvline() returning -1 and /ex ! - WG7J
               * Now also used to indicate if the message should
               * be sent or not !
               */
        /* Now ask users if they want to send this ! - WG7J */
                if( Mbsendquery && !(m->sid & MBX_SID)) {
                    if(m->type == TELNET_LINK || m->type == TIP_LINK)
                        c = tkeywait("Send(N=no)?",0);
                    else  /* For AX.25 and NET/ROM connects */
                        c = mykeywait("Send(N=no)?",m);
                    if(c == -1 || c == 'n' || c == 'N') {
                        done = 0;   /* signal delete of message */
                        tputs(MsgAborted);
                    }
                }
                break;  /* all done */
            }
        }
        if(!done) {
        /* We did NOT get ^Z or /EX, but mbxrecvline returned -1 !!!
         * This means the connection is gone ! - WG7J
         * Now can also mean that the user doesn't want to send msg !
         */
            MYFCLOSE(m->tfile);
#ifdef RLINE
            if(check_r)
                MYFCLOSE(m->tfp);
#endif
            del_list(cclist);
            free(rhdr); /*Just in case*/
            return 0;
        }
    } else {
        fprintf(m->tfile,"----- Forwarded message -----\n\n");
        msgtofile(m);
        fprintf(m->tfile,"----- End of forwarded message -----\n");
    }
  
    free(rhdr);
    /* Insert customised signature if one is found */
    if(!(m->sid & MBX_SID)) {       /* not a forwarding BBS */
        sprintf(sigwork,"%s/%s.sig",Signature,
        m->tofrom ? m->tofrom : m->name);
        if((fp = fopen(sigwork,READ_TEXT)) != NULLFILE){
            while(fgets(sigwork,LINELEN,fp) != NULLCHAR)
                fputs(sigwork,m->tfile);
            fclose(fp);
        }
    }
  
    if((host = strrchr(m->to,'@')) == NULLCHAR) {
        host = Hostname;        /* use our hostname */
        if(m->origto != NULLCHAR) {
            /* rewrite_address() will be called again by our
             * SMTP server, so revert to the original address.
             */
            free(m->to);
            m->to = m->origto;
            m->origto = NULLCHAR;
        }
    }
    else
        host++; /* use the host part of address */
  
    /* make up full from name for work file */
    if(m->tofrom != NULLCHAR)
        sprintf(fullfrom,"%s%%%s@%s",m->tofrom, m->name, Hostname);
    else
        sprintf(fullfrom,"%s@%s",m->name,Hostname);
    if(cclist != NULLLIST && stricmp(host,Hostname) != 0) {
        fseek(m->tfile,0L,0);   /* reset to beginning */
        fail = queuejob(m->tfile,Hostname,cclist,fullfrom);
        del_list(cclist);
        cclist = NULLLIST;
    }
    addlist(&cclist,m->to,0);
    fseek(m->tfile,0L,0);
    fail += queuejob(m->tfile,host,cclist,fullfrom);
    del_list(cclist);
    MYFCLOSE(m->tfile);
    if(fail){
        if(!(m->sid & MBX_SID)) /* only when we're not a bbs */
            tputs("Couldn't queue msg!\n");
    } else {
        if(m->sid & MBX_SID)
            MbRecvd++;
        else {
            tputs("Msg queued\n");
            MbSent++;
        }
#ifdef notdef
    /* BID is now saved in the smtp server ! - WG7J */
        if(m->tomsgid != NULLCHAR && \
        (fp = fopen(Historyfile,APPEND_TEXT)) != NULLFILE) {
        /* Timestamp added to allow automatic expiry of bid file - WG7J */
            time(&now);
            fprintf(fp,"%s %ld\n",m->tomsgid,now); /* Save BID */
            fclose(fp);
        }
#endif
    }
    /* Instead of kicking off the smtp now, let it happen in a short time.
     * That way the user gets the prompt back immediately, and the smtp kick
     * will run when the user most likely is working on typing the next command.
     * this *should* improve the user's perception of system's speed, and
     * might speed up bbs forwarding a bit too... :-) - WG7J
     */
    {
        #define WAITTM 5000L
        extern struct timer Smtpcli_t;
        int32 t,orig_dur;
  
        stop_timer(&Smtpcli_t);
        orig_dur = Smtpcli_t.duration;
        t = read_timer(&Smtpcli_t);
        if(t <= 0  || t > WAITTM)
            set_timer(&Smtpcli_t,WAITTM);  /* set timer duration */
        if(Smtpcli_t.func == NULL) {    /* in case not set yet */
            Smtpcli_t.func = (void (*)__ARGS((void*)))smtptick;/* what to call on timeout */
            Smtpcli_t.arg = NULL;       /* dummy value */
        }
        start_timer(&Smtpcli_t);        /* and fire it up */
        Smtpcli_t.duration = orig_dur;  /* then back to previous interval */
    }
 #ifdef notdef
    smtptick(NULL);     /* wake SMTP to send that mail */
 #endif
  
    return 0;
}
  
  
#endif /* MAILCMDS */
