home *** CD-ROM | disk | FTP | other *** search
/ High Voltage Shareware / high1.zip / high1 / DIR3 / KA9Q212.ZIP / MAILBOX.C < prev    next >
C/C++ Source or Header  |  1993-07-16  |  48KB  |  1,908 lines

  1. /* There are only two functions in this mailbox code that depend on the
  2.  * underlying protocol, namely mbx_getname() and dochat(). All the other
  3.  * functions can hopefully be used without modification on other stream
  4.  * oriented protocols than AX.25 or NET/ROM.
  5.  *
  6.  * SM0RGV 890506, most work done previously by W9NK
  7.  *
  8.  *** Changed 900114 by KA9Q to use newline mapping features in stream socket
  9.  *    interface code; everything here uses C eol convention (\n)
  10.  *
  11.  *    Numerous new commands and other changes by SM0RGV, 900120
  12.  *
  13.  *    14 Mar 92    GT    Fix declaration syntax error under BC++ 3.00.
  14.  *    25 Jun 92    paul@wolf.demon.co.uk  Make privilege check for
  15.  *                    external email from mailbox
  16.  *    25 Jun 92    paul@wolf.demon.co.uk  Add message if entering mailbox
  17.  *                    with an '@' password.
  18.  *
  19.  */
  20.  
  21. /****************************************************************************
  22. *    $Id: mailbox.c 1.5 93/07/16 11:46:33 ROOT_DOS Exp $
  23. *    08 May 93    1.5        GT    Fix warnings.                                    *
  24. ****************************************************************************/
  25.  
  26. #include <stdio.h>
  27. #include <time.h>
  28. #include <ctype.h>
  29. #ifdef    UNIX
  30. #include <sys/types.h>
  31. #include <sys/stat.h>
  32. #endif
  33.  
  34. #include "global.h"
  35. #include "config.h"
  36. #include "timer.h"
  37. #include "proc.h"
  38. #include "socket.h"
  39. #include "usock.h"
  40. #include "session.h"
  41. #include "smtp.h"
  42. #include "dirutil.h"
  43. #include "telnet.h"
  44. #include "ftp.h"
  45. #include "ftpserv.h"
  46. #include "commands.h"
  47. #include "netuser.h"
  48. #include "files.h"
  49. #include "bm.h"
  50. #include "mailbox.h"
  51. #include "ax25mail.h"
  52. #include "nr4mail.h"
  53. #include "cmdparse.h"
  54.  
  55. /*
  56. #define MBDEBUG
  57. */
  58.  
  59. struct mbx *Mbox[NUMMBX];
  60. static char *Motd = NULLCHAR;
  61. static int Attended = TRUE;    /* default to attended mode */
  62. unsigned Maxlet = BM_NLET;
  63.  
  64. char Noperm[] = "Permission denied.\n";
  65. char Nosock[] = "Can't create socket\n";
  66.  
  67. static char Mbbanner[] = "[NET-H$]\nWelcome %s to the %s TCP/IP Mailbox (%s)\n%s";
  68. static char Mbmenu[] = "Current msg# %d : A,B,C,D,E,F,G,H,I,J,K,L,N,R,S,T,U,V,W,Z,? >\n";
  69. static char Longmenu1[] = "(?)help    (A)rea     (B)ye      (C)hat     (D)ownload (E)scape   (F)inger\n";
  70. static char Longmenu2[] = "(G)ateway  (H)elp     (I)nfo     (J)heard   (K)ill     (L)ist     (N)etrom\n";
  71. static char Longmenu3[] = "(R)ead     (S)end     (T)elnet   (U)pload   (V)erbose  (W)hat     (Z)ap\n";
  72. static char Loginbanner[] = "\nKA9Q NOS (%s)\n\n";
  73. static char Howtoend[] = "Terminate with /EX or ^Z in first column (^A aborts):\n";
  74.  
  75. static int doarea __ARGS((int argc,char *argv[],void *p));
  76. static int mbx_getname __ARGS((struct mbx *m));
  77.  
  78. /************************************************************************/
  79. /*            C O M M A N D S                    */
  80. /************************************************************************/
  81.  
  82. static int doattend __ARGS((int argc,char *argv[],void *p));
  83. static int domaxmsg __ARGS((int argc,char *argv[],void *p));
  84. static int domotd __ARGS((int argc,char *argv[],void *p));
  85. static int dotimeout __ARGS((int argc,char *argv[],void *p));
  86. static int check_mail_privilege __ARGS((char *name));
  87. static void string_tidy __ARGS((char *str));
  88.  
  89. /* mbox subcommand table */
  90. static struct cmds Mbtab[] = {
  91.     { "attend",    doattend,    0, 0, NULLCHAR },
  92. #ifdef    AX25
  93.     { "kick",        dombkick,    0, 0, NULLCHAR },
  94. #endif
  95.     { "maxmsg",    domaxmsg,    0, 0, NULLCHAR },
  96.     { "motd",        domotd,        0, 0, NULLCHAR },
  97.     { "status",    domboxdisplay,    0, 0, NULLCHAR },
  98. #ifdef    AX25
  99.     { "timer",    dombtimer,    0, 0, NULLCHAR },
  100. #endif
  101.     { "tiptimeout",    dotimeout,    0, 0, NULLCHAR },
  102.     { NULLCHAR },
  103. };
  104.  
  105.  
  106. int
  107. dombox(argc,argv,p)
  108. int argc;
  109. char *argv[];
  110. void *p;
  111. {
  112.     if(argc == 1)
  113.         return domboxdisplay(argc,argv,p);
  114.     return subcmd(Mbtab,argc,argv,p);
  115. }
  116.  
  117. /* if unattended mode is set, ax25, telnet and maybe other sessions will
  118.  * be restricted.
  119.  */
  120. static int
  121. doattend(argc,argv,p)
  122. int argc;
  123. char *argv[];
  124. void *p;
  125. {
  126.     return setbool(&Attended,"Attended flag",argc,argv);
  127. }
  128.  
  129. static int
  130. domaxmsg(argc,argv,p)
  131. int argc;
  132. char *argv[];
  133. void *p;
  134. {
  135.     return setuns(&Maxlet,"Maximum messages per area",argc,argv);
  136. }
  137.  
  138. static int
  139. domotd(argc,argv,p)
  140. int argc;
  141. char *argv[];
  142. void *p;
  143. {
  144.     if(argc > 2) {
  145.         tprintf("Usage: mbox motd \"<your message>\"\n");
  146.         return 0;
  147.     }
  148.  
  149.     if(argc < 2) {
  150.         if(Motd != NULLCHAR)
  151.             tputs(Motd);
  152.     }
  153.     else {
  154.         if(Motd != NULLCHAR){
  155.             free(Motd);
  156.             Motd = NULLCHAR;    /* reset the pointer */
  157.         }
  158.         if(!strlen(argv[1]))
  159.             return 0;        /* clearing the buffer */
  160.         Motd = mallocw(strlen(argv[1])+5);/* allow for the EOL char */
  161.         strcpy(Motd, argv[1]);
  162.         strcat(Motd, "\n");        /* add the EOL char */
  163.     }
  164.     return 0;
  165. }
  166.  
  167. int
  168. domboxdisplay(argc,argv,p)
  169. int argc;
  170. char *argv[];
  171. void *p;
  172. {
  173.     int i, j, len;
  174.     struct mbx *m;
  175.     char fsocket[MAXSOCKSIZE];
  176.     static char *states[] = {"LOGIN","CMD","SUBJ","DATA","REVFWD",
  177.                 "TRYING","FORWARD"};
  178.  
  179.     tprintf("User       State    S#  Where\n");
  180.  
  181.     for (i = 0; i < NUMMBX; i++){
  182.         if((m = Mbox[i]) != NULLMBX){
  183.             len = MAXSOCKSIZE;
  184.             j = getpeername(m->user,fsocket,&len);
  185.             if(tprintf("%-11s%-9s%-4u%s\n",m->name,
  186.                    states[m->state],m->user,
  187.                    j != -1 ? psocket(fsocket): "") == EOF)
  188.                 break;
  189.         }
  190.     }
  191.     return 0;
  192. }
  193.  
  194. static int
  195. dotimeout(argc,argv,p)
  196. int argc;
  197. char *argv[];
  198. void *p;
  199. {
  200.     return setuns(&Tiptimeout,"Tip connection timeout",argc,argv);
  201. }
  202.  
  203.  
  204. /**********************************************************************/
  205.  
  206. void
  207. listusers(s)
  208. int s;
  209. {
  210.     int outsave;
  211.  
  212.     usprintf(s,"\nCurrent remote users:\n");
  213.     outsave = Curproc->output;
  214.     Curproc->output = s;
  215.     domboxdisplay(0,NULLCHARP,NULL);
  216.     Curproc->output = outsave;
  217. }
  218.  
  219. struct mbx *
  220. newmbx()
  221. {
  222.     int i;
  223.     struct mbx *m;
  224.  
  225.     for(i = 0; i < NUMMBX; i++){
  226.         if(Mbox[i] == NULLMBX){
  227.             m = Mbox[i] = (struct mbx *)callocw(1,sizeof(struct mbx));
  228.             m->mbnum = i;
  229.             return m;
  230.         }
  231.     }
  232.     /* If we get here, there are no free mailbox sessions */
  233.     return NULLMBX;
  234. }
  235.  
  236. static int
  237. mbx_getname(m)
  238. struct mbx *m;
  239. {
  240. #ifdef    AX25
  241.     char *cp;
  242. #endif
  243.     union sp sp;
  244.     char tmp[MAXSOCKSIZE];
  245.     char buf[MBXLINE];
  246.     char *buf1;
  247.     char *password;
  248.     int len = MAXSOCKSIZE;
  249.     int anony = 0;
  250.     int oldmode;
  251.  
  252.     sp.p = tmp;
  253.     sp.sa->sa_family = AF_LOCAL;    /* default to AF_LOCAL */
  254.     getpeername(m->user,tmp,&len);
  255.     m->path = mallocw(MBXLINE);
  256.     /* This is one of the two parts of the mbox code that depends on the
  257.      * underlying protocol. We have to figure out the name of the
  258.      * calling station. This is only practical when AX.25 or NET/ROM is
  259.      * used. Telnet users have to identify themselves by a login procedure.
  260.      */
  261.     switch(sp.sa->sa_family){
  262. #ifdef    AX25
  263.     case AF_NETROM:
  264.     case AF_AX25:
  265.         /* NETROM and AX25 socket address structures are "compatible" */
  266.         pax25(m->name,sp.ax->ax25_addr);
  267.         cp = strchr(m->name,'-');
  268.         if(cp != NULLCHAR)            /* get rid of SSID */
  269.             *cp = '\0';
  270.         /* SMTP wants the name to be in lower case */
  271.         cp = m->name;
  272.         while(*cp){
  273.             if(isupper(*cp))
  274.                 *cp = tolower(*cp);
  275.             ++cp;
  276.         }
  277.         anony = 1;
  278.         /* Try to find the privileges of this user from the userfile */
  279.         if((m->privs = userlogin(m->name,buf,&m->path,MBXLINE,&anony)) == -1)
  280.             if((m->privs = userlogin("bbs",buf,&m->path,MBXLINE,&anony)) == -1)
  281.                 if((m->privs = userlogin("anonymous",buf,&m->path,MBXLINE,
  282.                      &anony)) == -1){
  283.                         m->privs = 0;
  284.                         free(m->path);
  285.                         m->path = NULLCHAR;
  286.                 }
  287.         if(m->privs & EXCLUDED_CMD)
  288.             return -1;
  289.         return 0;
  290. #endif
  291.     case AF_LOCAL:
  292.     case AF_INET:
  293.         m->state = MBX_LOGIN;
  294.         tprintf(Loginbanner,Hostname);
  295.         for(;;){
  296.             tputs("login: ");
  297.             usflush(m->user);
  298.             if(mbxrecvline(m->user,m->name,sizeof(m->name),-1) == EOF)
  299.                 return -1;
  300.             if(*m->name == '\0')
  301.                 continue;
  302.  
  303.             if ((buf1=userlookup(m->name,&password,NULLCHARP,NULLINT,NULL))!=NULLCHAR)
  304.             {
  305.                 if (strcmp(password,"@")==0)
  306.                     tprintf("*** Enter your email address as your password\n");
  307.                 free(buf1);
  308.             }
  309.  
  310.             tprintf("Password: %c%c%c",IAC,WILL,TN_ECHO);
  311.             usflush(m->user);
  312.             oldmode = sockmode(m->user,SOCK_BINARY);
  313.             if(mbxrecvline(m->user,buf,MBXLINE,-1) == EOF)
  314.                 return -1;
  315.             tprintf("%c%c%c",IAC,WONT,TN_ECHO);
  316.             sockmode(m->user,oldmode);
  317.             tputc('\n');
  318.             usflush(m->user);
  319.             /* This is needed if the password was send before the
  320.              * telnet no-echo options were receied. We neeed to
  321.              * flush the eold sequence from the input buffers, sigh
  322.              */
  323.             if(socklen(m->user,0))/* discard any remaining input */
  324.                 recv_mbuf(m->user,NULL,0,NULLCHAR,0);
  325.             if((m->privs = userlogin(m->name,buf,&m->path,MBXLINE,&anony))
  326.              != -1){
  327.                 if(anony)
  328.                     log(m->user,"MBOX login: %s Password: %s",m->name,buf);
  329.                 else
  330.                     log(m->user,"MBOX login: %s",m->name);
  331.                 if(m->privs & EXCLUDED_CMD)
  332.                     return -1;
  333.                 return 0;
  334.             }
  335.             tprintf("Login incorrect\n");
  336.             *m->name = '\0';    /* wipe any garbage */
  337.         }
  338.     }
  339.     return 0;
  340. }
  341.  
  342. /* Incoming mailbox session */
  343. void
  344. mbx_incom(s,t,p)
  345. int s;
  346. void *t;
  347. void *p;
  348. {
  349.     struct mbx *m;
  350.     struct usock *up;
  351.     char *buf[3];
  352.     int rval;
  353.  
  354.     sockmode(s,SOCK_ASCII);
  355.     sockowner(s,Curproc);    /* We own it now */
  356.     /* Secede from the parent's sockets, and use the network socket that
  357.      * was passed to us for both input and output. The reference
  358.      * count on this socket will still be 1; this allows the domboxbye()
  359.      * command to work by closing that socket with a single call.
  360.      * If we return, the socket will be closed automatically.
  361.      */
  362.     close_s(Curproc->output);
  363.     close_s(Curproc->input);
  364.     Curproc->output = Curproc->input = s;
  365.  
  366.     /* We'll do our own flushing right before we read input */
  367.     setflush(s,-1);
  368.  
  369.     log(s,"open MBOX");
  370.     if((m = newmbx()) == NULLMBX){
  371.         tprintf("Too many mailbox sessions\n");
  372.         return;
  373.     }
  374.     m->user = s;
  375.     m->escape = 24;        /* default escape character is Ctrl-X */
  376.     m->type = (int) t;
  377.     /* get the name of the remote station */
  378.     if(mbx_getname(m) == -1) {
  379.         exitbbs(m);
  380.         return;
  381.     }
  382.  
  383.     m->state = MBX_CMD;    /* start in command state */
  384.  
  385.     /* Now say hi */
  386.     tprintf(Mbbanner,m->name,Hostname,Version,
  387.         Motd != NULLCHAR ? Motd : "");
  388.     /* Enable our local message area */
  389.     buf[1] = m->name;
  390.     doarea(2,buf,m);
  391.     tprintf(Mbmenu,m->current);
  392.     while(mbxrecvline(s,m->line,MBXLINE,-1) != EOF){
  393.         if((rval = mbx_parse(m)) == -2)
  394.             break;
  395.         if(rval == 1)
  396.             tprintf("Bad syntax.\n");
  397.         if(!(m->sid & MBX_SID) && isnewprivmail(m) > 0L)
  398.             tprintf("You have new mail.\n");
  399.         scanmail(m);
  400.         tprintf((m->sid & MBX_SID) ? ">\n" : Mbmenu, m->current);
  401.         m->state = MBX_CMD;
  402.     }
  403.     exitbbs(m);
  404.     /* nasty hack! we may have screwed up reference count */
  405.     /* by invoking newproc("smtp_send",....); Fudge it!   */
  406.     if((up = itop(Curproc->output)) != NULLUSOCK)
  407.         up->refcnt = 1;
  408.     close_s(Curproc->output);
  409. }
  410.  
  411. void
  412. exitbbs(m)
  413. struct mbx *m;
  414. {
  415.     closenotes(m);
  416.     free(m->to);
  417.     free(m->tofrom);
  418.     free(m->origto);
  419.     free(m->tomsgid);
  420.     free(m->path);
  421.     free((char *)m->mbox);
  422.     Mbox[m->mbnum] = NULLMBX;
  423.     free((char *)m);
  424. }
  425.  
  426. /**********************************************************************/
  427.  
  428. static int dochat __ARGS((int argc,char *argv[],void *p));
  429. static int dodownload __ARGS((int argc,char *argv[],void *p));
  430. static int dombupload __ARGS((int argc,char *argv[],void *p));
  431. static int dowhat __ARGS((int argc,char *argv[],void *p));
  432. static int dozap __ARGS((int argc,char *argv[],void *p));
  433. static int dosend __ARGS((int argc,char *argv[],void *p));
  434. static int dosid __ARGS((int argc,char *argv[],void *p));
  435. static int dosysop __ARGS((int argc,char *argv[],void *p));
  436. static int dostars __ARGS((int argc,char *argv[],void *p));
  437. static int dombhelp __ARGS((int argc,char *argv[],void *p));
  438. static int dombtelnet __ARGS((int argc,char *argv[],void *p));
  439. static int dombfinger __ARGS((int argc,char *argv[],void *p));
  440. static void gw_alarm __ARGS((void *p));
  441. static void gw_input __ARGS((int s,void *notused,void *p));
  442. static void gw_superv __ARGS((int null,void *proc,void *p));
  443. static int mbx_to __ARGS((int argc,char *argv[],void *p));
  444. static int mbx_data __ARGS((struct mbx *m,struct list *cclist,char *extra));
  445. static int msgidcheck __ARGS((char *string));
  446. static int uuencode __ARGS((FILE *infile,int s,char *infilename));
  447.  
  448. static struct cmds Mbcmds[] = {
  449.     { "",        doreadnext,    0, 0, NULLCHAR },
  450.     { "area",        doarea,        0, 0, NULLCHAR },
  451.     { "send",        dosend,        0, 0, NULLCHAR },
  452.     { "read",        doreadmsg,    0, 2, "R numbers" },
  453.     { "verbose",    doreadmsg,    0, 2, "V numbers" },
  454. #ifdef    AX25
  455.     { "jheard",    doaxheard,    0, 0, NULLCHAR },
  456. #endif
  457.     { "kill",        dodelmsg,    0, 2, "K numbers" },
  458.     { "list",        dolistnotes,    0, 0, NULLCHAR },
  459.     { "escape",    dombescape,    0, 0, NULLCHAR },
  460.     { "download",    dodownload,    0, 2, "D[U] filename" },
  461.     { "upload",    dombupload,    0, 2, "U filename" },
  462.     { "what",        dowhat,        0, 0, NULLCHAR },
  463.     { "zap",        dozap,        0, 2, "Z filename" },
  464. #ifdef AX25
  465.     { "gateway",    dogateway,    0, 3, "G interface callsigns" },
  466. #endif
  467.     { "telnet",    dombtelnet,    0, 2, "T hostname" },
  468.     { "finger",    dombfinger,    0, 0, NULLCHAR },
  469. #ifdef    NETROM
  470.     { "netrom",    dombnetrom,    0, 0, NULLCHAR },
  471. #endif
  472.     { "chat",        dochat,        0, 0, NULLCHAR },
  473.     { "bye",        domboxbye,    0, 0, NULLCHAR },
  474.     { "help",        dombhelp,    0, 0, NULLCHAR },
  475.     { "info",        dombhelp,    0, 0, NULLCHAR },
  476.     { "?",        dombhelp,    0, 0, NULLCHAR },
  477.     { "[",        dosid,        0, 0, NULLCHAR },
  478. #ifdef    AX25
  479.     { "f>",        dorevfwd,    0, 0, NULLCHAR },
  480. #endif
  481.     { "@",        dosysop,    0, 0, NULLCHAR },
  482.     { "***",        dostars,    0, 0, NULLCHAR },
  483.     { NULLCHAR,    NULLFP,        0, 0, "Huh?" },
  484. };
  485.  
  486. /* "twocmds" defines the MBL/RLI two-letter commands, eg. "SB", "SP" and so on.
  487.  * They have to be treated specially since cmdparse() wants a space between
  488.  * the actual command and its arguments.
  489.  * "SP FOO" is converted to "s  foo" and the second command letter is saved
  490.  * in m->stype. Longer commands like "SEND" are unaffected, except for
  491.  * commands starting with "[", i.e. the SID, since we don't know what it will
  492.  * look like.
  493.  */
  494. static char twocmds[] = "slrd[";    /* S,L,R,D are two-letter commands */
  495. int
  496. mbx_parse(m)
  497. struct mbx *m;
  498. {
  499.     char *cp;
  500.     int i;
  501.     char *newargv[2];
  502.     /* Translate entire buffer to lower case */
  503.     for (cp = m->line; *cp != '\0'; ++cp)
  504.         if(isupper(*cp))
  505.             *cp = tolower(*cp);
  506.     /* Skip any spaces at the begining */
  507.     for(cp = m->line;isspace(*cp);++cp)
  508.         ;
  509.     m->stype = ' ';
  510.     if(*cp != '\0' && *(cp+1) != '\0')
  511.     for(i=0; i<strlen(twocmds); ++i){
  512.         if(*cp == twocmds[i] && (isspace(*(cp+2)) || *(cp+2) == '\0'
  513.          || *cp == '[')){
  514.             if(islower(*(++cp)))
  515.                 m->stype = toupper(*cp); /* Save the second character */
  516.             else
  517.                 m->stype = *cp;
  518.             *cp = ' ';
  519.             break;
  520.         }
  521.     }
  522.     /* See if the input line consists solely of digits */
  523.     cp = m->line;
  524.     for(cp = m->line;isspace(*cp);++cp)
  525.         ;
  526.     newargv[1] = cp;
  527.     for(;*cp != '\0' && isdigit(*cp);++cp)
  528.         ;
  529.     if(*cp == '\0' && strlen(newargv[1]) > 0) {
  530.         newargv[0] = "read";
  531.         return doreadmsg(2,newargv,(void *)m);
  532.     }
  533.     else
  534.         return cmdparse(Mbcmds,m->line,(void *)m);
  535. }
  536.  
  537. /* This works like recvline(), but telnet options are answered and the
  538.  * terminating newline character is not put into the buffer. If the
  539.  * incoming character equals the value of escape, any queued input is
  540.  * flushed and -2 returned.
  541.  */
  542. int
  543. mbxrecvline(s,buf,len,escape)
  544. int s;
  545. char *buf;
  546. int len;
  547. int escape;
  548. {
  549.     int c, cnt = 0
  550. #ifdef    foo
  551.         , opt
  552. #endif
  553.         ;
  554.     if(buf == NULLCHAR)
  555.         return 0;
  556.     usflush(Curproc->output);
  557.     while((c = recvchar(s)) != EOF){
  558.         if(c == IAC){        /* Telnet command escape */
  559.             if((c = recvchar(s)) == EOF)
  560.                 break;
  561. #ifndef    foo
  562.             if(c > 250 && c < 255 && recvchar(s) != EOF){
  563. #endif
  564. #ifdef    foo
  565.             if(c > 250 && c < 255 && (opt = recvchar(s)) != EOF){
  566.                 switch(c){
  567.                 case WILL:
  568.                     tprintf("%c%c%c",IAC,DONT,opt);
  569.                     break;
  570.                 case WONT:
  571.                     tprintf("%c%c%c",IAC,DONT,opt);
  572.                     break;
  573.                 case DO:
  574.                     tprintf("%c%c%c",IAC,WONT,opt);
  575.                     break;
  576.                 case DONT:
  577.                     tprintf("%c%c%c",IAC,WONT,opt);
  578.                 }
  579. #endif
  580. /* to be fixed             usflush(Curproc->output);*/
  581.                 continue;
  582.             }
  583.             if(c != IAC && (c = recvchar(s)) == EOF)
  584.                 break;
  585.         }
  586.         /* ordinary character */
  587.         if(c == '\r' || c == '\n')
  588.             break;
  589.         if(uchar(c) == escape){
  590.             if(socklen(s,0)) /* discard any remaining input */
  591.                 recv_mbuf(s,NULL,0,NULLCHAR,0);
  592.             cnt = -2;
  593.             break;
  594.         }
  595.         *buf++ = c;
  596.         ++cnt;
  597.         if(cnt == len - 1)
  598.             break;
  599.     }
  600.     if(c == EOF && cnt == 0)
  601.         return -1;
  602.     *buf = '\0';
  603.     return cnt;
  604. }
  605.  
  606. int
  607. domboxbye(argc,argv,p)
  608. int argc;
  609. char *argv[];
  610. void *p;
  611. {
  612.     struct mbx *m;
  613.  
  614.     m = (struct mbx *)p;
  615.     /* Now say goodbye */
  616.     tprintf("Thank you %s, for calling the %s Tcp/Ip Mailbox.\n",m->name,
  617.         Hostname);
  618.     if(m->type == TIP)
  619.         tprintf("Please hang up now.\n");
  620.  
  621.     return -2;    /* signal that exitbbs() should be called */
  622. }
  623. static int
  624. dombhelp(argc,argv,p)
  625. int argc;
  626. char *argv[];
  627. void *p;
  628. {
  629.     char buf[255];
  630.     int i;
  631.     FILE *fp;
  632.     if(*argv[0] == '?') {
  633.         tputs(Longmenu1);
  634.         tputs(Longmenu2);
  635.         tputs(Longmenu3);
  636.         return 0;
  637.     }
  638.     buf[0] = '\0';
  639.     if(argc > 1)
  640.         for(i=0; Mbcmds[i].name != NULLCHAR; ++i)
  641.             if(!strncmp(Mbcmds[i].name,argv[1],strlen(argv[1]))) {
  642.                 sprintf(buf,"%s/%s.hlp",Helpdir,Mbcmds[i].name);
  643.                 break;
  644.             }
  645.     if(buf[0] == '\0')
  646.         if(*argv[0] == 'i')            /* INFO command */
  647.             sprintf(buf,"%s/info.hlp",Helpdir);
  648.         else
  649.             sprintf(buf,"%s/help.hlp",Helpdir);
  650.     if((fp = fopen(buf,READ_TEXT)) != NULLFILE) {
  651.         sendfile(fp,Curproc->output,ASCII_TYPE,0);
  652.         fclose(fp);
  653.     }
  654.     else
  655.         tprintf("No help available. (%s not found)\n",buf);
  656.     return 0;
  657. }
  658.  
  659. static int
  660. dochat(argc,argv,p)
  661. int argc;
  662. char *argv[];
  663. void *p;
  664. {
  665.     char buf[8], *newargv[3];
  666.  
  667.     if(Attended){
  668.         newargv[0] = "telnet";
  669.         newargv[1] = Hostname;
  670.         sprintf(buf,"%d",IPPORT_TTYLINK);
  671.         newargv[2] = buf;
  672.         return dombtelnet(3,newargv,p);
  673.     }
  674.     else {
  675.         tprintf("Sorry - the system is unattended.\007\n");
  676.     }
  677.     /* It returns only after a disconnect or refusal */
  678.     return 0;
  679. }
  680.  
  681. static int
  682. dosend(argc,argv,p)
  683. int argc;
  684. char *argv[];
  685. void *p;
  686. {
  687.     int cccnt = 0, fail = 0;
  688.     char *host, *cp, fullfrom[MBXLINE], sigwork[LINELEN], *rhdr = NULLCHAR;
  689.     struct list *ap, *cclist = NULLLIST;
  690.     struct mbx *m;
  691.     FILE *fp;
  692.  
  693.     m = (struct mbx *)p;
  694.     if((m->stype != 'R' || (m->sid & MBX_SID)) && mbx_to(argc,argv,m)
  695.        == -1){
  696.         if(m->sid & MBX_SID)
  697.             tprintf("NO - syntax error\n");
  698.         else {
  699.             tprintf("S command syntax error - format is:\n");
  700.             tprintf("  S[F] name [@ host] [< from_addr] [$bulletin_id]\n");
  701.             tprintf("  SR [number]\n");
  702.         }
  703.         return 0;
  704.     }
  705.     if(m->stype != 'R' && msgidcheck(m->tomsgid)) {
  706.         if(m->sid & MBX_SID)
  707.             tputs("NO - ");
  708.         tprintf("Already have %s\n",m->tomsgid);
  709.         return 0;
  710.     }
  711.     if(m->stype == 'R' && !(m->sid & MBX_SID) &&
  712.        mbx_reply(argc,argv,m,&cclist,&rhdr) == -1)
  713.         return 0;
  714.     if((cp = rewrite_address(m->to)) != NULLCHAR)
  715.          if(strcmp(m->to,cp) != 0){
  716.           m->origto = m->to;
  717.           m->to = cp;
  718.          }
  719.          else
  720.           free(cp);
  721.     if((m->origto != NULLCHAR || m->stype == 'R') && !(m->sid & MBX_SID))
  722.         tprintf("To: %s\n", m->to);
  723.     if(validate_address(m->to) == 0){
  724.         if(m->sid & MBX_SID)
  725.             tprintf("NO - bad address\n");
  726.         else
  727.             tprintf("Bad user or host name\n");
  728.         free(rhdr);
  729.         del_list(cclist);
  730.         /* We don't free any more buffers here. They are freed upon
  731.          * the next call to mbx_to() or to domboxbye()
  732.          */
  733.         return 0;
  734.     }
  735.  
  736. /* Check to ensure that users without Telnet access don't use external
  737.    Email - added by paul@wolf.demon.co.uk */
  738.     if (!(m->privs & TELNET_CMD)){
  739. /* Bang paths (!), redirectors (%) and hosts other than Hostname are
  740.    disallowed */
  741.         fail+=check_mail_privilege(m->to);
  742.  
  743.         for ( ap = cclist; ap != NULLLIST; ap = ap->next) {
  744.             fail+=check_mail_privilege(ap->val);
  745.         }
  746.  
  747.         if (fail){
  748.             tprintf(Noperm);
  749.             free(rhdr);
  750.             del_list(cclist);
  751.             /* We don't free any more buffers here. They are freed upon
  752.              * the next call to mbx_to() or to domboxbye()
  753.              */
  754.             return 0;
  755.         }
  756.  
  757.  
  758.     }
  759.  
  760.     /* Display the Cc: line (during SR command) */
  761.     for(ap = cclist; ap != NULLLIST; ap = ap->next) {
  762.         if(cccnt == 0){
  763.             tprintf("%s",Hdrs[CC]);
  764.             cccnt = 4;
  765.         }
  766.         else {
  767.             tputs(", ");
  768.             cccnt += 2;
  769.         }
  770.         if(cccnt + strlen(ap->val) > 80 - 3) {
  771.             tputs("\n    ");
  772.             cccnt = 4;
  773.         }
  774.         tputs(ap->val);
  775.         cccnt += strlen(ap->val);
  776.     }
  777.     if(cccnt)
  778.         tputc('\n');
  779.     m->state = MBX_SUBJ;
  780.     if(m->stype != 'R' || (m->sid & MBX_SID) != 0) {
  781.         tprintf((m->sid & MBX_SID) ? "OK\n" : "Subject: ");
  782.         if(mbxrecvline(m->user,m->line,MBXLINE,-1) == -1)
  783.             return 0;
  784.     }
  785.     else                /* Replying to a message */
  786.         tprintf("Subject: %s\n",m->line);
  787.     if(mbx_data(m,cclist,rhdr) == -1){
  788.         free(rhdr);
  789.         del_list(cclist);
  790.         tputs("Can't create temp file for mail\n");
  791.         return 0;
  792.     }
  793.     free(rhdr);
  794.     m->state = MBX_DATA;
  795.     if((m->sid & MBX_SID) == 0 && m->stype != 'F')
  796.         tprintf("Enter message.  %s",Howtoend);
  797.  
  798.     if(m->stype != 'F' || (m->sid & MBX_SID) != 0)
  799.         while(mbxrecvline(m->user,m->line,MBXLINE,-1) != -1){
  800.             if(m->line[0] == 0x01){  /* CTRL-A */
  801.                 fclose(m->tfile);
  802.                 tputs("Aborted.\n");
  803.                 del_list(cclist);
  804.                 return 0;
  805.             }
  806.             if(m->line[0] != CTLZ && stricmp(m->line, "/ex"))
  807.                 fprintf(m->tfile,"%s\n",m->line);
  808.             else
  809.                 break;    /* all done */
  810.         }
  811.     else {
  812.         fprintf(m->tfile,"----- Forwarded message -----\n\n");
  813.         msgtofile(m,m->current,m->tfile,0);
  814.         fprintf(m->tfile,"----- End of forwarded message -----\n");
  815.     }
  816.  
  817.     /* Insert customised signature if one is found */
  818.     if(!(m->sid & MBX_SID)) {    /* not a forwarding BBS */
  819.          sprintf(sigwork,"%s/%s.sig",Signature,
  820.              m->tofrom ? m->tofrom : m->name);
  821.          if((fp = fopen(sigwork,READ_TEXT)) != NULLFILE){
  822.           while(fgets(sigwork,LINELEN,fp) != NULLCHAR)
  823.             fputs(sigwork,m->tfile);
  824.           fclose(fp);
  825.          }
  826.     }
  827.  
  828.     if((host = strrchr(m->to,'@')) == NULLCHAR) {
  829.         host = Hostname;    /* use our hostname */
  830.         if(m->origto != NULLCHAR) {
  831.             /* rewrite_address() will be called again by our
  832.              * SMTP server, so revert to the original address.
  833.              */
  834.              free(m->to);
  835.             m->to = m->origto;
  836.             m->origto = NULLCHAR;
  837.         }
  838.     }
  839.     else
  840.         host++;    /* use the host part of address */
  841.  
  842.     /* make up full from name for work file */
  843.     if(m->tofrom != NULLCHAR)
  844.         sprintf(fullfrom,"%s%%%s.bbs@%s",m->tofrom, m->name, Hostname);
  845.     else
  846.         sprintf(fullfrom,"%s@%s",m->name,Hostname);
  847.     if(cclist != NULLLIST && stricmp(host,Hostname) != 0) {
  848.         fseek(m->tfile,0L,0);    /* reset to beginning */
  849.         fail = queuejob(m->tfile,Hostname,cclist,fullfrom,NULL);
  850.         del_list(cclist);
  851.         cclist = NULLLIST;
  852.     }
  853.     addlist(&cclist,m->to,0);
  854.     fseek(m->tfile,0L,0);
  855.     fail += queuejob(m->tfile,host,cclist,fullfrom,NULL);
  856.     del_list(cclist);
  857.     fclose(m->tfile);
  858.     if(fail)
  859.          tputs("Couldn't queue message for delivery\n");
  860.     else
  861.          if(m->tomsgid != NULLCHAR &&
  862.         (fp = fopen(Historyfile,APPEND_TEXT)) != NULLFILE) {
  863.           fprintf(fp,"%s\n",m->tomsgid); /* Save BID in history file */
  864.           fclose(fp);
  865.          }
  866.     smtptick(NULL);        /* wake SMTP to send that mail */
  867.     return 0;
  868. }
  869.  
  870. static int
  871. dosid(argc,argv,p)
  872. int argc;
  873. char *argv[];
  874. void *p;
  875. {
  876.     struct mbx *m;
  877.     char *cp;
  878.  
  879.     m = (struct mbx *)p;
  880.     if(argc == 1)
  881.         return 1;
  882.     if(argv[1][strlen(argv[1]) - 1] != ']') /* must be an SID */
  883.         return 1;
  884.     m->sid = MBX_SID;
  885.     /* Now check to see if this is an RLI board.
  886.      * As usual, Hank does it a bit differently from
  887.      * the rest of the world.
  888.      */
  889.     if(m->stype == 'R' && strncmp(argv[1],"li",2) == 0)/* [RLI] at a minimum */
  890.         m->sid |= MBX_RLI_SID;
  891.     /* Check to see if the BBS supports a kludge called "hierarchical
  892.      * routing designators."
  893.      *
  894.      * No need to check for ']' -- it must be there or this is not
  895.      * a valid mbox id -- it is checked earlier (fix de OH3LKU)
  896.      */
  897.     if((cp = strchr(argv[1],'-')) != NULLCHAR
  898.      && (cp=strchr(cp+1,'h')) != NULLCHAR
  899.      && strchr(cp+1,'$'))
  900.         m->sid |= MBX_HIER_SID;    
  901.     return 0;
  902. }
  903.  
  904. int
  905. dombescape(argc,argv,p)
  906. int argc;
  907. char *argv[];
  908. void *p;
  909. {
  910.     struct mbx *m;
  911.  
  912.     m = (struct mbx *)p;
  913.     if(argc < 2){
  914.         tprintf("The escape character is: ");
  915.         if(m->escape < 32)
  916.             tprintf("CTRL-%c\n",m->escape+'A'-1);
  917.         else
  918.             tprintf("'%c'\n",m->escape);
  919.         return 0;
  920.     }
  921.     if(strlen(argv[1]) > 1)
  922.         if(isdigit(*argv[1]))
  923.             m->escape = (char) atoi(argv[1]);
  924.         else
  925.             return 1;
  926.     else
  927.         m->escape = *argv[1];
  928.     return 0;
  929. }
  930.  
  931. static int
  932. dodownload(argc,argv,p)
  933. int argc;
  934. char *argv[];
  935. void *p;
  936. {
  937.     struct mbx *m;
  938.     FILE *fp;
  939.     char *file;
  940.  
  941.     m = (struct mbx *)p;
  942.     file = pathname(m->path,argv[1]);
  943.     if(!permcheck(m->path,m->privs,RETR_CMD,file)){
  944.         tprintf(Noperm);
  945.         return 0;
  946.     }
  947.     if((fp = fopen(file,READ_TEXT)) == NULLFILE)
  948.         tprintf("Can't open \"%s\": %s\n",file,sys_errlist[errno]);
  949.     else
  950.         if(m->stype == 'U'){            /* uuencode ? */
  951.             fclose(fp);
  952.             fp = fopen(file,READ_BINARY);    /* assume non-ascii */
  953.             uuencode(fp,m->user,file);
  954.         } else
  955.             sendfile(fp,m->user,ASCII_TYPE,0);
  956.     free(file);
  957.     fclose(fp);
  958.     return 0;
  959. }
  960.  
  961. static int
  962. dombupload(argc,argv,p)
  963. int argc;
  964. char *argv[];
  965. void *p;
  966. {
  967.     struct mbx *m;
  968.     FILE *fp;
  969.     char *file, buf[LINELEN];
  970.  
  971.     m = (struct mbx *)p;
  972.     file = pathname(m->path,argv[1]);
  973.     if(!permcheck(m->path,m->privs,STOR_CMD,file)){
  974.         tprintf(Noperm);
  975.         return 0;
  976.     }
  977.     if((fp = fopen(file,WRITE_TEXT)) == NULLFILE){
  978.         tprintf("Can't create \"%s\": %s\n",file,sys_errlist[errno]);
  979.         free(file);
  980.         return 0;
  981.     }
  982.     log(m->user,"MBOX upload: %s",file);
  983.     tprintf("Send file,  %s",Howtoend);
  984.     for(;;){
  985.         if(mbxrecvline(m->user,buf,LINELEN,-1) == -1){
  986.             unlink(file);
  987.             break;
  988.         }
  989.         if(buf[0] == 0x01){  /* CTRL-A */
  990.             unlink(file);
  991.             tprintf("Aborted.\n");
  992.             break;
  993.         }
  994.         if(buf[0] == CTLZ || !stricmp("/ex",buf))
  995.             break;
  996.         fputs(buf,fp);
  997. #if !defined(UNIX) && !defined(__TURBOC__) && !defined(AMIGA)
  998.         /* Needed only if the OS uses a CR/LF
  999.          * convention and putc doesn't do
  1000.          * an automatic translation
  1001.          */
  1002.         if(putc('\r',fp) == EOF)
  1003.             break;
  1004. #endif
  1005.         if(putc('\n',fp) == EOF)
  1006.             break;
  1007.     }
  1008.     free(file);
  1009.     fclose(fp);
  1010.     return 0;
  1011. }
  1012.  
  1013. static int
  1014. dowhat(argc,argv,p)
  1015. int argc;
  1016. char *argv[];
  1017. void *p;
  1018. {
  1019.     struct mbx *m;
  1020.     FILE *fp;
  1021.     char *file;
  1022.  
  1023.     m = (struct mbx *)p;
  1024.     if(argc < 2)
  1025.         file = strdup(m->path);
  1026.     else
  1027.         file = pathname(m->path,argv[1]);
  1028.     if(!permcheck(m->path,m->privs,RETR_CMD,file)){
  1029.         tprintf(Noperm);
  1030.         return 0;
  1031.     }
  1032.     if((fp = dir(file,1)) == NULLFILE)
  1033.         tprintf("Can't read directory: \"%s\": %s\n",file,sys_errlist[errno]);
  1034.     else
  1035.         sendfile(fp,m->user,ASCII_TYPE,0);
  1036.     free(file);
  1037.     fclose(fp);
  1038.     return 0;
  1039. }
  1040.  
  1041. static int
  1042. dozap(argc,argv,p)
  1043. int argc;
  1044. char *argv[];
  1045. void *p;
  1046. {
  1047.     struct mbx *m;
  1048.     char *file;
  1049.  
  1050.     m = (struct mbx *)p;
  1051.     file = pathname(m->path,argv[1]);
  1052.     if(!permcheck(m->path,m->privs,DELE_CMD,file)){
  1053.         tprintf(Noperm);
  1054.         return 0;
  1055.     }
  1056.     if(unlink(file))
  1057.         tprintf("Zap failed: %s\n",sys_errlist[errno]);
  1058.     log(m->user,"MBOX Zap: %s",file);
  1059.     free(file);
  1060.     return 0;
  1061. }
  1062.  
  1063. static int
  1064. dosysop(argc,argv,p)
  1065. int argc;
  1066. char *argv[];
  1067. void *p;
  1068. {
  1069.     struct mbx *m;
  1070.     int c;
  1071.     extern struct cmds Cmds[];
  1072.  
  1073.     m = (struct mbx *) p;
  1074.     if(!(m->privs & SYSOP_CMD)){
  1075.         tprintf(Noperm);
  1076.         return 0;
  1077.     }
  1078.     dombescape(1,NULLCHARP,p);
  1079.     for(;;){
  1080.         tprintf("Net> ");
  1081.         usflush(Curproc->output);
  1082.         c = mbxrecvline(Curproc->input,m->line,MBXLINE,m->escape);
  1083.         if(c == EOF || c == -2)
  1084.             break;
  1085.         log(m->user,"MBOX sysop: %s",m->line);
  1086.         cmdparse(Cmds,m->line,NULL);
  1087.     }
  1088.     return 0;
  1089. }
  1090.  
  1091. /* Handle the "*** Done" command when reverse forwarding ends or the
  1092.  * "*** LINKED to" command.
  1093.  */
  1094. static int
  1095. dostars(argc,argv,p)
  1096. int argc;
  1097. char *argv[];
  1098. void *p;
  1099. {
  1100.     struct mbx *m;
  1101.     int anony = 1;
  1102.     m = (struct mbx *)p;
  1103.     /* The "*** LINKED to" command is only allowed to stations with
  1104.      * SYSOP privileges to prevent others from obtaining the same.
  1105.      */
  1106.     if((m->privs & SYSOP_CMD) && argc == 4 && !strcmp(argv[1],"linked")) {
  1107.         strcpy(m->name,argv[3]);
  1108.         /* Try to find the privileges of this user from the userfile */
  1109.         if((m->privs = userlogin(m->name,NULLCHAR,&m->path,MBXLINE,
  1110.                      &anony)) == -1)
  1111.              if((m->privs = userlogin("bbs",NULLCHAR,&m->path,
  1112.                       MBXLINE,&anony)) == -1)
  1113.               if((m->privs = userlogin("anonymous",NULLCHAR,
  1114.                        &m->path,MBXLINE,&anony)) == -1){
  1115.                         m->privs = 0;
  1116.                         free(m->path);
  1117.                         m->path = NULLCHAR;
  1118.               }
  1119.         tprintf("Oh, hello %s.\n",m->name);
  1120.         if(m->privs & EXCLUDED_CMD)
  1121.             return domboxbye(0,NULLCHARP,p);
  1122.         changearea(m,m->name);
  1123.         return 0;
  1124.     }
  1125.     if(argc > 1 && (m->sid & MBX_SID))    /* "*** Done" or similar */
  1126.         return 2;
  1127.     return -1;
  1128. }
  1129.  
  1130. static int
  1131. doarea(argc,argv,p)
  1132. int argc;
  1133. char *argv[];
  1134. void *p;
  1135. {
  1136.     struct mbx *m;
  1137.     FILE *fp;
  1138.  
  1139.     m = (struct mbx *) p;
  1140.     if(argc < 2){
  1141.         tprintf("Current message area is: %s\n",m->area);
  1142.         tprintf("Available areas are:\n%-15s  Your private mail area\n",
  1143.           m->name);
  1144.         if((fp = fopen(Arealist,READ_TEXT)) == NULLFILE)
  1145.             return 0;
  1146.         sendfile(fp,m->user,ASCII_TYPE,0);
  1147.         fclose(fp);
  1148.         return 0;
  1149.     }
  1150.     if((m->privs & SYSOP_CMD) || strcmp(m->name,argv[1]) == 0){
  1151.         changearea(m,argv[1]);
  1152.         if(m->nmsgs){
  1153.             if(!strcmp(m->name,m->area))
  1154.                 tprintf("You have ");
  1155.             else
  1156.                 tprintf("%s: ",m->area);
  1157.             tprintf("%d message%s -  %d new.\n", m->nmsgs,
  1158.               m->nmsgs == 1 ? " " : "s ", m->newmsgs);
  1159.         }
  1160.         return 0;
  1161.     }
  1162.     if(isarea(argv[1])) {
  1163.         changearea(m,argv[1]);
  1164.         tprintf("%s: %d message%s.\n", m->area, m->nmsgs,
  1165.           m->nmsgs == 1 ? "" : "s");
  1166.     }
  1167.     else
  1168.         tprintf("No such message area: %s\n",argv[1]);
  1169.     return 0;
  1170. }
  1171.  
  1172. /* subroutine to do the actual switch from one area to another */
  1173. void
  1174. changearea(m,area)
  1175. struct mbx *m;
  1176. char *area;
  1177. {
  1178.     closenotes(m);
  1179.     m->nmsgs = m->newmsgs = m->current = 0;
  1180.     strcpy(m->area,area);
  1181.     scanmail(m);
  1182. }
  1183.  
  1184. static int
  1185. dombtelnet(argc,argv,p)
  1186. int argc;
  1187. char *argv[];
  1188. void *p;
  1189. {
  1190.     struct mbx *m;
  1191.     int s, len, i;
  1192.     char dsocket[MAXSOCKSIZE];
  1193.     struct sockaddr_in fsocket;
  1194.  
  1195.     m = (struct mbx *) p;
  1196.     fsocket.sin_family = AF_INET;
  1197.     if(argc < 3)
  1198.         fsocket.sin_port = IPPORT_TELNET;
  1199.     else
  1200.         fsocket.sin_port = atoi(argv[2]);
  1201.  
  1202.     if((fsocket.sin_addr.s_addr = resolve(argv[1])) == 0){
  1203.         tprintf(Badhost,argv[1]);
  1204.         return 0;
  1205.     }
  1206.     /* Only local telnets are are allowed to the unprivileged user */
  1207.     if(!(m->privs & TELNET_CMD) && !ismyaddr(fsocket.sin_addr.s_addr)){
  1208.         tprintf(Noperm);
  1209.         return 0;
  1210.     }
  1211.     if((s = socket(AF_INET,SOCK_STREAM,0)) == -1){
  1212.         tprintf(Nosock);
  1213.         return 0;
  1214.     }
  1215.     if(fsocket.sin_port == IPPORT_TTYLINK) {
  1216.         m->startmsg = mallocw(80);
  1217.         len = MAXSOCKSIZE;
  1218.         i = getpeername(m->user,dsocket,&len);
  1219.         sprintf(m->startmsg,"*** Incoming call from %s@%s ***\n",
  1220.             m->name,i != -1 ? psocket(dsocket): Hostname);
  1221.     }
  1222.     return gw_connect(m,s,(char *)&fsocket,SOCKSIZE);
  1223. }
  1224.  
  1225. static int
  1226. dombfinger(argc,argv,p)
  1227. int argc;
  1228. char *argv[];
  1229. void *p;
  1230. {
  1231.     struct mbx *m;
  1232.     char *host, *user = NULLCHAR, buf[8], *newargv[3];
  1233.  
  1234.     if(argc > 2){
  1235.         tprintf("Usage: F user@host  or  F @host  or  F user.\n");
  1236.         return 0;
  1237.     }
  1238.     host = Hostname;
  1239.     if(argc == 2){
  1240.         if((host = strchr(argv[1], '@')) != NULLCHAR){
  1241.             *host = '\0';
  1242.             host++;
  1243.         } else
  1244.             host = Hostname;
  1245.         user = argv[1];
  1246.     }
  1247.     m = (struct mbx *) p;
  1248.     m->startmsg = mallocw(80);
  1249.     if(user != NULLCHAR)
  1250.         sprintf(m->startmsg,"%s\n",user);
  1251.     else
  1252.         strcpy(m->startmsg,"\n");
  1253.     newargv[0] = "telnet";
  1254.     newargv[1] = host;
  1255.     sprintf(buf,"%d",IPPORT_FINGER);
  1256.     newargv[2] = buf;
  1257.     return dombtelnet(3,newargv,p);
  1258. }
  1259.  
  1260. /* Generic mbox gateway code. It sends and frees the contents of m->startmsg
  1261.  * when the connection has been established unless it a null pointer.
  1262.  */
  1263. int
  1264. gw_connect(m,s,fsocket,len)
  1265. struct mbx *m;
  1266. int s;
  1267. char *fsocket;
  1268. int len;
  1269. {
  1270.     int c;
  1271.     char *cp;
  1272.     struct proc *child;
  1273.     struct gwalarm *gwa;
  1274.  
  1275.     sockmode(s,SOCK_ASCII);
  1276.     child = newproc("gateway supervisor",256,gw_superv,0,Curproc,m,0);
  1277.     tprintf("Trying %s...  ",psocket((struct sockaddr *)fsocket));
  1278.     dombescape(0,NULLCHARP,(void *)m);
  1279.     usflush(Curproc->output);
  1280.     if(connect(s,fsocket,len) == -1){
  1281.         cp = sockerr(s);
  1282.         tprintf("Connection failed: ");
  1283.         if(cp != NULLCHAR)
  1284.             tprintf("%s errno %d\n",cp,errno);
  1285.         else
  1286.             tprintf("Escape character sent.\n");
  1287.         free(m->startmsg);
  1288.         m->startmsg = NULLCHAR;
  1289.         killproc(child);
  1290.         close_s(s);
  1291.         return 0;
  1292.     }
  1293.     /* The user did not type the escape character */
  1294.     killproc(child);
  1295.     tputs("Connected.\n");
  1296.     
  1297.     if(m->startmsg != NULLCHAR){
  1298.         usputs(s,m->startmsg);
  1299.         free(m->startmsg);
  1300.         m->startmsg = NULLCHAR;
  1301.     }
  1302.     /* Since NOS does not flush the output socket after a certain
  1303.      * period of time, we have to arrange that ourselves.
  1304.      */
  1305.     gwa = (struct gwalarm *) mallocw(sizeof(struct gwalarm));
  1306.     gwa->s1 = Curproc->output;
  1307.     gwa->s2 = s;
  1308.     set_timer(&gwa->t,240L);
  1309.     gwa->t.func = gw_alarm;
  1310.     gwa->t.arg = (void *) gwa;
  1311.     start_timer(&gwa->t);
  1312.     /* Fork off the receive process */
  1313.     child = newproc("gateway in",1024,gw_input,s,NULL,Curproc,0);
  1314.     
  1315.     for(;;){
  1316.         if((c = recvchar(Curproc->input)) == EOF)
  1317.             break;
  1318.         if(c == m->escape){
  1319.             tputs("Disconnecting.");
  1320.             if(socklen(Curproc->input,0))
  1321.                 recv_mbuf(Curproc->input,NULL,0,NULLCHAR,0);
  1322.             break;
  1323.         }
  1324.         if(usputc(s,(char)c) == EOF)
  1325.             break;
  1326.     }
  1327.     stop_timer(&gwa->t);
  1328.     free((char *)gwa);
  1329.     close_s(s);
  1330.     killproc(child); /* get rid of the receive process */
  1331.     tprintf("%c%c%c\n",IAC,WONT,TN_ECHO);
  1332.     return 0;
  1333. }
  1334.  
  1335. static void
  1336. gw_input(s,notused,p)
  1337. int s;
  1338. void *notused;
  1339. void *p;
  1340. {
  1341.     int c;
  1342.     char *cp;
  1343.     struct proc *parent;
  1344.  
  1345.     parent = (struct proc *) p;
  1346.     while((c = recvchar(s)) != EOF)
  1347.         tputc((char)c);
  1348.     tprintf("Disconnected ");
  1349.     cp = sockerr(s);
  1350.     if(cp != NULLCHAR)
  1351.         tputs(cp);
  1352.     /* Tell the parent that we are no longer connected */
  1353.     alert(parent,ENOTCONN);
  1354.     pwait(Curproc); /* Now wait to be killed */
  1355. }
  1356.  
  1357. /* Check if the escape character is typed while the parent process is busy
  1358.  * doing other things. 
  1359.  */
  1360. static void
  1361. gw_superv(null,proc,p)
  1362. int null;
  1363. void *proc;
  1364. void *p;
  1365. {
  1366.     struct proc *parent;
  1367.     struct mbx *m;
  1368.     int c;
  1369.     parent = (struct proc *) proc;
  1370.     m = (struct mbx *) p;
  1371.     while((c = recvchar(Curproc->input)) != EOF)
  1372.         if(c == m->escape){
  1373.             /* flush anything in the input queue */
  1374.             if(socklen(Curproc->input,0))
  1375.                 recv_mbuf(Curproc->input,NULL,0,NULLCHAR,0);
  1376.             break;
  1377.         }
  1378.     alert(parent,EINTR);     /* Tell the parent to quit */
  1379.     pwait(Curproc);         /* Please kill me */
  1380. }
  1381.  
  1382. static void
  1383. gw_alarm(p)
  1384. void *p;
  1385. {
  1386.     struct gwalarm *gwa = (struct gwalarm *)p;
  1387.     char oldbl;
  1388.     struct usock *up;
  1389.  
  1390.     /* Flush sockets s1 and s2, but first make sure that the socket
  1391.      * is set to non-blocking mode, to prevent the flush from blocking
  1392.      * if the high water mark has been reached.
  1393.      */
  1394.     if((up = itop(gwa->s1)) != NULLUSOCK) {
  1395.         oldbl = up->noblock;
  1396.         up->noblock = 1;
  1397.         usflush(gwa->s1);
  1398.         up->noblock = oldbl;
  1399.     }
  1400.     if((up = itop(gwa->s2)) != NULLUSOCK) {
  1401.         oldbl = up->noblock;
  1402.         up->noblock = 1;
  1403.         usflush(gwa->s2);
  1404.         up->noblock = oldbl;
  1405.     }
  1406.     start_timer(&gwa->t);
  1407. }
  1408.  
  1409. /* States for send line parser state machine */
  1410. #define        LOOK_FOR_USER        2
  1411. #define        IN_USER            3
  1412. #define        AFTER_USER        4
  1413. #define        LOOK_FOR_HOST        5
  1414. #define        IN_HOST            6
  1415. #define        AFTER_HOST        7
  1416. #define        LOOK_FOR_FROM        8
  1417. #define        IN_FROM            9
  1418. #define        AFTER_FROM        10
  1419. #define        LOOK_FOR_MSGID        11
  1420. #define        IN_MSGID        12
  1421. #define        FINAL_STATE        13
  1422. #define        ERROR_STATE        14
  1423.  
  1424. /* Prepare the addressee.  If the address is bad, return -1, otherwise
  1425.  * return 0
  1426.  */
  1427. static int
  1428. mbx_to(argc,argv,p)
  1429. int argc;
  1430. char *argv[];
  1431. void *p;
  1432. {
  1433.     register char *cp;
  1434.     int state, i;
  1435.     char *user, *host, *from, *msgid;
  1436.     int userlen = 0, hostlen = 0, fromlen = 0, msgidlen = 0;
  1437.     struct mbx *m;
  1438.  
  1439.     m = (struct mbx *)p;
  1440.     /* Free anything that might be allocated
  1441.      * since the last call to mbx_to() or mbx_reply()
  1442.      */
  1443.     free(m->to);
  1444.     m->to = NULLCHAR;
  1445.     free(m->tofrom);
  1446.     m->tofrom = NULLCHAR;
  1447.     free(m->tomsgid);
  1448.     m->tomsgid = NULLCHAR;
  1449.     free(m->origto);
  1450.     m->origto = NULLCHAR;
  1451.  
  1452.     if(argc == 1)
  1453.         return -1;
  1454.     i = 1;
  1455.     cp = argv[i];
  1456.     state = LOOK_FOR_USER;
  1457.     while(state < FINAL_STATE){
  1458. #ifdef MBDEBUG
  1459.         tprintf("State is %d, char is %c\n", state, *cp);
  1460. #endif
  1461.         switch(state){
  1462.         case LOOK_FOR_USER:
  1463.             if(*cp == '@' || *cp == '<' || *cp == '$'){
  1464.                 state = ERROR_STATE;        /* no user */
  1465.             } else {
  1466.                 user = cp;            /* point at start */
  1467.                 userlen++;            /* start counting */
  1468.                 state = IN_USER;
  1469.             }
  1470.             break;
  1471.         case IN_USER:
  1472.             switch(*cp){
  1473.             case '\0':
  1474.                 state = AFTER_USER;        /* done with username */
  1475.                 break;
  1476.             case '@':
  1477.                 state = LOOK_FOR_HOST;        /* hostname should follow */
  1478.                 break;
  1479.             case '<':
  1480.                 state = LOOK_FOR_FROM;        /* from name should follow */
  1481.                 break;
  1482.             case '$':
  1483.                 state = LOOK_FOR_MSGID;    /* message id should follow */
  1484.                 break;
  1485.             default:
  1486.                 userlen++;            /* part of username */
  1487.             }
  1488.             break;
  1489.         case AFTER_USER:
  1490.             switch(*cp){
  1491.             case '@':
  1492.                 state = LOOK_FOR_HOST;        /* hostname follows */
  1493.                 break;
  1494.             case '<':
  1495.                 state = LOOK_FOR_FROM;        /* fromname follows */
  1496.                 break;
  1497.             case '$':
  1498.             state = LOOK_FOR_MSGID;    /* message id follows */
  1499.                 break;
  1500.             default:
  1501.                 state = ERROR_STATE;
  1502.             }
  1503.             break;
  1504.         case LOOK_FOR_HOST:
  1505.             if(*cp == '@' || *cp == '<' || *cp == '$'){
  1506.                 state = ERROR_STATE;
  1507.                 break;
  1508.             }
  1509.             if(*cp == '\0')
  1510.                 break;
  1511.             host = cp;
  1512.             hostlen++;
  1513.             state = IN_HOST;
  1514.             break;
  1515.         case IN_HOST:
  1516.             switch(*cp){
  1517.             case '\0':
  1518.                 state = AFTER_HOST;        /* found user@host */
  1519.                 break;
  1520.             case '@':
  1521.                 state = ERROR_STATE;        /* user@host@? */
  1522.                 break;
  1523.             case '<':
  1524.                 state = LOOK_FOR_FROM;        /* fromname follows */
  1525.                 break;
  1526.             case '$':
  1527.                 state = LOOK_FOR_MSGID;    /* message id follows */
  1528.                 break;
  1529.             default:
  1530.                 hostlen++;
  1531.             }
  1532.             break;
  1533.         case AFTER_HOST:
  1534.             switch(*cp){
  1535.             case '@':
  1536.                 state = ERROR_STATE;        /* user@host @ */
  1537.                 break;
  1538.             case '<':
  1539.                 state = LOOK_FOR_FROM;        /* user@host < */
  1540.                 break;
  1541.             case '$':
  1542.                 state = LOOK_FOR_MSGID;    /* user@host $ */
  1543.                 break;
  1544.             default:
  1545.                 state = ERROR_STATE;        /* user@host foo */
  1546.             }
  1547.             break;
  1548.         case LOOK_FOR_FROM:
  1549.             if(*cp == '@' || *cp == '<' || *cp == '$'){
  1550.                 state = ERROR_STATE;
  1551.                 break;
  1552.             }
  1553.             if(*cp == '\0')
  1554.                 break;
  1555.             from = cp;
  1556.             fromlen++;
  1557.             state = IN_FROM;
  1558.             break;
  1559.         case IN_FROM:
  1560.             switch(*cp){
  1561.             case '\0':
  1562.                 state = AFTER_FROM;        /* user@host <foo */
  1563.                 break;
  1564.             case '<':
  1565.                 state = ERROR_STATE;        /* user@host <foo< */
  1566.                 break;
  1567.             case '$':
  1568.                 state = LOOK_FOR_MSGID;    /* message id follows */
  1569.                 break;
  1570.             default:
  1571.                 fromlen++;
  1572.             }
  1573.             break;
  1574.         case AFTER_FROM:
  1575.             switch(*cp){
  1576.             case '@':                /* user@host <foo @ */
  1577.             case '<':                /* user@host <foo < */
  1578.                 state = ERROR_STATE;
  1579.                 break;
  1580.             case '$':
  1581.                 state = LOOK_FOR_MSGID;    /* user@host <foo $ */
  1582.                 break;
  1583.             default:
  1584.                 state = ERROR_STATE;        /* user@host foo */
  1585.             }
  1586.             break;
  1587.         case LOOK_FOR_MSGID:
  1588.             if(*cp == '\0')
  1589.                 break;
  1590.             msgid = cp;
  1591.             msgidlen++;
  1592.             state = IN_MSGID;
  1593.             break;
  1594.         case IN_MSGID:
  1595.             if(*cp == '\0')
  1596.                 state = FINAL_STATE;
  1597.             else
  1598.                 msgidlen++;
  1599.             break;
  1600.         default:
  1601.             /* what are we doing in this state? */
  1602.             state = ERROR_STATE;
  1603.         }
  1604.         if(*(cp) == '\0'){
  1605.             ++i;
  1606.             if(i < argc)
  1607.             cp = argv[i];
  1608.             else break;
  1609.         } else
  1610.             ++cp;
  1611.     }
  1612.     if(state == ERROR_STATE || state == LOOK_FOR_HOST
  1613.      || state == LOOK_FOR_FROM || state == LOOK_FOR_MSGID)
  1614.         return -1;        /* syntax error */
  1615.  
  1616.     m->to = mallocw(userlen + hostlen + 2);
  1617.  
  1618.     strncpy(m->to, user, userlen);
  1619.     m->to[userlen] = '\0';
  1620.  
  1621.     if(hostlen){
  1622.         m->to[userlen] = '@';
  1623.         strncpy(m->to + userlen + 1, host, hostlen);
  1624.         m->to[userlen + hostlen + 1] = '\0';
  1625.     }
  1626.     if(fromlen){
  1627.         m->tofrom = mallocw(fromlen + 1);
  1628.         strncpy(m->tofrom, from, fromlen);
  1629.         m->tofrom[fromlen] = '\0';
  1630.     }
  1631.     if(msgidlen){
  1632.         m->tomsgid = mallocw(msgidlen + 1);
  1633.         strncpy(m->tomsgid, msgid, msgidlen);
  1634.         m->tomsgid[msgidlen] = '\0';
  1635.     }
  1636.     return 0;
  1637. }
  1638.  
  1639. /* This opens the data file and writes the mail header into it.
  1640.  * Returns 0 if OK, and -1 if not.
  1641.  */
  1642. static int
  1643. mbx_data(m,cclist,extra)
  1644. struct mbx *m;
  1645. struct list *cclist;    /* list of carbon copy recipients */
  1646. char *extra;        /* optional extra header lines */
  1647. {
  1648.     time_t t;
  1649.     struct list *ap;
  1650.     int cccnt = 0;
  1651.     
  1652.     if((m->tfile = tmpfile()) == NULLFILE)
  1653.         return -1;
  1654.     time(&t);
  1655.     fprintf(m->tfile,Hdrs[RECEIVED]);
  1656.     if(m->tofrom != NULLCHAR)
  1657.         fprintf(m->tfile,"from %s.bbs ",m->name);
  1658.     fprintf(m->tfile,"by %s (%s)\n\tid AA%ld ; %s",
  1659.         Hostname, Version, get_msgid(), ptime(&t));
  1660.     fprintf(m->tfile,"%s%s",Hdrs[DATE],ptime(&t));
  1661.     fprintf(m->tfile,Hdrs[MSGID]);
  1662.     if(m->tomsgid)
  1663.         fprintf(m->tfile,"<%s@%s.bbs>\n", m->tomsgid, m->name);
  1664.     else
  1665.         fprintf(m->tfile,"<%ld@%s>\n",get_msgid(), Hostname);
  1666.     fprintf(m->tfile,Hdrs[FROM]);
  1667.     if(m->tofrom)
  1668.         fprintf(m->tfile,"%s%%%s.bbs@%s\n",
  1669.             m->tofrom, m->name, Hostname);
  1670.     else
  1671.         fprintf(m->tfile,"%s@%s\n", m->name, Hostname);
  1672.     fprintf(m->tfile,"%s%s\n",Hdrs[TO],m->origto != NULLCHAR ? m->origto : m->to);
  1673.     /* Write Cc: line */
  1674.     for(ap = cclist; ap != NULLLIST; ap = ap->next) {
  1675.         if(cccnt == 0){
  1676.             fprintf(m->tfile,"%s",Hdrs[CC]);
  1677.             cccnt = 4;
  1678.         }
  1679.         else {
  1680.                fprintf(m->tfile,", ");
  1681.                cccnt += 2;
  1682.         }
  1683.         if(cccnt + strlen(ap->val) > 80 - 3) {
  1684.                fprintf(m->tfile,"\n    ");
  1685.                cccnt = 4;
  1686.         }
  1687.         fputs(ap->val,m->tfile);
  1688.         cccnt += strlen(ap->val);
  1689.     }
  1690.     if(cccnt)
  1691.         fputc('\n',m->tfile);
  1692.     fprintf(m->tfile,"%s%s\n",Hdrs[SUBJECT],m->line);
  1693.     if(!isspace(m->stype) && ((m->stype != 'R' && m->stype != 'F') ||
  1694.       (m->sid & MBX_SID) !=0))
  1695.           fprintf(m->tfile,"%s%c\n", Hdrs[BBSTYPE],m->stype);
  1696.     if(extra != NULLCHAR)
  1697.         fprintf(m->tfile,extra);
  1698.     fprintf(m->tfile,"\n");
  1699.  
  1700.     return 0;
  1701. }
  1702.  
  1703. /* Returns true if string is in history file or if string appears to be a
  1704.  * message id generated by our system.
  1705.  */
  1706. static int
  1707. msgidcheck(string)
  1708. char *string;
  1709. {
  1710.      FILE *fp;
  1711.      char buf[LINELEN], *cp;
  1712.      if(string == NULLCHAR)
  1713.       return 0;
  1714.      /* BID's that we have generated ourselves are not kept in the history
  1715.       * file. Such BID's are in the nnnn_hhhh form, where hhhh is a part of
  1716.       * our hostname, truncated so that the BID is no longer than 11
  1717.       * characters.
  1718.       */
  1719.      if((cp = strchr(string,'_')) != NULLCHAR && *(cp+1) != '\0' && 
  1720.     strnicmp(cp+1,Hostname,strlen(cp+1)) == 0)
  1721.       return 1;
  1722.  
  1723.      if((fp = fopen(Historyfile,READ_TEXT)) == NULLFILE)
  1724.       return 0;
  1725.      while(fgets(buf,LINELEN,fp) != NULLCHAR) {
  1726.       rip(buf);
  1727.       if(stricmp(string,buf) == 0) {    /* found */
  1728.            fclose(fp);
  1729.            return 1;
  1730.       }
  1731.      }
  1732.      fclose(fp);
  1733.      return 0;
  1734. }
  1735.      
  1736. /* Read the rewrite file for lines where the first word is a regular
  1737.  * expression and the second word are rewriting rules. The special
  1738.  * character '$' followed by a digit denotes the string that matched
  1739.  * a '*' character. The '*' characters are numbered from 1 to 9.
  1740.  * Example: the line "*@*.* $2@$1.ampr.org" would rewrite the address
  1741.  * "foo@bar.xxx" to "bar@foo.ampr.org".
  1742.  * $H is replaced by our hostname, and $$ is an escaped $ character.
  1743.  * If the third word on the line has an 'r' character in it, the function
  1744.  * will recurse with the new address.
  1745.  */
  1746. char *
  1747. rewrite_address(addr)
  1748. char *addr;
  1749. {
  1750.     char *argv[10], buf[MBXLINE], *cp, *cp2, *retstr;
  1751.     int cnt;
  1752.     FILE *fp;
  1753.     if ((fp = fopen(Rewritefile,READ_TEXT)) == NULLFILE)
  1754.         return NULLCHAR;
  1755.     memset((char *)argv,0,10*sizeof(char *));
  1756.     while(fgets(buf,MBXLINE,fp) != NULLCHAR) {
  1757.         if(*buf == '#')        /* skip commented lines */
  1758.             continue;
  1759.         string_tidy(buf);   /* clean up the buffer */
  1760.         if((cp = strchr(buf,' ')) == NULLCHAR) /* get the first word */
  1761.             if((cp = strchr(buf,'\t')) == NULLCHAR)
  1762.                 continue;
  1763.         *cp = '\0';
  1764.         if((cp2 = strchr(buf,'\t')) != NULLCHAR){
  1765.             *cp = ' ';
  1766.             cp = cp2;
  1767.             *cp = '\0';
  1768.         }
  1769.         if(!wildmat(addr,buf,argv))
  1770.             continue;        /* no match */
  1771.         rip(++cp);
  1772.         cp2 = retstr = (char *) callocw(1,MBXLINE);
  1773.         while(*cp != '\0' && *cp != ' ' && *cp != '\t')
  1774.             if(*cp == '$') {
  1775.                 if(isdigit(*(++cp)))
  1776.                     if(argv[*cp - '0'-1] != '\0')
  1777.                         strcat(cp2,argv[*cp - '0'-1]);
  1778.                 if(*cp == 'h' || *cp == 'H') /* Our hostname */
  1779.                     strcat(cp2,Hostname);
  1780.                 if(*cp == '$')    /* Escaped $ character */
  1781.                     strcat(cp2,"$");
  1782.                 cp2 = retstr + strlen(retstr);
  1783.                 cp++;
  1784.             }
  1785.             else
  1786.                 *cp2++ = *cp++;
  1787.         for(cnt=0; argv[cnt] != NULLCHAR; ++cnt)
  1788.             free(argv[cnt]);
  1789.         fclose(fp);
  1790.         /* If there remains an 'r' character on the line, repeat
  1791.          * everything by recursing.
  1792.          */
  1793.         if(strchr(cp,'r') != NULLCHAR || strchr(cp,'R') != NULLCHAR) {
  1794.             if((cp2 = rewrite_address(retstr)) != NULLCHAR) {
  1795.                 free(retstr);
  1796.                 return cp2;
  1797.             }
  1798.         }
  1799.         return retstr;
  1800.     }
  1801.     fclose(fp);
  1802.     return NULLCHAR;
  1803. }
  1804.  
  1805. /* uuencode a file -- translated from C++; both versions copyright 1990
  1806.    by David R. Evans, G4AMJ/NQ0I
  1807. */
  1808.  
  1809. static int
  1810. uuencode(infile,s,infilename)
  1811. FILE *infile;
  1812. int s;            /* output socket */
  1813. char *infilename;
  1814. {
  1815.   unsigned long cnt = 0L;            /* 11 Mar 92  GT  BC3 couldn't cope.    */
  1816.   int n_read_so_far = 0;
  1817.   int n_written_so_far = 0;
  1818.   int in_chars;
  1819.   int n;
  1820.   int mode = 0755;
  1821.   unsigned char in[3], out[4], line[100];
  1822. #ifdef UNIX
  1823.   struct stat stb;
  1824.   
  1825.   if(stat(infilename,&stb) != -1)
  1826.        mode = stb.st_mode & 0777;    /* get real file protection mode */
  1827. #endif
  1828.  
  1829.   usprintf(s, "begin %03o %s\n", mode, infilename);
  1830.  
  1831.   /* do the encode */
  1832.   for(;;) {
  1833.     in_chars = fread(in, 1, 3, infile);
  1834.     out[0] = in[0] >> 2;
  1835.     out[1] = in[0] << 6;
  1836.     out[1] = out[1] >> 2;
  1837.     out[1] = out[1] | (in[1] >> 4);
  1838.     out[2] = in[1] << 4;
  1839.     out[2] = out[2] >> 2;
  1840.     out[2] = out[2] | (in[2] >> 6);
  1841.     out[3] = in[2] << 2;
  1842.     out[3] = out[3] >> 2;
  1843.     for (n = 0; n < 4; n++)
  1844.       out[n] += ' ';
  1845.     n_read_so_far += in_chars;
  1846.     for (n = 0; n < 4; n++)
  1847.       line[n_written_so_far++] = out[n];
  1848.     if (((in_chars != 3) || (n_written_so_far == 60)) && n_read_so_far > 0) {
  1849.       line[(n_read_so_far + 2) / 3 * 4] = '\0';
  1850.       
  1851.       usprintf(s,"%c%s\n",n_read_so_far + ' ', line);
  1852.       cnt += n_read_so_far;
  1853.       n_read_so_far = 0;
  1854.       n_written_so_far = 0;
  1855.     }
  1856.     if (in_chars == 0)
  1857.       break;
  1858.   }
  1859.   if (usprintf(s," \nend\nsize %lu\n", cnt) == EOF)
  1860.     return 1;
  1861.   return 0;
  1862. }
  1863.  
  1864. /* Added by paul@wolf.demon.co.uk - Check to see if 'name' contains
  1865.  * a bang path, a mail redirector, or a host name != Hostname
  1866.  *  Returns 1 if so, 0 if the name is OK
  1867.  */
  1868.  
  1869. static int
  1870. check_mail_privilege (name)
  1871. char *name;
  1872. {
  1873.     char    *host;
  1874.  
  1875.     if ((strchr(name,'!') != NULLCHAR ) || ( strchr(name,'%') != NULLCHAR))
  1876.         return 1;
  1877.     if ((host = strrchr(name,'@')) == NULLCHAR)
  1878.         return 0;
  1879.     host++;
  1880.     if (stricmp(host,Hostname) != 0)
  1881.         return 1;
  1882.  
  1883.     return 0;
  1884. }
  1885.  
  1886. /* string_tidy - reduce runs of whitespace to 1 space */
  1887. static void string_tidy(char *str)   
  1888.     {
  1889.     char *ip=str;
  1890.     char *op=str;
  1891.     int c;
  1892.  
  1893.     while ((c = *ip++) != 0)
  1894.         {
  1895.         if (c != ' ' && c != '\t' )
  1896.             {
  1897.             *op++ = c;
  1898.             continue;
  1899.             }
  1900.             
  1901.         *op++ = ' ';
  1902.         while (*ip != 0 && (*ip == ' ' || *ip =='\t'))
  1903.             ip++;
  1904.         }
  1905.         
  1906.     *op = '\0';  
  1907.     }
  1908.