+ for (sp = tp = buf; sp < last; sp++)
+ if (*sp != '\r')
+ *tp++ = *sp;
+ *tp = '\0';
+ last = tp;
+ }
+
+ n = 0;
+ if (ctl->mda || ctl->bsmtp) {
+ n = fwrite(buf, 1, last - buf, sinkfp);
+ if (ferror(sinkfp)) n = -1;
+ } else if (ctl->smtp_socket != -1)
+ n = SockWrite(ctl->smtp_socket, buf, last - buf);
+
+ phase = oldphase;
+
+ return(n);
+}
+
+static int open_bsmtp_sink(struct query *ctl, struct msgblk *msg,
+ int *good_addresses, int *bad_addresses)
+/* open a BSMTP stream */
+{
+ struct idlist *idp;
+ int need_anglebrs;
+
+ if (strcmp(ctl->bsmtp, "-") == 0)
+ sinkfp = stdout;
+ else
+ sinkfp = fopen(ctl->bsmtp, "a");
+
+ if (!sinkfp || ferror(sinkfp)) {
+ report(stderr, GT_("BSMTP file open failed: %s\n"),
+ strerror(errno));
+ return(PS_BSMTP);
+ }
+
+ /* see the ap computation under the SMTP branch */
+ need_anglebrs = (msg->return_path[0] != '<');
+ fprintf(sinkfp,
+ "MAIL FROM:%s%s%s",
+ need_anglebrs ? "<" : "",
+ (msg->return_path[0]) ? msg->return_path : user,
+ need_anglebrs ? ">" : "");
+
+ if (ctl->pass8bits || (ctl->mimemsg & MSG_IS_8BIT))
+ fputs(" BODY=8BITMIME", sinkfp);
+ else if (ctl->mimemsg & MSG_IS_7BIT)
+ fputs(" BODY=7BIT", sinkfp);
+
+ /* exim's BSMTP processor does not handle SIZE */
+ /* fprintf(sinkfp, " SIZE=%d", msg->reallen); */
+
+ fprintf(sinkfp, "\r\n");
+
+ /*
+ * RFC 1123 requires that the domain name part of the
+ * RCPT TO address be "canonicalized", that is a FQDN
+ * or MX but not a CNAME. Some listeners (like exim)
+ * enforce this. Now that we have the actual hostname,
+ * compute what we should canonicalize with.
+ */
+ xfree(ctl->destaddr);
+ ctl->destaddr = xstrdup(ctl->smtpaddress ? ctl->smtpaddress : "localhost");
+
+ *bad_addresses = 0;
+ for (idp = msg->recipients; idp; idp = idp->next)
+ if (idp->val.status.mark == XMIT_ACCEPT)
+ {
+ fprintf(sinkfp, "RCPT TO:<%s>\r\n",
+ rcpt_address (ctl, idp->id, 1));
+ (*good_addresses)++;
+ }
+
+ fputs("DATA\r\n", sinkfp);
+
+ if (fflush(sinkfp) || ferror(sinkfp))
+ {
+ report(stderr, GT_("BSMTP preamble write failed: %s.\n"), strerror(errno));
+ return(PS_BSMTP);
+ }
+
+ return(PS_SUCCESS);
+}
+
+/* this is experimental and will be removed if double bounces are reported */
+#define EXPLICIT_BOUNCE_ON_BAD_ADDRESS
+
+
+static const char *is_quad(const char *q)
+/* Check if the string passed in points to what could be one quad of a
+ * dotted-quad IP address. Requirements are that the string is not a
+ * NULL pointer, begins with a period (which is skipped) or a digit
+ * and ends with a period or a NULL. If these requirements are met, a
+ * pointer to the last character (the period or the NULL character) is
+ * returned; otherwise NULL.
+ */
+{
+ const char *r;
+
+ if (!q || !*q)
+ return NULL;
+ if (*q == '.')
+ q++;
+ for(r=q;isdigit((unsigned char)*r);r++)
+ ;
+ if ( ((*r) && (*r != '.')) || ((r-q) < 1) || ((r-q)>3) )
+ return NULL;
+ /* Make sure quad is < 255 */
+ if ( (r-q) == 3)
+ {
+ if (*q > '2')
+ return NULL;
+ else if (*q == '2')
+ {
+ if (*(q+1) > '5')
+ return NULL;
+ else if (*(q+1) == '5')
+ {
+ if (*(q+2) > '5')
+ return NULL;
+ }
+ }
+ }
+ return r;
+}
+
+static int is_dottedquad(const char *hostname)
+/* Returns a true value if the passed in string looks like an IP
+ * address in dotted-quad form, and a false value otherwise.
+ */
+
+{
+ return ((hostname=is_quad(is_quad(is_quad(is_quad(hostname))))) != NULL) &&
+ (*hostname == '\0');
+}
+
+static int open_smtp_sink(struct query *ctl, struct msgblk *msg,
+ int *good_addresses, int *bad_addresses /* this must be signed, to prevent endless loop in from_addresses */)
+/* open an SMTP stream */
+{
+ const char *ap;
+ struct idlist *idp;
+ char options[MSGBUFSIZE];
+ char addr[HOSTLEN+USERNAMELEN+1];
+#ifdef EXPLICIT_BOUNCE_ON_BAD_ADDRESS
+ char **from_responses;
+#endif /* EXPLICIT_BOUNCE_ON_BAD_ADDRESS */
+ int total_addresses;
+ int force_transient_error = 0;
+ int smtp_err;
+
+ /*
+ * Compute ESMTP options.
+ */
+ options[0] = '\0';
+ if (ctl->server.esmtp_options & ESMTP_8BITMIME) {
+ if (ctl->pass8bits || (ctl->mimemsg & MSG_IS_8BIT))
+ strcpy(options, " BODY=8BITMIME");
+ else if (ctl->mimemsg & MSG_IS_7BIT)
+ strcpy(options, " BODY=7BIT");
+ }
+
+ if ((ctl->server.esmtp_options & ESMTP_SIZE) && msg->reallen > 0)
+ sprintf(options + strlen(options), " SIZE=%d", msg->reallen);
+
+ /*
+ * Try to get the SMTP listener to take the Return-Path
+ * address as MAIL FROM. If it won't, fall back on the
+ * remotename and mailserver host. This won't affect replies,
+ * which use the header From address anyway; the MAIL FROM
+ * address is a place for the SMTP listener to send
+ * bouncemail. The point is to guarantee a FQDN in the MAIL
+ * FROM line -- some SMTP listeners, like smail, become
+ * unhappy otherwise.
+ *
+ * RFC 1123 requires that the domain name part of the
+ * MAIL FROM address be "canonicalized", that is a
+ * FQDN or MX but not a CNAME. We'll assume the Return-Path
+ * header is already in this form here (it certainly
+ * is if rewrite is on). RFC 1123 is silent on whether
+ * a nonexistent hostname part is considered canonical.
+ *
+ * This is a potential problem if the MTAs further upstream
+ * didn't pass canonicalized From/Return-Path lines, *and* the
+ * local SMTP listener insists on them.
+ *
+ * Handle the case where an upstream MTA is setting a return
+ * path equal to "@". Ghod knows why anyone does this, but
+ * it's been reported to happen in mail from Amazon.com and
+ * Motorola.
+ *
+ * Also, if the hostname is a dotted quad, wrap it in square brackets.
+ * Apparently this is required by RFC2821, section 4.1.3.
+ */
+ if (!msg->return_path[0] || (msg->return_path[0] == '@'))
+ {
+ if (strchr(ctl->remotename,'@') || strchr(ctl->remotename,'!'))
+ {
+ snprintf(addr, sizeof(addr), "%s", ctl->remotename);
+ }
+ else if (is_dottedquad(ctl->server.truename))
+ {
+ snprintf(addr, sizeof(addr), "%s@[%s]", ctl->remotename,
+ ctl->server.truename);
+ }
+ else
+ {
+ snprintf(addr, sizeof(addr),
+ "%s@%s", ctl->remotename, ctl->server.truename);
+ }
+ ap = addr;
+ }
+ else if (strchr(msg->return_path,'@') || strchr(msg->return_path,'!'))
+ ap = msg->return_path;
+ /* in case Return-Path was "<>" we want to preserve that */
+ else if (strcmp(msg->return_path,"<>") == 0)
+ ap = msg->return_path;
+ else /* in case Return-Path existed but was local */
+ {
+ if (is_dottedquad(ctl->server.truename))
+ {
+ snprintf(addr, sizeof(addr), "%s@[%s]", msg->return_path,
+ ctl->server.truename);
+ }
+ else
+ {
+ snprintf(addr, sizeof(addr), "%s@%s",
+ msg->return_path, ctl->server.truename);
+ }
+ ap = addr;
+ }