1 # include "sendmail.h" 2 3 # ifndef SMTP 4 SCCSID(@(#)srvrsmtp.c 3.27 08/15/82 (no SMTP)); 5 # else SMTP 6 7 SCCSID(@(#)srvrsmtp.c 3.27 08/15/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 version %s at your service", HostName, Version); 92 (void) setjmp(TopFrame); 93 QuickAbort = FALSE; 94 for (;;) 95 { 96 /* setup for the read */ 97 CurEnv->e_to = NULL; 98 Errors = 0; 99 (void) fflush(stdout); 100 101 /* read the input line */ 102 p = sfgets(inp, sizeof inp, InChannel); 103 104 /* handle errors */ 105 if (p == NULL) 106 { 107 /* end of file, just die */ 108 message("421", "%s Lost input channel", HostName); 109 finis(); 110 } 111 112 /* clean up end of line */ 113 fixcrlf(inp, TRUE); 114 115 /* echo command to transcript */ 116 fprintf(Xscript, "<<< %s\n", inp); 117 118 /* break off command */ 119 for (p = inp; isspace(*p); p++) 120 continue; 121 cmd = p; 122 while (*++p != '\0' && !isspace(*p)) 123 continue; 124 if (*p != '\0') 125 *p++ = '\0'; 126 127 /* decode command */ 128 for (c = CmdTab; c->cmdname != NULL; c++) 129 { 130 if (sameword(c->cmdname, cmd)) 131 break; 132 } 133 134 /* process command */ 135 switch (c->cmdcode) 136 { 137 case CMDHELO: /* hello -- introduce yourself */ 138 define('s', newstr(p)); 139 message("250", "%s Hello %s, pleased to meet you", 140 HostName, p); 141 break; 142 143 case CMDMAIL: /* mail -- designate sender */ 144 if (hasmail) 145 { 146 message("503", "Sender already specified"); 147 break; 148 } 149 p = skipword(p, "from"); 150 if (p == NULL) 151 break; 152 if (index(p, ',') != NULL) 153 { 154 message("501", "Source routing not implemented"); 155 Errors++; 156 break; 157 } 158 setsender(p); 159 if (Errors == 0) 160 { 161 message("250", "Sender ok"); 162 hasmail = TRUE; 163 } 164 break; 165 166 case CMDRCPT: /* rcpt -- designate recipient */ 167 p = skipword(p, "to"); 168 if (p == NULL) 169 break; 170 if (index(p, ',') != NULL) 171 { 172 message("501", "Source routing not implemented"); 173 Errors++; 174 break; 175 } 176 sendto(p, 1, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 177 if (Errors == 0) 178 { 179 message("250", "%s... Recipient ok", p); 180 rcps++; 181 } 182 break; 183 184 case CMDDATA: /* data -- text of mail */ 185 if (!hasmail) 186 { 187 message("503", "Need MAIL command"); 188 break; 189 } 190 else if (rcps <= 0) 191 { 192 message("503", "Need RCPT (recipient)"); 193 break; 194 } 195 196 /* collect the text of the message */ 197 collect(TRUE); 198 if (Errors != 0) 199 break; 200 201 /* if sending to multiple people, mail back errors */ 202 if (rcps != 1) 203 HoldErrs = MailBack = TRUE; 204 205 /* send to all recipients */ 206 sendall(CurEnv, FALSE); 207 208 /* reset strange modes */ 209 HoldErrs = FALSE; 210 CurEnv->e_to = NULL; 211 212 /* issue success if appropriate */ 213 if (Errors == 0 || rcps != 1) 214 message("250", "Sent"); 215 break; 216 217 case CMDRSET: /* rset -- reset state */ 218 message("250", "Reset state"); 219 finis(); 220 221 case CMDVRFY: /* vrfy -- verify address */ 222 vrfyqueue = NULL; 223 QuickAbort = TRUE; 224 sendto(p, 1, (ADDRESS *) NULL, &vrfyqueue); 225 if (Errors != 0) 226 break; 227 while (vrfyqueue != NULL) 228 { 229 register ADDRESS *a = vrfyqueue->q_next; 230 char *code; 231 232 while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 233 a = a->q_next; 234 235 if (!bitset(QDONTSEND|QBADADDR, 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 tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 298 tTflag(p); 299 message("200", "Debug set"); 300 break; 301 302 case CMDDBGVERBOSE: /* set verbose mode */ 303 Verbose = TRUE; 304 message("200", "Verbose mode"); 305 break; 306 307 case CMDDBGKILL: /* kill the parent */ 308 if (kill(MotherPid, SIGTERM) >= 0) 309 message("200", "Mother is dead"); 310 else 311 message("500", "Can't kill Mom"); 312 break; 313 # endif DEBUG 314 315 case CMDERROR: /* unknown command */ 316 message("500", "Command unrecognized"); 317 break; 318 319 default: 320 syserr("smtp: unknown code %d", c->cmdcode); 321 break; 322 } 323 } 324 } 325 /* 326 ** SKIPWORD -- skip a fixed word. 327 ** 328 ** Parameters: 329 ** p -- place to start looking. 330 ** w -- word to skip. 331 ** 332 ** Returns: 333 ** p following w. 334 ** NULL on error. 335 ** 336 ** Side Effects: 337 ** clobbers the p data area. 338 */ 339 340 static char * 341 skipword(p, w) 342 register char *p; 343 char *w; 344 { 345 register char *q; 346 extern bool sameword(); 347 348 /* find beginning of word */ 349 while (isspace(*p)) 350 p++; 351 q = p; 352 353 /* find end of word */ 354 while (*p != '\0' && *p != ':' && !isspace(*p)) 355 p++; 356 while (isspace(*p)) 357 *p++ = '\0'; 358 if (*p != ':') 359 { 360 syntax: 361 message("501", "Syntax error"); 362 Errors++; 363 return (NULL); 364 } 365 *p++ = '\0'; 366 while (isspace(*p)) 367 p++; 368 369 /* see if the input word matches desired word */ 370 if (!sameword(q, w)) 371 goto syntax; 372 373 return (p); 374 } 375 /* 376 ** HELP -- implement the HELP command. 377 ** 378 ** Parameters: 379 ** topic -- the topic we want help for. 380 ** 381 ** Returns: 382 ** none. 383 ** 384 ** Side Effects: 385 ** outputs the help file to message output. 386 */ 387 388 help(topic) 389 char *topic; 390 { 391 register FILE *hf; 392 int len; 393 char buf[MAXLINE]; 394 bool noinfo; 395 extern char *HelpFile; 396 397 hf = fopen(HelpFile, "r"); 398 if (hf == NULL) 399 { 400 /* no help */ 401 message("502", "HELP not implemented"); 402 return; 403 } 404 405 len = strlen(topic); 406 makelower(topic); 407 noinfo = TRUE; 408 409 while (fgets(buf, sizeof buf, hf) != NULL) 410 { 411 if (strncmp(buf, topic, len) == 0) 412 { 413 register char *p; 414 415 p = index(buf, '\t'); 416 if (p == NULL) 417 p = buf; 418 else 419 p++; 420 fixcrlf(p, TRUE); 421 message("214-", p); 422 noinfo = FALSE; 423 } 424 } 425 426 if (noinfo) 427 message("504", "HELP topic unknown"); 428 else 429 message("214", "End of HELP info"); 430 (void) fclose(hf); 431 } 432 433 # endif SMTP 434