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