+#ifdef SUPPORT_I18N
+/* If it supports internationalised messages, and this meesage need that,
+request it */
+
+if ( sx->peer_offered & PEER_OFFERED_UTF8
+ && addrlist->prop.utf8_msg
+ && !addrlist->prop.utf8_downcvt
+ )
+ Ustrcpy(p, " SMTPUTF8"), p += 9;
+#endif
+
+/* check if all addresses have DSN-lasthop flag; do not send RET and ENVID if so */
+for (sx->dsn_all_lasthop = TRUE, addr = addrlist, address_count = 0;
+ addr && address_count < sx->max_rcpt;
+ addr = addr->next) if (addr->transport_return == PENDING_DEFER)
+ {
+ address_count++;
+ if (!(addr->dsn_flags & rf_dsnlasthop))
+ {
+ sx->dsn_all_lasthop = FALSE;
+ break;
+ }
+ }
+
+/* Add any DSN flags to the mail command */
+
+if (sx->peer_offered & PEER_OFFERED_DSN && !sx->dsn_all_lasthop)
+ {
+ if (dsn_ret == dsn_ret_hdrs)
+ { Ustrcpy(p, " RET=HDRS"); p += 9; }
+ else if (dsn_ret == dsn_ret_full)
+ { Ustrcpy(p, " RET=FULL"); p += 9; }
+
+ if (dsn_envid)
+ {
+ string_format(p, sizeof(sx->buffer) - (p-sx->buffer), " ENVID=%s", dsn_envid);
+ while (*p) p++;
+ }
+ }
+
+/* If an authenticated_sender override has been specified for this transport
+instance, expand it. If the expansion is forced to fail, and there was already
+an authenticated_sender for this message, the original value will be used.
+Other expansion failures are serious. An empty result is ignored, but there is
+otherwise no check - this feature is expected to be used with LMTP and other
+cases where non-standard addresses (e.g. without domains) might be required. */
+
+if (smtp_mail_auth_str(p, sizeof(sx->buffer) - (p-sx->buffer), addrlist, sx->ob))
+ return ERROR;
+
+return OK;
+}
+
+
+static void
+build_rcptcmd_options(smtp_context * sx, const address_item * addr)
+{
+uschar * p = sx->buffer;
+*p = 0;
+
+/* Add any DSN flags to the rcpt command */
+
+if (sx->peer_offered & PEER_OFFERED_DSN && !(addr->dsn_flags & rf_dsnlasthop))
+ {
+ if (addr->dsn_flags & rf_dsnflags)
+ {
+ int i;
+ BOOL first = TRUE;
+
+ Ustrcpy(p, " NOTIFY=");
+ while (*p) p++;
+ for (i = 0; i < nelem(rf_list); i++) if (addr->dsn_flags & rf_list[i])
+ {
+ if (!first) *p++ = ',';
+ first = FALSE;
+ Ustrcpy(p, rf_names[i]);
+ while (*p) p++;
+ }
+ }
+
+ if (addr->dsn_orcpt)
+ {
+ string_format(p, sizeof(sx->buffer) - (p-sx->buffer), " ORCPT=%s",
+ addr->dsn_orcpt);
+ while (*p) p++;
+ }
+ }
+}
+
+
+
+/*
+Return:
+ 0 good, rcpt results in addr->transport_return (PENDING_OK, DEFER, FAIL)
+ -1 MAIL response error
+ -2 any non-MAIL read i/o error
+ -3 non-MAIL response timeout
+ -4 internal error; channel still usable
+ -5 transmit failed
+ */
+
+int
+smtp_write_mail_and_rcpt_cmds(smtp_context * sx, int * yield)
+{
+address_item * addr;
+int address_count;
+int rc;
+
+if (build_mailcmd_options(sx, sx->first_addr) != OK)
+ {
+ *yield = ERROR;
+ return -4;
+ }
+
+/* From here until we send the DATA command, we can make use of PIPELINING
+if the server host supports it. The code has to be able to check the responses
+at any point, for when the buffer fills up, so we write it totally generally.
+When PIPELINING is off, each command written reports that it has flushed the
+buffer. */
+
+sx->pending_MAIL = TRUE; /* The block starts with MAIL */
+
+ {
+ uschar * s = sx->from_addr;
+#ifdef SUPPORT_I18N
+ uschar * errstr = NULL;
+
+ /* If we must downconvert, do the from-address here. Remember we had to
+ for the to-addresses (done below), and also (ugly) for re-doing when building
+ the delivery log line. */
+
+ if ( sx->addrlist->prop.utf8_msg
+ && (sx->addrlist->prop.utf8_downcvt || !(sx->peer_offered & PEER_OFFERED_UTF8))
+ )
+ {
+ if (s = string_address_utf8_to_alabel(s, &errstr), errstr)
+ {
+ set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE);
+ *yield = ERROR;
+ return -4;
+ }
+ setflag(sx->addrlist, af_utf8_downcvt);
+ }
+#endif
+
+ rc = smtp_write_command(&sx->outblock, pipelining_active,
+ "MAIL FROM:<%s>%s\r\n", s, sx->buffer);
+ }
+
+mail_command = string_copy(big_buffer); /* Save for later error message */
+
+switch(rc)
+ {
+ case -1: /* Transmission error */
+ return -5;
+
+ case +1: /* Cmd was sent */
+ if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer), '2',
+ sx->ob->command_timeout))
+ {
+ if (errno == 0 && sx->buffer[0] == '4')
+ {
+ errno = ERRNO_MAIL4XX;
+ sx->addrlist->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8;
+ }
+ return -1;
+ }
+ sx->pending_MAIL = FALSE;
+ break;
+
+ /* otherwise zero: command queued for pipeline */
+ }
+
+/* Pass over all the relevant recipient addresses for this host, which are the
+ones that have status PENDING_DEFER. If we are using PIPELINING, we can send
+several before we have to read the responses for those seen so far. This
+checking is done by a subroutine because it also needs to be done at the end.
+Send only up to max_rcpt addresses at a time, leaving next_addr pointing to
+the next one if not all are sent.
+
+In the MUA wrapper situation, we want to flush the PIPELINING buffer for the
+last address because we want to abort if any recipients have any kind of
+problem, temporary or permanent. We know that all recipient addresses will have
+the PENDING_DEFER status, because only one attempt is ever made, and we know
+that max_rcpt will be large, so all addresses will be done at once.
+
+For verify we flush the pipeline after any (the only) rcpt address. */
+
+for (addr = sx->first_addr, address_count = 0;
+ addr && address_count < sx->max_rcpt;
+ addr = addr->next) if (addr->transport_return == PENDING_DEFER)
+ {
+ int count;
+ BOOL no_flush;
+ uschar * rcpt_addr;
+
+ addr->dsn_aware = sx->peer_offered & PEER_OFFERED_DSN
+ ? dsn_support_yes : dsn_support_no;
+
+ address_count++;
+ no_flush = pipelining_active && !sx->verify && (!mua_wrapper || addr->next);
+
+ build_rcptcmd_options(sx, addr);
+
+ /* Now send the RCPT command, and process outstanding responses when
+ necessary. After a timeout on RCPT, we just end the function, leaving the
+ yield as OK, because this error can often mean that there is a problem with
+ just one address, so we don't want to delay the host. */
+
+ rcpt_addr = transport_rcpt_address(addr, sx->tblock->rcpt_include_affixes);
+
+#ifdef SUPPORT_I18N
+ if ( testflag(sx->addrlist, af_utf8_downcvt)
+ && !(rcpt_addr = string_address_utf8_to_alabel(rcpt_addr, NULL))
+ )
+ {
+ /*XXX could we use a per-address errstr here? Not fail the whole send? */
+ errno = ERRNO_EXPANDFAIL;
+ return -5; /*XXX too harsh? */
+ }
+#endif
+
+ count = smtp_write_command(&sx->outblock, no_flush, "RCPT TO:<%s>%s%s\r\n",
+ rcpt_addr, sx->igquotstr, sx->buffer);
+
+ if (count < 0) return -5;
+ if (count > 0)
+ {
+ switch(sync_responses(sx, count, 0))
+ {
+ case 3: sx->ok = TRUE; /* 2xx & 5xx => OK & progress made */
+ case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */
+ break;
+
+ case 1: sx->ok = TRUE; /* 2xx (only) => OK, but if LMTP, */
+ if (!sx->lmtp) /* can't tell about progress yet */
+ sx->completed_addr = TRUE;
+ case 0: /* No 2xx or 5xx, but no probs */
+ break;
+
+ case -1: return -3; /* Timeout on RCPT */
+ case -2: return -2; /* non-MAIL read i/o error */
+ default: return -1; /* any MAIL error */
+ }
+ sx->pending_MAIL = FALSE; /* Dealt with MAIL */
+ }
+ } /* Loop for next address */
+
+sx->next_addr = addr;
+return 0;
+}
+
+
+/*************************************************
+* Deliver address list to given host *
+*************************************************/
+
+/* If continue_hostname is not null, we get here only when continuing to
+deliver down an existing channel. The channel was passed as the standard
+input. TLS is never active on a passed channel; the previous process always
+closes it down before passing the connection on.
+
+Otherwise, we have to make a connection to the remote host, and do the
+initial protocol exchange.
+
+When running as an MUA wrapper, if the sender or any recipient is rejected,
+temporarily or permanently, we force failure for all recipients.
+
+Arguments:
+ addrlist chain of potential addresses to deliver; only those whose
+ transport_return field is set to PENDING_DEFER are currently
+ being processed; others should be skipped - they have either
+ been delivered to an earlier host or IP address, or been
+ failed by one of them.
+ host host to deliver to
+ host_af AF_INET or AF_INET6
+ port default TCP/IP port to use, in host byte order
+ interface interface to bind to, or NULL
+ tblock transport instance block
+ message_defer set TRUE if yield is OK, but all addresses were deferred
+ because of a non-recipient, non-host failure, that is, a
+ 4xx response to MAIL FROM, DATA, or ".". This is a defer
+ that is specific to the message.
+ suppress_tls if TRUE, don't attempt a TLS connection - this is set for
+ a second attempt after TLS initialization fails
+
+Returns: OK - the connection was made and the delivery attempted;
+ the result for each address is in its data block.
+ DEFER - the connection could not be made, or something failed
+ while setting up the SMTP session, or there was a
+ non-message-specific error, such as a timeout.
+ ERROR - a filter command is specified for this transport,
+ and there was a problem setting it up; OR helo_data
+ or add_headers or authenticated_sender is specified
+ for this transport, and the string failed to expand
+*/
+
+static int
+smtp_deliver(address_item *addrlist, host_item *host, int host_af, int port,
+ uschar *interface, transport_instance *tblock,
+ BOOL *message_defer, BOOL suppress_tls)
+{
+address_item *addr;
+int yield = OK;
+int save_errno;
+int rc;
+time_t start_delivery_time = time(NULL);
+
+BOOL pass_message = FALSE;
+uschar *message = NULL;
+uschar new_message_id[MESSAGE_ID_LENGTH + 1];
+uschar *p;
+
+smtp_context sx;