1 /* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "dict.h"
5 #include "doveadm.h"
6 #include "doveadm-print.h"
7 
8 #include <stdio.h>
9 #include <unistd.h>
10 
11 static int
cmd_dict_init_full(struct doveadm_cmd_context * cctx,doveadm_command_ver2_t * cmd ATTR_UNUSED,enum dict_iterate_flags * iter_flags,struct dict ** dict_r,struct dict_op_settings * dopset_r)12 cmd_dict_init_full(struct doveadm_cmd_context *cctx,
13 		   doveadm_command_ver2_t *cmd ATTR_UNUSED, enum dict_iterate_flags *iter_flags,
14 		   struct dict **dict_r, struct dict_op_settings *dopset_r)
15 {
16 	struct dict_settings dict_set;
17 	struct dict *dict;
18 	bool set = FALSE;
19 	const char *dict_uri, *error, *key, *username = "";
20 	i_zero(dopset_r);
21 
22 	if (doveadm_cmd_param_bool(cctx, "exact", &set) && set)
23 		*iter_flags |= DICT_ITERATE_FLAG_EXACT_KEY;
24 	if (doveadm_cmd_param_bool(cctx, "recurse", &set) && set)
25 		*iter_flags |= DICT_ITERATE_FLAG_RECURSE;
26 	if (doveadm_cmd_param_bool(cctx, "no-value", &set) && set)
27 		*iter_flags |= DICT_ITERATE_FLAG_NO_VALUE;
28 	(void)doveadm_cmd_param_str(cctx, "user", &username);
29 	dopset_r->username = username;
30 
31 	if (!doveadm_cmd_param_str(cctx, "dict-uri", &dict_uri)) {
32 		i_error("dictionary URI must be specified");
33 		doveadm_exit_code = EX_USAGE;
34 		return -1;
35 	}
36 
37 	if (!doveadm_cmd_param_str(cctx, "prefix", &key) &&
38 	    !doveadm_cmd_param_str(cctx, "key", &key))
39 		key = "";
40 
41 	if (!str_begins(key, DICT_PATH_PRIVATE) &&
42 	    !str_begins(key, DICT_PATH_SHARED)) {
43 		i_error("Key must begin with '"DICT_PATH_PRIVATE
44 			"' or '"DICT_PATH_SHARED"': %s", key);
45 		doveadm_exit_code = EX_USAGE;
46 		return -1;
47 	}
48 	if (username[0] == '\0' &&
49 	    str_begins(key, DICT_PATH_PRIVATE)) {
50 		i_error("-u must be specified for "DICT_PATH_PRIVATE" keys");
51 		doveadm_exit_code = EX_USAGE;
52 		return -1;
53 	}
54 
55 	dict_drivers_register_builtin();
56 	i_zero(&dict_set);
57 	dict_set.base_dir = doveadm_settings->base_dir;
58 	if (dict_init(dict_uri, &dict_set, &dict, &error) < 0) {
59 		i_error("dict_init(%s) failed: %s", dict_uri, error);
60 		doveadm_exit_code = EX_TEMPFAIL;
61 		return -1;
62 	}
63 	*dict_r = dict;
64 	return 0;
65 }
66 
67 static int
cmd_dict_init(struct doveadm_cmd_context * cctx,doveadm_command_ver2_t * cmd,struct dict ** dict_r,struct dict_op_settings * set_r)68 cmd_dict_init(struct doveadm_cmd_context *cctx,
69 	      doveadm_command_ver2_t *cmd, struct dict **dict_r,
70 	      struct dict_op_settings *set_r)
71 {
72 	enum dict_iterate_flags iter_flags = 0;
73 	return cmd_dict_init_full(cctx, cmd, &iter_flags, dict_r, set_r);
74 }
75 
76 struct doveadm_dict_ctx {
77 	pool_t pool;
78 	int ret;
79 	const char *const *values;
80 	const char *error;
81 };
82 
dict_lookup_callback(const struct dict_lookup_result * result,struct doveadm_dict_ctx * ctx)83 static void dict_lookup_callback(const struct dict_lookup_result *result,
84 				 struct doveadm_dict_ctx *ctx)
85 {
86 	ctx->ret = result->ret;
87 	ctx->values = result->values == NULL ? NULL :
88 		p_strarray_dup(ctx->pool, result->values);
89 	ctx->error = p_strdup(ctx->pool, result->error);
90 }
91 
cmd_dict_get(struct doveadm_cmd_context * cctx)92 static void cmd_dict_get(struct doveadm_cmd_context *cctx)
93 {
94 	struct doveadm_dict_ctx ctx;
95 	struct dict *dict;
96 	const char *key;
97 	struct dict_op_settings set;
98 
99 	if (!doveadm_cmd_param_str(cctx, "key", &key)) {
100 		i_error("dict-get: Missing key");
101 		doveadm_exit_code = EX_USAGE;
102 		return;
103 	}
104 
105 	if (cmd_dict_init(cctx, cmd_dict_get, &dict, &set) < 0)
106 		return;
107 
108 	doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE);
109 	doveadm_print_header("value", "", DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE);
110 
111 	i_zero(&ctx);
112 	ctx.pool = pool_alloconly_create("doveadm dict lookup", 512);
113 	ctx.ret = -2;
114 	dict_lookup_async(dict, &set, key, dict_lookup_callback, &ctx);
115 	while (ctx.ret == -2)
116 		dict_wait(dict);
117 	if (ctx.ret < 0) {
118 		i_error("dict_lookup(%s) failed: %s", key, ctx.error);
119 		doveadm_exit_code = EX_TEMPFAIL;
120 	} else if (ctx.ret == 0) {
121 		i_error("%s doesn't exist", key);
122 		doveadm_exit_code = DOVEADM_EX_NOTFOUND;
123 	} else {
124 		unsigned int i, values_count = str_array_length(ctx.values);
125 
126 		for (i = 1; i < values_count; i++)
127 			doveadm_print_header("value", "", DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE);
128 		for (i = 0; i < values_count; i++)
129 			doveadm_print(ctx.values[i]);
130 	}
131 	pool_unref(&ctx.pool);
132 	dict_deinit(&dict);
133 }
134 
cmd_dict_set(struct doveadm_cmd_context * cctx)135 static void cmd_dict_set(struct doveadm_cmd_context *cctx)
136 {
137 	struct dict *dict;
138 	struct dict_transaction_context *trans;
139 	const char *error;
140 	const char *key, *value = "";
141 	struct dict_op_settings set;
142 
143 	if (!doveadm_cmd_param_str(cctx, "key", &key) ||
144 	    !doveadm_cmd_param_str(cctx, "value", &value)) {
145 		i_error("dict set: Missing parameters");
146 		doveadm_exit_code = EX_USAGE;
147 		return;
148 	}
149 
150 	if (cmd_dict_init(cctx, cmd_dict_set, &dict, &set) < 0)
151 		return;
152 
153 	trans = dict_transaction_begin(dict, &set);
154 	dict_set(trans, key, value);
155 	if (dict_transaction_commit(&trans, &error) <= 0) {
156 		i_error("dict_transaction_commit() failed: %s", error);
157 		doveadm_exit_code = EX_TEMPFAIL;
158 	}
159 	dict_deinit(&dict);
160 }
161 
cmd_dict_unset(struct doveadm_cmd_context * cctx)162 static void cmd_dict_unset(struct doveadm_cmd_context *cctx)
163 {
164 	struct dict *dict;
165 	struct dict_transaction_context *trans;
166 	const char *error;
167 	const char *key;
168 	struct dict_op_settings set;
169 
170 	if (!doveadm_cmd_param_str(cctx, "key", &key)) {
171 		i_error("dict unset: Missing key");
172 		doveadm_exit_code = EX_USAGE;
173 		return;
174 	}
175 
176 	if (cmd_dict_init(cctx, cmd_dict_unset, &dict, &set) < 0)
177 		return;
178 
179 	trans = dict_transaction_begin(dict, &set);
180 	dict_unset(trans, key);
181 	if (dict_transaction_commit(&trans, &error) <= 0) {
182 		i_error("dict_transaction_commit() failed: %s", error);
183 		doveadm_exit_code = EX_TEMPFAIL;
184 	}
185 	dict_deinit(&dict);
186 }
187 
cmd_dict_inc(struct doveadm_cmd_context * cctx)188 static void cmd_dict_inc(struct doveadm_cmd_context *cctx)
189 {
190 	struct dict *dict;
191 	struct dict_transaction_context *trans;
192 	const char *error;
193 	const char *key;
194 	int64_t diff;
195 	int ret;
196 	struct dict_op_settings set;
197 
198 	if (!doveadm_cmd_param_str(cctx, "key", &key) ||
199 	    !doveadm_cmd_param_int64(cctx, "difference", &diff)) {
200 		i_error("dict-inc: Missing parameters");
201 		doveadm_exit_code = EX_USAGE;
202 		return;
203 	}
204 
205 	if (cmd_dict_init(cctx, cmd_dict_inc, &dict, &set) < 0)
206 		return;
207 
208 	trans = dict_transaction_begin(dict, &set);
209 	dict_atomic_inc(trans, key, diff);
210 	ret = dict_transaction_commit(&trans, &error);
211 	if (ret < 0) {
212 		i_error("dict_transaction_commit() failed: %s", error);
213 		doveadm_exit_code = EX_TEMPFAIL;
214 	} else if (ret == 0) {
215 		i_error("%s doesn't exist", key);
216 		doveadm_exit_code = DOVEADM_EX_NOTFOUND;
217 	}
218 	dict_deinit(&dict);
219 }
220 
cmd_dict_iter(struct doveadm_cmd_context * cctx)221 static void cmd_dict_iter(struct doveadm_cmd_context *cctx)
222 {
223 	struct dict *dict;
224 	struct dict_iterate_context *iter;
225 	enum dict_iterate_flags iter_flags = 0;
226 	const char *prefix, *key, *const *values, *error;
227 	bool header_printed = FALSE;
228 	struct dict_op_settings set;
229 
230 	if (!doveadm_cmd_param_str(cctx, "prefix", &prefix)) {
231 		i_error("dict-iter: Missing prefix");
232 		doveadm_exit_code = EX_USAGE;
233 		return;
234 	}
235 
236 	if (cmd_dict_init_full(cctx, cmd_dict_iter, &iter_flags, &dict, &set) < 0)
237 		return;
238 
239 	doveadm_print_init(DOVEADM_PRINT_TYPE_TAB);
240 	doveadm_print_header_simple("key");
241 	if ((iter_flags & DICT_ITERATE_FLAG_NO_VALUE) == 0)
242 		doveadm_print_header_simple("value");
243 
244 	iter = dict_iterate_init(dict, &set, prefix, iter_flags);
245 	while (dict_iterate_values(iter, &key, &values)) {
246 		unsigned int values_count = str_array_length(values);
247 		if (!header_printed) {
248 			for (unsigned int i = 1; i < values_count; i++)
249 				doveadm_print_header_simple("value");
250 			header_printed = TRUE;
251 		}
252 		doveadm_print(key);
253 		if ((iter_flags & DICT_ITERATE_FLAG_NO_VALUE) == 0) {
254 			for (unsigned int i = 0; i < values_count; i++)
255 				doveadm_print(values[i]);
256 		}
257 	}
258 	if (dict_iterate_deinit(&iter, &error) < 0) {
259 		i_error("dict_iterate_deinit(%s) failed: %s", prefix, error);
260 		doveadm_exit_code = EX_TEMPFAIL;
261 	}
262 	dict_deinit(&dict);
263 }
264 
265 static struct doveadm_cmd_ver2 doveadm_cmd_dict[] = {
266 {
267 	.name = "dict get",
268 	.cmd = cmd_dict_get,
269 	.usage = "[-u <user>] <dict uri> <key>",
270 DOVEADM_CMD_PARAMS_START
271 DOVEADM_CMD_PARAM('u', "user", CMD_PARAM_STR, 0)
272 DOVEADM_CMD_PARAM('\0', "dict-uri", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL)
273 DOVEADM_CMD_PARAM('\0', "key", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL)
274 DOVEADM_CMD_PARAMS_END
275 },
276 {
277 	.name = "dict set",
278 	.cmd = cmd_dict_set,
279 	.usage = "[-u <user>] <dict uri> <key> <value>",
280 DOVEADM_CMD_PARAMS_START
281 DOVEADM_CMD_PARAM('u', "user", CMD_PARAM_STR, 0)
282 DOVEADM_CMD_PARAM('\0', "dict-uri", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL)
283 DOVEADM_CMD_PARAM('\0', "key", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL)
284 DOVEADM_CMD_PARAM('\0', "value", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL)
285 DOVEADM_CMD_PARAMS_END
286 },
287 {
288 	.name = "dict unset",
289 	.cmd = cmd_dict_unset,
290 	.usage = "[-u <user>] <dict uri> <key>",
291 DOVEADM_CMD_PARAMS_START
292 DOVEADM_CMD_PARAM('u', "user", CMD_PARAM_STR, 0)
293 DOVEADM_CMD_PARAM('\0', "dict-uri", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL)
294 DOVEADM_CMD_PARAM('\0', "key", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL)
295 DOVEADM_CMD_PARAMS_END
296 },
297 {
298 	.name = "dict inc",
299 	.cmd = cmd_dict_inc,
300 	.usage = "[-u <user>] <dict uri> <key> <diff>",
301 DOVEADM_CMD_PARAMS_START
302 DOVEADM_CMD_PARAM('u', "user", CMD_PARAM_STR, 0)
303 DOVEADM_CMD_PARAM('\0', "dict-uri", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL)
304 DOVEADM_CMD_PARAM('\0', "key", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL)
305 DOVEADM_CMD_PARAM('\0', "difference", CMD_PARAM_INT64, CMD_PARAM_FLAG_POSITIONAL)
306 DOVEADM_CMD_PARAMS_END
307 },
308 {
309 	.name = "dict iter",
310 	.cmd = cmd_dict_iter,
311 	.usage = "[-u <user>] [-1RV] <dict uri> <prefix>",
312 DOVEADM_CMD_PARAMS_START
313 DOVEADM_CMD_PARAM('u', "user", CMD_PARAM_STR, 0)
314 DOVEADM_CMD_PARAM('1', "exact", CMD_PARAM_BOOL, 0)
315 DOVEADM_CMD_PARAM('R', "recurse", CMD_PARAM_BOOL, 0)
316 DOVEADM_CMD_PARAM('V', "no-value", CMD_PARAM_BOOL, 0)
317 DOVEADM_CMD_PARAM('\0', "dict-uri", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL)
318 DOVEADM_CMD_PARAM('\0', "prefix", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL)
319 DOVEADM_CMD_PARAMS_END
320 }
321 };
322 
doveadm_register_dict_commands(void)323 void doveadm_register_dict_commands(void)
324 {
325 	unsigned int i;
326 
327 	for (i = 0; i < N_ELEMENTS(doveadm_cmd_dict); i++)
328 		doveadm_cmd_register_ver2(&doveadm_cmd_dict[i]);
329 }
330