/*++ /* 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 #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; 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"); }