/* NOTE: because of size, the previous 'mailbox.c' and 'mailbox2.c'
 * have been split in 5 parts:
 * mboxcmd.c, containing the 'mbox' subcommands,
 * mailbox.c, containing general mailbox user commands,
 * mboxfile.c, containing file mailbox user commands,
 * mboxmail.c, containing mail mailbox user commands, and
 * mboxgate.c, containing gateway mailbox user commands.
 * 940215 - WG7J
 */
  
/* There is only one function in this mailbox code that depend on the
 * underlying protocol family, namely mbx_getname(). All the other
 * functions can hopefully be used without modification on other stream
 * oriented protocols than AX.25 or NET/ROM.
 *
 * SM0RGV 890506, most work done previously by W9NK
 *
 *** Changed 900114 by KA9Q to use newline mapping features in stream socket
 *      interface code; everything here uses C eol convention (\n)
 *
 *      Numerous new commands and other changes by SM0RGV, 900120
 *
 * Gateway function now support outgoing connects with the user's call
 * with inverted ssid. Users can connect to system alias as well...
 * See also several mods in socket.c,ax25.c and others
 * 11/15/91, WG7J/PA3DIS
 *
 * Userlogging, RM,VM and KM commands, and R:-line interpretation
 * added 920307 and later, Johan. K. Reinalda, WG7J/PA3DIS
 *
 * Inactivity timeout-disconnect added 920325 and later - WG7J
 *
 */
#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 MAILBOX
#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"
#include "mailutil.h"
#include "index.h"

#define FLUSHWAIT 20

struct mbx *Mbox;
int BbsUsers;
int Totallogins;
int MbMaxUsers;
extern int MbShowAliases;
extern struct cmds DFAR Cmds[];
  
static int mbx_getname __ARGS((struct mbx *m));
static int dombhelp __ARGS((int argc,char *argv[],void *p));
static int dombexpert __ARGS((int argc,char *argv[],void *p));
static int dombipheard __ARGS((int argc,char *argv[],void *p));
#ifdef AX25
static int dombjheard __ARGS((int argc,char *argv[],void *p));
#endif
#if defined FOQ_CMDS && defined TTYLINKSERVER
static int dochat __ARGS((int argc,char *argv[],void *p));
#endif
#if defined FOQ_CMDS && defined CALLCLI
static int dombcallbook __ARGS((int argc,char *argv[],void *p));
#endif

#ifdef RSYSOPSERVER
static char DFAR RSysopbanner[] = "\nRemote Login at %s - %s\n\n";
#endif
char Loginbanner[] = "\nJNOS (%s)\n\n";
char Mbwelcome[] = "\nWelcome %s,\n";
#ifdef MAILCMDS
char Mbbanner[] = "to the %s TCP/IP Mailbox (JNOS %s).\n";
#else
char Mbbanner[] = "to the %s TCP/IP Server (JNOS %s).\n";
#endif
char CurUsers[] = "Currently %d user%s.\n";
  
#if defined MAILCMDS || defined FILECMDS
char Howtoend[] = "End with /EX or ^Z in first column (^A aborts):\n";
char MsgAborted[] = "Msg aborted\n";
#endif
  
#ifdef MAILCMDS
char MbCurrent[] = "Current msg# %d.\n";
#endif
  
char Mbmenu[] = "?,"
#ifdef MAILCMDS
"A,"
#endif
"B,"
#ifdef GATECMDS
#if defined NETROM || defined AX25
"C,"
#endif
#endif
#ifdef CONVERS
"CONV,"
#endif
#ifdef FILECMDS
"D,"
#endif
#ifdef GATECMDS
"E,"
#endif
#ifdef FOQ_CMDS
"F,"
#endif
"H,I,IH,IP,"
#ifdef AX25
"J,"
#endif
#ifdef MAILCMDS
"K,L,"
#endif
"M,"
#if defined GATECMDS && defined NETROM
"N,NR,"
#endif
#if defined FOQ_CMDS && defined TTYLINKSERVER
"O,"
#endif
#ifdef GATECMDS
#ifdef AX25
"P,"
#endif
"PI,"
#endif
#if defined FOQ_CMDS && defined CALLCLI
"Q,"
#endif
#ifdef MAILCMDS
"R,S,"
#endif
#ifdef GATECMDS
"T,"
#endif
#ifdef FILECMDS
"U,"
#endif
#ifdef MAILCMDS
"V,"
#endif
#ifdef FILECMDS
"W,"
#endif
"X"
#ifdef FILECMDS
",Z"
#endif
" >\n";
  
extern char Mbnrid[];
  
char Longmenu[] =
#ifdef MAILCMDS
"Mail   : Area Kill List Read Send Verbose\n"
#endif
  
#ifdef GATECMDS
  
"Gateway:"
#if defined AX25 || defined NETROM
" Connect"
#endif
" Escape"
#ifdef NETROM
" Nodes NRroute"
#endif
#ifdef AX25
" Ports"
#endif
" PIng Telnet\n"
  
#endif /* GATECMDS */
  
#ifdef FILECMDS
"File   : Download Upload What Zap\n"
#endif
  
"General: ?-Help Bye"
#ifdef CONVERS
" CONVers"
#endif
#ifdef FOQ_CMDS
" Finger"
#endif
" Help Info IHeard\n"
"         IProute"
#ifdef AX25
" Jheard"
#endif
" Mbox"
#if defined FOQ_CMDS && defined TTYLINKSERVER
" Operator"
#endif
#if defined FOQ_CMDS && defined CALLCLI
" Query"
#endif
" Xpert\n\n";
  
  
/* This is called by the finger-daemon */
void
listusers(s)
int s;
{
    int outsave;
    struct mbx m;
  
    m.privs = 0;
    m.stype = ' ';
  
#ifdef notdef
    usputs(s,"\nCurrent remote users:\n");
#endif
    outsave = Curproc->output;
    Curproc->output = s;
    dombusers(0,NULLCHARP,&m);
    Curproc->output = outsave;
}
  
struct mbx *
newmbx()
{
    struct mbx *m,*new;
  
    if (MbMaxUsers && BbsUsers >= MbMaxUsers)
        return NULLMBX;
    if((new = (struct mbx *) calloc(1,sizeof(struct mbx))) == NULLMBX)
        return NULLMBX;
    BbsUsers++;
    /* add it the list */
    if((m=Mbox) == NULLMBX)
        Mbox = new;
    else {
        while(m->next)
            m=m->next;
        m->next = new;
    }
    return new;
}
  
static int
mbx_getname(m)
struct mbx *m;
{
    char *cp;
#ifdef notdef
    FILE *tfp;
#endif
    union sp sp;
    char tmp[MAXSOCKSIZE];
    int len = MAXSOCKSIZE;
    int anony = 0;
    int oldmode;
    int founddigit=0;
    int count=0;
#ifdef AX25
    int32 flags;
    int ax_25 = 0;
    struct usock *up;
#endif
#ifdef MD5AUTHENTICATE
    int32 t;
#endif
  
