2 * Samba Unix/Linux SMB client library
4 * Copyright (C) Christopher Davis 2012
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.
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.
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/>.
21 #include "popt_common.h"
22 #include "lib/util/data_blob.h"
23 #include "lib/registry/registry.h"
25 #include "regedit_treeview.h"
26 #include "regedit_valuelist.h"
27 #include "regedit_dialog.h"
28 #include "regedit_list.h"
35 #define KEY_WIDTH (COLS / 4)
36 #define KEY_HEIGHT (LINES - KEY_START_Y - 2)
37 #define VAL_START_X KEY_WIDTH
39 #define VAL_WIDTH (COLS - KEY_WIDTH)
40 #define VAL_HEIGHT (LINES - VAL_START_Y - 2)
42 #define HELP1_START_Y (LINES - 2)
43 #define HELP1_START_X 0
44 #define HELP1_WIDTH (LINES)
45 #define HELP2_START_Y (LINES - 1)
46 #define HELP2_START_X 0
47 #define HELP2_WIDTH (LINES)
48 #define PATH_START_Y 0
49 #define PATH_START_X 6
50 #define PATH_MAX_Y (COLS - 1)
51 #define PATH_WIDTH (COLS - 6)
52 #define PATH_WIDTH_MAX 1024
55 struct registry_context
*registry_context
;
59 struct value_list
*vl
;
60 struct tree_view
*keys
;
62 struct regedit_search_opts active_search
;
65 static struct regedit
*regedit_main
= NULL
;
67 static void show_path(struct regedit
*regedit
)
70 int start_win
= PATH_START_X
;
72 if (PATH_START_X
+ regedit
->path_len
> COLS
) {
73 start_pad
= 3 + PATH_START_X
+ regedit
->path_len
- COLS
;
74 mvprintw(PATH_START_Y
, start_win
, "...");
77 copywin(regedit
->path_label
, regedit
->main_window
, 0, start_pad
,
78 PATH_START_Y
, start_win
, PATH_START_Y
, PATH_MAX_Y
, false);
80 mvchgat(0, 0, COLS
, A_BOLD
, PAIR_YELLOW_CYAN
, NULL
);
83 static void print_path(struct regedit
*regedit
, struct tree_node
*node
)
85 regedit
->path_len
= tree_node_print_path(regedit
->path_label
, node
);
89 static void print_help(struct regedit
*regedit
)
91 const char *khelp
= "[n] New Key [s] New Subkey [d] Del Key "
92 "[LEFT] Ascend [RIGHT] Descend";
93 const char *vhelp
= "[n] New Value [d] Del Value [ENTER] Edit "
95 const char *msg
= "KEYS";
96 const char *help
= khelp
;
97 const char *genhelp
= "[TAB] Switch sections [q] Quit "
98 "[UP] List up [DOWN] List down "
99 "[/] Search [x] Next";
102 if (!regedit
->tree_input
) {
107 move(HELP1_START_Y
, HELP1_START_X
);
109 attron(COLOR_PAIR(PAIR_BLACK_CYAN
));
110 mvaddstr(HELP1_START_Y
, HELP1_START_X
, help
);
111 pad
= COLS
- strlen(msg
) - strlen(help
);
112 for (i
= 0; i
< pad
; ++i
) {
115 attroff(COLOR_PAIR(PAIR_BLACK_CYAN
));
116 attron(COLOR_PAIR(PAIR_YELLOW_CYAN
) | A_BOLD
);
118 attroff(COLOR_PAIR(PAIR_YELLOW_CYAN
) | A_BOLD
);
120 move(HELP2_START_Y
, HELP2_START_X
);
122 mvaddstr(HELP2_START_Y
, HELP2_START_X
, genhelp
);
125 static void print_heading(struct regedit
*regedit
)
127 if (regedit
->tree_input
) {
128 tree_view_set_selected(regedit
->keys
, true);
129 value_list_set_selected(regedit
->vl
, false);
131 tree_view_set_selected(regedit
->keys
, false);
132 value_list_set_selected(regedit
->vl
, true);
138 static void load_values(struct regedit
*regedit
)
140 struct tree_node
*node
;
142 node
= tree_view_get_current_node(regedit
->keys
);
143 value_list_load(regedit
->vl
, node
->key
);
146 static void add_reg_key(struct regedit
*regedit
, struct tree_node
*node
,
152 if (!subkey
&& tree_node_is_top_level(node
)) {
156 msg
= "Enter name of new key";
158 msg
= "Enter name of new subkey";
160 dialog_input(regedit
, &name
, "New Key", msg
);
163 struct registry_key
*new_key
;
164 struct tree_node
*new_node
;
165 struct tree_node
*list
;
166 struct tree_node
*parent
;
170 list
= node
->child_head
;
172 parent
= node
->parent
;
173 list
= tree_node_first(node
);
174 SMB_ASSERT(list
!= NULL
);
176 rv
= reg_key_add_name(regedit
, parent
->key
, name
,
177 NULL
, NULL
, &new_key
);
178 if (W_ERROR_IS_OK(rv
)) {
179 /* The list of subkeys may not be present in
180 cache yet, so if not, don't bother allocating
181 a new node for the key. */
183 new_node
= tree_node_new(parent
, parent
,
185 SMB_ASSERT(new_node
);
186 tree_node_insert_sorted(list
, new_node
);
188 /* Reopen the parent key to make sure the
189 new subkey will be noticed. */
190 tree_node_reopen_key(parent
);
193 list
= tree_node_first(node
);
194 tree_view_clear(regedit
->keys
);
195 tree_view_update(regedit
->keys
, list
);
199 tree_view_set_current_node(regedit
->keys
, node
);
201 msg
= get_friendly_werror_msg(rv
);
202 dialog_notice(regedit
, DIA_ALERT
, "New Key",
203 "Failed to create key: %s", msg
);
205 talloc_free(discard_const(name
));
209 static WERROR
next_depth_first(struct tree_node
**node
)
213 SMB_ASSERT(node
!= NULL
&& *node
!= NULL
);
215 if (tree_node_has_children(*node
)) {
216 /* 1. If the node has children, go to the first one. */
217 rv
= tree_node_load_children(*node
);
218 if (W_ERROR_IS_OK(rv
)) {
219 SMB_ASSERT((*node
)->child_head
!= NULL
);
220 *node
= (*node
)->child_head
;
222 } else if ((*node
)->next
) {
223 /* 2. If there's a node directly after this one, go there */
224 *node
= (*node
)->next
;
226 /* 3. Otherwise, go up the hierarchy to find the next one */
228 *node
= (*node
)->parent
;
229 if (*node
&& (*node
)->next
) {
230 *node
= (*node
)->next
;
239 static WERROR
regedit_search_next(struct regedit
*regedit
)
242 struct regedit_search_opts
*opts
= ®edit
->active_search
;
244 if (opts
->search_recursive
) {
245 rv
= next_depth_first(&opts
->node
);
246 if (!W_ERROR_IS_OK(rv
)) {
250 opts
->node
= opts
->node
->next
;
256 static WERROR
regedit_search(struct regedit
*regedit
)
258 struct regedit_search_opts
*opts
;
259 struct tree_node
*found
;
262 opts
= ®edit
->active_search
;
264 if (!opts
->query
|| !opts
->match
) {
268 SMB_ASSERT(opts
->search_key
|| opts
->search_value
);
270 for (found
= NULL
; opts
->node
&& !found
; ) {
271 if (opts
->search_key
&&
272 opts
->match(opts
->node
->name
, opts
->query
)) {
275 if (opts
->search_value
) {
277 rv = regedit_search_value(regedit);
278 if (W_ERROR_IS_OK(rv)) {
280 } else if (!W_ERROR_EQUAL(rv, WERR_NO_MORE_ITEMS)) {
285 rv
= regedit_search_next(regedit
);
286 if (!W_ERROR_IS_OK(rv
)) {
292 /* Put the cursor on the node that was found */
293 if (!tree_view_is_node_visible(regedit
->keys
, found
)) {
294 tree_view_update(regedit
->keys
,
295 tree_node_first(found
));
296 print_path(regedit
, found
);
298 tree_view_set_current_node(regedit
->keys
, found
);
299 load_values(regedit
);
300 tree_view_show(regedit
->keys
);
301 value_list_show(regedit
->vl
);
309 static void handle_tree_input(struct regedit
*regedit
, int c
)
311 struct tree_node
*node
;
315 tree_view_driver(regedit
->keys
, ML_CURSOR_DOWN
);
316 load_values(regedit
);
319 tree_view_driver(regedit
->keys
, ML_CURSOR_UP
);
320 load_values(regedit
);
325 node
= tree_view_get_current_node(regedit
->keys
);
326 if (node
&& tree_node_has_children(node
)) {
329 rv
= tree_node_load_children(node
);
330 if (W_ERROR_IS_OK(rv
)) {
331 print_path(regedit
, node
->child_head
);
332 tree_view_update(regedit
->keys
, node
->child_head
);
333 value_list_load(regedit
->vl
, node
->child_head
->key
);
335 const char *msg
= get_friendly_werror_msg(rv
);
336 dialog_notice(regedit
, DIA_ALERT
, "Loading Subkeys",
337 "Failed to load subkeys: %s", msg
);
342 node
= tree_view_get_current_node(regedit
->keys
);
343 if (node
&& !tree_node_is_top_level(node
)) {
344 print_path(regedit
, node
->parent
);
346 tree_view_update(regedit
->keys
, tree_node_first(node
));
347 tree_view_set_current_node(regedit
->keys
, node
);
348 value_list_load(regedit
->vl
, node
->key
);
353 node
= tree_view_get_current_node(regedit
->keys
);
354 add_reg_key(regedit
, node
, false);
358 node
= tree_view_get_current_node(regedit
->keys
);
359 add_reg_key(regedit
, node
, true);
365 node
= tree_view_get_current_node(regedit
->keys
);
366 if (tree_node_is_top_level(node
)) {
369 sel
= dialog_notice(regedit
, DIA_CONFIRM
,
371 "Really delete key \"%s\"?",
373 if (sel
== DIALOG_OK
) {
375 struct tree_node
*pop
;
376 struct tree_node
*parent
= node
->parent
;
378 rv
= reg_key_del(node
, parent
->key
, node
->name
);
379 if (W_ERROR_IS_OK(rv
)) {
380 tree_node_reopen_key(parent
);
381 tree_view_clear(regedit
->keys
);
382 pop
= tree_node_pop(&node
);
384 node
= parent
->child_head
;
386 node
= tree_node_first(parent
);
387 print_path(regedit
, node
);
389 tree_view_update(regedit
->keys
, node
);
390 value_list_load(regedit
->vl
, node
->key
);
392 const char *msg
= get_friendly_werror_msg(rv
);
393 dialog_notice(regedit
, DIA_ALERT
, "Delete Key",
394 "Failed to delete key: %s", msg
);
401 tree_view_show(regedit
->keys
);
402 value_list_show(regedit
->vl
);
405 static void handle_value_input(struct regedit
*regedit
, int c
)
407 struct value_item
*vitem
;
408 bool binmode
= false;
412 value_list_driver(regedit
->vl
, ML_CURSOR_DOWN
);
415 value_list_driver(regedit
->vl
, ML_CURSOR_UP
);
423 vitem
= value_list_get_current_item(regedit
->vl
);
425 struct tree_node
*node
;
426 node
= tree_view_get_current_node(regedit
->keys
);
427 dialog_edit_value(regedit
, node
->key
, vitem
->type
,
429 tree_node_reopen_key(node
);
430 value_list_load(regedit
->vl
, node
->key
);
438 sel
= dialog_select_type(regedit
, &new_type
);
439 if (sel
== DIALOG_OK
) {
440 struct tree_node
*node
;
441 node
= tree_view_get_current_node(regedit
->keys
);
442 dialog_edit_value(regedit
, node
->key
, new_type
, NULL
,
444 tree_node_reopen_key(node
);
445 value_list_load(regedit
->vl
, node
->key
);
451 vitem
= value_list_get_current_item(regedit
->vl
);
455 sel
= dialog_notice(regedit
, DIA_CONFIRM
,
457 "Really delete value \"%s\"?",
459 if (sel
== DIALOG_OK
) {
460 struct tree_node
*node
;
461 node
= tree_view_get_current_node(regedit
->keys
);
462 reg_del_value(regedit
, node
->key
,
464 tree_node_reopen_key(node
);
465 value_list_load(regedit
->vl
, node
->key
);
471 value_list_show(regedit
->vl
);
474 static bool find_substring(const char *haystack
, const char *needle
)
476 return strstr(haystack
, needle
) != NULL
;
479 static bool find_substring_nocase(const char *haystack
, const char *needle
)
481 return strcasestr(haystack
, needle
) != NULL
;
484 static void handle_main_input(struct regedit
*regedit
, int c
)
487 case 18: { /* CTRL-R */
488 struct tree_node
*root
, *node
;
491 node
= tree_view_get_current_node(regedit
->keys
);
492 path
= tree_node_get_path(regedit
, node
);
493 SMB_ASSERT(path
!= NULL
);
495 root
= tree_node_new_root(regedit
, regedit
->registry_context
);
496 SMB_ASSERT(root
!= NULL
);
498 tree_view_set_root(regedit
->keys
, root
);
499 tree_view_set_path(regedit
->keys
, path
);
500 node
= tree_view_get_current_node(regedit
->keys
);
501 value_list_load(regedit
->vl
, node
->key
);
502 tree_view_show(regedit
->keys
);
503 value_list_show(regedit
->vl
);
504 print_path(regedit
, node
);
505 talloc_free(discard_const(path
));
512 struct regedit_search_opts
*opts
;
514 opts
= ®edit
->active_search
;
516 talloc_free(discard_const(opts
->query
));
518 rv
= dialog_search_input(regedit
, opts
);
519 if (rv
== DIALOG_OK
) {
520 SMB_ASSERT(opts
->query
!= NULL
);
521 opts
->match
= find_substring
;
522 opts
->node
= regedit
->keys
->root
;
523 if (opts
->search_nocase
) {
524 opts
->match
= find_substring_nocase
;
526 if (opts
->search_relative
) {
528 tree_view_get_current_node(regedit
->keys
);
530 regedit_search(regedit
);
536 regedit_search(regedit
);
539 regedit
->tree_input
= !regedit
->tree_input
;
540 print_heading(regedit
);
543 if (regedit
->tree_input
) {
544 handle_tree_input(regedit
, c
);
546 handle_value_input(regedit
, c
);
551 int regedit_getch(void)
555 SMB_ASSERT(regedit_main
);
558 if (c
== KEY_RESIZE
) {
559 tree_view_resize(regedit_main
->keys
, KEY_HEIGHT
, KEY_WIDTH
,
560 KEY_START_Y
, KEY_START_X
);
561 value_list_resize(regedit_main
->vl
, VAL_HEIGHT
, VAL_WIDTH
,
562 VAL_START_Y
, VAL_START_X
);
563 print_heading(regedit_main
);
564 show_path(regedit_main
);
570 static void regedit_panic_handler(const char *msg
)
576 static void display_window(TALLOC_CTX
*mem_ctx
, struct registry_context
*ctx
)
578 struct regedit
*regedit
;
579 struct tree_node
*root
;
588 fault_configure(regedit_panic_handler
);
590 colors
= has_colors();
593 use_default_colors();
594 assume_default_colors(COLOR_WHITE
, COLOR_BLUE
);
595 init_pair(PAIR_YELLOW_CYAN
, COLOR_YELLOW
, COLOR_CYAN
);
596 init_pair(PAIR_BLACK_CYAN
, COLOR_BLACK
, COLOR_CYAN
);
597 init_pair(PAIR_YELLOW_BLUE
, COLOR_YELLOW
, COLOR_BLUE
);
600 regedit
= talloc_zero(mem_ctx
, struct regedit
);
601 SMB_ASSERT(regedit
!= NULL
);
602 regedit_main
= regedit
;
604 regedit
->registry_context
= ctx
;
605 regedit
->main_window
= stdscr
;
606 keypad(regedit
->main_window
, TRUE
);
608 mvwprintw(regedit
->main_window
, 0, 0, "Path: ");
609 regedit
->path_label
= newpad(1, PATH_WIDTH_MAX
);
610 SMB_ASSERT(regedit
->path_label
);
611 wprintw(regedit
->path_label
, "/");
612 show_path(regedit_main
);
614 root
= tree_node_new_root(regedit
, ctx
);
615 SMB_ASSERT(root
!= NULL
);
617 regedit
->keys
= tree_view_new(regedit
, root
, KEY_HEIGHT
, KEY_WIDTH
,
618 KEY_START_Y
, KEY_START_X
);
619 SMB_ASSERT(regedit
->keys
!= NULL
);
621 regedit
->vl
= value_list_new(regedit
, VAL_HEIGHT
, VAL_WIDTH
,
622 VAL_START_Y
, VAL_START_X
);
623 SMB_ASSERT(regedit
->vl
!= NULL
);
625 regedit
->tree_input
= true;
626 print_heading(regedit
);
628 tree_view_show(regedit
->keys
);
629 load_values(regedit
);
630 value_list_show(regedit
->vl
);
636 key
= regedit_getch();
638 handle_main_input(regedit
, key
);
641 } while (key
!= 'q' || key
== 'Q');
646 int main(int argc
, const char **argv
)
648 struct poptOption long_options
[] = {
652 POPT_COMMON_CONNECTION
653 POPT_COMMON_CREDENTIALS
658 struct user_auth_info
*auth_info
;
660 struct registry_context
*ctx
;
663 frame
= talloc_stackframe();
665 setup_logging("regedit", DEBUG_DEFAULT_STDERR
);
666 lp_set_cmdline("log level", "0");
668 /* process options */
669 auth_info
= user_auth_info_init(frame
);
670 if (auth_info
== NULL
) {
673 popt_common_set_auth_info(auth_info
);
674 pc
= poptGetContext("regedit", argc
, argv
, long_options
, 0);
676 while ((opt
= poptGetNextOpt(pc
)) != -1) {
680 if (!lp_load_global(get_dyn_CONFIGFILE())) {
681 DEBUG(0, ("ERROR loading config file...\n"));
685 /* some simple tests */
687 rv
= reg_open_samba3(frame
, &ctx
);
688 if (!W_ERROR_IS_OK(rv
)) {
694 display_window(frame
, ctx
);