regedit: Add new value type selection dialog.
[gd/samba-autobuild/.git] / source3 / utils / regedit_dialog.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 "regedit_dialog.h"
22 #include "regedit_valuelist.h"
23 #include "util_reg.h"
24 #include "lib/registry/registry.h"
25 #include <stdarg.h>
26 #include <form.h>
27
28 static int dialog_free(struct dialog *dia)
29 {
30         if (dia->window) {
31                 delwin(dia->window);
32         }
33         if (dia->sub_window) {
34                 delwin(dia->sub_window);
35         }
36         if (dia->panel) {
37                 del_panel(dia->panel);
38         }
39         if (dia->choices) {
40                 unpost_menu(dia->choices);
41                 free_menu(dia->choices);
42         }
43         if (dia->choice_items) {
44                 ITEM **it;
45                 for (it = dia->choice_items; *it != NULL; ++it) {
46                         free_item(*it);
47                 }
48         }
49
50         return 0;
51 }
52
53 struct dialog *dialog_new(TALLOC_CTX *ctx, const char *title, int nlines,
54                           int ncols, int y, int x)
55 {
56         struct dialog *dia;
57
58         dia = talloc_zero(ctx, struct dialog);
59         if (dia == NULL) {
60                 return NULL;
61         }
62
63         talloc_set_destructor(dia, dialog_free);
64
65         dia->window = newwin(nlines, ncols, y, x);
66         if (dia->window == NULL) {
67                 goto fail;
68         }
69
70         box(dia->window, 0, 0);
71         mvwaddstr(dia->window, 0, 1, title);
72
73         /* body of the dialog within the box outline */
74         dia->sub_window = derwin(dia->window, nlines - 2, ncols - 2, 1, 1);
75         if (dia->sub_window == NULL) {
76                 goto fail;
77         }
78
79         dia->panel = new_panel(dia->window);
80         if (dia->panel == NULL) {
81                 goto fail;
82         }
83
84         return dia;
85
86 fail:
87         talloc_free(dia);
88
89         return NULL;
90
91 }
92
93 static void center_dialog_above_window(WINDOW *below, int *nlines, int *ncols,
94                                        int *y, int *x)
95 {
96         int maxy, maxx;
97         int centery, centerx;
98
99         getmaxyx(below, maxy, maxx);
100
101         centery = maxy / 2;
102         centerx = maxx / 2;
103         *y = 0;
104         *x = 0;
105
106         if (*nlines > maxy) {
107                 *nlines = maxy;
108         }
109         if (*ncols > maxx) {
110                 *ncols = maxx;
111         }
112
113         if (*nlines < centery) {
114                 *y = centery - *nlines;
115         }
116         if (*ncols < centerx) {
117                 *x = centerx - *ncols;
118         }
119 }
120
121 struct dialog *dialog_center_new(TALLOC_CTX *ctx, const char *title, int nlines,
122                                  int ncols, WINDOW *below)
123 {
124         int y, x;
125
126         center_dialog_above_window(below, &nlines, &ncols, &y, &x);
127
128         return dialog_new(ctx, title, nlines, ncols, y, x);
129 }
130
131 struct dialog *dialog_choice_new(TALLOC_CTX *ctx, const char *title,
132                                  const char **choices, int nlines,
133                                  int ncols, int y, int x)
134 {
135         size_t nchoices, i;
136         struct dialog *dia;
137
138         dia = dialog_new(ctx, title, nlines, ncols, y, x);
139         if (dia == NULL) {
140                 return NULL;
141         }
142
143         dia->menu_window = derwin(dia->sub_window, 1, ncols - 3,
144                                   nlines - 3, 0);
145         if (dia->menu_window == NULL) {
146                 goto fail;
147         }
148
149         for (nchoices = 0; choices[nchoices] != NULL; ++nchoices)
150                 ;
151         dia->choice_items = talloc_zero_array(dia, ITEM *, nchoices + 1);
152         if (dia->choice_items == NULL) {
153                 goto fail;
154         }
155         for (i = 0; i < nchoices; ++i) {
156                 char *desc = talloc_strdup(dia, choices[i]);
157                 if (desc == NULL) {
158                         goto fail;
159                 }
160                 dia->choice_items[i] = new_item(desc, desc);
161                 if (dia->choice_items[i] == NULL) {
162                         goto fail;
163                 }
164                 /* store choice index */
165                 set_item_userptr(dia->choice_items[i], (void*)(uintptr_t)i);
166         }
167
168         dia->choices = new_menu(dia->choice_items);
169         if (dia->choices == NULL) {
170                 goto fail;
171         }
172
173         set_menu_format(dia->choices, 1, ncols);
174         set_menu_win(dia->choices, dia->sub_window);
175         set_menu_sub(dia->choices, dia->menu_window);
176         menu_opts_off(dia->choices, O_SHOWDESC);
177         set_menu_mark(dia->choices, "* ");
178         post_menu(dia->choices);
179         wmove(dia->sub_window, 0, 0);
180
181         return dia;
182
183 fail:
184         talloc_free(dia);
185
186         return NULL;
187 }
188
189 struct dialog *dialog_choice_center_new(TALLOC_CTX *ctx, const char *title,
190                                         const char **choices, int nlines,
191                                         int ncols, WINDOW *below)
192 {
193         int y, x;
194
195         center_dialog_above_window(below, &nlines, &ncols, &y, &x);
196
197         return dialog_choice_new(ctx, title, choices, nlines, ncols, y, x);
198 }
199
200 struct dialog *dialog_confirm_new(TALLOC_CTX *ctx, const char *title,
201                                   WINDOW *below, const char *msg, ...)
202 {
203         va_list ap;
204         struct dialog *dia;
205         char *str;
206         const char *choices[] = {
207                 "Ok",
208                 "Cancel",
209                 NULL
210         };
211         int width;
212
213         va_start(ap, msg);
214         str = talloc_vasprintf(ctx, msg, ap);
215         va_end(ap);
216         if (str == NULL) {
217                 return NULL;
218         }
219
220         width = strlen(str) + 2;
221
222         dia = dialog_choice_center_new(ctx, title, choices, 5, width, below);
223         if (dia == NULL) {
224                 return NULL;
225         }
226
227         waddstr(dia->sub_window, str);
228         talloc_free(str);
229
230         return dia;
231 }
232
233 static int handle_menu_input(MENU *menu, int c)
234 {
235         ITEM *item;
236
237         switch (c) {
238         case KEY_LEFT:
239                 menu_driver(menu, REQ_LEFT_ITEM);
240                 break;
241         case KEY_RIGHT:
242                 menu_driver(menu, REQ_RIGHT_ITEM);
243                 break;
244         case KEY_ENTER:
245         case '\n':
246                 item = current_item(menu);
247                 return (int)(uintptr_t)item_userptr(item);
248         }
249
250         return -1;
251 }
252
253 int dialog_modal_loop(struct dialog *dia)
254 {
255         int c;
256         int selection = -1;
257
258         keypad(dia->window, true);
259         update_panels();
260         doupdate();
261
262         while (selection == -1) {
263                 c = wgetch(dia->window);
264                 selection = handle_menu_input(dia->choices, c);
265                 update_panels();
266                 doupdate();
267         }
268
269         talloc_free(dia);
270
271         return selection;
272 }
273
274 static void handle_form_input(FORM *frm, int c)
275 {
276         switch (c) {
277         case '\n':
278                 form_driver(frm, REQ_NEW_LINE);
279                 break;
280         case KEY_UP:
281                 form_driver(frm, REQ_UP_CHAR);
282                 break;
283         case KEY_DOWN:
284                 form_driver(frm, REQ_DOWN_CHAR);
285                 break;
286         case '\b':
287         case KEY_BACKSPACE:
288                 form_driver(frm, REQ_DEL_PREV);
289                 break;
290         case KEY_LEFT:
291                 form_driver(frm, REQ_LEFT_CHAR);
292                 break;
293         case KEY_RIGHT:
294                 form_driver(frm, REQ_RIGHT_CHAR);
295                 break;
296         default:
297                 form_driver(frm, c);
298                 break;
299         }
300 }
301
302 #define MAX_FIELDS 8
303
304 enum input_section {
305         IN_NAME,
306         IN_DATA,
307         IN_MENU
308 };
309
310 struct edit_dialog {
311         struct dialog *dia;
312         WINDOW *input_win;
313         FORM *input;
314         FIELD *field[MAX_FIELDS];
315         enum input_section section;
316 };
317
318 static int edit_dialog_free(struct edit_dialog *edit)
319 {
320         if (edit->input) {
321                 unpost_form(edit->input);
322         }
323         if (edit->field[0]) {
324                 free_field(edit->field[0]);
325         }
326         if (edit->field[1]) {
327                 free_field(edit->field[1]);
328         }
329         delwin(edit->input_win);
330
331         return 0;
332 }
333
334 static WERROR fill_value_buffer(struct edit_dialog *edit,
335                                 const struct value_item *vitem)
336 {
337         char *tmp;
338
339         switch (vitem->type) {
340         case REG_DWORD: {
341                 uint32_t v = 0;
342                 if (vitem->data.length >= 4) {
343                         v = IVAL(vitem->data.data, 0);
344                 }
345                 tmp = talloc_asprintf(edit, "0x%x", v);
346                 if (tmp == NULL) {
347                         return WERR_NOMEM;
348                 }
349                 set_field_buffer(edit->field[1], 0, tmp);
350                 talloc_free(tmp);
351                 set_field_type(edit->field[1], TYPE_REGEXP,
352                                "^ *([0-9]+|0[xX][0-9a-fA-F]+) *$");
353                 break;
354         }
355         case REG_SZ:
356         case REG_EXPAND_SZ: {
357                 const char *s;
358
359                 if (!pull_reg_sz(edit, &vitem->data, &s)) {
360                         return WERR_NOMEM;
361                 }
362                 set_field_buffer(edit->field[1], 0, s);
363                 break;
364         }
365         case REG_MULTI_SZ: {
366                 const char **p, **a;
367                 char *buf = NULL;
368
369                 if (!pull_reg_multi_sz(edit, &vitem->data, &a)) {
370                         return WERR_NOMEM;
371                 }
372                 for (p = a; *p != NULL; ++p) {
373                         if (buf == NULL) {
374                                 buf = talloc_asprintf(edit, "%s\n", *p);
375                         } else {
376                                 buf = talloc_asprintf_append(buf, "%s\n", *p);
377                         }
378                         if (buf == NULL) {
379                                 return WERR_NOMEM;
380                         }
381                 }
382                 set_field_buffer(edit->field[1], 0, buf);
383                 talloc_free(buf);
384         }
385
386         }
387
388         return WERR_OK;
389 }
390
391 static char *string_trim_n(TALLOC_CTX *ctx, const char *buf, size_t n)
392 {
393         char *str;
394
395         str = talloc_strndup(ctx, buf, n);
396
397         if (str) {
398                 trim_string(str, " ", " ");
399         }
400
401         return str;
402 }
403
404 static char *string_trim(TALLOC_CTX *ctx, const char *buf)
405 {
406         char *str;
407
408         str = talloc_strdup(ctx, buf);
409
410         if (str) {
411                 trim_string(str, " ", " ");
412         }
413
414         return str;
415 }
416
417 static WERROR set_value(struct edit_dialog *edit, struct registry_key *key,
418                         uint32_t type)
419 {
420         WERROR rv;
421         DATA_BLOB blob;
422         const char *buf = field_buffer(edit->field[1], 0);
423         char *name = string_trim(edit, field_buffer(edit->field[0], 0));
424
425         if (!buf) {
426                 return WERR_OK;
427         }
428         if (!field_status(edit->field[1])) {
429                 return WERR_OK;
430         }
431
432         switch (type) {
433         case REG_DWORD: {
434                 uint32_t val;
435                 int base = 10;
436
437                 if (buf[0] == '0' && tolower(buf[1]) == 'x') {
438                         base = 16;
439                 }
440
441                 val = strtoul(buf, NULL, base);
442                 blob = data_blob_talloc(edit, NULL, sizeof(val));
443                 SIVAL(blob.data, 0, val);
444                 rv = WERR_OK;
445                 break;
446         }
447         case REG_SZ:
448         case REG_EXPAND_SZ: {
449                 char *str = string_trim(edit, buf);
450                 if (!str || !push_reg_sz(edit, &blob, str)) {
451                         rv = WERR_NOMEM;
452                 }
453                 break;
454         }
455         case REG_MULTI_SZ: {
456                 int rows, cols, max;
457                 const char **arr;
458                 size_t i;
459
460                 dynamic_field_info(edit->field[1], &rows, &cols, &max);
461
462                 arr = talloc_zero_array(edit, const char *, rows + 1);
463                 if (arr == NULL) {
464                         return WERR_NOMEM;
465                 }
466                 for (i = 0; *buf; ++i, buf += cols) {
467                         SMB_ASSERT(i < rows);
468                         arr[i] = string_trim_n(edit, buf, cols);
469                 }
470                 if (!push_reg_multi_sz(edit, &blob, arr)) {
471                         rv = WERR_NOMEM;
472                 }
473                 break;
474         }
475         }
476
477         rv = reg_val_set(key, name, type, blob);
478
479         return rv;
480 }
481
482 static void section_down(struct edit_dialog *edit)
483 {
484         switch (edit->section) {
485         case IN_NAME:
486                 if (form_driver(edit->input, REQ_VALIDATION) == E_OK) {
487                         edit->section = IN_DATA;
488                         set_current_field(edit->input, edit->field[1]);
489                 }
490                 break;
491         case IN_DATA:
492                 if (form_driver(edit->input, REQ_VALIDATION) == E_OK) {
493                         edit->section = IN_MENU;
494                         menu_driver(edit->dia->choices, REQ_FIRST_ITEM);
495                 }
496                 break;
497         case IN_MENU:
498                 edit->section = IN_NAME;
499                 set_current_field(edit->input, edit->field[0]);
500                 break;
501         }
502 }
503
504 static void section_up(struct edit_dialog *edit)
505 {
506         switch (edit->section) {
507         case IN_NAME:
508                 if (form_driver(edit->input, REQ_VALIDATION) == E_OK) {
509                         edit->section = IN_MENU;
510                         menu_driver(edit->dia->choices, REQ_FIRST_ITEM);
511                 }
512                 break;
513         case IN_DATA:
514                 if (form_driver(edit->input, REQ_VALIDATION) == E_OK) {
515                         edit->section = IN_NAME;
516                         set_current_field(edit->input, edit->field[0]);
517                 }
518                 break;
519         case IN_MENU:
520                 edit->section = IN_DATA;
521                 set_current_field(edit->input, edit->field[1]);
522                 break;
523         }
524 }
525
526 WERROR dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key, uint32_t type,
527                       const struct value_item *vitem, WINDOW *below)
528 {
529         struct edit_dialog *edit;
530         const char *choices[] = {
531                 "Ok",
532                 "Cancel",
533                 NULL
534         };
535         char *title;
536         int nlines, ncols, val_rows;
537         WERROR rv = WERR_NOMEM;
538         int selection;
539
540         edit = talloc_zero(ctx, struct edit_dialog);
541         if (edit == NULL) {
542                 return rv;
543         }
544         talloc_set_destructor(edit, edit_dialog_free);
545
546         title = talloc_asprintf(edit, "Edit %s value", str_regtype(vitem->type));
547         if (title == NULL) {
548                 goto finish;
549         }
550
551         nlines = 9;
552         if (vitem->type == REG_MULTI_SZ) {
553                 nlines += 4;
554         }
555         ncols = 50;
556         edit->dia = dialog_choice_center_new(edit, title, choices, nlines,
557                                              ncols, below);
558         talloc_free(title);
559         if (edit->dia == NULL) {
560                 goto finish;
561         }
562
563         /* name */
564         edit->field[0] = new_field(1, ncols - 4, 1, 1, 0, 0);
565         if (edit->field[0] == NULL) {
566                 goto finish;
567         }
568
569         /* data */
570         val_rows = 1;
571         if (vitem->type == REG_MULTI_SZ) {
572                 val_rows += 4;
573         }
574         edit->field[1] = new_field(val_rows, ncols - 4, 4, 1, 0, 0);
575         if (edit->field[1] == NULL) {
576                 goto finish;
577         }
578         set_field_back(edit->field[0], A_REVERSE);
579         set_field_back(edit->field[1], A_REVERSE);
580         field_opts_off(edit->field[0], O_BLANK | O_AUTOSKIP | O_STATIC);
581         field_opts_off(edit->field[1], O_BLANK | O_AUTOSKIP | O_STATIC | O_WRAP);
582
583         if (vitem) {
584                 set_field_buffer(edit->field[0], 0, vitem->value_name);
585                 field_opts_off(edit->field[0], O_EDIT);
586                 fill_value_buffer(edit, vitem);
587         }
588
589         edit->input = new_form(edit->field);
590         if (edit->input == NULL) {
591                 goto finish;
592         }
593         form_opts_off(edit->input, O_NL_OVERLOAD | O_BS_OVERLOAD);
594
595         edit->input_win = derwin(edit->dia->sub_window, nlines - 3, ncols - 3, 0, 0);
596         if (edit->input_win == NULL) {
597                 goto finish;
598         }
599
600         set_form_win(edit->input, edit->dia->sub_window);
601         set_form_sub(edit->input, edit->input_win);
602         post_form(edit->input);
603         mvwprintw(edit->dia->sub_window, 0, 0, "Name");
604         mvwprintw(edit->dia->sub_window, 3, 0, "Data");
605
606         keypad(edit->dia->window, true);
607         update_panels();
608         doupdate();
609
610         edit->section = IN_NAME;
611
612         while (1) {
613                 int c = wgetch(edit->dia->window);
614                 if (c == '\t') {
615                         section_down(edit);
616                         continue;
617                 } else if (c == KEY_BTAB) {
618                         section_up(edit);
619                         continue;
620                 }
621
622                 if (edit->section == IN_NAME || edit->section == IN_DATA) {
623                         handle_form_input(edit->input, c);
624                 } else {
625                         selection = handle_menu_input(edit->dia->choices, c);
626                         if (selection != -1) {
627                                 goto finish;
628                         }
629                 }
630
631                 update_panels();
632                 doupdate();
633         }
634
635 finish:
636         if (selection == DIALOG_OK) {
637                 rv = set_value(edit, key, type);
638         }
639
640         talloc_free(edit);
641
642         return rv;
643 }
644
645 int dialog_select_type(TALLOC_CTX *ctx, int *type, WINDOW *below)
646 {
647         struct dialog *dia;
648         const char *choices[] = {
649                 "OK",
650                 "Cancel",
651                 NULL
652         };
653         const char *reg_types[] = {
654                 "REG_DWORD",
655                 "REG_SZ",
656                 "REG_EXPAND_SZ",
657                 "REG_MULTI_SZ",
658         };
659 #define NTYPES (sizeof(reg_types) / sizeof(const char*))
660         ITEM **item;
661         MENU *list;
662         WINDOW *type_win;
663         int sel = -1;
664         size_t i;
665
666         dia = dialog_choice_center_new(ctx, "New Value", choices, 10, 20,
667                                        below);
668         if (dia == NULL) {
669                 return -1;
670         }
671
672         mvwprintw(dia->sub_window, 0, 0, "Choose type:");
673         type_win = derwin(dia->sub_window, 6, 18, 1, 0);
674         if (type_win == NULL) {
675                 goto finish;
676         }
677
678         item = talloc_zero_array(dia, ITEM *, NTYPES + 1);
679         if (item == NULL) {
680                 goto finish;
681         }
682
683         for (i = 0; i < NTYPES; ++i) {
684                 int t = regtype_by_string(reg_types[i]);
685
686                 item[i] = new_item(reg_types[i], reg_types[i]);
687                 if (item[i] == NULL) {
688                         goto finish;
689                 }
690                 set_item_userptr(item[i], (void*)(uintptr_t)t);
691         }
692
693         list = new_menu(item);
694         if (list == NULL) {
695                 goto finish;
696         }
697
698         set_menu_format(list, 7, 1);
699         set_menu_win(list, dia->sub_window);
700         set_menu_sub(list, type_win);
701         menu_opts_off(list, O_SHOWDESC);
702         set_menu_mark(list, "* ");
703         post_menu(list);
704
705         keypad(dia->window, true);
706         update_panels();
707         doupdate();
708
709         while (sel == -1) {
710                 ITEM *it;
711                 int c = wgetch(dia->window);
712
713                 switch (c) {
714                 case KEY_UP:
715                         menu_driver(list, REQ_UP_ITEM);
716                         break;
717                 case KEY_DOWN:
718                         menu_driver(list, REQ_DOWN_ITEM);
719                         break;
720                 case KEY_LEFT:
721                         menu_driver(dia->choices, REQ_LEFT_ITEM);
722                         break;
723                 case KEY_RIGHT:
724                         menu_driver(dia->choices, REQ_RIGHT_ITEM);
725                         break;
726                 case '\n':
727                 case KEY_ENTER:
728                         it = current_item(list);
729                         *type = (int)(uintptr_t)item_userptr(it);
730                         it = current_item(dia->choices);
731                         sel = (int)(uintptr_t)item_userptr(it);
732                         break;
733                 }
734         }
735
736 finish:
737         if (list) {
738                 unpost_menu(list);
739                 free_menu(list);
740         }
741         if (item) {
742                 ITEM **it;
743                 for (it = item; *it; ++it) {
744                         free_item(*it);
745                 }
746         }
747         if (type_win) {
748                 delwin(type_win);
749         }
750         talloc_free(dia);
751
752         return sel;
753 }