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