From fc7bae7f2fa3668ada9bd173e9f24c7166e1dd13 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Thu, 24 Oct 2019 23:34:19 +0100 Subject: [PATCH] Support moving messages across named queues. Bug 2456 --- doc/doc-docbook/spec.xfpt | 14 +++++++++++++ doc/doc-txt/NewStuff | 2 ++ src/src/exim.c | 25 ++++++++++++---------- src/src/functions.h | 21 +++++++++++++++---- src/src/globals.c | 1 + src/src/globals.h | 1 + src/src/macros.h | 2 +- src/src/queue.c | 8 ++++++++ src/src/spool_out.c | 26 ++++++++++++++--------- test/log/0576 | 8 ++++++++ test/runtest | 17 ++++++++++++++- test/scripts/0000-Basic/0576 | 40 ++++++++++++++++++++++++++++++++++++ test/stdout/0576 | 29 ++++++++++++++++++++++++++ 13 files changed, 167 insertions(+), 27 deletions(-) diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 4fa87ff9e..7cd3e8621 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -3963,6 +3963,20 @@ is sent to the sender, containing the text &"cancelled by administrator"&. Bounce messages are just discarded. This option can be used only by an admin user. +.new +.vitem &%-MG%&&~<&'queue&~name'&&~<&'message&~id'&>&~<&'message&~id'&>&~... +.oindex "&%-MG%&" +.cindex queue named +.cindex "named queues" +.cindex "queue" "moving messages" +This option requests that each listed message be moved from its current +queue to the given named queue. +The destination queue name argument is required, but can be an empty +string to define the default queue. +If the messages are not currently located in the default queue, +a &%-qG%& option will be required to define the source queue. +.wen + .vitem &%-Mmad%&&~<&'message&~id'&>&~<&'message&~id'&>&~... .oindex "&%-Mmad%&" .cindex "delivery" "cancelling all" diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index 731769629..5d0c8bd31 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -48,6 +48,8 @@ Version 4.93 GnuTLS was already there, being done purely by the library (server side only, and exim must be run as root). +16: Command-line option to move messages from one named queue to another. + Version 4.92 -------------- diff --git a/src/src/exim.c b/src/src/exim.c index 2b6297bf5..3290d6346 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -2787,6 +2787,11 @@ for (i = 1; i < argc; i++) msg_action = MSG_DELIVER; deliver_give_up = TRUE; } + else if (Ustrcmp(argrest, "G") == 0) + { + msg_action = MSG_SETQUEUE; + queue_name_dest = argv[++i]; + } else if (Ustrcmp(argrest, "mad") == 0) { msg_action = MSG_MARK_ALL_DELIVERED; @@ -4064,11 +4069,13 @@ count. Only an admin user can use the test interface to scan for email if (!f.admin_user) { BOOL debugset = (debug_selector & ~D_v) != 0; - if (deliver_give_up || f.daemon_listen || malware_test_file || - (count_queue && queue_list_requires_admin) || - (list_queue && queue_list_requires_admin) || - (queue_interval >= 0 && prod_requires_admin) || - (debugset && !f.running_in_test_harness)) + if ( deliver_give_up || f.daemon_listen || malware_test_file + || count_queue && queue_list_requires_admin + || list_queue && queue_list_requires_admin + || queue_interval >= 0 && prod_requires_admin + || queue_name_dest && prod_requires_admin + || debugset && !f.running_in_test_harness + ) exim_fail("exim:%s permission denied\n", debugset? " debugging" : ""); } @@ -4165,13 +4172,9 @@ now for those OS that require the first call to os_getloadavg() to be done as root. There will be further calls later for each message received. */ #ifdef LOAD_AVG_NEEDS_ROOT -if (receiving_message && - (queue_only_load >= 0 || - (f.is_inetd && smtp_load_reserve >= 0) - )) - { +if ( receiving_message + && (queue_only_load >= 0 || (f.is_inetd && smtp_load_reserve >= 0))) load_average = OS_GETLOADAVG(); - } #endif /* The queue_only configuration option can be overridden by -odx on the command diff --git a/src/src/functions.h b/src/src/functions.h index 8905d02a2..3b3a12b18 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -878,20 +878,33 @@ return string_sprintf("%s/%s/%s/%s", # endif static inline uschar * -spool_sname(const uschar * purpose, uschar * subdir) +spool_q_sname(const uschar * purpose, const uschar * q, uschar * subdir) { return string_sprintf("%s%s%s%s%s", - queue_name, *queue_name ? "/" : "", + q, *q ? "/" : "", purpose, *subdir ? "/" : "", subdir); } +static inline uschar * +spool_sname(const uschar * purpose, uschar * subdir) +{ +return spool_q_sname(purpose, queue_name, subdir); +} + +static inline uschar * +spool_q_fname(const uschar * purpose, const uschar * q, + const uschar * subdir, const uschar * fname, const uschar * suffix) +{ +return string_sprintf("%s/%s/%s/%s/%s%s", + spool_directory, q, purpose, subdir, fname, suffix); +} + static inline uschar * spool_fname(const uschar * purpose, const uschar * subdir, const uschar * fname, const uschar * suffix) { -return string_sprintf("%s/%s/%s/%s/%s%s", - spool_directory, queue_name, purpose, subdir, fname, suffix); +return spool_q_fname(purpose, queue_name, subdir, fname, suffix); } static inline void diff --git a/src/src/globals.c b/src/src/globals.c index 677c03e77..07665bf75 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -1194,6 +1194,7 @@ uschar *qualify_domain_sender = NULL; uschar *queue_domains = NULL; int queue_interval = -1; uschar *queue_name = US""; +uschar *queue_name_dest = NULL; uschar *queue_only_file = NULL; int queue_only_load = -1; uschar *queue_run_max = US"5"; diff --git a/src/src/globals.h b/src/src/globals.h index e4725a719..0466da500 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -787,6 +787,7 @@ extern pid_t queue_run_pid; /* PID of the queue running process or 0 extern int queue_run_pipe; /* Pipe for synchronizing */ extern int queue_interval; /* Queue running interval */ extern uschar *queue_name; /* Name of queue, if nondefault spooling */ +extern uschar *queue_name_dest; /* Destination queue, for moving messages */ extern BOOL queue_only; /* TRUE to disable immediate delivery */ extern int queue_only_load; /* Max load before auto-queue */ extern BOOL queue_only_load_latch; /* Latch queue_only_load TRUE */ diff --git a/src/src/macros.h b/src/src/macros.h index e36c09c47..76913d64e 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -845,7 +845,7 @@ enum { enum { MSG_DELIVER, MSG_FREEZE, MSG_REMOVE, MSG_THAW, MSG_ADD_RECIPIENT, MSG_MARK_ALL_DELIVERED, MSG_MARK_DELIVERED, MSG_EDIT_SENDER, - MSG_SHOW_COPY, MSG_LOAD, + MSG_SHOW_COPY, MSG_LOAD, MSG_SETQUEUE, /* These ones must be last: a test for >= MSG_SHOW_BODY is used to test for actions that list individual spool files. */ MSG_SHOW_BODY, MSG_SHOW_HEADER, MSG_SHOW_LOG }; diff --git a/src/src/queue.c b/src/src/queue.c index 670f51c45..d9ff13375 100644 --- a/src/src/queue.c +++ b/src/src/queue.c @@ -1265,6 +1265,14 @@ switch(action) } + case MSG_SETQUEUE: + /* The global "queue_name_dest" is used as destination, "queue_name" + as source */ + + spool_move_message(id, message_subdir, US"", US""); + break; + + case MSG_MARK_ALL_DELIVERED: for (int i = 0; i < recipients_count; i++) tree_add_nonrecipient(recipients_list[i].address); diff --git a/src/src/spool_out.c b/src/src/spool_out.c index acc6c7b5f..463961f57 100644 --- a/src/src/spool_out.c +++ b/src/src/spool_out.c @@ -417,6 +417,7 @@ start-up time. Arguments: dir base directory name + dq destiinationqueue name subdir subdirectory name id message id suffix suffix to add to id @@ -429,11 +430,11 @@ Returns: TRUE if all went well */ static BOOL -make_link(uschar *dir, uschar *subdir, uschar *id, uschar *suffix, uschar *from, - uschar *to, BOOL noentok) +make_link(uschar *dir, uschar * dq, uschar *subdir, uschar *id, uschar *suffix, + uschar *from, uschar *to, BOOL noentok) { uschar * fname = spool_fname(string_sprintf("%s%s", from, dir), subdir, id, suffix); -uschar * tname = spool_fname(string_sprintf("%s%s", to, dir), subdir, id, suffix); +uschar * tname = spool_q_fname(string_sprintf("%s%s", to, dir), dq, subdir, id, suffix); if (Ulink(fname, tname) < 0 && (!noentok || errno != ENOENT)) { log_write(0, LOG_MAIN|LOG_PANIC, "link(\"%s\", \"%s\") failed while moving " @@ -503,13 +504,15 @@ Returns: TRUE if all is well BOOL spool_move_message(uschar *id, uschar *subdir, uschar *from, uschar *to) { +uschar * dest_qname = queue_name_dest ? queue_name_dest : queue_name; + /* Create any output directories that do not exist. */ (void) directory_make(spool_directory, - spool_sname(string_sprintf("%sinput", to), subdir), + spool_q_sname(string_sprintf("%sinput", to), dest_qname, subdir), INPUT_DIRECTORY_MODE, TRUE); (void) directory_make(spool_directory, - spool_sname(string_sprintf("%smsglog", to), subdir), + spool_q_sname(string_sprintf("%smsglog", to), dest_qname, subdir), INPUT_DIRECTORY_MODE, TRUE); /* Move the message by first creating new hard links for all the files, and @@ -521,9 +524,9 @@ rule of waiting for a -H file before doing anything. When moving messages off the mail spool, the -D file should be open and locked at the time, thus keeping Exim's hands off. */ -if (!make_link(US"msglog", subdir, id, US"", from, to, TRUE) || - !make_link(US"input", subdir, id, US"-D", from, to, FALSE) || - !make_link(US"input", subdir, id, US"-H", from, to, FALSE)) +if (!make_link(US"msglog", dest_qname, subdir, id, US"", from, to, TRUE) || + !make_link(US"input", dest_qname, subdir, id, US"-D", from, to, FALSE) || + !make_link(US"input", dest_qname, subdir, id, US"-H", from, to, FALSE)) return FALSE; if (!break_link(US"input", subdir, id, US"-H", from, FALSE) || @@ -531,8 +534,11 @@ if (!break_link(US"input", subdir, id, US"-H", from, FALSE) || !break_link(US"msglog", subdir, id, US"", from, TRUE)) return FALSE; -log_write(0, LOG_MAIN, "moved from %sinput, %smsglog to %sinput, %smsglog", - from, from, to, to); +log_write(0, LOG_MAIN, "moved from %s%s%s%sinput, %smsglog to %s%s%s%sinput, %smsglog", + *queue_name?"(":"", *queue_name?queue_name:US"", *queue_name?") ":"", + from, from, + *dest_qname?"(":"", *dest_qname?dest_qname:US"", *dest_qname?") ":"", + to, to); return TRUE; } diff --git a/test/log/0576 b/test/log/0576 index 8c1cb73df..246b10578 100644 --- a/test/log/0576 +++ b/test/log/0576 @@ -20,6 +20,14 @@ 1999-03-02 09:44:33 10HmbA-0005vi-00 => alternate F= R=all T=dump 1999-03-02 09:44:33 10HmbA-0005vi-00 Completed 1999-03-02 09:44:33 End queue run: pid=pppp +1999-03-02 09:44:33 using queue '' +1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local-smtp S=sss for normal@test.ex +1999-03-02 09:44:33 using queue 'alternate' +1999-03-02 09:44:33 10HmbC-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local-smtp S=sss Q=alternate for alternate@test.ex +1999-03-02 09:44:33 10HmbB-0005vi-00 moved from input, msglog to (third) input, msglog +1999-03-02 09:44:33 10HmbC-0005vi-00 moved from (alternate) input, msglog to (third) input, msglog +1999-03-02 09:44:33 10HmbB-0005vi-00 moved from (third) input, msglog to input, msglog +1999-03-02 09:44:33 10HmbC-0005vi-00 moved from (third) input, msglog to input, msglog ******** SERVER ******** 1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, -qGlowpri/3s, not listening for SMTP diff --git a/test/runtest b/test/runtest index 0d6bcd1d7..db855baf9 100755 --- a/test/runtest +++ b/test/runtest @@ -2546,9 +2546,24 @@ elsif (/^((?i:[A-Z\d_]+=\S+\s+)+)?(\d+)?\s*(sudo(?:\s+-u\s+(\w+))?\s+)?exim(_\S+ if ($args =~ /\$msg/) { - my @listcmd = ("$parm_cwd/eximdir/exim", '-bp', + my($queuespec); + if ($args =~ /-qG\w+/) { $queuespec = $&; } + + my @listcmd; + + if (defined $queuespec) + { + @listcmd = ("$parm_cwd/eximdir/exim", '-bp', + $queuespec, "-DEXIM_PATH=$parm_cwd/eximdir/exim", -C => "$parm_cwd/test-config"); + } + else + { + @listcmd = ("$parm_cwd/eximdir/exim", '-bp', + "-DEXIM_PATH=$parm_cwd/eximdir/exim", + -C => "$parm_cwd/test-config"); + } print ">> Getting queue list from:\n>> @listcmd\n" if $debug; # We need the message ids sorted in ascending order. # Message id is: --. On some systems (*BSD) the diff --git a/test/scripts/0000-Basic/0576 b/test/scripts/0000-Basic/0576 index dedc73d23..becd16056 100644 --- a/test/scripts/0000-Basic/0576 +++ b/test/scripts/0000-Basic/0576 @@ -72,4 +72,44 @@ sudo mv DIR/spool/alternate/input/* DIR/spool/input/ exim -q **** # +# +# Native queue transfer +exim -bs +MAIL FROM: +RCPT TO: +DATA +Subject: test + +foo +. +RSET +MAIL FROM: +RCPT TO: +DATA +Subject: test + +foo +. +QUIT +**** +exim -bp +**** +exim -bp -qGalternate +**** +# +exim -MG third $msg1 +**** +exim -qGalternate -MG third $msg1 +**** +exim -bp -qGthird +**** +exim -qGthird -MG '' $msg1 $msg2 +**** +exim -bp +**** +exim -bp -qGalternate +**** +exim -bp -qGthird +**** +# no_stderr_check diff --git a/test/stdout/0576 b/test/stdout/0576 index a15ecdd5d..91c57a20d 100644 --- a/test/stdout/0576 +++ b/test/stdout/0576 @@ -29,6 +29,35 @@ 354 Enter message, ending with "." on a line by itself 250 OK id=10HmbA-0005vi-00 221 the.local.host.name closing connection +220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 +250 OK +250 Accepted +354 Enter message, ending with "." on a line by itself +250 OK id=10HmbB-0005vi-00 +250 Reset OK +250 OK +250 Accepted +354 Enter message, ending with "." on a line by itself +250 OK id=10HmbC-0005vi-00 +221 the.local.host.name closing connection + 0m sss 10HmbB-0005vi-00 + normal@test.ex + + 0m sss 10HmbC-0005vi-00 + alternate@test.ex + +Message 10HmbB-0005vi-00 Message 10HmbC-0005vi-00 0m 323 10HmbB-0005vi-00 + normal@test.ex + + 0m sss 10HmbC-0005vi-00 + alternate@test.ex + +Message 10HmbB-0005vi-00 Message 10HmbC-0005vi-00 0m 323 10HmbB-0005vi-00 + normal@test.ex + + 0m sss 10HmbC-0005vi-00 + alternate@test.ex + ******** SERVER ******** ### default q -- 2.30.2