    sp.p = tmp;
    sp.sa->sa_family = AF_LOCAL;    /* default to AF_LOCAL */
    getpeername(m->user,tmp,&len);
    m->family = sp.sa->sa_family;
    m->path = mallocw(MBXLINE);
    m->morerows = 20;  /* formerly just for telnet ... now default for all */
    /* This is one of the two parts of the mbox code that depends on the
     * underlying protocol. We have to figure out the name of the
     * calling station. This is only practical when AX.25 or NET/ROM is
     * used. Telnet users have to identify themselves by a login procedure.
     */
    switch(sp.sa->sa_family){
#ifdef  AX25
        case AF_AX25:
        /* If this is not to the convers call, and this port is
         * set for NO_AX25, then disconnect ! - WG7J
         */
            if((m->type != CONF_LINK) && ((up = itop(m->user)) != NULLUSOCK) ) {
                if((flags=up->cb.ax25->iface->flags) & NO_AX25)
                    return -1;
            }
            ax_25 = 1;
        /* note fallthrough */
        case AF_NETROM:
        /* NETROM and AX25 socket address structures are "compatible" */
        /* Save user call, in case user wants to use gateway function */
            memcpy(m->call,sp.ax->ax25_addr,AXALEN);
            m->call[ALEN] &= 0xfe;/*Make sure E-bit isn't set !*/
            pax25(m->name,sp.ax->ax25_addr);
            cp = strchr(m->name,'-');
            if(cp != NULLCHAR)                      /* get rid of SSID */
                *cp = '\0';
        /* SMTP wants the name to be in lower case */
#ifdef __TURBOC__
            (void)strlwr(m->name);
#else
            cp = m->name;
            while(*cp){
                if(isupper(*cp))
                    *cp = tolower(*cp);
                ++cp;
            }
#endif
            anony = 1;
        /* Try to find the privileges of this user from the userfile */
            if((m->privs = userlogin(m->name,m->line,&m->path,MBXLINE,
              &anony,ax_25 ? "ax25perm" : "nrperm")) == -1){
                m->privs = 0;
                free(m->path);
                m->path = NULLCHAR;
            }
            if(m->privs & EXCLUDED_CMD)
                return -1;
            if(ax_25)
                if(((flags & USERS_ONLY) && (m->privs & IS_BBS)) ||
                    ((flags & BBS_ONLY) && !(m->privs & IS_BBS)) ||
                    ((flags & SYSOP_ONLY) && !(m->privs & SYSOP_CMD)))
                    return -1;
#ifdef AX25PASSWORD
            /* KF5MG - non-bbses get LOGIN: PASSWORD: prompts. */
            if(m->privs & IS_BBS
#ifndef NRPASSWORD
               || !ax_25
#endif
              )
#endif /* AX25PASSWORD */
            return 0;
#ifdef AX25PASSWORD
            if(m->path == NULLCHAR) m->path = mallocw(MBXLINE);
#ifdef NRPASSWORD
            ax_25++;   /* 1 => NetRom, 2 => ax.25 */
#endif
            /* Note we fall into the AF_LOCAL case */
#endif /* AX25PASSWORD */
#endif /* AX25 */
        case AF_LOCAL:
        case AF_INET:
            m->state = MBX_LOGIN;
#ifdef RSYSOPSERVER
            if(m->type == RSYSOP_LINK)
               tprintf(RSysopbanner,Hostname,Version);
            else {
#endif
            tprintf(Loginbanner,Hostname);
            if(Mtmsg != NULLCHAR)
                tputs(Mtmsg);
#ifdef RSYSOPSERVER
            }
#endif
            for(;;){
            /* Maximum of 3 tries - WG7J */
                if(count++ == 3)
                    return -1;
                oldmode = sockmode(m->user,SOCK_ASCII);
                tputs("login: ");
                tflush();
                if(mbxrecvline(m) == -1)
                    return -1;
                if(*m->line == 4) /* Control-d */
                    return -1;
                if(*m->line == '\0')
                    continue;
            /* Chop off after name lenght */
                m->line[MBXNAME] = '\0';
  
            /* add a little test to avoid 'Mailfile busy' syndrome - WG7J
             * Check for characters illegal in MS-DOS file names.
             */
                for(cp = m->line;*cp != '\0';cp++) {
#ifdef MSDOS
                    if(dosfnchr(*cp) == 0)
                        break;
#endif
#ifdef UNIX
                    if(*cp == '/') break;
#endif
#ifndef __TURBOC__
                    *cp = tolower(*cp);   /* use lowercase henceforth */
#endif
                }
                if(*cp != '\0')
                    continue;
#ifdef __TURBOC__
                (void)strlwr(m->line);   /* use lowercase henceforth */
#endif
                strcpy(m->name,m->line);
#ifdef MD5AUTHENTICATE
                time(&t);
#endif
#ifdef AX25PASSWORD
                /* KF5MG - send a password: prompt to ax.25 users */
                if(ax_25) {
                   anony = 0;
#ifdef MD5AUTHENTICATE
                   tprintf("Password [%lx] : ", t);
#else
                   tputs("Password: ");
#endif /* MD5AUTHENTICATE */
                   tflush();
                   if(mbxrecvline(m) == -1)
                       return -1;
                } else {
#endif /* AX25PASSWORD */
#ifdef MD5AUTHENTICATE
                tprintf("Password [%lx] : %c%c%c",t,IAC,WILL,TN_ECHO);
#else
                tprintf("Password: %c%c%c",IAC,WILL,TN_ECHO);
#endif
                SET_OPT(m,TN_ECHO);
                tflush();
                sockmode(m->user,SOCK_BINARY);
                if(mbxrecvline(m) == -1)
                    return -1;
                tprintf("%c%c%c",IAC,WONT,TN_ECHO);
                RESET_OPT(m,TN_ECHO);
                sockmode(m->user,oldmode);
                tputc('\n');
                tflush();
#ifdef AX25PASSWORD
                }
#endif
            /* This is needed if the password was sent before the
             * telnet no-echo options were received. We need to
             * flush the old sequence from the input buffers, sigh
             */
                if(socklen(m->user,0))/* discard any remaining input */
                    recv_mbuf(m->user,NULL,0,NULLCHAR,0);
                cp = "tcpperm";   /* alternative to univperm */
#ifdef AX25PASSWORD
                if(ax_25) cp = "ax25perm";
#ifdef NRPASSWORD
                if(ax_25==1) cp = "nrperm";
#endif /* NRPASSWORD */
#endif /* AX25PASSWORD */
#ifdef TIPSERVER
                if(m->type==TIP_LINK) cp = "tipperm";
#endif
#ifdef MD5AUTHENTICATE
                memcpy(m->path, (char *)&t, sizeof(t));  /* hide it in path */
#endif
                if((m->privs = userlogin(m->name,m->line,&m->path,MBXLINE,
                    &anony,cp)) != -1){
                    if(anony)
                        log(m->user,"MBOX login: %s Password: %s",m->name,m->line);
                    else
                        log(m->user,"MBOX login: %s",m->name);
                    if(m->privs & EXCLUDED_CMD)
                        return -1;
#ifdef AX25
            /*try to set the name as the user-call.
             *this is a very crude test! Be careful...
             *Login must have at leat 1 digit (0-9) in it,
             *and it must be possible to convert it to a call.
             *if this doesn't work, disallow the gateway command,
             *no matter if this was allowed by priviledges or not.
             *Be careful, some one with login name '4us' and
             *permission set to allow gateway/netrom, will
             *go out as '4us-15' or '4us-0' !!!!!
             *11/15/91 WG7J/PA3DIS
             */
                    for(cp=m->name;*cp != '\0';cp++)
                        if(isdigit((int)*cp))
                            break;
                    if(*cp != '\0')
                        founddigit = 1;
                    if( (setcall(m->call,m->name) == -1) || (!founddigit) ) {
                        m->privs &= ~(AX25_CMD|NETROM_CMD);
                    }
#else
                    m->privs &= ~(AX25_CMD|NETROM_CMD);
  
#endif /* AX25 */
                    return 0;
                }
                tputs("Login incorrect\n");
                log(m->user,"MBOX Login failed: %s",m->name);
#ifdef MAILERROR
                mail_error("MBOX Login failed: %s, pw %s",m->name,m->line);
#endif
                *m->name = '\0';        /* wipe any garbage */
            }
    }
    return 0;
}
  
