From 8daa83a594a2e98f39d764422bfbdbc62c9efd44 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 19:20:00 +0200 Subject: Adding upstream version 2:4.20.0+dfsg. Signed-off-by: Daniel Baumann --- source3/utils/regedit_dialog.c | 2328 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2328 insertions(+) create mode 100644 source3/utils/regedit_dialog.c (limited to 'source3/utils/regedit_dialog.c') diff --git a/source3/utils/regedit_dialog.c b/source3/utils/regedit_dialog.c new file mode 100644 index 0000000..d1cb45f --- /dev/null +++ b/source3/utils/regedit_dialog.c @@ -0,0 +1,2328 @@ +/* + * Samba Unix/Linux SMB client library + * Registry Editor + * Copyright (C) Christopher Davis 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "includes.h" +#include "regedit.h" +#include "regedit_dialog.h" +#include "regedit_valuelist.h" +#include "regedit_hexedit.h" +#include "util_reg.h" +#include "lib/registry/registry.h" +#include "lib/util/smb_strtox.h" +#include +#include + +static char *string_trim_n(TALLOC_CTX *ctx, const char *buf, size_t n) +{ + char *str; + + str = talloc_strndup(ctx, buf, n); + + if (str) { + trim_string(str, " ", " "); + } + + return str; +} + +static char *string_trim(TALLOC_CTX *ctx, const char *buf) +{ + char *str; + + str = talloc_strdup(ctx, buf); + + if (str) { + trim_string(str, " ", " "); + } + + return str; +} + +static int dialog_free(struct dialog *dia) +{ + dialog_destroy(dia); + + return 0; +} + +static bool default_validator(struct dialog *dia, struct dialog_section *sect, + void *arg) +{ + return true; +} + +struct dialog *dialog_new(TALLOC_CTX *ctx, short color, const char *title, + int y, int x) +{ + struct dialog *dia; + + dia = talloc_zero(ctx, struct dialog); + if (dia == NULL) { + return NULL; + } + + talloc_set_destructor(dia, dialog_free); + + dia->title = talloc_strdup(dia, title); + if (dia->title == NULL) { + goto fail; + } + dia->x = x; + dia->y = y; + dia->color = color; + dia->submit = default_validator; + + return dia; + +fail: + talloc_free(dia); + + return NULL; + +} + +void dialog_set_submit_cb(struct dialog *dia, dialog_submit_cb cb, void *arg) +{ + dia->submit = cb; + dia->submit_arg = arg; +} + +static void center_above_window(int *nlines, int *ncols, int *y, int *x) +{ + int centery, centerx; + + centery = LINES / 2; + centerx = COLS / 2; + *y = 0; + *x = 0; + + if (*nlines > LINES) { + *nlines = LINES; + } + if (*ncols > COLS) { + *ncols = COLS; + } + + if (*nlines/2 < centery) { + *y = centery - *nlines / 2; + } + if (*ncols/2 < centerx) { + *x = centerx - *ncols / 2; + } +} + +void dialog_section_destroy(struct dialog_section *section) +{ + if (section->ops->destroy) { + section->ops->destroy(section); + } + if (section->window) { + delwin(section->window); + section->window = NULL; + } +} + +void dialog_section_init(struct dialog_section *section, + const struct dialog_section_ops *ops, + int nlines, int ncols) +{ + section->ops = ops; + section->nlines = nlines; + section->ncols = ncols; +} + +const char *dialog_section_get_name(struct dialog_section *section) +{ + return section->name; +} + +void dialog_section_set_name(struct dialog_section *section, const char *name) +{ + TALLOC_FREE(section->name); + section->name = talloc_strdup(section, name); +} + +void dialog_section_set_justify(struct dialog_section *section, + enum section_justify justify) +{ + section->justify = justify; +} + +/* append a section to the dialog's circular list */ +void dialog_append_section(struct dialog *dia, + struct dialog_section *section) +{ + SMB_ASSERT(section != NULL); + + if (!dia->head_section) { + dia->head_section = section; + } + if (dia->tail_section) { + dia->tail_section->next = section; + } + section->prev = dia->tail_section; + section->next = dia->head_section; + dia->head_section->prev = section; + dia->tail_section = section; +} + +struct dialog_section *dialog_find_section(struct dialog *dia, const char *name) +{ + struct dialog_section *section = dia->head_section; + + do { + if (section->name && strequal(section->name, name)) { + return section; + } + section = section->next; + } while (section != dia->head_section); + + return NULL; +} + +static void section_on_input(struct dialog *dia, int c) +{ + struct dialog_section *section = dia->current_section; + + if (!section->ops->on_input) { + return; + } + section->ops->on_input(dia, section, c); +} + +static bool section_on_tab(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_tab) { + return false; + } + return section->ops->on_tab(dia, section); +} + +static bool section_on_btab(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_btab) { + return false; + } + return section->ops->on_btab(dia, section); +} + +static bool section_on_up(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_up) { + return false; + } + return section->ops->on_up(dia, section); +} + +static bool section_on_down(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_down) { + return false; + } + return section->ops->on_down(dia, section); +} + +static bool section_on_left(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_left) { + return false; + } + return section->ops->on_left(dia, section); +} + +static bool section_on_right(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_right) { + return false; + } + return section->ops->on_right(dia, section); +} + +static enum dialog_action section_on_enter(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_enter) { + return DIALOG_OK; + } + return section->ops->on_enter(dia, section); +} + +static bool section_on_focus(struct dialog *dia, bool forward) +{ + struct dialog_section *section = dia->current_section; + + if (!section->ops->on_focus) { + return false; + } + return section->ops->on_focus(dia, section, forward); +} + +static void section_on_leave_focus(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (section->ops->on_leave_focus) { + section->ops->on_leave_focus(dia, section); + } +} + +static void section_set_next_focus(struct dialog *dia) +{ + section_on_leave_focus(dia); + + do { + dia->current_section = dia->current_section->next; + } while (!section_on_focus(dia, true)); +} + +static void section_set_previous_focus(struct dialog *dia) +{ + section_on_leave_focus(dia); + + do { + dia->current_section = dia->current_section->prev; + } while (!section_on_focus(dia, false)); +} + +WERROR dialog_create(struct dialog *dia) +{ + WERROR rv = WERR_OK; + int row, col; + int nlines, ncols; + struct dialog_section *section; + + nlines = 0; + ncols = 0; + SMB_ASSERT(dia->head_section != NULL); + + /* calculate total size based on sections */ + section = dia->head_section; + do { + nlines += section->nlines; + ncols = MAX(ncols, section->ncols); + section = section->next; + } while (section != dia->head_section); + + /* fill in widths for sections that expand */ + section = dia->head_section; + do { + if (section->ncols < 0) { + section->ncols = ncols; + } + section = section->next; + } while (section != dia->head_section); + + /* create window for dialog */ + nlines += 4; + ncols += 6; + dia->pad = newpad(nlines, ncols); + if (dia->pad == NULL) { + rv = WERR_NOT_ENOUGH_MEMORY; + goto fail; + } + dia->centered = false; + if (dia->y < 0 || dia->x < 0) { + dia->centered = true; + center_above_window(&nlines, &ncols, &dia->y, &dia->x); + } + dia->window = newwin(nlines, ncols, dia->y, dia->x); + if (dia->window == NULL) { + rv = WERR_NOT_ENOUGH_MEMORY; + goto fail; + } + dia->panel = new_panel(dia->window); + if (dia->panel == NULL) { + rv = WERR_NOT_ENOUGH_MEMORY; + goto fail; + } + + /* setup color and border */ + getmaxyx(dia->pad, nlines, ncols); + wbkgdset(dia->pad, ' ' | COLOR_PAIR(dia->color)); + wclear(dia->pad); + mvwhline(dia->pad, 1, 2, 0, ncols - 4); + mvwhline(dia->pad, nlines - 2, 2, 0, ncols - 4); + mvwvline(dia->pad, 2, 1, 0, nlines - 4); + mvwvline(dia->pad, 2, ncols - 2, 0, nlines - 4); + mvwaddch(dia->pad, 1, 1, ACS_ULCORNER); + mvwaddch(dia->pad, 1, ncols - 2, ACS_URCORNER); + mvwaddch(dia->pad, nlines - 2, 1, ACS_LLCORNER); + mvwaddch(dia->pad, nlines - 2, ncols - 2, ACS_LRCORNER); + col = ncols / 2 - MIN(strlen(dia->title) + 2, ncols) / 2; + mvwprintw(dia->pad, 1, col, " %s ", dia->title); + + /* create subwindows for each section */ + row = 2; + section = dia->head_section; + do { + col = 3; + + switch (section->justify) { + case SECTION_JUSTIFY_LEFT: + break; + case SECTION_JUSTIFY_CENTER: + col += (ncols - 6)/ 2 - section->ncols / 2; + break; + case SECTION_JUSTIFY_RIGHT: + break; + } + + section->window = subpad(dia->pad, section->nlines, + section->ncols, row, col); + if (section->window == NULL) { + rv = WERR_NOT_ENOUGH_MEMORY; + goto fail; + } + SMB_ASSERT(section->ops->create != NULL); + rv = section->ops->create(dia, section); + row += section->nlines; + section = section->next; + } while (section != dia->head_section && W_ERROR_IS_OK(rv)); + + dia->current_section = dia->head_section; + section_set_next_focus(dia); + +fail: + return rv; +} + +void dialog_show(struct dialog *dia) +{ + int nlines, ncols; + int pad_y, pad_x; + int y, x; + int rv; + + touchwin(dia->pad); + getmaxyx(dia->window, nlines, ncols); + getmaxyx(dia->pad, pad_y, pad_x); + y = 0; + if (pad_y > nlines) { + y = (pad_y - nlines) / 2; + } + x = 0; + if (pad_x > ncols) { + x = (pad_x - ncols) / 2; + } + rv = copywin(dia->pad, dia->window, y, x, 0, 0, + nlines - 1, ncols - 1, false); + SMB_ASSERT(rv == OK); + + getyx(dia->pad, pad_y, pad_x); + wmove(dia->window, pad_y - y, pad_x - x); + touchwin(dia->window); + wnoutrefresh(dia->window); +} + +void dialog_destroy(struct dialog *dia) +{ + struct dialog_section *section; + + section = dia->head_section; + do { + dialog_section_destroy(section); + section = section->next; + } while (section != dia->head_section); + + if (dia->panel) { + del_panel(dia->panel); + dia->panel = NULL; + } + if (dia->window) { + delwin(dia->window); + dia->window = NULL; + } +} + +static int dialog_getch(struct dialog *dia) +{ + int c; + + c = regedit_getch(); + if (c == KEY_RESIZE) { + int nlines, ncols, y, x; + int pad_nlines, pad_ncols; + int win_nlines, win_ncols; + + getmaxyx(dia->window, win_nlines, win_ncols); + getmaxyx(dia->pad, pad_nlines, pad_ncols); + getbegyx(dia->window, y, x); + + nlines = pad_nlines; + ncols = pad_ncols; + + if (dia->centered) { + center_above_window(&nlines, &ncols, &y, &x); + } else { + if (nlines + y > LINES) { + if (nlines > LINES) { + y = 0; + } else { + y = LINES - nlines; + } + } + if (ncols + x > COLS) { + if (ncols > COLS) { + x = 0; + } else { + x = COLS - ncols; + } + } + } + if (nlines != win_nlines || ncols != win_ncols) { + wresize(dia->window, nlines, ncols); + replace_panel(dia->panel, dia->window); + } + move_panel(dia->panel, y, x); + } + + return c; +} + +bool dialog_handle_input(struct dialog *dia, WERROR *err, + enum dialog_action *action) +{ + int c; + + *err = WERR_OK; + + c = dialog_getch(dia); + + switch (c) { + case '\t': + if (!section_on_tab(dia)) { + section_set_next_focus(dia); + } + break; + case KEY_BTAB: + if (!section_on_btab(dia)) { + section_set_previous_focus(dia); + } + break; + case KEY_UP: + if (!section_on_up(dia)) { + section_set_previous_focus(dia); + } + break; + case KEY_DOWN: + if (!section_on_down(dia)) { + section_set_next_focus(dia); + } + break; + case KEY_LEFT: + if (!section_on_left(dia)) { + section_set_previous_focus(dia); + } + break; + case KEY_RIGHT: + if (!section_on_right(dia)) { + section_set_next_focus(dia); + } + break; + case '\n': + case KEY_ENTER: + *action = section_on_enter(dia); + switch (*action) { + case DIALOG_IGNORE: + break; + case DIALOG_CANCEL: + return false; + case DIALOG_OK: + return !dia->submit(dia, dia->current_section, + dia->submit_arg); + } + break; + case 27: /* ESC */ + return false; + default: + section_on_input(dia, c); + break; + } + + return true; +} + +void dialog_modal_loop(struct dialog *dia, WERROR *err, + enum dialog_action *action) +{ + do { + dialog_show(dia); + update_panels(); + doupdate(); + } while (dialog_handle_input(dia, err, action)); +} + +/* text label */ +struct dialog_section_label { + struct dialog_section section; + char **text; +}; + +static WERROR label_create(struct dialog *dia, struct dialog_section *section) +{ + int row; + struct dialog_section_label *label = + talloc_get_type_abort(section, struct dialog_section_label); + + for (row = 0; row < section->nlines; ++row) { + mvwaddstr(section->window, row, 0, label->text[row]); + } + + return WERR_OK; +} + +struct dialog_section_ops label_ops = { + .create = label_create, +}; + +static int label_free(struct dialog_section_label *label) +{ + dialog_section_destroy(&label->section); + return 0; +} + +struct dialog_section *dialog_section_label_new_va(TALLOC_CTX *ctx, + const char *msg, va_list ap) +{ + struct dialog_section_label *label; + char *tmp, *ptmp, *line, *saveptr; + int nlines, ncols; + + label = talloc_zero(ctx, struct dialog_section_label); + if (label == NULL) { + return NULL; + } + talloc_set_destructor(label, label_free); + tmp = talloc_vasprintf(label, msg, ap); + if (tmp == NULL) { + goto fail; + } + + for (nlines = 0, ncols = 0, ptmp = tmp; + (line = strtok_r(ptmp, "\n", &saveptr)) != NULL; + ++nlines) { + ptmp = NULL; + label->text = talloc_realloc(label, label->text, + char *, nlines + 1); + if (label->text == NULL) { + goto fail; + } + ncols = MAX(ncols, strlen(line)); + label->text[nlines] = talloc_strdup(label->text, line); + if (label->text[nlines] == NULL) { + goto fail; + } + } + talloc_free(tmp); + dialog_section_init(&label->section, &label_ops, nlines, ncols); + + return &label->section; + +fail: + talloc_free(label); + return NULL; +} + +struct dialog_section *dialog_section_label_new(TALLOC_CTX *ctx, + const char *msg, ...) +{ + va_list ap; + struct dialog_section *rv; + + va_start(ap, msg); + rv = dialog_section_label_new_va(ctx, msg, ap); + va_end(ap); + + return rv; +} + +/* horizontal separator */ +struct dialog_section_hsep { + struct dialog_section section; + int sep; +}; + +static WERROR hsep_create(struct dialog *dia, struct dialog_section *section) +{ + int y, x; + struct dialog_section_hsep *hsep = + talloc_get_type_abort(section, struct dialog_section_hsep); + + whline(section->window, hsep->sep, section->ncols); + + if (hsep->sep == 0 || hsep->sep == ACS_HLINE) { + /* change the border characters around this section to + tee chars */ + getparyx(section->window, y, x); + mvwaddch(dia->pad, y, x - 1, ACS_HLINE); + mvwaddch(dia->pad, y, x - 2, ACS_LTEE); + mvwaddch(dia->pad, y, x + section->ncols, ACS_HLINE); + mvwaddch(dia->pad, y, x + section->ncols + 1, ACS_RTEE); + } + + return WERR_OK; +} + +struct dialog_section_ops hsep_ops = { + .create = hsep_create +}; + +static int hsep_free(struct dialog_section_hsep *hsep) +{ + dialog_section_destroy(&hsep->section); + return 0; +} + +struct dialog_section *dialog_section_hsep_new(TALLOC_CTX *ctx, int sep) +{ + struct dialog_section_hsep *hsep; + + hsep = talloc_zero(ctx, struct dialog_section_hsep); + if (hsep) { + talloc_set_destructor(hsep, hsep_free); + dialog_section_init(&hsep->section, &hsep_ops, 1, -1); + hsep->sep = sep; + } + + return &hsep->section; +} + +/* text input field */ +struct dialog_section_text_field { + struct dialog_section section; + unsigned opts; + FIELD *field[2]; + FORM *form; + int length; +}; + +static int get_cursor_col(struct dialog_section_text_field *field) +{ + int col; + + col = field->form->curcol + field->form->begincol; + + return col; +} + +static WERROR text_field_create(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + text_field->field[0] = new_field(section->nlines, section->ncols, + 0, 0, 0, 0); + if (text_field->field[0] == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + set_field_back(text_field->field[0], A_REVERSE); + set_field_opts(text_field->field[0], text_field->opts); + + text_field->form = new_form(text_field->field); + if (text_field->form == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + + set_form_win(text_field->form, dia->window); + set_form_sub(text_field->form, section->window); + set_current_field(text_field->form, text_field->field[0]); + post_form(text_field->form); + + return WERR_OK; +} + +static void text_field_destroy(struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + if (text_field->form) { + unpost_form(text_field->form); + free_form(text_field->form); + text_field->form = NULL; + } + if (text_field->field[0]) { + free_field(text_field->field[0]); + text_field->field[0] = NULL; + } +} + +static void text_field_on_input(struct dialog *dia, + struct dialog_section *section, + int c) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + switch (c) { + case KEY_BACKSPACE: + if (text_field->length) { + text_field->length--; + } + form_driver(text_field->form, REQ_DEL_PREV); + break; + case '\x7f': + case KEY_DC: + if (text_field->length) { + text_field->length--; + } + form_driver(text_field->form, REQ_DEL_CHAR); + break; + default: + text_field->length++; + form_driver(text_field->form, c); + break; + } +} + +static bool text_field_on_up(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + if (section->nlines > 1) { + form_driver(text_field->form, REQ_UP_CHAR); + return true; + } + return false; +} + +static bool text_field_on_down(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + if (section->nlines > 1) { + form_driver(text_field->form, REQ_DOWN_CHAR); + return true; + } + return false; +} + +static bool text_field_on_left(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + form_driver(text_field->form, REQ_LEFT_CHAR); + + return true; +} + +static bool text_field_on_right(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + if (section->nlines > 1 || + get_cursor_col(text_field) < text_field->length) { + form_driver(text_field->form, REQ_RIGHT_CHAR); + } + + return true; +} + +static enum dialog_action text_field_on_enter(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + if (section->nlines > 1) { + text_field->length += text_field->form->cols; + form_driver(text_field->form, REQ_NEW_LINE); + return DIALOG_IGNORE; + } + + return DIALOG_OK; +} + +static bool text_field_on_focus(struct dialog *dia, + struct dialog_section *section, bool forward) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + pos_form_cursor(text_field->form); + + return true; +} + +struct dialog_section_ops text_field_ops = { + .create = text_field_create, + .destroy = text_field_destroy, + .on_input = text_field_on_input, + .on_up = text_field_on_up, + .on_down = text_field_on_down, + .on_left = text_field_on_left, + .on_right = text_field_on_right, + .on_enter = text_field_on_enter, + .on_focus = text_field_on_focus +}; + +static int text_field_free(struct dialog_section_text_field *text_field) +{ + dialog_section_destroy(&text_field->section); + return 0; +} + +struct dialog_section *dialog_section_text_field_new(TALLOC_CTX *ctx, + int height, int width) +{ + struct dialog_section_text_field *text_field; + + text_field = talloc_zero(ctx, struct dialog_section_text_field); + if (text_field == NULL) { + return NULL; + } + talloc_set_destructor(text_field, text_field_free); + dialog_section_init(&text_field->section, &text_field_ops, + height, width); + text_field->opts = O_ACTIVE | O_PUBLIC | O_EDIT | O_VISIBLE | O_NULLOK; + + return &text_field->section; +} + +const char *dialog_section_text_field_get(TALLOC_CTX *ctx, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + form_driver(text_field->form, REQ_VALIDATION); + + return string_trim(ctx, field_buffer(text_field->field[0], 0)); +} + +void dialog_section_text_field_set(struct dialog_section *section, + const char *s) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + text_field->length = strlen(s); + set_field_buffer(text_field->field[0], 0, s); +} + +const char **dialog_section_text_field_get_lines(TALLOC_CTX *ctx, + struct dialog_section *section) +{ + int rows, cols, max; + const char **arr; + size_t i; + const char *buf; + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + form_driver(text_field->form, REQ_VALIDATION); + buf = field_buffer(text_field->field[0], 0); + + dynamic_field_info(text_field->field[0], &rows, &cols, &max); + + arr = talloc_zero_array(ctx, const char *, rows + 1); + if (arr == NULL) { + return NULL; + } + for (i = 0; *buf; ++i, buf += cols) { + SMB_ASSERT(i < rows); + arr[i] = string_trim_n(arr, buf, cols); + } + + return arr; +} + +WERROR dialog_section_text_field_set_lines(TALLOC_CTX *ctx, + struct dialog_section *section, + const char **array) +{ + int rows, cols, max; + size_t padding, length, idx; + const char **arrayp; + char *buf = NULL; + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + dynamic_field_info(text_field->field[0], &rows, &cols, &max); + /* try to fit each string on it's own line. each line + needs to be padded with whitespace manually, since + ncurses fields do not have newlines. */ + for (idx = 0, arrayp = array; *arrayp != NULL; ++arrayp) { + length = MIN(strlen(*arrayp), cols); + padding = cols - length; + buf = talloc_realloc(ctx, buf, char, + talloc_array_length(buf) + + length + padding + 1); + if (buf == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + memcpy(&buf[idx], *arrayp, length); + idx += length; + memset(&buf[idx], ' ', padding); + idx += padding; + buf[idx] = '\0'; + } + + set_field_buffer(text_field->field[0], 0, buf); + talloc_free(buf); + + return WERR_OK; +} + +bool dialog_section_text_field_get_int(struct dialog_section *section, + long long *out) +{ + bool rv; + const char *buf; + char *endp; + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + form_driver(text_field->form, REQ_VALIDATION); + + buf = string_trim(section, field_buffer(text_field->field[0], 0)); + if (buf == NULL) { + return false; + } + *out = strtoll(buf, &endp, 0); + rv = true; + if (endp == buf || endp == NULL || endp[0] != '\0') { + rv = false; + } + + return rv; +} + + +bool dialog_section_text_field_get_uint(struct dialog_section *section, + unsigned long long *out) +{ + const char *buf; + int error = 0; + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + form_driver(text_field->form, REQ_VALIDATION); + + buf = string_trim(section, field_buffer(text_field->field[0], 0)); + if (buf == NULL) { + return false; + } + *out = smb_strtoull(buf, NULL, 0, &error, SMB_STR_FULL_STR_CONV); + if (error != 0) { + return false; + } + + return true; +} + +/* hex editor field */ +struct dialog_section_hexedit { + struct dialog_section section; + struct hexedit *buf; +}; + +#define HEXEDIT_MIN_SIZE 1 +static WERROR hexedit_create(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + hexedit->buf = hexedit_new(dia, section->window, NULL, + HEXEDIT_MIN_SIZE); + if (hexedit->buf == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + + hexedit_refresh(hexedit->buf); + + return WERR_OK; +} + +static void hexedit_destroy(struct dialog_section *section) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + if (hexedit->buf) { + TALLOC_FREE(hexedit->buf); + } +} + +static void hexedit_on_input(struct dialog *dia, + struct dialog_section *section, + int c) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + switch (c) { + case KEY_BACKSPACE: + hexedit_driver(hexedit->buf, HE_BACKSPACE); + break; + case '\x7f': + case KEY_DC: + hexedit_driver(hexedit->buf, HE_DELETE); + break; + default: + hexedit_driver(hexedit->buf, c); + break; + } +} + +static bool hexedit_on_up(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + hexedit_driver(hexedit->buf, HE_CURSOR_UP); + + return true; +} + +static bool hexedit_on_down(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + hexedit_driver(hexedit->buf, HE_CURSOR_DOWN); + + return true; +} + +static bool hexedit_on_left(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + hexedit_driver(hexedit->buf, HE_CURSOR_LEFT); + + return true; +} + +static bool hexedit_on_right(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + hexedit_driver(hexedit->buf, HE_CURSOR_RIGHT); + + return true; +} + +static enum dialog_action hexedit_on_enter(struct dialog *dia, + struct dialog_section *section) +{ + return DIALOG_IGNORE; +} + +static bool hexedit_on_focus(struct dialog *dia, + struct dialog_section *section, bool forward) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + hexedit_set_cursor(hexedit->buf); + + return true; +} + +struct dialog_section_ops hexedit_ops = { + .create = hexedit_create, + .destroy = hexedit_destroy, + .on_input = hexedit_on_input, + .on_up = hexedit_on_up, + .on_down = hexedit_on_down, + .on_left = hexedit_on_left, + .on_right = hexedit_on_right, + .on_enter = hexedit_on_enter, + .on_focus = hexedit_on_focus +}; + +static int hexedit_free(struct dialog_section_hexedit *hexedit) +{ + dialog_section_destroy(&hexedit->section); + return 0; +} + +struct dialog_section *dialog_section_hexedit_new(TALLOC_CTX *ctx, int height) +{ + struct dialog_section_hexedit *hexedit; + + hexedit = talloc_zero(ctx, struct dialog_section_hexedit); + if (hexedit == NULL) { + return NULL; + } + talloc_set_destructor(hexedit, hexedit_free); + dialog_section_init(&hexedit->section, &hexedit_ops, + height, LINE_WIDTH); + + return &hexedit->section; +} + +WERROR dialog_section_hexedit_set_buf(struct dialog_section *section, + const void *data, size_t size) +{ + WERROR rv; + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + SMB_ASSERT(hexedit->buf != NULL); + + rv = hexedit_set_buf(hexedit->buf, data, size); + if (W_ERROR_IS_OK(rv)) { + hexedit_refresh(hexedit->buf); + hexedit_set_cursor(hexedit->buf); + } + + return rv; +} + +void dialog_section_hexedit_get_buf(struct dialog_section *section, + const void **data, size_t *size) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + SMB_ASSERT(hexedit->buf != NULL); + *data = hexedit_get_buf(hexedit->buf); + *size = hexedit_get_buf_len(hexedit->buf); +} + +WERROR dialog_section_hexedit_resize(struct dialog_section *section, + size_t size) +{ + WERROR rv; + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + SMB_ASSERT(hexedit->buf != NULL); + rv = hexedit_resize_buffer(hexedit->buf, size); + if (W_ERROR_IS_OK(rv)) { + hexedit_refresh(hexedit->buf); + } + + return rv; +} + + +/* button box */ +struct dialog_section_buttons { + struct dialog_section section; + struct button_spec *spec; + int current_button; +}; + +static void buttons_unhighlight(struct dialog_section_buttons *buttons) +{ + short pair; + attr_t attr; + + /* + * Some GCC versions will complain if the macro version of + * wattr_get is used. So we should enforce the use of the + * function instead. See: + * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html + */ + (wattr_get)(buttons->section.window, &attr, &pair, NULL); + mvwchgat(buttons->section.window, 0, 0, -1, A_NORMAL, pair, NULL); + wnoutrefresh(buttons->section.window); +} + +static void buttons_highlight(struct dialog_section_buttons *buttons) +{ + struct button_spec *spec = &buttons->spec[buttons->current_button]; + short pair; + attr_t attr; + + /* + * Some GCC versions will complain if the macro version of + * wattr_get is used. So we should enforce the use of the + * function instead. See: + * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html + */ + (wattr_get)(buttons->section.window, &attr, &pair, NULL); + mvwchgat(buttons->section.window, 0, 0, -1, A_NORMAL, pair, NULL); + mvwchgat(buttons->section.window, 0, spec->col, + strlen(spec->label), A_REVERSE, pair, NULL); + wmove(buttons->section.window, 0, spec->col + 2); + wcursyncup(buttons->section.window); + wnoutrefresh(buttons->section.window); +} + +static bool buttons_highlight_next(struct dialog_section_buttons *buttons) +{ + if (buttons->current_button < talloc_array_length(buttons->spec) - 1) { + buttons->current_button++; + buttons_highlight(buttons); + return true; + } + return false; +} + +static bool buttons_highlight_previous(struct dialog_section_buttons *buttons) +{ + if (buttons->current_button > 0) { + buttons->current_button--; + buttons_highlight(buttons); + return true; + } + return false; +} + +static WERROR buttons_create(struct dialog *dia, + struct dialog_section *section) +{ + size_t i, nbuttons; + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); + + nbuttons = talloc_array_length(buttons->spec); + for (i = 0; i < nbuttons; ++i) { + struct button_spec *spec = &buttons->spec[i]; + mvwaddstr(section->window, 0, spec->col, spec->label); + } + + buttons->current_button = 0; + + return WERR_OK; +} + +static bool buttons_on_btab(struct dialog *dia, struct dialog_section *section) +{ + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); + + return buttons_highlight_previous(buttons); +} + +static bool buttons_on_tab(struct dialog *dia, struct dialog_section *section) +{ + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); + + return buttons_highlight_next(buttons); +} + +static enum dialog_action buttons_on_enter(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); + struct button_spec *spec = &buttons->spec[buttons->current_button]; + + if (spec->on_enter) { + return spec->on_enter(dia, section); + } + + return spec->action; +} + +static bool buttons_on_focus(struct dialog *dia, + struct dialog_section *section, + bool forward) +{ + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); + + if (forward) { + buttons->current_button = 0; + } else { + buttons->current_button = talloc_array_length(buttons->spec) - 1; + } + buttons_highlight(buttons); + + return true; +} + +static void buttons_on_leave_focus(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); + buttons_unhighlight(buttons); +} + +struct dialog_section_ops buttons_ops = { + .create = buttons_create, + .on_tab = buttons_on_tab, + .on_btab = buttons_on_btab, + .on_up = buttons_on_btab, + .on_down = buttons_on_tab, + .on_left = buttons_on_btab, + .on_right = buttons_on_tab, + .on_enter = buttons_on_enter, + .on_focus = buttons_on_focus, + .on_leave_focus = buttons_on_leave_focus +}; + +static int buttons_free(struct dialog_section_buttons *buttons) +{ + dialog_section_destroy(&buttons->section); + return 0; +} + +struct dialog_section *dialog_section_buttons_new(TALLOC_CTX *ctx, + const struct button_spec *spec) +{ + struct dialog_section_buttons *buttons; + size_t i, nbuttons; + int width; + + buttons = talloc_zero(ctx, struct dialog_section_buttons); + if (buttons == NULL) { + return NULL; + } + talloc_set_destructor(buttons, buttons_free); + + for (nbuttons = 0; spec[nbuttons].label; ++nbuttons) { + } + buttons->spec = talloc_zero_array(buttons, struct button_spec, nbuttons); + if (buttons->spec == NULL) { + goto fail; + } + + for (width = 0, i = 0; i < nbuttons; ++i) { + buttons->spec[i] = spec[i]; + buttons->spec[i].label = talloc_asprintf(buttons->spec, + "[ %s ]", + spec[i].label); + if (!buttons->spec[i].label) { + goto fail; + } + + buttons->spec[i].col = width; + width += strlen(buttons->spec[i].label); + if (i != nbuttons - 1) { + ++width; + } + } + + dialog_section_init(&buttons->section, &buttons_ops, 1, width); + + return &buttons->section; + +fail: + talloc_free(buttons); + return NULL; +} + +/* options */ +struct dialog_section_options { + struct dialog_section section; + struct option_spec *spec; + int current_option; + bool single_select; +}; + +static void options_unhighlight(struct dialog_section_options *options) +{ + short pair; + attr_t attr; + size_t row; + + /* + * Some GCC versions will complain if the macro version of + * wattr_get is used. So we should enforce the use of the + * function instead. See: + * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html + */ + (wattr_get)(options->section.window, &attr, &pair, NULL); + for (row = 0; row < options->section.nlines; ++row) { + mvwchgat(options->section.window, row, 0, -1, A_NORMAL, pair, NULL); + } + wnoutrefresh(options->section.window); +} + +static void options_highlight(struct dialog_section_options *options) +{ + struct option_spec *spec = &options->spec[options->current_option]; + short pair; + attr_t attr; + size_t row; + + /* + * Some GCC versions will complain if the macro version of + * wattr_get is used. So we should enforce the use of the + * function instead. See: + * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html + */ + (wattr_get)(options->section.window, &attr, &pair, NULL); + for (row = 0; row < options->section.nlines; ++row) { + mvwchgat(options->section.window, row, 0, -1, A_NORMAL, pair, NULL); + } + mvwchgat(options->section.window, spec->row, spec->col, + strlen(spec->label), A_REVERSE, pair, NULL); + wmove(options->section.window, spec->row, spec->col + 4); + wcursyncup(options->section.window); + wnoutrefresh(options->section.window); +} + +static void options_render_state(struct dialog_section_options *options) +{ + size_t i, noptions; + + noptions = talloc_array_length(options->spec); + for (i = 0; i < noptions; ++i) { + struct option_spec *spec = &options->spec[i]; + char c = ' '; + if (*spec->state) + c = 'x'; + mvwaddch(options->section.window, + spec->row, spec->col + 1, c); + wnoutrefresh(options->section.window); + } +} + +static bool options_highlight_next(struct dialog_section_options *options) +{ + if (options->current_option < talloc_array_length(options->spec) - 1) { + options->current_option++; + options_highlight(options); + return true; + } + return false; +} + +static bool options_highlight_previous(struct dialog_section_options *options) +{ + if (options->current_option > 0) { + options->current_option--; + options_highlight(options); + return true; + } + return false; +} + +static WERROR options_create(struct dialog *dia, + struct dialog_section *section) +{ + size_t i, noptions; + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + + noptions = talloc_array_length(options->spec); + for (i = 0; i < noptions; ++i) { + struct option_spec *spec = &options->spec[i]; + mvwaddstr(section->window, spec->row, spec->col, + spec->label); + } + + options->current_option = 0; + options_render_state(options); + + return WERR_OK; +} + +static bool options_on_btab(struct dialog *dia, struct dialog_section *section) +{ + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + + return options_highlight_previous(options); +} + +static bool options_on_tab(struct dialog *dia, struct dialog_section *section) +{ + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + + return options_highlight_next(options); +} + +static void options_on_input(struct dialog *dia, struct dialog_section *section, int c) +{ + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + + if (c == ' ') { + struct option_spec *spec = &options->spec[options->current_option]; + if (options->single_select) { + size_t i, noptions; + noptions = talloc_array_length(options->spec); + for (i = 0; i < noptions; ++i) { + *(options->spec[i].state) = false; + } + } + *spec->state = !*spec->state; + options_unhighlight(options); + options_render_state(options); + options_highlight(options); + } +} + +static enum dialog_action options_on_enter(struct dialog *dia, struct dialog_section *section) +{ + options_on_input(dia, section, ' '); + return DIALOG_OK; +} + +static bool options_on_focus(struct dialog *dia, + struct dialog_section *section, + bool forward) +{ + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + + if (forward) { + options->current_option = 0; + } else { + options->current_option = talloc_array_length(options->spec) - 1; + } + options_highlight(options); + + return true; +} + +static void options_on_leave_focus(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + options_unhighlight(options); +} + +struct dialog_section_ops options_ops = { + .create = options_create, + .on_tab = options_on_tab, + .on_btab = options_on_btab, + .on_up = options_on_btab, + .on_down = options_on_tab, + .on_left = options_on_btab, + .on_right = options_on_tab, + .on_input = options_on_input, + .on_enter = options_on_enter, + .on_focus = options_on_focus, + .on_leave_focus = options_on_leave_focus +}; + +static int options_free(struct dialog_section_options *options) +{ + dialog_section_destroy(&options->section); + return 0; +} + +struct dialog_section *dialog_section_options_new(TALLOC_CTX *ctx, + const struct option_spec *spec, + int maxcol, bool single_select) +{ + struct dialog_section_options *options; + size_t i, noptions; + int width, maxwidth, maxrows; + + options = talloc_zero(ctx, struct dialog_section_options); + if (options == NULL) { + return NULL; + } + talloc_set_destructor(options, options_free); + + for (noptions = 0; spec[noptions].label; ++noptions) { + } + options->spec = talloc_zero_array(options, struct option_spec, noptions); + if (options->spec == NULL) { + goto fail; + } + + maxrows = noptions / maxcol; + if (noptions % maxcol) { + ++maxrows; + } + + for (width = 0, maxwidth = 0, i = 0; i < noptions; ++i) { + options->spec[i] = spec[i]; + options->spec[i].label = talloc_asprintf(options->spec, + "[ ] %s", + spec[i].label); + if (!options->spec[i].label) { + goto fail; + } + + options->spec[i].col = maxwidth; + options->spec[i].row = i % maxrows; + width = MAX(strlen(options->spec[i].label), width); + if (options->spec[i].row == maxrows - 1 || i == noptions - 1) { + maxwidth += width + 1; + width = 0; + } + } + + dialog_section_init(&options->section, &options_ops, maxrows, maxwidth - 1); + options->single_select = single_select; + + return &options->section; + +fail: + talloc_free(options); + return NULL; +} + + +enum input_type { + DLG_IN_LONG, + DLG_IN_ULONG, + DLG_IN_STR, +}; + +struct input_req { + TALLOC_CTX *ctx; + enum input_type type; + union { + void *out; + unsigned long *out_ulong; + long *out_long; + const char **out_str; + } out; +}; + +static bool input_on_submit(struct dialog *dia, struct dialog_section *section, + void *arg) +{ + struct input_req *req = arg; + struct dialog_section *data; + unsigned long long out_ulong; + long long out_long; + + data = dialog_find_section(dia, "input"); + + switch (req->type) { + case DLG_IN_LONG: + if (!dialog_section_text_field_get_int(data, &out_long)) { + dialog_notice(dia, DIA_ALERT, "Error", + "Input must be a number."); + return false; + } + if (out_long < LONG_MIN || out_long > LONG_MAX) { + dialog_notice(dia, DIA_ALERT, "Error", + "Number is out of range."); + return false; + } + *req->out.out_long = out_long; + break; + case DLG_IN_ULONG: + if (!dialog_section_text_field_get_uint(data, &out_ulong)) { + dialog_notice(dia, DIA_ALERT, "Error", + "Input must be a number greater than zero."); + return false; + } + if (out_ulong > ULONG_MAX) { + dialog_notice(dia, DIA_ALERT, "Error", + "Number is out of range."); + return false; + } + *req->out.out_ulong = out_ulong; + break; + case DLG_IN_STR: + *req->out.out_str = dialog_section_text_field_get(req->ctx, data); + break; + } + + return true; +} + +static int dialog_input_internal(TALLOC_CTX *ctx, void *output, + enum input_type type, + const char *title, + const char *msg, va_list ap) + PRINTF_ATTRIBUTE(5,0); + +static int dialog_input_internal(TALLOC_CTX *ctx, void *output, + enum input_type type, + const char *title, + const char *msg, va_list ap) +{ + WERROR err; + struct input_req req; + enum dialog_action action; + struct dialog *dia; + struct dialog_section *section; + struct button_spec spec[] = { + {.label = "OK", .action = DIALOG_OK}, + {.label = "Cancel", .action = DIALOG_CANCEL}, + { 0 } + }; + + req.ctx = ctx; + req.type = type; + req.out.out = output; + *req.out.out_str = NULL; + + dia = dialog_new(ctx, PAIR_BLACK_CYAN, title, -1, -1); + dialog_set_submit_cb(dia, input_on_submit, &req); + section = dialog_section_label_new_va(dia, msg, ap); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, ' '); + dialog_append_section(dia, section); + section = dialog_section_text_field_new(dia, 1, -1); + dialog_section_set_name(section, "input"); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, 0); + dialog_append_section(dia, section); + section = dialog_section_buttons_new(dia, spec); + dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER); + dialog_append_section(dia, section); + + dialog_create(dia); + dialog_show(dia); + dialog_modal_loop(dia, &err, &action); + talloc_free(dia); + + return action; +} + +int dialog_input(TALLOC_CTX *ctx, const char **output, const char *title, + const char *msg, ...) +{ + va_list ap; + int rv; + + va_start(ap, msg); + rv = dialog_input_internal(ctx, output, DLG_IN_STR, title, msg, ap); + va_end(ap); + + return rv; +} + +int dialog_input_ulong(TALLOC_CTX *ctx, unsigned long *output, + const char *title, const char *msg, ...) +{ + va_list ap; + int rv; + + va_start(ap, msg); + rv = dialog_input_internal(ctx, output, DLG_IN_ULONG, title, msg, ap); + va_end(ap); + + return rv; +} + +int dialog_input_long(TALLOC_CTX *ctx, long *output, + const char *title, const char *msg, ...) +{ + va_list ap; + int rv; + + va_start(ap, msg); + rv = dialog_input_internal(ctx, output, DLG_IN_LONG, title, msg, ap); + va_end(ap); + + return rv; +} + +int dialog_notice(TALLOC_CTX *ctx, enum dialog_type type, + const char *title, const char *msg, ...) +{ + va_list ap; + WERROR err; + enum dialog_action action; + struct dialog *dia; + struct dialog_section *section; + struct button_spec spec[3]; + + memset(&spec, '\0', sizeof(spec)); + spec[0].label = "OK"; + spec[0].action = DIALOG_OK; + if (type == DIA_CONFIRM) { + spec[1].label = "Cancel"; + spec[1].action = DIALOG_CANCEL; + } + + dia = dialog_new(ctx, PAIR_BLACK_CYAN, title, -1, -1); + va_start(ap, msg); + section = dialog_section_label_new_va(dia, msg, ap); + va_end(ap); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, 0); + dialog_append_section(dia, section); + section = dialog_section_buttons_new(dia, spec); + dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER); + dialog_append_section(dia, section); + + dialog_create(dia); + dialog_show(dia); + dialog_modal_loop(dia, &err, &action); + talloc_free(dia); + + return action; +} + + +struct edit_req { + uint32_t type; + uint32_t mode; + struct registry_key *key; + const struct value_item *vitem; +}; + +static WERROR fill_value_buffer(struct dialog *dia, struct edit_req *edit) +{ + char *tmp; + struct dialog_section *data; + + if (edit->vitem == NULL) { + return WERR_OK; + } + + data = dialog_find_section(dia, "data"); + SMB_ASSERT(data != NULL); + + switch (edit->mode) { + case REG_DWORD: { + uint32_t v = 0; + if (edit->vitem->data.length >= 4) { + v = IVAL(edit->vitem->data.data, 0); + } + tmp = talloc_asprintf(dia, "%u", (unsigned)v); + if (tmp == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + dialog_section_text_field_set(data, tmp); + talloc_free(tmp); + break; + } + case REG_SZ: + case REG_EXPAND_SZ: { + const char *s; + + if (!pull_reg_sz(dia, &edit->vitem->data, &s)) { + return WERR_NOT_ENOUGH_MEMORY; + } + dialog_section_text_field_set(data, s); + break; + } + case REG_MULTI_SZ: { + const char **array; + + if (!pull_reg_multi_sz(dia, &edit->vitem->data, &array)) { + return WERR_NOT_ENOUGH_MEMORY; + } + return dialog_section_text_field_set_lines(dia, data, array); + } + case REG_BINARY: + default: + return dialog_section_hexedit_set_buf(data, + edit->vitem->data.data, + edit->vitem->data.length); + } + + return WERR_OK; +} + +static bool value_exists(TALLOC_CTX *ctx, const struct registry_key *key, + const char *name) +{ + uint32_t type; + DATA_BLOB blob; + WERROR rv; + + rv = reg_key_get_value_by_name(ctx, key, name, &type, &blob); + + return W_ERROR_IS_OK(rv); +} + +static bool edit_on_submit(struct dialog *dia, struct dialog_section *section, + void *arg) +{ + struct edit_req *edit = arg; + WERROR rv; + DATA_BLOB blob; + const char *name; + struct dialog_section *name_section, *data; + + name_section = dialog_find_section(dia, "name"); + if (name_section) { + name = dialog_section_text_field_get(dia, name_section); + if (*name == '\0') { + dialog_notice(dia, DIA_ALERT, "Error", + "Value name must not be blank."); + return false; + } + if (value_exists(dia, edit->key, name)) { + dialog_notice(dia, DIA_ALERT, "Error", + "Value named \"%s\" already exists.", + name); + return false; + } + } else { + SMB_ASSERT(edit->vitem); + name = edit->vitem->value_name; + } + SMB_ASSERT(name); + + data = dialog_find_section(dia, "data"); + SMB_ASSERT(data != NULL); + + rv = WERR_OK; + switch (edit->mode) { + case REG_DWORD: { + unsigned long long v; + uint32_t val; + + if (!dialog_section_text_field_get_uint(data, &v)) { + dialog_notice(dia, DIA_ALERT, "Error", + "REG_DWORD value must be an integer."); + return false; + } + if (v > UINT32_MAX) { + dialog_notice(dia, DIA_ALERT, "Error", + "REG_DWORD value must less than %lu.", + (unsigned long)UINT32_MAX); + return false; + } + val = (uint32_t)v; + blob = data_blob_talloc(dia, NULL, sizeof(val)); + SIVAL(blob.data, 0, val); + break; + } + case REG_SZ: + case REG_EXPAND_SZ: { + const char *buf; + + buf = dialog_section_text_field_get(dia, data); + if (!buf || !push_reg_sz(dia, &blob, buf)) { + rv = WERR_NOT_ENOUGH_MEMORY; + } + break; + } + case REG_MULTI_SZ: { + const char **lines; + + lines = dialog_section_text_field_get_lines(dia, data); + if (!lines || !push_reg_multi_sz(dia, &blob, lines)) { + rv = WERR_NOT_ENOUGH_MEMORY; + } + break; + } + case REG_BINARY: { + const void *buf; + size_t len; + + dialog_section_hexedit_get_buf(data, &buf, &len); + blob = data_blob_talloc(dia, buf, len); + break; + } + } + + if (W_ERROR_IS_OK(rv)) { + rv = reg_val_set(edit->key, name, edit->type, blob); + } + + if (!W_ERROR_IS_OK(rv)) { + const char *msg = get_friendly_werror_msg(rv); + dialog_notice(dia, DIA_ALERT, "Error", + "Error saving value:\n%s", msg); + + return false; + } + + return true; + +} + +static enum dialog_action edit_on_resize(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section *data; + unsigned long size; + int rv; + + data = dialog_find_section(dia, "data"); + rv = dialog_input_ulong(dia, &size, "Resize", "Enter size of buffer"); + if (rv == DIALOG_OK) { + dialog_section_hexedit_resize(data, size); + } + + return DIALOG_IGNORE; +} + +int dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key, + uint32_t type, const struct value_item *vitem, + bool force_binary, WERROR *err, + const char **name) +{ + enum dialog_action action; + struct dialog *dia; + struct dialog_section *section; + struct edit_req edit; + struct button_spec buttons[] = { + {.label = "OK", .action = DIALOG_OK}, + {.label = "Cancel", .action = DIALOG_CANCEL}, + { 0 } + }; + struct button_spec buttons_hexedit[] = { + {.label = "OK", .action = DIALOG_OK}, + {.label = "Resize Buffer", .on_enter = edit_on_resize}, + {.label = "Cancel", .action = DIALOG_CANCEL}, + { 0 } + }; + + + edit.key = key; + edit.vitem = vitem; + edit.type = type; + edit.mode = type; + if (force_binary || (vitem && vitem->unprintable)) { + edit.mode = REG_BINARY; + } + + dia = dialog_new(ctx, PAIR_BLACK_CYAN, "Edit Value", -1, -1); + dialog_set_submit_cb(dia, edit_on_submit, &edit); + + section = dialog_section_label_new(dia, "Type"); + dialog_append_section(dia, section); + section = dialog_section_label_new(dia, "%s", + str_regtype(type)); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, ' '); + dialog_append_section(dia, section); + + section = dialog_section_label_new(dia, "Name"); + dialog_append_section(dia, section); + if (vitem) { + section = dialog_section_label_new(dia, "%s", + vitem->value_name); + } else { + section = dialog_section_text_field_new(dia, 1, 50); + dialog_section_set_name(section, "name"); + } + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, ' '); + dialog_append_section(dia, section); + + section = dialog_section_label_new(dia, "Data"); + dialog_append_section(dia, section); + + switch (edit.mode) { + case REG_DWORD: + case REG_SZ: + case REG_EXPAND_SZ: + section = dialog_section_text_field_new(dia, 1, 50); + break; + case REG_MULTI_SZ: + section = dialog_section_text_field_new(dia, 10, 50); + break; + case REG_BINARY: + default: + section = dialog_section_hexedit_new(dia, 10); + break; + } + + dialog_section_set_name(section, "data"); + dialog_append_section(dia, section); + + section = dialog_section_hsep_new(dia, 0); + dialog_append_section(dia, section); + if (edit.mode == REG_BINARY) { + section = dialog_section_buttons_new(dia, buttons_hexedit); + } else { + section = dialog_section_buttons_new(dia, buttons); + } + dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER); + dialog_append_section(dia, section); + + dialog_create(dia); + + *err = fill_value_buffer(dia, &edit); + if (!W_ERROR_IS_OK(*err)) { + return DIALOG_CANCEL; + } + + dialog_show(dia); + dialog_modal_loop(dia, err, &action); + + if (action == DIALOG_OK && name) { + if (vitem) { + *name = talloc_strdup(ctx, vitem->value_name); + } else if ((section = dialog_find_section(dia, "name"))) { + *name = dialog_section_text_field_get(ctx, section); + } + } + + talloc_free(dia); + + return action; +} + +int dialog_select_type(TALLOC_CTX *ctx, int *type) +{ + WERROR err; + enum dialog_action action; + struct dialog *dia; + struct dialog_section *section; + const char *reg_types[] = { + "REG_BINARY", + "REG_DWORD", + "REG_EXPAND_SZ", + "REG_MULTI_SZ", + "REG_SZ" + }; + #define NTYPES ARRAY_SIZE(reg_types) + struct button_spec spec[] = { + {.label = "OK", .action = DIALOG_OK}, + {.label = "Cancel", .action = DIALOG_CANCEL}, + { 0 } + }; + bool flags[NTYPES] = { true }; + struct option_spec opsec[NTYPES + 1]; + unsigned i; + + memset(&opsec, '\0', sizeof(opsec)); + for (i = 0; i < NTYPES; ++i) { + opsec[i].label = reg_types[i]; + opsec[i].state = &flags[i]; + } + + dia = dialog_new(ctx, PAIR_BLACK_CYAN, "New Value", -1, -1); + + section = dialog_section_label_new(dia, "Select type for new value:"); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, ' '); + dialog_append_section(dia, section); + section = dialog_section_options_new(dia, opsec, 2, true); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, 0); + dialog_append_section(dia, section); + section = dialog_section_buttons_new(dia, spec); + dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER); + dialog_append_section(dia, section); + + dialog_create(dia); + dialog_show(dia); + + dialog_modal_loop(dia, &err, &action); + if (action == DIALOG_OK) { + for (i = 0; i < NTYPES; ++i) { + if (flags[i]) { + *type = regtype_by_string(reg_types[i]); + break; + } + } + } + + talloc_free(dia); + + return action; +} + +struct search_req { + TALLOC_CTX *ctx; + struct regedit_search_opts *opts; +}; + +static bool search_on_submit(struct dialog *dia, struct dialog_section *section, + void *arg) +{ + struct search_req *search = arg; + struct dialog_section *query; + + query = dialog_find_section(dia, "query"); + SMB_ASSERT(query != NULL); + + if (!search->opts->search_key && !search->opts->search_value) { + dialog_notice(dia, DIA_ALERT, "Error", + "Must search a key and/or a value"); + return false; + } + + talloc_free(discard_const(search->opts->query)); + search->opts->query = dialog_section_text_field_get(search->ctx, query); + SMB_ASSERT(search->opts->query != NULL); + if (search->opts->query[0] == '\0') { + dialog_notice(dia, DIA_ALERT, "Error", + "Query must not be blank."); + return false; + } + + return true; +} + +int dialog_search_input(TALLOC_CTX *ctx, struct regedit_search_opts *opts) +{ + WERROR err; + enum dialog_action action; + struct dialog *dia; + struct dialog_section *section, *query; + struct search_req search; + struct button_spec spec[] = { + {.label = "Search", .action = DIALOG_OK}, + {.label = "Cancel", .action = DIALOG_CANCEL}, + { 0 } + }; + struct option_spec search_opts[] = { + {.label = "Search Keys", .state = &opts->search_key}, + {.label = "Search Values", .state = &opts->search_value}, + {.label = "Recursive", .state = &opts->search_recursive}, + {.label = "Case Sensitive", .state = &opts->search_case}, + { 0 } + }; + + if (!opts->search_key && !opts->search_value) { + opts->search_key = true; + } + + search.ctx = ctx; + search.opts = opts; + dia = dialog_new(ctx, PAIR_BLACK_CYAN, "Search", -1, -1); + dialog_set_submit_cb(dia, search_on_submit, &search); + section = dialog_section_label_new(dia, "Query"); + dialog_append_section(dia, section); + query = dialog_section_text_field_new(dia, 1, -1); + dialog_section_set_name(query, "query"); + dialog_append_section(dia, query); + section = dialog_section_hsep_new(dia, 0); + dialog_append_section(dia, section); + section = dialog_section_options_new(dia, search_opts, 2, false); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, 0); + dialog_append_section(dia, section); + section = dialog_section_buttons_new(dia, spec); + dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER); + dialog_append_section(dia, section); + + dialog_create(dia); + if (opts->query) { + dialog_section_text_field_set(query, opts->query); + } + + dialog_modal_loop(dia, &err, &action); + talloc_free(dia); + + return action; +} -- cgit v1.2.3