1 # include <ctype.h> 2 # include <sysexits.h> 3 # include "sendmail.h" 4 5 # ifndef SMTP 6 SCCSID(@(#)usersmtp.c 4.4 09/07/83 (no SMTP)); 7 # else SMTP 8 9 SCCSID(@(#)usersmtp.c 4.4 09/07/83); 10 11 12 13 /* 14 ** USERSMTP -- run SMTP protocol from the user end. 15 ** 16 ** This protocol is described in RFC821. 17 */ 18 19 #define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */ 20 #define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */ 21 #define SMTPCLOSING 421 /* "Service Shutting Down" */ 22 23 char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */ 24 char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */ 25 FILE *SmtpOut; /* output file */ 26 FILE *SmtpIn; /* input file */ 27 int SmtpPid; /* pid of mailer */ 28 29 /* following represents the state of the SMTP connection */ 30 int SmtpState; /* connection state, see below */ 31 32 #define SMTP_CLOSED 0 /* connection is closed */ 33 #define SMTP_OPEN 1 /* connection is open for business */ 34 #define SMTP_SSD 2 /* service shutting down */ 35 /* 36 ** SMTPINIT -- initialize SMTP. 37 ** 38 ** Opens the connection and sends the initial protocol. 39 ** 40 ** Parameters: 41 ** m -- mailer to create connection to. 42 ** pvp -- pointer to parameter vector to pass to 43 ** the mailer. 44 ** 45 ** Returns: 46 ** appropriate exit status -- EX_OK on success. 47 ** If not EX_OK, it should close the connection. 48 ** 49 ** Side Effects: 50 ** creates connection and sends initial protocol. 51 */ 52 53 jmp_buf CtxGreeting; 54 55 smtpinit(m, pvp) 56 struct mailer *m; 57 char **pvp; 58 { 59 register int r; 60 EVENT *gte; 61 char buf[MAXNAME]; 62 extern greettimeout(); 63 64 /* 65 ** Open the connection to the mailer. 66 */ 67 68 #ifdef DEBUG 69 if (SmtpState == SMTP_OPEN) 70 syserr("smtpinit: already open"); 71 #endif DEBUG 72 73 SmtpIn = SmtpOut = NULL; 74 SmtpState = SMTP_CLOSED; 75 SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn); 76 if (SmtpPid < 0) 77 { 78 # ifdef DEBUG 79 if (tTd(18, 1)) 80 printf("smtpinit: cannot open %s: stat %d errno %d\n", 81 pvp[0], ExitStat, errno); 82 # endif DEBUG 83 return (ExitStat); 84 } 85 SmtpState = SMTP_OPEN; 86 87 /* 88 ** Get the greeting message. 89 ** This should appear spontaneously. Give it five minutes to 90 ** happen. 91 */ 92 93 if (setjmp(CtxGreeting) != 0) 94 goto tempfail; 95 gte = setevent(300, greettimeout, 0); 96 r = reply(m); 97 clrevent(gte); 98 if (r < 0 || REPLYTYPE(r) != 2) 99 goto tempfail; 100 101 /* 102 ** Send the HELO command. 103 ** My mother taught me to always introduce myself. 104 */ 105 106 smtpmessage("HELO %s", m, HostName); 107 r = reply(m); 108 if (r < 0) 109 goto tempfail; 110 else if (REPLYTYPE(r) == 5) 111 goto unavailable; 112 else if (REPLYTYPE(r) != 2) 113 goto tempfail; 114 115 /* 116 ** If this is expected to be another sendmail, send some internal 117 ** commands. 118 */ 119 120 if (bitnset(M_INTERNAL, m->m_flags)) 121 { 122 /* tell it to be verbose */ 123 smtpmessage("VERB", m); 124 r = reply(m); 125 if (r < 0) 126 goto tempfail; 127 128 /* tell it we will be sending one transaction only */ 129 smtpmessage("ONEX", m); 130 r = reply(m); 131 if (r < 0) 132 goto tempfail; 133 } 134 135 /* 136 ** Send the MAIL command. 137 ** Designates the sender. 138 */ 139 140 expand("$g", buf, &buf[sizeof buf - 1], CurEnv); 141 if (CurEnv->e_from.q_mailer == LocalMailer || 142 !bitnset(M_FROMPATH, m->m_flags)) 143 { 144 smtpmessage("MAIL From:<%s>", m, buf); 145 } 146 else 147 { 148 smtpmessage("MAIL From:<@%s%c%s>", m, HostName, 149 buf[0] == '@' ? ',' : ':', buf); 150 } 151 r = reply(m); 152 if (r < 0 || REPLYTYPE(r) == 4) 153 goto tempfail; 154 else if (r == 250) 155 return (EX_OK); 156 else if (r == 552) 157 goto unavailable; 158 159 /* protocol error -- close up */ 160 smtpquit(m); 161 return (EX_PROTOCOL); 162 163 /* signal a temporary failure */ 164 tempfail: 165 smtpquit(m); 166 return (EX_TEMPFAIL); 167 168 /* signal service unavailable */ 169 unavailable: 170 smtpquit(m); 171 return (EX_UNAVAILABLE); 172 } 173 174 175 static 176 greettimeout() 177 { 178 /* timeout reading the greeting message */ 179 longjmp(CtxGreeting, 1); 180 } 181 /* 182 ** SMTPRCPT -- designate recipient. 183 ** 184 ** Parameters: 185 ** to -- address of recipient. 186 ** m -- the mailer we are sending to. 187 ** 188 ** Returns: 189 ** exit status corresponding to recipient status. 190 ** 191 ** Side Effects: 192 ** Sends the mail via SMTP. 193 */ 194 195 smtprcpt(to, m) 196 ADDRESS *to; 197 register MAILER *m; 198 { 199 register int r; 200 extern char *remotename(); 201 202 smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE)); 203 204 r = reply(m); 205 if (r < 0 || REPLYTYPE(r) == 4) 206 return (EX_TEMPFAIL); 207 else if (REPLYTYPE(r) == 2) 208 return (EX_OK); 209 else if (r == 550 || r == 551 || r == 553) 210 return (EX_NOUSER); 211 else if (r == 552 || r == 554) 212 return (EX_UNAVAILABLE); 213 return (EX_PROTOCOL); 214 } 215 /* 216 ** SMTPDATA -- send the data and clean up the transaction. 217 ** 218 ** Parameters: 219 ** m -- mailer being sent to. 220 ** e -- the envelope for this message. 221 ** 222 ** Returns: 223 ** exit status corresponding to DATA command. 224 ** 225 ** Side Effects: 226 ** none. 227 */ 228 229 smtpdata(m, e) 230 struct mailer *m; 231 register ENVELOPE *e; 232 { 233 register int r; 234 235 /* 236 ** Send the data. 237 ** First send the command and check that it is ok. 238 ** Then send the data. 239 ** Follow it up with a dot to terminate. 240 ** Finally get the results of the transaction. 241 */ 242 243 /* send the command and check ok to proceed */ 244 smtpmessage("DATA", m); 245 r = reply(m); 246 if (r < 0 || REPLYTYPE(r) == 4) 247 return (EX_TEMPFAIL); 248 else if (r == 554) 249 return (EX_UNAVAILABLE); 250 else if (r != 354) 251 return (EX_PROTOCOL); 252 253 /* now output the actual message */ 254 (*e->e_puthdr)(SmtpOut, m, CurEnv); 255 putline("\n", SmtpOut, m); 256 (*e->e_putbody)(SmtpOut, m, CurEnv); 257 258 /* terminate the message */ 259 fprintf(SmtpOut, ".%s", m->m_eol); 260 if (Verbose && !HoldErrs) 261 nmessage(Arpa_Info, ">>> ."); 262 263 /* check for the results of the transaction */ 264 r = reply(m); 265 if (r < 0 || REPLYTYPE(r) == 4) 266 return (EX_TEMPFAIL); 267 else if (r == 250) 268 return (EX_OK); 269 else if (r == 552 || r == 554) 270 return (EX_UNAVAILABLE); 271 return (EX_PROTOCOL); 272 } 273 /* 274 ** SMTPQUIT -- close the SMTP connection. 275 ** 276 ** Parameters: 277 ** name -- name of mailer we are quitting. 278 ** 279 ** Returns: 280 ** none. 281 ** 282 ** Side Effects: 283 ** sends the final protocol and closes the connection. 284 */ 285 286 smtpquit(name, m) 287 char *name; 288 register MAILER *m; 289 { 290 int i; 291 292 /* if the connection is already closed, don't bother */ 293 if (SmtpIn == NULL) 294 return; 295 296 /* send the quit message if not a forced quit */ 297 if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD) 298 { 299 smtpmessage("QUIT", m); 300 (void) reply(m); 301 if (SmtpState == SMTP_CLOSED) 302 return; 303 } 304 305 /* now actually close the connection */ 306 (void) fclose(SmtpIn); 307 (void) fclose(SmtpOut); 308 SmtpIn = SmtpOut = NULL; 309 SmtpState = SMTP_CLOSED; 310 311 /* and pick up the zombie */ 312 i = endmailer(SmtpPid, name); 313 if (i != EX_OK) 314 syserr("smtpquit %s: stat %d", name, i); 315 } 316 /* 317 ** REPLY -- read arpanet reply 318 ** 319 ** Parameters: 320 ** m -- the mailer we are reading the reply from. 321 ** 322 ** Returns: 323 ** reply code it reads. 324 ** 325 ** Side Effects: 326 ** flushes the mail file. 327 */ 328 329 reply(m) 330 MAILER *m; 331 { 332 (void) fflush(SmtpOut); 333 334 if (tTd(18, 1)) 335 printf("reply\n"); 336 337 /* 338 ** Read the input line, being careful not to hang. 339 */ 340 341 for (;;) 342 { 343 register int r; 344 register char *p; 345 346 /* actually do the read */ 347 if (CurEnv->e_xfp != NULL) 348 (void) fflush(CurEnv->e_xfp); /* for debugging */ 349 350 /* if we are in the process of closing just give the code */ 351 if (SmtpState == SMTP_CLOSED) 352 return (SMTPCLOSING); 353 354 /* get the line from the other side */ 355 p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn); 356 if (p == NULL) 357 { 358 extern char MsgBuf[]; /* err.c */ 359 extern char Arpa_TSyserr[]; /* conf.c */ 360 361 message(Arpa_TSyserr, "reply: read error"); 362 # ifdef DEBUG 363 /* if debugging, pause so we can see state */ 364 if (tTd(18, 100)) 365 pause(); 366 # endif DEBUG 367 # ifdef LOG 368 syslog(LOG_ERR, "%s", &MsgBuf[4]); 369 # endif LOG 370 SmtpState = SMTP_CLOSED; 371 smtpquit("reply error", m); 372 return (-1); 373 } 374 fixcrlf(SmtpReplyBuffer, TRUE); 375 376 if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL) 377 { 378 /* serious error -- log the previous command */ 379 if (SmtpMsgBuffer[0] != '\0') 380 fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer); 381 SmtpMsgBuffer[0] = '\0'; 382 383 /* now log the message as from the other side */ 384 fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer); 385 } 386 387 /* display the input for verbose mode */ 388 if (Verbose && !HoldErrs) 389 nmessage(Arpa_Info, "%s", SmtpReplyBuffer); 390 391 /* if continuation is required, we can go on */ 392 if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0])) 393 continue; 394 395 /* decode the reply code */ 396 r = atoi(SmtpReplyBuffer); 397 398 /* extra semantics: 0xx codes are "informational" */ 399 if (r < 100) 400 continue; 401 402 /* reply code 421 is "Service Shutting Down" */ 403 if (r == SMTPCLOSING && SmtpState != SMTP_SSD) 404 { 405 /* send the quit protocol */ 406 SmtpState = SMTP_SSD; 407 smtpquit("SMTP Shutdown", m); 408 } 409 410 return (r); 411 } 412 } 413 /* 414 ** SMTPMESSAGE -- send message to server 415 ** 416 ** Parameters: 417 ** f -- format 418 ** m -- the mailer to control formatting. 419 ** a, b, c -- parameters 420 ** 421 ** Returns: 422 ** none. 423 ** 424 ** Side Effects: 425 ** writes message to SmtpOut. 426 */ 427 428 /*VARARGS1*/ 429 smtpmessage(f, m, a, b, c) 430 char *f; 431 MAILER *m; 432 { 433 (void) sprintf(SmtpMsgBuffer, f, a, b, c); 434 if (tTd(18, 1) || (Verbose && !HoldErrs)) 435 nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer); 436 if (SmtpOut != NULL) 437 fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, m->m_eol); 438 } 439 440 # endif SMTP 441