+ _("Failed: boot %s"), cod->bd->image);
+ }
+
+ return 0;
+}
+
+static int menu_sysinfo_execute(struct pmenu_item *item)
+{
+ cui_show_sysinfo(cui_from_item(item));
+ return 0;
+}
+
+static int menu_config_execute(struct pmenu_item *item)
+{
+ cui_show_config(cui_from_item(item));
+ return 0;
+}
+
+static int menu_lang_execute(struct pmenu_item *item)
+{
+ cui_show_lang(cui_from_item(item));
+ return 0;
+}
+
+static int menu_statuslog_execute(struct pmenu_item *item)
+{
+ cui_show_statuslog(cui_from_item(item));
+ return 0;
+}
+
+static void menu_reinit_cb(struct nc_scr *scr)
+{
+ struct pmenu *menu = pmenu_from_scr(scr);
+
+ cui_send_reinit(cui_from_pmenu(menu));
+}
+
+static int menu_reinit_execute(struct pmenu_item *item)
+{
+ struct cui *cui = cui_from_item(item);
+
+ if (!cui->client)
+ return 0;
+
+ /* If we don't need to authenticate, send the reinit immediately */
+ if (discover_client_authenticated(cui->client)) {
+ cui_send_reinit(cui);
+ return 0;
+ }
+
+ if (!cui->current)
+ return 0;
+
+ if (cui->auth_screen)
+ return 0;
+
+ cui->auth_screen = auth_screen_init(cui, cui->current->main_ncw,
+ false, NULL, menu_reinit_cb, cui_auth_exit);
+
+ if (cui->auth_screen)
+ cui_set_current(cui, auth_screen_scr(cui->auth_screen));
+
+ return 0;
+}
+
+static int menu_add_url_execute(struct pmenu_item *item)
+{
+ if (cui_from_item(item)->client)
+ cui_show_add_url(cui_from_item(item));
+ return 0;
+}
+
+static int menu_plugin_execute(struct pmenu_item *item)
+{
+ if (cui_from_item(item)->client)
+ cui_show_plugin_menu(cui_from_item(item));
+ return 0;
+}
+
+static void cui_boot_cb(struct nc_scr *scr)
+{
+ struct pmenu *menu = pmenu_from_scr(scr);
+
+ if (pmenu_find_selected(menu))
+ cui_boot(pmenu_find_selected(menu));
+}
+
+static int cui_boot_check(struct pmenu_item *item)
+{
+ struct cui_opt_data *cod = cod_from_item(item);
+ struct cui *cui = cui_from_item(item);
+
+ if (discover_client_authenticated(cui->client))
+ return cui_boot(item);
+
+ /* Client doesn't need authentication to boot the default option */
+ if (cui->default_item == cod->opt_hash)
+ return cui_boot(item);
+
+ cui_show_auth(cui, item->pmenu->scr.main_ncw, false, cui_boot_cb);
+
+ return 0;
+}
+
+static void cui_luks_cb(struct nc_scr *scr)
+{
+ struct cui_opt_data *cod;
+ struct pmenu_item *item;
+ struct pmenu *menu;
+ struct cui *cui;
+
+ menu = pmenu_from_scr(scr);
+ item = pmenu_find_selected(menu);
+ cod = cod_from_item(item);
+ cui = cui_from_item(item);
+
+ cui_show_open_luks(cui, scr->main_ncw, cod->dev);
+}
+
+static int cui_open_luks_device(struct pmenu_item *item)
+{
+ struct cui_opt_data *cod = cod_from_item(item);
+ struct cui *cui = cui_from_item(item);
+
+ if (discover_client_authenticated(cui->client))
+ cui_show_open_luks(cui, item->pmenu->scr.main_ncw, cod->dev);
+ else
+ cui_show_auth(cui, item->pmenu->scr.main_ncw, false,
+ cui_luks_cb);
+
+ return 0;
+}
+
+static void cui_boot_editor_on_exit(struct cui *cui,
+ struct pmenu_item *item,
+ struct pb_boot_data *bd)
+{
+ struct pmenu *menu = cui->main;
+ struct cui_opt_data *cod;
+ int idx, top, rows, cols;
+ static int user_idx = 0;
+
+ /* Was the edit cancelled? */
+ if (!bd) {
+ cui_set_current(cui, &cui->main->scr);
+ talloc_free(cui->boot_editor);
+ cui->boot_editor = NULL;
+ return;
+ }
+
+ /* Is this was a new item, we'll need to update the menu */
+ if (!item) {
+ int insert_pt;
+
+ cod = talloc_zero(NULL, struct cui_opt_data);
+ cod->name = talloc_asprintf(cod, _("User item %u"), ++user_idx);
+
+ item = pmenu_item_create(menu, cod->name);
+ if (!item) {
+ talloc_free(cod);
+ goto out;
+ }
+
+ item->on_edit = cui_item_edit;
+ item->on_execute = cui_boot_check;
+ item->data = cod;
+
+ talloc_steal(item, cod);
+
+ /* Detach the items array. */
+ set_menu_items(menu->ncm, NULL);
+
+ /* Insert new item at insert_pt. */
+ insert_pt = pmenu_grow(menu, 1);
+ pmenu_item_insert(menu, item, insert_pt);
+
+ /* Re-attach the items array. */
+ set_menu_items(menu->ncm, menu->items);
+
+ /* If our index is above the current top row, align
+ * us to the new top. Otherwise, align us to the new
+ * bottom */
+ menu_format(cui->main->ncm, &rows, &cols);
+ top = top_row(cui->main->ncm);
+ idx = item_index(item->nci);
+
+ if (top >= idx)
+ top = idx;
+ else
+ top = idx < rows ? 0 : idx - rows + 1;
+
+ set_top_row(cui->main->ncm, top);
+ set_current_item(item->pmenu->ncm, item->nci);
+
+ nc_scr_post(&menu->scr);
+ } else {
+ cod = item->data;
+ }
+
+ cod->bd = talloc_steal(cod, bd);
+
+out:
+ cui_set_current(cui, &cui->main->scr);
+ talloc_free(cui->boot_editor);
+ cui->boot_editor = NULL;
+}
+
+void cui_item_edit(struct pmenu_item *item)
+{
+ struct cui *cui = cui_from_item(item);
+ cui->boot_editor = boot_editor_init(cui, item, cui->sysinfo,
+ cui_boot_editor_on_exit);
+ cui_set_current(cui, boot_editor_scr(cui->boot_editor));
+}
+
+void cui_item_new(struct pmenu *menu)
+{
+ struct cui *cui = cui_from_pmenu(menu);
+ cui->boot_editor = boot_editor_init(cui, NULL, cui->sysinfo,
+ cui_boot_editor_on_exit);
+ cui_set_current(cui, boot_editor_scr(cui->boot_editor));
+}
+
+
+/* Call pb-plugin to install a plugin specified by plugin_file */
+static int cui_install_plugin(struct pmenu_item *item)
+{
+ struct cui *cui = cui_from_item(item);
+ struct cui_opt_data *cod = cod_from_item(item);
+ int rc;
+
+ rc = cui_send_plugin_install(cui, cod->pd->plugin_file);
+
+ if (rc) {
+ pb_log("cui_send_plugin_install failed!\n");
+ nc_scr_status_printf(cui->current,
+ _("Failed to send install request"));
+ } else {
+ nc_scr_status_printf(cui->current, _("Installing plugin %s"),
+ cod->pd->plugin_file);
+ pb_debug("cui_send_plugin_install sent!\n");
+ }
+
+ return rc;
+}
+
+static void cui_plugin_install_cb(struct nc_scr *scr)
+{
+ struct pmenu *menu = pmenu_from_scr(scr);
+
+ if (pmenu_find_selected(menu))
+ cui_install_plugin(pmenu_find_selected(menu));
+ else
+ pb_debug("%s: no current item\n", __func__);
+}
+
+static int cui_plugin_install_check(struct pmenu_item *item)
+{
+ struct cui *cui = cui_from_item(item);
+
+ if (discover_client_authenticated(cui->client))
+ return cui_install_plugin(item);
+
+ cui_show_auth(cui, item->pmenu->scr.main_ncw, false,
+ cui_plugin_install_cb);
+
+ return 0;
+}
+
+static void cui_sysinfo_exit(struct cui *cui)
+{
+ cui_set_current(cui, &cui->main->scr);
+ talloc_free(cui->sysinfo_screen);
+ cui->sysinfo_screen = NULL;
+}
+
+void cui_show_sysinfo(struct cui *cui)
+{
+ cui->sysinfo_screen = sysinfo_screen_init(cui, cui->sysinfo,
+ cui_sysinfo_exit);
+ cui_set_current(cui, sysinfo_screen_scr(cui->sysinfo_screen));
+}
+
+static void cui_config_exit(struct cui *cui)
+{
+ cui_set_current(cui, &cui->main->scr);
+ talloc_free(cui->config_screen);
+ cui->config_screen = NULL;
+}
+
+void cui_show_config(struct cui *cui)
+{
+ cui->config_screen = config_screen_init(cui, cui->config,
+ cui->sysinfo, cui_config_exit);
+ cui_set_current(cui, config_screen_scr(cui->config_screen));
+}
+
+static void cui_lang_exit(struct cui *cui)
+{
+ cui_set_current(cui, &cui->main->scr);
+ talloc_free(cui->lang_screen);
+ cui->lang_screen = NULL;
+}
+
+void cui_show_lang(struct cui *cui)
+{
+ cui->lang_screen = lang_screen_init(cui, cui->config, cui_lang_exit);
+ cui_set_current(cui, lang_screen_scr(cui->lang_screen));
+}
+
+static void cui_statuslog_exit(struct cui *cui)
+{
+ cui_set_current(cui, &cui->main->scr);
+ talloc_free(cui->statuslog_screen);
+ cui->statuslog_screen = NULL;
+}
+
+void cui_show_statuslog(struct cui *cui)
+{
+ cui->statuslog_screen = statuslog_screen_init(cui, cui_statuslog_exit);
+ cui_set_current(cui, statuslog_screen_scr(cui->statuslog_screen));
+}
+
+static void cui_add_url_exit(struct cui *cui)
+{
+ cui_set_current(cui, &cui->main->scr);
+ talloc_free(cui->add_url_screen);
+ cui->add_url_screen = NULL;
+}
+
+static void cui_plugin_exit(struct cui *cui)
+{
+ cui_set_current(cui, &cui->plugin_menu->scr);
+ talloc_free(cui->plugin_screen);
+ cui->plugin_screen = NULL;
+}
+
+static void cui_plugin_menu_exit(struct pmenu *menu)
+{
+ struct cui *cui = cui_from_pmenu(menu);
+ cui_set_current(cui, &cui->main->scr);
+}
+
+void cui_show_add_url(struct cui *cui)
+{
+ cui->add_url_screen = add_url_screen_init(cui, cui_add_url_exit);
+ cui_set_current(cui, add_url_screen_scr(cui->add_url_screen));
+}
+
+void cui_show_plugin_menu(struct cui *cui)
+{
+ cui_set_current(cui, &cui->plugin_menu->scr);
+}
+
+void cui_show_plugin(struct pmenu_item *item)
+{
+ struct cui *cui = cui_from_item(item);
+ cui->plugin_screen = plugin_screen_init(cui, item, cui_plugin_exit);
+ cui_set_current(cui, plugin_screen_scr(cui->plugin_screen));
+}
+
+static void cui_help_exit(struct cui *cui)
+{
+ cui_set_current(cui, help_screen_return_scr(cui->help_screen));
+ talloc_free(cui->help_screen);
+ cui->help_screen = NULL;
+}
+
+void cui_show_help(struct cui *cui, const char *title,
+ const struct help_text *text)
+{
+ if (!cui->current)
+ return;
+
+ if (cui->help_screen)
+ return;
+
+ cui->help_screen = help_screen_init(cui, cui->current,
+ title, text, cui_help_exit);
+
+ if (cui->help_screen)
+ cui_set_current(cui, help_screen_scr(cui->help_screen));
+}
+
+static void cui_subset_exit(struct cui *cui)
+{
+ cui_set_current(cui, subset_screen_return_scr(cui->subset_screen));
+ talloc_free(cui->subset_screen);
+ cui->subset_screen = NULL;
+}
+
+void cui_show_subset(struct cui *cui, const char *title,
+ void *arg)
+{
+ if (!cui->current)
+ return;
+
+ if (cui->subset_screen)
+ return;
+
+ cui->subset_screen = subset_screen_init(cui, cui->current,
+ title, arg, cui_subset_exit);
+
+ if (cui->subset_screen)
+ cui_set_current(cui, subset_screen_scr(cui->subset_screen));
+}
+
+static void cui_auth_exit(struct cui *cui)
+{
+ struct nc_scr *return_scr = auth_screen_return_scr(cui->auth_screen);
+
+ /*
+ * Destroy the auth screen first so that the subwindow is cleaned up
+ * before the return_scr posts. If we don't do this operations on the
+ * main_ncw can cause a blank screen at first (eg. status update).
+ */
+ nc_scr_unpost(cui->current);
+ talloc_free(cui->auth_screen);
+ cui->auth_screen = NULL;
+
+ cui->current = return_scr;
+ nc_scr_post(cui->current);
+}
+
+void cui_show_auth(struct cui *cui, WINDOW *parent, bool set_password,
+ void (*callback)(struct nc_scr *))
+{
+ if (!cui->current)
+ return;
+
+ if (cui->auth_screen)
+ return;
+
+ cui->auth_screen = auth_screen_init(cui, parent, set_password, NULL,
+ callback, cui_auth_exit);
+
+ if (cui->auth_screen)
+ cui_set_current(cui, auth_screen_scr(cui->auth_screen));
+}
+
+void cui_show_open_luks(struct cui *cui, WINDOW *parent,
+ const struct device *dev)
+{
+ if (!cui->current)
+ return;
+
+ if (cui->auth_screen)
+ return;
+
+ cui->auth_screen = auth_screen_init(cui, parent, false, dev,
+ NULL, cui_auth_exit);
+
+ if (cui->auth_screen)
+ cui_set_current(cui, auth_screen_scr(cui->auth_screen));
+}
+/**
+ * cui_set_current - Set the currently active screen and redraw it.
+ */
+
+struct nc_scr *cui_set_current(struct cui *cui, struct nc_scr *scr)
+{
+ struct nc_scr *old;
+
+ DBGS("%p -> %p\n", cui->current, scr);
+
+ assert(cui->current != scr);
+
+ old = cui->current;
+ nc_scr_unpost(old);
+
+ cui->current = scr;
+
+ nc_scr_post(cui->current);
+
+ return old;
+}
+
+static bool set_temp_autoboot_opt(struct cui *cui, struct autoboot_option *opt)
+{
+ cui->autoboot_opt = opt;
+ if (cui->client)
+ discover_client_send_temp_autoboot(cui->client, opt);
+
+ return true;
+}
+
+static bool key_cancels_boot(int key)
+{
+ unsigned int i;
+
+ if (key == 0xc)
+ return false;
+
+ for (i = 0; i < ARRAY_SIZE(autoboot_override_keys); i++)
+ if (key == autoboot_override_keys[i].key)
+ return false;
+
+ return true;
+}
+
+static bool process_global_keys(struct cui *cui, int key)
+{
+ unsigned int i;
+
+ switch (key) {
+ case 0xc:
+ if (cui->current && cui->current->main_ncw)
+ wrefresh(curscr);
+ return true;
+ }
+
+ /* check for autoboot override keys */
+ for (i = 0; i < ARRAY_SIZE(autoboot_override_keys); i++) {
+ if (key != autoboot_override_keys[i].key)
+ continue;
+
+ pb_log("Sending temporary autoboot override\n");
+ set_temp_autoboot_opt(cui, &autoboot_override_keys[i].opt);
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * cui_process_key - Process input on stdin.
+ */
+
+static int cui_process_key(void *arg)
+{
+ struct cui *cui = cui_from_arg(arg);
+ unsigned int i;
+ char *sequence;
+ int grab;
+
+ assert(cui->current);
+
+ for (;;) {
+ int c = getch();
+
+ pb_debug("%s: got key %d\n", __func__, c);
+
+ if (c == ERR)
+ break;
+
+ if (c == 27) {
+ /*
+ * If this is a console code sequence try to parse it
+ * and don't treat this as a key press.
+ */
+ grab = getch();
+ if (grab != ERR && grab != 27) {
+ ungetch(grab);
+ pb_debug("%s: Caught unhandled command sequence\n",
+ __func__);
+ sequence = handle_control_sequence(cui, c);
+ pb_debug("Caught sequence ");
+ if (sequence) {
+ pb_debug("(%zu): ", strlen(sequence));
+ for (i = 0; i < strlen(sequence); i++)
+ pb_debug("0%o ", sequence[i]);
+ pb_debug("\n");
+ } else
+ pb_debug("(0): (none)\n");
+ continue;
+ }
+ }
+
+ if (cui->preboot_mode) {
+ /* Turn curses options back on if the user interacts */
+ cui->preboot_mode = false;
+ cui_set_curses_options(true);
+ }
+
+ if (!cui->has_input && key_cancels_boot(c)) {
+ cui->has_input = true;
+ if (cui->client) {
+ pb_log("UI input received (key = %d), aborting "
+ "default boot\n", c);
+ discover_client_cancel_default(cui->client);
+ } else {
+ pb_log("UI input received (key = %d), aborting "
+ "once server connects\n", c);
+ }
+ }
+
+ if (process_global_keys(cui, c))
+ continue;
+
+ cui->current->process_key(cui->current, c);