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