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