/* put up the prompt */
void
putprompt(m)
struct mbx *m;
{
    char area[64];
    char *cp1,*cp2;
  
#ifdef MAILCMDS
#ifdef FBBFWD
    if(m->sid & MBX_FBBFWD) {
       /* do nothing */
    } else
#endif
    if(m->sid & MBX_SID)
        tputs(">\n");
    else {
#endif
        if(m->sid & MBX_NRID)
            tputs(Mbnrid);
#ifdef MAILCMDS
        if(m->sid & MBX_AREA) {
            cp1 = m->area;
            cp2 = area;
            /* Convert / and \ into . */
            while(*cp1 != '\0') {
                if(*cp1=='/')
                    *cp2 = '.';
                else
                    *cp2 = *cp1;
                cp1++;
                cp2++;
            }
            *cp2 = '\0';
            tprintf("Area: %s ",area);
        }
#endif
        if(m->sid & MBX_EXPERT) {
#ifdef MAILCMDS
            tprintf("(#%d) ",m->current);
#endif
            tputs(">\n");
        }
        else {
#ifdef MAILCMDS
            tprintf(MbCurrent,m->current);
#endif
            if(MbShowAliases && AliasList) {
                struct alias *a;
  
                for(a=AliasList;a;a=a->next)
                    tprintf("%s,",a->name);
            }
            tputs(Mbmenu);
        }
  
#ifdef MAILCMDS
    }
#endif
}
  
#ifdef MB_XFBB_KLUDGE
/* Incoming mailbox session for a port flagged as cronly (see tipmail.c) */
void
mbx_incom_cr(s,t,p)
int s;
void *t;
void *p;
{
    struct usock *up;
    if ((up=itop(s)) != NULLUSOCK)
        strcpy(up->eol,"\r" /*AX_EOL*/);  /* just CR */

    mbx_incom(s,t,p);
}
#endif /* MB_FBB_KLUDGE */

/* Incoming mailbox session */
void
mbx_incom(s,t,p)
int s;
void *t;
void *p;
{
    struct tipcb *tip;
    struct mbx *m;
    struct alias *a;
    char tmp[AXBUF];
    int rval;
    FILE *fp;
    int newpriv = 0;
  
/*
    struct usock *up;
    if((up = itop(s)) != NULLUSOCK)
    if(up->iface->flags & NO_AX25)
 */
  
    sockmode(s,SOCK_ASCII);
    sockowner(s,Curproc);   /* We own it now */
    /* Secede from the parent's sockets, and use the network socket that
     * was passed to us for both input and output. The reference
     * count on this socket will still be 1; this allows the domboxbye()
     * command to work by closing that socket with a single call.
     * If we return, the socket will be closed automatically.
     */
    close_s(Curproc->output);
    close_s(Curproc->input);
    Curproc->output = Curproc->input = s;
  
    if((m = newmbx()) == NULLMBX){
#ifdef MBOX_FULL_MSG
        tputs(MBOX_FULL_MSG);
#else
        tputs("Too many mailbox sessions\n");
#endif
        rval = FLUSHWAIT;   /* limit attempts to flush msg */
        while(socklen(s,1) > 0 && rval--)
            pause(500L);
        return;
    }
  
    /* We'll do our own flushing right before we read input */
    setflush(s,-1);
  
    m->proc = Curproc;
    m->user = s;
    m->escape = 20;     /* default escape character is Ctrl-T */
    m->type = (t == NULL) ? TELNET_LINK : (int) t;
  
#ifdef TIPSERVER
#ifdef XMODEM
    if(m->type == TIP_LINK) {
        tip = (struct tipcb *) p;
        tip->raw=0;
        m->tip=tip;
    }
#endif
#endif
  
    /* discard any remaining input */
    /*
    while(socklen(s,0))
    recv_mbuf(s,NULL,0,NULLCHAR,0);
    */
  
    /* get the name of the remote station */
    if(mbx_getname(m) == -1) {
        exitbbs(m);
        return;
    }
    Totallogins++;
    log(s,"MBOX open");
  
#ifdef RSYSOPSERVER
    if(m->type == RSYSOP_LINK) {
       m->state = MBX_CMD;         /* start in command state */
       tputc ('\n');
       dosysop (1, (char **)0, (void *)m);
       log(m->user, "MBOX exit: %s", m->name);     /* N5KNX: log exits */
       exitbbs(m);
       return;
    }
#endif

    if(m->privs & IS_BBS)
        m->sid |= MBX_SID; /*force bbs status*/
    else if(m->privs & IS_EXPERT)
        m->sid |= MBX_EXPERT;
#ifdef FBBCMP
    if(m->privs & NO_FBBCMP)
        m->sid |= MBX_FOREGO_FBBCMP;
#endif
  
    loguser(m);
    m->state = MBX_CMD;     /* start in command state */
#ifdef MAILCMDS
#if defined(MBFWD) && defined(FBBFWD)
#ifdef FBBCMP
    if(Mfbb==2 && !(m->sid&MBX_FOREGO_FBBCMP))
        tputs(MboxIdFC);
    else
#endif // FBBCMP
    if(Mfbb)
        tputs(MboxIdF);
    else
#endif /* FBBFWD */
    tputs(MboxId);
#endif
  
    /* Say 'hello' only if user is not a bbs - WG7J */
#ifdef MAILCMDS
    if(!(m->sid & (MBX_SID+MBX_EXPERT))) {
#endif
  
#ifdef USERLOG
        tprintf(Mbwelcome,m->username ? m->username : m->name);
#else
        tprintf(Mbwelcome,m->name);
#endif
  
#if defined AX25 || defined NETROM
        if(m->family == AF_INET)
#endif
            tprintf(Mbbanner,Hostname,Version);
#if defined AX25 || defined NETROM
        else
            tprintf(Mbbanner,pax25(tmp,Bbscall),Version);
#endif
  
        /* How many users are there currently ? */
        tprintf(CurUsers,BbsUsers,BbsUsers == 1 ? "" : "s");
  
#ifdef MAILCMDS
        /* Do we accept third party mail ? */
        if(!ThirdParty)
            tputs(Mbwarning);
#endif
        tputc('\n');
  
        /* Is there a message of the day ? */
        if((fp = fopen(Motdfile,READ_TEXT)) != NULLFILE) {
            sendfile(fp,m->user,ASCII_TYPE,0, m);
            fclose(fp);
        }
  
#ifdef MAILCMDS
    }
    if(!(m->sid & MBX_SID)) {
        char *aargv[2];
        /* Enable our local message area,
         * only if we're not a bbs - WG7J
         */
        aargv[1] = m->name;
        doarea(2,aargv,m);
#ifdef USERLOG
        /* Tell about new arrived mail in message areas - WG7J */
        if(Mbnewmail)
            listnewmail(m,1);
  
#ifdef REGISTER
        /* See if the username is empty. If so, the user hasn't
         * registerd yet, so we need to beep and remind.
         */
        if(MbRegister && !m->username)
            tprintf("\n\007Please type 'REGISTER' at the > prompt.\n");
#endif /* REGISTER */
#endif /* USERLOG */
    }
#endif /* MAILCMDS */
  
    /* Send prompt */
    putprompt(m);
  
    /* now get commands */
    while(mbxrecvline(m) != -1){
#ifdef MAILCMDS
        /* Only tell about new mail when in our own area - WG7J */
        if(!(m->sid & MBX_SID)){
            if(isnewprivmail(m) > 0L)
                newpriv = 1;
            else
                newpriv = 0;
            /* Do not check mailfile if we're bbs - WG7J*/
            scanmail(m);
        }
#endif
        /* check for an alias - WG7J */
        if((a=findalias(m->line)) != NULL) {
            strcpy(m->line,a->cmd);
            if(m->line[0]=='@') {  /* VK2ANK: expansion begins with '@' ? */
                cmdparse(Cmds, m->line+1, NULL);  /* then execute as a console cmd */
                goto skip_mbx_parse;
            }
        }
  
        if((rval = mbx_parse(m)) == -2)
            break;
        if(rval == 1)
            tputs("Bad syntax.\n");
skip_mbx_parse:
#ifdef MAILCMDS
        if(newpriv) {
            tputs("You have new mail. ");
            if(m->areatype != PRIVATE)
                tprintf("Change area with 'A %s'. ", m->name);
            tputs("Please Kill when read!\n");
        }
#endif
        putprompt(m);
        m->state = MBX_CMD;
    }
    log(m->user, "MBOX exit: %s", m->name);	/* N5KNX: log exits */
    exitbbs(m);
}
  
