7 /* show Postfix queue file contents
9 /* \fBpostcat\fR [\fB-oqv\fR] [\fB-c \fIconfig_dir\fR] [\fIfiles\fR...]
11 /* The \fBpostcat\fR(1) command prints the contents of the named
12 /* \fIfiles\fR in human-readable form. The files are expected
13 /* to be in Postfix queue file format. If no
14 /* \fIfiles\fR are specified on the command line, the program
15 /* reads from standard input.
18 /* .IP "\fB-c \fIconfig_dir\fR"
19 /* The \fBmain.cf\fR configuration file is in the named directory
20 /* instead of the default configuration directory.
22 /* Print the queue file offset of each record.
24 /* Search the Postfix queue for the named \fIfiles\fR instead
25 /* of taking the names literally.
27 /* Available in Postfix version 2.0 and later.
29 /* Enable verbose logging for debugging purposes. Multiple \fB-v\fR
30 /* options make the software increasingly verbose.
32 /* Problems are reported to the standard error stream.
36 /* .IP \fBMAIL_CONFIG\fR
37 /* Directory with Postfix configuration files.
38 /* CONFIGURATION PARAMETERS
41 /* The following \fBmain.cf\fR parameters are especially relevant to
44 /* The text below provides only a parameter summary. See
45 /* \fBpostconf\fR(5) for more details including examples.
46 /* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
47 /* The default location of the Postfix main.cf and master.cf
48 /* configuration files.
49 /* .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
50 /* The location of the Postfix top-level queue directory.
52 /* /var/spool/postfix, Postfix queue directory
54 /* postconf(5), Postfix configuration
58 /* The Secure Mailer license must be distributed with this software.
61 /* IBM T.J. Watson Research
63 /* Yorktown Heights, NY 10598, USA
77 /* Utility library. */
82 #include <msg_vstream.h>
83 #include <vstring_vstream.h>
84 #include <stringops.h>
90 #include <mail_queue.h>
91 #include <mail_conf.h>
92 #include <mail_params.h>
93 #include <mail_version.h>
94 #include <mail_proto.h>
96 /* Application-specific. */
98 #define PC_FLAG_QUEUE (1<<0) /* search queue */
99 #define PC_FLAG_OFFSET (1<<1) /* print record offsets */
101 #define STR vstring_str
102 #define LEN VSTRING_LEN
104 /* postcat - visualize Postfix queue file contents */
106 static void postcat(VSTREAM
*fp
, VSTRING
*buffer
, int flags
)
116 const char *error_text
;
119 int rec_flags
= (msg_verbose
? REC_FLAG_NONE
: REC_FLAG_DEFAULT
);
121 #define TEXT_RECORD(rec_type) \
122 (rec_type == REC_TYPE_CONT || rec_type == REC_TYPE_NORM)
125 * See if this is a plausible file.
127 if ((ch
= VSTREAM_GETC(fp
)) != VSTREAM_EOF
) {
128 if (!strchr(REC_TYPE_ENVELOPE
, ch
)) {
129 msg_warn("%s: input is not a valid queue file", VSTREAM_PATH(fp
));
132 vstream_ungetc(fp
, ch
);
136 * Now look at the rest.
139 if (flags
& PC_FLAG_OFFSET
)
140 offset
= vstream_ftell(fp
);
141 rec_type
= rec_get_raw(fp
, buffer
, 0, rec_flags
);
142 if (rec_type
== REC_TYPE_ERROR
)
143 msg_fatal("record read error");
144 if (rec_type
== REC_TYPE_EOF
)
147 vstream_printf("*** ENVELOPE RECORDS %s ***\n", VSTREAM_PATH(fp
));
150 if (prev_type
== REC_TYPE_CONT
&& !TEXT_RECORD(rec_type
))
151 VSTREAM_PUTCHAR('\n');
152 if (flags
& PC_FLAG_OFFSET
)
153 vstream_printf("%9lu ", (unsigned long) offset
);
156 REC_TYPE_TIME_SCAN(STR(buffer
), tv
);
158 vstream_printf("%s: %s", rec_type_name(rec_type
),
159 asctime(localtime(&time
)));
162 REC_TYPE_WARN_SCAN(STR(buffer
), time
);
163 vstream_printf("%s: %s", rec_type_name(rec_type
),
164 asctime(localtime(&time
)));
166 case REC_TYPE_PTR
: /* pointer */
167 vstream_printf("%s: ", rec_type_name(rec_type
));
168 vstream_fwrite(VSTREAM_OUT
, STR(buffer
), LEN(buffer
));
169 VSTREAM_PUTCHAR('\n');
170 if (rec_goto(fp
, STR(buffer
)) == REC_TYPE_ERROR
)
171 msg_fatal("bad pointer record, or input is not seekable");
173 case REC_TYPE_CONT
: /* REC_TYPE_FILT collision */
175 vstream_printf("%s: ", rec_type_name(rec_type
));
176 else if (msg_verbose
)
177 vstream_printf("unterminated_text: ");
178 vstream_fwrite(VSTREAM_OUT
, STR(buffer
), LEN(buffer
));
179 if (!in_message
|| msg_verbose
|| (flags
& PC_FLAG_OFFSET
) != 0) {
181 VSTREAM_PUTCHAR('\n');
186 vstream_printf("%s: ", rec_type_name(rec_type
));
187 vstream_fwrite(VSTREAM_OUT
, STR(buffer
), LEN(buffer
));
188 VSTREAM_PUTCHAR('\n');
192 vstream_printf("%s: ", rec_type_name(rec_type
));
193 vstream_fwrite(VSTREAM_OUT
, STR(buffer
), LEN(buffer
));
194 VSTREAM_PUTCHAR('\n');
198 vstream_printf("*** MESSAGE CONTENTS %s ***\n", VSTREAM_PATH(fp
));
202 vstream_printf("*** HEADER EXTRACTED %s ***\n", VSTREAM_PATH(fp
));
206 vstream_printf("*** MESSAGE FILE END %s ***\n", VSTREAM_PATH(fp
));
209 error_text
= split_nameval(STR(buffer
), &attr_name
, &attr_value
);
210 if (error_text
!= 0) {
211 msg_warn("%s: malformed attribute: %s: %.100s",
212 VSTREAM_PATH(fp
), error_text
, STR(buffer
));
215 if (strcmp(attr_name
, MAIL_ATTR_CREATE_TIME
) == 0) {
216 time
= atol(attr_value
);
217 vstream_printf("%s: %s", MAIL_ATTR_CREATE_TIME
,
218 asctime(localtime(&time
)));
221 vstream_printf("%s: %s=%s\n", rec_type_name(rec_type
),
222 attr_name
, attr_value
);
225 vstream_printf("%s: %s\n", rec_type_name(rec_type
), STR(buffer
));
228 prev_type
= rec_type
;
231 * In case the next record is broken.
233 vstream_fflush(VSTREAM_OUT
);
234 } while (rec_type
!= REC_TYPE_END
);
237 /* usage - explain and terminate */
239 static NORETURN
usage(char *myname
)
241 msg_fatal("usage: %s [-c config_dir] [-q (access queue)] [-v] [file(s)...]",
245 MAIL_VERSION_STAMP_DECLARE
;
247 int main(int argc
, char **argv
)
255 static char *queue_names
[] = {
267 * Fingerprint executables and core dumps.
269 MAIL_VERSION_STAMP_ALLOCATE
;
272 * To minimize confusion, make sure that the standard file descriptors
273 * are open before opening anything else. XXX Work around for 44BSD where
274 * fstat can return EBADF on an open file descriptor.
276 for (fd
= 0; fd
< 3; fd
++)
277 if (fstat(fd
, &st
) == -1
278 && (close(fd
), open("/dev/null", O_RDWR
, 0)) != fd
)
279 msg_fatal("open /dev/null: %m");
284 msg_vstream_init(argv
[0], VSTREAM_ERR
);
289 while ((ch
= GETOPT(argc
, argv
, "c:oqv")) > 0) {
292 if (setenv(CONF_ENV_PATH
, optarg
, 1) < 0)
293 msg_fatal("out of memory");
296 flags
|= PC_FLAG_OFFSET
;
299 flags
|= PC_FLAG_QUEUE
;
310 * Further initialization...
317 buffer
= vstring_alloc(10);
320 * If no file names are given, copy stdin.
322 if (argc
== optind
) {
323 vstream_control(VSTREAM_IN
,
324 VSTREAM_CTL_PATH
, "stdin",
326 postcat(VSTREAM_IN
, buffer
, flags
);
330 * Copy the named queue files in the specified order.
332 else if (flags
& PC_FLAG_QUEUE
) {
333 if (chdir(var_queue_dir
))
334 msg_fatal("chdir %s: %m", var_queue_dir
);
335 while (optind
< argc
) {
336 if (!mail_queue_id_ok(argv
[optind
]))
337 msg_fatal("bad mail queue ID: %s", argv
[optind
]);
338 for (fp
= 0, tries
= 0; fp
== 0 && tries
< 2; tries
++)
339 for (cpp
= queue_names
; fp
== 0 && *cpp
!= 0; cpp
++)
340 fp
= mail_queue_open(*cpp
, argv
[optind
], O_RDONLY
, 0);
342 msg_fatal("open queue file %s: %m", argv
[optind
]);
343 postcat(fp
, buffer
, flags
);
344 if (vstream_fclose(fp
))
345 msg_warn("close %s: %m", argv
[optind
]);
351 * Copy the named files in the specified order.
354 while (optind
< argc
) {
355 if ((fp
= vstream_fopen(argv
[optind
], O_RDONLY
, 0)) == 0)
356 msg_fatal("open %s: %m", argv
[optind
]);
357 postcat(fp
, buffer
, flags
);
358 if (vstream_fclose(fp
))
359 msg_warn("close %s: %m", argv
[optind
]);
367 vstring_free(buffer
);