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