14549Seric # include "sendmail.h" 24549Seric 35181Seric # ifndef SMTP 4*9283Seric SCCSID(@(#)srvrsmtp.c 3.35 11/17/82 (no SMTP)); 55181Seric # else SMTP 64556Seric 7*9283Seric SCCSID(@(#)srvrsmtp.c 3.35 11/17/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 */ 418544Seric # define CMDDBGSHOWQ 12 /* showq -- show send queue (DEBUG) */ 428544Seric # define CMDDBGDEBUG 13 /* debug -- set debug mode */ 438544Seric # define CMDVERB 14 /* verb -- go into verbose mode */ 448544Seric # define CMDDBGKILL 15 /* kill -- kill sendmail */ 458544Seric # define CMDDBGWIZ 16 /* wiz -- become a wizard */ 464549Seric 474549Seric static struct cmd CmdTab[] = 484549Seric { 494549Seric "mail", CMDMAIL, 504976Seric "rcpt", CMDRCPT, 514976Seric "mrcp", CMDRCPT, /* for old MTP compatability */ 524549Seric "data", CMDDATA, 534549Seric "rset", CMDRSET, 544549Seric "vrfy", CMDVRFY, 557762Seric "expn", CMDVRFY, 564549Seric "help", CMDHELP, 574549Seric "noop", CMDNOOP, 584549Seric "quit", CMDQUIT, 594577Seric "mrsq", CMDMRSQ, 604976Seric "helo", CMDHELO, 618544Seric "verb", CMDVERB, 625003Seric # ifdef DEBUG 638544Seric "showq", CMDDBGSHOWQ, 648544Seric "debug", CMDDBGDEBUG, 658544Seric "kill", CMDDBGKILL, 668544Seric "wiz", CMDDBGWIZ, 675003Seric # endif DEBUG 684549Seric NULL, CMDERROR, 694549Seric }; 704549Seric 718544Seric # ifdef DEBUG 728544Seric bool IsWiz = FALSE; /* set if we are a wizard */ 738544Seric char *WizWord = NULL; /* the wizard word to compare against */ 748544Seric # endif DEBUG 758544Seric 764549Seric smtp() 774549Seric { 784549Seric register char *p; 798544Seric register struct cmd *c; 804549Seric char *cmd; 814549Seric extern char *skipword(); 824549Seric extern bool sameword(); 834549Seric bool hasmail; /* mail command received */ 844713Seric int rcps; /* number of recipients */ 855003Seric auto ADDRESS *vrfyqueue; 868544Seric char inp[MAXLINE]; 877124Seric extern char Version[]; 887356Seric extern tick(); 898544Seric extern bool iswiz(); 904549Seric 915003Seric hasmail = FALSE; 924713Seric rcps = 0; 937363Seric if (OutChannel != stdout) 947363Seric { 957363Seric /* arrange for debugging output to go to remote host */ 967363Seric (void) close(1); 977363Seric (void) dup(fileno(OutChannel)); 987363Seric } 997851Seric message("220", "%s Sendmail %s ready at %s", HostName, 1007797Seric Version, arpadate(NULL)); 1017762Seric (void) setjmp(TopFrame); 1027762Seric QuickAbort = FALSE; 1034549Seric for (;;) 1044549Seric { 1057356Seric /* setup for the read */ 1066907Seric CurEnv->e_to = NULL; 1074577Seric Errors = 0; 1087275Seric (void) fflush(stdout); 1097356Seric 1107356Seric /* read the input line */ 1117685Seric p = sfgets(inp, sizeof inp, InChannel); 1127356Seric 1137685Seric /* handle errors */ 1147356Seric if (p == NULL) 1157356Seric { 1164549Seric /* end of file, just die */ 1174558Seric message("421", "%s Lost input channel", HostName); 1184549Seric finis(); 1194549Seric } 1204549Seric 1214549Seric /* clean up end of line */ 1224558Seric fixcrlf(inp, TRUE); 1234549Seric 1244713Seric /* echo command to transcript */ 1257557Seric fprintf(Xscript, "<<< %s\n", inp); 1264713Seric 1274549Seric /* break off command */ 1284549Seric for (p = inp; isspace(*p); p++) 1294549Seric continue; 1304549Seric cmd = p; 1314549Seric while (*++p != '\0' && !isspace(*p)) 1324549Seric continue; 1334549Seric if (*p != '\0') 1344549Seric *p++ = '\0'; 1354549Seric 1364549Seric /* decode command */ 1374549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1384549Seric { 1394549Seric if (sameword(c->cmdname, cmd)) 1404549Seric break; 1414549Seric } 1424549Seric 1434549Seric /* process command */ 1444549Seric switch (c->cmdcode) 1454549Seric { 1464976Seric case CMDHELO: /* hello -- introduce yourself */ 1475187Seric define('s', newstr(p)); 1484997Seric message("250", "%s Hello %s, pleased to meet you", 1494997Seric HostName, p); 1504976Seric break; 1514976Seric 1524549Seric case CMDMAIL: /* mail -- designate sender */ 1534558Seric if (hasmail) 1544558Seric { 1554558Seric message("503", "Sender already specified"); 1564558Seric break; 1574558Seric } 1584549Seric p = skipword(p, "from"); 1594549Seric if (p == NULL) 1604549Seric break; 1614549Seric if (index(p, ',') != NULL) 1624549Seric { 1634549Seric message("501", "Source routing not implemented"); 1644549Seric Errors++; 1654549Seric break; 1664549Seric } 1674549Seric setsender(p); 1684577Seric if (Errors == 0) 1694549Seric { 1704549Seric message("250", "Sender ok"); 1714549Seric hasmail = TRUE; 1724549Seric } 1734549Seric break; 1744549Seric 1754976Seric case CMDRCPT: /* rcpt -- designate recipient */ 1764549Seric p = skipword(p, "to"); 1774549Seric if (p == NULL) 1784549Seric break; 1798081Seric sendto(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 1804577Seric if (Errors == 0) 1814549Seric { 1826057Seric message("250", "%s... Recipient ok", p); 1834713Seric rcps++; 1844549Seric } 1854549Seric break; 1864549Seric 1874549Seric case CMDDATA: /* data -- text of mail */ 1884976Seric if (!hasmail) 1894549Seric { 1904976Seric message("503", "Need MAIL command"); 1914976Seric break; 1924549Seric } 1934713Seric else if (rcps <= 0) 1944549Seric { 1954976Seric message("503", "Need RCPT (recipient)"); 1964976Seric break; 1974549Seric } 1984976Seric 1994976Seric /* collect the text of the message */ 2004976Seric collect(TRUE); 2014976Seric if (Errors != 0) 2024976Seric break; 2034976Seric 2048238Seric /* 2058238Seric ** Arrange to send to everyone. 2068238Seric ** If sending to multiple people, mail back 2078238Seric ** errors rather than reporting directly. 2088238Seric ** In any case, don't mail back errors for 2098238Seric ** anything that has happened up to 2108238Seric ** now (the other end will do this). 2118238Seric ** Then send to everyone. 2128238Seric ** Finally give a reply code. If an error has 2138238Seric ** already been given, don't mail a 2148238Seric ** message back. 2158238Seric ** We goose error returns by clearing FatalErrors. 2168238Seric */ 2178238Seric 2184976Seric if (rcps != 1) 2194976Seric HoldErrs = MailBack = TRUE; 2208238Seric FatalErrors = FALSE; 2214976Seric 2224976Seric /* send to all recipients */ 223*9283Seric sendall(CurEnv, SendMode); 2246907Seric CurEnv->e_to = NULL; 2254976Seric 2268238Seric /* issue success if appropriate and reset */ 2278238Seric if (Errors == 0 || HoldErrs) 2288238Seric { 2298238Seric HoldErrs = FALSE; 230*9283Seric message("250", "Ok"); 2318238Seric } 2328238Seric else 2338238Seric FatalErrors = FALSE; 2344549Seric break; 2354549Seric 2364549Seric case CMDRSET: /* rset -- reset state */ 2374549Seric message("250", "Reset state"); 2384549Seric finis(); 2394549Seric 2404549Seric case CMDVRFY: /* vrfy -- verify address */ 2415003Seric vrfyqueue = NULL; 2427762Seric QuickAbort = TRUE; 2438081Seric sendto(p, (ADDRESS *) NULL, &vrfyqueue); 2447762Seric if (Errors != 0) 2457762Seric break; 2465003Seric while (vrfyqueue != NULL) 2475003Seric { 2485003Seric register ADDRESS *a = vrfyqueue->q_next; 2495003Seric char *code; 2505003Seric 2517685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 2525003Seric a = a->q_next; 2535003Seric 2547685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 2555003Seric { 2565003Seric if (a != NULL) 2575003Seric code = "250-"; 2585003Seric else 2595003Seric code = "250"; 2605003Seric if (vrfyqueue->q_fullname == NULL) 2615003Seric message(code, "<%s>", vrfyqueue->q_paddr); 2625003Seric else 2635003Seric message(code, "%s <%s>", 2645003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 2655003Seric } 2665003Seric else if (a == NULL) 2675003Seric message("554", "Self destructive alias loop"); 2685003Seric vrfyqueue = a; 2695003Seric } 2704549Seric break; 2714549Seric 2724549Seric case CMDHELP: /* help -- give user info */ 2734577Seric if (*p == '\0') 2744577Seric p = "SMTP"; 2754577Seric help(p); 2764549Seric break; 2774549Seric 2784549Seric case CMDNOOP: /* noop -- do nothing */ 2794549Seric message("200", "OK"); 2804549Seric break; 2814549Seric 2824549Seric case CMDQUIT: /* quit -- leave mail */ 2834549Seric message("221", "%s closing connection", HostName); 2844549Seric finis(); 2854549Seric 2864577Seric case CMDMRSQ: /* mrsq -- negotiate protocol */ 2874577Seric if (*p == 'R' || *p == 'T') 2884577Seric { 2894577Seric /* recipients first or text first */ 2904577Seric message("200", "%c ok, please continue", *p); 2914577Seric } 2924577Seric else if (*p == '?') 2934577Seric { 2944577Seric /* what do I prefer? anything, anytime */ 2954577Seric message("215", "R Recipients first is my choice"); 2964577Seric } 2974577Seric else if (*p == '\0') 2984577Seric { 2994577Seric /* no meaningful scheme */ 3004577Seric message("200", "okey dokie boobie"); 3014577Seric } 3024577Seric else 3034577Seric { 3044577Seric /* bad argument */ 3054577Seric message("504", "Scheme unknown"); 3064577Seric } 3074577Seric break; 3084577Seric 3098544Seric case CMDVERB: /* set verbose mode */ 3108544Seric Verbose = TRUE; 3118544Seric message("200", "Verbose mode"); 3128544Seric break; 3138544Seric 3145003Seric # ifdef DEBUG 3155003Seric case CMDDBGSHOWQ: /* show queues */ 3166907Seric printf("Send Queue="); 3176907Seric printaddr(CurEnv->e_sendqueue, TRUE); 3185003Seric break; 3197275Seric 3207275Seric case CMDDBGDEBUG: /* set debug mode */ 3217676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 3227676Seric tTflag(p); 3237676Seric message("200", "Debug set"); 3247275Seric break; 3257275Seric 3267282Seric case CMDDBGKILL: /* kill the parent */ 3278544Seric if (!iswiz()) 3288544Seric break; 3297282Seric if (kill(MotherPid, SIGTERM) >= 0) 3307282Seric message("200", "Mother is dead"); 3317282Seric else 3327282Seric message("500", "Can't kill Mom"); 3337282Seric break; 3348544Seric 3358544Seric case CMDDBGWIZ: /* become a wizard */ 3368544Seric if (WizWord != NULL) 3378544Seric { 3388544Seric char seed[3]; 3398544Seric extern char *crypt(); 3408544Seric 3418544Seric strncpy(seed, WizWord, 2); 3428544Seric if (strcmp(WizWord, crypt(p, seed)) != 0) 3438544Seric { 3448544Seric message("500", "You are no wizard!"); 3458544Seric break; 3468544Seric } 3478544Seric } 3488544Seric IsWiz = TRUE; 3498544Seric message("200", "Please pass, oh mighty wizard"); 3508544Seric break; 3515003Seric # endif DEBUG 3525003Seric 3534549Seric case CMDERROR: /* unknown command */ 3544549Seric message("500", "Command unrecognized"); 3554549Seric break; 3564549Seric 3574549Seric default: 3584549Seric syserr("smtp: unknown code %d", c->cmdcode); 3594549Seric break; 3604549Seric } 3614549Seric } 3624549Seric } 3634549Seric /* 3644549Seric ** SKIPWORD -- skip a fixed word. 3654549Seric ** 3664549Seric ** Parameters: 3674549Seric ** p -- place to start looking. 3684549Seric ** w -- word to skip. 3694549Seric ** 3704549Seric ** Returns: 3714549Seric ** p following w. 3724549Seric ** NULL on error. 3734549Seric ** 3744549Seric ** Side Effects: 3754549Seric ** clobbers the p data area. 3764549Seric */ 3774549Seric 3784549Seric static char * 3794549Seric skipword(p, w) 3804549Seric register char *p; 3814549Seric char *w; 3824549Seric { 3834549Seric register char *q; 3844549Seric extern bool sameword(); 3854549Seric 3864549Seric /* find beginning of word */ 3874549Seric while (isspace(*p)) 3884549Seric p++; 3894549Seric q = p; 3904549Seric 3914549Seric /* find end of word */ 3924549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 3934549Seric p++; 3944549Seric while (isspace(*p)) 3954549Seric *p++ = '\0'; 3964549Seric if (*p != ':') 3974549Seric { 3984549Seric syntax: 3994549Seric message("501", "Syntax error"); 4004549Seric Errors++; 4014549Seric return (NULL); 4024549Seric } 4034549Seric *p++ = '\0'; 4044549Seric while (isspace(*p)) 4054549Seric p++; 4064549Seric 4074549Seric /* see if the input word matches desired word */ 4084549Seric if (!sameword(q, w)) 4094549Seric goto syntax; 4104549Seric 4114549Seric return (p); 4124549Seric } 4134577Seric /* 4144577Seric ** HELP -- implement the HELP command. 4154577Seric ** 4164577Seric ** Parameters: 4174577Seric ** topic -- the topic we want help for. 4184577Seric ** 4194577Seric ** Returns: 4204577Seric ** none. 4214577Seric ** 4224577Seric ** Side Effects: 4234577Seric ** outputs the help file to message output. 4244577Seric */ 4254577Seric 4264577Seric help(topic) 4274577Seric char *topic; 4284577Seric { 4294577Seric register FILE *hf; 4304577Seric int len; 4314577Seric char buf[MAXLINE]; 4324577Seric bool noinfo; 4334577Seric 4348269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 4354577Seric { 4364577Seric /* no help */ 4374577Seric message("502", "HELP not implemented"); 4384577Seric return; 4394577Seric } 4404577Seric 4414577Seric len = strlen(topic); 4424577Seric makelower(topic); 4434577Seric noinfo = TRUE; 4444577Seric 4454577Seric while (fgets(buf, sizeof buf, hf) != NULL) 4464577Seric { 4474577Seric if (strncmp(buf, topic, len) == 0) 4484577Seric { 4494577Seric register char *p; 4504577Seric 4514577Seric p = index(buf, '\t'); 4524577Seric if (p == NULL) 4534577Seric p = buf; 4544577Seric else 4554577Seric p++; 4564577Seric fixcrlf(p, TRUE); 4574577Seric message("214-", p); 4584577Seric noinfo = FALSE; 4594577Seric } 4604577Seric } 4614577Seric 4624577Seric if (noinfo) 4634577Seric message("504", "HELP topic unknown"); 4644577Seric else 4654577Seric message("214", "End of HELP info"); 4664628Seric (void) fclose(hf); 4674577Seric } 4688544Seric /* 4698544Seric ** ISWIZ -- tell us if we are a wizard 4708544Seric ** 4718544Seric ** If not, print a nasty message. 4728544Seric ** 4738544Seric ** Parameters: 4748544Seric ** none. 4758544Seric ** 4768544Seric ** Returns: 4778544Seric ** TRUE if we are a wizard. 4788544Seric ** FALSE if we are not a wizard. 4798544Seric ** 4808544Seric ** Side Effects: 4818544Seric ** Prints a 500 exit stat if we are not a wizard. 4828544Seric */ 4835181Seric 4848544Seric bool 4858544Seric iswiz() 4868544Seric { 4878544Seric if (!IsWiz) 4888544Seric message("500", "Mere mortals musn't mutter that mantra"); 4898544Seric return (IsWiz); 4908544Seric } 4918544Seric 4925181Seric # endif SMTP 493