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