mirror of
https://github.com/samba-team/samba.git
synced 2024-12-28 07:21:54 +03:00
5d070d9f18
Signed-off-by: Stefan Metzmacher <metze@samba.org> Reviewed-by: Jeremy Allison <jra@samba.org>
497 lines
9.8 KiB
C
497 lines
9.8 KiB
C
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include "regedit.h"
|
|
#include "regedit_valuelist.h"
|
|
#include "regedit_list.h"
|
|
#include "lib/registry/registry.h"
|
|
|
|
#define HEADING_X 3
|
|
|
|
static int value_list_free(struct value_list *vl)
|
|
{
|
|
if (vl->panel) {
|
|
del_panel(vl->panel);
|
|
}
|
|
if (vl->sub) {
|
|
delwin(vl->sub);
|
|
}
|
|
if (vl->window) {
|
|
delwin(vl->window);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const char *vl_get_column_header(const void *data, unsigned col)
|
|
{
|
|
switch (col) {
|
|
case 0:
|
|
return "Name";
|
|
case 1:
|
|
return "Type";
|
|
case 2:
|
|
return "Data";
|
|
}
|
|
|
|
return "???";
|
|
}
|
|
|
|
static const void *vl_get_first_row(const void *data)
|
|
{
|
|
const struct value_list *vl;
|
|
|
|
if (data) {
|
|
vl = talloc_get_type_abort(data, struct value_list);
|
|
if (vl->nvalues) {
|
|
return &vl->values[0];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static const void *vl_get_next_row(const void *data, const void *row)
|
|
{
|
|
const struct value_list *vl;
|
|
const struct value_item *value = row;
|
|
|
|
SMB_ASSERT(data != NULL);
|
|
SMB_ASSERT(value != NULL);
|
|
vl = talloc_get_type_abort(data, struct value_list);
|
|
if (value == &vl->values[vl->nvalues - 1]) {
|
|
return NULL;
|
|
}
|
|
|
|
return value + 1;
|
|
}
|
|
|
|
static const void *vl_get_prev_row(const void *data, const void *row)
|
|
{
|
|
const struct value_list *vl;
|
|
const struct value_item *value = row;
|
|
|
|
SMB_ASSERT(data != NULL);
|
|
SMB_ASSERT(value != NULL);
|
|
vl = talloc_get_type_abort(data, struct value_list);
|
|
if (value == &vl->values[0]) {
|
|
return NULL;
|
|
}
|
|
|
|
return value - 1;
|
|
}
|
|
|
|
static const char *vl_get_item_label(const void *row, unsigned col)
|
|
{
|
|
const struct value_item *value = row;
|
|
|
|
SMB_ASSERT(value != NULL);
|
|
SMB_ASSERT(value->value_name != NULL);
|
|
switch (col) {
|
|
case 0:
|
|
return value->value_name;
|
|
case 1:
|
|
return str_regtype(value->type);
|
|
case 2:
|
|
if (value->value) {
|
|
return value->value;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
return "???";
|
|
}
|
|
|
|
static struct multilist_accessors vl_accessors = {
|
|
.get_column_header = vl_get_column_header,
|
|
.get_first_row = vl_get_first_row,
|
|
.get_next_row = vl_get_next_row,
|
|
.get_prev_row = vl_get_prev_row,
|
|
.get_item_label = vl_get_item_label
|
|
};
|
|
|
|
struct value_list *value_list_new(TALLOC_CTX *ctx, int nlines, int ncols,
|
|
int begin_y, int begin_x)
|
|
{
|
|
struct value_list *vl;
|
|
|
|
vl = talloc_zero(ctx, struct value_list);
|
|
if (vl == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
talloc_set_destructor(vl, value_list_free);
|
|
|
|
vl->window = newwin(nlines, ncols, begin_y, begin_x);
|
|
if (vl->window == NULL) {
|
|
goto fail;
|
|
}
|
|
vl->sub = subwin(vl->window, nlines - 2, ncols - 2,
|
|
begin_y + 1, begin_x + 1);
|
|
if (vl->sub == NULL) {
|
|
goto fail;
|
|
}
|
|
box(vl->window, 0, 0);
|
|
mvwprintw(vl->window, 0, HEADING_X, "Value");
|
|
|
|
vl->panel = new_panel(vl->window);
|
|
if (vl->panel == NULL) {
|
|
goto fail;
|
|
}
|
|
|
|
vl->list = multilist_new(vl, vl->sub, &vl_accessors, 3);
|
|
if (vl->list == NULL) {
|
|
goto fail;
|
|
}
|
|
|
|
return vl;
|
|
|
|
fail:
|
|
talloc_free(vl);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void value_list_set_selected(struct value_list *vl, bool reverse)
|
|
{
|
|
attr_t attr = A_NORMAL;
|
|
|
|
if (reverse) {
|
|
attr = A_REVERSE;
|
|
}
|
|
mvwchgat(vl->window, 0, HEADING_X, 5, attr, 0, NULL);
|
|
}
|
|
|
|
void value_list_resize(struct value_list *vl, int nlines, int ncols,
|
|
int begin_y, int begin_x)
|
|
{
|
|
WINDOW *nwin, *nsub;
|
|
|
|
nwin = newwin(nlines, ncols, begin_y, begin_x);
|
|
if (nwin == NULL) {
|
|
return;
|
|
}
|
|
nsub = subwin(nwin, nlines - 2, ncols - 2, begin_y + 1, begin_x + 1);
|
|
if (nsub == NULL) {
|
|
delwin(nwin);
|
|
return;
|
|
}
|
|
replace_panel(vl->panel, nwin);
|
|
delwin(vl->sub);
|
|
delwin(vl->window);
|
|
vl->window = nwin;
|
|
vl->sub = nsub;
|
|
box(vl->window, 0, 0);
|
|
mvwprintw(vl->window, 0, HEADING_X, "Value");
|
|
multilist_set_window(vl->list, vl->sub);
|
|
value_list_show(vl);
|
|
}
|
|
|
|
static uint32_t get_num_values(TALLOC_CTX *ctx, const struct registry_key *key)
|
|
{
|
|
const char *classname;
|
|
uint32_t num_subkeys;
|
|
uint32_t num_values;
|
|
NTTIME last_change_time;
|
|
uint32_t max_subkeynamelen;
|
|
uint32_t max_valnamelen;
|
|
uint32_t max_valbufsize;
|
|
WERROR rv;
|
|
|
|
rv = reg_key_get_info(ctx, key, &classname, &num_subkeys,
|
|
&num_values, &last_change_time,
|
|
&max_subkeynamelen, &max_valnamelen,
|
|
&max_valbufsize);
|
|
|
|
if (W_ERROR_IS_OK(rv)) {
|
|
return num_values;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void value_list_show(struct value_list *vl)
|
|
{
|
|
multilist_refresh(vl->list);
|
|
touchwin(vl->window);
|
|
wnoutrefresh(vl->window);
|
|
wnoutrefresh(vl->sub);
|
|
}
|
|
|
|
static bool string_is_printable(const char *s)
|
|
{
|
|
const char *p;
|
|
|
|
for (p = s; *p; ++p) {
|
|
if (!isprint(*p)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static WERROR append_data_summary(TALLOC_CTX *ctx, struct value_item *vitem)
|
|
{
|
|
char *tmp = NULL;
|
|
|
|
/* This is adapted from print_registry_value() in net_registry_util.c */
|
|
|
|
switch(vitem->type) {
|
|
case REG_DWORD: {
|
|
uint32_t v = 0;
|
|
if (vitem->data.length >= 4) {
|
|
v = IVAL(vitem->data.data, 0);
|
|
}
|
|
tmp = talloc_asprintf(ctx, "0x%08x (%u)", v, v);
|
|
break;
|
|
}
|
|
case REG_SZ:
|
|
case REG_EXPAND_SZ: {
|
|
const char *s;
|
|
|
|
if (!pull_reg_sz(ctx, &vitem->data, &s)) {
|
|
break;
|
|
}
|
|
vitem->unprintable = !string_is_printable(s);
|
|
if (vitem->unprintable) {
|
|
tmp = talloc_asprintf(ctx, "(unprintable)");
|
|
} else {
|
|
tmp = talloc_asprintf(ctx, "%s", s);
|
|
}
|
|
break;
|
|
}
|
|
case REG_MULTI_SZ: {
|
|
size_t i, len;
|
|
const char **a;
|
|
const char *val;
|
|
|
|
if (!pull_reg_multi_sz(ctx, &vitem->data, &a)) {
|
|
break;
|
|
}
|
|
for (len = 0; a[len] != NULL; ++len) {
|
|
}
|
|
tmp = talloc_asprintf(ctx, "(%u) ", (unsigned)len);
|
|
if (tmp == NULL) {
|
|
return WERR_NOMEM;
|
|
}
|
|
for (i = 0; i < len; ++i) {
|
|
if (!string_is_printable(a[i])) {
|
|
val = "(unprintable)";
|
|
vitem->unprintable = true;
|
|
} else {
|
|
val = a[i];
|
|
}
|
|
if (i == len - 1) {
|
|
tmp = talloc_asprintf_append(tmp,
|
|
"[%u]=\"%s\"",
|
|
(unsigned)i, val);
|
|
} else {
|
|
tmp = talloc_asprintf_append(tmp,
|
|
"[%u]=\"%s\", ",
|
|
(unsigned)i, val);
|
|
}
|
|
if (tmp == NULL) {
|
|
return WERR_NOMEM;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case REG_BINARY:
|
|
tmp = talloc_asprintf(ctx, "(%d bytes)",
|
|
(int)vitem->data.length);
|
|
break;
|
|
default:
|
|
tmp = talloc_asprintf(ctx, "(unknown)");
|
|
break;
|
|
}
|
|
|
|
if (tmp == NULL) {
|
|
return WERR_NOMEM;
|
|
}
|
|
|
|
vitem->value = tmp;
|
|
|
|
return WERR_OK;
|
|
}
|
|
|
|
static int vitem_cmp(struct value_item *a, struct value_item *b)
|
|
{
|
|
return strcmp(a->value_name, b->value_name);
|
|
}
|
|
|
|
/* load only the value names into memory to enable searching */
|
|
WERROR value_list_load_quick(struct value_list *vl, struct registry_key *key)
|
|
{
|
|
uint32_t nvalues;
|
|
uint32_t idx;
|
|
struct value_item *vitem, *new_items;
|
|
WERROR rv;
|
|
|
|
multilist_set_data(vl->list, NULL);
|
|
vl->nvalues = 0;
|
|
TALLOC_FREE(vl->values);
|
|
|
|
nvalues = get_num_values(vl, key);
|
|
if (nvalues == 0) {
|
|
return WERR_OK;
|
|
}
|
|
|
|
new_items = talloc_zero_array(vl, struct value_item, nvalues);
|
|
if (new_items == NULL) {
|
|
return WERR_NOMEM;
|
|
}
|
|
|
|
for (idx = 0; idx < nvalues; ++idx) {
|
|
vitem = &new_items[idx];
|
|
rv = reg_key_get_value_by_index(new_items, key, idx,
|
|
&vitem->value_name,
|
|
&vitem->type,
|
|
&vitem->data);
|
|
if (!W_ERROR_IS_OK(rv)) {
|
|
talloc_free(new_items);
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
TYPESAFE_QSORT(new_items, nvalues, vitem_cmp);
|
|
vl->nvalues = nvalues;
|
|
vl->values = new_items;
|
|
|
|
return rv;
|
|
}
|
|
|
|
/* sync up the UI with the list */
|
|
WERROR value_list_sync(struct value_list *vl)
|
|
{
|
|
uint32_t idx;
|
|
WERROR rv;
|
|
|
|
for (idx = 0; idx < vl->nvalues; ++idx) {
|
|
rv = append_data_summary(vl->values, &vl->values[idx]);
|
|
if (!W_ERROR_IS_OK(rv)) {
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
rv = multilist_set_data(vl->list, vl);
|
|
if (W_ERROR_IS_OK(rv)) {
|
|
multilist_refresh(vl->list);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
WERROR value_list_load(struct value_list *vl, struct registry_key *key)
|
|
{
|
|
WERROR rv;
|
|
|
|
rv = value_list_load_quick(vl, key);
|
|
if (!W_ERROR_IS_OK(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
rv = value_list_sync(vl);
|
|
|
|
return rv;
|
|
}
|
|
|
|
struct value_item *value_list_find_next_item(struct value_list *vl,
|
|
struct value_item *vitem,
|
|
const char *s,
|
|
regedit_search_match_fn_t match)
|
|
{
|
|
struct value_item *end;
|
|
|
|
if (!vl->values) {
|
|
return NULL;
|
|
}
|
|
|
|
if (vitem) {
|
|
++vitem;
|
|
} else {
|
|
vitem = &vl->values[0];
|
|
}
|
|
|
|
for (end = &vl->values[vl->nvalues]; vitem < end; ++vitem) {
|
|
if (match(vitem->value_name, s)) {
|
|
return vitem;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct value_item *value_list_find_prev_item(struct value_list *vl,
|
|
struct value_item *vitem,
|
|
const char *s,
|
|
regedit_search_match_fn_t match)
|
|
{
|
|
struct value_item *end;
|
|
|
|
if (!vl->values) {
|
|
return NULL;
|
|
}
|
|
|
|
if (vitem) {
|
|
--vitem;
|
|
} else {
|
|
vitem = &vl->values[vl->nvalues - 1];
|
|
}
|
|
|
|
for (end = &vl->values[-1]; vitem > end; --vitem) {
|
|
if (match(vitem->value_name, s)) {
|
|
return vitem;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct value_item *value_list_get_current_item(struct value_list *vl)
|
|
{
|
|
return discard_const_p(struct value_item,
|
|
multilist_get_current_row(vl->list));
|
|
}
|
|
|
|
void value_list_set_current_item_by_name(struct value_list *vl,
|
|
const char *name)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < vl->nvalues; ++i) {
|
|
if (strequal(vl->values[i].value_name, name)) {
|
|
multilist_set_current_row(vl->list, &vl->values[i]);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void value_list_set_current_item(struct value_list *vl,
|
|
const struct value_item *item)
|
|
{
|
|
multilist_set_current_row(vl->list, item);
|
|
}
|
|
|
|
void value_list_driver(struct value_list *vl, int c)
|
|
{
|
|
multilist_driver(vl->list, c);
|
|
}
|