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