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