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