1 /* $NetBSD: showq_json.c,v 1.2 2017/02/14 01:16:47 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* showq_json 8 6 /* SUMMARY 7 /* JSON queue status formatter 8 /* SYNOPSIS 9 /* void showq_json( 10 /* VSTREAM *showq) 11 /* DESCRIPTION 12 /* This function converts showq(8) daemon output to JSON format. 13 /* DIAGNOSTICS 14 /* Fatal errors: out of memory, malformed showq(8) daemon output. 15 /* LICENSE 16 /* .ad 17 /* .fi 18 /* The Secure Mailer license must be distributed with this software. 19 /* AUTHOR(S) 20 /* Wietse Venema 21 /* IBM T.J. Watson Research 22 /* P.O. Box 704 23 /* Yorktown Heights, NY 10598, USA 24 /* 25 /* Wietse Venema 26 /* Google, Inc. 27 /* 111 8th Avenue 28 /* New York, NY 10011, USA 29 /*--*/ 30 31 /* System library. */ 32 33 #include <sys_defs.h> 34 #include <stdlib.h> 35 #include <unistd.h> 36 #include <string.h> 37 #include <sysexits.h> 38 #include <ctype.h> 39 40 /* Utility library. */ 41 42 #include <vstring.h> 43 #include <vstream.h> 44 #include <stringops.h> 45 #include <mymalloc.h> 46 #include <msg.h> 47 48 /* Global library. */ 49 50 #include <mail_proto.h> 51 #include <mail_queue.h> 52 #include <mail_date.h> 53 #include <mail_params.h> 54 55 /* Application-specific. */ 56 57 #include <postqueue.h> 58 59 #define STR(x) vstring_str(x) 60 #define LEN(x) VSTRING_LEN(x) 61 62 /* json_quote - quote JSON string */ 63 64 static char *json_quote(VSTRING *result, const char *text) 65 { 66 unsigned char *cp; 67 int ch; 68 69 /* 70 * We use short escape sequences for common control characters. Note that 71 * RFC 4627 allows "/" (0x2F) to be sent without quoting. Differences 72 * with RFC 4627: we send DEL (0x7f) as \u007F; the result remains RFC 73 * 4627 complaint. 74 */ 75 VSTRING_RESET(result); 76 for (cp = (unsigned char *) text; (ch = *cp) != 0; cp++) { 77 if (UNEXPECTED(ISCNTRL(ch))) { 78 switch (ch) { 79 case '\b': 80 VSTRING_ADDCH(result, '\\'); 81 VSTRING_ADDCH(result, 'b'); 82 break; 83 case '\f': 84 VSTRING_ADDCH(result, '\\'); 85 VSTRING_ADDCH(result, 'f'); 86 break; 87 case '\n': 88 VSTRING_ADDCH(result, '\\'); 89 VSTRING_ADDCH(result, 'n'); 90 break; 91 case '\r': 92 VSTRING_ADDCH(result, '\\'); 93 VSTRING_ADDCH(result, 'r'); 94 break; 95 case '\t': 96 VSTRING_ADDCH(result, '\\'); 97 VSTRING_ADDCH(result, 't'); 98 break; 99 default: 100 vstring_sprintf(result, "\\u%04X", ch); 101 break; 102 } 103 } else { 104 switch (ch) { 105 case '\\': 106 case '"': 107 VSTRING_ADDCH(result, '\\'); 108 /* FALLTHROUGH */ 109 default: 110 VSTRING_ADDCH(result, ch); 111 break; 112 } 113 } 114 } 115 VSTRING_TERMINATE(result); 116 117 /* 118 * Force the result to be UTF-8 (with SMTPUTF8 enabled) or ASCII (with 119 * SMTPUTF8 disabled). 120 */ 121 printable(STR(result), '?'); 122 return (STR(result)); 123 } 124 125 /* json_message - report status for one message */ 126 127 static void format_json(VSTREAM *showq_stream) 128 { 129 static VSTRING *queue_name = 0; 130 static VSTRING *queue_id = 0; 131 static VSTRING *addr = 0; 132 static VSTRING *why = 0; 133 static VSTRING *quote_buf = 0; 134 long arrival_time; 135 long message_size; 136 int showq_status; 137 int rcpt_count = 0; 138 139 /* 140 * One-time initialization. 141 */ 142 if (queue_name == 0) { 143 queue_name = vstring_alloc(100); 144 queue_id = vstring_alloc(100); 145 addr = vstring_alloc(100); 146 why = vstring_alloc(100); 147 quote_buf = vstring_alloc(100); 148 } 149 150 /* 151 * Read the message properties and sender address. 152 */ 153 if (attr_scan(showq_stream, ATTR_FLAG_MORE | ATTR_FLAG_STRICT, 154 RECV_ATTR_STR(MAIL_ATTR_QUEUE, queue_name), 155 RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id), 156 RECV_ATTR_LONG(MAIL_ATTR_TIME, &arrival_time), 157 RECV_ATTR_LONG(MAIL_ATTR_SIZE, &message_size), 158 RECV_ATTR_STR(MAIL_ATTR_SENDER, addr), 159 ATTR_TYPE_END) != 5) 160 msg_fatal_status(EX_SOFTWARE, "malformed showq server response"); 161 vstream_printf("{"); 162 vstream_printf("\"queue_name\": \"%s\", ", 163 json_quote(quote_buf, STR(queue_name))); 164 vstream_printf("\"queue_id\": \"%s\", ", 165 json_quote(quote_buf, STR(queue_id))); 166 vstream_printf("\"arrival_time\": %ld, ", arrival_time); 167 vstream_printf("\"message_size\": %ld, ", message_size); 168 vstream_printf("\"sender\": \"%s\", ", 169 json_quote(quote_buf, STR(addr))); 170 171 /* 172 Read zero or more (recipient, reason) pair(s) until attr_scan_more() 173 * consumes a terminator. If the showq daemon messes up, don't try to 174 * resynchronize. 175 */ 176 vstream_printf("\"recipients\": ["); 177 for (rcpt_count = 0; (showq_status = attr_scan_more(showq_stream)) > 0; rcpt_count++) { 178 if (rcpt_count > 0) 179 vstream_printf(", "); 180 vstream_printf("{"); 181 if (attr_scan(showq_stream, ATTR_FLAG_MORE | ATTR_FLAG_STRICT, 182 RECV_ATTR_STR(MAIL_ATTR_RECIP, addr), 183 RECV_ATTR_STR(MAIL_ATTR_WHY, why), 184 ATTR_TYPE_END) != 2) 185 msg_fatal_status(EX_SOFTWARE, "malformed showq server response"); 186 vstream_printf("\"address\": \"%s\"", 187 json_quote(quote_buf, STR(addr))); 188 if (LEN(why) > 0) 189 vstream_printf(", \"delay_reason\": \"%s\"", 190 json_quote(quote_buf, STR(why))); 191 vstream_printf("}"); 192 } 193 vstream_printf("]"); 194 if (showq_status < 0) 195 msg_fatal_status(EX_SOFTWARE, "malformed showq server response"); 196 vstream_printf("}\n"); 197 vstream_fflush(VSTREAM_OUT); 198 } 199 200 /* showq_json - streaming JSON-format output adapter */ 201 202 void showq_json(VSTREAM *showq_stream) 203 { 204 int showq_status; 205 206 /* 207 * Emit zero or more queue file objects until attr_scan_more() 208 * consumes a terminator. 209 */ 210 while ((showq_status = attr_scan_more(showq_stream)) > 0) { 211 format_json(showq_stream); 212 } 213 if (showq_status < 0) 214 msg_fatal_status(EX_SOFTWARE, "malformed showq server response"); 215 } 216