s3:libsmb: make use of smb1cli_tcon_set_values()
[mat/samba.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.h"
22 #include "regedit_dialog.h"
23 #include "regedit_valuelist.h"
24 #include "regedit_hexedit.h"
25 #include "util_reg.h"
26 #include "lib/registry/registry.h"
27 #include <stdarg.h>
28 #include <form.h>
29
30 static char *string_trim_n(TALLOC_CTX *ctx, const char *buf, size_t n)
31 {
32         char *str;
33
34         str = talloc_strndup(ctx, buf, n);
35
36         if (str) {
37                 trim_string(str, " ", " ");
38         }
39
40         return str;
41 }
42
43 static char *string_trim(TALLOC_CTX *ctx, const char *buf)
44 {
45         char *str;
46
47         str = talloc_strdup(ctx, buf);
48
49         if (str) {
50                 trim_string(str, " ", " ");
51         }
52
53         return str;
54 }
55
56 static int dialog_free(struct dialog *dia)
57 {
58         if (dia->window) {
59                 delwin(dia->window);
60         }
61         if (dia->sub_window) {
62                 delwin(dia->sub_window);
63         }
64         if (dia->panel) {
65                 del_panel(dia->panel);
66         }
67         if (dia->choices) {
68                 unpost_menu(dia->choices);
69                 free_menu(dia->choices);
70         }
71         if (dia->choice_items) {
72                 ITEM **it;
73                 for (it = dia->choice_items; *it != NULL; ++it) {
74                         free_item(*it);
75                 }
76         }
77
78         return 0;
79 }
80
81 struct dialog *dialog_new(TALLOC_CTX *ctx, const char *title, int nlines,
82                           int ncols, int y, int x)
83 {
84         struct dialog *dia;
85
86         dia = talloc_zero(ctx, struct dialog);
87         if (dia == NULL) {
88                 return NULL;
89         }
90
91         talloc_set_destructor(dia, dialog_free);
92
93         dia->window = newwin(nlines, ncols, y, x);
94         if (dia->window == NULL) {
95                 goto fail;
96         }
97
98         box(dia->window, 0, 0);
99         mvwaddstr(dia->window, 0, 1, title);
100
101         /* body of the dialog within the box outline */
102         dia->sub_window = derwin(dia->window, nlines - 2, ncols - 2, 1, 1);
103         if (dia->sub_window == NULL) {
104                 goto fail;
105         }
106
107         dia->panel = new_panel(dia->window);
108         if (dia->panel == NULL) {
109                 goto fail;
110         }
111
112         return dia;
113
114 fail:
115         talloc_free(dia);
116
117         return NULL;
118
119 }
120
121 static void center_dialog_above_window(int *nlines, int *ncols,
122                                        int *y, int *x)
123 {
124         int centery, centerx;
125
126         centery = LINES / 2;
127         centerx = COLS / 2;
128         *y = 0;
129         *x = 0;
130
131         if (*nlines > LINES) {
132                 *nlines = LINES;
133         }
134         if (*ncols > COLS) {
135                 *ncols = COLS;
136         }
137
138         if (*nlines/2 < centery) {
139                 *y = centery - *nlines / 2;
140         }
141         if (*ncols/2 < centerx) {
142                 *x = centerx - *ncols / 2;
143         }
144 }
145
146 static int dialog_getch(struct dialog *dia)
147 {
148         int c;
149
150         c = regedit_getch();
151
152         if (c == KEY_RESIZE) {
153                 int nlines, ncols, y, x;
154
155                 getmaxyx(dia->window, nlines, ncols);
156                 getbegyx(dia->window, y, x);
157                 if (dia->centered) {
158                         center_dialog_above_window(&nlines, &ncols, &y, &x);
159                 } else {
160                         if (nlines + y > LINES) {
161                                 if (nlines > LINES) {
162                                         y = 0;
163                                 } else {
164                                         y = LINES - nlines;
165                                 }
166                         }
167                         if (ncols + x > COLS) {
168                                 if (ncols > COLS) {
169                                         x = 0;
170                                 } else {
171                                         x = COLS - ncols;
172                                 }
173                         }
174                 }
175                 move_panel(dia->panel, y, x);
176                 doupdate();
177         }
178
179         return c;
180 }
181
182 struct dialog *dialog_center_new(TALLOC_CTX *ctx, const char *title,
183                                  int nlines, int ncols)
184 {
185         struct dialog *dia;
186         int y, x;
187
188         center_dialog_above_window(&nlines, &ncols, &y, &x);
189
190         dia = dialog_new(ctx, title, nlines, ncols, y, x);
191         if (dia) {
192                 dia->centered = true;
193         }
194
195         return dia;
196 }
197
198 struct dialog *dialog_choice_new(TALLOC_CTX *ctx, const char *title,
199                                  const char **choices, int nlines,
200                                  int ncols, int y, int x)
201 {
202         size_t nchoices, i;
203         struct dialog *dia;
204
205         dia = dialog_new(ctx, title, nlines, ncols, y, x);
206         if (dia == NULL) {
207                 return NULL;
208         }
209
210         dia->menu_window = derwin(dia->sub_window, 1, ncols - 3,
211                                   nlines - 3, 0);
212         if (dia->menu_window == NULL) {
213                 goto fail;
214         }
215
216         for (nchoices = 0; choices[nchoices] != NULL; ++nchoices)
217                 ;
218         dia->choice_items = talloc_zero_array(dia, ITEM *, nchoices + 1);
219         if (dia->choice_items == NULL) {
220                 goto fail;
221         }
222         for (i = 0; i < nchoices; ++i) {
223                 char *desc = talloc_strdup(dia, choices[i]);
224                 if (desc == NULL) {
225                         goto fail;
226                 }
227                 dia->choice_items[i] = new_item(desc, desc);
228                 if (dia->choice_items[i] == NULL) {
229                         goto fail;
230                 }
231                 /* store choice index */
232                 set_item_userptr(dia->choice_items[i], (void*)(uintptr_t)i);
233         }
234
235         dia->choices = new_menu(dia->choice_items);
236         if (dia->choices == NULL) {
237                 goto fail;
238         }
239
240         set_menu_format(dia->choices, 1, ncols);
241         set_menu_win(dia->choices, dia->sub_window);
242         set_menu_sub(dia->choices, dia->menu_window);
243         menu_opts_off(dia->choices, O_SHOWDESC);
244         set_menu_mark(dia->choices, "* ");
245         post_menu(dia->choices);
246         wmove(dia->sub_window, 0, 0);
247
248         return dia;
249
250 fail:
251         talloc_free(dia);
252
253         return NULL;
254 }
255
256 struct dialog *dialog_choice_center_new(TALLOC_CTX *ctx, const char *title,
257                                         const char **choices, int nlines,
258                                         int ncols)
259 {
260         int y, x;
261         struct dialog *dia;
262         center_dialog_above_window(&nlines, &ncols, &y, &x);
263
264         dia = dialog_choice_new(ctx, title, choices, nlines, ncols, y, x);
265         if (dia) {
266                 dia->centered = true;
267         }
268
269         return dia;
270 }
271
272 static bool current_item_is_first(MENU *menu)
273 {
274         const ITEM *it = current_item(menu);
275
276         return item_index(it) == 0;
277 }
278
279 static bool current_item_is_last(MENU *menu)
280 {
281         const ITEM *it = current_item(menu);
282
283         return item_index(it) == item_count(menu) - 1;
284 }
285
286 static int handle_menu_input(MENU *menu, int c)
287 {
288         ITEM *item;
289
290         switch (c) {
291         case KEY_BTAB:
292                 if (current_item_is_first(menu)) {
293                         menu_driver(menu, REQ_LAST_ITEM);
294                 } else {
295                         menu_driver(menu, REQ_LEFT_ITEM);
296                 }
297                 break;
298         case KEY_LEFT:
299                 menu_driver(menu, REQ_LEFT_ITEM);
300                 break;
301         case '\t':
302                 if (current_item_is_last(menu)) {
303                         menu_driver(menu, REQ_FIRST_ITEM);
304                         break;
305                 } else {
306                         menu_driver(menu, REQ_RIGHT_ITEM);
307                 }
308         case KEY_RIGHT:
309                 menu_driver(menu, REQ_RIGHT_ITEM);
310                 break;
311         case KEY_ENTER:
312         case '\n':
313                 item = current_item(menu);
314                 return (int)(uintptr_t)item_userptr(item);
315         }
316
317         return -1;
318 }
319
320 static void handle_form_input(FORM *frm, int c)
321 {
322         switch (c) {
323         case '\n':
324                 form_driver(frm, REQ_NEW_LINE);
325                 break;
326         case KEY_UP:
327                 form_driver(frm, REQ_UP_CHAR);
328                 break;
329         case KEY_DOWN:
330                 form_driver(frm, REQ_DOWN_CHAR);
331                 break;
332         case '\b':
333         case KEY_BACKSPACE:
334                 form_driver(frm, REQ_DEL_PREV);
335                 break;
336         case KEY_LEFT:
337                 form_driver(frm, REQ_LEFT_CHAR);
338                 break;
339         case KEY_RIGHT:
340                 form_driver(frm, REQ_RIGHT_CHAR);
341                 break;
342         default:
343                 form_driver(frm, c);
344                 break;
345         }
346 }
347
348 static int modal_loop(struct dialog *dia)
349 {
350         int c;
351         int selection = -1;
352
353         update_panels();
354         doupdate();
355
356         while (selection == -1) {
357                 c = dialog_getch(dia);
358                 selection = handle_menu_input(dia->choices, c);
359                 update_panels();
360                 doupdate();
361         }
362
363         talloc_free(dia);
364
365         return selection;
366 }
367
368 static struct dialog *dialog_msg_new(TALLOC_CTX *ctx, const char *title,
369                                      const char **choices, int nlines,
370                                      const char *msg, va_list ap)
371 {
372         struct dialog *dia;
373         char *str;
374         int width;
375 #define MIN_WIDTH 20
376
377         str = talloc_vasprintf(ctx, msg, ap);
378         if (str == NULL) {
379                 return NULL;
380         }
381
382         width = strlen(str) + 2;
383         if (width < MIN_WIDTH) {
384                 width = MIN_WIDTH;
385         }
386         dia = dialog_choice_center_new(ctx, title, choices, nlines, width);
387         if (dia == NULL) {
388                 return NULL;
389         }
390
391         waddstr(dia->sub_window, str);
392         talloc_free(str);
393
394         return dia;
395 }
396
397 int dialog_input(TALLOC_CTX *ctx, char **output, const char *title,
398                  const char *msg, ...)
399 {
400         va_list ap;
401         struct dialog *dia;
402         const char *choices[] = {
403                 "Ok",
404                 "Cancel",
405                 NULL
406         };
407         FIELD *field[2] = {0};
408         FORM *input;
409         WINDOW *input_win;
410         int y, x;
411         int rv = -1;
412         bool input_section = true;
413
414         va_start(ap, msg);
415         dia = dialog_msg_new(ctx, title, choices, 7, msg, ap);
416         va_end(ap);
417         if (dia == NULL) {
418                 return -1;
419         }
420
421         getmaxyx(dia->sub_window, y, x);
422         input_win = derwin(dia->sub_window, 1, x - 2, 2, 1);
423         if (input_win == NULL) {
424                 goto finish;
425         }
426         field[0] = new_field(1, x - 2, 0, 0, 0, 0);
427         if (field[0] == NULL) {
428                 goto finish;
429         }
430
431         field_opts_off(field[0], O_BLANK | O_AUTOSKIP | O_STATIC);
432         set_field_back(field[0], A_REVERSE);
433
434         input = new_form(field);
435         form_opts_off(input, O_NL_OVERLOAD | O_BS_OVERLOAD);
436         set_form_win(input, dia->sub_window);
437         set_form_sub(input, input_win);
438         set_current_field(input, field[0]);
439         post_form(input);
440         *output = NULL;
441
442         update_panels();
443         doupdate();
444
445         while (rv == -1) {
446                 int c = dialog_getch(dia);
447
448                 if (c == '\t' || c == KEY_BTAB) {
449                         if (input_section) {
450                                 int valid = form_driver(input, REQ_VALIDATION);
451                                 if (valid == E_OK) {
452                                         input_section = false;
453                                         if (c == '\t') {
454                                                 menu_driver(dia->choices,
455                                                             REQ_FIRST_ITEM);
456                                         } else {
457                                                 menu_driver(dia->choices,
458                                                             REQ_LAST_ITEM);
459                                         }
460                                         pos_menu_cursor(dia->choices);
461                                 }
462                         } else {
463                                 if ((c == '\t' &&
464                                      current_item_is_last(dia->choices)) ||
465                                     (c == KEY_BTAB &&
466                                      current_item_is_first(dia->choices))) {
467                                         input_section = true;
468                                         set_current_field(input, field[0]);
469                                         pos_form_cursor(input);
470                                 } else {
471                                         handle_menu_input(dia->choices, c);
472                                 }
473                         }
474                 } else if (input_section) {
475                         handle_form_input(input, c);
476                 } else {
477                         rv = handle_menu_input(dia->choices, c);
478                         if (rv == DIALOG_OK) {
479                                 const char *buf = field_buffer(field[0], 0);
480                                 *output = string_trim(ctx, buf);
481                         }
482                 }
483                 update_panels();
484                 doupdate();
485         }
486
487 finish:
488         if (input) {
489                 unpost_form(input);
490                 free_form(input);
491         }
492         if (field[0]) {
493                 free_field(field[0]);
494         }
495         if (input_win) {
496                 delwin(input_win);
497         }
498         talloc_free(dia);
499
500         return rv;
501 }
502
503 int dialog_notice(TALLOC_CTX *ctx, enum dialog_type type,
504                   const char *title, const char *msg, ...)
505 {
506         va_list ap;
507         struct dialog *dia;
508         const char *choices[] = {
509                 "Ok",
510                 "Cancel",
511                 NULL
512         };
513
514         if (type == DIA_ALERT) {
515                 choices[1] = NULL;
516         }
517
518         va_start(ap, msg);
519         dia = dialog_msg_new(ctx, title, choices, 5, msg, ap);
520         va_end(ap);
521         if (dia == NULL) {
522                 return -1;
523         }
524
525         return modal_loop(dia);
526 }
527
528 #define EDIT_WIDTH              50
529 #define EDIT_INTERNAL_WIDTH     (EDIT_WIDTH - 2)
530 #define EDIT_INPUT_WIDTH        (EDIT_INTERNAL_WIDTH - 2)
531
532 #define EDIT_NAME_LABEL_Y       0
533 #define EDIT_NAME_LABEL_X       0
534 #define EDIT_NAME_LABEL_WIDTH   EDIT_INTERNAL_WIDTH
535 #define EDIT_NAME_LABEL_HEIGHT  1
536 #define EDIT_NAME_INPUT_Y       1
537 #define EDIT_NAME_INPUT_X       1
538 #define EDIT_NAME_INPUT_WIDTH   EDIT_INPUT_WIDTH
539 #define EDIT_NAME_INPUT_HEIGHT  1
540
541 #define EDIT_DATA_LABEL_Y       3
542 #define EDIT_DATA_LABEL_X       0
543 #define EDIT_DATA_LABEL_WIDTH   EDIT_INTERNAL_WIDTH
544 #define EDIT_DATA_LABEL_HEIGHT  1
545 #define EDIT_DATA_INPUT_Y       4
546 #define EDIT_DATA_INPUT_X       1
547 #define EDIT_DATA_INPUT_WIDTH   EDIT_INPUT_WIDTH
548 #define EDIT_DATA_HEIGHT_ONELINE        1
549 #define EDIT_DATA_HEIGHT_MULTILINE      5
550 #define EDIT_DATA_HEIGHT_BUF            10
551
552 #define EDIT_FORM_WIN_Y                 0
553 #define EDIT_FORM_WIN_X                 0
554 #define EDIT_FORM_WIN_HEIGHT_ONELINE    \
555         (EDIT_NAME_LABEL_HEIGHT + EDIT_NAME_INPUT_HEIGHT + 1 + \
556          EDIT_DATA_LABEL_HEIGHT + EDIT_DATA_HEIGHT_ONELINE)
557
558 #define EDIT_FORM_WIN_HEIGHT_MULTILINE  \
559         (EDIT_NAME_LABEL_HEIGHT + EDIT_NAME_INPUT_HEIGHT + 1 + \
560          EDIT_DATA_LABEL_HEIGHT + EDIT_DATA_HEIGHT_MULTILINE)
561
562 #define EDIT_FORM_WIN_HEIGHT_BUF        \
563         (EDIT_NAME_LABEL_HEIGHT + EDIT_NAME_INPUT_HEIGHT + 1 + \
564          EDIT_DATA_LABEL_HEIGHT)
565
566 #define EDIT_FORM_WIN_WIDTH             EDIT_INTERNAL_WIDTH
567
568 #define EDIT_PAD                5
569 #define EDIT_HEIGHT_ONELINE     (EDIT_FORM_WIN_HEIGHT_ONELINE + EDIT_PAD)
570
571 #define EDIT_HEIGHT_MULTILINE   (EDIT_FORM_WIN_HEIGHT_MULTILINE + EDIT_PAD)
572
573 #define EDIT_HEIGHT_BUF         \
574         (EDIT_FORM_WIN_HEIGHT_BUF + EDIT_DATA_HEIGHT_BUF + EDIT_PAD)
575
576 #define MAX_FIELDS 5
577 #define FLD_NAME 1
578 #define FLD_DATA 3
579
580 #define DIALOG_RESIZE 2
581
582 enum input_section {
583         IN_NAME,
584         IN_DATA,
585         IN_MENU
586 };
587
588 struct edit_dialog {
589         struct dialog *dia;
590         WINDOW *input_win;
591         FORM *input;
592         FIELD *field[MAX_FIELDS];
593         struct hexedit *buf;
594         enum input_section section;
595         bool closing;
596         uint32_t mode;
597 };
598
599 static int edit_dialog_free(struct edit_dialog *edit)
600 {
601         FIELD **f;
602
603         if (edit->input) {
604                 unpost_form(edit->input);
605                 free_form(edit->input);
606         }
607         for (f = edit->field; *f; ++f) {
608                 free_field(*f);
609         }
610         delwin(edit->input_win);
611
612         return 0;
613 }
614
615 static WERROR fill_value_buffer(struct edit_dialog *edit,
616                                 const struct value_item *vitem)
617 {
618         char *tmp;
619
620         switch (vitem->type) {
621         case REG_DWORD: {
622                 uint32_t v = 0;
623                 if (vitem->data.length >= 4) {
624                         v = IVAL(vitem->data.data, 0);
625                 }
626                 tmp = talloc_asprintf(edit, "0x%x", v);
627                 if (tmp == NULL) {
628                         return WERR_NOMEM;
629                 }
630                 set_field_buffer(edit->field[FLD_DATA], 0, tmp);
631                 talloc_free(tmp);
632                 break;
633         }
634         case REG_SZ:
635         case REG_EXPAND_SZ: {
636                 const char *s;
637
638                 if (!pull_reg_sz(edit, &vitem->data, &s)) {
639                         return WERR_NOMEM;
640                 }
641                 set_field_buffer(edit->field[FLD_DATA], 0, s);
642                 break;
643         }
644         case REG_MULTI_SZ: {
645                 const char **p, **a;
646                 char *buf = NULL;
647
648                 if (!pull_reg_multi_sz(edit, &vitem->data, &a)) {
649                         return WERR_NOMEM;
650                 }
651                 for (p = a; *p != NULL; ++p) {
652                         if (buf == NULL) {
653                                 buf = talloc_asprintf(edit, "%s\n", *p);
654                         } else {
655                                 buf = talloc_asprintf_append(buf, "%s\n", *p);
656                         }
657                         if (buf == NULL) {
658                                 return WERR_NOMEM;
659                         }
660                 }
661                 set_field_buffer(edit->field[FLD_DATA], 0, buf);
662                 talloc_free(buf);
663         }
664         case REG_BINARY:
665                 /* initialized upon dialog creation */
666                 break;
667         }
668
669         return WERR_OK;
670 }
671
672 static bool value_exists(TALLOC_CTX *ctx, const struct registry_key *key,
673                          const char *name)
674 {
675         uint32_t type;
676         DATA_BLOB blob;
677         WERROR rv;
678
679         rv = reg_key_get_value_by_name(ctx, key, name, &type, &blob);
680
681         return W_ERROR_IS_OK(rv);
682 }
683
684 static WERROR set_value(struct edit_dialog *edit, struct registry_key *key,
685                         uint32_t type, bool new_value)
686 {
687         WERROR rv;
688         DATA_BLOB blob;
689         char *name = string_trim(edit, field_buffer(edit->field[FLD_NAME], 0));
690
691         if (!new_value && !edit->buf && !field_status(edit->field[FLD_DATA])) {
692                 return WERR_OK;
693         }
694         if (new_value && value_exists(edit, key, name)) {
695                 return WERR_FILE_EXISTS;
696         }
697
698         switch (edit->mode) {
699         case REG_DWORD: {
700                 uint32_t val;
701                 int base = 10;
702                 const char *buf = field_buffer(edit->field[FLD_DATA], 0);
703
704                 if (buf[0] == '0' && tolower(buf[1]) == 'x') {
705                         base = 16;
706                 }
707
708                 val = strtoul(buf, NULL, base);
709                 blob = data_blob_talloc(edit, NULL, sizeof(val));
710                 SIVAL(blob.data, 0, val);
711                 rv = WERR_OK;
712                 break;
713         }
714         case REG_SZ:
715         case REG_EXPAND_SZ: {
716                 const char *buf = field_buffer(edit->field[FLD_DATA], 0);
717                 char *str = string_trim(edit, buf);
718
719                 if (!str || !push_reg_sz(edit, &blob, str)) {
720                         rv = WERR_NOMEM;
721                 }
722                 break;
723         }
724         case REG_MULTI_SZ: {
725                 int rows, cols, max;
726                 const char **arr;
727                 size_t i;
728                 const char *buf = field_buffer(edit->field[FLD_DATA], 0);
729
730                 dynamic_field_info(edit->field[FLD_DATA], &rows, &cols, &max);
731
732                 arr = talloc_zero_array(edit, const char *, rows + 1);
733                 if (arr == NULL) {
734                         return WERR_NOMEM;
735                 }
736                 for (i = 0; *buf; ++i, buf += cols) {
737                         SMB_ASSERT(i < rows);
738                         arr[i] = string_trim_n(edit, buf, cols);
739                 }
740                 if (!push_reg_multi_sz(edit, &blob, arr)) {
741                         rv = WERR_NOMEM;
742                 }
743                 break;
744         }
745         case REG_BINARY:
746                 blob = data_blob_talloc(edit, NULL, edit->buf->len);
747                 memcpy(blob.data, edit->buf->data, edit->buf->len);
748                 break;
749         }
750
751         rv = reg_val_set(key, name, type, blob);
752
753         return rv;
754 }
755
756 static void section_down(struct edit_dialog *edit)
757 {
758         switch (edit->section) {
759         case IN_NAME:
760                 if (form_driver(edit->input, REQ_VALIDATION) == E_OK) {
761                         edit->section = IN_DATA;
762                         if (edit->buf) {
763                                 hexedit_set_cursor(edit->buf);
764                         } else {
765                                 set_current_field(edit->input,
766                                                   edit->field[FLD_DATA]);
767                                 pos_form_cursor(edit->input);
768                         }
769                 }
770                 break;
771         case IN_DATA:
772                 if (edit->buf ||
773                     form_driver(edit->input, REQ_VALIDATION) == E_OK) {
774                         edit->section = IN_MENU;
775                         menu_driver(edit->dia->choices, REQ_FIRST_ITEM);
776                         pos_menu_cursor(edit->dia->choices);
777                 }
778                 break;
779         case IN_MENU:
780                 if (current_item_is_last(edit->dia->choices)) {
781                         edit->section = IN_NAME;
782                         set_current_field(edit->input, edit->field[FLD_NAME]);
783                         pos_form_cursor(edit->input);
784                 } else {
785                         menu_driver(edit->dia->choices, REQ_RIGHT_ITEM);
786                 }
787                 break;
788         }
789 }
790
791 static void section_up(struct edit_dialog *edit)
792 {
793         switch (edit->section) {
794         case IN_NAME:
795                 if (form_driver(edit->input, REQ_VALIDATION) == E_OK) {
796                         edit->section = IN_MENU;
797                         menu_driver(edit->dia->choices, REQ_LAST_ITEM);
798                         pos_menu_cursor(edit->dia->choices);
799                 }
800                 break;
801         case IN_DATA:
802                 if (edit->buf ||
803                     form_driver(edit->input, REQ_VALIDATION) == E_OK) {
804                         edit->section = IN_NAME;
805                         set_current_field(edit->input, edit->field[FLD_NAME]);
806                         pos_form_cursor(edit->input);
807                 }
808                 break;
809         case IN_MENU:
810                 if (current_item_is_first(edit->dia->choices)) {
811                         edit->section = IN_DATA;
812                         if (edit->buf) {
813                                 hexedit_set_cursor(edit->buf);
814                         } else {
815                                 set_current_field(edit->input,
816                                                   edit->field[FLD_DATA]);
817                                 pos_form_cursor(edit->input);
818                         }
819                 } else {
820                         menu_driver(edit->dia->choices, REQ_LEFT_ITEM);
821                 }
822                 break;
823         }
824 }
825
826 static void handle_hexedit_input(struct hexedit *buf, int c)
827 {
828         switch (c) {
829         case KEY_UP:
830                 hexedit_driver(buf, HE_CURSOR_UP);
831                 break;
832         case KEY_DOWN:
833                 hexedit_driver(buf, HE_CURSOR_DOWN);
834                 break;
835         case KEY_LEFT:
836                 hexedit_driver(buf, HE_CURSOR_LEFT);
837                 break;
838         case KEY_RIGHT:
839                 hexedit_driver(buf, HE_CURSOR_RIGHT);
840                 break;
841         default:
842                 hexedit_driver(buf, c);
843                 break;
844         }
845
846         hexedit_set_cursor(buf);
847 }
848
849 static WERROR edit_init_dialog(struct edit_dialog *edit, uint32_t type)
850 {
851         char *title;
852         int diaheight = -1;
853         int winheight = -1;
854         const char *choices[] = {
855                 "Ok",
856                 "Cancel",
857                 "Resize",
858                 NULL
859         };
860
861         switch (edit->mode) {
862         case REG_MULTI_SZ:
863                 diaheight = EDIT_HEIGHT_MULTILINE;
864                 winheight = EDIT_FORM_WIN_HEIGHT_MULTILINE;
865                 choices[2] = NULL;
866                 break;
867         case REG_BINARY:
868                 diaheight = EDIT_HEIGHT_BUF;
869                 winheight = EDIT_FORM_WIN_HEIGHT_BUF;
870                 break;
871         default:
872                 diaheight = EDIT_HEIGHT_ONELINE;
873                 winheight = EDIT_FORM_WIN_HEIGHT_ONELINE;
874                 choices[2] = NULL;
875                 break;
876         }
877
878         title = talloc_asprintf(edit, "Edit %s value", str_regtype(type));
879         if (title == NULL) {
880                 return WERR_NOMEM;
881         }
882         edit->dia = dialog_choice_center_new(edit, title, choices, diaheight,
883                                              EDIT_WIDTH);
884         talloc_free(title);
885         if (edit->dia == NULL) {
886                 return WERR_NOMEM;
887         }
888         edit->input_win = derwin(edit->dia->sub_window, winheight,
889                                  EDIT_FORM_WIN_WIDTH,
890                                  EDIT_FORM_WIN_Y, EDIT_FORM_WIN_X);
891         if (edit->input_win == NULL) {
892                 return WERR_NOMEM;
893         }
894
895         return WERR_OK;
896 }
897
898 static WERROR edit_init_form(struct edit_dialog *edit,
899                              const struct value_item *vitem)
900 {
901
902         edit->field[0] = new_field(EDIT_NAME_LABEL_HEIGHT,
903                                    EDIT_NAME_LABEL_WIDTH,
904                                    EDIT_NAME_LABEL_Y,
905                                    EDIT_NAME_LABEL_X, 0, 0);
906         if (edit->field[0] == NULL) {
907                 return WERR_NOMEM;
908         }
909         set_field_buffer(edit->field[0], 0, "Name");
910         field_opts_off(edit->field[0], O_EDIT);
911
912         edit->field[FLD_NAME] = new_field(EDIT_NAME_INPUT_HEIGHT,
913                                           EDIT_NAME_INPUT_WIDTH,
914                                           EDIT_NAME_INPUT_Y,
915                                           EDIT_NAME_INPUT_X, 0, 0);
916         if (edit->field[FLD_NAME] == NULL) {
917                 return WERR_NOMEM;
918         }
919
920         edit->field[2] = new_field(EDIT_DATA_LABEL_HEIGHT,
921                                    EDIT_DATA_LABEL_WIDTH,
922                                    EDIT_DATA_LABEL_Y,
923                                    EDIT_DATA_LABEL_X, 0, 0);
924         if (edit->field[2] == NULL) {
925                 return WERR_NOMEM;
926         }
927         set_field_buffer(edit->field[2], 0, "Data");
928         field_opts_off(edit->field[2], O_EDIT);
929
930         if (edit->mode == REG_BINARY) {
931                 size_t len = 8;
932                 const void *buf = NULL;
933
934                 if (vitem) {
935                         len = vitem->data.length;
936                         buf = vitem->data.data;
937                 }
938                 edit->buf = hexedit_new(edit, edit->dia->sub_window,
939                                         EDIT_DATA_HEIGHT_BUF,
940                                         EDIT_DATA_INPUT_Y,
941                                         EDIT_DATA_INPUT_X,
942                                         buf, len);
943                 if (edit->buf == NULL) {
944                         return WERR_NOMEM;
945                 }
946                 hexedit_refresh(edit->buf);
947                 hexedit_set_cursor(edit->buf);
948         } else {
949                 int val_rows = EDIT_DATA_HEIGHT_ONELINE;
950
951                 if (edit->mode == REG_MULTI_SZ) {
952                         val_rows = EDIT_DATA_HEIGHT_MULTILINE;
953                 }
954                 edit->field[FLD_DATA] = new_field(val_rows,
955                                                   EDIT_DATA_INPUT_WIDTH,
956                                                   EDIT_DATA_INPUT_Y,
957                                                   EDIT_DATA_INPUT_X, 0, 0);
958                 if (edit->field[FLD_DATA] == NULL) {
959                         return WERR_NOMEM;
960                 }
961         }
962
963         set_field_back(edit->field[FLD_NAME], A_REVERSE);
964         field_opts_off(edit->field[FLD_NAME], O_BLANK | O_AUTOSKIP | O_STATIC);
965         if (edit->field[FLD_DATA]) {
966                 set_field_back(edit->field[FLD_DATA], A_REVERSE);
967                 field_opts_off(edit->field[FLD_DATA],
968                                O_BLANK | O_AUTOSKIP | O_STATIC | O_WRAP);
969                 if (edit->mode == REG_DWORD) {
970                         set_field_type(edit->field[FLD_DATA], TYPE_REGEXP,
971                                        "^ *([0-9]+|0[xX][0-9a-fA-F]+) *$");
972                 }
973         }
974
975         if (vitem) {
976                 set_field_buffer(edit->field[FLD_NAME], 0, vitem->value_name);
977                 field_opts_off(edit->field[FLD_NAME], O_EDIT);
978                 fill_value_buffer(edit, vitem);
979         }
980
981         edit->input = new_form(edit->field);
982         if (edit->input == NULL) {
983                 return WERR_NOMEM;
984         }
985         form_opts_off(edit->input, O_NL_OVERLOAD | O_BS_OVERLOAD);
986
987         set_form_win(edit->input, edit->dia->sub_window);
988         set_form_sub(edit->input, edit->input_win);
989         set_current_field(edit->input, edit->field[FLD_NAME]);
990         post_form(edit->input);
991
992         return WERR_OK;
993 }
994
995 static WERROR handle_editor_input(struct edit_dialog *edit,
996                                   struct registry_key *key,
997                                   uint32_t type,
998                                   const struct value_item *vitem, int c)
999 {
1000         WERROR rv = WERR_OK;
1001         int selection;
1002
1003         if (edit->section == IN_NAME) {
1004                 handle_form_input(edit->input, c);
1005         } else if (edit->section == IN_DATA) {
1006                 if (edit->buf) {
1007                         handle_hexedit_input(edit->buf, c);
1008                 } else {
1009                         handle_form_input(edit->input, c);
1010                 }
1011         } else {
1012                 selection = handle_menu_input(edit->dia->choices, c);
1013                 if (selection == DIALOG_OK) {
1014                         rv = set_value(edit, key, type, vitem == NULL);
1015                         if (W_ERROR_EQUAL(rv, WERR_FILE_EXISTS)) {
1016                                 dialog_notice(edit, DIA_ALERT,
1017                                               "Value exists",
1018                                               "Value name already exists.");
1019                                 selection = -1;
1020                         } else {
1021                                 edit->closing = true;
1022                         }
1023                 } else if (selection == DIALOG_RESIZE) {
1024                         char *n;
1025                         size_t newlen = 0;
1026
1027                         dialog_input(edit, &n, "Resize buffer",
1028                                      "Enter new size");
1029                         if (n) {
1030                                 newlen = strtoul(n, NULL, 10);
1031                                 edit->section = IN_DATA;
1032                                 hexedit_resize_buffer(edit->buf, newlen);
1033                                 hexedit_refresh(edit->buf);
1034                                 hexedit_set_cursor(edit->buf);
1035                                 talloc_free(n);
1036                         }
1037                 } else if (selection == DIALOG_CANCEL) {
1038                         edit->closing = true;
1039                 }
1040         }
1041
1042         return rv;
1043 }
1044
1045 WERROR dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key,
1046                          uint32_t type, const struct value_item *vitem,
1047                          bool force_binary)
1048 {
1049         struct edit_dialog *edit;
1050         WERROR rv = WERR_NOMEM;
1051
1052         edit = talloc_zero(ctx, struct edit_dialog);
1053         if (edit == NULL) {
1054                 return rv;
1055         }
1056         talloc_set_destructor(edit, edit_dialog_free);
1057
1058         edit->mode = type;
1059         if (force_binary || (vitem && vitem->unprintable)) {
1060                 edit->mode = REG_BINARY;
1061         }
1062
1063         rv = edit_init_dialog(edit, type);
1064         if (!W_ERROR_IS_OK(rv)) {
1065                 goto finish;
1066         }
1067         rv = edit_init_form(edit, vitem);
1068         if (!W_ERROR_IS_OK(rv)) {
1069                 goto finish;
1070         }
1071
1072         update_panels();
1073         doupdate();
1074
1075         edit->section = IN_NAME;
1076         edit->closing = false;
1077
1078         do {
1079                 int c = dialog_getch(edit->dia);
1080
1081                 if (c == '\t') {
1082                         section_down(edit);
1083                 } else if (c == KEY_BTAB) {
1084                         section_up(edit);
1085                 } else {
1086                         rv = handle_editor_input(edit, key, type, vitem, c);
1087                 }
1088
1089                 update_panels();
1090                 doupdate();
1091         } while (!edit->closing);
1092
1093 finish:
1094         talloc_free(edit);
1095
1096         return rv;
1097 }
1098
1099 int dialog_select_type(TALLOC_CTX *ctx, int *type)
1100 {
1101         struct dialog *dia;
1102         const char *choices[] = {
1103                 "OK",
1104                 "Cancel",
1105                 NULL
1106         };
1107         const char *reg_types[] = {
1108                 "REG_DWORD",
1109                 "REG_SZ",
1110                 "REG_EXPAND_SZ",
1111                 "REG_MULTI_SZ",
1112                 "REG_BINARY",
1113         };
1114 #define NTYPES (sizeof(reg_types) / sizeof(const char*))
1115         ITEM **item;
1116         MENU *list;
1117         WINDOW *type_win;
1118         int sel = -1;
1119         size_t i;
1120
1121         dia = dialog_choice_center_new(ctx, "New Value", choices, 10, 20);
1122         if (dia == NULL) {
1123                 return -1;
1124         }
1125
1126         mvwprintw(dia->sub_window, 0, 0, "Choose type:");
1127         type_win = derwin(dia->sub_window, 6, 18, 1, 0);
1128         if (type_win == NULL) {
1129                 goto finish;
1130         }
1131
1132         item = talloc_zero_array(dia, ITEM *, NTYPES + 1);
1133         if (item == NULL) {
1134                 goto finish;
1135         }
1136
1137         for (i = 0; i < NTYPES; ++i) {
1138                 int t = regtype_by_string(reg_types[i]);
1139
1140                 item[i] = new_item(reg_types[i], reg_types[i]);
1141                 if (item[i] == NULL) {
1142                         goto finish;
1143                 }
1144                 set_item_userptr(item[i], (void*)(uintptr_t)t);
1145         }
1146
1147         list = new_menu(item);
1148         if (list == NULL) {
1149                 goto finish;
1150         }
1151
1152         set_menu_format(list, 7, 1);
1153         set_menu_win(list, dia->sub_window);
1154         set_menu_sub(list, type_win);
1155         menu_opts_off(list, O_SHOWDESC);
1156         set_menu_mark(list, "* ");
1157         post_menu(list);
1158
1159         update_panels();
1160         doupdate();
1161
1162         while (sel == -1) {
1163                 ITEM *it;
1164                 int c = dialog_getch(dia);
1165
1166                 switch (c) {
1167                 case KEY_UP:
1168                         menu_driver(list, REQ_UP_ITEM);
1169                         break;
1170                 case KEY_DOWN:
1171                         menu_driver(list, REQ_DOWN_ITEM);
1172                         break;
1173                 case KEY_LEFT:
1174                         menu_driver(dia->choices, REQ_LEFT_ITEM);
1175                         break;
1176                 case KEY_RIGHT:
1177                         menu_driver(dia->choices, REQ_RIGHT_ITEM);
1178                         break;
1179                 case '\n':
1180                 case KEY_ENTER:
1181                         it = current_item(list);
1182                         *type = (int)(uintptr_t)item_userptr(it);
1183                         it = current_item(dia->choices);
1184                         sel = (int)(uintptr_t)item_userptr(it);
1185                         break;
1186                 }
1187
1188                 update_panels();
1189                 doupdate();
1190         }
1191
1192 finish:
1193         if (list) {
1194                 unpost_menu(list);
1195                 free_menu(list);
1196         }
1197         if (item) {
1198                 ITEM **it;
1199                 for (it = item; *it; ++it) {
1200                         free_item(*it);
1201                 }
1202         }
1203         if (type_win) {
1204                 delwin(type_win);
1205         }
1206         talloc_free(dia);
1207
1208         return sel;
1209 }