14549Seric # include "sendmail.h" 24549Seric 3*5003Seric static char SccsId[] = "@(#)srvrsmtp.c 3.10 11/21/81"; 44556Seric 54549Seric /* 64549Seric ** SMTP -- run the SMTP protocol. 74549Seric ** 84549Seric ** Parameters: 94549Seric ** none. 104549Seric ** 114549Seric ** Returns: 124549Seric ** never. 134549Seric ** 144549Seric ** Side Effects: 154549Seric ** Reads commands from the input channel and processes 164549Seric ** them. 174549Seric */ 184549Seric 194549Seric struct cmd 204549Seric { 214549Seric char *cmdname; /* command name */ 224549Seric int cmdcode; /* internal code, see below */ 234549Seric }; 244549Seric 254549Seric /* values for cmdcode */ 264549Seric # define CMDERROR 0 /* bad command */ 274549Seric # define CMDMAIL 1 /* mail -- designate sender */ 284976Seric # define CMDRCPT 2 /* rcpt -- designate recipient */ 294549Seric # define CMDDATA 3 /* data -- send message text */ 304549Seric # define CMDRSET 5 /* rset -- reset state */ 314549Seric # define CMDVRFY 6 /* vrfy -- verify address */ 324549Seric # define CMDHELP 7 /* help -- give usage info */ 334549Seric # define CMDNOOP 8 /* noop -- do nothing */ 344549Seric # define CMDQUIT 9 /* quit -- close connection and die */ 354577Seric # define CMDMRSQ 10 /* mrsq -- for old mtp compat only */ 364976Seric # define CMDHELO 11 /* helo -- be polite */ 37*5003Seric # define CMDDBGSHOWQ 12 /* showq -- show send queue (DEBUG) */ 384549Seric 394549Seric static struct cmd CmdTab[] = 404549Seric { 414549Seric "mail", CMDMAIL, 424976Seric "rcpt", CMDRCPT, 434976Seric "mrcp", CMDRCPT, /* for old MTP compatability */ 444549Seric "data", CMDDATA, 454549Seric "rset", CMDRSET, 464549Seric "vrfy", CMDVRFY, 474549Seric "help", CMDHELP, 484549Seric "noop", CMDNOOP, 494549Seric "quit", CMDQUIT, 504577Seric "mrsq", CMDMRSQ, 514976Seric "helo", CMDHELO, 52*5003Seric # ifdef DEBUG 53*5003Seric "showq", CMDDBGSHOWQ, 54*5003Seric # endif DEBUG 554549Seric NULL, CMDERROR, 564549Seric }; 574549Seric 584549Seric smtp() 594549Seric { 604549Seric char inp[MAXLINE]; 614549Seric register char *p; 624549Seric struct cmd *c; 634549Seric char *cmd; 644549Seric extern char *skipword(); 654549Seric extern bool sameword(); 664549Seric bool hasmail; /* mail command received */ 674713Seric int rcps; /* number of recipients */ 68*5003Seric auto ADDRESS *vrfyqueue; 69*5003Seric ADDRESS *prev; 704549Seric 71*5003Seric hasmail = FALSE; 724713Seric rcps = 0; 734549Seric message("220", "%s Sendmail at your service", HostName); 744549Seric for (;;) 754549Seric { 764549Seric To = NULL; 774577Seric Errors = 0; 784549Seric if (fgets(inp, sizeof inp, InChannel) == NULL) 794549Seric { 804549Seric /* end of file, just die */ 814558Seric message("421", "%s Lost input channel", HostName); 824549Seric finis(); 834549Seric } 844549Seric 854549Seric /* clean up end of line */ 864558Seric fixcrlf(inp, TRUE); 874549Seric 884713Seric /* echo command to transcript */ 894713Seric fprintf(Xscript, "*** %s\n", inp); 904713Seric 914549Seric /* break off command */ 924549Seric for (p = inp; isspace(*p); p++) 934549Seric continue; 944549Seric cmd = p; 954549Seric while (*++p != '\0' && !isspace(*p)) 964549Seric continue; 974549Seric if (*p != '\0') 984549Seric *p++ = '\0'; 994549Seric 1004549Seric /* decode command */ 1014549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1024549Seric { 1034549Seric if (sameword(c->cmdname, cmd)) 1044549Seric break; 1054549Seric } 1064549Seric 1074549Seric /* process command */ 1084549Seric switch (c->cmdcode) 1094549Seric { 1104976Seric case CMDHELO: /* hello -- introduce yourself */ 1114997Seric message("250", "%s Hello %s, pleased to meet you", 1124997Seric HostName, p); 1134976Seric break; 1144976Seric 1154549Seric case CMDMAIL: /* mail -- designate sender */ 1164558Seric if (hasmail) 1174558Seric { 1184558Seric message("503", "Sender already specified"); 1194558Seric break; 1204558Seric } 1214549Seric p = skipword(p, "from"); 1224549Seric if (p == NULL) 1234549Seric break; 1244549Seric if (index(p, ',') != NULL) 1254549Seric { 1264549Seric message("501", "Source routing not implemented"); 1274549Seric Errors++; 1284549Seric break; 1294549Seric } 1304549Seric setsender(p); 1314577Seric if (Errors == 0) 1324549Seric { 1334549Seric message("250", "Sender ok"); 1344549Seric hasmail = TRUE; 1354549Seric } 1364549Seric break; 1374549Seric 1384976Seric case CMDRCPT: /* rcpt -- designate recipient */ 1394549Seric p = skipword(p, "to"); 1404549Seric if (p == NULL) 1414549Seric break; 1424549Seric if (index(p, ',') != NULL) 1434549Seric { 1444549Seric message("501", "Source routing not implemented"); 1454549Seric Errors++; 1464549Seric break; 1474549Seric } 148*5003Seric sendto(p, 1, (ADDRESS *) NULL, &SendQueue); 1494577Seric if (Errors == 0) 1504549Seric { 1514549Seric message("250", "Recipient ok"); 1524713Seric rcps++; 1534549Seric } 1544549Seric break; 1554549Seric 1564549Seric case CMDDATA: /* data -- text of mail */ 1574976Seric if (!hasmail) 1584549Seric { 1594976Seric message("503", "Need MAIL command"); 1604976Seric break; 1614549Seric } 1624713Seric else if (rcps <= 0) 1634549Seric { 1644976Seric message("503", "Need RCPT (recipient)"); 1654976Seric break; 1664549Seric } 1674976Seric 1684976Seric /* collect the text of the message */ 1694976Seric collect(TRUE); 1704976Seric if (Errors != 0) 1714976Seric break; 1724976Seric 1734976Seric /* if sending to multiple people, mail back errors */ 1744976Seric if (rcps != 1) 1754976Seric HoldErrs = MailBack = TRUE; 1764976Seric 1774976Seric /* send to all recipients */ 1784976Seric sendall(FALSE); 1794976Seric 1804976Seric /* reset strange modes */ 1814976Seric HoldErrs = FALSE; 1824976Seric To = NULL; 1834976Seric 1844976Seric /* issue success if appropriate */ 1854976Seric if (Errors == 0 || rcps != 1) 1864976Seric message("250", "Sent"); 1874549Seric break; 1884549Seric 1894549Seric case CMDRSET: /* rset -- reset state */ 1904549Seric message("250", "Reset state"); 1914549Seric finis(); 1924549Seric 1934549Seric case CMDVRFY: /* vrfy -- verify address */ 194*5003Seric vrfyqueue = NULL; 195*5003Seric sendto(p, 1, (ADDRESS *) NULL, &vrfyqueue); 196*5003Seric while (vrfyqueue != NULL) 197*5003Seric { 198*5003Seric register ADDRESS *a = vrfyqueue->q_next; 199*5003Seric char *code; 200*5003Seric 201*5003Seric while (a != NULL && bitset(QDONTSEND, a->q_flags)) 202*5003Seric a = a->q_next; 203*5003Seric 204*5003Seric if (!bitset(QDONTSEND, vrfyqueue->q_flags)) 205*5003Seric { 206*5003Seric if (a != NULL) 207*5003Seric code = "250-"; 208*5003Seric else 209*5003Seric code = "250"; 210*5003Seric if (vrfyqueue->q_fullname == NULL) 211*5003Seric message(code, "<%s>", vrfyqueue->q_paddr); 212*5003Seric else 213*5003Seric message(code, "%s <%s>", 214*5003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 215*5003Seric } 216*5003Seric else if (a == NULL) 217*5003Seric message("554", "Self destructive alias loop"); 218*5003Seric vrfyqueue = a; 219*5003Seric } 2204549Seric break; 2214549Seric 2224549Seric case CMDHELP: /* help -- give user info */ 2234577Seric if (*p == '\0') 2244577Seric p = "SMTP"; 2254577Seric help(p); 2264549Seric break; 2274549Seric 2284549Seric case CMDNOOP: /* noop -- do nothing */ 2294549Seric message("200", "OK"); 2304549Seric break; 2314549Seric 2324549Seric case CMDQUIT: /* quit -- leave mail */ 2334549Seric message("221", "%s closing connection", HostName); 2344549Seric finis(); 2354549Seric 2364577Seric case CMDMRSQ: /* mrsq -- negotiate protocol */ 2374577Seric if (*p == 'R' || *p == 'T') 2384577Seric { 2394577Seric /* recipients first or text first */ 2404577Seric message("200", "%c ok, please continue", *p); 2414577Seric } 2424577Seric else if (*p == '?') 2434577Seric { 2444577Seric /* what do I prefer? anything, anytime */ 2454577Seric message("215", "R Recipients first is my choice"); 2464577Seric } 2474577Seric else if (*p == '\0') 2484577Seric { 2494577Seric /* no meaningful scheme */ 2504577Seric message("200", "okey dokie boobie"); 2514577Seric } 2524577Seric else 2534577Seric { 2544577Seric /* bad argument */ 2554577Seric message("504", "Scheme unknown"); 2564577Seric } 2574577Seric break; 2584577Seric 259*5003Seric # ifdef DEBUG 260*5003Seric case CMDDBGSHOWQ: /* show queues */ 261*5003Seric printf("SendQueue="); 262*5003Seric printaddr(SendQueue, TRUE); 263*5003Seric break; 264*5003Seric # endif DEBUG 265*5003Seric 2664549Seric case CMDERROR: /* unknown command */ 2674549Seric message("500", "Command unrecognized"); 2684549Seric break; 2694549Seric 2704549Seric default: 2714549Seric syserr("smtp: unknown code %d", c->cmdcode); 2724549Seric break; 2734549Seric } 2744549Seric } 2754549Seric } 2764549Seric /* 2774549Seric ** SKIPWORD -- skip a fixed word. 2784549Seric ** 2794549Seric ** Parameters: 2804549Seric ** p -- place to start looking. 2814549Seric ** w -- word to skip. 2824549Seric ** 2834549Seric ** Returns: 2844549Seric ** p following w. 2854549Seric ** NULL on error. 2864549Seric ** 2874549Seric ** Side Effects: 2884549Seric ** clobbers the p data area. 2894549Seric */ 2904549Seric 2914549Seric static char * 2924549Seric skipword(p, w) 2934549Seric register char *p; 2944549Seric char *w; 2954549Seric { 2964549Seric register char *q; 2974549Seric extern bool sameword(); 2984549Seric 2994549Seric /* find beginning of word */ 3004549Seric while (isspace(*p)) 3014549Seric p++; 3024549Seric q = p; 3034549Seric 3044549Seric /* find end of word */ 3054549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 3064549Seric p++; 3074549Seric while (isspace(*p)) 3084549Seric *p++ = '\0'; 3094549Seric if (*p != ':') 3104549Seric { 3114549Seric syntax: 3124549Seric message("501", "Syntax error"); 3134549Seric Errors++; 3144549Seric return (NULL); 3154549Seric } 3164549Seric *p++ = '\0'; 3174549Seric while (isspace(*p)) 3184549Seric p++; 3194549Seric 3204549Seric /* see if the input word matches desired word */ 3214549Seric if (!sameword(q, w)) 3224549Seric goto syntax; 3234549Seric 3244549Seric return (p); 3254549Seric } 3264577Seric /* 3274577Seric ** HELP -- implement the HELP command. 3284577Seric ** 3294577Seric ** Parameters: 3304577Seric ** topic -- the topic we want help for. 3314577Seric ** 3324577Seric ** Returns: 3334577Seric ** none. 3344577Seric ** 3354577Seric ** Side Effects: 3364577Seric ** outputs the help file to message output. 3374577Seric */ 3384577Seric 3394577Seric help(topic) 3404577Seric char *topic; 3414577Seric { 3424577Seric register FILE *hf; 3434577Seric int len; 3444577Seric char buf[MAXLINE]; 3454577Seric bool noinfo; 3464582Seric extern char *HelpFile; 3474577Seric 3484582Seric hf = fopen(HelpFile, "r"); 3494577Seric if (hf == NULL) 3504577Seric { 3514577Seric /* no help */ 3524577Seric message("502", "HELP not implemented"); 3534577Seric return; 3544577Seric } 3554577Seric 3564577Seric len = strlen(topic); 3574577Seric makelower(topic); 3584577Seric noinfo = TRUE; 3594577Seric 3604577Seric while (fgets(buf, sizeof buf, hf) != NULL) 3614577Seric { 3624577Seric if (strncmp(buf, topic, len) == 0) 3634577Seric { 3644577Seric register char *p; 3654577Seric 3664577Seric p = index(buf, '\t'); 3674577Seric if (p == NULL) 3684577Seric p = buf; 3694577Seric else 3704577Seric p++; 3714577Seric fixcrlf(p, TRUE); 3724577Seric message("214-", p); 3734577Seric noinfo = FALSE; 3744577Seric } 3754577Seric } 3764577Seric 3774577Seric if (noinfo) 3784577Seric message("504", "HELP topic unknown"); 3794577Seric else 3804577Seric message("214", "End of HELP info"); 3814628Seric (void) fclose(hf); 3824577Seric } 383