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