diff options
Diffstat (limited to '')
l--------- | src/postcat/.indent.pro | 1 | ||||
-rw-r--r-- | src/postcat/.printfck | 25 | ||||
-rw-r--r-- | src/postcat/Makefile.in | 128 | ||||
-rw-r--r-- | src/postcat/b_test.ref | 2 | ||||
-rw-r--r-- | src/postcat/bh_test.ref | 9 | ||||
-rw-r--r-- | src/postcat/default_test.ref | 21 | ||||
-rw-r--r-- | src/postcat/e_test.ref | 12 | ||||
-rw-r--r-- | src/postcat/eb_test.ref | 14 | ||||
-rw-r--r-- | src/postcat/eh_test.ref | 19 | ||||
-rw-r--r-- | src/postcat/h_test.ref | 7 | ||||
-rw-r--r-- | src/postcat/postcat.c | 598 | ||||
-rw-r--r-- | src/postcat/test-queue-file | bin | 0 -> 573 bytes |
12 files changed, 836 insertions, 0 deletions
diff --git a/src/postcat/.indent.pro b/src/postcat/.indent.pro new file mode 120000 index 0000000..5c837ec --- /dev/null +++ b/src/postcat/.indent.pro @@ -0,0 +1 @@ +../../.indent.pro
\ No newline at end of file diff --git a/src/postcat/.printfck b/src/postcat/.printfck new file mode 100644 index 0000000..66016ed --- /dev/null +++ b/src/postcat/.printfck @@ -0,0 +1,25 @@ +been_here_xt 2 0 +bounce_append 5 0 +cleanup_out_format 1 0 +defer_append 5 0 +mail_command 1 0 +mail_print 1 0 +msg_error 0 0 +msg_fatal 0 0 +msg_info 0 0 +msg_panic 0 0 +msg_warn 0 0 +opened 4 0 +post_mail_fprintf 1 0 +qmgr_message_bounce 2 0 +rec_fprintf 2 0 +sent 4 0 +smtp_cmd 1 0 +smtp_mesg_fail 2 0 +smtp_printf 1 0 +smtp_rcpt_fail 3 0 +smtp_site_fail 2 0 +udp_syslog 1 0 +vstream_fprintf 1 0 +vstream_printf 0 0 +vstring_sprintf 1 0 diff --git a/src/postcat/Makefile.in b/src/postcat/Makefile.in new file mode 100644 index 0000000..06c5eb8 --- /dev/null +++ b/src/postcat/Makefile.in @@ -0,0 +1,128 @@ +SHELL = /bin/sh +SRCS = postcat.c +OBJS = postcat.o +HDRS = +TESTSRC = +DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) +CFLAGS = $(DEBUG) $(OPT) $(DEFS) +TESTPROG= +PROG = postcat +INC_DIR = ../../include +LIBS = ../../lib/lib$(LIB_PREFIX)global$(LIB_SUFFIX) \ + ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX) + +.c.o:; $(CC) $(CFLAGS) -c $*.c + +$(PROG): $(OBJS) $(LIBS) + $(CC) $(CFLAGS) $(SHLIB_RPATH) -o $@ $(OBJS) $(LIBS) $(SYSLIBS) + +$(OBJS): ../../conf/makedefs.out + +Makefile: Makefile.in + cat ../../conf/makedefs.out $? >$@ + +test: $(TESTPROG) + +tests: default_test ebh_test e_test b_test h_test eb_test eh_test bh_test + +root_tests: + +update: ../../bin/$(PROG) + +../../bin/$(PROG): $(PROG) + cp $(PROG) ../../bin + +printfck: $(OBJS) $(PROG) + rm -rf printfck + mkdir printfck + sed '1,/^# do not edit/!d' Makefile >printfck/Makefile + set -e; for i in *.c; do printfck -f .printfck $$i >printfck/$$i; done + cd printfck; make "INC_DIR=../../../include" `cd ..; ls *.o` + +lint: + lint $(DEFS) $(SRCS) $(LINTFIX) + +clean: + rm -f *.o *core $(PROG) $(TESTPROG) junk + rm -rf printfck + +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 \ + $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \ + -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' \ + -e 's/o: \.\//o: /' -e p -e '}' ; \ + done | LANG=C sort -u) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in + @$(EXPORT) make -f Makefile.in Makefile 1>&2 + +# do not edit below this line - it is generated by 'make depend' +postcat.o: ../../include/argv.h +postcat.o: ../../include/attr.h +postcat.o: ../../include/check_arg.h +postcat.o: ../../include/clean_env.h +postcat.o: ../../include/htable.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_parm_split.h +postcat.o: ../../include/mail_proto.h +postcat.o: ../../include/mail_queue.h +postcat.o: ../../include/mail_version.h +postcat.o: ../../include/msg.h +postcat.o: ../../include/msg_vstream.h +postcat.o: ../../include/mymalloc.h +postcat.o: ../../include/nvtable.h +postcat.o: ../../include/rec_type.h +postcat.o: ../../include/record.h +postcat.o: ../../include/stringops.h +postcat.o: ../../include/sys_defs.h +postcat.o: ../../include/vbuf.h +postcat.o: ../../include/vstream.h +postcat.o: ../../include/vstring.h +postcat.o: ../../include/vstring_vstream.h +postcat.o: ../../include/warn_stat.h +postcat.o: postcat.c diff --git a/src/postcat/b_test.ref b/src/postcat/b_test.ref new file mode 100644 index 0000000..bacff0c --- /dev/null +++ b/src/postcat/b_test.ref @@ -0,0 +1,2 @@ + +text diff --git a/src/postcat/bh_test.ref b/src/postcat/bh_test.ref new file mode 100644 index 0000000..bfe9e2d --- /dev/null +++ b/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/src/postcat/default_test.ref b/src/postcat/default_test.ref new file mode 100644 index 0000000..eede38b --- /dev/null +++ b/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/src/postcat/e_test.ref b/src/postcat/e_test.ref new file mode 100644 index 0000000..308b0df --- /dev/null +++ b/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/src/postcat/eb_test.ref b/src/postcat/eb_test.ref new file mode 100644 index 0000000..b61beb2 --- /dev/null +++ b/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/src/postcat/eh_test.ref b/src/postcat/eh_test.ref new file mode 100644 index 0000000..3142553 --- /dev/null +++ b/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/src/postcat/h_test.ref b/src/postcat/h_test.ref new file mode 100644 index 0000000..e1a8025 --- /dev/null +++ b/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/src/postcat/postcat.c b/src/postcat/postcat.c new file mode 100644 index 0000000..36f2740 --- /dev/null +++ b/src/postcat/postcat.c @@ -0,0 +1,598 @@ +/*++ +/* NAME +/* postcat 1 +/* SUMMARY +/* show Postfix queue file contents +/* SYNOPSIS +/* \fBpostcat\fR [\fB-bdehnoqv\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 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) shows the envelope and message +/* content, as if the options \fB-beh\fR were specified. 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 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-d\fR +/* Print the decimal type of each record. +/* .IP \fB-e\fR +/* Show message envelope content. +/* .sp +/* This feature is available in Postfix 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 2.7 and later. +/* .IP \fB-o\fR +/* Print the queue file offset of each record. +/* .IP \fB-q\fR +/* Search the Postfix queue for the named \fIfiles\fR instead +/* of taking the names literally. +/* +/* This feature is available in Postfix 2.0 and later. +/* .IP \fB-r\fR +/* Print records in file order, don't follow pointer records. +/* +/* This feature is available in Postfix 3.7 and later. +/* .IP "\fB-s \fIoffset\fR" +/* Skip to the specified queue file offset. +/* +/* This feature is available in Postfix 3.7 and later. +/* .IP \fB-v\fR +/* Enable verbose logging for debugging purposes. Multiple \fB-v\fR +/* options make the software increasingly verbose. +/* DIAGNOSTICS +/* Problems are reported to the standard error stream. +/* ENVIRONMENT +/* .ad +/* .fi +/* .IP \fBMAIL_CONFIG\fR +/* Directory with Postfix configuration files. +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* The following \fBmain.cf\fR parameters are especially relevant to +/* this program. +/* +/* The text below provides only a parameter summary. See +/* \fBpostconf\fR(5) for more details including examples. +/* .IP "\fBconfig_directory (see 'postconf -d' output)\fR" +/* The default location of the Postfix main.cf and master.cf +/* configuration files. +/* .IP "\fBimport_environment (see 'postconf -d' output)\fR" +/* The list of environment parameters that a privileged Postfix +/* process will import from a non-Postfix parent process, or name=value +/* environment overrides. +/* .IP "\fBqueue_directory (see 'postconf -d' output)\fR" +/* The location of the Postfix top-level queue directory. +/* FILES +/* /var/spool/postfix, Postfix queue directory +/* SEE ALSO +/* postconf(5), Postfix configuration +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <fcntl.h> +#include <string.h> +#include <stdio.h> /* sscanf() */ + +/* Utility library. */ + +#include <msg.h> +#include <vstream.h> +#include <vstring.h> +#include <msg_vstream.h> +#include <vstring_vstream.h> +#include <stringops.h> +#include <warn_stat.h> +#include <clean_env.h> + +/* Global library. */ + +#include <record.h> +#include <rec_type.h> +#include <mail_queue.h> +#include <mail_conf.h> +#include <mail_params.h> +#include <mail_version.h> +#include <mail_proto.h> +#include <is_header.h> +#include <lex_822.h> +#include <mail_parm_split.h> + +/* Application-specific. */ + +#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_FLAG_PRINT_RTYPE_DEC (1<<5) /* print decimal record type */ +#define PC_FLAG_PRINT_RTYPE_SYM (1<<6) /* print symbolic record type */ +#define PC_FLAG_RAW (1<<7) /* don't follow pointers */ + +#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 */ + +off_t start_offset = 0; + +#define STR vstring_str +#define LEN VSTRING_LEN + +/* postcat - visualize Postfix queue file contents */ + +static void postcat(VSTREAM *fp, VSTRING *buffer, int flags) +{ + int prev_type = 0; + int rec_type; + struct timeval tv; + time_t time; + int ch; + off_t offset; + 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) + + /* + * Skip over or absorb some bytes. + */ + if (start_offset > 0) { + if (fp == VSTREAM_IN) { + for (offset = 0; offset < start_offset; offset++) + if (VSTREAM_GETC(fp) == VSTREAM_EOF) + msg_fatal("%s: skip %ld bytes failed after %ld", + VSTREAM_PATH(fp), (long) start_offset, + (long) offset); + } else { + if (vstream_fseek(fp, start_offset, SEEK_SET) < 0) + msg_fatal("%s: seek to %ld: %m", + VSTREAM_PATH(fp), (long) start_offset); + } + } + + /* + * See if this is a plausible file. + */ + if (start_offset == 0 && (ch = VSTREAM_GETC(fp)) != VSTREAM_EOF) { + if (!strchr(REC_TYPE_ENVELOPE, ch)) { + msg_warn("%s: input is not a valid queue file", VSTREAM_PATH(fp)); + return; + } + vstream_ungetc(fp, ch); + } + + /* + * Other preliminaries. + */ + if (start_offset == 0 && (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. + */ + 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; + + /* + * 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, type, text) do { \ + if ((flags) & PC_FLAG_PRINT_OFFSET) \ + vstream_printf("%9lu ", (unsigned long) (offset)); \ + if (flags & PC_FLAG_PRINT_RTYPE_DEC) \ + vstream_printf("%3d ", (type)); \ + 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)); \ + if (flags & PC_FLAG_PRINT_RTYPE_DEC) \ + vstream_printf("%3d ", (type)); \ + 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, rec_type, "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, rec_type, "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, rec_type, "MESSAGE FILE END"); + if (flags & PC_FLAG_RAW) + continue; + /* 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 ((flags & PC_FLAG_RAW) == 0 + && 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) { + /* Optional output (here before we update the state machine). */ + if (do_print) + PRINT_RECORD(flags, offset, rec_type, STR(buffer)); + /* Read the message size/offset for the state machine optimizer. */ + 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_warn("invalid size record: %.100s", 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; + } + + /* + * 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); + if (flags & PC_FLAG_PRINT_RTYPE_DEC) + vstream_printf("%3d ", rec_type); + switch (rec_type) { + case REC_TYPE_TIME: + REC_TYPE_TIME_SCAN(STR(buffer), tv); + time = tv.tv_sec; + vstream_printf("%s: %s", rec_type_name(rec_type), + asctime(localtime(&time))); + break; + case REC_TYPE_WARN: + REC_TYPE_WARN_SCAN(STR(buffer), time); + vstream_printf("%s: %s", rec_type_name(rec_type), + asctime(localtime(&time))); + break; + case REC_TYPE_CONT: /* REC_TYPE_FILT collision */ + 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 (state == PC_STATE_ENV || msg_verbose + || (flags & PC_FLAG_PRINT_OFFSET) != 0) { + rec_type = 0; + VSTREAM_PUTCHAR('\n'); + } + break; + case REC_TYPE_NORM: + 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_DTXT: + /* 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); + if (error_text != 0) { + msg_warn("%s: malformed attribute: %s: %.100s", + VSTREAM_PATH(fp), error_text, STR(buffer)); + break; + } + if (strcmp(attr_name, MAIL_ATTR_CREATE_TIME) == 0) { + time = atol(attr_value); + vstream_printf("%s: %s", MAIL_ATTR_CREATE_TIME, + asctime(localtime(&time))); + } else { + 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)); + break; + } + prev_type = rec_type; + + /* + * In case the next record is broken. + */ + vstream_fflush(VSTREAM_OUT); + } +} + +/* usage - explain and terminate */ + +static NORETURN usage(char *myname) +{ + msg_fatal("usage: %s [-b (body text)] [-c config_dir] [-d (decimal record type)] [-e (envelope records)] [-h (header text)] [-q (access queue)] [-v] [file(s)...]", + myname); +} + +MAIL_VERSION_STAMP_DECLARE; + +int main(int argc, char **argv) +{ + VSTRING *buffer; + VSTREAM *fp; + int ch; + int fd; + struct stat st; + int flags = 0; + static char *queue_names[] = { + MAIL_QUEUE_MAILDROP, + MAIL_QUEUE_INCOMING, + MAIL_QUEUE_ACTIVE, + MAIL_QUEUE_DEFERRED, + MAIL_QUEUE_HOLD, + MAIL_QUEUE_SAVED, + 0, + }; + char **cpp; + int tries; + ARGV *import_env; + + /* + * Fingerprint executables and core dumps. + */ + MAIL_VERSION_STAMP_ALLOCATE; + + /* + * To minimize confusion, make sure that the standard file descriptors + * are open before opening anything else. XXX Work around for 44BSD where + * fstat can return EBADF on an open file descriptor. + */ + for (fd = 0; fd < 3; fd++) + if (fstat(fd, &st) == -1 + && (close(fd), open("/dev/null", O_RDWR, 0)) != fd) + msg_fatal("open /dev/null: %m"); + + /* + * Set up logging. + */ + msg_vstream_init(argv[0], VSTREAM_ERR); + + /* + * Check the Postfix library version as soon as we enable logging. + */ + MAIL_VERSION_CHECK; + + /* + * Parse JCL. + */ + while ((ch = GETOPT(argc, argv, "bc:dehoqrs:v")) > 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 'd': + flags |= PC_FLAG_PRINT_RTYPE_DEC; + break; + case 'e': + flags |= PC_FLAG_PRINT_ENV; + break; + case 'h': + flags |= PC_FLAG_PRINT_HEADER; + break; + case 'o': + flags |= PC_FLAG_PRINT_OFFSET; + break; + case 'q': + flags |= PC_FLAG_SEARCH_QUEUE; + break; + case 'r': + flags |= PC_FLAG_RAW; + break; + case 's': + if (!alldig(optarg) || (start_offset = atol(optarg)) < 0) + msg_fatal("bad offset: %s", optarg); + break; + case 'v': + msg_verbose++; + break; + default: + usage(argv[0]); + } + } + if ((flags & PC_MASK_PRINT_ALL) == 0) + flags |= PC_MASK_PRINT_ALL; + + /* + * Further initialization... + */ + mail_conf_read(); + import_env = mail_parm_split(VAR_IMPORT_ENVIRON, var_import_environ); + update_env(import_env->argv); + argv_free(import_env); + + /* + * Initialize. + */ + buffer = vstring_alloc(10); + + /* + * If no file names are given, copy stdin. + */ + if (argc == optind) { + vstream_control(VSTREAM_IN, + CA_VSTREAM_CTL_PATH("stdin"), + CA_VSTREAM_CTL_END); + postcat(VSTREAM_IN, buffer, flags); + } + + /* + * Copy the named queue files in the specified order. + */ + else if (flags & PC_FLAG_SEARCH_QUEUE) { + if (chdir(var_queue_dir)) + msg_fatal("chdir %s: %m", var_queue_dir); + while (optind < argc) { + if (!mail_queue_id_ok(argv[optind])) + msg_fatal("bad mail queue ID: %s", argv[optind]); + for (fp = 0, tries = 0; fp == 0 && tries < 2; tries++) + for (cpp = queue_names; fp == 0 && *cpp != 0; cpp++) + fp = mail_queue_open(*cpp, argv[optind], O_RDONLY, 0); + if (fp == 0) + msg_fatal("open queue file %s: %m", argv[optind]); + postcat(fp, buffer, flags); + if (vstream_fclose(fp)) + msg_warn("close %s: %m", argv[optind]); + optind++; + } + } + + /* + * Copy the named files in the specified order. + */ + else { + while (optind < argc) { + if ((fp = vstream_fopen(argv[optind], O_RDONLY, 0)) == 0) + msg_fatal("open %s: %m", argv[optind]); + postcat(fp, buffer, flags); + if (vstream_fclose(fp)) + msg_warn("close %s: %m", argv[optind]); + optind++; + } + } + + /* + * Clean up. + */ + vstring_free(buffer); + exit(0); +} diff --git a/src/postcat/test-queue-file b/src/postcat/test-queue-file Binary files differnew file mode 100644 index 0000000..4979c1d --- /dev/null +++ b/src/postcat/test-queue-file |