1 # include "sendmail.h" 2 3 # ifndef SMTP 4 SCCSID(@(#)srvrsmtp.c 3.34 10/16/82 (no SMTP)); 5 # else SMTP 6 7 SCCSID(@(#)srvrsmtp.c 3.34 10/16/82); 8 9 /* 10 ** SMTP -- run the SMTP protocol. 11 ** 12 ** Parameters: 13 ** none. 14 ** 15 ** Returns: 16 ** never. 17 ** 18 ** Side Effects: 19 ** Reads commands from the input channel and processes 20 ** them. 21 */ 22 23 struct cmd 24 { 25 char *cmdname; /* command name */ 26 int cmdcode; /* internal code, see below */ 27 }; 28 29 /* values for cmdcode */ 30 # define CMDERROR 0 /* bad command */ 31 # define CMDMAIL 1 /* mail -- designate sender */ 32 # define CMDRCPT 2 /* rcpt -- designate recipient */ 33 # define CMDDATA 3 /* data -- send message text */ 34 # define CMDRSET 5 /* rset -- reset state */ 35 # define CMDVRFY 6 /* vrfy -- verify address */ 36 # define CMDHELP 7 /* help -- give usage info */ 37 # define CMDNOOP 8 /* noop -- do nothing */ 38 # define CMDQUIT 9 /* quit -- close connection and die */ 39 # define CMDMRSQ 10 /* mrsq -- for old mtp compat only */ 40 # define CMDHELO 11 /* helo -- be polite */ 41 # define CMDDBGSHOWQ 12 /* showq -- show send queue (DEBUG) */ 42 # define CMDDBGDEBUG 13 /* debug -- set debug mode */ 43 # define CMDVERB 14 /* verb -- go into verbose mode */ 44 # define CMDDBGKILL 15 /* kill -- kill sendmail */ 45 # define CMDDBGWIZ 16 /* wiz -- become a wizard */ 46 47 static struct cmd CmdTab[] = 48 { 49 "mail", CMDMAIL, 50 "rcpt", CMDRCPT, 51 "mrcp", CMDRCPT, /* for old MTP compatability */ 52 "data", CMDDATA, 53 "rset", CMDRSET, 54 "vrfy", CMDVRFY, 55 "expn", CMDVRFY, 56 "help", CMDHELP, 57 "noop", CMDNOOP, 58 "quit", CMDQUIT, 59 "mrsq", CMDMRSQ, 60 "helo", CMDHELO, 61 "verb", CMDVERB, 62 # ifdef DEBUG 63 "showq", CMDDBGSHOWQ, 64 "debug", CMDDBGDEBUG, 65 "kill", CMDDBGKILL, 66 "wiz", CMDDBGWIZ, 67 # endif DEBUG 68 NULL, CMDERROR, 69 }; 70 71 # ifdef DEBUG 72 bool IsWiz = FALSE; /* set if we are a wizard */ 73 char *WizWord = NULL; /* the wizard word to compare against */ 74 # endif DEBUG 75 76 smtp() 77 { 78 register char *p; 79 register struct cmd *c; 80 char *cmd; 81 extern char *skipword(); 82 extern bool sameword(); 83 bool hasmail; /* mail command received */ 84 int rcps; /* number of recipients */ 85 auto ADDRESS *vrfyqueue; 86 char inp[MAXLINE]; 87 extern char Version[]; 88 extern tick(); 89 extern bool iswiz(); 90 91 hasmail = FALSE; 92 rcps = 0; 93 if (OutChannel != stdout) 94 { 95 /* arrange for debugging output to go to remote host */ 96 (void) close(1); 97 (void) dup(fileno(OutChannel)); 98 } 99 message("220", "%s Sendmail %s ready at %s", HostName, 100 Version, arpadate(NULL)); 101 (void) setjmp(TopFrame); 102 QuickAbort = FALSE; 103 for (;;) 104 { 105 /* setup for the read */ 106 CurEnv->e_to = NULL; 107 Errors = 0; 108 (void) fflush(stdout); 109 110 /* read the input line */ 111 p = sfgets(inp, sizeof inp, InChannel); 112 113 /* handle errors */ 114 if (p == NULL) 115 { 116 /* end of file, just die */ 117 message("421", "%s Lost input channel", HostName); 118 finis(); 119 } 120 121 /* clean up end of line */ 122 fixcrlf(inp, TRUE); 123 124 /* echo command to transcript */ 125 fprintf(Xscript, "<<< %s\n", inp); 126 127 /* break off command */ 128 for (p = inp; isspace(*p); p++) 129 continue; 130 cmd = p; 131 while (*++p != '\0' && !isspace(*p)) 132 continue; 133 if (*p != '\0') 134 *p++ = '\0'; 135 136 /* decode command */ 137 for (c = CmdTab; c->cmdname != NULL; c++) 138 { 139 if (sameword(c->cmdname, cmd)) 140 break; 141 } 142 143 /* process command */ 144 switch (c->cmdcode) 145 { 146 case CMDHELO: /* hello -- introduce yourself */ 147 define('s', newstr(p)); 148 message("250", "%s Hello %s, pleased to meet you", 149 HostName, p); 150 break; 151 152 case CMDMAIL: /* mail -- designate sender */ 153 if (hasmail) 154 { 155 message("503", "Sender already specified"); 156 break; 157 } 158 p = skipword(p, "from"); 159 if (p == NULL) 160 break; 161 if (index(p, ',') != NULL) 162 { 163 message("501", "Source routing not implemented"); 164 Errors++; 165 break; 166 } 167 setsender(p); 168 if (Errors == 0) 169 { 170 message("250", "Sender ok"); 171 hasmail = TRUE; 172 } 173 break; 174 175 case CMDRCPT: /* rcpt -- designate recipient */ 176 p = skipword(p, "to"); 177 if (p == NULL) 178 break; 179 sendto(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 180 if (Errors == 0) 181 { 182 message("250", "%s... Recipient ok", p); 183 rcps++; 184 } 185 break; 186 187 case CMDDATA: /* data -- text of mail */ 188 if (!hasmail) 189 { 190 message("503", "Need MAIL command"); 191 break; 192 } 193 else if (rcps <= 0) 194 { 195 message("503", "Need RCPT (recipient)"); 196 break; 197 } 198 199 /* collect the text of the message */ 200 collect(TRUE); 201 if (Errors != 0) 202 break; 203 204 /* 205 ** Arrange to send to everyone. 206 ** If sending to multiple people, mail back 207 ** errors rather than reporting directly. 208 ** In any case, don't mail back errors for 209 ** anything that has happened up to 210 ** now (the other end will do this). 211 ** Then send to everyone. 212 ** Finally give a reply code. If an error has 213 ** already been given, don't mail a 214 ** message back. 215 ** We goose error returns by clearing FatalErrors. 216 */ 217 218 if (rcps != 1) 219 HoldErrs = MailBack = TRUE; 220 FatalErrors = FALSE; 221 222 /* send to all recipients */ 223 sendall(CurEnv, FALSE); 224 CurEnv->e_to = NULL; 225 226 /* issue success if appropriate and reset */ 227 if (Errors == 0 || HoldErrs) 228 { 229 HoldErrs = FALSE; 230 message("250", "Sent"); 231 } 232 else 233 FatalErrors = FALSE; 234 break; 235 236 case CMDRSET: /* rset -- reset state */ 237 message("250", "Reset state"); 238 finis(); 239 240 case CMDVRFY: /* vrfy -- verify address */ 241 vrfyqueue = NULL; 242 QuickAbort = TRUE; 243 sendto(p, (ADDRESS *) NULL, &vrfyqueue); 244 if (Errors != 0) 245 break; 246 while (vrfyqueue != NULL) 247 { 248 register ADDRESS *a = vrfyqueue->q_next; 249 char *code; 250 251 while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 252 a = a->q_next; 253 254 if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 255 { 256 if (a != NULL) 257 code = "250-"; 258 else 259 code = "250"; 260 if (vrfyqueue->q_fullname == NULL) 261 message(code, "<%s>", vrfyqueue->q_paddr); 262 else 263 message(code, "%s <%s>", 264 vrfyqueue->q_fullname, vrfyqueue->q_paddr); 265 } 266 else if (a == NULL) 267 message("554", "Self destructive alias loop"); 268 vrfyqueue = a; 269 } 270 break; 271 272 case CMDHELP: /* help -- give user info */ 273 if (*p == '\0') 274 p = "SMTP"; 275 help(p); 276 break; 277 278 case CMDNOOP: /* noop -- do nothing */ 279 message("200", "OK"); 280 break; 281 282 case CMDQUIT: /* quit -- leave mail */ 283 message("221", "%s closing connection", HostName); 284 finis(); 285 286 case CMDMRSQ: /* mrsq -- negotiate protocol */ 287 if (*p == 'R' || *p == 'T') 288 { 289 /* recipients first or text first */ 290 message("200", "%c ok, please continue", *p); 291 } 292 else if (*p == '?') 293 { 294 /* what do I prefer? anything, anytime */ 295 message("215", "R Recipients first is my choice"); 296 } 297 else if (*p == '\0') 298 { 299 /* no meaningful scheme */ 300 message("200", "okey dokie boobie"); 301 } 302 else 303 { 304 /* bad argument */ 305 message("504", "Scheme unknown"); 306 } 307 break; 308 309 case CMDVERB: /* set verbose mode */ 310 Verbose = TRUE; 311 message("200", "Verbose mode"); 312 break; 313 314 # ifdef DEBUG 315 case CMDDBGSHOWQ: /* show queues */ 316 printf("Send Queue="); 317 printaddr(CurEnv->e_sendqueue, TRUE); 318 break; 319 320 case CMDDBGDEBUG: /* set debug mode */ 321 tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 322 tTflag(p); 323 message("200", "Debug set"); 324 break; 325 326 case CMDDBGKILL: /* kill the parent */ 327 if (!iswiz()) 328 break; 329 if (kill(MotherPid, SIGTERM) >= 0) 330 message("200", "Mother is dead"); 331 else 332 message("500", "Can't kill Mom"); 333 break; 334 335 case CMDDBGWIZ: /* become a wizard */ 336 if (WizWord != NULL) 337 { 338 char seed[3]; 339 extern char *crypt(); 340 341 strncpy(seed, WizWord, 2); 342 if (strcmp(WizWord, crypt(p, seed)) != 0) 343 { 344 message("500", "You are no wizard!"); 345 break; 346 } 347 } 348 IsWiz = TRUE; 349 message("200", "Please pass, oh mighty wizard"); 350 break; 351 # endif DEBUG 352 353 case CMDERROR: /* unknown command */ 354 message("500", "Command unrecognized"); 355 break; 356 357 default: 358 syserr("smtp: unknown code %d", c->cmdcode); 359 break; 360 } 361 } 362 } 363 /* 364 ** SKIPWORD -- skip a fixed word. 365 ** 366 ** Parameters: 367 ** p -- place to start looking. 368 ** w -- word to skip. 369 ** 370 ** Returns: 371 ** p following w. 372 ** NULL on error. 373 ** 374 ** Side Effects: 375 ** clobbers the p data area. 376 */ 377 378 static char * 379 skipword(p, w) 380 register char *p; 381 char *w; 382 { 383 register char *q; 384 extern bool sameword(); 385 386 /* find beginning of word */ 387 while (isspace(*p)) 388 p++; 389 q = p; 390 391 /* find end of word */ 392 while (*p != '\0' && *p != ':' && !isspace(*p)) 393 p++; 394 while (isspace(*p)) 395 *p++ = '\0'; 396 if (*p != ':') 397 { 398 syntax: 399 message("501", "Syntax error"); 400 Errors++; 401 return (NULL); 402 } 403 *p++ = '\0'; 404 while (isspace(*p)) 405 p++; 406 407 /* see if the input word matches desired word */ 408 if (!sameword(q, w)) 409 goto syntax; 410 411 return (p); 412 } 413 /* 414 ** HELP -- implement the HELP command. 415 ** 416 ** Parameters: 417 ** topic -- the topic we want help for. 418 ** 419 ** Returns: 420 ** none. 421 ** 422 ** Side Effects: 423 ** outputs the help file to message output. 424 */ 425 426 help(topic) 427 char *topic; 428 { 429 register FILE *hf; 430 int len; 431 char buf[MAXLINE]; 432 bool noinfo; 433 434 if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 435 { 436 /* no help */ 437 message("502", "HELP not implemented"); 438 return; 439 } 440 441 len = strlen(topic); 442 makelower(topic); 443 noinfo = TRUE; 444 445 while (fgets(buf, sizeof buf, hf) != NULL) 446 { 447 if (strncmp(buf, topic, len) == 0) 448 { 449 register char *p; 450 451 p = index(buf, '\t'); 452 if (p == NULL) 453 p = buf; 454 else 455 p++; 456 fixcrlf(p, TRUE); 457 message("214-", p); 458 noinfo = FALSE; 459 } 460 } 461 462 if (noinfo) 463 message("504", "HELP topic unknown"); 464 else 465 message("214", "End of HELP info"); 466 (void) fclose(hf); 467 } 468 /* 469 ** ISWIZ -- tell us if we are a wizard 470 ** 471 ** If not, print a nasty message. 472 ** 473 ** Parameters: 474 ** none. 475 ** 476 ** Returns: 477 ** TRUE if we are a wizard. 478 ** FALSE if we are not a wizard. 479 ** 480 ** Side Effects: 481 ** Prints a 500 exit stat if we are not a wizard. 482 */ 483 484 bool 485 iswiz() 486 { 487 if (!IsWiz) 488 message("500", "Mere mortals musn't mutter that mantra"); 489 return (IsWiz); 490 } 491 492 # endif SMTP 493