/* 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 are only two functions in this mailbox code that depend on the
 * underlying protocol, namely mbx_getname() and dochat(). 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"
  
struct mbx *Mbox;
int BbsUsers;
int Totallogins;
extern int MbShowAliases;
  
#ifdef RLOGINSERVER
static char DFAR RLoginbanner[] = "\nRemote Login at %s - %s\n\n";
#endif
char Loginbanner[] = "\nJNOS (%s)\n\n";
char Mbwelcome[] = "\nWelcome %s,\n";
char Mbbanner[] = "to the %s TCP/IP Mailbox (JNOS %s).\n";
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
"ALI,"
"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,J,"
#ifdef MAILCMDS
"K,L,"
#endif
"M,"
#if defined GATECMDS && defined NETROM
"N,NR,"
#endif
#if defined FOQ_CMDS && defined TTYLINKSERVER
"O,"
#endif
#ifdef GATECMDS
"P,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[];

/* 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((new = (struct mbx *) callocw(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
  
    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);
    /* 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] &= 0xfc;/*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 */
            cp = m->name;
            while(*cp){
                if(isupper(*cp))
                    *cp = tolower(*cp);
                ++cp;
            }
            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 ? "ax25def" : "netdef")) == -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;
            return 0;
#endif
        case AF_LOCAL:
        case AF_INET:
            m->state = MBX_LOGIN;
#ifdef RLOGINSERVER
            if(m->type == RLOGIN_LINK)
               tprintf(RLoginbanner,Hostname,Version);
            else {
#endif
            tprintf(Loginbanner,Hostname);
            if(Mtmsg != NULLCHAR)
                tputs(Mtmsg);
#ifdef RLOGINSERVER
            }
#endif
            for(;;){
            /* Maximum of 3 tries - WG7J */
                if(count++ == 3)
                    return -1;
                oldmode = sockmode(m->user,SOCK_ASCII);
                tputs("login: ");
                usflush(m->user);
                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++)
                    if(dosfnchr(*cp) == 0)
                        break;
                if(*cp != '\0')
                    continue;
                strcpy(m->name,m->line);
                tprintf("Password: %c%c%c",IAC,WILL,TN_ECHO);
                usflush(m->user);
                sockmode(m->user,SOCK_BINARY);
                if(mbxrecvline(m) == -1)
                    return -1;
                tprintf("%c%c%c",IAC,WONT,TN_ECHO);
                sockmode(m->user,oldmode);
                tputc('\n');
                usflush(m->user);
            /* This is needed if the password was send before the
             * telnet no-echo options were receied. We neeed to
             * flush the eold sequence from the input buffers, sigh
             */
                if(socklen(m->user,0))/* discard any remaining input */
                    recv_mbuf(m->user,NULL,0,NULLCHAR,0);
                if((m->privs = userlogin(m->name,m->line,&m->path,MBXLINE,
                &anony,"tcpdef")) != -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;
                        m->privs &= ~NETROM_CMD;
                    }
#else
                    m->privs &= ~AX25_CMD;
                    m->privs &= ~NETROM_CMD;
  
#endif /* AX25 */
            /* Set the morerows - WG7J */
                    m->morerows = 20;
                    return 0;
                }
                tputs("Login incorrect\n");
                log(m->user,"MBOX Login failed: %s, pw %s",m->name,m->line);
#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
    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)
            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
}
  
