+ /*
+ * 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;
+ }
+
+ if ((smtp_err = SMTP_from(ctl->smtp_socket, ctl->smtphostmode,
+ ap, options)) == SM_UNRECOVERABLE)
+ {
+ smtp_close(ctl, 0);
+ return(PS_TRANSIENT);
+ }
+ if (smtp_err != SM_OK)
+ {
+ int err = handle_smtp_report(ctl, msg); /* map to PS_TRANSIENT or PS_REFUSED */
+
+ smtp_rset(ctl); /* stay on the safe side */
+ return(err);
+ }
+
+ /*
+ * Now list the recipient addressees
+ */
+ total_addresses = 0;
+ for (idp = msg->recipients; idp; idp = idp->next)
+ total_addresses++;
+#ifdef EXPLICIT_BOUNCE_ON_BAD_ADDRESS
+ from_responses = (char **)xmalloc(sizeof(char *) * total_addresses);
+#endif /* EXPLICIT_BOUNCE_ON_BAD_ADDRESS */
+ for (idp = msg->recipients; idp; idp = idp->next)
+ if (idp->val.status.mark == XMIT_ACCEPT)
+ {
+ const char *address;
+ address = rcpt_address (ctl, idp->id, 1);
+ if ((smtp_err = SMTP_rcpt(ctl->smtp_socket, ctl->smtphostmode,
+ address)) == SM_UNRECOVERABLE)
+ {
+ smtp_close(ctl, 0);
+transient:
+#ifdef EXPLICIT_BOUNCE_ON_BAD_ADDRESS
+ while (*bad_addresses)
+ free(from_responses[--*bad_addresses]);
+ free(from_responses);
+#endif /* EXPLICIT_BOUNCE_ON_BAD_ADDRESS */
+ return(PS_TRANSIENT);
+ }
+ if (smtp_err == SM_OK)
+ (*good_addresses)++;
+ else
+ {
+ switch (handle_smtp_report_without_bounce(ctl, msg))