ui/ncurses: Add nc-auth and authenticate when required.
[petitboot] / ui / ncurses / nc-lang.c
1 /*
2  *  Copyright (C) 2013 IBM Corporation
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; version 2 of the License.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License
14  *  along with this program; if not, write to the Free Software
15  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17
18 #if defined(HAVE_CONFIG_H)
19 #include "config.h"
20 #endif
21
22 #include <stdlib.h>
23 #include <string.h>
24 #include <locale.h>
25
26 #include <talloc/talloc.h>
27 #include <types/types.h>
28 #include <log/log.h>
29 #include <i18n/i18n.h>
30 #include <pb-config/pb-config.h>
31
32 #include "ui/common/discover-client.h"
33 #include "nc-cui.h"
34 #include "nc-lang.h"
35 #include "nc-widgets.h"
36
37 #define N_FIELDS        7
38
39 static struct lang {
40         const char      *name;
41         const wchar_t   *label;
42 } languages[] = {
43         { "de_DE.utf8", L"Deutsch"},
44         { "en_US.utf8", L"English"},
45         { "es_ES.utf8", L"Espa\u00f1ol"},
46         { "fr_FR.utf8", L"Fran\u00e7ais"},
47         { "it_IT.utf8", L"Italiano"},
48         { "ja_JP.utf8", L"\u65e5\u672c\u8a9e"},
49         { "ko_KR.utf8", L"\ud55c\uad6d\uc5b4"},
50         { "pt_BR.utf8", L"Portugu\u00eas/Brasil"},
51         { "ru_RU.utf8", L"\u0420\u0443\u0441\u0441\u043a\u0438\u0439"},
52         { "zh_CN.utf8", L"\u7b80\u4f53\u4e2d\u6587"},
53         { "zh_TW.utf8", L"\u7e41\u9ad4\u4e2d\u6587"},
54 };
55
56 struct lang_screen {
57         struct nc_scr           scr;
58         struct cui              *cui;
59         struct nc_widgetset     *widgetset;
60         WINDOW                  *pad;
61
62         bool                    exit;
63         void                    (*on_exit)(struct cui *);
64
65         int                     scroll_y;
66
67         int                     label_x;
68         int                     field_x;
69
70         struct {
71                 struct nc_widget_select         *lang_f;
72                 struct nc_widget_label          *lang_l;
73
74                 struct nc_widget_label          *save_l;
75                 struct nc_widget_checkbox       *save_cb;
76
77                 struct nc_widget_label          *safe_mode;
78                 struct nc_widget_button         *ok_b;
79                 struct nc_widget_button         *cancel_b;
80         } widgets;
81 };
82
83 static struct lang_screen *lang_screen_from_scr(struct nc_scr *scr)
84 {
85         struct lang_screen *lang_screen;
86
87         assert(scr->sig == pb_lang_screen_sig);
88         lang_screen = (struct lang_screen *)
89                 ((char *)scr - (size_t)&((struct lang_screen *)0)->scr);
90         assert(lang_screen->scr.sig == pb_lang_screen_sig);
91         return lang_screen;
92 }
93
94 static void pad_refresh(struct lang_screen *screen)
95 {
96         int y, x, rows, cols;
97
98         getmaxyx(screen->scr.sub_ncw, rows, cols);
99         getbegyx(screen->scr.sub_ncw, y, x);
100
101         prefresh(screen->pad, screen->scroll_y, 0, y, x, rows, cols);
102 }
103
104 static void lang_screen_process_key(struct nc_scr *scr, int key)
105 {
106         struct lang_screen *screen = lang_screen_from_scr(scr);
107         bool handled;
108
109         handled = widgetset_process_key(screen->widgetset, key);
110
111         if (!handled) {
112                 switch (key) {
113                 case 'x':
114                 case 27: /* esc */
115                         screen->exit = true;
116                         break;
117                 }
118         }
119
120         if (screen->exit) {
121                 screen->on_exit(screen->cui);
122         } else if (handled && (screen->cui->current == scr)) {
123                 pad_refresh(screen);
124         }
125 }
126
127 static const char *lang_get_lang_name(struct lang_screen *screen)
128 {
129         struct lang *lang;
130         int idx;
131
132         idx = widget_select_get_value(screen->widgets.lang_f);
133
134         /* Option -1 ("Unknown") can only be populated from the current
135          * language, so there's no change here */
136         if (idx == -1)
137                 return NULL;
138
139         lang = &languages[idx];
140
141         return lang->name;
142 }
143
144 static int lang_process_form(struct lang_screen *screen)
145 {
146         struct config *config;
147         const char *lang;
148         int rc;
149
150         config = config_copy(screen, screen->cui->config);
151
152         lang = lang_get_lang_name(screen);
153
154         if (!lang || (config->lang && !strcmp(lang, config->lang)))
155                 return 0;
156
157         config->lang = talloc_strdup(screen, lang);
158
159         config->safe_mode = false;
160         rc = cui_send_config(screen->cui, config);
161         talloc_free(config);
162
163         if (rc)
164                 pb_log("cui_send_config failed!\n");
165         else
166                 pb_debug("config sent!\n");
167
168         return 0;
169 }
170
171 static void lang_screen_resize(struct nc_scr *scr)
172 {
173         struct lang_screen *screen = lang_screen_from_scr(scr);
174         (void)screen;
175 }
176
177 static int lang_screen_post(struct nc_scr *scr)
178 {
179         struct lang_screen *screen = lang_screen_from_scr(scr);
180
181         if (screen->exit)
182                 screen->on_exit(screen->cui);
183
184         widgetset_post(screen->widgetset);
185         nc_scr_frame_draw(scr);
186         wrefresh(screen->scr.main_ncw);
187         pad_refresh(screen);
188         return 0;
189 }
190
191 static int lang_screen_unpost(struct nc_scr *scr)
192 {
193         struct lang_screen *screen = lang_screen_from_scr(scr);
194         widgetset_unpost(screen->widgetset);
195         return 0;
196 }
197
198 struct nc_scr *lang_screen_scr(struct lang_screen *screen)
199 {
200         return &screen->scr;
201 }
202
203 static void lang_screen_update_cb(struct nc_scr *scr)
204 {
205         struct lang_screen *screen = lang_screen_from_scr(scr);
206
207         if (!lang_process_form(screen))
208                 screen->exit = true;
209 }
210
211 static void ok_click(void *arg)
212 {
213         struct lang_screen *screen = arg;
214         const char *lang;
215
216         if (!widget_checkbox_get_value(screen->widgets.save_cb)) {
217                 /* Just update the client display */
218                 lang = lang_get_lang_name(screen);
219                 if (lang)
220                         cui_update_language(screen->cui, lang);
221                 screen->exit = true;
222                 return;
223         }
224
225         if (discover_client_authenticated(screen->cui->client)) {
226                 if (lang_process_form(screen))
227                         /* errors are written to the status line, so we'll need
228                          * to refresh */
229                         wrefresh(screen->scr.main_ncw);
230                 else
231                         screen->exit = true;
232         } else {
233                 cui_show_auth(screen->cui, screen->scr.main_ncw, false,
234                                 lang_screen_update_cb);
235         }
236 }
237
238 static void cancel_click(void *arg)
239 {
240         struct lang_screen *screen = arg;
241         screen->exit = true;
242 }
243
244 static int layout_pair(struct lang_screen *screen, int y,
245                 struct nc_widget_label *label,
246                 struct nc_widget *field)
247 {
248         struct nc_widget *label_w = widget_label_base(label);
249         widget_move(label_w, y, screen->label_x);
250         widget_move(field, y, screen->field_x);
251         return max(widget_height(label_w), widget_height(field));
252 }
253
254 static void lang_screen_layout_widgets(struct lang_screen *screen)
255 {
256         int y;
257
258         y = 1;
259
260         y += layout_pair(screen, y, screen->widgets.lang_l,
261                         widget_select_base(screen->widgets.lang_f));
262
263         y += 1;
264
265         y += layout_pair(screen, y, screen->widgets.save_l,
266                         widget_checkbox_base(screen->widgets.save_cb));
267         y += 1;
268
269         if (screen->cui->config->safe_mode) {
270                 widget_move(widget_label_base(screen->widgets.safe_mode),
271                         y, screen->field_x);
272                 y += 1;
273         }
274
275         widget_move(widget_button_base(screen->widgets.ok_b),
276                         y, screen->field_x);
277         widget_move(widget_button_base(screen->widgets.cancel_b),
278                         y, screen->field_x + 14);
279 }
280
281 static void lang_screen_setup_empty(struct lang_screen *screen)
282 {
283         widget_new_label(screen->widgetset, 2, screen->field_x,
284                         _("Waiting for configuration data..."));
285         screen->widgets.cancel_b = widget_new_button(screen->widgetset,
286                         4, screen->field_x, 9, _("Cancel"),
287                         cancel_click, screen);
288 }
289
290
291 static void lang_screen_setup_widgets(struct lang_screen *screen,
292                 const struct config *config)
293 {
294         struct nc_widgetset *set = screen->widgetset;
295         unsigned int i;
296         bool found;
297
298         build_assert(sizeof(screen->widgets) / sizeof(struct widget *)
299                         == N_FIELDS);
300
301         screen->widgets.lang_l = widget_new_label(set, 0, 0, _("Language"));
302         screen->widgets.lang_f = widget_new_select(set, 0, 0, 50);
303
304         found = false;
305
306         for (i = 0; i < ARRAY_SIZE(languages); i++) {
307                 struct lang *lang = &languages[i];
308                 bool selected;
309                 char *label;
310                 int len;
311
312                 len = wcstombs(NULL, lang->label, 0);
313                 assert(len >= 0);
314                 if (len < 0) {
315                         label = talloc_asprintf(screen,
316                                 "Unable to display text in this locale (%s)\n",
317                                 setlocale(LC_ALL, NULL));
318                 } else {
319                         label = talloc_array(screen, char, len + 1);
320                         wcstombs(label, lang->label, len + 1);
321                 }
322
323                 selected = config->lang && !strcmp(lang->name, config->lang);
324                 found |= selected;
325
326                 widget_select_add_option(screen->widgets.lang_f, i,
327                                         label, selected);
328         }
329
330         if (!found && config->lang) {
331                 char *label = talloc_asprintf(screen,
332                                 _("Unknown language '%s'"), config->lang);
333                 widget_select_add_option(screen->widgets.lang_f, -1,
334                                         label, true);
335         }
336
337         screen->widgets.save_l = widget_new_label(set, 0, 0,
338                         _("Save changes?"));
339         screen->widgets.save_cb = widget_new_checkbox(set, 0, 0, false);
340
341         if (config->safe_mode)
342                 screen->widgets.safe_mode = widget_new_label(set, 0, 0,
343                          _("Selecting 'OK' will exit safe mode"));
344
345         screen->widgets.ok_b = widget_new_button(set, 0, 0, 10, _("OK"),
346                         ok_click, screen);
347         screen->widgets.cancel_b = widget_new_button(set, 0, 0, 10, _("Cancel"),
348                         cancel_click, screen);
349 }
350
351 static void lang_screen_widget_focus(struct nc_widget *widget, void *arg)
352 {
353         struct lang_screen *screen = arg;
354         int w_y, s_max;
355
356         w_y = widget_y(widget) + widget_focus_y(widget);
357         s_max = getmaxy(screen->scr.sub_ncw) - 1;
358
359         if (w_y < screen->scroll_y)
360                 screen->scroll_y = w_y;
361
362         else if (w_y + screen->scroll_y + 1 > s_max)
363                 screen->scroll_y = 1 + w_y - s_max;
364
365         else
366                 return;
367
368         pad_refresh(screen);
369 }
370
371 static void lang_screen_draw(struct lang_screen *screen,
372                 const struct config *config)
373 {
374         bool repost = false;
375         int height;
376
377         height = ARRAY_SIZE(languages) + N_FIELDS + 4;
378         if (!screen->pad || getmaxy(screen->pad) < height) {
379                 if (screen->pad)
380                         delwin(screen->pad);
381                 screen->pad = newpad(height, COLS);
382         }
383
384         if (screen->widgetset) {
385                 widgetset_unpost(screen->widgetset);
386                 talloc_free(screen->widgetset);
387                 repost = true;
388         }
389
390         screen->widgetset = widgetset_create(screen, screen->scr.main_ncw,
391                         screen->pad);
392         widgetset_set_widget_focus(screen->widgetset,
393                         lang_screen_widget_focus, screen);
394
395         if (!config) {
396                 lang_screen_setup_empty(screen);
397         } else {
398                 lang_screen_setup_widgets(screen, config);
399                 lang_screen_layout_widgets(screen);
400         }
401
402         if (repost)
403                 widgetset_post(screen->widgetset);
404 }
405
406 void lang_screen_update(struct lang_screen *screen,
407                 const struct config *config)
408 {
409         lang_screen_draw(screen, config);
410         pad_refresh(screen);
411 }
412
413 static int lang_screen_destroy(void *arg)
414 {
415         struct lang_screen *screen = arg;
416         if (screen->pad)
417                 delwin(screen->pad);
418         return 0;
419 }
420
421 struct lang_screen *lang_screen_init(struct cui *cui,
422                 const struct config *config,
423                 void (*on_exit)(struct cui *))
424 {
425         struct lang_screen *screen;
426
427         screen = talloc_zero(cui, struct lang_screen);
428         talloc_set_destructor(screen, lang_screen_destroy);
429         nc_scr_init(&screen->scr, pb_lang_screen_sig, 0,
430                         cui, lang_screen_process_key,
431                         lang_screen_post, lang_screen_unpost,
432                         lang_screen_resize);
433
434         screen->cui = cui;
435         screen->on_exit = on_exit;
436         screen->label_x = 2;
437         screen->field_x = 17;
438
439         screen->scr.frame.ltitle = talloc_strdup(screen,
440                         _("Petitboot Language Selection"));
441         screen->scr.frame.rtitle = NULL;
442         screen->scr.frame.help = talloc_strdup(screen,
443                         _("tab=next, shift+tab=previous, x=exit"));
444         nc_scr_frame_draw(&screen->scr);
445
446         scrollok(screen->scr.sub_ncw, true);
447
448         lang_screen_draw(screen, config);
449
450         return screen;
451 }