/* VE5VA */
#include "ve5va.h"

/* 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;
  
#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";
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
"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[];
  
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 *) 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 ? "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 || !ax_25)
#endif
            return 0;
#endif
        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: ");
                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);
#ifdef AX25PASSWORD
                /* KF5MG - send a password: prompt to ax.25 users */
                if(ax_25) {
                   anony = 0;
                   tputs("Password: ");
                   usflush(m->user);
                   if(mbxrecvline(m) == -1)
                       return -1;
                } else {
#endif
                tprintf("Password: %c%c%c",IAC,WILL,TN_ECHO);
                SET_OPT(m,TN_ECHO);
                usflush(m->user);
                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');
                usflush(m->user);
#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";
#endif
#ifdef TIPSERVER
                if(m->type==TIP_LINK) cp = "tipperm";
#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;
                        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
#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
}
  
/* 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;
  
    if((m = newmbx()) == NULLMBX){
        tputs("Too many mailbox sessions\n");
        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;
  
    loguser(m);
    m->state = MBX_CMD;     /* start in command state */
#ifdef MAILCMDS
#if defined(MBFWD) && defined(FBBFWD)
    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,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;
    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, 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[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,     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
 * 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;
    usflush(Curproc->output);
    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;*/
                }
                usflush(Curproc->output);
                alarm(Mbtdiscinit*1000L);   /* restart inactivity timeout */
                continue;
            }
            if(c != IAC && (c = recvchar(s)) == EOF)
                break;
        }
        /* ordinary character */
        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;
{
    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) */
    }
    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 */
#endif

    /* for bbs's, just disconnect */
    if(m->sid & MBX_SID)
        return -2;
  
#ifdef USERLOG
#ifdef MAILCMDS
    setlastread(m);
#endif
#endif
    /* 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
    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[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,a->name);
                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
  
        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 */
  

#ifdef VE5VA
extern int numpass;
pass(p)
void *p;
{
   char tmpnum[MAXPWDLEN];

   register struct mbx *m;
   register char *cp;
   register int i,j;
   int sum;

   m = p;
   sum = 0;
   /* clear tmpnum array
      It is used to ensure that five distinct integers are
      produced as a challenge
   */
   for(cp = &tmpnum[0];cp<&tmpnum[MAXPWDLEN];cp++)*cp = 0;
   /* get five distinct random integers to generate the password */
   for(i = 0;i < 5;i++) {
      do {
         j = random(numpass);
         /* make sure rand produces five distinct numbers otherwise
            it weakens the password scheme
         */
         if(tmpnum[j])continue;
         tmpnum[j] = 1;
         sum += Mbpasswd[j];
         tprintf(" %2d", j);
         break;
      }while(1);
   }
   tputc('\n');
   j = mbxrecvline(m);
   if(j == EOF || j == -2)
       return 0;
   if(*m->line == '\0')
       return(0);
   p = m->line;
   if(atoi(p) == sum)return(1);
   return(0);
}
#endif  

/*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;

#ifdef VE5VA
    if(numpass) {
       if(pass(m) == 0)return 0;
    }
/* NOTE that this else must not have anything after it because it depends
   upon the following if statement
*/
    else
#endif

    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;
    }
    /* 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) {
            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");
#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 */
