-/* $Cambridge: exim/src/src/transports/smtp.c,v 1.22 2006/02/23 12:41:23 ph10 Exp $ */
+/* $Cambridge: exim/src/src/transports/smtp.c,v 1.26 2006/09/25 11:25:37 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
(void *)offsetof(smtp_transport_options_block, allow_localhost) },
{ "authenticated_sender", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, authenticated_sender) },
+ { "authenticated_sender_force", opt_bool,
+ (void *)offsetof(smtp_transport_options_block, authenticated_sender_force) },
{ "command_timeout", opt_time,
(void *)offsetof(smtp_transport_options_block, command_timeout) },
{ "connect_timeout", opt_time,
5, /* hosts_max_try */
50, /* hosts_max_try_hardlimit */
FALSE, /* allow_localhost */
+ FALSE, /* authenticated_sender_force */
FALSE, /* gethostbyname */
TRUE, /* dns_qualify_single */
FALSE, /* dns_search_parents */
but before running it in a sub-process. It is used for two things:
(1) To set the fallback host list in addresses, when delivering.
- (2) To pass back the interface, port, and protocol options, for use during
- callout verification.
+ (2) To pass back the interface, port, protocol, and other options, for use
+ during callout verification.
Arguments:
tblock pointer to the transport instance block
tf->gethostbyname = ob->gethostbyname;
tf->qualify_single = ob->dns_qualify_single;
tf->search_parents = ob->dns_search_parents;
+ tf->helo_data = ob->helo_data;
}
/* Set the fallback host list for all the addresses that don't have fallback
if (errno == 0 && buffer[0] != 0)
{
uschar flushbuffer[4096];
+ int save_errno = 0;
+ if (buffer[0] == '4')
+ {
+ save_errno = ERRNO_MAIL4XX;
+ addr->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8;
+ }
while (count-- > 0)
{
if (!smtp_read_response(inblock, flushbuffer, sizeof(flushbuffer),
&& (errno != 0 || flushbuffer[0] == 0))
break;
}
+ errno = save_errno;
}
return -3;
}
else
{
- int bincode = (buffer[1] - '0')*10 + buffer[2] - '0';
-
addr->transport_return = DEFER;
addr->basic_errno = ERRNO_RCPT4XX;
- addr->more_errno |= bincode << 8;
+ addr->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8;
/* Log temporary errors if there are more hosts to be tried. */
int code;
uschar *msg;
BOOL pass_message;
- if (pending_DATA > 0 || (yield & 1) != 0) return -3;
+ if (pending_DATA > 0 || (yield & 1) != 0)
+ {
+ if (errno == 0 && buffer[0] == '4')
+ {
+ errno = ERRNO_DATA4XX;
+ addrlist->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8;
+ }
+ return -3;
+ }
(void)check_response(host, &errno, 0, buffer, &code, &msg, &pass_message);
DEBUG(D_transport) debug_printf("%s\nerror for DATA ignored: pipelining "
"is in use and there were no good recipients\n", msg);
/* Add the authenticated sender address if present */
-if (smtp_authenticated && local_authenticated_sender != NULL)
+if ((smtp_authenticated || ob->authenticated_sender_force) &&
+ local_authenticated_sender != NULL)
{
string_format(p, sizeof(buffer) - (p-buffer), " AUTH=%s",
auth_xtextencode(local_authenticated_sender,
case +1: /* Block was sent */
if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
- ob->command_timeout)) goto RESPONSE_FAILED;
+ ob->command_timeout))
+ {
+ if (errno == 0 && buffer[0] == '4')
+ {
+ errno = ERRNO_MAIL4XX;
+ addrlist->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8;
+ }
+ goto RESPONSE_FAILED;
+ }
pending_MAIL = FALSE;
break;
}
/* For SMTP, we now read a single response that applies to the whole message.
If it is OK, then all the addresses have been delivered. */
- if (!lmtp) ok = smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
- ob->final_timeout);
+ if (!lmtp)
+ {
+ ok = smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
+ ob->final_timeout);
+ if (!ok && errno == 0 && buffer[0] == '4')
+ {
+ errno = ERRNO_DATA4XX;
+ addrlist->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8;
+ }
+ }
/* For LMTP, we get back a response for every RCPT command that we sent;
some may be accepted and some rejected. For those that get a response, their
/* LMTP - if the response fails badly (e.g. timeout), use it for all the
remaining addresses. Otherwise, it's a return code for just the one
- address. */
+ address. For temporary errors, add a retry item for the address so that
+ it doesn't get tried again too soon. */
if (lmtp)
{
if (errno != 0 || buffer[0] == 0) goto RESPONSE_FAILED;
addr->message = string_sprintf("LMTP error after %s: %s",
big_buffer, string_printing(buffer));
- addr->transport_return = (buffer[0] == '5')? FAIL : DEFER;
+ setflag(addr, af_pass_message); /* Allow message to go to user */
+ if (buffer[0] == '5')
+ addr->transport_return = FAIL;
+ else
+ {
+ errno = ERRNO_DATA4XX;
+ addr->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8;
+ addr->transport_return = DEFER;
+ retry_add_item(addr, addr->address_retry_key, 0);
+ }
continue;
}
completed_address = TRUE; /* NOW we can set this flag */
}
}
- /* If there was an I/O error or timeout or other transportation error,
- indicated by errno being non-zero, defer all addresses and yield DEFER,
- except for the case of failed add_headers expansion, or a transport filter
- failure, when the yield should be ERROR, to stop it trying other hosts.
-
- However, handle timeouts after MAIL FROM or "." and loss of connection after
+ /* We want to handle timeouts after MAIL or "." and loss of connection after
"." specially. They can indicate a problem with the sender address or with
- the contents of the message rather than a real error on the connection.
- Therefore, treat these cases in the same way as a 4xx response.
-
- The following condition tests for NOT these special cases. */
+ the contents of the message rather than a real error on the connection. These
+ cases are treated in the same way as a 4xx response. This next bit of code
+ does the classification. */
- else if (save_errno != 0 &&
- (save_errno != ETIMEDOUT ||
- (Ustrncmp(smtp_command,"MAIL",4) != 0 &&
- Ustrncmp(smtp_command,"end ",4) != 0)) &&
- (save_errno != ERRNO_SMTPCLOSED ||
- Ustrncmp(smtp_command,"end ",4) != 0))
+ else
{
- yield = (save_errno == ERRNO_CHHEADER_FAIL ||
- save_errno == ERRNO_FILTER_FAIL)? ERROR : DEFER;
- set_errno(addrlist, save_errno, message, DEFER, pass_message);
- }
+ BOOL message_error;
- /* Otherwise we have a message-specific error response from the remote
- host. This is one of
- (a) negative response or timeout after "mail from"
- (b) negative response after "data"
- (c) negative response or timeout or dropped connection after "."
- It won't be a negative response or timeout after "rcpt to", as that is dealt
- with separately above. The action in all cases is to set an appropriate
- error code for all the addresses, but to leave yield set to OK because
- the host itself has not failed. [It might in practice have failed for a
- timeout after MAIL FROM, or "." but if so, we'll discover that at the next
- delivery attempt.] For a temporary error, set the message_defer flag, and
- write to the logs for information if this is not the last host. The error for
- the last host will be logged as part of the address's log line. */
+ switch(save_errno)
+ {
+ case 0:
+ case ERRNO_MAIL4XX:
+ case ERRNO_DATA4XX:
+ message_error = TRUE;
+ break;
- else
- {
- if (mua_wrapper) code = '5'; /* Force hard failure in wrapper mode */
+ case ETIMEDOUT:
+ message_error = Ustrncmp(smtp_command,"MAIL",4) == 0 ||
+ Ustrncmp(smtp_command,"end ",4) == 0;
+ break;
+
+ case ERRNO_SMTPCLOSED:
+ message_error = Ustrncmp(smtp_command,"end ",4) == 0;
+ break;
+
+ default:
+ message_error = FALSE;
+ break;
+ }
- set_errno(addrlist, save_errno, message, (code == '5')? FAIL : DEFER,
- pass_message);
+ /* Handle the cases that are treated as message errors. These are:
- /* If there's an errno, the message contains just the identity of
- the host. */
+ (a) negative response or timeout after MAIL
+ (b) negative response after DATA
+ (c) negative response or timeout or dropped connection after "."
- if (code != '5') /* Anything other than 5 is treated as temporary */
+ It won't be a negative response or timeout after RCPT, as that is dealt
+ with separately above. The action in all cases is to set an appropriate
+ error code for all the addresses, but to leave yield set to OK because the
+ host itself has not failed. Of course, it might in practice have failed
+ when we've had a timeout, but if so, we'll discover that at the next
+ delivery attempt. For a temporary error, set the message_defer flag, and
+ write to the logs for information if this is not the last host. The error
+ for the last host will be logged as part of the address's log line. */
+
+ if (message_error)
{
- if (save_errno > 0)
- message = US string_sprintf("%s: %s", message, strerror(save_errno));
- if (host->next != NULL) log_write(0, LOG_MAIN, "%s", message);
- deliver_msglog("%s %s\n", tod_stamp(tod_log), message);
- *message_defer = TRUE;
+ if (mua_wrapper) code = '5'; /* Force hard failure in wrapper mode */
+ set_errno(addrlist, save_errno, message, (code == '5')? FAIL : DEFER,
+ pass_message);
+
+ /* If there's an errno, the message contains just the identity of
+ the host. */
+
+ if (code != '5') /* Anything other than 5 is treated as temporary */
+ {
+ if (save_errno > 0)
+ message = US string_sprintf("%s: %s", message, strerror(save_errno));
+ if (host->next != NULL) log_write(0, LOG_MAIN, "%s", message);
+ deliver_msglog("%s %s\n", tod_stamp(tod_log), message);
+ *message_defer = TRUE;
+ }
+ }
+
+ /* Otherwise, we have an I/O error or a timeout other than after MAIL or
+ ".", or some other transportation error. We defer all addresses and yield
+ DEFER, except for the case of failed add_headers expansion, or a transport
+ filter failure, when the yield should be ERROR, to stop it trying other
+ hosts. */
+
+ else
+ {
+ yield = (save_errno == ERRNO_CHHEADER_FAIL ||
+ save_errno == ERRNO_FILTER_FAIL)? ERROR : DEFER;
+ set_errno(addrlist, save_errno, message, DEFER, pass_message);
}
}
}