1 /* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "array.h"
5 #include "istream.h"
6 #include "ostream.h"
7 #include "doveadm-print-private.h"
8 
9 struct doveadm_print_header_context {
10 	const char *key;
11 	char *sticky_value;
12 	bool sticky;
13 };
14 
15 struct doveadm_print_context {
16 	pool_t pool;
17 	ARRAY(struct doveadm_print_header_context) headers;
18 	const struct doveadm_print_vfuncs *v;
19 
20 	unsigned int header_idx;
21 	bool print_stream_open;
22 };
23 
24 bool doveadm_print_hide_titles = FALSE;
25 struct ostream *doveadm_print_ostream = NULL;
26 
27 static struct doveadm_print_context *ctx;
28 
doveadm_print_is_initialized(void)29 bool doveadm_print_is_initialized(void)
30 {
31 	return ctx != NULL;
32 }
33 
doveadm_print_header(const char * key,const char * title,enum doveadm_print_header_flags flags)34 void doveadm_print_header(const char *key, const char *title,
35 			  enum doveadm_print_header_flags flags)
36 {
37 	struct doveadm_print_header hdr;
38 	struct doveadm_print_header_context *hdr_ctx;
39 
40 	i_assert(title != NULL);
41 
42 	i_zero(&hdr);
43 	hdr.key = key;
44 	hdr.title = title;
45 	hdr.flags = flags;
46 	ctx->v->header(&hdr);
47 
48 	hdr_ctx = array_append_space(&ctx->headers);
49 	hdr_ctx->key = p_strdup(ctx->pool, key);
50 	hdr_ctx->sticky = (flags & DOVEADM_PRINT_HEADER_FLAG_STICKY) != 0;
51 }
52 
doveadm_print_header_simple(const char * key_title)53 void doveadm_print_header_simple(const char *key_title)
54 {
55 	doveadm_print_header(key_title, key_title, 0);
56 }
57 
doveadm_print_get_headers_count(void)58 unsigned int doveadm_print_get_headers_count(void)
59 {
60 	return array_count(&ctx->headers);
61 }
62 
doveadm_print_sticky_headers(void)63 static void doveadm_print_sticky_headers(void)
64 {
65 	const struct doveadm_print_header_context *headers;
66 	unsigned int count;
67 
68 	headers = array_get(&ctx->headers, &count);
69 	i_assert(count > 0);
70 	for (;;) {
71 		if (ctx->header_idx == count)
72 			ctx->header_idx = 0;
73 		else if (headers[ctx->header_idx].sticky) {
74 			ctx->v->print(headers[ctx->header_idx].sticky_value);
75 			ctx->header_idx++;
76 		} else {
77 			break;
78 		}
79 	}
80 }
81 
doveadm_print(const char * value)82 void doveadm_print(const char *value)
83 {
84 	i_assert(!ctx->print_stream_open);
85 
86 	doveadm_print_sticky_headers();
87 	ctx->v->print(value);
88 	ctx->header_idx++;
89 }
90 
doveadm_print_num(uintmax_t value)91 void doveadm_print_num(uintmax_t value)
92 {
93 	T_BEGIN {
94 		doveadm_print(dec2str(value));
95 	} T_END;
96 }
97 
doveadm_print_stream(const void * value,size_t size)98 void doveadm_print_stream(const void *value, size_t size)
99 {
100 	if (!ctx->print_stream_open) {
101 		doveadm_print_sticky_headers();
102 		ctx->print_stream_open = TRUE;
103 	}
104 	ctx->v->print_stream(value, size);
105 	if (size == 0) {
106 		ctx->header_idx++;
107 		ctx->print_stream_open = FALSE;
108 	}
109 }
110 
doveadm_print_istream(struct istream * input)111 int doveadm_print_istream(struct istream *input)
112 {
113 	const unsigned char *data;
114 	size_t size;
115 	ssize_t ret;
116 
117 	while ((ret = i_stream_read_more(input, &data, &size)) > 0) {
118 		doveadm_print_stream(data, size);
119 		i_stream_skip(input, size);
120 	}
121 	i_assert(ret == -1);
122 	doveadm_print_stream("", 0);
123 	if (input->stream_errno != 0) {
124 		/* caller will log the error */
125 		return -1;
126 	}
127 	return 0;
128 }
129 
doveadm_print_sticky(const char * key,const char * value)130 void doveadm_print_sticky(const char *key, const char *value)
131 {
132 	struct doveadm_print_header_context *hdr;
133 
134 	if (ctx == NULL) {
135 		/* command doesn't really print anything */
136 		return;
137 	}
138 
139 	array_foreach_modifiable(&ctx->headers, hdr) {
140 		if (strcmp(hdr->key, key) == 0) {
141 			i_free(hdr->sticky_value);
142 			hdr->sticky_value = i_strdup(value);
143 			return;
144 		}
145 	}
146 	i_unreached();
147 }
148 
doveadm_print_flush(void)149 void doveadm_print_flush(void)
150 {
151 	if (ctx != NULL && ctx->v->flush != NULL)
152 		ctx->v->flush();
153 	o_stream_uncork(doveadm_print_ostream);
154 	o_stream_cork(doveadm_print_ostream);
155 }
156 
doveadm_print_unstick_headers(void)157 void doveadm_print_unstick_headers(void)
158 {
159 	struct doveadm_print_header_context *hdr;
160 
161 	if (ctx != NULL) {
162 		array_foreach_modifiable(&ctx->headers, hdr)
163 			hdr->sticky = FALSE;
164 	}
165 }
166 
doveadm_print_init(const char * name)167 void doveadm_print_init(const char *name)
168 {
169 	pool_t pool;
170 	unsigned int i;
171 
172 	if (ctx != NULL) {
173 		/* already forced the type */
174 		return;
175 	}
176 
177 	pool = pool_alloconly_create("doveadm print", 1024);
178 	ctx = p_new(pool, struct doveadm_print_context, 1);
179 	ctx->pool = pool;
180 	p_array_init(&ctx->headers, pool, 16);
181 
182 	for (i = 0; doveadm_print_vfuncs_all[i] != NULL; i++) {
183 		if (strcmp(doveadm_print_vfuncs_all[i]->name, name) == 0) {
184 			ctx->v = doveadm_print_vfuncs_all[i];
185 			break;
186 		}
187 	}
188 	if (ctx->v == NULL)
189 		i_fatal("Unknown print formatter: %s", name);
190 	if (ctx->v->init != NULL)
191 		ctx->v->init();
192 }
193 
doveadm_print_deinit(void)194 void doveadm_print_deinit(void)
195 {
196 	struct doveadm_print_header_context *hdr;
197 
198 	if (ctx == NULL)
199 		return;
200 
201 	if (ctx->v->flush != NULL && doveadm_print_ostream != NULL) {
202 		ctx->v->flush();
203 		o_stream_uncork(doveadm_print_ostream);
204 	}
205 	if (ctx->v->deinit != NULL)
206 		ctx->v->deinit();
207 	array_foreach_modifiable(&ctx->headers, hdr)
208 		i_free(hdr->sticky_value);
209 	pool_unref(&ctx->pool);
210 	ctx = NULL;
211 }
212