1 # include <ctype.h> 2 # include <sysexits.h> 3 # include "sendmail.h" 4 5 # ifndef SMTP 6 SCCSID(@(#)usersmtp.c 3.29 11/28/82 (no SMTP)); 7 # else SMTP 8 9 SCCSID(@(#)usersmtp.c 3.29 11/28/82); 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 static char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */ 24 static FILE *SmtpOut; /* output file */ 25 static FILE *SmtpIn; /* input file */ 26 static int SmtpPid; /* pid of mailer */ 27 static 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); 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 (Xscript != NULL) 282 (void) fflush(Xscript); /* for debugging */ 283 if (!SmtpClosing) 284 { 285 p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn); 286 if (p == NULL) 287 return (-1); 288 fixcrlf(SmtpReplyBuffer, TRUE); 289 } 290 291 /* log the input in the transcript for future error returns */ 292 if (Verbose && !HoldErrs) 293 nmessage(Arpa_Info, "%s", SmtpReplyBuffer); 294 if (Xscript != NULL) 295 fprintf(Xscript, "%s\n", SmtpReplyBuffer); 296 297 /* if continuation is required, we can go on */ 298 if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0])) 299 continue; 300 301 /* decode the reply code */ 302 r = atoi(SmtpReplyBuffer); 303 304 /* extra semantics: 0xx codes are "informational" */ 305 if (r < 100) 306 continue; 307 308 /* reply code 421 is "Service Shutting Down" */ 309 if (r == SMTPCLOSING && !SmtpClosing) 310 { 311 smtpquit("SMTP Shutdown"); 312 SmtpClosing = TRUE; 313 } 314 315 return (r); 316 } 317 } 318 /* 319 ** SMTPMESSAGE -- send message to server 320 ** 321 ** Parameters: 322 ** f -- format 323 ** a, b, c -- parameters 324 ** 325 ** Returns: 326 ** none. 327 ** 328 ** Side Effects: 329 ** writes message to SmtpOut. 330 */ 331 332 /*VARARGS1*/ 333 smtpmessage(f, a, b, c) 334 char *f; 335 { 336 char buf[100]; 337 338 (void) sprintf(buf, f, a, b, c); 339 if (tTd(18, 1) || (Verbose && !HoldErrs)) 340 nmessage(Arpa_Info, ">>> %s", buf); 341 if (Xscript != NULL) 342 fprintf(Xscript, ">>> %s\n", buf); 343 if (!SmtpClosing) 344 fprintf(SmtpOut, "%s\r\n", buf); 345 } 346 347 # endif SMTP 348