14549Seric # include "sendmail.h" 24549Seric 35181Seric # ifndef SMTP 4*9314Seric SCCSID(@(#)srvrsmtp.c 3.36 11/21/82 (no SMTP)); 55181Seric # else SMTP 64556Seric 7*9314Seric SCCSID(@(#)srvrsmtp.c 3.36 11/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 */ 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 */ 46*9314Seric # define CMDONEX 17 /* onex -- sending one transaction only */ 474549Seric 484549Seric static struct cmd CmdTab[] = 494549Seric { 504549Seric "mail", CMDMAIL, 514976Seric "rcpt", CMDRCPT, 524976Seric "mrcp", CMDRCPT, /* for old MTP compatability */ 534549Seric "data", CMDDATA, 544549Seric "rset", CMDRSET, 554549Seric "vrfy", CMDVRFY, 567762Seric "expn", CMDVRFY, 574549Seric "help", CMDHELP, 584549Seric "noop", CMDNOOP, 594549Seric "quit", CMDQUIT, 604577Seric "mrsq", CMDMRSQ, 614976Seric "helo", CMDHELO, 628544Seric "verb", CMDVERB, 63*9314Seric "onex", CMDONEX, 645003Seric # ifdef DEBUG 658544Seric "showq", CMDDBGSHOWQ, 668544Seric "debug", CMDDBGDEBUG, 678544Seric "kill", CMDDBGKILL, 688544Seric "wiz", CMDDBGWIZ, 695003Seric # endif DEBUG 704549Seric NULL, CMDERROR, 714549Seric }; 724549Seric 738544Seric # ifdef DEBUG 748544Seric bool IsWiz = FALSE; /* set if we are a wizard */ 758544Seric char *WizWord = NULL; /* the wizard word to compare against */ 768544Seric # endif DEBUG 778544Seric 784549Seric smtp() 794549Seric { 804549Seric register char *p; 818544Seric register struct cmd *c; 824549Seric char *cmd; 834549Seric extern char *skipword(); 844549Seric extern bool sameword(); 854549Seric bool hasmail; /* mail command received */ 864713Seric int rcps; /* number of recipients */ 875003Seric auto ADDRESS *vrfyqueue; 88*9314Seric bool onexact = FALSE; /* one transaction this connection */ 89*9314Seric bool firsttime = TRUE; /* this is the first transaction */ 908544Seric char inp[MAXLINE]; 917124Seric extern char Version[]; 927356Seric extern tick(); 938544Seric extern bool iswiz(); 944549Seric 955003Seric hasmail = FALSE; 964713Seric rcps = 0; 977363Seric if (OutChannel != stdout) 987363Seric { 997363Seric /* arrange for debugging output to go to remote host */ 1007363Seric (void) close(1); 1017363Seric (void) dup(fileno(OutChannel)); 1027363Seric } 1037851Seric message("220", "%s Sendmail %s ready at %s", HostName, 1047797Seric Version, arpadate(NULL)); 1057762Seric (void) setjmp(TopFrame); 1067762Seric QuickAbort = FALSE; 1074549Seric for (;;) 1084549Seric { 1097356Seric /* setup for the read */ 1106907Seric CurEnv->e_to = NULL; 1114577Seric Errors = 0; 1127275Seric (void) fflush(stdout); 1137356Seric 1147356Seric /* read the input line */ 1157685Seric p = sfgets(inp, sizeof inp, InChannel); 1167356Seric 1177685Seric /* handle errors */ 1187356Seric if (p == NULL) 1197356Seric { 1204549Seric /* end of file, just die */ 1214558Seric message("421", "%s Lost input channel", HostName); 1224549Seric finis(); 1234549Seric } 1244549Seric 1254549Seric /* clean up end of line */ 1264558Seric fixcrlf(inp, TRUE); 1274549Seric 1284713Seric /* echo command to transcript */ 1297557Seric fprintf(Xscript, "<<< %s\n", inp); 1304713Seric 1314549Seric /* break off command */ 1324549Seric for (p = inp; isspace(*p); p++) 1334549Seric continue; 1344549Seric cmd = p; 1354549Seric while (*++p != '\0' && !isspace(*p)) 1364549Seric continue; 1374549Seric if (*p != '\0') 1384549Seric *p++ = '\0'; 1394549Seric 1404549Seric /* decode command */ 1414549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1424549Seric { 1434549Seric if (sameword(c->cmdname, cmd)) 1444549Seric break; 1454549Seric } 1464549Seric 1474549Seric /* process command */ 1484549Seric switch (c->cmdcode) 1494549Seric { 1504976Seric case CMDHELO: /* hello -- introduce yourself */ 1515187Seric define('s', newstr(p)); 1524997Seric message("250", "%s Hello %s, pleased to meet you", 1534997Seric HostName, p); 1544976Seric break; 1554976Seric 1564549Seric case CMDMAIL: /* mail -- designate sender */ 157*9314Seric firsttime = FALSE; 158*9314Seric 159*9314Seric /* check for validity of this command */ 1604558Seric if (hasmail) 1614558Seric { 1624558Seric message("503", "Sender already specified"); 1634558Seric break; 1644558Seric } 1654549Seric p = skipword(p, "from"); 1664549Seric if (p == NULL) 1674549Seric break; 1684549Seric if (index(p, ',') != NULL) 1694549Seric { 1704549Seric message("501", "Source routing not implemented"); 1714549Seric Errors++; 1724549Seric break; 1734549Seric } 1744549Seric setsender(p); 1754577Seric if (Errors == 0) 1764549Seric { 1774549Seric message("250", "Sender ok"); 1784549Seric hasmail = TRUE; 1794549Seric } 1804549Seric break; 1814549Seric 1824976Seric case CMDRCPT: /* rcpt -- designate recipient */ 1834549Seric p = skipword(p, "to"); 1844549Seric if (p == NULL) 1854549Seric break; 1868081Seric sendto(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 1874577Seric if (Errors == 0) 1884549Seric { 1896057Seric message("250", "%s... Recipient ok", p); 1904713Seric rcps++; 1914549Seric } 1924549Seric break; 1934549Seric 1944549Seric case CMDDATA: /* data -- text of mail */ 1954976Seric if (!hasmail) 1964549Seric { 1974976Seric message("503", "Need MAIL command"); 1984976Seric break; 1994549Seric } 2004713Seric else if (rcps <= 0) 2014549Seric { 2024976Seric message("503", "Need RCPT (recipient)"); 2034976Seric break; 2044549Seric } 2054976Seric 2064976Seric /* collect the text of the message */ 2074976Seric collect(TRUE); 2084976Seric if (Errors != 0) 2094976Seric break; 2104976Seric 2118238Seric /* 2128238Seric ** Arrange to send to everyone. 2138238Seric ** If sending to multiple people, mail back 2148238Seric ** errors rather than reporting directly. 2158238Seric ** In any case, don't mail back errors for 2168238Seric ** anything that has happened up to 2178238Seric ** now (the other end will do this). 2188238Seric ** Then send to everyone. 2198238Seric ** Finally give a reply code. If an error has 2208238Seric ** already been given, don't mail a 2218238Seric ** message back. 2228238Seric ** We goose error returns by clearing FatalErrors. 2238238Seric */ 2248238Seric 2254976Seric if (rcps != 1) 2264976Seric HoldErrs = MailBack = TRUE; 2278238Seric FatalErrors = FALSE; 2284976Seric 2294976Seric /* send to all recipients */ 2309283Seric sendall(CurEnv, SendMode); 2316907Seric CurEnv->e_to = NULL; 2324976Seric 2338238Seric /* issue success if appropriate and reset */ 2348238Seric if (Errors == 0 || HoldErrs) 2358238Seric { 2368238Seric HoldErrs = FALSE; 2379283Seric message("250", "Ok"); 2388238Seric } 2398238Seric else 2408238Seric FatalErrors = FALSE; 2414549Seric break; 2424549Seric 2434549Seric case CMDRSET: /* rset -- reset state */ 2444549Seric message("250", "Reset state"); 2454549Seric finis(); 2464549Seric 2474549Seric case CMDVRFY: /* vrfy -- verify address */ 2485003Seric vrfyqueue = NULL; 2497762Seric QuickAbort = TRUE; 2508081Seric sendto(p, (ADDRESS *) NULL, &vrfyqueue); 2517762Seric if (Errors != 0) 2527762Seric break; 2535003Seric while (vrfyqueue != NULL) 2545003Seric { 2555003Seric register ADDRESS *a = vrfyqueue->q_next; 2565003Seric char *code; 2575003Seric 2587685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 2595003Seric a = a->q_next; 2605003Seric 2617685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 2625003Seric { 2635003Seric if (a != NULL) 2645003Seric code = "250-"; 2655003Seric else 2665003Seric code = "250"; 2675003Seric if (vrfyqueue->q_fullname == NULL) 2685003Seric message(code, "<%s>", vrfyqueue->q_paddr); 2695003Seric else 2705003Seric message(code, "%s <%s>", 2715003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 2725003Seric } 2735003Seric else if (a == NULL) 2745003Seric message("554", "Self destructive alias loop"); 2755003Seric vrfyqueue = a; 2765003Seric } 2774549Seric break; 2784549Seric 2794549Seric case CMDHELP: /* help -- give user info */ 2804577Seric if (*p == '\0') 2814577Seric p = "SMTP"; 2824577Seric help(p); 2834549Seric break; 2844549Seric 2854549Seric case CMDNOOP: /* noop -- do nothing */ 2864549Seric message("200", "OK"); 2874549Seric break; 2884549Seric 2894549Seric case CMDQUIT: /* quit -- leave mail */ 2904549Seric message("221", "%s closing connection", HostName); 2914549Seric finis(); 2924549Seric 2934577Seric case CMDMRSQ: /* mrsq -- negotiate protocol */ 2944577Seric if (*p == 'R' || *p == 'T') 2954577Seric { 2964577Seric /* recipients first or text first */ 2974577Seric message("200", "%c ok, please continue", *p); 2984577Seric } 2994577Seric else if (*p == '?') 3004577Seric { 3014577Seric /* what do I prefer? anything, anytime */ 3024577Seric message("215", "R Recipients first is my choice"); 3034577Seric } 3044577Seric else if (*p == '\0') 3054577Seric { 3064577Seric /* no meaningful scheme */ 3074577Seric message("200", "okey dokie boobie"); 3084577Seric } 3094577Seric else 3104577Seric { 3114577Seric /* bad argument */ 3124577Seric message("504", "Scheme unknown"); 3134577Seric } 3144577Seric break; 3154577Seric 3168544Seric case CMDVERB: /* set verbose mode */ 3178544Seric Verbose = TRUE; 3188544Seric message("200", "Verbose mode"); 3198544Seric break; 3208544Seric 321*9314Seric case CMDONEX: /* doing one transaction only */ 322*9314Seric onexact = TRUE; 323*9314Seric message("200", "Only one transaction"); 324*9314Seric break; 325*9314Seric 3265003Seric # ifdef DEBUG 3275003Seric case CMDDBGSHOWQ: /* show queues */ 3286907Seric printf("Send Queue="); 3296907Seric printaddr(CurEnv->e_sendqueue, TRUE); 3305003Seric break; 3317275Seric 3327275Seric case CMDDBGDEBUG: /* set debug mode */ 3337676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 3347676Seric tTflag(p); 3357676Seric message("200", "Debug set"); 3367275Seric break; 3377275Seric 3387282Seric case CMDDBGKILL: /* kill the parent */ 3398544Seric if (!iswiz()) 3408544Seric break; 3417282Seric if (kill(MotherPid, SIGTERM) >= 0) 3427282Seric message("200", "Mother is dead"); 3437282Seric else 3447282Seric message("500", "Can't kill Mom"); 3457282Seric break; 3468544Seric 3478544Seric case CMDDBGWIZ: /* become a wizard */ 3488544Seric if (WizWord != NULL) 3498544Seric { 3508544Seric char seed[3]; 3518544Seric extern char *crypt(); 3528544Seric 3538544Seric strncpy(seed, WizWord, 2); 3548544Seric if (strcmp(WizWord, crypt(p, seed)) != 0) 3558544Seric { 3568544Seric message("500", "You are no wizard!"); 3578544Seric break; 3588544Seric } 3598544Seric } 3608544Seric IsWiz = TRUE; 3618544Seric message("200", "Please pass, oh mighty wizard"); 3628544Seric break; 3635003Seric # endif DEBUG 3645003Seric 3654549Seric case CMDERROR: /* unknown command */ 3664549Seric message("500", "Command unrecognized"); 3674549Seric break; 3684549Seric 3694549Seric default: 3704549Seric syserr("smtp: unknown code %d", c->cmdcode); 3714549Seric break; 3724549Seric } 3734549Seric } 3744549Seric } 3754549Seric /* 3764549Seric ** SKIPWORD -- skip a fixed word. 3774549Seric ** 3784549Seric ** Parameters: 3794549Seric ** p -- place to start looking. 3804549Seric ** w -- word to skip. 3814549Seric ** 3824549Seric ** Returns: 3834549Seric ** p following w. 3844549Seric ** NULL on error. 3854549Seric ** 3864549Seric ** Side Effects: 3874549Seric ** clobbers the p data area. 3884549Seric */ 3894549Seric 3904549Seric static char * 3914549Seric skipword(p, w) 3924549Seric register char *p; 3934549Seric char *w; 3944549Seric { 3954549Seric register char *q; 3964549Seric extern bool sameword(); 3974549Seric 3984549Seric /* find beginning of word */ 3994549Seric while (isspace(*p)) 4004549Seric p++; 4014549Seric q = p; 4024549Seric 4034549Seric /* find end of word */ 4044549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 4054549Seric p++; 4064549Seric while (isspace(*p)) 4074549Seric *p++ = '\0'; 4084549Seric if (*p != ':') 4094549Seric { 4104549Seric syntax: 4114549Seric message("501", "Syntax error"); 4124549Seric Errors++; 4134549Seric return (NULL); 4144549Seric } 4154549Seric *p++ = '\0'; 4164549Seric while (isspace(*p)) 4174549Seric p++; 4184549Seric 4194549Seric /* see if the input word matches desired word */ 4204549Seric if (!sameword(q, w)) 4214549Seric goto syntax; 4224549Seric 4234549Seric return (p); 4244549Seric } 4254577Seric /* 4264577Seric ** HELP -- implement the HELP command. 4274577Seric ** 4284577Seric ** Parameters: 4294577Seric ** topic -- the topic we want help for. 4304577Seric ** 4314577Seric ** Returns: 4324577Seric ** none. 4334577Seric ** 4344577Seric ** Side Effects: 4354577Seric ** outputs the help file to message output. 4364577Seric */ 4374577Seric 4384577Seric help(topic) 4394577Seric char *topic; 4404577Seric { 4414577Seric register FILE *hf; 4424577Seric int len; 4434577Seric char buf[MAXLINE]; 4444577Seric bool noinfo; 4454577Seric 4468269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 4474577Seric { 4484577Seric /* no help */ 4494577Seric message("502", "HELP not implemented"); 4504577Seric return; 4514577Seric } 4524577Seric 4534577Seric len = strlen(topic); 4544577Seric makelower(topic); 4554577Seric noinfo = TRUE; 4564577Seric 4574577Seric while (fgets(buf, sizeof buf, hf) != NULL) 4584577Seric { 4594577Seric if (strncmp(buf, topic, len) == 0) 4604577Seric { 4614577Seric register char *p; 4624577Seric 4634577Seric p = index(buf, '\t'); 4644577Seric if (p == NULL) 4654577Seric p = buf; 4664577Seric else 4674577Seric p++; 4684577Seric fixcrlf(p, TRUE); 4694577Seric message("214-", p); 4704577Seric noinfo = FALSE; 4714577Seric } 4724577Seric } 4734577Seric 4744577Seric if (noinfo) 4754577Seric message("504", "HELP topic unknown"); 4764577Seric else 4774577Seric message("214", "End of HELP info"); 4784628Seric (void) fclose(hf); 4794577Seric } 4808544Seric /* 4818544Seric ** ISWIZ -- tell us if we are a wizard 4828544Seric ** 4838544Seric ** If not, print a nasty message. 4848544Seric ** 4858544Seric ** Parameters: 4868544Seric ** none. 4878544Seric ** 4888544Seric ** Returns: 4898544Seric ** TRUE if we are a wizard. 4908544Seric ** FALSE if we are not a wizard. 4918544Seric ** 4928544Seric ** Side Effects: 4938544Seric ** Prints a 500 exit stat if we are not a wizard. 4948544Seric */ 4955181Seric 4968544Seric bool 4978544Seric iswiz() 4988544Seric { 4998544Seric if (!IsWiz) 5008544Seric message("500", "Mere mortals musn't mutter that mantra"); 5018544Seric return (IsWiz); 5028544Seric } 5038544Seric 5045181Seric # endif SMTP 505