#include "lib.h"
#include "array.h"
+#include "istream.h"
+#include "str.h"
#include "doveadm-cmd.h"
+#include "doveadm.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
static struct doveadm_cmd *doveadm_commands[] = {
&doveadm_cmd_stop,
&doveadm_cmd_stats_dump
};
+static struct doveadm_cmd_ver2 *doveadm_commands_ver2[] = {
+};
+
ARRAY_TYPE(doveadm_cmd) doveadm_cmds;
+ARRAY_TYPE(doveadm_cmd_ver2) doveadm_cmds_ver2;
+ARRAY_DEFINE_TYPE(getopt_option_array, struct option);
void doveadm_register_cmd(const struct doveadm_cmd *cmd)
{
array_append(&doveadm_cmds, cmd, 1);
}
+void doveadm_cmd_register_ver2(struct doveadm_cmd_ver2 *cmd)
+{
+ if (cmd->cmd == NULL) {
+ if (cmd->mail_cmd != NULL)
+ cmd->cmd = doveadm_cmd_ver2_to_mail_cmd_wrapper;
+ else if (cmd->old_cmd != NULL)
+ cmd->cmd = doveadm_cmd_ver2_to_cmd_wrapper;
+ else i_unreached();
+ }
+ array_append(&doveadm_cmds_ver2, cmd, 1);
+}
+
+const struct doveadm_cmd_ver2* doveadm_cmd_find_ver2(const char *cmd_name,
+ int argc, const char *argv[])
+{
+ int i;
+ const struct doveadm_cmd_ver2 *cmd;
+ const char *cptr;
+
+ for(i=0;i<argc;i++) {
+ if (strcmp(argv[i],cmd_name)==0) break;
+ }
+
+ i_assert(i != argc);
+
+ array_foreach(&doveadm_cmds_ver2, cmd) {
+ cptr = cmd->name;
+ /* cannot reuse i here because this needs be
+ done more than once */
+ for(int k=0; cptr != NULL && i+k < argc; k++) {
+ size_t alen = strlen(argv[i+k]);
+ /* make sure we don't overstep */
+ if (strlen(cptr) < alen) break;
+ /* did not match */
+ if (strncmp(cptr, argv[i+k], alen) != 0) break;
+ /* do not accept abbreviations */
+ if (cptr[alen] != ' ' && cptr[alen] != '\0') break;
+ cptr += alen;
+ if (*cptr != '\0') cptr++; /* consume space */
+ }
+ /* name was fully consumed */
+ if (*cptr == '\0') return cmd;
+ }
+
+ return NULL;
+}
+
static const struct doveadm_cmd *
doveadm_cmd_find_multi_word(const struct doveadm_cmd *cmd,
const char *cmdname, int *_argc, char **_argv[])
unsigned int i;
i_array_init(&doveadm_cmds, 32);
+ i_array_init(&doveadm_cmds_ver2, 2);
+
for (i = 0; i < N_ELEMENTS(doveadm_commands); i++)
doveadm_register_cmd(doveadm_commands[i]);
+ for (i = 0; i < N_ELEMENTS(doveadm_commands_ver2); i++)
+ doveadm_cmd_register_ver2(doveadm_commands_ver2[i]);
+
doveadm_register_auth_commands();
doveadm_register_director_commands();
doveadm_register_instance_commands();
{
array_free(&doveadm_cmds);
}
+
+static const struct doveadm_cmd_param*
+doveadm_cmd_param_get(int argc, const struct doveadm_cmd_param* params, const char *name)
+{
+ i_assert(params != NULL);
+ for(int i = 0; i < argc; i++) {
+ if (strcmp(params[i].name, name) == 0 && params[i].value_set)
+ return &(params[i]);
+ }
+ return NULL;
+}
+
+bool doveadm_cmd_param_bool(int argc, const struct doveadm_cmd_param* params, const char *name, bool* value)
+{
+ const struct doveadm_cmd_param* param;
+ if ((param = doveadm_cmd_param_get(argc, params, name))==NULL) return FALSE;
+
+ if (param->type == CMD_PARAM_NONE || param->type == CMD_PARAM_BOOL) {
+ *value = param->value.v_bool;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+bool doveadm_cmd_param_int64(int argc, const struct doveadm_cmd_param* params, const char *name, int64_t* value)
+{
+ const struct doveadm_cmd_param* param;
+ if ((param = doveadm_cmd_param_get(argc, params, name))==NULL) return FALSE;
+
+ if (param->type == CMD_PARAM_INT64) {
+ *value = param->value.v_int64;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+bool doveadm_cmd_param_str(int argc, const struct doveadm_cmd_param* params, const char *name, const char** value)
+{
+ const struct doveadm_cmd_param* param;
+ if ((param = doveadm_cmd_param_get(argc, params, name))==NULL) return FALSE;
+
+ if (param->type == CMD_PARAM_STR) {
+ *value = param->value.v_string;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+bool doveadm_cmd_param_array(int argc, struct doveadm_cmd_param* params, const char *name, ARRAY_TYPE(const_string)** value)
+{
+ const struct doveadm_cmd_param* param;
+ if ((param = doveadm_cmd_param_get(argc, params, name))==NULL) return FALSE;
+ if (param->type == CMD_PARAM_STR) {
+ *value = (ARRAY_TYPE(const_string)*)&(param->value.v_array);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+bool doveadm_cmd_param_istream(int argc, struct doveadm_cmd_param* params, const char *name, struct istream** value)
+{
+ const struct doveadm_cmd_param* param;
+ if ((param = doveadm_cmd_param_get(argc, params, name))==NULL) return FALSE;
+
+ if (param->type == CMD_PARAM_ISTREAM) {
+ *value = param->value.v_istream;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+doveadm_cmd_params_to_argv(const char *name, int pargc, const struct doveadm_cmd_param* params,
+ ARRAY_TYPE(const_string) *argv)
+{
+ int i;
+ const char * const * cptr;
+ i_assert(array_count(argv) == 0);
+ array_append(argv, &name, 1);
+ for(i=0;i<pargc;i++) {
+ if (params[i].value_set && params[i].opt != NULL &&
+ *(params[i].opt) != ':' && *(params[i].opt) != '?') {
+ const char *optarg = t_strdup_printf("-%c", params[i].opt[0]);
+ if (params[i].type == CMD_PARAM_STR) {
+ array_append(argv, &optarg, 1);
+ array_append(argv, ¶ms[i].value.v_string,1);
+ } else if (params[i].type == CMD_PARAM_ARRAY) {
+ array_foreach(¶ms[i].value.v_array, cptr) {
+ array_append(argv, &optarg, 1);
+ array_append(argv, cptr, 1);
+ }
+ }
+ } else if (params[i].value_set) {
+ if (params[i].type == CMD_PARAM_ARRAY) {
+ array_append_array(argv, ¶ms[i].value.v_array);
+ } else {
+ array_append(argv, ¶ms[i].value.v_string,1);
+ }
+ }
+ }
+ array_append_zero(argv);
+}
+
+int
+doveadm_cmd_ver2_to_cmd_wrapper(const struct doveadm_cmd_ver2* cmd,
+ int argc, const struct doveadm_cmd_param* param)
+{
+ unsigned int pargc;
+ const char **pargv;
+
+ i_assert(cmd->old_cmd != NULL);
+
+ ARRAY_TYPE(const_string) nargv;
+ t_array_init(&nargv, 8);
+ doveadm_cmd_params_to_argv(cmd->name, argc, param, &nargv);
+ pargv = array_get_modifiable(&nargv, &pargc);
+ i_getopt_reset();
+ cmd->old_cmd(pargc-1, (char**)pargv);
+
+ return 0;
+}
+
+static void
+doveadm_build_options(const struct doveadm_cmd_param par[],
+ string_t *shortopts,
+ ARRAY_TYPE(getopt_option_array) *longopts)
+{
+ const char *optp;
+ for(size_t i=0; par[i].name != NULL; i++) {
+ struct option longopt;
+ if ((par[i].flags & CMD_PARAM_FLAG_DO_NOT_EXPOSE) != 0) continue;
+ longopt.name = par[i].name;
+ longopt.flag = 0;
+ longopt.val = 0;
+ if (par[i].opt) {
+ optp = par[i].opt;
+ if (*optp != ':' && *optp != '?') {
+ longopt.val = *optp;
+ str_append_c(shortopts, *optp);
+ optp++;
+ if (optp[0] != '\0')
+ str_append_c(shortopts, *optp);
+ }
+ switch(*optp) {
+ case ':': longopt.has_arg = 1; break;
+ case '?': longopt.has_arg = 2; break;
+ default:
+ longopt.has_arg = 0;
+ }
+ } else {
+ longopt.has_arg = 0;
+ }
+ array_append(longopts, &longopt, 1);
+ }
+ array_append_zero(longopts);
+}
+
+static void doveadm_fill_param(struct doveadm_cmd_param *param,
+ const char *value, pool_t pool)
+{
+ param->value_set = TRUE;
+ switch(param->type) {
+ case CMD_PARAM_NONE:
+ case CMD_PARAM_BOOL:
+ param->value.v_bool = TRUE; break;
+ case CMD_PARAM_INT64:
+ if (str_to_int64(value, ¶m->value.v_int64) != 0) {
+ param->value_set = FALSE;
+ }
+ break;
+ case CMD_PARAM_STR:
+ if (value != NULL) {
+ param->value.v_string = p_strdup(pool, value);
+ } else {
+ param->value.v_string = NULL;
+ }
+ break;
+ case CMD_PARAM_ARRAY:
+ if (!array_is_created(¶m->value.v_array))
+ p_array_init(¶m->value.v_array, pool, 8);
+ const char *val = p_strdup(pool, value);
+ array_append(¶m->value.v_array, &val, 1);
+ break;
+ case CMD_PARAM_ISTREAM: {
+ struct istream *is;
+ if (strcmp(value,"-") == 0) {
+ is = i_stream_create_fd(STDIN_FILENO, IO_BLOCK_SIZE, FALSE);
+ } else {
+ is = i_stream_create_file(value, IO_BLOCK_SIZE);
+ }
+ param->value.v_istream = is;
+ }
+ }
+}
+
+bool doveadm_cmd_try_run_ver2(const char *cmd_name, int argc, const char *argv[])
+{
+ const struct doveadm_cmd_ver2 *cmd;
+
+ cmd = doveadm_cmd_find_ver2(cmd_name, argc, argv);
+ if (cmd == NULL)
+ return FALSE;
+
+ if (doveadm_cmd_run_ver2(cmd, argc, argv) < 0)
+ doveadm_exit_code = EX_USAGE;
+ return TRUE;
+}
+
+int doveadm_cmd_run_ver2(const struct doveadm_cmd_ver2 *cmd, int argc, const char *argv[])
+{
+ struct doveadm_cmd_param *param;
+ ARRAY(struct doveadm_cmd_param) pargv;
+ ARRAY_TYPE(getopt_option_array) opts;
+ const char *cptr;
+ unsigned int pargc;
+ int c,li;
+ pool_t pool = pool_datastack_create();
+ string_t *optbuf = str_new(pool, 64);
+
+ p_array_init(&opts, pool, 4);
+
+ // build parameters
+ doveadm_build_options(cmd->parameters, optbuf, &opts);
+
+ p_array_init(&pargv, pool, 20);
+
+ for(pargc=0;cmd->parameters[pargc].name != NULL;pargc++) {
+ param = array_append_space(&pargv);
+ memcpy(param, &(cmd->parameters[pargc]), sizeof(struct doveadm_cmd_param));
+ param->value_set = FALSE;
+ }
+ i_assert(pargc == array_count(&opts)-1); /* opts is NULL-terminated */
+
+ while((c = getopt_long(argc, (char*const*)argv, str_c(optbuf), array_idx(&opts, 0), &li)) > -1) {
+ switch(c) {
+ case 0:
+ doveadm_fill_param(array_idx_modifiable(&pargv,li), optarg, pool);
+ break;
+ case '?':
+ case ':':
+ return -1;
+ default:
+ // hunt the option
+ for(unsigned int i = 0; i < pargc; i++) {
+ const struct option *longopt = array_idx(&opts,i);
+ if (longopt->val == c)
+ doveadm_fill_param(array_idx_modifiable(&pargv,i), optarg, pool);
+ }
+ }
+ }
+
+ cptr = cmd->name;
+ while((cptr = strchr(cptr+1, ' ')) != NULL) optind++;
+
+ /* process positional arguments */
+ for(;optind<argc;optind++) {
+ struct doveadm_cmd_param *ptr;
+ bool found = FALSE;
+ array_foreach_modifiable(&pargv, ptr) {
+ if ((ptr->flags & CMD_PARAM_FLAG_POSITIONAL) != 0 &&
+ (ptr->value_set == FALSE || ptr->type == CMD_PARAM_ARRAY)) {
+ doveadm_fill_param(ptr, argv[optind], pool);
+ found = TRUE;
+ break;
+ }
+ }
+ if (!found) {
+ i_error("Extraneous arguments found");
+ return -1;
+ }
+ }
+
+ param = array_get_modifiable(&pargv, &pargc);
+
+ // FIXME: Unsure what do to with return value
+ cmd->cmd(cmd, pargc, param);
+
+ // unref istreams
+ array_foreach_modifiable(&pargv, param) {
+ if (param->type == CMD_PARAM_ISTREAM && param->value.v_istream != NULL)
+ i_stream_unref(¶m->value.v_istream);
+ }
+ return 0;
+}
#ifndef DOVEADM_CMD_H
#define DOVEADM_CMD_H
+#define DOVEADM_CMD_PARAMS_START .parameters = (const struct doveadm_cmd_param[]){
+#define DOVEADM_CMD_PARAM(optP, nameP, typeP, flagP ) { .opt = optP, .name = nameP, .type = typeP, .flags = flagP },
+#define DOVEADM_CMD_PARAMS_END { .opt = NULL, .name = NULL, .type = CMD_PARAM_NONE, .flags = CMD_PARAM_FLAG_NONE } }
+
+struct doveadm_cmd_ver2;
+struct doveadm_mail_cmd_context;
+
typedef void doveadm_command_t(int argc, char *argv[]);
+typedef enum {
+ CMD_PARAM_NONE, /* same as below, used for empty */
+ CMD_PARAM_BOOL, /* value will contain 1 (not pointer) */
+ CMD_PARAM_INT64, /* ditto but contains number (not pointer) */
+ CMD_PARAM_STR, /* value contains const char* */
+ CMD_PARAM_ARRAY, /* value contains const char*[] */
+ CMD_PARAM_ISTREAM /* value contains struct istream* */
+} doveadm_cmd_param_t;
+
+typedef enum {
+ CMD_PARAM_FLAG_NONE = 0x0,
+ CMD_PARAM_FLAG_POSITIONAL = 0x1,
+ CMD_PARAM_FLAG_DO_NOT_EXPOSE = 0x2,
+} doveadm_cmd_param_flag_t;
+
+struct doveadm_cmd_param {
+ const char *opt;
+ const char *name;
+ doveadm_cmd_param_t type;
+ bool value_set;
+ struct {
+ bool v_bool;
+ int64_t v_int64;
+ const char* v_string;
+ ARRAY_TYPE(const_string) v_array;
+ struct istream* v_istream;
+ } value;
+ doveadm_cmd_param_flag_t flags;
+};
+
+typedef int doveadm_command_ver2_t(const struct doveadm_cmd_ver2* cmd,
+ int argc, const struct doveadm_cmd_param[]);
+
struct doveadm_cmd {
doveadm_command_t *cmd;
const char *name;
const char *short_usage;
};
+
+struct doveadm_cmd_ver2 {
+ doveadm_command_ver2_t *cmd;
+ doveadm_command_t *old_cmd;
+ struct doveadm_mail_cmd_context *(*mail_cmd)(void);
+ const char *name;
+ const char *usage;
+ const struct doveadm_cmd_param *parameters;
+};
+
ARRAY_DEFINE_TYPE(doveadm_cmd, struct doveadm_cmd);
extern ARRAY_TYPE(doveadm_cmd) doveadm_cmds;
extern struct doveadm_cmd doveadm_cmd_stop;
extern struct doveadm_cmd doveadm_cmd_reload;
+ARRAY_DEFINE_TYPE(doveadm_cmd_ver2, struct doveadm_cmd_ver2);
+extern ARRAY_TYPE(doveadm_cmd_ver2) doveadm_cmds_ver2;
+
extern struct doveadm_cmd doveadm_cmd_dump;
extern struct doveadm_cmd doveadm_cmd_pw;
extern struct doveadm_cmd doveadm_cmd_who;
void doveadm_cmds_init(void);
void doveadm_cmds_deinit(void);
+int doveadm_cmd_ver2_to_cmd_wrapper(const struct doveadm_cmd_ver2* cmd,
+ int argc, const struct doveadm_cmd_param[]);
+int doveadm_cmd_ver2_to_mail_cmd_wrapper(const struct doveadm_cmd_ver2* cmd,
+ int argc, const struct doveadm_cmd_param argv[]);
+
+void doveadm_cmd_register_ver2(struct doveadm_cmd_ver2 *cmd);
+const struct doveadm_cmd_ver2 *doveadm_cmd_find_ver2(const char *cmd_name,
+ int argc, const char *argv[]);
+/* Returns FALSE if cmd_name doesn't exist, TRUE if it exists. */
+bool doveadm_cmd_try_run_ver2(const char *cmd_name, int argc,
+ const char *argv[]);
+/* Returns 0 if success, -1 if parameters were invalid. */
+int doveadm_cmd_run_ver2(const struct doveadm_cmd_ver2 *cmd,
+ int argc, const char *argv[]);
+
+bool doveadm_cmd_param_bool(int argc, const struct doveadm_cmd_param* params, const char *name, bool* value);
+bool doveadm_cmd_param_int64(int argc, const struct doveadm_cmd_param* params, const char *name, int64_t* value);
+bool doveadm_cmd_param_str(int argc, const struct doveadm_cmd_param* params, const char *name, const char** value);
+bool doveadm_cmd_param_array(int argc, struct doveadm_cmd_param* params, const char *name, ARRAY_TYPE(const_string)** value);
+bool doveadm_cmd_param_istream(int argc, struct doveadm_cmd_param* params, const char *name, struct istream** value);
+
#endif
return ctx;
}
+static struct doveadm_mail_cmd_context *
+doveadm_mail_cmdline_init(const struct doveadm_mail_cmd *cmd)
+{
+ struct doveadm_mail_cmd_context *ctx;
+
+ ctx = doveadm_mail_cmd_init(cmd, doveadm_settings);
+ ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT;
+ if (doveadm_debug)
+ ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_DEBUG;
+ ctx->cur_username = getenv("USER");
+ return ctx;
+}
+
static void
doveadm_mail_cmd_exec(struct doveadm_mail_cmd_context *ctx,
const char *wildcard_user)
const char *getopt_args, *wildcard_user;
int c;
- ctx = doveadm_mail_cmd_init(cmd, doveadm_settings);
+ ctx = doveadm_mail_cmdline_init(cmd);
ctx->full_args = (const void *)(argv + 1);
- ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT;
- if (doveadm_debug)
- ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_DEBUG;
-
getopt_args = "AF:S:u:";
/* keep context's getopt_args first in case it contains '+' */
if (ctx->getopt_args != NULL)
getopt_args = t_strconcat(ctx->getopt_args, getopt_args, NULL);
i_assert(master_getopt_str_is_valid(getopt_args));
- ctx->cur_username = getenv("USER");
wildcard_user = NULL;
while ((c = getopt(argc, argv, getopt_args)) > 0) {
switch (c) {
&cmd_dsync_server
};
+static struct doveadm_cmd_ver2 *mail_commands_ver2[] = {
+};
+
void doveadm_mail_init(void)
{
struct module_dir_load_settings mod_set;
for (i = 0; i < N_ELEMENTS(mail_commands); i++)
doveadm_mail_register_cmd(mail_commands[i]);
+ for (i = 0; i < N_ELEMENTS(mail_commands_ver2); i++)
+ doveadm_cmd_register_ver2(mail_commands_ver2[i]);
+
memset(&mod_set, 0, sizeof(mod_set));
mod_set.abi_version = DOVECOT_ABI_VERSION;
mod_set.require_init_funcs = TRUE;
mail_storage_hooks_deinit();
array_free(&doveadm_mail_cmds);
}
+
+int
+doveadm_cmd_ver2_to_mail_cmd_wrapper(const struct doveadm_cmd_ver2* cmd,
+ int argc, const struct doveadm_cmd_param argv[])
+{
+ struct doveadm_mail_cmd_context *ctx;
+ const char *wildcard_user;
+ ARRAY_TYPE(const_string) pargv;
+ int i;
+ struct doveadm_mail_cmd mail_cmd = {
+ .alloc = cmd->mail_cmd
+ };
+
+ ctx = doveadm_mail_cmdline_init(&mail_cmd);
+
+ ctx->iterate_all_users = FALSE;
+ wildcard_user = NULL;
+ t_array_init(&pargv, 8);
+
+ for(i=0;i<argc;i++) {
+ if (!argv[i].value_set)
+ continue;
+
+ if (strcmp(argv[i].name, "all_users") == 0) {
+ ctx->iterate_all_users = argv[i].value.v_bool;
+ } else if (strcmp(argv[i].name, "socket_path") == 0) {
+ doveadm_settings->doveadm_socket_path = argv[i].value.v_string;
+ if (doveadm_settings->doveadm_worker_count == 0)
+ doveadm_settings->doveadm_worker_count = 1;
+ } else if (strcmp(argv[i].name, "user") == 0) {
+ ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP;
+ ctx->cur_username = argv[i].value.v_string;
+ if (strchr(ctx->cur_username, '*') != NULL ||
+ strchr(ctx->cur_username, '?') != NULL) {
+ wildcard_user = ctx->cur_username;
+ ctx->cur_username = NULL;
+ }
+ } else if (strcmp(argv[i].name, "user_list") == 0) {
+ ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP;
+ wildcard_user = "*";
+ ctx->users_list_input = argv[i].value.v_istream;
+ } else if (ctx->v.parse_arg != NULL && argv[i].opt != NULL &&
+ *(argv[i]).opt != '?' && *(argv[i]).opt != ':') {
+ optarg = (char*)argv[i].value.v_string;
+ ctx->v.parse_arg(ctx, *(argv[i].opt));
+ } else if ((argv[i].flags & CMD_PARAM_FLAG_POSITIONAL) != 0) {
+ /* feed this into pargv */
+ if (argv[i].type == CMD_PARAM_ARRAY)
+ array_append_array(&pargv, &argv[i].value.v_array);
+ else if (argv[i].type == CMD_PARAM_STR)
+ array_append(&pargv, &argv[i].value.v_string, 1);
+ } else {
+ doveadm_exit_code = EX_USAGE;
+ i_error("invalid parameter: %s", argv[i].name);
+ return -1;
+ }
+ }
+
+ array_append_zero(&pargv);
+ ctx->args = array_idx(&pargv, 0);
+ ctx->full_args = ctx->args;
+
+ doveadm_mail_cmd_exec(ctx, wildcard_user);
+ return 0;
+}