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