X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/8bc732e8b03ebb4309f4b42626917148d176db49..ff5aac2be82177a7c96c71ce7dd0939e70436402:/src/src/transport.c diff --git a/src/src/transport.c b/src/src/transport.c index 15c30bf04..daf334c9b 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2016 */ /* See the file NOTICE for conditions of use and distribution. */ /* General functions concerned with transportation, and generic options for all @@ -66,6 +66,10 @@ optionlist optionlist_transports[] = { (void *)offsetof(transport_instance, driver_name) }, { "envelope_to_add", opt_bool|opt_public, (void *)(offsetof(transport_instance, envelope_to_add)) }, +#ifndef DISABLE_EVENT + { "event_action", opt_stringptr | opt_public, + (void *)offsetof(transport_instance, event_action) }, +#endif { "group", opt_expand_gid|opt_public, (void *)offsetof(transport_instance, gid) }, { "headers_add", opt_stringptr|opt_public|opt_rep_str, @@ -80,6 +84,8 @@ optionlist optionlist_transports[] = { (void *)offsetof(transport_instance, home_dir) }, { "initgroups", opt_bool|opt_public, (void *)offsetof(transport_instance, initgroups) }, + { "max_parallel", opt_stringptr|opt_public, + (void *)offsetof(transport_instance, max_parallel) }, { "message_size_limit", opt_stringptr|opt_public, (void *)offsetof(transport_instance, message_size_limit) }, { "rcpt_include_affixes", opt_bool|opt_public, @@ -94,10 +100,6 @@ optionlist optionlist_transports[] = { (void *)offsetof(transport_instance, shadow_condition) }, { "shadow_transport", opt_stringptr|opt_public, (void *)offsetof(transport_instance, shadow) }, -#ifdef EXPERIMENTAL_TPDA - { "tpda_event_action",opt_stringptr | opt_public, - (void *)offsetof(transport_instance, tpda_event_action) }, -#endif { "transport_filter", opt_stringptr|opt_public, (void *)offsetof(transport_instance, filter_command) }, { "transport_filter_timeout", opt_time|opt_public, @@ -367,7 +369,7 @@ write_chunk(int fd, uschar *chunk, int len, BOOL use_crlf) { uschar *start = chunk; uschar *end = chunk + len; -register uschar *ptr; +uschar *ptr; int mlen = DELIVER_OUT_BUFFER_SIZE - nl_escape_length - 2; /* The assumption is made that the check string will never stretch over move @@ -406,7 +408,7 @@ possible. */ for (ptr = start; ptr < end; ptr++) { - register int ch; + int ch; /* Flush the buffer if it has reached the threshold - we want to leave enough room for the next uschar, plus a possible extra CR for an LF, plus the escape @@ -558,8 +560,7 @@ struct aci *ppp; /* Do nothing if we have already handled this address. If not, remember it so that we don't handle it again. */ -for (ppp = *pdlist; ppp != NULL; ppp = ppp->next) - { if (p == ppp->ptr) return TRUE; } +for (ppp = *pdlist; ppp; ppp = ppp->next) if (p == ppp->ptr) return TRUE; ppp = store_get(sizeof(struct aci)); ppp->next = *pdlist; @@ -571,19 +572,17 @@ ppp->ptr = p; for (pp = p;; pp = pp->parent) { address_item *dup; - for (dup = addr_duplicate; dup != NULL; dup = dup->next) - { - if (dup->dupof != pp) continue; /* Not a dup of our address */ - if (!write_env_to(dup, pplist, pdlist, first, fd, use_crlf)) return FALSE; - } - if (pp->parent == NULL) break; + for (dup = addr_duplicate; dup; dup = dup->next) + if (dup->dupof == pp) /* a dup of our address */ + if (!write_env_to(dup, pplist, pdlist, first, fd, use_crlf)) + return FALSE; + if (!pp->parent) break; } /* Check to see if we have already output the progenitor. */ -for (ppp = *pplist; ppp != NULL; ppp = ppp->next) - { if (pp == ppp->ptr) break; } -if (ppp != NULL) return TRUE; +for (ppp = *pplist; ppp; ppp = ppp->next) if (pp == ppp->ptr) break; +if (ppp) return TRUE; /* Remember what we have output, and output it. */ @@ -628,16 +627,16 @@ that means they were rewritten, or are a record of envelope rewriting, or were removed (e.g. Bcc). If remove_headers is not null, skip any headers that match any entries therein. It is a colon-sep list; expand the items separately and squash any empty ones. -Then check addr->p.remove_headers too, provided that addr is not NULL. */ +Then check addr->prop.remove_headers too, provided that addr is not NULL. */ -for (h = header_list; h != NULL; h = h->next) if (h->type != htype_old) +for (h = header_list; h; h = h->next) if (h->type != htype_old) { int i; - uschar *list = remove_headers; + const uschar *list = remove_headers; BOOL include_header = TRUE; - for (i = 0; i < 2; i++) /* For remove_headers && addr->p.remove_headers */ + for (i = 0; i < 2; i++) /* For remove_headers && addr->prop.remove_headers */ { if (list) { @@ -653,7 +652,7 @@ for (h = header_list; h != NULL; h = h->next) if (h->type != htype_old) errno = ERRNO_CHHEADER_FAIL; return FALSE; } - len = Ustrlen(s); + len = s ? Ustrlen(s) : 0; if (strncmpic(h->text, s, len) != 0) continue; ss = h->text + len; while (*ss == ' ' || *ss == '\t') ss++; @@ -661,7 +660,7 @@ for (h = header_list; h != NULL; h = h->next) if (h->type != htype_old) } if (s != NULL) { include_header = FALSE; break; } } - if (addr != NULL) list = addr->p.remove_headers; + if (addr != NULL) list = addr->prop.remove_headers; } /* If this header is to be output, try to rewrite it if there are rewriting @@ -709,11 +708,10 @@ Headers added to an address by a router are guaranteed to end with a newline. if (addr) { int i; - header_line *hprev = addr->p.extra_headers; + header_line *hprev = addr->prop.extra_headers; header_line *hnext; for (i = 0; i < 2; i++) - { - for (h = hprev, hprev = NULL; h != NULL; h = hnext) + for (h = hprev, hprev = NULL; h; h = hnext) { hnext = h->next; h->next = hprev; @@ -725,7 +723,6 @@ if (addr) debug_printf("added header line(s):\n%s---\n", h->text); } } - } } /* If a string containing additional headers exists it is a newline-sep @@ -740,13 +737,8 @@ if (add_headers) int sep = '\n'; uschar * s; - while ((s = string_nextinlist(&add_headers, &sep, NULL, 0))) - if (!(s = expand_string(s))) - { - if (!expand_string_forcedfail) - { errno = ERRNO_CHHEADER_FAIL; return FALSE; } - } - else + while ((s = string_nextinlist(CUSS &add_headers, &sep, NULL, 0))) + if ((s = expand_string(s))) { int len = Ustrlen(s); if (len > 0) @@ -762,6 +754,8 @@ if (add_headers) } } } + else if (!expand_string_forcedfail) + { errno = ERRNO_CHHEADER_FAIL; return FALSE; } } /* Separate headers from body with a blank line */ @@ -845,39 +839,41 @@ chunk_ptr = deliver_out_buffer; /* Set up the data for start-of-line data checking and escaping */ nl_partial_match = -1; -if (check_string != NULL && escape_string != NULL) +if (check_string && escape_string) { nl_check = check_string; nl_check_length = Ustrlen(nl_check); nl_escape = escape_string; nl_escape_length = Ustrlen(nl_escape); } -else nl_check_length = nl_escape_length = 0; +else + nl_check_length = nl_escape_length = 0; /* Whether the escaping mechanism is applied to headers or not is controlled by an option (set for SMTP, not otherwise). Negate the length if not wanted till after the headers. */ -if ((options & topt_escape_headers) == 0) nl_check_length = -nl_check_length; +if (!(options & topt_escape_headers)) + nl_check_length = -nl_check_length; /* Write the headers if required, including any that have to be added. If there are header rewriting rules, apply them. */ -if ((options & topt_no_headers) == 0) +if (!(options & topt_no_headers)) { /* Add return-path: if requested. */ - if ((options & topt_add_return_path) != 0) + if (options & topt_add_return_path) { uschar buffer[ADDRESS_MAXLENGTH + 20]; - sprintf(CS buffer, "Return-path: <%.*s>\n", ADDRESS_MAXLENGTH, + int n = sprintf(CS buffer, "Return-path: <%.*s>\n", ADDRESS_MAXLENGTH, return_path); - if (!write_chunk(fd, buffer, Ustrlen(buffer), use_crlf)) return FALSE; + if (!write_chunk(fd, buffer, n, use_crlf)) return FALSE; } /* Add envelope-to: if requested */ - if ((options & topt_add_envelope_to) != 0) + if (options & topt_add_envelope_to) { BOOL first = TRUE; address_item *p; @@ -891,10 +887,9 @@ if ((options & topt_no_headers) == 0) anchors for lists of addresses already handled; they have to be defined at this level becuase write_env_to() calls itself recursively. */ - for (p = addr; p != NULL; p = p->next) - { - if (!write_env_to(p, &plist, &dlist, &first, fd, use_crlf)) return FALSE; - } + for (p = addr; p; p = p->next) + if (!write_env_to(p, &plist, &dlist, &first, fd, use_crlf)) + return FALSE; /* Add a final newline and reset the store used for tracking duplicates */ @@ -907,14 +902,14 @@ if ((options & topt_no_headers) == 0) if ((options & topt_add_delivery_date) != 0) { uschar buffer[100]; - sprintf(CS buffer, "Delivery-date: %s\n", tod_stamp(tod_full)); - if (!write_chunk(fd, buffer, Ustrlen(buffer), use_crlf)) return FALSE; + int n = sprintf(CS buffer, "Delivery-date: %s\n", tod_stamp(tod_full)); + if (!write_chunk(fd, buffer, n, use_crlf)) return FALSE; } /* Then the message's headers. Don't write any that are flagged as "old"; that means they were rewritten, or are a record of envelope rewriting, or were removed (e.g. Bcc). If remove_headers is not null, skip any headers that - match any entries therein. Then check addr->p.remove_headers too, provided that + match any entries therein. Then check addr->prop.remove_headers too, provided that addr is not NULL. */ if (!transport_headers_send(addr, fd, add_headers, remove_headers, &write_chunk, use_crlf, rewrite_rules, rewrite_existflags)) @@ -927,11 +922,12 @@ negative in cases where it isn't to apply to the headers). Then ensure the body is positioned at the start of its file (following the message id), then write it, applying the size limit if required. */ -if ((options & topt_no_body) == 0) +if (!(options & topt_no_body)) { nl_check_length = abs(nl_check_length); nl_partial_match = 0; - lseek(deliver_datafile, SPOOL_DATA_START_OFFSET, SEEK_SET); + if (lseek(deliver_datafile, SPOOL_DATA_START_OFFSET, SEEK_SET) < 0) + return FALSE; while ((len = read(deliver_datafile, deliver_in_buffer, DELIVER_IN_BUFFER_SIZE)) > 0) { @@ -958,7 +954,7 @@ nl_check_length = nl_escape_length = 0; /* If requested, add a terminating "." line (SMTP output). */ -if ((options & topt_end_dot) != 0 && !write_chunk(fd, US".\n", 2, use_crlf)) +if (options & topt_end_dot && !write_chunk(fd, US".\n", 2, use_crlf)) return FALSE; /* Write out any remaining data in the buffer before returning. */ @@ -983,50 +979,37 @@ return (len = chunk_ptr - deliver_out_buffer) <= 0 || signing the file, send the signed message down the original fd (or TLS fd). Arguments: - as for internal_transport_write_message() above, with additional arguments: - uschar *dkim_private_key DKIM: The private key to use (filename or - plain data) - uschar *dkim_domain DKIM: The domain to use - uschar *dkim_selector DKIM: The selector to use. - uschar *dkim_canon DKIM: The canonalization scheme to use, - "simple" or "relaxed" - uschar *dkim_strict DKIM: What to do if signing fails: - 1/true => throw error - 0/false => send anyway - uschar *dkim_sign_headers DKIM: List of headers that should be included - in signature generation + as for internal_transport_write_message() above, with additional arguments + for DKIM. Returns: TRUE on success; FALSE (with errno) for any failure */ BOOL -dkim_transport_write_message(address_item *addr, int fd, int options, - int size_limit, uschar *add_headers, uschar *remove_headers, +dkim_transport_write_message(address_item *addr, int out_fd, int options, + uschar *add_headers, uschar *remove_headers, uschar *check_string, uschar *escape_string, rewrite_rule *rewrite_rules, - int rewrite_existflags, uschar *dkim_private_key, uschar *dkim_domain, - uschar *dkim_selector, uschar *dkim_canon, uschar *dkim_strict, uschar *dkim_sign_headers - ) + int rewrite_existflags, struct ob_dkim * dkim) { int dkim_fd; int save_errno = 0; BOOL rc; -uschar dkim_spool_name[256]; -char sbuf[2048]; +uschar * dkim_spool_name; int sread = 0; int wwritten = 0; uschar *dkim_signature = NULL; -off_t size = 0; +off_t k_file_size; /* If we can't sign, just call the original function. */ -if (!(dkim_private_key && dkim_domain && dkim_selector)) - return transport_write_message(addr, fd, options, - size_limit, add_headers, remove_headers, +if (!(dkim->dkim_private_key && dkim->dkim_domain && dkim->dkim_selector)) + return transport_write_message(addr, out_fd, options, + 0, add_headers, remove_headers, check_string, escape_string, rewrite_rules, rewrite_existflags); -(void)string_format(dkim_spool_name, 256, "%s/input/%s/%s-%d-K", - spool_directory, message_subdir, message_id, (int)getpid()); +dkim_spool_name = spool_fname(US"input", message_subdir, message_id, + string_sprintf("-%d-K", (int)getpid())); if ((dkim_fd = Uopen(dkim_spool_name, O_RDWR|O_CREAT|O_TRUNC, SPOOL_MODE)) < 0) { @@ -1036,10 +1019,10 @@ if ((dkim_fd = Uopen(dkim_spool_name, O_RDWR|O_CREAT|O_TRUNC, SPOOL_MODE)) < 0) goto CLEANUP; } -/* Call original function to write the -K file */ +/* Call original function to write the -K file; does the CRLF expansion */ rc = transport_write_message(addr, dkim_fd, options, - size_limit, add_headers, remove_headers, + 0, add_headers, remove_headers, check_string, escape_string, rewrite_rules, rewrite_existflags); @@ -1050,24 +1033,24 @@ if (!rc) goto CLEANUP; } -if (dkim_private_key && dkim_domain && dkim_selector) +if (dkim->dkim_private_key && dkim->dkim_domain && dkim->dkim_selector) { /* Rewind file and feed it to the goats^W DKIM lib */ lseek(dkim_fd, 0, SEEK_SET); dkim_signature = dkim_exim_sign(dkim_fd, - dkim_private_key, - dkim_domain, - dkim_selector, - dkim_canon, - dkim_sign_headers); + dkim->dkim_private_key, + dkim->dkim_domain, + dkim->dkim_selector, + dkim->dkim_canon, + dkim->dkim_sign_headers); if (!dkim_signature) { - if (dkim_strict) + if (dkim->dkim_strict) { - uschar *dkim_strict_result = expand_string(dkim_strict); + uschar *dkim_strict_result = expand_string(dkim->dkim_strict); if (dkim_strict_result) - if ( (strcmpic(dkim_strict,US"1") == 0) || - (strcmpic(dkim_strict,US"true") == 0) ) + if ( (strcmpic(dkim->dkim_strict,US"1") == 0) || + (strcmpic(dkim->dkim_strict,US"true") == 0) ) { /* Set errno to something halfway meaningful */ save_errno = EACCES; @@ -1078,20 +1061,21 @@ if (dkim_private_key && dkim_domain && dkim_selector) } } } - else + + if (dkim_signature) { int siglen = Ustrlen(dkim_signature); while(siglen > 0) { -#ifdef SUPPORT_TLS - wwritten = tls_out.active == fd - ? tls_write(FALSE, dkim_signature, siglen) - : write(fd, dkim_signature, siglen); -#else - wwritten = write(fd, dkim_signature, siglen); -#endif + #ifdef SUPPORT_TLS + wwritten = tls_out.active == out_fd + ? tls_write(FALSE, dkim_signature, siglen) + : write(out_fd, dkim_signature, siglen); + #else + wwritten = write(out_fd, dkim_signature, siglen); + #endif if (wwritten == -1) - { + { /* error, bail out */ save_errno = errno; rc = FALSE; @@ -1103,64 +1087,69 @@ if (dkim_private_key && dkim_domain && dkim_selector) } } -/* Fetch file size */ -size = lseek(dkim_fd, 0, SEEK_END); - -/* Rewind file */ -lseek(dkim_fd, 0, SEEK_SET); - #ifdef HAVE_LINUX_SENDFILE /* We can use sendfile() to shove the file contents to the socket. However only if we don't use TLS, as then there's another layer of indirection before the data finally hits the socket. */ -if (tls_out.active != fd) +if (tls_out.active != out_fd) { ssize_t copied = 0; off_t offset = 0; - while(copied >= 0 && offset < size) - copied = sendfile(fd, dkim_fd, &offset, size - offset); + + k_file_size = lseek(dkim_fd, 0, SEEK_END); /* Fetch file size */ + + /* Rewind file */ + lseek(dkim_fd, 0, SEEK_SET); + + while(copied >= 0 && offset < k_file_size) + copied = sendfile(out_fd, dkim_fd, &offset, k_file_size - offset); if (copied < 0) { save_errno = errno; rc = FALSE; } - goto CLEANUP; } +else + #endif -/* Send file down the original fd */ -while((sread = read(dkim_fd, sbuf, 2048)) > 0) { - char *p = sbuf; - /* write the chunk */ + /* Rewind file */ + lseek(dkim_fd, 0, SEEK_SET); - while (sread) + /* Send file down the original fd */ + while((sread = read(dkim_fd, deliver_out_buffer, DELIVER_OUT_BUFFER_SIZE)) >0) { + char *p = deliver_out_buffer; + /* write the chunk */ + + while (sread) + { #ifdef SUPPORT_TLS - wwritten = tls_out.active == fd - ? tls_write(FALSE, US p, sread) - : write(fd, p, sread); + wwritten = tls_out.active == out_fd + ? tls_write(FALSE, US p, sread) + : write(out_fd, p, sread); #else - wwritten = write(fd, p, sread); + wwritten = write(out_fd, p, sread); #endif - if (wwritten == -1) - { - /* error, bail out */ - save_errno = errno; - rc = FALSE; - goto CLEANUP; + if (wwritten == -1) + { + /* error, bail out */ + save_errno = errno; + rc = FALSE; + goto CLEANUP; + } + p += wwritten; + sread -= wwritten; } - p += wwritten; - sread -= wwritten; } - } -if (sread == -1) - { - save_errno = errno; - rc = FALSE; - goto CLEANUP; + if (sread == -1) + { + save_errno = errno; + rc = FALSE; + } } CLEANUP: @@ -1200,7 +1189,7 @@ transport_write_message(address_item *addr, int fd, int options, BOOL use_crlf; BOOL last_filter_was_NL = TRUE; int rc, len, yield, fd_read, fd_write, save_errno; -int pfd[2]; +int pfd[2] = {-1, -1}; pid_t filter_pid, write_pid; transport_filter_timed_out = FALSE; @@ -1208,7 +1197,10 @@ transport_filter_timed_out = FALSE; /* If there is no filter command set up, call the internal function that does the actual work, passing it the incoming fd, and return its result. */ -if (transport_filter_argv == NULL) +if ( !transport_filter_argv + || !*transport_filter_argv + || !**transport_filter_argv + ) return internal_transport_write_message(addr, fd, options, size_limit, add_headers, remove_headers, check_string, escape_string, rewrite_rules, rewrite_existflags); @@ -1242,8 +1234,8 @@ yield = FALSE; write_pid = (pid_t)(-1); (void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); -filter_pid = child_open(transport_filter_argv, NULL, 077, &fd_write, &fd_read, - FALSE); +filter_pid = child_open(USS transport_filter_argv, NULL, 077, + &fd_write, &fd_read, FALSE); (void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) & ~FD_CLOEXEC); if (filter_pid < 0) goto TIDY_UP; /* errno set */ @@ -1411,22 +1403,21 @@ filter was not NL, insert a NL to make the SMTP protocol work. */ if (yield) { nl_check_length = nl_escape_length = 0; - if ((options & topt_end_dot) != 0 && (last_filter_was_NL? - !write_chunk(fd, US".\n", 2, use_crlf) : - !write_chunk(fd, US"\n.\n", 3, use_crlf))) - { + if ( options & topt_end_dot + && ( last_filter_was_NL + ? !write_chunk(fd, US".\n", 2, options) + : !write_chunk(fd, US"\n.\n", 3, options) + ) ) yield = FALSE; - } /* Write out any remaining data in the buffer. */ else - { - yield = (len = chunk_ptr - deliver_out_buffer) <= 0 || - transport_write_block(fd, deliver_out_buffer, len); - } + yield = (len = chunk_ptr - deliver_out_buffer) <= 0 + || transport_write_block(fd, deliver_out_buffer, len); } -else errno = save_errno; /* From some earlier error */ +else + errno = save_errno; /* From some earlier error */ DEBUG(D_transport) { @@ -1481,7 +1472,7 @@ void transport_update_waiting(host_item *hostlist, uschar *tpname) { uschar buffer[256]; -uschar *prevname = US""; +const uschar *prevname = US""; host_item *host; open_db dbblock; open_db *dbm_file; @@ -1622,20 +1613,32 @@ Arguments: as set by the caller transport new_message_id set to the message id of a waiting message more set TRUE if there are yet more messages waiting + oicf_func function to call to validate if it is ok to send + to this message_id from the current instance. + oicf_data opaque data for oicf_func Returns: TRUE if new_message_id set; FALSE otherwise */ +typedef struct msgq_s +{ + uschar message_id [MESSAGE_ID_LENGTH + 1]; + BOOL bKeep; +} msgq_t; + BOOL -transport_check_waiting(uschar *transport_name, uschar *hostname, - int local_message_max, uschar *new_message_id, BOOL *more) +transport_check_waiting(const uschar *transport_name, const uschar *hostname, + int local_message_max, uschar *new_message_id, BOOL *more, oicf oicf_func, void *oicf_data) { dbdata_wait *host_record; -int host_length, path_len; +int host_length; open_db dbblock; open_db *dbm_file; uschar buffer[256]; +int i; +struct stat statbuf; + *more = FALSE; DEBUG(D_transport) @@ -1664,8 +1667,7 @@ if (dbm_file == NULL) return FALSE; /* See if there is a record for this host; if not, there's nothing to do. */ -host_record = dbfn_read(dbm_file, hostname); -if (host_record == NULL) +if (!(host_record = dbfn_read(dbm_file, hostname))) { dbfn_close(dbm_file); DEBUG(D_transport) debug_printf("no messages waiting for %s\n", hostname); @@ -1688,58 +1690,106 @@ until one is found for which a spool file actually exists. If the record gets emptied, delete it and continue with any continuation records that may exist. */ -host_length = host_record->count * MESSAGE_ID_LENGTH; +/* For Bug 1141, I refactored this major portion of the routine, it is risky +but the 1 off will remain without it. This code now allows me to SKIP over +a message I do not want to send out on this run. */ -/* Loop to handle continuation host records in the database */ +host_length = host_record->count * MESSAGE_ID_LENGTH; -for (;;) +while (1) { - BOOL found = FALSE; + msgq_t *msgq; + int msgq_count = 0; + int msgq_actual = 0; + BOOL bFound = FALSE; + BOOL bContinuation = FALSE; + + /* create an array to read entire message queue into memory for processing */ - sprintf(CS buffer, "%s/input/", spool_directory); - path_len = Ustrlen(buffer); + msgq = (msgq_t*) malloc(sizeof(msgq_t) * host_record->count); + msgq_count = host_record->count; + msgq_actual = msgq_count; - for (host_length -= MESSAGE_ID_LENGTH; host_length >= 0; - host_length -= MESSAGE_ID_LENGTH) + for (i = 0; i < host_record->count; ++i) { - struct stat statbuf; - Ustrncpy(new_message_id, host_record->text + host_length, + msgq[i].bKeep = TRUE; + + Ustrncpy(msgq[i].message_id, host_record->text + (i * MESSAGE_ID_LENGTH), MESSAGE_ID_LENGTH); - new_message_id[MESSAGE_ID_LENGTH] = 0; + msgq[i].message_id[MESSAGE_ID_LENGTH] = 0; + } - if (split_spool_directory) - sprintf(CS(buffer + path_len), "%c/%s-D", new_message_id[5], new_message_id); - else - sprintf(CS(buffer + path_len), "%s-D", new_message_id); + /* first thing remove current message id if it exists */ - /* The listed message may be the one we are currently processing. If - so, we want to remove it from the list without doing anything else. - If not, do a stat to see if it is an existing message. If it is, break - the loop to handle it. No need to bother about locks; as this is all - "hint" processing, it won't matter if it doesn't exist by the time exim - actually tries to deliver it. */ + for (i = 0; i < msgq_count; ++i) + if (Ustrcmp(msgq[i].message_id, message_id) == 0) + { + msgq[i].bKeep = FALSE; + break; + } + + /* now find the next acceptable message_id */ - if (Ustrcmp(new_message_id, message_id) != 0 && - Ustat(buffer, &statbuf) == 0) + for (i = msgq_count - 1; i >= 0; --i) if (msgq[i].bKeep) + { + uschar subdir[2]; + + subdir[0] = split_spool_directory ? msgq[i].message_id[5] : 0; + subdir[1] = 0; + + if (Ustat(spool_fname(US"input", subdir, msgq[i].message_id, US"-D"), + &statbuf) != 0) + msgq[i].bKeep = FALSE; + else if (!oicf_func || oicf_func(msgq[i].message_id, oicf_data)) { - found = TRUE; + Ustrcpy(new_message_id, msgq[i].message_id); + msgq[i].bKeep = FALSE; + bFound = TRUE; break; } } - /* If we have removed all the message ids from the record delete the record. - If there is a continuation record, fetch it and remove it from the file, - as it will be rewritten as the main record. Repeat in the case of an - empty continuation. */ + /* re-count */ + for (msgq_actual = 0, i = 0; i < msgq_count; ++i) + if (msgq[i].bKeep) + msgq_actual++; + + /* reassemble the host record, based on removed message ids, from in + memory queue */ + + if (msgq_actual <= 0) + { + host_length = 0; + host_record->count = 0; + } + else + { + host_length = msgq_actual * MESSAGE_ID_LENGTH; + host_record->count = msgq_actual; + + if (msgq_actual < msgq_count) + { + int new_count; + for (new_count = 0, i = 0; i < msgq_count; ++i) + if (msgq[i].bKeep) + Ustrncpy(&host_record->text[new_count++ * MESSAGE_ID_LENGTH], + msgq[i].message_id, MESSAGE_ID_LENGTH); + + host_record->text[new_count * MESSAGE_ID_LENGTH] = 0; + } + } + +/* Jeremy: check for a continuation record, this code I do not know how to +test but the code should work */ while (host_length <= 0) { int i; - dbdata_wait *newr = NULL; + dbdata_wait * newr = NULL; /* Search for a continuation */ - for (i = host_record->sequence - 1; i >= 0 && newr == NULL; i--) + for (i = host_record->sequence - 1; i >= 0 && !newr; i--) { sprintf(CS buffer, "%.200s:%d", hostname, i); newr = dbfn_read(dbm_file, buffer); @@ -1747,7 +1797,7 @@ for (;;) /* If no continuation, delete the current and break the loop */ - if (newr == NULL) + if (!newr) { dbfn_delete(dbm_file, hostname); break; @@ -1758,11 +1808,15 @@ for (;;) dbfn_delete(dbm_file, buffer); host_record = newr; host_length = host_record->count * MESSAGE_ID_LENGTH; - } - /* If we found an existing message, break the continuation loop. */ + bContinuation = TRUE; + } - if (found) break; + if (bFound) /* Usual exit from main loop */ + { + free (msgq); + break; + } /* If host_length <= 0 we have emptied a record and not found a good message, and there are no continuation records. Otherwise there is a continuation @@ -1774,7 +1828,20 @@ for (;;) DEBUG(D_transport) debug_printf("waiting messages already delivered\n"); return FALSE; } - } + + /* we were not able to find an acceptable message, nor was there a + * continuation record. So bug out, outer logic will clean this up. + */ + + if (!bContinuation) + { + Ustrcpy(new_message_id, message_id); + dbfn_close(dbm_file); + return FALSE; + } + + free(msgq); + } /* we need to process a continuation record */ /* Control gets here when an existing message has been encountered; its id is in new_message_id, and host_length is the revised length of the @@ -1784,6 +1851,7 @@ record if required, close the database, and return TRUE. */ if (host_length > 0) { host_record->count = host_length/MESSAGE_ID_LENGTH; + dbfn_write(dbm_file, hostname, host_record, (int)sizeof(dbdata_wait) + host_length); *more = TRUE; } @@ -1792,8 +1860,6 @@ dbfn_close(dbm_file); return TRUE; } - - /************************************************* * Deliver waiting message down same socket * *************************************************/ @@ -1813,8 +1879,8 @@ Returns: FALSE if fork fails; TRUE otherwise */ BOOL -transport_pass_socket(uschar *transport_name, uschar *hostname, - uschar *hostaddress, uschar *id, int socket_fd) +transport_pass_socket(const uschar *transport_name, const uschar *hostname, + const uschar *hostaddress, uschar *id, int socket_fd) { pid_t pid; int status; @@ -1824,7 +1890,7 @@ DEBUG(D_transport) debug_printf("transport_pass_socket entered\n"); if ((pid = fork()) == 0) { int i = 16; - uschar **argv; + const uschar **argv; /* Disconnect entirely from the parent process. If we are running in the test harness, wait for a bit to allow the previous process time to finish, @@ -1837,12 +1903,9 @@ if ((pid = fork()) == 0) /* Set up the calling arguments; use the standard function for the basics, but we have a number of extras that may be added. */ - argv = child_exec_exim(CEE_RETURN_ARGV, TRUE, &i, FALSE, 0); + argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, &i, FALSE, 0); - #ifdef EXPERIMENTAL_DSN - /* Call with the dsn flag */ if (smtp_use_dsn) argv[i++] = US"-MCD"; - #endif if (smtp_authenticated) argv[i++] = US"-MCA"; @@ -1861,9 +1924,9 @@ if ((pid = fork()) == 0) } argv[i++] = US"-MC"; - argv[i++] = transport_name; - argv[i++] = hostname; - argv[i++] = hostaddress; + argv[i++] = US transport_name; + argv[i++] = US hostname; + argv[i++] = US hostaddress; argv[i++] = string_sprintf("%d", continue_sequence + 1); argv[i++] = id; argv[i++] = NULL; @@ -1917,7 +1980,7 @@ case, no addresses are passed. Arguments: argvptr pointer to anchor for argv vector - cmd points to the command string + cmd points to the command string (modified IN PLACE) expand_arguments true if expansion is to occur expand_failed error value to set if expansion fails; not relevant if addr == NULL @@ -1931,11 +1994,12 @@ Returns: TRUE if all went well; otherwise an error will be */ BOOL -transport_set_up_command(uschar ***argvptr, uschar *cmd, BOOL expand_arguments, - int expand_failed, address_item *addr, uschar *etext, uschar **errptr) +transport_set_up_command(const uschar ***argvptr, uschar *cmd, + BOOL expand_arguments, int expand_failed, address_item *addr, + uschar *etext, uschar **errptr) { address_item *ad; -uschar **argv; +const uschar **argv; uschar *s, *ss; int address_count = 0; int argcount = 0; @@ -1969,7 +2033,7 @@ while (*s != 0 && argcount < max_args) if (*s != 0) s++; *ss++ = 0; } - else argv[argcount++] = string_dequote(&s); + else argv[argcount++] = string_copy(string_dequote(CUSS &s)); while (isspace(*s)) s++; } @@ -2098,7 +2162,8 @@ if (expand_arguments) if (*s != 0) s++; *ss++ = 0; } - else address_pipe_argv[address_pipe_argcount++] = string_dequote(&s); + else address_pipe_argv[address_pipe_argcount++] = + string_copy(string_dequote(CUSS &s)); while (isspace(*s)) s++; /* strip space after arg */ } @@ -2166,9 +2231,9 @@ if (expand_arguments) else { - uschar *expanded_arg; + const uschar *expanded_arg; enable_dollar_recipients = allow_dollar_recipients; - expanded_arg = expand_string(argv[i]); + expanded_arg = expand_cstring(argv[i]); enable_dollar_recipients = FALSE; if (expanded_arg == NULL)