diff --git a/postfix/HISTORY b/postfix/HISTORY index 5b06aab4f..b0b7d6c95 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -22010,3 +22010,18 @@ Apologies for any names omitted. Bitrot: OpenSSL API cleanups. Viktor Dukhovni. Files: .indent.pro, tls/tls.h, tls/tls_dane.c, tls/tls_fprint.c, tls/tls_misc.c, tls/tls_server.c, tls/tls_verify.c. + +20151124 + + Bugfix (introduced: Postfix 3.0): don't throttle a destination + after opportunistic TLS failure. Viktor Dukhovni and Wietse. + Files: smtp/smtp_proto.c, smtp/smtp.h, smtp/smtp_trouble.c. + +20151128 + + Feature: JSON-formatted queue listing with "postqueue -j". + Output is a stream of JSON objects, one per queue file. To + simplify stream-mode parsing, each JSON object is followed by + a newline character. Files: postqueue/postqueue.c, + postqueue/postqueue.h, postqueue/showq_compat.c, + postqueue/showq_json.c, showq/showq.c. diff --git a/postfix/README_FILES/STRESS_README b/postfix/README_FILES/STRESS_README index 85b73f521..ea535aaa6 100644 --- a/postfix/README_FILES/STRESS_README +++ b/postfix/README_FILES/STRESS_README @@ -105,8 +105,8 @@ later. 6 smtpd_starttls_timeout = ${stress?{10}:{300}}s 7 address_verify_poll_count = ${stress?{1}:{3}} -With Postfix versions before 3.0, replace ${stress?{x}:{y}} with ${stress?x}$ -{stress:y}. +Postfix versions before 3.0 use the older form ${stress?x}${stress:y} instead +of the newer form ${stress?{x}:{y}}. Translation: @@ -149,8 +149,8 @@ Translation: $unverified_sender_tempfail_action. No mail should be lost, as long as this measure is used only temporarily. -The syntax of ${name?value} and ${name:value} is explained at the beginning of -the postconf(5) manual page. +The syntax of ${name?{value}:{value}}, ${name?value} and ${name:value} is +explained at the beginning of the postconf(5) manual page. NOTE: Please keep in mind that the stress-adaptive feature is a fairly desperate measure to keep ssoommee legitimate mail flowing under overload diff --git a/postfix/html/STRESS_README.html b/postfix/html/STRESS_README.html index 3c0d0f622..4255dbefa 100644 --- a/postfix/html/STRESS_README.html +++ b/postfix/html/STRESS_README.html @@ -168,8 +168,8 @@ default with Postfix 2.6 and later.

-

With Postfix versions before 3.0, replace ${stress?{x}:{y}} -with ${stress?x}${stress:y}.

+

Postfix versions before 3.0 use the older form ${stress?x}${stress:y} +instead of the newer form ${stress?{x}:{y}}.

Translation:

@@ -219,8 +219,9 @@ as this measure is used only temporarily.

-

The syntax of ${name?value} and ${name:value} is explained at -the beginning of the postconf(5) manual page.

+

The syntax of ${name?{value}:{value}}, ${name?value} and +${name:value} is explained at the beginning of the postconf(5) +manual page.

NOTE: Please keep in mind that the stress-adaptive feature is a fairly desperate measure to keep some legitimate mail diff --git a/postfix/html/postqueue.1.html b/postfix/html/postqueue.1.html index d22c06945..3a332d720 100644 --- a/postfix/html/postqueue.1.html +++ b/postfix/html/postqueue.1.html @@ -10,11 +10,20 @@ POSTQUEUE(1) POSTQUEUE(1) postqueue - Postfix queue control SYNOPSIS + To flush the mail queue: + postqueue [-v] [-c config_dir] -f + postqueue [-v] [-c config_dir] -i queue_id - postqueue [-v] [-c config_dir] -p + postqueue [-v] [-c config_dir] -s site + To list the mail queue: + + postqueue [-v] [-c config_dir] -j + + postqueue [-v] [-c config_dir] -p + DESCRIPTION The postqueue(1) command implements the Postfix user interface for queue management. It implements operations that are traditionally @@ -46,6 +55,51 @@ POSTQUEUE(1) POSTQUEUE(1) This feature is available with Postfix version 2.4 and later. + -j Produce a queue listing in JSON format, based on output from the + showq(8) daemon. The result is a stream of zero or more JSON + objects, one per queue file. Each object is followed by a new- + line character to support simple streaming parsers. + + Object members have string values unless indicated otherwise. + Programs should ignore object members that are not listed here; + the list of members is expected to grow over time. + + queue_name + The name of the queue where the message was found. Note + that the contents of the mail queue may change while it + is being listed; some messages may appear more than once, + and some messages may be missed. + + queue_id + The queue file name. The name may be reused unless + "enable_long_queue_ids = true". + + arrival_time + The number of seconds since the start of the UNIX epoch. + + message_size + The number of bytes in the message header and body. This + number does not include message envelope information. It + is approximately equal to the number of bytes that would + be transmitted via SMTP including the <CR><LF> line end- + ings. + + sender The envelope sender address. + + recipients + An array containing zero or more objects with members: + + address + One recipient address. + + delay_reason + If present, the reason for delayed delivery. Some + delayed recipients have no delay reason, for exam- + ple, when delivery is in progress or when the sys- + tem was stopped before it could record the reason. + + This feature is available in Postfix 3.1 and later. + -p Produce a traditional sendmail-style queue listing. This option implements the traditional mailq command, by contacting the Postfix showq(8) daemon. @@ -82,6 +136,9 @@ POSTQUEUE(1) POSTQUEUE(1) This program is designed to run with set-group ID privileges, so that it can connect to Postfix daemon processes. +STANDARDS + RFC 7159 (JSON notation) + DIAGNOSTICS Problems are logged to syslogd(8) and to the standard error stream. @@ -169,5 +226,10 @@ POSTQUEUE(1) POSTQUEUE(1) P.O. Box 704 Yorktown Heights, NY 10598, USA + Wietse Venema + Google, Inc. + 111 8th Avenue + New York, NY 10011, USA + POSTQUEUE(1) diff --git a/postfix/html/showq.8.html b/postfix/html/showq.8.html index 2733e0f0b..ab9311f57 100644 --- a/postfix/html/showq.8.html +++ b/postfix/html/showq.8.html @@ -13,17 +13,18 @@ SHOWQ(8) SHOWQ(8) showq [generic Postfix daemon options] DESCRIPTION - The showq(8) daemon reports the Postfix mail queue status. It is the - program that emulates the sendmail `mailq' command. + The showq(8) daemon reports the Postfix mail queue status. The output + is meant to be formatted by the postqueue(1) command, as it emulates + the Sendmail `mailq' command. - The showq(8) daemon can also be run in stand-alone mode by the supe- - ruser. This mode of operation is used to emulate the `mailq' command + The showq(8) daemon can also be run in stand-alone mode by the supe- + ruser. This mode of operation is used to emulate the `mailq' command while the Postfix mail system is down. SECURITY - The showq(8) daemon can run in a chroot jail at fixed low privilege, - and takes no input from the client. Its service port is accessible to - local untrusted users, so the service can be susceptible to denial of + The showq(8) daemon can run in a chroot jail at fixed low privilege, + and takes no input from the client. Its service port is accessible to + local untrusted users, so the service can be susceptible to denial of service attacks. STANDARDS @@ -33,19 +34,19 @@ SHOWQ(8) SHOWQ(8) Problems and transactions are logged to syslogd(8). CONFIGURATION PARAMETERS - Changes to main.cf are picked up automatically as showq(8) processes + Changes to main.cf are picked up automatically as showq(8) processes run for only a limited amount of time. Use the command "postfix reload" to speed up a change. - The text below provides only a parameter summary. See postconf(5) for + 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 master.cf con- + The default location of the Postfix main.cf and master.cf con- figuration files. daemon_timeout (18000s) - How much time a Postfix daemon process may take to handle a + How much time a Postfix daemon process may take to handle a request before it is terminated by a built-in watchdog timer. duplicate_filter_limit (1000) @@ -57,11 +58,11 @@ SHOWQ(8) SHOWQ(8) The recipient of mail addressed to the null address. ipc_timeout (3600s) - The time limit for sending or receiving information over an + The time limit for sending or receiving information over an internal communication channel. max_idle (100s) - The maximum amount of time that an idle Postfix daemon process + The maximum amount of time that an idle Postfix daemon process waits for an incoming connection before terminating voluntarily. max_use (100) @@ -81,8 +82,8 @@ SHOWQ(8) SHOWQ(8) The syslog facility of Postfix logging. syslog_name (see 'postconf -d' output) - The mail system name that is prepended to the process name in - syslog records, so that "smtpd" becomes, for example, "post- + The mail system name that is prepended to the process name in + syslog records, so that "smtpd" becomes, for example, "post- fix/smtpd". Available in Postfix version 2.9 and later: @@ -110,5 +111,10 @@ SHOWQ(8) SHOWQ(8) P.O. Box 704 Yorktown Heights, NY 10598, USA + Wietse Venema + Google, Inc. + 111 8th Avenue + New York, NY 10011, USA + SHOWQ(8) diff --git a/postfix/makedefs b/postfix/makedefs index dbd2c569b..754eabac2 100644 --- a/postfix/makedefs +++ b/postfix/makedefs @@ -158,6 +158,11 @@ # 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 #-- # Emit system-dependent Makefile macro definitions to standard output. diff --git a/postfix/man/man1/postqueue.1 b/postfix/man/man1/postqueue.1 index 58d780060..f102893f1 100644 --- a/postfix/man/man1/postqueue.1 +++ b/postfix/man/man1/postqueue.1 @@ -8,13 +8,19 @@ Postfix queue control .SH "SYNOPSIS" .na .nf +\fBTo flush the mail queue\fR: + \fBpostqueue\fR [\fB\-v\fR] [\fB\-c \fIconfig_dir\fR] \fB\-f\fR -.br + \fBpostqueue\fR [\fB\-v\fR] [\fB\-c \fIconfig_dir\fR] \fB\-i \fIqueue_id\fR -.br -\fBpostqueue\fR [\fB\-v\fR] [\fB\-c \fIconfig_dir\fR] \fB\-p\fR -.br + \fBpostqueue\fR [\fB\-v\fR] [\fB\-c \fIconfig_dir\fR] \fB\-s \fIsite\fR + +\fBTo list the mail queue\fR: + +\fBpostqueue\fR [\fB\-v\fR] [\fB\-c \fIconfig_dir\fR] \fB\-j\fR + +\fBpostqueue\fR [\fB\-v\fR] [\fB\-c \fIconfig_dir\fR] \fB\-p\fR .SH DESCRIPTION .ad .fi @@ -46,6 +52,48 @@ This option implements the traditional \fBsendmail \-qI\fR command, by contacting the \fBflush\fR(8) server. This feature is available with Postfix version 2.4 and later. +.IP "\fB\-j\fR" +Produce a queue listing in JSON format, based on output +from the showq(8) daemon. The result is a stream of zero +or more JSON objects, one per queue file. Each object is +followed by a newline character to support simple streaming +parsers. +.sp +Object members have string values unless indicated otherwise. +Programs should ignore object members that are not listed +here; the list of members is expected to grow over time. +.RS +.IP \fBqueue_name\fB +The name of the queue where the message was found. Note +that the contents of the mail queue may change while it is +being listed; some messages may appear more than once, and +some messages may be missed. +.IP \fBqueue_id\fB +The queue file name. The name may be reused unless +"enable_long_queue_ids = true". +.IP \fBarrival_time\fB +The number of seconds since the start of the UNIX epoch. +.IP \fBmessage_size\fB +The number of bytes in the message header and body. This +number does not include message envelope information. It +is approximately equal to the number of bytes that would +be transmitted via SMTP including the line endings. +.IP \fBsender\fB +The envelope sender address. +.IP \fBrecipients\fB +An array containing zero or more objects with members: +.RS +.IP \fBaddress\fB +One recipient address. +.IP \fBdelay_reason\fB +If present, the reason for delayed delivery. Some delayed +recipients have no delay reason, for example, when delivery +is in progress or when the system was stopped before it +could record the reason. +.RE +.RE +.IP +This feature is available in Postfix 3.1 and later. .IP \fB\-p\fR Produce a traditional sendmail\-style queue listing. This option implements the traditional \fBmailq\fR command, @@ -85,6 +133,10 @@ this option is available for the super\-user only. .fi This program is designed to run with set\-group ID privileges, so that it can connect to Postfix daemon processes. +.SH "STANDARDS" +.na +.nf +RFC 7159 (JSON notation) .SH DIAGNOSTICS .ad .fi @@ -187,3 +239,8 @@ 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 diff --git a/postfix/man/man8/showq.8 b/postfix/man/man8/showq.8 index fe94231c1..5830d92b7 100644 --- a/postfix/man/man8/showq.8 +++ b/postfix/man/man8/showq.8 @@ -13,7 +13,8 @@ list the Postfix mail queue .ad .fi The \fBshowq\fR(8) daemon reports the Postfix mail queue status. -It is the program that emulates the sendmail `mailq' command. +The output is meant to be formatted by the postqueue(1) command, +as it emulates the Sendmail `mailq' command. The \fBshowq\fR(8) daemon can also be run in stand\-alone mode by the superuser. This mode of operation is used to emulate @@ -111,3 +112,8 @@ 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 diff --git a/postfix/postfix-install b/postfix/postfix-install index d5db0bbf3..01d17b485 100644 --- a/postfix/postfix-install +++ b/postfix/postfix-install @@ -183,6 +183,11 @@ # 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 #-- # Initialize. diff --git a/postfix/proto/STRESS_README.html b/postfix/proto/STRESS_README.html index 526bccef2..31d01c5af 100644 --- a/postfix/proto/STRESS_README.html +++ b/postfix/proto/STRESS_README.html @@ -168,8 +168,8 @@ default with Postfix 2.6 and later.

