diff --git a/postfix/HISTORY b/postfix/HISTORY
index dd04f009a..1cb927103 100644
--- a/postfix/HISTORY
+++ b/postfix/HISTORY
@@ -15242,3 +15242,20 @@ Apologies for any names omitted.
The queue file would be corrupted when the delay_warning_time
record was marked as "done" after sending the "your mail
is delayed" notice. File: qmgr/qmgr_message.c.
+
+20090522
+
+ Bugfix (introduced: Postfix 2.3). The cleanup server
+ rejected mail with records of type REC_TYPE_DRCP (recipient
+ deleted by Milter), but such records could be present in
+ mail re-submitted with "postsuper -r". Found during code
+ review. Files: global/record.h, cleanup/cleanup_envelope.c.
+
+20090524
+
+ Feature: new postcat options: -e (print envelope), -h (print
+ header), and -b (print body). Specify "postcat -bh" to
+ suppress information about envelope records, and "postcat
+ -h" to get the message header only. With large messages,
+ "postcat -h" is much faster than manually stripping the
+ message body from the output. File: postcat/postcat.c.
diff --git a/postfix/WISHLIST b/postfix/WISHLIST
index 3a097008f..b3e7caf4a 100644
--- a/postfix/WISHLIST
+++ b/postfix/WISHLIST
@@ -2,14 +2,6 @@ Wish list:
Remove this file from the stable release.
- The cleanup server rejects mail with REC_TYPE_DRCP (recipient
- deleted by Milter). What happens after "postsuper -r" with
- such mail? If it is rejected, the mail is discarded.
-
- With "mixed recipient and other" queue files that have many
- recpients, can qmgr_message_read() set message->rcpt_offset
- multiple times? If it does, then recipients will be skipped.
-
Apply header_checks and body_checks when Milters add or
modify the message content. Use case: Milters that add
spamminess headers.
diff --git a/postfix/html/postcat.1.html b/postfix/html/postcat.1.html
index e5b841e23..cfd23409a 100644
--- a/postfix/html/postcat.1.html
+++ b/postfix/html/postcat.1.html
@@ -10,7 +10,7 @@ POSTCAT(1) POSTCAT(1)
postcat - show Postfix queue file contents
SYNOPSIS
- postcat [-oqv] [-c config_dir] [files...]
+ postcat [-bhmoqv] [-c config_dir] [files...]
DESCRIPTION
The postcat(1) command prints the contents of the named
@@ -18,13 +18,36 @@ POSTCAT(1) POSTCAT(1)
in Postfix queue file format. If no files are specified on
the command line, the program reads from standard input.
+ By default, postcat(1) behaves as if all three options -b,
+ -e, and -h are given. To view message content only, spec-
+ ify -bh (Postfix 2.7 and later).
+
Options:
+ -b Show body content. The -b option starts producing
+ output at the first non-header line, and stops when
+ the end of the message is reached.
+
+ This feature is available in Postfix version 2.7
+ and later.
+
-c config_dir
- The main.cf configuration file is in the named
+ The main.cf configuration file is in the named
directory instead of the default configuration
directory.
+ -e Show message envelope content.
+
+ This feature is available in Postfix version 2.7
+ and later.
+
+ -h Show message header content. The -h option pro-
+ duces output from the beginning of the message up
+ to, but not including, the first non-header line.
+
+ This feature is available in Postfix version 2.7
+ and later.
+
-o Print the queue file offset of each record.
-q Search the Postfix queue for the named files
@@ -33,7 +56,7 @@ POSTCAT(1) POSTCAT(1)
Available in Postfix version 2.0 and later.
-v Enable verbose logging for debugging purposes. Mul-
- tiple -v options make the software increasingly
+ tiple -v options make the software increasingly
verbose.
DIAGNOSTICS
@@ -44,18 +67,18 @@ POSTCAT(1) POSTCAT(1)
Directory with Postfix configuration files.
CONFIGURATION PARAMETERS
- The following main.cf parameters are especially relevant
+ The following main.cf parameters are especially relevant
to this program.
- The text below provides only a parameter summary. See
+ The text below provides only a parameter summary. See
postconf(5) for more details including examples.
config_directory (see 'postconf -d' output)
- The default location of the Postfix main.cf and
+ The default location of the Postfix main.cf and
master.cf configuration files.
queue_directory (see 'postconf -d' output)
- The location of the Postfix top-level queue direc-
+ The location of the Postfix top-level queue direc-
tory.
FILES
@@ -65,7 +88,7 @@ POSTCAT(1) POSTCAT(1)
postconf(5), Postfix configuration
LICENSE
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
AUTHOR(S)
diff --git a/postfix/html/postfix-wrapper.5.html b/postfix/html/postfix-wrapper.5.html
index 925fd207e..d84edda88 100644
--- a/postfix/html/postfix-wrapper.5.html
+++ b/postfix/html/postfix-wrapper.5.html
@@ -10,8 +10,8 @@ POSTFIX-WRAPPER(5) POSTFIX-WRAPPER(5)
postfix-wrapper - Postfix multi-instance API
DESCRIPTION
- Postfix versions 2.6 and later provide support for multi-
- ple Postfix instances. Instances share executable files
+ Support for managing multiple Postfix instances is avail-
+ able as of version 2.6. Instances share executable files
and documentation, but have their own directories for con-
figuration, queue and data files.
@@ -59,53 +59,53 @@ POSTFIX-WRAPPER(5) POSTFIX-WRAPPER(5)
environment variable (the -c command-line option has
higher precedence).
- When no Postfix instance information is specified, the
- postfix(1) command will operate on all Postfix instances.
+ Otherwise, the postfix(1) command will operate on all
+ Postfix instances.
ENABLING POSTFIX(1) MULTI-INSTANCE MODE
- By default, the postfix(1) command operates in single-
- instance mode. In this mode the command invokes the post-
- fix-script file directly (currently installed in the dae-
- mon directory). This file contains the commands that
- start or stop one Postfix instance, that upgrade the con-
+ By default, the postfix(1) command operates in single-
+ instance mode. In this mode the command invokes the post-
+ fix-script file directly (currently installed in the dae-
+ mon directory). This file contains the commands that
+ start or stop one Postfix instance, that upgrade the con-
figuration of one Postfix instance, and so on.
- When the postfix(1) command operates in multi-instance
- mode as discussed below, the command needs to execute
- start, stop, etc. commands for each Postfix instance.
- This multiplication of commands is handled by a multi-
+ When the postfix(1) command operates in multi-instance
+ mode as discussed below, the command needs to execute
+ start, stop, etc. commands for each Postfix instance.
+ This multiplication of commands is handled by a multi-
instance manager program.
Turning on postfix(1) multi-instance mode goes as follows:
in the default Postfix instance's main.cf file, 1) specify
- the pathname of a multi-instance manager program with the
- multi_instance_wrapper parameter; 2) populate the
- multi_instance_directories parameter with the configura-
- tion directory pathnames of additional Postfix instances.
+ the pathname of a multi-instance manager program with the
+ multi_instance_wrapper parameter; 2) populate the
+ multi_instance_directories parameter with the configura-
+ tion directory pathnames of additional Postfix instances.
For example:
/etc/postfix/main.cf:
multi_instance_wrapper = $daemon_directory/postfix-wrapper
multi_instance_directories = /etc/postfix-test
- The $daemon_directory/postfix-wrapper file implements a
- simple manager and contains instructions for creating
- Postfix instances by hand. The postmulti(1) command pro-
- vides a more extensive implementation including support
+ The $daemon_directory/postfix-wrapper file implements a
+ simple manager and contains instructions for creating
+ Postfix instances by hand. The postmulti(1) command pro-
+ vides a more extensive implementation including support
for life-cycle management.
- The multi_instance_directories and other main.cf parame-
+ The multi_instance_directories and other main.cf parame-
ters are listed below in the CONFIGURATION PARAMETERS sec-
tion.
In multi-instance mode, the postfix(1) command invokes the
- $multi_instance_wrapper command instead of the postfix-
- script file. This multi-instance manager in turn executes
- the postfix(1) command in single-instance mode for each
+ $multi_instance_wrapper command instead of the postfix-
+ script file. This multi-instance manager in turn executes
+ the postfix(1) command in single-instance mode for each
Postfix instance.
- To illustrate the main ideas behind multi-instance opera-
- tion, below is an example of a simple but useful multi-
+ To illustrate the main ideas behind multi-instance opera-
+ tion, below is an example of a simple but useful multi-
instance manager implementation:
#!/bin/sh
@@ -139,124 +139,124 @@ POSTFIX-WRAPPER(5) POSTFIX-WRAPPER(5)
PER-INSTANCE MULTI-INSTANCE MANAGER CONTROLS
Each Postfix instance has its own main.cf file with param-
eters that control how the multi-instance manager operates
- on that instance. This section discusses the most impor-
+ on that instance. This section discusses the most impor-
tant settings.
The setting "multi_instance_enable = yes" allows the
- multi-instance manager to start (stop, etc.) the corre-
- sponding Postfix instance. For safety reasons, this set-
+ multi-instance manager to start (stop, etc.) the corre-
+ sponding Postfix instance. For safety reasons, this set-
ting is not the default.
The default setting "multi_instance_enable = no" is useful
for manual testing with "postfix -c /path/name start" etc.
- The multi-instance manager will not start such an
- instance, and it will skip commands such as "stop" or
- "flush" that require a running Postfix instance. The
+ The multi-instance manager will not start such an
+ instance, and it will skip commands such as "stop" or
+ "flush" that require a running Postfix instance. The
multi-instance manager will execute commands such as
"check", "set-permissions" or "upgrade-configuration", and
- it will replace "start" by "check" so that problems will
+ it will replace "start" by "check" so that problems will
be reported even when the instance is disabled.
MAINTAINING SHARED AND NON-SHARED FILES
- Some files are shared between Postfix instances, such as
+ Some files are shared between Postfix instances, such as
executables and manpages, and some files are per-instance,
- such as configuration files, mail queue files, and data
- files. See the NON-SHARED FILES section below for a list
+ such as configuration files, mail queue files, and data
+ files. See the NON-SHARED FILES section below for a list
of per-instance files.
Before Postfix multi-instance support was implemented, the
- executables, manpages, etc., have always been maintained
+ executables, manpages, etc., have always been maintained
as part of the default Postfix instance.
- With multi-instance support, we simply continue to do
- this. Specifically, a Postfix instance will not check or
- update shared files when that instance's config_directory
- value is listed with the default main.cf file's
+ With multi-instance support, we simply continue to do
+ this. Specifically, a Postfix instance will not check or
+ update shared files when that instance's config_directory
+ value is listed with the default main.cf file's
multi_instance_directories parameter.
The consequence of this approach is that the default Post-
- fix instance should be checked and updated before any
+ fix instance should be checked and updated before any
other instances.
MULTI-INSTANCE API SUMMARY
Only the multi-instance manager implements support for the
- multi_instance_enable configuration parameter. The multi-
- instance manager will start only Postfix instances whose
- main.cf file has "multi_instance_enable = yes". A setting
+ multi_instance_enable configuration parameter. The multi-
+ instance manager will start only Postfix instances whose
+ main.cf file has "multi_instance_enable = yes". A setting
of "no" allows a Postfix instance to be tested by hand.
The postfix(1) command operates on only one Postfix
- instance when the -c option is specified, or when
+ instance when the -c option is specified, or when
MAIL_CONFIG is present in the process environment. This is
necessary to terminate recursion.
- Otherwise, when the multi_instance_directories parameter
- value is non-empty, the postfix(1) command executes the
- command specified with the multi_instance_wrapper parame-
- ter, instead of executing the commands in postfix-script.
+ Otherwise, when the multi_instance_directories parameter
+ value is non-empty, the postfix(1) command executes the
+ command specified with the multi_instance_wrapper parame-
+ ter, instead of executing the commands in postfix-script.
- The multi-instance manager skips commands such as "stop"
- or "reload" that require a running Postfix instance, when
- an instance does not have "multi_instance_enable = yes".
+ The multi-instance manager skips commands such as "stop"
+ or "reload" that require a running Postfix instance, when
+ an instance does not have "multi_instance_enable = yes".
This avoids false error messages.
- The multi-instance manager replaces a "start" command by
- "check" when a Postfix instance's main.cf file does not
+ The multi-instance manager replaces a "start" command by
+ "check" when a Postfix instance's main.cf file does not
have "multi_instance_enable = yes". This substitution
- ensures that problems will be reported even when the
+ ensures that problems will be reported even when the
instance is disabled.
- No Postfix command or script will update or check shared
- files when its config_directory value is listed in the
- default main.cf's multi_instance_directories parameter
- value. Therefore, the default instance should be checked
- and updated before any Postfix instances that depend on
+ No Postfix command or script will update or check shared
+ files when its config_directory value is listed in the
+ default main.cf's multi_instance_directories parameter
+ value. Therefore, the default instance should be checked
+ and updated before any Postfix instances that depend on
it.
- Set-gid commands such as postdrop(1) and postqueue(1)
- effectively append the multi_instance_directories parame-
- ter value to the legacy alternate_config_directories
- parameter value. The commands use this information to
- determine whether a -c option or MAIL_CONFIG environment
+ Set-gid commands such as postdrop(1) and postqueue(1)
+ effectively append the multi_instance_directories parame-
+ ter value to the legacy alternate_config_directories
+ parameter value. The commands use this information to
+ determine whether a -c option or MAIL_CONFIG environment
setting specifies a legitimate value.
- The legacy alternate_config_directories parameter remains
- necessary for non-default Postfix instances that are run-
- ning different versions of Postfix, or that are not man-
+ The legacy alternate_config_directories parameter remains
+ necessary for non-default Postfix instances that are run-
+ ning different versions of Postfix, or that are not man-
aged together with the default Postfix instance.
ENVIRONMENT VARIABLES
MAIL_CONFIG
When present, this forces the postfix(1) command to
- operate only on the specified Postfix instance.
- This environment variable is exported by the post-
- fix(1) -c option, so that postfix(1) commands in
+ operate only on the specified Postfix instance.
+ This environment variable is exported by the post-
+ fix(1) -c option, so that postfix(1) commands in
descendant processes will work correctly.
CONFIGURATION PARAMETERS
- The text below provides only a parameter summary. See
+ The text below provides only a parameter summary. See
postconf(5) for more details.
multi_instance_directories (empty)
- An optional list of non-default Postfix configura-
+ An optional list of non-default Postfix configura-
tion directories; these directories belong to addi-
- tional Postfix instances that share the Postfix
+ tional Postfix instances that share the Postfix
executable files and documentation with the default
- Postfix instance, and that are started, stopped,
+ Postfix instance, and that are started, stopped,
etc., together with the default Postfix instance.
multi_instance_wrapper (empty)
- The pathname of a multi-instance manager command
- that the postfix(1) command invokes when the
- multi_instance_directories parameter value is non-
+ The pathname of a multi-instance manager command
+ that the postfix(1) command invokes when the
+ multi_instance_directories parameter value is non-
empty.
multi_instance_name (empty)
- The optional instance name of this Postfix
+ The optional instance name of this Postfix
instance.
multi_instance_group (empty)
- The optional instance group name of this Postfix
+ The optional instance group name of this Postfix
instance.
multi_instance_enable (no)
@@ -265,7 +265,7 @@ POSTFIX-WRAPPER(5) POSTFIX-WRAPPER(5)
NON-SHARED FILES
config_directory (see 'postconf -d' output)
- The default location of the Postfix main.cf and
+ The default location of the Postfix main.cf and
master.cf configuration files.
data_directory (see 'postconf -d' output)
@@ -273,7 +273,7 @@ POSTFIX-WRAPPER(5) POSTFIX-WRAPPER(5)
example: caches, pseudo-random numbers).
queue_directory (see 'postconf -d' output)
- The location of the Postfix top-level queue direc-
+ The location of the Postfix top-level queue direc-
tory.
SEE ALSO
@@ -282,7 +282,7 @@ POSTFIX-WRAPPER(5) POSTFIX-WRAPPER(5)
$daemon_directory/postfix-wrapper simple multi-instance manager
LICENSE
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
AUTHOR(S)
diff --git a/postfix/man/man1/postcat.1 b/postfix/man/man1/postcat.1
index 4c40c6b4f..0a83ee41c 100644
--- a/postfix/man/man1/postcat.1
+++ b/postfix/man/man1/postcat.1
@@ -8,7 +8,7 @@ show Postfix queue file contents
.SH "SYNOPSIS"
.na
.nf
-\fBpostcat\fR [\fB-oqv\fR] [\fB-c \fIconfig_dir\fR] [\fIfiles\fR...]
+\fBpostcat\fR [\fB-bhmoqv\fR] [\fB-c \fIconfig_dir\fR] [\fIfiles\fR...]
.SH DESCRIPTION
.ad
.fi
@@ -18,10 +18,30 @@ to be in Postfix queue file format. If no
\fIfiles\fR are specified on the command line, the program
reads from standard input.
+By default, \fBpostcat\fR(1) behaves as if all three options
+\fB-b\fR, \fB-e\fR, and \fB-h\fR are given. To view message
+content only, specify \fB-bh\fR (Postfix 2.7 and later).
+
Options:
+.IP \fB-b\fR
+Show body content. The \fB-b\fR option starts producing
+output at the first non-header line, and stops when the end
+of the message is reached.
+.sp
+This feature is available in Postfix version 2.7 and later.
.IP "\fB-c \fIconfig_dir\fR"
The \fBmain.cf\fR configuration file is in the named directory
instead of the default configuration directory.
+.IP \fB-e\fR
+Show message envelope content.
+.sp
+This feature is available in Postfix version 2.7 and later.
+.IP \fB-h\fR
+Show message header content. The \fB-h\fR option produces
+output from the beginning of the message up to, but not
+including, the first non-header line.
+.sp
+This feature is available in Postfix version 2.7 and later.
.IP \fB-o\fR
Print the queue file offset of each record.
.IP \fB-q\fR
diff --git a/postfix/man/man5/postfix-wrapper.5 b/postfix/man/man5/postfix-wrapper.5
index d43a0a990..75f69ad00 100644
--- a/postfix/man/man5/postfix-wrapper.5
+++ b/postfix/man/man5/postfix-wrapper.5
@@ -8,8 +8,8 @@ Postfix multi-instance API
.SH DESCRIPTION
.ad
.fi
-Postfix versions 2.6 and later provide support for multiple
-Postfix instances. Instances share executable files and
+Support for managing multiple Postfix instances is available
+as of version 2.6. Instances share executable files and
documentation, but have their own directories for configuration,
queue and data files.
@@ -62,8 +62,8 @@ Alternatively, the postfix(1) command accepts the instance's
configuration directory via the MAIL_CONFIG environment
variable (the -c command-line option has higher precedence).
-When no Postfix instance information is specified, the
-postfix(1) command will operate on all Postfix instances.
+Otherwise, the postfix(1) command will operate on all Postfix
+instances.
.SH "ENABLING POSTFIX(1) MULTI-INSTANCE MODE"
.na
.nf
diff --git a/postfix/proto/postfix-wrapper b/postfix/proto/postfix-wrapper
index abf3f56be..67c82a078 100644
--- a/postfix/proto/postfix-wrapper
+++ b/postfix/proto/postfix-wrapper
@@ -4,8 +4,8 @@
# SUMMARY
# Postfix multi-instance API
# DESCRIPTION
-# Postfix versions 2.6 and later provide support for multiple
-# Postfix instances. Instances share executable files and
+# Support for managing multiple Postfix instances is available
+# as of version 2.6. Instances share executable files and
# documentation, but have their own directories for configuration,
# queue and data files.
#
@@ -54,8 +54,8 @@
# configuration directory via the MAIL_CONFIG environment
# variable (the -c command-line option has higher precedence).
#
-# When no Postfix instance information is specified, the
-# postfix(1) command will operate on all Postfix instances.
+# Otherwise, the postfix(1) command will operate on all Postfix
+# instances.
# ENABLING POSTFIX(1) MULTI-INSTANCE MODE
# .ad
# .fi
diff --git a/postfix/src/cleanup/cleanup.c b/postfix/src/cleanup/cleanup.c
index 0289931e9..36f4f7948 100644
--- a/postfix/src/cleanup/cleanup.c
+++ b/postfix/src/cleanup/cleanup.c
@@ -462,8 +462,7 @@ static void cleanup_service(VSTREAM *src, char *unused_service, char **argv)
state->errs |= CLEANUP_STAT_BAD;
break;
}
- if (type == REC_TYPE_PTR || type == REC_TYPE_DTXT
- || type == REC_TYPE_DRCP) {
+ if (REC_GET_HIDDEN_TYPE(type)) {
msg_warn("%s: record type %d not allowed - discarding this message",
state->queue_id, type);
state->errs |= CLEANUP_STAT_BAD;
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index b08b75bb6..f045565a4 100644
--- a/postfix/src/global/mail_version.h
+++ b/postfix/src/global/mail_version.h
@@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20090519"
+#define MAIL_RELEASE_DATE "20090524"
#define MAIL_VERSION_NUMBER "2.7"
#ifdef SNAPSHOT
diff --git a/postfix/src/global/record.c b/postfix/src/global/record.c
index b82548ce5..9686d35c8 100644
--- a/postfix/src/global/record.c
+++ b/postfix/src/global/record.c
@@ -61,6 +61,9 @@
/* REC_SPACE_NEED(buflen, reclen)
/* ssize_t buflen;
/* ssize_t reclen;
+/*
+/* REC_GET_HIDDEN_TYPE(type)
+/* int type;
/* DESCRIPTION
/* This module reads and writes typed variable-length records.
/* Each record contains a 1-byte type code (0..255), a length
@@ -89,6 +92,10 @@
/* enables the REC_FLAG_FOLLOW_PTR, REC_FLAG_SKIP_DTXT
/* and REC_FLAG_SEEK_END features.
/*
+/* REC_GET_HIDDEN_TYPE() is an unsafe macro that returns
+/* non-zero when the specified record type is "not exposed"
+/* by rec_get().
+/*
/* rec_put() stores the specified record and returns the record
/* type, or REC_TYPE_ERROR in case of problems.
/*
diff --git a/postfix/src/global/record.h b/postfix/src/global/record.h
index 472da46c9..c685436b1 100644
--- a/postfix/src/global/record.h
+++ b/postfix/src/global/record.h
@@ -42,14 +42,17 @@ extern int rec_pad(VSTREAM *, int, int);
#define REC_PUT_BUF(v, t, b) rec_put((v), (t), vstring_str(b), VSTRING_LEN(b))
-#define REC_FLAG_NONE (0)
-#define REC_FLAG_FOLLOW_PTR (1<<0) /* follow PTR records */
-#define REC_FLAG_SKIP_DTXT (1<<1) /* skip DTXT records */
-#define REC_FLAG_SEEK_END (1<<2) /* seek EOF after END record */
+#define REC_FLAG_NONE (0)
+#define REC_FLAG_FOLLOW_PTR (1<<0) /* follow PTR records */
+#define REC_FLAG_SKIP_DTXT (1<<1) /* skip DTXT records */
+#define REC_FLAG_SEEK_END (1<<2) /* seek EOF after END record */
#define REC_FLAG_DEFAULT \
(REC_FLAG_FOLLOW_PTR | REC_FLAG_SKIP_DTXT | REC_FLAG_SEEK_END)
+#define REC_GET_HIDDEN_TYPE(t) \
+ ((t) == REC_TYPE_PTR || (t) == REC_TYPE_DTXT)
+
#define rec_get(fp, buf, limit) \
rec_get_raw((fp), (buf), (limit), REC_FLAG_DEFAULT)
diff --git a/postfix/src/postcat/Makefile.in b/postfix/src/postcat/Makefile.in
index 237bf6092..1b08ad60b 100644
--- a/postfix/src/postcat/Makefile.in
+++ b/postfix/src/postcat/Makefile.in
@@ -22,7 +22,7 @@ Makefile: Makefile.in
test: $(TESTPROG)
-tests:
+tests: default_test ebh_test e_test b_test h_test eb_test eh_test bh_test
root_tests:
@@ -47,6 +47,46 @@ clean:
tidy: clean
+default_test: test-queue-file default_test.ref
+ ./postcat test-queue-file >postcat.tmp 2>&1
+ diff default_test.ref postcat.tmp
+ rm -f postcat.tmp
+
+ebh_test: test-queue-file default_test.ref
+ ./postcat -ebh test-queue-file >postcat.tmp 2>&1
+ diff default_test.ref postcat.tmp
+ rm -f postcat.tmp
+
+e_test: test-queue-file e_test.ref
+ ./postcat -e test-queue-file >postcat.tmp 2>&1
+ diff e_test.ref postcat.tmp
+ rm -f postcat.tmp
+
+b_test: test-queue-file b_test.ref
+ ./postcat -b test-queue-file >postcat.tmp 2>&1
+ diff b_test.ref postcat.tmp
+ rm -f postcat.tmp
+
+h_test: test-queue-file h_test.ref
+ ./postcat -h test-queue-file >postcat.tmp 2>&1
+ diff h_test.ref postcat.tmp
+ rm -f postcat.tmp
+
+eb_test: test-queue-file eb_test.ref
+ ./postcat -eb test-queue-file >postcat.tmp 2>&1
+ diff eb_test.ref postcat.tmp
+ rm -f postcat.tmp
+
+eh_test: test-queue-file eh_test.ref
+ ./postcat -eh test-queue-file >postcat.tmp 2>&1
+ diff eh_test.ref postcat.tmp
+ rm -f postcat.tmp
+
+bh_test: test-queue-file bh_test.ref
+ ./postcat -bh test-queue-file >postcat.tmp 2>&1
+ diff bh_test.ref postcat.tmp
+ rm -f postcat.tmp
+
depend: $(MAKES)
(sed '1,/^# do not edit/!d' Makefile.in; \
set -e; for i in [a-z][a-z0-9]*.c; do \
@@ -59,6 +99,8 @@ depend: $(MAKES)
# do not edit below this line - it is generated by 'make depend'
postcat.o: ../../include/attr.h
postcat.o: ../../include/iostuff.h
+postcat.o: ../../include/is_header.h
+postcat.o: ../../include/lex_822.h
postcat.o: ../../include/mail_conf.h
postcat.o: ../../include/mail_params.h
postcat.o: ../../include/mail_proto.h
diff --git a/postfix/src/postcat/b_test.ref b/postfix/src/postcat/b_test.ref
new file mode 100644
index 000000000..bacff0c4c
--- /dev/null
+++ b/postfix/src/postcat/b_test.ref
@@ -0,0 +1,2 @@
+
+text
diff --git a/postfix/src/postcat/bh_test.ref b/postfix/src/postcat/bh_test.ref
new file mode 100644
index 000000000..bfe9e2d90
--- /dev/null
+++ b/postfix/src/postcat/bh_test.ref
@@ -0,0 +1,9 @@
+Received: by hades.porcupine.org (Postfix, from userid 1001)
+ id DE040290405; Sun, 21 Jan 2007 13:33:08 -0500 (EST)
+From: me@porcupine.org
+To: you@porcupine.org
+Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org>
+Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT)
+Subject: hey!
+
+text
diff --git a/postfix/src/postcat/default_test.ref b/postfix/src/postcat/default_test.ref
new file mode 100644
index 000000000..eede38b88
--- /dev/null
+++ b/postfix/src/postcat/default_test.ref
@@ -0,0 +1,21 @@
+*** ENVELOPE RECORDS test-queue-file ***
+message_size: 332 182 1 0 332
+message_arrival_time: Sun Jan 21 13:32:59 2007
+create_time: Sun Jan 21 13:33:08 2007
+named_attribute: rewrite_context=local
+sender_fullname: Wietse Venema
+sender: me@porcupine.org
+*** MESSAGE CONTENTS test-queue-file ***
+Received: by hades.porcupine.org (Postfix, from userid 1001)
+ id DE040290405; Sun, 21 Jan 2007 13:33:08 -0500 (EST)
+From: me@porcupine.org
+To: you@porcupine.org
+Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org>
+Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT)
+Subject: hey!
+
+text
+*** HEADER EXTRACTED test-queue-file ***
+original_recipient: you@porcupine.org
+recipient: you@porcupine.org
+*** MESSAGE FILE END test-queue-file ***
diff --git a/postfix/src/postcat/e_test.ref b/postfix/src/postcat/e_test.ref
new file mode 100644
index 000000000..308b0df8e
--- /dev/null
+++ b/postfix/src/postcat/e_test.ref
@@ -0,0 +1,12 @@
+*** ENVELOPE RECORDS test-queue-file ***
+message_size: 332 182 1 0 332
+message_arrival_time: Sun Jan 21 13:32:59 2007
+create_time: Sun Jan 21 13:33:08 2007
+named_attribute: rewrite_context=local
+sender_fullname: Wietse Venema
+sender: me@porcupine.org
+*** MESSAGE CONTENTS test-queue-file ***
+*** HEADER EXTRACTED test-queue-file ***
+original_recipient: you@porcupine.org
+recipient: you@porcupine.org
+*** MESSAGE FILE END test-queue-file ***
diff --git a/postfix/src/postcat/eb_test.ref b/postfix/src/postcat/eb_test.ref
new file mode 100644
index 000000000..b61beb26a
--- /dev/null
+++ b/postfix/src/postcat/eb_test.ref
@@ -0,0 +1,14 @@
+*** ENVELOPE RECORDS test-queue-file ***
+message_size: 332 182 1 0 332
+message_arrival_time: Sun Jan 21 13:32:59 2007
+create_time: Sun Jan 21 13:33:08 2007
+named_attribute: rewrite_context=local
+sender_fullname: Wietse Venema
+sender: me@porcupine.org
+*** MESSAGE CONTENTS test-queue-file ***
+
+text
+*** HEADER EXTRACTED test-queue-file ***
+original_recipient: you@porcupine.org
+recipient: you@porcupine.org
+*** MESSAGE FILE END test-queue-file ***
diff --git a/postfix/src/postcat/eh_test.ref b/postfix/src/postcat/eh_test.ref
new file mode 100644
index 000000000..3142553fe
--- /dev/null
+++ b/postfix/src/postcat/eh_test.ref
@@ -0,0 +1,19 @@
+*** ENVELOPE RECORDS test-queue-file ***
+message_size: 332 182 1 0 332
+message_arrival_time: Sun Jan 21 13:32:59 2007
+create_time: Sun Jan 21 13:33:08 2007
+named_attribute: rewrite_context=local
+sender_fullname: Wietse Venema
+sender: me@porcupine.org
+*** MESSAGE CONTENTS test-queue-file ***
+Received: by hades.porcupine.org (Postfix, from userid 1001)
+ id DE040290405; Sun, 21 Jan 2007 13:33:08 -0500 (EST)
+From: me@porcupine.org
+To: you@porcupine.org
+Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org>
+Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT)
+Subject: hey!
+*** HEADER EXTRACTED test-queue-file ***
+original_recipient: you@porcupine.org
+recipient: you@porcupine.org
+*** MESSAGE FILE END test-queue-file ***
diff --git a/postfix/src/postcat/h_test.ref b/postfix/src/postcat/h_test.ref
new file mode 100644
index 000000000..e1a8025ae
--- /dev/null
+++ b/postfix/src/postcat/h_test.ref
@@ -0,0 +1,7 @@
+Received: by hades.porcupine.org (Postfix, from userid 1001)
+ id DE040290405; Sun, 21 Jan 2007 13:33:08 -0500 (EST)
+From: me@porcupine.org
+To: you@porcupine.org
+Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org>
+Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT)
+Subject: hey!
diff --git a/postfix/src/postcat/postcat.c b/postfix/src/postcat/postcat.c
index 1db3d506b..6838776a0 100644
--- a/postfix/src/postcat/postcat.c
+++ b/postfix/src/postcat/postcat.c
@@ -4,7 +4,7 @@
/* SUMMARY
/* show Postfix queue file contents
/* SYNOPSIS
-/* \fBpostcat\fR [\fB-oqv\fR] [\fB-c \fIconfig_dir\fR] [\fIfiles\fR...]
+/* \fBpostcat\fR [\fB-bhmoqv\fR] [\fB-c \fIconfig_dir\fR] [\fIfiles\fR...]
/* DESCRIPTION
/* The \fBpostcat\fR(1) command prints the contents of the named
/* \fIfiles\fR in human-readable form. The files are expected
@@ -12,10 +12,30 @@
/* \fIfiles\fR are specified on the command line, the program
/* reads from standard input.
/*
+/* By default, \fBpostcat\fR(1) behaves as if all three options
+/* \fB-b\fR, \fB-e\fR, and \fB-h\fR are given. To view message
+/* content only, specify \fB-bh\fR (Postfix 2.7 and later).
+/*
/* Options:
+/* .IP \fB-b\fR
+/* Show body content. The \fB-b\fR option starts producing
+/* output at the first non-header line, and stops when the end
+/* of the message is reached.
+/* .sp
+/* This feature is available in Postfix version 2.7 and later.
/* .IP "\fB-c \fIconfig_dir\fR"
/* The \fBmain.cf\fR configuration file is in the named directory
/* instead of the default configuration directory.
+/* .IP \fB-e\fR
+/* Show message envelope content.
+/* .sp
+/* This feature is available in Postfix version 2.7 and later.
+/* .IP \fB-h\fR
+/* Show message header content. The \fB-h\fR option produces
+/* output from the beginning of the message up to, but not
+/* including, the first non-header line.
+/* .sp
+/* This feature is available in Postfix version 2.7 and later.
/* .IP \fB-o\fR
/* Print the queue file offset of each record.
/* .IP \fB-q\fR
@@ -71,6 +91,7 @@
#include
#include
#include
+#include /* sscanf() */
/* Utility library. */
@@ -90,11 +111,26 @@
#include
#include
#include
+#include
+#include
/* Application-specific. */
-#define PC_FLAG_QUEUE (1<<0) /* search queue */
-#define PC_FLAG_OFFSET (1<<1) /* print record offsets */
+#define PC_FLAG_SEARCH_QUEUE (1<<0) /* search queue */
+#define PC_FLAG_PRINT_OFFSET (1<<1) /* print record offsets */
+#define PC_FLAG_PRINT_ENV (1<<2) /* print envelope records */
+#define PC_FLAG_PRINT_HEADER (1<<3) /* print header records */
+#define PC_FLAG_PRINT_BODY (1<<4) /* print body records */
+
+#define PC_MASK_PRINT_TEXT (PC_FLAG_PRINT_HEADER | PC_FLAG_PRINT_BODY)
+#define PC_MASK_PRINT_ALL (PC_FLAG_PRINT_ENV | PC_MASK_PRINT_TEXT)
+
+ /*
+ * State machine.
+ */
+#define PC_STATE_ENV 0 /* initial or extracted envelope */
+#define PC_STATE_HEADER 1 /* primary header */
+#define PC_STATE_BODY 2 /* other */
#define STR vstring_str
#define LEN VSTRING_LEN
@@ -107,14 +143,16 @@ static void postcat(VSTREAM *fp, VSTRING *buffer, int flags)
int rec_type;
struct timeval tv;
time_t time;
- int first = 1;
int ch;
off_t offset;
- int in_message = 0;
const char *error_text;
char *attr_name;
char *attr_value;
int rec_flags = (msg_verbose ? REC_FLAG_NONE : REC_FLAG_DEFAULT);
+ int state; /* state machine, input type */
+ int do_print; /* state machine, output control */
+ long data_offset; /* state machine, read optimization */
+ long data_size; /* state machine, read optimization */
#define TEXT_RECORD(rec_type) \
(rec_type == REC_TYPE_CONT || rec_type == REC_TYPE_NORM)
@@ -130,24 +168,149 @@ static void postcat(VSTREAM *fp, VSTRING *buffer, int flags)
vstream_ungetc(fp, ch);
}
+ /*
+ * Other preliminaries.
+ */
+ if (flags & PC_FLAG_PRINT_ENV)
+ vstream_printf("*** ENVELOPE RECORDS %s ***\n",
+ VSTREAM_PATH(fp));
+ state = PC_STATE_ENV;
+ do_print = (flags & PC_FLAG_PRINT_ENV);
+ data_offset = data_size = -1;
+
/*
* Now look at the rest.
*/
- do {
- if (flags & PC_FLAG_OFFSET)
+ for (;;) {
+ if (flags & PC_FLAG_PRINT_OFFSET)
offset = vstream_ftell(fp);
rec_type = rec_get_raw(fp, buffer, 0, rec_flags);
if (rec_type == REC_TYPE_ERROR)
msg_fatal("record read error");
if (rec_type == REC_TYPE_EOF)
break;
- if (first == 1) {
- vstream_printf("*** ENVELOPE RECORDS %s ***\n", VSTREAM_PATH(fp));
- first = 0;
+
+ /*
+ * First inspect records that have side effects on the (envelope,
+ * header, body) state machine or on the record reading order.
+ *
+ * XXX Comments marked "Optimization:" identify subtle code that will
+ * likely need to be revised when the queue file organization is
+ * changed.
+ */
+#define PRINT_MARKER(flags, fp, offset, text) do { \
+ if ((flags) & PC_FLAG_PRINT_OFFSET) \
+ vstream_printf("%9lu ", (unsigned long) (offset)); \
+ vstream_printf("*** %s %s ***\n", (text), VSTREAM_PATH(fp)); \
+ vstream_fflush(VSTREAM_OUT); \
+} while (0)
+
+#define PRINT_RECORD(flags, offset, type, value) do { \
+ if ((flags) & PC_FLAG_PRINT_OFFSET) \
+ vstream_printf("%9lu ", (unsigned long) (offset)); \
+ vstream_printf("%s: %s\n", rec_type_name(rec_type), (value)); \
+ vstream_fflush(VSTREAM_OUT); \
+} while (0)
+
+ if (TEXT_RECORD(rec_type)) {
+ /* This is wrong when the message starts with whitespace. */
+ if (state == PC_STATE_HEADER && (flags & (PC_MASK_PRINT_TEXT))
+ && prev_type != REC_TYPE_CONT && TEXT_RECORD(rec_type)
+ && !(is_header(STR(buffer)) || IS_SPACE_TAB(STR(buffer)[0]))) {
+ /* Update the state machine. */
+ state = PC_STATE_BODY;
+ do_print = (flags & PC_FLAG_PRINT_BODY);
+ /* Optimization: terminate if nothing left to print. */
+ if (do_print == 0 && (flags & PC_FLAG_PRINT_ENV) == 0)
+ break;
+ /* Optimization: skip to extracted segment marker. */
+ if (do_print == 0 && (flags & PC_FLAG_PRINT_ENV)
+ && data_offset >= 0 && data_size >= 0
+ && vstream_fseek(fp, data_offset + data_size, SEEK_SET) < 0)
+ msg_fatal("seek error: %m");
+ }
+ /* Optional output happens further down below. */
+ } else if (rec_type == REC_TYPE_MESG) {
+ /* Sanity check. */
+ if (state != PC_STATE_ENV)
+ msg_warn("%s: out-of-order message content marker",
+ VSTREAM_PATH(fp));
+ /* Optional output. */
+ if (flags & PC_FLAG_PRINT_ENV)
+ PRINT_MARKER(flags, fp, offset, "MESSAGE CONTENTS");
+ /* Optimization: skip to extracted segment marker. */
+ if ((flags & PC_MASK_PRINT_TEXT) == 0
+ && data_offset >= 0 && data_size >= 0
+ && vstream_fseek(fp, data_offset + data_size, SEEK_SET) < 0)
+ msg_fatal("seek error: %m");
+ /* Update the state machine, even when skipping. */
+ state = PC_STATE_HEADER;
+ do_print = (flags & PC_FLAG_PRINT_HEADER);
+ continue;
+ } else if (rec_type == REC_TYPE_XTRA) {
+ /* Sanity check. */
+ if (state != PC_STATE_HEADER && state != PC_STATE_BODY)
+ msg_warn("%s: out-of-order extracted segment marker",
+ VSTREAM_PATH(fp));
+ /* Optional output (terminate preceding header/body line). */
+ if (do_print && prev_type == REC_TYPE_CONT)
+ VSTREAM_PUTCHAR('\n');
+ if (flags & PC_FLAG_PRINT_ENV)
+ PRINT_MARKER(flags, fp, offset, "HEADER EXTRACTED");
+ /* Update the state machine. */
+ state = PC_STATE_ENV;
+ do_print = (flags & PC_FLAG_PRINT_ENV);
+ /* Optimization: terminate if nothing left to print. */
+ if (do_print == 0)
+ break;
+ continue;
+ } else if (rec_type == REC_TYPE_END) {
+ /* Sanity check. */
+ if (state != PC_STATE_ENV)
+ msg_warn("%s: out-of-order message end marker",
+ VSTREAM_PATH(fp));
+ /* Optional output. */
+ if (flags & PC_FLAG_PRINT_ENV)
+ PRINT_MARKER(flags, fp, offset, "MESSAGE FILE END");
+ /* Terminate the state machine. */
+ break;
+ } else if (rec_type == REC_TYPE_PTR) {
+ /* Optional output. */
+ /* This record type is exposed only with '-v'. */
+ if (do_print)
+ PRINT_RECORD(flags, offset, rec_type, STR(buffer));
+ /* Skip to the pointer's target record. */
+ if (rec_goto(fp, STR(buffer)) == REC_TYPE_ERROR)
+ msg_fatal("bad pointer record, or input is not seekable");
+ continue;
+ } else if (rec_type == REC_TYPE_SIZE) {
+ if (data_size >= 0 || data_offset >= 0) {
+ msg_warn("file contains multiple size records");
+ } else {
+ if (sscanf(STR(buffer), "%ld %ld", &data_size, &data_offset) != 2
+ || data_offset <= 0 || data_size <= 0)
+ msg_fatal("invalid size record: %.100s", STR(buffer));
+ /* Optional output (here since we update the state machine). */
+ if (do_print)
+ PRINT_RECORD(flags, offset, rec_type, STR(buffer));
+ /* Optimization: skip to the message header. */
+ if ((flags & PC_FLAG_PRINT_ENV) == 0) {
+ if (vstream_fseek(fp, data_offset, SEEK_SET) < 0)
+ msg_fatal("seek error: %m");
+ /* Update the state machine. */
+ state = PC_STATE_HEADER;
+ do_print = (flags & PC_FLAG_PRINT_HEADER);
+ }
+ }
+ continue;
}
- if (prev_type == REC_TYPE_CONT && !TEXT_RECORD(rec_type))
- VSTREAM_PUTCHAR('\n');
- if (flags & PC_FLAG_OFFSET)
+
+ /*
+ * Don't inspect side-effect-free records that aren't printed.
+ */
+ if (do_print == 0)
+ continue;
+ if (flags & PC_FLAG_PRINT_OFFSET)
vstream_printf("%9lu ", (unsigned long) offset);
switch (rec_type) {
case REC_TYPE_TIME:
@@ -161,20 +324,14 @@ static void postcat(VSTREAM *fp, VSTRING *buffer, int flags)
vstream_printf("%s: %s", rec_type_name(rec_type),
asctime(localtime(&time)));
break;
- case REC_TYPE_PTR: /* pointer */
- vstream_printf("%s: ", rec_type_name(rec_type));
- vstream_fwrite(VSTREAM_OUT, STR(buffer), LEN(buffer));
- VSTREAM_PUTCHAR('\n');
- if (rec_goto(fp, STR(buffer)) == REC_TYPE_ERROR)
- msg_fatal("bad pointer record, or input is not seekable");
- break;
case REC_TYPE_CONT: /* REC_TYPE_FILT collision */
- if (!in_message)
+ if (state == PC_STATE_ENV)
vstream_printf("%s: ", rec_type_name(rec_type));
else if (msg_verbose)
vstream_printf("unterminated_text: ");
vstream_fwrite(VSTREAM_OUT, STR(buffer), LEN(buffer));
- if (!in_message || msg_verbose || (flags & PC_FLAG_OFFSET) != 0) {
+ if (state == PC_STATE_ENV || msg_verbose
+ || (flags & PC_FLAG_PRINT_OFFSET) != 0) {
rec_type = 0;
VSTREAM_PUTCHAR('\n');
}
@@ -186,22 +343,10 @@ static void postcat(VSTREAM *fp, VSTRING *buffer, int flags)
VSTREAM_PUTCHAR('\n');
break;
case REC_TYPE_DTXT:
- if (msg_verbose) {
- vstream_printf("%s: ", rec_type_name(rec_type));
- vstream_fwrite(VSTREAM_OUT, STR(buffer), LEN(buffer));
- VSTREAM_PUTCHAR('\n');
- }
- break;
- case REC_TYPE_MESG:
- vstream_printf("*** MESSAGE CONTENTS %s ***\n", VSTREAM_PATH(fp));
- in_message = 1;
- break;
- case REC_TYPE_XTRA:
- vstream_printf("*** HEADER EXTRACTED %s ***\n", VSTREAM_PATH(fp));
- in_message = 0;
- break;
- case REC_TYPE_END:
- vstream_printf("*** MESSAGE FILE END %s ***\n", VSTREAM_PATH(fp));
+ /* This record type is exposed only with '-v'. */
+ vstream_printf("%s: ", rec_type_name(rec_type));
+ vstream_fwrite(VSTREAM_OUT, STR(buffer), LEN(buffer));
+ VSTREAM_PUTCHAR('\n');
break;
case REC_TYPE_ATTR:
error_text = split_nameval(STR(buffer), &attr_name, &attr_value);
@@ -214,10 +359,10 @@ static void postcat(VSTREAM *fp, VSTRING *buffer, int flags)
time = atol(attr_value);
vstream_printf("%s: %s", MAIL_ATTR_CREATE_TIME,
asctime(localtime(&time)));
- break;
+ } else {
+ vstream_printf("%s: %s=%s\n", rec_type_name(rec_type),
+ attr_name, attr_value);
}
- vstream_printf("%s: %s=%s\n", rec_type_name(rec_type),
- attr_name, attr_value);
break;
default:
vstream_printf("%s: %s\n", rec_type_name(rec_type), STR(buffer));
@@ -229,7 +374,7 @@ static void postcat(VSTREAM *fp, VSTRING *buffer, int flags)
* In case the next record is broken.
*/
vstream_fflush(VSTREAM_OUT);
- } while (rec_type != REC_TYPE_END);
+ }
}
/* usage - explain and terminate */
@@ -284,17 +429,26 @@ int main(int argc, char **argv)
/*
* Parse JCL.
*/
- while ((ch = GETOPT(argc, argv, "c:oqv")) > 0) {
+ while ((ch = GETOPT(argc, argv, "bc:ehoqv")) > 0) {
switch (ch) {
+ case 'b':
+ flags |= PC_FLAG_PRINT_BODY;
+ break;
case 'c':
if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
msg_fatal("out of memory");
break;
+ case 'e':
+ flags |= PC_FLAG_PRINT_ENV;
+ break;
+ case 'h':
+ flags |= PC_FLAG_PRINT_HEADER;
+ break;
case 'o':
- flags |= PC_FLAG_OFFSET;
+ flags |= PC_FLAG_PRINT_OFFSET;
break;
case 'q':
- flags |= PC_FLAG_QUEUE;
+ flags |= PC_FLAG_SEARCH_QUEUE;
break;
case 'v':
msg_verbose++;
@@ -303,6 +457,8 @@ int main(int argc, char **argv)
usage(argv[0]);
}
}
+ if ((flags & PC_MASK_PRINT_ALL) == 0)
+ flags |= PC_MASK_PRINT_ALL;
/*
* Further initialization...
@@ -327,7 +483,7 @@ int main(int argc, char **argv)
/*
* Copy the named queue files in the specified order.
*/
- else if (flags & PC_FLAG_QUEUE) {
+ else if (flags & PC_FLAG_SEARCH_QUEUE) {
if (chdir(var_queue_dir))
msg_fatal("chdir %s: %m", var_queue_dir);
while (optind < argc) {
diff --git a/postfix/src/postcat/test-queue-file b/postfix/src/postcat/test-queue-file
new file mode 100644
index 000000000..4979c1df5
Binary files /dev/null and b/postfix/src/postcat/test-queue-file differ