Expand PMF_FN_* macros.
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / postcat / postcat.c
blob38fb5ab13cd76adba2cdead8daa19991836bc13b
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* postcat 1
6 /* SUMMARY
7 /* show Postfix queue file contents
8 /* SYNOPSIS
9 /* \fBpostcat\fR [\fB-oqv\fR] [\fB-c \fIconfig_dir\fR] [\fIfiles\fR...]
10 /* DESCRIPTION
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.
17 /* Options:
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.
21 /* .IP \fB-o\fR
22 /* Print the queue file offset of each record.
23 /* .IP \fB-q\fR
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.
28 /* .IP \fB-v\fR
29 /* Enable verbose logging for debugging purposes. Multiple \fB-v\fR
30 /* options make the software increasingly verbose.
31 /* DIAGNOSTICS
32 /* Problems are reported to the standard error stream.
33 /* ENVIRONMENT
34 /* .ad
35 /* .fi
36 /* .IP \fBMAIL_CONFIG\fR
37 /* Directory with Postfix configuration files.
38 /* CONFIGURATION PARAMETERS
39 /* .ad
40 /* .fi
41 /* The following \fBmain.cf\fR parameters are especially relevant to
42 /* this program.
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.
51 /* FILES
52 /* /var/spool/postfix, Postfix queue directory
53 /* SEE ALSO
54 /* postconf(5), Postfix configuration
55 /* LICENSE
56 /* .ad
57 /* .fi
58 /* The Secure Mailer license must be distributed with this software.
59 /* AUTHOR(S)
60 /* Wietse Venema
61 /* IBM T.J. Watson Research
62 /* P.O. Box 704
63 /* Yorktown Heights, NY 10598, USA
64 /*--*/
66 /* System library. */
68 #include <sys_defs.h>
69 #include <sys/stat.h>
70 #include <sys/time.h>
71 #include <stdlib.h>
72 #include <unistd.h>
73 #include <time.h>
74 #include <fcntl.h>
75 #include <string.h>
77 /* Utility library. */
79 #include <msg.h>
80 #include <vstream.h>
81 #include <vstring.h>
82 #include <msg_vstream.h>
83 #include <vstring_vstream.h>
84 #include <stringops.h>
86 /* Global library. */
88 #include <record.h>
89 #include <rec_type.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)
108 int prev_type = 0;
109 int rec_type;
110 struct timeval tv;
111 time_t time;
112 int first = 1;
113 int ch;
114 off_t offset;
115 int in_message = 0;
116 const char *error_text;
117 char *attr_name;
118 char *attr_value;
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));
130 return;
132 vstream_ungetc(fp, ch);
136 * Now look at the rest.
138 do {
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)
145 break;
146 if (first == 1) {
147 vstream_printf("*** ENVELOPE RECORDS %s ***\n", VSTREAM_PATH(fp));
148 first = 0;
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);
154 switch (rec_type) {
155 case REC_TYPE_TIME:
156 REC_TYPE_TIME_SCAN(STR(buffer), tv);
157 time = tv.tv_sec;
158 vstream_printf("%s: %s", rec_type_name(rec_type),
159 asctime(localtime(&time)));
160 break;
161 case REC_TYPE_WARN:
162 REC_TYPE_WARN_SCAN(STR(buffer), time);
163 vstream_printf("%s: %s", rec_type_name(rec_type),
164 asctime(localtime(&time)));
165 break;
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");
172 break;
173 case REC_TYPE_CONT: /* REC_TYPE_FILT collision */
174 if (!in_message)
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) {
180 rec_type = 0;
181 VSTREAM_PUTCHAR('\n');
183 break;
184 case REC_TYPE_NORM:
185 if (msg_verbose)
186 vstream_printf("%s: ", rec_type_name(rec_type));
187 vstream_fwrite(VSTREAM_OUT, STR(buffer), LEN(buffer));
188 VSTREAM_PUTCHAR('\n');
189 break;
190 case REC_TYPE_DTXT:
191 if (msg_verbose) {
192 vstream_printf("%s: ", rec_type_name(rec_type));
193 vstream_fwrite(VSTREAM_OUT, STR(buffer), LEN(buffer));
194 VSTREAM_PUTCHAR('\n');
196 break;
197 case REC_TYPE_MESG:
198 vstream_printf("*** MESSAGE CONTENTS %s ***\n", VSTREAM_PATH(fp));
199 in_message = 1;
200 break;
201 case REC_TYPE_XTRA:
202 vstream_printf("*** HEADER EXTRACTED %s ***\n", VSTREAM_PATH(fp));
203 in_message = 0;
204 break;
205 case REC_TYPE_END:
206 vstream_printf("*** MESSAGE FILE END %s ***\n", VSTREAM_PATH(fp));
207 break;
208 case REC_TYPE_ATTR:
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));
213 break;
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)));
219 break;
221 vstream_printf("%s: %s=%s\n", rec_type_name(rec_type),
222 attr_name, attr_value);
223 break;
224 default:
225 vstream_printf("%s: %s\n", rec_type_name(rec_type), STR(buffer));
226 break;
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)...]",
242 myname);
245 MAIL_VERSION_STAMP_DECLARE;
247 int main(int argc, char **argv)
249 VSTRING *buffer;
250 VSTREAM *fp;
251 int ch;
252 int fd;
253 struct stat st;
254 int flags = 0;
255 static char *queue_names[] = {
256 MAIL_QUEUE_MAILDROP,
257 MAIL_QUEUE_INCOMING,
258 MAIL_QUEUE_ACTIVE,
259 MAIL_QUEUE_DEFERRED,
260 MAIL_QUEUE_HOLD,
263 char **cpp;
264 int tries;
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");
282 * Set up logging.
284 msg_vstream_init(argv[0], VSTREAM_ERR);
287 * Parse JCL.
289 while ((ch = GETOPT(argc, argv, "c:oqv")) > 0) {
290 switch (ch) {
291 case 'c':
292 if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
293 msg_fatal("out of memory");
294 break;
295 case 'o':
296 flags |= PC_FLAG_OFFSET;
297 break;
298 case 'q':
299 flags |= PC_FLAG_QUEUE;
300 break;
301 case 'v':
302 msg_verbose++;
303 break;
304 default:
305 usage(argv[0]);
310 * Further initialization...
312 mail_conf_read();
315 * Initialize.
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",
325 VSTREAM_CTL_END);
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);
341 if (fp == 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]);
346 optind++;
351 * Copy the named files in the specified order.
353 else {
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]);
360 optind++;
365 * Clean up.
367 vstring_free(buffer);
368 exit(0);