* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2014 */
+/* Copyright (c) University of Cambridge 1995 - 2015 */
/* See the file NOTICE for conditions of use and distribution. */
/* General functions concerned with transportation, and generic options for all
(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,
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)
{
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)
{
}
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
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++)
{
int sep = '\n';
uschar * s;
- while ((s = string_nextinlist(&add_headers, &sep, NULL, 0)))
+ while ((s = string_nextinlist(CUSS &add_headers, &sep, NULL, 0)))
if (!(s = expand_string(s)))
{
if (!expand_string_forcedfail)
/* 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))
uschar *dkim_strict_result = expand_string(dkim_strict);
if (dkim_strict_result)
if ( (strcmpic(dkim_strict,US"1") == 0) ||
- (strcmpic(dkim_strict,US"true") == 0) )
+ (strcmpic(dkim_strict,US"true") == 0) )
{
/* Set errno to something halfway meaningful */
save_errno = EACCES;
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 */
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;
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];
+msgq_t *msgq = NULL;
+int msgq_count = 0;
+int msgq_actual = 0;
+int i;
+BOOL bFound = FALSE;
+uschar spool_dir [PATH_MAX];
+uschar spool_file [PATH_MAX];
+struct stat statbuf;
+BOOL bContinuation = FALSE;
+
*more = FALSE;
DEBUG(D_transport)
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 */
+sprintf(CS spool_dir, "%s/input/", spool_directory);
-for (;;)
+host_length = host_record->count * MESSAGE_ID_LENGTH;
+
+while (1)
{
- BOOL found = 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;
+ }
+
+ /* first thing remove current message id if it exists */
+
+ 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 */
+
+ bFound = FALSE;
+
+ for (i = msgq_count - 1; i >= 0; --i) if (msgq[i].bKeep)
+ {
if (split_spool_directory)
- sprintf(CS(buffer + path_len), "%c/%s-D", new_message_id[5], new_message_id);
+ sprintf(CS spool_file, "%s%c/%s-D",
+ spool_dir, msgq[i].message_id[5], msgq[i].message_id);
else
- sprintf(CS(buffer + path_len), "%s-D", new_message_id);
-
- /* 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. */
+ sprintf(CS spool_file, "%s%s-D", spool_dir, msgq[i].message_id);
- if (Ustrcmp(new_message_id, message_id) != 0 &&
- Ustat(buffer, &statbuf) == 0)
+ if (Ustat(spool_file, &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 */
+
+ bContinuation = FALSE;
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);
/* If no continuation, delete the current and break the loop */
- if (newr == NULL)
+ if (!newr)
{
dbfn_delete(dbm_file, hostname);
break;
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)
+ 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
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;
+ }
+ } /* we need to process a continuation record */
+
+/* clean up in memory queue */
+if (msgq)
+ {
+ free (msgq);
+ msgq = NULL;
+ msgq_count = 0;
+ msgq_actual = 0;
}
/* Control gets here when an existing message has been encountered; its
if (host_length > 0)
{
+ uschar msg [MESSAGE_ID_LENGTH + 1];
+ int i;
+
host_record->count = host_length/MESSAGE_ID_LENGTH;
+
+ /* rebuild the host_record->text */
+
+ for (i = 0; i < host_record->count; ++i)
+ {
+ Ustrncpy(msg, host_record->text + (i*MESSAGE_ID_LENGTH), MESSAGE_ID_LENGTH);
+ msg[MESSAGE_ID_LENGTH] = 0;
+ }
+
dbfn_write(dbm_file, hostname, host_record, (int)sizeof(dbdata_wait) + host_length);
*more = TRUE;
}
return TRUE;
}
-
-
/*************************************************
* Deliver waiting message down same socket *
*************************************************/
*/
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;
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,
/* 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);
/* Call with the dsn flag */
if (smtp_use_dsn) argv[i++] = US"-MCD";
}
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;
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
*/
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;
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++;
}
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 */
}
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)