/* Incoming mailbox session */
void
mbx_incom(s,t,p)
int s;
void *t;
void *p;
{
    struct tipcb *tip;
    struct mbx *m,*mp,*pp;
    struct usock *up;
    struct alias *a;
    char *buf[3];
    char tmp[AXBUF];
    int rval;
    FILE *fp;
    int newpriv = 0;
  
/*
    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;
  
    /* We'll do our own flushing right before we read input */
    setflush(s,-1);
  
    if((m = newmbx()) == NULLMBX){
        tputs("Too many mailbox sessions\n");
        return;
    }
  
    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 RLOGINSERVER
    if(m->type == RLOGIN_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;
  
    loguser(m);
    m->state = MBX_CMD;     /* start in command state */
#ifdef MAILCMDS
    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,Mycall),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)) {
        /* Enable our local message area,
         * only if we're not a bbs - WG7J
         */
        buf[1] = m->name;
        doarea(2,buf,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((rval = mbx_parse(m)) == -2)
            break;
        if(rval == 1)
            tputs("Bad syntax.\n");
#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;
    struct usock *up;
  
    /* 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;
    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, 2, "D[U|X] <filename>",
#else
    "download", dodownload, 0, 2, "D[U] <filename>",
#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[U|X] <filename>",
#else
    "upload",   dombupload, 0, 2, "U[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,
#endif
#endif
    "@",        dosysop,    0, 0, NULLCHAR,
    "***",      dostars,    0, 0, NULLCHAR,
    ";",        dombsemicolon, 0, 0, NULLCHAR,
    NULLCHAR,   NULLFP,     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 */
    for (cp = m->line; *cp != '\0'; ++cp)
        if(isupper(*cp))
            *cp = tolower(*cp);
    /* 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
 */
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;
    usflush(Curproc->output);
    alarm(Mbtdiscinit*1000L);   /* Start inactivity timeout - WG7J */
    while((c = recvchar(s)) != EOF){
        alarm(0L);
        if(c == IAC){           /* Telnet command escape */
            if((c = recvchar(s)) == EOF)
                break;
            if(c >= 250 && c < 255 && (opt = recvchar(s)) != EOF){
                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 */
                            tprintf("%c%c%c",IAC,DO,opt);
                        /* Tell client to do editing */
                                tprintf("%c%c%c%c%c%c%c",IAC,SB,TN_LINEMODE,1,1,IAC,SE);
                        } else
                            tprintf("%c%c%c",IAC,DONT,opt);
                        break;
                    case WONT:
                        tprintf("%c%c%c",IAC,DONT,opt);
                        break;
                    case DO:
                            tprintf("%c%c%c",IAC,WONT,opt);
                            break;
                    case DONT:
                        tprintf("%c%c%c",IAC,WONT,opt);
                }
/* to be fixed                  usflush(Curproc->output);*/
                continue;
            }
            if(c != IAC && (c = recvchar(s)) == EOF)
                break;
        }
        /* ordinary character */
        if(c == '\r' || c == '\n')
            break;
        if(uchar(c) == escape){
            if(socklen(s,0)) /* discard any remaining input */
                recv_mbuf(s,NULL,0,NULLCHAR,0);
            cnt = -2;
            break;
        }
        /* Handle <del> chars - from wa7tas */
        if(c == 8 && cnt > 0) {
            *--buf = 0;
            cnt--;
        } else {
            *buf++ = c;
            ++cnt;
        }
        if(cnt == MBXLINE - 1)
            break;
        alarm(Mbtdiscinit*1000L);   /* Restart inactivity timeout - WG7J */
    }
    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;
{
    if (m->type == TELNET_LINK || m->type == TIP_LINK) {
#ifdef notdef
        if ((up=itop(m->proc->output)) != NULLSOCK && 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) */
    }
    return 0;
}

  
int
domboxbye(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    struct mbx *m;
  
    m = (struct mbx *)p;
  
    /* for bbs's, just disconnect */
    if(m->sid & MBX_SID)
        return -2;
  
#ifdef USERLOG
#ifdef MAILCMDS
    setlastread(m);
#endif
    updatedefaults(m);
#endif
    /* Now say goodbye */
    if(!(m->privs & MBX_EXPERT))
        tprintf("\nThank you %s,\nfor calling %s JNOS.\n\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
    usflush(m->user);
    return -2;      /* signal that exitbbs() should be called */
}
  
static int
dombhelp(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    char buf[FILE_PATH_SIZE];
    int i;
    FILE *fp;
    struct mbx *m = (struct mbx *)p;
  
    if(*argv[0] == '?') {
        if(MbShowAliases && AliasList) {
            struct alias *a;
  
            tputs("Aliases:");
            for(a=AliasList;a;a=a->next)
                tprintf(" %s",a->name);
            tputc('\n');
        }

        sprintf(buf,"%s/longmenu.eng",Helpdir);
        if((fp = fopen(buf,READ_TEXT)) != NULLFILE) {
            sendfile(fp,Curproc->output,ASCII_TYPE,0,m);
            fclose(fp);
        } else {
            tputs("No menu available.\n");
        }
        return 0;
    }
  
    buf[0] = '\0';
    if(argc > 1)
        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
  
        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 & MBX_SYSOP)) ) {
            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&MBX_SYSOP) )
            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;
    extern struct cmds DFAR Cmds[];
  
    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\aType 'exit' to return\n");
  
    for(;;){
        tprintf("%lu Jnos> ",coreleft());
        usflush(Curproc->output);
        if(mbxrecvline(m) < 0)
            break;
        log(m->user,"MBOX sysop: %s",m->line);
        if(cmdparse(Cmds,m->line,NULL) == -2)
            break;
    }
    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) {
            puts(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,"")) == -1)
            if((m->privs = userlogin("bbs",NULLCHAR,&m->path,
                MBXLINE,&anony,"")) == -1)
                if((m->privs = userlogin("anonymous",NULLCHAR,
                &m->path,MBXLINE,&anony,"")) == -1){
                    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;
            m->privs &= ~NETROM_CMD;
        }
#else
        m->privs &= ~AX25_CMD;
        m->privs &= ~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");
    newargv[0] = "";
    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 */
