regedit: Update dialog position on screen resize.
[bbaumbach/samba-autobuild/.git] / source3 / utils / regedit.c
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 "popt_common.h"
22 #include "lib/util/data_blob.h"
23 #include "lib/registry/registry.h"
24 #include "regedit.h"
25 #include "regedit_treeview.h"
26 #include "regedit_valuelist.h"
27 #include "regedit_dialog.h"
28 #include <ncurses.h>
29 #include <menu.h>
30 #include <panel.h>
31
32 #define KEY_START_X     0
33 #define KEY_START_Y     3
34 #define KEY_WIDTH       (COLS / 4)
35 #define KEY_HEIGHT      (LINES - KEY_START_Y)
36 #define VAL_START_X     KEY_WIDTH
37 #define VAL_START_Y     3
38 #define VAL_WIDTH       (COLS - KEY_WIDTH)
39 #define VAL_HEIGHT      (LINES - VAL_START_Y)
40 #define HEADING_START_Y KEY_START_Y - 1
41
42 struct regedit {
43         WINDOW *main_window;
44         WINDOW *path_label;
45         struct value_list *vl;
46         struct tree_view *keys;
47         bool tree_input;
48 };
49
50 static struct regedit *regedit_main = NULL;
51
52 /* load all available hives */
53 static struct tree_node *load_hives(TALLOC_CTX *mem_ctx,
54                                     struct registry_context *ctx)
55 {
56         const char *hives[] = {
57                 "HKEY_CLASSES_ROOT",
58                 "HKEY_CURRENT_USER",
59                 "HKEY_LOCAL_MACHINE",
60                 "HKEY_PERFORMANCE_DATA",
61                 "HKEY_USERS",
62                 "HKEY_CURRENT_CONFIG",
63                 "HKEY_DYN_DATA",
64                 "HKEY_PERFORMANCE_TEXT",
65                 "HKEY_PERFORMANCE_NLSTEXT",
66                 NULL
67         };
68         struct tree_node *root, *prev, *node;
69         struct registry_key *key;
70         WERROR rv;
71         size_t i;
72
73         root = NULL;
74         prev = NULL;
75
76         for (i = 0; hives[i] != NULL; ++i) {
77                 rv = reg_get_predefined_key_by_name(ctx, hives[i], &key);
78                 if (!W_ERROR_IS_OK(rv)) {
79                         continue;
80                 }
81
82                 node = tree_node_new(mem_ctx, NULL, hives[i], key);
83                 if (node == NULL) {
84                         return NULL;
85                 }
86
87                 if (root == NULL) {
88                         root = node;
89                 }
90                 if (prev) {
91                         tree_node_append(prev, node);
92                 }
93                 prev = node;
94         }
95
96         return root;
97 }
98
99 static void print_heading(struct regedit *regedit)
100 {
101         move(HEADING_START_Y, 0);
102         clrtoeol();
103
104         if (regedit->tree_input) {
105                 attron(A_REVERSE);
106         } else {
107                 attroff(A_REVERSE);
108         }
109         mvprintw(HEADING_START_Y, KEY_START_X, "Key");
110         attroff(A_REVERSE);
111
112         if (!regedit->tree_input) {
113                 attron(A_REVERSE);
114         } else {
115                 attroff(A_REVERSE);
116         }
117         mvprintw(HEADING_START_Y, VAL_START_X, "Value");
118         attroff(A_REVERSE);
119 }
120
121 static void add_reg_key(struct regedit *regedit, struct tree_node *node,
122                         bool subkey)
123 {
124         char *name;
125         const char *msg;
126
127         if (!subkey && !node->parent) {
128                 return;
129         }
130
131         msg = "Enter name of new key";
132         if (subkey) {
133                 msg = "Enter name of new subkey";
134         }
135         dialog_input(regedit, &name, "New Key", msg);
136         if (name) {
137                 WERROR rv;
138                 struct registry_key *new_key;
139                 struct tree_node *new_node;
140                 struct tree_node *list;
141                 struct tree_node *parent;
142
143                 if (subkey) {
144                         parent = node;
145                         list = node->child_head;
146                 } else {
147                         parent = node->parent;
148                         list = tree_node_first(node);
149                         SMB_ASSERT(list != NULL);
150                 }
151                 rv = reg_key_add_name(regedit, parent->key, name,
152                                       NULL, NULL, &new_key);
153                 if (W_ERROR_IS_OK(rv)) {
154                         /* The list of subkeys may not be present in
155                            cache yet, so if not, don't bother allocating
156                            a new node for the key. */
157                         if (list) {
158                                 new_node = tree_node_new(parent, parent,
159                                                          name, new_key);
160                                 SMB_ASSERT(new_node);
161                                 tree_node_append_last(list, new_node);
162                         }
163
164                         list = tree_node_first(node);
165                         tree_view_clear(regedit->keys);
166                         tree_view_update(regedit->keys, list);
167                 } else {
168                         dialog_notice(regedit, DIA_ALERT, "New Key",
169                                       "Failed to create key.");
170                 }
171                 talloc_free(name);
172         }
173 }
174
175 static void handle_tree_input(struct regedit *regedit, int c)
176 {
177         struct tree_node *node;
178
179         switch (c) {
180         case KEY_DOWN:
181                 menu_driver(regedit->keys->menu, REQ_DOWN_ITEM);
182                 node = item_userptr(current_item(regedit->keys->menu));
183                 value_list_load(regedit->vl, node->key);
184                 break;
185         case KEY_UP:
186                 menu_driver(regedit->keys->menu, REQ_UP_ITEM);
187                 node = item_userptr(current_item(regedit->keys->menu));
188                 value_list_load(regedit->vl, node->key);
189                 break;
190         case '\n':
191         case KEY_ENTER:
192         case KEY_RIGHT:
193                 node = item_userptr(current_item(regedit->keys->menu));
194                 if (node && tree_node_has_children(node)) {
195                         tree_node_load_children(node);
196                         tree_node_print_path(regedit->path_label,
197                                              node->child_head);
198                         tree_view_update(regedit->keys, node->child_head);
199                         value_list_load(regedit->vl, node->child_head->key);
200                 }
201                 break;
202         case KEY_LEFT:
203                 node = item_userptr(current_item(regedit->keys->menu));
204                 if (node && node->parent) {
205                         tree_node_print_path(regedit->path_label, node->parent);
206                         node = tree_node_first(node->parent);
207                         tree_view_update(regedit->keys, node);
208                         value_list_load(regedit->vl, node->key);
209                 }
210                 break;
211         case 'n':
212         case 'N':
213                 node = item_userptr(current_item(regedit->keys->menu));
214                 add_reg_key(regedit, node, false);
215                 break;
216         case 's':
217         case 'S':
218                 node = item_userptr(current_item(regedit->keys->menu));
219                 add_reg_key(regedit, node, true);
220                 break;
221         case 'd':
222         case 'D': {
223                 int sel;
224
225                 node = item_userptr(current_item(regedit->keys->menu));
226                 if (!node->parent) {
227                         break;
228                 }
229                 sel = dialog_notice(regedit, DIA_CONFIRM,
230                                     "Delete Key",
231                                      "Really delete key \"%s\"?",
232                                      node->name);
233                 if (sel == DIALOG_OK) {
234                         WERROR rv;
235                         struct tree_node *pop;
236                         struct tree_node *parent = node->parent;
237
238                         rv = reg_key_del(node, parent->key, node->name);
239                         if (W_ERROR_IS_OK(rv)) {
240                                 tree_view_clear(regedit->keys);
241                                 pop = tree_node_pop(&node);
242                                 tree_node_free_recursive(pop);
243                                 node = parent->child_head;
244                                 if (node == NULL) {
245                                         node = tree_node_first(parent);
246                                         tree_node_print_path(regedit->path_label,
247                                                              node);
248                                 }
249                                 tree_view_update(regedit->keys, node);
250                                 value_list_load(regedit->vl, node->key);
251                         } else {
252                                 dialog_notice(regedit, DIA_ALERT, "Delete Key",
253                                               "Failed to delete key.");
254                         }
255                 }
256                 break;
257         }
258         }
259
260         tree_view_show(regedit->keys);
261         value_list_show(regedit->vl);
262 }
263
264 static void handle_value_input(struct regedit *regedit, int c)
265 {
266         struct value_item *vitem;
267
268         switch (c) {
269         case KEY_DOWN:
270                 menu_driver(regedit->vl->menu, REQ_DOWN_ITEM);
271                 break;
272         case KEY_UP:
273                 menu_driver(regedit->vl->menu, REQ_UP_ITEM);
274                 break;
275         case '\n':
276         case KEY_ENTER:
277                 vitem = item_userptr(current_item(regedit->vl->menu));
278                 if (vitem) {
279                         struct tree_node *node;
280                         node = item_userptr(current_item(regedit->keys->menu));
281                         dialog_edit_value(regedit, node->key, vitem->type,
282                                           vitem);
283                         value_list_load(regedit->vl, node->key);
284                 }
285                 break;
286         case 'n':
287         case 'N': {
288                 int new_type;
289                 int sel;
290
291                 sel = dialog_select_type(regedit, &new_type);
292                 if (sel == DIALOG_OK) {
293                         struct tree_node *node;
294                         node = item_userptr(current_item(regedit->keys->menu));
295                         dialog_edit_value(regedit, node->key, new_type, NULL);
296                         value_list_load(regedit->vl, node->key);
297                 }
298                 break;
299         }
300         case 'd':
301         case 'D':
302                 vitem = item_userptr(current_item(regedit->vl->menu));
303                 if (vitem) {
304                         int sel;
305
306                         sel = dialog_notice(regedit, DIA_CONFIRM,
307                                             "Delete Value",
308                                              "Really delete value \"%s\"?",
309                                              vitem->value_name);
310                         if (sel == DIALOG_OK) {
311                                 ITEM *it = current_item(regedit->keys->menu);
312                                 struct tree_node *node = item_userptr(it);
313                                 reg_del_value(regedit, node->key,
314                                               vitem->value_name);
315                                 value_list_load(regedit->vl, node->key);
316                         }
317                 }
318                 break;
319         }
320
321         value_list_show(regedit->vl);
322 }
323
324 static void handle_main_input(struct regedit *regedit, int c)
325 {
326         switch (c) {
327         case '\t':
328                 regedit->tree_input = !regedit->tree_input;
329                 print_heading(regedit);
330                 break;
331         default:
332                 if (regedit->tree_input) {
333                         handle_tree_input(regedit, c);
334                 } else {
335                         handle_value_input(regedit, c);
336                 }
337         }
338 }
339
340 int regedit_getch(void)
341 {
342         int c;
343
344         SMB_ASSERT(regedit_main);
345
346         c = getch();
347
348         if (c == KEY_RESIZE) {
349                 tree_view_resize(regedit_main->keys, KEY_HEIGHT, KEY_WIDTH,
350                                  KEY_START_Y, KEY_START_X);
351                 value_list_resize(regedit_main->vl, VAL_HEIGHT, VAL_WIDTH,
352                                   VAL_START_Y, VAL_START_X);
353                 print_heading(regedit_main);
354         }
355
356         return c;
357 }
358
359 static void display_window(TALLOC_CTX *mem_ctx, struct registry_context *ctx)
360 {
361         struct regedit *regedit;
362         struct tree_node *root;
363         int c;
364
365         initscr();
366         start_color();
367         cbreak();
368         noecho();
369
370         regedit = talloc_zero(mem_ctx, struct regedit);
371         SMB_ASSERT(regedit != NULL);
372         regedit_main = regedit;
373
374         regedit->main_window = stdscr;
375         keypad(regedit->main_window, TRUE);
376
377         mvwprintw(regedit->main_window, 0, 0, "Path: ");
378         regedit->path_label = derwin(regedit->main_window, 1, COLS - 6, 0, 6);
379         wprintw(regedit->path_label, "/");
380
381         root = load_hives(regedit, ctx);
382         SMB_ASSERT(root != NULL);
383
384         regedit->keys = tree_view_new(regedit, root, KEY_HEIGHT, KEY_WIDTH,
385                                       KEY_START_Y, KEY_START_X);
386         SMB_ASSERT(regedit->keys != NULL);
387
388         regedit->vl = value_list_new(regedit, VAL_HEIGHT, VAL_WIDTH,
389                                      VAL_START_Y, VAL_START_X);
390         SMB_ASSERT(regedit->vl != NULL);
391
392         regedit->tree_input = true;
393         print_heading(regedit);
394
395         tree_view_show(regedit->keys);
396         value_list_show(regedit->vl);
397
398         update_panels();
399         doupdate();
400         while (1) {
401                 c = regedit_getch();
402                 if (c == 'q' || c == 'Q') {
403                         break;
404                 }
405                 handle_main_input(regedit, c);
406                 update_panels();
407                 doupdate();
408         }
409
410         endwin();
411 }
412
413 int main(int argc, char **argv)
414 {
415         struct poptOption long_options[] = {
416                 POPT_AUTOHELP
417                 /* ... */
418                 POPT_COMMON_SAMBA
419                 POPT_COMMON_CONNECTION
420                 POPT_COMMON_CREDENTIALS
421                 POPT_TABLEEND
422         };
423         int opt;
424         poptContext pc;
425         struct user_auth_info *auth_info;
426         TALLOC_CTX *frame;
427         struct registry_context *ctx;
428         WERROR rv;
429
430         talloc_enable_leak_report_full();
431
432         frame = talloc_stackframe();
433
434         setup_logging("regedit", DEBUG_DEFAULT_STDERR);
435         lp_set_cmdline("log level", "0");
436
437         /* process options */
438         auth_info = user_auth_info_init(frame);
439         if (auth_info == NULL) {
440                 exit(1);
441         }
442         popt_common_set_auth_info(auth_info);
443         pc = poptGetContext("regedit", argc, (const char **)argv, long_options, 0);
444
445         while ((opt = poptGetNextOpt(pc)) != -1) {
446                 /* TODO */
447         }
448
449         if (!lp_load_global(get_dyn_CONFIGFILE())) {
450                 DEBUG(0, ("ERROR loading config file...\n"));
451                 exit(1);
452         }
453
454         /* some simple tests */
455
456         rv = reg_open_samba3(frame, &ctx);
457         if (!W_ERROR_IS_OK(rv)) {
458                 TALLOC_FREE(frame);
459
460                 return 1;
461         }
462
463         display_window(frame, ctx);
464
465         //talloc_report_full(frame, stdout);
466
467         TALLOC_FREE(frame);
468
469         return 0;
470 }