summaryrefslogtreecommitdiffstats
path: root/src/postqueue/showq_json.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/postqueue/showq_json.c')
-rw-r--r--src/postqueue/showq_json.c221
1 files changed, 221 insertions, 0 deletions
diff --git a/src/postqueue/showq_json.c b/src/postqueue/showq_json.c
new file mode 100644
index 0000000..fc205c7
--- /dev/null
+++ b/src/postqueue/showq_json.c
@@ -0,0 +1,221 @@
+/*++
+/* 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 <sys_defs.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sysexits.h>
+#include <ctype.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+#include <vstream.h>
+#include <stringops.h>
+#include <mymalloc.h>
+#include <msg.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+#include <mail_queue.h>
+#include <mail_date.h>
+#include <mail_params.h>
+
+/* Application-specific. */
+
+#include <postqueue.h>
+
+#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;
+ int forced_expire;
+
+ /*
+ * 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
+ | ATTR_FLAG_PRINTABLE,
+ 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_INT(MAIL_ATTR_FORCED_EXPIRE, &forced_expire),
+ RECV_ATTR_STR(MAIL_ATTR_SENDER, addr),
+ ATTR_TYPE_END) != 6)
+ 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("\"forced_expire\": %s, ", forced_expire ? "true" : "false");
+ 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
+ | ATTR_FLAG_PRINTABLE,
+ 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");
+ if (vstream_fflush(VSTREAM_OUT) && errno != EPIPE)
+ msg_fatal_status(EX_IOERR, "output write error: %m");
+}
+
+/* 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
+ && vstream_ferror(VSTREAM_OUT) == 0) {
+ format_json(showq_stream);
+ }
+ if (showq_status < 0)
+ msg_fatal_status(EX_SOFTWARE, "malformed showq server response");
+}