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