void
exitbbs(m)
struct mbx *m;
{
    struct mbx *mp,*pp;
    int i;
  
    /* Since we've added an ax.25 T2 timer, which will delay the flushing of any
       queued output, and we could lose some of this output when we close the
       user's socket [this sends an immediate disconnect and frees queued o/p]
       we'll try to force out anything queued now.  But we must be careful not
       to get stuck here forever (ie, circuit dropped out but we have infinite
       retries set). Main idea is to shutdown, but flush if we can! n5knx/k5jb
     */
    i = FLUSHWAIT;
    while(socklen(m->user,1) > 0 && i--)   /* wait for I frame to be ACKed - K5JB */
        pause(500L);

    /* Moving the socket close call to here
     * will send a disconnect to the user before cleaning up
     * the user's data structure. This gives a faster response perception
     * to the user - WG7J
     * N5KNX: but we must be careful to reset Curproc->{input,output} since
     * otherwise killproc() will try again to close these sockets, and by
     * then some other process may own it.  This sure is a kludge!
     */
    close_s(Curproc->output);
    if (itop(Curproc->output) == NULLUSOCK) {
        if (Curproc->input == Curproc->output) Curproc->input = -1;
        Curproc->output = -1;
    }
  
  
#ifdef MAILCMDS
    closenotes(m);
    free(m->to);
    free(m->tofrom);
    free(m->origto);
    free(m->origbbs);
    free(m->subject);
    free(m->date);
    free(m->tomsgid);
#endif
    free(m->path);
    free(m->startmsg);
#ifdef USERLOG
    free(m->username);
    free(m->IPemail);
    free(m->homebbs);
#endif
#ifdef MAILCMDS
    /* Close the tempfiles if they are not nullpointers - WG7J */
    if(m->tfile != (FILE *) 0)
        fclose(m->tfile);
    if(m->tfp != (FILE *) 0)
        fclose(m->tfp);
    if(m->mfile != (FILE *) 0)
        fclose(m->mfile);
    if(m->stdinbuf != NULLCHAR)
        free(m->stdinbuf);
    if(m->stdoutbuf != NULLCHAR)
        free(m->stdoutbuf);
    free((char *)m->mbox);
#endif
    /* now free it from list */
    for(mp=Mbox,pp=NULLMBX;mp && mp!=m;pp=mp,mp=mp->next);
    if(!mp)
        /* what happened ??? */
        return;
    if(pp==NULLMBX)     /* first one on list */
        Mbox = Mbox->next;
    else
        pp->next = m->next;
#ifdef FBBCMP
/* If this was an FBB system capable of compression, we most likely have msgs
   in the SMTP queue that we delayed processing until now.
*/
    if (m->sid & MBX_FBBCMP) smtptick(NULL);    /* start processing SMTP queue */
#endif
    free((char *)m);
    BbsUsers--;
}
  
/**********************************************************************/
  
