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