* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2015 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions that operate on the input queue. */
queue_filename **append = &first;
while (a && b)
- if (Ustrcmp(a->text, b->text) < 0)
+ {
+ int d;
+ if ((d = Ustrncmp(a->text, b->text, 6)) == 0)
+ d = Ustrcmp(a->text + 14, b->text + 14);
+ if (d < 0)
{
*append = a;
append= &a->next;
append= &b->next;
b = b->next;
}
+ }
*append = a ? a : b;
return first;
else
{
- int j;
next->next = NULL;
- for (j = 0; j < LOG2_MAXNODES; j++)
+ for (int j = 0; j < LOG2_MAXNODES; j++)
if (root[j])
{
next = merge_queue_lists(next, root[j]);
- root[j] = (j == LOG2_MAXNODES - 1)? next : NULL;
+ root[j] = j == LOG2_MAXNODES - 1 ? next : NULL;
}
else
{
void
queue_run(uschar *start_id, uschar *stop_id, BOOL recurse)
{
-BOOL force_delivery = queue_run_force || deliver_selectstring != NULL ||
+BOOL force_delivery = f.queue_run_force || deliver_selectstring != NULL ||
deliver_selectstring_sender != NULL;
const pcre *selectstring_regex = NULL;
const pcre *selectstring_regex_sender = NULL;
uschar *log_detail = NULL;
int subcount = 0;
-int i;
uschar subdirs[64];
/* Cancel any specific queue domains. Turn off the flag that causes SMTP
queue_domains = NULL;
queue_smtp_domains = NULL;
-queue_smtp = queue_2stage;
+f.queue_smtp = f.queue_2stage;
queue_run_pid = getpid();
-queue_running = TRUE;
+f.queue_running = TRUE;
/* Log the true start of a queue run, and fancy options */
uschar extras[8];
uschar *p = extras;
- if (queue_2stage) *p++ = 'q';
- if (queue_run_first_delivery) *p++ = 'i';
- if (queue_run_force) *p++ = 'f';
- if (deliver_force_thaw) *p++ = 'f';
- if (queue_run_local) *p++ = 'l';
+ if (f.queue_2stage) *p++ = 'q';
+ if (f.queue_run_first_delivery) *p++ = 'i';
+ if (f.queue_run_force) *p++ = 'f';
+ if (f.deliver_force_thaw) *p++ = 'f';
+ if (f.queue_run_local) *p++ = 'l';
*p = 0;
p = big_buffer;
- sprintf(CS p, "pid=%d", (int)queue_run_pid);
- while (*p != 0) p++;
+ p += sprintf(CS p, "pid=%d", (int)queue_run_pid);
if (extras[0] != 0)
- {
- sprintf(CS p, " -q%s", extras);
- while (*p != 0) p++;
- }
+ p += sprintf(CS p, " -q%s", extras);
- if (deliver_selectstring != NULL)
- {
- sprintf(CS p, " -R%s %s", deliver_selectstring_regex? "r" : "",
+ if (deliver_selectstring)
+ p += sprintf(CS p, " -R%s %s", f.deliver_selectstring_regex? "r" : "",
deliver_selectstring);
- while (*p != 0) p++;
- }
- if (deliver_selectstring_sender != NULL)
- {
- sprintf(CS p, " -S%s %s", deliver_selectstring_sender_regex? "r" : "",
+ if (deliver_selectstring_sender)
+ p += sprintf(CS p, " -S%s %s", f.deliver_selectstring_sender_regex? "r" : "",
deliver_selectstring_sender);
- while (*p != 0) p++;
- }
log_detail = string_copy(big_buffer);
if (*queue_name)
/* If deliver_selectstring is a regex, compile it. */
-if (deliver_selectstring != NULL && deliver_selectstring_regex)
+if (deliver_selectstring && f.deliver_selectstring_regex)
selectstring_regex = regex_must_compile(deliver_selectstring, TRUE, FALSE);
-if (deliver_selectstring_sender != NULL && deliver_selectstring_sender_regex)
+if (deliver_selectstring_sender && f.deliver_selectstring_sender_regex)
selectstring_regex_sender =
regex_must_compile(deliver_selectstring_sender, TRUE, FALSE);
When the first argument of queue_get_spool_list() is -1 (for queue_run_in_
order), it scans all directories and makes a single message list. */
-for (i = (queue_run_in_order? -1 : 0);
- i <= (queue_run_in_order? -1 : subcount);
+for (int i = queue_run_in_order ? -1 : 0;
+ i <= (queue_run_in_order ? -1 : subcount);
i++)
{
- queue_filename *f;
void *reset_point1 = store_get(0);
DEBUG(D_queue_run)
debug_printf("queue running subdirectory '%c'\n", subdirs[i]);
}
- for (f = queue_get_spool_list(i, subdirs, &subcount, !queue_run_in_order);
- f;
- f = f->next)
+ for (queue_filename * fq = queue_get_spool_list(i, subdirs, &subcount,
+ !queue_run_in_order);
+ fq; fq = fq->next)
{
pid_t pid;
int status;
/* Unless deliveries are forced, if deliver_queue_load_max is non-negative,
check that the load average is low enough to permit deliveries. */
- if (!queue_run_force && deliver_queue_load_max >= 0)
+ if (!f.queue_run_force && deliver_queue_load_max >= 0)
if ((load_average = os_getloadavg()) > deliver_queue_load_max)
{
log_write(L_queue_run, LOG_MAIN, "Abandon queue run: %s (load %.2f, max %.2f)",
/* Skip this message unless it's within the ID limits */
- if (stop_id && Ustrncmp(f->text, stop_id, MESSAGE_ID_LENGTH) > 0)
+ if (stop_id && Ustrncmp(fq->text, stop_id, MESSAGE_ID_LENGTH) > 0)
continue;
- if (start_id && Ustrncmp(f->text, start_id, MESSAGE_ID_LENGTH) < 0)
+ if (start_id && Ustrncmp(fq->text, start_id, MESSAGE_ID_LENGTH) < 0)
continue;
/* Check that the message still exists */
- message_subdir[0] = f->dir_uschar;
- if (Ustat(spool_fname(US"input", message_subdir, f->text, US""), &statbuf) < 0)
+ message_subdir[0] = fq->dir_uschar;
+ if (Ustat(spool_fname(US"input", message_subdir, fq->text, US""), &statbuf) < 0)
continue;
/* There are some tests that require the reading of the header file. Ensure
message when many are not going to be delivered. */
if (deliver_selectstring || deliver_selectstring_sender ||
- queue_run_first_delivery)
+ f.queue_run_first_delivery)
{
BOOL wanted = TRUE;
- BOOL orig_dont_deliver = dont_deliver;
+ BOOL orig_dont_deliver = f.dont_deliver;
void *reset_point2 = store_get(0);
/* Restore the original setting of dont_deliver after reading the header,
follow. If the message is chosen for delivery, the header is read again
in the deliver_message() function, in a subprocess. */
- if (spool_read_header(f->text, FALSE, TRUE) != spool_read_OK) continue;
- dont_deliver = orig_dont_deliver;
+ if (spool_read_header(fq->text, FALSE, TRUE) != spool_read_OK) continue;
+ f.dont_deliver = orig_dont_deliver;
/* Now decide if we want to deliver this message. As we have read the
header file, we might as well do the freeze test now, and save forking
another process. */
- if (deliver_freeze && !deliver_force_thaw)
+ if (f.deliver_freeze && !f.deliver_force_thaw)
{
log_write(L_skip_delivery, LOG_MAIN, "Message is frozen");
wanted = FALSE;
/* Check first_delivery in the case when there are no message logs. */
- else if (queue_run_first_delivery && !deliver_firsttime)
+ else if (f.queue_run_first_delivery && !f.deliver_firsttime)
{
- DEBUG(D_queue_run) debug_printf("%s: not first delivery\n", f->text);
+ DEBUG(D_queue_run) debug_printf("%s: not first delivery\n", fq->text);
wanted = FALSE;
}
/* Sender matching */
else if ( deliver_selectstring_sender
- && !(deliver_selectstring_sender_regex
+ && !(f.deliver_selectstring_sender_regex
? (pcre_exec(selectstring_regex_sender, NULL,
CS sender_address, Ustrlen(sender_address), 0, PCRE_EOPT,
NULL, 0) >= 0)
) )
{
DEBUG(D_queue_run) debug_printf("%s: sender address did not match %s\n",
- f->text, deliver_selectstring_sender);
+ fq->text, deliver_selectstring_sender);
wanted = FALSE;
}
for (i = 0; i < recipients_count; i++)
{
uschar *address = recipients_list[i].address;
- if ( (deliver_selectstring_regex
+ if ( (f.deliver_selectstring_regex
? (pcre_exec(selectstring_regex, NULL, CS address,
Ustrlen(address), 0, PCRE_EOPT, NULL, 0) >= 0)
: (strstric(address, deliver_selectstring, FALSE) != NULL)
{
DEBUG(D_queue_run)
debug_printf("%s: no recipient address matched %s\n",
- f->text, deliver_selectstring);
+ fq->text, deliver_selectstring);
wanted = FALSE;
}
}
/* Recover store used when reading the header */
- received_protocol = NULL;
- sender_address = sender_ident = NULL;
- authenticated_id = authenticated_sender = NULL;
+ spool_clear_header_globals();
store_reset(reset_point2);
if (!wanted) continue; /* With next message */
}
/* Now deliver the message; get the id by cutting the -H off the file
name. The return of the process is zero if a delivery was attempted. */
- set_process_info("running queue: %s", f->text);
- f->text[SPOOL_NAME_LENGTH-2] = 0;
+ set_process_info("running queue: %s", fq->text);
+ fq->text[SPOOL_NAME_LENGTH-2] = 0;
if ((pid = fork()) == 0)
{
int rc;
- if (running_in_test_harness) millisleep(100);
+ if (f.running_in_test_harness) millisleep(100);
(void)close(pfd[pipe_read]);
- rc = deliver_message(f->text, force_delivery, FALSE);
+ rc = deliver_message(fq->text, force_delivery, FALSE);
_exit(rc == DELIVER_NOT_ATTEMPTED);
}
if (pid < 0)
then wait for the first level process to terminate. */
(void)close(pfd[pipe_write]);
- set_process_info("running queue: waiting for %s (%d)", f->text, pid);
+ set_process_info("running queue: waiting for %s (%d)", fq->text, pid);
while (wait(&status) != pid);
/* A zero return means a delivery was attempted; turn off the force flag
for any subsequent calls unless queue_force is set. */
- if ((status & 0xffff) == 0) force_delivery = queue_run_force;
+ if ((status & 0xffff) == 0) force_delivery = f.queue_run_force;
/* If the process crashed, tell somebody */
else if ((status & 0x00ff) != 0)
log_write(0, LOG_MAIN|LOG_PANIC,
"queue run: process %d crashed with signal %d while delivering %s",
- (int)pid, status & 0x00ff, f->text);
+ (int)pid, status & 0x00ff, fq->text);
/* Before continuing, wait till the pipe gets closed at the far end. This
tells us that any children created by the delivery to re-use any SMTP
/* If we are in the test harness, and this is not the first of a 2-stage
queue run, update fudged queue times. */
- if (running_in_test_harness && !queue_2stage)
+ if (f.running_in_test_harness && !f.queue_2stage)
{
uschar *fqtnext = Ustrchr(fudged_queue_times, '/');
if (fqtnext != NULL) fudged_queue_times = fqtnext + 1;
}
} /* End loop for list of messages */
+ tree_nonrecipients = NULL;
store_reset(reset_point1); /* Scavenge list of messages */
/* If this was the first time through for random order processing, and
sub-directories have been found, randomize their order if necessary. */
if (i == 0 && subcount > 1 && !queue_run_in_order)
- {
- int j, r;
- for (j = 1; j <= subcount; j++)
+ for (int j = 1; j <= subcount; j++)
+ {
+ int r;
if ((r = random_number(100)) >= 50)
{
int k = (r % subcount) + 1;
subdirs[j] = subdirs[k];
subdirs[k] = x;
}
- }
+ }
} /* End loop for multiple directories */
/* If queue_2stage is true, we do it all again, with the 2stage flag
turned off. */
-if (queue_2stage)
+if (f.queue_2stage)
{
- queue_2stage = FALSE;
+ f.queue_2stage = FALSE;
queue_run(start_id, stop_id, TRUE);
}
{
int subcount;
int count = 0;
-queue_filename *f = NULL;
uschar subdirs[64];
-f = queue_get_spool_list(
- -1, /* entire queue */
- subdirs, /* for holding sub list */
- &subcount, /* for subcount */
- FALSE); /* not random */
-for (; f != NULL; f = f->next) count++;
+
+for (queue_filename *f = queue_get_spool_list(
+ -1, /* entire queue */
+ subdirs, /* for holding sub list */
+ &subcount, /* for subcount */
+ FALSE); /* not random */
+ f; f = f->next) count++;
fprintf(stdout, "%d\n", count);
}
void
queue_list(int option, uschar **list, int count)
{
-int i;
int subcount;
int now = (int)time(NULL);
void *reset_point;
-queue_filename *f = NULL;
+queue_filename * qf = NULL;
uschar subdirs[64];
/* If given a list of messages, build a chain containing their ids. */
if (count > 0)
{
queue_filename *last = NULL;
- for (i = 0; i < count; i++)
+ for (int i = 0; i < count; i++)
{
queue_filename *next =
store_get(sizeof(queue_filename) + Ustrlen(list[i]) + 2);
sprintf(CS next->text, "%s-H", list[i]);
next->dir_uschar = '*';
next->next = NULL;
- if (i == 0) f = next; else last->next = next;
+ if (i == 0) qf = next; else last->next = next;
last = next;
}
}
/* Otherwise get a list of the entire queue, in order if necessary. */
else
- f = queue_get_spool_list(
+ qf = queue_get_spool_list(
-1, /* entire queue */
subdirs, /* for holding sub list */
&subcount, /* for subcount */
/* Now scan the chain and print information, resetting store used
each time. */
-for (reset_point = store_get(0); f; f = f->next)
+for (reset_point = store_get(0);
+ qf;
+ spool_clear_header_globals(), store_reset(reset_point), qf = qf->next
+ )
{
int rc, save_errno;
int size = 0;
BOOL env_read;
message_size = 0;
- message_subdir[0] = f->dir_uschar;
- rc = spool_read_header(f->text, FALSE, count <= 0);
- if (rc == spool_read_notopen && errno == ENOENT && count <= 0) goto next;
+ message_subdir[0] = qf->dir_uschar;
+ rc = spool_read_header(qf->text, FALSE, count <= 0);
+ if (rc == spool_read_notopen && errno == ENOENT && count <= 0)
+ continue;
save_errno = errno;
env_read = (rc == spool_read_OK || rc == spool_read_hdrerror);
if (env_read)
{
- int ptr;
+ int i, ptr;
FILE *jread;
struct stat statbuf;
- uschar * fname = spool_fname(US"input", message_subdir, f->text, US"");
+ uschar * fname = spool_fname(US"input", message_subdir, qf->text, US"");
ptr = Ustrlen(fname)-1;
fname[ptr] = 'D';
if (Ustat(fname, &statbuf) == 0)
size = message_size + statbuf.st_size - SPOOL_DATA_START_OFFSET + 1;
- i = (now - received_time)/60; /* minutes on queue */
+ i = (now - received_time.tv_sec)/60; /* minutes on queue */
if (i > 90)
{
i = (i + 30)/60;
}
fprintf(stdout, "%s ", string_format_size(size, big_buffer));
- for (i = 0; i < 16; i++) fputc(f->text[i], stdout);
+ for (int i = 0; i < 16; i++) fputc(qf->text[i], stdout);
if (env_read && sender_address)
{
printf(" <%s>", sender_address);
- if (sender_set_untrusted) printf(" (%s)", originator_login);
+ if (f.sender_set_untrusted) printf(" (%s)", originator_login);
}
if (rc != spool_read_OK)
if (save_errno == ERRNO_SPOOLFORMAT)
{
struct stat statbuf;
- uschar * fname = spool_fname(US"input", message_subdir, f->text, US"");
+ uschar * fname = spool_fname(US"input", message_subdir, qf->text, US"");
if (Ustat(fname, &statbuf) == 0)
printf("*** spool format error: size=" OFF_T_FMT " ***",
if (rc != spool_read_hdrerror)
{
printf("\n\n");
- goto next;
+ continue;
}
}
- if (deliver_freeze) printf(" *** frozen ***");
+ if (f.deliver_freeze) printf(" *** frozen ***");
printf("\n");
if (recipients_list)
{
- for (i = 0; i < recipients_count; i++)
+ for (int i = 0; i < recipients_count; i++)
{
tree_node *delivered =
tree_search(tree_nonrecipients, recipients_list[i].address);
if (!delivered || option != 1)
- printf(" %s %s\n", (delivered != NULL)? "D":" ",
- recipients_list[i].address);
+ printf(" %s %s\n",
+ delivered ? "D" : " ", recipients_list[i].address);
if (delivered) delivered->data.val = TRUE;
}
if (option == 2 && tree_nonrecipients)
queue_list_extras(tree_nonrecipients);
printf("\n");
}
-
-next:
- received_protocol = NULL;
- sender_fullhost = sender_helo_name =
- sender_rcvhost = sender_host_address = sender_address = sender_ident = NULL;
- sender_host_authenticated = authenticated_sender = authenticated_id = NULL;
- interface_address = NULL;
- acl_var_m = NULL;
-
- store_reset(reset_point);
}
}
BOOL
queue_action(uschar *id, int action, uschar **argv, int argc, int recipients_arg)
{
-int i, j;
BOOL yield = TRUE;
BOOL removed = FALSE;
struct passwd *pw;
if (action >= MSG_SHOW_BODY)
{
- int fd, i, rc;
+ int fd, rc;
uschar *subdirectory, *suffix;
- if (!admin_user)
+ if (!f.admin_user)
{
printf("Permission denied\n");
return FALSE;
suffix = US"";
}
- for (i = 0; i < 2; i++)
+ for (int i = 0; i < 2; i++)
{
message_subdir[0] = split_spool_directory == (i == 0) ? id[5] : 0;
if ((fd = Uopen(spool_fname(subdirectory, message_subdir, id, suffix),
{
yield = FALSE;
printf("Spool data file for %s does not exist\n", id);
- if (action != MSG_REMOVE || !admin_user) return FALSE;
+ if (action != MSG_REMOVE || !f.admin_user) return FALSE;
printf("Continuing, to ensure all files removed\n");
}
else
printf("Spool read error for %s: %s\n", spoolname, strerror(errno));
else
printf("Spool format error for %s\n", spoolname);
- if (action != MSG_REMOVE || !admin_user)
+ if (action != MSG_REMOVE || !f.admin_user)
{
(void)close(deliver_datafile);
deliver_datafile = -1;
mess about, but the original sender is permitted to remove a message. That's
why we leave this check until after the headers are read. */
-if (!admin_user && (action != MSG_REMOVE || real_uid != originator_uid))
+if (!f.admin_user && (action != MSG_REMOVE || real_uid != originator_uid))
{
printf("Permission denied\n");
(void)close(deliver_datafile);
switch(action)
{
case MSG_SHOW_COPY:
- deliver_in_buffer = store_malloc(DELIVER_IN_BUFFER_SIZE);
- deliver_out_buffer = store_malloc(DELIVER_OUT_BUFFER_SIZE);
- transport_write_message(1, NULL, 0);
- break;
+ {
+ transport_ctx tctx = {{0}};
+ deliver_in_buffer = store_malloc(DELIVER_IN_BUFFER_SIZE);
+ deliver_out_buffer = store_malloc(DELIVER_OUT_BUFFER_SIZE);
+ tctx.u.fd = 1;
+ (void) transport_write_message(&tctx, 0);
+ break;
+ }
case MSG_FREEZE:
- if (deliver_freeze)
+ if (f.deliver_freeze)
{
yield = FALSE;
printf("is already frozen\n");
}
else
{
- deliver_freeze = TRUE;
- deliver_manual_thaw = FALSE;
+ f.deliver_freeze = TRUE;
+ f.deliver_manual_thaw = FALSE;
deliver_frozen_at = time(NULL);
if (spool_write_header(id, SW_MODIFYING, &errmsg) >= 0)
{
case MSG_THAW:
- if (!deliver_freeze)
+ if (!f.deliver_freeze)
{
yield = FALSE;
printf("is not frozen\n");
}
else
{
- deliver_freeze = FALSE;
- deliver_manual_thaw = TRUE;
+ f.deliver_freeze = FALSE;
+ f.deliver_manual_thaw = TRUE;
if (spool_write_header(id, SW_MODIFYING, &errmsg) >= 0)
{
printf("is no longer frozen\n");
suffix[2] = 0;
message_subdir[0] = id[5];
- for (j = 0; j < 2; message_subdir[0] = 0, j++)
+ for (int j = 0; j < 2; message_subdir[0] = 0, j++)
{
uschar * fname = spool_fname(US"msglog", message_subdir, id, US"");
DEBUG(D_any) debug_printf(" (ok)\n");
}
- for (i = 0; i < 3; i++)
+ for (int i = 0; i < 3; i++)
{
uschar * fname;
else printf("has been removed or did not exist\n");
if (removed)
{
+#ifndef DISABLE_EVENT
+ for (int i = 0; i < recipients_count; i++)
+ {
+ tree_node *delivered =
+ tree_search(tree_nonrecipients, recipients_list[i].address);
+ if (!delivered)
+ {
+ uschar * save_local = deliver_localpart;
+ const uschar * save_domain = deliver_domain;
+ uschar * addr = recipients_list[i].address, * errmsg = NULL;
+ int start, end, dom;
+
+ if (!parse_extract_address(addr, &errmsg, &start, &end, &dom, TRUE))
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "failed to parse address '%.100s'\n: %s", addr, errmsg);
+ else
+ {
+ deliver_localpart =
+ string_copyn(addr+start, dom ? (dom-1) - start : end - start);
+ deliver_domain = dom
+ ? CUS string_copyn(addr+dom, end - dom) : CUS"";
+
+ event_raise(event_action, US"msg:fail:internal",
+ string_sprintf("message removed by %s", username));
+
+ deliver_localpart = save_local;
+ deliver_domain = save_domain;
+ }
+ }
+ }
+ (void) event_raise(event_action, US"msg:complete", NULL);
+#endif
log_write(0, LOG_MAIN, "removed by %s", username);
log_write(0, LOG_MAIN, "Completed");
}
case MSG_MARK_ALL_DELIVERED:
- for (i = 0; i < recipients_count; i++)
- {
+ for (int i = 0; i < recipients_count; i++)
tree_add_nonrecipient(recipients_list[i].address);
- }
+
if (spool_write_header(id, SW_MODIFYING, &errmsg) >= 0)
{
printf("has been modified\n");
- for (i = 0; i < recipients_count; i++)
+ for (int i = 0; i < recipients_count; i++)
log_write(0, LOG_MAIN, "address <%s> marked delivered by %s",
recipients_list[i].address, username);
}
}
else if (action == MSG_MARK_DELIVERED)
{
+ int i;
for (i = 0; i < recipients_count; i++)
if (Ustrcmp(recipients_list[i].address, recipient) == 0) break;
if (i >= recipients_count)
void
queue_check_only(void)
{
-BOOL *set;
int sep = 0;
struct stat statbuf;
const uschar *s;
-uschar *ss, *name;
+uschar *ss;
uschar buffer[1024];
if (queue_only_file == NULL) return;
{
if (Ustrncmp(ss, "smtp", 4) == 0)
{
- name = US"queue_smtp";
- set = &queue_smtp;
ss += 4;
+ if (Ustat(ss, &statbuf) == 0)
+ {
+ f.queue_smtp = TRUE;
+ DEBUG(D_receive) debug_printf("queue_smtp set because %s exists\n", ss);
+ }
}
else
{
- name = US"queue_only";
- set = &queue_only;
- }
-
- if (Ustat(ss, &statbuf) == 0)
- {
- *set = TRUE;
- DEBUG(D_receive) debug_printf("%s set because %s exists\n", name, ss);
+ if (Ustat(ss, &statbuf) == 0)
+ {
+ queue_only = TRUE;
+ DEBUG(D_receive) debug_printf("queue_only set because %s exists\n", ss);
+ }
}
}
}