static struct cmds DFAR Mbcmds[] = {
#ifdef MAILCMDS
    "",             doreadnext,     0, 0, NULLCHAR,
#endif
    "?",            dombhelp,       0, 0, NULLCHAR,
#ifdef MAILCMDS
    "area",     doarea,     0, 0, NULLCHAR,
#endif
    "alias",    dombalias,  0, 0, NULLCHAR,
    "bye",      domboxbye,  0, 0, NULLCHAR,
#ifdef GATECMDS
#if defined AX25 || defined NETROM
    "connect",  dombconnect,0, 0, NULLCHAR,
#endif
#endif
#ifdef CONVERS
    "convers",  dombconvers,0, 0, NULLCHAR,
#endif
#ifdef FILECMDS
#ifdef XMODEM
    "download", dodownload, 0, 0, NULLCHAR,
#else
    "download", dodownload, 0, 0, NULLCHAR,
#endif
#endif
#ifdef GATECMDS
    "escape",       dombescape,     0, 0, NULLCHAR,
#endif
#ifdef FOQ_CMDS
    "finger",       dombfinger,     0, 0, NULLCHAR,
#endif
    "help",         dombhelp,       0, 0, NULLCHAR,
    "info",         dombhelp,       0, 0, NULLCHAR,
    "iheard",   dombipheard,0, 0, NULLCHAR,
    "iproute",  dombiproute,0, 0, NULLCHAR,
#ifdef  AX25
    "jheard",   dombjheard, 0, 0, NULLCHAR,
#endif
#ifdef MAILCMDS
    "kill",     dodelmsg,   0, 0, NULLCHAR,
    "list",     dolistnotes,0, 0, NULLCHAR,
#endif
    "mboxuser", dombusers,  0, 0, NULLCHAR,
#if defined GATECMDS && defined NETROM
    "nodes",    dombnrnodes,0, 0, NULLCHAR,
    "nroutes",  dombnrneighbour, 0, 0, NULLCHAR,
#endif
#if defined FOQ_CMDS && defined TTYLINKSERVER
    "operator", dochat,     0, 0, NULLCHAR,
#endif
#if defined GATECMDS && defined AX25
    "ports",    dombports,  0, 0, NULLCHAR,
#endif
#ifdef GATECMDS
    "ping",     dombping,   0, 2, "PI <host> [<len>] [<timeout>]",
#endif
#if defined FOQ_CMDS && defined CALLCLI
    "query", dombcallbook, 0, 2, "Q callsign\nMultiple callsigns allowed per line",
#endif
#ifdef MAILCMDS
    "read",     doreadmsg,  0, 0, NULLCHAR,
    "send",         dosend,         0, 0, NULLCHAR,
#if defined USERLOG && defined REGISTER
    "register",     doregister,     0, 0, NULLCHAR,
#endif
#endif
#ifdef GATECMDS
    "telnet",   dombtelnet, 0, 2, "T hostname",
#endif
#ifdef FILECMDS
#ifdef XMODEM
    "upload",   dombupload, 0, 2, "U[X] <filename>",
#else
    "upload",   dombupload, 0, 2, "U <filename>",
#endif
#endif
#ifdef MAILCMDS
    "verbose",  doreadmsg,  0, 0, NULLCHAR,
#endif
#ifdef FILECMDS
    "what",     dowhat,     0, 0, NULLCHAR,
#endif
    "xpert",    dombexpert, 0, 0, NULLCHAR,
#ifdef FILECMDS
    "zap",      dozap,      0, 2, "Z filename",
#endif
#ifdef MAILCMDS
    "[",        dosid,      0, 0, NULLCHAR,
#ifdef MBFWD
    "f>",       dorevfwd,   0, 0, NULLCHAR,
#ifdef FBBFWD
#ifdef FBBCMP
    "fa",       dofbbfwd,   0, 0, NULLCHAR,
#endif
    "fb",       dofbbfwd,   0, 0, NULLCHAR,
    "ff",       dofbbfwd,   0, 0, NULLCHAR,
    "fq",       domboxbye,  0, 0, NULLCHAR,
#endif
#endif
#endif
    "@",        dosysop,    0, 0, NULLCHAR,
    "***",      dostars,    0, 0, NULLCHAR,
    ";",        dombsemicolon, 0, 0, NULLCHAR,
    NULLCHAR,   NULLFP((int,char **,void *)), 0, 0, "Huh?",
};
  
/* "twocmds" defines the MBL/RLI two-letter commands, eg. "SB", "SP" and so on.
 * They have to be treated specially since cmdparse() wants a space between
 * the actual command and its arguments.
 * "SP FOO" is converted to "s  foo" and the second command letter is saved
 * in m->stype. Longer commands like "SEND" are unaffected, except for
 * commands starting with "[", i.e. the SID, since we don't know what it will
 * look like.
 */
static char twocmds[] =
#ifdef MAILCMDS
"aklrsv"
#endif
#ifdef FILECMDS
"du"
#endif
"[mx";
  
int
mbx_parse(m)
struct mbx *m;
{
    char *cp;
    int i;
    char *newargv[2];
  
    /* Translate entire buffer to lower case */
#ifdef __TURBOC__
    (void)strlwr(m->line);
#else
    for (cp = m->line; *cp != '\0'; ++cp)
        if(isupper(*cp))
            *cp = tolower(*cp);
#endif
    /* Skip any spaces at the begining */
    for(cp = m->line;isspace(*cp);++cp)
        ;
    m->stype = ' ';
    if(*cp != '\0' && *(cp+1) != '\0') {
        for(i=0; i<strlen(twocmds); ++i){
            if(*cp == twocmds[i] && (isspace(*(cp+2)) || *(cp+2) == '\0'
            || *cp == '[')){
                if(islower(*(++cp)))
                    m->stype = toupper(*cp); /* Save the second character */
                else
                    m->stype = *cp;
                *cp = ' ';
                break;
            }
        }
    }
#ifdef MAILCMDS
    /* See if the input line consists solely of digits */
    cp = m->line;
    for(cp = m->line;isspace(*cp);++cp)
        ;
    newargv[1] = cp;
    for(;*cp != '\0' && isdigit(*cp);++cp)
        ;
    if(*cp == '\0' && strlen(newargv[1]) > 0) {
        newargv[0] = "r";
        return doreadmsg(2,newargv,(void *)m);
    } else
#endif
        return cmdparse(Mbcmds,m->line,(void *)m);
}
  
/* This works like recvline(), but telnet options are answered and the
 * terminating newline character is not put into the buffer. If the
 * incoming character equals the value of escape, any queued input is
 * flushed and -2 returned.
 *
 * mbxrecvline() now can gobble up suboptions - 94/02/14 VE4WTS
 * OH2BNS/N5KNX: don't test for telnet options unless TCP link type
 */
int
mbxrecvline(m)
struct mbx *m;
{
    int s = m->user;
    int escape = m->escape;
    char *buf = m->line;
    int c, cnt = 0, opt,cl;
  
    if(buf == NULLCHAR)
        return 0;
    tflush();
    alarm(Mbtdiscinit*1000L);   /* Start inactivity timeout - WG7J */
    while((c = recvchar(s)) != EOF){
        if(c == IAC &&          /* Telnet command escape */
           (m->type == TELNET_LINK || m->type == TIP_LINK || m->type == RSYSOP_LINK)) {  /* OH2BNS: makes sense? */
            if((c = recvchar(s)) == EOF)
                break;
            if(c >= 250 && c < 255 && (opt = recvchar(s)) != EOF){
                if (opt <= MAXTELNETOPT)  /*  range we can handle ? */
                  switch(c){
                    case SB:
                        opt=recvchar(s); /* Get the real option */
                        if(opt==EOF)
                            break;
                        cl=opt;
                        c=recvchar(s);
                    /* Gobble up until we see IAC SE */
                        while((c!=EOF) && !(cl==IAC && c==SE)){
                        /* maybe check for timeout here, in case someone
                           happened to send a binary file with the IAC SB
                           sequence in it. */
                            cl=c; /* keep track of second last char read */
                            c=recvchar(s);
                        }
                    /* and tell the client where to go... */
                    /* tprintf("%c%c%c",IAC,WONT,opt); */
                        break;
                    case WILL:
                        if(opt==TN_LINEMODE){
                        /* we WANT linemode */
                            if (!OPT_IS_DEFINED(m,opt) || !OPT_IS_SET(m,opt)) {
                                tprintf("%c%c%c",IAC,DO,opt);
                                SET_OPT(m,opt);
                            }
                        /* Tell client to do editing */
                                tprintf("%c%c%c%c%c%c%c",IAC,SB,TN_LINEMODE,1,1,IAC,SE);
                        } else if (!OPT_IS_DEFINED(m,opt) || !OPT_IS_SET(m,opt)) {
                            tprintf("%c%c%c",IAC,DONT,opt);
                            RESET_OPT(m,opt);
                        }
                        break;
                    case WONT:
                        if (!OPT_IS_DEFINED(m,opt) || OPT_IS_SET(m,opt)) {
                            tprintf("%c%c%c",IAC,DONT,opt);
                            RESET_OPT(m,opt);
                        }
                        break;
                    case DO:
                        if (!OPT_IS_DEFINED(m,opt) || !OPT_IS_SET(m,opt)) {
                            tprintf("%c%c%c",IAC,WONT,opt);
                            RESET_OPT(m,opt);
                        }
                        break;
                    case DONT:
                        if (!OPT_IS_DEFINED(m,opt) || OPT_IS_SET(m,opt)) {
                            tprintf("%c%c%c",IAC,WONT,opt);
                            RESET_OPT(m,opt);
                        }
                        /*break;*/
                }
                tflush();
                alarm(Mbtdiscinit*1000L);   /* restart inactivity timeout */
                continue;
            }
            else if(c != IAC) {  /* telnet non-option cmd "c" or EOF read */
                alarm(Mbtdiscinit*1000L);   /* restart inactivity timeout */
                continue;
            }
        }
        /* ordinary character or escaped IAC */
        if(c == '\r' || c == '\n')
            break;
        if(uchar(c) == escape && !(m->sid & MBX_SID)){ /* treat esc from BBS as data */
            if(socklen(s,0)) /* discard any remaining input */
                recv_mbuf(s,NULL,0,NULLCHAR,0);
            cnt = -2;
            break;
        }
        /* Handle <bs> and <del> chars - from wa7tas */
        if((c == 8 || c == 127) && cnt > 0) {
            *--buf = 0;
            cnt--;
        } else if (c) {  /* discard NULs */
            *buf++ = c;
            ++cnt;
        }
        if(cnt == MBXLINE - 1)
            break;
    }
    alarm(0L);    /* disable inactivity timeout */
    if(c == EOF && cnt == 0)
        return -1;
    *buf = '\0';
    return cnt;
}
  
