1 # include "sendmail.h" 2 3 static char SccsId[] = "@(#)srvrsmtp.c 3.10 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 # define CMDDBGSHOWQ 12 /* showq -- show send queue (DEBUG) */ 38 39 static struct cmd CmdTab[] = 40 { 41 "mail", CMDMAIL, 42 "rcpt", CMDRCPT, 43 "mrcp", CMDRCPT, /* for old MTP compatability */ 44 "data", CMDDATA, 45 "rset", CMDRSET, 46 "vrfy", CMDVRFY, 47 "help", CMDHELP, 48 "noop", CMDNOOP, 49 "quit", CMDQUIT, 50 "mrsq", CMDMRSQ, 51 "helo", CMDHELO, 52 # ifdef DEBUG 53 "showq", CMDDBGSHOWQ, 54 # endif DEBUG 55 NULL, CMDERROR, 56 }; 57 58 smtp() 59 { 60 char inp[MAXLINE]; 61 register char *p; 62 struct cmd *c; 63 char *cmd; 64 extern char *skipword(); 65 extern bool sameword(); 66 bool hasmail; /* mail command received */ 67 int rcps; /* number of recipients */ 68 auto ADDRESS *vrfyqueue; 69 ADDRESS *prev; 70 71 hasmail = FALSE; 72 rcps = 0; 73 message("220", "%s Sendmail at your service", HostName); 74 for (;;) 75 { 76 To = NULL; 77 Errors = 0; 78 if (fgets(inp, sizeof inp, InChannel) == NULL) 79 { 80 /* end of file, just die */ 81 message("421", "%s Lost input channel", HostName); 82 finis(); 83 } 84 85 /* clean up end of line */ 86 fixcrlf(inp, TRUE); 87 88 /* echo command to transcript */ 89 fprintf(Xscript, "*** %s\n", inp); 90 91 /* break off command */ 92 for (p = inp; isspace(*p); p++) 93 continue; 94 cmd = p; 95 while (*++p != '\0' && !isspace(*p)) 96 continue; 97 if (*p != '\0') 98 *p++ = '\0'; 99 100 /* decode command */ 101 for (c = CmdTab; c->cmdname != NULL; c++) 102 { 103 if (sameword(c->cmdname, cmd)) 104 break; 105 } 106 107 /* process command */ 108 switch (c->cmdcode) 109 { 110 case CMDHELO: /* hello -- introduce yourself */ 111 message("250", "%s Hello %s, pleased to meet you", 112 HostName, p); 113 break; 114 115 case CMDMAIL: /* mail -- designate sender */ 116 if (hasmail) 117 { 118 message("503", "Sender already specified"); 119 break; 120 } 121 p = skipword(p, "from"); 122 if (p == NULL) 123 break; 124 if (index(p, ',') != NULL) 125 { 126 message("501", "Source routing not implemented"); 127 Errors++; 128 break; 129 } 130 setsender(p); 131 if (Errors == 0) 132 { 133 message("250", "Sender ok"); 134 hasmail = TRUE; 135 } 136 break; 137 138 case CMDRCPT: /* rcpt -- designate recipient */ 139 p = skipword(p, "to"); 140 if (p == NULL) 141 break; 142 if (index(p, ',') != NULL) 143 { 144 message("501", "Source routing not implemented"); 145 Errors++; 146 break; 147 } 148 sendto(p, 1, (ADDRESS *) NULL, &SendQueue); 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 vrfyqueue = NULL; 195 sendto(p, 1, (ADDRESS *) NULL, &vrfyqueue); 196 while (vrfyqueue != NULL) 197 { 198 register ADDRESS *a = vrfyqueue->q_next; 199 char *code; 200 201 while (a != NULL && bitset(QDONTSEND, a->q_flags)) 202 a = a->q_next; 203 204 if (!bitset(QDONTSEND, vrfyqueue->q_flags)) 205 { 206 if (a != NULL) 207 code = "250-"; 208 else 209 code = "250"; 210 if (vrfyqueue->q_fullname == NULL) 211 message(code, "<%s>", vrfyqueue->q_paddr); 212 else 213 message(code, "%s <%s>", 214 vrfyqueue->q_fullname, vrfyqueue->q_paddr); 215 } 216 else if (a == NULL) 217 message("554", "Self destructive alias loop"); 218 vrfyqueue = a; 219 } 220 break; 221 222 case CMDHELP: /* help -- give user info */ 223 if (*p == '\0') 224 p = "SMTP"; 225 help(p); 226 break; 227 228 case CMDNOOP: /* noop -- do nothing */ 229 message("200", "OK"); 230 break; 231 232 case CMDQUIT: /* quit -- leave mail */ 233 message("221", "%s closing connection", HostName); 234 finis(); 235 236 case CMDMRSQ: /* mrsq -- negotiate protocol */ 237 if (*p == 'R' || *p == 'T') 238 { 239 /* recipients first or text first */ 240 message("200", "%c ok, please continue", *p); 241 } 242 else if (*p == '?') 243 { 244 /* what do I prefer? anything, anytime */ 245 message("215", "R Recipients first is my choice"); 246 } 247 else if (*p == '\0') 248 { 249 /* no meaningful scheme */ 250 message("200", "okey dokie boobie"); 251 } 252 else 253 { 254 /* bad argument */ 255 message("504", "Scheme unknown"); 256 } 257 break; 258 259 # ifdef DEBUG 260 case CMDDBGSHOWQ: /* show queues */ 261 printf("SendQueue="); 262 printaddr(SendQueue, TRUE); 263 break; 264 # endif DEBUG 265 266 case CMDERROR: /* unknown command */ 267 message("500", "Command unrecognized"); 268 break; 269 270 default: 271 syserr("smtp: unknown code %d", c->cmdcode); 272 break; 273 } 274 } 275 } 276 /* 277 ** SKIPWORD -- skip a fixed word. 278 ** 279 ** Parameters: 280 ** p -- place to start looking. 281 ** w -- word to skip. 282 ** 283 ** Returns: 284 ** p following w. 285 ** NULL on error. 286 ** 287 ** Side Effects: 288 ** clobbers the p data area. 289 */ 290 291 static char * 292 skipword(p, w) 293 register char *p; 294 char *w; 295 { 296 register char *q; 297 extern bool sameword(); 298 299 /* find beginning of word */ 300 while (isspace(*p)) 301 p++; 302 q = p; 303 304 /* find end of word */ 305 while (*p != '\0' && *p != ':' && !isspace(*p)) 306 p++; 307 while (isspace(*p)) 308 *p++ = '\0'; 309 if (*p != ':') 310 { 311 syntax: 312 message("501", "Syntax error"); 313 Errors++; 314 return (NULL); 315 } 316 *p++ = '\0'; 317 while (isspace(*p)) 318 p++; 319 320 /* see if the input word matches desired word */ 321 if (!sameword(q, w)) 322 goto syntax; 323 324 return (p); 325 } 326 /* 327 ** HELP -- implement the HELP command. 328 ** 329 ** Parameters: 330 ** topic -- the topic we want help for. 331 ** 332 ** Returns: 333 ** none. 334 ** 335 ** Side Effects: 336 ** outputs the help file to message output. 337 */ 338 339 help(topic) 340 char *topic; 341 { 342 register FILE *hf; 343 int len; 344 char buf[MAXLINE]; 345 bool noinfo; 346 extern char *HelpFile; 347 348 hf = fopen(HelpFile, "r"); 349 if (hf == NULL) 350 { 351 /* no help */ 352 message("502", "HELP not implemented"); 353 return; 354 } 355 356 len = strlen(topic); 357 makelower(topic); 358 noinfo = TRUE; 359 360 while (fgets(buf, sizeof buf, hf) != NULL) 361 { 362 if (strncmp(buf, topic, len) == 0) 363 { 364 register char *p; 365 366 p = index(buf, '\t'); 367 if (p == NULL) 368 p = buf; 369 else 370 p++; 371 fixcrlf(p, TRUE); 372 message("214-", p); 373 noinfo = FALSE; 374 } 375 } 376 377 if (noinfo) 378 message("504", "HELP topic unknown"); 379 else 380 message("214", "End of HELP info"); 381 (void) fclose(hf); 382 } 383