-

With Postfix versions before 3.0, replace ${stress?{x}:{y}} -with ${stress?x}${stress:y}.

+

Postfix versions before 3.0 use the older form ${stress?x}${stress:y} +instead of the newer form ${stress?{x}:{y}}.

Translation:

@@ -219,8 +219,9 @@ as this measure is used only temporarily.

-

The syntax of ${name?value} and ${name:value} is explained at -the beginning of the postconf(5) manual page.

+

The syntax of ${name?{value}:{value}}, ${name?value} and +${name:value} is explained at the beginning of the postconf(5) +manual page.

NOTE: Please keep in mind that the stress-adaptive feature is a fairly desperate measure to keep some legitimate mail diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index d2c85a7a5..e1fd21f54 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 "20151031" +#define MAIL_RELEASE_DATE "20151129" #define MAIL_VERSION_NUMBER "3.1" #ifdef SNAPSHOT diff --git a/postfix/src/postqueue/Makefile.in b/postfix/src/postqueue/Makefile.in index b7a3341e3..28a4fd9d4 100644 --- a/postfix/src/postqueue/Makefile.in +++ b/postfix/src/postqueue/Makefile.in @@ -1,7 +1,7 @@ SHELL = /bin/sh -SRCS = postqueue.c -OBJS = postqueue.o -HDRS = +SRCS = postqueue.c showq_compat.c showq_json.c +OBJS = postqueue.o showq_compat.o showq_json.o +HDRS = postqueue.h TESTSRC = DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) CFLAGS = $(DEBUG) $(OPT) $(DEFS) @@ -16,7 +16,7 @@ LIBS = ../../lib/lib$(LIB_PREFIX)global$(LIB_SUFFIX) \ $(PROG): $(OBJS) $(LIBS) $(CC) $(CFLAGS) $(SHLIB_RPATH) -o $@ $(OBJS) $(LIBS) $(SYSLIBS) -$(OBJS): ../../conf/makedefs.out +$(OBJS): ../../conf/makedefs.out postqueue.h Makefile: Makefile.in cat ../../conf/makedefs.out $? >$@ @@ -84,6 +84,7 @@ postqueue.o: ../../include/mymalloc.h postqueue.o: ../../include/nvtable.h postqueue.o: ../../include/safe.h postqueue.o: ../../include/smtp_stream.h +postqueue.o: ../../include/stringops.h postqueue.o: ../../include/sys_defs.h postqueue.o: ../../include/user_acl.h postqueue.o: ../../include/valid_hostname.h @@ -93,3 +94,40 @@ postqueue.o: ../../include/vstream.h postqueue.o: ../../include/vstring.h postqueue.o: ../../include/warn_stat.h postqueue.o: postqueue.c +postqueue.o: postqueue.h +showq_compat.o: ../../include/attr.h +showq_compat.o: ../../include/check_arg.h +showq_compat.o: ../../include/htable.h +showq_compat.o: ../../include/iostuff.h +showq_compat.o: ../../include/mail_date.h +showq_compat.o: ../../include/mail_params.h +showq_compat.o: ../../include/mail_proto.h +showq_compat.o: ../../include/mail_queue.h +showq_compat.o: ../../include/msg.h +showq_compat.o: ../../include/mymalloc.h +showq_compat.o: ../../include/nvtable.h +showq_compat.o: ../../include/stringops.h +showq_compat.o: ../../include/sys_defs.h +showq_compat.o: ../../include/vbuf.h +showq_compat.o: ../../include/vstream.h +showq_compat.o: ../../include/vstring.h +showq_compat.o: postqueue.h +showq_compat.o: showq_compat.c +showq_json.o: ../../include/attr.h +showq_json.o: ../../include/check_arg.h +showq_json.o: ../../include/htable.h +showq_json.o: ../../include/iostuff.h +showq_json.o: ../../include/mail_date.h +showq_json.o: ../../include/mail_params.h +showq_json.o: ../../include/mail_proto.h +showq_json.o: ../../include/mail_queue.h +showq_json.o: ../../include/msg.h +showq_json.o: ../../include/mymalloc.h +showq_json.o: ../../include/nvtable.h +showq_json.o: ../../include/stringops.h +showq_json.o: ../../include/sys_defs.h +showq_json.o: ../../include/vbuf.h +showq_json.o: ../../include/vstream.h +showq_json.o: ../../include/vstring.h +showq_json.o: postqueue.h +showq_json.o: showq_json.c diff --git a/postfix/src/postqueue/postqueue.c b/postfix/src/postqueue/postqueue.c index fdf1d7284..af9d38678 100644 --- a/postfix/src/postqueue/postqueue.c +++ b/postfix/src/postqueue/postqueue.c @@ -4,13 +4,19 @@ /* SUMMARY /* Postfix queue control /* SYNOPSIS +/* \fBTo flush the mail queue\fR: +/* /* \fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-f\fR -/* .br +/* /* \fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-i \fIqueue_id\fR -/* .br -/* \fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-p\fR -/* .br +/* /* \fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-s \fIsite\fR +/* +/* \fBTo list the mail queue\fR: +/* +/* \fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-j\fR +/* +/* \fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-p\fR /* DESCRIPTION /* The \fBpostqueue\fR(1) command implements the Postfix user interface /* for queue management. It implements operations that are @@ -40,6 +46,48 @@ /* command, by contacting the \fBflush\fR(8) server. /* /* This feature is available with Postfix version 2.4 and later. +/* .IP "\fB-j\fR" +/* Produce a queue listing in JSON format, based on output +/* from the showq(8) daemon. The result is a stream of zero +/* or more JSON objects, one per queue file. Each object is +/* followed by a newline character to support simple streaming +/* parsers. +/* .sp +/* Object members have string values unless indicated otherwise. +/* Programs should ignore object members that are not listed +/* here; the list of members is expected to grow over time. +/* .RS +/* .IP \fBqueue_name\fB +/* The name of the queue where the message was found. Note +/* that the contents of the mail queue may change while it is +/* being listed; some messages may appear more than once, and +/* some messages may be missed. +/* .IP \fBqueue_id\fB +/* The queue file name. The name may be reused unless +/* "enable_long_queue_ids = true". +/* .IP \fBarrival_time\fB +/* The number of seconds since the start of the UNIX epoch. +/* .IP \fBmessage_size\fB +/* The number of bytes in the message header and body. This +/* number does not include message envelope information. It +/* is approximately equal to the number of bytes that would +/* be transmitted via SMTP including the line endings. +/* .IP \fBsender\fB +/* The envelope sender address. +/* .IP \fBrecipients\fB +/* An array containing zero or more objects with members: +/* .RS +/* .IP \fBaddress\fB +/* One recipient address. +/* .IP \fBdelay_reason\fB +/* If present, the reason for delayed delivery. Some delayed +/* recipients have no delay reason, for example, when delivery +/* is in progress or when the system was stopped before it +/* could record the reason. +/* .RE +/* .RE +/* .IP +/* This feature is available in Postfix 3.1 and later. /* .IP \fB-p\fR /* Produce a traditional sendmail-style queue listing. /* This option implements the traditional \fBmailq\fR command, @@ -77,6 +125,8 @@ /* .fi /* This program is designed to run with set-group ID privileges, so /* that it can connect to Postfix daemon processes. +/* STANDARDS +/* RFC 7159 (JSON notation) /* DIAGNOSTICS /* Problems are logged to \fBsyslogd\fR(8) and to the standard error /* stream. @@ -161,6 +211,11 @@ /* 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. */ @@ -188,6 +243,7 @@ #include #include #include +#include /* Global library. */ @@ -208,6 +264,8 @@ /* Application-specific. */ +#include + /* * WARNING WARNING WARNING * @@ -241,6 +299,7 @@ #define PQ_MODE_FLUSH_QUEUE 2 /* flush queue */ #define PQ_MODE_FLUSH_SITE 3 /* flush site */ #define PQ_MODE_FLUSH_FILE 4 /* flush message */ +#define PQ_MODE_JSON_LIST 5 /* JSON-format queue listing */ /* * Silly little macros (SLMs). @@ -261,10 +320,9 @@ static const CONFIG_STR_TABLE str_table[] = { /* show_queue - show queue status */ -static void show_queue(void) +static void show_queue(int mode) { const char *errstr; - char buf[VSTREAM_BUFSIZE]; VSTREAM *showq; int n; uid_t uid = getuid(); @@ -277,19 +335,20 @@ static void show_queue(void) errstr, (long) uid); /* - * Connect to the show queue service. Terminate silently when piping into - * a program that terminates early. + * Connect to the show queue service. */ if ((showq = mail_connect(MAIL_CLASS_PUBLIC, var_showq_service, BLOCKING)) != 0) { - while ((n = vstream_fread(showq, buf, sizeof(buf))) > 0) { - if (vstream_fwrite(VSTREAM_OUT, buf, n) != n - || vstream_fflush(VSTREAM_OUT) != 0) { - if (errno == EPIPE) - break; - msg_fatal("write error: %m"); - } + switch (mode) { + case PQ_MODE_MAILQ_LIST: + showq_compat(showq); + break; + case PQ_MODE_JSON_LIST: + showq_json(showq); + break; + default: + msg_panic("show_queue: unknown mode %d", mode); } - if (vstream_fclose(showq) && errno != EPIPE) + if (vstream_fclose(showq)) msg_warn("close: %m"); } @@ -308,21 +367,40 @@ static void show_queue(void) * directly. Just run the showq program in stand-alone mode. */ else if (geteuid() == 0) { + char *showq_path; ARGV *argv; int stat; msg_warn("Mail system is down -- accessing queue directly"); + showq_path = concatenate(var_daemon_dir, "/", var_showq_service, + (char *) 0); argv = argv_alloc(6); - argv_add(argv, var_showq_service, "-u", "-S", (char *) 0); + argv_add(argv, showq_path, "-u", "-S", (char *) 0); for (n = 0; n < msg_verbose; n++) argv_add(argv, "-v", (char *) 0); argv_terminate(argv); - stat = mail_run_foreground(var_daemon_dir, argv->argv); + if ((showq = vstream_popen(O_RDONLY, + CA_VSTREAM_POPEN_ARGV(argv->argv), + CA_VSTREAM_POPEN_END)) == 0) { + stat = -1; + } else { + switch (mode) { + case PQ_MODE_MAILQ_LIST: + showq_compat(showq); + break; + case PQ_MODE_JSON_LIST: + showq_json(showq); + break; + default: + msg_panic("show_queue: unknown mode %d", mode); + } + stat = vstream_pclose(showq); + } argv_free(argv); if (stat != 0) msg_fatal_status(stat < 0 ? EX_OSERR : EX_SOFTWARE, - "Error running %s/%s", - var_daemon_dir, argv->argv[0]); + "Error running %s", showq_path); + myfree(showq_path); } /* @@ -500,7 +578,7 @@ int main(int argc, char **argv) * mail configuration read routine. Don't do complex things until we have * completed initializations. */ - while ((c = GETOPT(argc, argv, "c:fi:ps:v")) > 0) { + while ((c = GETOPT(argc, argv, "c:fi:jps:v")) > 0) { switch (c) { case 'c': /* non-default configuration */ if (setenv(CONF_ENV_PATH, optarg, 1) < 0) @@ -517,6 +595,11 @@ int main(int argc, char **argv) mode = PQ_MODE_FLUSH_FILE; id_to_flush = optarg; break; + case 'j': + if (mode != PQ_MODE_DEFAULT) + usage(); + mode = PQ_MODE_JSON_LIST; + break; case 'p': /* traditional mailq */ if (mode != PQ_MODE_DEFAULT) usage(); @@ -597,7 +680,8 @@ int main(int argc, char **argv) msg_panic("unknown operation mode: %d", mode); /* NOTREACHED */ case PQ_MODE_MAILQ_LIST: - show_queue(); + case PQ_MODE_JSON_LIST: + show_queue(mode); exit(0); break; case PQ_MODE_FLUSH_SITE: diff --git a/postfix/src/postqueue/postqueue.h b/postfix/src/postqueue/postqueue.h new file mode 100644 index 000000000..b1b7d27ca --- /dev/null +++ b/postfix/src/postqueue/postqueue.h @@ -0,0 +1,35 @@ +/*++ +/* NAME +/* postqueue 5h +/* SUMMARY +/* postqueue internal interfaces +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * showq_compat.c + */ +extern void showq_compat(VSTREAM *); + + /* + * showq_json.c + */ +extern void showq_json(VSTREAM *); + +/* 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 +/*--*/ diff --git a/postfix/src/postqueue/showq_compat.c b/postfix/src/postqueue/showq_compat.c new file mode 100644 index 000000000..c533288e8 --- /dev/null +++ b/postfix/src/postqueue/showq_compat.c @@ -0,0 +1,209 @@ +/*++ +/* NAME +/* showq_compat 8 +/* SUMMARY +/* Sendmail mailq compatibitily adapter +/* SYNOPSIS +/* void showq_compat( +/* VSTREAM *showq) +/* DESCRIPTION +/* This function converts a record stream from the showq(8) +/* daemon to of an approximation of Sendmail mailq command +/* output. +/* DIAGNOSTICS +/* Fatal errors: out of memory, malformed showq(8) daemon output. +/* 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 +#include +#include +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include + +/* Application-specific. */ + +#include + + /* + * The enable_long_queue_ids parameter determines the output format. + * + * The historical output format for short queue IDs (inode number and time in + * microseconds modulo 1) is not suitable for large inode numbers, but we + * won't change it to avoid breaking compatibility with programs that parse + * this output. + */ +#define S_STRING_FORMAT "%-11s %7s %-20s %s\n" +#define S_SENDER_FORMAT "%-11s %7ld %20.20s %s\n" +#define S_HEADINGS "-Queue ID-", "--Size--", \ + "----Arrival Time----", "-Sender/Recipient-------" + +#define L_STRING_FORMAT "%-17s %8s %-19s %s\n" +#define L_SENDER_FORMAT "%-17s %8ld %19.19s %s\n" +#define L_HEADINGS "----Queue ID-----", "--Size--", \ + "---Arrival Time----", "--Sender/Recipient------" + +#define STR(x) vstring_str(x) + +/* showq_message - report status for one message */ + +static unsigned long showq_message(VSTREAM *showq_stream) +{ + static VSTRING *queue_name = 0; + static VSTRING *queue_id = 0; + static VSTRING *id_status = 0; + static VSTRING *addr = 0; + static VSTRING *why = 0; + long arrival_time; + long message_size; + int message_status; + char *saved_reason = mystrdup(""); + const char *show_reason; + int padding; + int showq_status; + time_t time_t_arrival_time; + + /* + * One-time initialization. + */ + if (queue_name == 0) { + queue_name = vstring_alloc(100); + queue_id = vstring_alloc(100); + id_status = vstring_alloc(100); + addr = vstring_alloc(100); + why = vstring_alloc(100); + } + + /* + * Read the message properties and sender address. + */ + if (attr_scan(showq_stream, ATTR_FLAG_MORE | ATTR_FLAG_STRICT, + RECV_ATTR_STR(MAIL_ATTR_QUEUE, queue_name), + RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id), + RECV_ATTR_LONG(MAIL_ATTR_TIME, &arrival_time), + RECV_ATTR_LONG(MAIL_ATTR_SIZE, &message_size), + RECV_ATTR_STR(MAIL_ATTR_SENDER, addr), + ATTR_TYPE_END) != 5) + msg_fatal_status(EX_SOFTWARE, "malformed showq server response"); + + /* + * Decorate queue file names in specific states, then print the result + * left-aligned, followed by other status info and the sender address + * which is already in externalized RFC 5321 form. + */ + message_status = (strcmp(STR(queue_name), MAIL_QUEUE_ACTIVE) == 0 ? '*' : + strcmp(STR(queue_name), MAIL_QUEUE_HOLD) == 0 ? '!' : ' '); + vstring_sprintf(id_status, "%s%c", STR(queue_id), message_status); + time_t_arrival_time = arrival_time; + vstream_printf(var_long_queue_ids ? + L_SENDER_FORMAT : S_SENDER_FORMAT, STR(id_status), + message_size, asctime(localtime(&time_t_arrival_time)), + STR(addr)); + + /* + * Read zero or more (recipient, reason) pair(s) until attr_scan_more() + * consumes a terminator. If the showq daemon messes up, don't try to + * resynchronize. + */ + while ((showq_status = attr_scan_more(showq_stream)) > 0) { + if (attr_scan(showq_stream, ATTR_FLAG_MORE | ATTR_FLAG_STRICT, + RECV_ATTR_STR(MAIL_ATTR_RECIP, addr), + RECV_ATTR_STR(MAIL_ATTR_WHY, why), + ATTR_TYPE_END) != 2) + msg_fatal_status(EX_SOFTWARE, "malformed showq server response"); + + /* + * Don't output a "(reason)" line when no recipient has a reason, or + * when the previous recipient has the same (non)reason as the + * current recipient. Do output a "(reason unavailable)" when the + * previous recipient has a reason, and the current recipient has + * none. + */ + if (strcmp(saved_reason, STR(why)) != 0) { + myfree(saved_reason); + saved_reason = mystrdup(STR(why)); + show_reason = *saved_reason ? saved_reason : "reason unavailable"; + if ((padding = 76 - strlen(show_reason)) < 0) + padding = 0; + vstream_printf("%*s(%s)\n", padding, "", show_reason); + } + vstream_printf(var_long_queue_ids ? + L_STRING_FORMAT : S_STRING_FORMAT, + "", "", "", STR(addr)); + } + if (showq_status < 0) + msg_fatal_status(EX_SOFTWARE, "malformed showq server response"); + myfree(saved_reason); + return (message_size); +} + +/* showq_compat - legacy mailq-style output adapter */ + +void showq_compat(VSTREAM *showq_stream) +{ + unsigned long file_count = 0; + unsigned long queue_size = 0; + int showq_status; + + /* + * Process zero or more queue file objects until attr_scan_more() + * consumes a terminator. + */ + while ((showq_status = attr_scan_more(showq_stream)) > 0) { + if (file_count > 0) { + vstream_printf("\n"); + } else if (var_long_queue_ids) { + vstream_printf(L_STRING_FORMAT, L_HEADINGS); + } else { + vstream_printf(S_STRING_FORMAT, S_HEADINGS); + } + queue_size += showq_message(showq_stream); + file_count++; + vstream_fflush(VSTREAM_OUT); + } + if (showq_status < 0) + msg_fatal_status(EX_SOFTWARE, "malformed showq server response"); + + /* + * Print the queue summary. + */ + if (file_count == 0) + vstream_printf("Mail queue is empty\n"); + else { + vstream_printf("\n-- %lu Kbytes in %lu Request%s.\n", + queue_size / 1024, file_count, + file_count == 1 ? "" : "s"); + } + vstream_fflush(VSTREAM_OUT); +} diff --git a/postfix/src/postqueue/showq_json.c b/postfix/src/postqueue/showq_json.c new file mode 100644 index 000000000..9a9f30aba --- /dev/null +++ b/postfix/src/postqueue/showq_json.c @@ -0,0 +1,213 @@ +/*++ +/* NAME +/* showq_json 8 +/* SUMMARY +/* JSON queue status formatter +/* SYNOPSIS +/* void showq_json( +/* VSTREAM *showq) +/* DESCRIPTION +/* This function converts showq(8) daemon output to JSON format. +/* DIAGNOSTICS +/* Fatal errors: out of memory, malformed showq(8) daemon output. +/* 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 +#include +#include +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include + +/* Application-specific. */ + +#include + +#define STR(x) vstring_str(x) +#define LEN(x) VSTRING_LEN(x) + +/* json_quote - quote JSON string */ + +static char *json_quote(VSTRING *result, const char *text) +{ + unsigned char *cp; + int ch; + + /* + * We use short escape sequences for common control characters. Note that + * RFC 4627 allows "/" (0x2F) to be sent without quoting. Differences + * with RFC 4627: we send DEL (0x7f) as \u007F; the result remains RFC + * 4627 complaint. + */ + VSTRING_RESET(result); + for (cp = (unsigned char *) text; (ch = *cp) != 0; cp++) { + if (UNEXPECTED(ISCNTRL(ch))) { + switch (ch) { + case '\b': + VSTRING_ADDCH(result, '\\'); + VSTRING_ADDCH(result, 'b'); + break; + case '\f': + VSTRING_ADDCH(result, '\\'); + VSTRING_ADDCH(result, 'f'); + break; + case '\n': + VSTRING_ADDCH(result, '\\'); + VSTRING_ADDCH(result, 'n'); + break; + case '\r': + VSTRING_ADDCH(result, '\\'); + VSTRING_ADDCH(result, 'r'); + break; + case '\t': + VSTRING_ADDCH(result, '\\'); + VSTRING_ADDCH(result, 't'); + break; + default: + vstring_sprintf(result, "\\u%04X", ch); + break; + } + } else { + switch (ch) { + case '\\': + case '"': + VSTRING_ADDCH(result, '\\'); + /* FALLTHROUGH */ + default: + VSTRING_ADDCH(result, ch); + break; + } + } + } + VSTRING_TERMINATE(result); + + /* + * Force the result to be UTF-8 (with SMTPUTF8 enabled) or ASCII (with + * SMTPUTF8 disabled). + */ + printable(STR(result), '?'); + return (STR(result)); +} + +/* json_message - report status for one message */ + +static void format_json(VSTREAM *showq_stream) +{ + static VSTRING *queue_name = 0; + static VSTRING *queue_id = 0; + static VSTRING *addr = 0; + static VSTRING *why = 0; + static VSTRING *quote_buf = 0; + long arrival_time; + long message_size; + int showq_status; + int rcpt_count = 0; + + /* + * One-time initialization. + */ + if (queue_name == 0) { + queue_name = vstring_alloc(100); + queue_id = vstring_alloc(100); + addr = vstring_alloc(100); + why = vstring_alloc(100); + quote_buf = vstring_alloc(100); + } + + /* + * Read the message properties and sender address. + */ + if (attr_scan(showq_stream, ATTR_FLAG_MORE | ATTR_FLAG_STRICT, + RECV_ATTR_STR(MAIL_ATTR_QUEUE, queue_name), + RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id), + RECV_ATTR_LONG(MAIL_ATTR_TIME, &arrival_time), + RECV_ATTR_LONG(MAIL_ATTR_SIZE, &message_size), + RECV_ATTR_STR(MAIL_ATTR_SENDER, addr), + ATTR_TYPE_END) != 5) + msg_fatal_status(EX_SOFTWARE, "malformed showq server response"); + vstream_printf("{"); + vstream_printf("\"queue_name\": \"%s\",", + json_quote(quote_buf, STR(queue_name))); + vstream_printf("\"queue_id\": \"%s\",", + json_quote(quote_buf, STR(queue_id))); + vstream_printf("\"arrival_time\": %ld,", arrival_time); + vstream_printf("\"message_size\": %ld,", message_size); + vstream_printf("\"sender\": \"%s\",", + json_quote(quote_buf, STR(addr))); + + /* + Read zero or more (recipient, reason) pair(s) until attr_scan_more() + * consumes a terminator. If the showq daemon messes up, don't try to + * resynchronize. + */ + vstream_printf("\"recipients\": ["); + for (rcpt_count = 0; (showq_status = attr_scan_more(showq_stream)) > 0; rcpt_count++) { + if (rcpt_count > 0) + vstream_printf(","); + vstream_printf("{"); + if (attr_scan(showq_stream, ATTR_FLAG_MORE | ATTR_FLAG_STRICT, + RECV_ATTR_STR(MAIL_ATTR_RECIP, addr), + RECV_ATTR_STR(MAIL_ATTR_WHY, why), + ATTR_TYPE_END) != 2) + msg_fatal_status(EX_SOFTWARE, "malformed showq server response"); + vstream_printf("\"address\": \"%s\"", + json_quote(quote_buf, STR(addr))); + if (LEN(why) > 0) + vstream_printf(",\"delay_reason\": \"%s\",", + json_quote(quote_buf, STR(why))); + vstream_printf("}"); + } + vstream_printf("]"); + if (showq_status < 0) + msg_fatal_status(EX_SOFTWARE, "malformed showq server response"); + vstream_printf("}\n"); + vstream_fflush(VSTREAM_OUT); +} + +/* showq_json - streaming JSON-format output adapter */ + +void showq_json(VSTREAM *showq_stream) +{ + int showq_status; + + /* + * Emit zero or more queue file objects until attr_scan_more() + * consumes a terminator. + */ + while ((showq_status = attr_scan_more(showq_stream)) > 0) { + format_json(showq_stream); + } + if (showq_status < 0) + msg_fatal_status(EX_SOFTWARE, "malformed showq server response"); +} diff --git a/postfix/src/showq/showq.c b/postfix/src/showq/showq.c index 6d4054f1d..80fd222aa 100644 --- a/postfix/src/showq/showq.c +++ b/postfix/src/showq/showq.c @@ -7,7 +7,8 @@ /* \fBshowq\fR [generic Postfix daemon options] /* DESCRIPTION /* The \fBshowq\fR(8) daemon reports the Postfix mail queue status. -/* It is the program that emulates the sendmail `mailq' command. +/* The output is meant to be formatted by the postqueue(1) command, +/* as it emulates the Sendmail `mailq' command. /* /* The \fBshowq\fR(8) daemon can also be run in stand-alone mode /* by the superuser. This mode of operation is used to emulate @@ -89,6 +90,11 @@ /* 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. */ @@ -140,18 +146,6 @@ int var_dup_filter_limit; char *var_empty_addr; -#define S_STRING_FORMAT "%-10s %8s %-20s %s\n" -#define S_SENDER_FORMAT "%-11s %7ld %20.20s %s\n" -#define S_DROP_FORMAT "%-10s%c %7ld %20.20s (maildrop queue, sender UID %u)\n" -#define S_HEADINGS "-Queue ID-", "--Size--", \ - "----Arrival Time----", "-Sender/Recipient-------" - -#define L_STRING_FORMAT "%-17s %8s %-19s %s\n" -#define L_SENDER_FORMAT "%-17s %8ld %19.19s %s\n" -#define L_DROP_FORMAT "%-16s%c %8ld %19.19s (maildrop queue, sender UID %u)\n" -#define L_HEADINGS "----Queue ID-----", "--Size--", \ - "---Arrival Time----", "--Sender/Recipient------" - static void showq_reasons(VSTREAM *, BOUNCE_LOG *, RCPT_BUF *, DSN_BUF *, HTABLE *); @@ -167,15 +161,30 @@ static void showq_report(VSTREAM *client, char *queue, char *id, int rec_type; time_t arrival_time = 0; char *start; - long msg_size = 0; + long msg_size = size; BOUNCE_LOG *logfile; HTABLE *dup_filter = 0; RCPT_BUF *rcpt_buf = 0; DSN_BUF *dsn_buf = 0; - char status = (strcmp(queue, MAIL_QUEUE_ACTIVE) == 0 ? '*' : - strcmp(queue, MAIL_QUEUE_HOLD) == 0 ? '!' : ' '); + int sender_seen = 0; int msg_size_ok = 0; + /* + * Let the optimizer worry about eliminating duplicate code. + */ +#define SHOWQ_CLEANUP_AND_RETURN { \ + if (sender_seen > 0) \ + attr_print(client, ATTR_FLAG_NONE, ATTR_TYPE_END); \ + vstring_free(buf); \ + vstring_free(printable_quoted_addr); \ + if (rcpt_buf) \ + rcpb_free(rcpt_buf); \ + if (dsn_buf) \ + dsb_free(dsn_buf); \ + if (dup_filter) \ + htable_free(dup_filter, (void (*) (void *)) 0); \ + } + /* * XXX addresses in defer logfiles are in printable quoted form, while * addresses in message envelope records are in raw unquoted form. This @@ -192,12 +201,16 @@ static void showq_report(VSTREAM *client, char *queue, char *id, msg_info("record %c %s", rec_type, printable(start, '?')); switch (rec_type) { case REC_TYPE_TIME: - arrival_time = atol(start); + /* TODO: parse seconds and microseconds. */ + if (arrival_time == 0) + arrival_time = atol(start); break; case REC_TYPE_SIZE: - if (msg_size == 0) { - if ((msg_size_ok = ((msg_size = atol(start)) > 0)) == 0) { - msg_warn("%s: malformed size record: %.100s", + if (msg_size_ok == 0) { + msg_size_ok = (alldig(start) && (msg_size = atol(start)) >= 0); + if (msg_size_ok == 0) { + msg_warn("%s: malformed size record: %.100s " + "-- using file size instead", id, printable(start, '?')); msg_size = size; } @@ -208,25 +221,40 @@ static void showq_report(VSTREAM *client, char *queue, char *id, start = var_empty_addr; quote_822_local(printable_quoted_addr, start); printable(STR(printable_quoted_addr), '?'); - /* quote_822_local() saves buf, so we can reuse its space. */ - vstring_sprintf(buf, "%s%c", id, status); - vstream_fprintf(client, var_long_queue_ids ? - L_SENDER_FORMAT : S_SENDER_FORMAT, STR(buf), - msg_size > 0 ? msg_size : size, arrival_time > 0 ? - asctime(localtime(&arrival_time)) : - asctime(localtime(&mtime)), - STR(printable_quoted_addr)); + if (sender_seen++ > 0) { + msg_warn("%s: duplicate sender address: %s " + "-- skipping remainder of this file", + id, STR(printable_quoted_addr)); + SHOWQ_CLEANUP_AND_RETURN; + } + attr_print(client, ATTR_FLAG_MORE, + SEND_ATTR_STR(MAIL_ATTR_QUEUE, queue), + SEND_ATTR_STR(MAIL_ATTR_QUEUEID, id), + SEND_ATTR_LONG(MAIL_ATTR_TIME, arrival_time > 0 ? + arrival_time : mtime), + SEND_ATTR_LONG(MAIL_ATTR_SIZE, msg_size), + SEND_ATTR_STR(MAIL_ATTR_SENDER, + STR(printable_quoted_addr)), + ATTR_TYPE_END); break; case REC_TYPE_RCPT: + if (sender_seen == 0) { + msg_warn("%s: missing sender address: %s " + "-- skipping remainder of this file", + id, STR(printable_quoted_addr)); + SHOWQ_CLEANUP_AND_RETURN; + } if (*start == 0) /* can't happen? */ start = var_empty_addr; quote_822_local(printable_quoted_addr, start); printable(STR(printable_quoted_addr), '?'); if (dup_filter == 0 || htable_locate(dup_filter, STR(printable_quoted_addr)) == 0) - vstream_fprintf(client, var_long_queue_ids ? - L_STRING_FORMAT : S_STRING_FORMAT, - "", "", "", STR(printable_quoted_addr)); + attr_print(client, ATTR_FLAG_MORE, + SEND_ATTR_STR(MAIL_ATTR_RECIP, + STR(printable_quoted_addr)), + SEND_ATTR_STR(MAIL_ATTR_WHY, ""), + ATTR_TYPE_END); break; case REC_TYPE_MESG: if (msg_size_ok && vstream_fseek(qfile, msg_size, SEEK_CUR) < 0) @@ -237,18 +265,22 @@ static void showq_report(VSTREAM *client, char *queue, char *id, } /* - * With the heading printed, see if there is a defer logfile. The - * defer logfile is not necessarily complete: delivery may be + * Before listing any recipients from the queue file, try to list + * recipients from the corresponding defer logfile with per-recipient + * descriptions why delivery was deferred. + * + * The defer logfile is not necessarily complete: delivery may be * interrupted (postfix stop or reload) before all recipients have * been tried. * * Therefore we keep a record of recipients found in the defer logfile, * and try to avoid listing those recipients again when processing - * the remainder of the queue file. + * recipients from the queue file. */ if (rec_type == REC_TYPE_FROM - && dup_filter == 0 && (logfile = bounce_log_open(MAIL_QUEUE_DEFER, id, O_RDONLY, 0)) != 0) { + if (dup_filter != 0) + msg_panic("showq_report: attempt to reuse duplicate filter"); dup_filter = htable_create(var_dup_filter_limit); if (rcpt_buf == 0) rcpt_buf = rcpb_create(); @@ -259,14 +291,7 @@ static void showq_report(VSTREAM *client, char *queue, char *id, msg_warn("close %s %s: %m", MAIL_QUEUE_DEFER, id); } } - vstring_free(buf); - vstring_free(printable_quoted_addr); - if (rcpt_buf) - rcpb_free(rcpt_buf); - if (dsn_buf) - dsb_free(dsn_buf); - if (dup_filter) - htable_free(dup_filter, (void (*) (void *)) 0); + SHOWQ_CLEANUP_AND_RETURN; } /* showq_reasons - show deferral reasons */ @@ -274,8 +299,6 @@ static void showq_report(VSTREAM *client, char *queue, char *id, static void showq_reasons(VSTREAM *client, BOUNCE_LOG *bp, RCPT_BUF *rcpt_buf, DSN_BUF *dsn_buf, HTABLE *dup_filter) { - char *saved_reason = 0; - int padding; RECIPIENT *rcpt = &rcpt_buf->rcpt; DSN *dsn = &dsn_buf->dsn; @@ -289,23 +312,11 @@ static void showq_reasons(VSTREAM *client, BOUNCE_LOG *bp, RCPT_BUF *rcpt_buf, if (htable_locate(dup_filter, rcpt->address) == 0) htable_enter(dup_filter, rcpt->address, (void *) 0); - /* - * Don't print the reason when the previous recipient had the same - * problem. - */ - if (saved_reason == 0 || strcmp(saved_reason, dsn->reason) != 0) { - if (saved_reason) - myfree(saved_reason); - saved_reason = mystrdup(dsn->reason); - if ((padding = 76 - strlen(saved_reason)) < 0) - padding = 0; - vstream_fprintf(client, "%*s(%s)\n", padding, "", saved_reason); - } - vstream_fprintf(client, var_long_queue_ids ? L_STRING_FORMAT : - S_STRING_FORMAT, "", "", "", rcpt->address); + attr_print(client, ATTR_FLAG_MORE, + SEND_ATTR_STR(MAIL_ATTR_RECIP, rcpt->address), + SEND_ATTR_STR(MAIL_ATTR_WHY, dsn->reason), + ATTR_TYPE_END); } - if (saved_reason) - myfree(saved_reason); } @@ -318,7 +329,6 @@ static void showq_service(VSTREAM *client, char *unused_service, char **argv) int status; char *id; int file_count; - unsigned long queue_size = 0; struct stat st; struct queue_info { char *name; /* queue name */ @@ -368,30 +378,14 @@ static void showq_service(VSTREAM *client, char *unused_service, char **argv) saved_id = mystrdup(id); status = mail_open_ok(qp->name, id, &st, &path); if (status == MAIL_OPEN_YES) { - if (file_count == 0) { - if (var_long_queue_ids) - vstream_fprintf(client, L_STRING_FORMAT, L_HEADINGS); - else - vstream_fprintf(client, S_STRING_FORMAT, S_HEADINGS); - } else - vstream_fprintf(client, "\n"); if ((qfile = mail_queue_open(qp->name, id, O_RDONLY, 0)) != 0) { - queue_size += st.st_size; showq_report(client, qp->name, id, qfile, (long) st.st_size, st.st_mtime); if (vstream_fclose(qfile)) msg_warn("close file %s %s: %m", qp->name, id); - } else if (strcmp(qp->name, MAIL_QUEUE_MAILDROP) == 0) { - queue_size += st.st_size; - vstream_fprintf(client, var_long_queue_ids ? - L_DROP_FORMAT : S_DROP_FORMAT, id, ' ', - (long) st.st_size, - asctime(localtime(&st.st_mtime)), - (unsigned) st.st_uid); - } else if (errno != ENOENT) - msg_fatal("open %s %s: %m", qp->name, id); - file_count++; - vstream_fflush(client); + } else if (errno != ENOENT) { + msg_warn("open %s %s: %m", qp->name, id); + } } vstream_fflush(client); } @@ -399,13 +393,7 @@ static void showq_service(VSTREAM *client, char *unused_service, char **argv) myfree(saved_id); scan_dir_close(scan); } - if (file_count == 0) - vstream_fprintf(client, "Mail queue is empty\n"); - else { - vstream_fprintf(client, "\n-- %lu Kbytes in %d Request%s.\n", - queue_size / 1024, file_count, - file_count == 1 ? "" : "s"); - } + attr_print(client, ATTR_FLAG_NONE, ATTR_TYPE_END); } MAIL_VERSION_STAMP_DECLARE; diff --git a/postfix/src/smtp/smtp.h b/postfix/src/smtp/smtp.h index f8c08ff9e..90e7c3b2c 100644 --- a/postfix/src/smtp/smtp.h +++ b/postfix/src/smtp/smtp.h @@ -567,16 +567,21 @@ extern void smtp_rcpt_done(SMTP_STATE *, SMTP_RESP *, RECIPIENT *); /* * smtp_trouble.c */ +#define SMTP_THROTTLE 1 +#define SMTP_NOTHROTTLE 0 extern int smtp_sess_fail(SMTP_STATE *); -extern int PRINTFLIKE(4, 5) smtp_site_fail(SMTP_STATE *, const char *, - SMTP_RESP *, const char *,...); -extern int PRINTFLIKE(4, 5) smtp_mesg_fail(SMTP_STATE *, const char *, +extern int PRINTFLIKE(5, 6) smtp_misc_fail(SMTP_STATE *, int, const char *, SMTP_RESP *, const char *,...); extern void PRINTFLIKE(5, 6) smtp_rcpt_fail(SMTP_STATE *, RECIPIENT *, const char *, SMTP_RESP *, const char *,...); extern int smtp_stream_except(SMTP_STATE *, int, const char *); +#define smtp_site_fail(state, mta, resp, ...) \ + smtp_misc_fail((state), SMTP_THROTTLE, (mta), (resp), __VA_ARGS__) +#define smtp_mesg_fail(state, mta, resp, ...) \ + smtp_misc_fail((state), SMTP_NOTHROTTLE, (mta), (resp), __VA_ARGS__) + /* * smtp_unalias.c */ diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c index e55110895..89a3aab94 100644 --- a/postfix/src/smtp/smtp_proto.c +++ b/postfix/src/smtp/smtp_proto.c @@ -935,10 +935,17 @@ static int smtp_start_tls(SMTP_STATE *state) * authentication. If the server doesn't announce SASL support over * plaintext connections, then we don't want delivery to fail with * "relay access denied". + * + * If TLS is opportunistic, don't throttle the destination, otherwise if + * the mail is volume is high enough we may have difficulty ever + * draining even the deferred mail, as new mail provides a constant + * stream of negative feedback. */ if (PLAINTEXT_FALLBACK_OK_AFTER_STARTTLS_FAILURE) RETRY_AS_PLAINTEXT; - return (smtp_site_fail(state, DSN_BY_LOCAL_MTA, + return (smtp_misc_fail(state, state->tls->level == TLS_LEV_MAY ? + SMTP_NOTHROTTLE : SMTP_THROTTLE, + DSN_BY_LOCAL_MTA, SMTP_RESP_FAKE(&fake, "4.7.5"), "Cannot start TLS: handshake failure")); } diff --git a/postfix/src/smtp/smtp_trouble.c b/postfix/src/smtp/smtp_trouble.c index 2262e6cf0..014ea1ed6 100644 --- a/postfix/src/smtp/smtp_trouble.c +++ b/postfix/src/smtp/smtp_trouble.c @@ -32,6 +32,13 @@ /* SMTP_STATE *state; /* int exception; /* const char *description; +/* AUXILIARY FUNCTIONS +/* int smtp_misc_fail(state, throttle, mta_name, resp, format, ...) +/* SMTP_STATE *state; +/* int throttle; +/* const char *mta_name; +/* SMTP_RESP *resp; +/* const char *format; /* DESCRIPTION /* This module handles all non-fatal errors that can happen while /* attempting to deliver mail via SMTP, and implements the policy @@ -82,6 +89,13 @@ /* remaining recipients. /* The result is non-zero. /* +/* smtp_misc_fail() provides a more detailed interface than +/* smtp_site_fail() and smtp_mesg_fail(), which are convenience +/* wrappers around smtp_misc_fail(). The throttle argument +/* is either SMTP_THROTTLE or SMTP_NOTHROTTLE; it is used only +/* in the "soft error, final server" policy, and determines +/* whether a destination will be marked as problematic. +/* /* smtp_rcpt_fail() handles the case where a recipient is not /* accepted by the server for reasons other than that the server /* recipient limit is reached. @@ -162,9 +176,6 @@ #include "smtp.h" #include "smtp_sasl.h" -#define SMTP_THROTTLE 1 -#define SMTP_NOTHROTTLE 0 - /* smtp_check_code - check response code */ static void smtp_check_code(SMTP_SESSION *session, int code) @@ -310,10 +321,10 @@ static void vsmtp_fill_dsn(SMTP_STATE *state, const char *mta_name, reply ? DSB_DTYPE_SMTP : DSB_DTYPE_NONE, reply); } -/* smtp_site_fail - throttle this queue; skip, defer or bounce all recipients */ +/* smtp_misc_fail - maybe throttle queue; skip/defer/bounce all recipients */ -int smtp_site_fail(SMTP_STATE *state, const char *mta_name, SMTP_RESP *resp, - const char *format,...) +int smtp_misc_fail(SMTP_STATE *state, int throttle, const char *mta_name, + SMTP_RESP *resp, const char *format,...) { va_list ap; @@ -330,30 +341,7 @@ int smtp_site_fail(SMTP_STATE *state, const char *mta_name, SMTP_RESP *resp, /* * Skip, defer or bounce recipients, and throttle this queue. */ - return (smtp_bulk_fail(state, SMTP_THROTTLE)); -} - -/* smtp_mesg_fail - skip, defer or bounce all recipients; no queue throttle */ - -int smtp_mesg_fail(SMTP_STATE *state, const char *mta_name, SMTP_RESP *resp, - const char *format,...) -{ - va_list ap; - - /* - * Initialize. - */ - va_start(ap, format); - vsmtp_fill_dsn(state, mta_name, resp->dsn, resp->str, format, ap); - va_end(ap); - - if (state->session && mta_name) - smtp_check_code(state->session, resp->code); - - /* - * Skip, defer or bounce recipients, but don't throttle this queue. - */ - return (smtp_bulk_fail(state, SMTP_NOTHROTTLE)); + return (smtp_bulk_fail(state, throttle)); } /* smtp_rcpt_fail - skip, defer, or bounce recipient */ diff --git a/postfix/src/util/attr.h b/postfix/src/util/attr.h index 21e14e254..9fb79bd34 100644 --- a/postfix/src/util/attr.h +++ b/postfix/src/util/attr.h @@ -110,6 +110,7 @@ CHECK_VAL_HELPER_DCL(ATTR, ATTR_SCAN_SLAVE_FN); #define attr_vprint attr_vprint0 #define attr_scan attr_scan0 #define attr_vscan attr_vscan0 +#define attr_scan_more attr_scan_more0 /* * attr_print64.c. @@ -122,6 +123,7 @@ extern int attr_vprint64(VSTREAM *, int, va_list); */ extern int WARN_UNUSED_RESULT attr_scan64(VSTREAM *, int,...); extern int WARN_UNUSED_RESULT attr_vscan64(VSTREAM *, int, va_list); +extern int WARN_UNUSED_RESULT attr_scan_more64(VSTREAM *); /* * attr_print0.c. @@ -134,12 +136,14 @@ extern int attr_vprint0(VSTREAM *, int, va_list); */ extern int WARN_UNUSED_RESULT attr_scan0(VSTREAM *, int,...); extern int WARN_UNUSED_RESULT attr_vscan0(VSTREAM *, int, va_list); +extern int WARN_UNUSED_RESULT attr_scan_more0(VSTREAM *); /* * attr_scan_plain.c. */ extern int attr_print_plain(VSTREAM *, int,...); extern int attr_vprint_plain(VSTREAM *, int, va_list); +extern int attr_scan_more_plain(VSTREAM *); /* * attr_print_plain.c. @@ -147,7 +151,6 @@ extern int attr_vprint_plain(VSTREAM *, int, va_list); extern int WARN_UNUSED_RESULT attr_scan_plain(VSTREAM *, int,...); extern int WARN_UNUSED_RESULT attr_vscan_plain(VSTREAM *, int, va_list); - /* * Attribute names for testing the compatibility of the read and write * routines. diff --git a/postfix/src/util/attr_scan0.c b/postfix/src/util/attr_scan0.c index b1f301610..f465de917 100644 --- a/postfix/src/util/attr_scan0.c +++ b/postfix/src/util/attr_scan0.c @@ -7,15 +7,18 @@ /* #include /* /* int attr_scan0(fp, flags, type, name, ..., ATTR_TYPE_END) -/* VSTREAM fp; +/* VSTREAM *fp; /* int flags; /* int type; /* char *name; /* /* int attr_vscan0(fp, flags, ap) -/* VSTREAM fp; +/* VSTREAM *fp; /* int flags; /* va_list ap; +/* +/* int attr_scan_more0(fp) +/* VSTREAM *fp; /* DESCRIPTION /* attr_scan0() takes zero or more (name, value) request attributes /* and recovers the attribute values from the byte stream that was @@ -24,6 +27,11 @@ /* attr_vscan0() provides an alternative interface that is convenient /* for calling from within a variadic function. /* +/* attr_scan_more0() returns 0 when a terminator is found (and +/* consumes that terminator), returns 1 when more input is +/* expected (without consuming input), and returns -1 otherwise +/* (error). +/* /* The input stream is formatted as follows, where (item)* stands /* for zero or more instances of the specified item, and where /* (item1 | item2) stands for choice: @@ -144,6 +152,11 @@ /* 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. */ @@ -345,14 +358,14 @@ int attr_vscan0(VSTREAM *fp, int flags, va_list ap) /* * See if the caller asks for this attribute. */ - if (wanted_type == ATTR_TYPE_HASH + if (wanted_type == ATTR_TYPE_HASH && strcmp(ATTR_NAME_OPEN, STR(name_buf)) == 0) { wanted_type = ATTR_TYPE_CLOSE; wanted_name = "(any attribute name or '}')"; /* Advance in the input stream. */ continue; } else if (wanted_type == ATTR_TYPE_CLOSE - && strcmp(ATTR_NAME_CLOSE, STR(name_buf)) == 0) { + && strcmp(ATTR_NAME_CLOSE, STR(name_buf)) == 0) { /* Advance in the argument list. */ wanted_type = -1; break; @@ -450,6 +463,30 @@ int attr_scan0(VSTREAM *fp, int flags,...) return (ret); } +/* attr_scan_more0 - look ahead for more */ + +int attr_scan_more0(VSTREAM *fp) +{ + int ch; + + switch (ch = VSTREAM_GETC(fp)) { + case 0: + if (msg_verbose) + msg_info("%s: terminator (consumed)", VSTREAM_PATH(fp)); + return (0); + case VSTREAM_EOF: + if (msg_verbose) + msg_info("%s: EOF", VSTREAM_PATH(fp)); + return (-1); + default: + if (msg_verbose) + msg_info("%s: non-terminator '%c' (lookahead)", + VSTREAM_PATH(fp), ch); + (void) vstream_ungetc(fp, ch); + return (1); + } +} + #ifdef TEST /* diff --git a/postfix/src/util/attr_scan64.c b/postfix/src/util/attr_scan64.c index 0880d83ad..bb8ba3e6b 100644 --- a/postfix/src/util/attr_scan64.c +++ b/postfix/src/util/attr_scan64.c @@ -7,15 +7,18 @@ /* #include /* /* int attr_scan64(fp, flags, type, name, ..., ATTR_TYPE_END) -/* VSTREAM fp; +/* VSTREAM *fp; /* int flags; /* int type; /* char *name; /* /* int attr_vscan64(fp, flags, ap) -/* VSTREAM fp; +/* VSTREAM *fp; /* int flags; /* va_list ap; +/* +/* int attr_scan_more64(fp) +/* VSTREAM *fp; /* DESCRIPTION /* attr_scan64() takes zero or more (name, value) request attributes /* and recovers the attribute values from the byte stream that was @@ -24,6 +27,11 @@ /* attr_vscan64() provides an alternative interface that is convenient /* for calling from within a variadic function. /* +/* attr_scan_more64() returns 0 when a terminator is found +/* (and consumes that terminator), returns 1 when more input +/* is expected (without consuming input), and returns -1 +/* otherwise (error). +/* /* The input stream is formatted as follows, where (item)* stands /* for zero or more instances of the specified item, and where /* (item1 | item2) stands for choice: @@ -146,6 +154,11 @@ /* 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. */ @@ -348,14 +361,14 @@ int attr_vscan64(VSTREAM *fp, int flags, va_list ap) /* * See if the caller asks for this attribute. */ - if (wanted_type == ATTR_TYPE_HASH - && ch == '\n' && strcmp(ATTR_NAME_OPEN, STR(name_buf)) == 0) { + if (wanted_type == ATTR_TYPE_HASH + && ch == '\n' && strcmp(ATTR_NAME_OPEN, STR(name_buf)) == 0) { wanted_type = ATTR_TYPE_CLOSE; wanted_name = "(any attribute name or '}')"; /* Advance in the input stream. */ continue; } else if (wanted_type == ATTR_TYPE_CLOSE - && ch == '\n' && strcmp(ATTR_NAME_CLOSE, STR(name_buf)) == 0) { + && ch == '\n' && strcmp(ATTR_NAME_CLOSE, STR(name_buf)) == 0) { /* Advance in the argument list. */ wanted_type = -1; break; @@ -509,6 +522,30 @@ int attr_scan64(VSTREAM *fp, int flags,...) return (ret); } +/* attr_scan_more64 - look ahead for more */ + +int attr_scan_more64(VSTREAM *fp) +{ + int ch; + + switch (ch = VSTREAM_GETC(fp)) { + case '\n': + if (msg_verbose) + msg_info("%s: terminator (consumed)", VSTREAM_PATH(fp)); + return (0); + case VSTREAM_EOF: + if (msg_verbose) + msg_info("%s: EOF", VSTREAM_PATH(fp)); + return (-1); + default: + if (msg_verbose) + msg_info("%s: non-terminator '%c' (lookahead)", + VSTREAM_PATH(fp), ch); + (void) vstream_ungetc(fp, ch); + return (1); + } +} + #ifdef TEST /* diff --git a/postfix/src/util/attr_scan_plain.c b/postfix/src/util/attr_scan_plain.c index fed76cddd..97499d320 100644 --- a/postfix/src/util/attr_scan_plain.c +++ b/postfix/src/util/attr_scan_plain.c @@ -7,15 +7,18 @@ /* #include /* /* int attr_scan_plain(fp, flags, type, name, ..., ATTR_TYPE_END) -/* VSTREAM fp; +/* VSTREAM *fp; /* int flags; /* int type; /* char *name; /* /* int attr_vscan_plain(fp, flags, ap) -/* VSTREAM fp; +/* VSTREAM *fp; /* int flags; /* va_list ap; +/* +/* int attr_scan_more_plain(fp) +/* VSTREAM *fp; /* DESCRIPTION /* attr_scan_plain() takes zero or more (name, value) request attributes /* and recovers the attribute values from the byte stream that was @@ -24,6 +27,11 @@ /* attr_vscan_plain() provides an alternative interface that is convenient /* for calling from within a variadic function. /* +/* attr_scan_more_plain() returns 0 when a terminator is found +/* (and consumes that terminator), returns 1 when more input +/* is expected (without consuming input), and returns -1 +/* otherwise (error). +/* /* The input stream is formatted as follows, where (item)* stands /* for zero or more instances of the specified item, and where /* (item1 | item2) stands for choice: @@ -144,6 +152,11 @@ /* 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. */ @@ -361,14 +374,14 @@ int attr_vscan_plain(VSTREAM *fp, int flags, va_list ap) /* * See if the caller asks for this attribute. */ - if (wanted_type == ATTR_TYPE_HASH - && ch == '\n' && strcmp(ATTR_NAME_OPEN, STR(name_buf)) == 0) { + if (wanted_type == ATTR_TYPE_HASH + && ch == '\n' && strcmp(ATTR_NAME_OPEN, STR(name_buf)) == 0) { wanted_type = ATTR_TYPE_CLOSE; wanted_name = "(any attribute name or '}')"; /* Advance in the input stream. */ continue; } else if (wanted_type == ATTR_TYPE_CLOSE - && ch == '\n' && strcmp(ATTR_NAME_CLOSE, STR(name_buf)) == 0) { + && ch == '\n' && strcmp(ATTR_NAME_CLOSE, STR(name_buf)) == 0) { /* Advance in the argument list. */ wanted_type = -1; break; @@ -492,6 +505,30 @@ int attr_scan_plain(VSTREAM *fp, int flags,...) return (ret); } +/* attr_scan_more_plain - look ahead for more */ + +int attr_scan_more_plain(VSTREAM *fp) +{ + int ch; + + switch (ch = VSTREAM_GETC(fp)) { + case '\n': + if (msg_verbose) + msg_info("%s: terminator (consumed)", VSTREAM_PATH(fp)); + return (0); + case VSTREAM_EOF: + if (msg_verbose) + msg_info("%s: EOF", VSTREAM_PATH(fp)); + return (-1); + default: + if (msg_verbose) + msg_info("%s: non-terminator '%c' (lookahead)", + VSTREAM_PATH(fp), ch); + (void) vstream_ungetc(fp, ch); + return (1); + } +} + #ifdef TEST /* diff --git a/postfix/src/util/vstream.c b/postfix/src/util/vstream.c index 4f8c185f0..86a96aaf7 100644 --- a/postfix/src/util/vstream.c +++ b/postfix/src/util/vstream.c @@ -399,7 +399,8 @@ /* This is an alias for vstream_bufstat(stream, VSTREAM_BST_IN_PEND). /* /* vstream_peek_data() returns a pointer to the unread bytes -/* that exist according to vstream_peek(). +/* that exist according to vstream_peek(), or null if no unread +/* bytes are available. /* /* vstream_setjmp() saves processing context and makes that context /* available for use with vstream_longjmp(). Normally, vstream_setjmp()