1 /*
2  * Samba Unix/Linux SMB client library
3  * Registry Editor
4  * Copyright (C) Christopher Davis 2012
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "includes.h"
21 #include "regedit.h"
22 #include "regedit_valuelist.h"
23 #include "regedit_list.h"
24 #include "lib/registry/registry.h"
25 
26 #define HEADING_X 3
27 
value_list_free(struct value_list * vl)28 static int value_list_free(struct value_list *vl)
29 {
30 	if (vl->panel) {
31 		del_panel(vl->panel);
32 	}
33 	if (vl->sub) {
34 		delwin(vl->sub);
35 	}
36 	if (vl->window) {
37 		delwin(vl->window);
38 	}
39 
40 	return 0;
41 }
42 
vl_get_column_header(const void * data,unsigned col)43 static const char *vl_get_column_header(const void *data, unsigned col)
44 {
45 	switch (col) {
46 	case 0:
47 		return "Name";
48 	case 1:
49 		return "Type";
50 	case 2:
51 		return "Data";
52 	}
53 
54 	return "???";
55 }
56 
vl_get_first_row(const void * data)57 static const void *vl_get_first_row(const void *data)
58 {
59 	const struct value_list *vl;
60 
61 	if (data) {
62 		vl = talloc_get_type_abort(data, struct value_list);
63 		if (vl->nvalues) {
64 			return &vl->values[0];
65 		}
66 	}
67 
68 	return NULL;
69 }
70 
vl_get_next_row(const void * data,const void * row)71 static const void *vl_get_next_row(const void *data, const void *row)
72 {
73 	const struct value_list *vl;
74 	const struct value_item *value = row;
75 
76 	SMB_ASSERT(data != NULL);
77 	SMB_ASSERT(value != NULL);
78 	vl = talloc_get_type_abort(data, struct value_list);
79 	if (value == &vl->values[vl->nvalues - 1]) {
80 		return NULL;
81 	}
82 
83 	return value + 1;
84 }
85 
vl_get_prev_row(const void * data,const void * row)86 static const void *vl_get_prev_row(const void *data, const void *row)
87 {
88 	const struct value_list *vl;
89 	const struct value_item *value = row;
90 
91 	SMB_ASSERT(data != NULL);
92 	SMB_ASSERT(value != NULL);
93 	vl = talloc_get_type_abort(data, struct value_list);
94 	if (value == &vl->values[0]) {
95 		return NULL;
96 	}
97 
98 	return value - 1;
99 }
100 
vl_get_item_label(const void * row,unsigned col)101 static const char *vl_get_item_label(const void *row, unsigned col)
102 {
103 	const struct value_item *value = row;
104 
105 	SMB_ASSERT(value != NULL);
106 	SMB_ASSERT(value->value_name != NULL);
107 	switch (col) {
108 	case 0:
109 		return value->value_name;
110 	case 1:
111 		return str_regtype(value->type);
112 	case 2:
113 		if (value->value) {
114 			return value->value;
115 		}
116 		return "";
117 	}
118 
119 	return "???";
120 }
121 
122 static struct multilist_accessors vl_accessors = {
123 	.get_column_header = vl_get_column_header,
124 	.get_first_row = vl_get_first_row,
125 	.get_next_row = vl_get_next_row,
126 	.get_prev_row = vl_get_prev_row,
127 	.get_item_label = vl_get_item_label
128 };
129 
value_list_new(TALLOC_CTX * ctx,int nlines,int ncols,int begin_y,int begin_x)130 struct value_list *value_list_new(TALLOC_CTX *ctx, int nlines, int ncols,
131 				  int begin_y, int begin_x)
132 {
133 	struct value_list *vl;
134 
135 	vl = talloc_zero(ctx, struct value_list);
136 	if (vl == NULL) {
137 		return NULL;
138 	}
139 
140 	talloc_set_destructor(vl, value_list_free);
141 
142 	vl->window = newwin(nlines, ncols, begin_y, begin_x);
143 	if (vl->window == NULL) {
144 		goto fail;
145 	}
146 	vl->sub = subwin(vl->window, nlines - 2, ncols - 2,
147 			 begin_y + 1, begin_x + 1);
148 	if (vl->sub == NULL) {
149 		goto fail;
150 	}
151 	box(vl->window, 0, 0);
152 	mvwprintw(vl->window, 0, HEADING_X, "Value");
153 
154 	vl->panel = new_panel(vl->window);
155 	if (vl->panel == NULL) {
156 		goto fail;
157 	}
158 
159 	vl->list = multilist_new(vl, vl->sub, &vl_accessors, 3);
160 	if (vl->list == NULL) {
161 		goto fail;
162 	}
163 
164 	return vl;
165 
166 fail:
167 	talloc_free(vl);
168 
169 	return NULL;
170 }
171 
value_list_set_selected(struct value_list * vl,bool reverse)172 void value_list_set_selected(struct value_list *vl, bool reverse)
173 {
174 	attr_t attr = A_NORMAL;
175 
176 	if (reverse) {
177 		attr = A_REVERSE;
178 	}
179 	mvwchgat(vl->window, 0, HEADING_X, 5, attr, 0, NULL);
180 }
181 
value_list_resize(struct value_list * vl,int nlines,int ncols,int begin_y,int begin_x)182 void value_list_resize(struct value_list *vl, int nlines, int ncols,
183 		       int begin_y, int begin_x)
184 {
185 	WINDOW *nwin, *nsub;
186 
187 	nwin = newwin(nlines, ncols, begin_y, begin_x);
188 	if (nwin == NULL) {
189 		return;
190 	}
191 	nsub = subwin(nwin, nlines - 2, ncols - 2, begin_y + 1, begin_x + 1);
192 	if (nsub == NULL) {
193 		delwin(nwin);
194 		return;
195 	}
196 	replace_panel(vl->panel, nwin);
197 	delwin(vl->sub);
198 	delwin(vl->window);
199 	vl->window = nwin;
200 	vl->sub = nsub;
201 	box(vl->window, 0, 0);
202 	mvwprintw(vl->window, 0, HEADING_X, "Value");
203 	multilist_set_window(vl->list, vl->sub);
204 	value_list_show(vl);
205 }
206 
get_num_values(TALLOC_CTX * ctx,const struct registry_key * key)207 static uint32_t get_num_values(TALLOC_CTX *ctx, const struct registry_key *key)
208 {
209 	const char *classname;
210 	uint32_t num_subkeys;
211 	uint32_t num_values;
212 	NTTIME last_change_time;
213 	uint32_t max_subkeynamelen;
214 	uint32_t max_valnamelen;
215 	uint32_t max_valbufsize;
216 	WERROR rv;
217 
218 	rv = reg_key_get_info(ctx, key, &classname, &num_subkeys,
219 			      &num_values, &last_change_time,
220 			      &max_subkeynamelen, &max_valnamelen,
221 			      &max_valbufsize);
222 
223 	if (W_ERROR_IS_OK(rv)) {
224 		return num_values;
225 	}
226 
227 	return 0;
228 }
229 
value_list_show(struct value_list * vl)230 void value_list_show(struct value_list *vl)
231 {
232 	multilist_refresh(vl->list);
233 	touchwin(vl->window);
234 	wnoutrefresh(vl->window);
235 	wnoutrefresh(vl->sub);
236 }
237 
string_is_printable(const char * s)238 static bool string_is_printable(const char *s)
239 {
240 	const char *p;
241 
242 	for (p = s; *p; ++p) {
243 		if (!isprint(*p)) {
244 			return false;
245 		}
246 	}
247 
248 	return true;
249 }
250 
append_data_summary(TALLOC_CTX * ctx,struct value_item * vitem)251 static WERROR append_data_summary(TALLOC_CTX *ctx, struct value_item *vitem)
252 {
253 	char *tmp = NULL;
254 
255 /* This is adapted from print_registry_value() in net_registry_util.c */
256 
257 	switch(vitem->type) {
258 	case REG_DWORD: {
259 		uint32_t v = 0;
260 		if (vitem->data.length >= 4) {
261 			v = IVAL(vitem->data.data, 0);
262 		}
263 		tmp = talloc_asprintf(ctx, "0x%08x (%u)", v, v);
264 		break;
265 	}
266 	case REG_SZ:
267 	case REG_EXPAND_SZ: {
268 		const char *s;
269 
270 		if (!pull_reg_sz(ctx, &vitem->data, &s)) {
271 			break;
272 		}
273 		vitem->unprintable = !string_is_printable(s);
274 		if (vitem->unprintable) {
275 			tmp = talloc_asprintf(ctx, "(unprintable)");
276 		} else {
277 			tmp = talloc_asprintf(ctx, "%s", s);
278 		}
279 		break;
280 	}
281 	case REG_MULTI_SZ: {
282 		size_t i, len;
283 		const char **a;
284 		const char *val;
285 
286 		if (!pull_reg_multi_sz(ctx, &vitem->data, &a)) {
287 			break;
288 		}
289 		for (len = 0; a[len] != NULL; ++len) {
290 		}
291 		tmp = talloc_asprintf(ctx, "(%u) ", (unsigned)len);
292 		if (tmp == NULL) {
293 			return WERR_NOT_ENOUGH_MEMORY;
294 		}
295 		for (i = 0; i < len; ++i) {
296 			if (!string_is_printable(a[i])) {
297 				val = "(unprintable)";
298 				vitem->unprintable = true;
299 			} else {
300 				val = a[i];
301 			}
302 			if (i == len - 1) {
303 				tmp = talloc_asprintf_append(tmp,
304 							     "[%u]=\"%s\"",
305 							     (unsigned)i, val);
306 			} else {
307 				tmp = talloc_asprintf_append(tmp,
308 							     "[%u]=\"%s\", ",
309 							     (unsigned)i, val);
310 			}
311 			if (tmp == NULL) {
312 				return WERR_NOT_ENOUGH_MEMORY;
313 			}
314 		}
315 		break;
316 	}
317 	case REG_BINARY:
318 		tmp = talloc_asprintf(ctx, "(%d bytes)",
319 				      (int)vitem->data.length);
320 		break;
321 	default:
322 		tmp = talloc_asprintf(ctx, "(unknown)");
323 		break;
324 	}
325 
326 	if (tmp == NULL) {
327 		return WERR_NOT_ENOUGH_MEMORY;
328 	}
329 
330 	vitem->value = tmp;
331 
332 	return WERR_OK;
333 }
334 
vitem_cmp(struct value_item * a,struct value_item * b)335 static int vitem_cmp(struct value_item *a, struct value_item *b)
336 {
337 	return strcmp(a->value_name, b->value_name);
338 }
339 
340 /* load only the value names into memory to enable searching */
value_list_load_quick(struct value_list * vl,struct registry_key * key)341 WERROR value_list_load_quick(struct value_list *vl, struct registry_key *key)
342 {
343 	uint32_t nvalues;
344 	uint32_t idx;
345 	struct value_item *vitem, *new_items;
346 	WERROR rv;
347 
348 	multilist_set_data(vl->list, NULL);
349 	vl->nvalues = 0;
350 	TALLOC_FREE(vl->values);
351 
352 	nvalues = get_num_values(vl, key);
353 	if (nvalues == 0) {
354 		return WERR_OK;
355 	}
356 
357 	new_items = talloc_zero_array(vl, struct value_item, nvalues);
358 	if (new_items == NULL) {
359 		return WERR_NOT_ENOUGH_MEMORY;
360 	}
361 
362 	for (idx = 0; idx < nvalues; ++idx) {
363 		vitem = &new_items[idx];
364 		rv = reg_key_get_value_by_index(new_items, key, idx,
365 						&vitem->value_name,
366 						&vitem->type,
367 						&vitem->data);
368 		if (!W_ERROR_IS_OK(rv)) {
369 			talloc_free(new_items);
370 			return rv;
371 		}
372 	}
373 
374 	TYPESAFE_QSORT(new_items, nvalues, vitem_cmp);
375 	vl->nvalues = nvalues;
376 	vl->values = new_items;
377 
378 	return rv;
379 }
380 
381 /* sync up the UI with the list */
value_list_sync(struct value_list * vl)382 WERROR value_list_sync(struct value_list *vl)
383 {
384 	uint32_t idx;
385 	WERROR rv;
386 
387 	for (idx = 0; idx < vl->nvalues; ++idx) {
388 		rv = append_data_summary(vl->values, &vl->values[idx]);
389 		if (!W_ERROR_IS_OK(rv)) {
390 			return rv;
391 		}
392 	}
393 
394 	rv = multilist_set_data(vl->list, vl);
395 	if (W_ERROR_IS_OK(rv)) {
396 		multilist_refresh(vl->list);
397 	}
398 
399 	return rv;
400 }
401 
value_list_load(struct value_list * vl,struct registry_key * key)402 WERROR value_list_load(struct value_list *vl, struct registry_key *key)
403 {
404 	WERROR rv;
405 
406 	rv = value_list_load_quick(vl, key);
407 	if (!W_ERROR_IS_OK(rv)) {
408 		return rv;
409 	}
410 
411 	rv = value_list_sync(vl);
412 
413 	return rv;
414 }
415 
value_list_find_next_item(struct value_list * vl,struct value_item * vitem,const char * s,regedit_search_match_fn_t match)416 struct value_item *value_list_find_next_item(struct value_list *vl,
417 					     struct value_item *vitem,
418 					     const char *s,
419 					     regedit_search_match_fn_t match)
420 {
421 	struct value_item *end;
422 
423 	if (!vl->values) {
424 		return NULL;
425 	}
426 
427 	if (vitem) {
428 		++vitem;
429 	} else {
430 		vitem = &vl->values[0];
431 	}
432 
433 	for (end = &vl->values[vl->nvalues]; vitem < end; ++vitem) {
434 		if (match(vitem->value_name, s)) {
435 			return vitem;
436 		}
437 	}
438 
439 	return NULL;
440 }
441 
value_list_find_prev_item(struct value_list * vl,struct value_item * vitem,const char * s,regedit_search_match_fn_t match)442 struct value_item *value_list_find_prev_item(struct value_list *vl,
443 					     struct value_item *vitem,
444 					     const char *s,
445 					     regedit_search_match_fn_t match)
446 {
447 	struct value_item *end;
448 
449 	if (!vl->values) {
450 		return NULL;
451 	}
452 
453 	if (vitem) {
454 		--vitem;
455 	} else {
456 		vitem = &vl->values[vl->nvalues - 1];
457 	}
458 
459 	for (end = &vl->values[-1]; vitem > end; --vitem) {
460 		if (match(vitem->value_name, s)) {
461 			return vitem;
462 		}
463 	}
464 
465 	return NULL;
466 }
467 
value_list_get_current_item(struct value_list * vl)468 struct value_item *value_list_get_current_item(struct value_list *vl)
469 {
470 	return discard_const_p(struct value_item,
471 			       multilist_get_current_row(vl->list));
472 }
473 
value_list_set_current_item_by_name(struct value_list * vl,const char * name)474 void value_list_set_current_item_by_name(struct value_list *vl,
475 					 const char *name)
476 {
477 	size_t i;
478 
479 	for (i = 0; i < vl->nvalues; ++i) {
480 		if (strequal(vl->values[i].value_name, name)) {
481 			multilist_set_current_row(vl->list, &vl->values[i]);
482 			return;
483 		}
484 	}
485 }
486 
value_list_set_current_item(struct value_list * vl,const struct value_item * item)487 void value_list_set_current_item(struct value_list *vl,
488 				 const struct value_item *item)
489 {
490 	multilist_set_current_row(vl->list, item);
491 }
492 
value_list_driver(struct value_list * vl,int c)493 void value_list_driver(struct value_list *vl, int c)
494 {
495 	multilist_driver(vl->list, c);
496 }
497