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