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