diff options
Diffstat (limited to 'src/postqueue/showq_json.c')
-rw-r--r-- | src/postqueue/showq_json.c | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/src/postqueue/showq_json.c b/src/postqueue/showq_json.c new file mode 100644 index 0000000..e9d4fb5 --- /dev/null +++ b/src/postqueue/showq_json.c @@ -0,0 +1,219 @@ +/*++ +/* 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, + 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, + 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"); +} |