/* New forwarding option, simply ignore all data - WG7J */
int
dombsemicolon(int argc,char *argv[],void *p) {
  
    return 0;
}
  
/* Determine what type of prompt is optimal, ie, can we read just one char? */
int
charmode_ok(m)
struct mbx *m;
{
#ifndef LINEMODE_PROMPT_ALWAYS
    if (m->type == TELNET_LINK || m->type == TIP_LINK) {
#ifdef notdef
        if ((up=itop(m->user)) != NULLUSOCK && up->type == TYPE_TCP) {
	  /* up->cb.tcb->??? can't get to telnet->session->ttystate.echo*/
        }
#endif
        if (!(m->sid & MBX_LINEMODE)) return 1;   /* char mode OK (see XP cmd) */
    }
#endif /* LINEMODE_PROMPT_ALWAYS */
    return 0;
}

  
int
domboxbye(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    struct mbx *m;
  
    m = (struct mbx *)p;
  
#ifdef USERLOG
    updatedefaults(m);       /* moved here so we also track BBSes */

#ifdef MAILCMDS
    setlastread(m);
#endif
#endif /* USERLOG */

    /* for bbs's, just disconnect */
    if(m->sid & MBX_SID)
        return -2;
  
    /* Now say goodbye */
    if(!(m->privs & IS_EXPERT))
        tprintf("\nThank you %s, for calling %s JNOS.\n",
#ifdef USERLOG
        m->username ? m->username : m->name,
#else
        m->name,
#endif
        Hostname);
#ifdef TIPSERVER
    if(m->type == TIP_LINK)
        tputs("Please hang up now.\n");
#endif
    tflush();
    return -2;      /* signal that exitbbs() should be called */
}
  
static int
dombhelp(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    char buf[255];
    int i;
    FILE *fp;
    struct mbx *m = (struct mbx *)p;
    struct alias *a;
  
    if(*argv[0] == '?') {
        if(MbShowAliases && AliasList) {
  
            tputs("Aliases:");
            for(a=AliasList;a;a=a->next)
                tprintf(" %s",a->name);
            tputc('\n');
        }
        tputs(Longmenu);
        return 0;
    }
  
    buf[0] = '\0';
    if(argc > 1) {
        for(a=AliasList;a;a=a->next)
            if(!stricmp(a->name,argv[1])) {
                sprintf(buf,"%s/%s.hlp",Helpdir,argv[1]); /* use lower-case */
                break;
            }
        if (! a)
          for(i=0; Mbcmds[i].name != NULLCHAR; ++i)
            if(!strncmp(Mbcmds[i].name,argv[1],strlen(argv[1]))) {
                sprintf(buf,"%s/%s.hlp",Helpdir,Mbcmds[i].name);
                break;
            }
    }
    if(buf[0] == '\0')
        if(*argv[0] == 'i') {
            /* INFO command */
            tprintf(Nosversion,Version);
            sprintf(buf,"%s/info.hlp",Helpdir);
        } else
            sprintf(buf,"%s/help.hlp",Helpdir);
  
    if((fp = fopen(buf,READ_TEXT)) != NULLFILE) {
        sendfile(fp,Curproc->output,ASCII_TYPE,0,m);
        fclose(fp);
    } else {
        if(*argv[0]!='i')
            tputs("No help available.\n");
    }
    return 0;
}
  
extern void dumproute __ARGS((struct route *rp,char *p));
extern char RouteHeader[];
  
