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