X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Fdeliver.c;h=6fac6d8db54082cb8a19f5d93e891999f7d8b78e;hb=c562fd30c03126dd72d76ec43842d9d00fcd8bf3;hp=d30b3940202323df39168275926cd214cb88c7a1;hpb=0f9d3aeadcfa071f193901a289144802124a4e2a;p=exim.git diff --git a/src/src/deliver.c b/src/src/deliver.c index d30b39402..6fac6d8db 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -141,11 +141,13 @@ the first address. */ if (addr->host_list == NULL) { deliver_host = deliver_host_address = US""; + deliver_host_port = 0; } else { deliver_host = addr->host_list->name; deliver_host_address = addr->host_list->address; + deliver_host_port = addr->host_list->port; } deliver_recipients = addr; @@ -697,7 +699,15 @@ d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr) 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"); + testflag(addr, af_cert_verified) + ? +#ifdef EXPERIMENTAL_DANE + testflag(addr, af_dane_verified) + ? "dane" + : +#endif + "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"\""); @@ -705,6 +715,73 @@ d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr) } #endif + + + +#ifdef EXPERIMENTAL_TPDA +int +tpda_raise_event(uschar * action, uschar * event, uschar * ev_data) +{ +uschar * s; +if (action) + { + DEBUG(D_deliver) + debug_printf("TPDA(%s): tpda_event_action=|%s| tpda_delivery_IP=%s\n", + event, + action, deliver_host_address); + + tpda_event = event; + tpda_data = ev_data; + + if (!(s = expand_string(action)) && *expand_string_message) + log_write(0, LOG_MAIN|LOG_PANIC, + "failed to expand tpda_event_action %s in %s: %s\n", + event, transport_name, expand_string_message); + + tpda_event = tpda_data = NULL; + + /* If the expansion returns anything but an empty string, flag for + the caller to modify his normal processing + */ + if (s && *s) + { + DEBUG(D_deliver) + debug_printf("TPDA(%s): event_action returned \"%s\"\n", event, s); + return DEFER; + } + } +return OK; +} + +static void +tpda_msg_event(uschar * event, address_item * addr) +{ +uschar * save_domain = deliver_domain; +uschar * save_local = deliver_localpart; +uschar * save_host = deliver_host; + +if (!addr->transport) + return; + +router_name = addr->router ? addr->router->name : NULL; +transport_name = addr->transport->name; +deliver_domain = addr->domain; +deliver_localpart = addr->local_part; +deliver_host = addr->host_used ? addr->host_used->name : NULL; + +(void) tpda_raise_event(addr->transport->tpda_event_action, event, + addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0 + ? addr->message : NULL); + +deliver_host = save_host; +deliver_localpart = save_local; +deliver_domain = save_domain; +router_name = transport_name = NULL; +} +#endif /*EXPERIMENTAL_TPDA*/ + + + /* If msg is NULL this is a delivery log and logchar is used. Otherwise 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. @@ -721,19 +798,13 @@ int ptr = 0; /* expanding buffer, for */ uschar *s; /* building log lines; */ void *reset_point; /* released afterwards. */ - /* Log the delivery on the main log. We use an extensible string to build up the log line, and reset the store afterwards. Remote deliveries should always 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; + /* presume no successful remote delivery */ lookup_dnssec_authenticated = NULL; #endif @@ -782,13 +853,8 @@ if ((log_extra_selector & LX_delivery_size) != 0) if (addr->transport->info->local) { - if (addr->host_list != NULL) - { + if (addr->host_list) 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)); @@ -804,24 +870,20 @@ else 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; +#ifdef EXPERIMENTAL_TPDA + deliver_host_address = addr->host_used->address; + deliver_host_port = addr->host_used->port; /* DNS lookup status */ lookup_dnssec_authenticated = addr->host_used->dnssec==DS_YES ? US"yes" : addr->host_used->dnssec==DS_NO ? US"no" : NULL; - #endif +#endif } - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS s = d_tlslog(s, &size, &ptr, addr); - #endif +#endif if (addr->authenticator) { @@ -834,10 +896,10 @@ else } } - #ifndef DISABLE_PRDR +#ifndef DISABLE_PRDR if (addr->flags & af_prdr_used) s = string_append(s, &size, &ptr, 1, US" PRDR"); - #endif +#endif } /* confirmation message (SMTP (host_used) and LMTP (driver_name)) */ @@ -877,21 +939,9 @@ 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; - } +if (!msg) tpda_msg_event(US"msg:delivery", addr); #endif + store_reset(reset_point); return; } @@ -930,7 +980,6 @@ int ptr = 0; /* expanding buffer, for */ uschar *s; /* building log lines; */ void *reset_point; /* released afterwards. */ - DEBUG(D_deliver) debug_printf("post-process %s (%d)\n", addr->address, result); /* Set up driver kind and name for logging. Disable logging if the router or @@ -1089,7 +1138,7 @@ if (result == OK) } /* Certificates for logging (via TPDA) */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS tls_out.ourcert = addr->ourcert; addr->ourcert = NULL; tls_out.peercert = addr->peercert; @@ -1098,11 +1147,14 @@ if (result == OK) tls_out.cipher = addr->cipher; tls_out.peerdn = addr->peerdn; tls_out.ocsp = addr->ocsp; - #endif +# ifdef EXPERIMENTAL_DANE + tls_out.dane_verified = testflag(addr, af_dane_verified); +# endif +#endif delivery_log(LOG_MAIN, addr, logchar, NULL); - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (tls_out.ourcert) { tls_free_cert(tls_out.ourcert); @@ -1116,7 +1168,10 @@ if (result == OK) tls_out.cipher = NULL; tls_out.peerdn = NULL; tls_out.ocsp = OCSP_NOT_REQ; - #endif +# ifdef EXPERIMENTAL_DANE + tls_out.dane_verified = FALSE; +# endif +#endif } @@ -1200,6 +1255,11 @@ else if (result == DEFER || result == PANIC) s = string_append(s, &size, &ptr, 2, US": ", US strerror(addr->basic_errno)); + if (addr->host_used) + s = string_append(s, &size, &ptr, 5, + US" H=", addr->host_used->name, + US" [", addr->host_used->address, US"]"); + if (addr->message != NULL) s = string_append(s, &size, &ptr, 2, US": ", addr->message); @@ -1297,9 +1357,9 @@ else if (addr->host_used != NULL) s = d_hostlog(s, &size, &ptr, addr); - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS s = d_tlslog(s, &size, &ptr, addr); - #endif +#endif if (addr->basic_errno > 0) s = string_append(s, &size, &ptr, 2, US": ", @@ -1319,6 +1379,11 @@ else deliver_msglog("%s %s\n", now, s); log_write(0, LOG_MAIN, "** %s", s); + +#ifdef EXPERIMENTAL_TPDA + tpda_msg_event(US"msg:fail:delivery", addr); +#endif + store_reset(reset_point); } @@ -1888,19 +1953,19 @@ if ((pid = fork()) == 0) diagnosis that it's reasonable to make them something that has to be explicitly requested. */ - #ifdef RLIMIT_CORE +#ifdef RLIMIT_CORE struct rlimit rl; rl.rlim_cur = 0; rl.rlim_max = 0; if (setrlimit(RLIMIT_CORE, &rl) < 0) { - #ifdef SETRLIMIT_NOT_SUPPORTED +# ifdef SETRLIMIT_NOT_SUPPORTED if (errno != ENOSYS && errno != ENOTSUP) - #endif +# endif log_write(0, LOG_MAIN|LOG_PANIC, "setrlimit(RLIMIT_CORE) failed: %s", strerror(errno)); } - #endif +#endif /* Reset the random number generator, so different processes don't all have the same sequence. */ @@ -2823,6 +2888,8 @@ uschar *ptr = endptr; uschar *msg = p->msg; BOOL done = p->done; BOOL unfinished = TRUE; +/* minimum size to read is header size including id, subid and length */ +int required = PIPE_HEADER_SIZE; /* Loop through all items, reading from the pipe when necessary. The pipe is set up to be non-blocking, but there are two different Unix mechanisms in @@ -2845,12 +2912,15 @@ while (!done) { retry_item *r, **rp; int remaining = endptr - ptr; + uschar header[PIPE_HEADER_SIZE + 1]; + uschar id, subid; + uschar *endc; /* Read (first time) or top up the chars in the buffer if necessary. There will be only one read if we get all the available data (i.e. don't fill the buffer completely). */ - if (remaining < 2500 && unfinished) + if (remaining < required && unfinished) { int len; int available = big_buffer_size - remaining; @@ -2883,17 +2953,63 @@ while (!done) won't read any more, as "unfinished" will get set FALSE. */ endptr += len; + remaining += len; unfinished = len == available; } /* If we are at the end of the available data, exit the loop. */ - if (ptr >= endptr) break; + /* copy and read header */ + memcpy(header, ptr, PIPE_HEADER_SIZE); + header[PIPE_HEADER_SIZE] = '\0'; + id = header[0]; + subid = header[1]; + required = Ustrtol(header + 2, &endc, 10) + PIPE_HEADER_SIZE; /* header + data */ + if (*endc) + { + msg = string_sprintf("failed to read pipe from transport process " + "%d for transport %s: error reading size from header", pid, addr->transport->driver_name); + done = TRUE; + break; + } + + DEBUG(D_deliver) + debug_printf("header read id:%c,subid:%c,size:%s,required:%d,remaining:%d,unfinished:%d\n", + id, subid, header+2, required, remaining, unfinished); + + /* is there room for the dataset we want to read ? */ + if (required > big_buffer_size - PIPE_HEADER_SIZE) + { + msg = string_sprintf("failed to read pipe from transport process " + "%d for transport %s: big_buffer too small! required size=%d buffer size=%d", pid, addr->transport->driver_name, + required, big_buffer_size - PIPE_HEADER_SIZE); + done = TRUE; + break; + } + + /* we wrote all datasets with atomic write() calls + remaining < required only happens if big_buffer was too small + to get all available data from pipe. unfinished has to be true + as well. */ + if (remaining < required) + { + if (unfinished) + continue; + msg = string_sprintf("failed to read pipe from transport process " + "%d for transport %s: required size=%d > remaining size=%d and unfinished=false", + pid, addr->transport->driver_name, required, remaining); + done = TRUE; + break; + } + + /* step behind the header */ + ptr += PIPE_HEADER_SIZE; + /* Handle each possible type of item, assuming the complete item is available in store. */ - switch (*ptr++) + switch (id) { /* Host items exist only if any hosts were marked unusable. Match up by checking the IP address. */ @@ -2987,10 +3103,10 @@ while (!done) it in with the other info, in order to keep each message short enough to guarantee it won't be split in the pipe. */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS case 'X': if (addr == NULL) goto ADDR_MISMATCH; /* Below, in 'A' handler */ - switch (*ptr++) + switch (subid) { case '1': addr->cipher = NULL; @@ -3015,20 +3131,20 @@ while (!done) (void) tls_import_cert(ptr, &addr->ourcert); break; - #ifndef DISABLE_OCSP +# ifndef DISABLE_OCSP case '4': addr->ocsp = OCSP_NOT_REQ; if (*ptr) addr->ocsp = *ptr - '0'; break; - #endif +# endif } while (*ptr++); break; - #endif /*SUPPORT_TLS*/ +#endif /*SUPPORT_TLS*/ case 'C': /* client authenticator information */ - switch (*ptr++) + switch (subid) { case '1': addr->authenticator = (*ptr)? string_copy(ptr) : NULL; @@ -3049,14 +3165,14 @@ while (!done) break; #endif - #ifdef EXPERIMENTAL_DSN +#ifdef EXPERIMENTAL_DSN case 'D': - if (addr == NULL) break; + if (addr == NULL) goto ADDR_MISMATCH; 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 +#endif case 'A': if (addr == NULL) @@ -3119,7 +3235,7 @@ while (!done) continue_hostname = NULL; } done = TRUE; - DEBUG(D_deliver) debug_printf("Z%c item read\n", *ptr); + DEBUG(D_deliver) debug_printf("Z0%c item read\n", *ptr); break; /* Anything else is a disaster. */ @@ -3572,9 +3688,40 @@ while (parcount > max) static void -rmt_dlv_checked_write(int fd, void * buf, int size) +rmt_dlv_checked_write(int fd, char id, char subid, void * buf, int size) +{ +uschar writebuffer[PIPE_HEADER_SIZE + BIG_BUFFER_SIZE]; +int header_length; + +/* we assume that size can't get larger then BIG_BUFFER_SIZE which currently is set to 16k */ +/* complain to log if someone tries with buffer sizes we can't handle*/ + +if (size > 99999) +{ + log_write(0, LOG_MAIN|LOG_PANIC_DIE, + "Failed writing transport result to pipe: can't handle buffers > 99999 bytes. truncating!\n"); + size = 99999; +} + +/* to keep the write() atomic we build header in writebuffer and copy buf behind */ +/* two write() calls would increase the complexity of reading from pipe */ + +/* convert size to human readable string prepended by id and subid */ +header_length = snprintf(CS writebuffer, PIPE_HEADER_SIZE+1, "%c%c%05d", id, subid, size); +if (header_length != PIPE_HEADER_SIZE) { -int ret = write(fd, buf, size); + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "header snprintf failed\n"); + writebuffer[0] = '\0'; +} + +DEBUG(D_deliver) debug_printf("header write id:%c,subid:%c,size:%d,final:%s\n", + id, subid, size, writebuffer); + +if (buf && size > 0) + memcpy(writebuffer + PIPE_HEADER_SIZE, buf, size); + +size += PIPE_HEADER_SIZE; +int ret = write(fd, writebuffer, size); if(ret != size) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Failed writing transport result to pipe: %s\n", ret == -1 ? strerror(errno) : "short write"); @@ -3954,11 +4101,11 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) that it can use either of them, though it prefers O_NONBLOCK, which distinguishes between EOF and no-more-data. */ - #ifdef O_NONBLOCK +#ifdef O_NONBLOCK (void)fcntl(pfd[pipe_read], F_SETFL, O_NONBLOCK); - #else +#else (void)fcntl(pfd[pipe_read], F_SETFL, O_NDELAY); - #endif +#endif /* If the maximum number of subprocesses already exist, wait for a process to finish. If we ran out of file descriptors, parmax will have been reduced @@ -4100,8 +4247,8 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) for (h = addr->host_list; h != NULL; h = h->next) { if (h->address == NULL || h->status < hstatus_unusable) continue; - sprintf(CS big_buffer, "H%c%c%s", h->status, h->why, h->address); - rmt_dlv_checked_write(fd, big_buffer, Ustrlen(big_buffer+3) + 4); + sprintf(CS big_buffer, "%c%c%s", h->status, h->why, h->address); + rmt_dlv_checked_write(fd, 'H', '0', big_buffer, Ustrlen(big_buffer+2) + 3); } /* The number of bytes written. This is the same for each address. Even @@ -4109,9 +4256,8 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) size of each one is the same, and it's that value we have got because transport_count gets reset before calling transport_write_message(). */ - big_buffer[0] = 'S'; - memcpy(big_buffer+1, &transport_count, sizeof(transport_count)); - rmt_dlv_checked_write(fd, big_buffer, sizeof(transport_count) + 1); + memcpy(big_buffer, &transport_count, sizeof(transport_count)); + rmt_dlv_checked_write(fd, 'S', '0', big_buffer, sizeof(transport_count)); /* Information about what happened to each address. Four item types are used: an optional 'X' item first, for TLS information, then an optional "C" @@ -4125,13 +4271,16 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) /* The certificate verification status goes into the flags */ if (tls_out.certificate_verified) setflag(addr, af_cert_verified); +#ifdef EXPERIMENTAL_DANE + if (tls_out.dane_verified) setflag(addr, af_dane_verified); +#endif /* Use an X item only if there's something to send */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (addr->cipher) { ptr = big_buffer; - sprintf(CS ptr, "X1%.128s", addr->cipher); + sprintf(CS ptr, "%.128s", addr->cipher); while(*ptr++); if (!addr->peerdn) *ptr++ = 0; @@ -4141,79 +4290,76 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) while(*ptr++); } - rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer); + rmt_dlv_checked_write(fd, 'X', '1', big_buffer, ptr - big_buffer); } 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); + rmt_dlv_checked_write(fd, 'X', '2', 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); + rmt_dlv_checked_write(fd, 'X', '3', big_buffer, ptr - big_buffer); } - #ifndef DISABLE_OCSP +# ifndef DISABLE_OCSP if (addr->ocsp > OCSP_NOT_REQ) { ptr = big_buffer; - sprintf(CS ptr, "X4%c", addr->ocsp + '0'); + sprintf(CS ptr, "%c", addr->ocsp + '0'); while(*ptr++); - rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer); + rmt_dlv_checked_write(fd, 'X', '4', big_buffer, ptr - big_buffer); } - # endif - #endif /*SUPPORT_TLS*/ +# endif +#endif /*SUPPORT_TLS*/ if (client_authenticator) { ptr = big_buffer; - sprintf(CS big_buffer, "C1%.64s", client_authenticator); + sprintf(CS big_buffer, "%.64s", client_authenticator); while(*ptr++); - rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer); + rmt_dlv_checked_write(fd, 'C', '1', big_buffer, ptr - big_buffer); } if (client_authenticated_id) { ptr = big_buffer; - sprintf(CS big_buffer, "C2%.64s", client_authenticated_id); + sprintf(CS big_buffer, "%.64s", client_authenticated_id); while(*ptr++); - rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer); + rmt_dlv_checked_write(fd, 'C', '2', big_buffer, ptr - big_buffer); } if (client_authenticated_sender) { ptr = big_buffer; - sprintf(CS big_buffer, "C3%.64s", client_authenticated_sender); + sprintf(CS big_buffer, "%.64s", client_authenticated_sender); while(*ptr++); - rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer); + rmt_dlv_checked_write(fd, 'C', '3', big_buffer, ptr - big_buffer); } - #ifndef DISABLE_PRDR +#ifndef DISABLE_PRDR if (addr->flags & af_prdr_used) - rmt_dlv_checked_write(fd, "P", 1); - #endif + rmt_dlv_checked_write(fd, 'P', '0', NULL, 0); +#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); +#ifdef EXPERIMENTAL_DSN + memcpy(big_buffer, &addr->dsn_aware, sizeof(addr->dsn_aware)); + rmt_dlv_checked_write(fd, 'D', '0', big_buffer, sizeof(addr->dsn_aware)); DEBUG(D_deliver) debug_printf("DSN write: addr->dsn_aware = %d\n", addr->dsn_aware); - #endif +#endif /* Retry information: for most success cases this will be null. */ for (r = addr->retries; r != NULL; r = r->next) { uschar *ptr; - sprintf(CS big_buffer, "R%c%.500s", r->flags, r->key); + sprintf(CS big_buffer, "%c%.500s", r->flags, r->key); ptr = big_buffer + Ustrlen(big_buffer+2) + 3; memcpy(ptr, &(r->basic_errno), sizeof(r->basic_errno)); ptr += sizeof(r->basic_errno); @@ -4224,13 +4370,13 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) sprintf(CS ptr, "%.512s", r->message); while(*ptr++); } - rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer); + rmt_dlv_checked_write(fd, 'R', '0', big_buffer, ptr - big_buffer); } /* The rest of the information goes in an 'A' item. */ - ptr = big_buffer + 3; - sprintf(CS big_buffer, "A%c%c", addr->transport_return, + ptr = big_buffer + 2; + sprintf(CS big_buffer, "%c%c", addr->transport_return, addr->special_action); memcpy(ptr, &(addr->basic_errno), sizeof(addr->basic_errno)); ptr += sizeof(addr->basic_errno); @@ -4265,7 +4411,7 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) : addr->host_used->dnssec==DS_NO ? '1' : '0'; } - rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer); + rmt_dlv_checked_write(fd, 'A', '0', big_buffer, ptr - big_buffer); } /* Add termination flag, close the pipe, and that's it. The character @@ -4273,9 +4419,8 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) A change from non-NULL to NULL indicates a problem with a continuing connection. */ - big_buffer[0] = 'Z'; - big_buffer[1] = (continue_transport == NULL)? '0' : '1'; - rmt_dlv_checked_write(fd, big_buffer, 2); + big_buffer[0] = (continue_transport == NULL)? '0' : '1'; + rmt_dlv_checked_write(fd, 'Z', '0', big_buffer, 1); (void)close(fd); exit(EXIT_SUCCESS); } @@ -4577,6 +4722,10 @@ if (ancestor != addr) string_printing(original)); } +if (addr->host_used) + fprintf(f, "\n host %s [%s]", + addr->host_used->name, addr->host_used->address); + fprintf(f, "%s", CS se); return yield; } @@ -4921,7 +5070,7 @@ attempted. */ if (deliver_freeze) { - #ifdef SUPPORT_MOVE_FROZEN_MESSAGES +#ifdef SUPPORT_MOVE_FROZEN_MESSAGES /* Moving to another directory removes the message from Exim's view. Other tools must be used to deal with it. Logging of this action happens in spool_move_message() and its subfunctions. */ @@ -4929,7 +5078,7 @@ if (deliver_freeze) if (move_frozen_messages && spool_move_message(id, message_subdir, US"", US"F")) return continue_closedown(); /* yields DELIVER_NOT_ATTEMPTED */ - #endif +#endif /* For all frozen messages (bounces or not), timeout_frozen_after sets the maximum time to keep messages that are frozen. Thaw if we reach it, with a @@ -5358,13 +5507,13 @@ if (process_recipients != RECIP_IGNORE) if (r->pno >= 0) new->onetime_parent = recipients_list[r->pno].address; - #ifdef EXPERIMENTAL_DSN +#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 +#endif switch (process_recipients) { @@ -5434,6 +5583,25 @@ if (process_recipients != RECIP_IGNORE) addr_last = new; break; } + +#ifdef EXPERIMENTAL_TPDA + if (process_recipients != RECIP_ACCEPT) + { + uschar * save_local = deliver_localpart; + uschar * save_domain = deliver_domain; + + deliver_localpart = expand_string( + string_sprintf("${local_part:%s}", new->address)); + deliver_domain = expand_string( + string_sprintf("${domain:%s}", new->address)); + + (void) tpda_raise_event(delivery_event_action, + US"msg:fail:internal", new->message); + + deliver_localpart = save_local; + deliver_domain = save_domain; + } +#endif } } } @@ -6300,21 +6468,21 @@ if (addr_remote != NULL) regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)", FALSE, TRUE); - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (regex_STARTTLS == NULL) regex_STARTTLS = regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE); - #endif +#endif - #ifndef DISABLE_PRDR +#ifndef DISABLE_PRDR if (regex_PRDR == NULL) regex_PRDR = regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE); - #endif +#endif - #ifdef EXPERIMENTAL_DSN +#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 +#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 @@ -6390,6 +6558,7 @@ if (mua_wrapper) { uschar *s = (addr_failed->user_message != NULL)? addr_failed->user_message : addr_failed->message; + host_item * host; fprintf(stderr, "Delivery failed: "); if (addr_failed->basic_errno > 0) @@ -6397,6 +6566,8 @@ if (mua_wrapper) fprintf(stderr, "%s", strerror(addr_failed->basic_errno)); if (s != NULL) fprintf(stderr, ": "); } + if ((host = addr_failed->host_used)) + fprintf(stderr, "H=%s [%s]: ", host->name, host->address); if (s == NULL) { if (addr_failed->basic_errno <= 0) fprintf(stderr, "unknown error"); @@ -6497,71 +6668,73 @@ if (addr_senddsn != NULL) time(NULL), rand()); DEBUG(D_deliver) debug_printf("DSN: MIME boundary: %s\n", boundaryStr); - if (errors_reply_to != NULL) fprintf(f,"Reply-To: %s\n", errors_reply_to); + if (errors_reply_to) + 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, "Auto-Submitted: auto-generated\n" + "From: Mail Delivery System \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" - fprintf(f,"This message was created automatically by mail delivery software.\n"); - fprintf(f," ----- The following addresses had successful delivery notifications -----\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 != NULL) + while(addr_dsntmp) { - if ((addr_dsntmp->dsn_flags & rf_dsnlasthop) == 1) { - fprintf(f,"<%s> (relayed via non DSN router)\n\n", addr_dsntmp->address); - } - else if (addr_dsntmp->dsn_aware == dsn_support_no) { - 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> (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", boundaryStr); - fprintf(f,"Content-type: message/delivery-status\n\n"); - - fprintf(f,"Reporting-MTA: dns; %s\n", smtp_active_hostname); + 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); + fprintf(f, "Original-Envelope-ID: %s\n", dsn_envid); else - fprintf(f,"X-Original-Envelope-ID: error decoding xtext formated ENVID\n"); + fprintf(f, "X-Original-Envelope-ID: error decoding xtext formated ENVID\n"); } - fprintf(f,"\n"); + fputc('\n', f); - addr_dsntmp = addr_senddsn; - while(addr_dsntmp != NULL) + for (addr_dsntmp = addr_senddsn; + addr_dsntmp; + addr_dsntmp = addr_dsntmp->next) { - if (addr_dsntmp->dsn_orcpt != NULL) { + if (addr_dsntmp->dsn_orcpt) fprintf(f,"Original-Recipient: %s\n", addr_dsntmp->dsn_orcpt); - } - fprintf(f,"Action: delivered\n"); - fprintf(f,"Final-Recipient: rfc822;%s\n", addr_dsntmp->address); - fprintf(f,"Status: 2.0.0\n"); - if ((addr_dsntmp->host_used != NULL) && (addr_dsntmp->host_used->name != NULL)) - fprintf(f,"Remote-MTA: dns; %s\nDiagnostic-Code: smtp; 250 Ok\n", addr_dsntmp->host_used->name); + + 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 - if ((addr_dsntmp->dsn_flags & rf_dsnlasthop) == 1) - fprintf(f,"Diagnostic-Code: X-Exim; relayed via non DSN router\n"); - else - fprintf(f,"Diagnostic-Code: X-Exim; relayed via non SMTP router\n"); - fprintf(f,"\n"); - addr_dsntmp = addr_dsntmp->next; + 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\n", boundaryStr); - fprintf(f,"Content-type: text/rfc822-headers\n\n"); + fprintf(f, "--%s\nContent-type: text/rfc822-headers\n\n", boundaryStr); fflush(f); transport_filter_argv = NULL; /* Just in case */ @@ -6579,7 +6752,7 @@ if (addr_senddsn != NULL) rc = child_close(pid, 0); /* Waits for child to close, no timeout */ } } -#endif +#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 @@ -6757,68 +6930,68 @@ while (addr_failed != NULL) #ifdef EXPERIMENTAL_DSN /* generate boundary string and output MIME-Headers */ - snprintf(boundaryStr, 63, "%l-eximdsn-%d", (long) time(NULL), rand()); - fprintf(f,"Content-Type: multipart/report; report-type=delivery-status; boundary=%s\n", boundaryStr); - fprintf(f,"MIME-Version: 1.0\n"); + 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" : ""); - } #ifdef EXPERIMENTAL_DSN /* output human readable part as text/plain section */ - fprintf(f,"--%s\n", boundaryStr); - fprintf(f,"Content-type: text/plain; charset=us-ascii\n\n"); + fprintf(f, "--%s\n" + "Content-type: text/plain; charset=us-ascii\n\n", + boundaryStr); #endif - emf_text = next_emf(emf, US"intro"); - if (emf_text != NULL) fprintf(f, "%s", CS emf_text); else + 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 @@ -6855,7 +7028,7 @@ wording. */ } } - 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. */ @@ -6869,11 +7042,13 @@ wording. */ 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" : ""); @@ -6885,15 +7060,15 @@ wording. */ /* 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 */ @@ -6916,32 +7091,36 @@ wording. */ addr->next = handled_addr; handled_addr = topaddr; } - fprintf(f, "\n"); + fputc('\n', f); } #ifdef EXPERIMENTAL_DSN /* output machine readable part */ - fprintf(f,"--%s\n", boundaryStr); - fprintf(f,"Content-type: message/delivery-status\n\n"); - - fprintf(f,"Reporting-MTA: dns; %s\n", smtp_active_hostname); - if (dsn_envid != NULL) { + 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); + fprintf(f, "Original-Envelope-ID: %s\n", dsn_envid); else - fprintf(f,"X-Original-Envelope-ID: error decoding xtext formated ENVID\n"); + fprintf(f, "X-Original-Envelope-ID: error decoding xtext formated ENVID\n"); } - fprintf(f,"\n"); + fputc('\n', f); - for (addr = handled_addr; addr != NULL; addr = addr->next) + for (addr = handled_addr; addr; addr = addr->next) { - fprintf(f,"Action: failed\n"); - fprintf(f,"Final-Recipient: rfc822;%s\n", addr->address); - fprintf(f,"Status: 5.0.0\n"); - if ((addr->host_used != NULL) && (addr->host_used->name != NULL)) - fprintf(f,"Remote-MTA: dns; %s\nDiagnostic-Code: smtp; %d\n", addr->host_used->name, addr->basic_errno); + 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 @@ -6958,7 +7137,9 @@ wording. */ 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"); @@ -6981,18 +7162,19 @@ wording. */ { 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, @@ -7001,10 +7183,10 @@ wording. */ /* 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 @@ -7019,7 +7201,7 @@ wording. */ bounce_return_size_limit is always honored. */ - fprintf(f,"\n--%s\n", boundaryStr); + fprintf(f, "\n--%s\n", boundaryStr); dsnlimitmsg = US"X-Exim-DSN-Information: Due to administrative limits only headers are returned"; dsnnotifyhdr = NULL; @@ -7061,12 +7243,11 @@ wording. */ fflush(f); /* we never add the final text. close the file */ - if (emf != NULL) + if (emf) (void)fclose(emf); - fprintf(f,"\n"); - fprintf(f,"--%s--\n", boundaryStr); -#endif + 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. */ @@ -7181,7 +7362,11 @@ if (addr_defer == NULL) /* Unset deliver_freeze so that we won't try to move the spool files further down */ deliver_freeze = FALSE; - } + +#ifdef EXPERIMENTAL_TPDA + (void) tpda_raise_event(delivery_event_action, US"msg:complete", NULL); +#endif +} /* If there are deferred addresses, we are keeping this message because it is not yet completed. Lose any temporary files that were catching output from @@ -7370,7 +7555,7 @@ else if (addr_defer != (address_item *)(+1)) uschar boundaryStr[64]; #endif - if (warn_message_file != NULL) + if (warn_message_file) { wmf = Ufopen(warn_message_file, "rb"); if (wmf == NULL) @@ -7383,7 +7568,7 @@ else if (addr_defer != (address_item *)(+1)) 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); @@ -7391,13 +7576,16 @@ else if (addr_defer != (address_item *)(+1)) #ifdef EXPERIMENTAL_DSN /* generated boundary string and output MIME-Headers */ - snprintf(boundaryStr, 63, "%l-eximdsn-%d", (long) time(NULL), rand()); - fprintf(f,"Content-Type: multipart/report; report-type=delivery-status; boundary=%s\n", boundaryStr); - fprintf(f,"MIME-Version: 1.0\n"); + 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 - wmf_text = next_emf(wmf, US"header"); - if (wmf_text != NULL) + 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", @@ -7405,12 +7593,14 @@ else if (addr_defer != (address_item *)(+1)) #ifdef EXPERIMENTAL_DSN /* output human readable part as text/plain section */ - fprintf(f,"--%s\n", boundaryStr); - fprintf(f,"Content-type: text/plain; charset=us-ascii\n\n"); + fprintf(f, "--%s\n" + "Content-type: text/plain; charset=us-ascii\n\n", + boundaryStr); #endif - wmf_text = next_emf(wmf, US"intro"); - if (wmf_text != NULL) fprintf(f, "%s", CS wmf_text); else + 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"); @@ -7420,28 +7610,27 @@ else if (addr_defer != (address_item *)(+1)) "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 */ @@ -7450,23 +7639,23 @@ else if (addr_defer != (address_item *)(+1)) /* store addr_defer for machine readable part */ address_item *addr_dsndefer = addr_defer; #endif - fprintf(f, "\n"); - while (addr_defer != NULL) + 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 @@ -7480,11 +7669,15 @@ else if (addr_defer != (address_item *)(+1)) #ifdef EXPERIMENTAL_DSN /* output machine readable part */ - fprintf(f,"\n--%s\n", boundaryStr); - fprintf(f,"Content-type: message/delivery-status\n\n"); + fprintf(f, "\n--%s\n" + "Content-type: message/delivery-status\n\n" + "Reporting-MTA: dns; %s\n", + boundaryStr, + smtp_active_hostname); - fprintf(f,"Reporting-MTA: dns; %s\n", smtp_active_hostname); - if (dsn_envid != NULL) { + + if (dsn_envid) + { /* must be decoded from xtext: see RFC 3461:6.3a */ uschar *xdec_envid; if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0) @@ -7492,24 +7685,25 @@ else if (addr_defer != (address_item *)(+1)) else fprintf(f,"X-Original-Envelope-ID: error decoding xtext formated ENVID\n"); } - fprintf(f,"\n"); + fputc('\n', f); - while (addr_dsndefer != NULL) + while (addr_dsndefer) { - if (addr_dsndefer->dsn_orcpt != NULL) { + 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 != NULL) && (addr_dsndefer->host_used->name != NULL)) + 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->host_used->name, addr_dsndefer->basic_errno); addr_dsndefer = addr_dsndefer->next; } - fprintf(f,"\n--%s\n", boundaryStr); - fprintf(f,"Content-type: text/rfc822-headers\n\n"); + 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 */ @@ -7520,11 +7714,10 @@ else if (addr_defer != (address_item *)(+1)) transport_write_message(NULL, fileno(f), topt, 0, NULL, NULL, NULL, NULL, NULL, 0); fflush(f); - fprintf(f,"\n"); - fprintf(f,"--%s--\n", boundaryStr); + fprintf(f,"\n--%s--\n", boundaryStr); fflush(f); -#endif +#endif /*EXPERIMENTAL_DSN*/ /* Close and wait for child process to complete, without a timeout. If there's an error, don't update the count. */ @@ -7637,10 +7830,10 @@ if (remove_journal) /* Move the message off the spool if reqested */ - #ifdef SUPPORT_MOVE_FROZEN_MESSAGES +#ifdef SUPPORT_MOVE_FROZEN_MESSAGES if (deliver_freeze && move_frozen_messages) (void)spool_move_message(id, message_subdir, US"", US"F"); - #endif +#endif } /* Closing the data file frees the lock; if the file has been unlinked it