/* Show non-private routes only */
int
dombiproute(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    int i,bits;
    struct route *rp;
    struct mbx *m = (struct mbx *)p;
    char buf[85];
  
    if(m->privs & NO_LISTS) {
        tputs(Noperm);
        return 0;
    }
  
    tputs(RouteHeader);
    for(bits=31;bits>=0;bits--){
        for(i=0;i<HASHMOD;i++){
            for(rp = Routes[bits][i];rp != NULLROUTE;rp = rp->next){
                if(!(rp->flags & RTPRIVATE)) {
                    dumproute(rp,buf);
                    if(tprintf("%s\n",&buf[4]) == EOF)
                        return 0;
                }
            }
        }
    }
    if(R_default.iface != NULLIF && !(R_default.flags & RTPRIVATE)) {
        dumproute(&R_default,buf);
        if(tprintf("%s\n",&buf[4]) == EOF)
            return 0;
    }
    return 0;
}
  
  
static int
dombexpert(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    struct mbx *m;
  
    m = (struct mbx *)p;
  
    switch(m->stype) {
        case 'M':
            if(argc == 1)
                tprintf("-more- after %d lines\n",m->morerows);
            else {
                m->morerows = atoi(argv[1]);
            }
            break;
        case 'A':
            m->sid ^= MBX_AREA;
            break;
        case 'N':
            m->sid ^= MBX_NRID;
            break;
        case 'P':
            m->sid ^= MBX_LINEMODE;
            tprintf("LINEMODE is now %sabled\n", m->sid&MBX_LINEMODE ? "en" : "dis");
            break;
  
#if defined USERLOG && defined REGISTER
        case 'R':
            if(argc > 1) {
            /* Change the state of the 'Reply-to' header */
                if(!stricmp(argv[1],"on"))
                    m->sid |= MBX_REPLYADDR;
                else if(!stricmp(argv[1],"off"))
                    m->sid &= ~MBX_REPLYADDR;
            }
            tprintf("'Reply-to: %s' header is %sadded when sending mail.\n",
            ( m->IPemail ?
            ((m->sid & MBX_REPLYADDR) ? m->IPemail : "") :
            "" ),
            (m->sid & MBX_REPLYADDR) ? "" : "not " );
            if((m->sid & MBX_REPLYADDR) && !m->IPemail)
                tprintf("Please 'register' to set your email reply-to address!\n");
            break;
#endif
  
  
#if defined(ENCAP) && defined(MBOX_DYNIPROUTE)
        case 'G':
            /* XG 44-net-addr   -- add encap route to user's gateway system.
             * Allows g/w with dynamic addr to "register" an encapped route
             * with us (for 15 mins or so) [see DYNIPROUTE_TTL]. -- n5knx
             */
            if(m->privs & XG_ALLOWED && argc > 1 && m->family == AF_INET)  {
                union sp sp;
                struct sockaddr_in insock;
                struct route *rp;
                int len,logit;
                unsigned int nbits;
                int32 ampr_addr;
                char *p;
#ifndef DYNIPROUTE_TTL
#define DYNIPROUTE_TTL 900L
#endif

                len = sizeof(insock);
                if ((p=strchr(argv[1],'/')) != NULLCHAR) {
                    *p++='\0';
                    nbits = atoi(p);
                    if (nbits < 16) nbits=16;  /* surely a mistake! */
                }
                else nbits=32;
                if ((ampr_addr = resolve(argv[1])) == 0L || ampr_addr == Loopback.addr)
                    tprintf(Badhost,argv[1]);
                else if (getpeername(m->user,(char *)&insock,&len) == 0 && len != 0) {
                    /* replace only routes to Loopback, or to encap with a running timer */
                    if ((rp=rt_lookup(ampr_addr))!=NULLROUTE && (rp->iface!=&Encap
                        || !dur_timer(&rp->timer) || !run_timer(&rp->timer)))
                        goto xg_invalid;
                    logit=(!rp || rp->gateway != insock.sin_addr.s_addr);/* G/W changed? */
                    if(rt_add(ampr_addr,nbits,insock.sin_addr.s_addr,&Encap,1,DYNIPROUTE_TTL,1) == NULLROUTE)
                        tputs("Can't add encap route.\n");
                    else {
                        if (logit) {/* GW changed? */
                            char adest[25];
                            strcpy(adest,inet_ntoa(ampr_addr)); /* log() overwrites static buf */
                            log(m->user,"XG %s/%d", adest, nbits);
                        }
#ifdef  RSPF_EVEN_IF_PRIVATE /* unlikely to be useful */
                        rspfrouteupcall(ampr_addr,nbits,insock.sin_addr.s_addr); /* Do an RSPF upcall */
#endif  /* RSPF */
                    }
                }
                /* else tputs(Badsocket); /* Unlikely to happen; if it does tputs() fails too */
            }
            else
xg_invalid:
                tputs("XG invalid.\n");  /* Don't be too precise */
            break;
#endif /* ENCAP && MBOX_DYNIPROUTE */

        default:
            m->sid ^= MBX_EXPERT;
            break;
    }
    return 0;
}
  
#if defined FOQ_CMDS && defined TTYLINKSERVER
extern char SysopBusy[];
  
static int
dochat(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    char buf[8], *newargv[3];
    struct mbx *m;
  
    m = (struct mbx *)p;
  
    if (MAttended) {
        m->state = MBX_CHAT;
        newargv[0] = "C";
        newargv[1] = Hostname;
        sprintf(buf,"%d",IPPORT_TTYLINK);
        newargv[2] = buf;
        m->startmsg = mallocw(50);
        sprintf(m->startmsg,"*** MBOX Chat with %s\n",m->name);
        return dombtelnet(3,newargv,p);
    } else {
        tputs(SysopBusy);
    }
    /* It returns only after a disconnect or refusal */
    return 0;
}
#endif /* TTYLINKSERVER */
  
static int
dombipheard(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    struct mbx *m = (struct mbx *)p;
  
    if(m->privs & NO_LISTS) {
        tputs(Noperm);
        return 0;
    }
    return doipheard(argc,argv,NULL);
}
  
#ifdef AX25
static int
dombjheard(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    struct iface *ifp;
    struct mbx *m = (struct mbx *)p;
  
    if(m->privs & NO_LISTS) {
        tputs(Noperm);
        return 0;
    }
  
    if(argc > 1){
        if( ((ifp = if_lookup(argv[1])) == NULLIF) || (ifp->type != CL_AX25) ||
        ((ifp->flags & HIDE_PORT) && !(m->privs & SYSOP_CMD)) ) {
            tprintf(Badinterface,argv[1]);
            return 0;
        }
        axheard(ifp);
        return 0;
    }
    for(ifp = Ifaces;ifp != NULLIF;ifp = ifp->next){
        if((ifp->flags & LOG_AXHEARD)  && ( !(ifp->flags & HIDE_PORT) || m->privs&SYSOP_CMD) )
            if(axheard(ifp) == EOF)
                break;
    }
    return 0;
}
#endif
  
#if defined FOQ_CMDS && defined CALLCLI
static int
dombcallbook(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    struct mbx *m;
    char buf[8], *newargv[3];
    extern char *Callserver;
    int req, ret = 0;
  
    m = (struct mbx *) p;
  
    sprintf(buf,"%d",IPPORT_CALLDB);
    newargv[0] = "Q";
    newargv[1] = Callserver;
    newargv[2] = buf;
  
    for (req = 1; req < argc; req++)  {
        if(argv[req] == NULLCHAR)
            return ret;
        m->startmsg = mallocw(80);  /* is freed each time by gw_connect()         */
        sprintf(m->startmsg,"%s\n", argv[req]);
        log(m->user, "MBOX callbook %s: %s",m->name,argv[req]);
        tprintf("Looking for \" %s \" in the callbook at %s\n",argv[req],Callserver);
        ret = dombtelnet(3,newargv,p);
    }
    return ret; /* It looks like all possible returns are zero anyway!    */
}
#endif /* CALLCLI */
  
