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