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