1 # include <ctype.h> 2 # include <sysexits.h> 3 # include "sendmail.h" 4 5 # ifndef SMTP 6 SCCSID(@(#)usersmtp.c 3.35 01/05/83 (no SMTP)); 7 # else SMTP 8 9 SCCSID(@(#)usersmtp.c 3.35 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 if (SmtpClosing) 246 return; 247 } 248 249 /* now actually close the connection */ 250 (void) fclose(SmtpIn); 251 (void) fclose(SmtpOut); 252 SmtpIn = SmtpOut = NULL; 253 254 /* and pick up the zombie */ 255 i = endmailer(SmtpPid, name); 256 if (i != EX_OK) 257 syserr("smtpquit %s: stat %d", name, i); 258 } 259 /* 260 ** REPLY -- read arpanet reply 261 ** 262 ** Parameters: 263 ** none. 264 ** 265 ** Returns: 266 ** reply code it reads. 267 ** 268 ** Side Effects: 269 ** flushes the mail file. 270 */ 271 272 reply() 273 { 274 (void) fflush(SmtpOut); 275 276 if (tTd(18, 1)) 277 printf("reply\n"); 278 279 /* 280 ** Read the input line, being careful not to hang. 281 */ 282 283 for (;;) 284 { 285 register int r; 286 register char *p; 287 288 /* actually do the read */ 289 if (CurEnv->e_xfp != NULL) 290 (void) fflush(CurEnv->e_xfp); /* for debugging */ 291 292 /* if we are in the process of closing just give the code */ 293 if (SmtpClosing) 294 return (SMTPCLOSING); 295 296 /* get the line from the other side */ 297 p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn); 298 if (p == NULL) 299 { 300 extern char MsgBuf[]; /* err.c */ 301 extern char Arpa_TSyserr[]; /* conf.c */ 302 303 message(Arpa_TSyserr, "reply: read error"); 304 # ifdef LOG 305 syslog(LOG_ERR, "%s", &MsgBuf[4]); 306 # endif LOG 307 SmtpClosing = TRUE; 308 smtpquit("reply error"); 309 return (-1); 310 } 311 fixcrlf(SmtpReplyBuffer, TRUE); 312 313 /* log the input in the transcript for future error returns */ 314 if (Verbose && !HoldErrs) 315 nmessage(Arpa_Info, "%s", SmtpReplyBuffer); 316 else if (CurEnv->e_xfp != NULL) 317 fprintf(CurEnv->e_xfp, "%s\n", SmtpReplyBuffer); 318 319 /* if continuation is required, we can go on */ 320 if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0])) 321 continue; 322 323 /* decode the reply code */ 324 r = atoi(SmtpReplyBuffer); 325 326 /* extra semantics: 0xx codes are "informational" */ 327 if (r < 100) 328 continue; 329 330 /* reply code 421 is "Service Shutting Down" */ 331 if (r == SMTPCLOSING) 332 { 333 /* send the quit protocol */ 334 smtpquit("SMTP Shutdown"); 335 SmtpClosing = TRUE; 336 } 337 338 return (r); 339 } 340 } 341 /* 342 ** SMTPMESSAGE -- send message to server 343 ** 344 ** Parameters: 345 ** f -- format 346 ** a, b, c -- parameters 347 ** 348 ** Returns: 349 ** none. 350 ** 351 ** Side Effects: 352 ** writes message to SmtpOut. 353 */ 354 355 /*VARARGS1*/ 356 smtpmessage(f, a, b, c) 357 char *f; 358 { 359 char buf[100]; 360 361 (void) sprintf(buf, f, a, b, c); 362 if (tTd(18, 1) || (Verbose && !HoldErrs)) 363 nmessage(Arpa_Info, ">>> %s", buf); 364 else if (CurEnv->e_xfp != NULL) 365 fprintf(CurEnv->e_xfp, ">>> %s\n", buf); 366 if (!SmtpClosing) 367 fprintf(SmtpOut, "%s\r\n", buf); 368 } 369 370 # endif SMTP 371