14549Seric # include "sendmail.h" 24549Seric 35181Seric # ifndef SMTP 4*8238Seric SCCSID(@(#)srvrsmtp.c 3.32 09/21/82 (no SMTP)); 55181Seric # else SMTP 64556Seric 7*8238Seric SCCSID(@(#)srvrsmtp.c 3.32 09/21/82); 85181Seric 94549Seric /* 104549Seric ** SMTP -- run the SMTP protocol. 114549Seric ** 124549Seric ** Parameters: 134549Seric ** none. 144549Seric ** 154549Seric ** Returns: 164549Seric ** never. 174549Seric ** 184549Seric ** Side Effects: 194549Seric ** Reads commands from the input channel and processes 204549Seric ** them. 214549Seric */ 224549Seric 234549Seric struct cmd 244549Seric { 254549Seric char *cmdname; /* command name */ 264549Seric int cmdcode; /* internal code, see below */ 274549Seric }; 284549Seric 294549Seric /* values for cmdcode */ 304549Seric # define CMDERROR 0 /* bad command */ 314549Seric # define CMDMAIL 1 /* mail -- designate sender */ 324976Seric # define CMDRCPT 2 /* rcpt -- designate recipient */ 334549Seric # define CMDDATA 3 /* data -- send message text */ 344549Seric # define CMDRSET 5 /* rset -- reset state */ 354549Seric # define CMDVRFY 6 /* vrfy -- verify address */ 364549Seric # define CMDHELP 7 /* help -- give usage info */ 374549Seric # define CMDNOOP 8 /* noop -- do nothing */ 384549Seric # define CMDQUIT 9 /* quit -- close connection and die */ 394577Seric # define CMDMRSQ 10 /* mrsq -- for old mtp compat only */ 404976Seric # define CMDHELO 11 /* helo -- be polite */ 417275Seric # define CMDDBGSHOWQ 12 /* _showq -- show send queue (DEBUG) */ 427275Seric # define CMDDBGDEBUG 13 /* _debug -- set debug mode */ 437275Seric # define CMDDBGVERBOSE 14 /* _verbose -- go into verbose mode */ 447282Seric # define CMDDBGKILL 15 /* _kill -- kill sendmail */ 454549Seric 464549Seric static struct cmd CmdTab[] = 474549Seric { 484549Seric "mail", CMDMAIL, 494976Seric "rcpt", CMDRCPT, 504976Seric "mrcp", CMDRCPT, /* for old MTP compatability */ 514549Seric "data", CMDDATA, 524549Seric "rset", CMDRSET, 534549Seric "vrfy", CMDVRFY, 547762Seric "expn", CMDVRFY, 554549Seric "help", CMDHELP, 564549Seric "noop", CMDNOOP, 574549Seric "quit", CMDQUIT, 584577Seric "mrsq", CMDMRSQ, 594976Seric "helo", CMDHELO, 605003Seric # ifdef DEBUG 617275Seric "_showq", CMDDBGSHOWQ, 627275Seric "_debug", CMDDBGDEBUG, 637275Seric "_verbose", CMDDBGVERBOSE, 647282Seric "_kill", CMDDBGKILL, 655003Seric # endif DEBUG 664549Seric NULL, CMDERROR, 674549Seric }; 684549Seric 694549Seric smtp() 704549Seric { 714549Seric char inp[MAXLINE]; 724549Seric register char *p; 734549Seric struct cmd *c; 744549Seric char *cmd; 754549Seric extern char *skipword(); 764549Seric extern bool sameword(); 774549Seric bool hasmail; /* mail command received */ 784713Seric int rcps; /* number of recipients */ 795003Seric auto ADDRESS *vrfyqueue; 807124Seric extern char Version[]; 817356Seric extern tick(); 824549Seric 835003Seric hasmail = FALSE; 844713Seric rcps = 0; 857363Seric if (OutChannel != stdout) 867363Seric { 877363Seric /* arrange for debugging output to go to remote host */ 887363Seric (void) close(1); 897363Seric (void) dup(fileno(OutChannel)); 907363Seric } 917851Seric message("220", "%s Sendmail %s ready at %s", HostName, 927797Seric Version, arpadate(NULL)); 937762Seric (void) setjmp(TopFrame); 947762Seric QuickAbort = FALSE; 954549Seric for (;;) 964549Seric { 977356Seric /* setup for the read */ 986907Seric CurEnv->e_to = NULL; 994577Seric Errors = 0; 1007275Seric (void) fflush(stdout); 1017356Seric 1027356Seric /* read the input line */ 1037685Seric p = sfgets(inp, sizeof inp, InChannel); 1047356Seric 1057685Seric /* handle errors */ 1067356Seric if (p == NULL) 1077356Seric { 1084549Seric /* end of file, just die */ 1094558Seric message("421", "%s Lost input channel", HostName); 1104549Seric finis(); 1114549Seric } 1124549Seric 1134549Seric /* clean up end of line */ 1144558Seric fixcrlf(inp, TRUE); 1154549Seric 1164713Seric /* echo command to transcript */ 1177557Seric fprintf(Xscript, "<<< %s\n", inp); 1184713Seric 1194549Seric /* break off command */ 1204549Seric for (p = inp; isspace(*p); p++) 1214549Seric continue; 1224549Seric cmd = p; 1234549Seric while (*++p != '\0' && !isspace(*p)) 1244549Seric continue; 1254549Seric if (*p != '\0') 1264549Seric *p++ = '\0'; 1274549Seric 1284549Seric /* decode command */ 1294549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1304549Seric { 1314549Seric if (sameword(c->cmdname, cmd)) 1324549Seric break; 1334549Seric } 1344549Seric 1354549Seric /* process command */ 1364549Seric switch (c->cmdcode) 1374549Seric { 1384976Seric case CMDHELO: /* hello -- introduce yourself */ 1395187Seric define('s', newstr(p)); 1404997Seric message("250", "%s Hello %s, pleased to meet you", 1414997Seric HostName, p); 1424976Seric break; 1434976Seric 1444549Seric case CMDMAIL: /* mail -- designate sender */ 1454558Seric if (hasmail) 1464558Seric { 1474558Seric message("503", "Sender already specified"); 1484558Seric break; 1494558Seric } 1504549Seric p = skipword(p, "from"); 1514549Seric if (p == NULL) 1524549Seric break; 1534549Seric if (index(p, ',') != NULL) 1544549Seric { 1554549Seric message("501", "Source routing not implemented"); 1564549Seric Errors++; 1574549Seric break; 1584549Seric } 1594549Seric setsender(p); 1604577Seric if (Errors == 0) 1614549Seric { 1624549Seric message("250", "Sender ok"); 1634549Seric hasmail = TRUE; 1644549Seric } 1654549Seric break; 1664549Seric 1674976Seric case CMDRCPT: /* rcpt -- designate recipient */ 1684549Seric p = skipword(p, "to"); 1694549Seric if (p == NULL) 1704549Seric break; 1718081Seric sendto(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 1724577Seric if (Errors == 0) 1734549Seric { 1746057Seric message("250", "%s... Recipient ok", p); 1754713Seric rcps++; 1764549Seric } 1774549Seric break; 1784549Seric 1794549Seric case CMDDATA: /* data -- text of mail */ 1804976Seric if (!hasmail) 1814549Seric { 1824976Seric message("503", "Need MAIL command"); 1834976Seric break; 1844549Seric } 1854713Seric else if (rcps <= 0) 1864549Seric { 1874976Seric message("503", "Need RCPT (recipient)"); 1884976Seric break; 1894549Seric } 1904976Seric 1914976Seric /* collect the text of the message */ 1924976Seric collect(TRUE); 1934976Seric if (Errors != 0) 1944976Seric break; 1954976Seric 196*8238Seric /* 197*8238Seric ** Arrange to send to everyone. 198*8238Seric ** If sending to multiple people, mail back 199*8238Seric ** errors rather than reporting directly. 200*8238Seric ** In any case, don't mail back errors for 201*8238Seric ** anything that has happened up to 202*8238Seric ** now (the other end will do this). 203*8238Seric ** Then send to everyone. 204*8238Seric ** Finally give a reply code. If an error has 205*8238Seric ** already been given, don't mail a 206*8238Seric ** message back. 207*8238Seric ** We goose error returns by clearing FatalErrors. 208*8238Seric */ 209*8238Seric 2104976Seric if (rcps != 1) 2114976Seric HoldErrs = MailBack = TRUE; 212*8238Seric FatalErrors = FALSE; 2134976Seric 2144976Seric /* send to all recipients */ 2157046Seric sendall(CurEnv, FALSE); 2166907Seric CurEnv->e_to = NULL; 2174976Seric 218*8238Seric /* issue success if appropriate and reset */ 219*8238Seric if (Errors == 0 || HoldErrs) 220*8238Seric { 221*8238Seric HoldErrs = FALSE; 2224976Seric message("250", "Sent"); 223*8238Seric } 224*8238Seric else 225*8238Seric FatalErrors = FALSE; 2264549Seric break; 2274549Seric 2284549Seric case CMDRSET: /* rset -- reset state */ 2294549Seric message("250", "Reset state"); 2304549Seric finis(); 2314549Seric 2324549Seric case CMDVRFY: /* vrfy -- verify address */ 2335003Seric vrfyqueue = NULL; 2347762Seric QuickAbort = TRUE; 2358081Seric sendto(p, (ADDRESS *) NULL, &vrfyqueue); 2367762Seric if (Errors != 0) 2377762Seric break; 2385003Seric while (vrfyqueue != NULL) 2395003Seric { 2405003Seric register ADDRESS *a = vrfyqueue->q_next; 2415003Seric char *code; 2425003Seric 2437685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 2445003Seric a = a->q_next; 2455003Seric 2467685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 2475003Seric { 2485003Seric if (a != NULL) 2495003Seric code = "250-"; 2505003Seric else 2515003Seric code = "250"; 2525003Seric if (vrfyqueue->q_fullname == NULL) 2535003Seric message(code, "<%s>", vrfyqueue->q_paddr); 2545003Seric else 2555003Seric message(code, "%s <%s>", 2565003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 2575003Seric } 2585003Seric else if (a == NULL) 2595003Seric message("554", "Self destructive alias loop"); 2605003Seric vrfyqueue = a; 2615003Seric } 2624549Seric break; 2634549Seric 2644549Seric case CMDHELP: /* help -- give user info */ 2654577Seric if (*p == '\0') 2664577Seric p = "SMTP"; 2674577Seric help(p); 2684549Seric break; 2694549Seric 2704549Seric case CMDNOOP: /* noop -- do nothing */ 2714549Seric message("200", "OK"); 2724549Seric break; 2734549Seric 2744549Seric case CMDQUIT: /* quit -- leave mail */ 2754549Seric message("221", "%s closing connection", HostName); 2764549Seric finis(); 2774549Seric 2784577Seric case CMDMRSQ: /* mrsq -- negotiate protocol */ 2794577Seric if (*p == 'R' || *p == 'T') 2804577Seric { 2814577Seric /* recipients first or text first */ 2824577Seric message("200", "%c ok, please continue", *p); 2834577Seric } 2844577Seric else if (*p == '?') 2854577Seric { 2864577Seric /* what do I prefer? anything, anytime */ 2874577Seric message("215", "R Recipients first is my choice"); 2884577Seric } 2894577Seric else if (*p == '\0') 2904577Seric { 2914577Seric /* no meaningful scheme */ 2924577Seric message("200", "okey dokie boobie"); 2934577Seric } 2944577Seric else 2954577Seric { 2964577Seric /* bad argument */ 2974577Seric message("504", "Scheme unknown"); 2984577Seric } 2994577Seric break; 3004577Seric 3015003Seric # ifdef DEBUG 3025003Seric case CMDDBGSHOWQ: /* show queues */ 3036907Seric printf("Send Queue="); 3046907Seric printaddr(CurEnv->e_sendqueue, TRUE); 3055003Seric break; 3067275Seric 3077275Seric case CMDDBGDEBUG: /* set debug mode */ 3087676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 3097676Seric tTflag(p); 3107676Seric message("200", "Debug set"); 3117275Seric break; 3127275Seric 3137275Seric case CMDDBGVERBOSE: /* set verbose mode */ 3147275Seric Verbose = TRUE; 3157275Seric message("200", "Verbose mode"); 3167275Seric break; 3177282Seric 3187282Seric case CMDDBGKILL: /* kill the parent */ 3197282Seric if (kill(MotherPid, SIGTERM) >= 0) 3207282Seric message("200", "Mother is dead"); 3217282Seric else 3227282Seric message("500", "Can't kill Mom"); 3237282Seric break; 3245003Seric # endif DEBUG 3255003Seric 3264549Seric case CMDERROR: /* unknown command */ 3274549Seric message("500", "Command unrecognized"); 3284549Seric break; 3294549Seric 3304549Seric default: 3314549Seric syserr("smtp: unknown code %d", c->cmdcode); 3324549Seric break; 3334549Seric } 3344549Seric } 3354549Seric } 3364549Seric /* 3374549Seric ** SKIPWORD -- skip a fixed word. 3384549Seric ** 3394549Seric ** Parameters: 3404549Seric ** p -- place to start looking. 3414549Seric ** w -- word to skip. 3424549Seric ** 3434549Seric ** Returns: 3444549Seric ** p following w. 3454549Seric ** NULL on error. 3464549Seric ** 3474549Seric ** Side Effects: 3484549Seric ** clobbers the p data area. 3494549Seric */ 3504549Seric 3514549Seric static char * 3524549Seric skipword(p, w) 3534549Seric register char *p; 3544549Seric char *w; 3554549Seric { 3564549Seric register char *q; 3574549Seric extern bool sameword(); 3584549Seric 3594549Seric /* find beginning of word */ 3604549Seric while (isspace(*p)) 3614549Seric p++; 3624549Seric q = p; 3634549Seric 3644549Seric /* find end of word */ 3654549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 3664549Seric p++; 3674549Seric while (isspace(*p)) 3684549Seric *p++ = '\0'; 3694549Seric if (*p != ':') 3704549Seric { 3714549Seric syntax: 3724549Seric message("501", "Syntax error"); 3734549Seric Errors++; 3744549Seric return (NULL); 3754549Seric } 3764549Seric *p++ = '\0'; 3774549Seric while (isspace(*p)) 3784549Seric p++; 3794549Seric 3804549Seric /* see if the input word matches desired word */ 3814549Seric if (!sameword(q, w)) 3824549Seric goto syntax; 3834549Seric 3844549Seric return (p); 3854549Seric } 3864577Seric /* 3874577Seric ** HELP -- implement the HELP command. 3884577Seric ** 3894577Seric ** Parameters: 3904577Seric ** topic -- the topic we want help for. 3914577Seric ** 3924577Seric ** Returns: 3934577Seric ** none. 3944577Seric ** 3954577Seric ** Side Effects: 3964577Seric ** outputs the help file to message output. 3974577Seric */ 3984577Seric 3994577Seric help(topic) 4004577Seric char *topic; 4014577Seric { 4024577Seric register FILE *hf; 4034577Seric int len; 4044577Seric char buf[MAXLINE]; 4054577Seric bool noinfo; 4064582Seric extern char *HelpFile; 4074577Seric 4084582Seric hf = fopen(HelpFile, "r"); 4094577Seric if (hf == NULL) 4104577Seric { 4114577Seric /* no help */ 4124577Seric message("502", "HELP not implemented"); 4134577Seric return; 4144577Seric } 4154577Seric 4164577Seric len = strlen(topic); 4174577Seric makelower(topic); 4184577Seric noinfo = TRUE; 4194577Seric 4204577Seric while (fgets(buf, sizeof buf, hf) != NULL) 4214577Seric { 4224577Seric if (strncmp(buf, topic, len) == 0) 4234577Seric { 4244577Seric register char *p; 4254577Seric 4264577Seric p = index(buf, '\t'); 4274577Seric if (p == NULL) 4284577Seric p = buf; 4294577Seric else 4304577Seric p++; 4314577Seric fixcrlf(p, TRUE); 4324577Seric message("214-", p); 4334577Seric noinfo = FALSE; 4344577Seric } 4354577Seric } 4364577Seric 4374577Seric if (noinfo) 4384577Seric message("504", "HELP topic unknown"); 4394577Seric else 4404577Seric message("214", "End of HELP info"); 4414628Seric (void) fclose(hf); 4424577Seric } 4435181Seric 4445181Seric # endif SMTP 445