/*Password protection added - 920118, WG7J */
int
dosysop(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    struct mbx *m;
    int c;
    int len,pwdc[5],i,valid=0;
    char *cp;
  
    m = (struct mbx *) p;
    log(m->user,"MBOX: %s attempting SYSOP",m->name);
  
    /*If you want anyone with the password to go sysop-mode
     *comment out the next 4 line ! -WG7J
     */
    if(!(m->privs & SYSOP_CMD)){
        tputs(Noperm);
#ifdef MAILERROR
        mail_error("%s: SYSOP denied!\n",m->name);
#endif
        return 0;
    }
  
    /*only if set,
     *check for the password before letting users proceed
     */
    m->state = MBX_SYSOPTRY;
    if((len = strlen(Mbpasswd)) != 0) {;
        for (i=0;i<5;i++)
            tprintf("%d ",(pwdc[i]=RANDOM(len))); /*print the random chars*/
        tputc('\n');
        while(1) {
            c = mbxrecvline(m);
            if(c == EOF || c == -2)
                return 0;
            if(*m->line == '\0')
                break;
            cp = m->line;
            for(i=0;i<5;i++)
                if(*cp++ != Mbpasswd[pwdc[i]])
                    break;
            if (i == 5)
                valid = 1;
        }
        if(!valid)
            return 0;
    }
  
    log(m->user,"MBOX: %s is now SYSOP",m->name);
    m->state = MBX_SYSOP;
    tputs("\n\007Type 'exit' to return\n");
  
    for(;;){
#ifdef EDITOR
        struct usock *up;

        if ((up=itop(Curproc->output)) == NULLUSOCK)
            break;  /* weird error ... disconnect? */
        c = up->refcnt;  /* remember current refcnt */
#endif
        tprintf("%lu Jnos> ",farcoreleft());
        /* tflush(); already done in mbxrecvline() */
        if(mbxrecvline(m) < 0)
            break;
        log(m->user,"MBOX sysop: %s",m->line);
        if(cmdparse(Cmds,m->line,NULL) == -2)
            break;
#ifdef EDITOR
        /* editor reads Curproc->input, so we can't call mbxrecvline() until he's
         * finished reading stdin.  If we had the process # we could pwait but
         * that would require a more substantial change to cmdparse, or much
         * more work here. At present only the editor requires special care.
         * [the --more-- prompt's read in morecmd() works by accident, as it's
         * LIFO] -- n5knx
         */
        if(*(m->line) && !strnicmp(m->line, "editor", strlen(m->line))) { /* very special case */
            while(up->refcnt != c)
                pause(5000L);  /* test every 5s if editor process has quit */
        }
#endif /* EDITOR */
    }
    /* remove potential remote traces - WG7J */
    removetrace();
    return 0;
}
  
/* Handle the "*** Done" command when reverse forwarding ends or the
 * "*** LINKED to" command.
 */
int
dostars(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    struct mbx *m;
    int anony = 1;
    int founddigit = 0;
    long oldprivs;
    char *cp;
  
    m = (struct mbx *)p;
  
    /* Allow 'linked to' from anyone, but reset SYSOP priviledges
     * when the sysop-password is not set !
     * Also try to set the new call !
     * Inspired by Kurt, wb5bbw
     * Check for the strange TEXNET linked message !
     * 920220 - WG7J
     */
    if((argc >= 4) && !strcmp(argv[1],"linked") && !strcmp(argv[2],"to")) {
        if(m->privs & NO_LINKEDTO) {
            tputs(Noperm);
#ifdef GWTRACE
            log(m->user,"MBOX LINKED: %s permission denied",m->name);
#endif
            return 0;
        }
#ifdef GWTRACE
        log(m->user,"MBOX LINKED: %s changed to %s",m->name,argv[3]);
#endif
#ifdef USERLOG
#ifdef MAILCMDS
        setlastread(m);
#endif
        updatedefaults(m);
#endif
        strcpy(m->name,argv[3]);
        oldprivs = m->privs; /*Save this !*/
        /* Try to find the privileges of this user from the userfile */
        if((m->privs = userlogin(m->name,NULLCHAR,&m->path,MBXLINE,
            &anony,"linkperm")) == -1)
#ifdef notdef
            if((m->privs = userlogin("bbs",NULLCHAR,&m->path,
                MBXLINE,&anony,"anonymous")) == -1)
#endif
                {
                    m->privs = 0;
                    free(m->path);
                    m->path = NULLCHAR;
                }
        if(m->privs & EXCLUDED_CMD)
            return domboxbye(0,NULLCHARP,p);
#ifdef AX25
    /* Set the call */
        for(cp=m->name;*cp != '\0';cp++)
            if(isdigit((int)*cp))
                break;
        if(*cp != '\0')
            founddigit = 1;
        if( (setcall(m->call,m->name) == -1) || (!founddigit) ) {
            m->privs &= ~(AX25_CMD|NETROM_CMD);
        }
#else
        m->privs &= ~(AX25_CMD|NETROM_CMD);
#endif
        /*Kill ssid in name, if any*/
        if((cp=strchr(m->name,'-')) != NULLCHAR)
            *cp = '\0';
        /* Check if sysop password is set,
         * if not, disallow sysop privs no matter what !
         */
        if(*Mbpasswd == '\0')
            m->privs &= ~SYSOP_CMD;
        /* Check to see if any of NO_READ,NO_SEND or NO_3PARTY were set,
         * if so, dis-allow those no matter what
         * (so that users cannot get priviledges by issuing a ***linked)
         * 920220 - WG7J
         */
        if(oldprivs & NO_SENDCMD)
            m->privs |= NO_SENDCMD;
        if(oldprivs & NO_READCMD)
            m->privs |= NO_READCMD;
        if(oldprivs & NO_3PARTY)
            m->privs |= NO_3PARTY;
        if(oldprivs & NO_CONVERS)
            m->privs |= NO_CONVERS;
        if(oldprivs & NO_LISTS)
            m->privs |= NO_LISTS;
  
        /* Log this new user in */
        loguser(m);
  
#ifdef USERLOG
        tprintf("Oh, hello %s.\n",m->username ? m->username : m->name);
#else
        tprintf("Oh, hello %s.\n",m->name);
#endif
  
#ifdef MAILCMDS
        changearea(m,m->name);
#endif
        return 0;
    }
  
    if(argc > 1 && (m->sid & MBX_SID))      /* "*** Done" or similar */
        return -2;
    return -1;
}
  
#ifdef FOQ_CMDS
int
dombfinger(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    struct mbx *m;
    char *host, *user = NULLCHAR, buf[8], *newargv[3];
  
    if(argc > 2){
        tputs("Usage: F user@host  or  F @host  or  F user.\n");
        return 0;
    }
    host = Hostname;
    if(argc == 2){
        if((host = strchr(argv[1], '@')) != NULLCHAR){
            *host = '\0';
            host++;
        } else
            host = Hostname;
        user = argv[1];
    }
    m = (struct mbx *) p;
    m->startmsg = mallocw(80);
    if(user != NULLCHAR)
        sprintf(m->startmsg,"%s\n",user);
    else
        strcpy(m->startmsg,"\n");
#ifdef MBOX_FINGER_ALLOWED
    newargv[0] = "Q";	/* treat Finger like Query: allow regardless of TELNET_CMD permission */
#else
    newargv[0] = "";
#endif
    newargv[1] = host;
    sprintf(buf,"%d",IPPORT_FINGER);
    newargv[2] = buf;
    return dombtelnet(3,newargv,p);
}
#endif /* FOQ_CMDS */
  
#ifdef  CONVERS
extern int Mbconverse;
extern int CDefaultChannel;
  
int
dombconvers(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    struct mbx *m = (struct mbx *)p;
    int channel = 0;
  
    if(m->privs & NO_CONVERS) {
        tputs(Noperm);
#ifdef MAILERROR
        mail_error("%s: converse denied\n",m->name);
#endif
        return 0;
    }
    if(!Mbconverse) {
        tputs("Mailbox Convers server not enabled\n");
        return 0;
    }
    m->state = MBX_CONVERS;
    if(argc > 1)
        channel = atoi(argv[1]);
    else
        channel = CDefaultChannel;
#ifdef GWTRACE
    log(m->user,"MBOX CONVERS: %s",m->name);
#endif
    mbox_converse(m,channel);
    return 0;
}
#endif /* CONVERS */
  
#endif /* MAILBOX */
