xref: /netbsd-src/external/ibm-public/postfix/dist/src/postqueue/showq_json.c (revision e89934bbf778a6d6d6894877c4da59d0c7835b0f)
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