* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2009 */
+/* Copyright (c) University of Cambridge 1995 - 2014 */
/* See the file NOTICE for conditions of use and distribution. */
/* The main code for delivering a message. */
static address_item *addr_remote = NULL;
static address_item *addr_route = NULL;
static address_item *addr_succeed = NULL;
+#ifdef EXPERIMENTAL_DSN
+static address_item *addr_dsntmp = NULL;
+static address_item *addr_senddsn = NULL;
+#endif
static FILE *message_log = NULL;
static BOOL update_spool;
+static uschar *
+d_hostlog(uschar * s, int * sizep, int * ptrp, address_item * addr)
+{
+ s = string_append(s, sizep, ptrp, 5, US" H=", addr->host_used->name,
+ US" [", addr->host_used->address, US"]");
+ if ((log_extra_selector & LX_outgoing_port) != 0)
+ s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d",
+ addr->host_used->port));
+ return s;
+}
+
+#ifdef SUPPORT_TLS
+static uschar *
+d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr)
+{
+ if ((log_extra_selector & LX_tls_cipher) != 0 && addr->cipher != NULL)
+ s = string_append(s, sizep, ptrp, 2, US" X=", addr->cipher);
+ if ((log_extra_selector & LX_tls_certificate_verified) != 0 &&
+ addr->cipher != NULL)
+ s = string_append(s, sizep, ptrp, 2, US" CV=",
+ testflag(addr, af_cert_verified)? "yes":"no");
+ if ((log_extra_selector & LX_tls_peerdn) != 0 && addr->peerdn != NULL)
+ s = string_append(s, sizep, ptrp, 3, US" DN=\"",
+ string_printing(addr->peerdn), US"\"");
+ return s;
+}
+#endif
+
/* If msg is NULL this is a delivery log and logchar is used. Otherwise
-this is a nonstandard call; no two-characher delivery flag is written
+this is a nonstandard call; no two-character delivery flag is written
but sender-host and sender are prefixed and "msg" is inserted in the log line.
Arguments:
have a pointer to the host item that succeeded; local deliveries can have a
pointer to a single host item in their host list, for use by the transport. */
+#ifdef EXPERIMENTAL_TPDA
+ tpda_delivery_ip = NULL; /* presume no successful remote delivery */
+ tpda_delivery_port = 0;
+ tpda_delivery_fqdn = NULL;
+ tpda_delivery_local_part = NULL;
+ tpda_delivery_domain = NULL;
+ tpda_delivery_confirmation = NULL;
+ lookup_dnssec_authenticated = NULL;
+#endif
+
s = reset_point = store_get(size);
log_address = string_log_address(addr, (log_write_selector & L_all_parents) != 0, TRUE);
if (addr->transport->info->local)
{
if (addr->host_list != NULL)
+ {
s = string_append(s, &size, &ptr, 2, US" H=", addr->host_list->name);
+ #ifdef EXPERIMENTAL_TPDA
+ tpda_delivery_fqdn = addr->host_list->name;
+ #endif
+ }
if (addr->shadow_message != NULL)
s = string_cat(s, &size, &ptr, addr->shadow_message,
Ustrlen(addr->shadow_message));
else
{
- if (addr->host_used != NULL)
+ if (addr->host_used)
{
- s = string_append(s, &size, &ptr, 5, US" H=", addr->host_used->name,
- US" [", addr->host_used->address, US"]");
- if ((log_extra_selector & LX_outgoing_port) != 0)
- s = string_append(s, &size, &ptr, 2, US":", string_sprintf("%d",
- addr->host_used->port));
+ s = d_hostlog(s, &size, &ptr, addr);
if (continue_sequence > 1)
s = string_cat(s, &size, &ptr, US"*", 1);
+
+ #ifdef EXPERIMENTAL_TPDA
+ tpda_delivery_ip = addr->host_used->address;
+ tpda_delivery_port = addr->host_used->port;
+ tpda_delivery_fqdn = addr->host_used->name;
+ tpda_delivery_local_part = addr->local_part;
+ tpda_delivery_domain = addr->domain;
+ tpda_delivery_confirmation = addr->message;
+
+ /* DNS lookup status */
+ lookup_dnssec_authenticated = addr->host_used->dnssec==DS_YES ? US"yes"
+ : addr->host_used->dnssec==DS_NO ? US"no"
+ : NULL;
+ #endif
}
#ifdef SUPPORT_TLS
- if ((log_extra_selector & LX_tls_cipher) != 0 && addr->cipher != NULL)
- s = string_append(s, &size, &ptr, 2, US" X=", addr->cipher);
- if ((log_extra_selector & LX_tls_certificate_verified) != 0 &&
- addr->cipher != NULL)
- s = string_append(s, &size, &ptr, 2, US" CV=",
- testflag(addr, af_cert_verified)? "yes":"no");
- if ((log_extra_selector & LX_tls_peerdn) != 0 && addr->peerdn != NULL)
- s = string_append(s, &size, &ptr, 3, US" DN=\"",
- string_printing(addr->peerdn), US"\"");
+ s = d_tlslog(s, &size, &ptr, addr);
#endif
if (addr->authenticator)
}
}
- if ((log_extra_selector & LX_smtp_confirmation) != 0 &&
- addr->message != NULL)
- {
- int i;
- uschar *p = big_buffer;
- uschar *ss = addr->message;
- *p++ = '\"';
- for (i = 0; i < 100 && ss[i] != 0; i++)
- {
- if (ss[i] == '\"' || ss[i] == '\\') *p++ = '\\';
- *p++ = ss[i];
- }
- *p++ = '\"';
- *p = 0;
- s = string_append(s, &size, &ptr, 2, US" C=", big_buffer);
- }
+ #ifndef DISABLE_PRDR
+ if (addr->flags & af_prdr_used)
+ s = string_append(s, &size, &ptr, 1, US" PRDR");
+ #endif
+ }
+
+/* confirmation message (SMTP (host_used) and LMTP (driver_name)) */
+
+if (log_extra_selector & LX_smtp_confirmation &&
+ addr->message &&
+ (addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0))
+ {
+ int i;
+ uschar *p = big_buffer;
+ uschar *ss = addr->message;
+ *p++ = '\"';
+ for (i = 0; i < 256 && ss[i] != 0; i++) /* limit logged amount */
+ {
+ if (ss[i] == '\"' || ss[i] == '\\') *p++ = '\\'; /* quote \ and " */
+ *p++ = ss[i];
+ }
+ *p++ = '\"';
+ *p = 0;
+ s = string_append(s, &size, &ptr, 2, US" C=", big_buffer);
}
/* Time on queue and actual time taken to deliver */
if ((log_extra_selector & LX_queue_time) != 0)
- {
s = string_append(s, &size, &ptr, 2, US" QT=",
- readconf_printtime(time(NULL) - received_time));
- }
+ readconf_printtime( (int) ((long)time(NULL) - (long)received_time)) );
if ((log_extra_selector & LX_deliver_time) != 0)
- {
s = string_append(s, &size, &ptr, 2, US" DT=",
readconf_printtime(addr->more_errno));
- }
/* string_cat() always leaves room for the terminator. Release the
store we used to build the line after writing it. */
s[ptr] = 0;
log_write(0, flags, "%s", s);
+
+#ifdef EXPERIMENTAL_TPDA
+if (addr->transport->tpda_delivery_action)
+ {
+ DEBUG(D_deliver)
+ debug_printf(" TPDA(Delivery): tpda_deliver_action=|%s| tpda_delivery_IP=%s\n",
+ addr->transport->tpda_delivery_action, tpda_delivery_ip);
+
+ router_name = addr->router->name;
+ transport_name = addr->transport->name;
+ if (!expand_string(addr->transport->tpda_delivery_action) && *expand_string_message)
+ log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand tpda_deliver_action in %s: %s\n",
+ transport_name, expand_string_message);
+ router_name = NULL;
+ transport_name = NULL;
+ }
+#endif
store_reset(reset_point);
return;
}
if (((Ustrstr(addr->message, "failed to expand") != NULL) || (Ustrstr(addr->message, "expansion of ") != NULL)) &&
(Ustrstr(addr->message, "mysql") != NULL ||
Ustrstr(addr->message, "pgsql") != NULL ||
+#ifdef EXPERIMENTAL_REDIS
+ Ustrstr(addr->message, "redis") != NULL ||
+#endif
Ustrstr(addr->message, "sqlite") != NULL ||
Ustrstr(addr->message, "ldap:") != NULL ||
Ustrstr(addr->message, "ldapdn:") != NULL ||
(void)close(addr->return_file);
}
-/* The sucess case happens only after delivery by a transport. */
+/* The success case happens only after delivery by a transport. */
if (result == OK)
{
DEBUG(D_deliver) debug_printf("%s delivered\n", addr->address);
if (addr->parent == NULL)
- {
deliver_msglog("%s %s: %s%s succeeded\n", now, addr->address,
driver_name, driver_kind);
- }
else
{
deliver_msglog("%s %s <%s>: %s%s succeeded\n", now, addr->address,
child_done(addr, now);
}
+ /* Certificates for logging (via TPDA) */
+ #ifdef SUPPORT_TLS
+ tls_out.ourcert = addr->ourcert;
+ addr->ourcert = NULL;
+ tls_out.peercert = addr->peercert;
+ addr->peercert = NULL;
+
+ tls_out.cipher = addr->cipher;
+ tls_out.peerdn = addr->peerdn;
+ tls_out.ocsp = addr->ocsp;
+ #endif
+
delivery_log(LOG_MAIN, addr, logchar, NULL);
+
+ #ifdef SUPPORT_TLS
+ if (tls_out.ourcert)
+ {
+ tls_free_cert(tls_out.ourcert);
+ tls_out.ourcert = NULL;
+ }
+ if (tls_out.peercert)
+ {
+ tls_free_cert(tls_out.peercert);
+ tls_out.peercert = NULL;
+ }
+ tls_out.cipher = NULL;
+ tls_out.peerdn = NULL;
+ tls_out.ocsp = OCSP_NOT_REQ;
+ #endif
}
if (used_return_path != NULL &&
(log_extra_selector & LX_return_path_on_delivery) != 0)
- {
s = string_append(s, &size, &ptr, 3, US" P=<", used_return_path, US">");
- }
if (addr->router != NULL)
s = string_append(s, &size, &ptr, 2, US" R=", addr->router->name);
s = string_append(s, &size, &ptr, 2, US" T=", addr->transport->name);
if (addr->host_used != NULL)
- s = string_append(s, &size, &ptr, 5, US" H=", addr->host_used->name,
- US" [", addr->host_used->address, US"]");
+ s = d_hostlog(s, &size, &ptr, addr);
+
+ #ifdef SUPPORT_TLS
+ s = d_tlslog(s, &size, &ptr, addr);
+ #endif
if (addr->basic_errno > 0)
s = string_append(s, &size, &ptr, 2, US": ",
#ifdef SUPPORT_TLS
case 'X':
- if (addr == NULL) goto ADDR_MISMATCH; /* Below, in 'A' handler */
- addr->cipher = (*ptr)? string_copy(ptr) : NULL;
- while (*ptr++);
- addr->peerdn = (*ptr)? string_copy(ptr) : NULL;
+ if (addr == NULL) goto ADDR_MISMATCH; /* Below, in 'A' handler */
+ switch (*ptr++)
+ {
+ case '1':
+ addr->cipher = NULL;
+ addr->peerdn = NULL;
+
+ if (*ptr)
+ addr->cipher = string_copy(ptr);
+ while (*ptr++);
+ if (*ptr)
+ addr->peerdn = string_copy(ptr);
+ break;
+
+ case '2':
+ addr->peercert = NULL;
+ if (*ptr)
+ (void) tls_import_cert(ptr, &addr->peercert);
+ break;
+
+ case '3':
+ addr->ourcert = NULL;
+ if (*ptr)
+ (void) tls_import_cert(ptr, &addr->ourcert);
+ break;
+
+ #ifndef DISABLE_OCSP
+ case '4':
+ addr->ocsp = OCSP_NOT_REQ;
+ if (*ptr)
+ addr->ocsp = *ptr - '0';
+ break;
+ #endif
+ }
while (*ptr++);
break;
- #endif
+ #endif /*SUPPORT_TLS*/
case 'C': /* client authenticator information */
switch (*ptr++)
- {
- case '1':
- addr->authenticator = (*ptr)? string_copy(ptr) : NULL;
- break;
- case '2':
- addr->auth_id = (*ptr)? string_copy(ptr) : NULL;
- break;
- case '3':
- addr->auth_sndr = (*ptr)? string_copy(ptr) : NULL;
- break;
- }
+ {
+ case '1':
+ addr->authenticator = (*ptr)? string_copy(ptr) : NULL;
+ break;
+ case '2':
+ addr->auth_id = (*ptr)? string_copy(ptr) : NULL;
+ break;
+ case '3':
+ addr->auth_sndr = (*ptr)? string_copy(ptr) : NULL;
+ break;
+ }
while (*ptr++);
break;
+#ifndef DISABLE_PRDR
+ case 'P':
+ addr->flags |= af_prdr_used;
+ break;
+#endif
+
+ #ifdef EXPERIMENTAL_DSN
+ case 'D':
+ if (addr == NULL) break;
+ memcpy(&(addr->dsn_aware), ptr, sizeof(addr->dsn_aware));
+ ptr += sizeof(addr->dsn_aware);
+ DEBUG(D_deliver) debug_printf("DSN read: addr->dsn_aware = %d\n", addr->dsn_aware);
+ break;
+ #endif
+
case 'A':
if (addr == NULL)
{
addr->user_message = (*ptr)? string_copy(ptr) : NULL;
while(*ptr++);
- /* Always two strings for host information, followed by the port number */
+ /* Always two strings for host information, followed by the port number and DNSSEC mark */
if (*ptr != 0)
{
while(*ptr++);
memcpy(&(h->port), ptr, sizeof(h->port));
ptr += sizeof(h->port);
+ h->dnssec = *ptr == '2' ? DS_YES
+ : *ptr == '1' ? DS_NO
+ : DS_UNK;
+ ptr++;
addr->host_used = h;
}
else ptr++;
retry_item *r;
/* The certificate verification status goes into the flags */
-
if (tls_out.certificate_verified) setflag(addr, af_cert_verified);
/* Use an X item only if there's something to send */
-
#ifdef SUPPORT_TLS
- if (addr->cipher != NULL)
+ if (addr->cipher)
{
ptr = big_buffer;
- sprintf(CS ptr, "X%.128s", addr->cipher);
+ sprintf(CS ptr, "X1%.128s", addr->cipher);
while(*ptr++);
- if (addr->peerdn == NULL) *ptr++ = 0; else
+ if (!addr->peerdn)
+ *ptr++ = 0;
+ else
{
sprintf(CS ptr, "%.512s", addr->peerdn);
while(*ptr++);
}
+
rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
}
- #endif
+ if (addr->peercert)
+ {
+ ptr = big_buffer;
+ *ptr++ = 'X'; *ptr++ = '2';
+ if (!tls_export_cert(ptr, big_buffer_size-2, addr->peercert))
+ while(*ptr++);
+ else
+ *ptr++ = 0;
+ rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
+ }
+ if (addr->ourcert)
+ {
+ ptr = big_buffer;
+ *ptr++ = 'X'; *ptr++ = '3';
+ if (!tls_export_cert(ptr, big_buffer_size-2, addr->ourcert))
+ while(*ptr++);
+ else
+ *ptr++ = 0;
+ rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
+ }
+ #ifndef DISABLE_OCSP
+ if (addr->ocsp > OCSP_NOT_REQ)
+ {
+ ptr = big_buffer;
+ sprintf(CS ptr, "X4%c", addr->ocsp + '0');
+ while(*ptr++);
+ rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
+ }
+ # endif
+ #endif /*SUPPORT_TLS*/
if (client_authenticator)
{
rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
}
+ #ifndef DISABLE_PRDR
+ if (addr->flags & af_prdr_used)
+ rmt_dlv_checked_write(fd, "P", 1);
+ #endif
+
+ #ifdef EXPERIMENTAL_DSN
+ big_buffer[0] = 'D';
+ memcpy(big_buffer+1, &addr->dsn_aware, sizeof(addr->dsn_aware));
+ rmt_dlv_checked_write(fd, big_buffer, sizeof(addr->dsn_aware) + 1);
+ DEBUG(D_deliver) debug_printf("DSN write: addr->dsn_aware = %d\n", addr->dsn_aware);
+ #endif
+
/* Retry information: for most success cases this will be null. */
for (r = addr->retries; r != NULL; r = r->next)
while(*ptr++);
memcpy(ptr, &(addr->host_used->port), sizeof(addr->host_used->port));
ptr += sizeof(addr->host_used->port);
+
+ /* DNS lookup status */
+ *ptr++ = addr->host_used->dnssec==DS_YES ? '2'
+ : addr->host_used->dnssec==DS_NO ? '1' : '0';
+
}
rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
}
if (r->pno >= 0)
new->onetime_parent = recipients_list[r->pno].address;
+ #ifdef EXPERIMENTAL_DSN
+ /* If DSN support is enabled, set the dsn flags and the original receipt
+ to be passed on to other DSN enabled MTAs */
+ new->dsn_flags = r->dsn_flags & rf_dsnflags;
+ new->dsn_orcpt = r->orcpt;
+ DEBUG(D_deliver) debug_printf("DSN: set orcpt: %s flags: %d\n", new->dsn_orcpt, new->dsn_flags);
+ #endif
+
switch (process_recipients)
{
/* RECIP_DEFER is set when a system filter freezes a message. */
regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE);
#endif
+ #ifndef DISABLE_PRDR
+ if (regex_PRDR == NULL) regex_PRDR =
+ regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE);
+ #endif
+
+ #ifdef EXPERIMENTAL_DSN
+ /* Set the regex to check for DSN support on remote MTA */
+ if (regex_DSN == NULL) regex_DSN =
+ regex_must_compile(US"\\n250[\\s\\-]DSN(\\s|\\n|$)", FALSE, TRUE);
+ #endif
+
/* Now sort the addresses if required, and do the deliveries. The yield of
do_remote_deliveries is FALSE when mua_wrapper is set and all addresses
cannot be delivered in one transaction. */
else if (!dont_deliver) retry_update(&addr_defer, &addr_failed, &addr_succeed);
+#ifdef EXPERIMENTAL_DSN
+/* Send DSN for successful messages */
+addr_dsntmp = addr_succeed;
+addr_senddsn = NULL;
+
+while(addr_dsntmp != NULL)
+ {
+ DEBUG(D_deliver)
+ debug_printf("DSN: processing router : %s\n", addr_dsntmp->router->name);
+
+ DEBUG(D_deliver)
+ debug_printf("DSN: processing successful delivery address: %s\n", addr_dsntmp->address);
+
+ /* af_ignore_error not honored here. it's not an error */
+
+ DEBUG(D_deliver) debug_printf("DSN: Sender_address: %s\n", sender_address);
+ DEBUG(D_deliver) debug_printf("DSN: orcpt: %s flags: %d\n", addr_dsntmp->dsn_orcpt, addr_dsntmp->dsn_flags);
+ DEBUG(D_deliver) debug_printf("DSN: envid: %s ret: %d\n", dsn_envid, dsn_ret);
+ DEBUG(D_deliver) debug_printf("DSN: Final recipient: %s\n", addr_dsntmp->address);
+ DEBUG(D_deliver) debug_printf("DSN: Remote SMTP server supports DSN: %d\n", addr_dsntmp->dsn_aware);
+
+ /* send report if next hop not DSN aware or a router flagged "last DSN hop"
+ and a report was requested */
+ if (((addr_dsntmp->dsn_aware != dsn_support_yes) ||
+ ((addr_dsntmp->dsn_flags & rf_dsnlasthop) != 0))
+ &&
+ (((addr_dsntmp->dsn_flags & rf_dsnflags) != 0) &&
+ ((addr_dsntmp->dsn_flags & rf_notify_success) != 0)))
+ {
+ /* copy and relink address_item and send report with all of them at once later */
+ address_item *addr_next;
+ addr_next = addr_senddsn;
+ addr_senddsn = store_get(sizeof(address_item));
+ memcpy(addr_senddsn, addr_dsntmp, sizeof(address_item));
+ addr_senddsn->next = addr_next;
+ }
+ else
+ {
+ DEBUG(D_deliver) debug_printf("DSN: *** NOT SENDING DSN SUCCESS Message ***\n");
+ }
+
+ addr_dsntmp = addr_dsntmp->next;
+ }
+
+if (addr_senddsn != NULL)
+ {
+ pid_t pid;
+ int fd;
+
+ /* create exim process to send message */
+ pid = child_open_exim(&fd);
+
+ DEBUG(D_deliver) debug_printf("DSN: child_open_exim returns: %d\n", pid);
+
+ if (pid < 0) /* Creation of child failed */
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Process %d (parent %d) failed to "
+ "create child process to send failure message: %s", getpid(),
+ getppid(), strerror(errno));
+
+ DEBUG(D_deliver) debug_printf("DSN: child_open_exim failed\n");
+
+ }
+ else /* Creation of child succeeded */
+ {
+ FILE *f = fdopen(fd, "wb");
+ /* header only as required by RFC. only failure DSN needs to honor RET=FULL */
+ int topt = topt_add_return_path | topt_no_body;
+ uschar boundaryStr[64];
+
+ DEBUG(D_deliver) debug_printf("sending error message to: %s\n", sender_address);
+
+ /* build unique id for MIME boundary */
+ snprintf(boundaryStr, sizeof(boundaryStr)-1, TIME_T_FMT "-eximdsn-%d",
+ time(NULL), rand());
+ DEBUG(D_deliver) debug_printf("DSN: MIME boundary: %s\n", boundaryStr);
+
+ if (errors_reply_to)
+ fprintf(f, "Reply-To: %s\n", errors_reply_to);
+
+ fprintf(f, "Auto-Submitted: auto-generated\n"
+ "From: Mail Delivery System <Mailer-Daemon@%s>\n"
+ "To: %s\n"
+ "Subject: Delivery Status Notification\n"
+ "Content-Type: multipart/report; report-type=delivery-status; boundary=%s\n"
+ "MIME-Version: 1.0\n\n"
+
+ "--%s\n"
+ "Content-type: text/plain; charset=us-ascii\n\n"
+
+ "This message was created automatically by mail delivery software.\n"
+ " ----- The following addresses had successful delivery notifications -----\n",
+ qualify_domain_sender, sender_address, boundaryStr, boundaryStr);
+
+ addr_dsntmp = addr_senddsn;
+ while(addr_dsntmp)
+ {
+ fprintf(f, "<%s> (relayed %s)\n\n",
+ addr_dsntmp->address,
+ (addr_dsntmp->dsn_flags & rf_dsnlasthop) == 1
+ ? "via non DSN router"
+ : addr_dsntmp->dsn_aware == dsn_support_no
+ ? "to non-DSN-aware mailer"
+ : "via non \"Remote SMTP\" router"
+ );
+ addr_dsntmp = addr_dsntmp->next;
+ }
+ fprintf(f, "--%s\n"
+ "Content-type: message/delivery-status\n\n"
+ "Reporting-MTA: dns; %s\n",
+ boundaryStr, smtp_active_hostname);
+
+ if (dsn_envid != NULL) {
+ /* must be decoded from xtext: see RFC 3461:6.3a */
+ uschar *xdec_envid;
+ if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0)
+ fprintf(f, "Original-Envelope-ID: %s\n", dsn_envid);
+ else
+ fprintf(f, "X-Original-Envelope-ID: error decoding xtext formated ENVID\n");
+ }
+ fputc('\n', f);
+
+ for (addr_dsntmp = addr_senddsn;
+ addr_dsntmp;
+ addr_dsntmp = addr_dsntmp->next)
+ {
+ if (addr_dsntmp->dsn_orcpt)
+ fprintf(f,"Original-Recipient: %s\n", addr_dsntmp->dsn_orcpt);
+
+ fprintf(f, "Action: delivered\n"
+ "Final-Recipient: rfc822;%s\n"
+ "Status: 2.0.0\n",
+ addr_dsntmp->address);
+
+ if (addr_dsntmp->host_used && addr_dsntmp->host_used->name)
+ fprintf(f, "Remote-MTA: dns; %s\nDiagnostic-Code: smtp; 250 Ok\n",
+ addr_dsntmp->host_used->name);
+ else
+ fprintf(f,"Diagnostic-Code: X-Exim; relayed via non %s router\n",
+ (addr_dsntmp->dsn_flags & rf_dsnlasthop) == 1 ? "DSN" : "SMTP");
+ fputc('\n', f);
+ }
+
+ fprintf(f, "--%s\nContent-type: text/rfc822-headers\n\n", boundaryStr);
+
+ fflush(f);
+ transport_filter_argv = NULL; /* Just in case */
+ return_path = sender_address; /* In case not previously set */
+
+ /* Write the original email out */
+ transport_write_message(NULL, fileno(f), topt, 0, NULL, NULL, NULL, NULL, NULL, 0);
+ fflush(f);
+
+ fprintf(f,"\n");
+ fprintf(f,"--%s--\n", boundaryStr);
+
+ fflush(f);
+ fclose(f);
+ rc = child_close(pid, 0); /* Waits for child to close, no timeout */
+ }
+ }
+#endif /*EXPERIMENTAL_DSN*/
+
/* If any addresses failed, we must send a message to somebody, unless
af_ignore_error is set, in which case no action is taken. It is possible for
several messages to get sent if there are addresses with different
it from the list, throw away any saved message file, log it, and
mark the recipient done. */
- if (testflag(addr_failed, af_ignore_error))
- {
+ if (testflag(addr_failed, af_ignore_error)
+#ifdef EXPERIMENTAL_DSN
+ || (((addr_failed->dsn_flags & rf_dsnflags) != 0)
+ && ((addr_failed->dsn_flags & rf_notify_failure) != rf_notify_failure))
+#endif
+ )
+ {
addr = addr_failed;
addr_failed = addr->next;
if (addr->return_filename != NULL) Uunlink(addr->return_filename);
BOOL to_sender = strcmpic(sender_address, bounce_recipient) == 0;
int max = (bounce_return_size_limit/DELIVER_IN_BUFFER_SIZE + 1) *
DELIVER_IN_BUFFER_SIZE;
+#ifdef EXPERIMENTAL_DSN
+ uschar boundaryStr[64];
+ uschar *dsnlimitmsg;
+ uschar *dsnnotifyhdr;
+ int topt;
+#endif
DEBUG(D_deliver)
debug_printf("sending error message to: %s\n", bounce_recipient);
moan_write_from(f);
fprintf(f, "To: %s\n", bounce_recipient);
+#ifdef EXPERIMENTAL_DSN
+ /* generate boundary string and output MIME-Headers */
+ snprintf(boundaryStr, sizeof(boundaryStr)-1, TIME_T_FMT "-eximdsn-%d",
+ time(NULL), rand());
+
+ fprintf(f, "Content-Type: multipart/report;"
+ " report-type=delivery-status; boundary=%s\n"
+ "MIME-Version: 1.0\n",
+ boundaryStr);
+#endif
+
/* Open a template file if one is provided. Log failure to open, but
carry on - default texts will be used. */
- if (bounce_message_file != NULL)
- {
- emf = Ufopen(bounce_message_file, "rb");
- if (emf == NULL)
+ if (bounce_message_file)
+ if (!(emf = Ufopen(bounce_message_file, "rb")))
log_write(0, LOG_MAIN|LOG_PANIC, "Failed to open %s for error "
"message texts: %s", bounce_message_file, strerror(errno));
- }
/* Quietly copy to configured additional addresses if required. */
- bcc = moan_check_errorcopy(bounce_recipient);
- if (bcc != NULL) fprintf(f, "Bcc: %s\n", bcc);
+ if ((bcc = moan_check_errorcopy(bounce_recipient)))
+ fprintf(f, "Bcc: %s\n", bcc);
/* The texts for the message can be read from a template file; if there
isn't one, or if it is too short, built-in texts are used. The first
emf text is a Subject: and any other headers. */
- emf_text = next_emf(emf, US"header");
- if (emf_text != NULL) fprintf(f, "%s\n", emf_text); else
- {
+ if ((emf_text = next_emf(emf, US"header")))
+ fprintf(f, "%s\n", emf_text);
+ else
fprintf(f, "Subject: Mail delivery failed%s\n\n",
to_sender? ": returning message to sender" : "");
- }
- emf_text = next_emf(emf, US"intro");
- if (emf_text != NULL) fprintf(f, "%s", CS emf_text); else
+#ifdef EXPERIMENTAL_DSN
+ /* output human readable part as text/plain section */
+ fprintf(f, "--%s\n"
+ "Content-type: text/plain; charset=us-ascii\n\n",
+ boundaryStr);
+#endif
+
+ if ((emf_text = next_emf(emf, US"intro")))
+ fprintf(f, "%s", CS emf_text);
+ else
{
fprintf(f,
/* This message has been reworded several times. It seems to be confusing to
somebody, however it is worded. I have retreated to the original, simple
wording. */
"This message was created automatically by mail delivery software.\n");
- if (bounce_message_text != NULL) fprintf(f, "%s", CS bounce_message_text);
+
+ if (bounce_message_text)
+ fprintf(f, "%s", CS bounce_message_text);
if (to_sender)
- {
fprintf(f,
"\nA message that you sent could not be delivered to one or more of its\n"
"recipients. This is a permanent error. The following address(es) failed:\n");
- }
else
- {
fprintf(f,
"\nA message sent by\n\n <%s>\n\n"
"could not be delivered to one or more of its recipients. The following\n"
"address(es) failed:\n", sender_address);
- }
}
- fprintf(f, "\n");
+ fputc('\n', f);
/* Process the addresses, leaving them on the msgchain if they have a
file name for a return message. (There has already been a check in
}
}
- fprintf(f, "\n");
+ fputc('\n', f);
/* Get the next text, whether we need it or not, so as to be
positioned for the one after. */
fd, and the return_filename field in the *last* one will be set (to the
name of the file). */
- if (msgchain != NULL)
+ if (msgchain)
{
address_item *nextaddr;
- if (emf_text != NULL) fprintf(f, "%s", CS emf_text); else
+ if (emf_text)
+ fprintf(f, "%s", CS emf_text);
+ else
fprintf(f,
"The following text was generated during the delivery "
"attempt%s:\n", (filecount > 1)? "s" : "");
/* List all the addresses that relate to this file */
- fprintf(f, "\n");
- while(addr != NULL) /* Insurance */
+ fputc('\n', f);
+ while(addr) /* Insurance */
{
print_address_information(addr, f, US"------ ", US"\n ",
US" ------\n");
- if (addr->return_filename != NULL) break;
+ if (addr->return_filename) break;
addr = addr->next;
}
- fprintf(f, "\n");
+ fputc('\n', f);
/* Now copy the file */
addr->next = handled_addr;
handled_addr = topaddr;
}
- fprintf(f, "\n");
+ fputc('\n', f);
}
+#ifdef EXPERIMENTAL_DSN
+ /* output machine readable part */
+ fprintf(f, "--%s\n"
+ "Content-type: message/delivery-status\n\n"
+ "Reporting-MTA: dns; %s\n",
+ boundaryStr, smtp_active_hostname);
+
+ if (dsn_envid)
+ {
+ /* must be decoded from xtext: see RFC 3461:6.3a */
+ uschar *xdec_envid;
+ if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0)
+ fprintf(f, "Original-Envelope-ID: %s\n", dsn_envid);
+ else
+ fprintf(f, "X-Original-Envelope-ID: error decoding xtext formated ENVID\n");
+ }
+ fputc('\n', f);
+
+ for (addr = handled_addr; addr; addr = addr->next)
+ {
+ fprintf(f, "Action: failed\n"
+ "Final-Recipient: rfc822;%s\n"
+ "Status: 5.0.0\n",
+ addr->address);
+ if (addr->host_used && addr->host_used->name)
+ fprintf(f, "Remote-MTA: dns; %s\nDiagnostic-Code: smtp; %d\n",
+ addr->host_used->name, addr->basic_errno);
+ }
+#endif
+
/* Now copy the message, trying to give an intelligible comment if
it is too long for it all to be copied. The limit isn't strictly
applied because of the buffering. There is, however, an option
emf_text = next_emf(emf, US"copy");
+#ifndef EXPERIMENTAL_DSN
if (bounce_return_message)
{
int topt = topt_add_return_path;
if (!bounce_return_body) topt |= topt_no_body;
- if (emf_text != NULL) fprintf(f, "%s", CS emf_text); else
+ if (emf_text)
+ fprintf(f, "%s", CS emf_text);
+ else
{
if (bounce_return_body) fprintf(f,
"------ This is a copy of the message, including all the headers. ------\n");
{
struct stat statbuf;
if (fstat(deliver_datafile, &statbuf) == 0 && statbuf.st_size > max)
- {
- if (emf_text != NULL) fprintf(f, "%s", CS emf_text); else
- {
+ if (emf_text)
+ fprintf(f, "%s", CS emf_text);
+ else
fprintf(f,
"------ The body of the message is " OFF_T_FMT " characters long; only the first\n"
"------ %d or so are included here.\n", statbuf.st_size, max);
- }
- }
}
- fprintf(f, "\n");
+ fputc('\n', f);
fflush(f);
+
transport_filter_argv = NULL; /* Just in case */
return_path = sender_address; /* In case not previously set */
transport_write_message(NULL, fileno(f), topt,
/* Write final text and close the template file if one is open */
- if (emf != NULL)
+ if (emf)
{
- emf_text = next_emf(emf, US"final");
- if (emf_text != NULL) fprintf(f, "%s", CS emf_text);
+ if ((emf_text = next_emf(emf, US"final")))
+ fprintf(f, "%s", CS emf_text);
(void)fclose(emf);
}
+#else
+ /* add message body
+ we ignore the intro text from template and add
+ the text for bounce_return_size_limit at the end.
+
+ bounce_return_message is ignored
+ in case RET= is defined we honor these values
+ otherwise bounce_return_body is honored.
+
+ bounce_return_size_limit is always honored.
+ */
+
+ fprintf(f, "\n--%s\n", boundaryStr);
+
+ dsnlimitmsg = US"X-Exim-DSN-Information: Due to administrative limits only headers are returned";
+ dsnnotifyhdr = NULL;
+ topt = topt_add_return_path;
+
+ /* RET=HDRS? top priority */
+ if (dsn_ret == dsn_ret_hdrs)
+ topt |= topt_no_body;
+ else
+ /* no full body return at all? */
+ if (!bounce_return_body)
+ {
+ topt |= topt_no_body;
+ /* add header if we overrule RET=FULL */
+ if (dsn_ret == dsn_ret_full)
+ dsnnotifyhdr = dsnlimitmsg;
+ }
+ /* size limited ... return headers only if limit reached */
+ else if (bounce_return_size_limit > 0)
+ {
+ struct stat statbuf;
+ if (fstat(deliver_datafile, &statbuf) == 0 && statbuf.st_size > max)
+ {
+ topt |= topt_no_body;
+ dsnnotifyhdr = dsnlimitmsg;
+ }
+ }
+
+ if (topt & topt_no_body)
+ fprintf(f,"Content-type: text/rfc822-headers\n\n");
+ else
+ fprintf(f,"Content-type: message/rfc822\n\n");
+
+ fflush(f);
+ transport_filter_argv = NULL; /* Just in case */
+ return_path = sender_address; /* In case not previously set */
+ transport_write_message(NULL, fileno(f), topt,
+ 0, dsnnotifyhdr, NULL, NULL, NULL, NULL, 0);
+ fflush(f);
+
+ /* we never add the final text. close the file */
+ if (emf)
+ (void)fclose(emf);
+
+ fprintf(f, "\n--%s--\n", boundaryStr);
+#endif /*EXPERIMENTAL_DSN*/
/* Close the file, which should send an EOF to the child process
that is receiving the message. Wait for it to finish. */
if ((log_extra_selector & LX_queue_time_overall) != 0)
log_write(0, LOG_MAIN, "Completed QT=%s",
- readconf_printtime(time(NULL) - received_time));
+ readconf_printtime( (int) ((long)time(NULL) - (long)received_time)) );
else
log_write(0, LOG_MAIN, "Completed");
it also defers). */
if (!queue_2stage && delivery_attempted &&
+#ifdef EXPERIMENTAL_DSN
+ (((addr_defer->dsn_flags & rf_dsnflags) == 0) ||
+ (addr_defer->dsn_flags & rf_notify_delay) == rf_notify_delay) &&
+#endif
delay_warning[1] > 0 && sender_address[0] != 0 &&
(delay_warning_condition == NULL ||
expand_check_condition(delay_warning_condition,
uschar *wmf_text;
FILE *wmf = NULL;
FILE *f = fdopen(fd, "wb");
+#ifdef EXPERIMENTAL_DSN
+ uschar boundaryStr[64];
+#endif
- if (warn_message_file != NULL)
+ if (warn_message_file)
{
wmf = Ufopen(warn_message_file, "rb");
if (wmf == NULL)
string_sprintf("%d minutes", show_time/60):
string_sprintf("%d hours", show_time/3600);
- if (errors_reply_to != NULL)
+ if (errors_reply_to)
fprintf(f, "Reply-To: %s\n", errors_reply_to);
fprintf(f, "Auto-Submitted: auto-replied\n");
moan_write_from(f);
fprintf(f, "To: %s\n", recipients);
- wmf_text = next_emf(wmf, US"header");
- if (wmf_text != NULL)
+#ifdef EXPERIMENTAL_DSN
+ /* generated boundary string and output MIME-Headers */
+ snprintf(boundaryStr, sizeof(boundaryStr)-1,
+ TIME_T_FMT "-eximdsn-%d", time(NULL), rand());
+
+ fprintf(f, "Content-Type: multipart/report;"
+ " report-type=delivery-status; boundary=%s\n"
+ "MIME-Version: 1.0\n",
+ boundaryStr);
+#endif
+
+ if ((wmf_text = next_emf(wmf, US"header")))
fprintf(f, "%s\n", wmf_text);
else
fprintf(f, "Subject: Warning: message %s delayed %s\n\n",
message_id, warnmsg_delay);
- wmf_text = next_emf(wmf, US"intro");
- if (wmf_text != NULL) fprintf(f, "%s", CS wmf_text); else
+#ifdef EXPERIMENTAL_DSN
+ /* output human readable part as text/plain section */
+ fprintf(f, "--%s\n"
+ "Content-type: text/plain; charset=us-ascii\n\n",
+ boundaryStr);
+#endif
+
+ if ((wmf_text = next_emf(wmf, US"intro")))
+ fprintf(f, "%s", CS wmf_text);
+ else
{
fprintf(f,
"This message was created automatically by mail delivery software.\n");
"A message that you sent has not yet been delivered to one or more of its\n"
"recipients after more than ");
- else fprintf(f,
+ else
+ fprintf(f,
"A message sent by\n\n <%s>\n\n"
"has not yet been delivered to one or more of its recipients after more than \n",
- sender_address);
+ sender_address);
- fprintf(f, "%s on the queue on %s.\n\n", warnmsg_delay,
- primary_hostname);
- fprintf(f, "The message identifier is: %s\n", message_id);
+ fprintf(f, "%s on the queue on %s.\n\n"
+ "The message identifier is: %s\n",
+ warnmsg_delay, primary_hostname, message_id);
for (h = header_list; h != NULL; h = h->next)
- {
if (strncmpic(h->text, US"Subject:", 8) == 0)
fprintf(f, "The subject of the message is: %s", h->text + 9);
else if (strncmpic(h->text, US"Date:", 5) == 0)
fprintf(f, "The date of the message is: %s", h->text + 6);
- }
- fprintf(f, "\n");
+ fputc('\n', f);
fprintf(f, "The address%s to which the message has not yet been "
"delivered %s:\n",
- (addr_defer->next == NULL)? "" : "es",
- (addr_defer->next == NULL)? "is": "are");
+ !addr_defer->next ? "" : "es",
+ !addr_defer->next ? "is": "are");
}
/* List the addresses, with error information if allowed */
- fprintf(f, "\n");
- while (addr_defer != NULL)
+#ifdef EXPERIMENTAL_DSN
+ /* store addr_defer for machine readable part */
+ address_item *addr_dsndefer = addr_defer;
+#endif
+ fputc('\n', f);
+ while (addr_defer)
{
address_item *addr = addr_defer;
addr_defer = addr->next;
if (print_address_information(addr, f, US" ", US"\n ", US""))
print_address_error(addr, f, US"Delay reason: ");
- fprintf(f, "\n");
+ fputc('\n', f);
}
- fprintf(f, "\n");
+ fputc('\n', f);
/* Final text */
- if (wmf != NULL)
+ if (wmf)
{
- wmf_text = next_emf(wmf, US"final");
- if (wmf_text != NULL) fprintf(f, "%s", CS wmf_text);
+ if ((wmf_text = next_emf(wmf, US"final")))
+ fprintf(f, "%s", CS wmf_text);
(void)fclose(wmf);
}
else
"and when that happens, the message will be returned to you.\n");
}
+#ifdef EXPERIMENTAL_DSN
+ /* output machine readable part */
+ fprintf(f, "\n--%s\n"
+ "Content-type: message/delivery-status\n\n"
+ "Reporting-MTA: dns; %s\n",
+ boundaryStr,
+ smtp_active_hostname);
+
+
+ if (dsn_envid)
+ {
+ /* must be decoded from xtext: see RFC 3461:6.3a */
+ uschar *xdec_envid;
+ if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0)
+ fprintf(f,"Original-Envelope-ID: %s\n", dsn_envid);
+ else
+ fprintf(f,"X-Original-Envelope-ID: error decoding xtext formated ENVID\n");
+ }
+ fputc('\n', f);
+
+ while (addr_dsndefer)
+ {
+ if (addr_dsndefer->dsn_orcpt)
+ fprintf(f,"Original-Recipient: %s\n", addr_dsndefer->dsn_orcpt);
+
+ fprintf(f,"Action: delayed\n");
+ fprintf(f,"Final-Recipient: rfc822;%s\n", addr_dsndefer->address);
+ fprintf(f,"Status: 4.0.0\n");
+ if (addr_dsndefer->host_used && addr_dsndefer->host_used->name)
+ fprintf(f,"Remote-MTA: dns; %s\nDiagnostic-Code: smtp; %d\n",
+ addr_dsndefer->host_used->name, addr_dsndefer->basic_errno);
+ addr_dsndefer = addr_dsndefer->next;
+ }
+
+ fprintf(f, "\n--%s\n"
+ "Content-type: text/rfc822-headers\n\n",
+ boundaryStr);
+
+ fflush(f);
+ /* header only as required by RFC. only failure DSN needs to honor RET=FULL */
+ int topt = topt_add_return_path | topt_no_body;
+ transport_filter_argv = NULL; /* Just in case */
+ return_path = sender_address; /* In case not previously set */
+ /* Write the original email out */
+ transport_write_message(NULL, fileno(f), topt, 0, NULL, NULL, NULL, NULL, NULL, 0);
+ fflush(f);
+
+ fprintf(f,"\n--%s--\n", boundaryStr);
+
+ fflush(f);
+#endif /*EXPERIMENTAL_DSN*/
+
/* Close and wait for child process to complete, without a timeout.
If there's an error, don't update the count. */
return final_yield;
}
+/* vi: aw ai sw=2
+*/
/* End of deliver.c */