1 # include "sendmail.h" 2 3 static char SccsId[] = "@(#)srvrsmtp.c 3.8 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 "help", CMDHELP, 47 "noop", CMDNOOP, 48 "quit", CMDQUIT, 49 "mrsq", CMDMRSQ, 50 "helo", CMDHELO, 51 NULL, CMDERROR, 52 }; 53 54 smtp() 55 { 56 char inp[MAXLINE]; 57 register char *p; 58 struct cmd *c; 59 char *cmd; 60 extern char *skipword(); 61 extern bool sameword(); 62 bool hasmail; /* mail command received */ 63 int rcps; /* number of recipients */ 64 bool hasdata; /* has mail data */ 65 66 hasmail = hasdata = FALSE; 67 rcps = 0; 68 message("220", "%s Sendmail at your service", HostName); 69 for (;;) 70 { 71 To = NULL; 72 Errors = 0; 73 if (fgets(inp, sizeof inp, InChannel) == NULL) 74 { 75 /* end of file, just die */ 76 message("421", "%s Lost input channel", HostName); 77 finis(); 78 } 79 80 /* clean up end of line */ 81 fixcrlf(inp, TRUE); 82 83 /* echo command to transcript */ 84 fprintf(Xscript, "*** %s\n", inp); 85 86 /* break off command */ 87 for (p = inp; isspace(*p); p++) 88 continue; 89 cmd = p; 90 while (*++p != '\0' && !isspace(*p)) 91 continue; 92 if (*p != '\0') 93 *p++ = '\0'; 94 95 /* decode command */ 96 for (c = CmdTab; c->cmdname != NULL; c++) 97 { 98 if (sameword(c->cmdname, cmd)) 99 break; 100 } 101 102 /* process command */ 103 switch (c->cmdcode) 104 { 105 case CMDHELO: /* hello -- introduce yourself */ 106 message("250", "%s Pleased to meet you", HostName); 107 break; 108 109 case CMDMAIL: /* mail -- designate sender */ 110 if (hasmail) 111 { 112 message("503", "Sender already specified"); 113 break; 114 } 115 p = skipword(p, "from"); 116 if (p == NULL) 117 break; 118 if (index(p, ',') != NULL) 119 { 120 message("501", "Source routing not implemented"); 121 Errors++; 122 break; 123 } 124 setsender(p); 125 if (Errors == 0) 126 { 127 message("250", "Sender ok"); 128 hasmail = TRUE; 129 } 130 break; 131 132 case CMDRCPT: /* rcpt -- designate recipient */ 133 p = skipword(p, "to"); 134 if (p == NULL) 135 break; 136 if (index(p, ',') != NULL) 137 { 138 message("501", "Source routing not implemented"); 139 Errors++; 140 break; 141 } 142 sendto(p, 1, (ADDRESS *) NULL); 143 if (Errors == 0) 144 { 145 message("250", "Recipient ok"); 146 rcps++; 147 } 148 break; 149 150 case CMDDATA: /* data -- text of mail */ 151 if (!hasmail) 152 { 153 message("503", "Need MAIL command"); 154 break; 155 } 156 else if (rcps <= 0) 157 { 158 message("503", "Need RCPT (recipient)"); 159 break; 160 } 161 162 /* collect the text of the message */ 163 collect(TRUE); 164 if (Errors != 0) 165 break; 166 167 /* if sending to multiple people, mail back errors */ 168 if (rcps != 1) 169 HoldErrs = MailBack = TRUE; 170 171 /* send to all recipients */ 172 sendall(FALSE); 173 174 /* reset strange modes */ 175 HoldErrs = FALSE; 176 To = NULL; 177 178 /* issue success if appropriate */ 179 if (Errors == 0 || rcps != 1) 180 message("250", "Sent"); 181 break; 182 183 case CMDRSET: /* rset -- reset state */ 184 message("250", "Reset state"); 185 finis(); 186 187 case CMDVRFY: /* vrfy -- verify address */ 188 sendto(p, 1, (ADDRESS *) NULL); 189 if (Errors == 0) 190 message("250", "user ok"); 191 break; 192 193 case CMDHELP: /* help -- give user info */ 194 if (*p == '\0') 195 p = "SMTP"; 196 help(p); 197 break; 198 199 case CMDNOOP: /* noop -- do nothing */ 200 message("200", "OK"); 201 break; 202 203 case CMDQUIT: /* quit -- leave mail */ 204 message("221", "%s closing connection", HostName); 205 finis(); 206 207 case CMDMRSQ: /* mrsq -- negotiate protocol */ 208 if (*p == 'R' || *p == 'T') 209 { 210 /* recipients first or text first */ 211 message("200", "%c ok, please continue", *p); 212 } 213 else if (*p == '?') 214 { 215 /* what do I prefer? anything, anytime */ 216 message("215", "R Recipients first is my choice"); 217 } 218 else if (*p == '\0') 219 { 220 /* no meaningful scheme */ 221 message("200", "okey dokie boobie"); 222 } 223 else 224 { 225 /* bad argument */ 226 message("504", "Scheme unknown"); 227 } 228 break; 229 230 case CMDERROR: /* unknown command */ 231 message("500", "Command unrecognized"); 232 break; 233 234 default: 235 syserr("smtp: unknown code %d", c->cmdcode); 236 break; 237 } 238 } 239 } 240 /* 241 ** SKIPWORD -- skip a fixed word. 242 ** 243 ** Parameters: 244 ** p -- place to start looking. 245 ** w -- word to skip. 246 ** 247 ** Returns: 248 ** p following w. 249 ** NULL on error. 250 ** 251 ** Side Effects: 252 ** clobbers the p data area. 253 */ 254 255 static char * 256 skipword(p, w) 257 register char *p; 258 char *w; 259 { 260 register char *q; 261 extern bool sameword(); 262 263 /* find beginning of word */ 264 while (isspace(*p)) 265 p++; 266 q = p; 267 268 /* find end of word */ 269 while (*p != '\0' && *p != ':' && !isspace(*p)) 270 p++; 271 while (isspace(*p)) 272 *p++ = '\0'; 273 if (*p != ':') 274 { 275 syntax: 276 message("501", "Syntax error"); 277 Errors++; 278 return (NULL); 279 } 280 *p++ = '\0'; 281 while (isspace(*p)) 282 p++; 283 284 /* see if the input word matches desired word */ 285 if (!sameword(q, w)) 286 goto syntax; 287 288 return (p); 289 } 290 /* 291 ** HELP -- implement the HELP command. 292 ** 293 ** Parameters: 294 ** topic -- the topic we want help for. 295 ** 296 ** Returns: 297 ** none. 298 ** 299 ** Side Effects: 300 ** outputs the help file to message output. 301 */ 302 303 help(topic) 304 char *topic; 305 { 306 register FILE *hf; 307 int len; 308 char buf[MAXLINE]; 309 bool noinfo; 310 extern char *HelpFile; 311 312 hf = fopen(HelpFile, "r"); 313 if (hf == NULL) 314 { 315 /* no help */ 316 message("502", "HELP not implemented"); 317 return; 318 } 319 320 len = strlen(topic); 321 makelower(topic); 322 noinfo = TRUE; 323 324 while (fgets(buf, sizeof buf, hf) != NULL) 325 { 326 if (strncmp(buf, topic, len) == 0) 327 { 328 register char *p; 329 330 p = index(buf, '\t'); 331 if (p == NULL) 332 p = buf; 333 else 334 p++; 335 fixcrlf(p, TRUE); 336 message("214-", p); 337 noinfo = FALSE; 338 } 339 } 340 341 if (noinfo) 342 message("504", "HELP topic unknown"); 343 else 344 message("214", "End of HELP info"); 345 (void) fclose(hf); 346 } 347