+
+/*
+ * Plugin restore option part
+ */
+
+/* Free a plugin_config_item */
+void free_plugin_config_item(plugin_config_item *elt)
+{
+ free(elt->plugin_name);
+ free_pool_memory(elt->content);
+ free(elt);
+}
+
+/* Free a list of plugins (do not free the list itself) */
+void free_plugin_config_items(alist *lst)
+{
+ plugin_config_item *elt;
+
+ if (!lst) {
+ return;
+ }
+
+ foreach_alist(elt, lst) {
+ free_plugin_config_item(elt);
+ }
+}
+
+/* Structure used in the sql query to get configuration restore objects */
+struct plugin_config_handler_t
+{
+ UAContext *ua; /* UAContext for user input */
+ POOLMEM *tmp; /* Used to store the config object */
+ alist *plugins; /* Configuration plugin list */
+ alist *content; /* Temp file used by each plugin */
+};
+
+/* DB handler to get all configuration restore objects for a given
+ * set of jobids
+ */
+static int plugin_config_handler(void *ctx, int num_fields, char **row)
+{
+ struct plugin_config_handler_t *pch = (struct plugin_config_handler_t *)ctx;
+ UAContext *ua = pch->ua;
+ JCR *jcr = ua->jcr;
+ int32_t len;
+
+ /* object */
+ db_unescape_object(jcr, ua->db,
+ row[8], /* Object */
+ str_to_uint64(row[1]), /* Object length */
+ &pch->tmp, &len);
+
+ /* Is compressed ? */
+ if (str_to_int64(row[5]) > 0) {
+ int full_len = str_to_int64(row[2]);
+ int out_len = full_len + 100; /* full length */
+ char *obj = (char *)malloc(out_len);
+ Zinflate(pch->tmp, len, obj, out_len); /* out_len is updated */
+ if (out_len != full_len) {
+ ua->error_msg(_("Decompression failed. Len wanted=%d got=%d. Object=%s\n"),
+ full_len, out_len, row[9]);
+ }
+ obj[out_len] = 0;
+ pch->content->append(obj);
+
+ } else {
+ pch->tmp[len]=0;
+ pch->content->append(bstrdup(pch->tmp));
+ }
+
+ pch->plugins->append(bstrdup(row[9]));
+ return 0;
+}
+
+/* Save a Plugin Config object (ConfigFile) inside the JCR
+ * using a list of plugin_config_item
+ *
+ * We allow only one Plugin Config object per Plugin
+ */
+static void plugin_config_save_jcr(UAContext *ua, JCR *jcr,
+ char *pname, ConfigFile *ini)
+{
+ plugin_config_item *elt;
+ if (!jcr->plugin_config) {
+ jcr->plugin_config = New(alist(5, not_owned_by_alist));
+ }
+
+ /* Store only one Plugin Config object per plugin command */
+ for (int i = 0; i < jcr->plugin_config->size() ; i++) {
+ elt = (plugin_config_item *) jcr->plugin_config->get(i);
+ if (strcmp(elt->plugin_name, pname) == 0) {
+ jcr->plugin_config->remove(i);
+ free_plugin_config_item(elt);
+ break;
+ }
+ }
+
+ elt = (plugin_config_item *) malloc (sizeof(plugin_config_item));
+ elt->plugin_name = bstrdup(pname);
+ elt->content = get_pool_memory(PM_FNAME);
+ ini->dump_results(&elt->content);
+ jcr->plugin_config->append(elt);
+}
+
+/* TODO: Allow to have sub-menus Advanced.restore_mode can be
+ * in a Advanced panel (sub menu)
+ */
+
+/* Take the ConfigIni struture and display user menu for a given plugin */
+static int plugin_display_options(UAContext *ua, JCR *jcr, ConfigFile *ini)
+{
+ int i, nb;
+ int jcr_pos = -1;
+ POOL_MEM prompt, tmp;
+ bool found;
+ INI_ITEM_HANDLER *h;
+
+ /* TODO: See how to work in API mode
+ if (ua->api) {
+ ua->signal(BNET_RUN_CMD);
+ }
+ */
+
+ /* Take a look in the plugin_config list to see if we have something to
+ * initialize
+ */
+ if (jcr->plugin_config) {
+ plugin_config_item *item=NULL;
+
+ for (jcr_pos = 0; jcr_pos < jcr->plugin_config->size() ; jcr_pos++) {
+ item = (plugin_config_item *)jcr->plugin_config->get(jcr_pos);
+
+ if (strcmp(item->plugin_name, ini->plugin_name) == 0) /* bpipe:xxx:yyyy */
+ {
+ if (!ini->dump_string(item->content, strlen(item->content)) ||
+ !ini->parse(ini->out_fname))
+ {
+ ua->error_msg(_("Unable to use current plugin configuration, "
+ "discarding it."));
+ }
+ /* When we are here, we can type yes (it will add it back), or no
+ * to not use this plugin configuration. So, don't keep it in the
+ * list.
+ */
+ jcr->plugin_config->remove(jcr_pos);
+ free_plugin_config_item(item);
+ break;
+ }
+ }
+ }
+
+configure_again:
+ ua->send_msg(_("Plugin Restore Options\n"));
+
+ for (nb=0; ini->items[nb].name; nb++) {
+
+ if (ini->items[nb].found) {
+ /* When calling the handler, It will convert the value
+ * to a string representation in ini->edit
+ */
+ ini->items[nb].handler(NULL, ini, &ini->items[nb]);
+ } else {
+ if (ini->items[nb].required) {
+ pm_strcpy(ini->edit, _("*None, but required*"));
+
+ } else {
+ pm_strcpy(ini->edit, _("*None*"));
+ }
+ }
+
+ Mmsg(tmp, "%s:", ini->items[nb].name);
+
+ Mmsg(prompt, "%-20s %-20s ",
+ tmp.c_str(), ini->edit);
+
+ if (ini->items[nb].default_value) {
+ Mmsg(tmp, "(%s)", ini->items[nb].default_value);
+ pm_strcat(prompt, tmp.c_str());
+ }
+
+ ua->send_msg("%s\n", prompt.c_str());
+ }
+
+ if (!get_cmd(ua, _("Use above plugin configuration? (yes/mod/no): "))) {
+ ini->clear_items();
+ return 0;
+ }
+
+ /* '', 'y', 'ye', and 'yes' are valid */
+ if (strncasecmp(ua->cmd, _("yes"), strlen(ua->cmd)) == 0) {
+ return 1;
+ }
+
+ if (strncasecmp(ua->cmd, _("no"), strlen(ua->cmd)) == 0) {
+ ini->clear_items();
+ return 0;
+ }
+
+ /* When using "mod", we display the list of parameters with their
+ * comments, and we let the user choose one entry to modify
+ */
+ if (strncasecmp(ua->cmd, _("mod"), strlen(ua->cmd)) == 0) {
+ start_prompt(ua, _("You have the following choices:\n"));
+
+ for (nb=0; ini->items[nb].name; nb++) {
+
+ if (ini->items[nb].comment) {
+ Mmsg(tmp, " (%s)", ini->items[nb].comment);
+ } else {
+ pm_strcpy(tmp, "");
+ }
+
+ Mmsg(prompt, "%s%s ",
+ ini->items[nb].name, tmp.c_str());
+
+ add_prompt(ua, prompt.c_str());
+ }
+
+ i = do_prompt(ua, NULL, _("Select parameter to modify"), NULL, 0);
+
+ if (i < 0) {
+ ini->clear_items();
+ return 0;
+ }
+
+ Mmsg(prompt, _("Please enter a value for %s: "), ini->items[i].name);
+
+ /* Now use the handler to know how to ask the value to the user.
+ * For example, boolean will use get_yes_no(), pint32 will use get_pint()
+ */
+ h = ini->items[i].handler;
+ if (h == ini_store_int32 ||
+ h == ini_store_pint32) {
+ found = ini->items[i].found = get_pint(ua, prompt.c_str());
+ if (found) {
+ ini->items[i].val.int32val = ua->pint32_val;
+ }
+
+ } else if (h == ini_store_bool) {
+ found = ini->items[i].found = get_yesno(ua, prompt.c_str());
+ if (found) {
+ ini->items[i].val.boolval = ua->pint32_val;
+ }
+
+ } else if (h == ini_store_name) {
+ found = ini->items[i].found = get_cmd(ua, prompt.c_str());
+ if (found) {
+ strncpy(ini->items[i].val.nameval, ua->cmd, MAX_NAME_LENGTH -1);
+ ini->items[i].val.nameval[MAX_NAME_LENGTH - 1] = 0;
+ }
+
+ } else if (h == ini_store_str) {
+ found = ini->items[i].found = get_cmd(ua, prompt.c_str());
+ if (found) {
+ ini->items[i].val.strval = bstrdup(ua->cmd);
+ }
+
+ } else if (h == ini_store_int64 ||
+ h == ini_store_pint64) {
+ found = ini->items[i].found = get_pint(ua, prompt.c_str());
+ if (found) {
+ ini->items[i].val.int64val = ua->int64_val;
+ }
+ }
+ goto configure_again;
+ }
+
+ return 1; /* never reached */
+}
+
+/* Display a menu with all plugins */
+static void plugin_config(UAContext *ua, JCR *jcr, run_ctx &rc)
+{
+ int i, nb;
+ char *elt, *tmp;
+ ConfigFile *ini = NULL;
+ POOLMEM *query=NULL;
+ struct plugin_config_handler_t pch;
+
+ /* No jobids for this restore, probably wrong */
+ if (!jcr->JobIds || !jcr->JobIds[0]) {
+ return;
+ }
+
+ if (!open_client_db(ua)) {
+ return;
+ }
+
+ pch.ua = ua;
+ query = get_pool_memory(PM_FNAME);
+ pch.tmp = get_pool_memory(PM_MESSAGE);
+ pch.plugins = New(alist(10, owned_by_alist));
+ pch.content = New(alist(10, owned_by_alist));
+
+ /* Get all RestoreObject PLUGIN_CONFIG for the given Job */
+ Mmsg(query, get_restore_objects, jcr->JobIds, FT_PLUGIN_CONFIG);
+ db_sql_query(ua->db, query, plugin_config_handler, &pch);
+
+ if (!pch.plugins || pch.plugins->size() == 0) {
+ ua->info_msg(_("No plugin to configure\n"));
+ goto bail_out;
+ }
+
+ /* TODO: Let see if we want to configure plugins that were given in command
+ * line.
+ */
+
+ start_prompt(ua, _("Plugins to configure:\n"));
+
+ nb=0;
+ foreach_alist(elt, pch.plugins) {
+ nb++;
+ pm_strcpy(query, elt);
+ add_prompt(ua, query);
+ }
+
+ i = do_prompt(ua, "", _("Select plugin to configure"), NULL, 0);
+
+ if (i < 0) {
+ goto bail_out;
+ }
+
+
+ elt = (char *)pch.plugins->get(i);
+ ini = new ConfigFile();
+ /* Try to read the plugin configuration, if error, loop to configure
+ * something else, or bail_out
+ */
+ tmp = (char *)pch.content->get(i);
+ if (!ini->dump_string(tmp, strlen(tmp)) || /* Send the string to a file */
+ !ini->unserialize(ini->out_fname)) { /* Read the file to initialize the ConfigFile */
+
+ ua->error_msg(_("Can't configure %32s\n"), elt);
+ goto bail_out;
+ }
+
+ ini->set_plugin_name(elt);
+
+ if (plugin_display_options(ua, jcr, ini)) {
+ ini->dump_results(&query);
+ Dmsg1(50, "plugin: %s\n", query);
+
+ /* Save the plugin somewhere in the JCR */
+ plugin_config_save_jcr(ua, jcr, elt, ini);
+ }
+
+bail_out:
+ free_pool_memory(pch.tmp);
+ free_pool_memory(query);
+ if (ini) {
+ delete ini;
+ }
+ delete pch.plugins;
+ delete pch.content;
+}
+