diff -urN exim-4.64.org/README.DSN exim-4.64/README.DSN --- exim-4.64.org/README.DSN 1970-01-01 01:00:00.000000000 +0100 +++ exim-4.64/README.DSN 2006-12-21 16:43:20.808508250 +0100 @@ -0,0 +1,104 @@ +Exim DSN Patch (4.60) +--------------------- + +This patch is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This patch is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this patch; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. + +Install +------- +cd into the source tree for a vanilla exim + +patch -p1 dsn_aware = (*ptr)? string_copy(ptr) : string_copy(" "); + while (*ptr++); + DEBUG(D_deliver) debug_printf("DSN read: addr->dsn_aware = %s\n", addr->dsn_aware); + break; + #endif + case 'A': if (addr == NULL) { @@ -3935,6 +3947,15 @@ } #endif + #ifdef SUPPORT_DSN + if (addr->dsn_aware == NULL) + addr->dsn_aware = string_copy(" "); + DEBUG(D_deliver) debug_printf("DSN write: addr->dsn_aware = %s\n", addr->dsn_aware); + sprintf(big_buffer, "D%s", addr->dsn_aware); + DEBUG(D_deliver) debug_printf("DSN write: big_buffer = %s (%d)\n", big_buffer, strlen(big_buffer)+1); + write(fd, big_buffer, strlen(big_buffer)+1); + #endif + /* Retry information: for most success cases this will be null. */ for (r = addr->retries; r != NULL; r = r->next) @@ -5072,6 +5093,14 @@ if (r->pno >= 0) new->onetime_parent = recipients_list[r->pno].address; + #ifdef SUPPORT_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_printf("DSN (deliver): 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. */ @@ -5981,6 +6010,12 @@ regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE); #endif + #ifdef SUPPORT_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. */ @@ -6085,6 +6120,179 @@ else if (!dont_deliver) retry_update(&addr_defer, &addr_failed, &addr_succeed); +#ifdef SUPPORT_DSN +/* ********** philb - Send DSN for successful messages */ + +addr_dsntmp = addr_succeed; + +while(addr_dsntmp != NULL) +{ + BOOL dsn_sendmessage = FALSE; + uschar dsnmsgbuf[4096]; + + 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); + + if (testflag(addr_dsntmp, af_ignore_error)) + { + DEBUG(D_deliver) + debug_printf("DSN: Ignore error for: %s\n", addr_dsntmp->address); + } + else + { + DEBUG(D_deliver) debug_printf("DSN: Checking Flag\n"); + if (addr_dsntmp->dsn_aware == NULL) { + DEBUG(D_deliver) debug_printf("DSN: dsn_aware was NULL, setting to space at %s %d\n", __FILE__, __LINE__); + addr_dsntmp->dsn_aware = string_copy(" "); + } + 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: Remote SMTP server supports DSN: %s\n", addr_dsntmp->dsn_aware); + + /* Process the flags */ + if((addr_dsntmp->dsn_flags & rf_dsnflags) != 0) + { + /* We've got at least one flag set */ + + /* set flag so we don't send bounces */ + setflag(addr_dsntmp, af_ignore_error); + + if((addr_dsntmp->dsn_flags & rf_notify_never) != 0) + { + DEBUG(D_deliver) debug_printf("DSN: NEVER FLAG\n"); + + /* nothing to do here */ + } + + if((addr_dsntmp->dsn_flags & rf_notify_success) != 0) + { + DEBUG(D_deliver) debug_printf("DSN: SUCCESS FLAG\n"); + + dsn_sendmessage = TRUE; + } + + if((addr_dsntmp->dsn_flags & rf_notify_failure) != 0) + { + DEBUG(D_deliver) debug_printf("DSN: FAILURE FLAG\n"); + + /* allow bounce messages */ + clearflag(addr_dsntmp, af_ignore_error); + } + + if((addr_dsntmp->dsn_flags & rf_notify_delay) != 0) + { + DEBUG(D_deliver) debug_printf("DSN: DELAY FLAG\n"); + + /* hmm, what to do here? */ + } + } + + if ((addr_dsntmp->dsn_aware != 0) && (addr_dsntmp->dsn_aware[0] != 'Y') && (dsn_sendmessage == TRUE) && (addr_dsntmp->router->dsn_process == TRUE)) + { + pid_t pid; + int fd; + + /* remote MTA does not support DSN, so we need to send message */ + + /* 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"); + int topt = topt_add_return_path; + uschar boundaryStr[64]; + + DEBUG(D_deliver) debug_printf("sending error message to: %s\n", sender_address); + + /* build unique id for MIME boundary */ + snprintf(boundaryStr, 63, "%d-cwdsn-%d", pid, rand()); + DEBUG(D_deliver) debug_printf("DSN: MIME boundary: %s\n", boundaryStr); + + /* if the sender doesn't want the whole message returned, don't send the body */ + if (dsn_ret != dsn_ret_full) topt |= topt_no_body; + + if (errors_reply_to != NULL) fprintf(f,"Reply-To: %s\n", errors_reply_to); + + fprintf(f,"Auto-Submitted: auto-generated\n"); + fprintf(f,"From: Mail Delivery System \n", qualify_domain_sender); + fprintf(f,"To: %s\n", sender_address); + fprintf(f,"Subject: Delivery Status Notification\n"); + fprintf(f,"Content-Type: multipart/report; report-type=delivery-status; boundary=%s\n", boundaryStr); + fprintf(f,"MIME-Version: 1.0\n\n"); + + fprintf(f,"--%s\n", boundaryStr); + fprintf(f,"Content-type: text/plain; charset=us-ascii\n\n"); + + fprintf(f,"This message was created automatically by mail delivery software.\n"); + fprintf(f," ----- The following addresses had successful delivery notifications -----\n"); +/* AH: added specific message for non "Remote SMTP" situations */ + if (addr_dsntmp->dsn_aware[0] == 'N') { + fprintf(f,"<%s> (relayed to non-DSN-aware mailer)\n\n", addr_dsntmp->address); + } else { + fprintf(f,"<%s> (relayed via non \"Remote SMTP\" router)\n\n", addr_dsntmp->address); + } + + fprintf(f,"--%s\n", boundaryStr); + fprintf(f,"Content-type: message/delivery-status\n\n"); + + if (dsn_envid) { /* Test for NULL added by GC */ + fprintf(f,"Original-Envelope-Id: %s\n", dsn_envid); + } + fprintf(f,"Reporting-MTA: dns; %s\n", qualify_domain_sender); + if (addr_dsntmp->dsn_orcpt) { /* Test for NULL added by GC */ + fprintf(f,"Original-Recipient: %s\n", addr_dsntmp->dsn_orcpt); + } + fprintf(f,"Action: delivered\n\n"); + + fprintf(f,"--%s\n", boundaryStr); + 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 */ + + /* Write the original email out */ + transport_write_message(NULL, fileno(f), topt, 2048, 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 */ + } + } + else + { if (addr_dsntmp->router->dsn_process == TRUE) + DEBUG(D_deliver) debug_printf("DSN: *** NOT SENDING DSN SUCCESS Message ***\n"); + if (addr_dsntmp->router->dsn_process == FALSE) + DEBUG(D_deliver) debug_printf("DSN: *** NOT SENDING DSN SUCCESS Message (gagged) ***\n"); + } + } + + addr_dsntmp = addr_dsntmp->next; +} + +/* ********** philb - end of mod */ +#endif + /* 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 diff -urN exim-4.64.org/src/EDITME exim-4.64/src/EDITME --- exim-4.64.org/src/EDITME 2006-12-20 10:46:04.000000000 +0100 +++ exim-4.64/src/EDITME 2006-12-21 16:43:20.812508500 +0100 @@ -195,6 +195,8 @@ # least one type of lookup. You should consider whether you want to build # the Exim monitor or not. +# Support DSN +SUPPORT_DSN=yes #------------------------------------------------------------------------------ # These settings determine which individual router drivers are included in the diff -urN exim-4.64.org/src/exim.c exim-4.64/src/exim.c --- exim-4.64.org/src/exim.c 2006-12-20 10:46:04.000000000 +0100 +++ exim-4.64/src/exim.c 2006-12-21 16:43:20.816508750 +0100 @@ -920,6 +920,9 @@ #ifdef EXPERIMENTAL_DOMAINKEYS fprintf(f, " Experimental_DomainKeys"); #endif +#ifdef SUPPORT_DSN + fprintf(f, " C&W_DSN_1.2"); +#endif fprintf(f, "\n"); fprintf(f, "Lookups:"); @@ -2208,6 +2211,16 @@ break; } + #ifdef SUPPORT_DSN + /* -MCD: set the smtp_use_dsn flag; this indicates that the host + that exim is connected to supports the esmtp extension DSN */ + else if (strcmp(argrest, "CD") == 0) + { + smtp_use_dsn = TRUE; + break; + } + #endif + /* -MCP: set the smtp_use_pipelining flag; this is useful only when it preceded -MC (see above) */ diff -urN exim-4.64.org/src/globals.c exim-4.64/src/globals.c --- exim-4.64.org/src/globals.c 2006-12-20 10:46:04.000000000 +0100 +++ exim-4.64/src/globals.c 2006-12-21 16:43:20.816508750 +0100 @@ -123,6 +123,13 @@ uschar *tls_verify_hosts = NULL; #endif +#ifdef SUPPORT_DSN +BOOL dsn = TRUE; +uschar *dsn_envid = NULL; +int dsn_ret = 0; +const pcre *regex_DSN = NULL; +BOOL smtp_use_dsn = FALSE; +#endif /* Input-reading functions for messages, so we can use special ones for incoming TCP/IP. The defaults use stdin. We never need these for any @@ -273,6 +280,11 @@ NULL, /* cipher */ NULL, /* peerdn */ #endif + #ifdef SUPPORT_DSN + NULL, /* dsn_orcpt */ + 0, /* dsn_flags */ + NULL, /* dsn_aware */ + #endif (uid_t)(-1), /* uid */ (gid_t)(-1), /* gid */ 0, /* flags */ @@ -963,6 +975,9 @@ TRUE, /* verify_sender */ FALSE, /* uid_set */ FALSE, /* unseen */ +#ifdef SUPPORT_DSN + FALSE, /* dsn_process */ +#endif self_freeze, /* self_code */ (uid_t)(-1), /* uid */ @@ -972,6 +987,7 @@ NULL, /* transport instance */ NULL, /* pass_router */ NULL /* redirect_router */ + }; ip_address_item *running_interfaces = NULL; diff -urN exim-4.64.org/src/globals.h exim-4.64/src/globals.h --- exim-4.64.org/src/globals.h 2006-12-20 10:46:04.000000000 +0100 +++ exim-4.64/src/globals.h 2006-12-21 16:43:20.816508750 +0100 @@ -85,6 +85,13 @@ extern uschar *tls_verify_hosts; /* Mandatory client verification */ #endif +#ifdef SUPPORT_DSN +extern BOOL dsn; /* FALSE if DSN not to be used */ +extern uschar *dsn_envid; /* DSN envid string */ +extern int dsn_ret; /* DSN ret type*/ +extern const pcre *regex_DSN; /* For recognizing DSN settings */ +extern BOOL smtp_use_dsn; /* Global for passed connections */ +#endif /* Input-reading functions for messages, so we can use special ones for incoming TCP/IP. */ diff -urN exim-4.64.org/src/local_scan.h exim-4.64/src/local_scan.h --- exim-4.64.org/src/local_scan.h 2006-12-20 10:46:04.000000000 +0100 +++ exim-4.64/src/local_scan.h 2006-12-21 16:43:20.816508750 +0100 @@ -125,9 +125,13 @@ field is always NULL except for one_time aliases that had errors_to on the routers that generated them. */ +/* Added the dsn attributes orcpt and dsn_flags for DSN support*/ + typedef struct recipient_item { uschar *address; /* the recipient address */ int pno; /* parent number for "one_time" alias, or -1 */ + uschar *orcpt; /* DSN orcpt */ + int dsn_flags; /* DSN flags */ uschar *errors_to; /* the errors_to address or NULL */ #ifdef EXPERIMENTAL_BRIGHTMAIL uschar *bmi_optin; diff -urN exim-4.64.org/src/macros.h exim-4.64/src/macros.h --- exim-4.64.org/src/macros.h 2006-12-20 10:46:04.000000000 +0100 +++ exim-4.64/src/macros.h 2006-12-21 16:43:20.816508750 +0100 @@ -735,6 +735,22 @@ #define topt_no_body 0x040 /* Omit body */ #define topt_escape_headers 0x080 /* Apply escape check to headers */ +/* Flags for recipient_block, used in DSN support */ + +#define rf_onetime 0x01 /* A one-time alias */ +#define rf_notify_never 0x02 /* NOTIFY= settings */ +#define rf_notify_success 0x04 +#define rf_notify_failure 0x08 +#define rf_notify_delay 0x10 + +#define rf_dsnflags (rf_notify_never | rf_notify_success | \ + rf_notify_failure | rf_notify_delay) + +/* DSN RET types */ + +#define dsn_ret_full 1 +#define dsn_ret_hdrs 2 + /* Codes for the host_find_failed and host_all_ignored options. */ #define hff_freeze 0 diff -urN exim-4.64.org/src/readconf.c exim-4.64/src/readconf.c --- exim-4.64.org/src/readconf.c 2006-12-20 10:46:04.000000000 +0100 +++ exim-4.64/src/readconf.c 2006-12-21 16:43:20.820509000 +0100 @@ -203,6 +203,9 @@ { "dns_retrans", opt_time, &dns_retrans }, { "dns_retry", opt_int, &dns_retry }, { "dns_use_edns0", opt_int, &dns_use_edns0 }, +#ifdef SUPPORT_DSN + { "dsn", opt_bool, &dsn }, +#endif /* This option is now a no-op, retained for compability */ { "drop_cr", opt_bool, &drop_cr }, /*********************************************************/ diff -urN exim-4.64.org/src/receive.c exim-4.64/src/receive.c --- exim-4.64.org/src/receive.c 2006-12-20 10:46:04.000000000 +0100 +++ exim-4.64/src/receive.c 2006-12-21 16:43:20.820509000 +0100 @@ -474,6 +474,8 @@ memcpy(recipients_list, oldlist, oldmax * sizeof(recipient_item)); } +/* memset added by GC to blank dsn records, etc. */ +memset(&recipients_list[recipients_count], 0, sizeof(recipient_item)); recipients_list[recipients_count].address = recipient; recipients_list[recipients_count].pno = pno; #ifdef EXPERIMENTAL_BRIGHTMAIL diff -urN exim-4.64.org/src/route.c exim-4.64/src/route.c --- exim-4.64.org/src/route.c 2006-12-20 10:46:04.000000000 +0100 +++ exim-4.64/src/route.c 2006-12-21 16:43:20.824509250 +0100 @@ -60,6 +60,10 @@ (void *)offsetof(router_instance, domains) }, { "driver", opt_stringptr|opt_public, (void *)offsetof(router_instance, driver_name) }, + #ifdef SUPPORT_DSN + { "dsn_process", opt_bool|opt_public, + (void *)offsetof(router_instance, dsn_process) }, + #endif { "errors_to", opt_stringptr|opt_public, (void *)(offsetof(router_instance, errors_to)) }, { "expn", opt_bool|opt_public, @@ -272,6 +276,13 @@ if (r->pass_router_name != NULL) set_router(r, r->pass_router_name, &(r->pass_router), TRUE); + + #ifdef SUPPORT_DSN + if (r->dsn_process == FALSE) + DEBUG(D_route) debug_printf("%s router skipping DSN - add dsn_process to router\n", r->name); + if (r->dsn_process == TRUE) + DEBUG(D_route) debug_printf("%s router performing DSN \n", r->name); + #endif } } @@ -1408,7 +1419,10 @@ copyflag(new, addr, af_propagate); new->p.address_data = addr->p.address_data; - +#ifdef SUPPORT_DSN + new->dsn_flags = addr->dsn_flags; + new->dsn_orcpt = addr->dsn_orcpt; +#endif /* As it has turned out, we haven't set headers_add or headers_remove for the * clone. Thinking about it, it isn't entirely clear whether they should be diff -urN exim-4.64.org/src/smtp_in.c exim-4.64/src/smtp_in.c --- exim-4.64.org/src/smtp_in.c 2006-12-20 10:46:04.000000000 +0100 +++ exim-4.64/src/smtp_in.c 2006-12-21 16:43:20.824509250 +0100 @@ -832,6 +832,13 @@ sender_verified_list = NULL; /* No senders verified */ memset(sender_address_cache, 0, sizeof(sender_address_cache)); memset(sender_domain_cache, 0, sizeof(sender_domain_cache)); + +#ifdef SUPPORT_DSN +/* Reset the DSN flags */ +dsn_ret = 0; +dsn_envid = NULL; +#endif + authenticated_sender = NULL; #ifdef EXPERIMENTAL_BRIGHTMAIL bmi_run = 0; @@ -2317,6 +2324,10 @@ int ptr, size, rc; int c, i; auth_instance *au; +#ifdef SUPPORT_DSN + uschar *orcpt = NULL; + int flags; +#endif switch(smtp_read_command(TRUE)) { @@ -2735,6 +2746,12 @@ s = string_cat(s, &size, &ptr, US"-8BITMIME\r\n", 11); } + #ifdef SUPPORT_DSN + /* Advertise DSN support if configured to do so. */ + if (dsn) + s = string_cat(s, &size, &ptr, US"250-DSN\r\n", 9); + #endif + /* Advertise ETRN if there's an ACL checking whether a host is permitted to issue it; a check is made when any host actually tries. */ @@ -2946,6 +2963,43 @@ (strcmpic(value, US"8BITMIME") == 0 || strcmpic(value, US"7BIT") == 0)) {} + #ifdef SUPPORT_DSN + + /* Handle the two DSN options, but only if configured to do so (which + will have caused "DSN" to be given in the EHLO response). The code itself + is included only if configured in at build time. */ + + else if (dsn && strcmpic(name, US"RET") == 0) + { + /* Check if RET has already been set */ + if (dsn_ret > 0) { + synprot_error(L_smtp_syntax_error, 501, NULL, + US"RET can be specified once only"); + goto COMMAND_LOOP; + } + dsn_ret = (strcmpic(value, US"HDRS") == 0)? dsn_ret_hdrs : + (strcmpic(value, US"FULL") == 0)? dsn_ret_full : 0; + DEBUG(D_receive) debug_printf("DSN_RET: %d\n", dsn_ret); + /* Check for invalid invalid value, and exit with error */ + if (dsn_ret == 0) { + synprot_error(L_smtp_syntax_error, 501, NULL, + US"Value for RET is invalid"); + goto COMMAND_LOOP; + } + } + else if (dsn && strcmpic(name, US"ENVID") == 0) + { + /* Check if the dsn envid has been already set */ + if (dsn_envid != NULL) { + synprot_error(L_smtp_syntax_error, 501, NULL, + US"ENVID can be specified once only"); + goto COMMAND_LOOP; + } + dsn_envid = string_copy(value); + DEBUG(D_receive) debug_printf("DSN_ENVID: %s\n", dsn_envid); + } + #endif + /* Handle the AUTH extension. If the value given is not "<>" and either the ACL says "yes" or there is no ACL but the sending host is authenticated, we set it up as the authenticated sender. However, if the @@ -3193,6 +3247,89 @@ rcpt_fail_count++; break; } + + #ifdef SUPPORT_DSN + /* Set the DSN flags orcpt and dsn_flags from the session*/ + orcpt = NULL; + flags = 0; + + if (esmtp) for(;;) + { + uschar *name, *value, *end; + int size; + + if (!extract_option(&name, &value)) + { + break; + } + + if (strcmpic(name, US"ORCPT") == 0) + { + /* Check whether orcpt has been already set */ + if (orcpt != NULL) { + synprot_error(L_smtp_syntax_error, 501, NULL, + US"ORCPT can be specified once only"); + goto COMMAND_LOOP; + } + orcpt = string_copy(value); + DEBUG(D_receive) debug_printf("DSN orcpt: %s\n", orcpt); + } + + else if (strcmpic(name, US"NOTIFY") == 0) + { + /* Check if the notify flags have been already set */ + if (flags > 0) + { + synprot_error(L_smtp_syntax_error, 501, NULL, + US"NOTIFY can be specified once only"); + goto COMMAND_LOOP; + } + if (strcmpic(value, US"NEVER") == 0) flags |= rf_notify_never; else + { + uschar *p = value; + while (*p != 0) + { + uschar *pp = p; + while (*pp != 0 && *pp != ',') pp++; + if (*pp == ',') *pp++ = 0; + if (strcmpic(p, US"SUCCESS") == 0) { + DEBUG(D_receive) debug_printf("GC: Setting notify success\n"); + flags |= rf_notify_success; + } + else if (strcmpic(p, US"FAILURE") == 0) { + DEBUG(D_receive) debug_printf("GC: Setting notify failure\n"); + flags |= rf_notify_failure; + } + else if (strcmpic(p, US"DELAY") == 0) { + DEBUG(D_receive) debug_printf("GC: Setting notify delay\n"); + flags |= rf_notify_delay; + } + else + { + /* Catch any strange values */ + synprot_error(L_smtp_syntax_error, 501, NULL, + US"Invalid value for NOTIFY parameter"); + goto COMMAND_LOOP; + } + p = pp; + } + DEBUG(D_receive) debug_printf("DSN Flags: %x\n", flags); + } + } + + /* Unknown option. Stick back the terminator characters and break + the loop. An error for a malformed address will occur. */ + + else + { + DEBUG(D_receive) debug_printf("Invalid dsn command: %s : %s\n", name, value); + name[-1] = ' '; + value[-1] = '='; + break; + } + } + #endif + /* Apply SMTP rewriting then extract the working address. Don't allow "<>" as a recipient address */ @@ -3300,6 +3437,24 @@ if (user_msg == NULL) smtp_printf("250 Accepted\r\n"); else smtp_user_msg(US"250", user_msg); receive_add_recipient(recipient, -1); + + #ifdef SUPPORT_DSN + + /* Set the dsn flags in the recipients_list */ + if (orcpt != NULL) + recipients_list[recipients_count-1].orcpt = orcpt; + else + recipients_list[recipients_count-1].orcpt = NULL; + + if (flags != 0) + recipients_list[recipients_count-1].dsn_flags = flags; + else + recipients_list[recipients_count-1].dsn_flags = 0; + debug_printf("DSN-AJ(smtp-in): orcpt: %s flags: %d\n", recipients_list[recipients_count-1].orcpt, recipients_list[recipients_count-1].dsn_flags); + + + #endif + } /* The recipient was discarded */ diff -urN exim-4.64.org/src/spool_in.c exim-4.64/src/spool_in.c --- exim-4.64.org/src/spool_in.c 2006-12-20 10:46:04.000000000 +0100 +++ exim-4.64/src/spool_in.c 2006-12-21 16:47:28.415982750 +0100 @@ -291,6 +291,13 @@ spam_score_int = NULL; #endif +#ifdef SUPPORT_DSN +#ifndef COMPILE_UTILITY +dsn_ret = 0; +dsn_envid = NULL; +#endif /* COMPILE_UTILITY */ +#endif + /* Generate the full name and open the file. If message_subdir is already set, just look in the given directory. Otherwise, look in both the split and unsplit directories, as for the data file above. */ @@ -465,6 +472,19 @@ case 'd': if (Ustrcmp(p, "eliver_firsttime") == 0) deliver_firsttime = TRUE; +#ifdef SUPPORT_DSN +#ifndef COMPILE_UTILITY + /* Check if the dsn flags have been set in the header file */ + else if (Ustrncmp(p, "sn_ret", 8) == 0) + { + dsn_ret = atoi(big_buffer + 8); + } + else if (Ustrncmp(p, "sn_envid", 10) == 0) + { + dsn_envid = string_copy(big_buffer + 11); + } +#endif /* COMPILE_UTILITY */ +#endif break; case 'f': @@ -598,6 +618,10 @@ { int nn; int pno = -1; + #ifdef SUPPORT_DSN + int dsn_flags = 0; + uschar *orcpt = NULL; + #endif uschar *errors_to = NULL; uschar *p; @@ -666,10 +690,19 @@ } /* Handle current format Exim 4 spool files */ + /* Spool file is modified if DSN is supported + Original was "address errors_to len(errors_to),pno + New for DSN support is now: + "address errors_to orcpt len(errors_to),len(orcpt),pno,dsn_flags */ else if (*p == '#') { int flags; + + #ifndef COMPILE_UTILITY + DEBUG(D_deliver) debug_printf("**** SPOOL_IN - Exim 4 standard format spoolfile\n"); + #endif /* COMPILE_UTILITY */ + (void)sscanf(CS p+1, "%d", &flags); if ((flags & 0x01) != 0) /* one_time data exists */ @@ -682,15 +715,82 @@ { p -= len; errors_to = string_copy(p); + } + } + + *(--p) = 0; /* Terminate address */ + } + #ifdef SUPPORT_DSN + else if (*p == '!') /* Handle Exim4 + DSN spool files */ + { + int flags; + int temp_dsn_flags; + + #ifndef COMPILE_UTILITY + DEBUG(D_deliver) debug_printf("**** SPOOL_IN - C&W DSN format spoolfile\n"); + #endif /* COMPILE_UTILITY */ + + sscanf(CS p+1, "%d,%d", &flags, &temp_dsn_flags); + + if (((flags & 0x01) != 0) || (temp_dsn_flags > 0)) /* one_time data or dsn_flags exist */ + { + int len; + int len_orcpt; + + #ifndef COMPILE_UTILITY + DEBUG(D_deliver) debug_printf("**** spool_in dsn_flags = 0\n"); + #endif /* COMPILE_UTILITY */ + + dsn_flags = 0; + + while (isdigit(*(--p)) || *p == ',' || *p == '-'); + sscanf(CS p+1, "%d,%d,%d,%d", &len, &len_orcpt, &pno, &dsn_flags); + + *p = 0; + if (len_orcpt > 0) + { + p -= len_orcpt; + orcpt = string_copy(p); } + *(--p) = 0; /* change the space to a NULL */ + + if (len > 0) + { + p -= len; + errors_to = string_copy(p); + } } *(--p) = 0; /* Terminate address */ } + #endif + #ifndef COMPILE_UTILITY + else + { + DEBUG(D_deliver) debug_printf("**** SPOOL_IN - No additional fields\n"); + } + #endif /* COMPILE_UTILITY */ + + #ifdef SUPPORT_DSN + #ifndef COMPILE_UTILITY + DEBUG(D_deliver) debug_printf("**** SPOOL_IN - address: |%s| errorsto: |%s| orcpt: |%s| dsn_flags: %d\n", + big_buffer, errors_to, orcpt, dsn_flags); + #endif /* COMPILE_UTILITY */ + #endif + #ifndef SUPPORT_DSN + #ifndef COMPILE_UTILITY + DEBUG(D_deliver) debug_printf("**** SPOOL_IN - address: |%s| errorsto: |%s|\n", + big_buffer, errors_to); + #endif /* COMPILE_UTILITY */ + #endif recipients_list[recipients_count].address = string_copy(big_buffer); recipients_list[recipients_count].pno = pno; recipients_list[recipients_count].errors_to = errors_to; + #ifdef SUPPORT_DSN + recipients_list[recipients_count].orcpt = orcpt; + recipients_list[recipients_count].dsn_flags = dsn_flags; + #endif } /* The remainder of the spool header file contains the headers for the message, diff -urN exim-4.64.org/src/spool_out.c exim-4.64/src/spool_out.c --- exim-4.64.org/src/spool_out.c 2006-12-20 10:46:04.000000000 +0100 +++ exim-4.64/src/spool_out.c 2006-12-21 16:43:20.828509500 +0100 @@ -232,6 +232,15 @@ if (tls_peerdn != NULL) fprintf(f, "-tls_peerdn %s\n", tls_peerdn); #endif +#ifdef SUPPORT_DSN +/* Write the dsn flags to the spool header file */ +DEBUG(D_deliver) debug_printf("DSN: Write SPOOL :-dsn_envid %s\n", dsn_envid); +if (dsn_envid != NULL) fprintf(f, "-dsn_envid %s\n", dsn_envid); +DEBUG(D_deliver) debug_printf("DSN: Write SPOOL :-dsn_ret %d\n", dsn_ret); +if (dsn_ret != 0) fprintf(f, "-dsn_ret %d\n", dsn_ret); +#endif + + /* To complete the envelope, write out the tree of non-recipients, followed by the list of recipients. These won't be disjoint the first time, when no checking has been done. If a recipient is a "one-time" alias, it is followed by @@ -242,14 +251,36 @@ for (i = 0; i < recipients_count; i++) { recipient_item *r = recipients_list + i; - if (r->pno < 0 && r->errors_to == NULL) +#ifdef SUPPORT_DSN +DEBUG(D_deliver) debug_printf("DSN: Flags :%d\n", r->dsn_flags); +#endif + if (r->pno < 0 && r->errors_to == NULL + #ifdef SUPPORT_DSN + && r->dsn_flags == 0 + #endif + ) fprintf(f, "%s\n", r->address); else { uschar *errors_to = (r->errors_to == NULL)? US"" : r->errors_to; + #ifdef SUPPORT_DSN + uschar *orcpt = (r->orcpt == NULL)? US"" : r->orcpt; + fprintf(f, "%s %s %s %d,%d,%d,%d!1\n", r->address, errors_to, orcpt, + Ustrlen(errors_to), Ustrlen(orcpt), r->pno, r->dsn_flags); + #else fprintf(f, "%s %s %d,%d#1\n", r->address, errors_to, Ustrlen(errors_to), r->pno); + #endif } + + #ifdef SUPPORT_DSN + DEBUG(D_deliver) debug_printf("DSN :**** SPOOL_OUT - address: |%s| errorsto: |%s| orcpt: |%s| dsn_flags: %d\n", + r->address, r->errors_to, r->orcpt, r->dsn_flags); + #endif + #ifndef SUPPORT_DSN + DEBUG(D_deliver) debug_printf("**** SPOOL_OUT - address: |%s| errorsto: |%s|\n", + r->address, r->errors_to); + #endif } /* Put a blank line before the headers */ diff -urN exim-4.64.org/src/structs.h exim-4.64/src/structs.h --- exim-4.64.org/src/structs.h 2006-12-20 10:46:04.000000000 +0100 +++ exim-4.64/src/structs.h 2006-12-21 16:43:20.828509500 +0100 @@ -281,7 +281,9 @@ BOOL verify_sender; /* Use this router when verifying a sender */ BOOL uid_set; /* Flag to indicate uid is set */ BOOL unseen; /* If TRUE carry on, even after success */ - +#ifdef SUPPORT_DSN + BOOL dsn_process; /* If TRUE, activate DSN for this router */ +#endif int self_code; /* Encoded version of "self" */ uid_t uid; /* Fixed uid value */ gid_t gid; /* Fixed gid value */ @@ -561,6 +563,12 @@ uschar *peerdn; /* DN of server's certificate */ #endif + #ifdef SUPPORT_DSN + uschar *dsn_orcpt; /* DSN orcpt value */ + int dsn_flags; /* DSN flags */ + uschar *dsn_aware; /* DSN aware flag */ + #endif + uid_t uid; /* uid for transporting */ gid_t gid; /* gid for transporting */ diff -urN exim-4.64.org/src/transport.c exim-4.64/src/transport.c --- exim-4.64.org/src/transport.c 2006-12-20 10:46:04.000000000 +0100 +++ exim-4.64/src/transport.c 2006-12-21 16:43:20.832509750 +0100 @@ -1786,6 +1786,11 @@ argv = child_exec_exim(CEE_RETURN_ARGV, TRUE, &i, FALSE, 0); + #ifdef SUPPORT_DSN + /* Call with the dsn flag */ + if (smtp_use_dsn) argv[i++] = US"-MCD"; + #endif + if (smtp_authenticated) argv[i++] = US"-MCA"; #ifdef SUPPORT_TLS diff -urN exim-4.64.org/src/transports/smtp.c exim-4.64/src/transports/smtp.c --- exim-4.64.org/src/transports/smtp.c 2006-12-20 10:46:04.000000000 +0100 +++ exim-4.64/src/transports/smtp.c 2006-12-21 16:43:20.832509750 +0100 @@ -191,6 +191,16 @@ #endif }; +#ifdef SUPPORT_DSN +/* some DSN flags for use later */ + +static int rf_list[] = {rf_notify_never, rf_notify_success, + rf_notify_failure, rf_notify_delay }; + +static uschar *rf_names[] = { "NEVER", "SUCCESS", "FAILURE", "DELAY" }; +#endif + + /* Local statics */ @@ -1152,6 +1162,14 @@ DEBUG(D_transport) debug_printf("%susing PIPELINING\n", smtp_use_pipelining? "" : "not "); + #ifdef SUPPORT_DSN + /* Note if the server supports DSN */ + smtp_use_dsn = dsn && + esmtp && pcre_exec(regex_DSN, NULL, CS buffer, (int)Ustrlen(CS buffer), 0, + PCRE_EOPT, NULL, 0) >= 0; + DEBUG(D_transport) debug_printf("use_dsn=%d\n", smtp_use_dsn); + #endif + /* Note if the response to EHLO specifies support for the AUTH extension. If it has, check that this host is one we want to authenticate to, and do the business. The host name and address must be available when the @@ -1339,6 +1357,26 @@ while (*p) p++; } +#ifdef SUPPORT_DSN +/* Add any DSN flags to the mail command */ + +if (smtp_use_dsn) + { + if (dsn_ret == dsn_ret_hdrs) + { + strcpy(p, " RET=HDRS"); + while (*p) p++; + } + else if (dsn_ret == dsn_ret_full) + { + strcpy(p, " RET=FULL"); + while (*p) p++; + } + if (dsn_envid != NULL) + string_format(p, sizeof(buffer) - (p-buffer), " ENVID=%s", dsn_envid); + } +#endif + /* Add the authenticated sender address if present */ if ((smtp_authenticated || ob->authenticated_sender_force) && @@ -1401,18 +1439,66 @@ int count; BOOL no_flush; + #ifdef SUPPORT_DSN + /* philb - set dsn_aware flag for this recipient */ + if(smtp_use_dsn) + addr->dsn_aware = string_copy("Y"); + else + addr->dsn_aware = string_copy("N"); + #endif + if (addr->transport_return != PENDING_DEFER) continue; address_count++; no_flush = smtp_use_pipelining && (!mua_wrapper || addr->next != NULL); + #ifdef SUPPORT_DSN + /* Add any DSN flags to the rcpt command and add to the sent string */ + + p = buffer; + *p = 0; + + if (smtp_use_dsn) + { + if ((addr->dsn_flags & rf_dsnflags) != 0) + { + int i; + BOOL first = TRUE; + strcpy(p, " NOTIFY="); + while (*p) p++; + for (i = 0; i < 4; i++) + { + if ((addr->dsn_flags & rf_list[i]) != 0) + { + if (!first) *p++ = ','; + first = FALSE; + strcpy(p, rf_names[i]); + while (*p) p++; + } + } + } + + if (addr->dsn_orcpt != NULL) + string_format(p, sizeof(buffer) - (p-buffer), " ORCPT=%s", + addr->dsn_orcpt); + } + + #endif + + /* 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. */ + #ifdef SUPPORT_DSN + count = smtp_write_command(&outblock, no_flush, "RCPT TO:<%s>%s%s\r\n", + transport_rcpt_address(addr, tblock->rcpt_include_affixes), igquotstr, buffer); + #else count = smtp_write_command(&outblock, no_flush, "RCPT TO:<%s>%s\r\n", transport_rcpt_address(addr, tblock->rcpt_include_affixes), igquotstr); + #endif + if (count < 0) goto SEND_FAILED; if (count > 0) {